mirror of
https://repo.dec05eba.com/gpu-screen-recorder-ui
synced 2026-01-31 01:13:04 +09:00
Hyprland and KDE workarounds should work with flatpak now
This commit is contained in:
@@ -132,6 +132,14 @@ install_data(
|
||||
install_mode: 'rwxr-xr-x'
|
||||
)
|
||||
|
||||
executable(
|
||||
'gsr-hyprland-helper',
|
||||
[
|
||||
'tools/gsr-hyprland-helper/main.c'
|
||||
],
|
||||
install : true
|
||||
)
|
||||
|
||||
install_subdir('images', install_dir : gsr_ui_resources_path)
|
||||
install_subdir('fonts', install_dir : gsr_ui_resources_path)
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "../include/HyprlandWorkaround.hpp"
|
||||
#include "include/Process.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <iostream>
|
||||
@@ -15,121 +16,42 @@ namespace gsr {
|
||||
static ActiveHyprlandWindow *active_hyprland_window = nullptr;
|
||||
static bool hyprland_listener_thread_started = false;
|
||||
|
||||
static std::string get_hyprland_socket_path() {
|
||||
const char* xdg_runtime_dir = std::getenv("XDG_RUNTIME_DIR");
|
||||
const char* instance_sig = std::getenv("HYPRLAND_INSTANCE_SIGNATURE");
|
||||
|
||||
if (!xdg_runtime_dir || !instance_sig) {
|
||||
std::cerr << "Error: Env vars XDG_RUNTIME_DIR or HYPRLAND_INSTANCE_SIGNATURE not set." << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return std::string(xdg_runtime_dir) + "/hypr/" + instance_sig + "/.socket2.sock";
|
||||
}
|
||||
|
||||
static void hyprland_listener_thread() {
|
||||
int sock_fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (sock_fd == -1) {
|
||||
perror("socket");
|
||||
const bool inside_flatpak = access("/app/manifest.json", F_OK) == 0;
|
||||
|
||||
const std::string hyprland_helper_bin = (
|
||||
inside_flatpak ?
|
||||
"flatpak-spawn --host -- /var/lib/flatpak/app/com.dec05eba.gpu_screen_recorder/current/active/files/bin/gsr-hyprland-helper"
|
||||
: "/usr/bin/gsr-hyprland-helper"
|
||||
);
|
||||
|
||||
FILE* pipe = popen(hyprland_helper_bin.c_str(), "r");
|
||||
if (!pipe) {
|
||||
std::cerr << "Failed to start gsr-hyprland-helper process\n";
|
||||
return;
|
||||
}
|
||||
|
||||
struct sockaddr_un addr;
|
||||
std::memset(&addr, 0, sizeof(addr));
|
||||
addr.sun_family = AF_UNIX;
|
||||
std::cerr << "Started Hyprland helper thread\n";
|
||||
|
||||
std::string socket_path = get_hyprland_socket_path();
|
||||
std::strncpy(addr.sun_path, socket_path.c_str(), sizeof(addr.sun_path) - 1);
|
||||
char buffer[4096];
|
||||
const std::string prefix = "Window title changed: ";
|
||||
|
||||
if (connect(sock_fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
|
||||
perror("connect");
|
||||
close(sock_fd);
|
||||
return;
|
||||
}
|
||||
while (fgets(buffer, sizeof(buffer), pipe) != nullptr) {
|
||||
std::string line(buffer);
|
||||
|
||||
std::cout << "Connected to Hyprland event socket: " << socket_path << std::endl;
|
||||
|
||||
std::array<char, 4096> buffer; // fixed buffer, scary!
|
||||
std::string incomplete_line;
|
||||
|
||||
while (hyprland_listener_thread_started) {
|
||||
ssize_t bytes_read = read(sock_fd, buffer.data(), buffer.size());
|
||||
|
||||
if (bytes_read == 0) {
|
||||
std::cerr << "Connection closed by Hyprland" << std::endl;
|
||||
hyprland_listener_thread_started = false;
|
||||
break;
|
||||
if (!line.empty() && line.back() == '\n') {
|
||||
line.pop_back();
|
||||
}
|
||||
|
||||
if (bytes_read < 0) {
|
||||
perror("read");
|
||||
break;
|
||||
}
|
||||
size_t pos = line.find(prefix);
|
||||
if (pos != std::string::npos) {
|
||||
std::string title = line.substr(pos + prefix.length());
|
||||
|
||||
std::string chunk(buffer.data(), bytes_read);
|
||||
std::string data = incomplete_line + chunk;
|
||||
|
||||
size_t start_pos = 0;
|
||||
size_t newline_pos;
|
||||
|
||||
while ((newline_pos = data.find('\n', start_pos)) != std::string::npos) {
|
||||
std::string line = data.substr(start_pos, newline_pos - start_pos);
|
||||
|
||||
size_t delimiter = line.find(">>");
|
||||
|
||||
if (delimiter != std::string::npos) {
|
||||
std::string event_name = line.substr(0, delimiter);
|
||||
std::string event_data = line.substr(delimiter+2);
|
||||
|
||||
if (event_name == "activewindowv2") {
|
||||
active_hyprland_window->window_id = event_data;
|
||||
}
|
||||
|
||||
if (event_name == "activewindow") {
|
||||
size_t title_delimiter = event_data.find(",");
|
||||
if (title_delimiter != std::string::npos) {
|
||||
std::string window_title = event_data.substr(title_delimiter+1);
|
||||
if (window_title == "gsr ui" || window_title == "gsr notify") {
|
||||
// ignoring ourselves
|
||||
start_pos = newline_pos + 1;
|
||||
continue;
|
||||
}
|
||||
active_hyprland_window->title = window_title;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (event_name == "windowtitlev2") {
|
||||
size_t data_delimiter = event_data.find(",");
|
||||
if (data_delimiter != std::string::npos) {
|
||||
std::string win_id = event_data.substr(0, data_delimiter);
|
||||
std::string new_window_title = event_data.substr(data_delimiter+1);
|
||||
if (new_window_title == "gsr ui" || new_window_title == "gsr notify") {
|
||||
// ignoring ourselves
|
||||
start_pos = newline_pos + 1;
|
||||
continue;
|
||||
}
|
||||
if (active_hyprland_window->window_id != win_id) {
|
||||
// not our case
|
||||
start_pos = newline_pos + 1;
|
||||
continue;
|
||||
}
|
||||
active_hyprland_window->title = new_window_title;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
start_pos = newline_pos + 1;
|
||||
}
|
||||
|
||||
if (start_pos < data.length()) {
|
||||
incomplete_line = data.substr(start_pos);
|
||||
} else {
|
||||
incomplete_line.clear();
|
||||
active_hyprland_window->title = title;
|
||||
}
|
||||
}
|
||||
|
||||
close(sock_fd);
|
||||
pclose(pipe);
|
||||
}
|
||||
|
||||
std::string get_current_hyprland_window_title() {
|
||||
|
||||
@@ -24,13 +24,11 @@ namespace gsr {
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
const bool inside_flatpak = access("/app/manifest.json", F_OK) == 0;
|
||||
|
||||
FILE* pipe = popen("/usr/bin/gsr-kwin-helper", "r");
|
||||
const std::string kwin_helper_bin = inside_flatpak ? "/app/bin/gsr-kwin-helper" : "/usr/bin/gsr-kwin-helper";
|
||||
|
||||
FILE* pipe = popen(kwin_helper_bin.c_str(), "r");
|
||||
if (!pipe) {
|
||||
std::cerr << "Failed to start gsr-kwin-helper process\n";
|
||||
return;
|
||||
|
||||
243
tools/gsr-hyprland-helper/main.c
Normal file
243
tools/gsr-hyprland-helper/main.c
Normal file
@@ -0,0 +1,243 @@
|
||||
#include "unistd.h"
|
||||
#include "string.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
|
||||
|
||||
struct ActiveHyprlandWindow {
|
||||
char* window_id;
|
||||
char* title;
|
||||
};
|
||||
|
||||
|
||||
struct ActiveHyprlandWindow* active_window;
|
||||
|
||||
|
||||
const char* get_hyprland_socket_path() {
|
||||
const char* xdg_runtime_dir = getenv("XDG_RUNTIME_DIR");
|
||||
const char* instance_sig = getenv("HYPRLAND_INSTANCE_SIGNATURE");
|
||||
if (!xdg_runtime_dir || !instance_sig) {
|
||||
fprintf(stderr, "Error: Env vars not set.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// allocate buffer for the full path
|
||||
size_t path_len = strlen(xdg_runtime_dir) + strlen(instance_sig) + 32;
|
||||
char* socket_path = malloc(path_len);
|
||||
if (!socket_path) {
|
||||
fprintf(stderr, "Error: Memory allocation failed.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
snprintf(socket_path, path_len, "%s/hypr/%s/.socket2.sock", xdg_runtime_dir, instance_sig);
|
||||
return socket_path;
|
||||
}
|
||||
|
||||
|
||||
void print_window_title(void) {
|
||||
if (strlen(active_window->title) == 0) {
|
||||
printf("Window title changed: %s\n", "Desktop");
|
||||
fflush(stdout);
|
||||
return;
|
||||
}
|
||||
printf("Window title changed: %s\n", active_window->title);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
|
||||
void handle_ipc(void) {
|
||||
int sock_fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (sock_fd == -1) {
|
||||
perror("socket");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
active_window = malloc(sizeof(struct ActiveHyprlandWindow));
|
||||
active_window->window_id = NULL;
|
||||
active_window->title = NULL;
|
||||
|
||||
|
||||
struct sockaddr_un addr;
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sun_family = AF_UNIX;
|
||||
|
||||
|
||||
const char* socket_path = get_hyprland_socket_path();
|
||||
strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path) - 1);
|
||||
|
||||
|
||||
if (connect(sock_fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
|
||||
perror("connect");
|
||||
close(sock_fd);
|
||||
free((void*)socket_path);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
printf("Connected to Hyprland socket: %s\n", socket_path);
|
||||
fflush(stdout);
|
||||
free((void*)socket_path);
|
||||
|
||||
|
||||
char buffer[4096];
|
||||
char* incomplete_line = NULL;
|
||||
size_t incomplete_len = 0;
|
||||
|
||||
|
||||
while (1) {
|
||||
ssize_t bytes_read = read(sock_fd, buffer, sizeof(buffer));
|
||||
|
||||
|
||||
if (bytes_read == 0) {
|
||||
fprintf(stderr, "Connection closed by Hyprland\n");
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if (bytes_read < 0) {
|
||||
perror("read");
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
size_t total_len = incomplete_len + bytes_read;
|
||||
char *data = malloc(total_len + 1);
|
||||
if (!data) {
|
||||
perror("malloc");
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if (incomplete_line) {
|
||||
memcpy(data, incomplete_line, incomplete_len);
|
||||
fflush(stdout);
|
||||
free(incomplete_line);
|
||||
incomplete_line = NULL;
|
||||
}
|
||||
memcpy(data + incomplete_len, buffer, bytes_read);
|
||||
data[total_len] = '\0';
|
||||
|
||||
|
||||
char* line_start = data;
|
||||
char* newline_pos;
|
||||
|
||||
|
||||
while ((newline_pos = strchr(line_start, '\n')) != NULL) {
|
||||
*newline_pos = '\0';
|
||||
char *line = line_start;
|
||||
|
||||
|
||||
char *delimiter = strstr(line, ">>");
|
||||
|
||||
|
||||
if (delimiter != NULL) {
|
||||
*delimiter = '\0';
|
||||
char *event_name = line;
|
||||
char *event_data = delimiter + 2;
|
||||
|
||||
|
||||
if (strcmp(event_name, "activewindowv2") == 0) {
|
||||
fflush(stdout);
|
||||
if (active_window->window_id) {
|
||||
free(active_window->window_id);
|
||||
}
|
||||
active_window->window_id = strdup(event_data);
|
||||
}
|
||||
|
||||
|
||||
if (strcmp(event_name, "activewindow") == 0) {
|
||||
char *title_delimiter = strchr(event_data, ',');
|
||||
if (title_delimiter != NULL) {
|
||||
char *window_title = title_delimiter + 1;
|
||||
if (strcmp(window_title, "gsr ui") == 0 ||
|
||||
strcmp(window_title, "gsr notify") == 0) {
|
||||
line_start = newline_pos + 1;
|
||||
continue;
|
||||
}
|
||||
fflush(stdout);
|
||||
if (active_window->title) {
|
||||
free(active_window->title);
|
||||
}
|
||||
active_window->title = strdup(window_title);
|
||||
print_window_title();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (strcmp(event_name, "windowtitlev2") == 0) {
|
||||
char *data_delimiter = strchr(event_data, ',');
|
||||
if (data_delimiter != NULL) {
|
||||
*data_delimiter = '\0';
|
||||
char *win_id = event_data;
|
||||
char *new_window_title = data_delimiter + 1;
|
||||
|
||||
if (strcmp(new_window_title, "gsr ui") == 0 ||
|
||||
strcmp(new_window_title, "gsr notify") == 0) {
|
||||
line_start = newline_pos + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strcmp(active_window->window_id, win_id) != 0) {
|
||||
line_start = newline_pos + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
fflush(stdout);
|
||||
if (active_window->title) {
|
||||
free(active_window->title);
|
||||
}
|
||||
active_window->title = strdup(new_window_title);
|
||||
print_window_title();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
line_start = newline_pos + 1;
|
||||
}
|
||||
|
||||
|
||||
size_t remaining = strlen(line_start);
|
||||
if (remaining > 0) {
|
||||
incomplete_line = malloc(remaining + 1);
|
||||
if (incomplete_line) {
|
||||
memcpy(incomplete_line, line_start, remaining);
|
||||
incomplete_line[remaining] = '\0';
|
||||
incomplete_len = remaining;
|
||||
}
|
||||
} else {
|
||||
incomplete_len = 0;
|
||||
}
|
||||
|
||||
|
||||
fflush(stdout);
|
||||
free(data);
|
||||
}
|
||||
|
||||
|
||||
if (incomplete_line) {
|
||||
fflush(stdout);
|
||||
free(incomplete_line);
|
||||
}
|
||||
|
||||
if (active_window) {
|
||||
if (active_window->window_id) {
|
||||
free(active_window->window_id);
|
||||
}
|
||||
if (active_window->title) {
|
||||
free(active_window->title);
|
||||
}
|
||||
free(active_window);
|
||||
}
|
||||
|
||||
close(sock_fd);
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
handle_ipc();
|
||||
return 0;
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "dbus/dbus.h"
|
||||
#include <unistd.h>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
@@ -56,7 +57,17 @@ public:
|
||||
|
||||
std::cout << "DBus server initialized on com.dec05eba.gsr_kwin_helper\n";
|
||||
|
||||
if (!load_kwin_script(connection, KWIN_HELPER_SCRIPT_PATH)) {
|
||||
const bool inside_flatpak = access("/app/manifest.json", F_OK) == 0;
|
||||
|
||||
std::string helper_path = (
|
||||
!inside_flatpak
|
||||
? KWIN_HELPER_SCRIPT_PATH
|
||||
: "/var/lib/flatpak/app/com.dec05eba.gpu_screen_recorder/current/active/files/share/gsr-ui/gsrkwinhelper.js"
|
||||
);
|
||||
|
||||
std::cout << "KWin script path: " << helper_path << std::endl;
|
||||
|
||||
if (!load_kwin_script(connection, helper_path.c_str())) {
|
||||
std::cerr << "Warning: Failed to load KWin script\n";
|
||||
}
|
||||
|
||||
@@ -218,7 +229,6 @@ int main(int argc, char** argv) {
|
||||
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