mirror of
https://github.com/JetBrains/JetBrainsRuntime.git
synced 2025-12-06 09:29:38 +01:00
Compare commits
62 Commits
kharitonov
...
wayland-de
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fd009fe88e | ||
|
|
738f762d3e | ||
|
|
ef9172f50e | ||
|
|
ed8a7ad702 | ||
|
|
6238545ead | ||
|
|
9bda49c60c | ||
|
|
a8cdbbfb8d | ||
|
|
2d7c8037fa | ||
|
|
b51d9559b0 | ||
|
|
f410ae50af | ||
|
|
7ebdeb734b | ||
|
|
f7821f8d45 | ||
|
|
28a609691a | ||
|
|
2f47290c19 | ||
|
|
ba1c4a464d | ||
|
|
466f1dd0fd | ||
|
|
fd37168007 | ||
|
|
607dcb78a8 | ||
|
|
f9f5359629 | ||
|
|
6b19a747f7 | ||
|
|
7d7d9f9bf5 | ||
|
|
66c28a3606 | ||
|
|
e2321b5594 | ||
|
|
5813b10e65 | ||
|
|
d121a93cb1 | ||
|
|
f7638abee2 | ||
|
|
55b1310c24 | ||
|
|
bfe03f4bd1 | ||
|
|
15a09a1564 | ||
|
|
858380c36d | ||
|
|
a81b44d79d | ||
|
|
7f9aee3c7f | ||
|
|
0478a24483 | ||
|
|
c113772448 | ||
|
|
757194800f | ||
|
|
b9c4ac35ec | ||
|
|
adf8d95f7b | ||
|
|
b2986aef46 | ||
|
|
cea81933d9 | ||
|
|
6779e2c59b | ||
|
|
4b7c5f62a9 | ||
|
|
df204bb882 | ||
|
|
0d7fdcf415 | ||
|
|
73c8c50262 | ||
|
|
16cacd0b55 | ||
|
|
19496fcef9 | ||
|
|
9cb4769361 | ||
|
|
dc36d0afaf | ||
|
|
20ca5a41f4 | ||
|
|
e864ea8469 | ||
|
|
b3e31866ec | ||
|
|
4cdce4b44a | ||
|
|
2187957e7e | ||
|
|
14aa544c86 | ||
|
|
de5214531a | ||
|
|
ea6f74d64f | ||
|
|
56e174709b | ||
|
|
c5103ff4a8 | ||
|
|
49c103709e | ||
|
|
4b21d041d8 | ||
|
|
c1ee18adfb | ||
|
|
693e16b0a1 |
53
README.md
53
README.md
@@ -1,5 +1,58 @@
|
||||
# Welcome to the JDK!
|
||||
|
||||
## Wakefield
|
||||
This is a temporary section created to host information on the
|
||||
[Wakefield](https://wiki.openjdk.java.net/display/wakefield) project.
|
||||
|
||||
### Building
|
||||
There are two addition `configure` arguments:
|
||||
```
|
||||
--with-wayland specify prefix directory for the wayland package
|
||||
(expecting the headers under PATH/include)
|
||||
--with-wayland-include specify directory for the wayland include files
|
||||
```
|
||||
As usual, there should be no need to specify those explicitly unless you're doing
|
||||
something tricky.
|
||||
However, a variant of `libwayland-dev` needs to be installed on the build system.
|
||||
|
||||
### Running
|
||||
Make sure your system is configured such that `libwayland` can find the socket to connect to;
|
||||
usually this means that the environment variable `WAYLAND_DISPLAY` is set to something
|
||||
sensible. Then add this argument to `java`
|
||||
```
|
||||
-Dawt.toolkit.name=WLToolkit
|
||||
```
|
||||
|
||||
### Testing
|
||||
Testing that involves `Robot` is done inside a [Weston](https://gitlab.freedesktop.org/wayland/weston/)
|
||||
instance with a special module loaded called `libwakefield`
|
||||
that provides the necessary functionality. The Wayland-specific tests are therefore executed with a dedicated test driver
|
||||
`test/jdk/java/awt/wakefield/WakefieldTestDriver.java`. The driver also provides an easy
|
||||
way to run the test in several configurations with a different size and even number
|
||||
of "outputs" (monitors).
|
||||
|
||||
To run the Wayland-specific tests, perform these steps:
|
||||
* Install Weston version 9 (earlier versions are known NOT to work).
|
||||
* Obtain `libwakefield.so` either by building from source (available under
|
||||
`src/java.desktop/share/native/libwakefield` and not integrated into the rest of the
|
||||
build infrastructure; see `README.md` there)
|
||||
or by fetching the latest pre-built `x64` binary
|
||||
```
|
||||
wget https://github.com/mkartashev/wakefield/raw/main/libwakefield.so
|
||||
```
|
||||
* Set `LIBWAKEFIELD` environment variable to the full path to `libwakefield.so`
|
||||
```
|
||||
export LIBWAKEFIELD=/tmp/wakefield-testing/libwakefield.so
|
||||
```
|
||||
* Run `jtreg` like so
|
||||
```
|
||||
jtreg -e:XDG_RUNTIME_DIR -e:LIBWAKEFIELD -testjdk:... test/jdk/java/awt/wakefield/
|
||||
```
|
||||
|
||||
This was verified to work in `Ubuntu 21.10`.
|
||||
This does NOT work in `Ubuntu 21.04` or `Fedora 34`.
|
||||
|
||||
## Generic Info (not Wakefield-specific)
|
||||
For build instructions please see the
|
||||
[online documentation](https://openjdk.org/groups/build/doc/building.html),
|
||||
or either of these files:
|
||||
|
||||
162
make/autoconf/lib-wayland.m4
Normal file
162
make/autoconf/lib-wayland.m4
Normal file
@@ -0,0 +1,162 @@
|
||||
#
|
||||
# Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
# Copyright (c) 2023, JetBrains s.r.o.. All rights reserved.
|
||||
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
#
|
||||
# This code is free software; you can redistribute it and/or modify it
|
||||
# under the terms of the GNU General Public License version 2 only, as
|
||||
# published by the Free Software Foundation. Oracle designates this
|
||||
# particular file as subject to the "Classpath" exception as provided
|
||||
# by Oracle in the LICENSE file that accompanied this code.
|
||||
#
|
||||
# This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
# version 2 for more details (a copy is included in the LICENSE file that
|
||||
# accompanied this code).
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License version
|
||||
# 2 along with this work; if not, write to the Free Software Foundation,
|
||||
# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
#
|
||||
# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
# or visit www.oracle.com if you need additional information or have any
|
||||
# questions.
|
||||
#
|
||||
|
||||
################################################################################
|
||||
# Setup wayland
|
||||
################################################################################
|
||||
AC_DEFUN_ONCE([LIB_SETUP_WAYLAND],
|
||||
[
|
||||
AC_ARG_WITH(wayland, [AS_HELP_STRING([--with-wayland],
|
||||
[specify prefix directory for the wayland package
|
||||
(expecting the headers under PATH/include)])])
|
||||
AC_ARG_WITH(wayland-include, [AS_HELP_STRING([--with-wayland-include],
|
||||
[specify directory for the wayland include files])])
|
||||
|
||||
if test "x$NEEDS_LIB_WAYLAND" = xfalse; then
|
||||
if (test "x${with_wayland}" != x && test "x${with_wayland}" != xno) || \
|
||||
(test "x${with_wayland_include}" != x && test "x${with_wayland_include}" != xno); then
|
||||
AC_MSG_WARN([[wayland not used, so --with-wayland[-*] is ignored]])
|
||||
fi
|
||||
WAYLAND_CFLAGS=
|
||||
WAYLAND_LIBS=
|
||||
VULKAN_FLAGS=
|
||||
VULKAN_ENABLED=false
|
||||
else
|
||||
WAYLAND_FOUND=no
|
||||
|
||||
if test "x${with_wayland}" = xno || test "x${with_wayland_include}" = xno; then
|
||||
AC_MSG_ERROR([It is not possible to disable the use of wayland. Remove the --without-wayland option.])
|
||||
fi
|
||||
|
||||
if test "x${with_wayland}" != x; then
|
||||
AC_MSG_CHECKING([for wayland headers])
|
||||
if test -s "${with_wayland}/include/wayland-client.h" && test -s "${with_wayland}/include/wayland-cursor.h"; then
|
||||
WAYLAND_CFLAGS="-I${with_wayland}/include"
|
||||
WAYLAND_LIBS="-L${with_wayland}/lib -lwayland-client -lwayland-cursor"
|
||||
|
||||
WAYLAND_FOUND=yes
|
||||
AC_MSG_RESULT([$WAYLAND_FOUND])
|
||||
else
|
||||
AC_MSG_ERROR([Can't find 'include/wayland-client.h' and 'include/wayland-cursor.h' under ${with_wayland} given with the --with-wayland option.])
|
||||
fi
|
||||
fi
|
||||
if test "x${with_wayland_include}" != x; then
|
||||
AC_MSG_CHECKING([for wayland headers])
|
||||
if test -s "${with_wayland_include}/wayland-client.h" && test -s "${with_wayland_include}/wayland-cursor.h"; then
|
||||
WAYLAND_CFLAGS="-I${with_wayland_include}"
|
||||
WAYLAND_FOUND=yes
|
||||
AC_MSG_RESULT([$WAYLAND_FOUND])
|
||||
else
|
||||
AC_MSG_ERROR([Can't find 'wayland-client.h' and 'wayland-cursor.h' under ${with_wayland_include} given with the --with-wayland-include option.])
|
||||
fi
|
||||
fi
|
||||
if test "x$WAYLAND_FOUND" = xno; then
|
||||
# Are the wayland headers installed in the default /usr/include location?
|
||||
AC_CHECK_HEADERS([wayland-client.h wayland-cursor.h],
|
||||
[ WAYLAND_FOUND=yes ],
|
||||
[ WAYLAND_FOUND=no; break ]
|
||||
)
|
||||
if test "x$WAYLAND_FOUND" = xyes; then
|
||||
WAYLAND_CFLAGS=
|
||||
WAYLAND_LIBS="-lwayland-client -lwayland-cursor"
|
||||
DEFAULT_WAYLAND=yes
|
||||
fi
|
||||
fi
|
||||
if test "x$WAYLAND_FOUND" = xno; then
|
||||
HELP_MSG_MISSING_DEPENDENCY([wayland])
|
||||
AC_MSG_ERROR([Could not find wayland! $HELP_MSG ])
|
||||
fi
|
||||
|
||||
|
||||
# Checking for vulkan sdk
|
||||
|
||||
AC_ARG_WITH(vulkan, [AS_HELP_STRING([--with-vulkan],
|
||||
[specify whether we use vulkan])])
|
||||
|
||||
AC_ARG_WITH(vulkan-include, [AS_HELP_STRING([--with-vulkan-include],
|
||||
[specify directory for the vulkan include files])])
|
||||
|
||||
if test "x$SUPPORTS_LIB_VULKAN" = xfalse; then
|
||||
|
||||
if (test "x${with_vulkan}" != x && test "x${with_vulkan}" != xno) || \
|
||||
(test "x${with_vulkan_include}" != x && test "x${with_vulkan_include}" != xno); then
|
||||
AC_MSG_WARN([[vulkan not used, so --with-vulkan-include is ignored]])
|
||||
fi
|
||||
VULKAN_FLAGS=
|
||||
VULKAN_ENABLED=false
|
||||
else
|
||||
# Do not build vulkan rendering pipeline by default
|
||||
if (test "x${with_vulkan}" = x && test "x${with_vulkan_include}" = x) || \
|
||||
test "x${with_vulkan}" = xno || test "x${with_vulkan_include}" = xno ; then
|
||||
VULKAN_FLAGS=
|
||||
VULKAN_ENABLED=false
|
||||
else
|
||||
VULKAN_FOUND=no
|
||||
|
||||
if test "x${with_vulkan_include}" != x; then
|
||||
AC_CHECK_HEADERS([${with_vulkan_include}/include/vulkan/vulkan.h],
|
||||
[ VULKAN_FOUND=yes
|
||||
VULKAN_FLAGS="-DVK_USE_PLATFORM_WAYLAND_KHR -I${with_vulkan_include}/include -DVULKAN_ENABLED"
|
||||
VULKAN_ENABLED=true
|
||||
],
|
||||
[ AC_MSG_ERROR([Can't find 'vulkan/vulkan.h' under '${with_vulkan_include}']) ]
|
||||
)
|
||||
fi
|
||||
|
||||
if test "x$VULKAN_FOUND" = xno; then
|
||||
# Check vulkan sdk location
|
||||
AC_CHECK_HEADERS([$VULKAN_SDK/include/vulkan/vulkan.h],
|
||||
[ VULKAN_FOUND=yes
|
||||
VULKAN_FLAGS="-DVK_USE_PLATFORM_WAYLAND_KHR -I${VULKAN_SDK}/include -DVULKAN_ENABLED"
|
||||
VULKAN_ENABLED=true
|
||||
],
|
||||
[ VULKAN_FOUND=no; break ]
|
||||
)
|
||||
fi
|
||||
|
||||
if test "x$VULKAN_FOUND" = xno; then
|
||||
# Check default /usr/include location
|
||||
AC_CHECK_HEADERS([vulkan/vulkan.h],
|
||||
[ VULKAN_FOUND=yes
|
||||
VULKAN_FLAGS="-DVK_USE_PLATFORM_WAYLAND_KHR -DVULKAN_ENABLED"
|
||||
VULKAN_ENABLED=true
|
||||
],
|
||||
[ VULKAN_FOUND=no; break ]
|
||||
)
|
||||
fi
|
||||
|
||||
if test "x$VULKAN_FOUND" = xno; then
|
||||
HELP_MSG_MISSING_DEPENDENCY([vulkan])
|
||||
AC_MSG_ERROR([Could not find vulkan! $HELP_MSG ])
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
AC_SUBST(VULKAN_FLAGS)
|
||||
AC_SUBST(VULKAN_ENABLED)
|
||||
AC_SUBST(WAYLAND_CFLAGS)
|
||||
AC_SUBST(WAYLAND_LIBS)
|
||||
])
|
||||
@@ -33,6 +33,7 @@ m4_include([lib-freetype.m4])
|
||||
m4_include([lib-hsdis.m4])
|
||||
m4_include([lib-std.m4])
|
||||
m4_include([lib-x11.m4])
|
||||
m4_include([lib-wayland.m4])
|
||||
|
||||
m4_include([lib-tests.m4])
|
||||
|
||||
@@ -41,14 +42,23 @@ m4_include([lib-tests.m4])
|
||||
################################################################################
|
||||
AC_DEFUN_ONCE([LIB_DETERMINE_DEPENDENCIES],
|
||||
[
|
||||
# Check if X11 is needed
|
||||
# Check if X11, wayland and vulkan is needed
|
||||
if test "x$OPENJDK_TARGET_OS" = xwindows || test "x$OPENJDK_TARGET_OS" = xmacosx; then
|
||||
# No X11 support on windows or macosx
|
||||
# No X11 and wayland support on windows or macosx
|
||||
NEEDS_LIB_X11=false
|
||||
NEEDS_LIB_WAYLAND=false
|
||||
SUPPORTS_LIB_VULKAN=false
|
||||
elif test "x$ENABLE_HEADLESS_ONLY" = xtrue; then
|
||||
# No X11 support needed when building headless only
|
||||
NEEDS_LIB_X11=false
|
||||
NEEDS_LIB_WAYLAND=false
|
||||
SUPPORTS_LIB_VULKAN=false
|
||||
else
|
||||
# All other instances need X11, even if building headless only, libawt still
|
||||
# All other instances need X11 and wayland, even if building headless only, libawt still
|
||||
# needs X11 headers.
|
||||
NEEDS_LIB_X11=true
|
||||
NEEDS_LIB_WAYLAND=true
|
||||
SUPPORTS_LIB_VULKAN=true
|
||||
fi
|
||||
|
||||
# Check if fontconfig is needed
|
||||
@@ -105,6 +115,7 @@ AC_DEFUN_ONCE([LIB_SETUP_LIBRARIES],
|
||||
LIB_SETUP_LIBFFI
|
||||
LIB_SETUP_MISC_LIBS
|
||||
LIB_SETUP_X11
|
||||
LIB_SETUP_WAYLAND
|
||||
|
||||
LIB_TESTS_SETUP_GTEST
|
||||
|
||||
|
||||
@@ -453,7 +453,10 @@ endif
|
||||
# Necessary additional compiler flags to compile X11
|
||||
X_CFLAGS:=@X_CFLAGS@
|
||||
X_LIBS:=@X_LIBS@
|
||||
|
||||
WAYLAND_CFLAGS:=@WAYLAND_CFLAGS@
|
||||
WAYLAND_LIBS:=@WAYLAND_LIBS@
|
||||
VULKAN_FLAGS:=@VULKAN_FLAGS@
|
||||
VULKAN_ENABLED:=@VULKAN_ENABLED@
|
||||
# The lowest required version of macosx
|
||||
MACOSX_VERSION_MIN=@MACOSX_VERSION_MIN@
|
||||
# The highest allowed version of macosx
|
||||
|
||||
@@ -190,7 +190,7 @@ ifeq ($(call isTargetOs, windows macosx), false)
|
||||
common/font \
|
||||
#
|
||||
|
||||
LIBAWT_XAWT_EXCLUDES := medialib debug
|
||||
LIBAWT_XAWT_EXCLUDES := medialib debug wl vulkan
|
||||
|
||||
LIBAWT_XAWT_EXTRA_HEADER_DIRS := \
|
||||
$(LIBAWT_DEFAULT_HEADER_DIRS) \
|
||||
@@ -262,6 +262,97 @@ endif
|
||||
|
||||
################################################################################
|
||||
|
||||
ifeq ($(call isTargetOs, windows macosx), false)
|
||||
ifeq ($(ENABLE_HEADLESS_ONLY), false)
|
||||
|
||||
LIBAWT_WLAWT_EXTRA_SRC := \
|
||||
common/awt \
|
||||
common/java2d \
|
||||
common/font \
|
||||
#
|
||||
|
||||
LIBAWT_WLAWT_EXCLUDES := medialib debug opengl x11
|
||||
LIBAWT_WLAWT_EXCLUDE_FILES := common/awt/X11Color.c common/awt/awt_Font.c
|
||||
|
||||
# Substitute Vulkan with stubs if disabled.
|
||||
ifeq ($(VULKAN_ENABLED), false)
|
||||
LIBAWT_WLAWT_EXCLUDES += vulkan
|
||||
LIBAWT_WLAWT_EXTRA_FILES += $(TOPDIR)/src/$(MODULE)/share/native/common/java2d/vulkan/VKStubs.c
|
||||
endif
|
||||
|
||||
LIBAWT_WLAWT_EXTRA_HEADER_DIRS := \
|
||||
$(LIBAWT_DEFAULT_HEADER_DIRS) \
|
||||
libawt_wlawt/awt \
|
||||
include \
|
||||
common/awt/debug \
|
||||
common/awt/systemscale \
|
||||
common/font \
|
||||
common/java2d/wl \
|
||||
common/java2d/vulkan \
|
||||
#
|
||||
|
||||
# Enable 'wakefield' extension for java.awt.Robot support
|
||||
WAKEFIELD_ROBOT_CFLAGS=-DWAKEFIELD_ROBOT
|
||||
|
||||
LIBAWT_WLAWT_CFLAGS += -DWLAWT \
|
||||
$(WAKEFIELD_ROBOT_CFLAGS) \
|
||||
$(FONTCONFIG_CFLAGS) \
|
||||
$(VULKAN_FLAGS) \
|
||||
$(CUPS_CFLAGS)
|
||||
|
||||
LIBAWT_WLAWT_CXXFLAGS += $(VULKAN_FLAGS)
|
||||
|
||||
LIBAWT_WLAWT_LIBS := $(LIBM) -lawt $(WAYLAND_LIBS) $(LIBDL) -ljava -ljvm -lrt
|
||||
|
||||
ifeq ($(call isTargetOs, linux), true)
|
||||
LIBAWT_WLAWT_LIBS += -lpthread
|
||||
endif
|
||||
|
||||
ifeq ($(TOOLCHAIN_TYPE), gcc)
|
||||
# Turn off all warnings for the following files since they contain warnings
|
||||
# that cannot be turned of individually.
|
||||
# redefining a macro
|
||||
BUILD_LIBAWT_WLAWT_awt_Font.c_CFLAGS := -w
|
||||
# initializing a declared 'extern'
|
||||
BUILD_LIBAWT_WLAWT_debug_mem.c_CFLAGS := -w
|
||||
endif
|
||||
|
||||
$(eval $(call SetupJdkLibrary, BUILD_LIBAWT_WLAWT, \
|
||||
NAME := awt_wlawt, \
|
||||
EXCLUDE_FILES := $(LIBAWT_WLAWT_EXCLUDE_FILES), \
|
||||
EXTRA_FILES := $(LIBAWT_WLAWT_EXTRA_FILES), \
|
||||
EXTRA_SRC := $(LIBAWT_WLAWT_EXTRA_SRC), \
|
||||
EXTRA_HEADER_DIRS := $(LIBAWT_WLAWT_EXTRA_HEADER_DIRS), \
|
||||
EXCLUDES := $(LIBAWT_WLAWT_EXCLUDES), \
|
||||
OPTIMIZATION := LOW, \
|
||||
TOOLCHAIN := TOOLCHAIN_LINK_CXX, \
|
||||
CFLAGS := $(CFLAGS_JDKLIB) $(LIBAWT_WLAWT_CFLAGS), \
|
||||
CXXFLAGS := $(CXXFLAGS_JDKLIB) $(LIBAWT_WLAWT_CXXFLAGS), \
|
||||
WARNINGS_AS_ERRORS_xlc := false, \
|
||||
DISABLED_WARNINGS_C_gcc := type-limits pointer-to-int-cast \
|
||||
unused-result maybe-uninitialized format \
|
||||
format-security int-to-pointer-cast parentheses \
|
||||
implicit-fallthrough undef unused-function, \
|
||||
DISABLED_WARNINGS_CXX_gcc := undef, \
|
||||
DISABLED_WARNINGS_clang := parentheses format undef \
|
||||
logical-op-parentheses format-nonliteral int-conversion, \
|
||||
LDFLAGS := $(LDFLAGS_JDKLIB) \
|
||||
$(call SET_SHARED_LIBRARY_ORIGIN) \
|
||||
-L$(INSTALL_LIBRARIES_HERE), \
|
||||
LIBS := $(LIBAWT_WLAWT_LIBS), \
|
||||
))
|
||||
|
||||
$(BUILD_LIBAWT_WLAWT): $(call FindLib, java.base, java)
|
||||
|
||||
$(BUILD_LIBAWT_WLAWT): $(BUILD_LIBAWT)
|
||||
|
||||
TARGETS += $(BUILD_LIBAWT_WLAWT)
|
||||
|
||||
endif
|
||||
endif
|
||||
|
||||
################################################################################
|
||||
|
||||
# The fast floor code loses precision.
|
||||
LCMS_CFLAGS=-DCMS_DONT_USE_FAST_FLOOR
|
||||
|
||||
@@ -360,7 +451,7 @@ ifeq ($(call isTargetOs, windows macosx), false)
|
||||
$(TOPDIR)/src/$(MODULE)/$(OPENJDK_TARGET_OS_TYPE)/native/common/awt \
|
||||
#
|
||||
|
||||
LIBAWT_HEADLESS_EXCLUDES := medialib
|
||||
LIBAWT_HEADLESS_EXCLUDES := medialib wl vulkan
|
||||
|
||||
LIBAWT_HEADLESS_EXTRA_HEADER_DIRS := \
|
||||
$(LIBAWT_DEFAULT_HEADER_DIRS) \
|
||||
@@ -492,13 +583,9 @@ ifneq ($(filter $(TOOLCHAIN_TYPE), gcc clang), )
|
||||
endif
|
||||
|
||||
ifeq ($(call isTargetOs, windows), true)
|
||||
LIBFONTMANAGER_EXCLUDE_FILES += X11FontScaler.c \
|
||||
X11TextRenderer.c
|
||||
LIBFONTMANAGER_OPTIMIZATION := HIGHEST
|
||||
else ifeq ($(call isTargetOs, macosx), true)
|
||||
LIBFONTMANAGER_EXCLUDE_FILES += X11FontScaler.c \
|
||||
X11TextRenderer.c \
|
||||
fontpath.c \
|
||||
LIBFONTMANAGER_EXCLUDE_FILES += fontpath.c \
|
||||
lcdglyph.c
|
||||
else
|
||||
LIBFONTMANAGER_EXCLUDE_FILES += fontpath.c \
|
||||
@@ -523,7 +610,7 @@ $(eval $(call SetupJdkLibrary, BUILD_LIBFONTMANAGER, \
|
||||
EXTRA_HEADER_DIRS := $(LIBFONTMANAGER_EXTRA_HEADER_DIRS), \
|
||||
EXTRA_SRC := $(LIBFONTMANAGER_EXTRA_SRC), \
|
||||
WARNINGS_AS_ERRORS_xlc := false, \
|
||||
DISABLED_WARNINGS_gcc := $(HARFBUZZ_DISABLED_WARNINGS_gcc), \
|
||||
DISABLED_WARNINGS_gcc := $(HARFBUZZ_DISABLED_WARNINGS_gcc) unused-result, \
|
||||
DISABLED_WARNINGS_CXX_gcc := $(HARFBUZZ_DISABLED_WARNINGS_CXX_gcc), \
|
||||
DISABLED_WARNINGS_clang := $(HARFBUZZ_DISABLED_WARNINGS_clang), \
|
||||
DISABLED_WARNINGS_microsoft := $(HARFBUZZ_DISABLED_WARNINGS_microsoft), \
|
||||
@@ -553,6 +640,51 @@ TARGETS += $(BUILD_LIBFONTMANAGER)
|
||||
|
||||
################################################################################
|
||||
|
||||
ifeq ($(call isTargetOs, windows macosx), false)
|
||||
ifeq ($(ENABLE_HEADLESS_ONLY), false)
|
||||
|
||||
LIBFONTMANAGER_XAWT_EXCLUDE_FILES := $(LIBFONTMANAGER_EXCLUDE_FILES)
|
||||
LIBFONTMANAGER_XAWT_CFLAGS := $(LIBFONTMANAGER_CFLAGS)
|
||||
LIBFONTMANAGER_XAWT_OPTIMIZATION := $(LIBFONTMANAGER_OPTIMIZATION)
|
||||
LIBFONTMANAGER_XAWT_EXTRA_HEADER_DIRS := $(LIBFONTMANAGER_EXTRA_HEADER_DIRS) \
|
||||
libfontmanager
|
||||
LIBFONTMANAGER_XAWT_EXTRA_SRC :=
|
||||
|
||||
$(eval $(call SetupJdkLibrary, BUILD_LIBFONTMANAGER_XAWT, \
|
||||
NAME := fontmanager_xawt, \
|
||||
EXCLUDE_FILES := $(LIBFONTMANAGER_XAWT_EXCLUDE_FILES) \
|
||||
AccelGlyphCache.c, \
|
||||
TOOLCHAIN := TOOLCHAIN_LINK_CXX, \
|
||||
CFLAGS := $(CFLAGS_JDKLIB) $(LIBFONTMANAGER_XAWT_CFLAGS), \
|
||||
CXXFLAGS := $(CXXFLAGS_JDKLIB) $(LIBFONTMANAGER_XAWT_CFLAGS), \
|
||||
OPTIMIZATION := $(LIBFONTMANAGER_XAWT_OPTIMIZATION), \
|
||||
CFLAGS_windows = -DCC_NOEX, \
|
||||
EXTRA_HEADER_DIRS := $(LIBFONTMANAGER_XAWT_EXTRA_HEADER_DIRS), \
|
||||
EXTRA_SRC := $(LIBFONTMANAGER_XAWT_EXTRA_SRC), \
|
||||
WARNINGS_AS_ERRORS_xlc := false, \
|
||||
DISABLED_WARNINGS_gcc := $(HARFBUZZ_DISABLED_WARNINGS_gcc), \
|
||||
DISABLED_WARNINGS_CXX_gcc := $(HARFBUZZ_DISABLED_WARNINGS_CXX_gcc), \
|
||||
DISABLED_WARNINGS_clang := $(HARFBUZZ_DISABLED_WARNINGS_clang), \
|
||||
DISABLED_WARNINGS_microsoft := $(HARFBUZZ_DISABLED_WARNINGS_microsoft), \
|
||||
LDFLAGS := $(subst -Xlinker -z -Xlinker defs,, \
|
||||
$(subst -Wl$(COMMA)-z$(COMMA)defs,,$(LDFLAGS_JDKLIB))) $(LDFLAGS_CXX_JDK) \
|
||||
$(call SET_SHARED_LIBRARY_ORIGIN), \
|
||||
LDFLAGS_unix := -L$(INSTALL_LIBRARIES_HERE), \
|
||||
LDFLAGS_aix := -Wl$(COMMA)-berok, \
|
||||
LIBS := $(BUILD_LIBFONTMANAGER_FONTLIB), \
|
||||
LIBS_unix := -lfontmanager -lawt -ljava -ljvm $(LIBM) $(LIBCXX), \
|
||||
))
|
||||
|
||||
$(BUILD_LIBFONTMANAGER_XAWT): $(BUILD_LIBFONTMANAGER)
|
||||
$(BUILD_LIBFONTMANAGER_XAWT): $(BUILD_LIBAWT_XAWT)
|
||||
|
||||
TARGETS += $(BUILD_LIBFONTMANAGER_XAWT)
|
||||
|
||||
endif
|
||||
endif
|
||||
|
||||
################################################################################
|
||||
|
||||
ifeq ($(call isTargetOs, windows), true)
|
||||
|
||||
LIBJAWT_CFLAGS := -EHsc -DUNICODE -D_UNICODE
|
||||
|
||||
@@ -4083,10 +4083,20 @@ public class Window extends Container implements Accessible {
|
||||
|
||||
static {
|
||||
AWTAccessor.setWindowAccessor(new AWTAccessor.WindowAccessor() {
|
||||
private static final boolean isWLToolkit = Toolkit.getDefaultToolkit()
|
||||
.getClass().getName().equals("sun.awt.wl.WLToolkit");
|
||||
public void updateWindow(Window window) {
|
||||
window.updateWindow();
|
||||
}
|
||||
|
||||
public boolean needUpdateWindowAfterPaint(Window window) {
|
||||
return window != null && isWLToolkit;
|
||||
}
|
||||
|
||||
public boolean needUpdateWindow(Window window) {
|
||||
return window != null && (isWLToolkit || !window.isOpaque());
|
||||
}
|
||||
|
||||
public void setSecurityWarningSize(Window window, int width, int height)
|
||||
{
|
||||
window.securityWarningWidth = width;
|
||||
|
||||
@@ -803,8 +803,7 @@ public class RepaintManager
|
||||
Window window = dirty instanceof Window ?
|
||||
(Window)dirty :
|
||||
SwingUtilities.getWindowAncestor(dirty);
|
||||
if (window != null &&
|
||||
!window.isOpaque())
|
||||
if (AWTAccessor.getWindowAccessor().needUpdateWindow(window))
|
||||
{
|
||||
windows.add(window);
|
||||
}
|
||||
@@ -1338,6 +1337,12 @@ public class RepaintManager
|
||||
g.setClip(x, y, w, h);
|
||||
paintingComponent.paintToOffscreen(g, x, y, w, h, x + w, y + h);
|
||||
}
|
||||
final Window window = SwingUtilities.getWindowAncestor(paintingComponent);
|
||||
if (AWTAccessor.getWindowAccessor().needUpdateWindowAfterPaint(window)) {
|
||||
// TODO: maybe introduce a more general "commitWindow" call instead
|
||||
// of mixing Windows-specific "updateWindow" with Wayland commits.
|
||||
AWTAccessor.getWindowAccessor().updateWindow(window);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -292,6 +292,10 @@ public final class AWTAccessor {
|
||||
*/
|
||||
void updateWindow(Window window);
|
||||
|
||||
boolean needUpdateWindowAfterPaint(Window window);
|
||||
|
||||
boolean needUpdateWindow(Window window);
|
||||
|
||||
/**
|
||||
* Set the size of the security warning.
|
||||
*/
|
||||
|
||||
@@ -86,6 +86,7 @@ typedef struct FontManagerNativeIDs {
|
||||
but we need access method to use it from separate rasterizer lib */
|
||||
extern FontManagerNativeIDs sunFontIDs;
|
||||
JNIEXPORT FontManagerNativeIDs getSunFontIDs(JNIEnv* env);
|
||||
JNIEXPORT const FontManagerNativeIDs* getSunFontIDsPtr(JNIEnv* env);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
336
src/java.desktop/share/native/common/java2d/vulkan/VKBase.cpp
Normal file
336
src/java.desktop/share/native/common/java2d/vulkan/VKBase.cpp
Normal file
@@ -0,0 +1,336 @@
|
||||
/*
|
||||
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
#include "VKBase.h"
|
||||
#include <Trace.h>
|
||||
#include <set>
|
||||
|
||||
#define VALIDATION_LAYER_NAME "VK_LAYER_KHRONOS_validation"
|
||||
static const uint32_t REQUIRED_VULKAN_VERSION = VK_MAKE_API_VERSION(0, 1, 0, 0);
|
||||
|
||||
|
||||
// ========== Vulkan instance ==========
|
||||
|
||||
static vk::raii::Context* context = nullptr;
|
||||
vk::raii::Instance vkInstance = nullptr;
|
||||
|
||||
#if defined(DEBUG)
|
||||
static vk::raii::DebugUtilsMessengerEXT debugMessenger = nullptr;
|
||||
|
||||
static VkBool32 debugCallback(
|
||||
VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
|
||||
VkDebugUtilsMessageTypeFlagsEXT messageType,
|
||||
const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
|
||||
void* pUserData
|
||||
) {
|
||||
auto data = (const vk::DebugUtilsMessengerCallbackDataEXT*) pCallbackData;
|
||||
if (data == nullptr) return 0;
|
||||
// Here we can filter messages like this:
|
||||
// if (std::strcmp(data->pMessageIdName, "UNASSIGNED-BestPractices-DrawState-ClearCmdBeforeDraw") == 0) return 0;
|
||||
|
||||
int level = J2D_TRACE_OFF;
|
||||
if (messageSeverity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT) level = J2D_TRACE_VERBOSE;
|
||||
else if (messageSeverity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT) level = J2D_TRACE_INFO;
|
||||
else if (messageSeverity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) level = J2D_TRACE_WARNING;
|
||||
else if (messageSeverity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) level = J2D_TRACE_ERROR;
|
||||
|
||||
J2dRlsTraceLn(level, data->pMessage);
|
||||
|
||||
// TODO if (messageSeverity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) ABORT?
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool createInstance() {
|
||||
try {
|
||||
// Load library.
|
||||
vk::raii::Context ctx;
|
||||
uint32_t version = ctx.enumerateInstanceVersion();
|
||||
J2dRlsTrace3(J2D_TRACE_INFO, "Vulkan: Available (%d.%d.%d)\n",
|
||||
VK_API_VERSION_MAJOR(version), VK_API_VERSION_MINOR(version), VK_API_VERSION_PATCH(version));
|
||||
|
||||
if (version < REQUIRED_VULKAN_VERSION) {
|
||||
J2dRlsTrace(J2D_TRACE_ERROR, "Vulkan: Unsupported version\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Populate maps and log supported layers & extensions.
|
||||
std::set<std::string> layers, extensions;
|
||||
J2dRlsTrace(J2D_TRACE_VERBOSE, " Supported instance layers:\n");
|
||||
for (auto& l : ctx.enumerateInstanceLayerProperties()) {
|
||||
J2dRlsTrace1(J2D_TRACE_VERBOSE, " %s\n", (char*) l.layerName);
|
||||
layers.emplace((char*) l.layerName);
|
||||
}
|
||||
J2dRlsTrace(J2D_TRACE_VERBOSE, " Supported instance extensions:\n");
|
||||
for (auto& e : ctx.enumerateInstanceExtensionProperties(nullptr)) {
|
||||
J2dRlsTrace1(J2D_TRACE_VERBOSE, " %s\n", (char*) e.extensionName);
|
||||
extensions.emplace((char*) e.extensionName);
|
||||
}
|
||||
|
||||
std::vector<const char*> enabledLayers, enabledExtensions;
|
||||
const void* pNext = nullptr;
|
||||
|
||||
// Check required layers & extensions.
|
||||
#if defined(VK_USE_PLATFORM_WAYLAND_KHR)
|
||||
enabledExtensions.push_back(VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME);
|
||||
#endif
|
||||
enabledExtensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME);
|
||||
bool requiredNotFound = false;
|
||||
for (auto e : enabledExtensions) {
|
||||
if (extensions.find(e) == extensions.end()) {
|
||||
J2dRlsTrace1(J2D_TRACE_ERROR, "Vulkan: Required instance extension not supported: %s\n", (char*) e);
|
||||
requiredNotFound = true;
|
||||
}
|
||||
}
|
||||
if (requiredNotFound) return false;
|
||||
|
||||
// Configure validation
|
||||
#ifdef DEBUG
|
||||
std::array<vk::ValidationFeatureEnableEXT, 4> enabledValidationFeatures = {
|
||||
vk::ValidationFeatureEnableEXT::eGpuAssisted,
|
||||
vk::ValidationFeatureEnableEXT::eGpuAssistedReserveBindingSlot,
|
||||
vk::ValidationFeatureEnableEXT::eBestPractices,
|
||||
vk::ValidationFeatureEnableEXT::eSynchronizationValidation
|
||||
};
|
||||
vk::ValidationFeaturesEXT validationFeatures {enabledValidationFeatures};
|
||||
if (layers.find(VALIDATION_LAYER_NAME) != layers.end() &&
|
||||
extensions.find(VK_EXT_DEBUG_UTILS_EXTENSION_NAME) != extensions.end()) {
|
||||
enabledLayers.push_back(VALIDATION_LAYER_NAME);
|
||||
enabledExtensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
|
||||
pNext = &validationFeatures;
|
||||
} else {
|
||||
J2dRlsTrace2(J2D_TRACE_WARNING, "Vulkan: %s and %s are not supported\n",
|
||||
VALIDATION_LAYER_NAME, VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
|
||||
}
|
||||
#endif
|
||||
|
||||
vk::ApplicationInfo applicationInfo {
|
||||
/*pApplicationName*/ "OpenJDK",
|
||||
/*applicationVersion*/ 0,
|
||||
/*pEngineName*/ "OpenJDK",
|
||||
/*engineVersion*/ 0,
|
||||
/*apiVersion*/ REQUIRED_VULKAN_VERSION
|
||||
};
|
||||
|
||||
vk::InstanceCreateInfo instanceCreateInfo {
|
||||
/*flags*/ {},
|
||||
/*pApplicationInfo*/ &applicationInfo,
|
||||
/*ppEnabledLayerNames*/ enabledLayers,
|
||||
/*ppEnabledExtensionNames*/ enabledExtensions,
|
||||
/*pNext*/ pNext
|
||||
};
|
||||
|
||||
// Save context object at persistent address before passing it further.
|
||||
context = new vk::raii::Context(std::move(ctx));
|
||||
|
||||
vkInstance = vk::raii::Instance(*context, instanceCreateInfo);
|
||||
J2dRlsTrace(J2D_TRACE_INFO, "Vulkan: Instance created\n");
|
||||
|
||||
// Create debug messenger
|
||||
#if defined(DEBUG)
|
||||
if (pNext) {
|
||||
debugMessenger = vk::raii::DebugUtilsMessengerEXT(vkInstance, vk::DebugUtilsMessengerCreateInfoEXT {
|
||||
/*flags*/ {},
|
||||
/*messageSeverity*/ vk::DebugUtilsMessageSeverityFlagBitsEXT::eError |
|
||||
vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning |
|
||||
vk::DebugUtilsMessageSeverityFlagBitsEXT::eInfo |
|
||||
vk::DebugUtilsMessageSeverityFlagBitsEXT::eVerbose,
|
||||
/*messageType*/ vk::DebugUtilsMessageTypeFlagBitsEXT::eGeneral |
|
||||
vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation |
|
||||
vk::DebugUtilsMessageTypeFlagBitsEXT::ePerformance,
|
||||
/*pfnUserCallback*/ &debugCallback
|
||||
});
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
return true;
|
||||
} catch (std::exception& e) {
|
||||
// Usually this means we didn't find the shared library.
|
||||
J2dRlsTrace1(J2D_TRACE_ERROR, "Vulkan: %s\n", e.what());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ========== Vulkan device ==========
|
||||
|
||||
#if defined(VK_USE_PLATFORM_WAYLAND_KHR)
|
||||
extern struct wl_display *wl_display;
|
||||
#endif
|
||||
|
||||
class PhysicalDevice : vk::raii::PhysicalDevice {
|
||||
friend class Device;
|
||||
|
||||
bool supported = false;
|
||||
int queueFamily = -1;
|
||||
std::vector<const char*> enabledLayers, enabledExtensions;
|
||||
|
||||
public:
|
||||
PhysicalDevice(vk::raii::PhysicalDevice&& handle) : vk::raii::PhysicalDevice(std::move(handle)) {
|
||||
const auto& properties = getProperties();
|
||||
const auto& queueFamilies = getQueueFamilyProperties();
|
||||
|
||||
J2dRlsTrace5(J2D_TRACE_INFO, "Vulkan: Found device %s (%d.%d.%d, %s)\n",
|
||||
(const char*) properties.deviceName,
|
||||
VK_API_VERSION_MAJOR(properties.apiVersion),
|
||||
VK_API_VERSION_MINOR(properties.apiVersion),
|
||||
VK_API_VERSION_PATCH(properties.apiVersion),
|
||||
vk::to_string(properties.deviceType).c_str());
|
||||
if (properties.apiVersion < REQUIRED_VULKAN_VERSION) {
|
||||
J2dRlsTrace(J2D_TRACE_INFO, " Unsupported Vulkan version\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check supported queue families.
|
||||
for (unsigned int i = 0; i < queueFamilies.size(); i++) {
|
||||
const auto& family = queueFamilies[i];
|
||||
#if defined(VK_USE_PLATFORM_WAYLAND_KHR)
|
||||
bool presentationSupported = getWaylandPresentationSupportKHR(i, *wl_display);
|
||||
#endif
|
||||
char logFlags[5] {
|
||||
family.queueFlags & vk::QueueFlagBits::eGraphics ? 'G' : '-',
|
||||
family.queueFlags & vk::QueueFlagBits::eCompute ? 'C' : '-',
|
||||
family.queueFlags & vk::QueueFlagBits::eTransfer ? 'T' : '-',
|
||||
family.queueFlags & vk::QueueFlagBits::eSparseBinding ? 'S' : '-',
|
||||
presentationSupported ? 'P' : '-'
|
||||
};
|
||||
J2dRlsTrace3(J2D_TRACE_INFO, " %d queues in family (%.*s)\n", family.queueCount, 5, logFlags);
|
||||
|
||||
// TODO use compute workloads? Separate transfer-only DMA queue?
|
||||
if (queueFamily == -1 && (family.queueFlags & vk::QueueFlagBits::eGraphics) && presentationSupported) {
|
||||
queueFamily = i;
|
||||
}
|
||||
}
|
||||
if (queueFamily == -1) {
|
||||
J2dRlsTrace(J2D_TRACE_INFO, " No suitable queue\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// Populate maps and log supported layers & extensions.
|
||||
std::set<std::string> layers, extensions;
|
||||
J2dRlsTrace(J2D_TRACE_VERBOSE, " Supported device layers:\n");
|
||||
for (auto& l : enumerateDeviceLayerProperties()) {
|
||||
J2dRlsTrace1(J2D_TRACE_VERBOSE, " %s\n", (char*) l.layerName);
|
||||
layers.emplace((char*) l.layerName);
|
||||
}
|
||||
J2dRlsTrace(J2D_TRACE_VERBOSE, " Supported device extensions:\n");
|
||||
for (auto& e : enumerateDeviceExtensionProperties(nullptr)) {
|
||||
J2dRlsTrace1(J2D_TRACE_VERBOSE, " %s\n", (char*) e.extensionName);
|
||||
extensions.emplace((char*) e.extensionName);
|
||||
}
|
||||
|
||||
// Check required layers & extensions.
|
||||
enabledExtensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
|
||||
bool requiredNotFound = false;
|
||||
for (auto e : enabledExtensions) {
|
||||
if (extensions.find(e) == extensions.end()) {
|
||||
J2dRlsTrace1(J2D_TRACE_INFO, " Required device extension not supported: %s\n", (char*) e);
|
||||
requiredNotFound = true;
|
||||
}
|
||||
}
|
||||
if (requiredNotFound) return;
|
||||
|
||||
// Validation layer
|
||||
#ifdef DEBUG
|
||||
if (layers.find(VALIDATION_LAYER_NAME) != layers.end()) {
|
||||
enabledLayers.push_back(VALIDATION_LAYER_NAME);
|
||||
} else {
|
||||
J2dRlsTrace1(J2D_TRACE_INFO, " %s device layer is not supported\n", VALIDATION_LAYER_NAME);
|
||||
}
|
||||
#endif
|
||||
|
||||
// This device is supported
|
||||
supported = true;
|
||||
}
|
||||
|
||||
operator bool() const {
|
||||
vk::PhysicalDevice handle = **this;
|
||||
return handle && supported;
|
||||
}
|
||||
};
|
||||
|
||||
Device::Device(const PhysicalDevice& physicalDevice) : vk::raii::Device(nullptr) {
|
||||
float queuePriorities[1] {1.0f}; // We only use one queue for now
|
||||
std::vector<vk::DeviceQueueCreateInfo> queueCreateInfos;
|
||||
queueCreateInfos.push_back(vk::DeviceQueueCreateInfo {
|
||||
{}, (uint32_t) physicalDevice.queueFamily, 1, &queuePriorities[0]
|
||||
});
|
||||
|
||||
vk::DeviceCreateInfo deviceCreateInfo {
|
||||
/*flags*/ {},
|
||||
/*pQueueCreateInfos*/ queueCreateInfos,
|
||||
/*ppEnabledLayerNames*/ physicalDevice.enabledLayers,
|
||||
/*ppEnabledExtensionNames*/ physicalDevice.enabledExtensions,
|
||||
/*pEnabledFeatures*/ nullptr
|
||||
};
|
||||
*((vk::raii::Device*) this) = {physicalDevice, deviceCreateInfo};
|
||||
J2dRlsTrace(J2D_TRACE_INFO, "Vulkan: Device created\n"); // TODO which one?
|
||||
}
|
||||
|
||||
static std::vector<PhysicalDevice> physicalDevices; // Only supported ones.
|
||||
|
||||
static bool initDevices() {
|
||||
try {
|
||||
// Find suitable devices.
|
||||
for (auto& handle : vkInstance.enumeratePhysicalDevices()) {
|
||||
PhysicalDevice physicalDevice {std::move(handle)};
|
||||
if (physicalDevice) { // Supported.
|
||||
physicalDevices.push_back(std::move(physicalDevice));
|
||||
}
|
||||
}
|
||||
if (physicalDevices.empty()) {
|
||||
J2dRlsTrace(J2D_TRACE_ERROR, "Vulkan: No suitable device found\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create virtual device for a physical device.
|
||||
// TODO system property for manual choice of GPU
|
||||
// TODO integrated/discrete presets
|
||||
// TODO performance/power saving mode switch on the fly?
|
||||
Device device {physicalDevices[0]}; // TODO pick first just to check that virtual device creation works
|
||||
|
||||
return true;
|
||||
} catch (std::exception& e) {
|
||||
J2dRlsTrace1(J2D_TRACE_ERROR, "Vulkan: %s\n", e.what());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extern "C" jboolean VK_Init() {
|
||||
if (createInstance() && initDevices()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
physicalDevices.clear();
|
||||
#if defined(DEBUG)
|
||||
debugMessenger = nullptr;
|
||||
#endif
|
||||
vkInstance = nullptr;
|
||||
delete context;
|
||||
|
||||
return false;
|
||||
}
|
||||
54
src/java.desktop/share/native/common/java2d/vulkan/VKBase.h
Normal file
54
src/java.desktop/share/native/common/java2d/vulkan/VKBase.h
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
#ifndef VKBase_h_Included
|
||||
#define VKBase_h_Included
|
||||
#ifdef __cplusplus
|
||||
|
||||
|
||||
#define VK_NO_PROTOTYPES
|
||||
#define VULKAN_HPP_NO_DEFAULT_DISPATCHER
|
||||
#include <vulkan/vulkan_raii.hpp>
|
||||
|
||||
extern vk::raii::Instance vkInstance;
|
||||
|
||||
class PhysicalDevice;
|
||||
class Device : public vk::raii::Device {
|
||||
public:
|
||||
Device(const PhysicalDevice& physicalDevice);
|
||||
};
|
||||
|
||||
extern "C" {
|
||||
#endif //__cplusplus
|
||||
|
||||
typedef unsigned char jboolean;
|
||||
|
||||
jboolean VK_Init();
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif //__cplusplus
|
||||
#endif //VKBase_h_Included
|
||||
35
src/java.desktop/share/native/common/java2d/vulkan/VKStubs.c
Normal file
35
src/java.desktop/share/native/common/java2d/vulkan/VKStubs.c
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
// These are stubs in case we were built with Vulkan disabled.
|
||||
#ifndef VULKAN_ENABLED
|
||||
|
||||
typedef unsigned char jboolean;
|
||||
|
||||
jboolean VK_Init() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -50,8 +50,8 @@
|
||||
#define FLOOR_ASSIGN(l, r)\
|
||||
if ((r)<0) (l) = ((int)floor(r)); else (l) = ((int)(r))
|
||||
|
||||
GlyphBlitVector* setupBlitVector(JNIEnv *env, jobject glyphlist,
|
||||
jint fromGlyph, jint toGlyph) {
|
||||
JNIEXPORT GlyphBlitVector* setupBlitVector(JNIEnv *env, jobject glyphlist,
|
||||
jint fromGlyph, jint toGlyph) {
|
||||
|
||||
int g;
|
||||
size_t bytesNeeded;
|
||||
@@ -140,7 +140,7 @@ GlyphBlitVector* setupBlitVector(JNIEnv *env, jobject glyphlist,
|
||||
return gbv;
|
||||
}
|
||||
|
||||
jint RefineBounds(GlyphBlitVector *gbv, SurfaceDataBounds *bounds) {
|
||||
JNIEXPORT jint RefineBounds(GlyphBlitVector *gbv, SurfaceDataBounds *bounds) {
|
||||
int index;
|
||||
jint dx1, dy1, dx2, dy2;
|
||||
ImageRef glyphImage;
|
||||
@@ -487,8 +487,8 @@ Java_sun_java2d_loops_DrawGlyphListLCD_DrawGlyphListLCD
|
||||
* rendered fractional metrics, there's typically more space between the
|
||||
* glyphs. Perhaps disabling X-axis grid-fitting will help with that.
|
||||
*/
|
||||
GlyphBlitVector* setupLCDBlitVector(JNIEnv *env, jobject glyphlist,
|
||||
jint fromGlyph, jint toGlyph) {
|
||||
JNIEXPORT GlyphBlitVector* setupLCDBlitVector(JNIEnv *env, jobject glyphlist,
|
||||
jint fromGlyph, jint toGlyph) {
|
||||
|
||||
int g;
|
||||
size_t bytesNeeded;
|
||||
|
||||
@@ -39,11 +39,11 @@ typedef struct {
|
||||
ImageRef *glyphs;
|
||||
} GlyphBlitVector;
|
||||
|
||||
extern jint RefineBounds(GlyphBlitVector *gbv, SurfaceDataBounds *bounds);
|
||||
extern GlyphBlitVector* setupBlitVector(JNIEnv *env, jobject glyphlist,
|
||||
jint fromGlyph, jint toGlyph);
|
||||
extern GlyphBlitVector* setupLCDBlitVector(JNIEnv *env, jobject glyphlist,
|
||||
JNIEXPORT jint RefineBounds(GlyphBlitVector *gbv, SurfaceDataBounds *bounds);
|
||||
JNIEXPORT GlyphBlitVector* setupBlitVector(JNIEnv *env, jobject glyphlist,
|
||||
jint fromGlyph, jint toGlyph);
|
||||
JNIEXPORT GlyphBlitVector* setupLCDBlitVector(JNIEnv *env, jobject glyphlist,
|
||||
jint fromGlyph, jint toGlyph);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -207,6 +207,12 @@ JNIEXPORT FontManagerNativeIDs getSunFontIDs(JNIEnv *env) {
|
||||
return sunFontIDs;
|
||||
}
|
||||
|
||||
JNIEXPORT const FontManagerNativeIDs* getSunFontIDsPtr(JNIEnv* env) {
|
||||
|
||||
initFontIDs(env);
|
||||
return &sunFontIDs;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_font_StrikeCache
|
||||
* Method: freeIntPointer
|
||||
|
||||
97
src/java.desktop/share/native/libwakefield/CMakeLists.txt
Normal file
97
src/java.desktop/share/native/libwakefield/CMakeLists.txt
Normal file
@@ -0,0 +1,97 @@
|
||||
#
|
||||
# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
# Copyright (c) 2022, JetBrains s.r.o.. All rights reserved.
|
||||
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
#
|
||||
# This code is free software; you can redistribute it and/or modify it
|
||||
# under the terms of the GNU General Public License version 2 only, as
|
||||
# published by the Free Software Foundation.
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
project(wakefield C)
|
||||
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
|
||||
set(CMAKE_INSTALL_PREFIX ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
find_program(WAYLAND_SCANNER wayland-scanner)
|
||||
if (NOT WAYLAND_SCANNER)
|
||||
message(FATAL_ERROR "wayland-scanner not found")
|
||||
endif ()
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT wakefield-server-protocol.h
|
||||
COMMAND ${WAYLAND_SCANNER} server-header ${CMAKE_CURRENT_SOURCE_DIR}/protocol/wakefield.xml wakefield-server-protocol.h
|
||||
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/protocol/wakefield.xml
|
||||
VERBATIM)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT wakefield-server-protocol.c
|
||||
COMMAND ${WAYLAND_SCANNER} private-code ${CMAKE_CURRENT_SOURCE_DIR}/protocol/wakefield.xml wakefield-server-protocol.c
|
||||
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/protocol/wakefield.xml
|
||||
VERBATIM)
|
||||
|
||||
find_path(
|
||||
WESTON_INCLUDES
|
||||
weston/weston.h
|
||||
/usr/include /usr/local/include)
|
||||
|
||||
if (NOT WESTON_INCLUDES)
|
||||
message(FATAL_ERROR "weston/weston.h not found")
|
||||
endif ()
|
||||
|
||||
find_path(
|
||||
LIBWESTON_INCLUDES
|
||||
libweston/libweston.h
|
||||
/usr/include /usr/local/include
|
||||
PATH_SUFFIXES libweston-9)
|
||||
|
||||
if (NOT LIBWESTON_INCLUDES)
|
||||
message(FATAL_ERROR "libweston/libweston.h not found")
|
||||
endif ()
|
||||
|
||||
find_path(
|
||||
PIXMAN_INCLUDES
|
||||
pixman.h
|
||||
/usr/include /usr/local/include
|
||||
PATH_SUFFIXES pixman-1)
|
||||
|
||||
if (NOT PIXMAN_INCLUDES)
|
||||
message(FATAL_ERROR "pixman.h not found")
|
||||
endif ()
|
||||
|
||||
add_library(wakefield SHARED src/wakefield.c wakefield-server-protocol.c wakefield-server-protocol.h)
|
||||
target_include_directories(wakefield PUBLIC
|
||||
${WESTON_INCLUDES} ${LIBWESTON_INCLUDES} ${PIXMAN_INCLUDES}
|
||||
${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
install(TARGETS wakefield DESTINATION .)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT wakefield-client-protocol.h
|
||||
COMMAND ${WAYLAND_SCANNER} client-header ${CMAKE_CURRENT_SOURCE_DIR}/protocol/wakefield.xml wakefield-client-protocol.h
|
||||
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/protocol/wakefield.xml
|
||||
VERBATIM)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT wakefield-client-protocol.c
|
||||
COMMAND ${WAYLAND_SCANNER} code ${CMAKE_CURRENT_SOURCE_DIR}/protocol/wakefield.xml wakefield-client-protocol.c
|
||||
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/protocol/wakefield.xml
|
||||
VERBATIM)
|
||||
|
||||
add_custom_target(client ALL DEPENDS wakefield-client-protocol.h wakefield-client-protocol.c)
|
||||
43
src/java.desktop/share/native/libwakefield/README.md
Normal file
43
src/java.desktop/share/native/libwakefield/README.md
Normal file
@@ -0,0 +1,43 @@
|
||||
# Wakefield Protocol
|
||||
This is a [Weston](https://github.com/wayland-project/weston) protocol extension
|
||||
created to satisfy the requirements of the
|
||||
[Wakefield](https://openjdk.java.net/projects/wakefield/) project *testing*.
|
||||
|
||||
## Rationale
|
||||
Many of `java.awt.Robot` capabilities contradict the basic principles of
|
||||
[Wayland](https://openjdk.java.net/projects/wakefield/). The examples include
|
||||
obtaining the color of a pixel at specified coordinates by automated tests.
|
||||
This extension aims to help in implementation of those capabilities for the use
|
||||
in test environment *only*.
|
||||
|
||||
## Prerequisites
|
||||
On Ubuntu 21.04 (in addition to the usual C development environment):
|
||||
```bash
|
||||
$ sudo apt install cmake libwayland-dev wayland-protocols \
|
||||
libweston-9-0 libweston-9-dev weston libpixman-1-dev libpixman-1-0
|
||||
```
|
||||
|
||||
## Build
|
||||
|
||||
```bash
|
||||
$ mkdir build && cd build
|
||||
$ cmake .../src/java.desktop/share/native/libwakefield/
|
||||
$ make
|
||||
```
|
||||
This should build `libwakefield.so` in the current (build) directory.
|
||||
|
||||
## Smoke test
|
||||
|
||||
```bash
|
||||
$ PWD=`pwd` && \
|
||||
weston --socket=wayland-test --width=800 --height=600 --use-pixman --socket=wayland-42 \
|
||||
--modules="$PWD/libwakefield.so"
|
||||
```
|
||||
This will create a new Weston compositor instance with the Wayland socket
|
||||
named `wayland-42`.
|
||||
Now you can verify that a new global has been published by running this command:
|
||||
```bash
|
||||
$ WAYLAND_DISPLAY=wayland-42 weston-info | grep wakefield
|
||||
interface: 'wakefield', version: 1, name: 21
|
||||
```
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<protocol name="wakefield">
|
||||
<copyright>
|
||||
Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
Copyright (c) 2022, JetBrains s.r.o.. All rights reserved.
|
||||
DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
|
||||
This code is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License version 2 only, as
|
||||
published by the Free Software Foundation.
|
||||
|
||||
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.
|
||||
</copyright>
|
||||
|
||||
<interface name="wakefield" version="1">
|
||||
<description summary="provides capabilities necessary to for java.awt.Robot and such"></description>
|
||||
|
||||
<request name="destroy" type="destructor">
|
||||
</request>
|
||||
|
||||
<request name="move_surface">
|
||||
<description summary="facilitates implementation of Frame.setLocation()">
|
||||
This instructs the window manager to position the given wl_surface
|
||||
at the given absolute coordinates. The subsequent get_surface_location
|
||||
request will return these coordinates unless the surface was moved by
|
||||
a third party.
|
||||
</description>
|
||||
<arg name="surface" type="object" interface="wl_surface"/>
|
||||
<arg name="x" type="int"/>
|
||||
<arg name="y" type="int"/>
|
||||
</request>
|
||||
|
||||
<request name="get_surface_location">
|
||||
<description summary="facilitates implementation of Frame.getLocation()">
|
||||
This requests a surface_location event for the given surface.
|
||||
</description>
|
||||
<arg name="surface" type="object" interface="wl_surface"/>
|
||||
</request>
|
||||
|
||||
<event name="surface_location">
|
||||
<description summary="facilitates implementation of Frame.getLocation()">
|
||||
This event reveals the absolute coordinates of the surface if error_code is zero.
|
||||
If error_code is non-zero, (x, y) are undefined.
|
||||
The surface argument always correspond to that of the get_surface_location request.
|
||||
</description>
|
||||
<arg name="surface" type="object" interface="wl_surface"/>
|
||||
<arg name="x" type="int"/>
|
||||
<arg name="y" type="int"/>
|
||||
<arg name="error_code" type="uint" enum="error"/>
|
||||
</event>
|
||||
|
||||
<request name="get_pixel_color">
|
||||
<description summary="facilitates implementation of Robot.getPixelColor()">
|
||||
This requests a pixel_color event at the given absolute coordinates.
|
||||
</description>
|
||||
<arg name="x" type="int"/>
|
||||
<arg name="y" type="int"/>
|
||||
</request>
|
||||
|
||||
<event name="pixel_color">
|
||||
<description summary="facilitates implementation of Robot.getPixelColor()">
|
||||
This event shows the color (24-bit, format r8g8b8) of the pixel with the given
|
||||
absolute coordinates.
|
||||
The (x, y) arguments correspond to that of the get_pixel_color request.
|
||||
If error_code is non-zero, the rgb argument is undefined and the error_code argument
|
||||
contains a code from the error enum.
|
||||
</description>
|
||||
<arg name="x" type="int"/>
|
||||
<arg name="y" type="int"/>
|
||||
<arg name="rgb" type="uint"/>
|
||||
<arg name="error_code" type="uint" enum="error"/>
|
||||
</event>
|
||||
|
||||
<enum name="error">
|
||||
<entry name="no_error" value="0" summary="error code 0 reserved for the absence of error"/>
|
||||
<entry name="invalid_coordinates" value="1" summary="supplied absolute coordinates point
|
||||
outside of any output"/>
|
||||
<entry name="out_of_memory" value="2" summary="the request could not be fulfilled due to memory allocation error"/>
|
||||
<entry name="internal" value="3" summary="a generic error code for internal errors"/>
|
||||
<entry name="format" value="4" summary="(temporary?) color cannot be converted to RGB format"/>
|
||||
</enum>
|
||||
|
||||
<request name="capture_create">
|
||||
<arg name="buffer" type="object" interface="wl_buffer" summary="shall be an instance by the wl_shm factory"/>
|
||||
<arg name="x" type="int"/>
|
||||
<arg name="y" type="int"/>
|
||||
<!--
|
||||
<arg name="capture_pointer" type="int" summary="0 to exclude the mouse pointer surface from the capture"/>
|
||||
-->
|
||||
</request>
|
||||
|
||||
<event name="capture_ready">
|
||||
<arg name="buffer" type="object" interface="wl_buffer"/>
|
||||
<arg name="error_code" type="uint" enum="error"/>
|
||||
</event>
|
||||
</interface>
|
||||
|
||||
</protocol>
|
||||
546
src/java.desktop/share/native/libwakefield/src/wakefield.c
Normal file
546
src/java.desktop/share/native/libwakefield/src/wakefield.c
Normal file
@@ -0,0 +1,546 @@
|
||||
/*
|
||||
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2022, JetBrains s.r.o.. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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 <weston/weston.h>
|
||||
#include <libweston/weston-log.h>
|
||||
|
||||
#include <pixman.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "wakefield-server-protocol.h"
|
||||
|
||||
#ifndef container_of
|
||||
#define container_of(ptr, type, member) ({ \
|
||||
const __typeof__( ((type *)0)->member ) *__mptr = (ptr); \
|
||||
(type *)( (char *)__mptr - offsetof(type,member) );})
|
||||
#endif
|
||||
|
||||
struct wakefield {
|
||||
struct weston_compositor *compositor;
|
||||
struct wl_listener destroy_listener;
|
||||
|
||||
struct weston_log_scope *log;
|
||||
};
|
||||
|
||||
static struct weston_output*
|
||||
get_output_for_point(struct wakefield* wakefield, int32_t x, int32_t y)
|
||||
{
|
||||
struct weston_output *o;
|
||||
wl_list_for_each(o, &wakefield->compositor->output_list, link) {
|
||||
if (o->destroying)
|
||||
continue;
|
||||
|
||||
if (pixman_region32_contains_point(&o->region, x, y, NULL)) {
|
||||
return o;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
wakefield_get_pixel_color(struct wl_client *client,
|
||||
struct wl_resource *resource,
|
||||
int32_t x,
|
||||
int32_t y)
|
||||
{
|
||||
struct wakefield *wakefield = wl_resource_get_user_data(resource);
|
||||
struct weston_compositor *compositor = wakefield->compositor;
|
||||
|
||||
weston_log_scope_printf(wakefield->log, "WAKEFIELD: get_pixel_color at (%d, %d)\n", x, y);
|
||||
|
||||
const unsigned int byte_per_pixel = (PIXMAN_FORMAT_BPP(compositor->read_format) / 8);
|
||||
uint32_t pixel = 0;
|
||||
if (byte_per_pixel > sizeof(pixel)) {
|
||||
weston_log_scope_printf(wakefield->log,
|
||||
"WAKEFIELD: compositor pixel format (%d) exceeds allocated storage (%d > %ld)\n",
|
||||
compositor->read_format,
|
||||
byte_per_pixel,
|
||||
sizeof(pixel));
|
||||
wakefield_send_pixel_color(resource, x, y, 0, WAKEFIELD_ERROR_FORMAT);
|
||||
return;
|
||||
}
|
||||
|
||||
struct weston_output *output = get_output_for_point(wakefield, x, y);
|
||||
if (output == NULL) {
|
||||
weston_log_scope_printf(wakefield->log,
|
||||
"WAKEFIELD: pixel location (%d, %d) doesn't map to any output\n", x, y);
|
||||
wakefield_send_pixel_color(resource, x, y, 0, WAKEFIELD_ERROR_INVALID_COORDINATES);
|
||||
return;
|
||||
}
|
||||
|
||||
const int output_x = x - output->x;
|
||||
const int output_y = y - output->y;
|
||||
weston_log_scope_printf(wakefield->log,
|
||||
"WAKEFIELD: reading pixel color at (%d, %d) of '%s'\n",
|
||||
output_x, output_y, output->name);
|
||||
compositor->renderer->read_pixels(output,
|
||||
compositor->read_format, &pixel,
|
||||
output_x, output_y, 1, 1);
|
||||
|
||||
uint32_t rgb = 0;
|
||||
switch (compositor->read_format) {
|
||||
case PIXMAN_a8r8g8b8:
|
||||
case PIXMAN_x8r8g8b8:
|
||||
case PIXMAN_r8g8b8:
|
||||
rgb = pixel & 0x00ffffffu;
|
||||
break;
|
||||
|
||||
default:
|
||||
weston_log_scope_printf(wakefield->log,
|
||||
"WAKEFIELD: compositor pixel format %d (see pixman.h) not supported\n",
|
||||
compositor->read_format);
|
||||
wakefield_send_pixel_color(resource, x, y, 0, WAKEFIELD_ERROR_FORMAT);
|
||||
return;
|
||||
}
|
||||
weston_log_scope_printf(wakefield->log, "WAKEFIELD: color is 0x%08x\n", rgb);
|
||||
|
||||
wakefield_send_pixel_color(resource, x, y, rgb, WAKEFIELD_ERROR_NO_ERROR);
|
||||
}
|
||||
|
||||
static void
|
||||
wakefield_get_surface_location(struct wl_client *client,
|
||||
struct wl_resource *resource,
|
||||
struct wl_resource *surface_resource)
|
||||
{
|
||||
// See also weston-test.c`move_surface() and the corresponding protocol
|
||||
|
||||
struct wakefield *wakefield = wl_resource_get_user_data(resource);
|
||||
struct weston_surface *surface = wl_resource_get_user_data(surface_resource);
|
||||
struct weston_view *view = container_of(surface->views.next, struct weston_view, surface_link);
|
||||
|
||||
if (!view) {
|
||||
weston_log_scope_printf(wakefield->log, "WAKEFIELD: get_location error\n");
|
||||
wakefield_send_surface_location(resource, surface_resource, 0, 0,
|
||||
WAKEFIELD_ERROR_INTERNAL);
|
||||
return;
|
||||
}
|
||||
|
||||
float fx;
|
||||
float fy;
|
||||
weston_view_to_global_float(view, 0, 0, &fx, &fy);
|
||||
const int32_t x = (int32_t)fx;
|
||||
const int32_t y = (int32_t)fy;
|
||||
weston_log_scope_printf(wakefield->log, "WAKEFIELD: get_location: %d, %d\n", x, y);
|
||||
|
||||
wakefield_send_surface_location(resource, surface_resource, x, y,
|
||||
WAKEFIELD_ERROR_NO_ERROR);
|
||||
}
|
||||
|
||||
static void
|
||||
wakefield_move_surface(struct wl_client *client,
|
||||
struct wl_resource *resource,
|
||||
struct wl_resource *surface_resource,
|
||||
int32_t x,
|
||||
int32_t y)
|
||||
{
|
||||
struct wakefield *wakefield = wl_resource_get_user_data(resource);
|
||||
struct weston_surface *surface = wl_resource_get_user_data(surface_resource);
|
||||
struct weston_view *view = container_of(surface->views.next, struct weston_view, surface_link);
|
||||
|
||||
if (!view) {
|
||||
weston_log_scope_printf(wakefield->log, "WAKEFIELD: move_surface error\n");
|
||||
return;
|
||||
}
|
||||
|
||||
weston_view_set_position(view, (float)x, (float)y);
|
||||
weston_view_update_transform(view);
|
||||
|
||||
weston_log_scope_printf(wakefield->log, "WAKEFIELD: move_surface to (%d, %d)\n", x, y);
|
||||
}
|
||||
|
||||
static pixman_format_code_t
|
||||
wl_shm_format_to_pixman(uint32_t wl_shm_format)
|
||||
{
|
||||
pixman_format_code_t rc;
|
||||
|
||||
switch (wl_shm_format) {
|
||||
case WL_SHM_FORMAT_ARGB8888:
|
||||
rc = PIXMAN_a8r8g8b8;
|
||||
break;
|
||||
case WL_SHM_FORMAT_XRGB8888:
|
||||
rc = PIXMAN_x8r8g8b8;
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of pixels in the given non-empty region.
|
||||
*/
|
||||
static uint64_t
|
||||
size_in_pixels(pixman_region32_t *region)
|
||||
{
|
||||
const pixman_box32_t * const e = pixman_region32_extents(region);
|
||||
assert (e->x2 >= e->x1);
|
||||
assert (e->y2 >= e->y1);
|
||||
|
||||
return ((uint64_t)(e->x2 - e->x1))*(e->y2 - e->y1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of pixels of the largest portion that the given region occupies on
|
||||
* any of the compositor's outputs.
|
||||
*
|
||||
* @param fits_entirely (OUT) set to true iff the entire region fits as a whole on just one output
|
||||
* @return largest area size in pixels (could be zero).
|
||||
*/
|
||||
static uint64_t
|
||||
get_largest_area_in_one_output(struct weston_compositor *compositor, pixman_region32_t *region, bool *fits_entirely)
|
||||
{
|
||||
uint64_t area = 0; // in pixels
|
||||
*fits_entirely = false;
|
||||
|
||||
pixman_region32_t region_in_output;
|
||||
pixman_region32_init(®ion_in_output);
|
||||
|
||||
struct weston_output *output;
|
||||
wl_list_for_each(output, &compositor->output_list, link) {
|
||||
if (output->destroying)
|
||||
continue;
|
||||
|
||||
pixman_region32_intersect(®ion_in_output, region, &output->region);
|
||||
if (pixman_region32_not_empty(®ion_in_output)) {
|
||||
const uint64_t this_area = size_in_pixels(®ion_in_output);
|
||||
if (this_area > area) {
|
||||
area = this_area;
|
||||
}
|
||||
if (pixman_region32_equal(®ion_in_output, region)) {
|
||||
*fits_entirely = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pixman_region32_fini(®ion_in_output);
|
||||
|
||||
return area;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets every pixel in the given buffer to 0.
|
||||
*/
|
||||
static void
|
||||
clear_buffer(struct wl_shm_buffer *buffer)
|
||||
{
|
||||
const int32_t height = wl_shm_buffer_get_height(buffer);
|
||||
const size_t stride = wl_shm_buffer_get_stride(buffer);
|
||||
const size_t buffer_byte_size = height*stride;
|
||||
|
||||
wl_shm_buffer_begin_access(buffer);
|
||||
{
|
||||
uint32_t *data = wl_shm_buffer_get_data(buffer);
|
||||
memset(data, 0, buffer_byte_size);
|
||||
}
|
||||
wl_shm_buffer_end_access(buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies 4-byte pixels from the given array to the given buffer
|
||||
* at the given offset into the buffer.
|
||||
*
|
||||
* @param buffer target buffer (format is assumed to be 4 byte per pixel)
|
||||
* @param data array of 4-byte pixels of size (width*height)
|
||||
* @param target_x horizontal coordinate of the top-left corner in the buffer
|
||||
* where the given data should be placed
|
||||
* @param target_y vertical coordinate of the top-left corner in the buffer
|
||||
* where the given data should be placed
|
||||
* @param width the source image width in pixels
|
||||
* @param height the source image height in pixels
|
||||
*/
|
||||
static void
|
||||
copy_pixels_to_shm_buffer(struct wl_shm_buffer *buffer, uint32_t *data,
|
||||
int32_t target_x, int32_t target_y,
|
||||
int32_t width, int32_t height)
|
||||
{
|
||||
assert (target_x >= 0 && target_y >= 0);
|
||||
assert (data);
|
||||
|
||||
const int32_t buffer_width = wl_shm_buffer_get_width(buffer);
|
||||
|
||||
wl_shm_buffer_begin_access(buffer);
|
||||
{
|
||||
uint32_t * const buffer_data = wl_shm_buffer_get_data(buffer);
|
||||
assert (buffer_data);
|
||||
|
||||
for (int32_t y = 0; y < height; y++) {
|
||||
const uint32_t * const src_line = &data[y*width];
|
||||
uint32_t * const dst_line = &buffer_data[(target_y + y)*buffer_width];
|
||||
for (int32_t x = 0; x < width; x++) {
|
||||
dst_line[target_x + x] = src_line[x];
|
||||
}
|
||||
}
|
||||
}
|
||||
wl_shm_buffer_end_access(buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that the given buffer format is supported and sends the "capture ready" event
|
||||
* with the appropriate error code if it wasn't.
|
||||
*/
|
||||
static bool
|
||||
check_buffer_format_supported(struct wakefield *wakefield, struct wl_resource *resource,
|
||||
struct wl_resource *buffer_resource, uint32_t buffer_format)
|
||||
{
|
||||
if (buffer_format != WL_SHM_FORMAT_ARGB8888
|
||||
&& buffer_format != WL_SHM_FORMAT_XRGB8888) {
|
||||
weston_log_scope_printf(wakefield->log,
|
||||
"WAKEFIELD: buffer for image capture has unsupported format %d, "
|
||||
"check codes in enum 'format' in wayland.xml\n",
|
||||
buffer_format);
|
||||
wakefield_send_capture_ready(resource, buffer_resource, WAKEFIELD_ERROR_FORMAT);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that the given buffer type is shm and sends the "capture ready" event
|
||||
* with the appropriate error code if it wasn't.
|
||||
*/
|
||||
static bool
|
||||
check_buffer_type_supported(struct wakefield *wakefield, struct wl_resource *resource,
|
||||
struct wl_resource *buffer_resource)
|
||||
{
|
||||
struct wl_shm_buffer *buffer = wl_shm_buffer_get(buffer_resource);
|
||||
|
||||
if (!buffer) {
|
||||
weston_log_scope_printf(wakefield->log, "WAKEFIELD: buffer for image capture not from wl_shm\n");
|
||||
wakefield_send_capture_ready(resource, buffer_resource, WAKEFIELD_ERROR_INTERNAL);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that the given capture area is not empty and sends the successful "capture ready" event
|
||||
* if it was.
|
||||
*/
|
||||
static bool
|
||||
capture_is_empty(struct wakefield *wakefield, struct wl_resource *resource,
|
||||
struct wl_resource *buffer_resource, uint64_t largest_capture_area)
|
||||
{
|
||||
if (largest_capture_area == 0) {
|
||||
// All outputs might've just disappeared
|
||||
weston_log_scope_printf(wakefield->log,
|
||||
"WAKEFIELD: captured area size on all outputs is zero.\n");
|
||||
wakefield_send_capture_ready(resource, buffer_resource, WAKEFIELD_ERROR_NO_ERROR);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void
|
||||
wakefield_capture_create(struct wl_client *client,
|
||||
struct wl_resource *resource,
|
||||
struct wl_resource *buffer_resource,
|
||||
int32_t x,
|
||||
int32_t y)
|
||||
{
|
||||
struct wakefield *wakefield = wl_resource_get_user_data(resource);
|
||||
|
||||
if (!check_buffer_type_supported(wakefield, resource, buffer_resource)) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct wl_shm_buffer *buffer = wl_shm_buffer_get(buffer_resource);
|
||||
assert (buffer); // actually, verified earlier
|
||||
const uint32_t buffer_format = wl_shm_buffer_get_format(buffer);
|
||||
if (!check_buffer_format_supported(wakefield, resource, buffer_resource, buffer_format)) {
|
||||
return;
|
||||
}
|
||||
|
||||
clear_buffer(buffer); // in case some outputs disappear mid-flight or a part of the capture is out of screen
|
||||
|
||||
const int32_t width = wl_shm_buffer_get_width(buffer);
|
||||
const int32_t height = wl_shm_buffer_get_height(buffer);
|
||||
|
||||
pixman_region32_t region_global; // capture region in global coordinates
|
||||
pixman_region32_t region_in_output; // capture region in a particular output in that output's coordinates
|
||||
|
||||
pixman_region32_init_rect(®ion_global, x, y, width, height);
|
||||
pixman_region32_init(®ion_in_output);
|
||||
|
||||
bool fits_entirely;
|
||||
const uint64_t largest_capture_area = get_largest_area_in_one_output(wakefield->compositor, ®ion_global, &fits_entirely);
|
||||
if (capture_is_empty(wakefield, resource, buffer_resource, largest_capture_area)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const size_t bpp = 4; // byte-per-pixel
|
||||
void *per_output_buffer = NULL;
|
||||
if (!fits_entirely) {
|
||||
// Can't read screen pixels directly into the resulting buffer, have to use an intermediate storage.
|
||||
per_output_buffer = malloc(largest_capture_area * bpp);
|
||||
if (per_output_buffer == NULL) {
|
||||
weston_log_scope_printf(wakefield->log,
|
||||
"WAKEFIELD: failed to allocate %ld bytes for temporary capture buffer.\n",
|
||||
largest_capture_area);
|
||||
wakefield_send_capture_ready(resource, buffer_resource, WAKEFIELD_ERROR_OUT_OF_MEMORY);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const pixman_format_code_t buffer_format_pixman = wl_shm_format_to_pixman(buffer_format);
|
||||
struct weston_output *output;
|
||||
wl_list_for_each(output, &wakefield->compositor->output_list, link) {
|
||||
if (output->destroying)
|
||||
continue;
|
||||
|
||||
pixman_region32_intersect(®ion_in_output, ®ion_global, &output->region);
|
||||
if (pixman_region32_not_empty(®ion_in_output)) {
|
||||
const pixman_box32_t * const e = pixman_region32_extents(®ion_in_output);
|
||||
|
||||
const int32_t region_x_in_global = e->x1;
|
||||
const int32_t region_y_in_global = e->y1;
|
||||
const int32_t width_in_output = e->x2 - e->x1;
|
||||
const int32_t height_in_output = e->y2 - e->y1;
|
||||
weston_log_scope_printf(wakefield->log, "WAKEFIELD: output '%s' has a chunk of the image at (%d, %d) sized (%d, %d)\n",
|
||||
output->name,
|
||||
e->x1, e->y1,
|
||||
width_in_output, height_in_output);
|
||||
|
||||
// Better, but not available in the current libweston:
|
||||
// weston_output_region_from_global(output, ®ion_in_output);
|
||||
|
||||
// Now convert region_in_output from global to output-local coordinates.
|
||||
pixman_region32_translate(®ion_in_output, -output->x, -output->y);
|
||||
|
||||
const pixman_box32_t * const e_in_output = pixman_region32_extents(®ion_in_output);
|
||||
const int32_t x_in_output = e_in_output->x1;
|
||||
const int32_t y_in_output = e_in_output->y1;
|
||||
|
||||
weston_log_scope_printf(wakefield->log,
|
||||
"WAKEFIELD: grabbing pixels at (%d, %d) of size %dx%d, format %s\n",
|
||||
x_in_output, y_in_output,
|
||||
width_in_output, height_in_output,
|
||||
buffer_format_pixman == PIXMAN_a8r8g8b8 ? "ARGB8888" : "XRGB8888");
|
||||
|
||||
if (per_output_buffer) {
|
||||
wakefield->compositor->renderer->read_pixels(output,
|
||||
buffer_format_pixman, // TODO: may not work with all renderers, check screenshooter_frame_notify() in libweston
|
||||
per_output_buffer,
|
||||
x_in_output, y_in_output,
|
||||
width_in_output, height_in_output);
|
||||
|
||||
copy_pixels_to_shm_buffer(buffer, per_output_buffer,
|
||||
region_x_in_global - x, region_y_in_global - y,
|
||||
width_in_output, height_in_output);
|
||||
} else {
|
||||
wl_shm_buffer_begin_access(buffer);
|
||||
{
|
||||
void *data = wl_shm_buffer_get_data(buffer);
|
||||
wakefield->compositor->renderer->read_pixels(output,
|
||||
buffer_format_pixman,
|
||||
data,
|
||||
x_in_output, y_in_output,
|
||||
width, height);
|
||||
}
|
||||
wl_shm_buffer_end_access(buffer);
|
||||
// This is the case of the entire region located on just one output,
|
||||
// and we have just processed it, so can exit immediately.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pixman_region32_fini(®ion_in_output);
|
||||
pixman_region32_fini(®ion_global);
|
||||
}
|
||||
|
||||
if (per_output_buffer) {
|
||||
free(per_output_buffer);
|
||||
}
|
||||
|
||||
wakefield_send_capture_ready(resource, buffer_resource, WAKEFIELD_ERROR_NO_ERROR);
|
||||
}
|
||||
|
||||
static const struct wakefield_interface wakefield_implementation = {
|
||||
.get_surface_location = wakefield_get_surface_location,
|
||||
.move_surface = wakefield_move_surface,
|
||||
.get_pixel_color = wakefield_get_pixel_color,
|
||||
.capture_create = wakefield_capture_create
|
||||
};
|
||||
|
||||
static void
|
||||
wakefield_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id)
|
||||
{
|
||||
struct wakefield *wakefield = data;
|
||||
|
||||
struct wl_resource *resource = wl_resource_create(client, &wakefield_interface, 1, id);
|
||||
if (resource) {
|
||||
wl_resource_set_implementation(resource, &wakefield_implementation, wakefield, NULL);
|
||||
}
|
||||
|
||||
weston_log_scope_printf(wakefield->log, "WAKEFIELD: bind\n");
|
||||
}
|
||||
|
||||
static void
|
||||
wakefield_destroy(struct wl_listener *listener, void *data)
|
||||
{
|
||||
struct wakefield *wakefield = container_of(listener, struct wakefield, destroy_listener);
|
||||
|
||||
weston_log_scope_printf(wakefield->log, "WAKEFIELD: destroy\n");
|
||||
|
||||
wl_list_remove(&wakefield->destroy_listener.link);
|
||||
|
||||
weston_log_scope_destroy(wakefield->log);
|
||||
free(wakefield);
|
||||
}
|
||||
|
||||
WL_EXPORT int
|
||||
wet_module_init(struct weston_compositor *wc, int *argc, char *argv[])
|
||||
{
|
||||
struct wakefield *wakefield = zalloc(sizeof(struct wakefield));
|
||||
if (wakefield == NULL)
|
||||
return -1;
|
||||
|
||||
if (!weston_compositor_add_destroy_listener_once(wc, &wakefield->destroy_listener,
|
||||
wakefield_destroy)) {
|
||||
free(wakefield);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
wakefield->compositor = wc;
|
||||
// Log scope; add this to weston option list to subscribe: `--logger-scopes=wakefield`
|
||||
// See https://wayland.pages.freedesktop.org/weston/toc/libweston/log.html for more info.
|
||||
wakefield->log = weston_compositor_add_log_scope(wc, "wakefield",
|
||||
"wakefield plugin own actions",
|
||||
NULL, NULL, NULL);
|
||||
|
||||
if (wl_global_create(wc->wl_display, &wakefield_interface,
|
||||
1, wakefield, wakefield_bind) == NULL) {
|
||||
wl_list_remove(&wakefield->destroy_listener.link);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -28,17 +28,51 @@ package sun.awt;
|
||||
import java.io.File;
|
||||
import java.awt.GraphicsEnvironment;
|
||||
import java.awt.Toolkit;
|
||||
import java.lang.annotation.Native;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
|
||||
import sun.awt.wl.WLGraphicsEnvironment;
|
||||
import sun.security.action.GetPropertyAction;
|
||||
|
||||
public class PlatformGraphicsInfo {
|
||||
@Native
|
||||
public static final int TK_UNDEF = 0;
|
||||
@Native
|
||||
public static final int TK_X11 = 1;
|
||||
@Native
|
||||
public static final int TK_WAYLAND = 2;
|
||||
|
||||
private static int toolkitID = TK_UNDEF;
|
||||
|
||||
private static int getToolkitID() {
|
||||
if (toolkitID == TK_UNDEF) {
|
||||
@SuppressWarnings("removal")
|
||||
String name = AccessController.doPrivileged(
|
||||
new GetPropertyAction("awt.toolkit.name"));
|
||||
if ("XToolkit".equals(name)) {
|
||||
toolkitID = TK_X11;
|
||||
} if ("WLToolkit".equals(name)) {
|
||||
toolkitID = TK_WAYLAND;
|
||||
} else {
|
||||
toolkitID = TK_X11;
|
||||
}
|
||||
}
|
||||
return toolkitID;
|
||||
}
|
||||
|
||||
public static GraphicsEnvironment createGE() {
|
||||
return new X11GraphicsEnvironment();
|
||||
return (getToolkitID() == TK_X11)?
|
||||
new X11GraphicsEnvironment() :
|
||||
WLGraphicsEnvironment.getSingleInstance();
|
||||
}
|
||||
|
||||
public static Toolkit createToolkit() {
|
||||
return new sun.awt.X11.XToolkit();
|
||||
return (getToolkitID() == TK_X11)?
|
||||
new sun.awt.X11.XToolkit() :
|
||||
new sun.awt.wl.WLToolkit();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -52,8 +86,26 @@ public class PlatformGraphicsInfo {
|
||||
boolean noDisplay =
|
||||
AccessController.doPrivileged((PrivilegedAction<Boolean>) () -> {
|
||||
|
||||
final String display = System.getenv("DISPLAY");
|
||||
return display == null || display.trim().isEmpty();
|
||||
if (getToolkitID() == TK_X11) {
|
||||
final String display = System.getenv("DISPLAY");
|
||||
return display == null || display.trim().isEmpty();
|
||||
} else {
|
||||
// This code needs to be in sync with what wl_display_connect() does
|
||||
// in WLToolkit.initIDs().
|
||||
final String wl_display = System.getenv("WAYLAND_DISPLAY");
|
||||
if (wl_display != null && !wl_display.trim().isEmpty()) {
|
||||
return false; // not headless
|
||||
}
|
||||
|
||||
// Check $XDG_RUNTIME_DIR/wayland-0.
|
||||
final String socketDir = System.getenv("XDG_RUNTIME_DIR");
|
||||
if (socketDir != null && !socketDir.trim().isEmpty()) {
|
||||
final Path defaultSocketPath = Path.of(socketDir).resolve("wayland-0");
|
||||
return !Files.isReadable(defaultSocketPath);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
});
|
||||
if (noDisplay) {
|
||||
return true;
|
||||
|
||||
79
src/java.desktop/unix/classes/sun/awt/wl/WLButtonPeer.java
Normal file
79
src/java.desktop/unix/classes/sun/awt/wl/WLButtonPeer.java
Normal file
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2021, JetBrains s.r.o.. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package sun.awt.wl;
|
||||
|
||||
import java.awt.Button;
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.event.FocusEvent;
|
||||
import java.awt.peer.ButtonPeer;
|
||||
import sun.util.logging.PlatformLogger;
|
||||
|
||||
public class WLButtonPeer extends WLComponentPeer implements ButtonPeer {
|
||||
private static final PlatformLogger log = PlatformLogger.getLogger("sun.awt.wl.WLButtonPeer");
|
||||
String label;
|
||||
|
||||
|
||||
public WLButtonPeer(Button target) {
|
||||
super(target);
|
||||
label = target.getLabel();
|
||||
}
|
||||
|
||||
public boolean isFocusable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLabel(String label) {
|
||||
log.info("Not implemented: WLButtonPeer.setLabel(String)");
|
||||
}
|
||||
|
||||
public void setBackground(Color c) {
|
||||
super.setBackground(c);
|
||||
}
|
||||
|
||||
public void focusGained(FocusEvent e) {
|
||||
super.focusGained(e);
|
||||
log.info("Not implemented: WLButtonPeer.focusGained(FocusEvent)");
|
||||
}
|
||||
|
||||
public void focusLost(FocusEvent e) {
|
||||
super.focusLost(e);
|
||||
log.info("Not implemented: WLButtonPeer.focusLost(FocusEvent)");
|
||||
}
|
||||
|
||||
@Override
|
||||
void paintPeer(Graphics g) {
|
||||
super.paintPeer(g);
|
||||
log.info("Not implemented: WLButtonPeer.paintPeer(Graphics)");
|
||||
}
|
||||
|
||||
public Dimension getMinimumSize() {
|
||||
log.info("Not implemented: WLButtonPeer.getMinimumSize()");
|
||||
return new Dimension(0, 0);
|
||||
}
|
||||
}
|
||||
45
src/java.desktop/unix/classes/sun/awt/wl/WLCanvasPeer.java
Normal file
45
src/java.desktop/unix/classes/sun/awt/wl/WLCanvasPeer.java
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright (c) 2002, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2022, JetBrains s.r.o.. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package sun.awt.wl;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.GraphicsConfiguration;
|
||||
import java.awt.GraphicsEnvironment;
|
||||
import java.awt.peer.CanvasPeer;
|
||||
|
||||
class WLCanvasPeer extends WLComponentPeer implements CanvasPeer {
|
||||
|
||||
WLCanvasPeer(Component target) {
|
||||
super(target);
|
||||
}
|
||||
|
||||
|
||||
public GraphicsConfiguration getAppropriateGraphicsConfiguration(
|
||||
GraphicsConfiguration gc)
|
||||
{
|
||||
return gc;
|
||||
}
|
||||
}
|
||||
1059
src/java.desktop/unix/classes/sun/awt/wl/WLComponentPeer.java
Normal file
1059
src/java.desktop/unix/classes/sun/awt/wl/WLComponentPeer.java
Normal file
File diff suppressed because it is too large
Load Diff
49
src/java.desktop/unix/classes/sun/awt/wl/WLCustomCursor.java
Normal file
49
src/java.desktop/unix/classes/sun/awt/wl/WLCustomCursor.java
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2022 JetBrains s.r.o.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.awt.wl;
|
||||
|
||||
import sun.awt.AWTAccessor;
|
||||
import sun.awt.CustomCursor;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
@SuppressWarnings("serial") // JDK-implementation class
|
||||
final class WLCustomCursor extends CustomCursor {
|
||||
WLCustomCursor(Image cursor, Point hotSpot, String name) throws IndexOutOfBoundsException {
|
||||
super(cursor, hotSpot, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void createNativeCursor(Image im, int[] pixels, int width, int height, int xHotSpot, int yHotSpot) {
|
||||
long pData = nativeCreateCustomCursor(pixels, width, height, xHotSpot, yHotSpot);
|
||||
if (pData == 0) {
|
||||
pData = -1; // mark as unavailable
|
||||
}
|
||||
AWTAccessor.getCursorAccessor().setPData(this, pData);
|
||||
}
|
||||
|
||||
private static native long nativeCreateCustomCursor(int[] pixels, int width, int height, int xHotSpot, int yHotSpot);
|
||||
}
|
||||
158
src/java.desktop/unix/classes/sun/awt/wl/WLDecoratedPeer.java
Normal file
158
src/java.desktop/unix/classes/sun/awt/wl/WLDecoratedPeer.java
Normal file
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* Copyright (c) 2022, JetBrains s.r.o.. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package sun.awt.wl;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.event.WindowEvent;
|
||||
|
||||
public abstract class WLDecoratedPeer extends WLWindowPeer {
|
||||
private final WLFrameDecoration decoration;
|
||||
|
||||
public WLDecoratedPeer(Window target, boolean isUndecorated, boolean showMinimize, boolean showMaximize) {
|
||||
super(target);
|
||||
decoration = isUndecorated ? null : new WLFrameDecoration(this, showMinimize, showMaximize);
|
||||
}
|
||||
|
||||
private static native void initIDs();
|
||||
|
||||
static {
|
||||
if (!GraphicsEnvironment.isHeadless()) {
|
||||
initIDs();
|
||||
}
|
||||
}
|
||||
|
||||
public abstract boolean isResizable();
|
||||
|
||||
public abstract void setState(int newState);
|
||||
public abstract int getState();
|
||||
public abstract void setExtendedState(int newState);
|
||||
|
||||
@Override
|
||||
public Insets getInsets() {
|
||||
return decoration == null ? new Insets(0, 0, 0, 0) : decoration.getInsets();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beginValidate() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endValidate() {
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
setFrameTitle(title);
|
||||
notifyClientDecorationsChanged();
|
||||
}
|
||||
|
||||
public void setResizable(boolean resizeable) {
|
||||
notifyClientDecorationsChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension getMinimumSize() {
|
||||
final Dimension targetMinimumSize = target.isMinimumSizeSet()
|
||||
? target.getMinimumSize()
|
||||
: new Dimension(1, 1);
|
||||
final Dimension frameMinimumSize = decoration != null
|
||||
? decoration.getMinimumSize()
|
||||
: new Dimension(1, 1);
|
||||
return new Rectangle(targetMinimumSize)
|
||||
.union(new Rectangle(frameMinimumSize))
|
||||
.getSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateWindow() {
|
||||
// signals the end of repainting by Swing and/or AWT
|
||||
paintClientDecorations(getGraphics());
|
||||
super.updateWindow();
|
||||
}
|
||||
|
||||
// called from native code
|
||||
void postWindowClosing() {
|
||||
WLToolkit.postEvent(new WindowEvent((Window) target, WindowEvent.WINDOW_CLOSING));
|
||||
}
|
||||
|
||||
@Override
|
||||
void postMouseEvent(MouseEvent e) {
|
||||
if (decoration == null) {
|
||||
super.postMouseEvent(e);
|
||||
} else {
|
||||
decoration.processMouseEvent(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void notifyConfigured(int newWidth, int newHeight, boolean active, boolean maximized) {
|
||||
super.notifyConfigured(newWidth, newHeight, active, maximized);
|
||||
if (decoration != null) decoration.setActive(active);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rectangle getVisibleBounds() {
|
||||
// TODO: modify if our decorations ever acquire special effects that
|
||||
// do not count into "visible bounds" of the window
|
||||
return super.getVisibleBounds();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBounds(int x, int y, int width, int height, int op) {
|
||||
super.setBounds(x, y, width, height, op);
|
||||
notifyClientDecorationsChanged();
|
||||
}
|
||||
|
||||
final void notifyClientDecorationsChanged() {
|
||||
if (decoration != null) {
|
||||
final Rectangle bounds = decoration.getBounds();
|
||||
decoration.markRepaintNeeded();
|
||||
postPaintEvent(getTarget(), bounds.x, bounds.y, bounds.width, bounds.height);
|
||||
}
|
||||
}
|
||||
|
||||
void postPaintEvent() {
|
||||
// Full re-paint must include window decorations, if any
|
||||
notifyClientDecorationsChanged();
|
||||
super.postPaintEvent();
|
||||
}
|
||||
|
||||
final void paintClientDecorations(final Graphics g) {
|
||||
if (decoration != null && decoration.isRepaintNeeded()) {
|
||||
decoration.paint(g);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
Cursor getCursor(int x, int y) {
|
||||
if (decoration != null) {
|
||||
Cursor cursor = decoration.getCursor(x, y);
|
||||
if (cursor != null) {
|
||||
return cursor;
|
||||
}
|
||||
}
|
||||
return super.getCursor(x, y);
|
||||
}
|
||||
}
|
||||
71
src/java.desktop/unix/classes/sun/awt/wl/WLDialogPeer.java
Normal file
71
src/java.desktop/unix/classes/sun/awt/wl/WLDialogPeer.java
Normal file
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (c) 2022, JetBrains s.r.o.. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package sun.awt.wl;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.peer.DialogPeer;
|
||||
import java.util.List;
|
||||
|
||||
public class WLDialogPeer extends WLDecoratedPeer implements DialogPeer {
|
||||
|
||||
public WLDialogPeer(Dialog target) {
|
||||
super(target, target.isUndecorated(), false, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVisible(boolean vis) {
|
||||
super.setVisible(vis);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void blockWindows(List<Window> windows) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isResizable() {
|
||||
return ((Dialog)target).isResizable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTitle() {
|
||||
return ((Dialog)target).getTitle();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setState(int newState) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getState() {
|
||||
return Frame.NORMAL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setExtendedState(int newState) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
388
src/java.desktop/unix/classes/sun/awt/wl/WLFrameDecoration.java
Normal file
388
src/java.desktop/unix/classes/sun/awt/wl/WLFrameDecoration.java
Normal file
@@ -0,0 +1,388 @@
|
||||
/*
|
||||
* Copyright 2022 JetBrains s.r.o.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package sun.awt.wl;
|
||||
|
||||
import sun.swing.SwingUtilities2;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import java.awt.geom.Ellipse2D;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class WLFrameDecoration {
|
||||
private static final int HEIGHT = 30;
|
||||
private static final int BUTTON_ICON_SIZE = 3;
|
||||
private static final int BUTTON_CIRCLE_SIZE = 9;
|
||||
private static final int BUTTON_MAXIMIZED_LINE_GAP = 2;
|
||||
private static final Font FONT = new Font(Font.DIALOG, Font.BOLD, 12);
|
||||
private static final Color BACKGROUND = Color.white;
|
||||
private static final Color ICON_BACKGROUND = new Color(0xe8e8e8);
|
||||
private static final Color ICON_HOVERED_BACKGROUND = new Color(0xe0e0e0);
|
||||
private static final Color ICON_PRESSED_BACKGROUND = Color.lightGray;
|
||||
private static final Color ACTIVE_FOREGROUND = Color.darkGray;
|
||||
private static final Color INACTIVE_FOREGROUND = Color.gray;
|
||||
private static final int SIGNIFICANT_DRAG_DISTANCE = 4;
|
||||
private static final int RESIZE_EDGE_THICKNESS = 5;
|
||||
|
||||
private static final int XDG_TOPLEVEL_RESIZE_EDGE_TOP = 1;
|
||||
private static final int XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM = 2;
|
||||
private static final int XDG_TOPLEVEL_RESIZE_EDGE_LEFT = 4;
|
||||
private static final int XDG_TOPLEVEL_RESIZE_EDGE_RIGHT = 8;
|
||||
|
||||
private static final int[] RESIZE_CURSOR_TYPES = {
|
||||
-1, // not used
|
||||
Cursor.N_RESIZE_CURSOR,
|
||||
Cursor.S_RESIZE_CURSOR,
|
||||
-1, // not used
|
||||
Cursor.W_RESIZE_CURSOR,
|
||||
Cursor.NW_RESIZE_CURSOR,
|
||||
Cursor.SW_RESIZE_CURSOR,
|
||||
-1, // not used
|
||||
Cursor.E_RESIZE_CURSOR,
|
||||
Cursor.NE_RESIZE_CURSOR,
|
||||
Cursor.SE_RESIZE_CURSOR
|
||||
};
|
||||
|
||||
private final WLDecoratedPeer peer;
|
||||
|
||||
private final ButtonState closeButton;
|
||||
private final ButtonState maximizeButton;
|
||||
private final ButtonState minimizeButton;
|
||||
|
||||
private boolean active;
|
||||
private boolean pointerInside;
|
||||
private boolean pressedInside;
|
||||
private Point pressedLocation;
|
||||
|
||||
public WLFrameDecoration(WLDecoratedPeer peer, boolean showMinimize, boolean showMaximize) {
|
||||
this.peer = peer;
|
||||
closeButton = new ButtonState(this::getCloseButtonCenter, peer::postWindowClosing);
|
||||
maximizeButton = showMaximize ? new ButtonState(this::getMaximizeButtonCenter, this::toggleMaximizedState) : null;
|
||||
minimizeButton = showMinimize ? new ButtonState(this::getMinimizeButtonCenter, this::minimizeWindow) : null;
|
||||
}
|
||||
|
||||
public Insets getInsets() {
|
||||
return new Insets(HEIGHT, 0, 0, 0);
|
||||
}
|
||||
|
||||
public Rectangle getBounds() {
|
||||
return new Rectangle(0, 0, peer.getWidth(), HEIGHT);
|
||||
}
|
||||
|
||||
public Dimension getMinimumSize() {
|
||||
return new Dimension(getButtonSpaceWidth(), HEIGHT);
|
||||
}
|
||||
|
||||
private boolean hasMinimizeButton() {
|
||||
return minimizeButton != null;
|
||||
}
|
||||
|
||||
private boolean hasMaximizeButton() {
|
||||
return maximizeButton != null && peer.isResizable();
|
||||
}
|
||||
|
||||
private Point getCloseButtonCenter() {
|
||||
int width = peer.getWidth();
|
||||
return width >= HEIGHT ? new Point(width - HEIGHT / 2, HEIGHT / 2) : null;
|
||||
}
|
||||
|
||||
private Point getMaximizeButtonCenter() {
|
||||
if (!hasMaximizeButton()) return null;
|
||||
int width = peer.getWidth();
|
||||
return width >= 2 * HEIGHT ? new Point(width - HEIGHT * 3 / 2, HEIGHT / 2) : null;
|
||||
}
|
||||
|
||||
private Point getMinimizeButtonCenter() {
|
||||
if (!hasMinimizeButton()) return null;
|
||||
int width = peer.getWidth();
|
||||
int buttonSpaceWidth = getButtonSpaceWidth();
|
||||
return width >= buttonSpaceWidth ? new Point(width - buttonSpaceWidth + HEIGHT / 2, HEIGHT / 2) : null;
|
||||
}
|
||||
|
||||
private int getButtonSpaceWidth() {
|
||||
final int numButtons = 1
|
||||
+ (hasMaximizeButton() ? 1 : 0)
|
||||
+ (hasMinimizeButton() ? 1 : 0);
|
||||
return numButtons * HEIGHT;
|
||||
}
|
||||
|
||||
public void paint(final Graphics g) {
|
||||
int width = peer.getWidth();
|
||||
int height = peer.getHeight();
|
||||
if (width <= 0 || height <= 0) return;
|
||||
Graphics2D g2d = (Graphics2D) g.create(0, 0, width, HEIGHT);
|
||||
try {
|
||||
doPaint(g2d);
|
||||
} finally {
|
||||
g2d.dispose();
|
||||
needRepaint = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void doPaint(Graphics2D g) {
|
||||
int width = peer.getWidth();
|
||||
String title = peer.getTitle();
|
||||
Color foregroundColor = active ? ACTIVE_FOREGROUND : INACTIVE_FOREGROUND;
|
||||
|
||||
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
|
||||
|
||||
g.setColor(BACKGROUND);
|
||||
g.fillRect(0, 0, width, HEIGHT);
|
||||
|
||||
paintTitle(g, title, foregroundColor, width);
|
||||
|
||||
Point closeButtonCenter = getCloseButtonCenter();
|
||||
if (closeButtonCenter != null) {
|
||||
paintButtonBackground(g, closeButtonCenter, closeButton);
|
||||
paintCloseButton(g, closeButtonCenter, foregroundColor);
|
||||
}
|
||||
Point maximizedButtonCenter = getMaximizeButtonCenter();
|
||||
if (maximizedButtonCenter != null) {
|
||||
paintButtonBackground(g, maximizedButtonCenter, maximizeButton);
|
||||
paintMaximizeButton(g, maximizedButtonCenter, foregroundColor);
|
||||
}
|
||||
Point minimizedButtonCenter = getMinimizeButtonCenter();
|
||||
if (minimizedButtonCenter != null) {
|
||||
paintButtonBackground(g, minimizedButtonCenter, minimizeButton);
|
||||
paintMinimizeButton(g, minimizedButtonCenter, foregroundColor);
|
||||
}
|
||||
}
|
||||
|
||||
private void paintTitle(Graphics2D g, String title, Color foregroundColor, int width) {
|
||||
g.setColor(foregroundColor);
|
||||
g.setFont(FONT);
|
||||
FontMetrics fm = g.getFontMetrics();
|
||||
int leftMargin = HEIGHT / 2 - BUTTON_CIRCLE_SIZE; // same as space between close button and right window edge
|
||||
int availableWidth = width - getButtonSpaceWidth() - leftMargin;
|
||||
String text = SwingUtilities2.clipStringIfNecessary(null, fm, title, availableWidth);
|
||||
int textWidth = fm.stringWidth(text);
|
||||
g.drawString(text,
|
||||
Math.min((width - textWidth) / 2, availableWidth - textWidth),
|
||||
(HEIGHT - fm.getHeight()) / 2 + fm.getAscent());
|
||||
}
|
||||
|
||||
private void paintButtonBackground(Graphics2D g, Point center, ButtonState state) {
|
||||
if (active) {
|
||||
g.setColor(state.pressed ? ICON_PRESSED_BACKGROUND :
|
||||
state.hovered ? ICON_HOVERED_BACKGROUND : ICON_BACKGROUND);
|
||||
g.fill(new Ellipse2D.Float(center.x - BUTTON_CIRCLE_SIZE + .5f,
|
||||
center.y - BUTTON_CIRCLE_SIZE + .5f,
|
||||
2 * BUTTON_CIRCLE_SIZE, 2 * BUTTON_CIRCLE_SIZE));
|
||||
}
|
||||
}
|
||||
|
||||
private void paintCloseButton(Graphics2D g, Point center, Color foregroundColor) {
|
||||
g.setColor(foregroundColor);
|
||||
g.drawLine(center.x - BUTTON_ICON_SIZE, center.y - BUTTON_ICON_SIZE,
|
||||
center.x + BUTTON_ICON_SIZE, center.y + BUTTON_ICON_SIZE);
|
||||
g.drawLine(center.x - BUTTON_ICON_SIZE, center.y + BUTTON_ICON_SIZE,
|
||||
center.x + BUTTON_ICON_SIZE, center.y - BUTTON_ICON_SIZE);
|
||||
}
|
||||
|
||||
private void paintMaximizeButton(Graphics2D g, Point center, Color foregroundColor) {
|
||||
g.setColor(foregroundColor);
|
||||
if (peer.getState() == Frame.MAXIMIZED_BOTH) {
|
||||
g.drawRect(center.x - BUTTON_ICON_SIZE, center.y - BUTTON_ICON_SIZE + BUTTON_MAXIMIZED_LINE_GAP,
|
||||
2 * BUTTON_ICON_SIZE - BUTTON_MAXIMIZED_LINE_GAP, 2 * BUTTON_ICON_SIZE - BUTTON_MAXIMIZED_LINE_GAP);
|
||||
g.drawLine(center.x - BUTTON_ICON_SIZE + BUTTON_MAXIMIZED_LINE_GAP, center.y - BUTTON_ICON_SIZE,
|
||||
center.x + BUTTON_ICON_SIZE, center.y - BUTTON_ICON_SIZE);
|
||||
g.drawLine(center.x + BUTTON_ICON_SIZE, center.y - BUTTON_ICON_SIZE,
|
||||
center.x + BUTTON_ICON_SIZE, center.y + BUTTON_ICON_SIZE - BUTTON_MAXIMIZED_LINE_GAP);
|
||||
} else {
|
||||
g.drawRect(center.x - BUTTON_ICON_SIZE, center.y - BUTTON_ICON_SIZE,
|
||||
2 * BUTTON_ICON_SIZE, 2 * BUTTON_ICON_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
private void paintMinimizeButton(Graphics2D g, Point center, Color foregroundColor) {
|
||||
g.setColor(foregroundColor);
|
||||
g.drawLine(center.x - BUTTON_ICON_SIZE, center.y + BUTTON_ICON_SIZE,
|
||||
center.x + BUTTON_ICON_SIZE, center.y + BUTTON_ICON_SIZE);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private static MouseEvent convertEvent(MouseEvent e, int newId) {
|
||||
return new MouseEvent(e.getComponent(),
|
||||
newId,
|
||||
e.getWhen(),
|
||||
e.getModifiers() | e.getModifiersEx(),
|
||||
e.getX(),
|
||||
e.getY(),
|
||||
e.getXOnScreen(),
|
||||
e.getYOnScreen(),
|
||||
e.getClickCount(),
|
||||
e.isPopupTrigger(),
|
||||
e.getButton());
|
||||
}
|
||||
|
||||
private boolean isSignificantDrag(Point p) {
|
||||
return pressedLocation != null && pressedLocation.distance(p) > SIGNIFICANT_DRAG_DISTANCE;
|
||||
}
|
||||
|
||||
private boolean pressedInDragStartArea() {
|
||||
return pressedLocation != null &&
|
||||
pressedLocation.y >= 0 &&
|
||||
pressedLocation.y < HEIGHT &&
|
||||
pressedLocation.x >= 0 &&
|
||||
pressedLocation.x < peer.getWidth() - getButtonSpaceWidth();
|
||||
}
|
||||
|
||||
void processMouseEvent(MouseEvent e) {
|
||||
final boolean isLMB = e.getButton() == MouseEvent.BUTTON1;
|
||||
final boolean isRMB = e.getButton() == MouseEvent.BUTTON3;
|
||||
final boolean isPressed = e.getID() == MouseEvent.MOUSE_PRESSED;
|
||||
final boolean isLMBPressed = isLMB && isPressed;
|
||||
final boolean isRMBPressed = isRMB && isPressed;
|
||||
|
||||
if (isRMBPressed && getBounds().contains(e.getX(), e.getY())) {
|
||||
peer.showWindowMenu(e.getX(), e.getY());
|
||||
return;
|
||||
}
|
||||
|
||||
Point point = e.getPoint();
|
||||
if (isLMBPressed && peer.isResizable()) {
|
||||
int resizeSide = getResizeEdges(point.x, point.y);
|
||||
if (resizeSide != 0) {
|
||||
peer.startResize(resizeSide);
|
||||
// workaround for https://gitlab.gnome.org/GNOME/mutter/-/issues/2523
|
||||
WLToolkit.resetPointerInputState();
|
||||
return;
|
||||
}
|
||||
}
|
||||
boolean pointerInside = e.getY() >= HEIGHT && e.getID() != MouseEvent.MOUSE_EXITED ||
|
||||
pressedInside && e.getID() == MouseEvent.MOUSE_DRAGGED;
|
||||
if (pointerInside && !this.pointerInside && e.getID() != MouseEvent.MOUSE_ENTERED) {
|
||||
WLToolkit.postEvent(convertEvent(e, MouseEvent.MOUSE_ENTERED));
|
||||
}
|
||||
if (pointerInside || this.pointerInside && e.getID() == MouseEvent.MOUSE_EXITED) {
|
||||
WLToolkit.postEvent(e);
|
||||
}
|
||||
if (!pointerInside && this.pointerInside && e.getID() != MouseEvent.MOUSE_EXITED) {
|
||||
WLToolkit.postEvent(convertEvent(e, MouseEvent.MOUSE_EXITED));
|
||||
}
|
||||
this.pointerInside = pointerInside;
|
||||
if (e.getID() == MouseEvent.MOUSE_DRAGGED) {
|
||||
pressedInside = pointerInside;
|
||||
}
|
||||
if (closeButton.processMouseEvent(e) |
|
||||
(maximizeButton != null && maximizeButton.processMouseEvent(e)) |
|
||||
(minimizeButton != null && minimizeButton.processMouseEvent(e))) {
|
||||
peer.notifyClientDecorationsChanged();
|
||||
}
|
||||
if (e.getID() == MouseEvent.MOUSE_PRESSED) {
|
||||
pressedLocation = point;
|
||||
} else if (e.getID() == MouseEvent.MOUSE_DRAGGED && pressedInDragStartArea() && isSignificantDrag(point)) {
|
||||
peer.startDrag();
|
||||
} else if (e.getID() == MouseEvent.MOUSE_CLICKED && e.getClickCount() == 2 && pressedInDragStartArea()
|
||||
&& peer.isResizable()) {
|
||||
toggleMaximizedState();
|
||||
} else if (e.getID() == MouseEvent.MOUSE_MOVED && !pointerInside) {
|
||||
peer.updateCursorImmediately();
|
||||
}
|
||||
}
|
||||
|
||||
private int getResizeEdges(int x, int y) {
|
||||
if (!peer.isResizable()) return 0;
|
||||
int edges = 0;
|
||||
if (x < RESIZE_EDGE_THICKNESS) {
|
||||
edges |= XDG_TOPLEVEL_RESIZE_EDGE_LEFT;
|
||||
} else if (x > peer.getWidth() - RESIZE_EDGE_THICKNESS) {
|
||||
edges |= XDG_TOPLEVEL_RESIZE_EDGE_RIGHT;
|
||||
}
|
||||
if (y < RESIZE_EDGE_THICKNESS) {
|
||||
edges |= XDG_TOPLEVEL_RESIZE_EDGE_TOP;
|
||||
} else if (y > peer.getHeight() - RESIZE_EDGE_THICKNESS) {
|
||||
edges |= XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM;
|
||||
}
|
||||
return edges;
|
||||
}
|
||||
|
||||
void setActive(boolean active) {
|
||||
if (active != this.active) {
|
||||
this.active = active;
|
||||
peer.notifyClientDecorationsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private void toggleMaximizedState() {
|
||||
peer.setExtendedState(peer.getState() == Frame.NORMAL ? Frame.MAXIMIZED_BOTH : Frame.NORMAL);
|
||||
}
|
||||
|
||||
private void minimizeWindow() {
|
||||
peer.setState(Frame.ICONIFIED);
|
||||
}
|
||||
|
||||
private volatile boolean needRepaint = true;
|
||||
|
||||
boolean isRepaintNeeded() {
|
||||
return needRepaint;
|
||||
}
|
||||
|
||||
void markRepaintNeeded() {
|
||||
needRepaint = true;
|
||||
}
|
||||
|
||||
Cursor getCursor(int x, int y) {
|
||||
int edges = getResizeEdges(x, y);
|
||||
if (edges != 0) {
|
||||
return Cursor.getPredefinedCursor(RESIZE_CURSOR_TYPES[edges]);
|
||||
}
|
||||
if (y < HEIGHT) {
|
||||
return Cursor.getDefaultCursor();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static class ButtonState {
|
||||
private final Supplier<Point> location;
|
||||
private final Runnable action;
|
||||
private boolean hovered;
|
||||
private boolean pressed;
|
||||
|
||||
private ButtonState(Supplier<Point> location, Runnable action) {
|
||||
this.location = location;
|
||||
this.action = action;
|
||||
}
|
||||
|
||||
private boolean processMouseEvent(MouseEvent e) {
|
||||
Point buttonCenter = location.get();
|
||||
boolean ourLocation = buttonCenter != null && e.getID() != MouseEvent.MOUSE_EXITED &&
|
||||
Math.abs(buttonCenter.x - e.getX()) <= BUTTON_CIRCLE_SIZE &&
|
||||
Math.abs(buttonCenter.y - e.getY()) <= BUTTON_CIRCLE_SIZE;
|
||||
boolean oldHovered = hovered;
|
||||
boolean oldPressed = pressed;
|
||||
hovered = ourLocation;
|
||||
if (ourLocation && e.getID() == MouseEvent.MOUSE_PRESSED) {
|
||||
pressed = true;
|
||||
} else if (e.getID() == MouseEvent.MOUSE_RELEASED) {
|
||||
pressed = false;
|
||||
} else if (ourLocation && e.getID() == MouseEvent.MOUSE_CLICKED) {
|
||||
action.run();
|
||||
}
|
||||
return oldPressed != pressed || !pressed && oldHovered != hovered;
|
||||
}
|
||||
}
|
||||
}
|
||||
167
src/java.desktop/unix/classes/sun/awt/wl/WLFramePeer.java
Normal file
167
src/java.desktop/unix/classes/sun/awt/wl/WLFramePeer.java
Normal file
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2022, JetBrains s.r.o.. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package sun.awt.wl;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.WindowEvent;
|
||||
import java.awt.peer.FramePeer;
|
||||
import sun.awt.AWTAccessor;
|
||||
import sun.util.logging.PlatformLogger;
|
||||
|
||||
public class WLFramePeer extends WLDecoratedPeer implements FramePeer {
|
||||
private static final PlatformLogger log = PlatformLogger.getLogger("sun.awt.wl.WLFramePeer");
|
||||
|
||||
private int state; // Guarded by getStateLock()
|
||||
private int widthBeforeMaximized; // Guarded by getStateLock()
|
||||
private int heightBeforeMaximized; // Guarded by getStateLock()
|
||||
|
||||
public WLFramePeer(Frame target) {
|
||||
super(target, target.isUndecorated(),
|
||||
Toolkit.getDefaultToolkit().isFrameStateSupported(Frame.ICONIFIED),
|
||||
Toolkit.getDefaultToolkit().isFrameStateSupported(Frame.MAXIMIZED_BOTH));
|
||||
}
|
||||
|
||||
@Override
|
||||
void configureWLSurface() {
|
||||
super.configureWLSurface();
|
||||
int state = getFrame().getExtendedState();
|
||||
if (state != Frame.NORMAL) {
|
||||
setState(state);
|
||||
}
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public void beginLayout() {
|
||||
// log.info("Not implemented: WLFramePeer.beginLayout()");
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void endLayout() {
|
||||
// log.info("Not implemented: WLFramePeer.endLayout()");
|
||||
// }
|
||||
|
||||
@Override
|
||||
public void setMenuBar(MenuBar mb) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isResizable() {
|
||||
return getFrame().isResizable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTitle() {
|
||||
return getFrame().getTitle();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setState(int newState) {
|
||||
if (!isVisible()) return;
|
||||
|
||||
if ((newState & Frame.ICONIFIED) != 0) {
|
||||
// Per xdg-shell.xml, "There is no way to know if the surface
|
||||
// is currently minimized, nor is there any way to unset
|
||||
// minimization on this surface". So 'state' will never
|
||||
// have 'Frame.ICONIFIED' bit set and every
|
||||
// request to iconify will be granted.
|
||||
requestMinimized();
|
||||
AWTAccessor.getFrameAccessor().setExtendedState(getFrame(), newState & ~Frame.ICONIFIED);
|
||||
} else if (newState == Frame.MAXIMIZED_BOTH) {
|
||||
requestMaximized();
|
||||
} else /* Frame.NORMAL */ {
|
||||
requestUnmaximized();
|
||||
}
|
||||
}
|
||||
|
||||
public void setExtendedState(int newState) {
|
||||
getFrame().setExtendedState(newState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getState() {
|
||||
synchronized(getStateLock()) {
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaximizedBounds(Rectangle bounds) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBoundsPrivate(int x, int y, int width, int height) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rectangle getBoundsPrivate() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void emulateActivation(boolean activate) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toFront() {
|
||||
//throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toBack() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
void notifyConfigured(int newWidth, int newHeight, boolean active, boolean maximized) {
|
||||
int widthBefore = getWidth();
|
||||
int heightBefore = getHeight();
|
||||
|
||||
super.notifyConfigured(newWidth, newHeight, active, maximized);
|
||||
|
||||
synchronized (getStateLock()) {
|
||||
int oldState = state;
|
||||
state = maximized ? Frame.MAXIMIZED_BOTH : Frame.NORMAL;
|
||||
AWTAccessor.getFrameAccessor().setExtendedState(getFrame(), state);
|
||||
if (state != oldState) {
|
||||
if (maximized) {
|
||||
widthBeforeMaximized = widthBefore;
|
||||
heightBeforeMaximized = heightBefore;
|
||||
} else if (newWidth == 0 && newHeight == 0 && widthBeforeMaximized > 0 && heightBeforeMaximized > 0) {
|
||||
performUnlocked(() -> target.setSize(widthBeforeMaximized, heightBeforeMaximized));
|
||||
}
|
||||
WLToolkit.postEvent(new WindowEvent(getFrame(), WindowEvent.WINDOW_STATE_CHANGED, oldState, state));
|
||||
notifyClientDecorationsChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Frame getFrame() {
|
||||
return (Frame)target;
|
||||
}
|
||||
}
|
||||
100
src/java.desktop/unix/classes/sun/awt/wl/WLGraphicsConfig.java
Normal file
100
src/java.desktop/unix/classes/sun/awt/wl/WLGraphicsConfig.java
Normal file
@@ -0,0 +1,100 @@
|
||||
package sun.awt.wl;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.image.ColorModel;
|
||||
import java.awt.image.DirectColorModel;
|
||||
import java.awt.image.WritableRaster;
|
||||
|
||||
import sun.awt.image.OffScreenImage;
|
||||
import sun.java2d.SurfaceData;
|
||||
import sun.java2d.loops.SurfaceType;
|
||||
import sun.java2d.wl.WLSurfaceData;
|
||||
|
||||
public class WLGraphicsConfig extends GraphicsConfiguration {
|
||||
private final WLGraphicsDevice device;
|
||||
|
||||
private final Object dataLock = new Object();
|
||||
|
||||
private final Rectangle bounds = new Rectangle(800, 600);
|
||||
|
||||
// This is Wayland scale for the use with wl_buffers only.
|
||||
// From wayland.xml, wl_surface.set_buffer_scale request:
|
||||
// "It is intended that you pick the same buffer scale as the scale of the
|
||||
// output that the surface is displayed on."
|
||||
private int wlBufferScale = 1;
|
||||
|
||||
public WLGraphicsConfig(WLGraphicsDevice device) {
|
||||
this.device = device;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WLGraphicsDevice getDevice() {
|
||||
return device;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ColorModel getColorModel() {
|
||||
return new DirectColorModel(24, 0xff0000, 0xff00, 0xff);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ColorModel getColorModel(int transparency) {
|
||||
return switch (transparency) {
|
||||
case Transparency.OPAQUE -> getColorModel();
|
||||
case Transparency.BITMASK -> new DirectColorModel(25, 0xff0000, 0xff00, 0xff, 0x1000000);
|
||||
case Transparency.TRANSLUCENT -> new DirectColorModel(32, 0xff0000, 0xff00, 0xff, 0xff000000);
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
|
||||
public Image createAcceleratedImage(Component target,
|
||||
int width, int height)
|
||||
{
|
||||
ColorModel model = getColorModel(Transparency.OPAQUE);
|
||||
WritableRaster raster = model.createCompatibleWritableRaster(width, height);
|
||||
return new OffScreenImage(target, model, raster, model.isAlphaPremultiplied());
|
||||
}
|
||||
|
||||
@Override
|
||||
public AffineTransform getDefaultTransform() {
|
||||
double scale = getScale();
|
||||
return AffineTransform.getScaleInstance(scale, scale);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AffineTransform getNormalizingTransform() {
|
||||
// TODO: may not be able to implement this fully, but we can try
|
||||
// obtaining physical width/height from wl_output.geometry event.
|
||||
// Those may be 0, of course.
|
||||
return getDefaultTransform();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rectangle getBounds() {
|
||||
synchronized (dataLock) {
|
||||
return bounds;
|
||||
}
|
||||
}
|
||||
|
||||
public SurfaceType getSurfaceType() {
|
||||
return SurfaceType.IntArgb;
|
||||
}
|
||||
|
||||
public SurfaceData createSurfaceData(WLComponentPeer peer) {
|
||||
return WLSurfaceData.createData(peer);
|
||||
}
|
||||
|
||||
int getScale() {
|
||||
synchronized (dataLock) {
|
||||
return wlBufferScale;
|
||||
}
|
||||
}
|
||||
|
||||
void update(int width, int height, int scale) {
|
||||
synchronized (dataLock) {
|
||||
this.wlBufferScale = scale;
|
||||
bounds.setSize(width, height);
|
||||
}
|
||||
}
|
||||
}
|
||||
125
src/java.desktop/unix/classes/sun/awt/wl/WLGraphicsDevice.java
Normal file
125
src/java.desktop/unix/classes/sun/awt/wl/WLGraphicsDevice.java
Normal file
@@ -0,0 +1,125 @@
|
||||
package sun.awt.wl;
|
||||
|
||||
import sun.awt.AWTAccessor;
|
||||
import sun.awt.DisplayChangedListener;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class WLGraphicsDevice extends GraphicsDevice implements DisplayChangedListener {
|
||||
private final int wlID; // ID of wl_output object received from Wayland
|
||||
private String name;
|
||||
private int x;
|
||||
private int y;
|
||||
|
||||
private final java.util.List<WLComponentPeer> peers = new ArrayList<>();
|
||||
private final WLGraphicsConfig config = new WLGraphicsConfig(this);
|
||||
|
||||
public WLGraphicsDevice(int id) {
|
||||
this.wlID = id;
|
||||
}
|
||||
|
||||
int getWLID() {
|
||||
return wlID;
|
||||
}
|
||||
|
||||
void updateConfiguration(String name, int x, int y, int width, int height, int scale) {
|
||||
this.name = name == null ? "wl_output." + wlID : name;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
config.update(width, height, scale);
|
||||
}
|
||||
|
||||
void updateConfiguration(WLGraphicsDevice gd) {
|
||||
final Rectangle bounds = config.getBounds();
|
||||
updateConfiguration(gd.name, gd.x, gd.y, bounds.width, bounds.height, config.getScale());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getType() {
|
||||
return TYPE_RASTER_SCREEN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIDstring() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GraphicsConfiguration[] getConfigurations() {
|
||||
// From wayland.xml, wl_output.mode event:
|
||||
// "Non-current modes are deprecated. A compositor can decide to only
|
||||
// advertise the current mode and never send other modes. Clients
|
||||
// should not rely on non-current modes."
|
||||
// So there is just one config, always.
|
||||
return new GraphicsConfiguration[] {config};
|
||||
}
|
||||
|
||||
@Override
|
||||
public GraphicsConfiguration getDefaultConfiguration() {
|
||||
return config;
|
||||
}
|
||||
|
||||
int getScale() {
|
||||
return config.getScale();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFullScreenSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void setFullScreenWindow(Window w) {
|
||||
Window old = getFullScreenWindow();
|
||||
if (w == old) {
|
||||
return;
|
||||
}
|
||||
|
||||
super.setFullScreenWindow(w);
|
||||
|
||||
if (isFullScreenSupported()) {
|
||||
if (w != null) {
|
||||
enterFullScreenExclusive(w);
|
||||
} else {
|
||||
exitFullScreenExclusive(old);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void displayChanged() {
|
||||
synchronized (peers) {
|
||||
peers.forEach(WLComponentPeer::displayChanged);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paletteChanged() {
|
||||
}
|
||||
|
||||
private void enterFullScreenExclusive(Window w) {
|
||||
WLComponentPeer peer = AWTAccessor.getComponentAccessor().getPeer(w);
|
||||
if (peer != null) {
|
||||
peer.requestFullScreen(wlID);
|
||||
}
|
||||
}
|
||||
|
||||
private void exitFullScreenExclusive(Window w) {
|
||||
WLComponentPeer peer = AWTAccessor.getComponentAccessor().getPeer(w);
|
||||
if (peer != null) {
|
||||
peer.requestUnsetFullScreen();
|
||||
}
|
||||
}
|
||||
|
||||
public void addWindow(WLComponentPeer peer) {
|
||||
synchronized (peers) {
|
||||
peers.add(peer);
|
||||
}
|
||||
}
|
||||
|
||||
public void removeWindow(WLComponentPeer peer) {
|
||||
synchronized (peers) {
|
||||
peers.remove(peer);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
package sun.awt.wl;
|
||||
|
||||
import java.awt.GraphicsDevice;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
import sun.java2d.SunGraphicsEnvironment;
|
||||
import sun.java2d.SurfaceManagerFactory;
|
||||
import sun.java2d.UnixSurfaceManagerFactory;
|
||||
import sun.util.logging.PlatformLogger;
|
||||
import sun.util.logging.PlatformLogger.Level;
|
||||
|
||||
public class WLGraphicsEnvironment extends SunGraphicsEnvironment {
|
||||
private static final PlatformLogger log = PlatformLogger.getLogger("sun.awt.wl.WLGraphicsEnvironment");
|
||||
|
||||
private static boolean vkwlAvailable;
|
||||
|
||||
static {
|
||||
SurfaceManagerFactory.setInstance(new UnixSurfaceManagerFactory());
|
||||
vkwlAvailable = initVKWL();
|
||||
if (log.isLoggable(Level.INFO)) {
|
||||
log.info("Vulkan rendering available: " + (vkwlAvailable?"YES":"NO"));
|
||||
}
|
||||
}
|
||||
|
||||
private static native boolean initVKWL();
|
||||
|
||||
private WLGraphicsEnvironment() {
|
||||
}
|
||||
|
||||
private static class Holder {
|
||||
static final WLGraphicsEnvironment INSTANCE = new WLGraphicsEnvironment();
|
||||
}
|
||||
|
||||
public static WLGraphicsEnvironment getSingleInstance() {
|
||||
return Holder.INSTANCE;
|
||||
}
|
||||
|
||||
public static boolean isVKWLAvailable() {
|
||||
return vkwlAvailable;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getNumScreens() {
|
||||
try {
|
||||
outputHasBeenConfigured.await();
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
synchronized (devices) {
|
||||
return devices.size();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected GraphicsDevice makeScreenDevice(int screenNum) {
|
||||
try {
|
||||
outputHasBeenConfigured.await();
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
synchronized (devices) {
|
||||
return devices.get(screenNum);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDisplayLocal() {
|
||||
return true;
|
||||
}
|
||||
|
||||
private final CountDownLatch outputHasBeenConfigured = new CountDownLatch(1);
|
||||
private final List<WLGraphicsDevice> devices = new ArrayList<>(5);
|
||||
|
||||
private void notifyOutputConfigured(String name, int wlID, int x, int y, int width, int height,
|
||||
int subpixel, int transform, int scale) {
|
||||
// Called from native code whenever a new output appears or an existing one changes its properties
|
||||
synchronized (devices) {
|
||||
boolean newOutput = true;
|
||||
for (final WLGraphicsDevice gd : devices) {
|
||||
if (gd.getWLID() == wlID) {
|
||||
gd.updateConfiguration(name, x, y, width, height, scale);
|
||||
newOutput = false;
|
||||
}
|
||||
}
|
||||
if (newOutput) {
|
||||
final WLGraphicsDevice gd = new WLGraphicsDevice(wlID);
|
||||
gd.updateConfiguration(name, x, y, width, height, scale);
|
||||
devices.add(gd);
|
||||
outputHasBeenConfigured.countDown();
|
||||
}
|
||||
}
|
||||
displayChanged();
|
||||
}
|
||||
|
||||
private void notifyOutputDestroyed(int wlID) {
|
||||
// Called from native code whenever one of the outputs is no longer available.
|
||||
// All surfaces that were partly visible on that output should have
|
||||
// notifySurfaceLeftOutput().
|
||||
|
||||
// NB: id may *not* be that of any output; if so, just ignore this event.
|
||||
synchronized (devices) {
|
||||
devices.removeIf(gd -> gd.getWLID() == wlID);
|
||||
}
|
||||
}
|
||||
|
||||
WLGraphicsDevice notifySurfaceEnteredOutput(WLComponentPeer wlComponentPeer, int wlOutputID) {
|
||||
synchronized (devices) {
|
||||
for (WLGraphicsDevice gd : devices) {
|
||||
if (gd.getWLID() == wlOutputID) {
|
||||
gd.addWindow(wlComponentPeer);
|
||||
return gd;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
WLGraphicsDevice notifySurfaceLeftOutput(WLComponentPeer wlComponentPeer, int wlOutputID) {
|
||||
synchronized (devices) {
|
||||
for (WLGraphicsDevice gd : devices) {
|
||||
if (gd.getWLID() == wlOutputID) {
|
||||
gd.removeWindow(wlComponentPeer);
|
||||
return gd;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
233
src/java.desktop/unix/classes/sun/awt/wl/WLInputState.java
Normal file
233
src/java.desktop/unix/classes/sun/awt/wl/WLInputState.java
Normal file
@@ -0,0 +1,233 @@
|
||||
/*
|
||||
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2022, JetBrains s.r.o.. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.awt.wl;
|
||||
|
||||
/**
|
||||
* MouseEvent objects cannot be created directly from WLPointerEvent because they require
|
||||
* 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 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,
|
||||
WLPointerEvent eventWithTimestamp,
|
||||
WLPointerEvent eventWithCoordinates,
|
||||
PointerButtonEvent pointerButtonPressedEvent,
|
||||
int modifiers,
|
||||
long surfaceForKeyboardInput) {
|
||||
/**
|
||||
* 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
|
||||
* @param linuxCode button code corresponding to WLPointerEvent.PointerButtonCodes.linuxCode
|
||||
*/
|
||||
record PointerButtonEvent(long surface, long serial, long timestamp, int clickCount, int linuxCode) {}
|
||||
|
||||
static WLInputState initialState() {
|
||||
return new WLInputState(null, null, null, null,
|
||||
null, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new state based on the existing one and the supplied WLPointerEvent.
|
||||
*/
|
||||
WLInputState update(WLPointerEvent pointerEvent) {
|
||||
final WLPointerEvent newEventWithSurface = pointerEvent.hasSurface()
|
||||
? pointerEvent : eventWithSurface;
|
||||
final WLPointerEvent newEventWithSerial = pointerEvent.hasSerial()
|
||||
? pointerEvent : eventWithSerial;
|
||||
final WLPointerEvent newEventWithTimestamp = pointerEvent.hasTimestamp()
|
||||
? pointerEvent : eventWithTimestamp;
|
||||
final WLPointerEvent newEventWithCoordinates = pointerEvent.hasCoordinates()
|
||||
? pointerEvent : eventWithCoordinates;
|
||||
final PointerButtonEvent newPointerButtonEvent = getNewPointerButtonEvent(pointerEvent,
|
||||
newEventWithSurface,
|
||||
newEventWithSerial,
|
||||
newEventWithTimestamp);
|
||||
final int newModifiers = getNewModifiers(pointerEvent);
|
||||
|
||||
return new WLInputState(
|
||||
newEventWithSurface,
|
||||
newEventWithSerial,
|
||||
newEventWithTimestamp,
|
||||
newEventWithCoordinates,
|
||||
newPointerButtonEvent,
|
||||
newModifiers,
|
||||
surfaceForKeyboardInput);
|
||||
}
|
||||
|
||||
public WLInputState updatedFromKeyboardEnterEvent(long serial, long surfacePtr) {
|
||||
// "The compositor must send the wl_keyboard.modifiers event after this event".
|
||||
return new WLInputState(
|
||||
eventWithSurface,
|
||||
eventWithSerial,
|
||||
eventWithTimestamp,
|
||||
eventWithCoordinates,
|
||||
pointerButtonPressedEvent,
|
||||
modifiers,
|
||||
surfacePtr);
|
||||
}
|
||||
|
||||
public WLInputState updatedFromKeyboardModifiersEvent(long serial, int keyboardModifiers) {
|
||||
// "The compositor must send the wl_keyboard.modifiers event after this event".
|
||||
final int oldPointerModifiers = modifiers & WLPointerEvent.PointerButtonCodes.combinedMask();
|
||||
final int newModifiers = oldPointerModifiers | keyboardModifiers;
|
||||
return new WLInputState(
|
||||
eventWithSurface,
|
||||
eventWithSerial,
|
||||
eventWithTimestamp,
|
||||
eventWithCoordinates,
|
||||
pointerButtonPressedEvent,
|
||||
newModifiers,
|
||||
surfaceForKeyboardInput);
|
||||
}
|
||||
|
||||
public WLInputState updatedFromKeyboardLeaveEvent(long serial, long surfacePtr) {
|
||||
// "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".
|
||||
final int newModifiers = modifiers & WLPointerEvent.PointerButtonCodes.combinedMask();
|
||||
return new WLInputState(
|
||||
eventWithSurface,
|
||||
eventWithSerial,
|
||||
eventWithTimestamp,
|
||||
eventWithCoordinates,
|
||||
pointerButtonPressedEvent,
|
||||
newModifiers,
|
||||
0);
|
||||
}
|
||||
|
||||
public WLInputState resetPointerState() {
|
||||
return new WLInputState(
|
||||
eventWithSurface,
|
||||
eventWithSerial,
|
||||
eventWithTimestamp,
|
||||
eventWithCoordinates,
|
||||
pointerButtonPressedEvent,
|
||||
0,
|
||||
surfaceForKeyboardInput);
|
||||
}
|
||||
|
||||
private PointerButtonEvent getNewPointerButtonEvent(WLPointerEvent pointerEvent,
|
||||
WLPointerEvent newEventWithSurface,
|
||||
WLPointerEvent newEventWithSerial,
|
||||
WLPointerEvent newEventWithTimestamp) {
|
||||
if (pointerEvent.hasButtonEvent() && pointerEvent.getIsButtonPressed() && newEventWithSurface != null) {
|
||||
assert newEventWithSerial != null && newEventWithTimestamp != null;
|
||||
int clickCount = 1;
|
||||
final boolean pressedSameButton = pointerButtonPressedEvent != null
|
||||
&& pointerEvent.getButtonCode() == pointerButtonPressedEvent.linuxCode;
|
||||
if (pressedSameButton) {
|
||||
final boolean clickedSameSurface
|
||||
= newEventWithSurface.getSurface() == pointerButtonPressedEvent.surface;
|
||||
final boolean clickedQuickly
|
||||
= (pointerEvent.getTimestamp() - pointerButtonPressedEvent.timestamp)
|
||||
<= WLToolkit.getMulticlickTime();
|
||||
if (clickedSameSurface && clickedQuickly) {
|
||||
clickCount = pointerButtonPressedEvent.clickCount + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return new PointerButtonEvent(
|
||||
newEventWithSurface.getSurface(),
|
||||
newEventWithSerial.getSerial(),
|
||||
newEventWithTimestamp.getTimestamp(),
|
||||
clickCount,
|
||||
pointerEvent.getButtonCode());
|
||||
}
|
||||
|
||||
return pointerButtonPressedEvent;
|
||||
}
|
||||
|
||||
private int getNewModifiers(WLPointerEvent pointerEvent) {
|
||||
int newModifiers = modifiers;
|
||||
|
||||
if (pointerEvent.hasLeaveEvent()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (pointerEvent.hasButtonEvent()) {
|
||||
final WLPointerEvent.PointerButtonCodes buttonCode
|
||||
= WLPointerEvent.PointerButtonCodes.recognizedOrNull(pointerEvent.getButtonCode());
|
||||
if (buttonCode != null) {
|
||||
if (pointerEvent.getIsButtonPressed()) {
|
||||
newModifiers |= buttonCode.javaMask;
|
||||
} else {
|
||||
newModifiers &= ~buttonCode.javaMask;
|
||||
}
|
||||
}
|
||||
}
|
||||
return newModifiers;
|
||||
}
|
||||
|
||||
public boolean hasThisPointerButtonPressed(int linuxCode) {
|
||||
return pointerButtonPressedEvent != null && pointerButtonPressedEvent.linuxCode == linuxCode;
|
||||
}
|
||||
|
||||
public boolean hasPointerButtonPressed() {
|
||||
return WLPointerEvent.PointerButtonCodes.anyMatchMask(modifiers);
|
||||
}
|
||||
|
||||
public long getSurfaceForKeyboardInput() {
|
||||
return surfaceForKeyboardInput;
|
||||
}
|
||||
|
||||
public int getPointerX() {
|
||||
return eventWithCoordinates != null ? eventWithCoordinates.getSurfaceX() : 0;
|
||||
}
|
||||
|
||||
public int getPointerY() {
|
||||
return eventWithCoordinates != null ? eventWithCoordinates.getSurfaceY() : 0;
|
||||
}
|
||||
|
||||
public WLComponentPeer getPeer() {
|
||||
return eventWithSurface != null
|
||||
? WLToolkit.componentPeerFromSurface(eventWithSurface.getSurface())
|
||||
: null;
|
||||
}
|
||||
|
||||
public long getTimestamp() {
|
||||
return eventWithTimestamp != null ? eventWithTimestamp.getTimestamp() : 0;
|
||||
}
|
||||
|
||||
public int getClickCount() {
|
||||
return pointerButtonPressedEvent != null ? pointerButtonPressedEvent.clickCount : 1;
|
||||
}
|
||||
|
||||
public int getModifiers() {
|
||||
return modifiers;
|
||||
}
|
||||
}
|
||||
1031
src/java.desktop/unix/classes/sun/awt/wl/WLKeySym.java
Normal file
1031
src/java.desktop/unix/classes/sun/awt/wl/WLKeySym.java
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,50 @@
|
||||
package sun.awt.wl;
|
||||
|
||||
import sun.awt.KeyboardFocusManagerPeerImpl;
|
||||
import sun.util.logging.PlatformLogger;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.Window;
|
||||
|
||||
public class WLKeyboardFocusManagerPeer extends KeyboardFocusManagerPeerImpl {
|
||||
private static final PlatformLogger focusLog = PlatformLogger.getLogger("sun.awt.wl.focus.WLKeyboardFocusManagerPeer");
|
||||
|
||||
private Window currentFocusedWindow;
|
||||
private static final WLKeyboardFocusManagerPeer instance = new WLKeyboardFocusManagerPeer();
|
||||
|
||||
public static WLKeyboardFocusManagerPeer getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCurrentFocusedWindow(Window win) {
|
||||
synchronized (this) {
|
||||
if (focusLog.isLoggable(PlatformLogger.Level.FINER)) {
|
||||
focusLog.finer("Current focused window -> " + win);
|
||||
}
|
||||
currentFocusedWindow = win;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Window getCurrentFocusedWindow() {
|
||||
synchronized (this) {
|
||||
return currentFocusedWindow;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCurrentFocusOwner(Component comp) {
|
||||
if (comp != currentFocusedWindow) {
|
||||
// In Wayland, only Window can be focused, not any widget in it.
|
||||
focusLog.severe("Unexpected focus owner set in a Window: " + comp);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getCurrentFocusOwner() {
|
||||
synchronized (this) {
|
||||
return currentFocusedWindow;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2022, JetBrains s.r.o.. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.awt.wl;
|
||||
|
||||
import java.awt.Graphics;
|
||||
import sun.awt.LightweightFrame;
|
||||
import sun.awt.OverrideNativeWindowHandle;
|
||||
import sun.swing.JLightweightFrame;
|
||||
import sun.swing.SwingAccessor;
|
||||
|
||||
public class WLLightweightFramePeer extends WLFramePeer implements OverrideNativeWindowHandle {
|
||||
|
||||
WLLightweightFramePeer(LightweightFrame target) {
|
||||
super(target);
|
||||
}
|
||||
|
||||
private LightweightFrame getLwTarget() {
|
||||
return (LightweightFrame)target;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Graphics getGraphics() {
|
||||
return getLwTarget().getGraphics();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void updateCursorImmediately() {
|
||||
SwingAccessor.getJLightweightFrameAccessor().updateCursor((JLightweightFrame)getLwTarget());
|
||||
}
|
||||
|
||||
private volatile long overriddenWindowHandle;
|
||||
|
||||
@Override
|
||||
public void overrideWindowHandle(final long handle) {
|
||||
overriddenWindowHandle = handle;
|
||||
}
|
||||
|
||||
public long getOverriddenWindowHandle() {
|
||||
return overriddenWindowHandle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void wlSetVisible(boolean visible) {
|
||||
this.visible = visible;
|
||||
}
|
||||
}
|
||||
268
src/java.desktop/unix/classes/sun/awt/wl/WLPointerEvent.java
Normal file
268
src/java.desktop/unix/classes/sun/awt/wl/WLPointerEvent.java
Normal file
@@ -0,0 +1,268 @@
|
||||
/*
|
||||
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2022, JetBrains s.r.o.. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.awt.wl;
|
||||
|
||||
import java.awt.event.InputEvent;
|
||||
import java.awt.event.MouseEvent;
|
||||
|
||||
/**
|
||||
* A wayland "pointer" (mouse/surface/trackpad) event received from the native code.
|
||||
* It accumulates one or more events that occurred since the last time and gets sent
|
||||
* when Wayland generates the "frame" event denoting some sort of finished state
|
||||
* of the pointer input.
|
||||
* Therefore, not all fields are valid at all times and the many 'hasXXX()' methods
|
||||
* have to be consulted prior to calling any of the 'getXXX()' ones.
|
||||
* For example, the event with only 'has_leave_event' will not have 'surface_x'
|
||||
* or 'timestamp' set.
|
||||
* The unset fields are initialized to their default values (0 or false).
|
||||
* Refer to wayland.xml for the full documentation.
|
||||
*/
|
||||
class WLPointerEvent {
|
||||
private boolean has_enter_event;
|
||||
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;
|
||||
private long timestamp;
|
||||
|
||||
private int surface_x;
|
||||
private int surface_y;
|
||||
|
||||
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 WLPointerEvent() {}
|
||||
|
||||
static WLPointerEvent newInstance() {
|
||||
// Invoked from the native code
|
||||
return new WLPointerEvent();
|
||||
}
|
||||
|
||||
public enum PointerButtonCodes {
|
||||
// see <linux/input-event-codes.h>
|
||||
LEFT(0x110, MouseEvent.BUTTON1, InputEvent.BUTTON1_DOWN_MASK),
|
||||
MIDDLE(0x112, MouseEvent.BUTTON2, InputEvent.BUTTON2_DOWN_MASK),
|
||||
RIGHT(0x111, MouseEvent.BUTTON3, InputEvent.BUTTON3_DOWN_MASK);
|
||||
|
||||
public final int linuxCode; // The code from <linux/input-event-codes.h>
|
||||
public final int javaCode; // The code from MouseEvents.BUTTONx
|
||||
public final int javaMask; // The mask from InputEvent.BUTTONx_DOWN_MASK
|
||||
|
||||
PointerButtonCodes(int linuxCode, int javaCode, int javaMask) {
|
||||
this.linuxCode = linuxCode;
|
||||
this.javaCode = javaCode;
|
||||
this.javaMask = javaMask;
|
||||
}
|
||||
|
||||
static PointerButtonCodes recognizedOrNull(int linuxCode) {
|
||||
for (var e : values()) {
|
||||
if (e.linuxCode == linuxCode) {
|
||||
return e;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static boolean anyMatchMask(int mask) {
|
||||
for (var c : values()) {
|
||||
if ((mask & c.javaMask) != 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int combinedMask() {
|
||||
int mask = 0;
|
||||
for (var c : values()) {
|
||||
mask |= c.javaMask;
|
||||
}
|
||||
return mask;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return switch (this) {
|
||||
case LEFT -> "left";
|
||||
case MIDDLE -> "middle";
|
||||
case RIGHT -> "right";
|
||||
};
|
||||
}
|
||||
|
||||
public boolean isPopupTrigger() {
|
||||
// TODO: this should probably be configurable for left- and right-handed?
|
||||
return this == RIGHT;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasEnterEvent() {
|
||||
return has_enter_event;
|
||||
}
|
||||
|
||||
public boolean hasLeaveEvent() {
|
||||
return has_leave_event;
|
||||
}
|
||||
|
||||
public boolean hasMotionEvent() {
|
||||
return has_motion_event;
|
||||
}
|
||||
|
||||
public boolean hasButtonEvent() {
|
||||
return has_button_event;
|
||||
}
|
||||
|
||||
public boolean hasAxisEvent() {
|
||||
return has_axis_event;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if this event's field 'surface' is valid.
|
||||
*/
|
||||
public boolean hasSurface() {
|
||||
return hasEnterEvent() || hasLeaveEvent();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if this event's field 'serial' is valid.
|
||||
*/
|
||||
public boolean hasSerial() {
|
||||
return hasEnterEvent() || hasLeaveEvent() || hasButtonEvent();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if this event's field 'timestamp' is valid.
|
||||
*/
|
||||
public boolean hasTimestamp() {
|
||||
return hasMotionEvent() || hasButtonEvent();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if this event's fields 'surface_x' and 'surface_y' are valid.
|
||||
*/
|
||||
public boolean hasCoordinates() {
|
||||
return hasMotionEvent() || hasEnterEvent();
|
||||
}
|
||||
|
||||
public long getSurface() {
|
||||
assert hasSurface();
|
||||
return surface;
|
||||
}
|
||||
|
||||
public long getSerial() {
|
||||
assert hasSerial();
|
||||
return serial;
|
||||
}
|
||||
|
||||
public long getTimestamp() {
|
||||
assert hasTimestamp();
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public int getSurfaceX() {
|
||||
assert hasCoordinates();
|
||||
return surface_x;
|
||||
}
|
||||
|
||||
public int getSurfaceY() {
|
||||
assert hasCoordinates();
|
||||
return surface_y;
|
||||
}
|
||||
|
||||
public int getButtonCode() {
|
||||
assert hasButtonEvent();
|
||||
return buttonCode;
|
||||
}
|
||||
|
||||
public boolean getIsButtonPressed() {
|
||||
assert hasButtonEvent();
|
||||
return isButtonPressed;
|
||||
}
|
||||
|
||||
public boolean getIsAxis0Valid() {
|
||||
assert hasAxisEvent();
|
||||
return axis_0_valid;
|
||||
}
|
||||
|
||||
public int getAxis0Value() {
|
||||
assert hasAxisEvent();
|
||||
return axis_0_value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
builder.append("WLPointerEvent(");
|
||||
if (hasSurface()) {
|
||||
builder.append("surface: 0x").append(Long.toHexString(getSurface()));
|
||||
}
|
||||
|
||||
if (hasSerial()) {
|
||||
builder.append(", serial: ").append(getSerial());
|
||||
}
|
||||
|
||||
if (hasTimestamp()) {
|
||||
builder.append(", timestamp: ").append(getTimestamp());
|
||||
}
|
||||
|
||||
if (hasEnterEvent()) builder.append(" enter");
|
||||
if (hasLeaveEvent()) builder.append(" leave");
|
||||
|
||||
if (hasMotionEvent()) {
|
||||
builder.append(" motion ");
|
||||
builder.append("x: ").append(getSurfaceX()).append(", y: ").append(getSurfaceY());
|
||||
|
||||
}
|
||||
if (hasButtonEvent()) {
|
||||
builder.append(" button ");
|
||||
|
||||
builder.append(buttonCodeToString(getButtonCode())).append(" ");
|
||||
builder.append(getIsButtonPressed() ? "pressed" : "released");
|
||||
}
|
||||
|
||||
if (hasAxisEvent()) {
|
||||
builder.append("axis");
|
||||
if (axis_0_valid) {
|
||||
builder.append("vertical-scroll: ").append(axis_0_value).append(" ");
|
||||
}
|
||||
}
|
||||
|
||||
builder.append(")");
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
private static String buttonCodeToString(int code) {
|
||||
final PointerButtonCodes recognizedCode = PointerButtonCodes.recognizedOrNull(code);
|
||||
return String.valueOf(recognizedCode);
|
||||
}
|
||||
}
|
||||
75
src/java.desktop/unix/classes/sun/awt/wl/WLRepaintArea.java
Normal file
75
src/java.desktop/unix/classes/sun/awt/wl/WLRepaintArea.java
Normal file
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2022, JetBrains s.r.o.. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
|
||||
package sun.awt.wl;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
import sun.awt.AWTAccessor;
|
||||
import sun.awt.RepaintArea;
|
||||
|
||||
/**
|
||||
* The {@code RepaintArea} is a geometric construct created for the
|
||||
* purpose of holding the geometry of several coalesced paint events.
|
||||
* This geometry is accessed synchronously, although it is written such
|
||||
* that painting may still be executed asynchronously.
|
||||
*
|
||||
* @author Eric Hawkes
|
||||
*/
|
||||
final class WLRepaintArea extends RepaintArea {
|
||||
|
||||
/**
|
||||
* Calls {@code Component.update(Graphics)} with given Graphics.
|
||||
*/
|
||||
protected void updateComponent(Component comp, Graphics g) {
|
||||
if (comp != null) {
|
||||
// We don't call peer.paintPeer() here, because we shouldn't paint
|
||||
// native component when processing UPDATE events.
|
||||
super.updateComponent(comp, g);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls {@code Component.paint(Graphics)} with given Graphics.
|
||||
*/
|
||||
protected void paintComponent(Component comp, Graphics g) {
|
||||
if (comp != null) {
|
||||
final WLComponentPeer peer = AWTAccessor.getComponentAccessor()
|
||||
.getPeer(comp);
|
||||
try {
|
||||
if (peer != null) {
|
||||
peer.paintPeer(g);
|
||||
}
|
||||
super.paintComponent(comp, g);
|
||||
} finally {
|
||||
if (comp instanceof Window window) {
|
||||
AWTAccessor.getWindowAccessor().updateWindow(window);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
142
src/java.desktop/unix/classes/sun/awt/wl/WLRobotPeer.java
Normal file
142
src/java.desktop/unix/classes/sun/awt/wl/WLRobotPeer.java
Normal file
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2022, JetBrains s.r.o.. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.awt.wl;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.peer.RobotPeer;
|
||||
|
||||
public class WLRobotPeer implements RobotPeer {
|
||||
private final WLGraphicsConfig wgc;
|
||||
|
||||
static {
|
||||
initIDs();
|
||||
}
|
||||
|
||||
public WLRobotPeer(WLGraphicsDevice gd) {
|
||||
wgc = (WLGraphicsConfig) gd.getDefaultConfiguration();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseMove(int x, int y) {
|
||||
throw new UnsupportedOperationException("Not implemented: WLRobotPeer.mouseMove()");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mousePress(int buttons) {
|
||||
throw new UnsupportedOperationException("Not implemented: WLRobotPeer.mousePress()");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseRelease(int buttons) {
|
||||
throw new UnsupportedOperationException("Not implemented: WLRobotPeer.mouseRelease()");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseWheel(int wheelAmt) {
|
||||
throw new UnsupportedOperationException("Not implemented: WLRobotPeer.mouseWheel()");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyPress(int keycode) {
|
||||
throw new UnsupportedOperationException("Not implemented: WLRobotPeer.keyPress()");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyRelease(int keycode) {
|
||||
throw new UnsupportedOperationException("Not implemented: WLRobotPeer.keyRelease()");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRGBPixel(int x, int y) {
|
||||
checkExtensionPresent();
|
||||
|
||||
// The native implementation allows for just one such request at a time
|
||||
synchronized(WLRobotPeer.class) {
|
||||
return getRGBPixelImpl(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int [] getRGBPixels(Rectangle bounds) {
|
||||
checkExtensionPresent();
|
||||
|
||||
// The native implementation allows for just one such request at a time
|
||||
synchronized(WLRobotPeer.class) {
|
||||
return getRGBPixelsImpl(bounds.x, bounds.y, bounds.width, bounds.height);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the location in absolute coordinates of the Wayland
|
||||
* surface pointed to by the argument.
|
||||
* Throws UnsupportedOperationException if the Wayland extension
|
||||
* that implements this wasn't loaded on the server side.
|
||||
*
|
||||
* @param wlSurfacePtr a pointer to struct wl_surface
|
||||
* @return location of the surface in absolute coordinates
|
||||
*/
|
||||
static Point getLocationOfWLSurface(long wlSurfacePtr) {
|
||||
checkExtensionPresent();
|
||||
|
||||
// The native implementation allows for just one such request at a time
|
||||
synchronized(WLRobotPeer.class) {
|
||||
return getLocationOfWLSurfaceImpl(wlSurfacePtr);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the screen location of the given Wayland surface to the given
|
||||
* absolute coordinates.
|
||||
* Does nothing if the Wayland extension that supports this functionality
|
||||
* was not loaded by the server.
|
||||
*
|
||||
* @param wlSurfacePtr a pointer to struct wl_surface
|
||||
* @param x the absolute x-coordinate
|
||||
* @param y the absolute y-coordinate
|
||||
*/
|
||||
static void setLocationOfWLSurface(long wlSurfacePtr, int x, int y) {
|
||||
if (isRobotExtensionPresent) {
|
||||
setLocationOfWLSurfaceImpl(wlSurfacePtr, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkExtensionPresent() {
|
||||
if (!isRobotExtensionPresent) {
|
||||
throw new UnsupportedOperationException("WLRobotPeer: wakefield extension not present in Wayland instance");
|
||||
}
|
||||
}
|
||||
|
||||
private static final boolean isRobotExtensionPresent = isRobotExtensionPresentImpl();
|
||||
|
||||
private static native void initIDs();
|
||||
|
||||
private static native boolean isRobotExtensionPresentImpl();
|
||||
private static native int getRGBPixelImpl(int x, int y);
|
||||
private static native int[] getRGBPixelsImpl(int x, int y, int width, int height);
|
||||
private static native Point getLocationOfWLSurfaceImpl(long wlSurfacePtr);
|
||||
private static native void setLocationOfWLSurfaceImpl(long wlSurfacePtr, int x, int y);
|
||||
}
|
||||
994
src/java.desktop/unix/classes/sun/awt/wl/WLToolkit.java
Normal file
994
src/java.desktop/unix/classes/sun/awt/wl/WLToolkit.java
Normal file
@@ -0,0 +1,994 @@
|
||||
/*
|
||||
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2022, JetBrains s.r.o.. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.awt.wl;
|
||||
|
||||
import sun.awt.AWTAccessor;
|
||||
import sun.awt.AppContext;
|
||||
import sun.awt.LightweightFrame;
|
||||
import sun.awt.PeerEvent;
|
||||
import sun.awt.SunToolkit;
|
||||
import sun.awt.UNIXToolkit;
|
||||
import sun.awt.datatransfer.DataTransferer;
|
||||
import sun.util.logging.PlatformLogger;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.datatransfer.Clipboard;
|
||||
import java.awt.dnd.DragGestureEvent;
|
||||
import java.awt.dnd.DragGestureListener;
|
||||
import java.awt.dnd.DragGestureRecognizer;
|
||||
import java.awt.dnd.DragSource;
|
||||
import java.awt.dnd.InvalidDnDOperationException;
|
||||
import java.awt.dnd.peer.DragSourceContextPeer;
|
||||
import java.awt.event.InputEvent;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.WindowEvent;
|
||||
import java.awt.font.TextAttribute;
|
||||
import java.awt.im.InputMethodHighlight;
|
||||
import java.awt.im.spi.InputMethodDescriptor;
|
||||
import java.awt.peer.ButtonPeer;
|
||||
import java.awt.peer.CanvasPeer;
|
||||
import java.awt.peer.CheckboxMenuItemPeer;
|
||||
import java.awt.peer.CheckboxPeer;
|
||||
import java.awt.peer.ChoicePeer;
|
||||
import java.awt.peer.DesktopPeer;
|
||||
import java.awt.peer.DialogPeer;
|
||||
import java.awt.peer.FileDialogPeer;
|
||||
import java.awt.peer.FontPeer;
|
||||
import java.awt.peer.FramePeer;
|
||||
import java.awt.peer.KeyboardFocusManagerPeer;
|
||||
import java.awt.peer.LabelPeer;
|
||||
import java.awt.peer.ListPeer;
|
||||
import java.awt.peer.MenuBarPeer;
|
||||
import java.awt.peer.MenuItemPeer;
|
||||
import java.awt.peer.MenuPeer;
|
||||
import java.awt.peer.MouseInfoPeer;
|
||||
import java.awt.peer.PanelPeer;
|
||||
import java.awt.peer.PopupMenuPeer;
|
||||
import java.awt.peer.RobotPeer;
|
||||
import java.awt.peer.ScrollPanePeer;
|
||||
import java.awt.peer.ScrollbarPeer;
|
||||
import java.awt.peer.SystemTrayPeer;
|
||||
import java.awt.peer.TaskbarPeer;
|
||||
import java.awt.peer.TextAreaPeer;
|
||||
import java.awt.peer.TextFieldPeer;
|
||||
import java.awt.peer.TrayIconPeer;
|
||||
import java.awt.peer.WindowPeer;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.concurrent.Semaphore;
|
||||
|
||||
/**
|
||||
* On events handling: the WLToolkit class creates a thread named "AWT-Wayland"
|
||||
* where the communication with the Wayland server is chiefly carried on such
|
||||
* as sending requests over and receiving events from the server.
|
||||
* For "system" events that are not meant to trigger any user code, a separate
|
||||
* thread is utilized called "AWT-Wayland-system-dispatcher". It is the only
|
||||
* thread where such events are handled. For other events, such as mouse click
|
||||
* events, the Wayland handlers are supposed to "transfer" themselves to
|
||||
* "AWT-EventThread" by means of SunToolkit.postEvent(). See the implementation
|
||||
* of run() method for more comments.
|
||||
*/
|
||||
public class WLToolkit extends UNIXToolkit implements Runnable {
|
||||
private static final PlatformLogger log = PlatformLogger.getLogger("sun.awt.wl.WLToolkit");
|
||||
private static final PlatformLogger logKeys = PlatformLogger.getLogger("sun.awt.wl.WLToolkit.keys");
|
||||
|
||||
/**
|
||||
* Maximum wait time in ms between attempts to receive more data (events)
|
||||
* from the Wayland server on the toolkit thread.
|
||||
*/
|
||||
private static final int WAYLAND_DISPLAY_INTERACTION_TIMEOUT_MS = 50;
|
||||
|
||||
/**
|
||||
* Returned by readEvents() to signify the presence of events on the default
|
||||
* Wayland display queue that have not been dispatched yet.
|
||||
*/
|
||||
private static final int READ_RESULT_FINISHED_WITH_EVENTS = 0;
|
||||
|
||||
/**
|
||||
* Returned by readEvents() to signify the absence of events on the default
|
||||
* Wayland display queue.
|
||||
*/
|
||||
private static final int READ_RESULT_FINISHED_NO_EVENTS = 1;
|
||||
|
||||
/**
|
||||
* Returned by readEvents() in case of an error condition like
|
||||
* disappearing of the Wayland display. Errors not specifically
|
||||
* related to Wayland are reported via an exception.
|
||||
*/
|
||||
private static final int READ_RESULT_ERROR = 2;
|
||||
|
||||
private static final int MOUSE_BUTTONS_COUNT = 3;
|
||||
private static final int AWT_MULTICLICK_DEFAULT_TIME_MS = 500;
|
||||
|
||||
private static native void initIDs();
|
||||
|
||||
static {
|
||||
if (!GraphicsEnvironment.isHeadless()) {
|
||||
initIDs();
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("removal")
|
||||
public WLToolkit() {
|
||||
AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
|
||||
final String extraButtons = "sun.awt.enableExtraMouseButtons";
|
||||
areExtraMouseButtonsEnabled =
|
||||
Boolean.parseBoolean(System.getProperty(extraButtons, "true"));
|
||||
System.setProperty(extraButtons, String.valueOf(areExtraMouseButtonsEnabled));
|
||||
return null;
|
||||
});
|
||||
|
||||
Thread toolkitThread = new Thread(this, "AWT-Wayland");
|
||||
toolkitThread.setDaemon(true);
|
||||
toolkitThread.start();
|
||||
|
||||
final Thread toolkitSystemThread = new Thread(this::dispatchNonDefaultQueues, "AWT-Wayland-system-dispatcher");
|
||||
toolkitSystemThread.setDaemon(true);
|
||||
toolkitSystemThread.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ButtonPeer createButton(Button target) {
|
||||
ButtonPeer peer = new WLButtonPeer(target);
|
||||
targetCreatedPeer(target, peer);
|
||||
return peer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FramePeer createLightweightFrame(LightweightFrame target) {
|
||||
FramePeer peer = new WLLightweightFramePeer(target);
|
||||
targetCreatedPeer(target, peer);
|
||||
return peer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FramePeer createFrame(Frame target) {
|
||||
FramePeer peer = new WLFramePeer(target);
|
||||
targetCreatedPeer(target, peer);
|
||||
return peer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wayland events coming to queues other that the default are handled here.
|
||||
* The code is executed on a separate thread and must not call any user code.
|
||||
*/
|
||||
private void dispatchNonDefaultQueues() {
|
||||
dispatchNonDefaultQueuesImpl(); // does not return until error or server disconnect
|
||||
}
|
||||
|
||||
private final Semaphore eventsQueued = new Semaphore(0);
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while(true) {
|
||||
int result = readEvents();
|
||||
if (result == READ_RESULT_ERROR) {
|
||||
log.severe("Wayland protocol I/O error");
|
||||
// TODO: display disconnect handling here?
|
||||
break;
|
||||
} else if (result == READ_RESULT_FINISHED_WITH_EVENTS) {
|
||||
SunToolkit.postEvent(AppContext.getAppContext(), new PeerEvent(this, () -> {
|
||||
WLToolkit.awtLock();
|
||||
try {
|
||||
dispatchEventsOnEDT();
|
||||
} finally {
|
||||
eventsQueued.release();
|
||||
WLToolkit.awtUnlock();
|
||||
}
|
||||
}, PeerEvent.ULTIMATE_PRIORITY_EVENT));
|
||||
try {
|
||||
eventsQueued.acquire();
|
||||
} catch (InterruptedException e) {
|
||||
log.severe("Wayland protocol I/O thread interrupted");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If more than this amount milliseconds has passed since the same mouse button click,
|
||||
* the next click is considered separate and not part of multi-click event.
|
||||
* @return maximum milliseconds between same mouse button clicks for them to be a multiclick
|
||||
*/
|
||||
static long getMulticlickTime() {
|
||||
/* TODO: get from the system somehow */
|
||||
return AWT_MULTICLICK_DEFAULT_TIME_MS;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The rate of repeating keys in characters per second
|
||||
* Set from the native code by the 'repeat_info' Wayland event (see wayland.xml).
|
||||
*/
|
||||
static volatile int keyRepeatRate = 33;
|
||||
|
||||
/**
|
||||
* Delay in milliseconds since key down until repeating starts.
|
||||
* Set from the native code by the 'repeat_info' Wayland event (see wayland.xml).
|
||||
*/
|
||||
static volatile int keyRepeatDelay = 500;
|
||||
|
||||
static int getKeyRepeatRate() {
|
||||
return keyRepeatRate;
|
||||
}
|
||||
|
||||
static int getKeyRepeatDelay() {
|
||||
return keyRepeatDelay;
|
||||
}
|
||||
|
||||
private static class KeyRepeatManager {
|
||||
private Timer keyRepeatTimer;
|
||||
private PostKeyEventTask postKeyEventTask;
|
||||
|
||||
KeyRepeatManager() {
|
||||
}
|
||||
|
||||
private void stopKeyRepeat() {
|
||||
assert EventQueue.isDispatchThread();
|
||||
|
||||
if (postKeyEventTask != null) {
|
||||
postKeyEventTask.cancel();
|
||||
postKeyEventTask = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void initiateDelayedKeyRepeat(long keycode,
|
||||
int keyCodePoint,
|
||||
WLComponentPeer peer) {
|
||||
assert EventQueue.isDispatchThread();
|
||||
assert postKeyEventTask == null;
|
||||
|
||||
if (keyRepeatTimer == null) {
|
||||
// The following starts a dedicated daemon thread.
|
||||
keyRepeatTimer = new Timer("WLToolkit Key Repeat", true);
|
||||
}
|
||||
|
||||
postKeyEventTask = new PostKeyEventTask(keycode, keyCodePoint, peer);
|
||||
|
||||
assert WLToolkit.keyRepeatRate > 0;
|
||||
assert WLToolkit.keyRepeatDelay > 0;
|
||||
|
||||
keyRepeatTimer.schedule(
|
||||
postKeyEventTask,
|
||||
WLToolkit.keyRepeatDelay,
|
||||
(long)(1000.0 / WLToolkit.keyRepeatRate));
|
||||
}
|
||||
|
||||
static boolean xkbCodeRequiresRepeat(long code) {
|
||||
return !WLKeySym.xkbCodeIsModifier(code);
|
||||
}
|
||||
|
||||
void keyboardEvent(long keycode, int keyCodePoint,
|
||||
boolean isPressed, WLComponentPeer peer) {
|
||||
stopKeyRepeat();
|
||||
if (isPressed && KeyRepeatManager.xkbCodeRequiresRepeat(keycode)) {
|
||||
initiateDelayedKeyRepeat(keycode, keyCodePoint, peer);
|
||||
}
|
||||
}
|
||||
|
||||
void windowEvent(WindowEvent event) {
|
||||
if (event.getID() == WindowEvent.WINDOW_LOST_FOCUS) {
|
||||
stopKeyRepeat();
|
||||
}
|
||||
}
|
||||
|
||||
private static class PostKeyEventTask extends TimerTask {
|
||||
final long keycode;
|
||||
final int keyCodePoint;
|
||||
final WLComponentPeer peer;
|
||||
|
||||
PostKeyEventTask(long keycode,
|
||||
int keyCodePoint,
|
||||
WLComponentPeer peer) {
|
||||
this.keycode = keycode;
|
||||
this.keyCodePoint = keyCodePoint;
|
||||
this.peer = peer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
final long timestamp = System.currentTimeMillis();
|
||||
try {
|
||||
EventQueue.invokeAndWait(() -> {
|
||||
generateKeyEventFrom(timestamp, keycode, keyCodePoint, true, peer);
|
||||
});
|
||||
} catch (InterruptedException ignored) {
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final KeyRepeatManager keyRepeatManager = new KeyRepeatManager();
|
||||
|
||||
private static WLInputState inputState = WLInputState.initialState();
|
||||
|
||||
private static void dispatchPointerEvent(WLPointerEvent e) {
|
||||
// Invoked from the native code
|
||||
assert EventQueue.isDispatchThread();
|
||||
|
||||
if (log.isLoggable(PlatformLogger.Level.FINE)) log.fine("dispatchPointerEvent: " + e);
|
||||
|
||||
final WLInputState oldInputState = inputState;
|
||||
final WLInputState newInputState = oldInputState.update(e);
|
||||
inputState = newInputState;
|
||||
final WLComponentPeer peer = newInputState.getPeer();
|
||||
if (peer == null) {
|
||||
// we don't know whom to notify of the event
|
||||
log.severe("Surface doesn't map to any component");
|
||||
} else {
|
||||
peer.dispatchPointerEventInContext(e, oldInputState, newInputState);
|
||||
}
|
||||
}
|
||||
|
||||
private static void dispatchKeyboardKeyEvent(long serial,
|
||||
long timestamp,
|
||||
long keycode,
|
||||
int keyCodePoint, // UTF32 character
|
||||
boolean isPressed) {
|
||||
// Invoked from the native code
|
||||
assert EventQueue.isDispatchThread();
|
||||
|
||||
if (logKeys.isLoggable(PlatformLogger.Level.FINE)) {
|
||||
logKeys.fine("dispatchKeyboardKeyEvent: keycode " + keycode + ", code point 0x"
|
||||
+ Integer.toHexString(keyCodePoint) + ", " + (isPressed ? "pressed" : "released")
|
||||
+ ", serial " + serial + ", timestamp " + timestamp);
|
||||
}
|
||||
|
||||
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 WLComponentPeer peer = componentPeerFromSurface(surfacePtr);
|
||||
if (peer != null) {
|
||||
generateKeyEventFrom(timestamp, keycode, keyCodePoint, isPressed, peer);
|
||||
keyRepeatManager.keyboardEvent(keycode, keyCodePoint, isPressed, peer);
|
||||
}
|
||||
}
|
||||
|
||||
private static void generateKeyEventFrom(long timestamp, long keycode, int keyCodePoint,
|
||||
boolean isPressed, WLComponentPeer peer) {
|
||||
// See also XWindow.handleKeyPress()
|
||||
final char keyChar = Character.isBmpCodePoint(keyCodePoint)
|
||||
? (char) keyCodePoint
|
||||
: KeyEvent.CHAR_UNDEFINED;
|
||||
final WLKeySym.KeyDescriptor keyDescriptor = WLKeySym.KeyDescriptor.fromXKBCode(keycode);
|
||||
final int jkeyExtended = keyDescriptor.javaKeyCode() == KeyEvent.VK_UNDEFINED
|
||||
? primaryUnicodeToJavaKeycode(keyCodePoint)
|
||||
: keyDescriptor.javaKeyCode();
|
||||
postKeyEvent(peer.getTarget(),
|
||||
isPressed ? KeyEvent.KEY_PRESSED : KeyEvent.KEY_RELEASED,
|
||||
timestamp,
|
||||
keyDescriptor.javaKeyCode(),
|
||||
keyChar,
|
||||
keyDescriptor.keyLocation(),
|
||||
keycode,
|
||||
jkeyExtended);
|
||||
|
||||
if (isPressed && keyChar != 0 && keyChar != KeyEvent.CHAR_UNDEFINED) {
|
||||
postKeyEvent(peer.getTarget(),
|
||||
KeyEvent.KEY_TYPED,
|
||||
timestamp,
|
||||
KeyEvent.VK_UNDEFINED,
|
||||
keyChar,
|
||||
KeyEvent.KEY_LOCATION_UNKNOWN,
|
||||
keycode,
|
||||
KeyEvent.VK_UNDEFINED);
|
||||
}
|
||||
}
|
||||
|
||||
private static int primaryUnicodeToJavaKeycode(int codePoint) {
|
||||
return (codePoint > 0? sun.awt.ExtendedKeyCodes.getExtendedKeyCodeForChar(codePoint) : 0);
|
||||
}
|
||||
|
||||
private static void postKeyEvent(Component source,
|
||||
int id,
|
||||
long timestamp,
|
||||
int keyCode,
|
||||
char keyChar,
|
||||
int keyLocation,
|
||||
long rawCode,
|
||||
int extendedKeyCode) {
|
||||
final KeyEvent keyEvent = new KeyEvent(source, id, timestamp,
|
||||
inputState.getModifiers(), keyCode, keyChar, keyLocation);
|
||||
|
||||
AWTAccessor.KeyEventAccessor kea = AWTAccessor.getKeyEventAccessor();
|
||||
kea.setRawCode(keyEvent, rawCode);
|
||||
kea.setExtendedKeyCode(keyEvent, (long) extendedKeyCode);
|
||||
if (logKeys.isLoggable(PlatformLogger.Level.FINE)) {
|
||||
logKeys.fine(String.valueOf(keyEvent));
|
||||
}
|
||||
postEvent(keyEvent);
|
||||
}
|
||||
|
||||
private static void dispatchKeyboardModifiersEvent(long serial,
|
||||
boolean isShiftActive,
|
||||
boolean isAltActive,
|
||||
boolean isCtrlActive,
|
||||
boolean isMetaActive) {
|
||||
// Invoked from the native code
|
||||
assert EventQueue.isDispatchThread();
|
||||
|
||||
final int newModifiers =
|
||||
(isShiftActive ? InputEvent.SHIFT_DOWN_MASK : 0)
|
||||
| (isAltActive ? InputEvent.ALT_DOWN_MASK : 0)
|
||||
| (isCtrlActive ? InputEvent.CTRL_DOWN_MASK : 0)
|
||||
| (isMetaActive ? InputEvent.META_DOWN_MASK : 0);
|
||||
if (logKeys.isLoggable(PlatformLogger.Level.FINE)) {
|
||||
logKeys.fine("dispatchKeyboardModifiersEvent: new modifiers 0x"
|
||||
+ Integer.toHexString(newModifiers));
|
||||
}
|
||||
|
||||
inputState = inputState.updatedFromKeyboardModifiersEvent(serial, newModifiers);
|
||||
}
|
||||
|
||||
private static void dispatchKeyboardEnterEvent(long serial, long surfacePtr) {
|
||||
// Invoked from the native code
|
||||
assert EventQueue.isDispatchThread();
|
||||
|
||||
if (logKeys.isLoggable(PlatformLogger.Level.FINE)) {
|
||||
logKeys.fine("dispatchKeyboardEnterEvent: " + serial + ", surface 0x"
|
||||
+ Long.toHexString(surfacePtr));
|
||||
}
|
||||
|
||||
final WLInputState newInputState = inputState.updatedFromKeyboardEnterEvent(serial, surfacePtr);
|
||||
final WLComponentPeer peer = componentPeerFromSurface(surfacePtr);
|
||||
if (peer != null && peer.getTarget() instanceof Window window) {
|
||||
WLKeyboardFocusManagerPeer.getInstance().setCurrentFocusedWindow(window);
|
||||
final WindowEvent windowEnterEvent = new WindowEvent(window, WindowEvent.WINDOW_GAINED_FOCUS);
|
||||
postEvent(windowEnterEvent);
|
||||
}
|
||||
inputState = newInputState;
|
||||
}
|
||||
|
||||
private static void dispatchKeyboardLeaveEvent(long serial, long surfacePtr) {
|
||||
// Invoked from the native code
|
||||
assert EventQueue.isDispatchThread();
|
||||
|
||||
if (logKeys.isLoggable(PlatformLogger.Level.FINE)) {
|
||||
logKeys.fine("dispatchKeyboardLeaveEvent: " + serial + ", surface 0x"
|
||||
+ Long.toHexString(surfacePtr));
|
||||
}
|
||||
|
||||
final WLInputState newInputState = inputState.updatedFromKeyboardLeaveEvent(serial, surfacePtr);
|
||||
final WLComponentPeer peer = componentPeerFromSurface(surfacePtr);
|
||||
if (peer != null && peer.getTarget() instanceof Window window) {
|
||||
final WindowEvent winLostFocusEvent = new WindowEvent(window, WindowEvent.WINDOW_LOST_FOCUS);
|
||||
WLKeyboardFocusManagerPeer.getInstance().setCurrentFocusedWindow(null);
|
||||
WLKeyboardFocusManagerPeer.getInstance().setCurrentFocusOwner(null);
|
||||
keyRepeatManager.windowEvent(winLostFocusEvent);
|
||||
postEvent(winLostFocusEvent);
|
||||
}
|
||||
inputState = newInputState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps 'struct wl_surface*' to WLComponentPeer that owns the Wayland surface.
|
||||
*/
|
||||
private static final Map<Long, WLComponentPeer> wlSurfaceToComponentMap = Collections.synchronizedMap(new HashMap<>());
|
||||
|
||||
static void registerWLSurface(long wlSurfacePtr, WLComponentPeer componentPeer) {
|
||||
if (log.isLoggable(PlatformLogger.Level.FINE)) {
|
||||
log.fine("registerWLSurface: 0x" + Long.toHexString(wlSurfacePtr) + "->" + componentPeer);
|
||||
}
|
||||
wlSurfaceToComponentMap.put(wlSurfacePtr, componentPeer);
|
||||
}
|
||||
|
||||
static void unregisterWLSurface(long wlSurfacePtr) {
|
||||
wlSurfaceToComponentMap.remove(wlSurfacePtr);
|
||||
}
|
||||
|
||||
static WLComponentPeer componentPeerFromSurface(long wlSurfacePtr) {
|
||||
return wlSurfaceToComponentMap.get(wlSurfacePtr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RobotPeer createRobot(GraphicsDevice screen) throws AWTException {
|
||||
if (screen instanceof WLGraphicsDevice) {
|
||||
return new WLRobotPeer((WLGraphicsDevice) screen);
|
||||
}
|
||||
return super.createRobot(screen);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDynamicLayout(boolean b) {
|
||||
log.info("Not implemented: WLToolkit.setDynamicLayout()");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isDynamicLayoutSet() {
|
||||
log.info("Not implemented: WLToolkit.isDynamicLayoutSet()");
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean isDynamicLayoutSupported() {
|
||||
log.info("Not implemented: WLToolkit.isDynamicLayoutSupported()");
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDynamicLayoutActive() {
|
||||
return isDynamicLayoutSupported();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FontPeer getFontPeer(String name, int style){
|
||||
log.info("Not implemented: WLToolkit.getFontPeer()");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DragSourceContextPeer createDragSourceContextPeer(DragGestureEvent dge) throws InvalidDnDOperationException {
|
||||
log.info("Not implemented: WLToolkit.createDragSourceContextPeer()");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends DragGestureRecognizer> T
|
||||
createDragGestureRecognizer(Class<T> recognizerClass,
|
||||
DragSource ds,
|
||||
Component c,
|
||||
int srcActions,
|
||||
DragGestureListener dgl)
|
||||
{
|
||||
log.info("Not implemented: WLToolkit.createDragGestureRecognizer()");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CheckboxMenuItemPeer createCheckboxMenuItem(CheckboxMenuItem target) {
|
||||
log.info("Not implemented: WLToolkit.createCheckboxMenuItem()");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuItemPeer createMenuItem(MenuItem target) {
|
||||
log.info("Not implemented: WLToolkit.createMenuItem()");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextFieldPeer createTextField(TextField target) {
|
||||
log.info("Not implemented: WLToolkit.createTextField()");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LabelPeer createLabel(Label target) {
|
||||
log.info("Not implemented: WLToolkit.createLabel()");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListPeer createList(java.awt.List target) {
|
||||
log.info("Not implemented: WLToolkit.createList()");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CheckboxPeer createCheckbox(Checkbox target) {
|
||||
log.info("Not implemented: WLToolkit.createCheckbox()");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScrollbarPeer createScrollbar(Scrollbar target) {
|
||||
log.info("Not implemented: WLToolkit.createScrollbar()");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScrollPanePeer createScrollPane(ScrollPane target) {
|
||||
log.info("Not implemented: WLToolkit.createScrollPane()");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextAreaPeer createTextArea(TextArea target) {
|
||||
log.info("Not implemented: WLToolkit.createTextArea()");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChoicePeer createChoice(Choice target) {
|
||||
log.info("Not implemented: WLToolkit.createChoice()");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CanvasPeer createCanvas(Canvas target) {
|
||||
WLCanvasPeer peer = new WLCanvasPeer(target);
|
||||
targetCreatedPeer(target, peer);
|
||||
return peer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PanelPeer createPanel(Panel target) {
|
||||
log.info("Not implemented: WLToolkit.createPanel()");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WindowPeer createWindow(Window target) {
|
||||
WLWindowPeer peer = new WLWindowPeer(target);
|
||||
targetCreatedPeer(target, peer);
|
||||
return peer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DialogPeer createDialog(Dialog target) {
|
||||
WLDialogPeer peer = new WLDialogPeer(target);
|
||||
targetCreatedPeer(target, peer);
|
||||
return peer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileDialogPeer createFileDialog(FileDialog target) {
|
||||
log.info("Not implemented: WLToolkit.createFileDialog()");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuBarPeer createMenuBar(MenuBar target) {
|
||||
log.info("Not implemented: WLToolkit.createMenuBar()");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MenuPeer createMenu(Menu target) {
|
||||
log.info("Not implemented: WLToolkit.createMenu()");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PopupMenuPeer createPopupMenu(PopupMenu target) {
|
||||
log.info("Not implemented: WLToolkit.createPopupMenu()");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized MouseInfoPeer getMouseInfoPeer() {
|
||||
log.info("Not implemented: WLToolkit.getMouseInfoPeer()");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyboardFocusManagerPeer getKeyboardFocusManagerPeer() throws HeadlessException {
|
||||
return WLKeyboardFocusManagerPeer.getInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new custom cursor.
|
||||
*/
|
||||
@Override
|
||||
public Cursor createCustomCursor(Image cursor, Point hotSpot, String name)
|
||||
throws IndexOutOfBoundsException {
|
||||
return new WLCustomCursor(cursor, hotSpot, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TrayIconPeer createTrayIcon(TrayIcon target)
|
||||
throws HeadlessException
|
||||
{
|
||||
log.info("Not implemented: WLToolkit.createTrayIcon()");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SystemTrayPeer createSystemTray(SystemTray target) throws HeadlessException {
|
||||
log.info("Not implemented: WLToolkit.createSystemTray()");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTraySupported() {
|
||||
log.info("Not implemented: WLToolkit.isTraySupported()");
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DataTransferer getDataTransferer() {
|
||||
log.info("Not implemented: WLToolkit.getDataTransferer()");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension getBestCursorSize(int preferredWidth, int preferredHeight) {
|
||||
// we don't restrict the maximum size
|
||||
return new Dimension(Math.max(1, preferredWidth), Math.max(1, preferredHeight));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaximumCursorColors() {
|
||||
return 16777216; // 24 bits per pixel, 8 bits per channel
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<TextAttribute, ?> mapInputMethodHighlight( InputMethodHighlight highlight) {
|
||||
log.info("Not implemented: WLToolkit.mapInputMethodHighlight()");
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
public boolean getLockingKeyState(int key) {
|
||||
log.info("Not implemented: WLToolkit.getLockingKeyState()");
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Clipboard getSystemClipboard() {
|
||||
log.info("Not implemented: WLToolkit.getSystemClipboard()");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Clipboard getSystemSelection() {
|
||||
log.info("Not implemented: WLToolkit.getSystemSelection()");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beep() {
|
||||
log.info("Not implemented: WLToolkit.beep()");
|
||||
}
|
||||
|
||||
@Override
|
||||
public PrintJob getPrintJob(final Frame frame, final String doctitle,
|
||||
final Properties props) {
|
||||
log.info("Not implemented: WLToolkit.getPrintJob()");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PrintJob getPrintJob(final Frame frame, final String doctitle,
|
||||
final JobAttributes jobAttributes,
|
||||
final PageAttributes pageAttributes)
|
||||
{
|
||||
log.info("Not implemented: WLToolkit.getPrintJob()");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getScreenResolution() {
|
||||
log.info("Not implemented: WLToolkit.getScreenResolution()");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new input method adapter descriptor for native input methods.
|
||||
*/
|
||||
@Override
|
||||
public InputMethodDescriptor getInputMethodAdapterDescriptor() {
|
||||
log.info("Not implemented: WLToolkit.getInputMethodAdapterDescriptor()");
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether enableInputMethods should be set to true for peered
|
||||
* TextComponent instances on this platform. True by default.
|
||||
*/
|
||||
@Override
|
||||
public boolean enableInputMethodsForTextComponent() {
|
||||
log.info("Not implemented: WLToolkit.enableInputMethodsForTextComponent()");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isFrameStateSupported(int state)
|
||||
throws HeadlessException
|
||||
{
|
||||
// TODO: set based on wm_capabilities event
|
||||
return switch (state) {
|
||||
case Frame.NORMAL -> true;
|
||||
case Frame.ICONIFIED -> true;
|
||||
case Frame.MAXIMIZED_BOTH -> true;
|
||||
default -> false;
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initializeDesktopProperties() {
|
||||
super.initializeDesktopProperties();
|
||||
|
||||
if (!GraphicsEnvironment.isHeadless()) {
|
||||
desktopProperties.put("awt.mouse.numButtons", MOUSE_BUTTONS_COUNT);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNumberOfButtons(){
|
||||
return MOUSE_BUTTONS_COUNT;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object lazilyLoadDesktopProperty(String name) {
|
||||
log.info("Not implemented: WLToolkit.lazilyLoadDesktopProperty()");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void addPropertyChangeListener(String name, PropertyChangeListener pcl) {
|
||||
log.info("Not implemented: WLToolkit.addPropertyChangeListener()");
|
||||
}
|
||||
|
||||
/**
|
||||
* @see SunToolkit#needsXEmbedImpl
|
||||
*/
|
||||
@Override
|
||||
protected boolean needsXEmbedImpl() {
|
||||
log.info("Not implemented: WLToolkit.needsXEmbedImpl()");
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isModalityTypeSupported(Dialog.ModalityType modalityType) {
|
||||
return (modalityType == Dialog.ModalityType.MODELESS) ||
|
||||
(modalityType == Dialog.ModalityType.APPLICATION_MODAL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isModalExclusionTypeSupported(Dialog.ModalExclusionType exclusionType) {
|
||||
log.info("Not implemented: WLToolkit.isModalExclusionTypeSupported()");
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAlwaysOnTopSupported() {
|
||||
log.info("Not implemented: WLToolkit.isAlwaysOnTopSupported()");
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useBufferPerWindow() {
|
||||
log.info("Not implemented: WLToolkit.useBufferPerWindow()");
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
@Override
|
||||
protected boolean syncNativeQueue(long timeout) {
|
||||
log.info("Not implemented: WLToolkit.syncNativeQueue()");
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void grab(Window w) {
|
||||
log.info("Not implemented: WLToolkit.grab()");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ungrab(Window w) {
|
||||
log.info("Not implemented: WLToolkit.ungrab()");
|
||||
}
|
||||
/**
|
||||
* Returns if the java.awt.Desktop class is supported on the current
|
||||
* desktop.
|
||||
* <p>
|
||||
* The methods of java.awt.Desktop class are supported on the Gnome desktop.
|
||||
* Check if the running desktop is Gnome by checking the window manager.
|
||||
*/
|
||||
@Override
|
||||
public boolean isDesktopSupported(){
|
||||
log.info("Not implemented: WLToolkit.isDesktopSupported()");
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DesktopPeer createDesktopPeer(Desktop target){
|
||||
log.info("Not implemented: WLToolkit.createDesktopPeer()");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTaskbarSupported(){
|
||||
log.info("Not implemented: WLToolkit.isTaskbarSupported()");
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TaskbarPeer createTaskbarPeer(Taskbar target){
|
||||
log.info("Not implemented: WLToolkit.createTaskbarPeer()");
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean areExtraMouseButtonsEnabled = true;
|
||||
@Override
|
||||
public boolean areExtraMouseButtonsEnabled() throws HeadlessException {
|
||||
return areExtraMouseButtonsEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWindowOpacitySupported() {
|
||||
log.info("Not implemented: WLToolkit.isWindowOpacitySupported()");
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWindowShapingSupported() {
|
||||
log.info("Not implemented: WLToolkit.isWindowShapingSupported()");
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWindowTranslucencySupported() {
|
||||
log.info("Not implemented: WLToolkit.isWindowTranslucencySupported()");
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTranslucencyCapable(GraphicsConfiguration gc) {
|
||||
log.info("Not implemented: WLToolkit.isWindowTranslucencySupported()");
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sync() {
|
||||
flushImpl();
|
||||
}
|
||||
|
||||
private native int readEvents();
|
||||
private native void dispatchEventsOnEDT();
|
||||
private native void flushImpl();
|
||||
private native void dispatchNonDefaultQueuesImpl();
|
||||
|
||||
protected static void targetDisposedPeer(Object target, Object peer) {
|
||||
SunToolkit.targetDisposedPeer(target, peer);
|
||||
}
|
||||
|
||||
static void postEvent(AWTEvent event) {
|
||||
SunToolkit.postEvent(AppContext.getAppContext(), event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean needUpdateWindow() {
|
||||
return true;
|
||||
}
|
||||
|
||||
static WLInputState getInputState() {
|
||||
return inputState;
|
||||
}
|
||||
|
||||
// this emulates pointer leave event, which isn't sent sometimes by compositor
|
||||
static void resetPointerInputState() {
|
||||
inputState = inputState.resetPointerState();
|
||||
}
|
||||
}
|
||||
153
src/java.desktop/unix/classes/sun/awt/wl/WLWindowPeer.java
Normal file
153
src/java.desktop/unix/classes/sun/awt/wl/WLWindowPeer.java
Normal file
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* Copyright (c) 2022, JetBrains s.r.o.. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package sun.awt.wl;
|
||||
|
||||
import sun.awt.AWTAccessor;
|
||||
import java.awt.*;
|
||||
import java.awt.peer.ComponentPeer;
|
||||
import java.awt.peer.WindowPeer;
|
||||
|
||||
public class WLWindowPeer extends WLComponentPeer implements WindowPeer {
|
||||
private static Font defaultFont;
|
||||
|
||||
static synchronized Font getDefaultFont() {
|
||||
if (null == defaultFont) {
|
||||
defaultFont = new Font(Font.DIALOG, Font.PLAIN, 12);
|
||||
}
|
||||
return defaultFont;
|
||||
}
|
||||
|
||||
public WLWindowPeer(Window target) {
|
||||
super(target);
|
||||
|
||||
if (!target.isFontSet()) {
|
||||
target.setFont(getDefaultFont());
|
||||
}
|
||||
if (!target.isBackgroundSet()) {
|
||||
target.setBackground(SystemColor.window);
|
||||
}
|
||||
if (!target.isForegroundSet()) {
|
||||
target.setForeground(SystemColor.windowText);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void wlSetVisible(boolean v) {
|
||||
super.wlSetVisible(v);
|
||||
final AWTAccessor.ComponentAccessor acc = AWTAccessor.getComponentAccessor();
|
||||
for (Component c : ((Window)target).getComponents()) {
|
||||
ComponentPeer cPeer = acc.getPeer(c);
|
||||
if (cPeer instanceof WLComponentPeer) {
|
||||
((WLComponentPeer) cPeer).wlSetVisible(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void configureWLSurface() {
|
||||
super.configureWLSurface();
|
||||
updateMinimumSize();
|
||||
updateMaximumSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Insets getInsets() {
|
||||
return new Insets(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beginValidate() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endValidate() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toFront() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toBack() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateAlwaysOnTopState() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateFocusableWindowState() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setModalBlocked(Dialog blocker, boolean blocked) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateMinimumSize() {
|
||||
final Dimension minSize = getMinimumSize();
|
||||
super.setMinimumSizeTo(minSize);
|
||||
}
|
||||
|
||||
public void updateMaximumSize() {
|
||||
// TODO: make sure this is called when our target's maximum size changes
|
||||
final Dimension maxSize = target.isMaximumSizeSet() ? target.getMaximumSize() : null;
|
||||
if (maxSize != null) super.setMaximumSizeTo(maxSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateIconImages() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOpacity(float opacity) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOpaque(boolean isOpaque) {
|
||||
if (!isOpaque) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateWindow() {
|
||||
commitToServer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void repositionSecurityWarning() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
@@ -28,10 +28,12 @@ package sun.java2d;
|
||||
|
||||
import java.awt.GraphicsConfiguration;
|
||||
|
||||
import sun.awt.X11GraphicsConfig;
|
||||
import sun.awt.image.SunVolatileImage;
|
||||
import sun.awt.image.VolatileSurfaceManager;
|
||||
import sun.java2d.opengl.GLXGraphicsConfig;
|
||||
import sun.java2d.opengl.GLXVolatileSurfaceManager;
|
||||
import sun.java2d.wl.WLVolatileSurfaceManager;
|
||||
import sun.java2d.x11.X11VolatileSurfaceManager;
|
||||
import sun.java2d.xr.*;
|
||||
|
||||
@@ -60,8 +62,10 @@ public class UnixSurfaceManagerFactory extends SurfaceManagerFactory {
|
||||
return new GLXVolatileSurfaceManager(vImg, context);
|
||||
} else if(gc instanceof XRGraphicsConfig) {
|
||||
return new XRVolatileSurfaceManager(vImg, context);
|
||||
}else {
|
||||
} else if (gc instanceof X11GraphicsConfig){
|
||||
return new X11VolatileSurfaceManager(vImg, context);
|
||||
} else {
|
||||
return new WLVolatileSurfaceManager(vImg, context);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
101
src/java.desktop/unix/classes/sun/java2d/wl/WLSurfaceData.java
Normal file
101
src/java.desktop/unix/classes/sun/java2d/wl/WLSurfaceData.java
Normal file
@@ -0,0 +1,101 @@
|
||||
package sun.java2d.wl;
|
||||
|
||||
import java.awt.GraphicsConfiguration;
|
||||
import java.awt.GraphicsDevice;
|
||||
import java.awt.GraphicsEnvironment;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.image.ColorModel;
|
||||
import java.awt.image.Raster;
|
||||
|
||||
import sun.awt.image.SunVolatileImage;
|
||||
import sun.awt.wl.WLComponentPeer;
|
||||
import sun.awt.wl.WLGraphicsConfig;
|
||||
import sun.java2d.SurfaceData;
|
||||
import sun.java2d.loops.SurfaceType;
|
||||
import sun.util.logging.PlatformLogger;
|
||||
|
||||
public class WLSurfaceData extends SurfaceData {
|
||||
|
||||
private static final PlatformLogger log = PlatformLogger.getLogger("sun.java2d.wl.WLSurfaceData");
|
||||
private final WLComponentPeer peer;
|
||||
private final WLGraphicsConfig graphicsConfig;
|
||||
private final int depth;
|
||||
|
||||
public native void assignSurface(long surfacePtr);
|
||||
|
||||
protected native void initOps(int width, int height, int backgroundRGB);
|
||||
|
||||
protected WLSurfaceData(WLComponentPeer peer,
|
||||
WLGraphicsConfig gc,
|
||||
SurfaceType sType,
|
||||
ColorModel cm) {
|
||||
super(sType, cm);
|
||||
this.peer = peer;
|
||||
this.graphicsConfig = gc;
|
||||
this.depth = cm.getPixelSize();
|
||||
final int backgroundRGB = peer.getBackground() != null
|
||||
? peer.getBackground().getRGB()
|
||||
: 0;
|
||||
initOps(peer.getBufferWidth(), peer.getBufferHeight(), backgroundRGB);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method for instantiating a Window SurfaceData
|
||||
*/
|
||||
public static WLSurfaceData createData(WLComponentPeer peer) {
|
||||
WLGraphicsConfig gc = getGC(peer);
|
||||
return new WLSurfaceData(peer, gc, gc.getSurfaceType(), peer.getColorModel());
|
||||
}
|
||||
|
||||
public static WLGraphicsConfig getGC(WLComponentPeer peer) {
|
||||
if (peer != null) {
|
||||
return (WLGraphicsConfig) peer.getGraphicsConfiguration();
|
||||
} else {
|
||||
GraphicsEnvironment env =
|
||||
GraphicsEnvironment.getLocalGraphicsEnvironment();
|
||||
GraphicsDevice gd = env.getDefaultScreenDevice();
|
||||
return (WLGraphicsConfig) gd.getDefaultConfiguration();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public SurfaceData getReplacement() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GraphicsConfiguration getDeviceConfiguration() {
|
||||
return graphicsConfig;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Raster getRaster(int x, int y, int w, int h) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rectangle getBounds() {
|
||||
Rectangle r = peer.getBufferBounds();
|
||||
r.x = r.y = 0;
|
||||
return r;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getDestination() {
|
||||
return peer.getTarget();
|
||||
}
|
||||
|
||||
public static boolean isAccelerationEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static SurfaceData createData(WLGraphicsConfig gc, int width, int height, ColorModel cm,
|
||||
SunVolatileImage vImg, long drawable, int opaque,
|
||||
boolean b) {
|
||||
throw new UnsupportedOperationException("SurfaceData not associated with a Component is not supported");
|
||||
}
|
||||
|
||||
public native void revalidate(int width, int height);
|
||||
|
||||
public native void commitToServer();
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2022, JetBrains s.r.o.. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.java2d.wl;
|
||||
|
||||
import java.awt.GraphicsConfiguration;
|
||||
import java.awt.ImageCapabilities;
|
||||
import java.awt.Transparency;
|
||||
import java.awt.image.ColorModel;
|
||||
import sun.awt.wl.WLGraphicsConfig;
|
||||
import sun.awt.image.SunVolatileImage;
|
||||
import sun.awt.image.VolatileSurfaceManager;
|
||||
import sun.java2d.SurfaceData;
|
||||
|
||||
public class WLVolatileSurfaceManager extends VolatileSurfaceManager {
|
||||
public WLVolatileSurfaceManager(SunVolatileImage vImg, Object context) {
|
||||
super(vImg, context);
|
||||
}
|
||||
|
||||
protected boolean isAccelerationEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SurfaceData initAcceleratedSurface() {
|
||||
throw new UnsupportedOperationException("accelerated surface not supported");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImageCapabilities getCapabilities(GraphicsConfiguration gc) {
|
||||
// neither accelerated nor volatile
|
||||
return new ImageCapabilities(false);
|
||||
}
|
||||
}
|
||||
@@ -96,7 +96,7 @@ typedef struct {
|
||||
int num;
|
||||
} fDirRecord, *fDirRecordPtr;
|
||||
|
||||
#ifndef HEADLESS
|
||||
#ifdef XAWT
|
||||
|
||||
/*
|
||||
* Returns True if display is local, False of it's remote.
|
||||
@@ -194,7 +194,7 @@ static char **getX11FontPath ()
|
||||
}
|
||||
|
||||
|
||||
#endif /* !HEADLESS */
|
||||
#endif /* XAWT */
|
||||
|
||||
#if defined(__linux__)
|
||||
/* from awt_LoadLibrary.c */
|
||||
@@ -334,7 +334,7 @@ static char *getPlatformFontPathChars(JNIEnv *env, jboolean noType1, jboolean is
|
||||
* this code could throw an exception and the fontpath would fail to
|
||||
* be initialised.
|
||||
*/
|
||||
#ifndef HEADLESS
|
||||
#ifdef XAWT
|
||||
if (isX11) { // The following only works in an x11 environment.
|
||||
#if defined(__linux__)
|
||||
/* There's no headless build on linux ... */
|
||||
@@ -356,7 +356,7 @@ static char *getPlatformFontPathChars(JNIEnv *env, jboolean noType1, jboolean is
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif /* !HEADLESS */
|
||||
#endif /* XAWT */
|
||||
path = mergePaths(fcdirs, x11dirs, knowndirs, noType1);
|
||||
if (fcdirs != NULL) {
|
||||
char **p = fcdirs;
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
#include "VKWLGraphicsConfig.h"
|
||||
#include <dlfcn.h>
|
||||
#include <Trace.h>
|
||||
#include <stdint.h>
|
||||
#include <jvm_md.h>
|
||||
#include "jni.h"
|
||||
|
||||
// TODO
|
||||
@@ -0,0 +1,6 @@
|
||||
#ifndef VKWLGraphicsConfig_h_Included
|
||||
#define VKWLGraphicsConfig_h_Included
|
||||
|
||||
// TODO
|
||||
|
||||
#endif /* VKWLGraphicsConfig_h_Included */
|
||||
772
src/java.desktop/unix/native/common/java2d/wl/WLBuffers.c
Normal file
772
src/java.desktop/unix/native/common/java2d/wl/WLBuffers.c
Normal file
@@ -0,0 +1,772 @@
|
||||
/*
|
||||
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2022, JetBrains s.r.o.. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <sys/mman.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include "Trace.h"
|
||||
#include "jni_util.h"
|
||||
|
||||
#include "awt.h"
|
||||
|
||||
#include "WLBuffers.h"
|
||||
|
||||
#ifndef HEADLESS
|
||||
|
||||
extern struct wl_shm_pool *CreateShmPool(int32_t size, const char *name, void **data); // defined in WLToolkit.c
|
||||
|
||||
static bool
|
||||
BufferShowToWayland(WLSurfaceBufferManager * manager);
|
||||
|
||||
static void
|
||||
SurfaceBufferCreate(WLSurfaceBufferManager * manager, size_t newSize);
|
||||
|
||||
static void
|
||||
SurfaceBufferDestroy(WLSurfaceBufferManager * manager, bool destroyPool);
|
||||
|
||||
static void
|
||||
SurfaceBufferAdjust(WLSurfaceBufferManager * manager);
|
||||
|
||||
static void
|
||||
ScheduleFrameCallback(WLSurfaceBufferManager * manager);
|
||||
|
||||
static inline void
|
||||
RegisterFrameLost(const char* reason)
|
||||
{
|
||||
if (getenv("J2D_STATS")) {
|
||||
fprintf(stderr, "WLBuffers: frame lost, reason '%s'\n", reason);
|
||||
fflush(stderr);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
ReportFatalError(const char* file, int line, const char *msg)
|
||||
{
|
||||
fprintf(stderr, "Fatal error at %s:%d: %s\n", file, line, msg);
|
||||
fflush(stderr);
|
||||
assert(0);
|
||||
}
|
||||
|
||||
static inline void
|
||||
AssertCalledOnEDT(const char* file, int line)
|
||||
{
|
||||
char threadName[16];
|
||||
pthread_getname_np(pthread_self(), threadName, sizeof(threadName));
|
||||
if (strncmp(threadName, "AWT-EventQueue", 14) != 0) {
|
||||
fprintf(stderr, "Assert failed (called on %s instead of EDT) at %s:%d\n", threadName, file, line);
|
||||
fflush(stderr);
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
AssertDrawLockIsHeld(WLSurfaceBufferManager* manager, const char * file, int line);
|
||||
|
||||
#define ASSERT_ON_EDT() AssertCalledOnEDT(__FILE__, __LINE__)
|
||||
#define WL_FATAL_ERROR(msg) ReportFatalError(__FILE__, __LINE__, msg)
|
||||
#define ASSERT_DRAW_LOCK_IS_HELD(manager) AssertDrawLockIsHeld(manager, __FILE__, __LINE__)
|
||||
#define ASSERT_SHOW_LOCK_IS_HELD(manager) AssertShowLockIsHeld(manager, __FILE__, __LINE__)
|
||||
|
||||
#define MUTEX_LOCK(m) if (pthread_mutex_lock(&(m))) { WL_FATAL_ERROR("Failed to lock mutex"); }
|
||||
#define MUTEX_UNLOCK(m) if (pthread_mutex_unlock(&(m))) { WL_FATAL_ERROR("Failed to unlock mutex"); }
|
||||
|
||||
/**
|
||||
* Represents one rectangular area linked into a list.
|
||||
*
|
||||
* Such a list represents portions of a buffer that have been
|
||||
* modified (damaged) in some way.
|
||||
*/
|
||||
typedef struct DamageList {
|
||||
jint x, y;
|
||||
jint width, height;
|
||||
struct DamageList *next;
|
||||
} DamageList;
|
||||
|
||||
static DamageList*
|
||||
DamageList_Add(DamageList* list, jint x, jint y, jint width, jint height)
|
||||
{
|
||||
DamageList * l = list;
|
||||
DamageList * p = NULL;
|
||||
while (l) {
|
||||
if (x >= l->x && y >= l->y
|
||||
&& x + width <= l->x + l->width
|
||||
&& y + height <= l->y + l->height) {
|
||||
// No need to add an area completely covered by another one.
|
||||
return list;
|
||||
} else if (x <= l->x && y <= l->y
|
||||
&& x + width >= l->x + l->width
|
||||
&& y + height >= l->y + l->height) {
|
||||
// The new element will cover this area, no need to keep
|
||||
// a separate damage element for it.
|
||||
DamageList * next = l->next;
|
||||
if (p != NULL) {
|
||||
p->next = next;
|
||||
} else {
|
||||
list = next;
|
||||
}
|
||||
free(l);
|
||||
l = next;
|
||||
} else {
|
||||
p = l;
|
||||
l = l->next;
|
||||
}
|
||||
}
|
||||
|
||||
DamageList *item = malloc(sizeof(DamageList));
|
||||
item->x = x;
|
||||
item->y = y;
|
||||
item->width = width;
|
||||
item->height = height;
|
||||
item->next = list;
|
||||
return item;
|
||||
}
|
||||
|
||||
static void
|
||||
DamageList_SendAll(DamageList* list, struct wl_surface* wlSurface)
|
||||
{
|
||||
while (list) {
|
||||
wl_surface_damage_buffer(wlSurface,
|
||||
list->x, list->y,
|
||||
list->width, list->height);
|
||||
list = list->next;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
DamageList_FreeAll(DamageList* list)
|
||||
{
|
||||
while (list) {
|
||||
DamageList * next = list->next;
|
||||
free(list);
|
||||
list = next;
|
||||
}
|
||||
}
|
||||
|
||||
typedef enum WLSurfaceBufferState {
|
||||
/// The buffer has no valid content yet.
|
||||
WL_BUFFER_NEW = 0,
|
||||
/// The buffer is being read by Wayland and must not be modified by us.
|
||||
WL_BUFFER_LOCKED = 1,
|
||||
/// The buffer was read by Wayland and subsequently released to us.
|
||||
/// Can be modified now. Still has valid content.
|
||||
WL_BUFFER_RELEASED = 2
|
||||
} WLSurfaceBufferState;
|
||||
|
||||
// Identifies a frame that is being drawn or displayed on the screen.
|
||||
// Will stay unique for approx 2 years of uptime at 60fps.
|
||||
typedef uint32_t frame_id_t;
|
||||
|
||||
/**
|
||||
* Contains data needed to maintain a wl_buffer instance.
|
||||
*
|
||||
* This buffer is usually attached to a wl_surface.
|
||||
* Its size determines the size of the surface.
|
||||
*/
|
||||
typedef struct WLSurfaceBuffer {
|
||||
struct wl_shm_pool * wlPool;
|
||||
struct wl_buffer * wlBuffer;
|
||||
pixel_t * data; /// points to a memory segment shared with Wayland
|
||||
size_t size; /// size of the memory segment; may be more than necessary
|
||||
WLSurfaceBufferState state;
|
||||
DamageList * damageList; /// Areas of the buffer that need to be re-drawn by Wayland
|
||||
frame_id_t frameID; /// ID of the frame currently sent to Wayland
|
||||
} WLSurfaceBuffer;
|
||||
|
||||
/**
|
||||
* Represents a buffer to draw in.
|
||||
*
|
||||
* The underlying pixels array is pointed to by the data field.
|
||||
* The changes made by drawing are accumulated in the damageList field.
|
||||
*/
|
||||
struct WLDrawBuffer {
|
||||
WLSurfaceBufferManager * manager;
|
||||
pixel_t * data;
|
||||
DamageList * damageList;
|
||||
frame_id_t frameID; /// ID of the frame being drawn
|
||||
};
|
||||
|
||||
/**
|
||||
* Contains data necessary to manage multiple backing buffers for one wl_surface.
|
||||
*
|
||||
* There's one and only buffer attached to the surface that Wayland reads from,
|
||||
* which is allocated in shared memory: bufferForShow.
|
||||
*
|
||||
* There's one buffer (but it possible to have more) that can be used for drawing.
|
||||
* When Wayland is ready to receive updates, the modified portions of that buffer
|
||||
* are copied over to bufferForShow.
|
||||
*
|
||||
* The size of bufferForShow is determined by width and height fields; the size of
|
||||
* bufferForShow can lag behind and will re-adjust after Wayland has released it
|
||||
* back to us.
|
||||
*/
|
||||
struct WLSurfaceBufferManager {
|
||||
struct wl_surface * wlSurface; // only accessed under showLock
|
||||
int backgroundRGB;
|
||||
|
||||
/**
|
||||
* ID of the "drawing" frame to be sent to Wayland.
|
||||
* Is set by committing a new frame with WLSBM_SurfaceCommit().
|
||||
* Gets re-set to 0 right after sending to Wayland.
|
||||
*/
|
||||
frame_id_t commitFrameID; // only accessed under showLock
|
||||
struct wl_callback* wl_frame_callback; // only accessed under showLock
|
||||
|
||||
pthread_mutex_t showLock;
|
||||
WLSurfaceBuffer bufferForShow; // only accessed under showLock
|
||||
|
||||
pthread_mutex_t drawLock;
|
||||
WLDrawBuffer bufferForDraw; // only accessed under drawLock
|
||||
jint width; // only accessed under drawLock
|
||||
jint height; // only accessed under drawLock
|
||||
bool sizeChanged; // only accessed under drawLock
|
||||
};
|
||||
|
||||
static inline void
|
||||
AssertDrawLockIsHeld(WLSurfaceBufferManager* manager, const char * file, int line)
|
||||
{
|
||||
// TODO: would be nice to be able to check the mutex owner
|
||||
}
|
||||
|
||||
static inline void
|
||||
AssertShowLockIsHeld(WLSurfaceBufferManager* manager, const char * file, int line)
|
||||
{
|
||||
if (pthread_mutex_trylock(&manager->showLock) == 0) {
|
||||
fprintf(stderr, "showLock not acquired at %s:%d\n", file, line);
|
||||
fflush(stderr);
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
ShowFrameNumbers(WLSurfaceBufferManager* manager, const char *fmt, ...)
|
||||
{
|
||||
// TODO: this is temporary debugging code that will be removed in the future
|
||||
if (getenv("J2D_TRACE_LEVEL")) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
fprintf(stderr, ">>> ");
|
||||
vfprintf(stderr, fmt, args);
|
||||
fprintf(stderr, "; showing frame %d, drawing frame %d, frame to be committed %d\n",
|
||||
manager->bufferForShow.frameID,
|
||||
manager->bufferForDraw.frameID,
|
||||
manager->commitFrameID);
|
||||
fflush(stderr);
|
||||
va_end(args);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of bytes in the "draw" buffer.
|
||||
*
|
||||
* This can differ from the size of the "display" buffer at
|
||||
* certain points in time until that "display" buffer gets
|
||||
* released to us by Wayland and readjusts itself.
|
||||
*/
|
||||
static inline size_t
|
||||
DrawBufferSizeInBytes(WLSurfaceBufferManager * manager)
|
||||
{
|
||||
const jint stride = manager->width * (jint)sizeof(pixel_t);
|
||||
return stride * manager->height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of pixels in the "draw" buffer.
|
||||
*/
|
||||
static inline jint
|
||||
DrawBufferSizeInPixels(WLSurfaceBufferManager * manager)
|
||||
{
|
||||
return manager->width * manager->height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of bytes in the buffer shared
|
||||
* with Wayland (the "display" buffer).
|
||||
*/
|
||||
static inline size_t
|
||||
ShowBufferSizeInBytes(WLSurfaceBufferManager * manager)
|
||||
{
|
||||
return manager->bufferForShow.size;
|
||||
}
|
||||
|
||||
static void
|
||||
wl_buffer_release(void * data, struct wl_buffer * wl_buffer)
|
||||
{
|
||||
/* Sent by the compositor when it's no longer using this buffer */
|
||||
WLSurfaceBufferManager *manager = (WLSurfaceBufferManager *) data;
|
||||
MUTEX_LOCK(manager->showLock);
|
||||
assert(manager->bufferForShow.wlBuffer == wl_buffer);
|
||||
assert(manager->bufferForShow.state == WL_BUFFER_LOCKED);
|
||||
ShowFrameNumbers(manager, "wl_buffer_release");
|
||||
manager->bufferForShow.state = WL_BUFFER_RELEASED;
|
||||
MUTEX_UNLOCK(manager->showLock);
|
||||
}
|
||||
|
||||
static const struct wl_buffer_listener wl_buffer_listener = {
|
||||
.release = wl_buffer_release,
|
||||
};
|
||||
|
||||
static void
|
||||
wl_frame_callback_done(void * data,
|
||||
struct wl_callback * wl_callback,
|
||||
uint32_t callback_data)
|
||||
{
|
||||
WLSurfaceBufferManager * manager = (WLSurfaceBufferManager *) data;
|
||||
MUTEX_LOCK(manager->showLock);
|
||||
assert(manager->wl_frame_callback == wl_callback);
|
||||
wl_callback_destroy(manager->wl_frame_callback);
|
||||
manager->wl_frame_callback = NULL;
|
||||
|
||||
struct wl_surface * wlSurface = manager->wlSurface;
|
||||
if (wlSurface) {
|
||||
// Wayland is ready to get a new frame from us. Send whatever we have
|
||||
// managed to draw by this time (maybe nothing).
|
||||
if (!BufferShowToWayland(manager)) {
|
||||
// Re-schedule the same callback if we were unable to send the
|
||||
// new frame to Wayland. This can happen, for instance, if Wayland
|
||||
// haven't released the surface buffer to us yet.
|
||||
ScheduleFrameCallback(manager);
|
||||
}
|
||||
|
||||
ShowFrameNumbers(manager, "wl_frame_callback_done");
|
||||
// Need to commit either the damage done to the surface or the re-scheduled
|
||||
// callback.
|
||||
wl_surface_commit(wlSurface);
|
||||
}
|
||||
MUTEX_UNLOCK(manager->showLock);
|
||||
}
|
||||
|
||||
static const struct wl_callback_listener wl_frame_callback_listener = {
|
||||
.done = wl_frame_callback_done
|
||||
};
|
||||
|
||||
static void
|
||||
ScheduleFrameCallback(WLSurfaceBufferManager * manager)
|
||||
{
|
||||
ASSERT_SHOW_LOCK_IS_HELD(manager);
|
||||
assert(manager->wlSurface);
|
||||
|
||||
if (!manager->wl_frame_callback) {
|
||||
manager->wl_frame_callback = wl_surface_frame(manager->wlSurface);
|
||||
wl_callback_add_listener(manager->wl_frame_callback, &wl_frame_callback_listener, manager);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies all the damaged areas from the drawing buffer to show buffer and
|
||||
* transfers manager to the "new frame sent" state.
|
||||
*
|
||||
* Returns true if a buffer (possibly with damage) was attached to the managed
|
||||
* Wayland surface and false otherwise.
|
||||
*/
|
||||
static bool
|
||||
SendToWayland(WLSurfaceBufferManager * manager)
|
||||
{
|
||||
ASSERT_SHOW_LOCK_IS_HELD(manager);
|
||||
|
||||
if (manager->wlSurface) { // may get called without an associated wl_surface
|
||||
assert (manager->bufferForShow.state != WL_BUFFER_LOCKED);
|
||||
manager->bufferForShow.state = WL_BUFFER_LOCKED;
|
||||
// wl_buffer_listener will release bufferForShow when Wayland's done with it
|
||||
wl_surface_attach(manager->wlSurface, manager->bufferForShow.wlBuffer, 0, 0);
|
||||
|
||||
DamageList_SendAll(manager->bufferForShow.damageList, manager->wlSurface);
|
||||
DamageList_FreeAll(manager->bufferForShow.damageList);
|
||||
manager->bufferForShow.damageList = NULL;
|
||||
|
||||
manager->bufferForShow.frameID = manager->bufferForDraw.frameID;
|
||||
manager->bufferForDraw.frameID++;
|
||||
manager->commitFrameID = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline void
|
||||
CopyDamagedArea(WLSurfaceBufferManager * manager, jint x, jint y, jint width, jint height)
|
||||
{
|
||||
assert(x >= 0);
|
||||
assert(y >= 0);
|
||||
assert(width >= 0);
|
||||
assert(height >= 0);
|
||||
assert(height + y >= 0);
|
||||
assert(width + x >= 0);
|
||||
|
||||
pixel_t * dest = manager->bufferForShow.data;
|
||||
pixel_t * src = manager->bufferForDraw.data;
|
||||
|
||||
for (jint i = y; i < height + y; i++) {
|
||||
pixel_t * dest_row = &dest[i * manager->width];
|
||||
pixel_t * src_row = &src [i * manager->width];
|
||||
for (jint j = x; j < width + x; j++) {
|
||||
dest_row[j] = src_row[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies areas from the current damageList of the drawing surface to
|
||||
* the buffer associated with the Wayland surface for displaying.
|
||||
*
|
||||
* Clears the list of damaged areas from the drawing buffer and
|
||||
* moves that list to the displaying buffer so that Wayland can get
|
||||
* notified of what has changed in the buffer.
|
||||
*/
|
||||
static bool
|
||||
CopyDamagedAreasToShowBuffer(WLSurfaceBufferManager * manager)
|
||||
{
|
||||
ASSERT_SHOW_LOCK_IS_HELD(manager);
|
||||
ASSERT_DRAW_LOCK_IS_HELD(manager);
|
||||
assert(manager->bufferForShow.damageList == NULL);
|
||||
assert(DrawBufferSizeInBytes(manager) <= ShowBufferSizeInBytes(manager));
|
||||
|
||||
const bool willCommit = (manager->bufferForDraw.damageList != NULL) && manager->wlSurface != NULL;
|
||||
if (willCommit) {
|
||||
manager->bufferForShow.damageList = manager->bufferForDraw.damageList;
|
||||
manager->bufferForDraw.damageList = NULL;
|
||||
|
||||
for (DamageList* l = manager->bufferForShow.damageList; l; l = l->next) {
|
||||
CopyDamagedArea(manager, l->x, l->y, l->width, l->height);
|
||||
}
|
||||
}
|
||||
|
||||
return willCommit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to send damaged areas in the drawing buffer from the frame specified
|
||||
* by manager->commitFrameID to Wayland by copying them to the show buffer
|
||||
* and notifying Wayland of that.
|
||||
*
|
||||
* Returns false if another attempt to send this frame must be scheduled
|
||||
* and true otherwise (a new frame is expected).
|
||||
*/
|
||||
static bool
|
||||
BufferShowToWayland(WLSurfaceBufferManager * manager)
|
||||
{
|
||||
ASSERT_SHOW_LOCK_IS_HELD(manager);
|
||||
|
||||
if (manager->commitFrameID != manager->bufferForDraw.frameID) {
|
||||
RegisterFrameLost("Attempt to commit a frame with ID different from the one we committed");
|
||||
ShowFrameNumbers(manager, "BufferShowToWayland - skipped frame");
|
||||
MUTEX_UNLOCK(manager->showLock);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool needAnotherTry = true;
|
||||
ShowFrameNumbers(manager, "BufferShowToWayland");
|
||||
|
||||
const bool bufferForDrawWasLocked = pthread_mutex_trylock(&manager->drawLock);
|
||||
if (bufferForDrawWasLocked) {
|
||||
// Can't display the buffer with new pixels, so let's give
|
||||
// what we already have.
|
||||
if (manager->bufferForShow.state == WL_BUFFER_NEW) {
|
||||
RegisterFrameLost("No old frame to show while the new one isn't ready yet");
|
||||
} else {
|
||||
// The state is either released or locked already
|
||||
RegisterFrameLost("Repeating last frame while the new one isn't ready yet");
|
||||
}
|
||||
} else { // bufferForDraw was not locked, but is locked now
|
||||
if (manager->bufferForShow.state == WL_BUFFER_LOCKED) {
|
||||
RegisterFrameLost("New buffer is available, but Wayland hasn't released the old one yet");
|
||||
pthread_mutex_unlock(&manager->drawLock);
|
||||
} else {
|
||||
if (manager->sizeChanged) {
|
||||
manager->sizeChanged = false;
|
||||
SurfaceBufferAdjust(manager);
|
||||
// NB: the new buffer may get committed later if, for instance,
|
||||
// there is nothing to show at the moment (draw buffer damage is empty).
|
||||
// TODO: maybe avoid changing size until the moment it will actually have
|
||||
// a chance to be committed? Otherwise we might be wasting time adjusting.
|
||||
}
|
||||
const bool needCommit = CopyDamagedAreasToShowBuffer(manager);
|
||||
pthread_mutex_unlock(&manager->drawLock);
|
||||
if (needCommit) {
|
||||
needAnotherTry = !SendToWayland(manager);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return !needAnotherTry;
|
||||
}
|
||||
|
||||
static void
|
||||
SurfaceBufferCreate(WLSurfaceBufferManager * manager, size_t newSize)
|
||||
{
|
||||
const bool createPool = (newSize > ShowBufferSizeInBytes(manager));
|
||||
if (createPool) {
|
||||
manager->bufferForShow.size = newSize;
|
||||
manager->bufferForShow.wlPool = CreateShmPool(newSize, "jwlshm", (void**)&manager->bufferForShow.data);
|
||||
if (!manager->bufferForShow.wlPool)
|
||||
return;
|
||||
} else {
|
||||
assert(manager->bufferForShow.size >= newSize);
|
||||
assert(manager->bufferForShow.data);
|
||||
assert(manager->bufferForShow.wlPool);
|
||||
memset(manager->bufferForShow.data, 0, newSize);
|
||||
}
|
||||
|
||||
const int32_t stride = manager->width * sizeof(pixel_t);
|
||||
manager->bufferForShow.wlBuffer = wl_shm_pool_create_buffer(manager->bufferForShow.wlPool, 0,
|
||||
manager->width,
|
||||
manager->height,
|
||||
stride,
|
||||
WL_SHM_FORMAT_XRGB8888);
|
||||
manager->bufferForShow.state = WL_BUFFER_NEW;
|
||||
wl_buffer_add_listener(manager->bufferForShow.wlBuffer,
|
||||
&wl_buffer_listener,
|
||||
manager);
|
||||
}
|
||||
|
||||
static void
|
||||
SurfaceBufferDestroy(WLSurfaceBufferManager * manager, bool destroyPool)
|
||||
{
|
||||
if (destroyPool) {
|
||||
// NB: the server (Wayland) will hold this memory for a bit longer, so it's
|
||||
// OK to unmap now without waiting for the "release" event for the buffer
|
||||
// from Wayland.
|
||||
munmap(manager->bufferForShow.data, manager->bufferForShow.size);
|
||||
manager->bufferForShow.size = 0;
|
||||
manager->bufferForShow.data = NULL;
|
||||
wl_shm_pool_destroy(manager->bufferForShow.wlPool);
|
||||
manager->bufferForShow.wlPool = NULL;
|
||||
}
|
||||
// "Destroying the wl_buffer after wl_buffer.release does not change
|
||||
// the surface contents" (source: wayland.xml)
|
||||
wl_buffer_destroy(manager->bufferForShow.wlBuffer);
|
||||
manager->bufferForShow.wlBuffer = NULL;
|
||||
DamageList_FreeAll(manager->bufferForShow.damageList);
|
||||
manager->bufferForShow.damageList = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
SurfaceBufferAdjust(WLSurfaceBufferManager * manager)
|
||||
{
|
||||
assert(manager->bufferForShow.state != WL_BUFFER_LOCKED);
|
||||
ASSERT_SHOW_LOCK_IS_HELD(manager);
|
||||
ASSERT_DRAW_LOCK_IS_HELD(manager);
|
||||
|
||||
const bool needNewMemory = (DrawBufferSizeInBytes(manager) > ShowBufferSizeInBytes(manager));
|
||||
SurfaceBufferDestroy(manager, needNewMemory);
|
||||
SurfaceBufferCreate(manager, DrawBufferSizeInBytes(manager));
|
||||
}
|
||||
|
||||
static void
|
||||
DrawBufferCreate(WLSurfaceBufferManager * manager)
|
||||
{
|
||||
assert(manager->bufferForDraw.data == NULL);
|
||||
assert(manager->bufferForDraw.damageList == NULL);
|
||||
|
||||
manager->bufferForDraw.frameID++;
|
||||
manager->bufferForDraw.manager = manager;
|
||||
manager->bufferForDraw.data = malloc(DrawBufferSizeInBytes(manager));
|
||||
|
||||
// TODO: do we really need this here? Why can't pipelines draw the background?
|
||||
for (jint i = 0; i < DrawBufferSizeInPixels(manager); ++i) {
|
||||
manager->bufferForDraw.data[i] = manager->backgroundRGB;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
DrawBufferDestroy(WLSurfaceBufferManager * manager)
|
||||
{
|
||||
free(manager->bufferForDraw.data);
|
||||
manager->bufferForDraw.data = NULL;
|
||||
DamageList_FreeAll(manager->bufferForDraw.damageList);
|
||||
manager->bufferForDraw.damageList = NULL;
|
||||
}
|
||||
|
||||
WLSurfaceBufferManager *
|
||||
WLSBM_Create(jint width, jint height, jint rgb)
|
||||
{
|
||||
WLSurfaceBufferManager * manager = calloc(1, sizeof(WLSurfaceBufferManager));
|
||||
if (!manager) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
manager->width = width;
|
||||
manager->height = height;
|
||||
manager->backgroundRGB = rgb;
|
||||
|
||||
pthread_mutex_init(&manager->showLock, NULL);
|
||||
SurfaceBufferCreate(manager, DrawBufferSizeInBytes(manager));
|
||||
if (manager->bufferForShow.wlPool == NULL) {
|
||||
free(manager);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pthread_mutexattr_t attr;
|
||||
pthread_mutexattr_init(&attr);
|
||||
// Recursive mutex is required because the same "draw" buffer can be
|
||||
// both read from and written to (when scrolling, for instance).
|
||||
// So it needs to be able to be "locked" twice: once for writing and
|
||||
// once for reading.
|
||||
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE_NP);
|
||||
pthread_mutex_init(&manager->drawLock, &attr);
|
||||
DrawBufferCreate(manager);
|
||||
|
||||
J2dTrace3(J2D_TRACE_INFO, "WLSBM_Create: created %p for %dx%d px\n", manager, width, height);
|
||||
return manager;
|
||||
}
|
||||
|
||||
void
|
||||
WLSBM_SurfaceAssign(WLSurfaceBufferManager * manager, struct wl_surface* wl_surface)
|
||||
{
|
||||
J2dTrace2(J2D_TRACE_INFO, "WLSBM_SurfaceAssign: assigned surface %p to manger %p\n", wl_surface, manager);
|
||||
|
||||
MUTEX_LOCK(manager->showLock);
|
||||
manager->wlSurface = wl_surface;
|
||||
MUTEX_UNLOCK(manager->showLock);
|
||||
}
|
||||
|
||||
void
|
||||
WLSBM_Destroy(WLSurfaceBufferManager * manager)
|
||||
{
|
||||
J2dTrace1(J2D_TRACE_INFO, "WLSBM_Destroy: manger %p\n", manager);
|
||||
|
||||
// NB: must never be called in parallel with the Wayland event handlers
|
||||
MUTEX_LOCK(manager->showLock);
|
||||
MUTEX_LOCK(manager->drawLock);
|
||||
if (manager->wl_frame_callback) {
|
||||
wl_callback_destroy(manager->wl_frame_callback);
|
||||
}
|
||||
SurfaceBufferDestroy(manager, true);
|
||||
DrawBufferDestroy(manager);
|
||||
MUTEX_UNLOCK(manager->drawLock);
|
||||
MUTEX_UNLOCK(manager->showLock);
|
||||
|
||||
pthread_mutex_destroy(&manager->showLock);
|
||||
pthread_mutex_destroy(&manager->drawLock);
|
||||
memset(manager, 0, sizeof(*manager));
|
||||
free(manager);
|
||||
}
|
||||
|
||||
jint
|
||||
WLSBM_WidthGet(WLSurfaceBufferManager * manager)
|
||||
{
|
||||
ASSERT_DRAW_LOCK_IS_HELD(manager);
|
||||
return manager->width;
|
||||
}
|
||||
|
||||
jint
|
||||
WLSBM_HeightGet(WLSurfaceBufferManager * manager)
|
||||
{
|
||||
ASSERT_DRAW_LOCK_IS_HELD(manager);
|
||||
return manager->height;
|
||||
}
|
||||
|
||||
WLDrawBuffer *
|
||||
WLSBM_BufferAcquireForDrawing(WLSurfaceBufferManager * manager)
|
||||
{
|
||||
MUTEX_LOCK(manager->drawLock);
|
||||
|
||||
// We are going to "damage" the drawing frame now and therefore
|
||||
// shall not commit until WLSBM_SurfaceCommit() is issued.
|
||||
// Setting commitFrameID to a number that no draw frame has
|
||||
// achieves just that.
|
||||
|
||||
// This effectively disables redrawing during very fast interactive
|
||||
// resize of applications that draw often (like animated pictures)
|
||||
// as new frames often appear after the size had been changed, but before
|
||||
// the change has been committed to Wayland. In this case we don't
|
||||
// commit and wait for a finished frame, which may not come quick
|
||||
// enough before the next size change, which re-starts the same cycle.
|
||||
MUTEX_LOCK(manager->showLock);
|
||||
manager->commitFrameID = 0;
|
||||
MUTEX_UNLOCK(manager->showLock);
|
||||
return &manager->bufferForDraw;
|
||||
}
|
||||
|
||||
void
|
||||
WLSBM_BufferReturn(WLSurfaceBufferManager * manager, WLDrawBuffer * buffer)
|
||||
{
|
||||
if (&manager->bufferForDraw == buffer) {
|
||||
MUTEX_UNLOCK(buffer->manager->drawLock);
|
||||
ShowFrameNumbers(manager, "WLSBM_BufferReturn");
|
||||
} else {
|
||||
WL_FATAL_ERROR("WLSBM_BufferReturn() called with an unidentified buffer");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
WLSBM_SurfaceCommit(WLSurfaceBufferManager * manager)
|
||||
{
|
||||
MUTEX_LOCK(manager->showLock);
|
||||
// Request that the frame with this ID to be committed to Wayland
|
||||
// and no other. Any attempt to draw on this frame will cancel
|
||||
// the commit.
|
||||
manager->commitFrameID = manager->bufferForDraw.frameID;
|
||||
|
||||
ShowFrameNumbers(manager, "WLSBM_SurfaceCommit");
|
||||
if (manager->wlSurface) {
|
||||
ScheduleFrameCallback(manager);
|
||||
wl_surface_commit(manager->wlSurface);
|
||||
}
|
||||
MUTEX_UNLOCK(manager->showLock);
|
||||
}
|
||||
|
||||
void
|
||||
WLSB_Damage(WLDrawBuffer * buffer, jint x, jint y, jint width, jint height)
|
||||
{
|
||||
assert(&buffer->manager->bufferForDraw == buffer); // only one buffer currently supported
|
||||
ASSERT_DRAW_LOCK_IS_HELD(buffer->manager);
|
||||
assert(x >= 0);
|
||||
assert(y >= 0);
|
||||
assert(x + width <= buffer->manager->width);
|
||||
assert(y + height <= buffer->manager->height);
|
||||
|
||||
buffer->damageList = DamageList_Add(buffer->damageList, x, y, width, height);
|
||||
ShowFrameNumbers(buffer->manager, "WLSB_Damage (at %d, %d %dx%d)", x, y, width, height);
|
||||
}
|
||||
|
||||
pixel_t *
|
||||
WLSB_DataGet(WLDrawBuffer * buffer)
|
||||
{
|
||||
ASSERT_DRAW_LOCK_IS_HELD(buffer->manager);
|
||||
return buffer->data;
|
||||
}
|
||||
|
||||
void
|
||||
WLSBM_SizeChangeTo(WLSurfaceBufferManager * manager, jint width, jint height)
|
||||
{
|
||||
MUTEX_LOCK(manager->drawLock);
|
||||
|
||||
if (manager->width != width || manager->height != height) {
|
||||
DrawBufferDestroy(manager);
|
||||
|
||||
manager->width = width;
|
||||
manager->height = height;
|
||||
manager->sizeChanged = true;
|
||||
|
||||
DrawBufferCreate(manager);
|
||||
ShowFrameNumbers(manager, "WLSBM_SizeChangeTo %dx%d", width, height);
|
||||
}
|
||||
|
||||
MUTEX_UNLOCK(manager->drawLock);
|
||||
}
|
||||
|
||||
#endif
|
||||
134
src/java.desktop/unix/native/common/java2d/wl/WLBuffers.h
Normal file
134
src/java.desktop/unix/native/common/java2d/wl/WLBuffers.h
Normal file
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2022, JetBrains s.r.o.. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
#ifndef WLBUFFERS_H
|
||||
#define WLBUFFERS_H
|
||||
|
||||
#include <wayland-client.h>
|
||||
|
||||
#include "jni.h"
|
||||
|
||||
/**
|
||||
* An opaque type representing WayLand Surface Buffer Manager.
|
||||
*/
|
||||
typedef struct WLSurfaceBufferManager WLSurfaceBufferManager;
|
||||
|
||||
/**
|
||||
* An opaque type representing a drawing buffer obtained from
|
||||
* the WayLand Surface Buffer Manager.
|
||||
*/
|
||||
typedef struct WLDrawBuffer WLDrawBuffer;
|
||||
|
||||
/**
|
||||
* An unsigned integer representing a pixel in memory.
|
||||
* The format is defined by WLSB_DataGet() below.
|
||||
*/
|
||||
typedef uint32_t pixel_t;
|
||||
|
||||
/**
|
||||
* Create a WayLand Surface Buffer Manager for a surface of size width x height
|
||||
* pixels with the background color rgb.
|
||||
*
|
||||
* At least two buffers are associated with the manager:
|
||||
* - a drawing buffer that SurfaceDataOps operate with (see WLSurfaceData.c) and
|
||||
* - a displaying buffer that is essentially wl_buffer attached to wl_surface.
|
||||
*
|
||||
* Wayland displays pixels from the displaying buffer and we draw pixels to
|
||||
* the drawing buffer. The manager is responsible for timely copying from
|
||||
* the drawing to displaying buffer, synchronization, and sending
|
||||
* the appropriate notifications to Wayland.
|
||||
*/
|
||||
WLSurfaceBufferManager * WLSBM_Create(jint width, jint height, jint rgb);
|
||||
|
||||
/**
|
||||
* Free all resources allocated for the WayLand Surface Buffer Manager,
|
||||
* including associated buffers.
|
||||
*/
|
||||
void WLSBM_Destroy(WLSurfaceBufferManager *);
|
||||
|
||||
/**
|
||||
* Associate a wl_surface with the WayLand Surface Buffer Manager.
|
||||
* The "displaying" buffer will be attached to this surface when
|
||||
* the buffer is ready to be displayed.
|
||||
*/
|
||||
void WLSBM_SurfaceAssign(WLSurfaceBufferManager *, struct wl_surface *);
|
||||
|
||||
void WLSBM_SurfaceCommit(WLSurfaceBufferManager *);
|
||||
|
||||
/**
|
||||
* Returns the width of the buffer managed by this
|
||||
* WayLand Surface Buffer Manager.
|
||||
*/
|
||||
jint WLSBM_WidthGet(WLSurfaceBufferManager *);
|
||||
|
||||
/**
|
||||
* Returns the height of the buffer managed by this
|
||||
* WayLand Surface Buffer Manager.
|
||||
*/
|
||||
jint WLSBM_HeightGet(WLSurfaceBufferManager *);
|
||||
|
||||
/**
|
||||
* Changes the size of the buffer associated with the WayLand Surface
|
||||
* Buffer Manager.
|
||||
* The change is synchronized with drawing on the buffer,
|
||||
* so any draw operation performed after return from this function
|
||||
* will get a buffer of a new size.
|
||||
* The size of the buffer associated with the Wayland surface that
|
||||
* displays things will change later, but before the changes in drawing buffer
|
||||
* are propagated to the display buffer.
|
||||
*/
|
||||
void WLSBM_SizeChangeTo(WLSurfaceBufferManager *, jint width, jint height);
|
||||
|
||||
/**
|
||||
* Returns a buffer managed by the WayLand Surface Buffer Manager
|
||||
* that is suitable for drawing on it.
|
||||
* This operation locks the buffer such that any subsequent attempt
|
||||
* will wait (on a mutex) for the buffer to be returned back to
|
||||
* the manager with WLSBM_BufferReturn().
|
||||
*/
|
||||
WLDrawBuffer* WLSBM_BufferAcquireForDrawing(WLSurfaceBufferManager *);
|
||||
|
||||
/**
|
||||
* Releases the drawing buffer to the WayLand Surface Buffer Manager
|
||||
* so that the changed areas can be sent to Wayland for displaying.
|
||||
* Unlocks the buffer so that it can be modified again with
|
||||
* WLSBM_BufferAcquireForDrawing().
|
||||
*/
|
||||
void WLSBM_BufferReturn(WLSurfaceBufferManager *, WLDrawBuffer*);
|
||||
|
||||
/**
|
||||
* Records an area of WayLand Surface Buffer that was modified in the buffer.
|
||||
*/
|
||||
void WLSB_Damage(WLDrawBuffer *, jint x, jint y, jint width, jint height);
|
||||
|
||||
/**
|
||||
* Returns a pointer to the memory area where pixels of the WayLand
|
||||
* Surface Buffer are stored.
|
||||
* Each pixel is represented with a uint32_t and has WL_SHM_FORMAT_XRGB8888 format.
|
||||
*/
|
||||
pixel_t * WLSB_DataGet(WLDrawBuffer *);
|
||||
|
||||
#endif
|
||||
264
src/java.desktop/unix/native/common/java2d/wl/WLSurfaceData.c
Normal file
264
src/java.desktop/unix/native/common/java2d/wl/WLSurfaceData.c
Normal file
@@ -0,0 +1,264 @@
|
||||
/*
|
||||
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2022, JetBrains s.r.o.. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
#include <wayland-client.h>
|
||||
|
||||
#include "jni.h"
|
||||
#include "jni_util.h"
|
||||
|
||||
#include "awt.h"
|
||||
#include "Trace.h"
|
||||
#include "WLSurfaceData.h"
|
||||
#include "WLBuffers.h"
|
||||
|
||||
struct WLSDOps {
|
||||
SurfaceDataOps sdOps;
|
||||
WLSurfaceBufferManager * bufferManager;
|
||||
pthread_mutex_t lock;
|
||||
};
|
||||
|
||||
void
|
||||
logWSDOp(char* str, void* p, jint lockFlags)
|
||||
{
|
||||
J2dTrace2(J2D_TRACE_INFO, "%s: %p, ", str, p);
|
||||
J2dTrace7(J2D_TRACE_INFO, "[%c%c%c%c%c%c%c]",
|
||||
(lockFlags&SD_LOCK_READ) ? 'R' : '.',
|
||||
(lockFlags&SD_LOCK_WRITE) ? 'W' : '.',
|
||||
(lockFlags&SD_LOCK_LUT) ? 'L' : '.',
|
||||
(lockFlags&SD_LOCK_INVCOLOR) ? 'C' : '.',
|
||||
(lockFlags&SD_LOCK_INVGRAY) ? 'G' : '.',
|
||||
(lockFlags&SD_LOCK_FASTEST) ? 'F' : '.',
|
||||
(lockFlags&SD_LOCK_PARTIAL) ? 'P' : '.');
|
||||
J2dTrace(J2D_TRACE_INFO, "\n");
|
||||
}
|
||||
|
||||
typedef struct WLSDPrivate {
|
||||
jint lockFlags;
|
||||
WLDrawBuffer * wlBuffer;
|
||||
} WLSDPrivate;
|
||||
|
||||
JNIEXPORT WLSDOps * JNICALL
|
||||
WLSurfaceData_GetOps(JNIEnv *env, jobject sData)
|
||||
{
|
||||
#ifdef HEADLESS
|
||||
return NULL;
|
||||
#else
|
||||
SurfaceDataOps *ops = SurfaceData_GetOps(env, sData);
|
||||
if (ops == NULL) {
|
||||
SurfaceData_ThrowInvalidPipeException(env, "not an valid WLSurfaceData");
|
||||
}
|
||||
return (WLSDOps *) ops;
|
||||
#endif /* !HEADLESS */
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_sun_java2d_wl_WLSurfaceData_assignSurface(JNIEnv *env, jobject wsd,
|
||||
jlong wlSurfacePtr)
|
||||
{
|
||||
#ifndef HEADLESS
|
||||
J2dTrace(J2D_TRACE_INFO, "WLSurfaceData_assignSurface\n");
|
||||
WLSDOps *wsdo = (WLSDOps*)SurfaceData_GetOps(env, wsd);
|
||||
if (wsdo == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
WLSBM_SurfaceAssign(wsdo->bufferManager, (struct wl_surface*)jlong_to_ptr(wlSurfacePtr));
|
||||
#endif /* !HEADLESS */
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_sun_java2d_wl_WLSurfaceData_commitToServer(JNIEnv *env, jobject wsd)
|
||||
{
|
||||
#ifndef HEADLESS
|
||||
J2dTrace(J2D_TRACE_INFO, "WLSurfaceData_commitToServer\n");
|
||||
WLSDOps *wsdo = (WLSDOps*)SurfaceData_GetOps(env, wsd);
|
||||
if (wsdo == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
WLSBM_SurfaceCommit(wsdo->bufferManager);
|
||||
#endif /* !HEADLESS */
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_sun_java2d_wl_WLSurfaceData_revalidate(JNIEnv *env, jobject wsd,
|
||||
jint width, jint height)
|
||||
{
|
||||
#ifndef HEADLESS
|
||||
J2dTrace2(J2D_TRACE_INFO, "WLSurfaceData_revalidate to size %d x %d\n", width, height);
|
||||
WLSDOps *wsdo = (WLSDOps*)SurfaceData_GetOps(env, wsd);
|
||||
if (wsdo == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
WLSBM_SizeChangeTo(wsdo->bufferManager, width, height);
|
||||
#endif /* !HEADLESS */
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the implementation of the general surface LockFunc defined in
|
||||
* SurfaceData.h.
|
||||
*/
|
||||
jint
|
||||
WLSD_Lock(JNIEnv *env,
|
||||
SurfaceDataOps *ops,
|
||||
SurfaceDataRasInfo *pRasInfo,
|
||||
jint lockflags)
|
||||
{
|
||||
#ifndef HEADLESS
|
||||
WLSDOps *wlso = (WLSDOps*)ops;
|
||||
logWSDOp("WLSD_Lock", wlso, lockflags);
|
||||
|
||||
pthread_mutex_lock(&wlso->lock);
|
||||
WLSDPrivate *priv = (WLSDPrivate *) &(pRasInfo->priv);
|
||||
priv->lockFlags = lockflags;
|
||||
priv->wlBuffer = WLSBM_BufferAcquireForDrawing(wlso->bufferManager);
|
||||
|
||||
J2dTrace4(J2D_TRACE_INFO, "WLSD_Lock() at %d, %d for %dx%d\n",
|
||||
pRasInfo->bounds.x1, pRasInfo->bounds.y1,
|
||||
pRasInfo->bounds.x2 - pRasInfo->bounds.x1,
|
||||
pRasInfo->bounds.y2 - pRasInfo->bounds.y1);
|
||||
SurfaceData_IntersectBoundsXYWH(&pRasInfo->bounds,
|
||||
0,
|
||||
0,
|
||||
WLSBM_WidthGet(wlso->bufferManager),
|
||||
WLSBM_HeightGet(wlso->bufferManager));
|
||||
#endif
|
||||
return SD_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
WLSD_GetRasInfo(JNIEnv *env,
|
||||
SurfaceDataOps *ops,
|
||||
SurfaceDataRasInfo *pRasInfo)
|
||||
{
|
||||
#ifndef HEADLESS
|
||||
WLSDPrivate *priv = (WLSDPrivate *) &(pRasInfo->priv);
|
||||
WLSDOps *wlso = (WLSDOps*)ops;
|
||||
logWSDOp("WLSD_GetRasInfo", wlso, priv->lockFlags);
|
||||
|
||||
if (priv->lockFlags & SD_LOCK_RD_WR) {
|
||||
pRasInfo->rasBase = WLSB_DataGet(priv->wlBuffer);
|
||||
pRasInfo->pixelStride = sizeof(pixel_t);
|
||||
pRasInfo->pixelBitOffset = 0;
|
||||
pRasInfo->scanStride = sizeof(pixel_t) * WLSBM_WidthGet(wlso->bufferManager);
|
||||
} else {
|
||||
pRasInfo->rasBase = NULL;
|
||||
}
|
||||
|
||||
pRasInfo->lutBase = NULL;
|
||||
pRasInfo->invColorTable = NULL;
|
||||
pRasInfo->redErrTable = NULL;
|
||||
pRasInfo->grnErrTable = NULL;
|
||||
pRasInfo->bluErrTable = NULL;
|
||||
pRasInfo->invGrayTable = NULL;
|
||||
|
||||
if (priv->lockFlags & SD_LOCK_WRITE) {
|
||||
WLSB_Damage(priv->wlBuffer, pRasInfo->bounds.x1, pRasInfo->bounds.y1,
|
||||
pRasInfo->bounds.x2 - pRasInfo->bounds.x1, pRasInfo->bounds.y2 - pRasInfo->bounds.y1);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
WLSD_Unlock(JNIEnv *env,
|
||||
SurfaceDataOps *ops,
|
||||
SurfaceDataRasInfo *pRasInfo)
|
||||
{
|
||||
#ifndef HEADLESS
|
||||
WLSDOps *wsdo = (WLSDOps*)ops;
|
||||
J2dTrace1(J2D_TRACE_INFO, "WLSD_Unlock: %p\n", wsdo);
|
||||
WLSDPrivate *priv = (WLSDPrivate *) &(pRasInfo->priv);
|
||||
WLSBM_BufferReturn(wsdo->bufferManager, priv->wlBuffer);
|
||||
pthread_mutex_unlock(&wsdo->lock);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
WLSD_Dispose(JNIEnv *env, SurfaceDataOps *ops)
|
||||
{
|
||||
#ifndef HEADLESS
|
||||
/* ops is assumed non-null as it is checked in SurfaceData_DisposeOps */
|
||||
J2dTrace1(J2D_TRACE_INFO, "WLSD_Dispose %p\n", ops);
|
||||
WLSDOps *wsdo = (WLSDOps*)ops;
|
||||
|
||||
// No Wayland event handlers should be able to run while this method
|
||||
// runs. Those handlers may retain a reference to the buffer manager
|
||||
// and therefore must be cancelled before that reference becomes stale.
|
||||
AWT_LOCK();
|
||||
WLSBM_Destroy(wsdo->bufferManager);
|
||||
wsdo->bufferManager = NULL;
|
||||
AWT_NOFLUSH_UNLOCK();
|
||||
|
||||
pthread_mutex_destroy(&wsdo->lock);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_java2d_wl_WLSurfaceData
|
||||
* Method: initOps
|
||||
* Signature: (Ljava/lang/Object;Ljava/lang/Object;I)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL
|
||||
Java_sun_java2d_wl_WLSurfaceData_initOps(JNIEnv *env, jobject wsd,
|
||||
jint width,
|
||||
jint height,
|
||||
jint backgroundRGB)
|
||||
{
|
||||
#ifndef HEADLESS
|
||||
|
||||
WLSDOps *wsdo = (WLSDOps*)SurfaceData_InitOps(env, wsd, sizeof(WLSDOps));
|
||||
J2dTrace1(J2D_TRACE_INFO, "WLSurfaceData_initOps: %p\n", wsdo);
|
||||
jboolean hasException;
|
||||
if (wsdo == NULL) {
|
||||
JNU_ThrowOutOfMemoryError(env, "Initialization of SurfaceData failed.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (width <= 0) {
|
||||
width = 1;
|
||||
}
|
||||
if (height <= 0) {
|
||||
height = 1;
|
||||
}
|
||||
|
||||
wsdo->sdOps.Lock = WLSD_Lock;
|
||||
wsdo->sdOps.Unlock = WLSD_Unlock;
|
||||
wsdo->sdOps.GetRasInfo = WLSD_GetRasInfo;
|
||||
wsdo->sdOps.Dispose = WLSD_Dispose;
|
||||
wsdo->bufferManager = WLSBM_Create(width, height, backgroundRGB);
|
||||
pthread_mutexattr_t attr;
|
||||
pthread_mutexattr_init(&attr);
|
||||
// Recursive mutex is required because blit can be done with both source
|
||||
// and destination being the same surface (during scrolling, for example).
|
||||
// So WLSD_Lock() should be able to lock the same surface twice in a row.
|
||||
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE_NP);
|
||||
pthread_mutex_init(&wsdo->lock, &attr);
|
||||
#endif /* !HEADLESS */
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2022, JetBrains s.r.o.. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
#include "SurfaceData.h"
|
||||
|
||||
typedef struct WLSDOps WLSDOps;
|
||||
|
||||
/*
|
||||
* This function returns a pointer to a native WLSDOps structure
|
||||
* for accessing the indicated WL SurfaceData Java object. It
|
||||
* verifies that the indicated SurfaceData object is an instance
|
||||
* of WLSurfaceData before returning and will return NULL if the
|
||||
* wrong SurfaceData object is being accessed. This function will
|
||||
* throw the appropriate Java exception if it returns NULL so that
|
||||
* the caller can simply return.
|
||||
*
|
||||
* Note to callers:
|
||||
* This function uses JNI methods so it is important that the
|
||||
* caller not have any outstanding GetPrimitiveArrayCritical or
|
||||
* GetStringCritical locks which have not been released.
|
||||
*
|
||||
* The caller may continue to use JNI methods after this method
|
||||
* is called since this function will not leave any outstanding
|
||||
* JNI Critical locks unreleased.
|
||||
*/
|
||||
JNIEXPORT WLSDOps * JNICALL
|
||||
WLSurfaceData_GetOps(JNIEnv *env, jobject sData);
|
||||
@@ -31,7 +31,7 @@
|
||||
#include <jni_util.h>
|
||||
#include <jvm.h>
|
||||
#include "gdefs.h"
|
||||
|
||||
#include "sun_awt_PlatformGraphicsInfo.h"
|
||||
#include <sys/param.h>
|
||||
#include <sys/utsname.h>
|
||||
|
||||
@@ -79,6 +79,34 @@ JNIEXPORT jboolean JNICALL AWTIsHeadless() {
|
||||
return isHeadless;
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL AWTGetToolkitID() {
|
||||
static JNIEnv *env = NULL;
|
||||
static jint toolkitID;
|
||||
jmethodID toolkitIDFn;
|
||||
jclass platformGraphicsInfoClass;
|
||||
|
||||
if (env == NULL) {
|
||||
env = (JNIEnv *)JNU_GetEnv(jvm, JNI_VERSION_1_2);
|
||||
platformGraphicsInfoClass = (*env)->FindClass(env,
|
||||
"sun/awt/PlatformGraphicsInfo");
|
||||
if (platformGraphicsInfoClass == NULL) {
|
||||
return 0;
|
||||
}
|
||||
toolkitIDFn = (*env)->GetStaticMethodID(env,
|
||||
platformGraphicsInfoClass, "getToolkitID", "()I");
|
||||
if (toolkitIDFn == NULL) {
|
||||
return 0;
|
||||
}
|
||||
toolkitID = (*env)->CallStaticBooleanMethod(env, platformGraphicsInfoClass,
|
||||
toolkitIDFn);
|
||||
if ((*env)->ExceptionCheck(env)) {
|
||||
(*env)->ExceptionClear(env);
|
||||
return JNI_TRUE;
|
||||
}
|
||||
}
|
||||
return toolkitID;
|
||||
}
|
||||
|
||||
#define CHECK_EXCEPTION_FATAL(env, message) \
|
||||
if ((*env)->ExceptionCheck(env)) { \
|
||||
(*env)->ExceptionClear(env); \
|
||||
@@ -94,6 +122,7 @@ JNIEXPORT jboolean JNICALL AWTIsHeadless() {
|
||||
#define DEFAULT_PATH LWAWT_PATH
|
||||
#else
|
||||
#define XAWT_PATH "/libawt_xawt.so"
|
||||
#define WLAWT_PATH "/libawt_wlawt.so"
|
||||
#define DEFAULT_PATH XAWT_PATH
|
||||
#define HEADLESS_PATH "/libawt_headless.so"
|
||||
#endif
|
||||
@@ -109,6 +138,7 @@ AWT_OnLoad(JavaVM *vm, void *reserved)
|
||||
struct utsname name;
|
||||
JNIEnv *env = (JNIEnv *)JNU_GetEnv(vm, JNI_VERSION_1_2);
|
||||
void *v;
|
||||
jint tkID = 0;
|
||||
|
||||
if (awtHandle != NULL) {
|
||||
/* Avoid several loading attempts */
|
||||
@@ -128,10 +158,16 @@ AWT_OnLoad(JavaVM *vm, void *reserved)
|
||||
* loading appropriate awt library, i.e. libawt_xawt or libawt_headless
|
||||
*/
|
||||
|
||||
tkID = AWTGetToolkitID();
|
||||
|
||||
#ifdef MACOSX
|
||||
tk = LWAWT_PATH;
|
||||
#else
|
||||
tk = XAWT_PATH;
|
||||
if (tkID == sun_awt_PlatformGraphicsInfo_TK_WAYLAND) {
|
||||
tk = WLAWT_PATH;
|
||||
} else {
|
||||
tk = XAWT_PATH;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef MACOSX
|
||||
|
||||
503
src/java.desktop/unix/native/libawt_wlawt/WLComponentPeer.c
Normal file
503
src/java.desktop/unix/native/libawt_wlawt/WLComponentPeer.c
Normal file
@@ -0,0 +1,503 @@
|
||||
/*
|
||||
* Copyright (c) 2021, JetBrains s.r.o.. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <jni.h>
|
||||
#include <Trace.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "jni_util.h"
|
||||
#include "WLToolkit.h"
|
||||
#include "WLRobotPeer.h"
|
||||
#include "WLGraphicsEnvironment.h"
|
||||
|
||||
#ifdef WAKEFIELD_ROBOT
|
||||
#include "wakefield-client-protocol.h"
|
||||
#endif
|
||||
|
||||
static jfieldID nativePtrID;
|
||||
static jmethodID postWindowClosingMID;
|
||||
static jmethodID notifyConfiguredMID;
|
||||
static jmethodID notifyEnteredOutputMID;
|
||||
static jmethodID notifyLeftOutputMID;
|
||||
|
||||
struct WLFrame {
|
||||
jobject nativeFramePeer; // weak reference
|
||||
struct wl_surface *wl_surface;
|
||||
struct xdg_surface *xdg_surface;
|
||||
struct WLFrame *parent;
|
||||
struct xdg_positioner *xdg_positioner;
|
||||
jboolean toplevel;
|
||||
union {
|
||||
struct xdg_toplevel *xdg_toplevel;
|
||||
struct xdg_popup *xdg_popup;
|
||||
};
|
||||
jboolean configuredPending;
|
||||
int32_t configuredWidth;
|
||||
int32_t configuredHeight;
|
||||
jboolean configuredActive;
|
||||
jboolean configuredMaximized;
|
||||
};
|
||||
|
||||
static void
|
||||
xdg_surface_configure(void *data,
|
||||
struct xdg_surface *xdg_surface,
|
||||
uint32_t serial)
|
||||
{
|
||||
xdg_surface_ack_configure(xdg_surface, serial);
|
||||
|
||||
struct WLFrame *wlFrame = (struct WLFrame*)data;
|
||||
assert(wlFrame);
|
||||
|
||||
if (wlFrame->configuredPending) {
|
||||
wlFrame->configuredPending = JNI_FALSE;
|
||||
|
||||
JNIEnv *env = getEnv();
|
||||
const jobject nativeFramePeer = (*env)->NewLocalRef(env, wlFrame->nativeFramePeer);
|
||||
if (nativeFramePeer) {
|
||||
(*env)->CallVoidMethod(env, nativeFramePeer, notifyConfiguredMID,
|
||||
wlFrame->configuredWidth, wlFrame->configuredHeight,
|
||||
wlFrame->configuredActive, wlFrame->configuredMaximized);
|
||||
(*env)->DeleteLocalRef(env, nativeFramePeer);
|
||||
JNU_CHECK_EXCEPTION(env);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
wl_surface_entered_output(void *data,
|
||||
struct wl_surface *wl_surface,
|
||||
struct wl_output *output)
|
||||
{
|
||||
J2dTrace2(J2D_TRACE_INFO, "wl_surface %p entered output %p\n", wl_surface, output);
|
||||
struct WLFrame *wlFrame = (struct WLFrame*) data;
|
||||
uint32_t wlOutputID = WLOutputID(output);
|
||||
if (wlOutputID == 0) return;
|
||||
|
||||
JNIEnv *env = getEnv();
|
||||
const jobject nativeFramePeer = (*env)->NewLocalRef(env, wlFrame->nativeFramePeer);
|
||||
if (nativeFramePeer) {
|
||||
(*env)->CallVoidMethod(env, nativeFramePeer, notifyEnteredOutputMID, wlOutputID);
|
||||
(*env)->DeleteLocalRef(env, nativeFramePeer);
|
||||
JNU_CHECK_EXCEPTION(env);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
wl_surface_left_output(void *data,
|
||||
struct wl_surface *wl_surface,
|
||||
struct wl_output *output)
|
||||
{
|
||||
J2dTrace2(J2D_TRACE_INFO, "wl_surface %p left output %p\n", wl_surface, output);
|
||||
struct WLFrame *wlFrame = (struct WLFrame*) data;
|
||||
uint32_t wlOutputID = WLOutputID(output);
|
||||
if (wlOutputID == 0) return;
|
||||
|
||||
JNIEnv *env = getEnv();
|
||||
const jobject nativeFramePeer = (*env)->NewLocalRef(env, wlFrame->nativeFramePeer);
|
||||
if (nativeFramePeer) {
|
||||
(*env)->CallVoidMethod(env, nativeFramePeer, notifyLeftOutputMID, wlOutputID);
|
||||
(*env)->DeleteLocalRef(env, nativeFramePeer);
|
||||
JNU_CHECK_EXCEPTION(env);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static const struct xdg_surface_listener xdg_surface_listener = {
|
||||
.configure = xdg_surface_configure,
|
||||
};
|
||||
|
||||
static const struct wl_surface_listener wl_surface_listener = {
|
||||
.enter = wl_surface_entered_output,
|
||||
.leave = wl_surface_left_output
|
||||
};
|
||||
|
||||
static void
|
||||
xdg_toplevel_configure(void *data,
|
||||
struct xdg_toplevel *xdg_toplevel,
|
||||
int32_t width,
|
||||
int32_t height,
|
||||
struct wl_array *states)
|
||||
{
|
||||
J2dTrace3(J2D_TRACE_INFO, "WLComponentPeer: xdg_toplevel_configure(%p, %d, %d)\n",
|
||||
xdg_toplevel, width, height);
|
||||
|
||||
struct WLFrame *wlFrame = (struct WLFrame*)data;
|
||||
assert(wlFrame);
|
||||
|
||||
jboolean active = JNI_FALSE;
|
||||
jboolean maximized = JNI_FALSE;
|
||||
uint32_t *p;
|
||||
wl_array_for_each(p, states) {
|
||||
uint32_t state = *p;
|
||||
switch (state) {
|
||||
case XDG_TOPLEVEL_STATE_ACTIVATED:
|
||||
active = JNI_TRUE;
|
||||
break;
|
||||
case XDG_TOPLEVEL_STATE_FULLSCREEN:
|
||||
break;
|
||||
case XDG_TOPLEVEL_STATE_MAXIMIZED:
|
||||
maximized = JNI_TRUE;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
wlFrame->configuredPending = JNI_TRUE;
|
||||
wlFrame->configuredWidth = width;
|
||||
wlFrame->configuredHeight = height;
|
||||
wlFrame->configuredActive = active;
|
||||
wlFrame->configuredMaximized = maximized;
|
||||
}
|
||||
|
||||
static void
|
||||
xdg_popup_configure(void *data,
|
||||
struct xdg_popup *xdg_popup,
|
||||
int32_t x,
|
||||
int32_t y,
|
||||
int32_t width,
|
||||
int32_t height)
|
||||
{
|
||||
J2dTrace5(J2D_TRACE_INFO, "WLComponentPeer: xdg_popup_configure(%p, %d, %d, %d, %d)\n",
|
||||
xdg_popup, x, y, width, height);
|
||||
|
||||
struct WLFrame *wlFrame = (struct WLFrame*)data;
|
||||
assert(wlFrame);
|
||||
|
||||
wlFrame->configuredPending = JNI_TRUE;
|
||||
wlFrame->configuredWidth = width;
|
||||
wlFrame->configuredHeight = height;
|
||||
}
|
||||
|
||||
static void
|
||||
xdg_toplevel_close(void *data,
|
||||
struct xdg_toplevel *xdg_toplevel)
|
||||
{
|
||||
struct WLFrame *frame = (struct WLFrame *) data;
|
||||
JNIEnv *env = getEnv();
|
||||
const jobject nativeFramePeer = (*env)->NewLocalRef(env, frame->nativeFramePeer);
|
||||
if (nativeFramePeer) {
|
||||
(*env)->CallVoidMethod(env, nativeFramePeer, postWindowClosingMID);
|
||||
(*env)->DeleteLocalRef(env, nativeFramePeer);
|
||||
JNU_CHECK_EXCEPTION(env);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
xdg_popup_done(void *data,
|
||||
struct xdg_popup *xdg_popup)
|
||||
{
|
||||
J2dTrace1(J2D_TRACE_INFO, "WLComponentPeer: xdg_popup_done(%p)\n", xdg_popup);
|
||||
// TODO
|
||||
}
|
||||
|
||||
static const struct xdg_toplevel_listener xdg_toplevel_listener = {
|
||||
.configure = xdg_toplevel_configure,
|
||||
.close = xdg_toplevel_close,
|
||||
};
|
||||
|
||||
static const struct xdg_popup_listener xdg_popup_listener = {
|
||||
.configure = xdg_popup_configure,
|
||||
.popup_done = xdg_popup_done,
|
||||
};
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_sun_awt_wl_WLComponentPeer_initIDs
|
||||
(JNIEnv *env, jclass clazz)
|
||||
{
|
||||
CHECK_NULL(nativePtrID = (*env)->GetFieldID(env, clazz, "nativePtr", "J"));
|
||||
CHECK_NULL_THROW_IE(env,
|
||||
notifyConfiguredMID = (*env)->GetMethodID(env, clazz, "notifyConfigured", "(IIZZ)V"),
|
||||
"Failed to find method WLComponentPeer.notifyConfigured");
|
||||
CHECK_NULL_THROW_IE(env,
|
||||
notifyEnteredOutputMID = (*env)->GetMethodID(env, clazz, "notifyEnteredOutput", "(I)V"),
|
||||
"Failed to find method WLComponentPeer.notifyEnteredOutput");
|
||||
CHECK_NULL_THROW_IE(env,
|
||||
notifyLeftOutputMID = (*env)->GetMethodID(env, clazz, "notifyLeftOutput", "(I)V"),
|
||||
"Failed to find method WLComponentPeer.notifyLeftOutput");
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_sun_awt_wl_WLDecoratedPeer_initIDs
|
||||
(JNIEnv *env, jclass clazz)
|
||||
{
|
||||
CHECK_NULL_THROW_IE(env,
|
||||
postWindowClosingMID = (*env)->GetMethodID(env, clazz, "postWindowClosing", "()V"),
|
||||
"Failed to find method WLDecoratedPeer.postWindowClosing");
|
||||
}
|
||||
|
||||
JNIEXPORT jlong JNICALL
|
||||
Java_sun_awt_wl_WLComponentPeer_nativeCreateFrame
|
||||
(JNIEnv *env, jobject obj)
|
||||
{
|
||||
struct WLFrame *frame = (struct WLFrame *) calloc(1, sizeof(struct WLFrame));
|
||||
frame->nativeFramePeer = (*env)->NewWeakGlobalRef(env, obj);
|
||||
return (jlong)frame;
|
||||
}
|
||||
|
||||
static void
|
||||
FrameSetTitle
|
||||
(JNIEnv* env, struct WLFrame *frame, jstring title)
|
||||
{
|
||||
if (!frame->xdg_toplevel) return;
|
||||
|
||||
jboolean iscopy = JNI_FALSE;
|
||||
const char *title_c_str = JNU_GetStringPlatformChars(env, title, &iscopy);
|
||||
if (title_c_str) {
|
||||
xdg_toplevel_set_title(frame->xdg_toplevel, title_c_str);
|
||||
if (iscopy) {
|
||||
JNU_ReleaseStringPlatformChars(env, title, title_c_str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
FrameSetAppID
|
||||
(JNIEnv* env, struct WLFrame *frame, jstring appid)
|
||||
{
|
||||
if (!frame->xdg_toplevel) return;
|
||||
|
||||
jboolean iscopy = JNI_FALSE;
|
||||
const char *id_c_str = JNU_GetStringPlatformChars(env, appid, &iscopy);
|
||||
if (id_c_str) {
|
||||
xdg_toplevel_set_app_id(frame->xdg_toplevel, id_c_str);
|
||||
if (iscopy) {
|
||||
JNU_ReleaseStringPlatformChars(env, appid, id_c_str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_sun_awt_wl_WLComponentPeer_nativeSetTitle
|
||||
(JNIEnv *env, jobject obj, jlong ptr, jstring title)
|
||||
{
|
||||
struct WLFrame *frame = jlong_to_ptr(ptr);
|
||||
FrameSetTitle(env, frame, title);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_sun_awt_wl_WLComponentPeer_nativeRequestMinimized
|
||||
(JNIEnv *env, jobject obj, jlong ptr)
|
||||
{
|
||||
struct WLFrame *frame = jlong_to_ptr(ptr);
|
||||
if (frame->xdg_toplevel) {
|
||||
xdg_toplevel_set_minimized(frame->xdg_toplevel);
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_sun_awt_wl_WLComponentPeer_nativeRequestMaximized
|
||||
(JNIEnv *env, jobject obj, jlong ptr)
|
||||
{
|
||||
struct WLFrame *frame = jlong_to_ptr(ptr);
|
||||
if (frame->xdg_toplevel) {
|
||||
xdg_toplevel_set_maximized(frame->xdg_toplevel);
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_sun_awt_wl_WLComponentPeer_nativeRequestUnmaximized
|
||||
(JNIEnv *env, jobject obj, jlong ptr)
|
||||
{
|
||||
struct WLFrame *frame = jlong_to_ptr(ptr);
|
||||
if (frame->xdg_toplevel) {
|
||||
xdg_toplevel_unset_maximized(frame->xdg_toplevel);
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_sun_awt_wl_WLComponentPeer_nativeRequestFullScreen
|
||||
(JNIEnv *env, jobject obj, jlong ptr, jint wlID)
|
||||
{
|
||||
struct WLFrame *frame = jlong_to_ptr(ptr);
|
||||
if (frame->xdg_toplevel) {
|
||||
struct wl_output *wl_output = WLOutputByID((uint32_t)wlID);
|
||||
xdg_toplevel_set_fullscreen(frame->xdg_toplevel, wl_output);
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_sun_awt_wl_WLComponentPeer_nativeRequestUnsetFullScreen
|
||||
(JNIEnv *env, jobject obj, jlong ptr)
|
||||
{
|
||||
struct WLFrame *frame = jlong_to_ptr(ptr);
|
||||
if (frame->xdg_toplevel) {
|
||||
xdg_toplevel_unset_fullscreen(frame->xdg_toplevel);
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_sun_awt_wl_WLComponentPeer_nativeCreateWLSurface
|
||||
(JNIEnv *env, jobject obj, jlong ptr, jlong parentPtr, jboolean isPopup,
|
||||
jint x, jint y, jstring title, jstring appid)
|
||||
{
|
||||
struct WLFrame *frame = (struct WLFrame *) ptr;
|
||||
struct WLFrame *parentFrame = (struct WLFrame*) parentPtr;
|
||||
if (frame->wl_surface) return;
|
||||
frame->wl_surface = wl_compositor_create_surface(wl_compositor);
|
||||
frame->xdg_surface = xdg_wm_base_get_xdg_surface(xdg_wm_base, frame->wl_surface);
|
||||
|
||||
wl_surface_add_listener(frame->wl_surface, &wl_surface_listener, frame);
|
||||
xdg_surface_add_listener(frame->xdg_surface, &xdg_surface_listener, frame);
|
||||
frame->toplevel = !isPopup;
|
||||
if (frame->toplevel) {
|
||||
frame->xdg_toplevel = xdg_surface_get_toplevel(frame->xdg_surface);
|
||||
xdg_toplevel_add_listener(frame->xdg_toplevel, &xdg_toplevel_listener, frame);
|
||||
if (title) {
|
||||
FrameSetTitle(env, frame, title);
|
||||
}
|
||||
if (appid) {
|
||||
FrameSetAppID(env, frame, appid);
|
||||
}
|
||||
if (parentFrame) {
|
||||
xdg_toplevel_set_parent(frame->xdg_toplevel, parentFrame->xdg_toplevel);
|
||||
}
|
||||
} else {
|
||||
assert(parentFrame);
|
||||
struct xdg_positioner *xdg_positioner =
|
||||
xdg_wm_base_create_positioner(xdg_wm_base);
|
||||
xdg_positioner_set_offset(xdg_positioner, x, y);
|
||||
frame->xdg_popup = xdg_surface_get_popup(frame->xdg_surface, parentFrame->xdg_surface, xdg_positioner);
|
||||
xdg_popup_add_listener(frame->xdg_popup, &xdg_popup_listener, frame);
|
||||
}
|
||||
#ifdef WAKEFIELD_ROBOT
|
||||
if (wakefield) {
|
||||
// TODO: this doesn't work quite as expected for some reason
|
||||
wakefield_move_surface(wakefield, frame->wl_surface, x, y);
|
||||
}
|
||||
#endif
|
||||
// From xdg-shell.xml: "After creating a role-specific object and
|
||||
// setting it up, the client must perform an initial commit
|
||||
// without any buffer attached"
|
||||
wl_surface_commit(frame->wl_surface);
|
||||
}
|
||||
|
||||
static void
|
||||
DoHide(struct WLFrame *frame)
|
||||
{
|
||||
if (frame->wl_surface) {
|
||||
if(frame->toplevel) {
|
||||
xdg_toplevel_destroy(frame->xdg_toplevel);
|
||||
} else {
|
||||
xdg_popup_destroy(frame->xdg_popup);
|
||||
}
|
||||
xdg_surface_destroy(frame->xdg_surface);
|
||||
wl_surface_destroy(frame->wl_surface);
|
||||
frame->wl_surface = NULL;
|
||||
frame->xdg_surface = NULL;
|
||||
frame->xdg_toplevel = NULL;
|
||||
frame->xdg_popup = NULL;
|
||||
frame->toplevel = JNI_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_sun_awt_wl_WLComponentPeer_nativeHideFrame
|
||||
(JNIEnv *env, jobject obj, jlong ptr)
|
||||
{
|
||||
struct WLFrame *frame = jlong_to_ptr(ptr);
|
||||
DoHide(frame);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_sun_awt_wl_WLComponentPeer_nativeDisposeFrame
|
||||
(JNIEnv *env, jobject obj, jlong ptr)
|
||||
{
|
||||
struct WLFrame *frame = jlong_to_ptr(ptr);
|
||||
DoHide(frame);
|
||||
(*env)->DeleteWeakGlobalRef(env, frame->nativeFramePeer);
|
||||
free(frame);
|
||||
}
|
||||
|
||||
JNIEXPORT jlong JNICALL Java_sun_awt_wl_WLComponentPeer_getWLSurface
|
||||
(JNIEnv *env, jobject obj, jlong ptr)
|
||||
{
|
||||
return ptr_to_jlong(((struct WLFrame*)jlong_to_ptr(ptr))->wl_surface);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_sun_awt_wl_WLComponentPeer_nativeStartDrag
|
||||
(JNIEnv *env, jobject obj, 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);
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_sun_awt_wl_WLComponentPeer_nativeStartResize
|
||||
(JNIEnv *env, jobject obj, jlong ptr, jint edges)
|
||||
{
|
||||
struct WLFrame *frame = jlong_to_ptr(ptr);
|
||||
if (frame->toplevel && wl_seat) {
|
||||
xdg_toplevel_resize(frame->xdg_toplevel, wl_seat, last_mouse_pressed_serial, edges);
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_sun_awt_wl_WLComponentPeer_nativeSetWindowGeometry
|
||||
(JNIEnv *env, jobject obj, jlong ptr, jint x, jint y, jint width, jint height)
|
||||
{
|
||||
struct WLFrame *frame = jlong_to_ptr(ptr);
|
||||
if (frame->xdg_surface) {
|
||||
xdg_surface_set_window_geometry(frame->xdg_surface, x, y, width, height);
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_sun_awt_wl_WLComponentPeer_nativeSetMinimumSize
|
||||
(JNIEnv *env, jobject obj, jlong ptr, jint width, jint height)
|
||||
{
|
||||
struct WLFrame *frame = jlong_to_ptr(ptr);
|
||||
if (frame->toplevel) {
|
||||
xdg_toplevel_set_min_size(frame->xdg_toplevel, width, height);
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_sun_awt_wl_WLComponentPeer_nativeSetMaximumSize
|
||||
(JNIEnv *env, jobject obj, jlong ptr, jint width, jint height)
|
||||
{
|
||||
struct WLFrame *frame = jlong_to_ptr(ptr);
|
||||
if (frame->toplevel) {
|
||||
xdg_toplevel_set_max_size(frame->xdg_toplevel, width, height);
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_sun_awt_wl_WLComponentPeer_nativeShowWindowMenu
|
||||
(JNIEnv *env, jobject obj, 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);
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_sun_awt_wl_WLComponentPeer_nativeSetBufferScale
|
||||
(JNIEnv *env, jobject obj, jlong ptr, jint scale)
|
||||
{
|
||||
struct WLFrame *frame = jlong_to_ptr(ptr);
|
||||
if (frame->wl_surface) {
|
||||
wl_surface_set_buffer_scale(frame->wl_surface, scale);
|
||||
}
|
||||
}
|
||||
172
src/java.desktop/unix/native/libawt_wlawt/WLCursor.c
Normal file
172
src/java.desktop/unix/native/libawt_wlawt/WLCursor.c
Normal file
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
* Copyright 2022 JetBrains s.r.o.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <jni.h>
|
||||
#include <jni_util.h>
|
||||
|
||||
#include "WLToolkit.h"
|
||||
|
||||
struct WLCursor {
|
||||
struct wl_buffer *buffer;
|
||||
bool managed;
|
||||
int32_t width;
|
||||
int32_t height;
|
||||
int32_t hotspot_x;
|
||||
int32_t hotspot_y;
|
||||
};
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_java_awt_Cursor_initIDs
|
||||
(JNIEnv *env, jclass cls)
|
||||
{
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_java_awt_Cursor_finalizeImpl
|
||||
(JNIEnv *env, jclass clazz, jlong pData)
|
||||
{
|
||||
if (pData != -1) {
|
||||
struct WLCursor *cursor = jlong_to_ptr(pData);
|
||||
if (cursor->managed)
|
||||
wl_buffer_destroy(cursor->buffer);
|
||||
free(cursor);
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT jlong JNICALL Java_sun_awt_wl_WLComponentPeer_nativeGetPredefinedCursor
|
||||
(JNIEnv *env, jclass cls, jstring name)
|
||||
{
|
||||
if (!wl_cursor_theme)
|
||||
return 0;
|
||||
|
||||
jboolean isCopy = JNI_FALSE;
|
||||
const char *name_c_str = JNU_GetStringPlatformChars(env, name, &isCopy);
|
||||
if (!name_c_str)
|
||||
return 0;
|
||||
struct wl_cursor *wl_cursor = wl_cursor_theme_get_cursor(wl_cursor_theme, name_c_str);
|
||||
if (isCopy) {
|
||||
JNU_ReleaseStringPlatformChars(env, name, name_c_str);
|
||||
}
|
||||
|
||||
if (!wl_cursor || !wl_cursor->image_count)
|
||||
return 0;
|
||||
struct wl_cursor_image *wl_cursor_image = wl_cursor->images[0]; // animated cursors aren't currently supported
|
||||
|
||||
struct WLCursor *cursor = (struct WLCursor*) malloc(sizeof(struct WLCursor));
|
||||
if (cursor) {
|
||||
cursor->buffer = wl_cursor_image_get_buffer(wl_cursor_image);
|
||||
cursor->managed = false;
|
||||
cursor->width = wl_cursor_image->width;
|
||||
cursor->height = wl_cursor_image->height;
|
||||
cursor->hotspot_x = wl_cursor_image->hotspot_x;
|
||||
cursor->hotspot_y = wl_cursor_image->hotspot_y;
|
||||
}
|
||||
return ptr_to_jlong(cursor);
|
||||
}
|
||||
|
||||
JNIEXPORT jlong JNICALL Java_sun_awt_wl_WLCustomCursor_nativeCreateCustomCursor
|
||||
(JNIEnv *env, jclass cls, jintArray pixels, jint width, jint height, jint xHotSpot, jint yHotSpot)
|
||||
{
|
||||
int pixelCount = (*env)->GetArrayLength(env, pixels);
|
||||
if (pixelCount == 0 || pixelCount >= 0x20000000 /* byte size won't fit into int32_t */)
|
||||
return 0;
|
||||
int32_t byteSize = pixelCount * 4;
|
||||
jint *sharedBuffer;
|
||||
struct wl_shm_pool *pool = CreateShmPool(byteSize, "customCursor", (void**)&sharedBuffer);
|
||||
if (!pool)
|
||||
return 0;
|
||||
|
||||
(*env)->GetIntArrayRegion(env, pixels, 0, pixelCount, sharedBuffer);
|
||||
#if defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||
// Wayland requires little-endian data
|
||||
for (int i = 0; i < pixelCount; i++) {
|
||||
jint value = sharedBuffer[i];
|
||||
sharedBuffer[i] = (value & 0xFF) << 24 |
|
||||
(value & 0xFF00) << 8 |
|
||||
(value & 0xFF0000) >> 8 |
|
||||
(value & 0xFF000000) >> 24 & 0xFF;
|
||||
}
|
||||
#endif
|
||||
|
||||
struct wl_buffer *buffer = wl_shm_pool_create_buffer(pool, 0, width, height, width * 4, WL_SHM_FORMAT_ARGB8888);
|
||||
wl_shm_pool_destroy(pool);
|
||||
|
||||
struct WLCursor *cursor = (struct WLCursor*) malloc(sizeof(struct WLCursor));
|
||||
if (!cursor) {
|
||||
wl_buffer_destroy(buffer);
|
||||
return 0;
|
||||
}
|
||||
cursor->buffer = buffer;
|
||||
cursor->managed = true;
|
||||
cursor->width = width;
|
||||
cursor->height = height;
|
||||
cursor->hotspot_x = xHotSpot;
|
||||
cursor->hotspot_y = yHotSpot;
|
||||
return ptr_to_jlong(cursor);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_sun_awt_wl_WLComponentPeer_nativeSetCursor
|
||||
(JNIEnv *env, jclass cls, jlong pData)
|
||||
{
|
||||
struct wl_buffer *buffer = NULL;
|
||||
int32_t width = 0;
|
||||
int32_t height = 0;
|
||||
int32_t hotspot_x = 0;
|
||||
int32_t hotspot_y = 0;
|
||||
|
||||
if (pData != -1) {
|
||||
struct WLCursor *cursor = jlong_to_ptr(pData);
|
||||
buffer = cursor->buffer;
|
||||
width = cursor->width;
|
||||
height = cursor->height;
|
||||
hotspot_x = cursor->hotspot_x;
|
||||
hotspot_y = cursor->hotspot_y;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if (buffer != last_buffer) {
|
||||
last_buffer = buffer;
|
||||
wl_surface_attach(wl_cursor_surface, buffer, 0, 0);
|
||||
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, hotspot_y);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,286 @@
|
||||
/*
|
||||
* Copyright (c) 2022, JetBrains s.r.o.. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
#ifndef HEADLESS
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <Trace.h>
|
||||
|
||||
#include "jvm_md.h"
|
||||
#include "jni_util.h"
|
||||
#include "awt.h"
|
||||
#include "WLToolkit.h"
|
||||
#include "VKBase.h"
|
||||
#include "VKWLGraphicsConfig.h"
|
||||
|
||||
typedef struct WLOutput {
|
||||
struct WLOutput * next;
|
||||
|
||||
struct wl_output * wl_output;
|
||||
uint32_t id;
|
||||
|
||||
int32_t x;
|
||||
int32_t y;
|
||||
int32_t width;
|
||||
int32_t height;
|
||||
|
||||
uint32_t subpixel;
|
||||
uint32_t transform;
|
||||
uint32_t scale;
|
||||
|
||||
char * name;
|
||||
} WLOutput;
|
||||
|
||||
static jclass geClass;
|
||||
static jmethodID notifyOutputConfiguredMID;
|
||||
static jmethodID notifyOutputDestroyedMID;
|
||||
static jmethodID getSingleInstanceMID;
|
||||
|
||||
WLOutput * outputList;
|
||||
|
||||
static void
|
||||
wl_output_geometry(
|
||||
void *data,
|
||||
struct wl_output * wl_output,
|
||||
int32_t x,
|
||||
int32_t y,
|
||||
int32_t physical_width,
|
||||
int32_t physical_height,
|
||||
int32_t subpixel,
|
||||
const char *make,
|
||||
const char *model,
|
||||
int32_t transform)
|
||||
{
|
||||
WLOutput *output = data;
|
||||
|
||||
// TODO: there's also a recommended, but unstable interface xdg_output;
|
||||
// we may want to switch to that one day.
|
||||
output->x = x;
|
||||
output->y = y;
|
||||
output->subpixel = subpixel;
|
||||
output->transform = transform;
|
||||
}
|
||||
|
||||
static void
|
||||
wl_output_mode(
|
||||
void *data,
|
||||
struct wl_output *wl_output,
|
||||
uint32_t flags,
|
||||
int32_t width,
|
||||
int32_t height,
|
||||
int32_t refresh)
|
||||
{
|
||||
if (flags & WL_OUTPUT_MODE_CURRENT) {
|
||||
WLOutput *output = data;
|
||||
output->width = width;
|
||||
output->height = height;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
wl_output_name(
|
||||
void *data,
|
||||
struct wl_output *wl_output,
|
||||
const char *name)
|
||||
{
|
||||
WLOutput * output = data;
|
||||
free(output->name);
|
||||
if (name) {
|
||||
output->name = strdup(name);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
wl_output_description(
|
||||
void *data,
|
||||
struct wl_output *wl_output,
|
||||
const char *description)
|
||||
{
|
||||
// intentionally empty
|
||||
}
|
||||
|
||||
static void
|
||||
wl_output_scale(
|
||||
void *data,
|
||||
struct wl_output *wl_output,
|
||||
int32_t factor)
|
||||
{
|
||||
WLOutput * output = data;
|
||||
output->scale = factor;
|
||||
}
|
||||
|
||||
static void
|
||||
wl_output_done(
|
||||
void *data,
|
||||
struct wl_output *wl_output)
|
||||
{
|
||||
WLOutput * output = data;
|
||||
|
||||
JNIEnv *env = getEnv();
|
||||
jobject obj = (*env)->CallStaticObjectMethod(env, geClass, getSingleInstanceMID);
|
||||
CHECK_NULL_THROW_IE(env, obj, "WLGraphicsEnvironment.getSingleInstance() returned null");
|
||||
jstring outputName = output->name ? JNU_NewStringPlatform(env, output->name) : NULL;
|
||||
JNU_CHECK_EXCEPTION(env);
|
||||
(*env)->CallVoidMethod(env, obj, notifyOutputConfiguredMID,
|
||||
outputName,
|
||||
output->id,
|
||||
output->x,
|
||||
output->y,
|
||||
output->width,
|
||||
output->height,
|
||||
(jint)output->subpixel,
|
||||
(jint)output->transform,
|
||||
(jint)output->scale);
|
||||
JNU_CHECK_EXCEPTION(env);
|
||||
}
|
||||
|
||||
struct wl_output_listener wl_output_listener = {
|
||||
.geometry = &wl_output_geometry,
|
||||
.mode = &wl_output_mode,
|
||||
.name = &wl_output_name,
|
||||
.description = &wl_output_description,
|
||||
.done = &wl_output_done,
|
||||
.scale = &wl_output_scale
|
||||
};
|
||||
|
||||
jboolean
|
||||
WLGraphicsEnvironment_initIDs
|
||||
(JNIEnv *env, jclass clazz)
|
||||
{
|
||||
CHECK_NULL_RETURN(
|
||||
geClass = (jclass)(*env)->NewGlobalRef(env, clazz),
|
||||
JNI_FALSE);
|
||||
CHECK_NULL_RETURN(
|
||||
getSingleInstanceMID = (*env)->GetStaticMethodID(env, clazz,
|
||||
"getSingleInstance",
|
||||
"()Lsun/awt/wl/WLGraphicsEnvironment;"),
|
||||
JNI_FALSE);
|
||||
CHECK_NULL_RETURN(
|
||||
notifyOutputConfiguredMID = (*env)->GetMethodID(env, clazz,
|
||||
"notifyOutputConfigured",
|
||||
"(Ljava/lang/String;IIIIIIII)V"),
|
||||
JNI_FALSE);
|
||||
CHECK_NULL_RETURN(
|
||||
notifyOutputDestroyedMID = (*env)->GetMethodID(env, clazz,
|
||||
"notifyOutputDestroyed", "(I)V"),
|
||||
JNI_FALSE);
|
||||
|
||||
return JNI_TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
WLOutputRegister(struct wl_registry *wl_registry, uint32_t id)
|
||||
{
|
||||
WLOutput * output = calloc(1, sizeof(WLOutput));
|
||||
|
||||
output->id = id;
|
||||
output->wl_output = wl_registry_bind(wl_registry, id, &wl_output_interface, 2);
|
||||
wl_output_add_listener(output->wl_output, &wl_output_listener, output);
|
||||
|
||||
output->next = outputList;
|
||||
outputList = output;
|
||||
}
|
||||
|
||||
void
|
||||
WLOutputDeregister(struct wl_registry *wl_registry, uint32_t id)
|
||||
{
|
||||
WLOutput * cur = outputList;
|
||||
WLOutput * prev = NULL;
|
||||
|
||||
while (cur) {
|
||||
if (cur->id == id) {
|
||||
if (prev) {
|
||||
prev->next = cur->next;
|
||||
} else {
|
||||
outputList = cur->next;
|
||||
}
|
||||
wl_output_destroy(cur->wl_output);
|
||||
WLOutput * next = cur->next;
|
||||
free(cur->name);
|
||||
free(cur);
|
||||
cur = next;
|
||||
} else {
|
||||
prev = cur;
|
||||
cur = cur->next;
|
||||
}
|
||||
}
|
||||
|
||||
JNIEnv *env = getEnv();
|
||||
jobject obj = (*env)->CallStaticObjectMethod(env, geClass, getSingleInstanceMID);
|
||||
CHECK_NULL_THROW_IE(env, obj, "WLGraphicsEnvironment.getSingleInstance() returned null");
|
||||
(*env)->CallVoidMethod(env, obj, notifyOutputDestroyedMID, id);
|
||||
JNU_CHECK_EXCEPTION(env);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
WLOutputID(struct wl_output *wlOutput)
|
||||
{
|
||||
for(WLOutput * cur = outputList; cur; cur = cur->next) {
|
||||
if (cur->wl_output == wlOutput) {
|
||||
return cur->id;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct wl_output*
|
||||
WLOutputByID(uint32_t id)
|
||||
{
|
||||
for(WLOutput * cur = outputList; cur; cur = cur->next) {
|
||||
if (cur->id == id) {
|
||||
return cur->wl_output;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_awt_wl_WLGraphicsEnvironment
|
||||
* Method: initVKWL
|
||||
* Signature: ()Z
|
||||
*/
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_sun_awt_wl_WLGraphicsEnvironment_initVKWL(JNIEnv *env, jclass wlge)
|
||||
{
|
||||
jboolean vkwlAvailable = JNI_FALSE;
|
||||
/* TODO: The following sequence lead to uninitialized awt lock
|
||||
BufferedImage.getGraphics()
|
||||
BufferedImage.createGraphics()
|
||||
GraphicsEnvironment.getLocalGraphicsEnvironment()
|
||||
GraphicsEnvironment$LocalGE.createGE()
|
||||
PlatformGraphicsInfo.createGE()
|
||||
WLGraphicsEnvironment.initVKWL()
|
||||
*/
|
||||
//AWT_LOCK();
|
||||
vkwlAvailable = VK_Init();
|
||||
//AWT_UNLOCK();
|
||||
return vkwlAvailable;
|
||||
}
|
||||
#endif // #ifndef HEADLESS
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (c) 2022, JetBrains s.r.o.. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
#include "jni.h"
|
||||
|
||||
jboolean WLGraphicsEnvironment_initIDs(JNIEnv *env, jclass clazz);
|
||||
void WLOutputRegister(struct wl_registry *wl_registry, uint32_t id);
|
||||
void WLOutputDeregister(struct wl_registry *wl_registry, uint32_t id);
|
||||
uint32_t WLOutputID(struct wl_output *wlOutput);
|
||||
struct wl_output* WLOutputByID(uint32_t id);
|
||||
402
src/java.desktop/unix/native/libawt_wlawt/WLRobotPeer.c
Normal file
402
src/java.desktop/unix/native/libawt_wlawt/WLRobotPeer.c
Normal file
@@ -0,0 +1,402 @@
|
||||
/*
|
||||
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2022, JetBrains s.r.o.. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include <Trace.h>
|
||||
#include <jni_util.h>
|
||||
|
||||
#include "sun_awt_wl_WLRobotPeer.h"
|
||||
#include "WLToolkit.h"
|
||||
|
||||
#include "wakefield-client-protocol.h"
|
||||
|
||||
#ifdef WAKEFIELD_ROBOT
|
||||
struct wakefield *wakefield;
|
||||
struct wl_event_queue *robot_queue;
|
||||
#endif
|
||||
|
||||
#ifndef HEADLESS
|
||||
static struct wl_buffer*
|
||||
allocate_buffer(JNIEnv *env, int width, int height, uint32_t **buffer_data, int* size);
|
||||
#endif
|
||||
|
||||
#ifdef WAKEFIELD_ROBOT
|
||||
// These structs are used to transfer data between the thread that made the request
|
||||
// and the thread where the event handler was invoked in a race-free manner.
|
||||
struct pixel_color_request_t {
|
||||
pthread_mutex_t mutex;
|
||||
pthread_cond_t cond;
|
||||
|
||||
bool is_data_available;
|
||||
uint32_t error_code;
|
||||
|
||||
uint32_t rgb;
|
||||
} pixel_color_request;
|
||||
|
||||
struct screen_capture_request_t {
|
||||
pthread_mutex_t mutex;
|
||||
pthread_cond_t cond;
|
||||
|
||||
bool is_data_available;
|
||||
uint32_t error_code;
|
||||
} screen_capture_request;
|
||||
|
||||
struct surface_location_request_t {
|
||||
pthread_mutex_t mutex;
|
||||
pthread_cond_t cond;
|
||||
|
||||
bool is_data_available;
|
||||
uint32_t error_code;
|
||||
|
||||
int32_t x;
|
||||
int32_t y;
|
||||
} surface_location_request;
|
||||
|
||||
#define WAKEFIELD_REQUEST_INIT(r) (r).is_data_available = false
|
||||
|
||||
#define WAKEFIELD_REQUEST_WAIT_START(r) pthread_mutex_lock(&(r).mutex); \
|
||||
while (!(r).is_data_available) { \
|
||||
pthread_cond_wait(&(r).cond, &(r).mutex);\
|
||||
}
|
||||
|
||||
#define WAKEFIELD_REQUEST_WAIT_END(r) pthread_mutex_unlock(&(r).mutex)
|
||||
|
||||
#define WAKEFIELD_EVENT_NOTIFY_START(r) pthread_mutex_lock(&(r).mutex)
|
||||
|
||||
#define WAKEFIELD_EVENT_NOTIFY_END(r) (r).is_data_available = true; \
|
||||
pthread_cond_broadcast(&(r).cond); \
|
||||
pthread_mutex_unlock(&(r).mutex)
|
||||
|
||||
static int
|
||||
init_mutex_and_cond(JNIEnv *env, pthread_mutex_t *mutex, pthread_cond_t *cond)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
rc = pthread_mutex_init(mutex, NULL);
|
||||
if (rc != 0) {
|
||||
JNU_ThrowInternalError(env, "pthread_mutex_init() failed");
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = pthread_cond_init(cond, NULL);
|
||||
if (rc != 0) {
|
||||
JNU_ThrowInternalError(env, "pthread_cond_init() failed");
|
||||
return rc;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void
|
||||
handle_wakefield_error(JNIEnv *env, uint32_t error_code);
|
||||
#endif
|
||||
|
||||
static jclass pointClass; // java.awt.Point
|
||||
static jmethodID pointClassConstrMID; // java.awt.Point(int, int)
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_sun_awt_wl_WLRobotPeer_initIDs(JNIEnv *env, jclass clazz)
|
||||
{
|
||||
#ifdef WAKEFIELD_ROBOT
|
||||
int rc = init_mutex_and_cond(env, &pixel_color_request.mutex, &pixel_color_request.cond);
|
||||
if (rc != 0) return;
|
||||
|
||||
rc = init_mutex_and_cond(env, &screen_capture_request.mutex, &screen_capture_request.cond);
|
||||
if (rc != 0) return;
|
||||
|
||||
rc = init_mutex_and_cond(env, &surface_location_request.mutex, &surface_location_request.cond);
|
||||
if (rc != 0) return;
|
||||
#endif
|
||||
|
||||
const jclass pointClassLocal = (*env)->FindClass(env, "java/awt/Point");
|
||||
if (pointClassLocal == NULL) {
|
||||
JNU_ThrowInternalError(env, "cannot find class java.awt.Point");
|
||||
return;
|
||||
}
|
||||
pointClass = (*env)->NewGlobalRef(env, pointClassLocal); // never free'ed
|
||||
|
||||
pointClassConstrMID = (*env)->GetMethodID(env, pointClass, "<init>", "(II)V");
|
||||
if (pointClassConstrMID == NULL) {
|
||||
JNU_ThrowInternalError(env, "cannot find java.awt.Point(int, int)");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_sun_awt_wl_WLRobotPeer_isRobotExtensionPresentImpl(JNIEnv *env, jclass clazz)
|
||||
{
|
||||
if (!wl_display) {
|
||||
J2dTrace(J2D_TRACE_ERROR, "WLRobotPeer: isRobotExtensionPresent can't work without a Wayland display\n");
|
||||
return JNI_FALSE;
|
||||
}
|
||||
|
||||
#ifdef WAKEFIELD_ROBOT
|
||||
return (wakefield != NULL);
|
||||
#else
|
||||
J2dTrace(J2D_TRACE_ERROR, "WLToolkit: robot extension was not enabled at build time \n");
|
||||
return JNI_FALSE;
|
||||
#endif
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_sun_awt_wl_WLRobotPeer_getRGBPixelImpl(JNIEnv *env, jclass clazz, jint x, jint y)
|
||||
{
|
||||
#ifdef WAKEFIELD_ROBOT
|
||||
if (!wakefield) {
|
||||
JNU_ThrowByName(env, "java/awt/AWTError", "no 'wakefield' protocol extension");
|
||||
return 0;
|
||||
}
|
||||
|
||||
WAKEFIELD_REQUEST_INIT(pixel_color_request);
|
||||
|
||||
wakefield_get_pixel_color(wakefield, x, y);
|
||||
wl_flush_to_server(env); // the event will be delivered on a dedicated thread, see wakefield_pixel_color()
|
||||
|
||||
WAKEFIELD_REQUEST_WAIT_START(pixel_color_request);
|
||||
const uint32_t error_code = pixel_color_request.error_code;
|
||||
const uint32_t rgb = pixel_color_request.rgb;
|
||||
WAKEFIELD_REQUEST_WAIT_END(pixel_color_request);
|
||||
|
||||
if (error_code != WAKEFIELD_ERROR_NO_ERROR) {
|
||||
handle_wakefield_error(env, error_code);
|
||||
return 0;
|
||||
} else {
|
||||
return (jint)rgb;
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
JNIEXPORT jobject JNICALL
|
||||
Java_sun_awt_wl_WLRobotPeer_getLocationOfWLSurfaceImpl
|
||||
(JNIEnv *env, jclass clazz, jlong wlSurfacePtr)
|
||||
{
|
||||
#ifdef WAKEFIELD_ROBOT
|
||||
if (!wakefield) {
|
||||
JNU_ThrowByName(env, "java/awt/AWTError", "no 'wakefield' protocol extension");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
WAKEFIELD_REQUEST_INIT(surface_location_request);
|
||||
|
||||
struct wl_surface * const surface = (struct wl_surface*) wlSurfacePtr;
|
||||
wakefield_get_surface_location(wakefield, surface);
|
||||
wl_flush_to_server(env); // the event will be delivered on a dedicated thread, see wakefield_surface_location()
|
||||
|
||||
WAKEFIELD_REQUEST_WAIT_START(surface_location_request);
|
||||
const uint32_t error_code = surface_location_request.error_code;
|
||||
const int32_t x = surface_location_request.x;
|
||||
const int32_t y = surface_location_request.y;
|
||||
WAKEFIELD_REQUEST_WAIT_END(surface_location_request);
|
||||
|
||||
if (error_code != WAKEFIELD_ERROR_NO_ERROR) {
|
||||
handle_wakefield_error(env, error_code);
|
||||
return NULL;
|
||||
} else {
|
||||
return (*env)->NewObject(env, pointClass, pointClassConstrMID, x, y);
|
||||
}
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_sun_awt_wl_WLRobotPeer_setLocationOfWLSurfaceImpl
|
||||
(JNIEnv *env, jclass clazz, jlong wlSurfacePtr, jint x, jint y)
|
||||
{
|
||||
#ifdef WAKEFIELD_ROBOT
|
||||
if (!wakefield) {
|
||||
JNU_ThrowByName(env, "java/awt/AWTError", "no 'wakefield' protocol extension");
|
||||
return;
|
||||
}
|
||||
|
||||
J2dTrace2(J2D_TRACE_INFO, "WLRobotPeer: sending move_surface request to wakefield %d, %d\n",
|
||||
x, y);
|
||||
|
||||
struct wl_surface * const surface = (struct wl_surface*) wlSurfacePtr;
|
||||
wakefield_move_surface(wakefield, surface, x, y);
|
||||
wl_surface_commit(surface);
|
||||
wl_flush_to_server(env);
|
||||
#endif
|
||||
}
|
||||
|
||||
JNIEXPORT jintArray JNICALL
|
||||
Java_sun_awt_wl_WLRobotPeer_getRGBPixelsImpl
|
||||
(JNIEnv *env, jclass clazz, jint x, jint y, jint width, jint height)
|
||||
{
|
||||
#ifdef WAKEFIELD_ROBOT
|
||||
if (!wakefield) {
|
||||
JNU_ThrowByName(env, "java/awt/AWTError", "no 'wakefield' protocol extension");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint32_t *buffer_data;
|
||||
int buffer_size_in_bytes;
|
||||
struct wl_buffer *buffer = allocate_buffer(env, width, height, &buffer_data, &buffer_size_in_bytes);
|
||||
if (!buffer) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
WAKEFIELD_REQUEST_INIT(screen_capture_request);
|
||||
|
||||
wakefield_capture_create(wakefield, buffer, x, y);
|
||||
wl_display_flush(wl_display); // event will be delivered on EDT, see wakefield_capture_ready()
|
||||
|
||||
WAKEFIELD_REQUEST_WAIT_START(screen_capture_request);
|
||||
const uint32_t error_code = screen_capture_request.error_code;
|
||||
WAKEFIELD_REQUEST_WAIT_END(screen_capture_request);
|
||||
|
||||
jintArray arrayObj = NULL;
|
||||
|
||||
if (error_code != WAKEFIELD_ERROR_NO_ERROR) {
|
||||
handle_wakefield_error(env, error_code);
|
||||
} else {
|
||||
arrayObj = (*env)->NewIntArray(env, buffer_size_in_bytes / 4);
|
||||
if (arrayObj != NULL) {
|
||||
jint *array = (*env)->GetPrimitiveArrayCritical(env, arrayObj, NULL);
|
||||
if (array == NULL) {
|
||||
JNU_ThrowOutOfMemoryError(env, "Wayland robot screen capture");
|
||||
} else {
|
||||
memcpy(array, buffer_data, buffer_size_in_bytes);
|
||||
(*env)->ReleasePrimitiveArrayCritical(env, arrayObj, array, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wl_buffer_destroy(buffer);
|
||||
munmap(buffer_data, buffer_size_in_bytes);
|
||||
return arrayObj;
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef WAKEFIELD_ROBOT
|
||||
|
||||
static void wakefield_surface_location(void *data, struct wakefield *wakefield,
|
||||
struct wl_surface *surface, int32_t x, int32_t y,
|
||||
uint32_t error_code)
|
||||
{
|
||||
J2dTrace3(J2D_TRACE_INFO, "WLRobotPeer: event wakefield_surface_location: coordinates %d, %d (error %d)\n",
|
||||
x, y, error_code);
|
||||
|
||||
WAKEFIELD_EVENT_NOTIFY_START(surface_location_request);
|
||||
surface_location_request.error_code = error_code;
|
||||
surface_location_request.x = x;
|
||||
surface_location_request.y = y;
|
||||
WAKEFIELD_EVENT_NOTIFY_END(surface_location_request);
|
||||
}
|
||||
|
||||
static void wakefield_pixel_color(void *data, struct wakefield *wakefield,
|
||||
int32_t x, int32_t y, uint32_t rgb,
|
||||
uint32_t error_code)
|
||||
{
|
||||
J2dTrace4(J2D_TRACE_INFO, "WLRobotPeer: event wakefield_pixel_color: %d, %d color 0x%08x (error %d)\n",
|
||||
x, y, rgb, error_code);
|
||||
|
||||
WAKEFIELD_EVENT_NOTIFY_START(pixel_color_request);
|
||||
pixel_color_request.error_code = error_code;
|
||||
pixel_color_request.rgb = rgb;
|
||||
WAKEFIELD_EVENT_NOTIFY_END(pixel_color_request);
|
||||
}
|
||||
|
||||
static void wakefield_capture_ready(void *data, struct wakefield *wakefield,
|
||||
struct wl_buffer *buffer,
|
||||
uint32_t error_code)
|
||||
{
|
||||
J2dTrace2(J2D_TRACE_INFO, "WLRobotPeer: event wakefield_capture_ready: buffer %p (error %d)\n",
|
||||
buffer, error_code);
|
||||
|
||||
WAKEFIELD_EVENT_NOTIFY_START(screen_capture_request);
|
||||
screen_capture_request.error_code = error_code;
|
||||
WAKEFIELD_EVENT_NOTIFY_END(screen_capture_request);
|
||||
}
|
||||
|
||||
static void
|
||||
handle_wakefield_error(JNIEnv *env, uint32_t error_code)
|
||||
{
|
||||
J2dTrace1(J2D_TRACE_ERROR, "WLRobotPeer: error code %d\n", error_code);
|
||||
|
||||
switch (error_code) {
|
||||
case WAKEFIELD_ERROR_OUT_OF_MEMORY:
|
||||
JNU_ThrowOutOfMemoryError(env, "Wayland robot");
|
||||
break;
|
||||
|
||||
case WAKEFIELD_ERROR_FORMAT:
|
||||
JNU_ThrowInternalError(env, "Wayland robot unsupported buffer format");
|
||||
break;
|
||||
|
||||
case WAKEFIELD_ERROR_INTERNAL:
|
||||
JNU_ThrowInternalError(env, "Wayland robot");
|
||||
break;
|
||||
|
||||
case WAKEFIELD_ERROR_INVALID_COORDINATES:
|
||||
// not really an error, but a reason to return something default
|
||||
break;
|
||||
|
||||
default:
|
||||
// not all errors warrant an exception
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
struct wakefield_listener wakefield_listener = {
|
||||
.surface_location = wakefield_surface_location,
|
||||
.pixel_color = wakefield_pixel_color,
|
||||
.capture_ready = wakefield_capture_ready
|
||||
};
|
||||
#endif // WAKEFIELD_ROBOT
|
||||
|
||||
#ifndef HEADLESS
|
||||
|
||||
static struct wl_buffer*
|
||||
allocate_buffer(JNIEnv *env, int width, int height, uint32_t **buffer_data, int* size)
|
||||
{
|
||||
const int stride = width * 4;
|
||||
*size = height * stride;
|
||||
*buffer_data = NULL;
|
||||
|
||||
if (*size <= 0) {
|
||||
JNU_ThrowByName(env, "java/awt/AWTError", "buffer size overflow");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct wl_shm_pool *pool = CreateShmPool(*size, "wl_shm_robot", (void**)buffer_data);
|
||||
if (!pool) {
|
||||
JNU_ThrowByName(env, "java/awt/AWTError", "couldn't create shared memory pool");
|
||||
return NULL;
|
||||
}
|
||||
struct wl_buffer *buffer = wl_shm_pool_create_buffer(
|
||||
(struct wl_shm_pool*)pool, 0, width, height, stride, WL_SHM_FORMAT_XRGB8888);
|
||||
wl_shm_pool_destroy(pool); // buffers keep references to the pool, can "destroy" now
|
||||
|
||||
return buffer;
|
||||
}
|
||||
#endif // HEADLESS
|
||||
31
src/java.desktop/unix/native/libawt_wlawt/WLRobotPeer.h
Normal file
31
src/java.desktop/unix/native/libawt_wlawt/WLRobotPeer.h
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2022, JetBrains s.r.o.. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
#ifdef WAKEFIELD_ROBOT
|
||||
extern struct wakefield *wakefield;
|
||||
extern struct wakefield_listener wakefield_listener;
|
||||
extern struct wl_event_queue *robot_queue;
|
||||
#endif
|
||||
1247
src/java.desktop/unix/native/libawt_wlawt/WLToolkit.c
Normal file
1247
src/java.desktop/unix/native/libawt_wlawt/WLToolkit.c
Normal file
File diff suppressed because it is too large
Load Diff
60
src/java.desktop/unix/native/libawt_wlawt/WLToolkit.h
Normal file
60
src/java.desktop/unix/native/libawt_wlawt/WLToolkit.h
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (c) 2021, JetBrains s.r.o.. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
#include <wayland-client.h>
|
||||
#include <wayland-cursor.h>
|
||||
#include "xdg-shell-client-protocol.h"
|
||||
|
||||
#define CHECK_NULL_THROW_OOME_RETURN(env, x, msg, z)\
|
||||
do { \
|
||||
if ((x) == NULL) { \
|
||||
JNU_ThrowOutOfMemoryError((env), (msg));\
|
||||
return (z); \
|
||||
} \
|
||||
} while(0) \
|
||||
|
||||
#define CHECK_NULL_THROW_IE(env, x, msg) \
|
||||
do { \
|
||||
if ((x) == NULL) { \
|
||||
JNU_ThrowInternalError((env), (msg));\
|
||||
return; \
|
||||
} \
|
||||
} while(0) \
|
||||
|
||||
extern struct wl_seat *wl_seat;
|
||||
extern struct wl_display *wl_display;
|
||||
extern struct wl_pointer *wl_pointer;
|
||||
extern struct wl_compositor *wl_compositor;
|
||||
extern struct xdg_wm_base *xdg_wm_base;
|
||||
extern struct wl_cursor_theme *wl_cursor_theme;
|
||||
|
||||
extern uint32_t last_mouse_pressed_serial;
|
||||
extern uint32_t last_pointer_enter_serial;
|
||||
|
||||
JNIEnv *getEnv();
|
||||
|
||||
int wl_flush_to_server(JNIEnv* env);
|
||||
|
||||
struct wl_shm_pool *CreateShmPool(int32_t size, const char *name, void **data);
|
||||
@@ -0,0 +1,73 @@
|
||||
/* Generated by wayland-scanner 1.19.0 */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2022, JetBrains s.r.o.. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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 <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include "wayland-util.h"
|
||||
|
||||
extern const struct wl_interface wl_buffer_interface;
|
||||
extern const struct wl_interface wl_surface_interface;
|
||||
|
||||
static const struct wl_interface *wakefield_types[] = {
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
&wl_surface_interface,
|
||||
NULL,
|
||||
NULL,
|
||||
&wl_surface_interface,
|
||||
&wl_buffer_interface,
|
||||
NULL,
|
||||
NULL,
|
||||
&wl_surface_interface,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
&wl_buffer_interface,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct wl_message wakefield_requests[] = {
|
||||
{ "destroy", "", wakefield_types + 0 },
|
||||
{ "move_surface", "oii", wakefield_types + 4 },
|
||||
{ "get_surface_location", "o", wakefield_types + 7 },
|
||||
{ "get_pixel_color", "ii", wakefield_types + 0 },
|
||||
{ "capture_create", "oii", wakefield_types + 8 },
|
||||
};
|
||||
|
||||
static const struct wl_message wakefield_events[] = {
|
||||
{ "surface_location", "oiiu", wakefield_types + 11 },
|
||||
{ "pixel_color", "iiuu", wakefield_types + 0 },
|
||||
{ "capture_ready", "ou", wakefield_types + 15 },
|
||||
};
|
||||
|
||||
WL_EXPORT const struct wl_interface wakefield_interface = {
|
||||
"wakefield", 1,
|
||||
5, wakefield_requests,
|
||||
3, wakefield_events,
|
||||
};
|
||||
|
||||
@@ -0,0 +1,266 @@
|
||||
/* Generated by wayland-scanner 1.19.0 */
|
||||
|
||||
#ifndef WAKEFIELD_CLIENT_PROTOCOL_H
|
||||
#define WAKEFIELD_CLIENT_PROTOCOL_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include "wayland-client.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @page page_wakefield The wakefield protocol
|
||||
* @section page_ifaces_wakefield Interfaces
|
||||
* - @subpage page_iface_wakefield - provides capabilities necessary to for java.awt.Robot and such
|
||||
* @section page_copyright_wakefield Copyright
|
||||
* <pre>
|
||||
*
|
||||
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2022, JetBrains s.r.o.. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
* </pre>
|
||||
*/
|
||||
struct wakefield;
|
||||
struct wl_buffer;
|
||||
struct wl_surface;
|
||||
|
||||
#ifndef WAKEFIELD_INTERFACE
|
||||
#define WAKEFIELD_INTERFACE
|
||||
/**
|
||||
* @page page_iface_wakefield wakefield
|
||||
* @section page_iface_wakefield_desc Description
|
||||
* @section page_iface_wakefield_api API
|
||||
* See @ref iface_wakefield.
|
||||
*/
|
||||
/**
|
||||
* @defgroup iface_wakefield The wakefield interface
|
||||
*/
|
||||
extern const struct wl_interface wakefield_interface;
|
||||
#endif
|
||||
|
||||
#ifndef WAKEFIELD_ERROR_ENUM
|
||||
#define WAKEFIELD_ERROR_ENUM
|
||||
enum wakefield_error {
|
||||
/**
|
||||
* error code 0 reserved for the absence of error
|
||||
*/
|
||||
WAKEFIELD_ERROR_NO_ERROR = 0,
|
||||
/**
|
||||
* supplied absolute coordinates point outside of any output
|
||||
*/
|
||||
WAKEFIELD_ERROR_INVALID_COORDINATES = 1,
|
||||
/**
|
||||
* the request could not be fulfilled due to memory allocation error
|
||||
*/
|
||||
WAKEFIELD_ERROR_OUT_OF_MEMORY = 2,
|
||||
/**
|
||||
* a generic error code for internal errors
|
||||
*/
|
||||
WAKEFIELD_ERROR_INTERNAL = 3,
|
||||
/**
|
||||
* (temporary?) color cannot be converted to RGB format
|
||||
*/
|
||||
WAKEFIELD_ERROR_FORMAT = 4,
|
||||
};
|
||||
#endif /* WAKEFIELD_ERROR_ENUM */
|
||||
|
||||
/**
|
||||
* @ingroup iface_wakefield
|
||||
* @struct wakefield_listener
|
||||
*/
|
||||
struct wakefield_listener {
|
||||
/**
|
||||
* facilitates implementation of Frame.getLocation()
|
||||
*
|
||||
* This event reveals the absolute coordinates of the surface if
|
||||
* error_code is zero. If error_code is non-zero, (x, y) are
|
||||
* undefined. The surface argument always correspond to that of the
|
||||
* get_surface_location request.
|
||||
*/
|
||||
void (*surface_location)(void *data,
|
||||
struct wakefield *wakefield,
|
||||
struct wl_surface *surface,
|
||||
int32_t x,
|
||||
int32_t y,
|
||||
uint32_t error_code);
|
||||
/**
|
||||
* facilitates implementation of Robot.getPixelColor()
|
||||
*
|
||||
* This event shows the color (24-bit, format r8g8b8) of the
|
||||
* pixel with the given absolute coordinates. The (x, y) arguments
|
||||
* correspond to that of the get_pixel_color request. If error_code
|
||||
* is non-zero, the rgb argument is undefined and the error_code
|
||||
* argument contains a code from the error enum.
|
||||
*/
|
||||
void (*pixel_color)(void *data,
|
||||
struct wakefield *wakefield,
|
||||
int32_t x,
|
||||
int32_t y,
|
||||
uint32_t rgb,
|
||||
uint32_t error_code);
|
||||
/**
|
||||
*/
|
||||
void (*capture_ready)(void *data,
|
||||
struct wakefield *wakefield,
|
||||
struct wl_buffer *buffer,
|
||||
uint32_t error_code);
|
||||
};
|
||||
|
||||
/**
|
||||
* @ingroup iface_wakefield
|
||||
*/
|
||||
static inline int
|
||||
wakefield_add_listener(struct wakefield *wakefield,
|
||||
const struct wakefield_listener *listener, void *data)
|
||||
{
|
||||
return wl_proxy_add_listener((struct wl_proxy *) wakefield,
|
||||
(void (**)(void)) listener, data);
|
||||
}
|
||||
|
||||
#define WAKEFIELD_DESTROY 0
|
||||
#define WAKEFIELD_MOVE_SURFACE 1
|
||||
#define WAKEFIELD_GET_SURFACE_LOCATION 2
|
||||
#define WAKEFIELD_GET_PIXEL_COLOR 3
|
||||
#define WAKEFIELD_CAPTURE_CREATE 4
|
||||
|
||||
/**
|
||||
* @ingroup iface_wakefield
|
||||
*/
|
||||
#define WAKEFIELD_SURFACE_LOCATION_SINCE_VERSION 1
|
||||
/**
|
||||
* @ingroup iface_wakefield
|
||||
*/
|
||||
#define WAKEFIELD_PIXEL_COLOR_SINCE_VERSION 1
|
||||
/**
|
||||
* @ingroup iface_wakefield
|
||||
*/
|
||||
#define WAKEFIELD_CAPTURE_READY_SINCE_VERSION 1
|
||||
|
||||
/**
|
||||
* @ingroup iface_wakefield
|
||||
*/
|
||||
#define WAKEFIELD_DESTROY_SINCE_VERSION 1
|
||||
/**
|
||||
* @ingroup iface_wakefield
|
||||
*/
|
||||
#define WAKEFIELD_MOVE_SURFACE_SINCE_VERSION 1
|
||||
/**
|
||||
* @ingroup iface_wakefield
|
||||
*/
|
||||
#define WAKEFIELD_GET_SURFACE_LOCATION_SINCE_VERSION 1
|
||||
/**
|
||||
* @ingroup iface_wakefield
|
||||
*/
|
||||
#define WAKEFIELD_GET_PIXEL_COLOR_SINCE_VERSION 1
|
||||
/**
|
||||
* @ingroup iface_wakefield
|
||||
*/
|
||||
#define WAKEFIELD_CAPTURE_CREATE_SINCE_VERSION 1
|
||||
|
||||
/** @ingroup iface_wakefield */
|
||||
static inline void
|
||||
wakefield_set_user_data(struct wakefield *wakefield, void *user_data)
|
||||
{
|
||||
wl_proxy_set_user_data((struct wl_proxy *) wakefield, user_data);
|
||||
}
|
||||
|
||||
/** @ingroup iface_wakefield */
|
||||
static inline void *
|
||||
wakefield_get_user_data(struct wakefield *wakefield)
|
||||
{
|
||||
return wl_proxy_get_user_data((struct wl_proxy *) wakefield);
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
wakefield_get_version(struct wakefield *wakefield)
|
||||
{
|
||||
return wl_proxy_get_version((struct wl_proxy *) wakefield);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup iface_wakefield
|
||||
*/
|
||||
static inline void
|
||||
wakefield_destroy(struct wakefield *wakefield)
|
||||
{
|
||||
wl_proxy_marshal((struct wl_proxy *) wakefield,
|
||||
WAKEFIELD_DESTROY);
|
||||
|
||||
wl_proxy_destroy((struct wl_proxy *) wakefield);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup iface_wakefield
|
||||
*
|
||||
* This instructs the window manager to position the given wl_surface
|
||||
* at the given absolute coordinates. The subsequent get_surface_location
|
||||
* request will return these coordinates unless the surface was moved by
|
||||
* a third party.
|
||||
*/
|
||||
static inline void
|
||||
wakefield_move_surface(struct wakefield *wakefield, struct wl_surface *surface, int32_t x, int32_t y)
|
||||
{
|
||||
wl_proxy_marshal((struct wl_proxy *) wakefield,
|
||||
WAKEFIELD_MOVE_SURFACE, surface, x, y);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup iface_wakefield
|
||||
*
|
||||
* This requests a surface_location event for the given surface.
|
||||
*/
|
||||
static inline void
|
||||
wakefield_get_surface_location(struct wakefield *wakefield, struct wl_surface *surface)
|
||||
{
|
||||
wl_proxy_marshal((struct wl_proxy *) wakefield,
|
||||
WAKEFIELD_GET_SURFACE_LOCATION, surface);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup iface_wakefield
|
||||
*
|
||||
* This requests a pixel_color event at the given absolute coordinates.
|
||||
*/
|
||||
static inline void
|
||||
wakefield_get_pixel_color(struct wakefield *wakefield, int32_t x, int32_t y)
|
||||
{
|
||||
wl_proxy_marshal((struct wl_proxy *) wakefield,
|
||||
WAKEFIELD_GET_PIXEL_COLOR, x, y);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ingroup iface_wakefield
|
||||
*/
|
||||
static inline void
|
||||
wakefield_capture_create(struct wakefield *wakefield, struct wl_buffer *buffer, int32_t x, int32_t y)
|
||||
{
|
||||
wl_proxy_marshal((struct wl_proxy *) wakefield,
|
||||
WAKEFIELD_CAPTURE_CREATE, buffer, x, y);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
181
src/java.desktop/unix/native/libawt_wlawt/xdg-shell-protocol.c
Normal file
181
src/java.desktop/unix/native/libawt_wlawt/xdg-shell-protocol.c
Normal file
@@ -0,0 +1,181 @@
|
||||
/* Generated by wayland-scanner 1.18.0 */
|
||||
|
||||
/*
|
||||
* Copyright © 2008-2013 Kristian Høgsberg
|
||||
* Copyright © 2013 Rafael Antognolli
|
||||
* Copyright © 2013 Jasper St. Pierre
|
||||
* Copyright © 2010-2013 Intel Corporation
|
||||
* Copyright © 2015-2017 Samsung Electronics Co., Ltd
|
||||
* Copyright © 2015-2017 Red Hat Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include "wayland-util.h"
|
||||
|
||||
#ifndef __has_attribute
|
||||
# define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */
|
||||
#endif
|
||||
|
||||
#if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4)
|
||||
#define WL_PRIVATE __attribute__ ((visibility("hidden")))
|
||||
#else
|
||||
#define WL_PRIVATE
|
||||
#endif
|
||||
|
||||
extern const struct wl_interface wl_output_interface;
|
||||
extern const struct wl_interface wl_seat_interface;
|
||||
extern const struct wl_interface wl_surface_interface;
|
||||
extern const struct wl_interface xdg_popup_interface;
|
||||
extern const struct wl_interface xdg_positioner_interface;
|
||||
extern const struct wl_interface xdg_surface_interface;
|
||||
extern const struct wl_interface xdg_toplevel_interface;
|
||||
|
||||
static const struct wl_interface *xdg_shell_types[] = {
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
&xdg_positioner_interface,
|
||||
&xdg_surface_interface,
|
||||
&wl_surface_interface,
|
||||
&xdg_toplevel_interface,
|
||||
&xdg_popup_interface,
|
||||
&xdg_surface_interface,
|
||||
&xdg_positioner_interface,
|
||||
&xdg_toplevel_interface,
|
||||
&wl_seat_interface,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
&wl_seat_interface,
|
||||
NULL,
|
||||
&wl_seat_interface,
|
||||
NULL,
|
||||
NULL,
|
||||
&wl_output_interface,
|
||||
&wl_seat_interface,
|
||||
NULL,
|
||||
&xdg_positioner_interface,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct wl_message xdg_wm_base_requests[] = {
|
||||
{ "destroy", "", xdg_shell_types + 0 },
|
||||
{ "create_positioner", "n", xdg_shell_types + 4 },
|
||||
{ "get_xdg_surface", "no", xdg_shell_types + 5 },
|
||||
{ "pong", "u", xdg_shell_types + 0 },
|
||||
};
|
||||
|
||||
static const struct wl_message xdg_wm_base_events[] = {
|
||||
{ "ping", "u", xdg_shell_types + 0 },
|
||||
};
|
||||
|
||||
WL_PRIVATE const struct wl_interface xdg_wm_base_interface = {
|
||||
"xdg_wm_base", 3,
|
||||
4, xdg_wm_base_requests,
|
||||
1, xdg_wm_base_events,
|
||||
};
|
||||
|
||||
static const struct wl_message xdg_positioner_requests[] = {
|
||||
{ "destroy", "", xdg_shell_types + 0 },
|
||||
{ "set_size", "ii", xdg_shell_types + 0 },
|
||||
{ "set_anchor_rect", "iiii", xdg_shell_types + 0 },
|
||||
{ "set_anchor", "u", xdg_shell_types + 0 },
|
||||
{ "set_gravity", "u", xdg_shell_types + 0 },
|
||||
{ "set_constraint_adjustment", "u", xdg_shell_types + 0 },
|
||||
{ "set_offset", "ii", xdg_shell_types + 0 },
|
||||
{ "set_reactive", "3", xdg_shell_types + 0 },
|
||||
{ "set_parent_size", "3ii", xdg_shell_types + 0 },
|
||||
{ "set_parent_configure", "3u", xdg_shell_types + 0 },
|
||||
};
|
||||
|
||||
WL_PRIVATE const struct wl_interface xdg_positioner_interface = {
|
||||
"xdg_positioner", 3,
|
||||
10, xdg_positioner_requests,
|
||||
0, NULL,
|
||||
};
|
||||
|
||||
static const struct wl_message xdg_surface_requests[] = {
|
||||
{ "destroy", "", xdg_shell_types + 0 },
|
||||
{ "get_toplevel", "n", xdg_shell_types + 7 },
|
||||
{ "get_popup", "n?oo", xdg_shell_types + 8 },
|
||||
{ "set_window_geometry", "iiii", xdg_shell_types + 0 },
|
||||
{ "ack_configure", "u", xdg_shell_types + 0 },
|
||||
};
|
||||
|
||||
static const struct wl_message xdg_surface_events[] = {
|
||||
{ "configure", "u", xdg_shell_types + 0 },
|
||||
};
|
||||
|
||||
WL_PRIVATE const struct wl_interface xdg_surface_interface = {
|
||||
"xdg_surface", 3,
|
||||
5, xdg_surface_requests,
|
||||
1, xdg_surface_events,
|
||||
};
|
||||
|
||||
static const struct wl_message xdg_toplevel_requests[] = {
|
||||
{ "destroy", "", xdg_shell_types + 0 },
|
||||
{ "set_parent", "?o", xdg_shell_types + 11 },
|
||||
{ "set_title", "s", xdg_shell_types + 0 },
|
||||
{ "set_app_id", "s", xdg_shell_types + 0 },
|
||||
{ "show_window_menu", "ouii", xdg_shell_types + 12 },
|
||||
{ "move", "ou", xdg_shell_types + 16 },
|
||||
{ "resize", "ouu", xdg_shell_types + 18 },
|
||||
{ "set_max_size", "ii", xdg_shell_types + 0 },
|
||||
{ "set_min_size", "ii", xdg_shell_types + 0 },
|
||||
{ "set_maximized", "", xdg_shell_types + 0 },
|
||||
{ "unset_maximized", "", xdg_shell_types + 0 },
|
||||
{ "set_fullscreen", "?o", xdg_shell_types + 21 },
|
||||
{ "unset_fullscreen", "", xdg_shell_types + 0 },
|
||||
{ "set_minimized", "", xdg_shell_types + 0 },
|
||||
};
|
||||
|
||||
static const struct wl_message xdg_toplevel_events[] = {
|
||||
{ "configure", "iia", xdg_shell_types + 0 },
|
||||
{ "close", "", xdg_shell_types + 0 },
|
||||
};
|
||||
|
||||
WL_PRIVATE const struct wl_interface xdg_toplevel_interface = {
|
||||
"xdg_toplevel", 3,
|
||||
14, xdg_toplevel_requests,
|
||||
2, xdg_toplevel_events,
|
||||
};
|
||||
|
||||
static const struct wl_message xdg_popup_requests[] = {
|
||||
{ "destroy", "", xdg_shell_types + 0 },
|
||||
{ "grab", "ou", xdg_shell_types + 22 },
|
||||
{ "reposition", "3ou", xdg_shell_types + 24 },
|
||||
};
|
||||
|
||||
static const struct wl_message xdg_popup_events[] = {
|
||||
{ "configure", "iiii", xdg_shell_types + 0 },
|
||||
{ "popup_done", "", xdg_shell_types + 0 },
|
||||
{ "repositioned", "3u", xdg_shell_types + 0 },
|
||||
};
|
||||
|
||||
WL_PRIVATE const struct wl_interface xdg_popup_interface = {
|
||||
"xdg_popup", 3,
|
||||
3, xdg_popup_requests,
|
||||
3, xdg_popup_events,
|
||||
};
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <dlfcn.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <jni.h>
|
||||
#include <jni_util.h>
|
||||
#include <jvm.h>
|
||||
#include "gdefs.h"
|
||||
#include "sun_awt_PlatformGraphicsInfo.h"
|
||||
#include <sys/param.h>
|
||||
#include <sys/utsname.h>
|
||||
|
||||
#ifdef AIX
|
||||
#include "porting_aix.h" /* For the 'dladdr' function. */
|
||||
#endif
|
||||
|
||||
#ifndef MACOSX
|
||||
#ifndef STATIC_BUILD
|
||||
|
||||
#define CHECK_EXCEPTION_FATAL(env, message) \
|
||||
if ((*env)->ExceptionCheck(env)) { \
|
||||
(*env)->ExceptionClear(env); \
|
||||
(*env)->FatalError(env, message); \
|
||||
}
|
||||
|
||||
static void *fontmanagerHandle = NULL;
|
||||
|
||||
JNIEXPORT jboolean JNICALL AWTIsHeadless();
|
||||
JNIEXPORT jint JNICALL AWTGetToolkitID();
|
||||
|
||||
jint
|
||||
Fontmanager_OnLoad(JavaVM *vm, void *reserved)
|
||||
{
|
||||
JNIEnv *env = (JNIEnv *)JNU_GetEnv(vm, JNI_VERSION_1_2);
|
||||
|
||||
if (!AWTIsHeadless() && AWTGetToolkitID() == sun_awt_PlatformGraphicsInfo_TK_X11) {
|
||||
|
||||
Dl_info dlinfo;
|
||||
char buf[MAXPATHLEN];
|
||||
int32_t len;
|
||||
char *p, *tk;
|
||||
|
||||
tk = "/libfontmanager_xawt.so";
|
||||
|
||||
/* Get address of this library and the directory containing it. */
|
||||
dladdr((void *)Fontmanager_OnLoad, &dlinfo);
|
||||
realpath((char *)dlinfo.dli_fname, buf);
|
||||
len = strlen(buf);
|
||||
p = strrchr(buf, '/');
|
||||
/* Calculate library name to load */
|
||||
strncpy(p, tk, MAXPATHLEN-len-1);
|
||||
|
||||
jstring jbuf = JNU_NewStringPlatform(env, buf);
|
||||
CHECK_EXCEPTION_FATAL(env, "Could not allocate library name");
|
||||
JNU_CallStaticMethodByName(env, NULL, "java/lang/System", "load",
|
||||
"(Ljava/lang/String;)V",
|
||||
jbuf);
|
||||
fontmanagerHandle = dlopen(buf, RTLD_LAZY | RTLD_GLOBAL);
|
||||
}
|
||||
|
||||
return JNI_VERSION_1_2;
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
DEF_JNI_OnLoad(JavaVM *vm, void *reserved)
|
||||
{
|
||||
return Fontmanager_OnLoad(vm, reserved);
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
@@ -366,8 +366,9 @@ JNIEXPORT jobject JNICALL
|
||||
* leadingY : made-up number, but being compatible with what 1.4.x did
|
||||
* advance : no need to set yMaxLinearAdvanceWidth - it will be zero.
|
||||
*/
|
||||
metrics = (*env)->NewObject(env, sunFontIDs.strikeMetricsClass,
|
||||
sunFontIDs.strikeMetricsCtr,
|
||||
const FontManagerNativeIDs* sunFontIDs = getSunFontIDsPtr(env);
|
||||
metrics = (*env)->NewObject(env, sunFontIDs->strikeMetricsClass,
|
||||
sunFontIDs->strikeMetricsCtr,
|
||||
j0, ay, j0, dy, j1, j0, j0, j1, mx, j0);
|
||||
/* printf("X11 asc=%f dsc=%f adv=%f scale=%f\n", */
|
||||
/* ay, dy, mx, (float)context->scale); */
|
||||
@@ -62,7 +62,7 @@ JNIEXPORT void JNICALL Java_sun_font_X11TextRenderer_doDrawGlyphList
|
||||
SurfaceDataBounds bounds;
|
||||
Region_GetBounds(env, clip, &bounds);
|
||||
|
||||
glyphCount = (*env)->GetIntField(env, glyphlist, sunFontIDs.glyphListLen);
|
||||
glyphCount = (*env)->GetIntField(env, glyphlist, getSunFontIDsPtr(env)->glyphListLen);
|
||||
if ((gbv = setupBlitVector(env, glyphlist, 0, glyphCount)) == NULL) {
|
||||
return;
|
||||
}
|
||||
41
test/jdk/java/awt/Toolkit/Wayland/WaylandToolkit.java
Normal file
41
test/jdk/java/awt/Toolkit/Wayland/WaylandToolkit.java
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2021, JetBrains s.r.o.. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary Check for Wayland toolkit
|
||||
* @run main/othervm -Dawt.toolkit.name=WLToolkit WaylandToolkit
|
||||
* @requires os.family == "linux"
|
||||
*/
|
||||
import java.awt.*;
|
||||
|
||||
public class WaylandToolkit {
|
||||
|
||||
public static void main(String args[]) {
|
||||
Toolkit tk = Toolkit.getDefaultToolkit();
|
||||
if (!"sun.awt.wl.WLToolkit".equals(tk.getClass().getName())) {
|
||||
throw new RuntimeException("WLToolkit not found");
|
||||
}
|
||||
}
|
||||
}
|
||||
174
test/jdk/java/awt/wakefield/ScreenCapture.java
Normal file
174
test/jdk/java/awt/wakefield/ScreenCapture.java
Normal file
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2022, JetBrains s.r.o.. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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 java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import javax.swing.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import javax.imageio.ImageIO;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @key headful
|
||||
* @summary Windows HiDPI support
|
||||
* @requires (os.family == "linux")
|
||||
* @library /test/lib
|
||||
* @build ScreenCapture
|
||||
* @run driver WakefieldTestDriver 1x1400x800 ScreenCapture
|
||||
* @run driver WakefieldTestDriver 2x830x800 ScreenCapture
|
||||
*/
|
||||
|
||||
public class ScreenCapture {
|
||||
private static final Color[] COLORS = {
|
||||
Color.GREEN, Color.BLUE, Color.ORANGE, Color.RED};
|
||||
|
||||
private static volatile ScreenCapture theTest;
|
||||
|
||||
private static JFrame frame;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
runSwing( () -> {
|
||||
frame = new JFrame();
|
||||
frame.setPreferredSize(new Dimension(800, 300));
|
||||
frame.setUndecorated(true);
|
||||
frame.add(new JPanel() {
|
||||
@Override
|
||||
public void paint(Graphics g) {
|
||||
super.paintComponent(g);
|
||||
final int w = getWidth();
|
||||
final int h = getHeight();
|
||||
g.setColor(COLORS[0]);
|
||||
g.fillRect(0, 0, w / 2, h / 2);
|
||||
g.setColor(COLORS[1]);
|
||||
g.fillRect(w / 2, 0, w / 2, h / 2);
|
||||
g.setColor(COLORS[2]);
|
||||
g.fillRect(0, h / 2, w / 2, h / 2);
|
||||
g.setColor(COLORS[3]);
|
||||
g.fillRect(w / 2, h / 2, w / 2, h / 2);
|
||||
}
|
||||
});
|
||||
frame.pack();
|
||||
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
frame.setVisible(true);
|
||||
frame.setBounds(432, 89, 800, 300);
|
||||
});
|
||||
|
||||
final Robot robot = new Robot();
|
||||
robot.waitForIdle();
|
||||
|
||||
final Rectangle rect = frame.getBounds();
|
||||
rect.setLocation(frame.getLocationOnScreen());
|
||||
|
||||
// the windows fades in slowly, need to wait quite a bit in order to get reliable colors
|
||||
robot.delay(1000);
|
||||
|
||||
System.out.println("Creating screen capture of " + rect);
|
||||
final BufferedImage image = robot.createScreenCapture(rect);
|
||||
|
||||
try {
|
||||
checkPixelColor(robot, rect.x + OFFSET, rect.y + OFFSET, COLORS[0]);
|
||||
checkPixelColor(robot, rect.x + rect.width - OFFSET, rect.y + OFFSET, COLORS[1]);
|
||||
checkPixelColor(robot, rect.x + OFFSET, rect.y + rect.height - OFFSET, COLORS[2]);
|
||||
checkPixelColor(robot, rect.x + rect.width - OFFSET, rect.y + rect.height - OFFSET, COLORS[3]);
|
||||
} finally {
|
||||
frame.dispose();
|
||||
}
|
||||
|
||||
final int w = image.getWidth();
|
||||
final int h = image.getHeight();
|
||||
|
||||
if (w != frame.getWidth() || h != frame.getHeight()) {
|
||||
throw new RuntimeException("Wrong image size!");
|
||||
}
|
||||
|
||||
checkRectColor(image, new Rectangle(0, 0, w / 2, h / 2), COLORS[0]);
|
||||
checkRectColor(image, new Rectangle(w / 2, 0, w / 2, h / 2), COLORS[1]);
|
||||
checkRectColor(image, new Rectangle(0, h / 2, w / 2, h / 2), COLORS[2]);
|
||||
checkRectColor(image, new Rectangle(w / 2, h / 2, w / 2, h / 2), COLORS[3]);
|
||||
|
||||
System.exit(0); // temporary
|
||||
}
|
||||
|
||||
private static final int OFFSET = 5;
|
||||
|
||||
static void checkPixelColor(Robot robot, int x, int y, Color expectedColor) {
|
||||
System.out.print("Checking pixel at " + x + ", " + y + " to have color " + expectedColor);
|
||||
final Color actualColor = robot.getPixelColor(x, y);
|
||||
if (!actualColor.equals(expectedColor)) {
|
||||
System.out.println("... Mismatch: found " + actualColor + " instead");
|
||||
throw new RuntimeException("Wrong color of pixel on screen");
|
||||
} else {
|
||||
System.out.println("... OK");
|
||||
}
|
||||
}
|
||||
|
||||
static void checkRectColor(BufferedImage image, Rectangle rect, Color expectedColor) {
|
||||
System.out.println("Checking rectangle " + rect + " to have color " + expectedColor);
|
||||
final Point[] pointsToCheck = new Point[] {
|
||||
new Point(rect.x + OFFSET, rect.y + OFFSET), // top left corner
|
||||
new Point(rect.x + rect.width - OFFSET, rect.y + OFFSET), // top right corner
|
||||
new Point(rect.x + rect.width / 2, rect.y + rect.height / 2), // center
|
||||
new Point(rect.x + OFFSET, rect.y + rect.height - OFFSET), // bottom left corner
|
||||
new Point(rect.x + rect.width - OFFSET, rect.y + rect.height - OFFSET) // bottom right corner
|
||||
};
|
||||
|
||||
for (final var point : pointsToCheck) {
|
||||
System.out.print("Checking color at " + point + " to be equal to " + expectedColor);
|
||||
final int actualColor = image.getRGB(point.x, point.y);
|
||||
if (actualColor != expectedColor.getRGB()) {
|
||||
System.out.println("... Mismatch: found " + new Color(actualColor) + " instead. Check image.png.");
|
||||
try {
|
||||
ImageIO.write(image, "png", new File("image.png"));
|
||||
} catch(IOException e) {
|
||||
System.out.println("failed to save image.png.");
|
||||
e.printStackTrace();
|
||||
}
|
||||
throw new RuntimeException("Wrong image color!");
|
||||
} else {
|
||||
System.out.println("... OK");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
if (frame != null) {
|
||||
frame.dispose();
|
||||
frame = null;
|
||||
}
|
||||
}
|
||||
|
||||
private static void runSwing(Runnable r) {
|
||||
try {
|
||||
SwingUtilities.invokeAndWait(r);
|
||||
} catch (InterruptedException e) {
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
162
test/jdk/java/awt/wakefield/WakefieldTestDriver.java
Normal file
162
test/jdk/java/awt/wakefield/WakefieldTestDriver.java
Normal file
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* 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 jdk.test.lib.process.ProcessTools;
|
||||
import jdk.test.lib.process.OutputAnalyzer;
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.io.IOException;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class WakefieldTestDriver {
|
||||
final static int DEFAULT_NUMBER_OF_SCREENS = 1;
|
||||
final static int DEFAULT_SCREEN_WIDTH = 1280;
|
||||
final static int DEFAULT_SCREEN_HEIGHT = 800;
|
||||
final static int TEST_TIMEOUT_SECONDS = 10;
|
||||
static void usage() {
|
||||
System.out.println(
|
||||
"""
|
||||
WakefieldTestDriver [NxWxH] ClassName [args]
|
||||
where
|
||||
N - number of Weston outputs (screens); defaults to 1
|
||||
W - width of each screen in pixels; defaults to 1280
|
||||
H - height of each screen in pixels; defaults to 800
|
||||
ClassName - class to execute
|
||||
args - arguments to give to the class""");
|
||||
}
|
||||
|
||||
public static void main(String args[]) throws Exception {
|
||||
if (args.length < 1) {
|
||||
usage();
|
||||
throw new RuntimeException("Insufficient arguments to the test driver");
|
||||
}
|
||||
|
||||
checkEnv(System.getenv());
|
||||
|
||||
final List<String> jvmArgs = new ArrayList<String>();
|
||||
jvmArgs.add("-Dawt.toolkit.name=WLToolkit");
|
||||
jvmArgs.add("-Xcheck:jni");
|
||||
|
||||
int nScreens = DEFAULT_NUMBER_OF_SCREENS;
|
||||
int screenWidth = DEFAULT_SCREEN_WIDTH;
|
||||
int screenHeight = DEFAULT_SCREEN_HEIGHT;
|
||||
final String firstArg = args[0];
|
||||
if (Character.isDigit(firstArg.charAt(0))) {
|
||||
try {
|
||||
final int firstXIndex = firstArg.indexOf("x", 0);
|
||||
final int secondXIndex = firstArg.indexOf("x", firstXIndex + 1);
|
||||
nScreens = Integer.valueOf(firstArg.substring(0, firstXIndex));
|
||||
screenWidth = Integer.valueOf(firstArg.substring(firstXIndex + 1, secondXIndex));
|
||||
screenHeight = Integer.valueOf(firstArg.substring(secondXIndex + 1, firstArg.length()));
|
||||
|
||||
for (int i = 1; i < args.length; ++i) {
|
||||
jvmArgs.add(args[i]);
|
||||
}
|
||||
} catch (IndexOutOfBoundsException | NumberFormatException ignored) {
|
||||
usage();
|
||||
throw new RuntimeException("Error parsing the first argument of the test driver");
|
||||
}
|
||||
} else {
|
||||
jvmArgs.addAll(Arrays.asList(args));
|
||||
}
|
||||
|
||||
final String socketName = SOCKET_NAME_PREFIX + ProcessHandle.current().pid();
|
||||
final Process westonProcess = launchWeston(nScreens, screenWidth, screenHeight, socketName);
|
||||
try {
|
||||
System.out.println("Running test with WAYLAND_DISPLAY=" + socketName);
|
||||
final ProcessBuilder pb = ProcessTools.createTestJvm(jvmArgs);
|
||||
final Map<String, String> env = pb.environment();
|
||||
env.put("WAYLAND_DISPLAY", socketName);
|
||||
|
||||
final Process p = pb.start();
|
||||
final OutputAnalyzer output = new OutputAnalyzer(p);
|
||||
final boolean exited = p.waitFor(TEST_TIMEOUT_SECONDS, TimeUnit.SECONDS);
|
||||
if (!exited) p.destroy();
|
||||
System.out.println("Test finished. Output: [[[");
|
||||
System.out.println(output.getOutput());
|
||||
System.out.println("]]]");
|
||||
if (!exited) {
|
||||
throw new RuntimeException("Test timed out after " + TEST_TIMEOUT_SECONDS + " seconds");
|
||||
}
|
||||
|
||||
if (exited && output.getExitValue() != 0) {
|
||||
throw new RuntimeException("Test finished with non-zero exit code");
|
||||
}
|
||||
} finally {
|
||||
if (westonProcess != null) {
|
||||
final OutputAnalyzer westonOutput = new OutputAnalyzer(westonProcess);
|
||||
westonProcess.destroy();
|
||||
|
||||
System.out.println("Weston output: [[[");
|
||||
System.out.println(westonOutput.getOutput());
|
||||
System.out.println("]]]");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void checkEnv(Map<String, String> env) {
|
||||
if (!env.containsKey("DISPLAY")) {
|
||||
throw new RuntimeException("DISPLAY environment variable must be set to run this test driver");
|
||||
}
|
||||
|
||||
if (!env.containsKey("XDG_RUNTIME_DIR")) {
|
||||
throw new RuntimeException("XDG_RUNTIME_DIR environment variable must be set to run this test driver; pass -e:XDG_RUNTIME_DIR to jtreg");
|
||||
}
|
||||
|
||||
if (!env.containsKey("LIBWAKEFIELD")) {
|
||||
throw new RuntimeException("Set LIBWAKEFIELD environment variable to full path to libwakefield.so");
|
||||
}
|
||||
|
||||
if (!Files.exists(Path.of(env.get("LIBWAKEFIELD")))) {
|
||||
throw new RuntimeException("LIBWAKEFIELD (" + env.get("LIBWAKEFIELD") + " does not point to an executable");
|
||||
}
|
||||
}
|
||||
|
||||
static final String SOCKET_NAME_PREFIX = "wakefield-";
|
||||
|
||||
static Process launchWeston(int nScreens, int width, int height, String socketName) throws IOException {
|
||||
final List<String> args = new ArrayList<String>();
|
||||
args.add("weston");
|
||||
args.add("--backend=x11-backend.so");
|
||||
args.add("--socket=" + socketName);
|
||||
args.add("--output-count=" + nScreens);
|
||||
args.add("--width=" + width);
|
||||
args.add("--height=" + height);
|
||||
args.add("--use-pixman");
|
||||
args.add("--idle-time=0");
|
||||
args.add("--logger-scopes=log,wakefield");
|
||||
//args.add("--log=weston-server-log.txt"); // Log to stderr, its easier to manage this way.
|
||||
args.add("--no-config");
|
||||
args.add("--modules=" + System.getenv("LIBWAKEFIELD"));
|
||||
|
||||
System.out.println("Running " + String.join(" ", args));
|
||||
|
||||
return new ProcessBuilder(args).redirectErrorStream(true).start();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user