mirror of
https://repo.dec05eba.com/gpu-screen-recorder-ui
synced 2026-04-08 12:24:52 +09:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0269387b9a | ||
|
|
5c4ebbab59 | ||
|
|
40b2af5668 | ||
|
|
aa717a95ec | ||
|
|
86424607b7 | ||
|
|
74bb6f0070 | ||
|
|
61bbaf3728 | ||
|
|
fed47000ce | ||
|
|
7f43adfbd5 | ||
|
|
1f6251baf3 | ||
|
|
d1220b013e |
6
TODO
6
TODO
@@ -250,6 +250,8 @@ Sometimes when opening gpu screen recorder ui gsr-global-hotkeys incorrectly det
|
||||
|
||||
When running replay for a long time and then stopping it it takes a while. Improve this.
|
||||
|
||||
When adding webcamera make replay auto start wait for camera to be available (when /dev/video device exists and can be initialized), just like audio device.
|
||||
Make it possible to resize webcam box from top left, top right and bottom left as well.
|
||||
|
||||
Make it possible to resize webcam box from top left, top right and bottom left as well.
|
||||
The flatpak version can for some get stuck at shutdown when instant replay is running. It only happens in the flatpak version and only when instant replay is running and it happens always. Manual SIGINT on gsr-ui stops gsr-ui properly, so why does it fail when shutting down the computer when the systemd stop signal is SIGINT? Maybe its related to the flatpak version being launched through gsr-gtk. I cant personally reproduce it.
|
||||
|
||||
Redesign the UI to allow capturing multiple video sources. Move webcam to capture sources as well then. Maybe design the UI to work more like obs studio then, where you start recording and then add sources at capture time, with a preview.
|
||||
|
||||
BIN
images/info.png
Normal file
BIN
images/info.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
BIN
images/question_mark.png
Normal file
BIN
images/question_mark.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
BIN
images/warning.png
Normal file
BIN
images/warning.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.1 KiB |
@@ -60,10 +60,14 @@ namespace gsr {
|
||||
bool overclock = false;
|
||||
bool record_cursor = true;
|
||||
bool restore_portal_session = true;
|
||||
bool low_power_mode = false;
|
||||
|
||||
std::string webcam_source = "";
|
||||
bool webcam_flip_horizontally = false;
|
||||
std::string webcam_video_format = "auto";
|
||||
int32_t webcam_camera_width = 0;
|
||||
int32_t webcam_camera_height = 0;
|
||||
int32_t webcam_camera_fps = 0;
|
||||
int32_t webcam_x = 0; // A value between 0 and 100 (percentage)
|
||||
int32_t webcam_y = 0; // A value between 0 and 100 (percentage)
|
||||
int32_t webcam_width = 30; // A value between 0 and 100 (percentage), 0 = Don't scale it
|
||||
@@ -96,6 +100,11 @@ namespace gsr {
|
||||
std::string stream_key;
|
||||
};
|
||||
|
||||
struct KickStreamConfig {
|
||||
std::string stream_url;
|
||||
std::string stream_key;
|
||||
};
|
||||
|
||||
struct CustomStreamConfig {
|
||||
std::string url;
|
||||
std::string key;
|
||||
@@ -108,6 +117,7 @@ namespace gsr {
|
||||
YoutubeStreamConfig youtube;
|
||||
TwitchStreamConfig twitch;
|
||||
RumbleStreamConfig rumble;
|
||||
KickStreamConfig kick;
|
||||
CustomStreamConfig custom;
|
||||
ConfigHotkey start_stop_hotkey;
|
||||
};
|
||||
|
||||
@@ -25,9 +25,9 @@ namespace gsr {
|
||||
bool png = false;
|
||||
};
|
||||
|
||||
struct SupportedCameraPixelFormats {
|
||||
bool yuyv = false;
|
||||
bool mjpeg = false;
|
||||
enum GsrCameraPixelFormat {
|
||||
YUYV,
|
||||
MJPEG
|
||||
};
|
||||
|
||||
struct GsrMonitor {
|
||||
@@ -35,10 +35,16 @@ namespace gsr {
|
||||
mgl::vec2i size;
|
||||
};
|
||||
|
||||
struct GsrCameraSetup {
|
||||
mgl::vec2i resolution;
|
||||
int fps;
|
||||
//GsrCameraPixelFormat pixel_format;
|
||||
};
|
||||
|
||||
struct GsrCamera {
|
||||
std::string path;
|
||||
mgl::vec2i size;
|
||||
SupportedCameraPixelFormats supported_pixel_formats;
|
||||
std::vector<GsrCameraSetup> yuyv_setups;
|
||||
std::vector<GsrCameraSetup> mjpeg_setups;
|
||||
};
|
||||
|
||||
struct GsrVersion {
|
||||
|
||||
@@ -108,6 +108,7 @@ namespace gsr {
|
||||
void on_event(mgl::Event &event);
|
||||
|
||||
void recreate_global_hotkeys(const char *hotkey_option);
|
||||
void update_led_indicator_after_settings_change();
|
||||
void create_frontpage_ui_components();
|
||||
void xi_setup();
|
||||
void handle_xi_events();
|
||||
|
||||
@@ -47,6 +47,9 @@ namespace gsr {
|
||||
mgl::Texture trash_texture;
|
||||
mgl::Texture masked_texture;
|
||||
mgl::Texture unmasked_texture;
|
||||
mgl::Texture warning_texture;
|
||||
mgl::Texture info_texture;
|
||||
mgl::Texture question_mark_texture;
|
||||
|
||||
mgl::Texture ps4_home_texture;
|
||||
mgl::Texture ps4_options_texture;
|
||||
|
||||
@@ -18,7 +18,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 add_item(const std::string &text, const std::string &id);
|
||||
void add_item(const std::string &text, const std::string &id, bool allow_duplicate = true);
|
||||
void clear_items();
|
||||
|
||||
// The item can only be selected if it's enabled
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "Widget.hpp"
|
||||
|
||||
#include <mglpp/graphics/Sprite.hpp>
|
||||
#include <functional>
|
||||
|
||||
namespace gsr {
|
||||
class Image : public Widget {
|
||||
@@ -21,6 +22,8 @@ namespace gsr {
|
||||
void draw(mgl::Window &window, mgl::vec2f offset) override;
|
||||
|
||||
mgl::vec2f get_size() override;
|
||||
|
||||
std::function<void(bool inside)> on_mouse_move;
|
||||
private:
|
||||
mgl::Sprite sprite;
|
||||
mgl::vec2f size;
|
||||
|
||||
@@ -23,6 +23,8 @@ namespace gsr {
|
||||
void load();
|
||||
void save();
|
||||
void on_navigate_away_from_page() override;
|
||||
|
||||
std::function<void()> on_config_changed;
|
||||
private:
|
||||
std::unique_ptr<ComboBox> create_record_area_box();
|
||||
std::unique_ptr<Widget> create_record_area();
|
||||
|
||||
@@ -67,7 +67,9 @@ namespace gsr {
|
||||
std::unique_ptr<Widget> create_change_video_resolution_section();
|
||||
std::unique_ptr<Widget> create_capture_target_section();
|
||||
std::unique_ptr<List> create_webcam_sources();
|
||||
std::unique_ptr<List> create_webcam_video_setups();
|
||||
std::unique_ptr<List> create_webcam_video_format();
|
||||
std::unique_ptr<List> create_webcam_video_setup_list();
|
||||
std::unique_ptr<Widget> create_webcam_location_widget();
|
||||
std::unique_ptr<CheckBox> create_flip_camera_checkbox();
|
||||
std::unique_ptr<List> create_webcam_body();
|
||||
@@ -76,6 +78,7 @@ namespace gsr {
|
||||
std::unique_ptr<Button> create_remove_audio_device_button(List *audio_input_list_ptr, List *audio_device_list_ptr);
|
||||
std::unique_ptr<List> create_audio_device(AudioDeviceType device_type, List *audio_input_list_ptr);
|
||||
std::unique_ptr<Button> create_add_audio_track_button();
|
||||
void update_application_audio_warning_visibility();
|
||||
std::unique_ptr<Button> create_add_audio_output_device_button(List *audio_input_list_ptr);
|
||||
std::unique_ptr<Button> create_add_audio_input_device_button(List *audio_input_list_ptr);
|
||||
std::unique_ptr<ComboBox> create_application_audio_selection_combobox(List *application_audio_row);
|
||||
@@ -85,6 +88,7 @@ namespace gsr {
|
||||
std::unique_ptr<List> create_add_audio_buttons(List *audio_input_list_ptr);
|
||||
std::unique_ptr<List> create_audio_input_section();
|
||||
std::unique_ptr<CheckBox> create_application_audio_invert_checkbox();
|
||||
std::unique_ptr<Widget> create_application_audio_warning();
|
||||
std::unique_ptr<List> create_audio_track_title_and_remove(Subsection *audio_track_subsection, const char *title);
|
||||
std::unique_ptr<Subsection> create_audio_track_section(Widget *parent_widget);
|
||||
std::unique_ptr<List> create_audio_track_section_list();
|
||||
@@ -129,6 +133,7 @@ namespace gsr {
|
||||
std::unique_ptr<CheckBox> create_led_indicator(const char *type);
|
||||
std::unique_ptr<CheckBox> create_notifications(const char *type);
|
||||
std::unique_ptr<List> create_indicator(const char *type);
|
||||
std::unique_ptr<Widget> create_low_power_mode();
|
||||
void add_replay_widgets();
|
||||
void add_record_widgets();
|
||||
|
||||
@@ -203,6 +208,8 @@ namespace gsr {
|
||||
Entry *twitch_stream_key_entry_ptr = nullptr;
|
||||
Entry *youtube_stream_key_entry_ptr = nullptr;
|
||||
Entry *rumble_stream_key_entry_ptr = nullptr;
|
||||
Entry *kick_stream_url_entry_ptr = nullptr;
|
||||
Entry *kick_stream_key_entry_ptr = nullptr;
|
||||
Entry *stream_url_entry_ptr = nullptr;
|
||||
Entry *stream_key_entry_ptr = nullptr;
|
||||
Entry *replay_time_entry_ptr = nullptr;
|
||||
@@ -214,9 +221,11 @@ namespace gsr {
|
||||
CheckBox *led_indicator_checkbox_ptr = nullptr;
|
||||
CheckBox *show_notification_checkbox_ptr = nullptr;
|
||||
ComboBox *webcam_sources_box_ptr = nullptr;
|
||||
ComboBox *webcam_video_setup_box_ptr = nullptr;
|
||||
ComboBox *webcam_video_format_box_ptr = nullptr;
|
||||
List *webcam_body_list_ptr = nullptr;
|
||||
CheckBox *flip_camera_horizontally_checkbox_ptr = nullptr;
|
||||
CheckBox *low_power_mode_checkbox_ptr = nullptr;
|
||||
|
||||
PageStack *page_stack = nullptr;
|
||||
|
||||
@@ -237,5 +246,6 @@ namespace gsr {
|
||||
mgl::vec2f webcam_box_size_resize_start;
|
||||
|
||||
std::optional<GsrCamera> selected_camera;
|
||||
std::optional<GsrCameraSetup> selected_camera_setup;
|
||||
};
|
||||
}
|
||||
22
include/gui/Tooltip.hpp
Normal file
22
include/gui/Tooltip.hpp
Normal file
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include "Widget.hpp"
|
||||
#include <mglpp/graphics/Text.hpp>
|
||||
|
||||
namespace gsr {
|
||||
class Tooltip : public Widget {
|
||||
public:
|
||||
Tooltip(mgl::Font *font);
|
||||
Tooltip(const Tooltip&) = delete;
|
||||
Tooltip& operator=(const Tooltip&) = delete;
|
||||
|
||||
bool on_event(mgl::Event &event, mgl::Window &window, mgl::vec2f offset) override;
|
||||
void draw(mgl::Window &window, mgl::vec2f offset) override;
|
||||
|
||||
mgl::vec2f get_size() override;
|
||||
|
||||
void set_text(std::string text);
|
||||
private:
|
||||
mgl::Text label;
|
||||
};
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <mglpp/system/vec.hpp>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace mgl {
|
||||
class Event;
|
||||
@@ -44,9 +45,14 @@ namespace gsr {
|
||||
Alignment get_vertical_alignment() const;
|
||||
|
||||
void set_visible(bool visible);
|
||||
bool is_visible() const;
|
||||
|
||||
Widget* get_parent_widget();
|
||||
|
||||
void set_tooltip_text(std::string text);
|
||||
const std::string& get_tooltip_text() const;
|
||||
void handle_tooltip_event(mgl::Event &event, mgl::vec2f position, mgl::vec2f size);
|
||||
|
||||
void *userdata = nullptr;
|
||||
protected:
|
||||
void set_widget_as_selected_in_parent();
|
||||
@@ -61,8 +67,13 @@ namespace gsr {
|
||||
Alignment vertical_aligment = Alignment::START;
|
||||
|
||||
bool visible = true;
|
||||
std::string tooltip_text;
|
||||
};
|
||||
|
||||
void add_widget_to_remove(std::unique_ptr<Widget> widget);
|
||||
void remove_widgets_to_be_removed();
|
||||
|
||||
void set_current_tooltip(Widget *widget);
|
||||
void remove_as_current_tooltip(Widget *widget);
|
||||
void draw_tooltip(mgl::Window &window);
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
project('gsr-ui', ['c', 'cpp'], version : '1.9.2', default_options : ['warning_level=2', 'cpp_std=c++17'], subproject_dir : 'depends')
|
||||
project('gsr-ui', ['c', 'cpp'], version : '1.10.0', default_options : ['warning_level=2', 'cpp_std=c++17'], subproject_dir : 'depends')
|
||||
|
||||
add_project_arguments('-D_FILE_OFFSET_BITS=64', language : ['c', 'cpp'])
|
||||
|
||||
@@ -34,6 +34,7 @@ src = [
|
||||
'src/gui/GlobalSettingsPage.cpp',
|
||||
'src/gui/GsrPage.cpp',
|
||||
'src/gui/Subsection.cpp',
|
||||
'src/gui/Tooltip.cpp',
|
||||
'src/GlobalHotkeys/GlobalHotkeysX11.cpp',
|
||||
'src/GlobalHotkeys/GlobalHotkeysLinux.cpp',
|
||||
'src/GlobalHotkeys/GlobalHotkeysJoystick.cpp',
|
||||
@@ -67,7 +68,7 @@ gsr_ui_resources_path = join_paths(prefix, datadir, 'gsr-ui')
|
||||
icons_path = join_paths(prefix, datadir, 'icons')
|
||||
|
||||
add_project_arguments('-DGSR_UI_VERSION="' + meson.project_version() + '"', language: ['c', 'cpp'])
|
||||
add_project_arguments('-DGSR_FLATPAK_VERSION="5.11.3"', language: ['c', 'cpp'])
|
||||
add_project_arguments('-DGSR_FLATPAK_VERSION="5.12.0"', language: ['c', 'cpp'])
|
||||
|
||||
executable(
|
||||
meson.project_name(),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "gsr-ui"
|
||||
type = "executable"
|
||||
version = "1.9.2"
|
||||
version = "1.10.0"
|
||||
platforms = ["posix"]
|
||||
|
||||
[lang.cpp]
|
||||
|
||||
@@ -272,8 +272,10 @@ namespace gsr {
|
||||
}
|
||||
clipboard_copies.clear();
|
||||
|
||||
if(XGetSelectionOwner(dpy, clipboard_atom) == clipboard_window)
|
||||
if(XGetSelectionOwner(dpy, clipboard_atom) == clipboard_window) {
|
||||
XSetSelectionOwner(dpy, clipboard_atom, None, CurrentTime);
|
||||
XFlush(dpy);
|
||||
}
|
||||
|
||||
if(filepath.empty()) {
|
||||
// TODO: Cancel transfer
|
||||
|
||||
@@ -199,9 +199,13 @@ namespace gsr {
|
||||
{"streaming.record_options.overclock", &config.streaming_config.record_options.overclock},
|
||||
{"streaming.record_options.record_cursor", &config.streaming_config.record_options.record_cursor},
|
||||
{"streaming.record_options.restore_portal_session", &config.streaming_config.record_options.restore_portal_session},
|
||||
{"streaming.record_options.low_power_mode", &config.streaming_config.record_options.low_power_mode},
|
||||
{"streaming.record_options.webcam_source", &config.streaming_config.record_options.webcam_source},
|
||||
{"streaming.record_options.webcam_flip_horizontally", &config.streaming_config.record_options.webcam_flip_horizontally},
|
||||
{"streaming.record_options.webcam_video_format", &config.streaming_config.record_options.webcam_video_format},
|
||||
{"streaming.record_options.webcam_camera_width", &config.streaming_config.record_options.webcam_camera_width},
|
||||
{"streaming.record_options.webcam_camera_height", &config.streaming_config.record_options.webcam_camera_height},
|
||||
{"streaming.record_options.webcam_camera_fps", &config.streaming_config.record_options.webcam_camera_fps},
|
||||
{"streaming.record_options.webcam_x", &config.streaming_config.record_options.webcam_x},
|
||||
{"streaming.record_options.webcam_y", &config.streaming_config.record_options.webcam_y},
|
||||
{"streaming.record_options.webcam_width", &config.streaming_config.record_options.webcam_width},
|
||||
@@ -212,6 +216,8 @@ namespace gsr {
|
||||
{"streaming.youtube.key", &config.streaming_config.youtube.stream_key},
|
||||
{"streaming.twitch.key", &config.streaming_config.twitch.stream_key},
|
||||
{"streaming.rumble.key", &config.streaming_config.rumble.stream_key},
|
||||
{"streaming.kick.url", &config.streaming_config.kick.stream_url},
|
||||
{"streaming.kick.key", &config.streaming_config.kick.stream_key},
|
||||
{"streaming.custom.url", &config.streaming_config.custom.url},
|
||||
{"streaming.custom.key", &config.streaming_config.custom.key},
|
||||
{"streaming.custom.container", &config.streaming_config.custom.container},
|
||||
@@ -238,9 +244,13 @@ namespace gsr {
|
||||
{"record.record_options.overclock", &config.record_config.record_options.overclock},
|
||||
{"record.record_options.record_cursor", &config.record_config.record_options.record_cursor},
|
||||
{"record.record_options.restore_portal_session", &config.record_config.record_options.restore_portal_session},
|
||||
{"record.record_options.low_power_mode", &config.record_config.record_options.low_power_mode},
|
||||
{"record.record_options.webcam_source", &config.record_config.record_options.webcam_source},
|
||||
{"record.record_options.webcam_flip_horizontally", &config.record_config.record_options.webcam_flip_horizontally},
|
||||
{"record.record_options.webcam_video_format", &config.record_config.record_options.webcam_video_format},
|
||||
{"record.record_options.webcam_camera_width", &config.record_config.record_options.webcam_camera_width},
|
||||
{"record.record_options.webcam_camera_height", &config.record_config.record_options.webcam_camera_height},
|
||||
{"record.record_options.webcam_camera_fps", &config.record_config.record_options.webcam_camera_fps},
|
||||
{"record.record_options.webcam_x", &config.record_config.record_options.webcam_x},
|
||||
{"record.record_options.webcam_y", &config.record_config.record_options.webcam_y},
|
||||
{"record.record_options.webcam_width", &config.record_config.record_options.webcam_width},
|
||||
@@ -274,9 +284,13 @@ 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.record_options.low_power_mode", &config.replay_config.record_options.low_power_mode},
|
||||
{"replay.record_options.webcam_source", &config.replay_config.record_options.webcam_source},
|
||||
{"replay.record_options.webcam_flip_horizontally", &config.replay_config.record_options.webcam_flip_horizontally},
|
||||
{"replay.record_options.webcam_video_format", &config.replay_config.record_options.webcam_video_format},
|
||||
{"replay.record_options.webcam_camera_width", &config.replay_config.record_options.webcam_camera_width},
|
||||
{"replay.record_options.webcam_camera_height", &config.replay_config.record_options.webcam_camera_height},
|
||||
{"replay.record_options.webcam_camera_fps", &config.replay_config.record_options.webcam_camera_fps},
|
||||
{"replay.record_options.webcam_x", &config.replay_config.record_options.webcam_x},
|
||||
{"replay.record_options.webcam_y", &config.replay_config.record_options.webcam_y},
|
||||
{"replay.record_options.webcam_width", &config.replay_config.record_options.webcam_width},
|
||||
|
||||
@@ -368,8 +368,7 @@ namespace gsr {
|
||||
if(!properties)
|
||||
goto next_crtc;
|
||||
|
||||
if(!get_drm_property_by_name(drm_fd, properties, "VRR_ENABLED", &vrr_enabled))
|
||||
goto next_crtc;
|
||||
get_drm_property_by_name(drm_fd, properties, "VRR_ENABLED", &vrr_enabled);
|
||||
|
||||
connector = get_drm_connector_by_crtc_id(drm_connectors, crtc->crtc_id);
|
||||
if(!connector)
|
||||
|
||||
@@ -310,32 +310,34 @@ namespace gsr {
|
||||
};
|
||||
}
|
||||
|
||||
static SupportedCameraPixelFormats parse_supported_camera_pixel_formats(std::string_view line) {
|
||||
SupportedCameraPixelFormats result;
|
||||
string_split_char(line, ',', [&](std::string_view column) {
|
||||
if(column == "yuyv")
|
||||
result.yuyv = true;
|
||||
else if(column == "mjpeg")
|
||||
result.mjpeg = true;
|
||||
static bool parse_camera_pixel_format(std::string_view line, GsrCameraPixelFormat &pixel_format) {
|
||||
if(line == "yuyv") {
|
||||
pixel_format = YUYV;
|
||||
return true;
|
||||
});
|
||||
return result;
|
||||
} else if(line == "mjpeg") {
|
||||
pixel_format = MJPEG;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static std::optional<GsrCamera> capture_option_line_to_camera(std::string_view line) {
|
||||
std::optional<GsrCamera> camera;
|
||||
static bool capture_option_line_to_camera(std::string_view line, std::string &path, GsrCameraSetup &camera_setup, GsrCameraPixelFormat &pixel_format) {
|
||||
const std::optional<KeyValue3> key_value3 = parse_3(line);
|
||||
if(!key_value3)
|
||||
return camera;
|
||||
return false;
|
||||
|
||||
path = key_value3->value1;
|
||||
|
||||
mgl::vec2i size;
|
||||
char value_buffer[256];
|
||||
snprintf(value_buffer, sizeof(value_buffer), "%.*s", (int)key_value3->value2.size(), key_value3->value2.data());
|
||||
if(sscanf(value_buffer, "%dx%d", &size.x, &size.y) != 2)
|
||||
return camera;
|
||||
if(sscanf(value_buffer, "%dx%d@%dhz", &camera_setup.resolution.x, &camera_setup.resolution.y, &camera_setup.fps) != 3)
|
||||
return false;
|
||||
|
||||
camera = GsrCamera{std::string(key_value3->value1), size, parse_supported_camera_pixel_formats(key_value3->value3)};
|
||||
return camera;
|
||||
if(!parse_camera_pixel_format(key_value3->value3, pixel_format))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static std::optional<GsrMonitor> capture_option_line_to_monitor(std::string_view line) {
|
||||
@@ -354,6 +356,37 @@ namespace gsr {
|
||||
return monitor;
|
||||
}
|
||||
|
||||
static GsrCamera* get_gsr_camera_by_path(std::vector<GsrCamera> &cameras, const std::string &path) {
|
||||
for(GsrCamera &camera : cameras) {
|
||||
if(camera.path == path)
|
||||
return &camera;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static void parse_camera_line(std::string_view line, std::vector<GsrCamera> &cameras) {
|
||||
std::string camera_path;
|
||||
GsrCameraSetup camera_setup;
|
||||
GsrCameraPixelFormat pixel_format;
|
||||
if(!capture_option_line_to_camera(line, camera_path, camera_setup, pixel_format))
|
||||
return;
|
||||
|
||||
GsrCamera *existing_camera = get_gsr_camera_by_path(cameras, camera_path);
|
||||
if(!existing_camera) {
|
||||
cameras.push_back(GsrCamera{camera_path, std::vector<GsrCameraSetup>{}, std::vector<GsrCameraSetup>{}});
|
||||
existing_camera = &cameras.back();
|
||||
}
|
||||
|
||||
switch(pixel_format) {
|
||||
case YUYV:
|
||||
existing_camera->yuyv_setups.push_back(camera_setup);
|
||||
break;
|
||||
case MJPEG:
|
||||
existing_camera->mjpeg_setups.push_back(camera_setup);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void parse_capture_options_line(SupportedCaptureOptions &capture_options, std::string_view line) {
|
||||
if(line == "window") {
|
||||
capture_options.window = true;
|
||||
@@ -364,9 +397,7 @@ namespace gsr {
|
||||
} else if(line == "portal") {
|
||||
capture_options.portal = true;
|
||||
} else if(!line.empty() && line[0] == '/') {
|
||||
std::optional<GsrCamera> camera = capture_option_line_to_camera(line);
|
||||
if(camera)
|
||||
capture_options.cameras.push_back(std::move(camera.value()));
|
||||
parse_camera_line(line, capture_options.cameras);
|
||||
} else {
|
||||
std::optional<GsrMonitor> monitor = capture_option_line_to_monitor(line);
|
||||
if(monitor)
|
||||
@@ -414,9 +445,7 @@ namespace gsr {
|
||||
}
|
||||
|
||||
string_split_char(stdout_str, '\n', [&](std::string_view line) {
|
||||
std::optional<GsrCamera> camera = capture_option_line_to_camera(line);
|
||||
if(camera)
|
||||
cameras.push_back(std::move(camera.value()));
|
||||
parse_camera_line(line, cameras);
|
||||
return true;
|
||||
});
|
||||
|
||||
|
||||
@@ -152,19 +152,24 @@ namespace gsr {
|
||||
if(read_led_brightness_timer.get_elapsed_time_seconds() > 0.2) {
|
||||
read_led_brightness_timer.restart();
|
||||
|
||||
bool led_status_outdated = false;
|
||||
bool any_keyboard_with_led_enabled = false;
|
||||
bool any_keyboard_with_led_disabled = false;
|
||||
char buffer[32];
|
||||
for(int led_brightness_file_fd : led_brightness_files) {
|
||||
const ssize_t bytes_read = read(led_brightness_file_fd, buffer, sizeof(buffer));
|
||||
if(bytes_read > 0) {
|
||||
if(buffer[0] == '0')
|
||||
led_status_outdated = true;
|
||||
any_keyboard_with_led_disabled = true;
|
||||
else
|
||||
any_keyboard_with_led_enabled = true;
|
||||
lseek(led_brightness_file_fd, 0, SEEK_SET);
|
||||
}
|
||||
}
|
||||
|
||||
if(led_status_outdated && led_enabled)
|
||||
if(led_enabled && any_keyboard_with_led_disabled)
|
||||
run_gsr_global_hotkeys_set_leds(true);
|
||||
else if(!led_enabled && any_keyboard_with_led_enabled)
|
||||
run_gsr_global_hotkeys_set_leds(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -525,8 +525,7 @@ namespace gsr {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Only do this if led indicator is enabled (at startup or when changing recording/screenshot settings to enabled it)
|
||||
led_indicator = std::make_unique<LedIndicator>();
|
||||
update_led_indicator_after_settings_change();
|
||||
}
|
||||
|
||||
Overlay::~Overlay() {
|
||||
@@ -880,6 +879,7 @@ namespace gsr {
|
||||
|
||||
close_button_widget.draw(*window, mgl::vec2f(0.0f, 0.0f));
|
||||
page_stack.draw(*window, mgl::vec2f(0.0f, 0.0f));
|
||||
draw_tooltip(*window);
|
||||
|
||||
if(cursor_texture.is_valid()) {
|
||||
cursor_sprite.set_position((window->get_mouse_position() - cursor_hotspot).to_vec2f());
|
||||
@@ -1179,6 +1179,15 @@ namespace gsr {
|
||||
global_hotkeys.reset();
|
||||
}
|
||||
|
||||
void Overlay::update_led_indicator_after_settings_change() {
|
||||
if(config.record_config.record_options.use_led_indicator || config.replay_config.record_options.use_led_indicator || config.streaming_config.record_options.use_led_indicator || config.screenshot_config.use_led_indicator) {
|
||||
if(!led_indicator)
|
||||
led_indicator = std::make_unique<LedIndicator>();
|
||||
} else {
|
||||
led_indicator.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void Overlay::create_frontpage_ui_components() {
|
||||
bg_screenshot_overlay = mgl::Rectangle(mgl::vec2f(get_theme().window_width, get_theme().window_height));
|
||||
top_bar_background = mgl::Rectangle(mgl::vec2f(get_theme().window_width, get_theme().window_height*0.06f).floor());
|
||||
@@ -1269,6 +1278,8 @@ namespace gsr {
|
||||
record_settings_page->on_config_changed = [this]() {
|
||||
if(recording_status == RecordingStatus::RECORD)
|
||||
show_notification("Recording settings have been modified.\nYou may need to restart recording to apply the changes.", notification_timeout_seconds, mgl::Color(255, 255, 255), get_color_theme().tint_color, NotificationType::RECORD);
|
||||
|
||||
update_led_indicator_after_settings_change();
|
||||
};
|
||||
page_stack.push(std::move(record_settings_page));
|
||||
} else if(id == "pause") {
|
||||
@@ -1294,6 +1305,8 @@ namespace gsr {
|
||||
stream_settings_page->on_config_changed = [this]() {
|
||||
if(recording_status == RecordingStatus::STREAM)
|
||||
show_notification("Streaming settings have been modified.\nYou may need to restart streaming to apply the changes.", notification_timeout_seconds, mgl::Color(255, 255, 255), get_color_theme().tint_color, NotificationType::STREAM);
|
||||
|
||||
update_led_indicator_after_settings_change();
|
||||
};
|
||||
page_stack.push(std::move(stream_settings_page));
|
||||
} else if(id == "start") {
|
||||
@@ -1376,6 +1389,9 @@ namespace gsr {
|
||||
button->set_icon_padding_scale(1.2f);
|
||||
button->on_click = [&]() {
|
||||
auto screenshot_settings_page = std::make_unique<ScreenshotSettingsPage>(&gsr_info, config, &page_stack);
|
||||
screenshot_settings_page->on_config_changed = [this]() {
|
||||
update_led_indicator_after_settings_change();
|
||||
};
|
||||
page_stack.push(std::move(screenshot_settings_page));
|
||||
};
|
||||
front_page_ptr->add_widget(std::move(button));
|
||||
@@ -2514,6 +2530,11 @@ namespace gsr {
|
||||
args.push_back("yes");
|
||||
}
|
||||
|
||||
if(record_options.low_power_mode) {
|
||||
args.push_back("-low-power");
|
||||
args.push_back("yes");
|
||||
}
|
||||
|
||||
if(record_options.record_area_option == "region")
|
||||
add_region_command(args, region_str, region_str_size, region_selector);
|
||||
}
|
||||
@@ -2742,6 +2763,9 @@ namespace gsr {
|
||||
capture_source_arg += ";width=" + std::to_string(webcam_size.x) + "%";
|
||||
capture_source_arg += ";height=" + std::to_string(webcam_size.y) + "%";
|
||||
capture_source_arg += ";pixfmt=" + record_options.webcam_video_format;
|
||||
capture_source_arg += ";camera_width=" + std::to_string(record_options.webcam_camera_width);
|
||||
capture_source_arg += ";camera_height=" + std::to_string(record_options.webcam_camera_height);
|
||||
capture_source_arg += ";camera_fps=" + std::to_string(record_options.webcam_camera_fps);
|
||||
if(record_options.webcam_flip_horizontally)
|
||||
capture_source_arg += ";hflip=true";
|
||||
}
|
||||
@@ -3125,6 +3149,7 @@ namespace gsr {
|
||||
|
||||
static std::string streaming_get_url(const Config &config) {
|
||||
std::string url;
|
||||
fprintf(stderr, "streaming service: %s\n", config.streaming_config.streaming_service.c_str());
|
||||
if(config.streaming_config.streaming_service == "twitch") {
|
||||
url += "rtmp://live.twitch.tv/app/";
|
||||
url += config.streaming_config.twitch.stream_key;
|
||||
@@ -3134,6 +3159,13 @@ namespace gsr {
|
||||
} else if(config.streaming_config.streaming_service == "rumble") {
|
||||
url += "rtmp://rtmp.rumble.com/live/";
|
||||
url += config.streaming_config.rumble.stream_key;
|
||||
} else if(config.streaming_config.streaming_service == "kick") {
|
||||
fprintf(stderr, "kick: %s, %s\n", config.streaming_config.kick.stream_url.c_str(), config.streaming_config.kick.stream_key.c_str());
|
||||
url += config.streaming_config.kick.stream_url;
|
||||
if(!url.empty() && url.back() != '/')
|
||||
url += "/";
|
||||
url += "app/";
|
||||
url += config.streaming_config.kick.stream_key;
|
||||
} else if(config.streaming_config.streaming_service == "custom") {
|
||||
url = config.streaming_config.custom.url;
|
||||
if(url.size() >= 7 && strncmp(url.c_str(), "rtmp://", 7) == 0)
|
||||
|
||||
@@ -129,6 +129,15 @@ namespace gsr {
|
||||
if(!theme->unmasked_texture.load_from_file((resources_path + "images/unmasked.png").c_str(), mgl::Texture::LoadOptions{false, false, MGL_TEXTURE_SCALE_LINEAR_MIPMAP}))
|
||||
goto error;
|
||||
|
||||
if(!theme->warning_texture.load_from_file((resources_path + "images/warning.png").c_str(), mgl::Texture::LoadOptions{false, false, MGL_TEXTURE_SCALE_LINEAR_MIPMAP}))
|
||||
goto error;
|
||||
|
||||
if(!theme->question_mark_texture.load_from_file((resources_path + "images/question_mark.png").c_str(), mgl::Texture::LoadOptions{false, false, MGL_TEXTURE_SCALE_LINEAR_MIPMAP}))
|
||||
goto error;
|
||||
|
||||
if(!theme->info_texture.load_from_file((resources_path + "images/info.png").c_str(), mgl::Texture::LoadOptions{false, false, MGL_TEXTURE_SCALE_LINEAR_MIPMAP}))
|
||||
goto error;
|
||||
|
||||
if(!theme->ps4_home_texture.load_from_file((resources_path + "images/ps4_home.png").c_str(), mgl::Texture::LoadOptions{false, false, MGL_TEXTURE_SCALE_LINEAR_MIPMAP}))
|
||||
goto error;
|
||||
|
||||
|
||||
@@ -40,6 +40,8 @@ namespace gsr {
|
||||
if(!visible)
|
||||
return true;
|
||||
|
||||
handle_tooltip_event(event, position + offset, get_size());
|
||||
|
||||
if(event.type == mgl::Event::MouseButtonPressed && event.mouse_button.button == mgl::Mouse::Left) {
|
||||
const bool clicked_inside = mgl::FloatRect(position + offset, get_size()).contains({ (float)event.mouse_button.x, (float)event.mouse_button.y });
|
||||
if(clicked_inside) {
|
||||
|
||||
@@ -83,7 +83,14 @@ namespace gsr {
|
||||
draw_unselected(window, draw_pos);
|
||||
}
|
||||
|
||||
void ComboBox::add_item(const std::string &text, const std::string &id) {
|
||||
void ComboBox::add_item(const std::string &text, const std::string &id, bool allow_duplicate) {
|
||||
if(!allow_duplicate) {
|
||||
for(const auto &item : items) {
|
||||
if(item.id == id)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
items.push_back({mgl::Text(text, *font), id, {0.0f, 0.0f}});
|
||||
items.back().text.set_max_width(font->get_character_size() * 20); // TODO: Make a proper solution
|
||||
//items.back().text.set_max_rows(1);
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
#include "../../include/gui/Utils.hpp"
|
||||
|
||||
#include <mglpp/window/Window.hpp>
|
||||
#include <mglpp/window/Event.hpp>
|
||||
#include <mglpp/system/FloatRect.hpp>
|
||||
#include <mglpp/graphics/Texture.hpp>
|
||||
|
||||
namespace gsr {
|
||||
@@ -19,8 +21,15 @@ namespace gsr {
|
||||
if(!visible)
|
||||
return;
|
||||
|
||||
const mgl::vec2f draw_pos = (position + offset).floor();
|
||||
|
||||
if(on_mouse_move) {
|
||||
const bool mouse_inside = mgl::FloatRect(draw_pos, get_size()).contains(window.get_mouse_position().to_vec2f());
|
||||
on_mouse_move(mouse_inside);
|
||||
}
|
||||
|
||||
sprite.set_size(get_size());
|
||||
sprite.set_position((position + offset).floor());
|
||||
sprite.set_position(draw_pos);
|
||||
window.draw(sprite);
|
||||
}
|
||||
|
||||
|
||||
@@ -357,6 +357,8 @@ namespace gsr {
|
||||
}
|
||||
|
||||
void ScreenshotSettingsPage::save() {
|
||||
Config prev_config = config;
|
||||
|
||||
config.screenshot_config.record_area_option = record_area_box_ptr->get_selected_id();
|
||||
config.screenshot_config.image_width = atoi(image_width_entry_ptr->get_text().c_str());
|
||||
config.screenshot_config.image_height = atoi(image_height_entry_ptr->get_text().c_str());
|
||||
@@ -390,5 +392,8 @@ namespace gsr {
|
||||
}
|
||||
|
||||
save_config(config);
|
||||
|
||||
if(on_config_changed && config != prev_config)
|
||||
on_config_changed();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "../../include/gui/FileChooser.hpp"
|
||||
#include "../../include/gui/Subsection.hpp"
|
||||
#include "../../include/gui/CustomRendererWidget.hpp"
|
||||
#include "../../include/gui/Image.hpp"
|
||||
#include "../../include/gui/Utils.hpp"
|
||||
#include "../../include/Theme.hpp"
|
||||
#include "../../include/GsrInfo.hpp"
|
||||
@@ -205,6 +206,7 @@ namespace gsr {
|
||||
|
||||
webcam_sources_box_ptr->on_selection_changed = [this](const std::string&, const std::string &id) {
|
||||
selected_camera = std::nullopt;
|
||||
selected_camera_setup = std::nullopt;
|
||||
webcam_video_format_box_ptr->clear_items();
|
||||
if(id == "") {
|
||||
webcam_body_list_ptr->set_visible(false);
|
||||
@@ -220,20 +222,53 @@ namespace gsr {
|
||||
webcam_body_list_ptr->set_visible(true);
|
||||
webcam_video_format_box_ptr->add_item("Auto (recommended)", "auto");
|
||||
|
||||
if(it->supported_pixel_formats.yuyv)
|
||||
if(!it->yuyv_setups.empty())
|
||||
webcam_video_format_box_ptr->add_item("YUYV", "yuyv");
|
||||
|
||||
if(it->supported_pixel_formats.mjpeg)
|
||||
if(!it->mjpeg_setups.empty())
|
||||
webcam_video_format_box_ptr->add_item("Motion-JPEG", "mjpeg");
|
||||
|
||||
webcam_video_format_box_ptr->set_selected_item(get_current_record_options().webcam_video_format);
|
||||
selected_camera = *it;
|
||||
|
||||
// TODO: Set from config
|
||||
if(webcam_video_format_box_ptr->get_selected_id() == "yuyv" && !it->yuyv_setups.empty())
|
||||
selected_camera_setup = selected_camera->yuyv_setups.front();
|
||||
else if(webcam_video_format_box_ptr->get_selected_id() == "mjpeg" && !it->mjpeg_setups.empty())
|
||||
selected_camera_setup = selected_camera->mjpeg_setups.front();
|
||||
else if(webcam_video_format_box_ptr->get_selected_id() == "auto") {
|
||||
if(!it->mjpeg_setups.empty())
|
||||
selected_camera_setup = selected_camera->mjpeg_setups.front();
|
||||
else if(!it->yuyv_setups.empty())
|
||||
selected_camera_setup = selected_camera->yuyv_setups.front();
|
||||
}
|
||||
};
|
||||
|
||||
ll->add_widget(std::move(combobox));
|
||||
return ll;
|
||||
}
|
||||
|
||||
std::unique_ptr<List> SettingsPage::create_webcam_video_setups() {
|
||||
auto ll = std::make_unique<List>(List::Orientation::VERTICAL);
|
||||
ll->add_widget(std::make_unique<Label>(&get_theme().body_font, "Video setup:", get_color_theme().text_color));
|
||||
|
||||
auto combobox = std::make_unique<ComboBox>(&get_theme().body_font);
|
||||
webcam_video_setup_box_ptr = combobox.get();
|
||||
|
||||
ll->add_widget(std::move(combobox));
|
||||
return ll;
|
||||
}
|
||||
|
||||
static std::vector<GsrCameraSetup> sort_camera_setup(const std::vector<GsrCameraSetup> &setups) {
|
||||
auto result = setups;
|
||||
std::sort(result.begin(), result.end(), [](const auto &a, const auto &b) {
|
||||
const uint64_t score_a = (uint64_t)a.resolution.x * (uint64_t)a.resolution.y * (uint64_t)a.fps;
|
||||
const uint64_t score_b = (uint64_t)b.resolution.x * (uint64_t)b.resolution.y * (uint64_t)b.fps;
|
||||
return score_a > score_b;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
std::unique_ptr<List> SettingsPage::create_webcam_video_format() {
|
||||
auto ll = std::make_unique<List>(List::Orientation::VERTICAL);
|
||||
ll->add_widget(std::make_unique<Label>(&get_theme().body_font, "Video format:", get_color_theme().text_color));
|
||||
@@ -243,12 +278,52 @@ namespace gsr {
|
||||
|
||||
webcam_video_format_box_ptr->on_selection_changed = [this](const std::string&, const std::string &id) {
|
||||
get_current_record_options().webcam_video_format = id;
|
||||
|
||||
auto it = std::find_if(capture_options.cameras.begin(), capture_options.cameras.end(), [&](const GsrCamera &camera) {
|
||||
return camera.path == webcam_sources_box_ptr->get_selected_id();
|
||||
});
|
||||
if(it == capture_options.cameras.end())
|
||||
return;
|
||||
|
||||
webcam_video_setup_box_ptr->clear_items();
|
||||
if(id == "yuyv") {
|
||||
const auto setups = sort_camera_setup(it->yuyv_setups);
|
||||
for(const auto &setup : setups) {
|
||||
char setup_str[256];
|
||||
snprintf(setup_str, sizeof(setup_str), "%dx%d@%dhz", setup.resolution.x, setup.resolution.y, setup.fps);
|
||||
webcam_video_setup_box_ptr->add_item(setup_str, setup_str, false);
|
||||
}
|
||||
} else if(id == "mjpeg") {
|
||||
const auto setups = sort_camera_setup(it->mjpeg_setups);
|
||||
for(const auto &setup : setups) {
|
||||
char setup_str[256];
|
||||
snprintf(setup_str, sizeof(setup_str), "%dx%d@%dhz", setup.resolution.x, setup.resolution.y, setup.fps);
|
||||
webcam_video_setup_box_ptr->add_item(setup_str, setup_str, false);
|
||||
}
|
||||
} else if(id == "auto") {
|
||||
auto setups = it->yuyv_setups;
|
||||
setups.insert(setups.end(), it->mjpeg_setups.begin(), it->mjpeg_setups.end());
|
||||
setups = sort_camera_setup(setups);
|
||||
|
||||
for(const auto &setup : setups) {
|
||||
char setup_str[256];
|
||||
snprintf(setup_str, sizeof(setup_str), "%dx%d@%dhz", setup.resolution.x, setup.resolution.y, setup.fps);
|
||||
webcam_video_setup_box_ptr->add_item(setup_str, setup_str, false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ll->add_widget(std::move(combobox));
|
||||
return ll;
|
||||
}
|
||||
|
||||
std::unique_ptr<List> SettingsPage::create_webcam_video_setup_list() {
|
||||
auto list = std::make_unique<List>(List::Orientation::HORIZONTAL);
|
||||
list->add_widget(create_webcam_video_format());
|
||||
list->add_widget(create_webcam_video_setups());
|
||||
return list;
|
||||
}
|
||||
|
||||
std::unique_ptr<Widget> SettingsPage::create_webcam_location_widget() {
|
||||
const float camera_screen_width = std::min(400.0f, (float)settings_scrollable_page_ptr->get_inner_size().x * 0.90f);
|
||||
camera_screen_size = mgl::vec2f(camera_screen_width, camera_screen_width * 0.5625);
|
||||
@@ -261,13 +336,13 @@ namespace gsr {
|
||||
|
||||
auto camera_location_widget = std::make_unique<CustomRendererWidget>(camera_screen_size);
|
||||
camera_location_widget->draw_handler = [this, screen_border_size, screen_border](mgl::Window &window, mgl::vec2f pos, mgl::vec2f size) {
|
||||
if(!selected_camera.has_value())
|
||||
if(!selected_camera.has_value() || !selected_camera_setup.has_value())
|
||||
return;
|
||||
|
||||
pos = pos.floor();
|
||||
size = size.floor();
|
||||
const mgl::vec2i mouse_pos = window.get_mouse_position();
|
||||
const mgl::vec2f webcam_box_min_size = clamp_keep_aspect_ratio(selected_camera->size.to_vec2f(), screen_inner_size * 0.2f);
|
||||
const mgl::vec2f webcam_box_min_size = clamp_keep_aspect_ratio(selected_camera_setup->resolution.to_vec2f(), screen_inner_size * 0.2f);
|
||||
|
||||
if(moving_webcam_box) {
|
||||
webcam_box_pos = mouse_pos.to_vec2f() - screen_border_size - webcam_box_grab_offset - pos;
|
||||
@@ -276,7 +351,7 @@ namespace gsr {
|
||||
webcam_box_size = webcam_box_size_resize_start + mouse_diff;
|
||||
}
|
||||
|
||||
webcam_box_size = clamp_keep_aspect_ratio(selected_camera->size.to_vec2f(), webcam_box_size);
|
||||
webcam_box_size = clamp_keep_aspect_ratio(selected_camera_setup->resolution.to_vec2f(), webcam_box_size);
|
||||
|
||||
if(webcam_box_pos.x < 0.0f)
|
||||
webcam_box_pos.x = 0.0f;
|
||||
@@ -300,7 +375,7 @@ namespace gsr {
|
||||
else if(webcam_box_pos.y + webcam_box_size.y > screen_inner_size.y)
|
||||
webcam_box_size.y = screen_inner_size.y - webcam_box_pos.y;
|
||||
|
||||
webcam_box_size = clamp_keep_aspect_ratio(selected_camera->size.to_vec2f(), webcam_box_size);
|
||||
webcam_box_size = clamp_keep_aspect_ratio(selected_camera_setup->resolution.to_vec2f(), webcam_box_size);
|
||||
|
||||
{
|
||||
draw_rectangle_outline(window, pos, size, mgl::Color(255, 0, 0, 255), screen_border);
|
||||
@@ -310,7 +385,7 @@ namespace gsr {
|
||||
}
|
||||
|
||||
{
|
||||
webcam_box_drawn_size = clamp_keep_aspect_ratio(selected_camera->size.to_vec2f(), webcam_box_size);
|
||||
webcam_box_drawn_size = clamp_keep_aspect_ratio(selected_camera_setup->resolution.to_vec2f(), webcam_box_size);
|
||||
webcam_box_drawn_pos = (pos + screen_border_size + webcam_box_pos).floor();
|
||||
|
||||
draw_rectangle_outline(window, webcam_box_drawn_pos, webcam_box_drawn_size, mgl::Color(0, 255, 0, 255), screen_border);
|
||||
@@ -391,7 +466,7 @@ namespace gsr {
|
||||
body_list->add_widget(create_webcam_location_widget());
|
||||
body_list->add_widget(std::make_unique<Label>(&get_theme().body_font, "* Right click in the bottom right corner to resize the webcam", get_color_theme().text_color));
|
||||
body_list->add_widget(create_flip_camera_checkbox());
|
||||
body_list->add_widget(create_webcam_video_format());
|
||||
body_list->add_widget(create_webcam_video_setup_list());
|
||||
return body_list;
|
||||
}
|
||||
|
||||
@@ -446,8 +521,9 @@ namespace gsr {
|
||||
auto remove_audio_track_button = std::make_unique<Button>(&get_theme().body_font, "", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 0));
|
||||
remove_audio_track_button->set_icon(&get_theme().trash_texture);
|
||||
remove_audio_track_button->set_icon_padding_scale(0.75f);
|
||||
remove_audio_track_button->on_click = [audio_input_list_ptr, audio_device_list_ptr]() {
|
||||
remove_audio_track_button->on_click = [this, audio_input_list_ptr, audio_device_list_ptr]() {
|
||||
audio_input_list_ptr->remove_widget(audio_device_list_ptr);
|
||||
update_application_audio_warning_visibility();
|
||||
};
|
||||
return remove_audio_track_button;
|
||||
}
|
||||
@@ -470,11 +546,47 @@ namespace gsr {
|
||||
return button;
|
||||
}
|
||||
|
||||
void SettingsPage::update_application_audio_warning_visibility() {
|
||||
audio_track_section_list_ptr->for_each_child_widget([](std::unique_ptr<Widget> &child_widget) {
|
||||
Subsection *audio_subsection = dynamic_cast<Subsection*>(child_widget.get());
|
||||
List *audio_track_section_items_list_ptr = dynamic_cast<List*>(audio_subsection->get_inner_widget());
|
||||
List *audio_input_list_ptr = dynamic_cast<List*>(audio_track_section_items_list_ptr->get_child_widget_by_index(2));
|
||||
List *application_audio_warning_list_ptr = dynamic_cast<List*>(audio_track_section_items_list_ptr->get_child_widget_by_index(4));
|
||||
|
||||
int num_output_devices = 0;
|
||||
int num_application_audio = 0;
|
||||
|
||||
audio_input_list_ptr->for_each_child_widget([&num_output_devices, &num_application_audio](std::unique_ptr<Widget> &child_widget){
|
||||
List *audio_track_line = dynamic_cast<List*>(child_widget.get());
|
||||
const AudioTrackType audio_track_type = (AudioTrackType)(uintptr_t)audio_track_line->userdata;
|
||||
switch(audio_track_type) {
|
||||
case AudioTrackType::DEVICE: {
|
||||
Label *label = dynamic_cast<Label*>(audio_track_line->get_child_widget_by_index(0));
|
||||
const bool is_output_device = starts_with(label->get_text().c_str(), "Output device");
|
||||
if(is_output_device)
|
||||
num_output_devices++;
|
||||
break;
|
||||
}
|
||||
case AudioTrackType::APPLICATION:
|
||||
case AudioTrackType::APPLICATION_CUSTOM: {
|
||||
num_application_audio++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
application_audio_warning_list_ptr->set_visible(num_output_devices > 0 && num_application_audio > 0);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
std::unique_ptr<Button> SettingsPage::create_add_audio_output_device_button(List *audio_input_list_ptr) {
|
||||
auto button = std::make_unique<Button>(&get_theme().body_font, "Add output device", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
|
||||
button->on_click = [this, audio_input_list_ptr]() {
|
||||
audio_devices = get_audio_devices();
|
||||
audio_input_list_ptr->add_widget(create_audio_device(AudioDeviceType::OUTPUT, audio_input_list_ptr));
|
||||
update_application_audio_warning_visibility();
|
||||
};
|
||||
return button;
|
||||
}
|
||||
@@ -533,6 +645,8 @@ namespace gsr {
|
||||
audio_input_list_ptr->add_widget(create_custom_application_audio(audio_input_list_ptr));
|
||||
else
|
||||
audio_input_list_ptr->add_widget(create_application_audio(audio_input_list_ptr));
|
||||
|
||||
update_application_audio_warning_visibility();
|
||||
};
|
||||
return add_audio_track_button;
|
||||
}
|
||||
@@ -557,6 +671,18 @@ namespace gsr {
|
||||
return application_audio_invert_checkbox;
|
||||
}
|
||||
|
||||
std::unique_ptr<Widget> SettingsPage::create_application_audio_warning() {
|
||||
auto list = std::make_unique<List>(List::Orientation::HORIZONTAL);
|
||||
list->set_spacing(0.003f);
|
||||
list->set_visible(false);
|
||||
|
||||
const int font_character_size = get_theme().body_font.get_character_size();
|
||||
list->add_widget(std::make_unique<Image>(&get_theme().warning_texture, mgl::vec2f(font_character_size, font_character_size), Image::ScaleBehavior::SCALE));
|
||||
list->add_widget(std::make_unique<Label>(&get_theme().body_font, "Recording output devices and application audio may record all output audio, which is likely\nnot what you want to do. Remove the output devices.", get_color_theme().text_color));
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
static void update_audio_track_titles(List *audio_track_section_list_ptr) {
|
||||
int index = 0;
|
||||
audio_track_section_list_ptr->for_each_child_widget([&index](std::unique_ptr<Widget> &widget) {
|
||||
@@ -604,6 +730,7 @@ namespace gsr {
|
||||
list_ptr->add_widget(create_add_audio_buttons(audio_input_section_ptr));
|
||||
list_ptr->add_widget(std::move(audio_input_section));
|
||||
list_ptr->add_widget(create_application_audio_invert_checkbox());
|
||||
list_ptr->add_widget(create_application_audio_warning());
|
||||
|
||||
set_application_audio_options_visible(subsection.get(), view_radio_button_ptr->get_selected_id() == "advanced", *gsr_info);
|
||||
return subsection;
|
||||
@@ -1082,6 +1209,33 @@ namespace gsr {
|
||||
return list;
|
||||
}
|
||||
|
||||
std::unique_ptr<Widget> SettingsPage::create_low_power_mode() {
|
||||
auto list = std::make_unique<List>(List::Orientation::HORIZONTAL, List::Alignment::CENTER);
|
||||
|
||||
auto checkbox = std::make_unique<CheckBox>(&get_theme().body_font, "Record in low-power mode");
|
||||
checkbox->set_visible(gsr_info->gpu_info.vendor == GpuVendor::AMD);
|
||||
low_power_mode_checkbox_ptr = checkbox.get();
|
||||
|
||||
list->add_widget(std::move(checkbox));
|
||||
|
||||
auto info = std::make_unique<Image>(&get_theme().question_mark_texture, low_power_mode_checkbox_ptr->get_size(), Image::ScaleBehavior::SCALE);
|
||||
info->set_tooltip_text(
|
||||
"Do not force the GPU to go into high performance mode when recording.\n"
|
||||
"May affect recording performance, especially when playing a video at the same time.\n"
|
||||
"If enabled then it's recommended to use sync to content frame rate mode to reduce power usage when idle."
|
||||
);
|
||||
Image *info_ptr = info.get();
|
||||
info->on_mouse_move = [info_ptr](bool inside) {
|
||||
if(inside)
|
||||
set_current_tooltip(info_ptr);
|
||||
else
|
||||
remove_as_current_tooltip(info_ptr);
|
||||
};
|
||||
list->add_widget(std::move(info));
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
void SettingsPage::add_replay_widgets() {
|
||||
auto file_info_list = std::make_unique<List>(List::Orientation::VERTICAL);
|
||||
auto file_info_data_list = std::make_unique<List>(List::Orientation::HORIZONTAL);
|
||||
@@ -1097,6 +1251,7 @@ namespace gsr {
|
||||
general_list->add_widget(create_save_replay_in_game_folder());
|
||||
if(gsr_info->system_info.gsr_version >= GsrVersion{5, 0, 3})
|
||||
general_list->add_widget(create_restart_replay_on_save());
|
||||
general_list->add_widget(create_low_power_mode());
|
||||
|
||||
settings_list_ptr->add_widget(std::make_unique<Subsection>("General", std::move(general_list), mgl::vec2f(settings_scrollable_page_ptr->get_inner_size().x, 0.0f)));
|
||||
settings_list_ptr->add_widget(std::make_unique<Subsection>("Replay indicator", create_indicator("replay"), mgl::vec2f(settings_scrollable_page_ptr->get_inner_size().x, 0.0f)));
|
||||
@@ -1153,6 +1308,7 @@ namespace gsr {
|
||||
|
||||
auto general_list = std::make_unique<List>(List::Orientation::VERTICAL);
|
||||
general_list->add_widget(create_save_recording_in_game_folder());
|
||||
general_list->add_widget(create_low_power_mode());
|
||||
|
||||
settings_list_ptr->add_widget(std::make_unique<Subsection>("General", std::move(general_list), mgl::vec2f(settings_scrollable_page_ptr->get_inner_size().x, 0.0f)));
|
||||
settings_list_ptr->add_widget(std::make_unique<Subsection>("Recording indicator", create_indicator("recording"), mgl::vec2f(settings_scrollable_page_ptr->get_inner_size().x, 0.0f)));
|
||||
@@ -1173,6 +1329,7 @@ namespace gsr {
|
||||
streaming_service_box->add_item("Twitch", "twitch");
|
||||
streaming_service_box->add_item("YouTube", "youtube");
|
||||
streaming_service_box->add_item("Rumble", "rumble");
|
||||
streaming_service_box->add_item("Kick", "kick");
|
||||
streaming_service_box->add_item("Custom", "custom");
|
||||
streaming_service_box_ptr = streaming_service_box.get();
|
||||
return streaming_service_box;
|
||||
@@ -1216,6 +1373,8 @@ namespace gsr {
|
||||
twitch_stream_key_entry_ptr = add_stream_key_entry_to_list(stream_key_list.get());
|
||||
youtube_stream_key_entry_ptr = add_stream_key_entry_to_list(stream_key_list.get());
|
||||
rumble_stream_key_entry_ptr = add_stream_key_entry_to_list(stream_key_list.get());
|
||||
kick_stream_url_entry_ptr = add_stream_key_entry_to_list(stream_key_list.get());
|
||||
kick_stream_key_entry_ptr = add_stream_key_entry_to_list(stream_key_list.get());
|
||||
|
||||
stream_key_list_ptr = stream_key_list.get();
|
||||
return stream_key_list;
|
||||
@@ -1282,7 +1441,7 @@ namespace gsr {
|
||||
settings_list_ptr->add_widget(std::make_unique<Subsection>("Streaming info", std::move(streaming_info_list), mgl::vec2f(settings_scrollable_page_ptr->get_inner_size().x, 0.0f)));
|
||||
|
||||
auto general_list = std::make_unique<List>(List::Orientation::VERTICAL);
|
||||
general_list->add_widget(create_save_recording_in_game_folder());
|
||||
general_list->add_widget(create_low_power_mode());
|
||||
|
||||
settings_list_ptr->add_widget(std::make_unique<Subsection>("General", std::move(general_list), mgl::vec2f(settings_scrollable_page_ptr->get_inner_size().x, 0.0f)));
|
||||
settings_list_ptr->add_widget(std::make_unique<Subsection>("Streaming indicator", create_indicator("streaming"), mgl::vec2f(settings_scrollable_page_ptr->get_inner_size().x, 0.0f)));
|
||||
@@ -1291,12 +1450,15 @@ namespace gsr {
|
||||
const bool twitch_option = id == "twitch";
|
||||
const bool youtube_option = id == "youtube";
|
||||
const bool rumble_option = id == "rumble";
|
||||
const bool kick_option = id == "kick";
|
||||
const bool custom_option = id == "custom";
|
||||
stream_key_list_ptr->set_visible(!custom_option);
|
||||
custom_stream_list_ptr->set_visible(custom_option);
|
||||
twitch_stream_key_entry_ptr->get_parent_widget()->set_visible(twitch_option);
|
||||
youtube_stream_key_entry_ptr->get_parent_widget()->set_visible(youtube_option);
|
||||
rumble_stream_key_entry_ptr->get_parent_widget()->set_visible(rumble_option);
|
||||
kick_stream_url_entry_ptr->get_parent_widget()->set_visible(kick_option);
|
||||
kick_stream_key_entry_ptr->get_parent_widget()->set_visible(kick_option);
|
||||
return true;
|
||||
};
|
||||
streaming_service_box_ptr->on_selection_changed("Twitch", "twitch");
|
||||
@@ -1407,6 +1569,8 @@ namespace gsr {
|
||||
auto audio_track_section = create_audio_track_section(audio_section_ptr);
|
||||
audio_track_section_list_ptr->add_widget(std::move(audio_track_section));
|
||||
}
|
||||
|
||||
update_application_audio_warning_visibility();
|
||||
}
|
||||
|
||||
void SettingsPage::load_common(RecordOptions &record_options) {
|
||||
@@ -1425,17 +1589,22 @@ namespace gsr {
|
||||
restore_portal_session_checkbox_ptr->set_checked(record_options.restore_portal_session);
|
||||
show_notification_checkbox_ptr->set_checked(record_options.show_notifications);
|
||||
led_indicator_checkbox_ptr->set_checked(record_options.use_led_indicator);
|
||||
low_power_mode_checkbox_ptr->set_checked(record_options.low_power_mode);
|
||||
|
||||
char webcam_setup_str[256];
|
||||
snprintf(webcam_setup_str, sizeof(webcam_setup_str), "%dx%d@%dhz", record_options.webcam_camera_width, record_options.webcam_camera_height, record_options.webcam_camera_fps);
|
||||
|
||||
webcam_sources_box_ptr->set_selected_item(record_options.webcam_source);
|
||||
flip_camera_horizontally_checkbox_ptr->set_checked(record_options.webcam_flip_horizontally);
|
||||
webcam_video_format_box_ptr->set_selected_item(record_options.webcam_video_format);
|
||||
webcam_video_setup_box_ptr->set_selected_item(webcam_setup_str);
|
||||
webcam_box_pos.x = ((float)record_options.webcam_x / 100.0f) * screen_inner_size.x;
|
||||
webcam_box_pos.y = ((float)record_options.webcam_y / 100.0f) * screen_inner_size.y;
|
||||
webcam_box_size.x = ((float)record_options.webcam_width / 100.0f * screen_inner_size.x);
|
||||
webcam_box_size.y = ((float)record_options.webcam_height / 100.0f * screen_inner_size.y);
|
||||
|
||||
if(selected_camera.has_value())
|
||||
webcam_box_size = clamp_keep_aspect_ratio(selected_camera->size.to_vec2f(), webcam_box_size);
|
||||
if(selected_camera_setup.has_value())
|
||||
webcam_box_size = clamp_keep_aspect_ratio(selected_camera_setup->resolution.to_vec2f(), webcam_box_size);
|
||||
|
||||
if(record_options.record_area_width == 0)
|
||||
record_options.record_area_width = 1920;
|
||||
@@ -1505,6 +1674,8 @@ namespace gsr {
|
||||
youtube_stream_key_entry_ptr->set_text(config.streaming_config.youtube.stream_key);
|
||||
twitch_stream_key_entry_ptr->set_text(config.streaming_config.twitch.stream_key);
|
||||
rumble_stream_key_entry_ptr->set_text(config.streaming_config.rumble.stream_key);
|
||||
kick_stream_url_entry_ptr->set_text(config.streaming_config.kick.stream_url);
|
||||
kick_stream_key_entry_ptr->set_text(config.streaming_config.kick.stream_key);
|
||||
stream_url_entry_ptr->set_text(config.streaming_config.custom.url);
|
||||
stream_key_entry_ptr->set_text(config.streaming_config.custom.key);
|
||||
container_box_ptr->set_selected_item(config.streaming_config.custom.container);
|
||||
@@ -1568,13 +1739,22 @@ namespace gsr {
|
||||
record_options.restore_portal_session = restore_portal_session_checkbox_ptr->is_checked();
|
||||
record_options.show_notifications = show_notification_checkbox_ptr->is_checked();
|
||||
record_options.use_led_indicator = led_indicator_checkbox_ptr->is_checked();
|
||||
record_options.low_power_mode = low_power_mode_checkbox_ptr->is_checked();
|
||||
|
||||
if(selected_camera.has_value())
|
||||
webcam_box_size = clamp_keep_aspect_ratio(selected_camera->size.to_vec2f(), webcam_box_size);
|
||||
if(selected_camera_setup.has_value())
|
||||
webcam_box_size = clamp_keep_aspect_ratio(selected_camera_setup->resolution.to_vec2f(), webcam_box_size);
|
||||
|
||||
int camera_width = 0;
|
||||
int camera_height = 0;
|
||||
int camera_fps = 0;
|
||||
sscanf(webcam_video_setup_box_ptr->get_selected_id().c_str(), "%dx%d@%dhz", &camera_width, &camera_height, &camera_fps);
|
||||
|
||||
record_options.webcam_source = webcam_sources_box_ptr->get_selected_id();
|
||||
record_options.webcam_flip_horizontally = flip_camera_horizontally_checkbox_ptr->is_checked();
|
||||
record_options.webcam_video_format = webcam_video_format_box_ptr->get_selected_id();
|
||||
record_options.webcam_camera_width = camera_width;
|
||||
record_options.webcam_camera_height = camera_height;
|
||||
record_options.webcam_camera_fps = camera_fps;
|
||||
record_options.webcam_x = std::round((webcam_box_pos.x / screen_inner_size.x) * 100.0f);
|
||||
record_options.webcam_y = std::round((webcam_box_pos.y / screen_inner_size.y) * 100.0f);
|
||||
record_options.webcam_width = std::round((webcam_box_size.x / screen_inner_size.x) * 100.0f);
|
||||
@@ -1653,6 +1833,8 @@ namespace gsr {
|
||||
config.streaming_config.youtube.stream_key = youtube_stream_key_entry_ptr->get_text();
|
||||
config.streaming_config.twitch.stream_key = twitch_stream_key_entry_ptr->get_text();
|
||||
config.streaming_config.rumble.stream_key = rumble_stream_key_entry_ptr->get_text();
|
||||
config.streaming_config.kick.stream_url = kick_stream_url_entry_ptr->get_text();
|
||||
config.streaming_config.kick.stream_key = kick_stream_key_entry_ptr->get_text();
|
||||
config.streaming_config.custom.url = stream_url_entry_ptr->get_text();
|
||||
config.streaming_config.custom.key = stream_key_entry_ptr->get_text();
|
||||
config.streaming_config.custom.container = container_box_ptr->get_selected_id();
|
||||
|
||||
67
src/gui/Tooltip.cpp
Normal file
67
src/gui/Tooltip.cpp
Normal file
@@ -0,0 +1,67 @@
|
||||
#include "../../include/gui/Tooltip.hpp"
|
||||
#include "../../include/Theme.hpp"
|
||||
|
||||
#include <mglpp/graphics/Rectangle.hpp>
|
||||
#include <mglpp/graphics/Sprite.hpp>
|
||||
#include <mglpp/window/Window.hpp>
|
||||
|
||||
namespace gsr {
|
||||
static const float padding_top_scale = 0.008f;
|
||||
static const float padding_bottom_scale = 0.008f;
|
||||
static const float padding_left_scale = 0.008f;
|
||||
static const float padding_right_scale = 0.008f;
|
||||
static const float accent_scale = 0.0025f;
|
||||
|
||||
Tooltip::Tooltip(mgl::Font *font) : label("", *font) {}
|
||||
|
||||
bool Tooltip::on_event(mgl::Event&, mgl::Window&, mgl::vec2f) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void Tooltip::draw(mgl::Window &window, mgl::vec2f offset) {
|
||||
if(!visible)
|
||||
return;
|
||||
|
||||
const mgl::vec2f draw_pos = (window.get_mouse_position().to_vec2f() + offset).floor();
|
||||
|
||||
const int padding_top = get_theme().window_height * padding_top_scale;
|
||||
const int padding_left = get_theme().window_height * padding_left_scale;
|
||||
const int accent_height = get_theme().window_height * accent_scale;
|
||||
const int icon_height = label.get_font()->get_character_size();
|
||||
|
||||
mgl::Rectangle background(get_size());
|
||||
background.set_position(draw_pos - mgl::vec2f(0.0f, background.get_size().y));
|
||||
background.set_color(mgl::Color(0, 0, 0));
|
||||
window.draw(background);
|
||||
|
||||
mgl::Rectangle accent(background.get_position(), mgl::vec2f(background.get_size().x, accent_height).floor());
|
||||
accent.set_color(get_color_theme().tint_color);
|
||||
window.draw(accent);
|
||||
|
||||
mgl::Sprite icon_sprite(&get_theme().info_texture, background.get_position() + mgl::vec2f(padding_left, accent_height + padding_top).floor());
|
||||
icon_sprite.set_height(icon_height);
|
||||
window.draw(icon_sprite);
|
||||
|
||||
label.set_position(background.get_position() + mgl::vec2f(padding_left, accent_height + padding_top + icon_sprite.get_size().y).floor());
|
||||
window.draw(label);
|
||||
}
|
||||
|
||||
mgl::vec2f Tooltip::get_size() {
|
||||
if(!visible)
|
||||
return {0.0f, 0.0f};
|
||||
|
||||
const int padding_top = get_theme().window_height * padding_top_scale;
|
||||
const int padding_bottom = get_theme().window_height * padding_bottom_scale;
|
||||
const int padding_left = get_theme().window_height * padding_left_scale;
|
||||
const int padding_right = get_theme().window_height * padding_right_scale;
|
||||
const int accent_height = get_theme().window_height * accent_scale;
|
||||
const mgl::vec2f text_size = label.get_bounds().size.floor();
|
||||
const int icon_height = label.get_font()->get_character_size();
|
||||
|
||||
return mgl::vec2f(padding_left + text_size.x + padding_right, accent_height + padding_top + icon_height + text_size.y + padding_bottom).floor();
|
||||
}
|
||||
|
||||
void Tooltip::set_text(std::string text) {
|
||||
label.set_string(std::move(text));
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,16 @@
|
||||
#include "../../include/gui/Widget.hpp"
|
||||
#include "../../include/gui/Tooltip.hpp"
|
||||
#include "../../include/Theme.hpp"
|
||||
#include <vector>
|
||||
|
||||
#include <mglpp/window/Event.hpp>
|
||||
|
||||
namespace gsr {
|
||||
static std::vector<std::unique_ptr<Widget>> widgets_to_remove;
|
||||
static Widget *current_tooltip_widget = nullptr;
|
||||
static std::unique_ptr<Tooltip> tooltip;
|
||||
|
||||
static void set_current_tooltip_text(Widget *widget);
|
||||
|
||||
Widget::Widget() {
|
||||
|
||||
@@ -10,6 +18,7 @@ namespace gsr {
|
||||
|
||||
Widget::~Widget() {
|
||||
remove_widget_as_selected_in_parent();
|
||||
remove_as_current_tooltip(this);
|
||||
}
|
||||
|
||||
void Widget::set_position(mgl::vec2f position) {
|
||||
@@ -64,10 +73,34 @@ namespace gsr {
|
||||
this->visible = visible;
|
||||
}
|
||||
|
||||
bool Widget::is_visible() const {
|
||||
return visible;
|
||||
}
|
||||
|
||||
Widget* Widget::get_parent_widget() {
|
||||
return parent_widget;
|
||||
}
|
||||
|
||||
void Widget::set_tooltip_text(std::string text) {
|
||||
tooltip_text = std::move(text);
|
||||
if(current_tooltip_widget == this)
|
||||
set_current_tooltip_text(current_tooltip_widget);
|
||||
}
|
||||
|
||||
const std::string& Widget::get_tooltip_text() const {
|
||||
return tooltip_text;
|
||||
}
|
||||
|
||||
void Widget::handle_tooltip_event(mgl::Event &event, mgl::vec2f position, mgl::vec2f size) {
|
||||
if(event.type == mgl::Event::MouseMoved) {
|
||||
if(mgl::FloatRect(position, size).contains(mgl::vec2f(event.mouse_move.x, event.mouse_move.y))) {
|
||||
set_current_tooltip(this);
|
||||
} else {
|
||||
remove_as_current_tooltip(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void add_widget_to_remove(std::unique_ptr<Widget> widget) {
|
||||
widgets_to_remove.push_back(std::move(widget));
|
||||
}
|
||||
@@ -78,4 +111,40 @@ namespace gsr {
|
||||
}
|
||||
widgets_to_remove.clear();
|
||||
}
|
||||
|
||||
void set_current_tooltip(Widget *widget) {
|
||||
if(current_tooltip_widget == widget)
|
||||
return;
|
||||
|
||||
set_current_tooltip_text(widget);
|
||||
}
|
||||
|
||||
void remove_as_current_tooltip(Widget *widget) {
|
||||
if(current_tooltip_widget == widget)
|
||||
set_current_tooltip_text(nullptr);
|
||||
}
|
||||
|
||||
void set_current_tooltip_text(Widget *widget) {
|
||||
if(widget && !widget->get_tooltip_text().empty()) {
|
||||
current_tooltip_widget = widget;
|
||||
if(!tooltip)
|
||||
tooltip = std::make_unique<Tooltip>(&get_theme().body_font);
|
||||
tooltip->set_text(current_tooltip_widget->get_tooltip_text());
|
||||
} else {
|
||||
current_tooltip_widget = nullptr;
|
||||
tooltip.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void draw_tooltip(mgl::Window &window) {
|
||||
if(!tooltip)
|
||||
return;
|
||||
|
||||
if(!current_tooltip_widget->is_visible()) {
|
||||
set_current_tooltip(nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
tooltip->draw(window, mgl::vec2f(0.0f, 0.0f));
|
||||
}
|
||||
}
|
||||
@@ -243,17 +243,6 @@ static void keyboard_event_process_input_event_data(keyboard_event *self, event_
|
||||
fprintf(stderr, "Error: failed to write event data to virtual keyboard for exclusively grabbed device\n");
|
||||
}
|
||||
|
||||
if(event.type == EV_LED) {
|
||||
write(fd, &event, sizeof(event));
|
||||
|
||||
const struct input_event syn_event = {
|
||||
.type = EV_SYN,
|
||||
.code = 0,
|
||||
.value = 0
|
||||
};
|
||||
write(fd, &syn_event, sizeof(syn_event));
|
||||
}
|
||||
|
||||
if(!extra_data->is_possibly_non_keyboard_device)
|
||||
return;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user