From d2f6b0738bdd90a5503fff404dfa4f0ad0962ef3 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Thu, 14 Nov 2024 00:25:37 +0100 Subject: Add option to start replay on fullscreen --- src/Config.cpp | 2 +- src/Overlay.cpp | 149 ++++++++++++++++++++++++++++++++++++++++++---- src/gui/LineSeparator.cpp | 2 +- src/gui/RadioButton.cpp | 47 ++++++++++++--- src/gui/SettingsPage.cpp | 25 ++++---- src/main.cpp | 2 +- 6 files changed, 194 insertions(+), 33 deletions(-) (limited to 'src') diff --git a/src/Config.cpp b/src/Config.cpp index 3a0b70b..29b82cf 100644 --- a/src/Config.cpp +++ b/src/Config.cpp @@ -132,7 +132,7 @@ namespace gsr { {"replay.record_options.overclock", &config.replay_config.record_options.overclock}, {"replay.record_options.record_cursor", &config.replay_config.record_options.record_cursor}, {"replay.record_options.restore_portal_session", &config.replay_config.record_options.restore_portal_session}, - {"replay.start_replay_automatically", &config.replay_config.start_replay_automatically}, + {"replay.turn_on_replay_automatically_mode", &config.replay_config.turn_on_replay_automatically_mode}, {"replay.save_video_in_game_folder", &config.replay_config.save_video_in_game_folder}, {"replay.show_replay_started_notifications", &config.replay_config.show_replay_started_notifications}, {"replay.show_replay_stopped_notifications", &config.replay_config.show_replay_stopped_notifications}, diff --git a/src/Overlay.cpp b/src/Overlay.cpp index e6a218c..8cd4bb9 100644 --- a/src/Overlay.cpp +++ b/src/Overlay.cpp @@ -30,6 +30,66 @@ extern "C" { namespace gsr { static const mgl::Color bg_color(0, 0, 0, 100); static const double force_window_on_top_timeout_seconds = 1.0; + static const double focused_window_fullscreen_check_timeout_seconds = 1.0; + + static bool window_has_atom(Display *dpy, Window window, Atom atom) { + Atom type; + unsigned long len, bytes_left; + int format; + unsigned char *properties = NULL; + if(XGetWindowProperty(dpy, window, atom, 0, 1024, False, AnyPropertyType, &type, &format, &len, &bytes_left, &properties) < Success) + return false; + + if(properties) + XFree(properties); + + return type != None; + } + + static bool window_is_user_program(Display *dpy, Window window) { + const Atom net_wm_state_atom = XInternAtom(dpy, "_NET_WM_STATE", False); + const Atom wm_state_atom = XInternAtom(dpy, "WM_STATE", False); + return window_has_atom(dpy, window, net_wm_state_atom) || window_has_atom(dpy, window, wm_state_atom); + } + + static Window get_window_at_cursor_position(Display *dpy) { + Window root_window = None; + Window window = None; + int dummy_i; + unsigned int dummy_u; + int cursor_pos_x = 0; + int cursor_pos_y = 0; + XQueryPointer(dpy, DefaultRootWindow(dpy), &root_window, &window, &dummy_i, &dummy_i, &cursor_pos_x, &cursor_pos_y, &dummy_u); + return window; + } + + static Window get_focused_window(Display *dpy) { + const Atom net_active_window_atom = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False); + Window focused_window = None; + + // Atom type = None; + // int format = 0; + // unsigned long num_items = 0; + // unsigned long bytes_left = 0; + // unsigned char *data = NULL; + // XGetWindowProperty(dpy, DefaultRootWindow(dpy), net_active_window_atom, 0, 1, False, XA_WINDOW, &type, &format, &num_items, &bytes_left, &data); + + // fprintf(stderr, "focused window: %p\n", (void*)data); + + // if(type == XA_WINDOW && num_items == 1 && data) + // return *(Window*)data; + + int revert_to = 0; + XGetInputFocus(dpy, &focused_window, &revert_to); + if(focused_window && focused_window != DefaultRootWindow(dpy) && window_is_user_program(dpy, focused_window)) + return focused_window; + + focused_window = get_window_at_cursor_position(dpy); + if(focused_window && focused_window != DefaultRootWindow(dpy) && window_is_user_program(dpy, focused_window)) + return focused_window; + + return None; + } static mgl::Texture texture_from_ximage(XImage *img) { uint8_t *texture_data = (uint8_t*)malloc(img->width * img->height * 3); @@ -82,17 +142,6 @@ namespace gsr { return result; } - static Window get_window_at_cursor_position(Display *display) { - Window root_window = None; - Window window = None; - int dummy_i; - unsigned int dummy_u; - int cursor_pos_x = 0; - int cursor_pos_y = 0; - XQueryPointer(display, DefaultRootWindow(display), &root_window, &window, &dummy_i, &dummy_i, &cursor_pos_x, &cursor_pos_y, &dummy_u); - return window; - } - struct DrawableGeometry { int x, y, width, height; }; @@ -130,6 +179,57 @@ namespace gsr { && diff_int(geometry.width, monitor->size.x, margin) && diff_int(geometry.height, monitor->size.y, margin); } + /*static bool is_window_fullscreen_on_monitor(Display *display, Window window, const mgl_monitor *monitors, int num_monitors) { + if(!window) + return false; + + DrawableGeometry geometry; + if(!get_drawable_geometry(display, window, &geometry)) + return false; + + const int margin = 2; + for(int i = 0; i < num_monitors; ++i) { + const mgl_monitor *mon = &monitors[i]; + if(diff_int(geometry.x, mon->pos.x, margin) && diff_int(geometry.y, mon->pos.y, margin) + && diff_int(geometry.width, mon->size.x, margin) && diff_int(geometry.height, mon->size.y, margin)) + { + return true; + } + } + + return false; + }*/ + + static bool window_is_fullscreen(Display *display, Window window) { + const Atom wm_state_atom = XInternAtom(display, "_NET_WM_STATE", False); + const Atom wm_state_fullscreen_atom = XInternAtom(display, "_NET_WM_STATE_FULLSCREEN", False); + + Atom type = None; + int format = 0; + unsigned long num_items = 0; + unsigned long bytes_after = 0; + unsigned char *properties = nullptr; + if(XGetWindowProperty(display, window, wm_state_atom, 0, 1024, False, XA_ATOM, &type, &format, &num_items, &bytes_after, &properties) < Success) { + fprintf(stderr, "Failed to get window wm state property\n"); + return false; + } + + if(!properties) + return false; + + bool is_fullscreen = false; + Atom *atoms = (Atom*)properties; + for(unsigned long i = 0; i < num_items; ++i) { + if(atoms[i] == wm_state_fullscreen_atom) { + is_fullscreen = true; + break; + } + } + + XFree(properties); + return is_fullscreen; + } + static void set_focused_window(Display *dpy, Window window) { XSetInputFocus(dpy, window, RevertToParent, CurrentTime); @@ -220,7 +320,7 @@ namespace gsr { const std::string notify_bg_color_str = color_to_hex_str(get_color_theme().tint_color); setenv("GSR_NOTIFY_BG_COLOR", notify_bg_color_str.c_str(), true); - if(config.replay_config.start_replay_automatically) + if(config.replay_config.turn_on_replay_automatically_mode == "turn_on_at_system_startup") on_press_start_replay(true); } @@ -289,6 +389,7 @@ namespace gsr { bool Overlay::draw() { update_notification_process_status(); update_gsr_process_status(); + update_focused_fullscreen_status(); if(!visible) return false; @@ -753,6 +854,30 @@ namespace gsr { recording_status = RecordingStatus::NONE; } + void Overlay::update_focused_fullscreen_status() { + if(focused_fullscreen_clock.get_elapsed_time_seconds() < focused_window_fullscreen_check_timeout_seconds) + return; + + focused_fullscreen_clock.restart(); + if(config.replay_config.turn_on_replay_automatically_mode != "turn_on_at_fullscreen") + return; + + mgl_context *context = mgl_get_context(); + Display *display = (Display*)context->connection; + + const Window focused_window = get_focused_window(display); + if(window && focused_window == window->get_system_handle()) + return; + + if(recording_status == RecordingStatus::NONE) { + if(focused_window != 0 && window_is_fullscreen(display, focused_window)) + on_press_start_replay(false); + } else if(recording_status == RecordingStatus::REPLAY) { + if(focused_window == 0 || !window_is_fullscreen(display, focused_window)) + on_press_start_replay(false); + } + } + void Overlay::update_ui_recording_paused() { if(!visible || recording_status != RecordingStatus::RECORD) return; diff --git a/src/gui/LineSeparator.cpp b/src/gui/LineSeparator.cpp index 637c84f..040a75a 100644 --- a/src/gui/LineSeparator.cpp +++ b/src/gui/LineSeparator.cpp @@ -16,7 +16,7 @@ namespace gsr { }; } - LineSeparator::LineSeparator(Type type, float width) : type(type), width(width) { + LineSeparator::LineSeparator(Orientation orientation, float width) : orientation(orientation), width(width) { } diff --git a/src/gui/RadioButton.cpp b/src/gui/RadioButton.cpp index 8c937c6..061d811 100644 --- a/src/gui/RadioButton.cpp +++ b/src/gui/RadioButton.cpp @@ -14,7 +14,7 @@ namespace gsr { static const float spacing_scale = 0.007f; static const float border_scale = 0.0015f; - RadioButton::RadioButton(mgl::Font *font) : font(font) { + RadioButton::RadioButton(mgl::Font *font, Orientation orientation) : font(font), orientation(orientation) { } @@ -44,7 +44,14 @@ namespace gsr { return false; } - draw_pos.x += item_size.x + spacing_scale * get_theme().window_height; + switch(orientation) { + case Orientation::VERTICAL: + draw_pos.y += item_size.y + spacing_scale * get_theme().window_height; + break; + case Orientation::HORIZONTAL: + draw_pos.x += item_size.x + spacing_scale * get_theme().window_height; + break; + } } } return true; @@ -85,7 +92,14 @@ namespace gsr { item.text.set_position((draw_pos + item_size * 0.5f - item.text.get_bounds().size * 0.5f).floor()); window.draw(item.text); - draw_pos.x += item_size.x + spacing_scale * get_theme().window_height; + switch(orientation) { + case Orientation::VERTICAL: + draw_pos.y += item_size.y + spacing_scale * get_theme().window_height; + break; + case Orientation::HORIZONTAL: + draw_pos.x += item_size.x + spacing_scale * get_theme().window_height; + break; + } } } @@ -98,13 +112,32 @@ namespace gsr { const int padding_left = padding_left_scale * get_theme().window_height; const int padding_right = padding_right_scale * get_theme().window_height; - size = { 0.0f, font->get_character_size() + (float)padding_top + (float)padding_bottom }; + size = { 0.0f, 0.0f }; for(Item &item : items) { const mgl::vec2f bounds = item.text.get_bounds().size; - size.x += bounds.x + padding_left + padding_right; + switch(orientation) { + case Orientation::VERTICAL: + size.x = std::max(size.x, bounds.x + padding_left + padding_right); + size.y += bounds.y + padding_top + padding_bottom; + break; + case Orientation::HORIZONTAL: + size.x += bounds.x + padding_left + padding_right; + size.y = font->get_character_size() + (float)padding_top + (float)padding_bottom; + break; + } } - if(items.size() > 1) - size.x += (items.size() - 1) * spacing_scale * get_theme().window_height; + + if(items.size() > 1) { + switch(orientation) { + case Orientation::VERTICAL: + size.y += (items.size() - 1) * spacing_scale * get_theme().window_height; + break; + case Orientation::HORIZONTAL: + size.x += (items.size() - 1) * spacing_scale * get_theme().window_height; + break; + } + } + dirty = false; } diff --git a/src/gui/SettingsPage.cpp b/src/gui/SettingsPage.cpp index 083fec5..7f50363 100644 --- a/src/gui/SettingsPage.cpp +++ b/src/gui/SettingsPage.cpp @@ -42,7 +42,7 @@ namespace gsr { } std::unique_ptr SettingsPage::create_view_radio_button() { - auto view_radio_button = std::make_unique(&get_theme().body_font); + auto view_radio_button = std::make_unique(&get_theme().body_font, RadioButton::Orientation::HORIZONTAL); view_radio_button->add_item("Simple view", "simple"); view_radio_button->add_item("Advanced view", "advanced"); view_radio_button->set_horizontal_alignment(Widget::Alignment::CENTER); @@ -228,7 +228,7 @@ namespace gsr { } std::unique_ptr SettingsPage::create_audio_type_button() { - auto audio_type_radio_button = std::make_unique(&get_theme().body_font); + auto audio_type_radio_button = std::make_unique(&get_theme().body_font, RadioButton::Orientation::HORIZONTAL); audio_type_radio_button->add_item("Audio devices", "audio_devices"); audio_type_radio_button->add_item("Application audio", "app_audio"); audio_type_radio_button_ptr = audio_type_radio_button.get(); @@ -321,10 +321,10 @@ namespace gsr { auto audio_section = std::make_unique("Audio", std::move(audio_device_section_list), mgl::vec2f(settings_scrollable_page_ptr->get_inner_size().x, 0.0f)); audio_device_section_list_ptr->add_widget(create_audio_type_button()); - audio_device_section_list_ptr->add_widget(std::make_unique(LineSeparator::Type::HORIZONTAL, audio_section->get_inner_size().x)); + audio_device_section_list_ptr->add_widget(std::make_unique(LineSeparator::Orientation::HORIZONTAL, audio_section->get_inner_size().x)); audio_device_section_list_ptr->add_widget(create_audio_device_section()); audio_device_section_list_ptr->add_widget(create_application_audio_section()); - //audio_device_section_list_ptr->add_widget(std::make_unique(LineSeparator::Type::HORIZONTAL, audio_section->get_inner_size().x)); + //audio_device_section_list_ptr->add_widget(std::make_unique(LineSeparator::Orientation::HORIZONTAL, audio_section->get_inner_size().x)); audio_device_section_list_ptr->add_widget(create_merge_audio_tracks_checkbox()); audio_device_section_list_ptr->add_widget(create_audio_codec()); return audio_section; @@ -646,10 +646,13 @@ namespace gsr { return replay_time_list; } - std::unique_ptr SettingsPage::create_start_replay_on_startup() { - auto checkbox = std::make_unique(&get_theme().body_font, "Turn on replay automatically"); - start_replay_automatically_ptr = checkbox.get(); - return checkbox; + std::unique_ptr SettingsPage::create_start_replay_automatically() { + auto radiobutton = std::make_unique(&get_theme().body_font, RadioButton::Orientation::VERTICAL); + radiobutton->add_item("Don't turn on replay automatically", "dont_turn_on_automatically"); + radiobutton->add_item("Turn on replay at system startup", "turn_on_at_system_startup"); + radiobutton->add_item("Turn on replay when starting a fullscreen application", "turn_on_at_fullscreen"); + turn_on_replay_automatically_mode_ptr = radiobutton.get(); + return radiobutton; } std::unique_ptr SettingsPage::create_save_replay_in_game_folder() { @@ -685,7 +688,7 @@ namespace gsr { settings_list_ptr->add_widget(std::make_unique("File info", std::move(file_info_list), mgl::vec2f(settings_scrollable_page_ptr->get_inner_size().x, 0.0f))); auto general_list = std::make_unique(List::Orientation::VERTICAL); - general_list->add_widget(create_start_replay_on_startup()); + general_list->add_widget(create_start_replay_automatically()); general_list->add_widget(create_save_replay_in_game_folder()); settings_list_ptr->add_widget(std::make_unique("General", std::move(general_list), mgl::vec2f(settings_scrollable_page_ptr->get_inner_size().x, 0.0f))); @@ -1015,7 +1018,7 @@ namespace gsr { void SettingsPage::load_replay() { load_common(config.replay_config.record_options); - start_replay_automatically_ptr->set_checked(config.replay_config.start_replay_automatically); + turn_on_replay_automatically_mode_ptr->set_selected_item(config.replay_config.turn_on_replay_automatically_mode); save_replay_in_game_folder_ptr->set_checked(config.replay_config.save_video_in_game_folder); show_replay_started_notification_checkbox_ptr->set_checked(config.replay_config.show_replay_started_notifications); show_replay_stopped_notification_checkbox_ptr->set_checked(config.replay_config.show_replay_stopped_notifications); @@ -1142,7 +1145,7 @@ namespace gsr { void SettingsPage::save_replay() { save_common(config.replay_config.record_options); - config.replay_config.start_replay_automatically = start_replay_automatically_ptr->is_checked(); + config.replay_config.turn_on_replay_automatically_mode = turn_on_replay_automatically_mode_ptr->get_selected_id(); config.replay_config.save_video_in_game_folder = save_replay_in_game_folder_ptr->is_checked(); config.replay_config.show_replay_started_notifications = show_replay_started_notification_checkbox_ptr->is_checked(); config.replay_config.show_replay_stopped_notifications = show_replay_stopped_notification_checkbox_ptr->is_checked(); diff --git a/src/main.cpp b/src/main.cpp index 16152cc..c2b7ab0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -156,7 +156,7 @@ int main(void) { global_hotkeys.poll_events(); overlay->handle_events(); if(!overlay->draw()) - std::this_thread::sleep_for(std::chrono::milliseconds(50)); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); } fprintf(stderr, "info: shutting down!\n"); -- cgit v1.2.3