mirror of
https://github.com/JetBrains/JetBrainsRuntime.git
synced 2025-12-06 09:29:38 +01:00
8313697: [XWayland][Screencast] consequent getPixelColor calls are slow
8310334: [XWayland][Screencast] screen capture error message in debug
Backport-of: 2f04bc5f93
This commit is contained in:
committed by
Vitaly Provodin
parent
b598d8999a
commit
f49a1bfcf5
@@ -35,6 +35,8 @@ import java.security.AccessController;
|
||||
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 @@ public class ScreencastHelper {
|
||||
).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<Rectangle> affectedScreenBounds = getSystemScreensBounds()
|
||||
|
||||
@@ -324,6 +324,10 @@ final class TokenStorage {
|
||||
return tokenItem;
|
||||
})
|
||||
.filter(Objects::nonNull)
|
||||
.sorted((t1, t2) -> //Token with more screens preferred
|
||||
t2.allowedScreensBounds.size()
|
||||
- t1.allowedScreensBounds.size()
|
||||
)
|
||||
.toList();
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
@@ -3112,6 +3113,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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -717,6 +717,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);
|
||||
|
||||
|
||||
@@ -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 && !sessionClosed) {
|
||||
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;
|
||||
}
|
||||
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
|
||||
);
|
||||
pw.context = fp_pw_context_new(
|
||||
fp_pw_thread_loop_get_loop(pw.loop),
|
||||
NULL,
|
||||
0
|
||||
);
|
||||
|
||||
if (!pw.context) {
|
||||
DEBUG_SCREENCAST("!!! Could not create a pipewire context\n", NULL);
|
||||
doCleanup();
|
||||
return FALSE;
|
||||
}
|
||||
if (!pw.context) {
|
||||
DEBUG_SCREENCAST("!!! Could not create a pipewire context\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;
|
||||
}
|
||||
if (fp_pw_thread_loop_start(pw.loop) != 0) {
|
||||
DEBUG_SCREENCAST("!!! Could not start pipewire thread loop\n", NULL);
|
||||
doCleanup();
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
fp_pw_thread_loop_lock(pw.loop);
|
||||
fp_pw_thread_loop_lock(pw.loop);
|
||||
|
||||
pw.core = fp_pw_context_connect_fd(
|
||||
pw.context,
|
||||
pw.pwFd,
|
||||
NULL,
|
||||
0
|
||||
);
|
||||
pw.core = fp_pw_context_connect_fd(
|
||||
pw.context,
|
||||
pw.pwFd,
|
||||
NULL,
|
||||
0
|
||||
);
|
||||
|
||||
if (!pw.core) {
|
||||
DEBUG_SCREENCAST("!!! Could not create pipewire core\n", NULL);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
pw_core_add_listener(pw.core, &pw.coreListener, &coreEvents, NULL);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user