Compare commits

..

26 Commits

Author SHA1 Message Date
Vitaly Provodin
8b739ff937 update exclude list on results of 21.0.4_b620.4 test runs 2024-10-07 08:12:19 +04:00
Nikita Tsarev
1e1115a28e JBR-7524: Workaround for showing window tiling actions when hovering over the maximize button on macOS 2024-10-04 11:45:31 +02:00
Maxim Kartashev
3ede03b58a JBR-7504 Use accurate event serial number with the clipboard 2024-10-04 12:59:19 +04:00
Maxim Kartashev
231126c134 JBR-7504 WLToolkit - Middle click paste doesn't work properly when pasting to other applications 2024-10-04 12:58:38 +04:00
Nikita Tsarev
cb2eb9bb7f JBR-7672: Only abort key repeat when the key that is being repeated is released [WLToolkit] 2024-09-30 10:44:59 +02:00
Nikita Tsarev
022221b0bb JBR-7662: Fix key repeat manager sometimes not cancelling properly [WLToolkit] 2024-09-30 10:41:58 +02:00
Vitaly Provodin
746032ad3e JBR-7511 migrate build platforms to OL8 for the release 2024.3 2024-09-28 05:28:12 +07:00
Vitaly Provodin
6826f09811 JBR-7511 migrate build platforms to OL8
- remove Vulcan part that causing builds to fail
- modify scripts for building images from Oracle Linux 8
- update jb/build/VerifyDependencies.java to check libraries have no dependency on symbols from glibc version higher than 2.28
- rename Ubuntu2004 docker files
- upgrade wayland up to wayland-devel-1.21.0-1
2024-09-28 02:26:04 +04:00
bourgesl
5157205ed8 JBR-7616: fixed type (str) 2024-09-27 16:17:22 +02:00
Nikita Tsarev
1ccb174ecb JBR-7675: Respect disabling key repeat [WLToolkit] 2024-09-27 15:02:50 +02:00
bourgesl
226d6f982c JBR-7616: added ThreadUtilities.lwc_plog(env, formatMsg, ...) to use LWCToolkit's PlatformLogger instead of NSlog (only as fallback now) used by MTLRenderQueue, updated MTLUtils to share mtlDstTypeToStr(op)
(cherry picked from commit 6e00460b0009817593d04773516559d1ec5a9292)
2024-09-24 12:35:41 +02:00
Maxim Kartashev
5099233a6d JBR-7651 runtime/cds/appcds/complexURI/ComplexURITest.java throws java.nio.file.AccessDeniedException 2024-09-24 12:43:52 +04:00
Vladimir Dvorak
8d37639b50 JBR-7649 fix DCEVM crashes on assert() in VM_Exit 2024-09-23 19:36:39 +02:00
Vladimir Dvorak
9cfcff32c4 JBR-7523 fix ClassUnloading support in DCEVM 2024-09-23 19:36:16 +02:00
Vladimir Dvorak
ff87567544 JBR-7635 Skip rolled-back classes in search for affected classes 2024-09-23 19:35:56 +02:00
Vitaly Provodin
f912b0e071 update exclude list on results of 21.0.4_b607.1 test runs 2024-09-23 12:42:53 +04:00
Alexey Ushakov
7c057eb7f6 JBR-7588 Metal: Reuse MTLContext for all GCs of the same GPU
Implemented reference counting for shared MTLContext objects. Supported multiple display links per MTLContext. Also, works for macOS version < 10.13
2024-09-20 21:17:08 +02:00
bourgesl
d4a299a2e7 JBR-7616: improved MTLRenderQueue exception handling
(cherry picked from commit 9df0cce15d1b1773634907881d0c515a100cf412)
2024-09-16 21:57:05 +02:00
Maxim Kartashev
b5f427d580 JBR-7576 Cannot make CDS dump due to whitespaces in paths 2024-09-16 14:16:51 +04:00
Vitaly Provodin
e752f9a857 update exclude list on results of 21.0.4_b591.1 test runs 2024-09-13 04:11:28 +04:00
Maxim Kartashev
e37c2450a8 JBR-7600 Provide ability to add messages to fatal error log
Use JNU_LOG_EVENT(env, msg, ...) to save a message in the internal
JVM ring buffer that gets printed out to the Events section
of the fatal error log if JVM crashed.
2024-09-12 14:27:30 +04:00
Nikita Gubarkov
78ef30a322 JBR-7614 JBR API: fix generated proxy name clash. 2024-09-11 18:19:19 +02:00
Nikita Provotorov
6afbc34d4b JBR-5673: Wayland: support touch scrolling.
- Adding information to WLPointerEvent about wl_pointer::axis* events along the X axis;
- Introducing 'WLComponentPeer#convertPointerEventToMWEParameters' - a routine for converting WLPointerEvent parameters to parameters required for MouseWheelEvent s;
- Handling both X and Y axes within the WLPointerEvent dispatching routine.
2024-09-11 15:52:45 +02:00
Nikita Provotorov
874d5698bc JBR-7459: Wayland: touchpad scrolling is too sensitive.
- Remaking the mapping of wl_pointer::axis events values to MouseWheelEvent rotations to eliminate the touchpad scrolling behavior "the more slowly the fingers move, the more pixels are scrolled";
- Accumulating the fraction parts of wl_pointer::axis events values to improve the accuracy of touchpad scrolling;
- Distinguishing between wheel scrolling and touchpad scrolling to fine-tune MouseWheelEvent parameters for each of these cases.
2024-09-11 15:52:44 +02:00
Sergei Tachenov
dd1afa581e JBR-7586 Fix title click ungrab when an active user component is clicked
Swing requires that clicking frame decorations should cause the window
to be ungrabbed. However, if a custom title is used, and that title contains
user-provided components, then clicking such components should not
cause the window to be ungrabbed, otherwise a menu located in a custom
title behaves incorrectly.

Fix by using the same logic as for the native actions, such as moving the window.
If the native actions are allowed, then ungrabbing is allowed as well.
Otherwise, do not ungrab, let the component behave like it's located in the client area.

The fix is supplemented with a new regression test "test/jdk/jb/javax/swing/CustomTitleBar/JMenuClickToCloseTest.java".
2024-09-09 16:00:46 +02:00
Nikita Tsarev
cd5cad7154 JBR-7594 Check for LWCToolkit in JBR TextInput API 2024-09-06 13:39:02 +02:00
82 changed files with 2211 additions and 612 deletions

View File

@@ -1,46 +0,0 @@
# 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"

View File

@@ -0,0 +1,44 @@
# 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.
FROM arm64v8/oraclelinux:8
# Install the necessary build tools
RUN yum -y update; \
yum -y install gcc-toolset-10-10.1-0.el8.aarch64; \
yum -y install \
alsa-lib-devel-1.1.9-4.el8.aarch64 \
autoconf-2.69-29.el8_10.1.noarch \
automake-1.16.1-6.el8.noarch \
bzip2-libs-1.0.6-26.el8.aarch64 \
cups-devel-2.2.6-60.el8_10.aarch64 \
file-5.33-26.el8.aarch64 \
fontconfig-devel-2.13.1-4.el8.aarch64 \
freetype-devel-2.9.1-9.el8.aarch64 \
gcc-c++-8.5.0-22.0.1.el8_10.aarch64 \
git-2.43.5-1.el8_10.aarch64 \
git-core-2.43.5-1.el8_10.aarch64 \
libtool-2.4.6-25.el8.aarch64 \
libXi-devel-1.7.10-1.el8.aarch64 \
libXrandr-devel-1.5.2-1.el8.aarch64 \
libXrender-devel-0.9.10-7.el8.aarch64 \
libXt-devel-1.1.5-12.el8.aarch64 \
libXtst-devel-1.2.3-7.el8.aarch64 \
make-devel-4.2.1-11.el8.aarch64 \
rsync-3.1.3-19.el8_7.1.aarch64 \
unzip-6.0-46.el8.aarch64 \
wayland-devel-1.21.0-1.el8.aarch64; \
yum -y clean all
RUN git config --global user.email "teamcity@jetbrains.com" && \
git config --global user.name "builduser"
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"

View File

@@ -0,0 +1,45 @@
# NOTE: This Dockerfile is meant to be used from the mkdocker_x86_64.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 amd64/oraclelinux:8
# Install the necessary build tools
RUN yum -y update; \
yum -y install gcc-toolset-10-10.1-0.el8.x86_64; \
yum -y install \
alsa-lib-devel-1.1.9-4.el8.x86_64 \
autoconf-2.69-29.el8_10.1.noarch \
automake-1.16.1-6.el8.noarch \
bzip2-libs-1.0.6-26.el8.x86_64 \
cups-devel-2.2.6-60.el8_10.x86_64 \
file-5.33-26.el8.x86_64 \
fontconfig-devel-2.13.1-4.el8.x86_64 \
freetype-devel-2.9.1-9.el8.x86_64 \
gcc-c++-8.5.0-22.0.1.el8_10.x86_64 \
git-2.43.5-1.el8_10.x86_64 \
git-core-2.43.5-1.el8_10.x86_64 \
libtool-2.4.6-25.el8.x86_64 \
libXi-devel-1.7.10-1.el8.x86_64 \
libXrandr-devel-1.5.2-1.el8.x86_64 \
libXrender-devel-0.9.10-7.el8.x86_64 \
libXt-devel-1.1.5-12.el8.x86_64 \
libXtst-devel-1.2.3-7.el8.x86_64 \
make-devel-4.2.1-11.el8.x86_64 \
rsync-3.1.3-19.el8_7.1.x86_64 \
unzip-6.0-46.el8.x86_64 \
wayland-devel-1.21.0-1.el8.x86_64 \
python36-3.6.8-39.module+el8.10.0+90274+07ba55de; \
yum -y clean all
RUN git config --global user.email "teamcity@jetbrains.com" && \
git config --global user.name "builduser" && \
git config --global --add safe.directory '*'
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"

View File

@@ -1,73 +0,0 @@
# 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 \
python3-3.6.8-17.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"
# Build GLSLC
RUN curl -OL https://github.com/Kitware/CMake/releases/download/v3.27.5/cmake-3.27.5-linux-x86_64.tar.gz \
&& echo 138c68addae825b16ed78d792dafef5e0960194833f48bd77e7e0429c6bc081c *cmake-3.27.5-linux-x86_64.tar.gz | sha256sum -c - \
&& tar -xzf cmake-3.27.5-linux-x86_64.tar.gz \
&& rm cmake-3.27.5-linux-x86_64.tar.gz \
&& git clone https://github.com/google/shaderc --branch v2023.6 \
&& cd shaderc \
&& ./utils/git-sync-deps \
&& mkdir build \
&& cd build \
&& /cmake-3.27.5-linux-x86_64/bin/cmake \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=/usr/local \
-DSHADERC_SKIP_TESTS=ON \
-DSHADERC_SKIP_EXAMPLES=ON \
-DSHADERC_SKIP_COPYRIGHT_CHECK=ON \
.. \
&& make install
ENV PATH="/cmake-3.27.5-linux-x86_64/bin::${PATH}"
# Checkout Vulkan headers
RUN mkdir /vulkan \
&& cd /vulkan \
&& git init \
&& git remote add -f origin https://github.com/KhronosGroup/Vulkan-Headers.git \
&& git fetch origin \
&& git checkout v1.3.265 -- include \
&& rm -r .git \
&& mkdir /vulkan_hpp \
&& cd /vulkan_hpp \
&& git init \
&& git remote add -f origin https://github.com/KhronosGroup/Vulkan-Hpp.git \
&& git fetch origin \
&& git checkout v1.3.265 -- vulkan \
&& rm -r .git

View File

@@ -4,10 +4,9 @@ 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_REMOTE_FILE=https://cdn.azul.com/zulu/bin/zulu21.36.17-ca-jdk21.0.4-linux_aarch64.tar.gz
BOOT_JDK_SHA=da3c2d7db33670bcf66532441aeb7f33dcf0d227c8dafe7ce35cee67f6829c4c
BOOT_JDK_LOCAL_FILE=boot_jdk.tar.gz
if [ ! -f $BOOT_JDK_LOCAL_FILE ]; then
@@ -22,7 +21,7 @@ sha256sum -c - <<EOF
$BOOT_JDK_SHA *$BOOT_JDK_LOCAL_FILE
EOF
docker build -t jbrdevenv_arm64v8 -f Dockerfile.aarch64 .
docker build -t jetbrains/runtime:jbr21env_oraclelinux8_aarch64 -f Dockerfile.oraclelinux_aarch64 .
# NB: the resulting container can (and should) be used without the network
# connection (--network none) during build in order to reduce the chance

View File

@@ -0,0 +1,28 @@
#!/bin/bash
set -euo pipefail
set -x
# This script creates a Docker image suitable for building x86-64 variant
BOOT_JDK_REMOTE_FILE=zulu21.36.17-ca-jdk21.0.4-linux_x64.tar.gz
BOOT_JDK_SHA=318d0c2ed3c876fb7ea2c952945cdcf7decfb5264ca51aece159e635ac53d544
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 - <<EOF
$BOOT_JDK_SHA *$BOOT_JDK_LOCAL_FILE
EOF
docker build -t jetbrains/runtime:jbr21env_oraclelinux8_amd64 -f Dockerfile.oraclelinux_x86_64 .
# NB: the resulting container can (and should) be used without the network
# connection (--network none) during build in order to reduce the chance
# of build contamination.

View File

