From 5dc945eb6a0361ee7a64314f7a2acbf24ea9b565 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Thu, 31 Mar 2022 02:38:18 +0200 Subject: Start/stop recording if not running/already running --- src/Process.cpp | 93 ++++++++++++++++++++++++++++++++++++++++ src/gui/Button.cpp | 15 +++++++ src/main.cpp | 122 ++++++++++++++++++++++++++++++++++++++++++++++------- 3 files changed, 215 insertions(+), 15 deletions(-) create mode 100644 src/Process.cpp (limited to 'src') diff --git a/src/Process.cpp b/src/Process.cpp new file mode 100644 index 0000000..96f6840 --- /dev/null +++ b/src/Process.cpp @@ -0,0 +1,93 @@ +#include "../include/Process.hpp" +#include +#include +#include +#include +#include +#include +#include + +namespace gsr { + bool exec_program_daemonized(const char **args) { + /* 1 argument */ + if(args[0] == nullptr) + return -1; + + pid_t pid = vfork(); + if(pid == -1) { + perror("Failed to vfork"); + return false; + } else if(pid == 0) { /* child */ + setsid(); + signal(SIGHUP, SIG_IGN); + + // Daemonize child to make the parent the init process which will reap the zombie child + pid_t second_child = vfork(); + if(second_child == 0) { // child + execvp(args[0], (char* const*)args); + perror("execvp"); + _exit(127); + } else if(second_child != -1) { + _exit(0); + } + } else { /* parent */ + waitpid(pid, nullptr, 0); + } + return true; + } + + static const char *pid_file = "/tmp/gpu-screen-recorder"; + + static bool is_process_running_program(pid_t pid, const char *program_name) { + char filepath[256]; + snprintf(filepath, sizeof(filepath), "/proc/%ld/exe", (long)pid); + + char resolved_path[PATH_MAX]; + const ssize_t resolved_path_len = readlink(filepath, resolved_path, sizeof(resolved_path) - 1); + if(resolved_path_len == -1) + return false; + + resolved_path[resolved_path_len] = '\0'; + + const int program_name_len = strlen(program_name); + return resolved_path_len >= program_name_len && memcmp(resolved_path + resolved_path_len - program_name_len, program_name, program_name_len) == 0; + } + + bool is_gpu_screen_recorder_running(int &gsr_pid, GsrMode &mode) { + gsr_pid = -1; + mode = GsrMode::Unknown; + + char buffer[256]; + int fd = open(pid_file, O_RDONLY); + if(fd == -1) + return false; + + ssize_t bytes_read = read(fd, buffer, sizeof(buffer) - 1); + if(bytes_read < 0) { + perror("failed to read gpu-screen-recorder pid file"); + close(fd); + return true; + } + buffer[bytes_read] = '\0'; + close(fd); + + long pid = 0; + if(sscanf(buffer, "%ld %120s", &pid, buffer) == 2) { + gsr_pid = pid; + if(is_process_running_program(pid, "gpu-screen-recorder")) { + if(strcmp(buffer, "replay") == 0) + mode = GsrMode::Replay; + else if(strcmp(buffer, "record") == 0) + mode = GsrMode::Record; + else if(strcmp(buffer, "stream") == 0) + mode = GsrMode::Stream; + else + mode = GsrMode::Unknown; + return true; + } + } else { + fprintf(stderr, "Warning: gpu-screen-recorder pid file is in incorrect format, it's possible that its corrupt. Ignoring...\n"); + } + return false; + } +} \ No newline at end of file diff --git a/src/gui/Button.cpp b/src/gui/Button.cpp index b266639..2250560 100644 --- a/src/gui/Button.cpp +++ b/src/gui/Button.cpp @@ -10,6 +10,7 @@ namespace gsr { } void Button::on_event(mgl::Event &event, mgl::Window&) { + /* if(event.type == mgl::Event::MouseMoved) { const bool inside = mgl::FloatRect(position, size).contains({ (float)event.mouse_move.x, (float)event.mouse_move.y }); if(mouse_inside && !inside) { @@ -17,10 +18,24 @@ namespace gsr { } else if(!mouse_inside && inside) { mouse_inside = true; } + } else if(event.type == mgl::Event::MouseButtonPressed && mouse_inside) { + + } + */ + if(event.type == mgl::Event::MouseButtonPressed && mouse_inside) { + if(on_click) + on_click(); } } void Button::draw(mgl::Window &window) { + const bool inside = mgl::FloatRect(position, size).contains(window.get_mouse_position().to_vec2f()); + if(mouse_inside && !inside) { + mouse_inside = false; + } else if(!mouse_inside && inside) { + mouse_inside = true; + } + if(mouse_inside) { // Background /* diff --git a/src/main.cpp b/src/main.cpp index ad7f6d3..ac0b7dd 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,11 +1,14 @@ #include "../include/gui/Button.hpp" #include "../include/window_texture.h" +#include "../include/Process.hpp" #include #include #include #include #include +#include +#include #include #include @@ -26,6 +29,15 @@ extern "C" { #include } +struct Config { + float scale = 1.0f; +}; + +static Config& get_config() { + static Config config; + return config; +} + static void usage() { fprintf(stderr, "usage: window-overlay \n"); exit(1); @@ -86,9 +98,10 @@ int main(int argc, char **argv) { return 1; } - XSelectInput(display, target_window, VisibilityChangeMask | StructureNotifyMask); + XSelectInput(display, target_window, StructureNotifyMask | VisibilityChangeMask); mgl::vec2i target_window_size = { target_win_attr.width, target_win_attr.height }; + get_config().scale = std::min(1.0, (double)target_win_attr.width / 1920.0); mgl::Window::CreateParams window_create_params; window_create_params.size = target_window_size; @@ -108,11 +121,11 @@ int main(int argc, char **argv) { startup_error("failed to load file: fonts/Orbitron-Regular.ttf"); mgl::Font title_font; - if(!title_font.load_from_file(title_font_file, 32)) + if(!title_font.load_from_file(title_font_file, 32 * get_config().scale)) startup_error("failed to load font: fonts/Orbitron-Bold.ttf"); mgl::Font font; - if(!font.load_from_file(font_file, 20)) + if(!font.load_from_file(font_file, 20 * get_config().scale)) startup_error("failed to load font: fonts/Orbitron-Regular.ttf"); mgl::Texture replay_button_texture; @@ -132,6 +145,7 @@ int main(int argc, char **argv) { mgl::Text description; mgl::Sprite icon; gsr::Button button; + gsr::GsrMode mode; }; const char *titles[] = { @@ -140,12 +154,18 @@ int main(int argc, char **argv) { "Livestream" }; - const char *descriptions[] = { + const char *descriptions_off[] = { "Off", "Not recording", "Not streaming" }; + const char *descriptions_on[] = { + "On", + "Recording", + "Streaming" + }; + mgl::Texture *textures[] = { &replay_button_texture, &record_button_texture, @@ -159,17 +179,19 @@ int main(int argc, char **argv) { mgl::Text title(titles[i], {0.0f, 0.0f}, title_font); title.set_color(mgl::Color(255, 255, 255)); - mgl::Text description(descriptions[i], {0.0f, 0.0f}, font); + mgl::Text description(descriptions_off[i], {0.0f, 0.0f}, font); description.set_color(mgl::Color(255, 255, 255, 150)); mgl::Sprite sprite(textures[i]); - gsr::Button button({425, 300}); + sprite.set_scale(get_config().scale); + gsr::Button button(mgl::vec2f(425 * get_config().scale, 300 * get_config().scale).floor()); MainButton main_button = { std::move(title), std::move(description), std::move(sprite), - std::move(button) + std::move(button), + gsr::GsrMode::Unknown }; main_buttons.push_back(std::move(main_button)); @@ -179,8 +201,59 @@ int main(int argc, char **argv) { // Settings button shapes.push_back({}); - const int per_button_width = 425; - const mgl::vec2i overlay_desired_size = { per_button_width * (int)main_buttons.size(), 300 }; + // Replay + main_buttons[0].button.on_click = [&]() { + /* + char window_to_record_str[32]; + snprintf(window_to_record_str, sizeof(window_to_record_str), "%ld", target_window); + + const char *args[] = { + "gpu-screen-recorder", "-w", window_to_record_str, + "-c", "mp4", + "-f", "60", + "-o", "/home/dec05eba/Videos/gpu-screen-recorder.mp4", + nullptr + }; + gsr::exec_program_daemonized(args); + */ + }; + main_buttons[0].mode = gsr::GsrMode::Replay; + + // TODO: Monitor /tmp/gpu-screen-recorder and update ui to match state + + // Record + main_buttons[1].button.on_click = [&]() { + int gpu_screen_recorder_process = -1; + gsr::GsrMode gsr_mode = gsr::GsrMode::Unknown; + if(gsr::is_gpu_screen_recorder_running(gpu_screen_recorder_process, gsr_mode) && gpu_screen_recorder_process > 0) { + kill(gpu_screen_recorder_process, SIGINT); + int status; + if(waitpid(gpu_screen_recorder_process, &status, 0) == -1) { + perror("waitpid failed"); + /* Ignore... */ + } + exit(0); + } + + char window_to_record_str[32]; + snprintf(window_to_record_str, sizeof(window_to_record_str), "%ld", target_window); + + const char *args[] = { + "gpu-screen-recorder", "-w", window_to_record_str, + "-c", "mp4", + "-f", "60", + "-o", "/home/dec05eba/Videos/gpu-screen-recorder.mp4", + nullptr + }; + gsr::exec_program_daemonized(args); + exit(0); + }; + main_buttons[1].mode = gsr::GsrMode::Record; + + main_buttons[2].mode = gsr::GsrMode::Stream; + + const int per_button_width = 425 * get_config().scale; + const mgl::vec2i overlay_desired_size(per_button_width * (int)main_buttons.size(), 300 * get_config().scale); XGCValues xgcv; xgcv.foreground = WhitePixel(display, DefaultScreen(display)); @@ -191,11 +264,25 @@ int main(int argc, char **argv) { GC shape_gc = None; auto update_overlay_shape = [&]() { - const int main_button_margin = 20; + const int main_button_margin = 20 * get_config().scale; const mgl::vec2i main_buttons_start_pos = target_window_size/2 - overlay_desired_size/2; mgl::vec2i main_button_pos = main_buttons_start_pos; + int gpu_screen_recorder_process = -1; + gsr::GsrMode gsr_mode = gsr::GsrMode::Unknown; + gsr::is_gpu_screen_recorder_running(gpu_screen_recorder_process, gsr_mode); + for(size_t i = 0; i < main_buttons.size(); ++i) { + if(main_buttons[i].mode != gsr::GsrMode::Unknown && main_buttons[i].mode == gsr_mode) { + main_buttons[i].description.set_string(descriptions_on[i]); + main_buttons[i].description.set_color(mgl::Color(118, 185, 0)); + main_buttons[i].icon.set_color(mgl::Color(118, 185, 0)); + } else { + main_buttons[i].description.set_string(descriptions_off[i]); + main_buttons[i].description.set_color(mgl::Color(255, 255, 255)); + main_buttons[i].icon.set_color(mgl::Color(255, 255, 255)); + } + main_buttons[i].title.set_position( mgl::vec2f( main_button_pos.x + per_button_width * 0.5f - main_buttons[i].title.get_bounds().size.x * 0.5f, @@ -208,8 +295,8 @@ int main(int argc, char **argv) { main_buttons[i].icon.set_position( mgl::vec2f( - main_button_pos.x + per_button_width * 0.5f - main_buttons[i].icon.get_texture()->get_size().x * 0.5f, - main_button_pos.y + overlay_desired_size.y * 0.5f - main_buttons[i].icon.get_texture()->get_size().y * 0.5f).floor()); + main_button_pos.x + per_button_width * 0.5f - main_buttons[i].icon.get_texture()->get_size().x * main_buttons[i].icon.get_scale().x * 0.5f, + main_button_pos.y + overlay_desired_size.y * 0.5f - main_buttons[i].icon.get_texture()->get_size().y * main_buttons[i].icon.get_scale().y * 0.5f).floor()); main_buttons[i].button.set_position(main_button_pos.to_vec2f()); shapes[i] = { @@ -218,7 +305,7 @@ int main(int argc, char **argv) { main_button_pos.x += per_button_width; } - const mgl::vec2i settings_button_size(128, 128); + const mgl::vec2i settings_button_size(128 * get_config().scale, 128 * get_config().scale); shapes[main_buttons.size()] = { (short)(main_buttons_start_pos.x + overlay_desired_size.x), (short)(main_buttons_start_pos.y - settings_button_size.y), (unsigned short)settings_button_size.x, (unsigned short)settings_button_size.y @@ -256,13 +343,18 @@ int main(int argc, char **argv) { Cursor default_cursor = XCreateFontCursor(display, XC_arrow); // TODO: Retry if these fail - XGrabPointer(display, window.get_system_handle(), True, ButtonPressMask|ButtonReleaseMask, GrabModeAsync, GrabModeAsync, None, default_cursor, CurrentTime); + XGrabPointer(display, window.get_system_handle(), True, ButtonPressMask | ButtonReleaseMask, GrabModeAsync, GrabModeAsync, None, default_cursor, CurrentTime); XGrabKeyboard(display, window.get_system_handle(), True, GrabModeAsync, GrabModeAsync, CurrentTime); XEvent xev; mgl::Event event; while(window.is_open()) { + if(XCheckTypedWindowEvent(display, target_window, DestroyNotify, &xev)) { + window.close(); + break; + } + if(XCheckTypedWindowEvent(display, target_window, VisibilityNotify, &xev)) { if(xev.xvisibility.state) { @@ -290,7 +382,7 @@ int main(int argc, char **argv) { } } - window.clear(mgl::Color(37, 43, 47, 255)); + window.clear(mgl::Color(37, 43, 47)); for(auto &main_button : main_buttons) { main_button.button.draw(window); window.draw(main_button.icon); -- cgit v1.2.3