mirror of
https://repo.dec05eba.com/gpu-screen-recorder-ui
synced 2026-04-07 20:08:07 +09:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e938241fe8 | ||
|
|
a9637f87e7 | ||
|
|
eb4ce76f01 | ||
|
|
d4d61b8c93 | ||
|
|
35a2fcc615 | ||
|
|
bb54b67956 | ||
|
|
35578e79ec | ||
|
|
ce6c924f58 | ||
|
|
77dd32a3ff | ||
|
|
b0def958c7 | ||
|
|
698538ac84 |
18
README.md
18
README.md
@@ -54,5 +54,19 @@ If you want to donate you can donate via bitcoin or monero.
|
||||
* Monero: 4An9kp2qW1C9Gah7ewv4JzcNFQ5TAX7ineGCqXWK6vQnhsGGcRpNgcn8r9EC3tMcgY7vqCKs3nSRXhejMHBaGvFdN2egYet
|
||||
|
||||
# Known issues
|
||||
* When the UI is open the wallpaper is shown instead of the game on Hyprland and Sway. This is an issue with Hyprland and Sway. It cant be fixed until the UI is redesigned to not be a fullscreen overlay.
|
||||
* Opening the UI when a game is fullscreened can mess up the game window a bit on Hyprland and Sway. I believe this is an issue in Hyprland and Sway.
|
||||
* When the UI is open the wallpaper is shown instead of the game on Hyprland. This is an issue with Hyprland. It cant be fixed until the UI is redesigned to not be a fullscreen overlay.
|
||||
* Opening the UI when a game is fullscreened can mess up the game window a bit on Hyprland. I believe this is an issue with Hyprland.
|
||||
|
||||
# FAQ
|
||||
## I get an error when trying to start the gpu-screen-recorder-ui.service systemd service
|
||||
If you have previously used the flatpak version of GPU Screen Recorder with the new UI then non-flatpak version of the systemd service will conflict with that.
|
||||
Run these commands to first remove the flatpak version of the systemd service:
|
||||
```
|
||||
systemctl stop --user gpu-screen-recorder-ui
|
||||
rm ~/.local/share/systemd/user/gpu-screen-recorder-ui.service
|
||||
systemctl --user daemon-reload
|
||||
```
|
||||
and then start and enable the non-flatpak systemd service:
|
||||
```
|
||||
systemctl enable --now --user gpu-screen-recorder-ui`
|
||||
```
|
||||
7
TODO
7
TODO
@@ -20,8 +20,6 @@ Make hotkeys configurable.
|
||||
|
||||
Filechooser should have the option to select list view, search bar and common folders/mounted drives on the left side for quick navigation. Also a button to create a new directory.
|
||||
|
||||
Support wayland (excluding gnome, or force xwayland on gnome).
|
||||
|
||||
Restart replay on system start if monitor resolution changes.
|
||||
|
||||
Show warning when selecting hevc/av1 on amd because of amd driver/ffmpeg bug.
|
||||
@@ -111,4 +109,7 @@ Re-enable hotkey disable option for flatpak.
|
||||
|
||||
Make gsr-ui flatpak systemd work nicely with non-flatpak gsr-ui. Maybe change ExecStart to do flatpak run ... || gsr-ui, but make it run as a shell command first with /bin/sh -c "".
|
||||
|
||||
When enabling X11 global hotkey again only grab lalt, not ralt.
|
||||
When enabling X11 global hotkey again only grab lalt, not ralt.
|
||||
|
||||
When adding window capture only add it to recording and streaming and do the window selection when recording starts, to make it more ergonomic with hotkeys.
|
||||
If hotkey for recording/streaming start is pressed on the button for start is clicked then hide the ui if it's visible and show the window selection option (cursor).
|
||||
Submodule depends/mglpp updated: 1ce8b2de75...e776c85d19
@@ -5,6 +5,7 @@
|
||||
#include "GsrInfo.hpp"
|
||||
#include "Config.hpp"
|
||||
#include "window_texture.h"
|
||||
#include "WindowUtils.hpp"
|
||||
|
||||
#include <mglpp/window/Window.hpp>
|
||||
#include <mglpp/window/Event.hpp>
|
||||
@@ -97,7 +98,7 @@ namespace gsr {
|
||||
void on_press_start_replay(bool disable_notification);
|
||||
void on_press_start_record();
|
||||
void on_press_start_stream();
|
||||
bool update_compositor_texture(const mgl_monitor *monitor);
|
||||
bool update_compositor_texture(const Monitor &monitor);
|
||||
|
||||
void force_window_on_top();
|
||||
private:
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <mglpp/system/vec.hpp>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <X11/Xlib.h>
|
||||
|
||||
namespace gsr {
|
||||
@@ -10,10 +11,16 @@ namespace gsr {
|
||||
CURSOR
|
||||
};
|
||||
|
||||
struct Monitor {
|
||||
mgl::vec2i position;
|
||||
mgl::vec2i size;
|
||||
};
|
||||
|
||||
Window get_focused_window(Display *dpy, WindowCaptureType cap_type);
|
||||
std::string get_focused_window_name(Display *dpy, WindowCaptureType window_capture_type);
|
||||
mgl::vec2i get_cursor_position(Display *dpy, Window *window);
|
||||
mgl::vec2i create_window_get_center_position(Display *display);
|
||||
std::string get_window_manager_name(Display *display);
|
||||
bool is_compositor_running(Display *dpy, int screen);
|
||||
std::vector<Monitor> get_monitors(Display *dpy);
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
project('gsr-ui', ['c', 'cpp'], version : '1.0.6', default_options : ['warning_level=2', 'cpp_std=c++17'], subproject_dir : 'depends')
|
||||
project('gsr-ui', ['c', 'cpp'], version : '1.0.7', default_options : ['warning_level=2', 'cpp_std=c++17'], subproject_dir : 'depends')
|
||||
|
||||
if get_option('buildtype') == 'debug'
|
||||
add_project_arguments('-g3', language : ['c', 'cpp'])
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "gsr-ui"
|
||||
type = "executable"
|
||||
version = "1.0.6"
|
||||
version = "1.0.7"
|
||||
platforms = ["posix"]
|
||||
|
||||
[lang.cpp]
|
||||
|
||||
@@ -164,7 +164,7 @@ namespace gsr {
|
||||
return std::abs(a - b) <= difference;
|
||||
}
|
||||
|
||||
static bool is_window_fullscreen_on_monitor(Display *display, Window window, const mgl_monitor *monitor) {
|
||||
static bool is_window_fullscreen_on_monitor(Display *display, Window window, const Monitor &monitor) {
|
||||
if(!window)
|
||||
return false;
|
||||
|
||||
@@ -173,8 +173,8 @@ namespace gsr {
|
||||
return false;
|
||||
|
||||
const int margin = 2;
|
||||
return diff_int(geometry.x, monitor->pos.x, margin) && diff_int(geometry.y, monitor->pos.y, margin)
|
||||
&& diff_int(geometry.width, monitor->size.x, margin) && diff_int(geometry.height, monitor->size.y, margin);
|
||||
return diff_int(geometry.x, monitor.position.x, margin) && diff_int(geometry.y, monitor.position.y, margin)
|
||||
&& diff_int(geometry.width, monitor.size.x, margin) && diff_int(geometry.height, monitor.size.y, margin);
|
||||
}
|
||||
|
||||
/*static bool is_window_fullscreen_on_monitor(Display *display, Window window, const mgl_monitor *monitors, int num_monitors) {
|
||||
@@ -279,15 +279,13 @@ namespace gsr {
|
||||
}
|
||||
|
||||
// Returns the first monitor if not found. Assumes there is at least one monitor connected.
|
||||
static const mgl_monitor* find_monitor_at_position(mgl::Window &window, mgl::vec2i pos) {
|
||||
const mgl_window *win = window.internal_window();
|
||||
assert(win->num_monitors > 0);
|
||||
for(int i = 0; i < win->num_monitors; ++i) {
|
||||
const mgl_monitor *mon = &win->monitors[i];
|
||||
if(mgl::IntRect({ mon->pos.x, mon->pos.y }, { mon->size.x, mon->size.y }).contains(pos))
|
||||
return mon;
|
||||
static const Monitor* find_monitor_at_position(const std::vector<Monitor> &monitors, mgl::vec2i pos) {
|
||||
assert(!monitors.empty());
|
||||
for(const Monitor &monitor : monitors) {
|
||||
if(mgl::IntRect(monitor.position, monitor.size).contains(pos))
|
||||
return &monitor;
|
||||
}
|
||||
return &win->monitors[0];
|
||||
return &monitors.front();
|
||||
}
|
||||
|
||||
static std::string get_power_supply_online_filepath() {
|
||||
@@ -712,25 +710,39 @@ namespace gsr {
|
||||
mgl_context *context = mgl_get_context();
|
||||
Display *display = (Display*)context->connection;
|
||||
|
||||
const std::vector<Monitor> monitors = get_monitors(display);
|
||||
if(monitors.empty()) {
|
||||
fprintf(stderr, "gsr warning: no monitors found, not showing overlay\n");
|
||||
window.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string wm_name = get_window_manager_name(display);
|
||||
const bool is_kwin = wm_name == "KWin";
|
||||
const bool is_wlroots = wm_name.find("wlroots") != std::string::npos;
|
||||
|
||||
// The cursor position is wrong on wayland if an x11 window is not focused. On wayland we instead create a window and get the position where the wayland compositor puts it
|
||||
Window x11_cursor_window = None;
|
||||
const mgl::vec2i cursor_position = get_cursor_position(display, &x11_cursor_window);
|
||||
const mgl::vec2i monitor_position_query_value = (x11_cursor_window || gsr_info.system_info.display_server != DisplayServer::WAYLAND) ? cursor_position : create_window_get_center_position(display);
|
||||
|
||||
const Monitor *focused_monitor = find_monitor_at_position(monitors, monitor_position_query_value);
|
||||
if(is_wlroots) {
|
||||
window_pos = focused_monitor->position;
|
||||
window_size = focused_monitor->size;
|
||||
} else {
|
||||
window_pos = {0, 0};
|
||||
window_size = {32, 32};
|
||||
}
|
||||
|
||||
// Wayland doesn't allow XGrabPointer/XGrabKeyboard when a wayland application is focused.
|
||||
// If the focused window is a wayland application then don't use override redirect and instead create
|
||||
// a fullscreen window for the ui.
|
||||
const bool prevent_game_minimizing = gsr_info.system_info.display_server != DisplayServer::WAYLAND || x11_cursor_window;
|
||||
|
||||
window_size = { 32, 32 };
|
||||
window_pos = { 0, 0 };
|
||||
|
||||
mgl::Window::CreateParams window_create_params;
|
||||
window_create_params.size = window_size;
|
||||
if(prevent_game_minimizing) {
|
||||
if(is_wlroots || prevent_game_minimizing) {
|
||||
window_create_params.min_size = window_size;
|
||||
window_create_params.max_size = window_size;
|
||||
}
|
||||
@@ -743,7 +755,10 @@ namespace gsr {
|
||||
// MGL_WINDOW_TYPE_DIALOG is needed for kde plasma wayland in some cases, otherwise the window will pop up on another activity
|
||||
// or may not be visible at all
|
||||
window_create_params.window_type = (is_kwin && gsr_info.system_info.display_server == DisplayServer::WAYLAND) ? MGL_WINDOW_TYPE_DIALOG : MGL_WINDOW_TYPE_NORMAL;
|
||||
window_create_params.render_api = MGL_RENDER_API_EGL;
|
||||
// Nvidia + Wayland + Egl doesn't work on some systems properly and it instead falls back to software rendering.
|
||||
// Use Glx on Wayland to workaround this issue. This is fine since Egl is only needed for x11 to reliably get the texture of the fullscreen window on Nvidia
|
||||
// when a compositor isn't running.
|
||||
window_create_params.render_api = gsr_info.system_info.display_server == DisplayServer::WAYLAND ? MGL_RENDER_API_GLX : MGL_RENDER_API_EGL;
|
||||
|
||||
if(!window->create("gsr ui", window_create_params))
|
||||
fprintf(stderr, "error: failed to create window\n");
|
||||
@@ -754,36 +769,26 @@ namespace gsr {
|
||||
data = 1;
|
||||
XChangeProperty(display, window->get_system_handle(), XInternAtom(display, "GAMESCOPE_EXTERNAL_OVERLAY", False), XA_CARDINAL, 32, PropModeReplace, &data, 1);
|
||||
|
||||
window_pos = focused_monitor->position;
|
||||
window_size = focused_monitor->size;
|
||||
if(!init_theme(resources_path)) {
|
||||
fprintf(stderr, "Error: failed to load theme\n");
|
||||
::exit(1);
|
||||
}
|
||||
|
||||
mgl_window *win = window->internal_window();
|
||||
if(win->num_monitors == 0) {
|
||||
fprintf(stderr, "gsr warning: no monitors found, not showing overlay\n");
|
||||
window.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
const mgl_monitor *focused_monitor = find_monitor_at_position(*window, monitor_position_query_value);
|
||||
window_pos = {focused_monitor->pos.x, focused_monitor->pos.y};
|
||||
window_size = {focused_monitor->size.x, focused_monitor->size.y};
|
||||
get_theme().set_window_size(window_size);
|
||||
|
||||
if(prevent_game_minimizing) {
|
||||
if(is_wlroots || prevent_game_minimizing) {
|
||||
window->set_size(window_size);
|
||||
window->set_size_limits(window_size, window_size);
|
||||
window->set_position(window_pos);
|
||||
}
|
||||
|
||||
mgl_window *win = window->internal_window();
|
||||
win->cursor_position.x = cursor_position.x - window_pos.x;
|
||||
win->cursor_position.y = cursor_position.y - window_pos.y;
|
||||
|
||||
update_compositor_texture(focused_monitor);
|
||||
|
||||
top_bar_text = mgl::Text("GPU Screen Recorder", get_theme().top_bar_font);
|
||||
logo_sprite = mgl::Sprite(&get_theme().logo_texture);
|
||||
update_compositor_texture(*focused_monitor);
|
||||
|
||||
bg_screenshot_overlay = mgl::Rectangle(mgl::vec2f(get_theme().window_width, get_theme().window_height));
|
||||
top_bar_background = mgl::Rectangle(mgl::vec2f(get_theme().window_width, get_theme().window_height*0.06f).floor());
|
||||
@@ -991,7 +996,8 @@ namespace gsr {
|
||||
// XFlush(display);
|
||||
// }
|
||||
|
||||
window->set_fullscreen(true);
|
||||
if(!is_wlroots)
|
||||
window->set_fullscreen(true);
|
||||
|
||||
visible = true;
|
||||
|
||||
@@ -1954,7 +1960,7 @@ namespace gsr {
|
||||
show_notification("Streaming has started", 3.0, get_color_theme().tint_color, get_color_theme().tint_color, NotificationType::STREAM);
|
||||
}
|
||||
|
||||
bool Overlay::update_compositor_texture(const mgl_monitor *monitor) {
|
||||
bool Overlay::update_compositor_texture(const Monitor &monitor) {
|
||||
window_texture_deinit(&window_texture);
|
||||
window_texture_sprite.set_texture(nullptr);
|
||||
screenshot_texture.clear();
|
||||
@@ -1977,7 +1983,7 @@ namespace gsr {
|
||||
window_texture_texture = mgl::Texture(window_texture.texture_id, MGL_TEXTURE_FORMAT_RGB);
|
||||
window_texture_sprite.set_texture(&window_texture_texture);
|
||||
} else {
|
||||
XImage *img = XGetImage(display, DefaultRootWindow(display), monitor->pos.x, monitor->pos.y, monitor->size.x, monitor->size.y, AllPlanes, ZPixmap);
|
||||
XImage *img = XGetImage(display, DefaultRootWindow(display), monitor.position.x, monitor.position.y, monitor.size.x, monitor.size.y, AllPlanes, ZPixmap);
|
||||
if(!img)
|
||||
fprintf(stderr, "Error: failed to take a screenshot\n");
|
||||
|
||||
@@ -2003,4 +2009,4 @@ namespace gsr {
|
||||
XFlush(display);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/fcntl.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
namespace gsr {
|
||||
static void get_runtime_filepath(char *buffer, size_t buffer_size, const char *filename) {
|
||||
|
||||
@@ -4,11 +4,20 @@
|
||||
#include <X11/Xatom.h>
|
||||
#include <X11/Xutil.h>
|
||||
|
||||
#include <mglpp/system/Utf8.hpp>
|
||||
|
||||
extern "C" {
|
||||
#include <mgl/window/window.h>
|
||||
}
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <poll.h>
|
||||
|
||||
#include <optional>
|
||||
|
||||
#define MAX_PROPERTY_VALUE_LEN 4096
|
||||
|
||||
namespace gsr {
|
||||
@@ -134,7 +143,26 @@ namespace gsr {
|
||||
return None;
|
||||
}
|
||||
|
||||
static char* get_window_title(Display *dpy, Window window) {
|
||||
static std::string utf8_sanitize(const uint8_t *str, int size) {
|
||||
const uint32_t zero_width_space_codepoint = 0x200b; // Some games such as the finals has zero-width space characters
|
||||
std::string result;
|
||||
for(int i = 0; i < size;) {
|
||||
// Some games such as the finals has utf8-bom between each character, wtf?
|
||||
if(i + 3 < size && memcmp(str + i, "\xEF\xBB\xBF", 3) == 0) {
|
||||
i += 3;
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32_t codepoint = 0;
|
||||
size_t codepoint_length = 1;
|
||||
if(mgl::utf8_decode(str + i, size - i, &codepoint, &codepoint_length) && codepoint != zero_width_space_codepoint)
|
||||
result.append((const char*)str + i, codepoint_length);
|
||||
i += codepoint_length;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static std::optional<std::string> get_window_title(Display *dpy, Window window) {
|
||||
const Atom net_wm_name_atom = XInternAtom(dpy, "_NET_WM_NAME", False);
|
||||
const Atom wm_name_atom = XInternAtom(dpy, "WM_NAME", False);
|
||||
const Atom utf8_string_atom = XInternAtom(dpy, "UTF8_STRING", False);
|
||||
@@ -147,7 +175,7 @@ namespace gsr {
|
||||
XGetWindowProperty(dpy, window, net_wm_name_atom, 0, 1024, False, utf8_string_atom, &type, &format, &num_items, &bytes_left, &data);
|
||||
|
||||
if(type == utf8_string_atom && format == 8 && data)
|
||||
return (char*)data;
|
||||
return utf8_sanitize(data, num_items);
|
||||
|
||||
type = None;
|
||||
format = 0;
|
||||
@@ -157,16 +185,18 @@ namespace gsr {
|
||||
XGetWindowProperty(dpy, window, wm_name_atom, 0, 1024, False, 0, &type, &format, &num_items, &bytes_left, &data);
|
||||
|
||||
if((type == XA_STRING || type == utf8_string_atom) && data)
|
||||
return (char*)data;
|
||||
return utf8_sanitize(data, num_items);
|
||||
|
||||
return NULL;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
static const char* strip(const char *str, int *len) {
|
||||
int str_len = strlen(str);
|
||||
static std::string strip(const std::string &str) {
|
||||
int start_index = 0;
|
||||
int str_len = str.size();
|
||||
|
||||
for(int i = 0; i < str_len; ++i) {
|
||||
if(str[i] != ' ') {
|
||||
str += i;
|
||||
start_index += i;
|
||||
str_len -= i;
|
||||
break;
|
||||
}
|
||||
@@ -179,14 +209,7 @@ namespace gsr {
|
||||
}
|
||||
}
|
||||
|
||||
*len = str_len;
|
||||
return str;
|
||||
}
|
||||
|
||||
static std::string strip_strip(const char *str) {
|
||||
int len = 0;
|
||||
str = strip(str, &len);
|
||||
return std::string(str, len);
|
||||
return str.substr(start_index, str_len);
|
||||
}
|
||||
|
||||
std::string get_focused_window_name(Display *dpy, WindowCaptureType window_capture_type) {
|
||||
@@ -196,17 +219,16 @@ namespace gsr {
|
||||
return result;
|
||||
|
||||
// Window title is not always ideal (for example for a browser), but for games its pretty much required
|
||||
char *window_title = get_window_title(dpy, focused_window);
|
||||
const std::optional<std::string> window_title = get_window_title(dpy, focused_window);
|
||||
if(window_title) {
|
||||
result = strip_strip(window_title);
|
||||
XFree(window_title);
|
||||
result = strip(window_title.value());
|
||||
return result;
|
||||
}
|
||||
|
||||
XClassHint class_hint = {nullptr, nullptr};
|
||||
XGetClassHint(dpy, focused_window, &class_hint);
|
||||
if(class_hint.res_class) {
|
||||
result = strip_strip(class_hint.res_class);
|
||||
result = strip(class_hint.res_class);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -442,11 +464,9 @@ namespace gsr {
|
||||
if(!window)
|
||||
return wm_name;
|
||||
|
||||
char *window_title = get_window_title(display, window);
|
||||
if(window_title) {
|
||||
wm_name = strip_strip(window_title);
|
||||
XFree(window_title);
|
||||
}
|
||||
const std::optional<std::string> window_title = get_window_title(display, window);
|
||||
if(window_title)
|
||||
wm_name = strip(window_title.value());
|
||||
|
||||
return wm_name;
|
||||
}
|
||||
@@ -454,7 +474,18 @@ namespace gsr {
|
||||
bool is_compositor_running(Display *dpy, int screen) {
|
||||
char prop_name[20];
|
||||
snprintf(prop_name, sizeof(prop_name), "_NET_WM_CM_S%d", screen);
|
||||
Atom prop_atom = XInternAtom(dpy, prop_name, False);
|
||||
const Atom prop_atom = XInternAtom(dpy, prop_name, False);
|
||||
return XGetSelectionOwner(dpy, prop_atom) != None;
|
||||
}
|
||||
|
||||
static void get_monitors_callback(const mgl_monitor *monitor, void *userdata) {
|
||||
std::vector<Monitor> *monitors = (std::vector<Monitor>*)userdata;
|
||||
monitors->push_back({mgl::vec2i(monitor->pos.x, monitor->pos.y), mgl::vec2i(monitor->size.x, monitor->size.y)});
|
||||
}
|
||||
|
||||
std::vector<Monitor> get_monitors(Display *dpy) {
|
||||
std::vector<Monitor> monitors;
|
||||
mgl_for_each_active_monitor_output(dpy, get_monitors_callback, &monitors);
|
||||
return monitors;
|
||||
}
|
||||
}
|
||||
49
src/main.cpp
49
src/main.cpp
@@ -10,6 +10,7 @@
|
||||
#include <signal.h>
|
||||
#include <thread>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include <X11/keysym.h>
|
||||
#include <mglpp/mglpp.hpp>
|
||||
@@ -37,6 +38,7 @@ static void disable_prime_run() {
|
||||
unsetenv("__NV_PRIME_RENDER_OFFLOAD_PROVIDER");
|
||||
unsetenv("__GLX_VENDOR_LIBRARY_NAME");
|
||||
unsetenv("__VK_LAYER_NV_optimus");
|
||||
unsetenv("DRI_PRIME");
|
||||
}
|
||||
|
||||
static std::unique_ptr<gsr::GlobalHotkeysX11> register_x11_hotkeys(gsr::Overlay *overlay) {
|
||||
@@ -188,6 +190,48 @@ static bool is_gsr_ui_virtual_keyboard_running() {
|
||||
return virtual_keyboard_running;
|
||||
}
|
||||
|
||||
static void install_flatpak_systemd_service() {
|
||||
const bool systemd_service_exists = system(
|
||||
"data_home=$(flatpak-spawn --host -- /bin/sh -c 'echo \"${XDG_DATA_HOME:-$HOME/.local/share}\"') && "
|
||||
"flatpak-spawn --host -- ls \"$data_home/systemd/user/gpu-screen-recorder-ui.service\"") == 0;
|
||||
if(systemd_service_exists)
|
||||
return;
|
||||
|
||||
bool service_install_successful = (system(
|
||||
"data_home=$(flatpak-spawn --host -- /bin/sh -c 'echo \"${XDG_DATA_HOME:-$HOME/.local/share}\"') && "
|
||||
"flatpak-spawn --host -- install -Dm644 /var/lib/flatpak/app/com.dec05eba.gpu_screen_recorder/current/active/files/share/gpu-screen-recorder/gpu-screen-recorder-ui.service \"$data_home/systemd/user/gpu-screen-recorder-ui.service\"") == 0);
|
||||
service_install_successful &= (system("flatpak-spawn --host -- systemctl --user daemon-reload") == 0);
|
||||
if(service_install_successful)
|
||||
fprintf(stderr, "Info: the systemd service file was missing. It has now been installed\n");
|
||||
else
|
||||
fprintf(stderr, "Error: the systemd service file is missing and failed to install it again\n");
|
||||
}
|
||||
|
||||
static void remove_flatpak_systemd_service() {
|
||||
char systemd_service_path[PATH_MAX];
|
||||
const char *xdg_data_home = getenv("XDG_DATA_HOME");
|
||||
const char *home = getenv("HOME");
|
||||
if(xdg_data_home) {
|
||||
snprintf(systemd_service_path, sizeof(systemd_service_path), "%s/systemd/user/gpu-screen-recorder-ui.service", xdg_data_home);
|
||||
} else if(home) {
|
||||
snprintf(systemd_service_path, sizeof(systemd_service_path), "%s/.local/share/systemd/user/gpu-screen-recorder-ui.service", home);
|
||||
} else {
|
||||
fprintf(stderr, "Error: failed to get user home directory\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if(access(systemd_service_path, F_OK) != 0)
|
||||
return;
|
||||
|
||||
remove(systemd_service_path);
|
||||
system("systemctl --user daemon-reload");
|
||||
fprintf(stderr, "Info: conflicting flatpak version of the systemd service for gsr-ui was found at \"%s\", it has now been removed\n", systemd_service_path);
|
||||
}
|
||||
|
||||
static bool is_flatpak() {
|
||||
return getenv("FLATPAK_ID") != nullptr;
|
||||
}
|
||||
|
||||
static void usage() {
|
||||
printf("usage: gsr-ui [action]\n");
|
||||
printf("OPTIONS:\n");
|
||||
@@ -227,6 +271,11 @@ int main(int argc, char **argv) {
|
||||
usage();
|
||||
}
|
||||
|
||||
if(is_flatpak())
|
||||
install_flatpak_systemd_service();
|
||||
else
|
||||
remove_flatpak_systemd_service();
|
||||
|
||||
// TODO: This is a shitty method to detect if multiple instances of gsr-ui is running but this will work properly even in flatpak
|
||||
// that uses pid sandboxing. Replace this with a better method once we no longer rely on linux global hotkeys on some platform.
|
||||
// TODO: This method doesn't work when disabling hotkeys and the method below with pidof gsr-ui doesn't work in flatpak.
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/poll.h>
|
||||
#include <poll.h>
|
||||
|
||||
/* LINUX */
|
||||
#include <linux/input.h>
|
||||
@@ -184,6 +184,7 @@ static void keyboard_event_process_input_event_data(keyboard_event *self, event_
|
||||
if(event.type == EV_KEY) {
|
||||
keyboard_event_process_key_state_change(self, event, extra_data, fd);
|
||||
|
||||
/* We do this conversion from keycode to keysym back to keycode to support different keyboard layouts in the X server (which Wayland also uses to support Xwayland) */
|
||||
uint32_t keycode = event.code;
|
||||
const uint32_t keysym = keycode_to_keysym(self, event.code);
|
||||
if(keysym)
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#include <stdint.h>
|
||||
|
||||
/* POSIX */
|
||||
#include <sys/poll.h>
|
||||
#include <poll.h>
|
||||
|
||||
/* LINUX */
|
||||
#include <linux/input-event-codes.h>
|
||||
|
||||
Reference in New Issue
Block a user