@@ -62,6 +62,7 @@ $(eval $(call SetupJdkLibrary, BUILD_LIBJAVA, \
ProcessImpl_md.c_CFLAGS := $(VERSION_CFLAGS), \
WARNINGS_AS_ERRORS_xlc := false, \
DISABLED_WARNINGS_gcc_ProcessImpl_md.c := unused-result, \
DISABLED_WARNINGS_clang_jni_util.c := format-nonliteral, \
LDFLAGS := $(LDFLAGS_JDKLIB) \
$(call SET_SHARED_LIBRARY_ORIGIN), \
LDFLAGS_macosx := -L$(SUPPORT_OUTPUTDIR)/native/$(MODULE)/, \

View File

@@ -81,8 +81,10 @@ ifeq ($(call isTargetOs, windows), true)
BUILD_JDK_JTREG_LIBRARIES_LIBS_libNewDirectByteBuffer := $(WIN_LIB_JAVA)
BUILD_JDK_JTREG_LIBRARIES_LIBS_libGetXSpace := $(WIN_LIB_JAVA)
BUILD_JDK_JTREG_LIBRARIES_LIBS_libFatalErrorTest := $(WIN_LIB_JAVA)
BUILD_JDK_JTREG_LIBRARIES_LIBS_libLogEventTest := $(WIN_LIB_JAVA)
else
BUILD_JDK_JTREG_LIBRARIES_LIBS_libFatalErrorTest := -ljava
BUILD_JDK_JTREG_LIBRARIES_LIBS_libLogEventTest := -ljava
BUILD_JDK_JTREG_LIBRARIES_LIBS_libstringPlatformChars := -ljava
BUILD_JDK_JTREG_LIBRARIES_LIBS_libDirectIO := -ljava
BUILD_JDK_JTREG_LIBRARIES_LIBS_libNewDirectByteBuffer := -ljava

View File

@@ -472,7 +472,9 @@ InstanceKlass* ClassListParser::load_class_from_source(Symbol* class_name, TRAPS
THROW_NULL(vmSymbols::java_lang_ClassNotFoundException());
}
InstanceKlass* k = UnregisteredClasses::load_class(class_name, _source, CHECK_NULL);
ResourceMark rm;
char * source_path = os::strdup_check_oom(ClassLoader::uri_to_path(_source));
InstanceKlass* k = UnregisteredClasses::load_class(class_name, source_path, CHECK_NULL);
if (k->local_interfaces()->length() != _interfaces->length()) {
print_specified_interfaces();
print_actual_interfaces(k);

View File

@@ -171,6 +171,8 @@ void ClassListWriter::write_to_stream(const InstanceKlass* k, outputStream* stre
}
}
// NB: the string following "source: " is not really a proper file name, but rather
// a truncated URI referring to a file. It must be decoded after reading.
#ifdef _WINDOWS
// "file:/C:/dir/foo.jar" -> "C:/dir/foo.jar"
stream->print(" source: %s", cfs->source() + 6);

View File

@@ -586,7 +586,7 @@ int FileMapInfo::get_module_shared_path_index(Symbol* location) {
// skip_uri_protocol was also called during dump time -- see ClassLoaderExt::process_module_table()
ResourceMark rm;
const char* file = ClassLoader::skip_uri_protocol(location->as_C_string());
const char* file = ClassLoader::uri_to_path(location->as_C_string());
for (int i = ClassLoaderExt::app_module_paths_start_index(); i < get_number_of_shared_paths(); i++) {
SharedClassPathEntry* ent = shared_path(i);
assert(ent->in_named_module(), "must be");

View File

@@ -78,6 +78,9 @@
#include "utilities/macros.hpp"
#include "utilities/utf8.hpp"
#include <stdlib.h>
#include <ctype.h>
// Entry point in java.dll for path canonicalization
typedef int (*canonicalize_fn_t)(const char *orig, char *out, int len);
@@ -1224,7 +1227,7 @@ InstanceKlass* ClassLoader::load_class(Symbol* name, bool search_append_only, TR
}
#if INCLUDE_CDS
char* ClassLoader::skip_uri_protocol(char* source) {
static const char* skip_uri_protocol(const char* source) {
if (strncmp(source, "file:", 5) == 0) {
// file: protocol path could start with file:/ or file:///
// locate the char after all the forward slashes
@@ -1243,6 +1246,52 @@ char* ClassLoader::skip_uri_protocol(char* source) {
return source;
}
static char decode_percent_encoded(const char *str, size_t& index) {
if (str[index] == '%'
&& isxdigit(str[index + 1])
&& isxdigit(str[index + 2])) {
char hex[3];
hex[0] = str[index + 1];
hex[1] = str[index + 2];
hex[2] = '\0';
index += 2;
return (char) strtol(hex, NULL, 16);
}
return str[index];
}
char* ClassLoader::uri_to_path(const char* uri) {
const size_t len = strlen(uri) + 1;
char* path = NEW_RESOURCE_ARRAY(char, len);
uri = skip_uri_protocol(uri);
if (strncmp(uri, "//", 2) == 0) {
// Skip the empty "authority" part
uri += 2;
}
#ifdef _WINDOWS
if (uri[0] == '/') {
// Absolute path name on Windows does not begin with a slash
uri += 1;
}
#endif
size_t path_index = 0;
for (size_t i = 0; i < strlen(uri); ++i) {
char decoded = decode_percent_encoded(uri, i);
#ifdef _WINDOWS
if (decoded == '/') {
decoded = '\\';
}
#endif
path[path_index++] = decoded;
}
path[path_index] = '\0';
return path;
}
// Record the shared classpath index and loader type for classes loaded
// by the builtin loaders at dump time.
void ClassLoader::record_result(JavaThread* current, InstanceKlass* ik,
@@ -1276,7 +1325,7 @@ void ClassLoader::record_result(JavaThread* current, InstanceKlass* ik,
// Save the path from the file: protocol or the module name from the jrt: protocol
// if no protocol prefix is found, path is the same as stream->source(). This path
// must be valid since the class has been successfully parsed.
char* path = skip_uri_protocol(src);
const char* path = ClassLoader::uri_to_path(src);
assert(path != nullptr, "sanity");
for (int i = 0; i < FileMapInfo::get_number_of_shared_paths(); i++) {
SharedClassPathEntry* ent = FileMapInfo::shared_path(i);

View File

@@ -364,7 +364,7 @@ class ClassLoader: AllStatic {
// entries during shared classpath setup time.
static int num_module_path_entries();
static void exit_with_path_failure(const char* error, const char* message);
static char* skip_uri_protocol(char* source);
static char* uri_to_path(const char* uri);
static void record_result(JavaThread* current, InstanceKlass* ik,
const ClassFileStream* stream, bool redefined);
#endif

View File

@@ -98,12 +98,10 @@ void ClassLoaderExt::process_module_table(JavaThread* current, ModuleEntryTable*
ModulePathsGatherer(JavaThread* current, GrowableArray<char*>* module_paths) :
_current(current), _module_paths(module_paths) {}
void do_module(ModuleEntry* m) {
char* path = m->location()->as_C_string();
if (strncmp(path, "file:", 5) == 0) {
path = ClassLoader::skip_uri_protocol(path);
char* path_copy = NEW_RESOURCE_ARRAY(char, strlen(path) + 1);
strcpy(path_copy, path);
_module_paths->append(path_copy);
char* uri = m->location()->as_C_string();
if (strncmp(uri, "file:", 5) == 0) {
char* path = ClassLoader::uri_to_path(uri);
_module_paths->append(path);
}
}
};

View File

@@ -4046,8 +4046,7 @@ void InstanceKlass::verify_on(outputStream* st) {
}
guarantee(sib->is_klass(), "should be klass");
// TODO: (DCEVM) explain
guarantee(sib->super() == super || AllowEnhancedClassRedefinition && super->newest_version() == vmClasses::Object_klass(), "siblings should have same superklass");
guarantee(sib->super() == super || AllowEnhancedClassRedefinition && sib->super()->newest_version() == super->newest_version(), "siblings should have same superklass");
}
// Verify local interfaces

View File

@@ -202,6 +202,7 @@ Klass::Klass(KlassKind kind) : _kind(kind),
_is_redefining(false),
_update_information(NULL),
_is_copying_backwards(false),
_is_rolled_back(false),
_shared_class_path_index(-1) {
CDS_ONLY(_shared_class_flags = 0;)
CDS_JAVA_HEAP_ONLY(_archived_mirror_index = -1;)

View File

@@ -178,6 +178,7 @@ class Klass : public Metadata {
bool _is_redefining;
int* _update_information;
bool _is_copying_backwards; // Does the class need to copy fields backwards? => possibly overwrite itself?
bool _is_rolled_back; // true if class was rolled back in redefinition
private:
// This is an index into FileMapHeader::_shared_path_table[], to
@@ -416,6 +417,8 @@ protected:
void set_update_information(int *info) { _update_information = info; }
bool is_copying_backwards() const { return _is_copying_backwards; }
void set_copying_backwards(bool b) { _is_copying_backwards = b; }
bool is_rolled_back() { return _is_rolled_back; }
void set_rolled_back(bool b) { _is_rolled_back = b;}
protected: // internal accessors
void set_subklass(Klass* s);

View File

@@ -554,6 +554,9 @@ JNI_ENTRY(jint, jni_ThrowNew(JNIEnv *env, jclass clazz, const char *message))
report_fatal(INTERNAL_ERROR, "<dummy>", 0, "%s", message);
ShouldNotReachHere();
return 0;
} else if (name->equals("java/lang/Exception$JB$$Event")) {
Events::log(THREAD, "%s", message);
return 0;
}
Handle class_loader (THREAD, k->class_loader());
Handle protection_domain (THREAD, k->protection_domain());

View File

@@ -106,6 +106,7 @@ u8 VM_EnhancedRedefineClasses::_id_counter = 0;
// @param class_load_kind always jvmti_class_load_kind_redefine
VM_EnhancedRedefineClasses::VM_EnhancedRedefineClasses(jint class_count, const jvmtiClassDefinition *class_defs, JvmtiClassLoadKind class_load_kind) :
VM_GC_Operation(Universe::heap()->total_collections(), GCCause::_heap_inspection, Universe::heap()->total_full_collections(), true) {
_new_classes = nullptr;
_affected_klasses = nullptr;
_class_count = class_count;
_class_defs = class_defs;
@@ -717,12 +718,6 @@ void VM_EnhancedRedefineClasses::doit() {
Universe::objectArrayKlassObj()->append_to_sibling_list();
}
if (_object_klass_redefined) {
// TODO: This is a hack; it keeps old mirror instances on the heap. A correct solution could be to hold the old mirror class in the new mirror class.
ClassUnloading = false;
ClassUnloadingWithConcurrentMark = false;
}
if (needs_instance_update) {
// Do a full garbage collection to update the instance sizes accordingly
log_trace(redefine, class, redefine, metadata)("Before redefinition full GC run");
@@ -788,6 +783,10 @@ void VM_EnhancedRedefineClasses::doit() {
Universe::set_inside_redefinition(false);
_timer_vm_op_doit.stop();
if (log_is_enabled(Trace, redefine, class, codecache)) {
CodeCache::print();
}
}
// Cleanup - runs in JVM thread
@@ -953,6 +952,8 @@ jvmtiError VM_EnhancedRedefineClasses::load_new_class_versions_single_step(TRAPS
log_info(redefine, class, load, exceptions)("link_class exception: '%s'", ex_name->as_C_string());
}
CLEAR_PENDING_EXCEPTION;
the_class->set_redefinition_flags(Klass::NoRedefinition);
if (ex_name == vmSymbols::java_lang_OutOfMemoryError()) {
return JVMTI_ERROR_OUT_OF_MEMORY;
} else if (ex_name == vmSymbols::java_lang_NoClassDefFoundError()) {
@@ -1025,9 +1026,11 @@ jvmtiError VM_EnhancedRedefineClasses::load_new_class_versions_single_step(TRAPS
// The hidden class loader data has been artificially been kept alive to
// this point. The mirror and any instances of this class have to keep
// it alive afterwards.
if (the_class->class_loader_data()->keep_alive_cnt() > 0) {
the_class->class_loader_data()->dec_keep_alive();
}
// Keep old classloader data alive otherwise it crashes with ClassUnloading=true
// if (the_class->class_loader_data()->keep_alive_cnt() > 0) {
// the_class->class_loader_data()->dec_keep_alive();
// }
}
} else {
@@ -1614,6 +1617,7 @@ void VM_EnhancedRedefineClasses::rollback() {
new_class->set_redefining(false);
new_class->old_version()->set_new_version(nullptr);
new_class->set_old_version(nullptr);
new_class->set_rolled_back(true);
}
_new_classes->clear();
}
@@ -2242,6 +2246,10 @@ class AffectedKlassClosure : public KlassClosure {
return;
}
if (klass->is_rolled_back()) {
return;
}
if (klass->check_redefinition_flag(Klass::MarkedAsAffected)) {
_affected_klasses->append(klass);
return;

View File

@@ -382,7 +382,7 @@ public:
bool operator()(WeakHandle* entry) {
oop mem_name = entry->peek();
if (mem_name == NULL) {
if (mem_name == nullptr) {
// Removed
return true;
}
@@ -426,7 +426,7 @@ void ResolvedMethodTable::adjust_method_entries_dcevm(bool * trace_name_printed)
ResolvedMethodTableLookup lookup(thread, method_hash(old_method), old_method);
_local_table->remove(thread, lookup);
InstanceKlass* newer_klass = InstanceKlass::cast(old_method->method_holder()->new_version());
InstanceKlass* newer_klass = InstanceKlass::cast(old_method->method_holder()->newest_version());
Method* newer_method = nullptr;
// Method* new_method;
@@ -436,7 +436,7 @@ void ResolvedMethodTable::adjust_method_entries_dcevm(bool * trace_name_printed)
newer_method = newer_klass->method_with_idnum(old_method->orig_method_idnum());
// TODO: JBR21 - check why the newer_method can be nullptr
// assert(newer_method != NULL, "method_with_idnum() should not be NULL");
// assert(newer_method != nullptr, "method_with_idnum() should not be nullptr");
if (newer_method != nullptr) {
assert(newer_klass == newer_method->method_holder(), "call after swapping redefined guts");

View File

@@ -4021,15 +4021,6 @@ jint Arguments::parse(const JavaVMInitArgs* initial_cmd_args) {
setup_hotswap_agent();
if (AllowEnhancedClassRedefinition) {
if (!FLAG_IS_CMDLINE(ClassUnloading)) {
ClassUnloading = false;
ClassUnloadingWithConcurrentMark = false;
} else {
warning("The JVM is unstable when using -XX:+AllowEnhancedClassRedefinition and -XX:+ClassUnloading together!");
}
}
#if !INCLUDE_CDS
if (DumpSharedSpaces || RequireSharedSpaces) {
jio_fprintf(defaultStream::error_stream(),

View File

@@ -145,7 +145,7 @@ class Proxy {
interFace = target = null;
flags = 0;
}
inverse = inverseProxy == null ? new Proxy(repository, inverseInfo, specialization, this, null, null) : inverseProxy;
inverse = inverseProxy == null ? new Proxy(repository, inverseInfo, inverseSpecialization, this, null, null) : inverseProxy;
if (inverse.getInterface() != null) directDependencies = Set.of(inverse);
}

View File

@@ -103,7 +103,12 @@ class ProxyGenerator {
accessContext.canAccess(info.targetLookup.lookupClass()) ? info.targetLookup.lookupClass() : Object.class;
targetDescriptor = constructorTargetParameterType == null ? "" : constructorTargetParameterType.descriptorString();
proxyName = proxyGenLookup.lookupClass().getPackageName().replace('.', '/') + "/" + interFace.getSimpleName();
// Even though generated proxy is hidden and therefore has no qualified name,
// it can reference itself via internal name, which can lead to name collisions.
// Let's consider specialized proxy for java/util/List - if we name proxy similarly,
// methods calls to java/util/List will be treated by VM as calls to proxy class,
// not standard library interface. Therefore we append $$$ to proxy name to avoid name collision.
proxyName = proxyGenLookup.lookupClass().getPackageName().replace('.', '/') + "/" + interFace.getSimpleName() + "$$$";
originalProxyWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES) {
@Override

View File

@@ -124,4 +124,5 @@ public class Exception extends Throwable {
}
private static class JB$$Assertion {}
private static class JB$$Event {}
}

View File

@@ -1320,3 +1320,40 @@ JNU_Fatal(JNIEnv *env, const char *file, int line, const char *msg)
(*env)->ThrowNew(env, cls, real_msg);
}
}
JNIEXPORT void JNICALL
JNU_LogEvent(JNIEnv *env, const char *file, int line, const char *fmt, ...)
{
jclass cls = (*env)->FindClass(env, "java/lang/Exception$JB$$Event");
if (cls == 0) return;
va_list args;
va_start(args, fmt);
int len = vsnprintf(NULL, 0, fmt, args);
va_end(args);
if (len < 0) return;
int suffix_len = (int) strlen(file) + 64;
len += suffix_len;
char * real_msg = malloc(len);
if (real_msg == NULL) return;
va_start(args, fmt);
vsnprintf(real_msg, len, fmt, args);
va_end(args);
char * suffix = malloc(suffix_len);
if (suffix == NULL) {
free(real_msg);
return;
}
snprintf(suffix, suffix_len, " (%s:%d)", file, line);
strncat(real_msg, suffix, suffix_len);
free(suffix);
// Throwing an exception by this name will result in Events::log() call
(*env)->ThrowNew(env, cls, real_msg);
free(real_msg);
}

View File

@@ -244,6 +244,8 @@ JNU_GetStaticFieldByName(JNIEnv *env,
JNIEXPORT void JNICALL
JNU_Fatal(JNIEnv *env, const char *file, int line, const char *msg);
JNIEXPORT void JNICALL
JNU_LogEvent(JNIEnv *env, const char *file, int line, const char *fmt, ...);
/************************************************************************
* Miscellaneous utilities used by the class libraries
@@ -329,6 +331,11 @@ JNU_Fatal(JNIEnv *env, const char *file, int line, const char *msg);
} \
} while(0)
#define JNU_LOG_EVENT(env, fmt, ...) \
do { \
JNU_LogEvent((env), __FILE__, __LINE__, (fmt), ##__VA_ARGS__); \
} while(0)
/************************************************************************
* Debugging utilities
*/

View File

@@ -32,6 +32,7 @@
#import "LWCToolkit.h"
@class AWTView;
@class AWTWindowZoomButtonMouseResponder;
@interface AWTWindow : NSObject <NSWindowDelegate> {
@private
@@ -79,6 +80,7 @@
@property (nonatomic, retain) NSLayoutConstraint *customTitleBarHeightConstraint;
@property (nonatomic, retain) NSMutableArray *customTitleBarButtonCenterXConstraints;
@property (nonatomic) BOOL hideTabController;
@property (nonatomic, retain) AWTWindowZoomButtonMouseResponder* zoomButtonMouseResponder;
- (id) initWithPlatformWindow:(jobject)javaPlatformWindow
ownerWindow:owner
@@ -128,8 +130,14 @@
NSColor* _color;
}
- (void)configureColors;
@end
@interface AWTWindowZoomButtonMouseResponder : NSResponder
- (id) initWithWindow:(NSWindow*)window;
@end
#endif _AWTWINDOW_H

View File

@@ -82,6 +82,7 @@ BOOL isColorMatchingEnabled() {
@interface NSWindow (Private)
- (void)_setTabBarAccessoryViewController:(id)controller;
- (void)setIgnoreMove:(BOOL)value;
- (BOOL)isIgnoreMove;
- (void)_adjustWindowToScreen;
@end
@@ -403,6 +404,10 @@ AWT_NS_WINDOW_IMPLEMENTATION
self.movable = !value;
}
- (BOOL)isIgnoreMove {
return _ignoreMove;
}
- (void)_adjustWindowToScreen {
if (_ignoreMove) {
self.movable = YES;
@@ -808,6 +813,7 @@ AWT_ASSERT_APPKIT_THREAD;
self.customTitleBarConstraints = nil;
self.customTitleBarHeightConstraint = nil;
self.customTitleBarButtonCenterXConstraints = nil;
self.zoomButtonMouseResponder = nil;
[super dealloc];
}
@@ -1659,6 +1665,9 @@ static const CGFloat DefaultHorizontalTitleBarButtonOffset = 20.0;
]];
[self.nsWindow setIgnoreMove:YES];
self.zoomButtonMouseResponder = [[AWTWindowZoomButtonMouseResponder alloc] initWithWindow:self.nsWindow];
[self.zoomButtonMouseResponder release]; // property retains the object
AWTWindowDragView* windowDragView = [[AWTWindowDragView alloc] initWithPlatformWindow:self.javaPlatformWindow];
[titlebar addSubview:windowDragView positioned:NSWindowBelow relativeTo:closeButtonView];
@@ -1906,6 +1915,62 @@ static const CGFloat DefaultHorizontalTitleBarButtonOffset = 20.0;
@end // AWTWindow
@implementation AWTWindowZoomButtonMouseResponder {
NSWindow* _window;
NSTrackingArea* _trackingArea;
}
- (id) initWithWindow:(NSWindow*)window {
self = [super init];
if (self == nil) {
return nil;
}
if (![window isKindOfClass: [AWTWindow_Normal class]]) {
[self release];
return nil;
}
NSView* zoomButtonView = [window standardWindowButton:NSWindowZoomButton];
if (zoomButtonView == nil) {
[self release];
return nil;
}
_window = [window retain];
_trackingArea = [[NSTrackingArea alloc]
initWithRect:zoomButtonView.bounds
options:NSTrackingMouseEnteredAndExited | NSTrackingActiveInKeyWindow
owner:self
userInfo:nil
];
[zoomButtonView addTrackingArea:_trackingArea];
return self;
}
- (void)mouseEntered:(NSEvent*)event {
if ([_window isIgnoreMove]) {
// Enable moving the window while we're mousing over the "maximize" button so that
// macOS 15 window tiling actions can properly appear
_window.movable = YES;
}
}
- (void)mouseExited:(NSEvent*)event {
if ([_window isIgnoreMove]) {
_window.movable = NO;
}
}
- (void)dealloc {
[_window release];
[_trackingArea release];
[super dealloc];
}
@end
@implementation AWTWindowDragView {
BOOL _dragging;
}

View File

@@ -85,10 +85,13 @@
@property jboolean useMaskColor;
@property (readonly, strong) id<MTLDevice> device;
@property (readonly) NSString* shadersLib;
@property (strong) id<MTLCommandQueue> commandQueue;
@property (strong) id<MTLCommandQueue> blitCommandQueue;
@property (strong) id<MTLBuffer> vertexBuffer;
@property (readonly) NSMutableDictionary<NSNumber*, NSValue*>* displayLinks;
@property (readonly) EncoderManager * encoderManager;
@property (readonly) MTLSamplerManager * samplerManager;
@property (readonly) MTLStencilManager * stencilManager;
@@ -106,10 +109,13 @@
*/
+ (MTLContext*) setSurfacesEnv:(JNIEnv*)env src:(jlong)pSrc dst:(jlong)pDst;
- (id)initWithDevice:(jint)displayID shadersLib:(NSString*)shadersLib;
+ (NSMutableDictionary*) contextStore;
+ (MTLContext*) createContextWithDeviceIfAbsent:(jint)displayID shadersLib:(NSString*)mtlShadersLib;
- (id)initWithDevice:(id<MTLDevice>)device display:(jint) displayID shadersLib:(NSString*)mtlShadersLib;
- (void)dealloc;
- (void)handleDisplayLink: (BOOL)enabled source:(const char*)src;
- (void)handleDisplayLink:(BOOL)enabled display:(jint)display source:(const char*)src;
- (void)createDisplayLinkIfAbsent: (jint)displayID;
/**
* Resets the current clip state (disables both scissor and depth tests).

View File

@@ -72,7 +72,11 @@ static struct TxtVertex verts[PGRAM_VERTEX_COUNT] = {
{{-1.0, 1.0}, {0.0, 0.0}}
};
MTLTransform* tempTransform = nil;
typedef struct {
jint displayID;
CVDisplayLinkRef displayLink;
MTLContext* mtlc;
} DLParams;
@implementation MTLCommandBufferWrapper {
id<MTLCommandBuffer> _commandBuffer;
@@ -134,7 +138,6 @@ MTLTransform* tempTransform = nil;
@implementation MTLContext {
MTLCommandBufferWrapper * _commandBufferWrapper;
CVDisplayLinkRef _displayLink;
NSMutableSet* _layers;
int _displayLinkCount;
CFTimeInterval _lastRedrawTime;
@@ -153,34 +156,101 @@ MTLTransform* tempTransform = nil;
@synthesize textureFunction,
vertexCacheEnabled, aaEnabled, useMaskColor,
device, pipelineStateStorage,
device, shadersLib, pipelineStateStorage,
commandQueue, blitCommandQueue, vertexBuffer,
texturePool, paint=_paint, encoderManager=_encoderManager,
samplerManager=_samplerManager, stencilManager=_stencilManager;
extern void initSamplers(id<MTLDevice> device);
- (id)initWithDevice:(jint)displayID shadersLib:(NSString*)shadersLib {
+ (NSMutableDictionary*) contextStore {
static NSMutableDictionary<NSNumber*, MTLContext*> *_contextStore;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
_contextStore = [[NSMutableDictionary alloc] init];
});
return _contextStore;
}
+ (MTLContext*) createContextWithDeviceIfAbsent:(jint)displayID shadersLib:(NSString*)mtlShadersLib {
// Initialization code here.
id<MTLDevice> device = CGDirectDisplayCopyCurrentMetalDevice(displayID);
if (device == nil) {
J2dRlsTraceLn1(J2D_TRACE_ERROR, "MTLContext_createContextWithDeviceIfAbsent(): Cannot create device from "
"displayID=%d", displayID)
// Fallback to the default metal device for main display
jint mainDisplayID = CGMainDisplayID();
if (displayID == mainDisplayID) {
device = MTLCreateSystemDefaultDevice();
}
if (device == nil) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLContext_createContextWithDeviceIfAbsent(): Cannot fallback to default "
"metal device")
return nil;
}
}
id<NSCopying> devID = nil;
if (@available(macOS 10.13, *)) {
devID = @(device.registryID);
} else {
devID = device.name;
}
MTLContext* mtlc = MTLContext.contextStore[devID];
if (mtlc == nil) {
mtlc = [[MTLContext alloc] initWithDevice:device display:displayID shadersLib:mtlShadersLib];
if (mtlc != nil) {
MTLContext.contextStore[devID] = mtlc;
[mtlc release];
J2dRlsTraceLn4(J2D_TRACE_INFO,
"MTLContext_createContextWithDeviceIfAbsent: new context(%p) for display=%d device=\"%s\" "
"retainCount=%d", mtlc, displayID, [mtlc.device.name UTF8String], mtlc.retainCount)
}
} else {
if (![mtlc.shadersLib isEqualToString:mtlShadersLib]) {
J2dRlsTraceLn3(J2D_TRACE_ERROR,
"MTLContext_createContextWithDeviceIfAbsent: cannot reuse context(%p) for display=%d "
"device=\"%s\", shaders lib has been changed", mtlc, displayID, [mtlc.device.name UTF8String])
return nil;
}
[mtlc retain];
J2dRlsTraceLn4(J2D_TRACE_INFO,
"MTLContext_createContextWithDeviceIfAbsent: reuse context(%p) for display=%d device=\"%s\" "
"retainCount=%d", mtlc, displayID, [mtlc.device.name UTF8String], mtlc.retainCount)
}
[mtlc createDisplayLinkIfAbsent:displayID];
return mtlc;
}
+ (void) releaseContext:(MTLContext*) mtlc {
id<NSCopying> devID = nil;
if (@available(macOS 10.13, *)) {
devID = @(mtlc.device.registryID);
} else {
devID = mtlc.device.name;
}
MTLContext* ctx = MTLContext.contextStore[devID];
if (mtlc == ctx) {
if (mtlc.retainCount > 1) {
[mtlc release];
J2dRlsTraceLn2(J2D_TRACE_INFO, "MTLContext_releaseContext: release context(%p) retainCount=%d", mtlc, mtlc.retainCount);
} else {
[MTLContext.contextStore removeObjectForKey:devID];
J2dRlsTraceLn1(J2D_TRACE_INFO, "MTLContext_releaseContext: dealloc context(%p)", mtlc);
}
}
}
- (id)initWithDevice:(id<MTLDevice>)mtlDevice display:(jint) displayID shadersLib:(NSString*)mtlShadersLib {
self = [super init];
if (self) {
// Initialization code here.
device = CGDirectDisplayCopyCurrentMetalDevice(displayID);
if (device == nil) {
J2dRlsTraceLn1(J2D_TRACE_ERROR, "MTLContext.initWithDevice(): Cannot create device from displayID=%d",
displayID);
// Fallback to the default metal device for main display
jint mainDisplayID = CGMainDisplayID();
if (displayID == mainDisplayID) {
device = MTLCreateSystemDefaultDevice();
}
if (device == nil) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLContext.initWithDevice(): Cannot fallback to default metal device");
return nil;
}
}
device = mtlDevice;
shadersLib = [[NSString alloc] initWithString:mtlShadersLib];
pipelineStateStorage = [[MTLPipelineStatesStorage alloc] initWithDevice:device shaderLibPath:shadersLib];
if (pipelineStateStorage == nil) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLContext.initWithDevice(): Failed to initialize MTLPipelineStatesStorage.");
@@ -213,25 +283,8 @@ extern void initSamplers(id<MTLDevice> device);
_displayLinkCount = 0;
_lastRedrawTime = 0.0;
if (isDisplaySyncEnabled()) {
_displayLinks = [[NSMutableDictionary alloc] init];
_layers = [[NSMutableSet alloc] init];
if (TRACE_CVLINK) {
J2dRlsTraceLn2(J2D_TRACE_VERBOSE, "MTLContext_CVDisplayLinkCreateWithCGDisplay: "
"ctx=%p displayID=%d", self, displayID);
}
CHECK_CVLINK("CreateWithCGDisplay",
CVDisplayLinkCreateWithCGDisplay(displayID, &_displayLink));
if (_displayLink == nil) {
J2dRlsTraceLn(J2D_TRACE_ERROR,
"MTLContext.initWithDevice(): Failed to initialize CVDisplayLink.");
return nil;
} else {
CHECK_CVLINK("SetOutputCallback", CVDisplayLinkSetOutputCallback(
_displayLink,
&mtlDisplayLinkCallback,
(__bridge void *) self));
}
} else {
_displayLink = nil;
}
_glyphCacheLCD = [[MTLGlyphCache alloc] initWithContext:self];
_glyphCacheAA = [[MTLGlyphCache alloc] initWithContext:self];
@@ -239,13 +292,47 @@ extern void initSamplers(id<MTLDevice> device);
return self;
}
- (void)handleDisplayLink: (BOOL)enabled source:(const char*)src {
if (_displayLink == nil) {
- (void)createDisplayLinkIfAbsent: (jint)displayID {
if (isDisplaySyncEnabled() && _displayLinks[@(displayID)] == nil) {
CVDisplayLinkRef _displayLink;
if (TRACE_CVLINK) {
J2dRlsTraceLn2(J2D_TRACE_VERBOSE, "MTLContext_createDisplayLinkIfAbsent: "
"ctx=%p displayID=%d", self, displayID);
}
CHECK_CVLINK("CreateWithCGDisplay",
CVDisplayLinkCreateWithCGDisplay(displayID, &_displayLink));
if (_displayLink == nil) {
J2dRlsTraceLn(J2D_TRACE_ERROR,
"MTLContext_createDisplayLinkIfAbsent: Failed to initialize CVDisplayLink.");
} else {
DLParams* dlParams = malloc(sizeof (DLParams ));
dlParams->displayID = displayID;
dlParams->displayLink = _displayLink;
dlParams->mtlc = self;
_displayLinks[@(displayID)] = [NSValue valueWithPointer:dlParams];
CHECK_CVLINK("SetOutputCallback", CVDisplayLinkSetOutputCallback(
_displayLink,
&mtlDisplayLinkCallback,
(__bridge DLParams*) dlParams));
}
}
}
- (void)handleDisplayLink: (BOOL)enabled display:(jint) display source:(const char*)src {
if (_displayLinks == nil) {
if (TRACE_CVLINK) {
J2dRlsTraceLn2(J2D_TRACE_VERBOSE, "MTLContext_handleDisplayLink[%s]: "
"ctx=%p - displayLink = nil", src, self);
"ctx=%p - displayLinks = nil", src, self);
}
} else {
NSValue* dlParamsVal = _displayLinks[@(display)];
if (dlParamsVal == nil) {
J2dRlsTraceLn3(J2D_TRACE_ERROR, "MTLContext_handleDisplayLink[%s]: "
"ctx=%p, no display link for %d ", src, self, display);
return;
}
DLParams *dlParams = [dlParamsVal pointerValue];
CVDisplayLinkRef _displayLink = dlParams->displayLink;
if (enabled) {
if (!CVDisplayLinkIsRunning(_displayLink)) {
CHECK_CVLINK("Start", CVDisplayLinkStart(_displayLink));
@@ -269,10 +356,11 @@ extern void initSamplers(id<MTLDevice> device);
- (void)dealloc {
J2dTraceLn(J2D_TRACE_INFO, "MTLContext.dealloc");
if (_displayLink != nil) {
if (_displayLinks != nil) {
[self haltRedraw];
}
[shadersLib release];
// TODO : Check that texturePool is completely released.
// texturePool content is released in MTLCommandBufferWrapper.onComplete()
//self.texturePool = nil;
@@ -627,7 +715,7 @@ extern void initSamplers(id<MTLDevice> device);
}
}
- (void) redraw {
- (void) redraw:(NSNumber*)displayIDNum {
AWT_ASSERT_APPKIT_THREAD;
/*
* Avoid repeated invocations by UIKit Main Thread
@@ -650,7 +738,7 @@ extern void initSamplers(id<MTLDevice> device);
if (_layers.count > 0) {
[_layers removeAllObjects];
}
[self handleDisplayLink:NO source:"redraw"];
[self handleDisplayLink:NO display:[displayIDNum integerValue] source:"redraw"];
}
}
@@ -658,8 +746,8 @@ CVReturn mtlDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp*
{
J2dTraceLn1(J2D_TRACE_VERBOSE, "MTLContext_mtlDisplayLinkCallback: ctx=%p", displayLinkContext);
@autoreleasepool {
MTLContext *ctx = (__bridge MTLContext *)displayLinkContext;
[ThreadUtilities performOnMainThread:@selector(redraw) on:ctx withObject:nil waitUntilDone:NO];
DLParams* dlParams = (__bridge DLParams* *)displayLinkContext;
[ThreadUtilities performOnMainThread:@selector(redraw:) on:dlParams->mtlc withObject:@(dlParams->displayID) waitUntilDone:NO];
}
return kCVReturnSuccess;
}
@@ -674,25 +762,25 @@ CVReturn mtlDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp*
// Request for redraw before starting display link to avoid rendering problem on M2 processor
[layer setNeedsDisplay];
}
[self handleDisplayLink:YES source:"startRedraw"];
[self handleDisplayLink:YES display:layer.displayID source:"startRedraw"];
}
- (void)stopRedraw:(MTLLayer*) layer {
AWT_ASSERT_APPKIT_THREAD;
J2dTraceLn2(J2D_TRACE_VERBOSE, "MTLContext_stopRedraw: ctx=%p layer=%p", self, layer);
if (_displayLink != nil) {
if (_displayLinks != nil) {
if (--layer.redrawCount <= 0) {
[_layers removeObject:layer];
layer.redrawCount = 0;
}
if ((_layers.count == 0) && (_displayLinkCount == 0)) {
[self handleDisplayLink:NO source:"stopRedraw"];
[self handleDisplayLink:NO display:layer.displayID source:"stopRedraw"];
}
}
}
- (void)haltRedraw {
if (_displayLink != nil) {
if (_displayLinks != nil) {
if (TRACE_CVLINK) {
J2dRlsTraceLn1(J2D_TRACE_VERBOSE, "MTLContext_haltRedraw: ctx=%p", self);
}
@@ -703,10 +791,16 @@ CVReturn mtlDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp*
[_layers removeAllObjects];
}
_displayLinkCount = 0;
[self handleDisplayLink:NO source:"haltRedraw"];
CVDisplayLinkRelease(_displayLink);
_displayLink = NULL;
NSEnumerator<NSNumber*>* keyEnum = _displayLinks.keyEnumerator;
NSNumber* displayIDVal;
while ((displayIDVal = [keyEnum nextObject])) {
[self handleDisplayLink:NO display:[displayIDVal integerValue] source:"haltRedraw"];
DLParams *dlParams = [(NSValue*)_displayLinks[displayIDVal] pointerValue];
CVDisplayLinkRelease(dlParams->displayLink);
free(dlParams);
}
[_displayLinks release];
_displayLinks = NULL;
}
}

View File

@@ -45,6 +45,7 @@
*/
typedef struct _MTLGraphicsConfigInfo {
MTLContext *context;
jint displayID;
} MTLGraphicsConfigInfo;
#endif /* MTLGraphicsConfig_h_Included */

View File

@@ -48,7 +48,7 @@ MTLGC_DestroyMTLGraphicsConfig(jlong pConfigInfo)
mtlinfo->context = nil;
[ThreadUtilities performOnMainThreadWaiting:NO block:^() {
if (mtlc != NULL) {
[mtlc release];
[MTLContext releaseContext:mtlc];
}
free(mtlinfo);
}];
@@ -92,7 +92,7 @@ JNI_COCOA_ENTER(env);
[ThreadUtilities performOnMainThreadWaiting:YES block:^() {
MTLContext* mtlc = [[MTLContext alloc] initWithDevice:displayID
MTLContext* mtlc = [MTLContext createContextWithDeviceIfAbsent:displayID
shadersLib:path];
if (mtlc != 0L) {
// create the MTLGraphicsConfigInfo record for this context
@@ -100,6 +100,7 @@ JNI_COCOA_ENTER(env);
if (mtlinfo != NULL) {
memset(mtlinfo, 0, sizeof(MTLGraphicsConfigInfo));
mtlinfo->context = mtlc;
mtlinfo->displayID = displayID;
} else {
J2dRlsTraceLn(J2D_TRACE_ERROR, "MTLGraphicsConfig_getMTLConfigInfo: could not allocate memory for mtlinfo");
[mtlc release];

View File

@@ -34,6 +34,7 @@
@property (nonatomic) jobject javaLayer;
@property (readwrite, assign) MTLContext* ctx;
@property (readwrite, assign) NSInteger displayID;
@property (readwrite, assign) id<MTLTexture>* buffer;
@property (readwrite, assign) id<MTLTexture>* outBuffer;
@property (readwrite, atomic) int nextDrawableCount;

View File

@@ -554,6 +554,7 @@ Java_sun_java2d_metal_MTLLayer_validate
layer.buffer = &bmtlsdo->pTexture;
layer.outBuffer = &bmtlsdo->pOutTexture;
layer.ctx = ((MTLSDOps *)bmtlsdo->privOps)->configInfo->context;
layer.displayID = ((MTLSDOps *)bmtlsdo->privOps)->configInfo->displayID;
layer.device = layer.ctx.device;
layer.pixelFormat = MTLPixelFormatBGRA8Unorm;

View File

@@ -36,8 +36,11 @@
#include "MTLRenderQueue.h"
#include "MTLRenderer.h"
#include "MTLTextRenderer.h"
#include "MTLUtils.h"
#import "ThreadUtilities.h"
#define TRACE_OP 0
/**
* References to the "current" context and destination surface.
*/
@@ -48,6 +51,64 @@ jint mtlPreviousOp = MTL_OP_INIT;
extern BOOL isDisplaySyncEnabled();
extern void MTLGC_DestroyMTLGraphicsConfig(jlong pConfigInfo);
static const char* mtlOpCodeToStr(uint opcode);
static const char* mtlOpToStr(uint op);
/*
* Derived from JNI_COCOA_ENTER(env):
* Create a pool and initiate a try block to catch any exception
*/
#define RENDER_LOOP_ENTER(env) \
BOOL sync = NO; \
jint opcode = -1; \
const NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; \
@try
/*
* Derived from JNI_COCOA_EXIT(env):
* Don't allow NSExceptions to escape to Java.
* If there is a Java exception that has been thrown that should escape.
* And ensure we drain the auto-release pool.
*/
#define RENDER_LOOP_EXIT(env, className) \
@catch (NSException *e) { \
lwc_plog(env, "%s_flushBuffer: Failed opcode=%s op=%s dstType=%s ctx=%p", \
className, mtlOpCodeToStr(opcode), mtlOpToStr(mtlPreviousOp), \
mtlDstTypeToStr(DST_TYPE(dstOps)), mtlc); \
char* str = [NSString stringWithFormat:@"%@", [e description]].UTF8String; \
lwc_plog(env, "%s_flushBuffer Exception: %s", className, str); \
str = [NSString stringWithFormat:@"%@", [e callStackSymbols]].UTF8String; \
lwc_plog(env, "%s_flushBuffer callstack: %s", className, str); \
/* Finally (JetBrains Runtime only) report this message to JVM crash log: */ \
JNU_LOG_EVENT(env, "%s_flushBuffer: Failed opcode=%s op=%s dstType=%s ctx=%p", \
className, mtlOpCodeToStr(opcode), mtlOpToStr(mtlPreviousOp), \
mtlDstTypeToStr(DST_TYPE(dstOps)), mtlc); \
/* report fatal failure to make a crash report: */ \
JNU_Fatal(env, __FILE__, __LINE__, "flushBuffer failed"); \
} \
@finally { \
/* flush GPU state before draining pool: */ \
MTLRenderQueue_reset(mtlc, sync); \
[pool drain]; \
}
void MTLRenderQueue_reset(MTLContext* context, BOOL sync)
{
// Ensure flushing encoder before draining the NSAutoreleasePool:
if (context != NULL) {
if (mtlPreviousOp == MTL_OP_MASK_OP) {
MTLVertexCache_DisableMaskCache(context);
}
if (isDisplaySyncEnabled()) {
[context commitCommandBuffer:NO display:YES];
} else {
[context commitCommandBuffer:sync display:YES];
}
}
RESET_PREVIOUS_OP();
}
void MTLRenderQueue_CheckPreviousOp(jint op) {
if (mtlPreviousOp == op) {
@@ -69,6 +130,9 @@ void MTLRenderQueue_CheckPreviousOp(jint op) {
J2dTraceLn1(J2D_TRACE_VERBOSE,
"MTLRenderQueue_CheckPreviousOp: new op=%d", op);
if (TRACE_OP) J2dRlsTraceLn2(J2D_TRACE_INFO, "MTLRenderQueue_CheckPreviousOp: op=%s\tnew op=%s",
mtlOpToStr(mtlPreviousOp), mtlOpToStr(op));
switch (mtlPreviousOp) {
case MTL_OP_INIT :
mtlPreviousOp = op;
@@ -99,7 +163,6 @@ Java_sun_java2d_metal_MTLRenderQueue_flushBuffer
jlong buf, jint limit)
{
unsigned char *b, *end;
BOOL sync = NO;
J2dTraceLn1(J2D_TRACE_INFO,
"MTLRenderQueue_flushBuffer: limit=%d", limit);
@@ -111,13 +174,17 @@ Java_sun_java2d_metal_MTLRenderQueue_flushBuffer
}
end = b + limit;
@autoreleasepool {
// Handle any NSException thrown:
RENDER_LOOP_ENTER(env)
{
while (b < end) {
jint opcode = NEXT_INT(b);
opcode = NEXT_INT(b);
J2dTraceLn2(J2D_TRACE_VERBOSE,
"MTLRenderQueue_flushBuffer: opcode=%d, rem=%d",
opcode, (end-b));
if (TRACE_OP) J2dRlsTraceLn1(J2D_TRACE_VERBOSE, "MTLRenderQueue_flushBuffer: opcode=%s", mtlOpCodeToStr(opcode));
switch (opcode) {
@@ -143,7 +210,6 @@ Java_sun_java2d_metal_MTLRenderQueue_flushBuffer
CHECK_RENDER_OP(MTL_OP_OTHER, dstOps, sync);
if ([mtlc useXORComposite]) {
[mtlc commitCommandBuffer:YES display:NO];
J2dTraceLn(J2D_TRACE_VERBOSE,
"DRAW_RECT in XOR mode - Force commit earlier draw calls before DRAW_RECT.");
@@ -660,6 +726,20 @@ Java_sun_java2d_metal_MTLRenderQueue_flushBuffer
break;
}
case sun_java2d_pipe_BufferedOpCodes_FLUSH_BUFFER:
{
CHECK_PREVIOUS_OP(MTL_OP_OTHER);
jlong pLayerPtr = NEXT_LONG(b);
MTLLayer* layer = (MTLLayer*)pLayerPtr;
if (layer != nil) {
[layer flushBuffer];
} else {
J2dRlsTraceLn(J2D_TRACE_ERROR,
"MTLRenderQueue_flushBuffer(FLUSH_BUFFER): MTLLayer is nil");
}
break;
}
case sun_java2d_pipe_BufferedOpCodes_SYNC:
{
CHECK_PREVIOUS_OP(MTL_OP_SYNC);
@@ -862,40 +942,14 @@ Java_sun_java2d_metal_MTLRenderQueue_flushBuffer
break;
}
case sun_java2d_pipe_BufferedOpCodes_FLUSH_BUFFER:
{
CHECK_PREVIOUS_OP(MTL_OP_OTHER);
jlong pLayerPtr = NEXT_LONG(b);
MTLLayer* layer = (MTLLayer*)pLayerPtr;
if (layer != nil) {
[layer flushBuffer];
} else {
J2dRlsTraceLn(J2D_TRACE_ERROR,
"MTLRenderQueue_flushBuffer(FLUSH_BUFFER): MTLLayer is nil");
}
break;
}
default:
J2dRlsTraceLn1(J2D_TRACE_ERROR,
"MTLRenderQueue_flushBuffer: invalid opcode=%d", opcode);
return;
}
}
if (mtlc != NULL) {
if (mtlPreviousOp == MTL_OP_MASK_OP) {
MTLVertexCache_DisableMaskCache(mtlc);
}
if (isDisplaySyncEnabled()) {
[mtlc commitCommandBuffer:NO display:YES];
} else {
[mtlc commitCommandBuffer:sync display:YES];
}
}
RESET_PREVIOUS_OP();
} // while op
}
RENDER_LOOP_EXIT(env, "MTLRenderQueue");
}
/**
@@ -916,4 +970,108 @@ BMTLSDOps *
MTLRenderQueue_GetCurrentDestination()
{
return dstOps;
}
}
/* debugging helper functions */
static const char* mtlOpToStr(uint op) {
#undef CASE_MTL_OP
#define CASE_MTL_OP(X) \
case MTL_OP_##X: \
return #X;
switch (op) {
CASE_MTL_OP(INIT)
CASE_MTL_OP(AA)
CASE_MTL_OP(SET_COLOR)
CASE_MTL_OP(RESET_PAINT)
CASE_MTL_OP(SYNC)
CASE_MTL_OP(SHAPE_CLIP_SPANS)
CASE_MTL_OP(MASK_OP)
CASE_MTL_OP(OTHER)
default:
return "";
}
#undef CASE_MTL_OP
}
static const char* mtlOpCodeToStr(uint opcode) {
#undef CASE_BUF_OP
#define CASE_BUF_OP(X) \
case sun_java2d_pipe_BufferedOpCodes_##X: \
return #X;
switch (opcode) {
// draw ops
CASE_BUF_OP(DRAW_LINE)
CASE_BUF_OP(DRAW_RECT)
CASE_BUF_OP(DRAW_POLY)
CASE_BUF_OP(DRAW_PIXEL)
CASE_BUF_OP(DRAW_SCANLINES)
CASE_BUF_OP(DRAW_PARALLELOGRAM)
CASE_BUF_OP(DRAW_AAPARALLELOGRAM)
// fill ops
CASE_BUF_OP(FILL_RECT)
CASE_BUF_OP(FILL_SPANS)
CASE_BUF_OP(FILL_PARALLELOGRAM)
CASE_BUF_OP(FILL_AAPARALLELOGRAM)
// copy-related ops
CASE_BUF_OP(COPY_AREA)
CASE_BUF_OP(BLIT)
CASE_BUF_OP(MASK_FILL)
CASE_BUF_OP(MASK_BLIT)
CASE_BUF_OP(SURFACE_TO_SW_BLIT)
// text-related ops
CASE_BUF_OP(DRAW_GLYPH_LIST)
// state-related ops
CASE_BUF_OP(SET_RECT_CLIP)
CASE_BUF_OP(BEGIN_SHAPE_CLIP)
CASE_BUF_OP(SET_SHAPE_CLIP_SPANS)
CASE_BUF_OP(END_SHAPE_CLIP)
CASE_BUF_OP(RESET_CLIP)
CASE_BUF_OP(SET_ALPHA_COMPOSITE)
CASE_BUF_OP(SET_XOR_COMPOSITE)
CASE_BUF_OP(RESET_COMPOSITE)
CASE_BUF_OP(SET_TRANSFORM)
CASE_BUF_OP(RESET_TRANSFORM)
// context-related ops
CASE_BUF_OP(SET_SURFACES)
CASE_BUF_OP(SET_SCRATCH_SURFACE)
CASE_BUF_OP(FLUSH_SURFACE)
CASE_BUF_OP(DISPOSE_SURFACE)
CASE_BUF_OP(DISPOSE_CONFIG)
CASE_BUF_OP(INVALIDATE_CONTEXT)
CASE_BUF_OP(SYNC)
CASE_BUF_OP(RESTORE_DEVICES)
CASE_BUF_OP(CONFIGURE_SURFACE) /* unsupported */
CASE_BUF_OP(SWAP_BUFFERS) /* unsupported */
CASE_BUF_OP(FLUSH_BUFFER)
// special no-op (mainly used for achieving 8-byte alignment)
CASE_BUF_OP(NOOP)
// paint-related ops
CASE_BUF_OP(RESET_PAINT)
CASE_BUF_OP(SET_COLOR)
CASE_BUF_OP(SET_GRADIENT_PAINT)
CASE_BUF_OP(SET_LINEAR_GRADIENT_PAINT)
CASE_BUF_OP(SET_RADIAL_GRADIENT_PAINT)
CASE_BUF_OP(SET_TEXTURE_PAINT)
// BufferedImageOp-related ops
CASE_BUF_OP(ENABLE_CONVOLVE_OP)
CASE_BUF_OP(DISABLE_CONVOLVE_OP)
CASE_BUF_OP(ENABLE_RESCALE_OP)
CASE_BUF_OP(DISABLE_RESCALE_OP)
CASE_BUF_OP(ENABLE_LOOKUP_OP)
CASE_BUF_OP(DISABLE_LOOKUP_OP)
default:
return "";
}
#undef CASE_BUF_OP
}

View File

@@ -27,7 +27,12 @@
#define MTLUtils_h_Included
#import <Metal/Metal.h>
#import "MTLSurfaceDataBase.h"
#define MTLAASampleCount 4
#define DST_TYPE(dstOps) ((dstOps != NULL) ? dstOps->drawableType : MTLSD_UNDEFINED)
const char* mtlDstTypeToStr(uint op);
#endif /* MTLUtils_h_Included */

View File

@@ -82,3 +82,22 @@ jboolean isOptionEnabled(const char * option) {
NSString * lowerCaseProp = [optionProp localizedLowercaseString];
return [@"true" isEqual:lowerCaseProp];
}
const char* mtlDstTypeToStr(uint op) {
#undef CASE_MTLSD_OP
#define CASE_MTLSD_OP(X) \
case MTLSD_##X: \
return #X;
switch (op) {
CASE_MTLSD_OP(UNDEFINED)
CASE_MTLSD_OP(WINDOW)
CASE_MTLSD_OP(TEXTURE)
CASE_MTLSD_OP(FLIP_BACKBUFFER)
CASE_MTLSD_OP(RT_TEXTURE)
default:
return "";
}
#undef CASE_MTLSD_OP
}

View File

@@ -222,11 +222,12 @@
*/
#define JNI_COCOA_EXIT(env) \
} \
@catch (NSException *e) { \
NSLog(@"%@", [e callStackSymbols]); \
@catch (NSException *e) { \
NSLog(@"Apple AWT Cocoa Exception: %@", [e description]); \
NSLog(@"Apple AWT Cocoa Exception callstack: %@", [e callStackSymbols]); \
} \
@finally { \
[pool drain]; \
[pool drain]; \
};
/* Same as above but adds a clean up action.
@@ -236,10 +237,11 @@
} \
@catch (NSException *e) { \
{ action; }; \
NSLog(@"%@", [e callStackSymbols]); \
NSLog(@"Apple AWT Cocoa Exception: %@", [e description]); \
NSLog(@"Apple AWT Cocoa Exception callstack: %@", [e callStackSymbols]); \
} \
@finally { \
[pool drain]; \
[pool drain]; \
};
/******** STRING CONVERSION SUPPORT *********/

View File

@@ -139,7 +139,6 @@ __attribute__((visibility("default")))
+ (void)detachCurrentThread;
+ (void)setAppkitThreadGroup:(jobject)group;
+ (NSString*)getCaller;
+ (void)performOnMainThreadNowOrLater:(void (^)())block;
+ (void)performOnMainThreadWaiting:(BOOL)wait block:(void (^)())block;
+ (void)performOnMainThread:(SEL)aSelector on:(id)target withObject:(id)arg waitUntilDone:(BOOL)wait;
@@ -149,4 +148,7 @@ __attribute__((visibility("default")))
JNIEXPORT void OSXAPP_SetJavaVM(JavaVM *vm);
/* LWCToolkit's PlatformLogger wrapper */
JNIEXPORT void lwc_plog(JNIEnv* env, const char *formatMsg, ...);
#endif /* __THREADUTILITIES_H */

View File

@@ -31,12 +31,6 @@
#import "ThreadUtilities.h"
#define USE_LWC_LOG 1
#if USE_LWC_LOG == 1
void lwc_plog(JNIEnv* env, const char *formatMsg, ...);
#endif
/* Returns the MainThread latency threshold in milliseconds
* used to detect slow operations that may cause high latencies or delays.
* If <=0, the MainThread monitor is disabled */
@@ -142,7 +136,7 @@ AWT_ASSERT_APPKIT_THREAD;
if ([NSThread isMainThread]) {
block();
} else {
[self performOnMainThread:@selector(invokeBlockCopy:) on:self withObject:Block_copy(block) waitUntilDone:NO];
[ThreadUtilities performOnMainThread:@selector(invokeBlockCopy:) on:self withObject:Block_copy(block) waitUntilDone:NO];
}
}
@@ -150,7 +144,7 @@ AWT_ASSERT_APPKIT_THREAD;
if ([NSThread isMainThread] && wait) {
block();
} else {
[self performOnMainThread:@selector(invokeBlockCopy:) on:self withObject:Block_copy(block) waitUntilDone:wait];
[ThreadUtilities performOnMainThread:@selector(invokeBlockCopy:) on:self withObject:Block_copy(block) waitUntilDone:wait];
}
}
@@ -180,22 +174,19 @@ AWT_ASSERT_APPKIT_THREAD;
setBlockingEventDispatchThread(NO);
}
});
[self performSelectorOnMainThread:@selector(invokeBlockCopy:) withObject:blockCopy waitUntilDone:YES modes:javaModes];
[ThreadUtilities performSelectorOnMainThread:@selector(invokeBlockCopy:) withObject:blockCopy waitUntilDone:YES modes:javaModes];
} else {
[target performSelectorOnMainThread:aSelector withObject:arg waitUntilDone:wait modes:javaModes];
}
} else {
// Perform instrumentation on selector:
const NSString* caller = [self getCaller];
const NSString* caller = [ThreadUtilities getCaller];
BOOL invokeDirect = NO;
BOOL blockingEDT;
BOOL blockingEDT = NO;
if ([NSThread isMainThread] && wait) {
invokeDirect = YES;
blockingEDT = NO;
} else if (wait && isEventDispatchThread()) {
blockingEDT = YES;
} else {
blockingEDT = NO;
}
const char* operation = (invokeDirect ? "now " : (blockingEDT ? "block" : "later"));
@@ -212,18 +203,14 @@ AWT_ASSERT_APPKIT_THREAD;
}
const double elapsedMs = (CACurrentMediaTime() - start) * 1000.0;
if (elapsedMs > mtThreshold) {
#if USE_LWC_LOG == 1
lwc_plog([self getJNIEnv], "performOnMainThread(%s)[time: %.3lf ms]: [%s]", operation, elapsedMs, toCString(caller));
#else
NSLog(@"performOnMainThread(%s)[time: %.3lf ms]: [%@]", operation, elapsedMs, caller);
#endif
lwc_plog([ThreadUtilities getJNIEnv], "performOnMainThread(%s)[time: %.3lf ms]: [%s]", operation, elapsedMs, toCString(caller));
}
}
});
if (invokeDirect) {
[self performSelector:@selector(invokeBlockCopy:) withObject:blockCopy];
[ThreadUtilities performSelector:@selector(invokeBlockCopy:) withObject:blockCopy];
} else {
[self performSelectorOnMainThread:@selector(invokeBlockCopy:) withObject:blockCopy waitUntilDone:wait modes:javaModes];
[ThreadUtilities performSelectorOnMainThread:@selector(invokeBlockCopy:) withObject:blockCopy waitUntilDone:wait modes:javaModes];
}
}
}
@@ -264,11 +251,11 @@ JNIEXPORT void JNICALL Java_sun_awt_AWTThreading_notifyEventDispatchThreadStarte
}
}
#if USE_LWC_LOG == 1
void lwc_plog(JNIEnv* env, const char *formatMsg, ...) {
if (formatMsg == NULL)
/* LWCToolkit's PlatformLogger wrapper */
JNIEXPORT void lwc_plog(JNIEnv* env, const char *formatMsg, ...) {
if ((env == NULL) || (formatMsg == NULL)) {
return;
}
static jobject loggerObject = NULL;
static jmethodID midWarn = NULL;
@@ -286,20 +273,28 @@ void lwc_plog(JNIEnv* env, const char *formatMsg, ...) {
}
GET_METHOD(midWarn, clazz, "warning", "(Ljava/lang/String;)V");
}
if (midWarn != NULL) {
va_list args;
va_start(args, formatMsg);
/* formatted message can be large (stack trace ?) => 16 kb */
const int bufSize = 16 * 1024;
char buf[bufSize];
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wformat-nonliteral"
vsnprintf(buf, bufSize, formatMsg, args);
#pragma clang diagnostic pop
va_end(args);
va_list args;
va_start(args, formatMsg);
const int bufSize = 512;
char buf[bufSize];
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wformat-nonliteral"
vsnprintf(buf, bufSize, formatMsg, args);
#pragma clang diagnostic pop
va_end(args);
jstring jstr = (*env)->NewStringUTF(env, buf);
(*env)->CallVoidMethod(env, loggerObject, midWarn, jstr);
(*env)->DeleteLocalRef(env, jstr);
const jstring javaString = (*env)->NewStringUTF(env, buf);
if ((*env)->ExceptionCheck(env)) {
// fallback:
NSLog(@"%s\n", buf); \
} else {
JNU_CHECK_EXCEPTION(env);
(*env)->CallVoidMethod(env, loggerObject, midWarn, javaString);
CHECK_EXCEPTION();
return;
}
(*env)->DeleteLocalRef(env, javaString);
}
}
#endif

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2024, 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,7 +23,6 @@
* questions.
*/
package java.awt;
import java.awt.image.BufferedImage;
@@ -38,7 +37,6 @@ import sun.font.FontManager;
import sun.font.FontManagerFactory;
import sun.java2d.HeadlessGraphicsEnvironment;
import sun.java2d.SunGraphicsEnvironment;
import sun.security.action.GetPropertyAction;
/**
*

View File

@@ -61,6 +61,7 @@ public final class WLClipboard extends SunClipboard {
// A handle to the native clipboard representation, 0 if not available.
private long clipboardNativePtr; // guarded by 'this'
private long ourOfferNativePtr; // guarded by 'this'
// The list of numeric format IDs the current clipboard is available in;
// could be null or empty.
@@ -134,8 +135,19 @@ public final class WLClipboard extends SunClipboard {
protected void setContentsNative(Transferable contents) {
// The server requires "serial number of the event that triggered this request"
// as a proof of the right to copy data.
WLPointerEvent wlPointerEvent = WLToolkit.getInputState().eventWithSerial();
long eventSerial = wlPointerEvent == null ? 0 : wlPointerEvent.getSerial();
// It is not specified which event's serial number the Wayland server expects here,
// so the following is a speculation based on experiments.
// The worst case is that a "wrong" serial will be silently ignored, and our clipboard
// will be out of sync with the real one that Wayland maintains.
long eventSerial = isPrimary
? WLToolkit.getInputState().pointerButtonSerial()
: WLToolkit.getInputState().keySerial();
if (!isPrimary && eventSerial == 0) {
// The "regular" clipboard's content can be changed with either a mouse click
// (like on a menu item) or with the keyboard (Ctrl-C).
eventSerial = WLToolkit.getInputState().pointerButtonSerial();
}
if (log.isLoggable(PlatformLogger.Level.FINE)) {
log.fine("Clipboard: About to offer new contents using Wayland event serial " + eventSerial);
}
@@ -161,7 +173,12 @@ public final class WLClipboard extends SunClipboard {
log.fine("Clipboard: Offering new contents (" + contents + ") in these MIME formats: " + Arrays.toString(mime));
}
offerData(eventSerial, mime, contents, dataOfferQueuePtr);
synchronized (this) {
if (ourOfferNativePtr != 0) {
cancelOffer(ourOfferNativePtr);
}
ourOfferNativePtr = offerData(eventSerial, mime, contents, dataOfferQueuePtr);
}
// Once we have offered the data, someone may come back and ask to provide them.
// In that event, the transferContentsWithType() will be called from native on the
@@ -303,24 +320,25 @@ public final class WLClipboard extends SunClipboard {
* has been made available. The list of supported formats
* should have already been received and saved in newClipboardFormats.
*
* @param nativePtr a native handle to the clipboard
* @param newClipboardNativePtr a native handle to the clipboard
*/
private void handleNewClipboard(long nativePtr) {
private void handleNewClipboard(long newClipboardNativePtr) {
// Since we have a new clipboard, the existing one is no longer valid.
// We have now way of knowing whether this "new" one is the same as the "old" one.
// We have no way of knowing whether this "new" one is the same as the "old" one.
lostOwnershipNow(null);
if (log.isLoggable(PlatformLogger.Level.FINE)) {
log.fine("Clipboard: new clipboard is available: " + nativePtr);
log.fine("Clipboard: new clipboard is available: " + newClipboardNativePtr);
}
synchronized (this) {
long oldClipboardNativePtr = clipboardNativePtr;
if (oldClipboardNativePtr != 0) {
// "The client must destroy the previous selection data_offer, if any, upon receiving this event."
destroyClipboard(oldClipboardNativePtr);
}
clipboardFormats = newClipboardFormats;
clipboardNativePtr = nativePtr; // Could be NULL
clipboardNativePtr = newClipboardNativePtr; // Could be NULL
newClipboardFormats = new ArrayList<>(INITIAL_MIME_FORMATS_COUNT);
@@ -328,6 +346,13 @@ public final class WLClipboard extends SunClipboard {
}
}
private void handleOfferCancelled(long offerNativePtr) {
synchronized (this) {
assert offerNativePtr == ourOfferNativePtr;
ourOfferNativePtr = 0;
}
}
@Override
protected void registerClipboardViewerChecked() {
// TODO: is there any need to do more here?
@@ -416,8 +441,8 @@ public final class WLClipboard extends SunClipboard {
private static native long createDataOfferQueue();
private static native void dispatchDataOfferQueueImpl(long dataOfferQueuePtr);
private native long initNative(boolean isPrimary);
private native void offerData(long eventSerial, String[] mime, Object data, long dataOfferQueuePtr);
private native void cancelOffer(long eventSerial); // TODO: this is unused, delete, maybe?
private native long offerData(long eventSerial, String[] mime, Object data, long dataOfferQueuePtr);
private native void cancelOffer(long offerNativePtr);
private native int requestDataInFormat(long clipboardNativePtr, String mime);
private native void destroyClipboard(long clipboardNativePtr);

View File

@@ -1,6 +1,6 @@
/*
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2022, JetBrains s.r.o.. All rights reserved.
* Copyright (c) 2022-2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2022-2024, 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
@@ -100,7 +100,6 @@ public class WLComponentPeer implements ComponentPeer {
{"hand"}, // HAND_CURSOR
{"move"}, // MOVE_CURSOR
};
private static final int WHEEL_SCROLL_AMOUNT = 3;
private static final int MINIMUM_WIDTH = 1;
private static final int MINIMUM_HEIGHT = 1;
@@ -772,10 +771,16 @@ public class WLComponentPeer implements ComponentPeer {
return target.getSize();
}
void showWindowMenu(int x, int y) {
void showWindowMenu(long serial, int x, int y) {
// "This request must be used in response to some sort of user action like
// a button press, key press, or touch down event."
// So 'serial' must appertain to such an event.
assert serial != 0;
int xNative = javaUnitsToSurfaceUnits(x);
int yNative = javaUnitsToSurfaceUnits(y);
performLocked(() -> nativeShowWindowMenu(nativePtr, xNative, yNative));
performLocked(() -> nativeShowWindowMenu(serial, nativePtr, xNative, yNative));
}
@Override
@@ -853,7 +858,7 @@ public class WLComponentPeer implements ComponentPeer {
}
private void updateCursorImmediately(WLInputState inputState) {
WLComponentPeer peer = inputState.getPeer();
WLComponentPeer peer = inputState.peerForPointerEvents();
if (peer == null) return;
Cursor cursor = peer.getCursor(inputState.getPointerX(), inputState.getPointerY());
setCursor(cursor, getGraphicsDevice() != null ? getGraphicsDevice().getDisplayScale() : 1);
@@ -871,6 +876,14 @@ public class WLComponentPeer implements ComponentPeer {
}
private static void setCursor(Cursor c, int scale) {
long serial = WLToolkit.getInputState().pointerEnterSerial();
if (serial == 0) {
if (log.isLoggable(Level.WARNING)) {
log.warning("setCursor aborted due to missing event serial");
}
return; // Wayland will ignore the request anyway
}
Cursor cursor;
if (c.getType() == Cursor.CUSTOM_CURSOR && !(c instanceof WLCustomCursor)) {
cursor = Cursor.getDefaultCursor();
@@ -895,7 +908,7 @@ public class WLComponentPeer implements ComponentPeer {
}
AWTAccessor.getCursorAccessor().setPData(cursor, scale, pData);
}
nativeSetCursor(pData, scale);
nativeSetCursor(pData, scale, serial);
});
}
@@ -1020,7 +1033,16 @@ public class WLComponentPeer implements ComponentPeer {
}
final void activate() {
performLocked(() -> nativeActivate(nativePtr));
// "The serial can come from an input or focus event."
long serial = WLToolkit.getInputState().keyboardEnterSerial();
long surface = WLToolkit.getInputState().surfaceForKeyboardInput();
if (serial != 0) {
performLocked(() -> nativeActivate(serial, nativePtr, surface));
} else {
if (log.isLoggable(Level.WARNING)) {
log.warning("activate() aborted due to missing keyboard enter event serial");
}
}
}
private static native void initIDs();
@@ -1043,8 +1065,8 @@ public class WLComponentPeer implements ComponentPeer {
protected native void nativeDisposeFrame(long ptr);
private native long getWLSurface(long ptr);
private native void nativeStartDrag(long ptr);
private native void nativeStartResize(long ptr, int edges);
private native void nativeStartDrag(long serial, long ptr);
private native void nativeStartResize(long serial, long ptr, int edges);
private native void nativeSetTitle(long ptr, String title);
private native void nativeRequestMinimized(long ptr);
@@ -1058,11 +1080,11 @@ public class WLComponentPeer implements ComponentPeer {
private native void nativeSetWindowGeometry(long ptr, int x, int y, int width, int height);
private native void nativeSetMinimumSize(long ptr, int width, int height);
private native void nativeSetMaximumSize(long ptr, int width, int height);
private static native void nativeSetCursor(long pData, int scale);
private static native void nativeSetCursor(long pData, int scale, long pointerEnterSerial);
private static native long nativeGetPredefinedCursor(String name, int scale);
private static native long nativeDestroyPredefinedCursor(long pData);
private native void nativeShowWindowMenu(long ptr, int x, int y);
private native void nativeActivate(long ptr);
private native void nativeShowWindowMenu(long serial, long ptr, int x, int y);
private native void nativeActivate(long serial, long ptr, long activatingSurfacePtr);
static long getNativePtrFor(Component component) {
final ComponentAccessor acc = AWTAccessor.getComponentAccessor();
@@ -1154,19 +1176,128 @@ public class WLComponentPeer implements ComponentPeer {
}
}
if (e.hasAxisEvent() && e.getIsAxis0Valid()) {
final MouseEvent mouseEvent = new MouseWheelEvent(getTarget(),
MouseEvent.MOUSE_WHEEL,
timestamp,
newInputState.getModifiers(),
x, y,
xAbsolute, yAbsolute,
1,
isPopupTrigger,
MouseWheelEvent.WHEEL_UNIT_SCROLL,
1,
Integer.signum(e.getAxis0Value()) * WHEEL_SCROLL_AMOUNT);
postMouseEvent(mouseEvent);
if (e.hasAxisEvent()) {
convertPointerEventToMWEParameters(e, xAxisWheelRoundRotationsAccumulator, yAxisWheelRoundRotationsAccumulator, mweConversionInfo);
if (log.isLoggable(PlatformLogger.Level.FINE)) {
log.fine("{0} -> {1}", e, mweConversionInfo);
}
// macOS's and Windows' AWT implement the following logic, so do we:
// Shift + a vertical scroll means a horizontal scroll.
// AWT/Swing components are also aware of it.
final boolean isShiftPressed = (newInputState.getModifiers() & KeyEvent.SHIFT_DOWN_MASK) != 0;
// These values decide whether a horizontal scrolling MouseWheelEvent will be created and posted
final int horizontalMWEScrollAmount;
final double horizontalMWEPreciseRotations;
final int horizontalMWERoundRotations;
// These values decide whether a vertical scrolling MouseWheelEvent will be created and posted
final int verticalMWEScrollAmount;
final double verticalMWEPreciseRotations;
final int verticalMWERoundRotations;
if (isShiftPressed) {
// Pressing Shift makes only a horizontal scrolling MouseWheelEvent possible
verticalMWEScrollAmount = 0;
verticalMWEPreciseRotations = 0;
verticalMWERoundRotations = 0;
// Now we're deciding values of which axis will be used to generate a horizontal MouseWheelEvent
if (mweConversionInfo.xAxisDirectionSign == mweConversionInfo.yAxisDirectionSign) {
// The scrolling directions don't contradict each other.
// Let's pick the more influencing axis.
final var xAxisUnitsToScroll = mweConversionInfo.xAxisMWEScrollAmount * (
Math.abs(mweConversionInfo.xAxisMWEPreciseRotations) > Math.abs(mweConversionInfo.xAxisMWERoundRotations)
? mweConversionInfo.xAxisMWEPreciseRotations
: mweConversionInfo.xAxisMWERoundRotations );
final var yAxisUnitsToScroll = mweConversionInfo.yAxisMWEScrollAmount * (
Math.abs(mweConversionInfo.yAxisMWEPreciseRotations) > Math.abs(mweConversionInfo.yAxisMWERoundRotations)
? mweConversionInfo.yAxisMWEPreciseRotations
: mweConversionInfo.yAxisMWERoundRotations );
if (xAxisUnitsToScroll > yAxisUnitsToScroll) {
horizontalMWEScrollAmount = mweConversionInfo.xAxisMWEScrollAmount;
horizontalMWEPreciseRotations = mweConversionInfo.xAxisMWEPreciseRotations;
horizontalMWERoundRotations = mweConversionInfo.xAxisMWERoundRotations;
} else {
horizontalMWEScrollAmount = mweConversionInfo.yAxisMWEScrollAmount;
horizontalMWEPreciseRotations = mweConversionInfo.yAxisMWEPreciseRotations;
horizontalMWERoundRotations = mweConversionInfo.yAxisMWERoundRotations;
}
} else if (mweConversionInfo.yAxisMWERoundRotations != 0 || mweConversionInfo.yAxisMWEPreciseRotations != 0) {
// The scrolling directions contradict.
// I think consistently choosing the Y axis values (unless they're zero) provides the most expected UI behavior here.
horizontalMWEScrollAmount = mweConversionInfo.yAxisMWEScrollAmount;
horizontalMWEPreciseRotations = mweConversionInfo.yAxisMWEPreciseRotations;
horizontalMWERoundRotations = mweConversionInfo.yAxisMWERoundRotations;
} else {
horizontalMWEScrollAmount = mweConversionInfo.xAxisMWEScrollAmount;
horizontalMWEPreciseRotations = mweConversionInfo.xAxisMWEPreciseRotations;
horizontalMWERoundRotations = mweConversionInfo.xAxisMWERoundRotations;
}
} else {
// Shift is not pressed, so both horizontal and vertical MouseWheelEvent s are possible.
horizontalMWEScrollAmount = mweConversionInfo.xAxisMWEScrollAmount;
horizontalMWEPreciseRotations = mweConversionInfo.xAxisMWEPreciseRotations;
horizontalMWERoundRotations = mweConversionInfo.xAxisMWERoundRotations;
verticalMWEScrollAmount = mweConversionInfo.yAxisMWEScrollAmount;
verticalMWEPreciseRotations = mweConversionInfo.yAxisMWEPreciseRotations;
verticalMWERoundRotations = mweConversionInfo.yAxisMWERoundRotations;
}
if (e.xAxisHasStopEvent()) {
xAxisWheelRoundRotationsAccumulator.reset();
}
if (e.yAxisHasStopEvent()) {
yAxisWheelRoundRotationsAccumulator.reset();
}
if (verticalMWERoundRotations != 0 || verticalMWEPreciseRotations != 0) {
assert(verticalMWEScrollAmount > 0);
final MouseEvent mouseEvent = new MouseWheelEvent(getTarget(),
MouseEvent.MOUSE_WHEEL,
timestamp,
// Making sure the event will cause scrolling along the vertical axis
newInputState.getModifiers() & ~KeyEvent.SHIFT_DOWN_MASK,
x, y,
xAbsolute, yAbsolute,
1,
isPopupTrigger,
MouseWheelEvent.WHEEL_UNIT_SCROLL,
verticalMWEScrollAmount,
verticalMWERoundRotations,
verticalMWEPreciseRotations);
postMouseEvent(mouseEvent);
}
if (horizontalMWERoundRotations != 0 || horizontalMWEPreciseRotations != 0) {
assert(horizontalMWEScrollAmount > 0);
final MouseEvent mouseEvent = new MouseWheelEvent(getTarget(),
MouseEvent.MOUSE_WHEEL,
timestamp,
// Making sure the event will cause scrolling along the horizontal axis
newInputState.getModifiers() | KeyEvent.SHIFT_DOWN_MASK,
x, y,
xAbsolute, yAbsolute,
1,
isPopupTrigger,
MouseWheelEvent.WHEEL_UNIT_SCROLL,
horizontalMWEScrollAmount,
horizontalMWERoundRotations,
horizontalMWEPreciseRotations);
postMouseEvent(mouseEvent);
}
}
if (e.hasMotionEvent()) {
@@ -1197,12 +1328,182 @@ public class WLComponentPeer implements ComponentPeer {
}
}
void startDrag() {
performLocked(() -> nativeStartDrag(nativePtr));
/**
* Accumulates fractional parts of wheel rotation steps until their absolute sum represents one or more full step(s).
* This allows implementing smoother scrolling, e.g., the sequence of wl_pointer::axis events with values
* [0.2, 0.1, 0.4, 0.4] can be accumulated into 1.1=0.2+0.1+0.4+0.4, making it possible to
* generate a MouseWheelEvent with wheelRotation=1
* (instead of 4 tries to generate a MouseWheelEvent with wheelRotation=0 due to double->int conversion)
*/
private static final class MouseWheelRoundRotationsAccumulator {
/**
* This method is intended to accumulate fractional numbers of wheel rotations.
*
* @param fractionalRotations - fractional number of wheel rotations (usually got from a {@code wl_pointer::axis} event)
* @return The number of wheel round rotations accumulated
* @see #accumulateSteps120Rotations
*/
public int accumulateFractionalRotations(double fractionalRotations) {
// The code assumes that the target component ({@link WLComponentPeer#target}) never changes.
// If it did, all the accumulating fields would have to be reset each time the target changed.
accumulatedFractionalRotations += fractionalRotations;
final int result = (int)accumulatedFractionalRotations;
accumulatedFractionalRotations -= result;
return result;
}
/**
* This method is intended to accumulate 1/120 fractions of a rotation step.
*
* @param steps120Rotations - a number of 1/120 parts of a wheel step (so that, e.g.,
* 30 means one quarter of a step,
* 240 means two steps,
* -240 means two steps in the negative direction,
* 540 means 4.5 steps).
* Usually got from a {@code wl_pointer::axis_discrete}/{@code axis_value120} event.
* @return The number of wheel round rotations accumulated
* @see #accumulateFractionalRotations
*/
public int accumulateSteps120Rotations(int steps120Rotations) {
// The code assumes that the target component ({@link WLComponentPeer#target}) never changes.
// If it did, all the accumulating fields would have to be reset each time the target changed.
accumulatedSteps120Rotations += steps120Rotations;
final int result = accumulatedSteps120Rotations / 120;
accumulatedSteps120Rotations %= 120;
return result;
}
public void reset() {
accumulatedFractionalRotations = 0;
accumulatedSteps120Rotations = 0;
}
private double accumulatedFractionalRotations = 0;
private int accumulatedSteps120Rotations = 0;
}
private final MouseWheelRoundRotationsAccumulator xAxisWheelRoundRotationsAccumulator = new MouseWheelRoundRotationsAccumulator();
private final MouseWheelRoundRotationsAccumulator yAxisWheelRoundRotationsAccumulator = new MouseWheelRoundRotationsAccumulator();
private static final class PointerEventToMWEConversionInfo {
public double xAxisVector = 0;
public int xAxisSteps120 = 0;
public int xAxisDirectionSign = 0;
public double xAxisMWEPreciseRotations = 0;
public int xAxisMWERoundRotations = 0;
public int xAxisMWEScrollAmount = 0;
public double yAxisVector = 0;
public int yAxisSteps120 = 0;
public int yAxisDirectionSign = 0;
public double yAxisMWEPreciseRotations = 0;
public int yAxisMWERoundRotations = 0;
public int yAxisMWEScrollAmount = 0;
private final StringBuilder toStringBuf = new StringBuilder(1024);
@Override
public String toString() {
toStringBuf.setLength(0);
toStringBuf.append("PointerEventToMWEConversionInfo[")
.append("xAxisVector=" ).append(xAxisVector ).append(", ")
.append("xAxisSteps120=" ).append(xAxisSteps120 ).append(", ")
.append("xAxisDirectionSign=" ).append(xAxisDirectionSign ).append(", ")
.append("xAxisMWEPreciseRotations=").append(xAxisMWEPreciseRotations).append(", ")
.append("xAxisMWERoundRotations=" ).append(xAxisMWERoundRotations ).append(", ")
.append("xAxisMWEScrollAmount=" ).append(xAxisMWEScrollAmount ).append(", ")
.append("yAxisVector=" ).append(yAxisVector ).append(", ")
.append("yAxisSteps120=" ).append(yAxisSteps120 ).append(", ")
.append("yAxisDirectionSign=" ).append(yAxisDirectionSign ).append(", ")
.append("yAxisMWEPreciseRotations=").append(yAxisMWEPreciseRotations).append(", ")
.append("yAxisMWERoundRotations=" ).append(yAxisMWERoundRotations ).append(", ")
.append("yAxisMWEScrollAmount=" ).append(yAxisMWEScrollAmount )
.append(']');
return toStringBuf.toString();
}
}
private final PointerEventToMWEConversionInfo mweConversionInfo = new PointerEventToMWEConversionInfo();
private static void convertPointerEventToMWEParameters(
WLPointerEvent dispatchingEvent,
MouseWheelRoundRotationsAccumulator xAxisWheelRoundRotationsAccumulator,
MouseWheelRoundRotationsAccumulator yAxisWheelRoundRotationsAccumulator,
PointerEventToMWEConversionInfo mweConversionInfo) {
// WLPointerEvent -> MouseWheelEvent conversion constants.
// Please keep in mind that they're all related, so that changing one may require adjusting the others
// (or altering this conversion routine).
// XToolkit uses 3 units per a wheel step, so do we here to preserve the user experience
final int STEPS120_MWE_SCROLL_AMOUNT = 3;
// For touchpad scrolling, it's worth being able to scroll the minimum possible number of units (i.e. 1)
final int VECTOR_MWE_SCROLL_AMOUNT = 1;
// 0.28 has experimentally been found as providing a good balance between
// wheel scrolling sensitivity and touchpad scrolling sensitivity
final double VECTOR_LENGTH_TO_MWE_ROTATIONS_FACTOR = 0.28;
mweConversionInfo.xAxisVector = dispatchingEvent.xAxisHasVectorValue() ? dispatchingEvent.getXAxisVectorValue() : 0;
mweConversionInfo.xAxisSteps120 = dispatchingEvent.xAxisHasSteps120Value() ? dispatchingEvent.getXAxisSteps120Value() : 0;
// Converting the X axis Wayland values to MouseWheelEvent parameters.
// wl_pointer::axis_discrete/axis_value120 are preferred over wl_pointer::axis because
// they're closer to MouseWheelEvent by their nature.
if (mweConversionInfo.xAxisSteps120 != 0) {
mweConversionInfo.xAxisDirectionSign = Integer.signum(mweConversionInfo.xAxisSteps120);
mweConversionInfo.xAxisMWEPreciseRotations = mweConversionInfo.xAxisSteps120 / 120d;
mweConversionInfo.xAxisMWERoundRotations = xAxisWheelRoundRotationsAccumulator.accumulateSteps120Rotations(mweConversionInfo.xAxisSteps120);
// It would be probably better to calculate the scrollAmount taking the xAxisVector value into
// consideration, so that the wheel scrolling speed could be adjusted via some system settings.
// However, neither Gnome nor KDE currently provide such a setting, making it difficult to test
// how well such an approach would work. So leaving it as is for now.
mweConversionInfo.xAxisMWEScrollAmount = STEPS120_MWE_SCROLL_AMOUNT;
} else {
mweConversionInfo.xAxisDirectionSign = (int)Math.signum(mweConversionInfo.xAxisVector);
mweConversionInfo.xAxisMWEPreciseRotations = mweConversionInfo.xAxisVector * VECTOR_LENGTH_TO_MWE_ROTATIONS_FACTOR;
mweConversionInfo.xAxisMWERoundRotations = xAxisWheelRoundRotationsAccumulator.accumulateFractionalRotations(mweConversionInfo.xAxisMWEPreciseRotations);
mweConversionInfo.xAxisMWEScrollAmount = VECTOR_MWE_SCROLL_AMOUNT;
}
mweConversionInfo.yAxisVector = dispatchingEvent.yAxisHasVectorValue() ? dispatchingEvent.getYAxisVectorValue() : 0;
mweConversionInfo.yAxisSteps120 = dispatchingEvent.yAxisHasSteps120Value() ? dispatchingEvent.getYAxisSteps120Value() : 0;
// Converting the Y axis Wayland values to MouseWheelEvent parameters.
// (Currently, the routine is exactly like for X axis)
if (mweConversionInfo.yAxisSteps120 != 0) {
mweConversionInfo.yAxisDirectionSign = Integer.signum(mweConversionInfo.yAxisSteps120);
mweConversionInfo.yAxisMWEPreciseRotations = mweConversionInfo.yAxisSteps120 / 120d;
mweConversionInfo.yAxisMWERoundRotations = yAxisWheelRoundRotationsAccumulator.accumulateSteps120Rotations(mweConversionInfo.yAxisSteps120);
mweConversionInfo.yAxisMWEScrollAmount = STEPS120_MWE_SCROLL_AMOUNT;
} else {
mweConversionInfo.yAxisDirectionSign = (int)Math.signum(mweConversionInfo.yAxisVector);
mweConversionInfo.yAxisMWEPreciseRotations = mweConversionInfo.yAxisVector * VECTOR_LENGTH_TO_MWE_ROTATIONS_FACTOR;
mweConversionInfo.yAxisMWERoundRotations = yAxisWheelRoundRotationsAccumulator.accumulateFractionalRotations(mweConversionInfo.yAxisMWEPreciseRotations);
mweConversionInfo.yAxisMWEScrollAmount = VECTOR_MWE_SCROLL_AMOUNT;
}
}
void startResize(int edges) {
performLocked(() -> nativeStartResize(nativePtr, edges));
void startDrag(long serial) {
// "This request must be used in response to some sort of user action like a button press,
// key press, or touch down event. The passed serial is used to determine the type
// of interactive move (touch, pointer, etc)."
assert serial != 0;
performLocked(() -> nativeStartDrag(serial, nativePtr));
}
void startResize(long serial, int edges) {
// "This request must be used in response to some sort of user action like a button press,
// key press, or touch down event. The passed serial is used to determine the type
// of interactive resize (touch, pointer, etc)."
assert serial != 0;
performLocked(() -> nativeStartResize(serial, nativePtr, edges));
}
/**

View File

@@ -334,7 +334,7 @@ public class WLFrameDecoration {
if (isLMBPressed && peer.isInteractivelyResizable()) {
int resizeSide = getResizeEdges(point.x, point.y);
if (resizeSide != 0) {
peer.startResize(resizeSide);
peer.startResize(WLToolkit.getInputState().pointerButtonSerial(), resizeSide);
// workaround for https://gitlab.gnome.org/GNOME/mutter/-/issues/2523
WLToolkit.resetPointerInputState();
return true;
@@ -344,7 +344,7 @@ public class WLFrameDecoration {
if (isUndecorated) return false;
if (isRMBPressed && getBounds().contains(e.getX(), e.getY())) {
peer.showWindowMenu(e.getX(), e.getY());
peer.showWindowMenu(WLToolkit.getInputState().pointerButtonSerial(), e.getX(), e.getY());
return true;
}
@@ -371,7 +371,8 @@ public class WLFrameDecoration {
if (e.getID() == MouseEvent.MOUSE_PRESSED) {
pressedLocation = point;
} else if (e.getID() == MouseEvent.MOUSE_DRAGGED && pressedInDragStartArea() && isSignificantDrag(point)) {
peer.startDrag();
peer.startDrag(WLToolkit.getInputState().pointerButtonSerial());
} else if (e.getID() == MouseEvent.MOUSE_CLICKED && e.getClickCount() == 2 && pressedInDragStartArea()
&& peer.isFrameStateSupported(Frame.MAXIMIZED_BOTH)) {
toggleMaximizedState();

View File

@@ -31,16 +31,22 @@ package sun.awt.wl;
* the information of certain events from the past like keyboard modifiers keys getting
* pressed. WLInputState maintains this information.
*
* @param eventWithSurface null or the latest PointerEvent such that hasSurface() == true
* @param eventWithSerial null or the latest PointerEvent such that hasSerial() == true
* @param eventWithTimestamp null or the latest PointerEvent such that hasTimestamp() == true
* @param eventWithCoordinates null or the latest PointerEvent such that hasCoordinates() == true
* @param eventWithSurface null or the latest WLPointerEvent such that hasSurface() == true
* @param pointerEnterSerial zero or the serial of the latest wl_pointer::enter event
* @param pointerButtonSerial zero or the serial of the latest wl_pointer::button event
* @param keyboardEnterSerial zero or the serial of the latest wl_keyboard::enter event
* @param keySerial zero or the serial of the latest wl_keyboard::key event
* @param eventWithTimestamp null or the latest WLPointerEvent such that hasTimestamp() == true
* @param eventWithCoordinates null or the latest WLPointerEvent such that hasCoordinates() == true
* @param pointerButtonPressedEvent null or the latest PointerButtonEvent such that getIsButtonPressed() == true
* @param modifiers a bit set of modifiers reflecting currently pressed keys (@see WLInputState.getNewModifiers())
* @param surfaceForKeyboardInput represents 'struct wl_surface*' that keyboards events should go to
*/
record WLInputState(WLPointerEvent eventWithSurface,
WLPointerEvent eventWithSerial,
long pointerEnterSerial,
long pointerButtonSerial,
long keyboardEnterSerial,
long keySerial,
WLPointerEvent eventWithTimestamp,
WLPointerEvent eventWithCoordinates,
PointerButtonEvent pointerButtonPressedEvent,
@@ -50,7 +56,6 @@ record WLInputState(WLPointerEvent eventWithSurface,
/**
* Groups together information about a mouse pointer button event.
* @param surface 'struct wl_surface*' the button was pressed over
* @param serial serial number of the event as received from Wayland
* @param timestamp time of the event as received from Wayland
* @param clickCount number of consecutive clicks of the same button performed
* within WLToolkit.getMulticlickTime() milliseconds from one another
@@ -60,7 +65,6 @@ record WLInputState(WLPointerEvent eventWithSurface,
*/
record PointerButtonEvent(
long surface,
long serial,
long timestamp,
int clickCount,
int linuxCode,
@@ -68,25 +72,26 @@ record WLInputState(WLPointerEvent eventWithSurface,
int surfaceY) {}
static WLInputState initialState() {
return new WLInputState(null, null, null, null,
return new WLInputState(null, 0, 0, 0, 0, null, null,
null, 0, 0, false);
}
/**
* Creates new state based on the existing one and the supplied WLPointerEvent.
* Creates a new state based on the existing one and the supplied WLPointerEvent.
*/
WLInputState update(WLPointerEvent pointerEvent) {
WLInputState updatedFromPointerEvent(WLPointerEvent pointerEvent) {
final WLPointerEvent newEventWithSurface = pointerEvent.hasSurface()
? pointerEvent : eventWithSurface;
final WLPointerEvent newEventWithSerial = pointerEvent.hasSerial()
? pointerEvent : eventWithSerial;
final long newPointerEnterSerial = pointerEvent.hasEnterEvent()
? pointerEvent.getSerial() : pointerEnterSerial;
final long newPointerButtonSerial = pointerEvent.hasButtonEvent()
? pointerEvent.getSerial() : pointerButtonSerial;
final WLPointerEvent newEventWithTimestamp = pointerEvent.hasTimestamp()
? pointerEvent : eventWithTimestamp;
final WLPointerEvent newEventWithCoordinates = pointerEvent.hasCoordinates()
? pointerEvent : eventWithCoordinates;
final PointerButtonEvent newPointerButtonEvent = getNewPointerButtonEvent(pointerEvent,
newEventWithSurface,
newEventWithSerial,
newEventWithTimestamp,
newEventWithCoordinates);
final int newModifiers = getNewModifiers(pointerEvent);
@@ -96,7 +101,10 @@ record WLInputState(WLPointerEvent eventWithSurface,
return new WLInputState(
newEventWithSurface,
newEventWithSerial,
newPointerEnterSerial,
newPointerButtonSerial,
keyboardEnterSerial,
keySerial,
newEventWithTimestamp,
newEventWithCoordinates,
newPointerButtonEvent,
@@ -105,11 +113,29 @@ record WLInputState(WLPointerEvent eventWithSurface,
newPointerOverSurface);
}
public WLInputState updatedFromKeyEvent(long serial) {
return new WLInputState(
eventWithSurface,
pointerEnterSerial,
pointerButtonSerial,
keyboardEnterSerial,
serial,
eventWithTimestamp,
eventWithCoordinates,
pointerButtonPressedEvent,
modifiers,
surfaceForKeyboardInput,
isPointerOverSurface);
}
public WLInputState updatedFromKeyboardEnterEvent(long serial, long surfacePtr) {
// "The compositor must send the wl_keyboard.modifiers event after this event".
return new WLInputState(
eventWithSurface,
eventWithSerial,
pointerEnterSerial,
pointerButtonSerial,
serial,
0,
eventWithTimestamp,
eventWithCoordinates,
pointerButtonPressedEvent,
@@ -119,12 +145,15 @@ record WLInputState(WLPointerEvent eventWithSurface,
}
public WLInputState updatedFromKeyboardModifiersEvent(long serial, int keyboardModifiers) {
// "The compositor must send the wl_keyboard.modifiers event after this event".
// NB: there seem to be no use for the serial number of this kind of event so far
final int oldPointerModifiers = modifiers & WLPointerEvent.PointerButtonCodes.combinedMask();
final int newModifiers = oldPointerModifiers | keyboardModifiers;
return new WLInputState(
eventWithSurface,
eventWithSerial,
pointerEnterSerial,
pointerButtonSerial,
keyboardEnterSerial,
keySerial,
eventWithTimestamp,
eventWithCoordinates,
pointerButtonPressedEvent,
@@ -134,12 +163,21 @@ record WLInputState(WLPointerEvent eventWithSurface,
}
public WLInputState updatedFromKeyboardLeaveEvent(long serial, long surfacePtr) {
// NB: there seem to be no use for the serial number of this kind of event so far
// "After this event client must assume that all keys, including modifiers,
// are lifted and also it must stop key repeating if there's some going on".
// We learned from experience that some serials become invalid after focus lost, so they
// are zeroed-out to prevent the use of a stale serial.
// Note: Wayland doesn't report failure when a stale serial is passed.
final int newModifiers = modifiers & WLPointerEvent.PointerButtonCodes.combinedMask();
return new WLInputState(
eventWithSurface,
eventWithSerial,
pointerEnterSerial,
0,
0,
0,
eventWithTimestamp,
eventWithCoordinates,
pointerButtonPressedEvent,
@@ -151,7 +189,10 @@ record WLInputState(WLPointerEvent eventWithSurface,
public WLInputState resetPointerState() {
return new WLInputState(
eventWithSurface,
eventWithSerial,
pointerEnterSerial,
pointerButtonSerial,
keyboardEnterSerial,
keySerial,
eventWithTimestamp,
eventWithCoordinates,
pointerButtonPressedEvent,
@@ -162,11 +203,10 @@ record WLInputState(WLPointerEvent eventWithSurface,
private PointerButtonEvent getNewPointerButtonEvent(WLPointerEvent pointerEvent,
WLPointerEvent newEventWithSurface,
WLPointerEvent newEventWithSerial,
WLPointerEvent newEventWithTimestamp,
WLPointerEvent newEventWithPosition) {
if (pointerEvent.hasButtonEvent() && pointerEvent.getIsButtonPressed() && newEventWithSurface != null) {
assert newEventWithSerial != null && newEventWithTimestamp != null && newEventWithPosition != null;
assert newEventWithTimestamp != null && newEventWithPosition != null;
int clickCount = 1;
final boolean pressedSameButton = pointerButtonPressedEvent != null
&& pointerEvent.getButtonCode() == pointerButtonPressedEvent.linuxCode;
@@ -186,7 +226,6 @@ record WLInputState(WLPointerEvent eventWithSurface,
return new PointerButtonEvent(
newEventWithSurface.getSurface(),
newEventWithSerial.getSerial(),
newEventWithTimestamp.getTimestamp(),
clickCount,
pointerEvent.getButtonCode(),
@@ -226,16 +265,12 @@ record WLInputState(WLPointerEvent eventWithSurface,
return WLPointerEvent.PointerButtonCodes.anyMatchMask(modifiers);
}
public long getSurfaceForKeyboardInput() {
return surfaceForKeyboardInput;
}
public int getPointerX() {
int x = eventWithCoordinates != null ? eventWithCoordinates.getSurfaceX() : 0;
if (!WLGraphicsEnvironment.isDebugScaleEnabled()) {
return x;
} else {
WLComponentPeer peer = getPeer();
WLComponentPeer peer = peerForPointerEvents();
return peer == null ? x : peer.surfaceUnitsToJavaUnits(x);
}
}
@@ -245,12 +280,18 @@ record WLInputState(WLPointerEvent eventWithSurface,
if (!WLGraphicsEnvironment.isDebugScaleEnabled()) {
return y;
} else {
WLComponentPeer peer = getPeer();
WLComponentPeer peer = peerForPointerEvents();
return peer == null ? y : peer.surfaceUnitsToJavaUnits(y);
}
}
public WLComponentPeer getPeer() {
/**
* Which peer mouse pointer events should be delivered to, if any.
*
* @return WLComponentPeer instance corresponding to the Wayland surface that
* received pointer-related events the last
*/
public WLComponentPeer peerForPointerEvents() {
return eventWithSurface != null
? WLToolkit.componentPeerFromSurface(eventWithSurface.getSurface())
: null;
@@ -263,7 +304,7 @@ record WLInputState(WLPointerEvent eventWithSurface,
if (isPointerOverSurface && eventWithCoordinates != null) {
int x = getPointerX();
int y = getPointerY();
WLComponentPeer peer = getPeer();
WLComponentPeer peer = peerForPointerEvents();
if (peer != null) {
return x >= 0
&& x < peer.getWidth()

View File

@@ -38,36 +38,68 @@ class WLKeyboard {
}
private class KeyRepeatManager {
// Methods and fields should only be accessed from the EDT
private final Timer timer = new Timer("WLKeyboard.KeyRepeatManager", true);
private TimerTask currentRepeatTask;
private int delayBeforeRepeatMillis = 500;
private int delayBetweenRepeatMillis = 50;
private int currentRepeatKeycode;
private int delayBeforeRepeatMillis;
private int delayBetweenRepeatMillis;
// called from native code
void setRepeatInfo(int charsPerSecond, int delayMillis) {
// this function receives (0, 0) when key repeat is disabled
assert EventQueue.isDispatchThread();
this.delayBeforeRepeatMillis = delayMillis;
this.delayBetweenRepeatMillis = (int) (1000.0 / charsPerSecond);
if (charsPerSecond > 0) {
this.delayBetweenRepeatMillis = (int) (1000.0 / charsPerSecond);
} else {
this.delayBetweenRepeatMillis = 0;
}
cancelRepeat();
}
boolean isRepeatEnabled() {
return this.delayBeforeRepeatMillis > 0 || this.delayBetweenRepeatMillis > 0;
}
void cancelRepeat() {
assert EventQueue.isDispatchThread();
if (currentRepeatTask != null) {
currentRepeatTask.cancel();
currentRepeatTask = null;
currentRepeatKeycode = 0;
}
}
void startRepeat(long timestamp, int keycode) {
// called from native code
void stopRepeat(int keycode) {
assert EventQueue.isDispatchThread();
if (currentRepeatKeycode == keycode) {
cancelRepeat();
}
}
// called from native code
void startRepeat(long serial, long timestamp, int keycode) {
assert EventQueue.isDispatchThread();
cancelRepeat();
if (keycode == 0) {
if (keycode == 0 || !isRepeatEnabled()) {
return;
}
currentRepeatKeycode = keycode;
long delta = timestamp - System.currentTimeMillis();
currentRepeatTask = new TimerTask() {
@Override
public void run() {
try {
EventQueue.invokeAndWait(() -> {
handleKeyPress(delta + System.currentTimeMillis(), keycode, true);
if (this == currentRepeatTask) {
handleKeyRepeat(serial, delta + System.currentTimeMillis(), keycode);
}
});
} catch (InterruptedException ignored) {
} catch (InvocationTargetException e) {
@@ -125,11 +157,12 @@ class WLKeyboard {
}
public void onLostFocus() {
assert EventQueue.isDispatchThread();
keyRepeatManager.cancelRepeat();
cancelCompose();
}
private native void handleKeyPress(long timestamp, int keycode, boolean isRepeat);
private native void handleKeyRepeat(long serial, long timestamp, int keycode);
private native void cancelCompose();

View File

@@ -41,7 +41,7 @@ public class WLMouseInfoPeer implements MouseInfoPeer {
@Override
public boolean isWindowUnderMouse(Window w) {
WLInputState inputState = WLToolkit.getInputState();
WLComponentPeer peerUnderMouse = inputState.getPeer();
WLComponentPeer peerUnderMouse = inputState.peerForPointerEvents();
return peerUnderMouse != null
&& peerUnderMouse.getTarget() == w
&& inputState.isPointerOverPeer();

View File

@@ -1,6 +1,6 @@
/*
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2022, JetBrains s.r.o.. All rights reserved.
* Copyright (c) 2022-2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2022-2024, 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
@@ -46,7 +46,6 @@ class WLPointerEvent {
private boolean has_leave_event;
private boolean has_motion_event;
private boolean has_button_event;
private boolean has_axis_event;
private long surface; /// 'struct wl_surface *' this event appertains to
private long serial;
@@ -58,8 +57,19 @@ class WLPointerEvent {
private int buttonCode; // pointer button code corresponding to PointerButtonCodes.linuxCode
private boolean isButtonPressed; // true if button was pressed, false if released
private boolean axis_0_valid; // is vertical scroll included in this event?
private int axis_0_value; // "length of vector in surface-local coordinate space" (source: wayland.xml)
private boolean xAxis_hasVectorValue; // whether xAxis_vectorValue is valid
private boolean xAxis_hasStopEvent; // whether wl_pointer::axis_stop event has been received for this axis
private boolean xAxis_hasSteps120Value; // whether xAxis_steps120Value is valid
private double xAxis_vectorValue; // "length of vector in surface-local coordinate space" (source: wayland.xml)
private int xAxis_steps120Value; // "high-resolution wheel scroll information, with each multiple of 120
// representing one logical scroll step (a wheel detent)" (source: wayland.xml)
private boolean yAxis_hasVectorValue; // whether yAxis_vectorValue is valid
private boolean yAxis_hasStopEvent; // whether wl_pointer::axis_stop event has been received for this axis
private boolean yAxis_hasSteps120Value; // whether yAxis_steps120Value is valid
private double yAxis_vectorValue; // "length of vector in surface-local coordinate space" (source: wayland.xml)
private int yAxis_steps120Value; // "high-resolution wheel scroll information, with each multiple of 120
// representing one logical scroll step (a wheel detent)" (source: wayland.xml)
private WLPointerEvent() {}
@@ -175,7 +185,7 @@ class WLPointerEvent {
}
public boolean hasAxisEvent() {
return has_axis_event;
return xAxisHasEvents() || yAxisHasEvents();
}
/**
@@ -241,14 +251,60 @@ class WLPointerEvent {
return isButtonPressed;
}
public boolean getIsAxis0Valid() {
assert hasAxisEvent();
return axis_0_valid;
public boolean xAxisHasEvents() {
return xAxisHasVectorValue() ||
xAxisHasStopEvent() ||
xAxisHasSteps120Value();
}
public int getAxis0Value() {
assert hasAxisEvent();
return axis_0_value;
public boolean xAxisHasVectorValue() {
return xAxis_hasVectorValue;
}
public boolean xAxisHasStopEvent() {
return xAxis_hasStopEvent;
}
public boolean xAxisHasSteps120Value() {
return xAxis_hasSteps120Value;
}
public double getXAxisVectorValue() {
assert xAxisHasVectorValue();
return xAxis_vectorValue;
}
public int getXAxisSteps120Value() {
assert xAxisHasSteps120Value();
return xAxis_steps120Value;
}
public boolean yAxisHasEvents() {
return yAxisHasVectorValue() ||
yAxisHasStopEvent() ||
yAxisHasSteps120Value();
}
public boolean yAxisHasVectorValue() {
return yAxis_hasVectorValue;
}
public boolean yAxisHasStopEvent() {
return yAxis_hasStopEvent;
}
public boolean yAxisHasSteps120Value() {
return yAxis_hasSteps120Value;
}
public double getYAxisVectorValue() {
assert yAxisHasVectorValue();
return yAxis_vectorValue;
}
public int getYAxisSteps120Value() {
assert yAxisHasSteps120Value();
return yAxis_steps120Value;
}
@Override
@@ -283,9 +339,32 @@ class WLPointerEvent {
}
if (hasAxisEvent()) {
builder.append("axis");
if (axis_0_valid) {
builder.append("vertical-scroll: ").append(axis_0_value).append(" ");
builder.append(" axis");
if (yAxisHasEvents()) {
builder.append(" vertical-scroll:");
if (yAxisHasVectorValue()) {
builder.append(" ").append(getYAxisVectorValue());
}
if (yAxisHasSteps120Value()) {
builder.append(" ").append(getYAxisSteps120Value()).append("/120 steps");
}
if (yAxisHasStopEvent()) {
builder.append(" stop");
}
}
if (xAxisHasEvents()) {
builder.append(" horizontal-scroll:");
if (xAxisHasVectorValue()) {
builder.append(" ").append(getXAxisVectorValue());
}
if (xAxisHasSteps120Value()) {
builder.append(" ").append(getXAxisSteps120Value()).append("/120 steps");
}
if (xAxisHasStopEvent()) {
builder.append(" stop");
}
}
}

View File

@@ -298,9 +298,9 @@ public class WLToolkit extends UNIXToolkit implements Runnable {
if (log.isLoggable(PlatformLogger.Level.FINE)) log.fine("dispatchPointerEvent: " + e);
final WLInputState oldInputState = inputState;
final WLInputState newInputState = oldInputState.update(e);
final WLInputState newInputState = oldInputState.updatedFromPointerEvent(e);
inputState = newInputState;
final WLComponentPeer peer = newInputState.getPeer();
final WLComponentPeer peer = newInputState.peerForPointerEvents();
if (peer == null) {
// We don't know whom to notify of the event; may happen when
// the surface has recently disappeared, in which case
@@ -310,7 +310,8 @@ public class WLToolkit extends UNIXToolkit implements Runnable {
}
}
private static void dispatchKeyboardKeyEvent(long timestamp,
private static void dispatchKeyboardKeyEvent(long serial,
long timestamp,
int id,
int keyCode,
int keyLocation,
@@ -320,13 +321,15 @@ public class WLToolkit extends UNIXToolkit implements Runnable {
// Invoked from the native code
assert EventQueue.isDispatchThread();
inputState = inputState.updatedFromKeyEvent(serial);
if (timestamp == 0) {
// Happens when a surface was focused with keys already pressed.
// Fake the timestamp by peeking at the last known event.
timestamp = inputState.getTimestamp();
}
final long surfacePtr = inputState.getSurfaceForKeyboardInput();
final long surfacePtr = inputState.surfaceForKeyboardInput();
final WLComponentPeer peer = componentPeerFromSurface(surfacePtr);
if (peer != null) {
if (extendedKeyCode >= 0x1000000) {

View File

@@ -26,7 +26,11 @@ public class WLWindowMoveService implements WindowMoveService {
final AWTAccessor.ComponentAccessor acc = AWTAccessor.getComponentAccessor();
ComponentPeer peer = acc.getPeer(window);
if (peer instanceof WLComponentPeer wlPeer) {
wlPeer.startDrag();
long serial = WLToolkit.getInputState().pointerButtonSerial();
if (serial == 0) serial = WLToolkit.getInputState().keySerial();
if (serial != 0) {
wlPeer.startDrag(serial);
}
} else {
throw new IllegalArgumentException("AWT window must have WLComponentPeer as its peer");
}

View File

@@ -40,11 +40,14 @@ typedef void* data_source_t;
static jmethodID transferContentsWithTypeMID; // WLCipboard.transferContentsWithType()
static jmethodID handleClipboardFormatMID; // WLCipboard.handleClipboardFormat()
static jmethodID handleNewClipboardMID; // WLCipboard.handleNewClipboard()
static jmethodID handleOfferCancelledMID; // WLCipboard.handleOfferCancelled()
static jfieldID isPrimaryFID; // WLClipboard.isPrimary
typedef struct DataSourcePayload {
data_source_t source;
jobject clipboard; // a global reference to WLClipboard
jobject content; // a global reference to Transferable
jboolean isPrimary;
} DataSourcePayload;
static DataSourcePayload *
@@ -52,6 +55,7 @@ DataSourcePayload_Create(jobject clipboard, jobject content)
{
DataSourcePayload * payload = malloc(sizeof(struct DataSourcePayload));
if (payload) {
payload->source = NULL;
payload->clipboard = clipboard;
payload->content = content;
}
@@ -273,11 +277,25 @@ CleanupClipboard(DataSourcePayload *payload)
if (payload->clipboard != NULL) (*env)->DeleteGlobalRef(env, payload->clipboard);
if (payload->content != NULL) (*env)->DeleteGlobalRef(env, payload->content);
if (payload->source != NULL) {
if (payload->isPrimary) {
zwp_primary_selection_source_v1_destroy(payload->source);
} else {
wl_data_source_destroy(payload->source);
}
}
DataSourcePayload_Destroy(payload);
}
}
static void
OfferCancelled(DataSourcePayload *payload) {
JNIEnv *env = getEnv();
(*env)->CallVoidMethod(env, payload->clipboard, handleOfferCancelledMID, ptr_to_jlong(payload));
EXCEPTION_CLEAR(env);
CleanupClipboard(payload);
}
static void
wl_data_source_target(void *data,
struct wl_data_source *wl_data_source,
@@ -302,8 +320,9 @@ wl_data_source_handle_cancelled(
void *data,
struct wl_data_source *source)
{
CleanupClipboard(data);
wl_data_source_destroy(source);
JNU_RUNTIME_ASSERT(getEnv(), data != NULL && source == ((DataSourcePayload*)data)->source, "Unexpected data source cancelled");
OfferCancelled(data);
}
static const struct wl_data_source_listener wl_data_source_listener = {
@@ -330,8 +349,9 @@ zwp_selection_source_handle_cancelled(
void *data,
struct zwp_primary_selection_source_v1 *source)
{
CleanupClipboard(data);
zwp_primary_selection_source_v1_destroy(source);
JNU_RUNTIME_ASSERT(getEnv(), data != NULL && source == ((DataSourcePayload*)data)->source, "Unexpected selection source cancelled");
OfferCancelled(data);
}
static const struct zwp_primary_selection_source_v1_listener zwp_selection_source_listener = {
@@ -360,6 +380,12 @@ initJavaRefs(JNIEnv* env, jclass wlClipboardClass)
"(J)V",
JNI_FALSE);
GET_METHOD_RETURN(handleOfferCancelledMID,
wlClipboardClass,
"handleOfferCancelled",
"(J)V",
JNI_FALSE);
GET_FIELD_RETURN(isPrimaryFID,
wlClipboardClass,
"isPrimary",
@@ -538,6 +564,9 @@ offerData(
: (data_source_t)wl_data_device_manager_create_data_source(wl_ddm);
if (source != NULL) {
payload->source = source;
payload->isPrimary = isPrimary;
wl_proxy_set_queue((struct wl_proxy*)source, jlong_to_ptr(dataOfferQueuePtr));
if (isPrimary) {
@@ -587,7 +616,7 @@ offerData(
* Retains the reference to clipboard content for the further use when the actual
* clipboard data get requested.
*/
JNIEXPORT void JNICALL
JNIEXPORT jlong JNICALL
Java_sun_awt_wl_WLClipboard_offerData(
JNIEnv *env,
jobject obj,
@@ -597,46 +626,42 @@ Java_sun_awt_wl_WLClipboard_offerData(
jlong dataOfferQueuePtr)
{
jobject clipboardGlobalRef = (*env)->NewGlobalRef(env, obj); // deleted by ...source_handle_cancelled()
CHECK_NULL(clipboardGlobalRef);
CHECK_NULL_RETURN(clipboardGlobalRef, 0);
jobject contentGlobalRef = (*env)->NewGlobalRef(env, content); // deleted by ...source_handle_cancelled()
CHECK_NULL(contentGlobalRef);
CHECK_NULL_RETURN(contentGlobalRef, 0);
DataSourcePayload * payload = DataSourcePayload_Create(clipboardGlobalRef, contentGlobalRef);
if (payload == NULL) {
(*env)->DeleteGlobalRef(env, clipboardGlobalRef);
(*env)->DeleteGlobalRef(env, contentGlobalRef);
}
CHECK_NULL_THROW_OOME(env, payload, "failed to allocate memory for DataSourcePayload");
CHECK_NULL_THROW_OOME_RETURN(env, payload, "failed to allocate memory for DataSourcePayload", 0);
const jboolean isPrimary = isPrimarySelectionClipboard(env, obj);
if (!offerData(env,payload, isPrimary, eventSerial, mimeTypes, dataOfferQueuePtr)) {
if (!offerData(env, payload, isPrimary, eventSerial, mimeTypes, dataOfferQueuePtr)) {
// Failed to create a data source; give up and cleanup.
CleanupClipboard(payload);
}
return ptr_to_jlong(payload);
}
JNIEXPORT void JNICALL
Java_sun_awt_wl_WLClipboard_cancelOffer(
JNIEnv *env,
jobject obj,
jlong eventSerial)
jlong payloadNativePtr)
{
// This should automatically deliver the "cancelled" event where we clean up
// both the previous source and the global reference to the transferable object.
const jboolean isPrimary = isPrimarySelectionClipboard(env, obj);
if (isPrimary) {
zwp_primary_selection_device_v1_set_selection(zwp_selection_device, NULL, eventSerial);
} else {
wl_data_device_set_selection(wl_data_device, NULL, eventSerial);
}
wlFlushToServer(env);
JNU_RUNTIME_ASSERT(env, payloadNativePtr != 0, "NULL pointer to clipboard data source");
CleanupClipboard(jlong_to_ptr(payloadNativePtr));
}
/**
* Asks Wayland to provide the data for the clipboard in the given MIME
* format.
*
* Returns the file desriptor from which the data must be read or -1
* Returns the file descriptor from which the data must be read or -1
* in case of an error.
*
* NB: the returned file descriptor must be closed by the caller.
@@ -696,4 +721,4 @@ Java_sun_awt_wl_WLClipboard_destroyClipboard(
struct wl_data_offer * offer = jlong_to_ptr(clipboardNativePtr);
wl_data_offer_destroy(offer);
}
}
}

View File

@@ -585,9 +585,6 @@ DoHide(JNIEnv *env, struct WLFrame *frame)
} else {
xdg_popup_destroy(frame->xdg_popup);
}
if (wl_surface_in_focus == frame->wl_surface) {
wl_surface_in_focus = NULL;
}
if (frame->gtk_surface != NULL) {
gtk_surface1_destroy(frame->gtk_surface);
}
@@ -632,21 +629,21 @@ JNIEXPORT jlong JNICALL Java_sun_awt_wl_WLComponentPeer_getWLSurface
}
JNIEXPORT void JNICALL Java_sun_awt_wl_WLComponentPeer_nativeStartDrag
(JNIEnv *env, jobject obj, jlong ptr)
(JNIEnv *env, jobject obj, jlong serial, jlong ptr)
{
struct WLFrame *frame = jlong_to_ptr(ptr);
if (frame->toplevel && wl_seat) {
xdg_toplevel_move(frame->xdg_toplevel, wl_seat, last_mouse_pressed_serial);
xdg_toplevel_move(frame->xdg_toplevel, wl_seat, serial);
wlFlushToServer(env);
}
}
JNIEXPORT void JNICALL Java_sun_awt_wl_WLComponentPeer_nativeStartResize
(JNIEnv *env, jobject obj, jlong ptr, jint edges)
(JNIEnv *env, jobject obj, jlong serial, jlong ptr, jint edges)
{
struct WLFrame *frame = jlong_to_ptr(ptr);
if (frame->toplevel && wl_seat && frame->xdg_toplevel != NULL) {
xdg_toplevel_resize(frame->xdg_toplevel, wl_seat, last_mouse_pressed_serial, edges);
xdg_toplevel_resize(frame->xdg_toplevel, wl_seat, serial, edges);
wlFlushToServer(env);
}
}
@@ -716,27 +713,28 @@ JNIEXPORT void JNICALL Java_sun_awt_wl_WLComponentPeer_nativeSetMaximumSize
}
JNIEXPORT void JNICALL Java_sun_awt_wl_WLComponentPeer_nativeShowWindowMenu
(JNIEnv *env, jobject obj, jlong ptr, jint x, jint y)
(JNIEnv *env, jobject obj, jlong serial, jlong ptr, jint x, jint y)
{
struct WLFrame *frame = jlong_to_ptr(ptr);
if (frame->toplevel) {
xdg_toplevel_show_window_menu(frame->xdg_toplevel, wl_seat, last_mouse_pressed_serial, x, y);
xdg_toplevel_show_window_menu(frame->xdg_toplevel, wl_seat, serial, x, y);
wlFlushToServer(env);
}
}
JNIEXPORT void JNICALL
Java_sun_awt_wl_WLComponentPeer_nativeActivate
(JNIEnv *env, jobject obj, jlong ptr)
(JNIEnv *env, jobject obj, jlong serial, jlong ptr, jlong activatingSurfacePtr)
{
struct WLFrame *frame = jlong_to_ptr(ptr);
if (frame->wl_surface && xdg_activation_v1 && wl_seat) {
struct xdg_activation_token_v1 *token = xdg_activation_v1_get_activation_token(xdg_activation_v1);
CHECK_NULL(token);
xdg_activation_token_v1_add_listener(token, &xdg_activation_token_v1_listener, frame);
xdg_activation_token_v1_set_serial(token, last_input_or_focus_serial, wl_seat);
if (wl_surface_in_focus) {
xdg_activation_token_v1_set_surface(token, wl_surface_in_focus);
xdg_activation_token_v1_set_serial(token, serial, wl_seat);
if (activatingSurfacePtr) {
struct wl_surface* surface = jlong_to_ptr(activatingSurfacePtr);
xdg_activation_token_v1_set_surface(token, surface);
}
xdg_activation_token_v1_commit(token);
frame->activation_token_list = add_token(env, frame->activation_token_list, token);

View File

@@ -141,7 +141,7 @@ JNIEXPORT jlong JNICALL Java_sun_awt_wl_WLCustomCursor_nativeCreateCustomCursor
}
JNIEXPORT void JNICALL Java_sun_awt_wl_WLComponentPeer_nativeSetCursor
(JNIEnv *env, jclass cls, jlong pData, jint scale)
(JNIEnv *env, jclass cls, jlong pData, jint scale, jlong pointerEnterSerial)
{
struct wl_buffer *buffer = NULL;
int32_t width = 0;
@@ -161,29 +161,17 @@ JNIEXPORT void JNICALL Java_sun_awt_wl_WLComponentPeer_nativeSetCursor
}
static struct wl_surface *wl_cursor_surface = NULL;
static struct wl_buffer *last_buffer = NULL;
static uint32_t last_serial = 0;
static int32_t last_hotspot_x = 0;
static int32_t last_hotspot_y = 0;
if (!wl_cursor_surface)
wl_cursor_surface = wl_compositor_create_surface(wl_compositor);
CHECK_NULL(wl_cursor_surface);
if (buffer != last_buffer) {
last_buffer = buffer;
wl_surface_attach(wl_cursor_surface, buffer, 0, 0);
wl_surface_set_buffer_scale(wl_cursor_surface, scale);
wl_surface_damage_buffer(wl_cursor_surface, 0, 0, width, height);
wl_surface_commit(wl_cursor_surface);
}
wl_surface_attach(wl_cursor_surface, buffer, 0, 0);
wl_surface_set_buffer_scale(wl_cursor_surface, scale);
wl_surface_damage_buffer(wl_cursor_surface, 0, 0, width, height);
wl_surface_commit(wl_cursor_surface);
if (last_pointer_enter_serial != last_serial || hotspot_x != last_hotspot_x || hotspot_y != last_hotspot_y) {
last_serial = last_pointer_enter_serial;
last_hotspot_x = hotspot_x;
last_hotspot_y = hotspot_y;
wl_pointer_set_cursor(wl_pointer, last_pointer_enter_serial, wl_cursor_surface,
hotspot_x / scale, hotspot_y / scale);
wlFlushToServer(env);
}
wl_pointer_set_cursor(wl_pointer, pointerEnterSerial, wl_cursor_surface,
hotspot_x / scale, hotspot_y / scale);
wlFlushToServer(env);
}

View File

@@ -26,6 +26,7 @@
#endif
#include "WLKeyboard.h"
#include "WLToolkit.h"
#include <sun_awt_wl_WLKeyboard.h>
#include <stdint.h>
@@ -163,20 +164,18 @@ struct xkb_compose_state;
typedef void
(*xkb_keymap_key_iter_t)(struct xkb_keymap *keymap, xkb_keycode_t key, void *data);
static jclass keyboardClass; // sun.awt.wl.WLKeyboard
static jclass keyRepeatManagerClass; // sun.awt.wl.WLKeyboard.KeyRepeatManager
static jmethodID setRepeatInfoMID; // sun.awt.wl.WLKeyboard.KeyRepeatManager.setRepeatInfo
static jmethodID startRepeatMID; // sun.awt.wl.WLKeyboard.KeyRepeatManager.startRepeat
static jmethodID cancelRepeatMID; // sun.awt.wl.WLKeyboard.KeyRepeatManager.cancelRepeat
static jmethodID stopRepeatMID; // sun.awt.wl.WLKeyboard.KeyRepeatManager.stopRepeat
static bool
initJavaRefs(JNIEnv *env) {
CHECK_NULL_RETURN(keyboardClass = (*env)->FindClass(env, "sun/awt/wl/WLKeyboard"), false);
CHECK_NULL_RETURN(keyRepeatManagerClass = (*env)->FindClass(env, "sun/awt/wl/WLKeyboard$KeyRepeatManager"), false);
CHECK_NULL_RETURN(setRepeatInfoMID = (*env)->GetMethodID(env, keyRepeatManagerClass, "setRepeatInfo", "(II)V"),
false);
CHECK_NULL_RETURN(startRepeatMID = (*env)->GetMethodID(env, keyRepeatManagerClass, "startRepeat", "(JI)V"), false);
CHECK_NULL_RETURN(cancelRepeatMID = (*env)->GetMethodID(env, keyRepeatManagerClass, "cancelRepeat", "()V"), false);
CHECK_NULL_RETURN(startRepeatMID = (*env)->GetMethodID(env, keyRepeatManagerClass, "startRepeat", "(JJI)V"), false);
CHECK_NULL_RETURN(stopRepeatMID = (*env)->GetMethodID(env, keyRepeatManagerClass, "stopRepeat", "(I)V"), false);
return true;
}
@@ -1102,12 +1101,13 @@ convertKeysymToJavaCode(xkb_keysym_t keysym, int *javaKeyCode, int *javaKeyLocat
// Posts one UTF-16 code unit as a KEY_TYPED event
static void
postKeyTypedJavaChar(long timestamp, uint16_t javaChar) {
postKeyTypedJavaChar(long serial, long timestamp, uint16_t javaChar) {
#ifdef WL_KEYBOARD_DEBUG
fprintf(stderr, "postKeyTypedJavaChar(0x%04x)\n", (int) javaChar);
#endif
struct WLKeyEvent event = {
.serial = serial,
.timestamp = timestamp,
.id = java_awt_event_KeyEvent_KEY_TYPED,
.keyCode = java_awt_event_KeyEvent_VK_UNDEFINED,
@@ -1122,7 +1122,7 @@ postKeyTypedJavaChar(long timestamp, uint16_t javaChar) {
// Posts one Unicode code point as KEY_TYPED events
static void
postKeyTypedCodepoint(long timestamp, uint32_t codePoint) {
postKeyTypedCodepoint(long serial, long timestamp, uint32_t codePoint) {
if (codePoint >= 0x10000) {
// break the codepoint into surrogates
@@ -1131,16 +1131,16 @@ postKeyTypedCodepoint(long timestamp, uint32_t codePoint) {
uint16_t highSurrogate = (uint16_t) (0xD800 + ((codePoint >> 10) & 0x3ff));
uint16_t lowSurrogate = (uint16_t) (0xDC00 + (codePoint & 0x3ff));
postKeyTypedJavaChar(timestamp, highSurrogate);
postKeyTypedJavaChar(timestamp, lowSurrogate);
postKeyTypedJavaChar(serial, timestamp, highSurrogate);
postKeyTypedJavaChar(serial, timestamp, lowSurrogate);
} else {
postKeyTypedJavaChar(timestamp, (uint16_t) codePoint);
postKeyTypedJavaChar(serial, timestamp, (uint16_t) codePoint);
}
}
// Posts a UTF-8 encoded string as KEY_TYPED events
static void
postKeyTypedEvents(long timestamp, const char *string) {
postKeyTypedEvents(long serial, long timestamp, const char *string) {
#ifdef WL_KEYBOARD_DEBUG
fprintf(stderr, "postKeyTypedEvents(b\"");
for (const char *c = string; *c; ++c) {
@@ -1176,13 +1176,13 @@ postKeyTypedEvents(long timestamp, const char *string) {
// a single codepoint in range U+0000 to U+007F
remaining = 0;
curCodePoint = 0;
postKeyTypedCodepoint(timestamp, *ptr & 0x7f);
postKeyTypedCodepoint(serial, timestamp, *ptr & 0x7f);
} else if ((*ptr & 0xc0) == 0x80) {
// continuation byte
curCodePoint = (curCodePoint << 6u) | (uint32_t) (*ptr & 0x3f);
--remaining;
if (remaining == 0) {
postKeyTypedCodepoint(timestamp, curCodePoint);
postKeyTypedCodepoint(serial, timestamp, curCodePoint);
curCodePoint = 0;
}
} else {
@@ -1203,35 +1203,47 @@ getJavaKeyCharForKeycode(xkb_keycode_t xkbKeycode) {
// Posts an XKB keysym as KEY_TYPED events, without consulting the current compose state.
static void
handleKeyTypeNoCompose(long timestamp, xkb_keycode_t xkbKeycode) {
handleKeyTypeNoCompose(long serial, long timestamp, xkb_keycode_t xkbKeycode) {
#ifdef WL_KEYBOARD_DEBUG
fprintf(stderr, "handleKeyTypeNoCompose: xkbKeycode = %d\n", xkbKeycode);
#endif
int bufSize = xkb.state_key_get_utf8(keyboard.state, xkbKeycode, NULL, 0) + 1;
char buf[bufSize];
xkb.state_key_get_utf8(keyboard.state, xkbKeycode, buf, bufSize);
postKeyTypedEvents(timestamp, buf);
postKeyTypedEvents(serial, timestamp, buf);
}
// Handles generating KEY_TYPED events for an XKB keysym, translating it using the active compose state
static void
handleKeyType(long timestamp, xkb_keycode_t xkbKeycode) {
handleKeyType(long serial, long timestamp, xkb_keycode_t xkbKeycode) {
#ifdef WL_KEYBOARD_DEBUG
fprintf(stderr, "handleKeyType(xkbKeycode = %d)\n", xkbKeycode);
#endif
xkb_keysym_t keysym = xkb.state_key_get_one_sym(keyboard.state, xkbKeycode);
#ifdef WL_KEYBOARD_DEBUG
char buf[256];
xkb.keysym_get_name(keysym, buf, sizeof buf);
fprintf(stderr, "handleKeyType: keysym = %d (%s)\n", keysym, buf);
#endif
if (!keyboard.composeState ||
(xkb.compose_state_feed(keyboard.composeState, keysym) == XKB_COMPOSE_FEED_IGNORED)) {
handleKeyTypeNoCompose(timestamp, xkbKeycode);
handleKeyTypeNoCompose(serial, timestamp, xkbKeycode);
return;
}
switch (xkb.compose_state_get_status(keyboard.composeState)) {
case XKB_COMPOSE_NOTHING:
xkb.compose_state_reset(keyboard.composeState);
handleKeyTypeNoCompose(timestamp, xkbKeycode);
handleKeyTypeNoCompose(serial, timestamp, xkbKeycode);
break;
case XKB_COMPOSE_COMPOSING:
break;
case XKB_COMPOSE_COMPOSED: {
char buf[MAX_COMPOSE_UTF8_LENGTH];
xkb.compose_state_get_utf8(keyboard.composeState, buf, sizeof buf);
postKeyTypedEvents(timestamp, buf);
postKeyTypedEvents(serial, timestamp, buf);
xkb.compose_state_reset(keyboard.composeState);
break;
}
@@ -1249,13 +1261,25 @@ handleKeyType(long timestamp, xkb_keycode_t xkbKeycode) {
// or stopping it if isPressed = false.
// 2. From the key repeat manager. In this case, isRepeat = true and isPressed = true.
static void
handleKey(long timestamp, uint32_t keycode, bool isPressed, bool isRepeat) {
handleKey(long serial, long timestamp, uint32_t keycode, bool isPressed, bool isRepeat) {
#ifdef WL_KEYBOARD_DEBUG
fprintf(stderr, "handleKey(keycode = %d, isPressed = %d, isRepeat = %d)\n", keycode, isPressed, isRepeat);
#endif
JNIEnv *env = getEnv();
xkb_keycode_t xkbKeycode = keycode + 8;
bool keyRepeats = xkb.keymap_key_repeats(keyboard.keymap, xkbKeycode);
xkb_keysym_t keysym = translateKeycodeToKeysym(keycode, TRANSLATE_USING_ACTIVE_LAYOUT);
xkb_keysym_t qwertyKeysym = translateKeycodeToKeysym(keycode, TRANSLATE_USING_QWERTY);
#ifdef WL_KEYBOARD_DEBUG
char buf[256];
xkb.keysym_get_name(keysym, buf, sizeof buf);
fprintf(stderr, "handleKey: keysym = %d (%s)\n", keysym, buf);
xkb.keysym_get_name(qwertyKeysym, buf, sizeof buf);
fprintf(stderr, "handleKey: qwertyKeysym = %d (%s)\n", qwertyKeysym, buf);
#endif
int javaKeyCode, javaExtendedKeyCode, javaKeyLocation;
// If the national layouts support is enabled, and the current keyboard is not ascii-capable,
@@ -1288,7 +1312,12 @@ handleKey(long timestamp, uint32_t keycode, bool isPressed, bool isRepeat) {
}
}
#ifdef WL_KEYBOARD_DEBUG
fprintf(stderr, "handleKey: javaKeyCode = %d\n", javaKeyCode);
#endif
struct WLKeyEvent event = {
.serial = serial,
.timestamp = timestamp,
.id = isPressed ? java_awt_event_KeyEvent_KEY_PRESSED : java_awt_event_KeyEvent_KEY_RELEASED,
.keyCode = javaKeyCode,
@@ -1301,14 +1330,14 @@ handleKey(long timestamp, uint32_t keycode, bool isPressed, bool isRepeat) {
wlPostKeyEvent(&event);
if (isPressed) {
handleKeyType(timestamp, xkbKeycode);
handleKeyType(serial, timestamp, xkbKeycode);
if (!isRepeat && xkb.keymap_key_repeats(keyboard.keymap, xkbKeycode)) {
(*env)->CallVoidMethod(env, keyboard.keyRepeatManager, startRepeatMID, timestamp, keycode);
if (!isRepeat && keyRepeats) {
(*env)->CallVoidMethod(env, keyboard.keyRepeatManager, startRepeatMID, serial, timestamp, keycode);
JNU_CHECK_EXCEPTION(env);
}
} else {
(*env)->CallVoidMethod(env, keyboard.keyRepeatManager, cancelRepeatMID);
} else if (keyRepeats) {
(*env)->CallVoidMethod(env, keyboard.keyRepeatManager, stopRepeatMID, keycode);
JNU_CHECK_EXCEPTION(env);
}
}
@@ -1421,9 +1450,8 @@ Java_sun_awt_wl_WLKeyboard_initialize(JNIEnv *env, jobject instance, jobject key
}
JNIEXPORT void JNICALL
Java_sun_awt_wl_WLKeyboard_handleKeyPress(JNIEnv *env, jobject instance, jlong timestamp, jint keycode,
jboolean isRepeat) {
handleKey(timestamp, keycode, true, isRepeat);
Java_sun_awt_wl_WLKeyboard_handleKeyRepeat(JNIEnv *env, jobject instance, jlong serial, jlong timestamp, jint keycode) {
handleKey(serial, timestamp, keycode, true, true);
}
JNIEXPORT void JNICALL
@@ -1467,8 +1495,8 @@ wlSetKeymap(const char *serializedKeymap) {
}
void
wlSetKeyState(long timestamp, uint32_t keycode, bool isPressed) {
handleKey(timestamp, keycode, isPressed, false);
wlSetKeyState(long serial, long timestamp, uint32_t keycode, bool isPressed) {
handleKey(serial, timestamp, keycode, isPressed, false);
}
void
@@ -1487,4 +1515,4 @@ wlSetModifiers(uint32_t depressed, uint32_t latched, uint32_t locked, uint32_t g
if (group != oldLayoutIndex) {
onKeyboardLayoutChanged();
}
}
}

View File

@@ -32,6 +32,7 @@
bool wlInitKeyboard(JNIEnv* env);
struct WLKeyEvent {
long serial;
long timestamp;
int id;
int keyCode;
@@ -42,7 +43,7 @@ struct WLKeyEvent {
};
void wlSetKeymap(const char* serializedKeymap);
void wlSetKeyState(long timestamp, uint32_t keycode, bool isPressed);
void wlSetKeyState(long serial, long timestamp, uint32_t keycode, bool isPressed);
void wlSetRepeatInfo(int charsPerSecond, int delayMillis);
void wlSetModifiers(uint32_t depressed, uint32_t latched, uint32_t locked, uint32_t group);
void wlPostKeyEvent(const struct WLKeyEvent* event);

View File

@@ -1,6 +1,6 @@
/*
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2022, JetBrains s.r.o.. All rights reserved.
* Copyright (c) 2022-2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2022-2024, 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
@@ -78,14 +78,9 @@ struct wl_pointer *wl_pointer; // optional, check for NULL before use
#define MAX_CURSOR_SCALE 100
struct wl_cursor_theme *cursor_themes[MAX_CURSOR_SCALE] = {NULL};
struct wl_surface *wl_surface_in_focus = NULL;
struct wl_data_device_manager *wl_ddm = NULL;
struct zwp_primary_selection_device_manager_v1 *zwp_selection_dm = NULL; // optional, check for NULL before use
uint32_t last_mouse_pressed_serial = 0;
uint32_t last_pointer_enter_serial = 0;
uint32_t last_input_or_focus_serial = 0;
static uint32_t num_of_outstanding_sync = 0;
// This group of definitions corresponds to declarations from awt.h
@@ -104,7 +99,6 @@ static jfieldID hasEnterEventFID;
static jfieldID hasLeaveEventFID;
static jfieldID hasMotionEventFID;
static jfieldID hasButtonEventFID;
static jfieldID hasAxisEventFID;
static jfieldID serialFID;
static jfieldID surfaceFID;
static jfieldID timestampFID;
@@ -112,8 +106,16 @@ static jfieldID surfaceXFID;
static jfieldID surfaceYFID;
static jfieldID buttonCodeFID;
static jfieldID isButtonPressedFID;
static jfieldID axis_0_validFID;
static jfieldID axis_0_valueFID;
static jfieldID xAxis_hasVectorValueFID;
static jfieldID xAxis_hasStopEventFID;
static jfieldID xAxis_hasSteps120ValueFID;
static jfieldID xAxis_vectorValueFID;
static jfieldID xAxis_steps120ValueFID;
static jfieldID yAxis_hasVectorValueFID;
static jfieldID yAxis_hasStopEventFID;
static jfieldID yAxis_hasSteps120ValueFID;
static jfieldID yAxis_vectorValueFID;
static jfieldID yAxis_steps120ValueFID;
static jmethodID dispatchKeyboardKeyEventMID;
static jmethodID dispatchKeyboardModifiersEventMID;
@@ -143,10 +145,7 @@ struct pointer_event_cumulative {
bool has_leave_event : 1;
bool has_motion_event : 1;
bool has_button_event : 1;
bool has_axis_event : 1;
bool has_axis_source_event : 1;
bool has_axis_stop_event : 1;
bool has_axis_discrete_event : 1;
uint32_t time;
uint32_t serial;
@@ -159,9 +158,19 @@ struct pointer_event_cumulative {
uint32_t state;
struct {
bool valid;
wl_fixed_t value;
int32_t discrete;
// wl_pointer::axis
bool has_vector_value : 1;
// wl_pointer::axis_stop
bool has_stop_event : 1;
// wl_pointer::axis_discrete or wl_pointer::axis_value120
bool has_steps120_value : 1;
// wl_pointer::axis
wl_fixed_t vector_value;
// wl_pointer::axis_discrete or wl_pointer::axis_value120
// In the former case, the value is multiplied by 120 for compatibility with wl_pointer::axis_value120
int32_t steps120_value;
} axes[2];
uint32_t axis_source;
};
@@ -178,8 +187,6 @@ wl_pointer_enter(void *data, struct wl_pointer *wl_pointer,
pointer_event.surface = surface;
pointer_event.surface_x = surface_x,
pointer_event.surface_y = surface_y;
last_pointer_enter_serial = serial;
}
static void
@@ -210,9 +217,6 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer, uint32_t serial,
pointer_event.serial = serial;
pointer_event.button = button,
pointer_event.state = state;
if (state) {
last_mouse_pressed_serial = serial;
}
}
static void
@@ -221,10 +225,9 @@ wl_pointer_axis(void *data, struct wl_pointer *wl_pointer, uint32_t time,
{
assert(axis < sizeof(pointer_event.axes)/sizeof(pointer_event.axes[0]));
pointer_event.has_axis_event = true;
pointer_event.time = time;
pointer_event.axes[axis].valid = true;
pointer_event.axes[axis].value = value;
pointer_event.axes[axis].has_vector_value = true;
pointer_event.time = time;
pointer_event.axes[axis].vector_value = value;
}
static void
@@ -241,18 +244,35 @@ wl_pointer_axis_stop(void *data, struct wl_pointer *wl_pointer,
{
assert(axis < sizeof(pointer_event.axes)/sizeof(pointer_event.axes[0]));
pointer_event.has_axis_stop_event = true;
pointer_event.time = time;
pointer_event.axes[axis].valid = true;
pointer_event.axes[axis].has_stop_event = true;
pointer_event.time = time;
}
static void
wl_pointer_axis_discrete(void *data, struct wl_pointer *wl_pointer,
uint32_t axis, int32_t discrete)
{
pointer_event.has_axis_discrete_event = true;
pointer_event.axes[axis].valid = true;
pointer_event.axes[axis].discrete = discrete;
assert(axis < sizeof(pointer_event.axes)/sizeof(pointer_event.axes[0]));
// wl_pointer::axis_discrete event is deprecated with wl_pointer version 8 - this event is not sent to clients
// supporting version 8 or later.
// It's just an additional check to work around possible bugs in compositors when they send both
// wl_pointer::axis_discrete and wl_pointer::axis_value120 events within the same frame.
// In this case wl_pointer::axis_value120 would be preferred.
if (!pointer_event.axes[axis].has_steps120_value) {
pointer_event.axes[axis].has_steps120_value = true;
pointer_event.axes[axis].steps120_value = discrete * 120;
}
}
static void
wl_pointer_axis_value120(void *data, struct wl_pointer *wl_pointer,
uint32_t axis, int32_t value120)
{
assert(axis < sizeof(pointer_event.axes)/sizeof(pointer_event.axes[0]));
pointer_event.axes[axis].has_steps120_value = true;
pointer_event.axes[axis].steps120_value = value120;
}
static inline void
@@ -268,7 +288,6 @@ fillJavaPointerEvent(JNIEnv* env, jobject pointerEventRef)
(*env)->SetBooleanField(env, pointerEventRef, hasLeaveEventFID, pointer_event.has_leave_event);
(*env)->SetBooleanField(env, pointerEventRef, hasMotionEventFID, pointer_event.has_motion_event);
(*env)->SetBooleanField(env, pointerEventRef, hasButtonEventFID, pointer_event.has_button_event);
(*env)->SetBooleanField(env, pointerEventRef, hasAxisEventFID, pointer_event.has_axis_event);
(*env)->SetLongField(env, pointerEventRef, surfaceFID, (long)pointer_event.surface);
(*env)->SetLongField(env, pointerEventRef, serialFID, pointer_event.serial);
@@ -281,8 +300,17 @@ fillJavaPointerEvent(JNIEnv* env, jobject pointerEventRef)
(*env)->SetBooleanField(env, pointerEventRef, isButtonPressedFID,
(pointer_event.state == WL_POINTER_BUTTON_STATE_PRESSED));
(*env)->SetBooleanField(env, pointerEventRef, axis_0_validFID, pointer_event.axes[0].valid);
(*env)->SetIntField(env, pointerEventRef, axis_0_valueFID, wl_fixed_to_int(pointer_event.axes[0].value));
(*env)->SetBooleanField(env, pointerEventRef, xAxis_hasVectorValueFID, pointer_event.axes[1].has_vector_value);
(*env)->SetBooleanField(env, pointerEventRef, xAxis_hasStopEventFID, pointer_event.axes[1].has_stop_event);
(*env)->SetBooleanField(env, pointerEventRef, xAxis_hasSteps120ValueFID, pointer_event.axes[1].has_steps120_value);
(*env)->SetDoubleField (env, pointerEventRef, xAxis_vectorValueFID, wl_fixed_to_double(pointer_event.axes[1].vector_value));
(*env)->SetIntField (env, pointerEventRef, xAxis_steps120ValueFID, pointer_event.axes[1].steps120_value);
(*env)->SetBooleanField(env, pointerEventRef, yAxis_hasVectorValueFID, pointer_event.axes[0].has_vector_value);
(*env)->SetBooleanField(env, pointerEventRef, yAxis_hasStopEventFID, pointer_event.axes[0].has_stop_event);
(*env)->SetBooleanField(env, pointerEventRef, yAxis_hasSteps120ValueFID, pointer_event.axes[0].has_steps120_value);
(*env)->SetDoubleField (env, pointerEventRef, yAxis_vectorValueFID, wl_fixed_to_double(pointer_event.axes[0].vector_value));
(*env)->SetIntField (env, pointerEventRef, yAxis_steps120ValueFID, pointer_event.axes[0].steps120_value);
}
static void
@@ -315,7 +343,9 @@ static const struct wl_pointer_listener wl_pointer_listener = {
.frame = wl_pointer_frame,
.axis_source = wl_pointer_axis_source,
.axis_stop = wl_pointer_axis_stop,
.axis_discrete = wl_pointer_axis_discrete
.axis_discrete = wl_pointer_axis_discrete/*,
This is only supported if the libwayland-client supports version 8 of the wl_pointer interface
.axis_value120 = wl_pointer_axis_value120*/
};
@@ -345,9 +375,6 @@ static void
wl_keyboard_enter(void *data, struct wl_keyboard *wl_keyboard,
uint32_t serial, struct wl_surface *surface, struct wl_array *keys)
{
wl_surface_in_focus = surface;
last_input_or_focus_serial = serial;
JNIEnv* env = getEnv();
(*env)->CallStaticVoidMethod(env,
tkClass,
@@ -360,16 +387,13 @@ static void
wl_keyboard_key(void *data, struct wl_keyboard *wl_keyboard,
uint32_t serial, uint32_t time, uint32_t keycode, uint32_t state)
{
JNIEnv* env = getEnv();
wlSetKeyState(time, keycode, state ? true : false);
wlSetKeyState(serial, time, keycode, state ? true : false);
}
static void
wl_keyboard_leave(void *data, struct wl_keyboard *wl_keyboard,
uint32_t serial, struct wl_surface *surface)
{
wl_surface_in_focus = NULL;
JNIEnv* env = getEnv();
(*env)->CallStaticVoidMethod(env,
tkClass,
@@ -400,9 +424,7 @@ static void
wl_keyboard_repeat_info(void *data, struct wl_keyboard *wl_keyboard,
int32_t rate, int32_t delay)
{
if (rate > 0 && delay > 0) {
wlSetRepeatInfo(rate, delay);
}
wlSetRepeatInfo(rate, delay);
}
void
@@ -413,6 +435,7 @@ wlPostKeyEvent(const struct WLKeyEvent* event)
env,
tkClass,
dispatchKeyboardKeyEventMID,
event->serial,
event->timestamp,
event->id,
event->keyCode,
@@ -630,8 +653,6 @@ initJavaRefs(JNIEnv *env, jclass clazz)
JNI_FALSE);
CHECK_NULL_RETURN(hasButtonEventFID = (*env)->GetFieldID(env, pointerEventClass, "has_button_event", "Z"),
JNI_FALSE);
CHECK_NULL_RETURN(hasAxisEventFID = (*env)->GetFieldID(env, pointerEventClass, "has_axis_event", "Z"),
JNI_FALSE);
CHECK_NULL_RETURN(serialFID = (*env)->GetFieldID(env, pointerEventClass, "serial", "J"), JNI_FALSE);
CHECK_NULL_RETURN(surfaceFID = (*env)->GetFieldID(env, pointerEventClass, "surface", "J"), JNI_FALSE);
@@ -640,8 +661,18 @@ initJavaRefs(JNIEnv *env, jclass clazz)
CHECK_NULL_RETURN(surfaceYFID = (*env)->GetFieldID(env, pointerEventClass, "surface_y", "I"), JNI_FALSE);
CHECK_NULL_RETURN(buttonCodeFID = (*env)->GetFieldID(env, pointerEventClass, "buttonCode", "I"), JNI_FALSE);
CHECK_NULL_RETURN(isButtonPressedFID = (*env)->GetFieldID(env, pointerEventClass, "isButtonPressed", "Z"), JNI_FALSE);
CHECK_NULL_RETURN(axis_0_validFID = (*env)->GetFieldID(env, pointerEventClass, "axis_0_valid", "Z"), JNI_FALSE);
CHECK_NULL_RETURN(axis_0_valueFID = (*env)->GetFieldID(env, pointerEventClass, "axis_0_value", "I"), JNI_FALSE);
CHECK_NULL_RETURN(xAxis_hasVectorValueFID = (*env)->GetFieldID(env, pointerEventClass, "xAxis_hasVectorValue", "Z"), JNI_FALSE);
CHECK_NULL_RETURN(xAxis_hasStopEventFID = (*env)->GetFieldID(env, pointerEventClass, "xAxis_hasStopEvent", "Z"), JNI_FALSE);
CHECK_NULL_RETURN(xAxis_hasSteps120ValueFID = (*env)->GetFieldID(env, pointerEventClass, "xAxis_hasSteps120Value", "Z"), JNI_FALSE);
CHECK_NULL_RETURN(xAxis_vectorValueFID = (*env)->GetFieldID(env, pointerEventClass, "xAxis_vectorValue", "D"), JNI_FALSE);
CHECK_NULL_RETURN(xAxis_steps120ValueFID = (*env)->GetFieldID(env, pointerEventClass, "xAxis_steps120Value", "I"), JNI_FALSE);
CHECK_NULL_RETURN(yAxis_hasVectorValueFID = (*env)->GetFieldID(env, pointerEventClass, "yAxis_hasVectorValue", "Z"), JNI_FALSE);
CHECK_NULL_RETURN(yAxis_hasStopEventFID = (*env)->GetFieldID(env, pointerEventClass, "yAxis_hasStopEvent", "Z"), JNI_FALSE);
CHECK_NULL_RETURN(yAxis_hasSteps120ValueFID = (*env)->GetFieldID(env, pointerEventClass, "yAxis_hasSteps120Value", "Z"), JNI_FALSE);
CHECK_NULL_RETURN(yAxis_vectorValueFID = (*env)->GetFieldID(env, pointerEventClass, "yAxis_vectorValue", "D"), JNI_FALSE);
CHECK_NULL_RETURN(yAxis_steps120ValueFID = (*env)->GetFieldID(env, pointerEventClass, "yAxis_steps120Value", "I"), JNI_FALSE);
CHECK_NULL_RETURN(dispatchKeyboardEnterEventMID = (*env)->GetStaticMethodID(env, tkClass,
"dispatchKeyboardEnterEvent",
@@ -653,7 +684,7 @@ initJavaRefs(JNIEnv *env, jclass clazz)
JNI_FALSE);
CHECK_NULL_RETURN(dispatchKeyboardKeyEventMID = (*env)->GetStaticMethodID(env, tkClass,
"dispatchKeyboardKeyEvent",
"(JIIIIIC)V"),
"(JJIIIIIC)V"),
JNI_FALSE);
CHECK_NULL_RETURN(dispatchKeyboardModifiersEventMID = (*env)->GetStaticMethodID(env, tkClass,
"dispatchKeyboardModifiersEvent",

View File

@@ -63,12 +63,6 @@ extern struct wl_cursor_theme *wl_cursor_theme;
extern struct wl_data_device_manager *wl_ddm;
extern struct zwp_primary_selection_device_manager_v1 *zwp_selection_dm; // optional, check for NULL before use
extern struct wl_surface *wl_surface_in_focus;
extern uint32_t last_mouse_pressed_serial;
extern uint32_t last_pointer_enter_serial;
extern uint32_t last_input_or_focus_serial;
JNIEnv *getEnv();
int wlFlushToServer(JNIEnv* env);

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, 2024, 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
@@ -28,20 +28,41 @@ package sun.awt;
import java.awt.GraphicsEnvironment;
import java.awt.Toolkit;
import sun.awt.windows.WToolkit;
public class PlatformGraphicsInfo {
private static final boolean hasDisplays;
static {
loadAWTLibrary();
hasDisplays = hasDisplays0();
}
@SuppressWarnings("removal")
private static void loadAWTLibrary() {
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<Void>() {
public Void run() {
System.loadLibrary("awt");
return null;
}
});
}
private static native boolean hasDisplays0();
public static GraphicsEnvironment createGE() {
return new Win32GraphicsEnvironment();
}
public static Toolkit createToolkit() {
return new sun.awt.windows.WToolkit();
return new WToolkit();
}
public static boolean getDefaultHeadlessProperty() {
// On Windows, we assume we can always create headful apps.
// Here is where we can add code that would actually check.
return false;
// If we don't find usable displays, we run headless.
return !hasDisplays;
}
/*

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2024, 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
@@ -66,7 +66,8 @@ public final class Win32GraphicsEnvironment extends SunGraphicsEnvironment {
WToolkit.loadLibraries();
// setup flags before initializing native layer
WindowsFlags.initFlags();
initDisplayWrapper();
initDisplay();
// Install correct surface manager factory.
SurfaceManagerFactory.setInstance(new WindowsSurfaceManagerFactory());
@@ -88,21 +89,12 @@ public final class Win32GraphicsEnvironment extends SunGraphicsEnvironment {
}
/**
* Initializes native components of the graphics environment. This
* Initializes native components of the graphics environment. This
* includes everything from the native GraphicsDevice elements to
* the DirectX rendering layer.
*/
private static native void initDisplay();
private static boolean displayInitialized; // = false;
public static void initDisplayWrapper() {
if (!displayInitialized) {
displayInitialized = true;
if (!isUIScaleEnabled()) setProcessDPIAwareness(PROCESS_SYSTEM_DPI_AWARE);
initDisplay();
}
}
public Win32GraphicsEnvironment() {
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2001, 2024, 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
@@ -85,60 +85,75 @@
#include "Trace.h"
#include "D3DPipelineManager.h"
typedef struct {
int monitorCounter;
int monitorLimit;
HMONITOR* hmpMonitors;
} MonitorData;
/* Some helper functions (from awt_MMStub.h/cpp) */
int g_nMonitorCounter;
int g_nMonitorLimit;
HMONITOR* g_hmpMonitors;
// Only monitors where CreateDC does not fail are valid
static BOOL IsValidMonitor(HMONITOR hMon)
{
MONITORINFOEX mieInfo;
memset((void*)(&mieInfo), 0, sizeof(MONITORINFOEX));
mieInfo.cbSize = sizeof(MONITORINFOEX);
if (!::GetMonitorInfo(hMon, (LPMONITORINFOEX)(&mieInfo))) {
J2dTraceLn1(J2D_TRACE_INFO, "Devices::IsValidMonitor: GetMonitorInfo failed for monitor with handle %p", hMon);
return FALSE;
}
HDC hDC = CreateDC(mieInfo.szDevice, NULL, NULL, NULL);
if (NULL == hDC) {
J2dTraceLn2(J2D_TRACE_INFO, "Devices::IsValidMonitor: CreateDC failed for monitor with handle %p, device: %S", hMon, mieInfo.szDevice);
return FALSE;
}
::DeleteDC(hDC);
return TRUE;
}
// Callback for CountMonitors below
BOOL WINAPI clb_fCountMonitors(HMONITOR hMon, HDC hDC, LPRECT rRect, LPARAM lP)
static BOOL WINAPI clb_fCountMonitors(HMONITOR hMon, HDC hDC, LPRECT rRect, LPARAM lpMonitorCounter)
{
g_nMonitorCounter ++;
if (IsValidMonitor(hMon)) {
(*((int *)lpMonitorCounter))++;
}
return TRUE;
}
int WINAPI CountMonitors(void)
{
g_nMonitorCounter = 0;
::EnumDisplayMonitors(NULL, NULL, clb_fCountMonitors, 0L);
return g_nMonitorCounter;
int monitorCounter = 0;
::EnumDisplayMonitors(NULL, NULL, clb_fCountMonitors, (LPARAM)&monitorCounter);
return monitorCounter;
}
// Callback for CollectMonitors below
BOOL WINAPI clb_fCollectMonitors(HMONITOR hMon, HDC hDC, LPRECT rRect, LPARAM lP)
static BOOL WINAPI clb_fCollectMonitors(HMONITOR hMon, HDC hDC, LPRECT rRect, LPARAM lpMonitorData)
{
if ((g_nMonitorCounter < g_nMonitorLimit) && (NULL != g_hmpMonitors)) {
g_hmpMonitors[g_nMonitorCounter] = hMon;
g_nMonitorCounter ++;
MonitorData* pMonitorData = (MonitorData *)lpMonitorData;
if ((pMonitorData->monitorCounter < pMonitorData->monitorLimit) && (IsValidMonitor(hMon))) {
pMonitorData->hmpMonitors[pMonitorData->monitorCounter] = hMon;
pMonitorData->monitorCounter++;
}
return TRUE;
}
int WINAPI CollectMonitors(HMONITOR* hmpMonitors, int nNum)
static int WINAPI CollectMonitors(HMONITOR* hmpMonitors, int nNum)
{
int retCode = 0;
if (NULL != hmpMonitors) {
g_nMonitorCounter = 0;
g_nMonitorLimit = nNum;
g_hmpMonitors = hmpMonitors;
::EnumDisplayMonitors(NULL, NULL, clb_fCollectMonitors, 0L);
retCode = g_nMonitorCounter;
g_nMonitorCounter = 0;
g_nMonitorLimit = 0;
g_hmpMonitors = NULL;
MonitorData monitorData;
monitorData.monitorCounter = 0;
monitorData.monitorLimit = nNum;
monitorData.hmpMonitors = hmpMonitors;
::EnumDisplayMonitors(NULL, NULL, clb_fCollectMonitors, (LPARAM)&monitorData);
return monitorData.monitorCounter;
} else {
return 0;
}
return retCode;
}
BOOL WINAPI MonitorBounds(HMONITOR hmMonitor, RECT* rpBounds)

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2001, 2024, 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
@@ -74,4 +74,6 @@ static CriticalSection arrayLock;
BOOL WINAPI MonitorBounds (HMONITOR, RECT*);
int WINAPI CountMonitors (void);
#endif // _DEVICES_H_

View File

@@ -701,13 +701,17 @@ MsgRouting AwtFrame::WmNcMouseUp(WPARAM hitTest, int x, int y, int button) {
}
MsgRouting AwtFrame::WmNcMouseDown(WPARAM hitTest, int x, int y, int button) {
BOOL customTitleBar = IsTitleBarHitTest(hitTest) && HasCustomTitleBar();
// By Swing request, click on the Frame's decorations (even on
// grabbed Frame) should generate UngrabEvent
if (m_grabbedWindow != NULL/* && !m_grabbedWindow->IsOneOfOwnersOf(this)*/) {
// However, if the custom bar was clicked, and there's a component that handles mouse clicks,
// then ungrabbing causes undesired behavior, such as a menu collapsing and reopening.
// So don't ungrab in that case.
if (m_grabbedWindow != NULL && (!customTitleBar || AreCustomTitleBarNativeActionsAllowed())) {
m_grabbedWindow->Ungrab();
}
customTitleBarTouchDragPosition = (LPARAM) -1;
if (IsTitleBarHitTest(hitTest) && HasCustomTitleBar()) {
if (customTitleBar) {
// When double-clicking title bar of native Windows apps, they respond to second mouse press, not release
const int LEFT_DBLCLCK = LEFT_BUTTON | DBL_CLICK;
BOOL maximize = (button & LEFT_DBLCLCK) == LEFT_DBLCLCK && IsResizable();

View File

@@ -0,0 +1,37 @@
/*
* Copyright (c) 2024 SAP SE. 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_awt_PlatformGraphicsInfo.h>
#include "Devices.h"
/*
* Class: sun_awt_PlatformGraphicsInfo
* Method: hasDisplays0
* Signature: ()Z
*/
JNIEXPORT jboolean JNICALL
Java_sun_awt_PlatformGraphicsInfo_hasDisplays0(JNIEnv *env, jclass thisClass) {
return CountMonitors() > 0 ? JNI_TRUE : JNI_FALSE;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 1999, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1999, 2024, 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
@@ -180,7 +180,9 @@ void AwtWin32GraphicsDevice::Initialize()
}
gpBitmapInfo->bmiHeader.biBitCount = 0;
HDC hBMDC = this->GetDC();
VERIFY(hBMDC != NULL);
HBITMAP hBM = ::CreateCompatibleBitmap(hBMDC, 1, 1);
VERIFY(hBM != NULL);
VERIFY(::GetDIBits(hBMDC, hBM, 0, 1, NULL, gpBitmapInfo, DIB_RGB_COLORS));
if (colorData->bitsperpixel > 8) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 1999, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1999, 2024, 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
@@ -36,10 +36,8 @@
BOOL DWMIsCompositionEnabled();
void initScreens(JNIEnv *env) {
if (!Devices::UpdateInstance(env)) {
JNU_ThrowInternalError(env, "Could not update the devices array.");
return;
}
}

View File

@@ -131,6 +131,7 @@ gc/TestAllocHumongousFragment.java#iu-aggressive 8298781 generic-all
# :hotspot_runtime
runtime/cds/appcds/complexURI/ComplexURITest.java JBR-6616 window-aarch64
runtime/cds/appcds/TestDumpClassListSource.java JBR-6099 generic-all
runtime/cds/appcds/dynamicArchive/CDSStreamTestDriver.java initial_run generic-all
runtime/cds/appcds/methodHandles/MethodHandlesInvokersTest.java initial_run generic-all

View File

@@ -0,0 +1,160 @@
/* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2024 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.
*
* 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.
*/
/*
* @test
* @summary Verifies that CDS works with jar located in directories
* with names that need escaping
* @bug 8339460
* @requires vm.cds
* @requires vm.cds.custom.loaders
* @requires vm.flagless
* @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds
* @compile mypackage/Main.java mypackage/Another.java
* @run main/othervm ComplexURITest
*/
import jdk.test.lib.process.OutputAnalyzer;
import jdk.test.lib.process.ProcessTools;
import jdk.test.lib.Platform;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
public class ComplexURITest {
final static String listFileName = "test-classlist.txt";
final static String archiveName = "test-dynamic.jsa";
final static String moduleName = "mymodule";
public static void main(String[] args) throws Exception {
System.setProperty("test.noclasspath", "true");
String jarFile = JarBuilder.build(moduleName, "mypackage/Main", "mypackage/Another");
Path subDir = Path.of(".", "dir with space");
Files.createDirectory(subDir);
Path newJarFilePath = subDir.resolve(moduleName + ".jar");
Files.move(Path.of(jarFile), newJarFilePath);
jarFile = newJarFilePath.toString();
File fileList = new File(listFileName);
delete(fileList.toPath());
File fileArchive = new File(archiveName);
delete(fileArchive.toPath());
createClassList(jarFile);
if (!fileList.exists()) {
throw new RuntimeException("No class list created at " + fileList);
}
createArchive(jarFile);
if (!fileArchive.exists()) {
throw new RuntimeException("No shared classes archive created at " + fileArchive);
}
useArchive(jarFile);
delete(fileArchive.toPath());
createDynamicArchive(jarFile);
if (!fileArchive.exists()) {
throw new RuntimeException("No dynamic archive created at " + fileArchive);
}
testDynamicArchive(jarFile);
}
private static void delete(Path path) throws IOException {
if (Files.exists(path)) {
if (Platform.isWindows()) {
Files.setAttribute(path, "dos:readonly", false);
}
Files.delete(path);
}
}
private static void createClassList(String jarFile) throws Exception {
String[] launchArgs = {
"-XX:DumpLoadedClassList=" + listFileName,
"--module-path",
jarFile,
"--module",
moduleName + "/mypackage.Main"};
ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(launchArgs);
OutputAnalyzer output = TestCommon.executeAndLog(pb, "create-list");
output.shouldHaveExitValue(0);
}
private static void createArchive(String jarFile) throws Exception {
String[] launchArgs = {
"-Xshare:dump",
"-XX:SharedClassListFile=" + listFileName,
"-XX:SharedArchiveFile=" + archiveName,
"--module-path",
jarFile,
"--module",
moduleName + "/mypackage.Main"};
ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(launchArgs);
OutputAnalyzer output = TestCommon.executeAndLog(pb, "dump-archive");
output.shouldHaveExitValue(0);
}
private static void useArchive(String jarFile) throws Exception {
String[] launchArgs = {
"-Xshare:on",
"-XX:SharedArchiveFile=" + archiveName,
"--module-path",
jarFile,
"--module",
moduleName + "/mypackage.Main"};
ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(launchArgs);
OutputAnalyzer output = TestCommon.executeAndLog(pb, "use-archive");
output.shouldHaveExitValue(0);
}
private static void createDynamicArchive(String jarFile) throws Exception {
String[] launchArgs = {
"-XX:ArchiveClassesAtExit=" + archiveName,
"--module-path",
jarFile,
"--module",
moduleName + "/mypackage.Main"};
ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(launchArgs);
OutputAnalyzer output = TestCommon.executeAndLog(pb, "dynamic-archive");
output.shouldHaveExitValue(0);
}
private static void testDynamicArchive(String jarFile) throws Exception {
String[] launchArgs = {
"-XX:SharedArchiveFile=" + archiveName,
"-XX:+PrintSharedArchiveAndExit",
"--module-path",
jarFile,
"--module",
moduleName + "/mypackage.Main"};
ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(launchArgs);
OutputAnalyzer output = TestCommon.executeAndLog(pb, "dynamic-archive");
output.shouldHaveExitValue(0);
output.shouldContain("archive is valid");
output.shouldContain(": mypackage.Main app_loader");
output.shouldContain(": mypackage.Another unregistered_loader");
}
}

View File

@@ -0,0 +1,27 @@
/* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2024 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.
*
* 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 mypackage;
public class Another {
}

View File

@@ -0,0 +1,37 @@
/* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2024 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.
*
* 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 mypackage;
import java.net.URL;
import java.net.URLClassLoader;
public class Main {
public static void main(String[] args) throws Exception {
URL url1 = Main.class.getProtectionDomain().getCodeSource().getLocation();
System.out.println("Will load Another from " + url1);
ClassLoader cl = URLClassLoader.newInstance(new URL[] { url1 }, null);
var anotherClass = cl.loadClass("mypackage.Another");
System.out.println("Class " + anotherClass + " loaded successfully");
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2024, 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
@@ -33,7 +33,6 @@ import java.nio.file.attribute.BasicFileAttributes;
/**
* @test
* @bug 8189604 8208702
* @requires !vm.debug | os.family != "windows"
* @run main/othervm -Djava.awt.headless=false HangDuringStaticInitialization
* @run main/othervm -Djava.awt.headless=true HangDuringStaticInitialization
*/

View File

@@ -0,0 +1,80 @@
/*
* Copyright 2024 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.
*
* 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.
*/
/**
* @test
* @summary Verifies the error log does contain events previously
* logged with JNU_LOG_EVENT()
* @library /test/lib
* @run main/native LogEventTest
*/
import jdk.test.lib.Platform;
import jdk.test.lib.process.ProcessTools;
import jdk.test.lib.process.OutputAnalyzer;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
public class LogEventTest {
public static void main(String args[]) throws Exception {
if (args.length > 0 && args[0].equals("--test")) {
System.out.println("Proceeding to crash JVM with a simulated assertion failure");
String osDependentLibraryFileName = toPlatformLibraryName("LogEventTest");
String nativePathName = System.getProperty("test.nativepath");
Path nativePath = Paths.get(nativePathName).resolve(osDependentLibraryFileName);
System.out.println("Loading library from: " + nativePath);
System.load(String.valueOf(nativePath));
logEvent(42);
crashJVM();
System.out.println("...shouldn't reach here");
} else {
generateAndVerifyCrashLogContents();
}
}
public static void generateAndVerifyCrashLogContents() throws Exception {
String nativePathSetting = "-Dtest.nativepath=" + System.getProperty("test.nativepath");
ArrayList<String> opts = new ArrayList<>();
opts.add("-XX:-CreateCoredumpOnCrash");
opts.add("-XX:+ErrorFileToStdout");
opts.add("--enable-native-access=ALL-UNNAMED");
opts.add(nativePathSetting);
opts.add(LogEventTest.class.getName());
opts.add("--test");
ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder(opts);
OutputAnalyzer output = new OutputAnalyzer(pb.start());
output.outputTo(System.out);
output.shouldContain("LogEventTest: unique log message");
output.shouldContain("LogEventTest: 42");
}
private static String toPlatformLibraryName(String name) {
return (Platform.isWindows() ? "" : "lib") + name + "." + Platform.sharedLibraryExt();
}
static native void logEvent(int arg);
static native void crashJVM();
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright 2024 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.
*
* 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_util.h>
JNIEXPORT void JNICALL
Java_LogEventTest_logEvent(JNIEnv* env, jclass cls, jint arg) {
JNU_LOG_EVENT(env, "LogEventTest: unique log message");
JNU_LOG_EVENT(env, "LogEventTest: %d", arg);
}
JNIEXPORT void JNICALL
Java_LogEventTest_crashJVM(JNIEnv* env, jclass cls) {
JNU_RUNTIME_ASSERT(env, 0 == 1, "Java_LogEventTest_crashJVM: unique message");
}

View File

@@ -30,13 +30,14 @@ import java.io.InputStreamReader;
/**
* @test
* @summary VerifyDependencies checks readability verifies that a Linux shared
* library has no dependency on symbols from glibc version higher than 2.17
* library has no dependency on symbols from glibc version higher than 2.28
* @run main VerifyDependencies
* @requires (os.family == "linux")
*/
public class VerifyDependencies {
static final public String EXPECTED_VERSION = "2.28";
public static void verifyLibrary(String libraryPath) throws IOException {
Process process;
BufferedReader reader;
@@ -51,12 +52,12 @@ public class VerifyDependencies {
System.out.println(line);
if (line.contains("GLIBC_")) {
String version = extractVersion(line);
if (compareVersions(version, "2.17") > 0) {
if (compareVersions(version, EXPECTED_VERSION) > 0) {
throw new RuntimeException(libraryPath + " has a dependency on glibc version " + version);
}
}
}
System.out.println(libraryPath + " has no dependency on glibc version higher than 2.17");
System.out.println(libraryPath + " has no dependency on glibc version higher than " + EXPECTED_VERSION);
}
private static String extractVersion(String line) {

View File

@@ -31,6 +31,8 @@
import com.jetbrains.Extensions;
import com.jetbrains.internal.JBRApi;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -103,6 +105,25 @@ public class RealTest {
// Asking for BAR must return null, as it is not supported
requireNull(JBRApi.getService(Service.class, Extensions.FOO, Extensions.BAR));
requireNull(JBRApi.getService(Service.class, Extensions.BAR));
// Test specialized (implicit) List proxy
List<Api2Way> list = Objects.requireNonNull(service.testList(null));
Api2Way listItem = new TwoWayImpl();
list.add(listItem);
if (list.size() != 1) {
throw new Error("Unexpected List size");
}
if (list.get(0) != listItem) {
throw new Error("Unexpected List item");
}
service.testList(list);
if (!list.isEmpty()) {
throw new Error("Unexpected List size");
}
list = new ArrayList<>();
if (list != service.testList(list)) {
throw new Error("List passthrough mismatch");
}
}
private static class TwoWayImpl implements Api2Way {

View File

@@ -28,6 +28,7 @@ import com.jetbrains.Extensions;
import com.jetbrains.Provides;
import com.jetbrains.Provided;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.IntSupplier;
@@ -47,6 +48,7 @@ public class Real {
*/
void testMethodNameConflict(Api2Way a);
void testMethodNameConflict(ApiLazyNumber a);
List<Api2Way> testList(List<Api2Way> list);
}
@Provided

View File

@@ -26,6 +26,8 @@ package com.jetbrains.test.jbr;
import com.jetbrains.Provided;
import com.jetbrains.Provides;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.IntSupplier;
@@ -54,6 +56,14 @@ public class Real {
}
void testMethodNameConflict(JBR2Way a) {}
void testMethodNameConflict(JBRLazyNumber a) {}
List<JBR2Way> testList(List<JBR2Way> list) {
if (list == null) {
return new ArrayList<>();
} else {
list.clear();
return list;
}
}
}
@Provides

View File

@@ -0,0 +1,134 @@
/*
* Copyright 2000-2024 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.
*
* 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.
*/
import com.jetbrains.JBR;
import com.jetbrains.WindowDecorations;
import test.jb.testhelpers.TitleBar.TestUtils;
import java.awt.Point;
import java.awt.Dimension;
import java.awt.Robot;
import java.awt.event.InputEvent;
import javax.swing.JFrame;
import javax.swing.JLayeredPane;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.SwingUtilities;
/*
* @test
* @summary Check an expanded menu in a custom title bar can be closed by clicking it
* @requires (os.family != "mac")
* @library ../../../testhelpers/TitleBar
* @build TestUtils
* @run main/othervm JMenuClickToCloseTest
*/
public class JMenuClickToCloseTest {
private static WindowDecorations windowDecorations;
private static JMenu menu;
private static JFrame jFrame;
private static boolean passed = false;
static volatile Point menuLocation;
static volatile Dimension menuSize;
static volatile boolean isMenuVisible;
public static void main(String... args) throws Exception {
var robot = new Robot();
robot.setAutoDelay(50);
try {
SwingUtilities.invokeAndWait(JMenuClickToCloseTest::prepareUI);
if (windowDecorations == null) {
System.out.println("TEST SKIPPED - custom window decorations aren't supported in this environment");
return;
}
robot.waitForIdle();
SwingUtilities.invokeAndWait(() -> {
menuLocation = menu.getLocationOnScreen();
menuSize = menu.getSize();
});
robot.mouseMove(
menuLocation.x + menuSize.width / 2,
menuLocation.y + menuSize.height / 2
);
robot.waitForIdle();
robot.mousePress(InputEvent.BUTTON1_DOWN_MASK);
robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK);
robot.waitForIdle();
robot.delay(1000);
SwingUtilities.invokeAndWait(() -> {
isMenuVisible = menu.isPopupMenuVisible();
});
if (!isMenuVisible) {
throw new RuntimeException("Menu was not opened on click");
}
robot.waitForIdle();
robot.mousePress(InputEvent.BUTTON1_DOWN_MASK);
robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK);
robot.waitForIdle();
robot.delay(1000);
SwingUtilities.invokeAndWait(() -> {
isMenuVisible = menu.isPopupMenuVisible();
});
if (isMenuVisible) {
throw new RuntimeException("Menu was not closed on second click");
}
} finally {
SwingUtilities.invokeAndWait(JMenuClickToCloseTest::disposeUI);
}
if (!passed) {
System.out.println("TEST FAILED");
} else {
System.out.println("TEST PASSED");
}
}
private static void prepareUI() {
windowDecorations = JBR.getWindowDecorations();
if (windowDecorations == null) return;
var menuBar = new JMenuBar();
menu = new JMenu("test");
var menuItem = new JMenuItem("test");
menu.add(menuItem);
menuBar.add(menu);
var titleBar = windowDecorations.createCustomTitleBar();
titleBar.setHeight(100.0f);
jFrame = TestUtils.createJFrameWithCustomTitleBar(titleBar);
jFrame.setJMenuBar(menuBar);
jFrame.getRootPane().getLayeredPane().add(menuBar, (Object)(JLayeredPane.DEFAULT_LAYER - 1));
jFrame.setVisible(true);
jFrame.requestFocus();
}
private static void disposeUI() {
if (jFrame != null) {
jFrame.dispose();
}
}
}

View File

@@ -1,3 +1,4 @@
java/awt/Choice/ChoiceHandleMouseEvent.java
java/awt/Choice/ChoiceHandleMouseEvent_2.java JBR-7174 windows-all
java/awt/Graphics/XORPaint.java#id0 JBR-7499 windows-x64
java/awt/Toolkit/LWCToolkitInvokeAndWaitTest.java nobug macosx-all,linux-all,windows-all
@@ -122,9 +123,11 @@ javax/swing/JSpinner/4670051/DateFieldUnderCursorTest.java JBR-5397 macosx-all
java/awt/Button/DoubleActionEventTest.java JBR-6949 windows-x64
java/awt/Choice/ChoiceConsumeMouseEvents.java JBR-6951 windows-x64
java/awt/Choice/ChoiceFreezeTest.java JBR-6952 windows-x64
java/awt/Choice/ChoiceMouseEventOutbounds.java TBD windows-x64
java/awt/Choice/ChoicePopupLocation/ChoicePopupLocation.java JBR-6857,JBR-5505 macosx-all,windows-all
java/awt/Choice/RemoveAllShrinkTest/RemoveAllShrinkTest.java JBR-5510,8310487,JBR-6950 linux-5.18.2-arch1-1,linux-all,windows-x64
java/awt/Choice/ResizeAutoClosesChoice/ResizeAutoClosesChoice.java JBR-5510,JBR-5905 linux-5.18.2-arch1-1,linux-all,windows-x64
java/awt/Component/NativeInLightShow/NativeInLightShow.java JBR-7715 window-x64
java/awt/datatransfer/DragUnicodeBetweenJVMTest/DragUnicodeBetweenJVMTest.java JBR-5538 windows-x64
java/awt/dnd/Button2DragTest/Button2DragTest.java 8310490,JBR-5505 windows-all,linux-all
java/awt/dnd/DropActionChangedTest.java JBR-6757 windows-x64
@@ -140,6 +143,7 @@ java/awt/FullScreen/MultimonFullscreenTest/MultimonDeadlockTest.java JBR-5505 wi
java/awt/hidpi/DrawOnFrameGraphicsTest.java JBR-5505 windows-all
java/awt/im/4959409/bug4959409.java JBR-5505 windows-all
java/awt/im/memoryleak/InputContextMemoryLeakTest.java JBR-5505 windows-all
java/awt/image/VolatileImage/DrawBufImgOp.java
java/awt/LightweightDispatcher/LWDispatcherMemoryLeakTest.java JBR-5505 windows-all
java/awt/List/ItemEventTest/ItemEventTest.java JBR-5711,JBR-5505 windows-all,linux-all
java/awt/Mixing/AWT_Mixing/JComboBoxOverlapping.java 8049405,JBR-5510,JBR-5505 macosx-all,linux-5.18.2-arch1-1,windows-all

View File

@@ -1,3 +1,4 @@
java/awt/Graphics2D/DrawPrimitivesTest.java JBR-5620 macosx-aarch64
java/awt/image/multiresolution/MultiresolutionIconTest.java JBR-7146 macosx-12.7.4,macosx-12.7.5,macosx-12.7.6
java/awt/image/multiresolution/MultiResolutionJOptionPaneIconTest.java JBR-7146 macosx-12.7.4,macosx-12.7.5,macosx-12.7.6
java/awt/Paint/PaintNativeOnUpdate.java JBR-7146 macosx-12.7.4,macosx-12.7.5,macosx-12.7.6
@@ -9,7 +10,7 @@ java/awt/Window/MultiWindowApp/ChildAlwaysOnTopTest.java JBR-7312 macosx-all
java/awt/Window/ShapedAndTranslucentWindows/TranslucentChoice.java JBR-7146 macosx-12.7.4,macosx-12.7.5,macosx-12.7.6
java/awt/Window/WindowTitleVisibleTest/WindowTitleVisibleTest.java JBR-7146 macosx-12.7.4,macosx-12.7.5,macosx-12.7.6
javax/swing/JButton/8151303/PressedIconTest.java JBR-7146 macosx-12.7.4,macosx-12.7.5,macosx-12.7.6
javax/swing/JComponent/7154030/bug7154030.java JBR-7146 macosx-12.7.4,macosx-12.7.5,macosx-12.7.6
javax/swing/JComponent/7154030/bug7154030.java JBR-7713 macosx-aarch64
javax/swing/JInternalFrame/8160248/JInternalFrameDraggingTest.java JBR-7146 macosx-12.7.4,macosx-12.7.5,macosx-12.7.6
javax/swing/JInternalFrame/DockIconRepaint/DockIconRepaint.java JBR-7146 macosx-12.7.4,macosx-12.7.5,macosx-12.7.6
javax/swing/JSpinner/4670051/DateFieldUnderCursorTest.java JBR-7146 macosx-12.7.4,macosx-12.7.5,macosx-12.7.6

View File

@@ -21,7 +21,7 @@ java/awt/GraphicsDevice/DisplayModes/UnknownRefrshRateTest.java JBR-6336 macosx-
java/awt/GraphicsDevice/IncorrectDisplayModeExitFullscreen.java JBR-6336 macosx-all
java/awt/datatransfer/Clipboard/GetContentsInterruptedTest.java JBR-5086 linux-5.15.0-46-generic Ubuntu 20.04
java/awt/Robot/MouseLocationOnScreen/MouseLocationOnScreen.java JBR-5390 macosx-all,linux-all
java/awt/Robot/NonEmptyErrorStream.java JBR-5442,JBR-5510 JBR-5442,linux-5.18.2-arch1-1
java/awt/Robot/NonEmptyErrorStream.java 8340330,JBR-5442,JBR-5510,JBR-5442 macosx-15.0,linux-5.18.2-arch1-1
java/awt/Robot/RobotMoveMultiscreen.java JBR-5442 linux-all
java/awt/Robot/SpuriousMouseEvents/SpuriousMouseEvents.java JBR-6572 linux-all
java/awt/Toolkit/AWTEventListenerProxyTest/AWTEventListenerProxyTest.java JBR-6065 windows-all

View File

@@ -154,6 +154,7 @@ java/awt/Frame/8158918/SetExtendedState.java JBR-6408 linux-all
java/awt/Frame/GetGraphicsStressTest/GetGraphicsStressTest.java JBR-6509 generic-all
java/awt/Frame/MaximizedUndecorated/MaximizedUndecorated.java 8022302 generic-all
java/awt/Frame/MaximizedToIconified/MaximizedToIconified.java 8296972 macosx-all
java/awt/Frame/MaximizedToMaximized/MaximizedToMaximized.java 8340595 macosx-15.0
java/awt/Frame/MaximizedToOppositeScreen/MaximizedToOppositeScreenBig.java JBR-5303 windows-all
java/awt/Frame/RestoreToOppositeScreen/RestoreToOppositeScreen.java 8286840 linux-all
java/awt/dnd/AcceptDropMultipleTimes/AcceptDropMultipleTimes.java JBR-4880,JBR-6683 windows-all,linux-all
@@ -257,6 +258,7 @@ java/awt/FullScreen/FullscreenWindowProps/FullscreenWindowProps.java JBR-4275,JB
java/awt/FullScreen/NoResizeEventOnDMChangeTest/NoResizeEventOnDMChangeTest.java 7188711,8253184 linux-all,windows-all
java/awt/FullScreen/UninitializedDisplayModeChangeTest/UninitializedDisplayModeChangeTest.java 7188711,8273617,JBR-4880,8253184 macosx-all,linux-all,windows-all
java/awt/Focus/8013611/JDK8013611.java 8175366 windows-all,macosx-all
java/awt/Focus/6401036/InputVerifierTest2.java JBR-7537 linux-aarch64
java/awt/Focus/6981400/Test1.java 8029675,JBR-5510 windows-all,macosx-all,linux-5.18.2-arch1-1
java/awt/Focus/6981400/Test3.java 8173264 generic-all
java/awt/Focus/8000326/SetFocusTraversalKeysEnabledTest.java JBR-4997,JBR-5729 windows-all,linux-all
@@ -328,7 +330,7 @@ java/awt/Window/ShapedAndTranslucentWindows/Translucent.java 8222328 windows-all
java/awt/Window/AlwaysOnTop/AlwaysOnTopEvenOfWindow.java JBR-6686 linux-aarch64
java/awt/Window/AlwaysOnTop/AutoTestOnTop.java 6847593,8253184 linux-all,windows-all
java/awt/Window/AlwaysOnTop/TestAlwaysOnTopBeforeShow.java JBR-6661 windows-all
java/awt/Window/Grab/GrabTest.java 8253184,JBR-6922 windows-all,linux-all
java/awt/Window/Grab/GrabTest.java 8253184,JBR-6922,8317288 windows-all,linux-all,macosx-all
java/awt/Window/GrabSequence/GrabSequence.java 6848409 macosx-all,linux-all
java/awt/Window/LocationAtScreenCorner/LocationAtScreenCorner.java 8203371 linux-all
java/awt/FontClass/FontAccess.java JBR-5225 windows-all
@@ -356,7 +358,6 @@ sun/java2d/SunGraphics2D/DrawImageBilinear.java 8297175 linux-all
java/awt/Graphics2D/CopyAreaOOB.java JBR-5354 macosx-all,windows-all,linux-5.18.2-arch1-1
java/awt/Graphics2D/DrawString/DisposerTest.java JBR-5010,JBR-5510 linux-aarch64,linux-5.18.2-arch1-1
java/awt/Graphics2D/DrawString/DrawRotatedStringUsingRotatedFont.java 8266283 generic-all
java/awt/Graphics2D/DrawString/TextRenderingTest.java JBR-4260 macosx-11.7.1
java/awt/Graphics2D/ScaledTransform/ScaledTransform.java 8277240 linux-all
sun/java2d/SunGraphics2D/PolyVertTest.java 6986565 generic-all
sun/java2d/SunGraphics2D/SimplePrimQuality.java 6992007 generic-all
@@ -389,6 +390,7 @@ java/awt/Frame/MiscUndecorated/ActiveSwingWindowTest.java JBR-5210 windows-all
java/awt/Frame/MiscUndecorated/FrameCloseTest.java JBR-5210 windows-all
java/awt/Frame/MiscUndecorated/RepaintTest.java 8266244,JBR-5786 macosx-aarch64,generic-all
java/awt/Robot/HiDPIMouseClick/HiDPIRobotMouseClick.java 8253184 windows-all
java/awt/Robot/NonEmptyErrorStream.java 8340330,JBR-5510 macosx-15.0,linux-5.18.2-arch1-1
java/awt/Robot/RobotExtraButton/RobotExtraButton.java JBR-6554 linux-all
java/awt/Modal/FileDialog/FileDialogAppModal1Test.java 7186009,8253184 macosx-all,windows-all
java/awt/Modal/FileDialog/FileDialogAppModal2Test.java 7186009,8253184 macosx-all,windows-all
@@ -1441,7 +1443,6 @@ java/awt/Choice/ChoiceGeneratesItemEvents.java JBR-5510 linux-5.18.2-arch1-1
java/awt/Choice/ChoiceStaysOpenedOnTAB.java JBR-5510 linux-5.18.2-arch1-1
java/awt/Graphics/XORPaint.java#id2 JBR-5510 linux-5.18.2-arch1-1
java/awt/Robot/HiDPIScreenCapture/ScreenCaptureGtkTest.java JBR-5510 linux-5.18.2-arch1-1
java/awt/Robot/NonEmptyErrorStream.java JBR-5510 linux-5.18.2-arch1-1
javax/swing/JComboBox/TestComboBoxComponentRendering.java JBR-5510 linux-5.18.2-arch1-1
javax/swing/JMenu/TestDisabledMenuForegroundColor.java JBR-5510 linux-5.18.2-arch1-1
javax/swing/JPasswordField/TestSelectedTextBackgroundColor.java JBR-5510 linux-5.18.2-arch1-1

View File

@@ -54,6 +54,7 @@ java/awt/PopupMenu/PopupMenuLocation.java 8238720,JBR-5071 windows-all,macosx-al
java/awt/Window/AlwaysOnTop/SyncAlwaysOnTopFieldTest.java JBR-6845 linux-all
java/awt/Window/TopLevelLocation/TopLevelLocation.java JBR-5799 windows-all
java/awt/Window/WindowSizeDifferentScreens/WindowSizeDifferentScreens.java JBR-5513 linux-all
javax/swing/JComboBox/TestComboBoxComponentRendering.java JBR-6110 linux-x64
javax/swing/JComponent/7154030/bug7154030.java JBR-6134 windows-x64
javax/swing/JPopupMenu/6580930/bug6580930.java JBR-5071 linux-all
javax/swing/JSlider/6848475/bug6848475.java JBR-7329,JBR-7472 windows-all