mirror of
https://github.com/JetBrains/JetBrainsRuntime.git
synced 2025-12-06 09:29:38 +01:00
JBR-9002 Wayland: deadlock with J2DDemo
This commit is contained in:
@@ -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) {
|
||||
|
||||
145
src/java.desktop/unix/classes/sun/awt/wl/WLCursorManager.java
Normal file
145
src/java.desktop/unix/classes/sun/awt/wl/WLCursorManager.java
Normal 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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user