Skip to content

Commit 621e901

Browse files
tsarnjbrbot
authored andcommitted
JBR-9483 Wayland: Support toplevel icons
This patch implements support for the xdg_toplevel_icon_v1 protocol. The image choosing logic is just to pick the largest square image for now. The image scale factor is also not set, since it's unclear if it's needed and how it interacts with multi-monitor setups. NOTE: this patch introduces a dependency on wayland-protocols 1.37+.
1 parent a7a5fe8 commit 621e901

File tree

7 files changed

+158
-3
lines changed

7 files changed

+158
-3
lines changed

make/modules/java.desktop/gensrc/GensrcWayland.gmk

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ WAYLAND_BASIC_PROTOCOL_FILES := \
3333
$(WAYLAND_PROTOCOLS_ROOT)/stable/viewporter/viewporter.xml \
3434
$(WAYLAND_PROTOCOLS_ROOT)/stable/xdg-shell/xdg-shell.xml \
3535
$(WAYLAND_PROTOCOLS_ROOT)/staging/xdg-activation/xdg-activation-v1.xml \
36+
$(WAYLAND_PROTOCOLS_ROOT)/staging/xdg-toplevel-icon/xdg-toplevel-icon-v1.xml \
3637
$(WAYLAND_PROTOCOLS_ROOT)/unstable/primary-selection/primary-selection-unstable-v1.xml \
3738
$(WAYLAND_PROTOCOLS_ROOT)/unstable/xdg-output/xdg-output-unstable-v1.xml \
3839
$(WAYLAND_PROTOCOLS_ROOT)/unstable/relative-pointer/relative-pointer-unstable-v1.xml \

src/java.desktop/unix/classes/sun/awt/wl/WLComponentPeer.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -899,6 +899,10 @@ void showWindowMenu(long serial, int x, int y) {
899899
performLocked(() -> nativeShowWindowMenu(serial, nativePtr, xNative, yNative));
900900
}
901901

