From 52afad5824c9e2d74eb40eb26e3fce2707dd3156 Mon Sep 17 00:00:00 2001 From: p0358 Date: Thu, 19 Feb 2026 22:29:57 +0100 Subject: [PATCH] kwin: emit window fullscreen info + refactor helper script The helper script was also refactored to minimize the amount of callbacks added and the memory used. There's no need to keep callbacks attached for non-active windows, which happened before. Also it should be more efficient and simpler to send info over with just a single DBus call (also if more fields were to be added). Both the script and the helper app will send/print info only if it changed since the previous one. Otherwise we'd keep spamming fullscreen false update when navigating the desktop and so on. --- include/KwinWorkaround.hpp | 2 + src/KwinWorkaround.cpp | 22 ++++---- tools/gsr-kwin-helper/gsrkwinhelper.js | 75 +++++++++++++------------- tools/gsr-kwin-helper/main.cpp | 45 ++++++++++++---- 4 files changed, 87 insertions(+), 57 deletions(-) diff --git a/include/KwinWorkaround.hpp b/include/KwinWorkaround.hpp index 0c512cb..200345c 100644 --- a/include/KwinWorkaround.hpp +++ b/include/KwinWorkaround.hpp @@ -5,8 +5,10 @@ namespace gsr { struct ActiveKwinWindow { std::string title = "Game"; + bool fullscreen = false; }; void start_kwin_helper_thread(); std::string get_current_kwin_window_title(); + bool get_current_kwin_window_fullscreen(); } diff --git a/src/KwinWorkaround.cpp b/src/KwinWorkaround.cpp index 7278a8c..6f06818 100644 --- a/src/KwinWorkaround.cpp +++ b/src/KwinWorkaround.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -24,7 +25,8 @@ namespace gsr { std::cerr << "Started a KWin helper thread\n"; char buffer[4096]; - const std::string prefix = "Active window title set to: "; + const std::string prefix_title = "Active window title set to: "; + const std::string prefix_fullscreen = "Active window fullscreen state set to: "; std::string line; while (fgets(buffer, sizeof(buffer), pipe) != nullptr) { @@ -34,15 +36,13 @@ namespace gsr { line.pop_back(); } - size_t pos = line.find(prefix); - if (pos != std::string::npos) { - std::string title = line.substr(pos + prefix.length()); - - if (title == "gsr ui" || title == "gsr notify") { - continue; // ignore the overlay and notification - } - + size_t pos = std::string::npos; + if ((pos = line.find(prefix_title)) != std::string::npos) { + std::string title = line.substr(pos + prefix_title.length()); active_kwin_window.title = std::move(title); + } else if ((pos = line.find(prefix_fullscreen)) != std::string::npos) { + std::string fullscreen = line.substr(pos + prefix_fullscreen.length()); + active_kwin_window.fullscreen = fullscreen == "1"; } } @@ -53,6 +53,10 @@ namespace gsr { return active_kwin_window.title; } + bool get_current_kwin_window_fullscreen() { + return active_kwin_window.fullscreen; + } + void start_kwin_helper_thread() { if (kwin_helper_thread_started) { return; diff --git a/tools/gsr-kwin-helper/gsrkwinhelper.js b/tools/gsr-kwin-helper/gsrkwinhelper.js index fd2cb72..8a5df47 100644 --- a/tools/gsr-kwin-helper/gsrkwinhelper.js +++ b/tools/gsr-kwin-helper/gsrkwinhelper.js @@ -1,52 +1,51 @@ const DAEMON_DBUS_NAME = "com.dec05eba.gpu_screen_recorder"; -// utils -function sendNewActiveWindowTitle(title) { +function dbusSendUpdateActiveWindow(title, isFullscreen) { callDBus( DAEMON_DBUS_NAME, "/", DAEMON_DBUS_NAME, - "setActiveWindowTitle", title + "updateActiveWindow", + title, isFullscreen, ); } -function sendNewActiveWindowFullscreen(isFullscreen) { - callDBus( - DAEMON_DBUS_NAME, "/", DAEMON_DBUS_NAME, - "setActiveWindowFullscreen", isFullscreen - ); +let prevWindow = null; +let prevEmitActiveWindowUpdate = null; + +let prevCaption = null; +let prevFullScreen = null; + +function emitActiveWindowUpdate(window) { + if (workspace.activeWindow === window) { + let caption = window.caption || ""; + let fullScreen = window.fullScreen || false; + if (caption !== prevCaption || fullScreen !== prevFullScreen) { + dbusSendUpdateActiveWindow(caption, fullScreen); + prevCaption = caption; + prevFullScreen = fullScreen; + } + } } -// track handlers to avoid duplicates -const windowEventHandlers = new Map(); - -function subscribeToClient(client) { - if (!client || windowEventHandlers.has(client)) return; - - const emitActiveTitle = () => { - if (workspace.activeWindow === client) { - sendNewActiveWindowTitle(client.caption || ""); +function subscribeToWindow(window) { + if (!window) return; + if (prevWindow !== window) { + if (prevWindow !== null) { + prevWindow.captionChanged.disconnect(prevEmitActiveWindowUpdate); + prevWindow.fullScreenChanged.disconnect(prevEmitActiveWindowUpdate); } - }; - - const emitActiveFullscreen = () => { - if (workspace.activeWindow === client) { - sendNewActiveWindowFullscreen(client.fullScreen); - } - }; - - windowEventHandlers.set(client, { - title: emitActiveTitle, - fs: emitActiveFullscreen, - }); - - client.captionChanged.connect(emitActiveTitle); - client.fullScreenChanged.connect(emitActiveFullscreen); + let emitActiveWindowUpdateBound = emitActiveWindowUpdate.bind(null, window); + window.captionChanged.connect(emitActiveWindowUpdateBound); + window.fullScreenChanged.connect(emitActiveWindowUpdateBound); + prevWindow = window; + prevEmitActiveWindowUpdate = emitActiveWindowUpdateBound; + } } -function updateActiveWindow(client) { - if (!client) return; - sendNewActiveWindowTitle(client.caption || ""); - sendNewActiveWindowFullscreen(client.fullScreen); - subscribeToClient(client); +function updateActiveWindow(window) { + if (!window) return; + if (window.resourceName === "gsr-ui" || window.resourceName === "gsr-notify") return; // ignore the overlay and notification + emitActiveWindowUpdate(window); + subscribeToWindow(window); } // handle window focus changes @@ -55,4 +54,4 @@ workspace.windowActivated.connect(updateActiveWindow); // handle initial state if (workspace.activeWindow) { updateActiveWindow(workspace.activeWindow); -} \ No newline at end of file +} diff --git a/tools/gsr-kwin-helper/main.cpp b/tools/gsr-kwin-helper/main.cpp index e050c28..9f564eb 100644 --- a/tools/gsr-kwin-helper/main.cpp +++ b/tools/gsr-kwin-helper/main.cpp @@ -8,8 +8,9 @@ static const char* INTROSPECTION_XML = "\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n" "\n" " \n" - " \n" + " \n" " \n" + " \n" " \n" " \n" " \n" @@ -23,6 +24,7 @@ static const char* INTROSPECTION_XML = class GsrKwinHelper { public: std::string active_window_title; + bool active_window_fullscreen; DBusConnection* connection = nullptr; DBusError err; @@ -83,8 +85,8 @@ public: if (dbus_message_is_method_call(msg, "org.freedesktop.DBus.Introspectable", "Introspect")) { handle_introspect(msg); - } else if (dbus_message_is_method_call(msg, "com.dec05eba.gpu_screen_recorder", "setActiveWindowTitle")) { - handle_set_title(msg); + } else if (dbus_message_is_method_call(msg, "com.dec05eba.gpu_screen_recorder", "updateActiveWindow")) { + handle_update_active_window(msg); } dbus_message_unref(msg); @@ -108,9 +110,10 @@ public: dbus_message_unref(reply); } - void handle_set_title(DBusMessage* msg) { + void handle_update_active_window(DBusMessage* msg) { DBusMessageIter args; - const char* title = nullptr; + DBusBasicValue title; + DBusBasicValue fullscreen; if (!dbus_message_iter_init(msg, &args)) { send_error_reply(msg, "No arguments provided"); @@ -123,15 +126,37 @@ public: } dbus_message_iter_get_basic(&args, &title); + + if (!dbus_message_iter_next(&args)) { + send_error_reply(msg, "Not enough arguments provided"); + return; + } + + if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_BOOLEAN) { + send_error_reply(msg, "Expected boolean argument"); + return; + } + + dbus_message_iter_get_basic(&args, &fullscreen); - if (title) { - active_window_title = title; - std::cout << "Active window title set to: " << active_window_title << "\n"; - std::cout.flush(); - send_success_reply(msg); + if (title.str) { + if (active_window_title != title.str) { + active_window_title = title.str; + std::cout << "Active window title set to: " << active_window_title << "\n"; + std::cout.flush(); + } } else { send_error_reply(msg, "Failed to read string"); + return; } + + if (active_window_fullscreen != fullscreen.bool_val) { + active_window_fullscreen = fullscreen.bool_val; + std::cout << "Active window fullscreen state set to: " << active_window_fullscreen << "\n"; + std::cout.flush(); + } + + send_success_reply(msg); } void send_success_reply(DBusMessage* msg) {