Compare commits

...

18 Commits
1.7.4 ... 1.7.8

Author SHA1 Message Date
dec05eba
ca4061f171 1.7.8 2025-10-03 13:09:16 +02:00
dec05eba
0b4af1e6bb Fix rpc file getting deleted when launching gsr-ui twice. Use unix domain socket instead 2025-10-03 13:08:57 +02:00
dec05eba
9e03cd0354 Test fix for alpha egl 2025-10-03 01:56:50 +02:00
dec05eba
3d4badf5cd Fix build for old meson version 2025-09-30 11:14:10 +02:00
dec05eba
071ecf46de Update TODO 2025-09-30 11:07:09 +02:00
dec05eba
5ee2b95384 Workaround amd driver bug: kill notifications with SIGINT instead of SIGKILL 2025-09-24 18:38:22 +02:00
dec05eba
d610a980f8 Update flatpak version reference 2025-09-23 19:43:43 +02:00
dec05eba
70780ae14e 1.7.6 2025-09-21 03:32:19 +02:00
dec05eba
5f7cb94f4e Only do override-redirect on wayland if the focused x11 application is fullscreen (fixes input focus issue on cosmic when clicking on a window behind the overlay 2025-09-19 14:14:46 +02:00
dec05eba
748c51e2b6 Reorder README.md 2025-09-17 18:02:30 +02:00
dec05eba
3ba9ce771b Update flatpak version reference 2025-09-10 21:36:19 +02:00
dec05eba
c18b062180 README 2025-09-09 16:45:59 +02:00
dec05eba
705da21363 README 2025-09-09 16:43:25 +02:00
dec05eba
609a3e54fd 1.7.5 2025-09-06 19:16:09 +02:00
dec05eba
4e62d12e8c Allow 'sync to content' framerate mode option on wayland (only desktop portal) 2025-09-06 01:58:28 +02:00
dec05eba
b4e003c8f7 Only use mallopt M_MMAP_THRESHOLD if glibc 2025-09-03 18:21:58 +02:00
dec05eba
9efe9d3c91 Add content framerate mode (for x11) 2025-09-01 18:11:16 +02:00
dec05eba
ef4a0fe7cb Update flatpak version reference 2025-08-25 22:30:10 +02:00
13 changed files with 267 additions and 108 deletions

View File

@@ -4,18 +4,18 @@
A fullscreen overlay UI for [GPU Screen Recorder](https://git.dec05eba.com/gpu-screen-recorder/about/) in the style of ShadowPlay.\
The application is currently primarly designed for X11 but it can run on Wayland as well through XWayland, with some caveats because of Wayland limitations.
# Usage
You can start the overlay UI and make it start automatically on system startup by running `systemctl enable --now --user gpu-screen-recorder-ui`.
Alternatively you can run `gsr-ui` and go into settings and enable start on system startup setting.\
Press `Left Alt+Z` to show/hide the UI. Go into settings to view all of the different hotkeys configured.\
If you use a non-systemd distro and want to start the UI on system startup then you have to manually add `gsr-ui` to your system startup script.\
A program called `gsr-ui-cli` is also installed when installing this software. This can be used to remotely control the UI. Run `gsr-ui-cli --help` to list the available commands.
# 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`).\
If you are running another distro then you can run `sudo ./install.sh`, but you need to manually install the dependencies, as described below.\
You can also install gpu screen recorder from [flathub](https://flathub.org/apps/details/com.dec05eba.gpu_screen_recorder) which includes this UI.
# Usage
Press `Left Alt+Z` to show/hide the UI. Go into settings (the icon on the right) to view all of the different hotkeys configured.\
You can start the overlay UI and make it start automatically on system startup by running `systemctl enable --now --user gpu-screen-recorder-ui`.
Alternatively you can run `gsr-ui` and go into settings and enable start on system startup setting.\
If you use a non-systemd distro and want to start the UI on system startup then you have to manually add `gsr-ui launch-daemon` to your system startup script.\
A program called `gsr-ui-cli` is also installed when installing this software. This can be used to remotely control the UI. Run `gsr-ui-cli --help` to list the available commands.
# Dependencies
GPU Screen Recorder UI uses meson build system so you need to install `meson` to build GPU Screen Recorder UI.

9
TODO
View File

@@ -206,3 +206,12 @@ Support localization.
Add option to not capture cursor in screenshot when doing region/window capture.
Window selection doesn't work when a window is fullscreen on x11.
Make it possible to change replay duration of the "save 1 min" and "save 10 min" by adding them to the replay settings as options.
If replay duration is set below the "save 1 min" or "save 10 min" then gray them out and when hovering over those buttons
show a tooltip that says that those buttons cant be used because the replay duration in replay settings is set to a lower value than that (and display the replay duration there).
The UI is unusable on a vertical monitor.
Steam overlay interfers with controller input in gsr ui. Maybe move controller handling the gsr-global-hotkeys to do a grab on the controller, to only give the key input to gsr ui.

View File

@@ -4,31 +4,47 @@
#include <functional>
#include <unordered_map>
#include <string>
#include <poll.h>
typedef struct _IO_FILE FILE;
#define GSR_RPC_MAX_CONNECTIONS 8
#define GSR_RPC_MAX_POLLS (1 + GSR_RPC_MAX_CONNECTIONS) /* +1 to include the socket_fd itself for accept */
#define GSR_RPC_MAX_MESSAGE_SIZE 128
namespace gsr {
using RpcCallback = std::function<void(const std::string &name)>;
enum class RpcOpenResult {
OK,
CONNECTION_REFUSED,
ERROR
};
class Rpc {
public:
Rpc() = default;
struct PollData {
char buffer[GSR_RPC_MAX_MESSAGE_SIZE];
int buffer_size = 0;
};
Rpc();
Rpc(const Rpc&) = delete;
Rpc& operator=(const Rpc&) = delete;
~Rpc();
bool create(const char *name);
bool open(const char *name);
RpcOpenResult 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);
void handle_client_data(int client_fd, PollData &poll_data);
private:
int fd = 0;
FILE *file = nullptr;
std::string fifo_filepath;
int socket_fd = 0;
std::string socket_filepath;
struct pollfd polls[GSR_RPC_MAX_POLLS];
PollData polls_data[GSR_RPC_MAX_POLLS];
int num_polls = 0;
std::unordered_map<std::string, RpcCallback> handlers_by_name;
};
}

View File

@@ -19,7 +19,9 @@ namespace gsr {
void draw(mgl::Window &window, mgl::vec2f offset) override;
void add_item(const std::string &text, const std::string &id);
// The item can only be selected if it's enabled
void set_selected_item(const std::string &id, bool trigger_event = true, bool trigger_event_even_if_selection_not_changed = true);
void set_item_enabled(const std::string &id, bool enabled);
const std::string& get_selected_id() const;
mgl::vec2f get_size() override;
@@ -36,6 +38,7 @@ namespace gsr {
mgl::Text text;
std::string id;
mgl::vec2f position;
bool enabled = true;
};
mgl::vec2f max_size;

View File

@@ -1,4 +1,4 @@
project('gsr-ui', ['c', 'cpp'], version : '1.7.4', default_options : ['warning_level=2', 'cpp_std=c++17'], subproject_dir : 'depends')
project('gsr-ui', ['c', 'cpp'], version : '1.7.8', default_options : ['warning_level=2', 'cpp_std=c++17'], subproject_dir : 'depends')
add_project_arguments('-D_FILE_OFFSET_BITS=64', language : ['c', 'cpp'])
@@ -65,7 +65,7 @@ datadir = get_option('datadir')
gsr_ui_resources_path = join_paths(prefix, datadir, 'gsr-ui')
add_project_arguments('-DGSR_UI_VERSION="' + meson.project_version() + '"', language: ['c', 'cpp'])
add_project_arguments('-DGSR_FLATPAK_VERSION="5.7.6"', language: ['c', 'cpp'])
add_project_arguments('-DGSR_FLATPAK_VERSION="5.8.2"', language: ['c', 'cpp'])
executable(
meson.project_name(),

View File

@@ -1,7 +1,7 @@
[package]
name = "gsr-ui"
type = "executable"
version = "1.7.4"
version = "1.7.8"
platforms = ["posix"]
[lang.cpp]

View File

@@ -512,7 +512,7 @@ namespace gsr {
hide();
if(notification_process > 0) {
kill(notification_process, SIGKILL);
kill(notification_process, SIGINT);
int status;
if(waitpid(notification_process, &status, 0) == -1) {
perror("waitpid failed");
@@ -959,6 +959,7 @@ namespace gsr {
const bool is_kwin = wm_name == "KWin";
const bool is_wlroots = wm_name.find("wlroots") != std::string::npos;
const bool is_hyprland = wm_name.find("Hyprland") != std::string::npos;
//const bool is_smithay = wm_name.find("Smithay") != std::string::npos;
const bool hyprland_waybar_is_dock = is_hyprland && is_hyprland_waybar_running_as_dock();
std::optional<CursorInfo> cursor_info;
@@ -986,8 +987,7 @@ namespace gsr {
// 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.
// TODO: (x11_cursor_window && is_window_fullscreen_on_monitor(display, x11_cursor_window, *focused_monitor))
const bool prevent_game_minimizing = gsr_info.system_info.display_server != DisplayServer::WAYLAND || x11_cursor_window || is_wlroots || is_hyprland;
const bool prevent_game_minimizing = gsr_info.system_info.display_server != DisplayServer::WAYLAND || (x11_cursor_window && is_window_fullscreen_on_monitor(display, x11_cursor_window, *focused_monitor)) || is_wlroots || is_hyprland;
if(prevent_game_minimizing) {
window_pos = focused_monitor->position;
@@ -1700,7 +1700,7 @@ namespace gsr {
notification_args[arg_index++] = nullptr;
if(notification_process > 0) {
kill(notification_process, SIGKILL);
kill(notification_process, SIGINT);
int status = 0;
waitpid(notification_process, &status, 0);
}
@@ -1828,8 +1828,6 @@ namespace gsr {
result += std::to_string(seconds) + " second" + (seconds == 1 ? "" : "s");
}
fprintf(stderr, "to duration string: %f, %d, %d, %d\n", duration_sec, seconds, minutes, hours);
return result;
}
@@ -2560,6 +2558,14 @@ namespace gsr {
*container = change_container_if_codec_not_supported(*video_codec, *container);
}
static std::string get_framerate_mode_validate(const RecordOptions &record_options, const GsrInfo &gsr_info) {
(void)gsr_info;
std::string framerate_mode = record_options.framerate_mode;
if(framerate_mode == "auto")
framerate_mode = "vfr";
return framerate_mode;
}
bool Overlay::on_press_start_replay(bool disable_notification, bool finished_selection) {
if(region_selector.is_started() || window_selector.is_started())
return false;
@@ -2632,7 +2638,7 @@ namespace gsr {
const std::string video_bitrate = std::to_string(config.replay_config.record_options.video_bitrate);
const std::string output_directory = config.replay_config.save_directory;
const std::vector<std::string> audio_tracks = create_audio_tracks_cli_args(config.replay_config.record_options.audio_tracks_list, gsr_info);
const std::string framerate_mode = config.replay_config.record_options.framerate_mode == "auto" ? "vfr" : config.replay_config.record_options.framerate_mode;
const std::string framerate_mode = get_framerate_mode_validate(config.replay_config.record_options, gsr_info);
const std::string replay_time = std::to_string(config.replay_config.replay_time);
const char *container = config.replay_config.container.c_str();
const char *video_codec = config.replay_config.record_options.video_codec.c_str();
@@ -2829,7 +2835,7 @@ namespace gsr {
const std::string video_bitrate = std::to_string(config.record_config.record_options.video_bitrate);
const std::string output_file = config.record_config.save_directory + "/Video_" + get_date_str() + "." + container_to_file_extension(config.record_config.container.c_str());
const std::vector<std::string> audio_tracks = create_audio_tracks_cli_args(config.record_config.record_options.audio_tracks_list, gsr_info);
const std::string framerate_mode = config.record_config.record_options.framerate_mode == "auto" ? "vfr" : config.record_config.record_options.framerate_mode;
const std::string framerate_mode = get_framerate_mode_validate(config.record_config.record_options, gsr_info);
const char *container = config.record_config.container.c_str();
const char *video_codec = config.record_config.record_options.video_codec.c_str();
const char *encoder = "gpu";
@@ -3005,7 +3011,7 @@ namespace gsr {
// But we check it anyways as streaming on some sites can fail if there is more than one audio track
if(audio_tracks.size() > 1)
audio_tracks.resize(1);
const std::string framerate_mode = config.streaming_config.record_options.framerate_mode == "auto" ? "vfr" : config.streaming_config.record_options.framerate_mode;
const std::string framerate_mode = get_framerate_mode_validate(config.streaming_config.record_options, gsr_info);
const char *container = "flv";
if(config.streaming_config.streaming_service == "custom")
container = config.streaming_config.custom.container.c_str();

View File

@@ -5,11 +5,12 @@
#include <limits.h>
#include <string.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <poll.h>
#include <sys/socket.h>
#include <sys/un.h>
namespace gsr {
static void get_runtime_filepath(char *buffer, size_t buffer_size, const char *filename) {
static void get_socket_filepath(char *buffer, size_t buffer_size, const char *filename) {
char dir[PATH_MAX];
const char *runtime_dir = getenv("XDG_RUNTIME_DIR");
@@ -24,79 +25,117 @@ namespace gsr {
snprintf(buffer, buffer_size, "%s/%s", dir, filename);
}
static int create_socket(const char *name, struct sockaddr_un *addr, std::string &socket_filepath) {
char socket_filepath_tmp[PATH_MAX];
get_socket_filepath(socket_filepath_tmp, sizeof(socket_filepath_tmp), name);
socket_filepath = socket_filepath_tmp;
memset(addr, 0, sizeof(*addr));
if(strlen(name) > sizeof(addr->sun_path))
return false;
addr->sun_family = AF_UNIX;
snprintf(addr->sun_path, sizeof(addr->sun_path), "%s", socket_filepath.c_str());
return socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0);
}
Rpc::Rpc() {
num_polls = 0;
}
Rpc::~Rpc() {
if(fd > 0)
close(fd);
if(socket_fd > 0)
close(socket_fd);
if(file)
fclose(file);
if(!fifo_filepath.empty())
unlink(fifo_filepath.c_str());
if(!socket_filepath.empty())
unlink(socket_filepath.c_str());
}
bool Rpc::create(const char *name) {
if(file) {
if(socket_fd > 0) {
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;
unlink(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());
fifo_filepath.clear();
struct sockaddr_un addr;
socket_fd = create_socket(name, &addr, socket_filepath);
if(socket_fd <= 0) {
fprintf(stderr, "Error: Rpc::create: failed to create socket, error: %s\n", strerror(errno));
return false;
}
if(!open_filepath(fifo_filepath.c_str())) {
unlink(fifo_filepath.c_str());
fifo_filepath.clear();
unlink(socket_filepath.c_str());
if(bind(socket_fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
const int err = errno;
close(socket_fd);
socket_fd = 0;
fprintf(stderr, "Error: Rpc::create: failed to bind, error: %s\n", strerror(err));
return false;
}
if(listen(socket_fd, GSR_RPC_MAX_CONNECTIONS) == -1) {
const int err = errno;
close(socket_fd);
socket_fd = 0;
fprintf(stderr, "Error: Rpc::create: failed to listen, error: %s\n", strerror(err));
return false;
}
polls[0].fd = socket_fd;
polls[0].events = POLLIN;
polls[0].revents = 0;
++num_polls;
return true;
}
bool Rpc::open(const char *name) {
if(file) {
RpcOpenResult Rpc::open(const char *name) {
if(socket_fd > 0) {
fprintf(stderr, "Error: Rpc::open: already created/opened\n");
return false;
return RpcOpenResult::ERROR;
}
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;
struct sockaddr_un addr;
socket_fd = create_socket(name, &addr, socket_filepath);
socket_filepath.clear(); /* We dont want to delete the socket on exit as the client */
if(socket_fd <= 0) {
fprintf(stderr, "Error: Rpc::open: failed to create socket, error: %s\n", strerror(errno));
return RpcOpenResult::ERROR;
}
fd = 0;
return true;
while(true) {
if(connect(socket_fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
const int err = errno;
if(err == EWOULDBLOCK) {
usleep(10 * 1000);
} else {
close(socket_fd);
socket_fd = 0;
if(err != ENOENT && err != ECONNREFUSED)
fprintf(stderr, "Error: Rpc::create: failed to connect, error: %s\n", strerror(err));
return RpcOpenResult::ERROR;
}
} else {
break;
}
}
return RpcOpenResult::OK;
}
bool Rpc::write(const char *str, size_t size) {
if(!file) {
fprintf(stderr, "Error: Rpc::write: fifo not created/opened yet\n");
if(socket_fd <= 0) {
fprintf(stderr, "Error: Rpc::write: unix domain socket 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);
const ssize_t bytes_written = ::write(socket_fd, str + offset, size - offset);
if(bytes_written > 0)
offset += bytes_written;
}
@@ -104,30 +143,73 @@ namespace gsr {
}
void Rpc::poll() {
if(!file) {
//fprintf(stderr, "Error: Rpc::poll: fifo not created/opened yet\n");
if(socket_fd <= 0) {
//fprintf(stderr, "Error: Rpc::poll: unix domain socket 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;
while(::poll(polls, num_polls, 0) > 0) {
for(int i = 0; i < num_polls; ++i) {
if(polls[i].fd == socket_fd) {
if(polls[i].revents & (POLLERR|POLLHUP)) {
close(socket_fd);
socket_fd = 0;
return;
}
if(line[line_len - 1] == '\n') {
line[line_len - 1] = '\0';
--line_len;
const int client_fd = accept(socket_fd, NULL, NULL);
if(num_polls >= GSR_RPC_MAX_POLLS) {
if(errno != EWOULDBLOCK)
fprintf(stderr, "Error: Rpc::poll: unable to accept more clients, error: %s\n", strerror(errno));
} else {
polls[num_polls].fd = client_fd;
polls[num_polls].events = POLLIN;
polls[num_polls].revents = 0;
++num_polls;
}
continue;
}
if(polls[i].revents & POLLIN)
handle_client_data(polls[i].fd, polls_data[i]);
if(polls[i].revents & (POLLERR|POLLHUP)) {
close(polls[i].fd);
polls[i] = polls[num_polls - 1];
memcpy(polls_data[i].buffer, polls_data[num_polls - 1].buffer, polls_data[num_polls - 1].buffer_size);
polls_data[i].buffer_size = polls_data[num_polls - 1].buffer_size;
--num_polls;
--i;
}
}
name = line;
auto it = handlers_by_name.find(name);
if(it != handlers_by_name.end())
it->second(name);
}
}
void Rpc::handle_client_data(int client_fd, PollData &poll_data) {
char *write_buffer = poll_data.buffer + poll_data.buffer_size;
const ssize_t num_bytes_read = read(client_fd, write_buffer, sizeof(poll_data.buffer) - poll_data.buffer_size);
if(num_bytes_read <= 0)
return;
poll_data.buffer_size += num_bytes_read;
const char *newline_p = (const char*)memchr(write_buffer, '\n', num_bytes_read);
if(!newline_p)
return;
const size_t command_size = newline_p - poll_data.buffer;
std::string name;
name.assign(poll_data.buffer, command_size);
memmove(poll_data.buffer, newline_p + 1, poll_data.buffer_size - (command_size + 1));
poll_data.buffer_size -= (command_size + 1);
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;
}

View File

@@ -36,7 +36,7 @@ namespace gsr {
for(size_t i = 0; i < items.size(); ++i) {
Item &item = items[i];
item_size.y = padding_top + item.text.get_bounds().size.y + padding_bottom;
if(mgl::FloatRect(item.position, item_size).contains(mouse_pos)) {
if(mgl::FloatRect(item.position, item_size).contains(mouse_pos) && item.enabled) {
const size_t prev_selected_item = selected_item;
selected_item = i;
show_dropdown = false;
@@ -93,7 +93,7 @@ namespace gsr {
void ComboBox::set_selected_item(const std::string &id, bool trigger_event, bool trigger_event_even_if_selection_not_changed) {
for(size_t i = 0; i < items.size(); ++i) {
auto &item = items[i];
if(item.id == id) {
if(item.id == id && item.enabled) {
const size_t prev_selected_item = selected_item;
selected_item = i;
dirty = true;
@@ -106,6 +106,22 @@ namespace gsr {
}
}
void ComboBox::set_item_enabled(const std::string &id, bool enabled) {
for(size_t i = 0; i < items.size(); ++i) {
auto &item = items[i];
if(item.id == id) {
item.enabled = enabled;
item.text.set_color(item.enabled ? mgl::Color(255, 255, 255, 255) : mgl::Color(255, 255, 255, 80));
if(selected_item == i) {
selected_item = 0;
show_dropdown = false;
dirty = true;
}
return;
}
}
}
const std::string& ComboBox::get_selected_id() const {
if(items.empty()) {
static std::string dummy;
@@ -150,7 +166,7 @@ namespace gsr {
Item &item = items[i];
item_size.y = padding_top + item.text.get_bounds().size.y + padding_bottom;
if(!cursor_inside) {
if(!cursor_inside && item.enabled) {
cursor_inside = mgl::FloatRect(items_draw_pos, item_size).contains(mouse_pos);
if(cursor_inside) {
mgl::Rectangle item_background(items_draw_pos.floor(), item_size.floor());

View File

@@ -568,6 +568,10 @@ namespace gsr {
framerate_mode_box->add_item("Auto (Recommended)", "auto");
framerate_mode_box->add_item("Constant", "cfr");
framerate_mode_box->add_item("Variable", "vfr");
if(gsr_info->system_info.display_server == DisplayServer::X11)
framerate_mode_box->add_item("Sync to content", "content");
else
framerate_mode_box->add_item("Sync to content (Only X11 or desktop portal capture)", "content");
framerate_mode_box_ptr = framerate_mode_box.get();
return framerate_mode_box;
}

View File

@@ -195,7 +195,9 @@ enum class LaunchAction {
int main(int argc, char **argv) {
setlocale(LC_ALL, "C"); // Sigh... stupid C
#ifdef __GLIBC__
mallopt(M_MMAP_THRESHOLD, 65536);
#endif
if(geteuid() == 0) {
fprintf(stderr, "Error: don't run gsr-ui as the root user\n");
@@ -224,16 +226,13 @@ int main(int argc, char **argv) {
set_display_server_environment_variables();
auto rpc = std::make_unique<gsr::Rpc>();
const bool rpc_created = rpc->create("gsr-ui");
if(!rpc_created)
fprintf(stderr, "Error: Failed to create rpc\n");
const gsr::RpcOpenResult rpc_open_result = rpc->open("gsr-ui");
if(is_gsr_ui_virtual_keyboard_running() || !rpc_created) {
if(is_gsr_ui_virtual_keyboard_running() || rpc_open_result == gsr::RpcOpenResult::OK) {
if(launch_action == LaunchAction::LAUNCH_DAEMON)
return 1;
rpc = std::make_unique<gsr::Rpc>();
if(rpc->open("gsr-ui") && rpc->write("show_ui\n", 8)) {
if(rpc->write("show_ui\n", 8)) {
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");
@@ -243,6 +242,9 @@ int main(int argc, char **argv) {
return 1;
}
if(!rpc->create("gsr-ui"))
fprintf(stderr, "Error: Failed to create rpc\n");
if(gsr::pidof("gpu-screen-recorder", -1) != -1) {
const char *args[] = { "gsr-notify", "--text", "GPU Screen Recorder is already running in another process.\nPlease close it before using GPU Screen Recorder UI.", "--timeout", "5.0", "--icon-color", "ff0000", "--bg-color", "ff0000", nullptr };
gsr::exec_program_daemonized(args);

View File

@@ -5,9 +5,11 @@
#include <stdbool.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/socket.h>
#include <sys/un.h>
static void get_runtime_filepath(char *buffer, size_t buffer_size, const char *filename) {
static void get_socket_filepath(char *buffer, size_t buffer_size, const char *filename) {
char dir[PATH_MAX];
const char *runtime_dir = getenv("XDG_RUNTIME_DIR");
@@ -23,7 +25,7 @@ static void get_runtime_filepath(char *buffer, size_t buffer_size, const char *f
}
/* Assumes |str| size is less than 256 */
static void fifo_write_all(int file_fd, const char *str) {
static void file_write_all(int file_fd, const char *str) {
char command[256];
const ssize_t command_size = snprintf(command, sizeof(command), "%s\n", str);
if(command_size >= (ssize_t)sizeof(command)) {
@@ -33,7 +35,7 @@ static void fifo_write_all(int file_fd, const char *str) {
ssize_t offset = 0;
while(offset < (ssize_t)command_size) {
const ssize_t bytes_written = write(file_fd, str + offset, command_size - offset);
const ssize_t bytes_written = write(file_fd, command + offset, command_size - offset);
if(bytes_written > 0)
offset += bytes_written;
}
@@ -112,15 +114,34 @@ int main(int argc, char **argv) {
usage();
}
char fifo_filepath[PATH_MAX];
get_runtime_filepath(fifo_filepath, sizeof(fifo_filepath), "gsr-ui");
const int fifo_fd = open(fifo_filepath, O_RDWR | O_NONBLOCK);
if(fifo_fd <= 0) {
fprintf(stderr, "Error: failed to open fifo file %s. Maybe gsr-ui is not running?\n", fifo_filepath);
char socket_filepath[PATH_MAX];
get_socket_filepath(socket_filepath, sizeof(socket_filepath), "gsr-ui");
const int socket_fd = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0);
if(socket_fd <= 0) {
fprintf(stderr, "Error: failed to create socket\n");
exit(2);
}
fifo_write_all(fifo_fd, command);
close(fifo_fd);
struct sockaddr_un addr = {0};
addr.sun_family = AF_UNIX;
snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", socket_filepath);
for(;;) {
if(connect(socket_fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
const int err = errno;
if(err == EWOULDBLOCK) {
usleep(10 * 1000);
} else {
fprintf(stderr, "Error: failed to connect, error: %s. Maybe gsr-ui is not running?\n", strerror(err));
exit(2);
}
} else {
break;
}
}
file_write_all(socket_fd, command);
close(socket_fd);
return 0;
}