diff --git a/src/java.desktop/unix/classes/sun/awt/screencast/ScreencastHelper.java b/src/java.desktop/unix/classes/sun/awt/screencast/ScreencastHelper.java index e61fd1a90e6f6..6a19b9fab87aa 100644 --- a/src/java.desktop/unix/classes/sun/awt/screencast/ScreencastHelper.java +++ b/src/java.desktop/unix/classes/sun/awt/screencast/ScreencastHelper.java @@ -35,6 +35,8 @@ import java.util.Arrays; import java.util.List; import java.util.Set; +import java.util.Timer; +import java.util.TimerTask; import java.util.stream.IntStream; /** @@ -54,6 +56,13 @@ public class ScreencastHelper { private static final int DENIED = -11; private static final int OUT_OF_BOUNDS = -12; + private static final int DELAY_BEFORE_SESSION_CLOSE = 2000; + + private static volatile TimerTask timerTask = null; + private static final Timer timerCloseSession + = new Timer("auto-close screencast session", true); + + private ScreencastHelper() { } @@ -105,11 +114,30 @@ private static List getSystemScreensBounds() { ).toList(); } + private static synchronized native void closeSession(); + + private static void timerCloseSessionRestart() { + if (timerTask != null) { + timerTask.cancel(); + } + + timerTask = new TimerTask() { + @Override + public void run() { + closeSession(); + } + }; + + timerCloseSession.schedule(timerTask, DELAY_BEFORE_SESSION_CLOSE); + } + public static synchronized void getRGBPixels( int x, int y, int width, int height, int[] pixelArray ) { if (!IS_NATIVE_LOADED) return; + timerCloseSessionRestart(); + Rectangle captureArea = new Rectangle(x, y, width, height); List affectedScreenBounds = getSystemScreensBounds() diff --git a/src/java.desktop/unix/classes/sun/awt/screencast/TokenStorage.java b/src/java.desktop/unix/classes/sun/awt/screencast/TokenStorage.java index 022ca5e7dbb41..d39f7943c29fb 100644 --- a/src/java.desktop/unix/classes/sun/awt/screencast/TokenStorage.java +++ b/src/java.desktop/unix/classes/sun/awt/screencast/TokenStorage.java @@ -324,6 +324,10 @@ static Set getTokens(List affectedScreenBounds) { return tokenItem; }) .filter(Objects::nonNull) + .sorted((t1, t2) -> //Token with more screens preferred + t2.allowedScreensBounds.size() + - t1.allowedScreensBounds.size() + ) .toList(); } diff --git a/src/java.desktop/unix/native/libawt_xawt/awt/gtk3_interface.c b/src/java.desktop/unix/native/libawt_xawt/awt/gtk3_interface.c index 206a91132bc29..4c690db2e67f1 100644 --- a/src/java.desktop/unix/native/libawt_xawt/awt/gtk3_interface.c +++ b/src/java.desktop/unix/native/libawt_xawt/awt/gtk3_interface.c @@ -604,6 +604,7 @@ GtkApi* gtk3_load(JNIEnv *env, const char* lib_name) fp_g_string_new = dl_symbol("g_string_new"); fp_g_string_erase = dl_symbol("g_string_erase"); + fp_g_string_set_size = dl_symbol("g_string_set_size"); fp_g_string_free = dl_symbol("g_string_free"); glib_version_2_68 = !fp_glib_check_version(2, 68, 0); @@ -3110,6 +3111,7 @@ static void gtk3_init(GtkApi* gtk) { gtk->g_string_new = fp_g_string_new; gtk->g_string_erase = fp_g_string_erase; + gtk->g_string_set_size = fp_g_string_set_size; gtk->g_string_free = fp_g_string_free; gtk->g_string_replace = fp_g_string_replace; gtk->g_string_printf = fp_g_string_printf; diff --git a/src/java.desktop/unix/native/libawt_xawt/awt/gtk3_interface.h b/src/java.desktop/unix/native/libawt_xawt/awt/gtk3_interface.h index 0a3c8364e7973..b858d5e680802 100644 --- a/src/java.desktop/unix/native/libawt_xawt/awt/gtk3_interface.h +++ b/src/java.desktop/unix/native/libawt_xawt/awt/gtk3_interface.h @@ -758,6 +758,9 @@ static GString *(*fp_g_string_erase)(GString *string, gssize pos, gssize len); +static GString *(*fp_g_string_set_size)(GString* string, + gsize len); + static gchar *(*fp_g_string_free)(GString *string, gboolean free_segment); diff --git a/src/java.desktop/unix/native/libawt_xawt/awt/gtk_interface.h b/src/java.desktop/unix/native/libawt_xawt/awt/gtk_interface.h index 9c8c5743f7aac..1ddc239d9b23d 100644 --- a/src/java.desktop/unix/native/libawt_xawt/awt/gtk_interface.h +++ b/src/java.desktop/unix/native/libawt_xawt/awt/gtk_interface.h @@ -716,6 +716,10 @@ typedef struct GtkApi { gssize pos, gssize len); + GString *(*g_string_set_size)(GString* string, + gsize len); + + gchar *(*g_string_free)(GString *string, gboolean free_segment); diff --git a/src/java.desktop/unix/native/libawt_xawt/awt/screencast_pipewire.c b/src/java.desktop/unix/native/libawt_xawt/awt/screencast_pipewire.c index a5443784e003a..132ccd0b7c44f 100644 --- a/src/java.desktop/unix/native/libawt_xawt/awt/screencast_pipewire.c +++ b/src/java.desktop/unix/native/libawt_xawt/awt/screencast_pipewire.c @@ -43,6 +43,9 @@ int DEBUG_SCREENCAST_ENABLED = FALSE; (*env)->ExceptionDescribe(env); \ } +static volatile gboolean sessionClosed = TRUE; +static GString *activeSessionToken; + struct ScreenSpace screenSpace = {0}; static struct PwLoopData pw = {0}; @@ -89,8 +92,8 @@ static void doCleanup() { struct ScreenProps *screenProps = &screenSpace.screens[i]; if (screenProps->data) { if (screenProps->data->stream) { - fp_pw_thread_loop_lock(pw.loop); fp_pw_stream_disconnect(screenProps->data->stream); + fp_pw_thread_loop_lock(pw.loop); fp_pw_stream_destroy(screenProps->data->stream); fp_pw_thread_loop_unlock(pw.loop); screenProps->data->stream = NULL; @@ -123,7 +126,11 @@ static void doCleanup() { if (screenSpace.screens) { free(screenSpace.screens); screenSpace.screens = NULL; + screenSpace.screenCount = 0; } + + gtk->g_string_set_size(activeSessionToken, 0); + sessionClosed = TRUE; } /** @@ -132,6 +139,24 @@ static void doCleanup() { static gboolean initScreencast(const gchar *token, GdkRectangle *affectedBounds, gint affectedBoundsLength) { + gboolean isSameToken = !token + ? FALSE + : strcmp(token, activeSessionToken->str) == 0; + + if (!sessionClosed) { + if (isSameToken) { + DEBUG_SCREENCAST("Reusing active session.\n", NULL); + return TRUE; + } else { + DEBUG_SCREENCAST( + "Active session has a different token |%s| -> |%s|," + " closing current session.\n", + activeSessionToken->str, token + ); + doCleanup(); + } + } + fp_pw_init(NULL, NULL); pw.pwFd = RESULT_ERROR; @@ -145,6 +170,8 @@ static gboolean initScreencast(const gchar *token, return FALSE; } + gtk->g_string_printf(activeSessionToken, "%s", token); + sessionClosed = FALSE; return TRUE; } @@ -386,8 +413,19 @@ static gboolean connectStream(int index) { data->screenProps = &screenSpace.screens[index]; - data->hasFormat = FALSE; + if (!sessionClosed && data->stream) { + fp_pw_thread_loop_lock(pw.loop); + int result = fp_pw_stream_set_active(data->stream, TRUE); + fp_pw_thread_loop_unlock(pw.loop); + + DEBUG_SCREEN_PREFIX(data->screenProps, + "stream %p: activate result |%i|\n", + data->stream, result); + + return result == 0; // 0 - success + }; + data->hasFormat = FALSE; data->stream = fp_pw_stream_new( pw.core, @@ -505,60 +543,64 @@ static const struct pw_core_events coreEvents = { * @return TRUE on success */ static gboolean doLoop(GdkRectangle requestedArea) { - pw.loop = fp_pw_thread_loop_new("AWT Pipewire Thread", NULL); - - if (!pw.loop) { - DEBUG_SCREENCAST("!!! Could not create a loop\n", NULL); - doCleanup(); - return FALSE; - } - - pw.context = fp_pw_context_new( - fp_pw_thread_loop_get_loop(pw.loop), - NULL, - 0 - ); + if (!pw.loop && !sessionClosed) { + pw.loop = fp_pw_thread_loop_new("AWT Pipewire Thread", NULL); - if (!pw.context) { - DEBUG_SCREENCAST("!!! Could not create a pipewire context\n", NULL); - doCleanup(); - return FALSE; - } + if (!pw.loop) { + DEBUG_SCREENCAST("!!! Could not create a loop\n", NULL); + doCleanup(); + return FALSE; + } - if (fp_pw_thread_loop_start(pw.loop) != 0) { - DEBUG_SCREENCAST("!!! Could not start pipewire thread loop\n", NULL); - doCleanup(); - return FALSE; - } + pw.context = fp_pw_context_new( + fp_pw_thread_loop_get_loop(pw.loop), + NULL, + 0 + ); - fp_pw_thread_loop_lock(pw.loop); + if (!pw.context) { + DEBUG_SCREENCAST("!!! Could not create a pipewire context\n", NULL); + doCleanup(); + return FALSE; + } - pw.core = fp_pw_context_connect_fd( - pw.context, - pw.pwFd, - NULL, - 0 - ); + if (fp_pw_thread_loop_start(pw.loop) != 0) { + DEBUG_SCREENCAST("!!! Could not start pipewire thread loop\n", NULL); + doCleanup(); + return FALSE; + } - if (!pw.core) { - DEBUG_SCREENCAST("!!! Could not create pipewire core\n", NULL); - goto fail; - } + fp_pw_thread_loop_lock(pw.loop); - pw_core_add_listener(pw.core, &pw.coreListener, &coreEvents, NULL); + pw.core = fp_pw_context_connect_fd( + pw.context, + pw.pwFd, + NULL, + 0 + ); - for (int i = 0; i < screenSpace.screenCount; ++i) { - struct PwStreamData *data = - (struct PwStreamData*) malloc(sizeof (struct PwStreamData)); - if (!data) { - ERR("failed to allocate memory\n"); + if (!pw.core) { + DEBUG_SCREENCAST("!!! Could not create pipewire core\n", NULL); goto fail; } - memset(data, 0, sizeof (struct PwStreamData)); + pw_core_add_listener(pw.core, &pw.coreListener, &coreEvents, NULL); + } + for (int i = 0; i < screenSpace.screenCount; ++i) { struct ScreenProps *screen = &screenSpace.screens[i]; - screen->data = data; + if (!screen->data && !sessionClosed) { + struct PwStreamData *data = + (struct PwStreamData*) malloc(sizeof (struct PwStreamData)); + if (!data) { + ERR("failed to allocate memory\n"); + goto fail; + } + + memset(data, 0, sizeof (struct PwStreamData)); + + screen->data = data; + } DEBUG_SCREEN_PREFIX(screen, "@@@ adding screen %i\n", i); if (checkScreen(i, requestedArea)) { @@ -746,6 +788,8 @@ JNIEXPORT jboolean JNICALL Java_sun_awt_screencast_ScreencastHelper_loadPipewire return JNI_FALSE; } + activeSessionToken = gtk->g_string_new(""); + gboolean usable = initXdgDesktopPortal(); portalScreenCastCleanup(); return usable; @@ -783,6 +827,17 @@ static void arrayToRectangles(JNIEnv *env, (*env)->ReleaseIntArrayElements(env, boundsArray, body, 0); } +/* + * Class: sun_awt_screencast_ScreencastHelper + * Method: closeSession + * Signature: ()V + */ +JNIEXPORT void JNICALL +Java_sun_awt_screencast_ScreencastHelper_closeSession(JNIEnv *env, jclass cls) { + DEBUG_SCREENCAST("closing screencast session\n\n", NULL); + doCleanup(); +} + /* * Class: sun_awt_screencast_ScreencastHelper * Method: getRGBPixelsImpl @@ -805,7 +860,7 @@ JNIEXPORT jint JNICALL Java_sun_awt_screencast_ScreencastHelper_getRGBPixelsImpl boundsLen = (*env)->GetArrayLength(env, affectedScreensBoundsArray); EXCEPTION_CHECK_DESCRIBE(); if (boundsLen % 4 != 0) { - DEBUG_SCREENCAST("%s:%i incorrect array length\n", __FUNCTION__, __LINE__); + DEBUG_SCREENCAST("incorrect array length\n", NULL); return RESULT_ERROR; } affectedBoundsLength = boundsLen / 4; @@ -896,11 +951,12 @@ JNIEXPORT jint JNICALL Java_sun_awt_screencast_ScreencastHelper_getRGBPixelsImpl fp_pw_thread_loop_lock(pw.loop); fp_pw_stream_set_active(screenProps->data->stream, FALSE); - fp_pw_stream_disconnect(screenProps->data->stream); fp_pw_thread_loop_unlock(pw.loop); + + screenProps->captureDataReady = FALSE; } } - doCleanup(); + releaseToken(env, jtoken, token); return 0; } diff --git a/src/java.desktop/unix/native/libawt_xawt/awt/screencast_portal.c b/src/java.desktop/unix/native/libawt_xawt/awt/screencast_portal.c index e314359a32543..8590cf27da20f 100644 --- a/src/java.desktop/unix/native/libawt_xawt/awt/screencast_portal.c +++ b/src/java.desktop/unix/native/libawt_xawt/awt/screencast_portal.c @@ -777,6 +777,10 @@ int portalScreenCastOpenPipewireRemote() { } void portalScreenCastCleanup() { + if (!portal) { + return; + } + if (portal->screenCastSessionHandle) { gtk->g_dbus_connection_call_sync( portal->connection, @@ -796,9 +800,6 @@ void portalScreenCastCleanup() { portal->screenCastSessionHandle = NULL; } - if (!portal) { - return; - } if (portal->connection) { gtk->g_object_unref(portal->connection); portal->connection = NULL;