diff --git a/make/modules/java.desktop/gensrc/GensrcWayland.gmk b/make/modules/java.desktop/gensrc/GensrcWayland.gmk index 32cbae05e25e..9ed8f210d065 100644 --- a/make/modules/java.desktop/gensrc/GensrcWayland.gmk +++ b/make/modules/java.desktop/gensrc/GensrcWayland.gmk @@ -33,6 +33,7 @@ WAYLAND_BASIC_PROTOCOL_FILES := \ $(WAYLAND_PROTOCOLS_ROOT)/stable/viewporter/viewporter.xml \ $(WAYLAND_PROTOCOLS_ROOT)/stable/xdg-shell/xdg-shell.xml \ $(WAYLAND_PROTOCOLS_ROOT)/staging/xdg-activation/xdg-activation-v1.xml \ + $(WAYLAND_PROTOCOLS_ROOT)/staging/xdg-toplevel-icon/xdg-toplevel-icon-v1.xml \ $(WAYLAND_PROTOCOLS_ROOT)/unstable/primary-selection/primary-selection-unstable-v1.xml \ $(WAYLAND_PROTOCOLS_ROOT)/unstable/xdg-output/xdg-output-unstable-v1.xml \ $(WAYLAND_PROTOCOLS_ROOT)/unstable/relative-pointer/relative-pointer-unstable-v1.xml \ diff --git a/src/java.desktop/unix/classes/sun/awt/wl/WLComponentPeer.java b/src/java.desktop/unix/classes/sun/awt/wl/WLComponentPeer.java index 124a7e9793c7..c6940b003d88 100644 --- a/src/java.desktop/unix/classes/sun/awt/wl/WLComponentPeer.java +++ b/src/java.desktop/unix/classes/sun/awt/wl/WLComponentPeer.java @@ -899,6 +899,10 @@ public class WLComponentPeer implements ComponentPeer, WLSurfaceSizeListener { performLocked(() -> nativeShowWindowMenu(serial, nativePtr, xNative, yNative)); } + void setIcon(int size, int[] pixels) { + performLocked(() -> nativeSetIcon(nativePtr, size, pixels)); + } + @Override public ColorModel getColorModel() { GraphicsConfiguration graphicsConfig = target.getGraphicsConfiguration(); @@ -1167,6 +1171,7 @@ public class WLComponentPeer implements ComponentPeer, WLSurfaceSizeListener { private native void nativeSetMinimumSize(long ptr, int width, int height); private native void nativeSetMaximumSize(long ptr, int width, int height); private native void nativeShowWindowMenu(long serial, long ptr, int x, int y); + private native void nativeSetIcon(long ptr, int size, int[] pixels); static long getNativePtrFor(Component component) { final ComponentAccessor acc = AWTAccessor.getComponentAccessor(); diff --git a/src/java.desktop/unix/classes/sun/awt/wl/WLToolkit.java b/src/java.desktop/unix/classes/sun/awt/wl/WLToolkit.java index 0e2bded3fb4c..cbab8a45e392 100644 --- a/src/java.desktop/unix/classes/sun/awt/wl/WLToolkit.java +++ b/src/java.desktop/unix/classes/sun/awt/wl/WLToolkit.java @@ -84,6 +84,7 @@ import java.awt.peer.WindowPeer; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.Arrays; +import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -142,6 +143,11 @@ public class WLToolkit extends UNIXToolkit implements Runnable { private static final boolean isKDE; + // NOTE: xdg_toplevel_icon_v1 is pretty much only supported on KDE, and KWin always sends 96px as the icon size, + // regardless of the display resolution, scale, or anything else. + // TODO: this is currently unused + private static final java.util.List preferredIconSizes = new ArrayList<>(); + private static native void initIDs(long displayPtr); static { @@ -1120,4 +1126,9 @@ public class WLToolkit extends UNIXToolkit implements Runnable { public static boolean isKDE() { return isKDE; } + + // called from native + private static void handleToplevelIconSize(int size) { + preferredIconSizes.add(size); + } } diff --git a/src/java.desktop/unix/classes/sun/awt/wl/WLWindowPeer.java b/src/java.desktop/unix/classes/sun/awt/wl/WLWindowPeer.java index c57edc7e3463..67c1542b8aab 100644 --- a/src/java.desktop/unix/classes/sun/awt/wl/WLWindowPeer.java +++ b/src/java.desktop/unix/classes/sun/awt/wl/WLWindowPeer.java @@ -39,7 +39,9 @@ import java.awt.Color; import java.awt.Component; import java.awt.Dialog; import java.awt.Font; +import java.awt.Graphics2D; import java.awt.GraphicsConfiguration; +import java.awt.Image; import java.awt.Insets; import java.awt.Rectangle; import java.awt.RenderingHints; @@ -51,6 +53,7 @@ import java.awt.image.BufferedImage; import java.awt.peer.ComponentPeer; import java.awt.peer.WindowPeer; import java.lang.ref.WeakReference; +import java.util.List; public class WLWindowPeer extends WLComponentPeer implements WindowPeer, SurfacePixelGrabber { private static Font defaultFont; @@ -180,7 +183,38 @@ public class WLWindowPeer extends WLComponentPeer implements WindowPeer, Surface @Override public void updateIconImages() { - // No support for this from Wayland, icon is a desktop integration feature. + List iconImages = getWindow().getIconImages(); + if (iconImages == null || iconImages.isEmpty()) { + setIcon(0, null); + return; + } + + Image image = iconImages.stream() + .filter(x -> x.getWidth(null) > 0 && x.getHeight(null) > 0) + .filter(x -> x.getWidth(null) == x.getHeight(null)) + .max((a, b) -> Integer.compare(a.getWidth(null), b.getWidth(null))) + .orElse(null); + if (image == null) { + return; + } + + int width = image.getWidth(null); + int height = image.getHeight(null); + int size = width; + + BufferedImage bufferedImage; + if (image instanceof BufferedImage && ((BufferedImage) image).getType() == BufferedImage.TYPE_INT_ARGB) { + bufferedImage = (BufferedImage) image; + } else { + bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); + Graphics2D g = bufferedImage.createGraphics(); + g.drawImage(image, 0, 0, null); + g.dispose(); + } + + int[] pixels = new int[width * height]; + bufferedImage.getRGB(0, 0, width, height, pixels, 0, width); + setIcon(size, pixels); } @Override diff --git a/src/java.desktop/unix/native/libawt_wlawt/WLComponentPeer.c b/src/java.desktop/unix/native/libawt_wlawt/WLComponentPeer.c index e5e91b052286..aacf555b3fd7 100644 --- a/src/java.desktop/unix/native/libawt_wlawt/WLComponentPeer.c +++ b/src/java.desktop/unix/native/libawt_wlawt/WLComponentPeer.c @@ -36,6 +36,8 @@ #include "WLGraphicsEnvironment.h" #include "xdg-decoration-unstable-v1.h" +#include + #ifdef WAKEFIELD_ROBOT #include "wakefield.h" #endif @@ -67,6 +69,9 @@ struct WLFrame { jboolean configuredActive; jboolean configuredMaximized; jboolean configuredFullscreen; + + struct wl_buffer* iconBuffer; + struct wl_shm_pool* iconPool; }; static void @@ -571,3 +576,57 @@ JNIEXPORT void JNICALL Java_sun_awt_wl_ServerSideFrameDecoration_disposeImpl } } +JNIEXPORT void JNICALL Java_sun_awt_wl_WLComponentPeer_nativeSetIcon + (JNIEnv *env, jobject obj, jlong ptr, jint size, jobject pixelArray) +{ + struct WLFrame *frame = jlong_to_ptr(ptr); + if (frame == NULL || !frame->toplevel || xdg_toplevel_icon_manager == NULL) { + return; + } + + bool hasIcon = frame->iconBuffer != NULL; + bool willHaveIcon = size > 0 && pixelArray != NULL; + size_t iconByteSize = size * size * 4; + + if (!willHaveIcon) { + xdg_toplevel_icon_manager_v1_set_icon(xdg_toplevel_icon_manager, frame->xdg_toplevel, NULL); + } + + if (hasIcon) { + if (frame->iconBuffer != NULL) { + wl_buffer_destroy(frame->iconBuffer); + frame->iconBuffer = NULL; + } + if (frame->iconPool != NULL) { + wl_shm_pool_destroy(frame->iconPool); + frame->iconPool = NULL; + } + } + + if (willHaveIcon) { + void* poolData = NULL; + struct wl_shm_pool* pool = CreateShmPool(iconByteSize, "toplevel_icon", &poolData, NULL); + if (pool == NULL) { + return; + } + (*env)->GetIntArrayRegion(env, pixelArray, 0, size * size, poolData); + struct wl_buffer* buffer = wl_shm_pool_create_buffer(pool, 0, size, size, size * 4, WL_SHM_FORMAT_ARGB8888); + if (buffer == NULL) { + wl_shm_pool_destroy(pool); + return; + } + + struct xdg_toplevel_icon_v1* icon = xdg_toplevel_icon_manager_v1_create_icon(xdg_toplevel_icon_manager); + if (icon == NULL) { + wl_buffer_destroy(buffer); + wl_shm_pool_destroy(pool); + return; + } + xdg_toplevel_icon_v1_add_buffer(icon, buffer, 1); + xdg_toplevel_icon_manager_v1_set_icon(xdg_toplevel_icon_manager, frame->xdg_toplevel, icon); + xdg_toplevel_icon_v1_destroy(icon); + + frame->iconPool = pool; + frame->iconBuffer = buffer; + } +} diff --git a/src/java.desktop/unix/native/libawt_wlawt/WLToolkit.c b/src/java.desktop/unix/native/libawt_wlawt/WLToolkit.c index d7205cb0a57b..02542720e9b1 100644 --- a/src/java.desktop/unix/native/libawt_wlawt/WLToolkit.c +++ b/src/java.desktop/unix/native/libawt_wlawt/WLToolkit.c @@ -93,8 +93,10 @@ struct zwp_primary_selection_device_manager_v1 *zwp_selection_dm = NULL; // opti struct zxdg_output_manager_v1 *zxdg_output_manager_v1 = NULL; // optional, check for NULL before use struct zwp_text_input_manager_v3 *zwp_text_input_manager = NULL; // optional, check for NULL before use +struct xdg_toplevel_icon_manager_v1 *xdg_toplevel_icon_manager; // optional, check for NULL before use static uint32_t num_of_outstanding_sync = 0; +static bool waiting_for_xdg_toplevel_icon_manager_done = false; // This group of definitions corresponds to declarations from awt.h jclass tkClass = NULL; @@ -135,6 +137,7 @@ static jmethodID dispatchKeyboardModifiersEventMID; static jmethodID dispatchKeyboardEnterEventMID; static jmethodID dispatchKeyboardLeaveEventMID; static jmethodID dispatchRelativePointerEventMID; +static jmethodID handleToplevelIconSizeMID; JNIEnv *getEnv() { JNIEnv *env; @@ -570,6 +573,36 @@ static const struct wl_seat_listener wl_seat_listener = { .name = wl_seat_name }; +static void +xdg_toplevel_icon_manager_icon_size(void *data, + struct xdg_toplevel_icon_manager_v1 *xdg_toplevel_icon_manager_v1, + int32_t size) +{ + (void)data; + (void)xdg_toplevel_icon_manager_v1; + JNIEnv* env = getEnv(); + if (env == NULL) { + return; + } + + (*env)->CallStaticVoidMethod(env, tkClass, handleToplevelIconSizeMID, size); + JNU_CHECK_EXCEPTION(env); +} + +static void +xdg_toplevel_icon_manager_icon_size_done(void *data, + struct xdg_toplevel_icon_manager_v1 *xdg_toplevel_icon_manager_v1) +{ + (void)data; + (void)xdg_toplevel_icon_manager_v1; + waiting_for_xdg_toplevel_icon_manager_done = false; +} + +static const struct xdg_toplevel_icon_manager_v1_listener xdg_toplevel_icon_manager_v1_listener = { + .icon_size = xdg_toplevel_icon_manager_icon_size, + .done = xdg_toplevel_icon_manager_icon_size_done, +}; + static void registry_global(void *data, struct wl_registry *wl_registry, uint32_t name, const char *interface, uint32_t version) @@ -636,6 +669,12 @@ registry_global(void *data, struct wl_registry *wl_registry, } } else if (strcmp(interface, zxdg_decoration_manager_v1_interface.name) == 0) { xdg_decoration_manager = wl_registry_bind(wl_registry, name, &zxdg_decoration_manager_v1_interface, 1); + } else if (strcmp(interface, xdg_toplevel_icon_manager_v1_interface.name) == 0) { + xdg_toplevel_icon_manager = wl_registry_bind(wl_registry, name, &xdg_toplevel_icon_manager_v1_interface, 1); + if (xdg_toplevel_icon_manager != NULL) { + waiting_for_xdg_toplevel_icon_manager_done = true; + xdg_toplevel_icon_manager_v1_add_listener(xdg_toplevel_icon_manager, &xdg_toplevel_icon_manager_v1_listener, NULL); + } } #ifdef WAKEFIELD_ROBOT @@ -710,6 +749,11 @@ initJavaRefs(JNIEnv *env, jclass clazz) "(Lsun/awt/wl/WLPointerEvent;)V"), JNI_FALSE); + CHECK_NULL_RETURN(handleToplevelIconSizeMID = (*env)->GetStaticMethodID(env, tkClass, + "handleToplevelIconSize", + "(I)V"), + JNI_FALSE); + CHECK_NULL_RETURN(pointerEventClass = (*env)->FindClass(env, "sun/awt/wl/WLPointerEvent"), JNI_FALSE); @@ -844,7 +888,7 @@ getCursorTheme(int scale) { static void finalizeInit(JNIEnv *env) { // NB: we are NOT on EDT here so shouldn't dispatch EDT-sensitive stuff - while (num_of_outstanding_sync > 0) { + while (num_of_outstanding_sync > 0 || waiting_for_xdg_toplevel_icon_manager_done) { // There are outstanding events that carry information essential for the toolkit // to be fully operational, such as, for example, the number of outputs. // Those events were subscribed to when handling globals in registry_global(). diff --git a/src/java.desktop/unix/native/libawt_wlawt/WLToolkit.h b/src/java.desktop/unix/native/libawt_wlawt/WLToolkit.h index e4d01b2a6e47..a4cd8f5720d5 100644 --- a/src/java.desktop/unix/native/libawt_wlawt/WLToolkit.h +++ b/src/java.desktop/unix/native/libawt_wlawt/WLToolkit.h @@ -33,7 +33,7 @@ #include "relative-pointer-unstable-v1.h" #include "text-input-unstable-v3.h" #include "xdg-decoration-unstable-v1.h" - +#include "xdg-toplevel-icon-v1.h" #include "jvm_md.h" #include "jni_util.h" @@ -73,6 +73,7 @@ extern struct zxdg_output_manager_v1 *zxdg_output_manager_v1; // optional, check extern struct zwp_relative_pointer_manager_v1* relative_pointer_manager; extern struct zwp_text_input_manager_v3 *zwp_text_input_manager; // optional, check for NULL before use extern struct zxdg_decoration_manager_v1* xdg_decoration_manager; // optional, check for NULL before use +extern struct xdg_toplevel_icon_manager_v1 *xdg_toplevel_icon_manager; // optional, check for NULL before use JNIEnv *getEnv();