11/*
2- * Copyright (c) 2023, 2024 , Oracle and/or its affiliates. All rights reserved.
2+ * Copyright (c) 2023, 2025 , Oracle and/or its affiliates. All rights reserved.
33 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44 *
55 * This code is free software; you can redistribute it and/or modify it
3232import java .util .Set ;
3333import java .util .Timer ;
3434import java .util .TimerTask ;
35+ import java .util .function .Function ;
3536import java .util .stream .IntStream ;
36- import java .util .function .Supplier ;
3737
3838/**
3939 * Helper class for grabbing pixels from the screen using the
4040 * <a href="https://flatpak.github.io/xdg-desktop-portal/#gdbus-org.freedesktop.portal.ScreenCast">
4141 * org.freedesktop.portal.ScreenCast API</a>
4242 */
4343
44- public class ScreencastHelper {
44+ public final class ScreencastHelper {
4545
4646 static final boolean SCREENCAST_DEBUG ;
4747 private static final boolean IS_NATIVE_LOADED ;
4848
49-
5049 private static final int ERROR = -1 ;
5150 private static final int DENIED = -11 ;
5251 private static final int OUT_OF_BOUNDS = -12 ;
52+ private static final int NO_STREAMS = -13 ;
53+
54+ private static final int XDG_METHOD_SCREENCAST = 0 ;
55+ private static final int XDG_METHOD_REMOTE_DESKTOP = 1 ;
5356
5457 private static final int DELAY_BEFORE_SESSION_CLOSE = 2000 ;
5558
@@ -58,26 +61,37 @@ public class ScreencastHelper {
5861 = new Timer ("auto-close screencast session" , true );
5962
6063
61- private ScreencastHelper () {
62- }
64+ private ScreencastHelper () {}
6365
6466 static {
65- boolean isDebugEnabled =
66- ((Supplier <Boolean >) () -> {
67- final String str =
68- System .getProperty ("javafx.robot.screenshotDebug" , "false" );
69- return "true" .equalsIgnoreCase (str );
70- }).get ();
71- SCREENCAST_DEBUG = isDebugEnabled ;
72-
73- IS_NATIVE_LOADED = loadPipewire (SCREENCAST_DEBUG );
67+ SCREENCAST_DEBUG = Boolean .getBoolean ("javafx.robot.screenshotDebug" );
68+
69+ boolean loadFailed = false ;
70+
71+ boolean shouldLoadNative = XdgDesktopPortal .isRemoteDesktop ()
72+ || XdgDesktopPortal .isScreencast ();
73+
74+ int methodId = XdgDesktopPortal .isScreencast ()
75+ ? XDG_METHOD_SCREENCAST
76+ : XDG_METHOD_REMOTE_DESKTOP ;
77+
78+ if (!(shouldLoadNative && loadPipewire (methodId , SCREENCAST_DEBUG ))) {
79+
80+ System .err .println (
81+ "Could not load native libraries for ScreencastHelper"
82+ );
83+
84+ loadFailed = true ;
85+ }
86+
87+ IS_NATIVE_LOADED = !loadFailed ;
7488 }
7589
7690 public static boolean isAvailable () {
7791 return IS_NATIVE_LOADED ;
7892 }
7993
80- private static native boolean loadPipewire (boolean screencastDebug );
94+ private static native boolean loadPipewire (int method , boolean isDebug );
8195
8296 private static native int getRGBPixelsImpl (
8397 int x , int y , int width , int height ,
@@ -196,21 +210,62 @@ public static synchronized void getRGBPixels(
196210 debugReturnValue (retVal );
197211 }
198212
199- private static void debugReturnValue (int retVal ) {
213+ private static boolean debugReturnValue (int retVal ) {
200214 if (retVal == DENIED ) {
201- // user explicitly denied the capture, no more tries.
202215 if (SCREENCAST_DEBUG ) {
203- System .err .println ("Screen Capture in the selected area was not allowed " );
216+ System .err .println ("robot action: access denied by user. " );
204217 }
205218 } else if (retVal == ERROR ) {
206219 if (SCREENCAST_DEBUG ) {
207- System .err .println ("Screen capture failed." );
220+ System .err .println ("robot action: failed." );
208221 }
209222 } else if (retVal == OUT_OF_BOUNDS ) {
210223 if (SCREENCAST_DEBUG ) {
211224 System .err .println (
212225 "Token does not provide access to requested area." );
213226 }
227+ } else if (retVal == NO_STREAMS ) {
228+ if (SCREENCAST_DEBUG ) {
229+ System .err .println ("robot action: no streams available" );
230+ }
214231 }
232+ return retVal != ERROR ;
215233 }
234+
235+ private static void performWithToken (Function <String , Integer > func ) {
236+ if (!XdgDesktopPortal .isRemoteDesktop () || !IS_NATIVE_LOADED ) return ;
237+
238+ timerCloseSessionRestart ();
239+
240+ for (TokenItem tokenItem : TokenStorage .getTokens (getSystemScreensBounds ())) {
241+ int retVal = func .apply (tokenItem .token );
242+
243+ if (retVal >= 0 || !debugReturnValue (retVal )) {
244+ return ;
245+ }
246+ }
247+
248+ debugReturnValue (func .apply (null ));
249+ }
250+
251+ public static synchronized void remoteDesktopMouseMove (int x , int y ) {
252+ performWithToken ((token ) -> remoteDesktopMouseMoveImpl (x , y , token ));
253+ }
254+
255+ public static synchronized void remoteDesktopMouseButton (boolean isPress , int buttons ) {
256+ performWithToken ((token ) -> remoteDesktopMouseButtonImpl (isPress , buttons , token ));
257+ }
258+
259+ public static synchronized void remoteDesktopMouseWheel (int wheel ) {
260+ performWithToken ((token ) -> remoteDesktopMouseWheelImpl (wheel , token ));
261+ }
262+
263+ public static synchronized void remoteDesktopKey (boolean isPress , int key ) {
264+ performWithToken ((token ) -> remoteDesktopKeyImpl (isPress , key , token ));
265+ }
266+
267+ private static synchronized native int remoteDesktopMouseMoveImpl (int x , int y , String token );
268+ private static synchronized native int remoteDesktopMouseButtonImpl (boolean isPress , int buttons , String token );
269+ private static synchronized native int remoteDesktopMouseWheelImpl (int wheelAmt , String token );
270+ private static synchronized native int remoteDesktopKeyImpl (boolean isPress , int key , String token );
216271}
0 commit comments