mirror of
https://repo.dec05eba.com/gpu-screen-recorder-ui
synced 2026-01-31 01:13:04 +09:00
Added KWin workaround to get current window title
This commit is contained in:
11
include/KwinWorkaround.hpp
Normal file
11
include/KwinWorkaround.hpp
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
|
||||
namespace gsr {
|
||||
struct ActiveKwinWindow {
|
||||
std::string title = "Game";
|
||||
};
|
||||
|
||||
void start_kwin_helper_thread();
|
||||
std::string get_current_kwin_window_title();
|
||||
}
|
||||
20
meson.build
20
meson.build
@@ -42,6 +42,7 @@ src = [
|
||||
'src/CursorTracker/CursorTrackerWayland.cpp',
|
||||
'src/Utils.cpp',
|
||||
'src/HyprlandWorkaround.cpp',
|
||||
'src/KwinWorkaround.cpp',
|
||||
'src/WindowUtils.cpp',
|
||||
'src/RegionSelector.cpp',
|
||||
'src/WindowSelector.cpp',
|
||||
@@ -71,6 +72,8 @@ icons_path = join_paths(prefix, datadir, 'icons')
|
||||
add_project_arguments('-DGSR_UI_VERSION="' + meson.project_version() + '"', language: ['c', 'cpp'])
|
||||
add_project_arguments('-DGSR_FLATPAK_VERSION="5.12.0"', language: ['c', 'cpp'])
|
||||
|
||||
add_project_arguments('-DKWIN_HELPER_SCRIPT_PATH="' + gsr_ui_resources_path + '/gsrkwinhelper.js"', language: ['c', 'cpp'])
|
||||
|
||||
executable(
|
||||
meson.project_name(),
|
||||
src,
|
||||
@@ -112,6 +115,23 @@ executable(
|
||||
install : true
|
||||
)
|
||||
|
||||
executable(
|
||||
'gsr-kwin-helper',
|
||||
[
|
||||
'tools/gsr-kwin-helper/main.cpp'
|
||||
],
|
||||
install : true,
|
||||
dependencies: [
|
||||
dependency('dbus-1'),
|
||||
]
|
||||
)
|
||||
|
||||
install_data(
|
||||
'tools/gsr-kwin-helper/gsrkwinhelper.js',
|
||||
install_dir: gsr_ui_resources_path,
|
||||
install_mode: 'rwxr-xr-x'
|
||||
)
|
||||
|
||||
install_subdir('images', install_dir : gsr_ui_resources_path)
|
||||
install_subdir('fonts', install_dir : gsr_ui_resources_path)
|
||||
|
||||
|
||||
76
src/KwinWorkaround.cpp
Normal file
76
src/KwinWorkaround.cpp
Normal file
@@ -0,0 +1,76 @@
|
||||
#include "../include/KwinWorkaround.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <iostream>
|
||||
#include <sys/types.h>
|
||||
#include <thread>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <array>
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
|
||||
namespace gsr {
|
||||
static ActiveKwinWindow *active_kwin_window = nullptr;
|
||||
static bool kwin_helper_thread_started = false;
|
||||
|
||||
std::string get_current_kwin_window_title() {
|
||||
if (active_kwin_window == nullptr) {
|
||||
return "Game";
|
||||
}
|
||||
|
||||
return active_kwin_window->title;
|
||||
}
|
||||
|
||||
void kwin_script_thread() {
|
||||
const bool inside_flatpak = getenv("FLATPAK_ID") != NULL;
|
||||
if(inside_flatpak) {
|
||||
std::cerr << "Sorry, KWin workaround isn't available for Flatpak yet. Stay tuned!\n";
|
||||
return;
|
||||
}
|
||||
|
||||
FILE* pipe = popen("/usr/bin/gsr-kwin-helper", "r");
|
||||
if (!pipe) {
|
||||
std::cerr << "Failed to start gsr-kwin-helper process\n";
|
||||
return;
|
||||
}
|
||||
|
||||
std::cerr << "Started a KWin helper thread\n";
|
||||
|
||||
char buffer[4096];
|
||||
const std::string prefix = "Active window title set to: ";
|
||||
|
||||
while (fgets(buffer, sizeof(buffer), pipe) != nullptr) {
|
||||
std::string line(buffer);
|
||||
|
||||
if (!line.empty() && line.back() == '\n') {
|
||||
line.pop_back();
|
||||
}
|
||||
|
||||
size_t pos = line.find(prefix);
|
||||
if (pos != std::string::npos) {
|
||||
std::string title = line.substr(pos + prefix.length());
|
||||
active_kwin_window->title = title;
|
||||
}
|
||||
}
|
||||
|
||||
pclose(pipe);
|
||||
}
|
||||
|
||||
void start_kwin_helper_thread() {
|
||||
if (kwin_helper_thread_started) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (active_kwin_window == nullptr) {
|
||||
active_kwin_window = new ActiveKwinWindow();
|
||||
}
|
||||
|
||||
kwin_helper_thread_started = true;
|
||||
|
||||
std::thread([&] {
|
||||
kwin_script_thread();
|
||||
}).detach();
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "../include/gui/ScreenshotSettingsPage.hpp"
|
||||
#include "../include/gui/GlobalSettingsPage.hpp"
|
||||
#include "../include/gui/Utils.hpp"
|
||||
#include "../include/KwinWorkaround.hpp"
|
||||
#include "../include/HyprlandWorkaround.hpp"
|
||||
#include "../include/gui/PageStack.hpp"
|
||||
#include "../include/WindowUtils.hpp"
|
||||
@@ -553,6 +554,10 @@ namespace gsr {
|
||||
if (get_window_manager_name(x11_dpy).find("Hyprland") != std::string::npos) {
|
||||
start_hyprland_listener_thread();
|
||||
}
|
||||
|
||||
if (get_window_manager_name(x11_dpy) == "KWin") {
|
||||
start_kwin_helper_thread();
|
||||
}
|
||||
}
|
||||
|
||||
update_led_indicator_after_settings_change();
|
||||
@@ -1974,10 +1979,16 @@ namespace gsr {
|
||||
|
||||
const std::string wm_name = get_window_manager_name(display);
|
||||
const bool is_hyprland = wm_name.find("Hyprland") != std::string::npos;
|
||||
const bool is_kwin = wm_name == "KWin";
|
||||
|
||||
const bool inside_flatpak = getenv("FLATPAK_ID") != NULL;
|
||||
|
||||
if (is_hyprland) {
|
||||
focused_window_name = get_current_hyprland_window_title();
|
||||
}
|
||||
else if (is_kwin && !inside_flatpak) {
|
||||
focused_window_name = get_current_kwin_window_title();
|
||||
}
|
||||
else {
|
||||
if(focused_window_name.empty())
|
||||
focused_window_name = get_focused_window_name(display, WindowCaptureType::FOCUSED, false);
|
||||
|
||||
58
tools/gsr-kwin-helper/gsrkwinhelper.js
Normal file
58
tools/gsr-kwin-helper/gsrkwinhelper.js
Normal file
@@ -0,0 +1,58 @@
|
||||
const DAEMON_DBUS_NAME = "com.dec05eba.gsr_kwin_helper";
|
||||
|
||||
// utils
|
||||
function sendNewActiveWindowTitle(title) {
|
||||
callDBus(
|
||||
DAEMON_DBUS_NAME, "/", DAEMON_DBUS_NAME,
|
||||
"setActiveWindowTitle", title
|
||||
);
|
||||
}
|
||||
|
||||
function sendNewActiveWindowFullscreen(isFullscreen) {
|
||||
callDBus(
|
||||
DAEMON_DBUS_NAME, "/", DAEMON_DBUS_NAME,
|
||||
"setActiveWindowFullscreen", isFullscreen
|
||||
);
|
||||
}
|
||||
|
||||
// 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 || "");
|
||||
}
|
||||
};
|
||||
|
||||
const emitActiveFullscreen = () => {
|
||||
if (workspace.activeWindow === client) {
|
||||
sendNewActiveWindowFullscreen(client.fullScreen);
|
||||
}
|
||||
};
|
||||
|
||||
windowEventHandlers.set(client, {
|
||||
title: emitActiveTitle,
|
||||
fs: emitActiveFullscreen,
|
||||
});
|
||||
|
||||
client.captionChanged.connect(emitActiveTitle);
|
||||
client.fullScreenChanged.connect(emitActiveFullscreen);
|
||||
}
|
||||
|
||||
function updateActiveWindow(client) {
|
||||
if (!client) return;
|
||||
sendNewActiveWindowTitle(client.caption || "");
|
||||
sendNewActiveWindowFullscreen(client.fullScreen);
|
||||
subscribeToClient(client);
|
||||
}
|
||||
|
||||
// handle window focus changes
|
||||
workspace.windowActivated.connect(updateActiveWindow);
|
||||
|
||||
// handle initial state
|
||||
if (workspace.activeWindow) {
|
||||
updateActiveWindow(workspace.activeWindow);
|
||||
}
|
||||
225
tools/gsr-kwin-helper/main.cpp
Normal file
225
tools/gsr-kwin-helper/main.cpp
Normal file
@@ -0,0 +1,225 @@
|
||||
#include "dbus/dbus.h"
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
|
||||
const char* INTROSPECTION_XML =
|
||||
"<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\"\n"
|
||||
"\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n"
|
||||
"<node>\n"
|
||||
" <interface name='com.dec05eba.gsr_kwin_helper'>\n"
|
||||
" <method name='setActiveWindowTitle'>\n"
|
||||
" <arg type='s' name='title' direction='in'/>\n"
|
||||
" </method>\n"
|
||||
" </interface>\n"
|
||||
" <interface name='org.freedesktop.DBus.Introspectable'>\n"
|
||||
" <method name='Introspect'>\n"
|
||||
" <arg type='s' name='data' direction='out'/>\n"
|
||||
" </method>\n"
|
||||
" </interface>\n"
|
||||
"</node>\n";
|
||||
|
||||
|
||||
class GsrKwinHelper {
|
||||
public:
|
||||
std::string active_window_title;
|
||||
DBusConnection* connection = nullptr;
|
||||
DBusError err;
|
||||
|
||||
bool init() {
|
||||
dbus_error_init(&err);
|
||||
|
||||
connection = dbus_bus_get(DBUS_BUS_SESSION, &err);
|
||||
if (dbus_error_is_set(&err)) {
|
||||
std::cerr << "Failed to connect to session bus: " << err.message << "\n";
|
||||
dbus_error_free(&err);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!connection) {
|
||||
std::cerr << "Connection is null\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
int ret = dbus_bus_request_name(connection, "com.dec05eba.gsr_kwin_helper",
|
||||
DBUS_NAME_FLAG_REPLACE_EXISTING, &err);
|
||||
if (dbus_error_is_set(&err)) {
|
||||
std::cerr << "Failed to request name: " << err.message << "\n";
|
||||
dbus_error_free(&err);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ret != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
|
||||
std::cerr << "Not primary owner of the name\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
std::cout << "DBus server initialized on com.dec05eba.gsr_kwin_helper\n";
|
||||
|
||||
if (!load_kwin_script(connection, KWIN_HELPER_SCRIPT_PATH)) {
|
||||
std::cerr << "Warning: Failed to load KWin script\n";
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void run() {
|
||||
while (true) {
|
||||
dbus_connection_read_write(connection, 100);
|
||||
DBusMessage* msg = dbus_connection_pop_message(connection);
|
||||
|
||||
if (!msg) {
|
||||
continue;
|
||||
}
|
||||
|
||||
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.gsr_kwin_helper", "setActiveWindowTitle")) {
|
||||
handle_set_title(msg);
|
||||
}
|
||||
|
||||
dbus_message_unref(msg);
|
||||
}
|
||||
}
|
||||
|
||||
void handle_introspect(DBusMessage* msg) {
|
||||
DBusMessage* reply = dbus_message_new_method_return(msg);
|
||||
if (!reply) return;
|
||||
|
||||
DBusMessageIter args;
|
||||
dbus_message_iter_init_append(reply, &args);
|
||||
|
||||
if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &INTROSPECTION_XML)) {
|
||||
dbus_message_unref(reply);
|
||||
return;
|
||||
}
|
||||
|
||||
dbus_connection_send(connection, reply, nullptr);
|
||||
dbus_connection_flush(connection);
|
||||
dbus_message_unref(reply);
|
||||
}
|
||||
|
||||
void handle_set_title(DBusMessage* msg) {
|
||||
DBusMessageIter args;
|
||||
const char* title = nullptr;
|
||||
|
||||
if (!dbus_message_iter_init(msg, &args)) {
|
||||
send_error_reply(msg, "No arguments provided");
|
||||
return;
|
||||
}
|
||||
|
||||
if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_STRING) {
|
||||
send_error_reply(msg, "Expected string argument");
|
||||
return;
|
||||
}
|
||||
|
||||
dbus_message_iter_get_basic(&args, &title);
|
||||
|
||||
if (title) {
|
||||
active_window_title = title;
|
||||
std::cout << "Active window title set to: " << active_window_title << "\n";
|
||||
std::cout.flush();
|
||||
send_success_reply(msg);
|
||||
} else {
|
||||
send_error_reply(msg, "Failed to read string");
|
||||
}
|
||||
}
|
||||
|
||||
void send_success_reply(DBusMessage* msg) {
|
||||
DBusMessage* reply = dbus_message_new_method_return(msg);
|
||||
if (reply) {
|
||||
dbus_connection_send(connection, reply, nullptr);
|
||||
dbus_connection_flush(connection);
|
||||
dbus_message_unref(reply);
|
||||
}
|
||||
}
|
||||
|
||||
void send_error_reply(DBusMessage* msg, const char* error_msg) {
|
||||
DBusMessage* reply = dbus_message_new_error(msg, "com.dec05eba.gsr_kwin_helper.Error", error_msg);
|
||||
if (reply) {
|
||||
dbus_connection_send(connection, reply, nullptr);
|
||||
dbus_connection_flush(connection);
|
||||
dbus_message_unref(reply);
|
||||
}
|
||||
}
|
||||
|
||||
bool call_kwin_method(DBusConnection* conn, const char* method,
|
||||
const char* arg1 = nullptr, const char* arg2 = nullptr) {
|
||||
DBusMessage* msg = dbus_message_new_method_call(
|
||||
"org.kde.KWin",
|
||||
"/Scripting",
|
||||
"org.kde.kwin.Scripting",
|
||||
method
|
||||
);
|
||||
|
||||
if (!msg) {
|
||||
std::cerr << "Failed to create message for " << method << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (arg1) {
|
||||
dbus_message_append_args(msg, DBUS_TYPE_STRING, &arg1, DBUS_TYPE_INVALID);
|
||||
if (arg2) {
|
||||
dbus_message_append_args(msg, DBUS_TYPE_STRING, &arg2, DBUS_TYPE_INVALID);
|
||||
}
|
||||
}
|
||||
|
||||
DBusError err;
|
||||
dbus_error_init(&err);
|
||||
|
||||
// Send message and wait for reply (with 1 second timeout)
|
||||
DBusMessage* reply = dbus_connection_send_with_reply_and_block(conn, msg, 1000, &err);
|
||||
|
||||
dbus_message_unref(msg);
|
||||
|
||||
if (dbus_error_is_set(&err)) {
|
||||
std::cerr << "Error calling " << method << ": " << err.message << "\n";
|
||||
dbus_error_free(&err);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (reply) {
|
||||
dbus_message_unref(reply);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool load_kwin_script(DBusConnection* conn, const char* script_path) {
|
||||
// Unload existing script
|
||||
call_kwin_method(conn, "unloadScript", "gsrkwinhelper");
|
||||
|
||||
if (!call_kwin_method(conn, "loadScript", script_path, "gsrkwinhelper")) {
|
||||
std::cerr << "Failed to load KWin script\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!call_kwin_method(conn, "start")) {
|
||||
std::cerr << "Failed to start KWin script\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
std::cout << "KWin script loaded and started successfully\n";
|
||||
return true;
|
||||
}
|
||||
|
||||
~GsrKwinHelper() {
|
||||
if (connection) {
|
||||
dbus_bus_release_name(connection, "com.dec05eba.gsr_kwin_helper", nullptr);
|
||||
dbus_connection_unref(connection);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
GsrKwinHelper helper;
|
||||
|
||||
if (!helper.init()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::cout << "Server running. Helper script path: " << KWIN_HELPER_SCRIPT_PATH << "\n";
|
||||
helper.run();
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user