Compare commits

...

44 Commits

Author SHA1 Message Date
Alexey Ushakov
a0262fae05 Converted vulkan code to C++ 2022-11-16 21:43:05 +01:00
Alexey Ushakov
c49624baf9 Moved vulkan instance initialization logic to WLGraphicsEnvironment 2022-11-16 19:41:28 +01:00
Alexey Ushakov
4729e32f8b Refactored basic java2d classes for vulkan 2022-11-15 20:08:01 +01:00
Alexey Ushakov
bb98aad651 Added basic classes for vulkan rendering pipeline 2022-11-14 22:03:32 +01:00
Maxim Kartashev
e2321b5594 Implemented getColorModel() and createAcceleratedImage()
This is enough to make J2Ddemo and StylePad work
2022-11-11 12:04:24 +03:00
Dmitry Batrak
5813b10e65 maximize/un-maximize improvements
* remove 'roundtrip' calls - they don't seem to be needed after recent changes to paint logic
* remove unneeded lock in WLFramePeer.setState - corresponding code doesn't query or modify any state
* always repaint client decorations on frame state change - it might not be accompanied by size change
* remember the size of frame before maximization, use it on de-maximization, if compositor doesn't propose a size itself
2022-11-09 16:18:28 +03:00
Maxim Kartashev
d121a93cb1 JBR-4918 More bugfixes in Wayland buffers management
Event-driven painting of client decorations.
Smooth window resize.
Transactional commits at AWT and Swing level
based on frame numbers.
2022-11-08 08:40:18 +03:00
Dmitry Batrak
f7638abee2 initialize memory allocated for WLFrame
just in case, to prevent potential usage of uninitialized fields in future
2022-11-02 12:41:08 +03:00
Dmitry Batrak
55b1310c24 support setting state to a window before making it visible, and right afterwards 2022-10-31 12:23:16 +03:00
Maxim Kartashev
bfe03f4bd1 Revert "JBR-4918 More bugfixes in Wayland buffers management"
This reverts commit 15a09a1564.
2022-10-28 13:20:27 +03:00
Maxim Kartashev
15a09a1564 JBR-4918 More bugfixes in Wayland buffers management
Event-driven painting of client decorations.
Smooth window resize.
Transactional commits at AWT and Swing level
based on frame numbers.
2022-10-28 11:28:57 +03:00
Dmitry Batrak
858380c36d fix assertion in WLKeyboardFocusManagerPeer 2022-10-21 18:34:02 +03:00
Dmitry Batrak
a81b44d79d client-side decorations, and some fixes for minimize/maximize window functionality 2022-10-21 16:46:36 +03:00
Dmitry Batrak
7f9aee3c7f make default component focused on frame activation 2022-10-21 12:05:01 +03:00
Maxim Kartashev
0478a24483 JBR-4918 Additional bugfixes in Wayland buffers management 2022-10-21 09:48:51 +03:00
Maxim Kartashev
c113772448 JBR-4865 Support xdg-shell functions
Implemented maximize/fullscreen together with the reverse functions.
2022-10-19 11:24:08 +03:00
Maxim Kartashev
757194800f JBR-4918 Implement support for window size change 2022-10-18 11:07:01 +03:00
Maxim Kartashev
b9c4ac35ec JBR-4865 Support xdg-shell functions 2022-10-18 11:06:57 +03:00
Dmitry Batrak
adf8d95f7b simplify Wayland events dispatching, fix known issues 2022-10-13 10:23:03 +03:00
Maxim Kartashev
b2986aef46 JBR-4621 Implemented key repeat 2022-10-12 14:23:09 +03:00
Maxim Kartashev
cea81933d9 JBR-4621 Input events support for Wayland
This includes basic mouse and keyboard support.
2022-10-12 14:23:09 +03:00
Maxim Kartashev
6779e2c59b Let WLToolkit work with DISPLAY unset 2022-10-12 14:23:06 +03:00
Alexey Ushakov
4b7c5f62a9 Improved sun.awt.wl.WLGraphicsEnvironment to support createCraphics() 2022-10-12 14:22:34 +03:00
Maxim Kartashev
df204bb882 Added libwakefield source code to the tree
It is not integrated into the build infrastructure both for simplicity
and to avoid otherwise unnecessary dependencies on weston, pixman, etc.

Also fixed copyrights in the recently added files, including the
auto-generated ones.
2022-10-12 14:22:34 +03:00
Maxim Kartashev
0d7fdcf415 Made it possible for Wayland tests to run in parallel
Also fixed a potential crash in getLocationOnScreen().
2022-10-12 14:22:34 +03:00
Maxim Kartashev
73c8c50262 Wayland test harness and sample test 2022-10-12 14:22:34 +03:00
Maxim Kartashev
16cacd0b55 AWT Robot to support Wayland natively
Requires the presence of the 'wakefield' protocol extension on the
server side; will throw UOE on use otherwise. Can be completely
disabled by undefining WAKEFIELD_ROBOT during compilation.

Provides the ability to re-position the surface to the given absolute
coordinates, query the surface's position, obtain RGB of a pixel at the
given absolute coordinates and take a screenshot of an area.
2022-10-12 14:22:34 +03:00
Nikita Gubarkov
19496fcef9 Suppress unused-result warning for libfontmanager 2022-10-12 14:22:34 +03:00
nikita.gubarkov
9cb4769361 Text rendering support
Extracted X11-related code from libfontmanager into libfontmanager_xawt
2022-10-12 14:22:33 +03:00
Maxim Kartashev
dc36d0afaf Reduced xdg_wm_base protocol version to 1 in order to run under Weston
This was done purely for convenience. The version can be bumped back up
at any time, but the change will require a more recent version
of Weston for testing.
2022-10-12 14:22:33 +03:00
Alexey Ushakov
20ca5a41f4 Added JFrame support 2022-10-12 14:22:33 +03:00
Alexey Ushakov
e864ea8469 Fixed child hw component position 2022-10-12 14:22:33 +03:00
Alexey Ushakov
b3e31866ec Implemented heavyweight button rendering 2022-10-12 14:22:33 +03:00
Alexey Ushakov
4cdce4b44a Moved native window management to WLComponentPeer 2022-10-12 14:22:33 +03:00
Alexey Ushakov
2187957e7e Added WLRepaintArea 2022-10-12 14:22:33 +03:00
Alexey Ushakov
14aa544c86 Refactored peers 2022-10-12 14:22:32 +03:00
Alexey Ushakov
de5214531a Added stubs for WLTK button peer 2022-10-12 14:22:32 +03:00
Alexey Ushakov
ea6f74d64f Added 2d surface support 2022-10-12 14:22:32 +03:00
Alexey Ushakov
56e174709b Added support for background color. Refactoring 2022-10-12 14:22:32 +03:00
Alexey Ushakov
c5103ff4a8 Make simple awt window visible 2022-10-12 14:22:32 +03:00
Dmitry Batrak
49c103709e window showing and event loop prototype 2022-10-12 14:22:32 +03:00
Dmitry Batrak
4b21d041d8 more stubbing for WLToolkit, add WLFramePeer 2022-10-12 14:22:31 +03:00
Dmitry Batrak
c1ee18adfb more stubbing for WLToolkit 2022-10-12 14:22:31 +03:00
Alexey Ushakov
693e16b0a1 Created stub version of WLToolkit
A wayland base toolkit with native part linked to wayland-client library
2022-10-12 14:22:28 +03:00
64 changed files with 13317 additions and 36 deletions

View File

@@ -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:

View File

@@ -0,0 +1,91 @@
#
# 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.
#
################################################################################
# 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=
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"; then
WAYLAND_CFLAGS="-I${with_wayland}/include"
WAYLAND_LIBS="-L${with_wayland}/lib -lwayland-client"
WAYLAND_FOUND=yes
AC_MSG_RESULT([$WAYLAND_FOUND])
else
AC_MSG_ERROR([Can't find 'include/wayland-client.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"; 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' 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_FOUND=yes
WAYLAND_CFLAGS=
WAYLAND_LIBS="-lwayland-client"
DEFAULT_WAYLAND=yes
])
fi
if test "x$WAYLAND_FOUND" = xno; then
HELP_MSG_MISSING_DEPENDENCY([wayland])
AC_MSG_ERROR([Could not find wayland! $HELP_MSG ])
fi
fi
AC_SUBST(WAYLAND_CFLAGS)
AC_SUBST(WAYLAND_LIBS)
])

View File

@@ -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,20 @@ m4_include([lib-tests.m4])
################################################################################
AC_DEFUN_ONCE([LIB_DETERMINE_DEPENDENCIES],
[
# Check if X11 is needed
# Check if X11 and wayland 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
elif test "x$ENABLE_HEADLESS_ONLY" = xtrue; then
# No X11 support needed when building headless only
NEEDS_LIB_X11=false
NEEDS_LIB_WAYLAND=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
fi
# Check if fontconfig is needed
@@ -105,6 +112,7 @@ AC_DEFUN_ONCE([LIB_SETUP_LIBRARIES],
LIB_SETUP_LIBFFI
LIB_SETUP_MISC_LIBS
LIB_SETUP_X11
LIB_SETUP_WAYLAND
LIB_TESTS_SETUP_GTEST

View File

@@ -453,7 +453,8 @@ endif
# Necessary additional compiler flags to compile X11
X_CFLAGS:=@X_CFLAGS@
X_LIBS:=@X_LIBS@
WAYLAND_CFLAGS:=@WAYLAND_CFLAGS@
WAYLAND_LIBS:=@WAYLAND_LIBS@
# The lowest required version of macosx
MACOSX_VERSION_MIN=@MACOSX_VERSION_MIN@
# The highest allowed version of macosx

View File

@@ -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,89 @@ 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
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) \
$(CUPS_CFLAGS)
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_SRC := $(LIBAWT_WLAWT_EXTRA_SRC), \
EXTRA_HEADER_DIRS := $(LIBAWT_WLAWT_EXTRA_HEADER_DIRS), \
EXCLUDES := $(LIBAWT_WLAWT_EXCLUDES), \
OPTIMIZATION := LOW, \
CXXFLAGS := $(CXXFLAGS_JDKLIB), \
CFLAGS := $(CFLAGS_JDKLIB) $(LIBAWT_WLAWT_CFLAGS), \
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 := type-limits \
unused-result maybe-uninitialized format \
format-security parentheses \
implicit-fallthrough undef unused-function error, \
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
@@ -367,6 +450,7 @@ ifeq ($(call isTargetOs, windows macosx), false)
common/awt/debug \
common/font \
common/java2d/opengl \
common/java2d/vulkan \
#
LIBAWT_HEADLESS_CFLAGS := $(CUPS_CFLAGS) $(FONTCONFIG_CFLAGS) $(X_CFLAGS) \
@@ -379,8 +463,13 @@ ifeq ($(call isTargetOs, windows macosx), false)
OPTIMIZATION := LOW, \
CFLAGS := $(CFLAGS_JDKLIB) \
$(LIBAWT_HEADLESS_CFLAGS), \
CXXFLAGS := $(CXXFLAGS_JDKLIB), \
EXTRA_HEADER_DIRS := $(LIBAWT_HEADLESS_EXTRA_HEADER_DIRS), \
DISABLED_WARNINGS_gcc := unused-function, \
DISABLED_WARNINGS_CXX_gcc := type-limits \
unused-result maybe-uninitialized format \
format-security parentheses \
implicit-fallthrough undef unused-function error, \
LDFLAGS := $(LDFLAGS_JDKLIB) \
$(call SET_SHARED_LIBRARY_ORIGIN), \
LDFLAGS_unix := -L$(INSTALL_LIBRARIES_HERE), \
@@ -492,13 +581,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 +608,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 +638,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

View File

@@ -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;

View File

@@ -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);
}
}
/**

View File

@@ -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.
*/

