Skip to content

8351907: [XWayland] [OL10] Robot.mousePress() is delivered to wrong place #223

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 3 additions & 6 deletions src/java.desktop/unix/classes/sun/awt/UNIXToolkit.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2004, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2004, 2025, 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
Expand Down Expand Up @@ -50,7 +50,6 @@
import java.awt.image.WritableRaster;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;

import sun.awt.X11.XBaseWindow;
Expand Down Expand Up @@ -255,14 +254,12 @@ public boolean shouldDisableSystemTray() {
return result;
}

private Integer getGnomeShellMajorVersion() {
public Integer getGnomeShellMajorVersion() {
try {
Process process =
new ProcessBuilder("/usr/bin/gnome-shell", "--version")
.start();
try (InputStreamReader isr = new InputStreamReader(process.getInputStream());
BufferedReader reader = new BufferedReader(isr)) {

try (BufferedReader reader = process.inputReader()) {
if (process.waitFor(2, SECONDS) && process.exitValue() == 0) {
String line = reader.readLine();
if (line != null) {
Expand Down
63 changes: 36 additions & 27 deletions src/java.desktop/unix/classes/sun/awt/X11/XRobotPeer.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2025, 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
Expand Down Expand Up @@ -35,33 +35,18 @@
import sun.awt.X11GraphicsConfig;
import sun.awt.X11GraphicsDevice;
import sun.awt.screencast.ScreencastHelper;
import sun.awt.screencast.XdgDesktopPortal;

final class XRobotPeer implements RobotPeer {

private static final boolean tryGtk;
private static final String screenshotMethod;
private static final String METHOD_X11 = "x11";
private static final String METHOD_SCREENCAST = "dbusScreencast";

static {
loadNativeLibraries();

tryGtk = Boolean.parseBoolean(
System.getProperty("awt.robot.gtk", "true")
);

boolean isOnWayland = false;

if (Toolkit.getDefaultToolkit() instanceof SunToolkit sunToolkit) {
isOnWayland = sunToolkit.isRunningOnWayland();
}

screenshotMethod = System.getProperty(
"awt.robot.screenshotMethod",
isOnWayland
? METHOD_SCREENCAST
: METHOD_X11
);
}

private static volatile boolean useGtk;
Expand All @@ -86,39 +71,63 @@ final class XRobotPeer implements RobotPeer {
@Override
public void mouseMove(int x, int y) {
mouseMoveImpl(xgc, xgc.scaleUp(x), xgc.scaleUp(y));
if (XdgDesktopPortal.isRemoteDesktop() && ScreencastHelper.isAvailable()) {
// We still call mouseMoveImpl on purpose to change the mouse position
// within the XWayland server so that we can retrieve it later.
ScreencastHelper.remoteDesktopMouseMove(xgc.scaleUp(x), xgc.scaleUp(y));
}
}

@Override
public void mousePress(int buttons) {
mousePressImpl(buttons);
if (XdgDesktopPortal.isRemoteDesktop() && ScreencastHelper.isAvailable()) {
ScreencastHelper.remoteDesktopMouseButton(true, buttons);
} else {
mousePressImpl(buttons);
}
}

@Override
public void mouseRelease(int buttons) {
mouseReleaseImpl(buttons);
if (XdgDesktopPortal.isRemoteDesktop() && ScreencastHelper.isAvailable()) {
ScreencastHelper.remoteDesktopMouseButton(false, buttons);
} else {
mouseReleaseImpl(buttons);
}
}

@Override
public void mouseWheel(int wheelAmt) {
mouseWheelImpl(wheelAmt);
if (XdgDesktopPortal.isRemoteDesktop() && ScreencastHelper.isAvailable()) {
ScreencastHelper.remoteDesktopMouseWheel(wheelAmt);
} else {
mouseWheelImpl(wheelAmt);
}
}

@Override
public void keyPress(int keycode) {
keyPressImpl(keycode);
if (XdgDesktopPortal.isRemoteDesktop() && ScreencastHelper.isAvailable()) {
ScreencastHelper.remoteDesktopKey(true, keycode);
} else {
keyPressImpl(keycode);
}
}

@Override
public void keyRelease(int keycode) {
keyReleaseImpl(keycode);
if (XdgDesktopPortal.isRemoteDesktop() && ScreencastHelper.isAvailable()) {
ScreencastHelper.remoteDesktopKey(false, keycode);
} else {
keyReleaseImpl(keycode);
}
}

@Override
public int getRGBPixel(int x, int y) {
int[] pixelArray = new int[1];
if (screenshotMethod.equals(METHOD_SCREENCAST)
&& ScreencastHelper.isAvailable()) {

if ((XdgDesktopPortal.isScreencast()
|| XdgDesktopPortal.isRemoteDesktop()) && ScreencastHelper.isAvailable()) {
ScreencastHelper.getRGBPixels(x, y, 1, 1, pixelArray);
} else {
getRGBPixelsImpl(xgc, x, y, 1, 1, pixelArray, useGtk);
Expand All @@ -129,8 +138,8 @@ public int getRGBPixel(int x, int y) {
@Override
public int[] getRGBPixels(Rectangle bounds) {
int[] pixelArray = new int[bounds.width * bounds.height];
if (screenshotMethod.equals(METHOD_SCREENCAST)
&& ScreencastHelper.isAvailable()) {
if ((XdgDesktopPortal.isScreencast()
|| XdgDesktopPortal.isRemoteDesktop()) && ScreencastHelper.isAvailable()) {

ScreencastHelper.getRGBPixels(bounds.x, bounds.y,
bounds.width, bounds.height,
Expand Down
27 changes: 17 additions & 10 deletions src/java.desktop/unix/classes/sun/awt/X11/XToolkit.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2002, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2002, 2025, 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
Expand Down Expand Up @@ -138,6 +138,8 @@
import sun.awt.X11GraphicsEnvironment;
import sun.awt.XSettings;
import sun.awt.datatransfer.DataTransferer;
import sun.awt.screencast.ScreencastHelper;
import sun.awt.screencast.XdgDesktopPortal;
import sun.awt.util.PerformanceLogger;
import sun.awt.util.ThreadGroupUtils;
import sun.font.FontConfigManager;
Expand Down Expand Up @@ -1521,16 +1523,21 @@ public int getNumberOfButtons(){
awtLock();
try {
if (numberOfButtons == 0) {
numberOfButtons = getNumberOfButtonsImpl();
numberOfButtons = (numberOfButtons > MAX_BUTTONS_SUPPORTED)? MAX_BUTTONS_SUPPORTED : numberOfButtons;
//4th and 5th buttons are for wheel and shouldn't be reported as buttons.
//If we have more than 3 physical buttons and a wheel, we report N-2 buttons.
//If we have 3 physical buttons and a wheel, we report 3 buttons.
//If we have 1,2,3 physical buttons, we report it as is i.e. 1,2 or 3 respectively.
if (numberOfButtons >=5) {
numberOfButtons -= 2;
} else if (numberOfButtons == 4 || numberOfButtons ==5){
if (XdgDesktopPortal.isRemoteDesktop()
&& ScreencastHelper.isAvailable()) {
numberOfButtons = 3;
} else {
numberOfButtons = getNumberOfButtonsImpl();
numberOfButtons = (numberOfButtons > MAX_BUTTONS_SUPPORTED) ? MAX_BUTTONS_SUPPORTED : numberOfButtons;
//4th and 5th buttons are for wheel and shouldn't be reported as buttons.
//If we have more than 3 physical buttons and a wheel, we report N-2 buttons.
//If we have 3 physical buttons and a wheel, we report 3 buttons.
//If we have 1,2,3 physical buttons, we report it as is i.e. 1,2 or 3 respectively.
if (numberOfButtons >= 5) {
numberOfButtons -= 2;
} else if (numberOfButtons == 4 || numberOfButtons == 5) {
numberOfButtons = 3;
}
}
}
//Assume don't have to re-query the number again and again.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2023, 2025, 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
Expand Down Expand Up @@ -38,6 +38,7 @@
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.function.Function;
import java.util.stream.IntStream;

/**
Expand All @@ -51,10 +52,13 @@ public class ScreencastHelper {
static final boolean SCREENCAST_DEBUG;
private static final boolean IS_NATIVE_LOADED;


private static final int ERROR = -1;
private static final int DENIED = -11;
private static final int OUT_OF_BOUNDS = -12;
private static final int NO_STREAMS = -13;

private static final int XDG_METHOD_SCREENCAST = 0;
private static final int XDG_METHOD_REMOTE_DESKTOP = 1;

private static final int DELAY_BEFORE_SESSION_CLOSE = 2000;

Expand All @@ -63,17 +67,23 @@ public class ScreencastHelper {
= new Timer("auto-close screencast session", true);


private ScreencastHelper() {
}
private ScreencastHelper() {}

static {
SCREENCAST_DEBUG = Boolean.getBoolean("awt.robot.screenshotDebug");

boolean loadFailed = false;

boolean shouldLoadNative = XdgDesktopPortal.isRemoteDesktop()
|| XdgDesktopPortal.isScreencast();

int methodId = XdgDesktopPortal.isScreencast()
? XDG_METHOD_SCREENCAST
: XDG_METHOD_REMOTE_DESKTOP;

if (!(Toolkit.getDefaultToolkit() instanceof UNIXToolkit tk
&& tk.loadGTK())
|| !loadPipewire(SCREENCAST_DEBUG)) {
|| !(shouldLoadNative && loadPipewire(methodId, SCREENCAST_DEBUG))) {

System.err.println(
"Could not load native libraries for ScreencastHelper"
Expand All @@ -89,7 +99,7 @@ public static boolean isAvailable() {
return IS_NATIVE_LOADED;
}

private static native boolean loadPipewire(boolean screencastDebug);
private static native boolean loadPipewire(int method, boolean isDebug);

private static native int getRGBPixelsImpl(
int x, int y, int width, int height,
Expand Down Expand Up @@ -186,7 +196,7 @@ public static synchronized void getRGBPixels(

if (retVal >= 0) { // we have received a screen data
return;
} else if (!checkReturnValue(retVal)) {
} else if (!checkReturnValue(retVal, true)) {
return;
} // else, try other tokens
}
Expand All @@ -200,25 +210,72 @@ public static synchronized void getRGBPixels(
null
);

checkReturnValue(retVal);
checkReturnValue(retVal, true);
}

private static boolean checkReturnValue(int retVal) {
private static boolean checkReturnValue(int retVal,
boolean throwException) {
if (retVal == DENIED) {
// user explicitly denied the capture, no more tries.
throw new SecurityException(
"Screen Capture in the selected area was not allowed"
);
if (SCREENCAST_DEBUG) {
System.err.println("robot action: access denied by user.");
}
if (throwException) {
// user explicitly denied the capture, no more tries.
throw new SecurityException(
"Screen Capture in the selected area was not allowed"
);
}
} else if (retVal == ERROR) {
if (SCREENCAST_DEBUG) {
System.err.println("Screen capture failed.");
System.err.println("robot action: failed.");
}
} else if (retVal == OUT_OF_BOUNDS) {
if (SCREENCAST_DEBUG) {
System.err.println(
"Token does not provide access to requested area.");
}
} else if (retVal == NO_STREAMS) {
if (SCREENCAST_DEBUG) {
System.err.println("robot action: no streams available");
}
}
return retVal != ERROR;
}

private static void performWithToken(Function<String, Integer> func) {
if (!XdgDesktopPortal.isRemoteDesktop() || !IS_NATIVE_LOADED) return;

timerCloseSessionRestart();

for (TokenItem tokenItem : TokenStorage.getTokens(getSystemScreensBounds())) {
int retVal = func.apply(tokenItem.token);

if (retVal >= 0 || !checkReturnValue(retVal, false)) {
return;
}
}

checkReturnValue(func.apply(null), false);
}

public static synchronized void remoteDesktopMouseMove(int x, int y) {
performWithToken((token) -> remoteDesktopMouseMoveImpl(x, y, token));
}

public static synchronized void remoteDesktopMouseButton(boolean isPress, int buttons) {
performWithToken((token) -> remoteDesktopMouseButtonImpl(isPress, buttons, token));
}

public static synchronized void remoteDesktopMouseWheel(int wheel) {
performWithToken((token) -> remoteDesktopMouseWheelImpl(wheel, token));
}

public static synchronized void remoteDesktopKey(boolean isPress, int key) {
performWithToken((token) -> remoteDesktopKeyImpl(isPress, key, token));
}

private static synchronized native int remoteDesktopMouseMoveImpl(int x, int y, String token);
private static synchronized native int remoteDesktopMouseButtonImpl(boolean isPress, int buttons, String token);
private static synchronized native int remoteDesktopMouseWheelImpl(int wheelAmt, String token);
private static synchronized native int remoteDesktopKeyImpl(boolean isPress, int key, String token);
}
Loading