902+
void setIcon(int size, int[] pixels) {
903+
performLocked(() -> nativeSetIcon(nativePtr, size, pixels));
904+
}
905+
902906
@Override
903907
public ColorModel getColorModel() {
904908
GraphicsConfiguration graphicsConfig = target.getGraphicsConfiguration();
@@ -1167,6 +1171,7 @@ protected native void nativeRepositionWLPopup(long ptr,
11671171
private native void nativeSetMinimumSize(long ptr, int width, int height);
11681172
private native void nativeSetMaximumSize(long ptr, int width, int height);
11691173
private native void nativeShowWindowMenu(long serial, long ptr, int x, int y);
1174+
private native void nativeSetIcon(long ptr, int size, int[] pixels);
11701175

11711176
static long getNativePtrFor(Component component) {
11721177
final ComponentAccessor acc = AWTAccessor.getComponentAccessor();

src/java.desktop/unix/classes/sun/awt/wl/WLToolkit.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@
8484
import java.security.AccessController;
8585
import java.security.PrivilegedAction;
8686
import java.util.Arrays;
87+
import java.util.ArrayList;
8788
import java.util.Collections;
8889
import java.util.HashMap;
8990
import java.util.Map;
@@ -142,6 +143,11 @@ public class WLToolkit extends UNIXToolkit implements Runnable {
142143

143144
private static final boolean isKDE;
144145

146+
// NOTE: xdg_toplevel_icon_v1 is pretty much only supported on KDE, and KWin always sends 96px as the icon size,
147+
// regardless of the display resolution, scale, or anything else.
148+
// TODO: this is currently unused
149+
private static final java.util.List<Integer> preferredIconSizes = new ArrayList<>();
150+
145151
private static native void initIDs(long displayPtr);
146152

147153
static {
@@ -1120,4 +1126,9 @@ public static WLCursorManager getCursorManager() {
11201126
public static boolean isKDE() {
11211127
return isKDE;
11221128
}
1129+
1130+
// called from native
1131+
private static void handleToplevelIconSize(int size) {
1132+
preferredIconSizes.add(size);
1133+
}
11231134
}

src/java.desktop/unix/classes/sun/awt/wl/WLWindowPeer.java

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,9 @@
3939
import java.awt.Component;
4040
import java.awt.Dialog;
4141
import java.awt.Font;
42+
import java.awt.Graphics2D;
4243
import java.awt.GraphicsConfiguration;
44+
import java.awt.Image;
4345
import java.awt.Insets;
4446
import java.awt.Rectangle;
4547
import java.awt.RenderingHints;
@@ -51,6 +53,7 @@
5153
import java.awt.peer.ComponentPeer;
5254
import java.awt.peer.WindowPeer;
5355
import java.lang.ref.WeakReference;
56+
import java.util.List;
5457

5558
public class WLWindowPeer extends WLComponentPeer implements WindowPeer, SurfacePixelGrabber {
5659
private static Font defaultFont;
@@ -180,7 +183,38 @@ public void updateMinimumSize() {
180183

181184
@Override
182185
public void updateIconImages() {
183-
// No support for this from Wayland, icon is a desktop integration feature.
186+
List<Image> iconImages = getWindow().getIconImages();
187+
if (iconImages == null || iconImages.isEmpty()) {
188+
setIcon(0, null);
189+
return;
190+
}
191+
192+
Image image = iconImages.stream()
193+
.filter(x -> x.getWidth(null) > 0 && x.getHeight(null) > 0)
194+
.filter(x -> x.getWidth(null) == x.getHeight(null))
195+
.max((a, b) -> Integer.compare(a.getWidth(null), b.getWidth(null)))
196+
.orElse(null);
197+
if (image == null) {
198+
return;
199+
}
200+
201+
int width = image.getWidth(null);
202+
int height = image.getHeight(null);
203+
int size = width;
204+
205+
BufferedImage bufferedImage;
206+
if (image instanceof BufferedImage && ((BufferedImage) image).getType() == BufferedImage.TYPE_INT_ARGB) {
207+
bufferedImage = (BufferedImage) image;
208+
} else {
209+
bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
210+
Graphics2D g = bufferedImage.createGraphics();
211+
g.drawImage(image, 0, 0, null);
212+
g.dispose();
213+
}
214+
215+
int[] pixels = new int[width * height];
216+
bufferedImage.getRGB(0, 0, width, height, pixels, 0, width);
217+
setIcon(size, pixels);
184218
}
185219

186220
@Override

src/java.desktop/unix/native/libawt_wlawt/WLComponentPeer.c

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@
3636
#include "WLGraphicsEnvironment.h"
3737

3838
#include "xdg-decoration-unstable-v1.h"
39+
#include <stdbool.h>
40+
3941
#ifdef WAKEFIELD_ROBOT
4042
#include "wakefield.h"
4143
#endif
@@ -67,6 +69,9 @@ struct WLFrame {
6769
jboolean configuredActive;
6870
jboolean configuredMaximized;
6971
jboolean configuredFullscreen;
72+
73+
struct wl_buffer* iconBuffer;
74+
struct wl_shm_pool* iconPool;
7075
};
7176

7277
static void
@@ -571,3 +576,57 @@ JNIEXPORT void JNICALL Java_sun_awt_wl_ServerSideFrameDecoration_disposeImpl
571576
}
572577
}
573578

579+
JNIEXPORT void JNICALL Java_sun_awt_wl_WLComponentPeer_nativeSetIcon
580+
(JNIEnv *env, jobject obj, jlong ptr, jint size, jobject pixelArray)
581+
{
582+
struct WLFrame *frame = jlong_to_ptr(ptr);
583+
if (frame == NULL || !frame->toplevel || xdg_toplevel_icon_manager == NULL) {
584+
return;
585+
}
586+
587+
bool hasIcon = frame->iconBuffer != NULL;
588+
bool willHaveIcon = size > 0 && pixelArray != NULL;
589+
size_t iconByteSize = size * size * 4;
590+
591+
if (!willHaveIcon) {
592+
xdg_toplevel_icon_manager_v1_set_icon(xdg_toplevel_icon_manager, frame->xdg_toplevel, NULL);
593+
}
594+
595+
if (hasIcon) {
596+
if (frame->iconBuffer != NULL) {
597+
wl_buffer_destroy(frame->iconBuffer);
598+
frame->iconBuffer = NULL;
599+
}
600+
if (frame->iconPool != NULL) {
601+
wl_shm_pool_destroy(frame->iconPool);
602+
frame->iconPool = NULL;
603+
}
604+
}
605+
606+
if (willHaveIcon) {
607+
void* poolData = NULL;
608+
struct wl_shm_pool* pool = CreateShmPool(iconByteSize, "toplevel_icon", &poolData, NULL);
609+
if (pool == NULL) {
610+
return;
611+
}
612+
(*env)->GetIntArrayRegion(env, pixelArray, 0, size * size, poolData);
613+
struct wl_buffer* buffer = wl_shm_pool_create_buffer(pool, 0, size, size, size * 4, WL_SHM_FORMAT_ARGB8888);
614+
if (buffer == NULL) {
615+
wl_shm_pool_destroy(pool);
616+
return;
617+
}
618+
619+
struct xdg_toplevel_icon_v1* icon = xdg_toplevel_icon_manager_v1_create_icon(xdg_toplevel_icon_manager);
620+
if (icon == NULL) {
621+
wl_buffer_destroy(buffer);
622+
wl_shm_pool_destroy(pool);
623+
return;
624+
}
625+
xdg_toplevel_icon_v1_add_buffer(icon, buffer, 1);
626+
xdg_toplevel_icon_manager_v1_set_icon(xdg_toplevel_icon_manager, frame->xdg_toplevel, icon);
627+
xdg_toplevel_icon_v1_destroy(icon);
628+
629+
frame->iconPool = pool;
630+
frame->iconBuffer = buffer;
631+
}
632+
}

src/java.desktop/unix/native/libawt_wlawt/WLToolkit.c

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,10 @@ struct zwp_primary_selection_device_manager_v1 *zwp_selection_dm = NULL; // opti
9393
struct zxdg_output_manager_v1 *zxdg_output_manager_v1 = NULL; // optional, check for NULL before use
9494

9595
struct zwp_text_input_manager_v3 *zwp_text_input_manager = NULL; // optional, check for NULL before use
96+
struct xdg_toplevel_icon_manager_v1 *xdg_toplevel_icon_manager; // optional, check for NULL before use
9697

9798
static uint32_t num_of_outstanding_sync = 0;
99+
static bool waiting_for_xdg_toplevel_icon_manager_done = false;
98100

99101
// This group of definitions corresponds to declarations from awt.h
100102
jclass tkClass = NULL;
@@ -135,6 +137,7 @@ static jmethodID dispatchKeyboardModifiersEventMID;
135137
static jmethodID dispatchKeyboardEnterEventMID;
136138
static jmethodID dispatchKeyboardLeaveEventMID;
137139
static jmethodID dispatchRelativePointerEventMID;
140+
static jmethodID handleToplevelIconSizeMID;
138141

139142
JNIEnv *getEnv() {
140143
JNIEnv *env;
@@ -570,6 +573,36 @@ static const struct wl_seat_listener wl_seat_listener = {
570573
.name = wl_seat_name
571574
};
572575

576+
static void
577+
xdg_toplevel_icon_manager_icon_size(void *data,
578+
struct xdg_toplevel_icon_manager_v1 *xdg_toplevel_icon_manager_v1,
579+
int32_t size)
580+
{
581+
(void)data;
582+
(void)xdg_toplevel_icon_manager_v1;
583+
JNIEnv* env = getEnv();
584+
if (env == NULL) {
585+
return;
586+
}
587+
588+
(*env)->CallStaticVoidMethod(env, tkClass, handleToplevelIconSizeMID, size);
589+
JNU_CHECK_EXCEPTION(env);
590+
}
591+
592+
static void
593+
xdg_toplevel_icon_manager_icon_size_done(void *data,
594+
struct xdg_toplevel_icon_manager_v1 *xdg_toplevel_icon_manager_v1)
595+
{
596+
(void)data;
597+
(void)xdg_toplevel_icon_manager_v1;
598+
waiting_for_xdg_toplevel_icon_manager_done = false;
599+
}
600+
601+
static const struct xdg_toplevel_icon_manager_v1_listener xdg_toplevel_icon_manager_v1_listener = {
602+
.icon_size = xdg_toplevel_icon_manager_icon_size,
603+
.done = xdg_toplevel_icon_manager_icon_size_done,
604+
};
605+
573606
static void
574607
registry_global(void *data, struct wl_registry *wl_registry,
575608
uint32_t name, const char *interface, uint32_t version)
@@ -636,6 +669,12 @@ registry_global(void *data, struct wl_registry *wl_registry,
636669
}
637670
} else if (strcmp(interface, zxdg_decoration_manager_v1_interface.name) == 0) {
638671
xdg_decoration_manager = wl_registry_bind(wl_registry, name, &zxdg_decoration_manager_v1_interface, 1);
672+
} else if (strcmp(interface, xdg_toplevel_icon_manager_v1_interface.name) == 0) {
673+
xdg_toplevel_icon_manager = wl_registry_bind(wl_registry, name, &xdg_toplevel_icon_manager_v1_interface, 1);
674+
if (xdg_toplevel_icon_manager != NULL) {
675+
waiting_for_xdg_toplevel_icon_manager_done = true;
676+
xdg_toplevel_icon_manager_v1_add_listener(xdg_toplevel_icon_manager, &xdg_toplevel_icon_manager_v1_listener, NULL);
677+
}
639678
}
640679

641680
#ifdef WAKEFIELD_ROBOT
@@ -710,6 +749,11 @@ initJavaRefs(JNIEnv *env, jclass clazz)
710749
"(Lsun/awt/wl/WLPointerEvent;)V"),
711750
JNI_FALSE);
712751

752+
CHECK_NULL_RETURN(handleToplevelIconSizeMID = (*env)->GetStaticMethodID(env, tkClass,
753+
"handleToplevelIconSize",
754+
"(I)V"),
755+
JNI_FALSE);
756+
713757
CHECK_NULL_RETURN(pointerEventClass = (*env)->FindClass(env,
714758
"sun/awt/wl/WLPointerEvent"),
715759
JNI_FALSE);
@@ -844,7 +888,7 @@ getCursorTheme(int scale) {
844888
static void
845889
finalizeInit(JNIEnv *env) {
846890
// NB: we are NOT on EDT here so shouldn't dispatch EDT-sensitive stuff
847-
while (num_of_outstanding_sync > 0) {
891+
while (num_of_outstanding_sync > 0 || waiting_for_xdg_toplevel_icon_manager_done) {
848892
// There are outstanding events that carry information essential for the toolkit
849893
// to be fully operational, such as, for example, the number of outputs.
850894
// Those events were subscribed to when handling globals in registry_global().

src/java.desktop/unix/native/libawt_wlawt/WLToolkit.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
#include "relative-pointer-unstable-v1.h"
3434
#include "text-input-unstable-v3.h"
3535
#include "xdg-decoration-unstable-v1.h"
36-
36+
#include "xdg-toplevel-icon-v1.h"
3737
#include "jvm_md.h"
3838
#include "jni_util.h"
3939

@@ -73,6 +73,7 @@ extern struct zxdg_output_manager_v1 *zxdg_output_manager_v1; // optional, check
7373
extern struct zwp_relative_pointer_manager_v1* relative_pointer_manager;
7474
extern struct zwp_text_input_manager_v3 *zwp_text_input_manager; // optional, check for NULL before use
7575
extern struct zxdg_decoration_manager_v1* xdg_decoration_manager; // optional, check for NULL before use
76+
extern struct xdg_toplevel_icon_manager_v1 *xdg_toplevel_icon_manager; // optional, check for NULL before use
7677

7778
JNIEnv *getEnv();
7879

0 commit comments

Comments
 (0)