JBR-9002 Wayland: deadlock with J2DDemo

This commit is contained in:
Maxim Kartashev
2025-06-19 16:49:33 +04:00
parent a70d004636
commit dfa2f93ded
4 changed files with 156 additions and 103 deletions

View File

@@ -944,7 +944,7 @@ public class WLComponentPeer implements ComponentPeer, WLSurfaceSizeListener {
}
public void updateCursorImmediately() {
WLToolkit.updateCursorImmediatelyFor(this);
WLToolkit.getCursorManager().updateCursorImmediatelyFor(this);
}
Cursor cursorAt(int x, int y) {

View File

@@ -0,0 +1,145 @@
/*
* Copyright (c) 2025-2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2025-2025, 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.util.logging.PlatformLogger;
import java.awt.Cursor;
import java.awt.GraphicsConfiguration;
public class WLCursorManager {
private static final PlatformLogger log = PlatformLogger.getLogger("sun.awt.wl.WLCursorManager");
// mapping of AWT cursor types to X cursor names
// multiple variants can be specified, that will be tried in order
// See https://freedesktop.org/wiki/Specifications/cursor-spec/
private static final String[][] CURSOR_NAMES = {
{"default", "arrow", "left_ptr", "left_arrow"}, // DEFAULT_CURSOR
{"crosshair", "cross"}, // CROSSHAIR_CURSOR
{"text", "xterm"}, // TEXT_CURSOR
{"wait", "watch", "progress"}, // WAIT_CURSOR
{"sw-resize", "bottom_left_corner"}, // SW_RESIZE_CURSOR
{"se-resize", "bottom_right_corner"}, // SE_RESIZE_CURSOR
{"nw-resize", "top_left_corner"}, // NW_RESIZE_CURSOR
{"ne-resize", "top_right_corner"}, // NE_RESIZE_CURSOR
{"n-resize", "top_side"}, // N_RESIZE_CURSOR
{"s-resize", "bottom_side"}, // S_RESIZE_CURSOR
{"w-resize", "left_side"}, // W_RESIZE_CURSOR
{"e-resize", "right_side"}, // E_RESIZE_CURSOR
{"pointer", "pointing_hand", "hand1", "hand2"}, // HAND_CURSOR
{"move"}, // MOVE_CURSOR
};
private static final WLCursorManager instance = new WLCursorManager();
private Cursor currentCursor; // accessed under the 'instance' lock
public static WLCursorManager getInstance() {
return instance;
}
private WLCursorManager() {
}
public void reset() {
synchronized (instance) {
currentCursor = null;
}
}
public void updateCursorImmediatelyFor(WLComponentPeer peer) {
var inputState = WLToolkit.getInputState();
Cursor cursor = peer.cursorAt(inputState.getPointerX(), inputState.getPointerY());
GraphicsConfiguration gc = peer.getGraphicsConfiguration();
int scale = gc instanceof WLGraphicsConfig wlGC ? wlGC.getDisplayScale() : 1;
setCursorTo(cursor, scale);
}
private void setCursorTo(Cursor c, int scale) {
var inputState = WLToolkit.getInputState();
long serial = inputState.pointerEnterSerial();
if (serial == 0) {
if (log.isLoggable(PlatformLogger.Level.FINE)) {
log.fine("setCursor aborted due to missing event serial");
}
return; // Wayland will ignore the request anyway
}
Cursor cursor;
if (c.getType() == Cursor.CUSTOM_CURSOR && !(c instanceof WLCustomCursor)) {
cursor = Cursor.getDefaultCursor();
} else {
cursor = c;
}
// Native methods that work with cursor are not thread-safe, so we need to synchronize access to them.
synchronized (instance) {
// Cursors may get updated very often; prevent vacuous updates from reaching the native code so
// as not to overwhelm the Wayland server.
if (cursor == currentCursor) return;
currentCursor = cursor;
long pData = AWTAccessor.getCursorAccessor().getPData(cursor, scale);
if (pData == 0) {
// TODO: instead of destroying and creating a new cursor after changing the scale caching could be used
long oldPData = AWTAccessor.getCursorAccessor().getPData(cursor);
if (oldPData != 0 && oldPData != -1) {
nativeDestroyPredefinedCursor(oldPData);
}
pData = createNativeCursor(cursor.getType(), scale);
if (pData == 0) {
pData = createNativeCursor(Cursor.DEFAULT_CURSOR, scale);
}
if (pData == 0) {
pData = -1; // mark as unavailable
}
AWTAccessor.getCursorAccessor().setPData(cursor, scale, pData);
}
nativeSetCursor(pData, scale, serial);
}
}
private long createNativeCursor(int type, int scale) {
if (type < Cursor.DEFAULT_CURSOR || type > Cursor.MOVE_CURSOR) {
type = Cursor.DEFAULT_CURSOR;
}
for (String name : CURSOR_NAMES[type]) {
long pData = nativeGetPredefinedCursor(name, scale);
if (pData != 0) {
return pData;
}
}
return 0;
}
private static native void nativeSetCursor(long pData, int scale, long pointerEnterSerial);
private static native long nativeGetPredefinedCursor(String name, int scale);
private static native void nativeDestroyPredefinedCursor(long pData);
}

View File

@@ -47,7 +47,6 @@ import java.awt.dnd.DragGestureListener;
import java.awt.dnd.DragGestureRecognizer;
import java.awt.dnd.DragSource;
import java.awt.dnd.InvalidDnDOperationException;
import java.awt.dnd.MouseDragGestureRecognizer;
import java.awt.dnd.peer.DragSourceContextPeer;
import java.awt.event.KeyEvent;
import java.awt.event.WindowEvent;
@@ -82,7 +81,6 @@ import java.awt.peer.TextAreaPeer;
import java.awt.peer.TextFieldPeer;
import java.awt.peer.TrayIconPeer;
import java.awt.peer.WindowPeer;
import java.beans.PropertyChangeListener;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Arrays;
@@ -135,32 +133,10 @@ public class WLToolkit extends UNIXToolkit implements Runnable {
private static final int MOUSE_BUTTONS_COUNT = 7;
private static final int AWT_MULTICLICK_DEFAULT_TIME_MS = 500;
// mapping of AWT cursor types to X cursor names
// multiple variants can be specified, that will be tried in order
// See https://freedesktop.org/wiki/Specifications/cursor-spec/
private static final String[][] CURSOR_NAMES = {
{"default", "arrow", "left_ptr", "left_arrow"}, // DEFAULT_CURSOR
{"crosshair", "cross"}, // CROSSHAIR_CURSOR
{"text", "xterm"}, // TEXT_CURSOR
{"wait", "watch", "progress"}, // WAIT_CURSOR
{"sw-resize", "bottom_left_corner"}, // SW_RESIZE_CURSOR
{"se-resize", "bottom_right_corner"}, // SE_RESIZE_CURSOR
{"nw-resize", "top_left_corner"}, // NW_RESIZE_CURSOR
{"ne-resize", "top_right_corner"}, // NE_RESIZE_CURSOR
{"n-resize", "top_side"}, // N_RESIZE_CURSOR
{"s-resize", "bottom_side"}, // S_RESIZE_CURSOR
{"w-resize", "left_side"}, // W_RESIZE_CURSOR
{"e-resize", "right_side"}, // E_RESIZE_CURSOR
{"pointer", "pointing_hand", "hand1", "hand2"}, // HAND_CURSOR
{"move"}, // MOVE_CURSOR
};
private static boolean initialized = false;
private static Thread toolkitThread;
private final WLDataDevice dataDevice;
private static Cursor currentCursor;
private static Boolean sunAwtDisableGtkFileDialogs = null;
private static native void initIDs(long displayPtr);
@@ -326,7 +302,7 @@ public class WLToolkit extends UNIXToolkit implements Runnable {
inputState = newInputState;
if (e.hasLeaveEvent() || e.hasEnterEvent()) {
// We've lost the control over the cursor, assume no knowledge about it
currentCursor = null;
getCursorManager().reset();
}
final WLComponentPeer peer = newInputState.peerForPointerEvents();
if (peer == null) {
@@ -1098,79 +1074,8 @@ public class WLToolkit extends UNIXToolkit implements Runnable {
return initialized;
}
static void performLockedGlobal(Runnable task) {
WLToolkit.awtLock();
try {
task.run();
} finally {
WLToolkit.awtUnlock();
}
public static WLCursorManager getCursorManager() {
return WLCursorManager.getInstance();
}
static void updateCursorImmediatelyFor(WLComponentPeer peer) {
Cursor cursor = peer.cursorAt(inputState.getPointerX(), inputState.getPointerY());
GraphicsConfiguration gc = peer.getGraphicsConfiguration();
int scale = gc instanceof WLGraphicsConfig wlGC ? wlGC.getDisplayScale() : 1;
setCursorTo(cursor, scale);
}
static void setCursorTo(Cursor c, int scale) {
long serial = inputState.pointerEnterSerial();
if (serial == 0) {
if (log.isLoggable(PlatformLogger.Level.FINE)) {
log.fine("setCursor aborted due to missing event serial");
}
return; // Wayland will ignore the request anyway
}
Cursor cursor;
if (c.getType() == Cursor.CUSTOM_CURSOR && !(c instanceof WLCustomCursor)) {
cursor = Cursor.getDefaultCursor();
} else {
cursor = c;
}
performLockedGlobal(() -> {
// Cursors may get updated very often; prevent vacuous updates from reaching the native code so
// as not to overwhelm the Wayland server.
if (cursor == currentCursor) return;
currentCursor = cursor;
long pData = AWTAccessor.getCursorAccessor().getPData(cursor, scale);
if (pData == 0) {
// instead of destroying and creating new cursor after changing scale could be used caching
long oldPData = AWTAccessor.getCursorAccessor().getPData(cursor);
if (oldPData != 0 && oldPData != -1) {
nativeDestroyPredefinedCursor(oldPData);
}
pData = createNativeCursor(cursor.getType(), scale);
if (pData == 0) {
pData = createNativeCursor(Cursor.DEFAULT_CURSOR, scale);
}
if (pData == 0) {
pData = -1; // mark as unavailable
}
AWTAccessor.getCursorAccessor().setPData(cursor, scale, pData);
}
nativeSetCursor(pData, scale, serial);
});
}
private static long createNativeCursor(int type, int scale) {
if (type < Cursor.DEFAULT_CURSOR || type > Cursor.MOVE_CURSOR) {
type = Cursor.DEFAULT_CURSOR;
}
for (String name : CURSOR_NAMES[type]) {
long pData = nativeGetPredefinedCursor(name, scale);
if (pData != 0) {
return pData;
}
}
return 0;
}
private static native void nativeSetCursor(long pData, int scale, long pointerEnterSerial);
private static native long nativeGetPredefinedCursor(String name, int scale);
private static native long nativeDestroyPredefinedCursor(long pData);
}

View File

@@ -31,6 +31,8 @@
#include "WLToolkit.h"
#include "WLGraphicsEnvironment.h"
#include "sun_awt_wl_WLCursorManager.h"
struct WLCursor {
struct wl_buffer *buffer;
bool managed;
@@ -58,7 +60,7 @@ Java_java_awt_Cursor_finalizeImpl
}
}
JNIEXPORT jlong JNICALL Java_sun_awt_wl_WLToolkit_nativeGetPredefinedCursor
JNIEXPORT jlong JNICALL Java_sun_awt_wl_WLCursorManager_nativeGetPredefinedCursor
(JNIEnv *env, jclass cls, jstring name, jint scale)
{
struct wl_cursor_theme *cursor_theme = getCursorTheme(scale);
@@ -88,9 +90,10 @@ JNIEXPORT jlong JNICALL Java_sun_awt_wl_WLToolkit_nativeGetPredefinedCursor
return ptr_to_jlong(cursor);
}
JNIEXPORT void JNICALL Java_sun_awt_wl_WLToolkit_nativeDestroyPredefinedCursor
(JNIEnv *env, jclass cls, struct WLCursor *cursor)
JNIEXPORT void JNICALL Java_sun_awt_wl_WLCursorManager_nativeDestroyPredefinedCursor
(JNIEnv *env, jclass cls, jlong cursorPtr)
{
struct WLCursor *cursor = jlong_to_ptr(cursorPtr);
free(cursor);
}
@@ -137,7 +140,7 @@ JNIEXPORT jlong JNICALL Java_sun_awt_wl_WLCustomCursor_nativeCreateCustomCursor
return ptr_to_jlong(cursor);
}
JNIEXPORT void JNICALL Java_sun_awt_wl_WLToolkit_nativeSetCursor
JNIEXPORT void JNICALL Java_sun_awt_wl_WLCursorManager_nativeSetCursor
(JNIEnv *env, jclass cls, jlong pData, jint scale, jlong pointerEnterSerial)
{
struct wl_buffer *buffer = NULL;