diff --git a/.github/README.md b/.github/README.md
new file mode 100644
index 000000000000..02ebeca2ec32
--- /dev/null
+++ b/.github/README.md
@@ -0,0 +1,214 @@
+[](https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub)
+
+# Welcome to JetBrains Runtime!
+
+JetBrains Runtime is a fork of [OpenJDK](https://github.com/openjdk/jdk) available for Windows, Mac OS X, and Linux.
+It supports enhanced class redefinition ([DCEVM](https://ssw.jku.at/dcevm/)),
+features optional [JCEF](https://github.com/JetBrains/jcef), a framework for embedding Chromium-based browsers,
+includes a number of improvements in font rendering, keyboards support,
+windowing/focus subsystems, HiDPI, accessibility, and performance, provides better desktop integration
+and bugfixes not yet present in OpenJDK.
+
+> **_NOTE_**: This is a **development** branch that is periodically synchronized with
+> the [OpenJDK master](https://github.com/openjdk/jdk/tree/master) branch.
+>
+ Release builds are based on these branches:
+ * [jbr11](https://github.com/JetBrains/JetBrainsRuntime/tree/jbr11) (JDK 11)
+ * [jbr17](https://github.com/JetBrains/JetBrainsRuntime/tree/jbr17) (JDK 17)
+
+Download the latest releases of JetBrains Runtime to use with JetBrains IDEs. The full list
+can be found on the [releases page](https://github.com/JetBrains/JetBrainsRuntime/releases).
+
+## Releases based on JDK 17
+
+| IDE Version | Latest JBR | Date Released |
+|-------------|--------------------------------------------------------------------------------------------------------|---------------|
+| 2023.3 | [17.0.9-b1087.7](https://github.com/JetBrains/JetBrainsRuntime/releases/tag/jbr-release-17.0.9b1087.7) | 20-Nov-2023 |
+| 2023.2 | [17.0.9-b1000.46](https://github.com/JetBrains/JetBrainsRuntime/releases/tag/jbr-release-17.0.9b1000.46) | 01-Nov-2023 |
+| 2023.1 | [17.0.7-b829.16](https://github.com/JetBrains/JetBrainsRuntime/releases/tag/jbr-release-17.0.7b829.16) | 06-Jun-2023 |
+| 2022.3 | [17.0.6-b653.34](https://github.com/JetBrains/JetBrainsRuntime/releases/tag/jbr-release-17.0.6b653.34) | 28-Feb-2023 |
+| 2022.2 | [17.0.6-b469.82](https://github.com/JetBrains/JetBrainsRuntime/releases/tag/jbr-release-17.0.6b469.82) | 06-Mar-2023 |
+
+## Releases based on JDK 11
+
+| IDE Version | Latest JBR | Date Released |
+|-------------|-------------------------------------------------------------------------------------------------------|---------------|
+| 2022.1 | [11_0_16-b2043.64](https://github.com/JetBrains/JetBrainsRuntime/releases/tag/jbr11_0_16b2043.64) | 10-Nov-2022 |
+| 2021.3 | [11_0_14_1-b1751.46](https://github.com/JetBrains/JetBrainsRuntime/releases/tag/jbr11_0_14_1b1751.46) | 21-Feb-2022 |
+| 2021.2 | [11_0_13-b1504.49](https://github.com/JetBrains/JetBrainsRuntime/releases/tag/jb11_0_13-b1504.49) | 15-Nov-2021 |
+| 2021.1 | [11.0.11+9-b1341.60](https://github.com/JetBrains/JetBrainsRuntime/issues/171#issuecomment-1248891540)| 15-Jun-2021 |
+| 2020.3 | [11_0_10-b1145.115](https://github.com/JetBrains/JetBrainsRuntime/issues/171#issuecomment-1249243977) | 21-Jun-2021 |
+
+## Contents
+- [Welcome to JetBrains Runtime](#welcome-to-jetbrains-runtime)
+ - [Why Use JetBrains Runtime?](#why-use-jetbrains-runtime)
+ - [Products Built on JetBrains Runtime](#products-built-on-jetbrains-runtime)
+ - [Getting Sources](#getting-sources)
+ - [macOS, Linux](#macos-linux)
+ - [Windows](#sources-windows)
+ - [Configuring the Build Environment](#configuring-the-build-environment)
+ - [Linux (Docker)](#linux-docker)
+ - [Ubuntu Linux](#ubuntu-linux)
+ - [Windows](#build-windows)
+ - [macOS](#macos)
+ - [Developing](#developing)
+ - [Contributing](#contributing)
+ - [Resources](#resources)
+
+## Why Use JetBrains Runtime?
+* **Embedded browser**: JetBrains Runtime includes the Java Chromium Embedded Framework ([JCEF](https://github.com/JetBrains/jcef)), which
+ enables you to embed a Chromium-based browsers in your JVM-based application.
+ To use it, [download a build with JCEF](https://github.com/JetBrains/JetBrainsRuntime/releases).
+* **Enhanced class re-definition** with the [DCEVM](https://ssw.jku.at/dcevm/) technology that makes it easier to reload
+ changed code without restarting JVM; this feature needs to be explicitly enabled with `-XX:+AllowEnhancedClassRedefinition`.
+* **Better FPS performance** for graphics-intensive applications.
+* **Improved font rendering**, **keyboard input** (such as shortcuts and multinational keyboards),
+ **HiDPI** and **accessibility** support.
+* **Robust desktop experience**: GUI-related fixes often reach JetBrains Runtime much earlier than the corresponding version of OpenJDK.
+
+## Products Built on JetBrains Runtime
+* [Android Studio](https://developer.android.com/studio). The official IDE for Google's Android operating system.
+* [CLion](https://www.jetbrains.com/clion/). A cross-platform IDE for C and C++ from JetBrains.
+* [DataGrip](https://www.jetbrains.com/datagrip/). The IDE for Databases and SQL from JetBrains.
+* [GoLand](https://www.jetbrains.com/go/). The cross-platform Go IDE from JetBrains.
+* [IntelliJ IDEA](https://www.jetbrains.com/idea/). The IDE for JVM from JetBrains.
+* [JProfiler](https://www.ej-technologies.com/products/jprofiler/overview.html). The Java profiler.
+* [PhpStorm](https://www.jetbrains.com/phpstorm/). The PHP IDE from JetBrains.
+* [PyCharm](https://www.jetbrains.com/pycharm/). The Python IDE from JetBrains.
+* [Rider](https://www.jetbrains.com/rider/). The cross-platform .NET IDE from JetBrains.
+* [RubyMine](https://www.jetbrains.com/ruby/). The Ruby and Rails IDE from JetBrains.
+* [Toolbox App](https://www.jetbrains.com/toolbox-app/). JetBrains IDE manager.
+* [WebStorm](https://www.jetbrains.com/webstorm/). The JavaScript IDE from JetBrains.
+* [YourKit](https://www.yourkit.com/). Java and .NET profilers.
+
+## Getting Sources
+### macOS, Linux
+```
+git config --global core.autocrlf input
+git clone git@github.com:JetBrains/JetBrainsRuntime.git
+```
+
+### Windows
+
+```
+git config --global core.autocrlf false
+git clone git@github.com:JetBrains/JetBrainsRuntime.git
+```
+
+## Configuring the Build Environment
+Here are quick per-platform instructions for those who can't wait to get started.
+Please refer to [OpenJDK build docs](https://openjdk.java.net/groups/build/doc/building.html) for in-depth
+coverage of all the details.
+
+> **_TIP:_** To get a preliminary report of what's missing, run `./configure` and check its output.
+> It would usually have meaningful advice on how to solve the problem.
+
+### Linux (Docker)
+Create a container:
+```
+$ cd jb/project/docker
+$ docker build .
+...
+Successfully built 942ea9900054
+```
+Run these commands in the new container:
+```
+$ docker run -v `pwd`../../../../:/JetBrainsRuntime -it 942ea9900054
+# cd /JetBrainsRuntime
+# sh ./configure
+# make images CONF=linux-x86_64-normal-server-release
+```
+
+### Ubuntu Linux
+Install the necessary tools, libraries, and headers with:
+```
+$ sudo apt-get install autoconf make build-essential libx11-dev libxext-dev libxrender-dev libxtst-dev \
+ libxt-dev libxrandr-dev libcups2-dev libfontconfig1-dev libasound2-dev
+```
+Get Java 18 (for instance, [Azul Zulu Builds of OpenJDK 18](https://www.azul.com/downloads/?version=java-18-sts&os=linux&package=jdk)).
+
+Then run the following:
+```
+$ cd JetBrainsRuntime
+$ git checkout main
+$ sh ./configure
+$ make images
+```
+This will build the release configuration under `./build/linux-x86_64-server-release/`.
+
+### Windows
+
+Install the following:
+* [Cygwin x64](http://www.cygwin.com/).
+ Required packages: `autoconf`, `binutils`, `cpio`, `diffutils`, `file`, `gawk`, `gcc-core`, `make`, `m4`, `unzip`, `zip`.
+ Install those together with Cygwin.
+* [Visual Studio compiler toolset](https://visualstudio.microsoft.com/downloads/).
+ Install with the desktop development kit, which includes Windows SDK and compilers.
+ Visual Studio 2019 is supported by default.
+* Java 18 (for instance, [Azul Zulu Builds of OpenJDK 18](https://www.azul.com/downloads/?version=java-18-sts&os=windows&package=jdk)).
+ If you have problems while configuring, read [Java tips on Cygwin](http://horstmann.com/articles/cygwin-tips.html).
+
+From the command line:
+```
+"C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvarsall.bat" amd64
+"c:\Program_Files\cygwin64\bin\mintty.exe" /bin/bash -l
+```
+The first command sets up environment variables, the second starts a Cygwin shell with the proper environment.
+
+In the Cygwin shell:
+```
+$ cd JetBrainsRuntime
+$ git checkout main
+$ bash configure --with-toolchain-version=2019
+$ make images
+```
+This will build the release configuration under `./build/windows-x86_64-server-release/`.
+
+### macOS
+Install the following:
+* Xcode command line developer tools and `autoconf` via [Homebrew](https://brew.sh/).
+* Java 18 (for instance, [Azul Zulu Builds of OpenJDK 18](https://www.azul.com/downloads/?version=java-18-sts&os=macos&package=jdk)).
+
+From the command line:
+```
+$ cd JetBrainsRuntime
+$ git checkout main
+$ sh ./configure
+$ make images
+```
+This will build the release configuration under `./build/macosx-x86_64-server-release/`.
+
+## Developing
+You can use [CLion](https://www.jetbrains.com/clion/) to develop native parts of the JetBrains Runtime and
+[IntelliJ IDEA](https://www.jetbrains.com/idea/) for the parts written in Java.
+Both require projects to be created.
+
+### CLion
+Run
+```
+$ make compile-commands
+```
+in the git root and open the resulting `build/.../compile_commands.json` file as a project.
+Then use `Tools | Compilation Database | Change Project Root` to point to git root of this repository.
+
+See also this detailed step-by-step tutorial for all platforms:
+[How to develop OpenJDK with CLion](https://blog.jetbrains.com/clion/2020/03/openjdk-with-clion/).
+
+### IDEA
+Run
+```
+$ sh ./bin/idea.sh
+```
+in the git root to generate project files (add `--help` for options). If you have multiple
+configurations (for example, `release` and `fastdebug`), supply the `--conf ` argument.
+Then open the git root directory as a project in IDEA.
+
+## Contributing
+We are happy to receive your pull requests!
+Before you submit one, please sign our [Contributor License Agreement (CLA)](https://www.jetbrains.com/agreements/cla/).
+
+## Resources
+* [JetBrains Runtime on GitHub](https://github.com/JetBrains/JetBrainsRuntime).
+* [OpenJDK build instructions](https://openjdk.java.net/groups/build/doc/building.html).
+* [OpenJDK test instructions](https://htmlpreview.github.io/?https://raw.githubusercontent.com/openjdk/jdk/master/doc/building.html#running-tests).
+* [How to develop OpenJDK with CLion](https://blog.jetbrains.com/clion/2020/03/openjdk-with-clion/).
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index ebc0f3d961d3..f3be198253fb 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -31,6 +31,7 @@ on:
- master
- pr/*
- jdk*
+ - main
workflow_dispatch:
inputs:
platforms:
@@ -60,7 +61,8 @@ jobs:
outputs:
linux-x64: ${{ steps.include.outputs.linux-x64 }}
linux-x86: ${{ steps.include.outputs.linux-x86 }}
- linux-x64-variants: ${{ steps.include.outputs.linux-x64-variants }}
+ # additional build options for linux-x64 are disabled
+ # linux-x64-variants: ${{ steps.include.outputs.linux-x64-variants }}
linux-cross-compile: ${{ steps.include.outputs.linux-cross-compile }}
macos-x64: ${{ steps.include.outputs.macos-x64 }}
macos-aarch64: ${{ steps.include.outputs.macos-aarch64 }}
diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml
new file mode 100644
index 000000000000..33697b7a9799
--- /dev/null
+++ b/.github/workflows/pr.yml
@@ -0,0 +1,270 @@
+#
+# Copyright 2000-2023 JetBrains s.r.o.
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# This code is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License version 2 only, as
+# published by the Free Software Foundation. Oracle designates this
+# particular file as subject to the "Classpath" exception as provided
+# by Oracle in the LICENSE file that accompanied this code.
+#
+# This code is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# version 2 for more details (a copy is included in the LICENSE file that
+# accompanied this code).
+#
+# You should have received a copy of the GNU General Public License version
+# 2 along with this work; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+# or visit www.oracle.com if you need additional information or have any
+# questions.
+#
+
+name: 'Build OpenJDK on pull request'
+
+on:
+ pull_request:
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+jobs:
+ ###
+ ### Determine platforms to include
+ ###
+ select:
+ name: 'Select platforms'
+ runs-on: ubuntu-22.04
+ outputs:
+ linux-x64: ${{ steps.include.outputs.linux-x64 }}
+ linux-x86: ${{ steps.include.outputs.linux-x86 }}
+ linux-cross-compile: ${{ steps.include.outputs.linux-cross-compile }}
+ macos-x64: ${{ steps.include.outputs.macos-x64 }}
+ macos-aarch64: ${{ steps.include.outputs.macos-aarch64 }}
+ windows-x64: ${{ steps.include.outputs.windows-x64 }}
+ windows-aarch64: ${{ steps.include.outputs.windows-aarch64 }}
+ windows-x86: ${{ steps.include.outputs.windows-x86 }}
+
+ steps:
+ # This function must be inlined in main.yml, or we'd be forced to checkout the repo
+ - name: 'Check what jobs to run'
+ id: include
+ run: |
+ # Determine which platform jobs to run
+
+ # Returns 'true' if the input platform list matches any of the platform monikers given as argument,
+ # 'false' otherwise.
+ # arg $1: platform name or names to look for
+ function check_platform() {
+ if [[ $GITHUB_EVENT_NAME == workflow_dispatch ]]; then
+ input='${{ github.event.inputs.platforms }}'
+ elif [[ $GITHUB_EVENT_NAME == push ]]; then
+ if [[ '${{ !secrets.JDK_SUBMIT_FILTER || startsWith(github.ref, 'refs/heads/submit/') }}' == 'false' ]]; then
+ # If JDK_SUBMIT_FILTER is set, and this is not a "submit/" branch, don't run anything
+ >&2 echo 'JDK_SUBMIT_FILTER is set and not a "submit/" branch'
+ echo 'false'
+ return
+ else
+ input='${{ secrets.JDK_SUBMIT_PLATFORMS }}'
+ fi
+ fi
+
+ normalized_input="$(echo ,$input, | tr -d ' ')"
+ if [[ "$normalized_input" == ",," ]]; then
+ # For an empty input, assume all platforms should run
+ echo 'true'
+ return
+ else
+ # Check for all acceptable platform names
+ for part in $* ; do
+ if echo "$normalized_input" | grep -q -e ",$part," ; then
+ echo 'true'
+ return
+ fi
+ done
+ fi
+
+ echo 'false'
+ }
+
+ echo "linux-x64=$(check_platform linux-x64 linux x64)" >> $GITHUB_OUTPUT
+ echo "linux-x86=$(check_platform linux-x86 linux x86)" >> $GITHUB_OUTPUT
+ echo "linux-x64-variants=$(check_platform linux-x64-variants variants)" >> $GITHUB_OUTPUT
+ echo "linux-cross-compile=$(check_platform linux-cross-compile cross-compile)" >> $GITHUB_OUTPUT
+ echo "macos-x64=$(check_platform macos-x64 macos x64)" >> $GITHUB_OUTPUT
+ echo "macos-aarch64=$(check_platform macos-aarch64 macos aarch64)" >> $GITHUB_OUTPUT
+ echo "windows-x64=$(check_platform windows-x64 windows x64)" >> $GITHUB_OUTPUT
+ echo "windows-x86=$(check_platform windows-x86 windows x86)" >> $GITHUB_OUTPUT
+ echo "windows-aarch64=$(check_platform windows-aarch64 windows aarch64)" >> $GITHUB_OUTPUT
+ echo "docs=$(check_platform docs)" >> $GITHUB_OUTPUT
+
+ ###
+ ### Build jobs
+ ###
+
+ build-linux-x64:
+ name: linux-x64
+ needs: select
+ uses: ./.github/workflows/build-linux.yml
+ with:
+ platform: linux-x64
+ gcc-major-version: '10'
+ apt-gcc-version: '10.4.0-4ubuntu1~22.04'
+ configure-arguments: ${{ github.event.inputs.configure-arguments }}
+ make-arguments: ${{ github.event.inputs.make-arguments }}
+ # The linux-x64 jdk bundle is used as buildjdk for the cross-compile job
+ if: needs.select.outputs.linux-x64 == 'true' || needs.select.outputs.linux-cross-compile == 'true'
+
+ build-linux-x86:
+ name: linux-x86
+ needs: select
+ uses: ./.github/workflows/build-linux.yml
+ with:
+ platform: linux-x86
+ gcc-major-version: '10'
+ gcc-package-suffix: '-multilib'
+ apt-gcc-version: '10.4.0-4ubuntu1~22.04'
+ apt-architecture: 'i386'
+ # Some multilib libraries do not have proper inter-dependencies, so we have to
+ # install their dependencies manually.
+ apt-extra-packages: 'libfreetype6-dev:i386 libtiff-dev:i386 libcupsimage2-dev:i386 libc6-i386'
+ extra-conf-options: '--with-target-bits=32'
+ configure-arguments: ${{ github.event.inputs.configure-arguments }}
+ make-arguments: ${{ github.event.inputs.make-arguments }}
+ if: needs.select.outputs.linux-x86 == 'true'
+
+ build-linux-cross-compile:
+ name: linux-cross-compile
+ needs:
+ - select
+ - build-linux-x64
+ uses: ./.github/workflows/build-cross-compile.yml
+ with:
+ gcc-major-version: '10'
+ apt-gcc-version: '10.4.0-4ubuntu1~22.04'
+ apt-gcc-cross-version: '10.4.0-4ubuntu1~22.04cross1'
+ configure-arguments: ${{ github.event.inputs.configure-arguments }}
+ make-arguments: ${{ github.event.inputs.make-arguments }}
+ if: needs.select.outputs.linux-cross-compile == 'true'
+
+ build-macos-x64:
+ name: macos-x64
+ needs: select
+ uses: ./.github/workflows/build-macos.yml
+ with:
+ platform: macos-x64
+ xcode-toolset-version: '12.5.1'
+ configure-arguments: ${{ github.event.inputs.configure-arguments }}
+ make-arguments: ${{ github.event.inputs.make-arguments }}
+ if: needs.select.outputs.macos-x64 == 'true'
+
+ build-macos-aarch64:
+ name: macos-aarch64
+ needs: select
+ uses: ./.github/workflows/build-macos.yml
+ with:
+ platform: macos-aarch64
+ xcode-toolset-version: '12.5.1'
+ extra-conf-options: '--openjdk-target=aarch64-apple-darwin'
+ configure-arguments: ${{ github.event.inputs.configure-arguments }}
+ make-arguments: ${{ github.event.inputs.make-arguments }}
+ if: needs.select.outputs.macos-aarch64 == 'true'
+
+ build-windows-x64:
+ name: windows-x64
+ needs: select
+ uses: ./.github/workflows/build-windows.yml
+ with:
+ platform: windows-x64
+ msvc-toolset-version: '14.29'
+ msvc-toolset-architecture: 'x86.x64'
+ configure-arguments: ${{ github.event.inputs.configure-arguments }}
+ make-arguments: ${{ github.event.inputs.make-arguments }}
+ if: needs.select.outputs.windows-x64 == 'true'
+
+ build-windows-x86:
+ name: windows-x86
+ needs: select
+ uses: ./.github/workflows/build-windows.yml
+ with:
+ platform: windows-x86
+ msvc-toolset-version: '14.29'
+ msvc-toolset-architecture: 'x86'
+ configure-arguments: ${{ github.event.inputs.configure-arguments }}
+ make-arguments: ${{ github.event.inputs.make-arguments }}
+ if: needs.select.outputs.windows-x86 == 'true'
+
+ build-windows-aarch64:
+ name: windows-aarch64
+ needs: select
+ uses: ./.github/workflows/build-windows.yml
+ with:
+ platform: windows-aarch64
+ msvc-toolset-version: '14.29'
+ msvc-toolset-architecture: 'arm64'
+ make-target: 'hotspot'
+ extra-conf-options: '--openjdk-target=aarch64-unknown-cygwin'
+ configure-arguments: ${{ github.event.inputs.configure-arguments }}
+ make-arguments: ${{ github.event.inputs.make-arguments }}
+ if: needs.select.outputs.windows-aarch64 == 'true'
+
+ build-docs:
+ name: docs
+ needs: select
+ uses: ./.github/workflows/build-linux.yml
+ with:
+ platform: linux-x64
+ debug-levels: '[ "debug" ]'
+ make-target: 'docs-jdk-bundles'
+ # Make sure we never try to make full docs, since that would require a
+ # build JDK, and we do not need the additional testing of the graphs.
+ extra-conf-options: '--disable-full-docs'
+ gcc-major-version: '10'
+ apt-gcc-version: '10.4.0-4ubuntu1~22.04'
+ configure-arguments: ${{ github.event.inputs.configure-arguments }}
+ make-arguments: ${{ github.event.inputs.make-arguments }}
+ if: needs.select.outputs.docs == 'true'
+
+ # Remove bundles so they are not misconstrued as binary distributions from the JDK project
+ remove-bundles:
+ name: 'Remove bundle artifacts'
+ runs-on: ubuntu-22.04
+ if: always()
+ needs:
+ - build-linux-x64
+ - build-linux-x86
+ - build-linux-cross-compile
+ - build-macos-x64
+ - build-macos-aarch64
+ - build-windows-x64
+ - build-windows-aarch64
+ - build-windows-x86
+
+ steps:
+ # Hack to get hold of the api environment variables that are only defined for actions
+ - name: 'Get API configuration'
+ id: api
+ uses: actions/github-script@v6
+ with:
+ script: 'return { url: process.env["ACTIONS_RUNTIME_URL"], token: process.env["ACTIONS_RUNTIME_TOKEN"] }'
+
+ - name: 'Remove bundle artifacts'
+ run: |
+ # Find and remove all bundle artifacts
+ ALL_ARTIFACT_URLS="$(curl -s \
+ -H 'Accept: application/json;api-version=6.0-preview' \
+ -H 'Authorization: Bearer ${{ fromJson(steps.api.outputs.result).token }}' \
+ '${{ fromJson(steps.api.outputs.result).url }}_apis/pipelines/workflows/${{ github.run_id }}/artifacts?api-version=6.0-preview')"
+ BUNDLE_ARTIFACT_URLS="$(echo "$ALL_ARTIFACT_URLS" | jq -r -c '.value | map(select(.name|startswith("bundles-"))) | .[].url')"
+ for url in $BUNDLE_ARTIFACT_URLS; do
+ echo "Removing $url"
+ curl -s \
+ -H 'Accept: application/json;api-version=6.0-preview' \
+ -H 'Authorization: Bearer ${{ fromJson(steps.api.outputs.result).token }}' \
+ -X DELETE "$url" \
+ || echo "Failed to remove bundle"
+ done
diff --git a/README.md b/README.md
index b3f30676b3cf..bbe063c24bb6 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,5 @@
+[](https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub)
+
# Welcome to the JDK!
For build instructions please see the
diff --git a/bin/idea.sh b/bin/idea.sh
index c85ae294454e..11c727dd936b 100644
--- a/bin/idea.sh
+++ b/bin/idea.sh
@@ -25,7 +25,26 @@
# Shell script for generating an IDEA project from a given list of modules
usage() {
- echo "usage: $0 [-h|--help] [-v|--verbose] [-o|--output ] [-c|--conf ] [modules]+"
+ echo "Usage: $0 [-h|--help] [-q|--quiet] [-a|--absolute-paths] [-r|--root ] [-o|--output ] [-c|--conf ] [modules...]"
+ echo " -h | --help"
+ echo " -q | --quiet
+ No stdout output"
+ echo " -a | --absolute-paths
+ Use absolute paths to this jdk, so that generated .idea
+ project files can be moved independently of jdk sources"
+ echo " -r | --root
+ Project content root
+ Default: $TOPLEVEL_DIR"
+ echo " -o | --output
+ Where .idea directory with project files will be generated
+ (e.g. using '-o .' will place project files in './.idea')
+ Default: same as --root"
+ echo " -c | --conf
+ make configuration (release, slowdebug etc)"
+ echo " [modules...]
+ Generate project modules for specific java modules
+ (e.g. 'java.base java.desktop')
+ Default: all existing modules (java.* and jdk.*)"
exit 1
}
@@ -33,10 +52,13 @@ SCRIPT_DIR=`dirname $0`
#assume TOP is the dir from which the script has been called
TOP=`pwd`
cd $SCRIPT_DIR; SCRIPT_DIR=`pwd`
+if [ "x$TOPLEVEL_DIR" = "x" ] ; then
+ cd .. ; TOPLEVEL_DIR=`pwd`
+fi
cd $TOP;
-IDEA_OUTPUT=$TOP/.idea
-VERBOSE="false"
+VERBOSE=true
+ABSOLUTE_PATHS=false
CONF_ARG=
while [ $# -gt 0 ]
do
@@ -45,14 +67,24 @@ do
usage
;;
- -v | --vebose )
- VERBOSE="true"
+ -q | --quiet )
+ VERBOSE=false
+ ;;
+
+ -a | --absolute-paths )
+ ABSOLUTE_PATHS=true
+ ;;
+
+ -r | --root )
+ TOPLEVEL_DIR="$2"
+ shift
;;
-o | --output )
- IDEA_OUTPUT=$2/.idea
+ IDEA_OUTPUT="$2/.idea"
shift
;;
+
-c | --conf )
CONF_ARG="CONF_NAME=$2"
shift
@@ -69,20 +101,17 @@ do
shift
done
-if [ -e $IDEA_OUTPUT ] ; then
- rm -r $IDEA_OUTPUT
+if [ "x$IDEA_OUTPUT" = "x" ] ; then
+ IDEA_OUTPUT="$TOPLEVEL_DIR/.idea"
fi
-mkdir -p $IDEA_OUTPUT || exit 1
-cd $IDEA_OUTPUT; IDEA_OUTPUT=`pwd`
-if [ "x$TOPLEVEL_DIR" = "x" ] ; then
- cd $SCRIPT_DIR/..
- TOPLEVEL_DIR=`pwd`
- cd $IDEA_OUTPUT
-fi
+mkdir -p $IDEA_OUTPUT || exit 1
+cd "$TOP" ; cd $TOPLEVEL_DIR; TOPLEVEL_DIR=`pwd`
+cd "$TOP" ; cd $IDEA_OUTPUT; IDEA_OUTPUT=`pwd`
+cd ..; IDEA_OUTPUT_PARENT=`pwd`
+cd "$SCRIPT_DIR/.." ; OPENJDK_DIR=`pwd`
-MAKE_DIR="$SCRIPT_DIR/../make"
-IDEA_MAKE="$MAKE_DIR/ide/idea/jdk"
+IDEA_MAKE="$OPENJDK_DIR/make/ide/idea/jdk"
IDEA_TEMPLATE="$IDEA_MAKE/template"
cp -r "$IDEA_TEMPLATE"/* "$IDEA_OUTPUT"
@@ -94,31 +123,31 @@ if [ -d "$TEMPLATES_OVERRIDE" ] ; then
done
fi
-if [ "$VERBOSE" = "true" ] ; then
- echo "output dir: $IDEA_OUTPUT"
- echo "idea template dir: $IDEA_TEMPLATE"
+if [ "$VERBOSE" = true ] ; then
+ echo "Will generate IDEA project files in \"$IDEA_OUTPUT\" for project \"$TOPLEVEL_DIR\""
fi
-cd $TOP ; make -f "$IDEA_MAKE/idea.gmk" -I $MAKE_DIR/.. idea MAKEOVERRIDES= OUT=$IDEA_OUTPUT/env.cfg MODULES="$*" $CONF_ARG || exit 1
+cd $TOP ; make -f "$IDEA_MAKE/idea.gmk" -I "$OPENJDK_DIR" idea TOPLEVEL_DIR="$TOPLEVEL_DIR" \
+ MAKEOVERRIDES= IDEA_OUTPUT_PARENT="$IDEA_OUTPUT_PARENT" OUT="$IDEA_OUTPUT/env.cfg" MODULES="$*" $CONF_ARG || exit 1
cd $SCRIPT_DIR
. $IDEA_OUTPUT/env.cfg
-# Expect MODULE_ROOTS, MODULE_NAMES, BOOT_JDK & SPEC to be set
-if [ "x$MODULE_ROOTS" = "x" ] ; then
- echo "FATAL: MODULE_ROOTS is empty" >&2; exit 1
+# Expect MODULES, MODULE_NAMES, RELATIVE_PROJECT_DIR, RELATIVE_BUILD_DIR to be set
+if [ "xMODULES" = "x" ] ; then
+ echo "FATAL: MODULES is empty" >&2; exit 1
fi
if [ "x$MODULE_NAMES" = "x" ] ; then
echo "FATAL: MODULE_NAMES is empty" >&2; exit 1
fi
-if [ "x$BOOT_JDK" = "x" ] ; then
- echo "FATAL: BOOT_JDK is empty" >&2; exit 1
+if [ "x$RELATIVE_PROJECT_DIR" = "x" ] ; then
+ echo "FATAL: RELATIVE_PROJECT_DIR is empty" >&2; exit 1
fi
-if [ "x$SPEC" = "x" ] ; then
- echo "FATAL: SPEC is empty" >&2; exit 1
+if [ "x$RELATIVE_BUILD_DIR" = "x" ] ; then
+ echo "FATAL: RELATIVE_BUILD_DIR is empty" >&2; exit 1
fi
if [ -d "$TOPLEVEL_DIR/.hg" ] ; then
@@ -129,6 +158,43 @@ if [ -d "$TOPLEVEL_DIR/.git" ] ; then
VCS_TYPE="Git"
fi
+if [ "$ABSOLUTE_PATHS" = true ] ; then
+ if [ "x$PATHTOOL" != "x" ]; then
+ PROJECT_DIR="`$PATHTOOL -am $OPENJDK_DIR`"
+ TOPLEVEL_PROJECT_DIR="`$PATHTOOL -am $TOPLEVEL_DIR`"
+ else
+ PROJECT_DIR="$OPENJDK_DIR"
+ TOPLEVEL_PROJECT_DIR="$TOPLEVEL_DIR"
+ fi
+ MODULE_DIR="$PROJECT_DIR"
+ TOPLEVEL_MODULE_DIR="$TOPLEVEL_PROJECT_DIR"
+ cd "$IDEA_OUTPUT_PARENT" && cd "$RELATIVE_BUILD_DIR" && BUILD_DIR="`pwd`"
+ CLION_SCRIPT_TOPDIR="$OPENJDK_DIR"
+ CLION_PROJECT_DIR="$PROJECT_DIR"
+else
+ if [ "$RELATIVE_PROJECT_DIR" = "." ] ; then
+ PROJECT_DIR=""
+ else
+ PROJECT_DIR="/$RELATIVE_PROJECT_DIR"
+ fi
+ if [ "$RELATIVE_TOPLEVEL_PROJECT_DIR" = "." ] ; then
+ TOPLEVEL_PROJECT_DIR=""
+ else
+ TOPLEVEL_PROJECT_DIR="/$RELATIVE_TOPLEVEL_PROJECT_DIR"
+ fi
+ MODULE_DIR="\$MODULE_DIR\$$PROJECT_DIR"
+ PROJECT_DIR="\$PROJECT_DIR\$$PROJECT_DIR"
+ TOPLEVEL_MODULE_DIR="\$MODULE_DIR\$$TOPLEVEL_PROJECT_DIR"
+ TOPLEVEL_PROJECT_DIR="\$PROJECT_DIR\$$TOPLEVEL_PROJECT_DIR"
+ BUILD_DIR="\$PROJECT_DIR\$/$RELATIVE_BUILD_DIR"
+ CLION_SCRIPT_TOPDIR="$CLION_RELATIVE_PROJECT_DIR"
+ CLION_PROJECT_DIR="\$PROJECT_DIR\$/$CLION_SCRIPT_TOPDIR"
+fi
+if [ "$VERBOSE" = true ] ; then
+ echo "Project root: $PROJECT_DIR"
+ echo "Generating IDEA project files..."
+fi
+
### Replace template variables
NUM_REPLACEMENTS=0
@@ -152,116 +218,106 @@ add_replacement() {
eval TO$NUM_REPLACEMENTS='$2'
}
+add_replacement "###PATHTOOL###" "$PATHTOOL"
+add_replacement "###CLION_SCRIPT_TOPDIR###" "$CLION_SCRIPT_TOPDIR"
+add_replacement "###CLION_PROJECT_DIR###" "$CLION_PROJECT_DIR"
+add_replacement "###PROJECT_DIR###" "$PROJECT_DIR"
+add_replacement "###MODULE_DIR###" "$MODULE_DIR"
+add_replacement "###TOPLEVEL_PROJECT_DIR###" "$TOPLEVEL_PROJECT_DIR"
+add_replacement "###TOPLEVEL_MODULE_DIR###" "$TOPLEVEL_MODULE_DIR"
add_replacement "###MODULE_NAMES###" "$MODULE_NAMES"
add_replacement "###VCS_TYPE###" "$VCS_TYPE"
-SPEC_DIR=`dirname $SPEC`
-if [ "x$CYGPATH" != "x" ]; then
- add_replacement "###BUILD_DIR###" "`$CYGPATH -am $SPEC_DIR`"
- add_replacement "###IMAGES_DIR###" "`$CYGPATH -am $SPEC_DIR`/images/jdk"
- add_replacement "###ROOT_DIR###" "`$CYGPATH -am $TOPLEVEL_DIR`"
- add_replacement "###IDEA_DIR###" "`$CYGPATH -am $IDEA_OUTPUT`"
- if [ "x$JT_HOME" = "x" ]; then
- add_replacement "###JTREG_HOME###" ""
- else
- add_replacement "###JTREG_HOME###" "`$CYGPATH -am $JT_HOME`"
- fi
-elif [ "x$WSL_DISTRO_NAME" != "x" ]; then
- add_replacement "###BUILD_DIR###" "`wslpath -am $SPEC_DIR`"
- add_replacement "###IMAGES_DIR###" "`wslpath -am $SPEC_DIR`/images/jdk"
- add_replacement "###ROOT_DIR###" "`wslpath -am $TOPLEVEL_DIR`"
- add_replacement "###IDEA_DIR###" "`wslpath -am $IDEA_OUTPUT`"
+add_replacement "###BUILD_DIR###" "$BUILD_DIR"
+add_replacement "###RELATIVE_BUILD_DIR###" "$RELATIVE_BUILD_DIR"
+if [ "x$PATHTOOL" != "x" ]; then
+ add_replacement "###BASH_RUNNER_PREFIX###" "\$PROJECT_DIR\$/.idea/bash.bat"
+else
+ add_replacement "###BASH_RUNNER_PREFIX###" ""
+fi
+if [ "x$PATHTOOL" != "x" ]; then
if [ "x$JT_HOME" = "x" ]; then
add_replacement "###JTREG_HOME###" ""
else
- add_replacement "###JTREG_HOME###" "`wslpath -am $JT_HOME`"
+ add_replacement "###JTREG_HOME###" "`$PATHTOOL -am $JT_HOME`"
fi
else
- add_replacement "###BUILD_DIR###" "$SPEC_DIR"
add_replacement "###JTREG_HOME###" "$JT_HOME"
- add_replacement "###IMAGES_DIR###" "$SPEC_DIR/images/jdk"
- add_replacement "###ROOT_DIR###" "$TOPLEVEL_DIR"
- add_replacement "###IDEA_DIR###" "$IDEA_OUTPUT"
fi
-SOURCE_PREFIX=" "
-
-for root in $MODULE_ROOTS; do
- if [ "x$CYGPATH" != "x" ]; then
- root=`$CYGPATH -am $root`
- elif [ "x$WSL_DISTRO_NAME" != "x" ]; then
- root=`wslpath -am $root`
- fi
-
- SOURCES=$SOURCES" $SOURCE_PREFIX""$root""$SOURCE_POSTFIX"
+MODULE_IMLS=""
+TEST_MODULE_DEPENDENCIES=""
+for module in $MODULE_NAMES; do
+ MODULE_IMLS="$MODULE_IMLS "
+ TEST_MODULE_DEPENDENCIES="$TEST_MODULE_DEPENDENCIES "
done
-
-add_replacement "###SOURCE_ROOTS###" "$SOURCES"
+add_replacement "###MODULE_IMLS###" "$MODULE_IMLS"
+add_replacement "###TEST_MODULE_DEPENDENCIES###" "$TEST_MODULE_DEPENDENCIES"
replace_template_dir "$IDEA_OUTPUT"
-### Compile the custom Logger
-
-CLASSES=$IDEA_OUTPUT/classes
+### Generate module project files
-if [ "x$ANT_HOME" = "x" ] ; then
- # try some common locations
- if [ -f "/usr/share/ant/lib/ant.jar" ] ; then
- ANT_HOME="/usr/share/ant"
- else
- try_ant=$(ls /opt/homebrew/Cellar/ant/*/libexec/lib/ant.jar 2> /dev/null | sort -r | head -n 1)
- if [ "x$try_ant" != "x" ] ; then
- ANT_HOME=$(cd $(dirname $try_ant)/.. && pwd)
- else
- try_ant=$(ls /usr/local/Cellar/ant/*/libexec/lib/ant.jar 2> /dev/null | sort -r | head -n 1)
- if [ "x$try_ant" != "x" ] ; then
- ANT_HOME=$(cd $(dirname $try_ant)/.. && pwd)
- fi
- fi
+if [ "$VERBOSE" = true ] ; then
+ echo "Generating project modules:"
fi
-else
- if [ ! -f "$ANT_HOME/lib/ant.jar" ] ; then
- echo "FATAL: ANT_HOME is incorrect. Try removing it and use autodetection, or fix the value" >&2; exit 1
+(
+DEFAULT_IFS="$IFS"
+IFS='#'
+for value in $MODULES; do
+ (
+ eval "$value"
+ if [ "$VERBOSE" = true ] ; then
+ echo " $module"
fi
-fi
+ MAIN_SOURCE_DIRS=""
+ CONTENT_ROOTS=""
+ IFS=' '
+ for dir in $moduleSrcDirs; do
+ case $dir in
+ "src/"*) MAIN_SOURCE_DIRS="$MAIN_SOURCE_DIRS " ;;
+ *"/support/gensrc/$module") ;; # Exclude generated sources to avoid module-info conflicts, see https://youtrack.jetbrains.com/issue/IDEA-185108
+ *) CONTENT_ROOTS="$CONTENT_ROOTS \
+ " ;;
+ esac
+ done
+ if [ "x$MAIN_SOURCE_DIRS" != "x" ] ; then
+ CONTENT_ROOTS="$MAIN_SOURCE_DIRS $CONTENT_ROOTS"
+ fi
+ add_replacement "###MODULE_CONTENT_ROOTS###" "$CONTENT_ROOTS"
+ DEPENDENCIES=""
+ for dep in $moduleDependencies; do
+ case $MODULE_NAMES in # Exclude skipped modules from dependencies
+ *"$dep"*) DEPENDENCIES="$DEPENDENCIES "
+ esac
+ done
+ add_replacement "###DEPENDENCIES###" "$DEPENDENCIES"
+ cp "$IDEA_OUTPUT/module.iml" "$IDEA_OUTPUT/$module.iml"
+ IFS="$DEFAULT_IFS"
+ replace_template_file "$IDEA_OUTPUT/$module.iml"
+ )
+done
+)
+rm "$IDEA_OUTPUT/module.iml"
-if [ "x$ANT_HOME" = "x" ] ; then
- echo "FATAL: cannot find ant. Try setting ANT_HOME." >&2; exit 1
-fi
-CP=$ANT_HOME/lib/ant.jar
-rm -rf $CLASSES; mkdir $CLASSES
+### Create shell script runner for Windows
-# If we have a Windows boot JDK, we need a .exe suffix
-if [ -e "$BOOT_JDK/bin/java.exe" ] ; then
- JAVAC=javac.exe
-else
- JAVAC=javac
+if [ "x$PATHTOOL" != "x" ]; then
+ echo "@echo off" > "$IDEA_OUTPUT/bash.bat"
+ if [ "x$WSL_DISTRO_NAME" != "x" ] ; then
+ echo "wsl -d $WSL_DISTRO_NAME --cd \"%cd%\" -e %*" >> "$IDEA_OUTPUT/bash.bat"
+ else
+ echo "$WINENV_ROOT\bin\bash.exe -l -c \"cd %CD:\=/%/ && %*\"" >> "$IDEA_OUTPUT/bash.bat"
+ fi
fi
-# If we are on WSL, the boot JDK might be either Windows or Linux,
-# and we need to use realpath instead of CYGPATH to make javac work on both.
-# We need to handle this case first since CYGPATH might be set on WSL.
-if [ "x$WSL_DISTRO_NAME" != "x" ]; then
- JAVAC_SOURCE_FILE=`realpath --relative-to=./ $IDEA_OUTPUT/src/idea/IdeaLoggerWrapper.java`
- JAVAC_SOURCE_PATH=`realpath --relative-to=./ $IDEA_OUTPUT/src`
- JAVAC_CLASSES=`realpath --relative-to=./ $CLASSES`
- ANT_TEMP=`mktemp -d -p ./`
- cp $ANT_HOME/lib/ant.jar $ANT_TEMP/ant.jar
- JAVAC_CP=$ANT_TEMP/ant.jar
-elif [ "x$CYGPATH" != "x" ] ; then ## CYGPATH may be set in env.cfg
- JAVAC_SOURCE_FILE=`$CYGPATH -am $IDEA_OUTPUT/src/idea/IdeaLoggerWrapper.java`
- JAVAC_SOURCE_PATH=`$CYGPATH -am $IDEA_OUTPUT/src`
- JAVAC_CLASSES=`$CYGPATH -am $CLASSES`
- JAVAC_CP=`$CYGPATH -am $CP`
-else
- JAVAC_SOURCE_FILE=$IDEA_OUTPUT/src/idea/IdeaLoggerWrapper.java
- JAVAC_SOURCE_PATH=$IDEA_OUTPUT/src
- JAVAC_CLASSES=$CLASSES
- JAVAC_CP=$CP
-fi
-$BOOT_JDK/bin/$JAVAC -d $JAVAC_CLASSES -sourcepath $JAVAC_SOURCE_PATH -cp $JAVAC_CP $JAVAC_SOURCE_FILE
-if [ "x$WSL_DISTRO_NAME" != "x" ]; then
- rm -rf $ANT_TEMP
-fi
+if [ "$VERBOSE" = true ] ; then
+ IDEA_PROJECT_DIR="`dirname $IDEA_OUTPUT`"
+ if [ "x$PATHTOOL" != "x" ]; then
+ IDEA_PROJECT_DIR="`$PATHTOOL -am $IDEA_PROJECT_DIR`"
+ fi
+ echo "
+Now you can open \"$IDEA_PROJECT_DIR\" as IDEA project
+You can also run 'bash \"$IDEA_OUTPUT/jdk-clion/update-project.sh\"' to generate Clion project"
+fi
\ No newline at end of file
diff --git a/configure b/configure
old mode 100644
new mode 100755
diff --git a/jb/branchdiff.py b/jb/branchdiff.py
new file mode 100755
index 000000000000..81d48de0cd68
--- /dev/null
+++ b/jb/branchdiff.py
@@ -0,0 +1,274 @@
+#!/usr/bin/env python3
+
+import argparse
+import os.path
+import sys
+import subprocess
+
+errors_count = 0
+
+
+def fatal(msg):
+ sys.stderr.write(f"[fatal] {msg}\n")
+ sys.exit(1)
+
+
+def error(msg):
+ global errors_count
+ errors_count += 1
+ sys.stderr.write(f"[error] {msg}\n")
+
+
+def verbose(options, *msg):
+ if options.verbose:
+ sys.stderr.write(f"[verbose] ")
+ sys.stderr.write(*msg)
+ sys.stderr.write('\n')
+
+
+def first_line(str):
+ return "" if not str else str.splitlines()[0]
+
+
+class Options:
+ def __init__(self):
+ ap = argparse.ArgumentParser(description="Show commit differences between branches of JBR git repos",
+ epilog="Example: %(prog)s --from origin/jbr17 --to jbr17.b469 --path "
+ "src/hotspot --limit 200")
+ ap.add_argument('--jbr', dest='jbrpath', help='path to JBR git root', required=True)
+ ap.add_argument('--from', dest='frombranch', help='branch to take commits from', required=True)
+ ap.add_argument('--to', dest='tobranch', help='branch to apply new commits to', required=True)
+ ap.add_argument('--path', dest='path', help='limit to changes in this path (relative to git root)')
+ ap.add_argument('--limit', dest='limit', help='limit to this many log entries in --jdk repo', type=int,
+ default=-1)
+ ap.add_argument('--html', dest="ishtml", help="print out HTML rather than plain text", action='store_true')
+ ap.add_argument('-o', dest="output", help="print the list of missing commits to this file"
+ " to be used as exclude list later")
+ ap.add_argument('--exclude', dest='exclude', help='exclude commits listed in the given file '
+ '(can use edited -o output file as input here)')
+ ap.add_argument('-v', dest='verbose', help="verbose output", default=False, action='store_true')
+ args = ap.parse_args()
+
+ if not os.path.isdir(args.jbrpath):
+ fatal(f"{args.jbrpath} not a directory")
+
+ if not git_is_available():
+ fatal("can't run git commands; make sure git is in PATH")
+
+ self.frombranch = args.frombranch
+ self.tobranch = args.tobranch
+ self.jbrpath = args.jbrpath
+ self.path = args.path
+ self.limit = args.limit
+ self.exclude = args.exclude
+ self.output = args.output
+ self.ishtml = args.ishtml
+ self.verbose = args.verbose
+
+
+class GitRepo:
+ def __init__(self, rootpath):
+ self.rootpath = rootpath
+
+ def run_git_cmd(self, git_args):
+ args = ["git", "-C", self.rootpath]
+ args.extend(git_args)
+ # print(f"Runnig git cmd '{' '.join(args)}'")
+ p = subprocess.run(args, capture_output=True, text=True)
+ if p.returncode != 0:
+ fatal(f"git returned non-zero code in {self.rootpath} ({first_line(p.stderr)})")
+ return p.stdout
+
+ def save_git_cmd(self, fname, git_args):
+ args = ["git", "-C", self.rootpath]
+ args.extend(git_args)
+ # print(f"Runnig git cmd '{' '.join(args)}'")
+ with open(fname, "w") as stdout_file:
+ p = subprocess.run(args, stdout=stdout_file)
+ if p.returncode != 0:
+ fatal(f"git returned non-zero code in {self.rootpath} ({first_line(p.stderr)})")
+
+ def current_branch(self):
+ branch_name = self.run_git_cmd(["branch", "--show-current"]).strip()
+ return branch_name
+
+ def log(self, branch, path=None, limit=None):
+ cmds = ["log", "--no-decorate", branch]
+ if limit:
+ cmds.extend(["-n", str(limit)])
+ if path:
+ cmds.append(path)
+ full_log = self.run_git_cmd(cmds)
+ return full_log
+
+
+class Commit:
+ def __init__(self, lines):
+ self.sha = lines[0].split()[1]
+ self.message = ""
+ self.fullmessage = ""
+ self.bugid = ""
+
+ # Commit message starts after one blank line
+ read_message = False
+ for l in lines:
+ if read_message:
+ self.fullmessage += l.strip() + "\n"
+ if not read_message and l == "":
+ read_message = True
+
+ if len(self.fullmessage) > 0:
+ self.message = first_line(self.fullmessage).strip()
+ t = self.message.split(' ')
+ if len(t) > 1:
+ bugid = t[0]
+ if bugid.startswith("fixup"):
+ bugid = t[1]
+ bugid = bugid.strip(":")
+ if bugid.startswith("JBR-") or bugid.isnumeric():
+ self.bugid = bugid
+
+
+class History:
+ def __init__(self, log):
+ log_itr = iter(log.splitlines())
+ self.commits = []
+ self.unique_fullmessages = set()
+ self.duplicates = set()
+ commit_lines = []
+ for line in log_itr:
+ if line.startswith("commit ") and len(commit_lines) > 0:
+ commit = Commit(commit_lines)
+ self.add_commit(commit)
+ commit_lines = []
+ commit_lines.append(line)
+
+ if len(commit_lines) > 0:
+ commit = Commit(commit_lines)
+ self.add_commit(commit)
+
+ def add_commit(self, commit):
+ self.commits.append(commit)
+ if commit.fullmessage in self.unique_fullmessages:
+ self.duplicates.add(commit.fullmessage)
+ else:
+ self.unique_fullmessages.add(commit.fullmessage)
+
+ def appears_more_than_once(self, commit):
+ return commit.fullmessage in self.duplicates
+
+ def contains(self, commit):
+ return commit.fullmessage in self.unique_fullmessages
+
+ def size(self):
+ return len(self.commits)
+
+
+def print_explanation(options, jbr):
+ verbose(options, f"Reading history from '{jbr.rootpath}'")
+ if options.path:
+ verbose(options, f"\t(only under '{options.path}')")
+ if options.limit > 0:
+ verbose(options, f"\t(up to '{options.limit}' commits)")
+ verbose(options, f"Searching for missing fixes in '{options.tobranch}' compared with '{options.frombranch}'")
+
+
+def git_is_available():
+ p = None
+ try:
+ p = subprocess.run(["git", "--help"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
+ except:
+ pass
+ return p is not None and p.returncode == 0
+
+
+def main():
+ check_python_min_requirements()
+ options = Options()
+ jbr = GitRepo(options.jbrpath)
+ print_explanation(options, jbr)
+
+ commits_to_save = []
+ try:
+ log_from = jbr.log(options.frombranch, options.path, options.limit)
+ log_to = jbr.log(options.tobranch, options.path, options.limit)
+ history_from = History(log_from)
+ history_to = History(log_to)
+
+ verbose(options,
+ f"Read {history_from.size()} commits from '{options.frombranch}', {history_to.size()} from {options.tobranch}")
+
+ exclude_list = []
+ if options.exclude:
+ with open(options.exclude, "r") as exclude_file:
+ l = exclude_file.read().split('\n')
+ exclude_list = list(filter(lambda line: not line.startswith("#"), l))
+
+ warned = set()
+ for c in history_from.commits:
+ if c.message:
+ verbose(options, f"Looking for commit '{c.message}'")
+ if c.message in exclude_list:
+ verbose(options, "...nope, in exclude list")
+ continue
+
+ if not history_to.contains(c):
+ commits_to_save.append(c)
+ else:
+ if history_from.appears_more_than_once(c) and c.fullmessage not in warned:
+ # Not sure which of those seemingly identical commits are present in the target branch
+ error(f"Commit '{c.message}' appears more than once in branch '{options.frombranch}'. ")
+ warned.add(c.fullmessage)
+ except KeyboardInterrupt:
+ fatal("Interrupted")
+
+ print_out_commits(options, commits_to_save)
+ save_commits_to_file(commits_to_save, options)
+ if errors_count > 0:
+ error(f"{errors_count} error(s) generated to stderr. MANUAL CHECK OF COMMITS IS REQUIRED.")
+
+
+def save_commits_to_file(commits_to_save, options):
+ if len(commits_to_save) > 0 and options.output:
+ print()
+ with open(options.output, "w") as out:
+ for i, c in enumerate(reversed(commits_to_save)):
+ print(f"# {c.sha}", file=out)
+ print(c.message, file=out)
+
+
+def print_out_commits(options, commits_to_save):
+ if options.ishtml:
+ print("")
+ print(f"Commits on {options.frombranch}"
+ f" missing from {options.tobranch}
")
+ if len(commits_to_save) > 0:
+ for c in sorted(commits_to_save, key=lambda commit: commit.bugid):
+ if options.ishtml:
+ msg = c.message
+ bugurl = ""
+ if c.bugid:
+ if c.bugid.isnumeric():
+ bugurl = f"https://bugs.openjdk.org/browse/JDK-{c.bugid}"
+ elif c.bugid.startswith("JBR-"):
+ bugurl = f"https://youtrack.jetbrains.com/issue/{c.bugid}"
+
+ if len(bugurl) > 0:
+ msg = msg.replace(c.bugid, f"{c.bugid} ")
+
+ sha = f"" \
+ f"{c.sha[0:8]} "
+ print(f"{msg} ({sha}) ")
+ else:
+ print(f"{c.message} ({c.sha[0:8]})")
+ if options.ishtml:
+ print("")
+
+
+def check_python_min_requirements():
+ if sys.version_info < (3, 6):
+ fatal("Minimum version 3.6 is required to run this script")
+
+
+if __name__ == '__main__':
+ main()
diff --git a/jb/jdkdiff.py b/jb/jdkdiff.py
new file mode 100755
index 000000000000..2d9f10b68607
--- /dev/null
+++ b/jb/jdkdiff.py
@@ -0,0 +1,230 @@
+#!/usr/bin/env python3
+
+import argparse
+import math
+import os.path
+import sys
+import subprocess
+
+
+def fatal(msg):
+ sys.stderr.write(f"[fatal] {msg}\n")
+ sys.exit(1)
+
+
+def verbose(options, *msg):
+ if options.verbose:
+ sys.stdout.write(f"[verbose] ")
+ sys.stdout.write(*msg)
+ sys.stdout.write('\n')
+
+
+def first_line(str):
+ return "" if not str else str.splitlines()[0]
+
+
+class Options:
+ def __init__(self):
+ ap = argparse.ArgumentParser(description="Show bugfixes differences between JBR and OpenJDK git repos",
+ epilog="Example: %(prog)s --jdk ./jdk11u/ --jbr ./JetBrainsRuntime/ --path src/hotspot --limit 200")
+ ap.add_argument('--jdk', dest='jdkpath', help='path to OpenJDK git repo', required=True)
+ ap.add_argument('--jbr', dest='jbrpath', help='path to JBR git repo', required=True)
+ ap.add_argument('--path', dest='path', help='limit to changes in this path (relative to git root)')
+ ap.add_argument('--limit', dest='limit', help='limit to this many log entries in --jdk repo', type=int, default=-1)
+ ap.add_argument('-o', dest="output_dir", help="save patches to this directory (created if necessary)")
+ ap.add_argument('-v', dest='verbose', help="verbose output", default=False, action='store_true')
+ args = ap.parse_args()
+
+ if not os.path.isdir(args.jdkpath):
+ fatal(f"{args.jdkpath} not a directory")
+
+ if not os.path.isdir(args.jbrpath):
+ fatal(f"{args.jbrpath} not a directory")
+
+ if not git_is_available():
+ fatal("can't run git commands; make sure git is in PATH")
+
+ self.jdkpath = args.jdkpath
+ self.jbrpath = args.jbrpath
+ self.path = args.path
+ self.limit = args.limit
+ self.output_dir = args.output_dir
+ self.verbose = args.verbose
+
+
+class GitRepo:
+ def __init__(self, rootpath):
+ self.rootpath = rootpath
+
+ def run_git_cmd(self, git_args):
+ args = ["git", "-C", self.rootpath]
+ args.extend(git_args)
+ # print(f"Runnig git cmd '{' '.join(args)}'")
+ p = subprocess.run(args, capture_output=True, text=True)
+ if p.returncode != 0:
+ fatal(f"git returned non-zero code in {self.rootpath} ({first_line(p.stderr)})")
+ return p.stdout
+
+ def save_git_cmd(self, fname, git_args):
+ args = ["git", "-C", self.rootpath]
+ args.extend(git_args)
+ # print(f"Runnig git cmd '{' '.join(args)}'")
+ with open(fname, "w") as stdout_file:
+ p = subprocess.run(args, stdout=stdout_file)
+ if p.returncode != 0:
+ fatal(f"git returned non-zero code in {self.rootpath} ({first_line(p.stderr)})")
+
+ def current_branch(self):
+ branch_name = self.run_git_cmd(["branch", "--show-current"]).strip()
+ return branch_name
+
+ def log(self, path=None, limit=None):
+ cmds = ["log", "--no-decorate"]
+ if limit:
+ cmds.extend(["-n", str(limit)])
+ if path:
+ cmds.append(path)
+ full_log = self.run_git_cmd(cmds)
+ return full_log
+
+
+class Commit:
+ def __init__(self, lines):
+ self.sha = lines[0].split()[1]
+ self.message = ""
+ self.bugid = None
+
+ # Commit message starts after one blank line
+ read_message = False
+ for l in lines:
+ if read_message:
+ self.message += l + "\n"
+ if not read_message and l == "":
+ read_message = True
+
+ if self.message and self.message != "" and ":" in self.message:
+ maybe_bugid = self.message.split(":")[0].strip()
+ if 10 >= len(maybe_bugid) >= 4:
+ self.bugid = maybe_bugid
+
+
+class History:
+ def __init__(self, log):
+ log_itr = iter(log.splitlines())
+ self.commits = []
+ commit_lines = []
+ for line in log_itr:
+ if line.startswith("commit ") and len(commit_lines) > 0:
+ commit = Commit(commit_lines)
+ self.commits.append(commit)
+ commit_lines = []
+ commit_lines.append(line)
+
+ if len(commit_lines) > 0:
+ commit = Commit(commit_lines)
+ self.commits.append(commit)
+
+ def contains(self, str):
+ return any(str in commit.message for commit in self.commits)
+
+ def size(self):
+ return len(self.commits)
+
+
+def print_explanation(options, jdk, jbr):
+ verbose(options, f"Reading history from '{jdk.rootpath}' on branch '{jdk.current_branch()}'")
+ if options.path:
+ verbose(options, f"\t(only under '{options.path}')")
+ verbose(options, f"Searching for same fixes in '{jbr.rootpath}' on branch '{jbr.current_branch()}'")
+
+
+def git_is_available():
+ p = None
+ try:
+ p = subprocess.run(["git", "--help"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
+ except:
+ pass
+ return p is not None and p.returncode == 0
+
+
+def main():
+ check_python_min_requirements()
+
+ options = Options()
+
+ jdk = GitRepo(options.jdkpath)
+ jbr = GitRepo(options.jbrpath)
+
+ print_explanation(options, jdk, jbr)
+
+ commits_to_save = []
+ try:
+ jdk_log = jdk.log(options.path, options.limit)
+ jdk_history = History(jdk_log)
+
+ jbr_log = jbr.log(options.path)
+ jbr_history = History(jbr_log)
+
+ verbose(options, f"Read {jdk_history.size()} commits in JDK, {jbr_history.size()} in JBR")
+
+ for c in jdk_history.commits:
+ if c.bugid:
+ verbose(options, f"Looking for bugfix for {c.bugid}")
+ if not jbr_history.contains(c.bugid):
+ commits_to_save.append(c)
+ print(f"[note] Fix for {c.bugid} not found in JBR ({jbr.rootpath})")
+ print(f" commit {c.sha}")
+ print(f" {first_line(c.message).strip()}")
+ except KeyboardInterrupt:
+ fatal("Interrupted")
+
+ if len(commits_to_save) > 0 and options.output_dir:
+ print()
+ if not os.path.exists(options.output_dir):
+ verbose(options, f"Creating output directory {options.output_dir}")
+ os.makedirs(options.output_dir)
+ nzeroes = len(str(len(commits_to_save)))
+ for i, c in enumerate(reversed(commits_to_save)):
+ fname = os.path.join(options.output_dir, f"{str(i).zfill(nzeroes)}-{c.bugid}.patch")
+ print(f"[note] {c.bugid} saved as {fname}")
+ fname = os.path.abspath(fname)
+ jdk.save_git_cmd(fname, ["format-patch", "-1", c.sha, "--stdout"])
+
+ script_fname = os.path.join(options.output_dir, "apply.sh")
+ with open(script_fname, "w") as script_file:
+ print(apply_script_code.format(os.path.abspath(jbr.rootpath), os.path.abspath(options.output_dir)),
+ file=script_file)
+ print(f"[note] Execute 'bash {script_fname}' to apply patches to {jbr.rootpath}")
+
+
+def check_python_min_requirements():
+ if sys.version_info < (3, 6):
+ fatal("Minimum version 3.6 is required to run this script")
+
+
+apply_script_code = """
+#!/bin/bash
+
+GITROOT={0}
+PATCHROOT={1}
+
+cd $PATCHROOT || exit 1
+PATCHES=$(find $PATCHROOT -name '*.patch' | sort -n)
+
+for P in $PATCHES; do
+ git -C $GITROOT am $P
+ if [ $? != 0 ]; then
+ mv "$P" "$P.failed"
+ echo "[ERROR] Patch $P did not apply cleanly. Try applying it manually."
+ echo "[NOTE] Execute this script to apply the remaining patches."
+ exit 1
+ else
+ mv "$P" "$P.done"
+ fi
+done
+
+echo "[NOTE] Done applying patches; check $PATCHROOT for .patch and .patch.failed to see if all have been applied."
+"""
+
+if __name__ == '__main__':
+ main()
diff --git a/jb/project/docker/Dockerfile b/jb/project/docker/Dockerfile
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/jb/project/docker/Dockerfile.aarch64 b/jb/project/docker/Dockerfile.aarch64
new file mode 100644
index 000000000000..db05aa28ce03
--- /dev/null
+++ b/jb/project/docker/Dockerfile.aarch64
@@ -0,0 +1,46 @@
+# NOTE: This Dockerfile is meant to be used from the mkdocker_aarch64.sh script.
+
+# Pull a concrete version of Linux that does NOT recieve updates after it's
+# been created. This is so that the image is as stable as possible to make
+# image creation reproducible.
+# NB: this also means there may be no security-related fixes there, need to
+# move the version to the next manually.
+
+# jetbrains/runtime:jbr17env_aarch64
+FROM arm64v8/centos:7
+
+# Install the necessary build tools
+RUN yum -y update; \
+ yum -y install centos-release-scl; \
+ yum -y install devtoolset-10-10.1-0.el7; \
+ yum -y install \
+ alsa-lib-devel-1.1.8-1.el7.aarch64 \
+ autoconf-2.69-11.el7.noarch \
+ automake-1.13.4-3.el7.noarch \
+ bzip2-1.0.6-13.el7.aarch64 \
+ cups-devel-1.6.3-51.el7.aarch64 \
+ file-5.11-37.el7.aarch64 \
+ fontconfig-devel-2.13.0-4.3.el7.aarch64 \
+ freetype-devel-2.8-14.el7_9.1.aarch64 \
+ giflib-devel-4.1.6-9.el7.aarch64 \
+ git-1.8.3.1-24.el7_9.aarch64 \
+ libtool-2.4.2-22.el7_3.aarch64 \
+ libXi-devel-1.7.9-1.el7.aarch64 \
+ libXrandr-devel-1.5.1-2.el7.aarch64 \
+ libXrender-devel-0.9.10-1.el7.aarch64 \
+ libXt-devel-1.1.5-3.el7.aarch64 \
+ libXtst-devel-1.2.3-1.el7.aarch64 \
+ make-3.82-24.el7.aarch64 \
+ rsync-3.1.2-12.el7_9.aarch64 \
+ tar-1.26-35.el7.aarch64 \
+ unzip-6.0-24.el7_9.aarch64 \
+ wayland-devel-1.15.0-1.el7 \
+ zip-3.0-11.el7.aarch64; \
+ yum -y clean all
+
+ENV PATH="/opt/rh/devtoolset-10/root/usr/bin:${PATH}"
+ENV LD_LIBRARY_PATH="/opt/rh/devtoolset-10/root/usr/lib64:/opt/rh/devtoolset-10/root/usr/lib:/opt/rh/devtoolset-10/root/usr/lib64/dyninst:/opt/rh/devtoolset-10/root/usr/lib/dyninst:/opt/rh/devtoolset-10/root/usr/lib64:/opt/rh/devtoolset-10/root/usr/lib"
+ENV PKG_CONFIG_PATH="/opt/rh/devtoolset-10/root/usr/lib64/pkgconfig"
+
+RUN git config --global user.email "teamcity@jetbrains.com" && \
+ git config --global user.name "builduser"
diff --git a/jb/project/docker/Dockerfile.musl_aarch64 b/jb/project/docker/Dockerfile.musl_aarch64
new file mode 100644
index 000000000000..0a803fe91a8d
--- /dev/null
+++ b/jb/project/docker/Dockerfile.musl_aarch64
@@ -0,0 +1,22 @@
+# NOTE: This Dockerfile is meant to be used from the mkdocker_musl_aarch64.sh script.
+
+# Pull a concrete version of Linux that does NOT recieve updates after it's
+# been created. This is so that the image is as stable as possible to make
+# image creation reproducible.
+# NB: this also means there may be no security-related fixes there, need to
+# move the version to the next manually.
+FROM arm64v8/alpine:3.12
+
+# Install the necessary build tools
+RUN apk --no-cache add --update bash grep tar zip bzip2 rsync fontconfig build-base \
+ git libx11-dev libxext-dev libxrandr-dev libxrender-dev libxt-dev \
+ libxtst-dev autoconf freetype-dev cups-dev alsa-lib-dev file \
+ fontconfig fontconfig-dev linux-headers
+
+# Set up boot JDK for building
+COPY boot_jdk_musl_aarch64.tar.gz /jdk17/
+RUN cd /jdk17 && tar --strip-components=1 -xzf boot_jdk_musl_aarch64.tar.gz && rm /jdk17/boot_jdk_musl_aarch64.tar.gz
+ENV BOOT_JDK=/jdk17
+
+RUN git config --global user.email "teamcity@jetbrains.com" && \
+ git config --global user.name "builduser"
diff --git a/jb/project/docker/Dockerfile.musl_x64 b/jb/project/docker/Dockerfile.musl_x64
new file mode 100644
index 000000000000..64af9ec73247
--- /dev/null
+++ b/jb/project/docker/Dockerfile.musl_x64
@@ -0,0 +1,22 @@
+# NOTE: This Dockerfile is meant to be used from the mkdocker_musl_x64.sh script.
+
+# Pull a concrete version of Linux that does NOT recieve updates after it's
+# been created. This is so that the image is as stable as possible to make
+# image creation reproducible.
+# NB: this also means there may be no security-related fixes there, need to
+# move the version to the next manually.
+FROM alpine:3.14
+
+# Install the necessary build tools
+RUN apk --no-cache add --update bash grep tar zip bzip2 rsync fontconfig build-base \
+ git libx11-dev libxext-dev libxrandr-dev libxrender-dev libxt-dev \
+ libxtst-dev autoconf freetype-dev cups-dev alsa-lib-dev file \
+ fontconfig fontconfig-dev linux-headers
+
+# Set up boot JDK for building
+COPY boot_jdk_musl_amd64.tar.gz /jdk17/
+RUN cd /jdk17 && tar --strip-components=1 -xzf boot_jdk_musl_amd64.tar.gz && rm /jdk17/boot_jdk_musl_amd64.tar.gz
+ENV BOOT_JDK=/jdk17
+
+RUN git config --global user.email "teamcity@jetbrains.com" && \
+ git config --global user.name "builduser"
diff --git a/jb/project/docker/Dockerfile.x86 b/jb/project/docker/Dockerfile.x86
new file mode 100644
index 000000000000..d5111ac4dcd5
--- /dev/null
+++ b/jb/project/docker/Dockerfile.x86
@@ -0,0 +1,55 @@
+# NOTE: This Dockerfile is meant to be used from the mkdocker_x86.sh script.
+
+# Pull a concrete version of Linux that does NOT receive updates after it's
+# been created. This is so that the image is as stable as possible to make
+# image creation reproducible.
+# NB: this also means there may be no security-related fixes there, need to
+# move the version to the next manually.
+#FROM i386/ubuntu:xenial
+#FROM i386/ubuntu:bionic
+FROM i386/ubuntu:focal
+
+RUN linux32 \
+ apt-get update && apt-get install -y --no-install-recommends apt-utils
+RUN export DEBIAN_FRONTEND=noninteractive \
+ export DEBCONF_NONINTERACTIVE_SEEN=true && \
+ echo 'tzdata tzdata/Areas select Etc' | debconf-set-selections; \
+ echo 'tzdata tzdata/Zones/Etc select UTC' | debconf-set-selections; \
+ linux32 \
+ apt-get -y install \
+ autoconf \
+ build-essential \
+ curl \
+ file \
+ git \
+ libx11-dev \
+ libxext-dev \
+ libxrender-dev \
+ libxrandr-dev \
+ libxtst-dev \
+ libxt-dev \
+ libcups2-dev \
+ libasound2-data \
+# libpng12-0 \
+ libasound2 \
+ libfreetype6 \
+ libfontconfig1-dev \
+ libasound2-dev \
+ rsync \
+ unzip \
+ zip
+RUN linux32 \
+ apt-get -y install \
+ g++-10 \
+ gcc-10 && \
+ update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-10 100 --slave /usr/bin/g++ g++ /usr/bin/g++-10 && \
+ apt-get clean -qy && \
+ rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
+
+# Set up boot JDK for building
+COPY boot_jdk_x86.tar.gz /jdk17/
+RUN cd /jdk17 && tar --strip-components=1 -xzf boot_jdk_x86.tar.gz && rm /jdk17/boot_jdk_x86.tar.gz
+ENV BOOT_JDK=/jdk17
+
+RUN git config --global user.email "teamcity@jetbrains.com" && \
+ git config --global user.name "builduser"
diff --git a/jb/project/docker/Dockerfile.x86_64 b/jb/project/docker/Dockerfile.x86_64
new file mode 100644
index 000000000000..e61c59f00373
--- /dev/null
+++ b/jb/project/docker/Dockerfile.x86_64
@@ -0,0 +1,36 @@
+# jetbrains/runtime:jbr17env_x86_64
+FROM centos:7
+RUN yum -y install centos-release-scl; \
+ yum -y install devtoolset-10-10.1-0.el7; \
+ yum -y install \
+ alsa-lib-devel-1.1.8-1.el7 \
+ autoconf-2.69-11.el7 \
+ automake-1.13.4-3.el7 \
+ bzip2-1.0.6-13.el7 \
+ cups-devel-1.6.3-51.el7 \
+ file-5.11-37.el7 \
+ fontconfig-devel-2.13.0-4.3.el7 \
+ freetype-devel-2.8-14.el7_9.1 \
+ giflib-devel-4.1.6-9.el7 \
+ git-1.8.3.1-24.el7_9 \
+ libtool-2.4.2-22.el7_3 \
+ libXi-devel-1.7.9-1.el7 \
+ libXrandr-devel-1.5.1-2.el7 \
+ libXrender-devel-0.9.10-1.el7 \
+ libXt-devel-1.1.5-3.el7 \
+ libXtst-devel-1.2.3-1.el7 \
+ make-3.82-24.el7 \
+ tar-1.26-35.el7 \
+ unzip-6.0-24.el7_9 \
+ wayland-devel-1.15.0-1.el7 \
+ wget-1.14-18.el7_6.1 \
+ which-2.20-7.el7 \
+ zip-3.0-11.el7
+
+RUN mkdir .git && \
+ git config user.email "teamcity@jetbrains.com" && \
+ git config user.name "builduser"
+
+ENV LD_LIBRARY_PATH="/opt/rh/devtoolset-10/root/usr/lib64:/opt/rh/devtoolset-10/root/usr/lib:/opt/rh/devtoolset-10/root/usr/lib64/dyninst:/opt/rh/devtoolset-10/root/usr/lib/dyninst:/opt/rh/devtoolset-10/root/usr/lib64:/opt/rh/devtoolset-10/root/usr/lib"
+ENV PATH="/opt/rh/devtoolset-10/root/usr/bin::${PATH}"
+ENV PKG_CONFIG_PATH="/opt/rh/devtoolset-10/root/usr/lib64/pkgconfig"
\ No newline at end of file
diff --git a/jb/project/docker/mkdocker_aarch64.sh b/jb/project/docker/mkdocker_aarch64.sh
new file mode 100755
index 000000000000..9299b27425de
--- /dev/null
+++ b/jb/project/docker/mkdocker_aarch64.sh
@@ -0,0 +1,29 @@
+#!/bin/bash
+
+set -euo pipefail
+set -x
+
+# This script creates a Docker image suitable for building AArch64 variant
+# of the JetBrains Runtime "dev" version.
+
+BOOT_JDK_REMOTE_FILE=zulu17.30.15-ca-jdk17.0.1-linux_aarch64.tar.gz
+BOOT_JDK_SHA=4d9c9116eb0cdd2d7fb220d6d27059f4bf1b7e95cc93d5512bd8ce3791af86c7
+BOOT_JDK_LOCAL_FILE=boot_jdk.tar.gz
+
+if [ ! -f $BOOT_JDK_LOCAL_FILE ]; then
+ # Obtain "boot JDK" from outside of the container.
+ wget -nc https://cdn.azul.com/zulu/bin/${BOOT_JDK_REMOTE_FILE} -O $BOOT_JDK_LOCAL_FILE
+else
+ echo "boot JDK \"$BOOT_JDK_LOCAL_FILE\" present, skipping download"
+fi
+
+# Verify that what we've downloaded can be trusted.
+sha256sum -c - <
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/jb/project/idea-project-files/jetbrains.api.iml b/jb/project/idea-project-files/jetbrains.api.iml
new file mode 100644
index 000000000000..eb800fa994a6
--- /dev/null
+++ b/jb/project/idea-project-files/jetbrains.api.iml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/jb/project/idea-project-files/modules.xml b/jb/project/idea-project-files/modules.xml
new file mode 100644
index 000000000000..b0e4703bf7af
--- /dev/null
+++ b/jb/project/idea-project-files/modules.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+ ###MODULE_IMLS###
+
+
+
+
+
+
diff --git a/jb/project/idea-project-files/vcs.xml b/jb/project/idea-project-files/vcs.xml
new file mode 100644
index 000000000000..c2220951e2dd
--- /dev/null
+++ b/jb/project/idea-project-files/vcs.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/jb/project/java-gradle/build.gradle b/jb/project/java-gradle/build.gradle
new file mode 100644
index 000000000000..c5d18ab682ef
--- /dev/null
+++ b/jb/project/java-gradle/build.gradle
@@ -0,0 +1,135 @@
+apply plugin: 'java'
+import org.gradle.internal.os.OperatingSystem
+
+repositories {
+ mavenCentral()
+}
+
+def test_jvm = {
+ if (project.hasProperty('jbsdkhome')) {
+ file(jbsdkhome + (OperatingSystem.current().isWindows()?"/bin/java.exe" : "/bin/java")).absolutePath
+ } else {
+ if (OperatingSystem.current().isMacOsX()) {
+ file('../../../build/macosx-x86_64-normal-server-release/images/jdk-bundle/jdk-11.0.4.jdk/Contents/Home/bin/java').absolutePath
+ } else if (OperatingSystem.current().isLinux()) {
+ file('../../../build/linux-x86_64-normal-server-release/images/jdk/bin/java').absolutePath
+ } else {
+ file('../../../build/windows-x86_64-normal-server-release/images/jdk/bin/java.exe').absolutePath
+ }
+ }
+}
+
+dependencies {
+ testCompile('junit:junit:4.12'){
+ exclude group: 'org.hamcrest'
+ }
+ testCompile 'org.hamcrest:hamcrest-library:1.3'
+ testCompile 'net.java.dev.jna:jna:4.4.0'
+ testCompile 'com.twelvemonkeys.imageio:imageio-tiff:3.3.2'
+ testCompile 'org.apache.commons:commons-lang3:3.0'
+}
+
+def jdk_modules = ["java.base", "java.logging", "java.prefs",
+ "java.se.ee", "java.sql", "java.datatransfer",
+ "java.management", "java.rmi", "java.security.jgss",
+ "java.sql.rowset", "java.desktop", "java.management.rmi",
+ "java.scripting", "java.security.sasl", "java.transaction",
+ "java.instrument", "java.naming", "java.se",
+ "java.smartcardio", "java.xml.crypto"]
+
+def jdk_class_dirs = []
+
+jdk_modules.collect(jdk_class_dirs) {
+ new File("../../../src/" + it + "/share/classes")
+}
+
+if (OperatingSystem.current().isMacOsX())
+ jdk_modules.collect(jdk_class_dirs) {
+ "../../../src/" + it + "/macosx/classes"
+ }
+else if (OperatingSystem.current().isLinux()) {
+ jdk_modules.collect(jdk_class_dirs) {
+ "../../../src/" + it + "/solaris/classes"
+ }
+ jdk_modules.collect(jdk_class_dirs) {
+ "../../../src/" + it + "/unix/classes"
+ }
+} else
+ jdk_modules.collect(jdk_class_dirs) {
+ "../../../src/" + it + "/windows/classes"
+ }
+
+sourceSets.main.java.srcDirs = jdk_class_dirs
+
+sourceSets {
+ test {
+ java {
+ srcDir "../../../test/jdk/jbu"
+ }
+ }
+}
+
+test.dependsOn.clear()
+
+test.dependsOn tasks.compileTestJava
+
+test {
+ systemProperty "jb.java2d.metal", "true"
+ systemProperty "testdata", file('../../../test/jdk/jbu/testdata').absolutePath
+
+// Generate golden images for DroidFontTest and MixedTextTest
+// systemProperty "gentestdata", ""
+
+// Enable Java2D logging (https://confluence.jetbrains.com/display/JRE/Java2D+Rendering+Logging)
+// systemProperty "sun.java2d.trace", "log"
+// systemProperty "sun.java2d.trace", "log,pimpl"
+
+ outputs.upToDateWhen { false }
+ executable = test_jvm()
+
+// Enable async/dtrace profiler
+ jvmArgs "-XX:+PreserveFramePointer"
+// Enable native J2D logging (only in debug build)
+// Can be turned on for J2D by adding "#define DEBUG 1" into jdk/src/share/native/sun/java2d/Trace.h
+
+// environment 'J2D_TRACE_LEVEL', '4'
+}
+
+def buildDir = project.buildscript.sourceFile.parentFile.parentFile.parentFile.parentFile
+
+def make_cmd = "make"
+if (OperatingSystem.current().isWindows()) {
+ def cyg_make_cmd = new File("c:/cygwin64/bin/make.exe")
+ if (cyg_make_cmd.exists()) make_cmd = cyg_make_cmd.absolutePath
+}
+def test_run = false
+task make_images {
+ doLast {
+ if (!test_run) {
+ def pb = new ProcessBuilder().command(make_cmd.toString(), "-C", buildDir.absolutePath, "images")
+ def proc = pb.redirectErrorStream(true).start()
+ proc.inputStream.eachLine { println it }
+ assert proc.waitFor() == 0
+ }
+ }
+}
+
+task make_clean {
+ doLast {
+ def pb = new ProcessBuilder().command(make_cmd.toString(), "-C", buildDir.absolutePath, "clean")
+ def proc = pb.redirectErrorStream(true).start()
+ proc.inputStream.eachLine { println it }
+ assert proc.waitFor() == 0
+ }
+}
+
+task run_test {
+ doLast {
+ test_run = true
+ }
+}
+
+tasks.cleanTest.dependsOn tasks.run_test
+classes.dependsOn.clear()
+classes.dependsOn tasks.make_images
+tasks.cleanClasses.dependsOn tasks.make_clean
diff --git a/jb/project/tools/common/modules.list b/jb/project/tools/common/modules.list
new file mode 100644
index 000000000000..522acb7cb43b
--- /dev/null
+++ b/jb/project/tools/common/modules.list
@@ -0,0 +1,54 @@
+java.base,
+java.compiler,
+java.datatransfer,
+java.desktop,
+java.instrument,
+java.logging,
+java.management,
+java.management.rmi,
+java.naming,
+java.net.http,
+java.prefs,
+java.rmi,
+java.scripting,
+java.se,
+java.security.jgss,
+java.security.sasl,
+java.smartcardio,
+java.sql,
+java.sql.rowset,
+java.transaction.xa,
+java.xml,
+java.xml.crypto,
+jdk.accessibility,
+jdk.attach,
+jdk.charsets,
+jdk.compiler,
+jdk.crypto.cryptoki,
+jdk.crypto.ec,
+jdk.dynalink,
+jdk.httpserver,
+jdk.internal.ed,
+jdk.internal.le,
+jdk.internal.vm.ci,
+jdk.javadoc,
+jdk.jdi,
+jdk.jdwp.agent,
+jdk.jfr,
+jdk.jsobject,
+jdk.localedata,
+jdk.management,
+jdk.management.agent,
+jdk.management.jfr,
+jdk.naming.dns,
+jdk.naming.rmi,
+jdk.net,
+jdk.sctp,
+jdk.security.auth,
+jdk.security.jgss,
+jdk.unsupported,
+jdk.unsupported.desktop,
+jdk.xml.dom,
+jdk.zipfs,
+jdk.hotspot.agent,
+jdk.jcmd
diff --git a/jb/project/tools/common/scripts/build-jbr-api.sh b/jb/project/tools/common/scripts/build-jbr-api.sh
new file mode 100644
index 000000000000..48aee8225592
--- /dev/null
+++ b/jb/project/tools/common/scripts/build-jbr-api.sh
@@ -0,0 +1,18 @@
+#!/bin//bash
+
+set -euo pipefail
+
+# $1 - Boot JDK
+# $2 - JBR part of API version
+
+cd "`dirname "$0"`/../../../../.."
+PWD="`pwd`"
+CONF="$PWD/build/jbr-api.conf"
+./configure --with-debug-level=release --with-boot-jdk=$1 || exit $?
+make jbr-api CONF=release MAKEOVERRIDES= "JBR_API_CONF_FILE=$CONF" JBR_API_JBR_VERSION=$2 || exit $?
+. $CONF || exit $?
+echo "##teamcity[buildNumber '$VERSION']"
+cp "$JAR" ./jbr-api-${VERSION}.jar || exit $?
+cp "$SOURCES_JAR" ./jbr-api-${VERSION}-sources.jar || exit $?
+echo "##teamcity[publishArtifacts '$PWD/jbr-api-${VERSION}.jar']"
+echo "##teamcity[publishArtifacts '$PWD/jbr-api-${VERSION}-sources.jar']"
\ No newline at end of file
diff --git a/jb/project/tools/common/scripts/common.sh b/jb/project/tools/common/scripts/common.sh
new file mode 100644
index 000000000000..f6f586828d34
--- /dev/null
+++ b/jb/project/tools/common/scripts/common.sh
@@ -0,0 +1,186 @@
+#!/bin/bash
+
+set -euo pipefail
+set -x
+
+function check_bundle_type_maketest() {
+ # check whether last char is 't', if so remove it
+ if [ "${bundle_type: -1}" == "t" ] && [ "${bundle_type: -2}" != "ft" ]; then
+ bundle_type="${bundle_type%?}"
+ do_maketest=1
+ else
+ do_maketest=0
+ fi
+}
+
+function getVersionProp() {
+ grep "^${1}" make/conf/version-numbers.conf | cut -d'=' -f2
+}
+
+while getopts ":i?" o; do
+ case "${o}" in
+ i) INC_BUILD=1 ;;
+ esac
+done
+shift $((OPTIND-1))
+
+if [[ $# -lt 2 ]]; then
+ echo "Required at least two arguments: build_number bundle_type"
+ exit 1
+fi
+
+build_number=$1
+bundle_type=$2
+# shellcheck disable=SC2034
+architecture=${3:-x64} # aarch64 or x64
+
+check_bundle_type_maketest
+
+tag_prefix="jdk-"
+VERSION_FEATURE=$(getVersionProp "DEFAULT_VERSION_FEATURE")
+VERSION_INTERIM=$(getVersionProp "DEFAULT_VERSION_INTERIM")
+VERSION_UPDATE=$(getVersionProp "DEFAULT_VERSION_UPDATE")
+VERSION_PATCH=$(getVersionProp "DEFAULT_VERSION_PATCH")
+[[ $VERSION_UPDATE = 0 ]] && JBSDK_VERSION="$VERSION_FEATURE" || JBSDK_VERSION="${VERSION_FEATURE}.${VERSION_INTERIM}.${VERSION_UPDATE}"
+[[ $VERSION_PATCH = 0 ]] || JBSDK_VERSION="${VERSION_FEATURE}.${VERSION_INTERIM}.${VERSION_UPDATE}.${VERSION_PATCH}"
+echo "##teamcity[setParameter name='env.JBSDK_VERSION' value='${JBSDK_VERSION}']"
+OPENJDK_TAG=$(git tag -l | grep "$tag_prefix$JBSDK_VERSION" | grep -v ga | sort -t "-" -k 2 -g | tail -n 1)
+JDK_BUILD_NUMBER=${JDK_BUILD_NUMBER:=$(echo $OPENJDK_TAG | awk -F "-|[+]" '{print $3}')}
+[ -z $JDK_BUILD_NUMBER ] && JDK_BUILD_NUMBER=1
+re='^[0-9]+$'
+if ! [[ $JDK_BUILD_NUMBER =~ $re ]] ; then
+ echo "error: JDK_BUILD_NUMBER Not a number: $JDK_BUILD_NUMBER"
+ JDK_BUILD_NUMBER=1
+fi
+echo "##teamcity[setParameter name='env.JDK_UPDATE_NUMBER' value='${JDK_BUILD_NUMBER}']"
+
+VENDOR_NAME="JetBrains s.r.o."
+VENDOR_VERSION_STRING="JBR-${JBSDK_VERSION}+${JDK_BUILD_NUMBER}-${build_number}"
+[ -z "$bundle_type" ] || VENDOR_VERSION_STRING="${VENDOR_VERSION_STRING}-${bundle_type}"
+
+do_reset_changes=0
+do_reset_dcevm=0
+HEAD_REVISION=0
+
+STATIC_CONF_ARGS=""
+common_conf_props_file="jb/project/tools/common/static_conf_args.txt"
+if [[ -f "$common_conf_props_file" ]]; then
+ STATIC_CONF_ARGS=$(<$common_conf_props_file)
+fi
+OS_NAME=$(uname -s)
+# Enable reproducible builds
+TZ=UTC
+export TZ
+SOURCE_DATE_EPOCH="$(git log -1 --pretty=%ct)"
+export SOURCE_DATE_EPOCH
+
+COPYRIGHT_YEAR=""
+BUILD_TIME=""
+TOUCH_TIME=""
+REPRODUCIBLE_TAR_OPTS=""
+case "$OS_NAME" in
+ Linux)
+ COPYRIGHT_YEAR="$(date --utc --date=@$SOURCE_DATE_EPOCH +%Y)"
+ BUILD_TIME="$(date --utc --date=@$SOURCE_DATE_EPOCH +%F)"
+ REPRODUCIBLE_TAR_OPTS="--mtime=@$SOURCE_DATE_EPOCH --owner=0 --group=0 --numeric-owner --pax-option=exthdr.name=%d/PaxHeaders/%f,delete=atime,delete=ctime"
+ ;;
+ CYGWIN*)
+ COPYRIGHT_YEAR="$(date --utc --date=@$SOURCE_DATE_EPOCH +%Y)"
+ BUILD_TIME="$(date --utc --date=@$SOURCE_DATE_EPOCH +%F)"
+ REPRODUCIBLE_TAR_OPTS="--mtime=@$SOURCE_DATE_EPOCH --owner=0 --group=0 --numeric-owner --pax-option=exthdr.name=%d/PaxHeaders/%f,delete=atime,delete=ctime"
+ ;;
+ Darwin)
+ COPYRIGHT_YEAR="$(date -u -r $SOURCE_DATE_EPOCH +%Y)"
+ BUILD_TIME="$(date -u -r $SOURCE_DATE_EPOCH +%F)"
+ TOUCH_TIME="$(date -u -r $SOURCE_DATE_EPOCH +%Y%m%d%H%M.%S)"
+ REPRODUCIBLE_TAR_OPTS="--uid 0 --gid 0 --numeric-owner"
+ ;;
+esac
+
+WITH_ZIPPED_NATIVE_DEBUG_SYMBOLS="--with-native-debug-symbols=zipped"
+
+if [ "$bundle_type" == "nomodft" ]; then
+ WITH_BUNDLED_FREETYPE="--with-freetype=bundled"
+else
+ WITH_BUNDLED_FREETYPE=""
+fi
+
+REPRODUCIBLE_BUILD_OPTS="--with-source-date=$SOURCE_DATE_EPOCH
+ --with-hotspot-build-time=$BUILD_TIME
+ --with-copyright-year=$COPYRIGHT_YEAR
+ --disable-absolute-paths-in-output
+ --with-build-user=builduser"
+
+function zip_native_debug_symbols() {
+ image_bundle_path=$(echo $1 | cut -d"/" -f-4)
+ jdk_name=$(echo $1 | cut -d"/" -f5)
+ jbr_diz_name=$2
+
+ [ -d "dizfiles" ] && rm -rf dizfiles
+ mkdir dizfiles
+
+ rsync_target="../../../../dizfiles"
+ [ -z "$jdk_name" ] && rsync_target=$rsync_target"/"$jbr_diz_name
+ (cd $image_bundle_path && find . -name '*.diz' -exec rsync -R {} $rsync_target \;)
+ [ ! -z "$jdk_name" ] && mv dizfiles/$jdk_name dizfiles/$jbr_diz_name
+
+ (cd dizfiles && find $jbr_diz_name -print0 | COPYFILE_DISABLE=1 \
+ tar --no-recursion --null -T - -czf ../"$jbr_diz_name".tar.gz) || do_exit $?
+}
+
+function do_exit() {
+ exit_code=$1
+ [ $do_reset_changes -eq 1 ] && git checkout HEAD jb/project/tools/common/modules.list src/java.desktop/share/classes/module-info.java
+ if [ $do_reset_dcevm -eq 1 ]; then
+ [ ! -z $HEAD_REVISION ] && git reset --hard $HEAD_REVISION
+ fi
+ exit "$exit_code"
+}
+
+function update_jsdk_mods() {
+ __jsdk=$1
+ __jcef_mods=$2
+ __orig_jsdk_mods=$3
+ __updated_jsdk_mods=$4
+
+ # re-create java.desktop.jmod with updated module-info.class
+ tmp=.java.desktop.$$.tmp
+ mkdir "$tmp" || exit $?
+ "$__jsdk"/bin/jmod extract --dir "$tmp" "$__orig_jsdk_mods"/java.desktop.jmod || exit $?
+ "$__jsdk"/bin/javac \
+ --patch-module java.desktop="$__orig_jsdk_mods"/java.desktop.jmod \
+ --module-path "$__jcef_mods" -d "$tmp"/classes src/java.desktop/share/classes/module-info.java || exit $?
+ "$__jsdk"/bin/jmod \
+ create --class-path "$tmp"/classes --config "$tmp"/conf --header-files "$tmp"/include --legal-notice "$tmp"/legal --libs "$tmp"/lib \
+ java.desktop.jmod || exit $?
+ mv java.desktop.jmod "$__updated_jsdk_mods" || exit $?
+ rm -rf "$tmp"
+
+ # re-create java.base.jmod with updated hashes
+ tmp=.java.base.$$.tmp
+ mkdir "$tmp" || exit $?
+ hash_modules=$("$__jsdk"/bin/jmod describe "$__orig_jsdk_mods"/java.base.jmod | grep hashes | awk '{print $2}' | tr '\n' '|' | sed s/\|$//) || exit $?
+ "$__jsdk"/bin/jmod extract --dir "$tmp" "$__orig_jsdk_mods"/java.base.jmod || exit $?
+ rm "$__updated_jsdk_mods"/java.base.jmod || exit $? # temp exclude from path
+ "$__jsdk"/bin/jmod \
+ create --module-path "$__updated_jsdk_mods" --hash-modules "$hash_modules" \
+ --class-path "$tmp"/classes --cmds "$tmp"/bin --config "$tmp"/conf --header-files "$tmp"/include --legal-notice "$tmp"/legal --libs "$tmp"/lib \
+ java.base.jmod || exit $?
+ mv java.base.jmod "$__updated_jsdk_mods" || exit $?
+ rm -rf "$tmp"
+}
+
+function get_mods_list() {
+ __mods=$1
+ echo $(ls $__mods) | sed s/\.jmod/,/g | sed s/,$//g | sed s/' '//g
+}
+
+function copy_jmods() {
+ __mods_list=$1
+ __jmods_from=$2
+ __jmods_to=$3
+
+ mkdir -p $__jmods_to
+
+ echo "${__mods_list}," | while read -d, mod; do cp $__jmods_from/$mod.jmod $__jmods_to/; done
+}
diff --git a/jb/project/tools/common/static_conf_args.txt b/jb/project/tools/common/static_conf_args.txt
new file mode 100644
index 000000000000..4bf644481990
--- /dev/null
+++ b/jb/project/tools/common/static_conf_args.txt
@@ -0,0 +1,4 @@
+--with-vendor-url=https://www.jetbrains.com/
+--with-vendor-bug-url=https://youtrack.jetbrains.com/issues/JBR
+--with-vendor-vm-bug-url=https://youtrack.jetbrains.com/issues/JBR
+
diff --git a/jb/project/tools/linux/scripts/mkimages_aarch64.sh b/jb/project/tools/linux/scripts/mkimages_aarch64.sh
new file mode 100755
index 000000000000..b60d62fae44f
--- /dev/null
+++ b/jb/project/tools/linux/scripts/mkimages_aarch64.sh
@@ -0,0 +1,169 @@
+#!/bin/bash
+
+set -euo pipefail
+set -x
+
+# The following parameters must be specified:
+# build_number - specifies the number of JetBrainsRuntime build
+# bundle_type - specifies bundle to be built;possible values:
+# or nomod - the release bundles without any additional modules (jcef)
+# jcef - the release bundles with jcef
+# fd - the fastdebug bundles which also include the jcef module
+#
+# This script makes test-image along with JDK images when bundle_type is set to "jcef".
+# If the character 't' is added at the end of bundle_type then it also makes test-image along with JDK images.
+#
+# Environment variables:
+# JDK_BUILD_NUMBER - specifies update release of OpenJDK build or the value of --with-version-build argument
+# to configure
+# By default JDK_BUILD_NUMBER is set zero
+# JCEF_PATH - specifies the path to the directory with JCEF binaries.
+# By default JCEF binaries should be located in ./jcef_linux_aarch64
+
+source jb/project/tools/common/scripts/common.sh
+
+JCEF_PATH=${JCEF_PATH:=./jcef_linux_aarch64}
+
+function do_configure {
+ sh configure \
+ $WITH_DEBUG_LEVEL \
+ --with-vendor-name="$VENDOR_NAME" \
+ --with-vendor-version-string="$VENDOR_VERSION_STRING" \
+ --with-jvm-features=shenandoahgc \
+ --with-version-pre= \
+ --with-version-build="$JDK_BUILD_NUMBER" \
+ --with-version-opt=b"$build_number" \
+ --with-boot-jdk="$BOOT_JDK" \
+ --enable-cds=yes \
+ $STATIC_CONF_ARGS \
+ $REPRODUCIBLE_BUILD_OPTS \
+ $WITH_ZIPPED_NATIVE_DEBUG_SYMBOLS \
+ $WITH_BUNDLED_FREETYPE \
+ || do_exit $?
+}
+
+function is_musl {
+ libc=$(ldd /bin/ls | grep 'musl' | head -1 | cut -d ' ' -f1)
+ if [ -z $libc ]; then
+ # This is not Musl, return 1 == false
+ return 1
+ fi
+ return 0
+}
+
+function create_image_bundle {
+ __bundle_name=$1
+ __arch_name=$2
+ __modules_path=$3
+ __modules=$4
+
+ libc_type_suffix=''
+ fastdebug_infix=''
+
+ if is_musl; then libc_type_suffix='musl-' ; fi
+
+ [ "$bundle_type" == "fd" ] && [ "$__arch_name" == "$JBRSDK_BUNDLE" ] && __bundle_name=$__arch_name && fastdebug_infix="fastdebug-"
+ JBR=${__bundle_name}-${JBSDK_VERSION}-linux-${libc_type_suffix}aarch64-${fastdebug_infix}b${build_number}
+ __root_dir=${__bundle_name}-${JBSDK_VERSION}-linux-${libc_type_suffix}aarch64-${fastdebug_infix:-}b${build_number}
+
+ echo Running jlink....
+ [ -d "$IMAGES_DIR"/"$__root_dir" ] && rm -rf "${IMAGES_DIR:?}"/"$__root_dir"
+ $JSDK/bin/jlink \
+ --module-path "$__modules_path" --no-man-pages --compress=2 \
+ --generate-cds-archive --add-modules "$__modules" --output "$IMAGES_DIR"/"$__root_dir"
+
+ grep -v "^JAVA_VERSION" "$JSDK"/release | grep -v "^MODULES" >> "$IMAGES_DIR"/"$__root_dir"/release
+ if [ "$__arch_name" == "$JBRSDK_BUNDLE" ]; then
+ sed 's/JBR/JBRSDK/g' "$IMAGES_DIR"/"$__root_dir"/release > release
+ mv release "$IMAGES_DIR"/"$__root_dir"/release
+ cp $IMAGES_DIR/jdk/lib/src.zip "$IMAGES_DIR"/"$__root_dir"/lib
+ cp $IMAGES_DIR/jdk/lib/server/*.jsa "$IMAGES_DIR"/"$__root_dir"/lib/server
+ copy_jmods "$__modules" "$__modules_path" "$IMAGES_DIR"/"$__root_dir"/jmods
+ zip_native_debug_symbols $IMAGES_DIR/jdk "${JBR}_diz"
+ fi
+
+ # jmod does not preserve file permissions (JDK-8173610)
+ [ -f "$IMAGES_DIR"/"$__root_dir"/lib/jcef_helper ] && chmod a+x "$IMAGES_DIR"/"$__root_dir"/lib/jcef_helper
+
+ echo Creating "$JBR".tar.gz ...
+
+ (cd "$IMAGES_DIR" &&
+ find "$__root_dir" -print0 | LC_ALL=C sort -z | \
+ tar $REPRODUCIBLE_TAR_OPTS \
+ --no-recursion --null -T - -cf "$JBR".tar) || do_exit $?
+ mv "$IMAGES_DIR"/"$JBR".tar ./"$JBR".tar
+ [ -f "$JBR".tar.gz ] && rm "$JBR.tar.gz"
+ touch -c -d "@$SOURCE_DATE_EPOCH" "$JBR".tar
+ gzip "$JBR".tar || do_exit $?
+ rm -rf "${IMAGES_DIR:?}"/"$__root_dir"
+}
+
+WITH_DEBUG_LEVEL="--with-debug-level=release"
+RELEASE_NAME=linux-aarch64-server-release
+
+jbr_name_postfix=""
+
+case "$bundle_type" in
+ "jcef")
+ do_reset_changes=1
+ jbr_name_postfix="_${bundle_type}"
+ do_maketest=1
+ ;;
+ "nomod" | "")
+ bundle_type=""
+ ;;
+ "nomodft" | "")
+ jbr_name_postfix="_ft"
+ ;;
+ "fd")
+ do_reset_changes=1
+ jbr_name_postfix="_${bundle_type}"
+ WITH_DEBUG_LEVEL="--with-debug-level=fastdebug"
+ RELEASE_NAME=linux-aarch64-server-fastdebug
+ ;;
+esac
+
+if [ -z "${INC_BUILD:-}" ]; then
+ do_configure || do_exit $?
+ make clean CONF=$RELEASE_NAME || do_exit $?
+fi
+make images CONF=$RELEASE_NAME || do_exit $?
+
+IMAGES_DIR=build/$RELEASE_NAME/images
+JSDK=$IMAGES_DIR/jdk
+JSDK_MODS_DIR=$IMAGES_DIR/jmods
+JBRSDK_BUNDLE=jbrsdk
+
+echo Fixing permissions
+chmod -R a+r $JSDK
+
+if [ "$bundle_type" == "jcef" ] || [ "$bundle_type" == "fd" ]; then
+ git apply -p0 < jb/project/tools/patches/add_jcef_module_aarch64.patch || do_exit $?
+ update_jsdk_mods $JSDK $JCEF_PATH/jmods $JSDK/jmods $JSDK_MODS_DIR || do_exit $?
+ cp $JCEF_PATH/jmods/* $JSDK_MODS_DIR # $JSDK/jmods is not changed
+ cat $JCEF_PATH/jcef.version >> $JSDK/release
+fi
+
+# create runtime image bundle
+modules=$(xargs < jb/project/tools/common/modules.list | sed s/" "//g) || do_exit $?
+create_image_bundle "jbr${jbr_name_postfix}" "jbr" $JSDK_MODS_DIR "$modules" || do_exit $?
+
+# create sdk image bundle
+modules=$(cat $JSDK/release | grep MODULES | sed s/MODULES=//g | sed s/' '/','/g | sed s/\"//g | sed s/\\n//g) || do_exit $?
+if [ "$bundle_type" == "jcef" ] || [ "$bundle_type" == "fd" ] || [ "$bundle_type" == "$JBRSDK_BUNDLE" ]; then
+ modules=${modules},$(get_mods_list "$JCEF_PATH"/jmods)
+fi
+create_image_bundle "$JBRSDK_BUNDLE${jbr_name_postfix}" $JBRSDK_BUNDLE $JSDK_MODS_DIR "$modules" || do_exit $?
+
+if [ $do_maketest -eq 1 ]; then
+ JBRSDK_TEST=${JBRSDK_BUNDLE}-${JBSDK_VERSION}-linux-${libc_type_suffix}test-aarch64-b${build_number}
+ echo Creating "$JBRSDK_TEST" ...
+ [ $do_reset_changes -eq 1 ] && git checkout HEAD jb/project/tools/common/modules.list src/java.desktop/share/classes/module-info.java
+ make test-image jbr-api CONF=$RELEASE_NAME JBR_API_JBR_VERSION=TEST || do_exit $?
+ cp "build/${RELEASE_NAME}/jbr-api/jbr-api.jar" "${IMAGES_DIR}/test"
+ tar -pcf "$JBRSDK_TEST".tar -C $IMAGES_DIR --exclude='test/jdk/demos' test || do_exit $?
+ [ -f "$JBRSDK_TEST.tar.gz" ] && rm "$JBRSDK_TEST.tar.gz"
+ gzip "$JBRSDK_TEST".tar || do_exit $?
+fi
+
+do_exit 0
diff --git a/jb/project/tools/linux/scripts/mkimages_x64.sh b/jb/project/tools/linux/scripts/mkimages_x64.sh
new file mode 100755
index 000000000000..632f7275d6b9
--- /dev/null
+++ b/jb/project/tools/linux/scripts/mkimages_x64.sh
@@ -0,0 +1,172 @@
+#!/bin/bash
+
+set -euo pipefail
+set -x
+
+# The following parameters must be specified:
+# build_number - specifies the number of JetBrainsRuntime build
+# bundle_type - specifies bundle to be built;possible values:
+# or nomod - the release bundles without any additional modules (jcef)
+# jcef - the release bundles with jcef
+# fd - the fastdebug bundles which also include the jcef module
+#
+# This script makes test-image along with JDK images when bundle_type is set to "jcef".
+# If the character 't' is added at the end of bundle_type then it also makes test-image along with JDK images.
+#
+# Environment variables:
+# JDK_BUILD_NUMBER - specifies update release of OpenJDK build or the value of --with-version-build argument
+# to configure
+# By default JDK_BUILD_NUMBER is set zero
+# JCEF_PATH - specifies the path to the directory with JCEF binaries.
+# By default JCEF binaries should be located in ./jcef_linux_x64
+
+source jb/project/tools/common/scripts/common.sh
+
+JCEF_PATH=${JCEF_PATH:=./jcef_linux_x64}
+
+function do_configure {
+ sh configure \
+ $WITH_DEBUG_LEVEL \
+ --with-vendor-name="$VENDOR_NAME" \
+ --with-vendor-version-string="$VENDOR_VERSION_STRING" \
+ --with-jvm-features=shenandoahgc \
+ --with-version-pre= \
+ --with-version-build="$JDK_BUILD_NUMBER" \
+ --with-version-opt=b"$build_number" \
+ --with-boot-jdk="$BOOT_JDK" \
+ --enable-cds=yes \
+ $STATIC_CONF_ARGS \
+ $REPRODUCIBLE_BUILD_OPTS \
+ $WITH_ZIPPED_NATIVE_DEBUG_SYMBOLS \
+ $WITH_BUNDLED_FREETYPE \
+ || do_exit $?
+}
+
+function is_musl {
+ libc=$(ldd /bin/ls | grep 'musl' | head -1 | cut -d ' ' -f1)
+ if [ -z $libc ]; then
+ # This is not Musl, return 1 == false
+ return 1
+ fi
+ return 0
+}
+
+function create_image_bundle {
+ __bundle_name=$1
+ __arch_name=$2
+ __modules_path=$3
+ __modules=$4
+
+ libc_type_suffix=''
+ fastdebug_infix=''
+
+ if is_musl; then libc_type_suffix='musl-' ; fi
+
+ [ "$bundle_type" == "fd" ] && [ "$__arch_name" == "$JBRSDK_BUNDLE" ] && __bundle_name=$__arch_name && fastdebug_infix="fastdebug-"
+ JBR=${__bundle_name}-${JBSDK_VERSION}-linux-${libc_type_suffix}x64-${fastdebug_infix}b${build_number}
+ __root_dir=${__bundle_name}-${JBSDK_VERSION}-linux-${libc_type_suffix}x64-${fastdebug_infix:-}b${build_number}
+
+ echo Running jlink....
+ [ -d "$IMAGES_DIR"/"$__root_dir" ] && rm -rf "${IMAGES_DIR:?}"/"$__root_dir"
+ $JSDK/bin/jlink \
+ --module-path "$__modules_path" --no-man-pages --compress=2 \
+ --generate-cds-archive --add-modules "$__modules" --output "$IMAGES_DIR"/"$__root_dir"
+
+ grep -v "^JAVA_VERSION" "$JSDK"/release | grep -v "^MODULES" >> "$IMAGES_DIR"/"$__root_dir"/release
+ if [ "$__arch_name" == "$JBRSDK_BUNDLE" ]; then
+ sed 's/JBR/JBRSDK/g' "$IMAGES_DIR"/"$__root_dir"/release > release
+ mv release "$IMAGES_DIR"/"$__root_dir"/release
+ cp $IMAGES_DIR/jdk/lib/src.zip "$IMAGES_DIR"/"$__root_dir"/lib
+ cp $IMAGES_DIR/jdk/lib/server/*.jsa "$IMAGES_DIR"/"$__root_dir"/lib/server
+ copy_jmods "$__modules" "$__modules_path" "$IMAGES_DIR"/"$__root_dir"/jmods
+ zip_native_debug_symbols $IMAGES_DIR/jdk "${JBR}_diz"
+ fi
+
+ # jmod does not preserve file permissions (JDK-8173610)
+ [ -f "$IMAGES_DIR"/"$__root_dir"/lib/jcef_helper ] && chmod a+x "$IMAGES_DIR"/"$__root_dir"/lib/jcef_helper
+
+ echo Creating "$JBR".tar.gz ...
+
+ (cd "$IMAGES_DIR" &&
+ find "$__root_dir" -print0 | LC_ALL=C sort -z | \
+ tar $REPRODUCIBLE_TAR_OPTS \
+ --no-recursion --null -T - -cf "$JBR".tar) || do_exit $?
+ mv "$IMAGES_DIR"/"$JBR".tar ./"$JBR".tar
+ [ -f "$JBR".tar.gz ] && rm "$JBR.tar.gz"
+ touch -c -d "@$SOURCE_DATE_EPOCH" "$JBR".tar
+ gzip "$JBR".tar || do_exit $?
+ rm -rf "${IMAGES_DIR:?}"/"$__root_dir"
+}
+
+WITH_DEBUG_LEVEL="--with-debug-level=release"
+RELEASE_NAME=linux-x86_64-server-release
+
+jbr_name_postfix=""
+
+case "$bundle_type" in
+ "jcef")
+ do_reset_changes=1
+ jbr_name_postfix="_${bundle_type}"
+ jbrsdk_name_postfix="_${bundle_type}"
+ do_maketest=1
+ ;;
+ "nomod" | "")
+ bundle_type=""
+ jbrsdk_name_postfix="_${bundle_type}"
+ ;;
+ "nomodft" | "")
+ jbr_name_postfix="_ft"
+ jbrsdk_name_postfix="_ft"
+ ;;
+ "fd")
+ do_reset_changes=1
+ jbr_name_postfix="_${bundle_type}"
+ WITH_DEBUG_LEVEL="--with-debug-level=fastdebug"
+ RELEASE_NAME=linux-x86_64-server-fastdebug
+ ;;
+esac
+
+if [ -z "${INC_BUILD:-}" ]; then
+ do_configure || do_exit $?
+ make clean CONF=$RELEASE_NAME || do_exit $?
+fi
+make images CONF=$RELEASE_NAME || do_exit $?
+
+IMAGES_DIR=build/$RELEASE_NAME/images
+JSDK=$IMAGES_DIR/jdk
+JSDK_MODS_DIR=$IMAGES_DIR/jmods
+JBRSDK_BUNDLE=jbrsdk
+
+echo Fixing permissions
+chmod -R a+r $JSDK
+
+if [ "$bundle_type" == "jcef" ] || [ "$bundle_type" == "fd" ]; then
+ git apply -p0 < jb/project/tools/patches/add_jcef_module.patch || do_exit $?
+ update_jsdk_mods $JSDK $JCEF_PATH/jmods $JSDK/jmods $JSDK_MODS_DIR || do_exit $?
+ cp $JCEF_PATH/jmods/* $JSDK_MODS_DIR # $JSDK/jmods is not changed
+ cat $JCEF_PATH/jcef.version >> $JSDK/release
+fi
+
+# create runtime image bundle
+modules=$(xargs < jb/project/tools/common/modules.list | sed s/" "//g) || do_exit $?
+create_image_bundle "jbr${jbr_name_postfix}" "jbr" $JSDK_MODS_DIR "$modules" || do_exit $?
+
+# create sdk image bundle
+modules=$(cat $JSDK/release | grep MODULES | sed s/MODULES=//g | sed s/' '/','/g | sed s/\"//g | sed s/\\n//g) || do_exit $?
+if [ "$bundle_type" == "jcef" ] || [ "$bundle_type" == "fd" ] || [ "$bundle_type" == "$JBRSDK_BUNDLE" ]; then
+ modules=${modules},$(get_mods_list "$JCEF_PATH"/jmods)
+fi
+create_image_bundle "$JBRSDK_BUNDLE${jbr_name_postfix}" $JBRSDK_BUNDLE $JSDK_MODS_DIR "$modules" || do_exit $?
+
+if [ $do_maketest -eq 1 ]; then
+ JBRSDK_TEST=${JBRSDK_BUNDLE}-${JBSDK_VERSION}-linux-${libc_type_suffix}test-x64-b${build_number}
+ echo Creating "$JBRSDK_TEST" ...
+ [ $do_reset_changes -eq 1 ] && git checkout HEAD jb/project/tools/common/modules.list src/java.desktop/share/classes/module-info.java
+ make test-image jbr-api CONF=$RELEASE_NAME JBR_API_JBR_VERSION=TEST || do_exit $?
+ cp "build/${RELEASE_NAME}/jbr-api/jbr-api.jar" "${IMAGES_DIR}/test"
+ tar -pcf "$JBRSDK_TEST".tar -C $IMAGES_DIR --exclude='test/jdk/demos' test || do_exit $?
+ [ -f "$JBRSDK_TEST.tar.gz" ] && rm "$JBRSDK_TEST.tar.gz"
+ gzip "$JBRSDK_TEST".tar || do_exit $?
+fi
+
+do_exit 0
diff --git a/jb/project/tools/linux/scripts/mkimages_x86.sh b/jb/project/tools/linux/scripts/mkimages_x86.sh
new file mode 100755
index 000000000000..a56f4b959377
--- /dev/null
+++ b/jb/project/tools/linux/scripts/mkimages_x86.sh
@@ -0,0 +1,145 @@
+#!/bin/bash
+
+set -euo pipefail
+set -x
+
+# The following parameters must be specified:
+# build_number - specifies the number of JetBrainsRuntime build
+# bundle_type - specifies bundle to be built;possible values:
+# or nomod - the release bundles without any additional modules (jcef)
+# jcef - the release bundles with jcef
+# fd - the fastdebug bundles which also include the jcef module
+#
+
+source jb/project/tools/common/scripts/common.sh
+
+function do_configure {
+ linux32 bash configure \
+ $WITH_DEBUG_LEVEL \
+ --with-vendor-name="$VENDOR_NAME" \
+ --with-vendor-version-string="$VENDOR_VERSION_STRING" \
+ --with-jvm-features=shenandoahgc \
+ --with-version-pre= \
+ --with-version-build="$JDK_BUILD_NUMBER" \
+ --with-version-opt=b"$build_number" \
+ --with-boot-jdk="$BOOT_JDK" \
+ $STATIC_CONF_ARGS \
+ --enable-cds=yes \
+ $REPRODUCIBLE_BUILD_OPTS \
+ $WITH_ZIPPED_NATIVE_DEBUG_SYMBOLS \
+ || do_exit $?
+}
+
+function is_musl {
+ libc=$(ldd /bin/ls | grep 'musl' | head -1 | cut -d ' ' -f1)
+ if [ -z $libc ]; then
+ # This is not Musl, return 1 == false
+ return 1
+ fi
+ return 0
+}
+
+function create_image_bundle {
+ __bundle_name=$1
+ __arch_name=$2
+ __modules_path=$3
+ __modules=$4
+
+ libc_type_suffix=''
+ fastdebug_infix=''
+
+ if is_musl; then libc_type_suffix='musl-' ; fi
+
+ [ "$bundle_type" == "fd" ] && [ "$__arch_name" == "$JBRSDK_BUNDLE" ] && __bundle_name=$__arch_name && fastdebug_infix="fastdebug-"
+ JBR=${__bundle_name}-${JBSDK_VERSION}-linux-${libc_type_suffix}x86-${fastdebug_infix}b${build_number}
+ __root_dir=${__bundle_name}-${JBSDK_VERSION}-linux-${libc_type_suffix}x86-${fastdebug_infix:-}b${build_number}
+
+ echo Running jlink....
+ [ -d "$IMAGES_DIR"/"$__root_dir" ] && rm -rf "${IMAGES_DIR:?}"/"$__root_dir"
+ $JSDK/bin/jlink \
+ --module-path "$__modules_path" --no-man-pages --compress=2 \
+ --generate-cds-archive --add-modules "$__modules" --output "$IMAGES_DIR"/"$__root_dir"
+
+ grep -v "^JAVA_VERSION" "$JSDK"/release | grep -v "^MODULES" >> "$IMAGES_DIR"/"$__root_dir"/release
+ if [ "$__arch_name" == "$JBRSDK_BUNDLE" ]; then
+ sed 's/JBR/JBRSDK/g' "$IMAGES_DIR"/"$__root_dir"/release > release
+ mv release "$IMAGES_DIR"/"$__root_dir"/release
+ cp $IMAGES_DIR/jdk/lib/src.zip "$IMAGES_DIR"/"$__root_dir"/lib
+ cp $IMAGES_DIR/jdk/lib/server/*.jsa "$IMAGES_DIR"/"$__root_dir"/lib/server
+ copy_jmods "$__modules" "$__modules_path" "$IMAGES_DIR"/"$__root_dir"/jmods
+ zip_native_debug_symbols $IMAGES_DIR/jdk "${JBR}_diz"
+ fi
+
+ # jmod does not preserve file permissions (JDK-8173610)
+ [ -f "$IMAGES_DIR"/"$__root_dir"/lib/jcef_helper ] && chmod a+x "$IMAGES_DIR"/"$__root_dir"/lib/jcef_helper
+
+ echo Creating "$JBR".tar.gz ...
+
+ (cd "$IMAGES_DIR" &&
+ find "$__root_dir" -print0 | LC_ALL=C sort -z | \
+ tar $REPRODUCIBLE_TAR_OPTS \
+ --no-recursion --null -T - -cf "$JBR".tar) || do_exit $?
+ mv "$IMAGES_DIR"/"$JBR".tar ./"$JBR".tar
+ [ -f "$JBR".tar.gz ] && rm "$JBR.tar.gz"
+ touch -c -d "@$SOURCE_DATE_EPOCH" "$JBR".tar
+ gzip "$JBR".tar || do_exit $?
+ rm -rf "${IMAGES_DIR:?}"/"$__root_dir"
+}
+
+WITH_DEBUG_LEVEL="--with-debug-level=release"
+RELEASE_NAME=linux-x86-server-release
+
+case "$bundle_type" in
+ "jcef")
+ echo "not implemented" && do_exit 1
+ ;;
+ "nomod" | "")
+ bundle_type=""
+ ;;
+ "fd")
+ do_reset_changes=1
+ WITH_DEBUG_LEVEL="--with-debug-level=fastdebug"
+ RELEASE_NAME=linux-x86-server-fastdebug
+ ;;
+esac
+
+if [ -z "${INC_BUILD:-}" ]; then
+ do_configure || do_exit $?
+ make clean CONF=$RELEASE_NAME || do_exit $?
+fi
+make images CONF=$RELEASE_NAME || do_exit $?
+
+IMAGES_DIR=build/$RELEASE_NAME/images
+JSDK=$IMAGES_DIR/jdk
+JSDK_MODS_DIR=$IMAGES_DIR/jmods
+JBRSDK_BUNDLE=jbrsdk
+
+echo Fixing permissions
+chmod -R a+r $JSDK
+
+if [ "$bundle_type" == "jcef" ] || [ "$bundle_type" == "fd" ]; then
+ jbr_name_postfix="_${bundle_type}"
+else
+ jbr_name_postfix=""
+fi
+
+# create runtime image bundle
+modules=$(grep -v "jdk.internal.vm" jb/project/tools/common/modules.list | xargs | sed s/" "//g) || do_exit $?
+create_image_bundle "jbr${jbr_name_postfix}" "jbr" $JSDK_MODS_DIR "$modules" || do_exit $?
+
+# create sdk image bundle
+modules=$(cat $JSDK/release | grep MODULES | sed s/MODULES=//g | sed s/' '/','/g | sed s/\"//g | sed s/\\n//g) || do_exit $?
+create_image_bundle "$JBRSDK_BUNDLE${jbr_name_postfix}" $JBRSDK_BUNDLE $JSDK_MODS_DIR "$modules" || do_exit $?
+
+if [ $do_maketest -eq 1 ]; then
+ JBRSDK_TEST=${JBRSDK_BUNDLE}-${JBSDK_VERSION}-linux-${libc_type_suffix}test-x86-b${build_number}
+ echo Creating "$JBRSDK_TEST" ...
+ [ $do_reset_changes -eq 1 ] && git checkout HEAD jb/project/tools/common/modules.list src/java.desktop/share/classes/module-info.java
+ make test-image jbr-api CONF=$RELEASE_NAME JBR_API_JBR_VERSION=TEST || do_exit $?
+ cp "build/${RELEASE_NAME}/jbr-api/jbr-api.jar" "${IMAGES_DIR}/test"
+ tar -pcf "$JBRSDK_TEST".tar -C $IMAGES_DIR --exclude='test/jdk/demos' test || do_exit $?
+ [ -f "$JBRSDK_TEST.tar.gz" ] && rm "$JBRSDK_TEST.tar.gz"
+ gzip "$JBRSDK_TEST".tar || do_exit $?
+fi
+
+do_exit 0
diff --git a/jb/project/tools/mac/scripts/codesign.sh b/jb/project/tools/mac/scripts/codesign.sh
new file mode 100755
index 000000000000..ae5c7400441d
--- /dev/null
+++ b/jb/project/tools/mac/scripts/codesign.sh
@@ -0,0 +1,47 @@
+#!/bin/bash
+SCRIPT_DIR="$(cd "$(dirname "$0")" >/dev/null && pwd)"
+
+source "$SCRIPT_DIR/jetsign-common.sh" || exit 1
+
+function isMacOsBinary() {
+ file "$1" | grep -q 'Mach-O'
+}
+
+function isSigned() {
+ codesign --verify "$1" >/dev/null 2>&1 && ! grep -q Signature=adhoc < <(codesign --display --verbose "$1" 2>&1)
+}
+
+# last argument is a path to be signed
+pathToBeSigned="$(pwd)/${*: -1}"
+jetSignArgs=("${@:1:$#-1}")
+if [[ ! -f "$pathToBeSigned" ]]; then
+ echo "$pathToBeSigned is missing or not a file"
+ exit 1
+elif isSigned "$pathToBeSigned" && ! isForced "${jetSignArgs[@]}" ; then
+ echo "Already signed: $pathToBeSigned"
+elif [[ "$JETSIGN_CLIENT" == "null" ]]; then
+ echo "JetSign client is missing, cannot proceed with signing"
+ exit 1
+elif ! isMacOsBinary "$pathToBeSigned" && [[ "$pathToBeSigned" != *.sit ]] && [[ "$pathToBeSigned" != *.tar.gz ]]; then
+ echo "$pathToBeSigned won't be signed, assumed not to be a macOS executable"
+else
+ if isMacOsBinary "$pathToBeSigned" && ! isSigned "$pathToBeSigned" ; then
+ echo "Unsigned macOS binary: $pathToBeSigned"
+ fi
+ workDir=$(dirname "$pathToBeSigned")
+ pathSigned="$workDir/signed/${pathToBeSigned##*/}"
+ jetSignExtensions=$(jetSignExtensions "${jetSignArgs[@]}")
+ contentType=$(jetSignContentType "$pathToBeSigned")
+ (
+ cd "$workDir" || exit 1
+ "$JETSIGN_CLIENT" -log-format text -denoted-content-type "$contentType" -extensions "$jetSignExtensions" "$pathToBeSigned"
+ # SRE-1223 (Codesign removes execute bits in executable files) workaround
+ chmod "$(stat -f %A "$pathToBeSigned")" "$pathSigned"
+ if isMacOsBinary "$pathSigned"; then
+ isSigned "$pathSigned"
+ fi
+ rm "$pathToBeSigned"
+ mv "$pathSigned" "$pathToBeSigned"
+ rm -rf "$workDir/signed"
+ )
+fi
diff --git a/jb/project/tools/mac/scripts/entitlements.xml b/jb/project/tools/mac/scripts/entitlements.xml
new file mode 100644
index 000000000000..9d4904529342
--- /dev/null
+++ b/jb/project/tools/mac/scripts/entitlements.xml
@@ -0,0 +1,16 @@
+
+
+
+
+ com.apple.security.cs.allow-jit
+
+ com.apple.security.cs.allow-unsigned-executable-memory
+
+ com.apple.security.cs.allow-dyld-environment-variables
+
+ com.apple.security.cs.disable-library-validation
+
+ com.apple.security.cs.disable-executable-page-protection
+
+
+
\ No newline at end of file
diff --git a/jb/project/tools/mac/scripts/jetsign-common.sh b/jb/project/tools/mac/scripts/jetsign-common.sh
new file mode 100755
index 000000000000..4153ba80dfd3
--- /dev/null
+++ b/jb/project/tools/mac/scripts/jetsign-common.sh
@@ -0,0 +1,63 @@
+#!/bin/bash
+set -euo pipefail
+
+function isForced() {
+ for arg in "$@"; do
+ if [[ "$arg" == --force ]]; then
+ return 0
+ fi
+ done
+ return 1
+}
+
+function jetSignExtensions() {
+ args=("$@")
+ ((lastElementIndex=${#args[@]}-1))
+ for index in "${!args[@]}"; do
+ arg=${args[$index]}
+ case "$arg" in
+ --sign | -s)
+ echo -n 'mac_codesign_identity='
+ continue
+ ;;
+ --entitlements)
+ echo -n 'mac_codesign_entitlements='
+ continue
+ ;;
+ --options=runtime)
+ echo -n 'mac_codesign_options=runtime'
+ ;;
+ --force)
+ echo -n 'mac_codesign_force=true'
+ ;;
+ --timestamp | --verbose | -v)
+ continue
+ ;;
+ *)
+ echo -n "$arg"
+ ;;
+ esac
+ if [[ $index != "$lastElementIndex" ]]; then
+ echo -n ","
+ fi
+ done
+}
+
+# See jetbrains.sign.util.FileUtil.contentType
+function jetSignContentType() {
+ case "${1##*/}" in
+ *.sit)
+ echo -n 'application/x-mac-app-zip'
+ ;;
+ *.tar.gz)
+ echo -n 'application/x-mac-app-targz'
+ ;;
+ *.pkg)
+ echo -n 'application/x-mac-pkg'
+ ;;
+ *)
+ echo -n 'application/x-mac-app-bin'
+ ;;
+ esac
+}
+
diff --git a/jb/project/tools/mac/scripts/mkimages.sh b/jb/project/tools/mac/scripts/mkimages.sh
new file mode 100755
index 000000000000..5b0e818226fa
--- /dev/null
+++ b/jb/project/tools/mac/scripts/mkimages.sh
@@ -0,0 +1,169 @@
+#!/bin/bash
+
+set -euo pipefail
+set -x
+
+# The following parameters must be specified:
+# build_number - specifies the number of JetBrainsRuntime build
+# bundle_type - specifies bundle to be built;possible values:
+# or nomod - the release bundles without any additional modules (jcef)
+# jcef - the release bundles with jcef
+# fd - the fastdebug bundles which also include the jcef module
+#
+# This script makes test-image along with JDK images when bundle_type is set to "jcef".
+# If the character 't' is added at the end of bundle_type then it also makes test-image along with JDK images.
+#
+# Environment variables:
+# JDK_BUILD_NUMBER - specifies update release of OpenJDK build or the value of --with-version-build argument
+# to configure
+# By default JDK_BUILD_NUMBER is set zero
+# JCEF_PATH - specifies the path to the directory with JCEF binaries.
+# By default JCEF binaries should be located in ./jcef_mac
+
+source jb/project/tools/common/scripts/common.sh
+
+JCEF_PATH=${JCEF_PATH:=./jcef_mac}
+BOOT_JDK=${BOOT_JDK:=$(/usr/libexec/java_home -v 17)}
+
+function do_configure {
+ sh configure \
+ $WITH_DEBUG_LEVEL \
+ --with-vendor-name="$VENDOR_NAME" \
+ --with-vendor-version-string="$VENDOR_VERSION_STRING" \
+ --with-macosx-bundle-name-base=${VENDOR_VERSION_STRING} \
+ --with-macosx-bundle-id-base="com.jetbrains.jbr" \
+ --with-jvm-features=shenandoahgc \
+ --with-version-pre= \
+ --with-version-build="$JDK_BUILD_NUMBER" \
+ --with-version-opt=b"$build_number" \
+ --with-boot-jdk="$BOOT_JDK" \
+ --enable-cds=yes \
+ $STATIC_CONF_ARGS \
+ $REPRODUCIBLE_BUILD_OPTS \
+ $WITH_ZIPPED_NATIVE_DEBUG_SYMBOLS \
+ || do_exit $?
+}
+
+function create_image_bundle {
+ __bundle_name=$1
+ __arch_name=$2
+ __modules_path=$3
+ __modules=$4
+
+ fastdebug_infix=''
+
+ tmp=.bundle.$$.tmp
+ mkdir "$tmp" || do_exit $?
+
+ [ "$bundle_type" == "fd" ] && [ "$__arch_name" == "$JBRSDK_BUNDLE" ] && __bundle_name=$__arch_name && fastdebug_infix="fastdebug-"
+ JBR=${__bundle_name}-${JBSDK_VERSION}-osx-${architecture}-${fastdebug_infix:-}b${build_number}
+ __root_dir=${__bundle_name}-${JBSDK_VERSION}-osx-${architecture}-${fastdebug_infix:-}b${build_number}
+
+ JRE_CONTENTS=$tmp/$__root_dir/Contents
+ mkdir -p "$JRE_CONTENTS" || do_exit $?
+
+ echo Running jlink...
+ "$JSDK"/bin/jlink \
+ --module-path "$__modules_path" --no-man-pages --compress=2 \
+ --generate-cds-archive --add-modules "$__modules" --output "$JRE_CONTENTS/Home" || do_exit $?
+
+ grep -v "^JAVA_VERSION" "$JSDK"/release | grep -v "^MODULES" >> "$JRE_CONTENTS/Home/release"
+ if [ "$__arch_name" == "$JBRSDK_BUNDLE" ]; then
+ sed 's/JBR/JBRSDK/g' $JRE_CONTENTS/Home/release > release
+ mv release $JRE_CONTENTS/Home/release
+ cp $IMAGES_DIR/jdk-bundle/jdk-$JBSDK_VERSION.jdk/Contents/Home/lib/src.zip $JRE_CONTENTS/Home/lib
+ cp $IMAGES_DIR/jdk-bundle/jdk-$JBSDK_VERSION.jdk/Contents/Home/lib/server/*.jsa $JRE_CONTENTS/Home/lib/server
+ copy_jmods "$__modules" "$__modules_path" "$JRE_CONTENTS"/Home/jmods
+ zip_native_debug_symbols $IMAGES_DIR/jdk-bundle/jdk-$JBSDK_VERSION.jdk "${JBR}_diz"
+ fi
+
+ if [ "$bundle_type" == "jcef" ]; then
+ cat $JCEF_PATH/jcef.version >> "$JRE_CONTENTS/Home/release"
+ fi
+
+ cp -R "$JSDK"/../MacOS "$JRE_CONTENTS"
+ cp "$JSDK"/../Info.plist "$JRE_CONTENTS"
+
+ [ -n "$bundle_type" ] && (cp -a $JCEF_PATH/Frameworks "$JRE_CONTENTS" || do_exit $?)
+
+ echo Creating "$JBR".tar.gz ...
+ # Normalize timestamp
+ #find "$tmp"/"$__root_dir" -print0 | xargs -0 touch -c -h -t "$TOUCH_TIME"
+
+ (cd "$tmp" &&
+ find "$__root_dir" -print0 | LC_ALL=C sort -z | \
+ COPYFILE_DISABLE=1 tar $REPRODUCIBLE_TAR_OPTS --no-recursion --null -T - \
+ -czf "$JBR".tar.gz --exclude='*.dSYM' --exclude='man') || do_exit $?
+ mv "$tmp"/"$JBR".tar.gz "$JBR".tar.gz
+ rm -rf "$tmp"
+}
+
+WITH_DEBUG_LEVEL="--with-debug-level=release"
+CONF_ARCHITECTURE=x86_64
+if [[ "${architecture}" == *aarch64* ]]; then
+ CONF_ARCHITECTURE=aarch64
+fi
+RELEASE_NAME=macosx-${CONF_ARCHITECTURE}-server-release
+
+case "$bundle_type" in
+ "jcef")
+ do_reset_changes=1
+ do_maketest=1
+ ;;
+ "nomod" | "")
+ bundle_type=""
+ ;;
+ "fd")
+ do_reset_changes=1
+ WITH_DEBUG_LEVEL="--with-debug-level=fastdebug"
+ RELEASE_NAME=macosx-${CONF_ARCHITECTURE}-server-fastdebug
+ JBSDK=macosx-${architecture}-server-release
+ ;;
+esac
+
+if [ -z "${INC_BUILD:-}" ]; then
+ do_configure || do_exit $?
+ make clean CONF=$RELEASE_NAME || do_exit $?
+fi
+make images CONF=$RELEASE_NAME || do_exit $?
+
+IMAGES_DIR=build/$RELEASE_NAME/images
+
+JSDK=$IMAGES_DIR/jdk-bundle/jdk-$JBSDK_VERSION.jdk/Contents/Home
+JSDK_MODS_DIR=$IMAGES_DIR/jmods
+JBRSDK_BUNDLE=jbrsdk
+
+# test/jdk/jb/java/awt/Focus/FullScreenFocusStealing.java test/jdk/java/awt/color/ICC_ColorSpace/MTTransformReplacedProfile.java test/jdk/java/awt/datatransfer/DataFlavor/DataFlavorRemoteTest.java test/jdk/java/awt/Robot/NonEmptyErrorStream.java
+
+if [ "$bundle_type" == "jcef" ] || [ "$bundle_type" == "fd" ]; then
+ git apply -p0 < jb/project/tools/patches/add_jcef_module.patch || do_exit $?
+ update_jsdk_mods "$JSDK" "$JCEF_PATH"/jmods "$JSDK"/jmods "$JSDK_MODS_DIR" || do_exit $?
+ cp $JCEF_PATH/jmods/* $JSDK_MODS_DIR # $JSDK/jmods is not changed
+
+ jbr_name_postfix="_${bundle_type}"
+else
+ jbr_name_postfix=""
+fi
+
+# create runtime image bundle
+modules=$(xargs < jb/project/tools/common/modules.list | sed s/" "//g) || do_exit $?
+create_image_bundle "jbr${jbr_name_postfix}" "jbr" $JSDK_MODS_DIR "$modules" || do_exit $?
+
+# create sdk image bundle
+modules=$(cat "$JSDK"/release | grep MODULES | sed s/MODULES=//g | sed s/' '/','/g | sed s/\"//g | sed s/\\n//g) || do_exit $?
+if [ "$bundle_type" == "jcef" ] || [ "$bundle_type" == "fd" ] || [ "$bundle_type" == "$JBRSDK_BUNDLE" ]; then
+ modules=${modules},$(get_mods_list "$JCEF_PATH"/jmods)
+fi
+create_image_bundle "$JBRSDK_BUNDLE${jbr_name_postfix}" "$JBRSDK_BUNDLE" "$JSDK_MODS_DIR" "$modules" || do_exit $?
+
+if [ $do_maketest -eq 1 ]; then
+ JBRSDK_TEST=${JBRSDK_BUNDLE}-${JBSDK_VERSION}-osx-test-${architecture}-b${build_number}
+ echo Creating "$JBRSDK_TEST" ...
+ [ $do_reset_changes -eq 1 ] && git checkout HEAD jb/project/tools/common/modules.list src/java.desktop/share/classes/module-info.java
+ make test-image jbr-api CONF=$RELEASE_NAME JBR_API_JBR_VERSION=TEST || do_exit $?
+ cp "build/${RELEASE_NAME}/jbr-api/jbr-api.jar" "${IMAGES_DIR}/test"
+ [ -f "$JBRSDK_TEST.tar.gz" ] && rm "$JBRSDK_TEST.tar.gz"
+ COPYFILE_DISABLE=1 tar -pczf "$JBRSDK_TEST".tar.gz -C $IMAGES_DIR --exclude='test/jdk/demos' test || do_exit $?
+fi
+
+do_exit 0
diff --git a/jb/project/tools/mac/scripts/notarize.sh b/jb/project/tools/mac/scripts/notarize.sh
new file mode 100755
index 000000000000..3bd05cfd63cf
--- /dev/null
+++ b/jb/project/tools/mac/scripts/notarize.sh
@@ -0,0 +1,40 @@
+#!/bin/bash
+
+#immediately exit script with an error if a command fails
+set -euo pipefail
+[[ "${SCRIPT_VERBOSE:-}" == "1" ]] && set -x
+
+APP_PATH=$1
+
+if [[ -z "$APP_PATH" ]]; then
+ echo "Usage: $0 AppPath"
+ exit 1
+fi
+if [[ ! -f "$APP_PATH" ]]; then
+ echo "AppName '$APP_PATH' does not exist or not a file"
+ exit 1
+fi
+
+function log() {
+ echo "$(date '+[%H:%M:%S]') $*"
+}
+
+
+# check required parameters
+: "${APPLE_ISSUER_ID}"
+: "${APPLE_KEY_ID}"
+: "${APPLE_PRIVATE_KEY}"
+
+# shellcheck disable=SC2064
+trap "rm -f \"$PWD/tmp_key\"" INT EXIT RETURN
+echo -n "${APPLE_PRIVATE_KEY}" > tmp_key
+
+log "Notarizing $APP_PATH..."
+xcrun notarytool submit --key tmp_key --key-id "${APPLE_KEY_ID}" --issuer "${APPLE_ISSUER_ID}" "$APP_PATH" 2>&1 | tee "notarytool.submit.out"
+REQUEST_ID="$(grep -e " id: " "notarytool.submit.out" | grep -oE '([0-9a-f-]{36})'| head -n1)"
+
+xcrun notarytool wait "$REQUEST_ID" --key tmp_key --key-id "${APPLE_KEY_ID}" --issuer "${APPLE_ISSUER_ID}" --timeout 6h ||:
+xcrun notarytool log "$REQUEST_ID" --key tmp_key --key-id "${APPLE_KEY_ID}" --issuer "${APPLE_ISSUER_ID}" developer_log.json ||:
+xcrun notarytool info "$REQUEST_ID" --key tmp_key --key-id "${APPLE_KEY_ID}" --issuer "${APPLE_ISSUER_ID}"
+
+log "Notarizing finished"
diff --git a/jb/project/tools/mac/scripts/productsign.sh b/jb/project/tools/mac/scripts/productsign.sh
new file mode 100755
index 000000000000..790a05081b0b
--- /dev/null
+++ b/jb/project/tools/mac/scripts/productsign.sh
@@ -0,0 +1,41 @@
+#!/bin/bash
+SCRIPT_DIR="$(cd "$(dirname "$0")" >/dev/null && pwd)"
+
+source "$SCRIPT_DIR/jetsign-common.sh" || exit 1
+
+function isSigned() {
+ pkgutil --check-signature "$1" >/dev/null 2>&1 && grep -q "signed by a developer certificate" < <(pkgutil --check-signature "$1" 2>&1)
+}
+
+# second last argument is a path to be signed
+pathToBeSigned="$(pwd)/${*:(-2):1}"
+# last argument is a path to signed file
+pathOut="$(pwd)/${*:(-1)}"
+jetSignArgs=("${@:1:$#-2}")
+if [[ ! -f "$pathToBeSigned" ]]; then
+ echo "$pathToBeSigned is missing or not a file"
+ exit 1
+elif isSigned "$pathToBeSigned" && ! isForced "${jetSignArgs[@]}" ; then
+ echo "Already signed: $pathToBeSigned"
+elif [[ "$JETSIGN_CLIENT" == "null" ]]; then
+ echo "JetSign client is missing, cannot proceed with signing"
+ exit 1
+elif [[ "$pathToBeSigned" != *.pkg ]]; then
+ echo "$pathToBeSigned won't be signed, assumed not to be a macOS package"
+else
+ if ! isSigned "$pathToBeSigned" ; then
+ echo "Unsigned macOS package: $pathToBeSigned"
+ fi
+ workDir=$(dirname "$pathToBeSigned")
+ pathSigned="$workDir/signed/${pathToBeSigned##*/}"
+ jetSignExtensions=$(jetSignExtensions "${jetSignArgs[@]}")
+ contentType=$(jetSignContentType "$pathToBeSigned")
+ (
+ cd "$workDir" || exit 1
+ "$JETSIGN_CLIENT" -log-format text -denoted-content-type "$contentType" -extensions "$jetSignExtensions" "$pathToBeSigned"
+ isSigned "$pathSigned"
+ rm -f "$pathOut"
+ mv "$pathSigned" "$pathOut"
+ rm -rf "$workDir/signed"
+ )
+fi
diff --git a/jb/project/tools/mac/scripts/sign.sh b/jb/project/tools/mac/scripts/sign.sh
new file mode 100755
index 000000000000..8328ecc2c53a
--- /dev/null
+++ b/jb/project/tools/mac/scripts/sign.sh
@@ -0,0 +1,162 @@
+#!/bin/bash
+
+#immediately exit script with an error if a command fails
+set -euo pipefail
+[[ "${SCRIPT_VERBOSE:-}" == "1" ]] && set -x
+
+if [[ $# -lt 5 ]]; then
+ echo "Usage: $0 AppDirectory AppName BundleId CertificateID InstallerCertificateID"
+ exit 1
+fi
+
+APPLICATION_PATH=$1
+PKG_NAME=$2
+BUNDLE_ID=$3
+JB_DEVELOPER_CERT=$4
+JB_INSTALLER_CERT=$5
+
+SCRIPT_DIR="$(cd "$(dirname "$0")" >/dev/null && pwd)"
+
+# Use JetBrains sign utility if it's available
+if [[ "${JETSIGN_CLIENT:=}" == "null" ]] || [[ "$JETSIGN_CLIENT" == "" ]]; then
+ JB_SIGN=false
+ SIGN_UTILITY="codesign"
+ PRODUCTSIGN_UTILITY="productsign"
+else
+ JB_SIGN=true
+ SIGN_UTILITY="$SCRIPT_DIR/codesign.sh"
+ PRODUCTSIGN_UTILITY="$SCRIPT_DIR/productsign.sh"
+fi
+
+if [[ ! -d "$APPLICATION_PATH" ]]; then
+ echo "AppDirectory '$APPLICATION_PATH' does not exist or not a directory"
+ exit 1
+fi
+
+function log() {
+ echo "$(date '+[%H:%M:%S]') $*"
+}
+
+# Cleanup files left from previous sign attempt (if any)
+find "$APPLICATION_PATH" -name '*.cstemp' -exec rm '{}' \;
+
+log "Signing libraries and executables..."
+# -perm +111 searches for executables
+for f in \
+ "Contents/Home/lib" "Contents/MacOS" \
+ "Contents/Home/Frameworks" \
+ "Contents/Frameworks"; do
+ if [ -d "$APPLICATION_PATH/$f" ]; then
+ find "$APPLICATION_PATH/$f" \
+ -type f \( -name "*.jnilib" -o -name "*.dylib" -o -name "*.so" -o -name "*.tbd" -o -name "*.node" -o -perm +111 \) \
+ -exec "$SIGN_UTILITY" --timestamp \
+ -v -s "$JB_DEVELOPER_CERT" --options=runtime --force \
+ --entitlements "$SCRIPT_DIR/entitlements.xml" {} \;
+ fi
+done
+
+log "Signing libraries in jars in $APPLICATION_PATH"
+
+# todo: add set -euo pipefail; into the inner sh -c
+# `-e` prevents `grep -q && printf` loginc
+# with `-o pipefail` there's no input for 'while' loop
+find "$APPLICATION_PATH" -name '*.jar' \
+ -exec sh -c "set -u; unzip -l \"\$0\" | grep -q -e '\.dylib\$' -e '\.jnilib\$' -e '\.so\$' -e '\.tbd\$' -e '^jattach\$' && printf \"\$0\0\" " {} \; |
+ while IFS= read -r -d $'\0' file; do
+ log "Processing libraries in $file"
+
+ rm -rf jarfolder jar.jar
+ mkdir jarfolder
+ filename="${file##*/}"
+ log "Filename: $filename"
+ cp "$file" jarfolder && (cd jarfolder && jar xf "$filename" && rm "$filename")
+
+ find jarfolder \
+ -type f \( -name "*.jnilib" -o -name "*.dylib" -o -name "*.so" -o -name "*.tbd" -o -name "jattach" \) \
+ -exec "$SIGN_UTILITY" --timestamp \
+ --force \
+ -v -s "$JB_DEVELOPER_CERT" --options=runtime \
+ --entitlements "$SCRIPT_DIR/entitlements.xml" {} \;
+
+ (cd jarfolder; zip -q -r -o -0 ../jar.jar .)
+ mv jar.jar "$file"
+ done
+
+rm -rf jarfolder jar.jar
+
+log "Signing other files..."
+# shellcheck disable=SC2043
+for f in \
+ "Contents/Home/bin"; do
+ if [ -d "$APPLICATION_PATH/$f" ]; then
+ find "$APPLICATION_PATH/$f" \
+ -type f \( -name "*.jnilib" -o -name "*.dylib" -o -name "*.so" -o -name "*.tbd" -o -perm +111 \) \
+ -exec "$SIGN_UTILITY" --timestamp \
+ -v -s "$JB_DEVELOPER_CERT" --options=runtime --force \
+ --entitlements "$SCRIPT_DIR/entitlements.xml" {} \;
+ fi
+done
+
+log "Signing whole frameworks..."
+# shellcheck disable=SC2043
+if [ "$JB_SIGN" = true ]; then for f in \
+ "Contents/Frameworks/cef_server.app/Contents/Frameworks" "Contents/Home/Frameworks" "Contents/Frameworks"; do
+ if [ -d "$APPLICATION_PATH/$f" ]; then
+ find "$APPLICATION_PATH/$f" \( -name '*.framework' -o -name '*.app' \) -maxdepth 1 | while read -r line
+ do
+ log "Signing '$line':"
+ tar -pczf tmp-to-sign.tar.gz -C "$(dirname "$line")" "$(basename "$line")"
+ "$SIGN_UTILITY" --timestamp \
+ -v -s "$JB_DEVELOPER_CERT" --options=runtime \
+ --force \
+ --entitlements "$SCRIPT_DIR/entitlements.xml" tmp-to-sign.tar.gz
+ rm -rf "$line"
+ tar -xzf tmp-to-sign.tar.gz --directory "$(dirname "$line")"
+ rm -f tmp-to-sign.tar.gz
+ done
+ fi
+done; fi
+
+log "Checking framework signatures..."
+for f in \
+ "Contents/Home/Frameworks" "Contents/Frameworks"; do
+ if [ -d "$APPLICATION_PATH/$f" ]; then
+ find "$APPLICATION_PATH/$f" -name '*.framework' -maxdepth 1 | while read -r line
+ do
+ log "Checking '$line':"
+ codesign --verify --deep --strict --verbose=4 "$line"
+ done
+ fi
+done
+
+log "Signing whole app..."
+if [ "$JB_SIGN" = true ]; then
+ tar -pczf tmp-to-sign.tar.gz --exclude='man' -C "$(dirname "$APPLICATION_PATH")" "$(basename "$APPLICATION_PATH")"
+ "$SIGN_UTILITY" --timestamp \
+ -v -s "$JB_DEVELOPER_CERT" --options=runtime \
+ --force \
+ --entitlements "$SCRIPT_DIR/entitlements.xml" tmp-to-sign.tar.gz
+ rm -rf "$APPLICATION_PATH"
+ tar -xzf tmp-to-sign.tar.gz --directory "$(dirname "$APPLICATION_PATH")"
+ rm -f tmp-to-sign.tar.gz
+else
+ "$SIGN_UTILITY" --timestamp \
+ -v -s "$JB_DEVELOPER_CERT" --options=runtime \
+ --force \
+ --entitlements "$SCRIPT_DIR/entitlements.xml" "$APPLICATION_PATH"
+fi
+
+BUILD_NAME="$(basename "$APPLICATION_PATH")"
+
+log "Creating $PKG_NAME..."
+rm -rf "$PKG_NAME"
+
+mkdir -p unsigned
+pkgbuild --identifier $BUNDLE_ID --root $APPLICATION_PATH \
+ --install-location /Library/Java/JavaVirtualMachines/${BUILD_NAME} unsigned/${PKG_NAME}
+log "Signing $PKG_NAME..."
+"$PRODUCTSIGN_UTILITY" --timestamp --sign "$JB_INSTALLER_CERT" unsigned/${PKG_NAME} ${PKG_NAME}
+
+log "Verifying java is not broken"
+find "$APPLICATION_PATH" \
+ -type f -name 'java' -perm +111 -exec {} -version \;
diff --git a/jb/project/tools/mac/scripts/signapp.sh b/jb/project/tools/mac/scripts/signapp.sh
new file mode 100755
index 000000000000..a763881a0fbb
--- /dev/null
+++ b/jb/project/tools/mac/scripts/signapp.sh
@@ -0,0 +1,136 @@
+#!/bin/bash
+
+#immediately exit script with an error if a command fails
+set -euo pipefail
+[[ "${SCRIPT_VERBOSE:-}" == "1" ]] && set -x
+
+export COPY_EXTENDED_ATTRIBUTES_DISABLE=true
+export COPYFILE_DISABLE=true
+
+INPUT_FILE=$1
+EXPLODED=$2.exploded
+BACKUP_JMODS=$2.backup
+USERNAME=$3
+PASSWORD=$4
+CODESIGN_STRING=$5
+JB_INSTALLER_CERT=$6
+NOTARIZE=$7
+BUNDLE_ID=$8
+
+SCRIPT_DIR="$(cd "$(dirname "$0")" >/dev/null && pwd)"
+
+function log() {
+ echo "$(date '+[%H:%M:%S]') $*"
+}
+
+log "Deleting $EXPLODED ..."
+if test -d "$EXPLODED"; then
+ find "$EXPLODED" -mindepth 1 -maxdepth 1 -exec chmod -R u+wx '{}' \;
+fi
+rm -rf "$EXPLODED"
+mkdir "$EXPLODED"
+rm -rf "$BACKUP_JMODS"
+mkdir "$BACKUP_JMODS"
+
+log "Unzipping $INPUT_FILE to $EXPLODED ..."
+tar -xzvf "$INPUT_FILE" --directory $EXPLODED
+BUILD_NAME="$(ls "$EXPLODED")"
+#sed -i '' s/BNDL/APPL/ $EXPLODED/$BUILD_NAME/Contents/Info.plist
+rm -f $EXPLODED/$BUILD_NAME/Contents/CodeResources
+rm "$INPUT_FILE"
+if test -d $EXPLODED/$BUILD_NAME/Contents/Home/jmods; then
+ mv $EXPLODED/$BUILD_NAME/Contents/Home/jmods $BACKUP_JMODS
+fi
+
+log "$INPUT_FILE extracted and removed"
+
+APP_NAME=$(basename "$INPUT_FILE" | awk -F".tar" '{ print $1 }')
+PKG_NAME="$APP_NAME.pkg"
+APPLICATION_PATH=$EXPLODED/$(ls $EXPLODED)
+
+find "$APPLICATION_PATH/Contents/Home/bin" \
+ -maxdepth 1 -type f -name '*.jnilib' -print0 |
+ while IFS= read -r -d $'\0' file; do
+ if [ -f "$file" ]; then
+ log "Linking $file"
+ b="$(basename "$file" .jnilib)"
+ ln -sf "$b.jnilib" "$(dirname "$file")/$b.dylib"
+ fi
+ done
+
+find "$APPLICATION_PATH/Contents/" \
+ -maxdepth 1 -type f -name '*.txt' -print0 |
+ while IFS= read -r -d $'\0' file; do
+ if [ -f "$file" ]; then
+ log "Moving $file"
+ mv "$file" "$APPLICATION_PATH/Contents/Resources"
+ fi
+ done
+
+non_plist=$(find "$APPLICATION_PATH/Contents/" -maxdepth 1 -type f -and -not -name 'Info.plist' | wc -l)
+if [[ $non_plist -gt 0 ]]; then
+ log "Only Info.plist file is allowed in Contents directory but found $non_plist file(s):"
+ log "$(find "$APPLICATION_PATH/Contents/" -maxdepth 1 -type f -and -not -name 'Info.plist')"
+ exit 1
+fi
+
+if [[ "${JETSIGN_CLIENT:=}" == "null" ]] || [[ "$JETSIGN_CLIENT" == "" ]]; then
+ log "Unlocking keychain..."
+ # Make sure *.p12 is imported into local KeyChain
+ security unlock-keychain -p "$PASSWORD" "/Users/$USERNAME/Library/Keychains/login.keychain"
+fi
+
+attempt=1
+limit=3
+set +e
+while [[ $attempt -le $limit ]]; do
+ log "Signing (attempt $attempt) $APPLICATION_PATH ..."
+ "$SCRIPT_DIR/sign.sh" "$APPLICATION_PATH" "$PKG_NAME" "$BUNDLE_ID" "$CODESIGN_STRING" "$JB_INSTALLER_CERT"
+ ec=$?
+ if [[ $ec -ne 0 ]]; then
+ ((attempt += 1))
+ if [ $attempt -eq $limit ]; then
+ set -e
+ fi
+ log "Signing failed, wait for 30 sec and try to sign again"
+ sleep 30
+ else
+ log "Signing done"
+ codesign -v "$APPLICATION_PATH" -vvvvv
+ log "Check sign done"
+ spctl -a -v $APPLICATION_PATH
+ ((attempt += limit))
+ fi
+done
+
+set -e
+
+if [ "$NOTARIZE" = "yes" ]; then
+ log "Notarizing..."
+ "$SCRIPT_DIR/notarize.sh" "$PKG_NAME"
+ log "Stapling..."
+ xcrun stapler staple "$APPLICATION_PATH" ||:
+ xcrun stapler staple "$PKG_NAME" ||:
+else
+ log "Notarization disabled"
+ log "Stapling disabled"
+fi
+
+log "Zipping $BUILD_NAME to $INPUT_FILE ..."
+(
+ #cd "$EXPLODED"
+ #ditto -c -k --sequesterRsrc --keepParent "$BUILD_NAME" "../$INPUT_FILE"
+ if test -d $BACKUP_JMODS/jmods; then
+ mv $BACKUP_JMODS/jmods $APPLICATION_PATH/Contents/Home
+ fi
+ if [[ "$APPLICATION_PATH" != "$EXPLODED/$BUILD_NAME" ]]; then
+ mv $APPLICATION_PATH $EXPLODED/$BUILD_NAME
+ else
+ echo "No move, source == destination: $APPLICATION_PATH"
+ fi
+
+ tar -pczvf $INPUT_FILE --exclude='man' -C $EXPLODED $BUILD_NAME
+ log "Finished zipping"
+)
+rm -rf "$EXPLODED"
+log "Done"
\ No newline at end of file
diff --git a/jb/project/tools/patches/add_jcef_module.patch b/jb/project/tools/patches/add_jcef_module.patch
new file mode 100644
index 000000000000..6a4aed824f6a
--- /dev/null
+++ b/jb/project/tools/patches/add_jcef_module.patch
@@ -0,0 +1,30 @@
+diff --git jb/project/tools/common/modules.list jb/project/tools/common/modules.list
+index 522acb7cb43..c40e689d5de 100644
+--- jb/project/tools/common/modules.list
++++ jb/project/tools/common/modules.list
+@@ -51,4 +51,7 @@ jdk.unsupported.desktop,
+ jdk.xml.dom,
+ jdk.zipfs,
+ jdk.hotspot.agent,
+-jdk.jcmd
++jdk.jcmd,
++jcef,
++gluegen.rt,
++jogl.all
+diff --git src/java.desktop/share/classes/module-info.java src/java.desktop/share/classes/module-info.java
+index 897647ee368..781d1809493 100644
+--- src/java.desktop/share/classes/module-info.java
++++ src/java.desktop/share/classes/module-info.java
+@@ -116,7 +116,11 @@ module java.desktop {
+ // see make/GensrcModuleInfo.gmk
+ exports sun.awt to
+ jdk.accessibility,
+- jdk.unsupported.desktop;
++ jdk.unsupported.desktop,
++ jcef,
++ jogl.all;
++
++ exports java.awt.peer to jcef;
+
+ exports java.awt.dnd.peer to jdk.unsupported.desktop;
+ exports sun.awt.dnd to jdk.unsupported.desktop;
diff --git a/jb/project/tools/patches/add_jcef_module_aarch64.patch b/jb/project/tools/patches/add_jcef_module_aarch64.patch
new file mode 100644
index 000000000000..2752dc95f3b9
--- /dev/null
+++ b/jb/project/tools/patches/add_jcef_module_aarch64.patch
@@ -0,0 +1,30 @@
+diff --git jb/project/tools/common/modules.list jb/project/tools/common/modules.list
+index 522acb7..c40e689 100644
+--- jb/project/tools/common/modules.list
++++ jb/project/tools/common/modules.list
+@@ -51,4 +51,7 @@ jdk.unsupported.desktop,
+ jdk.xml.dom,
+ jdk.zipfs,
+ jdk.hotspot.agent,
+-jdk.jcmd
++jdk.jcmd,
++jcef,
++gluegen.rt,
++jogl.all
+diff --git src/java.desktop/share/classes/module-info.java src/java.desktop/share/classes/module-info.java
+index 897647e..781d180 100644
+--- src/java.desktop/share/classes/module-info.java
++++ src/java.desktop/share/classes/module-info.java
+@@ -116,7 +116,11 @@ module java.desktop {
+ // see make/GensrcModuleInfo.gmk
+ exports sun.awt to
+ jdk.accessibility,
+- jdk.unsupported.desktop;
++ jdk.unsupported.desktop,
++ jcef,
++ jogl.all;
++
++ exports java.awt.peer to jcef;
+
+ exports java.awt.dnd.peer to jdk.unsupported.desktop;
+ exports sun.awt.dnd to jdk.unsupported.desktop;
diff --git a/jb/project/tools/perf/j2dbopts_poly250-AA-rand_col.txt b/jb/project/tools/perf/j2dbopts_poly250-AA-rand_col.txt
new file mode 100644
index 000000000000..9e94d063b1ff
--- /dev/null
+++ b/jb/project/tools/perf/j2dbopts_poly250-AA-rand_col.txt
@@ -0,0 +1,267 @@
+prog.verbose=disabled
+prog.printresults=enabled
+global.env.outputwidth=640
+global.env.outputheight=480
+global.env.runcount=5
+global.env.repcount=0
+global.env.testtime=2500
+global.results.workunits=units
+global.results.timeunits=sec
+global.results.ratio=unitspersec
+global.dest.offscreen=disabled
+global.dest.frame.defaultframe=enabled
+global.dest.frame.transframe=disabled
+global.dest.frame.shapedframe=disabled
+global.dest.frame.shapedtransframe=disabled
+global.dest.compatimg.compatimg=disabled
+global.dest.compatimg.opqcompatimg=disabled
+global.dest.compatimg.bmcompatimg=disabled
+global.dest.compatimg.transcompatimg=disabled
+global.dest.volimg.volimg=disabled
+global.dest.volimg.opqvolimg=disabled
+global.dest.volimg.bmvolimg=disabled
+global.dest.volimg.transvolimg=disabled
+global.dest.bufimg.IntXrgb=disabled
+global.dest.bufimg.IntArgb=disabled
+global.dest.bufimg.IntArgbPre=disabled
+global.dest.bufimg.3ByteBgr=disabled
+global.dest.bufimg.ByteIndexed=disabled
+global.dest.bufimg.ByteGray=disabled
+global.dest.bufimg.4ByteAbgr=disabled
+global.dest.bufimg.4ByteAbgrPre=disabled
+global.dest.bufimg.custom=disabled
+graphics.opts.anim=2
+graphics.opts.sizes=250
+graphics.opts.alpharule=SrcOver
+graphics.opts.transform=ident
+graphics.opts.extraalpha=Off
+graphics.opts.xormode=Off
+graphics.opts.clip=Off
+graphics.opts.renderhint=Default
+graphics.render.opts.paint=random
+graphics.render.opts.alphacolor=Off
+graphics.render.opts.antialias=On
+graphics.render.opts.stroke=width1
+graphics.render.tests.drawLine=disabled
+graphics.render.tests.drawLineHoriz=disabled
+graphics.render.tests.drawLineVert=disabled
+graphics.render.tests.fillRect=disabled
+graphics.render.tests.drawRect=disabled
+graphics.render.tests.fillOval=disabled
+graphics.render.tests.drawOval=disabled
+graphics.render.tests.fillPoly=disabled
+graphics.render.tests.drawPoly=enabled
+graphics.render.tests.shape.fillCubic=disabled
+graphics.render.tests.shape.drawCubic=disabled
+graphics.render.tests.shape.fillEllipse2D=disabled
+graphics.render.tests.shape.drawEllipse2D=disabled
+graphics.imaging.src.offscr.opaque=disabled
+graphics.imaging.src.offscr.bitmask=disabled
+graphics.imaging.src.offscr.translucent=disabled
+graphics.imaging.src.opqcompatimg.opaque=disabled
+graphics.imaging.src.opqcompatimg.bitmask=disabled
+graphics.imaging.src.opqcompatimg.translucent=disabled
+graphics.imaging.src.bmcompatimg.opaque=disabled
+graphics.imaging.src.bmcompatimg.bitmask=disabled
+graphics.imaging.src.bmcompatimg.translucent=disabled
+graphics.imaging.src.transcompatimg.opaque=disabled
+graphics.imaging.src.transcompatimg.bitmask=disabled
+graphics.imaging.src.transcompatimg.translucent=disabled
+graphics.imaging.src.opqvolimg.opaque=disabled
+graphics.imaging.src.opqvolimg.bitmask=disabled
+graphics.imaging.src.opqvolimg.translucent=disabled
+graphics.imaging.src.bmvolimg.opaque=disabled
+graphics.imaging.src.bmvolimg.bitmask=disabled
+graphics.imaging.src.bmvolimg.translucent=disabled
+graphics.imaging.src.transvolimg.opaque=disabled
+graphics.imaging.src.transvolimg.bitmask=disabled
+graphics.imaging.src.transvolimg.translucent=disabled
+graphics.imaging.src.bufimg.IntXrgb.opaque=disabled
+graphics.imaging.src.bufimg.IntXrgb.bitmask=disabled
+graphics.imaging.src.bufimg.IntXrgb.translucent=disabled
+graphics.imaging.src.bufimg.IntArgb.opaque=disabled
+graphics.imaging.src.bufimg.IntArgb.bitmask=disabled
+graphics.imaging.src.bufimg.IntArgb.translucent=disabled
+graphics.imaging.src.bufimg.IntArgbPre.opaque=disabled
+graphics.imaging.src.bufimg.IntArgbPre.bitmask=disabled
+graphics.imaging.src.bufimg.IntArgbPre.translucent=disabled
+graphics.imaging.src.bufimg.ByteGray.opaque=disabled
+graphics.imaging.src.bufimg.ByteGray.bitmask=disabled
+graphics.imaging.src.bufimg.ByteGray.translucent=disabled
+graphics.imaging.src.bufimg.3ByteBgr.opaque=disabled
+graphics.imaging.src.bufimg.3ByteBgr.bitmask=disabled
+graphics.imaging.src.bufimg.3ByteBgr.translucent=disabled
+graphics.imaging.src.bufimg.4ByteAbgr.opaque=disabled
+graphics.imaging.src.bufimg.4ByteAbgr.bitmask=disabled
+graphics.imaging.src.bufimg.4ByteAbgr.translucent=disabled
+graphics.imaging.src.bufimg.4ByteAbgrPre.opaque=disabled
+graphics.imaging.src.bufimg.4ByteAbgrPre.bitmask=disabled
+graphics.imaging.src.bufimg.4ByteAbgrPre.translucent=disabled
+graphics.imaging.src.bufimg.ByteIndexedBm.opaque=disabled
+graphics.imaging.src.bufimg.ByteIndexedBm.bitmask=disabled
+graphics.imaging.src.bufimg.ByteIndexedBm.translucent=disabled
+graphics.imaging.src.bufimg.unmanagedIntXrgb.opaque=disabled
+graphics.imaging.src.bufimg.unmanagedIntXrgb.bitmask=disabled
+graphics.imaging.src.bufimg.unmanagedIntXrgb.translucent=disabled
+graphics.imaging.src.bufimg.unmanagedIntArgb.opaque=disabled
+graphics.imaging.src.bufimg.unmanagedIntArgb.bitmask=disabled
+graphics.imaging.src.bufimg.unmanagedIntArgb.translucent=disabled
+graphics.imaging.src.bufimg.unmanagedIntArgbPre.opaque=disabled
+graphics.imaging.src.bufimg.unmanagedIntArgbPre.bitmask=disabled
+graphics.imaging.src.bufimg.unmanagedIntArgbPre.translucent=disabled
+graphics.imaging.src.bufimg.unmanaged3ByteBgr.opaque=disabled
+graphics.imaging.src.bufimg.unmanaged3ByteBgr.bitmask=disabled
+graphics.imaging.src.bufimg.unmanaged3ByteBgr.translucent=disabled
+graphics.imaging.benchmarks.opts.interpolation=Nearest neighbor
+graphics.imaging.benchmarks.opts.touchsrc=Off
+graphics.imaging.benchmarks.tests.drawimage=disabled
+graphics.imaging.benchmarks.tests.drawimagebg=disabled
+graphics.imaging.benchmarks.tests.drawimagescaleup=disabled
+graphics.imaging.benchmarks.tests.drawimagescaledown=disabled
+graphics.imaging.benchmarks.tests.drawimagescalesplit=disabled
+graphics.imaging.benchmarks.tests.drawimagetxform=disabled
+graphics.imaging.imageops.opts.op=convolve3x3zero
+graphics.imaging.imageops.tests.graphics2d.drawimageop=disabled
+graphics.imaging.imageops.tests.bufimgop.filternull=disabled
+graphics.imaging.imageops.tests.bufimgop.filtercached=disabled
+graphics.imaging.imageops.tests.rasterop.filternull=disabled
+graphics.imaging.imageops.tests.rasterop.filtercached=disabled
+graphics.misc.copytests.copyAreaVert=disabled
+graphics.misc.copytests.copyAreaHoriz=disabled
+graphics.misc.copytests.copyAreaDiag=disabled
+pixel.opts.renderto=Off
+pixel.opts.renderfrom=Off
+pixel.src.1BitBinary=disabled
+pixel.src.2BitBinary=disabled
+pixel.src.4BitBinary=disabled
+pixel.src.ByteIndexed=disabled
+pixel.src.ByteGray=disabled
+pixel.src.Short555=disabled
+pixel.src.Short565=disabled
+pixel.src.ShortGray=disabled
+pixel.src.3ByteBgr=disabled
+pixel.src.4ByteAbgr=disabled
+pixel.src.IntXrgb=disabled
+pixel.src.IntXbgr=disabled
+pixel.src.IntArgb=disabled
+pixel.bimgtests.getrgb=disabled
+pixel.bimgtests.setrgb=disabled
+pixel.rastests.getdataelem=disabled
+pixel.rastests.setdataelem=disabled
+pixel.rastests.getpixel=disabled
+pixel.rastests.setpixel=disabled
+pixel.dbtests.getelem=disabled
+pixel.dbtests.setelem=disabled
+text.opts.data.tlength=16
+text.opts.data.tscript=english
+text.opts.font.fname=serif,physical
+text.opts.font.fstyle=0
+text.opts.font.fsize=13.0
+text.opts.font.ftx=Identity
+text.opts.graphics.textaa=Off
+text.opts.graphics.tfm=Off
+text.opts.graphics.gaa=Off
+text.opts.graphics.gtx=Identity
+text.opts.advopts.gvstyle=0
+text.opts.advopts.tlruns=1
+text.opts.advopts.maptype=FONT
+text.Rendering.tests.drawString=disabled
+text.Rendering.tests.drawChars=disabled
+text.Rendering.tests.drawBytes=disabled
+text.Rendering.tests.drawGlyphVectors=disabled
+text.Rendering.tests.drawTextLayout=disabled
+text.Measuring.tests.stringWidth=disabled
+text.Measuring.tests.stringBounds=disabled
+text.Measuring.tests.charsWidth=disabled
+text.Measuring.tests.charsBounds=disabled
+text.Measuring.tests.fontcandisplay=disabled
+text.Measuring.tests.gvWidth=disabled
+text.Measuring.tests.gvLogicalBounds=disabled
+text.Measuring.tests.gvVisualBounds=disabled
+text.Measuring.tests.gvPixelBounds=disabled
+text.Measuring.tests.gvOutline=disabled
+text.Measuring.tests.gvGlyphLogicalBounds=disabled
+text.Measuring.tests.gvGlyphVisualBounds=disabled
+text.Measuring.tests.gvGlyphPixelBounds=disabled
+text.Measuring.tests.gvGlyphOutline=disabled
+text.Measuring.tests.gvGlyphTransform=disabled
+text.Measuring.tests.gvGlyphMetrics=disabled
+text.Measuring.tests.tlAdvance=disabled
+text.Measuring.tests.tlAscent=disabled
+text.Measuring.tests.tlBounds=disabled
+text.Measuring.tests.tlGetCaretInfo=disabled
+text.Measuring.tests.tlGetNextHit=disabled
+text.Measuring.tests.tlGetCaretShape=disabled
+text.Measuring.tests.tlGetLogicalHighlightShape=disabled
+text.Measuring.tests.tlHitTest=disabled
+text.Measuring.tests.tlOutline=disabled
+text.construction.tests.gvfromfontstring=disabled
+text.construction.tests.gvfromfontchars=disabled
+text.construction.tests.gvfromfontci=disabled
+text.construction.tests.gvfromfontglyphs=disabled
+text.construction.tests.gvfromfontlayout=disabled
+text.construction.tests.tlfromfont=disabled
+text.construction.tests.tlfrommap=disabled
+imageio.opts.size=250
+imageio.opts.content=photo
+imageio.input.opts.general.source.file=disabled
+imageio.input.opts.general.source.url=disabled
+imageio.input.opts.general.source.byteArray=disabled
+imageio.input.opts.imageio.useCache=Off
+imageio.input.image.toolkit.opts.format=
+imageio.input.image.toolkit.tests.createImage=disabled
+imageio.input.image.imageio.opts.format=
+imageio.input.image.imageio.tests.imageioRead=disabled
+imageio.input.image.imageio.reader.opts.seekForwardOnly=On
+imageio.input.image.imageio.reader.opts.ignoreMetadata=On
+imageio.input.image.imageio.reader.opts.installListener=Off
+imageio.input.image.imageio.reader.tests.read=disabled
+imageio.input.image.imageio.reader.tests.getImageMetadata=disabled
+imageio.input.stream.tests.construct=disabled
+imageio.input.stream.tests.read=disabled
+imageio.input.stream.tests.readByteArray=disabled
+imageio.input.stream.tests.readFullyByteArray=disabled
+imageio.input.stream.tests.readBit=disabled
+imageio.input.stream.tests.readByte=disabled
+imageio.input.stream.tests.readUnsignedByte=disabled
+imageio.input.stream.tests.readShort=disabled
+imageio.input.stream.tests.readUnsignedShort=disabled
+imageio.input.stream.tests.readInt=disabled
+imageio.input.stream.tests.readUnsignedInt=disabled
+imageio.input.stream.tests.readFloat=disabled
+imageio.input.stream.tests.readLong=disabled
+imageio.input.stream.tests.readDouble=disabled
+imageio.input.stream.tests.skipBytes=disabled
+imageio.output.opts.general.dest.file=disabled
+imageio.output.opts.general.dest.byteArray=disabled
+imageio.output.opts.imageio.useCache=Off
+imageio.output.image.imageio.opts.format=
+imageio.output.image.imageio.tests.imageioWrite=disabled
+imageio.output.image.imageio.writer.opts.installListener=Off
+imageio.output.image.imageio.writer.tests.write=disabled
+imageio.output.stream.tests.construct=disabled
+imageio.output.stream.tests.write=disabled
+imageio.output.stream.tests.writeByteArray=disabled
+imageio.output.stream.tests.writeBit=disabled
+imageio.output.stream.tests.writeByte=disabled
+imageio.output.stream.tests.writeShort=disabled
+imageio.output.stream.tests.writeInt=disabled
+imageio.output.stream.tests.writeFloat=disabled
+imageio.output.stream.tests.writeLong=disabled
+imageio.output.stream.tests.writeDouble=disabled
+cmm.opts.profiles=1001
+cmm.colorconv.data.fromRGB=disabled
+cmm.colorconv.data.toRGB=disabled
+cmm.colorconv.data.fromCIEXYZ=disabled
+cmm.colorconv.data.toCIEXYZ=disabled
+cmm.colorconv.ccop.ccopOptions.size=250
+cmm.colorconv.ccop.ccopOptions.content=photo
+cmm.colorconv.ccop.ccopOptions.srcType=INT_RGB
+cmm.colorconv.ccop.ccopOptions.dstType=INT_RGB
+cmm.colorconv.ccop.op_img=disabled
+cmm.colorconv.ccop.op_rst=disabled
+cmm.colorconv.ccop.op_draw=disabled
+cmm.colorconv.embed.embedOptions.Images=512x512
+cmm.colorconv.embed.embd_img_read=disabled
+cmm.profiles.getHeader=disabled
+cmm.profiles.getNumComponents=disabled
diff --git a/jb/project/tools/perf/j2dbopts_poly250-rand_col.txt b/jb/project/tools/perf/j2dbopts_poly250-rand_col.txt
new file mode 100644
index 000000000000..bf32c80daf64
--- /dev/null
+++ b/jb/project/tools/perf/j2dbopts_poly250-rand_col.txt
@@ -0,0 +1,267 @@
+prog.verbose=disabled
+prog.printresults=enabled
+global.env.outputwidth=640
+global.env.outputheight=480
+global.env.runcount=5
+global.env.repcount=0
+global.env.testtime=2500
+global.results.workunits=units
+global.results.timeunits=sec
+global.results.ratio=unitspersec
+global.dest.offscreen=disabled
+global.dest.frame.defaultframe=enabled
+global.dest.frame.transframe=disabled
+global.dest.frame.shapedframe=disabled
+global.dest.frame.shapedtransframe=disabled
+global.dest.compatimg.compatimg=disabled
+global.dest.compatimg.opqcompatimg=disabled
+global.dest.compatimg.bmcompatimg=disabled
+global.dest.compatimg.transcompatimg=disabled
+global.dest.volimg.volimg=disabled
+global.dest.volimg.opqvolimg=disabled
+global.dest.volimg.bmvolimg=disabled
+global.dest.volimg.transvolimg=disabled
+global.dest.bufimg.IntXrgb=disabled
+global.dest.bufimg.IntArgb=disabled
+global.dest.bufimg.IntArgbPre=disabled
+global.dest.bufimg.3ByteBgr=disabled
+global.dest.bufimg.ByteIndexed=disabled
+global.dest.bufimg.ByteGray=disabled
+global.dest.bufimg.4ByteAbgr=disabled
+global.dest.bufimg.4ByteAbgrPre=disabled
+global.dest.bufimg.custom=disabled
+graphics.opts.anim=2
+graphics.opts.sizes=250
+graphics.opts.alpharule=SrcOver
+graphics.opts.transform=ident
+graphics.opts.extraalpha=Off
+graphics.opts.xormode=Off
+graphics.opts.clip=Off
+graphics.opts.renderhint=Default
+graphics.render.opts.paint=random
+graphics.render.opts.alphacolor=Off
+graphics.render.opts.antialias=Off
+graphics.render.opts.stroke=width1
+graphics.render.tests.drawLine=disabled
+graphics.render.tests.drawLineHoriz=disabled
+graphics.render.tests.drawLineVert=disabled
+graphics.render.tests.fillRect=disabled
+graphics.render.tests.drawRect=disabled
+graphics.render.tests.fillOval=disabled
+graphics.render.tests.drawOval=disabled
+graphics.render.tests.fillPoly=disabled
+graphics.render.tests.drawPoly=enabled
+graphics.render.tests.shape.fillCubic=disabled
+graphics.render.tests.shape.drawCubic=disabled
+graphics.render.tests.shape.fillEllipse2D=disabled
+graphics.render.tests.shape.drawEllipse2D=disabled
+graphics.imaging.src.offscr.opaque=disabled
+graphics.imaging.src.offscr.bitmask=disabled
+graphics.imaging.src.offscr.translucent=disabled
+graphics.imaging.src.opqcompatimg.opaque=disabled
+graphics.imaging.src.opqcompatimg.bitmask=disabled
+graphics.imaging.src.opqcompatimg.translucent=disabled
+graphics.imaging.src.bmcompatimg.opaque=disabled
+graphics.imaging.src.bmcompatimg.bitmask=disabled
+graphics.imaging.src.bmcompatimg.translucent=disabled
+graphics.imaging.src.transcompatimg.opaque=disabled
+graphics.imaging.src.transcompatimg.bitmask=disabled
+graphics.imaging.src.transcompatimg.translucent=disabled
+graphics.imaging.src.opqvolimg.opaque=disabled
+graphics.imaging.src.opqvolimg.bitmask=disabled
+graphics.imaging.src.opqvolimg.translucent=disabled
+graphics.imaging.src.bmvolimg.opaque=disabled
+graphics.imaging.src.bmvolimg.bitmask=disabled
+graphics.imaging.src.bmvolimg.translucent=disabled
+graphics.imaging.src.transvolimg.opaque=disabled
+graphics.imaging.src.transvolimg.bitmask=disabled
+graphics.imaging.src.transvolimg.translucent=disabled
+graphics.imaging.src.bufimg.IntXrgb.opaque=disabled
+graphics.imaging.src.bufimg.IntXrgb.bitmask=disabled
+graphics.imaging.src.bufimg.IntXrgb.translucent=disabled
+graphics.imaging.src.bufimg.IntArgb.opaque=disabled
+graphics.imaging.src.bufimg.IntArgb.bitmask=disabled
+graphics.imaging.src.bufimg.IntArgb.translucent=disabled
+graphics.imaging.src.bufimg.IntArgbPre.opaque=disabled
+graphics.imaging.src.bufimg.IntArgbPre.bitmask=disabled
+graphics.imaging.src.bufimg.IntArgbPre.translucent=disabled
+graphics.imaging.src.bufimg.ByteGray.opaque=disabled
+graphics.imaging.src.bufimg.ByteGray.bitmask=disabled
+graphics.imaging.src.bufimg.ByteGray.translucent=disabled
+graphics.imaging.src.bufimg.3ByteBgr.opaque=disabled
+graphics.imaging.src.bufimg.3ByteBgr.bitmask=disabled
+graphics.imaging.src.bufimg.3ByteBgr.translucent=disabled
+graphics.imaging.src.bufimg.4ByteAbgr.opaque=disabled
+graphics.imaging.src.bufimg.4ByteAbgr.bitmask=disabled
+graphics.imaging.src.bufimg.4ByteAbgr.translucent=disabled
+graphics.imaging.src.bufimg.4ByteAbgrPre.opaque=disabled
+graphics.imaging.src.bufimg.4ByteAbgrPre.bitmask=disabled
+graphics.imaging.src.bufimg.4ByteAbgrPre.translucent=disabled
+graphics.imaging.src.bufimg.ByteIndexedBm.opaque=disabled
+graphics.imaging.src.bufimg.ByteIndexedBm.bitmask=disabled
+graphics.imaging.src.bufimg.ByteIndexedBm.translucent=disabled
+graphics.imaging.src.bufimg.unmanagedIntXrgb.opaque=disabled
+graphics.imaging.src.bufimg.unmanagedIntXrgb.bitmask=disabled
+graphics.imaging.src.bufimg.unmanagedIntXrgb.translucent=disabled
+graphics.imaging.src.bufimg.unmanagedIntArgb.opaque=disabled
+graphics.imaging.src.bufimg.unmanagedIntArgb.bitmask=disabled
+graphics.imaging.src.bufimg.unmanagedIntArgb.translucent=disabled
+graphics.imaging.src.bufimg.unmanagedIntArgbPre.opaque=disabled
+graphics.imaging.src.bufimg.unmanagedIntArgbPre.bitmask=disabled
+graphics.imaging.src.bufimg.unmanagedIntArgbPre.translucent=disabled
+graphics.imaging.src.bufimg.unmanaged3ByteBgr.opaque=disabled
+graphics.imaging.src.bufimg.unmanaged3ByteBgr.bitmask=disabled
+graphics.imaging.src.bufimg.unmanaged3ByteBgr.translucent=disabled
+graphics.imaging.benchmarks.opts.interpolation=Nearest neighbor
+graphics.imaging.benchmarks.opts.touchsrc=Off
+graphics.imaging.benchmarks.tests.drawimage=disabled
+graphics.imaging.benchmarks.tests.drawimagebg=disabled
+graphics.imaging.benchmarks.tests.drawimagescaleup=disabled
+graphics.imaging.benchmarks.tests.drawimagescaledown=disabled
+graphics.imaging.benchmarks.tests.drawimagescalesplit=disabled
+graphics.imaging.benchmarks.tests.drawimagetxform=disabled
+graphics.imaging.imageops.opts.op=convolve3x3zero
+graphics.imaging.imageops.tests.graphics2d.drawimageop=disabled
+graphics.imaging.imageops.tests.bufimgop.filternull=disabled
+graphics.imaging.imageops.tests.bufimgop.filtercached=disabled
+graphics.imaging.imageops.tests.rasterop.filternull=disabled
+graphics.imaging.imageops.tests.rasterop.filtercached=disabled
+graphics.misc.copytests.copyAreaVert=disabled
+graphics.misc.copytests.copyAreaHoriz=disabled
+graphics.misc.copytests.copyAreaDiag=disabled
+pixel.opts.renderto=Off
+pixel.opts.renderfrom=Off
+pixel.src.1BitBinary=disabled
+pixel.src.2BitBinary=disabled
+pixel.src.4BitBinary=disabled
+pixel.src.ByteIndexed=disabled
+pixel.src.ByteGray=disabled
+pixel.src.Short555=disabled
+pixel.src.Short565=disabled
+pixel.src.ShortGray=disabled
+pixel.src.3ByteBgr=disabled
+pixel.src.4ByteAbgr=disabled
+pixel.src.IntXrgb=disabled
+pixel.src.IntXbgr=disabled
+pixel.src.IntArgb=disabled
+pixel.bimgtests.getrgb=disabled
+pixel.bimgtests.setrgb=disabled
+pixel.rastests.getdataelem=disabled
+pixel.rastests.setdataelem=disabled
+pixel.rastests.getpixel=disabled
+pixel.rastests.setpixel=disabled
+pixel.dbtests.getelem=disabled
+pixel.dbtests.setelem=disabled
+text.opts.data.tlength=16
+text.opts.data.tscript=english
+text.opts.font.fname=serif,physical
+text.opts.font.fstyle=0
+text.opts.font.fsize=13.0
+text.opts.font.ftx=Identity
+text.opts.graphics.textaa=Off
+text.opts.graphics.tfm=Off
+text.opts.graphics.gaa=Off
+text.opts.graphics.gtx=Identity
+text.opts.advopts.gvstyle=0
+text.opts.advopts.tlruns=1
+text.opts.advopts.maptype=FONT
+text.Rendering.tests.drawString=disabled
+text.Rendering.tests.drawChars=disabled
+text.Rendering.tests.drawBytes=disabled
+text.Rendering.tests.drawGlyphVectors=disabled
+text.Rendering.tests.drawTextLayout=disabled
+text.Measuring.tests.stringWidth=disabled
+text.Measuring.tests.stringBounds=disabled
+text.Measuring.tests.charsWidth=disabled
+text.Measuring.tests.charsBounds=disabled
+text.Measuring.tests.fontcandisplay=disabled
+text.Measuring.tests.gvWidth=disabled
+text.Measuring.tests.gvLogicalBounds=disabled
+text.Measuring.tests.gvVisualBounds=disabled
+text.Measuring.tests.gvPixelBounds=disabled
+text.Measuring.tests.gvOutline=disabled
+text.Measuring.tests.gvGlyphLogicalBounds=disabled
+text.Measuring.tests.gvGlyphVisualBounds=disabled
+text.Measuring.tests.gvGlyphPixelBounds=disabled
+text.Measuring.tests.gvGlyphOutline=disabled
+text.Measuring.tests.gvGlyphTransform=disabled
+text.Measuring.tests.gvGlyphMetrics=disabled
+text.Measuring.tests.tlAdvance=disabled
+text.Measuring.tests.tlAscent=disabled
+text.Measuring.tests.tlBounds=disabled
+text.Measuring.tests.tlGetCaretInfo=disabled
+text.Measuring.tests.tlGetNextHit=disabled
+text.Measuring.tests.tlGetCaretShape=disabled
+text.Measuring.tests.tlGetLogicalHighlightShape=disabled
+text.Measuring.tests.tlHitTest=disabled
+text.Measuring.tests.tlOutline=disabled
+text.construction.tests.gvfromfontstring=disabled
+text.construction.tests.gvfromfontchars=disabled
+text.construction.tests.gvfromfontci=disabled
+text.construction.tests.gvfromfontglyphs=disabled
+text.construction.tests.gvfromfontlayout=disabled
+text.construction.tests.tlfromfont=disabled
+text.construction.tests.tlfrommap=disabled
+imageio.opts.size=250
+imageio.opts.content=photo
+imageio.input.opts.general.source.file=disabled
+imageio.input.opts.general.source.url=disabled
+imageio.input.opts.general.source.byteArray=disabled
+imageio.input.opts.imageio.useCache=Off
+imageio.input.image.toolkit.opts.format=
+imageio.input.image.toolkit.tests.createImage=disabled
+imageio.input.image.imageio.opts.format=
+imageio.input.image.imageio.tests.imageioRead=disabled
+imageio.input.image.imageio.reader.opts.seekForwardOnly=On
+imageio.input.image.imageio.reader.opts.ignoreMetadata=On
+imageio.input.image.imageio.reader.opts.installListener=Off
+imageio.input.image.imageio.reader.tests.read=disabled
+imageio.input.image.imageio.reader.tests.getImageMetadata=disabled
+imageio.input.stream.tests.construct=disabled
+imageio.input.stream.tests.read=disabled
+imageio.input.stream.tests.readByteArray=disabled
+imageio.input.stream.tests.readFullyByteArray=disabled
+imageio.input.stream.tests.readBit=disabled
+imageio.input.stream.tests.readByte=disabled
+imageio.input.stream.tests.readUnsignedByte=disabled
+imageio.input.stream.tests.readShort=disabled
+imageio.input.stream.tests.readUnsignedShort=disabled
+imageio.input.stream.tests.readInt=disabled
+imageio.input.stream.tests.readUnsignedInt=disabled
+imageio.input.stream.tests.readFloat=disabled
+imageio.input.stream.tests.readLong=disabled
+imageio.input.stream.tests.readDouble=disabled
+imageio.input.stream.tests.skipBytes=disabled
+imageio.output.opts.general.dest.file=disabled
+imageio.output.opts.general.dest.byteArray=disabled
+imageio.output.opts.imageio.useCache=Off
+imageio.output.image.imageio.opts.format=
+imageio.output.image.imageio.tests.imageioWrite=disabled
+imageio.output.image.imageio.writer.opts.installListener=Off
+imageio.output.image.imageio.writer.tests.write=disabled
+imageio.output.stream.tests.construct=disabled
+imageio.output.stream.tests.write=disabled
+imageio.output.stream.tests.writeByteArray=disabled
+imageio.output.stream.tests.writeBit=disabled
+imageio.output.stream.tests.writeByte=disabled
+imageio.output.stream.tests.writeShort=disabled
+imageio.output.stream.tests.writeInt=disabled
+imageio.output.stream.tests.writeFloat=disabled
+imageio.output.stream.tests.writeLong=disabled
+imageio.output.stream.tests.writeDouble=disabled
+cmm.opts.profiles=1001
+cmm.colorconv.data.fromRGB=disabled
+cmm.colorconv.data.toRGB=disabled
+cmm.colorconv.data.fromCIEXYZ=disabled
+cmm.colorconv.data.toCIEXYZ=disabled
+cmm.colorconv.ccop.ccopOptions.size=250
+cmm.colorconv.ccop.ccopOptions.content=photo
+cmm.colorconv.ccop.ccopOptions.srcType=INT_RGB
+cmm.colorconv.ccop.ccopOptions.dstType=INT_RGB
+cmm.colorconv.ccop.op_img=disabled
+cmm.colorconv.ccop.op_rst=disabled
+cmm.colorconv.ccop.op_draw=disabled
+cmm.colorconv.embed.embedOptions.Images=512x512
+cmm.colorconv.embed.embd_img_read=disabled
+cmm.profiles.getHeader=disabled
+cmm.profiles.getNumComponents=disabled
diff --git a/jb/project/tools/perf/j2dbopts_poly250.txt b/jb/project/tools/perf/j2dbopts_poly250.txt
new file mode 100644
index 000000000000..6cd894100d75
--- /dev/null
+++ b/jb/project/tools/perf/j2dbopts_poly250.txt
@@ -0,0 +1,267 @@
+prog.verbose=disabled
+prog.printresults=enabled
+global.env.outputwidth=640
+global.env.outputheight=480
+global.env.runcount=5
+global.env.repcount=0
+global.env.testtime=2500
+global.results.workunits=units
+global.results.timeunits=sec
+global.results.ratio=unitspersec
+global.dest.offscreen=disabled
+global.dest.frame.defaultframe=enabled
+global.dest.frame.transframe=disabled
+global.dest.frame.shapedframe=disabled
+global.dest.frame.shapedtransframe=disabled
+global.dest.compatimg.compatimg=disabled
+global.dest.compatimg.opqcompatimg=disabled
+global.dest.compatimg.bmcompatimg=disabled
+global.dest.compatimg.transcompatimg=disabled
+global.dest.volimg.volimg=disabled
+global.dest.volimg.opqvolimg=disabled
+global.dest.volimg.bmvolimg=disabled
+global.dest.volimg.transvolimg=disabled
+global.dest.bufimg.IntXrgb=disabled
+global.dest.bufimg.IntArgb=disabled
+global.dest.bufimg.IntArgbPre=disabled
+global.dest.bufimg.3ByteBgr=disabled
+global.dest.bufimg.ByteIndexed=disabled
+global.dest.bufimg.ByteGray=disabled
+global.dest.bufimg.4ByteAbgr=disabled
+global.dest.bufimg.4ByteAbgrPre=disabled
+global.dest.bufimg.custom=disabled
+graphics.opts.anim=2
+graphics.opts.sizes=250
+graphics.opts.alpharule=SrcOver
+graphics.opts.transform=ident
+graphics.opts.extraalpha=Off
+graphics.opts.xormode=Off
+graphics.opts.clip=Off
+graphics.opts.renderhint=Default
+graphics.render.opts.paint=single
+graphics.render.opts.alphacolor=Off
+graphics.render.opts.antialias=Off
+graphics.render.opts.stroke=width1
+graphics.render.tests.drawLine=disabled
+graphics.render.tests.drawLineHoriz=disabled
+graphics.render.tests.drawLineVert=disabled
+graphics.render.tests.fillRect=disabled
+graphics.render.tests.drawRect=disabled
+graphics.render.tests.fillOval=disabled
+graphics.render.tests.drawOval=disabled
+graphics.render.tests.fillPoly=disabled
+graphics.render.tests.drawPoly=enabled
+graphics.render.tests.shape.fillCubic=disabled
+graphics.render.tests.shape.drawCubic=disabled
+graphics.render.tests.shape.fillEllipse2D=disabled
+graphics.render.tests.shape.drawEllipse2D=disabled
+graphics.imaging.src.offscr.opaque=disabled
+graphics.imaging.src.offscr.bitmask=disabled
+graphics.imaging.src.offscr.translucent=disabled
+graphics.imaging.src.opqcompatimg.opaque=disabled
+graphics.imaging.src.opqcompatimg.bitmask=disabled
+graphics.imaging.src.opqcompatimg.translucent=disabled
+graphics.imaging.src.bmcompatimg.opaque=disabled
+graphics.imaging.src.bmcompatimg.bitmask=disabled
+graphics.imaging.src.bmcompatimg.translucent=disabled
+graphics.imaging.src.transcompatimg.opaque=disabled
+graphics.imaging.src.transcompatimg.bitmask=disabled
+graphics.imaging.src.transcompatimg.translucent=disabled
+graphics.imaging.src.opqvolimg.opaque=disabled
+graphics.imaging.src.opqvolimg.bitmask=disabled
+graphics.imaging.src.opqvolimg.translucent=disabled
+graphics.imaging.src.bmvolimg.opaque=disabled
+graphics.imaging.src.bmvolimg.bitmask=disabled
+graphics.imaging.src.bmvolimg.translucent=disabled
+graphics.imaging.src.transvolimg.opaque=disabled
+graphics.imaging.src.transvolimg.bitmask=disabled
+graphics.imaging.src.transvolimg.translucent=disabled
+graphics.imaging.src.bufimg.IntXrgb.opaque=disabled
+graphics.imaging.src.bufimg.IntXrgb.bitmask=disabled
+graphics.imaging.src.bufimg.IntXrgb.translucent=disabled
+graphics.imaging.src.bufimg.IntArgb.opaque=disabled
+graphics.imaging.src.bufimg.IntArgb.bitmask=disabled
+graphics.imaging.src.bufimg.IntArgb.translucent=disabled
+graphics.imaging.src.bufimg.IntArgbPre.opaque=disabled
+graphics.imaging.src.bufimg.IntArgbPre.bitmask=disabled
+graphics.imaging.src.bufimg.IntArgbPre.translucent=disabled
+graphics.imaging.src.bufimg.ByteGray.opaque=disabled
+graphics.imaging.src.bufimg.ByteGray.bitmask=disabled
+graphics.imaging.src.bufimg.ByteGray.translucent=disabled
+graphics.imaging.src.bufimg.3ByteBgr.opaque=disabled
+graphics.imaging.src.bufimg.3ByteBgr.bitmask=disabled
+graphics.imaging.src.bufimg.3ByteBgr.translucent=disabled
+graphics.imaging.src.bufimg.4ByteAbgr.opaque=disabled
+graphics.imaging.src.bufimg.4ByteAbgr.bitmask=disabled
+graphics.imaging.src.bufimg.4ByteAbgr.translucent=disabled
+graphics.imaging.src.bufimg.4ByteAbgrPre.opaque=disabled
+graphics.imaging.src.bufimg.4ByteAbgrPre.bitmask=disabled
+graphics.imaging.src.bufimg.4ByteAbgrPre.translucent=disabled
+graphics.imaging.src.bufimg.ByteIndexedBm.opaque=disabled
+graphics.imaging.src.bufimg.ByteIndexedBm.bitmask=disabled
+graphics.imaging.src.bufimg.ByteIndexedBm.translucent=disabled
+graphics.imaging.src.bufimg.unmanagedIntXrgb.opaque=disabled
+graphics.imaging.src.bufimg.unmanagedIntXrgb.bitmask=disabled
+graphics.imaging.src.bufimg.unmanagedIntXrgb.translucent=disabled
+graphics.imaging.src.bufimg.unmanagedIntArgb.opaque=disabled
+graphics.imaging.src.bufimg.unmanagedIntArgb.bitmask=disabled
+graphics.imaging.src.bufimg.unmanagedIntArgb.translucent=disabled
+graphics.imaging.src.bufimg.unmanagedIntArgbPre.opaque=disabled
+graphics.imaging.src.bufimg.unmanagedIntArgbPre.bitmask=disabled
+graphics.imaging.src.bufimg.unmanagedIntArgbPre.translucent=disabled
+graphics.imaging.src.bufimg.unmanaged3ByteBgr.opaque=disabled
+graphics.imaging.src.bufimg.unmanaged3ByteBgr.bitmask=disabled
+graphics.imaging.src.bufimg.unmanaged3ByteBgr.translucent=disabled
+graphics.imaging.benchmarks.opts.interpolation=Nearest neighbor
+graphics.imaging.benchmarks.opts.touchsrc=Off
+graphics.imaging.benchmarks.tests.drawimage=disabled
+graphics.imaging.benchmarks.tests.drawimagebg=disabled
+graphics.imaging.benchmarks.tests.drawimagescaleup=disabled
+graphics.imaging.benchmarks.tests.drawimagescaledown=disabled
+graphics.imaging.benchmarks.tests.drawimagescalesplit=disabled
+graphics.imaging.benchmarks.tests.drawimagetxform=disabled
+graphics.imaging.imageops.opts.op=convolve3x3zero
+graphics.imaging.imageops.tests.graphics2d.drawimageop=disabled
+graphics.imaging.imageops.tests.bufimgop.filternull=disabled
+graphics.imaging.imageops.tests.bufimgop.filtercached=disabled
+graphics.imaging.imageops.tests.rasterop.filternull=disabled
+graphics.imaging.imageops.tests.rasterop.filtercached=disabled
+graphics.misc.copytests.copyAreaVert=disabled
+graphics.misc.copytests.copyAreaHoriz=disabled
+graphics.misc.copytests.copyAreaDiag=disabled
+pixel.opts.renderto=Off
+pixel.opts.renderfrom=Off
+pixel.src.1BitBinary=disabled
+pixel.src.2BitBinary=disabled
+pixel.src.4BitBinary=disabled
+pixel.src.ByteIndexed=disabled
+pixel.src.ByteGray=disabled
+pixel.src.Short555=disabled
+pixel.src.Short565=disabled
+pixel.src.ShortGray=disabled
+pixel.src.3ByteBgr=disabled
+pixel.src.4ByteAbgr=disabled
+pixel.src.IntXrgb=disabled
+pixel.src.IntXbgr=disabled
+pixel.src.IntArgb=disabled
+pixel.bimgtests.getrgb=disabled
+pixel.bimgtests.setrgb=disabled
+pixel.rastests.getdataelem=disabled
+pixel.rastests.setdataelem=disabled
+pixel.rastests.getpixel=disabled
+pixel.rastests.setpixel=disabled
+pixel.dbtests.getelem=disabled
+pixel.dbtests.setelem=disabled
+text.opts.data.tlength=16
+text.opts.data.tscript=english
+text.opts.font.fname=serif,physical
+text.opts.font.fstyle=0
+text.opts.font.fsize=13.0
+text.opts.font.ftx=Identity
+text.opts.graphics.textaa=Off
+text.opts.graphics.tfm=Off
+text.opts.graphics.gaa=Off
+text.opts.graphics.gtx=Identity
+text.opts.advopts.gvstyle=0
+text.opts.advopts.tlruns=1
+text.opts.advopts.maptype=FONT
+text.Rendering.tests.drawString=disabled
+text.Rendering.tests.drawChars=disabled
+text.Rendering.tests.drawBytes=disabled
+text.Rendering.tests.drawGlyphVectors=disabled
+text.Rendering.tests.drawTextLayout=disabled
+text.Measuring.tests.stringWidth=disabled
+text.Measuring.tests.stringBounds=disabled
+text.Measuring.tests.charsWidth=disabled
+text.Measuring.tests.charsBounds=disabled
+text.Measuring.tests.fontcandisplay=disabled
+text.Measuring.tests.gvWidth=disabled
+text.Measuring.tests.gvLogicalBounds=disabled
+text.Measuring.tests.gvVisualBounds=disabled
+text.Measuring.tests.gvPixelBounds=disabled
+text.Measuring.tests.gvOutline=disabled
+text.Measuring.tests.gvGlyphLogicalBounds=disabled
+text.Measuring.tests.gvGlyphVisualBounds=disabled
+text.Measuring.tests.gvGlyphPixelBounds=disabled
+text.Measuring.tests.gvGlyphOutline=disabled
+text.Measuring.tests.gvGlyphTransform=disabled
+text.Measuring.tests.gvGlyphMetrics=disabled
+text.Measuring.tests.tlAdvance=disabled
+text.Measuring.tests.tlAscent=disabled
+text.Measuring.tests.tlBounds=disabled
+text.Measuring.tests.tlGetCaretInfo=disabled
+text.Measuring.tests.tlGetNextHit=disabled
+text.Measuring.tests.tlGetCaretShape=disabled
+text.Measuring.tests.tlGetLogicalHighlightShape=disabled
+text.Measuring.tests.tlHitTest=disabled
+text.Measuring.tests.tlOutline=disabled
+text.construction.tests.gvfromfontstring=disabled
+text.construction.tests.gvfromfontchars=disabled
+text.construction.tests.gvfromfontci=disabled
+text.construction.tests.gvfromfontglyphs=disabled
+text.construction.tests.gvfromfontlayout=disabled
+text.construction.tests.tlfromfont=disabled
+text.construction.tests.tlfrommap=disabled
+imageio.opts.size=250
+imageio.opts.content=photo
+imageio.input.opts.general.source.file=disabled
+imageio.input.opts.general.source.url=disabled
+imageio.input.opts.general.source.byteArray=disabled
+imageio.input.opts.imageio.useCache=Off
+imageio.input.image.toolkit.opts.format=
+imageio.input.image.toolkit.tests.createImage=disabled
+imageio.input.image.imageio.opts.format=
+imageio.input.image.imageio.tests.imageioRead=disabled
+imageio.input.image.imageio.reader.opts.seekForwardOnly=On
+imageio.input.image.imageio.reader.opts.ignoreMetadata=On
+imageio.input.image.imageio.reader.opts.installListener=Off
+imageio.input.image.imageio.reader.tests.read=disabled
+imageio.input.image.imageio.reader.tests.getImageMetadata=disabled
+imageio.input.stream.tests.construct=disabled
+imageio.input.stream.tests.read=disabled
+imageio.input.stream.tests.readByteArray=disabled
+imageio.input.stream.tests.readFullyByteArray=disabled
+imageio.input.stream.tests.readBit=disabled
+imageio.input.stream.tests.readByte=disabled
+imageio.input.stream.tests.readUnsignedByte=disabled
+imageio.input.stream.tests.readShort=disabled
+imageio.input.stream.tests.readUnsignedShort=disabled
+imageio.input.stream.tests.readInt=disabled
+imageio.input.stream.tests.readUnsignedInt=disabled
+imageio.input.stream.tests.readFloat=disabled
+imageio.input.stream.tests.readLong=disabled
+imageio.input.stream.tests.readDouble=disabled
+imageio.input.stream.tests.skipBytes=disabled
+imageio.output.opts.general.dest.file=disabled
+imageio.output.opts.general.dest.byteArray=disabled
+imageio.output.opts.imageio.useCache=Off
+imageio.output.image.imageio.opts.format=
+imageio.output.image.imageio.tests.imageioWrite=disabled
+imageio.output.image.imageio.writer.opts.installListener=Off
+imageio.output.image.imageio.writer.tests.write=disabled
+imageio.output.stream.tests.construct=disabled
+imageio.output.stream.tests.write=disabled
+imageio.output.stream.tests.writeByteArray=disabled
+imageio.output.stream.tests.writeBit=disabled
+imageio.output.stream.tests.writeByte=disabled
+imageio.output.stream.tests.writeShort=disabled
+imageio.output.stream.tests.writeInt=disabled
+imageio.output.stream.tests.writeFloat=disabled
+imageio.output.stream.tests.writeLong=disabled
+imageio.output.stream.tests.writeDouble=disabled
+cmm.opts.profiles=1001
+cmm.colorconv.data.fromRGB=disabled
+cmm.colorconv.data.toRGB=disabled
+cmm.colorconv.data.fromCIEXYZ=disabled
+cmm.colorconv.data.toCIEXYZ=disabled
+cmm.colorconv.ccop.ccopOptions.size=250
+cmm.colorconv.ccop.ccopOptions.content=photo
+cmm.colorconv.ccop.ccopOptions.srcType=INT_RGB
+cmm.colorconv.ccop.ccopOptions.dstType=INT_RGB
+cmm.colorconv.ccop.op_img=disabled
+cmm.colorconv.ccop.op_rst=disabled
+cmm.colorconv.ccop.op_draw=disabled
+cmm.colorconv.embed.embedOptions.Images=512x512
+cmm.colorconv.embed.embd_img_read=disabled
+cmm.profiles.getHeader=disabled
+cmm.profiles.getNumComponents=disabled
diff --git a/jb/project/tools/perf/run_inc.sh b/jb/project/tools/perf/run_inc.sh
new file mode 100644
index 000000000000..109119fa7877
--- /dev/null
+++ b/jb/project/tools/perf/run_inc.sh
@@ -0,0 +1,98 @@
+ST=1 # sleep between iterations
+
+# number of iterations (jvm spawned)
+N=5
+# number of repeats (within jvm)
+R=3
+
+type datamash 2>&1 > /dev/null ; ec=$?
+if [ $ec -ne 0 ] ; then
+ echo "Missing datamash utility"
+ exit 1
+fi
+
+DATAMASH_CMD="datamash --format=%.2f -H count x min x q1 x median x q3 x max x mad x"
+
+J2D_OPTS=""
+OS=""
+case "$OSTYPE" in
+ linux*) echo "Linux"
+ ;;
+ darwin*) echo "OSX"
+ ;;
+ *) echo "unknown: $OSTYPE"
+ exit 1
+ ;;
+esac
+
+read -r -d '' RENDER_OPS_DOC << EOM
+rendering_options:
+ -opengl # OpenGL pipeline (windows, linux, macOS)
+ -metal # Metal pipeline (macOS)
+ -tk tk_name # AWT toolkit (linux: WLToolkit|XToolkit)
+ -scale # UI scale
+EOM
+
+while [ $# -ge 1 ] ; do
+ case "$1" in
+ -opengl) J2D_OPTS=$J2D_OPTS" -Dsun.java2d.opengl=true"
+ shift
+ ;;
+ -metal) J2D_OPTS=$J2D_OPTS" -Dsun.java2d.metal=true"
+ shift
+ ;;
+ -tk) shift
+ if [ $# -ge 1 ] ; then
+ J2D_OPTS=$J2D_OPTS" -Dawt.toolkit.name="$1
+ shift
+ else
+ echo "Invalid parameters for -tk option. Use: -tk tkname"
+ exit 1
+ fi
+ ;;
+ -scale) shift
+ if [ $# -ge 1 ] ; then
+ J2D_OPTS=$J2D_OPTS" -Dsun.java2d.uiScale="$1
+ shift
+ else
+ echo "Invalid parameters for -scale option. Use: -scale scale"
+ exit 1
+ fi
+ ;;
+ -dSync) shift
+ if [ $# -ge 1 ] ; then
+ J2D_OPTS=$J2D_OPTS" -Dsun.java2d.metal.displaySync="$1
+ shift
+ else
+ echo "Invalid parameters for -dSync option. Use: -dSync [true|false]"
+ exit 1
+ fi
+ ;;
+ -jdk) shift
+ if [ $# -ge 1 ] ; then
+ JAVA=$1/bin/java
+ shift
+ else
+ echo "Invalid parameters for -jdk option"
+ exit 1
+ fi
+ ;;
+ *) break
+ ;;
+ esac
+done
+if [ -z "$JAVA" ] ; then
+ BUILD_DIR=`find $BASE_DIR/../../../../build -name '*-release' -type d | head -n 1`
+ JAVA=`find $BUILD_DIR/images/jdk -name java -type f | head -n 1`
+fi
+
+JAVA_HOME=`dirname $JAVA`/../
+"$JAVA" -version
+
+LANG=C
+
+WS_ROOT=$BASE_DIR/../../../..
+
+echo "N: $N"
+echo "R: $R"
+echo "J2D_OPTS: $J2D_OPTS"
diff --git a/jb/project/tools/perf/run_j2b.sh b/jb/project/tools/perf/run_j2b.sh
new file mode 100755
index 000000000000..b20d53921053
--- /dev/null
+++ b/jb/project/tools/perf/run_j2b.sh
@@ -0,0 +1,53 @@
+#!/bin/bash
+
+BASE_DIR=$(dirname "$0")
+source $BASE_DIR/run_inc.sh
+
+J2DBENCH_DIR=$WS_ROOT/src/demo/share/java2d/J2DBench
+
+if [ -z "$J2DBENCH" ]; then
+ if [ ! -f "$J2DBENCH_DIR/dist/J2DBench.jar" ]; then
+ PATH=$JAVA_HOME/bin:$PATH make -C $J2DBENCH_DIR
+ fi
+ if [ ! -f "$J2DBENCH_DIR/dist/J2DBench.jar" ]; then
+ echo "Cannot build J2DBench. You may use J2DBench env variable instead pointing to the J2DBench.jar."
+ exit 1
+ fi
+ J2DBENCH=$J2DBENCH_DIR/dist/J2DBench.jar
+fi
+
+if [ $# -ne 1 ] ; then
+ echo "Usage: run_j2b.sh [rendering_options] bench_name"
+ echo
+ echo "bench_name: poly250 poly250-rand_col poly250-AA-rand_col"
+ echo ""
+ echo "$RENDER_OPS_DOC"
+ exit 2
+fi
+
+if [ ! -f "$BASE_DIR/j2dbopts_$1.txt" ]; then
+ echo "Unknown test: $1"
+ exit 1
+fi
+
+
+
+OPTS="j2dbopts_$1.txt"
+#OPTS=j2dbopts_poly250.txt
+#OPTS=j2dbopts_poly250-rand_col.txt
+#OPTS=j2dbopts_poly250-AA-rand_col.txt
+
+echo "OPTS: $OPTS"
+
+for i in `seq $N`; do
+ if [ $i -eq 1 ]; then
+ echo x
+ fi
+ echo `$JAVA $J2D_OPTS -jar $J2DBENCH \
+ -batch -loadopts $BASE_DIR/$OPTS -saveres pl.res \
+ -title pl -desc pl | awk '/averaged/{print $3}' | head -n1`
+
+ if [ $i -ne $N ]; then
+ sleep $ST
+ fi
+done | $DATAMASH_CMD | expand -t12
diff --git a/jb/project/tools/perf/run_rp.sh b/jb/project/tools/perf/run_rp.sh
new file mode 100755
index 000000000000..5cbd0f6cdcb5
--- /dev/null
+++ b/jb/project/tools/perf/run_rp.sh
@@ -0,0 +1,64 @@
+#!/bin/bash
+
+BASE_DIR=$(dirname "$0")
+source $BASE_DIR/run_inc.sh
+
+RENDERPERFTEST_DIR=$WS_ROOT/test/jdk/performance/client/RenderPerfTest
+
+if [ -z "$RENDERPERFTEST" ]; then
+ if [ ! -f "$RENDERPERFTEST_DIR/dist/RenderPerfTest.jar" ]; then
+ PATH=$JAVA_HOME/bin:$PATH make -C $RENDERPERFTEST_DIR
+ fi
+ if [ ! -f "$RENDERPERFTEST_DIR/dist/RenderPerfTest.jar" ]; then
+ echo "Cannot build RenderPerfTest. You may use RENDERPERFTEST env variable instead pointing to the RenderPerfTest.jar."
+ exit 1
+ fi
+ RENDERPERFTEST=$RENDERPERFTEST_DIR/dist/RenderPerfTest.jar
+fi
+
+
+TRACE=false
+
+MODE=Robot
+#MODE=Buffer
+#MODE=Volatile
+
+if [ $# -ne 1 ] ; then
+ echo "Usage: run_rp.sh [rendering_options] bench_name"
+ echo
+ echo "bench_name: ArgbSurfaceBlitImage ArgbSwBlitImage BgrSurfaceBlitImage BgrSwBlitImage"
+ echo " Image ImageAA Image_XOR VolImage VolImageAA"
+ echo " ClipFlatBox ClipFlatBoxAA ClipFlatOval ClipFlatOvalAA"
+ echo " FlatBox FlatBoxAA FlatOval FlatOvalAA FlatOval_XOR FlatQuad FlatQuadAA"
+ echo " RotatedBox RotatedBoxAA RotatedBox_XOR RotatedOval RotatedOvalAA"
+ echo " WiredBox WiredBoxAA WiredBubbles WiredBubblesAA WiredQuad WiredQuadAA"
+ echo " Lines LinesAA Lines_XOR"
+ echo " TextGray TextLCD TextLCD_XOR TextNoAA TextNoAA_XOR"
+ echo " LargeTextGray LargeTextLCD LargeTextNoAA WhiteTextGray WhiteTextLCD WhiteTextNoAA"
+ echo " LinGrad3RotatedOval LinGrad3RotatedOvalAA LinGradRotatedOval LinGradRotatedOvalAA"
+ echo " RadGrad3RotatedOval RadGrad3RotatedOvalAA"
+ echo ""
+ echo "$RENDER_OPS_DOC"
+ exit 2
+fi
+
+
+# use time + repeat
+OPTS="$OPTS -r$R -t -e$MODE $1"
+
+echo "OPTS: $OPTS"
+
+echo "Unit: Milliseconds (not FPS), lower is better"
+
+for i in `seq $N` ; do
+ if [ $i -eq 1 ]; then
+ echo x
+ fi
+
+ $JAVA $J2D_OPTS -DTRACE=$TRACE \
+ -jar $RENDERPERFTEST $OPTS 2>&1 | tail -n +2 | \
+ awk '/'$1'/{print $3 }'
+ if [ $i -ne $N ]; then
+ sleep $ST
+ fi
+done | $DATAMASH_CMD | expand -t12
diff --git a/jb/project/tools/perf/run_sm.sh b/jb/project/tools/perf/run_sm.sh
new file mode 100755
index 000000000000..1d758dacd28e
--- /dev/null
+++ b/jb/project/tools/perf/run_sm.sh
@@ -0,0 +1,39 @@
+#!/bin/bash
+
+BASE_DIR=$(dirname "$0")
+source $BASE_DIR/run_inc.sh
+
+SWINGMARK_DIR=$WS_ROOT/test/jdk/performance/client/SwingMark
+
+if [ -z "$SWINGMARK" ]; then
+ if [ ! -f "$SWINGMARK_DIR/dist/SwingMark.jar" ]; then
+ PATH=$JAVA_HOME/bin:$PATH make -C $SWINGMARK_DIR
+ fi
+ if [ ! -f "$SWINGMARK_DIR/dist/SwingMark.jar" ]; then
+ echo "Cannot build SwingMark. You may use SWINGMARK env variable instead pointing to the SwingMark.jar."
+ exit 1
+ fi
+ SWINGMARK=$SWINGMARK_DIR/dist/SwingMark.jar
+fi
+
+if [ $# -eq 1 -a "$1" == "--help" ] ; then
+ shift
+ echo "Usage: run_sm [rendering_options]"
+ echo ""
+ echo "$RENDER_OPS_DOC"
+ exit 0
+fi
+
+for i in `seq $N` ; do
+ if [ $i -eq 1 ]; then
+ echo x
+ fi
+
+ # SwingMark gives 1 global 'Score: '
+ echo `$JAVA $J2D_OPTS -jar $BASE_DIR/../../../../test/jdk/performance/client/SwingMark/dist/SwingMark.jar \
+ -r $R -q -lf javax.swing.plaf.metal.MetalLookAndFeel | awk '/Score/{print $2}'`
+
+ if [ $i -ne $N ]; then
+ sleep $ST
+ fi
+done | $DATAMASH_CMD | expand -t12
diff --git a/jb/project/tools/test/check_jbr_size.sh b/jb/project/tools/test/check_jbr_size.sh
new file mode 100644
index 000000000000..3a9ef2f5a7ad
--- /dev/null
+++ b/jb/project/tools/test/check_jbr_size.sh
@@ -0,0 +1,162 @@
+#!/bin/bash
+
+set -euo pipefail
+
+TC_PRINT=0
+# Always print TeamCity service messages if running under TeamCity
+[[ -n "${TEAMCITY_VERSION:-}" ]] && TC_PRINT=1
+
+while getopts ":t" o; do
+ case "${o}" in
+ t) TC_PRINT=1 ;;
+ *);;
+ esac
+done
+shift $((OPTIND-1))
+
+NEWFILEPATH="$1"
+CONFIGID="$2"
+BUILDID="$3"
+TOKEN="$4"
+
+if [ ! -f "$NEWFILEPATH" ]; then
+ echo "File not found: $NEWFILEPATH"
+ exit 1
+fi
+#
+# Get the size of new artifact
+#
+
+unameOut="$(uname -s)"
+case "${unameOut}" in
+ Linux*)
+ NEWFILESIZE=$(stat -c%s "$NEWFILEPATH")
+ ;;
+ Darwin*)
+ NEWFILESIZE=$(stat -f%z "$NEWFILEPATH")
+ ;;
+ CYGWIN*)
+ NEWFILESIZE=$(stat -c%s "$NEWFILEPATH")
+ ;;
+ MINGW*)
+ NEWFILESIZE=$(stat -c%s "$NEWFILEPATH")
+ ;;
+ *)
+ echo "Unknown machine: ${unameOut}"
+ exit 1
+esac
+FILENAME=$(basename "${NEWFILEPATH}")
+
+#
+# Get pattern of artifact name
+# Base filename pattern: ----b.tar.gz: jbr_dcevm-17.0.2-osx-x64-b1234.tar.gz
+# BUNDLE_TYPE: jbr, jbrsdk, jbr_dcevm, jbrsdk_jcef etc.
+# OS_ARCH_PATTERN - : osx-x64, linux-aarch64, linux-musl-x64, windows-x64 etc.
+
+BUNDLE_TYPE=jbrsdk
+OS_ARCH_PATTERN=""
+FILE_EXTENSION=tar.gz
+
+re='(jbr[a-z_]*).*-[0-9_\.]+-(.+)-b[0-9]+(.+)'
+if [[ $FILENAME =~ $re ]]; then
+ BUNDLE_TYPE=${BASH_REMATCH[1]}
+ OS_ARCH_PATTERN=${BASH_REMATCH[2]}
+ FILE_EXTENSION=${BASH_REMATCH[3]}
+else
+ echo "File name $FILENAME does not match regex $re"
+ exit 1
+fi
+
+function test_started_msg() {
+ if [ $TC_PRINT -eq 1 ]; then
+ echo "##teamcity[testStarted name='$1']"
+ fi
+}
+
+function test_failed_msg() {
+ if [ $TC_PRINT -eq 1 ]; then
+ echo "##teamcity[testFailed name='$1' message='$2']"
+ fi
+}
+
+function test_finished_msg() {
+ if [ $TC_PRINT -eq 1 ]; then
+ echo "##teamcity[testFinished name='$1']"
+ fi
+}
+
+test_name="${BUNDLE_TYPE}_${OS_ARCH_PATTERN//\-/_}${FILE_EXTENSION//\./_}"
+test_started_msg "$test_name"
+
+echo "BUNDLE_TYPE: $BUNDLE_TYPE"
+echo "OS_ARCH_PATTERN: $OS_ARCH_PATTERN"
+echo "FILE_EXTENSION: $FILE_EXTENSION"
+echo "Size of $FILENAME is $NEWFILESIZE bytes"
+
+#
+# Get previous successful build ID
+# Example:
+# CONFIGID=IntellijCustomJdk_Jdk17_Master_LinuxX64jcef
+# BUILDID=12345678
+#
+# expected return value
+# id="123".number="567"
+#
+CURL_RESPONSE=$(curl -sSL --header "Authorization: Bearer $TOKEN" "https://buildserver.labs.intellij.net/app/rest/builds/?locator=buildType:(id:$CONFIGID),status:success,count:1,finishDate:(build:$BUILDID,condition:before)")
+re='id=\"([0-9]+)\".+number=\"([0-9\.]+)\"'
+
+# ID: Previous successful build id
+ID=0
+if [[ $CURL_RESPONSE =~ $re ]]; then
+ ID=${BASH_REMATCH[1]}
+ echo "Previous build ID: $ID"
+ echo "Previous build number: ${BASH_REMATCH[2]}"
+else
+ msg="ERROR: cannot find previous build"
+ echo "$msg"
+ echo "$CURL_RESPONSE"
+ test_failed_msg "$test_name" "$msg"
+ test_finished_msg "$test_name"
+ exit 1
+fi
+
+#
+# Get artifacts from previous successful build
+#
+# expected return value
+# name="jbrsdk_jcef*.tar.gz size="123'
+#
+CURL_RESPONSE=$(curl -sSL --header "Authorization: Bearer $TOKEN" "https://buildserver.labs.intellij.net/app/rest/builds/$ID?fields=id,number,artifacts(file(name,size))")
+echo "Artifacts of the previous build:"
+echo "$CURL_RESPONSE"
+
+# Find binary size (in response) with reg exp
+re="name=\"(${BUNDLE_TYPE}[^\"]+${OS_ARCH_PATTERN}[^\"]+${FILE_EXTENSION})\" size=\"([0-9]+)\""
+
+if [[ $CURL_RESPONSE =~ $re ]]; then
+ prevFileName=${BASH_REMATCH[1]}
+ echo "Previous artifact name: $prevFileName"
+ prevFileSize=${BASH_REMATCH[2]}
+ echo "Previous artifact size: $prevFileSize"
+
+ ((allowedSize=prevFileSize+prevFileSize/20)) # use 5% threshold
+ echo "Allowed size: $allowedSize"
+ if [[ "$NEWFILESIZE" -gt "$allowedSize" ]]; then
+ msg="ERROR: new size is significantly greater than previous size (need to investigate)"
+ echo "$msg"
+ test_failed_msg "$test_name" "$msg"
+ test_finished_msg "$test_name"
+ exit 1
+ else
+ echo "PASSED"
+ test_finished_msg "$test_name"
+ fi
+else
+ msg="ERROR: cannot find string with size in xml response:"
+ echo "Regex: $re"
+ echo "$msg"
+ echo "$CURL_RESPONSE"
+ test_failed_msg "$test_name" "$msg"
+ test_finished_msg "$test_name"
+ exit 1
+fi
diff --git a/jb/project/tools/test/perfcmp.sh b/jb/project/tools/test/perfcmp.sh
new file mode 100755
index 000000000000..f8c0970c6b3e
--- /dev/null
+++ b/jb/project/tools/test/perfcmp.sh
@@ -0,0 +1,93 @@
+#!/bin/bash
+
+set -euo pipefail
+set -x
+
+usage ()
+{
+echo "Usage: perfcmp.sh [options] "
+echo "Options:"
+echo -e " -h, --help\tdisplay this help"
+echo -e " -tc\tprint teacmity statistic"
+echo -e "test_results_cur - the file with metrics values for the current measuring"
+echo -e "test_results_ref - the file with metrics values for the reference measuring"
+echo -e "results - results of comaprison"
+echo -e "test_prefix - specifys measuring type, makes sense for enabled -tc, by default no prefixes"
+echo -e "noHeaders - by default 1-st line contains headers"
+echo -e ""
+echo -e "test_results_* files content should be in csv format with header and tab separator:"
+echo -e "The 1-st column is the test name"
+echo -e "The 2-st column is the test value"
+echo -e ""
+echo -e "Example:"
+echo -e "Test Value"
+echo -e "Testname 51.54"
+}
+
+while [ -n "$1" ]
+do
+case "$1" in
+-h | --help) usage
+exit 1 ;;
+-tc) tc=1
+ shift
+ break ;;
+*) break;;
+esac
+done
+
+if [[ "$#" < "3" ]]; then
+ echo "Error: Invalid arguments"
+ usage
+ exit 1
+fi
+
+curFile=$1
+refFile=$2
+resFile=$3
+testNamePrefix=$4
+noHeaders=$5
+echo $curFile
+echo $refFile
+echo $resFile
+
+curValues=`cat "$curFile" | cut -f 2 | tr -d '\t'`
+if [ -z $noHeaders ]; then
+ curValuesHeader=`echo "$curValues" | head -n +1`_cur
+ header=`cat "$refFile" | head -n +1 | awk -F'\t' -v x=$curValuesHeader '{print " "$1"\t"$2"_ref\t"x"\tratio"}'`
+ testContent=`paste -d '\t' $refFile <(echo "$curValues") | tail -n +2`
+else
+ testContent=`paste -d '\t' $refFile <(echo "$curValues") | tail -n +1`
+fi
+
+testContent=`echo "$testContent" | tr "," "." | awk -F'\t' '{
+ if ($3>$2+$2*0.1) {
+ print "* "$1"\t"$2"\t"$3"\t"(($2>0)?$3/$2:"-")
+ } else {
+ print " "$1"\t"$2"\t"$3"\t"(($2>0)?$3/$2:"-")
+ }
+}'`
+if [ -z $noHeaders ]; then
+ echo "$header" > $resFile
+fi
+echo "$testContent" >> $resFile
+cat "$resFile" | tr '\t' ';' | column -t -s ';' | tee $resFile
+
+if [ -z $tc ]; then
+exit 0
+fi
+
+failed=0
+echo "$testContent" 2>&1 | (
+ while read -r s; do
+ testname=`echo "$s" | cut -f 1 | tr -d "[:space:]" | tr -d "*"`
+ duration=`echo "$s" | cut -f 3`
+ echo "$s" | cut -c1 | grep -c "*" && failed=1
+ echo \#\#teamcity[testStarted name=\'$testNamePrefix$testname\']
+ echo "===>$s"
+ echo \#\#teamcity[buildStatisticValue key=\'$testNamePrefix$testname\' value=\'$duration\']
+ [ $failed -eq 1 ] && echo \#\#teamcity[testFailed name=\'$testNamePrefix$testname\' message=\'$s\']
+ echo \#\#teamcity[testFinished name=\'$testNamePrefix$testname\' duration=\'$duration\']
+ failed=0
+ done
+)
\ No newline at end of file
diff --git a/jb/project/tools/windows/scripts/mkimages_aarch64.sh b/jb/project/tools/windows/scripts/mkimages_aarch64.sh
new file mode 100644
index 000000000000..0380b05995c0
--- /dev/null
+++ b/jb/project/tools/windows/scripts/mkimages_aarch64.sh
@@ -0,0 +1,152 @@
+#!/bin/bash
+
+set -euo pipefail
+set -x
+
+# The following parameters must be specified:
+# build_number - specifies the number of JetBrainsRuntime build
+# bundle_type - specifies bundle to be built;possible values:
+# or nomod - the release bundles without any additional modules (jcef)
+# jcef - the release bundles with jcef
+# fd - the fastdebug bundles which also include the jcef module
+#
+# This script makes test-image along with JDK images when bundle_type is set to "jcef".
+# If the character 't' is added at the end of bundle_type then it also makes test-image along with JDK images.
+#
+# Environment variables:
+# JDK_BUILD_NUMBER - specifies update release of OpenJDK build or the value of --with-version-build argument
+# to configure
+# By default JDK_BUILD_NUMBER is set zero
+# JCEF_PATH - specifies the path to the directory with JCEF binaries.
+# By default JCEF binaries should be located in ./jcef_win_aarch64
+
+if [ -z "$BUILD_JDK" ]; then
+ echo "BUILD_JDK environment variable must be specified and point to a JDK built from the current sources" \
+ " and is able to run on the build system. See OpenJDK documentation for --with-build-jdk for more info."
+ exit 1
+fi
+
+source jb/project/tools/common/scripts/common.sh
+
+WORK_DIR=$(pwd)
+JCEF_PATH=${JCEF_PATH:=$WORK_DIR/jcef_win_aarch64}
+NVDA_PATH=${NVDA_PATH:=$WORK_DIR/nvda_controllerClient}
+
+function do_configure {
+ sh ./configure \
+ --enable-option-checking=fatal \
+ --openjdk-target=aarch64-unknown-cygwin \
+ $WITH_DEBUG_LEVEL \
+ --with-vendor-name="$VENDOR_NAME" \
+ --with-vendor-version-string="$VENDOR_VERSION_STRING" \
+ --with-jvm-features=shenandoahgc \
+ --with-version-pre= \
+ --with-version-build=$JDK_BUILD_NUMBER \
+ --with-version-opt=b${build_number} \
+ --with-toolchain-version=$TOOLCHAIN_VERSION \
+ --with-boot-jdk=$BOOT_JDK \
+ --with-build-jdk=$BUILD_JDK \
+ --with-nvdacontrollerclient=$NVDA_PATH \
+ --disable-ccache \
+ --enable-cds=yes \
+ $STATIC_CONF_ARGS \
+ $REPRODUCIBLE_BUILD_OPTS \
+ || do_exit $?
+}
+
+function create_image_bundle {
+ __bundle_name=$1
+ __arch_name=$2
+ __modules_path=$3
+ __modules=$4
+
+ fastdebug_infix=''
+
+ [ "$bundle_type" == "fd" ] && [ "$__arch_name" == "$JBRSDK_BUNDLE" ] && __bundle_name=$__arch_name && fastdebug_infix="fastdebug-"
+ __root_dir=${__bundle_name}-${JBSDK_VERSION}-windows-aarch64-${fastdebug_infix}b${build_number}
+
+ echo Running jlink ...
+ ${BUILD_JDK}/bin/jlink \
+ --module-path $__modules_path --no-man-pages --compress=2 \
+ --add-modules $__modules --output $__root_dir || do_exit $?
+
+ grep -v "^JAVA_VERSION" "$JSDK"/release | grep -v "^MODULES" >> $__root_dir/release
+ if [ "$__arch_name" == "$JBRSDK_BUNDLE" ]; then
+ sed 's/JBR/JBRSDK/g' $__root_dir/release > release
+ mv release $__root_dir/release
+ cp $IMAGES_DIR/jdk/lib/src.zip $__root_dir/lib
+ cp $IMAGES_DIR/jdk/bin/server/*.jsa $__root_dir/bin/server
+ for dir in $(ls -d $IMAGES_DIR/jdk/*); do
+ rsync -amv --include="*/" --include="*.pdb" --exclude="*" $dir $__root_dir
+ done
+ copy_jmods "$__modules" "$__modules_path" "$__root_dir"/jmods
+ fi
+}
+
+WITH_DEBUG_LEVEL="--with-debug-level=release"
+RELEASE_NAME=windows-aarch64-server-release
+
+case "$bundle_type" in
+ "jcef")
+ do_reset_changes=0
+ do_maketest=1
+ ;;
+ "nomod" | "")
+ bundle_type=""
+ ;;
+ "fd")
+ do_reset_changes=0
+ WITH_DEBUG_LEVEL="--with-debug-level=fastdebug"
+ RELEASE_NAME=windows-aarch64-server-fastdebug
+ ;;
+esac
+
+if [ -z "${INC_BUILD:-}" ]; then
+ do_configure || do_exit $?
+ if [ $do_maketest -eq 1 ]; then
+ make LOG=info CONF=$RELEASE_NAME clean images test-image jbr-api JBR_API_JBR_VERSION=TEST || do_exit $?
+ else
+ make LOG=info CONF=$RELEASE_NAME clean images || do_exit $?
+ fi
+else
+ if [ $do_maketest -eq 1 ]; then
+ make LOG=info CONF=$RELEASE_NAME images test-image jbr-api JBR_API_JBR_VERSION=TEST || do_exit $?
+ else
+ make LOG=info CONF=$RELEASE_NAME images || do_exit $?
+ fi
+fi
+
+IMAGES_DIR=build/$RELEASE_NAME/images
+JSDK=$IMAGES_DIR/jdk
+JSDK_MODS_DIR=$IMAGES_DIR/jmods
+JBRSDK_BUNDLE=jbrsdk
+
+where cygpath
+if [ $? -eq 0 ]; then
+ JCEF_PATH="$(cygpath -w $JCEF_PATH | sed 's/\\/\//g')"
+fi
+
+if [ "$bundle_type" == "jcef" ] || [ "$bundle_type" == "fd" ]; then
+ git apply -p0 < jb/project/tools/patches/add_jcef_module_aarch64.patch || do_exit $?
+ update_jsdk_mods "$BUILD_JDK" "$JCEF_PATH"/jmods "$JSDK"/jmods "$JSDK_MODS_DIR" || do_exit $?
+ cp $JCEF_PATH/jmods/* $JSDK_MODS_DIR # $JSDK/jmods is not unchanged
+
+ jbr_name_postfix="_${bundle_type}"
+ cat $JCEF_PATH/jcef.version >> $JSDK/release
+else
+ jbr_name_postfix=""
+fi
+
+# create runtime image bundle
+modules=$(xargs < jb/project/tools/common/modules.list | sed s/" "//g) || do_exit $?
+modules+=",jdk.crypto.mscapi"
+create_image_bundle "jbr${jbr_name_postfix}" "jbr" $JSDK_MODS_DIR "$modules" || do_exit $?
+
+# create sdk image bundle
+modules=$(cat ${JSDK}/release | grep MODULES | sed s/MODULES=//g | sed s/' '/','/g | sed s/\"//g | sed s/\\r//g | sed s/\\n//g) || do_exit $?
+if [ "$bundle_type" == "jcef" ] || [ "$bundle_type" == "fd" ] || [ "$bundle_type" == "$JBRSDK_BUNDLE" ]; then
+ modules=${modules},$(get_mods_list "$JCEF_PATH"/jmods)
+fi
+create_image_bundle "$JBRSDK_BUNDLE${jbr_name_postfix}" "$JBRSDK_BUNDLE" "$JSDK_MODS_DIR" "$modules" || do_exit $?
+
+do_exit 0
diff --git a/jb/project/tools/windows/scripts/mkimages_x64.sh b/jb/project/tools/windows/scripts/mkimages_x64.sh
new file mode 100755
index 000000000000..f8f54a92a37b
--- /dev/null
+++ b/jb/project/tools/windows/scripts/mkimages_x64.sh
@@ -0,0 +1,143 @@
+#!/bin/bash
+
+set -euo pipefail
+set -x
+
+# The following parameters must be specified:
+# build_number - specifies the number of JetBrainsRuntime build
+# bundle_type - specifies bundle to be built;possible values:
+# or nomod - the release bundles without any additional modules (jcef)
+# jcef - the release bundles with jcef
+# fd - the fastdebug bundles which also include the jcef module
+#
+# This script makes test-image along with JDK images when bundle_type is set to "jcef".
+# If the character 't' is added at the end of bundle_type then it also makes test-image along with JDK images.
+#
+# Environment variables:
+# JDK_BUILD_NUMBER - specifies update release of OpenJDK build or the value of --with-version-build argument
+# to configure
+# By default JDK_BUILD_NUMBER is set zero
+# JCEF_PATH - specifies the path to the directory with JCEF binaries.
+# By default JCEF binaries should be located in ./jcef_win_x64
+
+source jb/project/tools/common/scripts/common.sh
+
+WORK_DIR=$(pwd)
+JCEF_PATH=${JCEF_PATH:=$WORK_DIR/jcef_win_x64}
+NVDA_PATH=${NVDA_PATH:=$WORK_DIR/nvda_controllerClient}
+
+function do_configure {
+ sh ./configure \
+ $WITH_DEBUG_LEVEL \
+ --with-vendor-name="$VENDOR_NAME" \
+ --with-vendor-version-string="$VENDOR_VERSION_STRING" \
+ --with-jvm-features=shenandoahgc \
+ --with-version-pre= \
+ --with-version-build=$JDK_BUILD_NUMBER \
+ --with-version-opt=b${build_number} \
+ --with-toolchain-version=$TOOLCHAIN_VERSION \
+ --with-boot-jdk=$BOOT_JDK \
+ --with-nvdacontrollerclient=$NVDA_PATH \
+ --disable-ccache \
+ --enable-cds=yes \
+ $STATIC_CONF_ARGS \
+ $REPRODUCIBLE_BUILD_OPTS \
+ || do_exit $?
+}
+
+function create_image_bundle {
+ __bundle_name=$1
+ __arch_name=$2
+ __modules_path=$3
+ __modules=$4
+
+ fastdebug_infix=''
+
+ [ "$bundle_type" == "fd" ] && [ "$__arch_name" == "$JBRSDK_BUNDLE" ] && __bundle_name=$__arch_name && fastdebug_infix="fastdebug-"
+ __root_dir=${__bundle_name}-${JBSDK_VERSION}-windows-x64-${fastdebug_infix}b${build_number}
+
+ echo Running jlink ...
+ ${JSDK}/bin/jlink \
+ --module-path $__modules_path --no-man-pages --compress=2 \
+ --generate-cds-archive --add-modules $__modules --output $__root_dir || do_exit $?
+
+ grep -v "^JAVA_VERSION" "$JSDK"/release | grep -v "^MODULES" >> $__root_dir/release
+ if [ "$__arch_name" == "$JBRSDK_BUNDLE" ]; then
+ sed 's/JBR/JBRSDK/g' $__root_dir/release > release
+ mv release $__root_dir/release
+ cp $IMAGES_DIR/jdk/lib/src.zip $__root_dir/lib
+ cp $IMAGES_DIR/jdk/bin/server/*.jsa $__root_dir/bin/server
+ for dir in $(ls -d $IMAGES_DIR/jdk/*); do
+ rsync -amv --include="*/" --include="*.pdb" --exclude="*" $dir $__root_dir
+ done
+ copy_jmods "$__modules" "$__modules_path" "$__root_dir"/jmods
+ fi
+}
+
+WITH_DEBUG_LEVEL="--with-debug-level=release"
+RELEASE_NAME=windows-x86_64-server-release
+
+case "$bundle_type" in
+ "jcef")
+ do_reset_changes=0
+ do_maketest=1
+ ;;
+ "nomod" | "")
+ bundle_type=""
+ ;;
+ "fd")
+ do_reset_changes=0
+ WITH_DEBUG_LEVEL="--with-debug-level=fastdebug"
+ RELEASE_NAME=windows-x86_64-server-fastdebug
+ ;;
+esac
+
+if [ -z "${INC_BUILD:-}" ]; then
+ do_configure || do_exit $?
+ if [ $do_maketest -eq 1 ]; then
+ make LOG=info CONF=$RELEASE_NAME clean images test-image jbr-api JBR_API_JBR_VERSION=TEST || do_exit $?
+ else
+ make LOG=info CONF=$RELEASE_NAME clean images || do_exit $?
+ fi
+else
+ if [ $do_maketest -eq 1 ]; then
+ make LOG=info CONF=$RELEASE_NAME images test-image jbr-api JBR_API_JBR_VERSION=TEST || do_exit $?
+ else
+ make LOG=info CONF=$RELEASE_NAME images || do_exit $?
+ fi
+fi
+
+IMAGES_DIR=build/$RELEASE_NAME/images
+JSDK=$IMAGES_DIR/jdk
+JSDK_MODS_DIR=$IMAGES_DIR/jmods
+JBRSDK_BUNDLE=jbrsdk
+
+where cygpath
+if [ $? -eq 0 ]; then
+ JCEF_PATH="$(cygpath -w $JCEF_PATH | sed 's/\\/\//g')"
+fi
+
+if [ "$bundle_type" == "jcef" ] || [ "$bundle_type" == "fd" ]; then
+ git apply -p0 < jb/project/tools/patches/add_jcef_module.patch || do_exit $?
+ update_jsdk_mods "$JSDK" "$JCEF_PATH"/jmods "$JSDK"/jmods "$JSDK_MODS_DIR" || do_exit $?
+ cp $JCEF_PATH/jmods/* ${JSDK_MODS_DIR} # $JSDK/jmods is not unchanged
+
+ jbr_name_postfix="_${bundle_type}"
+ cat $JCEF_PATH/jcef.version >> $JSDK/release
+else
+ jbr_name_postfix=""
+fi
+
+# create runtime image bundle
+modules=$(xargs < jb/project/tools/common/modules.list | sed s/" "//g) || do_exit $?
+modules+=",jdk.crypto.mscapi"
+create_image_bundle "jbr${jbr_name_postfix}" "jbr" $JSDK_MODS_DIR "$modules" || do_exit $?
+
+# create sdk image bundle
+modules=$(cat ${JSDK}/release | grep MODULES | sed s/MODULES=//g | sed s/' '/','/g | sed s/\"//g | sed s/\\r//g | sed s/\\n//g)
+if [ "$bundle_type" == "jcef" ] || [ "$bundle_type" == "fd" ] || [ "$bundle_type" == "$JBRSDK_BUNDLE" ]; then
+ modules=${modules},$(get_mods_list "$JCEF_PATH"/jmods)
+fi
+create_image_bundle "$JBRSDK_BUNDLE${jbr_name_postfix}" "$JBRSDK_BUNDLE" "$JSDK_MODS_DIR" "$modules" || do_exit $?
+
+do_exit 0
diff --git a/jb/project/tools/windows/scripts/mkimages_x86.sh b/jb/project/tools/windows/scripts/mkimages_x86.sh
new file mode 100755
index 000000000000..a88e9e8d6853
--- /dev/null
+++ b/jb/project/tools/windows/scripts/mkimages_x86.sh
@@ -0,0 +1,132 @@
+#!/bin/bash
+
+set -euo pipefail
+set -x
+
+# The following parameters must be specified:
+# build_number - specifies the number of JetBrainsRuntime build
+# bundle_type - specifies bundle to be built;possible values:
+# or nomod - the release bundles without any additional modules (jcef)
+# jcef - the release bundles with jcef
+# fd - the fastdebug bundles which also include the jcef module
+#
+# $ ./java --version
+# openjdk 11.0.6 2020-01-14
+# OpenJDK Runtime Environment (build 11.0.6+${JDK_BUILD_NUMBER}-b${build_number})
+# OpenJDK 64-Bit Server VM (build 11.0.6+${JDK_BUILD_NUMBER}-b${build_number}, mixed mode)
+#
+
+source jb/project/tools/common/scripts/common.sh
+
+WORK_DIR=$(pwd)
+NVDA_PATH=${NVDA_PATH:=$WORK_DIR/nvda_controllerClient}
+
+
+function do_configure {
+ sh ./configure \
+ $WITH_DEBUG_LEVEL \
+ --with-vendor-name="$VENDOR_NAME" \
+ --with-vendor-version-string="$VENDOR_VERSION_STRING" \
+ --with-jvm-features=shenandoahgc \
+ --with-version-pre= \
+ --with-version-build=$JDK_BUILD_NUMBER \
+ --with-version-opt=b${build_number} \
+ --with-toolchain-version=$TOOLCHAIN_VERSION \
+ --with-boot-jdk=$BOOT_JDK \
+ --with-nvdacontrollerclient=$NVDA_PATH \
+ --disable-ccache \
+ --enable-cds=yes \
+ $STATIC_CONF_ARGS \
+ $REPRODUCIBLE_BUILD_OPTS \
+ || do_exit $?
+}
+
+function create_image_bundle {
+ __bundle_name=$1
+ __arch_name=$2
+ __modules_path=$3
+ __modules=$4
+
+ fastdebug_infix=''
+
+ [ "$bundle_type" == "fd" ] && [ "$__arch_name" == "$JBRSDK_BUNDLE" ] && __bundle_name=$__arch_name && fastdebug_infix="fastdebug-"
+ __root_dir=${__bundle_name}-${JBSDK_VERSION}-windows-x86-${fastdebug_infix}b${build_number}
+
+ echo Running jlink ...
+ ${JSDK}/bin/jlink \
+ --module-path $__modules_path --no-man-pages --compress=2 \
+ --generate-cds-archive --add-modules $__modules --output $__root_dir || do_exit $?
+
+ grep -v "^JAVA_VERSION" "$JSDK"/release | grep -v "^MODULES" >> $__root_dir/release
+ if [ "$__arch_name" == "$JBRSDK_BUNDLE" ]; then
+ sed 's/JBR/JBRSDK/g' $__root_dir/release > release
+ mv release $__root_dir/release
+ cp $IMAGES_DIR/jdk/lib/src.zip $__root_dir/lib
+ cp $IMAGES_DIR/jdk/bin/server/*.jsa $__root_dir/bin/server
+ for dir in $(ls -d $IMAGES_DIR/jdk/*); do
+ rsync -amv --include="*/" --include="*.pdb" --exclude="*" $dir $__root_dir
+ done
+ copy_jmods "$__modules" "$__modules_path" "$__root_dir"/jmods
+ fi
+}
+
+WITH_DEBUG_LEVEL="--with-debug-level=release"
+RELEASE_NAME=windows-x86_64-server-release
+
+case "$bundle_type" in
+ "jcef")
+ echo "not implemented" && do_exit 1
+ ;;
+ "nomod" | "")
+ bundle_type=""
+ ;;
+ "fd")
+ do_reset_changes=0
+ WITH_DEBUG_LEVEL="--with-debug-level=fastdebug"
+ RELEASE_NAME=windows-x86_64-server-fastdebug
+ ;;
+esac
+
+if [ -z "${INC_BUILD:-}" ]; then
+ do_configure || do_exit $?
+ if [ $do_maketest -eq 1 ]; then
+ make LOG=info CONF=$RELEASE_NAME clean images test-image jbr-api JBR_API_JBR_VERSION=TEST || do_exit $?
+ else
+ make LOG=info CONF=$RELEASE_NAME clean images || do_exit $?
+ fi
+else
+ if [ $do_maketest -eq 1 ]; then
+ make LOG=info CONF=$RELEASE_NAME images test-image jbr-api JBR_API_JBR_VERSION=TEST || do_exit $?
+ else
+ make LOG=info CONF=$RELEASE_NAME images || do_exit $?
+ fi
+fi
+
+IMAGES_DIR=build/$RELEASE_NAME/images
+JSDK=$IMAGES_DIR/jdk
+JSDK_MODS_DIR=$IMAGES_DIR/jmods
+JBRSDK_BUNDLE=jbrsdk
+
+if [ "$bundle_type" == "jcef" ] || [ "$bundle_type" == "fd" ]; then
+ git apply -p0 < jb/project/tools/patches/add_jcef_module.patch || do_exit $?
+ update_jsdk_mods "$JSDK" "$JCEF_PATH"/jmods "$JSDK"/jmods "$JSDK_MODS_DIR" || do_exit $?
+ cp $JCEF_PATH/jmods/* ${JSDK_MODS_DIR} # $JSDK/jmods is not unchanged
+
+ jbr_name_postfix="_${bundle_type}"
+else
+ jbr_name_postfix=""
+fi
+
+# create runtime image bundle
+modules=$(grep -v "jdk.internal.vm" jb/project/tools/common/modules.list | xargs | sed s/" "//g) || do_exit $?
+modules+=",jdk.crypto.mscapi"
+create_image_bundle "jbr${jbr_name_postfix}" "jbr" $JSDK_MODS_DIR "$modules" || do_exit $?
+
+# create sdk image bundle
+modules=$(cat ${JSDK}/release | grep MODULES | sed s/MODULES=//g | sed s/' '/','/g | sed s/\"//g | sed s/\\r//g | sed s/\\n//g)
+if [ "$bundle_type" == "jcef" ] || [ "$bundle_type" == "fd" ] || [ "$bundle_type" == "$JBRSDK_BUNDLE" ]; then
+ modules=${modules},$(get_mods_list "$JCEF_PATH"/jmods)
+fi
+create_image_bundle "$JBRSDK_BUNDLE${jbr_name_postfix}" "$JBRSDK_BUNDLE" "$JSDK_MODS_DIR" "$modules" || do_exit $?
+
+do_exit 0
diff --git a/jb/project/tools/windows/scripts/pack_aarch64.sh b/jb/project/tools/windows/scripts/pack_aarch64.sh
new file mode 100644
index 000000000000..4c7d86bf753e
--- /dev/null
+++ b/jb/project/tools/windows/scripts/pack_aarch64.sh
@@ -0,0 +1,57 @@
+#!/bin/bash
+
+set -euo pipefail
+set -x
+
+# The following parameters must be specified:
+# build_number - specifies the number of JetBrainsRuntime build
+# bundle_type - specifies bundle to be built;possible values:
+# or nomod - the release bundles without any additional modules (jcef)
+# jcef - the release bundles with jcef
+# fd - the fastdebug bundles which also include the jcef module
+#
+# This script packs test-image along with JDK images when bundle_type is set to "jcef".
+# If the character 't' is added at the end of bundle_type then it also makes test-image along with JDK images.
+#
+
+source jb/project/tools/common/scripts/common.sh
+
+[ "$bundle_type" == "jcef" ] && do_maketest=1
+
+function pack_jbr {
+ __bundle_name=$1
+ __arch_name=$2
+
+ fastdebug_infix=''
+
+ [ "$bundle_type" == "fd" ] && [ "$__arch_name" == "$JBRSDK_BUNDLE" ] && __bundle_name=$__arch_name && fastdebug_infix="fastdebug-"
+ JBR=${__bundle_name}-${JBSDK_VERSION}-windows-aarch64-${fastdebug_infix}b${build_number}
+ __root_dir=${__bundle_name}-${JBSDK_VERSION}-windows-aarch64-${fastdebug_infix}b${build_number}
+
+ echo Creating $JBR.tar.gz ...
+
+ /usr/bin/tar -czf $JBR.tar.gz -C $BASE_DIR $__root_dir || do_exit $?
+}
+
+[ "$bundle_type" == "nomod" ] && bundle_type=""
+
+JBRSDK_BUNDLE=jbrsdk
+RELEASE_NAME=windows-aarch64-server-release
+IMAGES_DIR=build/$RELEASE_NAME/images
+BASE_DIR=.
+
+if [ "$bundle_type" == "jcef" ] || [ "$bundle_type" == "dcevm" ] || [ "$bundle_type" == "fd" ]; then
+ jbr_name_postfix="_${bundle_type}"
+else
+ jbr_name_postfix=""
+fi
+
+pack_jbr jbr${jbr_name_postfix} jbr
+pack_jbr jbrsdk${jbr_name_postfix} jbrsdk
+
+if [ $do_maketest -eq 1 ]; then
+ JBRSDK_TEST=$JBRSDK_BUNDLE-$JBSDK_VERSION-windows-test-aarch64-b$build_number
+ cp "build/${RELEASE_NAME}/jbr-api/jbr-api.jar" "${IMAGES_DIR}/test" || do_exit $?
+ echo Creating $JBRSDK_TEST.tar.gz ...
+ /usr/bin/tar -czf $JBRSDK_TEST.tar.gz -C $IMAGES_DIR --exclude='test/jdk/demos' test || do_exit $?
+fi
\ No newline at end of file
diff --git a/jb/project/tools/windows/scripts/pack_x64.sh b/jb/project/tools/windows/scripts/pack_x64.sh
new file mode 100755
index 000000000000..1a98a2ead7a8
--- /dev/null
+++ b/jb/project/tools/windows/scripts/pack_x64.sh
@@ -0,0 +1,57 @@
+#!/bin/bash
+
+set -euo pipefail
+set -x
+
+# The following parameters must be specified:
+# build_number - specifies the number of JetBrainsRuntime build
+# bundle_type - specifies bundle to be built;possible values:
+# or nomod - the release bundles without any additional modules (jcef)
+# jcef - the release bundles with jcef
+# fd - the fastdebug bundles which also include the jcef module
+#
+# This script packs test-image along with JDK images when bundle_type is set to "jcef".
+# If the character 't' is added at the end of bundle_type then it also makes test-image along with JDK images.
+#
+
+source jb/project/tools/common/scripts/common.sh
+
+[ "$bundle_type" == "jcef" ] && do_maketest=1
+
+function pack_jbr {
+ __bundle_name=$1
+ __arch_name=$2
+
+ fastdebug_infix=''
+
+ [ "$bundle_type" == "fd" ] && [ "$__arch_name" == "$JBRSDK_BUNDLE" ] && __bundle_name=$__arch_name && fastdebug_infix="fastdebug-"
+ JBR=${__bundle_name}-${JBSDK_VERSION}-windows-x64-${fastdebug_infix}b${build_number}
+ __root_dir=${__bundle_name}-${JBSDK_VERSION}-windows-x64-${fastdebug_infix}b${build_number}
+
+ echo Creating $JBR.tar.gz ...
+ chmod -R ug+rwx,o+rx ${BASE_DIR}/$__root_dir
+ /usr/bin/tar -czf $JBR.tar.gz -C $BASE_DIR $__root_dir || do_exit $?
+}
+
+[ "$bundle_type" == "nomod" ] && bundle_type=""
+
+JBRSDK_BUNDLE=jbrsdk
+RELEASE_NAME=windows-x86_64-server-release
+IMAGES_DIR=build/$RELEASE_NAME/images
+BASE_DIR=.
+
+if [ "$bundle_type" == "jcef" ] || [ "$bundle_type" == "dcevm" ] || [ "$bundle_type" == "fd" ]; then
+ jbr_name_postfix="_${bundle_type}"
+else
+ jbr_name_postfix=""
+fi
+
+pack_jbr jbr${jbr_name_postfix} jbr
+pack_jbr jbrsdk${jbr_name_postfix} jbrsdk
+
+if [ $do_maketest -eq 1 ]; then
+ JBRSDK_TEST=$JBRSDK_BUNDLE-$JBSDK_VERSION-windows-test-x64-b$build_number
+ cp "build/${RELEASE_NAME}/jbr-api/jbr-api.jar" "${IMAGES_DIR}/test" || do_exit $?
+ echo Creating $JBRSDK_TEST.tar.gz ...
+ /usr/bin/tar -czf $JBRSDK_TEST.tar.gz -C $IMAGES_DIR --exclude='test/jdk/demos' test || do_exit $?
+fi
\ No newline at end of file
diff --git a/jb/project/tools/windows/scripts/pack_x86.sh b/jb/project/tools/windows/scripts/pack_x86.sh
new file mode 100755
index 000000000000..20db6e2cbebe
--- /dev/null
+++ b/jb/project/tools/windows/scripts/pack_x86.sh
@@ -0,0 +1,53 @@
+#!/bin/bash
+
+set -euo pipefail
+set -x
+
+# The following parameters must be specified:
+# build_number - specifies the number of JetBrainsRuntime build
+# bundle_type - specifies bundle to be built;possible values:
+# or nomod - the release bundles without any additional modules (jcef)
+# fd - the fastdebug bundles which also include the jcef module
+#
+
+source jb/project/tools/common/scripts/common.sh
+
+[ "$bundle_type" == "jcef" ] && echo "not implemented" && do_exit 1
+
+function pack_jbr {
+ __bundle_name=$1
+ __arch_name=$2
+
+ fastdebug_infix=''
+
+ [ "$bundle_type" == "fd" ] && [ "$__arch_name" == "$JBRSDK_BUNDLE" ] && __bundle_name=$__arch_name && fastdebug_infix="fastdebug-"
+ JBR=${__bundle_name}-${JBSDK_VERSION}-windows-x86-${fastdebug_infix}b${build_number}
+ __root_dir=${__bundle_name}-${JBSDK_VERSION}-windows-x86-${fastdebug_infix}b${build_number}
+
+ echo Creating $JBR.tar.gz ...
+ chmod -R ug+rwx,o+rx ${BASE_DIR}/$__root_dir
+ /usr/bin/tar -czf $JBR.tar.gz -C $BASE_DIR $__root_dir || do_exit $?
+}
+
+[ "$bundle_type" == "nomod" ] && bundle_type=""
+
+JBRSDK_BUNDLE=jbrsdk
+RELEASE_NAME=windows-x86_64-server-release
+IMAGES_DIR=build/$RELEASE_NAME/images
+BASE_DIR=.
+
+if [ "$bundle_type" == "jcef" ] || [ "$bundle_type" == "dcevm" ] || [ "$bundle_type" == "fd" ]; then
+ jbr_name_postfix="_${bundle_type}"
+else
+ jbr_name_postfix=""
+fi
+
+pack_jbr jbr${jbr_name_postfix} jbr
+pack_jbr jbrsdk${jbr_name_postfix} jbrsdk
+
+if [ $do_maketest -eq 1 ]; then
+ JBRSDK_TEST=$JBRSDK_BUNDLE-$JBSDK_VERSION-windows-test-x86-b$build_number
+ cp "build/${RELEASE_NAME}/jbr-api/jbr-api.jar" "${IMAGES_DIR}/test" || do_exit $?
+ echo Creating $JBRSDK_TEST.tar.gz ...
+ /usr/bin/tar -czf $JBRSDK_TEST.tar.gz -C $BASE_DIR --exclude='test/jdk/demos' test || do_exit $?
+fi
\ No newline at end of file
diff --git a/make/JBRApi.gmk b/make/JBRApi.gmk
new file mode 100644
index 000000000000..056b7641b831
--- /dev/null
+++ b/make/JBRApi.gmk
@@ -0,0 +1,93 @@
+#
+# Copyright 2000-2023 JetBrains s.r.o.
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# This code is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License version 2 only, as
+# published by the Free Software Foundation. Oracle designates this
+# particular file as subject to the "Classpath" exception as provided
+# by Oracle in the LICENSE file that accompanied this code.
+#
+# This code is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# version 2 for more details (a copy is included in the LICENSE file that
+# accompanied this code).
+#
+# You should have received a copy of the GNU General Public License version
+# 2 along with this work; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+# or visit www.oracle.com if you need additional information or have any
+# questions.
+#
+
+include $(SPEC)
+include MakeBase.gmk
+include JavaCompilation.gmk
+
+JBR_API_ROOT_DIR := $(TOPDIR)/src/jetbrains.api
+JBR_API_TOOLS_DIR := $(JBR_API_ROOT_DIR)/tools
+JBR_API_SRC_DIR := $(JBR_API_ROOT_DIR)/src
+JBR_API_OUTPUT_DIR := $(OUTPUTDIR)/jbr-api
+JBR_API_GENSRC_DIR := $(JBR_API_OUTPUT_DIR)/gensrc
+JBR_API_BIN_DIR := $(JBR_API_OUTPUT_DIR)/bin
+JBR_API_VERSION_PROPERTIES := $(JBR_API_ROOT_DIR)/version.properties
+JBR_API_VERSION_GENSRC := $(JBR_API_OUTPUT_DIR)/jbr-api.version
+JBR_API_GENSRC_BATCH := $(JBR_API_VERSION_GENSRC)
+
+JBR_API_SRC_FILES := $(call FindFiles, $(JBR_API_SRC_DIR))
+JBR_API_GENSRC_FILES := $(foreach f, $(call FindFiles, $(JBR_API_SRC_DIR)), \
+ $(JBR_API_GENSRC_DIR)/$(call RelativePath, $f, $(JBR_API_SRC_DIR)))
+
+ifeq ($(JBR_API_JBR_VERSION),)
+ JBR_API_JBR_VERSION := DEVELOPMENT
+ JBR_API_FAIL_ON_HASH_MISMATCH := false
+else
+ .PHONY: $(JBR_API_VERSION_PROPERTIES)
+ JBR_API_FAIL_ON_HASH_MISMATCH := true
+endif
+
+ARCHIVE_BUILD_JBR_API_BIN := $(JBR_API_BIN_DIR)
+$(eval $(call SetupJavaCompilation, BUILD_JBR_API, \
+ SMALL_JAVA := true, \
+ COMPILER := bootjdk, \
+ SRC := $(JBR_API_GENSRC_DIR), \
+ EXTRA_FILES := $(JBR_API_GENSRC_FILES), \
+ BIN := $(JBR_API_BIN_DIR), \
+ JAR := $(JBR_API_OUTPUT_DIR)/jbr-api.jar, \
+))
+
+$(eval $(call SetupJarArchive, BUILD_JBR_API_SOURCES_JAR, \
+ DEPENDENCIES := $(JBR_API_GENSRC_FILES), \
+ SRCS := $(JBR_API_GENSRC_DIR), \
+ JAR := $(JBR_API_OUTPUT_DIR)/jbr-api-sources.jar, \
+ SUFFIXES := .java, \
+ BIN := $(JBR_API_BIN_DIR), \
+))
+
+# Grouped targets may not be supported, so hack dependencies: sources -> version file -> generated sources
+$(JBR_API_VERSION_GENSRC): $(JBR_API_SRC_FILES) $(JBR_API_VERSION_PROPERTIES) $(JBR_API_TOOLS_DIR)/Gensrc.java
+ $(ECHO) Generating sources for JBR API
+ $(JAVA_CMD) $(JAVA_FLAGS_SMALL) "$(JBR_API_TOOLS_DIR)/Gensrc.java" \
+ "$(TOPDIR)/src" "$(JBR_API_OUTPUT_DIR)" "$(JBR_API_JBR_VERSION)"
+$(JBR_API_GENSRC_FILES): $(JBR_API_VERSION_GENSRC)
+ $(TOUCH) $@
+
+jbr-api-check-version: $(JBR_API_GENSRC_FILES) $(JBR_API_VERSION_PROPERTIES)
+ $(JAVA_CMD) $(JAVA_FLAGS_SMALL) "$(JBR_API_TOOLS_DIR)/CheckVersion.java" \
+ "$(JBR_API_ROOT_DIR)" "$(JBR_API_GENSRC_DIR)" "$(JBR_API_FAIL_ON_HASH_MISMATCH)"
+
+jbr-api: $(BUILD_JBR_API) $(BUILD_JBR_API_SOURCES_JAR) jbr-api-check-version
+
+.PHONY: jbr-api jbr-api-check-version
+
+ifneq ($(JBR_API_CONF_FILE),)
+ $(JBR_API_CONF_FILE): $(JBR_API_GENSRC_FILES)
+ $(ECHO) "VERSION=`$(CAT) $(JBR_API_VERSION_GENSRC)`" > $(JBR_API_CONF_FILE)
+ $(ECHO) "JAR=$(JBR_API_OUTPUT_DIR)/jbr-api.jar" >> $(JBR_API_CONF_FILE)
+ $(ECHO) "SOURCES_JAR=$(JBR_API_OUTPUT_DIR)/jbr-api-sources.jar" >> $(JBR_API_CONF_FILE)
+ jbr-api: $(JBR_API_CONF_FILE)
+ .PHONY: $(JBR_API_CONF_FILE)
+endif
diff --git a/make/Main.gmk b/make/Main.gmk
index b0b7565c138e..c0e5653fd626 100644
--- a/make/Main.gmk
+++ b/make/Main.gmk
@@ -1468,6 +1468,14 @@ create-main-targets-include:
@$(ECHO) ALL_MAIN_TARGETS := $(sort $(ALL_TARGETS)) > \
$(MAKESUPPORT_OUTPUTDIR)/main-targets.gmk
+################################################################################
+# JBR API
+
+$(eval $(call SetupTarget, jbr-api, \
+ MAKEFILE := JBRApi, \
+ TARGET := jbr-api \
+))
+
################################################################################
# Hook to include the corresponding custom file, if present.
$(eval $(call IncludeCustomExtension, Main-post.gmk))
diff --git a/make/autoconf/basic_windows.m4 b/make/autoconf/basic_windows.m4
index fb6fc526bfa2..c7fb88976d60 100644
--- a/make/autoconf/basic_windows.m4
+++ b/make/autoconf/basic_windows.m4
@@ -89,8 +89,8 @@ AC_DEFUN([BASIC_SETUP_PATHS_WINDOWS],
WINENV_TEMP_DIR=$($PATHTOOL -u $($CMD /q /c echo %TEMP% 2> /dev/null) | $TR -d '\r\n')
AC_MSG_RESULT([$WINENV_TEMP_DIR])
- if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.wsl2"; then
- # Don't trust the current directory for WSL2, but change to an OK temp dir
+ if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.wsl1" || test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.wsl2"; then
+ # Don't trust the current directory for WSL, but change to an OK temp dir
cd "$WINENV_TEMP_DIR"
# Bring along confdefs.h or autoconf gets all confused
cp "$CONFIGURE_START_DIR/confdefs.h" "$WINENV_TEMP_DIR"
@@ -228,7 +228,7 @@ AC_DEFUN([BASIC_WINDOWS_FINALIZE_FIXPATH],
# Platform-specific finalization
AC_DEFUN([BASIC_WINDOWS_FINALIZE],
[
- if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.wsl2"; then
+ if test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.wsl1" || test "x$OPENJDK_BUILD_OS_ENV" = "xwindows.wsl2"; then
# Change back from temp dir
cd $CONFIGURE_START_DIR
fi
diff --git a/make/autoconf/jdk-options.m4 b/make/autoconf/jdk-options.m4
index f56081223a60..674a8a2a38bd 100644
--- a/make/autoconf/jdk-options.m4
+++ b/make/autoconf/jdk-options.m4
@@ -234,6 +234,31 @@ AC_DEFUN_ONCE([JDKOPT_SETUP_JDK_OPTIONS],
fi
AC_SUBST(HOTSPOT_OVERRIDE_LIBPATH)
+ # Should we build the client for the JAWS screen reader?
+ if test "x$OPENJDK_TARGET_OS" = xwindows; then
+ AC_MSG_CHECKING([if JAWS client support is enabled])
+
+ A11Y_JAWS_ANNOUNCING_ENABLED=true
+ AC_ARG_ENABLE(
+ [jaws-client],
+ [AS_HELP_STRING([--disable-jaws-client], [Set to disable to exclude the client for the JAWS screen reader from the build])],
+ [
+ if test "x$ENABLE_HEADLESS_ONLY" = xtrue; then
+ AC_MSG_WARN([--[enable|disable]-jaws-client[=*] flags are ignored for headless builds])
+ elif test "x$enableval" != xyes; then
+ A11Y_JAWS_ANNOUNCING_ENABLED=false
+ fi
+ ]
+ )
+ if test "x$ENABLE_HEADLESS_ONLY" = xtrue; then
+ A11Y_JAWS_ANNOUNCING_ENABLED=false
+ fi
+
+ AC_MSG_RESULT([$A11Y_JAWS_ANNOUNCING_ENABLED])
+ else
+ A11Y_JAWS_ANNOUNCING_ENABLED=false
+ fi
+ AC_SUBST(A11Y_JAWS_ANNOUNCING_ENABLED)
])
###############################################################################
diff --git a/make/autoconf/lib-nvdacontrollerclient.m4 b/make/autoconf/lib-nvdacontrollerclient.m4
new file mode 100644
index 000000000000..08bf245596ac
--- /dev/null
+++ b/make/autoconf/lib-nvdacontrollerclient.m4
@@ -0,0 +1,121 @@
+#
+# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2022, JetBrains s.r.o.. All rights reserved.
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# This code is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License version 2 only, as
+# published by the Free Software Foundation. Oracle designates this
+# particular file as subject to the "Classpath" exception as provided
+# by Oracle in the LICENSE file that accompanied this code.
+#
+# This code is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# version 2 for more details (a copy is included in the LICENSE file that
+# accompanied this code).
+#
+# You should have received a copy of the GNU General Public License version
+# 2 along with this work; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+# or visit www.oracle.com if you need additional information or have any
+# questions.
+#
+
+################################################################################
+# Setup nvdacontrollerclient (The library for communication with
+# NVDA - a screen reader for Microsoft Windows)
+################################################################################
+AC_DEFUN_ONCE([LIB_SETUP_NVDACONTROLLERCLIENT], [
+ # To enable NVDA, user specifies neither --with-nvdacontrollerclient or
+ # a pair (--with-nvdacontrollerclient-include, --with-nvdacontrollerclient-lib)
+ AC_ARG_WITH(nvdacontrollerclient, [AS_HELP_STRING([--with-nvdacontrollerclient],
+ [specify prefix directory for the NVDA Controller Client library package
+ (expecting headers and libs under PATH//)])])
+ AC_ARG_WITH(nvdacontrollerclient-include, [AS_HELP_STRING([--with-nvdacontrollerclient-include],
+ [specify directory for the NVDA Controller Client include files])])
+ AC_ARG_WITH(nvdacontrollerclient-lib, [AS_HELP_STRING([--with-nvdacontrollerclient-lib],
+ [specify directory for the NVDA Controller Client library])])
+
+ NVDACONTROLLERCLIENT_FOUND=no
+ NVDACONTROLLERCLIENT_LIB=
+ NVDACONTROLLERCLIENT_DLL=
+ NVDACONTROLLERCLIENT_CFLAGS=
+
+ if test "x${NEEDS_LIB_NVDACONTROLLERCLIENT}" = "xtrue" ; then
+ if (test "x${with_nvdacontrollerclient_include}" = "x" && test "x${with_nvdacontrollerclient_lib}" != "x") || \
+ (test "x${with_nvdacontrollerclient_include}" != "x" && test "x${with_nvdacontrollerclient_lib}" = "x") ; then
+ AC_MSG_ERROR([Must specify both or neither of --with-nvdacontrollerclient-include and --with-nvdacontrollerclient-lib])
+ elif (test "x${with_nvdacontrollerclient}" != "x" && test "x${with_nvdacontrollerclient_include}" != "x") ; then
+ AC_MSG_ERROR([Must specify either --with-nvdacontrollerclient or a pair (--with-nvdacontrollerclient-include, --with-nvdacontrollerclient-lib)])
+ fi
+
+ if (test "x${with_nvdacontrollerclient}" != "x") || \
+ (test "x${with_nvdacontrollerclient_include}" != "x" && test "x${with_nvdacontrollerclient_lib}" != "x") ; then
+
+ AC_MSG_CHECKING([for nvdacontrollerclient])
+
+ if test "x${OPENJDK_TARGET_OS}" != "xwindows" ; then
+ AC_MSG_ERROR([--with-nvdacontrollerclient[-*] flags are applicable only to Windows builds])
+ fi
+
+ if test "x${OPENJDK_TARGET_CPU_ARCH}" = "xaarch64" ; then
+ NVDACONTROLLERCLIENT_BIN_BASENAME="nvdaControllerClient32"
+ NVDACONTROLLERCLIENT_ARCHDIR="arm64"
+ elif test "x${OPENJDK_TARGET_CPU_ARCH}" = "xx86" && test "x${OPENJDK_TARGET_CPU_BITS}" = "x64" ; then
+ NVDACONTROLLERCLIENT_BIN_BASENAME="nvdaControllerClient64"
+ NVDACONTROLLERCLIENT_ARCHDIR="x64"
+ elif test "x${OPENJDK_TARGET_CPU_ARCH}" = "xx86" && test "x${OPENJDK_TARGET_CPU_BITS}" = "x32" ; then
+ NVDACONTROLLERCLIENT_BIN_BASENAME="nvdaControllerClient32"
+ NVDACONTROLLERCLIENT_ARCHDIR="x86"
+ else
+ AC_MSG_ERROR([The nvdacontrollerclient library exists only for x86_32, x86_64, AArch64 architectures])
+ fi
+
+ if test "x${with_nvdacontrollerclient}" != "x" ; then
+ # NVDACONTROLLERCLIENT_ARCHDIR is used only here
+ NVDACONTROLLERCLIENT_INC_PATH="${with_nvdacontrollerclient}/${NVDACONTROLLERCLIENT_ARCHDIR}"
+ NVDACONTROLLERCLIENT_BIN_PATH="${with_nvdacontrollerclient}/${NVDACONTROLLERCLIENT_ARCHDIR}"
+ else
+ NVDACONTROLLERCLIENT_INC_PATH="${with_nvdacontrollerclient_include}"
+ NVDACONTROLLERCLIENT_BIN_PATH="${with_nvdacontrollerclient_lib}"
+ fi
+
+ POTENTIAL_NVDACONTROLLERCLIENT_DLL="${NVDACONTROLLERCLIENT_BIN_PATH}/${NVDACONTROLLERCLIENT_BIN_BASENAME}.dll"
+ POTENTIAL_NVDACONTROLLERCLIENT_LIB="${NVDACONTROLLERCLIENT_BIN_PATH}/${NVDACONTROLLERCLIENT_BIN_BASENAME}.lib"
+ POTENTIAL_NVDACONTROLLERCLIENT_EXP="${NVDACONTROLLERCLIENT_BIN_PATH}/${NVDACONTROLLERCLIENT_BIN_BASENAME}.exp"
+
+ if ! test -s "${POTENTIAL_NVDACONTROLLERCLIENT_DLL}" || \
+ ! test -s "${POTENTIAL_NVDACONTROLLERCLIENT_LIB}" || \
+ ! test -s "${POTENTIAL_NVDACONTROLLERCLIENT_EXP}" ; then
+ AC_MSG_ERROR([Could not find ${NVDACONTROLLERCLIENT_BIN_BASENAME}.dll and/or ${NVDACONTROLLERCLIENT_BIN_BASENAME}.lib and/or ${NVDACONTROLLERCLIENT_BIN_BASENAME}.exp inside ${NVDACONTROLLERCLIENT_BIN_PATH}])
+ fi
+ if ! test -s "${NVDACONTROLLERCLIENT_INC_PATH}/nvdaController.h" ; then
+ AC_MSG_ERROR([Could not find the header file nvdaController.h inside ${NVDACONTROLLERCLIENT_INC_PATH}])
+ fi
+
+ NVDACONTROLLERCLIENT_CFLAGS="-I${NVDACONTROLLERCLIENT_INC_PATH}"
+ NVDACONTROLLERCLIENT_DLL="${POTENTIAL_NVDACONTROLLERCLIENT_DLL}"
+ NVDACONTROLLERCLIENT_LIB="${POTENTIAL_NVDACONTROLLERCLIENT_LIB}"
+ NVDACONTROLLERCLIENT_FOUND=yes
+
+ AC_MSG_RESULT([includes at ${NVDACONTROLLERCLIENT_INC_PATH} ; binaries at ${NVDACONTROLLERCLIENT_BIN_PATH}])
+ fi
+ elif test "x${with_nvdacontrollerclient}" != "x" || \
+ test "x${with_nvdacontrollerclient_include}" != "x" || test "x${with_nvdacontrollerclient_lib}" != "x" ; then
+ AC_MSG_WARN([[nvdacontrollerclient is not used, so --with-nvdacontrollerclient[-*] is ignored]])
+ fi
+
+ if test "x${NVDACONTROLLERCLIENT_FOUND}" = "xyes" ; then
+ A11Y_NVDA_ANNOUNCING_ENABLED=true
+ else
+ A11Y_NVDA_ANNOUNCING_ENABLED=false
+ fi
+
+ AC_SUBST(A11Y_NVDA_ANNOUNCING_ENABLED)
+ AC_SUBST(NVDACONTROLLERCLIENT_CFLAGS)
+ AC_SUBST(NVDACONTROLLERCLIENT_DLL)
+ AC_SUBST(NVDACONTROLLERCLIENT_LIB)
+])
\ No newline at end of file
diff --git a/make/autoconf/lib-speechd.m4 b/make/autoconf/lib-speechd.m4
new file mode 100644
index 000000000000..0be30dc7db6d
--- /dev/null
+++ b/make/autoconf/lib-speechd.m4
@@ -0,0 +1,92 @@
+#
+# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2022, JetBrains s.r.o.. All rights reserved.
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# This code is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License version 2 only, as
+# published by the Free Software Foundation. Oracle designates this
+# particular file as subject to the "Classpath" exception as provided
+# by Oracle in the LICENSE file that accompanied this code.
+#
+# This code is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# version 2 for more details (a copy is included in the LICENSE file that
+# accompanied this code).
+#
+# You should have received a copy of the GNU General Public License version
+# 2 along with this work; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+# or visit www.oracle.com if you need additional information or have any
+# questions.
+#
+
+################################################################################
+# Setup speechd
+################################################################################
+AC_DEFUN_ONCE([LIB_SETUP_SPEECHD],
+[
+ AC_ARG_WITH(speechd, [AS_HELP_STRING([--with-speechd],
+ [specify prefix directory for the libspeechd package
+ (expecting the headers under PATH/include); required for AccessibleAnnouncer to work])])
+ AC_ARG_WITH(speechd-include, [AS_HELP_STRING([--with-speechd-include],
+ [specify directory for the speechd include files])])
+
+ if test "x$NEEDS_LIB_SPEECHD" = xfalse || test "x${with_speechd}" = xno || \
+ test "x${with_speechd_include}" = xno; then
+ if (test "x${with_speechd}" != x && test "x${with_speechd}" != xno) || \
+ (test "x${with_speechd_include}" != x && test "x${with_speechd_include}" != xno); then
+ AC_MSG_WARN([[speechd not used, so --with-speechd[-*] is ignored]])
+ fi
+ A11Y_SPEECHD_ANNOUNCING_ENABLED=false
+ SPEECHD_CFLAGS=
+ SPEECHD_LIBS=
+ else
+ SPEECHD_FOUND=no
+
+ if test "x${with_speechd}" != x && test "x${with_speechd}" != xyes; then
+ AC_MSG_CHECKING([for speechd header and library])
+ if test -s "${with_speechd}/include/libspeechd.h"; then
+ SPEECHD_CFLAGS="-I${with_speechd}/include"
+ SPEECHD_LIBS="-L${with_speechd}/lib -lspeechd"
+ SPEECHD_FOUND=yes
+ AC_MSG_RESULT([$SPEECHD_FOUND])
+ else
+ AC_MSG_ERROR([Can't find 'include/libspeechd.h' under ${with_speechd} given with the --with-speechd option.])
+ fi
+ fi
+ if test "x${with_speechd_include}" != x; then
+ AC_MSG_CHECKING([for speechd headers])
+ if test -s "${with_speechd_include}/libspeechd.h"; then
+ SPEECHD_CFLAGS="-I${with_speechd_include}"
+ SPEECHD_FOUND=yes
+ AC_MSG_RESULT([$SPEECHD_FOUND])
+ else
+ AC_MSG_ERROR([Can't find 'include/libspeechd.h' under ${with_speechd} given with the --with-speechd-include option.])
+ fi
+ fi
+ if test "x$SPEECHD_FOUND" = xno; then
+ # Are the libspeechd headers installed in the default /usr/include location?
+ AC_CHECK_HEADERS([libspeechd.h],
+ [ SPEECHD_FOUND=yes ],
+ [ SPEECHD_FOUND=no; break ]
+ )
+ if test "x$SPEECHD_FOUND" = xyes; then
+ SPEECHD_CFLAGS=
+ SPEECHD_LIBS="-lspeechd"
+ fi
+ fi
+ if test "x$SPEECHD_FOUND" = xno; then
+ A11Y_SPEECHD_ANNOUNCING_ENABLED=false
+ else
+ A11Y_SPEECHD_ANNOUNCING_ENABLED=true
+ fi
+ fi
+
+ AC_SUBST(A11Y_SPEECHD_ANNOUNCING_ENABLED)
+ AC_SUBST(SPEECHD_CFLAGS)
+ AC_SUBST(SPEECHD_LIBS)
+])
diff --git a/make/autoconf/libraries.m4 b/make/autoconf/libraries.m4
index 51d4f724c339..080fc4992877 100644
--- a/make/autoconf/libraries.m4
+++ b/make/autoconf/libraries.m4
@@ -33,7 +33,9 @@ m4_include([lib-freetype.m4])
m4_include([lib-hsdis.m4])
m4_include([lib-std.m4])
m4_include([lib-x11.m4])
-
+m4_include([lib-fontconfig.m4])
+m4_include([lib-speechd.m4])
+m4_include([lib-nvdacontrollerclient.m4])
m4_include([lib-tests.m4])
################################################################################
@@ -45,10 +47,16 @@ AC_DEFUN_ONCE([LIB_DETERMINE_DEPENDENCIES],
if test "x$OPENJDK_TARGET_OS" = xwindows || test "x$OPENJDK_TARGET_OS" = xmacosx; then
# No X11 support on windows or macosx
NEEDS_LIB_X11=false
+ NEEDS_LIB_SPEECHD=false
+ elif test "x$ENABLE_HEADLESS_ONLY" = xtrue; then
+ # No X11 support needed when building headless only
+ NEEDS_LIB_X11=false
+ NEEDS_LIB_SPEECHD=false
else
# All other instances need X11, even if building headless only, libawt still
# needs X11 headers.
NEEDS_LIB_X11=true
+ NEEDS_LIB_SPEECHD=true
fi
# Check if fontconfig is needed
@@ -87,6 +95,13 @@ AC_DEFUN_ONCE([LIB_DETERMINE_DEPENDENCIES],
else
NEEDS_LIB_FFI=false
fi
+
+ # Check if nvdacontrollerclient is needed
+ if test "x$OPENJDK_TARGET_OS" = xwindows && test "x$ENABLE_HEADLESS_ONLY" != xtrue; then
+ NEEDS_LIB_NVDACONTROLLERCLIENT=true
+ else
+ NEEDS_LIB_NVDACONTROLLERCLIENT=false
+ fi
])
################################################################################
@@ -126,7 +141,8 @@ AC_DEFUN_ONCE([LIB_SETUP_LIBRARIES],
LIB_SETUP_LIBFFI
LIB_SETUP_MISC_LIBS
LIB_SETUP_X11
-
+ LIB_SETUP_SPEECHD
+ LIB_SETUP_NVDACONTROLLERCLIENT
LIB_TESTS_SETUP_GTEST
BASIC_JDKLIB_LIBS=""
diff --git a/make/autoconf/spec.gmk.template b/make/autoconf/spec.gmk.template
index b78d3da6b32e..2b9de15f244a 100644
--- a/make/autoconf/spec.gmk.template
+++ b/make/autoconf/spec.gmk.template
@@ -468,6 +468,20 @@ UBSAN_LDFLAGS := @UBSAN_LDFLAGS@
X_CFLAGS := @X_CFLAGS@
X_LIBS := @X_LIBS@
+# Linux speechd a11y announcer
+A11Y_SPEECHD_ANNOUNCING_ENABLED:=@A11Y_SPEECHD_ANNOUNCING_ENABLED@
+SPEECHD_CFLAGS:=@SPEECHD_CFLAGS@
+SPEECHD_LIBS:=@SPEECHD_LIBS@
+
+# Windows NVDA a11y announcer
+A11Y_NVDA_ANNOUNCING_ENABLED:=@A11Y_NVDA_ANNOUNCING_ENABLED@
+NVDACONTROLLERCLIENT_CFLAGS:=@NVDACONTROLLERCLIENT_CFLAGS@
+NVDACONTROLLERCLIENT_DLL:=@NVDACONTROLLERCLIENT_DLL@
+NVDACONTROLLERCLIENT_LIB:=@NVDACONTROLLERCLIENT_LIB@
+
+# Windows the client for the JAWS screen reader
+A11Y_JAWS_ANNOUNCING_ENABLED:=@A11Y_JAWS_ANNOUNCING_ENABLED@
+
# The lowest required version of macosx
MACOSX_VERSION_MIN := @MACOSX_VERSION_MIN@
# The highest allowed version of macosx
diff --git a/make/ide/idea/jdk/build.xml b/make/ide/idea/jdk/build.xml
deleted file mode 100644
index 0292f0f5d359..000000000000
--- a/make/ide/idea/jdk/build.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/make/ide/idea/jdk/idea.gmk b/make/ide/idea/jdk/idea.gmk
index 310b582591c9..f231da5291fa 100644
--- a/make/ide/idea/jdk/idea.gmk
+++ b/make/ide/idea/jdk/idea.gmk
@@ -46,13 +46,18 @@ else #with SPEC
endif
idea:
- $(ECHO) "SUPPORT=$(SUPPORT_OUTPUTDIR)" >> $(OUT)
- $(ECHO) "MODULE_ROOTS=\"$(foreach mod, $(SEL_MODULES), $(call FindModuleSrcDirs,$(mod)))\"" >> $(OUT)
+ $(ECHO) "MODULES=\"$(foreach mod, $(SEL_MODULES), \
+ module='$(mod)' \
+ moduleSrcDirs='$(foreach m,$(call FindModuleSrcDirs,$(mod)),$(call RelativePath,$m,$(topdir)))' \
+ moduleDependencies='$(call FindTransitiveDepsForModule,$(mod))' \
+ #)\"" > $(OUT)
$(ECHO) "MODULE_NAMES=\"$(strip $(foreach mod, $(SEL_MODULES), $(mod)))\"" >> $(OUT)
- $(ECHO) "SEL_MODULES=\"$(SEL_MODULES)\"" >> $(OUT)
- $(ECHO) "BOOT_JDK=\"$(BOOT_JDK)\"" >> $(OUT)
- $(ECHO) "CYGPATH=\"$(PATHTOOL)\"" >> $(OUT)
- $(ECHO) "SPEC=\"$(SPEC)\"" >> $(OUT)
+ $(ECHO) "RELATIVE_TOPLEVEL_PROJECT_DIR=\"$(call RelativePath,$(TOPLEVEL_DIR),$(IDEA_OUTPUT_PARENT))\"" >> $(OUT)
+ $(ECHO) "RELATIVE_PROJECT_DIR=\"$(call RelativePath,$(topdir),$(IDEA_OUTPUT_PARENT))\"" >> $(OUT)
+ $(ECHO) "RELATIVE_BUILD_DIR=\"$(call RelativePath,$(OUTPUTDIR),$(IDEA_OUTPUT_PARENT))\"" >> $(OUT)
+ $(ECHO) "CLION_RELATIVE_PROJECT_DIR=\"$(call RelativePath,$(topdir),$(IDEA_OUTPUT_PARENT)/.idea/jdk-clion)\"" >> $(OUT)
+ $(ECHO) "PATHTOOL=\"$(PATHTOOL)\"" >> $(OUT)
$(ECHO) "JT_HOME=\"$(JT_HOME)\"" >> $(OUT)
+ $(ECHO) "WINENV_ROOT=\"$(WINENV_ROOT)\"" >> $(OUT)
endif
diff --git a/make/ide/idea/jdk/template/ant.xml b/make/ide/idea/jdk/template/ant.xml
deleted file mode 100644
index 9cb90246ea04..000000000000
--- a/make/ide/idea/jdk/template/ant.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/make/ide/idea/jdk/template/compiler.xml b/make/ide/idea/jdk/template/compiler.xml
index eef129e7b871..9281fa10db72 100644
--- a/make/ide/idea/jdk/template/compiler.xml
+++ b/make/ide/idea/jdk/template/compiler.xml
@@ -3,10 +3,10 @@
-
-
-
-
+
+
+
+
diff --git a/make/ide/idea/jdk/template/copyright/JetBrains.xml b/make/ide/idea/jdk/template/copyright/JetBrains.xml
new file mode 100644
index 000000000000..d4b6973ca964
--- /dev/null
+++ b/make/ide/idea/jdk/template/copyright/JetBrains.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/make/ide/idea/jdk/template/copyright/JetBrainsCE.xml b/make/ide/idea/jdk/template/copyright/JetBrainsCE.xml
new file mode 100644
index 000000000000..abd9a37522ac
--- /dev/null
+++ b/make/ide/idea/jdk/template/copyright/JetBrainsCE.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/make/ide/idea/jdk/template/copyright/profiles_settings.xml b/make/ide/idea/jdk/template/copyright/profiles_settings.xml
index 7d61b5cdf1fd..641bfc1076d2 100644
--- a/make/ide/idea/jdk/template/copyright/profiles_settings.xml
+++ b/make/ide/idea/jdk/template/copyright/profiles_settings.xml
@@ -1,3 +1,7 @@
-
-
+
+
+
+
+
+
\ No newline at end of file
diff --git a/make/ide/idea/jdk/template/.name b/make/ide/idea/jdk/template/jdk-clion/.idea/.name
similarity index 100%
rename from make/ide/idea/jdk/template/.name
rename to make/ide/idea/jdk/template/jdk-clion/.idea/.name
diff --git a/make/ide/idea/jdk/template/jdk-clion/.idea/copyright/JetBrains.xml b/make/ide/idea/jdk/template/jdk-clion/.idea/copyright/JetBrains.xml
new file mode 100644
index 000000000000..d4b6973ca964
--- /dev/null
+++ b/make/ide/idea/jdk/template/jdk-clion/.idea/copyright/JetBrains.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/make/ide/idea/jdk/template/jdk-clion/.idea/copyright/JetBrainsCE.xml b/make/ide/idea/jdk/template/jdk-clion/.idea/copyright/JetBrainsCE.xml
new file mode 100644
index 000000000000..abd9a37522ac
--- /dev/null
+++ b/make/ide/idea/jdk/template/jdk-clion/.idea/copyright/JetBrainsCE.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/make/ide/idea/jdk/template/jdk-clion/.idea/copyright/profiles_settings.xml b/make/ide/idea/jdk/template/jdk-clion/.idea/copyright/profiles_settings.xml
new file mode 100644
index 000000000000..641bfc1076d2
--- /dev/null
+++ b/make/ide/idea/jdk/template/jdk-clion/.idea/copyright/profiles_settings.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/make/ide/idea/jdk/template/jdk-clion/.idea/misc.xml b/make/ide/idea/jdk/template/jdk-clion/.idea/misc.xml
new file mode 100644
index 000000000000..787e31971f49
--- /dev/null
+++ b/make/ide/idea/jdk/template/jdk-clion/.idea/misc.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/make/ide/idea/jdk/template/jdk-clion/.idea/scopes/TestFiles.xml b/make/ide/idea/jdk/template/jdk-clion/.idea/scopes/TestFiles.xml
new file mode 100644
index 000000000000..6b6961cf7074
--- /dev/null
+++ b/make/ide/idea/jdk/template/jdk-clion/.idea/scopes/TestFiles.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/make/ide/idea/jdk/template/jdk-clion/.idea/scopes/scope_settings.xml b/make/ide/idea/jdk/template/jdk-clion/.idea/scopes/scope_settings.xml
new file mode 100644
index 000000000000..db1b8ba4462a
--- /dev/null
+++ b/make/ide/idea/jdk/template/jdk-clion/.idea/scopes/scope_settings.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/make/ide/idea/jdk/template/jdk-clion/.idea/vcs.xml b/make/ide/idea/jdk/template/jdk-clion/.idea/vcs.xml
new file mode 100644
index 000000000000..726bb30b95c9
--- /dev/null
+++ b/make/ide/idea/jdk/template/jdk-clion/.idea/vcs.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/make/ide/idea/jdk/template/jdk-clion/.idea/workspace.xml b/make/ide/idea/jdk/template/jdk-clion/.idea/workspace.xml
new file mode 100644
index 000000000000..221cc601bdf0
--- /dev/null
+++ b/make/ide/idea/jdk/template/jdk-clion/.idea/workspace.xml
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/make/ide/idea/jdk/template/jdk-clion/update-project.sh b/make/ide/idea/jdk/template/jdk-clion/update-project.sh
new file mode 100644
index 000000000000..571145aabdf9
--- /dev/null
+++ b/make/ide/idea/jdk/template/jdk-clion/update-project.sh
@@ -0,0 +1,29 @@
+#!/bin/bash
+
+TOPDIR="###CLION_SCRIPT_TOPDIR###"
+BUILD_DIR="###RELATIVE_BUILD_DIR###"
+PATHTOOL="###PATHTOOL###"
+
+
+
+cd "`dirname $0`"
+SCRIPT_DIR="`pwd`"
+cd "$TOPDIR"
+
+echo "Updating Clion project files in \"$SCRIPT_DIR\" for project \"`pwd`\""
+
+set -o pipefail
+make compile-commands SPEC="$BUILD_DIR/spec.gmk" | sed 's/^/ /' || exit 1
+
+if [ "x$PATHTOOL" != "x" ]; then
+ CLION_PROJECT_DIR="`$PATHTOOL -am $SCRIPT_DIR`"
+ sed "s/\\\\\\\\\\\\\\\\/\\\\\\\\/g" "$BUILD_DIR/compile_commands.json" > "$SCRIPT_DIR/compile_commands.json"
+else
+ CLION_PROJECT_DIR="$SCRIPT_DIR"
+ cp "$BUILD_DIR/compile_commands.json" "$SCRIPT_DIR"
+fi
+
+echo "
+Now you can open \"$CLION_PROJECT_DIR\" as Clion project
+If Clion complains about missing files when loading a project, building it may help:
+ cd \"`pwd`\" && make SPEC=\"$BUILD_DIR/spec.gmk\""
diff --git a/make/ide/idea/jdk/template/jdk.iml b/make/ide/idea/jdk/template/jdk.iml
index c7482e6212e0..3586d58c0aaa 100644
--- a/make/ide/idea/jdk/template/jdk.iml
+++ b/make/ide/idea/jdk/template/jdk.iml
@@ -2,10 +2,9 @@
-
- ###SOURCE_ROOTS###
-
-
+
+
+
diff --git a/make/ide/idea/jdk/template/misc.xml b/make/ide/idea/jdk/template/misc.xml
index 486001253483..acf28bc820e7 100644
--- a/make/ide/idea/jdk/template/misc.xml
+++ b/make/ide/idea/jdk/template/misc.xml
@@ -6,11 +6,8 @@
###JTREG_HOME###
###BUILD_DIR###
-
+
-
-
-
diff --git a/make/ide/idea/jdk/template/module.iml b/make/ide/idea/jdk/template/module.iml
new file mode 100644
index 000000000000..bd0e65301c72
--- /dev/null
+++ b/make/ide/idea/jdk/template/module.iml
@@ -0,0 +1,11 @@
+
+
+
+
+ ###MODULE_CONTENT_ROOTS###
+
+ ###DEPENDENCIES###
+
+
+
+
diff --git a/make/ide/idea/jdk/template/modules.xml b/make/ide/idea/jdk/template/modules.xml
index 2d8401d0398a..a87145139038 100644
--- a/make/ide/idea/jdk/template/modules.xml
+++ b/make/ide/idea/jdk/template/modules.xml
@@ -3,6 +3,8 @@
+ ###MODULE_IMLS###
+
diff --git a/make/ide/idea/jdk/template/scopes/TestFiles.xml b/make/ide/idea/jdk/template/scopes/TestFiles.xml
new file mode 100644
index 000000000000..6b6961cf7074
--- /dev/null
+++ b/make/ide/idea/jdk/template/scopes/TestFiles.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/make/ide/idea/jdk/template/src/idea/IdeaLoggerWrapper.java b/make/ide/idea/jdk/template/src/idea/IdeaLoggerWrapper.java
deleted file mode 100644
index 0071669815a5..000000000000
--- a/make/ide/idea/jdk/template/src/idea/IdeaLoggerWrapper.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package idea;
-
-import org.apache.tools.ant.Task;
-
-/**
- * This class implements a custom Ant task which replaces the standard Intellij IDEA Ant logger
- * with a custom one which generates tighter output.
- */
-public class IdeaLoggerWrapper extends Task {
- public void execute() {
- new JdkIdeaAntLogger(getProject());
- }
-}
diff --git a/make/ide/idea/jdk/template/src/idea/JdkIdeaAntLogger.java b/make/ide/idea/jdk/template/src/idea/JdkIdeaAntLogger.java
deleted file mode 100644
index dd640bd95a09..000000000000
--- a/make/ide/idea/jdk/template/src/idea/JdkIdeaAntLogger.java
+++ /dev/null
@@ -1,375 +0,0 @@
-/*
- * Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package idea;
-
-import org.apache.tools.ant.BuildEvent;
-import org.apache.tools.ant.BuildListener;
-import org.apache.tools.ant.DefaultLogger;
-import org.apache.tools.ant.Project;
-
-import java.util.EnumSet;
-import java.util.Stack;
-
-import static org.apache.tools.ant.Project.*;
-
-/**
- * This class is used to wrap the IntelliJ ant logger in order to provide more meaningful
- * output when building langtools. The basic ant output in IntelliJ can be quite cumbersome to
- * work with, as it provides two separate views: (i) a tree view, which is good to display build task
- * in a hierarchical fashion as they are processed; and a (ii) plain text view, which gives you
- * the full ant output. The main problem is that javac-related messages are buried into the
- * ant output (which is made very verbose by IntelliJ in order to support the tree view). It is
- * not easy to figure out which node to expand in order to see the error message; switching
- * to plain text doesn't help either, as now the output is totally flat.
- *
- * This logger class removes a lot of verbosity from the IntelliJ ant logger by not propagating
- * all the events to the IntelliJ's logger. In addition, certain events are handled in a custom
- * fashion, to generate better output during the build.
- */
-public final class JdkIdeaAntLogger extends DefaultLogger {
-
- /**
- * This is just a way to pass in customized binary string predicates;
- *
- * TODO: replace with {@code BiPredicate} and method reference when moving to 8
- */
- enum StringBinaryPredicate {
- CONTAINS() {
- @Override
- boolean apply(String s1, String s2) {
- return s1.contains(s2);
- }
- },
- STARTS_WITH {
- @Override
- boolean apply(String s1, String s2) {
- return s1.startsWith(s2);
- }
- },
- MATCHES {
- @Override
- boolean apply(String s1, String s2) {
- return s1.matches(s2);
- }
- };
-
- abstract boolean apply(String s1, String s2);
- }
-
- /**
- * Various kinds of ant messages that we shall intercept
- */
- enum MessageKind {
-
- /** a make error */
- MAKE_ERROR(StringBinaryPredicate.CONTAINS, MSG_ERR, "error:", "compiler.err"),
- /** a make warning */
- MAKE_WARNING(StringBinaryPredicate.CONTAINS, MSG_WARN, "warning:", "compiler.warn"),
- /** a make note */
- MAKE_NOTE(StringBinaryPredicate.CONTAINS, MSG_INFO, "note:", "compiler.note"),
- /** std make output */
- MAKE_OTHER(StringBinaryPredicate.MATCHES, MSG_INFO, ".*"),
- /** a javac crash */
- JAVAC_CRASH(StringBinaryPredicate.STARTS_WITH, MSG_ERR, "An exception has occurred in the compiler"),
- /** jtreg test success */
- JTREG_TEST_PASSED(StringBinaryPredicate.STARTS_WITH, MSG_INFO, "Passed: "),
- /** jtreg test failure */
- JTREG_TEST_FAILED(StringBinaryPredicate.STARTS_WITH, MSG_ERR, "FAILED: "),
- /** jtreg test error */
- JTREG_TEST_ERROR(StringBinaryPredicate.STARTS_WITH, MSG_ERR, "Error: "),
- /** jtreg report */
- JTREG_TEST_REPORT(StringBinaryPredicate.STARTS_WITH, MSG_INFO, "Report written");
-
- StringBinaryPredicate sbp;
- int priority;
- String[] keys;
-
- MessageKind(StringBinaryPredicate sbp, int priority, String... keys) {
- this.sbp = sbp;
- this.priority = priority;
- this.keys = keys;
- }
-
- /**
- * Does a given message string matches this kind?
- */
- boolean matches(String s) {
- for (String key : keys) {
- if (sbp.apply(s, key)) {
- return true;
- }
- }
- return false;
- }
- }
-
- /**
- * This enum is used to represent the list of tasks we need to keep track of during logging.
- */
- enum Task {
- /** javac task - invoked during compilation */
- MAKE("exec", MessageKind.MAKE_ERROR, MessageKind.MAKE_WARNING, MessageKind.MAKE_NOTE,
- MessageKind.MAKE_OTHER, MessageKind.JAVAC_CRASH),
- /** jtreg task - invoked during test execution */
- JTREG("jtreg", MessageKind.JTREG_TEST_PASSED, MessageKind.JTREG_TEST_FAILED, MessageKind.JTREG_TEST_ERROR, MessageKind.JTREG_TEST_REPORT),
- /** initial synthetic task when the logger is created */
- ROOT("") {
- @Override
- boolean matches(String s) {
- return false;
- }
- },
- /** synthetic task catching any other tasks not in this list */
- ANY("") {
- @Override
- boolean matches(String s) {
- return true;
- }
- };
-
- String taskName;
- MessageKind[] msgs;
-
- Task(String taskName, MessageKind... msgs) {
- this.taskName = taskName;
- this.msgs = msgs;
- }
-
- boolean matches(String s) {
- return s.equals(taskName);
- }
- }
-
- /**
- * This enum is used to represent the list of targets we need to keep track of during logging.
- * A regular expression is used to match a given target name.
- */
- enum Target {
- /** jtreg target - executed when launching tests */
- JTREG("jtreg") {
- @Override
- String getDisplayMessage(BuildEvent e) {
- return "Running jtreg tests: " + e.getProject().getProperty("jtreg.tests");
- }
- },
- /** build selected modules */
- BUILD_MODULE("build-module") {
- @Override
- String getDisplayMessage(BuildEvent e) {
- return "Building modules: " + e.getProject().getProperty("module.name") + "...";
- }
- },
- /** build images */
- BUILD_IMAGES("images") {
- @Override
- String getDisplayMessage(BuildEvent e) {
- return "Building images...";
- }
- },
- /** build images */
- CONFIGURE("-do-configure") {
- @Override
- String getDisplayMessage(BuildEvent e) {
- return "Configuring build...";
- }
- },
- /** synthetic target catching any other target not in this list */
- ANY("") {
- @Override
- String getDisplayMessage(BuildEvent e) {
- return "Executing Ant target(s): " + e.getProject().getProperty("ant.project.invoked-targets");
- }
- @Override
- boolean matches(String msg) {
- return true;
- }
- };
-
- String targetRegex;
-
- Target(String targetRegex) {
- this.targetRegex = targetRegex;
- }
-
- boolean matches(String msg) {
- return msg.matches(targetRegex);
- }
-
- abstract String getDisplayMessage(BuildEvent e);
- }
-
- /**
- * A custom build event used to represent status changes which should be notified inside
- * Intellij
- */
- static class StatusEvent extends BuildEvent {
-
- /** the target to which the status update refers */
- Target target;
-
- StatusEvent(BuildEvent e, Target target) {
- super(new StatusTask(e, target.getDisplayMessage(e)));
- this.target = target;
- setMessage(getTask().getTaskName(), 2);
- }
-
- /**
- * A custom task used to channel info regarding a status change
- */
- static class StatusTask extends org.apache.tools.ant.Task {
- StatusTask(BuildEvent event, String msg) {
- setProject(event.getProject());
- setOwningTarget(event.getTarget());
- setTaskName(msg);
- }
- }
- }
-
- /** wrapped ant logger (IntelliJ's own logger) */
- DefaultLogger logger;
-
- /** flag - is this the first target we encounter? */
- boolean firstTarget = true;
-
- /** flag - should subsequent failures be suppressed ? */
- boolean suppressTaskFailures = false;
-
- /** flag - have we ran into a javac crash ? */
- boolean crashFound = false;
-
- /** stack of status changes associated with pending targets */
- Stack statusEvents = new Stack<>();
-
- /** stack of pending tasks */
- Stack tasks = new Stack<>();
-
- public JdkIdeaAntLogger(Project project) {
- for (Object o : project.getBuildListeners()) {
- if (o instanceof DefaultLogger) {
- this.logger = (DefaultLogger)o;
- project.removeBuildListener((BuildListener)o);
- project.addBuildListener(this);
- }
- }
- tasks.push(Task.ROOT);
- }
-
- @Override
- public void buildStarted(BuildEvent event) {
- //do nothing
- }
-
- @Override
- public void buildFinished(BuildEvent event) {
- //do nothing
- }
-
- @Override
- public void targetStarted(BuildEvent event) {
- EnumSet statusKinds = firstTarget ?
- EnumSet.allOf(Target.class) :
- EnumSet.complementOf(EnumSet.of(Target.ANY));
-
- String targetName = event.getTarget().getName();
-
- for (Target statusKind : statusKinds) {
- if (statusKind.matches(targetName)) {
- StatusEvent statusEvent = new StatusEvent(event, statusKind);
- statusEvents.push(statusEvent);
- logger.taskStarted(statusEvent);
- firstTarget = false;
- return;
- }
- }
- }
-
- @Override
- public void targetFinished(BuildEvent event) {
- if (!statusEvents.isEmpty()) {
- StatusEvent lastEvent = statusEvents.pop();
- if (lastEvent.target.matches(event.getTarget().getName())) {
- logger.taskFinished(lastEvent);
- }
- }
- }
-
- @Override
- public void taskStarted(BuildEvent event) {
- String taskName = event.getTask().getTaskName();
- System.err.println("task started " + taskName);
- for (Task task : Task.values()) {
- if (task.matches(taskName)) {
- tasks.push(task);
- return;
- }
- }
- }
-
- @Override
- public void taskFinished(BuildEvent event) {
- if (tasks.peek() == Task.ROOT) {
- //we need to 'close' the root task to get nicer output
- logger.taskFinished(event);
- } else if (!suppressTaskFailures && event.getException() != null) {
- //the first (innermost) task failure should always be logged
- event.setMessage(event.getException().toString(), 0);
- event.setException(null);
- //note: we turn this into a plain message to avoid stack trace being logged by Idea
- logger.messageLogged(event);
- suppressTaskFailures = true;
- }
- tasks.pop();
- }
-
- @Override
- public void messageLogged(BuildEvent event) {
- String msg = event.getMessage();
-
- boolean processed = false;
-
- if (!tasks.isEmpty()) {
- Task task = tasks.peek();
- for (MessageKind messageKind : task.msgs) {
- if (messageKind.matches(msg)) {
- event.setMessage(msg, messageKind.priority);
- processed = true;
- if (messageKind == MessageKind.JAVAC_CRASH) {
- crashFound = true;
- }
- break;
- }
- }
- }
-
- if (event.getPriority() == MSG_ERR || crashFound) {
- //we log errors regardless of owning task
- logger.messageLogged(event);
- suppressTaskFailures = true;
- } else if (processed) {
- logger.messageLogged(event);
- }
- }
-}
diff --git a/make/ide/idea/jdk/template/test.iml b/make/ide/idea/jdk/template/test.iml
new file mode 100644
index 000000000000..a72758f3edca
--- /dev/null
+++ b/make/ide/idea/jdk/template/test.iml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+ ###TEST_MODULE_DEPENDENCIES###
+
+
+
diff --git a/make/ide/idea/jdk/template/vcs.xml b/make/ide/idea/jdk/template/vcs.xml
index 3fddb2616b9b..31bda57fe54a 100644
--- a/make/ide/idea/jdk/template/vcs.xml
+++ b/make/ide/idea/jdk/template/vcs.xml
@@ -4,13 +4,13 @@
-
-
+
+
-
+
diff --git a/make/ide/idea/jdk/template/workspace.xml b/make/ide/idea/jdk/template/workspace.xml
index f1270d4fb5d2..54f29e47608f 100644
--- a/make/ide/idea/jdk/template/workspace.xml
+++ b/make/ide/idea/jdk/template/workspace.xml
@@ -2,63 +2,62 @@
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/make/jdk/src/classes/build/tools/x11wrappergen/WrapperGenerator.java b/make/jdk/src/classes/build/tools/x11wrappergen/WrapperGenerator.java
index 6e39adc0fb3c..0191cf1e7856 100644
--- a/make/jdk/src/classes/build/tools/x11wrappergen/WrapperGenerator.java
+++ b/make/jdk/src/classes/build/tools/x11wrappergen/WrapperGenerator.java
@@ -1142,6 +1142,8 @@ public void writeNativeSizer(String file)
pw.println("/* This file is an automatically generated file, please do not edit this file, modify the XlibParser.java file instead !*/\n" );
pw.println("#include \n#include \n#include \n#include \n#include \n");
pw.println("#include ");
+ pw.println("#include ");
+ pw.println("#include ");
pw.println("#include ");
pw.println("#include \"awt_p.h\"");
pw.println("#include \"color.h\"");
diff --git a/make/modules/java.desktop/Copy.gmk b/make/modules/java.desktop/Copy.gmk
index 4232e62552c8..4c4c92a56609 100644
--- a/make/modules/java.desktop/Copy.gmk
+++ b/make/modules/java.desktop/Copy.gmk
@@ -82,3 +82,13 @@ $(eval $(call SetupCopyLegalFiles, COPY_LEGAL, \
TARGETS += $(COPY_LEGAL)
################################################################################
+
+FONTFILE_SRC_DIR := $(TOPDIR)/src/java.desktop/share
+FONTFILE_SRCS := $(wildcard $(FONTFILE_SRC_DIR)/fonts/*.ttf) $(wildcard $(FONTFILE_SRC_DIR)/fonts/*.otf) $(FONTFILE_SRC_DIR)/fonts/fonts.dir $(FONTFILE_SRC_DIR)/fonts/font.conf
+FONTFILE_TARGET_FILES := $(subst $(FONTFILE_SRC_DIR),$(LIB_DST_DIR),$(FONTFILE_SRCS))
+
+$(LIB_DST_DIR)/fonts/%: $(FONTFILE_SRC_DIR)/fonts/%
+ $(call install-file)
+
+
+TARGETS += $(FONTFILE_TARGET_FILES)
diff --git a/make/modules/java.desktop/lib/Awt2dLibraries.gmk b/make/modules/java.desktop/lib/Awt2dLibraries.gmk
index e274005e6074..36a514c915f1 100644
--- a/make/modules/java.desktop/lib/Awt2dLibraries.gmk
+++ b/make/modules/java.desktop/lib/Awt2dLibraries.gmk
@@ -74,6 +74,7 @@ ifeq ($(call isTargetOs, windows), true)
$(TOPDIR)/src/$(MODULE)/share/native/common/font \
$(TOPDIR)/src/$(MODULE)/share/native/common/java2d/opengl \
$(TOPDIR)/src/$(MODULE)/$(OPENJDK_TARGET_OS_TYPE)/native/common/awt/systemscale \
+ $(TOPDIR)/src/java.base/$(OPENJDK_TARGET_OS_TYPE)/native/launcher \
#
endif
@@ -125,6 +126,36 @@ ifeq ($(TOOLCHAIN_TYPE), gcc)
LIBAWT_CFLAGS += -fgcse-after-reload
endif
+# Setup NVDA a11y announcer
+ifeq ($(call isTargetOs, windows), true)
+ A11Y_NVDA_ANNOUNCING_CFLAGS := -DNO_A11Y_NVDA_ANNOUNCING
+ A11Y_NVDA_ANNOUNCING_LIBS :=
+ A11Y_NVDA_ANNOUNCING_DEPENDENCIES :=
+
+ ifeq ($(ENABLE_HEADLESS_ONLY), false)
+ ifeq ($(A11Y_NVDA_ANNOUNCING_ENABLED), true)
+ A11Y_NVDA_ANNOUNCING_CFLAGS := $(NVDACONTROLLERCLIENT_CFLAGS)
+ A11Y_NVDA_ANNOUNCING_LIBS := $(NVDACONTROLLERCLIENT_LIB)
+ A11Y_NVDA_ANNOUNCING_DEPENDENCIES := $(NVDACONTROLLERCLIENT_DLL)
+ endif
+ endif
+
+ LIBAWT_CFLAGS += $(A11Y_NVDA_ANNOUNCING_CFLAGS)
+endif
+
+# Setup the client for the JAWS screen reader
+ifeq ($(call isTargetOs, windows), true)
+ A11Y_JAWS_ANNOUNCING_CFLAGS := -DNO_A11Y_JAWS_ANNOUNCING
+
+ ifeq ($(ENABLE_HEADLESS_ONLY), false)
+ ifeq ($(A11Y_JAWS_ANNOUNCING_ENABLED), true)
+ A11Y_JAWS_ANNOUNCING_CFLAGS :=
+ endif
+ endif
+
+ LIBAWT_CFLAGS += $(A11Y_JAWS_ANNOUNCING_CFLAGS)
+endif
+
$(eval $(call SetupJdkLibrary, BUILD_LIBAWT, \
NAME := awt, \
EXTRA_SRC := $(LIBAWT_EXTRA_SRC), \
@@ -148,7 +179,7 @@ $(eval $(call SetupJdkLibrary, BUILD_LIBAWT, \
DISABLED_WARNINGS_microsoft_awt_Toolkit.cpp := 4267, \
LDFLAGS := $(LDFLAGS_JDKLIB) $(call SET_SHARED_LIBRARY_ORIGIN), \
LDFLAGS_macosx := -L$(INSTALL_LIBRARIES_HERE), \
- LDFLAGS_windows := -delayload:user32.dll -delayload:gdi32.dll \
+ LDFLAGS_windows := -delayload:user32.dll -delayload:gdi32.dll -delayload:gdiplus.dll \
-delayload:shell32.dll -delayload:winmm.dll \
-delayload:winspool.drv -delayload:imm32.dll \
-delayload:ole32.dll -delayload:comdlg32.dll \
@@ -163,10 +194,10 @@ $(eval $(call SetupJdkLibrary, BUILD_LIBAWT, \
-framework JavaRuntimeSupport \
-framework ApplicationServices \
-framework AudioToolbox, \
- LIBS_windows := kernel32.lib user32.lib gdi32.lib winspool.lib \
+ LIBS_windows := kernel32.lib user32.lib gdi32.lib gdiplus.lib winspool.lib \
imm32.lib ole32.lib uuid.lib shell32.lib \
comdlg32.lib winmm.lib comctl32.lib shlwapi.lib \
- delayimp.lib jvm.lib $(WIN_JAVA_LIB) advapi32.lib, \
+ delayimp.lib jvm.lib $(WIN_JAVA_LIB) advapi32.lib dwmapi.lib $(A11Y_NVDA_ANNOUNCING_LIBS), \
VERSIONINFO_RESOURCE := $(LIBAWT_VERSIONINFO_RESOURCE), \
EXTRA_RCFLAGS := $(LIBAWT_RCFLAGS), \
))
@@ -177,6 +208,18 @@ ifeq ($(call isTargetOs, macosx), true)
$(BUILD_LIBAWT): $(BUILD_LIBMLIB_IMAGE)
endif
+ifeq ($(call isTargetOs, windows)+$(ENABLE_HEADLESS_ONLY)+$(A11Y_NVDA_ANNOUNCING_ENABLED), true+false+true)
+ $(eval $(call SetupCopyFiles, COPY_NVDA_DEPENDENCIES, \
+ FILES := $(A11Y_NVDA_ANNOUNCING_DEPENDENCIES), \
+ DEST := $(BUILD_LIBAWT_OUTPUT_DIR), \
+ ))
+
+ $(COPY_NVDA_DEPENDENCIES): $(A11Y_NVDA_ANNOUNCING_DEPENDENCIES)
+ $(BUILD_LIBAWT): $(COPY_NVDA_DEPENDENCIES)
+
+ TARGETS += $(COPY_NVDA_DEPENDENCIES)
+endif
+
TARGETS += $(BUILD_LIBAWT)
################################################################################
@@ -190,6 +233,18 @@ ifeq ($(call isTargetOs, windows macosx), false)
common/font \
#
+ ifeq ($(A11Y_SPEECHD_ANNOUNCING_ENABLED), true)
+ A11Y_SPEECHD_ANNOUNCING_CFLAGS :=
+ else
+ A11Y_SPEECHD_ANNOUNCING_CFLAGS := -DNO_A11Y_SPEECHD_ANNOUNCING
+ endif
+
+ ifeq ($(call isTargetOs, linux), true)
+ LIBAWT_XAWT_EXTRA_SRC += \
+ $(TOPDIR)/src/$(MODULE)/linux/native/libawt_xawt \
+ #
+ endif
+
LIBAWT_XAWT_EXCLUDES := medialib debug
LIBPIPEWIRE_HEADER_DIRS := \
@@ -208,7 +263,10 @@ ifeq ($(call isTargetOs, windows macosx), false)
LIBAWT_XAWT_CFLAGS += -DXAWT -DXAWT_HACK \
$(FONTCONFIG_CFLAGS) \
- $(CUPS_CFLAGS)
+ $(CUPS_CFLAGS) \
+ $(SPEECHD_CFLAGS) \
+ $(A11Y_SPEECHD_ANNOUNCING_CFLAGS) \
+ #
ifeq ($(call isTargetOs, linux), true)
ifeq ($(DISABLE_XRENDER), true)
@@ -216,7 +274,7 @@ ifeq ($(call isTargetOs, windows macosx), false)
endif
endif
- LIBAWT_XAWT_LIBS := $(LIBM) -lawt -lXext -lX11 -lXrender $(LIBDL) -lXtst -lXi -ljava -ljvm
+ LIBAWT_XAWT_LIBS := $(LIBM) -lawt -lXext -lX11 -lXrender $(SPEECHD_LIBS) $(LIBDL) -lXtst -lXi -ljava -ljvm
ifeq ($(call isTargetOs, linux), true)
LIBAWT_XAWT_LIBS += -lpthread
@@ -244,6 +302,7 @@ ifeq ($(call isTargetOs, windows macosx), false)
DISABLED_WARNINGS_gcc_GLXSurfaceData.c := unused-function, \
DISABLED_WARNINGS_gcc_gtk2_interface.c := parentheses type-limits, \
DISABLED_WARNINGS_gcc_gtk3_interface.c := parentheses type-limits unused-function, \
+ DISABLED_WARNINGS_gcc_keycode_cache.c := unused-function, \
DISABLED_WARNINGS_gcc_OGLBufImgOps.c := format-nonliteral, \
DISABLED_WARNINGS_gcc_OGLPaints.c := format-nonliteral, \
DISABLED_WARNINGS_gcc_screencast_pipewire.c := undef, \
@@ -321,6 +380,7 @@ $(eval $(call SetupJdkLibrary, BUILD_LIBLCMS, \
HEADERS_FROM_SRC := $(LIBLCMS_HEADERS_FROM_SRC), \
DISABLED_WARNINGS_gcc := format-nonliteral type-limits stringop-truncation, \
DISABLED_WARNINGS_clang := format-nonliteral, \
+ DISABLED_WARNINGS_clang_cmstypes.c := unused-but-set-parameter, \
LDFLAGS := $(LDFLAGS_JDKLIB) \
$(call SET_SHARED_LIBRARY_ORIGIN), \
LDFLAGS_unix := -L$(INSTALL_LIBRARIES_HERE), \
@@ -545,11 +605,11 @@ ifeq ($(call isTargetOs, windows), true)
else ifeq ($(call isTargetOs, macosx), true)
LIBFONTMANAGER_EXCLUDE_FILES += X11FontScaler.c \
X11TextRenderer.c \
- fontpath.c \
- lcdglyph.c
+ lcdglyph.c \
+ lcdglyphDW.cpp
else
- LIBFONTMANAGER_EXCLUDE_FILES += fontpath.c \
- lcdglyph.c
+ LIBFONTMANAGER_EXCLUDE_FILES += lcdglyph.c \
+ lcdglyphDW.cpp
endif
LIBFONTMANAGER_CFLAGS += $(X_CFLAGS) -DLE_STANDALONE -DHEADLESS
@@ -896,6 +956,7 @@ ifeq ($(call isTargetOs, macosx), true)
DISABLED_WARNINGS_clang_OGLBufImgOps.c := format-nonliteral, \
DISABLED_WARNINGS_clang_OGLPaints.c := format-nonliteral, \
DISABLED_WARNINGS_clang_PrinterView.m := enum-conversion, \
+ DISABLED_WARNINGS_clang_SystemHotkey.m := format-nonliteral, \
LDFLAGS := $(LDFLAGS_JDKLIB) \
$(call SET_SHARED_LIBRARY_ORIGIN) \
-L$(INSTALL_LIBRARIES_HERE), \
@@ -905,6 +966,7 @@ ifeq ($(call isTargetOs, macosx), true)
-framework AudioToolbox \
-framework Carbon \
-framework Cocoa \
+ -framework IOKit \
-framework Metal \
-framework Security \
-framework ExceptionHandling \
diff --git a/make/test/JtregNativeJdk.gmk b/make/test/JtregNativeJdk.gmk
index 5f945e90dd28..67d9af9c72a4 100644
--- a/make/test/JtregNativeJdk.gmk
+++ b/make/test/JtregNativeJdk.gmk
@@ -127,6 +127,17 @@ else
BUILD_JDK_JTREG_EXCLUDE += exeLibraryCache.c
endif
+ifeq ($(OPENJDK_TARGET_OS), windows)
+ BUILD_JDK_JTREG_LIBRARIES_LIBS_libwindows_touch_robot := user32.lib
+ BUILD_JDK_JTREG_EXCLUDE += libtouchscreen_device.c
+else
+ ifeq ($(OPENJDK_TARGET_OS), linux)
+ BUILD_JDK_JTREG_EXCLUDE += libwindows_touch_robot.c
+ else
+ BUILD_JDK_JTREG_EXCLUDE += libtouchscreen_device.c libwindows_touch_robot.c
+ endif
+endif
+
ifeq ($(call isTargetOs, linux), true)
# Unconditionally compile with debug symbols and don't ever perform
# stripping during the test libraries' build.
diff --git a/src/demo/share/java2d/J2DBench/build.xml b/src/demo/share/java2d/J2DBench/build.xml
index 7b202946cf14..ba7417bd4122 100644
--- a/src/demo/share/java2d/J2DBench/build.xml
+++ b/src/demo/share/java2d/J2DBench/build.xml
@@ -49,7 +49,7 @@
-
+
+ * -basexml | -b
+ *
+ * This directory must contain one file with pattern *{openjdk}*.{res} which is considered as a container
+ * of reference scores and several *{jbsdk}*.{res}.
+ *
+ *
Names of these files have several mandatory fields separated by "_" and look like
+ * osName_jdkName_renderName_*.res
+ *
+ *
+ * If any of score is less than corresponding reference value by 5% then exit code 1 is returned otherwise
+ * exit code 0 is returned.
+ *
+ * Standard output will contain scores in format required for TeamCity charts.
+ *
+ * Created by vprovodin on 13/02/2017.
+ */
+public class TCChartReporter {
+
+ private static boolean printTCValues = false;
+ private static boolean printValues = false;
+
+ private static final DecimalFormat decimalFormat =
+ new DecimalFormat("0.00");
+
+ private static FileSystem defaultFileSystem = FileSystems.getDefault();
+
+ private static double getMeasurementError(String testCaseName, String osName) {
+ if (testCaseName.contains("text.Rendering.tests.drawString") && osName.toLowerCase().contains("lin") )
+ return 0.18;
+ return 0.1;
+ }
+
+ /**
+ * Level at which tests are grouped to be displayed in summary
+ */
+ private static final int LEVEL = 2;
+
+ /**
+ * Holds the groups and corresponding group-display-names
+ */
+ private static List groups = new ArrayList<>();
+ private static Map referenceValues = new HashMap<>();
+ private static boolean testFailed = false;
+
+ private static void printUsage() {
+ String usage =
+ "\njava TCChartReporter [options] " +
+ " \n\n" +
+ "where options include: " +
+ " \n" +
+ " -basexml | -b " +
+ "path to base-build result";
+ System.out.println(usage);
+ System.exit(0);
+ }
+
+ /**
+ * String = getTestResultsTableForSummary()
+ */
+ private static double generateTestCaseReport(
+ Object key,
+ Map testCaseResult,
+ Map testCaseResultCount) {
+
+ Integer curTestCountObj = testCaseResultCount.get(key.toString());
+ int curTestCount = 0;
+ if (curTestCountObj != null) {
+ curTestCount = curTestCountObj;
+ }
+
+ double totalScore = 0;
+
+ for (int i = 0; i < curTestCount; i++) {
+ J2DAnalyzer.ResultHolder resultTCR = testCaseResult.get(key.toString() + "_" + i);
+ totalScore = totalScore + resultTCR.getScore();
+ }
+
+ return totalScore;
+ }
+
+ /**
+ * Generate Testcase Summary Report for TC - *.out
+ */
+ private static void generateTestCaseSummaryReport(
+ String OJRname,
+ Map consoleResult,
+ Map testCaseResult,
+ Map testCaseResultCount,
+ boolean rememberReference) {
+
+ String curGroupName, curTestName;
+
+ Object[] groupNameArray = groups.toArray();
+
+ Object[] testCaseList = consoleResult.keySet().toArray();
+ Arrays.sort(testCaseList);
+
+ for (Object aGroupNameArray : groupNameArray) {
+
+ double value;
+ curGroupName = aGroupNameArray.toString();
+
+ for (Object aTestCaseList : testCaseList) {
+
+ curTestName = aTestCaseList.toString();
+
+ if (curTestName.contains(curGroupName)) {
+
+ value = generateTestCaseReport(curTestName, testCaseResult, testCaseResultCount);
+
+ if (printTCValues)
+ System.out.println("##teamcity[buildStatisticValue key='" + (OJRname.isEmpty() ? "" : OJRname + ".") + curTestName
+ + "' value='" + decimalFormat.format(value) + "']");
+ if (printValues)
+ System.out.println((OJRname.isEmpty() ? "" : OJRname + ".") + curTestName + "," + decimalFormat.format(value));
+ if (rememberReference) {
+ referenceValues.put(curTestName, value);
+ } else {
+ double refValue = referenceValues.getOrDefault(curTestName, 0.);
+ if (Math.abs(value/refValue - 1) >= getMeasurementError(curTestName, OJRname)) {
+ System.err.println(OJRname);
+ System.err.println(curTestName);
+ System.err.println("\treferenceValue=" + refValue);
+ System.err.println("\t actualValue=" + value);
+ System.err.println("\t diff:" + ((value / refValue - 1) * 100));
+ testFailed = (value < refValue);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * main
+ */
+ public static void main(String args[]) {
+
+ String baseXML = null;
+ int group = 2;
+
+ /* ---- Analysis Mode ----
+ BEST = 1;
+ WORST = 2;
+ AVERAGE = 3;
+ MIDAVG = 4;
+ ------------------------ */
+ int analyzerMode = 4;
+
+ try {
+
+ for (int i = 0; i < args.length; i++) {
+
+ if (args[i].startsWith("-basexml") ||
+ args[i].startsWith("-b")) {
+ i++;
+ baseXML = args[i];
+ } else if (args[i].startsWith("-tc")) {
+ printTCValues = true;
+ } else if (args[i].startsWith("-v"))
+ printValues = true;
+ }
+ } catch (Exception e) {
+ printUsage();
+ }
+
+ XMLHTMLReporter.setGroupLevel(group);
+ J2DAnalyzer.setMode(analyzerMode);
+ if (baseXML != null) {
+ generateComparisonReport(defaultFileSystem.getPath(baseXML));
+ } else {
+ printUsage();
+ }
+
+ if (testFailed)
+ System.exit(1);
+ }
+
+ /**
+ * Add Test Group to the list
+ */
+ private static void addGroup(String testName) {
+
+ String testNameSplit[] = testName.replace('.', '_').split("_");
+ StringBuilder group = new StringBuilder(testNameSplit[0]);
+ for (int i = 1; i < LEVEL; i++) {
+ group.append(".").append(testNameSplit[i]);
+ }
+
+ if (!groups.contains(group.toString()))
+ groups.add(group.toString());
+ }
+
+ private static List listResFiles(Path dir, String pattern) throws IOException {
+ List result = new ArrayList<>();
+ try (DirectoryStream stream = Files.newDirectoryStream(dir, pattern)) {
+ for (Path entry : stream) {
+ result.add(entry);
+ }
+ } catch (DirectoryIteratorException ex) {
+ throw ex.getCause();
+ }
+ return result;
+ }
+
+ /**
+ * Generate the reports from the base & target result XML
+ */
+ private static void generateComparisonReport(Path directoryToResFiles) {
+
+ if (directoryToResFiles.toFile().isDirectory()) {
+ List jbsdkFiles, openjdkFiles;
+
+ try {
+ jbsdkFiles = listResFiles(directoryToResFiles, "*{jbsdk,jbre}*.{res}");
+ openjdkFiles = listResFiles(directoryToResFiles, "*{openjdk}*.{res}");
+ } catch (IOException e) {
+ e.printStackTrace();
+ return;
+ }
+
+ readScores(openjdkFiles.get(0), true);
+
+ for (Path file : jbsdkFiles) {
+ readScores(file, false);
+ }
+ } else {
+ readScores(directoryToResFiles, true);
+ }
+ }
+
+ private static void readScores(Path file, boolean rememberReference) {
+ String fileName = file.getName(file.getNameCount() - 1).toString();
+ String osName="", jdkName="", renderName="";
+ if (fileName.contains("win") || fileName.contains("linux") || fileName.contains("osx")) {
+ String[] fileNameComponents = fileName.split("_");
+ if (fileNameComponents.length > 0)
+ osName = fileNameComponents[0];
+ if (fileNameComponents.length > 1)
+ jdkName = fileNameComponents[1];
+ if (fileNameComponents.length > 2)
+ renderName = fileNameComponents[2];
+ }
+
+ String resultXMLFileName = file.toString();
+
+ J2DAnalyzer.results = new Vector();
+ J2DAnalyzer.readResults(resultXMLFileName);
+ J2DAnalyzer.SingleResultSetHolder baseSRSH =
+ (J2DAnalyzer.SingleResultSetHolder) J2DAnalyzer.results.elementAt(0);
+ Enumeration baseEnum_ = baseSRSH.getKeyEnumeration();
+ Vector baseKeyvector = new Vector<>();
+ while (baseEnum_.hasMoreElements()) {
+ baseKeyvector.add((String) baseEnum_.nextElement());
+ }
+ String baseKeys[] = new String[baseKeyvector.size()];
+ baseKeyvector.copyInto(baseKeys);
+ J2DAnalyzer.sort(baseKeys);
+
+ Map consoleBaseRes = new HashMap<>();
+
+ Map testCaseBaseResult = new HashMap<>();
+ Map testCaseResultCount = new HashMap<>();
+
+ for (String baseKey : baseKeys) {
+
+ J2DAnalyzer.ResultHolder baseTCR =
+ baseSRSH.getResultByKey(baseKey);
+
+ Integer curTestCountObj = testCaseResultCount.get(baseTCR.getName());
+ int curTestCount = 0;
+ if (curTestCountObj != null) {
+ curTestCount = curTestCountObj;
+ }
+ curTestCount++;
+ testCaseBaseResult.put(baseTCR.getName() + "_" + (curTestCount - 1), baseTCR);
+ testCaseResultCount.put(baseTCR.getName(), curTestCount);
+
+ addGroup(baseTCR.getName());
+
+ Double curTotalScoreObj = consoleBaseRes.get(baseTCR.getName());
+ double curTotalScore = 0;
+ if (curTotalScoreObj != null) {
+ curTotalScore = curTotalScoreObj;
+ }
+ curTotalScore = curTotalScore + baseTCR.getScore();
+ consoleBaseRes.put(baseTCR.getName(), curTotalScore);
+ }
+
+ String OJRname = osName + "." + jdkName + "." + renderName;
+ generateTestCaseSummaryReport((OJRname.length() == 2? "": OJRname),
+ consoleBaseRes,
+ testCaseBaseResult,
+ testCaseResultCount,
+ rememberReference);
+
+ }
+}
diff --git a/src/hotspot/os/aix/os_aix.cpp b/src/hotspot/os/aix/os_aix.cpp
index 561ef77b18b4..9282b6dd2a55 100644
--- a/src/hotspot/os/aix/os_aix.cpp
+++ b/src/hotspot/os/aix/os_aix.cpp
@@ -1160,6 +1160,10 @@ void *os::dll_load(const char *filename, char *ebuf, int ebuflen) {
return nullptr;
}
+void * os::dll_load_utf8(const char *filename, char *ebuf, int ebuflen) {
+ return os::dll_load(filename, ebuf, ebuflen);
+}
+
void os::print_dll_info(outputStream *st) {
st->print_cr("Dynamic libraries:");
LoadedLibraries::print(st);
diff --git a/src/hotspot/os/bsd/os_bsd.cpp b/src/hotspot/os/bsd/os_bsd.cpp
index 1f2094fba1f6..d0ac030e70e5 100644
--- a/src/hotspot/os/bsd/os_bsd.cpp
+++ b/src/hotspot/os/bsd/os_bsd.cpp
@@ -1243,6 +1243,10 @@ void * os::dll_load(const char *filename, char *ebuf, int ebuflen) {
}
#endif // !__APPLE__
+void * os::dll_load_utf8(const char *filename, char *ebuf, int ebuflen) {
+ return os::dll_load(filename, ebuf, ebuflen);
+}
+
int _print_dll_info_cb(const char * name, address base_address, address top_address, void * param) {
outputStream * out = (outputStream *) param;
out->print_cr(INTPTR_FORMAT " \t%s", (intptr_t)base_address, name);
diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp
index fb6701a50146..736f9eca4c0e 100644
--- a/src/hotspot/os/linux/os_linux.cpp
+++ b/src/hotspot/os/linux/os_linux.cpp
@@ -1807,6 +1807,10 @@ void * os::dll_load(const char *filename, char *ebuf, int ebuflen) {
return nullptr;
}
+void * os::dll_load_utf8(const char *filename, char *ebuf, int ebuflen) {
+ return os::dll_load(filename, ebuf, ebuflen);
+}
+
void * os::Linux::dlopen_helper(const char *filename, char *ebuf, int ebuflen) {
#ifndef IA32
bool ieee_handling = IEEE_subnormal_handling_OK();
diff --git a/src/hotspot/os/posix/signals_posix.cpp b/src/hotspot/os/posix/signals_posix.cpp
index eaadb3673151..199196086ff7 100644
--- a/src/hotspot/os/posix/signals_posix.cpp
+++ b/src/hotspot/os/posix/signals_posix.cpp
@@ -559,6 +559,12 @@ int JVM_HANDLE_XXX_SIGNAL(int sig, siginfo_t* info,
{
assert(info != nullptr && ucVoid != nullptr, "sanity");
+ if (sig == SIGABRT) {
+ // Re-set the handler so that we don't recurse if/when abort() is called
+ // from here.
+ PosixSignals::install_generic_signal_handler(SIGABRT, (void*)SIG_DFL);
+ }
+
// Note: it's not uncommon that JNI code uses signal/sigset to install,
// then restore certain signal handler (e.g. to temporarily block SIGPIPE,
// or have a SIGILL handler when detecting CPU type). When that happens,
@@ -1341,6 +1347,10 @@ void install_signal_handlers() {
assert(ret == 0, "check");
}
+ if (CatchSIGABRT) {
+ set_signal_handler(SIGABRT);
+ }
+
#if defined(__APPLE__)
// lldb (gdb) installs both standard BSD signal handlers, and mach exception
// handlers. By replacing the existing task exception handler, we disable lldb's mach
diff --git a/src/hotspot/os/windows/os_windows.cpp b/src/hotspot/os/windows/os_windows.cpp
index c08476df7c95..62ce2f8ad99e 100644
--- a/src/hotspot/os/windows/os_windows.cpp
+++ b/src/hotspot/os/windows/os_windows.cpp
@@ -1529,9 +1529,221 @@ static int _print_module(const char* fname, address base_address,
return 0;
}
+static errno_t convert_to_UTF16(char const* source_str, UINT source_encoding, LPWSTR* dest_utf16_str) {
+ const int len_estimated = MultiByteToWideChar(source_encoding,
+ MB_ERR_INVALID_CHARS,
+ source_str,
+ -1, // source is null-terminated
+ NULL,
+ 0); // estimate characters count
+ if (len_estimated == 0) {
+ // Probably source_str contains characters that cannot be represented in the source_encoding given.
+ *dest_utf16_str = NULL;
+ return EINVAL;
+ }
+
+ *dest_utf16_str = NEW_C_HEAP_ARRAY(WCHAR, len_estimated, mtInternal);
+
+ const int len_real = MultiByteToWideChar(source_encoding,
+ MB_ERR_INVALID_CHARS,
+ source_str,
+ -1, // source is null-terminated
+ *dest_utf16_str, len_estimated);
+ assert(len_real == len_estimated, "length already checked above");
+
+ return ERROR_SUCCESS;
+}
+
+// Converts a string in the "platform" encoding to UTF16.
+static errno_t convert_to_UTF16(char const* platform_str, LPWSTR* utf16_str) {
+ return convert_to_UTF16(platform_str, CP_ACP, utf16_str);
+}
+
+static errno_t convert_UTF8_to_UTF16(char const* utf8_str, LPWSTR* utf16_str) {
+ return convert_to_UTF16(utf8_str, CP_UTF8, utf16_str);
+}
+
+// Converts a wide-character string in UTF-16 encoding to the 8-bit "platform" encoding.
+// Unless the platform encoding is UTF-8, not all characters in the source string can be represented in the dest string.
+// The function succeeds in this case anyway and just replaces these with a certain character.
+static errno_t convert_UTF16_to_platform(LPWSTR source_utf16_str, char*& dest_str) {
+ const int len_estimated = WideCharToMultiByte(CP_ACP,
+ 0,
+ source_utf16_str,
+ -1, // source is null-terminated
+ NULL,
+ 0, // estimate characters count
+ NULL, NULL);
+ if (len_estimated == 0) {
+ dest_str = NULL;
+ return EINVAL;
+ }
+
+ dest_str = NEW_C_HEAP_ARRAY(CHAR, len_estimated, mtInternal);
+
+ const int len_real = WideCharToMultiByte(CP_ACP,
+ 0,
+ source_utf16_str,
+ -1, // source is null-terminated
+ dest_str, len_estimated, NULL, NULL);
+ assert(len_real == len_estimated, "length already checked above");
+
+ return ERROR_SUCCESS;
+}
+
+class MemoryReleaserW : public StackObj {
+private:
+ WCHAR* _object_ptr;
+
+public:
+ MemoryReleaserW(WCHAR * object_ptr) : _object_ptr(object_ptr) {}
+ ~MemoryReleaserW() { if (_object_ptr != NULL) FREE_C_HEAP_ARRAY(WCHAR, _object_ptr); }
+};
+
+class MemoryReleaser : public StackObj {
+private:
+ CHAR* _object_ptr;
+
+public:
+ MemoryReleaser(CHAR * object_ptr) : _object_ptr(object_ptr) {}
+ ~MemoryReleaser() { if (_object_ptr != NULL) FREE_C_HEAP_ARRAY(CHAR, _object_ptr); }
+};
+
// Loads .dll/.so and
// in case of error it checks if .dll/.so was built for the
// same architecture as Hotspot is running on
+void * os::dll_load_utf8(const char *utf8_name, char *ebuf, int ebuflen) {
+ LPWSTR utf16_name = NULL;
+ errno_t err = convert_UTF8_to_UTF16(utf8_name, &utf16_name);
+ MemoryReleaserW release_utf16_name(utf16_name);
+ if (err != ERROR_SUCCESS) {
+ errno = err;
+ return NULL;
+ }
+
+ char* platform_name = NULL; // name of the library converted to the "platform" encoding for use in log messages
+ errno_t ignored_err = convert_UTF16_to_platform(utf16_name, platform_name);
+ MemoryReleaser release_platform_name(platform_name);
+
+ log_info(os)("attempting shared library load of %s", platform_name);
+
+ void * result = LoadLibraryW(utf16_name);
+
+ if (result != NULL) {
+ Events::log(NULL, "Loaded shared library %s", platform_name);
+ // Recalculate pdb search path if a DLL was loaded successfully.
+ SymbolEngine::recalc_search_path();
+ log_info(os)("shared library load of %s was successful", platform_name);
+ return result;
+ }
+ DWORD errcode = GetLastError();
+ // Read system error message into ebuf
+ // It may or may not be overwritten below (in the for loop and just above)
+ lasterror(ebuf, (size_t) ebuflen);
+ ebuf[ebuflen - 1] = '\0';
+ Events::log(NULL, "Loading shared library %s failed, error code %lu", platform_name, errcode);
+ log_info(os)("shared library load of %s failed, error code %lu", platform_name, errcode);
+
+ if (errcode == ERROR_MOD_NOT_FOUND) {
+ strncpy(ebuf, "Can't find dependent libraries", ebuflen - 1);
+ ebuf[ebuflen - 1] = '\0';
+ return NULL;
+ }
+
+ // Parsing dll below
+ // If we can read dll-info and find that dll was built
+ // for an architecture other than Hotspot is running in
+ // - then print to buffer "DLL was built for a different architecture"
+ // else call os::lasterror to obtain system error message
+ int fd = ::wopen(utf16_name, O_RDONLY | O_BINARY, 0);
+ if (fd < 0) {
+ return NULL;
+ }
+
+ uint32_t signature_offset;
+ uint16_t lib_arch = 0;
+ bool failed_to_get_lib_arch =
+ ( // Go to position 3c in the dll
+ (os::seek_to_file_offset(fd, IMAGE_FILE_PTR_TO_SIGNATURE) < 0)
+ ||
+ // Read location of signature
+ (sizeof(signature_offset) !=
+ (::read(fd, (void*)&signature_offset, sizeof(signature_offset))))
+ ||
+ // Go to COFF File Header in dll
+ // that is located after "signature" (4 bytes long)
+ (os::seek_to_file_offset(fd,
+ signature_offset + IMAGE_FILE_SIGNATURE_LENGTH) < 0)
+ ||
+ // Read field that contains code of architecture
+ // that dll was built for
+ (sizeof(lib_arch) != (::read(fd, (void*)&lib_arch, sizeof(lib_arch))))
+ );
+
+ ::close(fd);
+ if (failed_to_get_lib_arch) {
+ // file i/o error - report os::lasterror(...) msg
+ return NULL;
+ }
+
+ typedef struct {
+ uint16_t arch_code;
+ char* arch_name;
+ } arch_t;
+
+ static const arch_t arch_array[] = {
+ {IMAGE_FILE_MACHINE_I386, (char*)"IA 32"},
+ {IMAGE_FILE_MACHINE_AMD64, (char*)"AMD 64"},
+ {IMAGE_FILE_MACHINE_ARM64, (char*)"ARM 64"}
+ };
+#if (defined _M_ARM64)
+ static const uint16_t running_arch = IMAGE_FILE_MACHINE_ARM64;
+#elif (defined _M_AMD64)
+ static const uint16_t running_arch = IMAGE_FILE_MACHINE_AMD64;
+#elif (defined _M_IX86)
+ static const uint16_t running_arch = IMAGE_FILE_MACHINE_I386;
+#else
+ #error Method os::dll_load requires that one of following \
+ is defined :_M_AMD64 or _M_IX86 or _M_ARM64
+#endif
+
+
+ // Obtain a string for printf operation
+ // lib_arch_str shall contain string what platform this .dll was built for
+ // running_arch_str shall string contain what platform Hotspot was built for
+ char *running_arch_str = NULL, *lib_arch_str = NULL;
+ for (unsigned int i = 0; i < ARRAY_SIZE(arch_array); i++) {
+ if (lib_arch == arch_array[i].arch_code) {
+ lib_arch_str = arch_array[i].arch_name;
+ }
+ if (running_arch == arch_array[i].arch_code) {
+ running_arch_str = arch_array[i].arch_name;
+ }
+ }
+
+ assert(running_arch_str,
+ "Didn't find running architecture code in arch_array");
+
+ // If the architecture is right
+ // but some other error took place - report os::lasterror(...) msg
+ if (lib_arch == running_arch) {
+ return NULL;
+ }
+
+ if (lib_arch_str != NULL) {
+ ::_snprintf(ebuf, ebuflen - 1,
+ "Can't load %s-bit .dll on a %s-bit platform",
+ lib_arch_str, running_arch_str);
+ } else {
+ // don't know what architecture this dll was build for
+ ::_snprintf(ebuf, ebuflen - 1,
+ "Can't load this .dll (machine code=0x%x) on a %s-bit platform",
+ lib_arch, running_arch_str);
+ }
+
+ return NULL;
+}
+
void * os::dll_load(const char *name, char *ebuf, int ebuflen) {
log_info(os)("attempting shared library load of %s", name);
void* result;
@@ -4548,7 +4760,7 @@ static wchar_t* wide_abs_unc_path(char const* path, errno_t & err, int additiona
set_path_prefix(buf, &prefix, &prefix_off, &needs_fullpath);
LPWSTR unicode_path = nullptr;
- err = convert_to_unicode(buf, &unicode_path);
+ err = convert_to_UTF16(buf, &unicode_path);
FREE_C_HEAP_ARRAY(char, buf);
if (err != ERROR_SUCCESS) {
return nullptr;
diff --git a/src/hotspot/share/c1/c1_Instruction.cpp b/src/hotspot/share/c1/c1_Instruction.cpp
index b2d31e3c1a83..cf3652c95190 100644
--- a/src/hotspot/share/c1/c1_Instruction.cpp
+++ b/src/hotspot/share/c1/c1_Instruction.cpp
@@ -808,6 +808,19 @@ bool BlockBegin::try_merge(ValueStack* new_state, bool has_irreducible_loops) {
}
}
+ for_each_stack_value(existing_state, index, existing_value) {
+ if ( !(existing_value->as_Phi() != NULL && existing_value->as_Phi()->block() == this)) {
+ TRACE_PHI(tty->print_cr("Re-entered loop head without existing phi for stack - bailout"));
+ return false;
+ }
+ }
+ for_each_local_value(existing_state, index, existing_value) {
+ if (!(existing_value == new_state->local_at(index) || (existing_value->as_Phi() != NULL && existing_value->as_Phi()->as_Phi()->block() == this))) {
+ TRACE_PHI(tty->print_cr("Re-entered loop head without existing phi for modified local - bailout"));
+ return false;
+ }
+ }
+
#ifdef ASSERT
// check that all necessary phi functions are present
for_each_stack_value(existing_state, index, existing_value) {
diff --git a/src/hotspot/share/classfile/classLoaderDataGraph.cpp b/src/hotspot/share/classfile/classLoaderDataGraph.cpp
index 2046286651ea..653586704b32 100644
--- a/src/hotspot/share/classfile/classLoaderDataGraph.cpp
+++ b/src/hotspot/share/classfile/classLoaderDataGraph.cpp
@@ -47,6 +47,7 @@
#include "utilities/growableArray.hpp"
#include "utilities/macros.hpp"
#include "utilities/ostream.hpp"
+#include "utilities/vmError.hpp"
volatile size_t ClassLoaderDataGraph::_num_array_classes = 0;
volatile size_t ClassLoaderDataGraph::_num_instance_classes = 0;
@@ -186,7 +187,8 @@ ClassLoaderData* ClassLoaderDataGraph::add(Handle loader, bool has_class_mirror_
inline void assert_is_safepoint_or_gc() {
assert(SafepointSynchronize::is_at_safepoint() ||
Thread::current()->is_ConcurrentGC_thread() ||
- Thread::current()->is_Worker_thread(),
+ Thread::current()->is_Worker_thread() ||
+ VMError::is_error_reported(), // don't crash here again if we have already crashed
"Must be called by safepoint or GC");
}
diff --git a/src/hotspot/share/jfr/periodic/jfrNetworkUtilization.cpp b/src/hotspot/share/jfr/periodic/jfrNetworkUtilization.cpp
index 9d57cddb4480..cedf6b756982 100644
--- a/src/hotspot/share/jfr/periodic/jfrNetworkUtilization.cpp
+++ b/src/hotspot/share/jfr/periodic/jfrNetworkUtilization.cpp
@@ -113,10 +113,12 @@ class JfrNetworkInterfaceName : public JfrSerializer {
void serialize(JfrCheckpointWriter& writer) {} // we write each constant lazily
void on_rotation() {
- for (int i = 0; i < _interfaces->length(); ++i) {
- const InterfaceEntry& entry = _interfaces->at(i);
- if (entry.written) {
- entry.written = false;
+ if (_interfaces != NULL) {
+ for (int i = 0; i < _interfaces->length(); ++i) {
+ const InterfaceEntry &entry = _interfaces->at(i);
+ if (entry.written) {
+ entry.written = false;
+ }
}
}
}
diff --git a/src/hotspot/share/memory/metaspace/runningCounters.cpp b/src/hotspot/share/memory/metaspace/runningCounters.cpp
index 08dffc5c64c5..892f7ed5d27a 100644
--- a/src/hotspot/share/memory/metaspace/runningCounters.cpp
+++ b/src/hotspot/share/memory/metaspace/runningCounters.cpp
@@ -45,7 +45,8 @@ size_t RunningCounters::reserved_words_class() {
}
size_t RunningCounters::reserved_words_nonclass() {
- return VirtualSpaceList::vslist_nonclass()->reserved_words();
+ VirtualSpaceList* vs = VirtualSpaceList::vslist_nonclass();
+ return vs != NULL ? vs->reserved_words() : 0;
}
// Return total committed size, in words, for Metaspace
@@ -59,7 +60,8 @@ size_t RunningCounters::committed_words_class() {
}
size_t RunningCounters::committed_words_nonclass() {
- return VirtualSpaceList::vslist_nonclass()->committed_words();
+ VirtualSpaceList* vs = VirtualSpaceList::vslist_nonclass();
+ return vs != NULL ? vs->vslist_nonclass()->committed_words() : 0;
}
// ---- used chunks -----
diff --git a/src/hotspot/share/memory/universe.cpp b/src/hotspot/share/memory/universe.cpp
index dfeb6b11f6f2..56f51962e37f 100644
--- a/src/hotspot/share/memory/universe.cpp
+++ b/src/hotspot/share/memory/universe.cpp
@@ -86,6 +86,7 @@
#include "utilities/macros.hpp"
#include "utilities/ostream.hpp"
#include "utilities/preserveException.hpp"
+#include "utilities/vmError.hpp"
// Known objects
Klass* Universe::_typeArrayKlassObjs[T_LONG+1] = { nullptr /*, nullptr...*/ };
@@ -839,6 +840,8 @@ jint universe_init() {
ResolvedMethodTable::create_table();
+ VMError::init();
+
return JNI_OK;
}
diff --git a/src/hotspot/share/opto/loopPredicate.cpp b/src/hotspot/share/opto/loopPredicate.cpp
index fd0576832cf7..a74c1a183ba9 100644
--- a/src/hotspot/share/opto/loopPredicate.cpp
+++ b/src/hotspot/share/opto/loopPredicate.cpp
@@ -1223,7 +1223,7 @@ bool PhaseIdealLoop::loop_predication_impl_helper(IdealLoopTree* loop, IfProjNod
Node* idx = cmp->in(1);
assert(!invar.is_invariant(idx), "index is variant");
Node* rng = cmp->in(2);
- assert(rng->Opcode() == Op_LoadRange || iff->is_RangeCheck() || _igvn.type(rng)->is_int()->_lo >= 0, "must be");
+ // [tav: crash] assert(rng->Opcode() == Op_LoadRange || iff->is_RangeCheck() || _igvn.type(rng)->is_int()->_lo >= 0, "must be");
assert(invar.is_invariant(rng), "range must be invariant");
int scale = 1;
Node* offset = zero;
diff --git a/src/hotspot/share/prims/jni.cpp b/src/hotspot/share/prims/jni.cpp
index b6a4443a8c76..e1d07a2c5400 100644
--- a/src/hotspot/share/prims/jni.cpp
+++ b/src/hotspot/share/prims/jni.cpp
@@ -3519,6 +3519,19 @@ _JNI_IMPORT_OR_EXPORT_ jint JNICALL JNI_GetDefaultJavaVMInitArgs(void *args_) {
return ret;
}
+#ifdef LINUX
+static void unsetLdPreload() {
+ // Workaround for: JBR-2253 Preload libjsig.so to fix JNA crashes
+ // NOTE: Probably it still can affect child processes, see
+ // https://stackoverflow.com/questions/3275015/ld-preload-affects-new-child-even-after-unsetenvld-preload
+ char * envUnset = ::getenv("UNSET_LD_PRELOAD");
+ if (envUnset != NULL && strlen(envUnset) > 3 && strncmp("true", envUnset, 4) == 0) {
+ unsetenv("LD_PRELOAD");
+ fprintf(stdout, "LD_PRELOAD was unset\n");
+ }
+}
+#endif //LINUX
+
DT_RETURN_MARK_DECL(CreateJavaVM, jint
, HOTSPOT_JNI_CREATEJAVAVM_RETURN(_ret_ref));
@@ -3625,6 +3638,10 @@ static jint JNI_CreateJavaVM_inner(JavaVM **vm, void **penv, void *args) {
// Since this is not a JVM_ENTRY we have to set the thread state manually before leaving.
ThreadStateTransition::transition_from_vm(thread, _thread_in_native);
MACOS_AARCH64_ONLY(thread->enable_wx(WXExec));
+
+#ifdef LINUX
+ unsetLdPreload();
+#endif //LINUX
} else {
// If create_vm exits because of a pending exception, exit with that
// exception. In the future when we figure out how to reclaim memory,
diff --git a/src/hotspot/share/prims/jvm.cpp b/src/hotspot/share/prims/jvm.cpp
index 4ec0dfa34cc7..25878d1dff5e 100644
--- a/src/hotspot/share/prims/jvm.cpp
+++ b/src/hotspot/share/prims/jvm.cpp
@@ -3417,34 +3417,30 @@ JVM_LEAF(void*, JVM_LoadZipLibrary())
return ZipLibrary::handle();
JVM_END
-JVM_ENTRY_NO_ENV(void*, JVM_LoadLibrary(const char* name, jboolean throwException))
+JVM_ENTRY_NO_ENV(void*, JVM_LoadLibrary(const char* utf8_name, jboolean throwException))
//%note jvm_ct
char ebuf[1024];
void *load_result;
{
ThreadToNativeFromVM ttnfvm(thread);
- load_result = os::dll_load(name, ebuf, sizeof ebuf);
+ load_result = os::dll_load_utf8(utf8_name, ebuf, sizeof ebuf);
}
if (load_result == nullptr) {
if (throwException) {
char msg[1024];
- jio_snprintf(msg, sizeof msg, "%s: %s", name, ebuf);
- // Since 'ebuf' may contain a string encoded using
- // platform encoding scheme, we need to pass
- // Exceptions::unsafe_to_utf8 to the new_exception method
- // as the last argument. See bug 6367357.
+ jio_snprintf(msg, sizeof msg, "%s: %s", utf8_name, ebuf);
Handle h_exception =
Exceptions::new_exception(thread,
vmSymbols::java_lang_UnsatisfiedLinkError(),
- msg, Exceptions::unsafe_to_utf8);
+ msg);
THROW_HANDLE_0(h_exception);
} else {
- log_info(library)("Failed to load library %s", name);
+ log_info(library)("Failed to load library %s", utf8_name);
return load_result;
}
}
- log_info(library)("Loaded library %s, handle " INTPTR_FORMAT, name, p2i(load_result));
+ log_info(library)("Loaded library %s, handle " INTPTR_FORMAT, utf8_name, p2i(load_result));
return load_result;
JVM_END
diff --git a/src/hotspot/share/runtime/abstract_vm_version.cpp b/src/hotspot/share/runtime/abstract_vm_version.cpp
index f1f8888653e4..59983b93c294 100644
--- a/src/hotspot/share/runtime/abstract_vm_version.cpp
+++ b/src/hotspot/share/runtime/abstract_vm_version.cpp
@@ -122,7 +122,7 @@ const char* Abstract_VM_Version::vm_name() {
# define VENDOR_PADDING 64
#endif
#ifndef VENDOR
-# define VENDOR "Oracle Corporation"
+# define VENDOR "JetBrains s.r.o."
#endif
static const char vm_vendor_string[sizeof(VENDOR) < VENDOR_PADDING ? VENDOR_PADDING : sizeof(VENDOR)] = VENDOR;
diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp
index 76877db2903d..d1530d0e738d 100644
--- a/src/hotspot/share/runtime/arguments.cpp
+++ b/src/hotspot/share/runtime/arguments.cpp
@@ -37,6 +37,7 @@
#include "gc/shared/stringdedup/stringDedup.hpp"
#include "gc/shared/tlab_globals.hpp"
#include "jvm.h"
+#include "jfr/periodic/jfrOSInterface.hpp"
#include "logging/log.hpp"
#include "logging/logConfiguration.hpp"
#include "logging/logStream.hpp"
@@ -1291,6 +1292,8 @@ bool Arguments::add_property(const char* prop, PropertyWriteable writeable, Prop
if (old_java_vendor_url_bug != nullptr) {
os::free((void *)old_java_vendor_url_bug);
}
+ } else if (strcmp(key, "jbr.catch.SIGABRT") == 0) {
+ CatchSIGABRT = (strcmp(value, "true") == 0);
}
// Create new property and add at the end of the list
@@ -3918,3 +3921,10 @@ bool Arguments::copy_expand_pid(const char* src, size_t srclen,
*b = '\0';
return (p == src_end); // return false if not all of the source was copied
}
+
+void Arguments::add_virtualization_information_property()
+{
+ const char *virt_name = "undetected";
+ JFR_ONLY(virt_name = JfrOSInterface::virtualization_name();)
+ PropertyList_add(&_system_properties, new SystemProperty("jbr.virtualization.information", virt_name, false));
+}
diff --git a/src/hotspot/share/runtime/arguments.hpp b/src/hotspot/share/runtime/arguments.hpp
index 8251db3d0d59..8f3b8aa3f547 100644
--- a/src/hotspot/share/runtime/arguments.hpp
+++ b/src/hotspot/share/runtime/arguments.hpp
@@ -447,6 +447,8 @@ class Arguments : AllStatic {
_vm_info->set_value(vm_info);
}
+ static void add_virtualization_information_property();
+
// Property List manipulation
static void PropertyList_add(SystemProperty *element);
static void PropertyList_add(SystemProperty** plist, SystemProperty *element);
diff --git a/src/hotspot/share/runtime/globals.cpp b/src/hotspot/share/runtime/globals.cpp
index 66d1e3357086..f471cb4cb5ef 100644
--- a/src/hotspot/share/runtime/globals.cpp
+++ b/src/hotspot/share/runtime/globals.cpp
@@ -45,3 +45,5 @@ ALL_FLAGS(MATERIALIZE_DEVELOPER_FLAG,
MATERIALIZE_NOTPRODUCT_FLAG,
IGNORE_RANGE,
IGNORE_CONSTRAINT)
+
+MATERIALIZE_PRODUCT_FLAG(bool, CatchSIGABRT, false);
diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp
index 16198aca5370..6a553f795c5e 100644
--- a/src/hotspot/share/runtime/globals.hpp
+++ b/src/hotspot/share/runtime/globals.hpp
@@ -2022,4 +2022,6 @@ DECLARE_ARCH_FLAGS(ARCH_FLAGS)
DECLARE_FLAGS(RUNTIME_FLAGS)
DECLARE_FLAGS(RUNTIME_OS_FLAGS)
+DECLARE_PRODUCT_FLAG(bool, CatchSIGABRT, false);
+
#endif // SHARE_RUNTIME_GLOBALS_HPP
diff --git a/src/hotspot/share/runtime/jniHandles.cpp b/src/hotspot/share/runtime/jniHandles.cpp
index 25c8aa8b10b6..f50b491854eb 100644
--- a/src/hotspot/share/runtime/jniHandles.cpp
+++ b/src/hotspot/share/runtime/jniHandles.cpp
@@ -258,6 +258,23 @@ void JNIHandles::print_on(outputStream* st) {
st->flush();
}
+// Called while generating crash log, possibly not at safepoint
+void JNIHandles::print_on_unsafe(outputStream* st) {
+ st->print_cr("JNI global refs: " SIZE_FORMAT ", weak refs: " SIZE_FORMAT,
+ global_handles() != NULL ? global_handles()->allocation_count() : 0,
+ weak_global_handles() != NULL ? weak_global_handles()->allocation_count() : 0);
+ st->cr();
+ st->flush();
+}
+
+void JNIHandles::print_memory_usage_on(outputStream *st) {
+ st->print_cr("JNI global refs memory usage: " SIZE_FORMAT ", weak refs: " SIZE_FORMAT,
+ global_handles() != NULL ? global_handles()->total_memory_usage() : 0,
+ global_handles() != NULL ? weak_global_handles()->total_memory_usage() : 0);
+ st->cr();
+ st->flush();
+}
+
void JNIHandles::print() { print_on(tty); }
class VerifyJNIHandles: public OopClosure {
diff --git a/src/hotspot/share/runtime/jniHandles.hpp b/src/hotspot/share/runtime/jniHandles.hpp
index 230a3f5ca428..66e440cb8f19 100644
--- a/src/hotspot/share/runtime/jniHandles.hpp
+++ b/src/hotspot/share/runtime/jniHandles.hpp
@@ -110,6 +110,8 @@ class JNIHandles : AllStatic {
// Debugging
static void print_on(outputStream* st);
+ static void print_on_unsafe(outputStream* st);
+ static void print_memory_usage_on(outputStream* st);
static void print();
static void verify();
// The category predicates all require handle != nullptr.
diff --git a/src/hotspot/share/runtime/mutexLocker.cpp b/src/hotspot/share/runtime/mutexLocker.cpp
index efd8490cd80e..6948bb745945 100644
--- a/src/hotspot/share/runtime/mutexLocker.cpp
+++ b/src/hotspot/share/runtime/mutexLocker.cpp
@@ -156,6 +156,7 @@ Monitor* JVMCI_lock = nullptr;
Monitor* JVMCIRuntime_lock = nullptr;
#endif
+Mutex* OOMEStacks_lock = nullptr;
#define MAX_NUM_MUTEX 128
static Mutex* _mutex_array[MAX_NUM_MUTEX];
@@ -357,6 +358,8 @@ void mutex_init() {
// JVMCIRuntime_lock must be acquired before JVMCI_lock to avoid deadlock
MUTEX_DEFL(JVMCI_lock , PaddedMonitor, JVMCIRuntime_lock);
#endif
+
+ MUTEX_DEFN(OOMEStacks_lock , PaddedMutex , safepoint-1);
}
#undef MUTEX_DEFL
diff --git a/src/hotspot/share/runtime/mutexLocker.hpp b/src/hotspot/share/runtime/mutexLocker.hpp
index 840d01e62fa0..df6bde0eb265 100644
--- a/src/hotspot/share/runtime/mutexLocker.hpp
+++ b/src/hotspot/share/runtime/mutexLocker.hpp
@@ -155,6 +155,8 @@ extern Mutex* Bootclasspath_lock;
extern Mutex* tty_lock; // lock to synchronize output.
+extern Mutex* OOMEStacks_lock;
+
// A MutexLocker provides mutual exclusion with respect to a given mutex
// for the scope which contains the locker. The lock is an OS lock, not
// an object lock, and the two do not interoperate. Do not use Mutex-based
diff --git a/src/hotspot/share/runtime/os.cpp b/src/hotspot/share/runtime/os.cpp
index f9bd773e370e..dd9d27e853d6 100644
--- a/src/hotspot/share/runtime/os.cpp
+++ b/src/hotspot/share/runtime/os.cpp
@@ -991,6 +991,7 @@ static void print_hex_location(outputStream* st, address p, int unitsize) {
}
}
+ATTRIBUTE_NO_SANITIZE_ADDRESS("Memory is read raw here without any regard for objects' boundaries")
void os::print_hex_dump(outputStream* st, address start, address end, int unitsize,
int bytes_per_line, address logical_start) {
assert(unitsize == 1 || unitsize == 2 || unitsize == 4 || unitsize == 8, "just checking");
diff --git a/src/hotspot/share/runtime/os.hpp b/src/hotspot/share/runtime/os.hpp
index 67e1ef7dcf7b..48ce50b40993 100644
--- a/src/hotspot/share/runtime/os.hpp
+++ b/src/hotspot/share/runtime/os.hpp
@@ -733,6 +733,7 @@ class os: AllStatic {
// same architecture as HotSpot is running on
// in case of an error null is returned and an error message is stored in ebuf
static void* dll_load(const char *name, char *ebuf, int ebuflen);
+ static void* dll_load_utf8(const char *name, char *ebuf, int ebuflen);
// lookup symbol in a shared library
static void* dll_lookup(void* handle, const char* name);
diff --git a/src/hotspot/share/runtime/threads.cpp b/src/hotspot/share/runtime/threads.cpp
index 237e3a22451d..eeac4c9c5e9c 100644
--- a/src/hotspot/share/runtime/threads.cpp
+++ b/src/hotspot/share/runtime/threads.cpp
@@ -583,6 +583,8 @@ jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) {
JFR_ONLY(Jfr::on_create_vm_1();)
+ Arguments::add_virtualization_information_property();
+
// Should be done after the heap is fully created
main_thread->cache_global_variables();
diff --git a/src/hotspot/share/runtime/vmOperations.cpp b/src/hotspot/share/runtime/vmOperations.cpp
index 3469e3f2cc0b..674ecdf97e79 100644
--- a/src/hotspot/share/runtime/vmOperations.cpp
+++ b/src/hotspot/share/runtime/vmOperations.cpp
@@ -25,6 +25,7 @@
#include "precompiled.hpp"
#include "classfile/classLoaderDataGraph.hpp"
#include "classfile/vmSymbols.hpp"
+#include "classfile/symbolTable.hpp"
#include "code/codeCache.hpp"
#include "compiler/compileBroker.hpp"
#include "gc/shared/collectedHeap.hpp"
@@ -37,6 +38,7 @@
#include "memory/resourceArea.hpp"
#include "memory/universe.hpp"
#include "oops/symbol.hpp"
+#include "oops/symbolHandle.hpp"
#include "runtime/arguments.hpp"
#include "runtime/deoptimization.hpp"
#include "runtime/frame.inline.hpp"
@@ -50,7 +52,7 @@
#include "runtime/threadSMR.inline.hpp"
#include "runtime/vmOperations.hpp"
#include "services/threadService.hpp"
-#include "utilities/ticks.hpp"
+#include "javaCalls.hpp"
#define VM_OP_NAME_INITIALIZE(name) #name,
@@ -160,6 +162,37 @@ void VM_ZombieAll::doit() {
#endif // !PRODUCT
+
+// Prints out additional information supplied by the application
+// through the use of JBR API. The data in the form of a String
+// are obtained from Throwable.$$jb$getAdditionalInfoForJstack()
+// and, not not null, are included into the output.
+void VM_PrintThreads::print_additional_info() {
+ JavaThread *THREAD = JavaThread::current();
+ HandleMark hm(THREAD);
+ ResourceMark rm;
+
+ InstanceKlass *klass = vmClasses::Throwable_klass();
+ if (klass != NULL) {
+ TempNewSymbol method_name = SymbolTable::new_symbol("$$jb$getAdditionalInfoForJstack");
+ Symbol *signature = vmSymbols::void_string_signature();
+ Method *method = klass->find_method(method_name, signature);
+ if (method != NULL) {
+ JavaValue result(T_OBJECT);
+ JavaCalls::call_static(&result, klass,
+ method_name, signature, THREAD);
+ oop dump_oop = result.get_oop();
+ if (dump_oop != NULL) {
+ // convert Java String to utf8 string
+ char *s = java_lang_String::as_utf8_string(dump_oop);
+ _out->cr();
+ _out->print_raw_cr(s);
+ _out->cr();
+ }
+ }
+ }
+}
+
bool VM_PrintThreads::doit_prologue() {
// Get Heap_lock if concurrent locks will be dumped
if (_print_concurrent_locks) {
@@ -172,6 +205,7 @@ void VM_PrintThreads::doit() {
Threads::print_on(_out, true, false, _print_concurrent_locks, _print_extended_info);
if (_print_jni_handle_info) {
JNIHandles::print_on(_out);
+ JNIHandles::print_memory_usage_on(_out);
}
}
@@ -180,6 +214,13 @@ void VM_PrintThreads::doit_epilogue() {
// Release Heap_lock
Heap_lock->unlock();
}
+
+ // We should be on the "signal handler" thread, which is a JavaThread
+ if (Thread::current()->is_Java_thread()) {
+ // .. but best play it safe as we're going to need to make
+ // Java calls on the current thread.
+ print_additional_info();
+ }
}
void VM_PrintMetadata::doit() {
diff --git a/src/hotspot/share/runtime/vmOperations.hpp b/src/hotspot/share/runtime/vmOperations.hpp
index 8778fe29adea..620b97933581 100644
--- a/src/hotspot/share/runtime/vmOperations.hpp
+++ b/src/hotspot/share/runtime/vmOperations.hpp
@@ -154,6 +154,9 @@ class VM_PrintThreads: public VM_Operation {
void doit();
bool doit_prologue();
void doit_epilogue();
+
+private:
+ void print_additional_info();
};
class VM_PrintMetadata : public VM_Operation {
diff --git a/src/hotspot/share/utilities/compilerWarnings.hpp b/src/hotspot/share/utilities/compilerWarnings.hpp
index 28954843dc32..a0ca28ae117b 100644
--- a/src/hotspot/share/utilities/compilerWarnings.hpp
+++ b/src/hotspot/share/utilities/compilerWarnings.hpp
@@ -103,4 +103,10 @@
#define ALLOW_C_FUNCTION(name, ...) __VA_ARGS__
#endif
+#if defined(__clang__) || defined (__GNUC__)
+# define ATTRIBUTE_NO_SANITIZE_ADDRESS(reason_for_exclusion) __attribute__((no_sanitize_address))
+#else
+# define ATTRIBUTE_NO_SANITIZE_ADDRESS(reason_for_exclusion)
+#endif
+
#endif // SHARE_UTILITIES_COMPILERWARNINGS_HPP
diff --git a/src/hotspot/share/utilities/debug.cpp b/src/hotspot/share/utilities/debug.cpp
index d39335c6307f..3991de53bce8 100644
--- a/src/hotspot/share/utilities/debug.cpp
+++ b/src/hotspot/share/utilities/debug.cpp
@@ -251,6 +251,8 @@ void report_untested(const char* file, int line, const char* message) {
void report_java_out_of_memory(const char* message) {
static int out_of_memory_reported = 0;
+ VMError::record_oome_stack(message);
+
// A number of threads may attempt to report OutOfMemoryError at around the
// same time. To avoid dumping the heap or executing the data collection
// commands multiple times we just do it once when the first threads reports
diff --git a/src/hotspot/share/utilities/globalDefinitions_visCPP.hpp b/src/hotspot/share/utilities/globalDefinitions_visCPP.hpp
index 3b28328d7dd4..538acd028929 100644
--- a/src/hotspot/share/utilities/globalDefinitions_visCPP.hpp
+++ b/src/hotspot/share/utilities/globalDefinitions_visCPP.hpp
@@ -101,6 +101,11 @@ inline int g_isnan(jdouble f) { return _isnan(f); }
inline int g_isfinite(jfloat f) { return _finite(f); }
inline int g_isfinite(jdouble f) { return _finite(f); }
+// Miscellaneous
+
+// Visual Studio 2005 deprecates POSIX names - use ISO C++ names instead
+#define wopen _wopen
+
// Formatting.
#define FORMAT64_MODIFIER "ll"
diff --git a/src/hotspot/share/utilities/vmError.cpp b/src/hotspot/share/utilities/vmError.cpp
index d40504d6d200..48feef76a7f3 100644
--- a/src/hotspot/share/utilities/vmError.cpp
+++ b/src/hotspot/share/utilities/vmError.cpp
@@ -26,6 +26,7 @@
#include "precompiled.hpp"
#include "cds/metaspaceShared.hpp"
+#include "classfile/classLoaderDataGraph.hpp"
#include "code/codeCache.hpp"
#include "compiler/compilationFailureInfo.hpp"
#include "compiler/compileBroker.hpp"
@@ -48,6 +49,7 @@
#include "runtime/frame.inline.hpp"
#include "runtime/init.hpp"
#include "runtime/javaThread.inline.hpp"
+#include "runtime/jniHandles.hpp"
#include "runtime/os.inline.hpp"
#include "runtime/osThread.hpp"
#include "runtime/safefetch.hpp"
@@ -656,6 +658,7 @@ void VMError::clear_step_start_time() {
// could be nested report_and_die() calls on stack (see above). Only one
// thread can report error, so large buffers are statically allocated in data
// segment.
+ATTRIBUTE_NO_SANITIZE_ADDRESS("Memory is read raw here without any regard for objects' boundaries")
void VMError::report(outputStream* st, bool _verbose) {
// Used by reattempt step logic
static int continuation = 0;
@@ -1316,6 +1319,22 @@ void VMError::report(outputStream* st, bool _verbose) {
NativeHeapTrimmer::print_state(st);
st->cr();
+ STEP("JNI global references")
+ st->print_cr("JNI global refs:");
+ JNIHandles::print_on_unsafe(st);
+ JNIHandles::print_memory_usage_on(st);
+
+ STEP("OOME stack traces")
+ st->print_cr("OOME stack traces (most recent first):");
+ print_oome_stacks(st);
+
+ STEP("Classloader stats")
+ st->print_cr("Classloader memory used:");
+ FREE_C_HEAP_ARRAY(void*, _ballast_memory);
+ _ballast_memory = nullptr;
+ print_classloaders_stats(st);
+ print_dup_classes(st); // do it separately in case we're low on memory
+
STEP_IF("printing system", _verbose)
st->print_cr("--------------- S Y S T E M ---------------");
st->cr();
@@ -2161,3 +2180,176 @@ VMErrorCallbackMark::~VMErrorCallbackMark() {
assert(_thread->_vm_error_callbacks != nullptr, "Popped too far");
_thread->_vm_error_callbacks = _thread->_vm_error_callbacks->_next;
}
+
+char VMError::_oome_stacktrace[OOME_STACKTRACE_COUNT][OOME_STACKTRACE_BUFSIZE];
+int VMError::_oome_free_index;
+void * VMError::_ballast_memory;
+
+void VMError::init() {
+ _oome_free_index = 0;
+ // This will be released before dumping classes statistics as the latter
+ // may need to allocate some memory in an OOME situation.
+ _ballast_memory = NEW_C_HEAP_ARRAY(void*, 36000*4, mtStatistics);
+}
+
+void VMError::record_oome_stack(const char *message) {
+ Thread * thread = Thread::current();
+ if (!thread->is_Java_thread()) {
+ // The following code can't work in non-Java thread.
+ // Besides, it's not a true OOM *exception* if there's no Java on the stack.
+ return;
+ }
+
+ {
+ MutexLocker ml(OOMEStacks_lock);
+ ResourceMark rm;
+
+ stringStream st(_oome_stacktrace[_oome_free_index], OOME_STACKTRACE_BUFSIZE);
+ st.print_cr("OutOfMemoryError(\"%s\") on the thread \"%s\"", message, thread->name());
+ JavaThread::cast(thread)->print_stack_on(&st);
+
+ st.cr();
+
+ _oome_free_index = (_oome_free_index + 1) % OOME_STACKTRACE_COUNT;
+ }
+}
+
+void VMError::print_oome_stacks(outputStream *st) {
+ int index = _oome_free_index == 0 ? OOME_STACKTRACE_COUNT - 1 : _oome_free_index - 1;
+
+ for (int i = 0; i < OOME_STACKTRACE_COUNT; i++) {
+ const char * const trace = _oome_stacktrace[index];
+ if (strcmp(trace, "") != 0) st->print_raw_cr(trace);
+ index = index == 0 ? OOME_STACKTRACE_COUNT - 1 : index - 1;
+ }
+}
+
+class CLDCounterClosure : public CLDClosure {
+public:
+ // Loader Klass -> memory used in words
+ using TABLE = ResizeableResourceHashtable;
+
+private:
+ TABLE& _loaded_size;
+
+public:
+ explicit CLDCounterClosure(TABLE& loaded_size) : _loaded_size(loaded_size) {}
+
+ void do_cld(ClassLoaderData* cld) override {
+ Klass * const loader = cld->class_loader_klass();
+ ClassLoaderMetaspace * const ms = cld->metaspace_or_null();
+ if (ms != nullptr) {
+ size_t used_words = 0;
+ ms->usage_numbers(&used_words, nullptr, nullptr);
+
+ const size_t * const new_size_ptr = _loaded_size.get(loader);
+ size_t new_size = new_size_ptr == nullptr
+ ? used_words
+ : *new_size_ptr + used_words;
+ _loaded_size.put(loader, new_size);
+ }
+ }
+};
+
+static int compare_by_size(Pair* p1, Pair* p2) {
+ return p2->second == p1->second
+ ? 0
+ : p2->second > p1->second ? 1 : -1;
+}
+
+void VMError::print_classloaders_stats(outputStream *st) {
+ if (_thread) {
+ ResourceMark rm(_thread);
+ MutexLocker ml(_thread, ClassLoaderDataGraph_lock);
+
+ CLDCounterClosure::TABLE sizes(10, 100);
+ CLDCounterClosure counter(sizes);
+ ClassLoaderDataGraph::cld_do(&counter);
+ GrowableArray> loaders_stats;
+ sizes.iterate_all([&] (Klass* loader_klass, size_t total_words) {
+ loaders_stats.push(Pair(loader_klass, total_words));
+ });
+
+ loaders_stats.sort(compare_by_size);
+
+ for (const auto loaders_stat : loaders_stats) {
+ Klass * const loader_klass = loaders_stat.first;
+ const size_t total_words = loaders_stat.second;
+ const char *name = loader_klass != nullptr
+ ? loader_klass->external_name()
+ : BOOTSTRAP_LOADER_NAME;
+ const size_t total_bytes = total_words * BytesPerWord;
+ st->print_cr("Loader %-80s: " SIZE_FORMAT "%s",
+ name,
+ byte_size_in_proper_unit(total_bytes),
+ proper_unit_for_byte_size(total_bytes));
+ }
+
+ st->cr();
+ } else {
+ st->print_cr("Not available (crashed in non-Java thread)");
+ }
+}
+
+class DuplicateKlassClosure : public KlassClosure {
+ static bool klass_equals(Klass* const & klass1, Klass* const & klass2) {
+ return strcmp(klass1->external_name(), klass2->external_name()) == 0;
+ }
+
+ static unsigned klass_hash(Klass* const & k) {
+ int h = 0;
+ const char* p = k->external_name();
+ while (*p != '\0') {
+ h = 31 * h + *p;
+ p++;
+ }
+ return h;
+ }
+
+public:
+ // Klass -> number times loaded
+ using TABLE = ResizeableResourceHashtable;
+
+private:
+ TABLE& _classes_count;
+
+public:
+ explicit DuplicateKlassClosure(TABLE& classes_count) : _classes_count(classes_count) {}
+
+ void do_klass(Klass* klass) override {
+ const size_t * const new_count_ptr = _classes_count.get(klass);
+ const size_t new_count = new_count_ptr != nullptr ? *new_count_ptr + 1 : 1;
+ _classes_count.put(klass, new_count);
+ }
+};
+
+void VMError::print_dup_classes(outputStream *st) {
+ if (_thread) {
+ ResourceMark rm(_thread);
+ MutexLocker ma(_thread, MultiArray_lock);
+ MutexLocker mcld(_thread, ClassLoaderDataGraph_lock);
+
+ DuplicateKlassClosure::TABLE classes_count(100, 200);
+ DuplicateKlassClosure counter(classes_count);
+ ClassLoaderDataGraph::loaded_classes_do(&counter);
+
+ GrowableArray> dup_classes;
+ classes_count.iterate_all([&] (Klass* const name, size_t count) {
+ if (count > 1) dup_classes.push(Pair(name, count));
+ });
+
+ if (dup_classes.length() > 0) {
+ st->print_cr("Classes loaded by more than one classloader:");
+ dup_classes.sort(compare_by_size);
+ for (const auto dup : dup_classes) {
+ const char * const name = dup.first->external_name();
+ const size_t size = dup.first->size();
+ const size_t count = dup.second;
+ st->print_cr("Class %-80s: loaded " SIZE_FORMAT " times (x " SIZE_FORMAT "B)",
+ name, count, size);
+ }
+ st->cr();
+ }
+ }
+}
diff --git a/src/hotspot/share/utilities/vmError.hpp b/src/hotspot/share/utilities/vmError.hpp
index a2a098e2cda5..1228ed51f166 100644
--- a/src/hotspot/share/utilities/vmError.hpp
+++ b/src/hotspot/share/utilities/vmError.hpp
@@ -69,6 +69,12 @@ class VMError : public AllStatic {
// used for reattempt step logic
static const size_t _reattempt_required_stack_headroom;
+ static const int OOME_STACKTRACE_BUFSIZE = 16*K;
+ static const int OOME_STACKTRACE_COUNT = 4;
+ static char _oome_stacktrace[OOME_STACKTRACE_COUNT][OOME_STACKTRACE_BUFSIZE];
+ static int _oome_free_index;
+ static void * _ballast_memory;
+
// Thread id of the first error. We must be able to handle native thread,
// so use thread id instead of Thread* to identify thread.
static volatile intptr_t _first_error_tid;
@@ -225,6 +231,11 @@ class VMError : public AllStatic {
// permissions.
static int prepare_log_file(const char* pattern, const char* default_pattern, bool overwrite_existing, char* buf, size_t buflen);
+ static void init();
+ static void record_oome_stack(const char *message);
+ static void print_oome_stacks(outputStream *st);
+ static void print_classloaders_stats(outputStream *st);
+ static void print_dup_classes(outputStream *st);
};
class VMErrorCallback {
diff --git a/src/java.base/macosx/classes/sun/nio/fs/BsdFileSystem.java b/src/java.base/macosx/classes/sun/nio/fs/BsdFileSystem.java
index 3b99a456288c..92889ba62cdf 100644
--- a/src/java.base/macosx/classes/sun/nio/fs/BsdFileSystem.java
+++ b/src/java.base/macosx/classes/sun/nio/fs/BsdFileSystem.java
@@ -54,8 +54,8 @@ class BsdFileSystem extends UnixFileSystem {
public WatchService newWatchService()
throws IOException
{
- // use polling implementation until we implement a BSD/kqueue one
- return new PollingWatchService();
+ final boolean usePollingWatchService = Boolean.getBoolean("watch.service.polling");
+ return usePollingWatchService ? new PollingWatchService() : new MacOSXWatchService();
}
// lazy initialization of the list of supported attribute views
diff --git a/src/java.base/macosx/classes/sun/nio/fs/MacOSXWatchService.java b/src/java.base/macosx/classes/sun/nio/fs/MacOSXWatchService.java
new file mode 100644
index 000000000000..1b00d07c3885
--- /dev/null
+++ b/src/java.base/macosx/classes/sun/nio/fs/MacOSXWatchService.java
@@ -0,0 +1,827 @@
+/*
+ * Copyright (c) 2021, 2022, JetBrains s.r.o.. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.nio.fs;
+
+import sun.util.logging.PlatformLogger;
+
+import jdk.internal.misc.Unsafe;
+
+import java.io.IOException;
+import java.nio.file.ClosedWatchServiceException;
+import java.nio.file.DirectoryIteratorException;
+import java.nio.file.DirectoryStream;
+import java.nio.file.Files;
+import java.nio.file.LinkOption;
+import java.nio.file.NotDirectoryException;
+import java.nio.file.Path;
+import java.nio.file.StandardWatchEventKinds;
+import java.nio.file.WatchEvent;
+import java.nio.file.WatchKey;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.ArrayDeque;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.Queue;
+import java.util.Set;
+import java.util.function.Consumer;
+
+class MacOSXWatchService extends AbstractWatchService {
+ // Controls tracing in the native part of the watcher.
+ private static boolean tracingEnabled = false;
+ private static final PlatformLogger logger = PlatformLogger.getLogger("sun.nio.fs.MacOSXWatchService");
+
+ private final HashMap dirKeyToWatchKey = new HashMap<>();
+ private final HashMap eventStreamToWatchKey = new HashMap<>();
+ private final Object watchKeysLock = new Object();
+
+ private final CFRunLoopThread runLoopThread;
+
+ MacOSXWatchService() throws IOException {
+ runLoopThread = new CFRunLoopThread();
+ runLoopThread.setDaemon(true);
+ runLoopThread.start();
+
+ try {
+ // In order to be able to schedule any FSEventStream's, a reference to a run loop is required.
+ runLoopThread.waitForRunLoopRef();
+ } catch (InterruptedException e) {
+ throw new IOException(e);
+ }
+ }
+
+ @Override
+ WatchKey register(Path dir, WatchEvent.Kind>[] events, WatchEvent.Modifier... modifiers) throws IOException {
+ checkIsOpen();
+
+ final UnixPath unixDir = (UnixPath)dir;
+ final Object dirKey = checkPath(unixDir);
+ final EnumSet eventSet = FSEventKind.setOf(events);
+ final EnumSet modifierSet = WatchModifier.setOf(modifiers);
+ synchronized (closeLock()) {
+ checkIsOpen();
+
+ if (logger.isLoggable(PlatformLogger.Level.FINEST))
+ logger.finest("register for " + dir);
+
+ synchronized (watchKeysLock) {
+ MacOSXWatchKey watchKey = dirKeyToWatchKey.get(dirKey);
+ final boolean keyForDirAlreadyExists = (watchKey != null);
+ if (keyForDirAlreadyExists) {
+ eventStreamToWatchKey.remove(watchKey.getEventStreamRef());
+ watchKey.disable();
+ if (logger.isLoggable(PlatformLogger.Level.FINEST))
+ logger.finest("re-used existing watch key");
+ } else {
+ watchKey = new MacOSXWatchKey(this, unixDir, dirKey);
+ dirKeyToWatchKey.put(dirKey, watchKey);
+ if (logger.isLoggable(PlatformLogger.Level.FINEST))
+ logger.finest("created a new watch key");
+ }
+ if (logger.isLoggable(PlatformLogger.Level.FINEST))
+ logger.finest("starting to [re-]populate directory cache with data");
+ watchKey.enable(runLoopThread, eventSet, modifierSet);
+ eventStreamToWatchKey.put(watchKey.getEventStreamRef(), watchKey);
+ watchKeysLock.notify(); // So that run loop gets running again if stopped due to lack of event streams
+ return watchKey;
+ }
+ }
+ }
+
+ /**
+ * Invoked on the CFRunLoopThread by the native code to report directories that need to be re-scanned.
+ */
+ private void callback(final long eventStreamRef, final String[] paths, final long eventFlagsPtr) {
+ synchronized (watchKeysLock) {
+ final MacOSXWatchKey watchKey = eventStreamToWatchKey.get(eventStreamRef);
+ if (watchKey != null) {
+ if (logger.isLoggable(PlatformLogger.Level.FINEST))
+ logger.finest("Callback fired for '" + watchKey.getRealRootPath() + "'");
+ watchKey.handleEvents(paths, eventFlagsPtr);
+ } else {
+ if (logger.isLoggable(PlatformLogger.Level.FINEST))
+ logger.finest("Callback fired for watch key that is no longer there");
+ }
+ }
+ }
+
+ void cancel(final MacOSXWatchKey watchKey) {
+ synchronized (watchKeysLock) {
+ dirKeyToWatchKey.remove(watchKey.getRootPathKey());
+ eventStreamToWatchKey.remove(watchKey.getEventStreamRef());
+ }
+ }
+
+ void waitForEventSource() {
+ synchronized (watchKeysLock) {
+ if (isOpen() && eventStreamToWatchKey.isEmpty()) {
+ try {
+ watchKeysLock.wait();
+ } catch (InterruptedException ignore) {}
+ }
+ }
+ }
+
+ @Override
+ void implClose() {
+ synchronized (watchKeysLock) {
+ eventStreamToWatchKey.clear();
+ dirKeyToWatchKey.forEach((key, watchKey) -> watchKey.invalidate());
+ dirKeyToWatchKey.clear();
+ watchKeysLock.notify(); // Let waitForEventSource() go if it was waiting
+ runLoopThread.runLoopStop(); // Force exit from CFRunLoopRun()
+ }
+ }
+
+ private static void traceLine(final String text) {
+ logger.finest("NATIVE trace: " + text);
+ }
+
+ private class CFRunLoopThread extends Thread {
+ // Native reference to the CFRunLoop object of the watch service run loop.
+ private long runLoopRef;
+
+ public CFRunLoopThread() {
+ super("FileSystemWatcher");
+ }
+
+ synchronized void waitForRunLoopRef() throws InterruptedException {
+ if (runLoopRef == 0)
+ runLoopThread.wait(); // ...for CFRunLoopRef to become available
+ }
+
+ long getRunLoopRef() {
+ return runLoopRef;
+ }
+
+ synchronized void runLoopStop() {
+ if (runLoopRef != 0) {
+ // The run loop may have stuck in CFRunLoopRun() even though all of its input sources
+ // have been removed. Need to terminate it explicitly so that it can run to completion.
+ MacOSXWatchService.CFRunLoopStop(runLoopRef);
+ }
+ }
+
+ @Override
+ public void run() {
+ synchronized (this) {
+ runLoopRef = CFRunLoopGetCurrent();
+ notify();
+ }
+
+ while (isOpen()) {
+ CFRunLoopRun(MacOSXWatchService.this);
+ waitForEventSource();
+ }
+
+ synchronized (this) {
+ runLoopRef = 0; // CFRunLoopRef is no longer usable when the loop has been terminated
+ }
+ }
+ }
+
+ private void checkIsOpen() {
+ if (!isOpen())
+ throw new ClosedWatchServiceException();
+ }
+
+ private Object checkPath(UnixPath dir) throws IOException {
+ if (dir == null)
+ throw new NullPointerException("No path to watch");
+
+ UnixFileAttributes attrs;
+ try {
+ attrs = UnixFileAttributes.get(dir, true);
+ } catch (UnixException x) {
+ throw x.asIOException(dir);
+ }
+
+ if (!attrs.isDirectory())
+ throw new NotDirectoryException(dir.getPathForExceptionMessage());
+
+ final Object fileKey = attrs.fileKey();
+ if (fileKey == null)
+ throw new AssertionError("File keys must be supported");
+
+ return fileKey;
+ }
+
+ private enum FSEventKind {
+ CREATE, MODIFY, DELETE, OVERFLOW;
+
+ public static FSEventKind of(final WatchEvent.Kind> watchEventKind) {
+ if (StandardWatchEventKinds.ENTRY_CREATE == watchEventKind) {
+ return CREATE;
+ } else if (StandardWatchEventKinds.ENTRY_MODIFY == watchEventKind) {
+ return MODIFY;
+ } else if (StandardWatchEventKinds.ENTRY_DELETE == watchEventKind) {
+ return DELETE;
+ } else if (StandardWatchEventKinds.OVERFLOW == watchEventKind) {
+ return OVERFLOW;
+ } else {
+ throw new UnsupportedOperationException(watchEventKind.name());
+ }
+ }
+
+ public static EnumSet setOf(final WatchEvent.Kind>[] events) {
+ final EnumSet eventSet = EnumSet.noneOf(FSEventKind.class);
+ for (final WatchEvent.Kind> event: events) {
+ if (event == null) {
+ throw new NullPointerException("An element in event set is 'null'");
+ } else if (event == StandardWatchEventKinds.OVERFLOW) {
+ continue;
+ }
+
+ eventSet.add(FSEventKind.of(event));
+ }
+
+ if (eventSet.isEmpty())
+ throw new IllegalArgumentException("No events to register");
+
+ return eventSet;
+ }
+
+ }
+
+ private enum WatchModifier {
+ FILE_TREE, SENSITIVITY_HIGH, SENSITIVITY_MEDIUM, SENSITIVITY_LOW;
+
+ public static WatchModifier of(final WatchEvent.Modifier watchEventModifier) {
+ if (ExtendedOptions.FILE_TREE.matches(watchEventModifier)) {
+ return FILE_TREE;
+ } if (ExtendedOptions.SENSITIVITY_HIGH.matches(watchEventModifier)) {
+ return SENSITIVITY_HIGH;
+ } if (ExtendedOptions.SENSITIVITY_MEDIUM.matches(watchEventModifier)) {
+ return SENSITIVITY_MEDIUM;
+ } if (ExtendedOptions.SENSITIVITY_LOW.matches(watchEventModifier)) {
+ return SENSITIVITY_LOW;
+ } else {
+ throw new UnsupportedOperationException(watchEventModifier.name());
+ }
+ }
+
+ public static EnumSet setOf(final WatchEvent.Modifier[] modifiers) {
+ final EnumSet modifierSet = EnumSet.noneOf(WatchModifier.class);
+ for (final WatchEvent.Modifier modifier : modifiers) {
+ if (modifier == null)
+ throw new NullPointerException("An element in modifier set is 'null'");
+
+ modifierSet.add(WatchModifier.of(modifier));
+ }
+
+ return modifierSet;
+ }
+
+ public static double sensitivityOf(final EnumSet modifiers) {
+ if (modifiers.contains(SENSITIVITY_HIGH)) {
+ return 0.1;
+ } else if (modifiers.contains(SENSITIVITY_LOW)) {
+ return 1;
+ } else {
+ return 0.5; // aka SENSITIVITY_MEDIUM
+ }
+ }
+ }
+
+ private static class MacOSXWatchKey extends AbstractWatchKey {
+ private static final Unsafe unsafe = Unsafe.getUnsafe();
+
+ private static final long kFSEventStreamEventFlagMustScanSubDirs = 0x00000001;
+ private static final long kFSEventStreamEventFlagRootChanged = 0x00000020;
+
+ private final Path relativeRootPath;
+
+ // Full path to this key's watch root directory.
+ private final Path realRootPath;
+ private final int realRootPathLength;
+ private final Object rootPathKey;
+
+ // Kinds of events to be reported.
+ private EnumSet eventsToWatch;
+
+ // Should events in directories below realRootPath reported?
+ private boolean watchFileTree;
+
+ // Native FSEventStreamRef as returned by FSEventStreamCreate().
+ private long eventStreamRef;
+ private final Object eventStreamRefLock = new Object();
+
+ private final DirectoryTreeSnapshot directoryTreeSnapshot = new DirectoryTreeSnapshot();
+
+ MacOSXWatchKey(final MacOSXWatchService watchService, final UnixPath dir, final Object rootPathKey) throws IOException {
+ super(dir, watchService);
+ this.relativeRootPath = dir.getFileSystem().getPath("");
+ this.realRootPath = dir.toRealPath().normalize();
+ this.realRootPathLength = realRootPath.toString().length() + 1;
+ this.rootPathKey = rootPathKey;
+ }
+
+ synchronized void enable(final CFRunLoopThread runLoopThread,
+ final EnumSet eventsToWatch,
+ final EnumSet modifierSet) throws IOException {
+ assert(!isValid());
+
+ this.eventsToWatch = eventsToWatch;
+ this.watchFileTree = modifierSet.contains(WatchModifier.FILE_TREE);
+
+ directoryTreeSnapshot.build();
+
+ synchronized (eventStreamRefLock) {
+ final int kFSEventStreamCreateFlagWatchRoot = 0x00000004;
+ eventStreamRef = MacOSXWatchService.eventStreamCreate(
+ realRootPath.toString(),
+ WatchModifier.sensitivityOf(modifierSet),
+ kFSEventStreamCreateFlagWatchRoot);
+
+ if (eventStreamRef == 0)
+ throw new IOException("Unable to create FSEventStream");
+
+ MacOSXWatchService.eventStreamSchedule(eventStreamRef, runLoopThread.getRunLoopRef());
+ }
+ }
+
+ synchronized void disable() {
+ invalidate();
+ directoryTreeSnapshot.reset();
+ }
+
+ synchronized void handleEvents(final String[] paths, long eventFlagsPtr) {
+ if (paths == null) {
+ reportOverflow(null);
+ return;
+ }
+
+ if (logger.isLoggable(PlatformLogger.Level.FINEST))
+ logger.finest("handleEvents(): will handle " + paths.length + " events");
+
+ final Set dirsToScan = new LinkedHashSet<>(paths.length);
+ final Set dirsToScanRecursively = new LinkedHashSet<>();
+ collectDirsToScan(paths, eventFlagsPtr, dirsToScan, dirsToScanRecursively);
+
+ for (final Path recurseDir : dirsToScanRecursively) {
+ if (logger.isLoggable(PlatformLogger.Level.FINEST))
+ logger.finest("handleEvents(): scanning directory recursively " + recurseDir);
+ dirsToScan.removeIf(dir -> dir.startsWith(recurseDir));
+ assert(watchFileTree);
+ directoryTreeSnapshot.update(recurseDir, true);
+ }
+
+ for (final Path dir : dirsToScan) {
+ if (logger.isLoggable(PlatformLogger.Level.FINEST))
+ logger.finest("handleEvents(): scanning directory " + dir);
+ directoryTreeSnapshot.update(dir, false);
+ }
+ }
+
+ private Path toRelativePath(final String absPath) {
+ return (absPath.length() > realRootPathLength)
+ ? relativeRootPath.getFileSystem().getPath(absPath.substring(realRootPathLength))
+ : relativeRootPath;
+ }
+
+ private void collectDirsToScan(final String[] paths, long eventFlagsPtr,
+ final Set dirsToScan,
+ final Set dirsToScanRecursively) {
+ for (final String absPath : paths) {
+ if (absPath == null) {
+ reportOverflow(null);
+ continue;
+ }
+
+ Path path = toRelativePath(absPath);
+
+ if (logger.isLoggable(PlatformLogger.Level.FINEST))
+ logger.finest("handleEvents(): event path name " + path);
+
+ if (!watchFileTree && !relativeRootPath.equals(path)) {
+ if (logger.isLoggable(PlatformLogger.Level.FINEST))
+ logger.finest("handleEvents(): skipping event for a nested directory");
+ continue;
+ }
+
+ final int flags = unsafe.getInt(eventFlagsPtr);
+ if ((flags & kFSEventStreamEventFlagRootChanged) != 0) {
+ if (logger.isLoggable(PlatformLogger.Level.FINEST))
+ logger.finest("handleEvents(): watch root changed, path=" + path);
+ cancel();
+ signal();
+ break;
+ } else if ((flags & kFSEventStreamEventFlagMustScanSubDirs) != 0 && watchFileTree) {
+ dirsToScanRecursively.add(path);
+ } else {
+ dirsToScan.add(path);
+ }
+
+ final long SIZEOF_FS_EVENT_STREAM_EVENT_FLAGS = 4L; // FSEventStreamEventFlags is UInt32
+ eventFlagsPtr += SIZEOF_FS_EVENT_STREAM_EVENT_FLAGS;
+ }
+ }
+
+ /**
+ * Represents a snapshot of a directory tree.
+ * The snapshot includes subdirectories iff watchFileTree is true.
+ */
+ private class DirectoryTreeSnapshot {
+ private final HashMap snapshots;
+
+ DirectoryTreeSnapshot() {
+ this.snapshots = new HashMap<>(watchFileTree ? 256 : 1);
+ }
+
+ void build() throws IOException {
+ final Queue pathToDo = new ArrayDeque<>();
+ pathToDo.offer(relativeRootPath);
+
+ while (!pathToDo.isEmpty()) {
+ final Path path = pathToDo.poll();
+ try {
+ createForOneDirectory(path, watchFileTree ? pathToDo : null);
+ } catch (IOException e) {
+ final boolean exceptionForRootPath = relativeRootPath.equals(path);
+ if (exceptionForRootPath)
+ throw e; // report to the user as the watch root may have disappeared
+
+ // Ignore for sub-directories as some may have been removed during the scan.
+ // That's OK, those kinds of changes in the directory hierarchy is what
+ // WatchService is used for. However, it's impossible to catch all changes
+ // at this point, so we may fail to report some events that had occurred before
+ // FSEventStream has been created to watch for those changes.
+ }
+ }
+ }
+
+ private DirectorySnapshot createForOneDirectory(
+ final Path directory,
+ final Queue newDirectoriesFound) throws IOException {
+ if (logger.isLoggable(PlatformLogger.Level.FINEST))
+ logger.finest("Creating snapshot for one directory " + directory);
+
+ final DirectorySnapshot snapshot = DirectorySnapshot.create(getRealRootPath(), directory);
+ snapshots.put(directory, snapshot);
+ if (newDirectoriesFound != null)
+ snapshot.forEachDirectory(newDirectoriesFound::offer);
+
+ return snapshot;
+ }
+
+ void reset() {
+ snapshots.clear();
+ }
+
+ void update(final Path directory, final boolean recurse) {
+ if (!recurse) {
+ directoryTreeSnapshot.update(directory, null);
+ } else {
+ final Queue pathToDo = new ArrayDeque<>();
+ pathToDo.offer(directory);
+ while (!pathToDo.isEmpty()) {
+ final Path dir = pathToDo.poll();
+ directoryTreeSnapshot.update(dir, pathToDo);
+ }
+ }
+ }
+
+ private void update(final Path directory, final Queue modifiedDirs) {
+ if (logger.isLoggable(PlatformLogger.Level.FINEST))
+ logger.finest("update for " + directory);
+ final DirectorySnapshot snapshot = snapshots.get(directory);
+ if (snapshot == null) {
+ if (logger.isLoggable(PlatformLogger.Level.FINEST))
+ logger.finest("no snapshot for directory " + directory);
+ // This means that we missed a notification about an update of our parent.
+ // Report overflow (who knows what else we weren't notified about?) and
+ // do our best to recover from this mess by queueing our parent for an update.
+ reportOverflow(directory);
+ if (modifiedDirs != null)
+ modifiedDirs.offer(getParentOf(directory));
+
+ return;
+ }
+
+ // FSEvents API does not generate events for directories that got moved from/to the directory
+ // being watched, so we have to watch for new/deleted directories ourselves. If we still
+ // receive an event for, say, one of the new directories, it won't be reported again as this
+ // will count as refresh with no modifications detected.
+ final Queue createdDirs = new ArrayDeque<>();
+ final Queue deletedDirs = new ArrayDeque<>();
+ snapshot.update(MacOSXWatchKey.this, createdDirs, deletedDirs, modifiedDirs);
+
+ handleNewDirectories(createdDirs);
+ handleDeletedDirectories(deletedDirs);
+ }
+
+ private Path getParentOf(final Path directory) {
+ Path parent = directory.getParent();
+ if (parent == null)
+ parent = relativeRootPath;
+ return parent;
+ }
+
+ private void handleDeletedDirectories(final Queue deletedDirs) {
+ // We don't know the exact sequence in which these were deleted,
+ // so at least maintain a sensible order, i.e. children are deleted before the parent.
+ final LinkedList dirsToReportDeleted = new LinkedList<>();
+ while (!deletedDirs.isEmpty()) {
+ final Path path = deletedDirs.poll();
+ dirsToReportDeleted.addFirst(path);
+ final DirectorySnapshot directorySnapshot = snapshots.get(path);
+ if (directorySnapshot != null) // May be null if we're not watching the whole file tree.
+ directorySnapshot.forEachDirectory(deletedDirs::offer);
+ }
+
+ for(final Path path : dirsToReportDeleted) {
+ final DirectorySnapshot directorySnapshot = snapshots.remove(path);
+ if (directorySnapshot != null) {
+ // This is needed in case a directory tree was moved (mv -f) out of this directory.
+ directorySnapshot.forEachFile(MacOSXWatchKey.this::reportDeleted);
+ }
+ reportDeleted(path);
+ }
+ }
+
+ private void handleNewDirectories(final Queue createdDirs) {
+ // We don't know the exact sequence in which these were created,
+ // so at least maintain a sensible order, i.e. the parent created before its children.
+ while (!createdDirs.isEmpty()) {
+ final Path path = createdDirs.poll();
+ reportCreated(path);
+ if (watchFileTree) {
+ if (!snapshots.containsKey(path)) {
+ if (logger.isLoggable(PlatformLogger.Level.FINEST))
+ logger.finest("Just noticed yet another directory: " + path);
+ // Happens when a directory tree gets moved (mv -f) into this directory.
+ DirectorySnapshot newSnapshot = null;
+ try {
+ newSnapshot = createForOneDirectory(path, createdDirs);
+ } catch(IOException ignore) { }
+
+ if (newSnapshot != null)
+ newSnapshot.forEachFile(MacOSXWatchKey.this::reportCreated);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Represents a snapshot of a directory with a millisecond precision timestamp of the last modification.
+ */
+ private static class DirectorySnapshot {
+ // Path to this directory relative to the watch root.
+ private final Path directory;
+
+ // Maps file names to their attributes.
+ private final Map files;
+
+ // A counter to keep track of files that have disappeared since the last run.
+ private long currentTick;
+
+ private DirectorySnapshot(final Path directory) {
+ this.directory = directory;
+ this.files = new HashMap<>();
+ }
+
+ static DirectorySnapshot create(final Path realRootPath, final Path directory) throws IOException {
+ final DirectorySnapshot snapshot = new DirectorySnapshot(directory);
+ try (final DirectoryStream directoryStream = Files.newDirectoryStream(realRootPath.resolve(directory))) {
+ for (final Path file : directoryStream) {
+ try {
+ final BasicFileAttributes attrs = Files.readAttributes(file, BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS);
+ final Entry entry = new Entry(attrs.isDirectory(), attrs.lastModifiedTime().toMillis(), 0);
+ snapshot.files.put(file.getFileName(), entry);
+ } catch (IOException ignore) {}
+ }
+ } catch (DirectoryIteratorException e) {
+ throw e.getCause();
+ }
+
+ return snapshot;
+ }
+
+ void forEachDirectory(final Consumer consumer) {
+ files.forEach((path, entry) -> { if (entry.isDirectory) consumer.accept(directory.resolve(path)); } );
+ }
+
+ void forEachFile(final Consumer consumer) {
+ files.forEach((path, entry) -> { if (!entry.isDirectory) consumer.accept(directory.resolve(path)); } );
+ }
+
+ void update(final MacOSXWatchKey watchKey,
+ final Queue createdDirs,
+ final Queue deletedDirs,
+ final Queue modifiedDirs) {
+ currentTick++;
+
+ try (final DirectoryStream directoryStream = Files.newDirectoryStream(watchKey.getRealRootPath().resolve(directory))) {
+ for (final Path file : directoryStream) {
+ try {
+ final BasicFileAttributes attrs
+ = Files.readAttributes(file, BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS);
+ final Path fileName = file.getFileName();
+ final Entry entry = files.get(fileName);
+ final boolean isNew = (entry == null);
+ final long lastModified = attrs.lastModifiedTime().toMillis();
+ final Path relativePath = directory.resolve(fileName);
+
+ if (attrs.isDirectory()) {
+ if (isNew) {
+ files.put(fileName, new Entry(true, lastModified, currentTick));
+ if (createdDirs != null) createdDirs.offer(relativePath);
+ } else {
+ if (!entry.isDirectory) { // Used to be a file, now a directory
+ if (createdDirs != null) createdDirs.offer(relativePath);
+
+ files.put(fileName, new Entry(true, lastModified, currentTick));
+ watchKey.reportDeleted(relativePath);
+ } else if (entry.isModified(lastModified)) {
+ if (modifiedDirs != null) modifiedDirs.offer(relativePath);
+ watchKey.reportModified(relativePath);
+ }
+ entry.update(lastModified, currentTick);
+ }
+ } else {
+ if (isNew) {
+ files.put(fileName, new Entry(false, lastModified, currentTick));
+ watchKey.reportCreated(relativePath);
+ } else {
+ if (entry.isDirectory) { // Used to be a directory, now a file.
+ if (deletedDirs != null) deletedDirs.offer(relativePath);
+
+ files.put(fileName, new Entry(false, lastModified, currentTick));
+ watchKey.reportCreated(directory.resolve(fileName));
+ } else if (entry.isModified(lastModified)) {
+ watchKey.reportModified(relativePath);
+ }
+ entry.update(lastModified, currentTick);
+ }
+ }
+ } catch (IOException ignore) {
+ // Simply skip the file we couldn't read; it'll get marked as deleted later.
+ }
+ }
+ } catch (IOException | DirectoryIteratorException ignore) {
+ // Most probably this directory has just been deleted; our parent will notice that.
+ }
+
+ checkDeleted(watchKey, deletedDirs);
+ }
+
+ private void checkDeleted(final MacOSXWatchKey watchKey, final Queue deletedDirs) {
+ final Iterator> it = files.entrySet().iterator();
+ while (it.hasNext()) {
+ final Map.Entry mapEntry = it.next();
+ final Entry entry = mapEntry.getValue();
+ if (entry.lastTickCount != currentTick) {
+ final Path file = mapEntry.getKey();
+ it.remove();
+
+ if (entry.isDirectory) {
+ if (deletedDirs != null) deletedDirs.offer(directory.resolve(file));
+ } else {
+ watchKey.reportDeleted(directory.resolve(file));
+ }
+ }
+ }
+ }
+
+ /**
+ * Information about an entry in a directory.
+ */
+ private static class Entry {
+ private long lastModified;
+ private long lastTickCount;
+ private final boolean isDirectory;
+
+ Entry(final boolean isDirectory, final long lastModified, final long lastTickCount) {
+ this.lastModified = lastModified;
+ this.lastTickCount = lastTickCount;
+ this.isDirectory = isDirectory;
+ }
+
+ boolean isModified(final long lastModified) {
+ return (this.lastModified != lastModified);
+ }
+
+ void update(final long lastModified, final long lastTickCount) {
+ this.lastModified = lastModified;
+ this.lastTickCount = lastTickCount;
+ }
+ }
+ }
+
+ private void reportCreated(final Path path) {
+ if (logger.isLoggable(PlatformLogger.Level.FINEST))
+ logger.finest("About to report CREATE for path " + path);
+
+ if (eventsToWatch.contains(FSEventKind.CREATE))
+ signalEvent(StandardWatchEventKinds.ENTRY_CREATE, path);
+ }
+
+ private void reportDeleted(final Path path) {
+ if (logger.isLoggable(PlatformLogger.Level.FINEST))
+ logger.finest("About to report DELETE for path " + path);
+
+ if (eventsToWatch.contains(FSEventKind.DELETE))
+ signalEvent(StandardWatchEventKinds.ENTRY_DELETE, path);
+ }
+
+ private void reportModified(final Path path) {
+ if (logger.isLoggable(PlatformLogger.Level.FINEST))
+ logger.finest("About to report MODIFIED for path " + path);
+
+ if (eventsToWatch.contains(FSEventKind.MODIFY))
+ signalEvent(StandardWatchEventKinds.ENTRY_MODIFY, path);
+ }
+
+ private void reportOverflow(final Path path) {
+ if (logger.isLoggable(PlatformLogger.Level.FINEST))
+ logger.finest("About to report OVERFLOW for path " + path);
+
+ if (eventsToWatch.contains(FSEventKind.OVERFLOW))
+ signalEvent(StandardWatchEventKinds.OVERFLOW, path);
+ }
+
+ public Object getRootPathKey() {
+ return rootPathKey;
+ }
+
+ public Path getRealRootPath() {
+ return realRootPath;
+ }
+
+ @Override
+ public boolean isValid() {
+ synchronized (eventStreamRefLock) {
+ return eventStreamRef != 0;
+ }
+ }
+
+ @Override
+ public void cancel() {
+ if (!isValid()) return;
+
+ // First, must stop the corresponding run loop:
+ ((MacOSXWatchService) watcher()).cancel(this);
+
+ // Next, invalidate the corresponding native FSEventStream.
+ invalidate();
+ }
+
+ void invalidate() {
+ synchronized (eventStreamRefLock) {
+ if (isValid()) {
+ eventStreamStop(eventStreamRef);
+ eventStreamRef = 0;
+ }
+ }
+ }
+
+ long getEventStreamRef() {
+ synchronized (eventStreamRefLock) {
+ assert (isValid());
+ return eventStreamRef;
+ }
+ }
+ }
+
+ /* native methods */
+
+ private static native long eventStreamCreate(String dir, double latencyInSeconds, int flags);
+ private static native void eventStreamSchedule(long eventStreamRef, long runLoopRef);
+ private static native void eventStreamStop(long eventStreamRef);
+ private static native long CFRunLoopGetCurrent();
+ private static native void CFRunLoopRun(final MacOSXWatchService watchService);
+ private static native void CFRunLoopStop(long runLoopRef);
+
+ private static native void initIDs();
+
+ static {
+ tracingEnabled = logger.isLoggable(PlatformLogger.Level.FINEST);
+ System.loadLibrary("nio");
+ initIDs();
+ }
+}
diff --git a/src/java.base/macosx/native/libnio/fs/MacOSXWatchService.c b/src/java.base/macosx/native/libnio/fs/MacOSXWatchService.c
new file mode 100644
index 000000000000..72c08c9608b2
--- /dev/null
+++ b/src/java.base/macosx/native/libnio/fs/MacOSXWatchService.c
@@ -0,0 +1,253 @@
+/*
+ * Copyright (c) 2021, 2022, JetBrains s.r.o.. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "jni.h"
+#include "jni_util.h"
+#include "nio_util.h"
+
+#include
+
+#include
+#include
+
+#if defined(__GNUC__) || defined(__clang__)
+# ifndef ATTRIBUTE_PRINTF
+# define ATTRIBUTE_PRINTF(fmt,vargs) __attribute__((format(printf, fmt, vargs)))
+# endif
+#endif
+
+static void
+traceLine(JNIEnv* env, const char* fmt, ...) ATTRIBUTE_PRINTF(2, 3);
+
+// Controls exception stack trace output and debug trace.
+// Set by raising the logging level of sun.nio.fs.MacOSXWatchService to or above FINEST.
+static jboolean tracingEnabled;
+
+static jmethodID callbackMID; // MacOSXWatchService.callback()
+static __thread jobject watchService; // The instance of MacOSXWatchService that is associated with this thread
+
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_MacOSXWatchService_initIDs(JNIEnv* env, __unused jclass clazz)
+{
+ jfieldID tracingEnabledFieldID = (*env)->GetStaticFieldID(env, clazz, "tracingEnabled", "Z");
+ CHECK_NULL(tracingEnabledFieldID);
+ tracingEnabled = (*env)->GetStaticBooleanField(env, clazz, tracingEnabledFieldID);
+ if ((*env)->ExceptionCheck(env)) {
+ (*env)->ExceptionDescribe(env);
+ }
+
+ callbackMID = (*env)->GetMethodID(env, clazz, "callback", "(J[Ljava/lang/String;J)V");
+}
+
+extern CFStringRef toCFString(JNIEnv *env, jstring javaString);
+
+static void
+traceLine(JNIEnv* env, const char* fmt, ...)
+{
+ if (tracingEnabled) {
+ va_list vargs;
+ va_start(vargs, fmt);
+ char* buf = (char*)malloc(1024);
+ vsnprintf(buf, 1024, fmt, vargs);
+ const jstring text = JNU_NewStringPlatform(env, buf);
+ free(buf);
+ va_end(vargs);
+
+ jboolean ignoreException;
+ JNU_CallStaticMethodByName(env, &ignoreException, "sun/nio/fs/MacOSXWatchService", "traceLine", "(Ljava/lang/String;)V", text);
+ }
+}
+
+static jboolean
+convertToJavaStringArray(JNIEnv* env, char **eventPaths,
+ const jsize numEventsToReport, jobjectArray javaEventPathsArray)
+{
+ for (jsize i = 0; i < numEventsToReport; i++) {
+ const jstring path = JNU_NewStringPlatform(env, eventPaths[i]);
+ CHECK_NULL_RETURN(path, FALSE);
+ (*env)->SetObjectArrayElement(env, javaEventPathsArray, i, path);
+ }
+
+ return JNI_TRUE;
+}
+
+static void
+callJavaCallback(JNIEnv* env, jlong streamRef, jobjectArray javaEventPathsArray, jlong eventFlags)
+{
+ if (callbackMID != NULL && watchService != NULL) {
+ // We are called on the run loop thread, so it's OK to use the thread-local reference
+ // to the watch service.
+ (*env)->CallVoidMethod(env, watchService, callbackMID, streamRef, javaEventPathsArray, eventFlags);
+ }
+}
+
+/**
+ * Callback that is invoked on the run loop thread and informs of new file-system events from an FSEventStream.
+ */
+static void
+callback(__unused ConstFSEventStreamRef streamRef,
+ __unused void *clientCallBackInfo,
+ size_t numEventsTotal,
+ void *eventPaths,
+ const FSEventStreamEventFlags eventFlags[],
+ __unused const FSEventStreamEventId eventIds[])
+{
+ JNIEnv *env = (JNIEnv *) JNU_GetEnv(jvm, JNI_VERSION_1_2);
+ if (!env) { // Shouldn't happen as run loop starts from Java code
+ return;
+ }
+
+ // We can get more events at once than the number of Java array elements,
+ // so report them in chunks.
+ const size_t MAX_EVENTS_TO_REPORT_AT_ONCE = (INT_MAX - 2);
+
+ jboolean success = JNI_TRUE;
+ for(size_t eventIndex = 0; success && (eventIndex < numEventsTotal); ) {
+ const size_t numEventsRemaining = (numEventsTotal - eventIndex);
+ const jsize numEventsToReport = (numEventsRemaining > MAX_EVENTS_TO_REPORT_AT_ONCE)
+ ? MAX_EVENTS_TO_REPORT_AT_ONCE
+ : numEventsRemaining;
+
+ const jboolean localFramePushed = ((*env)->PushLocalFrame(env, numEventsToReport + 5) == JNI_OK);
+ success = localFramePushed;
+
+ jobjectArray javaEventPathsArray = NULL;
+ if (success) {
+ javaEventPathsArray = (*env)->NewObjectArray(env, (jsize)numEventsToReport, JNU_ClassString(env), NULL);
+ success = (javaEventPathsArray != NULL);
+ }
+
+ if (success) {
+ success = convertToJavaStringArray(env, &((char**)eventPaths)[eventIndex], numEventsToReport, javaEventPathsArray);
+ }
+
+ callJavaCallback(env, (jlong)streamRef, javaEventPathsArray, (jlong)&eventFlags[eventIndex]);
+
+ if ((*env)->ExceptionCheck(env)) {
+ if (tracingEnabled) (*env)->ExceptionDescribe(env);
+ }
+
+ if (localFramePushed) {
+ (*env)->PopLocalFrame(env, NULL);
+ }
+
+ eventIndex += numEventsToReport;
+ }
+}
+
+/**
+ * Creates a new FSEventStream and returns FSEventStreamRef for it.
+ */
+JNIEXPORT jlong JNICALL
+Java_sun_nio_fs_MacOSXWatchService_eventStreamCreate(JNIEnv* env, __unused jclass clazz,
+ jstring dir, jdouble latencyInSeconds, jint flags)
+{
+ const CFStringRef path = toCFString(env, dir);
+ CHECK_NULL_RETURN(path, 0);
+ const CFArrayRef pathsToWatch = CFArrayCreate(NULL, (const void **) &path, 1, NULL);
+ CHECK_NULL_RETURN(pathsToWatch, 0);
+
+ const FSEventStreamRef stream = FSEventStreamCreate(
+ NULL,
+ &callback,
+ NULL,
+ pathsToWatch,
+ kFSEventStreamEventIdSinceNow,
+ (CFAbsoluteTime) latencyInSeconds,
+ flags
+ );
+
+ traceLine(env, "created event stream 0x%p", stream);
+
+ return (jlong)stream;
+}
+
+
+/**
+ * Schedules the given FSEventStream on the run loop of the current thread. Starts the stream
+ * so that the run loop can receive events from the stream.
+ */
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_MacOSXWatchService_eventStreamSchedule(__unused JNIEnv* env, __unused jclass clazz,
+ jlong eventStreamRef, jlong runLoopRef)
+{
+ const FSEventStreamRef stream = (FSEventStreamRef)eventStreamRef;
+ const CFRunLoopRef runLoop = (CFRunLoopRef)runLoopRef;
+
+ FSEventStreamScheduleWithRunLoop(stream, runLoop, kCFRunLoopDefaultMode);
+ FSEventStreamStart(stream);
+
+ traceLine(env, "scheduled stream 0x%p on thread 0x%p", stream, CFRunLoopGetCurrent());
+}
+
+/**
+ * Performs the steps necessary to dispose of the given FSEventStreamRef.
+ * The stream must have been started and scheduled with a run loop.
+ */
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_MacOSXWatchService_eventStreamStop(__unused JNIEnv* env, __unused jclass clazz, jlong eventStreamRef)
+{
+ const FSEventStreamRef streamRef = (FSEventStreamRef)eventStreamRef;
+
+ FSEventStreamStop(streamRef); // Unregister with the FS Events service. No more callbacks from this stream
+ FSEventStreamInvalidate(streamRef); // Unschedule from any runloops
+ FSEventStreamRelease(streamRef); // Decrement the stream's refcount
+}
+
+/**
+ * Returns the CFRunLoop object for the current thread.
+ */
+JNIEXPORT jlong JNICALL
+Java_sun_nio_fs_MacOSXWatchService_CFRunLoopGetCurrent(__unused JNIEnv* env, __unused jclass clazz)
+{
+ const CFRunLoopRef currentRunLoop = CFRunLoopGetCurrent();
+ traceLine(env, "get current run loop: 0x%p", currentRunLoop);
+ return (jlong)currentRunLoop;
+}
+
+/**
+ * Simply calls CFRunLoopRun() to run current thread's run loop for as long as there are event sources
+ * attached to it.
+ */
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_MacOSXWatchService_CFRunLoopRun(__unused JNIEnv* env, __unused jclass clazz, jlong watchServiceObject)
+{
+ traceLine(env, "running run loop on 0x%p", CFRunLoopGetCurrent());
+
+ // Thread-local pointer to the WatchService instance will be used by the callback
+ // on this thread.
+ watchService = (*env)->NewGlobalRef(env, (jobject)watchServiceObject);
+ CFRunLoopRun();
+ (*env)->DeleteGlobalRef(env, (jobject)watchService);
+ watchService = NULL;
+
+ traceLine(env, "run loop done on 0x%p", CFRunLoopGetCurrent());
+}
+
+JNIEXPORT void JNICALL
+Java_sun_nio_fs_MacOSXWatchService_CFRunLoopStop(__unused JNIEnv* env, __unused jclass clazz, jlong runLoopRef)
+{
+ traceLine(env, "stopping run loop 0x%p", (void*)runLoopRef);
+ CFRunLoopStop((CFRunLoopRef)runLoopRef);
+}
diff --git a/src/java.base/macosx/native/libnio/fs/UTIFileTypeDetector.c b/src/java.base/macosx/native/libnio/fs/UTIFileTypeDetector.c
index 5e9451e850c1..2820bafbce9a 100644
--- a/src/java.base/macosx/native/libnio/fs/UTIFileTypeDetector.c
+++ b/src/java.base/macosx/native/libnio/fs/UTIFileTypeDetector.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -35,7 +35,7 @@
* If a memory error occurs, and OutOfMemoryError is thrown and
* NULL is returned.
*/
-static CFStringRef toCFString(JNIEnv *env, jstring javaString)
+CFStringRef toCFString(JNIEnv *env, jstring javaString)
{
if (javaString == NULL) {
return NULL;
diff --git a/src/java.base/share/classes/com/jetbrains/base/JBRApiModule.java b/src/java.base/share/classes/com/jetbrains/base/JBRApiModule.java
new file mode 100644
index 000000000000..49fca1b8c81f
--- /dev/null
+++ b/src/java.base/share/classes/com/jetbrains/base/JBRApiModule.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2000-2023 JetBrains s.r.o.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.jetbrains.base;
+
+import com.jetbrains.internal.JBRApi;
+
+import java.lang.invoke.MethodHandles;
+
+/**
+ * This class contains mapping between JBR API interfaces and implementation in {@code java.base} module.
+ */
+public class JBRApiModule {
+ static {
+ JBRApi.registerModule(MethodHandles.lookup(), JBRApiModule.class.getModule()::addExports)
+ .service("com.jetbrains.JBR$ServiceApi")
+ .withStatic("getService", "getService", "com.jetbrains.internal.JBRApi")
+ .service("com.jetbrains.Jstack")
+ .withStatic("includeInfoFrom", "$$jb$additionalInfoForJstack", "java.lang.Throwable");
+ }
+}
diff --git a/src/java.base/share/classes/com/jetbrains/bootstrap/JBRApiBootstrap.java b/src/java.base/share/classes/com/jetbrains/bootstrap/JBRApiBootstrap.java
new file mode 100644
index 000000000000..53f893c2e31b
--- /dev/null
+++ b/src/java.base/share/classes/com/jetbrains/bootstrap/JBRApiBootstrap.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2000-2023 JetBrains s.r.o.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.jetbrains.bootstrap;
+
+import com.jetbrains.internal.JBRApi;
+import jdk.internal.loader.ClassLoaders;
+
+import java.lang.invoke.MethodHandles;
+
+/**
+ * Bootstrap class, used to initialize {@linkplain JBRApi JBR API}.
+ */
+public class JBRApiBootstrap {
+ private JBRApiBootstrap() {}
+
+ /**
+ * Classes that register JBR API implementation for their modules.
+ */
+ private static final String[] MODULES = {
+ "com.jetbrains.base.JBRApiModule",
+ "com.jetbrains.desktop.JBRApiModule"
+ };
+
+ /**
+ * Called from static initializer of {@link com.jetbrains.JBR}.
+ * @param outerLookup lookup context inside {@code jetbrains.api} module
+ * @return implementation for {@link com.jetbrains.JBR.ServiceApi} interface
+ */
+ public static synchronized Object bootstrap(MethodHandles.Lookup outerLookup) {
+ if (!System.getProperty("jetbrains.api.enabled", "true").equalsIgnoreCase("true")) return null;
+ try {
+ Class> apiInterface = outerLookup.findClass("com.jetbrains.JBR$ServiceApi");
+ if (!outerLookup.hasFullPrivilegeAccess() ||
+ outerLookup.lookupClass().getPackage() != apiInterface.getPackage()) {
+ throw new IllegalArgumentException("Lookup must be full-privileged from the com.jetbrains package: " +
+ outerLookup.lookupClass().getName());
+ }
+ JBRApi.init(outerLookup);
+ ClassLoader classLoader = ClassLoaders.platformClassLoader();
+ for (String m : MODULES) Class.forName(m, true, classLoader);
+ return JBRApi.getService(apiInterface);
+ } catch(ClassNotFoundException | IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+}
diff --git a/src/java.base/share/classes/com/jetbrains/internal/ASMUtils.java b/src/java.base/share/classes/com/jetbrains/internal/ASMUtils.java
new file mode 100644
index 000000000000..af28b5c0ac86
--- /dev/null
+++ b/src/java.base/share/classes/com/jetbrains/internal/ASMUtils.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2000-2023 JetBrains s.r.o.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.jetbrains.internal;
+
+import jdk.internal.org.objectweb.asm.ClassVisitor;
+import jdk.internal.org.objectweb.asm.MethodVisitor;
+import jdk.internal.org.objectweb.asm.Type;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.lang.reflect.ClassFileFormatVersion;
+import java.lang.reflect.Method;
+
+import static jdk.internal.org.objectweb.asm.Opcodes.*;
+
+/**
+ * Utility class that helps with bytecode generation
+ */
+class ASMUtils {
+
+ private static final MethodHandle genericSignatureGetter;
+ static {
+ try {
+ genericSignatureGetter = MethodHandles.privateLookupIn(Method.class, MethodHandles.lookup())
+ .findVirtual(Method.class, "getGenericSignature", MethodType.methodType(String.class));
+ } catch (NoSuchMethodException | IllegalAccessException e) {
+ throw new Error(e);
+ }
+ }
+
+ public static final int CLASSFILE_VERSION = ClassFileFormatVersion.latest().major();
+
+ public static void generateUnsupportedMethod(ClassVisitor writer, Method interfaceMethod) {
+ InternalMethodInfo methodInfo = getInternalMethodInfo(interfaceMethod);
+ MethodVisitor p = writer.visitMethod(ACC_PUBLIC | ACC_FINAL, methodInfo.name(),
+ methodInfo.descriptor(), methodInfo.genericSignature(), methodInfo.exceptionNames());
+ p.visitTypeInsn(NEW, "java/lang/UnsupportedOperationException");
+ p.visitInsn(DUP);
+ p.visitLdcInsn("No implementation found for this method");
+ p.visitMethodInsn(INVOKESPECIAL, "java/lang/UnsupportedOperationException", "", "(Ljava/lang/String;)V", false);
+ p.visitInsn(ATHROW);
+ p.visitMaxs(-1, -1);
+ }
+
+ public static void logDeprecated(MethodVisitor writer, String message) {
+ writer.visitTypeInsn(NEW, "java/lang/Exception");
+ writer.visitInsn(DUP);
+ writer.visitLdcInsn(message);
+ writer.visitMethodInsn(INVOKESPECIAL, "java/lang/Exception", "", "(Ljava/lang/String;)V", false);
+ writer.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Exception", "printStackTrace", "()V", false);
+ }
+
+ protected record InternalMethodInfo(String name, String descriptor, String genericSignature,
+ String[] exceptionNames) {}
+
+ public static InternalMethodInfo getInternalMethodInfo(Method method) {
+ try {
+ return new InternalMethodInfo(
+ method.getName(),
+ Type.getMethodDescriptor(method),
+ (String) genericSignatureGetter.invoke(method),
+ getExceptionNames(method));
+ } catch (Throwable e) {
+ throw new Error(e);
+ }
+ }
+
+ private static String[] getExceptionNames(Method method) {
+ Class>[] exceptionTypes = method.getExceptionTypes();
+ String[] exceptionNames = new String[exceptionTypes.length];
+ for (int i = 0; i < exceptionTypes.length; i++) {
+ exceptionNames[i] = Type.getInternalName(exceptionTypes[i]);
+ }
+ return exceptionNames;
+ }
+
+ public static int getParameterSize(Class> c) {
+ if (c == Void.TYPE) {
+ return 0;
+ } else if (c == Long.TYPE || c == Double.TYPE) {
+ return 2;
+ }
+ return 1;
+ }
+
+ public static int getLoadOpcode(Class> c) {
+ if (c == Void.TYPE) {
+ throw new InternalError("Unexpected void type of load opcode");
+ }
+ return ILOAD + getOpcodeOffset(c);
+ }
+
+ public static int getReturnOpcode(Class> c) {
+ if (c == Void.TYPE) {
+ return RETURN;
+ }
+ return IRETURN + getOpcodeOffset(c);
+ }
+
+ public static int getOpcodeOffset(Class> c) {
+ if (c.isPrimitive()) {
+ if (c == Long.TYPE) {
+ return 1;
+ } else if (c == Float.TYPE) {
+ return 2;
+ } else if (c == Double.TYPE) {
+ return 3;
+ }
+ return 0;
+ } else {
+ return 4;
+ }
+ }
+}
diff --git a/src/java.base/share/classes/com/jetbrains/internal/JBRApi.java b/src/java.base/share/classes/com/jetbrains/internal/JBRApi.java
new file mode 100644
index 000000000000..dd45238c5f1d
--- /dev/null
+++ b/src/java.base/share/classes/com/jetbrains/internal/JBRApi.java
@@ -0,0 +1,352 @@
+/*
+ * Copyright 2000-2023 JetBrains s.r.o.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.jetbrains.internal;
+
+import sun.security.action.GetBooleanAction;
+
+import java.io.Serial;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.function.BiFunction;
+
+import static java.lang.invoke.MethodHandles.Lookup;
+
+/**
+ * JBR API is a collection of JBR-specific features that are accessed by client though
+ * {@link com.jetbrains.JBR jetbrains.api} module. Actual implementation is linked by
+ * JBR at runtime by generating {@linkplain Proxy proxy objects}.
+ * Mapping between interfaces and implementation code is defined in
+ * {@linkplain com.jetbrains.bootstrap.JBRApiBootstrap#MODULES registry classes}.
+ *
+ * This class has most basic methods for working with JBR API and cache of generated proxies.
+ *
+ *
How to add a new service
+ *
+ * Create your service interface in module {@link com.jetbrains.JBR jetbrains.api}:
+ * {@code
+ * package com.jetbrains;
+ *
+ * interface StringOptimizer {
+ * void optimize(String string);
+ * }
+ * }
+ *
+ * Create an implementation inside JBR:
+ * {@link java.lang.String java.lang.String}:{@code
+ * private static void optimizeInternal(String string) {
+ * string.hash = 0;
+ * string.hashIsZero = true;
+ * }
+ * }
+ *
+ * Register your service in corresponding
+ * {@linkplain com.jetbrains.bootstrap.JBRApiBootstrap#MODULES module registry class}:
+ * {@link com.jetbrains.base.JBRApiModule}:{@code
+ * .service("com.jetbrains.StringOptimizer", null)
+ * .withStatic("optimize", "java.lang.String", "optimizeInternal")
+ * }
+ *
+ * You can also bind the interface to implementation class
+ * (without actually implementing the interface):
+ * {@link java.lang.String java.lang.String}:{@code
+ * private static class StringOptimizerImpl {
+ *
+ * private void optimize(String string) {
+ * string.hash = 0;
+ * string.hashIsZero = true;
+ * }
+ * }
+ * }
+ * {@link com.jetbrains.base.JBRApiModule}:{@code
+ * .service("com.jetbrains.StringOptimizer", "java.lang.String$StringOptimizerImpl")
+ * }
+ *
+ *
+ * How to add a new proxy
+ * Registering a proxy is similar to registering a service:
+ * {@code
+ * .proxy("com.jetbrains.SomeProxy", "a.b.c.SomeProxyImpl")
+ * }
+ * Note that unlike service, proxy must have a target type.
+ * Also, proxy expects target object as a single constructor argument
+ * and can only be instantiated by JBR, there's no API that would allow
+ * user to directly create proxy object.
+ */
+public class JBRApi {
+ @SuppressWarnings("removal")
+ static final boolean VERBOSE = AccessController.doPrivileged(new GetBooleanAction("jetbrains.api.verbose"));
+
+ private static final Map registeredProxyInfoByInterfaceName = new HashMap<>();
+ private static final Map registeredProxyInfoByTargetName = new HashMap<>();
+ private static final ConcurrentMap, Proxy>> proxyByInterface = new ConcurrentHashMap<>();
+
+ /**
+ * lookup context inside {@code jetbrains.api} module
+ */
+ static Lookup outerLookup;
+ /**
+ * Known service and proxy interfaces extracted from {@link com.jetbrains.JBR.Metadata}
+ */
+ static Set knownServices, knownProxies;
+
+ public static void init(Lookup outerLookup) {
+ JBRApi.outerLookup = outerLookup;
+ try {
+ Class> metadataClass = outerLookup.findClass("com.jetbrains.JBR$Metadata");
+ Lookup lookup = MethodHandles.privateLookupIn(metadataClass, outerLookup);
+ knownServices = Set.of((String[]) lookup.findStaticVarHandle(metadataClass,
+ "KNOWN_SERVICES", String[].class).get());
+ knownProxies = Set.of((String[]) lookup.findStaticVarHandle(metadataClass,
+ "KNOWN_PROXIES", String[].class).get());
+ } catch (ClassNotFoundException | NoSuchFieldException | IllegalAccessException e) {
+ e.printStackTrace();
+ knownServices = Set.of();
+ knownProxies = Set.of();
+ }
+ if (VERBOSE) {
+ System.out.println("JBR API init\nKNOWN_SERVICES = " + knownServices + "\nKNOWN_PROXIES = " + knownProxies);
+ }
+ }
+
+ /**
+ * @return fully supported service implementation for the given interface, or null
+ * @apiNote this method is a part of internal {@link com.jetbrains.JBR.ServiceApi}
+ * service, but is not directly exposed to user.
+ */
+ public static T getService(Class interFace) {
+ Proxy p = getProxy(interFace);
+ return p != null && p.isSupported() ? p.getInstance() : null;
+ }
+
+ /**
+ * @return proxy for the given interface, or {@code null}
+ */
+ @SuppressWarnings("unchecked")
+ static Proxy getProxy(Class interFace) {
+ return (Proxy) proxyByInterface.computeIfAbsent(interFace, i -> {
+ RegisteredProxyInfo info = registeredProxyInfoByInterfaceName.get(i.getName());
+ if (info == null) return null;
+ ProxyInfo resolved = ProxyInfo.resolve(info);
+ if (resolved == null) {
+ if (VERBOSE) {
+ System.err.println("Couldn't resolve proxy info: " + i.getName());
+ }
+ return null;
+ } else return new Proxy<>(resolved);
+ });
+ }
+
+ /**
+ * @return true if given class represents a proxy interface. Even if {@code jetbrains.api}
+ * introduces new interfaces JBR is not aware of, these interfaces would still be detected
+ * by this method.
+ */
+ static boolean isKnownProxyInterface(Class> clazz) {
+ String name = clazz.getName();
+ return registeredProxyInfoByInterfaceName.containsKey(name) ||
+ knownServices.contains(name) || knownProxies.contains(name);
+ }
+
+ /**
+ * Reverse lookup by proxy target type name.
+ * @return user-side interface for given implementation target type name.
+ */
+ static Class> getProxyInterfaceByTargetName(String targetName) {
+ RegisteredProxyInfo info = registeredProxyInfoByTargetName.get(targetName);
+ if (info == null) return null;
+ try {
+ return Class.forName(info.interfaceName(), true,
+ (info.type().isPublicApi() ? outerLookup : info.apiModule()).lookupClass().getClassLoader());
+ } catch (ClassNotFoundException e) {
+ if (VERBOSE) e.printStackTrace();
+ return null;
+ }
+ }
+
+ public static InternalServiceBuilder internalServiceBuilder(Lookup interFace, String... targets) {
+ return new InternalServiceBuilder(new RegisteredProxyInfo(
+ interFace, interFace.lookupClass().getName(), targets, ProxyInfo.Type.INTERNAL_SERVICE, new ArrayList<>()));
+ }
+
+ public static class InternalServiceBuilder {
+
+ private final RegisteredProxyInfo info;
+
+ private InternalServiceBuilder(RegisteredProxyInfo info) {
+ this.info = info;
+ }
+
+ public InternalServiceBuilder withStatic(String interfaceMethodName, String methodName, String... classes) {
+ info.staticMethods().add(
+ new RegisteredProxyInfo.StaticMethodMapping(interfaceMethodName, methodName, classes));
+ return this;
+ }
+
+ @SuppressWarnings("removal")
+ public Object build() {
+ return AccessController.doPrivileged((PrivilegedAction) () -> {
+ ProxyInfo info = ProxyInfo.resolve(this.info);
+ if (info == null) return null;
+ ProxyGenerator generator = new ProxyGenerator(info);
+ if (!generator.areAllMethodsImplemented()) return null;
+ generator.defineClasses();
+ MethodHandle constructor = generator.findConstructor();
+ generator.init();
+ try {
+ return constructor.invoke();
+ } catch (Throwable e) {
+ throw new RuntimeException(e);
+ }
+ });
+ }
+ }
+
+ /**
+ * Called by {@linkplain com.jetbrains.bootstrap.JBRApiBootstrap#MODULES registry classes}
+ * to register a new mapping for corresponding modules.
+ */
+ public static ModuleRegistry registerModule(Lookup lookup, BiFunction addExports) {
+ addExports.apply(lookup.lookupClass().getPackageName(), outerLookup.lookupClass().getModule());
+ return new ModuleRegistry(lookup);
+ }
+
+ public static class ModuleRegistry {
+
+ private final Lookup lookup;
+ private RegisteredProxyInfo lastProxy;
+
+ private ModuleRegistry(Lookup lookup) {
+ this.lookup = lookup;
+ }
+
+ private ModuleRegistry addProxy(ProxyInfo.Type type, String interfaceName, String... targets) {
+ lastProxy = new RegisteredProxyInfo(lookup, interfaceName, targets, type, new ArrayList<>());
+ registeredProxyInfoByInterfaceName.put(interfaceName, lastProxy);
+ for (String target : targets) {
+ registeredProxyInfoByTargetName.put(target, lastProxy);
+ }
+ if (targets.length == 1) {
+ validate2WayMapping(lastProxy, registeredProxyInfoByInterfaceName.get(targets[0]));
+ validate2WayMapping(lastProxy, registeredProxyInfoByTargetName.get(interfaceName));
+ }
+ return this;
+ }
+ private static void validate2WayMapping(RegisteredProxyInfo p, RegisteredProxyInfo reverse) {
+ if (reverse != null &&
+ (!p.interfaceName().equals(reverse.targets()[0]) || !p.targets()[0].equals(reverse.interfaceName()))) {
+ throw new IllegalArgumentException("Invalid 2-way proxy mapping: " +
+ p.interfaceName() + " -> " + p.targets()[0] + " & " +
+ reverse.interfaceName() + " -> " + reverse.targets()[0]);
+ }
+ }
+
+ /**
+ * Register new {@linkplain ProxyInfo.Type#PROXY proxy} mapping.
+ *
+ * When {@code target} object is passed from JBR to client through service or any other proxy,
+ * it's converted to corresponding {@code interFace} type by creating a proxy object
+ * that implements {@code interFace} and delegates method calls to {@code target}.
+ * @param interFace interface name in {@link com.jetbrains.JBR jetbrains.api} module.
+ * @param targets corresponding class/interface names in current JBR module, first found will be used. This must not be empty.
+ * @apiNote class name example: {@code pac.ka.ge.Outer$Inner}
+ */
+ public ModuleRegistry proxy(String interFace, String... targets) {
+ if (targets.length == 0) throw new IllegalArgumentException("Proxy must have at least one target");
+ return addProxy(ProxyInfo.Type.PROXY, interFace, targets);
+ }
+
+ /**
+ * Register new {@linkplain ProxyInfo.Type#SERVICE service} mapping.
+ *
+ * Service is a singleton, which may be accessed by client using {@link com.jetbrains.JBR} class.
+ * @param interFace interface name in {@link com.jetbrains.JBR jetbrains.api} module.
+ * @param targets corresponding implementation class names in current JBR module, first found will be used.
+ * @apiNote class name example: {@code pac.ka.ge.Outer$Inner}
+ */
+ public ModuleRegistry service(String interFace, String... targets) {
+ return addProxy(ProxyInfo.Type.SERVICE, interFace, targets);
+ }
+
+ /**
+ * Register new {@linkplain ProxyInfo.Type#CLIENT_PROXY client proxy} mapping.
+ * This mapping type allows implementation of callbacks.
+ *
+ * When {@code target} object is passed from client to JBR through service or any other proxy,
+ * it's converted to corresponding {@code interFace} type by creating a proxy object
+ * that implements {@code interFace} and delegates method calls to {@code target}.
+ * @param interFace interface name in current JBR module.
+ * @param target corresponding class/interface name in {@link com.jetbrains.JBR jetbrains.api} module.
+ * @apiNote class name example: {@code pac.ka.ge.Outer$Inner}
+ */
+ public ModuleRegistry clientProxy(String interFace, String target) {
+ Objects.requireNonNull(target);
+ return addProxy(ProxyInfo.Type.CLIENT_PROXY, interFace, target);
+ }
+
+ /**
+ * Register new 2-way mapping.
+ * It creates both {@linkplain ProxyInfo.Type#PROXY proxy} and
+ * {@linkplain ProxyInfo.Type#CLIENT_PROXY client proxy} between given interfaces.
+ *
+ * It links together two given interfaces and allows passing such objects back and forth
+ * between JBR and {@link com.jetbrains.JBR jetbrains.api} module through services and other proxy methods.
+ * @param apiInterface interface name in {@link com.jetbrains.JBR jetbrains.api} module.
+ * @param jbrInterface interface name in current JBR module.
+ * @apiNote class name example: {@code pac.ka.ge.Outer$Inner}
+ */
+ public ModuleRegistry twoWayProxy(String apiInterface, String jbrInterface) {
+ clientProxy(jbrInterface, apiInterface);
+ proxy(apiInterface, jbrInterface);
+ return this;
+ }
+
+ /**
+ * Delegate "{@code interfaceMethodName}" method calls to first found static "{@code methodName}" in "{@code classes}".
+ */
+ public ModuleRegistry withStatic(String interfaceMethodName, String methodName, String... classes) {
+ lastProxy.staticMethods().add(
+ new RegisteredProxyInfo.StaticMethodMapping(interfaceMethodName, methodName, classes));
+ return this;
+ }
+ }
+
+ /**
+ * Thrown by service implementations indicating that the service is not available for some reason
+ */
+ public static class ServiceNotAvailableException extends RuntimeException {
+ @Serial
+ private static final long serialVersionUID = 1L;
+ public ServiceNotAvailableException() { super(); }
+ public ServiceNotAvailableException(String message) { super(message); }
+ public ServiceNotAvailableException(String message, Throwable cause) { super(message, cause); }
+ public ServiceNotAvailableException(Throwable cause) { super(cause); }
+ }
+}
diff --git a/src/java.base/share/classes/com/jetbrains/internal/Proxy.java b/src/java.base/share/classes/com/jetbrains/internal/Proxy.java
new file mode 100644
index 000000000000..29195c3f5621
--- /dev/null
+++ b/src/java.base/share/classes/com/jetbrains/internal/Proxy.java
@@ -0,0 +1,228 @@
+/*
+ * Copyright 2000-2023 JetBrains s.r.o.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.jetbrains.internal;
+
+import java.lang.invoke.MethodHandle;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * Proxy is needed to dynamically link JBR API interfaces and implementation at runtime.
+ * It implements user-side interfaces and delegates method calls to actual implementation
+ * code through {@linkplain java.lang.invoke.MethodHandle method handles}.
+ *
+ * There are 3 type of proxy objects:
+ *
+ * {@linkplain ProxyInfo.Type#PROXY Proxy} - implements client-side interface from
+ * {@code jetbrains.api} and delegates calls to JBR-side target object and optionally static methods.
+ * {@linkplain ProxyInfo.Type#SERVICE Service} - singleton {@linkplain ProxyInfo.Type#PROXY proxy},
+ * may delegate calls only to static methods, without target object.
+ * {@linkplain ProxyInfo.Type#CLIENT_PROXY Client proxy} - reverse proxy, implements JBR-side interface
+ * and delegates calls to client-side target object by interface defined in {@code jetbrains.api}.
+ * May be used to implement callbacks which are created by client and called by JBR.
+ *
+ *
+ * Method signatures of proxy interfaces and implementation are validated to ensure that proxy can
+ * properly delegate call to the target implementation code. If there's no implementation found for some
+ * interface methods, corresponding proxy is considered unsupported. Proxy is also considered unsupported
+ * if any proxy used by it is unsupported, more about it at {@link ProxyDependencyManager}.
+ *
+ * Mapping between interfaces and implementation code is defined in
+ * {@linkplain com.jetbrains.bootstrap.JBRApiBootstrap#MODULES registry classes}.
+ * @param interface type for this proxy.
+ */
+class Proxy {
+ private final ProxyInfo info;
+
+ private volatile ProxyGenerator generator;
+ private volatile Boolean allMethodsImplemented;
+
+ private volatile Boolean supported;
+
+ private volatile Class> proxyClass;
+ private volatile MethodHandle constructor;
+ private volatile MethodHandle targetExtractor;
+
+ private volatile boolean instanceInitialized;
+ private volatile INTERFACE instance;
+
+ Proxy(ProxyInfo info) {
+ this.info = info;
+ }
+
+ /**
+ * @return {@link ProxyInfo} structure of this proxy
+ */
+ ProxyInfo getInfo() {
+ return info;
+ }
+
+ private synchronized void initGenerator() {
+ if (generator != null) return;
+ generator = new ProxyGenerator(info);
+ allMethodsImplemented = generator.areAllMethodsImplemented();
+ }
+
+ /**
+ * Checks if implementation is found for all abstract interface methods of this proxy.
+ */
+ boolean areAllMethodsImplemented() {
+ if (allMethodsImplemented != null) return allMethodsImplemented;
+ synchronized (this) {
+ if (allMethodsImplemented == null) initGenerator();
+ return allMethodsImplemented;
+ }
+ }
+
+ /**
+ * Checks if all methods are {@linkplain #areAllMethodsImplemented() implemented}
+ * for this proxy and all proxies it {@linkplain ProxyDependencyManager uses}.
+ */
+ boolean isSupported() {
+ if (supported != null) return supported;
+ synchronized (this) {
+ if (supported == null) {
+ Set> dependencies = ProxyDependencyManager.getProxyDependencies(info.interFace);
+ for (Class> d : dependencies) {
+ Proxy> p = JBRApi.getProxy(d);
+ if (p == null || !p.areAllMethodsImplemented()) {
+ supported = false;
+ return false;
+ }
+ }
+ supported = true;
+ }
+ return supported;
+ }
+ }
+
+ private synchronized void defineClasses() {
+ if (constructor != null) return;
+ initGenerator();
+ generator.defineClasses();
+ proxyClass = generator.getProxyClass();
+ constructor = generator.findConstructor();
+ targetExtractor = generator.findTargetExtractor();
+ }
+
+ /**
+ * @return generated proxy class
+ */
+ Class> getProxyClass() {
+ if (proxyClass != null) return proxyClass;
+ synchronized (this) {
+ if (proxyClass == null) defineClasses();
+ return proxyClass;
+ }
+ }
+
+ /**
+ * @return method handle for the constructor of this proxy.
+ *
+ * For {@linkplain ProxyInfo.Type#SERVICE services}, constructor is no-arg.
+ * For non-{@linkplain ProxyInfo.Type#SERVICE services}, constructor is single-arg,
+ * expecting target object to which it would delegate method calls.
+ *
+ */
+ MethodHandle getConstructor() {
+ if (constructor != null) return constructor;
+ synchronized (this) {
+ if (constructor == null) defineClasses();
+ return constructor;
+ }
+ }
+
+ /**
+ * @return method handle for that extracts target object of the proxy, or null.
+ */
+ MethodHandle getTargetExtractor() {
+ // targetExtractor may be null, so check constructor instead
+ if (constructor != null) return targetExtractor;
+ synchronized (this) {
+ if (constructor == null) defineClasses();
+ return targetExtractor;
+ }
+ }
+
+ private synchronized void initClass(Set> actualUsages) {
+ defineClasses();
+ if (generator != null) {
+ actualUsages.addAll(generator.getDirectProxyDependencies());
+ generator.init();
+ generator = null;
+ }
+ }
+ private synchronized void initDependencyGraph() {
+ defineClasses();
+ if (generator == null) return;
+ Set> dependencyClasses = ProxyDependencyManager.getProxyDependencies(info.interFace);
+ Set> dependencies = new HashSet<>();
+ Set> actualUsages = new HashSet<>();
+ for (Class> d : dependencyClasses) {
+ Proxy> p = JBRApi.getProxy(d);
+ if (p != null) {
+ dependencies.add(p);
+ p.initClass(actualUsages);
+ }
+ }
+ actualUsages.removeAll(dependencies);
+ if (!actualUsages.isEmpty()) {
+ // Should never happen, this is a sign of broken dependency search
+ throw new RuntimeException("Some proxies are not in dependencies of " + info.interFace.getName() +
+ ", but are actually used by it: " +
+ actualUsages.stream().map(p -> p.info.interFace.getName()).collect(Collectors.joining(", ")));
+ }
+ }
+
+ /**
+ * @return instance for this {@linkplain ProxyInfo.Type#SERVICE service},
+ * returns {@code null} for other proxy types.
+ */
+ @SuppressWarnings("unchecked")
+ INTERFACE getInstance() {
+ if (instanceInitialized) return instance;
+ if (info.type != ProxyInfo.Type.SERVICE) return null;
+ synchronized (this) {
+ if (instance == null) {
+ initDependencyGraph();
+ try {
+ instance = (INTERFACE) getConstructor().invoke();
+ } catch (JBRApi.ServiceNotAvailableException e) {
+ if (JBRApi.VERBOSE) {
+ System.err.println("Service not available: " + info.interFace.getName());
+ e.printStackTrace();
+ }
+ } catch (Throwable e) {
+ throw new RuntimeException(e);
+ } finally {
+ instanceInitialized = true;
+ }
+ }
+ return instance;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/java.base/share/classes/com/jetbrains/internal/ProxyDependencyManager.java b/src/java.base/share/classes/com/jetbrains/internal/ProxyDependencyManager.java
new file mode 100644
index 000000000000..b4ac4035b28f
--- /dev/null
+++ b/src/java.base/share/classes/com/jetbrains/internal/ProxyDependencyManager.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright 2000-2023 JetBrains s.r.o.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.jetbrains.internal;
+
+import java.lang.reflect.*;
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.function.Consumer;
+
+/**
+ * This class collects {@linkplain Proxy proxy} dependencies.
+ *
+ * Dependencies of a class {@code C} are other classes that are
+ * used by {@code C} (i.e. supertypes, classes that appear in method
+ * parameters, return types) and all their dependencies. Any class
+ * is also considered a dependency of itself.
+ *
+ * Dependencies allow JBR to validate whole set of interfaces for
+ * a particular feature instead of treating them as separate entities.
+ *
Example
+ * Suppose we implemented some feature and added some API for it:
+ * {@code
+ * interface SomeFeature {
+ * SomeOtherObject createSomeObject(int magicNumber);
+ * }
+ * interface SomeOtherObject {
+ * int getMagicNumber();
+ * }
+ * }
+ * And then used it:
+ * {@code
+ * if (JBR.isSomeFeatureSupported()) {
+ * SomeOtherObject object = JBR.getSomeFeature().createSomeObject(123);
+ * int magic = object.getMagicNumber();
+ * }
+ * }
+ * Suppose JBR was able to find implementation for {@code SomeFeature.createSomeObject()},
+ * but not for {@code SomeOtherObject.getMagicNumber()}. So {@code JBR.getSomeFeature()}
+ * would succeed and return service instance, but {@code createSomeObject()} would fail,
+ * because JBR wasn't able to find implementation for {@code SomeOtherObject.getMagicNumber()}
+ * and therefore couldn't create proxy for {@code SomeOtherObject} class.
+ *
+ * To avoid such issues, not only proxy interface itself, but all proxies that are accessible
+ * from current proxy interface must have proper implementation.
+ */
+class ProxyDependencyManager {
+
+ private static final ConcurrentMap, Set>> cache = new ConcurrentHashMap<>();
+
+ /**
+ * @return all proxy interfaces that are used (directly or indirectly) by given interface, including itself.
+ */
+ static Set> getProxyDependencies(Class> interFace) {
+ Set> dependencies = cache.get(interFace);
+ if (dependencies != null) return dependencies;
+ step(null, interFace);
+ return cache.get(interFace);
+ }
+
+ /**
+ * Collect dependencies for given class and store them into cache.
+ */
+ private static void step(Node parent, Class> clazz) {
+ if (!clazz.getPackageName().startsWith("com.jetbrains") && !JBRApi.isKnownProxyInterface(clazz)) return;
+ if (parent != null && parent.findAndMergeCycle(clazz) != null) {
+ return;
+ }
+ Set> cachedDependencies = cache.get(clazz);
+ if (cachedDependencies != null) {
+ if (parent != null) parent.cycle.dependencies.addAll(cachedDependencies);
+ return;
+ }
+ Node node = new Node(parent, clazz);
+ ClassUsagesFinder.visitUsages(clazz, c -> step(node, c));
+ Class> correspondingProxyInterface = JBRApi.getProxyInterfaceByTargetName(clazz.getName());
+ if (correspondingProxyInterface != null) {
+ step(node, correspondingProxyInterface);
+ }
+ if (parent != null && parent.cycle != node.cycle) {
+ parent.cycle.dependencies.addAll(node.cycle.dependencies);
+ }
+ if (node.cycle.origin.equals(clazz)) {
+ // Put collected dependencies into cache only when we exit from the cycle
+ // Otherwise cache will contain incomplete data
+ for (Class> c : node.cycle.members) {
+ cache.put(c, node.cycle.dependencies);
+ if (JBRApi.VERBOSE) {
+ System.out.println("Found dependencies for " + c.getName() + ": " + node.cycle.dependencies);
+ }
+ }
+ }
+ }
+
+ /**
+ * Graph node, one per visited class
+ */
+ private static class Node {
+ private final Node parent;
+ private final Class> clazz;
+ private Cycle cycle;
+
+ private Node(Node parent, Class> clazz) {
+ this.parent = parent;
+ this.clazz = clazz;
+ cycle = new Cycle(clazz);
+ }
+
+ /**
+ * When classes form dependency cycle, they share all their dependencies.
+ * If cycle was found, merge all found dependencies for nodes that form the cycle.
+ */
+ private Cycle findAndMergeCycle(Class> clazz) {
+ if (this.clazz.equals(clazz)) return cycle;
+ if (parent == null) return null;
+ Cycle c = parent.findAndMergeCycle(clazz);
+ if (c != null && c != cycle) {
+ c.members.addAll(cycle.members);
+ c.dependencies.addAll(cycle.dependencies);
+ cycle = c;
+ }
+ return c;
+ }
+ }
+
+ /**
+ * Cycle info. For the sake of elegant code, single node
+ * also forms a cycle with itself as a single member and dependency.
+ */
+ private static class Cycle {
+ /**
+ * Origin is the first visited class from that cycle.
+ */
+ private final Class> origin;
+ private final Set> members = new HashSet<>();
+ private final Set> dependencies = new HashSet<>();
+
+ private Cycle(Class> origin) {
+ this.origin = origin;
+ members.add(origin);
+ if (JBRApi.isKnownProxyInterface(origin)) {
+ dependencies.add(origin);
+ }
+ }
+ }
+
+ /**
+ * Utility class that collects direct class usages using reflection
+ */
+ private static class ClassUsagesFinder {
+
+ private static void visitUsages(Class> c, Consumer> action) {
+ collect(c.getGenericSuperclass(), action);
+ for (java.lang.reflect.Type t : c.getGenericInterfaces()) collect(t, action);
+ for (Field f : c.getDeclaredFields()) collect(f.getGenericType(), action);
+ for (Method m : c.getDeclaredMethods()) {
+ collect(m.getGenericParameterTypes(), action);
+ collect(m.getGenericReturnType(), action);
+ collect(m.getGenericExceptionTypes(), action);
+ }
+ }
+
+ private static void collect(java.lang.reflect.Type type, Consumer> action) {
+ if (type instanceof Class> c) {
+ while (c.isArray()) c = Objects.requireNonNull(c.getComponentType());
+ if (!c.isPrimitive()) action.accept(c);
+ } else if (type instanceof TypeVariable> v) {
+ collect(v.getBounds(), action);
+ } else if (type instanceof WildcardType w) {
+ collect(w.getUpperBounds(), action);
+ collect(w.getLowerBounds(), action);
+ } else if (type instanceof ParameterizedType p) {
+ collect(p.getActualTypeArguments(), action);
+ collect(p.getRawType(), action);
+ collect(p.getOwnerType(), action);
+ } else if (type instanceof GenericArrayType a) {
+ collect(a.getGenericComponentType(), action);
+ }
+ }
+
+ private static void collect(java.lang.reflect.Type[] types, Consumer> action) {
+ for (java.lang.reflect.Type t : types) collect(t, action);
+ }
+ }
+}
diff --git a/src/java.base/share/classes/com/jetbrains/internal/ProxyGenerator.java b/src/java.base/share/classes/com/jetbrains/internal/ProxyGenerator.java
new file mode 100644
index 000000000000..ca0b37e233f0
--- /dev/null
+++ b/src/java.base/share/classes/com/jetbrains/internal/ProxyGenerator.java
@@ -0,0 +1,485 @@
+/*
+ * Copyright 2000-2023 JetBrains s.r.o.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.jetbrains.internal;
+
+import jdk.internal.org.objectweb.asm.*;
+import jdk.internal.org.objectweb.asm.util.CheckClassAdapter;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.*;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Supplier;
+
+import static com.jetbrains.internal.ASMUtils.*;
+import static java.lang.invoke.MethodHandles.Lookup;
+import static jdk.internal.org.objectweb.asm.Opcodes.*;
+
+/**
+ * This class generates {@linkplain Proxy proxy} classes.
+ * Each proxy is just a generated class implementing some interface and
+ * delegating method calls to method handles.
+ *
+ * There are 2 proxy dispatch modes:
+ *
+ * interface -> proxy -> {@linkplain #generateBridge bridge} -> method handle -> implementation code
+ * interface -> proxy -> method handle -> implementation code
+ *
+ * Generated proxy is always located in the same package with its interface and optional bridge is located in the
+ * same module with target implementation code. Bridge allows proxy to safely call hidden non-static implementation
+ * methods and is only needed for {@code jetbrains.api} -> JBR calls. For JBR -> {@code jetbrains.api} calls, proxy can
+ * invoke method handle directly.
+ */
+class ProxyGenerator {
+ private static final String OBJECT_DESCRIPTOR = "Ljava/lang/Object;";
+ private static final String MH_NAME = "java/lang/invoke/MethodHandle";
+ private static final String MH_DESCRIPTOR = "Ljava/lang/invoke/MethodHandle;";
+ private static final String CONVERSION_DESCRIPTOR = "(Ljava/lang/Object;)Ljava/lang/Object;";
+ /**
+ * Print warnings about usage of deprecated interfaces and methods to {@link System#err}.
+ */
+ private static final boolean LOG_DEPRECATED = System.getProperty("jetbrains.api.logDeprecated", String.valueOf(JBRApi.VERBOSE)).equalsIgnoreCase("true");
+ private static final boolean VERIFY_BYTECODE = Boolean.getBoolean("jetbrains.api.verifyBytecode");
+
+ private static final AtomicInteger nameCounter = new AtomicInteger();
+
+ private final ProxyInfo info;
+ private final boolean generateBridge;
+ private final String proxyName, bridgeName;
+ private final ClassWriter originalProxyWriter, originalBridgeWriter;
+ private final ClassVisitor proxyWriter, bridgeWriter;
+ private final List> handles = new ArrayList<>();
+ private final List>> classReferences = new ArrayList<>();
+ private final Set> directProxyDependencies = new HashSet<>();
+ private final List exceptions = new ArrayList<>();
+ private int bridgeMethodCounter;
+ private boolean allMethodsImplemented = true;
+ private Lookup generatedHandlesHolder, generatedProxy;
+
+ /**
+ * Creates new proxy generator from given {@link ProxyInfo},
+ * looks for abstract interface methods, corresponding implementation methods
+ * and generates proxy bytecode. However, it doesn't actually load generated
+ * classes until {@link #defineClasses()} is called.
+ */
+ ProxyGenerator(ProxyInfo info) {
+ if (JBRApi.VERBOSE) {
+ System.out.println("Generating proxy " + info.interFace.getName());
+ }
+ this.info = info;
+ generateBridge = info.type.isPublicApi();
+ int nameId = nameCounter.getAndIncrement();
+ proxyName = Type.getInternalName(info.interFace) + "$$JBRApiProxy$" + nameId;
+ bridgeName = generateBridge ? info.apiModule.lookupClass().getPackageName().replace('.', '/') + "/" +
+ info.interFace.getSimpleName() + "$$JBRApiBridge$" + nameId : null;
+
+ originalProxyWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
+ proxyWriter = VERIFY_BYTECODE ? new CheckClassAdapter(originalProxyWriter, true) : originalProxyWriter;
+ originalBridgeWriter = generateBridge ? new ClassWriter(ClassWriter.COMPUTE_FRAMES) : null;
+ if (generateBridge) {
+ bridgeWriter = VERIFY_BYTECODE ? new CheckClassAdapter(originalBridgeWriter, true) : originalBridgeWriter;
+ } else bridgeWriter = new ClassVisitor(Opcodes.ASM9) { // Empty visitor
+ @Override
+ public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
+ return new MethodVisitor(api) {};
+ }
+ };
+ proxyWriter.visit(CLASSFILE_VERSION, ACC_SUPER | ACC_FINAL | ACC_SYNTHETIC, proxyName, null,
+ "java/lang/Object", new String[] {Type.getInternalName(info.interFace)});
+ bridgeWriter.visit(CLASSFILE_VERSION, ACC_SUPER | ACC_FINAL | ACC_SYNTHETIC | ACC_PUBLIC, bridgeName, null,
+ "java/lang/Object", null);
+ generateConstructor();
+ generateMethods();
+ proxyWriter.visitEnd();
+ bridgeWriter.visitEnd();
+ }
+
+ boolean areAllMethodsImplemented() {
+ return allMethodsImplemented;
+ }
+
+ Set> getDirectProxyDependencies() {
+ return directProxyDependencies;
+ }
+
+ /**
+ * Insert all method handles and class references into static fields, so that proxy can call implementation methods.
+ */
+ void init() {
+ try {
+ for (int i = 0; i < handles.size(); i++) {
+ generatedHandlesHolder
+ .findStaticVarHandle(generatedHandlesHolder.lookupClass(), "h" + i, MethodHandle.class)
+ .set(handles.get(i).get());
+ }
+ for (int i = 0; i < classReferences.size(); i++) {
+ generatedHandlesHolder
+ .findStaticVarHandle(generatedHandlesHolder.lookupClass(), "c" + i, Class.class)
+ .set(classReferences.get(i).get());
+ }
+ } catch (NoSuchFieldException | IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ Class> getProxyClass() {
+ return generatedProxy.lookupClass();
+ }
+
+ /**
+ * @return method handle to constructor of generated proxy class.
+ *
+ * For {@linkplain ProxyInfo.Type#SERVICE services}, constructor is no-arg.
+ * For non-{@linkplain ProxyInfo.Type#SERVICE services}, constructor is single-arg,
+ * expecting target object to which it would delegate method calls.
+ *
+ */
+ MethodHandle findConstructor() {
+ try {
+ if (info.target == null) {
+ return generatedProxy.findConstructor(generatedProxy.lookupClass(), MethodType.methodType(void.class));
+ } else {
+ MethodHandle c = generatedProxy.findConstructor(generatedProxy.lookupClass(),
+ MethodType.methodType(void.class, Object.class));
+ if (info.type.isService()) {
+ try {
+ return MethodHandles.foldArguments(c, info.target.findConstructor(info.target.lookupClass(),
+ MethodType.methodType(void.class)).asType(MethodType.methodType(Object.class)));
+ } catch (NoSuchMethodException | IllegalAccessException e) {
+ throw new RuntimeException("Service implementation must have no-args constructor: " +
+ info.target.lookupClass(), e);
+ }
+ }
+ return c;
+ }
+ } catch (IllegalAccessException | NoSuchMethodException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * @return method handle that receives proxy and returns its target, or null
+ */
+ MethodHandle findTargetExtractor() {
+ if (info.target == null) return null;
+ try {
+ return generatedProxy.findGetter(generatedProxy.lookupClass(), "target", Object.class);
+ } catch (NoSuchFieldException | IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Loads generated classes.
+ */
+ void defineClasses() {
+ try {
+ Lookup bridge = !generateBridge ? null : MethodHandles.privateLookupIn(
+ info.apiModule.defineClass(originalBridgeWriter.toByteArray()), info.apiModule);
+ generatedProxy = info.interFaceLookup.defineHiddenClass(
+ originalProxyWriter.toByteArray(), true, Lookup.ClassOption.STRONG, Lookup.ClassOption.NESTMATE);
+ generatedHandlesHolder = generateBridge ? bridge : generatedProxy;
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private void generateConstructor() {
+ if (info.target != null) {
+ proxyWriter.visitField(ACC_PRIVATE | ACC_FINAL, "target", OBJECT_DESCRIPTOR, null, null);
+ }
+ MethodVisitor p = proxyWriter.visitMethod(ACC_PRIVATE, "", "(" +
+ (info.target == null ? "" : OBJECT_DESCRIPTOR) + ")V", null, null);
+ if (LOG_DEPRECATED && info.interFace.isAnnotationPresent(Deprecated.class)) {
+ logDeprecated(p, "Warning: using deprecated JBR API interface " + info.interFace.getName());
+ }
+ p.visitCode();
+ p.visitVarInsn(ALOAD, 0);
+ if (info.target != null) {
+ p.visitInsn(DUP);
+ p.visitVarInsn(ALOAD, 1);
+ p.visitFieldInsn(PUTFIELD, proxyName, "target", OBJECT_DESCRIPTOR);
+ }
+ p.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V", false);
+ p.visitInsn(RETURN);
+ p.visitMaxs(0, 0);
+ p.visitEnd();
+ }
+
+ private void generateMethods() {
+ for (Method method : info.interFace.getMethods()) {
+ int mod = method.getModifiers();
+ if (!Modifier.isAbstract(mod)) continue;
+ MethodMapping methodMapping = getTargetMethodMapping(method);
+
+ Exception e1 = null;
+ if (info.target != null) {
+ try {
+ MethodHandle handle = info.target.findVirtual(
+ info.target.lookupClass(), method.getName(), methodMapping.type());
+ generateMethod(method, handle, methodMapping, true);
+ continue;
+ } catch (NoSuchMethodException | IllegalAccessException e) {
+ e1 = e;
+ }
+ }
+
+ Exception e2 = null;
+ ProxyInfo.StaticMethodMapping mapping = info.staticMethods.get(method.getName());
+ if (mapping != null) {
+ try {
+ MethodHandle staticHandle =
+ mapping.lookup().findStatic(mapping.lookup().lookupClass(), mapping.methodName(), methodMapping.type());
+ generateMethod(method, staticHandle, methodMapping, false);
+ continue;
+ } catch (NoSuchMethodException | IllegalAccessException e) {
+ e2 = e;
+ }
+ }
+
+ if (e1 != null) exceptions.add(e1);
+ if (e2 != null) exceptions.add(e2);
+ generateUnsupportedMethod(proxyWriter, method);
+ if (JBRApi.VERBOSE) {
+ System.err.println("Couldn't generate method " + method.getName());
+ if (e1 != null) e1.printStackTrace();
+ if (e2 != null) e2.printStackTrace();
+ }
+ allMethodsImplemented = false;
+ }
+ }
+
+ private void generateMethod(Method interfaceMethod, MethodHandle handle, MethodMapping mapping, boolean passInstance) {
+ InternalMethodInfo methodInfo = getInternalMethodInfo(interfaceMethod);
+ String bridgeMethodDescriptor = mapping.getBridgeDescriptor(passInstance);
+
+ ClassVisitor handleWriter = generateBridge ? bridgeWriter : proxyWriter;
+ String bridgeOrProxyName = generateBridge ? bridgeName : proxyName;
+ String handleName = addHandle(handleWriter, () -> handle);
+ for (TypeMapping m : mapping) {
+ if (m.conversion() == TypeConversion.EXTRACT_TARGET ||
+ m.conversion() == TypeConversion.DYNAMIC_2_WAY) {
+ Proxy> from = m.fromProxy();
+ m.metadata.extractTargetHandle = addHandle(handleWriter, from::getTargetExtractor);
+ directProxyDependencies.add(from);
+ }
+ if (m.conversion() == TypeConversion.WRAP_INTO_PROXY ||
+ m.conversion() == TypeConversion.DYNAMIC_2_WAY) {
+ Proxy> to = m.toProxy();
+ m.metadata.proxyConstructorHandle = addHandle(handleWriter, to::getConstructor);
+ directProxyDependencies.add(to);
+ }
+ if (m.conversion() == TypeConversion.DYNAMIC_2_WAY) {
+ String classField = "c" + classReferences.size();
+ m.metadata.extractableClassField = classField;
+ classReferences.add(m.fromProxy()::getProxyClass);
+ handleWriter.visitField(ACC_PRIVATE | ACC_STATIC, classField, "Ljava/lang/Class;", null, null);
+ }
+ }
+ String bridgeMethodName = methodInfo.name() + "$bridge$" + bridgeMethodCounter;
+ bridgeMethodCounter++;
+
+ MethodVisitor p = proxyWriter.visitMethod(ACC_PUBLIC | ACC_FINAL, methodInfo.name(),
+ methodInfo.descriptor(), methodInfo.genericSignature(), methodInfo.exceptionNames());
+ MethodVisitor b = bridgeWriter.visitMethod(ACC_PUBLIC | ACC_STATIC, bridgeMethodName,
+ bridgeMethodDescriptor, null, null);
+ if (LOG_DEPRECATED && interfaceMethod.isAnnotationPresent(Deprecated.class)) {
+ logDeprecated(p, "Warning: using deprecated JBR API method " +
+ interfaceMethod.getDeclaringClass().getName() + "#" + interfaceMethod.getName());
+ }
+ p.visitCode();
+ b.visitCode();
+ MethodVisitor bp = generateBridge ? b : p;
+ bp.visitFieldInsn(GETSTATIC, bridgeOrProxyName, handleName, MH_DESCRIPTOR);
+ if (passInstance) {
+ p.visitVarInsn(ALOAD, 0);
+ p.visitFieldInsn(GETFIELD, proxyName, "target", OBJECT_DESCRIPTOR);
+ b.visitVarInsn(ALOAD, 0);
+ }
+ int lvIndex = 1;
+ for (TypeMapping param : mapping.parameterMapping) {
+ int opcode = getLoadOpcode(param.from());
+ p.visitVarInsn(opcode, lvIndex);
+ b.visitVarInsn(opcode, lvIndex - (passInstance ? 0 : 1));
+ lvIndex += getParameterSize(param.from());
+ convertValue(bp, bridgeOrProxyName, param);
+ }
+ if (generateBridge) {
+ p.visitMethodInsn(INVOKESTATIC, bridgeName, bridgeMethodName, bridgeMethodDescriptor, false);
+ }
+ bp.visitMethodInsn(INVOKEVIRTUAL, MH_NAME, "invoke", bridgeMethodDescriptor, false);
+ convertValue(bp, bridgeOrProxyName, mapping.returnMapping());
+ int returnOpcode = getReturnOpcode(mapping.returnMapping().to());
+ p.visitInsn(returnOpcode);
+ b.visitInsn(returnOpcode);
+ p.visitMaxs(0, 0);
+ b.visitMaxs(0, 0);
+ p.visitEnd();
+ b.visitEnd();
+ }
+
+ private String addHandle(ClassVisitor classWriter, Supplier handleSupplier) {
+ String handleName = "h" + handles.size();
+ handles.add(handleSupplier);
+ classWriter.visitField(ACC_PRIVATE | ACC_STATIC, handleName, MH_DESCRIPTOR, null, null);
+ return handleName;
+ }
+
+ private static void convertValue(MethodVisitor m, String handlesHolderName, TypeMapping mapping) {
+ if (mapping.conversion() == TypeConversion.IDENTITY) return;
+ Label skipConvert = new Label();
+ m.visitInsn(DUP);
+ m.visitJumpInsn(IFNULL, skipConvert);
+ switch (mapping.conversion()) {
+ case EXTRACT_TARGET ->
+ m.visitFieldInsn(GETSTATIC, handlesHolderName, mapping.metadata.extractTargetHandle, MH_DESCRIPTOR);
+ case WRAP_INTO_PROXY ->
+ m.visitFieldInsn(GETSTATIC, handlesHolderName, mapping.metadata.proxyConstructorHandle, MH_DESCRIPTOR);
+ case DYNAMIC_2_WAY -> {
+ m.visitInsn(DUP);
+ m.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "getClass", "()Ljava/lang/Class;", false);
+ m.visitFieldInsn(GETSTATIC, handlesHolderName, mapping.metadata.extractableClassField, "Ljava/lang/Class;");
+ Label elseBranch = new Label(), afterBranch = new Label();
+ m.visitJumpInsn(IF_ACMPNE, elseBranch);
+ m.visitFieldInsn(GETSTATIC, handlesHolderName, mapping.metadata.extractTargetHandle, MH_DESCRIPTOR);
+ m.visitJumpInsn(GOTO, afterBranch);
+ m.visitLabel(elseBranch);
+ m.visitFieldInsn(GETSTATIC, handlesHolderName, mapping.metadata.proxyConstructorHandle, MH_DESCRIPTOR);
+ m.visitLabel(afterBranch);
+ }
+ }
+ m.visitInsn(SWAP);
+ m.visitMethodInsn(INVOKEVIRTUAL, MH_NAME, "invoke", CONVERSION_DESCRIPTOR, false);
+ m.visitLabel(skipConvert);
+ }
+
+ private static MethodMapping getTargetMethodMapping(Method interfaceMethod) {
+ Class>[] params = interfaceMethod.getParameterTypes();
+ TypeMapping[] paramMappings = new TypeMapping[params.length];
+ for (int i = 0; i < params.length; i++) {
+ paramMappings[i] = getTargetTypeMapping(params[i]);
+ params[i] = paramMappings[i].to();
+ }
+ TypeMapping returnMapping = getTargetTypeMapping(interfaceMethod.getReturnType()).inverse();
+ return new MethodMapping(MethodType.methodType(returnMapping.from(), params), returnMapping, paramMappings);
+ }
+
+ private static TypeMapping getTargetTypeMapping(Class userType) {
+ TypeMappingMetadata m = new TypeMappingMetadata();
+ Proxy p = JBRApi.getProxy(userType);
+ if (p != null && p.getInfo().target != null) {
+ Proxy> r = JBRApi.getProxy(p.getInfo().target.lookupClass());
+ if (r != null && r.getInfo().target != null) {
+ return new TypeMapping(userType, p.getInfo().target.lookupClass(), TypeConversion.DYNAMIC_2_WAY, p, r, m);
+ }
+ return new TypeMapping(userType, p.getInfo().target.lookupClass(), TypeConversion.EXTRACT_TARGET, p, null, m);
+ }
+ Class> interFace = JBRApi.getProxyInterfaceByTargetName(userType.getName());
+ if (interFace != null) {
+ Proxy> r = JBRApi.getProxy(interFace);
+ if (r != null) {
+ return new TypeMapping(userType, interFace, TypeConversion.WRAP_INTO_PROXY, null, r, m);
+ }
+ }
+ return new TypeMapping(userType, userType, TypeConversion.IDENTITY, null, null, m);
+ }
+
+ private record MethodMapping(MethodType type, TypeMapping returnMapping, TypeMapping[] parameterMapping)
+ implements Iterable {
+ @Override
+ public Iterator iterator() {
+ return new Iterator<>() {
+ private int index = -1;
+ @Override
+ public boolean hasNext() {
+ return index < parameterMapping.length;
+ }
+ @Override
+ public TypeMapping next() {
+ TypeMapping m = index == -1 ? returnMapping : parameterMapping[index];
+ index++;
+ return m;
+ }
+ };
+ }
+
+ /**
+ * Every convertable parameter type is replaced with {@link Object} for bridge descriptor.
+ * Optional {@link Object} is added as first parameter for instance methods.
+ */
+ String getBridgeDescriptor(boolean passInstance) {
+ StringBuilder bd = new StringBuilder("(");
+ if (passInstance) bd.append(OBJECT_DESCRIPTOR);
+ for (TypeMapping m : parameterMapping) {
+ bd.append(m.getBridgeDescriptor());
+ }
+ bd.append(')');
+ bd.append(returnMapping.getBridgeDescriptor());
+ return bd.toString();
+ }
+ }
+
+ private record TypeMapping(Class> from, Class> to, TypeConversion conversion,
+ Proxy> fromProxy, Proxy> toProxy, TypeMappingMetadata metadata) {
+ TypeMapping inverse() {
+ return new TypeMapping(to, from, switch (conversion) {
+ case EXTRACT_TARGET -> TypeConversion.WRAP_INTO_PROXY;
+ case WRAP_INTO_PROXY -> TypeConversion.EXTRACT_TARGET;
+ default -> conversion;
+ }, toProxy, fromProxy, metadata);
+ }
+ String getBridgeDescriptor() {
+ if (conversion == TypeConversion.IDENTITY) return Type.getDescriptor(from);
+ else return "Ljava/lang/Object;";
+ }
+ }
+
+ private static class TypeMappingMetadata {
+ private String extractTargetHandle, proxyConstructorHandle, extractableClassField;
+ }
+
+ private enum TypeConversion {
+ /**
+ * No conversion.
+ */
+ IDENTITY,
+ /**
+ * Take a proxy object and extract its target implementation object.
+ */
+ EXTRACT_TARGET,
+ /**
+ * Create new proxy targeting given implementation object.
+ */
+ WRAP_INTO_PROXY,
+ /**
+ * Decide between {@link #EXTRACT_TARGET} and {@link #WRAP_INTO_PROXY} at runtime, depending on actual object.
+ */
+ DYNAMIC_2_WAY
+ }
+}
diff --git a/src/java.base/share/classes/com/jetbrains/internal/ProxyInfo.java b/src/java.base/share/classes/com/jetbrains/internal/ProxyInfo.java
new file mode 100644
index 000000000000..649747496da7
--- /dev/null
+++ b/src/java.base/share/classes/com/jetbrains/internal/ProxyInfo.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2000-2023 JetBrains s.r.o.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.jetbrains.internal;
+
+import java.lang.invoke.MethodHandles;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.stream.Stream;
+
+import static java.lang.invoke.MethodHandles.Lookup;
+
+/**
+ * Proxy info, like {@link RegisteredProxyInfo}, but with all classes and lookup contexts resolved.
+ * Contains all necessary information to create a {@linkplain Proxy proxy}.
+ */
+class ProxyInfo {
+
+ final Lookup apiModule;
+ final Type type;
+ final Lookup interFaceLookup;
+ final Class> interFace;
+ final Lookup target;
+ final Map staticMethods = new HashMap<>();
+
+ private ProxyInfo(RegisteredProxyInfo i) {
+ this.apiModule = i.apiModule();
+ type = i.type();
+ interFaceLookup = lookup(getInterfaceLookup(), i.interfaceName());
+ interFace = interFaceLookup == null ? null : interFaceLookup.lookupClass();
+ target = Stream.of(i.targets())
+ .map(t -> lookup(getTargetLookup(), t))
+ .filter(Objects::nonNull).findFirst().orElse(null);
+ for (RegisteredProxyInfo.StaticMethodMapping m : i.staticMethods()) {
+ Stream.of(m.classes())
+ .map(t -> lookup(getTargetLookup(), t))
+ .filter(Objects::nonNull).findFirst()
+ .ifPresent(l -> staticMethods.put(m.interfaceMethodName(), new StaticMethodMapping(l, m.methodName())));
+ }
+ }
+
+ /**
+ * Resolve all classes and lookups for given {@link RegisteredProxyInfo}.
+ */
+ static ProxyInfo resolve(RegisteredProxyInfo i) {
+ ProxyInfo info = new ProxyInfo(i);
+ if (info.interFace == null || (info.target == null && info.staticMethods.isEmpty())) return null;
+ if (!info.interFace.isInterface()) {
+ if (info.type == Type.CLIENT_PROXY) {
+ throw new RuntimeException("Tried to create client proxy for non-interface: " + info.interFace);
+ } else {
+ return null;
+ }
+ }
+ return info;
+ }
+
+ Lookup getInterfaceLookup() {
+ return type == Type.CLIENT_PROXY || type == Type.INTERNAL_SERVICE ? apiModule : JBRApi.outerLookup;
+ }
+
+ Lookup getTargetLookup() {
+ return type == Type.CLIENT_PROXY ? JBRApi.outerLookup : apiModule;
+ }
+
+ private Lookup lookup(Lookup lookup, String clazz) {
+ String[] nestedClasses = clazz.split("\\$");
+ clazz = "";
+ for (int i = 0; i < nestedClasses.length; i++) {
+ try {
+ if (i != 0) clazz += "$";
+ clazz += nestedClasses[i];
+ lookup = MethodHandles.privateLookupIn(Class.forName(clazz, false, lookup.lookupClass().getClassLoader()), lookup);
+ } catch (ClassNotFoundException | IllegalAccessException ignore) {
+ return null;
+ }
+ }
+ return lookup;
+ }
+
+ record StaticMethodMapping(Lookup lookup, String methodName) {}
+
+ /**
+ * Proxy type, see {@link Proxy}
+ */
+ enum Type {
+ PROXY,
+ SERVICE,
+ CLIENT_PROXY,
+ INTERNAL_SERVICE;
+
+ public boolean isPublicApi() {
+ return this == PROXY || this == SERVICE;
+ }
+
+ public boolean isService() {
+ return this == SERVICE || this == INTERNAL_SERVICE;
+ }
+ }
+}
diff --git a/src/java.base/share/classes/com/jetbrains/internal/RegisteredProxyInfo.java b/src/java.base/share/classes/com/jetbrains/internal/RegisteredProxyInfo.java
new file mode 100644
index 000000000000..ec636e82b39f
--- /dev/null
+++ b/src/java.base/share/classes/com/jetbrains/internal/RegisteredProxyInfo.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2000-2023 JetBrains s.r.o.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.jetbrains.internal;
+
+import java.lang.invoke.MethodHandles;
+import java.util.List;
+
+/**
+ * Raw proxy info, as it was registered through {@link JBRApi.ModuleRegistry}.
+ * Contains all necessary information to create a {@linkplain Proxy proxy}.
+ */
+record RegisteredProxyInfo(MethodHandles.Lookup apiModule,
+ String interfaceName,
+ String[] targets,
+ ProxyInfo.Type type,
+ List staticMethods) {
+
+ record StaticMethodMapping(String interfaceMethodName, String methodName, String[] classes) {}
+}
diff --git a/src/java.base/share/classes/java/lang/Throwable.java b/src/java.base/share/classes/java/lang/Throwable.java
index 1cd1a107a895..cd9815ad59f7 100644
--- a/src/java.base/share/classes/java/lang/Throwable.java
+++ b/src/java.base/share/classes/java/lang/Throwable.java
@@ -1152,4 +1152,20 @@ public final synchronized Throwable[] getSuppressed() {
else
return suppressedExceptions.toArray(EMPTY_THROWABLE_ARRAY);
}
+
+ private static volatile java.util.function.Supplier $$jb$additionalInfoSupplier = null;
+
+ // JBR API internals
+ private static void $$jb$additionalInfoForJstack(java.util.function.Supplier supplier) {
+ $$jb$additionalInfoSupplier = supplier;
+ }
+
+ // NB: this is invoked from the VM
+ private static String $$jb$getAdditionalInfoForJstack() {
+ final java.util.function.Supplier supplier = $$jb$additionalInfoSupplier;
+ if (supplier != null) {
+ return supplier.get();
+ }
+ return null;
+ }
}
diff --git a/src/java.base/share/classes/java/net/UnixDomainSocketAddress.java b/src/java.base/share/classes/java/net/UnixDomainSocketAddress.java
index 4940e0c4794d..da46a8399a94 100644
--- a/src/java.base/share/classes/java/net/UnixDomainSocketAddress.java
+++ b/src/java.base/share/classes/java/net/UnixDomainSocketAddress.java
@@ -160,13 +160,19 @@ public static UnixDomainSocketAddress of(String pathname) {
* @throws NullPointerException if path is {@code null}
*/
public static UnixDomainSocketAddress of(Path path) {
- FileSystem fs = path.getFileSystem();
- if (fs != FileSystems.getDefault()) {
- throw new IllegalArgumentException();
- }
- if (fs.getClass().getModule() != Object.class.getModule()) {
- throw new IllegalArgumentException();
+ final FileSystem fs = path.getFileSystem();
+ final FileSystem defaultFS = sun.nio.fs.DefaultFileSystemProvider.theFileSystem();
+ if (fs != defaultFS || fs.getClass().getModule() != Object.class.getModule()) {
+ try {
+ // Check if we'll be able to create a socket from this Path later on by
+ // testing for the presence of a method identical to
+ // AbstractFileSystemProvider.getSunPathForSocketFile()
+ fs.provider().getClass().getMethod("getSunPathForSocketFile", Path.class);
+ } catch (NoSuchMethodException | SecurityException e) {
+ throw new IllegalArgumentException();
+ }
}
+
return new UnixDomainSocketAddress(path);
}
diff --git a/src/java.base/share/classes/module-info.java b/src/java.base/share/classes/module-info.java
index 17a66b856a1e..8b31c5adf330 100644
--- a/src/java.base/share/classes/module-info.java
+++ b/src/java.base/share/classes/module-info.java
@@ -136,10 +136,13 @@
exports javax.security.auth.x500;
exports javax.security.cert;
-
// additional qualified exports may be inserted at build time
// see make/gensrc/GenModuleInfo.gmk
+ opens com.jetbrains.bootstrap;
+
+ exports com.jetbrains.internal to
+ java.desktop;
exports com.sun.crypto.provider to
jdk.crypto.cryptoki;
exports sun.invoke.util to
diff --git a/src/java.base/share/classes/sun/nio/ch/UnixDomainSockets.java b/src/java.base/share/classes/sun/nio/ch/UnixDomainSockets.java
index 251e79c6ecc6..fc34f46defe6 100644
--- a/src/java.base/share/classes/sun/nio/ch/UnixDomainSockets.java
+++ b/src/java.base/share/classes/sun/nio/ch/UnixDomainSockets.java
@@ -99,8 +99,22 @@ static UnixDomainSocketAddress checkAddress(SocketAddress sa) {
}
static byte[] getPathBytes(Path path) {
- FileSystemProvider provider = FileSystems.getDefault().provider();
- return ((AbstractFileSystemProvider) provider).getSunPathForSocketFile(path);
+ java.nio.file.FileSystem fs = path.getFileSystem();
+ FileSystemProvider provider = fs.provider();
+ if (fs == sun.nio.fs.DefaultFileSystemProvider.theFileSystem()) {
+ return ((AbstractFileSystemProvider) provider).getSunPathForSocketFile(path);
+ } else {
+ try {
+ java.lang.reflect.Method method = provider.getClass().getMethod("getSunPathForSocketFile", Path.class);
+ Object result = method.invoke(provider, path);
+ return (byte[]) result;
+ } catch (NoSuchMethodException | SecurityException e) {
+ // This should've been verified when UnixDomainSocketAddress was created
+ throw new Error("Can't find getSunPathForSocketFile(Path) in the non-default file system provider " + provider.getClass());
+ } catch (java.lang.reflect.InvocationTargetException | IllegalAccessException e) {
+ throw new RuntimeException("Can't invoke getSunPathForSocketFile(Path) from a non-default file system provider", e);
+ }
+ }
}
static FileDescriptor socket() throws IOException {
@@ -136,13 +150,11 @@ static UnixDomainSocketAddress generateTempName() throws IOException {
int rnd = random.nextInt(Integer.MAX_VALUE);
try {
final Path path = Path.of(dir, "socket_" + rnd);
- if (path.getFileSystem().provider() != sun.nio.fs.DefaultFileSystemProvider.instance()) {
- throw new UnsupportedOperationException(
- "Unix Domain Sockets not supported on non-default file system");
- }
return UnixDomainSocketAddress.of(path);
} catch (InvalidPathException e) {
throw new BindException("Invalid temporary directory");
+ } catch (IllegalArgumentException e) {
+ throw new UnsupportedOperationException("Unix Domain Sockets not supported on non-default file system without method getSunPathForSocketFile(Path)");
}
}
diff --git a/src/java.base/share/native/libjava/NativeLibraries.c b/src/java.base/share/native/libjava/NativeLibraries.c
index 229863060170..fa6c3572ac99 100644
--- a/src/java.base/share/native/libjava/NativeLibraries.c
+++ b/src/java.base/share/native/libjava/NativeLibraries.c
@@ -115,7 +115,6 @@ Java_jdk_internal_loader_NativeLibraries_load
(JNIEnv *env, jclass cls, jobject lib, jstring name,
jboolean isBuiltin, jboolean throwExceptionIfFail)
{
- const char *cname;
jint jniVersion;
jthrowable cause;
void * handle;
@@ -124,14 +123,15 @@ Java_jdk_internal_loader_NativeLibraries_load
if (!initIDs(env))
return JNI_FALSE;
- cname = JNU_GetStringPlatformChars(env, name, 0);
- if (cname == 0)
+ const char * utf8_name = GetStringUTF8Chars(env, name);
+ if (utf8_name == NULL)
return JNI_FALSE;
- handle = isBuiltin ? procHandle : JVM_LoadLibrary(cname, throwExceptionIfFail);
+
+ handle = isBuiltin ? procHandle : JVM_LoadLibrary(utf8_name, throwExceptionIfFail);
if (handle) {
JNI_OnLoad_t JNI_OnLoad;
JNI_OnLoad = (JNI_OnLoad_t)findJniFunction(env, handle,
- isBuiltin ? cname : NULL,
+ isBuiltin ? utf8_name : NULL,
JNI_TRUE);
if (JNI_OnLoad) {
JavaVM *jvm;
@@ -156,7 +156,7 @@ Java_jdk_internal_loader_NativeLibraries_load
char msg[256];
jio_snprintf(msg, sizeof(msg),
"unsupported JNI version 0x%08X required by %s",
- jniVersion, cname);
+ jniVersion, utf8_name);
JNU_ThrowByName(env, "java/lang/UnsatisfiedLinkError", msg);
if (!isBuiltin) {
JVM_UnloadLibrary(handle);
@@ -178,7 +178,7 @@ Java_jdk_internal_loader_NativeLibraries_load
loaded = JNI_TRUE;
done:
- JNU_ReleaseStringPlatformChars(env, name, cname);
+ ReleaseStringUTF8Chars(env, name, utf8_name);
return loaded;
}
diff --git a/src/java.base/share/native/libjava/jni_util.c b/src/java.base/share/native/libjava/jni_util.c
index 3d9004d969cb..a10bc28032eb 100644
--- a/src/java.base/share/native/libjava/jni_util.c
+++ b/src/java.base/share/native/libjava/jni_util.c
@@ -722,6 +722,35 @@ newStringUTF8(JNIEnv *env, const char *str)
return newSizedStringJava(env, str, len);
}
+static jobject
+getUTF8Charset(JNIEnv *env) {
+ static jobject utf8Charset = NULL;
+ if (utf8Charset == NULL) {
+ jstring enc = (*env)->NewStringUTF(env, "UTF-8");
+ if (enc == NULL) {
+ return NULL;
+ }
+ jboolean exc;
+ jvalue charset = JNU_CallStaticMethodByName(
+ env, &exc,
+ "java/nio/charset/Charset",
+ "forName",
+ "(Ljava/lang/String;)Ljava/nio/charset/Charset;",
+ enc);
+ if (exc) {
+ (*env)->ExceptionClear(env);
+ }
+ (*env)->DeleteLocalRef(env, enc);
+
+ if (!exc && charset.l != NULL) {
+ utf8Charset = (*env)->NewGlobalRef(env, charset.l);
+ (*env)->DeleteLocalRef(env, charset.l);
+ }
+ }
+
+ return utf8Charset;
+}
+
/* Initialize the fast encoding from the encoding name.
* Export InitializeEncoding so that the VM can initialize it if required.
*/
@@ -849,14 +878,26 @@ GetStringPlatformCharsStrict(JNIEnv *env, jstring jstr, jboolean *isCopy)
return getStringPlatformChars0(env, jstr, isCopy, JNI_TRUE);
}
-static const char* getStringBytes(JNIEnv *env, jstring jstr, jboolean strict) {
+/* Convert the given Java string into a null-terminated byte sequence according
+ * to the platform encoding (if needUTF8 is false) or to UTF-8 encoding (if
+ * needUTF8 is true).
+ */
+static const char* getStringBytes(JNIEnv *env, jstring jstr, jboolean strict, jboolean needUTF8) {
char *result = NULL;
jbyteArray hab = 0;
if ((*env)->EnsureLocalCapacity(env, 2) < 0)
return 0;
- hab = (*env)->CallObjectMethod(env, jstr, String_getBytes_ID, jnuCharset);
+ if (needUTF8) {
+ if (getUTF8Charset(env) == NULL) {
+ return NULL;
+ }
+ hab = (*env)->CallObjectMethod(env, jstr, String_getBytes_ID, getUTF8Charset(env));
+ } else {
+ hab = (*env)->CallObjectMethod(env, jstr, String_getBytes_ID, jnuCharset);
+ }
+
if (hab != 0) {
if (!(*env)->ExceptionCheck(env)) {
jint len = (*env)->GetArrayLength(env, hab);
@@ -882,6 +923,7 @@ static const char* getStringBytes(JNIEnv *env, jstring jstr, jboolean strict) {
}
(*env)->DeleteLocalRef(env, hab);
}
+
return result;
}
@@ -897,7 +939,8 @@ getStringUTF8(JNIEnv *env, jstring jstr, jboolean strict)
int ri;
jbyte coder = (*env)->GetByteField(env, jstr, String_coder_ID);
if (coder != java_lang_String_LATIN1) {
- return getStringBytes(env, jstr, strict);
+ const jboolean forceUTF8 = (fastEncoding != FAST_UTF_8);
+ return getStringBytes(env, jstr, strict, forceUTF8);
}
if ((*env)->EnsureLocalCapacity(env, 2) < 0) {
return NULL;
@@ -945,6 +988,18 @@ getStringUTF8(JNIEnv *env, jstring jstr, jboolean strict)
return result;
}
+JNIEXPORT const char *
+GetStringUTF8Chars(JNIEnv *env, jstring jstr)
+{
+ return getStringUTF8(env, jstr, JNI_FALSE);
+}
+
+JNIEXPORT void
+ReleaseStringUTF8Chars(JNIEnv* env, jstring jstr, const char *str)
+{
+ free((void *)str);
+}
+
JNIEXPORT const char * JNICALL
JNU_GetStringPlatformChars(JNIEnv *env, jstring jstr, jboolean *isCopy)
{
@@ -976,7 +1031,7 @@ getStringPlatformChars0(JNIEnv *env, jstring jstr, jboolean *isCopy, jboolean st
JNU_ThrowInternalError(env, "platform encoding not initialized");
return 0;
} else
- return getStringBytes(env, jstr, strict);
+ return getStringBytes(env, jstr, strict, JNI_FALSE /* Need platform encoding */);
}
JNIEXPORT void JNICALL
diff --git a/src/java.base/share/native/libjava/jni_util.h b/src/java.base/share/native/libjava/jni_util.h
index 238a20b18ea4..6d364ab03312 100644
--- a/src/java.base/share/native/libjava/jni_util.h
+++ b/src/java.base/share/native/libjava/jni_util.h
@@ -107,6 +107,13 @@ GetStringPlatformChars(JNIEnv *env, jstring jstr, jboolean *isCopy);
JNIEXPORT const char *
GetStringPlatformCharsStrict(JNIEnv *env, jstring jstr, jboolean *isCopy);
+/* Convert the Java string to UTF-8 (not "modified UTF-8") */
+JNIEXPORT const char *
+GetStringUTF8Chars(JNIEnv *env, jstring jstr);
+
+JNIEXPORT void
+ReleaseStringUTF8Chars(JNIEnv* env, jstring jstr, const char *str);
+
JNIEXPORT jstring JNICALL
JNU_NewStringPlatform(JNIEnv *env, const char *str);
diff --git a/src/java.base/share/native/libnio/nio_util.c b/src/java.base/share/native/libnio/nio_util.c
index 2235f0a5998d..6bea39d9e622 100644
--- a/src/java.base/share/native/libnio/nio_util.c
+++ b/src/java.base/share/native/libnio/nio_util.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -26,11 +26,15 @@
#include "jni.h"
#include "jvm.h"
#include "jni_util.h"
+#include "nio_util.h"
+
+JavaVM *jvm;
JNIEXPORT jint JNICALL
DEF_JNI_OnLoad(JavaVM *vm, void *reserved)
{
JNIEnv *env;
+ jvm = vm;
if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_2) != JNI_OK) {
return JNI_EVERSION; /* JNI version not supported */
diff --git a/src/java.base/unix/native/libjli/java_md.c b/src/java.base/unix/native/libjli/java_md.c
index 024cec571b77..10f2d5fc98da 100644
--- a/src/java.base/unix/native/libjli/java_md.c
+++ b/src/java.base/unix/native/libjli/java_md.c
@@ -27,6 +27,7 @@
#include "jvm_md.h"
#include
#include
+#include
#include
#include
#include
@@ -220,6 +221,39 @@ ContainsLibJVM(const char *env) {
return JNI_FALSE;
}
+static int
+HaveGLibCCompatLibrary(struct dl_phdr_info* info, size_t size, void* data)
+{
+ static const char * const GLIBC_COMPAT_LIBRARY_NAME = "libgcompat.so";
+
+ const char * const so_pathname = info->dlpi_name;
+ if (so_pathname != NULL && so_pathname[0] != 0) {
+ const char * const last_slash = JLI_StrRChr(so_pathname, '/');
+ const char * const so_basename = (last_slash != NULL) ? last_slash + 1 : so_pathname;
+ if (JLI_StrNCmp(so_basename, GLIBC_COMPAT_LIBRARY_NAME, JLI_StrLen(GLIBC_COMPAT_LIBRARY_NAME)) == 0) {
+ return JNI_TRUE;
+ }
+ }
+
+ return 0; /* also means continue to iterate */
+}
+
+static jboolean
+UsingMusl(void) {
+ const jlong start = CurrentTimeMicros();
+
+ const int found_gcompat = dl_iterate_phdr(HaveGLibCCompatLibrary, NULL);
+
+ if (JLI_IsTraceLauncher()) {
+ const jlong end = CurrentTimeMicros();
+ JLI_TraceLauncher("%ld micro seconds to check for the musl compatibility layer for glibc\n",
+ (long)(end - start));
+ }
+
+ return (found_gcompat != 0);
+}
+
+
/*
* Test whether the environment variable needs to be set, see flowchart.
*/
@@ -243,6 +277,10 @@ RequiresSetenv(const char *jvmpath) {
return JNI_TRUE;
#endif
+ if (UsingMusl()) {
+ return JNI_TRUE;
+ }
+
llp = getenv("LD_LIBRARY_PATH");
/* no environment variable is a good environment variable */
if (llp == NULL && dmllp == NULL) {
diff --git a/src/java.base/unix/native/libnio/ch/nio_util.h b/src/java.base/unix/native/libnio/ch/nio_util.h
index 5e20934678c9..664460d9c94f 100644
--- a/src/java.base/unix/native/libnio/ch/nio_util.h
+++ b/src/java.base/unix/native/libnio/ch/nio_util.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2001, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -54,6 +54,8 @@
#define MAX_UNIX_DOMAIN_PATH_LEN \
(int)(sizeof(((struct sockaddr_un *)0)->sun_path)-2)
+extern JavaVM *jvm;
+
/* NIO utility procedures */
diff --git a/src/java.base/windows/classes/sun/nio/fs/WindowsPathParser.java b/src/java.base/windows/classes/sun/nio/fs/WindowsPathParser.java
index 6198fbfd5b59..6ca7c96ccc19 100644
--- a/src/java.base/windows/classes/sun/nio/fs/WindowsPathParser.java
+++ b/src/java.base/windows/classes/sun/nio/fs/WindowsPathParser.java
@@ -186,14 +186,9 @@ private static String normalize(StringBuilder sb, String path, int off) {
int len = path.length();
off = nextNonSlash(path, off, len);
int start = off;
- char lastC = 0;
while (off < len) {
char c = path.charAt(off);
if (isSlash(c)) {
- if (lastC == ' ')
- throw new InvalidPathException(path,
- "Trailing char <" + lastC + ">",
- off - 1);
sb.append(path, start, off);
off = nextNonSlash(path, off, len);
if (off != len) //no slash at the end of normalized path
@@ -204,15 +199,10 @@ private static String normalize(StringBuilder sb, String path, int off) {
throw new InvalidPathException(path,
"Illegal char <" + c + ">",
off);
- lastC = c;
off++;
}
}
if (start != off) {
- if (lastC == ' ')
- throw new InvalidPathException(path,
- "Trailing char <" + lastC + ">",
- off - 1);
sb.append(path, start, off);
}
return sb.toString();
diff --git a/src/java.base/windows/native/launcher/icons/awt.ico b/src/java.base/windows/native/launcher/icons/awt.ico
index 8d2c9571ed9e..b99c0c69965b 100644
Binary files a/src/java.base/windows/native/launcher/icons/awt.ico and b/src/java.base/windows/native/launcher/icons/awt.ico differ
diff --git a/src/java.base/windows/native/launcher/java.rc b/src/java.base/windows/native/launcher/java.rc
index 27c6465de210..6789a9a6091b 100644
--- a/src/java.base/windows/native/launcher/java.rc
+++ b/src/java.base/windows/native/launcher/java.rc
@@ -1,6 +1,5 @@
#include "version.rc"
-
-#define IDI_ICON 2000
+#include "java_rc.h"
/////////////////////////////////////////////////////////////////////////////
//
diff --git a/src/java.base/windows/native/launcher/java_rc.h b/src/java.base/windows/native/launcher/java_rc.h
new file mode 100644
index 000000000000..d3a1efe7536e
--- /dev/null
+++ b/src/java.base/windows/native/launcher/java_rc.h
@@ -0,0 +1,6 @@
+#ifndef _JAVA_RC_H_
+#define _JAVA_RC_H_
+
+#define IDI_ICON 2000
+
+#endif // _JAVA_RC_H_
diff --git a/src/java.desktop/linux/native/libawt_xawt/AccessibleAnnouncer/AccessibleAnnouncer.c b/src/java.desktop/linux/native/libawt_xawt/AccessibleAnnouncer/AccessibleAnnouncer.c
new file mode 100644
index 000000000000..eacb036f5971
--- /dev/null
+++ b/src/java.desktop/linux/native/libawt_xawt/AccessibleAnnouncer/AccessibleAnnouncer.c
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2022, JetBrains s.r.o.. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "sun_swing_AccessibleAnnouncer.h"
+#include "OrcaAnnouncer.h"
+
+/*
+ * Class: sun_swing_AccessibleAnnouncer
+ * Method: nativeAnnounce
+ * Signature: (Ljavax/accessibility/Accessible;Ljava/lang/String;I)V
+ */
+JNIEXPORT void JNICALL Java_sun_swing_AccessibleAnnouncer_nativeAnnounce
+(JNIEnv *env, jclass cls, jobject accessible, jstring str, jint priority)
+{
+#ifndef NO_A11Y_SPEECHD_ANNOUNCING
+ if (OrcaAnnounce(env, str, priority) == 0)
+ {
+ return;
+ }
+#endif
+
+#ifdef DEBUG
+ fprintf(stderr, "Each announcer has failed or the build was made without any of them\n");
+#endif
+}
diff --git a/src/java.desktop/linux/native/libawt_xawt/AccessibleAnnouncer/AccessibleAnnouncerJNIUtils.h b/src/java.desktop/linux/native/libawt_xawt/AccessibleAnnouncer/AccessibleAnnouncerJNIUtils.h
new file mode 100644
index 000000000000..1c3861cf7d10
--- /dev/null
+++ b/src/java.desktop/linux/native/libawt_xawt/AccessibleAnnouncer/AccessibleAnnouncerJNIUtils.h
@@ -0,0 +1,175 @@
+/*
+ * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2022, JetBrains s.r.o.. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef ACCESSIBLEANNOUNCERJNIUTILS_H
+#define ACCESSIBLEANNOUNCERJNIUTILS_H
+
+#ifndef NO_A11Y_SPEECHD_ANNOUNCING
+
+#include "jni.h"
+
+#define GET_AccessibleAnnouncerUtilities()\
+if (jc_AccessibleAnnouncerUtilities == NULL) {\
+jclass cls = (*env)->FindClass(env, "sun/awt/AccessibleAnnouncerUtilities");\
+if (cls == NULL) {\
+return;\
+}\
+jc_AccessibleAnnouncerUtilities = (*env)->NewGlobalRef(env, cls);\
+(*env)->DeleteLocalRef(env, cls);\
+}\
+
+#define GET_AccessibleAnnouncerUtilitiesReturn(ret)\
+if (jc_AccessibleAnnouncerUtilities == NULL) {\
+jclass cls = (*env)->FindClass(env, "sun/awt/AccessibleAnnouncerUtilities");\
+if (cls == NULL) {\
+return ret;\
+}\
+jc_AccessibleAnnouncerUtilities = (*env)->NewGlobalRef(env, cls);\
+(*env)->DeleteLocalRef(env, cls);\
+}\
+
+#define GET_getOrcaConf()\
+GET_AccessibleAnnouncerUtilities();\
+if (jsm_getOrcaConf == NULL) {\
+jsm_getOrcaConf = (*env)->GetStaticMethodID(env, jc_AccessibleAnnouncerUtilities, "getOrcaConf", "()Ljava/lang/Object;");\
+if (jsm_getOrcaConf == NULL) {\
+return;\
+}\
+}\
+
+#define GET_getOrcaConfReturn(ret)\
+GET_AccessibleAnnouncerUtilitiesReturn(ret);\
+if (jsm_getOrcaConf == NULL) {\
+jsm_getOrcaConf = (*env)->GetStaticMethodID(env, jc_AccessibleAnnouncerUtilities, "getOrcaConf", "()Ljava/lang/Object;");\
+if (jsm_getOrcaConf == NULL) {\
+return ret;\
+}\
+}\
+
+#define GET_getSpeechServerInfo()\
+GET_AccessibleAnnouncerUtilities();\
+if (jsm_getSpeechServerInfo == NULL) {\
+jsm_getSpeechServerInfo = (*env)->GetStaticMethodID(env, jc_AccessibleAnnouncerUtilities, "getSpeechServerInfo", "(Ljava/lang/Object;)Ljava/lang/String;");\
+if (jsm_getSpeechServerInfo == NULL) {\
+return;\
+}\
+}\
+
+#define GET_getGain()\
+GET_AccessibleAnnouncerUtilities();\
+if (jsm_getGain == NULL) {\
+jsm_getGain = (*env)->GetStaticMethodID(env, jc_AccessibleAnnouncerUtilities, "getGain", "(Ljava/lang/Object;)D");\
+if (jsm_getGain == NULL) {\
+return;\
+}\
+}\
+
+#define GET_getVariant()\
+GET_AccessibleAnnouncerUtilities();\
+if (jsm_getVariant == NULL) {\
+jsm_getVariant = (*env)->GetStaticMethodID(env, jc_AccessibleAnnouncerUtilities, "getVariant", "(Ljava/lang/Object;)Ljava/lang/String;");\
+if (jsm_getVariant == NULL) {\
+return;\
+}\
+}\
+
+#define GET_getDialect()\
+GET_AccessibleAnnouncerUtilities();\
+if (jsm_getDialect == NULL) {\
+jsm_getDialect = (*env)->GetStaticMethodID(env, jc_AccessibleAnnouncerUtilities, "getDialect", "(Ljava/lang/Object;)Ljava/lang/String;");\
+if (jsm_getDialect == NULL) {\
+return;\
+}\
+}\
+
+#define GET_getLang()\
+GET_AccessibleAnnouncerUtilities();\
+if (jsm_getLang == NULL) {\
+jsm_getLang = (*env)->GetStaticMethodID(env, jc_AccessibleAnnouncerUtilities, "getLang", "(Ljava/lang/Object;)Ljava/lang/String;");\
+if (jsm_getLang == NULL) {\
+return;\
+}\
+}\
+
+#define GET_getName()\
+GET_AccessibleAnnouncerUtilities();\
+if (jsm_getName == NULL) {\
+jsm_getName = (*env)->GetStaticMethodID(env, jc_AccessibleAnnouncerUtilities, "getName", "(Ljava/lang/Object;)Ljava/lang/String;");\
+if (jsm_getName == NULL) {\
+return;\
+}\
+}\
+
+#define GET_getAveragePitch()\
+GET_AccessibleAnnouncerUtilities();\
+if (jsm_getAveragePitch == NULL) {\
+jsm_getAveragePitch = (*env)->GetStaticMethodID(env, jc_AccessibleAnnouncerUtilities, "getAveragePitch", "(Ljava/lang/Object;)D");\
+if (jsm_getAveragePitch == NULL) {\
+return;\
+}\
+}\
+
+#define GET_getRate()\
+GET_AccessibleAnnouncerUtilities();\
+if (jsm_getRate == NULL) {\
+jsm_getRate = (*env)->GetStaticMethodID(env, jc_AccessibleAnnouncerUtilities, "getRate", "(Ljava/lang/Object;)D");\
+if (jsm_getRate == NULL) {\
+return;\
+}\
+}\
+
+#define GET_getActiveProfile()\
+GET_AccessibleAnnouncerUtilities();\
+if (jsm_getActiveProfile == NULL) {\
+jsm_getActiveProfile = (*env)->GetStaticMethodID(env, jc_AccessibleAnnouncerUtilities, "getActiveProfile", "(Ljava/lang/Object;)Ljava/lang/String;");\
+if (jsm_getActiveProfile == NULL) {\
+return;\
+}\
+}\
+
+#define GET_getVerbalizePunctuationStyle()\
+GET_AccessibleAnnouncerUtilities();\
+if (jsm_getVerbalizePunctuationStyle == NULL) {\
+jsm_getVerbalizePunctuationStyle = (*env)->GetStaticMethodID(env, jc_AccessibleAnnouncerUtilities, "getVerbalizePunctuationStyle", "(Ljava/lang/Object;)I");\
+if (jsm_getVerbalizePunctuationStyle == NULL) {\
+return;\
+}\
+}\
+
+#define GET_getEnableSpeech(ret)\
+GET_AccessibleAnnouncerUtilitiesReturn(ret);\
+if (jsm_getEnableSpeech == NULL) {\
+jsm_getEnableSpeech = (*env)->GetStaticMethodID(env, jc_AccessibleAnnouncerUtilities, "getEnableSpeech", "(Ljava/lang/Object;)Z");\
+if (jsm_getEnableSpeech == NULL) {\
+return ret;\
+}\
+}\
+
+#endif // #ifndef NO_A11Y_SPEECHD_ANNOUNCING
+
+
+#endif //ACCESSIBLEANNOUNCERJNIUTILS_H
+
diff --git a/src/java.desktop/linux/native/libawt_xawt/AccessibleAnnouncer/OrcaAnnouncer.c b/src/java.desktop/linux/native/libawt_xawt/AccessibleAnnouncer/OrcaAnnouncer.c
new file mode 100644
index 000000000000..acf24f40cf5b
--- /dev/null
+++ b/src/java.desktop/linux/native/libawt_xawt/AccessibleAnnouncer/OrcaAnnouncer.c
@@ -0,0 +1,303 @@
+/*
+ * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2022, JetBrains s.r.o.. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef NO_A11Y_SPEECHD_ANNOUNCING
+
+#include "sun_swing_AccessibleAnnouncer.h"
+#include "OrcaAnnouncer.h"
+#include "AccessibleAnnouncerJNIUtils.h"
+#include "jni_util.h"
+#include "debug_assert.h"
+
+static jclass jc_AccessibleAnnouncerUtilities = NULL;
+static jmethodID jsm_getOrcaConf = NULL;
+static jmethodID jsm_getSpeechServerInfo = NULL;
+static jmethodID jsm_getGain = NULL;
+static jmethodID jsm_getVariant = NULL;
+static jmethodID jsm_getDialect = NULL;
+static jmethodID jsm_getLang = NULL;
+static jmethodID jsm_getName = NULL;
+static jmethodID jsm_getAveragePitch = NULL;
+static jmethodID jsm_getRate = NULL;
+static jmethodID jsm_getEstablished = NULL;
+static jmethodID jsm_getActiveProfile = NULL;
+static jmethodID jsm_getVerbalizePunctuationStyle = NULL;
+static jmethodID jsm_getOnlySpeakDisplayedText = NULL;
+static jmethodID jsm_getEnableSpeech = NULL;
+
+int OrcaAnnounce(JNIEnv *env, jstring str, jint priority)
+{
+ DASSERT(env != NULL);
+ DASSERT(str != NULL)
+
+ jobject conf = OrcaGetConf(env);
+ if (conf == NULL)
+ {
+#ifdef DEBUG
+ fprintf(stderr, "Failed to read Orca configuration file\n");
+#endif
+ return -1;
+ }
+
+ if (OrcaGetEnableSpeech(env, conf) <= 0)
+ {
+#ifdef DEBUG
+ fprintf(stderr, "Speech is disable\n");
+#endif
+ (*env)->DeleteLocalRef(env, conf);
+ return -1;
+ }
+
+ SPDConnection *connection = spd_open("Java announcer", NULL, NULL, SPD_MODE_SINGLE);
+ if (connection == NULL)
+ {
+#ifdef DEBUG
+ fprintf(stderr, "Speech dispatcher connection is null\n");
+#endif
+ (*env)->DeleteLocalRef(env, conf);
+ return -1;
+ }
+
+ const char *msg = JNU_GetStringPlatformChars(env, str, NULL);
+ if (msg == NULL)
+ {
+ if ((*env)->ExceptionCheck(env) == JNI_FALSE)
+ {
+ JNU_ThrowOutOfMemoryError(env, "OrcaAnnounce: failed to obtain chars from the announcing string");
+ }
+
+ spd_close(connection);
+ (*env)->DeleteLocalRef(env, conf);
+ return -1;
+ }
+
+ OrcaSetSpeechConf(env, connection, conf);
+ (*env)->DeleteLocalRef(env, conf);
+ int p = SPD_TEXT;
+ if (priority == sun_swing_AccessibleAnnouncer_ANNOUNCE_WITH_INTERRUPTING_CURRENT_OUTPUT)
+ {
+ p = SPD_MESSAGE;
+ }
+ int err = spd_say(connection, p, msg);
+ spd_close(connection);
+ JNU_ReleaseStringPlatformChars(env, str, msg);
+
+ if (err < 0)
+ {
+#ifdef DEBUG
+ fprintf(stderr, "Failed to say message\n");
+#endif
+ return -1;
+ }
+
+ return 0;
+}
+
+void OrcaSetSpeechConf(JNIEnv *env, SPDConnection *connection, jobject conf)
+{
+ OrcaSetOutputModule(env, connection, conf);
+ OrcaSetSynthesisVoice(env, connection, conf);
+ OrcaSetLanguage(env, connection, conf);
+ OrcaSetPunctuation(env, connection, conf);
+ OrcaSetVoiceRate(env, connection, conf);
+ OrcaSetVoicePitch(env, connection, conf);
+ OrcaSetVolume(env, connection, conf);
+}
+
+void OrcaSetVolume(JNIEnv *env, SPDConnection *connection, jobject conf)
+{
+ GET_getGain();
+ jdouble gain = (*env)->CallStaticDoubleMethod(env, jc_AccessibleAnnouncerUtilities, jsm_getGain, conf);
+ JNU_CHECK_EXCEPTION(env);
+ if (gain < 0)
+ {
+#ifdef DEBUG
+ fprintf(stderr, "Failed to read value of gain from config\n");
+#endif
+ return;
+ }
+
+ int volume = (int)((gain - 5) * 20);
+ spd_set_volume(connection, volume);
+}
+
+void OrcaSetVoiceRate(JNIEnv *env, SPDConnection *connection, jobject conf)
+{
+ GET_getRate();
+ jdouble rate = (*env)->CallStaticDoubleMethod(env, jc_AccessibleAnnouncerUtilities, jsm_getRate, conf);
+ JNU_CHECK_EXCEPTION(env);
+ if (rate < 0)
+ {
+#ifdef DEBUG
+ fprintf(stderr, "Failed to read value of rate from config\n");
+#endif
+ return;
+ }
+
+ int iRate = (int)((rate - 50) * 2);
+ spd_set_voice_rate(connection, iRate);
+}
+
+void OrcaSetPunctuation(JNIEnv *env, SPDConnection *connection, jobject conf)
+{
+ GET_getVerbalizePunctuationStyle();
+ jint punctuation = (*env)->CallStaticIntMethod(env, jc_AccessibleAnnouncerUtilities, jsm_getVerbalizePunctuationStyle, conf);
+ JNU_CHECK_EXCEPTION(env);
+ if (punctuation < 0)
+ {
+#ifdef DEBUG
+ fprintf(stderr, "Failed to read value of punctuation from config\n");
+#endif
+ return;
+ }
+
+ spd_set_punctuation(connection, punctuation);
+}
+
+void OrcaSetVoicePitch(JNIEnv *env, SPDConnection *connection, jobject conf)
+{
+ GET_getAveragePitch();
+ jdouble pitch = (*env)->CallStaticDoubleMethod(env, jc_AccessibleAnnouncerUtilities, jsm_getAveragePitch, conf);
+ JNU_CHECK_EXCEPTION(env);
+ if (pitch < 0)
+ {
+#ifdef DEBUG
+ fprintf(stderr, "Failed to read value of pitch from config\n");
+#endif
+ return;
+ }
+
+ int iPitch = (int)((pitch - 5) * 20);
+ spd_set_voice_pitch(connection, iPitch);
+}
+
+void OrcaSetOutputModule(JNIEnv *env, SPDConnection *connection, jobject conf)
+{
+ GET_getSpeechServerInfo();
+ jobject jStr = (*env)->CallStaticObjectMethod(env, jc_AccessibleAnnouncerUtilities, jsm_getSpeechServerInfo, conf);
+ JNU_CHECK_EXCEPTION(env);
+ if (jStr == NULL)
+ {
+#ifdef DEBUG
+ fprintf(stderr, "Failed to read value of speech server info from config\n");
+#endif
+ return;
+ }
+
+ const char *sintName = JNU_GetStringPlatformChars(env, jStr, NULL);
+ if (sintName == NULL)
+ {
+ if ((*env)->ExceptionCheck(env) == JNI_FALSE)
+ {
+ JNU_ThrowOutOfMemoryError(env, "OrcaAnnounce: failed to obtain chars from the sintName string");
+ }
+
+ (*env)->DeleteLocalRef(env, jStr);
+ return;
+ }
+
+ spd_set_output_module(connection, sintName);
+ JNU_ReleaseStringPlatformChars(env, jStr, sintName);
+ (*env)->DeleteLocalRef(env, jStr);
+}
+
+void OrcaSetLanguage(JNIEnv *env, SPDConnection *connection, jobject conf)
+{
+ GET_getLang();
+ jobject jStr = (*env)->CallStaticObjectMethod(env, jc_AccessibleAnnouncerUtilities, jsm_getLang, conf);
+ JNU_CHECK_EXCEPTION(env);
+ if (jStr == NULL)
+ {
+#ifdef DEBUG
+ fprintf(stderr, "Failed to read value of lang from config\n");
+#endif
+ return;
+ }
+
+ const char *lang = JNU_GetStringPlatformChars(env, jStr, NULL);
+ if (lang == NULL)
+ {
+ if ((*env)->ExceptionCheck(env) == JNI_FALSE)
+ {
+ JNU_ThrowOutOfMemoryError(env, "OrcaAnnounce: failed to obtain chars from the lang string");
+ }
+
+ (*env)->DeleteLocalRef(env, jStr);
+ return;
+ }
+
+ spd_set_language(connection, lang);
+ JNU_ReleaseStringPlatformChars(env, jStr, lang);
+ (*env)->DeleteLocalRef(env, jStr);
+}
+
+int OrcaGetEnableSpeech(JNIEnv *env, jobject conf)
+{
+ GET_getEnableSpeech(-1);
+ int es = (*env)->CallStaticBooleanMethod(env, jc_AccessibleAnnouncerUtilities, jsm_getEnableSpeech, conf);
+ JNU_CHECK_EXCEPTION_RETURN(env, -1);
+ return es;
+}
+
+void OrcaSetSynthesisVoice(JNIEnv *env, SPDConnection *connection, jobject conf)
+{
+ GET_getName();
+ jobject jStr = (*env)->CallStaticObjectMethod(env, jc_AccessibleAnnouncerUtilities, jsm_getName, conf);
+ JNU_CHECK_EXCEPTION(env);
+ if (jStr == NULL)
+ {
+#ifdef DEBUG
+ fprintf(stderr, "Failed to read value of voice name from config\n");
+#endif
+ return;
+ }
+
+ const char *voiceName = JNU_GetStringPlatformChars(env, jStr, NULL);
+ if (voiceName == NULL)
+ {
+ if ((*env)->ExceptionCheck(env) == JNI_FALSE)
+ {
+ JNU_ThrowOutOfMemoryError(env, "OrcaAnnounce: failed to obtain chars from the voiceName string");
+ }
+
+ (*env)->DeleteLocalRef(env, jStr);
+ return;
+ }
+
+ spd_set_synthesis_voice(connection, voiceName);
+ JNU_ReleaseStringPlatformChars(env, jStr, voiceName);
+ (*env)->DeleteLocalRef(env, jStr);
+}
+
+jobject OrcaGetConf(JNIEnv *env)
+{
+ GET_getOrcaConfReturn(NULL);
+ jobject o = (*env)->CallStaticObjectMethod(env, jc_AccessibleAnnouncerUtilities, jsm_getOrcaConf);
+ JNU_CHECK_EXCEPTION_RETURN(env, NULL);
+ return o;
+}
+
+#endif // #ifndef NO_A11Y_SPEECHD_ANNOUNCING
diff --git a/src/java.desktop/linux/native/libawt_xawt/AccessibleAnnouncer/OrcaAnnouncer.h b/src/java.desktop/linux/native/libawt_xawt/AccessibleAnnouncer/OrcaAnnouncer.h
new file mode 100644
index 000000000000..8eac261482cd
--- /dev/null
+++ b/src/java.desktop/linux/native/libawt_xawt/AccessibleAnnouncer/OrcaAnnouncer.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2022, JetBrains s.r.o.. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef ORCACONF_H
+#define ORCACONF_H
+
+#ifndef NO_A11Y_SPEECHD_ANNOUNCING
+
+#include
+#include "jni.h"
+
+int OrcaAnnounce(JNIEnv *env, jstring str, jint priority);
+jobject OrcaGetConf(JNIEnv *env);
+void OrcaSetSpeechConf(JNIEnv *env, SPDConnection *connection, jobject conf);
+void OrcaSetLanguage(JNIEnv *env, SPDConnection *connection, jobject conf);
+void OrcaSetOutputModule(JNIEnv *env, SPDConnection *connection, jobject conf);
+void OrcaSetPunctuation(JNIEnv *env, SPDConnection *connection, jobject conf);
+void OrcaSetSynthesisVoice(JNIEnv *env, SPDConnection *connection, jobject conf);
+void OrcaSetVoiceRate(JNIEnv *env, SPDConnection *connection, jobject conf);
+void OrcaSetVoicePitch(JNIEnv *env, SPDConnection *connection, jobject conf);
+void OrcaSetVolume(JNIEnv *env, SPDConnection *connection, jobject conf);
+int OrcaGetEnableSpeech(JNIEnv *env, jobject conf);
+
+#endif // #ifndef NO_A11Y_SPEECHD_ANNOUNCING
+
+#endif //ORCACONF_H
+
diff --git a/src/java.desktop/macosx/classes/com/apple/eawt/Application.java b/src/java.desktop/macosx/classes/com/apple/eawt/Application.java
index 6ed4a7c1daf1..d31e1e72a4fb 100644
--- a/src/java.desktop/macosx/classes/com/apple/eawt/Application.java
+++ b/src/java.desktop/macosx/classes/com/apple/eawt/Application.java
@@ -54,6 +54,7 @@
import sun.awt.AWTAccessor;
import sun.lwawt.LWWindowPeer;
import sun.lwawt.macosx.CPlatformWindow;
+import sun.util.logging.PlatformLogger;
/**
* The {@code Application} class allows you to integrate your Java application with the native Mac OS X environment.
@@ -74,6 +75,8 @@
* @since 1.4
*/
public class Application {
+ private static final PlatformLogger focusRequestLog = PlatformLogger.getLogger("jb.focus.requests");
+
private static native void nativeInitializeApplicationDelegate();
static Application sApplication = null;
@@ -297,6 +300,9 @@ public void disableSuddenTermination() {
* @since Java for Mac OS X 10.5 Update 6 - 1.6, 1.5
*/
public void requestForeground(final boolean allWindows) {
+ if (focusRequestLog.isLoggable(PlatformLogger.Level.FINE)) {
+ focusRequestLog.fine("requestForeground(" + (allWindows ? "allWindows" : "") + ")", new Throwable());
+ }
_AppMiscHandlers.requestActivation(allWindows);
}
@@ -425,4 +431,19 @@ public void requestToggleFullScreen(final Window window) {
((CPlatformWindow)platformWindow).toggleFullScreen();
}
+ public void requestEnterFullScreen(final Window window) {
+ final Object peer = AWTAccessor.getComponentAccessor().getPeer(window);
+ if (!(peer instanceof LWWindowPeer)) return;
+ Object platformWindow = ((LWWindowPeer) peer).getPlatformWindow();
+ if (!(platformWindow instanceof CPlatformWindow)) return;
+ ((CPlatformWindow)platformWindow).enterFullScreenMode();
+ }
+
+ public void requestLeaveFullScreen(final Window window) {
+ final Object peer = AWTAccessor.getComponentAccessor().getPeer(window);
+ if (!(peer instanceof LWWindowPeer)) return;
+ Object platformWindow = ((LWWindowPeer) peer).getPlatformWindow();
+ if (!(platformWindow instanceof CPlatformWindow)) return;
+ ((CPlatformWindow)platformWindow).exitFullScreenMode();
+ }
}
diff --git a/src/java.desktop/macosx/classes/com/apple/eawt/FullScreenHandler.java b/src/java.desktop/macosx/classes/com/apple/eawt/FullScreenHandler.java
index c1d36d8add2b..6f2e7fc764e0 100644
--- a/src/java.desktop/macosx/classes/com/apple/eawt/FullScreenHandler.java
+++ b/src/java.desktop/macosx/classes/com/apple/eawt/FullScreenHandler.java
@@ -67,8 +67,10 @@ static void removeFullScreenListenerFrom(final RootPaneContainer window, final F
}
static FullScreenHandler getHandlerFor(final RootPaneContainer window) {
- final Object value = window.getRootPane().getClientProperty(CLIENT_PROPERTY);
- if (value instanceof FullScreenHandler) return (FullScreenHandler)value;
+ if (window != null && window.getRootPane() != null) {
+ final Object value = window.getRootPane().getClientProperty(CLIENT_PROPERTY);
+ if (value instanceof FullScreenHandler) return (FullScreenHandler)value;
+ }
return null;
}
diff --git a/src/java.desktop/macosx/classes/com/apple/eawt/_AppEventHandler.java b/src/java.desktop/macosx/classes/com/apple/eawt/_AppEventHandler.java
index 248ae55f6d74..4ebd08b9cca0 100644
--- a/src/java.desktop/macosx/classes/com/apple/eawt/_AppEventHandler.java
+++ b/src/java.desktop/macosx/classes/com/apple/eawt/_AppEventHandler.java
@@ -27,6 +27,7 @@
import java.awt.EventQueue;
import java.awt.Frame;
+import java.awt.GraphicsEnvironment;
import java.awt.desktop.AboutEvent;
import java.awt.desktop.AboutHandler;
import java.awt.desktop.AppForegroundEvent;
@@ -65,8 +66,12 @@
import java.util.Map;
import sun.awt.AppContext;
import sun.awt.SunToolkit;
+import sun.java2d.SunGraphicsEnvironment;
+import sun.util.logging.PlatformLogger;
class _AppEventHandler {
+ private static final PlatformLogger logger = PlatformLogger.getLogger(_AppEventHandler.class.getName());
+
private static final int NOTIFY_ABOUT = 1;
private static final int NOTIFY_PREFS = 2;
private static final int NOTIFY_OPEN_APP = 3;
@@ -83,6 +88,7 @@ class _AppEventHandler {
private static final int NOTIFY_SCREEN_WAKE = 14;
private static final int NOTIFY_SYSTEM_SLEEP = 15;
private static final int NOTIFY_SYSTEM_WAKE = 16;
+ private static final int NOTIFY_SCREEN_CHANGE_PARAMETERS = 17;
private static final int REGISTER_USER_SESSION = 1;
private static final int REGISTER_SCREEN_SLEEP = 2;
@@ -264,6 +270,16 @@ private static void handleNativeNotification(final int code) {
case NOTIFY_SYSTEM_WAKE:
instance.systemSleepDispatcher.dispatch(new _NativeEvent(Boolean.FALSE));
break;
+ case NOTIFY_SCREEN_CHANGE_PARAMETERS:
+ if (logger.isLoggable(PlatformLogger.Level.FINE)) {
+ logger.fine("NOTIFY_SCREEN_CHANGE_PARAMETERS");
+ }
+ if (AppContext.getAppContext() != null) {
+ EventQueue.invokeLater(
+ () -> ((SunGraphicsEnvironment) GraphicsEnvironment.
+ getLocalGraphicsEnvironment()).displayParametersChanged());
+ }
+ break;
default:
System.err.println("EAWT unknown native notification: " + code);
break;
diff --git a/src/java.desktop/macosx/classes/com/apple/eawt/event/GestureHandler.java b/src/java.desktop/macosx/classes/com/apple/eawt/event/GestureHandler.java
index 4514da90ca90..e3813cf9dbdd 100644
--- a/src/java.desktop/macosx/classes/com/apple/eawt/event/GestureHandler.java
+++ b/src/java.desktop/macosx/classes/com/apple/eawt/event/GestureHandler.java
@@ -43,6 +43,7 @@ final class GestureHandler {
@Native static final int ROTATE = 2;
@Native static final int MAGNIFY = 3;
@Native static final int SWIPE = 4;
+ @Native static final int PRESSURE = 5;
// installs a private instance of GestureHandler, if necessary
static void addGestureListenerTo(final JComponent component, final GestureListener listener) {
@@ -97,6 +98,9 @@ public void run() {
case SWIPE:
firstNotifier.recursivelyHandleSwipe(a, b, new SwipeEvent());
return;
+ case PRESSURE:
+ firstNotifier.recursivelyHandlePressure(new PressureEvent(a,b));
+ return;
}
}
});
@@ -107,6 +111,7 @@ public void run() {
final List rotaters = new LinkedList();
final List magnifiers = new LinkedList();
final List swipers = new LinkedList();
+ final List pressures = new LinkedList();
GestureHandler() { }
@@ -115,6 +120,7 @@ void addListener(final GestureListener listener) {
if (listener instanceof RotationListener) rotaters.add((RotationListener)listener);
if (listener instanceof MagnificationListener) magnifiers.add((MagnificationListener)listener);
if (listener instanceof SwipeListener) swipers.add((SwipeListener)listener);
+ if (listener instanceof PressureListener) pressures.add((PressureListener)listener);
}
void removeListener(final GestureListener listener) {
@@ -169,6 +175,16 @@ void recursivelyHandleMagnify(final MagnificationEvent e) {
if (next != null) next.recursivelyHandleMagnify(e);
}
+ void recursivelyHandlePressure(final PressureEvent e) {
+ for (final PressureListener listener : handler.pressures) {
+ listener.pressure(e);
+ if (e.isConsumed()) return;
+ }
+
+ final PerComponentNotifier next = getNextNotifierForComponent(component.getParent());
+ if (next != null) next.recursivelyHandlePressure(e);
+ }
+
void recursivelyHandleSwipe(final double x, final double y, final SwipeEvent e) {
for (final SwipeListener listener : handler.swipers) {
if (x < 0) listener.swipedLeft(e);
diff --git a/src/java.desktop/macosx/classes/com/apple/eawt/event/PressureEvent.java b/src/java.desktop/macosx/classes/com/apple/eawt/event/PressureEvent.java
new file mode 100644
index 000000000000..f3140cc3dfb0
--- /dev/null
+++ b/src/java.desktop/macosx/classes/com/apple/eawt/event/PressureEvent.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.apple.eawt.event;
+
+/**
+ * Event indicating a swipe was performed by the user.
+ *
+ * @see PressureListener
+ *
+ * @since Java for Mac OS X 10.10 Update 3, JDK 8
+ */
+public class PressureEvent extends GestureEvent {
+
+ public double getPressure() {
+ return pressure;
+ }
+
+ public double getStage() {
+ return stage;
+ }
+
+ private double pressure;
+ private double stage;
+
+ PressureEvent(double pressure, double stage) {
+ this.pressure = pressure;
+ this.stage = stage;
+ }
+}
diff --git a/src/java.desktop/macosx/classes/com/apple/eawt/event/PressureListener.java b/src/java.desktop/macosx/classes/com/apple/eawt/event/PressureListener.java
new file mode 100644
index 000000000000..de6a646b0bb7
--- /dev/null
+++ b/src/java.desktop/macosx/classes/com/apple/eawt/event/PressureListener.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.apple.eawt.event;
+
+/**
+ * Listener interface for receiving pressure events.
+ *
+ * @see PressureEvent
+ * @see GestureUtilities
+ *
+ * @since Java for Mac OS X 10.5 Update 7, Java for Mac OS X 10.6 Update 2
+ */
+public interface PressureListener extends GestureListener {
+ public void pressure(final PressureEvent e);
+}
diff --git a/src/java.desktop/macosx/classes/com/apple/laf/AquaCaret.java b/src/java.desktop/macosx/classes/com/apple/laf/AquaCaret.java
index 9b0981aa8b29..bad7dc9c8b12 100644
--- a/src/java.desktop/macosx/classes/com/apple/laf/AquaCaret.java
+++ b/src/java.desktop/macosx/classes/com/apple/laf/AquaCaret.java
@@ -62,6 +62,7 @@ public void install(final JTextComponent c) {
public void deinstall(final JTextComponent c) {
c.removePropertyChangeListener(this);
super.deinstall(c);
+ mFocused = false;
}
@Override
diff --git a/src/java.desktop/macosx/classes/com/apple/laf/AquaImageFactory.java b/src/java.desktop/macosx/classes/com/apple/laf/AquaImageFactory.java
index 2fddf6483a5d..a1371c416889 100644
--- a/src/java.desktop/macosx/classes/com/apple/laf/AquaImageFactory.java
+++ b/src/java.desktop/macosx/classes/com/apple/laf/AquaImageFactory.java
@@ -84,11 +84,11 @@ public static IconUIResource getLockImageIcon() {
@SuppressWarnings("removal")
static Image getGenericJavaIcon() {
- return java.security.AccessController.doPrivileged(new PrivilegedAction() {
+ return checkValidOrStub(java.security.AccessController.doPrivileged(new PrivilegedAction() {
public Image run() {
return com.apple.eawt.Application.getApplication().getDockIconImage();
}
- });
+ }));
}
@SuppressWarnings("removal")
@@ -521,4 +521,23 @@ public static Color getSelectionInactiveForegroundColorUIResource() {
public static Color getSelectedControlColorUIResource() {
return new SystemColorProxy(LWCToolkit.getAppleColor(LWCToolkit.SELECTED_CONTROL_TEXT_COLOR));
}
+
+ private static class EmptyImage {
+ static final BufferedImage INSTANCE;
+ static {
+ INSTANCE = new BufferedImage(16, 16, BufferedImage.TYPE_INT_ARGB);
+ Graphics g = INSTANCE.createGraphics();
+ g.setColor(new Color(0, 0, 0, 0));
+ g.fillRect(0, 0, 16, 16);
+ g.dispose();
+ }
+ }
+
+ // [tav] a workaround for JBR-1492
+ private static Image checkValidOrStub(Image image) {
+ if (image == null || image.getWidth(null) <= 0 || image.getHeight(null) <= 0) {
+ return EmptyImage.INSTANCE;
+ }
+ return image;
+ }
}
diff --git a/src/java.desktop/macosx/classes/sun/awt/CGraphicsDevice.java b/src/java.desktop/macosx/classes/sun/awt/CGraphicsDevice.java
index 5a865f92cb62..0bf3c5fd3596 100644
--- a/src/java.desktop/macosx/classes/sun/awt/CGraphicsDevice.java
+++ b/src/java.desktop/macosx/classes/sun/awt/CGraphicsDevice.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -41,12 +41,14 @@
import sun.java2d.MacOSFlags;
import sun.java2d.metal.MTLGraphicsConfig;
import sun.java2d.opengl.CGLGraphicsConfig;
+import sun.util.logging.PlatformLogger;
import static java.awt.peer.ComponentPeer.SET_BOUNDS;
public final class CGraphicsDevice extends GraphicsDevice
- implements DisplayChangedListener {
+ implements DisplayChangedListener, DisplayParametersChangedListener {
+ private static final PlatformLogger logger = PlatformLogger.getLogger(CGraphicsDevice.class.getName());
/**
* CoreGraphics display ID. This identifier can become non-valid at any time
* therefore methods, which is using this id should be ready to it.
@@ -56,10 +58,9 @@ public final class CGraphicsDevice extends GraphicsDevice
private volatile double yResolution;
private volatile Rectangle bounds;
private volatile int scale;
+ private volatile Insets screenInsets;
private GraphicsConfiguration config;
- private static boolean metalPipelineEnabled = false;
- private static boolean oglPipelineEnabled = false;
private static AWTPermission fullScreenExclusivePermission;
@@ -71,70 +72,34 @@ public final class CGraphicsDevice extends GraphicsDevice
public CGraphicsDevice(final int displayID) {
this.displayID = displayID;
this.initialMode = getDisplayMode();
+ StringBuilder errorMessage = new StringBuilder();
- if (MacOSFlags.isMetalEnabled()) {
- // Try to create MTLGraphicsConfig, if it fails,
- // try to create CGLGraphicsConfig as a fallback
- this.config = MTLGraphicsConfig.getConfig(this, displayID);
-
- if (this.config != null) {
- metalPipelineEnabled = true;
- } else {
- // Try falling back to OpenGL pipeline
- if (MacOSFlags.isMetalVerbose()) {
- System.out.println("Metal rendering pipeline" +
- " initialization failed,using OpenGL" +
- " rendering pipeline");
- }
-
- this.config = CGLGraphicsConfig.getConfig(this);
+ this.config = CGraphicsEnvironment.usingMetalPipeline() ?
+ MTLGraphicsConfig.getConfig(this, displayID, errorMessage) :
+ CGLGraphicsConfig.getConfig(this);
- if (this.config != null) {
- oglPipelineEnabled = true;
- }
+ if (this.config == null) {
+ if (MacOSFlags.isMetalVerbose() || MacOSFlags.isOGLVerbose()) {
+ System.out.println(MacOSFlags.getRenderPipelineName() +
+ " rendering pipeline initialization failed");
}
+ throw new IllegalStateException("Error - unable to initialize " +
+ MacOSFlags.getRenderPipelineName());
} else {
- // Try to create CGLGraphicsConfig, if it fails,
- // try to create MTLGraphicsConfig as a fallback
- this.config = CGLGraphicsConfig.getConfig(this);
-
- if (this.config != null) {
- oglPipelineEnabled = true;
- } else {
- // Try falling back to Metal pipeline
- if (MacOSFlags.isOGLVerbose()) {
- System.out.println("OpenGL rendering pipeline" +
- " initialization failed,using Metal" +
- " rendering pipeline");
- }
-
- this.config = MTLGraphicsConfig.getConfig(this, displayID);
-
- if (this.config != null) {
- metalPipelineEnabled = true;
- }
+ if (MacOSFlags.isMetalVerbose() || MacOSFlags.isOGLVerbose()) {
+ System.out.println(MacOSFlags.getRenderPipelineName() +
+ " pipeline enabled on screen " + displayID);
}
}
- if (!metalPipelineEnabled && !oglPipelineEnabled) {
- // This indicates fallback to other rendering pipeline also failed.
- // Should never reach here
- throw new InternalError("Error - unable to initialize any" +
- " rendering pipeline.");
- }
-
- if (metalPipelineEnabled && MacOSFlags.isMetalVerbose()) {
- System.out.println("Metal pipeline enabled on screen " + displayID);
- } else if (oglPipelineEnabled && MacOSFlags.isOGLVerbose()) {
- System.out.println("OpenGL pipeline enabled on screen " + displayID);
- }
-
- // initializes default device state, might be redundant step since we
- // call "displayChanged()" later anyway, but we do not want to leave the
- // device in an inconsistent state after construction
+ // [JBR] we don't call displayChanged after creating a device, so call it here.
displayChanged();
}
+ int getDisplayID() {
+ return displayID;
+ }
+
/**
* Return a list of all configurations.
*/
@@ -183,13 +148,7 @@ Rectangle getBounds() {
}
public Insets getScreenInsets() {
- // the insets are queried synchronously and are not cached
- // since there are no Quartz or Cocoa means to receive notifications
- // on insets changes (e.g. when the Dock is resized):
- // the existing CGDisplayReconfigurationCallBack is not notified
- // as well as the NSApplicationDidChangeScreenParametersNotification
- // is fired on the Dock location changes only
- return nativeGetScreenInsets(displayID);
+ return screenInsets;
}
public int getScaleFactor() {
@@ -212,11 +171,37 @@ public void displayChanged() {
xResolution = nativeGetXResolution(displayID);
yResolution = nativeGetYResolution(displayID);
bounds = nativeGetBounds(displayID).getBounds(); //does integer rounding
+ screenInsets = nativeGetScreenInsets(displayID);
initScaleFactor();
resizeFSWindow(getFullScreenWindow(), bounds);
//TODO configs?
}
+ /**
+ * @return false if display parameters were changed, so we need to recreate the device.
+ */
+ boolean updateDevice() {
+ int s = scale;
+ double xr = xResolution, yr = yResolution;
+ var b = bounds;
+ displayChanged();
+ return s == scale && xr == xResolution && yr == yResolution && b.equals(bounds);
+ }
+
+ public void displayParametersChanged() {
+ Insets newScreenInsets = nativeGetScreenInsets(displayID);
+ if (!newScreenInsets.equals(screenInsets)) {
+ if (logger.isLoggable(PlatformLogger.Level.FINE)) {
+ logger.fine("Screen insets for display(" + displayID + ") changed " +
+ "[top=" + screenInsets.top + ",left=" + screenInsets.left +
+ ",bottom=" + screenInsets.bottom + ",right=" + screenInsets.right +
+ "]->[top=" + newScreenInsets.top + ",left=" + newScreenInsets.left +
+ ",bottom=" + newScreenInsets.bottom + ",right=" + newScreenInsets.right +
+ "]");
+ }
+ screenInsets = newScreenInsets;
+ }
+ }
@Override
public void paletteChanged() {
// devices do not need to react to this event.
@@ -380,11 +365,8 @@ public DisplayMode[] getDisplayModes() {
}
}
- public static boolean usingMetalPipeline() {
- return metalPipelineEnabled;
- }
-
private void initScaleFactor() {
+ int _scale = scale;
if (SunGraphicsEnvironment.isUIScaleEnabled()) {
double debugScale = SunGraphicsEnvironment.getDebugScale();
scale = (int) (debugScale >= 1
@@ -393,6 +375,9 @@ private void initScaleFactor() {
} else {
scale = 1;
}
+ if (_scale != scale && logger.isLoggable(PlatformLogger.Level.FINE)) {
+ logger.fine("current scale = " + _scale + ", new scale = " + scale + " (" + this + ")");
+ }
}
private static native double nativeGetScaleFactor(int displayID);
diff --git a/src/java.desktop/macosx/classes/sun/awt/CGraphicsEnvironment.java b/src/java.desktop/macosx/classes/sun/awt/CGraphicsEnvironment.java
index fe4431e30912..ab2850ef39f5 100644
--- a/src/java.desktop/macosx/classes/sun/awt/CGraphicsEnvironment.java
+++ b/src/java.desktop/macosx/classes/sun/awt/CGraphicsEnvironment.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -30,16 +30,22 @@
import java.awt.GraphicsDevice;
import java.awt.HeadlessException;
import java.awt.Toolkit;
+import java.io.File;
+import java.lang.annotation.Native;
import java.lang.ref.WeakReference;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
+import sun.java2d.MacOSFlags;
import sun.java2d.MacosxSurfaceManagerFactory;
import sun.java2d.SunGraphicsEnvironment;
import sun.java2d.SurfaceManagerFactory;
+import sun.util.logging.PlatformLogger;
/**
* This is an implementation of a GraphicsEnvironment object for the default
@@ -51,6 +57,14 @@
*/
public final class CGraphicsEnvironment extends SunGraphicsEnvironment {
+ private static final PlatformLogger logger =
+ PlatformLogger.getLogger(CGraphicsEnvironment.class.getName());
+
+ @Native private final static int MTL_SUPPORTED = 0;
+ @Native private final static int MTL_NO_DEVICE = 1;
+ @Native private final static int MTL_NO_SHADER_LIB = 2;
+ @Native private final static int MTL_ERROR = 3;
+
/**
* Fetch an array of all valid CoreGraphics display identifiers.
*/
@@ -67,9 +81,35 @@ public final class CGraphicsEnvironment extends SunGraphicsEnvironment {
*/
public static void init() { }
+ @SuppressWarnings("removal")
+ private static final String mtlShadersLib = AccessController.doPrivileged(
+ (PrivilegedAction) () ->
+ System.getProperty("java.home", "") + File.separator +
+ "lib" + File.separator + "shaders.metallib");
+
+ private static native int initMetal(String shaderLib);
+
static {
// Load libraries and initialize the Toolkit.
Toolkit.getDefaultToolkit();
+ metalPipelineEnabled = false;
+ if (MacOSFlags.isMetalEnabled()) {
+ int res = initMetal(mtlShadersLib);
+ if (res != MTL_SUPPORTED) {
+ if (logger.isLoggable(PlatformLogger.Level.FINE)) {
+ logger.fine("Cannot initialize Metal: " +
+ switch (res) {
+ case MTL_ERROR -> "Unexpected error.";
+ case MTL_NO_DEVICE -> "No MTLDevice.";
+ case MTL_NO_SHADER_LIB -> "No Metal shader library.";
+ default -> "Unexpected error (" + res + ").";
+ });
+ }
+ } else {
+ metalPipelineEnabled = true;
+ }
+ }
+
// Install the correct surface manager factory.
SurfaceManagerFactory.setInstance(new MacosxSurfaceManagerFactory());
}
@@ -88,6 +128,12 @@ public static void init() { }
*/
private native void deregisterDisplayReconfiguration(long context);
+ private static boolean metalPipelineEnabled;
+
+ public static boolean usingMetalPipeline() {
+ return metalPipelineEnabled;
+ }
+
/** Available CoreGraphics displays. */
private final Map devices = new HashMap<>(5);
/**
@@ -120,12 +166,17 @@ public CGraphicsEnvironment() {
}
}
+ public static String getMtlShadersLibPath() {
+ return mtlShadersLib;
+ }
+
/**
* Updates the list of devices and notify listeners.
*/
private void rebuildDevices() {
initDevices();
- displayChanged();
+ // Do not notify devices, this was already done in initDevices.
+ displayChanger.notifyListeners();
}
/**
@@ -166,7 +217,14 @@ private synchronized void initDevices() {
// So, we initialize the main display first, then retrieve actual list
// of displays, and then recheck the main display again.
if (!old.containsKey(mainDisplayID)) {
- old.put(mainDisplayID, new CGraphicsDevice(mainDisplayID));
+ try {
+ old.put(mainDisplayID, new CGraphicsDevice(mainDisplayID));
+ } catch (IllegalStateException e) {
+ if (logger.isLoggable(PlatformLogger.Level.FINE)) {
+ logger.fine("Unable to initialize graphics device for displayID=" +
+ mainDisplayID + " : " + e);
+ }
+ }
}
int[] displayIDs = getDisplayIDs();
@@ -175,8 +233,17 @@ private synchronized void initDevices() {
displayIDs = new int[]{mainDisplayID};
}
for (int id : displayIDs) {
- devices.put(id, old.containsKey(id) ? old.remove(id)
- : new CGraphicsDevice(id));
+ old.compute(id, (i, d) -> {
+ if (d != null && d.updateDevice()) {
+ // Device didn't change -> reuse
+ devices.put(i, d);
+ return null;
+ } else {
+ // Device changed -> create new
+ devices.put(i, new CGraphicsDevice(i));
+ return d;
+ }
+ });
}
// fetch the main display again, the old value might be outdated
mainDisplayID = getMainDisplayID();
@@ -212,9 +279,12 @@ private synchronized void initDevices() {
}
private CGraphicsDevice getSimilarDevice(CGraphicsDevice old) {
+ CGraphicsDevice sameId = devices.get(old.getDisplayID());
+ if (sameId != null) {
+ return sameId;
+ }
for (CGraphicsDevice device : devices.values()) {
if (device.getBounds().equals(old.getBounds())) {
- // for now we will use the bounds only
return device;
}
}
@@ -261,12 +331,15 @@ public Font[] getAllFonts() {
int numLogical = sLogicalFonts.length;
int numOtherFonts = superFonts.length;
- newFonts = new Font[numOtherFonts + numLogical];
- System.arraycopy(superFonts,0,newFonts,numLogical,numOtherFonts);
+ newFonts = new Font[numOtherFonts + numLogical * 4];
+ System.arraycopy(superFonts, 0, newFonts, numLogical * 4, numOtherFonts);
for (int i = 0; i < numLogical; i++)
{
- newFonts[i] = new Font(sLogicalFonts[i], Font.PLAIN, 1);
+ for (int j = 0; j < 4; j++)
+ {
+ newFonts[i * 4 + j] = new Font(sLogicalFonts[i], j, 1);
+ }
}
return newFonts;
}
diff --git a/src/java.desktop/macosx/classes/sun/font/CCharToGlyphMapper.java b/src/java.desktop/macosx/classes/sun/font/CCharToGlyphMapper.java
index 5fa60f1b7ec2..85f49d3f4898 100644
--- a/src/java.desktop/macosx/classes/sun/font/CCharToGlyphMapper.java
+++ b/src/java.desktop/macosx/classes/sun/font/CCharToGlyphMapper.java
@@ -28,6 +28,8 @@
import java.util.HashMap;
public class CCharToGlyphMapper extends CharToGlyphMapper {
+ private static final int UNMAPPED_CHAR = Integer.MIN_VALUE;
+
private static native int countGlyphs(final long nativeFontPtr);
private Cache cache = new Cache();
@@ -77,7 +79,8 @@ public synchronized boolean charsToGlyphsNS(int count,
if (code < FontUtilities.MIN_LAYOUT_CHARCODE) {
continue;
- } else if (FontUtilities.isComplexCharCode(code)) {
+ } else if (FontUtilities.isComplexCharCode(code) ||
+ CharToGlyphMapper.isVariationSelector(code)) {
return true;
} else if (code >= 0x10000) {
i += 1; // Empty glyph slot after surrogate
@@ -88,17 +91,33 @@ public synchronized boolean charsToGlyphsNS(int count,
return false;
}
+ public synchronized int charToVariationGlyph(int unicode, int variationSelector) {
+ if (variationSelector == 0) {
+ return charToGlyph(unicode);
+ }
+ final char[] unicodeArray = new char[4];
+ final int[] glyphArray = new int[4];
+
+ int size = Character.toChars(unicode, unicodeArray, 0);
+ size += Character.toChars(variationSelector, unicodeArray, size);
+
+ nativeCharsToGlyphs(fFont.getNativeFontPtr(), size, unicodeArray, glyphArray);
+
+ return glyphArray[0];
+ }
+
public synchronized int charToGlyph(char unicode) {
final int glyph = cache.get(unicode);
- if (glyph != 0) return glyph;
+ if (glyph != 0) return glyph == UNMAPPED_CHAR ? 0 : glyph;
final char[] unicodeArray = new char[] { unicode };
final int[] glyphArray = new int[1];
nativeCharsToGlyphs(fFont.getNativeFontPtr(), 1, unicodeArray, glyphArray);
- cache.put(unicode, glyphArray[0]);
+ int result = glyphArray[0];
+ cache.put(unicode, result == 0 ? UNMAPPED_CHAR : result);
- return glyphArray[0];
+ return result;
}
public synchronized int charToGlyph(int unicode) {
@@ -243,7 +262,7 @@ public synchronized void get(int count, char[] indices, int[] values)
final int value = get(code);
if (value != 0 && value != -1) {
- values[i] = value;
+ values[i] = value == UNMAPPED_CHAR ? 0 : value;
if (code >= 0x10000) {
values[i+1] = INVISIBLE_GLYPH_ID;
i++;
@@ -288,9 +307,10 @@ public synchronized void get(int count, char[] indices, int[] values)
low - LO_SURROGATE_START + 0x10000;
}
}
- values[i] = glyphCodes[m];
- put(code, values[i]);
- if (code >= 0x10000) {
+ values[i] = glyphCodes[m];
+ int glyphCode = values[i];
+ put(code, glyphCode == 0 ? UNMAPPED_CHAR : glyphCode);
+ if (code >= 0x10000) {
m++;
values[i + 1] = INVISIBLE_GLYPH_ID;
}
diff --git a/src/java.desktop/macosx/classes/sun/font/CCompositeFont.java b/src/java.desktop/macosx/classes/sun/font/CCompositeFont.java
new file mode 100644
index 000000000000..a26c55c35b80
--- /dev/null
+++ b/src/java.desktop/macosx/classes/sun/font/CCompositeFont.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2000-2023 JetBrains s.r.o.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package sun.font;
+
+import java.lang.ref.SoftReference;
+import java.util.ArrayList;
+import java.util.List;
+
+public final class CCompositeFont extends CompositeFont {
+ private final List fallbackFonts = new ArrayList<>();
+
+ public CCompositeFont(CFont font) {
+ super(new PhysicalFont[]{font});
+ mapper = new CCompositeGlyphMapper(this);
+ }
+
+ @Override
+ protected void initSlotMask() {
+ // List of fallback fonts can grow dynamically for CCompositeFont.
+ // Adding a new font to fallback list may require more bits
+ // to represent slot index, which will cause slotShift to increment,
+ // which in turn will invalidate all glyph codes returned earlier.
+ // This will cause rendering garbage when fallback list grows
+ // while rendering a chunk of text, so here we just set slotShift
+ // to fixed 8 bits and hope we never exceed it (just like before).
+ slotShift = 8;
+ slotMask = 0xff;
+ }
+
+ @Override
+ public synchronized int getNumSlots() {
+ return super.getNumSlots();
+ }
+
+ @Override
+ public CFont getSlotFont(int slot) {
+ if (slot == 0) return (CFont) super.getSlotFont(0);
+ synchronized (this) {
+ return fallbackFonts.get(slot - 1);
+ }
+ }
+
+ @Override
+ synchronized FontStrike getStrike(FontStrikeDesc desc, boolean copy) {
+ return super.getStrike(desc, copy);
+ }
+
+ @Override
+ protected synchronized int getValidatedGlyphCode(int glyphCode) {
+ return super.getValidatedGlyphCode(glyphCode);
+ }
+
+ @Override
+ public boolean hasSupplementaryChars() {
+ return false;
+ }
+
+ @Override
+ public boolean useAAForPtSize(int ptsize) {
+ return true;
+ }
+
+ public synchronized int findSlot(String fontName) {
+ for (int slot = 0; slot < numSlots; slot++) {
+ CFont slotFont = getSlotFont(slot);
+ if (fontName.equals(slotFont.getNativeFontName())) {
+ return slot;
+ }
+ }
+ return -1;
+ }
+
+ public synchronized int addSlot(CFont font) {
+ int slot = findSlot(font.getNativeFontName());
+ if (slot >= 0) return slot;
+ fallbackFonts.add(font);
+ lastFontStrike = new SoftReference<>(null);
+ strikeCache.clear();
+ return numSlots++;
+ }
+}
diff --git a/src/java.desktop/macosx/classes/sun/font/CCompositeGlyphMapper.java b/src/java.desktop/macosx/classes/sun/font/CCompositeGlyphMapper.java
index 1897c7af6420..a48fb61f7b07 100644
--- a/src/java.desktop/macosx/classes/sun/font/CCompositeGlyphMapper.java
+++ b/src/java.desktop/macosx/classes/sun/font/CCompositeGlyphMapper.java
@@ -25,131 +25,68 @@
package sun.font;
-public final class CCompositeGlyphMapper extends CompositeGlyphMapper {
-
- private CompositeFont font;
- private CharToGlyphMapper[] slotMappers;
+import java.awt.*;
- public CCompositeGlyphMapper(CompositeFont compFont) {
+public final class CCompositeGlyphMapper extends CompositeGlyphMapper {
+ public CCompositeGlyphMapper(CCompositeFont compFont) {
super(compFont);
- font = compFont;
- slotMappers = new CharToGlyphMapper[font.numSlots];
- missingGlyph = 0;
}
- private CharToGlyphMapper getSlotMapper(int slot) {
- CharToGlyphMapper mapper = slotMappers[slot];
- if (mapper == null) {
- mapper = font.getSlotFont(slot).getMapper();
- slotMappers[slot] = mapper;
+ @Override
+ public int charToVariationGlyph(int unicode, int variationSelector) {
+ if (variationSelector == 0) {
+ return charToGlyph(unicode);
+ } else {
+ int glyph = convertToGlyph(unicode, variationSelector);
+ if (glyph == missingGlyph) glyph = charToGlyph(unicode);
+ return glyph;
}
- return mapper;
}
- public boolean canDisplay(char ch) {
- int glyph = charToGlyph(ch);
- return glyph != missingGlyph;
+ @Override
+ protected int convertToGlyph(int unicode) {
+ int glyph = convertToGlyph(unicode, 0);
+ setCachedGlyphCode(unicode, glyph);
+ return glyph;
}
- private int convertToGlyph(int unicode) {
- for (int slot = 0; slot < font.numSlots; slot++) {
- CharToGlyphMapper mapper = getSlotMapper(slot);
- int glyphCode = mapper.charToGlyph(unicode);
- // The CFont Mappers will return a negative code
- // for fonts that will fill the glyph from fallbacks
- // - cascading font in OSX-speak. But we need to be
- // know here that only the codes > 0 are really present.
- if (glyphCode > 0) {
- glyphCode = compositeGlyphCode(slot, glyphCode);
- return glyphCode;
- }
+ @Override
+ protected int convertToGlyph(int unicode, int variationSelector) {
+ CCompositeFont compositeFont = (CCompositeFont) font;
+ CFont mainFont = (CFont) font.getSlotFont(0);
+ String[] fallbackFontInfo = new String[2];
+ int glyphCode = nativeCodePointToGlyph(mainFont.getNativeFontPtr(), unicode, variationSelector, fallbackFontInfo);
+ if (glyphCode == missingGlyph) {
+ return missingGlyph;
}
- return missingGlyph;
- }
-
- public int getNumGlyphs() {
- int numGlyphs = 0;
- for (int slot=0; slot<1 /*font.numSlots*/; slot++) {
- CharToGlyphMapper mapper = slotMappers[slot];
- if (mapper == null) {
- mapper = font.getSlotFont(slot).getMapper();
- slotMappers[slot] = mapper;
- }
- numGlyphs += mapper.getNumGlyphs();
+ String fallbackFontName = fallbackFontInfo[0];
+ String fallbackFontFamilyName = fallbackFontInfo[1];
+ if (fallbackFontName == null || fallbackFontFamilyName == null) {
+ return font.compositeGlyphCode(0, glyphCode);
}
- return numGlyphs;
- }
-
- public int charToGlyph(int unicode) {
- return convertToGlyph(unicode);
- }
-
- public int charToGlyph(char unicode) {
- return convertToGlyph(unicode);
- }
-
- public boolean charsToGlyphsNS(int count, char[] unicodes, int[] glyphs) {
-
- for (int i=0; i= HI_SURROGATE_START &&
- code <= HI_SURROGATE_END && i < count - 1) {
- char low = unicodes[i + 1];
- if (low >= LO_SURROGATE_START &&
- low <= LO_SURROGATE_END) {
- code = (code - HI_SURROGATE_START) *
- 0x400 + low - LO_SURROGATE_START + 0x10000;
- glyphs[i + 1] = INVISIBLE_GLYPH_ID;
- }
+ int slot = compositeFont.findSlot(fallbackFontName);
+
+ if (slot < 0) {
+ Font2D fallbackFont = FontManagerFactory.getInstance().findFont2D(fallbackFontName,
+ Font.PLAIN, FontManager.NO_FALLBACK);
+ if (!(fallbackFont instanceof CFont) ||
+ !fallbackFontName.equals(((CFont) fallbackFont).getNativeFontName())) {
+ // Native font fallback mechanism can return "hidden" fonts - their names start with dot,
+ // and they are not returned in a list of fonts available in system, but they can still be used
+ // if requested explicitly.
+ fallbackFont = new CFont(fallbackFontName, fallbackFontFamilyName, null);
}
- glyphs[i] = convertToGlyph(code);
+ if (mainFont.isFakeItalic()) fallbackFont = ((CFont)fallbackFont).createItalicVariant(false);
- if (code < FontUtilities.MIN_LAYOUT_CHARCODE) {
- continue;
- }
- else if (FontUtilities.isComplexCharCode(code)) {
- return true;
- }
- else if (code >= 0x10000) {
- i += 1; // Empty glyph slot after surrogate
- continue;
- }
- }
-
- return false;
- }
-
- public void charsToGlyphs(int count, char[] unicodes, int[] glyphs) {
- for (int i=0; i= HI_SURROGATE_START &&
- code <= HI_SURROGATE_END && i < count - 1) {
- char low = unicodes[i + 1];
-
- if (low >= LO_SURROGATE_START &&
- low <= LO_SURROGATE_END) {
- code = (code - HI_SURROGATE_START) *
- 0x400 + low - LO_SURROGATE_START + 0x10000;
-
- glyphs[i] = convertToGlyph(code);
- i += 1; // Empty glyph slot after surrogate
- glyphs[i] = INVISIBLE_GLYPH_ID;
- continue;
- }
- }
-
- glyphs[i] = convertToGlyph(code);
+ slot = compositeFont.addSlot((CFont) fallbackFont);
}
- }
- public void charsToGlyphs(int count, int[] unicodes, int[] glyphs) {
- for (int i=0; i listOfString);
-
- private CompositeFont createCompositeFont() {
- ArrayList listOfString = new ArrayList();
- getCascadeList(nativeFontPtr, listOfString);
-
- // In some italic cases the standard Mac cascade list is missing Arabic.
- listOfString.add("GeezaPro");
- CFontManager fm = (CFontManager) FontManagerFactory.getInstance();
- int numFonts = 1 + listOfString.size();
- PhysicalFont[] fonts = new PhysicalFont[numFonts];
- fonts[0] = this;
- int idx = 1;
- if (FontUtilities.isLogging()) {
- FontUtilities.logInfo("Cascading list for " + this + " :");
- }
- for (String s : listOfString) {
- if (FontUtilities.isLogging()) {
- FontUtilities.logInfo("Fallback:" + s);
- }
- if (s.equals(".AppleSymbolsFB")) {
- // Don't know why we get the weird name above .. replace.
- s = "AppleSymbols";
- }
- Font2D f2d = fm.getOrCreateFallbackFont(s);
- if (f2d == null || f2d == this) {
- continue;
- }
- fonts[idx++] = (PhysicalFont)f2d;
- }
- if (idx < fonts.length) {
- PhysicalFont[] orig = fonts;
- fonts = new PhysicalFont[idx];
- System.arraycopy(orig, 0, fonts, 0, idx);
- }
- CompositeFont compFont = new CompositeFont(fonts);
- compFont.mapper = new CCompositeGlyphMapper(compFont);
- return compFont;
- }
-
private CompositeFont compFont;
public CompositeFont getCompositeFont2D() {
if (compFont == null) {
- compFont = createCompositeFont();
+ compFont = new CCompositeFont(this);
}
return compFont;
}
@@ -270,6 +230,19 @@ protected FontStrike createStrike(FontStrikeDesc desc) {
return new CStrike(this, desc);
}
+ boolean isFakeItalic() {
+ return isFakeItalic;
+ }
+
+ String getNativeFontName() {
+ return nativeFontName;
+ }
+
+ @Override
+ public String getTypographicSubfamilyName() {
+ return faceName == null ? super.getTypographicSubfamilyName() : faceName;
+ }
+
// sun.font.Font2D caches the last used strike,
// but does not check if the properties of the strike match the properties
// of the incoming java.awt.Font object (size, style, etc).
@@ -297,4 +270,8 @@ public String toString() {
", familyName: " + familyName + ", style: " + style +
" } aka: " + super.toString();
}
+
+ public Font2D createItalic() {
+ return this.createItalicVariant(true);
+ }
}
diff --git a/src/java.desktop/macosx/classes/sun/font/CFontManager.java b/src/java.desktop/macosx/classes/sun/font/CFontManager.java
index baab32571535..d786b95d0cbb 100644
--- a/src/java.desktop/macosx/classes/sun/font/CFontManager.java
+++ b/src/java.desktop/macosx/classes/sun/font/CFontManager.java
@@ -33,21 +33,16 @@
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Locale;
-import java.util.Map;
import java.util.TreeMap;
-import java.util.Vector;
-import java.util.concurrent.ConcurrentHashMap;
import javax.swing.plaf.FontUIResource;
import sun.awt.FontConfiguration;
import sun.awt.HeadlessToolkit;
-import sun.awt.util.ThreadGroupUtils;
import sun.lwawt.macosx.*;
public final class CFontManager extends SunFontManager {
private static Hashtable genericFonts = new Hashtable();
- private final Map fallbackFonts = new ConcurrentHashMap<>();
@Override
protected FontConfiguration createFontConfiguration() {
@@ -144,6 +139,19 @@ protected void addNativeFontFamilyNames(TreeMap familyNames, Loc
}
}
+ @Override
+ protected String getSystemFontVersion(TrueTypeFont bundledFont) {
+ String version = getNativeFontVersion(bundledFont.getPostscriptName());
+ return version != null ? TrueTypeFont.parseVersion(version) : "0";
+ }
+
+ @Override
+ protected void loadJREFonts(String[] fonts) {
+ for (String name : fonts) {
+ loadNativeDirFonts(name);
+ }
+ }
+
protected void registerFontsInDir(final String dirName, boolean useJavaRasterizer,
int fontRank, boolean defer, boolean resolveSymLinks) {
@@ -164,44 +172,14 @@ protected void registerFontsInDir(final String dirName, boolean useJavaRasterize
private native void loadNativeDirFonts(String fontPath);
private native void loadNativeFonts();
+ native String getNativeFontVersion(String psName);
- void registerFont(String fontName, String fontFamilyName) {
- final CFont font = new CFont(fontName, fontFamilyName);
-
+ void registerFont(String fontName, String fontFamilyName, String faceName) {
+ // Use different family for specific font faces
+ final CFont font = new CFont(fontName, jreFamilyMap.getOrDefault(fontName, fontFamilyName), faceName);
registerGenericFont(font);
}
- void registerItalicDerived() {
- FontFamily[] famArr = FontFamily.getAllFontFamilies();
- for (int i=0; i fontToFileMap, HashMap fontToFamilyNameMap,
HashMap> familyToFontListMap, Locale locale) {}
-
- Font2D getOrCreateFallbackFont(String fontName) {
- Font2D font2D = findFont2D(fontName, Font.PLAIN, FontManager.NO_FALLBACK);
- if (font2D != null || fontName.startsWith(".")) {
- return font2D;
- } else {
- // macOS doesn't list some system fonts in [NSFontManager availableFontFamilies] output,
- // so they are not registered in font manager as part of 'loadNativeFonts'.
- // These fonts are present in [NSFontManager availableFonts] output though,
- // and can be accessed in the same way as other system fonts.
- return fallbackFonts.computeIfAbsent(fontName, name -> new CFont(name, null));
- }
- }
}
diff --git a/src/java.desktop/macosx/classes/sun/font/CStrike.java b/src/java.desktop/macosx/classes/sun/font/CStrike.java
index eb049c3d449d..2863325fddb5 100644
--- a/src/java.desktop/macosx/classes/sun/font/CStrike.java
+++ b/src/java.desktop/macosx/classes/sun/font/CStrike.java
@@ -25,9 +25,15 @@
package sun.font;
+import sun.lwawt.macosx.concurrent.Dispatch;
+
import java.awt.Rectangle;
import java.awt.geom.*;
-import java.util.*;
+import java.util.HashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
+import sun.lwawt.macosx.CThreading;
+import static sun.awt.SunHints.*;
+
public final class CStrike extends PhysicalStrike {
@@ -36,7 +42,9 @@ private static native long createNativeStrikePtr(long nativeFontPtr,
double[] glyphTx,
double[] invDevTxMatrix,
int aaHint,
- int fmHint);
+ int fmHint,
+ int subpixelResolutionX,
+ int subpixelResolutionY);
// Disposes the native strike
private static native void disposeNativeStrikePtr(long nativeStrikePtr);
@@ -60,6 +68,16 @@ private static native GeneralPath getNativeGlyphOutline(long nativeStrikePtr,
double x,
double y);
+ private static native void getNativeGlyphRenderData(long nativeStrikePtr,
+ int glyphCode,
+ double x,
+ double y,
+ GlyphRenderData result);
+
+ private static native void getNativeGlyphOutlineBounds(long nativeStrikePtr,
+ int glyphCode,
+ float [] rectData);
+
// returns the bounding rect for a glyph
private static native void getNativeGlyphImageBounds(long nativeStrikePtr,
int glyphCode,
@@ -119,7 +137,9 @@ public long getNativeStrikePtr() {
}
nativeStrikePtr =
createNativeStrikePtr(nativeFont.getNativeFontPtr(),
- glyphTx, invDevTxMatrix, aaHint, fmHint);
+ glyphTx, invDevTxMatrix, aaHint, fmHint,
+ FontUtilities.subpixelResolution.width,
+ FontUtilities.subpixelResolution.height);
}
return nativeStrikePtr;
@@ -173,19 +193,9 @@ Point2D.Float getGlyphMetrics(final int glyphCode) {
}
Rectangle2D.Float getGlyphOutlineBounds(int glyphCode) {
- GeneralPath gp = getGlyphOutline(glyphCode, 0f, 0f);
- Rectangle2D r2d = gp.getBounds2D();
- Rectangle2D.Float r2df;
- if (r2d instanceof Rectangle2D.Float) {
- r2df = (Rectangle2D.Float)r2d;
- } else {
- float x = (float)r2d.getX();
- float y = (float)r2d.getY();
- float w = (float)r2d.getWidth();
- float h = (float)r2d.getHeight();
- r2df = new Rectangle2D.Float(x, y, w, h);
- }
- return r2df;
+ final float [] rectData = new float[4];
+ getNativeGlyphOutlineBounds(getNativeStrikePtr(), glyphCode, rectData);
+ return new Rectangle2D.Float(rectData[0], rectData[1], rectData[2], rectData[3]);
}
// pt, result in device space
@@ -203,7 +213,15 @@ void getGlyphImageBounds(int glyphCode, Point2D.Float pt, Rectangle result) {
return;
}
- result.setRect(floatRect.x + pt.x, floatRect.y + pt.y, floatRect.width, floatRect.height);
+ boolean subpixel = desc.aaHint == INTVAL_TEXT_ANTIALIAS_ON &&
+ desc.fmHint == INTVAL_FRACTIONALMETRICS_ON;
+ float subpixelResolutionX = subpixel ? FontUtilities.subpixelResolution.width : 1;
+ float subpixelResolutionY = subpixel ? FontUtilities.subpixelResolution.height : 1;
+ // Before rendering, glyph positions are offset by 0.5 pixels, take into consideration
+ float x = ((int) (pt.x * subpixelResolutionX + 0.5f)) / subpixelResolutionX;
+ float y = ((int) (pt.y * subpixelResolutionY + 0.5f)) / subpixelResolutionY;
+
+ result.setRect(floatRect.x + x, floatRect.y + y, floatRect.width, floatRect.height);
}
private void getGlyphImageBounds(int glyphCode, float x, float y, Rectangle2D.Float floatRect) {
@@ -214,9 +232,10 @@ GeneralPath getGlyphOutline(int glyphCode, float x, float y) {
return getNativeGlyphOutline(getNativeStrikePtr(), glyphCode, x, y);
}
- // should implement, however not called though any path that is publicly exposed
- GeneralPath getGlyphVectorOutline(int[] glyphs, float x, float y) {
- throw new Error("not implemented yet");
+ GlyphRenderData getGlyphRenderData(int glyphCode, float x, float y) {
+ GlyphRenderData result = new GlyphRenderData();
+ getNativeGlyphRenderData(getNativeStrikePtr(), glyphCode, x, y, result);
+ return result;
}
// called from the Sun2D renderer
@@ -355,7 +374,7 @@ private static class GlyphInfoCache extends CStrikeDisposer {
private static final int SECOND_LAYER_SIZE = 16384; // 16384 = 128x128
// rdar://problem/5204197
- private boolean disposed = false;
+ private final AtomicBoolean disposed = new AtomicBoolean(false);
private final long[] firstLayerCache;
private SparseBitShiftingTwoLayerArray secondLayerCache;
@@ -418,41 +437,50 @@ public synchronized void put(final int index, final long value) {
}
public synchronized void dispose() {
- // rdar://problem/5204197
- // Note that sun.font.Font2D.getStrike() actively disposes
- // cleared strikeRef. We need to check the disposed flag to
- // prevent double frees of native resources.
- if (disposed) {
- return;
- }
-
- super.dispose();
-
- // clean out the first array
- disposeLongArray(firstLayerCache);
-
- // clean out the two layer arrays
- if (secondLayerCache != null) {
- final long[][] secondLayerLongArrayArray = secondLayerCache.cache;
- for (int i = 0; i < secondLayerLongArrayArray.length; i++) {
- final long[] longArray = secondLayerLongArrayArray[i];
- if (longArray != null) disposeLongArray(longArray);
- }
- }
+ final Runnable command = () -> {
+ // rdar://problem/5204197
+ // Note that sun.font.Font2D.getStrike() actively disposes
+ // cleared strikeRef. We need to check the disposed flag to
+ // prevent double frees of native resources.
+ if (disposed.compareAndSet(false, true)) {
+
+ super.dispose();
+
+ // clean out the first array
+ disposeLongArray(firstLayerCache);
+
+ // clean out the two layer arrays
+ if (secondLayerCache != null) {
+ final long[][] secondLayerLongArrayArray = secondLayerCache.cache;
+ for (final long[] longArray : secondLayerLongArrayArray) {
+ if (longArray != null) disposeLongArray(longArray);
+ }
+ }
- // clean up everyone else
- if (generalCache != null) {
- for (long longValue : generalCache.values()) {
- if (longValue != -1 && longValue != 0) {
- removeGlyphInfoFromCache(longValue);
- StrikeCache.freeLongPointer(longValue);
+ // clean up everyone else
+ if (generalCache != null) {
+ for (long longValue : generalCache.values()) {
+ if (longValue != -1 && longValue != 0) {
+ removeGlyphInfoFromCache(longValue);
+ StrikeCache.freeLongPointer(longValue);
+ }
+ }
}
}
- }
-
- // rdar://problem/5204197
- // Finally, set the flag.
- disposed = true;
+ };
+
+ // Move disposal code to AppKit thread in order to avoid the
+ // following deadlock:
+ // 1) CGLGraphicsConfig.getCGLConfigInfo (called from Java2D
+ // disposal thread) takes RenderQueue.lock
+ // 2) CGLLayer.drawInCGLContext is invoked on AppKit thread and
+ // blocked on RenderQueue.lock
+ // 1) invokes native block on AppKit and wait
+ //
+ // If dispatch instance is not available, run the code on
+ // disposal thread as before
+
+ CThreading.executeOnAppKit(command);
}
private static void disposeLongArray(final long[] longArray) {
diff --git a/src/java.desktop/macosx/classes/sun/font/CStrikeDisposer.java b/src/java.desktop/macosx/classes/sun/font/CStrikeDisposer.java
index b0c8c788ec99..2e72b977626c 100644
--- a/src/java.desktop/macosx/classes/sun/font/CStrikeDisposer.java
+++ b/src/java.desktop/macosx/classes/sun/font/CStrikeDisposer.java
@@ -25,6 +25,11 @@
package sun.font;
+import sun.lwawt.macosx.concurrent.Dispatch;
+import sun.lwawt.macosx.CThreading;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
/*
* This keeps track of data that needs to be cleaned up once a
* strike is freed.
@@ -49,6 +54,7 @@
class CStrikeDisposer extends FontStrikeDisposer {
long pNativeScalerContext;
+ private final AtomicBoolean disposed = new AtomicBoolean(false);
public CStrikeDisposer(Font2D font2D, FontStrikeDesc desc,
long pContext, int[] images)
@@ -76,12 +82,23 @@ public CStrikeDisposer(Font2D font2D, FontStrikeDesc desc) {
}
public synchronized void dispose() {
- if (!disposed) {
- if (pNativeScalerContext != 0L) {
- freeNativeScalerContext(pNativeScalerContext);
+ final Runnable command = () -> {
+ if (disposed.compareAndSet(false, true)) {
+ if (pNativeScalerContext != 0L) {
+ freeNativeScalerContext(pNativeScalerContext);
+ }
+ super.dispose();
}
- super.dispose();
- }
+ };
+ // Move disposal code to AppKit thread in order to avoid the
+ // following deadlock:
+ // 1) CGLGraphicsConfig.getCGLConfigInfo (called from Java2D
+ // disposal thread) takes RenderQueue.lock
+ // 2) CGLLayer.drawInCGLContext is invoked on AppKit thread and
+ // blocked on RenderQueue.lock
+ // 1) invokes native block on AppKit and wait
+
+ CThreading.executeOnAppKit(command);
}
private native void freeNativeScalerContext(long pContext);
diff --git a/src/java.desktop/macosx/classes/sun/font/NativeFont.java b/src/java.desktop/macosx/classes/sun/font/NativeFont.java
index d3203da3980a..a24ab1221051 100644
--- a/src/java.desktop/macosx/classes/sun/font/NativeFont.java
+++ b/src/java.desktop/macosx/classes/sun/font/NativeFont.java
@@ -79,12 +79,6 @@ public GeneralPath getGlyphOutline(long pScalerContext,
return null;
}
- public GeneralPath getGlyphVectorOutline(long pScalerContext,
- int[] glyphs, int numGlyphs,
- float x, float y) {
- return null;
- }
-
long getGlyphImage(long pScalerContext, int glyphCode) {
return 0L;
@@ -104,4 +98,8 @@ Rectangle2D.Float getGlyphOutlineBounds(long pScalerContext,
int glyphCode) {
return new Rectangle2D.Float(0f, 0f, 0f, 0f);
}
+
+ public SlotInfo getSlotInfoForGlyph(int glyphCode) {
+ return new SlotInfo(getDelegateFont());
+ }
}
diff --git a/src/java.desktop/macosx/classes/sun/font/NativeStrike.java b/src/java.desktop/macosx/classes/sun/font/NativeStrike.java
index de5c3aca312d..3afaf2c150a3 100644
--- a/src/java.desktop/macosx/classes/sun/font/NativeStrike.java
+++ b/src/java.desktop/macosx/classes/sun/font/NativeStrike.java
@@ -79,7 +79,7 @@ GeneralPath getGlyphOutline(int glyphCode, float x, float y) {
return null;
}
- GeneralPath getGlyphVectorOutline(int[] glyphs, float x, float y) {
+ GlyphRenderData getGlyphRenderData(int glyphCode, float x, float y) {
return null;
}
diff --git a/src/java.desktop/macosx/classes/sun/java2d/MacOSFlags.java b/src/java.desktop/macosx/classes/sun/java2d/MacOSFlags.java
index 37b573a339b2..5cd9420d8a86 100644
--- a/src/java.desktop/macosx/classes/sun/java2d/MacOSFlags.java
+++ b/src/java.desktop/macosx/classes/sun/java2d/MacOSFlags.java
@@ -41,6 +41,7 @@ public class MacOSFlags {
private static boolean oglEnabled;
private static boolean oglVerbose;
private static boolean metalEnabled;
+ private static boolean metalDisplaySyncEnabled;
private static boolean metalVerbose;
private enum PropertyState {ENABLED, DISABLED, UNSPECIFIED}
@@ -115,6 +116,9 @@ private static void initJavaFlags() {
metalEnabled = false;
}
+ metalDisplaySyncEnabled = metalEnabled &&
+ !"false".equals(System.getProperty("sun.java2d.metal.displaySync"));
+
oglVerbose = isBooleanPropTrueVerbose("sun.java2d.opengl");
metalVerbose = isBooleanPropTrueVerbose("sun.java2d.metal");
@@ -153,4 +157,12 @@ public static boolean isOGLEnabled() {
public static boolean isOGLVerbose() {
return oglVerbose;
}
+
+ public static boolean isMetalDisplaySyncEnabled() {
+ return metalDisplaySyncEnabled;
+ }
+
+ public static String getRenderPipelineName() {
+ return metalEnabled? "Metal" : oglEnabled ? "OpenGL" : "Unknown";
+ }
}
diff --git a/src/java.desktop/macosx/classes/sun/java2d/MacosxSurfaceManagerFactory.java b/src/java.desktop/macosx/classes/sun/java2d/MacosxSurfaceManagerFactory.java
index 93fac3fb22a4..b2d103740ce8 100644
--- a/src/java.desktop/macosx/classes/sun/java2d/MacosxSurfaceManagerFactory.java
+++ b/src/java.desktop/macosx/classes/sun/java2d/MacosxSurfaceManagerFactory.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -25,9 +25,9 @@
package sun.java2d;
+import sun.awt.CGraphicsEnvironment;
import sun.awt.image.SunVolatileImage;
import sun.awt.image.VolatileSurfaceManager;
-import sun.awt.CGraphicsDevice;
import sun.java2d.metal.MTLVolatileSurfaceManager;
import sun.java2d.opengl.CGLVolatileSurfaceManager;
@@ -51,7 +51,7 @@ public class MacosxSurfaceManagerFactory extends SurfaceManagerFactory {
public VolatileSurfaceManager createVolatileManager(SunVolatileImage vImg,
Object context)
{
- return CGraphicsDevice.usingMetalPipeline() ? new MTLVolatileSurfaceManager(vImg, context) :
+ return CGraphicsEnvironment.usingMetalPipeline() ? new MTLVolatileSurfaceManager(vImg, context) :
new CGLVolatileSurfaceManager(vImg, context);
}
}
diff --git a/src/java.desktop/macosx/classes/sun/java2d/metal/MTLGraphicsConfig.java b/src/java.desktop/macosx/classes/sun/java2d/metal/MTLGraphicsConfig.java
index 8a992361b962..143a13aa5735 100644
--- a/src/java.desktop/macosx/classes/sun/java2d/metal/MTLGraphicsConfig.java
+++ b/src/java.desktop/macosx/classes/sun/java2d/metal/MTLGraphicsConfig.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -27,6 +27,7 @@
import sun.awt.CGraphicsConfig;
import sun.awt.CGraphicsDevice;
+import sun.awt.CGraphicsEnvironment;
import sun.awt.image.OffScreenImage;
import sun.awt.image.SunVolatileImage;
import sun.awt.image.SurfaceManager;
@@ -58,9 +59,6 @@
import java.awt.image.DirectColorModel;
import java.awt.image.VolatileImage;
import java.awt.image.WritableRaster;
-import java.io.File;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
import static sun.java2d.metal.MTLContext.MTLContextCaps.CAPS_EXT_GRAD_SHADER;
import static sun.java2d.pipe.hw.AccelSurface.TEXTURE;
@@ -74,13 +72,6 @@ public final class MTLGraphicsConfig extends CGraphicsConfig
{
private static ImageCapabilities imageCaps = new MTLImageCaps();
- @SuppressWarnings("removal")
- private static final String mtlShadersLib = AccessController.doPrivileged(
- (PrivilegedAction) () ->
- System.getProperty("java.home", "") + File.separator +
- "lib" + File.separator + "shaders.metallib");
-
-
private BufferCapabilities bufferCaps;
private long pConfigInfo;
private ContextCapabilities mtlCaps;
@@ -88,7 +79,6 @@ public final class MTLGraphicsConfig extends CGraphicsConfig
private final Object disposerReferent = new Object();
private final int maxTextureSize;
- private static native boolean tryLoadMetalLibrary(int displayID, String shaderLib);
private static native long getMTLConfigInfo(int displayID, String mtlShadersLib);
/**
@@ -125,18 +115,14 @@ public SurfaceData createManagedSurface(int w, int h, int transparency) {
}
public static MTLGraphicsConfig getConfig(CGraphicsDevice device,
- int displayID)
+ int displayID, StringBuilder errorMessage)
{
- if (!tryLoadMetalLibrary(displayID, mtlShadersLib)) {
- return null;
- }
-
long cfginfo = 0;
int textureSize = 0;
MTLRenderQueue rq = MTLRenderQueue.getInstance();
rq.lock();
try {
- cfginfo = getMTLConfigInfo(displayID, mtlShadersLib);
+ cfginfo = getMTLConfigInfo(displayID, CGraphicsEnvironment.getMtlShadersLibPath());
if (cfginfo != 0L) {
textureSize = nativeGetMaxTextureSize();
// TODO : This clamping code is same as in OpenGL.
@@ -149,6 +135,7 @@ public static MTLGraphicsConfig getConfig(CGraphicsDevice device,
rq.unlock();
}
if (cfginfo == 0) {
+ errorMessage.append(" Cannot create MTLConfigInfo.");
return null;
}
@@ -158,6 +145,7 @@ public static MTLGraphicsConfig getConfig(CGraphicsDevice device,
CAPS_MULTITEXTURE | CAPS_TEXNONPOW2 | CAPS_TEXNONSQUARE |
CAPS_EXT_BIOP_SHADER | CAPS_EXT_GRAD_SHADER,
null);
+
return new MTLGraphicsConfig(device, cfginfo, textureSize, caps);
}
@@ -309,6 +297,11 @@ public void flip(final LWComponentPeer, ?> peer, final Image backBuffer,
}
}
+ @Override
+ public void flush(LWComponentPeer, ?> peer) {
+ MTLSurfaceData.flushBuffer(peer.getPlatformWindow().getLayerPtr());
+ }
+
private static class MTLBufferCaps extends BufferCapabilities {
public MTLBufferCaps(boolean dblBuf) {
super(imageCaps, imageCaps,
diff --git a/src/java.desktop/macosx/classes/sun/java2d/metal/MTLLayer.java b/src/java.desktop/macosx/classes/sun/java2d/metal/MTLLayer.java
index 890c55376c78..59366ed6d05b 100644
--- a/src/java.desktop/macosx/classes/sun/java2d/metal/MTLLayer.java
+++ b/src/java.desktop/macosx/classes/sun/java2d/metal/MTLLayer.java
@@ -29,10 +29,13 @@
import sun.java2d.SurfaceData;
import sun.lwawt.LWWindowPeer;
import sun.lwawt.macosx.CFLayer;
+import sun.util.logging.PlatformLogger;
+
import java.awt.GraphicsConfiguration;
import java.awt.Insets;
public class MTLLayer extends CFLayer {
+ private static final PlatformLogger logger = PlatformLogger.getLogger(MTLLayer.class.getName());
private native long nativeCreateLayer();
private static native void nativeSetScale(long layerPtr, double scale);
@@ -50,9 +53,17 @@ public MTLLayer(LWWindowPeer peer) {
setPtr(nativeCreateLayer());
this.peer = peer;
+
+ MTLGraphicsConfig gc = (MTLGraphicsConfig)getGraphicsConfiguration();
+ if (logger.isLoggable(PlatformLogger.Level.FINE)) {
+ logger.fine("device = " + (gc != null ? gc.getDevice() : "null"));
+ }
+ if (gc != null) {
+ setScale(gc.getDevice().getScaleFactor());
+ }
}
- public SurfaceData replaceSurfaceData() {
+ public SurfaceData replaceSurfaceData(int scale) {
if (getBounds().isEmpty()) {
surfaceData = NullSurfaceData.theInstance;
return surfaceData;
@@ -62,7 +73,10 @@ public SurfaceData replaceSurfaceData() {
// and blits the buffer to the layer surface (in display callback)
MTLGraphicsConfig gc = (MTLGraphicsConfig)getGraphicsConfiguration();
surfaceData = gc.createSurfaceData(this);
- setScale(gc.getDevice().getScaleFactor());
+ if (scale <= 0) {
+ scale = gc.getDevice().getScaleFactor();
+ }
+ setScale(scale);
if (peer != null) {
Insets insets = peer.getInsets();
execute(ptr -> nativeSetInsets(ptr, insets.top, insets.left));
@@ -100,6 +114,10 @@ public void dispose() {
private void setScale(final int _scale) {
if (scale != _scale) {
+ if (logger.isLoggable(PlatformLogger.Level.FINE)) {
+ MTLGraphicsConfig gc = (MTLGraphicsConfig)getGraphicsConfiguration();
+ logger.fine("current scale = " + scale + ", new scale = " + _scale + " (device = " + (gc != null ? gc.getDevice() : "null") + ")");
+ }
scale = _scale;
execute(ptr -> nativeSetScale(ptr, scale));
}
diff --git a/src/java.desktop/macosx/classes/sun/java2d/metal/MTLSurfaceData.java b/src/java.desktop/macosx/classes/sun/java2d/metal/MTLSurfaceData.java
index 9204bffda6a3..829c3226e085 100644
--- a/src/java.desktop/macosx/classes/sun/java2d/metal/MTLSurfaceData.java
+++ b/src/java.desktop/macosx/classes/sun/java2d/metal/MTLSurfaceData.java
@@ -53,6 +53,7 @@
import static sun.java2d.pipe.BufferedOpCodes.DISPOSE_SURFACE;
import static sun.java2d.pipe.BufferedOpCodes.FLUSH_SURFACE;
+import static sun.java2d.pipe.BufferedOpCodes.FLUSH_BUFFER;
import static sun.java2d.pipe.hw.ContextCapabilities.CAPS_MULTITEXTURE;
import static sun.java2d.pipe.hw.ContextCapabilities.CAPS_PS30;
@@ -220,7 +221,7 @@ public Rectangle getBounds() {
protected native boolean initRTexture(long pData, boolean isOpaque, int width, int height);
- protected native boolean initFlipBackbuffer(long pData);
+ protected native boolean initFlipBackbuffer(long pData, boolean isOpaque, int width, int height);
@Override
public SurfaceDataProxy makeProxyFor(SurfaceData srcData) {
@@ -246,7 +247,7 @@ private void initSurfaceNow(int width, int height) {
break;
case FLIP_BACKBUFFER:
- success = initFlipBackbuffer(getNativeOps());
+ success = initFlipBackbuffer(getNativeOps(), isOpaque, width, height);
break;
default:
@@ -270,6 +271,7 @@ protected void initSurface(final int width, final int height) {
switch (type) {
case TEXTURE:
case RT_TEXTURE:
+ case FLIP_BACKBUFFER:
// need to make sure the context is current before
// creating the texture
MTLContext.setScratchSurface(graphicsConfig);
@@ -554,7 +556,7 @@ public static class MTLLayerSurfaceData extends MTLSurfaceData {
private MTLLayerSurfaceData(MTLLayer layer, MTLGraphicsConfig gc,
int width, int height) {
- super(layer, gc, gc.getColorModel(), RT_TEXTURE, width, height);
+ super(layer, gc, gc.getColorModel(), FLIP_BACKBUFFER, width, height);
this.layer = layer;
initSurface(this.width, this.height);
}
@@ -639,4 +641,18 @@ public static void dispose(long pData, MTLGraphicsConfig gc) {
rq.unlock();
}
}
+
+ static void flushBuffer(long layer) {
+ MTLRenderQueue rq = MTLRenderQueue.getInstance();
+ rq.lock();
+ try {
+ RenderBuffer buf = rq.getBuffer();
+ rq.ensureCapacityAndAlignment(12, 4);
+ buf.putInt(FLUSH_BUFFER);
+ buf.putLong(layer);
+ rq.flushNow();
+ } finally {
+ rq.unlock();
+ }
+ }
}
diff --git a/src/java.desktop/macosx/classes/sun/java2d/opengl/CGLGraphicsConfig.java b/src/java.desktop/macosx/classes/sun/java2d/opengl/CGLGraphicsConfig.java
index bea5963383e3..77ecafd87c0b 100644
--- a/src/java.desktop/macosx/classes/sun/java2d/opengl/CGLGraphicsConfig.java
+++ b/src/java.desktop/macosx/classes/sun/java2d/opengl/CGLGraphicsConfig.java
@@ -26,6 +26,7 @@
package sun.java2d.opengl;
import java.awt.AWTException;
+import java.awt.AWTError;
import java.awt.BufferCapabilities;
import java.awt.Component;
import java.awt.Graphics;
@@ -61,6 +62,9 @@
import static sun.java2d.opengl.OGLContext.OGLContextCaps.CAPS_EXT_FBOBJECT;
import static sun.java2d.opengl.OGLSurfaceData.FBOBJECT;
import static sun.java2d.opengl.OGLSurfaceData.TEXTURE;
+import sun.lwawt.macosx.CThreading;
+import java.security.PrivilegedAction;
+import java.util.concurrent.Callable;
public final class CGLGraphicsConfig extends CGraphicsConfig
implements OGLGraphicsConfig
@@ -118,44 +122,63 @@ public SurfaceData createManagedSurface(int w, int h, int transparency) {
OGLSurfaceData.TEXTURE);
}
+ @SuppressWarnings("removal")
public static CGLGraphicsConfig getConfig(CGraphicsDevice device)
{
if (!cglAvailable) {
return null;
}
- long cfginfo = 0;
- int textureSize = 0;
- final String[] ids = new String[1];
- OGLRenderQueue rq = OGLRenderQueue.getInstance();
- rq.lock();
- try {
- // getCGLConfigInfo() creates and destroys temporary
- // surfaces/contexts, so we should first invalidate the current
- // Java-level context and flush the queue...
- OGLContext.invalidateCurrentContext();
- cfginfo = getCGLConfigInfo();
- if (cfginfo != 0L) {
- textureSize = nativeGetMaxTextureSize();
- // 7160609: GL still fails to create a square texture of this
- // size. Half should be safe enough.
- // Explicitly not support a texture more than 2^14, see 8010999.
- textureSize = textureSize <= 16384 ? textureSize / 2 : 8192;
- OGLContext.setScratchSurface(cfginfo);
- rq.flushAndInvokeNow(() -> {
- ids[0] = OGLContext.getOGLIdString();
- });
+ // Move CGLGraphicsConfig creation code to AppKit thread in order to avoid the
+ // following deadlock:
+ // 1) CGLGraphicsConfig.getCGLConfigInfo (called from EDT) takes RenderQueue.lock
+ // 2) CGLLayer.drawInCGLContext is invoked on AppKit thread and
+ // blocked on RenderQueue.lock
+ // 1) invokes native block on AppKit and wait
+
+ Callable command = () -> {
+ long cfginfo = 0;
+ int textureSize = 0;
+ final String[] ids = new String[1];
+ OGLRenderQueue rq = OGLRenderQueue.getInstance();
+ rq.lock();
+ try {
+ // getCGLConfigInfo() creates and destroys temporary
+ // surfaces/contexts, so we should first invalidate the current
+ // Java-level context and flush the queue...
+ OGLContext.invalidateCurrentContext();
+ cfginfo = getCGLConfigInfo();
+ if (cfginfo != 0L) {
+ textureSize = nativeGetMaxTextureSize();
+ // 7160609: GL still fails to create a square texture of this
+ // size. Half should be safe enough.
+ // Explicitly not support a texture more than 2^14, see 8010999.
+ textureSize = textureSize <= 16384 ? textureSize / 2 : 8192;
+ OGLContext.setScratchSurface(cfginfo);
+ rq.flushAndInvokeNow(() -> {
+ ids[0] = OGLContext.getOGLIdString();
+ });
+ }
+ } finally {
+ rq.unlock();
+ }
+ if (cfginfo == 0) {
+ return null;
}
- } finally {
- rq.unlock();
- }
- if (cfginfo == 0) {
- return null;
- }
- int oglCaps = getOGLCapabilities(cfginfo);
- ContextCapabilities caps = new OGLContextCaps(oglCaps, ids[0]);
- return new CGLGraphicsConfig(device, cfginfo, textureSize, caps);
+ int oglCaps = getOGLCapabilities(cfginfo);
+ ContextCapabilities caps = new OGLContextCaps(oglCaps, ids[0]);
+ return new CGLGraphicsConfig(device, cfginfo, textureSize, caps);
+ };
+
+ return java.security.AccessController.doPrivileged(
+ (PrivilegedAction) () -> {
+ try {
+ return CThreading.executeOnAppKit(command);
+ } catch (Throwable throwable) {
+ throw new AWTError(throwable.getMessage());
+ }
+ });
}
public static boolean isCGLAvailable() {
@@ -324,6 +347,11 @@ public void flip(final LWComponentPeer, ?> peer, final Image backBuffer,
}
}
+ @Override
+ public void flush(LWComponentPeer, ?> peer) {
+
+ }
+
private static class CGLBufferCaps extends BufferCapabilities {
public CGLBufferCaps(boolean dblBuf) {
super(imageCaps, imageCaps,
diff --git a/src/java.desktop/macosx/classes/sun/java2d/opengl/CGLLayer.java b/src/java.desktop/macosx/classes/sun/java2d/opengl/CGLLayer.java
index eb6629a0ae93..9f82e1b75055 100644
--- a/src/java.desktop/macosx/classes/sun/java2d/opengl/CGLLayer.java
+++ b/src/java.desktop/macosx/classes/sun/java2d/opengl/CGLLayer.java
@@ -31,8 +31,10 @@
import sun.lwawt.LWWindowPeer;
import sun.java2d.SurfaceData;
import sun.lwawt.macosx.CFLayer;
+import sun.util.logging.PlatformLogger;
public class CGLLayer extends CFLayer {
+ private static final PlatformLogger logger = PlatformLogger.getLogger(CGLLayer.class.getName());
private native long nativeCreateLayer();
private static native void nativeSetScale(long layerPtr, double scale);
@@ -46,9 +48,17 @@ public CGLLayer(LWWindowPeer peer) {
setPtr(nativeCreateLayer());
this.peer = peer;
+
+ CGraphicsConfig gc = (CGraphicsConfig)getGraphicsConfiguration();
+ if (logger.isLoggable(PlatformLogger.Level.FINE)) {
+ logger.fine("device = " + (gc != null ? gc.getDevice() : "null"));
+ }
+ if (gc != null) {
+ setScale(gc.getDevice().getScaleFactor());
+ }
}
- public SurfaceData replaceSurfaceData() {
+ public SurfaceData replaceSurfaceData(int scale) {
if (getBounds().isEmpty()) {
surfaceData = NullSurfaceData.theInstance;
return surfaceData;
@@ -57,8 +67,15 @@ public SurfaceData replaceSurfaceData() {
// the layer redirects all painting to the buffer's graphics
// and blits the buffer to the layer surface (in drawInCGLContext callback)
CGraphicsConfig gc = (CGraphicsConfig)getGraphicsConfiguration();
+ if (gc == null) {
+ surfaceData = NullSurfaceData.theInstance;
+ return surfaceData;
+ }
surfaceData = gc.createSurfaceData(this);
- setScale(gc.getDevice().getScaleFactor());
+ if (scale <= 0) {
+ scale = gc.getDevice().getScaleFactor();
+ }
+ setScale(scale);
// the layer holds a reference to the buffer, which in
// turn has a reference back to this layer
if (surfaceData instanceof CGLSurfaceData) {
@@ -90,8 +107,12 @@ public void dispose() {
super.dispose();
}
- private void setScale(final int _scale) {
+ private void setScale(int _scale) {
if (scale != _scale) {
+ if (logger.isLoggable(PlatformLogger.Level.FINE)) {
+ CGraphicsConfig gc = (CGraphicsConfig)getGraphicsConfiguration();
+ logger.fine("current scale = " + scale + ", new scale = " + _scale + " (device = " + (gc != null ? gc.getDevice() : "null") + ")");
+ }
scale = _scale;
execute(ptr -> nativeSetScale(ptr, scale));
}
@@ -107,7 +128,7 @@ private void drawInCGLContext() {
OGLRenderQueue rq = OGLRenderQueue.getInstance();
rq.lock();
try {
- execute(ptr -> blitTexture(ptr));
+ blitTexture(getPointer());
} finally {
rq.unlock();
}
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/LWComponentPeer.java b/src/java.desktop/macosx/classes/sun/lwawt/LWComponentPeer.java
index f37e4ad7425a..ca208235770e 100644
--- a/src/java.desktop/macosx/classes/sun/lwawt/LWComponentPeer.java
+++ b/src/java.desktop/macosx/classes/sun/lwawt/LWComponentPeer.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -68,12 +68,13 @@
import com.sun.java.swing.SwingUtilities3;
import sun.awt.AWTAccessor;
-import sun.awt.CGraphicsDevice;
+import sun.awt.CGraphicsEnvironment;
import sun.awt.PaintEventDispatcher;
import sun.awt.RepaintArea;
import sun.awt.SunToolkit;
import sun.awt.event.IgnorePaintEvent;
import sun.awt.image.SunVolatileImage;
+import sun.java2d.MacOSFlags;
import sun.java2d.SunGraphics2D;
import sun.java2d.metal.MTLRenderQueue;
import sun.java2d.opengl.OGLRenderQueue;
@@ -930,7 +931,7 @@ public boolean requestFocus(Component lightweightChild, boolean temporary,
int result = LWKeyboardFocusManagerPeer.shouldNativelyFocusHeavyweight(
getTarget(), lightweightChild, temporary,
- focusedWindowChangeAllowed, time, cause);
+ focusedWindowChangeAllowed, time, cause, true);
switch (result) {
case LWKeyboardFocusManagerPeer.SNFH_FAILURE:
return false;
@@ -955,21 +956,16 @@ public boolean requestFocus(Component lightweightChild, boolean temporary,
// however that is the shared code and this particular problem's reproducibility has
// platform specifics. So, it was decided to narrow down the fix to lwawt (OSX) in
// current release. TODO: consider fixing it in the shared code.
- if (!focusedWindowChangeAllowed) {
- LWWindowPeer decoratedPeer = parentPeer.isSimpleWindow() ?
- LWWindowPeer.getOwnerFrameDialog(parentPeer) : parentPeer;
-
- if (decoratedPeer == null || !decoratedPeer.getPlatformWindow().isActive()) {
- if (focusLog.isLoggable(PlatformLogger.Level.FINE)) {
- focusLog.fine("request rejected, focusedWindowChangeAllowed==false, " +
- "decoratedPeer is inactive: " + decoratedPeer);
- }
- LWKeyboardFocusManagerPeer.removeLastFocusRequest(getTarget());
- return false;
+ if (!focusedWindowChangeAllowed && !parentPeer.getPlatformWindow().isActive()) {
+ if (focusLog.isLoggable(PlatformLogger.Level.FINE)) {
+ focusLog.fine("request rejected, focusedWindowChangeAllowed==false, " +
+ "parentPeer is inactive: " + parentPeer);
}
+ LWKeyboardFocusManagerPeer.removeLastFocusRequest(getTarget());
+ return false;
}
- boolean res = parentPeer.requestWindowFocus(cause);
+ boolean res = !focusedWindowChangeAllowed || parentPeer.requestWindowFocus(cause);
// If parent window can be made focused and has been made focused (synchronously)
// then we can proceed with children, otherwise we retreat
if (!res || !parentWindow.isFocused()) {
@@ -984,9 +980,7 @@ public boolean requestFocus(Component lightweightChild, boolean temporary,
KeyboardFocusManagerPeer kfmPeer = LWKeyboardFocusManagerPeer.getInstance();
Component focusOwner = kfmPeer.getCurrentFocusOwner();
return LWKeyboardFocusManagerPeer.deliverFocus(lightweightChild,
- getTarget(), temporary,
- focusedWindowChangeAllowed,
- time, cause, focusOwner);
+ getTarget(), true, cause, focusOwner);
case LWKeyboardFocusManagerPeer.SNFH_SUCCESS_HANDLED:
return true;
@@ -1308,6 +1302,10 @@ private void handleJavaPaintEvent() {
// if (!isLayouting && !paintPending) {
if (!isLayouting()) {
targetPaintArea.paint(getTarget(), shouldClearRectBeforePaint());
+ if (MacOSFlags.isMetalEnabled() && !MacOSFlags.isMetalDisplaySyncEnabled()) {
+ SunToolkit.executeOnEventHandlerThread(getTarget(),
+ () -> getWindowPeerOrSelf().updateWindow());
+ }
}
}
@@ -1335,7 +1333,7 @@ public Point windowToLocal(int x, int y, LWWindowPeer wp) {
public Point windowToLocal(Point p, LWWindowPeer wp) {
LWComponentPeer, ?> cp = this;
- while (cp != wp) {
+ while (cp != null && cp != wp) {
Rectangle cpb = cp.getBounds();
p.x -= cpb.x;
p.y -= cpb.y;
@@ -1419,7 +1417,7 @@ protected final void paintPeer(final Graphics g) {
}
protected static final void flushOnscreenGraphics(){
- RenderQueue rq = CGraphicsDevice.usingMetalPipeline() ?
+ RenderQueue rq = CGraphicsEnvironment.usingMetalPipeline() ?
MTLRenderQueue.getInstance() : OGLRenderQueue.getInstance();
rq.lock();
try {
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/LWGraphicsConfig.java b/src/java.desktop/macosx/classes/sun/lwawt/LWGraphicsConfig.java
index d556ea759952..e989bde846ab 100644
--- a/src/java.desktop/macosx/classes/sun/lwawt/LWGraphicsConfig.java
+++ b/src/java.desktop/macosx/classes/sun/lwawt/LWGraphicsConfig.java
@@ -89,6 +89,11 @@ void assertOperationSupported(int numBuffers, BufferCapabilities caps)
void flip(LWComponentPeer, ?> peer, Image backBuffer, int x1, int y1,
int x2, int y2, BufferCapabilities.FlipContents flipAction);
+ /**
+ * Performs the native flush operation for the given peer
+ */
+ void flush(LWComponentPeer, ?> peer);
+
/**
* Creates a new hidden-acceleration image of the given width and height
* that is associated with the target Component.
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/LWMouseInfoPeer.java b/src/java.desktop/macosx/classes/sun/lwawt/LWMouseInfoPeer.java
index a507b75a9048..1982f120cdca 100644
--- a/src/java.desktop/macosx/classes/sun/lwawt/LWMouseInfoPeer.java
+++ b/src/java.desktop/macosx/classes/sun/lwawt/LWMouseInfoPeer.java
@@ -25,8 +25,7 @@
package sun.lwawt;
-import java.awt.Point;
-import java.awt.Window;
+import java.awt.*;
import java.awt.peer.MouseInfoPeer;
@@ -41,8 +40,21 @@ public int fillPointWithCoords(Point point) {
Point cursorPos = cursorManager.getCursorPosition();
point.x = cursorPos.x;
point.y = cursorPos.y;
- // TODO: multiscreen
- return 0;
+ GraphicsDevice[] gds = GraphicsEnvironment.getLocalGraphicsEnvironment().
+ getScreenDevices();
+ int nearestScreen = 0, nearestScreenDistance = Integer.MAX_VALUE;
+ for (int i = 0; i < gds.length; i++) {
+ Rectangle bounds = gds[i].getDefaultConfiguration().getBounds();
+ int dx = Math.max(Math.min(point.x, bounds.x + bounds.width), bounds.x) - point.x;
+ int dy = Math.max(Math.min(point.y, bounds.y + bounds.height), bounds.y) - point.y;
+ int dist = dx*dx + dy*dy;
+ if (dist == 0) return i;
+ if (dist < nearestScreenDistance) {
+ nearestScreen = i;
+ nearestScreenDistance = dist;
+ }
+ }
+ return nearestScreen;
}
@Override
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/LWToolkit.java b/src/java.desktop/macosx/classes/sun/lwawt/LWToolkit.java
index 6f38e62c46dc..37679c4640bc 100644
--- a/src/java.desktop/macosx/classes/sun/lwawt/LWToolkit.java
+++ b/src/java.desktop/macosx/classes/sun/lwawt/LWToolkit.java
@@ -35,6 +35,7 @@
import java.util.*;
import sun.awt.*;
+import sun.java2d.MacOSFlags;
import sun.print.*;
import sun.awt.util.ThreadGroupUtils;
@@ -364,6 +365,11 @@ public final boolean isTaskbarSupported() {
return true;
}
+ @Override
+ public boolean needUpdateWindowAfterPaint() {
+ return MacOSFlags.isMetalEnabled() && !MacOSFlags.isMetalDisplaySyncEnabled();
+ }
+
@Override
public final KeyboardFocusManagerPeer getKeyboardFocusManagerPeer() {
return LWKeyboardFocusManagerPeer.getInstance();
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/LWWindowPeer.java b/src/java.desktop/macosx/classes/sun/lwawt/LWWindowPeer.java
index 4ec8d87fee3f..7c208a070453 100644
--- a/src/java.desktop/macosx/classes/sun/lwawt/LWWindowPeer.java
+++ b/src/java.desktop/macosx/classes/sun/lwawt/LWWindowPeer.java
@@ -79,6 +79,7 @@
import sun.java2d.loops.Blit;
import sun.java2d.loops.CompositeType;
import sun.java2d.pipe.Region;
+import sun.lwawt.macosx.CPlatformWindow;
import sun.util.logging.PlatformLogger;
public class LWWindowPeer
@@ -95,6 +96,7 @@ public enum PeerType {
}
private static final PlatformLogger focusLog = PlatformLogger.getLogger("sun.lwawt.focus.LWWindowPeer");
+ private static final PlatformLogger logger = PlatformLogger.getLogger("sun.lwawt.LWWindowPeer");
private final PlatformWindow platformWindow;
@@ -301,23 +303,6 @@ protected void setVisibleImpl(final boolean visible) {
super.setVisibleImpl(visible);
// TODO: update graphicsConfig, see 4868278
platformWindow.setVisible(visible);
- if (isSimpleWindow()) {
- KeyboardFocusManagerPeer kfmPeer = LWKeyboardFocusManagerPeer.getInstance();
- if (visible) {
- if (!getTarget().isAutoRequestFocus()) {
- return;
- } else {
- requestWindowFocus(FocusEvent.Cause.ACTIVATION);
- }
- // Focus the owner in case this window is focused.
- } else if (kfmPeer.getCurrentFocusedWindow() == getTarget()) {
- // Transfer focus to the owner.
- LWWindowPeer owner = getOwnerFrameDialog(LWWindowPeer.this);
- if (owner != null) {
- owner.requestWindowFocus(FocusEvent.Cause.ACTIVATION);
- }
- }
- }
}
@Override
@@ -523,6 +508,7 @@ private void updateOpaque() {
@Override
public void updateWindow() {
+ getLWGC().flush(this);
}
public final boolean isTextured() {
@@ -734,7 +720,16 @@ public void notifyReshape(int x, int y, int w, int h) {
setBounds(x, y, w, h, SET_BOUNDS, false, false);
// Second, update the graphics config and surface data
- final boolean isNewDevice = updateGraphicsDevice();
+ GraphicsDevice newGraphicsDevice = platformWindow.getGraphicsDevice();
+ if (newGraphicsDevice == null) {
+ if (logger.isLoggable(PlatformLogger.Level.FINE)) {
+ logger.fine("Unable to update graphics device: " +
+ "platform window graphics device is null");
+ }
+ return;
+ }
+ boolean isNewDevice = updateGraphicsDevice(newGraphicsDevice);
+
if (isNewDevice && !isMaximizedBoundsSet()) {
setPlatformMaximizedBounds(getDefaultMaximizedBounds());
}
@@ -750,7 +745,11 @@ public void notifyReshape(int x, int y, int w, int h) {
}
if (tResized || pResized || invalid || isNewDevice) {
handleResize(w, h, true);
- repaintPeer();
+ // Changing window size fires up the execution of the current method on AppKit thread and as result it could
+ // leads to race condition with paint event on EDT. Running repaintPeer on EDT eliminates such issue.
+ SunToolkit.executeOnEventHandlerThread(getTarget(), () -> {
+ repaintPeer();
+ });
}
repositionSecurityWarning();
@@ -1044,7 +1043,7 @@ public void notifyMouseWheelEvent(long when, int x, int y, int absX,
*/
@Override
public void notifyKeyEvent(int id, long when, int modifiers,
- int keyCode, char keyChar, int keyLocation, int extendedKeyCode)
+ int keyCode, char keyChar, int keyLocation)
{
LWKeyboardFocusManagerPeer kfmPeer = LWKeyboardFocusManagerPeer.getInstance();
Component focusOwner = kfmPeer.getCurrentFocusOwner();
@@ -1058,13 +1057,9 @@ public void notifyKeyEvent(int id, long when, int modifiers,
KeyEvent keyEvent = new KeyEvent(focusOwner, id, when, modifiers,
keyCode, keyChar, keyLocation);
- if (extendedKeyCode >= 0) {
- AWTAccessor.getKeyEventAccessor().setExtendedKeyCode(keyEvent, extendedKeyCode);
- } else {
- AWTAccessor.getKeyEventAccessor().setExtendedKeyCode(keyEvent,
- (keyChar == KeyEvent.CHAR_UNDEFINED) ? keyCode
- : ExtendedKeyCodes.getExtendedKeyCodeForChar(keyChar));
- }
+ AWTAccessor.getKeyEventAccessor().setExtendedKeyCode(keyEvent,
+ (keyChar == KeyEvent.CHAR_UNDEFINED) ? keyCode
+ : ExtendedKeyCodes.getExtendedKeyCodeForChar(keyChar));
postEvent(keyEvent);
}
@@ -1137,8 +1132,7 @@ private boolean setGraphicsConfig(GraphicsConfiguration gc) {
/**
* Returns true if the GraphicsDevice has been changed, false otherwise.
*/
- public boolean updateGraphicsDevice() {
- GraphicsDevice newGraphicsDevice = platformWindow.getGraphicsDevice();
+ public boolean updateGraphicsDevice(GraphicsDevice newGraphicsDevice) {
synchronized (getStateLock()) {
if (graphicsDevice == newGraphicsDevice) {
return false;
@@ -1160,16 +1154,29 @@ public void run() {
@Override
public final void displayChanged() {
- if (updateGraphicsDevice()) {
+ GraphicsDevice newGraphicsDevice = platformWindow.getGraphicsDevice();
+ if (newGraphicsDevice == null) {
+ if (logger.isLoggable(PlatformLogger.Level.FINE)) {
+ logger.fine("Unable to update graphics device: " +
+ "platform window graphics device is null");
+ }
+ return;
+ }
+ boolean isNewDevice = updateGraphicsDevice(newGraphicsDevice);
+
+ if (isNewDevice) {
updateMinimumSize();
if (!isMaximizedBoundsSet()) {
setPlatformMaximizedBounds(getDefaultMaximizedBounds());
}
}
- // Replace surface unconditionally, because internal state of the
- // GraphicsDevice could be changed.
- replaceSurfaceData();
- repaintPeer();
+
+ SunToolkit.executeOnEventHandlerThread(getTarget(), () -> {
+ // Replace surface unconditionally, because internal state of the
+ // GraphicsDevice could be changed.
+ replaceSurfaceData();
+ repaintPeer();
+ });
}
@Override
@@ -1210,6 +1217,9 @@ private void replaceSurfaceData(final boolean blit) {
}
}
flushOnscreenGraphics();
+ if (((LWToolkit) Toolkit.getDefaultToolkit()).needUpdateWindowAfterPaint()) {
+ updateWindow();
+ }
}
private void blitSurfaceData(final SurfaceData src, final SurfaceData dst) {
@@ -1275,53 +1285,6 @@ public boolean requestWindowFocus(FocusEvent.Cause cause) {
return false;
}
- AppContext targetAppContext = AWTAccessor.getComponentAccessor().getAppContext(getTarget());
- KeyboardFocusManager kfm = AWTAccessor.getKeyboardFocusManagerAccessor()
- .getCurrentKeyboardFocusManager(targetAppContext);
- Window currentActive = kfm.getActiveWindow();
-
-
- Window opposite = LWKeyboardFocusManagerPeer.getInstance().
- getCurrentFocusedWindow();
-
- // Make the owner active window.
- if (isSimpleWindow()) {
- LWWindowPeer owner = getOwnerFrameDialog(this);
-
- // If owner is not natively active, request native
- // activation on it w/o sending events up to java.
- if (owner != null && !owner.platformWindow.isActive()) {
- if (focusLog.isLoggable(PlatformLogger.Level.FINE)) {
- focusLog.fine("requesting native focus to the owner " + owner);
- }
- LWWindowPeer currentActivePeer = currentActive == null ? null :
- (LWWindowPeer) AWTAccessor.getComponentAccessor().getPeer(
- currentActive);
-
- // Ensure the opposite is natively active and suppress sending events.
- if (currentActivePeer != null && currentActivePeer.platformWindow.isActive()) {
- if (focusLog.isLoggable(PlatformLogger.Level.FINE)) {
- focusLog.fine("the opposite is " + currentActivePeer);
- }
- currentActivePeer.skipNextFocusChange = true;
- }
- owner.skipNextFocusChange = true;
-
- owner.platformWindow.requestWindowFocus();
- }
-
- // DKFM will synthesize all the focus/activation events correctly.
- changeFocusedWindow(true, opposite);
- return true;
-
- // In case the toplevel is active but not focused, change focus directly,
- // as requesting native focus on it will not have effect.
- } else if (getTarget() == currentActive && !getTarget().hasFocus()) {
-
- changeFocusedWindow(true, opposite);
- return true;
- }
-
return platformWindow.requestWindowFocus();
}
@@ -1397,7 +1360,8 @@ protected void changeFocusedWindow(boolean becomesFocused, Window opposite) {
// - when the opposite (gaining focus) window is an owned/owner window.
// - for a simple window in any case.
if (!becomesFocused &&
- (isGrabbing() || this.isOneOfOwnersOf(grabbingWindow)))
+ (isGrabbing() || this.isOneOfOwnersOf(grabbingWindow)) &&
+ (opposite == null || getOwnerFrameDialog(AWTAccessor.getComponentAccessor().getPeer(opposite)) != this))
{
if (focusLog.isLoggable(PlatformLogger.Level.FINE)) {
focusLog.fine("ungrabbing on " + grabbingWindow);
@@ -1419,7 +1383,7 @@ protected void changeFocusedWindow(boolean becomesFocused, Window opposite) {
WindowEvent windowEvent = new TimedWindowEvent(getTarget(), eventID, opposite, System.currentTimeMillis());
// TODO: wrap in SequencedEvent
- postEvent(windowEvent);
+ SunToolkit.postPriorityEvent(windowEvent);
}
/*
@@ -1527,4 +1491,17 @@ public void updateSecurityWarningVisibility() {
public String toString() {
return super.toString() + " [target is " + getTarget() + "]";
}
+
+ /**
+ * [tav] Used externally.
+ */
+ @Override
+ public long getWindowHandle() {
+ final long[] handle = new long[1];
+ PlatformWindow window = getPlatformWindow();
+ if (window instanceof CPlatformWindow) {
+ ((CPlatformWindow)window).execute(ptr -> handle[0] = ptr);
+ }
+ return handle[0];
+ }
}
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/PlatformEventNotifier.java b/src/java.desktop/macosx/classes/sun/lwawt/PlatformEventNotifier.java
index aea8ff2e6a93..d613b9cea25a 100644
--- a/src/java.desktop/macosx/classes/sun/lwawt/PlatformEventNotifier.java
+++ b/src/java.desktop/macosx/classes/sun/lwawt/PlatformEventNotifier.java
@@ -61,5 +61,5 @@ void notifyMouseWheelEvent(long when, int x, int y, final int absX,
* Called by the delegate when a key is pressed.
*/
void notifyKeyEvent(int id, long when, int modifiers,
- int keyCode, char keyChar, int keyLocation, int extendedKeyCode);
+ int keyCode, char keyChar, int keyLocation);
}
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessibility.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessibility.java
index d4526324c585..b98ffc0dce26 100644
--- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessibility.java
+++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessibility.java
@@ -38,11 +38,14 @@
import java.lang.annotation.Native;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
+import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.Arrays;
+import java.util.function.Function;
+import java.util.concurrent.atomic.AtomicInteger;
import javax.accessibility.Accessible;
import javax.accessibility.AccessibleAction;
@@ -56,6 +59,7 @@
import javax.accessibility.AccessibleText;
import javax.accessibility.AccessibleValue;
import javax.swing.Icon;
+import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JEditorPane;
import javax.swing.JLabel;
@@ -72,21 +76,18 @@
class CAccessibility implements PropertyChangeListener {
private static Set ignoredRoles;
+ private static final int INVOKE_TIMEOUT_SECONDS;
static {
- loadAWTLibrary();
- }
-
- @SuppressWarnings("removal")
- private static void loadAWTLibrary() {
- // Need to load the native library for this code.
- java.security.AccessController.doPrivileged(
- new java.security.PrivilegedAction() {
- public Void run() {
- System.loadLibrary("awt");
- return null;
- }
+ // (-1) for the infinite timeout
+ @SuppressWarnings("removal")
+ int value = java.security.AccessController.doPrivileged(
+ (PrivilegedAction) () -> {
+ // Need to load the native library for this code.
+ System.loadLibrary("awt");
+ return Integer.getInteger("sun.lwawt.macosx.CAccessibility.invokeTimeoutSeconds", 1);
});
+ INVOKE_TIMEOUT_SECONDS = value;
}
static CAccessibility sAccessibility;
@@ -128,14 +129,14 @@ public void propertyChange(final PropertyChangeEvent evt) {
private native void focusChanged();
static T invokeAndWait(final Callable callable, final Component c) {
- return invokeAndWait(callable, c, null);
+ return invokeAndWait(callable, c, (T)null);
}
static T invokeAndWait(final Callable callable, final Component c, final T defValue) {
T value = null;
if (c != null) {
try {
- value = LWCToolkit.invokeAndWait(callable, c);
+ value = LWCToolkit.invokeAndWait(callable, c, INVOKE_TIMEOUT_SECONDS);
} catch (final Exception e) { e.printStackTrace(); }
}
@@ -776,7 +777,7 @@ private static Accessible createAccessibleTreeNode(JTree t, TreePath p) {
// This method is called from the native
// Each child takes up three entries in the array: one for itself, one for its role, and one for the recursion level
- private static Object[] getChildrenAndRolesRecursive(final Accessible a, final Component c, final int whichChildren, final boolean allowIgnored, final int level) {
+ private static Object[] getChildrenAndRolesRecursive(final Accessible a, final Component c, final int whichChildren, final boolean allowIgnored) {
if (a == null) return null;
return invokeAndWait(new Callable() {
public Object[] call() throws Exception {
@@ -832,7 +833,7 @@ public Object[] call() throws Exception {
parentStack.add(a);
ArrayList indexses = new ArrayList();
Integer index = 0;
- int currentLevel = level;
+ int currentLevel = 0;
while (!parentStack.isEmpty()) {
Accessible p = parentStack.get(parentStack.size() - 1);
@@ -1138,4 +1139,18 @@ public Boolean call() throws Exception {
}
}, c);
}
+
+ private static boolean isComboBoxEditable(Accessible a, Component c) {
+ if (a == null) return false;
+
+ return invokeAndWait(new Callable() {
+ public Boolean call() throws Exception {
+ Accessible sa = CAccessible.getSwingAccessible(a);
+ if (sa instanceof JComboBox> comboBox) {
+ return comboBox.isEditable();
+ }
+ return false;
+ }
+ }, c);
+ }
}
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessible.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessible.java
index 779423bc28fc..1ecf5bdd3f93 100644
--- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessible.java
+++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessible.java
@@ -25,13 +25,13 @@
package sun.lwawt.macosx;
-import java.awt.Component;
+import java.awt.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
+import java.security.PrivilegedAction;
-import javax.accessibility.Accessible;
-import javax.accessibility.AccessibleContext;
-import javax.swing.JTabbedPane;
+import javax.accessibility.*;
+import javax.swing.*;
import static javax.accessibility.AccessibleContext.ACCESSIBLE_ACTIVE_DESCENDANT_PROPERTY;
import static javax.accessibility.AccessibleContext.ACCESSIBLE_CARET_PROPERTY;
@@ -42,12 +42,24 @@
import static javax.accessibility.AccessibleContext.ACCESSIBLE_NAME_PROPERTY;
import static javax.accessibility.AccessibleContext.ACCESSIBLE_VALUE_PROPERTY;
-import javax.accessibility.AccessibleRole;
-import javax.accessibility.AccessibleState;
import sun.awt.AWTAccessor;
class CAccessible extends CFRetainedResource implements Accessible {
+ private static Timer timer = null;
+ private final static int SELECTED_CHILDREN_MILLISECONDS_DEFAULT = 100;
+ private static int SELECTED_CHILDREN_MILLISECONDS;
+
+ static {
+ @SuppressWarnings("removal") int scms = java.security.AccessController.doPrivileged(new PrivilegedAction() {
+ @Override
+ public Integer run() {
+ return Integer.getInteger("sun.lwawt.macosx.CAccessible.selectedChildrenMilliSeconds",
+ SELECTED_CHILDREN_MILLISECONDS_DEFAULT);
+ }
+ });
+ SELECTED_CHILDREN_MILLISECONDS = scms >= 0 ? scms : SELECTED_CHILDREN_MILLISECONDS_DEFAULT;
+ }
public static CAccessible getCAccessible(final Accessible a) {
if (a == null) return null;
@@ -75,6 +87,7 @@ public static CAccessible getCAccessible(final Accessible a) {
private static native void treeNodeCollapsed(long ptr);
private static native void selectedCellsChanged(long ptr);
private static native void tableContentCacheClear(long ptr);
+ private static native void updateZoomCaretFocus(long ptr);
private Accessible accessible;
@@ -99,7 +112,7 @@ protected synchronized void dispose() {
@Override
public AccessibleContext getAccessibleContext() {
- return accessible.getAccessibleContext();
+ return accessible != null ? accessible.getAccessibleContext() : null;
}
public void addNotificationListeners(Component c) {
@@ -113,27 +126,35 @@ private class AXChangeNotifier implements PropertyChangeListener {
@Override
public void propertyChange(PropertyChangeEvent e) {
+ assert EventQueue.isDispatchThread();
String name = e.getPropertyName();
if ( ptr != 0 ) {
Object newValue = e.getNewValue();
Object oldValue = e.getOldValue();
if (name.equals(ACCESSIBLE_CARET_PROPERTY)) {
- selectedTextChanged(ptr);
+ execute(ptr -> selectedTextChanged(ptr));
+ // Notify macOS Accessibility Zoom to move focus to the new caret location.
+ execute(ptr -> updateZoomCaretFocus(ptr));
} else if (name.equals(ACCESSIBLE_TEXT_PROPERTY)) {
- valueChanged(ptr);
+ execute(ptr -> valueChanged(ptr));
} else if (name.equals(ACCESSIBLE_SELECTION_PROPERTY)) {
- selectionChanged(ptr);
+ if (timer != null) {
+ timer.stop();
+ }
+ timer = new Timer(SELECTED_CHILDREN_MILLISECONDS, actionEvent -> execute(ptr -> selectionChanged(ptr)));
+ timer.setRepeats(false);
+ timer.start();
} else if (name.equals(ACCESSIBLE_TABLE_MODEL_CHANGED)) {
- valueChanged(ptr);
+ execute(ptr -> valueChanged(ptr));
if (CAccessible.getSwingAccessible(CAccessible.this) != null) {
Accessible a = CAccessible.getSwingAccessible(CAccessible.this);
AccessibleContext ac = a.getAccessibleContext();
if ((ac != null) && (ac.getAccessibleRole() == AccessibleRole.TABLE)) {
- tableContentCacheClear(ptr);
+ execute(ptr -> tableContentCacheClear(ptr));
}
}
} else if (name.equals(ACCESSIBLE_ACTIVE_DESCENDANT_PROPERTY)) {
- if (newValue instanceof AccessibleContext) {
+ if (newValue == null || newValue instanceof AccessibleContext) {
activeDescendant = (AccessibleContext)newValue;
if (newValue instanceof Accessible) {
Accessible a = (Accessible)newValue;
@@ -143,7 +164,7 @@ public void propertyChange(PropertyChangeEvent e) {
if (p != null) {
AccessibleContext pac = p.getAccessibleContext();
if ((pac != null) && (pac.getAccessibleRole() == AccessibleRole.TABLE)) {
- selectedCellsChanged(ptr);
+ execute(ptr -> selectedCellsChanged(ptr));
}
}
}
@@ -159,31 +180,48 @@ public void propertyChange(PropertyChangeEvent e) {
}
if (newValue == AccessibleState.EXPANDED) {
- treeNodeExpanded(ptr);
+ execute(ptr -> treeNodeExpanded(ptr));
} else if (newValue == AccessibleState.COLLAPSED) {
- treeNodeCollapsed(ptr);
+ execute(ptr -> treeNodeCollapsed(ptr));
+ }
+
+ if (thisRole == AccessibleRole.COMBO_BOX) {
+ AccessibleStateSet as = thisAC.getAccessibleStateSet();
+ if (as != null)
+ if (as.contains(AccessibleState.EXPANDED)) {
+ if (timer != null) {
+ timer.stop();
+ }
+ timer = new Timer(SELECTED_CHILDREN_MILLISECONDS, actionEvent -> execute(ptr -> selectionChanged(ptr)));
+ timer.setRepeats(false);
+ timer.start();
+ if (as.contains(AccessibleState.COLLAPSED)) {
+ execute(ptr -> valueChanged(ptr));
+ }
+ }
}
if (thisRole == AccessibleRole.POPUP_MENU) {
- if ( newValue != null &&
- ((AccessibleState)newValue) == AccessibleState.VISIBLE ) {
- menuOpened(ptr);
- } else if ( oldValue != null &&
- ((AccessibleState)oldValue) == AccessibleState.VISIBLE ) {
- menuClosed(ptr);
+ if (newValue != null &&
+ ((AccessibleState) newValue) == AccessibleState.VISIBLE) {
+ execute(ptr -> menuOpened(ptr));
+ } else if (oldValue != null &&
+ ((AccessibleState) oldValue) == AccessibleState.VISIBLE) {
+ execute(ptr -> menuClosed(ptr));
+ execute(ptr -> unregisterFromCocoaAXSystem(ptr));
}
} else if (thisRole == AccessibleRole.MENU_ITEM ||
(thisRole == AccessibleRole.MENU)) {
- if ( newValue != null &&
- ((AccessibleState)newValue) == AccessibleState.FOCUSED ) {
- menuItemSelected(ptr);
+ if (newValue != null &&
+ ((AccessibleState) newValue) == AccessibleState.FOCUSED) {
+ execute(ptr -> menuItemSelected(ptr));
}
}
// Do send check box state changes to native side
if (thisRole == AccessibleRole.CHECK_BOX) {
if (newValue != null && !newValue.equals(oldValue)) {
- valueChanged(ptr);
+ execute(ptr -> valueChanged(ptr));
}
// Notify native side to handle check box style menuitem
@@ -215,14 +253,14 @@ public void propertyChange(PropertyChangeEvent e) {
} else if (name.equals(ACCESSIBLE_NAME_PROPERTY)) {
//for now trigger only for JTabbedPane.
if (e.getSource() instanceof JTabbedPane) {
- titleChanged(ptr);
+ execute(ptr -> titleChanged(ptr));
}
} else if (name.equals(ACCESSIBLE_VALUE_PROPERTY)) {
AccessibleRole thisRole = accessible.getAccessibleContext()
.getAccessibleRole();
if (thisRole == AccessibleRole.SLIDER ||
thisRole == AccessibleRole.PROGRESS_BAR) {
- valueChanged(ptr);
+ execute(ptr -> valueChanged(ptr));
}
}
}
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessibleText.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessibleText.java
index cee23f2f96e3..2ae32d6a07cb 100644
--- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessibleText.java
+++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CAccessibleText.java
@@ -29,6 +29,7 @@
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.geom.Rectangle2D;
+import java.awt.im.InputMethodRequests;
import java.util.concurrent.Callable;
import javax.accessibility.Accessible;
@@ -343,4 +344,33 @@ static int[] getRangeForLine(final Accessible a, final int lineIndex) {
return new int[] { line.getStartOffset(), line.getEndOffset() };
}
+
+ static double[] getCaretRectangle(final Accessible a, final Component c) {
+ final double[] ret = new double[4];
+
+ if (a == null) return null;
+
+ return CAccessibility.invokeAndWait(new Callable() {
+ public double[] call() throws Exception {
+ final Accessible sa = CAccessible.getSwingAccessible(a);
+ if (!(sa instanceof Component)) return null;
+
+ final Component component = (Component) sa;
+
+ final InputMethodRequests imr = component.getInputMethodRequests();
+ if (imr == null) return null;
+
+ final Rectangle caretRectangle = imr.getTextLocation(null);
+
+ if (caretRectangle == null) return null;
+
+ ret[0] = caretRectangle.getX();
+ ret[1] = caretRectangle.getY();
+ ret[2] = 1; // Use 1 as caret width.
+ ret[3] = caretRectangle.getHeight();
+
+ return ret;
+ }
+ }, c);
+ }
}
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CClipboard.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CClipboard.java
index 19f9a4082205..ec574deaaa26 100644
--- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CClipboard.java
+++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CClipboard.java
@@ -89,7 +89,11 @@ protected void setContentsNative(Transferable contents) {
try {
byte[] bytes = DataTransferer.getInstance().translateTransferable(contents, flavor, format);
- setData(bytes, format);
+ if (DataFlavor.javaFileListFlavor.equals(flavor)) {
+ writeObjects(bytes);
+ } else {
+ setData(bytes, format);
+ }
} catch (IOException e) {
// Fix 4696186: don't print exception if data with
// javaJVMLocalObjectMimeType failed to serialize.
@@ -127,6 +131,7 @@ protected void registerClipboardViewerChecked() {
private native void declareTypes(long[] formats, SunClipboard newOwner);
private native void setData(byte[] data, long format);
+ private native void writeObjects(byte[] data);
void checkPasteboardAndNotify() {
if (checkPasteboardWithoutNotification()) {
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CCursorManager.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CCursorManager.java
index e4abc5fe1a9c..56400c1d293c 100644
--- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CCursorManager.java
+++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CCursorManager.java
@@ -45,6 +45,12 @@ public static CCursorManager getInstance() {
return theInstance;
}
+ private static void resetCurrentCursor() {
+ if (theInstance != null) {
+ theInstance.currentCursor = null;
+ }
+ }
+
private volatile Cursor currentCursor;
private CCursorManager() { }
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CEmbeddedFrame.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CEmbeddedFrame.java
index 049f8d0df32d..87ad9e075525 100644
--- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CEmbeddedFrame.java
+++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CEmbeddedFrame.java
@@ -100,11 +100,10 @@ public void handleScrollEvent(double pluginX, double pluginY, int modifierFlags,
deltaY, NSEvent.SCROLL_PHASE_UNSUPPORTED);
}
- public void handleKeyEvent(int eventType, int modifierFlags, String characters,
- String charsIgnoringMods, boolean isRepeat, short keyCode,
+ public void handleKeyEvent(int eventType, int modifierFlags, String characters, boolean isRepeat, short keyCode,
boolean needsKeyTyped) {
- responder.handleKeyEvent(eventType, modifierFlags, characters, charsIgnoringMods,
- keyCode, needsKeyTyped, isRepeat);
+ responder.handleKeyEvent(eventType, modifierFlags, characters,
+ null, keyCode, needsKeyTyped, isRepeat);
}
public void handleInputEvent(String text) {
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CFLayer.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CFLayer.java
index d88d33118717..ecb3ef6e6181 100644
--- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CFLayer.java
+++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CFLayer.java
@@ -43,7 +43,11 @@ protected CFLayer(long ptr, boolean disposeOnAppKitThread) {
super(ptr, disposeOnAppKitThread);
}
- public abstract SurfaceData replaceSurfaceData();
+ public abstract SurfaceData replaceSurfaceData(int scale);
+
+ public SurfaceData replaceSurfaceData() {
+ return replaceSurfaceData(0);
+ }
@Override
public void dispose() {
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CFileDialog.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CFileDialog.java
index 169aea8127e9..5e19d66f9495 100644
--- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CFileDialog.java
+++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CFileDialog.java
@@ -54,12 +54,19 @@
import java.security.AccessController;
import java.util.List;
+import com.jetbrains.desktop.JBRFileDialog;
import sun.awt.AWTAccessor;
import sun.java2d.pipe.Region;
+import sun.lwawt.LWWindowPeer;
import sun.security.action.GetBooleanAction;
+import sun.util.logging.PlatformLogger;
+
+import static com.jetbrains.desktop.JBRFileDialog.*;
class CFileDialog implements FileDialogPeer {
+ private static final PlatformLogger log = PlatformLogger.getLogger("sun.lwawt.macosx.CFileDialog");
+
private class Task implements Runnable {
@Override
@@ -68,9 +75,18 @@ public void run() {
@SuppressWarnings("removal")
boolean navigateApps = !AccessController.doPrivileged(
new GetBooleanAction("apple.awt.use-file-dialog-packages"));
- @SuppressWarnings("removal")
- boolean chooseDirectories = AccessController.doPrivileged(
- new GetBooleanAction("apple.awt.fileDialogForDirectories"));
+
+ JBRFileDialog jbrDialog = JBRFileDialog.get(target);
+ boolean createDirectories = (jbrDialog.getHints() & CREATE_DIRECTORIES_HINT) != 0;
+ boolean chooseDirectories = (jbrDialog.getHints() & SELECT_DIRECTORIES_HINT) != 0;
+ boolean chooseFiles = (jbrDialog.getHints() & SELECT_FILES_HINT) != 0;
+ if (!chooseDirectories && !chooseFiles) { // Fallback to default
+ @SuppressWarnings("removal")
+ boolean dir = AccessController.doPrivileged(
+ new GetBooleanAction("apple.awt.fileDialogForDirectories"));
+ chooseFiles = !dir;
+ chooseDirectories = dir;
+ }
int dialogMode = target.getMode();
String title = target.getTitle();
@@ -78,11 +94,22 @@ public void run() {
title = " ";
}
- String[] userFileNames = nativeRunFileDialog(title,
+ Window owner = target.getOwner();
+
+ long ownerPtr = owner == null ?
+ 0L :
+ ((CPlatformWindow) ((LWWindowPeer) AWTAccessor.getComponentAccessor().getPeer(owner))
+ .getPlatformWindow()).executeGet(ptr -> ptr);
+
+ String[] userFileNames = nativeRunFileDialog(
+ ownerPtr,
+ title,
dialogMode,
target.isMultipleMode(),
navigateApps,
chooseDirectories,
+ chooseFiles,
+ createDirectories,
target.getFilenameFilter() != null,
target.getDirectory(),
target.getFile());
@@ -91,7 +118,7 @@ public void run() {
String file = null;
File[] files = null;
- if (userFileNames != null) {
+ if (userFileNames != null && userFileNames.length > 0) {
// the dialog wasn't cancelled
int filesNumber = userFileNames.length;
files = new File[filesNumber];
@@ -152,6 +179,10 @@ public void setVisible(boolean visible) {
* If the dialog doesn't have a file filter return true.
*/
private boolean queryFilenameFilter(final String inFilename) {
+ if (!Boolean.getBoolean("awt.file.dialog.enable.filter")) {
+ return true;
+ }
+
boolean ret = false;
final FilenameFilter ff = target.getFilenameFilter();
@@ -161,14 +192,19 @@ private boolean queryFilenameFilter(final String inFilename) {
if (!fileObj.isDirectory()) {
File directoryObj = new File(fileObj.getParent());
String nameOnly = fileObj.getName();
- ret = ff.accept(directoryObj, nameOnly);
+ try {
+ ret = ff.accept(directoryObj, nameOnly);
+ } catch (Throwable e) {
+ log.warning("FilenameFilter call exception occurred", e);
+ }
}
return ret;
}
- private native String[] nativeRunFileDialog(String title, int mode,
+ private native String[] nativeRunFileDialog(long ownerPtr, String title, int mode,
boolean multipleMode, boolean shouldNavigateApps,
- boolean canChooseDirectories, boolean hasFilenameFilter,
+ boolean canChooseDirectories, boolean canChooseFiles,
+ boolean canCreateDirectories, boolean hasFilenameFilter,
String directory, String file);
@Override
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CInputMethod.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CInputMethod.java
index 64d8a101d69f..e8939a7659d8 100644
--- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CInputMethod.java
+++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CInputMethod.java
@@ -26,6 +26,7 @@
package sun.lwawt.macosx;
import java.awt.im.spi.*;
+import java.lang.reflect.Method;
import java.util.*;
import java.awt.*;
import java.awt.peer.*;
@@ -36,6 +37,7 @@
import java.lang.reflect.InvocationTargetException;
import java.text.AttributedCharacterIterator.Attribute;
import java.text.*;
+import java.util.concurrent.atomic.AtomicBoolean;
import javax.swing.text.JTextComponent;
import sun.awt.AWTAccessor;
@@ -45,8 +47,8 @@
import static sun.awt.AWTAccessor.ComponentAccessor;
public class CInputMethod extends InputMethodAdapter {
- private InputMethodContext fIMContext;
- private Component fAwtFocussedComponent;
+ private volatile InputMethodContext fIMContext;
+ private volatile Component fAwtFocussedComponent;
private LWComponentPeer, ?> fAwtFocussedComponentPeer;
private boolean isActive;
@@ -263,9 +265,12 @@ long getNativeViewPtr(LWComponentPeer, ?> peer) {
*/
public void removeNotify() {
if (fAwtFocussedComponentPeer != null) {
- nativeEndComposition(getNativeViewPtr(fAwtFocussedComponentPeer));
+ long modelPtr = getNativeViewPtr(fAwtFocussedComponentPeer);
+ nativeEndComposition(modelPtr, fAwtFocussedComponent);
+ nativeNotifyPeer(modelPtr, null);
}
+ fAwtFocussedComponent = null;
fAwtFocussedComponentPeer = null;
}
@@ -276,34 +281,32 @@ public void removeNotify() {
* to talk to when responding to key events.
*/
protected void setAWTFocussedComponent(Component component) {
- LWComponentPeer, ?> peer = null;
- long modelPtr = 0;
- CInputMethod imInstance = this;
+ if (component == null || component == fAwtFocussedComponent) {
+ // Sometimes input happens for the natively unfocused window
+ // (e.g. in case of system emoji picker),
+ // so we don't reset last focused component on focus lost.
+ return;
+ }
- // component will be null when we are told there's no focused component.
- // When that happens we need to notify the native architecture to stop generating IMEs
- if (component == null) {
- peer = fAwtFocussedComponentPeer;
- imInstance = null;
- } else {
- peer = getNearestNativePeer(component);
+ if (fAwtFocussedComponentPeer != null) {
+ long modelPtr = getNativeViewPtr(fAwtFocussedComponentPeer);
+ nativeNotifyPeer(modelPtr, null);
+ }
+ fAwtFocussedComponent = component;
+ fAwtFocussedComponentPeer = getNearestNativePeer(component);
+
+ if (fAwtFocussedComponentPeer != null) {
+ long modelPtr = getNativeViewPtr(fAwtFocussedComponentPeer);
+
+ CInputMethod imInstance = this;
// If we have a passive client, don't pass input method events to it.
if (component.getInputMethodRequests() == null) {
imInstance = null;
}
- }
-
- if (peer != null) {
- modelPtr = getNativeViewPtr(peer);
- // modelPtr refers to the ControlModel that either got or lost focus.
nativeNotifyPeer(modelPtr, imInstance);
}
-
- // Track the focused component and its nearest peer.
- fAwtFocussedComponent = component;
- fAwtFocussedComponentPeer = getNearestNativePeer(component);
}
/**
@@ -349,7 +352,7 @@ protected void setAWTFocussedComponent(Component component) {
*/
public void endComposition() {
if (fAwtFocussedComponentPeer != null)
- nativeEndComposition(getNativeViewPtr(fAwtFocussedComponentPeer));
+ nativeEndComposition(getNativeViewPtr(fAwtFocussedComponentPeer), fAwtFocussedComponent);
}
/**
@@ -423,6 +426,8 @@ public Object getControlObject() {
* text view. This effectively wipes out any text in progress.
*/
private synchronized void insertText(String aString) {
+ if (fAwtFocussedComponent == null) return;
+
AttributedString attribString = new AttributedString(aString);
// Set locale information on the new string.
@@ -494,7 +499,7 @@ private void addAttribute (boolean isThickUnderline, boolean isGray, int start,
/* Called from JNI to select the previously typed glyph during press and hold */
private void selectPreviousGlyph() {
- if (fIMContext == null) return; // ???
+ if (fIMContext == null || fAwtFocussedComponent == null) return; // ???
try {
LWCToolkit.invokeLater(new Runnable() {
public void run() {
@@ -536,8 +541,7 @@ public void run() {
private void dispatchText(int selectStart, int selectLength, boolean pressAndHold) {
// Nothing to do if we have no text.
- if (fCurrentText == null)
- return;
+ if (fCurrentText == null || fAwtFocussedComponent == null) return;
TextHitInfo theCaret = (selectLength == 0 ? TextHitInfo.beforeOffset(selectStart) : null);
TextHitInfo visiblePosition = TextHitInfo.beforeOffset(0);
@@ -556,19 +560,21 @@ private void dispatchText(int selectStart, int selectLength, boolean pressAndHol
/**
* Frequent callbacks from NSTextInput. I think we're supposed to commit it here?
*/
- private synchronized void unmarkText() {
- if (fCurrentText == null)
- return;
+ private synchronized void unmarkText(Component component) {
+ if (component == null) {
+ component = fAwtFocussedComponent;
+ }
+ if (fCurrentText == null || component == null) return;
TextHitInfo theCaret = TextHitInfo.afterOffset(fCurrentTextLength);
TextHitInfo visiblePosition = theCaret;
- InputMethodEvent event = new InputMethodEvent(fAwtFocussedComponent,
+ InputMethodEvent event = new InputMethodEvent(component,
InputMethodEvent.INPUT_METHOD_TEXT_CHANGED,
fCurrentText.getIterator(),
fCurrentTextLength,
theCaret,
visiblePosition);
- LWCToolkit.postEvent(LWCToolkit.targetToAppContext(fAwtFocussedComponent), event);
+ LWCToolkit.postEvent(LWCToolkit.targetToAppContext(component), event);
fCurrentText = null;
fCurrentTextAsString = null;
fCurrentTextLength = 0;
@@ -586,46 +592,47 @@ private synchronized boolean hasMarkedText() {
private synchronized String attributedSubstringFromRange(final int locationIn, final int lengthIn) {
final String[] retString = new String[1];
- try {
- LWCToolkit.invokeAndWait(new Runnable() {
- public void run() { synchronized(retString) {
- int location = locationIn;
- int length = lengthIn;
-
- if ((location + length) > (fIMContext.getCommittedTextLength() + fCurrentTextLength)) {
- length = fIMContext.getCommittedTextLength() - location;
- }
+ if (fIMContext != null && fAwtFocussedComponent != null) {
+ invokeAndWaitNoThrow(new Runnable() {
+ public void run() {
+ synchronized (retString) {
+ int location = locationIn;
+ int length = lengthIn;
- AttributedCharacterIterator theIterator = null;
+ if ((location + length) > (fIMContext.getCommittedTextLength() + fCurrentTextLength)) {
+ length = fIMContext.getCommittedTextLength() - location;
+ }
- if (fCurrentText == null) {
- theIterator = fIMContext.getCommittedText(location, location + length, null);
- } else {
- int insertSpot = fIMContext.getInsertPositionOffset();
+ AttributedCharacterIterator theIterator = null;
- if (location < insertSpot) {
+ if (fCurrentText == null) {
theIterator = fIMContext.getCommittedText(location, location + length, null);
- } else if (location >= insertSpot && location < insertSpot + fCurrentTextLength) {
- theIterator = fCurrentText.getIterator(null, location - insertSpot, location - insertSpot +length);
- } else {
- theIterator = fIMContext.getCommittedText(location - fCurrentTextLength, location - fCurrentTextLength + length, null);
+ } else {
+ int insertSpot = fIMContext.getInsertPositionOffset();
+
+ if (location < insertSpot) {
+ theIterator = fIMContext.getCommittedText(location, location + length, null);
+ } else if (location >= insertSpot && location < insertSpot + fCurrentTextLength) {
+ theIterator = fCurrentText.getIterator(null, location - insertSpot, location - insertSpot + length);
+ } else {
+ theIterator = fIMContext.getCommittedText(location - fCurrentTextLength, location - fCurrentTextLength + length, null);
+ }
}
- }
- // Get the characters from the iterator
- char[] selectedText = new char[theIterator.getEndIndex() - theIterator.getBeginIndex()];
- char current = theIterator.first();
- int index = 0;
- while (current != CharacterIterator.DONE) {
- selectedText[index++] = current;
- current = theIterator.next();
- }
+ // Get the characters from the iterator
+ char[] selectedText = new char[theIterator.getEndIndex() - theIterator.getBeginIndex()];
+ char current = theIterator.first();
+ int index = 0;
+ while (current != CharacterIterator.DONE) {
+ selectedText[index++] = current;
+ current = theIterator.next();
+ }
- retString[0] = new String(selectedText);
- }}
+ retString[0] = new String(selectedText);
+ }
+ }
}, fAwtFocussedComponent);
- } catch (InvocationTargetException ite) { ite.printStackTrace(); }
-
+ }
synchronized(retString) { return retString[0]; }
}
@@ -638,8 +645,8 @@ public void run() { synchronized(retString) {
private synchronized int[] selectedRange() {
final int[] returnValue = new int[2];
- try {
- LWCToolkit.invokeAndWait(new Runnable() {
+ if (fIMContext != null && fAwtFocussedComponent != null) {
+ invokeAndWaitNoThrow(new Runnable() {
public void run() { synchronized(returnValue) {
AttributedCharacterIterator theIterator = fIMContext.getSelectedText(null);
if (theIterator == null) {
@@ -672,7 +679,7 @@ public void run() { synchronized(returnValue) {
}}
}, fAwtFocussedComponent);
- } catch (InvocationTargetException ite) { ite.printStackTrace(); }
+ }
synchronized(returnValue) { return returnValue; }
}
@@ -684,20 +691,17 @@ public void run() { synchronized(returnValue) {
* return null.
*/
private synchronized int[] markedRange() {
- if (fCurrentText == null)
- return null;
+ if (fCurrentText == null || fAwtFocussedComponent == null) return null;
final int[] returnValue = new int[2];
- try {
- LWCToolkit.invokeAndWait(new Runnable() {
- public void run() { synchronized(returnValue) {
- // The insert position is always after the composed text, so the range start is the
- // insert spot less the length of the composed text.
- returnValue[0] = fIMContext.getInsertPositionOffset();
- }}
- }, fAwtFocussedComponent);
- } catch (InvocationTargetException ite) { ite.printStackTrace(); }
+ invokeAndWaitNoThrow(new Runnable() {
+ public void run() { synchronized(returnValue) {
+ // The insert position is always after the composed text, so the range start is the
+ // insert spot less the length of the composed text.
+ returnValue[0] = fIMContext.getInsertPositionOffset();
+ }}
+ }, fAwtFocussedComponent);
returnValue[1] = fCurrentTextLength;
synchronized(returnValue) { return returnValue; }
@@ -714,36 +718,38 @@ private synchronized int[] firstRectForCharacterRange(final int absoluteTextOffs
final int[] rect = new int[4];
try {
- LWCToolkit.invokeAndWait(new Runnable() {
- public void run() { synchronized(rect) {
- int insertOffset = fIMContext.getInsertPositionOffset();
- int composedTextOffset = absoluteTextOffset - insertOffset;
- if (composedTextOffset < 0) composedTextOffset = 0;
- Rectangle r = fIMContext.getTextLocation(TextHitInfo.beforeOffset(composedTextOffset));
- rect[0] = r.x;
- rect[1] = r.y;
- rect[2] = r.width;
- rect[3] = r.height;
-
- // This next if-block is a hack to work around a bug in JTextComponent. getTextLocation ignores
- // the TextHitInfo passed to it and always returns the location of the insertion point, which is
- // at the start of the composed text. We'll do some calculation so the candidate window for Kotoeri
- // follows the requested offset into the composed text.
- if (composedTextOffset > 0 && (fAwtFocussedComponent instanceof JTextComponent)) {
- Rectangle r2 = fIMContext.getTextLocation(TextHitInfo.beforeOffset(0));
-
- if (r.equals(r2)) {
- // FIXME: (SAK) If the candidate text wraps over two lines, this calculation pushes the candidate
- // window off the right edge of the component.
- String inProgressSubstring = fCurrentTextAsString.substring(0, composedTextOffset);
- Graphics g = fAwtFocussedComponent.getGraphics();
- int xOffset = g.getFontMetrics().stringWidth(inProgressSubstring);
- rect[0] += xOffset;
- g.dispose();
+ if (fIMContext != null && fAwtFocussedComponent != null) {
+ FxInvoker.invoke(() -> {
+ synchronized (rect) {
+ int insertOffset = fIMContext.getInsertPositionOffset();
+ int composedTextOffset = absoluteTextOffset - insertOffset;
+ if (composedTextOffset < 0) composedTextOffset = 0;
+ Rectangle r = fIMContext.getTextLocation(TextHitInfo.beforeOffset(composedTextOffset));
+ rect[0] = r.x;
+ rect[1] = r.y;
+ rect[2] = r.width;
+ rect[3] = r.height;
+
+ // This next if-block is a hack to work around a bug in JTextComponent. getTextLocation ignores
+ // the TextHitInfo passed to it and always returns the location of the insertion point, which is
+ // at the start of the composed text. We'll do some calculation so the candidate window for Kotoeri
+ // follows the requested offset into the composed text.
+ if (composedTextOffset > 0 && (fAwtFocussedComponent instanceof JTextComponent)) {
+ Rectangle r2 = fIMContext.getTextLocation(TextHitInfo.beforeOffset(0));
+
+ if (r.equals(r2) && fCurrentTextAsString != null) {
+ // FIXME: (SAK) If the candidate text wraps over two lines, this calculation pushes the candidate
+ // window off the right edge of the component.
+ String inProgressSubstring = fCurrentTextAsString.substring(0, composedTextOffset);
+ Graphics g = fAwtFocussedComponent.getGraphics();
+ int xOffset = g.getFontMetrics().stringWidth(inProgressSubstring);
+ rect[0] += xOffset;
+ g.dispose();
+ }
}
}
- }}
- }, fAwtFocussedComponent);
+ }, (sun.awt.im.InputContext)fIMContext, fAwtFocussedComponent);
+ }
} catch (InvocationTargetException ite) { ite.printStackTrace(); }
synchronized(rect) { return rect; }
@@ -758,12 +764,14 @@ private synchronized int characterIndexForPoint(final int screenX, final int scr
final int[] insertPositionOffset = new int[1];
try {
- LWCToolkit.invokeAndWait(new Runnable() {
- public void run() { synchronized(offsetInfo) {
- offsetInfo[0] = fIMContext.getLocationOffset(screenX, screenY);
- insertPositionOffset[0] = fIMContext.getInsertPositionOffset();
- }}
- }, fAwtFocussedComponent);
+ if (fIMContext != null && fAwtFocussedComponent != null) {
+ FxInvoker.invoke(() -> {
+ synchronized (offsetInfo) {
+ offsetInfo[0] = fIMContext.getLocationOffset(screenX, screenY);
+ insertPositionOffset[0] = fIMContext.getInsertPositionOffset();
+ }
+ }, (sun.awt.im.InputContext)fIMContext, fAwtFocussedComponent);
+ }
} catch (InvocationTargetException ite) { ite.printStackTrace(); }
// This bit of gymnastics ensures that the returned location is within the composed text.
@@ -799,7 +807,7 @@ public String getNativeInputMethodInfo()
// Note that if nativePeer isn't something that normally accepts keystrokes (i.e., a CPanel)
// these calls will be ignored.
private native void nativeNotifyPeer(long nativePeer, CInputMethod imInstance);
- private native void nativeEndComposition(long nativePeer);
+ private native void nativeEndComposition(long nativePeer, Component component);
private native void nativeHandleEvent(LWComponentPeer, ?> peer, AWTEvent event);
// Returns the locale of the active input method.
@@ -813,4 +821,71 @@ public String getNativeInputMethodInfo()
// Initialize toolbox routines
static native void nativeInit();
+
+ private static class FxInvoker {
+ final static Method GET_CLIENT_COMPONENT_METHOD;
+ static Class> JFX_PANEL_CLASS;
+
+ static {
+ Method m = null;
+ try {
+ m = sun.awt.im.InputContext.class.getDeclaredMethod("getClientComponent");
+ if (m != null) m.setAccessible(true);
+ } catch (NoSuchMethodException ignore) {
+ }
+ GET_CLIENT_COMPONENT_METHOD = m;
+ }
+
+ static Component getClientComponent(sun.awt.im.InputContext ctx) {
+ if (GET_CLIENT_COMPONENT_METHOD != null) {
+ try {
+ return (Component)GET_CLIENT_COMPONENT_METHOD.invoke(ctx);
+ } catch (IllegalAccessException | InvocationTargetException ignore) {
+ }
+ }
+ return null;
+ }
+
+ static boolean instanceofJFXPanel(Component clientComponent) {
+ if (clientComponent != null) {
+ if (JFX_PANEL_CLASS == null) {
+ try {
+ // the class is not available in the current class loader context, use the client class loader
+ JFX_PANEL_CLASS = Class.forName("javafx.embed.swing.JFXPanel", false, clientComponent.getClass().getClassLoader());
+ } catch (ClassNotFoundException ignore) {
+ }
+ }
+ if (JFX_PANEL_CLASS != null) {
+ return JFX_PANEL_CLASS.isInstance(clientComponent);
+ }
+ }
+ return false;
+ }
+
+ // Executed on AppKit
+ static void invoke(Runnable runnable, sun.awt.im.InputContext inputContext, Component targetToAppContext) throws InvocationTargetException {
+ AtomicBoolean runOnAppKit = new AtomicBoolean(false);
+
+ // 1) Do not run secondary msg loop in this case.
+ // 2) Delegate runnable back to FX when applicable.
+ invokeAndWaitNoThrow(() -> {
+ runOnAppKit.set(instanceofJFXPanel(getClientComponent(inputContext)));
+ if (!runOnAppKit.get()) {
+ runnable.run();
+ }
+ }, targetToAppContext);
+
+ if (runOnAppKit.get()) {
+ runnable.run();
+ }
+ }
+ }
+
+ static void invokeAndWaitNoThrow(Runnable runnable, Component component) {
+ try {
+ LWCToolkit.invokeAndWait(runnable, component);
+ } catch (Throwable e) {
+ e.printStackTrace();
+ }
+ }
}
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CMenu.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CMenu.java
index 3511d4aeb1d8..7fef2633d79a 100644
--- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CMenu.java
+++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CMenu.java
@@ -25,6 +25,7 @@
package sun.lwawt.macosx;
+import sun.awt.AWTThreading;
import java.awt.Menu;
import java.awt.MenuBar;
import java.awt.MenuItem;
@@ -64,14 +65,14 @@ long createModel() {
LWCToolkit.targetToPeer(getTarget().getParent());
if (parent instanceof CMenu) {
- return parent.executeGet(this::nativeCreateSubMenu);
+ return AWTThreading.executeWaitToolkit(() -> parent.executeGet(this::nativeCreateSubMenu));
}
if (parent instanceof CMenuBar) {
MenuBar parentContainer = (MenuBar)getTarget().getParent();
boolean isHelpMenu = parentContainer.getHelpMenu() == getTarget();
int insertionLocation = ((CMenuBar)parent).getNextInsertionIndex();
- return parent.executeGet(ptr -> nativeCreateMenu(ptr, isHelpMenu,
- insertionLocation));
+ return AWTThreading.executeWaitToolkit(() -> parent.executeGet(ptr -> nativeCreateMenu(ptr, isHelpMenu,
+ insertionLocation)));
}
throw new InternalError("Parent must be CMenu or CMenuBar");
}
@@ -84,7 +85,7 @@ public final void addItem(MenuItem item) {
@Override
public final void delItem(final int index) {
- execute(ptr -> nativeDeleteItem(ptr, index));
+ AWTThreading.executeWaitToolkit(() -> execute(ptr -> nativeDeleteItem(ptr, index)));
}
@Override
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CMenuItem.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CMenuItem.java
index c4b2640efb84..0a21cbf00a0e 100644
--- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CMenuItem.java
+++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CMenuItem.java
@@ -33,6 +33,7 @@
import java.awt.peer.MenuItemPeer;
import java.util.concurrent.atomic.AtomicBoolean;
+import sun.awt.AWTThreading;
import sun.awt.SunToolkit;
import sun.lwawt.LWToolkit;
@@ -61,7 +62,7 @@ private boolean isSeparator() {
@Override
long createModel() {
CMenuComponent parent = (CMenuComponent)LWToolkit.targetToPeer(getTarget().getParent());
- return parent.executeGet(ptr->nativeCreate(ptr, isSeparator()));
+ return AWTThreading.executeWaitToolkit(() -> parent.executeGet(ptr->nativeCreate(ptr, isSeparator())));
}
@SuppressWarnings("deprecation")
public void setLabel(String label, char keyChar, int keyCode, int modifiers) {
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPlatformComponent.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPlatformComponent.java
index a98b5529845d..51ac58a186ca 100644
--- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPlatformComponent.java
+++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPlatformComponent.java
@@ -27,9 +27,11 @@
package sun.lwawt.macosx;
import java.awt.Insets;
+import java.util.Objects;
import sun.lwawt.PlatformComponent;
import sun.lwawt.PlatformWindow;
+import sun.lwawt.LWWindowPeer;
/**
* On OSX {@code CPlatformComponent} stores pointer to the native CAlayer which
@@ -53,6 +55,8 @@ public long getPointer() {
@Override
public void initialize(final PlatformWindow platformWindow) {
+ Objects.requireNonNull(platformWindow);
+
this.platformWindow = platformWindow;
setPtr(nativeCreateComponent(platformWindow.getLayerPtr()));
}
@@ -61,10 +65,13 @@ public void initialize(final PlatformWindow platformWindow) {
@Override
public void setBounds(final int x, final int y, final int w, final int h) {
- // translates values from the coordinate system of the top-level window
- // to the coordinate system of the content view
- final Insets insets = platformWindow.getPeer().getInsets();
- execute(ptr->nativeSetBounds(ptr, x - insets.left, y - insets.top, w, h));
+ if (platformWindow != null) {
+ // translates values from the coordinate system of the top-level window
+ // to the coordinate system of the content view
+ final LWWindowPeer peer = platformWindow.getPeer();
+ final Insets insets = (peer != null) ? peer.getInsets() : new Insets(0, 0, 0, 0);
+ execute(ptr -> nativeSetBounds(ptr, x - insets.left, y - insets.top, w, h));
+ }
}
@Override
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPlatformEmbeddedFrame.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPlatformEmbeddedFrame.java
index 8570ecaa97a8..da9cea1a0811 100644
--- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPlatformEmbeddedFrame.java
+++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPlatformEmbeddedFrame.java
@@ -28,7 +28,7 @@
import java.awt.*;
import java.awt.event.FocusEvent;
-import sun.awt.CGraphicsDevice;
+import sun.awt.CGraphicsEnvironment;
import sun.java2d.SurfaceData;
import sun.java2d.metal.MTLLayer;
import sun.java2d.opengl.CGLLayer;
@@ -55,7 +55,7 @@ public class CPlatformEmbeddedFrame implements PlatformWindow {
@Override // PlatformWindow
public void initialize(Window target, final LWWindowPeer peer, PlatformWindow owner) {
this.peer = peer;
- if (CGraphicsDevice.usingMetalPipeline()) {
+ if (CGraphicsEnvironment.usingMetalPipeline()) {
this.windowLayer = new MTLLayer(peer);
} else {
this.windowLayer = new CGLLayer(peer);
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPlatformResponder.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPlatformResponder.java
index f14c7b40cbab..1447d9d31e95 100644
--- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPlatformResponder.java
+++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPlatformResponder.java
@@ -26,6 +26,7 @@
package sun.lwawt.macosx;
import sun.awt.SunToolkit;
+import sun.awt.event.KeyEventProcessing;
import sun.lwawt.LWWindowPeer;
import sun.lwawt.PlatformEventNotifier;
@@ -35,6 +36,8 @@
import java.awt.event.MouseWheelEvent;
import java.awt.event.KeyEvent;
import java.util.Locale;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
/**
* Translates NSEvents/NPCocoaEvents into AWT events.
@@ -46,6 +49,14 @@ final class CPlatformResponder {
private int lastKeyPressCode = KeyEvent.VK_UNDEFINED;
private final DeltaAccumulator deltaAccumulatorX = new DeltaAccumulator();
private final DeltaAccumulator deltaAccumulatorY = new DeltaAccumulator();
+ private boolean momentumStarted;
+ private int momentumX;
+ private int momentumY;
+ private int momentumModifiers;
+ private int lastDraggedAbsoluteX;
+ private int lastDraggedAbsoluteY;
+ private int lastDraggedRelativeX;
+ private int lastDraggedRelativeY;
CPlatformResponder(final PlatformEventNotifier eventNotifier,
final boolean isNpapiCallback) {
@@ -67,6 +78,18 @@ void handleMouseEvent(int eventType, int modifierFlags, int buttonNumber,
int jeventType = isNpapiCallback ? NSEvent.npToJavaEventType(eventType) :
NSEvent.nsToJavaEventType(eventType);
+ boolean dragged = jeventType == MouseEvent.MOUSE_DRAGGED;
+ if (dragged // ignore dragged event that does not change any location
+ && lastDraggedAbsoluteX == absX && lastDraggedRelativeX == x
+ && lastDraggedAbsoluteY == absY && lastDraggedRelativeY == y) return;
+
+ if (dragged || jeventType == MouseEvent.MOUSE_PRESSED) {
+ lastDraggedAbsoluteX = absX;
+ lastDraggedAbsoluteY = absY;
+ lastDraggedRelativeX = x;
+ lastDraggedRelativeY = y;
+ }
+
int jbuttonNumber = MouseEvent.NOBUTTON;
int jclickCount = 0;
@@ -79,12 +102,16 @@ void handleMouseEvent(int eventType, int modifierFlags, int buttonNumber,
}
int jmodifiers = NSEvent.nsToJavaModifiers(modifierFlags);
- if ((jeventType == MouseEvent.MOUSE_PRESSED) && (jbuttonNumber > MouseEvent.NOBUTTON)) {
- // 8294426: NSEvent.nsToJavaModifiers returns 0 on M2 MacBooks if the event is generated
- // via tapping (not pressing) on a trackpad
- // (System Preferences -> Trackpad -> Tap to click must be turned on).
- // So let's set the modifiers manually.
- jmodifiers |= MouseEvent.getMaskForButton(jbuttonNumber);
+ if (jbuttonNumber > MouseEvent.NOBUTTON) {
+ if ( (jeventType == MouseEvent.MOUSE_PRESSED) || (Jbr5762Fix.isEnabled && (jeventType == MouseEvent.MOUSE_DRAGGED)) ) {
+ // 8294426: NSEvent.nsToJavaModifiers returns 0 on M2 MacBooks if the event is generated
+ // via tapping (not pressing) on a trackpad
+ // (System Preferences -> Trackpad -> Tap to click must be turned on).
+ // So let's set the modifiers manually.
+ //
+ // JBR-5762: enforce modifiers for the pressed button of MOUSE_DRAGGED events as well.
+ jmodifiers |= MouseEvent.getMaskForButton(jbuttonNumber);
+ }
}
boolean jpopupTrigger = NSEvent.isPopupTrigger(jmodifiers);
@@ -97,11 +124,26 @@ void handleMouseEvent(int eventType, int modifierFlags, int buttonNumber,
/**
* Handles scroll events.
*/
- void handleScrollEvent(final int x, final int y, final int absX,
+ void handleScrollEvent(int x, int y, final int absX,
final int absY, final int modifierFlags,
final double deltaX, final double deltaY,
final int scrollPhase) {
int jmodifiers = NSEvent.nsToJavaModifiers(modifierFlags);
+
+ if (scrollPhase > NSEvent.SCROLL_PHASE_UNSUPPORTED) {
+ if (scrollPhase == NSEvent.SCROLL_PHASE_BEGAN) {
+ momentumStarted = false;
+ } else if (scrollPhase == NSEvent.SCROLL_PHASE_MOMENTUM_BEGAN) {
+ momentumStarted = true;
+ momentumX = x;
+ momentumY = y;
+ momentumModifiers = jmodifiers;
+ } else if (momentumStarted) {
+ x = momentumX;
+ y = momentumY;
+ jmodifiers = momentumModifiers;
+ }
+ }
final boolean isShift = (jmodifiers & InputEvent.SHIFT_DOWN_MASK) != 0;
int roundDeltaX = deltaAccumulatorX.getRoundedDelta(deltaX, scrollPhase);
@@ -134,8 +176,17 @@ private void dispatchScrollEvent(final int x, final int y, final int absX,
/**
* Handles key events.
+ *
+ * @param eventType macOS event type ID: keyDown, keyUp or flagsChanged
+ * @param modifierFlags macOS modifier flags mask (NSEventModifierFlags)
+ * @param chars NSEvent's characters property
+ * @param actualChars If non-null, then this key should generate KEY_TYPED events
+ * corresponding to characters in this string. Only valid for keyDown events.
+ * @param keyCode macOS virtual key code of the key being pressed or released
+ * @param needsKeyTyped post KEY_TYPED events?
+ * @param needsKeyReleased post KEY_RELEASED events?
*/
- void handleKeyEvent(int eventType, int modifierFlags, String chars, String charsIgnoringModifiers,
+ void handleKeyEvent(int eventType, int modifierFlags, String chars, String actualChars,
short keyCode, boolean needsKeyTyped, boolean needsKeyReleased) {
boolean isFlagsChangedEvent =
isNpapiCallback ? (eventType == CocoaConstants.NPCocoaEventFlagsChanged) :
@@ -143,13 +194,10 @@ void handleKeyEvent(int eventType, int modifierFlags, String chars, String chars
int jeventType = KeyEvent.KEY_PRESSED;
int jkeyCode = KeyEvent.VK_UNDEFINED;
- int jextendedkeyCode = -1;
int jkeyLocation = KeyEvent.KEY_LOCATION_UNKNOWN;
- boolean postsTyped = false;
boolean spaceKeyTyped = false;
char testChar = KeyEvent.CHAR_UNDEFINED;
- boolean isDeadChar = (chars!= null && chars.length() == 0);
if (isFlagsChangedEvent) {
int[] in = new int[] {modifierFlags, keyCode};
@@ -162,59 +210,33 @@ void handleKeyEvent(int eventType, int modifierFlags, String chars, String chars
jeventType = out[2];
} else {
if (chars != null && chars.length() > 0) {
- testChar = chars.charAt(0);
-
- //Check if String chars contains SPACE character.
+ // `chars` might contain more than one character, so why are we using the last one?
+ // It doesn't really matter actually! If the string contains more than one character,
+ // the only way that this character will be used is to construct the keyChar field of the KeyEvent object.
+ // That field is only guaranteed to be meaningful for KEY_TYPED events, so let's not overthink it.
+ // Please note: this character is NOT used to construct extended key codes, that happens
+ // inside the NSEvent.nsToJavaKeyInfo function.
+ testChar = chars.charAt(chars.length() - 1);
+
+ // Check if String chars contains SPACE character.
if (chars.trim().isEmpty()) {
spaceKeyTyped = true;
}
}
- char testCharIgnoringModifiers = charsIgnoringModifiers != null && charsIgnoringModifiers.length() > 0 ?
- charsIgnoringModifiers.charAt(0) : KeyEvent.CHAR_UNDEFINED;
-
- int[] in = new int[] {testCharIgnoringModifiers, isDeadChar ? 1 : 0, modifierFlags, keyCode};
- int[] out = new int[4]; // [jkeyCode, jkeyLocation, deadChar, extendedKeyCode]
+ int[] in = new int[] {keyCode, KeyEventProcessing.useNationalLayouts ? 1 : 0, KeyEventProcessing.reportDeadKeysAsNormal ? 1 : 0};
+ int[] out = new int[2]; // [jkeyCode, jkeyLocation]
- postsTyped = NSEvent.nsToJavaKeyInfo(in, out);
- if (!postsTyped) {
- testChar = KeyEvent.CHAR_UNDEFINED;
- }
-
- if(isDeadChar){
- testChar = (char) out[2];
- if(testChar == 0){
- return;
- }
- }
-
- // If Pinyin Simplified input method is selected, CAPS_LOCK key is supposed to switch
- // input to latin letters.
- // It is necessary to use testCharIgnoringModifiers instead of testChar for event
- // generation in such case to avoid uppercase letters in text components.
- LWCToolkit lwcToolkit = (LWCToolkit)Toolkit.getDefaultToolkit();
- if ((lwcToolkit.getLockingKeyState(KeyEvent.VK_CAPS_LOCK) &&
- Locale.SIMPLIFIED_CHINESE.equals(lwcToolkit.getDefaultKeyboardLocale())) ||
- (LWCToolkit.isLocaleUSInternationalPC(lwcToolkit.getDefaultKeyboardLocale()) &&
- LWCToolkit.isCharModifierKeyInUSInternationalPC(testChar) &&
- (testChar != testCharIgnoringModifiers))) {
- testChar = testCharIgnoringModifiers;
- }
+ NSEvent.nsToJavaKeyInfo(in, out);
jkeyCode = out[0];
- jextendedkeyCode = out[3];
jkeyLocation = out[1];
jeventType = isNpapiCallback ? NSEvent.npToJavaEventType(eventType) :
NSEvent.nsToJavaEventType(eventType);
}
- char javaChar = NSEvent.nsToJavaChar(testChar, modifierFlags, spaceKeyTyped);
- // Some keys may generate a KEY_TYPED, but we can't determine
- // what that character is. That's likely a bug, but for now we
- // just check for CHAR_UNDEFINED.
- if (javaChar == KeyEvent.CHAR_UNDEFINED) {
- postsTyped = false;
- }
+ char javaChar = (testChar == KeyEvent.CHAR_UNDEFINED) ? KeyEvent.CHAR_UNDEFINED :
+ NSEvent.nsToJavaChar(testChar, modifierFlags, spaceKeyTyped);
int jmodifiers = NSEvent.nsToJavaModifiers(modifierFlags);
long when = System.currentTimeMillis();
@@ -223,32 +245,34 @@ void handleKeyEvent(int eventType, int modifierFlags, String chars, String chars
lastKeyPressCode = jkeyCode;
}
eventNotifier.notifyKeyEvent(jeventType, when, jmodifiers,
- jkeyCode, javaChar, jkeyLocation, jextendedkeyCode);
-
- // Current browser may be sending input events, so don't
- // post the KEY_TYPED here.
- postsTyped &= needsKeyTyped;
+ jkeyCode, javaChar, jkeyLocation);
// That's the reaction on the PRESSED (not RELEASED) event as it comes to
// appear in MacOSX.
// Modifier keys (shift, etc) don't want to send TYPED events.
// On the other hand we don't want to generate keyTyped events
// for clipboard related shortcuts like Meta + [CVX]
- if (jeventType == KeyEvent.KEY_PRESSED && postsTyped &&
+ if (jeventType == KeyEvent.KEY_PRESSED && needsKeyTyped && javaChar != KeyEvent.CHAR_UNDEFINED &&
(jmodifiers & KeyEvent.META_DOWN_MASK) == 0) {
- // Enter and Space keys finish the input method processing,
- // KEY_TYPED and KEY_RELEASED events for them are synthesized in handleInputEvent.
- if (needsKeyReleased && (jkeyCode == KeyEvent.VK_ENTER || jkeyCode == KeyEvent.VK_SPACE)) {
- return;
+ if (actualChars == null) {
+ // Either macOS didn't send us anything in insertText: to type,
+ // or this event was not generated in AWTView.m. Let's fall back to using javaChar
+ // since we still need to generate KEY_TYPED events, for instance for Ctrl+ combinations.
+ // javaChar is guaranteed to be a valid character, since postsTyped is true.
+ actualChars = String.valueOf(javaChar);
}
- eventNotifier.notifyKeyEvent(KeyEvent.KEY_TYPED, when, jmodifiers,
- KeyEvent.VK_UNDEFINED, javaChar,
- KeyEvent.KEY_LOCATION_UNKNOWN, jextendedkeyCode);
- //If events come from Firefox, released events should also be generated.
+
+ for (char ch : actualChars.toCharArray()) {
+ eventNotifier.notifyKeyEvent(KeyEvent.KEY_TYPED, when, jmodifiers,
+ KeyEvent.VK_UNDEFINED, ch,
+ KeyEvent.KEY_LOCATION_UNKNOWN);
+ }
+
+ // If events come from Firefox, released events should also be generated.
if (needsKeyReleased) {
eventNotifier.notifyKeyEvent(KeyEvent.KEY_RELEASED, when, jmodifiers,
jkeyCode, javaChar,
- KeyEvent.KEY_LOCATION_UNKNOWN, jextendedkeyCode);
+ KeyEvent.KEY_LOCATION_UNKNOWN);
}
}
}
@@ -262,13 +286,13 @@ void handleInputEvent(String text) {
eventNotifier.notifyKeyEvent(KeyEvent.KEY_TYPED,
System.currentTimeMillis(),
0, KeyEvent.VK_UNDEFINED, c,
- KeyEvent.KEY_LOCATION_UNKNOWN, -1);
+ KeyEvent.KEY_LOCATION_UNKNOWN);
index++;
}
eventNotifier.notifyKeyEvent(KeyEvent.KEY_RELEASED,
System.currentTimeMillis(),
0, lastKeyPressCode, c,
- KeyEvent.KEY_LOCATION_UNKNOWN, -1);
+ KeyEvent.KEY_LOCATION_UNKNOWN);
}
}
@@ -314,4 +338,19 @@ else if (scrollPhase == NSEvent.SCROLL_PHASE_MOMENTUM_BEGAN) {
return roundDelta;
}
}
+
+ static class Jbr5762Fix {
+ static final boolean isEnabled;
+
+ static {
+ boolean isEnabledLocal = false;
+
+ try {
+ isEnabledLocal = Boolean.parseBoolean(System.getProperty("awt.mac.enforceMouseModifiersForMouseDragged", "true"));
+ } catch (Exception ignored) {
+ }
+
+ isEnabled = isEnabledLocal;
+ }
+ }
}
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPlatformView.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPlatformView.java
index b79f7c3ecf89..ddf85c410a29 100644
--- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPlatformView.java
+++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPlatformView.java
@@ -35,14 +35,16 @@
import java.util.concurrent.atomic.AtomicReference;
import sun.awt.CGraphicsEnvironment;
-import sun.awt.CGraphicsDevice;
import sun.java2d.metal.MTLLayer;
import sun.lwawt.LWWindowPeer;
import sun.java2d.SurfaceData;
import sun.java2d.opengl.CGLLayer;
+import sun.util.logging.PlatformLogger;
public class CPlatformView extends CFRetainedResource {
+ private static final PlatformLogger logger =
+ PlatformLogger.getLogger(CPlatformView.class.getName());
private native long nativeCreateView(int x, int y, int width, int height, long windowLayerPtr);
private static native void nativeSetAutoResizable(long awtView, boolean toResize);
private static native int nativeGetNSViewDisplayID(long awtView);
@@ -61,8 +63,7 @@ public CPlatformView() {
public void initialize(LWWindowPeer peer, CPlatformResponder responder) {
initializeBase(peer, responder);
- this.windowLayer = CGraphicsDevice.usingMetalPipeline()? createMTLLayer() : createCGLayer();
-
+ this.windowLayer = CGraphicsEnvironment.usingMetalPipeline()? createMTLLayer() : createCGLayer();
setPtr(nativeCreateView(0, 0, 0, 0, getWindowLayerPtr()));
}
@@ -199,8 +200,8 @@ private void deliverMouseEvent(final NSEvent event) {
}
private void deliverKeyEvent(NSEvent event) {
- responder.handleKeyEvent(event.getType(), event.getModifierFlags(), event.getCharacters(),
- event.getCharactersIgnoringModifiers(), event.getKeyCode(), true, false);
+ responder.handleKeyEvent(event.getType(), event.getModifierFlags(), event.getCharacters(), event.getActualCharacters(),
+ event.getKeyCode(), true, false);
}
/**
@@ -210,4 +211,13 @@ private void deliverKeyEvent(NSEvent event) {
private void deliverWindowDidExposeEvent() {
peer.notifyExpose(peer.getSize());
}
+
+ private void deliverChangeBackingProperties(float scale) {
+ if (logger.isLoggable(PlatformLogger.Level.FINE)) {
+ logger.fine("Changed backing properties, scale = " + scale);
+ }
+ if (scale > 0 && windowLayer != null) {
+ windowLayer.replaceSurfaceData(Math.round(scale));
+ }
+ }
}
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPlatformWindow.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPlatformWindow.java
index 1be76db3acdf..c6bacf4f5506 100644
--- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPlatformWindow.java
+++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPlatformWindow.java
@@ -30,19 +30,24 @@
import java.awt.DefaultKeyboardFocusManager;
import java.awt.Dialog;
import java.awt.Dialog.ModalityType;
+import java.awt.EventQueue;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Frame;
import java.awt.GraphicsDevice;
+import java.awt.GraphicsEnvironment;
import java.awt.Insets;
+import java.awt.KeyboardFocusManager;
import java.awt.MenuBar;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.event.FocusEvent;
+import java.awt.event.PaintEvent;
import java.awt.event.WindowEvent;
import java.awt.event.WindowStateListener;
+import java.awt.peer.ComponentPeer;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.lang.reflect.InvocationTargetException;
@@ -63,6 +68,9 @@
import sun.awt.AWTAccessor;
import sun.awt.AWTAccessor.ComponentAccessor;
import sun.awt.AWTAccessor.WindowAccessor;
+import sun.awt.AWTThreading;
+import sun.awt.PaintEventDispatcher;
+import sun.java2d.SunGraphicsEnvironment;
import sun.java2d.SurfaceData;
import sun.lwawt.LWLightweightFramePeer;
import sun.lwawt.LWToolkit;
@@ -75,6 +83,7 @@
public class CPlatformWindow extends CFRetainedResource implements PlatformWindow {
private native long nativeCreateNSWindow(long nsViewPtr,long ownerPtr, long styleBits, double x, double y, double w, double h);
private static native void nativeSetNSWindowStyleBits(long nsWindowPtr, int mask, int data);
+ private static native void nativeSetNSWindowAppearance(long nsWindowPtr, String appearanceName);
private static native void nativeSetNSWindowMenuBar(long nsWindowPtr, long menuBarPtr);
private static native Insets nativeGetNSWindowInsets(long nsWindowPtr);
private static native void nativeSetNSWindowBounds(long nsWindowPtr, double x, double y, double w, double h);
@@ -83,7 +92,8 @@ private static native void nativeSetNSWindowStandardFrame(long nsWindowPtr,
double x, double y, double w, double h);
private static native void nativeSetNSWindowMinMax(long nsWindowPtr, double minW, double minH, double maxW, double maxH);
private static native void nativePushNSWindowToBack(long nsWindowPtr);
- private static native void nativePushNSWindowToFront(long nsWindowPtr);
+ private static native void nativePushNSWindowToFront(long nsWindowPtr, boolean wait);
+ private static native void nativeHideWindow(long nsWindowPtr, boolean wait);
private static native void nativeSetNSWindowTitle(long nsWindowPtr, String title);
private static native void nativeRevalidateNSWindowShadow(long nsWindowPtr);
private static native void nativeSetNSWindowMinimizedIcon(long nsWindowPtr, long nsImage);
@@ -96,6 +106,11 @@ private static native void nativeSetNSWindowStandardFrame(long nsWindowPtr,
private static native void nativeEnterFullScreenMode(long nsWindowPtr);
private static native void nativeExitFullScreenMode(long nsWindowPtr);
static native CPlatformWindow nativeGetTopmostPlatformWindowUnderMouse();
+ private static native void nativeRaiseLevel(long nsWindowPtr, boolean popup, boolean onlyIfParentIsActive);
+ private static native boolean nativeDelayShowing(long nsWindowPtr);
+ private static native void nativeUpdateCustomTitleBar(long nsWindowPtr);
+ private static native void nativeCallDeliverMoveResizeEvent(long nsWindowPtr);
+ private static native void nativeSetRoundedCorners(long nsWindowPrt, float radius, int borderWidth, int borderColor);
// Logger to report issues happened during execution but that do not affect functionality
private static final PlatformLogger logger = PlatformLogger.getLogger("sun.lwawt.macosx.CPlatformWindow");
@@ -127,6 +142,8 @@ private static native void nativeSetNSWindowStandardFrame(long nsWindowPtr,
public static final String WINDOW_FULL_CONTENT = "apple.awt.fullWindowContent";
public static final String WINDOW_TRANSPARENT_TITLE_BAR = "apple.awt.transparentTitleBar";
public static final String WINDOW_TITLE_VISIBLE = "apple.awt.windowTitleVisible";
+ public static final String WINDOW_APPEARANCE = "apple.awt.windowAppearance";
+ public static final String WINDOW_CORNER_RADIUS = "apple.awt.windowCornerRadius";
// This system property is named as jdk.* because it is not specific to AWT
// and it is also used in JavaFX
@@ -155,7 +172,6 @@ private static native void nativeSetNSWindowStandardFrame(long nsWindowPtr,
static final int MINIMIZABLE = 1 << 8;
static final int RESIZABLE = 1 << 9; // both a style bit and prop bit
- static final int NONACTIVATING = 1 << 24;
static final int IS_DIALOG = 1 << 25;
static final int IS_MODAL = 1 << 26;
static final int IS_POPUP = 1 << 27;
@@ -269,6 +285,18 @@ public void applyProperty(final CPlatformWindow c, final Object value) {
public void applyProperty(final CPlatformWindow c, final Object value) {
c.setStyleBits(TITLE_VISIBLE, value == null ? true : Boolean.parseBoolean(value.toString()));
}
+ },
+ new Property(WINDOW_APPEARANCE) {
+ public void applyProperty(final CPlatformWindow c, final Object value) {
+ if (value != null && (value instanceof String)) {
+ c.execute(ptr -> nativeSetNSWindowAppearance(ptr, (String) value));
+ }
+ }
+ },
+ new Property(WINDOW_CORNER_RADIUS) {
+ public void applyProperty(final CPlatformWindow c, final Object value) {
+ c.setRoundedCorners(value);
+ }
}
}) {
@SuppressWarnings("deprecation")
@@ -350,17 +378,32 @@ public void initialize(Window _target, LWWindowPeer _peer, PlatformWindow _owner
if (owner != null) {
hasOwnerPtr = 0L != owner.executeGet(ownerPtr -> {
- ref.set(nativeCreateNSWindow(viewPtr, ownerPtr, styleBits,
- bounds.x, bounds.y,
- bounds.width, bounds.height));
+ if (logger.isLoggable(PlatformLogger.Level.FINE)) {
+ logger.fine("createNSWindow: owner=" + Long.toHexString(ownerPtr)
+ + ", styleBits=" + Integer.toHexString(styleBits)
+ + ", bounds=" + bounds);
+ }
+ long windowPtr = createNSWindow(viewPtr, ownerPtr, styleBits,
+ bounds.x, bounds.y, bounds.width, bounds.height);
+ if (logger.isLoggable(PlatformLogger.Level.FINE)) {
+ logger.fine("window created: " + Long.toHexString(windowPtr));
+ }
+ ref.set(windowPtr);
return 1;
});
}
if (!hasOwnerPtr) {
- ref.set(nativeCreateNSWindow(viewPtr, 0,
- styleBits, bounds.x, bounds.y,
- bounds.width, bounds.height));
+ if (logger.isLoggable(PlatformLogger.Level.FINE)) {
+ logger.fine("createNSWindow: styleBits=" + Integer.toHexString(styleBits)
+ + ", bounds=" + bounds);
+ }
+ long windowPtr = createNSWindow(viewPtr, 0, styleBits,
+ bounds.x, bounds.y, bounds.width, bounds.height);
+ if (logger.isLoggable(PlatformLogger.Level.FINE)) {
+ logger.fine("window created: " + Long.toHexString(windowPtr));
+ }
+ ref.set(windowPtr);
}
});
setPtr(ref.get());
@@ -397,18 +440,15 @@ CPlatformView createContentView() {
protected int getInitialStyleBits() {
// defaults style bits
- int styleBits = DECORATED | HAS_SHADOW | CLOSEABLE | MINIMIZABLE | ZOOMABLE | RESIZABLE | TITLE_VISIBLE;
+ int styleBits = DECORATED | HAS_SHADOW | CLOSEABLE | ZOOMABLE | RESIZABLE | TITLE_VISIBLE;
- if (isNativelyFocusableWindow()) {
- styleBits = SET(styleBits, SHOULD_BECOME_KEY, true);
- styleBits = SET(styleBits, SHOULD_BECOME_MAIN, true);
- }
+ styleBits |= getFocusableStyleBits();
final boolean isFrame = (target instanceof Frame);
final boolean isDialog = (target instanceof Dialog);
final boolean isPopup = (target.getType() == Window.Type.POPUP);
- if (isDialog) {
- styleBits = SET(styleBits, MINIMIZABLE, false);
+ if (isFrame) {
+ styleBits = SET(styleBits, MINIMIZABLE, true);
}
// Either java.awt.Frame or java.awt.Dialog can be undecorated, however java.awt.Window always is undecorated.
@@ -438,7 +478,6 @@ protected int getInitialStyleBits() {
if (isPopup) {
styleBits = SET(styleBits, TEXTURED, false);
// Popups in applets don't activate applet's process
- styleBits = SET(styleBits, NONACTIVATING, true);
styleBits = SET(styleBits, IS_POPUP, true);
}
@@ -536,9 +575,19 @@ protected int getInitialStyleBits() {
return styleBits;
}
- // this is the counter-point to -[CWindow _nativeSetStyleBit:]
private void setStyleBits(final int mask, final boolean value) {
- execute(ptr -> nativeSetNSWindowStyleBits(ptr, mask, value ? mask : 0));
+ setStyleBits(mask, value ? mask : 0);
+ }
+
+ // this is the counter-point to -[CWindow _nativeSetStyleBit:]
+ private void setStyleBits(final int mask, final int value) {
+ execute(ptr -> {
+ if (logger.isLoggable(PlatformLogger.Level.FINE)) {
+ logger.fine("nativeSetNSWindowStyleBits: window=" + Long.toHexString(ptr)
+ + ", mask=" + Integer.toHexString(mask) + ", value=" + Integer.toHexString(value));
+ }
+ nativeSetNSWindowStyleBits(ptr, mask, value);
+ });
}
private native void _toggleFullScreenMode(final long model);
@@ -603,6 +652,18 @@ public SurfaceData replaceSurfaceData() {
return contentView.replaceSurfaceData();
}
+ public void displayChanged(boolean profileOnly) {
+ if (logger.isLoggable(PlatformLogger.Level.FINE)) {
+ logger.fine(profileOnly ? "DISPLAY_PROFILE_CHANGED" : "DISPLAY_CHANGED");
+ }
+
+ if (peer != null && !profileOnly) {
+ EventQueue.invokeLater(
+ () -> ((SunGraphicsEnvironment) GraphicsEnvironment.
+ getLocalGraphicsEnvironment()).displayChanged());
+ }
+ }
+
@Override // PlatformWindow
public void setBounds(int x, int y, int w, int h) {
execute(ptr -> nativeSetNSWindowBounds(ptr, x, y, w, h));
@@ -655,6 +716,16 @@ public boolean isVisible() {
return this.visible;
}
+ private static LWWindowPeer getBlockerFor(Window window) {
+ if (window != null) {
+ ComponentPeer peer = AWTAccessor.getComponentAccessor().getPeer(window);
+ if (peer instanceof LWWindowPeer) {
+ return ((LWWindowPeer)peer).getBlocker();
+ }
+ }
+ return null;
+ }
+
@Override // PlatformWindow
public void setVisible(boolean visible) {
// Configure stuff
@@ -705,52 +776,58 @@ public void setVisible(boolean visible) {
}
}
+ this.visible = visible;
+
// Actually show or hide the window
LWWindowPeer blocker = (peer == null)? null : peer.getBlocker();
- if (blocker == null || !visible) {
+ if (!visible) {
+ execute(ptr -> AWTThreading.executeWaitToolkit(wait -> nativeHideWindow(ptr, wait)));
+ } else if (delayShowing()) {
+ if (blocker == null) {
+ Window focusedWindow = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusedWindow();
+ LWWindowPeer focusedWindowBlocker = getBlockerFor(focusedWindow);
+ if (focusedWindowBlocker == peer) {
+ // try to switch to target space if we're adding a modal dialog
+ // that would block currently focused window
+ owner.execute(CWrapper.NSWindow::orderFront);
+ }
+ }
+ } else if (blocker == null) {
// If it ain't blocked, or is being hidden, go regular way
- if (visible) {
- contentView.execute(viewPtr -> {
- execute(ptr -> CWrapper.NSWindow.makeFirstResponder(ptr,
- viewPtr));
- });
+ contentView.execute(viewPtr -> {
+ execute(ptr -> CWrapper.NSWindow.makeFirstResponder(ptr,
+ viewPtr));
+ });
- boolean isPopup = (target.getType() == Window.Type.POPUP);
- execute(ptr -> {
- if (isPopup) {
- // Popups in applets don't activate applet's process
- CWrapper.NSWindow.orderFrontRegardless(ptr);
- } else {
- CWrapper.NSWindow.orderFront(ptr);
- }
+ boolean isPopup = (target.getType() == Window.Type.POPUP);
+ execute(ptr -> {
+ if (isPopup) {
+ // Popups in applets don't activate applet's process
+ CWrapper.NSWindow.orderFrontRegardless(ptr);
+ } else {
+ CWrapper.NSWindow.orderFront(ptr);
+ }
- boolean isKeyWindow = CWrapper.NSWindow.isKeyWindow(ptr);
- if (!isKeyWindow) {
- CWrapper.NSWindow.makeKeyWindow(ptr);
- }
+ boolean isKeyWindow = CWrapper.NSWindow.isKeyWindow(ptr);
+ if (!isKeyWindow) {
+ logger.fine("setVisible: makeKeyWindow");
+ CWrapper.NSWindow.makeKeyWindow(ptr);
+ }
- if (owner != null
- && owner.getPeer() instanceof LWLightweightFramePeer) {
- LWLightweightFramePeer peer =
- (LWLightweightFramePeer) owner.getPeer();
-
- long ownerWindowPtr = peer.getOverriddenWindowHandle();
- if (ownerWindowPtr != 0) {
- //Place window above JavaFX stage
- CWrapper.NSWindow.addChildWindow(
- ownerWindowPtr, ptr,
- CWrapper.NSWindow.NSWindowAbove);
- }
+ if (owner != null
+ && owner.getPeer() instanceof LWLightweightFramePeer) {
+ LWLightweightFramePeer peer =
+ (LWLightweightFramePeer) owner.getPeer();
+
+ long ownerWindowPtr = peer.getOverriddenWindowHandle();
+ if (ownerWindowPtr != 0) {
+ //Place window above JavaFX stage
+ CWrapper.NSWindow.addChildWindow(
+ ownerWindowPtr, ptr,
+ CWrapper.NSWindow.NSWindowAbove);
}
- });
- } else {
- execute(ptr->{
- // immediately hide the window
- CWrapper.NSWindow.orderOut(ptr);
- // process the close
- CWrapper.NSWindow.close(ptr);
- });
- }
+ }
+ });
} else {
// otherwise, put it in a proper z-order
CPlatformWindow bw
@@ -763,7 +840,6 @@ public void setVisible(boolean visible) {
});
});
}
- this.visible = visible;
nativeSynthesizeMouseEnteredExitedEvents();
@@ -773,7 +849,7 @@ public void setVisible(boolean visible) {
// Manage parent-child relationship when showing
final ComponentAccessor acc = AWTAccessor.getComponentAccessor();
- if (visible) {
+ if (visible && !delayShowing()) {
// Order myself above my parent
if (owner != null && owner.isVisible()) {
owner.execute(ownerPtr -> {
@@ -805,7 +881,10 @@ public void setVisible(boolean visible) {
// Deal with the blocker of the window being shown
if (blocker != null && visible) {
// Make sure the blocker is above its siblings
- ((CPlatformWindow)blocker.getPlatformWindow()).orderAboveSiblings();
+ CPlatformWindow blockerWindow = (CPlatformWindow) blocker.getPlatformWindow();
+ if (!blockerWindow.delayShowing()) {
+ blockerWindow.orderAboveSiblings();
+ }
}
}
@@ -840,6 +919,7 @@ public void toBack() {
@Override // PlatformWindow
public void toFront() {
+ if (delayShowing()) return;
LWCToolkit lwcToolkit = (LWCToolkit) Toolkit.getDefaultToolkit();
Window w = DefaultKeyboardFocusManager.getCurrentKeyboardFocusManager().getActiveWindow();
final ComponentAccessor acc = AWTAccessor.getComponentAccessor();
@@ -849,7 +929,7 @@ public void toFront() {
lwcToolkit.activateApplicationIgnoringOtherApps();
}
updateFocusabilityForAutoRequestFocus(false);
- execute(CPlatformWindow::nativePushNSWindowToFront);
+ execute(ptr -> AWTThreading.executeWaitToolkit(wait -> nativePushNSWindowToFront(ptr, wait)));
updateFocusabilityForAutoRequestFocus(true);
}
@@ -893,6 +973,7 @@ public boolean rejectFocusRequest(FocusEvent.Cause cause) {
@Override
public boolean requestWindowFocus() {
+ if (delayShowing()) return false;
execute(ptr -> {
if (CWrapper.NSWindow.canBecomeMainWindow(ptr)) {
CWrapper.NSWindow.makeMainWindow(ptr);
@@ -911,10 +992,28 @@ public boolean isActive() {
return ref.get();
}
+ private boolean isTabbedWindow() {
+ AtomicBoolean ref = new AtomicBoolean();
+ execute(ptr -> {
+ ref.set(CWrapper.NSWindow.isTabbedWindow(ptr));
+ });
+ return ref.get();
+ }
+
+ // We want a window to be always shown at the same space as its owning window.
+ // But macOS doesn't have an API to control the target space for a window -
+ // it's always shown at the active space. So if the target space isn't active now,
+ // the only way to achieve our goal seems to be delaying the appearance of the
+ // window till the target space becomes active.
+ private boolean delayShowing() {
+ AtomicBoolean ref = new AtomicBoolean(false);
+ execute(ptr -> ref.set(nativeDelayShowing(ptr)));
+ return ref.get();
+ }
+
@Override
public void updateFocusableWindowState() {
- final boolean isFocusable = isNativelyFocusableWindow();
- setStyleBits(SHOULD_BECOME_KEY | SHOULD_BECOME_MAIN, isFocusable); // set both bits at once
+ setStyleBits(SHOULD_BECOME_KEY | SHOULD_BECOME_MAIN, getFocusableStyleBits()); // set both bits at once
}
@Override
@@ -1055,8 +1154,12 @@ public void setModalBlocked(boolean blocked) {
execute(ptr -> nativeSynthesizeMouseEnteredExitedEvents(ptr, CocoaConstants.NSEventTypeMouseExited));
}
- execute(ptr -> nativeSetEnabled(ptr, !blocked));
- checkBlockingAndOrder();
+ execute(ptr -> {
+ if (logger.isLoggable(PlatformLogger.Level.FINE)) {
+ logger.fine("nativeSetEnabled: window=" + Long.toHexString(ptr) + ", enabled=" + !blocked);
+ }
+ nativeSetEnabled(ptr, !blocked);
+ });
}
public final void invalidateShadow() {
@@ -1136,16 +1239,14 @@ static long getNativeViewPtr(PlatformWindow platformWindow) {
* Callbacks from the AWTWindow and AWTView objc classes.
*************************************************************/
private void deliverWindowFocusEvent(boolean gained, CPlatformWindow opposite){
- // Fix for 7150349: ignore "gained" notifications when the app is inactive.
- if (gained && !((LWCToolkit)Toolkit.getDefaultToolkit()).isApplicationActive()) {
- focusLogger.fine("the app is inactive, so the notification is ignored");
- return;
- }
-
LWWindowPeer oppositePeer = (opposite == null)? null : opposite.getPeer();
responder.handleWindowFocusEvent(gained, oppositePeer);
}
+ public void doDeliverMoveResizeEvent() {
+ execute(ptr -> nativeCallDeliverMoveResizeEvent(ptr));
+ }
+
protected void deliverMoveResizeEvent(int x, int y, int width, int height,
boolean byUser) {
AtomicBoolean ref = new AtomicBoolean();
@@ -1205,21 +1306,13 @@ private void deliverNCMouseDown() {
}
}
- /*
- * Our focus model is synthetic and only non-simple window
- * may become natively focusable window.
- */
- private boolean isNativelyFocusableWindow() {
- if (peer == null) {
- return false;
- }
-
- return !peer.isSimpleWindow() && target.getFocusableWindowState();
- }
-
- private boolean isBlocked() {
- LWWindowPeer blocker = (peer != null) ? peer.getBlocker() : null;
- return (blocker != null);
+ // returns a combination of SHOULD_BECOME_KEY/SHOULD_BECOME_MAIN relevant for the current window
+ private int getFocusableStyleBits() {
+ return (peer == null || target == null || !target.isFocusableWindow())
+ ? 0
+ : peer.isSimpleWindow()
+ ? SHOULD_BECOME_KEY
+ : SHOULD_BECOME_KEY | SHOULD_BECOME_MAIN;
}
/*
@@ -1228,30 +1321,39 @@ private boolean isBlocked() {
* circumstances.
*/
private void updateFocusabilityForAutoRequestFocus(boolean isFocusable) {
- if (target.isAutoRequestFocus() || !isNativelyFocusableWindow()) return;
- setStyleBits(SHOULD_BECOME_KEY | SHOULD_BECOME_MAIN, isFocusable); // set both bits at once
+ if (target.isAutoRequestFocus()) return;
+ int focusableStyleBits = getFocusableStyleBits();
+ if (focusableStyleBits == 0) return;
+ setStyleBits(SHOULD_BECOME_KEY | SHOULD_BECOME_MAIN,
+ isFocusable ? focusableStyleBits : 0); // set both bits at once
}
- private boolean checkBlockingAndOrder() {
+ private void checkBlockingAndOrder() {
LWWindowPeer blocker = (peer == null)? null : peer.getBlocker();
if (blocker == null) {
- return false;
+ // If it's not blocked, make sure it's above its siblings
+ orderAboveSiblings();
+ return;
}
if (blocker instanceof CPrinterDialogPeer) {
- return true;
+ return;
}
CPlatformWindow pWindow = (CPlatformWindow)blocker.getPlatformWindow();
- pWindow.orderAboveSiblings();
+ if (!pWindow.delayShowing()) {
+ pWindow.orderAboveSiblings();
- pWindow.execute(ptr -> {
- CWrapper.NSWindow.orderFrontRegardless(ptr);
- CWrapper.NSWindow.makeKeyAndOrderFront(ptr);
- CWrapper.NSWindow.makeMainWindow(ptr);
- });
- return true;
+ pWindow.execute(ptr -> {
+ if (logger.isLoggable(PlatformLogger.Level.FINE)) {
+ logger.fine("Focus blocker " + Long.toHexString(ptr));
+ }
+ CWrapper.NSWindow.orderFrontRegardless(ptr);
+ CWrapper.NSWindow.makeKeyAndOrderFront(ptr);
+ CWrapper.NSWindow.makeMainWindow(ptr);
+ });
+ }
}
private boolean isIconified() {
@@ -1289,7 +1391,9 @@ private void orderAboveSiblings() {
// which is going to become 'main window', are placed above their siblings.
CPlatformWindow rootOwner = getRootOwner();
if (rootOwner.isVisible() && !rootOwner.isIconified() && !rootOwner.isActive()) {
- rootOwner.execute(CWrapper.NSWindow::orderFront);
+ if (rootOwner != this || !isTabbedWindow()) {
+ rootOwner.execute(CWrapper.NSWindow::orderFrontIfOnActiveSpace);
+ }
}
// Do not order child windows of iconified owner.
@@ -1313,7 +1417,7 @@ private void orderAboveSiblingsImpl(Window[] windows) {
if (p instanceof LWWindowPeer) {
CPlatformWindow pw = (CPlatformWindow)((LWWindowPeer)p).getPlatformWindow();
iconified = isIconified();
- if (pw != null && pw.isVisible() && !iconified) {
+ if (pw != null && pw.isVisible() && !iconified && !pw.delayShowing()) {
// If the window is one of ancestors of 'main window' or is going to become main by itself,
// the window should be ordered above its siblings; otherwise the window is just ordered
// above its nearest parent.
@@ -1325,7 +1429,7 @@ private void orderAboveSiblingsImpl(Window[] windows) {
}
pwUnder.execute(underPtr -> {
pw.execute(ptr -> {
- CWrapper.NSWindow.orderWindow(ptr, CWrapper.NSWindow.NSWindowAbove, underPtr);
+ CWrapper.NSWindow.orderWindowIfOnActiveSpace(ptr, CWrapper.NSWindow.NSWindowAbove, underPtr);
});
});
pwUnder = pw;
@@ -1349,10 +1453,10 @@ private void orderAboveSiblingsImpl(Window[] windows) {
}
protected void applyWindowLevel(Window target) {
- if (target.isAlwaysOnTop() && target.getType() != Window.Type.POPUP) {
- execute(ptr->CWrapper.NSWindow.setLevel(ptr, CWrapper.NSWindow.NSFloatingWindowLevel));
- } else if (target.getType() == Window.Type.POPUP) {
- execute(ptr->CWrapper.NSWindow.setLevel(ptr, CWrapper.NSWindow.NSPopUpMenuWindowLevel));
+ boolean popup = target.getType() == Window.Type.POPUP;
+ boolean alwaysOnTop = target.isAlwaysOnTop();
+ if (popup || alwaysOnTop || owner != null) {
+ execute(ptr -> nativeRaiseLevel(ptr, popup, !popup && !alwaysOnTop));
}
}
@@ -1370,6 +1474,18 @@ private boolean isSimpleWindowOwnedByEmbeddedFrame() {
}
return false;
}
+
+ private long createNSWindow(long nsViewPtr,
+ long ownerPtr,
+ long styleBits,
+ double x,
+ double y,
+ double w,
+ double h) {
+ return AWTThreading.executeWaitToolkit(() ->
+ nativeCreateNSWindow(nsViewPtr, ownerPtr, styleBits, x, y, w, h));
+ }
+
// ----------------------------------------------------------------------
// NATIVE CALLBACKS
// ----------------------------------------------------------------------
@@ -1380,9 +1496,7 @@ private void windowWillMiniaturize() {
private void windowDidBecomeMain() {
lastBecomeMainTime = System.currentTimeMillis();
- if (checkBlockingAndOrder()) return;
- // If it's not blocked, make sure it's above its siblings
- orderAboveSiblings();
+ checkBlockingAndOrder();
}
private void windowWillEnterFullScreen() {
@@ -1402,4 +1516,37 @@ private void windowDidExitFullScreen() {
isInFullScreen = false;
isFullScreenAnimationOn = false;
}
+
+ // JBR API internals
+ private static void updateCustomTitleBar(ComponentPeer peer) {
+ if (peer instanceof LWWindowPeer lwwp &&
+ lwwp.getPlatformWindow() instanceof CPlatformWindow cpw) {
+ cpw.execute(CPlatformWindow::nativeUpdateCustomTitleBar);
+ }
+ }
+
+ // JBR API internals
+ private static void setRoundedCorners(Window window, Object params) {
+ Object peer = AWTAccessor.getComponentAccessor().getPeer(window);
+ if (peer instanceof CPlatformWindow) {
+ ((CPlatformWindow)peer).setRoundedCorners(params);
+ } else if (window instanceof RootPaneContainer) {
+ JRootPane rootpane = ((RootPaneContainer)window).getRootPane();
+ if (rootpane != null) {
+ rootpane.putClientProperty(WINDOW_CORNER_RADIUS, params);
+ }
+ }
+ }
+
+ private void setRoundedCorners(Object params) {
+ if (params instanceof Float) {
+ execute(ptr -> nativeSetRoundedCorners(ptr, (float) params, 0, 0));
+ } else if (params instanceof Object[]) {
+ Object[] values = (Object[]) params;
+ if (values.length == 3 && values[0] instanceof Float && values[1] instanceof Integer && values[2] instanceof Color) {
+ Color color = (Color) values[2];
+ execute(ptr -> nativeSetRoundedCorners(ptr, (float) values[0], (int) values[1], color.getRGB()));
+ }
+ }
+ }
}
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPrinterJob.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPrinterJob.java
index 407af09810c5..e90416ec242d 100644
--- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPrinterJob.java
+++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CPrinterJob.java
@@ -30,6 +30,7 @@
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.awt.print.*;
+import java.io.File;
import java.net.URI;
import java.security.AccessController;
import java.security.PrivilegedAction;
@@ -185,6 +186,15 @@ protected void setAttributes(PrintRequestAttributeSet attributes) throws Printer
if (attributes == null) {
return;
}
+ if (getPrintService() == null && isPrintToFile) {
+ Destination destination = (Destination)attributes.get(Destination.class);
+ if (destination != null) {
+ try {
+ destinationAttr = "" + new File(destination.getURI().getSchemeSpecificPart());
+ } catch (Exception e) {
+ }
+ }
+ }
Attribute attr = attributes.get(Media.class);
if (attr instanceof CustomMediaTray) {
CustomMediaTray customTray = (CustomMediaTray) attr;
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CRobot.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CRobot.java
index 5dba06cb612b..ab59a6a3d423 100644
--- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CRobot.java
+++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CRobot.java
@@ -30,10 +30,12 @@
import java.awt.peer.RobotPeer;
import sun.awt.CGraphicsDevice;
+import sun.security.action.GetIntegerAction;
final class CRobot implements RobotPeer {
private static final int MOUSE_LOCATION_UNKNOWN = -1;
+ private static final int DEFAULT_SAFE_DELAY_MILLIS = 50;
private final CGraphicsDevice fDevice;
private int mouseLastX = MOUSE_LOCATION_UNKNOWN;
@@ -50,7 +52,12 @@ final class CRobot implements RobotPeer {
*/
CRobot(CGraphicsDevice d) {
fDevice = d;
- initRobot();
+ int safeDelayMillis = GetIntegerAction.privilegedGetProperty(
+ "sun.awt.osx.RobotSafeDelayMillis", DEFAULT_SAFE_DELAY_MILLIS);
+ if (safeDelayMillis < 0) {
+ safeDelayMillis = DEFAULT_SAFE_DELAY_MILLIS;
+ }
+ initRobot(safeDelayMillis);
}
/**
@@ -187,7 +194,7 @@ public int getRGBPixel(int x, int y) {
return c;
}
- private native void initRobot();
+ private native void initRobot(int safeDelayMillis);
private native void mouseEvent(int lastX, int lastY, int buttonsState,
boolean isButtonsDownState,
boolean isMouseMove);
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CTextPipe.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CTextPipe.java
index 6aa36453ac0f..e54e5be9f10c 100644
--- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CTextPipe.java
+++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CTextPipe.java
@@ -81,32 +81,26 @@ public void drawString(final SunGraphics2D sg2d, final String s, final double x,
}
}
- private boolean hasSlotData(GlyphVector gv) {
+ private boolean hasSlotData(GlyphVector gv, Font2D font2D) {
final int length = gv.getNumGlyphs();
- for (int i = 0; i < length; i++) {
- if ((gv.getGlyphCode(i) & CompositeGlyphMapper.SLOTMASK) != 0) {
- return true;
+ if (length > 0) {
+ int slotMask = font2D
+ .getSlotInfoForGlyph(gv.getGlyphCode(0))
+ .getSlotMask();
+ for (int i = 0; i < length; i++) {
+ int glyphCode = gv.getGlyphCode(i);
+ if ((glyphCode & slotMask) != 0) {
+ return true;
+ }
}
}
return false;
}
- private Font getSlotFont(Font font, int slot) {
- Font2D f2d = FontUtilities.getFont2D(font);
- if (f2d instanceof CFont) {
- CompositeFont cf = ((CFont)f2d).getCompositeFont2D();
- PhysicalFont pf = cf.getSlotFont(slot);
- Font f = new Font(pf.getFontName(null),
- font.getStyle(), font.getSize());
- return f;
- }
- return null;
- }
-
- private GlyphVector getGlyphVectorWithRange(final Font font, final GlyphVector gV, int start, int count) {
+ private GlyphVector getGlyphVectorWithRange(final Font font, final GlyphVector gV, int start, int count, int slotShift) {
int[] glyphs = new int[count];
for (int i = 0; i < count; i++) {
- glyphs[i] = gV.getGlyphCode(start+i) & CompositeGlyphMapper.GLYPHMASK;
+ glyphs[i] = gV.getGlyphCode(start+i) >>> slotShift;
}
// Positions should be null to recalculate by native methods,
// if GV was segmented.
@@ -119,11 +113,11 @@ private GlyphVector getGlyphVectorWithRange(final Font font, final GlyphVector g
return sgv;
}
- private int getLengthOfSameSlot(final GlyphVector gV, final int targetSlot, final int start, final int length) {
+ private int getLengthOfSameSlot(final GlyphVector gV, final int targetSlot, final int slotMask,
+ final int start, final int length) {
int count = 1;
for (; start + count < length; count++) {
- int slot = (gV.getGlyphCode(start + count) &
- CompositeGlyphMapper.SLOTMASK) >> 24;
+ int slot = gV.getGlyphCode(start + count) & slotMask;
if (targetSlot != slot) {
break;
}
@@ -143,25 +137,33 @@ private void drawGlyphVectorImpl(final SunGraphics2D sg2d, final GlyphVector gV,
public void drawGlyphVector(final SunGraphics2D sg2d, final GlyphVector gV, final float x, final float y) {
final Font prevFont = sg2d.getFont();
- sg2d.setFont(gV.getFont());
+ Font gvFont = gV.getFont();
+ Font2D f2d = FontUtilities.getFont2D(gvFont);
+ if (f2d instanceof FontSubstitution fs) {
+ f2d = fs.getCompositeFont2D();
+ }
- if (hasSlotData(gV)) {
+ if (hasSlotData(gV, f2d)) {
final int length = gV.getNumGlyphs();
float[] positions = gV.getGlyphPositions(0, length, null);
int start = 0;
while (start < length) {
- int slot = (gV.getGlyphCode(start) &
- CompositeGlyphMapper.SLOTMASK) >> 24;
- sg2d.setFont(getSlotFont(gV.getFont(), slot));
- int count = getLengthOfSameSlot(gV, slot, start, length);
+ int glyphCode = gV.getGlyphCode(start);
+ Font2D.SlotInfo slotInfo = f2d.getSlotInfoForGlyph(glyphCode);
+ int slotMask = slotInfo.getSlotMask();
+ int slot = glyphCode & slotMask;
+ sg2d.setFont(new Font(slotInfo.font.getFontName(null),
+ gvFont.getStyle(), gvFont.getSize()));
+ int count = getLengthOfSameSlot(gV, slot, slotMask, start, length);
GlyphVector rangeGV = getGlyphVectorWithRange(sg2d.getFont(),
- gV, start, count);
+ gV, start, count, slotInfo.slotShift);
drawGlyphVectorImpl(sg2d, rangeGV,
x + positions[start * 2],
y + positions[start * 2 + 1]);
start += count;
}
} else {
+ sg2d.setFont(gvFont);
drawGlyphVectorImpl(sg2d, gV, x, y);
}
sg2d.setFont(prevFont);
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CThreading.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CThreading.java
new file mode 100644
index 000000000000..d7ab98a8fc8c
--- /dev/null
+++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CThreading.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package sun.lwawt.macosx;
+
+import sun.lwawt.macosx.concurrent.Dispatch;
+
+import java.awt.EventQueue;
+import java.awt.AWTError;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.FutureTask;
+
+@SuppressWarnings("removal")
+public class CThreading {
+ static String APPKIT_THREAD_NAME = "AWT-AppKit";
+
+ static boolean isEventQueue() {
+ return EventQueue.isDispatchThread();
+ }
+
+ private static native boolean isMainThread();
+
+ public static boolean isAppKit() {
+ if (APPKIT_THREAD_NAME.equals(Thread.currentThread().getName())) return true;
+
+ if (isMainThread()) {
+ Thread.currentThread().setName(APPKIT_THREAD_NAME);
+ return true;
+ }
+ return false;
+ }
+
+
+ static boolean assertEventQueue() {
+ final boolean isEventQueue = isEventQueue();
+ assert isEventQueue : "Threading violation: not EventQueue thread";
+ return isEventQueue;
+ }
+
+ static boolean assertNotEventQueue() {
+ final boolean isNotEventQueue = isEventQueue();
+ assert isNotEventQueue : "Threading violation: EventQueue thread";
+ return isNotEventQueue;
+ }
+
+ static boolean assertAppKit() {
+ final boolean isAppKitThread = isAppKit();
+ assert isAppKitThread : "Threading violation: not AppKit thread";
+ return isAppKitThread;
+ }
+
+ static boolean assertNotAppKit() {
+ final boolean isNotAppKitThread = !isAppKit();
+ assert isNotAppKitThread : "Threading violation: AppKit thread";
+ return isNotAppKitThread;
+ }
+
+ public static V executeOnAppKit(final Callable command) throws Throwable {
+ if (!isAppKit()) {
+ Dispatch dispatch = Dispatch.getInstance();
+
+ if (dispatch == null) {
+ throw new AWTError("Could not get Dispatch object");
+ }
+
+ Callable commandWithTNameFix = () -> {
+ if (!APPKIT_THREAD_NAME.equals(Thread.currentThread().getName())) {
+ Thread.currentThread().setName(APPKIT_THREAD_NAME);
+ }
+
+ return command.call();
+ };
+
+ FutureTask future = new FutureTask<>(commandWithTNameFix);
+
+ dispatch.getNonBlockingMainQueueExecutor().execute(future);
+
+ try {
+ return future.get();
+ } catch (InterruptedException e) {
+ throw new AWTError(e.getMessage());
+ } catch (ExecutionException e) {
+ throw e.getCause();
+ }
+ } else
+ return command.call();
+ }
+
+ public static V privilegedExecuteOnAppKit(Callable command)
+ throws Exception {
+ try {
+ return java.security.AccessController.doPrivileged(
+ (PrivilegedExceptionAction) () -> {
+ //noinspection TryWithIdenticalCatches
+ try {
+ return executeOnAppKit(command);
+ } catch (RuntimeException e) {
+ throw e;
+ } catch (Error e) {
+ throw e;
+ } catch (Throwable throwable) {
+ throw new Exception(throwable);
+ }
+ }
+ );
+ } catch (PrivilegedActionException e) {
+ throw e.getException();
+ }
+ }
+
+ public static void executeOnAppKit(Runnable command) {
+ if (!isAppKit()) {
+ Dispatch dispatch = Dispatch.getInstance();
+
+ if (dispatch != null) {
+ dispatch.getNonBlockingMainQueueExecutor().execute(command);
+ }
+ else {
+ throw new AWTError("Could not get Dispatch object");
+ }
+ } else
+ command.run();
+ }
+}
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CWarningWindow.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CWarningWindow.java
index bcb18c76ae10..52e85e338415 100644
--- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CWarningWindow.java
+++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CWarningWindow.java
@@ -245,7 +245,7 @@ public void notifyMouseWheelEvent(long when, int x, int y, int absX,
@Override
public void notifyKeyEvent(int id, long when, int modifiers, int keyCode,
- char keyChar, int keyLocation, int jextendedkeyCode) {
+ char keyChar, int keyLocation) {
}
protected int getInitialStyleBits() {
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CWrapper.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CWrapper.java
index 516b8de15506..1baecf34555b 100644
--- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/CWrapper.java
+++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/CWrapper.java
@@ -25,6 +25,8 @@
package sun.lwawt.macosx;
+import sun.awt.AWTThreading;
+
final class CWrapper {
private CWrapper() { }
@@ -45,22 +47,34 @@ static final class NSWindow {
// 'level' is one of the keys defined above
static native void setLevel(long window, int level);
- static native void makeKeyAndOrderFront(long window);
- static native void makeKeyWindow(long window);
+ static void makeKeyAndOrderFront(long window) {
+ AWTThreading.executeWaitToolkit(wait -> nativeMakeKeyAndOrderFront(window, wait));
+ }
+ static void makeKeyWindow(long window) {
+ AWTThreading.executeWaitToolkit(wait -> nativeMakeKeyWindow(window, wait));
+ }
static native void makeMainWindow(long window);
static native boolean canBecomeMainWindow(long window);
static native boolean isKeyWindow(long window);
static native void orderFront(long window);
+ static native void orderFrontIfOnActiveSpace(long window);
static native void orderFrontRegardless(long window);
static native void orderWindow(long window, int ordered, long relativeTo);
+ static native void orderWindowIfOnActiveSpace(long window, int ordered, long relativeTo);
/**
* Removes the window from the screen.
*
* @param window the pointer of the NSWindow
*/
- static native void orderOut(long window);
+ static void orderOut(long window) {
+ AWTThreading.executeWaitToolkit(wait -> nativeOrderOut(window, wait));
+ }
+
+ private static native void nativeOrderOut(long window, boolean wait);
+ private static native void nativeMakeKeyWindow(long window, boolean wait);
+ private static native void nativeMakeKeyAndOrderFront(long window, boolean wait);
/**
* Removes the window from the screen and releases it. According to
@@ -94,6 +108,8 @@ static final class NSWindow {
static native void zoom(long window);
static native void makeFirstResponder(long window, long responder);
+
+ static native boolean isTabbedWindow(long window);
}
static final class NSView {
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/LWCToolkit.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/LWCToolkit.java
index d430825bb856..6b33110aa6dc 100644
--- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/LWCToolkit.java
+++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/LWCToolkit.java
@@ -85,27 +85,19 @@
import java.awt.peer.TrayIconPeer;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.UndeclaredThrowableException;
-import java.net.MalformedURLException;
import java.net.URL;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
-import java.util.HashMap;
-import java.util.Locale;
-import java.util.Map;
-import java.util.MissingResourceException;
-import java.util.Objects;
-import java.util.ResourceBundle;
+import java.security.*;
+import java.util.*;
import java.util.concurrent.Callable;
-
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+import java.net.MalformedURLException;
import javax.swing.UIManager;
import com.apple.laf.AquaMenuBarUI;
-import sun.awt.AWTAccessor;
-import sun.awt.AppContext;
-import sun.awt.CGraphicsDevice;
-import sun.awt.LightweightFrame;
-import sun.awt.PlatformGraphicsInfo;
-import sun.awt.SunToolkit;
+import sun.awt.*;
import sun.awt.datatransfer.DataTransferer;
import sun.awt.dnd.SunDragSourceContextPeer;
import sun.awt.util.ThreadGroupUtils;
@@ -120,6 +112,8 @@
import sun.lwawt.PlatformDropTarget;
import sun.lwawt.PlatformWindow;
import sun.lwawt.SecurityWarningWindow;
+import sun.security.action.GetBooleanAction;
+import sun.util.logging.PlatformLogger;
@SuppressWarnings("serial") // JDK implementation class
final class NamedCursor extends Cursor {
@@ -143,6 +137,68 @@ public final class LWCToolkit extends LWToolkit {
private static native void initAppkit(ThreadGroup appKitThreadGroup, boolean headless);
private static CInputMethodDescriptor sInputMethodDescriptor;
+ private static native boolean switchKeyboardLayoutNative(String layoutName);
+
+ private static native String getKeyboardLayoutNativeId();
+
+ private static native String[] getKeyboardLayoutListNative(boolean includeAll);
+
+ private static native boolean setKeyboardLayoutEnabledNative(String layoutName, boolean enabled);
+
+ public static void switchKeyboardLayout (String layoutName) {
+ if (layoutName == null || layoutName.isEmpty()) {
+ throw new RuntimeException("A valid layout ID is expected. Found: " + layoutName);
+ }
+ if (!switchKeyboardLayoutNative(layoutName)) {
+ throw new RuntimeException("Couldn't switch layout to " + layoutName);
+ }
+ }
+
+ public static String getKeyboardLayoutId () {
+ return getKeyboardLayoutNativeId();
+ }
+
+ public static List getKeyboardLayoutList(boolean includeAll) {
+ String[] result = getKeyboardLayoutListNative(includeAll);
+ if (result == null) {
+ return new ArrayList<>();
+ }
+ return Arrays.asList(result);
+ }
+
+ public static void enableKeyboardLayout(String layoutName) {
+ if (layoutName == null || layoutName.isEmpty()) {
+ throw new RuntimeException("A valid layout ID is expected. Found: " + layoutName);
+ }
+ if (!setKeyboardLayoutEnabledNative(layoutName, true)) {
+ throw new RuntimeException("Couldn't enable layout " + layoutName);
+ }
+ }
+
+ public static void disableKeyboardLayout(String layoutName) {
+ if (layoutName == null || layoutName.isEmpty()) {
+ throw new RuntimeException("A valid layout ID is expected. Found: " + layoutName);
+ }
+ if (!setKeyboardLayoutEnabledNative(layoutName, false)) {
+ throw new RuntimeException("Couldn't disable layout " + layoutName);
+ }
+ }
+
+ public static boolean isKeyboardLayoutEnabled(String layoutName) {
+ return getKeyboardLayoutList(false).contains(layoutName);
+ }
+
+ public static boolean isKeyboardLayoutInstalled(String layoutName) {
+ return getKeyboardLayoutList(true).contains(layoutName);
+ }
+
+ // Listens to EDT state in invokeAndWait() and disposes the invocation event
+ // when EDT becomes free but the invocation event is not yet dispatched (considered lost).
+ // This prevents a deadlock and makes the invocation return some default result.
+ @SuppressWarnings("removal")
+ private static final boolean DISPOSE_INVOCATION_ON_EDT_FREE =
+ AccessController.doPrivileged(new GetBooleanAction("sun.lwawt.macosx.LWCToolkit.invokeAndWait.disposeOnEDTFree"));
+
static {
System.err.flush();
@@ -192,6 +248,8 @@ public Boolean run() {
}
});
+ private static final PlatformLogger log = PlatformLogger.getLogger(LWCToolkit.class.getName());
+
@SuppressWarnings("removal")
public LWCToolkit() {
final String extraButtons = "sun.awt.enableExtraMouseButtons";
@@ -501,15 +559,16 @@ public int getScreenResolution() throws HeadlessException {
public Insets getScreenInsets(final GraphicsConfiguration gc) {
GraphicsDevice gd = gc.getDevice();
if (!(gd instanceof CGraphicsDevice)) {
- return super.getScreenInsets(gc);
+ return AWTThreading.executeWaitToolkit(() -> super.getScreenInsets(gc));
}
- return ((CGraphicsDevice)gd).getScreenInsets();
+ CGraphicsDevice cgd = (CGraphicsDevice) gd;
+ return cgd.getScreenInsets();
}
@Override
public void sync() {
// flush the rendering pipeline
- if (CGraphicsDevice.usingMetalPipeline()) {
+ if (CGraphicsEnvironment.usingMetalPipeline()) {
MTLRenderQueue.sync();
} else {
OGLRenderQueue.sync();
@@ -529,6 +588,8 @@ public RobotPeer createRobot(GraphicsDevice screen) throws AWTException {
private native boolean isCapsLockOn();
+ private static native boolean setCapsLockState(boolean on);
+
/*
* NOTE: Among the keys this method is supposed to check,
* only Caps Lock works as a true locking key with OS X.
@@ -555,6 +616,25 @@ public boolean getLockingKeyState(int keyCode) throws UnsupportedOperationExcept
}
}
+ @Override
+ public void setLockingKeyState(int keyCode, boolean on) throws UnsupportedOperationException {
+ switch (keyCode) {
+ case KeyEvent.VK_NUM_LOCK:
+ case KeyEvent.VK_SCROLL_LOCK:
+ case KeyEvent.VK_KANA_LOCK:
+ throw new UnsupportedOperationException("Toolkit.setLockingKeyState");
+
+ case KeyEvent.VK_CAPS_LOCK:
+ if (!setCapsLockState(on)) {
+ throw new RuntimeException("failed to set caps lock state");
+ }
+ break;
+
+ default:
+ throw new IllegalArgumentException("invalid key for Toolkit.setLockingKeyState");
+ }
+ }
+
//Is it allowed to generate events assigned to extra mouse buttons.
//Set to true by default.
private static boolean areExtraMouseButtonsEnabled = true;
@@ -681,8 +761,12 @@ public void run() { synchronized(ret) {
public static T invokeAndWait(final Callable callable,
Component component) throws Exception {
+ return invokeAndWait(callable, component, -1);
+ }
+
+ public static T invokeAndWait(final Callable callable, Component component, int timeoutSeconds) throws Exception {
final CallableWrapper wrapper = new CallableWrapper<>(callable);
- invokeAndWait(wrapper, component);
+ invokeAndWait(wrapper, component, timeoutSeconds);
return wrapper.getResult();
}
@@ -708,40 +792,97 @@ public T getResult() throws Exception {
if (e != null) throw e;
return object;
}
+
+ @Override
+ public String toString() {
+ return "CallableWrapper{" + callable + "}";
+ }
}
/**
- * Kicks an event over to the appropriate event queue and waits for it to
+ * Kicks an event over to the appropriate eventqueue and waits for it to
* finish To avoid deadlocking, we manually run the NSRunLoop while waiting
* Any selector invoked using ThreadUtilities performOnMainThread will be
* processed in doAWTRunLoop The InvocationEvent will call
* LWCToolkit.stopAWTRunLoop() when finished, which will stop our manual
- * run loop. Does not dispatch native events while in the loop
+ * runloop Does not dispatch native events while in the loop
*/
public static void invokeAndWait(Runnable runnable, Component component)
- throws InvocationTargetException {
- Objects.requireNonNull(component, "Null component provided to invokeAndWait");
+ throws InvocationTargetException
+ {
+ invokeAndWait(runnable, component, -1);
+ }
+
+ public static void invokeAndWait(Runnable runnable, Component component, int timeoutSeconds)
+ throws InvocationTargetException
+ {
+ invokeAndWait(runnable, component, false, -1);
+ }
+
+ public static void invokeAndWait(Runnable runnable, Component component, boolean processEvents, int timeoutSeconds)
+ throws InvocationTargetException
+ {
+ if (log.isLoggable(PlatformLogger.Level.FINE)) {
+ log.fine("invokeAndWait started: " + runnable);
+ }
+
+ if (isBlockingEventDispatchThread()) {
+ String msg = "invokeAndWait is discarded as the EventDispatch thread is currently blocked";
+ if (log.isLoggable(PlatformLogger.Level.FINE)) {
+ log.fine(msg, new Throwable());
+ log.fine(AWTThreading.getInstance(component).printEventDispatchThreadStackTrace().toString());
+ } else if (log.isLoggable(PlatformLogger.Level.INFO)) {
+ StackTraceElement[] stack = new Throwable().getStackTrace();
+ log.info(msg + ". Originated at " + stack[stack.length - 1]);
+ Thread dispatchThread = AWTThreading.getInstance(component).getEventDispatchThread();
+ log.info(dispatchThread.getName() + " at: " + dispatchThread.getStackTrace()[0].toString());
+ }
+ return;
+ }
+
+ AWTThreading.TrackedInvocationEvent invocationEvent =
+ AWTThreading.createAndTrackInvocationEvent(component, runnable, true);
long mediator = createAWTRunLoopMediator();
- InvocationEvent invocationEvent =
- new InvocationEvent(component,
- runnable,
- () -> {
- if (mediator != 0) {
- stopAWTRunLoop(mediator);
- }
- },
- true);
+ invocationEvent.onDone(() -> stopAWTRunLoop(mediator));
- AppContext appContext = SunToolkit.targetToAppContext(component);
- SunToolkit.postEvent(appContext, invocationEvent);
- // 3746956 - flush events from PostEventQueue to prevent them from getting stuck and causing a deadlock
- SunToolkit.flushPendingEvents(appContext);
- doAWTRunLoop(mediator, false);
+ if (component != null) {
+ AppContext appContext = SunToolkit.targetToAppContext(component);
+ SunToolkit.postEvent(appContext, invocationEvent);
+
+ // 3746956 - flush events from PostEventQueue to prevent them from getting stuck and causing a deadlock
+ SunToolkit.flushPendingEvents(appContext);
+ }
+ else {
+ // This should be the equivalent to EventQueue.invokeAndWait
+ ((LWCToolkit)Toolkit.getDefaultToolkit()).getSystemEventQueueForInvokeAndWait().postEvent(invocationEvent);
+ }
+
+ if (DISPOSE_INVOCATION_ON_EDT_FREE) {
+ CompletableFuture eventDispatchThreadFreeFuture =
+ AWTThreading.getInstance(component).onEventDispatchThreadFree(() -> {
+ if (!invocationEvent.isCompleted(true)) {
+ // EventQueue is now empty but the posted InvocationEvent is still not dispatched,
+ // consider it lost then.
+ invocationEvent.dispose("InvocationEvent was lost");
+ }
+ });
+ invocationEvent.onDone(() -> eventDispatchThreadFreeFuture.cancel(false));
+ }
+
+ if (!doAWTRunLoop(mediator, processEvents, timeoutSeconds)) {
+ invocationEvent.dispose("InvocationEvent has timed out");
+ }
+
+ if (log.isLoggable(PlatformLogger.Level.FINE)) {
+ log.fine("invokeAndWait finished: " + runnable);
+ }
checkException(invocationEvent);
}
+ private static native boolean isBlockingEventDispatchThread();
+
public static void invokeLater(Runnable event, Component component)
throws InvocationTargetException {
Objects.requireNonNull(component, "Null component provided to invokeLater");
@@ -756,6 +897,10 @@ public static void invokeLater(Runnable event, Component component)
checkException(invocationEvent);
}
+ EventQueue getSystemEventQueueForInvokeAndWait() {
+ return getSystemEventQueueImpl();
+ }
+
/**
* Checks if exception occurred while {@code InvocationEvent} was processed and rethrows it as
* an {@code InvocationTargetException}
@@ -780,6 +925,11 @@ private static void checkException(InvocationEvent event) throws InvocationTarge
*/
static native void performOnMainThreadAfterDelay(Runnable r, long delay);
+ /**
+ * Schedules a {@code Runnable} execution on the Appkit thread and waits for completion.
+ */
+ public static native void performOnMainThreadAndWait(Runnable r);
+
// DnD support
@Override
@@ -834,23 +984,6 @@ public Locale getDefaultKeyboardLocale() {
return locale;
}
- public static boolean isLocaleUSInternationalPC(Locale locale) {
- return (locale != null ?
- locale.toString().equals("_US_UserDefined_15000") : false);
- }
-
- public static boolean isCharModifierKeyInUSInternationalPC(char ch) {
- // 5 characters: APOSTROPHE, QUOTATION MARK, ACCENT GRAVE, SMALL TILDE,
- // CIRCUMFLEX ACCENT
- final char[] modifierKeys = {'\'', '"', '`', '\u02DC', '\u02C6'};
- for (char modKey : modifierKeys) {
- if (modKey == ch) {
- return true;
- }
- }
- return false;
- }
-
@Override
public InputMethodDescriptor getInputMethodAdapterDescriptor() {
if (sInputMethodDescriptor == null)
@@ -935,9 +1068,16 @@ public boolean canPopupOverlapTaskBar() {
* if false - all events come after exit form the nested loop
*/
static void doAWTRunLoop(long mediator, boolean processEvents) {
- doAWTRunLoopImpl(mediator, processEvents, inAWT);
+ doAWTRunLoop(mediator, processEvents, -1);
+ }
+
+ /**
+ * Starts run-loop with the provided timeout. Use (-1) for the infinite value.
+ */
+ static boolean doAWTRunLoop(long mediator, boolean processEvents, int timeoutSeconds) {
+ return doAWTRunLoopImpl(mediator, processEvents, inAWT, timeoutSeconds);
}
- private static native void doAWTRunLoopImpl(long mediator, boolean processEvents, boolean inAWT);
+ private static native boolean doAWTRunLoopImpl(long mediator, boolean processEvents, boolean inAWT, int timeoutSeconds);
static native void stopAWTRunLoop(long mediator);
private native boolean nativeSyncQueue(long timeout);
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/NSEvent.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/NSEvent.java
index ca3d9ad5da7e..9bae8f0db308 100644
--- a/src/java.desktop/macosx/classes/sun/lwawt/macosx/NSEvent.java
+++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/NSEvent.java
@@ -56,15 +56,15 @@ final class NSEvent {
// Key event information
private short keyCode;
private String characters;
- private String charactersIgnoringModifiers;
+ private String actualCharacters;
// Called from native
- NSEvent(int type, int modifierFlags, short keyCode, String characters, String charactersIgnoringModifiers) {
+ NSEvent(int type, int modifierFlags, short keyCode, String characters, String actualCharacters) {
this.type = type;
this.modifierFlags = modifierFlags;
this.keyCode = keyCode;
this.characters = characters;
- this.charactersIgnoringModifiers = charactersIgnoringModifiers;
+ this.actualCharacters = actualCharacters;
}
// Called from native
@@ -132,20 +132,20 @@ short getKeyCode() {
return keyCode;
}
- String getCharactersIgnoringModifiers() {
- return charactersIgnoringModifiers;
- }
-
String getCharacters() {
return characters;
}
+ String getActualCharacters() {
+ return actualCharacters;
+ }
+
@Override
public String toString() {
return "NSEvent[" + getType() + " ," + getModifierFlags() + " ,"
+ getClickCount() + " ," + getButtonNumber() + " ," + getX() + " ,"
+ getY() + " ," + getAbsX() + " ," + getAbsY()+ " ," + getKeyCode() + " ,"
- + getCharacters() + " ," + getCharactersIgnoringModifiers() + "]";
+ + getCharacters() + " ," + getActualCharacters() + "]";
}
/*
@@ -256,7 +256,7 @@ static int nsToJavaEventType(int nsEventType) {
/*
* Converts NSEvent key info to AWT key info.
*/
- static native boolean nsToJavaKeyInfo(int[] in, int[] out);
+ static native void nsToJavaKeyInfo(int[] in, int[] out);
/*
* Converts NSEvent key modifiers to AWT key info.
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/concurrent/Dispatch.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/concurrent/Dispatch.java
new file mode 100644
index 000000000000..b0b3d2acfb34
--- /dev/null
+++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/concurrent/Dispatch.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.lwawt.macosx.concurrent;
+
+import java.util.concurrent.*;
+
+/**
+ * Factory for {@link Executor}s and {@link ExecutorService}s backed by
+ * libdispatch.
+ *
+ * Access is controlled through the Dispatch.getInstance() method, because
+ * performed tasks occur on threads owned by libdispatch. These threads are
+ * not owned by any particular AppContext or have any specific context
+ * classloader installed.
+ *
+ * @since Java for Mac OS X 10.6 Update 2
+ */
+@SuppressWarnings("removal")
+public final class Dispatch {
+ /**
+ * The priorities of the three default asynchronous queues.
+ */
+ public enum Priority {
+ LOW(-2), NORMAL(0), HIGH(2); // values from
+
+ final int nativePriority;
+ Priority(final int nativePriority) { this.nativePriority = nativePriority; }
+ };
+
+ final static Dispatch instance = new Dispatch();
+
+ /**
+ * Factory method returns an instnace of Dispatch if supported by the
+ * underlying operating system, and if the caller's security manager
+ * permits "canInvokeInSystemThreadGroup".
+ *
+ * @return a factory instance of Dispatch, or null if not available
+ */
+ public static Dispatch getInstance() {
+ checkSecurity();
+ if (!LibDispatchNative.nativeIsDispatchSupported()) return null;
+
+ return instance;
+ }
+
+ private static void checkSecurity() {
+ final SecurityManager security = System.getSecurityManager();
+ if (security != null) security.checkPermission(new RuntimePermission("canInvokeInSystemThreadGroup"));
+ }
+
+ private Dispatch() { }
+
+ /**
+ * Creates an {@link Executor} that performs tasks asynchronously. The {@link Executor}
+ * cannot be shutdown, and enqueued {@link Runnable}s cannot be canceled. Passing null
+ * returns the {@link Priority.NORMAL} {@link Executor}.
+ *
+ * @param priority - the priority of the returned {@link Executor}
+ * @return an asynchronous {@link Executor}
+ */
+ public Executor getAsyncExecutor(Priority priority) {
+ if (priority == null) priority = Priority.NORMAL;
+ final long nativeQueue = LibDispatchNative.nativeCreateConcurrentQueue(priority.nativePriority);
+ if (nativeQueue == 0L) return null;
+ return new LibDispatchConcurrentQueue(nativeQueue);
+ }
+
+ int queueIndex = 0;
+ /**
+ * Creates an {@link ExecutorService} that performs tasks synchronously in FIFO order.
+ * Useful to protect a resource against concurrent modification, in lieu of a lock.
+ * Passing null returns an {@link ExecutorService} with a uniquely labeled queue.
+ *
+ * @param label - a label to name the queue, shown in several debugging tools
+ * @return a synchronous {@link ExecutorService}
+ */
+ public ExecutorService createSerialExecutor(String label) {
+ if (label == null) label = "";
+ if (label.length() > 256) label = label.substring(0, 256);
+ String queueName = "com.apple.java.concurrent.";
+ if ("".equals(label)) {
+ synchronized (this) {
+ queueName += queueIndex++;
+ }
+ } else {
+ queueName += label;
+ }
+
+ final long nativeQueue = LibDispatchNative.nativeCreateSerialQueue(queueName);
+ if (nativeQueue == 0) return null;
+ return new LibDispatchSerialQueue(nativeQueue);
+ }
+
+ Executor nonBlockingMainQueue = null;
+ /**
+ * Returns an {@link Executor} that performs the provided Runnables on the main queue of the process.
+ * Runnables submitted to this {@link Executor} will not run until the AWT is started or another native toolkit is running a CFRunLoop or NSRunLoop on the main thread.
+ *
+ * Submitting a Runnable to this {@link Executor} does not wait for the Runnable to complete.
+ * @return an asynchronous {@link Executor} that is backed by the main queue
+ */
+ public synchronized Executor getNonBlockingMainQueueExecutor() {
+ if (nonBlockingMainQueue != null) return nonBlockingMainQueue;
+ return nonBlockingMainQueue = new LibDispatchMainQueue.ASync();
+ }
+
+ Executor blockingMainQueue = null;
+ /**
+ * Returns an {@link Executor} that performs the provided Runnables on the main queue of the process.
+ * Runnables submitted to this {@link Executor} will not run until the AWT is started or another native toolkit is running a CFRunLoop or NSRunLoop on the main thread.
+ *
+ * Submitting a Runnable to this {@link Executor} will block until the Runnable has completed.
+ * @return an {@link Executor} that is backed by the main queue
+ */
+ public synchronized Executor getBlockingMainQueueExecutor() {
+ if (blockingMainQueue != null) return blockingMainQueue;
+ return blockingMainQueue = new LibDispatchMainQueue.Sync();
+ }
+}
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/concurrent/LibDispatchConcurrentQueue.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/concurrent/LibDispatchConcurrentQueue.java
new file mode 100644
index 000000000000..9ed6046c6668
--- /dev/null
+++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/concurrent/LibDispatchConcurrentQueue.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.lwawt.macosx.concurrent;
+
+import java.util.concurrent.Executor;
+
+class LibDispatchConcurrentQueue extends LibDispatchQueue implements Executor {
+ LibDispatchConcurrentQueue(final long queuePtr) {
+ super(queuePtr);
+ }
+
+ @Override
+ public void execute(final Runnable task) {
+ LibDispatchNative.nativeExecuteAsync(ptr, task);
+ }
+
+ @Override
+ protected synchronized void dispose() {
+ // should not dispose the default concurrent queues
+ }
+}
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/concurrent/LibDispatchMainQueue.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/concurrent/LibDispatchMainQueue.java
new file mode 100644
index 000000000000..d935372e8403
--- /dev/null
+++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/concurrent/LibDispatchMainQueue.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.lwawt.macosx.concurrent;
+
+import java.util.concurrent.Executor;
+
+abstract class LibDispatchMainQueue extends LibDispatchQueue implements Executor {
+ public LibDispatchMainQueue() {
+ super(LibDispatchNative.nativeGetMainQueue());
+ }
+
+ @Override
+ protected synchronized void dispose() {
+ // should not dispose the main queue
+ }
+
+ static class Sync extends LibDispatchMainQueue {
+ @Override
+ public void execute(final Runnable task) {
+ LibDispatchNative.nativeExecuteSync(ptr, task);
+ }
+ }
+
+ static class ASync extends LibDispatchMainQueue {
+ @Override
+ public void execute(final Runnable task) {
+ LibDispatchNative.nativeExecuteAsync(ptr, task);
+ }
+ }
+}
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/concurrent/LibDispatchNative.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/concurrent/LibDispatchNative.java
new file mode 100644
index 000000000000..bd8cc7d68540
--- /dev/null
+++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/concurrent/LibDispatchNative.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.lwawt.macosx.concurrent;
+
+@SuppressWarnings("removal")
+final class LibDispatchNative {
+ static {
+ java.security.AccessController.doPrivileged(
+ new java.security.PrivilegedAction() {
+ public Void run() {
+ System.loadLibrary("osx");
+ return null;
+ }
+ });
+ }
+
+ static native boolean nativeIsDispatchSupported();
+ static native long nativeGetMainQueue();
+ static native long nativeCreateConcurrentQueue(int priority);
+ static native long nativeCreateSerialQueue(String name);
+ static native void nativeReleaseQueue(long nativeQueue);
+ static native void nativeExecuteAsync(long nativeQueue, Runnable task);
+ static native void nativeExecuteSync(long nativeQueue, Runnable task);
+
+ private LibDispatchNative() { }
+}
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/concurrent/LibDispatchQueue.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/concurrent/LibDispatchQueue.java
new file mode 100644
index 000000000000..01362574c012
--- /dev/null
+++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/concurrent/LibDispatchQueue.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.lwawt.macosx.concurrent;
+
+class LibDispatchQueue extends LibDispatchRetainedResource {
+ LibDispatchQueue(final long queuePtr) {
+ super(queuePtr);
+ }
+}
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/concurrent/LibDispatchRetainedResource.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/concurrent/LibDispatchRetainedResource.java
new file mode 100644
index 000000000000..5cffc70cdf63
--- /dev/null
+++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/concurrent/LibDispatchRetainedResource.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.lwawt.macosx.concurrent;
+
+class LibDispatchRetainedResource {
+ protected long ptr;
+
+ protected LibDispatchRetainedResource(final long ptr) {
+ this.ptr = ptr;
+ }
+
+ protected synchronized void dispose() {
+ if (ptr != 0) LibDispatchNative.nativeReleaseQueue(ptr);
+ ptr = 0;
+ }
+
+ @Override
+ @SuppressWarnings("removal")
+ protected void finalize() throws Throwable {
+ dispose();
+ }
+}
diff --git a/src/java.desktop/macosx/classes/sun/lwawt/macosx/concurrent/LibDispatchSerialQueue.java b/src/java.desktop/macosx/classes/sun/lwawt/macosx/concurrent/LibDispatchSerialQueue.java
new file mode 100644
index 000000000000..2b5569edac02
--- /dev/null
+++ b/src/java.desktop/macosx/classes/sun/lwawt/macosx/concurrent/LibDispatchSerialQueue.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.lwawt.macosx.concurrent;
+
+import java.util.List;
+import java.util.concurrent.*;
+
+class LibDispatchSerialQueue extends AbstractExecutorService {
+ static final int RUNNING = 0;
+ static final int SHUTDOWN = 1;
+// static final int STOP = 2; // not supported by GCD
+ static final int TERMINATED = 3;
+
+ final Object lock = new Object();
+ LibDispatchQueue nativeQueueWrapper;
+ volatile int runState;
+
+ LibDispatchSerialQueue(final long queuePtr) {
+ nativeQueueWrapper = new LibDispatchQueue(queuePtr);
+ }
+
+ @Override
+ public void execute(final Runnable task) {
+ if (nativeQueueWrapper == null) return;
+ LibDispatchNative.nativeExecuteAsync(nativeQueueWrapper.ptr, task);
+ }
+
+ @Override
+ public boolean isShutdown() {
+ return runState != RUNNING;
+ }
+
+ @Override
+ public boolean isTerminated() {
+ return runState == TERMINATED;
+ }
+
+ @Override
+ public void shutdown() {
+ synchronized (lock) {
+ if (runState != RUNNING) return;
+
+ runState = SHUTDOWN;
+ execute(new Runnable() {
+ public void run() {
+ synchronized (lock) {
+ runState = TERMINATED;
+ lock.notifyAll(); // for the benefit of awaitTermination()
+ }
+ }
+ });
+ nativeQueueWrapper = null;
+ }
+ }
+
+ @Override
+ public List shutdownNow() {
+ shutdown();
+ return null;
+ }
+
+ @Override
+ public boolean awaitTermination(final long timeout, final TimeUnit unit) throws InterruptedException {
+ if (runState == TERMINATED) return true;
+
+ final long millis = unit.toMillis(timeout);
+ if (millis <= 0) return false;
+
+ synchronized (lock) {
+ if (runState == TERMINATED) return true;
+ lock.wait(timeout);
+ if (runState == TERMINATED) return true;
+ }
+
+ return false;
+ }
+}
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTEvent.h b/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTEvent.h
index 3c7cbe086ee5..21120a68f6e7 100644
--- a/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTEvent.h
+++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTEvent.h
@@ -26,13 +26,25 @@
#ifndef __AWTEVENT_H
#define __AWTEVENT_H
+#import
+
jlong UTC(NSEvent *event);
void DeliverJavaKeyEvent(JNIEnv *env, NSEvent *event, jobject peer);
void DeliverJavaMouseEvent(JNIEnv *env, NSEvent *event, jobject peer);
void SendAdditionalJavaEvents(JNIEnv *env, NSEvent *nsEvent, jobject peer);
-jint GetJavaMouseModifiers(NSInteger button, NSUInteger modifierFlags);
+jint GetJavaMouseModifiers(NSUInteger modifierFlags);
jint NsKeyModifiersToJavaModifiers(NSUInteger nsFlags, BOOL isExtMods);
NSUInteger JavaModifiersToNsKeyModifiers(jint javaModifiers, BOOL isExtMods);
-unichar NsCharToJavaChar(unichar nsChar, NSUInteger modifiers);
+unichar NsCharToJavaChar(unichar nsChar, NSUInteger modifiers, BOOL spaceKeyTyped);
+
+struct KeyCodeTranslationResult {
+ unichar character;
+ BOOL isSuccess;
+ BOOL isDead;
+ BOOL isTyped;
+};
+
+TISInputSourceRef GetCurrentUnderlyingLayout(BOOL useNationalLayouts);
+struct KeyCodeTranslationResult TranslateKeyCodeUsingLayout(TISInputSourceRef layout, unsigned short keyCode);
#endif /* __AWTEVENT_H */
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTEvent.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTEvent.m
index d26def5694fb..818cea8491fc 100644
--- a/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTEvent.m
+++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTEvent.m
@@ -28,6 +28,7 @@
#import "LWCToolkit.h"
#import "JNIUtilities.h"
+#import "AWTEvent.h"
#import
#import
@@ -60,143 +61,166 @@
#define KL_STANDARD java_awt_event_KeyEvent_KEY_LOCATION_STANDARD
#define KL_NUMPAD java_awt_event_KeyEvent_KEY_LOCATION_NUMPAD
#define KL_UNKNOWN java_awt_event_KeyEvent_KEY_LOCATION_UNKNOWN
-static struct _key
+#define KL_LEFT java_awt_event_KeyEvent_KEY_LOCATION_LEFT
+#define KL_RIGHT java_awt_event_KeyEvent_KEY_LOCATION_RIGHT
+
+struct KeyTableEntry
{
unsigned short keyCode;
- BOOL postsTyped;
+ unichar character;
+ BOOL variesBetweenLayouts;
jint javaKeyLocation;
jint javaKeyCode;
-}
-const keyTable[] =
+};
+
+static const struct KeyTableEntry unknownKeyEntry = {
+ 0xFFFF, 0, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED
+};
+
+static const struct KeyTableEntry keyTable[] =
{
- {0x00, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_A},
- {0x01, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_S},
- {0x02, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_D},
- {0x03, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_F},
- {0x04, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_H},
- {0x05, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_G},
- {0x06, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_Z},
- {0x07, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_X},
- {0x08, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_C},
- {0x09, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_V},
- {0x0A, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_BACK_QUOTE},
- {0x0B, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_B},
- {0x0C, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_Q},
- {0x0D, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_W},
- {0x0E, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_E},
- {0x0F, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_R},
- {0x10, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_Y},
- {0x11, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_T},
- {0x12, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_1},
- {0x13, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_2},
- {0x14, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_3},
- {0x15, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_4},
- {0x16, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_6},
- {0x17, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_5},
- {0x18, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_EQUALS},
- {0x19, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_9},
- {0x1A, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_7},
- {0x1B, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_MINUS},
- {0x1C, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_8},
- {0x1D, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_0},
- {0x1E, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_CLOSE_BRACKET},
- {0x1F, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_O},
- {0x20, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_U},
- {0x21, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_OPEN_BRACKET},
- {0x22, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_I},
- {0x23, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_P},
- {0x24, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_ENTER},
- {0x25, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_L},
- {0x26, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_J},
- {0x27, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_QUOTE},
- {0x28, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_K},
- {0x29, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_SEMICOLON},
- {0x2A, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_BACK_SLASH},
- {0x2B, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_COMMA},
- {0x2C, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_SLASH},
- {0x2D, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_N},
- {0x2E, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_M},
- {0x2F, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_PERIOD},
- {0x30, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_TAB},
- {0x31, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_SPACE},
- {0x32, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_BACK_QUOTE},
- {0x33, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_BACK_SPACE},
- {0x34, YES, KL_NUMPAD, java_awt_event_KeyEvent_VK_ENTER},
- {0x35, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_ESCAPE},
- {0x36, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED},
- {0x37, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_META}, // ****
- {0x38, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_SHIFT}, // ****
- {0x39, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_CAPS_LOCK},
- {0x3A, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_ALT}, // ****
- {0x3B, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_CONTROL}, // ****
- {0x3C, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED},
- {0x3D, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_ALT_GRAPH},
- {0x3E, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED},
- {0x3F, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED}, // the 'fn' key on PowerBooks
- {0x40, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F17},
- {0x41, YES, KL_NUMPAD, java_awt_event_KeyEvent_VK_DECIMAL},
- {0x42, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED},
- {0x43, YES, KL_NUMPAD, java_awt_event_KeyEvent_VK_MULTIPLY},
- {0x44, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED},
- {0x45, YES, KL_NUMPAD, java_awt_event_KeyEvent_VK_ADD},
- {0x46, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED},
- {0x47, NO, KL_NUMPAD, java_awt_event_KeyEvent_VK_CLEAR},
- {0x48, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED},
- {0x49, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED},
- {0x4A, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED},
- {0x4B, YES, KL_NUMPAD, java_awt_event_KeyEvent_VK_DIVIDE},
- {0x4C, YES, KL_NUMPAD, java_awt_event_KeyEvent_VK_ENTER},
- {0x4D, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED},
- {0x4E, YES, KL_NUMPAD, java_awt_event_KeyEvent_VK_SUBTRACT},
- {0x4F, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F18},
- {0x50, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F19},
- {0x51, YES, KL_NUMPAD, java_awt_event_KeyEvent_VK_EQUALS},
- {0x52, YES, KL_NUMPAD, java_awt_event_KeyEvent_VK_NUMPAD0},
- {0x53, YES, KL_NUMPAD, java_awt_event_KeyEvent_VK_NUMPAD1},
- {0x54, YES, KL_NUMPAD, java_awt_event_KeyEvent_VK_NUMPAD2},
- {0x55, YES, KL_NUMPAD, java_awt_event_KeyEvent_VK_NUMPAD3},
- {0x56, YES, KL_NUMPAD, java_awt_event_KeyEvent_VK_NUMPAD4},
- {0x57, YES, KL_NUMPAD, java_awt_event_KeyEvent_VK_NUMPAD5},
- {0x58, YES, KL_NUMPAD, java_awt_event_KeyEvent_VK_NUMPAD6},
- {0x59, YES, KL_NUMPAD, java_awt_event_KeyEvent_VK_NUMPAD7},
- {0x5A, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F20},
- {0x5B, YES, KL_NUMPAD, java_awt_event_KeyEvent_VK_NUMPAD8},
- {0x5C, YES, KL_NUMPAD, java_awt_event_KeyEvent_VK_NUMPAD9},
- {0x5D, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_BACK_SLASH}, // This is a combo yen/backslash on JIS keyboards.
- {0x5E, YES, KL_NUMPAD, java_awt_event_KeyEvent_VK_UNDERSCORE},
- {0x5F, YES, KL_NUMPAD, java_awt_event_KeyEvent_VK_COMMA},
- {0x60, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F5},
- {0x61, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F6},
- {0x62, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F7},
- {0x63, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F3},
- {0x64, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F8},
- {0x65, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F9},
- {0x66, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_ALPHANUMERIC},
- {0x67, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F11},
- {0x68, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_KATAKANA},
- {0x69, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F13},
- {0x6A, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F16},
- {0x6B, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F14},
- {0x6C, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED},
- {0x6D, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F10},
- {0x6E, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED},
- {0x6F, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F12},
- {0x70, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED},
- {0x71, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F15},
- {0x72, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_HELP},
- {0x73, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_HOME},
- {0x74, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_PAGE_UP},
- {0x75, YES, KL_STANDARD, java_awt_event_KeyEvent_VK_DELETE},
- {0x76, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F4},
- {0x77, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_END},
- {0x78, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F2},
- {0x79, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_PAGE_DOWN},
- {0x7A, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F1},
- {0x7B, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_LEFT},
- {0x7C, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_RIGHT},
- {0x7D, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_DOWN},
- {0x7E, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_UP},
- {0x7F, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED},
+ // Do not reorder the key codes! They are in ascending numeric order without gaps (from 0x00 to 0x7F).
+ // Characters in the second column are not the characters that UCKeyTranslate returns with the specified key code.
+ // Instead, they are the NSEvent.characters sent by macOS when pressing the specified key on the US layout.
+ // They are currently only used to determine whether the NextAppWindow shortcut was pressed.
+ // They are not used for KEY_TYPED events or anything of this sort.
+ {kVK_ANSI_A, 'a', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_A},
+ {kVK_ANSI_S, 's', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_S},
+ {kVK_ANSI_D, 'd', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_D},
+ {kVK_ANSI_F, 'f', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_F},
+ {kVK_ANSI_H, 'h', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_H},
+ {kVK_ANSI_G, 'g', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_G},
+ {kVK_ANSI_Z, 'z', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_Z},
+ {kVK_ANSI_X, 'x', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_X},
+ {kVK_ANSI_C, 'c', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_C},
+ {kVK_ANSI_V, 'v', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_V},
+ {kVK_ISO_Section, '\xa7', YES, KL_STANDARD, 0x1000000 + 0x00A7},
+ {kVK_ANSI_B, 'b', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_B},
+ {kVK_ANSI_Q, 'q', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_Q},
+ {kVK_ANSI_W, 'w', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_W},
+ {kVK_ANSI_E, 'e', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_E},
+ {kVK_ANSI_R, 'r', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_R},
+ {kVK_ANSI_Y, 'y', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_Y},
+ {kVK_ANSI_T, 't', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_T},
+ {kVK_ANSI_1, '1', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_1},
+ {kVK_ANSI_2, '2', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_2},
+ {kVK_ANSI_3, '3', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_3},
+ {kVK_ANSI_4, '4', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_4},
+ {kVK_ANSI_6, '6', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_6},
+ {kVK_ANSI_5, '5', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_5},
+ {kVK_ANSI_Equal, '=', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_EQUALS},
+ {kVK_ANSI_9, '9', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_9},
+ {kVK_ANSI_7, '7', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_7},
+ {kVK_ANSI_Minus, '-', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_MINUS},
+ {kVK_ANSI_8, '8', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_8},
+ {kVK_ANSI_0, '0', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_0},
+ {kVK_ANSI_RightBracket, ']', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_CLOSE_BRACKET},
+ {kVK_ANSI_O, 'o', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_O},
+ {kVK_ANSI_U, 'u', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_U},
+ {kVK_ANSI_LeftBracket, '[', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_OPEN_BRACKET},
+ {kVK_ANSI_I, 'i', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_I},
+ {kVK_ANSI_P, 'p', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_P},
+ {kVK_Return, NSCarriageReturnCharacter, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_ENTER},
+ {kVK_ANSI_L, 'l', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_L},
+ {kVK_ANSI_J, 'j', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_J},
+ {kVK_ANSI_Quote, '\'', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_QUOTE},
+ {kVK_ANSI_K, 'k', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_K},
+ {kVK_ANSI_Semicolon, ';', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_SEMICOLON},
+ {kVK_ANSI_Backslash, '\\', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_BACK_SLASH},
+ {kVK_ANSI_Comma, ',', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_COMMA},
+ {kVK_ANSI_Slash, '/', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_SLASH},
+ {kVK_ANSI_N, 'n', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_N},
+ {kVK_ANSI_M, 'm', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_M},
+ {kVK_ANSI_Period, '.', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_PERIOD},
+ {kVK_Tab, NSTabCharacter, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_TAB},
+ {kVK_Space, ' ', NO, KL_STANDARD, java_awt_event_KeyEvent_VK_SPACE},
+ {kVK_ANSI_Grave, '`', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_BACK_QUOTE},
+ {kVK_Delete, NSDeleteCharacter, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_BACK_SPACE},
+ {0x34, 0, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED}, // undefined
+ {kVK_Escape, '\x1b', NO, KL_STANDARD, java_awt_event_KeyEvent_VK_ESCAPE},
+ {kVK_RightCommand, 0, NO, KL_RIGHT, java_awt_event_KeyEvent_VK_META},
+ {kVK_Command, 0, NO, KL_LEFT, java_awt_event_KeyEvent_VK_META},
+ {kVK_Shift, 0, NO, KL_LEFT, java_awt_event_KeyEvent_VK_SHIFT},
+ {kVK_CapsLock, 0, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_CAPS_LOCK},
+ {kVK_Option, 0, NO, KL_LEFT, java_awt_event_KeyEvent_VK_ALT},
+ {kVK_Control, 0, NO, KL_LEFT, java_awt_event_KeyEvent_VK_CONTROL},
+ {kVK_RightShift, 0, NO, KL_RIGHT, java_awt_event_KeyEvent_VK_SHIFT},
+ {kVK_RightOption, 0, NO, KL_RIGHT, java_awt_event_KeyEvent_VK_ALT},
+ {kVK_RightControl, 0, NO, KL_RIGHT, java_awt_event_KeyEvent_VK_CONTROL},
+ {kVK_Function, 0, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED},
+ {kVK_F17, NSF17FunctionKey, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F17},
+ {kVK_ANSI_KeypadDecimal, '.', NO, KL_NUMPAD, java_awt_event_KeyEvent_VK_DECIMAL},
+ {0x42, 0, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED}, // undefined
+ {kVK_ANSI_KeypadMultiply, '*', NO, KL_NUMPAD, java_awt_event_KeyEvent_VK_MULTIPLY},
+ {0x44, 0, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED}, // undefined
+ {kVK_ANSI_KeypadPlus, '+', NO, KL_NUMPAD, java_awt_event_KeyEvent_VK_ADD},
+ {0x46, 0, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED}, // undefined
+ {kVK_ANSI_KeypadClear, NSClearLineFunctionKey, NO, KL_NUMPAD, java_awt_event_KeyEvent_VK_CLEAR},
+ {kVK_VolumeUp, 0, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED},
+ {kVK_VolumeDown, 0, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED},
+ {kVK_Mute, 0, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED},
+ {kVK_ANSI_KeypadDivide, '/', NO, KL_NUMPAD, java_awt_event_KeyEvent_VK_DIVIDE},
+ {kVK_ANSI_KeypadEnter, NSEnterCharacter, NO, KL_NUMPAD, java_awt_event_KeyEvent_VK_ENTER},
+ {0x4D, 0, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED}, // undefined
+ {kVK_ANSI_KeypadMinus, '-', NO, KL_NUMPAD, java_awt_event_KeyEvent_VK_SUBTRACT},
+ {kVK_F18, NSF18FunctionKey, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F18},
+ {kVK_F19, NSF19FunctionKey, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F19},
+ {kVK_ANSI_KeypadEquals, '=', NO, KL_NUMPAD, java_awt_event_KeyEvent_VK_EQUALS},
+ {kVK_ANSI_Keypad0, '0', NO, KL_NUMPAD, java_awt_event_KeyEvent_VK_NUMPAD0},
+ {kVK_ANSI_Keypad1, '1', NO, KL_NUMPAD, java_awt_event_KeyEvent_VK_NUMPAD1},
+ {kVK_ANSI_Keypad2, '2', NO, KL_NUMPAD, java_awt_event_KeyEvent_VK_NUMPAD2},
+ {kVK_ANSI_Keypad3, '3', NO, KL_NUMPAD, java_awt_event_KeyEvent_VK_NUMPAD3},
+ {kVK_ANSI_Keypad4, '4', NO, KL_NUMPAD, java_awt_event_KeyEvent_VK_NUMPAD4},
+ {kVK_ANSI_Keypad5, '5', NO, KL_NUMPAD, java_awt_event_KeyEvent_VK_NUMPAD5},
+ {kVK_ANSI_Keypad6, '6', NO, KL_NUMPAD, java_awt_event_KeyEvent_VK_NUMPAD6},
+ {kVK_ANSI_Keypad7, '7', NO, KL_NUMPAD, java_awt_event_KeyEvent_VK_NUMPAD7},
+ {kVK_F20, NSF20FunctionKey, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F20},
+ {kVK_ANSI_Keypad8, '8', NO, KL_NUMPAD, java_awt_event_KeyEvent_VK_NUMPAD8},
+ {kVK_ANSI_Keypad9, '9', NO, KL_NUMPAD, java_awt_event_KeyEvent_VK_NUMPAD9},
+ {kVK_JIS_Yen, '\xa5', YES, KL_STANDARD, 0x1000000 + 0x00A5},
+ {kVK_JIS_Underscore, '_', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_UNDERSCORE},
+ {kVK_JIS_KeypadComma, ',', NO, KL_NUMPAD, java_awt_event_KeyEvent_VK_COMMA},
+ {kVK_F5, NSF5FunctionKey, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F5},
+ {kVK_F6, NSF6FunctionKey, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F6},
+ {kVK_F7, NSF7FunctionKey, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F7},
+ {kVK_F3, NSF3FunctionKey, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F3},
+ {kVK_F8, NSF8FunctionKey, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F8},
+ {kVK_F9, NSF9FunctionKey, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F9},
+ {kVK_JIS_Eisu, 0, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_ALPHANUMERIC},
+ {kVK_F11, NSF11FunctionKey, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F11},
+ {kVK_JIS_Kana, 0, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_KATAKANA},
+ {kVK_F13, NSF13FunctionKey, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F13},
+ {kVK_F16, NSF16FunctionKey, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F16},
+ {kVK_F14, NSF14FunctionKey, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F14},
+ {0x6C, 0, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED}, // undefined
+ {kVK_F10, NSF10FunctionKey, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F10},
+ {0x6E, 0, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED}, // undefined
+ {kVK_F12, NSF12FunctionKey, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F12},
+ {0x70, 0, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED}, // undefined
+ {kVK_F15, NSF15FunctionKey, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F15},
+ {kVK_Help, NSHelpFunctionKey, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_HELP},
+ {kVK_Home, NSHomeFunctionKey, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_HOME},
+ {kVK_PageUp, NSPageUpFunctionKey, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_PAGE_UP},
+ {kVK_ForwardDelete, NSDeleteFunctionKey, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_DELETE},
+ {kVK_F4, NSF4FunctionKey, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F4},
+ {kVK_End, NSEndFunctionKey, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_END},
+ {kVK_F2, NSF2FunctionKey, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F2},
+ {kVK_PageDown, NSPageDownFunctionKey, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_PAGE_DOWN},
+ {kVK_F1, NSF1FunctionKey, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_F1},
+ {kVK_LeftArrow, NSLeftArrowFunctionKey, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_LEFT},
+ {kVK_RightArrow, NSRightArrowFunctionKey, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_RIGHT},
+ {kVK_DownArrow, NSDownArrowFunctionKey, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_DOWN},
+ {kVK_UpArrow, NSUpArrowFunctionKey, NO, KL_STANDARD, java_awt_event_KeyEvent_VK_UP},
+ {0x7F, 0, NO, KL_UNKNOWN, java_awt_event_KeyEvent_VK_UNDEFINED}, // undefined
+};
+
+static const struct KeyTableEntry keyTableJISOverride[] = {
+ {0x18, '^', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_CIRCUMFLEX},
+ {0x1E, '[', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_OPEN_BRACKET},
+ {0x21, '@', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_AT},
+ {0x27, ':', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_COLON},
+ {0x2A, ']', YES, KL_STANDARD, java_awt_event_KeyEvent_VK_CLOSE_BRACKET},
+ // Some other keys are already handled in the previous table, no need to repeat them here
};
/*
@@ -212,6 +236,7 @@
};
static const struct CharToVKEntry charToDeadVKTable[] = {
{0x0060, java_awt_event_KeyEvent_VK_DEAD_GRAVE},
+ {0x0027, java_awt_event_KeyEvent_VK_DEAD_ACUTE},
{0x00B4, java_awt_event_KeyEvent_VK_DEAD_ACUTE},
{0x0384, java_awt_event_KeyEvent_VK_DEAD_ACUTE}, // Unicode "GREEK TONOS" -- Greek keyboard, semicolon key
{0x005E, java_awt_event_KeyEvent_VK_DEAD_CIRCUMFLEX},
@@ -221,6 +246,7 @@
{0x02D8, java_awt_event_KeyEvent_VK_DEAD_BREVE},
{0x02D9, java_awt_event_KeyEvent_VK_DEAD_ABOVEDOT},
{0x00A8, java_awt_event_KeyEvent_VK_DEAD_DIAERESIS},
+ {0x00B0, java_awt_event_KeyEvent_VK_DEAD_ABOVERING},
{0x02DA, java_awt_event_KeyEvent_VK_DEAD_ABOVERING},
{0x02DD, java_awt_event_KeyEvent_VK_DEAD_DOUBLEACUTE},
{0x02C7, java_awt_event_KeyEvent_VK_DEAD_CARON},
@@ -232,6 +258,58 @@
{0,0}
};
+// This table is analogous to a similar one found in sun.awt.ExtendedKeyCodes.
+// It governs translating the unicode codepoints into the proper VK_ values for keys that have one.
+// It only deals with punctuation characters and certain exceptional letters from extended latin set.
+// Also see test/jdk/jb/sun/awt/macos/InputMethodTest/KeyCodesTest.java
+static const struct CharToVKEntry extraCharToVKTable[] = {
+ {0x0021, java_awt_event_KeyEvent_VK_EXCLAMATION_MARK},
+ {0x0022, java_awt_event_KeyEvent_VK_QUOTEDBL},
+ {0x0023, java_awt_event_KeyEvent_VK_NUMBER_SIGN},
+ {0x0024, java_awt_event_KeyEvent_VK_DOLLAR},
+ {0x0026, java_awt_event_KeyEvent_VK_AMPERSAND},
+ {0x0027, java_awt_event_KeyEvent_VK_QUOTE},
+ {0x0028, java_awt_event_KeyEvent_VK_LEFT_PARENTHESIS},
+ {0x0029, java_awt_event_KeyEvent_VK_RIGHT_PARENTHESIS},
+ {0x002A, java_awt_event_KeyEvent_VK_ASTERISK},
+ {0x002B, java_awt_event_KeyEvent_VK_PLUS},
+ {0x002C, java_awt_event_KeyEvent_VK_COMMA},
+ {0x002D, java_awt_event_KeyEvent_VK_MINUS},
+ {0x002E, java_awt_event_KeyEvent_VK_PERIOD},
+ {0x002F, java_awt_event_KeyEvent_VK_SLASH},
+ {0x003A, java_awt_event_KeyEvent_VK_COLON},
+ {0x003B, java_awt_event_KeyEvent_VK_SEMICOLON},
+ {0x003C, java_awt_event_KeyEvent_VK_LESS},
+ {0x003D, java_awt_event_KeyEvent_VK_EQUALS},
+ {0x003E, java_awt_event_KeyEvent_VK_GREATER},
+ {0x0040, java_awt_event_KeyEvent_VK_AT},
+ {0x005B, java_awt_event_KeyEvent_VK_OPEN_BRACKET},
+ {0x005C, java_awt_event_KeyEvent_VK_BACK_SLASH},
+ {0x005D, java_awt_event_KeyEvent_VK_CLOSE_BRACKET},
+ {0x005E, java_awt_event_KeyEvent_VK_CIRCUMFLEX},
+ {0x005F, java_awt_event_KeyEvent_VK_UNDERSCORE},
+ {0x0060, java_awt_event_KeyEvent_VK_BACK_QUOTE},
+ {0x007B, java_awt_event_KeyEvent_VK_BRACELEFT},
+ {0x007D, java_awt_event_KeyEvent_VK_BRACERIGHT},
+ {0x00A1, java_awt_event_KeyEvent_VK_INVERTED_EXCLAMATION_MARK},
+
+ // These are the extended latin characters which have a non-obvious key code.
+ // Their key codes are derived from the upper case instead of the lower case.
+ // It probably has to do with how these key codes are reported on Windows.
+ // Translating these characters to the key codes corresponding to their uppercase versions
+ // makes getExtendedKeyCodeForChar consistent with how these events are reported on macOS.
+ {0x00E4, 0x01000000+0x00C4},
+ {0x00E5, 0x01000000+0x00C5},
+ {0x00E6, 0x01000000+0x00C6},
+ {0x00E7, 0x01000000+0x00C7},
+ {0x00F1, 0x01000000+0x00D1},
+ {0x00F6, 0x01000000+0x00D6},
+ {0x00F8, 0x01000000+0x00D8},
+
+ {0x20AC, java_awt_event_KeyEvent_VK_EURO_SIGN},
+ {0, 0}
+};
+
// TODO: some constants below are part of CGS (private interfaces)...
// for now we will look at the raw key code to determine left/right status
// but not sure this is foolproof...
@@ -381,44 +459,154 @@ unichar NsCharToJavaChar(unichar nsChar, NSUInteger modifiers, BOOL spaceKeyType
return nsChar;
}
-static unichar NsGetDeadKeyChar(unsigned short keyCode)
+static const struct KeyTableEntry* GetKeyTableEntryForKeyCode(unsigned short keyCode) {
+ static const size_t keyTableSize = sizeof(keyTable) / sizeof(struct KeyTableEntry);
+ static const size_t keyTableJISOverrideSize = sizeof(keyTableJISOverride) / sizeof(struct KeyTableEntry);
+ BOOL isJIS = KBGetLayoutType(LMGetKbdType()) == kKeyboardJIS;
+
+ const struct KeyTableEntry* usKey = &unknownKeyEntry;
+
+ if (keyCode < keyTableSize) {
+ usKey = &keyTable[keyCode];
+ }
+
+ if (isJIS) {
+ for (int i = 0; i < keyTableJISOverrideSize; ++i) {
+ if (keyTableJISOverride[i].keyCode == keyCode) {
+ usKey = &keyTableJISOverride[i];
+ break;
+ }
+ }
+ }
+
+ return usKey;
+}
+
+TISInputSourceRef GetCurrentUnderlyingLayout(BOOL useNationalLayouts) {
+ // TISCopyCurrentKeyboardLayoutInputSource() should always return a key layout
+ // that has valid unicode character data for use with the UCKeyTranslate() function.
+ // This is more robust than checking whether the current input source has key layout data
+ // and then falling back to the override input source if it doesn't. This is because some
+ // custom IMEs don't set the override input source properly.
+
+ TISInputSourceRef currentLayout = TISCopyCurrentKeyboardLayoutInputSource();
+ Boolean currentAscii = currentLayout == nil ? NO :
+ CFBooleanGetValue((CFBooleanRef) TISGetInputSourceProperty(currentLayout, kTISPropertyInputSourceIsASCIICapable));
+ TISInputSourceRef underlyingLayout = (!useNationalLayouts || currentAscii) ? currentLayout : nil;
+
+ if (currentLayout) {
+ if (underlyingLayout) {
+ CFAutorelease(currentLayout);
+ } else {
+ CFRelease(currentLayout);
+ }
+ }
+
+ return underlyingLayout;
+}
+
+struct KeyCodeTranslationResult TranslateKeyCodeUsingLayout(TISInputSourceRef layout, unsigned short keyCode)
{
- TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource();
- CFDataRef uchr = (CFDataRef)TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData);
- if (uchr == nil) { return 0; }
+ struct KeyCodeTranslationResult result = {
+ .character = (unichar)0,
+ .isSuccess = NO,
+ .isDead = NO,
+ .isTyped = NO
+ };
+
+ const struct KeyTableEntry* usKey = GetKeyTableEntryForKeyCode(keyCode);
+
+ if (usKey == &unknownKeyEntry) {
+ return result;
+ }
+
+ if (!usKey->variesBetweenLayouts) {
+ result.isSuccess = YES;
+ result.character = usKey->character;
+ if (result.character != 0 && !(result.character >= NSUpArrowFunctionKey && result.character <= NSModeSwitchFunctionKey)) {
+ result.isTyped = YES;
+ }
+ return result;
+ }
+
+ if (layout == nil) {
+ // use the US layout
+ result.isSuccess = YES;
+ if (usKey->character != 0) {
+ result.character = usKey->character;
+ result.isTyped = YES;
+ }
+ return result;
+ }
+
+ CFDataRef uchr = (CFDataRef)TISGetInputSourceProperty(layout, kTISPropertyUnicodeKeyLayoutData);
+ if (uchr == nil) {
+ return result;
+ }
const UCKeyboardLayout *keyboardLayout = (const UCKeyboardLayout*)CFDataGetBytePtr(uchr);
- // Carbon modifiers should be used instead of NSEvent modifiers
- UInt32 modifierKeyState = (GetCurrentEventKeyModifiers() >> 8) & 0xFF;
-
- if (keyboardLayout) {
- UInt32 deadKeyState = 0;
- UniCharCount maxStringLength = 255;
- UniCharCount actualStringLength = 0;
- UniChar unicodeString[maxStringLength];
-
- // get the deadKeyState
- OSStatus status = UCKeyTranslate(keyboardLayout,
- keyCode, kUCKeyActionDown, modifierKeyState,
- LMGetKbdType(), kUCKeyTranslateNoDeadKeysBit,
- &deadKeyState,
- maxStringLength,
- &actualStringLength, unicodeString);
-
- if (status == noErr && deadKeyState != 0) {
- // Press SPACE to get the dead key char
- status = UCKeyTranslate(keyboardLayout,
- kVK_Space, kUCKeyActionDown, 0,
- LMGetKbdType(), 0,
- &deadKeyState,
- maxStringLength,
- &actualStringLength, unicodeString);
-
- if (status == noErr && actualStringLength > 0) {
- return unicodeString[0];
- }
+ if (keyboardLayout == NULL) {
+ return result;
+ }
+
+ UInt32 modifierKeyState = 0;
+ UInt32 deadKeyState = 0;
+ const UniCharCount maxStringLength = 255;
+ UniCharCount actualStringLength = 0;
+ UniChar unicodeString[maxStringLength];
+
+ // get the deadKeyState
+ OSStatus status = UCKeyTranslate(keyboardLayout,
+ keyCode, kUCKeyActionDown, modifierKeyState,
+ LMGetKbdType(), 0,
+ &deadKeyState,
+ maxStringLength,
+ &actualStringLength, unicodeString);
+
+ if (status != noErr) {
+ return result;
+ }
+
+ if (deadKeyState == 0) {
+ result.isSuccess = YES;
+ result.isDead = NO;
+
+ if (actualStringLength > 0) {
+ result.isTyped = YES;
+
+ // This is the character that will determine the Java key code of this key.
+ // There are some keys (even on ASCII-capable key layouts) that produce more than one
+ // code point (not just more than one UTF-16 code unit mind you!). It's unclear how one
+ // would go around constructing an extended key code for these keys. Luckily, if we
+ // use the last code unit to construct the extended key codes, there won't be any collisions
+ // among the standard macOS ASCII-capable key layouts. That seems good enough to me.
+ result.character = unicodeString[actualStringLength - 1];
}
+
+ return result;
}
- return 0;
+
+ deadKeyState = 0;
+
+ // Extract the dead key non-combining character
+ status = UCKeyTranslate(keyboardLayout,
+ keyCode, kUCKeyActionDown, modifierKeyState,
+ LMGetKbdType(), kUCKeyTranslateNoDeadKeysMask,
+ &deadKeyState,
+ maxStringLength,
+ &actualStringLength, unicodeString);
+
+ if (status != noErr) {
+ return result;
+ }
+
+ result.isSuccess = YES;
+ result.isDead = YES;
+
+ if (actualStringLength > 0) {
+ result.character = unicodeString[actualStringLength - 1];
+ }
+
+ return result;
}
/*
@@ -426,83 +614,175 @@ static unichar NsGetDeadKeyChar(unsigned short keyCode)
* NSEvent keyCodes and translate to the Java virtual key code.
*/
static void
-NsCharToJavaVirtualKeyCode(unichar ch, BOOL isDeadChar,
- NSUInteger flags, unsigned short key,
- jint *keyCode, jint *keyLocation, BOOL *postsTyped,
- unichar *deadChar, jint *extendedkeyCode)
+NsCharToJavaVirtualKeyCode(unsigned short key, const BOOL useNationalLayouts,
+ const BOOL reportDeadKeysAsNormal,
+ jint *keyCode, jint *keyLocation)
{
- static size_t size = sizeof(keyTable) / sizeof(struct _key);
- NSInteger offset;
-
- if (isDeadChar) {
- unichar testDeadChar = NsGetDeadKeyChar(key);
- const struct CharToVKEntry *map;
- for (map = charToDeadVKTable; map->c != 0; ++map) {
- if (testDeadChar == map->c) {
+ // This is going to be a lengthy explanation about what it is that we need to achieve in this function.
+ // It took me quite a while to figure out myself, so hopefully it will be useful to others as well.
+ // I will describe the desired behavior when useNationalLayouts = true. Setting this parameter to false should
+ // ideally make the behavior identical to the one of OpenJDK, barring a few obvious bugfixes, like JBR-3860.
+ //
+ // For clarity here's what I mean by certain phrases:
+ // - Input source: what macOS calls "Keyboard layout input source", so excluding emoji pickers and handwriting
+ // - Key layout: Input source in the com.apple.keylayout namespace, i.e. a "simple" keyboard
+ // - IME: Input source in the com.apple.inputmethod namespace, i.e. a "complex" input method
+ // - Physical layout: A property of the physical keyboard device that has to do with the physical location of keys and their mapping to key codes
+ // - Underlying key layout: The key layout which actually translates the keys that the user presses to Java events.
+ // - Key code: A macOS virtual key code (the property `keyCode` on `NSEvent`)
+ // - Java key code: The values returned by `KeyEvent.getKeyCode()`
+ // - Key: Keyboard key without any modifiers
+ // - Combo: Keyboard key with modifiers
+ // - Dead key/combo: A key/combo that sets a dead key state when interpreted using UCKeyTranslate
+ //
+ // Whenever I refer to a key on the physical keyboard I will use the US layout.
+ //
+ // This function needs to determine the following:
+ // - Java key code
+ // - Java key location
+ //
+ // Obtaining the key location is fairly easy, since it can be looked up in the table by key code and physical layout type.
+ //
+ // To understand how to obtain the Java key code, let's take a look at the types of input sources that we want to handle:
+ // - Latin-based key layouts (ABC, German, French, Spanish, etc)
+ // - Non-latin-based key layouts (Arabic, Armenian, Russian, etc)
+ // - Latin-based IMEs (Pinyin, Cantonese - Phonetic, Japanese Romaji, etc.)
+ // - Non-latin-based IMEs (Korean, Zhuyin, Japanese Kana, etc.)
+ //
+ // These are possible physical layouts supported on macOS:
+ // - ANSI (North America, most of Asia and others)
+ // - ISO (Europe, Latin America, Middle East and others)
+ // - JIS (Japan)
+ //
+ // As a rule, any input source can be used on any physical layout.
+ // This might cause some key codes to correspond to different characters on the same input source.
+ //
+ // Basically we want the following behavior:
+ // - Latin-based key layouts should report their own keys unchanged.
+ // - Other input sources should report the key on their underlying key layout.
+ //
+ // Latin-based IMEs make it easy to determine the underlying key layout.
+ // macOS allows us to obtain a copy of the input source by calling TISCopyCurrentKeyboardLayoutInputSource().
+ // There's also the TISCopyInputMethodKeyboardLayoutOverride() function, but certain IMs (Google Japanese IME)
+ // don't set it properly.
+ //
+ // Non-latin-based key layouts and IMEs will use the US key layout as the underlying one.
+ // This is the behavior of native apps.
+ //
+ // Java has builtin key codes for most characters that can appear at the base layer of various key layouts.
+ // The rest are constructed like this: 0x01000000 + codePoint. All keys on builtin ASCII-capable layouts produce
+ // no surrogate pairs, but some of them can produce strings containing more than one code point. These need to be
+ // dealt with carefully as to avoid having different keys produce same Java key codes.
+ //
+ // Here's the various groups of named Java key codes that we need to handle:
+ // - Fixed keys that don't vary between input sources: VK_SPACE, VK_SHIFT, VK_NUMPAD0-VK_NUMPAD9, VK_F1-VK_F24, etc.
+ // - Dead keys: VK_DEAD_ACUTE, VK_DEAD_GRAVE, etc.
+ // - Punctuation: VK_PLUS, VK_SLASH, VK_SEMICOLON, etc.
+ // - Latin letters: VK_A-VK_Z
+ // - Numbers: VK_0-VK_9
+ //
+ // Fixed keys are hardcoded in keyTable and keyTableJISOverride.
+ //
+ // Dead keys need to be mapped into the corresponding VK_DEAD_ key codes in the same way the normal keys are mapped,
+ // that is using an underlying layout. This is done by using the UCKeyTranslate function together with charToDeadVKTable.
+ // It is possible to extract a (usually) non-combining dead key character by calling UCKeyTranslate
+ // with the kUCKeyTranslateNoDeadKeysMask option.
+ //
+ // Punctuation is hardcoded in extraCharToVKTable. Latin letters and numbers are dealt with separately.
+ //
+ // Bonus! What does it mean to have the "national layouts" disabled? In my opinion this simply means that
+ // the underlying key layout is the one that the user currently uses, or the override key layout for the input method
+ // that the user currently uses. I think this approach strikes the right balance between preserving compatibility
+ // with OpenJDK where it matters, while at the same time fixing a lot of annoying bugs.
+
+ // Find out which key does the key code correspond to in the US/ABC key layout.
+ // Need to take into account that the same virtual key code may correspond to
+ // different keys depending on the physical layout.
+
+ const struct KeyTableEntry* usKey = GetKeyTableEntryForKeyCode(key);
+
+ // Determine the underlying layout.
+ // If underlyingLayout is nil then fall back to using the usKey.
+
+ TISInputSourceRef underlyingLayout = GetCurrentUnderlyingLayout(useNationalLayouts);
+
+ // Default to returning the US key data.
+ *keyCode = usKey->javaKeyCode;
+ *keyLocation = usKey->javaKeyLocation;
+
+ if (underlyingLayout == nil || !usKey->variesBetweenLayouts) {
+ return;
+ }
+
+ // Translate the key using the underlying key layout.
+ struct KeyCodeTranslationResult translatedKey = TranslateKeyCodeUsingLayout(underlyingLayout, key);
+
+ // Test whether this key is dead.
+ if (translatedKey.isDead && !reportDeadKeysAsNormal) {
+ for (const struct CharToVKEntry *map = charToDeadVKTable; map->c != 0; ++map) {
+ if (translatedKey.character == map->c) {
*keyCode = map->javaKey;
- *postsTyped = NO;
- // TODO: use UNKNOWN here?
- *keyLocation = java_awt_event_KeyEvent_KEY_LOCATION_UNKNOWN;
- *deadChar = testDeadChar;
return;
}
}
- // If we got here, we keep looking for a normal key.
+
+ // No builtin VK_DEAD_ constant for this dead key,
+ // nothing better to do than to fall back to the extended key code.
+ // This can happen on the following ascii-capable key layouts:
+ // - Apache (com.apple.keylayout.Apache)
+ // - Chickasaw (com.apple.keylayout.Chickasaw)
+ // - Choctaw (com.apple.keylayout.Choctaw)
+ // - Navajo (com.apple.keylayout.Navajo)
+ // - Vietnamese (com.apple.keylayout.Vietnamese)
+ // Vietnamese layout is unique among these in that the "dead key" is actually a self-containing symbol,
+ // that can be modified by an accent typed after it. In essence, it's like a dead key in reverse:
+ // the user should first type the letter and only then the necessary accent.
+ // This way the key code would be what the user expects.
+
+ *keyCode = 0x1000000 + translatedKey.character;
+ return;
}
- if ([[NSCharacterSet letterCharacterSet] characterIsMember:ch]) {
- // key is an alphabetic character
- unichar lower;
- lower = tolower(ch);
- offset = lower - 'a';
- if (offset >= 0 && offset <= 25) {
- // checking for A-Z characters
- *postsTyped = YES;
- // do quick conversion
- *keyCode = java_awt_event_KeyEvent_VK_A + offset;
- *keyLocation = java_awt_event_KeyEvent_KEY_LOCATION_STANDARD;
+ unichar ch = 0;
+
+ if (translatedKey.isDead || translatedKey.isTyped) {
+ ch = translatedKey.character;
+ }
+
+ // Together with the following two checks (letters and digits) this table
+ // properly handles all keys that have corresponding VK_ codes.
+ // Unfortunately not all keys are like that. They are handled separately.
+
+ for (const struct CharToVKEntry *map = extraCharToVKTable; map->c != 0; ++map) {
+ if (map->c == ch) {
+ *keyCode = map->javaKey;
return;
- } else {
- // checking for non-english characters
- // this value comes from ExtendedKeyCodes.java
- offset += 0x01000000;
- *postsTyped = YES;
- // do quick conversion
- // the keyCode is off by 32, so adding it here
- *extendedkeyCode = java_awt_event_KeyEvent_VK_A + offset + 32;
- *keyLocation = java_awt_event_KeyEvent_KEY_LOCATION_STANDARD;
}
}
- if ([[NSCharacterSet decimalDigitCharacterSet] characterIsMember:ch]) {
+ if (ch >= 'a' && ch <= 'z') {
+ // key is a basic latin letter
+ *keyCode = java_awt_event_KeyEvent_VK_A + ch - 'a';
+ return;
+ }
+
+ if (ch >= '0' && ch <= '9') {
// key is a digit
- offset = ch - '0';
- // make sure in range for decimal digits
- if (offset >= 0 && offset <= 9) {
- jboolean numpad = ((flags & NSNumericPadKeyMask) &&
- (key > 81 && key < 93));
- *postsTyped = YES;
- if (numpad) {
- *keyCode = offset + java_awt_event_KeyEvent_VK_NUMPAD0;
- *keyLocation = java_awt_event_KeyEvent_KEY_LOCATION_NUMPAD;
- } else {
- *keyCode = offset + java_awt_event_KeyEvent_VK_0;
- *keyLocation = java_awt_event_KeyEvent_KEY_LOCATION_STANDARD;
- }
- return;
- }
+ // numpad digits are already handled, since they don't vary between layouts
+ *keyCode = java_awt_event_KeyEvent_VK_0 + ch - '0';
+ return;
}
- if (key < size) {
- *postsTyped = keyTable[key].postsTyped;
- *keyCode = keyTable[key].javaKeyCode;
- *keyLocation = keyTable[key].javaKeyLocation;
- } else {
- // Should we report this? This means we've got a keyboard
- // we don't know about...
- *postsTyped = NO;
- *keyCode = java_awt_event_KeyEvent_VK_UNDEFINED;
- *keyLocation = java_awt_event_KeyEvent_KEY_LOCATION_UNKNOWN;
+ if (useNationalLayouts || [[NSCharacterSet letterCharacterSet] characterIsMember:ch]) {
+ // If useNationalLayouts = false, then we only convert the key codes for letters here.
+ // This is the default behavior in OpenJDK and I don't think it's a good idea to change that.
+
+ // Otherwise we also need to report characters other than letters.
+ // If we ended up in this branch, this means that the character doesn't have its own VK_ code.
+ // Apart from letters, this is the case for characters like the Section Sign (U+00A7)
+ // on the French keyboard (key ANSI_6) or Pound Sign (U+00A3) on the Italian – QZERTY keyboard (key ANSI_8).
+
+ *keyCode = 0x01000000 + ch;
}
}
@@ -541,8 +821,6 @@ static unichar NsGetDeadKeyChar(unsigned short keyCode)
*javaKeyLocation = java_awt_event_KeyEvent_KEY_LOCATION_LEFT;
} else if (eventKeyCode == cur->rightKeyCode) {
*javaKeyLocation = java_awt_event_KeyEvent_KEY_LOCATION_RIGHT;
- } else if (cur->nsMask == NSAlternateKeyMask) {
- continue;
}
*javaKeyType = (cur->nsMask & nsFlags) ?
java_awt_event_KeyEvent_KEY_PRESSED :
@@ -673,47 +951,38 @@ jlong UTC(NSEvent *event) {
/*
* Class: sun_lwawt_macosx_NSEvent
* Method: nsToJavaKeyInfo
- * Signature: ([I[I)Z
+ * Signature: ([I[I)V
*/
-JNIEXPORT jboolean JNICALL
+JNIEXPORT void JNICALL
Java_sun_lwawt_macosx_NSEvent_nsToJavaKeyInfo
(JNIEnv *env, jclass cls, jintArray inData, jintArray outData)
{
- BOOL postsTyped = NO;
-
JNI_COCOA_ENTER(env);
jboolean copy = JNI_FALSE;
jint *data = (*env)->GetIntArrayElements(env, inData, ©);
- CHECK_NULL_RETURN(data, postsTyped);
+ CHECK_NULL(data);
- // in = [testChar, testDeadChar, modifierFlags, keyCode]
- jchar testChar = (jchar)data[0];
- BOOL isDeadChar = (data[1] != 0);
- jint modifierFlags = data[2];
- jshort keyCode = (jshort)data[3];
+ // in = [keyCode, useNationalLayouts]
+ jshort keyCode = (jshort)data[0];
+ BOOL useNationalLayouts = (data[1] != 0);
+ BOOL reportDeadKeysAsNormal = (data[2] != 0);
jint jkeyCode = java_awt_event_KeyEvent_VK_UNDEFINED;
- jint jextendedkeyCode = -1;
jint jkeyLocation = java_awt_event_KeyEvent_KEY_LOCATION_UNKNOWN;
- jint testDeadChar = 0;
- NsCharToJavaVirtualKeyCode((unichar)testChar, isDeadChar,
- (NSUInteger)modifierFlags, (unsigned short)keyCode,
- &jkeyCode, &jkeyLocation, &postsTyped,
- (unichar *) &testDeadChar, &jextendedkeyCode);
+ NsCharToJavaVirtualKeyCode((unsigned short)keyCode,
+ useNationalLayouts,
+ reportDeadKeysAsNormal,
+ &jkeyCode, &jkeyLocation);
- // out = [jkeyCode, jkeyLocation, deadChar, jextendedkeyCode];
+ // out = [jkeyCode, jkeyLocation];
(*env)->SetIntArrayRegion(env, outData, 0, 1, &jkeyCode);
(*env)->SetIntArrayRegion(env, outData, 1, 1, &jkeyLocation);
- (*env)->SetIntArrayRegion(env, outData, 2, 1, &testDeadChar);
- (*env)->SetIntArrayRegion(env, outData, 3, 1, &jextendedkeyCode);
(*env)->ReleaseIntArrayElements(env, inData, data, 0);
JNI_COCOA_EXIT(env);
-
- return postsTyped;
}
/*
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTView.h b/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTView.h
index 9f64001212c5..76b3dd77c772 100644
--- a/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTView.h
+++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTView.h
@@ -45,6 +45,7 @@
jobject fInputMethodLOCKABLE;
BOOL fKeyEventsNeeded;
BOOL fProcessingKeystroke;
+ NSString* actualCharacters;
BOOL fEnablePressAndHold;
BOOL fInPressAndHold;
@@ -66,6 +67,6 @@
// Input method-related events
- (void)setInputMethod:(jobject)inputMethod;
-- (void)abandonInput;
+- (void)abandonInput:(jobject) component;
@end
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTView.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTView.m
index c349df39dccd..1263d8e0f489 100644
--- a/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTView.m
+++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTView.m
@@ -26,15 +26,22 @@
#import "jni_util.h"
#import "CGLGraphicsConfig.h"
#import "AWTView.h"
+#import "AWTEvent.h"
#import "AWTWindow.h"
#import "a11y/CommonComponentAccessibility.h"
#import "JavaAccessibilityUtilities.h"
#import "GeomUtilities.h"
#import "ThreadUtilities.h"
#import "JNIUtilities.h"
+#import "jni_util.h"
+#import "PropertiesUtilities.h"
+#import "sun_lwawt_macosx_CPlatformWindow.h"
#import
+#define DEFAULT_FRAME_WIDTH 1504
+#define DEFAULT_FRAME_HEIGHT 846
+
// keyboard layout
static NSString *kbdLayout;
@@ -45,18 +52,20 @@ @interface AWTView()
-(void) deliverResize: (NSRect) rect;
-(void) resetTrackingArea;
-(void) deliverJavaKeyEventHelper: (NSEvent*) event;
--(BOOL) isCodePointInUnicodeBlockNeedingIMEvent: (unichar) codePoint;
-(NSMutableString *) parseString : (id) complexString;
@end
// Uncomment this line to see fprintfs of each InputMethod API being called on this View
//#define IM_DEBUG TRUE
//#define EXTRA_DEBUG
+//#define LOG_KEY_EVENTS
static BOOL shouldUsePressAndHold() {
return YES;
}
+extern bool isSystemShortcut_NextWindowInApplication(NSUInteger modifiersMask, int keyCode, NSString * chars);
+
@implementation AWTView
@synthesize _dropTarget;
@@ -141,6 +150,26 @@ - (void) viewDidMoveToWindow {
}
}
+- (void)setFrame:(NSRect)newFrame {
+ if (isnan(newFrame.origin.x)) {
+ newFrame.origin.x = 0;
+ }
+
+ if (isnan(newFrame.origin.y)) {
+ newFrame.origin.y = 0;
+ }
+
+ if (isnan(newFrame.size.width)) {
+ newFrame.size.width = DEFAULT_FRAME_WIDTH;
+ }
+
+ if (isnan(newFrame.size.height)) {
+ newFrame.size.height = DEFAULT_FRAME_HEIGHT;
+ }
+
+ [super setFrame:newFrame];
+}
+
- (BOOL) acceptsFirstMouse: (NSEvent *)event {
return YES;
}
@@ -170,7 +199,16 @@ - (void)resizeWithOldSuperviewSize:(NSSize)oldBoundsSize {
* MouseEvents support
*/
+- (BOOL)shouldDelayWindowOrderingForEvent:(NSEvent *)event {
+ return [self isWindowDisabled];
+}
+
- (void) mouseDown: (NSEvent *)event {
+ if ([self isWindowDisabled]) {
+ [NSApp preventWindowOrdering];
+ [NSApp activateIgnoringOtherApps:YES];
+ }
+
NSInputManager *inputManager = [NSInputManager currentInputManager];
if ([inputManager wantsToHandleMouseEvents]) {
#if IM_DEBUG
@@ -218,10 +256,19 @@ - (void) mouseMoved: (NSEvent *)event {
NSPoint localPoint = [self convertPoint: eventLocation fromView: nil];
if ([self mouse: localPoint inRect: [self bounds]]) {
- [self deliverJavaMouseEvent: event];
- } else {
- [[self nextResponder] mouseDown:event];
+ NSWindow* eventWindow = [event window];
+ NSPoint screenPoint = (eventWindow == nil)
+ ? eventLocation
+ // SDK we build against is too old - it doesn't have convertPointToScreen
+ : [eventWindow convertRectToScreen:NSMakeRect(eventLocation.x, eventLocation.y, 0, 0)].origin;
+ // macOS can report mouseMoved events to a window even if it's not showing currently (see JBR-2702)
+ // so we're performing an additional check here
+ if (self.window.windowNumber == [NSWindow windowNumberAtPoint:screenPoint belowWindowWithWindowNumber:0]) {
+ [self deliverJavaMouseEvent: event];
+ return;
+ }
}
+ [[self nextResponder] mouseMoved:event];
}
- (void) mouseDragged: (NSEvent *)event {
@@ -257,16 +304,74 @@ - (void) scrollWheel: (NSEvent*) event {
* KeyEvents support
*/
+#ifdef LOG_KEY_EVENTS
+static void debugPrintNSString(const char* name, NSString* s) {
+ if (s == nil) {
+ fprintf(stderr, "\t%s: nil\n", name);
+ return;
+ }
+ const char* utf8 = [s UTF8String];
+ int codeUnits = [s length];
+ int bytes = strlen(utf8);
+ fprintf(stderr, "\t%s: [utf8 = \"", name);
+ for (const unsigned char* c = (const unsigned char*)utf8; *c; ++c) {
+ if (*c >= 0x20 && *c <= 0x7e) {
+ fputc(*c, stderr);
+ } else {
+ fprintf(stderr, "\\x%02x", *c);
+ }
+ }
+ fprintf(stderr, "\", utf16 = \"");
+ for (int i = 0; i < codeUnits; ++i) {
+ int c = (int)[s characterAtIndex:i];
+ if (c >= 0x20 && c <= 0x7e) {
+ fputc(c, stderr);
+ } else {
+ fprintf(stderr, "\\u%04x", c);
+ }
+ }
+ fprintf(stderr, "\", bytes = %d, codeUnits = %d]\n", bytes, codeUnits);
+}
+
+static void debugPrintNSEvent(NSEvent* event, const char* comment) {
+ NSEventType type = [event type];
+ if (type == NSEventTypeKeyDown) {
+ fprintf(stderr, "[AWTView.m] keyDown in %s\n", comment);
+ } else if (type == NSEventTypeKeyUp) {
+ fprintf(stderr, "[AWTView.m] keyUp in %s\n", comment);
+ } else if (type == NSEventTypeFlagsChanged) {
+ fprintf(stderr, "[AWTView.m] flagsChanged in %s\n", comment);
+ } else {
+ fprintf(stderr, "[AWTView.m] unknown event %d in %s\n", (int)type, comment);
+ return;
+ }
+ if (type == NSEventTypeKeyDown || type == NSEventTypeKeyUp) {
+ debugPrintNSString("characters", [event characters]);
+ debugPrintNSString("charactersIgnoringModifiers", [event charactersIgnoringModifiers]);
+ fprintf(stderr, "\tkeyCode: %d (0x%02x)\n", [event keyCode], [event keyCode]);
+ }
+ fprintf(stderr, "\tmodifierFlags: 0x%08x\n", (unsigned)[event modifierFlags]);
+ TISInputSourceRef is = TISCopyCurrentKeyboardLayoutInputSource();
+ fprintf(stderr, "\tTISCopyCurrentKeyboardLayoutInputSource: %s\n", is == nil ? "(nil)" : [(NSString*) TISGetInputSourceProperty(is, kTISPropertyInputSourceID) UTF8String]);
+}
+#endif
+
- (void) keyDown: (NSEvent *)event {
+#ifdef LOG_KEY_EVENTS
+ debugPrintNSEvent(event, "keyDown");
+#endif
fProcessingKeystroke = YES;
fKeyEventsNeeded = YES;
+ NSString *eventCharacters = [event characters];
+
// Allow TSM to look at the event and potentially send back NSTextInputClient messages.
[self interpretKeyEvents:[NSArray arrayWithObject:event]];
if (fEnablePressAndHold && [event willBeHandledByComplexInputMethod] &&
fInputMethodLOCKABLE)
{
+ BOOL skipProcessingCancelKeys = YES;
fProcessingKeystroke = NO;
if (!fInPressAndHold) {
fInPressAndHold = YES;
@@ -276,51 +381,85 @@ - (void) keyDown: (NSEvent *)event {
// input accented symbols
switch([event keyCode]) {
- case kVK_Escape:
+ case kVK_ForwardDelete:
case kVK_Delete:
+ skipProcessingCancelKeys = NO;
case kVK_Return:
- case kVK_ForwardDelete:
+ case kVK_Escape:
case kVK_PageUp:
case kVK_PageDown:
case kVK_DownArrow:
case kVK_UpArrow:
case kVK_Home:
case kVK_End:
- [self abandonInput];
- break;
+ // Abandon input to reset IM and unblock input after
+ // canceling input accented symbols
+ [self abandonInput:nil];
+ break;
}
}
- return;
+ if (skipProcessingCancelKeys) {
+ return;
+ }
}
- NSString *eventCharacters = [event characters];
BOOL isDeadKey = (eventCharacters != nil && [eventCharacters length] == 0);
if ((![self hasMarkedText] && fKeyEventsNeeded) || isDeadKey) {
[self deliverJavaKeyEventHelper: event];
}
+ if (actualCharacters != nil) {
+ [actualCharacters release];
+ actualCharacters = nil;
+ }
+
fProcessingKeystroke = NO;
}
- (void) keyUp: (NSEvent *)event {
+#ifdef LOG_KEY_EVENTS
+ debugPrintNSEvent(event, "keyUp");
+#endif
[self deliverJavaKeyEventHelper: event];
}
- (void) flagsChanged: (NSEvent *)event {
+#ifdef LOG_KEY_EVENTS
+ debugPrintNSEvent(event, "flagsChanged");
+#endif
[self deliverJavaKeyEventHelper: event];
}
- (BOOL) performKeyEquivalent: (NSEvent *) event {
+#ifdef LOG_KEY_EVENTS
+ debugPrintNSEvent(event, "performKeyEquivalent");
+#endif
// if IM is active key events should be ignored
if (![self hasMarkedText] && !fInPressAndHold) {
[self deliverJavaKeyEventHelper: event];
}
+ const NSUInteger modFlags =
+ [event modifierFlags] & (NSCommandKeyMask | NSAlternateKeyMask | NSShiftKeyMask | NSControlKeyMask);
+
+ // Workaround for JBR-3544
+ // When tabbing mode is on (jdk.allowMacOSTabbedWindows=true) and "Ctrl Opt N" / "Cmd Opt N" is pressed,
+ // macOS first sends it, and immediately then sends "Ctrl N" / "Cmd N".
+ // The workaround is to "eat" (by returning TRUE) the "Ctrl Opt N" / "Cmd Opt N",
+ // so macOS won't send its "fallback" version ("Ctrl N" / "Cmd N").
+ if ([event keyCode] == kVK_ANSI_N) {
+ const NSUInteger ctrlOpt = (NSControlKeyMask | NSAlternateKeyMask);
+ const NSUInteger cmdOpt = (NSCommandKeyMask | NSAlternateKeyMask);
+
+ if ((modFlags == ctrlOpt) || (modFlags == cmdOpt)) {
+ [[NSApp mainMenu] performKeyEquivalent: event]; // just in case (as in the workaround for 8020209 below)
+ return YES;
+ }
+ }
+
// Workaround for 8020209: special case for "Cmd =" and "Cmd ."
// because Cocoa calls performKeyEquivalent twice for these keystrokes
- NSUInteger modFlags = [event modifierFlags] &
- (NSCommandKeyMask | NSAlternateKeyMask | NSShiftKeyMask | NSControlKeyMask);
if (modFlags == NSCommandKeyMask) {
NSString *eventChars = [event charactersIgnoringModifiers];
if ([eventChars length] == 1) {
@@ -333,6 +472,23 @@ - (BOOL) performKeyEquivalent: (NSEvent *) event {
}
+ JNIEnv *env = [ThreadUtilities getJNIEnvUncached];
+ NSString *captureNextAppWinKey = [PropertiesUtilities javaSystemPropertyForKey:@"apple.awt.captureNextAppWinKey"
+ withEnv:env];
+ if ([@"true" isCaseInsensitiveLike:captureNextAppWinKey]) {
+ NSUInteger deviceIndependentModifierFlagsMask =
+ [event modifierFlags] & NSDeviceIndependentModifierFlagsMask;
+ // Why translate the key code here and not just use event.characters?
+ // The default macOS shortcut for NextWindowInApplication is Cmd+Backtick. Pressing Cmd+Dead Grave also works
+ // for layouts that have the backtick as a dead key. Unfortunately, some (but notably not all) of these layouts
+ // consider Cmd+Dead Grave to also be a dead key, which means that event.characters will be an empty string.
+ // Explicitly translating the key code with a proper underlying key layout fixes this.
+ struct KeyCodeTranslationResult translationResult = TranslateKeyCodeUsingLayout(GetCurrentUnderlyingLayout(YES), [event keyCode]);
+ if (translationResult.isSuccess && translationResult.character) {
+ return isSystemShortcut_NextWindowInApplication(deviceIndependentModifierFlagsMask, [event keyCode], [NSString stringWithCharacters:&translationResult.character length:1]) ? YES : NO;
+ }
+ }
+
return NO;
}
@@ -340,14 +496,17 @@ - (BOOL) performKeyEquivalent: (NSEvent *) event {
* Utility methods and accessors
*/
--(void) deliverJavaMouseEvent: (NSEvent *) event {
- BOOL isEnabled = YES;
+-(BOOL) isWindowDisabled {
NSWindow* window = [self window];
if ([window isKindOfClass: [AWTWindow_Panel class]] || [window isKindOfClass: [AWTWindow_Normal class]]) {
- isEnabled = [(AWTWindow*)[window delegate] isEnabled];
+ return ![(AWTWindow*)[window delegate] isEnabled];
+ } else {
+ return NO;
}
+}
- if (!isEnabled) {
+-(void) deliverJavaMouseEvent: (NSEvent *) event {
+ if ([self isWindowDisabled]) {
return;
}
@@ -457,20 +616,22 @@ -(void) deliverJavaKeyEventHelper: (NSEvent *) event {
JNIEnv *env = [ThreadUtilities getJNIEnv];
jstring characters = NULL;
- jstring charactersIgnoringModifiers = NULL;
if ([event type] != NSEventTypeFlagsChanged) {
characters = NSStringToJavaString(env, [event characters]);
- charactersIgnoringModifiers = NSStringToJavaString(env, [event charactersIgnoringModifiers]);
+ }
+ jstring jActualCharacters = NULL;
+ if (actualCharacters != nil) {
+ jActualCharacters = NSStringToJavaString(env, actualCharacters);
}
DECLARE_CLASS(jc_NSEvent, "sun/lwawt/macosx/NSEvent");
DECLARE_METHOD(jctor_NSEvent, jc_NSEvent, "", "(IISLjava/lang/String;Ljava/lang/String;)V");
jobject jEvent = (*env)->NewObject(env, jc_NSEvent, jctor_NSEvent,
- [event type],
- [event modifierFlags],
- [event keyCode],
- characters,
- charactersIgnoringModifiers);
+ [event type],
+ [event modifierFlags],
+ [event keyCode],
+ characters,
+ jActualCharacters);
CHECK_NULL(jEvent);
DECLARE_CLASS(jc_PlatformView, "sun/lwawt/macosx/CPlatformView");
@@ -485,6 +646,9 @@ -(void) deliverJavaKeyEventHelper: (NSEvent *) event {
if (characters != NULL) {
(*env)->DeleteLocalRef(env, characters);
}
+ if (jActualCharacters != NULL) {
+ (*env)->DeleteLocalRef(env, jActualCharacters);
+ }
(*env)->DeleteLocalRef(env, jEvent);
}
@@ -543,21 +707,6 @@ - (void) drawRect:(NSRect)dirtyRect {
}
}
--(BOOL) isCodePointInUnicodeBlockNeedingIMEvent: (unichar) codePoint {
- if ((codePoint == 0x0024) || (codePoint == 0x00A3) ||
- (codePoint == 0x00A5) ||
- ((codePoint >= 0x900) && (codePoint <= 0x97F)) ||
- ((codePoint >= 0x20A3) && (codePoint <= 0x20BF)) ||
- ((codePoint >= 0x3000) && (codePoint <= 0x303F)) ||
- ((codePoint >= 0xFF00) && (codePoint <= 0xFFEF))) {
- // Code point is in 'CJK Symbols and Punctuation' or
- // 'Halfwidth and Fullwidth Forms' Unicode block or
- // currency symbols unicode or Devanagari script
- return YES;
- }
- return NO;
-}
-
-(NSMutableString *) parseString : (id) complexString {
if ([complexString isKindOfClass:[NSString class]]) {
return [complexString mutableCopy];
@@ -610,6 +759,10 @@ + (AWTView *) awtView:(JNIEnv*)env ofAccessible:(jobject)jaccessible
- (id)getAxData:(JNIEnv*)env
{
+ NSString *a11yEnabledProp = [PropertiesUtilities javaSystemPropertyForKey:@"sun.awt.mac.a11y.enabled" withEnv:env];
+ if ([@"false" isCaseInsensitiveLike:a11yEnabledProp]) {
+ return nil;
+ }
jobject jcomponent = [self awtComponent:env];
id ax = [[[CommonComponentAccessibility alloc] initWithParent:self withEnv:env withAccessible:jcomponent withIndex:-1 withView:self withJavaRole:nil] autorelease];
(*env)->DeleteLocalRef(env, jcomponent);
@@ -698,7 +851,7 @@ - (NSData *)accessibleSelectedTextAsRTFD
- (BOOL)replaceAccessibleTextSelection:(NSString *)text
{
id focused = [self accessibilityFocusedUIElement];
- if (![focused respondsToSelector:@selector(setAccessibilitySelectedText)]) return NO;
+ if (![focused respondsToSelector:@selector(setAccessibilitySelectedText:)]) return NO;
[focused setAccessibilitySelectedText:text];
return YES;
}
@@ -775,9 +928,11 @@ - (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)flag
CDragSource *dragSource = self._dragSource;
NSDragOperation dragOp = NSDragOperationNone;
- if (dragSource != nil) {
+ if (dragSource != nil)
dragOp = [dragSource draggingSourceOperationMaskForLocal:flag];
- }
+ else if ([super respondsToSelector:@selector(draggingSourceOperationMaskForLocal:)])
+ dragOp = [super draggingSourceOperationMaskForLocal:flag];
+
return dragOp;
}
@@ -787,9 +942,11 @@ - (NSArray *)namesOfPromisedFilesDroppedAtDestination:(NSURL *)dropDestination
CDragSource *dragSource = self._dragSource;
NSArray* array = nil;
- if (dragSource != nil) {
+ if (dragSource != nil)
array = [dragSource namesOfPromisedFilesDroppedAtDestination:dropDestination];
- }
+ else if ([super respondsToSelector:@selector(namesOfPromisedFilesDroppedAtDestination:)])
+ array = [super namesOfPromisedFilesDroppedAtDestination:dropDestination];
+
return array;
}
@@ -829,9 +986,11 @@ - (BOOL)ignoreModifierKeysWhileDragging
CDragSource *dragSource = self._dragSource;
BOOL result = FALSE;
- if (dragSource != nil) {
+ if (dragSource != nil)
result = [dragSource ignoreModifierKeysWhileDragging];
- }
+ else if ([super respondsToSelector:@selector(ignoreModifierKeysWhileDragging)])
+ result = [super ignoreModifierKeysWhileDragging];
+
return result;
}
@@ -845,9 +1004,11 @@ - (NSDragOperation)draggingEntered:(id )sender
CDropTarget *dropTarget = self._dropTarget;
NSDragOperation dragOp = NSDragOperationNone;
- if (dropTarget != nil) {
+ if (dropTarget != nil)
dragOp = [dropTarget draggingEntered:sender];
- }
+ else if ([super respondsToSelector:@selector(draggingEntered:)])
+ dragOp = [super draggingEntered:sender];
+
return dragOp;
}
@@ -857,9 +1018,11 @@ - (NSDragOperation)draggingUpdated:(id )sender
CDropTarget *dropTarget = self._dropTarget;
NSDragOperation dragOp = NSDragOperationNone;
- if (dropTarget != nil) {
+ if (dropTarget != nil)
dragOp = [dropTarget draggingUpdated:sender];
- }
+ else if ([super respondsToSelector:@selector(draggingUpdated:)])
+ dragOp = [super draggingUpdated:sender];
+
return dragOp;
}
@@ -879,9 +1042,11 @@ - (BOOL)prepareForDragOperation:(id )sender
CDropTarget *dropTarget = self._dropTarget;
BOOL result = FALSE;
- if (dropTarget != nil) {
+ if (dropTarget != nil)
result = [dropTarget prepareForDragOperation:sender];
- }
+ else if ([super respondsToSelector:@selector(prepareForDragOperation:)])
+ result = [super prepareForDragOperation:sender];
+
return result;
}
@@ -891,9 +1056,11 @@ - (BOOL)performDragOperation:(id )sender
CDropTarget *dropTarget = self._dropTarget;
BOOL result = FALSE;
- if (dropTarget != nil) {
+ if (dropTarget != nil)
result = [dropTarget performDragOperation:sender];
- }
+ else if ([super respondsToSelector:@selector(performDragOperation:)])
+ result = [super performDragOperation:sender];
+
return result;
}
@@ -930,6 +1097,43 @@ - (void)draggingEnded:(id )sender
#define GET_CIM_CLASS_RETURN(ret) \
GET_CLASS_RETURN(jc_CInputMethod, "sun/lwawt/macosx/CInputMethod", ret);
+- (NSInteger) windowLevel
+{
+#ifdef IM_DEBUG
+ fprintf(stderr, "AWTView InputMethod Selector Called : [windowLevel]\n");
+#endif // IM_DEBUG
+
+ NSWindow* const ownerWindow = [self window];
+ if (ownerWindow == nil) {
+ return NSNormalWindowLevel;
+ }
+
+ const NSWindowLevel ownerWindowLevel = [ownerWindow level];
+ if ( (ownerWindowLevel != NSNormalWindowLevel) && (ownerWindowLevel != NSFloatingWindowLevel) ) {
+ // the window level has been overridden, let's believe it
+ return ownerWindowLevel;
+ }
+
+ AWTWindow* const delegate = (AWTWindow*)[ownerWindow delegate];
+ if (delegate == nil) {
+ return ownerWindowLevel;
+ }
+
+ const jint styleBits = [delegate styleBits];
+
+ const BOOL isPopup = ( (styleBits & sun_lwawt_macosx_CPlatformWindow_IS_POPUP) != 0 );
+ if (isPopup) {
+ return NSPopUpMenuWindowLevel;
+ }
+
+ const BOOL isModal = ( (styleBits & sun_lwawt_macosx_CPlatformWindow_IS_MODAL) != 0 );
+ if (isModal) {
+ return NSFloatingWindowLevel;
+ }
+
+ return ownerWindowLevel;
+}
+
- (void) insertText:(id)aString replacementRange:(NSRange)replacementRange
{
#ifdef IM_DEBUG
@@ -950,63 +1154,48 @@ - (void) insertText:(id)aString replacementRange:(NSRange)replacementRange
// Unicode value.
NSMutableString * useString = [self parseString:aString];
+ BOOL usingComplexIM = [self hasMarkedText] || !fProcessingKeystroke;
+
+#ifdef IM_DEBUG
NSUInteger utf16Length = [useString lengthOfBytesUsingEncoding:NSUTF16StringEncoding];
NSUInteger utf8Length = [useString lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
- BOOL aStringIsComplex = NO;
- unichar codePoint = [useString characterAtIndex:0];
-
-#ifdef IM_DEBUG
NSLog(@"insertText kbdlayout %@ ",(NSString *)kbdLayout);
NSLog(@"utf8Length %lu utf16Length %lu", (unsigned long)utf8Length, (unsigned long)utf16Length);
- NSLog(@"codePoint %x", codePoint);
#endif // IM_DEBUG
- if ((utf16Length > 2) ||
- ((utf8Length > 1) && [self isCodePointInUnicodeBlockNeedingIMEvent:codePoint]) ||
- ((codePoint == 0x5c) && ([(NSString *)kbdLayout containsString:@"Kotoeri"]))) {
-#ifdef IM_DEBUG
- NSLog(@"string complex ");
-#endif
- aStringIsComplex = YES;
- }
-
- if ([self hasMarkedText] || !fProcessingKeystroke || aStringIsComplex) {
- JNIEnv *env = [ThreadUtilities getJNIEnv];
+ JNIEnv *env = [ThreadUtilities getJNIEnv];
- GET_CIM_CLASS();
+ GET_CIM_CLASS();
+ // We need to select the previous glyph so that it is overwritten.
+ if (fPAHNeedsToSelect) {
DECLARE_METHOD(jm_selectPreviousGlyph, jc_CInputMethod, "selectPreviousGlyph", "()V");
- // We need to select the previous glyph so that it is overwritten.
- if (fPAHNeedsToSelect) {
- (*env)->CallVoidMethod(env, fInputMethodLOCKABLE, jm_selectPreviousGlyph);
- CHECK_EXCEPTION();
- fPAHNeedsToSelect = NO;
- }
+ (*env)->CallVoidMethod(env, fInputMethodLOCKABLE, jm_selectPreviousGlyph);
+ CHECK_EXCEPTION();
+ fPAHNeedsToSelect = NO;
+ }
+ if (usingComplexIM) {
DECLARE_METHOD(jm_insertText, jc_CInputMethod, "insertText", "(Ljava/lang/String;)V");
jstring insertedText = NSStringToJavaString(env, useString);
(*env)->CallVoidMethod(env, fInputMethodLOCKABLE, jm_insertText, insertedText);
CHECK_EXCEPTION();
(*env)->DeleteLocalRef(env, insertedText);
-
- // The input method event will create psuedo-key events for each character in the committed string.
- // We also don't want to send the character that triggered the insertText, usually a return. [3337563]
fKeyEventsNeeded = NO;
- }
- else {
- // Need to set back the fKeyEventsNeeded flag so that the string following the
- // marked text is not ignored by keyDown
- if ([useString length] > 0) {
- fKeyEventsNeeded = YES;
+ } else {
+ if (actualCharacters != nil) {
+ [actualCharacters release];
}
+ actualCharacters = [useString copy];
+ fKeyEventsNeeded = YES;
}
fPAHNeedsToSelect = NO;
// Abandon input to reset IM and unblock input after entering accented
// symbols
- [self abandonInput];
+ [self abandonInput:nil];
}
+ (void)keyboardInputSourceChanged:(NSNotification *)notification
@@ -1100,7 +1289,11 @@ - (void) setMarkedText:(id)aString selectedRange:(NSRange)selectionRange replace
}
}
-- (void) unmarkText
+- (void) unmarkText {
+ [self unmarkText:nil];
+}
+
+- (void) unmarkText:(jobject) component
{
#ifdef IM_DEBUG
fprintf(stderr, "AWTView InputMethod Selector Called : [unmarkText]\n");
@@ -1113,8 +1306,8 @@ - (void) unmarkText
// unmarkText cancels any input in progress and commits it to the text field.
JNIEnv *env = [ThreadUtilities getJNIEnv];
GET_CIM_CLASS();
- DECLARE_METHOD(jm_unmarkText, jc_CInputMethod, "unmarkText", "()V");
- (*env)->CallVoidMethod(env, fInputMethodLOCKABLE, jm_unmarkText);
+ DECLARE_METHOD(jm_unmarkText, jc_CInputMethod, "unmarkText", "(Ljava/awt/Component;)V");
+ (*env)->CallVoidMethod(env, fInputMethodLOCKABLE, jm_unmarkText, component);
CHECK_EXCEPTION();
}
@@ -1174,6 +1367,9 @@ - (NSAttributedString *) attributedSubstringForProposedRange:(NSRange)theRange a
jobject theString = (*env)->CallObjectMethod(env, fInputMethodLOCKABLE, jm_substringFromRange, theRange.location, theRange.length);
CHECK_EXCEPTION_NULL_RETURN(theString, nil);
+ if (!theString) {
+ return NULL;
+ }
id result = [[[NSAttributedString alloc] initWithString:JavaStringToNSString(env, theString)] autorelease];
#ifdef IM_DEBUG
NSLog(@"attributedSubstringFromRange returning \"%@\"", result);
@@ -1372,20 +1568,41 @@ - (void)setInputMethod:(jobject)inputMethod
object:nil];
}
-- (void)abandonInput
+- (void)abandonInput:(jobject) component
{
#ifdef IM_DEBUG
fprintf(stderr, "AWTView InputMethod Selector Called : [abandonInput]\n");
#endif // IM_DEBUG
[ThreadUtilities performOnMainThread:@selector(markedTextAbandoned:) on:[NSInputManager currentInputManager] withObject:self waitUntilDone:YES];
- [self unmarkText];
+ [self unmarkText:component];
}
/******************************** END NSTextInputClient Protocol ********************************/
+- (void)viewDidChangeBackingProperties {
+ JNIEnv *env = [ThreadUtilities getJNIEnv];
+ if ((*env)->IsSameObject(env, m_cPlatformView, NULL)) {
+ return;
+ }
+ static double debugScale = -2.0;
+ if (debugScale == -2.0) { // default debugScale value in SGE is -1.0
+ debugScale = JNU_CallStaticMethodByName(env, NULL, "sun/java2d/SunGraphicsEnvironment",
+ "getDebugScale", "()D").d;
+ }
-
+ if (self.window.backingScaleFactor > 0 && debugScale < 0) {
+ if (self.layer.contentsScale != self.window.backingScaleFactor) {
+ self.layer.contentsScale = self.window.backingScaleFactor;
+ DECLARE_CLASS(jc_CPlatformView, "sun/lwawt/macosx/CPlatformView");
+ DECLARE_METHOD(deliverChangeBackingProperties, jc_CPlatformView, "deliverChangeBackingProperties", "(F)V");
+ jobject jlocal = (*env)->NewLocalRef(env, m_cPlatformView);
+ (*env)->CallVoidMethod(env, jlocal, deliverChangeBackingProperties, self.window.backingScaleFactor);
+ CHECK_EXCEPTION();
+ (*env)->DeleteLocalRef(env, jlocal);
+ }
+ }
+}
@end // AWTView
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTWindow.h b/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTWindow.h
index 7d6fb4f3ebcf..be4edfa521fb 100644
--- a/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTWindow.h
+++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTWindow.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -46,7 +46,14 @@
jint preFullScreenLevel;
NSRect standardFrame;
BOOL isMinimizing;
- BOOL keyNotificationRecd;
+ BOOL isJustCreated;
+ NSWindowTabbingMode javaWindowTabbingMode;
+ BOOL isEnterFullScreen;
+ CGFloat _customTitleBarHeight;
+ BOOL hideTabController;
+ NSView *_fullScreenButtons;
+ NSView *_fullScreenOriginalButtons;
+
}
// An instance of either AWTWindow_Normal or AWTWindow_Panel
@@ -62,7 +69,16 @@
@property (nonatomic) jint preFullScreenLevel;
@property (nonatomic) NSRect standardFrame;
@property (nonatomic) BOOL isMinimizing;
-@property (nonatomic) BOOL keyNotificationRecd;
+@property (nonatomic) BOOL isJustCreated;
+@property (nonatomic) NSWindowTabbingMode javaWindowTabbingMode;
+@property (nonatomic) BOOL isEnterFullScreen;
+@property (nonatomic, retain) NSNumber *currentDisplayID;
+@property (nonatomic, readonly) CGFloat customTitleBarHeight;
+@property (nonatomic) BOOL customTitleBarControlsVisible;
+@property (nonatomic, retain) NSMutableArray *customTitleBarConstraints;
+@property (nonatomic, retain) NSLayoutConstraint *customTitleBarHeightConstraint;
+@property (nonatomic, retain) NSMutableArray *customTitleBarButtonCenterXConstraints;
+@property (nonatomic) BOOL hideTabController;
- (id) initWithPlatformWindow:(jobject)javaPlatformWindow
ownerWindow:owner
@@ -72,8 +88,11 @@
- (BOOL) isTopmostWindowUnderMouse;
+- (NSWindowTabbingMode) getJavaWindowTabbingMode;
+
// NSWindow overrides delegate methods
- (BOOL) canBecomeKeyWindow;
+- (void) becomeKeyWindow;
- (BOOL) canBecomeMainWindow;
- (BOOL) worksWhenModal;
- (void)sendEvent:(NSEvent *)event;
@@ -83,7 +102,9 @@
@end
-@interface AWTWindow_Normal : NSWindow
+@interface AWTWindow_Normal : NSWindow {
+ @private BOOL _ignoreMove;
+}
- (id) initWithDelegate:(AWTWindow *)delegate
frameRect:(NSRect)rect
styleMask:(NSUInteger)styleMask
@@ -97,4 +118,18 @@
contentView:(NSView *)view;
@end
+@interface AWTWindowDragView : NSView
+@property (nonatomic) jobject javaPlatformWindow;
+- (id) initWithPlatformWindow:(jobject)javaPlatformWindow;
+@end
+
+@interface AWTButtonsView : NSView {
+ @private BOOL _showButtons;
+ NSColor* _color;
+}
+
+- (void)configureColors;
+
+@end
+
#endif _AWTWINDOW_H
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTWindow.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTWindow.m
index 77d1e216a75c..da84ac6716d0 100644
--- a/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTWindow.m
+++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTWindow.m
@@ -23,8 +23,10 @@
* questions.
*/
+#include
#import
+#include
#import "sun_lwawt_macosx_CPlatformWindow.h"
#import "com_apple_eawt_event_GestureHandler.h"
#import "com_apple_eawt_FullScreenHandler.h"
@@ -34,7 +36,9 @@
#import "AWTView.h"
#import "GeomUtilities.h"
#import "ThreadUtilities.h"
+#import "NSApplicationAWT.h"
#import "JNIUtilities.h"
+#import "PropertiesUtilities.h"
#define MASK(KEY) \
(sun_lwawt_macosx_CPlatformWindow_ ## KEY)
@@ -52,6 +56,16 @@
#define GET_CPLATFORM_WINDOW_CLASS_RETURN(ret) \
GET_CLASS_RETURN(jc_CPlatformWindow, "sun/lwawt/macosx/CPlatformWindow", ret);
+@interface NSTitlebarAccessoryViewController (Private)
+- (void)_setHidden:(BOOL)h animated:(BOOL)a;
+@end
+
+@interface NSWindow (Private)
+- (void)_setTabBarAccessoryViewController:(id)controller;
+- (void)setIgnoreMove:(BOOL)value;
+- (void)_adjustWindowToScreen;
+@end
+
// Cocoa windowDidBecomeKey/windowDidResignKey notifications
// doesn't provide information about "opposite" window, so we
// have to do a bit of tracking. This variable points to a window
@@ -65,6 +79,11 @@
// It would be NSZeroPoint if 'Location by Platform' is not used.
static NSPoint lastTopLeftPoint;
+static BOOL ignoreResizeWindowDuringAnotherWindowEnd = NO;
+
+static BOOL fullScreenTransitionInProgress = NO;
+static BOOL orderingScheduled = NO;
+
// --------------------------------------------------------------
// NSWindow/NSPanel descendants implementation
#define AWT_NS_WINDOW_IMPLEMENTATION \
@@ -85,7 +104,15 @@ - (id) initWithDelegate:(AWTWindow *)delegate \
[self setInitialFirstResponder:view]; \
[self setReleasedWhenClosed:NO]; \
[self setPreservesContentDuringLiveResize:YES]; \
- \
+ [[NSNotificationCenter defaultCenter] addObserver:self \
+ selector:@selector(windowDidChangeScreen) \
+ name:NSWindowDidChangeScreenNotification object:self]; \
+ [[NSNotificationCenter defaultCenter] addObserver:self \
+ selector:@selector(windowDidChangeProfile) \
+ name:NSWindowDidChangeScreenProfileNotification \
+ object:self]; \
+ [self addObserver:self forKeyPath:@"visible" \
+ options:NSKeyValueObservingOptionNew context:nil]; \
return self; \
} \
\
@@ -102,14 +129,83 @@ - (BOOL) worksWhenModal { \
return [(AWTWindow*)[self delegate] worksWhenModal]; \
} \
\
+- (void)cursorUpdate:(NSEvent *)event { \
+ /* Prevent cursor updates from OS side */ \
+} \
+ \
- (void)sendEvent:(NSEvent *)event { \
[(AWTWindow*)[self delegate] sendEvent:event]; \
[super sendEvent:event]; \
-}
+} \
+ \
+- (void)becomeKeyWindow { \
+ [super becomeKeyWindow]; \
+ [(AWTWindow*)[self delegate] becomeKeyWindow]; \
+} \
+ \
+- (NSWindowTabbingMode)tabbingMode { \
+ return ((AWTWindow*)[self delegate]).javaWindowTabbingMode; \
+} \
+ \
+- (void)observeValueForKeyPath:(NSString *)keyPath \
+ ofObject:(id)object \
+ change:(NSDictionary *)change \
+ context:(void *)context { \
+ if ([keyPath isEqualToString:@"visible"]) { \
+ BOOL isVisible = \
+ [[change objectForKey:NSKeyValueChangeNewKey] \
+ boolValue]; \
+ if (isVisible) { \
+ [(AWTWindow*)[self delegate] \
+ _windowDidBecomeVisible]; \
+ } \
+ } \
+} \
+ \
+- (void)windowDidChangeScreen { \
+ [(AWTWindow*)[self delegate] _displayChanged:NO]; \
+} \
+ \
+- (void)windowDidChangeProfile { \
+ [(AWTWindow*)[self delegate] _displayChanged:YES]; \
+} \
+ \
+- (void)dealloc { \
+ [[NSNotificationCenter defaultCenter] removeObserver:self \
+ name:NSWindowDidChangeScreenNotification object:self]; \
+ [[NSNotificationCenter defaultCenter] removeObserver:self \
+ name:NSWindowDidChangeScreenProfileNotification \
+ object:self]; \
+ [self removeObserver:self forKeyPath:@"visible"]; \
+ [super dealloc]; \
+} \
@implementation AWTWindow_Normal
AWT_NS_WINDOW_IMPLEMENTATION
+// suppress exception (actually assertion) from [NSWindow _changeJustMain]
+// workaround for https://youtrack.jetbrains.com/issue/JBR-2562
+- (void)_changeJustMain {
+ @try {
+ // NOTE: we can't use [super _changeJustMain] directly because of the warning ('may not perform to selector')
+ // And [super performSelector:@selector(_changeJustMain)] will invoke this method (not a base method).
+ // So do it with objc-runtime.h (see stackoverflow.com/questions/14635024/using-objc-msgsendsuper-to-invoke-a-class-method)
+ Class superClass = [self superclass];
+ struct objc_super mySuper = {
+ self,
+ class_isMetaClass(object_getClass(self)) //check if we are an instance or Class
+ ? object_getClass(superClass) //if we are a Class, we need to send our metaclass (our Class's Class)
+ : superClass //if we are an instance, we need to send our Class (which we already have)
+ };
+ void (*_objc_msgSendSuper)(struct objc_super *, SEL) = (void *)&objc_msgSendSuper; //cast our pointer so the compiler can sort out the ABI
+ (*_objc_msgSendSuper)(&mySuper, @selector(_changeJustMain));
+ } @catch (NSException *ex) {
+ NSLog(@"WARNING: suppressed exception from _changeJustMain (workaround for JBR-2562)");
+ NSProcessInfo *processInfo = [NSProcessInfo processInfo];
+ [NSApplicationAWT logException:ex forProcess:processInfo];
+ }
+}
+
// Gesture support
- (void)postGesture:(NSEvent *)event as:(jint)type a:(jdouble)a b:(jdouble)b {
AWT_ASSERT_APPKIT_THREAD;
@@ -139,21 +235,35 @@ - (void)postGesture:(NSEvent *)event as:(jint)type a:(jdouble)a b:(jdouble)b {
}
}
-- (void)beginGestureWithEvent:(NSEvent *)event {
- [self postGesture:event
- as:com_apple_eawt_event_GestureHandler_PHASE
- a:-1.0
- b:0.0];
-}
-
-- (void)endGestureWithEvent:(NSEvent *)event {
- [self postGesture:event
- as:com_apple_eawt_event_GestureHandler_PHASE
- a:1.0
- b:0.0];
+- (BOOL)postPhaseEvent:(NSEvent *)event {
+ // Consider changing API to reflect MacOS api
+ // Gesture event should come with phase field
+ // PhaseEvent should be removed
+ const unsigned int NSEventPhaseBegan = 0x1 << 0;
+ const unsigned int NSEventPhaseEnded = 0x1 << 3;
+ const unsigned int NSEventPhaseCancelled = 0x1 << 4;
+
+ if (event.phase == NSEventPhaseBegan) {
+ [self postGesture:event
+ as:com_apple_eawt_event_GestureHandler_PHASE
+ a:-1.0
+ b:0.0];
+ return true;
+ } else if (event.phase == NSEventPhaseEnded ||
+ event.phase == NSEventPhaseCancelled) {
+ [self postGesture:event
+ as:com_apple_eawt_event_GestureHandler_PHASE
+ a:1.0
+ b:0.0];
+ return true;
+ }
+ return false;
}
- (void)magnifyWithEvent:(NSEvent *)event {
+ if ([self postPhaseEvent:event]) {
+ return;
+ }
[self postGesture:event
as:com_apple_eawt_event_GestureHandler_MAGNIFY
a:[event magnification]
@@ -161,6 +271,9 @@ - (void)magnifyWithEvent:(NSEvent *)event {
}
- (void)rotateWithEvent:(NSEvent *)event {
+ if ([self postPhaseEvent:event]) {
+ return;
+ }
[self postGesture:event
as:com_apple_eawt_event_GestureHandler_ROTATE
a:[event rotation]
@@ -174,6 +287,116 @@ - (void)swipeWithEvent:(NSEvent *)event {
b:[event deltaY]];
}
+- (void)pressureChangeWithEvent:(NSEvent *)event {
+ float pressure = event.pressure;
+ [self postGesture:event
+ as:com_apple_eawt_event_GestureHandler_PRESSURE
+ a:pressure
+ b:(([event respondsToSelector:@selector(stage)]) ? ((NSInteger)[event stage]) : -1)
+ ];
+}
+
+- (void)moveTabToNewWindow:(id)sender {
+ AWT_ASSERT_APPKIT_THREAD;
+
+ [super moveTabToNewWindow:sender];
+
+ JNIEnv *env = [ThreadUtilities getJNIEnv];
+ jobject platformWindow = (*env)->NewLocalRef(env, ((AWTWindow *)self.delegate).javaPlatformWindow);
+ if (platformWindow != NULL) {
+ // extract the target AWT Window object out of the CPlatformWindow
+ GET_CPLATFORM_WINDOW_CLASS();
+ DECLARE_FIELD(jf_target, jc_CPlatformWindow, "target", "Ljava/awt/Window;");
+ jobject awtWindow = (*env)->GetObjectField(env, platformWindow, jf_target);
+ if (awtWindow != NULL) {
+ DECLARE_CLASS(jc_Window, "java/awt/Window");
+ DECLARE_METHOD(jm_runMoveTabToNewWindowCallback, jc_Window, "runMoveTabToNewWindowCallback", "()V");
+ (*env)->CallVoidMethod(env, awtWindow, jm_runMoveTabToNewWindowCallback);
+ CHECK_EXCEPTION();
+ (*env)->DeleteLocalRef(env, awtWindow);
+ }
+ (*env)->DeleteLocalRef(env, platformWindow);
+ }
+
+#ifdef DEBUG
+ NSLog(@"=== Move Tab to new Window ===");
+#endif
+}
+
+// Call over Foundation from Java
+- (CGFloat) getTabBarVisibleAndHeight {
+ if (@available(macOS 10.13, *)) {
+ id tabGroup = [self tabGroup];
+#ifdef DEBUG
+ NSLog(@"=== Window tabBar: %@ ===", tabGroup);
+#endif
+ if ([tabGroup isTabBarVisible]) {
+ if ([tabGroup respondsToSelector:@selector(_tabBar)]) { // private member
+ CGFloat height = [[tabGroup _tabBar] frame].size.height;
+#ifdef DEBUG
+ NSLog(@"=== Window tabBar visible: %f ===", height);
+#endif
+ return height;
+ }
+#ifdef DEBUG
+ NSLog(@"=== NsWindow.tabGroup._tabBar not found ===");
+#endif
+ return -1; // if we don't get height return -1 and use default value in java without change native code
+ }
+#ifdef DEBUG
+ NSLog(@"=== Window tabBar not visible ===");
+#endif
+ } else {
+#ifdef DEBUG
+ NSLog(@"=== Window tabGroup not supported before macOS 10.13 ===");
+#endif
+ }
+ return 0;
+}
+
+- (void)orderOut:(id)sender {
+ ignoreResizeWindowDuringAnotherWindowEnd = YES;
+ [super orderOut:sender];
+}
+
+- (void)_setTabBarAccessoryViewController:(id)_controller {
+ if (((AWTWindow *)self.delegate).hideTabController) {
+ NSTitlebarAccessoryViewController* controller = [[NSTitlebarAccessoryViewController alloc] init];
+ controller.view = [[NSView alloc] init];
+ [controller.view setFrame:NSMakeRect(0, 0, 0, 0)];
+ [controller _setHidden:YES animated:NO];
+
+ [super _setTabBarAccessoryViewController:controller];
+ } else {
+ [super _setTabBarAccessoryViewController:_controller];
+ }
+}
+
+- (BOOL)isNativeSelected {
+ if (@available(macOS 10.13, *)) {
+ return [[self tabGroup] selectedWindow] == self;
+ }
+ return NO;
+}
+
+- (void)setIgnoreMove:(BOOL)value {
+ _ignoreMove = value;
+ self.movable = !value;
+}
+
+- (void)_adjustWindowToScreen {
+ if (_ignoreMove) {
+ self.movable = YES;
+ }
+
+ [super _adjustWindowToScreen];
+ [(AWTWindow *)self.delegate updateFullScreenButtons];
+
+ if (_ignoreMove) {
+ self.movable = NO;
+ }
+}
+
@end
@implementation AWTWindow_Panel
AWT_NS_WINDOW_IMPLEMENTATION
@@ -195,7 +418,10 @@ @implementation AWTWindow
@synthesize preFullScreenLevel;
@synthesize standardFrame;
@synthesize isMinimizing;
-@synthesize keyNotificationRecd;
+@synthesize isJustCreated;
+@synthesize javaWindowTabbingMode;
+@synthesize isEnterFullScreen;
+@synthesize hideTabController;
- (void) updateMinMaxSize:(BOOL)resizable {
if (resizable) {
@@ -226,11 +452,22 @@ + (NSUInteger) styleMaskForStyleBits:(jint)styleBits {
if (IS(styleBits, UTILITY)) type |= NSWindowStyleMaskUtilityWindow;
if (IS(styleBits, HUD)) type |= NSWindowStyleMaskHUDWindow;
if (IS(styleBits, SHEET)) type |= NSWindowStyleMaskDocModalWindow;
- if (IS(styleBits, NONACTIVATING)) type |= NSWindowStyleMaskNonactivatingPanel;
return type;
}
++ (jint) affectedStyleMaskForCustomTitleBar {
+ return MASK(FULL_WINDOW_CONTENT) | MASK(TRANSPARENT_TITLE_BAR) | MASK(TITLE_VISIBLE);
+}
+
++ (jint) overrideStyleBits:(jint)styleBits customTitleBarEnabled:(BOOL)customTitleBarEnabled fullscreen:(BOOL)fullscreen {
+ if (customTitleBarEnabled) {
+ styleBits |= MASK(FULL_WINDOW_CONTENT) | MASK(TRANSPARENT_TITLE_BAR);
+ if (!fullscreen) styleBits &= ~MASK(TITLE_VISIBLE);
+ }
+ return styleBits;
+}
+
// updates _METHOD_PROP_BITMASK based properties on the window
- (void) setPropertiesForStyleBits:(jint)bits mask:(jint)mask {
if (IS(mask, RESIZABLE)) {
@@ -269,9 +506,11 @@ - (void) setPropertiesForStyleBits:(jint)bits mask:(jint)mask {
if (IS(mask, FULLSCREENABLE) && [self.nsWindow respondsToSelector:@selector(toggleFullScreen:)]) {
if (IS(bits, FULLSCREENABLE)) {
- [self.nsWindow setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary];
+ self.nsWindow.collectionBehavior = self.nsWindow.collectionBehavior |
+ NSWindowCollectionBehaviorFullScreenPrimary;
} else {
- [self.nsWindow setCollectionBehavior:NSWindowCollectionBehaviorDefault];
+ self.nsWindow.collectionBehavior = self.nsWindow.collectionBehavior &
+ ~NSWindowCollectionBehaviorFullScreenPrimary;
}
}
@@ -280,7 +519,7 @@ - (void) setPropertiesForStyleBits:(jint)bits mask:(jint)mask {
}
if (IS(mask, TITLE_VISIBLE) && [self.nsWindow respondsToSelector:@selector(setTitleVisibility:)]) {
- [self.nsWindow setTitleVisibility:(IS(bits, TITLE_VISIBLE)) ? NSWindowTitleVisible :NSWindowTitleHidden];
+ [self.nsWindow setTitleVisibility:(IS(bits, TITLE_VISIBLE) ? NSWindowTitleVisible : NSWindowTitleHidden)];
}
}
@@ -293,10 +532,23 @@ - (id) initWithPlatformWindow:(jobject)platformWindow
{
AWT_ASSERT_APPKIT_THREAD;
+ self = [super init];
+ if (self == nil) return nil; // no hope
+ self.javaPlatformWindow = platformWindow;
+
NSUInteger newBits = bits;
if (IS(bits, SHEET) && owner == nil) {
newBits = bits & ~NSWindowStyleMaskDocModalWindow;
}
+
+ _customTitleBarHeight = -1.0f; // Negative means uninitialized
+ self.customTitleBarControlsVisible = YES;
+ self.customTitleBarConstraints = nil;
+ self.customTitleBarHeightConstraint = nil;
+ self.customTitleBarButtonCenterXConstraints = nil;
+ // Force properties if custom title bar is enabled, but store original value in self.styleBits.
+ newBits = [AWTWindow overrideStyleBits:newBits customTitleBarEnabled:self.isCustomTitleBarEnabled fullscreen:false];
+
NSUInteger styleMask = [AWTWindow styleMaskForStyleBits:newBits];
NSRect contentRect = rect; //[NSWindow contentRectForFrameRect:rect styleMask:styleMask];
@@ -307,12 +559,7 @@ - (id) initWithPlatformWindow:(jobject)platformWindow
contentRect.size.height = 1.0;
}
- self = [super init];
-
- if (self == nil) return nil; // no hope
-
if (IS(bits, UTILITY) ||
- IS(bits, NONACTIVATING) ||
IS(bits, HUD) ||
IS(bits, HIDES_ON_DEACTIVATE) ||
IS(bits, SHEET))
@@ -334,22 +581,29 @@ - (id) initWithPlatformWindow:(jobject)platformWindow
if (self.nsWindow == nil) return nil; // no hope either
[self.nsWindow release]; // the property retains the object already
- self.keyNotificationRecd = NO;
self.isEnabled = YES;
self.isMinimizing = NO;
- self.javaPlatformWindow = platformWindow;
self.styleBits = bits;
self.ownerWindow = owner;
- [self setPropertiesForStyleBits:styleBits mask:MASK(_METHOD_PROP_BITMASK)];
-
- if (IS(self.styleBits, IS_POPUP)) {
- [self.nsWindow setCollectionBehavior:NSWindowCollectionBehaviorFullScreenAuxiliary];
- }
+ [self setPropertiesForStyleBits:newBits mask:MASK(_METHOD_PROP_BITMASK)];
if (IS(bits, SHEET) && owner != nil) {
[self.nsWindow setStyleMask: NSWindowStyleMaskDocModalWindow];
}
+ self.isJustCreated = YES;
+
+ self.javaWindowTabbingMode = [self getJavaWindowTabbingMode];
+ self.nsWindow.collectionBehavior = NSWindowCollectionBehaviorManaged;
+ self.isEnterFullScreen = NO;
+
+ [self configureJavaWindowTabbingIdentifier];
+
+ if (self.isCustomTitleBarEnabled && !self.isFullScreen) {
+ [self setUpCustomTitleBar];
+ }
+
+ self.currentDisplayID = nil;
return self;
}
@@ -357,67 +611,101 @@ + (BOOL) isAWTWindow:(NSWindow *)window {
return [window isKindOfClass: [AWTWindow_Panel class]] || [window isKindOfClass: [AWTWindow_Normal class]];
}
-// Retrieves the list of possible window layers (levels)
-+ (NSArray*) getWindowLayers {
- static NSArray *windowLayers;
- static dispatch_once_t token;
+// returns id for the topmost window under mouse
++ (NSInteger) getTopmostWindowUnderMouseID {
+ return [NSWindow windowNumberAtPoint:[NSEvent mouseLocation] belowWindowWithWindowNumber:kCGNullWindowID];
+}
- // Initialize the list of possible window layers
- dispatch_once(&token, ^{
- // The layers are ordered from front to back, (i.e. the toppest one is the first)
- windowLayers = [NSArray arrayWithObjects:
- [NSNumber numberWithInt:CGWindowLevelForKey(kCGPopUpMenuWindowLevelKey)],
- [NSNumber numberWithInt:CGWindowLevelForKey(kCGFloatingWindowLevelKey)],
- [NSNumber numberWithInt:CGWindowLevelForKey(kCGNormalWindowLevelKey)],
- nil
- ];
- [windowLayers retain];
- });
- return windowLayers;
+// checks that this window is under the mouse cursor and this point is not overlapped by others windows
+- (BOOL) isTopmostWindowUnderMouse {
+ return [self.nsWindow windowNumber] == [AWTWindow getTopmostWindowUnderMouseID];
}
-// returns id for the topmost window under mouse
-+ (NSInteger) getTopmostWindowUnderMouseID {
- NSInteger result = -1;
+- (void) configureJavaWindowTabbingIdentifier {
+ AWT_ASSERT_APPKIT_THREAD;
- NSArray *windowLayers = [AWTWindow getWindowLayers];
- // Looking for the window under mouse starting from the toppest layer
- for (NSNumber *layer in windowLayers) {
- result = [AWTWindow getTopmostWindowUnderMouseIDImpl:[layer integerValue]];
- if (result != -1) {
- break;
- }
+ self.hideTabController = NO;
+
+ if (self.javaWindowTabbingMode != NSWindowTabbingModeAutomatic) {
+ return;
+ }
+
+ JNIEnv *env = [ThreadUtilities getJNIEnv];
+ jobject platformWindow = (*env)->NewLocalRef(env, self.javaPlatformWindow);
+ if (platformWindow == NULL) {
+ return;
}
- return result;
-}
-+ (NSInteger) getTopmostWindowUnderMouseIDImpl:(NSInteger)windowLayer {
- NSInteger result = -1;
+ GET_CPLATFORM_WINDOW_CLASS();
+ DECLARE_FIELD(jf_target, jc_CPlatformWindow, "target", "Ljava/awt/Window;");
+ jobject awtWindow = (*env)->GetObjectField(env, platformWindow, jf_target);
+
+ if (awtWindow != NULL) {
+ DECLARE_CLASS(jc_RootPaneContainer, "javax/swing/RootPaneContainer");
+ if ((*env)->IsInstanceOf(env, awtWindow, jc_RootPaneContainer)) {
+ DECLARE_METHOD(jm_getRootPane, jc_RootPaneContainer, "getRootPane", "()Ljavax/swing/JRootPane;");
+ jobject rootPane = (*env)->CallObjectMethod(env, awtWindow, jm_getRootPane);
+ CHECK_EXCEPTION();
- NSRect screenRect = [[NSScreen mainScreen] frame];
- NSPoint nsMouseLocation = [NSEvent mouseLocation];
- CGPoint cgMouseLocation = CGPointMake(nsMouseLocation.x, screenRect.size.height - nsMouseLocation.y);
+ if (rootPane != NULL) {
+ DECLARE_CLASS(jc_JComponent, "javax/swing/JComponent");
+ DECLARE_METHOD(jm_getClientProperty, jc_JComponent, "getClientProperty", "(Ljava/lang/Object;)Ljava/lang/Object;");
+ jstring jKey = NSStringToJavaString(env, @"JavaWindowTabbingIdentifier");
+ jobject jValue = (*env)->CallObjectMethod(env, rootPane, jm_getClientProperty, jKey);
+ CHECK_EXCEPTION();
+
+ if (jValue != NULL) {
+ DECLARE_CLASS(jc_String, "java/lang/String");
+ if ((*env)->IsInstanceOf(env, jValue, jc_String)) {
+ NSString *winId = JavaStringToNSString(env, (jstring)jValue);
+ [self.nsWindow setTabbingIdentifier:winId];
+ if ([winId characterAtIndex:0] == '+') {
+ self.hideTabController = YES;
+ [self.nsWindow _setTabBarAccessoryViewController:nil];
+ }
+ }
- NSMutableArray *windows = (NSMutableArray *)CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements, kCGNullWindowID);
+ (*env)->DeleteLocalRef(env, jValue);
+ }
- for (NSDictionary *window in windows) {
- NSInteger layer = [[window objectForKey:(id)kCGWindowLayer] integerValue];
- if (layer == windowLayer) {
- CGRect rect;
- CGRectMakeWithDictionaryRepresentation((CFDictionaryRef)[window objectForKey:(id)kCGWindowBounds], &rect);
- if (CGRectContainsPoint(rect, cgMouseLocation)) {
- result = [[window objectForKey:(id)kCGWindowNumber] integerValue];
- break;
+ (*env)->DeleteLocalRef(env, jKey);
+ (*env)->DeleteLocalRef(env, rootPane);
}
}
+
+ (*env)->DeleteLocalRef(env, awtWindow);
}
- [windows release];
- return result;
+
+ (*env)->DeleteLocalRef(env, platformWindow);
}
-// checks that this window is under the mouse cursor and this point is not overlapped by others windows
-- (BOOL) isTopmostWindowUnderMouse {
- return [self.nsWindow windowNumber] == [AWTWindow getTopmostWindowUnderMouseID];
+- (NSWindowTabbingMode) getJavaWindowTabbingMode {
+ AWT_ASSERT_APPKIT_THREAD;
+
+ BOOL result = NO;
+
+ JNIEnv *env = [ThreadUtilities getJNIEnv];
+ jobject platformWindow = (*env)->NewLocalRef(env, self.javaPlatformWindow);
+ if (platformWindow != NULL) {
+ // extract the target AWT Window object out of the CPlatformWindow
+ GET_CPLATFORM_WINDOW_CLASS_RETURN(NSWindowTabbingModeDisallowed);
+ DECLARE_FIELD_RETURN(jf_target, jc_CPlatformWindow, "target", "Ljava/awt/Window;", NSWindowTabbingModeDisallowed);
+ jobject awtWindow = (*env)->GetObjectField(env, platformWindow, jf_target);
+ if (awtWindow != NULL) {
+ DECLARE_CLASS_RETURN(jc_Window, "java/awt/Window", NSWindowTabbingModeDisallowed);
+ DECLARE_METHOD_RETURN(jm_hasTabbingMode, jc_Window, "hasTabbingMode", "()Z", NSWindowTabbingModeDisallowed);
+ result = (*env)->CallBooleanMethod(env, awtWindow, jm_hasTabbingMode) == JNI_TRUE ? YES : NO;
+ CHECK_EXCEPTION();
+ (*env)->DeleteLocalRef(env, awtWindow);
+ }
+ (*env)->DeleteLocalRef(env, platformWindow);
+ }
+
+#ifdef DEBUG
+ NSLog(@"=== getJavaWindowTabbingMode: %d ===", result);
+#endif
+
+ return result ? NSWindowTabbingModeAutomatic : NSWindowTabbingModeDisallowed;
}
+ (AWTWindow *) getTopmostWindowUnderMouse {
@@ -490,26 +778,13 @@ - (void) dealloc {
self.javaPlatformWindow = nil;
self.nsWindow = nil;
self.ownerWindow = nil;
+ self.currentDisplayID = nil;
+ self.customTitleBarConstraints = nil;
+ self.customTitleBarHeightConstraint = nil;
+ self.customTitleBarButtonCenterXConstraints = nil;
[super dealloc];
}
-// Tests whether window is blocked by modal dialog/window
-- (BOOL) isBlocked {
- BOOL isBlocked = NO;
-
- JNIEnv *env = [ThreadUtilities getJNIEnv];
- jobject platformWindow = (*env)->NewLocalRef(env, self.javaPlatformWindow);
- if (platformWindow != NULL) {
- GET_CPLATFORM_WINDOW_CLASS_RETURN(isBlocked);
- DECLARE_METHOD_RETURN(jm_isBlocked, jc_CPlatformWindow, "isBlocked", "()Z", isBlocked);
- isBlocked = (*env)->CallBooleanMethod(env, platformWindow, jm_isBlocked) == JNI_TRUE ? YES : NO;
- CHECK_EXCEPTION();
- (*env)->DeleteLocalRef(env, platformWindow);
- }
-
- return isBlocked;
-}
-
// Test whether window is simple window and owned by embedded frame
- (BOOL) isSimpleWindowOwnedByEmbeddedFrame {
BOOL isSimpleWindowOwnedByEmbeddedFrame = NO;
@@ -549,55 +824,115 @@ + (BOOL) isJavaPlatformWindowVisible:(NSWindow *)window {
return isVisible;
}
-// Orders window children based on the current focus state
-- (void) orderChildWindows:(BOOL)focus {
-AWT_ASSERT_APPKIT_THREAD;
+- (BOOL) delayShowing {
+ AWT_ASSERT_APPKIT_THREAD;
- if (self.isMinimizing || [self isBlocked]) {
- // Do not perform any ordering, if iconify is in progress
- // or the window is blocked by a modal window
+ return ownerWindow != nil &&
+ ([ownerWindow delayShowing] || !ownerWindow.nsWindow.onActiveSpace) &&
+ !nsWindow.visible;
+}
+
+- (BOOL) checkBlockingAndOrder {
+ AWT_ASSERT_APPKIT_THREAD;
+
+ JNIEnv *env = [ThreadUtilities getJNIEnv];
+ jobject platformWindow = (*env)->NewLocalRef(env, self.javaPlatformWindow);
+ if (platformWindow != NULL) {
+ GET_CPLATFORM_WINDOW_CLASS_RETURN(NO);
+ DECLARE_METHOD_RETURN(jm_checkBlockingAndOrder, jc_CPlatformWindow, "checkBlockingAndOrder", "()V", NO);
+ (*env)->CallVoidMethod(env, platformWindow, jm_checkBlockingAndOrder);
+ CHECK_EXCEPTION();
+ (*env)->DeleteLocalRef(env, platformWindow);
+ }
+ return YES;
+}
+
++ (void)activeSpaceDidChange {
+ AWT_ASSERT_APPKIT_THREAD;
+
+ if (fullScreenTransitionInProgress) {
+ orderingScheduled = YES;
return;
}
+ // show delayed windows
+ for (NSWindow *window in NSApp.windows) {
+ if ([AWTWindow isJavaPlatformWindowVisible:window] && !window.visible) {
+ AWTWindow *awtWindow = (AWTWindow *)[window delegate];
+ while (awtWindow.ownerWindow != nil) {
+ awtWindow = awtWindow.ownerWindow;
+ }
+ if (awtWindow.nsWindow.visible && awtWindow.nsWindow.onActiveSpace) {
+ [awtWindow checkBlockingAndOrder];
+ }
+ }
+ }
+}
+
+- (void) processVisibleChildren:(void(^)(AWTWindow*))action {
NSEnumerator *windowEnumerator = [[NSApp windows]objectEnumerator];
NSWindow *window;
while ((window = [windowEnumerator nextObject]) != nil) {
if ([AWTWindow isJavaPlatformWindowVisible:window]) {
AWTWindow *awtWindow = (AWTWindow *)[window delegate];
- AWTWindow *owner = awtWindow.ownerWindow;
- if (IS(awtWindow.styleBits, ALWAYS_ON_TOP)) {
- // Do not order 'always on top' windows
- continue;
- }
- while (awtWindow.ownerWindow != nil) {
- if (awtWindow.ownerWindow == self) {
- if (focus) {
- // Move the childWindow to floating level
- // so it will appear in front of its
- // parent which owns the focus
- [window setLevel:NSFloatingWindowLevel];
- } else {
- // Focus owner has changed, move the childWindow
- // back to normal window level
- [window setLevel:NSNormalWindowLevel];
- }
- // The childWindow should be displayed in front of
- // its nearest parentWindow
- [window orderWindow:NSWindowAbove relativeTo:[owner.nsWindow windowNumber]];
+ AWTWindow *parent = awtWindow.ownerWindow;
+ while (parent != nil) {
+ if (parent == self) {
+ action(awtWindow);
break;
}
- awtWindow = awtWindow.ownerWindow;
+ parent = parent.ownerWindow;
}
}
}
}
+// Orders window children based on the current focus state
+- (void) orderChildWindows:(BOOL)focus {
+AWT_ASSERT_APPKIT_THREAD;
+
+ if (self.isMinimizing) {
+ // Do not perform any ordering, if iconify is in progress
+ return;
+ }
+
+ [self processVisibleChildren:^void(AWTWindow* child){
+ // Do not order 'always on top' windows
+ if (!IS(child.styleBits, ALWAYS_ON_TOP)) {
+ NSWindow *window = child.nsWindow;
+ NSWindow *owner = child.ownerWindow.nsWindow;
+ if (focus) {
+ // Move the childWindow to floating level
+ // so it will appear in front of its
+ // parent which owns the focus
+ [window setLevel:NSFloatingWindowLevel];
+ } else {
+ // Focus owner has changed, move the childWindow
+ // back to normal window level
+ [window setLevel:NSNormalWindowLevel];
+ }
+ }
+ }];
+}
+
// NSWindow overrides
- (BOOL) canBecomeKeyWindow {
AWT_ASSERT_APPKIT_THREAD;
return self.isEnabled && (IS(self.styleBits, SHOULD_BECOME_KEY) || [self isSimpleWindowOwnedByEmbeddedFrame]);
}
+- (void) becomeKeyWindow {
+ AWT_ASSERT_APPKIT_THREAD;
+
+ // Reset current cursor in CCursorManager such that any following mouse update event
+ // restores the correct cursor to the frame context specific one.
+ JNIEnv *env = [ThreadUtilities getJNIEnv];
+ DECLARE_CLASS(jc_CCursorManager, "sun/lwawt/macosx/CCursorManager");
+ DECLARE_STATIC_METHOD(sjm_resetCurrentCursor, jc_CCursorManager, "resetCurrentCursor", "()V");
+ (*env)->CallStaticVoidMethod(env, jc_CCursorManager, sjm_resetCurrentCursor);
+ CHECK_EXCEPTION();
+}
+
- (BOOL) canBecomeMainWindow {
AWT_ASSERT_APPKIT_THREAD;
if (!self.isEnabled) {
@@ -606,15 +941,7 @@ - (BOOL) canBecomeMainWindow {
// We should bring up the modal dialog manually
[AWTToolkit eventCountPlusPlus];
- JNIEnv *env = [ThreadUtilities getJNIEnv];
- jobject platformWindow = (*env)->NewLocalRef(env, self.javaPlatformWindow);
- if (platformWindow != NULL) {
- GET_CPLATFORM_WINDOW_CLASS_RETURN(NO);
- DECLARE_METHOD_RETURN(jm_checkBlockingAndOrder, jc_CPlatformWindow, "checkBlockingAndOrder", "()Z", NO);
- (*env)->CallBooleanMethod(env, platformWindow, jm_checkBlockingAndOrder);
- CHECK_EXCEPTION();
- (*env)->DeleteLocalRef(env, platformWindow);
- }
+ if (![self checkBlockingAndOrder]) return NO;
}
return self.isEnabled && IS(self.styleBits, SHOULD_BECOME_MAIN);
@@ -628,21 +955,68 @@ - (BOOL) worksWhenModal {
// NSWindowDelegate methods
-- (void) _deliverMoveResizeEvent {
-AWT_ASSERT_APPKIT_THREAD;
+- (void)_windowDidBecomeVisible {
+ self.currentDisplayID = [AWTWindow getNSWindowDisplayID_AppKitThread:nsWindow];
+}
+
+- (void)_displayChanged:(BOOL)profileOnly {
+ AWT_ASSERT_APPKIT_THREAD;
+ if (!profileOnly) {
+ NSNumber* newDisplayID = [AWTWindow getNSWindowDisplayID_AppKitThread:nsWindow];
+ if (newDisplayID == nil) {
+ // Do not proceed with unset window display id
+ // to avoid receiving wrong boundary values
+ return;
+ }
+
+ if (self.currentDisplayID == nil) {
+ // Do not trigger notification at first initialization
+ self.currentDisplayID = newDisplayID;
+ return;
+ }
+
+ if ([self.currentDisplayID isEqualToNumber: newDisplayID]) {
+ return;
+ }
+ self.currentDisplayID = newDisplayID;
+ }
- // deliver the event if this is a user-initiated live resize or as a side-effect
- // of a Java initiated resize, because AppKit can override the bounds and force
- // the bounds of the window to avoid the Dock or remain on screen.
- [AWTToolkit eventCountPlusPlus];
JNIEnv *env = [ThreadUtilities getJNIEnv];
jobject platformWindow = (*env)->NewLocalRef(env, self.javaPlatformWindow);
if (platformWindow == NULL) {
- // TODO: create generic AWT assert
+ NSLog(@"[AWTWindow _displayChanged]: platformWindow == NULL");
+ return;
+ }
+ GET_CPLATFORM_WINDOW_CLASS();
+ DECLARE_METHOD(jm_displayChanged, jc_CPlatformWindow, "displayChanged", "(Z)V");
+ (*env)->CallVoidMethod(env, platformWindow, jm_displayChanged, profileOnly);
+ CHECK_EXCEPTION();
+ (*env)->DeleteLocalRef(env, platformWindow);
+}
+
+- (void) _deliverMoveResizeEvent {
+ AWT_ASSERT_APPKIT_THREAD;
+
+ // deliver the event if this is a user-initiated live resize or as a side-effect
+ // of a Java initiated resize, because AppKit can override the bounds and force
+ // the bounds of the window to avoid the Dock or remain on screen.
+ [AWTToolkit eventCountPlusPlus];
+ JNIEnv *env = [ThreadUtilities getJNIEnv];
+ jobject platformWindow = (*env)->NewLocalRef(env, self.javaPlatformWindow);
+ if (platformWindow == NULL) {
+ NSLog(@"[AWTWindow _deliverMoveResizeEvent]: platformWindow == NULL");
+ return;
+ }
+ NSRect frame;
+ @try {
+ frame = ConvertNSScreenRect(env, [self.nsWindow frame]);
+ } @catch (NSException *e) {
+ NSLog(@"WARNING: suppressed exception from ConvertNSScreenRect() in [AWTWindow _deliverMoveResizeEvent]");
+ NSProcessInfo *processInfo = [NSProcessInfo processInfo];
+ [NSApplicationAWT logException:e forProcess:processInfo];
+ return;
}
- NSRect frame = ConvertNSScreenRect(env, [self.nsWindow frame]);
-
GET_CPLATFORM_WINDOW_CLASS();
DECLARE_METHOD(jm_deliverMoveResizeEvent, jc_CPlatformWindow, "deliverMoveResizeEvent", "(IIIIZ)V");
(*env)->CallVoidMethod(env, platformWindow, jm_deliverMoveResizeEvent,
@@ -665,6 +1039,12 @@ - (void)windowDidMove:(NSNotification *)notification {
- (void)windowDidResize:(NSNotification *)notification {
AWT_ASSERT_APPKIT_THREAD;
+ if (self.isEnterFullScreen && ignoreResizeWindowDuringAnotherWindowEnd) {
+#ifdef DEBUG
+ NSLog(@"=== Native.windowDidResize: %@ | ignored in transition to fullscreen ===", self.nsWindow.title);
+#endif
+ return;
+ }
[self _deliverMoveResizeEvent];
}
@@ -689,24 +1069,14 @@ - (NSRect)windowWillUseStandardFrame:(NSWindow *)window
- (void) iconifyChildWindows:(BOOL)iconify {
AWT_ASSERT_APPKIT_THREAD;
- NSEnumerator *windowEnumerator = [[NSApp windows]objectEnumerator];
- NSWindow *window;
- while ((window = [windowEnumerator nextObject]) != nil) {
- if ([AWTWindow isJavaPlatformWindowVisible:window]) {
- AWTWindow *awtWindow = (AWTWindow *)[window delegate];
- while (awtWindow.ownerWindow != nil) {
- if (awtWindow.ownerWindow == self) {
- if (iconify) {
- [window orderOut:window];
- } else {
- [window orderFront:window];
- }
- break;
- }
- awtWindow = awtWindow.ownerWindow;
- }
+ [self processVisibleChildren:^void(AWTWindow* child){
+ NSWindow *window = child.nsWindow;
+ if (iconify) {
+ [window orderOut:window];
+ } else {
+ [window orderFront:window];
}
- }
+ }];
}
- (void) _deliverIconify:(BOOL)iconify {
@@ -777,19 +1147,10 @@ - (void) windowDidBecomeMain: (NSNotification *) notification {
AWT_ASSERT_APPKIT_THREAD;
[AWTToolkit eventCountPlusPlus];
#ifdef DEBUG
- NSLog(@"became main: %d %@ %@ %d", [self.nsWindow isKeyWindow], [self.nsWindow title], [self menuBarForWindow], self.keyNotificationRecd);
+ NSLog(@"became main: %d %@ %@", [self.nsWindow isKeyWindow], [self.nsWindow title], [self menuBarForWindow]);
#endif
- // if for some reason, no KEY notification is received but this main window is also a key window
- // then we need to execute the KEY notification functionality.
- if(self.keyNotificationRecd != YES && [self.nsWindow isKeyWindow]) {
- [self doWindowDidBecomeKey];
- }
- self.keyNotificationRecd = NO;
-
- if (![self.nsWindow isKeyWindow]) {
- [self activateWindowMenuBar];
- }
+ [self activateWindowMenuBar];
JNIEnv *env = [ThreadUtilities getJNIEnv];
jobject platformWindow = (*env)->NewLocalRef(env, self.javaPlatformWindow);
@@ -800,6 +1161,8 @@ - (void) windowDidBecomeMain: (NSNotification *) notification {
CHECK_EXCEPTION();
(*env)->DeleteLocalRef(env, platformWindow);
}
+
+ [self orderChildWindows:YES];
}
- (void) windowDidBecomeKey: (NSNotification *) notification {
@@ -808,22 +1171,29 @@ - (void) windowDidBecomeKey: (NSNotification *) notification {
#ifdef DEBUG
NSLog(@"became key: %d %@ %@", [self.nsWindow isMainWindow], [self.nsWindow title], [self menuBarForWindow]);
#endif
- [self doWindowDidBecomeKey];
- self.keyNotificationRecd = YES;
-}
-
-- (void) doWindowDidBecomeKey {
-AWT_ASSERT_APPKIT_THREAD;
AWTWindow *opposite = [AWTWindow lastKeyWindow];
if (![self.nsWindow isMainWindow]) {
- [self activateWindowMenuBar];
+ [self makeRelevantAncestorMain];
}
[AWTWindow setLastKeyWindow:nil];
[self _deliverWindowFocusEvent:YES oppositeWindow: opposite];
- [self orderChildWindows:YES];
+}
+
+- (void) makeRelevantAncestorMain {
+ NSWindow *nativeWindow;
+ AWTWindow *awtWindow = self;
+
+ do {
+ nativeWindow = awtWindow.nsWindow;
+ if ([nativeWindow canBecomeMainWindow]) {
+ [nativeWindow makeMainWindow];
+ break;
+ }
+ awtWindow = awtWindow.ownerWindow;
+ } while (awtWindow);
}
- (void) activateWindowMenuBar {
@@ -867,7 +1237,7 @@ - (void) windowDidResignKey: (NSNotification *) notification {
#ifdef DEBUG
NSLog(@"resigned key: %d %@ %@", [self.nsWindow isMainWindow], [self.nsWindow title], [self menuBarForWindow]);
#endif
- if (![self.nsWindow isMainWindow]) {
+ if (![self.nsWindow isMainWindow] || [NSApp keyWindow] == self.nsWindow) {
[self deactivateWindow];
}
}
@@ -881,6 +1251,9 @@ - (void) windowDidResignMain: (NSNotification *) notification {
if (![self.nsWindow isKeyWindow]) {
[self deactivateWindow];
}
+
+ [self.javaMenuBar deactivate];
+ [self orderChildWindows:NO];
}
- (void) deactivateWindow {
@@ -888,20 +1261,20 @@ - (void) deactivateWindow {
#ifdef DEBUG
NSLog(@"deactivating window: %@", [self.nsWindow title]);
#endif
- [self.javaMenuBar deactivate];
// the new key window
NSWindow *keyWindow = [NSApp keyWindow];
AWTWindow *opposite = nil;
if ([AWTWindow isAWTWindow: keyWindow]) {
- opposite = (AWTWindow *)[keyWindow delegate];
+ if (keyWindow != self.nsWindow) {
+ opposite = (AWTWindow *)[keyWindow delegate];
+ }
[AWTWindow setLastKeyWindow: self];
} else {
[AWTWindow setLastKeyWindow: nil];
}
[self _deliverWindowFocusEvent:NO oppositeWindow: opposite];
- [self orderChildWindows:NO];
}
- (BOOL)windowShouldClose:(id)sender {
@@ -938,163 +1311,768 @@ - (void)_notifyFullScreenOp:(jint)op withEnv:(JNIEnv *)env {
}
}
+// this is required to move owned windows to the full-screen space when owner goes to full-screen mode
+- (void)allowMovingChildrenBetweenSpaces:(BOOL)allow {
+ [self processVisibleChildren:^void(AWTWindow* child){
+ NSWindow *window = child.nsWindow;
+ NSWindowCollectionBehavior behavior = window.collectionBehavior;
+ behavior &= ~(NSWindowCollectionBehaviorManaged | NSWindowCollectionBehaviorTransient);
+ behavior |= allow ? NSWindowCollectionBehaviorTransient : NSWindowCollectionBehaviorManaged;
+ window.collectionBehavior = behavior;
+ }];
+}
+
+- (void) fullScreenTransitionStarted {
+ fullScreenTransitionInProgress = YES;
+}
+
+- (void) fullScreenTransitionFinished {
+ fullScreenTransitionInProgress = NO;
+ if (orderingScheduled) {
+ orderingScheduled = NO;
+ [self checkBlockingAndOrder];
+ }
+}
+
+- (CGFloat) customTitleBarHeight {
+ CGFloat h = _customTitleBarHeight;
+ if (h < 0.0f) {
+ JNIEnv *env = [ThreadUtilities getJNIEnvUncached];
+ GET_CPLATFORM_WINDOW_CLASS_RETURN(YES);
+ DECLARE_FIELD_RETURN(jf_target, jc_CPlatformWindow, "target", "Ljava/awt/Window;", 0.0f);
+ DECLARE_CLASS_RETURN(jc_Window, "java/awt/Window", 0.0f);
+ DECLARE_METHOD_RETURN(jm_internalCustomTitleBarHeight, jc_Window, "internalCustomTitleBarHeight", "()F", 0.0f);
+ DECLARE_METHOD_RETURN(jm_internalCustomTitleBarControlsVisible, jc_Window, "internalCustomTitleBarControlsVisible", "()Z", 0.0f);
+
+ jobject platformWindow = (*env)->NewLocalRef(env, self.javaPlatformWindow);
+ if (!platformWindow) return 0.0f;
+ jobject target = (*env)->GetObjectField(env, platformWindow, jf_target);
+ if (target) {
+ h = (CGFloat) (*env)->CallFloatMethod(env, target, jm_internalCustomTitleBarHeight);
+ self.customTitleBarControlsVisible = (BOOL) (*env)->CallBooleanMethod(env, target, jm_internalCustomTitleBarControlsVisible);
+ (*env)->DeleteLocalRef(env, target);
+ }
+ CHECK_EXCEPTION();
+ (*env)->DeleteLocalRef(env, platformWindow);
+ if (h < 0.0f) h = 0.0f;
+ _customTitleBarHeight = h;
+ }
+ return h;
+}
+
+- (BOOL) isCustomTitleBarEnabled {
+ CGFloat h = _customTitleBarHeight;
+ if (h < 0.0f) h = self.customTitleBarHeight;
+ return h > 0.0f;
+}
+
+- (void) updateCustomTitleBarInsets:(BOOL)hasControls {
+ CGFloat leftInset;
+ if (hasControls) {
+ CGFloat shrinkingFactor = self.customTitleBarButtonShrinkingFactor;
+ CGFloat horizontalButtonOffset = shrinkingFactor * DefaultHorizontalTitleBarButtonOffset;
+ leftInset = self.customTitleBarHeight + 2.0f * horizontalButtonOffset;
+ } else leftInset = 0.0f;
+
+ JNIEnv *env = [ThreadUtilities getJNIEnvUncached];
+ GET_CPLATFORM_WINDOW_CLASS();
+ DECLARE_FIELD(jf_target, jc_CPlatformWindow, "target", "Ljava/awt/Window;");
+ DECLARE_CLASS(jc_Window, "java/awt/Window");
+ DECLARE_METHOD(jm_internalCustomTitleBarUpdateInsets, jc_Window, "internalCustomTitleBarUpdateInsets", "(FF)V");
+
+ jobject platformWindow = (*env)->NewLocalRef(env, self.javaPlatformWindow);
+ if (!platformWindow) return;
+ jobject target = (*env)->GetObjectField(env, platformWindow, jf_target);
+ if (target) {
+ (*env)->CallVoidMethod(env, target, jm_internalCustomTitleBarUpdateInsets, (jfloat) leftInset, (jfloat) 0.0f);
+ (*env)->DeleteLocalRef(env, target);
+ }
+ CHECK_EXCEPTION();
+ (*env)->DeleteLocalRef(env, platformWindow);
+}
+
+- (void)windowWillEnterFullScreen:(NSNotification *)notification {
+ [self fullScreenTransitionStarted];
+ [self allowMovingChildrenBetweenSpaces:YES];
+
+ self.isEnterFullScreen = YES;
+
+ if (self.isCustomTitleBarEnabled) {
+ [self resetCustomTitleBar];
+ }
+
+ JNIEnv *env = [ThreadUtilities getJNIEnv];
+ GET_CPLATFORM_WINDOW_CLASS();
+ DECLARE_METHOD(jm_windowWillEnterFullScreen, jc_CPlatformWindow, "windowWillEnterFullScreen", "()V");
+ jobject platformWindow = (*env)->NewLocalRef(env, self.javaPlatformWindow);
+ if (platformWindow != NULL) {
+ (*env)->CallVoidMethod(env, platformWindow, jm_windowWillEnterFullScreen);
+ CHECK_EXCEPTION();
+ [self _notifyFullScreenOp:com_apple_eawt_FullScreenHandler_FULLSCREEN_WILL_ENTER withEnv:env];
+ (*env)->DeleteLocalRef(env, platformWindow);
+ }
+}
+
+- (void)windowDidEnterFullScreen:(NSNotification *)notification {
+ self.isEnterFullScreen = YES;
+
+ if (self.isCustomTitleBarEnabled) {
+ [self forceHideCustomTitleBarTitle:NO];
+ [self updateCustomTitleBarInsets:NO];
+
+ JNIEnv *env = [ThreadUtilities getJNIEnvUncached];
+ NSString *newFullScreenControls = [PropertiesUtilities
+ javaSystemPropertyForKey:@"apple.awt.newFullScreenControls" withEnv:env];
+ if ([@"true" isCaseInsensitiveLike:newFullScreenControls]) {
+ [self setWindowFullScreenControls];
+ }
+ }
+ [self allowMovingChildrenBetweenSpaces:NO];
+ [self fullScreenTransitionFinished];
+
+ JNIEnv *env = [ThreadUtilities getJNIEnv];
+ GET_CPLATFORM_WINDOW_CLASS();
+ DECLARE_METHOD(jm_windowDidEnterFullScreen, jc_CPlatformWindow, "windowDidEnterFullScreen", "()V");
+ jobject platformWindow = (*env)->NewLocalRef(env, self.javaPlatformWindow);
+ if (platformWindow != NULL) {
+ (*env)->CallVoidMethod(env, platformWindow, jm_windowDidEnterFullScreen);
+ CHECK_EXCEPTION();
+ [self _notifyFullScreenOp:com_apple_eawt_FullScreenHandler_FULLSCREEN_DID_ENTER withEnv:env];
+ (*env)->DeleteLocalRef(env, platformWindow);
+ }
+ [AWTWindow synthesizeMouseEnteredExitedEventsForAllWindows];
+}
+
+- (void)windowWillExitFullScreen:(NSNotification *)notification {
+ self.isEnterFullScreen = NO;
+
+ [self fullScreenTransitionStarted];
+
+ if (self.isCustomTitleBarEnabled) {
+ [self setWindowControlsHidden:YES];
+ [self updateCustomTitleBarInsets:self.customTitleBarControlsVisible];
+ [self forceHideCustomTitleBarTitle:YES];
+ }
+
+ JNIEnv *env = [ThreadUtilities getJNIEnv];
+ GET_CPLATFORM_WINDOW_CLASS();
+ DECLARE_METHOD(jm_windowWillExitFullScreen, jc_CPlatformWindow, "windowWillExitFullScreen", "()V");
+ if (jm_windowWillExitFullScreen == NULL) {
+ GET_CPLATFORM_WINDOW_CLASS();
+ jm_windowWillExitFullScreen = (*env)->GetMethodID(env, jc_CPlatformWindow, "windowWillExitFullScreen", "()V");
+ }
+ CHECK_NULL(jm_windowWillExitFullScreen);
+ jobject platformWindow = (*env)->NewLocalRef(env, self.javaPlatformWindow);
+ if (platformWindow != NULL) {
+ (*env)->CallVoidMethod(env, platformWindow, jm_windowWillExitFullScreen);
+ CHECK_EXCEPTION();
+ [self _notifyFullScreenOp:com_apple_eawt_FullScreenHandler_FULLSCREEN_WILL_EXIT withEnv:env];
+ (*env)->DeleteLocalRef(env, platformWindow);
+ }
+}
+
+- (void)windowDidExitFullScreen:(NSNotification *)notification {
+ [self resetWindowFullScreenControls];
+
+ self.isEnterFullScreen = NO;
+
+ [self fullScreenTransitionFinished];
+
+ if (self.isCustomTitleBarEnabled) {
+ [self setUpCustomTitleBar];
+ }
+
+ JNIEnv *env = [ThreadUtilities getJNIEnv];
+ jobject platformWindow = (*env)->NewLocalRef(env, self.javaPlatformWindow);
+ if (platformWindow != NULL) {
+ GET_CPLATFORM_WINDOW_CLASS();
+ DECLARE_METHOD(jm_windowDidExitFullScreen, jc_CPlatformWindow, "windowDidExitFullScreen", "()V");
+ (*env)->CallVoidMethod(env, platformWindow, jm_windowDidExitFullScreen);
+ CHECK_EXCEPTION();
+ [self _notifyFullScreenOp:com_apple_eawt_FullScreenHandler_FULLSCREEN_DID_EXIT withEnv:env];
+ (*env)->DeleteLocalRef(env, platformWindow);
+ }
+ [AWTWindow synthesizeMouseEnteredExitedEventsForAllWindows];
+}
+
+- (void)sendEvent:(NSEvent *)event {
+ if ([event type] == NSLeftMouseDown ||
+ [event type] == NSRightMouseDown ||
+ [event type] == NSOtherMouseDown) {
+ NSPoint p = [NSEvent mouseLocation];
+ NSRect frame = [self.nsWindow frame];
+ NSRect contentRect = [self.nsWindow contentRectForFrameRect:frame];
+
+ // Check if the click happened in the non-client area (title bar)
+ if (p.y >= (frame.origin.y + contentRect.size.height)) {
+ JNIEnv *env = [ThreadUtilities getJNIEnvUncached];
+ jobject platformWindow = (*env)->NewLocalRef(env, self.javaPlatformWindow);
+ if (platformWindow != NULL) {
+ // Currently, no need to deliver the whole NSEvent.
+ GET_CPLATFORM_WINDOW_CLASS();
+ DECLARE_METHOD(jm_deliverNCMouseDown, jc_CPlatformWindow, "deliverNCMouseDown", "()V");
+ (*env)->CallVoidMethod(env, platformWindow, jm_deliverNCMouseDown);
+ CHECK_EXCEPTION();
+ (*env)->DeleteLocalRef(env, platformWindow);
+ }
+ }
+ }
+}
+
+- (void)constrainSize:(NSSize*)size {
+ float minWidth = 0.f, minHeight = 0.f;
+
+ if (IS(self.styleBits, DECORATED)) {
+ NSRect frame = [self.nsWindow frame];
+ NSRect contentRect = [NSWindow contentRectForFrameRect:frame styleMask:[self.nsWindow styleMask]];
+
+ float top = frame.size.height - contentRect.size.height;
+ float left = contentRect.origin.x - frame.origin.x;
+ float bottom = contentRect.origin.y - frame.origin.y;
+ float right = frame.size.width - (contentRect.size.width + left);
+
+ // Speculative estimation: 80 - enough for window decorations controls
+ minWidth += left + right + 80;
+ minHeight += top + bottom;
+ }
+
+ minWidth = MAX(1.f, minWidth);
+ minHeight = MAX(1.f, minHeight);
+
+ size->width = MAX(size->width, minWidth);
+ size->height = MAX(size->height, minHeight);
+}
+
+- (void) setEnabled: (BOOL)flag {
+ self.isEnabled = flag;
+
+ if (IS(self.styleBits, CLOSEABLE)) {
+ [[self.nsWindow standardWindowButton:NSWindowCloseButton] setEnabled: flag];
+ }
+
+ if (IS(self.styleBits, MINIMIZABLE)) {
+ [[self.nsWindow standardWindowButton:NSWindowMiniaturizeButton] setEnabled: flag];
+ }
+
+ if (IS(self.styleBits, ZOOMABLE)) {
+ [[self.nsWindow standardWindowButton:NSWindowZoomButton] setEnabled: flag];
+ }
+
+ if (IS(self.styleBits, RESIZABLE)) {
+ [self updateMinMaxSize:flag];
+ [self.nsWindow setShowsResizeIndicator:flag];
+ }
+}
+
++ (void) setLastKeyWindow:(AWTWindow *)window {
+ [window retain];
+ [lastKeyWindow release];
+ lastKeyWindow = window;
+}
+
++ (AWTWindow *) lastKeyWindow {
+ return lastKeyWindow;
+}
+
+static const CGFloat DefaultHorizontalTitleBarButtonOffset = 20.0;
+
+- (CGFloat) customTitleBarButtonShrinkingFactor {
+ CGFloat minimumHeightWithoutShrinking = 28.0; // This is the smallest macOS title bar availabe with public APIs as of Monterey
+ CGFloat shrinkingFactor = fmin(self.customTitleBarHeight / minimumHeightWithoutShrinking, 1.0);
+ return shrinkingFactor;
+}
+
+- (void) setUpCustomTitleBar {
+ if (self.customTitleBarConstraints != nil) {
+ [self resetCustomTitleBar];
+ }
+ /**
+ * The view hierarchy normally looks as follows:
+ * NSThemeFrame
+ * ├─NSView (content view)
+ * └─NSTitlebarContainerView
+ * ├─_NSTitlebarDecorationView (only on Mojave 10.14 and newer)
+ * └─NSTitlebarView
+ * ├─NSVisualEffectView (only on Big Sur 11 and newer)
+ * ├─NSView (only on Big Sur and newer)
+ * ├─_NSThemeCloseWidget - Close
+ * ├─_NSThemeZoomWidget - Full Screen
+ * ├─_NSThemeWidget - Minimize (note the different order compared to their layout)
+ * └─AWTWindowDragView (we will create this)
+ *
+ * But the order and presence of decorations and effects has been unstable across different macOS versions,
+ * even patch upgrades, which is why the code below uses scans instead of indexed access
+ */
+ NSView* closeButtonView = [self.nsWindow standardWindowButton:NSWindowCloseButton];
+ NSView* zoomButtonView = [self.nsWindow standardWindowButton:NSWindowZoomButton];
+ NSView* miniaturizeButtonView = [self.nsWindow standardWindowButton:NSWindowMiniaturizeButton];
+ if (!closeButtonView || !zoomButtonView || !miniaturizeButtonView) {
+ NSLog(@"WARNING: setUpCustomTitleBar closeButtonView=%@, zoomButtonView=%@, miniaturizeButtonView=%@",
+ closeButtonView, zoomButtonView, miniaturizeButtonView);
+ return;
+ }
+ NSView* titlebar = closeButtonView.superview;
+ NSView* titlebarContainer = titlebar.superview;
+ NSView* themeFrame = titlebarContainer.superview;
+ if (!themeFrame) {
+ NSLog(@"WARNING: setUpCustomTitleBar titlebar=%@, titlebarContainer=%@, themeFrame=%@",
+ titlebar, titlebarContainer, themeFrame);
+ return;
+ }
+
+ self.customTitleBarConstraints = [[NSMutableArray alloc] init];
+ titlebarContainer.translatesAutoresizingMaskIntoConstraints = NO;
+ self.customTitleBarHeightConstraint = [titlebarContainer.heightAnchor constraintEqualToConstant:self.customTitleBarHeight];
+ [self.customTitleBarConstraints addObjectsFromArray:@[
+ [titlebarContainer.leftAnchor constraintEqualToAnchor:themeFrame.leftAnchor],
+ [titlebarContainer.widthAnchor constraintEqualToAnchor:themeFrame.widthAnchor],
+ [titlebarContainer.topAnchor constraintEqualToAnchor:themeFrame.topAnchor],
+ self.customTitleBarHeightConstraint,
+ ]];
+
+ [self.nsWindow setIgnoreMove:YES];
+
+ AWTWindowDragView* windowDragView = [[AWTWindowDragView alloc] initWithPlatformWindow:self.javaPlatformWindow];
+ [titlebar addSubview:windowDragView positioned:NSWindowBelow relativeTo:closeButtonView];
+
+ [@[titlebar, windowDragView] enumerateObjectsUsingBlock:^(NSView* view, NSUInteger index, BOOL* stop)
+ {
+ view.translatesAutoresizingMaskIntoConstraints = NO;
+ [self.customTitleBarConstraints addObjectsFromArray:@[
+ [view.leftAnchor constraintEqualToAnchor:titlebarContainer.leftAnchor],
+ [view.rightAnchor constraintEqualToAnchor:titlebarContainer.rightAnchor],
+ [view.topAnchor constraintEqualToAnchor:titlebarContainer.topAnchor],
+ [view.bottomAnchor constraintEqualToAnchor:titlebarContainer.bottomAnchor],
+ ]];
+ }];
+
+ CGFloat shrinkingFactor = self.customTitleBarButtonShrinkingFactor;
+ CGFloat horizontalButtonOffset = shrinkingFactor * DefaultHorizontalTitleBarButtonOffset;
+ self.customTitleBarButtonCenterXConstraints = [[NSMutableArray alloc] initWithCapacity:3];
+ [@[closeButtonView, miniaturizeButtonView, zoomButtonView] enumerateObjectsUsingBlock:^(NSView* button, NSUInteger index, BOOL* stop)
+ {
+ button.translatesAutoresizingMaskIntoConstraints = NO;
+ NSLayoutConstraint* buttonCenterXConstraint = [button.centerXAnchor constraintEqualToAnchor:titlebarContainer.leftAnchor
+ constant:(self.customTitleBarHeight / 2.0 + (index * horizontalButtonOffset))];
+ [self.customTitleBarButtonCenterXConstraints addObject:buttonCenterXConstraint];
+ [self.customTitleBarConstraints addObjectsFromArray:@[
+ [button.widthAnchor constraintLessThanOrEqualToAnchor:titlebarContainer.heightAnchor multiplier:0.5],
+ // Those corrections are required to keep the icons perfectly round because macOS adds a constant 2 px in resulting height to their frame
+ [button.heightAnchor constraintEqualToAnchor: button.widthAnchor multiplier:14.0/12.0 constant:-2.0],
+ [button.centerYAnchor constraintEqualToAnchor:titlebarContainer.centerYAnchor],
+ buttonCenterXConstraint,
+ ]];
+ }];
+
+ [NSLayoutConstraint activateConstraints:self.customTitleBarConstraints];
+ // These properties are already retained, release them so that retainCount = 1
+ [self.customTitleBarConstraints release];
+ [self.customTitleBarButtonCenterXConstraints release];
+
+ [self setWindowControlsHidden:!self.customTitleBarControlsVisible];
+ [self updateCustomTitleBarInsets:self.customTitleBarControlsVisible];
+}
+
+- (void) updateCustomTitleBarConstraints {
+ self.customTitleBarHeightConstraint.constant = self.customTitleBarHeight;
+ CGFloat shrinkingFactor = self.customTitleBarButtonShrinkingFactor;
+ CGFloat horizontalButtonOffset = shrinkingFactor * DefaultHorizontalTitleBarButtonOffset;
+ [self.customTitleBarButtonCenterXConstraints enumerateObjectsUsingBlock:^(NSLayoutConstraint* buttonConstraint, NSUInteger index, BOOL *stop)
+ {
+ buttonConstraint.constant = (self.customTitleBarHeight / 2.0 + (index * horizontalButtonOffset));
+ }];
+ [self setWindowControlsHidden:!self.customTitleBarControlsVisible];
+ [self updateCustomTitleBarInsets:self.customTitleBarControlsVisible];
+
+ [self updateFullScreenButtons];
+}
+
+- (void) resetCustomTitleBar {
+ // See [setUpCustomTitleBar] for the view hierarchy we're working with
+ NSView* closeButtonView = [self.nsWindow standardWindowButton:NSWindowCloseButton];
+ NSView* titlebar = closeButtonView.superview;
+ NSView* titlebarContainer = titlebar.superview;
+ if (!titlebarContainer) {
+ NSLog(@"WARNING: resetCustomTitleBar closeButtonView=%@, titlebar=%@, titlebarContainer=%@",
+ closeButtonView, titlebar, titlebarContainer);
+ return;
+ }
+
+ [NSLayoutConstraint deactivateConstraints:self.customTitleBarConstraints];
+
+ AWTWindowDragView* windowDragView = nil;
+ for (NSView* view in titlebar.subviews) {
+ if ([view isMemberOfClass:[AWTWindowDragView class]]) {
+ windowDragView = view;
+ }
+ }
+
+ if (windowDragView != nil) {
+ [windowDragView removeFromSuperview];
+ }
+
+ titlebarContainer.translatesAutoresizingMaskIntoConstraints = YES;
+ titlebar.translatesAutoresizingMaskIntoConstraints = YES;
+
+ self.customTitleBarConstraints = nil;
+ self.customTitleBarHeightConstraint = nil;
+ self.customTitleBarButtonCenterXConstraints = nil;
+
+ [self setWindowControlsHidden:NO];
+ [self updateCustomTitleBarInsets:NO];
+
+ [self.nsWindow setIgnoreMove:NO];
+}
+
+- (void) setWindowControlsHidden: (BOOL) hidden {
+ if (_fullScreenOriginalButtons != nil) {
+ [_fullScreenOriginalButtons.window.contentView setHidden:NO];
+ _fullScreenButtons.hidden = YES;
+ }
+
+ [self.nsWindow standardWindowButton:NSWindowCloseButton].hidden = hidden;
+ [self.nsWindow standardWindowButton:NSWindowZoomButton].hidden = hidden;
+ [self.nsWindow standardWindowButton:NSWindowMiniaturizeButton].hidden = hidden;
+}
+
+- (void) setWindowFullScreenControls {
+ JNIEnv *env = [ThreadUtilities getJNIEnvUncached];
+ NSString *dfmMode = [PropertiesUtilities javaSystemPropertyForKey:@"apple.awt.distraction.free.mode" withEnv:env];
+ if ([@"true" isCaseInsensitiveLike:dfmMode]) {
+ return;
+ }
+
+ NSView* oldCloseButton = [self.nsWindow standardWindowButton:NSWindowCloseButton];
+ _fullScreenOriginalButtons = oldCloseButton.superview;
+
+ NSRect closeButtonRect = [oldCloseButton frame];
+ NSRect miniaturizeButtonRect = [[self.nsWindow standardWindowButton:NSWindowMiniaturizeButton] frame];
+ NSRect zoomButtonRect = [[self.nsWindow standardWindowButton:NSWindowZoomButton] frame];
+
+ for (NSWindow* window in [[NSApplication sharedApplication] windows]) {
+ if ([window isKindOfClass:NSClassFromString(@"NSToolbarFullScreenWindow")]) {
+ [window.contentView setHidden:YES];
+ }
+ }
+
+ _fullScreenButtons = [[AWTButtonsView alloc] init];
+ [self updateFullScreenButtons];
+ [_fullScreenButtons addTrackingArea:[[NSTrackingArea alloc] initWithRect:[_fullScreenButtons visibleRect]
+ options:(NSTrackingActiveAlways | NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved)
+ owner:_fullScreenButtons userInfo:nil]];
+
+ NSUInteger masks = [self.nsWindow styleMask];
+
+ NSButton *closeButton = [NSWindow standardWindowButton:NSWindowCloseButton forStyleMask:masks];
+ [closeButton setFrame:closeButtonRect];
+ [_fullScreenButtons addSubview:closeButton];
+
+ NSButton *miniaturizeButton = [NSWindow standardWindowButton:NSWindowMiniaturizeButton forStyleMask:masks];
+ [miniaturizeButton setFrame:miniaturizeButtonRect];
+ [_fullScreenButtons addSubview:miniaturizeButton];
+
+ NSButton *zoomButton = [NSWindow standardWindowButton:NSWindowZoomButton forStyleMask:masks];
+ [zoomButton setFrame:zoomButtonRect];
+ [_fullScreenButtons addSubview:zoomButton];
+
+ [self.nsWindow.contentView addSubview:_fullScreenButtons];
+
+ [self updateColors];
+}
+
+- (void)updateColors {
+ if (_fullScreenButtons != nil) {
+ [(AWTButtonsView *)_fullScreenButtons configureColors];
+ }
+}
+
+- (void)updateFullScreenButtons {
+ if (_fullScreenButtons == nil || _fullScreenOriginalButtons == nil) {
+ return;
+ }
+
+ NSView *parent = self.nsWindow.contentView;
+ CGFloat w = 80;
+ CGFloat h = _fullScreenOriginalButtons.frame.size.height;
+ CGFloat x = 6;
+ CGFloat y = parent.frame.size.height - h - (self.customTitleBarHeight - h) / 2.0;
+
+ [_fullScreenButtons setFrame:NSMakeRect(x, y, w - x, h)];
+}
+
+- (void)updateFullScreenButtons: (BOOL) dfm {
+ if (dfm) {
+ if (_fullScreenButtons == nil || _fullScreenOriginalButtons == nil) {
+ return;
+ }
+ [_fullScreenOriginalButtons.window.contentView setHidden:NO];
+ [self resetWindowFullScreenControls];
+ } else {
+ if (!self.isCustomTitleBarEnabled || _fullScreenButtons != nil) {
+ return;
+ }
+ [self setWindowFullScreenControls];
+ }
+}
+
+- (void) resetWindowFullScreenControls {
+ if (_fullScreenButtons != nil) {
+ [_fullScreenButtons removeFromSuperview];
+ _fullScreenButtons = nil;
+ _fullScreenOriginalButtons = nil;
+ }
+}
+
+- (BOOL) isFullScreen {
+ NSUInteger masks = [self.nsWindow styleMask];
+ return (masks & NSWindowStyleMaskFullScreen) != 0;
+}
+
+- (void) forceHideCustomTitleBarTitle: (BOOL) hide {
+ jint bits = self.styleBits;
+ if (hide) bits &= ~MASK(TITLE_VISIBLE);
+ [self setPropertiesForStyleBits:bits mask:MASK(TITLE_VISIBLE)];
+}
+
+- (void) updateCustomTitleBar {
+ _customTitleBarHeight = -1.0f; // Reset for lazy init
+ BOOL enabled = self.isCustomTitleBarEnabled;
+ BOOL fullscreen = self.isFullScreen;
+
+ jint mask = [AWTWindow affectedStyleMaskForCustomTitleBar];
+ jint newBits = [AWTWindow overrideStyleBits:self.styleBits customTitleBarEnabled:enabled fullscreen:fullscreen];
+ // Copied from nativeSetNSWindowStyleBits:
+ // The content view must be resized first, otherwise the window will be resized to fit the existing
+ // content view.
+ NSUInteger styleMask = [AWTWindow styleMaskForStyleBits:newBits];
+ if (!fullscreen) {
+ NSRect frame = [nsWindow frame];
+ NSRect screenContentRect = [NSWindow contentRectForFrameRect:frame styleMask:styleMask];
+ NSRect contentFrame = NSMakeRect(screenContentRect.origin.x - frame.origin.x,
+ screenContentRect.origin.y - frame.origin.y,
+ screenContentRect.size.width,
+ screenContentRect.size.height);
+ nsWindow.contentView.frame = contentFrame;
+ }
+ // NSWindowStyleMaskFullScreen bit shouldn't be updated directly
+ [nsWindow setStyleMask:(((NSWindowStyleMask) styleMask) & ~NSWindowStyleMaskFullScreen |
+ nsWindow.styleMask & NSWindowStyleMaskFullScreen)];
+ // calls methods on NSWindow to change other properties, based on the mask
+ [self setPropertiesForStyleBits:newBits mask:mask];
+ if (!fullscreen && !self.nsWindow.miniaturized) [self _deliverMoveResizeEvent];
+
+ if (enabled != (self.customTitleBarConstraints != nil)) {
+ if (!fullscreen) {
+ if (self.isCustomTitleBarEnabled) {
+ [self setUpCustomTitleBar];
+ } else {
+ [self resetCustomTitleBar];
+ }
+ } else {
+ [self updateFullScreenButtons];
+ }
+ } else if (enabled) {
+ [self updateCustomTitleBarConstraints];
+ }
+}
+
+@end // AWTWindow
+
+@implementation AWTWindowDragView {
+ BOOL _dragging;
+}
+
+- (id) initWithPlatformWindow:(jobject)javaPlatformWindow {
+ self = [super init];
+ if (self == nil) return nil; // no hope
+
+ self.javaPlatformWindow = javaPlatformWindow;
+ return self;
+}
+
+- (BOOL) areCustomTitleBarNativeActionsAllowed {
+ JNIEnv *env = [ThreadUtilities getJNIEnvUncached];
+ GET_CPLATFORM_WINDOW_CLASS_RETURN(YES);
+ DECLARE_FIELD_RETURN(jf_target, jc_CPlatformWindow, "target", "Ljava/awt/Window;", YES);
+ DECLARE_CLASS_RETURN(jc_Window, "java/awt/Window", YES);
+ DECLARE_FIELD_RETURN(jf_customTitleBarHitTest, jc_Window, "customTitleBarHitTest", "I", YES);
-- (void)windowWillEnterFullScreen:(NSNotification *)notification {
- JNIEnv *env = [ThreadUtilities getJNIEnv];
- GET_CPLATFORM_WINDOW_CLASS();
- DECLARE_METHOD(jm_windowWillEnterFullScreen, jc_CPlatformWindow, "windowWillEnterFullScreen", "()V");
jobject platformWindow = (*env)->NewLocalRef(env, self.javaPlatformWindow);
- if (platformWindow != NULL) {
- (*env)->CallVoidMethod(env, platformWindow, jm_windowWillEnterFullScreen);
- CHECK_EXCEPTION();
- [self _notifyFullScreenOp:com_apple_eawt_FullScreenHandler_FULLSCREEN_WILL_ENTER withEnv:env];
- (*env)->DeleteLocalRef(env, platformWindow);
+ if (!platformWindow) return YES;
+ jint hitTest = java_awt_Window_CustomTitleBar_HIT_UNDEFINED;
+ jobject target = (*env)->GetObjectField(env, platformWindow, jf_target);
+ if (target) {
+ hitTest = (jint) (*env)->GetIntField(env, target, jf_customTitleBarHitTest);
+ (*env)->DeleteLocalRef(env, target);
}
+ CHECK_EXCEPTION();
+ (*env)->DeleteLocalRef(env, platformWindow);
+ return hitTest <= java_awt_Window_CustomTitleBar_HIT_TITLEBAR;
}
-- (void)windowDidEnterFullScreen:(NSNotification *)notification {
- JNIEnv *env = [ThreadUtilities getJNIEnv];
- GET_CPLATFORM_WINDOW_CLASS();
- DECLARE_METHOD(jm_windowDidEnterFullScreen, jc_CPlatformWindow, "windowDidEnterFullScreen", "()V");
- jobject platformWindow = (*env)->NewLocalRef(env, self.javaPlatformWindow);
- if (platformWindow != NULL) {
- (*env)->CallVoidMethod(env, platformWindow, jm_windowDidEnterFullScreen);
- CHECK_EXCEPTION();
- [self _notifyFullScreenOp:com_apple_eawt_FullScreenHandler_FULLSCREEN_DID_ENTER withEnv:env];
- (*env)->DeleteLocalRef(env, platformWindow);
- }
- [AWTWindow synthesizeMouseEnteredExitedEventsForAllWindows];
+- (BOOL) acceptsFirstMouse:(NSEvent *)event {
+ return YES;
}
-- (void)windowWillExitFullScreen:(NSNotification *)notification {
- JNIEnv *env = [ThreadUtilities getJNIEnv];
- GET_CPLATFORM_WINDOW_CLASS();
- DECLARE_METHOD(jm_windowWillExitFullScreen, jc_CPlatformWindow, "windowWillExitFullScreen", "()V");
- if (jm_windowWillExitFullScreen == NULL) {
- GET_CPLATFORM_WINDOW_CLASS();
- jm_windowWillExitFullScreen = (*env)->GetMethodID(env, jc_CPlatformWindow, "windowWillExitFullScreen", "()V");
- }
- CHECK_NULL(jm_windowWillExitFullScreen);
- jobject platformWindow = (*env)->NewLocalRef(env, self.javaPlatformWindow);
- if (platformWindow != NULL) {
- (*env)->CallVoidMethod(env, platformWindow, jm_windowWillExitFullScreen);
- CHECK_EXCEPTION();
- [self _notifyFullScreenOp:com_apple_eawt_FullScreenHandler_FULLSCREEN_WILL_EXIT withEnv:env];
- (*env)->DeleteLocalRef(env, platformWindow);
+- (BOOL) shouldDelayWindowOrderingForEvent:(NSEvent *)event {
+ return [[self.window contentView] shouldDelayWindowOrderingForEvent:event];
+}
+- (void) mouseDown: (NSEvent *)event {
+ _dragging = NO;
+ [[self.window contentView] mouseDown:event];
+}
+- (void) mouseUp: (NSEvent *)event {
+ [[self.window contentView] mouseUp:event];
+ if (event.clickCount == 2 && [self areCustomTitleBarNativeActionsAllowed]) {
+ NSString *action = [[NSUserDefaults standardUserDefaults] stringForKey:@"AppleActionOnDoubleClick"];
+ if (action != nil && [action caseInsensitiveCompare:@"Minimize"] == NSOrderedSame) {
+ [self.window performMiniaturize:nil];
+ } else if (action == nil || [action caseInsensitiveCompare:@"None"] != NSOrderedSame) { // action == "Maximize" (default)
+ [self.window performZoom:nil];
+ }
}
}
-
-- (void)windowDidExitFullScreen:(NSNotification *)notification {
- JNIEnv *env = [ThreadUtilities getJNIEnv];
- jobject platformWindow = (*env)->NewLocalRef(env, self.javaPlatformWindow);
- if (platformWindow != NULL) {
- GET_CPLATFORM_WINDOW_CLASS();
- DECLARE_METHOD(jm_windowDidExitFullScreen, jc_CPlatformWindow, "windowDidExitFullScreen", "()V");
- (*env)->CallVoidMethod(env, platformWindow, jm_windowDidExitFullScreen);
- CHECK_EXCEPTION();
- [self _notifyFullScreenOp:com_apple_eawt_FullScreenHandler_FULLSCREEN_DID_EXIT withEnv:env];
- (*env)->DeleteLocalRef(env, platformWindow);
+- (void) rightMouseDown: (NSEvent *)event {
+ [[self.window contentView] rightMouseDown:event];
+}
+- (void) rightMouseUp: (NSEvent *)event {
+ [[self.window contentView] rightMouseUp:event];
+}
+- (void) otherMouseDown: (NSEvent *)event {
+ [[self.window contentView] otherMouseDown:event];
+}
+- (void) otherMouseUp: (NSEvent *)event {
+ [[self.window contentView] otherMouseUp:event];
+}
+- (void) mouseMoved: (NSEvent *)event {
+ [[self.window contentView] mouseMoved:event];
+}
+- (void) mouseDragged: (NSEvent *)event {
+ if (!_dragging) {
+ _dragging = YES;
+ if ([self areCustomTitleBarNativeActionsAllowed]) {
+ [self.window performWindowDragWithEvent:event];
+ return;
+ }
}
- [AWTWindow synthesizeMouseEnteredExitedEventsForAllWindows];
+ [[self.window contentView] mouseDragged:event];
+}
+- (void) rightMouseDragged: (NSEvent *)event {
+ [[self.window contentView] rightMouseDragged:event];
+}
+- (void) otherMouseDragged: (NSEvent *)event {
+ [[self.window contentView] otherMouseDragged:event];
+}
+- (void) mouseEntered: (NSEvent *)event {
+ [[self.window contentView] mouseEntered:event];
+}
+- (void) mouseExited: (NSEvent *)event {
+ [[self.window contentView] mouseExited:event];
+}
+- (void) scrollWheel: (NSEvent*) event {
+ [[self.window contentView] scrollWheel:event];
+}
+- (void) keyDown: (NSEvent *)event {
+ [[self.window contentView] keyDown:event];
+}
+- (void) keyUp: (NSEvent *)event {
+ [[self.window contentView] keyUp:event];
+}
+- (void) flagsChanged: (NSEvent *)event {
+ [[self.window contentView] flagsChanged:event];
}
-- (void)sendEvent:(NSEvent *)event {
- if ([event type] == NSEventTypeLeftMouseDown ||
- [event type] == NSEventTypeRightMouseDown ||
- [event type] == NSEventTypeOtherMouseDown) {
- if ([self isBlocked]) {
- // Move parent windows to front and make sure that a child window is displayed
- // in front of its nearest parent.
- if (self.ownerWindow != nil) {
- JNIEnv *env = [ThreadUtilities getJNIEnvUncached];
- jobject platformWindow = (*env)->NewLocalRef(env, self.javaPlatformWindow);
- if (platformWindow != NULL) {
- GET_CPLATFORM_WINDOW_CLASS();
- DECLARE_METHOD(jm_orderAboveSiblings, jc_CPlatformWindow, "orderAboveSiblings", "()V");
- (*env)->CallVoidMethod(env,platformWindow, jm_orderAboveSiblings);
- CHECK_EXCEPTION();
- (*env)->DeleteLocalRef(env, platformWindow);
- }
- }
- [self orderChildWindows:YES];
- }
+@end
- NSPoint p = [NSEvent mouseLocation];
- NSRect frame = [self.nsWindow frame];
- NSRect contentRect = [self.nsWindow contentRectForFrameRect:frame];
+@implementation AWTButtonsView
- // Check if the click happened in the non-client area (title bar)
- if (p.y >= (frame.origin.y + contentRect.size.height)) {
- JNIEnv *env = [ThreadUtilities getJNIEnvUncached];
- jobject platformWindow = (*env)->NewLocalRef(env, self.javaPlatformWindow);
- if (platformWindow != NULL) {
- // Currently, no need to deliver the whole NSEvent.
- GET_CPLATFORM_WINDOW_CLASS();
- DECLARE_METHOD(jm_deliverNCMouseDown, jc_CPlatformWindow, "deliverNCMouseDown", "()V");
- (*env)->CallVoidMethod(env, platformWindow, jm_deliverNCMouseDown);
- CHECK_EXCEPTION();
- (*env)->DeleteLocalRef(env, platformWindow);
- }
- }
- }
+- (void)dealloc {
+ [_color release];
+ [super dealloc];
}
-- (void)constrainSize:(NSSize*)size {
- float minWidth = 0.f, minHeight = 0.f;
+- (void)mouseEntered:(NSEvent *)theEvent {
+ [self updateButtons:YES];
+}
- if (IS(self.styleBits, DECORATED)) {
- NSRect frame = [self.nsWindow frame];
- NSRect contentRect = [NSWindow contentRectForFrameRect:frame styleMask:[self.nsWindow styleMask]];
+- (void)mouseExited:(NSEvent *)theEvent {
+ [self updateButtons:NO];
+}
- float top = frame.size.height - contentRect.size.height;
- float left = contentRect.origin.x - frame.origin.x;
- float bottom = contentRect.origin.y - frame.origin.y;
- float right = frame.size.width - (contentRect.size.width + left);
+- (void)configureColors {
+ JNIEnv *env = [ThreadUtilities getJNIEnvUncached];
+ NSString *javaColor = [PropertiesUtilities javaSystemPropertyForKey:@"apple.awt.newFullScreenControls.background" withEnv:env];
- // Speculative estimation: 80 - enough for window decorations controls
- minWidth += left + right + 80;
- minHeight += top + bottom;
- }
+ [_color release];
- minWidth = MAX(1.f, minWidth);
- minHeight = MAX(1.f, minHeight);
+ if (javaColor == nil) {
+ _color = nil;
+ } else {
+ int rgb = [javaColor intValue];
- size->width = MAX(size->width, minWidth);
- size->height = MAX(size->height, minHeight);
+ CGFloat alpha = (((rgb >> 24) & 0xff) / 255.0);
+ CGFloat red = (((rgb >> 16) & 0xff) / 255.0);
+ CGFloat green = (((rgb >> 8) & 0xff) / 255.0);
+ CGFloat blue = (((rgb >> 0) & 0xff) / 255.0);
+
+ _color = [NSColor colorWithDeviceRed:red green:green blue:blue alpha:alpha];
+ [_color retain];
+ }
+
+ [self updateButtons:NO];
}
-- (void) setEnabled: (BOOL)flag {
- self.isEnabled = flag;
+- (void)updateButtons:(BOOL) flag {
+ _showButtons = flag;
- if (IS(self.styleBits, CLOSEABLE)) {
- [[self.nsWindow standardWindowButton:NSWindowCloseButton] setEnabled: flag];
+ if (self.subviews.count == 3) {
+ [self updateButton:0 flag:flag]; // close
+ [self updateButton:1 flag:NO]; // miniaturize
+ [self updateButton:2 flag:flag]; // zoom
}
- if (IS(self.styleBits, MINIMIZABLE)) {
- [[self.nsWindow standardWindowButton:NSWindowMiniaturizeButton] setEnabled: flag];
+ [self setNeedsDisplay:YES];
+}
+
+- (void)updateButton: (int)index flag:(BOOL) flag {
+ NSButton *button = (NSButton*)self.subviews[index];
+ [button setHidden:!flag];
+ [button setHighlighted:flag];
+}
+
+- (void)drawRect: (NSRect)dirtyRect {
+ if (self.subviews.count != 3) {
+ return;
}
- if (IS(self.styleBits, ZOOMABLE)) {
- [[self.nsWindow standardWindowButton:NSWindowZoomButton] setEnabled: flag];
+ if (_color == nil) {
+ [[NSColor whiteColor] setFill];
+ } else {
+ [_color setFill];
}
- if (IS(self.styleBits, RESIZABLE)) {
- [self updateMinMaxSize:flag];
- [self.nsWindow setShowsResizeIndicator:flag];
+ if (_showButtons) {
+ [self drawButton:1]; // miniaturize
+ } else {
+ for (int i = 0; i < 3; i++) {
+ [self drawButton:i];
+ }
}
}
-+ (void) setLastKeyWindow:(AWTWindow *)window {
- [window retain];
- [lastKeyWindow release];
- lastKeyWindow = window;
-}
+- (void)drawButton: (int)index {
+ NSButton *button = (NSButton*)self.subviews[index];
+ NSRect rect = button.frame;
+ CGFloat r = 12;
+ CGFloat x = rect.origin.x + (rect.size.width - r) / 2;
+ CGFloat y = rect.origin.y + (rect.size.height - r) / 2;
-+ (AWTWindow *) lastKeyWindow {
- return lastKeyWindow;
+ NSBezierPath* circlePath = [NSBezierPath bezierPath];
+ [circlePath appendBezierPathWithOvalInRect:CGRectMake(x, y, r, r)];
+ [circlePath fill];
}
-@end // AWTWindow
+@end
/*
* Class: sun_lwawt_macosx_CPlatformWindow
@@ -1118,7 +2096,7 @@ + (AWTWindow *) lastKeyWindow {
/*
* Class: sun_lwawt_macosx_CPlatformWindow
* Method: nativeCreateNSWindow
- * Signature: (JJIIII)J
+ * Signature: (JJIDDDD)J
*/
JNIEXPORT jlong JNICALL Java_sun_lwawt_macosx_CPlatformWindow_nativeCreateNSWindow
(JNIEnv *env, jobject obj, jlong contentViewPtr, jlong ownerPtr, jlong styleBits, jdouble x, jdouble y, jdouble w, jdouble h)
@@ -1131,6 +2109,17 @@ + (AWTWindow *) lastKeyWindow {
NSView *contentView = OBJC(contentViewPtr);
NSRect frameRect = NSMakeRect(x, y, w, h);
AWTWindow *owner = [OBJC(ownerPtr) delegate];
+
+ BOOL isIgnoreMouseEvents = NO;
+ GET_CPLATFORM_WINDOW_CLASS_RETURN(0);
+ DECLARE_FIELD_RETURN(jf_target, jc_CPlatformWindow, "target", "Ljava/awt/Window;", 0);
+ jobject awtWindow = (*env)->GetObjectField(env, obj, jf_target);
+ if (awtWindow != NULL) {
+ DECLARE_CLASS_RETURN(jc_Window, "java/awt/Window", 0);
+ DECLARE_METHOD_RETURN(jm_isIgnoreMouseEvents, jc_Window, "isIgnoreMouseEvents", "()Z", 0);
+ isIgnoreMouseEvents = (*env)->CallBooleanMethod(env, awtWindow, jm_isIgnoreMouseEvents) == JNI_TRUE ? YES : NO;
+ (*env)->DeleteLocalRef(env, awtWindow);
+ }
[ThreadUtilities performOnMainThreadWaiting:YES block:^(){
window = [[AWTWindow alloc] initWithPlatformWindow:platformWindow
@@ -1140,7 +2129,12 @@ + (AWTWindow *) lastKeyWindow {
contentView:contentView];
// the window is released is CPlatformWindow.nativeDispose()
- if (window) [window.nsWindow retain];
+ if (window) {
+ [window.nsWindow retain];
+ if (isIgnoreMouseEvents) {
+ [window.nsWindow setIgnoresMouseEvents:YES];
+ }
+ }
}];
JNI_COCOA_EXIT(env);
@@ -1164,9 +2158,13 @@ + (AWTWindow *) lastKeyWindow {
AWTWindow *window = (AWTWindow*)[nsWindow delegate];
+ BOOL customTitleBarEnabled = window.isCustomTitleBarEnabled;
+ BOOL fullscreen = window.isFullScreen;
// scans the bit field, and only updates the values requested by the mask
// (this implicitly handles the _CALLBACK_PROP_BITMASK case, since those are passive reads)
- jint newBits = window.styleBits & ~mask | bits & mask;
+ jint actualBits = window.styleBits & ~mask | bits & mask;
+ // Force properties if custom title bar is enabled, but store original value in self.styleBits.
+ jint newBits = [AWTWindow overrideStyleBits:actualBits customTitleBarEnabled:customTitleBarEnabled fullscreen:fullscreen];
BOOL resized = NO;
@@ -1174,7 +2172,8 @@ + (AWTWindow *) lastKeyWindow {
// The content view must be resized first, otherwise the window will be resized to fit the existing
// content view.
if (IS(mask, FULL_WINDOW_CONTENT)) {
- if (IS(newBits, FULL_WINDOW_CONTENT) != IS(window.styleBits, FULL_WINDOW_CONTENT)) {
+ if ((IS(newBits, FULL_WINDOW_CONTENT) != IS(window.styleBits, FULL_WINDOW_CONTENT) ||
+ customTitleBarEnabled) && !fullscreen) {
NSRect frame = [nsWindow frame];
NSUInteger styleMask = [AWTWindow styleMaskForStyleBits:newBits];
NSRect screenContentRect = [NSWindow contentRectForFrameRect:frame styleMask:styleMask];
@@ -1185,11 +2184,19 @@ + (AWTWindow *) lastKeyWindow {
nsWindow.contentView.frame = contentFrame;
resized = YES;
}
+ if (window.isJustCreated) {
+ // Perform Move/Resize event for just created windows
+ resized = YES;
+ window.isJustCreated = NO;
+ }
}
// resets the NSWindow's style mask if the mask intersects any of those bits
if (mask & MASK(_STYLE_PROP_BITMASK)) {
- [nsWindow setStyleMask:[AWTWindow styleMaskForStyleBits:newBits]];
+ NSWindowStyleMask styleMask = [AWTWindow styleMaskForStyleBits:newBits];
+ NSWindowStyleMask curMask = nsWindow.styleMask;
+ // NSWindowStyleMaskFullScreen bit shouldn't be updated directly
+ [nsWindow setStyleMask:(styleMask & ~NSWindowStyleMaskFullScreen | curMask & NSWindowStyleMaskFullScreen)];
}
// calls methods on NSWindow to change other properties, based on the mask
@@ -1197,7 +2204,7 @@ + (AWTWindow *) lastKeyWindow {
[window setPropertiesForStyleBits:newBits mask:mask];
}
- window.styleBits = newBits;
+ window.styleBits = actualBits;
if (resized) {
[window _deliverMoveResizeEvent];
@@ -1207,6 +2214,34 @@ + (AWTWindow *) lastKeyWindow {
JNI_COCOA_EXIT(env);
}
+/*
+ * Class: sun_lwawt_macosx_CPlatformWindow
+ * Method: nativeSetNSWindowAppearance
+ * Signature: (JLjava/lang/String;)V
+ */
+JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CPlatformWindow_nativeSetNSWindowAppearance
+ (JNIEnv *env, jclass clazz, jlong windowPtr, jstring appearanceName)
+{
+ JNI_COCOA_ENTER(env);
+
+ NSWindow *nsWindow = OBJC(windowPtr);
+ // create a global-ref around the appearanceName, so it can be safely passed to Main thread
+ jobject appearanceNameRef= (*env)->NewGlobalRef(env, appearanceName);
+
+ [ThreadUtilities performOnMainThreadWaiting:NO block:^(){
+ // attach the dispatch thread to the JVM if necessary, and get an env
+ JNIEnv* blockEnv = [ThreadUtilities getJNIEnvUncached];
+ NSAppearance* appearance = [NSAppearance appearanceNamed:
+ JavaStringToNSString(blockEnv, appearanceNameRef)];
+ if (appearance != NULL) {
+ [nsWindow setAppearance:appearance];
+ }
+ (*blockEnv)->DeleteGlobalRef(blockEnv, appearanceNameRef);
+ }];
+
+ JNI_COCOA_EXIT(env);
+}
+
/*
* Class: sun_lwawt_macosx_CPlatformWindow
* Method: nativeSetNSWindowMenuBar
@@ -1223,7 +2258,7 @@ + (AWTWindow *) lastKeyWindow {
AWTWindow *window = (AWTWindow*)[nsWindow delegate];
- if ([nsWindow isKeyWindow] || [nsWindow isMainWindow]) {
+ if ([nsWindow isMainWindow]) {
[window.javaMenuBar deactivate];
}
@@ -1234,7 +2269,7 @@ + (AWTWindow *) lastKeyWindow {
actualMenuBar = [[ApplicationDelegate sharedDelegate] defaultMenuBar];
}
- if ([nsWindow isKeyWindow] || [nsWindow isMainWindow]) {
+ if ([nsWindow isMainWindow]) {
[CMenuBar activate:actualMenuBar modallyDisabled:NO];
}
}];
@@ -1436,15 +2471,15 @@ + (AWTWindow *) lastKeyWindow {
/*
* Class: sun_lwawt_macosx_CPlatformWindow
* Method: nativePushNSWindowToFront
- * Signature: (J)V
+ * Signature: (JZ)V
*/
JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CPlatformWindow_nativePushNSWindowToFront
-(JNIEnv *env, jclass clazz, jlong windowPtr)
+(JNIEnv *env, jclass clazz, jlong windowPtr, jboolean wait)
{
JNI_COCOA_ENTER(env);
NSWindow *nsWindow = OBJC(windowPtr);
- [ThreadUtilities performOnMainThreadWaiting:NO block:^(){
+ [ThreadUtilities performOnMainThreadWaiting:(BOOL)wait block:^(){
if (![nsWindow isKeyWindow]) {
[nsWindow makeKeyAndOrderFront:nsWindow];
@@ -1456,6 +2491,38 @@ + (AWTWindow *) lastKeyWindow {
JNI_COCOA_EXIT(env);
}
+/*
+ * Class: sun_lwawt_macosx_CPlatformWindow
+ * Method: nativeHideWindow
+ * Signature: (JZ)V
+ */
+JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CPlatformWindow_nativeHideWindow
+(JNIEnv *env, jclass clazz, jlong windowPtr, jboolean wait)
+{
+JNI_COCOA_ENTER(env);
+
+ NSWindow *nsWindow = OBJC(windowPtr);
+ [ThreadUtilities performOnMainThreadWaiting:(BOOL)wait block:^(){
+ if (nsWindow.keyWindow) {
+ // When 'windowDidResignKey' is called during 'orderOut', current key window
+ // is reported as 'nil', so it's impossible to create WINDOW_FOCUS_LOST event
+ // with correct 'opposite' window.
+ // So, as a workaround, we perform focus transfer to a parent window explicitly here.
+ NSWindow *parentWindow = nsWindow;
+ while ((parentWindow = ((AWTWindow*)parentWindow.delegate).ownerWindow.nsWindow) != nil) {
+ if (parentWindow.canBecomeKeyWindow) {
+ [parentWindow makeKeyWindow];
+ break;
+ }
+ }
+ }
+ [nsWindow orderOut:nsWindow];
+ [nsWindow close];
+ }];
+
+JNI_COCOA_EXIT(env);
+}
+
/*
* Class: sun_lwawt_macosx_CPlatformWindow
* Method: nativeSetNSWindowTitle
@@ -1617,6 +2684,21 @@ + (AWTWindow *) lastKeyWindow {
JNI_COCOA_EXIT(env);
}
+// undocumented approach which avoids focus stealing
+// and can be used full screen switch is in progress for another window
+void enableFullScreenSpecial(NSWindow *nsWindow) {
+ NSKeyedArchiver *coder = [[NSKeyedArchiver alloc] init];
+ [nsWindow encodeRestorableStateWithCoder:coder];
+ [coder encodeBool:YES forKey:@"NSIsFullScreen"];
+ NSKeyedUnarchiver *decoder = [[NSKeyedUnarchiver alloc] initForReadingWithData:coder.encodedData];
+ decoder.requiresSecureCoding = YES;
+ decoder.decodingFailurePolicy = NSDecodingFailurePolicySetErrorAndReturn;
+ [nsWindow restoreStateWithCoder:decoder];
+ [decoder finishDecoding];
+ [decoder release];
+ [coder release];
+}
+
/*
* Class: sun_lwawt_macosx_CPlatformWindow
* Method: _toggleFullScreenMode
@@ -1632,7 +2714,17 @@ + (AWTWindow *) lastKeyWindow {
if (![nsWindow respondsToSelector:toggleFullScreenSelector]) return;
[ThreadUtilities performOnMainThreadWaiting:NO block:^(){
+ static BOOL inProgress = NO;
+ if ((nsWindow.styleMask & NSWindowStyleMaskFullScreen) != NSWindowStyleMaskFullScreen &&
+ (inProgress || !NSApp.active)) {
+ enableFullScreenSpecial(nsWindow);
+ if ((nsWindow.styleMask & NSWindowStyleMaskFullScreen) == NSWindowStyleMaskFullScreen) return; // success
+ // otherwise fall back to standard approach
+ }
+ BOOL savedValue = inProgress;
+ inProgress = YES;
[nsWindow performSelector:toggleFullScreenSelector withObject:nil];
+ inProgress = savedValue;
}];
JNI_COCOA_EXIT(env);
@@ -1673,6 +2765,8 @@ + (AWTWindow *) lastKeyWindow {
[nsWindow setDelegate: nil];
[window release];
+
+ ignoreResizeWindowDuringAnotherWindowEnd = NO;
}];
JNI_COCOA_EXIT(env);
@@ -1734,3 +2828,111 @@ + (AWTWindow *) lastKeyWindow {
JNI_COCOA_EXIT(env);
}
+JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CPlatformWindow_nativeRaiseLevel
+(JNIEnv *env, jclass clazz, jlong windowPtr, jboolean popup, jboolean onlyIfParentIsActive)
+{
+JNI_COCOA_ENTER(env);
+
+ NSWindow *nsWindow = OBJC(windowPtr);
+ [ThreadUtilities performOnMainThreadWaiting:NO block:^(){
+ AWTWindow *window = (AWTWindow*)[nsWindow delegate];
+ if (onlyIfParentIsActive) {
+ AWTWindow *parent = window;
+ do {
+ parent = parent.ownerWindow;
+ } while (parent != nil && !parent.nsWindow.isMainWindow);
+ if (parent == nil) {
+ return;
+ }
+ }
+ [nsWindow setLevel: popup ? NSPopUpMenuWindowLevel : NSFloatingWindowLevel];
+ }];
+
+JNI_COCOA_EXIT(env);
+}
+
+/*
+ * Class: sun_lwawt_macosx_CPlatformWindow
+ * Method: nativeDelayShowing
+ * Signature: (J)Z
+ */
+JNIEXPORT jboolean JNICALL Java_sun_lwawt_macosx_CPlatformWindow_nativeDelayShowing
+(JNIEnv *env, jclass clazz, jlong windowPtr)
+{
+ __block jboolean result = JNI_FALSE;
+
+ JNI_COCOA_ENTER(env);
+
+ NSWindow *nsWindow = (NSWindow *)jlong_to_ptr(windowPtr);
+ [ThreadUtilities performOnMainThreadWaiting:YES block:^(){
+ AWTWindow *window = (AWTWindow*)[nsWindow delegate];
+ result = [window delayShowing];
+ }];
+
+ JNI_COCOA_EXIT(env);
+
+ return result;
+}
+
+
+JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CPlatformWindow_nativeUpdateCustomTitleBar
+(JNIEnv *env, jclass clazz, jlong windowPtr)
+{
+ JNI_COCOA_ENTER(env);
+
+ NSWindow *nsWindow = (NSWindow *)jlong_to_ptr(windowPtr);
+ [ThreadUtilities performOnMainThreadWaiting:YES block:^(){
+ AWTWindow *window = (AWTWindow*)[nsWindow delegate];
+ [window updateCustomTitleBar];
+ }];
+
+ JNI_COCOA_EXIT(env);
+}
+
+JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CPlatformWindow_nativeCallDeliverMoveResizeEvent
+(JNIEnv *env, jclass clazz, jlong windowPtr)
+{
+ JNI_COCOA_ENTER(env);
+
+ NSWindow *nsWindow = (NSWindow *)jlong_to_ptr(windowPtr);
+ [ThreadUtilities performOnMainThreadWaiting:NO block:^(){
+ AWTWindow *window = (AWTWindow*)[nsWindow delegate];
+ [window _deliverMoveResizeEvent];
+ }];
+
+ JNI_COCOA_EXIT(env);
+}
+
+JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CPlatformWindow_nativeSetRoundedCorners
+(JNIEnv *env, jclass clazz, jlong windowPtr, jfloat radius, jint borderWidth, jint borderRgb)
+{
+ JNI_COCOA_ENTER(env);
+
+ NSWindow *w = (NSWindow *)jlong_to_ptr(windowPtr);
+ [ThreadUtilities performOnMainThreadWaiting:NO block:^(){
+ w.hasShadow = YES;
+ w.contentView.wantsLayer = YES;
+ w.contentView.layer.cornerRadius = radius;
+ w.contentView.layer.masksToBounds = YES;
+ w.contentView.layer.opaque = NO;
+
+ if (borderWidth > 0) {
+ CGFloat alpha = (((borderRgb >> 24) & 0xff) / 255.0);
+ CGFloat red = (((borderRgb >> 16) & 0xff) / 255.0);
+ CGFloat green = (((borderRgb >> 8) & 0xff) / 255.0);
+ CGFloat blue = (((borderRgb >> 0) & 0xff) / 255.0);
+ NSColor *color = [NSColor colorWithDeviceRed:red green:green blue:blue alpha:alpha];
+
+ w.contentView.layer.borderWidth = borderWidth;
+ w.contentView.layer.borderColor = color.CGColor;
+ }
+
+ w.backgroundColor = NSColor.clearColor;
+ w.opaque = NO;
+ // remove corner radius animation
+ [w.contentView.layer removeAllAnimations];
+ [w invalidateShadow];
+ }];
+
+ JNI_COCOA_EXIT(env);
+}
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/ApplicationDelegate.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/ApplicationDelegate.m
index a8a821423de4..0e1a96330e8a 100644
--- a/src/java.desktop/macosx/native/libawt_lwawt/awt/ApplicationDelegate.m
+++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/ApplicationDelegate.m
@@ -37,6 +37,7 @@
#import "ThreadUtilities.h"
#import "NSApplicationAWT.h"
#import "JNIUtilities.h"
+#import "AWTWindow.h"
#pragma mark App Menu helpers
@@ -258,6 +259,11 @@ - (id) init {
[ctr addObserver:clz selector:@selector(_appDidDeactivate) name:NSApplicationDidResignActiveNotification object:nil];
[ctr addObserver:clz selector:@selector(_appDidHide) name:NSApplicationDidHideNotification object:nil];
[ctr addObserver:clz selector:@selector(_appDidUnhide) name:NSApplicationDidUnhideNotification object:nil];
+ [ctr addObserver:clz selector:@selector(_didChangeScreenParameters) name:NSApplicationDidChangeScreenParametersNotification object:nil];
+ [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:[AWTWindow class]
+ selector:@selector(activeSpaceDidChange)
+ name:NSWorkspaceActiveSpaceDidChangeNotification
+ object:nil];
return self;
}
@@ -491,6 +497,10 @@ + (void)_registerForNotification:(NSNumber *)notificationTypeNum {
}
}
++ (void)_didChangeScreenParameters {
+ [ApplicationDelegate _notifyJava:com_apple_eawt__AppEventHandler_NOTIFY_SCREEN_CHANGE_PARAMETERS];
+}
+
// Retrieves the menu to be attached to the Dock icon (AppKit callback)
- (NSMenu *)applicationDockMenu:(NSApplication *)sender {
AWT_ASSERT_APPKIT_THREAD;
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/CClipboard.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/CClipboard.m
index 9818300f6a6b..866defd7f4eb 100644
--- a/src/java.desktop/macosx/native/libawt_lwawt/awt/CClipboard.m
+++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/CClipboard.m
@@ -24,6 +24,7 @@
*/
#import "CDataTransferer.h"
+#include "sun_lwawt_macosx_CDataTransferer.h"
#import "ThreadUtilities.h"
#import "JNIUtilities.h"
#import
@@ -174,6 +175,48 @@ - (BOOL) checkPasteboardWithoutNotification:(id)application {
JNI_COCOA_EXIT(env);
}
+/*
+ * Class: sun_lwawt_macosx_CClipboard
+ * Method: writeObjects
+ * Signature: ([BJ)V
+*/
+JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CClipboard_writeObjects
+(JNIEnv *env, jobject inObject, jbyteArray inBytes, jlong inFormat)
+{
+ if (inBytes == NULL) {
+ return;
+ }
+
+
+JNI_COCOA_ENTER(env);
+ jint nBytes = (*env)->GetArrayLength(env, inBytes);
+ jbyte *rawBytes = (*env)->GetPrimitiveArrayCritical(env, inBytes, NULL);
+ CHECK_NULL(rawBytes);
+ NSData *bytesAsData = [NSData dataWithBytes:rawBytes length:nBytes];
+ (*env)->ReleasePrimitiveArrayCritical(env, inBytes, rawBytes, JNI_ABORT);
+ NSString *format = formatForIndex(inFormat);
+ NSMutableArray *formatArray = [NSMutableArray arrayWithCapacity:2];
+ const char *bytes = [bytesAsData bytes];
+ BOOL isStart=YES;
+ for (int i = 0; i < [bytesAsData length]; i++) {
+ if ((unsigned char)bytes[i] == 0) {
+ isStart = YES;
+ } else {
+ if (isStart) {
+ isStart = NO;
+ NSString *path = [NSString stringWithUTF8String:(const char *)&bytes[i]];
+ NSURL *fileURL = [NSURL fileURLWithPath:path relativeToURL:nil];
+ [formatArray addObject:fileURL];
+ }
+ }
+ }
+ [ThreadUtilities performOnMainThreadWaiting:YES block:^() {
+ [[NSPasteboard generalPasteboard] writeObjects:formatArray];
+ }];
+JNI_COCOA_EXIT(env);
+}
+
+
/*
* Class: sun_lwawt_macosx_CClipboard
* Method: getClipboardFormats
@@ -260,13 +303,22 @@ - (BOOL) checkPasteboardWithoutNotification:(id)application {
}
NSUInteger dataSize = [clipData length];
+ const void *dataBuffer = [clipData bytes];
+ if (format == sun_lwawt_macosx_CDataTransferer_CF_STRING && dataSize >= 3) {
+ const unsigned char *bytesPtr = (const unsigned char *)dataBuffer;
+ if (bytesPtr[0] == 0xEF && bytesPtr[1] == 0xBB && bytesPtr[2] == 0xBF) {
+ // strip BOM from string content, like native applications do
+ dataSize -= 3;
+ dataBuffer = (const void *)(bytesPtr + 3);
+ }
+ }
+
returnValue = (*env)->NewByteArray(env, dataSize);
if (returnValue == NULL) {
return NULL;
}
if (dataSize != 0) {
- const void *dataBuffer = [clipData bytes];
(*env)->SetByteArrayRegion(env, returnValue, 0, dataSize, (jbyte *)dataBuffer);
}
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/CDragSource.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/CDragSource.m
index ddb8630fb67b..a70850ee3faa 100644
--- a/src/java.desktop/macosx/native/libawt_lwawt/awt/CDragSource.m
+++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/CDragSource.m
@@ -237,7 +237,7 @@ - (jbyteArray)convertData:(jlong)format
jobject transferer = [self dataTransferer:env];
jbyteArray data = nil;
- if (transferer != NULL) {
+ if (transferer != NULL && fComponent != NULL) {
GET_DT_CLASS_RETURN(NULL);
DECLARE_METHOD_RETURN(convertDataMethod, DataTransfererClass, "convertData", "(Ljava/lang/Object;Ljava/awt/datatransfer/Transferable;JLjava/util/Map;Z)[B", NULL);
data = (*env)->CallObjectMethod(env, transferer, convertDataMethod, fComponent, fTransferable, format, fFormatMap, (jboolean) TRUE);
@@ -744,8 +744,14 @@ - (void) postDragExit {
// Also see +[CWindow convertAWTToCocoaScreenRect]
// NSScreen-to-JavaScreen mapping:
- (NSPoint) mapNSScreenPointToJavaWithOffset:(NSPoint)screenPoint {
- NSRect mainR = [[[NSScreen screens] objectAtIndex:0] frame];
- NSPoint point = NSMakePoint(screenPoint.x, mainR.size.height - (screenPoint.y));
+ NSScreen *primaryScreen = [[NSScreen screens] firstObject];
+ NSPoint point = NSMakePoint(0, 0);
+ if (primaryScreen == nil) {
+ return point;
+ }
+
+ NSRect mainR = [primaryScreen frame];
+ point = NSMakePoint(screenPoint.x, mainR.size.height - (screenPoint.y));
// Adjust the point with the drag image offset to get the real mouse hotspot:
// The point should remain in screen coordinates (as per DragSourceEvent.getLocation() documentation)
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/CFileDialog.h b/src/java.desktop/macosx/native/libawt_lwawt/awt/CFileDialog.h
index 677c9715679e..45e841e244a1 100644
--- a/src/java.desktop/macosx/native/libawt_lwawt/awt/CFileDialog.h
+++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/CFileDialog.h
@@ -26,6 +26,8 @@
#import
@interface CFileDialog : NSObject {
+ NSWindow* fOwner;
+
// Should we query back to Java for a file filter?
jboolean fHasFileFilter;
@@ -54,12 +56,19 @@
// Can the dialog choose directories ?
BOOL fChooseDirectories;
+ // Can the dialog choose files ?
+ BOOL fChooseFiles;
+
+ // Can the dialog create directories ?
+ BOOL fCreateDirectories;
+
// Contains the absolute paths of the selected files as URLs
NSArray *fURLs;
}
// Allocator
-- (id) initWithFilter:(jboolean)inHasFilter
+- (id) initWithOwner:(NSWindow*) owner
+ filter:(jboolean)inHasFilter
fileDialog:(jobject)inDialog
title:(NSString *)inTitle
directory:(NSString *)inPath
@@ -68,6 +77,8 @@
multipleMode:(BOOL)inMultipleMode
shouldNavigate:(BOOL)inNavigateApps
canChooseDirectories:(BOOL)inChooseDirectories
+ canChooseFiles:(BOOL)inChooseFiles
+ canCreateDirectories:(BOOL)inCreateDirectories
withEnv:(JNIEnv*)env;
// Invoked from the main thread
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/CFileDialog.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/CFileDialog.m
index 3b895be8094f..cf7ea481fa46 100644
--- a/src/java.desktop/macosx/native/libawt_lwawt/awt/CFileDialog.m
+++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/CFileDialog.m
@@ -29,13 +29,17 @@
#import "ThreadUtilities.h"
#import "JNIUtilities.h"
#import "CFileDialog.h"
+#import "AWTWindow.h"
+#import "CMenuBar.h"
+#import "ApplicationDelegate.h"
#import "java_awt_FileDialog.h"
#import "sun_lwawt_macosx_CFileDialog.h"
@implementation CFileDialog
-- (id)initWithFilter:(jboolean)inHasFilter
+- (id)initWithOwner:(NSWindow*)owner
+ filter:(jboolean)inHasFilter
fileDialog:(jobject)inDialog
title:(NSString *)inTitle
directory:(NSString *)inPath
@@ -44,9 +48,13 @@ - (id)initWithFilter:(jboolean)inHasFilter
multipleMode:(BOOL)inMultipleMode
shouldNavigate:(BOOL)inNavigateApps
canChooseDirectories:(BOOL)inChooseDirectories
+ canChooseFiles:(BOOL)inChooseFiles
+canCreateDirectories:(BOOL)inCreateDirectories
withEnv:(JNIEnv*)env;
{
- if (self = [super init]) {
+ if (self = [super init]) {
+ fOwner = owner;
+ [fOwner retain];
fHasFileFilter = inHasFilter;
fFileDialog = (*env)->NewGlobalRef(env, inDialog);
fDirectory = inPath;
@@ -59,6 +67,8 @@ - (id)initWithFilter:(jboolean)inHasFilter
fMultipleMode = inMultipleMode;
fNavigateApps = inNavigateApps;
fChooseDirectories = inChooseDirectories;
+ fChooseFiles = inChooseFiles;
+ fCreateDirectories = inCreateDirectories;
fPanelResult = NSCancelButton;
}
@@ -86,6 +96,9 @@ -(void) dealloc {
[fURLs release];
fURLs = nil;
+ [fOwner release];
+ fOwner = nil;
+
[super dealloc];
}
@@ -117,14 +130,85 @@ - (void)safeSaveOrLoad {
if (fMode == java_awt_FileDialog_LOAD) {
NSOpenPanel *openPanel = (NSOpenPanel *)thePanel;
[openPanel setAllowsMultipleSelection:fMultipleMode];
- [openPanel setCanChooseFiles:!fChooseDirectories];
+ [openPanel setCanChooseFiles:fChooseFiles];
[openPanel setCanChooseDirectories:fChooseDirectories];
- [openPanel setCanCreateDirectories:YES];
+ [openPanel setCanCreateDirectories:fCreateDirectories];
}
[thePanel setDelegate:self];
- fPanelResult = [thePanel runModalForDirectory:fDirectory file:fFile];
- [thePanel setDelegate:nil];
+
+ NSMenuItem *editMenuItem = nil;
+ NSMenu *menu = [NSApp mainMenu];
+ if (menu != nil) {
+ if (menu.numberOfItems > 0) {
+ NSMenu *submenu = [[menu itemAtIndex:0] submenu];
+ if (submenu != nil) {
+ menu = submenu;
+ }
+ }
+
+ editMenuItem = [self createEditMenu];
+ if (menu.numberOfItems > 0) {
+ [menu insertItem:editMenuItem atIndex:0];
+ } else {
+ [menu addItem:editMenuItem];
+ }
+ [editMenuItem release];
+ }
+
+
+ if (fOwner != nil) {
+ if (fDirectory != nil) {
+ [thePanel setDirectoryURL:[NSURL fileURLWithPath:[fDirectory stringByExpandingTildeInPath]]];
+ }
+
+ if (fFile != nil) {
+ [thePanel setNameFieldStringValue:fFile];
+ }
+
+ CMenuBar *menuBar = nil;
+ if (fOwner != nil) {
+
+ // Finds appropriate menubar in our hierarchy,
+ AWTWindow *awtWindow = (AWTWindow *)fOwner.delegate;
+ while (awtWindow.ownerWindow != nil) {
+ awtWindow = awtWindow.ownerWindow;
+ }
+
+ BOOL isDisabled = NO;
+ if ([awtWindow.nsWindow isVisible]){
+ menuBar = awtWindow.javaMenuBar;
+ isDisabled = !awtWindow.isEnabled;
+ }
+
+ if (menuBar == nil) {
+ menuBar = [[ApplicationDelegate sharedDelegate] defaultMenuBar];
+ isDisabled = NO;
+ }
+
+ [CMenuBar activate:menuBar modallyDisabled:isDisabled];
+ }
+
+ if (@available(macOS 10.14, *)) {
+ [thePanel setAppearance:fOwner.appearance];
+ }
+
+ fPanelResult = [thePanel runModal];
+
+ if (menuBar != nil) {
+ [CMenuBar activate:menuBar modallyDisabled:NO];
+ }
+ }
+ else
+ {
+ fPanelResult = [thePanel runModalForDirectory:fDirectory file:fFile];
+ CMenuBar *menuBar = [[ApplicationDelegate sharedDelegate] defaultMenuBar];
+ [CMenuBar activate:menuBar modallyDisabled:NO];
+ }
+
+ if (editMenuItem != nil) {
+ [menu removeItem:editMenuItem];
+ }
if ([self userClickedOK]) {
if (fMode == java_awt_FileDialog_LOAD) {
@@ -135,11 +219,40 @@ - (void)safeSaveOrLoad {
}
[fURLs retain];
}
+
+ [thePanel setDelegate:nil];
}
[self disposer];
}
+- (NSMenuItem *) createEditMenu {
+ NSMenu *editMenu = [[NSMenu alloc] initWithTitle:@"Edit"];
+
+ NSMenuItem *cutItem = [[NSMenuItem alloc] initWithTitle:@"Cut" action:@selector(cut:) keyEquivalent:@"x"];
+ [editMenu addItem:cutItem];
+ [cutItem release];
+
+ NSMenuItem *copyItem = [[NSMenuItem alloc] initWithTitle:@"Copy" action:@selector(copy:) keyEquivalent:@"c"];
+ [editMenu addItem:copyItem];
+ [copyItem release];
+
+ NSMenuItem *pasteItem = [[NSMenuItem alloc] initWithTitle:@"Paste" action:@selector(paste:) keyEquivalent:@"v"];
+ [editMenu addItem:pasteItem];
+ [pasteItem release];
+
+ NSMenuItem *selectAllItem = [[NSMenuItem alloc] initWithTitle:@"Select All" action:@selector(selectAll:) keyEquivalent:@"a"];
+ [editMenu addItem:selectAllItem];
+ [selectAllItem release];
+
+ NSMenuItem *editMenuItem = [NSMenuItem new];
+ [editMenuItem setTitle:@"Edit"];
+ [editMenuItem setSubmenu:editMenu];
+ [editMenu release];
+
+ return editMenuItem;
+}
+
- (BOOL) askFilenameFilter:(NSString *)filename {
JNIEnv *env = [ThreadUtilities getJNIEnv];
jstring jString = NormalizedPathJavaStringFromNSString(env, filename);
@@ -175,7 +288,7 @@ - (BOOL)panel:(id)sender shouldEnableURL:(NSURL *)url {
}
- (BOOL) userClickedOK {
- return fPanelResult == NSOKButton;
+ return fPanelResult == NSFileHandlingPanelOKButton;
}
- (NSArray *)URLs {
@@ -186,14 +299,12 @@ - (NSArray *)URLs {
/*
* Class: sun_lwawt_macosx_CFileDialog
* Method: nativeRunFileDialog
- * Signature: (Ljava/lang/String;ILjava/io/FilenameFilter;
- * Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;
*/
JNIEXPORT jobjectArray JNICALL
Java_sun_lwawt_macosx_CFileDialog_nativeRunFileDialog
-(JNIEnv *env, jobject peer, jstring title, jint mode, jboolean multipleMode,
- jboolean navigateApps, jboolean chooseDirectories, jboolean hasFilter,
- jstring directory, jstring file)
+(JNIEnv *env, jobject peer, jlong ownerPtr, jstring title, jint mode, jboolean multipleMode,
+ jboolean navigateApps, jboolean chooseDirectories, jboolean chooseFiles, jboolean createDirectories,
+ jboolean hasFilter, jstring directory, jstring file)
{
jobjectArray returnValue = NULL;
@@ -203,7 +314,8 @@ - (NSArray *)URLs {
dialogTitle = @" ";
}
- CFileDialog *dialogDelegate = [[CFileDialog alloc] initWithFilter:hasFilter
+ CFileDialog *dialogDelegate = [[CFileDialog alloc] initWithOwner:(NSWindow *)jlong_to_ptr(ownerPtr)
+ filter:hasFilter
fileDialog:peer
title:dialogTitle
directory:JavaStringToNSString(env, directory)
@@ -212,6 +324,8 @@ - (NSArray *)URLs {
multipleMode:multipleMode
shouldNavigate:navigateApps
canChooseDirectories:chooseDirectories
+ canChooseFiles:chooseFiles
+ canCreateDirectories:createDirectories
withEnv:env];
[ThreadUtilities performOnMainThread:@selector(safeSaveOrLoad)
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/CGraphicsDevice.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/CGraphicsDevice.m
index 468c5f6dae09..08feb79ce444 100644
--- a/src/java.desktop/macosx/native/libawt_lwawt/awt/CGraphicsDevice.m
+++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/CGraphicsDevice.m
@@ -169,9 +169,11 @@ static jobject createJavaDisplayMode(CGDisplayModeRef mode, JNIEnv *env) {
w = CGDisplayModeGetWidth(mode);
CFRelease(currentBPP);
}
+ uint32_t flags = CGDisplayModeGetIOFlags(mode);
+ BOOL isDisplayModeDefault = (flags & kDisplayModeDefaultFlag) ? YES : NO;
DECLARE_CLASS_RETURN(jc_DisplayMode, "java/awt/DisplayMode", ret);
- DECLARE_METHOD_RETURN(jc_DisplayMode_ctor, jc_DisplayMode, "", "(IIII)V", ret);
- ret = (*env)->NewObject(env, jc_DisplayMode, jc_DisplayMode_ctor, w, h, bpp, refrate);
+ DECLARE_METHOD_RETURN(jc_DisplayMode_ctor, jc_DisplayMode, "", "(IIIIZ)V", ret);
+ ret = (*env)->NewObject(env, jc_DisplayMode, jc_DisplayMode_ctor, w, h, bpp, refrate, (jboolean)isDisplayModeDefault);
CHECK_EXCEPTION();
JNI_COCOA_EXIT(env);
return ret;
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/CGraphicsEnv.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/CGraphicsEnv.m
index 58338cbc6c13..ada7deb53796 100644
--- a/src/java.desktop/macosx/native/libawt_lwawt/awt/CGraphicsEnv.m
+++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/CGraphicsEnv.m
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -23,6 +23,9 @@
* questions.
*/
+#import
+#import
+#import "sun_awt_CGraphicsEnvironment.h"
#import "AWT_debug.h"
#import "JNIUtilities.h"
@@ -179,3 +182,36 @@
JNI_COCOA_EXIT(env);
}
+
+JNIEXPORT jint JNICALL Java_sun_awt_CGraphicsEnvironment_initMetal
+ (JNIEnv *env, jclass mtlgc, jstring shadersLibName)
+{
+ __block jint ret = sun_awt_CGraphicsEnvironment_MTL_ERROR;
+
+ JNI_COCOA_ENTER(env);
+
+ __block NSString* path = NormalizedPathNSStringFromJavaString(env, shadersLibName);
+
+ [ThreadUtilities performOnMainThreadWaiting:YES block:^() {
+
+ id device = MTLCreateSystemDefaultDevice();
+ if (device != nil) {
+ NSError* error = nil;
+ id lib = [device newLibraryWithFile:path error:&error];
+ if (lib != nil) {
+ ret = sun_awt_CGraphicsEnvironment_MTL_SUPPORTED;
+ } else {
+ J2dRlsTraceLn(J2D_TRACE_ERROR, "CGraphicsEnvironment_initMetal - "
+ "Failed to load Metal shader library.");
+ ret = sun_awt_CGraphicsEnvironment_MTL_NO_SHADER_LIB;
+ }
+ } else {
+ J2dRlsTraceLn(J2D_TRACE_ERROR, "CGraphicsEnvironment_initMetal - "
+ "Failed to create MTLDevice.");
+ ret = sun_awt_CGraphicsEnvironment_MTL_NO_DEVICE;
+ }
+ }];
+
+ JNI_COCOA_EXIT(env);
+ return ret;
+}
\ No newline at end of file
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/CInputMethod.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/CInputMethod.m
index 3bfd72668c37..4e4f6840fb98 100644
--- a/src/java.desktop/macosx/native/libawt_lwawt/awt/CInputMethod.m
+++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/CInputMethod.m
@@ -118,13 +118,6 @@ + (void) _nativeNotifyPeerWithView:(AWTView *)view inputMethod:(jobject) inputMe
[view setInputMethod:inputMethod]; // inputMethod is a GlobalRef or null to disable.
}
-+ (void) _nativeEndComposition:(AWTView *)view {
- if (view == nil) return;
-
- [view abandonInput];
-}
-
-
@end
/*
@@ -186,16 +179,21 @@ + (void) _nativeEndComposition:(AWTView *)view {
/*
* Class: sun_lwawt_macosx_CInputMethod
* Method: nativeEndComposition
- * Signature: (J)V
+ * Signature: (JLjava/awt/Component;)V
*/
JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CInputMethod_nativeEndComposition
-(JNIEnv *env, jobject this, jlong nativePeer)
+(JNIEnv *env, jobject this, jlong nativePeer, jobject component)
{
JNI_COCOA_ENTER(env);
- AWTView *view = (AWTView *)jlong_to_ptr(nativePeer);
-
- [ThreadUtilities performOnMainThreadWaiting:NO block:^(){
- [CInputMethod _nativeEndComposition:view];
+ AWTView *view = (AWTView *)jlong_to_ptr(nativePeer);
+ if (!view) return;
+ jobject componentRef = (*env)->NewGlobalRef(env, component);
+ [ThreadUtilities performOnMainThreadWaiting:NO block:^(){
+ [view abandonInput:componentRef];
+ if (componentRef) {
+ JNIEnv *env = [ThreadUtilities getJNIEnv];
+ (*env)->DeleteGlobalRef(env, componentRef);
+ }
}];
JNI_COCOA_EXIT(env);
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/CRobot.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/CRobot.m
index 147af2b7a7f0..ced9bf47e6de 100644
--- a/src/java.desktop/macosx/native/libawt_lwawt/awt/CRobot.m
+++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/CRobot.m
@@ -26,6 +26,7 @@
#import "JNIUtilities.h"
#import
+#import
#import "CRobotKeyCode.h"
#import "LWCToolkit.h"
@@ -63,6 +64,10 @@
static int gsEventNumber;
static int* gsButtonEventNumber;
static NSTimeInterval gNextKeyEventTime;
+static NSTimeInterval safeDelay;
+
+#define KEY_CODE_COUNT 128
+static CGEventFlags keyOwnFlags[KEY_CODE_COUNT];
static inline CGKeyCode GetCGKeyCode(jint javaKeyCode);
@@ -99,17 +104,31 @@ static inline void autoDelay(BOOL isMove) {
[NSThread sleepForTimeInterval:delay];
}
}
- gNextKeyEventTime = [[NSDate date] timeIntervalSinceReferenceDate] + 0.050;
+ gNextKeyEventTime = [[NSDate date] timeIntervalSinceReferenceDate] + safeDelay;
+}
+
+static void initKeyFlags() {
+ CGEventSourceRef source = CGEventSourceCreate(kCGEventSourceStatePrivate);
+ for (CGKeyCode keyCode = 0; keyCode < KEY_CODE_COUNT; keyCode++) {
+ CGEventRef event = CGEventCreateKeyboardEvent(source, keyCode, true);
+ if (event != NULL) {
+ keyOwnFlags[keyCode] = CGEventGetFlags(event);
+ CFRelease(event);
+ }
+ }
+ if (source != NULL) {
+ CFRelease(source);
+ }
}
/*
* Class: sun_lwawt_macosx_CRobot
* Method: initRobot
- * Signature: (V)V
+ * Signature: (I)V
*/
JNIEXPORT void JNICALL
Java_sun_lwawt_macosx_CRobot_initRobot
-(JNIEnv *env, jobject peer)
+(JNIEnv *env, jobject peer, jint safeDelayMillis)
{
// Set things up to let our app act like a synthetic keyboard and mouse.
// Always set all states, in case Apple ever changes default behaviors.
@@ -138,6 +157,7 @@ static inline void autoDelay(BOOL isMove) {
gsClickCount = 0;
gsLastClickTime = 0;
gNextKeyEventTime = 0;
+ safeDelay = (NSTimeInterval)safeDelayMillis/1000;
gsEventNumber = ROBOT_EVENT_NUMBER_START;
gsButtonEventNumber = (int*)SAFE_SIZE_ARRAY_ALLOC(malloc, sizeof(int), gNumberOfButtons);
@@ -149,6 +169,8 @@ static inline void autoDelay(BOOL isMove) {
for (i = 0; i < gNumberOfButtons; ++i) {
gsButtonEventNumber[i] = ROBOT_EVENT_NUMBER_START;
}
+
+ initKeyFlags();
}];
}
}
@@ -277,6 +299,20 @@ static inline void autoDelay(BOOL isMove) {
}];
}
+// CGEventCreateKeyboardEvent incorrectly handles flags pertinent to non-modifier keys
+// (e.g. F1-F12 keys always Fn flag set, while arrow keys always have Fn and NumPad flags set).
+// Those flags are not cleared for following key presses automatically, so we need to do it ourselves.
+// See JBR-4306 for details.
+static void clearStickyFlags(CGEventRef event, CGKeyCode keyCode, CGEventFlags flagToCheck) {
+ if (keyCode < KEY_CODE_COUNT && (keyOwnFlags[keyCode] & flagToCheck) == 0) {
+ CGEventFlags flags = CGEventGetFlags(event);
+ CGEventFlags updatedFlags = flags & ~flagToCheck;
+ if (updatedFlags != flags) {
+ CGEventSetFlags(event, updatedFlags);
+ }
+ }
+}
+
/*
* Class: sun_lwawt_macosx_CRobot
* Method: keyEvent
@@ -289,14 +325,39 @@ static inline void autoDelay(BOOL isMove) {
autoDelay(NO);
[ThreadUtilities performOnMainThreadWaiting:YES block:^(){
CGEventSourceRef source = CGEventSourceCreate(kCGEventSourceStateHIDSystemState);
- CGKeyCode keyCode = GetCGKeyCode(javaKeyCode);
+ CGKeyCode keyCode;
+
+ if (javaKeyCode & 0x2000000) {
+ // This is a dirty, dirty hack and is only used in tests.
+ // This allows us to send macOS virtual key code directly, without first looking up a Java key code.
+ // It also allows the caller to set the physical keyboard layout directly.
+ // The key code that should be passed to Robot in this case is the following:
+ // 0x2000000 | keyCode | (physicalLayout << 8)
+ // where physicalLayout is:
+ // 0 - ANSI
+ // 1 - ISO
+ // 2 - JIS
+
+ UInt32 physicalLayout = (javaKeyCode >> 8) & 0xff;
+ UInt32 keyboardType;
+ if (physicalLayout == 1) {
+ keyboardType = 41; // ISO
+ } else if (physicalLayout == 2) {
+ keyboardType = 42; // JIS
+ } else {
+ keyboardType = 40; // ANSI
+ }
+ CGEventSourceSetKeyboardType(source, keyboardType);
+ keyCode = javaKeyCode & 0xff;
+ } else {
+ keyCode = GetCGKeyCode(javaKeyCode);
+ }
CGEventRef event = CGEventCreateKeyboardEvent(source, keyCode, keyPressed);
if (event != NULL) {
- CGEventFlags flags = CGEventSourceFlagsState(kCGEventSourceStateHIDSystemState);
- if ((flags & kCGEventFlagMaskSecondaryFn) != 0) {
- flags ^= kCGEventFlagMaskSecondaryFn;
- CGEventSetFlags(event, flags);
- }
+ // this assumes Robot isn't used to generate Fn key presses
+ clearStickyFlags(event, keyCode, kCGEventFlagMaskSecondaryFn);
+ // there is no NumPad key, so this won't hurt in any case
+ clearStickyFlags(event, keyCode, kCGEventFlagMaskNumericPad);
CGEventPost(kCGHIDEventTap, event);
CFRelease(event);
}
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/CRobotKeyCode.h b/src/java.desktop/macosx/native/libawt_lwawt/awt/CRobotKeyCode.h
index d06b9e8ee461..a93704c7b9ec 100644
--- a/src/java.desktop/macosx/native/libawt_lwawt/awt/CRobotKeyCode.h
+++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/CRobotKeyCode.h
@@ -99,6 +99,7 @@ const static int OSX_kVK_Tab = 0x30;
const static int OSX_kVK_Space = 0x31;
const static int OSX_Delete = 0x33;
const static int OSX_Escape = 0x35;
+const static int OSX_RightCommand = 0x36;
const static int OSX_Command = 0x37;
const static int OSX_Shift = 0x38;
const static int OSX_CapsLock = 0x39;
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/CRobotKeyCode.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/CRobotKeyCode.m
index e8caf8ca5156..4510c9965bcf 100644
--- a/src/java.desktop/macosx/native/libawt_lwawt/awt/CRobotKeyCode.m
+++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/CRobotKeyCode.m
@@ -160,6 +160,11 @@ -(id) init {
[NSNumber numberWithInt : OSX_F19], [NSNumber numberWithInt : java_awt_event_KeyEvent_VK_F19],
[NSNumber numberWithInt : OSX_F20], [NSNumber numberWithInt : java_awt_event_KeyEvent_VK_F20],
+ // There's no VK_ key code for the section key (\u00a7), found on the Standard QWERTY (ABC) ISO layout.
+ // For consistency, let's use the code returned by getExtendedKeyCodeForChar for this symbol.
+ // Also see src/java.desktop/share/classes/sun/awt/ExtendedKeyCodes.java
+ [NSNumber numberWithInt : OSX_kVK_ISO_Section], [NSNumber numberWithInt : 0x1000000 + 0x00A7],
+
nil];
}
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/CWrapper.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/CWrapper.m
index 3244438bc1bc..f20f3225d34b 100644
--- a/src/java.desktop/macosx/native/libawt_lwawt/awt/CWrapper.m
+++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/CWrapper.m
@@ -30,12 +30,12 @@
/*
* Class: sun_lwawt_macosx_CWrapper$NSWindow
- * Method: makeKeyAndOrderFront
- * Signature: (J)V
+ * Method: nativeMakeKeyAndOrderFront
+ * Signature: (JZ)V
*/
JNIEXPORT void JNICALL
-Java_sun_lwawt_macosx_CWrapper_00024NSWindow_makeKeyAndOrderFront
-(JNIEnv *env, jclass cls, jlong windowPtr)
+Java_sun_lwawt_macosx_CWrapper_00024NSWindow_nativeMakeKeyAndOrderFront
+(JNIEnv *env, jclass cls, jlong windowPtr, jboolean wait)
{
JNI_COCOA_ENTER(env);
@@ -43,19 +43,19 @@
[ThreadUtilities performOnMainThread:@selector(makeKeyAndOrderFront:)
on:window
withObject:nil
- waitUntilDone:NO];
+ waitUntilDone:(BOOL)wait];
JNI_COCOA_EXIT(env);
}
/*
* Class: sun_lwawt_macosx_CWrapper$NSWindow
- * Method: makeKeyWindow
- * Signature: (J)V
+ * Method: nativeMakeKeyWindow
+ * Signature: (JZ)V
*/
JNIEXPORT void JNICALL
-Java_sun_lwawt_macosx_CWrapper_00024NSWindow_makeKeyWindow
-(JNIEnv *env, jclass cls, jlong windowPtr)
+Java_sun_lwawt_macosx_CWrapper_00024NSWindow_nativeMakeKeyWindow
+(JNIEnv *env, jclass cls, jlong windowPtr, jboolean wait)
{
JNI_COCOA_ENTER(env);
@@ -63,7 +63,7 @@
[ThreadUtilities performOnMainThread:@selector(makeKeyWindow)
on:window
withObject:nil
- waitUntilDone:NO];
+ waitUntilDone:(BOOL)wait];
JNI_COCOA_EXIT(env);
}
@@ -156,20 +156,39 @@
/*
* Class: sun_lwawt_macosx_CWrapper$NSWindow
- * Method: orderOut
+ * Method: orderFrontIfOnActiveSpace
* Signature: (J)V
*/
JNIEXPORT void JNICALL
-Java_sun_lwawt_macosx_CWrapper_00024NSWindow_orderOut
+Java_sun_lwawt_macosx_CWrapper_00024NSWindow_orderFrontIfOnActiveSpace
(JNIEnv *env, jclass cls, jlong windowPtr)
{
+JNI_COCOA_ENTER(env);
+
+ NSWindow *window = (NSWindow *)jlong_to_ptr(windowPtr);
+ [ThreadUtilities performOnMainThreadWaiting:NO block:^(){
+ if (window.onActiveSpace) [window orderFront:window];
+ }];
+
+JNI_COCOA_EXIT(env);
+}
+
+/*
+ * Class: sun_lwawt_macosx_CWrapper$NSWindow
+ * Method: nativeOrderOut
+ * Signature: (JZ)V
+ */
+JNIEXPORT void JNICALL
+Java_sun_lwawt_macosx_CWrapper_00024NSWindow_nativeOrderOut
+(JNIEnv *env, jclass cls, jlong windowPtr, jboolean wait)
+{
JNI_COCOA_ENTER(env);
NSWindow *window = (NSWindow *)jlong_to_ptr(windowPtr);
[ThreadUtilities performOnMainThread:@selector(orderOut:)
on:window
withObject:window
- waitUntilDone:NO];
+ waitUntilDone:(BOOL)wait];
JNI_COCOA_EXIT(env);
}
@@ -231,6 +250,26 @@
JNI_COCOA_EXIT(env);
}
+/*
+ * Class: sun_lwawt_macosx_CWrapper$NSWindow
+ * Method: orderWindowIfOnActiveSpace
+ * Signature: (JIJ)V
+ */
+JNIEXPORT void JNICALL
+Java_sun_lwawt_macosx_CWrapper_00024NSWindow_orderWindowIfOnActiveSpace
+(JNIEnv *env, jclass cls, jlong windowPtr, jint order, jlong relativeToPtr)
+{
+JNI_COCOA_ENTER(env);
+
+ NSWindow *window = (NSWindow *)jlong_to_ptr(windowPtr);
+ NSWindow *relativeTo = (NSWindow *)jlong_to_ptr(relativeToPtr);
+ [ThreadUtilities performOnMainThreadWaiting:NO block:^(){
+ if (window.onActiveSpace) [window orderWindow:(NSWindowOrderingMode)order relativeTo:[relativeTo windowNumber]];
+ }];
+
+JNI_COCOA_EXIT(env);
+}
+
// Used for CWrapper.NSWindow.setLevel() (and level() which isn't implemented yet)
static NSInteger LEVELS[sun_lwawt_macosx_CWrapper_NSWindow_MAX_WINDOW_LEVELS];
static void initLevels()
@@ -477,6 +516,31 @@ static void initLevels()
JNI_COCOA_EXIT(env);
}
+/*
+ * Class: sun_lwawt_macosx_CWrapper$NSWindow
+ * Method: isTabbedWindow
+ * Signature: (J)Z
+ */
+JNIEXPORT jboolean JNICALL
+Java_sun_lwawt_macosx_CWrapper_00024NSWindow_isTabbedWindow
+(JNIEnv *env, jclass cls, jlong windowPtr)
+{
+ __block jboolean isTabbedWindow = JNI_FALSE;
+
+JNI_COCOA_ENTER(env);
+
+ if (@available(macOS 10.13, *)) {
+ NSWindow *window = (NSWindow *)jlong_to_ptr(windowPtr);
+ [ThreadUtilities performOnMainThreadWaiting:YES block:^(){
+ isTabbedWindow = [[[window tabGroup] windows] count] > 1;
+ }];
+ }
+
+JNI_COCOA_EXIT(env);
+
+ return isTabbedWindow;
+}
+
/*
* Class: sun_lwawt_macosx_CWrapper$NSView
* Method: addSubview
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/Dispatch.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/Dispatch.m
new file mode 100644
index 000000000000..ee098fca5877
--- /dev/null
+++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/Dispatch.m
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * Must include this before JavaNativeFoundation.h to get jni.h from build
+ */
+#include "jni.h"
+#include "jni_util.h"
+
+#import "sun_lwawt_macosx_concurrent_LibDispatchNative.h"
+
+#import
+#import
+#import "JNIUtilities.h"
+
+/*
+ * Declare library specific JNI_Onload entry if static build
+ */
+DEF_STATIC_JNI_OnLoad
+
+/*
+ * Class: sun_lwawt_macosx_concurrent_LibDispatchNative
+ * Method: nativeIsDispatchSupported
+ * Signature: ()Z
+ */
+JNIEXPORT jboolean JNICALL Java_sun_lwawt_macosx_concurrent_LibDispatchNative_nativeIsDispatchSupported
+(JNIEnv *env, jclass clazz)
+{
+ return JNI_TRUE;
+}
+
+
+/*
+ * Class: sun_lwawt_macosx_concurrent_LibDispatchNative
+ * Method: nativeGetMainQueue
+ * Signature: ()J
+ */
+JNIEXPORT jlong JNICALL Java_sun_lwawt_macosx_concurrent_LibDispatchNative_nativeGetMainQueue
+(JNIEnv *env, jclass clazz)
+{
+ dispatch_queue_t queue = dispatch_get_main_queue();
+ return ptr_to_jlong(queue);
+}
+
+
+/*
+ * Class: sun_lwawt_macosx_concurrent_LibDispatchNative
+ * Method: nativeCreateConcurrentQueue
+ * Signature: (I)J
+ */
+JNIEXPORT jlong JNICALL Java_sun_lwawt_macosx_concurrent_LibDispatchNative_nativeCreateConcurrentQueue
+(JNIEnv *env, jclass clazz, jint priority)
+{
+ dispatch_queue_t queue = dispatch_get_global_queue((long)priority, 0);
+ return ptr_to_jlong(queue);
+}
+
+
+/*
+ * Class: sun_lwawt_macosx_concurrent_LibDispatchNative
+ * Method: nativeCreateSerialQueue
+ * Signature: (Ljava/lang/String;)J
+ */
+JNIEXPORT jlong JNICALL Java_sun_lwawt_macosx_concurrent_LibDispatchNative_nativeCreateSerialQueue
+(JNIEnv *env, jclass clazz, jstring name)
+{
+ if (name == NULL) return 0L;
+
+ jboolean isCopy;
+ const char *queue_name = (*env)->GetStringUTFChars(env, name, &isCopy);
+ dispatch_queue_t queue = dispatch_queue_create(queue_name, NULL);
+ (*env)->ReleaseStringUTFChars(env, name, queue_name);
+
+ return ptr_to_jlong(queue);
+}
+
+
+/*
+ * Class: sun_lwawt_macosx_concurrent_LibDispatchNative
+ * Method: nativeReleaseQueue
+ * Signature: (J)V
+ */
+JNIEXPORT void JNICALL Java_sun_lwawt_macosx_concurrent_LibDispatchNative_nativeReleaseQueue
+(JNIEnv *env, jclass clazz, jlong nativeQueue)
+{
+ if (nativeQueue == 0L) return;
+ dispatch_release((dispatch_queue_t)jlong_to_ptr(nativeQueue));
+}
+
+
+
+static void perform_dispatch(JNIEnv *env, jlong nativeQueue, jobject runnable, void (*dispatch_fxn)(dispatch_queue_t, dispatch_block_t))
+{
+JNI_COCOA_ENTER(env);
+ DECLARE_CLASS(jc_Runnable, "java/lang/Runnable");
+ DECLARE_METHOD(jm_run, jc_Runnable, "run", "()V");
+
+ dispatch_queue_t queue = (dispatch_queue_t)jlong_to_ptr(nativeQueue);
+ if (queue == NULL) return; // shouldn't happen
+
+ // create a global-ref around the Runnable, so it can be safely passed to the dispatch thread
+ jobject runnableRef = (*env)->NewGlobalRef(env, runnable);
+ dispatch_fxn(queue, ^{
+ // attach the dispatch thread to the JVM if necessary, and get an env
+ JNIEnv* blockEnv = [ThreadUtilities getJNIEnvUncached];
+
+ JNI_COCOA_ENTER(blockEnv);
+ // call the user's runnable
+ (*blockEnv)->CallVoidMethod(blockEnv, runnableRef, jm_run);
+ CHECK_EXCEPTION_IN_ENV(blockEnv);
+ (*blockEnv)->DeleteGlobalRef(blockEnv, runnableRef);
+ JNI_COCOA_EXIT(blockEnv);
+ });
+
+JNI_COCOA_EXIT(env);
+}
+
+
+/*
+ * Class: sun_lwawt_macosx_concurrent_LibDispatchNative
+ * Method: nativeExecuteAsync
+ * Signature: (JLjava/lang/Runnable;)V
+ */
+JNIEXPORT void JNICALL Java_sun_lwawt_macosx_concurrent_LibDispatchNative_nativeExecuteAsync
+(JNIEnv *env, jclass clazz, jlong nativeQueue, jobject runnable)
+{
+ // enqueues and returns
+ perform_dispatch(env, nativeQueue, runnable, dispatch_async);
+}
+
+
+/*
+ * Class: sun_lwawt_macosx_concurrent_LibDispatchNative
+ * Method: nativeExecuteSync
+ * Signature: (JLjava/lang/Runnable;)V
+ */
+JNIEXPORT void JNICALL Java_sun_lwawt_macosx_concurrent_LibDispatchNative_nativeExecuteSync
+(JNIEnv *env, jclass clazz, jlong nativeQueue, jobject runnable)
+{
+ // blocks until the Runnable completes
+ perform_dispatch(env, nativeQueue, runnable, dispatch_sync);
+}
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/GeomUtilities.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/GeomUtilities.m
index 13ad3d7db6e4..3bdca591edbf 100644
--- a/src/java.desktop/macosx/native/libawt_lwawt/awt/GeomUtilities.m
+++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/GeomUtilities.m
@@ -97,7 +97,7 @@ NSSize JavaToNSSize(JNIEnv *env, jobject dimension) {
}
static NSScreen *primaryScreen(JNIEnv *env) {
- NSScreen *primaryScreen = [[NSScreen screens] objectAtIndex:0];
+ NSScreen *primaryScreen = [[NSScreen screens] firstObject];
if (primaryScreen != nil) return primaryScreen;
if ((env != NULL) && ([NSThread isMainThread] == NO)) {
JNU_ThrowByName(env, "java/lang/RuntimeException", "Failed to convert, no screen.");
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/InitIDs.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/InitIDs.m
index 289e63ba98c8..353fdedbd0e9 100644
--- a/src/java.desktop/macosx/native/libawt_lwawt/awt/InitIDs.m
+++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/InitIDs.m
@@ -24,6 +24,8 @@
*/
#import
+#import
+#import
#import "InitIDs.h"
@@ -161,3 +163,30 @@
(JNIEnv *env, jclass cls)
{
}
+
+JNIEXPORT jarray JNICALL Java_sun_font_FontManagerNativeLibrary_loadedLibraries
+ (JNIEnv *env, jclass cls)
+{
+ const uint32_t count = _dyld_image_count();
+
+ jclass stringClazz = (*env)->FindClass(env, "java/lang/String");
+ CHECK_NULL_RETURN(stringClazz, NULL);
+ jarray libsArray = (*env)->NewObjectArray(env, count, stringClazz, NULL);
+ JNU_CHECK_EXCEPTION_RETURN(env, NULL);
+
+ if ((*env)->EnsureLocalCapacity(env, count + 2) != JNI_OK) {
+ return NULL; // OOME has been thrown already
+ }
+
+ for (uint32_t i = 0; i < count; i++) {
+ const char * name = _dyld_get_image_name(i);
+ if (name) {
+ jstring jName = (*env)->NewStringUTF(env, name);
+ JNU_CHECK_EXCEPTION_RETURN(env, NULL);
+ (*env)->SetObjectArrayElement(env, libsArray, i, jName);
+ JNU_CHECK_EXCEPTION_RETURN(env, NULL);
+ }
+ }
+
+ return libsArray;
+}
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityUtilities.h b/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityUtilities.h
index b829335fc299..d0b17940bf60 100644
--- a/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityUtilities.h
+++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityUtilities.h
@@ -28,7 +28,9 @@
extern NSString *const JavaAccessibilityIgnore;
extern NSMutableDictionary *sRoles;
+extern NSMutableDictionary *sAnnouncePriorities;
extern void initializeRoles();
+extern void initializeAnnouncePriorities();
#define GET_CACCESSIBILITY_CLASS() \
GET_CLASS(sjc_CAccessibility, "sun/lwawt/macosx/CAccessibility");
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityUtilities.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityUtilities.m
index bf66df162d13..8094f7faa633 100644
--- a/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityUtilities.m
+++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaAccessibilityUtilities.m
@@ -24,6 +24,7 @@
*/
#import "JavaAccessibilityUtilities.h"
+#import "sun_swing_AccessibleAnnouncer.h"
#import "JNIUtilities.h"
#import
@@ -40,7 +41,9 @@
NSString *const JavaAccessibilityIgnore = @"JavaAxIgnore";
NSMutableDictionary *sRoles = nil;
+NSMutableDictionary *sAnnouncePriorities = nil;
void initializeRoles();
+void initializeAnnouncePriorities();
// Unique
static jclass sjc_AccessibleState = NULL;
@@ -478,7 +481,7 @@ void initializeRoles()
[sRoles setObject:JavaAccessibilityIgnore forKey:@"frame"];
[sRoles setObject:JavaAccessibilityIgnore forKey:@"glasspane"];
[sRoles setObject:NSAccessibilityGroupRole forKey:@"groupbox"];
- [sRoles setObject:NSAccessibilityStaticTextRole forKey:@"hyperlink"]; //maybe a group?
+ [sRoles setObject:NSAccessibilityLinkRole forKey:@"hyperlink"];
[sRoles setObject:NSAccessibilityImageRole forKey:@"icon"];
[sRoles setObject:NSAccessibilityGroupRole forKey:@"internalframe"];
[sRoles setObject:NSAccessibilityStaticTextRole forKey:@"label"];
@@ -518,3 +521,10 @@ void initializeRoles()
[sRoles setObject:JavaAccessibilityIgnore forKey:@"viewport"];
[sRoles setObject:JavaAccessibilityIgnore forKey:@"window"];
}
+
+void initializeAnnouncePriorities() {
+ sAnnouncePriorities = [[NSMutableDictionary alloc] initWithCapacity:3];
+
+ [sAnnouncePriorities setObject:[NSNumber numberWithInt:NSAccessibilityPriorityMedium] forKey:[NSNumber numberWithInt:sun_swing_AccessibleAnnouncer_ANNOUNCE_WITHOUT_INTERRUPTING_CURRENT_OUTPUT]];
+ [sAnnouncePriorities setObject:[NSNumber numberWithInt:NSAccessibilityPriorityHigh] forKey:[NSNumber numberWithInt:sun_swing_AccessibleAnnouncer_ANNOUNCE_WITH_INTERRUPTING_CURRENT_OUTPUT]];
+}
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaComponentAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaComponentAccessibility.m
index da83387a58da..343d2ead514d 100644
--- a/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaComponentAccessibility.m
+++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/JavaComponentAccessibility.m
@@ -1008,7 +1008,7 @@ - (NSValue *)accessibilityPositionAttribute
point.y += size.height;
// Now make it into Cocoa screen coords.
- point.y = [[[[self view] window] screen] frame].size.height - point.y;
+ point.y = [[NSScreen screens][0] frame].size.height - point.y;
return [NSValue valueWithPoint:point];
}
@@ -1386,7 +1386,7 @@ - (id)accessibilityHitTest:(NSPoint)point withEnv:(JNIEnv *)env
"(Ljava/awt/Container;FF)Ljavax/accessibility/Accessible;", nil);
// Make it into java screen coords
- point.y = [[[[self view] window] screen] frame].size.height - point.y;
+ point.y = [[NSScreen screens][0] frame].size.height - point.y;
jobject jparent = fComponent;
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/LWCToolkit.h b/src/java.desktop/macosx/native/libawt_lwawt/awt/LWCToolkit.h
index 313b49e96512..06a9067b4283 100644
--- a/src/java.desktop/macosx/native/libawt_lwawt/awt/LWCToolkit.h
+++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/LWCToolkit.h
@@ -38,6 +38,10 @@ extern int gNumberOfButtons;
// InputEvent mask array
extern jint* gButtonDownMasks;
+extern int lcdSubPixelPosSupported;
+
+extern int useFontSmoothing;
+
@interface AWTToolkit : NSObject { }
+ (BOOL) inDoDragDropLoop;
+ (void) setInDoDragDropLoop:(BOOL)val;
@@ -53,3 +57,22 @@ extern jint* gButtonDownMasks;
/** Macro to cast a jlong to an Objective-C object (id). Casts to long on 32-bit systems to quiesce the compiler. */
#define OBJC(jl) ((id)jlong_to_ptr(jl))
+
+#ifndef kCFCoreFoundationVersionNumber10_13_Max
+#define kCFCoreFoundationVersionNumber10_13_Max 1499
+#endif
+
+#ifndef kCFCoreFoundationVersionNumber10_14_Max
+#define kCFCoreFoundationVersionNumber10_14_Max 1599
+#endif
+
+#ifndef IS_OSX_GT10_13
+#define IS_OSX_GT10_13 (floor(kCFCoreFoundationVersionNumber) > \
+ kCFCoreFoundationVersionNumber10_13_Max)
+#endif
+
+#ifndef IS_OSX_GT10_14
+#define IS_OSX_GT10_14 (floor(kCFCoreFoundationVersionNumber) > \
+kCFCoreFoundationVersionNumber10_14_Max)
+#endif
+
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/LWCToolkit.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/LWCToolkit.m
index cfd7460e275f..6b8a47024e4a 100644
--- a/src/java.desktop/macosx/native/libawt_lwawt/awt/LWCToolkit.m
+++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/LWCToolkit.m
@@ -40,8 +40,13 @@
#import "sun_lwawt_macosx_LWCToolkit.h"
#import "sizecalc.h"
+#import "AWTWindow.h"
#import
+#import
+
+#include
+#include
// SCROLL PHASE STATE
#define SCROLL_PHASE_UNSUPPORTED 1
@@ -52,6 +57,8 @@
int gNumberOfButtons;
jint* gButtonDownMasks;
+int lcdSubPixelPosSupported;
+int useFontSmoothing;
// Indicates that the app has been started with -XstartOnFirstThread
// (directly or via WebStart settings), and AWT should not run its
@@ -573,23 +580,34 @@ BOOL doLoadNativeColors(JNIEnv *env, jintArray jColors, BOOL useAppleColors) {
/*
* Class: sun_lwawt_macosx_LWCToolkit
* Method: doAWTRunLoopImpl
- * Signature: (JZZ)V
+ * Signature: (JZZI)Z
*/
-JNIEXPORT void JNICALL Java_sun_lwawt_macosx_LWCToolkit_doAWTRunLoopImpl
-(JNIEnv *env, jclass clz, jlong mediator, jboolean processEvents, jboolean inAWT)
+JNIEXPORT jboolean JNICALL Java_sun_lwawt_macosx_LWCToolkit_doAWTRunLoopImpl
+(JNIEnv *env, jclass clz, jlong mediator, jboolean processEvents, jboolean inAWT, jint timeoutSeconds/*(-1) for infinite*/)
{
-AWT_ASSERT_APPKIT_THREAD;
+ AWT_ASSERT_APPKIT_THREAD;
+ jboolean result = JNI_TRUE;
JNI_COCOA_ENTER(env);
AWTRunLoopObject* mediatorObject = (AWTRunLoopObject*)jlong_to_ptr(mediator);
- if (mediatorObject == nil) return;
+ if (mediatorObject == nil) return JNI_TRUE;
+
+ NSDate *date = timeoutSeconds > 0 ? [NSDate dateWithTimeIntervalSinceNow:timeoutSeconds] : nil;
// Don't use acceptInputForMode because that doesn't setup autorelease pools properly
BOOL isRunning = true;
while (![mediatorObject shouldEndRunLoop] && isRunning) {
isRunning = [[NSRunLoop currentRunLoop] runMode:(inAWT ? [ThreadUtilities javaRunLoopMode] : NSDefaultRunLoopMode)
beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.010]];
+
+ if (date != nil) {
+ NSDate *now = [[NSDate alloc] init];
+ if ([date compare:(now)] == NSOrderedAscending) result = JNI_FALSE;
+ [now release];
+ if (result == JNI_FALSE) break;
+ }
+
if (processEvents) {
//We do not spin a runloop here as date is nil, so does not matter which mode to use
// Processing all events excluding NSApplicationDefined which need to be processed
@@ -599,13 +617,30 @@ BOOL doLoadNativeColors(JNIEnv *env, jintArray jColors, BOOL useAppleColors) {
untilDate:nil
inMode:NSDefaultRunLoopMode
dequeue:YES]) != nil) {
- [NSApp sendEvent:event];
+ if ([event.window isKindOfClass:[AWTWindow_Normal class]]) {
+ // Filter only events from AWTWindow (to skip events from ScreenMenu)
+ // See https://youtrack.jetbrains.com/issue/IDEA-305287/Implement-non-blocking-ScreenMenu.invokeOpenLater#focus=Comments-27-6614719.0-0
+ [NSApp sendEvent:event];
+ }
}
}
}
[mediatorObject release];
JNI_COCOA_EXIT(env);
+
+ return result;
+}
+
+/*
+ * Class: sun_lwawt_macosx_LWCToolkit
+ * Method: isBlockingEventDispatchThread
+ * Signature: ()Z
+ */
+JNIEXPORT jboolean JNICALL Java_sun_lwawt_macosx_LWCToolkit_isBlockingEventDispatchThread
+ (JNIEnv *env, jclass clz)
+{
+ return ThreadUtilities.blockingEventDispatchThread;
}
/*
@@ -620,6 +655,8 @@ BOOL doLoadNativeColors(JNIEnv *env, jintArray jColors, BOOL useAppleColors) {
AWTRunLoopObject* mediatorObject = (AWTRunLoopObject*)jlong_to_ptr(mediator);
+ if (mediatorObject == nil) return;
+
[ThreadUtilities performOnMainThread:@selector(endRunLoop) on:mediatorObject withObject:nil waitUntilDone:NO];
[mediatorObject release];
@@ -645,6 +682,23 @@ BOOL doLoadNativeColors(JNIEnv *env, jintArray jColors, BOOL useAppleColors) {
JNI_COCOA_EXIT(env);
}
+/*
+ * Class: sun_lwawt_macosx_LWCToolkit
+ * Method: performOnMainThreadAndWait
+ * Signature: (Ljava/lang/Runnable)V
+ */
+JNIEXPORT void JNICALL Java_sun_lwawt_macosx_LWCToolkit_performOnMainThreadAndWait
+(JNIEnv *env, jclass clz, jobject runnable)
+{
+JNI_COCOA_ENTER(env);
+ jobject gRunnable = (*env)->NewGlobalRef(env, runnable);
+ CHECK_NULL(gRunnable);
+ [ThreadUtilities performOnMainThreadWaiting:YES block:^() {
+ JavaRunnable* performer = [[JavaRunnable alloc] initWithRunnable:gRunnable];
+ [performer perform];
+ }];
+JNI_COCOA_EXIT(env);
+}
/*
* Class: sun_lwawt_macosx_LWCToolkit
@@ -876,3 +930,157 @@ JNIEXPORT jint JNICALL DEF_JNI_OnLoad(JavaVM *vm, void *reserved) {
JNI_COCOA_EXIT(env);
return multiClickTime;
}
+
+/*
+ * Class: sun_lwawt_macosx_LWCToolkit
+ * Method: getKeyboardLayoutNativeId
+ * Signature: ()Ljava/lang/String;
+ */
+JNIEXPORT jstring JNICALL
+JNICALL Java_sun_lwawt_macosx_LWCToolkit_getKeyboardLayoutNativeId(JNIEnv *env, jclass cls) {
+ jstring jLayoutId = NULL;
+ JNI_COCOA_ENTER(env);
+ __block CFStringRef layoutId = nil;
+ [ThreadUtilities performOnMainThreadWaiting:YES block:^(){
+ TISInputSourceRef source = TISCopyCurrentKeyboardInputSource();
+ layoutId = (CFStringRef)CFRetain(TISGetInputSourceProperty(source, kTISPropertyInputSourceID));
+ CFRelease(source);
+ }];
+ jLayoutId = NSStringToJavaString(env, (NSString*)layoutId);
+ CFRelease(layoutId);
+ JNI_COCOA_EXIT(env);
+ return jLayoutId;
+}
+
+/*
+ * Class: sun_lwawt_macosx_LWCToolkit
+ * Method: switchKeyboardLayoutNative
+ * Signature: (Ljava/lang/String;)V
+ */
+JNIEXPORT jboolean JNICALL
+JNICALL Java_sun_lwawt_macosx_LWCToolkit_switchKeyboardLayoutNative(JNIEnv *env, jclass cls, jstring jLayoutId) {
+ __block bool success = false;
+ JNI_COCOA_ENTER(env);
+ __block NSString* layoutId = JavaStringToNSString(env, jLayoutId);
+ [ThreadUtilities performOnMainThreadWaiting:YES block:^(){
+ NSDictionary* searchParam = @{ (NSString*)kTISPropertyInputSourceID : layoutId };
+ CFArrayRef sources = TISCreateInputSourceList((CFDictionaryRef)searchParam, NO);
+
+ if (CFArrayGetCount(sources) == 0) {
+ NSLog(@"failed to switch to keyboard layout %@: no such layout", layoutId);
+ CFRelease(sources);
+ return;
+ }
+
+ TISInputSourceRef source = (TISInputSourceRef)CFArrayGetValueAtIndex(sources, 0);
+ OSStatus status = TISSelectInputSource(source);
+ if (status == noErr) {
+ success = true;
+ } else {
+ NSLog(@"failed to switch to keyboard layout %@, error code %d", layoutId, status);
+ }
+
+ CFRelease(sources);
+ }];
+ JNI_COCOA_EXIT(env);
+ return success;
+}
+
+/*
+ * Class: sun_lwawt_macosx_LWCToolkit
+ * Method: getKeyboardLayoutListNative
+ * Signature: (Z)[Ljava/lang/String;
+ */
+JNIEXPORT jarray JNICALL
+JNICALL Java_sun_lwawt_macosx_LWCToolkit_getKeyboardLayoutListNative(JNIEnv *env, jclass cls, jboolean includeAll) {
+ jclass stringClazz = (*env)->FindClass(env, "java/lang/String");
+ jarray out = NULL;
+ JNI_COCOA_ENTER(env);
+ __block NSMutableArray* layoutIds = [[[NSMutableArray alloc] init] autorelease];
+ [ThreadUtilities performOnMainThreadWaiting:YES block:^(){
+ CFArrayRef sources = TISCreateInputSourceList(nil, includeAll);
+ int numSources = CFArrayGetCount(sources);
+
+ for (int i = 0; i < numSources; ++i) {
+ TISInputSourceRef layout = CFArrayGetValueAtIndex(sources, i);
+ NSString* sourceId = TISGetInputSourceProperty(layout, kTISPropertyInputSourceID);
+ [layoutIds addObject:sourceId];
+ }
+
+ CFRelease(sources);
+ }];
+
+ int numLayoutIds = (int)[layoutIds count];
+ out = (*env)->NewObjectArray(env, numLayoutIds, stringClazz, NULL);
+ for (int i = 0; i < numLayoutIds; ++i) {
+ NSString* layoutId = [layoutIds objectAtIndex:i];
+ jstring jLayoutId = NSStringToJavaString(env, layoutId);
+ (*env)->SetObjectArrayElement(env, out, i, jLayoutId);
+ }
+ JNI_COCOA_EXIT(env);
+ return out;
+}
+
+/*
+ * Class: sun_lwawt_macosx_LWCToolkit
+ * Method: setKeyboardLayoutEnabledNative
+ * Signature: (Ljava/lang/String;Z)V
+ */
+JNIEXPORT jboolean JNICALL
+JNICALL Java_sun_lwawt_macosx_LWCToolkit_setKeyboardLayoutEnabledNative(JNIEnv *env, jclass cls, jstring jLayoutId, jboolean enabled) {
+ __block bool success = false;
+ JNI_COCOA_ENTER(env);
+ __block NSString* layoutId = JavaStringToNSString(env, jLayoutId);
+ [ThreadUtilities performOnMainThreadWaiting:YES block:^(){
+ NSDictionary* searchParam = @{ (NSString*)kTISPropertyInputSourceID : layoutId };
+ CFArrayRef sources = TISCreateInputSourceList((CFDictionaryRef)searchParam, YES);
+
+ if (CFArrayGetCount(sources) == 0) {
+ NSLog(@"failed to set keyboard layout %@ enabled state: no such layout", layoutId);
+ CFRelease(sources);
+ return;
+ }
+
+ TISInputSourceRef source = (TISInputSourceRef)CFArrayGetValueAtIndex(sources, 0);
+ OSStatus status = enabled ? TISEnableInputSource(source) : TISDisableInputSource(source);
+ if (status == noErr) {
+ success = true;
+ } else {
+ NSLog(@"failed to set keyboard layout %@ enabled state, error code %d", layoutId, status);
+ }
+
+ CFRelease(sources);
+ }];
+ JNI_COCOA_EXIT(env);
+ return success;
+}
+
+/*
+ * Class: sun_lwawt_macosx_LWCToolkit
+ * Method: setCapsLockState
+ * Signature: (Z)Z
+ */
+JNIEXPORT jboolean JNICALL
+JNICALL Java_sun_lwawt_macosx_LWCToolkit_setCapsLockState(JNIEnv *env, jclass cls, jboolean on) {
+ __block kern_return_t err = KERN_SUCCESS;
+ JNI_COCOA_ENTER(env);
+ [ThreadUtilities performOnMainThreadWaiting:YES block:^() {
+ io_service_t ioHIDService = IOServiceGetMatchingService(kIOMasterPortDefault,
+ IOServiceMatching(kIOHIDSystemClass));
+ if (!ioHIDService) {
+ err = KERN_FAILURE;
+ return;
+ }
+
+ io_connect_t ioHIDConnect;
+ err = IOServiceOpen(ioHIDService, mach_task_self(), kIOHIDParamConnectType, &ioHIDConnect);
+ if (err == KERN_SUCCESS) {
+ err = IOHIDSetModifierLockState(ioHIDConnect, kIOHIDCapsLockState, on);
+ IOServiceClose(ioHIDConnect);
+ }
+
+ IOObjectRelease(ioHIDService);
+ }];
+ JNI_COCOA_EXIT(env);
+ return err == KERN_SUCCESS;
+}
\ No newline at end of file
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/SystemHotkey.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/SystemHotkey.m
new file mode 100644
index 000000000000..783a36680578
--- /dev/null
+++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/SystemHotkey.m
@@ -0,0 +1,794 @@
+#import
+#import
+#import
+
+#import "CRobotKeyCode.h"
+#import "java_awt_event_KeyEvent.h"
+
+#include
+#include "jni_util.h"
+
+
+extern JavaVM *jvm;
+
+enum LOG_LEVEL {
+ LL_TRACE,
+ LL_DEBUG,
+ LL_INFO,
+ LL_WARNING,
+ LL_ERROR
+};
+
+void plog(int logLevel, const char *formatMsg, ...) {
+ if (!jvm)
+ return;
+ if (logLevel < LL_TRACE || logLevel > LL_ERROR || formatMsg == NULL)
+ return;
+
+ // TODO: check whether current logLevel is enabled in PlatformLogger
+ static jobject loggerObject = NULL;
+ static jclass clazz = NULL;
+ static jmethodID midTrace = NULL, midDebug = NULL, midInfo = NULL, midWarn = NULL, midError = NULL;
+
+ if (loggerObject == NULL) {
+ JNIEnv* env;
+ (*jvm)->AttachCurrentThreadAsDaemon(jvm, (void**)&env, NULL);
+ jclass shkClass = (*env)->FindClass(env, "java/awt/desktop/SystemHotkey");
+ if (shkClass == NULL)
+ return;
+ jfieldID fieldId = (*env)->GetStaticFieldID(env, shkClass, "ourLog", "Lsun/util/logging/PlatformLogger;");
+ if (fieldId == NULL)
+ return;
+ loggerObject = (*env)->GetStaticObjectField(env, shkClass, fieldId);
+ loggerObject = (*env)->NewGlobalRef(env, loggerObject);
+
+ clazz = (*env)->GetObjectClass(env, loggerObject);
+ if (clazz == NULL) {
+ NSLog(@"plogImpl: can't find PlatformLogger class");
+ return;
+ }
+
+ midTrace = (*env)->GetMethodID(env, clazz, "finest", "(Ljava/lang/String;)V");
+ midDebug = (*env)->GetMethodID(env, clazz, "fine", "(Ljava/lang/String;)V");
+ midInfo = (*env)->GetMethodID(env, clazz, "info", "(Ljava/lang/String;)V");
+ midWarn = (*env)->GetMethodID(env, clazz, "warning", "(Ljava/lang/String;)V");
+ midError = (*env)->GetMethodID(env, clazz, "severe", "(Ljava/lang/String;)V");
+ }
+
+ jmethodID mid = midTrace;
+ switch (logLevel) {
+ case LL_DEBUG: mid = midDebug; break;
+ case LL_INFO: mid = midInfo; break;
+ case LL_WARNING: mid = midWarn; break;
+ case LL_ERROR: mid = midError; break;
+ }
+ if (mid == NULL) {
+ NSLog(@"plogImpl: undefined log-method for level %d", logLevel);
+ return;
+ }
+
+ va_list args;
+ va_start(args, formatMsg);
+ const int bufSize = 512;
+ char buf[bufSize];
+ vsnprintf(buf, bufSize, formatMsg, args);
+ va_end(args);
+
+ JNIEnv* env;
+ jstring jstr;
+ (*jvm)->AttachCurrentThreadAsDaemon(jvm, (void**)&env, NULL);
+ jstr = (*env)->NewStringUTF(env, buf);
+
+ (*env)->CallVoidMethod(env, loggerObject, mid, jstr);
+ (*env)->DeleteLocalRef(env, jstr);
+}
+
+static const char * toCString(id obj) {
+ return obj == nil ? "nil" : [NSString stringWithFormat:@"%@", obj].UTF8String;
+}
+
+// Copy of java.awt.event.KeyEvent.[MODIFIER]_DOWN_MASK
+static const int AWT_SHIFT_DOWN_MASK = 1 << 6;
+static const int AWT_CTRL_DOWN_MASK = 1 << 7;
+static const int AWT_META_DOWN_MASK = 1 << 8;
+static const int AWT_ALT_DOWN_MASK = 1 << 9;
+
+static int symbolicHotKeysModifiers2java(int mask) {
+ // NOTE: these masks doesn't coincide with macos events masks
+ // and can be used only to parse data from settings domain "com.apple.symbolichotkeys"
+ const int MACOS_SHIFT_MASK = 0x020000;
+ const int MACOS_CONTROL_MASK = 0x040000;
+ const int MACOS_OPTION_MASK = 0x080000;
+ const int MACOS_CMD_MASK = 0x100000;
+
+ int result = 0;
+ if (mask & MACOS_SHIFT_MASK)
+ result |= AWT_SHIFT_DOWN_MASK;
+ if (mask & MACOS_CONTROL_MASK)
+ result |= AWT_CTRL_DOWN_MASK;
+ if (mask & MACOS_OPTION_MASK)
+ result |= AWT_ALT_DOWN_MASK;
+ if (mask & MACOS_CMD_MASK)
+ result |= AWT_META_DOWN_MASK;
+ return result;
+}
+
+static NSString * getAppleSymbolicHotKeysDescription(int hotKeyId) {
+ static NSDictionary * hotkeyId2DescMap = nil;
+ if (hotkeyId2DescMap == nil) {
+ hotkeyId2DescMap = [NSDictionary dictionaryWithObjectsAndKeys:
+ @"Move focus to the menu bar", [NSNumber numberWithInt:7],
+ @"Move focus to the Dock", [NSNumber numberWithInt:8],
+ @"Move focus to active or next window", [NSNumber numberWithInt:9],
+ @"Move focus to window toolbar", [NSNumber numberWithInt:10],
+ @"Move focus to floating window", [NSNumber numberWithInt:11],
+ @"Change the way Tab moves focus", [NSNumber numberWithInt:13],
+ @"Turn zoom on or off", [NSNumber numberWithInt:15],
+ @"Zoom in", [NSNumber numberWithInt:17],
+ @"Zoom out", [NSNumber numberWithInt:19],
+ @"Reverse Black and White", [NSNumber numberWithInt:21],
+ @"Turn image smoothing on or off", [NSNumber numberWithInt:23],
+ @"Increase Contrast", [NSNumber numberWithInt:25],
+ @"Decrease Contrast", [NSNumber numberWithInt:26],
+ @"Move focus to the next window in application", [NSNumber numberWithInt:27],
+ @"Save picture of screen as file", [NSNumber numberWithInt:28],
+ @"Copy picture of screen to clipboard", [NSNumber numberWithInt:29],
+ @"Save picture of selected area as file", [NSNumber numberWithInt:30],
+ @"Copy picture of selected area to clipboard", [NSNumber numberWithInt:31],
+ @"All Windows", [NSNumber numberWithInt:32],
+ @"Application Windows", [NSNumber numberWithInt:33],
+ @"All Windows (Slow)", [NSNumber numberWithInt:34],
+ @"Application Windows (Slow)", [NSNumber numberWithInt:35],
+ @"Desktop", [NSNumber numberWithInt:36],
+ @"Desktop (Slow)", [NSNumber numberWithInt:37],
+ @"Move focus to the window drawer", [NSNumber numberWithInt:51],
+ @"Turn Dock Hiding On/Off", [NSNumber numberWithInt:52],
+ @"Move focus to the status menus", [NSNumber numberWithInt:57],
+ @"Turn VoiceOver on / off", [NSNumber numberWithInt:59],
+ @"Select the previous input source", [NSNumber numberWithInt:60],
+ @"Select the next source in the Input Menu", [NSNumber numberWithInt:61],
+ @"Dashboard", [NSNumber numberWithInt:62],
+ @"Dashboard (Slow)", [NSNumber numberWithInt:63],
+ @"Show Spotlight search field", [NSNumber numberWithInt:64],
+ @"Show Spotlight window", [NSNumber numberWithInt:65],
+ @"Dictionary MouseOver", [NSNumber numberWithInt:70],
+ @"Hide and show Front Row", [NSNumber numberWithInt:73],
+ @"Activate Spaces", [NSNumber numberWithInt:75],
+ @"Activate Spaces (Slow)", [NSNumber numberWithInt:76],
+ @"Spaces Left", [NSNumber numberWithInt:79],
+ @"Spaces Right", [NSNumber numberWithInt:81],
+ @"Spaces Down", [NSNumber numberWithInt:83],
+ @"Spaces Up", [NSNumber numberWithInt:85],
+ @"Show Help Menu", [NSNumber numberWithInt:91],
+ @"Show Help Menu", [NSNumber numberWithInt:92],
+ @"Show Help Menu", [NSNumber numberWithInt:98],
+ @"Switch to Space 1", [NSNumber numberWithInt:118],
+ @"Switch to Space 2", [NSNumber numberWithInt:119],
+ @"Switch to Space 3", [NSNumber numberWithInt:120],
+ @"Switch to Space 4", [NSNumber numberWithInt:121],
+ @"Show Launchpad", [NSNumber numberWithInt:160],
+ @"Show Accessibility Controls", [NSNumber numberWithInt:162],
+ @"Show Notification Center", [NSNumber numberWithInt:163],
+ @"Turn Do-Not-Disturb On/Off", [NSNumber numberWithInt:175],
+ @"Turn focus following On/Off", [NSNumber numberWithInt:179],
+ nil
+ ];
+
+ [hotkeyId2DescMap retain];
+ }
+
+ return [hotkeyId2DescMap objectForKey : [NSNumber numberWithInt : hotKeyId]];
+}
+
+@interface DefaultParams: NSObject
+@property (assign) BOOL enabled;
+@property (strong) NSString *key_equivalent;
++(DefaultParams *) create:(NSString *)key_equivalent enabled:(BOOL)enabled;
+@end
+
+@implementation DefaultParams
++(DefaultParams *) create:(NSString *)key_equivalent enabled:(BOOL)enabled {
+ DefaultParams * result = [[[DefaultParams alloc] init] autorelease];
+ result.enabled = enabled;
+ result.key_equivalent = key_equivalent;
+ return result;
+}
+@end
+
+static NSMutableDictionary * createDefaultParams();
+
+static int NSModifiers2java(int mask) {
+ int result = 0;
+ if (mask & shiftKey)
+ result |= AWT_SHIFT_DOWN_MASK;
+ if (mask & controlKey)
+ result |= AWT_CTRL_DOWN_MASK;
+ if (mask & optionKey)
+ result |= AWT_ALT_DOWN_MASK;
+ if (mask & cmdKey)
+ result |= AWT_META_DOWN_MASK;
+ return result;
+}
+
+static int javaModifiers2NS(int jmask) {
+ int result = 0;
+ if (jmask & AWT_SHIFT_DOWN_MASK)
+ result |= NSShiftKeyMask;
+ if (jmask & AWT_CTRL_DOWN_MASK)
+ result |= NSControlKeyMask;
+ if (jmask & AWT_ALT_DOWN_MASK)
+ result |= NSAlternateKeyMask;
+ if (jmask & AWT_META_DOWN_MASK)
+ result |= NSCommandKeyMask;
+ return result;
+}
+
+typedef bool (^ Visitor)(int, const char *, int, const char *, int);
+
+static void visitServicesShortcut(Visitor visitorBlock, NSString * key_equivalent, NSString * desc) {
+ // @ - command
+ // $ - shift
+ // ^ - control
+ // ~ - alt(option)
+ const bool modIsSHIFT = [key_equivalent containsString:@"$"];
+ const bool modIsCONTROL = [key_equivalent containsString:@"^"];
+ const bool modIsOPTION = [key_equivalent containsString:@"~"];
+ const bool modIsCMD = [key_equivalent containsString:@"@"];
+ int modifiers = 0;
+ if (modIsSHIFT)
+ modifiers |= AWT_SHIFT_DOWN_MASK;
+ if (modIsCONTROL)
+ modifiers |= AWT_CTRL_DOWN_MASK;
+ if (modIsOPTION)
+ modifiers |= AWT_ALT_DOWN_MASK;
+ if (modIsCMD)
+ modifiers |= AWT_META_DOWN_MASK;
+
+ NSCharacterSet * excludeSet = [NSCharacterSet characterSetWithCharactersInString:@"@$^~"];
+ NSString * keyChar = [key_equivalent stringByTrimmingCharactersInSet:excludeSet];
+
+ visitorBlock(-1, keyChar.UTF8String, modifiers, desc.UTF8String, -1);
+}
+
+void readSystemHotkeysImpl(Visitor visitorBlock) {
+ // 1. read from com.apple.symbolichotkeys.plist (domain with custom (user defined) shortcuts)
+ @try {
+
+ NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults];
+ NSDictionary * shk = [defaults persistentDomainForName:@"com.apple.symbolichotkeys"];
+ if (shk != nil) {
+ // AppleSymbolicHotKeys = {
+ // 10 = {
+ // enabled = 1;
+ // value = {
+ // parameters = (
+ // 65535,
+ // 96,
+ // 8650752
+ // );
+ // type = standard;
+ // };
+ // };
+ // ......
+ // }
+ id hotkeys = [shk valueForKey:@"AppleSymbolicHotKeys"];
+ if (hotkeys == nil)
+ plog(LL_DEBUG, "key AppleSymbolicHotKeys doesn't exist in domain com.apple.symbolichotkeys");
+ else if (![hotkeys isKindOfClass:[NSDictionary class]])
+ plog(LL_DEBUG, "object for key 'AppleSymbolicHotKeys' isn't NSDictionary (class=%s)", [hotkeys className].UTF8String);
+ else {
+ for (id keyObj in hotkeys) {
+ if (![keyObj isKindOfClass:[NSString class]]) {
+ plog(LL_DEBUG, "key '%s' isn't instance of NSString (class=%s)", toCString(keyObj), [keyObj className].UTF8String);
+ continue;
+ }
+ NSString *hkNumber = keyObj;
+ id hkDesc = hotkeys[hkNumber];
+ if (![hkDesc isKindOfClass:[NSDictionary class]]) {
+ plog(LL_DEBUG, "hotkey descriptor '%s' isn't instance of NSDictionary (class=%s)", toCString(hkDesc), [hkDesc className].UTF8String);
+ continue;
+ }
+ NSDictionary *sdict = hkDesc;
+ id objValue = sdict[@"value"];
+ if (objValue == nil)
+ continue;
+
+ if (![objValue isKindOfClass:[NSDictionary class]]) {
+ plog(LL_DEBUG, "property 'value' %s isn't instance of NSDictionary (class=%s)", toCString(objValue), [objValue className].UTF8String);
+ continue;
+ }
+
+ id objEnabled = sdict[@"enabled"];
+ BOOL enabled = objEnabled != nil && [objEnabled boolValue] == YES;
+
+ if (!enabled)
+ continue;
+
+ NSDictionary * value = objValue;
+ id objParams = value[@"parameters"];
+ if (![objParams isKindOfClass:[NSArray class]]) {
+ plog(LL_DEBUG, "property 'parameters' %s isn't instance of NSArray (class=%s)", toCString(objParams), [objParams className].UTF8String);
+ continue;
+ }
+
+ NSArray *parameters = objParams;
+ if ([parameters count] < 3) {
+ plog(LL_DEBUG, "too small lenght of parameters %d", [parameters count]);
+ continue;
+ }
+
+ id p0 = parameters[0];
+ id p1 = parameters[1];
+ id p2 = parameters[2];
+
+ if (![p0 isKindOfClass:[NSNumber class]] || ![p1 isKindOfClass:[NSNumber class]] || ![p2 isKindOfClass:[NSNumber class]]) {
+ plog(LL_DEBUG, "some of parameters isn't instance of NSNumber (%s, %s, %s)", [p0 className].UTF8String, [p1 className].UTF8String, [p2 className].UTF8String);
+ continue;
+ }
+
+ //parameter 1: ASCII code of the character (or 65535 - hex 0xFFFF - for non-ASCII characters).
+ //parameter 2: the keyboard key code for the character.
+ //Parameter 3: the sum of the control, command, shift and option keys. these are bits 17-20 in binary: shift is bit 17, control is bit 18, option is bit 19, and command is bit 20.
+ // 0x020000 => "Shift",
+ // 0x040000 => "Control",
+ // 0x080000 => "Option",
+ // 0x100000 => "Command"
+
+ int asciiCode = p0 == nil ? 0xFFFF : [p0 intValue];
+ int vkeyCode = p1 == nil ? -1 : [p1 intValue];
+ int modifiers = p2 == nil ? 0 : [p2 intValue];
+
+ char keyCharBuf[64];
+ const char * keyCharStr = keyCharBuf;
+ if (asciiCode >= 0 && asciiCode <= 0xFF) {
+ sprintf(keyCharBuf, "%c", asciiCode);
+ } else
+ keyCharStr = NULL;
+ NSString * description = getAppleSymbolicHotKeysDescription([hkNumber intValue]);
+ visitorBlock(vkeyCode, keyCharStr, symbolicHotKeysModifiers2java(modifiers), description == nil ? NULL : description.UTF8String, [hkNumber intValue]);
+ }
+ }
+ } else {
+ plog(LL_DEBUG, "domain com.apple.symbolichotkeys doesn't exist");
+ }
+
+ // 2. read from Pbs (domain with services shortcuts)
+ NSMutableDictionary * allDefParams = createDefaultParams();
+ NSDictionary * pbs = [defaults persistentDomainForName:@"pbs"];
+ if (pbs) {
+// NSServicesStatus = {
+// "com.apple.Terminal - Open man Page in Terminal - openManPage" = {
+// "key_equivalent" = "@$m";
+// };
+// "com.apple.Terminal - Search man Page Index in Terminal - searchManPages" = {
+// "enabled_context_menu" = 0;
+// "enabled_services_menu" = 0;
+// "key_equivalent" = "@$a";
+// "presentation_modes" = {
+// ContextMenu = 0;
+// ServicesMenu = 0;
+// };
+// };
+// };
+// }
+
+ NSDictionary *services = [pbs valueForKey:@"NSServicesStatus"];
+ if (services) {
+ for (NSString *key in services) {
+ id value = services[key];
+ if (![value isKindOfClass:[NSDictionary class]]) {
+ plog(LL_DEBUG, "'%s' isn't instance of NSDictionary (class=%s)", toCString(value), [value className].UTF8String);
+ continue;
+ }
+ // NOTE: unchanged default params will not appear here, check allDefParams at the end of this loop
+ DefaultParams * defParams = [allDefParams objectForKey:key];
+ [allDefParams removeObjectForKey:key];
+
+ NSDictionary *sdict = value;
+ NSString *key_equivalent = sdict[@"key_equivalent"];
+ if (!key_equivalent && defParams != nil) {
+ key_equivalent = defParams.key_equivalent;
+ }
+ if (!key_equivalent)
+ continue;
+
+ NSString *enabled = sdict[@"enabled_services_menu"];
+ if (enabled != nil && [enabled boolValue] == NO) {
+ continue;
+ }
+ if (enabled == nil && defParams != nil && !defParams.enabled) {
+ continue;
+ }
+
+ visitServicesShortcut(visitorBlock, key_equivalent, key);
+ }
+ }
+ }
+
+ // Iterate through rest of allDefParams
+ for (NSString* key in allDefParams) {
+ DefaultParams * defParams = allDefParams[key];
+ if (!defParams.enabled) {
+ continue;
+ }
+ visitServicesShortcut(visitorBlock, defParams.key_equivalent, key);
+ }
+
+#ifdef USE_CARBON_CopySymbolicHotKeys
+ // 3. read from core services
+ CFArrayRef registeredHotKeys;
+ if(CopySymbolicHotKeys(®isteredHotKeys) == noErr) {
+ CFIndex count = CFArrayGetCount(registeredHotKeys);
+ for(CFIndex i = 0; i < count; i++) {
+ CFDictionaryRef hotKeyInfo = CFArrayGetValueAtIndex(registeredHotKeys, i);
+ CFNumberRef hotKeyCode = CFDictionaryGetValue(hotKeyInfo, kHISymbolicHotKeyCode);
+ CFNumberRef hotKeyModifiers = CFDictionaryGetValue(hotKeyInfo, kHISymbolicHotKeyModifiers);
+ CFBooleanRef hotKeyEnabled = CFDictionaryGetValue(hotKeyInfo, kHISymbolicHotKeyEnabled);
+
+ int64_t vkeyCode = -1;
+ CFNumberGetValue(hotKeyCode, kCFNumberSInt64Type, &vkeyCode);
+ int64_t keyModifiers = 0;
+ CFNumberGetValue(hotKeyModifiers, kCFNumberSInt64Type, &keyModifiers);
+ Boolean enabled = CFBooleanGetValue(hotKeyEnabled);
+ if (!enabled)
+ continue;
+
+ visitorBlock(vkeyCode, NULL, NSModifiers2java(keyModifiers), NULL, -1);
+ }
+
+ CFRelease(registeredHotKeys);
+ }
+#endif // USE_CARBON_CopySymbolicHotKeys
+ }
+ @catch (NSException *exception) {
+ NSLog(@"readSystemHotkeys: catched exception, reason '%@'", exception.reason);
+ }
+}
+
+bool isSystemShortcut_NextWindowInApplication(NSUInteger modifiersMask, int keyCode, NSString * chars) {
+ const int shortcutUid_NextWindowInApplication = 27;
+ static NSString * shortcutCharacter = nil;
+ static int shortcutMask = 0;
+ static int shortcutKeyCode = -1;
+ if (shortcutCharacter == nil && shortcutKeyCode == -1) {
+ readSystemHotkeysImpl(
+ ^bool(int vkeyCode, const char * keyCharStr, int jmodifiers, const char * descriptionStr, int hotkeyUid) {
+ if (hotkeyUid != shortcutUid_NextWindowInApplication)
+ return true;
+
+ if (keyCharStr != NULL) {
+ shortcutCharacter = [[NSString stringWithFormat:@"%s", keyCharStr] retain];
+ }
+
+ if (vkeyCode != -1) {
+ shortcutKeyCode = vkeyCode;
+ }
+
+ shortcutMask = javaModifiers2NS(jmodifiers);
+ return false;
+ }
+ );
+ if (shortcutCharacter == nil && shortcutKeyCode == -1) {
+ shortcutCharacter = @"`";
+ shortcutMask = NSCommandKeyMask;
+ }
+ }
+
+ int ignoredModifiers = NSAlphaShiftKeyMask | NSFunctionKeyMask | NSNumericPadKeyMask | NSHelpKeyMask;
+ // Ignore Shift because of JBR-4899.
+ if (!(shortcutMask & NSShiftKeyMask)) {
+ ignoredModifiers |= NSShiftKeyMask;
+ }
+ if ((modifiersMask & ~ignoredModifiers) == shortcutMask) {
+ return shortcutKeyCode == keyCode || [chars isEqualToString:shortcutCharacter];
+ }
+
+ return false;
+}
+
+JNIEXPORT void JNICALL Java_java_awt_desktop_SystemHotkeyReader_readSystemHotkeys(JNIEnv* env, jobject reader) {
+ jclass clsReader = (*env)->GetObjectClass(env, reader);
+ jmethodID methodAdd = (*env)->GetMethodID(env, clsReader, "add", "(ILjava/lang/String;ILjava/lang/String;)V");
+
+ readSystemHotkeysImpl(
+ ^bool(int vkeyCode, const char * keyCharStr, int jmodifiers, const char * descriptionStr, int hotkeyUid){
+ jstring jkeyChar = keyCharStr == NULL ? NULL : (*env)->NewStringUTF(env, keyCharStr);
+ jstring jdesc = descriptionStr == NULL ? NULL : (*env)->NewStringUTF(env, descriptionStr);
+ (*env)->CallVoidMethod(
+ env, reader, methodAdd, (jint)vkeyCode, jkeyChar, (jint)jmodifiers, jdesc
+ );
+ return true;
+ }
+ );
+}
+
+JNIEXPORT jint JNICALL Java_java_awt_desktop_SystemHotkeyReader_osx2java(JNIEnv* env, jclass clazz, jint osxKeyCode) {
+ static NSDictionary * osx2javaMap = nil;
+ if (osx2javaMap == nil) {
+ osx2javaMap = [NSDictionary dictionaryWithObjectsAndKeys:
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_BACK_SPACE], [NSNumber numberWithInt:OSX_Delete],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_TAB], [NSNumber numberWithInt:OSX_kVK_Tab],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_ENTER], [NSNumber numberWithInt:OSX_kVK_Return],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_CLEAR], [NSNumber numberWithInt:OSX_kVK_ANSI_KeypadClear],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_SHIFT], [NSNumber numberWithInt:OSX_Shift],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_CONTROL], [NSNumber numberWithInt:OSX_Control],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_ALT], [NSNumber numberWithInt:OSX_Option],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_ALT_GRAPH], [NSNumber numberWithInt:OSX_RightOption],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_CAPS_LOCK], [NSNumber numberWithInt:OSX_CapsLock],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_ESCAPE], [NSNumber numberWithInt:OSX_Escape],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_SPACE], [NSNumber numberWithInt:OSX_kVK_Space],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_PAGE_UP], [NSNumber numberWithInt:OSX_PageUp],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_PAGE_DOWN], [NSNumber numberWithInt:OSX_PageDown],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_END], [NSNumber numberWithInt:OSX_End],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_HOME], [NSNumber numberWithInt:OSX_Home],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_LEFT], [NSNumber numberWithInt:OSX_LeftArrow],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_UP], [NSNumber numberWithInt:OSX_UpArrow],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_RIGHT], [NSNumber numberWithInt:OSX_RightArrow],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_DOWN], [NSNumber numberWithInt:OSX_DownArrow],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_COMMA], [NSNumber numberWithInt:OSX_kVK_ANSI_Comma],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_MINUS], [NSNumber numberWithInt:OSX_kVK_ANSI_Minus],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_PERIOD], [NSNumber numberWithInt:OSX_kVK_ANSI_Period],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_SLASH], [NSNumber numberWithInt:OSX_kVK_ANSI_Slash],
+
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_0], [NSNumber numberWithInt:OSX_kVK_ANSI_0],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_1], [NSNumber numberWithInt:OSX_kVK_ANSI_1],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_2], [NSNumber numberWithInt:OSX_kVK_ANSI_2],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_3], [NSNumber numberWithInt:OSX_kVK_ANSI_3],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_4], [NSNumber numberWithInt:OSX_kVK_ANSI_4],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_5], [NSNumber numberWithInt:OSX_kVK_ANSI_5],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_6], [NSNumber numberWithInt:OSX_kVK_ANSI_6],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_7], [NSNumber numberWithInt:OSX_kVK_ANSI_7],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_8], [NSNumber numberWithInt:OSX_kVK_ANSI_8],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_9], [NSNumber numberWithInt:OSX_kVK_ANSI_9],
+
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_SEMICOLON], [NSNumber numberWithInt:OSX_kVK_ANSI_Semicolon],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_EQUALS], [NSNumber numberWithInt:OSX_kVK_ANSI_Equal],
+
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_A], [NSNumber numberWithInt:OSX_kVK_ANSI_A],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_B], [NSNumber numberWithInt:OSX_kVK_ANSI_B],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_C], [NSNumber numberWithInt:OSX_kVK_ANSI_C],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_D], [NSNumber numberWithInt:OSX_kVK_ANSI_D],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_E], [NSNumber numberWithInt:OSX_kVK_ANSI_E],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_F], [NSNumber numberWithInt:OSX_kVK_ANSI_F],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_G], [NSNumber numberWithInt:OSX_kVK_ANSI_G],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_H], [NSNumber numberWithInt:OSX_kVK_ANSI_H],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_I], [NSNumber numberWithInt:OSX_kVK_ANSI_I],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_J], [NSNumber numberWithInt:OSX_kVK_ANSI_J],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_K], [NSNumber numberWithInt:OSX_kVK_ANSI_K],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_L], [NSNumber numberWithInt:OSX_kVK_ANSI_L],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_M], [NSNumber numberWithInt:OSX_kVK_ANSI_M],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_N], [NSNumber numberWithInt:OSX_kVK_ANSI_N],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_O], [NSNumber numberWithInt:OSX_kVK_ANSI_O],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_P], [NSNumber numberWithInt:OSX_kVK_ANSI_P],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_Q], [NSNumber numberWithInt:OSX_kVK_ANSI_Q],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_R], [NSNumber numberWithInt:OSX_kVK_ANSI_R],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_S], [NSNumber numberWithInt:OSX_kVK_ANSI_S],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_T], [NSNumber numberWithInt:OSX_kVK_ANSI_T],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_U], [NSNumber numberWithInt:OSX_kVK_ANSI_U],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_V], [NSNumber numberWithInt:OSX_kVK_ANSI_V],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_W], [NSNumber numberWithInt:OSX_kVK_ANSI_W],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_X], [NSNumber numberWithInt:OSX_kVK_ANSI_X],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_Y], [NSNumber numberWithInt:OSX_kVK_ANSI_Y],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_Z], [NSNumber numberWithInt:OSX_kVK_ANSI_Z],
+
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_OPEN_BRACKET], [NSNumber numberWithInt:OSX_kVK_ANSI_LeftBracket],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_BACK_SLASH], [NSNumber numberWithInt:OSX_kVK_ANSI_Backslash],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_CLOSE_BRACKET], [NSNumber numberWithInt:OSX_kVK_ANSI_RightBracket],
+
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_NUMPAD0], [NSNumber numberWithInt:OSX_kVK_ANSI_Keypad0],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_NUMPAD1], [NSNumber numberWithInt:OSX_kVK_ANSI_Keypad1],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_NUMPAD2], [NSNumber numberWithInt:OSX_kVK_ANSI_Keypad2],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_NUMPAD3], [NSNumber numberWithInt:OSX_kVK_ANSI_Keypad3],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_NUMPAD4], [NSNumber numberWithInt:OSX_kVK_ANSI_Keypad4],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_NUMPAD5], [NSNumber numberWithInt:OSX_kVK_ANSI_Keypad5],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_NUMPAD6], [NSNumber numberWithInt:OSX_kVK_ANSI_Keypad6],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_NUMPAD7], [NSNumber numberWithInt:OSX_kVK_ANSI_Keypad7],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_NUMPAD8], [NSNumber numberWithInt:OSX_kVK_ANSI_Keypad8],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_NUMPAD9], [NSNumber numberWithInt:OSX_kVK_ANSI_Keypad9],
+
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_MULTIPLY], [NSNumber numberWithInt:OSX_kVK_ANSI_KeypadMultiply],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_ADD], [NSNumber numberWithInt:OSX_kVK_ANSI_KeypadPlus],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_SUBTRACT], [NSNumber numberWithInt:OSX_kVK_ANSI_KeypadMinus],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_DECIMAL], [NSNumber numberWithInt:OSX_kVK_ANSI_KeypadDecimal],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_DIVIDE], [NSNumber numberWithInt:OSX_kVK_ANSI_KeypadDivide],
+
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_F1], [NSNumber numberWithInt:OSX_F1],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_F2], [NSNumber numberWithInt:OSX_F2],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_F3], [NSNumber numberWithInt:OSX_F3],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_F4], [NSNumber numberWithInt:OSX_F4],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_F5], [NSNumber numberWithInt:OSX_F5],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_F6], [NSNumber numberWithInt:OSX_F6],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_F7], [NSNumber numberWithInt:OSX_F7],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_F8], [NSNumber numberWithInt:OSX_F8],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_F9], [NSNumber numberWithInt:OSX_F9],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_F10], [NSNumber numberWithInt:OSX_F10],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_F11], [NSNumber numberWithInt:OSX_F11],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_F12], [NSNumber numberWithInt:OSX_F12],
+
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_DELETE], [NSNumber numberWithInt:OSX_ForwardDelete],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_HELP], [NSNumber numberWithInt:OSX_Help],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_META], [NSNumber numberWithInt:OSX_Command],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_BACK_QUOTE], [NSNumber numberWithInt:OSX_kVK_ANSI_Grave],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_QUOTE], [NSNumber numberWithInt:OSX_kVK_ANSI_Quote],
+
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_F13], [NSNumber numberWithInt:OSX_F13],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_F14], [NSNumber numberWithInt:OSX_F14],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_F15], [NSNumber numberWithInt:OSX_F15],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_F16], [NSNumber numberWithInt:OSX_F16],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_F17], [NSNumber numberWithInt:OSX_F17],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_F18], [NSNumber numberWithInt:OSX_F18],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_F19], [NSNumber numberWithInt:OSX_F19],
+ [NSNumber numberWithInt:java_awt_event_KeyEvent_VK_F20], [NSNumber numberWithInt:OSX_F20],
+ nil
+ ];
+
+ [osx2javaMap retain];
+ }
+
+ id val = [osx2javaMap objectForKey : [NSNumber numberWithInt : osxKeyCode]];
+ if (nil != val)
+ return [val intValue];
+
+ return java_awt_event_KeyEvent_VK_UNDEFINED;
+}
+
+JNIEXPORT jstring JNICALL Java_java_awt_desktop_SystemHotkey_osxKeyCodeDescription(JNIEnv* env, jclass clazz, jint osxKeyCode) {
+ static NSDictionary * osxCode2DescMap = nil;
+ if (osxCode2DescMap == nil) {
+ osxCode2DescMap = [NSDictionary dictionaryWithObjectsAndKeys:
+ @"A", [NSNumber numberWithInt:kVK_ANSI_A],
+ @"S", [NSNumber numberWithInt:kVK_ANSI_S],
+ @"D", [NSNumber numberWithInt:kVK_ANSI_D],
+ @"F", [NSNumber numberWithInt:kVK_ANSI_F],
+ @"H", [NSNumber numberWithInt:kVK_ANSI_H],
+ @"G", [NSNumber numberWithInt:kVK_ANSI_G],
+ @"Z", [NSNumber numberWithInt:kVK_ANSI_Z],
+ @"X", [NSNumber numberWithInt:kVK_ANSI_X],
+ @"C", [NSNumber numberWithInt:kVK_ANSI_C],
+ @"V", [NSNumber numberWithInt:kVK_ANSI_V],
+ @"B", [NSNumber numberWithInt:kVK_ANSI_B],
+ @"Q", [NSNumber numberWithInt:kVK_ANSI_Q],
+ @"W", [NSNumber numberWithInt:kVK_ANSI_W],
+ @"E", [NSNumber numberWithInt:kVK_ANSI_E],
+ @"R", [NSNumber numberWithInt:kVK_ANSI_R],
+ @"Y", [NSNumber numberWithInt:kVK_ANSI_Y],
+ @"T", [NSNumber numberWithInt:kVK_ANSI_T],
+ @"1", [NSNumber numberWithInt:kVK_ANSI_1],
+ @"2", [NSNumber numberWithInt:kVK_ANSI_2],
+ @"3", [NSNumber numberWithInt:kVK_ANSI_3],
+ @"4", [NSNumber numberWithInt:kVK_ANSI_4],
+ @"6", [NSNumber numberWithInt:kVK_ANSI_6],
+ @"5", [NSNumber numberWithInt:kVK_ANSI_5],
+ @"Equal", [NSNumber numberWithInt:kVK_ANSI_Equal],
+ @"9", [NSNumber numberWithInt:kVK_ANSI_9],
+ @"7", [NSNumber numberWithInt:kVK_ANSI_7],
+ @"Minus", [NSNumber numberWithInt:kVK_ANSI_Minus],
+ @"8", [NSNumber numberWithInt:kVK_ANSI_8],
+ @"0", [NSNumber numberWithInt:kVK_ANSI_0],
+ @"RightBracket", [NSNumber numberWithInt:kVK_ANSI_RightBracket],
+ @"O", [NSNumber numberWithInt:kVK_ANSI_O],
+ @"U", [NSNumber numberWithInt:kVK_ANSI_U],
+ @"LeftBracket", [NSNumber numberWithInt:kVK_ANSI_LeftBracket],
+ @"I", [NSNumber numberWithInt:kVK_ANSI_I],
+ @"P", [NSNumber numberWithInt:kVK_ANSI_P],
+ @"L", [NSNumber numberWithInt:kVK_ANSI_L],
+ @"J", [NSNumber numberWithInt:kVK_ANSI_J],
+ @"Quote", [NSNumber numberWithInt:kVK_ANSI_Quote],
+ @"K", [NSNumber numberWithInt:kVK_ANSI_K],
+ @"Semicolon", [NSNumber numberWithInt:kVK_ANSI_Semicolon],
+ @"Backslash", [NSNumber numberWithInt:kVK_ANSI_Backslash],
+ @"Comma", [NSNumber numberWithInt:kVK_ANSI_Comma],
+ @"Slash", [NSNumber numberWithInt:kVK_ANSI_Slash],
+ @"N", [NSNumber numberWithInt:kVK_ANSI_N],
+ @"M", [NSNumber numberWithInt:kVK_ANSI_M],
+ @"Period", [NSNumber numberWithInt:kVK_ANSI_Period],
+ @"Grave", [NSNumber numberWithInt:kVK_ANSI_Grave],
+ @"KeypadDecimal", [NSNumber numberWithInt:kVK_ANSI_KeypadDecimal],
+ @"KeypadMultiply", [NSNumber numberWithInt:kVK_ANSI_KeypadMultiply],
+ @"KeypadPlus", [NSNumber numberWithInt:kVK_ANSI_KeypadPlus],
+ @"KeypadClear", [NSNumber numberWithInt:kVK_ANSI_KeypadClear],
+ @"KeypadDivide", [NSNumber numberWithInt:kVK_ANSI_KeypadDivide],
+ @"KeypadEnter", [NSNumber numberWithInt:kVK_ANSI_KeypadEnter],
+ @"KeypadMinus", [NSNumber numberWithInt:kVK_ANSI_KeypadMinus],
+ @"KeypadEquals", [NSNumber numberWithInt:kVK_ANSI_KeypadEquals],
+ @"Keypad0", [NSNumber numberWithInt:kVK_ANSI_Keypad0],
+ @"Keypad1", [NSNumber numberWithInt:kVK_ANSI_Keypad1],
+ @"Keypad2", [NSNumber numberWithInt:kVK_ANSI_Keypad2],
+ @"Keypad3", [NSNumber numberWithInt:kVK_ANSI_Keypad3],
+ @"Keypad4", [NSNumber numberWithInt:kVK_ANSI_Keypad4],
+ @"Keypad5", [NSNumber numberWithInt:kVK_ANSI_Keypad5],
+ @"Keypad6", [NSNumber numberWithInt:kVK_ANSI_Keypad6],
+ @"Keypad7", [NSNumber numberWithInt:kVK_ANSI_Keypad7],
+ @"Keypad8", [NSNumber numberWithInt:kVK_ANSI_Keypad8],
+ @"Keypad9", [NSNumber numberWithInt:kVK_ANSI_Keypad9],
+
+ /* keycodes for keys that are independent of keyboard layout*/
+ @"Return", [NSNumber numberWithInt:kVK_Return],
+ @"Tab", [NSNumber numberWithInt:kVK_Tab],
+ @"Space", [NSNumber numberWithInt:kVK_Space],
+ @"Delete", [NSNumber numberWithInt:kVK_Delete],
+ @"Escape", [NSNumber numberWithInt:kVK_Escape],
+ @"Command", [NSNumber numberWithInt:kVK_Command],
+ @"Shift", [NSNumber numberWithInt:kVK_Shift],
+ @"CapsLock", [NSNumber numberWithInt:kVK_CapsLock],
+ @"Option", [NSNumber numberWithInt:kVK_Option],
+ @"Control", [NSNumber numberWithInt:kVK_Control],
+ @"RightCommand", [NSNumber numberWithInt:kVK_RightCommand],
+ @"RightShift", [NSNumber numberWithInt:kVK_RightShift],
+ @"RightOption", [NSNumber numberWithInt:kVK_RightOption],
+ @"RightControl", [NSNumber numberWithInt:kVK_RightControl],
+ @"Function", [NSNumber numberWithInt:kVK_Function],
+ @"F17", [NSNumber numberWithInt:kVK_F17],
+ @"VolumeUp", [NSNumber numberWithInt:kVK_VolumeUp],
+ @"VolumeDown", [NSNumber numberWithInt:kVK_VolumeDown],
+ @"Mute", [NSNumber numberWithInt:kVK_Mute],
+ @"F18", [NSNumber numberWithInt:kVK_F18],
+ @"F19", [NSNumber numberWithInt:kVK_F19],
+ @"F20", [NSNumber numberWithInt:kVK_F20],
+ @"F5", [NSNumber numberWithInt:kVK_F5],
+ @"F6", [NSNumber numberWithInt:kVK_F6],
+ @"F7", [NSNumber numberWithInt:kVK_F7],
+ @"F3", [NSNumber numberWithInt:kVK_F3],
+ @"F8", [NSNumber numberWithInt:kVK_F8],
+ @"F9", [NSNumber numberWithInt:kVK_F9],
+ @"F11", [NSNumber numberWithInt:kVK_F11],
+ @"F13", [NSNumber numberWithInt:kVK_F13],
+ @"F16", [NSNumber numberWithInt:kVK_F16],
+ @"F14", [NSNumber numberWithInt:kVK_F14],
+ @"F10", [NSNumber numberWithInt:kVK_F10],
+ @"F12", [NSNumber numberWithInt:kVK_F12],
+ @"F15", [NSNumber numberWithInt:kVK_F15],
+ @"Help", [NSNumber numberWithInt:kVK_Help],
+ @"Home", [NSNumber numberWithInt:kVK_Home],
+ @"PageUp", [NSNumber numberWithInt:kVK_PageUp],
+ @"ForwardDelete", [NSNumber numberWithInt:kVK_ForwardDelete],
+ @"F4", [NSNumber numberWithInt:kVK_F4],
+ @"End", [NSNumber numberWithInt:kVK_End],
+ @"F2", [NSNumber numberWithInt:kVK_F2],
+ @"PageDown", [NSNumber numberWithInt:kVK_PageDown],
+ @"F1", [NSNumber numberWithInt:kVK_F1],
+ @"LeftArrow", [NSNumber numberWithInt:kVK_LeftArrow],
+ @"RightArrow", [NSNumber numberWithInt:kVK_RightArrow],
+ @"DownArrow", [NSNumber numberWithInt:kVK_DownArrow],
+ @"UpArrow", [NSNumber numberWithInt:kVK_UpArrow],
+ nil
+ ];
+
+ [osxCode2DescMap retain];
+ }
+
+ NSString * val = [osxCode2DescMap objectForKey : [NSNumber numberWithInt : osxKeyCode]];
+ if (val == nil)
+ return NULL;
+
+ return (*env)->NewStringUTF(env, val.UTF8String);
+}
+
+static NSDictionary * getDefaultParams() {
+ static NSDictionary * sid2defaults = nil;
+ if (sid2defaults == nil) {
+ sid2defaults = [NSDictionary dictionaryWithObjectsAndKeys:
+ [DefaultParams create:@"@$b" enabled:NO], @"com.apple.BluetoothFileExchange - Send File To Bluetooth Device - sendFileUsingBluetoothOBEXService",
+ [DefaultParams create:@"@^$c" enabled:YES], @"com.apple.ChineseTextConverterService - Convert Text from Simplified to Traditional Chinese - convertTextToTraditionalChinese",
+ [DefaultParams create:@"@~^$c" enabled:YES], @"com.apple.ChineseTextConverterService - Convert Text from Traditional to Simplified Chinese - convertTextToSimplifiedChinese",
+ [DefaultParams create:@"@$l" enabled:YES], @"com.apple.Safari - Search With %WebSearchProvider@ - searchWithWebSearchProvider",
+ [DefaultParams create:@"@*" enabled:NO], @"com.apple.ScriptEditor2 - Script Editor/Get Result of AppleScript - runAsAppleScript",
+ [DefaultParams create:@"@$f" enabled:NO], @"com.apple.SpotlightService - SEARCH_WITH_SPOTLIGHT - doSearchWithSpotlight",
+ [DefaultParams create:@"@$y" enabled:YES], @"com.apple.Stickies - Make Sticky - makeStickyFromTextService",
+ [DefaultParams create:@"@$m" enabled:YES], @"com.apple.Terminal - Open man Page in Terminal - openManPage",
+ [DefaultParams create:@"@$a" enabled:YES], @"com.apple.Terminal - Search man Page Index in Terminal - searchManPages",
+ nil
+ ];
+
+ [sid2defaults retain];
+ }
+
+ return sid2defaults;
+}
+
+static NSMutableDictionary * createDefaultParams() {
+ NSDictionary * sid2defaults = getDefaultParams();
+ NSMutableDictionary * result = [NSMutableDictionary dictionaryWithCapacity:[sid2defaults count]];
+ [result setDictionary:sid2defaults];
+ return result;
+}
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ComboBoxAccessibility.h b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ComboBoxAccessibility.h
index cfdd1faee61e..0679528b5816 100644
--- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ComboBoxAccessibility.h
+++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ComboBoxAccessibility.h
@@ -26,5 +26,7 @@
#import "CommonComponentAccessibility.h"
-@interface ComboBoxAccessibility : CommonComponentAccessibility
+@interface ComboBoxAccessibility : CommonComponentAccessibility {
+ CommonComponentAccessibility *value;
+}
@end
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ComboBoxAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ComboBoxAccessibility.m
index 52f171e9dedd..ec54c9b2e718 100644
--- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ComboBoxAccessibility.m
+++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/ComboBoxAccessibility.m
@@ -49,14 +49,60 @@ - (CommonComponentAccessibility *)accessibleSelection
if (axSelectedChild == NULL) {
return nil;
}
- return [CommonComponentAccessibility createWithAccessible:axSelectedChild withEnv:env withView:fView];
+ if (value != nil) {
+ [value release];
+ value = nil;
+ }
+ value = [[CommonComponentAccessibility createWithAccessible:axSelectedChild withEnv:env withView:fView] retain];
+ return value;
+}
+
+- (BOOL)isEditable
+{
+ JNIEnv* env = [ThreadUtilities getJNIEnv];
+ GET_CACCESSIBILITY_CLASS_RETURN(NO);
+ DECLARE_STATIC_METHOD_RETURN(sjm_isComboBoxEditable, sjc_CAccessibility, "isComboBoxEditable", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Z", NO);
+
+ BOOL isEditable = (*env)->CallStaticBooleanMethod(env, sjc_CAccessibility, sjm_isComboBoxEditable, fAccessible, fComponent);
+ CHECK_EXCEPTION();
+
+ return isEditable;
}
// NSAccessibilityElement protocol methods
- (id)accessibilityValue
{
- return [[self accessibleSelection] accessibilityLabel];
+ JNIEnv *env = [ThreadUtilities getJNIEnv];
+ BOOL expanded = isExpanded(env, [self axContextWithEnv:env], fComponent);
+ if (expanded) {
+ return nil;
+ }
+
+ [self accessibleSelection];
+
+ return value != nil ? [value accessibilityLabel] : nil;
+}
+
+- (NSArray *)accessibilitySelectedChildren
+{
+ return [NSArray arrayWithObject:[self accessibleSelection]];
+}
+
+- (NSAccessibilityRole)accessibilityRole
+{
+ return [self isEditable]
+ ? NSAccessibilityComboBoxRole
+ : NSAccessibilityPopUpButtonRole;
+}
+
+- (void)dealloc
+{
+ if (value != nil) {
+ [value release];
+ value = nil;
+ }
+ [super dealloc];
}
@end
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.h b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.h
index be0169a064df..f0f430012c67 100644
--- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.h
+++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.h
@@ -62,6 +62,7 @@
- (BOOL)isAccessibleWithEnv:(JNIEnv _Nonnull * _Nonnull)env forAccessible:(nonnull jobject)accessible;
+ (void)postFocusChanged:(nullable id)message;
++ (void)postAnnounceWithCaller:(id _Nonnull)caller andText:(NSString * _Nonnull)text andPriority:(NSNumber * _Nonnull)priority;
+ (void) initializeRolesMap;
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.m
index 41a3accd0a59..082e689b2306 100644
--- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.m
+++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/CommonComponentAccessibility.m
@@ -34,7 +34,7 @@
#import "AWTView.h"
#import "sun_lwawt_macosx_CAccessible.h"
#import "sun_lwawt_macosx_CAccessibility.h"
-
+#import "sun_swing_AccessibleAnnouncer.h"
// GET* macros defined in JavaAccessibilityUtilities.h, so they can be shared.
static jclass sjc_CAccessibility = NULL;
@@ -55,7 +55,7 @@
#define GET_CHILDRENANDROLESRECURSIVE_METHOD_RETURN(ret) \
GET_CACCESSIBILITY_CLASS_RETURN(ret); \
GET_STATIC_METHOD_RETURN(jm_getChildrenAndRolesRecursive, sjc_CAccessibility, "getChildrenAndRolesRecursive",\
- "(Ljavax/accessibility/Accessible;Ljava/awt/Component;IZI)[Ljava/lang/Object;", ret);
+ "(Ljavax/accessibility/Accessible;Ljava/awt/Component;IZ)[Ljava/lang/Object;", ret);
static jmethodID sjm_getAccessibleComponent = NULL;
#define GET_ACCESSIBLECOMPONENT_STATIC_METHOD_RETURN(ret) \
@@ -73,6 +73,9 @@
#define GET_CACCESSIBLE_CLASS_RETURN(ret) \
GET_CLASS_RETURN(sjc_CAccessible, "sun/lwawt/macosx/CAccessible", ret);
+#define GET_CACCESSIBLE_CLASS() \
+ GET_CLASS(sjc_CAccessible, "sun/lwawt/macosx/CAccessible");
+
static NSMutableDictionary * _Nullable rolesMap;
static NSMutableDictionary * _Nullable rowRolesMapForParent;
NSString *const IgnoreClassName = @"IgnoreAccessibility";
@@ -424,6 +427,28 @@ + (void)postFocusChanged:(id)message
NSAccessibilityPostNotification([NSApp accessibilityFocusedUIElement], NSAccessibilityFocusedUIElementChangedNotification);
}
++ (void)postAnnounceWithCaller:(id)caller andText:(NSString *)text andPriority:(NSNumber *)priority
+{
+ AWT_ASSERT_APPKIT_THREAD;
+
+ NSMutableDictionary *dictionary = [NSMutableDictionary dictionaryWithCapacity:2];
+ [dictionary setObject:text forKey: NSAccessibilityAnnouncementKey];
+
+ if (sAnnouncePriorities == nil) {
+ initializeAnnouncePriorities();
+ }
+
+ NSNumber *nsPriority = [sAnnouncePriorities objectForKey:priority];
+
+ if (nsPriority == nil) {
+ nsPriority = [sAnnouncePriorities objectForKey:[NSNumber numberWithInt:sun_swing_AccessibleAnnouncer_ANNOUNCE_WITHOUT_INTERRUPTING_CURRENT_OUTPUT]];
+ }
+
+ [dictionary setObject:nsPriority forKey:NSAccessibilityPriorityKey];
+
+ NSAccessibilityPostNotificationWithUserInfo(caller, NSAccessibilityAnnouncementRequestedNotification, dictionary);
+}
+
+ (jobject) getCAccessible:(jobject)jaccessible withEnv:(JNIEnv *)env {
DECLARE_CLASS_RETURN(sjc_Accessible, "javax/accessibility/Accessible", NULL);
GET_CACCESSIBLE_CLASS_RETURN(NULL);
@@ -458,7 +483,7 @@ + (NSArray *)childrenOfParent:(CommonComponentAccessibility *)parent withEnv:(JN
if (recursive) {
GET_CHILDRENANDROLESRECURSIVE_METHOD_RETURN(nil);
jchildrenAndRoles = (jobjectArray)(*env)->CallStaticObjectMethod(env, sjc_CAccessibility, jm_getChildrenAndRolesRecursive,
- parent->fAccessible, parent->fComponent, whichChildren, allowIgnored, 0);
+ parent->fAccessible, parent->fComponent, whichChildren, allowIgnored);
CHECK_EXCEPTION();
} else {
GET_CHILDRENANDROLES_METHOD_RETURN(nil);
@@ -1324,3 +1349,84 @@ - (BOOL)accessibilityPerformIncrement {
waitUntilDone:NO];
JNI_COCOA_EXIT(env);
}
+
+
+static void nativeAnnounceAppKit(jobject jAccessible, NSString *text, NSNumber *javaPriority);
+
+/*
+ * Class: sun_swing_AccessibleAnnouncer
+ * Method: nativeAnnounce
+ * Signature: (Ljavax/accessibility/Accessible;Ljava/lang/String;I)V
+ */
+JNIEXPORT void JNICALL Java_sun_swing_AccessibleAnnouncer_nativeAnnounce
+ (JNIEnv *env, jclass cls, jobject jAccessible, jstring str, jint priority)
+{
+ JNI_COCOA_ENTER(env);
+
+ NSString *const text = JavaStringToNSString(env, str);
+ NSNumber *const javaPriority = [NSNumber numberWithInt:priority];
+
+ // From JNI specification:
+ // > Local references are only valid in the thread in which they are created.
+ // > The native code must not pass local references from one thread to another.
+ //
+ // So we have to create a global ref and pass it to the AppKit thread.
+ const jobject jAccessibleGlobalRef = (jAccessible == NULL) ? NULL : (*env)->NewGlobalRef(env, jAccessible);
+
+ [ThreadUtilities performOnMainThreadWaiting:YES block:^{
+ nativeAnnounceAppKit(jAccessibleGlobalRef, text, javaPriority);
+ }];
+
+ if (jAccessibleGlobalRef != NULL) {
+ (*env)->DeleteGlobalRef(env, jAccessibleGlobalRef);
+ }
+
+ JNI_COCOA_EXIT(env);
+}
+
+void nativeAnnounceAppKit(
+ const jobject jAccessible,
+ NSString * const text,
+ NSNumber * const javaPriority
+) {
+ AWT_ASSERT_APPKIT_THREAD;
+ assert((text != NULL));
+ assert((javaPriority != NULL));
+
+ JNIEnv* const env = [ThreadUtilities getJNIEnv];
+ if (env == NULL) { // unlikely
+ NSLog(@"%s: failed to get JNIEnv instance\n%@\n", __func__, [NSThread callStackSymbols]);
+ return; // I believe it's dangerous to go on announcing in that case
+ }
+
+ id caller = nil;
+
+ // The meaning of this block is the following:
+ // if jAccessible is an instance of "javax/accessibility/Accessible"
+ // and sun.lwawt.macosx.CAccessible#getCAccessible(jAccessible) returns a non-null object
+ // then we obtain its private field sun.lwawt.macosx.CAccessible#ptr
+ // (which is a pointer to a native "part" of the accessible component)
+ // and assign it to caller
+ if (jAccessible != NULL) {
+ DECLARE_CLASS(jc_Accessible, "javax/accessibility/Accessible");
+
+ if ((*env)->IsInstanceOf(env, jAccessible, jc_Accessible) == JNI_TRUE) {
+ const jobject jCAX = [CommonComponentAccessibility getCAccessible:jAccessible withEnv:env];
+
+ if (jCAX != NULL) {
+ GET_CACCESSIBLE_CLASS();
+ DECLARE_FIELD(jf_ptr, sjc_CAccessible, "ptr", "J");
+
+ caller = (CommonComponentAccessibility*)jlong_to_ptr((*env)->GetLongField(env, jCAX, jf_ptr));
+
+ (*env)->DeleteLocalRef(env, jCAX);
+ }
+ }
+ }
+
+ if (caller == nil) {
+ caller = [NSApp accessibilityFocusedUIElement];
+ }
+
+ [CommonComponentAccessibility postAnnounceWithCaller:caller andText:text andPriority:javaPriority];
+}
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/MenuAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/MenuAccessibility.m
index e8b28d930685..483c9a16752d 100644
--- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/MenuAccessibility.m
+++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/MenuAccessibility.m
@@ -43,7 +43,7 @@ - (NSAccessibilityRole _Nonnull)accessibilityRole
- (BOOL)isAccessibilityElement
{
- return YES;
+ return [[[self accessibilityParent] accessibilityRole] isEqualToString:NSAccessibilityComboBoxRole];
}
- (id _Nullable)accessibilityValue
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/NavigableTextAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/NavigableTextAccessibility.m
index 138d502f10fb..288efb5a39e6 100644
--- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/NavigableTextAccessibility.m
+++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/NavigableTextAccessibility.m
@@ -306,6 +306,49 @@ - (id)accessibilityParent
return [super accessibilityParent];
}
+
+/*
+ * Class: sun_lwawt_macosx_CAccessible
+ * Method: updateZoomCaretFocus
+ * Signature: (I)V
+ */
+JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CAccessible_updateZoomCaretFocus
+ (JNIEnv *env, jclass jklass, jlong element)
+{
+ if (!UAZoomEnabled()) return;
+
+ JNI_COCOA_ENTER(env);
+ [ThreadUtilities performOnMainThread:@selector(postZoomChangeCaretFocus)
+ on:(NavigableTextAccessibility *) jlong_to_ptr(element)
+ withObject:nil
+ waitUntilDone:NO];
+ JNI_COCOA_EXIT(env);
+}
+
+- (void)postZoomChangeCaretFocus
+{
+ AWT_ASSERT_APPKIT_THREAD;
+
+ if (![self isEqual:[NSApp accessibilityFocusedUIElement]]) return;
+
+ JNIEnv *env = [ThreadUtilities getJNIEnv];
+
+ GET_CACCESSIBLETEXT_CLASS();
+ DECLARE_STATIC_METHOD(jm_getCaretRectangle, sjc_CAccessibleText, "getCaretRectangle",
+ "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)[D");
+ jdoubleArray axCaretBounds = (jdoubleArray) (*env)->CallStaticObjectMethod(env, sjc_CAccessibleText,
+ jm_getCaretRectangle, fAccessible,
+ fComponent);
+ CHECK_EXCEPTION();
+ if (axCaretBounds == NULL) return;
+
+ jdouble *values = (*env)->GetDoubleArrayElements(env, axCaretBounds, NULL);
+ CGRect caretRect = CGRectMake(values[0], values[1], values[2], values[3]);
+ (*env)->ReleaseDoubleArrayElements(env, axCaretBounds, values, JNI_ABORT);
+
+ UAZoomChangeFocus(&caretRect, &caretRect, kUAZoomFocusTypeInsertionPoint);
+}
+
/*
* Other text methods
- (NSRange)accessibilitySharedCharacterRange;
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/StaticTextAccessibility.m b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/StaticTextAccessibility.m
index b54a32be5eba..007744f5ecdd 100644
--- a/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/StaticTextAccessibility.m
+++ b/src/java.desktop/macosx/native/libawt_lwawt/awt/a11y/StaticTextAccessibility.m
@@ -29,6 +29,9 @@ @implementation StaticTextAccessibility
- (NSAccessibilityRole _Nonnull)accessibilityRole
{
+ if ([fJavaRole isEqualToString:@"hyperlink"]) {
+ return NSAccessibilityLinkRole;
+ }
return NSAccessibilityStaticTextRole;
}
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/font/AWTFont.h b/src/java.desktop/macosx/native/libawt_lwawt/font/AWTFont.h
index 3c1ca208c70b..6130e3891601 100644
--- a/src/java.desktop/macosx/native/libawt_lwawt/font/AWTFont.h
+++ b/src/java.desktop/macosx/native/libawt_lwawt/font/AWTFont.h
@@ -32,6 +32,7 @@
@public
NSFont *fFont;
CGFontRef fNativeCGFont;
+ NSFont *fFallbackBase; // used for system fonts
BOOL fIsFakeItalic;
}
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/font/AWTFont.m b/src/java.desktop/macosx/native/libawt_lwawt/font/AWTFont.m
index 5e1d6ba0cb1c..4e8044d9bf2d 100644
--- a/src/java.desktop/macosx/native/libawt_lwawt/font/AWTFont.m
+++ b/src/java.desktop/macosx/native/libawt_lwawt/font/AWTFont.m
@@ -36,16 +36,20 @@
@implementation AWTFont
-- (id) initWithFont:(NSFont *)font {
+- (id) initWithFont:(NSFont *)font fallbackBase:(NSFont *)fallbackBaseFont {
self = [super init];
if (self) {
fFont = [font retain];
fNativeCGFont = CTFontCopyGraphicsFont((CTFontRef)font, NULL);
+ fFallbackBase = [fallbackBaseFont retain];
}
return self;
}
- (void) dealloc {
+ [fFallbackBase release];
+ fFallbackBase = nil;
+
[fFont release];
fFont = nil;
@@ -73,13 +77,16 @@ + (AWTFont *) awtFontForName:(NSString *)name
{
// create font with family & size
NSFont *nsFont = nil;
+ NSFont *nsFallbackBase = nil;
if ((uiName != nil && [name isEqualTo:uiName]) ||
(uiBoldName != nil && [name isEqualTo:uiBoldName])) {
if (style & java_awt_Font_BOLD) {
nsFont = [NSFont boldSystemFontOfSize:1.0];
+ nsFallbackBase = [NSFont fontWithName:@"LucidaGrande-Bold" size:1.0];
} else {
nsFont = [NSFont systemFontOfSize:1.0];
+ nsFallbackBase = [NSFont fontWithName:@"LucidaGrande" size:1.0];
}
#ifdef DEBUG
NSLog(@"nsFont-name is : %@", nsFont.familyName);
@@ -110,7 +117,7 @@ + (AWTFont *) awtFontForName:(NSString *)name
nsFont = [[NSFontManager sharedFontManager] convertFont:nsFont toHaveTrait:NSBoldFontMask];
}
- return [[[AWTFont alloc] initWithFont:nsFont] autorelease];
+ return [[[AWTFont alloc] initWithFont:nsFont fallbackBase:nsFallbackBase] autorelease];
}
+ (NSFont *) nsFontForJavaFont:(jobject)javaFont env:(JNIEnv *)env {
@@ -177,6 +184,7 @@ + (NSFont *) nsFontForJavaFont:(jobject)javaFont env:(JNIEnv *)env {
static NSArray* sFilteredFonts = nil;
static NSDictionary* sFontFamilyTable = nil;
+static NSDictionary* sFontFaceTable = nil;
static NSString*
GetFamilyNameForFontName(NSString* fontname)
@@ -184,9 +192,16 @@ + (NSFont *) nsFontForJavaFont:(jobject)javaFont env:(JNIEnv *)env {
return [sFontFamilyTable objectForKey:fontname];
}
+static NSString*
+GetFaceForFontName(NSString* fontname)
+{
+ return [sFontFaceTable objectForKey:fontname];
+}
+
static void addFont(CTFontUIFontType uiType,
NSMutableArray *allFonts,
- NSMutableDictionary* fontFamilyTable) {
+ NSMutableDictionary* fontFamilyTable,
+ NSMutableDictionary* fontFacesTable) {
CTFontRef font = CTFontCreateUIFontForLanguage(uiType, 0.0, NULL);
if (font == NULL) {
@@ -210,6 +225,7 @@ static void addFont(CTFontUIFontType uiType,
CFRelease(font);
return;
}
+ CFStringRef face = CTFontDescriptorCopyAttribute(desc, kCTFontStyleNameAttribute);
if (uiType == kCTFontUIFontSystem) {
uiName = (NSString*)name;
}
@@ -218,61 +234,2888 @@ static void addFont(CTFontUIFontType uiType,
}
[allFonts addObject:name];
[fontFamilyTable setObject:family forKey:name];
+ if (face) {
+ [fontFacesTable setObject:face forKey:name];
+ }
#ifdef DEBUG
NSLog(@"name is : %@", (NSString*)name);
NSLog(@"family is : %@", (NSString*)family);
+ NSLog(@"face is : %@", (NSString*)face);
#endif
+ if (face) {
+ CFRelease(face);
+ }
CFRelease(family);
CFRelease(name);
CFRelease(desc);
CFRelease(font);
}
+static NSDictionary* prebuiltFamilyNames() {
+ return @{
+ @"..SFCompactDisplay-Regular" : @".SF Compact Display",
+ @"..SFCompactRounded-Regular" : @".SF Compact Rounded",
+ @"..SFCompactText-Italic" : @".SF Compact Text",
+ @"..SFCompactText-Regular" : @".SF Compact Text",
+ @".AlBayanPUA" : @".Al Bayan PUA",
+ @".AlBayanPUA-Bold" : @".Al Bayan PUA",
+ @".AlNilePUA" : @".Al Nile PUA",
+ @".AlNilePUA-Bold" : @".Al Nile PUA",
+ @".AlTarikhPUA" : @".Al Tarikh PUA",
+ @".AppleColorEmojiUI" : @".Apple Color Emoji UI",
+ @".AppleSDGothicNeoI-Bold" : @".Apple SD Gothic NeoI",
+ @".AppleSDGothicNeoI-ExtraBold" : @".Apple SD Gothic NeoI",
+ @".AppleSDGothicNeoI-Heavy" : @".Apple SD Gothic NeoI",
+ @".AppleSDGothicNeoI-Light" : @".Apple SD Gothic NeoI",
+ @".AppleSDGothicNeoI-Medium" : @".Apple SD Gothic NeoI",
+ @".AppleSDGothicNeoI-Regular" : @".Apple SD Gothic NeoI",
+ @".AppleSDGothicNeoI-SemiBold" : @".Apple SD Gothic NeoI",
+ @".AppleSDGothicNeoI-Thin" : @".Apple SD Gothic NeoI",
+ @".AppleSDGothicNeoI-UltraLight" : @".Apple SD Gothic NeoI",
+ @".ArabicUIDisplay-Black" : @".Arabic UI Display",
+ @".ArabicUIDisplay-Bold" : @".Arabic UI Display",
+ @".ArabicUIDisplay-Heavy" : @".Arabic UI Display",
+ @".ArabicUIDisplay-Light" : @".Arabic UI Display",
+ @".ArabicUIDisplay-Medium" : @".Arabic UI Display",
+ @".ArabicUIDisplay-Regular" : @".Arabic UI Display",
+ @".ArabicUIDisplay-Semibold" : @".Arabic UI Display",
+ @".ArabicUIDisplay-Thin" : @".Arabic UI Display",
+ @".ArabicUIDisplay-Ultralight" : @".Arabic UI Display",
+ @".ArabicUIText-Bold" : @".Arabic UI Text",
+ @".ArabicUIText-Heavy" : @".Arabic UI Text",
+ @".ArabicUIText-Light" : @".Arabic UI Text",
+ @".ArabicUIText-Medium" : @".Arabic UI Text",
+ @".ArabicUIText-Regular" : @".Arabic UI Text",
+ @".ArabicUIText-Semibold" : @".Arabic UI Text",
+ @".ArialHebrewDeskInterface" : @".Arial Hebrew Desk Interface",
+ @".ArialHebrewDeskInterface-Bold" : @".Arial Hebrew Desk Interface",
+ @".ArialHebrewDeskInterface-Light" : @".Arial Hebrew Desk Interface",
+ @".BaghdadPUA" : @".Baghdad PUA",
+ @".BeirutPUA" : @".Beirut PUA",
+ @".DamascusPUA" : @".Damascus PUA",
+ @".DamascusPUABold" : @".Damascus PUA",
+ @".DamascusPUALight" : @".Damascus PUA",
+ @".DamascusPUAMedium" : @".Damascus PUA",
+ @".DamascusPUASemiBold" : @".Damascus PUA",
+ @".DecoTypeNaskhPUA" : @".DecoType Naskh PUA",
+ @".DiwanKufiPUA" : @".Diwan Kufi PUA",
+ @".FarahPUA" : @".Farah PUA",
+ @".GeezaProInterface" : @".Geeza Pro Interface",
+ @".GeezaProInterface-Bold" : @".Geeza Pro Interface",
+ @".GeezaProInterface-Light" : @".Geeza Pro Interface",
+ @".GeezaProPUA" : @".Geeza Pro PUA",
+ @".GeezaProPUA-Bold" : @".Geeza Pro PUA",
+ @".HelveticaNeueDeskInterface-Bold" : @".Helvetica Neue DeskInterface",
+ @".HelveticaNeueDeskInterface-BoldItalic" : @".Helvetica Neue DeskInterface",
+ @".HelveticaNeueDeskInterface-Heavy" : @".Helvetica Neue DeskInterface",
+ @".HelveticaNeueDeskInterface-Italic" : @".Helvetica Neue DeskInterface",
+ @".HelveticaNeueDeskInterface-Light" : @".Helvetica Neue DeskInterface",
+ @".HelveticaNeueDeskInterface-MediumItalicP4" : @".Helvetica Neue DeskInterface",
+ @".HelveticaNeueDeskInterface-MediumP4" : @".Helvetica Neue DeskInterface",
+ @".HelveticaNeueDeskInterface-Regular" : @".Helvetica Neue DeskInterface",
+ @".HelveticaNeueDeskInterface-Thin" : @".Helvetica Neue DeskInterface",
+ @".HelveticaNeueDeskInterface-UltraLightP2" : @".Helvetica Neue DeskInterface",
+ @".HiraKakuInterface-W0" : @".Hiragino Kaku Gothic Interface",
+ @".HiraKakuInterface-W1" : @".Hiragino Kaku Gothic Interface",
+ @".HiraKakuInterface-W2" : @".Hiragino Kaku Gothic Interface",
+ @".HiraKakuInterface-W3" : @".Hiragino Kaku Gothic Interface",
+ @".HiraKakuInterface-W4" : @".Hiragino Kaku Gothic Interface",
+ @".HiraKakuInterface-W5" : @".Hiragino Kaku Gothic Interface",
+ @".HiraKakuInterface-W6" : @".Hiragino Kaku Gothic Interface",
+ @".HiraKakuInterface-W7" : @".Hiragino Kaku Gothic Interface",
+ @".HiraKakuInterface-W8" : @".Hiragino Kaku Gothic Interface",
+ @".HiraKakuInterface-W9" : @".Hiragino Kaku Gothic Interface",
+ @".HiraginoSansGBInterface-W3" : @".Hiragino Sans GB Interface",
+ @".HiraginoSansGBInterface-W6" : @".Hiragino Sans GB Interface",
+ @".Keyboard" : @".Keyboard",
+ @".KufiStandardGKPUA" : @".KufiStandardGK PUA",
+ @".LucidaGrandeUI" : @".Lucida Grande UI",
+ @".LucidaGrandeUI-Bold" : @".Lucida Grande UI",
+ @".MunaPUA" : @".Muna PUA",
+ @".MunaPUABlack" : @".Muna PUA",
+ @".MunaPUABold" : @".Muna PUA",
+ @".NadeemPUA" : @".Nadeem PUA",
+ @".NewYork-Black" : @".New York",
+ @".NewYork-BlackItalic" : @".New York",
+ @".NewYork-Bold" : @".New York",
+ @".NewYork-BoldG1" : @".New York",
+ @".NewYork-BoldG2" : @".New York",
+ @".NewYork-BoldG3" : @".New York",
+ @".NewYork-BoldG4" : @".New York",
+ @".NewYork-BoldItalic" : @".New York",
+ @".NewYork-BoldItalicG1" : @".New York",
+ @".NewYork-BoldItalicG2" : @".New York",
+ @".NewYork-BoldItalicG3" : @".New York",
+ @".NewYork-BoldItalicG4" : @".New York",
+ @".NewYork-Heavy" : @".New York",
+ @".NewYork-HeavyItalic" : @".New York",
+ @".NewYork-Medium" : @".New York",
+ @".NewYork-MediumItalic" : @".New York",
+ @".NewYork-Regular" : @".New York",
+ @".NewYork-RegularG1" : @".New York",
+ @".NewYork-RegularG2" : @".New York",
+ @".NewYork-RegularG3" : @".New York",
+ @".NewYork-RegularG4" : @".New York",
+ @".NewYork-RegularItalic" : @".New York",
+ @".NewYork-RegularItalicG1" : @".New York",
+ @".NewYork-RegularItalicG2" : @".New York",
+ @".NewYork-RegularItalicG3" : @".New York",
+ @".NewYork-RegularItalicG4" : @".New York",
+ @".NewYork-Semibold" : @".New York",
+ @".NewYork-SemiboldItalic" : @".New York",
+ @".SFArabic-Black" : @".SF Arabic",
+ @".SFArabic-Bold" : @".SF Arabic",
+ @".SFArabic-Heavy" : @".SF Arabic",
+ @".SFArabic-Light" : @".SF Arabic",
+ @".SFArabic-Medium" : @".SF Arabic",
+ @".SFArabic-Regular" : @".SF Arabic",
+ @".SFArabic-Semibold" : @".SF Arabic",
+ @".SFArabic-Thin" : @".SF Arabic",
+ @".SFArabic-Ultralight" : @".SF Arabic",
+ @".NotoNastaliqUrduUI" : @".Noto Nastaliq Urdu UI",
+ @".NotoNastaliqUrduUI-Bold" : @".Noto Nastaliq Urdu UI",
+ @".PingFangHK-Light" : @".PingFang HK",
+ @".PingFangHK-Medium" : @".PingFang HK",
+ @".PingFangHK-Regular" : @".PingFang HK",
+ @".PingFangHK-Semibold" : @".PingFang HK",
+ @".PingFangHK-Thin" : @".PingFang HK",
+ @".PingFangHK-Ultralight" : @".PingFang HK",
+ @".PingFangSC-Light" : @".PingFang SC",
+ @".PingFangSC-Medium" : @".PingFang SC",
+ @".PingFangSC-Regular" : @".PingFang SC",
+ @".PingFangSC-Semibold" : @".PingFang SC",
+ @".PingFangSC-Thin" : @".PingFang SC",
+ @".PingFangSC-Ultralight" : @".PingFang SC",
+ @".PingFangTC-Light" : @".PingFang TC",
+ @".PingFangTC-Medium" : @".PingFang TC",
+ @".PingFangTC-Regular" : @".PingFang TC",
+ @".PingFangTC-Semibold" : @".PingFang TC",
+ @".PingFangTC-Thin" : @".PingFang TC",
+ @".PingFangTC-Ultralight" : @".PingFang TC",
+ @".SFCompactDisplay-Black" : @".SF Compact Display",
+ @".SFCompactDisplay-Bold" : @".SF Compact Display",
+ @".SFCompactDisplay-Heavy" : @".SF Compact Display",
+ @".SFCompactDisplay-Light" : @".SF Compact Display",
+ @".SFCompactDisplay-Medium" : @".SF Compact Display",
+ @".SFCompactDisplay-Regular" : @".SF Compact Display",
+ @".SFCompactDisplay-Semibold" : @".SF Compact Display",
+ @".SFCompactDisplay-Thin" : @".SF Compact Display",
+ @".SFCompactDisplay-Ultralight" : @".SF Compact Display",
+ @".SFCompactRounded-Black" : @".SF Compact Rounded",
+ @".SFCompactRounded-Bold" : @".SF Compact Rounded",
+ @".SFCompactRounded-Heavy" : @".SF Compact Rounded",
+ @".SFCompactRounded-Light" : @".SF Compact Rounded",
+ @".SFCompactRounded-Medium" : @".SF Compact Rounded",
+ @".SFCompactRounded-Regular" : @".SF Compact Rounded",
+ @".SFCompactRounded-Semibold" : @".SF Compact Rounded",
+ @".SFCompactRounded-Thin" : @".SF Compact Rounded",
+ @".SFCompactRounded-Ultralight" : @".SF Compact Rounded",
+ @".SFCompactText-Bold" : @".SF Compact Text",
+ @".SFCompactText-BoldG1" : @".SF Compact Text",
+ @".SFCompactText-BoldG2" : @".SF Compact Text",
+ @".SFCompactText-BoldG3" : @".SF Compact Text",
+ @".SFCompactText-BoldItalic" : @".SF Compact Text",
+ @".SFCompactText-BoldItalicG1" : @".SF Compact Text",
+ @".SFCompactText-BoldItalicG2" : @".SF Compact Text",
+ @".SFCompactText-BoldItalicG3" : @".SF Compact Text",
+ @".SFCompactText-Heavy" : @".SF Compact Text",
+ @".SFCompactText-HeavyItalic" : @".SF Compact Text",
+ @".SFCompactText-Italic" : @".SF Compact Text",
+ @".SFCompactText-Light" : @".SF Compact Text",
+ @".SFCompactText-LightItalic" : @".SF Compact Text",
+ @".SFCompactText-Medium" : @".SF Compact Text",
+ @".SFCompactText-MediumItalic" : @".SF Compact Text",
+ @".SFCompactText-Regular" : @".SF Compact Text",
+ @".SFCompactText-RegularG1" : @".SF Compact Text",
+ @".SFCompactText-RegularG2" : @".SF Compact Text",
+ @".SFCompactText-RegularG3" : @".SF Compact Text",
+ @".SFCompactText-RegularItalic" : @".SF Compact Text",
+ @".SFCompactText-RegularItalicG1" : @".SF Compact Text",
+ @".SFCompactText-RegularItalicG2" : @".SF Compact Text",
+ @".SFCompactText-RegularItalicG3" : @".SF Compact Text",
+ @".SFCompactText-Semibold" : @".SF Compact Text",
+ @".SFCompactText-SemiboldItalic" : @".SF Compact Text",
+ @".SFCompactText-Thin" : @".SF Compact Text",
+ @".SFCompactText-ThinItalic" : @".SF Compact Text",
+ @".SFCompactText-Ultralight" : @".SF Compact Text",
+ @".SFCompactText-UltralightItalic" : @".SF Compact Text",
+ @".SFNSDisplay" : @".SF NS Display",
+ @".SFNSDisplay-Black" : @".SF NS Display",
+ @".SFNSDisplay-BlackItalic" : @".SF NS Display",
+ @".SFNSDisplay-Bold" : @".SF NS Display",
+ @".SFNSDisplay-BoldItalic" : @".SF NS Display",
+ @".SFNSDisplay-Heavy" : @".SF NS Display",
+ @".SFNSDisplay-HeavyItalic" : @".SF NS Display",
+ @".SFNSDisplay-Italic" : @".SF NS Display",
+ @".SFNSDisplay-Light" : @".SF NS Display",
+ @".SFNSDisplay-LightItalic" : @".SF NS Display",
+ @".SFNSDisplay-Medium" : @".SF NS Display",
+ @".SFNSDisplay-MediumItalic" : @".SF NS Display",
+ @".SFNSDisplay-Semibold" : @".SF NS Display",
+ @".SFNSDisplay-SemiboldItalic" : @".SF NS Display",
+ @".SFNSDisplay-Thin" : @".SF NS Display",
+ @".SFNSDisplay-ThinG1" : @".SF NS Display",
+ @".SFNSDisplay-ThinG2" : @".SF NS Display",
+ @".SFNSDisplay-ThinG3" : @".SF NS Display",
+ @".SFNSDisplay-ThinG4" : @".SF NS Display",
+ @".SFNSDisplay-ThinItalic" : @".SF NS Display",
+ @".SFNSDisplay-Ultralight" : @".SF NS Display",
+ @".SFNSDisplay-UltralightItalic" : @".SF NS Display",
+
+ @".SFNS-Black" : @".SF NS",
+ @".SFNS-BlackItalic" : @".SF NS",
+ @".SFNS-Bold" : @".SF NS",
+ @".SFNS-BoldG1" : @".SF NS",
+ @".SFNS-BoldG2" : @".SF NS",
+ @".SFNS-BoldG3" : @".SF NS",
+ @".SFNS-BoldG4" : @".SF NS",
+ @".SFNS-BoldItalic" : @".SF NS",
+ @".SFNS-Heavy" : @".SF NS",
+ @".SFNS-HeavyG1" : @".SF NS",
+ @".SFNS-HeavyG2" : @".SF NS",
+ @".SFNS-HeavyG3" : @".SF NS",
+ @".SFNS-HeavyG4" : @".SF NS",
+ @".SFNS-HeavyItalic" : @".SF NS",
+ @".SFNS-Light" : @".SF NS",
+ @".SFNS-LightG1" : @".SF NS",
+ @".SFNS-LightG2" : @".SF NS",
+ @".SFNS-LightG3" : @".SF NS",
+ @".SFNS-LightG4" : @".SF NS",
+ @".SFNS-LightItalic" : @".SF NS",
+ @".SFNS-Medium" : @".SF NS",
+ @".SFNS-MediumG1" : @".SF NS",
+ @".SFNS-MediumG2" : @".SF NS",
+ @".SFNS-MediumG3" : @".SF NS",
+ @".SFNS-MediumG4" : @".SF NS",
+ @".SFNS-MediumItalic" : @".SF NS",
+ @".SFNS-Regular" : @".SF NS",
+ @".SFNS-RegularG1" : @".SF NS",
+ @".SFNS-RegularG2" : @".SF NS",
+ @".SFNS-RegularG3" : @".SF NS",
+ @".SFNS-RegularG4" : @".SF NS",
+ @".SFNS-RegularItalic" : @".SF NS",
+ @".SFNS-Semibold" : @".SF NS",
+ @".SFNS-SemiboldG1" : @".SF NS",
+ @".SFNS-SemiboldG2" : @".SF NS",
+ @".SFNS-SemiboldG3" : @".SF NS",
+ @".SFNS-SemiboldG4" : @".SF NS",
+ @".SFNS-SemiboldItalic" : @".SF NS",
+ @".SFNS-Thin" : @".SF NS",
+ @".SFNS-ThinG1" : @".SF NS",
+ @".SFNS-ThinG2" : @".SF NS",
+ @".SFNS-ThinG3" : @".SF NS",
+ @".SFNS-ThinG4" : @".SF NS",
+ @".SFNS-ThinItalic" : @".SF NS",
+ @".SFNS-Ultralight" : @".SF NS",
+ @".SFNS-UltralightG1" : @".SF NS",
+ @".SFNS-UltralightG2" : @".SF NS",
+ @".SFNS-UltralightG3" : @".SF NS",
+ @".SFNS-UltralightG4" : @".SF NS",
+ @".SFNS-UltralightItalic" : @".SF NS",
+ @".SFNS-Ultrathin" : @".SF NS",
+ @".SFNS-UltrathinG1" : @".SF NS",
+ @".SFNS-UltrathinG2" : @".SF NS",
+ @".SFNS-UltrathinG3" : @".SF NS",
+ @".SFNS-UltrathinG4" : @".SF NS",
+ @".SFNS-UltrathinItalic" : @".SF NS",
+ @".SFNSDisplayCondensed-Black" : @".SF NS Display Condensed",
+ @".SFNSDisplayCondensed-Bold" : @".SF NS Display Condensed",
+ @".SFNSDisplayCondensed-Heavy" : @".SF NS Display Condensed",
+ @".SFNSDisplayCondensed-Light" : @".SF NS Display Condensed",
+ @".SFNSDisplayCondensed-Medium" : @".SF NS Display Condensed",
+ @".SFNSDisplayCondensed-Regular" : @".SF NS Display Condensed",
+ @".SFNSDisplayCondensed-Semibold" : @".SF NS Display Condensed",
+ @".SFNSDisplayCondensed-Thin" : @".SF NS Display Condensed",
+ @".SFNSDisplayCondensed-Ultralight" : @".SF NS Display Condensed",
+ @".SFNSMono-Bold" : @".SF NS Mono",
+ @".SFNSMono-BoldItalic" : @".SF NS Mono",
+ @".SFNSMono-Heavy" : @".SF NS Mono",
+ @".SFNSMono-HeavyItalic" : @".SF NS Mono",
+ @".SFNSMono-Light" : @".SF NS Mono",
+ @".SFNSMono-LightItalic" : @".SF NS Mono",
+ @".SFNSMono-Medium" : @".SF NS Mono",
+ @".SFNSMono-MediumItalic" : @".SF NS Mono",
+ @".SFNSMono-Regular" : @".SF NS Mono",
+ @".SFNSMono-RegularItalic" : @".SF NS Mono",
+ @".SFNSMono-Semibold" : @".SF NS Mono",
+ @".SFNSMono-SemiboldItalic" : @".SF NS Mono",
+ @".SFNSRounded-Black" : @".SF NS Rounded",
+ @".SFNSRounded-Bold" : @".SF NS Rounded",
+ @".SFNSRounded-BoldG1" : @".SF NS Rounded",
+ @".SFNSRounded-BoldG2" : @".SF NS Rounded",
+ @".SFNSRounded-BoldG3" : @".SF NS Rounded",
+ @".SFNSRounded-BoldG4" : @".SF NS Rounded",
+ @".SFNSRounded-Heavy" : @".SF NS Rounded",
+ @".SFNSRounded-HeavyG1" : @".SF NS Rounded",
+ @".SFNSRounded-HeavyG2" : @".SF NS Rounded",
+ @".SFNSRounded-HeavyG3" : @".SF NS Rounded",
+ @".SFNSRounded-HeavyG4" : @".SF NS Rounded",
+ @".SFNSRounded-Light" : @".SF NS Rounded",
+ @".SFNSRounded-LightG1" : @".SF NS Rounded",
+ @".SFNSRounded-LightG2" : @".SF NS Rounded",
+ @".SFNSRounded-LightG3" : @".SF NS Rounded",
+ @".SFNSRounded-LightG4" : @".SF NS Rounded",
+ @".SFNSRounded-Medium" : @".SF NS Rounded",
+ @".SFNSRounded-MediumG1" : @".SF NS Rounded",
+ @".SFNSRounded-MediumG2" : @".SF NS Rounded",
+ @".SFNSRounded-MediumG3" : @".SF NS Rounded",
+ @".SFNSRounded-MediumG4" : @".SF NS Rounded",
+ @".SFNSRounded-Regular" : @".SF NS Rounded",
+ @".SFNSRounded-RegularG1" : @".SF NS Rounded",
+ @".SFNSRounded-RegularG2" : @".SF NS Rounded",
+ @".SFNSRounded-RegularG3" : @".SF NS Rounded",
+ @".SFNSRounded-RegularG4" : @".SF NS Rounded",
+ @".SFNSRounded-Semibold" : @".SF NS Rounded",
+ @".SFNSRounded-SemiboldG1" : @".SF NS Rounded",
+ @".SFNSRounded-SemiboldG2" : @".SF NS Rounded",
+ @".SFNSRounded-SemiboldG3" : @".SF NS Rounded",
+ @".SFNSRounded-SemiboldG4" : @".SF NS Rounded",
+ @".SFNSRounded-Thin" : @".SF NS Rounded",
+ @".SFNSRounded-ThinG1" : @".SF NS Rounded",
+ @".SFNSRounded-ThinG2" : @".SF NS Rounded",
+ @".SFNSRounded-ThinG3" : @".SF NS Rounded",
+ @".SFNSRounded-ThinG4" : @".SF NS Rounded",
+ @".SFNSRounded-Ultralight" : @".SF NS Rounded",
+ @".SFNSRounded-UltralightG1" : @".SF NS Rounded",
+ @".SFNSRounded-UltralightG2" : @".SF NS Rounded",
+ @".SFNSRounded-UltralightG3" : @".SF NS Rounded",
+ @".SFNSRounded-UltralightG4" : @".SF NS Rounded",
+ @".SFNSRounded-Ultrathin" : @".SF NS Rounded",
+ @".SFNSRounded-UltrathinG1" : @".SF NS Rounded",
+ @".SFNSRounded-UltrathinG2" : @".SF NS Rounded",
+ @".SFNSRounded-UltrathinG3" : @".SF NS Rounded",
+ @".SFNSRounded-UltrathinG4" : @".SF NS Rounded",
+ @".SFNSSymbols-Black" : @".SF NS Symbols",
+ @".SFNSSymbols-Bold" : @".SF NS Symbols",
+ @".SFNSSymbols-Heavy" : @".SF NS Symbols",
+ @".SFNSSymbols-Light" : @".SF NS Symbols",
+ @".SFNSSymbols-Medium" : @".SF NS Symbols",
+ @".SFNSSymbols-Regular" : @".SF NS Symbols",
+ @".SFNSSymbols-Semibold" : @".SF NS Symbols",
+ @".SFNSSymbols-Thin" : @".SF NS Symbols",
+ @".SFNSSymbols-Ultralight" : @".SF NS Symbols",
+ @".SFNSText" : @".SF NS Text",
+ @".SFNSText-Bold" : @".SF NS Text",
+ @".SFNSText-BoldItalic" : @".SF NS Text",
+ @".SFNSText-Heavy" : @".SF NS Text",
+ @".SFNSText-HeavyItalic" : @".SF NS Text",
+ @".SFNSText-Italic" : @".SF NS Text",
+ @".SFNSText-Light" : @".SF NS Text",
+ @".SFNSText-LightItalic" : @".SF NS Text",
+ @".SFNSText-Medium" : @".SF NS Text",
+ @".SFNSText-MediumItalic" : @".SF NS Text",
+ @".SFNSText-Semibold" : @".SF NS Text",
+ @".SFNSText-SemiboldItalic" : @".SF NS Text",
+ @".SFNSTextCondensed-Bold" : @".SF NS Text Condensed",
+ @".SFNSTextCondensed-Heavy" : @".SF NS Text Condensed",
+ @".SFNSTextCondensed-Light" : @".SF NS Text Condensed",
+ @".SFNSTextCondensed-Medium" : @".SF NS Text Condensed",
+ @".SFNSTextCondensed-Regular" : @".SF NS Text Condensed",
+ @".SFNSTextCondensed-Semibold" : @".SF NS Text Condensed",
+ @".SFCompact-Black" : @".SFCompact",
+ @".SFCompact-BlackItalic" : @".SFCompact",
+ @".SFCompact-Bold" : @".SFCompact",
+ @".SFCompact-BoldG1" : @".SFCompact",
+ @".SFCompact-BoldG2" : @".SFCompact",
+ @".SFCompact-BoldG3" : @".SFCompact",
+ @".SFCompact-BoldG4" : @".SFCompact",
+ @".SFCompact-BoldItalic" : @".SFCompact",
+ @".SFCompact-BoldItalicG1" : @".SFCompact",
+ @".SFCompact-BoldItalicG2" : @".SFCompact",
+ @".SFCompact-BoldItalicG3" : @".SFCompact",
+ @".SFCompact-BoldItalicG4" : @".SFCompact",
+ @".SFCompact-Heavy" : @".SFCompact",
+ @".SFCompact-HeavyG1" : @".SFCompact",
+ @".SFCompact-HeavyG2" : @".SFCompact",
+ @".SFCompact-HeavyG3" : @".SFCompact",
+ @".SFCompact-HeavyG4" : @".SFCompact",
+ @".SFCompact-HeavyItalic" : @".SFCompact",
+ @".SFCompact-HeavyItalicG1" : @".SFCompact",
+ @".SFCompact-HeavyItalicG2" : @".SFCompact",
+ @".SFCompact-HeavyItalicG3" : @".SFCompact",
+ @".SFCompact-HeavyItalicG4" : @".SFCompact",
+ @".SFCompact-Light" : @".SFCompact",
+ @".SFCompact-LightG1" : @".SFCompact",
+ @".SFCompact-LightG2" : @".SFCompact",
+ @".SFCompact-LightG3" : @".SFCompact",
+ @".SFCompact-LightG4" : @".SFCompact",
+ @".SFCompact-LightItalic" : @".SFCompact",
+ @".SFCompact-LightItalicG1" : @".SFCompact",
+ @".SFCompact-LightItalicG2" : @".SFCompact",
+ @".SFCompact-LightItalicG3" : @".SFCompact",
+ @".SFCompact-LightItalicG4" : @".SFCompact",
+ @".SFCompact-Medium" : @".SFCompact",
+ @".SFCompact-MediumG1" : @".SFCompact",
+ @".SFCompact-MediumG2" : @".SFCompact",
+ @".SFCompact-MediumG3" : @".SFCompact",
+ @".SFCompact-MediumG4" : @".SFCompact",
+ @".SFCompact-MediumItalic" : @".SFCompact",
+ @".SFCompact-MediumItalicG1" : @".SFCompact",
+ @".SFCompact-MediumItalicG2" : @".SFCompact",
+ @".SFCompact-MediumItalicG3" : @".SFCompact",
+ @".SFCompact-MediumItalicG4" : @".SFCompact",
+ @".SFCompact-Regular" : @".SFCompact",
+ @".SFCompact-RegularG1" : @".SFCompact",
+ @".SFCompact-RegularG2" : @".SFCompact",
+ @".SFCompact-RegularG3" : @".SFCompact",
+ @".SFCompact-RegularG4" : @".SFCompact",
+ @".SFCompact-RegularItalic" : @".SFCompact",
+ @".SFCompact-RegularItalicG1" : @".SFCompact",
+ @".SFCompact-RegularItalicG2" : @".SFCompact",
+ @".SFCompact-RegularItalicG3" : @".SFCompact",
+ @".SFCompact-RegularItalicG4" : @".SFCompact",
+ @".SFCompact-Semibold" : @".SFCompact",
+ @".SFCompact-SemiboldG1" : @".SFCompact",
+ @".SFCompact-SemiboldG2" : @".SFCompact",
+ @".SFCompact-SemiboldG3" : @".SFCompact",
+ @".SFCompact-SemiboldG4" : @".SFCompact",
+ @".SFCompact-SemiboldItalic" : @".SFCompact",
+ @".SFCompact-SemiboldItalicG1" : @".SFCompact",
+ @".SFCompact-SemiboldItalicG2" : @".SFCompact",
+ @".SFCompact-SemiboldItalicG3" : @".SFCompact",
+ @".SFCompact-SemiboldItalicG4" : @".SFCompact",
+ @".SFCompact-Thin" : @".SFCompact",
+ @".SFCompact-ThinG1" : @".SFCompact",
+ @".SFCompact-ThinG2" : @".SFCompact",
+ @".SFCompact-ThinG3" : @".SFCompact",
+ @".SFCompact-ThinG4" : @".SFCompact",
+ @".SFCompact-ThinItalic" : @".SFCompact",
+ @".SFCompact-ThinItalicG1" : @".SFCompact",
+ @".SFCompact-ThinItalicG2" : @".SFCompact",
+ @".SFCompact-ThinItalicG3" : @".SFCompact",
+ @".SFCompact-ThinItalicG4" : @".SFCompact",
+ @".SFCompact-Ultralight" : @".SFCompact",
+ @".SFCompact-UltralightG1" : @".SFCompact",
+ @".SFCompact-UltralightG2" : @".SFCompact",
+ @".SFCompact-UltralightG3" : @".SFCompact",
+ @".SFCompact-UltralightG4" : @".SFCompact",
+ @".SFCompact-UltralightItalic" : @".SFCompact",
+ @".SFCompact-UltralightItalicG1" : @".SFCompact",
+ @".SFCompact-UltralightItalicG2" : @".SFCompact",
+ @".SFCompact-UltralightItalicG3" : @".SFCompact",
+ @".SFCompact-UltralightItalicG4" : @".SFCompact",
+ @".SFNS-CompressedBlack" : @".SFNS",
+ @".SFNS-CompressedBold" : @".SFNS",
+ @".SFNS-CompressedBoldG1" : @".SFNS",
+ @".SFNS-CompressedBoldG2" : @".SFNS",
+ @".SFNS-CompressedBoldG3" : @".SFNS",
+ @".SFNS-CompressedBoldG4" : @".SFNS",
+ @".SFNS-CompressedHeavy" : @".SFNS",
+ @".SFNS-CompressedHeavyG1" : @".SFNS",
+ @".SFNS-CompressedHeavyG2" : @".SFNS",
+ @".SFNS-CompressedHeavyG3" : @".SFNS",
+ @".SFNS-CompressedHeavyG4" : @".SFNS",
+ @".SFNS-CompressedLight" : @".SFNS",
+ @".SFNS-CompressedLightG1" : @".SFNS",
+ @".SFNS-CompressedLightG2" : @".SFNS",
+ @".SFNS-CompressedLightG3" : @".SFNS",
+ @".SFNS-CompressedLightG4" : @".SFNS",
+ @".SFNS-CompressedMedium" : @".SFNS",
+ @".SFNS-CompressedMediumG1" : @".SFNS",
+ @".SFNS-CompressedMediumG2" : @".SFNS",
+ @".SFNS-CompressedMediumG3" : @".SFNS",
+ @".SFNS-CompressedMediumG4" : @".SFNS",
+ @".SFNS-CompressedRegular" : @".SFNS",
+ @".SFNS-CompressedRegularG1" : @".SFNS",
+ @".SFNS-CompressedRegularG2" : @".SFNS",
+ @".SFNS-CompressedRegularG3" : @".SFNS",
+ @".SFNS-CompressedRegularG4" : @".SFNS",
+ @".SFNS-CompressedSemibold" : @".SFNS",
+ @".SFNS-CompressedSemiboldG1" : @".SFNS",
+ @".SFNS-CompressedSemiboldG2" : @".SFNS",
+ @".SFNS-CompressedSemiboldG3" : @".SFNS",
+ @".SFNS-CompressedSemiboldG4" : @".SFNS",
+ @".SFNS-CompressedThin" : @".SFNS",
+ @".SFNS-CompressedThinG1" : @".SFNS",
+ @".SFNS-CompressedThinG2" : @".SFNS",
+ @".SFNS-CompressedThinG3" : @".SFNS",
+ @".SFNS-CompressedThinG4" : @".SFNS",
+ @".SFNS-CompressedUltralight" : @".SFNS",
+ @".SFNS-CompressedUltralightG1" : @".SFNS",
+ @".SFNS-CompressedUltralightG2" : @".SFNS",
+ @".SFNS-CompressedUltralightG3" : @".SFNS",
+ @".SFNS-CompressedUltralightG4" : @".SFNS",
+ @".SFNS-CondensedBlack" : @".SFNS",
+ @".SFNS-CondensedBold" : @".SFNS",
+ @".SFNS-CondensedBoldG1" : @".SFNS",
+ @".SFNS-CondensedBoldG2" : @".SFNS",
+ @".SFNS-CondensedBoldG3" : @".SFNS",
+ @".SFNS-CondensedBoldG4" : @".SFNS",
+ @".SFNS-CondensedHeavy" : @".SFNS",
+ @".SFNS-CondensedHeavyG1" : @".SFNS",
+ @".SFNS-CondensedHeavyG2" : @".SFNS",
+ @".SFNS-CondensedHeavyG3" : @".SFNS",
+ @".SFNS-CondensedHeavyG4" : @".SFNS",
+ @".SFNS-CondensedLight" : @".SFNS",
+ @".SFNS-CondensedLightG1" : @".SFNS",
+ @".SFNS-CondensedLightG2" : @".SFNS",
+ @".SFNS-CondensedLightG3" : @".SFNS",
+ @".SFNS-CondensedLightG4" : @".SFNS",
+ @".SFNS-CondensedMedium" : @".SFNS",
+ @".SFNS-CondensedMediumG1" : @".SFNS",
+ @".SFNS-CondensedMediumG2" : @".SFNS",
+ @".SFNS-CondensedMediumG3" : @".SFNS",
+ @".SFNS-CondensedMediumG4" : @".SFNS",
+ @".SFNS-CondensedRegular" : @".SFNS",
+ @".SFNS-CondensedRegularG1" : @".SFNS",
+ @".SFNS-CondensedRegularG2" : @".SFNS",
+ @".SFNS-CondensedRegularG3" : @".SFNS",
+ @".SFNS-CondensedRegularG4" : @".SFNS",
+ @".SFNS-CondensedSemibold" : @".SFNS",
+ @".SFNS-CondensedSemiboldG1" : @".SFNS",
+ @".SFNS-CondensedSemiboldG2" : @".SFNS",
+ @".SFNS-CondensedSemiboldG3" : @".SFNS",
+ @".SFNS-CondensedSemiboldG4" : @".SFNS",
+ @".SFNS-CondensedThin" : @".SFNS",
+ @".SFNS-CondensedThinG1" : @".SFNS",
+ @".SFNS-CondensedThinG2" : @".SFNS",
+ @".SFNS-CondensedThinG3" : @".SFNS",
+ @".SFNS-CondensedThinG4" : @".SFNS",
+ @".SFNS-CondensedUltralight" : @".SFNS",
+ @".SFNS-CondensedUltralightG1" : @".SFNS",
+ @".SFNS-CondensedUltralightG2" : @".SFNS",
+ @".SFNS-CondensedUltralightG3" : @".SFNS",
+ @".SFNS-CondensedUltralightG4" : @".SFNS",
+ @".SFNS-ExpandedBlack" : @".SFNS",
+ @".SFNS-ExpandedBold" : @".SFNS",
+ @".SFNS-ExpandedBoldG1" : @".SFNS",
+ @".SFNS-ExpandedBoldG2" : @".SFNS",
+ @".SFNS-ExpandedBoldG3" : @".SFNS",
+ @".SFNS-ExpandedBoldG4" : @".SFNS",
+ @".SFNS-ExpandedHeavy" : @".SFNS",
+ @".SFNS-ExpandedHeavyG1" : @".SFNS",
+ @".SFNS-ExpandedHeavyG2" : @".SFNS",
+ @".SFNS-ExpandedHeavyG3" : @".SFNS",
+ @".SFNS-ExpandedHeavyG4" : @".SFNS",
+ @".SFNS-ExpandedLight" : @".SFNS",
+ @".SFNS-ExpandedLightG1" : @".SFNS",
+ @".SFNS-ExpandedLightG2" : @".SFNS",
+ @".SFNS-ExpandedLightG3" : @".SFNS",
+ @".SFNS-ExpandedLightG4" : @".SFNS",
+ @".SFNS-ExpandedMedium" : @".SFNS",
+ @".SFNS-ExpandedMediumG1" : @".SFNS",
+ @".SFNS-ExpandedMediumG2" : @".SFNS",
+ @".SFNS-ExpandedMediumG3" : @".SFNS",
+ @".SFNS-ExpandedMediumG4" : @".SFNS",
+ @".SFNS-ExpandedRegular" : @".SFNS",
+ @".SFNS-ExpandedRegularG1" : @".SFNS",
+ @".SFNS-ExpandedRegularG2" : @".SFNS",
+ @".SFNS-ExpandedRegularG3" : @".SFNS",
+ @".SFNS-ExpandedRegularG4" : @".SFNS",
+ @".SFNS-ExpandedSemibold" : @".SFNS",
+ @".SFNS-ExpandedSemiboldG1" : @".SFNS",
+ @".SFNS-ExpandedSemiboldG2" : @".SFNS",
+ @".SFNS-ExpandedSemiboldG3" : @".SFNS",
+ @".SFNS-ExpandedSemiboldG4" : @".SFNS",
+ @".SFNS-ExpandedThin" : @".SFNS",
+ @".SFNS-ExpandedThinG1" : @".SFNS",
+ @".SFNS-ExpandedThinG2" : @".SFNS",
+ @".SFNS-ExpandedThinG3" : @".SFNS",
+ @".SFNS-ExpandedThinG4" : @".SFNS",
+ @".SFNS-ExpandedUltralight" : @".SFNS",
+ @".SFNS-ExpandedUltralightG1" : @".SFNS",
+ @".SFNS-ExpandedUltralightG2" : @".SFNS",
+ @".SFNS-ExpandedUltralightG3" : @".SFNS",
+ @".SFNS-ExpandedUltralightG4" : @".SFNS",
+ @".SFNS-ExtraCompressedBlack" : @".SFNS",
+ @".SFNS-ExtraCompressedBold" : @".SFNS",
+ @".SFNS-ExtraCompressedBoldG1" : @".SFNS",
+ @".SFNS-ExtraCompressedBoldG2" : @".SFNS",
+ @".SFNS-ExtraCompressedBoldG3" : @".SFNS",
+ @".SFNS-ExtraCompressedBoldG4" : @".SFNS",
+ @".SFNS-ExtraCompressedHeavy" : @".SFNS",
+ @".SFNS-ExtraCompressedHeavyG1" : @".SFNS",
+ @".SFNS-ExtraCompressedHeavyG2" : @".SFNS",
+ @".SFNS-ExtraCompressedHeavyG3" : @".SFNS",
+ @".SFNS-ExtraCompressedHeavyG4" : @".SFNS",
+ @".SFNS-ExtraCompressedLight" : @".SFNS",
+ @".SFNS-ExtraCompressedLightG1" : @".SFNS",
+ @".SFNS-ExtraCompressedLightG2" : @".SFNS",
+ @".SFNS-ExtraCompressedLightG3" : @".SFNS",
+ @".SFNS-ExtraCompressedLightG4" : @".SFNS",
+ @".SFNS-ExtraCompressedMedium" : @".SFNS",
+ @".SFNS-ExtraCompressedMediumG1" : @".SFNS",
+ @".SFNS-ExtraCompressedMediumG2" : @".SFNS",
+ @".SFNS-ExtraCompressedMediumG3" : @".SFNS",
+ @".SFNS-ExtraCompressedMediumG4" : @".SFNS",
+ @".SFNS-ExtraCompressedRegular" : @".SFNS",
+ @".SFNS-ExtraCompressedRegularG1" : @".SFNS",
+ @".SFNS-ExtraCompressedRegularG2" : @".SFNS",
+ @".SFNS-ExtraCompressedRegularG3" : @".SFNS",
+ @".SFNS-ExtraCompressedRegularG4" : @".SFNS",
+ @".SFNS-ExtraCompressedSemibold" : @".SFNS",
+ @".SFNS-ExtraCompressedSemiboldG1" : @".SFNS",
+ @".SFNS-ExtraCompressedSemiboldG2" : @".SFNS",
+ @".SFNS-ExtraCompressedSemiboldG3" : @".SFNS",
+ @".SFNS-ExtraCompressedSemiboldG4" : @".SFNS",
+ @".SFNS-ExtraCompressedThin" : @".SFNS",
+ @".SFNS-ExtraCompressedThinG1" : @".SFNS",
+ @".SFNS-ExtraCompressedThinG2" : @".SFNS",
+ @".SFNS-ExtraCompressedThinG3" : @".SFNS",
+ @".SFNS-ExtraCompressedThinG4" : @".SFNS",
+ @".SFNS-ExtraCompressedUltralight" : @".SFNS",
+ @".SFNS-ExtraCompressedUltralightG1" : @".SFNS",
+ @".SFNS-ExtraCompressedUltralightG2" : @".SFNS",
+ @".SFNS-ExtraCompressedUltralightG3" : @".SFNS",
+ @".SFNS-ExtraCompressedUltralightG4" : @".SFNS",
+ @".SFNS-ExtraExpandedBlack" : @".SFNS",
+ @".SFNS-ExtraExpandedBold" : @".SFNS",
+ @".SFNS-ExtraExpandedBoldG1" : @".SFNS",
+ @".SFNS-ExtraExpandedBoldG2" : @".SFNS",
+ @".SFNS-ExtraExpandedBoldG3" : @".SFNS",
+ @".SFNS-ExtraExpandedBoldG4" : @".SFNS",
+ @".SFNS-ExtraExpandedHeavy" : @".SFNS",
+ @".SFNS-ExtraExpandedHeavyG1" : @".SFNS",
+ @".SFNS-ExtraExpandedHeavyG2" : @".SFNS",
+ @".SFNS-ExtraExpandedHeavyG3" : @".SFNS",
+ @".SFNS-ExtraExpandedHeavyG4" : @".SFNS",
+ @".SFNS-ExtraExpandedLight" : @".SFNS",
+ @".SFNS-ExtraExpandedLightG1" : @".SFNS",
+ @".SFNS-ExtraExpandedLightG2" : @".SFNS",
+ @".SFNS-ExtraExpandedLightG3" : @".SFNS",
+ @".SFNS-ExtraExpandedLightG4" : @".SFNS",
+ @".SFNS-ExtraExpandedMedium" : @".SFNS",
+ @".SFNS-ExtraExpandedMediumG1" : @".SFNS",
+ @".SFNS-ExtraExpandedMediumG2" : @".SFNS",
+ @".SFNS-ExtraExpandedMediumG3" : @".SFNS",
+ @".SFNS-ExtraExpandedMediumG4" : @".SFNS",
+ @".SFNS-ExtraExpandedRegular" : @".SFNS",
+ @".SFNS-ExtraExpandedRegularG1" : @".SFNS",
+ @".SFNS-ExtraExpandedRegularG2" : @".SFNS",
+ @".SFNS-ExtraExpandedRegularG3" : @".SFNS",
+ @".SFNS-ExtraExpandedRegularG4" : @".SFNS",
+ @".SFNS-ExtraExpandedSemibold" : @".SFNS",
+ @".SFNS-ExtraExpandedSemiboldG1" : @".SFNS",
+ @".SFNS-ExtraExpandedSemiboldG2" : @".SFNS",
+ @".SFNS-ExtraExpandedSemiboldG3" : @".SFNS",
+ @".SFNS-ExtraExpandedSemiboldG4" : @".SFNS",
+ @".SFNS-ExtraExpandedThin" : @".SFNS",
+ @".SFNS-ExtraExpandedThinG1" : @".SFNS",
+ @".SFNS-ExtraExpandedThinG2" : @".SFNS",
+ @".SFNS-ExtraExpandedThinG3" : @".SFNS",
+ @".SFNS-ExtraExpandedThinG4" : @".SFNS",
+ @".SFNS-ExtraExpandedUltralight" : @".SFNS",
+ @".SFNS-ExtraExpandedUltralightG1" : @".SFNS",
+ @".SFNS-ExtraExpandedUltralightG2" : @".SFNS",
+ @".SFNS-ExtraExpandedUltralightG3" : @".SFNS",
+ @".SFNS-ExtraExpandedUltralightG4" : @".SFNS",
+ @".SFNS-SemiCondensedBlack" : @".SFNS",
+ @".SFNS-SemiCondensedBold" : @".SFNS",
+ @".SFNS-SemiCondensedBoldG1" : @".SFNS",
+ @".SFNS-SemiCondensedBoldG2" : @".SFNS",
+ @".SFNS-SemiCondensedBoldG3" : @".SFNS",
+ @".SFNS-SemiCondensedBoldG4" : @".SFNS",
+ @".SFNS-SemiCondensedHeavy" : @".SFNS",
+ @".SFNS-SemiCondensedHeavyG1" : @".SFNS",
+ @".SFNS-SemiCondensedHeavyG2" : @".SFNS",
+ @".SFNS-SemiCondensedHeavyG3" : @".SFNS",
+ @".SFNS-SemiCondensedHeavyG4" : @".SFNS",
+ @".SFNS-SemiCondensedLight" : @".SFNS",
+ @".SFNS-SemiCondensedLightG1" : @".SFNS",
+ @".SFNS-SemiCondensedLightG2" : @".SFNS",
+ @".SFNS-SemiCondensedLightG3" : @".SFNS",
+ @".SFNS-SemiCondensedLightG4" : @".SFNS",
+ @".SFNS-SemiCondensedMedium" : @".SFNS",
+ @".SFNS-SemiCondensedMediumG1" : @".SFNS",
+ @".SFNS-SemiCondensedMediumG2" : @".SFNS",
+ @".SFNS-SemiCondensedMediumG3" : @".SFNS",
+ @".SFNS-SemiCondensedMediumG4" : @".SFNS",
+ @".SFNS-SemiCondensedRegular" : @".SFNS",
+ @".SFNS-SemiCondensedRegularG1" : @".SFNS",
+ @".SFNS-SemiCondensedRegularG2" : @".SFNS",
+ @".SFNS-SemiCondensedRegularG3" : @".SFNS",
+ @".SFNS-SemiCondensedRegularG4" : @".SFNS",
+ @".SFNS-SemiCondensedSemibold" : @".SFNS",
+ @".SFNS-SemiCondensedSemiboldG1" : @".SFNS",
+ @".SFNS-SemiCondensedSemiboldG2" : @".SFNS",
+ @".SFNS-SemiCondensedSemiboldG3" : @".SFNS",
+ @".SFNS-SemiCondensedSemiboldG4" : @".SFNS",
+ @".SFNS-SemiCondensedThin" : @".SFNS",
+ @".SFNS-SemiCondensedThinG1" : @".SFNS",
+ @".SFNS-SemiCondensedThinG2" : @".SFNS",
+ @".SFNS-SemiCondensedThinG3" : @".SFNS",
+ @".SFNS-SemiCondensedThinG4" : @".SFNS",
+ @".SFNS-SemiCondensedUltralight" : @".SFNS",
+ @".SFNS-SemiCondensedUltralightG1" : @".SFNS",
+ @".SFNS-SemiCondensedUltralightG2" : @".SFNS",
+ @".SFNS-SemiCondensedUltralightG3" : @".SFNS",
+ @".SFNS-SemiCondensedUltralightG4" : @".SFNS",
+ @".SFNS-SemiExpandedBlack" : @".SFNS",
+ @".SFNS-SemiExpandedBold" : @".SFNS",
+ @".SFNS-SemiExpandedBoldG1" : @".SFNS",
+ @".SFNS-SemiExpandedBoldG2" : @".SFNS",
+ @".SFNS-SemiExpandedBoldG3" : @".SFNS",
+ @".SFNS-SemiExpandedBoldG4" : @".SFNS",
+ @".SFNS-SemiExpandedHeavy" : @".SFNS",
+ @".SFNS-SemiExpandedHeavyG1" : @".SFNS",
+ @".SFNS-SemiExpandedHeavyG2" : @".SFNS",
+ @".SFNS-SemiExpandedHeavyG3" : @".SFNS",
+ @".SFNS-SemiExpandedHeavyG4" : @".SFNS",
+ @".SFNS-SemiExpandedLight" : @".SFNS",
+ @".SFNS-SemiExpandedLightG1" : @".SFNS",
+ @".SFNS-SemiExpandedLightG2" : @".SFNS",
+ @".SFNS-SemiExpandedLightG3" : @".SFNS",
+ @".SFNS-SemiExpandedLightG4" : @".SFNS",
+ @".SFNS-SemiExpandedMedium" : @".SFNS",
+ @".SFNS-SemiExpandedMediumG1" : @".SFNS",
+ @".SFNS-SemiExpandedMediumG2" : @".SFNS",
+ @".SFNS-SemiExpandedMediumG3" : @".SFNS",
+ @".SFNS-SemiExpandedMediumG4" : @".SFNS",
+ @".SFNS-SemiExpandedRegular" : @".SFNS",
+ @".SFNS-SemiExpandedRegularG1" : @".SFNS",
+ @".SFNS-SemiExpandedRegularG2" : @".SFNS",
+ @".SFNS-SemiExpandedRegularG3" : @".SFNS",
+ @".SFNS-SemiExpandedRegularG4" : @".SFNS",
+ @".SFNS-SemiExpandedSemibold" : @".SFNS",
+ @".SFNS-SemiExpandedSemiboldG1" : @".SFNS",
+ @".SFNS-SemiExpandedSemiboldG2" : @".SFNS",
+ @".SFNS-SemiExpandedSemiboldG3" : @".SFNS",
+ @".SFNS-SemiExpandedSemiboldG4" : @".SFNS",
+ @".SFNS-SemiExpandedThin" : @".SFNS",
+ @".SFNS-SemiExpandedThinG1" : @".SFNS",
+ @".SFNS-SemiExpandedThinG2" : @".SFNS",
+ @".SFNS-SemiExpandedThinG3" : @".SFNS",
+ @".SFNS-SemiExpandedThinG4" : @".SFNS",
+ @".SFNS-SemiExpandedUltralight" : @".SFNS",
+ @".SFNS-SemiExpandedUltralightG1" : @".SFNS",
+ @".SFNS-SemiExpandedUltralightG2" : @".SFNS",
+ @".SFNS-SemiExpandedUltralightG3" : @".SFNS",
+ @".SFNS-SemiExpandedUltralightG4" : @".SFNS",
+ @".SFNS-UltraCompressedBlack" : @".SFNS",
+ @".SFNS-UltraCompressedBold" : @".SFNS",
+ @".SFNS-UltraCompressedBoldG1" : @".SFNS",
+ @".SFNS-UltraCompressedBoldG2" : @".SFNS",
+ @".SFNS-UltraCompressedBoldG3" : @".SFNS",
+ @".SFNS-UltraCompressedBoldG4" : @".SFNS",
+ @".SFNS-UltraCompressedHeavy" : @".SFNS",
+ @".SFNS-UltraCompressedHeavyG1" : @".SFNS",
+ @".SFNS-UltraCompressedHeavyG2" : @".SFNS",
+ @".SFNS-UltraCompressedHeavyG3" : @".SFNS",
+ @".SFNS-UltraCompressedHeavyG4" : @".SFNS",
+ @".SFNS-UltraCompressedLight" : @".SFNS",
+ @".SFNS-UltraCompressedLightG1" : @".SFNS",
+ @".SFNS-UltraCompressedLightG2" : @".SFNS",
+ @".SFNS-UltraCompressedLightG3" : @".SFNS",
+ @".SFNS-UltraCompressedLightG4" : @".SFNS",
+ @".SFNS-UltraCompressedMedium" : @".SFNS",
+ @".SFNS-UltraCompressedMediumG1" : @".SFNS",
+ @".SFNS-UltraCompressedMediumG2" : @".SFNS",
+ @".SFNS-UltraCompressedMediumG3" : @".SFNS",
+ @".SFNS-UltraCompressedMediumG4" : @".SFNS",
+ @".SFNS-UltraCompressedRegular" : @".SFNS",
+ @".SFNS-UltraCompressedRegularG1" : @".SFNS",
+ @".SFNS-UltraCompressedRegularG2" : @".SFNS",
+ @".SFNS-UltraCompressedRegularG3" : @".SFNS",
+ @".SFNS-UltraCompressedRegularG4" : @".SFNS",
+ @".SFNS-UltraCompressedSemibold" : @".SFNS",
+ @".SFNS-UltraCompressedSemiboldG1" : @".SFNS",
+ @".SFNS-UltraCompressedSemiboldG2" : @".SFNS",
+ @".SFNS-UltraCompressedSemiboldG3" : @".SFNS",
+ @".SFNS-UltraCompressedSemiboldG4" : @".SFNS",
+ @".SFNS-UltraCompressedThin" : @".SFNS",
+ @".SFNS-UltraCompressedThinG1" : @".SFNS",
+ @".SFNS-UltraCompressedThinG2" : @".SFNS",
+ @".SFNS-UltraCompressedThinG3" : @".SFNS",
+ @".SFNS-UltraCompressedThinG4" : @".SFNS",
+ @".SFNS-UltraCompressedUltralight" : @".SFNS",
+ @".SFNS-UltraCompressedUltralightG1" : @".SFNS",
+ @".SFNS-UltraCompressedUltralightG2" : @".SFNS",
+ @".SFNS-UltraCompressedUltralightG3" : @".SFNS",
+ @".SFNS-UltraCompressedUltralightG4" : @".SFNS",
+ @".SanaPUA" : @".Sana PUA",
+ @".SavoyeLetPlainCC" : @".Savoye LET CC.",
+ @"AlBayan" : @"Al Bayan",
+ @"AlBayan-Bold" : @"Al Bayan",
+ @"AlNile" : @"Al Nile",
+ @"AlNile-Bold" : @"Al Nile",
+ @"AlTarikh" : @"Al Tarikh",
+ @"AmericanTypewriter" : @"American Typewriter",
+ @"AmericanTypewriter-Bold" : @"American Typewriter",
+ @"AmericanTypewriter-Condensed" : @"American Typewriter",
+ @"AmericanTypewriter-CondensedBold" : @"American Typewriter",
+ @"AmericanTypewriter-CondensedLight" : @"American Typewriter",
+ @"AmericanTypewriter-Light" : @"American Typewriter",
+ @"AmericanTypewriter-Semibold" : @"American Typewriter",
+ @"AndaleMono" : @"Andale Mono",
+ @"Apple-Chancery" : @"Apple Chancery",
+ @"AppleBraille" : @"Apple Braille",
+ @"AppleBraille-Outline6Dot" : @"Apple Braille",
+ @"AppleBraille-Outline8Dot" : @"Apple Braille",
+ @"AppleBraille-Pinpoint6Dot" : @"Apple Braille",
+ @"AppleBraille-Pinpoint8Dot" : @"Apple Braille",
+ @"AppleColorEmoji" : @"Apple Color Emoji",
+ @"AppleGothic" : @"AppleGothic",
+ @"AppleMyungjo" : @"AppleMyungjo",
+ @"AppleSDGothicNeo-Bold" : @"Apple SD Gothic Neo",
+ @"AppleSDGothicNeo-ExtraBold" : @"Apple SD Gothic Neo",
+ @"AppleSDGothicNeo-Heavy" : @"Apple SD Gothic Neo",
+ @"AppleSDGothicNeo-Light" : @"Apple SD Gothic Neo",
+ @"AppleSDGothicNeo-Medium" : @"Apple SD Gothic Neo",
+ @"AppleSDGothicNeo-Regular" : @"Apple SD Gothic Neo",
+ @"AppleSDGothicNeo-SemiBold" : @"Apple SD Gothic Neo",
+ @"AppleSDGothicNeo-Thin" : @"Apple SD Gothic Neo",
+ @"AppleSDGothicNeo-UltraLight" : @"Apple SD Gothic Neo",
+ @"AppleSymbols" : @"Apple Symbols",
+ @"AquaKana" : @".Aqua Kana",
+ @"AquaKana-Bold" : @".Aqua Kana",
+ @"Arial-Black" : @"Arial Black",
+ @"Arial-BoldItalicMT" : @"Arial",
+ @"Arial-BoldMT" : @"Arial",
+ @"Arial-ItalicMT" : @"Arial",
+ @"ArialHebrew" : @"Arial Hebrew",
+ @"ArialHebrew-Bold" : @"Arial Hebrew",
+ @"ArialHebrew-Light" : @"Arial Hebrew",
+ @"ArialHebrewScholar" : @"Arial Hebrew Scholar",
+ @"ArialHebrewScholar-Bold" : @"Arial Hebrew Scholar",
+ @"ArialHebrewScholar-Light" : @"Arial Hebrew Scholar",
+ @"ArialMT" : @"Arial",
+ @"ArialNarrow" : @"Arial Narrow",
+ @"ArialNarrow-Bold" : @"Arial Narrow",
+ @"ArialNarrow-BoldItalic" : @"Arial Narrow",
+ @"ArialNarrow-Italic" : @"Arial Narrow",
+ @"ArialRoundedMTBold" : @"Arial Rounded MT Bold",
+ @"ArialUnicodeMS" : @"Arial Unicode MS",
+ @"Athelas-Bold" : @"Athelas",
+ @"Athelas-BoldItalic" : @"Athelas",
+ @"Athelas-Italic" : @"Athelas",
+ @"Athelas-Regular" : @"Athelas",
+ @"Avenir-Black" : @"Avenir",
+ @"Avenir-BlackOblique" : @"Avenir",
+ @"Avenir-Book" : @"Avenir",
+ @"Avenir-BookOblique" : @"Avenir",
+ @"Avenir-Heavy" : @"Avenir",
+ @"Avenir-HeavyOblique" : @"Avenir",
+ @"Avenir-Light" : @"Avenir",
+ @"Avenir-LightOblique" : @"Avenir",
+ @"Avenir-Medium" : @"Avenir",
+ @"Avenir-MediumOblique" : @"Avenir",
+ @"Avenir-Oblique" : @"Avenir",
+ @"Avenir-Roman" : @"Avenir",
+ @"AvenirNext-Bold" : @"Avenir Next",
+ @"AvenirNext-BoldItalic" : @"Avenir Next",
+ @"AvenirNext-DemiBold" : @"Avenir Next",
+ @"AvenirNext-DemiBoldItalic" : @"Avenir Next",
+ @"AvenirNext-Heavy" : @"Avenir Next",
+ @"AvenirNext-HeavyItalic" : @"Avenir Next",
+ @"AvenirNext-Italic" : @"Avenir Next",
+ @"AvenirNext-Medium" : @"Avenir Next",
+ @"AvenirNext-MediumItalic" : @"Avenir Next",
+ @"AvenirNext-Regular" : @"Avenir Next",
+ @"AvenirNext-UltraLight" : @"Avenir Next",
+ @"AvenirNext-UltraLightItalic" : @"Avenir Next",
+ @"AvenirNextCondensed-Bold" : @"Avenir Next Condensed",
+ @"AvenirNextCondensed-BoldItalic" : @"Avenir Next Condensed",
+ @"AvenirNextCondensed-DemiBold" : @"Avenir Next Condensed",
+ @"AvenirNextCondensed-DemiBoldItalic" : @"Avenir Next Condensed",
+ @"AvenirNextCondensed-Heavy" : @"Avenir Next Condensed",
+ @"AvenirNextCondensed-HeavyItalic" : @"Avenir Next Condensed",
+ @"AvenirNextCondensed-Italic" : @"Avenir Next Condensed",
+ @"AvenirNextCondensed-Medium" : @"Avenir Next Condensed",
+ @"AvenirNextCondensed-MediumItalic" : @"Avenir Next Condensed",
+ @"AvenirNextCondensed-Regular" : @"Avenir Next Condensed",
+ @"AvenirNextCondensed-UltraLight" : @"Avenir Next Condensed",
+ @"AvenirNextCondensed-UltraLightItalic" : @"Avenir Next Condensed",
+ @"Ayuthaya" : @"Ayuthaya",
+ @"Baghdad" : @"Baghdad",
+ @"BanglaMN" : @"Bangla MN",
+ @"BanglaMN-Bold" : @"Bangla MN",
+ @"BanglaSangamMN" : @"Bangla Sangam MN",
+ @"BanglaSangamMN-Bold" : @"Bangla Sangam MN",
+ @"Baskerville" : @"Baskerville",
+ @"Baskerville-Bold" : @"Baskerville",
+ @"Baskerville-BoldItalic" : @"Baskerville",
+ @"Baskerville-Italic" : @"Baskerville",
+ @"Baskerville-SemiBold" : @"Baskerville",
+ @"Baskerville-SemiBoldItalic" : @"Baskerville",
+ @"Beirut" : @"Beirut",
+ @"BigCaslon-Medium" : @"Big Caslon",
+ @"BodoniOrnamentsITCTT" : @"Bodoni Ornaments",
+ @"BodoniSvtyTwoITCTT-Bold" : @"Bodoni 72",
+ @"BodoniSvtyTwoITCTT-Book" : @"Bodoni 72",
+ @"BodoniSvtyTwoITCTT-BookIta" : @"Bodoni 72",
+ @"BodoniSvtyTwoOSITCTT-Bold" : @"Bodoni 72 Oldstyle",
+ @"BodoniSvtyTwoOSITCTT-Book" : @"Bodoni 72 Oldstyle",
+ @"BodoniSvtyTwoOSITCTT-BookIt" : @"Bodoni 72 Oldstyle",
+ @"BodoniSvtyTwoSCITCTT-Book" : @"Bodoni 72 Smallcaps",
+ @"BradleyHandITCTT-Bold" : @"Bradley Hand",
+ @"BrushScriptMT" : @"Brush Script MT",
+ @"Calibri" : @"Calibri",
+ @"Calibri-Bold" : @"Calibri",
+ @"Calibri-BoldItalic" : @"Calibri",
+ @"Calibri-Italic" : @"Calibri",
+ @"Chalkboard" : @"Chalkboard",
+ @"Chalkboard-Bold" : @"Chalkboard",
+ @"ChalkboardSE-Bold" : @"Chalkboard SE",
+ @"ChalkboardSE-Light" : @"Chalkboard SE",
+ @"ChalkboardSE-Regular" : @"Chalkboard SE",
+ @"Chalkduster" : @"Chalkduster",
+ @"Charter-Black" : @"Charter",
+ @"Charter-BlackItalic" : @"Charter",
+ @"Charter-Bold" : @"Charter",
+ @"Charter-BoldItalic" : @"Charter",
+ @"Charter-Italic" : @"Charter",
+ @"Charter-Roman" : @"Charter",
+ @"Cochin" : @"Cochin",
+ @"Cochin-Bold" : @"Cochin",
+ @"Cochin-BoldItalic" : @"Cochin",
+ @"Cochin-Italic" : @"Cochin",
+ @"ComicSansMS" : @"Comic Sans MS",
+ @"ComicSansMS-Bold" : @"Comic Sans MS",
+ @"Copperplate" : @"Copperplate",
+ @"Copperplate-Bold" : @"Copperplate",
+ @"Copperplate-Light" : @"Copperplate",
+ @"CorsivaHebrew" : @"Corsiva Hebrew",
+ @"CorsivaHebrew-Bold" : @"Corsiva Hebrew",
+ @"Courier" : @"Courier",
+ @"Courier-Bold" : @"Courier",
+ @"Courier-BoldOblique" : @"Courier",
+ @"Courier-Oblique" : @"Courier",
+ @"CourierNewPS-BoldItalicMT" : @"Courier New",
+ @"CourierNewPS-BoldMT" : @"Courier New",
+ @"CourierNewPS-ItalicMT" : @"Courier New",
+ @"CourierNewPSMT" : @"Courier New",
+ @"DINAlternate-Bold" : @"DIN Alternate",
+ @"DINCondensed-Bold" : @"DIN Condensed",
+ @"Damascus" : @"Damascus",
+ @"DamascusBold" : @"Damascus",
+ @"DamascusLight" : @"Damascus",
+ @"DamascusMedium" : @"Damascus",
+ @"DamascusSemiBold" : @"Damascus",
+ @"DecoTypeNaskh" : @"DecoType Naskh",
+ @"DevanagariMT" : @"Devanagari MT",
+ @"DevanagariMT-Bold" : @"Devanagari MT",
+ @"DevanagariSangamMN" : @"Devanagari Sangam MN",
+ @"DevanagariSangamMN-Bold" : @"Devanagari Sangam MN",
+ @"Didot" : @"Didot",
+ @"Didot-Bold" : @"Didot",
+ @"Didot-Italic" : @"Didot",
+ @"DiwanKufi" : @"Diwan Kufi",
+ @"DiwanMishafi" : @"Mishafi",
+ @"DiwanMishafiGold" : @"Mishafi Gold",
+ @"DiwanThuluth" : @"Diwan Thuluth",
+ @"EuphemiaUCAS" : @"Euphemia UCAS",
+ @"EuphemiaUCAS-Bold" : @"Euphemia UCAS",
+ @"EuphemiaUCAS-Italic" : @"Euphemia UCAS",
+ @"Farah" : @"Farah",
+ @"Farisi" : @"Farisi",
+ @"Futura-Bold" : @"Futura",
+ @"Futura-CondensedExtraBold" : @"Futura",
+ @"Futura-CondensedMedium" : @"Futura",
+ @"Futura-Medium" : @"Futura",
+ @"Futura-MediumItalic" : @"Futura",
+ @"GB18030Bitmap" : @"GB18030 Bitmap",
+ @"Galvji" : @"Galvji",
+ @"Galvji-Bold" : @"Galvji",
+ @"Galvji-BoldOblique" : @"Galvji",
+ @"Galvji-Oblique" : @"Galvji",
+ @"GeezaPro" : @"Geeza Pro",
+ @"GeezaPro-Bold" : @"Geeza Pro",
+ @"Geneva" : @"Geneva",
+ @"Georgia" : @"Georgia",
+ @"Georgia-Bold" : @"Georgia",
+ @"Georgia-BoldItalic" : @"Georgia",
+ @"Georgia-Italic" : @"Georgia",
+ @"GillSans" : @"Gill Sans",
+ @"GillSans-Bold" : @"Gill Sans",
+ @"GillSans-BoldItalic" : @"Gill Sans",
+ @"GillSans-Italic" : @"Gill Sans",
+ @"GillSans-Light" : @"Gill Sans",
+ @"GillSans-LightItalic" : @"Gill Sans",
+ @"GillSans-SemiBold" : @"Gill Sans",
+ @"GillSans-SemiBoldItalic" : @"Gill Sans",
+ @"GillSans-UltraBold" : @"Gill Sans",
+ @"GujaratiMT" : @"Gujarati MT",
+ @"GujaratiMT-Bold" : @"Gujarati MT",
+ @"GujaratiSangamMN" : @"Gujarati Sangam MN",
+ @"GujaratiSangamMN-Bold" : @"Gujarati Sangam MN",
+ @"GurmukhiMN" : @"Gurmukhi MN",
+ @"GurmukhiMN-Bold" : @"Gurmukhi MN",
+ @"GurmukhiSangamMN" : @"Gurmukhi Sangam MN",
+ @"GurmukhiSangamMN-Bold" : @"Gurmukhi Sangam MN",
+ @"Helvetica" : @"Helvetica",
+ @"Helvetica-Bold" : @"Helvetica",
+ @"Helvetica-BoldOblique" : @"Helvetica",
+ @"Helvetica-Light" : @"Helvetica",
+ @"Helvetica-LightOblique" : @"Helvetica",
+ @"Helvetica-Oblique" : @"Helvetica",
+ @"HelveticaLTMM" : @".Helvetica LT MM",
+ @"HelveticaNeue" : @"Helvetica Neue",
+ @"HelveticaNeue-Bold" : @"Helvetica Neue",
+ @"HelveticaNeue-BoldItalic" : @"Helvetica Neue",
+ @"HelveticaNeue-CondensedBlack" : @"Helvetica Neue",
+ @"HelveticaNeue-CondensedBold" : @"Helvetica Neue",
+ @"HelveticaNeue-Italic" : @"Helvetica Neue",
+ @"HelveticaNeue-Light" : @"Helvetica Neue",
+ @"HelveticaNeue-LightItalic" : @"Helvetica Neue",
+ @"HelveticaNeue-Medium" : @"Helvetica Neue",
+ @"HelveticaNeue-MediumItalic" : @"Helvetica Neue",
+ @"HelveticaNeue-Thin" : @"Helvetica Neue",
+ @"HelveticaNeue-ThinItalic" : @"Helvetica Neue",
+ @"HelveticaNeue-UltraLight" : @"Helvetica Neue",
+ @"HelveticaNeue-UltraLightItalic" : @"Helvetica Neue",
+ @"Herculanum" : @"Herculanum",
+ @"HiraKakuPro-W3" : @"Hiragino Kaku Gothic Pro",
+ @"HiraKakuPro-W6" : @"Hiragino Kaku Gothic Pro",
+ @"HiraKakuProN-W3" : @"Hiragino Kaku Gothic ProN",
+ @"HiraKakuProN-W6" : @"Hiragino Kaku Gothic ProN",
+ @"HiraKakuStd-W8" : @"Hiragino Kaku Gothic Std",
+ @"HiraKakuStdN-W8" : @"Hiragino Kaku Gothic StdN",
+ @"HiraMaruPro-W4" : @"Hiragino Maru Gothic Pro",
+ @"HiraMaruProN-W4" : @"Hiragino Maru Gothic ProN",
+ @"HiraMinPro-W3" : @"Hiragino Mincho Pro",
+ @"HiraMinPro-W6" : @"Hiragino Mincho Pro",
+ @"HiraMinProN-W3" : @"Hiragino Mincho ProN",
+ @"HiraMinProN-W6" : @"Hiragino Mincho ProN",
+ @"HiraginoSans-W0" : @"Hiragino Sans",
+ @"HiraginoSans-W1" : @"Hiragino Sans",
+ @"HiraginoSans-W2" : @"Hiragino Sans",
+ @"HiraginoSans-W3" : @"Hiragino Sans",
+ @"HiraginoSans-W4" : @"Hiragino Sans",
+ @"HiraginoSans-W5" : @"Hiragino Sans",
+ @"HiraginoSans-W6" : @"Hiragino Sans",
+ @"HiraginoSans-W7" : @"Hiragino Sans",
+ @"HiraginoSans-W8" : @"Hiragino Sans",
+ @"HiraginoSans-W9" : @"Hiragino Sans",
+ @"HiraginoSansGB-W3" : @"Hiragino Sans GB",
+ @"HiraginoSansGB-W6" : @"Hiragino Sans GB",
+ @"HoeflerText-Black" : @"Hoefler Text",
+ @"HoeflerText-BlackItalic" : @"Hoefler Text",
+ @"HoeflerText-Italic" : @"Hoefler Text",
+ @"HoeflerText-Ornaments" : @"Hoefler Text",
+ @"HoeflerText-Regular" : @"Hoefler Text",
+ @"ITFDevanagari-Bold" : @"ITF Devanagari",
+ @"ITFDevanagari-Book" : @"ITF Devanagari",
+ @"ITFDevanagari-Demi" : @"ITF Devanagari",
+ @"ITFDevanagari-Light" : @"ITF Devanagari",
+ @"ITFDevanagari-Medium" : @"ITF Devanagari",
+ @"ITFDevanagariMarathi-Bold" : @"ITF Devanagari Marathi",
+ @"ITFDevanagariMarathi-Book" : @"ITF Devanagari Marathi",
+ @"ITFDevanagariMarathi-Demi" : @"ITF Devanagari Marathi",
+ @"ITFDevanagariMarathi-Light" : @"ITF Devanagari Marathi",
+ @"ITFDevanagariMarathi-Medium" : @"ITF Devanagari Marathi",
+ @"Impact" : @"Impact",
+ @"InaiMathi" : @"InaiMathi",
+ @"InaiMathi-Bold" : @"InaiMathi",
+ @"IowanOldStyle-Black" : @"Iowan Old Style",
+ @"IowanOldStyle-BlackItalic" : @"Iowan Old Style",
+ @"IowanOldStyle-Bold" : @"Iowan Old Style",
+ @"IowanOldStyle-BoldItalic" : @"Iowan Old Style",
+ @"IowanOldStyle-Italic" : @"Iowan Old Style",
+ @"IowanOldStyle-Roman" : @"Iowan Old Style",
+ @"IowanOldStyle-Titling" : @"Iowan Old Style",
+ @"Kailasa" : @"Kailasa",
+ @"Kailasa-Bold" : @"Kailasa",
+ @"KannadaMN" : @"Kannada MN",
+ @"KannadaMN-Bold" : @"Kannada MN",
+ @"KannadaSangamMN" : @"Kannada Sangam MN",
+ @"KannadaSangamMN-Bold" : @"Kannada Sangam MN",
+ @"Kefa-Bold" : @"Kefa",
+ @"Kefa-Regular" : @"Kefa",
+ @"KhmerMN" : @"Khmer MN",
+ @"KhmerMN-Bold" : @"Khmer MN",
+ @"KhmerSangamMN" : @"Khmer Sangam MN",
+ @"KohinoorBangla-Bold" : @"Kohinoor Bangla",
+ @"KohinoorBangla-Light" : @"Kohinoor Bangla",
+ @"KohinoorBangla-Medium" : @"Kohinoor Bangla",
+ @"KohinoorBangla-Regular" : @"Kohinoor Bangla",
+ @"KohinoorBangla-Semibold" : @"Kohinoor Bangla",
+ @"KohinoorDevanagari-Bold" : @"Kohinoor Devanagari",
+ @"KohinoorDevanagari-Light" : @"Kohinoor Devanagari",
+ @"KohinoorDevanagari-Medium" : @"Kohinoor Devanagari",
+ @"KohinoorDevanagari-Regular" : @"Kohinoor Devanagari",
+ @"KohinoorDevanagari-Semibold" : @"Kohinoor Devanagari",
+ @"KohinoorGujarati-Bold" : @"Kohinoor Gujarati",
+ @"KohinoorGujarati-Light" : @"Kohinoor Gujarati",
+ @"KohinoorGujarati-Medium" : @"Kohinoor Gujarati",
+ @"KohinoorGujarati-Regular" : @"Kohinoor Gujarati",
+ @"KohinoorGujarati-Semibold" : @"Kohinoor Gujarati",
+ @"KohinoorTelugu-Bold" : @"Kohinoor Telugu",
+ @"KohinoorTelugu-Light" : @"Kohinoor Telugu",
+ @"KohinoorTelugu-Medium" : @"Kohinoor Telugu",
+ @"KohinoorTelugu-Regular" : @"Kohinoor Telugu",
+ @"KohinoorTelugu-Semibold" : @"Kohinoor Telugu",
+ @"Kokonor" : @"Kokonor",
+ @"Krungthep" : @"Krungthep",
+ @"KufiStandardGK" : @"KufiStandardGK",
+ @"LaoMN" : @"Lao MN",
+ @"LaoMN-Bold" : @"Lao MN",
+ @"LaoSangamMN" : @"Lao Sangam MN",
+ @"LastResort" : @".LastResort",
+ @"LucidaGrande" : @"Lucida Grande",
+ @"LucidaGrande-Bold" : @"Lucida Grande",
+ @"Luminari-Regular" : @"Luminari",
+ @"MalayalamMN" : @"Malayalam MN",
+ @"MalayalamMN-Bold" : @"Malayalam MN",
+ @"MalayalamSangamMN" : @"Malayalam Sangam MN",
+ @"MalayalamSangamMN-Bold" : @"Malayalam Sangam MN",
+ @"Marion-Bold" : @"Marion",
+ @"Marion-Italic" : @"Marion",
+ @"Marion-Regular" : @"Marion",
+ @"MarkerFelt-Thin" : @"Marker Felt",
+ @"MarkerFelt-Wide" : @"Marker Felt",
+ @"Menlo-Bold" : @"Menlo",
+ @"Menlo-BoldItalic" : @"Menlo",
+ @"Menlo-Italic" : @"Menlo",
+ @"Menlo-Regular" : @"Menlo",
+ @"MicrosoftSansSerif" : @"Microsoft Sans Serif",
+ @"Monaco" : @"Monaco",
+ @"MonotypeGurmukhi" : @"Gurmukhi MT",
+ @"Mshtakan" : @"Mshtakan",
+ @"MshtakanBold" : @"Mshtakan",
+ @"MshtakanBoldOblique" : @"Mshtakan",
+ @"MshtakanOblique" : @"Mshtakan",
+ @"MuktaMahee-Bold" : @"Mukta Mahee",
+ @"MuktaMahee-ExtraBold" : @"Mukta Mahee",
+ @"MuktaMahee-ExtraLight" : @"Mukta Mahee",
+ @"MuktaMahee-Light" : @"Mukta Mahee",
+ @"MuktaMahee-Medium" : @"Mukta Mahee",
+ @"MuktaMahee-Regular" : @"Mukta Mahee",
+ @"MuktaMahee-SemiBold" : @"Mukta Mahee",
+ @"Muna" : @"Muna",
+ @"MunaBlack" : @"Muna",
+ @"MunaBold" : @"Muna",
+ @"MyanmarMN" : @"Myanmar MN",
+ @"MyanmarMN-Bold" : @"Myanmar MN",
+ @"MyanmarSangamMN" : @"Myanmar Sangam MN",
+ @"MyanmarSangamMN-Bold" : @"Myanmar Sangam MN",
+ @"Nadeem" : @"Nadeem",
+ @"NewPeninimMT" : @"New Peninim MT",
+ @"NewPeninimMT-Bold" : @"New Peninim MT",
+ @"NewPeninimMT-BoldInclined" : @"New Peninim MT",
+ @"NewPeninimMT-Inclined" : @"New Peninim MT",
+ @"Noteworthy-Bold" : @"Noteworthy",
+ @"Noteworthy-Light" : @"Noteworthy",
+ @"NotoNastaliqUrdu" : @"Noto Nastaliq Urdu",
+ @"NotoNastaliqUrdu-Bold" : @"Noto Nastaliq Urdu",
+ @"NotoSansArmenian-Black" : @"Noto Sans Armenian",
+ @"NotoSansArmenian-Bold" : @"Noto Sans Armenian",
+ @"NotoSansArmenian-ExtraBold" : @"Noto Sans Armenian",
+ @"NotoSansArmenian-ExtraLight" : @"Noto Sans Armenian",
+ @"NotoSansArmenian-Light" : @"Noto Sans Armenian",
+ @"NotoSansArmenian-Medium" : @"Noto Sans Armenian",
+ @"NotoSansArmenian-Regular" : @"Noto Sans Armenian",
+ @"NotoSansArmenian-SemiBold" : @"Noto Sans Armenian",
+ @"NotoSansArmenian-Thin" : @"Noto Sans Armenian",
+ @"NotoSansAvestan-Regular" : @"Noto Sans Avestan",
+ @"NotoSansBamum-Regular" : @"Noto Sans Bamum",
+ @"NotoSansBatak-Regular" : @"Noto Sans Batak",
+ @"NotoSansBrahmi-Regular" : @"Noto Sans Brahmi",
+ @"NotoSansBuginese-Regular" : @"Noto Sans Buginese",
+ @"NotoSansBuhid-Regular" : @"Noto Sans Buhid",
+ @"NotoSansCarian-Regular" : @"Noto Sans Carian",
+ @"NotoSansChakma-Regular" : @"Noto Sans Chakma",
+ @"NotoSansCham-Regular" : @"Noto Sans Cham",
+ @"NotoSansCoptic-Regular" : @"Noto Sans Coptic",
+ @"NotoSansCuneiform-Regular" : @"Noto Sans Cuneiform",
+ @"NotoSansCypriot-Regular" : @"Noto Sans Cypriot",
+ @"NotoSansEgyptianHieroglyphs-Regular" : @"Noto Sans Egyptian Hieroglyphs",
+ @"NotoSansGlagolitic-Regular" : @"Noto Sans Glagolitic",
+ @"NotoSansGothic-Regular" : @"Noto Sans Gothic",
+ @"NotoSansHanunoo-Regular" : @"Noto Sans Hanunoo",
+ @"NotoSansImperialAramaic-Regular" : @"Noto Sans Imperial Aramaic",
+ @"NotoSansInscriptionalPahlavi-Regular" : @"Noto Sans Inscriptional Pahlavi",
+ @"NotoSansInscriptionalParthian-Regular" : @"Noto Sans Inscriptional Parthian",
+ @"NotoSansJavanese-Regular" : @"Noto Sans Javanese",
+ @"NotoSansKaithi-Regular" : @"Noto Sans Kaithi",
+ @"NotoSansKannada-Black" : @"Noto Sans Kannada",
+ @"NotoSansKannada-Bold" : @"Noto Sans Kannada",
+ @"NotoSansKannada-ExtraBold" : @"Noto Sans Kannada",
+ @"NotoSansKannada-ExtraLight" : @"Noto Sans Kannada",
+ @"NotoSansKannada-Light" : @"Noto Sans Kannada",
+ @"NotoSansKannada-Medium" : @"Noto Sans Kannada",
+ @"NotoSansKannada-Regular" : @"Noto Sans Kannada",
+ @"NotoSansKannada-SemiBold" : @"Noto Sans Kannada",
+ @"NotoSansKannada-Thin" : @"Noto Sans Kannada",
+ @"NotoSansKayahLi-Regular" : @"Noto Sans Kayah Li",
+ @"NotoSansKharoshthi-Regular" : @"Noto Sans Kharoshthi",
+ @"NotoSansLepcha-Regular" : @"Noto Sans Lepcha",
+ @"NotoSansLimbu-Regular" : @"Noto Sans Limbu",
+ @"NotoSansLinearB-Regular" : @"Noto Sans Linear B",
+ @"NotoSansLisu-Regular" : @"Noto Sans Lisu",
+ @"NotoSansLycian-Regular" : @"Noto Sans Lycian",
+ @"NotoSansLydian-Regular" : @"Noto Sans Lydian",
+ @"NotoSansMandaic-Regular" : @"Noto Sans Mandaic",
+ @"NotoSansMeeteiMayek-Regular" : @"Noto Sans Meetei Mayek",
+ @"NotoSansMongolian" : @"Noto Sans Mongolian",
+ @"NotoSansMyanmar-Black" : @"Noto Sans Myanmar",
+ @"NotoSansMyanmar-Bold" : @"Noto Sans Myanmar",
+ @"NotoSansMyanmar-ExtraBold" : @"Noto Sans Myanmar",
+ @"NotoSansMyanmar-ExtraLight" : @"Noto Sans Myanmar",
+ @"NotoSansMyanmar-Light" : @"Noto Sans Myanmar",
+ @"NotoSansMyanmar-Medium" : @"Noto Sans Myanmar",
+ @"NotoSansMyanmar-Regular" : @"Noto Sans Myanmar",
+ @"NotoSansMyanmar-SemiBold" : @"Noto Sans Myanmar",
+ @"NotoSansMyanmar-Thin" : @"Noto Sans Myanmar",
+ @"NotoSansNKo-Regular" : @"Noto Sans NKo",
+ @"NotoSansNewTaiLue-Regular" : @"Noto Sans New Tai Lue",
+ @"NotoSansOgham-Regular" : @"Noto Sans Ogham",
+ @"NotoSansOlChiki-Regular" : @"Noto Sans Ol Chiki",
+ @"NotoSansOldItalic-Regular" : @"Noto Sans Old Italic",
+ @"NotoSansOldPersian-Regular" : @"Noto Sans Old Persian",
+ @"NotoSansOldSouthArabian-Regular" : @"Noto Sans Old South Arabian",
+ @"NotoSansOldTurkic-Regular" : @"Noto Sans Old Turkic",
+ @"NotoSansOriya" : @"Noto Sans Oriya",
+ @"NotoSansOriya-Bold" : @"Noto Sans Oriya",
+ @"NotoSansOsmanya-Regular" : @"Noto Sans Osmanya",
+ @"NotoSansPhagsPa-Regular" : @"Noto Sans PhagsPa",
+ @"NotoSansPhoenician-Regular" : @"Noto Sans Phoenician",
+ @"NotoSansRejang-Regular" : @"Noto Sans Rejang",
+ @"NotoSansRunic-Regular" : @"Noto Sans Runic",
+ @"NotoSansSamaritan-Regular" : @"Noto Sans Samaritan",
+ @"NotoSansSaurashtra-Regular" : @"Noto Sans Saurashtra",
+ @"NotoSansShavian-Regular" : @"Noto Sans Shavian",
+ @"NotoSansSundanese-Regular" : @"Noto Sans Sundanese",
+ @"NotoSansSylotiNagri-Regular" : @"Noto Sans Syloti Nagri",
+ @"NotoSansSyriac-Regular" : @"Noto Sans Syriac",
+ @"NotoSansTagalog-Regular" : @"Noto Sans Tagalog",
+ @"NotoSansTagbanwa-Regular" : @"Noto Sans Tagbanwa",
+ @"NotoSansTaiLe-Regular" : @"Noto Sans Tai Le",
+ @"NotoSansTaiTham" : @"Noto Sans Tai Tham",
+ @"NotoSansTaiViet-Regular" : @"Noto Sans Tai Viet",
+ @"NotoSansThaana-Regular" : @"Noto Sans Thaana",
+ @"NotoSansTifinagh-Regular" : @"Noto Sans Tifinagh",
+ @"NotoSansUgaritic-Regular" : @"Noto Sans Ugaritic",
+ @"NotoSansVai-Regular" : @"Noto Sans Vai",
+ @"NotoSansYi-Regular" : @"Noto Sans Yi",
+ @"NotoSansZawgyi-Black" : @"Noto Sans Zawgyi",
+ @"NotoSansZawgyi-Bold" : @"Noto Sans Zawgyi",
+ @"NotoSansZawgyi-ExtraBold" : @"Noto Sans Zawgyi",
+ @"NotoSansZawgyi-ExtraLight" : @"Noto Sans Zawgyi",
+ @"NotoSansZawgyi-Light" : @"Noto Sans Zawgyi",
+ @"NotoSansZawgyi-Medium" : @"Noto Sans Zawgyi",
+ @"NotoSansZawgyi-Regular" : @"Noto Sans Zawgyi",
+ @"NotoSansZawgyi-SemiBold" : @"Noto Sans Zawgyi",
+ @"NotoSansZawgyi-Thin" : @"Noto Sans Zawgyi",
+ @"NotoSerifBalinese-Regular" : @"Noto Serif Balinese",
+ @"NotoSerifMyanmar-Black" : @"Noto Serif Myanmar",
+ @"NotoSerifMyanmar-Bold" : @"Noto Serif Myanmar",
+ @"NotoSerifMyanmar-ExtraBold" : @"Noto Serif Myanmar",
+ @"NotoSerifMyanmar-ExtraLight" : @"Noto Serif Myanmar",
+ @"NotoSerifMyanmar-Light" : @"Noto Serif Myanmar",
+ @"NotoSerifMyanmar-Medium" : @"Noto Serif Myanmar",
+ @"NotoSerifMyanmar-Regular" : @"Noto Serif Myanmar",
+ @"NotoSerifMyanmar-SemiBold" : @"Noto Serif Myanmar",
+ @"NotoSerifMyanmar-Thin" : @"Noto Serif Myanmar",
+ @"Optima-Bold" : @"Optima",
+ @"Optima-BoldItalic" : @"Optima",
+ @"Optima-ExtraBlack" : @"Optima",
+ @"Optima-Italic" : @"Optima",
+ @"Optima-Regular" : @"Optima",
+ @"OriyaMN" : @"Oriya MN",
+ @"OriyaMN-Bold" : @"Oriya MN",
+ @"OriyaSangamMN" : @"Oriya Sangam MN",
+ @"OriyaSangamMN-Bold" : @"Oriya Sangam MN",
+ @"PTMono-Bold" : @"PT Mono",
+ @"PTMono-Regular" : @"PT Mono",
+ @"PTSans-Bold" : @"PT Sans",
+ @"PTSans-BoldItalic" : @"PT Sans",
+ @"PTSans-Caption" : @"PT Sans Caption",
+ @"PTSans-CaptionBold" : @"PT Sans Caption",
+ @"PTSans-Italic" : @"PT Sans",
+ @"PTSans-Narrow" : @"PT Sans Narrow",
+ @"PTSans-NarrowBold" : @"PT Sans Narrow",
+ @"PTSans-Regular" : @"PT Sans",
+ @"PTSerif-Bold" : @"PT Serif",
+ @"PTSerif-BoldItalic" : @"PT Serif",
+ @"PTSerif-Caption" : @"PT Serif Caption",
+ @"PTSerif-CaptionItalic" : @"PT Serif Caption",
+ @"PTSerif-Italic" : @"PT Serif",
+ @"PTSerif-Regular" : @"PT Serif",
+ @"Palatino-Bold" : @"Palatino",
+ @"Palatino-BoldItalic" : @"Palatino",
+ @"Palatino-Italic" : @"Palatino",
+ @"Palatino-Roman" : @"Palatino",
+ @"Papyrus" : @"Papyrus",
+ @"Papyrus-Condensed" : @"Papyrus",
+ @"Phosphate-Inline" : @"Phosphate",
+ @"Phosphate-Solid" : @"Phosphate",
+ @"PingFangHK-Light" : @"PingFang HK",
+ @"PingFangHK-Medium" : @"PingFang HK",
+ @"PingFangHK-Regular" : @"PingFang HK",
+ @"PingFangHK-Semibold" : @"PingFang HK",
+ @"PingFangHK-Thin" : @"PingFang HK",
+ @"PingFangHK-Ultralight" : @"PingFang HK",
+ @"PingFangSC-Light" : @"PingFang SC",
+ @"PingFangSC-Medium" : @"PingFang SC",
+ @"PingFangSC-Regular" : @"PingFang SC",
+ @"PingFangSC-Semibold" : @"PingFang SC",
+ @"PingFangSC-Thin" : @"PingFang SC",
+ @"PingFangSC-Ultralight" : @"PingFang SC",
+ @"PingFangTC-Light" : @"PingFang TC",
+ @"PingFangTC-Medium" : @"PingFang TC",
+ @"PingFangTC-Regular" : @"PingFang TC",
+ @"PingFangTC-Semibold" : @"PingFang TC",
+ @"PingFangTC-Thin" : @"PingFang TC",
+ @"PingFangTC-Ultralight" : @"PingFang TC",
+ @"PlantagenetCherokee" : @"Plantagenet Cherokee",
+ @"Raanana" : @"Raanana",
+ @"RaananaBold" : @"Raanana",
+ @"Rockwell-Bold" : @"Rockwell",
+ @"Rockwell-BoldItalic" : @"Rockwell",
+ @"Rockwell-Italic" : @"Rockwell",
+ @"Rockwell-Regular" : @"Rockwell",
+ @"SFMono-Bold" : @"SF Mono",
+ @"SFMono-BoldItalic" : @"SF Mono",
+ @"SFMono-Regular" : @"SF Mono",
+ @"SFMono-RegularItalic" : @"SF Mono",
+ @"STHeitiSC-Light" : @"Heiti SC",
+ @"STHeitiSC-Medium" : @"Heiti SC",
+ @"STHeitiTC-Light" : @"Heiti TC",
+ @"STHeitiTC-Medium" : @"Heiti TC",
+ @"STIXGeneral-Bold" : @"STIXGeneral",
+ @"STIXGeneral-BoldItalic" : @"STIXGeneral",
+ @"STIXGeneral-Italic" : @"STIXGeneral",
+ @"STIXGeneral-Regular" : @"STIXGeneral",
+ @"STIXIntegralsD-Bold" : @"STIXIntegralsD",
+ @"STIXIntegralsD-Regular" : @"STIXIntegralsD",
+ @"STIXIntegralsSm-Bold" : @"STIXIntegralsSm",
+ @"STIXIntegralsSm-Regular" : @"STIXIntegralsSm",
+ @"STIXIntegralsUp-Bold" : @"STIXIntegralsUp",
+ @"STIXIntegralsUp-Regular" : @"STIXIntegralsUp",
+ @"STIXIntegralsUpD-Bold" : @"STIXIntegralsUpD",
+ @"STIXIntegralsUpD-Regular" : @"STIXIntegralsUpD",
+ @"STIXIntegralsUpSm-Bold" : @"STIXIntegralsUpSm",
+ @"STIXIntegralsUpSm-Regular" : @"STIXIntegralsUpSm",
+ @"STIXNonUnicode-Bold" : @"STIXNonUnicode",
+ @"STIXNonUnicode-BoldItalic" : @"STIXNonUnicode",
+ @"STIXNonUnicode-Italic" : @"STIXNonUnicode",
+ @"STIXNonUnicode-Regular" : @"STIXNonUnicode",
+ @"STIXSizeFiveSym-Regular" : @"STIXSizeFiveSym",
+ @"STIXSizeFourSym-Bold" : @"STIXSizeFourSym",
+ @"STIXSizeFourSym-Regular" : @"STIXSizeFourSym",
+ @"STIXSizeOneSym-Bold" : @"STIXSizeOneSym",
+ @"STIXSizeOneSym-Regular" : @"STIXSizeOneSym",
+ @"STIXSizeThreeSym-Bold" : @"STIXSizeThreeSym",
+ @"STIXSizeThreeSym-Regular" : @"STIXSizeThreeSym",
+ @"STIXSizeTwoSym-Bold" : @"STIXSizeTwoSym",
+ @"STIXSizeTwoSym-Regular" : @"STIXSizeTwoSym",
+ @"STIXVariants-Bold" : @"STIXVariants",
+ @"STIXVariants-Regular" : @"STIXVariants",
+ @"STSong" : @"STSong",
+ @"STSongti-SC-Black" : @"Songti SC",
+ @"STSongti-SC-Bold" : @"Songti SC",
+ @"STSongti-SC-Light" : @"Songti SC",
+ @"STSongti-SC-Regular" : @"Songti SC",
+ @"STSongti-TC-Bold" : @"Songti TC",
+ @"STSongti-TC-Light" : @"Songti TC",
+ @"STSongti-TC-Regular" : @"Songti TC",
+ @"Sana" : @"Sana",
+ @"Sathu" : @"Sathu",
+ @"SavoyeLetPlain" : @"Savoye LET",
+ @"Seravek" : @"Seravek",
+ @"Seravek-Bold" : @"Seravek",
+ @"Seravek-BoldItalic" : @"Seravek",
+ @"Seravek-ExtraLight" : @"Seravek",
+ @"Seravek-ExtraLightItalic" : @"Seravek",
+ @"Seravek-Italic" : @"Seravek",
+ @"Seravek-Light" : @"Seravek",
+ @"Seravek-LightItalic" : @"Seravek",
+ @"Seravek-Medium" : @"Seravek",
+ @"Seravek-MediumItalic" : @"Seravek",
+ @"ShreeDev0714" : @"Shree Devanagari 714",
+ @"ShreeDev0714-Bold" : @"Shree Devanagari 714",
+ @"ShreeDev0714-BoldItalic" : @"Shree Devanagari 714",
+ @"ShreeDev0714-Italic" : @"Shree Devanagari 714",
+ @"SignPainter-HouseScript" : @"SignPainter",
+ @"SignPainter-HouseScriptSemibold" : @"SignPainter",
+ @"Silom" : @"Silom",
+ @"SinhalaMN" : @"Sinhala MN",
+ @"SinhalaMN-Bold" : @"Sinhala MN",
+ @"SinhalaSangamMN" : @"Sinhala Sangam MN",
+ @"SinhalaSangamMN-Bold" : @"Sinhala Sangam MN",
+ @"Skia-Regular" : @"Skia",
+ @"Skia-Regular_Black" : @"Skia",
+ @"Skia-Regular_Black-Condensed" : @"Skia",
+ @"Skia-Regular_Black-Extended" : @"Skia",
+ @"Skia-Regular_Bold" : @"Skia",
+ @"Skia-Regular_Condensed" : @"Skia",
+ @"Skia-Regular_Extended" : @"Skia",
+ @"Skia-Regular_Light" : @"Skia",
+ @"Skia-Regular_Light-Condensed" : @"Skia",
+ @"Skia-Regular_Light-Extended" : @"Skia",
+ @"SnellRoundhand" : @"Snell Roundhand",
+ @"SnellRoundhand-Black" : @"Snell Roundhand",
+ @"SnellRoundhand-Bold" : @"Snell Roundhand",
+ @"SukhumvitSet-Bold" : @"Sukhumvit Set",
+ @"SukhumvitSet-Light" : @"Sukhumvit Set",
+ @"SukhumvitSet-Medium" : @"Sukhumvit Set",
+ @"SukhumvitSet-SemiBold" : @"Sukhumvit Set",
+ @"SukhumvitSet-Text" : @"Sukhumvit Set",
+ @"SukhumvitSet-Thin" : @"Sukhumvit Set",
+ @"Superclarendon-Black" : @"Superclarendon",
+ @"Superclarendon-BlackItalic" : @"Superclarendon",
+ @"Superclarendon-Bold" : @"Superclarendon",
+ @"Superclarendon-BoldItalic" : @"Superclarendon",
+ @"Superclarendon-Italic" : @"Superclarendon",
+ @"Superclarendon-Light" : @"Superclarendon",
+ @"Superclarendon-LightItalic" : @"Superclarendon",
+ @"Superclarendon-Regular" : @"Superclarendon",
+ @"Symbol" : @"Symbol",
+ @"Tahoma" : @"Tahoma",
+ @"Tahoma-Bold" : @"Tahoma",
+ @"TamilMN" : @"Tamil MN",
+ @"TamilMN-Bold" : @"Tamil MN",
+ @"TamilSangamMN" : @"Tamil Sangam MN",
+ @"TamilSangamMN-Bold" : @"Tamil Sangam MN",
+ @"TeluguMN" : @"Telugu MN",
+ @"TeluguMN-Bold" : @"Telugu MN",
+ @"TeluguSangamMN" : @"Telugu Sangam MN",
+ @"TeluguSangamMN-Bold" : @"Telugu Sangam MN",
+ @"Thonburi" : @"Thonburi",
+ @"Thonburi-Bold" : @"Thonburi",
+ @"Thonburi-Light" : @"Thonburi",
+ @"Times-Bold" : @"Times",
+ @"Times-BoldItalic" : @"Times",
+ @"Times-Italic" : @"Times",
+ @"Times-Roman" : @"Times",
+ @"TimesLTMM" : @".Times LT MM",
+ @"TimesNewRomanPS-BoldItalicMT" : @"Times New Roman",
+ @"TimesNewRomanPS-BoldMT" : @"Times New Roman",
+ @"TimesNewRomanPS-ItalicMT" : @"Times New Roman",
+ @"TimesNewRomanPSMT" : @"Times New Roman",
+ @"Trattatello" : @"Trattatello",
+ @"Trebuchet-BoldItalic" : @"Trebuchet MS",
+ @"TrebuchetMS" : @"Trebuchet MS",
+ @"TrebuchetMS-Bold" : @"Trebuchet MS",
+ @"TrebuchetMS-Italic" : @"Trebuchet MS",
+ @"Verdana" : @"Verdana",
+ @"Verdana-Bold" : @"Verdana",
+ @"Verdana-BoldItalic" : @"Verdana",
+ @"Verdana-Italic" : @"Verdana",
+ @"Waseem" : @"Waseem",
+ @"WaseemLight" : @"Waseem",
+ @"Webdings" : @"Webdings",
+ @"Wingdings-Regular" : @"Wingdings",
+ @"Wingdings2" : @"Wingdings 2",
+ @"Wingdings3" : @"Wingdings 3",
+ @"ZapfDingbatsITC" : @"Zapf Dingbats",
+ @"Zapfino" : @"Zapfino",
+
+ // JetBrains fonts
+ @"DroidSans" : @"Droid Sans",
+ @"DroidSans-Bold" : @"Droid Sans",
+ @"DroidSansMono" : @"Droid Sans Mono",
+ @"DroidSansMonoDotted" : @"Droid Sans Mono Dotted",
+ @"DroidSansMonoSlashed" : @"Droid Sans Mono Slashed",
+ @"DroidSerif" : @"Droid Serif",
+ @"DroidSerif-Bold" : @"Droid Serif",
+ @"DroidSerif-BoldItalic" : @"Droid Serif",
+ @"DroidSerif-Italic" : @"Droid Serif",
+ @"FiraCode-Bold" : @"Fira Code",
+ @"FiraCode-Light" : @"Fira Code",
+ @"FiraCode-Medium" : @"Fira Code",
+ @"FiraCode-Regular" : @"Fira Code",
+ @"FiraCode-Retina" : @"Fira Code",
+ @"Inconsolata" : @"Inconsolata",
+ @"JetBrainsMono-Bold" : @"JetBrains Mono",
+ @"JetBrainsMono-Regular" : @"JetBrains Mono",
+ @"JetBrainsMono-Italic" : @"JetBrains Mono",
+ @"JetBrainsMono-BoldItalic" : @"JetBrains Mono",
+ @"Roboto-Light" : @"Roboto",
+ @"Roboto-Thin" : @"Roboto",
+ @"SourceCodePro-Bold" : @"Source Code Pro",
+ @"SourceCodePro-BoldIt" : @"Source Code Pro",
+ @"SourceCodePro-It" : @"Source Code Pro",
+ @"SourceCodePro-Regular" : @"Source Code Pro",
+ @"Inter-Bold": @"Inter",
+ @"Inter-BoldItalic": @"Inter",
+ @"Inter-Italic": @"Inter",
+ @"Inter-Regular": @"Inter"
+ };
+}
+
+static NSDictionary* prebuiltFaceNames() {
+ return @{
+ @".NewYork-Black" : @"Black",
+ @".NewYork-BlackItalic" : @"Black Italic",
+ @".NewYork-Bold" : @"Bold",
+ @".NewYork-BoldG1" : @"Bold G1",
+ @".NewYork-BoldG2" : @"Bold G2",
+ @".NewYork-BoldG3" : @"Bold G3",
+ @".NewYork-BoldG4" : @"Bold G4",
+ @".NewYork-BoldItalic" : @"Bold Italic",
+ @".NewYork-BoldItalicG1" : @"Bold Italic G1",
+ @".NewYork-BoldItalicG2" : @"Bold Italic G2",
+ @".NewYork-BoldItalicG3" : @"Bold Italic G3",
+ @".NewYork-BoldItalicG4" : @"Bold Italic G4",
+ @".NewYork-Heavy" : @"Heavy",
+ @".NewYork-HeavyItalic" : @"Heavy Italic",
+ @".NewYork-Medium" : @"Medium",
+ @".NewYork-MediumItalic" : @"Medium Italic",
+ @".NewYork-Regular" : @"Regular",
+ @".NewYork-RegularG1" : @"Regular G1",
+ @".NewYork-RegularG2" : @"Regular G2",
+ @".NewYork-RegularG3" : @"Regular G3",
+ @".NewYork-RegularG4" : @"Regular G4",
+ @".NewYork-RegularItalic" : @"Regular Italic",
+ @".NewYork-RegularItalicG1" : @"Regular Italic G1",
+ @".NewYork-RegularItalicG2" : @"Regular Italic G2",
+ @".NewYork-RegularItalicG3" : @"Regular Italic G3",
+ @".NewYork-RegularItalicG4" : @"Regular Italic G4",
+ @".NewYork-Semibold" : @"Semibold",
+ @".NewYork-SemiboldItalic" : @"Semibold Italic",
+ @".SFArabic-Black" : @"Black",
+ @".SFArabic-Bold" : @"Bold",
+ @".SFArabic-Heavy" : @"Heavy",
+ @".SFArabic-Light" : @"Light",
+ @".SFArabic-Medium" : @"Medium",
+ @".SFArabic-Regular" : @"Regular",
+ @".SFArabic-Semibold" : @"Semibold",
+ @".SFArabic-Thin" : @"Thin",
+ @".SFArabic-Ultralight" : @"Ultralight",
+ @".SFNS-Black" : @"Black",
+ @".SFNS-BlackItalic" : @"Black Italic",
+ @".SFNS-Bold" : @"Bold",
+ @".SFNS-BoldG1" : @"Bold G1",
+ @".SFNS-BoldG2" : @"Bold G2",
+ @".SFNS-BoldG3" : @"Bold G3",
+ @".SFNS-BoldG4" : @"Bold G4",
+ @".SFNS-BoldItalic" : @"Bold Italic",
+ @".SFNS-Heavy" : @"Heavy",
+ @".SFNS-HeavyG1" : @"Heavy G1",
+ @".SFNS-HeavyG2" : @"Heavy G2",
+ @".SFNS-HeavyG3" : @"Heavy G3",
+ @".SFNS-HeavyG4" : @"Heavy G4",
+ @".SFNS-HeavyItalic" : @"Heavy Italic",
+ @".SFNS-Light" : @"Light",
+ @".SFNS-LightG1" : @"Light G1",
+ @".SFNS-LightG2" : @"Light G2",
+ @".SFNS-LightG3" : @"Light G3",
+ @".SFNS-LightG4" : @"Light G4",
+ @".SFNS-LightItalic" : @"Light Italic",
+ @".SFNS-Medium" : @"Medium",
+ @".SFNS-MediumG1" : @"Medium G1",
+ @".SFNS-MediumG2" : @"Medium G2",
+ @".SFNS-MediumG3" : @"Medium G3",
+ @".SFNS-MediumG4" : @"Medium G4",
+ @".SFNS-MediumItalic" : @"Medium Italic",
+ @".SFNS-Regular" : @"Regular",
+ @".SFNS-RegularG1" : @"Regular G1",
+ @".SFNS-RegularG2" : @"Regular G2",
+ @".SFNS-RegularG3" : @"Regular G3",
+ @".SFNS-RegularG4" : @"Regular G4",
+ @".SFNS-RegularItalic" : @"Regular Italic",
+ @".SFNS-Semibold" : @"Semibold",
+ @".SFNS-SemiboldG1" : @"Semibold G1",
+ @".SFNS-SemiboldG2" : @"Semibold G2",
+ @".SFNS-SemiboldG3" : @"Semibold G3",
+ @".SFNS-SemiboldG4" : @"Semibold G4",
+ @".SFNS-SemiboldItalic" : @"Semibold Italic",
+ @".SFNS-Thin" : @"Thin",
+ @".SFNS-ThinG1" : @"Thin G1",
+ @".SFNS-ThinG2" : @"Thin G2",
+ @".SFNS-ThinG3" : @"Thin G3",
+ @".SFNS-ThinG4" : @"Thin G4",
+ @".SFNS-ThinItalic" : @"Thin Italic",
+ @".SFNS-Ultralight" : @"Ultralight",
+ @".SFNS-UltralightG1" : @"Ultralight G1",
+ @".SFNS-UltralightG2" : @"Ultralight G2",
+ @".SFNS-UltralightG3" : @"Ultralight G3",
+ @".SFNS-UltralightG4" : @"Ultralight G4",
+ @".SFNS-UltralightItalic" : @"Ultralight Italic",
+ @".SFNS-Ultrathin" : @"Ultrathin",
+ @".SFNS-UltrathinG1" : @"Ultrathin G1",
+ @".SFNS-UltrathinG2" : @"Ultrathin G2",
+ @".SFNS-UltrathinG3" : @"Ultrathin G3",
+ @".SFNS-UltrathinG4" : @"Ultrathin G4",
+ @".SFNS-UltrathinItalic" : @"Ultrathin Italic",
+ @".SFNSMono-Bold" : @"Bold",
+ @".SFNSMono-BoldItalic" : @"Bold Italic",
+ @".SFNSMono-Heavy" : @"Heavy",
+ @".SFNSMono-HeavyItalic" : @"Heavy Italic",
+ @".SFNSMono-Light" : @"Light",
+ @".SFNSMono-LightItalic" : @"Light Italic",
+ @".SFNSMono-Medium" : @"Medium",
+ @".SFNSMono-MediumItalic" : @"Medium Italic",
+ @".SFNSMono-Regular" : @"Regular",
+ @".SFNSMono-RegularItalic" : @"Regular Italic",
+ @".SFNSMono-Semibold" : @"Semibold",
+ @".SFNSMono-SemiboldItalic" : @"Semibold Italic",
+ @".SFCompact-BlackItalic" : @"BlackItalic",
+ @".SFCompact-Bold" : @"Bold",
+ @".SFCompact-BoldG1" : @"Bold G1",
+ @".SFCompact-BoldG2" : @"Bold G2",
+ @".SFCompact-BoldG3" : @"Bold G3",
+ @".SFCompact-BoldG4" : @"Bold G4",
+ @".SFCompact-BoldItalic" : @"Bold Italic",
+ @".SFCompact-BoldItalicG1" : @"Bold Italic G1",
+ @".SFCompact-BoldItalicG2" : @"Bold Italic G2",
+ @".SFCompact-BoldItalicG3" : @"Bold Italic G3",
+ @".SFCompact-BoldItalicG4" : @"Bold Italic G4",
+ @".SFCompact-Heavy" : @"Heavy",
+ @".SFCompact-HeavyG1" : @"Heavy G1",
+ @".SFCompact-HeavyG2" : @"Heavy G2",
+ @".SFCompact-HeavyG3" : @"Heavy G3",
+ @".SFCompact-HeavyG4" : @"Heavy G4",
+ @".SFCompact-HeavyItalic" : @"Heavy Italic",
+ @".SFCompact-HeavyItalicG1" : @"Heavy Italic G1",
+ @".SFCompact-HeavyItalicG2" : @"Heavy Italic G2",
+ @".SFCompact-HeavyItalicG3" : @"Heavy Italic G3",
+ @".SFCompact-HeavyItalicG4" : @"Heavy Italic G4",
+ @".SFCompact-Light" : @"Light",
+ @".SFCompact-LightG1" : @"Light G1",
+ @".SFCompact-LightG2" : @"Light G2",
+ @".SFCompact-LightG3" : @"Light G3",
+ @".SFCompact-LightG4" : @"Light G4",
+ @".SFCompact-LightItalic" : @"Light Italic",
+ @".SFCompact-LightItalicG1" : @"Light Italic G1",
+ @".SFCompact-LightItalicG2" : @"Light Italic G2",
+ @".SFCompact-LightItalicG3" : @"Light Italic G3",
+ @".SFCompact-LightItalicG4" : @"Light Italic G4",
+ @".SFCompact-Medium" : @"Medium",
+ @".SFCompact-MediumG1" : @"Medium G1",
+ @".SFCompact-MediumG2" : @"Medium G2",
+ @".SFCompact-MediumG3" : @"Medium G3",
+ @".SFCompact-MediumG4" : @"Medium G4",
+ @".SFCompact-MediumItalic" : @"Medium Italic",
+ @".SFCompact-MediumItalicG1" : @"Medium Italic G1",
+ @".SFCompact-MediumItalicG2" : @"Medium Italic G2",
+ @".SFCompact-MediumItalicG3" : @"Medium Italic G3",
+ @".SFCompact-MediumItalicG4" : @"Medium Italic G4",
+ @".SFCompact-Regular" : @"Regular",
+ @".SFCompact-RegularG1" : @"Regular G1",
+ @".SFCompact-RegularG2" : @"Regular G2",
+ @".SFCompact-RegularG3" : @"Regular G3",
+ @".SFCompact-RegularG4" : @"Regular G4",
+ @".SFCompact-RegularItalic" : @"Regular Italic",
+ @".SFCompact-RegularItalicG1" : @"Regular Italic G1",
+ @".SFCompact-RegularItalicG2" : @"Regular Italic G2",
+ @".SFCompact-RegularItalicG3" : @"Regular Italic G3",
+ @".SFCompact-RegularItalicG4" : @"Regular Italic G4",
+ @".SFCompact-Semibold" : @"Semibold",
+ @".SFCompact-SemiboldG1" : @"Semibold G1",
+ @".SFCompact-SemiboldG2" : @"Semibold G2",
+ @".SFCompact-SemiboldG3" : @"Semibold G3",
+ @".SFCompact-SemiboldG4" : @"Semibold G4",
+ @".SFCompact-SemiboldItalic" : @"Semibold Italic",
+ @".SFCompact-SemiboldItalicG1" : @"Semibold Italic G1",
+ @".SFCompact-SemiboldItalicG2" : @"Semibold Italic G2",
+ @".SFCompact-SemiboldItalicG3" : @"Semibold Italic G3",
+ @".SFCompact-SemiboldItalicG4" : @"Semibold Italic G4",
+ @".SFCompact-Thin" : @"Thin",
+ @".SFCompact-ThinG1" : @"Thin G1",
+ @".SFCompact-ThinG2" : @"Thin G2",
+ @".SFCompact-ThinG3" : @"Thin G3",
+ @".SFCompact-ThinG4" : @"Thin G4",
+ @".SFCompact-ThinItalic" : @"Thin Italic",
+ @".SFCompact-ThinItalicG1" : @"Thin Italic G1",
+ @".SFCompact-ThinItalicG2" : @"Thin Italic G2",
+ @".SFCompact-ThinItalicG3" : @"Thin Italic G3",
+ @".SFCompact-ThinItalicG4" : @"Thin Italic G4",
+ @".SFCompact-Ultralight" : @"Ultralight",
+ @".SFCompact-UltralightG1" : @"Ultralight G1",
+ @".SFCompact-UltralightG2" : @"Ultralight G2",
+ @".SFCompact-UltralightG3" : @"Ultralight G3",
+ @".SFCompact-UltralightG4" : @"Ultralight G4",
+ @".SFCompact-UltralightItalic" : @"Ultralight Italic",
+ @".SFCompact-UltralightItalicG1" : @"Ultralight Italic G1",
+ @".SFCompact-UltralightItalicG2" : @"Ultralight Italic G2",
+ @".SFCompact-UltralightItalicG3" : @"Ultralight Italic G3",
+ @".SFCompact-UltralightItalicG4" : @"Ultralight Italic G4",
+ @".SFNS-CompressedBlack" : @"Compressed Black",
+ @".SFNS-CompressedBold" : @"Compressed Bold",
+ @".SFNS-CompressedBoldG1" : @"Compressed Bold G1",
+ @".SFNS-CompressedBoldG2" : @"Compressed Bold G2",
+ @".SFNS-CompressedBoldG3" : @"Compressed Bold G3",
+ @".SFNS-CompressedBoldG4" : @"Compressed Bold G4",
+ @".SFNS-CompressedHeavy" : @"Compressed Heavy",
+ @".SFNS-CompressedHeavyG1" : @"Compressed Heavy G1",
+ @".SFNS-CompressedHeavyG2" : @"Compressed Heavy G2",
+ @".SFNS-CompressedHeavyG3" : @"Compressed Heavy G3",
+ @".SFNS-CompressedHeavyG4" : @"Compressed Heavy G4",
+ @".SFNS-CompressedLight" : @"Compressed Light",
+ @".SFNS-CompressedLightG1" : @"Compressed Light G1",
+ @".SFNS-CompressedLightG2" : @"Compressed Light G2",
+ @".SFNS-CompressedLightG3" : @"Compressed Light G3",
+ @".SFNS-CompressedLightG4" : @"Compressed Light G4",
+ @".SFNS-CompressedMedium" : @"Compressed Medium",
+ @".SFNS-CompressedMediumG1" : @"Compressed Medium G1",
+ @".SFNS-CompressedMediumG2" : @"Compressed Medium G2",
+ @".SFNS-CompressedMediumG3" : @"Compressed Medium G3",
+ @".SFNS-CompressedMediumG4" : @"Compressed Medium G4",
+ @".SFNS-CompressedRegular" : @"Compressed Regular",
+ @".SFNS-CompressedRegularG1" : @"Compressed Regular G1",
+ @".SFNS-CompressedRegularG2" : @"Compressed Regular G2",
+ @".SFNS-CompressedRegularG3" : @"Compressed Regular G3",
+ @".SFNS-CompressedRegularG4" : @"Compressed Regular G4",
+ @".SFNS-CompressedSemibold" : @"Compressed Semibold",
+ @".SFNS-CompressedSemiboldG1" : @"Compressed Semibold G1",
+ @".SFNS-CompressedSemiboldG2" : @"Compressed Semibold G2",
+ @".SFNS-CompressedSemiboldG3" : @"Compressed Semibold G3",
+ @".SFNS-CompressedSemiboldG4" : @"Compressed Semibold G4",
+ @".SFNS-CompressedThin" : @"Compressed Thin",
+ @".SFNS-CompressedThinG1" : @"Compressed Thin G1",
+ @".SFNS-CompressedThinG2" : @"Compressed Thin G2",
+ @".SFNS-CompressedThinG3" : @"Compressed Thin G3",
+ @".SFNS-CompressedThinG4" : @"Compressed Thin G4",
+ @".SFNS-CompressedUltralight" : @"Compressed Ultralight",
+ @".SFNS-CompressedUltralightG1" : @"Compressed Ultralight G1",
+ @".SFNS-CompressedUltralightG2" : @"Compressed Ultralight G2",
+ @".SFNS-CompressedUltralightG3" : @"Compressed Ultralight G3",
+ @".SFNS-CompressedUltralightG4" : @"Compressed Ultralight G4",
+ @".SFNS-CondensedBlack" : @"Condensed Black",
+ @".SFNS-CondensedBold" : @"Condensed Bold",
+ @".SFNS-CondensedBoldG1" : @"Condensed Bold G1",
+ @".SFNS-CondensedBoldG2" : @"Condensed Bold G2",
+ @".SFNS-CondensedBoldG3" : @"Condensed Bold G3",
+ @".SFNS-CondensedBoldG4" : @"Condensed Bold G4",
+ @".SFNS-CondensedHeavy" : @"Condensed Heavy",
+ @".SFNS-CondensedHeavyG1" : @"Condensed Heavy G1",
+ @".SFNS-CondensedHeavyG2" : @"Condensed Heavy G2",
+ @".SFNS-CondensedHeavyG3" : @"Condensed Heavy G3",
+ @".SFNS-CondensedHeavyG4" : @"Condensed Heavy G4",
+ @".SFNS-CondensedLight" : @"Condensed Light",
+ @".SFNS-CondensedLightG1" : @"Condensed Light G1",
+ @".SFNS-CondensedLightG2" : @"Condensed Light G2",
+ @".SFNS-CondensedLightG3" : @"Condensed Light G3",
+ @".SFNS-CondensedLightG4" : @"Condensed Light G4",
+ @".SFNS-CondensedMedium" : @"Condensed Medium",
+ @".SFNS-CondensedMediumG1" : @"Condensed Medium G1",
+ @".SFNS-CondensedMediumG2" : @"Condensed Medium G2",
+ @".SFNS-CondensedMediumG3" : @"Condensed Medium G3",
+ @".SFNS-CondensedMediumG4" : @"Condensed Medium G4",
+ @".SFNS-CondensedRegular" : @"Condensed Regular",
+ @".SFNS-CondensedRegularG1" : @"Condensed Regular G1",
+ @".SFNS-CondensedRegularG2" : @"Condensed Regular G2",
+ @".SFNS-CondensedRegularG3" : @"Condensed Regular G3",
+ @".SFNS-CondensedRegularG4" : @"Condensed Regular G4",
+ @".SFNS-CondensedSemibold" : @"Condensed Semibold",
+ @".SFNS-CondensedSemiboldG1" : @"Condensed Semibold G1",
+ @".SFNS-CondensedSemiboldG2" : @"Condensed Semibold G2",
+ @".SFNS-CondensedSemiboldG3" : @"Condensed Semibold G3",
+ @".SFNS-CondensedSemiboldG4" : @"Condensed Semibold G4",
+ @".SFNS-CondensedThin" : @"Condensed Thin",
+ @".SFNS-CondensedThinG1" : @"Condensed Thin G1",
+ @".SFNS-CondensedThinG2" : @"Condensed Thin G2",
+ @".SFNS-CondensedThinG3" : @"Condensed Thin G3",
+ @".SFNS-CondensedThinG4" : @"Condensed Thin G4",
+ @".SFNS-CondensedUltralight" : @"Condensed Ultralight",
+ @".SFNS-CondensedUltralightG1" : @"Condensed Ultralight G1",
+ @".SFNS-CondensedUltralightG2" : @"Condensed Ultralight G2",
+ @".SFNS-CondensedUltralightG3" : @"Condensed Ultralight G3",
+ @".SFNS-CondensedUltralightG4" : @"Condensed Ultralight G4",
+ @".SFNS-ExpandedBlack" : @"Expanded Black",
+ @".SFNS-ExpandedBold" : @"Expanded Bold",
+ @".SFNS-ExpandedBoldG1" : @"Expanded Bold G1",
+ @".SFNS-ExpandedBoldG2" : @"Expanded Bold G2",
+ @".SFNS-ExpandedBoldG3" : @"Expanded Bold G3",
+ @".SFNS-ExpandedBoldG4" : @"Expanded Bold G4",
+ @".SFNS-ExpandedHeavy" : @"Expanded Heavy",
+ @".SFNS-ExpandedHeavyG1" : @"Expanded Heavy G1",
+ @".SFNS-ExpandedHeavyG2" : @"Expanded Heavy G2",
+ @".SFNS-ExpandedHeavyG3" : @"Expanded Heavy G3",
+ @".SFNS-ExpandedHeavyG4" : @"Expanded Heavy G4",
+ @".SFNS-ExpandedLight" : @"Expanded Light",
+ @".SFNS-ExpandedLightG1" : @"Expanded Light G1",
+ @".SFNS-ExpandedLightG2" : @"Expanded Light G2",
+ @".SFNS-ExpandedLightG3" : @"Expanded Light G3",
+ @".SFNS-ExpandedLightG4" : @"Expanded Light G4",
+ @".SFNS-ExpandedMedium" : @"Expanded Medium",
+ @".SFNS-ExpandedMediumG1" : @"Expanded Medium G1",
+ @".SFNS-ExpandedMediumG2" : @"Expanded Medium G2",
+ @".SFNS-ExpandedMediumG3" : @"Expanded Medium G3",
+ @".SFNS-ExpandedMediumG4" : @"Expanded Medium G4",
+ @".SFNS-ExpandedRegular" : @"Expanded Regular",
+ @".SFNS-ExpandedRegularG1" : @"Expanded Regular G1",
+ @".SFNS-ExpandedRegularG2" : @"Expanded Regular G2",
+ @".SFNS-ExpandedRegularG3" : @"Expanded Regular G3",
+ @".SFNS-ExpandedRegularG4" : @"Expanded Regular G4",
+ @".SFNS-ExpandedSemibold" : @"Expanded Semibold",
+ @".SFNS-ExpandedSemiboldG1" : @"Expanded Semibold G1",
+ @".SFNS-ExpandedSemiboldG2" : @"Expanded Semibold G2",
+ @".SFNS-ExpandedSemiboldG3" : @"Expanded Semibold G3",
+ @".SFNS-ExpandedSemiboldG4" : @"Expanded Semibold G4",
+ @".SFNS-ExpandedThin" : @"Expanded Thin",
+ @".SFNS-ExpandedThinG1" : @"Expanded Thin G1",
+ @".SFNS-ExpandedThinG2" : @"Expanded Thin G2",
+ @".SFNS-ExpandedThinG3" : @"Expanded Thin G3",
+ @".SFNS-ExpandedThinG4" : @"Expanded Thin G4",
+ @".SFNS-ExpandedUltralight" : @"Expanded Ultralight",
+ @".SFNS-ExpandedUltralightG1" : @"Expanded Ultralight G1",
+ @".SFNS-ExpandedUltralightG2" : @"Expanded Ultralight G2",
+ @".SFNS-ExpandedUltralightG3" : @"Expanded Ultralight G3",
+ @".SFNS-ExpandedUltralightG4" : @"Expanded Ultralight G4",
+ @".SFNS-ExtraCompressedBlack" : @"ExtraCompressed Black",
+ @".SFNS-ExtraCompressedBold" : @"ExtraCompressed Bold",
+ @".SFNS-ExtraCompressedBoldG1" : @"ExtraCompressed Bold G1",
+ @".SFNS-ExtraCompressedBoldG2" : @"ExtraCompressed Bold G2",
+ @".SFNS-ExtraCompressedBoldG3" : @"ExtraCompressed Bold G3",
+ @".SFNS-ExtraCompressedBoldG4" : @"ExtraCompressed Bold G4",
+ @".SFNS-ExtraCompressedHeavy" : @"ExtraCompressed Heavy",
+ @".SFNS-ExtraCompressedHeavyG1" : @"ExtraCompressed Heavy G1",
+ @".SFNS-ExtraCompressedHeavyG2" : @"ExtraCompressed Heavy G2",
+ @".SFNS-ExtraCompressedHeavyG3" : @"ExtraCompressed Heavy G3",
+ @".SFNS-ExtraCompressedHeavyG4" : @"ExtraCompressed Heavy G4",
+ @".SFNS-ExtraCompressedLight" : @"ExtraCompressed Light",
+ @".SFNS-ExtraCompressedLightG1" : @"ExtraCompressed Light G1",
+ @".SFNS-ExtraCompressedLightG2" : @"ExtraCompressed Light G2",
+ @".SFNS-ExtraCompressedLightG3" : @"ExtraCompressed Light G3",
+ @".SFNS-ExtraCompressedLightG4" : @"ExtraCompressed Light G4",
+ @".SFNS-ExtraCompressedMedium" : @"ExtraCompressed Medium",
+ @".SFNS-ExtraCompressedMediumG1" : @"ExtraCompressed Medium G1",
+ @".SFNS-ExtraCompressedMediumG2" : @"ExtraCompressed Medium G2",
+ @".SFNS-ExtraCompressedMediumG3" : @"ExtraCompressed Medium G3",
+ @".SFNS-ExtraCompressedMediumG4" : @"ExtraCompressed Medium G4",
+ @".SFNS-ExtraCompressedRegular" : @"ExtraCompressed Regular",
+ @".SFNS-ExtraCompressedRegularG1" : @"ExtraCompressed Regular G1",
+ @".SFNS-ExtraCompressedRegularG2" : @"ExtraCompressed Regular G2",
+ @".SFNS-ExtraCompressedRegularG3" : @"ExtraCompressed Regular G3",
+ @".SFNS-ExtraCompressedRegularG4" : @"ExtraCompressed Regular G4",
+ @".SFNS-ExtraCompressedSemibold" : @"ExtraCompressed Semibold",
+ @".SFNS-ExtraCompressedSemiboldG1" : @"ExtraCompressed Semibold G1",
+ @".SFNS-ExtraCompressedSemiboldG2" : @"ExtraCompressed Semibold G2",
+ @".SFNS-ExtraCompressedSemiboldG3" : @"ExtraCompressed Semibold G3",
+ @".SFNS-ExtraCompressedSemiboldG4" : @"ExtraCompressed Semibold G4",
+ @".SFNS-ExtraCompressedThin" : @"ExtraCompressed Thin",
+ @".SFNS-ExtraCompressedThinG1" : @"ExtraCompressed Thin G1",
+ @".SFNS-ExtraCompressedThinG2" : @"ExtraCompressed Thin G2",
+ @".SFNS-ExtraCompressedThinG3" : @"ExtraCompressed Thin G3",
+ @".SFNS-ExtraCompressedThinG4" : @"ExtraCompressed Thin G4",
+ @".SFNS-ExtraCompressedUltralight" : @"ExtraCompressed Ultralight",
+ @".SFNS-ExtraCompressedUltralightG1" : @"ExtraCompressed Ultralight G1",
+ @".SFNS-ExtraCompressedUltralightG2" : @"ExtraCompressed Ultralight G2",
+ @".SFNS-ExtraCompressedUltralightG3" : @"ExtraCompressed Ultralight G3",
+ @".SFNS-ExtraCompressedUltralightG4" : @"ExtraCompressed Ultralight G4",
+ @".SFNS-ExtraExpandedBlack" : @"ExtraExpanded Black",
+ @".SFNS-ExtraExpandedBold" : @"ExtraExpanded Bold",
+ @".SFNS-ExtraExpandedBoldG1" : @"ExtraExpanded Bold G1",
+ @".SFNS-ExtraExpandedBoldG2" : @"ExtraExpanded Bold G2",
+ @".SFNS-ExtraExpandedBoldG3" : @"ExtraExpanded Bold G3",
+ @".SFNS-ExtraExpandedBoldG4" : @"ExtraExpanded Bold G4",
+ @".SFNS-ExtraExpandedHeavy" : @"ExtraExpanded Heavy",
+ @".SFNS-ExtraExpandedHeavyG1" : @"ExtraExpanded Heavy G1",
+ @".SFNS-ExtraExpandedHeavyG2" : @"ExtraExpanded Heavy G2",
+ @".SFNS-ExtraExpandedHeavyG3" : @"ExtraExpanded Heavy G3",
+ @".SFNS-ExtraExpandedHeavyG4" : @"ExtraExpanded Heavy G4",
+ @".SFNS-ExtraExpandedLight" : @"ExtraExpanded Light",
+ @".SFNS-ExtraExpandedLightG1" : @"ExtraExpanded Light G1",
+ @".SFNS-ExtraExpandedLightG2" : @"ExtraExpanded Light G2",
+ @".SFNS-ExtraExpandedLightG3" : @"ExtraExpanded Light G3",
+ @".SFNS-ExtraExpandedLightG4" : @"ExtraExpanded Light G4",
+ @".SFNS-ExtraExpandedMedium" : @"ExtraExpanded Medium",
+ @".SFNS-ExtraExpandedMediumG1" : @"ExtraExpanded Medium G1",
+ @".SFNS-ExtraExpandedMediumG2" : @"ExtraExpanded Medium G2",
+ @".SFNS-ExtraExpandedMediumG3" : @"ExtraExpanded Medium G3",
+ @".SFNS-ExtraExpandedMediumG4" : @"ExtraExpanded Medium G4",
+ @".SFNS-ExtraExpandedRegular" : @"ExtraExpanded Regular",
+ @".SFNS-ExtraExpandedRegularG1" : @"ExtraExpanded Regular G1",
+ @".SFNS-ExtraExpandedRegularG2" : @"ExtraExpanded Regular G2",
+ @".SFNS-ExtraExpandedRegularG3" : @"ExtraExpanded Regular G3",
+ @".SFNS-ExtraExpandedRegularG4" : @"ExtraExpanded Regular G4",
+ @".SFNS-ExtraExpandedSemibold" : @"ExtraExpanded Semibold",
+ @".SFNS-ExtraExpandedSemiboldG1" : @"ExtraExpanded Semibold G1",
+ @".SFNS-ExtraExpandedSemiboldG2" : @"ExtraExpanded Semibold G2",
+ @".SFNS-ExtraExpandedSemiboldG3" : @"ExtraExpanded Semibold G3",
+ @".SFNS-ExtraExpandedSemiboldG4" : @"ExtraExpanded Semibold G4",
+ @".SFNS-ExtraExpandedThin" : @"ExtraExpanded Thin",
+ @".SFNS-ExtraExpandedThinG1" : @"ExtraExpanded Thin G1",
+ @".SFNS-ExtraExpandedThinG2" : @"ExtraExpanded Thin G2",
+ @".SFNS-ExtraExpandedThinG3" : @"ExtraExpanded Thin G3",
+ @".SFNS-ExtraExpandedThinG4" : @"ExtraExpanded Thin G4",
+ @".SFNS-ExtraExpandedUltralight" : @"ExtraExpanded Ultralight",
+ @".SFNS-ExtraExpandedUltralightG1" : @"ExtraExpanded Ultralight G1",
+ @".SFNS-ExtraExpandedUltralightG2" : @"ExtraExpanded Ultralight G2",
+ @".SFNS-ExtraExpandedUltralightG3" : @"ExtraExpanded Ultralight G3",
+ @".SFNS-ExtraExpandedUltralightG4" : @"ExtraExpanded Ultralight G4",
+ @".SFNS-SemiCondensedBlack" : @"SemiCondensed Black",
+ @".SFNS-SemiCondensedBold" : @"SemiCondensed Bold",
+ @".SFNS-SemiCondensedBoldG1" : @"SemiCondensed Bold G1",
+ @".SFNS-SemiCondensedBoldG2" : @"SemiCondensed Bold G2",
+ @".SFNS-SemiCondensedBoldG3" : @"SemiCondensed Bold G3",
+ @".SFNS-SemiCondensedBoldG4" : @"SemiCondensed Bold G4",
+ @".SFNS-SemiCondensedHeavy" : @"SemiCondensed Heavy",
+ @".SFNS-SemiCondensedHeavyG1" : @"SemiCondensed Heavy G1",
+ @".SFNS-SemiCondensedHeavyG2" : @"SemiCondensed Heavy G2",
+ @".SFNS-SemiCondensedHeavyG3" : @"SemiCondensed Heavy G3",
+ @".SFNS-SemiCondensedHeavyG4" : @"SemiCondensed Heavy G4",
+ @".SFNS-SemiCondensedLight" : @"SemiCondensed Light",
+ @".SFNS-SemiCondensedLightG1" : @"SemiCondensed Light G1",
+ @".SFNS-SemiCondensedLightG2" : @"SemiCondensed Light G2",
+ @".SFNS-SemiCondensedLightG3" : @"SemiCondensed Light G3",
+ @".SFNS-SemiCondensedLightG4" : @"SemiCondensed Light G4",
+ @".SFNS-SemiCondensedMedium" : @"SemiCondensed Medium",
+ @".SFNS-SemiCondensedMediumG1" : @"SemiCondensed Medium G1",
+ @".SFNS-SemiCondensedMediumG2" : @"SemiCondensed Medium G2",
+ @".SFNS-SemiCondensedMediumG3" : @"SemiCondensed Medium G3",
+ @".SFNS-SemiCondensedMediumG4" : @"SemiCondensed Medium G4",
+ @".SFNS-SemiCondensedRegular" : @"SemiCondensed Regular",
+ @".SFNS-SemiCondensedRegularG1" : @"SemiCondensed Regular G1",
+ @".SFNS-SemiCondensedRegularG2" : @"SemiCondensed Regular G2",
+ @".SFNS-SemiCondensedRegularG3" : @"SemiCondensed Regular G3",
+ @".SFNS-SemiCondensedRegularG4" : @"SemiCondensed Regular G4",
+ @".SFNS-SemiCondensedSemibold" : @"SemiCondensed Semibold",
+ @".SFNS-SemiCondensedSemiboldG1" : @"SemiCondensed Semibold G1",
+ @".SFNS-SemiCondensedSemiboldG2" : @"SemiCondensed Semibold G2",
+ @".SFNS-SemiCondensedSemiboldG3" : @"SemiCondensed Semibold G3",
+ @".SFNS-SemiCondensedSemiboldG4" : @"SemiCondensed Semibold G4",
+ @".SFNS-SemiCondensedThin" : @"SemiCondensed Thin",
+ @".SFNS-SemiCondensedThinG1" : @"SemiCondensed Thin G1",
+ @".SFNS-SemiCondensedThinG2" : @"SemiCondensed Thin G2",
+ @".SFNS-SemiCondensedThinG3" : @"SemiCondensed Thin G3",
+ @".SFNS-SemiCondensedThinG4" : @"SemiCondensed Thin G4",
+ @".SFNS-SemiCondensedUltralight" : @"SemiCondensed Ultralight",
+ @".SFNS-SemiCondensedUltralightG1" : @"SemiCondensed Ultralight G1",
+ @".SFNS-SemiCondensedUltralightG2" : @"SemiCondensed Ultralight G2",
+ @".SFNS-SemiCondensedUltralightG3" : @"SemiCondensed Ultralight G3",
+ @".SFNS-SemiCondensedUltralightG4" : @"SemiCondensed Ultralight G4",
+ @".SFNS-SemiExpandedBlack" : @"SemiExpanded Black",
+ @".SFNS-SemiExpandedBold" : @"SemiExpanded Bold",
+ @".SFNS-SemiExpandedBoldG1" : @"SemiExpanded Bold G1",
+ @".SFNS-SemiExpandedBoldG2" : @"SemiExpanded Bold G2",
+ @".SFNS-SemiExpandedBoldG3" : @"SemiExpanded Bold G3",
+ @".SFNS-SemiExpandedBoldG4" : @"SemiExpanded Bold G4",
+ @".SFNS-SemiExpandedHeavy" : @"SemiExpanded Heavy",
+ @".SFNS-SemiExpandedHeavyG1" : @"SemiExpanded Heavy G1",
+ @".SFNS-SemiExpandedHeavyG2" : @"SemiExpanded Heavy G2",
+ @".SFNS-SemiExpandedHeavyG3" : @"SemiExpanded Heavy G3",
+ @".SFNS-SemiExpandedHeavyG4" : @"SemiExpanded Heavy G4",
+ @".SFNS-SemiExpandedLight" : @"SemiExpanded Light",
+ @".SFNS-SemiExpandedLightG1" : @"SemiExpanded Light G1",
+ @".SFNS-SemiExpandedLightG2" : @"SemiExpanded Light G2",
+ @".SFNS-SemiExpandedLightG3" : @"SemiExpanded Light G3",
+ @".SFNS-SemiExpandedLightG4" : @"SemiExpanded Light G4",
+ @".SFNS-SemiExpandedMedium" : @"SemiExpanded Medium",
+ @".SFNS-SemiExpandedMediumG1" : @"SemiExpanded Medium G1",
+ @".SFNS-SemiExpandedMediumG2" : @"SemiExpanded Medium G2",
+ @".SFNS-SemiExpandedMediumG3" : @"SemiExpanded Medium G3",
+ @".SFNS-SemiExpandedMediumG4" : @"SemiExpanded Medium G4",
+ @".SFNS-SemiExpandedRegular" : @"SemiExpanded Regular",
+ @".SFNS-SemiExpandedRegularG1" : @"SemiExpanded Regular G1",
+ @".SFNS-SemiExpandedRegularG2" : @"SemiExpanded Regular G2",
+ @".SFNS-SemiExpandedRegularG3" : @"SemiExpanded Regular G3",
+ @".SFNS-SemiExpandedRegularG4" : @"SemiExpanded Regular G4",
+ @".SFNS-SemiExpandedSemibold" : @"SemiExpanded Semibold",
+ @".SFNS-SemiExpandedSemiboldG1" : @"SemiExpanded Semibold G1",
+ @".SFNS-SemiExpandedSemiboldG2" : @"SemiExpanded Semibold G2",
+ @".SFNS-SemiExpandedSemiboldG3" : @"SemiExpanded Semibold G3",
+ @".SFNS-SemiExpandedSemiboldG4" : @"SemiExpanded Semibold G4",
+ @".SFNS-SemiExpandedThin" : @"SemiExpanded Thin",
+ @".SFNS-SemiExpandedThinG1" : @"SemiExpanded Thin G1",
+ @".SFNS-SemiExpandedThinG2" : @"SemiExpanded Thin G2",
+ @".SFNS-SemiExpandedThinG3" : @"SemiExpanded Thin G3",
+ @".SFNS-SemiExpandedThinG4" : @"SemiExpanded Thin G4",
+ @".SFNS-SemiExpandedUltralight" : @"SemiExpanded Ultralight",
+ @".SFNS-SemiExpandedUltralightG1" : @"SemiExpanded Ultralight G1",
+ @".SFNS-SemiExpandedUltralightG2" : @"SemiExpanded Ultralight G2",
+ @".SFNS-SemiExpandedUltralightG3" : @"SemiExpanded Ultralight G3",
+ @".SFNS-SemiExpandedUltralightG4" : @"SemiExpanded Ultralight G4",
+ @".SFNS-UltraCompressedBlack" : @"UltraCompressed Black",
+ @".SFNS-UltraCompressedBold" : @"UltraCompressed Bold",
+ @".SFNS-UltraCompressedBoldG1" : @"UltraCompressed Bold G1",
+ @".SFNS-UltraCompressedBoldG2" : @"UltraCompressed Bold G2",
+ @".SFNS-UltraCompressedBoldG3" : @"UltraCompressed Bold G3",
+ @".SFNS-UltraCompressedBoldG4" : @"UltraCompressed Bold G4",
+ @".SFNS-UltraCompressedHeavy" : @"UltraCompressed Heavy",
+ @".SFNS-UltraCompressedHeavyG1" : @"UltraCompressed Heavy G1",
+ @".SFNS-UltraCompressedHeavyG2" : @"UltraCompressed Heavy G2",
+ @".SFNS-UltraCompressedHeavyG3" : @"UltraCompressed Heavy G3",
+ @".SFNS-UltraCompressedHeavyG4" : @"UltraCompressed Heavy G4",
+ @".SFNS-UltraCompressedLight" : @"UltraCompressed Light",
+ @".SFNS-UltraCompressedLightG1" : @"UltraCompressed Light G1",
+ @".SFNS-UltraCompressedLightG2" : @"UltraCompressed Light G2",
+ @".SFNS-UltraCompressedLightG3" : @"UltraCompressed Light G3",
+ @".SFNS-UltraCompressedLightG4" : @"UltraCompressed Light G4",
+ @".SFNS-UltraCompressedMedium" : @"UltraCompressed Medium",
+ @".SFNS-UltraCompressedMediumG1" : @"UltraCompressed Medium G1",
+ @".SFNS-UltraCompressedMediumG2" : @"UltraCompressed Medium G2",
+ @".SFNS-UltraCompressedMediumG3" : @"UltraCompressed Medium G3",
+ @".SFNS-UltraCompressedMediumG4" : @"UltraCompressed Medium G4",
+ @".SFNS-UltraCompressedRegular" : @"UltraCompressed Regular",
+ @".SFNS-UltraCompressedRegularG1" : @"UltraCompressed Regular G1",
+ @".SFNS-UltraCompressedRegularG2" : @"UltraCompressed Regular G2",
+ @".SFNS-UltraCompressedRegularG3" : @"UltraCompressed Regular G3",
+ @".SFNS-UltraCompressedRegularG4" : @"UltraCompressed Regular G4",
+ @".SFNS-UltraCompressedSemibold" : @"UltraCompressed Semibold",
+ @".SFNS-UltraCompressedSemiboldG1" : @"UltraCompressed Semibold G1",
+ @".SFNS-UltraCompressedSemiboldG2" : @"UltraCompressed Semibold G2",
+ @".SFNS-UltraCompressedSemiboldG3" : @"UltraCompressed Semibold G3",
+ @".SFNS-UltraCompressedSemiboldG4" : @"UltraCompressed Semibold G4",
+ @".SFNS-UltraCompressedThin" : @"UltraCompressed Thin",
+ @".SFNS-UltraCompressedThinG1" : @"UltraCompressed Thin G1",
+ @".SFNS-UltraCompressedThinG2" : @"UltraCompressed Thin G2",
+ @".SFNS-UltraCompressedThinG3" : @"UltraCompressed Thin G3",
+ @".SFNS-UltraCompressedThinG4" : @"UltraCompressed Thin G4",
+ @".SFNS-UltraCompressedUltralight" : @"UltraCompressed Ultralight",
+ @".SFNS-UltraCompressedUltralightG1" : @"UltraCompressed Ultralight G1",
+ @".SFNS-UltraCompressedUltralightG2" : @"UltraCompressed Ultralight G2",
+ @".SFNS-UltraCompressedUltralightG3" : @"UltraCompressed Ultralight G3",
+ @".SFNS-UltraCompressedUltralightG4" : @"UltraCompressed Ultralight G4",
+ @".Keyboard" : @"Regular",
+ @"AlBayan" : @"Plain",
+ @"AlBayan-Bold" : @"Bold",
+ @"AlNile" : @"Regular",
+ @"AlNile-Bold" : @"Bold",
+ @"AlTarikh" : @"Regular",
+ @"AmericanTypewriter" : @"Regular",
+ @"AmericanTypewriter-Bold" : @"Bold",
+ @"AmericanTypewriter-Condensed" : @"Condensed",
+ @"AmericanTypewriter-CondensedBold" : @"Condensed Bold",
+ @"AmericanTypewriter-CondensedLight" : @"Condensed Light",
+ @"AmericanTypewriter-Light" : @"Light",
+ @"AmericanTypewriter-Semibold" : @"Semibold",
+ @"AndaleMono" : @"Regular",
+ @"Apple-Chancery" : @"Chancery",
+ @"AppleBraille" : @"Regular",
+ @"AppleBraille-Outline6Dot" : @"Outline 6 Dot",
+ @"AppleBraille-Outline8Dot" : @"Outline 8 Dot",
+ @"AppleBraille-Pinpoint6Dot" : @"Pinpoint 6 Dot",
+ @"AppleBraille-Pinpoint8Dot" : @"Pinpoint 8 Dot",
+ @"AppleColorEmoji" : @"Regular",
+ @"AppleGothic" : @"Regular",
+ @"AppleMyungjo" : @"Regular",
+ @"AppleSDGothicNeo-Bold" : @"Bold",
+ @"AppleSDGothicNeo-ExtraBold" : @"ExtraBold",
+ @"AppleSDGothicNeo-Heavy" : @"Heavy",
+ @"AppleSDGothicNeo-Light" : @"Light",
+ @"AppleSDGothicNeo-Medium" : @"Medium",
+ @"AppleSDGothicNeo-Regular" : @"Regular",
+ @"AppleSDGothicNeo-SemiBold" : @"SemiBold",
+ @"AppleSDGothicNeo-Thin" : @"Thin",
+ @"AppleSDGothicNeo-UltraLight" : @"UltraLight",
+ @"AppleSymbols" : @"Regular",
+ @"AquaKana" : @"Regular",
+ @"AquaKana-Bold" : @"Bold",
+ @"Arial-Black" : @"Regular",
+ @"Arial-BoldItalicMT" : @"Bold Italic",
+ @"Arial-BoldMT" : @"Bold",
+ @"Arial-ItalicMT" : @"Italic",
+ @"ArialHebrew" : @"Regular",
+ @"ArialHebrew-Bold" : @"Bold",
+ @"ArialHebrew-Light" : @"Light",
+ @"ArialHebrewScholar" : @"Regular",
+ @"ArialHebrewScholar-Bold" : @"Bold",
+ @"ArialHebrewScholar-Light" : @"Light",
+ @"ArialMT" : @"Regular",
+ @"ArialNarrow" : @"Regular",
+ @"ArialNarrow-Bold" : @"Bold",
+ @"ArialNarrow-BoldItalic" : @"Bold Italic",
+ @"ArialNarrow-Italic" : @"Italic",
+ @"ArialRoundedMTBold" : @"Regular",
+ @"ArialUnicodeMS" : @"Regular",
+ @"Athelas-Bold" : @"Bold",
+ @"Athelas-BoldItalic" : @"Bold Italic",
+ @"Athelas-Italic" : @"Italic",
+ @"Athelas-Regular" : @"Regular",
+ @"Avenir-Black" : @"Black",
+ @"Avenir-BlackOblique" : @"Black Oblique",
+ @"Avenir-Book" : @"Book",
+ @"Avenir-BookOblique" : @"Book Oblique",
+ @"Avenir-Heavy" : @"Heavy",
+ @"Avenir-HeavyOblique" : @"Heavy Oblique",
+ @"Avenir-Light" : @"Light",
+ @"Avenir-LightOblique" : @"Light Oblique",
+ @"Avenir-Medium" : @"Medium",
+ @"Avenir-MediumOblique" : @"Medium Oblique",
+ @"Avenir-Oblique" : @"Oblique",
+ @"Avenir-Roman" : @"Roman",
+ @"AvenirNext-Bold" : @"Bold",
+ @"AvenirNext-BoldItalic" : @"Bold Italic",
+ @"AvenirNext-DemiBold" : @"Demi Bold",
+ @"AvenirNext-DemiBoldItalic" : @"Demi Bold Italic",
+ @"AvenirNext-Heavy" : @"Heavy",
+ @"AvenirNext-HeavyItalic" : @"Heavy Italic",
+ @"AvenirNext-Italic" : @"Italic",
+ @"AvenirNext-Medium" : @"Medium",
+ @"AvenirNext-MediumItalic" : @"Medium Italic",
+ @"AvenirNext-Regular" : @"Regular",
+ @"AvenirNext-UltraLight" : @"Ultra Light",
+ @"AvenirNext-UltraLightItalic" : @"Ultra Light Italic",
+ @"AvenirNextCondensed-Bold" : @"Bold",
+ @"AvenirNextCondensed-BoldItalic" : @"Bold Italic",
+ @"AvenirNextCondensed-DemiBold" : @"Demi Bold",
+ @"AvenirNextCondensed-DemiBoldItalic" : @"Demi Bold Italic",
+ @"AvenirNextCondensed-Heavy" : @"Heavy",
+ @"AvenirNextCondensed-HeavyItalic" : @"Heavy Italic",
+ @"AvenirNextCondensed-Italic" : @"Italic",
+ @"AvenirNextCondensed-Medium" : @"Medium",
+ @"AvenirNextCondensed-MediumItalic" : @"Medium Italic",
+ @"AvenirNextCondensed-Regular" : @"Regular",
+ @"AvenirNextCondensed-UltraLight" : @"Ultra Light",
+ @"AvenirNextCondensed-UltraLightItalic" : @"Ultra Light Italic",
+ @"Ayuthaya" : @"Regular",
+ @"Baghdad" : @"Regular",
+ @"BanglaMN" : @"Regular",
+ @"BanglaMN-Bold" : @"Bold",
+ @"BanglaSangamMN" : @"Regular",
+ @"BanglaSangamMN-Bold" : @"Bold",
+ @"Baskerville" : @"Regular",
+ @"Baskerville-Bold" : @"Bold",
+ @"Baskerville-BoldItalic" : @"Bold Italic",
+ @"Baskerville-Italic" : @"Italic",
+ @"Baskerville-SemiBold" : @"SemiBold",
+ @"Baskerville-SemiBoldItalic" : @"SemiBold Italic",
+ @"Beirut" : @"Regular",
+ @"BigCaslon-Medium" : @"Medium",
+ @"BodoniOrnamentsITCTT" : @"Regular",
+ @"BodoniSvtyTwoITCTT-Bold" : @"Bold",
+ @"BodoniSvtyTwoITCTT-Book" : @"Book",
+ @"BodoniSvtyTwoITCTT-BookIta" : @"Book Italic",
+ @"BodoniSvtyTwoOSITCTT-Bold" : @"Bold",
+ @"BodoniSvtyTwoOSITCTT-Book" : @"Book",
+ @"BodoniSvtyTwoOSITCTT-BookIt" : @"Book Italic",
+ @"BodoniSvtyTwoSCITCTT-Book" : @"Book",
+ @"BradleyHandITCTT-Bold" : @"Bold",
+ @"BrushScriptMT" : @"Italic",
+ @"Chalkboard" : @"Regular",
+ @"Chalkboard-Bold" : @"Bold",
+ @"ChalkboardSE-Bold" : @"Bold",
+ @"ChalkboardSE-Light" : @"Light",
+ @"ChalkboardSE-Regular" : @"Regular",
+ @"Chalkduster" : @"Regular",
+ @"Charter-Black" : @"Black",
+ @"Charter-BlackItalic" : @"Black Italic",
+ @"Charter-Bold" : @"Bold",
+ @"Charter-BoldItalic" : @"Bold Italic",
+ @"Charter-Italic" : @"Italic",
+ @"Charter-Roman" : @"Roman",
+ @"Cochin" : @"Regular",
+ @"Cochin-Bold" : @"Bold",
+ @"Cochin-BoldItalic" : @"Bold Italic",
+ @"Cochin-Italic" : @"Italic",
+ @"ComicSansMS" : @"Regular",
+ @"ComicSansMS-Bold" : @"Bold",
+ @"Copperplate" : @"Regular",
+ @"Copperplate-Bold" : @"Bold",
+ @"Copperplate-Light" : @"Light",
+ @"CorsivaHebrew" : @"Regular",
+ @"CorsivaHebrew-Bold" : @"Bold",
+ @"Courier" : @"Regular",
+ @"Courier-Bold" : @"Bold",
+ @"Courier-BoldOblique" : @"Bold Oblique",
+ @"Courier-Oblique" : @"Oblique",
+ @"CourierNewPS-BoldItalicMT" : @"Bold Italic",
+ @"CourierNewPS-BoldMT" : @"Bold",
+ @"CourierNewPS-ItalicMT" : @"Italic",
+ @"CourierNewPSMT" : @"Regular",
+ @"DFKaiShu-SB-Estd-BF" : @"Regular",
+ @"DFWaWaSC-W5" : @"Regular",
+ @"DFWaWaTC-W5" : @"Regular",
+ @"DINAlternate-Bold" : @"Bold",
+ @"DINCondensed-Bold" : @"Bold",
+ @"Damascus" : @"Regular",
+ @"DamascusBold" : @"Bold",
+ @"DamascusLight" : @"Light",
+ @"DamascusMedium" : @"Medium",
+ @"DamascusSemiBold" : @"Semi Bold",
+ @"DecoTypeNaskh" : @"Regular",
+ @"DevanagariMT" : @"Regular",
+ @"DevanagariMT-Bold" : @"Bold",
+ @"DevanagariSangamMN" : @"Regular",
+ @"DevanagariSangamMN-Bold" : @"Bold",
+ @"Didot" : @"Regular",
+ @"Didot-Bold" : @"Bold",
+ @"Didot-Italic" : @"Italic",
+ @"DiwanKufi" : @"Regular",
+ @"DiwanMishafi" : @"Regular",
+ @"DiwanMishafiGold" : @"Regular",
+ @"DiwanThuluth" : @"Regular",
+ @"EuphemiaUCAS" : @"Regular",
+ @"EuphemiaUCAS-Bold" : @"Bold",
+ @"EuphemiaUCAS-Italic" : @"Italic",
+ @"FZLTTHB--B51-0" : @"Heavy",
+ @"FZLTTHK--GBK1-0" : @"Heavy",
+ @"FZLTXHB--B51-0" : @"Extralight",
+ @"FZLTXHK--GBK1-0" : @"Extralight",
+ @"FZLTZHB--B51-0" : @"Demibold",
+ @"FZLTZHK--GBK1-0" : @"Demibold",
+ @"Farah" : @"Regular",
+ @"Farisi" : @"Regular",
+ @"Futura-Bold" : @"Bold",
+ @"Futura-CondensedExtraBold" : @"Condensed ExtraBold",
+ @"Futura-CondensedMedium" : @"Condensed Medium",
+ @"Futura-Medium" : @"Medium",
+ @"Futura-MediumItalic" : @"Medium Italic",
+ @"GB18030Bitmap" : @"Regular",
+ @"Galvji" : @"Regular",
+ @"Galvji-Bold" : @"Bold",
+ @"Galvji-BoldOblique" : @"Bold Oblique",
+ @"Galvji-Oblique" : @"Oblique",
+ @"GeezaPro" : @"Regular",
+ @"GeezaPro-Bold" : @"Bold",
+ @"Geneva" : @"Regular",
+ @"Georgia" : @"Regular",
+ @"Georgia-Bold" : @"Bold",
+ @"Georgia-BoldItalic" : @"Bold Italic",
+ @"Georgia-Italic" : @"Italic",
+ @"GillSans" : @"Regular",
+ @"GillSans-Bold" : @"Bold",
+ @"GillSans-BoldItalic" : @"Bold Italic",
+ @"GillSans-Italic" : @"Italic",
+ @"GillSans-Light" : @"Light",
+ @"GillSans-LightItalic" : @"Light Italic",
+ @"GillSans-SemiBold" : @"SemiBold",
+ @"GillSans-SemiBoldItalic" : @"SemiBold Italic",
+ @"GillSans-UltraBold" : @"UltraBold",
+ @"GujaratiMT" : @"Regular",
+ @"GujaratiMT-Bold" : @"Bold",
+ @"GujaratiSangamMN" : @"Regular",
+ @"GujaratiSangamMN-Bold" : @"Bold",
+ @"GurmukhiMN" : @"Regular",
+ @"GurmukhiMN-Bold" : @"Bold",
+ @"GurmukhiSangamMN" : @"Regular",
+ @"GurmukhiSangamMN-Bold" : @"Bold",
+ @"HannotateSC-W5" : @"Regular",
+ @"HannotateSC-W7" : @"Bold",
+ @"HannotateTC-W5" : @"Regular",
+ @"HannotateTC-W7" : @"Bold",
+ @"HanziPenSC-W3" : @"Regular",
+ @"HanziPenSC-W5" : @"Bold",
+ @"HanziPenTC-W3" : @"Regular",
+ @"HanziPenTC-W5" : @"Bold",
+ @"Helvetica" : @"Regular",
+ @"Helvetica-Bold" : @"Bold",
+ @"Helvetica-BoldOblique" : @"Bold Oblique",
+ @"Helvetica-Light" : @"Light",
+ @"Helvetica-LightOblique" : @"Light Oblique",
+ @"Helvetica-Oblique" : @"Oblique",
+ @"HelveticaLTMM" : @"Regular",
+ @"HelveticaNeue" : @"Regular",
+ @"HelveticaNeue-Bold" : @"Bold",
+ @"HelveticaNeue-BoldItalic" : @"Bold Italic",
+ @"HelveticaNeue-CondensedBlack" : @"Condensed Black",
+ @"HelveticaNeue-CondensedBold" : @"Condensed Bold",
+ @"HelveticaNeue-Italic" : @"Italic",
+ @"HelveticaNeue-Light" : @"Light",
+ @"HelveticaNeue-LightItalic" : @"Light Italic",
+ @"HelveticaNeue-Medium" : @"Medium",
+ @"HelveticaNeue-MediumItalic" : @"Medium Italic",
+ @"HelveticaNeue-Thin" : @"Thin",
+ @"HelveticaNeue-ThinItalic" : @"Thin Italic",
+ @"HelveticaNeue-UltraLight" : @"UltraLight",
+ @"HelveticaNeue-UltraLightItalic" : @"UltraLight Italic",
+ @"Herculanum" : @"Regular",
+ @"HiraKakuPro-W3" : @"W3",
+ @"HiraKakuPro-W6" : @"W6",
+ @"HiraKakuProN-W3" : @"W3",
+ @"HiraKakuProN-W6" : @"W6",
+ @"HiraKakuStd-W8" : @"W8",
+ @"HiraKakuStdN-W8" : @"W8",
+ @"HiraMaruPro-W4" : @"W4",
+ @"HiraMaruProN-W4" : @"W4",
+ @"HiraMinPro-W3" : @"W3",
+ @"HiraMinPro-W6" : @"W6",
+ @"HiraMinProN-W3" : @"W3",
+ @"HiraMinProN-W6" : @"W6",
+ @"HiraginoSans-W0" : @"W0",
+ @"HiraginoSans-W1" : @"W1",
+ @"HiraginoSans-W2" : @"W2",
+ @"HiraginoSans-W3" : @"W3",
+ @"HiraginoSans-W4" : @"W4",
+ @"HiraginoSans-W5" : @"W5",
+ @"HiraginoSans-W6" : @"W6",
+ @"HiraginoSans-W7" : @"W7",
+ @"HiraginoSans-W8" : @"W8",
+ @"HiraginoSans-W9" : @"W9",
+ @"HiraginoSansCNS-W3" : @"W3",
+ @"HiraginoSansCNS-W6" : @"W6",
+ @"HiraginoSansGB-W3" : @"W3",
+ @"HiraginoSansGB-W6" : @"W6",
+ @"HoeflerText-Black" : @"Black",
+ @"HoeflerText-BlackItalic" : @"Black Italic",
+ @"HoeflerText-Italic" : @"Italic",
+ @"HoeflerText-Ornaments" : @"Ornaments",
+ @"HoeflerText-Regular" : @"Regular",
+ @"ITFDevanagari-Bold" : @"Bold",
+ @"ITFDevanagari-Book" : @"Book",
+ @"ITFDevanagari-Demi" : @"Demi",
+ @"ITFDevanagari-Light" : @"Light",
+ @"ITFDevanagari-Medium" : @"Medium",
+ @"ITFDevanagariMarathi-Bold" : @"Bold",
+ @"ITFDevanagariMarathi-Book" : @"Book",
+ @"ITFDevanagariMarathi-Demi" : @"Demi",
+ @"ITFDevanagariMarathi-Light" : @"Light",
+ @"ITFDevanagariMarathi-Medium" : @"Medium",
+ @"Impact" : @"Regular",
+ @"InaiMathi" : @"Regular",
+ @"InaiMathi-Bold" : @"Bold",
+ @"IowanOldStyle-Black" : @"Black",
+ @"IowanOldStyle-BlackItalic" : @"Black Italic",
+ @"IowanOldStyle-Bold" : @"Bold",
+ @"IowanOldStyle-BoldItalic" : @"Bold Italic",
+ @"IowanOldStyle-Italic" : @"Italic",
+ @"IowanOldStyle-Roman" : @"Roman",
+ @"IowanOldStyle-Titling" : @"Titling",
+ @"JCHEadA" : @"Regular",
+ @"JCfg" : @"Regular",
+ @"JCkg" : @"Regular",
+ @"JCsmPC" : @"Regular",
+ @"Kailasa" : @"Regular",
+ @"Kailasa-Bold" : @"Bold",
+ @"KannadaMN" : @"Regular",
+ @"KannadaMN-Bold" : @"Bold",
+ @"KannadaSangamMN" : @"Regular",
+ @"KannadaSangamMN-Bold" : @"Bold",
+ @"Kefa-Bold" : @"Bold",
+ @"Kefa-Regular" : @"Regular",
+ @"KhmerMN" : @"Regular",
+ @"KhmerMN-Bold" : @"Bold",
+ @"KhmerSangamMN" : @"Regular",
+ @"Klee-Demibold" : @"Demibold",
+ @"Klee-Medium" : @"Medium",
+ @"KohinoorBangla-Bold" : @"Bold",
+ @"KohinoorBangla-Light" : @"Light",
+ @"KohinoorBangla-Medium" : @"Medium",
+ @"KohinoorBangla-Regular" : @"Regular",
+ @"KohinoorBangla-Semibold" : @"Semibold",
+ @"KohinoorDevanagari-Bold" : @"Bold",
+ @"KohinoorDevanagari-Light" : @"Light",
+ @"KohinoorDevanagari-Medium" : @"Medium",
+ @"KohinoorDevanagari-Regular" : @"Regular",
+ @"KohinoorDevanagari-Semibold" : @"Semibold",
+ @"KohinoorGujarati-Bold" : @"Bold",
+ @"KohinoorGujarati-Light" : @"Light",
+ @"KohinoorGujarati-Medium" : @"Medium",
+ @"KohinoorGujarati-Regular" : @"Regular",
+ @"KohinoorGujarati-Semibold" : @"Semibold",
+ @"KohinoorTelugu-Bold" : @"Bold",
+ @"KohinoorTelugu-Light" : @"Light",
+ @"KohinoorTelugu-Medium" : @"Medium",
+ @"KohinoorTelugu-Regular" : @"Regular",
+ @"KohinoorTelugu-Semibold" : @"Semibold",
+ @"Kokonor" : @"Regular",
+ @"Krungthep" : @"Regular",
+ @"KufiStandardGK" : @"Regular",
+ @"LaoMN" : @"Regular",
+ @"LaoMN-Bold" : @"Bold",
+ @"LaoSangamMN" : @"Regular",
+ @"LastResort" : @"Regular",
+ @"LiGothicMed" : @"Medium",
+ @"LiHeiPro" : @"Medium",
+ @"LiSongPro" : @"Light",
+ @"LiSungLight" : @"Light",
+ @"LucidaGrande" : @"Regular",
+ @"LucidaGrande-Bold" : @"Bold",
+ @"Luminari-Regular" : @"Regular",
+ @"MLingWaiMedium-SC" : @"Medium",
+ @"MLingWaiMedium-TC" : @"Medium",
+ @"MalayalamMN" : @"Regular",
+ @"MalayalamMN-Bold" : @"Bold",
+ @"MalayalamSangamMN" : @"Regular",
+ @"MalayalamSangamMN-Bold" : @"Bold",
+ @"Marion-Bold" : @"Bold",
+ @"Marion-Italic" : @"Italic",
+ @"Marion-Regular" : @"Regular",
+ @"MarkerFelt-Thin" : @"Thin",
+ @"MarkerFelt-Wide" : @"Wide",
+ @"Menlo-Bold" : @"Bold",
+ @"Menlo-BoldItalic" : @"Bold Italic",
+ @"Menlo-Italic" : @"Italic",
+ @"Menlo-Regular" : @"Regular",
+ @"MicrosoftSansSerif" : @"Regular",
+ @"Monaco" : @"Regular",
+ @"MonotypeGurmukhi" : @"Regular",
+ @"Mshtakan" : @"Regular",
+ @"MshtakanBold" : @"Bold",
+ @"MshtakanBoldOblique" : @"BoldOblique",
+ @"MshtakanOblique" : @"Oblique",
+ @"MuktaMahee-Bold" : @"Bold",
+ @"MuktaMahee-ExtraBold" : @"ExtraBold",
+ @"MuktaMahee-ExtraLight" : @"ExtraLight",
+ @"MuktaMahee-Light" : @"Light",
+ @"MuktaMahee-Medium" : @"Medium",
+ @"MuktaMahee-Regular" : @"Regular",
+ @"MuktaMahee-SemiBold" : @"SemiBold",
+ @"Muna" : @"Regular",
+ @"MunaBlack" : @"Black",
+ @"MunaBold" : @"Bold",
+ @"MyanmarMN" : @"Regular",
+ @"MyanmarMN-Bold" : @"Bold",
+ @"MyanmarSangamMN" : @"Regular",
+ @"MyanmarSangamMN-Bold" : @"Bold",
+ @"Nadeem" : @"Regular",
+ @"NanumBrush" : @"Regular",
+ @"NanumGothic" : @"Regular",
+ @"NanumGothicBold" : @"Bold",
+ @"NanumGothicExtraBold" : @"ExtraBold",
+ @"NanumMyeongjo" : @"Regular",
+ @"NanumMyeongjoBold" : @"Bold",
+ @"NanumMyeongjoExtraBold" : @"ExtraBold",
+ @"NanumPen" : @"Regular",
+ @"NewPeninimMT" : @"Regular",
+ @"NewPeninimMT-Bold" : @"Bold",
+ @"NewPeninimMT-BoldInclined" : @"Bold Inclined",
+ @"NewPeninimMT-Inclined" : @"Inclined",
+ @"Noteworthy-Bold" : @"Bold",
+ @"Noteworthy-Light" : @"Light",
+ @"NotoNastaliqUrdu" : @"Regular",
+ @"NotoNastaliqUrdu-Bold" : @"Bold",
+ @"NotoSansArmenian-Black" : @"Black",
+ @"NotoSansArmenian-Bold" : @"Bold",
+ @"NotoSansArmenian-ExtraBold" : @"ExtraBold",
+ @"NotoSansArmenian-ExtraLight" : @"ExtraLight",
+ @"NotoSansArmenian-Light" : @"Light",
+ @"NotoSansArmenian-Medium" : @"Medium",
+ @"NotoSansArmenian-Regular" : @"Regular",
+ @"NotoSansArmenian-SemiBold" : @"SemiBold",
+ @"NotoSansArmenian-Thin" : @"Thin",
+ @"NotoSansAvestan-Regular" : @"Regular",
+ @"NotoSansBamum-Regular" : @"Regular",
+ @"NotoSansBatak-Regular" : @"Regular",
+ @"NotoSansBrahmi-Regular" : @"Regular",
+ @"NotoSansBuginese-Regular" : @"Regular",
+ @"NotoSansBuhid-Regular" : @"Regular",
+ @"NotoSansCarian-Regular" : @"Regular",
+ @"NotoSansChakma-Regular" : @"Regular",
+ @"NotoSansCham-Regular" : @"Regular",
+ @"NotoSansCoptic-Regular" : @"Regular",
+ @"NotoSansCuneiform-Regular" : @"Regular",
+ @"NotoSansCypriot-Regular" : @"Regular",
+ @"NotoSansEgyptianHieroglyphs-Regular" : @"Regular",
+ @"NotoSansGlagolitic-Regular" : @"Regular",
+ @"NotoSansGothic-Regular" : @"Regular",
+ @"NotoSansHanunoo-Regular" : @"Regular",
+ @"NotoSansImperialAramaic-Regular" : @"Regular",
+ @"NotoSansInscriptionalPahlavi-Regular" : @"Regular",
+ @"NotoSansInscriptionalParthian-Regular" : @"Regular",
+ @"NotoSansJavanese-Regular" : @"Regular",
+ @"NotoSansKaithi-Regular" : @"Regular",
+ @"NotoSansKannada-Black" : @"Black",
+ @"NotoSansKannada-Bold" : @"Bold",
+ @"NotoSansKannada-ExtraBold" : @"ExtraBold",
+ @"NotoSansKannada-ExtraLight" : @"ExtraLight",
+ @"NotoSansKannada-Light" : @"Light",
+ @"NotoSansKannada-Medium" : @"Medium",
+ @"NotoSansKannada-Regular" : @"Regular",
+ @"NotoSansKannada-SemiBold" : @"SemiBold",
+ @"NotoSansKannada-Thin" : @"Thin",
+ @"NotoSansKayahLi-Regular" : @"Regular",
+ @"NotoSansKharoshthi-Regular" : @"Regular",
+ @"NotoSansLepcha-Regular" : @"Regular",
+ @"NotoSansLimbu-Regular" : @"Regular",
+ @"NotoSansLinearB-Regular" : @"Regular",
+ @"NotoSansLisu-Regular" : @"Regular",
+ @"NotoSansLycian-Regular" : @"Regular",
+ @"NotoSansLydian-Regular" : @"Regular",
+ @"NotoSansMandaic-Regular" : @"Regular",
+ @"NotoSansMeeteiMayek-Regular" : @"Regular",
+ @"NotoSansMongolian" : @"Regular",
+ @"NotoSansMyanmar-Black" : @"Black",
+ @"NotoSansMyanmar-Bold" : @"Bold",
+ @"NotoSansMyanmar-ExtraBold" : @"ExtraBold",
+ @"NotoSansMyanmar-ExtraLight" : @"ExtraLight",
+ @"NotoSansMyanmar-Light" : @"Light",
+ @"NotoSansMyanmar-Medium" : @"Medium",
+ @"NotoSansMyanmar-Regular" : @"Regular",
+ @"NotoSansMyanmar-SemiBold" : @"SemiBold",
+ @"NotoSansMyanmar-Thin" : @"Thin",
+ @"NotoSansNKo-Regular" : @"Regular",
+ @"NotoSansNewTaiLue-Regular" : @"Regular",
+ @"NotoSansOgham-Regular" : @"Regular",
+ @"NotoSansOlChiki-Regular" : @"Regular",
+ @"NotoSansOldItalic-Regular" : @"Regular",
+ @"NotoSansOldPersian-Regular" : @"Regular",
+ @"NotoSansOldSouthArabian-Regular" : @"Regular",
+ @"NotoSansOldTurkic-Regular" : @"Regular",
+ @"NotoSansOriya" : @"Regular",
+ @"NotoSansOriya-Bold" : @"Bold",
+ @"NotoSansOsmanya-Regular" : @"Regular",
+ @"NotoSansPhagsPa-Regular" : @"Regular",
+ @"NotoSansPhoenician-Regular" : @"Regular",
+ @"NotoSansRejang-Regular" : @"Regular",
+ @"NotoSansRunic-Regular" : @"Regular",
+ @"NotoSansSamaritan-Regular" : @"Regular",
+ @"NotoSansSaurashtra-Regular" : @"Regular",
+ @"NotoSansShavian-Regular" : @"Regular",
+ @"NotoSansSundanese-Regular" : @"Regular",
+ @"NotoSansSylotiNagri-Regular" : @"Regular",
+ @"NotoSansSyriac-Regular" : @"Regular",
+ @"NotoSansTagalog-Regular" : @"Regular",
+ @"NotoSansTagbanwa-Regular" : @"Regular",
+ @"NotoSansTaiLe-Regular" : @"Regular",
+ @"NotoSansTaiTham" : @"Regular",
+ @"NotoSansTaiViet-Regular" : @"Regular",
+ @"NotoSansThaana-Regular" : @"Regular",
+ @"NotoSansTifinagh-Regular" : @"Regular",
+ @"NotoSansUgaritic-Regular" : @"Regular",
+ @"NotoSansVai-Regular" : @"Regular",
+ @"NotoSansYi-Regular" : @"Regular",
+ @"NotoSansZawgyi-Black" : @"Black",
+ @"NotoSansZawgyi-Bold" : @"Bold",
+ @"NotoSansZawgyi-ExtraBold" : @"ExtraBold",
+ @"NotoSansZawgyi-ExtraLight" : @"ExtraLight",
+ @"NotoSansZawgyi-Light" : @"Light",
+ @"NotoSansZawgyi-Medium" : @"Medium",
+ @"NotoSansZawgyi-Regular" : @"Regular",
+ @"NotoSansZawgyi-SemiBold" : @"SemiBold",
+ @"NotoSansZawgyi-Thin" : @"Thin",
+ @"NotoSerifBalinese-Regular" : @"Regular",
+ @"NotoSerifMyanmar-Black" : @"Black",
+ @"NotoSerifMyanmar-Bold" : @"Bold",
+ @"NotoSerifMyanmar-ExtraBold" : @"ExtraBold",
+ @"NotoSerifMyanmar-ExtraLight" : @"ExtraLight",
+ @"NotoSerifMyanmar-Light" : @"Light",
+ @"NotoSerifMyanmar-Medium" : @"Medium",
+ @"NotoSerifMyanmar-Regular" : @"Regular",
+ @"NotoSerifMyanmar-SemiBold" : @"SemiBold",
+ @"NotoSerifMyanmar-Thin" : @"Thin",
+ @"Optima-Bold" : @"Bold",
+ @"Optima-BoldItalic" : @"Bold Italic",
+ @"Optima-ExtraBlack" : @"ExtraBlack",
+ @"Optima-Italic" : @"Italic",
+ @"Optima-Regular" : @"Regular",
+ @"OriyaMN" : @"Regular",
+ @"OriyaMN-Bold" : @"Bold",
+ @"OriyaSangamMN" : @"Regular",
+ @"OriyaSangamMN-Bold" : @"Bold",
+ @"Osaka" : @"Regular",
+ @"Osaka-Mono" : @"Regular-Mono",
+ @"PSLOrnanongPro-Bold" : @"Bold",
+ @"PSLOrnanongPro-BoldItalic" : @"Bold Italic",
+ @"PSLOrnanongPro-Demibold" : @"Demibold",
+ @"PSLOrnanongPro-DemiboldItalic" : @"Demibold Italic",
+ @"PSLOrnanongPro-Italic" : @"Italic",
+ @"PSLOrnanongPro-Light" : @"Light",
+ @"PSLOrnanongPro-LightItalic" : @"Light Italic",
+ @"PSLOrnanongPro-Regular" : @"Regular",
+ @"PTMono-Bold" : @"Bold",
+ @"PTMono-Regular" : @"Regular",
+ @"PTSans-Bold" : @"Bold",
+ @"PTSans-BoldItalic" : @"Bold Italic",
+ @"PTSans-Caption" : @"Regular",
+ @"PTSans-CaptionBold" : @"Bold",
+ @"PTSans-Italic" : @"Italic",
+ @"PTSans-Narrow" : @"Regular",
+ @"PTSans-NarrowBold" : @"Bold",
+ @"PTSans-Regular" : @"Regular",
+ @"PTSerif-Bold" : @"Bold",
+ @"PTSerif-BoldItalic" : @"Bold Italic",
+ @"PTSerif-Caption" : @"Regular",
+ @"PTSerif-CaptionItalic" : @"Italic",
+ @"PTSerif-Italic" : @"Italic",
+ @"PTSerif-Regular" : @"Regular",
+ @"Palatino-Bold" : @"Bold",
+ @"Palatino-BoldItalic" : @"Bold Italic",
+ @"Palatino-Italic" : @"Italic",
+ @"Palatino-Roman" : @"Regular",
+ @"Papyrus" : @"Regular",
+ @"Papyrus-Condensed" : @"Condensed",
+ @"Phosphate-Inline" : @"Inline",
+ @"Phosphate-Solid" : @"Solid",
+ @"PingFangHK-Light" : @"Light",
+ @"PingFangHK-Medium" : @"Medium",
+ @"PingFangHK-Regular" : @"Regular",
+ @"PingFangHK-Semibold" : @"Semibold",
+ @"PingFangHK-Thin" : @"Thin",
+ @"PingFangHK-Ultralight" : @"Ultralight",
+ @"PingFangSC-Light" : @"Light",
+ @"PingFangSC-Medium" : @"Medium",
+ @"PingFangSC-Regular" : @"Regular",
+ @"PingFangSC-Semibold" : @"Semibold",
+ @"PingFangSC-Thin" : @"Thin",
+ @"PingFangSC-Ultralight" : @"Ultralight",
+ @"PingFangTC-Light" : @"Light",
+ @"PingFangTC-Medium" : @"Medium",
+ @"PingFangTC-Regular" : @"Regular",
+ @"PingFangTC-Semibold" : @"Semibold",
+ @"PingFangTC-Thin" : @"Thin",
+ @"PingFangTC-Ultralight" : @"Ultralight",
+ @"PlantagenetCherokee" : @"Regular",
+ @"Raanana" : @"Regular",
+ @"RaananaBold" : @"Bold",
+ @"Rockwell-Bold" : @"Bold",
+ @"Rockwell-BoldItalic" : @"Bold Italic",
+ @"Rockwell-Italic" : @"Italic",
+ @"Rockwell-Regular" : @"Regular",
+ @"SFMono-Bold" : @"Bold",
+ @"SFMono-BoldItalic" : @"Bold Italic",
+ @"SFMono-Heavy" : @"Heavy",
+ @"SFMono-HeavyItalic" : @"Heavy Italic",
+ @"SFMono-Light" : @"Light",
+ @"SFMono-LightItalic" : @"Light Italic",
+ @"SFMono-Medium" : @"Medium",
+ @"SFMono-MediumItalic" : @"Medium Italic",
+ @"SFMono-Regular" : @"Regular",
+ @"SFMono-RegularItalic" : @"Regular Italic",
+ @"SFMono-Semibold" : @"Semibold",
+ @"SFMono-SemiboldItalic" : @"Semibold Italic",
+ @"SIL-Hei-Med-Jian" : @"Regular",
+ @"SIL-Kai-Reg-Jian" : @"Regular",
+ @"STBaoliSC-Regular" : @"Regular",
+ @"STBaoliTC-Regular" : @"Regular",
+ @"STFangsong" : @"Regular",
+ @"STHeiti" : @"Regular",
+ @"STHeitiSC-Light" : @"Light",
+ @"STHeitiSC-Medium" : @"Medium",
+ @"STHeitiTC-Light" : @"Light",
+ @"STHeitiTC-Medium" : @"Medium",
+ @"STIXGeneral-Bold" : @"Bold",
+ @"STIXGeneral-BoldItalic" : @"Bold Italic",
+ @"STIXGeneral-Italic" : @"Italic",
+ @"STIXGeneral-Regular" : @"Regular",
+ @"STIXIntegralsD-Bold" : @"Bold",
+ @"STIXIntegralsD-Regular" : @"Regular",
+ @"STIXIntegralsSm-Bold" : @"Bold",
+ @"STIXIntegralsSm-Regular" : @"Regular",
+ @"STIXIntegralsUp-Bold" : @"Bold",
+ @"STIXIntegralsUp-Regular" : @"Regular",
+ @"STIXIntegralsUpD-Bold" : @"Bold",
+ @"STIXIntegralsUpD-Regular" : @"Regular",
+ @"STIXIntegralsUpSm-Bold" : @"Bold",
+ @"STIXIntegralsUpSm-Regular" : @"Regular",
+ @"STIXNonUnicode-Bold" : @"Bold",
+ @"STIXNonUnicode-BoldItalic" : @"Bold Italic",
+ @"STIXNonUnicode-Italic" : @"Italic",
+ @"STIXNonUnicode-Regular" : @"Regular",
+ @"STIXSizeFiveSym-Regular" : @"Regular",
+ @"STIXSizeFourSym-Bold" : @"Bold",
+ @"STIXSizeFourSym-Regular" : @"Regular",
+ @"STIXSizeOneSym-Bold" : @"Bold",
+ @"STIXSizeOneSym-Regular" : @"Regular",
+ @"STIXSizeThreeSym-Bold" : @"Bold",
+ @"STIXSizeThreeSym-Regular" : @"Regular",
+ @"STIXSizeTwoSym-Bold" : @"Bold",
+ @"STIXSizeTwoSym-Regular" : @"Regular",
+ @"STIXVariants-Bold" : @"Bold",
+ @"STIXVariants-Regular" : @"Regular",
+ @"STKaiti" : @"Regular",
+ @"STKaitiSC-Black" : @"Black",
+ @"STKaitiSC-Bold" : @"Bold",
+ @"STKaitiSC-Regular" : @"Regular",
+ @"STKaitiTC-Black" : @"Black",
+ @"STKaitiTC-Bold" : @"Bold",
+ @"STKaitiTC-Regular" : @"Regular",
+ @"STLibianSC-Regular" : @"Regular",
+ @"STLibianTC-Regular" : @"Regular",
+ @"STSong" : @"Regular",
+ @"STSongti-SC-Black" : @"Black",
+ @"STSongti-SC-Bold" : @"Bold",
+ @"STSongti-SC-Light" : @"Light",
+ @"STSongti-SC-Regular" : @"Regular",
+ @"STSongti-TC-Bold" : @"Bold",
+ @"STSongti-TC-Light" : @"Light",
+ @"STSongti-TC-Regular" : @"Regular",
+ @"STXihei" : @"Light",
+ @"STXingkaiSC-Bold" : @"Bold",
+ @"STXingkaiSC-Light" : @"Light",
+ @"STXingkaiTC-Bold" : @"Bold",
+ @"STXingkaiTC-Light" : @"Light",
+ @"STYuanti-SC-Bold" : @"Bold",
+ @"STYuanti-SC-Light" : @"Light",
+ @"STYuanti-SC-Regular" : @"Regular",
+ @"STYuanti-TC-Bold" : @"Bold",
+ @"STYuanti-TC-Light" : @"Light",
+ @"STYuanti-TC-Regular" : @"Regular",
+ @"Sana" : @"Regular",
+ @"Sathu" : @"Regular",
+ @"SavoyeLetPlain" : @"Plain",
+ @"Seravek" : @"Regular",
+ @"Seravek-Bold" : @"Bold",
+ @"Seravek-BoldItalic" : @"Bold Italic",
+ @"Seravek-ExtraLight" : @"ExtraLight",
+ @"Seravek-ExtraLightItalic" : @"ExtraLight Italic",
+ @"Seravek-Italic" : @"Italic",
+ @"Seravek-Light" : @"Light",
+ @"Seravek-LightItalic" : @"Light Italic",
+ @"Seravek-Medium" : @"Medium",
+ @"Seravek-MediumItalic" : @"Medium Italic",
+ @"ShreeDev0714" : @"Regular",
+ @"ShreeDev0714-Bold" : @"Bold",
+ @"ShreeDev0714-BoldItalic" : @"Bold Italic",
+ @"ShreeDev0714-Italic" : @"Italic",
+ @"SignPainter-HouseScript" : @"HouseScript",
+ @"SignPainter-HouseScriptSemibold" : @"HouseScript Semibold",
+ @"Silom" : @"Regular",
+ @"SinhalaMN" : @"Regular",
+ @"SinhalaMN-Bold" : @"Bold",
+ @"SinhalaSangamMN" : @"Regular",
+ @"SinhalaSangamMN-Bold" : @"Bold",
+ @"Skia-Regular" : @"Regular",
+ @"Skia-Regular_Black" : @"Black",
+ @"Skia-Regular_Black-Condensed" : @"Black Condensed",
+ @"Skia-Regular_Black-Extended" : @"Black Extended",
+ @"Skia-Regular_Bold" : @"Bold",
+ @"Skia-Regular_Condensed" : @"Condensed",
+ @"Skia-Regular_Extended" : @"Extended",
+ @"Skia-Regular_Light" : @"Light",
+ @"Skia-Regular_Light-Condensed" : @"Light Condensed",
+ @"Skia-Regular_Light-Extended" : @"Light Extended",
+ @"SnellRoundhand" : @"Regular",
+ @"SnellRoundhand-Black" : @"Black",
+ @"SnellRoundhand-Bold" : @"Bold",
+ @"SukhumvitSet-Bold" : @"Bold",
+ @"SukhumvitSet-Light" : @"Light",
+ @"SukhumvitSet-Medium" : @"Medium",
+ @"SukhumvitSet-SemiBold" : @"Semi Bold",
+ @"SukhumvitSet-Text" : @"Text",
+ @"SukhumvitSet-Thin" : @"Thin",
+ @"Superclarendon-Black" : @"Black",
+ @"Superclarendon-BlackItalic" : @"Black Italic",
+ @"Superclarendon-Bold" : @"Bold",
+ @"Superclarendon-BoldItalic" : @"Bold Italic",
+ @"Superclarendon-Italic" : @"Italic",
+ @"Superclarendon-Light" : @"Light",
+ @"Superclarendon-LightItalic" : @"Light Italic",
+ @"Superclarendon-Regular" : @"Regular",
+ @"Symbol" : @"Regular",
+ @"Tahoma" : @"Regular",
+ @"Tahoma-Bold" : @"Bold",
+ @"TamilMN" : @"Regular",
+ @"TamilMN-Bold" : @"Bold",
+ @"TamilSangamMN" : @"Regular",
+ @"TamilSangamMN-Bold" : @"Bold",
+ @"TeamViewer12" : @"Medium",
+ @"TeluguMN" : @"Regular",
+ @"TeluguMN-Bold" : @"Bold",
+ @"TeluguSangamMN" : @"Regular",
+ @"TeluguSangamMN-Bold" : @"Bold",
+ @"Thonburi" : @"Regular",
+ @"Thonburi-Bold" : @"Bold",
+ @"Thonburi-Light" : @"Light",
+ @"Times-Bold" : @"Bold",
+ @"Times-BoldItalic" : @"Bold Italic",
+ @"Times-Italic" : @"Italic",
+ @"Times-Roman" : @"Regular",
+ @"TimesLTMM" : @"Regular",
+ @"TimesNewRomanPS-BoldItalicMT" : @"Bold Italic",
+ @"TimesNewRomanPS-BoldMT" : @"Bold",
+ @"TimesNewRomanPS-ItalicMT" : @"Italic",
+ @"TimesNewRomanPSMT" : @"Regular",
+ @"ToppanBunkyuGothicPr6N-DB" : @"Demibold",
+ @"ToppanBunkyuGothicPr6N-Regular" : @"Regular",
+ @"ToppanBunkyuMidashiGothicStdN-ExtraBold" : @"Extrabold",
+ @"ToppanBunkyuMidashiMinchoStdN-ExtraBold" : @"Extrabold",
+ @"ToppanBunkyuMinchoPr6N-Regular" : @"Regular",
+ @"Trattatello" : @"Regular",
+ @"Trebuchet-BoldItalic" : @"Bold Italic",
+ @"TrebuchetMS" : @"Regular",
+ @"TrebuchetMS-Bold" : @"Bold",
+ @"TrebuchetMS-Italic" : @"Italic",
+ @"TsukuARdGothic-Bold" : @"Bold",
+ @"TsukuARdGothic-Regular" : @"Regular",
+ @"TsukuBRdGothic-Bold" : @"Bold",
+ @"TsukuBRdGothic-Regular" : @"Regular",
+ @"Verdana" : @"Regular",
+ @"Verdana-Bold" : @"Bold",
+ @"Verdana-BoldItalic" : @"Bold Italic",
+ @"Verdana-Italic" : @"Italic",
+ @"Waseem" : @"Regular",
+ @"WaseemLight" : @"Light",
+ @"Webdings" : @"Regular",
+ @"WeibeiSC-Bold" : @"Bold",
+ @"WeibeiTC-Bold" : @"Bold",
+ @"Wingdings-Regular" : @"Regular",
+ @"Wingdings2" : @"Regular",
+ @"Wingdings3" : @"Regular",
+ @"YuGo-Bold" : @"Bold",
+ @"YuGo-Medium" : @"Medium",
+ @"YuKyo-Bold" : @"Bold",
+ @"YuKyo-Medium" : @"Medium",
+ @"YuKyo_Yoko-Bold" : @"Bold",
+ @"YuKyo_Yoko-Medium" : @"Medium",
+ @"YuMin-Demibold" : @"Demibold",
+ @"YuMin-Extrabold" : @"Extrabold",
+ @"YuMin-Medium" : @"Medium",
+ @"YuMin_36pKn-Demibold" : @"Demibold",
+ @"YuMin_36pKn-Extrabold" : @"Extrabold",
+ @"YuMin_36pKn-Medium" : @"Medium",
+ @"YuppySC-Regular" : @"Regular",
+ @"YuppyTC-Regular" : @"Regular",
+ @"ZapfDingbatsITC" : @"Regular",
+ @"Zapfino" : @"Regular",
+
+ // JetBrains fonts
+ @"DroidSans" : @"Regular",
+ @"DroidSans-Bold" : @"Bold",
+ @"DroidSansMono" : @"Regular",
+ @"DroidSansMonoDotted" : @"Regular",
+ @"DroidSansMonoSlashed" : @"Regular",
+ @"DroidSerif" : @"Regular",
+ @"DroidSerif-Bold" : @"Bold",
+ @"DroidSerif-BoldItalic" : @"Bold Italic",
+ @"DroidSerif-Italic" : @"Italic",
+ @"FiraCode-Bold" : @"Bold",
+ @"FiraCode-Light" : @"Light",
+ @"FiraCode-Medium" : @"Medium",
+ @"FiraCode-Regular" : @"Regular",
+ @"FiraCode-Retina" : @"Retina",
+ @"Inconsolata" : @"Medium",
+ @"JetBrainsMono-Bold" : @"Bold",
+ @"JetBrainsMono-Regular" : @"Regular",
+ @"JetBrainsMono-Italic" : @"Italic",
+ @"JetBrainsMono-BoldItalic" : @"Bold Italic",
+ @"Roboto-Light" : @"Light",
+ @"Roboto-Thin" : @"Thin",
+ @"SourceCodePro-Bold" : @"Bold",
+ @"SourceCodePro-BoldIt" : @"Bold Italic",
+ @"SourceCodePro-It" : @"Italic",
+ @"SourceCodePro-Regular" : @"Regular",
+ @"Inter-Bold": @"Bold",
+ @"Inter-BoldItalic": @"Bold Italoc",
+ @"Inter-Italic": @"Italic",
+ @"Inter-Regular": @"Regular"
+ };
+}
+
+static bool
+isVisibleFamily(NSString *familyName) {
+ return familyName != nil &&
+ ([familyName characterAtIndex:0] != '.' ||
+ [familyName isEqualToString:@".SF NS Text"] ||
+ [familyName isEqualToString:@".SF NS Display"] ||
+ // Catalina
+ [familyName isEqualToString:@".SF NS Mono"] ||
+ [familyName isEqualToString:@".SF NS"]);
+}
+
static NSArray*
GetFilteredFonts()
{
if (sFilteredFonts == nil) {
NSFontManager *fontManager = [NSFontManager sharedFontManager];
- NSUInteger fontCount = [[fontManager availableFonts] count];
-
- NSMutableArray *allFonts = [[NSMutableArray alloc] initWithCapacity:fontCount];
+ NSArray *availableFonts= [fontManager availableFonts];
+ NSUInteger fontCount = [availableFonts count];
+ NSMutableArray* allFonts = [NSMutableArray arrayWithCapacity:fontCount];
NSMutableDictionary* fontFamilyTable = [[NSMutableDictionary alloc] initWithCapacity:fontCount];
- NSArray *allFamilies = [fontManager availableFontFamilies];
-
- NSUInteger familyCount = [allFamilies count];
-
- NSUInteger familyIndex;
- for (familyIndex = 0; familyIndex < familyCount; familyIndex++) {
- NSString *family = [allFamilies objectAtIndex:familyIndex];
-
- if ((family == nil) || [family characterAtIndex:0] == '.') {
+ NSMutableDictionary* fontFacesTable = [[NSMutableDictionary alloc] initWithCapacity:fontCount];
+ NSMutableDictionary* fontTable = [[NSMutableDictionary alloc] initWithCapacity:fontCount];
+ NSDictionary* prebuiltFamilies = prebuiltFamilyNames();
+ NSDictionary* prebuiltFaces = prebuiltFaceNames();
+ for (NSString *fontName in availableFonts) {
+ NSFont* font = nil;
+ NSString* familyName = [prebuiltFamilies objectForKey:fontName];
+ if (!familyName) {
+ font = [NSFont fontWithName:fontName size:NSFont.systemFontSize];
+ if(font && font.familyName) {
+ familyName = font.familyName;
+// NSLog(@"@\"%@\" : @\"%@\",", fontName, familyName);
+ }
+ }
+ if (!isVisibleFamily(familyName)) {
continue;
}
-
- NSArray *fontFaces = [fontManager availableMembersOfFontFamily:family];
- NSUInteger faceCount = [fontFaces count];
-
- NSUInteger faceIndex;
- for (faceIndex = 0; faceIndex < faceCount; faceIndex++) {
- NSString* face = [[fontFaces objectAtIndex:faceIndex] objectAtIndex:0];
- if (face != nil) {
- [allFonts addObject:face];
- [fontFamilyTable setObject:family forKey:face];
+ NSString* face = [prebuiltFaces objectForKey:fontName];
+ if (!face) {
+ if (!font) {
+ font = [NSFont fontWithName:fontName size:NSFont.systemFontSize];
+ }
+ if (font) {
+ NSFontDescriptor* desc = font.fontDescriptor;
+ if (desc) {
+ face = [desc objectForKey:NSFontFaceAttribute];
+// if (face) NSLog(@"@\"%@\" : @\"%@\",", fontName, face);
+ }
}
}
+ [allFonts addObject:fontName];
+ [fontFamilyTable setObject:familyName forKey:fontName];
+ if (face) {
+ [fontFacesTable setObject:face forKey:fontName];
+ }
}
+
/*
* JavaFX registers these fonts and so JDK needs to do so as well.
* If this isn't done we will have mis-matched rendering, since
* although these may include fonts that are enumerated normally
* they also demonstrably includes fonts that are not.
*/
- addFont(kCTFontUIFontSystem, allFonts, fontFamilyTable);
- addFont(kCTFontUIFontEmphasizedSystem, allFonts, fontFamilyTable);
+ addFont(kCTFontUIFontSystem, allFonts, fontFamilyTable, fontFacesTable);
+ addFont(kCTFontUIFontEmphasizedSystem, allFonts, fontFamilyTable, fontFacesTable);
sFilteredFonts = allFonts;
sFontFamilyTable = fontFamilyTable;
+ sFontFaceTable = fontFacesTable;
}
return sFilteredFonts;
@@ -296,7 +3139,7 @@ static OSStatus CreateFSRef(FSRef *myFSRefPtr, NSString *inPath)
(JNIEnv *env, jobject jthis)
{
DECLARE_CLASS(jc_CFontManager, "sun/font/CFontManager");
- DECLARE_METHOD(jm_registerFont, jc_CFontManager, "registerFont", "(Ljava/lang/String;Ljava/lang/String;)V");
+ DECLARE_METHOD(jm_registerFont, jc_CFontManager, "registerFont", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
jint num = 0;
@@ -311,14 +3154,48 @@ static OSStatus CreateFSRef(FSRef *myFSRefPtr, NSString *inPath)
jobject jFontName = NSStringToJavaString(env, fontname);
jobject jFontFamilyName =
NSStringToJavaString(env, GetFamilyNameForFontName(fontname));
+ NSString *face = GetFaceForFontName(fontname);
+ jobject jFaceName = face ? NSStringToJavaString(env, face) : NULL;
- (*env)->CallVoidMethod(env, jthis, jm_registerFont, jFontName, jFontFamilyName);
+ (*env)->CallVoidMethod(env, jthis, jm_registerFont, jFontName, jFontFamilyName, jFaceName);
CHECK_EXCEPTION();
(*env)->DeleteLocalRef(env, jFontName);
(*env)->DeleteLocalRef(env, jFontFamilyName);
+ if (jFaceName) {
+ (*env)->DeleteLocalRef(env, jFaceName);
+ }
+ }
+
+JNI_COCOA_EXIT(env);
+}
+
+/*
+ * Class: Java_sun_font_CFontManager_loadNativeDirFonts
+ * Method: getNativeFontVersion
+ * Signature: (Ljava/lang/String;)Ljava/lang/String;
+ */
+JNIEXPORT JNICALL jstring
+Java_sun_font_CFontManager_getNativeFontVersion
+ (JNIEnv *env, jclass clz, jstring psName)
+{
+ jstring result = NULL;
+JNI_COCOA_ENTER(env);
+ NSString *psNameStr = JavaStringToNSString(env, psName);
+ CTFontRef sFont = CTFontCreateWithName(psNameStr, 13, nil);
+ CFStringRef sFontPSName = CTFontCopyName(sFont, kCTFontPostScriptNameKey);
+ // CTFontCreateWithName always returns some font,
+ // so we need to check if it is right one
+ if ([psNameStr isEqualToString:sFontPSName]) {
+ CFStringRef fontVersionStr = CTFontCopyName(sFont,
+ kCTFontVersionNameKey);
+ result = NSStringToJavaString(env, fontVersionStr);
+ CFRelease(fontVersionStr);
}
+ CFRelease(sFontPSName);
+ CFRelease(sFont);
JNI_COCOA_EXIT(env);
+ return result;
}
/*
@@ -538,55 +3415,6 @@ static OSStatus CreateFSRef(FSRef *myFSRefPtr, NSString *inPath)
}
#endif
-/*
- * Class: sun_awt_FontDescriptor
- * Method: initIDs
- * Signature: ()V
- */
-JNIEXPORT void JNICALL
-Java_sun_font_CFont_getCascadeList
- (JNIEnv *env, jclass cls, jlong awtFontPtr, jobject arrayListOfString)
-{
-JNI_COCOA_ENTER(env);
- jclass alc = (*env)->FindClass(env, "java/util/ArrayList");
- if (alc == NULL) return;
- jmethodID addMID = (*env)->GetMethodID(env, alc, "add", "(Ljava/lang/Object;)Z");
- if (addMID == NULL) return;
-
- CFIndex i;
- AWTFont *awtFont = (AWTFont *)jlong_to_ptr(awtFontPtr);
- NSFont* nsFont = awtFont->fFont;
- CTFontRef font = (CTFontRef)nsFont;
- CFArrayRef codes = CFLocaleCopyISOLanguageCodes();
-
-#ifdef DEBUG
- CFStringRef base = CTFontCopyFullName(font);
- NSLog(@"BaseFont is : %@", (NSString*)base);
- CFRelease(base);
-#endif
- CFArrayRef fds = CTFontCopyDefaultCascadeListForLanguages(font, codes);
- CFRelease(codes);
- CFIndex cnt = CFArrayGetCount(fds);
- for (i=0; iCallBooleanMethod(env, arrayListOfString, addMID, jFontName);
- if ((*env)->ExceptionOccurred(env)) {
- CFRelease(fds);
- return;
- }
- (*env)->DeleteLocalRef(env, jFontName);
- }
- CFRelease(fds);
-JNI_COCOA_EXIT(env);
-}
-
static CFStringRef EMOJI_FONT_NAME = CFSTR("Apple Color Emoji");
bool IsEmojiFont(CTFontRef font)
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/font/AWTStrike.h b/src/java.desktop/macosx/native/libawt_lwawt/font/AWTStrike.h
index 05ef198764bd..cd8ba219036b 100644
--- a/src/java.desktop/macosx/native/libawt_lwawt/font/AWTStrike.h
+++ b/src/java.desktop/macosx/native/libawt_lwawt/font/AWTStrike.h
@@ -34,6 +34,9 @@
CGFloat fSize;
JRSFontRenderingStyle fStyle;
jint fAAStyle;
+ jint fFmHint;
+ jint fSubpixelResolutionX;
+ jint fSubpixelResolutionY;
CGAffineTransform fTx;
CGAffineTransform fDevTx;
@@ -41,6 +44,13 @@
CGAffineTransform fFontTx;
}
-+ (AWTStrike *) awtStrikeForFont:(AWTFont *)awtFont tx:(CGAffineTransform)tx invDevTx:(CGAffineTransform)invDevTx style:(JRSFontRenderingStyle)style aaStyle:(jint)aaStyle;
++ (AWTStrike *) awtStrikeForFont:(AWTFont *)awtFont
+ tx:(CGAffineTransform)tx
+ invDevTx:(CGAffineTransform)invDevTx
+ style:(JRSFontRenderingStyle)style
+ aaStyle:(jint)aaStyle
+ fmHint:(jint)fmHint
+ subpixelResolutionX:(jint)subpixelResolutionX
+ subpixelResolutionY:(jint)subpixelResolutionY;
@end
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/font/AWTStrike.m b/src/java.desktop/macosx/native/libawt_lwawt/font/AWTStrike.m
index 1e2ba23d9aa4..cf8e1070ca0e 100644
--- a/src/java.desktop/macosx/native/libawt_lwawt/font/AWTStrike.m
+++ b/src/java.desktop/macosx/native/libawt_lwawt/font/AWTStrike.m
@@ -31,6 +31,7 @@
#import "CoreTextSupport.h"
#import "JNIUtilities.h"
#include "fontscalerdefs.h"
+#import "LWCToolkit.h"
@implementation AWTStrike
@@ -40,13 +41,19 @@ - (id) initWithFont:(AWTFont *)awtFont
tx:(CGAffineTransform)tx
invDevTx:(CGAffineTransform)invDevTx
style:(JRSFontRenderingStyle)style
- aaStyle:(jint)aaStyle {
+ aaStyle:(jint)aaStyle
+ fmHint:(jint)fmHint
+subpixelResolutionX:(jint)subpixelResolutionX
+subpixelResolutionY:(jint)subpixelResolutionY {
self = [super init];
if (self) {
fAWTFont = [awtFont retain];
fStyle = style;
fAAStyle = aaStyle;
+ fFmHint = fmHint;
+ fSubpixelResolutionX = subpixelResolutionX;
+ fSubpixelResolutionY = subpixelResolutionY;
fTx = tx; // composited glyph and device transform
@@ -76,12 +83,18 @@ + (AWTStrike *) awtStrikeForFont:(AWTFont *)awtFont
tx:(CGAffineTransform)tx
invDevTx:(CGAffineTransform)invDevTx
style:(JRSFontRenderingStyle)style
- aaStyle:(jint)aaStyle {
+ aaStyle:(jint)aaStyle
+ fmHint:(jint)fmHint
+ subpixelResolutionX:(jint)subpixelResolutionX
+ subpixelResolutionY:(jint)subpixelResolutionY {
return [[[AWTStrike alloc] initWithFont:awtFont
tx:tx invDevTx:invDevTx
style:style
- aaStyle:aaStyle] autorelease];
+ aaStyle:aaStyle
+ fmHint:fmHint
+ subpixelResolutionX:subpixelResolutionX
+ subpixelResolutionY:subpixelResolutionY] autorelease];
}
@end
@@ -151,7 +164,7 @@ + (AWTStrike *) awtStrikeForFont:(AWTFont *)awtFont
// to indicate we should use CoreText to substitute the character
CGGlyph glyph;
const CTFontRef fallback = CTS_CopyCTFallbackFontAndGlyphForJavaGlyphCode(awtFont, glyphCode, &glyph);
- CGGlyphImages_GetGlyphMetrics(fallback, &awtStrike->fAltTx, awtStrike->fStyle, &glyph, 1, NULL, &advance);
+ CGGlyphImages_GetGlyphMetrics(fallback, &awtStrike->fAltTx, awtStrike->fSize, awtStrike->fStyle, &glyph, 1, NULL, &advance, IS_OSX_GT10_14);
CFRelease(fallback);
advance = CGSizeApplyAffineTransform(advance, awtStrike->fFontTx);
if (!JRSFontStyleUsesFractionalMetrics(awtStrike->fStyle)) {
@@ -188,7 +201,7 @@ + (AWTStrike *) awtStrikeForFont:(AWTFont *)awtFont
const CTFontRef fallback = CTS_CopyCTFallbackFontAndGlyphForJavaGlyphCode(awtFont, glyphCode, &glyph);
CGRect bbox;
- CGGlyphImages_GetGlyphMetrics(fallback, &tx, awtStrike->fStyle, &glyph, 1, &bbox, NULL);
+ CGGlyphImages_GetGlyphMetrics(fallback, &tx, awtStrike->fSize, awtStrike->fStyle, &glyph, 1, &bbox, NULL, IS_OSX_GT10_14);
CFRelease(fallback);
// the origin of this bounding box is relative to the bottom-left corner baseline
@@ -205,20 +218,11 @@ + (AWTStrike *) awtStrikeForFont:(AWTFont *)awtFont
JNI_COCOA_EXIT(env);
}
-/*
- * Class: sun_font_CStrike
- * Method: getNativeGlyphOutline
- * Signature: (JJIDD)Ljava/awt/geom/GeneralPath;
- */
-JNIEXPORT jobject JNICALL
-Java_sun_font_CStrike_getNativeGlyphOutline
- (JNIEnv *env, jclass clazz,
- jlong awtStrikePtr, jint glyphCode, jdouble xPos, jdouble yPos)
+jobject getGlyphOutline(JNIEnv *env, AWTStrike *awtStrike,
+ CTFontRef font, CGGlyph glyph,
+ jdouble xPos, jdouble yPos)
{
jobject generalPath = NULL;
-
-JNI_COCOA_ENTER(env);
-
AWTPathRef path = NULL;
jfloatArray pointCoords = NULL;
jbyteArray pointTypes = NULL;
@@ -228,21 +232,11 @@ + (AWTStrike *) awtStrikeForFont:(AWTFont *)awtFont
AWT_FONT_CLEANUP_SETUP;
- AWTStrike *awtStrike = (AWTStrike *)jlong_to_ptr(awtStrikePtr);
- AWTFont *awtfont = awtStrike->fAWTFont;
-
-AWT_FONT_CLEANUP_CHECK(awtfont);
-
// inverting the shear order and sign to compensate for the flipped coordinate system
CGAffineTransform tx = awtStrike->fTx;
tx.tx += xPos;
tx.ty += yPos;
- // get the right font and glyph for this "Java GlyphCode"
-
- CGGlyph glyph;
- const CTFontRef font = CTS_CopyCTFallbackFontAndGlyphForJavaGlyphCode(awtfont, glyphCode, &glyph);
-
// get the advance of this glyph
CGSize advance;
CTFontGetAdvancesForGlyphs(font, kCTFontDefaultOrientation, &glyph, &advance, 1);
@@ -255,7 +249,6 @@ + (AWTStrike *) awtStrikeForFont:(AWTFont *)awtFont
tx = awtStrike->fTx;
tx = CGAffineTransformConcat(tx, sInverseTX);
AWTGetGlyphOutline(&glyph, (NSFont *)font, &advance, &tx, 0, 1, &path);
- CFRelease(font);
pointCoords = (*env)->NewFloatArray(env, path->fNumberOfDataElements);
AWT_FONT_CLEANUP_CHECK(pointCoords);
@@ -289,10 +282,264 @@ + (AWTStrike *) awtStrikeForFont:(AWTFont *)awtFont
}
AWT_FONT_CLEANUP_FINISH;
-JNI_COCOA_EXIT(env);
return generalPath;
}
+/*
+ * Class: sun_font_CStrike
+ * Method: getNativeGlyphOutline
+ * Signature: (JJIDD)Ljava/awt/geom/GeneralPath;
+ */
+JNIEXPORT jobject JNICALL
+Java_sun_font_CStrike_getNativeGlyphOutline
+ (JNIEnv *env, jclass clazz,
+ jlong awtStrikePtr, jint glyphCode, jdouble xPos, jdouble yPos) {
+ jobject generalPath = NULL;
+
+ JNI_COCOA_ENTER(env);
+ AWT_FONT_CLEANUP_SETUP;
+
+ AWTStrike *awtStrike = (AWTStrike *)jlong_to_ptr(awtStrikePtr);
+ AWTFont *awtfont = awtStrike->fAWTFont;
+ AWT_FONT_CLEANUP_CHECK(awtfont);
+
+ // get the right font and glyph for this "Java GlyphCode"
+ CGGlyph glyph;
+ const CTFontRef font = CTS_CopyCTFallbackFontAndGlyphForJavaGlyphCode(awtfont, glyphCode, &glyph);
+
+ generalPath = getGlyphOutline(env, awtStrike, font, glyph, xPos, yPos);
+
+ cleanup:
+ CFRelease(font);
+
+ AWT_FONT_CLEANUP_FINISH;
+ JNI_COCOA_EXIT(env);
+
+ return generalPath;
+}
+
+// OpenType data is Big-Endian
+#define GET_BE_INT32(data, i) CFSwapInt32BigToHost(*(const UInt32*) ((const UInt8*) (data) + (i)))
+#define GET_BE_INT16(data, i) CFSwapInt16BigToHost(*(const UInt16*) ((const UInt8*) (data) + (i)))
+
+static bool addBitmapRenderData(JNIEnv *env, AWTStrike *awtStrike,
+ CTFontRef font, CGGlyph glyph,
+ jdouble xPos, jdouble yPos, jobject result) {
+ bool success = false;
+
+ DECLARE_CLASS_RETURN(jc_GlyphRenderData, "sun/font/GlyphRenderData", false);
+ DECLARE_METHOD_RETURN(GlyphRenderDataAddBitmap, jc_GlyphRenderData, "addBitmap", "(DDDDDDIIII[I)V", false);
+
+ AWT_FONT_CLEANUP_SETUP;
+
+ CTFontDescriptorRef descriptor = NULL;
+ CGFontRef cgFont = CTFontCopyGraphicsFont(font, &descriptor);
+
+ CFDataRef sbixTable = CGFontCopyTableForTag(cgFont, kCTFontTableSbix);
+ if (sbixTable == NULL) {
+ goto cleanup;
+ }
+
+ // Parse sbix table
+ CFIndex sbixSize = CFDataGetLength(sbixTable);
+ if (sbixSize < 8) {
+ goto cleanup; // Corrupted table
+ }
+ const UInt8* sbix = CFDataGetBytePtr(sbixTable);
+ UInt32 numStrikes = GET_BE_INT32(sbix, 4);
+ if (8 + 4 * numStrikes > sbixSize) {
+ goto cleanup; // Corrupted table
+ }
+ // Find last strike which has data for our glyph
+ // Last is usually the biggest
+ const UInt8* glyphData = NULL;
+ UInt32 size;
+ UInt16 ppem, ppi;
+ for (int i = numStrikes - 1; i >= 0; i--) {
+ const UInt8* strike = sbix + GET_BE_INT32(sbix, 8 + 4 * i);
+ if (strike + 12 + 4 * glyph > sbix + sbixSize) {
+ goto cleanup; // Corrupted table
+ }
+ UInt32 offset = GET_BE_INT32(strike, 4 + 4 * glyph);
+ size = GET_BE_INT32(strike, 8 + 4 * glyph) - offset;
+ if (size > 0) {
+ ppem = GET_BE_INT16(strike, 0);
+ ppi = GET_BE_INT16(strike, 2);
+ glyphData = strike + offset;
+ break;
+ }
+ }
+ if (glyphData == NULL) {
+ goto cleanup;
+ }
+ if (glyphData + 4 > sbix + sbixSize) {
+ goto cleanup; // Corrupted table
+ }
+
+ // Read glyph data
+ FourCharCode graphicType = GET_BE_INT32(glyphData, 4);
+ glyphData += 8;
+ size -= 8;
+ if (glyphData + size > sbix + sbixSize) {
+ goto cleanup; // Corrupted table
+ }
+
+ // Decode glyph image
+ CGDataProviderRef dataProvider = CGDataProviderCreateWithData(NULL, glyphData, size, NULL);
+ CGImageRef image = NULL;
+ if (graphicType == 'jpg ') {
+ image = CGImageCreateWithJPEGDataProvider(dataProvider, NULL, false, kCGRenderingIntentDefault);
+ } else if (graphicType == 'png ') {
+ image = CGImageCreateWithPNGDataProvider(dataProvider, NULL, false, kCGRenderingIntentDefault);
+ }
+ CGDataProviderRelease(dataProvider);
+
+ if (image != NULL) {
+ CGBitmapInfo info = CGImageGetBitmapInfo(image);
+ size_t bits = CGImageGetBitsPerPixel(image);
+ jint colorModel = -1;
+ if (info & (kCGImageAlphaPremultipliedLast | kCGImageAlphaLast)) {
+ colorModel = 0; // RGBA
+ } else if (info & (kCGImageAlphaPremultipliedFirst | kCGImageAlphaFirst)) {
+ colorModel = 1; // ARGB
+ }
+ if (colorModel != -1 && (info & kCGBitmapFloatComponents) == 0 && bits == 32) {
+ size_t width = CGImageGetWidth(image);
+ size_t height = CGImageGetHeight(image);
+ size_t pitch = CGImageGetBytesPerRow(image) / 4;
+ dataProvider = CGImageGetDataProvider(image);
+ CFDataRef data = CGDataProviderCopyData(dataProvider);
+
+ jbyteArray array = (*env)->NewIntArray(env, pitch * height);
+ (*env)->SetIntArrayRegion(env, array, 0, pitch * height, (const jint*) CFDataGetBytePtr(data));
+ CFRelease(data);
+
+ double pointSize = 72.0 * ppem / ppi;
+ double scale = 1.0 / pointSize;
+ font = CTFontCreateWithGraphicsFont(cgFont, pointSize, NULL, descriptor);
+ CGRect bbox = CTFontGetBoundingRectsForGlyphs(font, kCTFontOrientationDefault, &glyph, NULL, 1);
+ CFRelease(font);
+ double tx = bbox.origin.x + xPos * pointSize / awtStrike->fSize;
+ double ty = -bbox.origin.y - (double) height + yPos * pointSize / awtStrike->fSize;
+
+ jdouble m00 = awtStrike->fTx.a * scale, m10 = awtStrike->fTx.b * scale;
+ jdouble m01 = -awtStrike->fTx.c * scale, m11 = -awtStrike->fTx.d * scale;
+ jdouble m02 = m00 * tx + m01 * ty, m12 = m10 * tx + m11 * ty;
+
+ (*env)->CallVoidMethod(env, result, GlyphRenderDataAddBitmap,
+ m00, m10, m01, m11, m02, m12,
+ width, height, pitch, 0, array);
+ success = true;
+ }
+ CGImageRelease(image);
+ }
+
+ // Cleanup
+ cleanup:
+ if (sbixTable) {
+ CFRelease(sbixTable);
+ }
+ if (cgFont) {
+ CFRelease(cgFont);
+ }
+ if (descriptor) {
+ CFRelease(descriptor);
+ }
+
+ AWT_FONT_CLEANUP_FINISH;
+ return success;
+}
+
+/*
+ * Class: sun_font_CStrike
+ * Method: getNativeGlyphRenderData
+ * Signature: (JIDDLsun/font/GlyphRenderData;)V
+ */
+JNIEXPORT void JNICALL
+Java_sun_font_CStrike_getNativeGlyphRenderData
+ (JNIEnv *env, jclass clazz,
+ jlong awtStrikePtr, jint glyphCode, jdouble xPos, jdouble yPos, jobject result)
+{
+ JNI_COCOA_ENTER(env);
+
+ DECLARE_CLASS(jc_GlyphRenderData, "sun/font/GlyphRenderData");
+ DECLARE_FIELD(GlyphRenderDataOutline, jc_GlyphRenderData, "outline", "Ljava/awt/geom/GeneralPath;")
+
+ AWT_FONT_CLEANUP_SETUP;
+
+ AWTStrike *awtStrike = (AWTStrike *)jlong_to_ptr(awtStrikePtr);
+ AWTFont *awtfont = awtStrike->fAWTFont;
+ AWT_FONT_CLEANUP_CHECK(awtfont);
+
+ // get the right font and glyph for this "Java GlyphCode"
+ CGGlyph glyph;
+ const CTFontRef font = CTS_CopyCTFallbackFontAndGlyphForJavaGlyphCode(awtfont, glyphCode, &glyph);
+
+ if (!addBitmapRenderData(env, awtStrike, font, glyph, xPos, yPos, result)) {
+ jobject gp = getGlyphOutline(env, awtStrike, font, glyph, xPos, yPos);
+ if (gp != NULL) {
+ (*env)->SetObjectField(env, result, GlyphRenderDataOutline, gp);
+ }
+ }
+
+ cleanup:
+ CFRelease(font);
+
+ AWT_FONT_CLEANUP_FINISH;
+ JNI_COCOA_EXIT(env);
+}
+
+/*
+ * Class: sun_font_CStrike
+ * Method: getNativeGlyphOutlineBounds
+ * Signature: (JILjava/awt/geom/Rectangle2D/Float;DD)V
+ */
+JNIEXPORT void JNICALL Java_sun_font_CStrike_getNativeGlyphOutlineBounds
+ (JNIEnv *env, jclass clazz, jlong awtStrikePtr, jint glyphCode,
+ jfloatArray rectData)
+{
+ JNI_COCOA_ENTER(env);
+ AWTStrike *awtStrike = (AWTStrike *)jlong_to_ptr(awtStrikePtr);
+ AWTFont *awtfont = awtStrike->fAWTFont;
+
+ AWT_FONT_CLEANUP_SETUP;
+ AWT_FONT_CLEANUP_CHECK(awtfont);
+
+ // get the right font and glyph for this "Java GlyphCode"
+ CGGlyph glyph;
+ const CTFontRef font = CTS_CopyCTFallbackFontAndGlyphForJavaGlyphCode(
+ awtfont, glyphCode, &glyph);
+
+ CGRect bbox = CTFontGetBoundingRectsForGlyphs(
+ font, kCTFontOrientationDefault, &glyph, NULL, 1);
+
+ CGAffineTransform tx = CGAffineTransformConcat(awtStrike->fTx,
+ sInverseTX);
+
+ bbox = CGRectApplyAffineTransform (bbox, tx);
+ CFRelease(font);
+ jfloat *rawRectData =
+ (*env)->GetPrimitiveArrayCritical(env, rectData, NULL);
+
+ if (CGRectIsNull(bbox)) {
+ rawRectData[0] = 0.0f;
+ rawRectData[1] = 0.0f;
+ rawRectData[2] = 0.0f;
+ rawRectData[3] = 0.0f;
+ } else {
+ rawRectData[0] = (jfloat) bbox.origin.x;
+ rawRectData[1] = (jfloat) (-bbox.origin.y - bbox.size.height);
+ rawRectData[2] = (jfloat) bbox.size.width;
+ rawRectData[3] = (jfloat) bbox.size.height;
+ }
+
+ (*env)->ReleasePrimitiveArrayCritical(env, rectData, rawRectData, 0);
+ // Cleanup
+ cleanup:
+ AWT_FONT_CLEANUP_FINISH;
+
+ JNI_COCOA_EXIT(env);
+}
/*
* Class: sun_font_CStrike
* Method: getGlyphImagePtrsNative
@@ -341,7 +588,8 @@ + (AWTStrike *) awtStrikeForFont:(AWTFont *)awtFont
* Signature: (J[D[DII)J
*/
JNIEXPORT jlong JNICALL Java_sun_font_CStrike_createNativeStrikePtr
-(JNIEnv *env, jclass clazz, jlong nativeFontPtr, jdoubleArray glyphTxArray, jdoubleArray invDevTxArray, jint aaStyle, jint fmHint)
+(JNIEnv *env, jclass clazz, jlong nativeFontPtr, jdoubleArray glyphTxArray, jdoubleArray invDevTxArray,
+ jint aaStyle, jint fmHint, jint subpixelResolutionX, jint subpixelResolutionY)
{
AWTStrike *awtStrike = nil;
JNI_COCOA_ENTER(env);
@@ -352,7 +600,8 @@ + (AWTStrike *) awtStrikeForFont:(AWTFont *)awtFont
CGAffineTransform glyphTx = GetTxFromDoubles(env, glyphTxArray);
CGAffineTransform invDevTx = GetTxFromDoubles(env, invDevTxArray);
- awtStrike = [AWTStrike awtStrikeForFont:awtFont tx:glyphTx invDevTx:invDevTx style:style aaStyle:aaStyle]; // autoreleased
+ awtStrike = [AWTStrike awtStrikeForFont:awtFont tx:glyphTx invDevTx:invDevTx style:style
+ aaStyle:aaStyle fmHint:fmHint subpixelResolutionX:subpixelResolutionX subpixelResolutionY:subpixelResolutionY]; // autoreleased
if (awtStrike)
{
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/font/CCharToGlyphMapper.m b/src/java.desktop/macosx/native/libawt_lwawt/font/CCharToGlyphMapper.m
index e0cefbe01dc4..f1e1a8fb4d10 100644
--- a/src/java.desktop/macosx/native/libawt_lwawt/font/CCharToGlyphMapper.m
+++ b/src/java.desktop/macosx/native/libawt_lwawt/font/CCharToGlyphMapper.m
@@ -29,6 +29,7 @@
#import "CoreTextSupport.h"
#import "sun_font_CCharToGlyphMapper.h"
+#import "sun_font_CCompositeGlyphMapper.h"
/*
* Class: sun_font_CCharToGlyphMapper
@@ -115,3 +116,29 @@
JNI_COCOA_EXIT(env);
}
+
+/*
+ * Class: sun_font_CCompositeGlyphMapper
+ * Method: nativeCodePointToGlyph
+ * Signature: (JII[Ljava/lang/String;)I
+ */
+JNIEXPORT jint JNICALL
+Java_sun_font_CCompositeGlyphMapper_nativeCodePointToGlyph
+(JNIEnv *env, jclass clazz, jlong awtFontPtr, jint codePoint, jint variationSelector, jobjectArray resultArray)
+{
+JNI_COCOA_ENTER(env);
+ AWTFont *awtFont = (AWTFont *)jlong_to_ptr(awtFontPtr);
+ CFStringRef fontNames[] = {NULL, NULL};
+ CGGlyph glyph = CTS_CopyGlyphAndFontNamesForCodePoint(awtFont, (UnicodeScalarValue)codePoint,
+ (UnicodeScalarValue)variationSelector, fontNames);
+ if (glyph > 0) {
+ jstring fontName = NSStringToJavaString(env, (NSString *)fontNames[0]);
+ (*env)->SetObjectArrayElement(env, resultArray, 0, fontName);
+ jstring fontFamilyName = NSStringToJavaString(env, (NSString *)fontNames[1]);
+ (*env)->SetObjectArrayElement(env, resultArray, 1, fontFamilyName);
+ }
+ if (fontNames[0]) CFRelease(fontNames[0]);
+ if (fontNames[1]) CFRelease(fontNames[1]);
+ return glyph;
+JNI_COCOA_EXIT(env);
+}
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/font/CGGlyphImages.h b/src/java.desktop/macosx/native/libawt_lwawt/font/CGGlyphImages.h
index 736167d2af4c..2415ec5093f1 100644
--- a/src/java.desktop/macosx/native/libawt_lwawt/font/CGGlyphImages.h
+++ b/src/java.desktop/macosx/native/libawt_lwawt/font/CGGlyphImages.h
@@ -36,9 +36,11 @@ CGGlyphImages_GetGlyphImagePtrs(jlong glyphInfos[],
void
CGGlyphImages_GetGlyphMetrics(const CTFontRef font,
const CGAffineTransform *tx,
+ CGFloat fontSize,
const JRSFontRenderingStyle style,
const CGGlyph glyphs[],
size_t count,
CGRect bboxes[],
- CGSize advances[]);
+ CGSize advances[],
+ const bool isCatalinaOrAbove);
#endif /* __CGGLYPHIMAGES_H */
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/font/CGGlyphImages.m b/src/java.desktop/macosx/native/libawt_lwawt/font/CGGlyphImages.m
index f6e8fe6af552..0378c004845d 100644
--- a/src/java.desktop/macosx/native/libawt_lwawt/font/CGGlyphImages.m
+++ b/src/java.desktop/macosx/native/libawt_lwawt/font/CGGlyphImages.m
@@ -31,6 +31,7 @@
#import "fontscalerdefs.h" // contains the definition of GlyphInfo struct
#import "sun_awt_SunHints.h"
+#import "LWCToolkit.h"
//#define USE_IMAGE_ALIGNED_MEMORY 1
//#define CGGI_DEBUG 1
@@ -287,24 +288,27 @@ @implementation CGGI_GlyphCanvas
static void
CGGI_CopyImageFromCanvasToAlphaInfo(CGGI_GlyphCanvas *canvas, GlyphInfo *info)
{
- UInt32 *src = (UInt32 *)canvas->image->data;
size_t srcRowWidth = canvas->image->width;
- UInt8 *dest = (UInt8 *)info->image;
size_t destRowWidth = info->width;
-
size_t height = info->height;
- size_t y;
-
- // fill empty glyph image with black-on-white glyph
- for (y = 0; y < height; y++) {
- size_t destRow = y * destRowWidth;
- size_t srcRow = y * srcRowWidth;
- size_t x;
- for (x = 0; x < destRowWidth; x++) {
- UInt32 p = src[srcRow + x];
- dest[destRow + x] = CGGI_ConvertBWPixelToByteGray(p);
+ for (int sy = 0; sy < info->subpixelResolutionY; sy++) {
+ for (int sx = 0; sx < info->subpixelResolutionX; sx++) {
+ UInt32 *src = (UInt32 *)canvas->image->data + sy * height * srcRowWidth + sx * destRowWidth;
+ UInt8 *dest = (UInt8 *)info->image + (info->subpixelResolutionX * sy + sx) * destRowWidth * height;
+
+ size_t y;
+ // fill empty glyph image with black-on-white glyph
+ for (y = 0; y < height; y++) {
+ size_t destRow = y * destRowWidth;
+ size_t srcRow = y * srcRowWidth;
+ size_t x;
+ for (x = 0; x < destRowWidth; x++) {
+ UInt32 p = src[srcRow + x];
+ dest[destRow + x] = CGGI_ConvertBWPixelToByteGray(p);
+ }
+ }
}
}
}
@@ -352,8 +356,10 @@ @implementation CGGI_GlyphCanvas
} CGGI_GlyphInfoDescriptor;
typedef struct CGGI_RenderingMode {
- CGGI_GlyphInfoDescriptor *glyphDescriptor;
+ CGGI_GlyphInfoDescriptor *mainFontDescriptor;
JRSFontRenderingStyle cgFontMode;
+ bool lcdRendering;
+ bool subpixelResolution;
} CGGI_RenderingMode;
static CGGI_GlyphInfoDescriptor grey =
@@ -366,7 +372,7 @@ @implementation CGGI_GlyphCanvas
static inline CGGI_GlyphInfoDescriptor*
CGGI_GetGlyphInfoDescriptor(const CGGI_RenderingMode *mode, CTFontRef font)
{
- return IsEmojiFont(font) ? &argb : mode->glyphDescriptor;
+ return IsEmojiFont(font) ? &argb : mode->mainFontDescriptor;
}
static inline CGGI_RenderingMode
@@ -376,16 +382,26 @@ @implementation CGGI_GlyphCanvas
mode.cgFontMode = strike->fStyle;
NSException *e = nil;
+#ifdef USE_IMAGE_ALIGNED_MEMORY
+ mode.subpixelResolution = false;
+#else
+ mode.subpixelResolution = strike->fAAStyle == sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_ON &&
+ strike->fFmHint == sun_awt_SunHints_INTVAL_FRACTIONALMETRICS_ON &&
+ (strike->fSubpixelResolutionX > 1 || strike->fSubpixelResolutionY > 1);
+#endif
+
switch (strike->fAAStyle) {
case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_OFF:
case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_ON:
- mode.glyphDescriptor = &grey;
+ mode.lcdRendering = false;
+ mode.mainFontDescriptor = &grey;
break;
case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_HRGB:
case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_HBGR:
case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_VRGB:
case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_VBGR:
- mode.glyphDescriptor = &rgb;
+ mode.lcdRendering = true;
+ mode.mainFontDescriptor = &rgb;
break;
case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_GASP:
case sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_DEFAULT:
@@ -399,6 +415,10 @@ @implementation CGGI_GlyphCanvas
userInfo:nil];
@throw e;
}
+ if (IsEmojiFont((CTFontRef)strike->fAWTFont->fFont)) {
+ mode.mainFontDescriptor = &argb;
+ mode.subpixelResolution = false;
+ }
return mode;
}
@@ -431,7 +451,7 @@ @implementation CGGI_GlyphCanvas
}
uint32_t bmpInfo = kCGImageAlphaPremultipliedFirst;
- if (mode->glyphDescriptor == &rgb) {
+ if (mode->lcdRendering) {
bmpInfo |= kCGBitmapByteOrder32Host;
}
@@ -509,8 +529,8 @@ @implementation CGGI_GlyphCanvas
{
vImage_Buffer canvasRectToClear;
canvasRectToClear.data = canvas->image->data;
- canvasRectToClear.height = info->height;
- canvasRectToClear.width = info->width;
+ canvasRectToClear.height = info->height * info->subpixelResolutionY;
+ canvasRectToClear.width = info->width * info->subpixelResolutionX;
// use the row stride of the canvas, not the info
canvasRectToClear.rowBytes = canvas->image->rowBytes;
@@ -538,7 +558,8 @@ @implementation CGGI_GlyphCanvas
static inline GlyphInfo *
CGGI_CreateNewGlyphInfoFrom(CGSize advance, CGRect bbox,
const AWTStrike *strike,
- const CGGI_GlyphInfoDescriptor *glyphDescriptor)
+ const CGGI_GlyphInfoDescriptor *glyphDescriptor,
+ bool subpixelResolution)
{
size_t pixelSize = glyphDescriptor->pixelSize;
@@ -550,8 +571,23 @@ @implementation CGGI_GlyphCanvas
bbox.origin.x -= CGGI_GLYPH_BBOX_PADDING;
bbox.origin.y -= CGGI_GLYPH_BBOX_PADDING;
- vImagePixelCount width = ceilf(bbox.size.width);
- vImagePixelCount height = ceilf(bbox.size.height);
+ int subpixelResX = 1;
+ int subpixelResY = 1;
+ int topLeftX, topLeftY;
+ vImagePixelCount width, height;
+ if (subpixelResolution) {
+ subpixelResX = strike->fSubpixelResolutionX;
+ subpixelResY = strike->fSubpixelResolutionY;
+ topLeftX = floorf(bbox.origin.x);
+ topLeftY = floorf(bbox.origin.y);
+ width = ceilf(bbox.origin.x + bbox.size.width) - topLeftX;
+ height = ceilf(bbox.origin.y + bbox.size.height) - topLeftY;
+ } else {
+ topLeftX = round(bbox.origin.x);
+ topLeftY = round(bbox.origin.y);
+ width = ceilf(bbox.size.width);
+ height = ceilf(bbox.size.height);
+ }
// if the glyph is larger than 1MB, don't even try...
// the GlyphVector path should have taken over by now
@@ -567,24 +603,31 @@ @implementation CGGI_GlyphCanvas
}
advance = CGSizeApplyAffineTransform(advance, strike->fDevTx);
+ int imageBytes = height * width * pixelSize;
+ int extraPixelStorage = (strike->fAAStyle == sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_LCD_HRGB &&
+ strike->fFmHint == sun_awt_SunHints_INTVAL_FRACTIONALMETRICS_ON) ? 3 : 0;
+
#ifdef USE_IMAGE_ALIGNED_MEMORY
// create separate memory
GlyphInfo *glyphInfo = (GlyphInfo *)malloc(sizeof(GlyphInfo));
- void *image = (void *)malloc(height * width * pixelSize);
+ void *image = (void *)malloc(imageBytes + extraPixelStorage);
#else
// create a GlyphInfo struct fused to the image it points to
GlyphInfo *glyphInfo = (GlyphInfo *)malloc(sizeof(GlyphInfo) +
- height * width * pixelSize);
+ (imageBytes + extraPixelStorage) * subpixelResX * subpixelResY);
#endif
glyphInfo->advanceX = advance.width;
glyphInfo->advanceY = advance.height;
- glyphInfo->topLeftX = round(bbox.origin.x);
- glyphInfo->topLeftY = round(bbox.origin.y);
+ glyphInfo->topLeftX = topLeftX;
+ glyphInfo->topLeftY = topLeftY;
glyphInfo->width = width;
glyphInfo->height = height;
glyphInfo->rowBytes = width * pixelSize;
glyphInfo->cellInfo = NULL;
+ glyphInfo->subpixelResolutionX = subpixelResX;
+ glyphInfo->subpixelResolutionY = subpixelResY;
+ glyphInfo->format = (UInt8) pixelSize;
#ifdef USE_IMAGE_ALIGNED_MEMORY
glyphInfo->image = image;
@@ -592,12 +635,28 @@ @implementation CGGI_GlyphCanvas
glyphInfo->image = ((void *)glyphInfo) + sizeof(GlyphInfo);
#endif
+ int i;
+ for (i = 0; i < extraPixelStorage; i++) (glyphInfo->image)[imageBytes + i] = 0;
+
return glyphInfo;
}
#pragma mark --- Glyph Striking onto Canvas ---
+#define RENDER_GLYPH_BATCH_SIZE 16
+#define RENDER_GLYPH_ARRAY_INIT_8 glyph,glyph,glyph,glyph,glyph,glyph,glyph,glyph
+#define RENDER_GLYPH_ARRAY_INIT RENDER_GLYPH_ARRAY_INIT_8,RENDER_GLYPH_ARRAY_INIT_8
+
+static CTFontRef CopyFontWithSize(CTFontRef originalFont, CGFloat size) {
+ CTFontDescriptorRef descriptor = NULL;
+ CGFontRef cgFont = CTFontCopyGraphicsFont(originalFont, &descriptor);
+ CTFontRef result = CTFontCreateWithGraphicsFont(cgFont, size, NULL, descriptor);
+ if (cgFont) CFRelease(cgFont);
+ if (descriptor) CFRelease(descriptor);
+ return result;
+}
+
/*
* Clears the canvas, strikes the glyph with CoreGraphics, and then
* copies the struck pixels into the GlyphInfo image.
@@ -605,7 +664,7 @@ @implementation CGGI_GlyphCanvas
static inline void
CGGI_CreateImageForGlyph
(CGGI_GlyphCanvas *canvas, const CGGlyph glyph,
- GlyphInfo *info, const CGGI_GlyphInfoDescriptor *glyphDescriptor, const AWTStrike *strike, CTFontRef font)
+ GlyphInfo *info, const CGGI_GlyphInfoDescriptor *glyphDescriptor, const AWTStrike *strike, CTFontRef font, const bool isCatalinaOrAbove)
{
if (isnan(info->topLeftX) || isnan(info->topLeftY)) {
// Explicitly set glyphInfo width/height to be 0 to ensure
@@ -620,12 +679,14 @@ @implementation CGGI_GlyphCanvas
// clean the canvas
CGGI_ClearCanvas(canvas, info, glyphDescriptor == &argb);
+ CGContextSetShouldAntialias(canvas->context, strike->fAAStyle != sun_awt_SunHints_INTVAL_TEXT_ANTIALIAS_OFF);
// strike the glyph in the upper right corner
CGFloat x = -info->topLeftX;
CGFloat y = canvas->image->height + info->topLeftY;
- if (glyphDescriptor == &argb) {
+ CGPoint subpixelOffset = CGPointMake(1.0 / (float) info->subpixelResolutionX, 1.0 / (float) info->subpixelResolutionY);
+ if (isCatalinaOrAbove || glyphDescriptor == &argb) {
// Emoji glyphs are not rendered by CGContextShowGlyphsAtPoint.
// Also, it's not possible to use transformation matrix to get the emoji glyph
// rendered for the desired font size - actual-size font object is needed.
@@ -633,25 +694,52 @@ @implementation CGGI_GlyphCanvas
// which calculates glyph metrics.
CGAffineTransform matrix = CGContextGetTextMatrix(canvas->context);
- CGFloat fontSize = sqrt(fabs(matrix.a * matrix.d - matrix.b * matrix.c));
- CTFontRef sizedFont = CTFontCreateCopyWithSymbolicTraits(font, fontSize, NULL, 0, 0);
+ // Set actual font size from transformation matrix for color glyphs
+ CGFloat fontSize = glyphDescriptor != &argb ? strike->fSize :
+ sqrt(fabs(matrix.a * matrix.d - matrix.b * matrix.c));
+ CTFontRef sizedFont = CopyFontWithSize(font, fontSize);
CGFloat normFactor = 1.0 / fontSize;
CGAffineTransform normalizedMatrix = CGAffineTransformScale(matrix, normFactor, normFactor);
CGContextSetTextMatrix(canvas->context, normalizedMatrix);
- CGPoint userPoint = CGPointMake(x, y);
CGAffineTransform normalizedMatrixInv = CGAffineTransformInvert(normalizedMatrix);
- CGPoint textPoint = CGPointApplyAffineTransform(userPoint, normalizedMatrixInv);
- CTFontDrawGlyphs(sizedFont, &glyph, &textPoint, 1, canvas->context);
+ // Render glyphs with subpixel offsets in batches up to 16 per draw call.
+ // Maximum subpixel resolution is 16 in each dimension, which is 256 total images, which is too crazy.
+ // In practice 4x1 is enough, so we can draw up to something like 4x4 in one call.
+ const CGGlyph glyphs[RENDER_GLYPH_BATCH_SIZE] = {RENDER_GLYPH_ARRAY_INIT};
+ CGPoint glyphPositions[RENDER_GLYPH_BATCH_SIZE];
+
+ int glyphIndex = 0;
+ for (int sy = 0; sy < info->subpixelResolutionY; sy++) {
+ for (int sx = 0; sx < info->subpixelResolutionX; sx++) {
+ CGPoint userPoint = CGPointMake(x + info->width * sx + subpixelOffset.x * (float) sx,
+ y - info->height * sy - subpixelOffset.y * (float) sy);
+ glyphPositions[glyphIndex] = CGPointApplyAffineTransform(userPoint, normalizedMatrixInv);
+ glyphIndex++;
+ if (glyphIndex >= RENDER_GLYPH_BATCH_SIZE) {
+ CTFontDrawGlyphs(sizedFont, glyphs, glyphPositions, glyphIndex, canvas->context);
+ glyphIndex = 0;
+ }
+ }
+ }
+ if (glyphIndex > 0) {
+ CTFontDrawGlyphs(sizedFont, glyphs, glyphPositions, glyphIndex, canvas->context);
+ }
CFRelease(sizedFont);
// restore context's original state
CGContextSetTextMatrix(canvas->context, matrix);
CGContextSetFontSize(canvas->context, 1); // CTFontDrawGlyphs tampers with it
} else {
- CGContextShowGlyphsAtPoint(canvas->context, x, y, &glyph, 1);
+ for (int sy = 0; sy < info->subpixelResolutionY; sy++) {
+ for (int sx = 0; sx < info->subpixelResolutionX; sx++) {
+ CGContextShowGlyphsAtPoint(canvas->context,
+ x + info->width * sx + subpixelOffset.x * (float) sx,
+ y - info->height * sy - subpixelOffset.y * (float) sy, &glyph, 1);
+ }
+ }
}
// copy the glyph from the canvas into the info
@@ -664,10 +752,13 @@ @implementation CGGI_GlyphCanvas
static inline GlyphInfo *
CGGI_CreateImageForUnicode
(CGGI_GlyphCanvas *canvas, const AWTStrike *strike,
- const CGGI_RenderingMode *mode, const UnicodeScalarValue uniChar)
+ const CGGI_RenderingMode *mode, const UnicodeScalarValue uniChar,
+ const bool isCatalinaOrAbove)
{
- // save the state of the world
+ // save the graphics state
CGContextSaveGState(canvas->context);
+ // text matrix is not considered part of graphics state
+ CGAffineTransform originalTx = CGContextGetTextMatrix(canvas->context);
// get the glyph, measure it using CG
CGGlyph glyph;
@@ -684,20 +775,22 @@ @implementation CGGI_GlyphCanvas
fallback = CTS_CopyCTFallbackFontAndGlyphForUnicode(strike->fAWTFont, (const UTF16Char *)&charRef, &glyph, 1);
}
- CGAffineTransform tx = strike->fTx;
JRSFontRenderingStyle style = JRSFontAlignStyleForFractionalMeasurement(strike->fStyle);
+ CGGI_GlyphInfoDescriptor *glyphDescriptor = CGGI_GetGlyphInfoDescriptor(mode, fallback);
+
+ bool subpixelResolution = mode->subpixelResolution && glyphDescriptor == &grey;
+
CGRect bbox;
CGSize advance;
- CGGlyphImages_GetGlyphMetrics(fallback, &tx, style, &glyph, 1, &bbox, &advance);
+ CGGlyphImages_GetGlyphMetrics(fallback, &strike->fTx, strike->fSize, style, &glyph, 1, &bbox, &advance, isCatalinaOrAbove);
- CGGI_GlyphInfoDescriptor *glyphDescriptor = CGGI_GetGlyphInfoDescriptor(mode, fallback);
// create the Sun2D GlyphInfo we are going to strike into
- GlyphInfo *info = CGGI_CreateNewGlyphInfoFrom(advance, bbox, strike, glyphDescriptor);
+ GlyphInfo *info = CGGI_CreateNewGlyphInfoFrom(advance, bbox, strike, glyphDescriptor, subpixelResolution);
// fix the context size, just in case the substituted character is unexpectedly large
- CGGI_SizeCanvas(canvas, info->width, info->height, mode);
+ CGGI_SizeCanvas(canvas, info->width * info->subpixelResolutionX, info->height * info->subpixelResolutionY, mode);
// align the transform for the real CoreText strike
CGContextSetTextMatrix(canvas->context, strike->fAltTx);
@@ -707,10 +800,11 @@ @implementation CGGI_GlyphCanvas
CFRelease(cgFallback);
// clean the canvas - align, strike, and copy the glyph from the canvas into the info
- CGGI_CreateImageForGlyph(canvas, glyph, info, glyphDescriptor, strike, fallback);
+ CGGI_CreateImageForGlyph(canvas, glyph, info, glyphDescriptor, strike, fallback, isCatalinaOrAbove);
- // restore the state of the world
+ // restore graphics state
CGContextRestoreGState(canvas->context);
+ CGContextSetTextMatrix(canvas->context, originalTx);
CFRelease(fallback);
#ifdef CGGI_DEBUG
@@ -750,16 +844,15 @@ @implementation CGGI_GlyphCanvas
CGContextSetFont(canvas->context, strike->fAWTFont->fNativeCGFont);
JRSFontSetRenderingStyleOnContext(canvas->context, strike->fStyle);
- CTFontRef mainFont = (CTFontRef)strike->fAWTFont->fFont;
- CGGI_GlyphInfoDescriptor* mainFontDescriptor = CGGI_GetGlyphInfoDescriptor(mode, mainFont);
-
+ const bool isMojaveOrAbove = IS_OSX_GT10_13;
CFIndex i;
for (i = 0; i < len; i++) {
GlyphInfo *info = (GlyphInfo *)jlong_to_ptr(glyphInfos[i]);
if (info != NULL) {
- CGGI_CreateImageForGlyph(canvas, glyphs[i], info, mainFontDescriptor, strike, mainFont);
+ CGGI_CreateImageForGlyph(canvas, glyphs[i], info, mode->mainFontDescriptor,
+ strike, (CTFontRef)strike->fAWTFont->fFont, isMojaveOrAbove);
} else {
- info = CGGI_CreateImageForUnicode(canvas, strike, mode, uniChars[i]);
+ info = CGGI_CreateImageForUnicode(canvas, strike, mode, uniChars[i], isMojaveOrAbove);
glyphInfos[i] = ptr_to_jlong(info);
}
#ifdef CGGI_DEBUG
@@ -816,7 +909,7 @@ @implementation CGGI_GlyphCanvas
NSMutableDictionary *threadDict =
[[NSThread currentThread] threadDictionary];
- NSString* theKey = (mode->glyphDescriptor == &rgb) ?
+ NSString* theKey = mode->lcdRendering ?
threadLocalLCDCanvasKey : threadLocalAACanvasKey;
CGGI_GlyphCanvas *canvas = [threadDict objectForKey:theKey];
@@ -847,12 +940,10 @@ @implementation CGGI_GlyphCanvas
CGSize advances[], CGRect bboxes[], const CFIndex len)
{
AWTFont *font = strike->fAWTFont;
- CGAffineTransform tx = strike->fTx;
JRSFontRenderingStyle bboxCGMode = JRSFontAlignStyleForFractionalMeasurement(strike->fStyle);
CTFontRef fontRef = (CTFontRef)font->fFont;
- CGGlyphImages_GetGlyphMetrics(fontRef, &tx, bboxCGMode, glyphs, len, bboxes, advances);
- CGGI_GlyphInfoDescriptor* mainFontDescriptor = CGGI_GetGlyphInfoDescriptor(mode, fontRef);
+ CGGlyphImages_GetGlyphMetrics(fontRef, &strike->fTx, strike->fSize, bboxCGMode, glyphs, len, bboxes, advances, IS_OSX_GT10_14);
size_t maxWidth = 1;
size_t maxHeight = 1;
@@ -869,10 +960,15 @@ @implementation CGGI_GlyphCanvas
CGSize advance = advances[i];
CGRect bbox = bboxes[i];
- GlyphInfo *glyphInfo = CGGI_CreateNewGlyphInfoFrom(advance, bbox, strike, mainFontDescriptor);
+ GlyphInfo *glyphInfo = CGGI_CreateNewGlyphInfoFrom(advance, bbox, strike,
+ mode->mainFontDescriptor,
+ mode->subpixelResolution);
+
+ int w = glyphInfo->width * glyphInfo->subpixelResolutionX;
+ int h = glyphInfo->height * glyphInfo->subpixelResolutionY;
- if (maxWidth < glyphInfo->width) maxWidth = glyphInfo->width;
- if (maxHeight < glyphInfo->height) maxHeight = glyphInfo->height;
+ if (maxWidth < w) maxWidth = w;
+ if (maxHeight < h) maxHeight = h;
glyphInfos[i] = ptr_to_jlong(glyphInfo);
}
@@ -972,19 +1068,20 @@ @implementation CGGI_GlyphCanvas
void
CGGlyphImages_GetGlyphMetrics(const CTFontRef font,
const CGAffineTransform *tx,
+ CGFloat fontSize,
const JRSFontRenderingStyle style,
const CGGlyph glyphs[],
size_t count,
CGRect bboxes[],
- CGSize advances[]) {
- if (IsEmojiFont(font)) {
+ CGSize advances[],
+ const bool isCatalinaOrAbove) {
+ if (isCatalinaOrAbove || IsEmojiFont(font)) {
// Glyph metrics for emoji font are not strictly proportional to font size,
// so we need to construct real-sized font object to calculate them.
// The logic here must match the logic in CGGI_CreateImageForGlyph,
// which performs glyph drawing.
- CGFloat fontSize = sqrt(fabs(tx->a * tx->d - tx->b * tx->c));
- CTFontRef sizedFont = CTFontCreateCopyWithSymbolicTraits(font, fontSize, NULL, 0, 0);
+ CTFontRef sizedFont = CopyFontWithSize(font, fontSize);
if (bboxes) {
// JRSFontGetBoundingBoxesForGlyphsAndStyle works incorrectly for AppleColorEmoji font:
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/font/CoreTextSupport.h b/src/java.desktop/macosx/native/libawt_lwawt/font/CoreTextSupport.h
index cbd3dc5923b2..7987228bcfb4 100644
--- a/src/java.desktop/macosx/native/libawt_lwawt/font/CoreTextSupport.h
+++ b/src/java.desktop/macosx/native/libawt_lwawt/font/CoreTextSupport.h
@@ -35,6 +35,10 @@
#define HI_SURROGATE_END 0xDBFF
#define LO_SURROGATE_START 0xDC00
#define LO_SURROGATE_END 0xDFFF
+#define VS_START 0xFE00
+#define VS_END 0xFE0F
+#define VSS_START 0xE0100
+#define VSS_END 0xE01FF
/*
* Transform Unicode characters into glyphs.
@@ -54,9 +58,17 @@ CTFontRef CTS_CopyCTFallbackFontAndGlyphForJavaGlyphCode(const AWTFont *font, co
// Returns the substituted font, and places the appropriate glyph into "glyphRef"
CTFontRef CTS_CopyCTFallbackFontAndGlyphForUnicode(const AWTFont *font, const UTF16Char *charRef, CGGlyph *glyphRef, int count);
+// Transform a single Unicode character code into glyph code.
+// Names of the relevant font are also returned, if the substitution is used.
+// Non-null components of fontNames array should always be released by the calling code, regardless of the returned value.
+CGGlyph CTS_CopyGlyphAndFontNamesForCodePoint(const AWTFont *font, const UnicodeScalarValue codePoint, const UnicodeScalarValue variationSelector, CFStringRef fontNames[]);
+
// Breakup a 32 bit unicode value into the component surrogate pairs
void CTS_BreakupUnicodeIntoSurrogatePairs(int uniChar, UTF16Char charRef[]);
+// Get number of UTF16 values from 32 bit unicode value (2 for surrogate pair and 1 otherwise)
+int CTS_GetUnicodeSize(const UnicodeScalarValue unicode);
+
// Basic struct that holds everything CoreText is interested in
typedef struct CTS_ProviderStruct {
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/font/CoreTextSupport.m b/src/java.desktop/macosx/native/libawt_lwawt/font/CoreTextSupport.m
index 5663b3084ac7..9d5e4879aa76 100644
--- a/src/java.desktop/macosx/native/libawt_lwawt/font/CoreTextSupport.m
+++ b/src/java.desktop/macosx/native/libawt_lwawt/font/CoreTextSupport.m
@@ -88,59 +88,153 @@
CFRelease(ctStateDict); // GC
}
-/*
- * Transform Unicode characters into glyphs.
- *
- * Fills the "glyphsAsInts" array with the glyph codes for the current font,
- * or the negative unicode value if we know the character can be hot-substituted.
- *
- * This is the heart of "Universal Font Substitution" in Java.
- */
-void CTS_GetGlyphsAsIntsForCharacters
-(const AWTFont *font, const UniChar unicodes[], CGGlyph glyphs[], jint glyphsAsInts[], const size_t count)
+int NextUnicode(const UniChar unicodes[], UnicodeScalarValue *codePoint, const size_t index, const size_t limit) {
+ if (index >= limit) {
+ return 0;
+ }
+ UniChar unicode = unicodes[index];
+ UniChar nextUnicode = (index+1) < limit ? unicodes[index+1] : 0;
+ bool surrogatePair = unicode >= HI_SURROGATE_START && unicode <= HI_SURROGATE_END
+ && nextUnicode >= LO_SURROGATE_START && nextUnicode <= LO_SURROGATE_END;
+ *codePoint = surrogatePair ? (((int)(unicode - HI_SURROGATE_START)) << 10)
+ + nextUnicode - LO_SURROGATE_START + 0x10000 : unicode;
+ return surrogatePair ? 2 : 1;
+}
+
+void GetFontsAndGlyphsForCharacters(CTFontRef font, CTFontRef fallbackBase,
+ const UniChar unicodes[], CGGlyph glyphs[], jint glyphsAsInts[],
+ CTFontRef actualFonts[], const size_t count)
{
- CTFontGetGlyphsForCharacters((CTFontRef)font->fFont, unicodes, glyphs, count);
+ CTFontGetGlyphsForCharacters(font, unicodes, glyphs, count);
+ if (!fallbackBase) fallbackBase = font;
+ size_t i, size;
+ for (i = 0; i < count; i += size) {
+ UnicodeScalarValue codePoint, variationCodePoint;
+ int codePointSize = size = NextUnicode(unicodes, &codePoint, i, count);
+ if (size == 0) {
+ break;
+ }
- size_t i;
- for (i = 0; i < count; i++) {
- UniChar unicode = unicodes[i];
- UniChar nextUnicode = (i+1) < count ? unicodes[i+1] : 0;
- bool surrogatePair = unicode >= HI_SURROGATE_START && unicode <= HI_SURROGATE_END
- && nextUnicode >= LO_SURROGATE_START && nextUnicode <= LO_SURROGATE_END;
+ int variationSize = NextUnicode(unicodes, &variationCodePoint, i + size , count);
+ bool hasVariationSelector = variationSize > 0 &&
+ ((variationCodePoint >= VSS_START && variationCodePoint <= VSS_END) ||
+ (variationCodePoint >= VS_START && variationCodePoint <= VS_END));
+ if (hasVariationSelector) {
+ size += variationSize;
+ }
CGGlyph glyph = glyphs[i];
- if (glyph > 0) {
+ if (glyph > 0 && (!hasVariationSelector || glyphs[i + codePointSize] > 0)) {
glyphsAsInts[i] = glyph;
- if (surrogatePair) i++;
+ if (actualFonts) actualFonts[i] = nil;
continue;
}
- const CTFontRef fallback = JRSFontCreateFallbackFontForCharacters((CTFontRef)font->fFont, &unicodes[i],
- surrogatePair ? 2 : 1);
+ CTFontRef fallback = JRSFontCreateFallbackFontForCharacters(fallbackBase, &unicodes[i], size);
if (fallback) {
- CTFontGetGlyphsForCharacters(fallback, &unicodes[i], &glyphs[i], surrogatePair ? 2 : 1);
+ CTFontGetGlyphsForCharacters(fallback, &unicodes[i], &glyphs[i], size);
glyph = glyphs[i];
- CFRelease(fallback);
+ } else if (hasVariationSelector) { // Try without variation selector
+ fallback = JRSFontCreateFallbackFontForCharacters(fallbackBase, &unicodes[i], codePointSize);
+ if (fallback) {
+ CTFontGetGlyphsForCharacters(fallback, &unicodes[i], &glyphs[i], codePointSize);
+ glyph = glyphs[i];
+ }
+ }
+ if (actualFonts && glyph > 0) {
+ actualFonts[i] = fallback;
+ } else {
+ if (actualFonts) actualFonts[i] = nil;
+ if (fallback) CFRelease(fallback);
}
if (glyph > 0) {
- int codePoint = surrogatePair ? (((int)(unicode - HI_SURROGATE_START)) << 10)
- + nextUnicode - LO_SURROGATE_START + 0x10000 : unicode;
glyphsAsInts[i] = -codePoint; // set the glyph code to the negative unicode value
} else {
glyphsAsInts[i] = 0; // CoreText couldn't find a glyph for this character either
}
- if (surrogatePair) i++;
}
}
+/*
+ * Transform Unicode characters into glyphs.
+ *
+ * Fills the "glyphsAsInts" array with the glyph codes for the current font,
+ * or the negative unicode value if we know the character can be hot-substituted.
+ *
+ * This is the heart of "Universal Font Substitution" in Java.
+ */
+void CTS_GetGlyphsAsIntsForCharacters
+(const AWTFont *font, const UniChar unicodes[], CGGlyph glyphs[], jint glyphsAsInts[], const size_t count)
+{
+ GetFontsAndGlyphsForCharacters((CTFontRef)font->fFont, (CTFontRef)font->fFallbackBase,
+ unicodes, glyphs, glyphsAsInts, NULL, count);
+}
+
+/*
+ * Returns glyph code for a given Unicode character.
+ * Names of the corresponding substituted font are also returned if substitution is performed.
+ */
+CGGlyph CTS_CopyGlyphAndFontNamesForCodePoint
+(const AWTFont *font, const UnicodeScalarValue codePoint, const UnicodeScalarValue variationSelector, CFStringRef fontNames[])
+{
+ CTFontRef fontRef = (CTFontRef)font->fFont;
+ CTFontRef fallbackBase = (CTFontRef)font->fFallbackBase;
+ int codePointSize = CTS_GetUnicodeSize(codePoint);
+ int count = codePointSize + (variationSelector == 0 ? 0 : CTS_GetUnicodeSize(variationSelector));
+ UTF16Char unicodes[count];
+ if (codePoint < 0x10000) {
+ unicodes[0] = (UTF16Char)codePoint;
+ } else {
+ CTS_BreakupUnicodeIntoSurrogatePairs(codePoint, unicodes);
+ }
+ if (variationSelector != 0) {
+ UTF16Char* codes = &unicodes[codePointSize];
+ if (variationSelector < 0x10000) {
+ codes[0] = (UTF16Char)variationSelector;
+ } else {
+ CTS_BreakupUnicodeIntoSurrogatePairs(variationSelector, codes);
+ }
+ }
+ CGGlyph glyphs[count];
+ jint glyphsAsInts[count];
+ CTFontRef actualFonts[count];
+ GetFontsAndGlyphsForCharacters(fontRef, fallbackBase, unicodes, glyphs, glyphsAsInts, actualFonts, count);
+ CGGlyph glyph = glyphs[0];
+ bool substitutionHappened = glyphsAsInts[0] < 0;
+ if (glyph > 0 && substitutionHappened) {
+ CTFontRef actualFont = actualFonts[0];
+ CFStringRef fontName = nil, familyName = nil;
+ if (actualFont) {
+ fontName = CTFontCopyPostScriptName(actualFont);
+ familyName = CTFontCopyFamilyName(actualFont);
+ CFRelease(actualFont);
+ }
+ fontNames[0] = fontName;
+ fontNames[1] = familyName;
+ if (!fontName || !familyName) glyph = 0;
+ }
+ return glyph;
+}
+
/*
* Translates a Unicode into a CGGlyph/CTFontRef pair
* Returns the substituted font, and places the appropriate glyph into "glyphRef"
*/
CTFontRef CTS_CopyCTFallbackFontAndGlyphForUnicode
(const AWTFont *font, const UTF16Char *charRef, CGGlyph *glyphRef, int count) {
- CTFontRef fallback = JRSFontCreateFallbackFontForCharacters((CTFontRef)font->fFont, charRef, count);
+ CTFontRef primary = (CTFontRef)font->fFont;
+ CTFontRef fallbackBase = (CTFontRef)font->fFallbackBase;
+ if (fallbackBase) {
+ CTFontGetGlyphsForCharacters(primary, charRef, glyphRef, count);
+ if (glyphRef[0] > 0) {
+ CFRetain(primary);
+ return primary;
+ }
+ } else {
+ fallbackBase = primary;
+ }
+ CTFontRef fallback = JRSFontCreateFallbackFontForCharacters(fallbackBase, charRef, count);
if (fallback == NULL)
{
// use the original font if we somehow got duped into trying to fallback something we can't
@@ -190,3 +284,7 @@ void CTS_BreakupUnicodeIntoSurrogatePairs(int uniChar, UTF16Char charRef[]) {
charRef[0] = high_surrogate;
charRef[1] = low_surrogate;
}
+
+int CTS_GetUnicodeSize(const UnicodeScalarValue unicode) {
+ return unicode >= 0x10000 ? 2 : 1;
+}
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/EncoderManager.h b/src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/EncoderManager.h
index bec085f93db9..cc488651448e 100644
--- a/src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/EncoderManager.h
+++ b/src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/EncoderManager.h
@@ -86,7 +86,8 @@
isAA:(jboolean)isAA;
- (id _Nonnull)getTextEncoder:(const BMTLSDOps * _Nonnull)dstOps
- isSrcOpaque:(bool)isSrcOpaque;
+ isSrcOpaque:(bool)isSrcOpaque
+ gammaCorrection:(bool)gmc;
// Base method to obtain any MTLRenderCommandEncoder
- (id _Nonnull) getEncoder:(id _Nonnull)dest
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/EncoderManager.m b/src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/EncoderManager.m
index 95374d2c93e5..d21b36f19a69 100644
--- a/src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/EncoderManager.m
+++ b/src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/EncoderManager.m
@@ -42,7 +42,7 @@ - (void)dealloc;
- (void)reset:(id)destination
isAA:(jboolean)isAA
- isText:(jboolean)isText
+ isGMCText:(jboolean)isGMCText
isLCD:(jboolean)isLCD;
- (void)updateEncoder:(id)encoder
@@ -50,7 +50,6 @@ - (void)updateEncoder:(id)encoder
renderOptions:(const RenderOptions *)renderOptions
forceUpdate:(jboolean)forceUpdate;
@property (assign) jboolean aa;
-@property (assign) jboolean text;
@property (assign) jboolean lcd;
@property (assign) jboolean aaShader;
@property (retain) MTLPaint* paint;
@@ -64,7 +63,7 @@ @implementation EncoderStates {
id _destination;
jboolean _isAA;
- jboolean _isText;
+ jboolean _isGMCText;
jboolean _isLCD;
jboolean _isAAShader;
@@ -90,7 +89,6 @@ @implementation EncoderStates {
MTLTransform * _transform;
}
@synthesize aa = _isAA;
-@synthesize text = _isText;
@synthesize lcd = _isLCD;
@synthesize aaShader = _isAAShader;
@synthesize paint = _paint;
@@ -121,11 +119,11 @@ - (void)setContext:(MTLContext * _Nonnull)mtlc {
- (void)reset:(id)destination
isAA:(jboolean)isAA
- isText:(jboolean)isText
+ isGMCText:(jboolean)isGMCText
isLCD:(jboolean)isLCD {
_destination = destination;
_isAA = isAA;
- _isText = isText;
+ _isGMCText = isGMCText;
_isLCD = isLCD;
// NOTE: probably it's better to invalidate/reset all cached states now
}
@@ -175,7 +173,7 @@ - (void)updatePipelineState:(id)encoder
&& (_isTexture == renderOptions->isTexture && (!renderOptions->isTexture || _interpolationMode == renderOptions->interpolation)) // interpolation is used only in texture mode
&& _isAA == renderOptions->isAA
&& _isAAShader == renderOptions->isAAShader
- && _isText == renderOptions->isText
+ && _isGMCText == renderOptions->isGMCText
&& _isLCD == renderOptions->isLCD
&& _srcFlags.isOpaque == renderOptions->srcFlags.isOpaque && _srcFlags.isPremultiplied == renderOptions->srcFlags.isPremultiplied)
return;
@@ -186,7 +184,7 @@ - (void)updatePipelineState:(id)encoder
_interpolationMode = renderOptions->interpolation;
_isAA = renderOptions->isAA;
_isAAShader = renderOptions->isAAShader;
- _isText = renderOptions->isText;
+ _isGMCText = renderOptions->isGMCText;
_isLCD = renderOptions->isLCD;
_srcFlags = renderOptions->srcFlags;
@@ -345,10 +343,11 @@ - (void)setContext:(MTLContex * _Nonnull)mtlc {
}
- (id _Nonnull) getTextEncoder:(const BMTLSDOps * _Nonnull)dstOps
- isSrcOpaque:(bool)isSrcOpaque
+ isSrcOpaque:(bool)isSrcOpaque
+ gammaCorrection:(bool)gmc
{
RenderOptions roptions = {JNI_TRUE, JNI_FALSE, INTERPOLATION_NEAREST_NEIGHBOR, { isSrcOpaque, JNI_TRUE },
- JNI_TRUE, JNI_FALSE, JNI_FALSE};
+ gmc, JNI_FALSE, JNI_FALSE};
return [self getEncoder:dstOps->pTexture renderOptions:&roptions];
}
@@ -432,7 +431,7 @@ - (void)setContext:(MTLContex * _Nonnull)mtlc {
[_encoderStates reset:dest
isAA:renderOptions->isAA
- isText:renderOptions->isText
+ isGMCText:renderOptions->isGMCText
isLCD:renderOptions->isLCD];
}
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/MTLBlitLoops.m b/src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/MTLBlitLoops.m
index 6c91f6a6a13d..0bfff8ad7f48 100644
--- a/src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/MTLBlitLoops.m
+++ b/src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/MTLBlitLoops.m
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -235,14 +235,8 @@ static void fillSwizzleUniforms(struct SwizzleUniforms *uniforms, const MTLRaste
toTexture:dest
destinationSlice:0 destinationLevel:0 destinationOrigin:MTLOriginMake(dx1, dy1, 0)];
[blitEncoder endEncoding];
- [mtlc.encoderManager endEncoder];
-
- MTLCommandBufferWrapper * cbwrapper = [mtlc pullCommandBufferWrapper];
- id commandbuf = [cbwrapper getCommandBuffer];
- [commandbuf addCompletedHandler:^(id commandbuf) {
- [cbwrapper release];
- }];
- [commandbuf commit];
+
+ [mtlc commitCommandBuffer:NO display:NO];
}
}
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/MTLClip.m b/src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/MTLClip.m
index 3919d97c53ad..c15a8ac11903 100644
--- a/src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/MTLClip.m
+++ b/src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/MTLClip.m
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -174,15 +174,7 @@ - (void)endShapeClip:(BMTLSDOps *)dstOps context:(MTLContext *)mtlc {
// Complete the rendering to the stencil buffer ------------
[mtlc.encoderManager endEncoder];
-
- MTLCommandBufferWrapper* cbWrapper = [mtlc pullCommandBufferWrapper];
-
- id commandBuffer = [cbWrapper getCommandBuffer];
- [commandBuffer addCompletedHandler:^(id c) {
- [cbWrapper release];
- }];
-
- [commandBuffer commit];
+ [mtlc commitCommandBuffer:NO display:NO];
_stencilMaskGenerationInProgress = NO;
_stencilMaskGenerationStarted = NO;
_dstOps = dstOps;
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/MTLContext.h b/src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/MTLContext.h
index d684f983eeb3..3ab5b418ef06 100644
--- a/src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/MTLContext.h
+++ b/src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/MTLContext.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -39,8 +39,10 @@
#include "MTLClip.h"
#include "EncoderManager.h"
#include "MTLSamplerManager.h"
+#import "MTLGlyphCache.h"
@class MTLStencilManager;
+@class MTLLayer;
// Constant from
// https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf
@@ -67,9 +69,20 @@
@property (readonly) MTLTransform * transform;
@property (readonly) MTLClip * clip;
+/**
+ * There are two separate glyph caches: for AA and for LCD.
+ * Once one of them is initialized as either GRAY or LCD, it
+ * stays in that mode for the duration of the MTLContext (it is not safe
+ * to use this one glyph cache for all screens in a multi-monitor
+ * environment)
+ */
+@property (readonly) MTLGlyphCache *glyphCacheLCD;
+@property (readonly) MTLGlyphCache *glyphCacheAA;
+
@property jint textureFunction;
@property jboolean vertexCacheEnabled;
@property jboolean aaEnabled;
+@property jboolean useMaskColor;
@property (readonly, strong) id device;
@property (strong) id commandQueue;
@@ -93,7 +106,7 @@
*/
+ (MTLContext*) setSurfacesEnv:(JNIEnv*)env src:(jlong)pSrc dst:(jlong)pDst;
-- (id)initWithDevice:(id)d shadersLib:(NSString*)shadersLib;
+- (id)initWithDevice:(jint)displayID shadersLib:(NSString*)shadersLib;
- (void)dealloc;
/**
@@ -229,6 +242,9 @@
- (id)createCommandBuffer;
- (id)createBlitCommandBuffer;
+- (void)commitCommandBuffer:(BOOL)waitUntilCompleted display:(BOOL)updateDisplay;
+- (void)startRedraw:(MTLLayer*)layer;
+- (void)stopRedraw:(MTLLayer*)layer;
@end
/**
diff --git a/src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/MTLContext.m b/src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/MTLContext.m
index b88d60957f39..005512f79c68 100644
--- a/src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/MTLContext.m
+++ b/src/java.desktop/macosx/native/libawt_lwawt/java2d/metal/MTLContext.m
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -24,6 +24,7 @@
*/
#include