diff options
-rw-r--r-- | README.md | 4 | ||||
-rw-r--r-- | TODO | 8 | ||||
-rw-r--r-- | include/GlobalHotkeys/GlobalHotkeysJoystick.hpp | 2 | ||||
-rw-r--r-- | meson.build | 4 | ||||
-rw-r--r-- | project.conf | 2 | ||||
-rw-r--r-- | src/GlobalHotkeys/GlobalHotkeysJoystick.cpp | 13 | ||||
-rw-r--r-- | src/Overlay.cpp | 105 | ||||
-rw-r--r-- | src/Process.cpp | 21 | ||||
-rw-r--r-- | src/main.cpp | 5 |
9 files changed, 133 insertions, 31 deletions
@@ -61,8 +61,8 @@ I'm looking for somebody that can create sound effects for the notifications.  # Known issues -* 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 fullscreen can mess up the game window a bit on Hyprland. This is an issue with Hyprland. +* 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. Change your waybar dock mode to "dock" in its config to fix this. +* Opening the UI when a game is fullscreen can mess up the game window a bit on Hyprland. This is an issue with Hyprland. Change your waybar dock mode to "dock" in its config to fix this. * The background of the UI is black when opening the UI while a Wayland application is focused on COSMIC. This is an issue with COSMIC. * Unable to close the region selection with escape key while a Wayland application is focused on COSMIC. This is an issue with COSMIC. @@ -179,3 +179,11 @@ Show message that replay/streaming has to be restarted if recording settings are Support vector graphics. Maybe support svg, rendering it to a texture for better performance. Support freetype for text rendering. Maybe load freetype as runtime (with dlopen) and use that when available and fallback to stb_freetype if not available. + +Show .webm container option. It's currently chosen automatically if vp8/vp9 is chosen. The available containers should automatically switch depending on the video codec. + +In settings show audio levels for each audio. Maybe show audio level image beside the audio name in the dropdown box and switch to a different image (have 3-4 different images for each level) depending on the volume. + +Only use fake cursor on wayland if the focused x11 window is fullscreen. + +Create window as a real overlay window, using layer shell protocol, when possible. This will however minimize windows on floating wms. Check if this can be fixed somehow, or only use layer shell in tiling wms.
\ No newline at end of file diff --git a/include/GlobalHotkeys/GlobalHotkeysJoystick.hpp b/include/GlobalHotkeys/GlobalHotkeysJoystick.hpp index 4b266cb..0177d29 100644 --- a/include/GlobalHotkeys/GlobalHotkeysJoystick.hpp +++ b/include/GlobalHotkeys/GlobalHotkeysJoystick.hpp @@ -56,6 +56,8 @@ namespace gsr { bool down_pressed = false; bool left_pressed = false; bool right_pressed = false; + bool l3_button_pressed = false; + bool r3_button_pressed = false; bool save_replay = false; bool save_1_min_replay = false; diff --git a/meson.build b/meson.build index 45e15bb..d4d1fad 100644 --- a/meson.build +++ b/meson.build @@ -1,4 +1,4 @@ -project('gsr-ui', ['c', 'cpp'], version : '1.6.6', default_options : ['warning_level=2', 'cpp_std=c++17'], subproject_dir : 'depends') +project('gsr-ui', ['c', 'cpp'], version : '1.6.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']) @@ -62,7 +62,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.6.0"', language: ['c', 'cpp']) +add_project_arguments('-DGSR_FLATPAK_VERSION="5.7.0"', language: ['c', 'cpp']) executable( meson.project_name(), diff --git a/project.conf b/project.conf index 79059a9..54f9172 100644 --- a/project.conf +++ b/project.conf @@ -1,7 +1,7 @@ [package] name = "gsr-ui" type = "executable" -version = "1.6.6" +version = "1.6.7" platforms = ["posix"] [lang.cpp] diff --git a/src/GlobalHotkeys/GlobalHotkeysJoystick.cpp b/src/GlobalHotkeys/GlobalHotkeysJoystick.cpp index 23f8a20..5969438 100644 --- a/src/GlobalHotkeys/GlobalHotkeysJoystick.cpp +++ b/src/GlobalHotkeys/GlobalHotkeysJoystick.cpp @@ -11,6 +11,8 @@ namespace gsr { static constexpr int triangle_button = 2; static constexpr int options_button = 9; static constexpr int playstation_button = 10; + static constexpr int l3_button = 11; + static constexpr int r3_button = 12; static constexpr int axis_up_down = 7; static constexpr int axis_left_right = 6; @@ -266,7 +268,8 @@ namespace gsr { if((event.type & JS_EVENT_BUTTON) == JS_EVENT_BUTTON) { switch(event.number) { case playstation_button: { - playstation_button_pressed = event.value == button_pressed; + // Workaround weird steam input (in-game) behavior where steam triggers playstation button + options when pressing both l3 and r3 at the same time + playstation_button_pressed = (event.value == button_pressed) && !l3_button_pressed && !r3_button_pressed; break; } case options_button: { @@ -284,6 +287,14 @@ namespace gsr { save_10_min_replay = true; break; } + case l3_button: { + l3_button_pressed = event.value == button_pressed; + break; + } + case r3_button: { + r3_button_pressed = event.value == button_pressed; + break; + } } } else if((event.type & JS_EVENT_AXIS) == JS_EVENT_AXIS && playstation_button_pressed) { const int trigger_threshold = 16383; diff --git a/src/Overlay.cpp b/src/Overlay.cpp index 170759a..dbf71c7 100644 --- a/src/Overlay.cpp +++ b/src/Overlay.cpp @@ -273,6 +273,33 @@ namespace gsr { return true; } + static bool is_hyprland_waybar_running_as_dock() { + const char *args[] = { "hyprctl", "layers", nullptr }; + std::string stdout_str; + if(exec_program_on_host_get_stdout(args, stdout_str) != 0) + return false; + + int waybar_layer_level = -1; + int current_layer_level = 0; + string_split_char(stdout_str, '\n', [&](const std::string_view line) { + if(line.find("Layer level 0") != std::string_view::npos) + current_layer_level = 0; + else if(line.find("Layer level 1") != std::string_view::npos) + current_layer_level = 1; + else if(line.find("Layer level 2") != std::string_view::npos) + current_layer_level = 2; + else if(line.find("Layer level 3") != std::string_view::npos) + current_layer_level = 3; + else if(line.find("namespace: waybar") != std::string_view::npos) { + waybar_layer_level = current_layer_level; + return false; + } + return true; + }); + + return waybar_layer_level >= 0 && waybar_layer_level <= 1; + } + static Hotkey config_hotkey_to_hotkey(ConfigHotkey config_hotkey) { return { (uint32_t)mgl::Keyboard::key_to_x11_keysym((mgl::Keyboard::Key)config_hotkey.key), @@ -904,6 +931,8 @@ namespace gsr { 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; + const bool is_hyprland = wm_name.find("Hyprland") != std::string::npos; + const bool hyprland_waybar_is_dock = is_hyprland && is_hyprland_waybar_running_as_dock(); std::optional<CursorInfo> cursor_info; if(cursor_tracker) { @@ -931,7 +960,7 @@ namespace gsr { // 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; + const bool prevent_game_minimizing = gsr_info.system_info.display_server != DisplayServer::WAYLAND || x11_cursor_window || is_wlroots || is_hyprland; if(prevent_game_minimizing) { window_pos = focused_monitor->position; @@ -1035,7 +1064,7 @@ namespace gsr { // Owlboy seems to use xi events and XGrabPointer doesn't prevent owlboy from receiving events. xi_grab_all_mouse_devices(xi_display); - if(!is_wlroots) + if(!is_wlroots && !hyprland_waybar_is_dock) window->set_fullscreen(true); visible = true; @@ -2328,6 +2357,48 @@ namespace gsr { kill(gpu_screen_recorder_process, SIGRTMIN+5); } + static const char* switch_video_codec_to_usable_hardware_encoder(const GsrInfo &gsr_info) { + if(gsr_info.supported_video_codecs.h264) + return "h264"; + else if(gsr_info.supported_video_codecs.hevc) + return "hevc"; + else if(gsr_info.supported_video_codecs.av1) + return "av1"; + else if(gsr_info.supported_video_codecs.vp8) + return "vp8"; + else if(gsr_info.supported_video_codecs.vp9) + return "vp9"; + return nullptr; + } + + static const char* change_container_if_codec_not_supported(const char *video_codec, const char *container) { + if(strcmp(video_codec, "vp8") == 0 || strcmp(video_codec, "vp9") == 0) { + if(strcmp(container, "webm") != 0 && strcmp(container, "matroska") != 0) { + fprintf(stderr, "Warning: container '%s' is not compatible with video codec '%s', using webm container instead\n", container, video_codec); + return "webm"; + } + } else if(strcmp(container, "webm") == 0) { + fprintf(stderr, "Warning: container webm is not compatible with video codec '%s', using mp4 container instead\n", video_codec); + return "mp4"; + } + return container; + } + + static void choose_video_codec_and_container_with_fallback(const GsrInfo &gsr_info, const char **video_codec, const char **container, const char **encoder) { + *encoder = "gpu"; + if(strcmp(*video_codec, "h264_software") == 0) { + *video_codec = "h264"; + *encoder = "cpu"; + } else if(strcmp(*video_codec, "auto") == 0) { + *video_codec = switch_video_codec_to_usable_hardware_encoder(gsr_info); + if(!*video_codec) { + *video_codec = "h264"; + *encoder = "cpu"; + } + } + *container = change_container_if_codec_not_supported(*video_codec, *container); + } + bool Overlay::on_press_start_replay(bool disable_notification, bool finished_selection) { if(region_selector.is_started() || window_selector.is_started()) return false; @@ -2402,12 +2473,10 @@ namespace gsr { 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 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(); const char *encoder = "gpu"; - if(strcmp(video_codec, "h264_software") == 0) { - video_codec = "h264"; - encoder = "cpu"; - } + choose_video_codec_and_container_with_fallback(gsr_info, &video_codec, &container, &encoder); char size[64]; size[0] = '\0'; @@ -2419,7 +2488,7 @@ namespace gsr { std::vector<const char*> args = { "gpu-screen-recorder", "-w", recording_capture_target.c_str(), - "-c", config.replay_config.container.c_str(), + "-c", container, "-ac", config.replay_config.record_options.audio_codec.c_str(), "-cursor", config.replay_config.record_options.record_cursor ? "yes" : "no", "-cr", config.replay_config.record_options.color_range.c_str(), @@ -2582,12 +2651,10 @@ namespace gsr { 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 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"; - if(strcmp(video_codec, "h264_software") == 0) { - video_codec = "h264"; - encoder = "cpu"; - } + choose_video_codec_and_container_with_fallback(gsr_info, &video_codec, &container, &encoder); char size[64]; size[0] = '\0'; @@ -2599,7 +2666,7 @@ namespace gsr { std::vector<const char*> args = { "gpu-screen-recorder", "-w", recording_capture_target.c_str(), - "-c", config.record_config.container.c_str(), + "-c", container, "-ac", config.record_config.record_options.audio_codec.c_str(), "-cursor", config.record_config.record_options.record_cursor ? "yes" : "no", "-cr", config.record_config.record_options.color_range.c_str(), @@ -2748,16 +2815,12 @@ namespace gsr { 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 char *container = "flv"; + if(config.streaming_config.streaming_service == "custom") + container = config.streaming_config.custom.container.c_str(); const char *video_codec = config.streaming_config.record_options.video_codec.c_str(); const char *encoder = "gpu"; - if(strcmp(video_codec, "h264_software") == 0) { - video_codec = "h264"; - encoder = "cpu"; - } - - std::string container = "flv"; - if(config.streaming_config.streaming_service == "custom") - container = config.streaming_config.custom.container; + choose_video_codec_and_container_with_fallback(gsr_info, &video_codec, &container, &encoder); const std::string url = streaming_get_url(config); @@ -2771,7 +2834,7 @@ namespace gsr { std::vector<const char*> args = { "gpu-screen-recorder", "-w", recording_capture_target.c_str(), - "-c", container.c_str(), + "-c", container, "-ac", config.streaming_config.record_options.audio_codec.c_str(), "-cursor", config.streaming_config.record_options.record_cursor ? "yes" : "no", "-cr", config.streaming_config.record_options.color_range.c_str(), diff --git a/src/Process.cpp b/src/Process.cpp index 45be208..c02753a 100644 --- a/src/Process.cpp +++ b/src/Process.cpp @@ -176,11 +176,21 @@ namespace gsr { } } + static const char *get_basename(const char *path, int size) { + for(int i = size - 1; i >= 0; --i) { + if(path[i] == '/') + return path + i + 1; + } + return path; + } + // |output_buffer| should be at least PATH_MAX in size bool read_cmdline_arg0(const char *filepath, char *output_buffer, int output_buffer_size) { output_buffer[0] = '\0'; + const char *arg0_start = NULL; const char *arg0_end = NULL; + int arg0_size = 0; int fd = open(filepath, O_RDONLY); if(fd == -1) return false; @@ -190,13 +200,16 @@ namespace gsr { if(bytes_read == -1) goto err; - arg0_end = (const char*)memchr(buffer, '\0', bytes_read); + arg0_start = buffer; + arg0_end = (const char*)memchr(arg0_start, '\0', bytes_read); if(!arg0_end) goto err; - if((arg0_end - buffer) + 1 <= output_buffer_size) { - memcpy(output_buffer, buffer, arg0_end - buffer); - output_buffer[arg0_end - buffer] = '\0'; + arg0_start = get_basename(arg0_start, arg0_end - arg0_start); + arg0_size = arg0_end - arg0_start; + if(arg0_size + 1 <= output_buffer_size) { + memcpy(output_buffer, arg0_start, arg0_size); + output_buffer[arg0_size] = '\0'; close(fd); return true; } diff --git a/src/main.cpp b/src/main.cpp index 31ec8ff..a68ff7d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -240,6 +240,11 @@ int main(int argc, char **argv) { return 1; } + if(gsr::pidof("gpu-screen-recorder", getpid()) != -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); + } + if(is_flatpak()) install_flatpak_systemd_service(); else |