Compare commits

...

25 Commits

Author SHA1 Message Date
Maxim Kartashev
d05fa193aa JBR-4621 Implemented key repeat 2022-09-30 13:22:41 +03:00
Maxim Kartashev
0c13996782 JBR-4621 Input events support for Wayland
This includes basic mouse and keyboard support.
2022-09-23 09:23:18 +03:00
Maxim Kartashev
221ea45b95 Let WLToolkit work with DISPLAY unset 2022-07-08 11:44:31 +03:00
Alexey Ushakov
d7a5df34ef Improved sun.awt.wl.WLGraphicsEnvironment to support createCraphics() 2022-06-30 09:54:52 +02:00
Maxim Kartashev
194bce3a6e 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-04-27 15:10:29 +03:00
Maxim Kartashev
87d83f4b27 Made it possible for Wayland tests to run in parallel
Also fixed a potential crash in getLocationOnScreen().
2022-04-26 19:27:03 +03:00
Maxim Kartashev
5f99c93eae Wayland test harness and sample test 2022-04-26 18:23:21 +03:00
Maxim Kartashev
7ed196e3c6 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-04-19 19:24:29 +03:00
Nikita Gubarkov
7836128343 Suppress unused-result warning for libfontmanager 2022-04-14 23:50:47 +03:00
nikita.gubarkov
62b72beede Text rendering support
Extracted X11-related code from libfontmanager into libfontmanager_xawt
2022-04-14 16:49:21 +03:00
Maxim Kartashev
77e472ba63 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-04-08 07:58:41 -07:00
Alexey Ushakov
2767704152 Added JFrame support 2022-04-06 21:03:31 +02:00
Alexey Ushakov
d352fe2b32 Fixed child hw component position 2022-03-31 22:04:37 +02:00
Alexey Ushakov
0e47c87515 Implemented heavyweight button rendering 2022-02-18 20:45:39 +01:00
Alexey Ushakov
297303bfa6 Moved native window management to WLComponentPeer 2022-02-15 22:45:06 +01:00
Alexey Ushakov
d5f8602c3b Added WLRepaintArea 2022-02-15 21:52:10 +01:00
Alexey Ushakov
dfd5f679e5 Refactored peers 2022-02-11 21:51:43 +01:00
Alexey Ushakov
5cc0bfb209 Added stubs for WLTK button peer 2022-02-11 20:00:46 +01:00
Alexey Ushakov
43d6b5012f Added 2d surface support 2022-02-11 20:00:22 +01:00
Alexey Ushakov
659ea0935c Added support for background color. Refactoring 2022-02-11 20:00:22 +01:00
Alexey Ushakov
eb90129dca Make simple awt window visible 2022-02-11 20:00:22 +01:00
Dmitry Batrak
89db5c8960 window showing and event loop prototype 2022-02-11 20:00:22 +01:00
Dmitry Batrak
cc8e7d1ee7 more stubbing for WLToolkit, add WLFramePeer 2022-02-11 20:00:22 +01:00
Dmitry Batrak
b359a00f63 more stubbing for WLToolkit 2022-02-11 20:00:21 +01:00
Alexey Ushakov
0c9bbd67d3 Created stub version of WLToolkit
A wayland base toolkit with native part linked to wayland-client library
2022-02-11 20:00:21 +01:00
51 changed files with 10642 additions and 38 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.java.net/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,20 +33,27 @@ m4_include([lib-std.m4])
m4_include([lib-x11.m4])
m4_include([lib-fontconfig.m4])
m4_include([lib-tests.m4])
m4_include([lib-wayland.m4])
################################################################################
# Determine which libraries are needed for this configuration
################################################################################
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
@@ -94,6 +101,7 @@ AC_DEFUN_ONCE([LIB_SETUP_LIBRARIES],
[
LIB_SETUP_STD_LIBS
LIB_SETUP_X11
LIB_SETUP_WAYLAND
LIB_SETUP_CUPS
LIB_SETUP_FONTCONFIG
LIB_SETUP_FREETYPE

View File

@@ -444,7 +444,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

@@ -191,7 +191,7 @@ ifeq ($(call isTargetOs, windows macosx), false)
common/font \
#
LIBAWT_XAWT_EXCLUDES := medialib debug
LIBAWT_XAWT_EXCLUDES := medialib debug wl
LIBAWT_XAWT_EXTRA_HEADER_DIRS := \
$(LIBAWT_DEFAULT_HEADER_DIRS) \
@@ -263,6 +263,83 @@ 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 \
#
# 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, \
CFLAGS := $(CFLAGS_JDKLIB) $(LIBAWT_WLAWT_CFLAGS), \
WARNINGS_AS_ERRORS_xlc := false, \
DISABLED_WARNINGS_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_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
@@ -486,13 +563,9 @@ BUILD_LIBFONTMANAGER_FONTLIB += $(LIBFREETYPE_LIBS)
LIBFONTMANAGER_OPTIMIZATION := HIGHEST
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 \
@@ -517,7 +590,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), \
@@ -547,6 +620,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

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

@@ -25,35 +25,113 @@
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();
}
/**
* Called from java.awt.GraphicsEnvironment when
* to check if on this platform, the JDK should default to
* headless mode, in the case the application did specify
* headless mode, in the case the application did not specify
* a value for the java.awt.headless system property.
*/
@SuppressWarnings("removal")
public static boolean getDefaultHeadlessProperty() {
return
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;
}
/*
* If we positively identify a separate headless library support being present
* but no corresponding headful library, then we can support headless but
* not headful, so report that back to the caller.
* This does require duplication of knowing the name of the libraries
* also in libawt's OnLoad() but we need to make sure that the Java
* code is also set up as headless from the start - it is not so easy
* to try headful and then unwind that and then retry as headless.
*/
boolean headless =
AccessController.doPrivileged((PrivilegedAction<Boolean>) () -> {
String[] libDirs = System.getProperty("sun.boot.library.path", "").split(":");
for (String libDir : libDirs) {
File headlessLib = new File(libDir, "libawt_headless.so");
File xawtLib = new File(libDir, "libawt_xawt.so");
if (headlessLib.exists() && !xawtLib.exists()) {
return true;
}
}
return false;
});
return headless;
}
/**
@@ -63,7 +141,11 @@ public class PlatformGraphicsInfo {
*/
public static String getDefaultHeadlessMessage() {
return
"\nNo X11 DISPLAY variable was set,\n" +
"but this program performed an operation which requires it.";
"""
No X11 DISPLAY variable was set,
or no headful library support was found,
but this program performed an operation which requires it,
""";
}
}

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,610 @@
/*
* 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.SunToolkit;
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.AWTEvent;
import java.awt.AWTException;
import java.awt.BufferCapabilities;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.GraphicsConfiguration;
import java.awt.Image;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.SystemColor;
import java.awt.Toolkit;
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 long nativePtr;
private static final PlatformLogger log = PlatformLogger.getLogger("sun.awt.wl.WLComponentPeer");
protected final Component target;
protected WLGraphicsConfig graphicsConfig;
protected Color background;
SurfaceData surfaceData;
WLRepaintArea paintArea;
boolean paintPending = false;
boolean isLayouting = false;
boolean visible = false;
int x;
int y;
int width;
int height;
// used to check if we need to re-create surfaceData.
int oldWidth = -1;
int oldHeight = -1;
static {
initIDs();
}
/**
* Standard peer constructor, with corresponding Component
*/
WLComponentPeer(Component target) {
this.target = target;
this.background = target.getBackground();
initGraphicsConfiguration();
this.surfaceData = graphicsConfig.createSurfaceData(this);
this.nativePtr = nativeCreateFrame();
paintArea = new WLRepaintArea();
Rectangle bounds = target.getBounds();
x = bounds.x;
y = bounds.y;
width = bounds.width;
height = bounds.height;
log.info("WLComponentPeer: target=" + target + " x=" + x + " y=" + y +
" width=" + width + " height=" + height);
// TODO
// setup parent window for target
}
int getWidth() {
return width;
}
int getHeight() {
return height;
}
public final void repaint(int x, int y, int width, int height) {
if (!isVisible() || getWidth() == 0 || getHeight() == 0) {
return;
}
Graphics g = getGraphics();
if (g != null) {
try {
g.setClip(x, y, width, height);
if (SunToolkit.isDispatchThreadForAppContext(getTarget())) {
paint(g); // The native and target will be painted in place.
} else {
paintPeer(g);
postPaintEvent(target, x, y, width, height);
}
} finally {
g.dispose();
}
}
}
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()) {
PaintEvent pe = new PaintEvent(target, PaintEvent.PAINT,
new Rectangle(0, 0, width, height));
WLToolkit.postEvent(pe);
}
}
boolean isVisible() {
return visible;
}
void repaint() {
repaint(0, 0, getWidth(), getHeight());
}
@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 true;
}
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) {
nativeShowComponent(nativePtr, getParentNativePtr(target), target.getX(), target.getY());
WLToolkit.registerWLSurface(getWLSurface(), this);
((WLSurfaceData) surfaceData).initSurface(this, background != null ? background.getRGB() : 0, target.getWidth(), target.getHeight());
PaintEvent event = PaintEventDispatcher.getPaintEventDispatcher().
createPaintEvent(target, 0, 0, target.getWidth(), target.getHeight());
if (event != null) {
WLToolkit.postEvent(WLToolkit.targetToAppContext(event.getSource()), event);
}
} else {
WLToolkit.unregisterWLSurface(getWLSurface());
nativeHideFrame(nativePtr);
}
}
@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) {
log.info("Not implemented: WLComponentPeer.paintPeer(Graphics)");
}
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());
}
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) {
if (this.x != x || this.y != y) {
WLRobotPeer.setLocationOfWLSurface(getWLSurface(), x, y);
}
this.x = x;
this.y = y;
this.width = width;
this.height = height;
validateSurface();
layout();
}
void validateSurface() {
if ((width != oldWidth) || (height != oldHeight)) {
doValidateSurface();
oldWidth = width;
oldHeight = height;
}
}
final void doValidateSurface() {
SurfaceData oldData = surfaceData;
if (oldData != null) {
surfaceData = graphicsConfig.createSurfaceData(this);
oldData.invalidate();
}
}
public void coalescePaintEvent(PaintEvent e) {
Rectangle r = e.getUpdateRect();
if (!(e instanceof IgnorePaintEvent)) {
paintArea.add(r, e.getID());
}
if (true) {
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);
}
return;
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);
}
return;
}
}
}
@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();
}
@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;
}
@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) {
throw new UnsupportedOperationException();
}
@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();
}
private static native void initIDs();
protected native long nativeCreateFrame();
protected native void nativeShowComponent(long ptr, long parentPtr, int x, int y);
protected native void nativeHideFrame(long ptr);
protected native void nativeDisposeFrame(long ptr);
private native long getWLSurface();
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;
}
}

