mirror of
https://github.com/JetBrains/JetBrainsRuntime.git
synced 2025-12-06 09:29:38 +01:00
8351907: [XWayland] [OL10] Robot.mousePress() is delivered to wrong place
Reviewed-by: honkar, prr
This commit is contained in:
@@ -50,7 +50,6 @@ import java.awt.image.Raster;
|
||||
import java.awt.image.WritableRaster;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
import sun.awt.X11.XBaseWindow;
|
||||
import com.sun.java.swing.plaf.gtk.GTKConstants.TextDirection;
|
||||
@@ -254,14 +253,12 @@ public abstract class UNIXToolkit extends SunToolkit
|
||||
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) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2024, 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
|
||||
@@ -35,13 +35,11 @@ import sun.awt.UNIXToolkit;
|
||||
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();
|
||||
@@ -49,19 +47,6 @@ final class XRobotPeer implements RobotPeer {
|
||||
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;
|
||||
@@ -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);
|
||||
@@ -129,8 +138,8 @@ final class XRobotPeer implements RobotPeer {
|
||||
@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,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2002, 2024, 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
|
||||
@@ -138,6 +138,8 @@ import sun.awt.X11GraphicsDevice;
|
||||
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;
|
||||
@@ -1521,16 +1523,21 @@ public final class XToolkit extends UNIXToolkit implements Runnable {
|
||||
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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023, 2024, 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
|
||||
@@ -38,6 +38,7 @@ import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
/**
|
||||
@@ -51,10 +52,13 @@ public final 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;
|
||||
|
||||
@@ -63,17 +67,23 @@ public final 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"
|
||||
@@ -89,7 +99,7 @@ public final class ScreencastHelper {
|
||||
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,
|
||||
@@ -186,7 +196,7 @@ public final class ScreencastHelper {
|
||||
|
||||
if (retVal >= 0) { // we have received a screen data
|
||||
return;
|
||||
} else if (!checkReturnValue(retVal)) {
|
||||
} else if (!checkReturnValue(retVal, true)) {
|
||||
return;
|
||||
} // else, try other tokens
|
||||
}
|
||||
@@ -200,25 +210,72 @@ public final class ScreencastHelper {
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023, 2024, 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
|
||||
@@ -67,14 +67,15 @@ final class TokenStorage {
|
||||
private static final String REL_NAME_SECONDARY =
|
||||
".awt/robot/screencast-tokens.properties";
|
||||
|
||||
private static final String REL_RD_NAME =
|
||||
".java/robot/remote-desktop-tokens.properties";
|
||||
|
||||
private static final Properties PROPS = new Properties();
|
||||
private static final Path PROPS_PATH;
|
||||
private static final Path PROP_FILENAME;
|
||||
|
||||
static {
|
||||
Path propsPath = setupPath();
|
||||
|
||||
PROPS_PATH = propsPath;
|
||||
PROPS_PATH = setupPath();
|
||||
|
||||
if (PROPS_PATH != null) {
|
||||
PROP_FILENAME = PROPS_PATH.getFileName();
|
||||
@@ -95,11 +96,18 @@ final class TokenStorage {
|
||||
return null;
|
||||
}
|
||||
|
||||
Path path = Path.of(userHome, REL_NAME);
|
||||
Path secondaryPath = Path.of(userHome, REL_NAME_SECONDARY);
|
||||
Path path;
|
||||
Path secondaryPath = null;
|
||||
|
||||
if (XdgDesktopPortal.isRemoteDesktop()) {
|
||||
path = Path.of(userHome, REL_RD_NAME);
|
||||
} else {
|
||||
path = Path.of(userHome, REL_NAME);
|
||||
secondaryPath = Path.of(userHome, REL_NAME_SECONDARY);
|
||||
}
|
||||
|
||||
boolean copyFromSecondary = !Files.isWritable(path)
|
||||
&& Files.isWritable(secondaryPath);
|
||||
&& secondaryPath != null && Files.isWritable(secondaryPath);
|
||||
|
||||
Path workdir = path.getParent();
|
||||
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright (c) 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
|
||||
* 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.screencast;
|
||||
|
||||
import sun.awt.SunToolkit;
|
||||
import sun.awt.UNIXToolkit;
|
||||
|
||||
import java.awt.Toolkit;
|
||||
|
||||
public class XdgDesktopPortal {
|
||||
private static final String METHOD_X11 = "x11";
|
||||
private static final String METHOD_SCREENCAST = "dbusScreencast";
|
||||
private static final String METHOD_REMOTE_DESKTOP = "dbusRemoteDesktop";
|
||||
|
||||
private static final String method;
|
||||
private static final boolean isRemoteDesktop;
|
||||
private static final boolean isScreencast;
|
||||
|
||||
private XdgDesktopPortal() {}
|
||||
|
||||
static {
|
||||
boolean isOnWayland = false;
|
||||
|
||||
if (Toolkit.getDefaultToolkit() instanceof SunToolkit sunToolkit) {
|
||||
isOnWayland = sunToolkit.isRunningOnWayland();
|
||||
}
|
||||
|
||||
String defaultMethod = METHOD_X11;
|
||||
if (isOnWayland) {
|
||||
Integer gnomeShellVersion = null;
|
||||
|
||||
UNIXToolkit toolkit = (UNIXToolkit) Toolkit.getDefaultToolkit();
|
||||
if ("gnome".equals(toolkit.getDesktop())) {
|
||||
gnomeShellVersion = toolkit.getGnomeShellMajorVersion();
|
||||
}
|
||||
|
||||
defaultMethod = (gnomeShellVersion != null && gnomeShellVersion >= 47)
|
||||
? METHOD_REMOTE_DESKTOP
|
||||
: METHOD_SCREENCAST;
|
||||
}
|
||||
|
||||
String m = System.getProperty("awt.robot.screenshotMethod", defaultMethod);
|
||||
|
||||
if (!METHOD_REMOTE_DESKTOP.equals(m)
|
||||
&& !METHOD_SCREENCAST.equals(m)
|
||||
&& !METHOD_X11.equals(m)) {
|
||||
m = defaultMethod;
|
||||
}
|
||||
|
||||
isRemoteDesktop = METHOD_REMOTE_DESKTOP.equals(m);
|
||||
isScreencast = METHOD_SCREENCAST.equals(m);
|
||||
method = m;
|
||||
|
||||
}
|
||||
|
||||
public static String getMethod() {
|
||||
return method;
|
||||
}
|
||||
|
||||
public static boolean isRemoteDesktop() {
|
||||
return isRemoteDesktop;
|
||||
}
|
||||
|
||||
public static boolean isScreencast() {
|
||||
return isScreencast;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2005, 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
|
||||
@@ -617,11 +617,14 @@ GtkApi* gtk3_load(JNIEnv *env, const char* lib_name)
|
||||
|
||||
glib_version_2_68 = !fp_glib_check_version(2, 68, 0);
|
||||
if (glib_version_2_68) {
|
||||
// those function are called only by Screencast / Remote desktop
|
||||
fp_g_string_replace = dl_symbol("g_string_replace"); //since: 2.68
|
||||
fp_g_uuid_string_is_valid = //since: 2.52
|
||||
dl_symbol("g_uuid_string_is_valid");
|
||||
fp_g_variant_print = dl_symbol("g_variant_print"); // since 2.24
|
||||
}
|
||||
fp_g_string_printf = dl_symbol("g_string_printf");
|
||||
fp_g_strconcat = dl_symbol("g_strconcat");
|
||||
|
||||
fp_g_error_free = dl_symbol("g_error_free");
|
||||
fp_g_unix_fd_list_get = dl_symbol("g_unix_fd_list_get");
|
||||
@@ -3102,6 +3105,7 @@ static void gtk3_init(GtkApi* gtk) {
|
||||
gtk->g_variant_new_string = fp_g_variant_new_string;
|
||||
gtk->g_variant_new_boolean = fp_g_variant_new_boolean;
|
||||
gtk->g_variant_new_uint32 = fp_g_variant_new_uint32;
|
||||
gtk->g_variant_print = fp_g_variant_print;
|
||||
|
||||
gtk->g_variant_get = fp_g_variant_get;
|
||||
gtk->g_variant_get_string = fp_g_variant_get_string;
|
||||
@@ -3126,6 +3130,7 @@ static void gtk3_init(GtkApi* gtk) {
|
||||
gtk->g_string_free = fp_g_string_free;
|
||||
gtk->g_string_replace = fp_g_string_replace;
|
||||
gtk->g_string_printf = fp_g_string_printf;
|
||||
gtk->g_strconcat = fp_g_strconcat;
|
||||
gtk->g_uuid_string_is_valid = fp_g_uuid_string_is_valid;
|
||||
|
||||
gtk->g_main_context_iteration = fp_g_main_context_iteration;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2005, 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
|
||||
@@ -399,6 +399,7 @@ static gboolean (*fp_g_str_has_prefix)(const gchar *str, const gchar *prefix);
|
||||
static gchar** (*fp_g_strsplit)(const gchar *string, const gchar *delimiter,
|
||||
gint max_tokens);
|
||||
static void (*fp_g_strfreev)(gchar **str_array);
|
||||
static gchar* (*fp_g_strconcat)(const gchar* string1, ...);
|
||||
|
||||
|
||||
static cairo_surface_t* (*fp_cairo_image_surface_create)(cairo_format_t format,
|
||||
@@ -738,6 +739,8 @@ static GVariant *(*fp_g_variant_new_boolean)(gboolean value);
|
||||
|
||||
static GVariant *(*fp_g_variant_new_uint32)(guint32 value);
|
||||
|
||||
static gchar *(*fp_g_variant_print) (GVariant* value, gboolean type_annotate);
|
||||
|
||||
static void (*fp_g_variant_get)(GVariant *value,
|
||||
const gchar *format_string,
|
||||
...);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2005, 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
|
||||
@@ -678,6 +678,7 @@ typedef struct GtkApi {
|
||||
GVariant *(*g_variant_new_boolean)(gboolean value);
|
||||
GVariant *(*g_variant_new_uint32)(guint32 value);
|
||||
|
||||
gchar *(*g_variant_print)(GVariant* value, gboolean type_annotate);
|
||||
|
||||
void (*g_variant_get)(GVariant *value,
|
||||
const gchar *format_string,
|
||||
@@ -734,6 +735,8 @@ typedef struct GtkApi {
|
||||
const gchar *format,
|
||||
...);
|
||||
|
||||
gchar* (*g_strconcat)(const gchar* string1, ...);
|
||||
|
||||
gboolean (*g_uuid_string_is_valid)(const gchar *str);
|
||||
|
||||
|
||||
|
||||
@@ -94,6 +94,7 @@ struct pw_properties * (*fp_pw_properties_new)(const char *key, ...);
|
||||
|
||||
#include "gtk_interface.h"
|
||||
#include "gtk3_interface.h"
|
||||
#include "canvas.h"
|
||||
|
||||
int DEBUG_SCREENCAST_ENABLED = FALSE;
|
||||
|
||||
@@ -108,6 +109,7 @@ static GString *activeSessionToken;
|
||||
struct ScreenSpace screenSpace = {0};
|
||||
static struct PwLoopData pw = {0};
|
||||
volatile bool isGtkMainThread = FALSE;
|
||||
gboolean isRemoteDesktop = FALSE;
|
||||
|
||||
jclass tokenStorageClass = NULL;
|
||||
jmethodID storeTokenMethodID = NULL;
|
||||
@@ -198,7 +200,7 @@ static void doCleanup() {
|
||||
/**
|
||||
* @return TRUE on success
|
||||
*/
|
||||
static gboolean initScreencast(const gchar *token,
|
||||
static gboolean initPortal(const gchar *token,
|
||||
GdkRectangle *affectedBounds,
|
||||
gint affectedBoundsLength) {
|
||||
gboolean isSameToken = !token
|
||||
@@ -225,8 +227,8 @@ static gboolean initScreencast(const gchar *token,
|
||||
|
||||
if (!initScreenSpace()
|
||||
|| !initXdgDesktopPortal()
|
||||
|| (pw.pwFd = getPipewireFd(token,
|
||||
affectedBounds,
|
||||
|| !initAndStartSession(token, &pw.pwFd)
|
||||
|| (pw.pwFd = getPipewireFd(affectedBounds,
|
||||
affectedBoundsLength)) < 0) {
|
||||
doCleanup();
|
||||
return FALSE;
|
||||
@@ -858,10 +860,19 @@ void storeRestoreToken(const gchar* oldToken, const gchar* newToken) {
|
||||
* Signature: (IZ)Z
|
||||
*/
|
||||
JNIEXPORT jboolean JNICALL Java_sun_awt_screencast_ScreencastHelper_loadPipewire(
|
||||
JNIEnv *env, jclass cls, jboolean screencastDebug
|
||||
JNIEnv *env, jclass cls, jint method, jboolean screencastDebug
|
||||
) {
|
||||
DEBUG_SCREENCAST_ENABLED = screencastDebug;
|
||||
|
||||
if (method != XDG_METHOD_SCREENCAST
|
||||
&& method != XDG_METHOD_REMOTE_DESKTOP) {
|
||||
return JNI_FALSE;
|
||||
}
|
||||
|
||||
isRemoteDesktop = method == XDG_METHOD_REMOTE_DESKTOP;
|
||||
|
||||
DEBUG_SCREENCAST("method %d\n", method)
|
||||
|
||||
if (!loadSymbols()) {
|
||||
return JNI_FALSE;
|
||||
}
|
||||
@@ -934,7 +945,7 @@ static int makeScreencast(
|
||||
GdkRectangle *affectedScreenBounds,
|
||||
gint affectedBoundsLength
|
||||
) {
|
||||
if (!initScreencast(token, affectedScreenBounds, affectedBoundsLength)) {
|
||||
if (!initPortal(token, affectedScreenBounds, affectedBoundsLength)) {
|
||||
return pw.pwFd;
|
||||
}
|
||||
|
||||
@@ -1097,10 +1108,130 @@ JNIEXPORT jint JNICALL Java_sun_awt_screencast_ScreencastHelper_getRGBPixelsImpl
|
||||
releaseToken(env, jtoken, token);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_awt_screencast_ScreencastHelper
|
||||
* Method: remoteDesktopMouseMove
|
||||
* Signature: (IILjava/lang/String;)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL Java_sun_awt_screencast_ScreencastHelper_remoteDesktopMouseMoveImpl
|
||||
(JNIEnv *env, jclass cls, jint jx, jint jy, jstring jtoken) {
|
||||
|
||||
|
||||
const gchar *token = jtoken
|
||||
? (*env)->GetStringUTFChars(env, jtoken, NULL)
|
||||
: NULL;
|
||||
|
||||
|
||||
DEBUG_SCREENCAST("moving mouse to\n\t%d %d\n\twith token |%s|\n", jx, jy, token);
|
||||
|
||||
gboolean result = initPortal(token, NULL, 0);
|
||||
DEBUG_SCREENCAST("init result %b, moving to %d %d\n", result, jx, jy)
|
||||
|
||||
if (result) {
|
||||
if (!remoteDesktopMouseMove(jx, jy)) {
|
||||
releaseToken(env, jtoken, token);
|
||||
return RESULT_DENIED;
|
||||
}
|
||||
}
|
||||
|
||||
releaseToken(env, jtoken, token);
|
||||
|
||||
return result ? RESULT_OK : pw.pwFd;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_awt_screencast_ScreencastHelper
|
||||
* Method: remoteDesktopMouseButtonImpl
|
||||
* Signature: (ZILjava/lang/String;)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL Java_sun_awt_screencast_ScreencastHelper_remoteDesktopMouseButtonImpl
|
||||
(JNIEnv *env, jclass cls, jboolean isPress, jint buttons, jstring jtoken) {
|
||||
|
||||
const gchar *token = jtoken
|
||||
? (*env)->GetStringUTFChars(env, jtoken, NULL)
|
||||
: NULL;
|
||||
|
||||
gboolean result = initPortal(token, NULL, 0);
|
||||
DEBUG_SCREENCAST("init result %b, mouse pressing %d\n", result, buttons)
|
||||
|
||||
if (result) {
|
||||
if (!remoteDesktopMouse(isPress, buttons)) {
|
||||
releaseToken(env, jtoken, token);
|
||||
return RESULT_DENIED;
|
||||
}
|
||||
}
|
||||
|
||||
releaseToken(env, jtoken, token);
|
||||
|
||||
return result ? RESULT_OK : pw.pwFd;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_awt_screencast_ScreencastHelper
|
||||
* Method: remoteDesktopMouseWheelImpl
|
||||
* Signature: (ILjava/lang/String;)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL Java_sun_awt_screencast_ScreencastHelper_remoteDesktopMouseWheelImpl
|
||||
(JNIEnv *env, jclass cls, jint jWheelAmt, jstring jtoken) {
|
||||
|
||||
const gchar *token = jtoken
|
||||
? (*env)->GetStringUTFChars(env, jtoken, NULL)
|
||||
: NULL;
|
||||
|
||||
gboolean result = initPortal(token, NULL, 0);
|
||||
DEBUG_SCREENCAST("init result %b, mouse wheel %d\n", result, jWheelAmt)
|
||||
|
||||
if (result) {
|
||||
if (!remoteDesktopMouseWheel(jWheelAmt)) {
|
||||
releaseToken(env, jtoken, token);
|
||||
return RESULT_DENIED;
|
||||
}
|
||||
}
|
||||
|
||||
releaseToken(env, jtoken, token);
|
||||
|
||||
return result ? RESULT_OK : pw.pwFd;
|
||||
}
|
||||
|
||||
/*
|
||||
* Class: sun_awt_screencast_ScreencastHelper
|
||||
* Method: remoteDesktopKeyImpl
|
||||
* Signature: (ZILjava/lang/String;)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL Java_sun_awt_screencast_ScreencastHelper_remoteDesktopKeyImpl
|
||||
(JNIEnv *env, jclass cls, jboolean isPress, jint jkey, jstring jtoken) {
|
||||
|
||||
AWT_LOCK();
|
||||
int key = awt_getX11KeySym(jkey);
|
||||
AWT_UNLOCK();
|
||||
|
||||
if (key == NoSymbol) {
|
||||
return RESULT_ERROR;
|
||||
}
|
||||
|
||||
const gchar *token = jtoken
|
||||
? (*env)->GetStringUTFChars(env, jtoken, NULL)
|
||||
: NULL;
|
||||
|
||||
gboolean result = initPortal(token, NULL, 0);
|
||||
DEBUG_SCREENCAST("init result %b, key %d -> %d isPress %b\n", result, jkey, key, isPress)
|
||||
|
||||
if (result) {
|
||||
if (!remoteDesktopKey(isPress, key)) {
|
||||
releaseToken(env, jtoken, token);
|
||||
return RESULT_DENIED;
|
||||
}
|
||||
}
|
||||
|
||||
releaseToken(env, jtoken, token);
|
||||
|
||||
return result ? RESULT_OK : pw.pwFd;
|
||||
}
|
||||
|
||||
#else
|
||||
JNIEXPORT void JNICALL
|
||||
Java_sun_awt_screencast_ScreencastHelper_closeSession(JNIEnv *env, jclass cls) {
|
||||
}
|
||||
Java_sun_awt_screencast_ScreencastHelper_closeSession(JNIEnv *env, jclass cls) {}
|
||||
|
||||
JNIEXPORT jint JNICALL Java_sun_awt_screencast_ScreencastHelper_getRGBPixelsImpl(
|
||||
JNIEnv *env,
|
||||
@@ -1117,8 +1248,28 @@ JNIEXPORT jint JNICALL Java_sun_awt_screencast_ScreencastHelper_getRGBPixelsImpl
|
||||
}
|
||||
|
||||
JNIEXPORT jboolean JNICALL Java_sun_awt_screencast_ScreencastHelper_loadPipewire(
|
||||
JNIEnv *env, jclass cls, jboolean screencastDebug
|
||||
JNIEnv *env, jclass cls, jint method, jboolean screencastDebug
|
||||
) {
|
||||
return JNI_FALSE;
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL Java_sun_awt_screencast_ScreencastHelper_remoteDesktopMouseMoveImpl
|
||||
(JNIEnv *env, jclass cls, jint jx, jint jy, jstring token) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL Java_sun_awt_screencast_ScreencastHelper_remoteDesktopMouseButtonImpl
|
||||
(JNIEnv *env, jclass cls, jboolean isPress, jint buttons, jstring jtoken) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL Java_sun_awt_screencast_ScreencastHelper_remoteDesktopMouseWheelImpl
|
||||
(JNIEnv *env, jclass cls, jint jWheelAmt, jstring jtoken) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL Java_sun_awt_screencast_ScreencastHelper_remoteDesktopKeyImpl
|
||||
(JNIEnv *env, jclass cls, jboolean isPress, jint jkey, jstring jtoken) {
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -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
|
||||
@@ -41,6 +41,8 @@
|
||||
|
||||
void storeRestoreToken(const gchar* oldToken, const gchar* newToken);
|
||||
|
||||
void print_gvariant_content(gchar *caption, GVariant *response);
|
||||
|
||||
struct ScreenProps {
|
||||
guint32 id;
|
||||
GdkRectangle bounds;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023, 2024, 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
|
||||
@@ -24,11 +24,9 @@
|
||||
*/
|
||||
|
||||
#include "stdlib.h"
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <pwd.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "java_awt_event_InputEvent.h"
|
||||
|
||||
#ifndef _AIX
|
||||
#include "screencast_pipewire.h"
|
||||
@@ -36,10 +34,16 @@
|
||||
#include "screencast_portal.h"
|
||||
|
||||
extern volatile bool isGtkMainThread;
|
||||
extern gboolean isRemoteDesktop;
|
||||
|
||||
extern struct ScreenSpace screenSpace;
|
||||
|
||||
struct XdgDesktopPortalApi *portal = NULL;
|
||||
extern int DEBUG_SCREENCAST_ENABLED;
|
||||
|
||||
GDBusProxy *getProxy() {
|
||||
return isRemoteDesktop ? portal->remoteDesktopProxy : portal->screenCastProxy;
|
||||
}
|
||||
|
||||
void errHandle(
|
||||
GError *error,
|
||||
@@ -170,26 +174,38 @@ gboolean rebuildScreenData(GVariantIter *iterStreams, gboolean isTheOnlyMon) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks screencast protocol version
|
||||
* @return FALSE if version < 4, or could not be determined
|
||||
* Checks the version of the Screencast/Remote Desktop protocol
|
||||
* to determine whether it supports the restore_token.
|
||||
* @return FALSE if version is below required, or could not be determined
|
||||
*/
|
||||
gboolean checkVersion() {
|
||||
static guint32 version = 0;
|
||||
|
||||
const gchar *interface = isRemoteDesktop
|
||||
? PORTAL_IFACE_REMOTE_DESKTOP
|
||||
: PORTAL_IFACE_SCREENCAST;
|
||||
|
||||
if (version == 0) {
|
||||
GError *error = NULL;
|
||||
|
||||
GVariant *retVersion = gtk->g_dbus_proxy_call_sync(
|
||||
portal->screenCastProxy,
|
||||
getProxy(),
|
||||
"org.freedesktop.DBus.Properties.Get",
|
||||
gtk->g_variant_new("(ss)",
|
||||
"org.freedesktop.portal.ScreenCast",
|
||||
interface,
|
||||
"version"),
|
||||
G_DBUS_CALL_FLAGS_NONE,
|
||||
-1, NULL, NULL
|
||||
);
|
||||
|
||||
if (isRemoteDesktop) {
|
||||
print_gvariant_content("checkVersion Remote Desktop", retVersion);
|
||||
} else {
|
||||
print_gvariant_content("checkVersion ScreenCast", retVersion);
|
||||
}
|
||||
|
||||
if (!retVersion) { //no backend on system
|
||||
DEBUG_SCREENCAST("!!! could not detect the screencast version\n",
|
||||
NULL);
|
||||
DEBUG_SCREENCAST("!!! could not detect the %s version\n", interface);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@@ -200,8 +216,7 @@ gboolean checkVersion() {
|
||||
|
||||
if (!varVersion){
|
||||
gtk->g_variant_unref(retVersion);
|
||||
DEBUG_SCREENCAST("!!! could not get the screencast version\n",
|
||||
NULL);
|
||||
DEBUG_SCREENCAST("!!! could not get the %s version\n", interface);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@@ -212,16 +227,22 @@ gboolean checkVersion() {
|
||||
|
||||
}
|
||||
|
||||
DEBUG_SCREENCAST("ScreenCast protocol version %d\n", version);
|
||||
if (version < 4) {
|
||||
DEBUG_SCREENCAST("!!! ScreenCast protocol version %d < 4,"
|
||||
gboolean isVersionOk = isRemoteDesktop
|
||||
? version >= PORTAL_MIN_VERSION_REMOTE_DESKTOP
|
||||
: version >= PORTAL_MIN_VERSION_SCREENCAST;
|
||||
|
||||
if (!isVersionOk) {
|
||||
DEBUG_SCREENCAST("!!! %s protocol version %d < %d,"
|
||||
" session restore is not available\n",
|
||||
version);
|
||||
interface,
|
||||
version,
|
||||
isRemoteDesktop
|
||||
? PORTAL_MIN_VERSION_REMOTE_DESKTOP
|
||||
: PORTAL_MIN_VERSION_SCREENCAST
|
||||
);
|
||||
}
|
||||
|
||||
// restore_token was added in version 4, without it,
|
||||
// user confirmation is required for every screenshot.
|
||||
return version >= 4;
|
||||
return isVersionOk;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -266,9 +287,9 @@ gboolean initXdgDesktopPortal() {
|
||||
portal->connection,
|
||||
G_DBUS_PROXY_FLAGS_NONE,
|
||||
NULL,
|
||||
"org.freedesktop.portal.Desktop",
|
||||
"/org/freedesktop/portal/desktop",
|
||||
"org.freedesktop.portal.ScreenCast",
|
||||
PORTAL_DESKTOP_BUS_NAME,
|
||||
PORTAL_DESKTOP_OBJECT_PATH,
|
||||
PORTAL_IFACE_SCREENCAST,
|
||||
NULL,
|
||||
&err
|
||||
);
|
||||
@@ -277,6 +298,29 @@ gboolean initXdgDesktopPortal() {
|
||||
DEBUG_SCREENCAST("Failed to get ScreenCast portal: %s", err->message);
|
||||
ERR_HANDLE(err);
|
||||
return FALSE;
|
||||
} else {
|
||||
DEBUG_SCREENCAST("%s: connection/sender name %s / %s\n",
|
||||
"ScreenCast", name,
|
||||
portal->senderName);
|
||||
}
|
||||
|
||||
if (isRemoteDesktop) {
|
||||
portal->remoteDesktopProxy = gtk->g_dbus_proxy_new_sync(
|
||||
portal->connection,
|
||||
G_DBUS_PROXY_FLAGS_NONE,
|
||||
NULL,
|
||||
PORTAL_DESKTOP_BUS_NAME,
|
||||
PORTAL_DESKTOP_OBJECT_PATH,
|
||||
PORTAL_IFACE_REMOTE_DESKTOP,
|
||||
NULL,
|
||||
&err
|
||||
);
|
||||
|
||||
if (err) {
|
||||
DEBUG_SCREENCAST("Failed to get Remote Desktop portal: %s", err->message);
|
||||
ERR_HANDLE(err);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return checkVersion();
|
||||
@@ -337,8 +381,8 @@ static void registerScreenCastCallback(
|
||||
) {
|
||||
helper->id = gtk->g_dbus_connection_signal_subscribe(
|
||||
portal->connection,
|
||||
"org.freedesktop.portal.Desktop",
|
||||
"org.freedesktop.portal.Request",
|
||||
PORTAL_DESKTOP_BUS_NAME,
|
||||
PORTAL_IFACE_REQUEST,
|
||||
"Response",
|
||||
path,
|
||||
NULL,
|
||||
@@ -383,7 +427,8 @@ static void callbackScreenCastCreateSession(
|
||||
if (status != 0) {
|
||||
DEBUG_SCREENCAST("Failed to create ScreenCast: %u\n", status);
|
||||
} else {
|
||||
gtk->g_variant_lookup(result, "session_handle", "s", helper->data);
|
||||
gboolean returned = gtk->g_variant_lookup(result, "session_handle", "s", helper->data);
|
||||
DEBUG_SCREENCAST("session_handle returned %b %p\n", returned, helper->data)
|
||||
}
|
||||
|
||||
helper->isDone = TRUE;
|
||||
@@ -430,6 +475,9 @@ gboolean portalScreenCastCreateSession() {
|
||||
gtk->g_variant_new_string(requestToken)
|
||||
);
|
||||
|
||||
|
||||
DEBUG_SCREENCAST("sessionToken %s \n", sessionToken)
|
||||
|
||||
gtk->g_variant_builder_add(
|
||||
&builder,
|
||||
"{sv}",
|
||||
@@ -437,8 +485,14 @@ gboolean portalScreenCastCreateSession() {
|
||||
gtk->g_variant_new_string(sessionToken)
|
||||
);
|
||||
|
||||
DEBUG_SCREENCAST("portalScreenCastCreateSession: proxy %s %p (rd: %p / sc: %p)\n",
|
||||
isRemoteDesktop ? "remoteDesktop" : "screencast",
|
||||
getProxy(),
|
||||
portal->remoteDesktopProxy,
|
||||
portal->screenCastProxy);
|
||||
|
||||
GVariant *response = gtk->g_dbus_proxy_call_sync(
|
||||
portal->screenCastProxy,
|
||||
getProxy(),
|
||||
"CreateSession",
|
||||
gtk->g_variant_new("(a{sv})", &builder),
|
||||
G_DBUS_CALL_FLAGS_NONE,
|
||||
@@ -447,6 +501,8 @@ gboolean portalScreenCastCreateSession() {
|
||||
&err
|
||||
);
|
||||
|
||||
print_gvariant_content("CreateSession", response);
|
||||
|
||||
if (err) {
|
||||
DEBUG_SCREENCAST("Failed to create ScreenCast session: %s\n",
|
||||
err->message);
|
||||
@@ -455,6 +511,8 @@ gboolean portalScreenCastCreateSession() {
|
||||
waitForCallback(&helper);
|
||||
}
|
||||
|
||||
DEBUG_SCREENCAST("portal->screenCastSessionHandle %s\n", portal->screenCastSessionHandle);
|
||||
|
||||
unregisterScreenCastCallback(&helper);
|
||||
if (response) {
|
||||
gtk->g_variant_unref(response);
|
||||
@@ -500,6 +558,39 @@ static void callbackScreenCastSelectSources(
|
||||
callbackEnd();
|
||||
}
|
||||
|
||||
static void callbackRemoteDesktopSelectDevices(
|
||||
GDBusConnection *connection,
|
||||
const char *senderName,
|
||||
const char *objectPath,
|
||||
const char *interfaceName,
|
||||
const char *signalName,
|
||||
GVariant *parameters,
|
||||
void *data
|
||||
) {
|
||||
struct DBusCallbackHelper *helper = data;
|
||||
|
||||
helper->data = (void *) 0;
|
||||
|
||||
uint32_t status;
|
||||
GVariant* result = NULL;
|
||||
|
||||
gtk->g_variant_get(parameters, "(u@a{sv})", &status, &result);
|
||||
|
||||
if (status != 0) {
|
||||
DEBUG_SCREENCAST("Failed select devices: %u\n", status);
|
||||
} else {
|
||||
helper->data = (void *) 1;
|
||||
}
|
||||
|
||||
helper->isDone = TRUE;
|
||||
|
||||
if (result) {
|
||||
gtk->g_variant_unref(result);
|
||||
}
|
||||
|
||||
callbackEnd();
|
||||
}
|
||||
|
||||
gboolean portalScreenCastSelectSources(const gchar *token) {
|
||||
GError* err = NULL;
|
||||
|
||||
@@ -545,25 +636,33 @@ gboolean portalScreenCastSelectSources(const gchar *token) {
|
||||
gtk->g_variant_new_uint32(1)
|
||||
);
|
||||
|
||||
// In the case of Remote Desktop,
|
||||
// we add the restore_token and persist_mode to the SelectDevices call.
|
||||
|
||||
// 0: Do not persist (default)
|
||||
// 1: Permissions persist as long as the application is running
|
||||
// 2: Permissions persist until explicitly revoked
|
||||
gtk->g_variant_builder_add(
|
||||
&builder,
|
||||
"{sv}",
|
||||
"persist_mode",
|
||||
gtk->g_variant_new_uint32(2)
|
||||
);
|
||||
|
||||
if (validateToken(token)) {
|
||||
if (!isRemoteDesktop) {
|
||||
gtk->g_variant_builder_add(
|
||||
&builder,
|
||||
"{sv}",
|
||||
"restore_token",
|
||||
gtk->g_variant_new_string(token)
|
||||
"persist_mode",
|
||||
gtk->g_variant_new_uint32(2)
|
||||
);
|
||||
}
|
||||
|
||||
if (!isRemoteDesktop) {
|
||||
if (validateToken(token)) {
|
||||
DEBUG_SCREENCAST(">>> adding token %s\n", token);
|
||||
gtk->g_variant_builder_add(
|
||||
&builder,
|
||||
"{sv}",
|
||||
"restore_token",
|
||||
gtk->g_variant_new_string(token)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
GVariant *response = gtk->g_dbus_proxy_call_sync(
|
||||
portal->screenCastProxy,
|
||||
"SelectSources",
|
||||
@@ -574,6 +673,8 @@ gboolean portalScreenCastSelectSources(const gchar *token) {
|
||||
&err
|
||||
);
|
||||
|
||||
print_gvariant_content("SelectSources", response);
|
||||
|
||||
if (err) {
|
||||
DEBUG_SCREENCAST("Failed to call SelectSources: %s\n", err->message);
|
||||
ERR_HANDLE(err);
|
||||
@@ -624,6 +725,15 @@ static void callbackScreenCastStart(
|
||||
G_VARIANT_TYPE_ARRAY
|
||||
);
|
||||
|
||||
print_gvariant_content("Streams", streams);
|
||||
|
||||
if (!streams) {
|
||||
DEBUG_SCREENCAST("No streams available with current token\n", NULL);
|
||||
startHelper->result = RESULT_NO_STREAMS;
|
||||
helper->isDone = TRUE;
|
||||
return;
|
||||
}
|
||||
|
||||
GVariantIter iter;
|
||||
gtk->g_variant_iter_init(
|
||||
&iter,
|
||||
@@ -661,9 +771,7 @@ static void callbackScreenCastStart(
|
||||
|
||||
helper->isDone = TRUE;
|
||||
|
||||
if (streams) {
|
||||
gtk->g_variant_unref(streams);
|
||||
}
|
||||
gtk->g_variant_unref(streams);
|
||||
|
||||
callbackEnd();
|
||||
}
|
||||
@@ -706,7 +814,7 @@ ScreenCastResult portalScreenCastStart(const gchar *token) {
|
||||
);
|
||||
|
||||
GVariant *response = gtk->g_dbus_proxy_call_sync(
|
||||
portal->screenCastProxy,
|
||||
getProxy(),
|
||||
"Start",
|
||||
gtk->g_variant_new("(osa{sv})", portal->screenCastSessionHandle, "", &builder),
|
||||
G_DBUS_CALL_FLAGS_NONE,
|
||||
@@ -715,6 +823,8 @@ ScreenCastResult portalScreenCastStart(const gchar *token) {
|
||||
&err
|
||||
);
|
||||
|
||||
print_gvariant_content("Start", response);
|
||||
|
||||
if (err) {
|
||||
DEBUG_SCREENCAST("Failed to start session: %s\n", err->message);
|
||||
ERR_HANDLE(err);
|
||||
@@ -808,9 +918,9 @@ void portalScreenCastCleanup() {
|
||||
if (portal->screenCastSessionHandle) {
|
||||
gtk->g_dbus_connection_call_sync(
|
||||
portal->connection,
|
||||
"org.freedesktop.portal.Desktop",
|
||||
PORTAL_DESKTOP_BUS_NAME,
|
||||
portal->screenCastSessionHandle,
|
||||
"org.freedesktop.portal.Session",
|
||||
PORTAL_IFACE_SESSION,
|
||||
"Close",
|
||||
NULL,
|
||||
NULL,
|
||||
@@ -889,33 +999,140 @@ gboolean checkCanCaptureAllRequiredScreens(GdkRectangle *affectedBounds,
|
||||
return true;
|
||||
}
|
||||
|
||||
gboolean remoteDesktopSelectDevicesIfNeeded(const gchar* token) {
|
||||
if (!isRemoteDesktop || !portal->remoteDesktopProxy) {
|
||||
DEBUG_SCREENCAST("Skipping, remote desktop is not selected \n", NULL);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
GError* err = NULL;
|
||||
|
||||
gchar *requestPath = NULL;
|
||||
gchar *requestToken = NULL;
|
||||
|
||||
struct DBusCallbackHelper helper = {0};
|
||||
|
||||
|
||||
updateRequestPath(
|
||||
&requestPath,
|
||||
&requestToken
|
||||
);
|
||||
|
||||
registerScreenCastCallback(
|
||||
requestPath,
|
||||
&helper,
|
||||
callbackRemoteDesktopSelectDevices
|
||||
);
|
||||
|
||||
GVariantBuilder builder;
|
||||
|
||||
gtk->g_variant_builder_init(
|
||||
&builder,
|
||||
G_VARIANT_TYPE_VARDICT
|
||||
);
|
||||
|
||||
gtk->g_variant_builder_add(
|
||||
&builder,
|
||||
"{sv}", "handle_token",
|
||||
gtk->g_variant_new_string(requestToken)
|
||||
);
|
||||
|
||||
// 1: KEYBOARD
|
||||
// 2: POINTER
|
||||
// 4: TOUCHSCREEN
|
||||
gtk->g_variant_builder_add(
|
||||
&builder, "{sv}", "types",
|
||||
gtk->g_variant_new_uint32(1 | 2)
|
||||
);
|
||||
|
||||
// 0: Do not persist (default)
|
||||
// 1: Permissions persist as long as the application is running
|
||||
// 2: Permissions persist until explicitly revoked
|
||||
gtk->g_variant_builder_add(
|
||||
&builder,
|
||||
"{sv}",
|
||||
"persist_mode",
|
||||
gtk->g_variant_new_uint32(2)
|
||||
);
|
||||
|
||||
if (validateToken(token)) {
|
||||
gtk->g_variant_builder_add(
|
||||
&builder,
|
||||
"{sv}",
|
||||
"restore_token",
|
||||
gtk->g_variant_new_string(token)
|
||||
);
|
||||
}
|
||||
|
||||
GVariant *response = gtk->g_dbus_proxy_call_sync(
|
||||
portal->remoteDesktopProxy,
|
||||
"SelectDevices",
|
||||
gtk->g_variant_new("(oa{sv})", portal->screenCastSessionHandle, &builder),
|
||||
G_DBUS_CALL_FLAGS_NONE,
|
||||
-1,
|
||||
NULL,
|
||||
&err
|
||||
);
|
||||
|
||||
print_gvariant_content("SelectDevices", response);
|
||||
|
||||
if (err) {
|
||||
DEBUG_SCREENCAST("Failed to call SelectDevices: %s\n", err->message);
|
||||
ERR_HANDLE(err);
|
||||
} else {
|
||||
waitForCallback(&helper);
|
||||
}
|
||||
|
||||
unregisterScreenCastCallback(&helper);
|
||||
if (response) {
|
||||
gtk->g_variant_unref(response);
|
||||
}
|
||||
|
||||
free(requestPath);
|
||||
free(requestToken);
|
||||
|
||||
return helper.data != NULL;
|
||||
}
|
||||
|
||||
gboolean initAndStartSession(const gchar *token, int *retVal) {
|
||||
|
||||
*retVal = RESULT_ERROR;
|
||||
|
||||
int getPipewireFd(const gchar *token,
|
||||
GdkRectangle *affectedBounds,
|
||||
gint affectedBoundsLength) {
|
||||
if (!portalScreenCastCreateSession()) {
|
||||
DEBUG_SCREENCAST("Failed to create ScreenCast session\n", NULL);
|
||||
return RESULT_ERROR;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!portalScreenCastSelectSources(token)) {
|
||||
DEBUG_SCREENCAST("Failed to select sources\n", NULL);
|
||||
return RESULT_ERROR;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!remoteDesktopSelectDevicesIfNeeded(token)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
ScreenCastResult startResult = portalScreenCastStart(token);
|
||||
DEBUG_SCREENCAST("portalScreenCastStart result |%i|\n", startResult);
|
||||
|
||||
if (startResult != RESULT_OK) {
|
||||
DEBUG_SCREENCAST("Failed to start\n", NULL);
|
||||
return startResult;
|
||||
} else {
|
||||
if (!checkCanCaptureAllRequiredScreens(affectedBounds,
|
||||
affectedBoundsLength)) {
|
||||
DEBUG_SCREENCAST("The location of the screens has changed, "
|
||||
"the capture area is outside the allowed "
|
||||
"area.\n", NULL)
|
||||
return RESULT_OUT_OF_BOUNDS;
|
||||
}
|
||||
DEBUG_SCREENCAST("Failed to start %d\n", startResult);
|
||||
*retVal = startResult;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
*retVal = RESULT_OK;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int getPipewireFd(GdkRectangle *affectedBounds,
|
||||
gint affectedBoundsLength) {
|
||||
if (!checkCanCaptureAllRequiredScreens(affectedBounds,
|
||||
affectedBoundsLength)) {
|
||||
DEBUG_SCREENCAST("The location of the screens has changed, "
|
||||
"the capture area is outside the allowed "
|
||||
"area.\n", NULL)
|
||||
return RESULT_OUT_OF_BOUNDS;
|
||||
}
|
||||
|
||||
DEBUG_SCREENCAST("--- portalScreenCastStart\n", NULL);
|
||||
@@ -928,4 +1145,182 @@ int getPipewireFd(const gchar *token,
|
||||
DEBUG_SCREENCAST("pwFd %i\n", pipewireFd);
|
||||
return pipewireFd;
|
||||
}
|
||||
|
||||
|
||||
void print_gvariant_content(gchar *caption, GVariant *response) {
|
||||
if (!DEBUG_SCREENCAST_ENABLED) {
|
||||
return;
|
||||
}
|
||||
|
||||
gchar *str = NULL;
|
||||
if (response != NULL) {
|
||||
str = gtk->g_variant_print(response, TRUE);
|
||||
}
|
||||
|
||||
DEBUG_SCREENCAST("%s response:\n\t%s\n",
|
||||
caption, str);
|
||||
|
||||
gtk->g_free(str);
|
||||
}
|
||||
|
||||
static gboolean callRemoteDesktop(const gchar* methodName, GVariant *params) {
|
||||
GError *err = NULL;
|
||||
GVariantBuilder builder;
|
||||
gtk->g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
|
||||
|
||||
GVariant *response = gtk->g_dbus_proxy_call_sync(
|
||||
portal->remoteDesktopProxy,
|
||||
methodName,
|
||||
params,
|
||||
G_DBUS_CALL_FLAGS_NONE,
|
||||
-1,
|
||||
NULL,
|
||||
&err
|
||||
);
|
||||
|
||||
gchar * caption = gtk->g_strconcat("callRemoteDesktop ", methodName, NULL);
|
||||
print_gvariant_content(caption, response);
|
||||
gtk->g_free(caption);
|
||||
|
||||
DEBUG_SCREENCAST("%s: response %p err %p\n", methodName, response, err);
|
||||
|
||||
if (err) {
|
||||
DEBUG_SCREENCAST("Failed to call %s: %s\n", methodName, err->message);
|
||||
ERR_HANDLE(err);
|
||||
|
||||
// e.g. user denied mouse keyboard/interaction
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void clampCoordsIfNeeded(int *x, int *y) {
|
||||
if (screenSpace.screenCount <= 0 || x == NULL || y == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
GdkRectangle s0 = screenSpace.screens[0].bounds;
|
||||
int minX = s0.x;
|
||||
int minY = s0.y;
|
||||
int maxX = s0.x + s0.width;
|
||||
int maxY = s0.y + s0.height;
|
||||
|
||||
for (int i = 1; i < screenSpace.screenCount; ++i) {
|
||||
GdkRectangle s = screenSpace.screens[i].bounds;
|
||||
if (s.x < minX) minX = s.x;
|
||||
if (s.y < minY) minY = s.y;
|
||||
if (s.x + s.width > maxX) maxX = s.x + s.width;
|
||||
if (s.y + s.height > maxY) maxY = s.y + s.height;
|
||||
}
|
||||
|
||||
if (*x < minX) {
|
||||
*x = minX;
|
||||
} else if (*x > maxX) {
|
||||
*x = maxX - 1;
|
||||
}
|
||||
|
||||
if (*y < minY) {
|
||||
*y = minY;
|
||||
} else if (*y > maxY) {
|
||||
*y = maxY - 1;
|
||||
}
|
||||
}
|
||||
|
||||
gboolean remoteDesktopMouseMove(int x, int y) {
|
||||
guint32 streamId = 0;
|
||||
int relX = -1;
|
||||
int relY = -1;
|
||||
|
||||
DEBUG_SCREENCAST("mouseMove %d %d\n", x, y);
|
||||
clampCoordsIfNeeded(&x, &y);
|
||||
DEBUG_SCREENCAST("after clamping %d %d\n", x, y);
|
||||
|
||||
for (int i = 0; i < screenSpace.screenCount; ++i) {
|
||||
struct ScreenProps *screenProps = &screenSpace.screens[i];
|
||||
GdkRectangle rect = screenProps->bounds;
|
||||
|
||||
if (x >= rect.x &&
|
||||
y >= rect.y &&
|
||||
x < rect.x + rect.width &&
|
||||
y < rect.y + rect.height) {
|
||||
streamId = screenProps->id;
|
||||
relX = x - rect.x;
|
||||
relY = y - rect.y;
|
||||
|
||||
DEBUG_SCREENCAST("screenId#%i point %dx%d (rel %i %i) inside of screen (%d, %d, %d, %d)\n",
|
||||
streamId,
|
||||
x, y, relX, relY,
|
||||
rect.x, rect.y, rect.width, rect.height);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (streamId == 0) {
|
||||
DEBUG_SCREENCAST("outside of available screens\n", NULL);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
GVariantBuilder builder;
|
||||
gtk->g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
|
||||
GVariant *params = gtk->g_variant_new("(oa{sv}udd)", portal->screenCastSessionHandle, &builder,
|
||||
streamId, (double) relX, (double) relY);
|
||||
return callRemoteDesktop("NotifyPointerMotionAbsolute", params);
|
||||
}
|
||||
|
||||
gboolean callRemoteDesktopNotifyPointerButton(gboolean isPress, int evdevButton) {
|
||||
DEBUG_SCREENCAST("isPress %d evdevButton %d\n", isPress, evdevButton);
|
||||
|
||||
GVariantBuilder builder;
|
||||
gtk->g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT);
|
||||
GVariant *params = gtk->g_variant_new("(oa{sv}iu)",
|
||||
portal->screenCastSessionHandle, &builder, evdevButton, isPress);
|
||||
return callRemoteDesktop("NotifyPointerButton", params);
|
||||
}
|
||||
|
||||
gboolean remoteDesktopMouse(gboolean isPress, int buttons) {
|
||||
DEBUG_SCREENCAST("isPress %d awt buttons mask %d\n", isPress, buttons);
|
||||
|
||||
if (buttons & java_awt_event_InputEvent_BUTTON1_MASK
|
||||
|| buttons & java_awt_event_InputEvent_BUTTON1_DOWN_MASK) {
|
||||
if (!callRemoteDesktopNotifyPointerButton(isPress, 0x110)) { // BTN_LEFT
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
if (buttons & java_awt_event_InputEvent_BUTTON2_MASK
|
||||
|| buttons & java_awt_event_InputEvent_BUTTON2_DOWN_MASK) {
|
||||
if (!callRemoteDesktopNotifyPointerButton(isPress, 0x112)) { // BTN_MIDDLE
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
}
|
||||
if (buttons & java_awt_event_InputEvent_BUTTON3_MASK
|
||||
|| buttons & java_awt_event_InputEvent_BUTTON3_DOWN_MASK) {
|
||||
if (!callRemoteDesktopNotifyPointerButton(isPress, 0x111)) { // BTN_RIGHT
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean remoteDesktopMouseWheel(int wheelAmt) {
|
||||
DEBUG_SCREENCAST("MouseWheel %d\n", wheelAmt);
|
||||
|
||||
GVariantBuilder builder;
|
||||
gtk->g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT);
|
||||
GVariant *params = gtk->g_variant_new("(oa{sv}ui)", portal->screenCastSessionHandle, &builder, 0, wheelAmt);
|
||||
return callRemoteDesktop("NotifyPointerAxisDiscrete", params);
|
||||
}
|
||||
|
||||
gboolean remoteDesktopKey(gboolean isPress, int key) {
|
||||
DEBUG_SCREENCAST("Key%s key %d -> \n", isPress ? "Press" : "Release", key);
|
||||
|
||||
GVariantBuilder builder;
|
||||
gtk->g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT);
|
||||
GVariant *params = gtk->g_variant_new ("(oa{sv}iu)", portal->screenCastSessionHandle, &builder, key, isPress);
|
||||
return callRemoteDesktop("NotifyKeyboardKeysym", params);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -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
|
||||
@@ -36,11 +36,21 @@
|
||||
#define PORTAL_REQUEST_TEMPLATE "/org/freedesktop/portal/desktop/" \
|
||||
"request/%s/awtPipewire%lu"
|
||||
|
||||
#define PORTAL_DESKTOP_BUS_NAME "org.freedesktop.portal.Desktop"
|
||||
#define PORTAL_DESKTOP_OBJECT_PATH "/org/freedesktop/portal/desktop"
|
||||
|
||||
#define PORTAL_IFACE_REQUEST "org.freedesktop.portal.Request"
|
||||
#define PORTAL_IFACE_SESSION "org.freedesktop.portal.Session"
|
||||
#define PORTAL_IFACE_SCREENCAST "org.freedesktop.portal.ScreenCast"
|
||||
#define PORTAL_IFACE_REMOTE_DESKTOP "org.freedesktop.portal.RemoteDesktop"
|
||||
|
||||
#define PORTAL_MIN_VERSION_SCREENCAST 4
|
||||
#define PORTAL_MIN_VERSION_REMOTE_DESKTOP 2
|
||||
|
||||
void debug_screencast(const char *__restrict fmt, ...);
|
||||
|
||||
int getPipewireFd(const gchar *token,
|
||||
GdkRectangle *affectedBounds,
|
||||
gint affectedBoundsLength);
|
||||
gboolean initAndStartSession(const gchar *token, int *retVal);
|
||||
int getPipewireFd(GdkRectangle *affectedBounds, gint affectedBoundsLength);
|
||||
|
||||
void portalScreenCastCleanup();
|
||||
|
||||
@@ -48,8 +58,14 @@ gboolean initXdgDesktopPortal();
|
||||
|
||||
void errHandle(GError *error, const gchar *functionName, int lineNum);
|
||||
|
||||
gboolean remoteDesktopMouseMove(int x, int y);
|
||||
gboolean remoteDesktopMouseWheel(int wheelAmt);
|
||||
gboolean remoteDesktopMouse(gboolean isPress, int buttons);
|
||||
gboolean remoteDesktopKey(gboolean isPress, int key);
|
||||
|
||||
struct XdgDesktopPortalApi {
|
||||
GDBusConnection *connection;
|
||||
GDBusProxy *remoteDesktopProxy;
|
||||
GDBusProxy *screenCastProxy;
|
||||
gchar *senderName;
|
||||
char *screenCastSessionHandle;
|
||||
@@ -66,8 +82,15 @@ typedef enum {
|
||||
RESULT_ERROR = -1,
|
||||
RESULT_DENIED = -11,
|
||||
RESULT_OUT_OF_BOUNDS = -12,
|
||||
RESULT_NO_STREAMS = -13,
|
||||
} ScreenCastResult;
|
||||
|
||||
typedef enum {
|
||||
XDG_METHOD_SCREENCAST = 0,
|
||||
XDG_METHOD_REMOTE_DESKTOP = 1,
|
||||
} XdgPortalMethod;
|
||||
|
||||
|
||||
struct StartHelper {
|
||||
const gchar *token;
|
||||
ScreenCastResult result;
|
||||
|
||||
Reference in New Issue
Block a user