View File

@@ -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
}

View File

@@ -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;

View File

@@ -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
}

View File

@@ -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

View 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)

View 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
```

View File

@@ -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>

View 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(&region_in_output);
struct weston_output *output;
wl_list_for_each(output, &compositor->output_list, link) {
if (output->destroying)
continue;
pixman_region32_intersect(&region_in_output, region, &output->region);
if (pixman_region32_not_empty(&region_in_output)) {
const uint64_t this_area = size_in_pixels(&region_in_output);
if (this_area > area) {
area = this_area;
}
if (pixman_region32_equal(&region_in_output, region)) {
*fits_entirely = true;
break;
}
}
}
pixman_region32_fini(&region_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(&region_global, x, y, width, height);
pixman_region32_init(&region_in_output);
bool fits_entirely;
const uint64_t largest_capture_area = get_largest_area_in_one_output(wakefield->compositor, &region_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(&region_in_output, &region_global, &output->region);
if (pixman_region32_not_empty(&region_in_output)) {
const pixman_box32_t * const e = pixman_region32_extents(&region_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, &region_in_output);
// Now convert region_in_output from global to output-local coordinates.
pixman_region32_translate(&region_in_output, -output->x, -output->y);
const pixman_box32_t * const e_in_output = pixman_region32_extents(&region_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(&region_in_output);
pixman_region32_fini(&region_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;
}

View File

@@ -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() :
new WLGraphicsEnvironment();
}
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;

View 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);
}
}

View File

@@ -0,0 +1,70 @@
/*
* 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 {
private boolean eraseBackgroundDisabled;
WLCanvasPeer(Component target) {
super(target);
}
public GraphicsConfiguration getAppropriateGraphicsConfiguration(
GraphicsConfiguration gc)
{
if (graphicsConfig == null || gc == null) {
return gc;
}
graphicsConfig = (WLGraphicsConfig) GraphicsEnvironment.
getLocalGraphicsEnvironment().
getScreenDevices()[0].
getDefaultConfiguration();
return graphicsConfig;
}
protected boolean shouldFocusOnClick() {
// Canvas should always be able to be focused by mouse clicks.
return true;
}
public void disableBackgroundErase() {
eraseBackgroundDisabled = true;
}
protected boolean doEraseBackground() {
return !eraseBackgroundDisabled;
}
}

View File

@@ -0,0 +1,818 @@
/*
* 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.AWTAccessor.ComponentAccessor;
import sun.awt.PaintEventDispatcher;
import sun.awt.event.IgnorePaintEvent;
import sun.java2d.SunGraphics2D;
import sun.java2d.SurfaceData;
import sun.java2d.pipe.Region;
import sun.java2d.wl.WLSurfaceData;
import sun.util.logging.PlatformLogger;
import sun.util.logging.PlatformLogger.Level;
import java.awt.*;
import java.awt.event.ComponentEvent;
import java.awt.event.FocusEvent;
import java.awt.event.InputEvent;
import java.awt.event.InputMethodEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.awt.event.PaintEvent;
import java.awt.event.WindowEvent;
import java.awt.image.ColorModel;
import java.awt.image.VolatileImage;
import java.awt.peer.ComponentPeer;
import java.awt.peer.ContainerPeer;
import java.util.Objects;
public class WLComponentPeer implements ComponentPeer {
private static final PlatformLogger focusLog = PlatformLogger.getLogger("sun.awt.wl.focus.WLComponentPeer");
private static final String appID = System.getProperty("sun.java.command");
private long nativePtr;
private static final PlatformLogger log = PlatformLogger.getLogger("sun.awt.wl.WLComponentPeer");
protected final Component target;
protected WLGraphicsConfig graphicsConfig;
protected Color background;
WLSurfaceData surfaceData;
WLRepaintArea paintArea;
boolean paintPending = false;
boolean isLayouting = false;
boolean visible = false;
int x;
int y;
private final Object sizeLock = new Object();
int width; // protected by sizeLock
int height; // protected by sizeLock
static {
initIDs();
}
/**
* Standard peer constructor, with corresponding Component
*/
WLComponentPeer(Component target) {
this.target = target;
this.background = target.getBackground();
initGraphicsConfiguration();
Rectangle bounds = target.getBounds();
x = bounds.x;
y = bounds.y;
width = bounds.width;
height = bounds.height;
this.surfaceData = (WLSurfaceData) graphicsConfig.createSurfaceData(this);
this.nativePtr = nativeCreateFrame();
paintArea = new WLRepaintArea();
log.info("WLComponentPeer: target=" + target + " x=" + x + " y=" + y +
" width=" + width + " height=" + height);
// TODO
// setup parent window for target
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
public Color getBackground() {
return background;
}
public void postPaintEvent(Component target, int x, int y, int w, int h) {
PaintEvent event = PaintEventDispatcher.getPaintEventDispatcher().
createPaintEvent(target, x, y, w, h);
if (event != null) {
WLToolkit.postEvent(event);
}
}
void postPaintEvent() {
if (isVisible()) {
synchronized (sizeLock) {
postPaintEvent(getTarget(), 0, 0, width, height);
}
}
}
boolean isVisible() {
return visible;
}
@Override
public void reparent(ContainerPeer newContainer) {
throw new UnsupportedOperationException();
}
@Override
public boolean isReparentSupported() {
throw new UnsupportedOperationException();
}
@Override
public boolean isObscured() {
throw new UnsupportedOperationException();
}
@Override
public boolean canDetermineObscurity() {
throw new UnsupportedOperationException();
}
public void focusGained(FocusEvent e) {
}
public void focusLost(FocusEvent e) {
}
@Override
public boolean isFocusable() {
return false;
}
public boolean requestFocus(Component lightweightChild, boolean temporary,
boolean focusedWindowChangeAllowed, long time,
FocusEvent.Cause cause) {
final Component currentlyFocused = WLKeyboardFocusManagerPeer.getInstance().getCurrentFocusOwner();
if (currentlyFocused == null)
return false;
WLComponentPeer peer = AWTAccessor.getComponentAccessor().getPeer(currentlyFocused);
if (peer == null)
return false;
if (this == peer) {
WLKeyboardFocusManagerPeer.deliverFocus(lightweightChild,
target,
temporary,
focusedWindowChangeAllowed,
time,
cause,
WLKeyboardFocusManagerPeer.getInstance().getCurrentFocusOwner());
} else {
return false;
}
return true;
}
protected void wlSetVisible(boolean v) {
this.visible = v;
if (this.visible) {
final String title = target instanceof Frame frame ? frame.getTitle() : null;
nativeCreateWLSurface(nativePtr, getParentNativePtr(target), target.getX(), target.getY(), title, appID);
// Now wait for the sequence of configure events and the window
// will finally appear on screen after we post a PaintEvent
// from notifyConfigured()
configureWLSurface();
final long wlSurfacePtr = getWLSurface();
WLToolkit.registerWLSurface(wlSurfacePtr, this);
} else {
WLToolkit.unregisterWLSurface(getWLSurface());
surfaceData.assignSurface(0);
nativeHideFrame(nativePtr);
}
}
void configureWLSurface() {}
@Override
public void setVisible(boolean v) {
wlSetVisible(v);
}
/**
* @see ComponentPeer
*/
public void setEnabled(final boolean value) {
log.info("Not implemented: WLComponentPeer.setEnabled(boolean)");
}
@Override
public void paint(final Graphics g) {
paintPeer(g);
target.paint(g);
}
void paintPeer(final Graphics g) {
}
Graphics getGraphics(SurfaceData surfData, Color afore, Color aback, Font afont) {
if (surfData == null) return null;
Color bgColor = aback;
if (bgColor == null) {
bgColor = SystemColor.window;
}
Color fgColor = afore;
if (fgColor == null) {
fgColor = SystemColor.windowText;
}
Font font = afont;
if (font == null) {
font = new Font(Font.DIALOG, Font.PLAIN, 12);
}
return new SunGraphics2D(surfData, fgColor, bgColor, font);
}
public Graphics getGraphics() {
return getGraphics(surfaceData,
target.getForeground(),
target.getBackground(),
target.getFont());
}
/**
* Commits changes accumulated in the underlying SurfaceData object
* to the server for displaying on the screen. The request may not be
* granted immediately as the server may be busy reading data provided
* previously. In the latter case, the commit will happen later when
* the server notifies us (through an event on EDT) that the displaying
* buffer is ready to accept new data.
*/
public void commitToServer() {
if (getWLSurface() != 0) {
surfaceData.commitToServer();
}
}
public Component getTarget() {
return target;
}
public void print(Graphics g) {
log.info("Not implemented: WLComponentPeer.print(Graphics)");
}
public void setBounds(int x, int y, int width, int height, int op) {
final boolean positionChanged = this.x != x || this.y != y;
if (positionChanged) {
WLRobotPeer.setLocationOfWLSurface(getWLSurface(), x, y);
}
this.x = x;
this.y = y;
if (positionChanged) {
WLToolkit.postEvent(new ComponentEvent(getTarget(), ComponentEvent.COMPONENT_MOVED));
}
synchronized(sizeLock) {
final boolean sizeChanged = this.width != width || this.height != height;
if (sizeChanged) {
this.width = width;
this.height = height;
surfaceData.revalidate(width, height);
updateWindowGeometry();
layout();
WLToolkit.postEvent(new ComponentEvent(getTarget(), ComponentEvent.COMPONENT_RESIZED));
}
postPaintEvent();
}
}
public Rectangle getVisibleBounds() {
synchronized(sizeLock) {
return new Rectangle(0, 0, width, height);
}
}
private void updateWindowGeometry() {
// From xdg-shell.xml:
// "The window geometry of a surface is its "visible bounds" from the
// user's perspective. Client-side decorations often have invisible
// portions like drop-shadows which should be ignored for the
// purposes of aligning, placing and constraining windows"
if (nativePtr != 0) {
final Rectangle visibleBounds = getVisibleBounds();
nativeSetWindowGeometry(nativePtr,
visibleBounds.x, visibleBounds.y,
visibleBounds.width, visibleBounds.height);
}
}
public void coalescePaintEvent(PaintEvent e) {
Rectangle r = e.getUpdateRect();
if (!(e instanceof IgnorePaintEvent)) {
paintArea.add(r, e.getID());
}
switch (e.getID()) {
case PaintEvent.UPDATE -> {
if (log.isLoggable(Level.INFO)) {
log.info("WLCP coalescePaintEvent : UPDATE : add : x = " +
r.x + ", y = " + r.y + ", width = " + r.width + ",height = " + r.height);
}
}
case PaintEvent.PAINT -> {
if (log.isLoggable(Level.INFO)) {
log.info("WLCP coalescePaintEvent : PAINT : add : x = " +
r.x + ", y = " + r.y + ", width = " + r.width + ",height = " + r.height);
}
}
}
}
@Override
public Point getLocationOnScreen() {
final long wlSurfacePtr = getWLSurface();
if (wlSurfacePtr != 0) {
try {
return WLRobotPeer.getLocationOfWLSurface(wlSurfacePtr);
} catch (UnsupportedOperationException ignore) {
return new Point(0, 0);
}
} else {
throw new UnsupportedOperationException("getLocationOnScreen() not supported without wayland surface");
}
}
/**
* Translate the point of coordinates relative to this component to the absolute coordinates,
* if getLocationOnScreen() is supported. Otherwise, the argument is returned.
*/
Point relativePointToAbsolute(Point relativePoint) {
Point absolute = relativePoint;
try {
final Point myLocation = getLocationOnScreen();
absolute = absolute.getLocation();
absolute.translate(myLocation.x, myLocation.y);
} catch (UnsupportedOperationException ignore) {
}
return absolute;
}
@SuppressWarnings("fallthrough")
public void handleEvent(AWTEvent e) {
if ((e instanceof InputEvent inputEvent) && !inputEvent.isConsumed() && target.isEnabled()) {
if (e instanceof MouseEvent mouseEvent) {
if (e instanceof MouseWheelEvent mouseWheelEvent) {
handleJavaMouseWheelEvent(mouseWheelEvent);
} else
handleJavaMouseEvent(mouseEvent);
} else if (e instanceof KeyEvent keyEvent) {
handleJavaKeyEvent(keyEvent);
}
} else if (e instanceof KeyEvent && !((InputEvent) e).isConsumed()) {
// even if target is disabled.
log.info("Not implemented: WLComponentPeer.handleEvent(AWTEvent): handleF10JavaKeyEvent");
} else if (e instanceof InputMethodEvent) {
log.info("Not implemented: WLComponentPeer.handleEvent(AWTEvent): handleJavaInputMethodEvent");
}
int id = e.getID();
switch (id) {
case PaintEvent.PAINT:
// Got native painting
paintPending = false;
// Fallthrough to next statement
case PaintEvent.UPDATE:
log.info("WLComponentPeer.handleEvent(AWTEvent): UPDATE " + this);
// Skip all painting while layouting and all UPDATEs
// while waiting for native paint
if (!isLayouting && !paintPending) {
paintArea.paint(target, false);
}
return;
case FocusEvent.FOCUS_LOST:
case FocusEvent.FOCUS_GAINED:
handleJavaFocusEvent(e);
break;
case WindowEvent.WINDOW_LOST_FOCUS:
case WindowEvent.WINDOW_GAINED_FOCUS:
handleJavaWindowFocusEvent(e);
break;
default:
break;
}
}
void handleJavaKeyEvent(KeyEvent e) {
}
void handleJavaMouseEvent(MouseEvent e) {
}
void handleJavaMouseWheelEvent(MouseWheelEvent e) {
}
void handleJavaFocusEvent(AWTEvent e) {
if (focusLog.isLoggable(PlatformLogger.Level.FINER)) {
focusLog.finer(String.valueOf(e));
}
if (e.getID() == FocusEvent.FOCUS_GAINED) {
focusGained((FocusEvent)e);
} else {
focusLost((FocusEvent)e);
}
}
void handleJavaWindowFocusEvent(AWTEvent e) {
}
public void beginLayout() {
// Skip all painting till endLayout
isLayouting = true;
}
public void endLayout() {
log.info("WLComponentPeer.endLayout(): paintArea.isEmpty() " + paintArea.isEmpty());
if (!paintPending && !paintArea.isEmpty()
&& !AWTAccessor.getComponentAccessor().getIgnoreRepaint(target)) {
// if not waiting for native painting repaint damaged area
WLToolkit.postEvent(new PaintEvent(target, PaintEvent.PAINT,
new Rectangle()));
}
isLayouting = false;
}
public Dimension getMinimumSize() {
return target.getSize();
}
void setMinimumSizeTo(Dimension minSize) {
if (nativePtr != 0) {
nativeSetMinimumSize(nativePtr, minSize.width, minSize.height);
}
}
void setMaximumSizeTo(Dimension maxSize) {
if (nativePtr != 0) {
nativeSetMaximumSize(nativePtr, maxSize.width, maxSize.height);
}
}
@Override
public ColorModel getColorModel() {
if (graphicsConfig != null) {
return graphicsConfig.getColorModel();
}
else {
return Toolkit.getDefaultToolkit().getColorModel();
}
}
public Dimension getPreferredSize() {
return getMinimumSize();
}
public void layout() {}
@Override
public void setBackground(Color c) {
if (Objects.equals(background, c)) {
return;
}
background = c;
// TODO: propagate this change to WLSurfaceData
}
@Override
public void setForeground(Color c) {
if (log.isLoggable(PlatformLogger.Level.FINE)) {
log.fine("Set foreground to " + c);
}
log.info("Not implemented: WLComponentPeer.setForeground(Color)");
}
@Override
public FontMetrics getFontMetrics(Font font) {
throw new UnsupportedOperationException();
}
@Override
public void dispose() {
WLToolkit.unregisterWLSurface(getWLSurface());
nativeDisposeFrame(nativePtr);
}
@Override
public void setFont(Font f) {
throw new UnsupportedOperationException();
}
public Font getFont() {
return null;
}
public void updateCursorImmediately() {
log.info("Not implemented: WLComponentPeer.updateCursorImmediately()");
}
@Override
public Image createImage(int width, int height) {
return graphicsConfig.createAcceleratedImage(target, width, height);
}
@Override
public VolatileImage createVolatileImage(int width, int height) {
throw new UnsupportedOperationException();
}
protected void initGraphicsConfiguration() {
graphicsConfig = (WLGraphicsConfig) target.getGraphicsConfiguration();
}
@Override
public GraphicsConfiguration getGraphicsConfiguration() {
if (graphicsConfig == null) {
initGraphicsConfiguration();
}
return graphicsConfig;
}
@Override
public boolean handlesWheelScrolling() {
throw new UnsupportedOperationException();
}
@Override
public void createBuffers(int numBuffers, BufferCapabilities caps) throws AWTException {
throw new UnsupportedOperationException();
}
@Override
public void flip(int x1, int y1, int x2, int y2, BufferCapabilities.FlipContents flipAction) {
throw new UnsupportedOperationException();
}
@Override
public Image getBackBuffer() {
throw new UnsupportedOperationException();
}
@Override
public void destroyBuffers() {
throw new UnsupportedOperationException();
}
@Override
public void setZOrder(ComponentPeer above) {
log.info("Not implemented: WLComponentPeer.setZOrder(ComponentPeer)");
}
@Override
public void applyShape(Region shape) {
throw new UnsupportedOperationException();
}
@Override
public boolean updateGraphicsData(GraphicsConfiguration gc) {
throw new UnsupportedOperationException();
}
final void setFrameTitle(String title) {
Objects.requireNonNull(title);
if (nativePtr != 0) {
nativeSetTitle(nativePtr, title);
}
}
final void requestMinimized() {
if (nativePtr != 0) {
nativeRequestMinimized(nativePtr);
}
}
final void requestMaximized() {
if (nativePtr != 0) {
nativeRequestMaximized(nativePtr);
}
}
final void requestUnmaximized() {
if (nativePtr != 0) {
nativeRequestUnmaximized(nativePtr);
}
}
final void requestFullScreen() {
if (nativePtr != 0) {
nativeRequestFullScreen(nativePtr);
}
}
final void requestUnsetFullScreen() {
if (nativePtr != 0) {
nativeRequestUnsetFullScreen(nativePtr);
}
}
private static native void initIDs();
protected native long nativeCreateFrame();
protected native void nativeCreateWLSurface(long ptr, long parentPtr, int x, int y, String title, String appID);
protected native void nativeHideFrame(long ptr);
protected native void nativeDisposeFrame(long ptr);
private native long getWLSurface();
private native void nativeStartDrag(long ptr);
private native void nativeStartResize(long ptr, int edges);
private native void nativeSetTitle(long ptr, String title);
private native void nativeRequestMinimized(long ptr);
private native void nativeRequestMaximized(long ptr);
private native void nativeRequestUnmaximized(long ptr);
private native void nativeRequestFullScreen(long ptr);
private native void nativeRequestUnsetFullScreen(long ptr);
private native void nativeSetWindowGeometry(long ptr, int x, int y, int width, int height);
private native void nativeSetMinimumSize(long ptr, int width, int height);
private native void nativeSetMaximumSize(long ptr, int width, int height);
static long getParentNativePtr(Component target) {
Component parent = target.getParent();
if (parent == null) return 0;
final ComponentAccessor acc = AWTAccessor.getComponentAccessor();
ComponentPeer peer = acc.getPeer(parent);
return ((WLComponentPeer)peer).nativePtr;
}
private final Object state_lock = new Object();
/**
* This lock object is used to protect instance data from concurrent access
* by two threads.
*/
Object getStateLock() {
return state_lock;
}
void postMouseEvent(MouseEvent e) {
WLToolkit.postEvent(e);
}
/**
* Creates and posts mouse events based on the given WLPointerEvent received from Wayland,
* the freshly updated WLInputState, and the previous WLInputState.
*/
void dispatchPointerEventInContext(WLPointerEvent e, WLInputState oldInputState, WLInputState newInputState) {
final int x = newInputState.getPointerX();
final int y = newInputState.getPointerY();
final Point abs = relativePointToAbsolute(new Point(x, y));
int xAbsolute = abs.x;
int yAbsolute = abs.y;
final long timestamp = newInputState.getTimestamp();
if (e.hasEnterEvent()) {
final MouseEvent mouseEvent = new MouseEvent(getTarget(), MouseEvent.MOUSE_ENTERED,
timestamp,
newInputState.getModifiers(),
x, y,
xAbsolute, yAbsolute,
0, false, MouseEvent.NOBUTTON);
postMouseEvent(mouseEvent);
}
int clickCount = 0;
boolean isPopupTrigger = false;
int buttonChanged = MouseEvent.NOBUTTON;
if (e.hasButtonEvent()) {
final WLPointerEvent.PointerButtonCodes buttonCode
= WLPointerEvent.PointerButtonCodes.recognizedOrNull(e.getButtonCode());
if (buttonCode != null) {
clickCount = newInputState.getClickCount();
isPopupTrigger = buttonCode.isPopupTrigger();
buttonChanged = buttonCode.javaCode;
final MouseEvent mouseEvent = new MouseEvent(getTarget(),
e.getIsButtonPressed() ? MouseEvent.MOUSE_PRESSED : MouseEvent.MOUSE_RELEASED,
timestamp,
newInputState.getModifiers(),
x, y,
xAbsolute, yAbsolute,
clickCount,
isPopupTrigger,
buttonChanged);
postMouseEvent(mouseEvent);
final boolean isButtonReleased = !e.getIsButtonPressed();
final boolean wasSameButtonPressed = oldInputState.hasThisPointerButtonPressed(e.getButtonCode());
final boolean isButtonClicked = isButtonReleased && wasSameButtonPressed;
if (isButtonClicked) {
final MouseEvent mouseClickEvent = new MouseEvent(getTarget(),
MouseEvent.MOUSE_CLICKED,
timestamp,
newInputState.getModifiers(),
x, y,
xAbsolute, yAbsolute,
clickCount,
isPopupTrigger,
buttonChanged);
postMouseEvent(mouseClickEvent);
}
}
}
if (e.hasAxisEvent() && e.getIsAxis0Valid()) {
final MouseEvent mouseEvent = new MouseWheelEvent(getTarget(),
MouseEvent.MOUSE_WHEEL,
timestamp,
newInputState.getModifiers(),
x, y,
xAbsolute, yAbsolute,
1,
isPopupTrigger,
MouseWheelEvent.WHEEL_UNIT_SCROLL,
1,
e.getAxis0Value());
postMouseEvent(mouseEvent);
}
if (e.hasMotionEvent()) {
final MouseEvent mouseEvent = new MouseEvent(getTarget(),
newInputState.hasPointerButtonPressed()
? MouseEvent.MOUSE_DRAGGED : MouseEvent.MOUSE_MOVED,
timestamp,
newInputState.getModifiers(),
x, y,
xAbsolute, yAbsolute,
clickCount,
isPopupTrigger,
buttonChanged);
postMouseEvent(mouseEvent);
}
if (e.hasLeaveEvent()) {
final MouseEvent mouseEvent = new MouseEvent(getTarget(),
MouseEvent.MOUSE_EXITED,
timestamp,
newInputState.getModifiers(),
x, y,
xAbsolute, yAbsolute,
clickCount,
isPopupTrigger,
buttonChanged);
postMouseEvent(mouseEvent);
}
}
void startDrag() {
nativeStartDrag(nativePtr);
}
void startResize(int edges) {
nativeStartResize(nativePtr, edges);
}
void notifyConfigured(int width, int height, boolean active, boolean maximized) {
final long wlSurfacePtr = getWLSurface();
// TODO: this needs to be done only once after wlSetVisible(true)
surfaceData.assignSurface(wlSurfacePtr);
if (width != 0 && height != 0) target.setSize(width, height);
if (width == 0 && height == 0) {
// From xdg-shell.xml: "If the width or height arguments are zero,
// it means the client should decide its own window dimension".
// In case this is the first configure after setVisible(true), we
// need to post the initial paint event for the window to appear on
// the screen. In the other case, this paint event is posted
// by setBounds() eventually called from target.setSize() above.
postPaintEvent();
}
}
}

View File

@@ -0,0 +1,338 @@
/*
* 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.concurrent.atomic.AtomicBoolean;
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 final WLFramePeer 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(WLFramePeer peer) {
this.peer = peer;
closeButton = new ButtonState(this::getCloseButtonCenter, peer::postWindowClosing);
maximizeButton = new ButtonState(this::getMaximizeButtonCenter, this::toggleMaximizedState);
minimizeButton = new ButtonState(this::getMinimizeButtonCenter, this::minimizeWindow);
}
private Frame getFrame() {
return (Frame) peer.target;
}
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 Point getCloseButtonCenter() {
int width = peer.getWidth();
return width >= HEIGHT ? new Point(width - HEIGHT / 2, HEIGHT / 2) : null;
}
private Point getMaximizeButtonCenter() {
if (!getFrame().isResizable()) return null;
int width = peer.getWidth();
return width >= 2 * HEIGHT ? new Point(width - HEIGHT * 3 / 2, HEIGHT / 2) : null;
}
private Point getMinimizeButtonCenter() {
int width = peer.getWidth();
int buttonSpaceWidth = getButtonSpaceWidth();
return width >= buttonSpaceWidth ? new Point(width - buttonSpaceWidth + HEIGHT / 2, HEIGHT / 2) : null;
}
private int getButtonSpaceWidth() {
return (getFrame().isResizable() ? 3 : 2) * 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();
}
}
private void doPaint(Graphics2D g) {
int width = peer.getWidth();
String title = getFrame().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) {
Point point = e.getPoint();
if (e.getID() == MouseEvent.MOUSE_PRESSED && getFrame().isResizable()) {
int resizeSide = getResizeEdges(point);
if (resizeSide != 0) {
peer.startResize(resizeSide);
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.processMouseEvent(e) |
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()
&& getFrame().isResizable()) {
toggleMaximizedState();
}
}
private int getResizeEdges(Point p) {
if (!getFrame().isResizable()) return 0;
int edges = 0;
if (p.x < RESIZE_EDGE_THICKNESS) {
edges |= XDG_TOPLEVEL_RESIZE_EDGE_LEFT;
} else if (p.x > peer.getWidth() - RESIZE_EDGE_THICKNESS) {
edges |= XDG_TOPLEVEL_RESIZE_EDGE_RIGHT;
}
if (p.y < RESIZE_EDGE_THICKNESS) {
edges |= XDG_TOPLEVEL_RESIZE_EDGE_TOP;
} else if (p.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() {
getFrame().setExtendedState(peer.getState() == Frame.NORMAL ? Frame.MAXIMIZED_BOTH : Frame.NORMAL);
}
private void minimizeWindow() {
getFrame().setState(Frame.ICONIFIED);
}
private static final AtomicBoolean needRepaint = new AtomicBoolean(false);
boolean getRepaintNeededAndReset() {
return needRepaint.compareAndExchange(true, false);
}
void markRepaintNeeded() {
needRepaint.set(true);
}
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;
}
}
}

View File

@@ -0,0 +1,314 @@
/*
* 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.MouseEvent;
import java.awt.event.WindowEvent;
import java.awt.peer.ComponentPeer;
import java.awt.peer.FramePeer;
import sun.awt.AWTAccessor;
import sun.awt.AWTAccessor.ComponentAccessor;
import sun.util.logging.PlatformLogger;
public class WLFramePeer extends WLComponentPeer implements FramePeer {
private static final PlatformLogger log = PlatformLogger.getLogger("sun.awt.wl.WLFramePeer");
private final WLFrameDecoration decoration;
private int state; // Guarded by getStateLock()
private int widthBeforeMaximized; // Guarded by getStateLock()
private int heightBeforeMaximized; // Guarded by getStateLock()
public WLFramePeer(Frame target) {
super(target);
decoration = target.isUndecorated() ? null : new WLFrameDecoration(this);
}
@Override
protected void wlSetVisible(boolean v) {
super.wlSetVisible(v);
final ComponentAccessor acc = AWTAccessor.getComponentAccessor();
for (Component c : ((Frame)target).getComponents()) {
ComponentPeer cPeer = acc.getPeer(c);
if (cPeer instanceof WLComponentPeer) {
((WLComponentPeer) cPeer).wlSetVisible(v);
}
}
}
@Override
void configureWLSurface() {
super.configureWLSurface();
updateMinimumSize();
updateMaximumSize();
int state = ((Frame) target).getExtendedState();
if (state != Frame.NORMAL) {
setState(state);
}
}
private static native void initIDs();
static {
if (!GraphicsEnvironment.isHeadless()) {
initIDs();
}
}
@Override
public Insets getInsets() {
return decoration == null ? new Insets(0, 0, 0, 0) : decoration.getInsets();
}
@Override
public void beginValidate() {
}
@Override
public void endValidate() {
}
// @Override
// public void beginLayout() {
// log.info("Not implemented: WLFramePeer.beginLayout()");
// }
//
// @Override
// public void endLayout() {
// log.info("Not implemented: WLFramePeer.endLayout()");
// }
@Override
public void setTitle(String title) {
setFrameTitle(title);
notifyClientDecorationsChanged();
}
@Override
public void setMenuBar(MenuBar mb) {
throw new UnsupportedOperationException();
}
@Override
public void setResizable(boolean resizeable) {
notifyClientDecorationsChanged();
}
@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((Frame) target, newState & ~Frame.ICONIFIED);
} else if (newState == Frame.MAXIMIZED_BOTH) {
requestMaximized();
} else /* Frame.NORMAL */ {
requestUnmaximized();
}
}
@Override
public int getState() {
synchronized(getStateLock()) {
return state;
}
}
@Override
public void setMaximizedBounds(Rectangle bounds) {
}
@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 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
public void updateAlwaysOnTopState() {
throw new UnsupportedOperationException();
}
@Override
public void updateFocusableWindowState() {
}
@Override
public void setModalBlocked(Dialog blocker, boolean blocked) {
throw new UnsupportedOperationException();
}
@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() {
throw new UnsupportedOperationException();
}
@Override
public void setOpacity(float opacity) {
throw new UnsupportedOperationException();
}
@Override
public void setOpaque(boolean isOpaque) {
throw new UnsupportedOperationException();
}
@Override
public void updateWindow() {
// signals the end of repainting by Swing and/or AWT
paintClientDecorations(getGraphics());
commitToServer();
}
@Override
public void repositionSecurityWarning() {
throw new UnsupportedOperationException();
}
// 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 width, int height, boolean active, boolean maximized) {
int widthBefore = getWidth();
int heightBefore = getHeight();
super.notifyConfigured(width, height, active, maximized);
synchronized (getStateLock()) {
int oldState = state;
state = maximized ? Frame.MAXIMIZED_BOTH : Frame.NORMAL;
AWTAccessor.getFrameAccessor().setExtendedState((Frame)target, state);
if (state != oldState) {
if (maximized) {
widthBeforeMaximized = widthBefore;
heightBeforeMaximized = heightBefore;
} else if (width == 0 && height == 0 && widthBeforeMaximized > 0 && heightBeforeMaximized > 0) {
target.setSize(widthBeforeMaximized, heightBeforeMaximized);
}
WLToolkit.postEvent(new WindowEvent((Window)target, WindowEvent.WINDOW_STATE_CHANGED, oldState, state));
notifyClientDecorationsChanged();
}
}
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) {
notifyClientDecorationsChanged();
super.setBounds(x, y, width, height, op);
}
final void notifyClientDecorationsChanged() {
if (decoration != null) {
final Rectangle bounds = decoration.getBounds();
decoration.markRepaintNeeded();
postPaintEvent(getTarget(), bounds.x, bounds.y, bounds.width, bounds.height);
}
}
final void paintClientDecorations(final Graphics g) {
if (decoration != null && decoration.getRepaintNeededAndReset()) {
decoration.paint(g);
}
}
@Override
void paintPeer(final Graphics g) {
super.paintPeer(g);
}
}

View File

@@ -0,0 +1,77 @@
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 Rectangle bounds = new Rectangle(800, 600);
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() {
throw new UnsupportedOperationException();
}
@Override
public Rectangle getBounds() {
return bounds;
}
public SurfaceType getSurfaceType() {
return SurfaceType.IntArgb;
}
public SurfaceData createSurfaceData(WLComponentPeer peer) {
return WLSurfaceData.createData(peer);
}
public double getScale() {
return getDevice().getScaleFactor();
}
}

View File

@@ -0,0 +1,72 @@
package sun.awt.wl;
import sun.awt.AWTAccessor;
import sun.awt.X11ComponentPeer;
import java.awt.*;
public class WLGraphicsDevice extends GraphicsDevice {
private double scale = 1.0;
private final WLGraphicsConfig config = new WLGraphicsConfig(this);
@Override
public int getType() {
return TYPE_RASTER_SCREEN;
}
@Override
public String getIDstring() {
return "WLGraphicsDevice";
}
@Override
public GraphicsConfiguration[] getConfigurations() {
return new GraphicsConfiguration[] {config};
}
@Override
public GraphicsConfiguration getDefaultConfiguration() {
return config;
}
@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);
}
}
}
public double getScaleFactor() {
return scale;
}
private void enterFullScreenExclusive(Window w) {
WLComponentPeer peer = AWTAccessor.getComponentAccessor().getPeer(w);
if (peer != null) {
peer.requestFullScreen();
}
}
private void exitFullScreenExclusive(Window w) {
WLComponentPeer peer = AWTAccessor.getComponentAccessor().getPeer(w);
if (peer != null) {
peer.requestUnsetFullScreen();
}
}
}

View File

@@ -0,0 +1,44 @@
package sun.awt.wl;
import java.awt.GraphicsDevice;
import sun.java2d.SunGraphicsEnvironment;
import sun.java2d.SurfaceManagerFactory;
import sun.java2d.UnixSurfaceManagerFactory;
import sun.java2d.vulkan.VKGraphicsDevice;
import sun.util.logging.PlatformLogger;
public class WLGraphicsEnvironment extends SunGraphicsEnvironment {
private static final PlatformLogger log = PlatformLogger.getLogger("sun.awt.wl.WLComponentPeer");
private static boolean vulkanEnabled = false;
private static native boolean initVK();
static {
System.loadLibrary("awt");
SurfaceManagerFactory.setInstance(new UnixSurfaceManagerFactory());
String prop = System.getProperty("sun.java2d.vulkan");
if ("true".equals(prop)) {
vulkanEnabled = initVK();
}
}
@Override
protected int getNumScreens() {
log.info("Not implemented: WLGraphicsEnvironment.getNumScreens()");
return 1;
}
@Override
protected GraphicsDevice makeScreenDevice(int screennum) {
log.info("Not implemented: WLGraphicsEnvironment.makeScreenDevice(int)");
if (vulkanEnabled) {
return new VKGraphicsDevice();
} else {
return new WLGraphicsDevice();
}
}
@Override
public boolean isDisplayLocal() {
return true;
}
}

View File

@@ -0,0 +1,222 @@
/*
* 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);
}
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;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}

View 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);
}
}

View 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);
}
}
}
}
}

View 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);
}

View File

@@ -0,0 +1,986 @@
/*
* 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.MouseEvent;
import java.awt.event.MouseWheelEvent;
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, () -> {
try {
dispatchEventsOnEDT();
} finally {
eventsQueued.release();
}
}, 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 newInputState = inputState.update(e);
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, inputState, newInputState);
}
inputState = 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) {
log.info("Not implemented: WLToolkit.createWindow()");
return null;
}
@Override
public DialogPeer createDialog(Dialog target) {
log.info("Not implemented: WLToolkit.createDialog()");
return null;
}
@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 {
log.info("Not implemented: WLToolkit.createCustomCursor()");
return null;
}
@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;
}
/**
* Returns the supported cursor size
*/
@Override
public Dimension getBestCursorSize(int preferredWidth, int preferredHeight) {
log.info("Not implemented: WLToolkit.getBestCursorSize()");
return null;
}
@Override
public int getMaximumCursorColors() {
return 2; // Black and white.
}
@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) {
log.info("Not implemented: WLToolkit.isModalityTypeSupported()");
return false;
}
@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;
}
}

View File

@@ -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);
}
}

View File

@@ -0,0 +1,117 @@
/*
* 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.vulkan;
import sun.java2d.pipe.BufferedContext;
import sun.java2d.pipe.RenderBuffer;
import sun.java2d.pipe.RenderQueue;
import sun.java2d.pipe.hw.ContextCapabilities;
import java.lang.annotation.Native;
import static sun.java2d.pipe.BufferedOpCodes.SET_SCRATCH_SURFACE;
/**
* Note that the RenderQueue lock must be acquired before calling any of
* the methods in this class.
*/
final class VKContext extends BufferedContext {
public VKContext(RenderQueue rq) {
super(rq);
}
/**
* Convenience method that delegates to setScratchSurface() below.
*/
static void setScratchSurface(VKGraphicsConfig gc) {
setScratchSurface(gc.getNativeConfigInfo());
}
public static void setScratchSurface(long pConfigInfo) {
// assert MTLRenderQueue.getInstance().lock.isHeldByCurrentThread();
// invalidate the current context
currentContext = null;
// set the scratch context
VKRenderQueue rq = VKRenderQueue.getInstance();
RenderBuffer buf = rq.getBuffer();
rq.ensureCapacityAndAlignment(12, 4);
buf.putInt(SET_SCRATCH_SURFACE);
buf.putLong(pConfigInfo);
}
public static class VKContextCaps extends ContextCapabilities {
/** Indicates that the context is doublebuffered. */
@Native
public static final int CAPS_DOUBLEBUFFERED = (FIRST_PRIVATE_CAP << 0);
/**
* This cap will only be set if the lcdshader system property has been
* enabled and the hardware supports the minimum number of texture units
*/
@Native
static final int CAPS_EXT_LCD_SHADER = (FIRST_PRIVATE_CAP << 1);
/**
* This cap will only be set if the biopshader system property has been
* enabled and the hardware meets our minimum requirements.
*/
@Native
public static final int CAPS_EXT_BIOP_SHADER = (FIRST_PRIVATE_CAP << 2);
/**
* This cap will only be set if the gradshader system property has been
* enabled and the hardware meets our minimum requirements.
*/
@Native
static final int CAPS_EXT_GRAD_SHADER = (FIRST_PRIVATE_CAP << 3);
public VKContextCaps(int caps, String adapterId) {
super(caps, adapterId);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder(super.toString());
if ((caps & CAPS_DOUBLEBUFFERED) != 0) {
sb.append("CAPS_DOUBLEBUFFERED|");
}
if ((caps & CAPS_EXT_LCD_SHADER) != 0) {
sb.append("CAPS_EXT_LCD_SHADER|");
}
if ((caps & CAPS_EXT_BIOP_SHADER) != 0) {
sb.append("CAPS_BIOP_SHADER|");
}
if ((caps & CAPS_EXT_GRAD_SHADER) != 0) {
sb.append("CAPS_EXT_GRAD_SHADER|");
}
return sb.toString();
}
}
}

View File

@@ -0,0 +1,271 @@
/*
* 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.vulkan;
import static sun.java2d.pipe.hw.AccelSurface.RT_TEXTURE;
import static sun.java2d.pipe.hw.AccelSurface.TEXTURE;
import static sun.java2d.pipe.hw.ContextCapabilities.CAPS_MULTITEXTURE;
import static sun.java2d.pipe.hw.ContextCapabilities.CAPS_PS20;
import static sun.java2d.pipe.hw.ContextCapabilities.CAPS_PS30;
import static sun.java2d.pipe.hw.ContextCapabilities.CAPS_RT_TEXTURE_ALPHA;
import static sun.java2d.pipe.hw.ContextCapabilities.CAPS_RT_TEXTURE_OPAQUE;
import static sun.java2d.pipe.hw.ContextCapabilities.CAPS_TEXNONPOW2;
import static sun.java2d.pipe.hw.ContextCapabilities.CAPS_TEXNONSQUARE;
import java.awt.BufferCapabilities;
import java.awt.ImageCapabilities;
import java.awt.Transparency;
import java.awt.color.ColorSpace;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.DirectColorModel;
import java.awt.image.VolatileImage;
import java.awt.image.WritableRaster;
import sun.awt.image.SunVolatileImage;
import sun.awt.image.SurfaceManager;
import sun.awt.wl.WLGraphicsConfig;
import sun.java2d.Disposer;
import sun.java2d.DisposerRecord;
import sun.java2d.Surface;
import sun.java2d.SurfaceData;
import sun.java2d.pipe.hw.AccelGraphicsConfig;
import sun.java2d.pipe.hw.AccelSurface;
import sun.java2d.pipe.hw.AccelTypedVolatileImage;
import sun.java2d.pipe.hw.ContextCapabilities;
import sun.util.logging.PlatformLogger;
public final class VKGraphicsConfig extends WLGraphicsConfig
implements AccelGraphicsConfig, SurfaceManager.ProxiedGraphicsConfig
{
private static final PlatformLogger log =
PlatformLogger.getLogger("sun.java2d.vulkan.VKGraphicsConfig");
private static ImageCapabilities imageCaps = new VKImageCaps();
private final int maxTextureSize;
private BufferCapabilities bufferCaps;
private long pConfigInfo;
private ContextCapabilities vkCaps;
private final Object disposerReferent = new Object();
private final VKContext context;
private static native long getVKConfigInfo();
/**
* Returns maximum texture size supported by Vulkan. Must be
* called under VKRQ lock.
*/
private static native int nativeGetMaxTextureSize();
private VKGraphicsConfig(VKGraphicsDevice device,
long configInfo, int maxTextureSize,
ContextCapabilities vkCaps) {
super(device);
this.pConfigInfo = configInfo;
this.vkCaps = vkCaps;
this.maxTextureSize = maxTextureSize;
context = new VKContext(VKRenderQueue.getInstance());
// add a record to the Disposer so that we destroy the native
// VKGraphicsConfigInfo data when this object goes away
Disposer.addRecord(disposerReferent,
new VKGCDisposerRecord(pConfigInfo));
}
@Override
public Object getProxyKey() {
return this;
}
public SurfaceData createManagedSurface(int w, int h, int transparency) {
log.info("Not implemented: VKGraphicsConfig.createManagedSurface(int w, int h, " +
"int transparency)");
return null;
}
public static VKGraphicsConfig getConfig(VKGraphicsDevice device)
{
long cfginfo = 0;
int textureSize = 0;
VKRenderQueue rq = VKRenderQueue.getInstance();
rq.lock();
try {
cfginfo = getVKConfigInfo();
if (cfginfo != 0L) {
textureSize = nativeGetMaxTextureSize();
VKContext.setScratchSurface(cfginfo);
}
} finally {
rq.unlock();
}
// if (cfginfo == 0) {
// return null;
// }
ContextCapabilities caps = new VKContext.VKContextCaps(
CAPS_PS30 | CAPS_PS20 |
CAPS_RT_TEXTURE_ALPHA | CAPS_RT_TEXTURE_OPAQUE |
CAPS_MULTITEXTURE | CAPS_TEXNONPOW2 | CAPS_TEXNONSQUARE,
null);
return new VKGraphicsConfig(device, cfginfo, textureSize, caps);
}
/**
* Returns true if the provided capability bit is present for this config.
* See VKContext.java for a list of supported capabilities.
*/
public boolean isCapPresent(int cap) {
return ((vkCaps.getCaps() & cap) != 0);
}
public long getNativeConfigInfo() {
return pConfigInfo;
}
/**
* {@inheritDoc}
*
* @see sun.java2d.pipe.hw.BufferedContextProvider#getContext
*/
@Override
public VKContext getContext() {
return context;
}
@Override
public BufferedImage createCompatibleImage(int width, int height) {
ColorModel model = new DirectColorModel(24, 0xff0000, 0xff00, 0xff);
WritableRaster
raster = model.createCompatibleWritableRaster(width, height);
return new BufferedImage(model, raster, model.isAlphaPremultiplied(),
null);
}
@Override
public ColorModel getColorModel(int transparency) {
switch (transparency) {
case Transparency.OPAQUE:
return new DirectColorModel(24, 0xff0000, 0xff00, 0xff);
case Transparency.BITMASK:
return new DirectColorModel(25, 0xff0000, 0xff00, 0xff, 0x1000000);
case Transparency.TRANSLUCENT:
ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
return new DirectColorModel(cs, 32,
0xff0000, 0xff00, 0xff, 0xff000000,
true, DataBuffer.TYPE_INT);
default:
return null;
}
}
public boolean isDoubleBuffered() {
return true;
}
private static class VKGCDisposerRecord implements DisposerRecord {
private long pCfgInfo;
public VKGCDisposerRecord(long pCfgInfo) {
this.pCfgInfo = pCfgInfo;
}
public void dispose() {
if (pCfgInfo != 0) {
VKRenderQueue.disposeGraphicsConfig(pCfgInfo);
pCfgInfo = 0;
}
}
}
@Override
public String toString() {
return ("VKGraphicsConfig[" + getDevice().getIDstring() + "]");
}
private static class VKBufferCaps extends BufferCapabilities {
public VKBufferCaps(boolean dblBuf) {
super(imageCaps, imageCaps,
dblBuf ? FlipContents.UNDEFINED : null);
}
}
@Override
public BufferCapabilities getBufferCapabilities() {
if (bufferCaps == null) {
bufferCaps = new VKBufferCaps(isDoubleBuffered());
}
return bufferCaps;
}
private static class VKImageCaps extends ImageCapabilities {
private VKImageCaps() {
super(true);
}
public boolean isTrueVolatile() {
return true;
}
}
@Override
public ImageCapabilities getImageCapabilities() {
return imageCaps;
}
@Override
public VolatileImage createCompatibleVolatileImage(int width, int height,
int transparency,
int type) {
if ((type != RT_TEXTURE && type != TEXTURE) ||
transparency == Transparency.BITMASK) {
return null;
}
SunVolatileImage vi = new AccelTypedVolatileImage(this, width, height,
transparency, type);
Surface sd = vi.getDestSurface();
if (!(sd instanceof AccelSurface) ||
((AccelSurface)sd).getType() != type)
{
vi.flush();
vi = null;
}
return vi;
}
/**
* {@inheritDoc}
*
* @see AccelGraphicsConfig#getContextCapabilities
*/
@Override
public ContextCapabilities getContextCapabilities() {
return vkCaps;
}
}

View File

@@ -0,0 +1,63 @@
/*
* 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.vulkan;
import java.awt.DisplayMode;
import java.awt.GraphicsConfiguration;
import sun.awt.wl.WLGraphicsDevice;
import sun.util.logging.PlatformLogger;
public final class VKGraphicsDevice extends WLGraphicsDevice {
private static final PlatformLogger log =
PlatformLogger.getLogger("sun.java2d.vulkan.VKGraphicsDevice");
private GraphicsConfiguration config = VKGraphicsConfig.getConfig(this);
// Save/restore DisplayMode for the Full Screen mode
private DisplayMode initialMode;
public VKGraphicsDevice() {
this.initialMode = getDisplayMode();
}
/**
* Return a list of all configurations.
*/
@Override
public GraphicsConfiguration[] getConfigurations() {
return new GraphicsConfiguration[]{config};
}
/**
* Return the default configuration.
*/
@Override
public GraphicsConfiguration getDefaultConfiguration() {
return config;
}
}

View File

@@ -0,0 +1,256 @@
/*
* 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.vulkan;
import sun.awt.util.ThreadGroupUtils;
import sun.java2d.pipe.RenderBuffer;
import sun.java2d.pipe.RenderQueue;
import java.security.AccessController;
import java.security.PrivilegedAction;
import static sun.java2d.pipe.BufferedOpCodes.DISPOSE_CONFIG;
import static sun.java2d.pipe.BufferedOpCodes.SYNC;
/**
* MTL-specific implementation of RenderQueue. This class provides a
* single (daemon) thread that is responsible for periodically flushing
* the queue, thus ensuring that only one thread communicates with the native
* OpenGL libraries for the entire process.
*/
public class VKRenderQueue extends RenderQueue {
private static VKRenderQueue theInstance;
private final QueueFlusher flusher;
@SuppressWarnings("removal")
private VKRenderQueue() {
/*
* The thread must be a member of a thread group
* which will not get GCed before VM exit.
*/
flusher = AccessController.doPrivileged((PrivilegedAction<QueueFlusher>) QueueFlusher::new);
}
/**
* Returns the single VKRenderQueue instance. If it has not yet been
* initialized, this method will first construct the single instance
* before returning it.
*/
public static synchronized VKRenderQueue getInstance() {
if (theInstance == null) {
theInstance = new VKRenderQueue();
}
return theInstance;
}
/**
* Flushes the single VKRenderQueue instance synchronously. If an
* VKRenderQueue has not yet been instantiated, this method is a no-op.
* This method is useful in the case of Toolkit.sync(), in which we want
* to flush the MTL pipeline, but only if the MTL pipeline is currently
* enabled. Since this class has few external dependencies, callers need
* not be concerned that calling this method will trigger initialization
* of the MTL pipeline and related classes.
*/
public static void sync() {
if (theInstance != null) {
theInstance.lock();
try {
theInstance.ensureCapacity(4);
theInstance.getBuffer().putInt(SYNC);
theInstance.flushNow();
} finally {
theInstance.unlock();
}
}
}
/**
* Disposes the native memory associated with the given native
* graphics config info pointer on the single queue flushing thread.
*/
public static void disposeGraphicsConfig(long pConfigInfo) {
VKRenderQueue rq = getInstance();
rq.lock();
try {
// make sure we make the context associated with the given
// GraphicsConfig current before disposing the native resources
VKContext.setScratchSurface(pConfigInfo);
RenderBuffer buf = rq.getBuffer();
rq.ensureCapacityAndAlignment(12, 4);
buf.putInt(DISPOSE_CONFIG);
buf.putLong(pConfigInfo);
// this call is expected to complete synchronously, so flush now
rq.flushNow();
} finally {
rq.unlock();
}
}
/**
* Returns true if the current thread is the MTL QueueFlusher thread.
*/
public static boolean isQueueFlusherThread() {
return (Thread.currentThread() == getInstance().flusher.thread);
}
@Override
public void flushNow() {
// assert lock.isHeldByCurrentThread();
try {
flusher.flushNow();
} catch (Exception e) {
System.err.println("exception in flushNow:");
e.printStackTrace();
}
}
public void flushAndInvokeNow(Runnable r) {
// assert lock.isHeldByCurrentThread();
try {
flusher.flushAndInvokeNow(r);
} catch (Exception e) {
System.err.println("exception in flushAndInvokeNow:");
e.printStackTrace();
}
}
private native void flushBuffer(long buf, int limit);
private void flushBuffer() {
// assert lock.isHeldByCurrentThread();
int limit = buf.position();
if (limit > 0) {
// process the queue
flushBuffer(buf.getAddress(), limit);
}
// reset the buffer position
buf.clear();
// clear the set of references, since we no longer need them
refSet.clear();
}
private class QueueFlusher implements Runnable {
private boolean needsFlush;
private Runnable task;
private Error error;
private final Thread thread;
public QueueFlusher() {
String name = "Java2D Queue Flusher";
thread = new Thread(ThreadGroupUtils.getRootThreadGroup(),
this, name, 0, false);
thread.setDaemon(true);
thread.setPriority(Thread.MAX_PRIORITY);
thread.start();
}
public synchronized void flushNow() {
// wake up the flusher
needsFlush = true;
notify();
// wait for flush to complete
while (needsFlush) {
try {
wait();
} catch (InterruptedException e) {
}
}
// re-throw any error that may have occurred during the flush
if (error != null) {
throw error;
}
}
public synchronized void flushAndInvokeNow(Runnable task) {
this.task = task;
flushNow();
}
public synchronized void run() {
boolean timedOut = false;
while (true) {
while (!needsFlush) {
try {
timedOut = false;
/*
* Wait until we're woken up with a flushNow() call,
* or the timeout period elapses (so that we can
* flush the queue periodically).
*/
wait(100);
/*
* We will automatically flush the queue if the
* following conditions apply:
* - the wait() timed out
* - we can lock the queue (without blocking)
* - there is something in the queue to flush
* Otherwise, just continue (we'll flush eventually).
*/
if (!needsFlush && (timedOut = tryLock())) {
if (buf.position() > 0) {
needsFlush = true;
} else {
unlock();
}
}
} catch (InterruptedException e) {
}
}
try {
// reset the throwable state
error = null;
// flush the buffer now
flushBuffer();
// if there's a task, invoke that now as well
if (task != null) {
task.run();
}
} catch (Error e) {
error = e;
} catch (Exception x) {
System.err.println("exception in QueueFlusher:");
x.printStackTrace();
} finally {
if (timedOut) {
unlock();
}
task = null;
// allow the waiting thread to continue
needsFlush = false;
notify();
}
}
}
}
}

View File

@@ -0,0 +1,103 @@
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.getWidth(), peer.getHeight(), 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.getTarget().getBounds();
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) {
log.info("Not implemented: WLSurfaceData.createData(WLGraphicsConfig,int,int,ColorModel," +
"SunVolatileImage,long,int,boolean)");
return null;
}
public native void revalidate(int width, int height);
public native void commitToServer();
}

View File

@@ -0,0 +1,130 @@
/*
* Copyright (c) 2000, 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;
/**
* X11 platform implementation of the VolatileSurfaceManager class.
* The class attempts to create and use a pixmap-based SurfaceData
* object (X11PixmapSurfaceData).
* If this object cannot be created or re-created as necessary, the
* class falls back to a system memory based SurfaceData object
* (BufImgSurfaceData) that will be used until the accelerated
* SurfaceData can be restored.
*/
public class WLVolatileSurfaceManager extends VolatileSurfaceManager {
private boolean accelerationEnabled;
public WLVolatileSurfaceManager(SunVolatileImage vImg, Object context) {
super(vImg, context);
// We only accelerated opaque vImages currently
accelerationEnabled = WLSurfaceData.isAccelerationEnabled() &&
(vImg.getTransparency() == Transparency.OPAQUE);
if ((context != null) && !accelerationEnabled) {
// if we're wrapping a backbuffer drawable, we must ensure that
// the accelerated surface is initialized up front, regardless
// of whether acceleration is enabled. But we need to set
// the accelerationEnabled field to true to reflect that this
// SM is actually accelerated.
accelerationEnabled = true;
sdAccel = initAcceleratedSurface();
sdCurrent = sdAccel;
if (sdBackup != null) {
// release the system memory backup surface, as we won't be
// needing it in this case
sdBackup = null;
}
}
}
protected boolean isAccelerationEnabled() {
return accelerationEnabled;
}
/**
* Create a pixmap-based SurfaceData object
*/
protected SurfaceData initAcceleratedSurface() {
SurfaceData sData;
try {
WLGraphicsConfig gc = (WLGraphicsConfig)vImg.getGraphicsConfig();
ColorModel cm = gc.getColorModel();
long drawable = 0;
if (context instanceof Long) {
drawable = ((Long)context).longValue();
}
sData = WLSurfaceData.createData(gc,
vImg.getWidth(),
vImg.getHeight(),
cm, vImg, drawable,
Transparency.OPAQUE,
false);
} catch (NullPointerException ex) {
sData = null;
} catch (OutOfMemoryError er) {
sData = null;
}
return sData;
}
protected boolean isConfigValid(GraphicsConfiguration gc) {
// REMIND: we might be too paranoid here, requiring that
// the GC be exactly the same as the original one. The
// real answer is one that guarantees that pixmap copies
// will be correct (which requires like bit depths and
// formats).
return ((gc == null) || (gc == vImg.getGraphicsConfig()));
}
/**
* Need to override the default behavior because Pixmaps-based
* images are accelerated but not volatile.
*/
@Override
public ImageCapabilities getCapabilities(GraphicsConfiguration gc) {
if (isConfigValid(gc) && isAccelerationEnabled()) {
// accelerated but not volatile
return new ImageCapabilities(true);
}
// neither accelerated nor volatile
return new ImageCapabilities(false);
}
}

View File

@@ -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;

View File

@@ -0,0 +1,112 @@
/*
* 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 <dlfcn.h>
#include <Trace.h>
#include "vulkan/vulkan.h"
#include "vulkan/vulkan_wayland.h"
#include "jni.h"
#include "VKGraphicsConfig.h"
typedef void* (*vkGetInstanceProcAddrPtrType)(void *, const char *);
typedef int (*vkCreateInstancePtrType)(VkInstanceCreateInfo*, void *, void **);
extern "C" jboolean VKGC_IsVKAvailable() {
static jboolean vkAvailable = JNI_FALSE;
static jboolean firstTime = JNI_TRUE;
J2dRlsTraceLn(J2D_TRACE_INFO, "VKGC_IsVKAvailable");
while (firstTime) {
firstTime = JNI_FALSE;
void *lib = dlopen("libvulkan.so", RTLD_LOCAL|RTLD_LAZY);
if (lib == NULL) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "Could not open vulkan library");
break;
}
J2dRlsTraceLn(J2D_TRACE_INFO, "Found vulkan library");
vkGetInstanceProcAddrPtrType vkGetInstanceProcAddrPtr =
(vkGetInstanceProcAddrPtrType)dlsym(lib, "vkGetInstanceProcAddr");
if (!vkGetInstanceProcAddrPtr) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "Could not find vkGetInstanceProcAddr");
break;
}
vkCreateInstancePtrType vkCreateInstancePtr =
(vkCreateInstancePtrType)vkGetInstanceProcAddrPtr(0, "vkCreateInstance");
if (!vkCreateInstancePtr) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "Could not find vkCreateInstance");
break;
}
const char* instanceExtensions[] = { VK_KHR_SURFACE_EXTENSION_NAME, VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME};
VkInstanceCreateInfo instanceCreateInfo = {};
instanceCreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
instanceCreateInfo.pNext = NULL;
instanceCreateInfo.pApplicationInfo = NULL;
instanceCreateInfo.enabledExtensionCount = 2;
instanceCreateInfo.ppEnabledExtensionNames = instanceExtensions;
void *instance = 0;
int result = vkCreateInstancePtr(&instanceCreateInfo, NULL, &instance);
if (!instance || result != 0) {
J2dRlsTraceLn(J2D_TRACE_ERROR, "Cannot create vulkan instance");
break;
}
J2dRlsTraceLn(J2D_TRACE_INFO, "Vulkan is available");
dlclose(lib);
}
return vkAvailable;
}
/*
* Class: sun_java2d_vulkan_VKGraphicsConfig
* Method: getMTLConfigInfo
* Signature: (I)J
*/
JNIEXPORT jlong JNICALL Java_sun_java2d_vulkan_VKGraphicsConfig_getVKConfigInfo
(JNIEnv *env, jclass vkgc) {
fprintf(stderr, "Not implemented:VKGraphicsConfig_getVKConfigInfo\n");
return 0;
}
/*
* Class: sun_java2d_vulkan_VKGraphicsConfig
* Method: nativeGetMaxTextureSize
* Signature: ()I
*/
JNIEXPORT jint JNICALL Java_sun_java2d_vulkan_VKGraphicsConfig_nativeGetMaxTextureSize
(JNIEnv *env, jclass vkgc) {
fprintf(stderr, "Java_sun_java2d_vulkan_VKGraphicsConfig_nativeGetMaxTextureSize\n");
return 0;
}

View File

@@ -0,0 +1,39 @@
/*
* 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 VKGraphicsConfig_h_Included
#define VKGraphicsConfig_h_Included
#ifdef __cplusplus
extern "C" {
#endif
jboolean VKGC_IsVKAvailable();
#ifdef __cplusplus
}
#endif
#endif /* VKGraphicsConfig_h_Included */

View File

@@ -0,0 +1,817 @@
/*
* 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 <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <sys/mman.h>
#include <pthread.h>
#include <assert.h>
#include "Trace.h"
#include "jni_util.h"
#include "WLBuffers.h"
#ifndef HEADLESS
extern struct wl_shm *wl_shm; // 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
bool isFrameCallbackScheduled; // 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)
{
if (pthread_mutex_trylock(&manager->drawLock) == 0) {
fprintf(stderr, "drawLock not acquired at %s:%d\n", file, line);
fflush(stderr);
assert(0);
}
}
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);
}
}
static void
RandomName(char *buf) {
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
long r = ts.tv_nsec;
for (int i = 0; i < 6; ++i) {
buf[i] = 'A' + (r & 15) + (r & 16) * 2;
r >>= 5;
}
}
static int
CreateSharedMemoryFile(void) {
int retries = 100;
do {
char name[] = "/jwlshm-XXXXXX";
RandomName(name + sizeof(name) - 7);
--retries;
int fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600);
if (fd >= 0) {
shm_unlink(name);
return fd;
}
} while (retries > 0 && errno == EEXIST);
return -1;
}
static int
AllocateSharedMemoryFile(size_t size) {
int fd = CreateSharedMemoryFile();
if (fd < 0)
return -1;
int ret;
do {
ret = ftruncate(fd, size);
} while (ret < 0 && errno == EINTR);
if (ret < 0) {
close(fd);
return -1;
}
return fd;
}
/**
* 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)
{
wl_callback_destroy(wl_callback);
WLSurfaceBufferManager * manager = (WLSurfaceBufferManager *) data;
MUTEX_LOCK(manager->showLock);
manager->isFrameCallbackScheduled = false;
struct wl_surface * wlSurface = manager->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);
if (!manager->isFrameCallbackScheduled) {
struct wl_callback *wl_frame_callback = wl_surface_frame(manager->wlSurface);
wl_callback_add_listener(wl_frame_callback, &wl_frame_callback_listener, manager);
manager->isFrameCallbackScheduled = true;
}
}
/**
* 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;
int poolFD = AllocateSharedMemoryFile(newSize);
if (poolFD == -1) {
return;
}
pixel_t *data = (pixel_t *) mmap(NULL, newSize,
PROT_READ | PROT_WRITE, MAP_SHARED,
poolFD, 0);
if (data == MAP_FAILED) {
close(poolFD);
return;
}
manager->bufferForShow.data = data;
manager->bufferForShow.wlPool = wl_shm_create_pool(wl_shm, poolFD, newSize);
close(poolFD);
} 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_mutex_init(&manager->drawLock, NULL);
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);
pthread_mutex_destroy(&manager->showLock);
SurfaceBufferDestroy(manager, true);
DrawBufferDestroy(manager);
pthread_mutex_destroy(&manager->drawLock);
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

View 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

View File

@@ -0,0 +1,255 @@
/*
* 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 "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 (pthread_mutex_trylock(&wlso->lock) == 0) {
fprintf(stderr, "FATAL ERROR: WLSD_GetRasInfo() called without holding the lock");
pthread_mutex_unlock(&wlso->lock);
}
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;
WLSBM_Destroy(wsdo->bufferManager);
wsdo->bufferManager = NULL;
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_mutex_init(&wsdo->lock, NULL);
#endif /* !HEADLESS */
}

View File

@@ -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);

View File

@@ -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

View File

@@ -0,0 +1,440 @@
/*
* 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 <WLBuffers.h>
#include "jni_util.h"
#include "WLToolkit.h"
#include "WLRobotPeer.h"
#ifdef WAKEFIELD_ROBOT
#include "wakefield-client-protocol.h"
#endif
static jfieldID nativePtrID;
static jmethodID postWindowClosingMID;
static jmethodID notifyConfiguredMID;
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);
}
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);
}
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);
}
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);
}
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");
}
JNIEXPORT void JNICALL
Java_sun_awt_wl_WLFramePeer_initIDs
(JNIEnv *env, jclass clazz)
{
CHECK_NULL_THROW_IE(env,
postWindowClosingMID = (*env)->GetMethodID(env, clazz, "postWindowClosing", "()V"),
"Failed to find method WLFramePeer.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)
{
struct WLFrame *frame = jlong_to_ptr(ptr);
if (frame->xdg_toplevel) {
xdg_toplevel_set_fullscreen(frame->xdg_toplevel, NULL);
}
}
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, 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 = parentFrame == NULL;
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);
}
} else {
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 = (struct WLFrame *) ptr;
DoHide(frame);
}
JNIEXPORT void JNICALL
Java_sun_awt_wl_WLComponentPeer_nativeDisposeFrame
(JNIEnv *env, jobject obj, jlong ptr)
{
struct WLFrame *frame = (struct WLFrame *) ptr;
DoHide(frame);
(*env)->DeleteWeakGlobalRef(env, frame->nativeFramePeer);
free(frame);
}
JNIEXPORT jlong JNICALL Java_sun_awt_wl_WLComponentPeer_getWLSurface
(JNIEnv *env, jobject obj)
{
return (jlong)((struct WLFrame*)(*env)->GetLongField(env, obj, nativePtrID))->wl_surface;
}
JNIEXPORT void JNICALL Java_sun_awt_wl_WLComponentPeer_nativeStartDrag
(JNIEnv *env, jobject obj, jlong ptr)
{
struct WLFrame *frame = (struct WLFrame *) 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 = (struct WLFrame *) 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 = (struct WLFrame *) 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 = (struct WLFrame *) 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 = (struct WLFrame *) ptr;
if (frame->toplevel) {
xdg_toplevel_set_max_size(frame->xdg_toplevel, width, height);
}
}

View File

@@ -0,0 +1,42 @@
/*
* 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 <jni.h>
#include "VKGraphicsConfig.h"
/*
* Class: sun_awt_wl_WLGraphicsEnvironment
* Method: initVK
* Signature: ()Z
*/
JNIEXPORT jboolean JNICALL
Java_sun_awt_wl_WLGraphicsEnvironment_initVK(JNIEnv *env, jclass wlge)
{
jboolean vkAvailable;
vkAvailable = VKGC_IsVKAvailable();
return vkAvailable;
}

View File

@@ -0,0 +1,466 @@
/*
* 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 <time.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.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 void
random_shm_name(char *buf) {
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
long r = ts.tv_nsec;
for (int i = 0; i < 6; ++i) {
buf[i] = (char)('A' + (r & 15) + (r & 16) * 2);
r >>= 5;
}
}
static int
create_shm_file() {
int retries = 100;
do {
char name[] = "/wl_shm_robot-XXXXXX";
random_shm_name(name + sizeof(name) - 7);
--retries;
int fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600);
if (fd >= 0) {
shm_unlink(name);
return fd;
}
} while (retries > 0 && errno == EEXIST);
return -1;
}
static int
allocate_shm_file(int size) {
const int fd = create_shm_file();
if (fd < 0)
return -1;
int ret;
do {
ret = ftruncate(fd, size);
} while (ret < 0 && errno == EINTR);
if (ret < 0) {
close(fd);
return -1;
}
return fd;
}
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;
}
int fd = allocate_shm_file(*size);
if (fd == -1) {
JNU_ThrowByName(env, "java/awt/AWTError", "couldn't open shared memory buffer file");
return NULL;
}
uint32_t *data = (uint32_t *) mmap(NULL, *size,
PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (data == MAP_FAILED) {
JNU_ThrowByName(env, "java/awt/AWTError", "couldn't mmap shared memory buffer");
close(fd);
return NULL;
}
struct wl_shm_pool *pool = wl_shm_create_pool(wl_shm, fd, *size);
struct wl_buffer *buffer = wl_shm_pool_create_buffer(
(struct wl_shm_pool*)pool, 0, width, height, stride, WL_SHM_FORMAT_XRGB8888);
*buffer_data = data;
wl_shm_pool_destroy(pool); // buffers keep references to the pool, can "destroy" now
close(fd); // the server will keep this open as long as needed
return buffer;
}
#endif // HEADLESS

View 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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,55 @@
/*
* 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 "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_shm *wl_shm;
extern struct wl_compositor *wl_compositor;
extern struct xdg_wm_base *xdg_wm_base;
extern uint32_t last_mouse_pressed_serial;
JNIEnv *getEnv();
int wl_flush_to_server(JNIEnv* env);

View File

@@ -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,
};

View File

@@ -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

View 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,
};

View File

@@ -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

View File

@@ -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); */

View File

@@ -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;
}

View 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");
}
}
}

View 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);
}
}
}

View 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();
}
}