View File

@@ -0,0 +1,188 @@
/*
* 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.Component;
import java.awt.Window;
import java.awt.Dialog;
import java.awt.Frame;
import java.awt.Insets;
import java.awt.MenuBar;
import java.awt.Rectangle;
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");
public WLFramePeer(Frame target) {
super(target);
}
@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);
}
}
}
private static native void initIDs();
@Override
public Insets getInsets() {
return new Insets(0, 0, 0, 0);
}
@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) {
throw new UnsupportedOperationException();
}
@Override
public void setMenuBar(MenuBar mb) {
throw new UnsupportedOperationException();
}
@Override
public void setResizable(boolean resizeable) {
throw new UnsupportedOperationException();
}
@Override
public void setState(int state) {
throw new UnsupportedOperationException();
}
@Override
public int getState() {
throw new UnsupportedOperationException();
}
@Override
public void setMaximizedBounds(Rectangle bounds) {
}
@Override
public void setBoundsPrivate(int x, int y, int width, int height) {
throw new UnsupportedOperationException();
}
@Override
public Rectangle getBoundsPrivate() {
throw new UnsupportedOperationException();
}
@Override
public void emulateActivation(boolean activate) {
throw new UnsupportedOperationException();
}
@Override
public void toFront() {
throw new UnsupportedOperationException();
}
@Override
public void toBack() {
throw new UnsupportedOperationException();
}
@Override
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() {
throw new UnsupportedOperationException();
}
@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() {
throw new UnsupportedOperationException();
}
@Override
public void repositionSecurityWarning() {
throw new UnsupportedOperationException();
}
// called from native code
private void postWindowClosing() {
WLToolkit.postEvent(new WindowEvent((Window) target, WindowEvent.WINDOW_CLOSING));
}
}

View File

@@ -0,0 +1,63 @@
package sun.awt.wl;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.Rectangle;
import java.awt.geom.AffineTransform;
import java.awt.image.ColorModel;
import java.awt.image.DirectColorModel;
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) {
throw new UnsupportedOperationException();
}
@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,34 @@
package sun.awt.wl;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
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;
}
public double getScaleFactor() {
return scale;
}
}

View File

@@ -0,0 +1,31 @@
package sun.awt.wl;
import java.awt.GraphicsDevice;
import sun.java2d.SunGraphicsEnvironment;
import sun.java2d.SurfaceManagerFactory;
import sun.java2d.UnixSurfaceManagerFactory;
import sun.util.logging.PlatformLogger;
public class WLGraphicsEnvironment extends SunGraphicsEnvironment {
private static final PlatformLogger log = PlatformLogger.getLogger("sun.awt.wl.WLComponentPeer");
static {
SurfaceManagerFactory.setInstance(new UnixSurfaceManagerFactory());
}
@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)");
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,51 @@
package sun.awt.wl;
import sun.awt.KeyboardFocusManagerPeerImpl;
import sun.util.logging.PlatformLogger;
import java.awt.Component;
import java.awt.Window;
import java.awt.peer.KeyboardFocusManagerPeer;
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 != null) {
// In Wayland, only Window can be focused, not any widget in it.
focusLog.severe("Attempt to focus on a component 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,70 @@
/*
* 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.Component;
import java.awt.Graphics;
import sun.awt.AWTAccessor;
import sun.awt.RepaintArea;
import sun.awt.X11.XComponentPeer;
/**
* 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);
if (peer != null) {
peer.paintPeer(g);
}
super.paintComponent(comp, g);
}
}
}

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

File diff suppressed because it is too large Load Diff

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,96 @@
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 initSurface(WLComponentPeer peer, int rgb, int width, int height);
protected native void initOps(WLComponentPeer peer, WLGraphicsConfig gc, int depth);
protected WLSurfaceData(WLComponentPeer peer,
WLGraphicsConfig gc,
SurfaceType sType,
ColorModel cm)
{
super(sType, cm);
this.peer = peer;
this.graphicsConfig = gc;
this.depth = cm.getPixelSize();
initOps(peer, graphicsConfig, depth);
}
/**
* 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 null;
}
@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;
}
}

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,292 @@
/*
* 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 <wayland-client.h>
#include "jni.h"
#include "jni_util.h"
#include "Trace.h"
#include "WLSurfaceData.h"
extern struct wl_shm *wl_shm;
//#undef HEADLESS
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;
struct wl_buffer* 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 */
}
#ifndef HEADLESS
static void
wl_buffer_release(void *data, struct wl_buffer *wl_buffer) {
/* Sent by the compositor when it's no longer using this buffer */
wl_buffer_destroy(wl_buffer);
}
static const struct wl_buffer_listener wl_buffer_listener = {
.release = wl_buffer_release,
};
/* Shared memory support code (from https://wayland-book.com/) */
static void randname(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 create_shm_file() {
int retries = 100;
do {
char name[] = "/wl_shm-XXXXXX";
randname(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(size_t size) {
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;
}
#endif
/*
* Class: sun_java2d_wl_WLSurfaceData
* Method: initSurface
* Signature: (IIIJ)V
*/
JNIEXPORT void JNICALL
Java_sun_java2d_wl_WLSurfaceData_initSurface(JNIEnv *env, jobject wsd,
jobject peer,
jint rgb,
jint width, jint height)
{
#ifndef HEADLESS
J2dTrace6(J2D_TRACE_INFO, "WLSurfaceData_initSurface: %dx%d, rgba=(%d,%d,%d,%d)\n",
width, height, rgb&0xff, (rgb>>8)&0xff, (rgb>>16)&0xff, (rgb>>24)&0xff);
WLSDOps *wsdo = (WLSDOps*)SurfaceData_GetOps(env, wsd);
if (wsdo == NULL) {
return;
}
jboolean hasException;
if (!wsdo->wlSurface) {
wsdo->wlSurface = JNU_CallMethodByName(env, &hasException, peer, "getWLSurface", "()J").j;
}
if (width <= 0) {
width = 1;
}
if (height <= 0) {
height = 1;
}
int stride = width * 4;
int size = stride * height;
wsdo->fd = allocate_shm_file(size);
if (wsdo->fd == -1) {
return;
}
wsdo->data = (uint32_t *) (mmap(NULL, size,
PROT_READ | PROT_WRITE, MAP_SHARED, wsdo->fd, 0));
if (wsdo->data == MAP_FAILED) {
close(wsdo->fd);
return;
}
wsdo->dataSize = size;
wsdo->width = width;
wsdo->height = height;
for (int i = 0; i < height*width; ++i) {
wsdo->data[i] = rgb;
}
wsdo->wlShmPool = (jlong)wl_shm_create_pool(wl_shm, wsdo->fd, size);
wsdo->wlBuffer = (jlong) wl_shm_pool_create_buffer(
(struct wl_shm_pool*)wsdo->wlShmPool, 0, width, height, stride, WL_SHM_FORMAT_XRGB8888);
wl_surface_attach((struct wl_surface*)wsdo->wlSurface, (struct wl_buffer*)wsdo->wlBuffer, 0, 0);
wl_surface_commit((struct wl_surface*)wsdo->wlSurface);
#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;
if (priv->lockFlags & SD_LOCK_WRITE) {
pRasInfo->rasBase = wlso->data;
pRasInfo->pixelStride = 4;
pRasInfo->pixelBitOffset = 0;
pRasInfo->scanStride = 4 * wlso->width;
}
#endif
return SD_SUCCESS;
}
static void WLSD_GetRasInfo(JNIEnv *env,
SurfaceDataOps *ops,
SurfaceDataRasInfo *pRasInfo)
{
#ifndef HEADLESS
WLSDPrivate *priv = (WLSDPrivate *) &(pRasInfo->priv);
WLSDOps *wlso = (WLSDOps*)ops;
logWSDOp("WLSD_GetRasInfo", wlso, priv->lockFlags);
if (priv->lockFlags & SD_LOCK_WRITE) {
wl_surface_damage ((struct wl_surface*)wlso->wlSurface, 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);
wl_surface_commit((struct wl_surface*)wsdo->wlSurface);
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;
const bool surfaceFullyInitialized = (wsdo->wlShmPool && wsdo->wlBuffer);
if (surfaceFullyInitialized) {
close(wsdo->fd);
wsdo->fd = 0;
munmap(wsdo->data, wsdo->dataSize);
wl_shm_pool_destroy((struct wl_shm_pool *) wsdo->wlShmPool);
wl_buffer_add_listener((struct wl_buffer*)wsdo->wlBuffer, &wl_buffer_listener, 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,
jobject peer,
jobject graphicsConfig, jint depth)
{
#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;
}
wsdo->sdOps.Lock = WLSD_Lock;
wsdo->sdOps.Unlock = WLSD_Unlock;
wsdo->sdOps.GetRasInfo = WLSD_GetRasInfo;
wsdo->sdOps.Dispose = WLSD_Dispose;
wsdo->wlSurface = 0;
wsdo->bgPixel = 0;
wsdo->isBgInitialized = JNI_FALSE;
pthread_mutex_init(&wsdo->lock, NULL);
#endif /* !HEADLESS */
}

View File

@@ -0,0 +1,68 @@
/*
* 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;
struct _WLSDOps {
SurfaceDataOps sdOps;
jboolean invalid;
jobject peer;
jlong wlSurface;
jlong wlBuffer;
jlong wlShmPool;
int fd;
uint32_t *data;
jint dataSize;
jint width;
jint height;
jint bgPixel;
jint pixelStride;
pthread_mutex_t lock;
jboolean isBgInitialized;
};
/*
* 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,207 @@
/*
* 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 "jni_util.h"
#include "WLToolkit.h"
#include "WLRobotPeer.h"
#ifdef WAKEFIELD_ROBOT
#include "wakefield-client-protocol.h"
#endif
jfieldID nativePtrID;
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;
};
};
static void xdg_surface_configure(void *data,
struct xdg_surface *xdg_surface, uint32_t serial) {
xdg_surface_ack_configure(xdg_surface, serial);
}
static const struct xdg_surface_listener xdg_surface_listener = {
.configure = xdg_surface_configure,
};
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);
}
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();
jobject nativeFramePeer = (*env)->NewLocalRef(env, frame->nativeFramePeer);
if (nativeFramePeer) {
static jclass wlFramePeerCID = NULL;
if (!wlFramePeerCID) {
wlFramePeerCID = (*env)->FindClass(env, "sun/awt/wl/WLFramePeer");
}
static jmethodID postWindowClosingMID = NULL;
if (!postWindowClosingMID) {
postWindowClosingMID = (*env)->GetMethodID(env, wlFramePeerCID, "postWindowClosing", "()V");
}
(*env)->CallVoidMethod(env, nativeFramePeer, postWindowClosingMID);
(*env)->DeleteLocalRef(env, nativeFramePeer);
}
}
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"));
}
JNIEXPORT jlong JNICALL
Java_sun_awt_wl_WLComponentPeer_nativeCreateFrame
(JNIEnv *env, jobject obj)
{
struct WLFrame *frame = (struct WLFrame *) malloc(sizeof(struct WLFrame));
frame->nativeFramePeer = (*env)->NewWeakGlobalRef(env, obj);
frame->wl_surface = NULL;
frame->xdg_surface = NULL;
frame->toplevel = JNI_FALSE;
frame->xdg_popup = NULL;
return (jlong)frame;
}
JNIEXPORT void JNICALL
Java_sun_awt_wl_WLComponentPeer_nativeShowComponent
(JNIEnv *env, jobject obj, jlong ptr, jlong parentPtr, jint x, jint y)
{
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);
xdg_surface_add_listener(frame->xdg_surface, &xdg_surface_listener, NULL);
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);
} 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);
}
wl_surface_commit(frame->wl_surface);
wl_display_roundtrip(wl_display); // this should process 'configure' event, and send 'ack_configure' in response
#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
}
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;
}

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,44 @@
/*
* 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) \
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;
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();
}
}