aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2024-12-30 22:57:48 +0100
committerdec05eba <dec05eba@protonmail.com>2024-12-30 22:57:48 +0100
commitc2dca2e8f812302d9465186bf001442a29919088 (patch)
treeda27516ac81e69d1f038efab7b75e3e32590efad
parenta3d4455b3d238ed9308491c9046867d2c0d67b5f (diff)
Start on rpc, open existing instances ui when trying to launch gsr-ui a second time
-rw-r--r--README.md4
-rw-r--r--TODO4
-rw-r--r--include/Rpc.hpp34
-rw-r--r--meson.build1
-rw-r--r--src/GlobalHotkeysLinux.cpp11
-rw-r--r--src/Rpc.cpp133
-rw-r--r--src/main.cpp33
7 files changed, 201 insertions, 19 deletions
diff --git a/README.md b/README.md
index d1ee091..125a01d 100644
--- a/README.md
+++ b/README.md
@@ -9,7 +9,8 @@ You can report an issue by emailing the issue to dec05eba@protonmail.com.
# Usage
Run `gsr-ui` and press `Alt+Z` to show/hide the UI. You can start the overlay UI at system startup by running `systemctl enable --now --user gpu-screen-recorder-ui`.
There is also an option in the settings to enable/disable starting the program on system startup. This option only works on systems that use systemd.
-You have to manually add `gsr-ui` to system startup on systems that uses another init system.
+You have to manually add `gsr-ui` to system startup on systems that uses another init system.\
+Note that at the moment different keyboard layouts are not supported. The physical layout of keys are used for global hotkeys. If your Z and Y keys are swapped for example then you need to press Alt+Y instead of Alt+Z to open/hide the UI.
# Installation
If you are using an Arch Linux based distro then you can find gpu screen recorder ui on aur under the name gpu-screen-recorder-ui (`yay -S gpu-screen-recorder-ui`).\
@@ -50,3 +51,4 @@ If you want to donate you can donate via bitcoin or monero.
# Known issues
* Some games receive mouse input while the UI is open
* 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.
+* Different keyboard layouts are not supported at the moment. The physical layout of keys are used for global hotkeys. If your Z and Y keys are swapped for example then you need to press Alt+Y instead of Alt+Z to open/hide the UI. \ No newline at end of file
diff --git a/TODO b/TODO
index c42acba..72edbfb 100644
--- a/TODO
+++ b/TODO
@@ -119,4 +119,6 @@ Dont put widget position to int position when scrolling. This makes the UI jitte
Show a popup asking if the user wants to add the program to system startup when launching the program, with a dismiss option and "Do not show again".
-Show warning if another instance of gpu screen recorder is already running when starting recording? \ No newline at end of file
+Show warning if another instance of gpu screen recorder is already running when starting recording?
+
+Change gsr-global-hotkeys to outputs keys pressed instead of the command. This can be done pretty safely by only output keys when modifiers (such as alt) is pressed. \ No newline at end of file
diff --git a/include/Rpc.hpp b/include/Rpc.hpp
new file mode 100644
index 0000000..d6db218
--- /dev/null
+++ b/include/Rpc.hpp
@@ -0,0 +1,34 @@
+#pragma once
+
+#include <stddef.h>
+#include <functional>
+#include <unordered_map>
+#include <string>
+
+typedef struct _IO_FILE FILE;
+
+namespace gsr {
+ using RpcCallback = std::function<void(const std::string &name)>;
+
+ class Rpc {
+ public:
+ Rpc() = default;
+ Rpc(const Rpc&) = delete;
+ Rpc& operator=(const Rpc&) = delete;
+ ~Rpc();
+
+ bool create(const char *name);
+ bool open(const char *name);
+ bool write(const char *str, size_t size);
+ void poll();
+
+ bool add_handler(const std::string &name, RpcCallback callback);
+ private:
+ bool open_filepath(const char *filepath);
+ private:
+ int fd = 0;
+ FILE *file = nullptr;
+ std::string fifo_filepath;
+ std::unordered_map<std::string, RpcCallback> handlers_by_name;
+ };
+} \ No newline at end of file
diff --git a/meson.build b/meson.build
index b83070c..3bb65d9 100644
--- a/meson.build
+++ b/meson.build
@@ -38,6 +38,7 @@ src = [
'src/Overlay.cpp',
'src/GlobalHotkeysX11.cpp',
'src/GlobalHotkeysLinux.cpp',
+ 'src/Rpc.cpp',
'src/main.cpp',
]
diff --git a/src/GlobalHotkeysLinux.cpp b/src/GlobalHotkeysLinux.cpp
index f3ec2b3..d16cc06 100644
--- a/src/GlobalHotkeysLinux.cpp
+++ b/src/GlobalHotkeysLinux.cpp
@@ -89,7 +89,7 @@ namespace gsr {
}
bool GlobalHotkeysLinux::bind_action(const std::string &id, GlobalHotkeyCallback callback) {
- return bound_actions_by_id.insert(std::make_pair(id, callback)).second;
+ return bound_actions_by_id.insert(std::make_pair(id, std::move(callback))).second;
}
void GlobalHotkeysLinux::poll_events() {
@@ -103,20 +103,23 @@ namespace gsr {
return;
}
+ std::string action;
char buffer[256];
while(true) {
char *line = fgets(buffer, sizeof(buffer), read_file);
if(!line)
break;
- const int line_len = strlen(line);
+ int line_len = strlen(line);
if(line_len == 0)
continue;
- if(line[line_len - 1] == '\n')
+ if(line[line_len - 1] == '\n') {
line[line_len - 1] = '\0';
+ --line_len;
+ }
- const std::string action = line;
+ action = line;
auto it = bound_actions_by_id.find(action);
if(it != bound_actions_by_id.end())
it->second(action);
diff --git a/src/Rpc.cpp b/src/Rpc.cpp
new file mode 100644
index 0000000..206b1cf
--- /dev/null
+++ b/src/Rpc.cpp
@@ -0,0 +1,133 @@
+#include "../include/Rpc.hpp"
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <limits.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/fcntl.h>
+
+namespace gsr {
+ static void get_runtime_filepath(char *buffer, size_t buffer_size, const char *filename) {
+ char dir[PATH_MAX];
+
+ const char *runtime_dir = getenv("XDG_RUNTIME_DIR");
+ if(runtime_dir)
+ snprintf(dir, sizeof(dir), "%s", runtime_dir);
+ else
+ snprintf(dir, sizeof(dir), "/run/user/%d", geteuid());
+
+ if(access(dir, F_OK) != 0)
+ snprintf(dir, sizeof(dir), "/tmp");
+
+ snprintf(buffer, buffer_size, "%s/%s", dir, filename);
+ }
+
+ Rpc::~Rpc() {
+ if(fd > 0)
+ close(fd);
+
+ if(file)
+ fclose(file);
+
+ if(!fifo_filepath.empty())
+ remove(fifo_filepath.c_str());
+ }
+
+ bool Rpc::create(const char *name) {
+ if(file) {
+ fprintf(stderr, "Error: Rpc::create: already created/opened\n");
+ return false;
+ }
+
+ char fifo_filepath_tmp[PATH_MAX];
+ get_runtime_filepath(fifo_filepath_tmp, sizeof(fifo_filepath_tmp), name);
+ fifo_filepath = fifo_filepath_tmp;
+ remove(fifo_filepath.c_str());
+
+ if(mkfifo(fifo_filepath.c_str(), 0600) != 0) {
+ fprintf(stderr, "Error: mkfifo failed, error: %s, %s\n", strerror(errno), fifo_filepath.c_str());
+ return false;
+ }
+
+ if(!open_filepath(fifo_filepath.c_str())) {
+ remove(fifo_filepath.c_str());
+ fifo_filepath.clear();
+ return false;
+ }
+
+ return true;
+ }
+
+ bool Rpc::open(const char *name) {
+ if(file) {
+ fprintf(stderr, "Error: Rpc::open: already created/opened\n");
+ return false;
+ }
+
+ char fifo_filepath_tmp[PATH_MAX];
+ get_runtime_filepath(fifo_filepath_tmp, sizeof(fifo_filepath_tmp), name);
+ return open_filepath(fifo_filepath_tmp);
+ }
+
+ bool Rpc::open_filepath(const char *filepath) {
+ fd = ::open(filepath, O_RDWR | O_NONBLOCK);
+ if(fd <= 0)
+ return false;
+
+ file = fdopen(fd, "r+");
+ if(!file) {
+ close(fd);
+ fd = 0;
+ return false;
+ }
+ fd = 0;
+ return true;
+ }
+
+ bool Rpc::write(const char *str, size_t size) {
+ if(!file) {
+ fprintf(stderr, "Error: Rpc::write: fifo not created/opened yet\n");
+ return false;
+ }
+
+ ssize_t offset = 0;
+ while(offset < (ssize_t)size) {
+ const ssize_t bytes_written = fwrite(str + offset, 1, size - offset, file);
+ fflush(file);
+ if(bytes_written > 0)
+ offset += bytes_written;
+ }
+ return true;
+ }
+
+ void Rpc::poll() {
+ if(!file) {
+ //fprintf(stderr, "Error: Rpc::poll: fifo not created/opened yet\n");
+ return;
+ }
+
+ std::string name;
+ char line[1024];
+ while(fgets(line, sizeof(line), file)) {
+ int line_len = strlen(line);
+ if(line_len == 0)
+ continue;
+
+ if(line[line_len - 1] == '\n') {
+ line[line_len - 1] = '\0';
+ --line_len;
+ }
+
+ name = line;
+ auto it = handlers_by_name.find(name);
+ if(it != handlers_by_name.end())
+ it->second(name);
+ }
+ }
+
+ bool Rpc::add_handler(const std::string &name, RpcCallback callback) {
+ return handlers_by_name.insert(std::make_pair(name, std::move(callback))).second;
+ }
+} \ No newline at end of file
diff --git a/src/main.cpp b/src/main.cpp
index 6b6d60a..ffee855 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -5,6 +5,7 @@
#include "../include/GlobalHotkeysLinux.hpp"
#include "../include/gui/Utils.hpp"
#include "../include/Process.hpp"
+#include "../include/Rpc.hpp"
#include <unistd.h>
#include <signal.h>
@@ -193,8 +194,14 @@ int main(int argc, char **argv) {
// 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.
if(is_gsr_ui_virtual_keyboard_running()) {
- const char *args[] = { "gsr-notify", "--text", "Another instance of GPU Screen Recorder UI is already running.\nPress Alt+Z to open the UI.", "--timeout", "5.0", "--icon-color", "ff0000", "--bg-color", "ff0000", nullptr };
- gsr::exec_program_daemonized(args);
+ gsr::Rpc rpc;
+ if(rpc.open("gsr-ui") && rpc.write("show_ui", 7)) {
+ fprintf(stderr, "Error: another instance of gsr-ui is already running, opening that one instead\n");
+ } else {
+ fprintf(stderr, "Error: failed to send command to running gsr-ui instance, user will have to open the UI manually with Alt+Z\n");
+ const char *args[] = { "gsr-notify", "--text", "Another instance of GPU Screen Recorder UI is already running.\nPress Alt+Z to open the UI.", "--timeout", "5.0", "--icon-color", "ff0000", "--bg-color", "ff0000", nullptr };
+ gsr::exec_program_daemonized(args);
+ }
return 1;
}
// const pid_t gsr_ui_pid = gsr::pidof("gsr-ui");
@@ -264,18 +271,16 @@ int main(int argc, char **argv) {
fprintf(stderr, "Info: gsr ui is now ready, waiting for inputs. Press alt+z to show/hide the overlay\n");
+ auto rpc = std::make_unique<gsr::Rpc>();
+ if(!rpc->create("gsr-ui"))
+ fprintf(stderr, "Error: Failed to create rpc, commands won't be received\n");
+
auto overlay = std::make_unique<gsr::Overlay>(resources_path, std::move(gsr_info), std::move(capture_options), egl_funcs);
- // std::unique_ptr<gsr::GlobalHotkeys> global_hotkeys = nullptr;
- // if(display_server == gsr::DisplayServer::X11) {
- // global_hotkeys = register_x11_hotkeys(overlay.get());
- // if(!global_hotkeys) {
- // fprintf(stderr, "Info: failed to register some x11 hotkeys because they are registered by another program. Will use linux hotkeys instead\n");
- // global_hotkeys = register_linux_hotkeys(overlay.get());
- // }
- // } else {
- // global_hotkeys = register_linux_hotkeys(overlay.get());
- // }
+ rpc->add_handler("show_ui", [&](const std::string&) {
+ overlay->show();
+ });
+
std::unique_ptr<gsr::GlobalHotkeys> global_hotkeys = register_linux_hotkeys(overlay.get());
if(launch_action == LaunchAction::LAUNCH_SHOW)
@@ -290,6 +295,7 @@ int main(int argc, char **argv) {
const double frame_delta_seconds = frame_delta_clock.restart();
gsr::set_frame_delta_seconds(frame_delta_seconds);
+ rpc->poll();
global_hotkeys->poll_events();
overlay->handle_events(global_hotkeys.get());
if(!overlay->draw()) {
@@ -299,11 +305,12 @@ int main(int argc, char **argv) {
}
fprintf(stderr, "Info: shutting down!\n");
+ rpc.reset();
+ global_hotkeys.reset();
overlay.reset();
gsr::deinit_theme();
gsr::deinit_color_theme();
mgl_deinit();
- global_hotkeys.reset();
if(exit_reason == "back-to-old-ui") {
const char *args[] = { "gpu-screen-recorder-gtk", "use-old-ui", nullptr };