From 1d2fc77cfcd3d40c3a382d3f5f8e6c28e8b38da9 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Sat, 26 Oct 2024 11:37:52 +0200 Subject: Make the ui the daemon instead, add hotkey for recording/pause --- TODO | 4 +- gsr-ui-daemon/main.c | 116 ----------------- gsr-ui-daemon/project.conf | 11 -- images/pause.png | Bin 0 -> 392 bytes include/Overlay.hpp | 18 ++- include/Theme.hpp | 1 + include/Utils.hpp | 2 - include/gui/DropdownButton.hpp | 5 +- include/gui/FileChooser.hpp | 1 + meson.build | 7 - src/Overlay.cpp | 281 ++++++++++++++++------------------------- src/Theme.cpp | 3 + src/Utils.cpp | 17 --- src/gui/DropdownButton.cpp | 11 +- src/gui/FileChooser.cpp | 39 +++--- src/main.cpp | 53 +++++--- 16 files changed, 203 insertions(+), 366 deletions(-) delete mode 100644 gsr-ui-daemon/main.c delete mode 100644 gsr-ui-daemon/project.conf create mode 100644 images/pause.png diff --git a/TODO b/TODO index dc51b96..0c162aa 100644 --- a/TODO +++ b/TODO @@ -52,4 +52,6 @@ Have different modes. Overlay, window and side menu. Overlay can be used on x11, Look at /home/dec05eba/git/global-hotkeys for hotkeys. -Show navigation breadcrumbs for settings and deeper navigation (such as selecting a directory to save videos). \ No newline at end of file +Show navigation breadcrumbs for settings and deeper navigation (such as selecting a directory to save videos). + +If recording stops because of an error then the ui wont be running and we wont get a notification that recording failed. \ No newline at end of file diff --git a/gsr-ui-daemon/main.c b/gsr-ui-daemon/main.c deleted file mode 100644 index d5419d7..0000000 --- a/gsr-ui-daemon/main.c +++ /dev/null @@ -1,116 +0,0 @@ -#include -#include -#include -#include -#include - -#include -#include - -bool exec_program(const char **args, pid_t *process_id) { - *process_id = -1; - - /* 1 argument */ - if(args[0] == NULL) - return false; - - pid_t pid = vfork(); - if(pid == -1) { - perror("Failed to vfork"); - return false; - } else if(pid == 0) { /* child */ - execvp(args[0], (char* const*)args); - perror("execvp"); - _exit(127); - } else { /* parent */ - *process_id = pid; - } - - return true; -} - -static int ignore_xerror(Display *display, XErrorEvent *ee) { - (void)display; - (void)ee; - return 0; -} - -static void sigterm_handler(int dummy) { - (void)dummy; -} - -static const KeySym toggle_overlay_key = XK_Z; -static unsigned int toggle_overlay_modifiers = Mod1Mask; - -static void grab_keys(Display *display) { - unsigned int numlockmask = 0; - KeyCode numlock_keycode = XKeysymToKeycode(display, XK_Num_Lock); - XModifierKeymap *modmap = XGetModifierMapping(display); - for(int i = 0; i < 8; ++i) { - for(int j = 0; j < modmap->max_keypermod; ++j) { - if(modmap->modifiermap[i * modmap->max_keypermod + j] == numlock_keycode) - numlockmask = (1 << i); - } - } - XFreeModifiermap(modmap); - - XErrorHandler prev_error_handler = XSetErrorHandler(ignore_xerror); - - Window root_window = DefaultRootWindow(display); - unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask }; - for(int i = 0; i < 4; ++i) { - XGrabKey(display, XKeysymToKeycode(display, toggle_overlay_key), toggle_overlay_modifiers|modifiers[i], root_window, False, GrabModeAsync, GrabModeAsync); - } - - XSync(display, False); - XSetErrorHandler(prev_error_handler); -} - -int main(void) { - Display *display = XOpenDisplay(NULL); - if(!display) { - fprintf(stderr, "Error: XOpenDisplay failed\n"); - return 1; - } - - grab_keys(display); - const KeyCode overlay_keycode = XKeysymToKeycode(display, toggle_overlay_key); - - XSetErrorHandler(ignore_xerror); - /* Killing gpu-screen-recorder with SIGTERM also gives us SIGTERM. We want to ignore that as that has no meaning here */ - signal(SIGTERM, sigterm_handler); - - pid_t overlay_pid = -1; - - fprintf(stderr, "gsr ui is now ready, waiting for inputs. Press alt+z to show/hide the ui\n"); - - XEvent xev; - for(;;) { - XNextEvent(display, &xev); - if(xev.type == KeyPress && xev.xkey.keycode == overlay_keycode) { - if(overlay_pid != -1) { - int status; - if(waitpid(overlay_pid, &status, WNOHANG) == 0) { - kill(overlay_pid, SIGINT); - int status; - if(waitpid(overlay_pid, &status, 0) == -1) { - perror("waitpid failed"); - /* Ignore... */ - } - overlay_pid = -1; - continue; - } else { - overlay_pid = -1; - } - } - - if(overlay_pid == -1) { - fprintf(stderr, "launch ui\n"); - // TODO: window_with_input_focus - const char *args[] = { "gsr-ui", NULL }; - exec_program(args, &overlay_pid); - } - } - } - return 0; -} diff --git a/gsr-ui-daemon/project.conf b/gsr-ui-daemon/project.conf deleted file mode 100644 index 758d50c..0000000 --- a/gsr-ui-daemon/project.conf +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "gpu-screen-recorder-overlay-daemon" -type = "executable" -version = "0.1.0" -platforms = ["posix"] - -[config] -ignore_dirs = ["build"] - -[dependencies] -x11 = ">=1" diff --git a/images/pause.png b/images/pause.png new file mode 100644 index 0000000..debdf30 Binary files /dev/null and b/images/pause.png differ diff --git a/include/Overlay.hpp b/include/Overlay.hpp index fcc3781..fa6630d 100644 --- a/include/Overlay.hpp +++ b/include/Overlay.hpp @@ -23,6 +23,13 @@ namespace gsr { STREAM }; + enum class NotificationType { + NONE, + RECORD, + REPLAY, + STREAM + }; + class Overlay { public: Overlay(mgl::Window &window, std::string resources_path, GsrInfo gsr_info, egl_functions egl_funcs, mgl::Color bg_color); @@ -36,17 +43,17 @@ namespace gsr { void show(); void hide(); void toggle_show(); + void toggle_record(); + void toggle_pause(); + void show_notification(const char *str, double timeout_seconds, mgl::Color icon_color, mgl::Color bg_color, NotificationType notification_type); bool is_open() const; private: void process_key_bindings(mgl::Event &event); void update_gsr_process_status(); - void load_program_status(); - void save_program_status(); - void load_program_pid(); - void save_program_pid(); - void recording_stopped_remove_runtime_files(); + void update_ui_recording_paused(); + void update_ui_recording_unpaused(); void update_ui_recording_started(); void update_ui_recording_stopped(); @@ -89,6 +96,7 @@ namespace gsr { DropdownButton *stream_dropdown_button_ptr = nullptr; RecordingStatus recording_status = RecordingStatus::NONE; + bool paused = false; std::array key_bindings; }; diff --git a/include/Theme.hpp b/include/Theme.hpp index 79ad358..0dc3cd1 100644 --- a/include/Theme.hpp +++ b/include/Theme.hpp @@ -41,6 +41,7 @@ namespace gsr { mgl::Texture checkbox_background_texture; mgl::Texture play_texture; mgl::Texture stop_texture; + mgl::Texture pause_texture; double double_click_timeout_seconds = 0.4; diff --git a/include/Utils.hpp b/include/Utils.hpp index 8ca38b5..e7bb3bc 100644 --- a/include/Utils.hpp +++ b/include/Utils.hpp @@ -32,6 +32,4 @@ namespace gsr { // Returns the path to the parent directory (ignoring trailing /) // of "." if there is no parent directory and the directory path is relative std::string get_parent_directory(std::string_view directory); - - std::optional get_gsr_runtime_dir(); } \ No newline at end of file diff --git a/include/gui/DropdownButton.hpp b/include/gui/DropdownButton.hpp index 40d2810..cbbcda2 100644 --- a/include/gui/DropdownButton.hpp +++ b/include/gui/DropdownButton.hpp @@ -10,7 +10,7 @@ namespace gsr { class DropdownButton : public Widget { public: - DropdownButton(mgl::Font *title_font, mgl::Font *description_font, const char *title, const char *description_activated, const char *description_deactivated, mgl::Texture *icon_texture, mgl::vec2f size); + DropdownButton(mgl::Font *title_font, mgl::Font *description_font, const char *title, const char *description, mgl::Texture *icon_texture, mgl::vec2f size); DropdownButton(const DropdownButton&) = delete; DropdownButton& operator=(const DropdownButton&) = delete; @@ -21,6 +21,7 @@ namespace gsr { void set_item_label(const std::string &id, const std::string &new_label); void set_item_icon(const std::string &id, mgl::Texture *texture); + void set_description(std::string description_text); void set_activated(bool activated); mgl::vec2f get_size() override; @@ -50,8 +51,6 @@ namespace gsr { mgl::Text description; mgl::Sprite icon_sprite; - std::string description_activated; - std::string description_deactivated; bool activated = false; }; } \ No newline at end of file diff --git a/include/gui/FileChooser.hpp b/include/gui/FileChooser.hpp index 36dac2d..dbe49bb 100644 --- a/include/gui/FileChooser.hpp +++ b/include/gui/FileChooser.hpp @@ -54,6 +54,7 @@ namespace gsr { bool on_event(mgl::Event &event, mgl::Window &window, mgl::vec2f offset) override; void draw(mgl::Window &window, mgl::vec2f offset) override; + void draw_navigation(mgl::Window &window, mgl::vec2f draw_pos); mgl::vec2f get_size() override; diff --git a/meson.build b/meson.build index c16e567..82b00ca 100644 --- a/meson.build +++ b/meson.build @@ -57,12 +57,5 @@ executable( cpp_args : '-DGSR_UI_RESOURCES_PATH="' + gsr_ui_resources_path + '"', ) -executable( - 'gsr-ui-daemon', - ['gsr-ui-daemon/main.c'], - install : true, - dependencies : [dependency('x11')], -) - install_subdir('images', install_dir : gsr_ui_resources_path) install_subdir('fonts', install_dir : gsr_ui_resources_path) \ No newline at end of file diff --git a/src/Overlay.cpp b/src/Overlay.cpp index ff12770..204ff6f 100644 --- a/src/Overlay.cpp +++ b/src/Overlay.cpp @@ -197,32 +197,32 @@ namespace gsr { close_button_widget({0.0f, 0.0f}) { memset(&window_texture, 0, sizeof(window_texture)); - load_program_status(); - load_program_pid(); - key_bindings[0].key_event.code = mgl::Keyboard::Z; - key_bindings[0].key_event.alt = true; + key_bindings[0].key_event.code = mgl::Keyboard::Escape; + key_bindings[0].key_event.alt = false; key_bindings[0].key_event.control = false; key_bindings[0].key_event.shift = false; key_bindings[0].key_event.system = false; key_bindings[0].callback = [this]() { - while(!page_stack.empty()) { - page_stack.pop(); - } + page_stack.pop(); }; } Overlay::~Overlay() { hide(); - // if(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... */ - // } - // gpu_screen_recorder_process = -1; - // } + if(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... */ + } + gpu_screen_recorder_process = -1; + + // TODO: Show this with a slight delay to make sure it doesn't show up in the video + if(config->record_config.show_video_saved_notifications) + show_notification("Recording has been saved", 3.0, mgl::Color(255, 255, 255), get_theme().tint_color, NotificationType::RECORD); + } } static uint32_t key_event_to_bitmask(mgl::Event::KeyEvent key_event) { @@ -248,12 +248,10 @@ namespace gsr { return; close_button_widget.on_event(event, window, mgl::vec2f(0.0f, 0.0f)); - page_stack.on_event(event, window, mgl::vec2f(0.0f, 0.0f)); - if(event.type == mgl::Event::KeyReleased) { - if(event.key.code == mgl::Keyboard::Escape) - page_stack.pop(); - } - //process_key_bindings(event); + if(!page_stack.on_event(event, window, mgl::vec2f(0.0f, 0.0f))) + return; + + process_key_bindings(event); } void Overlay::draw(mgl::Window &window) { @@ -337,7 +335,7 @@ namespace gsr { auto main_buttons_list = std::make_unique(List::Orientation::HORIZONTAL); main_buttons_list->set_spacing(0.0f); { - auto button = std::make_unique(&get_theme().title_font, &get_theme().body_font, "Instant Replay", "On", "Off", &get_theme().replay_button_texture, + auto button = std::make_unique(&get_theme().title_font, &get_theme().body_font, "Instant Replay", "Off", &get_theme().replay_button_texture, mgl::vec2f(button_width, button_height)); replay_dropdown_button_ptr = button.get(); button->add_item("Turn on", "start", "Alt+Shift+F10"); @@ -347,18 +345,19 @@ namespace gsr { main_buttons_list->add_widget(std::move(button)); } { - auto button = std::make_unique(&get_theme().title_font, &get_theme().body_font, "Record", "Recording", "Not recording", &get_theme().record_button_texture, + auto button = std::make_unique(&get_theme().title_font, &get_theme().body_font, "Record", "Not recording", &get_theme().record_button_texture, mgl::vec2f(button_width, button_height)); record_dropdown_button_ptr = button.get(); button->add_item("Start", "start", "Alt+F9"); button->add_item("Pause", "pause", "Alt+F7"); button->add_item("Settings", "settings"); button->set_item_icon("start", &get_theme().play_texture); + button->set_item_icon("pause", &get_theme().pause_texture); button->on_click = std::bind(&Overlay::on_press_start_record, this, std::placeholders::_1); main_buttons_list->add_widget(std::move(button)); } { - auto button = std::make_unique(&get_theme().title_font, &get_theme().body_font, "Livestream", "Streaming", "Not streaming", &get_theme().stream_button_texture, + auto button = std::make_unique(&get_theme().title_font, &get_theme().body_font, "Livestream", "Not streaming", &get_theme().stream_button_texture, mgl::vec2f(button_width, button_height)); stream_dropdown_button_ptr = button.get(); button->add_item("Start", "start", "Alt+F8"); @@ -439,6 +438,9 @@ namespace gsr { if(gpu_screen_recorder_process > 0 && recording_status == RecordingStatus::RECORD) update_ui_recording_started(); + + if(paused) + update_ui_recording_paused(); } void Overlay::hide() { @@ -466,6 +468,58 @@ namespace gsr { show(); } + void Overlay::toggle_record() { + on_press_start_record("start"); + } + + void Overlay::toggle_pause() { + if(recording_status != RecordingStatus::RECORD || gpu_screen_recorder_process <= 0) + return; + + if(paused) { + update_ui_recording_unpaused(); + show_notification("Recording has been unpaused", 3.0, mgl::Color(255, 255, 255), get_theme().tint_color, NotificationType::RECORD); + } else { + update_ui_recording_paused(); + show_notification("Recording has been paused", 3.0, mgl::Color(255, 255, 255), get_theme().tint_color, NotificationType::RECORD); + } + + kill(gpu_screen_recorder_process, SIGUSR2); + paused = !paused; + } + + static const char* notification_type_to_string(NotificationType notification_type) { + switch(notification_type) { + case NotificationType::NONE: return nullptr; + case NotificationType::RECORD: return "record"; + case NotificationType::REPLAY: return "replay"; + case NotificationType::STREAM: return "stream"; + } + return nullptr; + } + + void Overlay::show_notification(const char *str, double timeout_seconds, mgl::Color icon_color, mgl::Color bg_color, NotificationType notification_type) { + char timeout_seconds_str[32]; + snprintf(timeout_seconds_str, sizeof(timeout_seconds_str), "%f", timeout_seconds); + + const std::string icon_color_str = color_to_hex_str(icon_color); + const std::string bg_color_str = color_to_hex_str(bg_color); + const char *notification_args[12] = { + "gsr-notify", "--text", str, "--timeout", timeout_seconds_str, + "--icon-color", icon_color_str.c_str(), "--bg-color", bg_color_str.c_str(), + }; + + const char *notification_type_str = notification_type_to_string(notification_type); + if(notification_type_str) { + notification_args[9] = "--icon"; + notification_args[10] = "record", + notification_args[11] = nullptr; + } else { + notification_args[9] = nullptr; + } + exec_program_daemonized(notification_args); + } + bool Overlay::is_open() const { return visible; } @@ -499,153 +553,52 @@ namespace gsr { gpu_screen_recorder_process = -1; recording_status = RecordingStatus::NONE; - recording_stopped_remove_runtime_files(); update_ui_recording_stopped(); if(exit_code == 0) { - if(config->record_config.show_video_saved_notifications) { - const std::string tint_color_as_hex = color_to_hex_str(get_theme().tint_color); - const char *notification_args[] = { - "gsr-notify", "--text", "Recording has been saved", "--timeout", "3.0", - "--icon", "record", - "--icon-color", "ffffff", "--bg-color", tint_color_as_hex.c_str(), - nullptr - }; - exec_program_daemonized(notification_args); - } + if(config->record_config.show_video_saved_notifications) + show_notification("Recording has been saved", 3.0, mgl::Color(255, 255, 255), get_theme().tint_color, NotificationType::RECORD); } else { fprintf(stderr, "Warning: gpu-screen-recorder (%d) exited with exit status %d\n", (int)gpu_screen_recorder_process, exit_code); - const char *notification_args[] = { - "gsr-notify", "--text", "Failed to start/save recording", "--timeout", "3.0", - "--icon", "record", - "--icon-color", "ff0000", "--bg-color", "ff0000", - nullptr - }; - exec_program_daemonized(notification_args); - } - } - - static RecordingStatus recording_status_from_string(const char *status) { - RecordingStatus recording_status = RecordingStatus::NONE; - if(strcmp(status, "none") == 0) - recording_status = RecordingStatus::NONE; - else if(strcmp(status, "replay") == 0) - recording_status = RecordingStatus::REPLAY; - else if(strcmp(status, "record") == 0) - recording_status = RecordingStatus::RECORD; - else if(strcmp(status, "stream") == 0) - recording_status = RecordingStatus::STREAM; - return recording_status; - } - - static const char* recording_status_to_string(RecordingStatus status) { - switch(status) { - case RecordingStatus::NONE: return "none"; - case RecordingStatus::REPLAY: return "replay"; - case RecordingStatus::RECORD: return "record"; - case RecordingStatus::STREAM: return "stream"; + show_notification("Failed to start/save recording", 3.0, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), NotificationType::RECORD); } - return "none"; } - void Overlay::load_program_status() { - recording_status = RecordingStatus::NONE; - - std::optional status_filepath = get_gsr_runtime_dir(); - if(!status_filepath) - throw std::runtime_error("Failed to find/create runtime directory /run/user/.../gsr-ui or /tmp/gsr-ui"); - - status_filepath.value() += "/status"; - - std::string file_content; - if(!file_get_content(status_filepath.value().c_str(), file_content)) + void Overlay::update_ui_recording_paused() { + if(!visible || recording_status != RecordingStatus::RECORD) return; - recording_status = recording_status_from_string(file_content.c_str()); + record_dropdown_button_ptr->set_description("Paused"); + record_dropdown_button_ptr->set_item_label("pause", "Unpause"); + record_dropdown_button_ptr->set_item_icon("pause", &get_theme().play_texture); } - void Overlay::save_program_status() { - std::optional status_filepath = get_gsr_runtime_dir(); - if(!status_filepath) - throw std::runtime_error("Failed to find/create runtime directory /run/user/.../gsr-ui or /tmp/gsr-ui"); - - status_filepath.value() += "/status"; - if(!file_overwrite(status_filepath.value().c_str(), recording_status_to_string(recording_status))) - fprintf(stderr, "Error: failed to update status to file %s\n", status_filepath.value().c_str()); - } - - void Overlay::load_program_pid() { - gpu_screen_recorder_process = -1; - - std::optional status_filepath = get_gsr_runtime_dir(); - if(!status_filepath) - throw std::runtime_error("Failed to find/create runtime directory /run/user/.../gsr-ui or /tmp/gsr-ui"); - - status_filepath.value() += "/pid"; - - std::string file_content; - if(!file_get_content(status_filepath.value().c_str(), file_content)) + void Overlay::update_ui_recording_unpaused() { + if(!visible || recording_status != RecordingStatus::RECORD) return; - int pid = -1; - if(sscanf(file_content.c_str(), "%d", &pid) != 1) { - fprintf(stderr, "Error: failed to read pid from file %s, content: %s\n", status_filepath.value().c_str(), file_content.c_str()); - return; - } - - char cmdline_path[256]; - snprintf(cmdline_path, sizeof(cmdline_path), "/proc/%d/cmdline", pid); - - char program_arg0[PATH_MAX]; - program_arg0[0] = '\0'; - if(!read_cmdline_arg0(cmdline_path, program_arg0)) { - fprintf(stderr, "Error: failed to parse arg0 from file %s. Was the gpu-screen-recorder process that was started by gsr-ui closed by another program or the user?\n", cmdline_path); - return; - } - - if(strcmp(program_arg0, "gpu-screen-recorder") != 0) { - fprintf(stderr, "Warning: process %d exists but doesn't belong to gpu-screen-recorder (is instead %s). Was the gpu-screen-recorder process that was started by gsr-ui closed by another program or the user?\n", pid, program_arg0); - return; - } - - gpu_screen_recorder_process = pid; + record_dropdown_button_ptr->set_description("Recording"); + record_dropdown_button_ptr->set_item_label("pause", "Pause"); + record_dropdown_button_ptr->set_item_icon("pause", &get_theme().pause_texture); } - void Overlay::save_program_pid() { - std::optional status_filepath = get_gsr_runtime_dir(); - if(!status_filepath) - throw std::runtime_error("Failed to find/create runtime directory /run/user/.../gsr-ui or /tmp/gsr-ui"); - - status_filepath.value() += "/pid"; - - char str[32]; - snprintf(str, sizeof(str), "%d", (int)gpu_screen_recorder_process); - if(!file_overwrite(status_filepath.value().c_str(), str)) - fprintf(stderr, "Error: failed to update pid to file %s\n", status_filepath.value().c_str()); - } - - void Overlay::recording_stopped_remove_runtime_files() { - std::optional status_filepath = get_gsr_runtime_dir(); - if(!status_filepath) { - fprintf(stderr, "Error: Failed to find/create runtime directory /run/user/.../gsr-ui or /tmp/gsr-ui"); + void Overlay::update_ui_recording_started() { + if(!visible) return; - } - const std::string status_file = status_filepath.value() + "/status"; - const std::string pid_file = status_filepath.value() + "/pid"; - remove(status_file.c_str()); - remove(pid_file.c_str()); - } - - void Overlay::update_ui_recording_started() { record_dropdown_button_ptr->set_item_label("start", "Stop and save"); record_dropdown_button_ptr->set_activated(true); + record_dropdown_button_ptr->set_description("Recording"); record_dropdown_button_ptr->set_item_icon("start", &get_theme().stop_texture); } void Overlay::update_ui_recording_stopped() { + if(!visible) + return; + record_dropdown_button_ptr->set_item_label("start", "Start"); record_dropdown_button_ptr->set_activated(false); + record_dropdown_button_ptr->set_description("Not recording"); record_dropdown_button_ptr->set_item_icon("start", &get_theme().play_texture); } @@ -700,23 +653,30 @@ namespace gsr { } void Overlay::on_press_start_record(const std::string &id) { + audio_devices = get_audio_devices(); + if(id == "settings") { auto record_settings_page = std::make_unique(SettingsPage::Type::RECORD, gsr_info, audio_devices, config, &page_stack); page_stack.push(std::move(record_settings_page)); return; } + if(id == "pause") { + toggle_pause(); + return; + } + if(id != "start") return; if(!config) config = Config(); + paused = false; + // window.close(); // usleep(1000 * 50); // 50 milliseconds - const std::string tint_color_as_hex = color_to_hex_str(get_theme().tint_color); - if(gpu_screen_recorder_process > 0) { kill(gpu_screen_recorder_process, SIGINT); int status; @@ -730,19 +690,11 @@ namespace gsr { //exit(0); gpu_screen_recorder_process = -1; recording_status = RecordingStatus::NONE; - recording_stopped_remove_runtime_files(); update_ui_recording_stopped(); // TODO: Show this with a slight delay to make sure it doesn't show up in the video - if(config->record_config.show_video_saved_notifications) { - const char *notification_args[] = { - "gsr-notify", "--text", "Recording has been saved", "--timeout", "3.0", - "--icon", "record", - "--icon-color", "ffffff", "--bg-color", tint_color_as_hex.c_str(), - nullptr - }; - exec_program_daemonized(notification_args); - } + if(config->record_config.show_video_saved_notifications) + show_notification("Recording has been saved", 3.0, mgl::Color(255, 255, 255), get_theme().tint_color, NotificationType::RECORD); return; } @@ -799,8 +751,6 @@ namespace gsr { // TODO: Show notification failed to start } else { recording_status = RecordingStatus::RECORD; - save_program_status(); - save_program_pid(); update_ui_recording_started(); } @@ -813,15 +763,8 @@ namespace gsr { // TODO: Do not run this is a daemon. Instead get the pid and when launching another notification close the current notification // program and start another one. This can also be used to check when the notification has finished by checking with waitpid NOWAIT // to see when the program has exit. - if(config->record_config.show_recording_started_notifications) { - const char *notification_args[] = { - "gsr-notify", "--text", "Recording has started", "--timeout", "3.0", - "--icon", "record", - "--icon-color", tint_color_as_hex.c_str(), "--bg-color", tint_color_as_hex.c_str(), - nullptr - }; - exec_program_daemonized(notification_args); - } + if(config->record_config.show_recording_started_notifications) + show_notification("Recording has started", 3.0, get_theme().tint_color, get_theme().tint_color, NotificationType::RECORD); //exit(0); // window.set_visible(false); // window.close(); diff --git a/src/Theme.cpp b/src/Theme.cpp index 037f159..3c8b981 100644 --- a/src/Theme.cpp +++ b/src/Theme.cpp @@ -94,6 +94,9 @@ namespace gsr { if(!theme->stop_texture.load_from_file((resources_path + "images/stop.png").c_str())) goto error; + if(!theme->pause_texture.load_from_file((resources_path + "images/pause.png").c_str())) + goto error; + return true; error: diff --git a/src/Utils.cpp b/src/Utils.cpp index 0f86ecc..6d45196 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -198,21 +198,4 @@ namespace gsr { } return result; } - - std::optional get_gsr_runtime_dir() { - std::optional result; - char runtime_dir_path[256]; - snprintf(runtime_dir_path, sizeof(runtime_dir_path), "/run/user/%u", (unsigned int)getuid()); - - struct stat st; - if(stat(runtime_dir_path, &st) == -1 || !S_ISDIR(st.st_mode)) - snprintf(runtime_dir_path, sizeof(runtime_dir_path), "/tmp"); - - strcat(runtime_dir_path, "/gsr-ui"); - if(create_directory_recursive(runtime_dir_path) != 0) - return result; - - result = runtime_dir_path; - return result; - } } \ No newline at end of file diff --git a/src/gui/DropdownButton.cpp b/src/gui/DropdownButton.cpp index b9b962f..28c1b5b 100644 --- a/src/gui/DropdownButton.cpp +++ b/src/gui/DropdownButton.cpp @@ -15,9 +15,8 @@ namespace gsr { static const float icon_spacing_scale = 0.008f; static const float border_scale = 0.003f; - DropdownButton::DropdownButton(mgl::Font *title_font, mgl::Font *description_font, const char *title, const char *description_activated, const char *description_deactivated, mgl::Texture *icon_texture, mgl::vec2f size) : - title_font(title_font), description_font(description_font), size(size), title(title, *title_font), description(description_deactivated, *description_font), - description_activated(description_activated), description_deactivated(description_deactivated) + DropdownButton::DropdownButton(mgl::Font *title_font, mgl::Font *description_font, const char *title, const char *description, mgl::Texture *icon_texture, mgl::vec2f size) : + title_font(title_font), description_font(description_font), size(size), title(title, *title_font), description(description, *description_font) { if(icon_texture && icon_texture->is_valid()) { icon_sprite.set_texture(icon_texture); @@ -202,6 +201,10 @@ namespace gsr { } } + void DropdownButton::set_description(std::string description_text) { + description.set_string(std::move(description_text)); + } + void DropdownButton::set_activated(bool activated) { if(this->activated == activated) return; @@ -209,11 +212,9 @@ namespace gsr { this->activated = activated; if(activated) { - description = mgl::Text(description_activated, *description_font); description.set_color(get_theme().tint_color); icon_sprite.set_color(get_theme().tint_color); } else { - description = mgl::Text(description_deactivated, *description_font); description.set_color(mgl::Color(150, 150, 150)); icon_sprite.set_color(mgl::Color(255, 255, 255)); } diff --git a/src/gui/FileChooser.cpp b/src/gui/FileChooser.cpp index 8ec498f..d445ccf 100644 --- a/src/gui/FileChooser.cpp +++ b/src/gui/FileChooser.cpp @@ -223,15 +223,36 @@ namespace gsr { current_directory_padding_top_scale * get_theme().window_height + current_directory_padding_bottom_scale * get_theme().window_height ); - mgl::vec2f current_directory_background_size = mgl::vec2f(size.x, current_directory_text.get_bounds().size.y + current_directory_padding.y).floor(); + const float current_directory_background_height = (int)(current_directory_text.get_bounds().size.y + current_directory_padding.y); + + draw_pos += mgl::vec2f(0.0f, current_directory_background_height + spacing_between_current_directory_and_content * get_theme().window_height); + const mgl::vec2f body_size = mgl::vec2f(size.x, size.y - (draw_pos.y - draw_pos_start.y)).floor(); + scrollable_page.set_size(body_size); + file_chooser_body_ptr->set_size(scrollable_page.get_inner_size()); + mgl::Rectangle content_background(scrollable_page.get_inner_size().floor()); + content_background.set_position(draw_pos.floor()); + content_background.set_color(mgl::Color(0, 0, 0, 120)); + window.draw(content_background); + + draw_navigation(window, draw_pos_start); + + scrollable_page.draw(window, draw_pos.floor()); + } + + void FileChooser::draw_navigation(mgl::Window &window, mgl::vec2f draw_pos) { + const mgl::vec2f current_directory_padding( + current_directory_padding_left_scale * get_theme().window_height + current_directory_padding_right_scale * get_theme().window_height, + current_directory_padding_top_scale * get_theme().window_height + current_directory_padding_bottom_scale * get_theme().window_height + ); + mgl::vec2f current_directory_background_size = mgl::vec2f(size.x, current_directory_text.get_bounds().size.y + current_directory_padding.y).floor(); up_arrow_sprite.set_height((int)(current_directory_background_size.y * 0.8f)); - up_arrow_sprite.set_position((draw_pos + mgl::vec2f(size.x - up_arrow_sprite.get_size().x, current_directory_background_size.y * 0.5f - up_arrow_sprite.get_size().y * 0.5f)).floor()); + up_arrow_sprite.set_position((draw_pos + mgl::vec2f(file_chooser_body_ptr->get_size().x - up_arrow_sprite.get_size().x, current_directory_background_size.y * 0.5f - up_arrow_sprite.get_size().y * 0.5f)).floor()); const bool mouse_inside_up_arrow = mgl::FloatRect(up_arrow_sprite.get_position(), up_arrow_sprite.get_size()).contains(window.get_mouse_position().to_vec2f()) && !has_parent_with_selected_child_widget(); up_arrow_sprite.set_color(mouse_inside_up_arrow ? get_theme().tint_color : mgl::Color(255, 255, 255)); window.draw(up_arrow_sprite); - current_directory_background_size.x -= (up_arrow_sprite.get_size().x + up_button_spacing_scale * get_theme().window_height); + current_directory_background_size.x = file_chooser_body_ptr->get_size().x - up_arrow_sprite.get_size().x - up_button_spacing_scale * get_theme().window_height; mgl::Rectangle current_directory_background(current_directory_background_size.floor()); current_directory_background.set_color(mgl::Color(0, 0, 0, 120)); current_directory_background.set_position(draw_pos.floor()); @@ -240,18 +261,6 @@ namespace gsr { current_directory_text.set_color(get_theme().text_color); current_directory_text.set_position((draw_pos + mgl::vec2f(current_directory_padding.x, current_directory_background_size.y * 0.5f - current_directory_text.get_bounds().size.y * 0.5f)).floor()); window.draw(current_directory_text); - - draw_pos += mgl::vec2f(0.0f, current_directory_background_size.y + spacing_between_current_directory_and_content * get_theme().window_height); - const mgl::vec2f body_size = mgl::vec2f(size.x, size.y - (draw_pos.y - draw_pos_start.y)).floor(); - scrollable_page.set_size(body_size); - file_chooser_body_ptr->set_size(scrollable_page.get_inner_size()); - - mgl::Rectangle content_background(scrollable_page.get_inner_size().floor()); - content_background.set_position(draw_pos.floor()); - content_background.set_color(mgl::Color(0, 0, 0, 120)); - window.draw(content_background); - - scrollable_page.draw(window, draw_pos.floor()); } mgl::vec2f FileChooser::get_size() { diff --git a/src/main.cpp b/src/main.cpp index 198cb9f..4f265fc 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -10,8 +10,7 @@ #include #include -#define XK_LATIN1 -#include +#include #include #include #include @@ -33,7 +32,7 @@ extern "C" { const mgl::Color bg_color(0, 0, 0, 100); static void usage() { - fprintf(stderr, "usage: window-overlay\n"); + fprintf(stderr, "usage: gsr-ui [toggle-record|toggle-pause]\n"); exit(1); } @@ -49,10 +48,15 @@ static void sigint_handler(int signal) { } int main(int argc, char **argv) { - (void)argv; - if(argc != 1) + setlocale(LC_ALL, "C"); // Sigh... stupid C + + if(argc > 3) usage(); + const char *action = NULL; + if(argc > 1) + action = argv[1]; + signal(SIGINT, sigint_handler); gsr::GsrInfo gsr_info; @@ -124,25 +128,44 @@ int main(int argc, char **argv) { exit(1); } + fprintf(stderr, "info: gsr ui is now ready, waiting for inputs. Press alt+z to show/hide the overlay\n"); + gsr::Overlay overlay(window, resources_path, gsr_info, egl_funcs, bg_color); - overlay.show(); + //overlay.show(); + + gsr::GlobalHotkeysX11 global_hotkeys; + const bool show_hotkey_registered = global_hotkeys.bind_key_press({ XK_z, Mod1Mask }, "open/hide", [&](const std::string &id) { + fprintf(stderr, "pressed %s\n", id.c_str()); + overlay.toggle_show(); + }); + + const bool record_hotkey_registered = global_hotkeys.bind_key_press({ XK_F9, Mod1Mask }, "record/save", [&](const std::string &id) { + fprintf(stderr, "pressed %s\n", id.c_str()); + overlay.toggle_record(); + }); + + const bool pause_hotkey_registered = global_hotkeys.bind_key_press({ XK_F7, Mod1Mask }, "pause/unpause", [&](const std::string &id) { + fprintf(stderr, "pressed %s\n", id.c_str()); + overlay.toggle_pause(); + }); + + if(!show_hotkey_registered) + fprintf(stderr, "error: failed to register hotkey alt+z for showing the overlay because the hotkey is registed by another program\n"); - // gsr::GlobalHotkeysX11 global_hotkeys; - // global_hotkeys.bind_key_press({ XK_z, Mod1Mask }, "open/hide", [&](const std::string &id) { - // fprintf(stderr, "pressed %s\n", id.c_str()); - // overlay.toggle_show(); - // }); + if(!record_hotkey_registered) + fprintf(stderr, "error: failed to register hotkey alt+f9 for recording because the hotkey is registed by another program\n"); - //fprintf(stderr, "info: gsr ui is now ready, waiting for inputs. Press alt+z to show/hide the overlay\n"); + if(!pause_hotkey_registered) + fprintf(stderr, "error: failed to register hotkey alt+f7 for pausing because the hotkey is registed by another program\n"); mgl::Event event; mgl::Clock frame_delta_clock; - while(window.is_open() && overlay.is_open() && running) { + while(window.is_open() && running) { const double frame_delta_seconds = frame_delta_clock.get_elapsed_time_seconds(); frame_delta_clock.restart(); gsr::set_frame_delta_seconds(frame_delta_seconds); - //global_hotkeys.poll_events(); + global_hotkeys.poll_events(); while(window.poll_event(event)) { overlay.on_event(event, window); } @@ -152,7 +175,7 @@ int main(int argc, char **argv) { window.display(); } - fprintf(stderr, "shutting down!\n"); + fprintf(stderr, "info: shutting down!\n"); gsr::deinit_theme(); window.close(); -- cgit v1.2.3