mirror of
https://repo.dec05eba.com/gpu-screen-recorder-ui
synced 2026-04-08 04:18:08 +09:00
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
61f8c666fe | ||
|
|
28be9d1c6f | ||
|
|
305c9df7ac | ||
|
|
d08ea69277 | ||
|
|
180a3b73db | ||
|
|
ac1d57e8ba | ||
|
|
5a32c469d3 | ||
|
|
5a17aae0ab | ||
|
|
329ccdc970 | ||
|
|
b64b90d0b1 | ||
|
|
41412db704 | ||
|
|
736f2f3095 | ||
|
|
719236d4f4 |
@@ -27,8 +27,7 @@ These are the dependencies needed to build GPU Screen Recorder UI:
|
||||
* linux-api-headers
|
||||
* libpulse (libpulse-simple)
|
||||
* libdrm
|
||||
* wayland-client
|
||||
* wayland-scanner
|
||||
* wayland (wayland-client, wayland-egl, wayland-scanner)
|
||||
|
||||
## Runtime dependencies
|
||||
There are also additional dependencies needed at runtime:
|
||||
|
||||
22
TODO
22
TODO
@@ -14,8 +14,6 @@ Add nvidia overclock option.
|
||||
|
||||
Add support for window selection in capture.
|
||||
|
||||
Add option to record the focused monitor. This works on wayland too when using kms capture since we can get cursor position without root and see which monitor (crtc) the cursor is on. Or use create_window_get_center_position.
|
||||
|
||||
Filechooser should have the option to select list view, search bar and common folders/mounted drives on the left side for quick navigation. Also a button to create a new directory.
|
||||
|
||||
Restart replay on system start if monitor resolution changes.
|
||||
@@ -159,4 +157,22 @@ If CursorTrackerWayland fails then fallback to getting focused monitor by window
|
||||
Maybe automatically switch to recording with the device that controls the monitor.
|
||||
In that case also add all monitors available to capture in the capture list and automatically choose the gpu that controls the monitor.
|
||||
|
||||
Support cjk font. Use fc-match to find the location of the font. This also works in flatpak, in which case the fonts are in /run/host/..., where it lists system fonts.
|
||||
Support cjk font. Use fc-match to find the location of the font. This also works in flatpak, in which case the fonts are in /run/host/..., where it lists system fonts.
|
||||
|
||||
Keyboard layout is incorrect on wayland when using kde plasma keyboard settings to setup multiple keyboards, for example when changing to french.
|
||||
Text input is correct, but hotkey is incorrect.
|
||||
Need to use "setxkbmap fr" as well.
|
||||
This happens only when grabbing keyboard (gsr-global-hotkeys). Same thing is seen with xev.
|
||||
|
||||
Getting focused monitor on wayland doesn't work when vrr is enabled. This is because it uses software cursor instead (at least on kde plasma wayland).
|
||||
Right now it falls back to create window & getting window position trick if there is no cursor visible (or a software cursor) and one monitor has vrr enabled.
|
||||
Remove this when linux & wayland supports vrr with hardware cursor plane.
|
||||
Find out another way to get cursor position on wayland.
|
||||
This was fixed in linux 6.11 and in kde plasma in this commit: https://invent.kde.org/plasma/kwin/-/merge_requests/7582/diffs.
|
||||
|
||||
Add option to start recording/replay/stream after the notification has disappeared. Show "Starting recording on this monitor in 3 seconds".
|
||||
See if we can use hardware overlay plane instead somehow.
|
||||
|
||||
When using wayland for mgl try using wlr-layer-shell and set layer to overlay and keyboard interactivity to exclusive. Do something similar for notifications.
|
||||
|
||||
When starting gsr-ui remove any temporary replay disk data that has possibly remained from a crash, by looking for all folders that starts with gsr-replay and end with .gsr, in the replay directory.
|
||||
@@ -2,7 +2,7 @@
|
||||
Description=GPU Screen Recorder UI Service
|
||||
|
||||
[Service]
|
||||
ExecStart=gsr-ui
|
||||
ExecStart=gsr-ui launch-daemon
|
||||
KillSignal=SIGINT
|
||||
Restart=on-failure
|
||||
RestartSec=5s
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
Description=GPU Screen Recorder UI Service
|
||||
|
||||
[Service]
|
||||
ExecStart=flatpak run com.dec05eba.gpu_screen_recorder gsr-ui
|
||||
ExecStart=flatpak run com.dec05eba.gpu_screen_recorder gsr-ui launch-daemon
|
||||
KillSignal=SIGINT
|
||||
Restart=on-failure
|
||||
RestartSec=5s
|
||||
|
||||
BIN
images/ps4_cross.png
Normal file
BIN
images/ps4_cross.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.2 KiB |
BIN
images/ps4_triangle.png
Normal file
BIN
images/ps4_triangle.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
@@ -117,8 +117,11 @@ namespace gsr {
|
||||
std::string save_directory;
|
||||
std::string container = "mp4";
|
||||
int32_t replay_time = 60;
|
||||
std::string replay_storage = "ram";
|
||||
ConfigHotkey start_stop_hotkey;
|
||||
ConfigHotkey save_hotkey;
|
||||
ConfigHotkey save_1_min_hotkey;
|
||||
ConfigHotkey save_10_min_hotkey;
|
||||
};
|
||||
|
||||
struct ScreenshotConfig {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "GlobalHotkeys.hpp"
|
||||
#include "Hotplug.hpp"
|
||||
#include "../Hotplug.hpp"
|
||||
#include <unordered_map>
|
||||
#include <thread>
|
||||
#include <poll.h>
|
||||
@@ -21,6 +21,8 @@ namespace gsr {
|
||||
bool start();
|
||||
// Currently valid ids:
|
||||
// save_replay
|
||||
// save_1_min_replay
|
||||
// save_10_min_replay
|
||||
// take_screenshot
|
||||
// toggle_record
|
||||
// toggle_replay
|
||||
@@ -56,6 +58,8 @@ namespace gsr {
|
||||
bool right_pressed = false;
|
||||
|
||||
bool save_replay = false;
|
||||
bool save_1_min_replay = false;
|
||||
bool save_10_min_replay = false;
|
||||
bool take_screenshot = false;
|
||||
bool toggle_record = false;
|
||||
bool toggle_replay = false;
|
||||
@@ -6,10 +6,10 @@
|
||||
#include "Config.hpp"
|
||||
#include "window_texture.h"
|
||||
#include "WindowUtils.hpp"
|
||||
#include "GlobalHotkeysJoystick.hpp"
|
||||
#include "GlobalHotkeys/GlobalHotkeysJoystick.hpp"
|
||||
#include "AudioPlayer.hpp"
|
||||
#include "RegionSelector.hpp"
|
||||
#include "CursorTracker.hpp"
|
||||
#include "CursorTracker/CursorTracker.hpp"
|
||||
|
||||
#include <mglpp/window/Window.hpp>
|
||||
#include <mglpp/window/Event.hpp>
|
||||
@@ -59,6 +59,8 @@ namespace gsr {
|
||||
void toggle_stream();
|
||||
void toggle_replay();
|
||||
void save_replay();
|
||||
void save_replay_1_min();
|
||||
void save_replay_10_min();
|
||||
void take_screenshot();
|
||||
void take_screenshot_region();
|
||||
void show_notification(const char *str, double timeout_seconds, mgl::Color icon_color, mgl::Color bg_color, NotificationType notification_type, const char *capture_target = nullptr);
|
||||
@@ -87,7 +89,7 @@ namespace gsr {
|
||||
void update_notification_process_status();
|
||||
void save_video_in_current_game_directory(const char *video_filepath, NotificationType notification_type);
|
||||
void on_replay_saved(const char *replay_saved_filepath);
|
||||
void update_gsr_replay_save();
|
||||
void process_gsr_output();
|
||||
void update_gsr_process_status();
|
||||
void update_gsr_screenshot_process_status();
|
||||
|
||||
@@ -96,7 +98,7 @@ namespace gsr {
|
||||
void update_power_supply_status();
|
||||
void update_system_startup_status();
|
||||
|
||||
void on_stop_recording(int exit_code);
|
||||
void on_stop_recording(int exit_code, const std::string &video_filepath);
|
||||
|
||||
void update_ui_recording_paused();
|
||||
void update_ui_recording_unpaused();
|
||||
@@ -110,7 +112,10 @@ namespace gsr {
|
||||
void update_ui_replay_started();
|
||||
void update_ui_replay_stopped();
|
||||
|
||||
void prepare_gsr_output_for_reading();
|
||||
void on_press_save_replay();
|
||||
void on_press_save_replay_1_min_replay();
|
||||
void on_press_save_replay_10_min_replay();
|
||||
bool on_press_start_replay(bool disable_notification, bool finished_region_selection);
|
||||
void on_press_start_record(bool finished_region_selection);
|
||||
void on_press_start_stream(bool finished_region_selection);
|
||||
@@ -204,6 +209,8 @@ namespace gsr {
|
||||
bool replay_save_show_notification = false;
|
||||
ReplayStartupMode replay_startup_mode = ReplayStartupMode::TURN_ON_AT_SYSTEM_STARTUP;
|
||||
bool try_replay_startup = true;
|
||||
bool replay_recording = false;
|
||||
int replay_save_duration_min = 0;
|
||||
|
||||
AudioPlayer audio_player;
|
||||
RegionSelector region_selector;
|
||||
@@ -211,7 +218,6 @@ namespace gsr {
|
||||
std::function<void()> on_region_selected;
|
||||
|
||||
std::string recording_capture_target;
|
||||
std::string replay_capture_target;
|
||||
std::string screenshot_capture_target;
|
||||
|
||||
std::unique_ptr<CursorTracker> cursor_tracker;
|
||||
|
||||
@@ -50,6 +50,8 @@ namespace gsr {
|
||||
mgl::Texture ps4_dpad_down_texture;
|
||||
mgl::Texture ps4_dpad_left_texture;
|
||||
mgl::Texture ps4_dpad_right_texture;
|
||||
mgl::Texture ps4_cross_texture;
|
||||
mgl::Texture ps4_triangle_texture;
|
||||
|
||||
double double_click_timeout_seconds = 0.4;
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ namespace gsr {
|
||||
|
||||
void string_split_char(std::string_view str, char delimiter, StringSplitCallback callback_func);
|
||||
bool starts_with(std::string_view str, const char *substr);
|
||||
bool ends_with(std::string_view str, const char *substr);
|
||||
|
||||
std::string get_home_dir();
|
||||
std::string get_config_dir();
|
||||
|
||||
@@ -21,6 +21,7 @@ namespace gsr {
|
||||
void set_item_label(const std::string &id, const std::string &new_label);
|
||||
void set_item_icon(const std::string &id, mgl::Texture *texture);
|
||||
void set_item_description(const std::string &id, const std::string &new_description);
|
||||
void set_item_enabled(const std::string &id, bool enabled);
|
||||
|
||||
void set_description(std::string description_text);
|
||||
void set_activated(bool activated);
|
||||
@@ -36,6 +37,7 @@ namespace gsr {
|
||||
mgl::Text description_text;
|
||||
mgl::Texture *icon_texture = nullptr;
|
||||
std::string id;
|
||||
bool enabled = true;
|
||||
};
|
||||
|
||||
std::vector<Item> items;
|
||||
|
||||
@@ -22,6 +22,8 @@ namespace gsr {
|
||||
NONE,
|
||||
REPLAY_START_STOP,
|
||||
REPLAY_SAVE,
|
||||
REPLAY_SAVE_1_MIN,
|
||||
REPLAY_SAVE_10_MIN,
|
||||
RECORD_START_STOP,
|
||||
RECORD_PAUSE_UNPAUSE,
|
||||
STREAM_START_STOP,
|
||||
@@ -56,6 +58,7 @@ namespace gsr {
|
||||
std::unique_ptr<RadioButton> create_enable_joystick_hotkeys_button();
|
||||
std::unique_ptr<List> create_show_hide_hotkey_options();
|
||||
std::unique_ptr<List> create_replay_hotkey_options();
|
||||
std::unique_ptr<List> create_replay_partial_save_hotkey_options();
|
||||
std::unique_ptr<List> create_record_hotkey_options();
|
||||
std::unique_ptr<List> create_stream_hotkey_options();
|
||||
std::unique_ptr<List> create_screenshot_hotkey_options();
|
||||
@@ -89,6 +92,8 @@ namespace gsr {
|
||||
|
||||
Button *turn_replay_on_off_button_ptr = nullptr;
|
||||
Button *save_replay_button_ptr = nullptr;
|
||||
Button *save_replay_1_min_button_ptr = nullptr;
|
||||
Button *save_replay_10_min_button_ptr = nullptr;
|
||||
Button *start_stop_recording_button_ptr = nullptr;
|
||||
Button *pause_unpause_recording_button_ptr = nullptr;
|
||||
Button *start_stop_streaming_button_ptr = nullptr;
|
||||
|
||||
@@ -23,7 +23,8 @@ namespace gsr {
|
||||
|
||||
void add_item(const std::string &text, const std::string &id);
|
||||
void set_selected_item(const std::string &id, bool trigger_event = true, bool trigger_event_even_if_selection_not_changed = true);
|
||||
const std::string get_selected_id() const;
|
||||
const std::string& get_selected_id() const;
|
||||
const std::string& get_selected_text() const;
|
||||
|
||||
mgl::vec2f get_size() override;
|
||||
|
||||
|
||||
@@ -20,6 +20,11 @@ namespace gsr {
|
||||
class LineSeparator;
|
||||
class Subsection;
|
||||
|
||||
enum class AudioDeviceType {
|
||||
OUTPUT,
|
||||
INPUT
|
||||
};
|
||||
|
||||
class SettingsPage : public StaticPage {
|
||||
public:
|
||||
enum class Type {
|
||||
@@ -54,11 +59,12 @@ namespace gsr {
|
||||
std::unique_ptr<List> create_restore_portal_session_section();
|
||||
std::unique_ptr<Widget> create_change_video_resolution_section();
|
||||
std::unique_ptr<Widget> create_capture_target_section();
|
||||
std::unique_ptr<ComboBox> create_audio_device_selection_combobox();
|
||||
std::unique_ptr<ComboBox> create_audio_device_selection_combobox(AudioDeviceType device_type);
|
||||
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(List *audio_input_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();
|
||||
std::unique_ptr<Button> create_add_audio_device_button(List *audio_input_list_ptr);
|
||||
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);
|
||||
std::unique_ptr<List> create_application_audio(List *audio_input_list_ptr);
|
||||
std::unique_ptr<List> create_custom_application_audio(List *audio_input_list_ptr);
|
||||
@@ -97,11 +103,12 @@ namespace gsr {
|
||||
std::unique_ptr<List> create_container_section();
|
||||
std::unique_ptr<List> create_replay_time_entry();
|
||||
std::unique_ptr<List> create_replay_time();
|
||||
std::unique_ptr<List> create_replay_storage();
|
||||
std::unique_ptr<RadioButton> create_start_replay_automatically();
|
||||
std::unique_ptr<CheckBox> create_save_replay_in_game_folder();
|
||||
std::unique_ptr<CheckBox> create_restart_replay_on_save();
|
||||
std::unique_ptr<Label> create_estimated_replay_file_size();
|
||||
void update_estimated_replay_file_size();
|
||||
void update_estimated_replay_file_size(const std::string &replay_storage_type);
|
||||
void update_replay_time_text();
|
||||
std::unique_ptr<CheckBox> create_save_recording_in_game_folder();
|
||||
std::unique_ptr<Label> create_estimated_record_file_size();
|
||||
@@ -186,6 +193,7 @@ namespace gsr {
|
||||
Entry *youtube_stream_key_entry_ptr = nullptr;
|
||||
Entry *stream_url_entry_ptr = nullptr;
|
||||
Entry *replay_time_entry_ptr = nullptr;
|
||||
RadioButton *replay_storage_button_ptr = nullptr;
|
||||
Label *replay_time_label_ptr = nullptr;
|
||||
RadioButton *turn_on_replay_automatically_mode_ptr = nullptr;
|
||||
Subsection *audio_section_ptr = nullptr;
|
||||
|
||||
14
meson.build
14
meson.build
@@ -1,4 +1,4 @@
|
||||
project('gsr-ui', ['c', 'cpp'], version : '1.4.0', default_options : ['warning_level=2', 'cpp_std=c++17'], subproject_dir : 'depends')
|
||||
project('gsr-ui', ['c', 'cpp'], version : '1.6.1', default_options : ['warning_level=2', 'cpp_std=c++17'], subproject_dir : 'depends')
|
||||
|
||||
if get_option('buildtype') == 'debug'
|
||||
add_project_arguments('-g3', language : ['c', 'cpp'])
|
||||
@@ -32,6 +32,11 @@ src = [
|
||||
'src/gui/GlobalSettingsPage.cpp',
|
||||
'src/gui/GsrPage.cpp',
|
||||
'src/gui/Subsection.cpp',
|
||||
'src/GlobalHotkeys/GlobalHotkeysX11.cpp',
|
||||
'src/GlobalHotkeys/GlobalHotkeysLinux.cpp',
|
||||
'src/GlobalHotkeys/GlobalHotkeysJoystick.cpp',
|
||||
'src/CursorTracker/CursorTrackerX11.cpp',
|
||||
'src/CursorTracker/CursorTrackerWayland.cpp',
|
||||
'src/Utils.cpp',
|
||||
'src/WindowUtils.cpp',
|
||||
'src/RegionSelector.cpp',
|
||||
@@ -39,11 +44,6 @@ src = [
|
||||
'src/GsrInfo.cpp',
|
||||
'src/Process.cpp',
|
||||
'src/Overlay.cpp',
|
||||
'src/GlobalHotkeysX11.cpp',
|
||||
'src/GlobalHotkeysLinux.cpp',
|
||||
'src/GlobalHotkeysJoystick.cpp',
|
||||
'src/CursorTrackerX11.cpp',
|
||||
'src/CursorTrackerWayland.cpp',
|
||||
'src/AudioPlayer.cpp',
|
||||
'src/Hotplug.cpp',
|
||||
'src/Rpc.cpp',
|
||||
@@ -61,7 +61,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.4.0"', language: ['c', 'cpp'])
|
||||
add_project_arguments('-DGSR_FLATPAK_VERSION="5.6.0"', language: ['c', 'cpp'])
|
||||
|
||||
executable(
|
||||
meson.project_name(),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "gsr-ui"
|
||||
type = "executable"
|
||||
version = "1.4.0"
|
||||
version = "1.6.1"
|
||||
platforms = ["posix"]
|
||||
|
||||
[lang.cpp]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "../include/Config.hpp"
|
||||
#include "../include/Utils.hpp"
|
||||
#include "../include/GsrInfo.hpp"
|
||||
#include "../include/GlobalHotkeys.hpp"
|
||||
#include "../include/GlobalHotkeys/GlobalHotkeys.hpp"
|
||||
#include <variant>
|
||||
#include <limits.h>
|
||||
#include <inttypes.h>
|
||||
@@ -84,8 +84,8 @@ namespace gsr {
|
||||
|
||||
modifier_str = mgl::Keyboard::key_to_string(modifier_key);
|
||||
if(!modifier_side) {
|
||||
string_remove_all(modifier_str, "Left");
|
||||
string_remove_all(modifier_str, "Right");
|
||||
string_remove_all(modifier_str, "Left ");
|
||||
string_remove_all(modifier_str, "Right ");
|
||||
}
|
||||
result += modifier_str;
|
||||
}
|
||||
@@ -148,6 +148,8 @@ namespace gsr {
|
||||
|
||||
replay_config.start_stop_hotkey = {mgl::Keyboard::F10, HOTKEY_MOD_LALT | HOTKEY_MOD_LSHIFT};
|
||||
replay_config.save_hotkey = {mgl::Keyboard::F10, HOTKEY_MOD_LALT};
|
||||
replay_config.save_1_min_hotkey = {mgl::Keyboard::F11, HOTKEY_MOD_LALT};
|
||||
replay_config.save_10_min_hotkey = {mgl::Keyboard::F12, HOTKEY_MOD_LALT};
|
||||
|
||||
screenshot_config.take_screenshot_hotkey = {mgl::Keyboard::Printscreen, 0};
|
||||
screenshot_config.take_screenshot_region_hotkey = {mgl::Keyboard::Printscreen, HOTKEY_MOD_LCTRL};
|
||||
@@ -262,8 +264,11 @@ namespace gsr {
|
||||
{"replay.save_directory", &config.replay_config.save_directory},
|
||||
{"replay.container", &config.replay_config.container},
|
||||
{"replay.time", &config.replay_config.replay_time},
|
||||
{"replay.replay_storage", &config.replay_config.replay_storage},
|
||||
{"replay.start_stop_hotkey", &config.replay_config.start_stop_hotkey},
|
||||
{"replay.save_hotkey", &config.replay_config.save_hotkey},
|
||||
{"replay.save_1_min_hotkey", &config.replay_config.save_1_min_hotkey},
|
||||
{"replay.save_10_min_hotkey", &config.replay_config.save_10_min_hotkey},
|
||||
|
||||
{"screenshot.record_area_option", &config.screenshot_config.record_area_option},
|
||||
{"screenshot.image_width", &config.screenshot_config.image_width},
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "../include/CursorTrackerWayland.hpp"
|
||||
#include "../../include/CursorTracker/CursorTrackerWayland.hpp"
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
@@ -27,19 +27,20 @@ namespace gsr {
|
||||
typedef struct {
|
||||
uint64_t crtc_id;
|
||||
mgl::vec2i size;
|
||||
bool vrr_enabled;
|
||||
} drm_connector;
|
||||
|
||||
typedef struct {
|
||||
drm_connector connectors[MAX_CONNECTORS];
|
||||
int num_connectors;
|
||||
bool has_any_crtc_with_vrr_enabled;
|
||||
} drm_connectors;
|
||||
|
||||
/* Returns plane_property_mask */
|
||||
static uint32_t plane_get_properties(int drm_fd, uint32_t plane_id, int *crtc_x, int *crtc_y, int *crtc_id, bool *is_cursor) {
|
||||
static uint32_t plane_get_properties(int drm_fd, uint32_t plane_id, int *crtc_x, int *crtc_y, int *crtc_id) {
|
||||
*crtc_x = 0;
|
||||
*crtc_y = 0;
|
||||
*crtc_id = 0;
|
||||
*is_cursor = false;
|
||||
|
||||
uint32_t property_mask = 0;
|
||||
|
||||
@@ -80,8 +81,8 @@ namespace gsr {
|
||||
return property_mask;
|
||||
}
|
||||
|
||||
static bool connector_get_property_by_name(int drm_fd, drmModeConnectorPtr props, const char *name, uint64_t *result) {
|
||||
for(int i = 0; i < props->count_props; ++i) {
|
||||
static bool get_drm_property_by_name(int drm_fd, drmModeObjectPropertiesPtr props, const char *name, uint64_t *result) {
|
||||
for(uint32_t i = 0; i < props->count_props; ++i) {
|
||||
drmModePropertyPtr prop = drmModeGetProperty(drm_fd, props->props[i]);
|
||||
if(!prop)
|
||||
continue;
|
||||
@@ -96,6 +97,14 @@ namespace gsr {
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool connector_get_property_by_name(int drm_fd, drmModeConnectorPtr props, const char *name, uint64_t *result) {
|
||||
drmModeObjectProperties properties;
|
||||
properties.count_props = (uint32_t)props->count_props;
|
||||
properties.props = props->props;
|
||||
properties.prop_values = props->prop_values;
|
||||
return get_drm_property_by_name(drm_fd, &properties, name, result);
|
||||
}
|
||||
|
||||
static drm_connector_type_count* drm_connector_types_get_index(drm_connector_type_count *type_counts, int *num_type_counts, int connector_type) {
|
||||
for(int i = 0; i < *num_type_counts; ++i) {
|
||||
if(type_counts[i].type == connector_type)
|
||||
@@ -325,7 +334,7 @@ namespace gsr {
|
||||
};
|
||||
|
||||
/* Returns nullptr if not found */
|
||||
static const drm_connector* get_drm_connector_by_crtc_id(const drm_connectors *connectors, uint32_t crtc_id) {
|
||||
static drm_connector* get_drm_connector_by_crtc_id(drm_connectors *connectors, uint32_t crtc_id) {
|
||||
for(int i = 0; i < connectors->num_connectors; ++i) {
|
||||
if(connectors->connectors[i].crtc_id == crtc_id)
|
||||
return &connectors->connectors[i];
|
||||
@@ -335,6 +344,8 @@ namespace gsr {
|
||||
|
||||
static void get_drm_connectors(int drm_fd, drm_connectors *drm_connectors) {
|
||||
drm_connectors->num_connectors = 0;
|
||||
drm_connectors->has_any_crtc_with_vrr_enabled = false;
|
||||
|
||||
drmModeResPtr resources = drmModeGetResources(drm_fd);
|
||||
if(!resources)
|
||||
return;
|
||||
@@ -350,23 +361,59 @@ namespace gsr {
|
||||
uint64_t crtc_id = 0;
|
||||
connector_get_property_by_name(drm_fd, connector, "CRTC_ID", &crtc_id);
|
||||
if(crtc_id == 0)
|
||||
goto next;
|
||||
goto next_connector;
|
||||
|
||||
crtc = drmModeGetCrtc(drm_fd, crtc_id);
|
||||
if(!crtc)
|
||||
goto next;
|
||||
goto next_connector;
|
||||
|
||||
drm_connectors->connectors[drm_connectors->num_connectors].crtc_id = crtc_id;
|
||||
drm_connectors->connectors[drm_connectors->num_connectors].size = mgl::vec2i{(int)crtc->width, (int)crtc->height};
|
||||
drm_connectors->connectors[drm_connectors->num_connectors].vrr_enabled = false;
|
||||
++drm_connectors->num_connectors;
|
||||
|
||||
next:
|
||||
next_connector:
|
||||
if(crtc)
|
||||
drmModeFreeCrtc(crtc);
|
||||
|
||||
if(connector)
|
||||
drmModeFreeConnector(connector);
|
||||
}
|
||||
|
||||
for(int i = 0; i < resources->count_crtcs; ++i) {
|
||||
drmModeCrtcPtr crtc = nullptr;
|
||||
drmModeObjectPropertiesPtr properties = nullptr;
|
||||
uint64_t vrr_enabled = 0;
|
||||
drm_connector *connector = nullptr;
|
||||
|
||||
crtc = drmModeGetCrtc(drm_fd, resources->crtcs[i]);
|
||||
if(!crtc)
|
||||
continue;
|
||||
|
||||
properties = drmModeObjectGetProperties(drm_fd, crtc->crtc_id, DRM_MODE_OBJECT_CRTC);
|
||||
if(!properties)
|
||||
goto next_crtc;
|
||||
|
||||
if(!get_drm_property_by_name(drm_fd, properties, "VRR_ENABLED", &vrr_enabled))
|
||||
goto next_crtc;
|
||||
|
||||
connector = get_drm_connector_by_crtc_id(drm_connectors, crtc->crtc_id);
|
||||
if(!connector)
|
||||
goto next_crtc;
|
||||
|
||||
if(vrr_enabled) {
|
||||
connector->vrr_enabled = true;
|
||||
drm_connectors->has_any_crtc_with_vrr_enabled = true;
|
||||
}
|
||||
|
||||
next_crtc:
|
||||
if(properties)
|
||||
drmModeFreeObjectProperties(properties);
|
||||
|
||||
if(crtc)
|
||||
drmModeFreeCrtc(crtc);
|
||||
}
|
||||
|
||||
drmModeFreeResources(resources);
|
||||
}
|
||||
|
||||
@@ -392,19 +439,20 @@ namespace gsr {
|
||||
|
||||
drm_connectors connectors;
|
||||
connectors.num_connectors = 0;
|
||||
connectors.has_any_crtc_with_vrr_enabled = false;
|
||||
get_drm_connectors(drm_fd, &connectors);
|
||||
|
||||
drmModePlaneResPtr planes = drmModeGetPlaneResources(drm_fd);
|
||||
if(!planes)
|
||||
return;
|
||||
|
||||
bool found_cursor = false;
|
||||
for(uint32_t i = 0; i < planes->count_planes; ++i) {
|
||||
drmModePlanePtr plane = nullptr;
|
||||
const drm_connector *connector = nullptr;
|
||||
int crtc_x = 0;
|
||||
int crtc_y = 0;
|
||||
int crtc_id = 0;
|
||||
bool is_cursor = false;
|
||||
uint32_t property_mask = 0;
|
||||
|
||||
plane = drmModeGetPlane(drm_fd, planes->planes[i]);
|
||||
@@ -414,7 +462,7 @@ namespace gsr {
|
||||
if(!plane->fb_id)
|
||||
goto next;
|
||||
|
||||
property_mask = plane_get_properties(drm_fd, planes->planes[i], &crtc_x, &crtc_y, &crtc_id, &is_cursor);
|
||||
property_mask = plane_get_properties(drm_fd, planes->planes[i], &crtc_x, &crtc_y, &crtc_id);
|
||||
if(property_mask != plane_property_all || crtc_id <= 0)
|
||||
goto next;
|
||||
|
||||
@@ -426,6 +474,7 @@ namespace gsr {
|
||||
latest_cursor_position.x = crtc_x;
|
||||
latest_cursor_position.y = crtc_y;
|
||||
latest_crtc_id = crtc_id;
|
||||
found_cursor = true;
|
||||
drmModeFreePlane(plane);
|
||||
break;
|
||||
}
|
||||
@@ -434,6 +483,11 @@ namespace gsr {
|
||||
drmModeFreePlane(plane);
|
||||
}
|
||||
|
||||
// On kde plasma wayland (and possibly other wayland compositors) it uses a software cursor only for the monitors with vrr enabled.
|
||||
// In that case we cant know the cursor location and we instead want to fallback to getting focused monitor by using the hack of creating a window and getting the position.
|
||||
if(!found_cursor && latest_crtc_id > 0 && connectors.has_any_crtc_with_vrr_enabled)
|
||||
latest_crtc_id = -1;
|
||||
|
||||
drmModeFreePlaneResources(planes);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#include "../include/CursorTrackerX11.hpp"
|
||||
#include "../include/WindowUtils.hpp"
|
||||
#include "../../include/CursorTracker/CursorTrackerX11.hpp"
|
||||
#include "../../include/WindowUtils.hpp"
|
||||
|
||||
namespace gsr {
|
||||
CursorTrackerX11::CursorTrackerX11(Display *dpy) : dpy(dpy) {
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "../include/GlobalHotkeysJoystick.hpp"
|
||||
#include "../../include/GlobalHotkeys/GlobalHotkeysJoystick.hpp"
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
@@ -7,6 +7,8 @@
|
||||
|
||||
namespace gsr {
|
||||
static constexpr int button_pressed = 1;
|
||||
static constexpr int cross_button = 0;
|
||||
static constexpr int triangle_button = 2;
|
||||
static constexpr int options_button = 9;
|
||||
static constexpr int playstation_button = 10;
|
||||
static constexpr int axis_up_down = 7;
|
||||
@@ -104,6 +106,20 @@ namespace gsr {
|
||||
it->second("save_replay");
|
||||
}
|
||||
|
||||
if(save_1_min_replay) {
|
||||
save_1_min_replay = false;
|
||||
auto it = bound_actions_by_id.find("save_1_min_replay");
|
||||
if(it != bound_actions_by_id.end())
|
||||
it->second("save_1_min_replay");
|
||||
}
|
||||
|
||||
if(save_10_min_replay) {
|
||||
save_10_min_replay = false;
|
||||
auto it = bound_actions_by_id.find("save_10_min_replay");
|
||||
if(it != bound_actions_by_id.end())
|
||||
it->second("save_10_min_replay");
|
||||
}
|
||||
|
||||
if(take_screenshot) {
|
||||
take_screenshot = false;
|
||||
auto it = bound_actions_by_id.find("take_screenshot");
|
||||
@@ -186,10 +202,27 @@ namespace gsr {
|
||||
return;
|
||||
|
||||
if((event.type & JS_EVENT_BUTTON) == JS_EVENT_BUTTON) {
|
||||
if(event.number == playstation_button)
|
||||
playstation_button_pressed = event.value == button_pressed;
|
||||
else if(playstation_button_pressed && event.number == options_button && event.value == button_pressed)
|
||||
toggle_show = true;
|
||||
switch(event.number) {
|
||||
case playstation_button: {
|
||||
playstation_button_pressed = event.value == button_pressed;
|
||||
break;
|
||||
}
|
||||
case options_button: {
|
||||
if(playstation_button_pressed && event.value == button_pressed)
|
||||
toggle_show = true;
|
||||
break;
|
||||
}
|
||||
case cross_button: {
|
||||
if(playstation_button_pressed && event.value == button_pressed)
|
||||
save_1_min_replay = true;
|
||||
break;
|
||||
}
|
||||
case triangle_button: {
|
||||
if(playstation_button_pressed && event.value == button_pressed)
|
||||
save_10_min_replay = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if((event.type & JS_EVENT_AXIS) == JS_EVENT_AXIS && playstation_button_pressed) {
|
||||
const int trigger_threshold = 16383;
|
||||
const bool prev_up_pressed = up_pressed;
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "../include/GlobalHotkeysLinux.hpp"
|
||||
#include "../../include/GlobalHotkeys/GlobalHotkeysLinux.hpp"
|
||||
#include <sys/wait.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "../include/GlobalHotkeysX11.hpp"
|
||||
#include "../../include/GlobalHotkeys/GlobalHotkeysX11.hpp"
|
||||
#include <X11/keysym.h>
|
||||
#include <mglpp/window/Event.hpp>
|
||||
#include <assert.h>
|
||||
399
src/Overlay.cpp
399
src/Overlay.cpp
@@ -12,10 +12,10 @@
|
||||
#include "../include/gui/Utils.hpp"
|
||||
#include "../include/gui/PageStack.hpp"
|
||||
#include "../include/WindowUtils.hpp"
|
||||
#include "../include/GlobalHotkeys.hpp"
|
||||
#include "../include/GlobalHotkeysLinux.hpp"
|
||||
#include "../include/CursorTrackerX11.hpp"
|
||||
#include "../include/CursorTrackerWayland.hpp"
|
||||
#include "../include/GlobalHotkeys/GlobalHotkeys.hpp"
|
||||
#include "../include/GlobalHotkeys/GlobalHotkeysLinux.hpp"
|
||||
#include "../include/CursorTracker/CursorTrackerX11.hpp"
|
||||
#include "../include/CursorTracker/CursorTrackerWayland.hpp"
|
||||
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
@@ -47,7 +47,7 @@ namespace gsr {
|
||||
static const double force_window_on_top_timeout_seconds = 1.0;
|
||||
static const double replay_status_update_check_timeout_seconds = 1.5;
|
||||
static const double replay_saving_notification_timeout_seconds = 0.5;
|
||||
static const double notification_timeout_seconds = 2.0;
|
||||
static const double notification_timeout_seconds = 2.5;
|
||||
static const double notification_error_timeout_seconds = 5.0;
|
||||
static const double cursor_tracker_update_timeout_sec = 0.1;
|
||||
|
||||
@@ -207,24 +207,21 @@ namespace gsr {
|
||||
return false;
|
||||
}*/
|
||||
|
||||
// Returns the first monitor if not found. Assumes there is at least one monitor connected.
|
||||
static const Monitor* find_monitor_at_position(const std::vector<Monitor> &monitors, mgl::vec2i pos) {
|
||||
assert(!monitors.empty());
|
||||
for(const Monitor &monitor : monitors) {
|
||||
if(mgl::IntRect(monitor.position, monitor.size).contains(pos))
|
||||
return &monitor;
|
||||
}
|
||||
return &monitors.front();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Returns the first monitor if not found. Assumes there is at least one monitor connected.
|
||||
static const Monitor* find_monitor_by_name(const std::vector<Monitor> &monitors, const std::string &name) {
|
||||
assert(!monitors.empty());
|
||||
for(const Monitor &monitor : monitors) {
|
||||
if(monitor.name == name)
|
||||
return &monitor;
|
||||
}
|
||||
return &monitors.front();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static std::string get_power_supply_online_filepath() {
|
||||
@@ -325,6 +322,20 @@ namespace gsr {
|
||||
overlay->save_replay();
|
||||
});
|
||||
|
||||
global_hotkeys->bind_key_press(
|
||||
config_hotkey_to_hotkey(overlay->get_config().replay_config.save_1_min_hotkey),
|
||||
"replay_save_1_min", [overlay](const std::string &id) {
|
||||
fprintf(stderr, "pressed %s\n", id.c_str());
|
||||
overlay->save_replay_1_min();
|
||||
});
|
||||
|
||||
global_hotkeys->bind_key_press(
|
||||
config_hotkey_to_hotkey(overlay->get_config().replay_config.save_10_min_hotkey),
|
||||
"replay_save_10_min", [overlay](const std::string &id) {
|
||||
fprintf(stderr, "pressed %s\n", id.c_str());
|
||||
overlay->save_replay_10_min();
|
||||
});
|
||||
|
||||
global_hotkeys->bind_key_press(
|
||||
config_hotkey_to_hotkey(overlay->get_config().screenshot_config.take_screenshot_hotkey),
|
||||
"take_screenshot", [overlay](const std::string &id) {
|
||||
@@ -371,6 +382,16 @@ namespace gsr {
|
||||
overlay->save_replay();
|
||||
});
|
||||
|
||||
global_hotkeys_js->bind_action("save_1_min_replay", [overlay](const std::string &id) {
|
||||
fprintf(stderr, "pressed %s\n", id.c_str());
|
||||
overlay->save_replay_1_min();
|
||||
});
|
||||
|
||||
global_hotkeys_js->bind_action("save_10_min_replay", [overlay](const std::string &id) {
|
||||
fprintf(stderr, "pressed %s\n", id.c_str());
|
||||
overlay->save_replay_10_min();
|
||||
});
|
||||
|
||||
global_hotkeys_js->bind_action("take_screenshot", [overlay](const std::string &id) {
|
||||
fprintf(stderr, "pressed %s\n", id.c_str());
|
||||
overlay->take_screenshot();
|
||||
@@ -688,7 +709,7 @@ namespace gsr {
|
||||
remove_widgets_to_be_removed();
|
||||
|
||||
update_notification_process_status();
|
||||
update_gsr_replay_save();
|
||||
process_gsr_output();
|
||||
update_gsr_process_status();
|
||||
update_gsr_screenshot_process_status();
|
||||
replay_status_update_status();
|
||||
@@ -697,7 +718,7 @@ namespace gsr {
|
||||
start_region_capture = false;
|
||||
hide();
|
||||
if(!region_selector.start(get_color_theme().tint_color)) {
|
||||
show_notification("Failed to start region capture", notification_error_timeout_seconds, mgl::Color(255, 0, 0, 0), mgl::Color(255, 0, 0, 0), NotificationType::NONE);
|
||||
show_notification("Failed to start region capture", notification_error_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), NotificationType::NONE);
|
||||
on_region_selected = nullptr;
|
||||
}
|
||||
}
|
||||
@@ -870,10 +891,14 @@ namespace gsr {
|
||||
const Monitor *focused_monitor = nullptr;
|
||||
if(cursor_info) {
|
||||
focused_monitor = find_monitor_by_name(monitors, cursor_info->monitor_name);
|
||||
if(!focused_monitor)
|
||||
focused_monitor = &monitors.front();
|
||||
cursor_position = cursor_info->position;
|
||||
} else {
|
||||
const mgl::vec2i monitor_position_query_value = (x11_cursor_window || gsr_info.system_info.display_server != DisplayServer::WAYLAND) ? cursor_position : create_window_get_center_position(display);
|
||||
focused_monitor = find_monitor_at_position(monitors, monitor_position_query_value);
|
||||
if(!focused_monitor)
|
||||
focused_monitor = &monitors.front();
|
||||
}
|
||||
|
||||
// Wayland doesn't allow XGrabPointer/XGrabKeyboard when a wayland application is focused.
|
||||
@@ -909,8 +934,11 @@ namespace gsr {
|
||||
// when a compositor isn't running.
|
||||
window_create_params.graphics_api = gsr_info.system_info.display_server == DisplayServer::WAYLAND ? MGL_GRAPHICS_API_GLX : MGL_GRAPHICS_API_EGL;
|
||||
|
||||
if(!window->create("gsr ui", window_create_params))
|
||||
if(!window->create("gsr ui", window_create_params)) {
|
||||
fprintf(stderr, "error: failed to create window\n");
|
||||
window.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
//window->set_low_latency(true);
|
||||
|
||||
@@ -1004,6 +1032,9 @@ namespace gsr {
|
||||
if(paused)
|
||||
update_ui_recording_paused();
|
||||
|
||||
if(replay_recording)
|
||||
update_ui_recording_started();
|
||||
|
||||
// Wayland compositors have retarded fullscreen animations that we cant disable in a proper way
|
||||
// without messing up window position.
|
||||
show_overlay_timeout_seconds = prevent_game_minimizing ? 0.0 : 0.15;
|
||||
@@ -1051,9 +1082,15 @@ namespace gsr {
|
||||
replay_dropdown_button_ptr = button.get();
|
||||
button->add_item("Turn on", "start", config.replay_config.start_stop_hotkey.to_string(false, false));
|
||||
button->add_item("Save", "save", config.replay_config.save_hotkey.to_string(false, false));
|
||||
if(gsr_info.system_info.gsr_version >= GsrVersion{5, 4, 0}) {
|
||||
button->add_item("Save 1 min", "save_1_min", config.replay_config.save_1_min_hotkey.to_string(false, false));
|
||||
button->add_item("Save 10 min", "save_10_min", config.replay_config.save_10_min_hotkey.to_string(false, false));
|
||||
}
|
||||
button->add_item("Settings", "settings");
|
||||
button->set_item_icon("start", &get_theme().play_texture);
|
||||
button->set_item_icon("save", &get_theme().save_texture);
|
||||
button->set_item_icon("save_1_min", &get_theme().save_texture);
|
||||
button->set_item_icon("save_10_min", &get_theme().save_texture);
|
||||
button->set_item_icon("settings", &get_theme().settings_small_texture);
|
||||
button->on_click = [this](const std::string &id) {
|
||||
if(id == "settings") {
|
||||
@@ -1066,10 +1103,17 @@ namespace gsr {
|
||||
page_stack.push(std::move(replay_settings_page));
|
||||
} else if(id == "save") {
|
||||
on_press_save_replay();
|
||||
} else if(id == "save_1_min") {
|
||||
on_press_save_replay_1_min_replay();
|
||||
} else if(id == "save_10_min") {
|
||||
on_press_save_replay_10_min_replay();
|
||||
} else if(id == "start") {
|
||||
on_press_start_replay(false, false);
|
||||
}
|
||||
};
|
||||
button->set_item_enabled("save", false);
|
||||
button->set_item_enabled("save_1_min", false);
|
||||
button->set_item_enabled("save_10_min", false);
|
||||
main_buttons_list->add_widget(std::move(button));
|
||||
}
|
||||
{
|
||||
@@ -1096,6 +1140,7 @@ namespace gsr {
|
||||
on_press_start_record(false);
|
||||
}
|
||||
};
|
||||
button->set_item_enabled("pause", false);
|
||||
main_buttons_list->add_widget(std::move(button));
|
||||
}
|
||||
{
|
||||
@@ -1174,23 +1219,15 @@ namespace gsr {
|
||||
};
|
||||
|
||||
settings_page->on_page_closed = [this]() {
|
||||
if(global_hotkeys) {
|
||||
replay_dropdown_button_ptr->set_item_description("start", config.replay_config.start_stop_hotkey.to_string(false, false));
|
||||
replay_dropdown_button_ptr->set_item_description("save", config.replay_config.save_hotkey.to_string(false, false));
|
||||
replay_dropdown_button_ptr->set_item_description("start", config.replay_config.start_stop_hotkey.to_string(false, false));
|
||||
replay_dropdown_button_ptr->set_item_description("save", config.replay_config.save_hotkey.to_string(false, false));
|
||||
replay_dropdown_button_ptr->set_item_description("save_1_min", config.replay_config.save_1_min_hotkey.to_string(false, false));
|
||||
replay_dropdown_button_ptr->set_item_description("save_10_min", config.replay_config.save_10_min_hotkey.to_string(false, false));
|
||||
|
||||
record_dropdown_button_ptr->set_item_description("start", config.record_config.start_stop_hotkey.to_string(false, false));
|
||||
record_dropdown_button_ptr->set_item_description("pause", config.record_config.pause_unpause_hotkey.to_string(false, false));
|
||||
record_dropdown_button_ptr->set_item_description("start", config.record_config.start_stop_hotkey.to_string(false, false));
|
||||
record_dropdown_button_ptr->set_item_description("pause", config.record_config.pause_unpause_hotkey.to_string(false, false));
|
||||
|
||||
stream_dropdown_button_ptr->set_item_description("start", config.streaming_config.start_stop_hotkey.to_string(false, false));
|
||||
} else {
|
||||
replay_dropdown_button_ptr->set_item_description("start", "");
|
||||
replay_dropdown_button_ptr->set_item_description("save", "");
|
||||
|
||||
record_dropdown_button_ptr->set_item_description("start", "");
|
||||
record_dropdown_button_ptr->set_item_description("pause", "");
|
||||
|
||||
stream_dropdown_button_ptr->set_item_description("start", "");
|
||||
}
|
||||
stream_dropdown_button_ptr->set_item_description("start", config.streaming_config.start_stop_hotkey.to_string(false, false));
|
||||
};
|
||||
|
||||
page_stack.push(std::move(settings_page));
|
||||
@@ -1368,6 +1405,14 @@ namespace gsr {
|
||||
on_press_save_replay();
|
||||
}
|
||||
|
||||
void Overlay::save_replay_1_min() {
|
||||
on_press_save_replay_1_min_replay();
|
||||
}
|
||||
|
||||
void Overlay::save_replay_10_min() {
|
||||
on_press_save_replay_10_min_replay();
|
||||
}
|
||||
|
||||
void Overlay::take_screenshot() {
|
||||
on_press_take_screenshot(false, false);
|
||||
}
|
||||
@@ -1575,7 +1620,6 @@ namespace gsr {
|
||||
truncate_string(focused_window_name, 20);
|
||||
const char *capture_target = nullptr;
|
||||
char msg[512];
|
||||
const std::string filename = focused_window_name + "/" + video_filename;
|
||||
|
||||
switch(notification_type) {
|
||||
case NotificationType::RECORD: {
|
||||
@@ -1583,9 +1627,9 @@ namespace gsr {
|
||||
return;
|
||||
|
||||
if(is_capture_target_monitor(recording_capture_target.c_str()))
|
||||
snprintf(msg, sizeof(msg), "Saved a recording of this monitor to '%s'", filename.c_str());
|
||||
snprintf(msg, sizeof(msg), "Saved a recording of this monitor to \"%s\"", focused_window_name.c_str());
|
||||
else
|
||||
snprintf(msg, sizeof(msg), "Saved a recording of %s to '%s'", recording_capture_target.c_str(), filename.c_str());
|
||||
snprintf(msg, sizeof(msg), "Saved a recording of %s to \"%s\"", recording_capture_target.c_str(), focused_window_name.c_str());
|
||||
|
||||
capture_target = recording_capture_target.c_str();
|
||||
break;
|
||||
@@ -1594,12 +1638,18 @@ namespace gsr {
|
||||
if(!config.replay_config.show_replay_saved_notifications)
|
||||
return;
|
||||
|
||||
if(is_capture_target_monitor(replay_capture_target.c_str()))
|
||||
snprintf(msg, sizeof(msg), "Saved a replay of this monitor to '%s'", filename.c_str());
|
||||
char duration[32];
|
||||
if(replay_save_duration_min > 0)
|
||||
snprintf(duration, sizeof(duration), " %d minute ", replay_save_duration_min);
|
||||
else
|
||||
snprintf(msg, sizeof(msg), "Saved a replay of %s to '%s'", replay_capture_target.c_str(), filename.c_str());
|
||||
snprintf(duration, sizeof(duration), " ");
|
||||
|
||||
capture_target = replay_capture_target.c_str();
|
||||
if(is_capture_target_monitor(recording_capture_target.c_str()))
|
||||
snprintf(msg, sizeof(msg), "Saved a%sreplay of this monitor to \"%s\"", duration, focused_window_name.c_str());
|
||||
else
|
||||
snprintf(msg, sizeof(msg), "Saved a%sreplay of %s to \"%s\"", duration, recording_capture_target.c_str(), focused_window_name.c_str());
|
||||
|
||||
capture_target = recording_capture_target.c_str();
|
||||
break;
|
||||
}
|
||||
case NotificationType::SCREENSHOT: {
|
||||
@@ -1607,9 +1657,9 @@ namespace gsr {
|
||||
return;
|
||||
|
||||
if(is_capture_target_monitor(screenshot_capture_target.c_str()))
|
||||
snprintf(msg, sizeof(msg), "Saved a screenshot of this monitor to '%s'", filename.c_str());
|
||||
snprintf(msg, sizeof(msg), "Saved a screenshot of this monitor to \"%s\"", focused_window_name.c_str());
|
||||
else
|
||||
snprintf(msg, sizeof(msg), "Saved a screenshot of %s to '%s'", screenshot_capture_target.c_str(), filename.c_str());
|
||||
snprintf(msg, sizeof(msg), "Saved a screenshot of %s to \"%s\"", screenshot_capture_target.c_str(), focused_window_name.c_str());
|
||||
|
||||
capture_target = screenshot_capture_target.c_str();
|
||||
break;
|
||||
@@ -1621,22 +1671,37 @@ namespace gsr {
|
||||
show_notification(msg, notification_timeout_seconds, mgl::Color(255, 255, 255), get_color_theme().tint_color, notification_type, capture_target);
|
||||
}
|
||||
|
||||
static NotificationType recording_status_to_notification_type(RecordingStatus recording_status) {
|
||||
switch(recording_status) {
|
||||
case RecordingStatus::NONE: return NotificationType::NONE;
|
||||
case RecordingStatus::REPLAY: return NotificationType::REPLAY;
|
||||
case RecordingStatus::RECORD: return NotificationType::RECORD;
|
||||
case RecordingStatus::STREAM: return NotificationType::STREAM;
|
||||
}
|
||||
return NotificationType::NONE;
|
||||
}
|
||||
|
||||
void Overlay::on_replay_saved(const char *replay_saved_filepath) {
|
||||
replay_save_show_notification = false;
|
||||
if(config.replay_config.save_video_in_game_folder) {
|
||||
save_video_in_current_game_directory(replay_saved_filepath, NotificationType::REPLAY);
|
||||
} else {
|
||||
const std::string filename = filepath_get_filename(replay_saved_filepath);
|
||||
char msg[512];
|
||||
if(is_capture_target_monitor(replay_capture_target.c_str()))
|
||||
snprintf(msg, sizeof(msg), "Saved a replay of this monitor to '%s'", filename.c_str());
|
||||
char duration[32];
|
||||
if(replay_save_duration_min > 0)
|
||||
snprintf(duration, sizeof(duration), " %d minute ", replay_save_duration_min);
|
||||
else
|
||||
snprintf(msg, sizeof(msg), "Saved a replay of %s to '%s'", replay_capture_target.c_str(), filename.c_str());
|
||||
show_notification(msg, notification_timeout_seconds, mgl::Color(255, 255, 255), get_color_theme().tint_color, NotificationType::REPLAY, replay_capture_target.c_str());
|
||||
snprintf(duration, sizeof(duration), " ");
|
||||
|
||||
char msg[512];
|
||||
if(is_capture_target_monitor(recording_capture_target.c_str()))
|
||||
snprintf(msg, sizeof(msg), "Saved a%sreplay of this monitor", duration);
|
||||
else
|
||||
snprintf(msg, sizeof(msg), "Saved a%sreplay of %s", duration, recording_capture_target.c_str());
|
||||
show_notification(msg, notification_timeout_seconds, mgl::Color(255, 255, 255), get_color_theme().tint_color, NotificationType::REPLAY, recording_capture_target.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void Overlay::update_gsr_replay_save() {
|
||||
void Overlay::process_gsr_output() {
|
||||
if(replay_save_show_notification && replay_save_clock.get_elapsed_time_seconds() >= replay_saving_notification_timeout_seconds) {
|
||||
replay_save_show_notification = false;
|
||||
show_notification("Saving replay, this might take some time", notification_timeout_seconds, mgl::Color(255, 255, 255), get_color_theme().tint_color, NotificationType::REPLAY);
|
||||
@@ -1644,15 +1709,36 @@ namespace gsr {
|
||||
|
||||
if(gpu_screen_recorder_process_output_file) {
|
||||
char buffer[1024];
|
||||
char *replay_saved_filepath = fgets(buffer, sizeof(buffer), gpu_screen_recorder_process_output_file);
|
||||
if(!replay_saved_filepath || replay_saved_filepath[0] == '\0')
|
||||
char *line = fgets(buffer, sizeof(buffer), gpu_screen_recorder_process_output_file);
|
||||
if(!line || line[0] == '\0')
|
||||
return;
|
||||
|
||||
const int line_len = strlen(replay_saved_filepath);
|
||||
if(replay_saved_filepath[line_len - 1] == '\n')
|
||||
replay_saved_filepath[line_len - 1] = '\0';
|
||||
const int line_len = strlen(line);
|
||||
if(line[line_len - 1] == '\n')
|
||||
line[line_len - 1] = '\0';
|
||||
|
||||
on_replay_saved(replay_saved_filepath);
|
||||
if(starts_with({line, (size_t)line_len}, "Error: ")) {
|
||||
show_notification(line + 7, notification_error_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), recording_status_to_notification_type(recording_status));
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string video_filepath = filepath_get_filename(line);
|
||||
if(starts_with(video_filepath, "Video_")) {
|
||||
on_stop_recording(0, line);
|
||||
return;
|
||||
}
|
||||
|
||||
switch(recording_status) {
|
||||
case RecordingStatus::NONE:
|
||||
break;
|
||||
case RecordingStatus::REPLAY:
|
||||
on_replay_saved(line);
|
||||
break;
|
||||
case RecordingStatus::RECORD:
|
||||
break;
|
||||
case RecordingStatus::STREAM:
|
||||
break;
|
||||
}
|
||||
} else if(gpu_screen_recorder_process_output_fd > 0) {
|
||||
char buffer[1024];
|
||||
read(gpu_screen_recorder_process_output_fd, buffer, sizeof(buffer));
|
||||
@@ -1679,6 +1765,7 @@ namespace gsr {
|
||||
case RecordingStatus::NONE:
|
||||
break;
|
||||
case RecordingStatus::REPLAY: {
|
||||
replay_save_duration_min = 0;
|
||||
update_ui_replay_stopped();
|
||||
if(exit_code == 0) {
|
||||
if(config.replay_config.show_replay_stopped_notifications)
|
||||
@@ -1691,7 +1778,7 @@ namespace gsr {
|
||||
}
|
||||
case RecordingStatus::RECORD: {
|
||||
update_ui_recording_stopped();
|
||||
on_stop_recording(exit_code);
|
||||
on_stop_recording(exit_code, record_filepath);
|
||||
break;
|
||||
}
|
||||
case RecordingStatus::STREAM: {
|
||||
@@ -1729,12 +1816,11 @@ namespace gsr {
|
||||
if(config.screenshot_config.save_screenshot_in_game_folder) {
|
||||
save_video_in_current_game_directory(screenshot_filepath.c_str(), NotificationType::SCREENSHOT);
|
||||
} else {
|
||||
const std::string filename = filepath_get_filename(screenshot_filepath.c_str());
|
||||
char msg[512];
|
||||
if(is_capture_target_monitor(screenshot_capture_target.c_str()))
|
||||
snprintf(msg, sizeof(msg), "Saved a screenshot of this monitor to '%s'", filename.c_str());
|
||||
snprintf(msg, sizeof(msg), "Saved a screenshot of this monitor");
|
||||
else
|
||||
snprintf(msg, sizeof(msg), "Saved a screenshot of %s to '%s'", screenshot_capture_target.c_str(), filename.c_str());
|
||||
snprintf(msg, sizeof(msg), "Saved a screenshot of %s", screenshot_capture_target.c_str());
|
||||
show_notification(msg, notification_timeout_seconds, mgl::Color(255, 255, 255), get_color_theme().tint_color, NotificationType::SCREENSHOT, screenshot_capture_target.c_str());
|
||||
}
|
||||
} else {
|
||||
@@ -1827,23 +1913,24 @@ namespace gsr {
|
||||
on_press_start_replay(true, false);
|
||||
}
|
||||
|
||||
void Overlay::on_stop_recording(int exit_code) {
|
||||
void Overlay::on_stop_recording(int exit_code, const std::string &video_filepath) {
|
||||
if(exit_code == 0) {
|
||||
if(config.record_config.save_video_in_game_folder) {
|
||||
save_video_in_current_game_directory(record_filepath.c_str(), NotificationType::RECORD);
|
||||
save_video_in_current_game_directory(video_filepath.c_str(), NotificationType::RECORD);
|
||||
} else {
|
||||
const std::string filename = filepath_get_filename(record_filepath.c_str());
|
||||
char msg[512];
|
||||
if(is_capture_target_monitor(recording_capture_target.c_str()))
|
||||
snprintf(msg, sizeof(msg), "Saved a recording of this monitor to '%s'", filename.c_str());
|
||||
snprintf(msg, sizeof(msg), "Saved a recording of this monitor");
|
||||
else
|
||||
snprintf(msg, sizeof(msg), "Saved a recording of %s to '%s'", recording_capture_target.c_str(), filename.c_str());
|
||||
snprintf(msg, sizeof(msg), "Saved a recording of %s", recording_capture_target.c_str());
|
||||
show_notification(msg, notification_timeout_seconds, mgl::Color(255, 255, 255), get_color_theme().tint_color, NotificationType::RECORD, recording_capture_target.c_str());
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "Warning: gpu-screen-recorder (%d) exited with exit status %d\n", (int)gpu_screen_recorder_process, exit_code);
|
||||
show_notification("Failed to start/save recording. Verify if settings are correct", notification_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), NotificationType::RECORD);
|
||||
}
|
||||
update_ui_recording_stopped();
|
||||
replay_recording = false;
|
||||
}
|
||||
|
||||
void Overlay::update_ui_recording_paused() {
|
||||
@@ -1872,6 +1959,7 @@ namespace gsr {
|
||||
record_dropdown_button_ptr->set_activated(true);
|
||||
record_dropdown_button_ptr->set_description("Recording");
|
||||
record_dropdown_button_ptr->set_item_icon("start", &get_theme().stop_texture);
|
||||
record_dropdown_button_ptr->set_item_enabled("pause", recording_status == RecordingStatus::RECORD);
|
||||
}
|
||||
|
||||
void Overlay::update_ui_recording_stopped() {
|
||||
@@ -1885,7 +1973,9 @@ namespace gsr {
|
||||
|
||||
record_dropdown_button_ptr->set_item_label("pause", "Pause");
|
||||
record_dropdown_button_ptr->set_item_icon("pause", &get_theme().pause_texture);
|
||||
record_dropdown_button_ptr->set_item_enabled("pause", false);
|
||||
paused = false;
|
||||
replay_recording = false;
|
||||
}
|
||||
|
||||
void Overlay::update_ui_streaming_started() {
|
||||
@@ -1906,6 +1996,7 @@ namespace gsr {
|
||||
stream_dropdown_button_ptr->set_activated(false);
|
||||
stream_dropdown_button_ptr->set_description("Not streaming");
|
||||
stream_dropdown_button_ptr->set_item_icon("start", &get_theme().play_texture);
|
||||
update_ui_recording_stopped();
|
||||
}
|
||||
|
||||
void Overlay::update_ui_replay_started() {
|
||||
@@ -1916,6 +2007,9 @@ namespace gsr {
|
||||
replay_dropdown_button_ptr->set_activated(true);
|
||||
replay_dropdown_button_ptr->set_description("On");
|
||||
replay_dropdown_button_ptr->set_item_icon("start", &get_theme().stop_texture);
|
||||
replay_dropdown_button_ptr->set_item_enabled("save", true);
|
||||
replay_dropdown_button_ptr->set_item_enabled("save_1_min", true);
|
||||
replay_dropdown_button_ptr->set_item_enabled("save_10_min", true);
|
||||
}
|
||||
|
||||
void Overlay::update_ui_replay_stopped() {
|
||||
@@ -1926,6 +2020,10 @@ namespace gsr {
|
||||
replay_dropdown_button_ptr->set_activated(false);
|
||||
replay_dropdown_button_ptr->set_description("Off");
|
||||
replay_dropdown_button_ptr->set_item_icon("start", &get_theme().play_texture);
|
||||
replay_dropdown_button_ptr->set_item_enabled("save", false);
|
||||
replay_dropdown_button_ptr->set_item_enabled("save_1_min", false);
|
||||
replay_dropdown_button_ptr->set_item_enabled("save_10_min", false);
|
||||
update_ui_recording_stopped();
|
||||
}
|
||||
|
||||
static std::string get_date_str() {
|
||||
@@ -2043,8 +2141,25 @@ namespace gsr {
|
||||
cursor_info = cursor_tracker->get_latest_cursor_info();
|
||||
}
|
||||
|
||||
if(cursor_info)
|
||||
return cursor_info->monitor_name;
|
||||
std::string focused_monitor_name;
|
||||
if(cursor_info) {
|
||||
focused_monitor_name = std::move(cursor_info->monitor_name);
|
||||
} else {
|
||||
mgl_context *context = mgl_get_context();
|
||||
Display *display = (Display*)context->connection;
|
||||
|
||||
Window x11_cursor_window = None;
|
||||
mgl::vec2i cursor_position = get_cursor_position(display, &x11_cursor_window);
|
||||
|
||||
const mgl::vec2i monitor_position_query_value = (x11_cursor_window || gsr_info.system_info.display_server != DisplayServer::WAYLAND) ? cursor_position : create_window_get_center_position(display);
|
||||
auto monitors = get_monitors(display);
|
||||
const Monitor *focused_monitor = find_monitor_at_position(monitors, monitor_position_query_value);
|
||||
if(focused_monitor)
|
||||
focused_monitor_name = focused_monitor->name;
|
||||
}
|
||||
|
||||
if(!focused_monitor_name.empty())
|
||||
return focused_monitor_name;
|
||||
else if(!capture_options.monitors.empty())
|
||||
return capture_options.monitors.front().name;
|
||||
else
|
||||
@@ -2054,15 +2169,47 @@ namespace gsr {
|
||||
}
|
||||
}
|
||||
|
||||
void Overlay::prepare_gsr_output_for_reading() {
|
||||
if(gpu_screen_recorder_process_output_fd <= 0)
|
||||
return;
|
||||
|
||||
const int fdl = fcntl(gpu_screen_recorder_process_output_fd, F_GETFL);
|
||||
fcntl(gpu_screen_recorder_process_output_fd, F_SETFL, fdl | O_NONBLOCK);
|
||||
gpu_screen_recorder_process_output_file = fdopen(gpu_screen_recorder_process_output_fd, "r");
|
||||
if(gpu_screen_recorder_process_output_file)
|
||||
gpu_screen_recorder_process_output_fd = -1;
|
||||
}
|
||||
|
||||
void Overlay::on_press_save_replay() {
|
||||
if(recording_status != RecordingStatus::REPLAY || gpu_screen_recorder_process <= 0)
|
||||
return;
|
||||
|
||||
replay_save_duration_min = 0;
|
||||
replay_save_show_notification = true;
|
||||
replay_save_clock.restart();
|
||||
kill(gpu_screen_recorder_process, SIGUSR1);
|
||||
}
|
||||
|
||||
void Overlay::on_press_save_replay_1_min_replay() {
|
||||
if(recording_status != RecordingStatus::REPLAY || gpu_screen_recorder_process <= 0)
|
||||
return;
|
||||
|
||||
replay_save_duration_min = 1;
|
||||
replay_save_show_notification = true;
|
||||
replay_save_clock.restart();
|
||||
kill(gpu_screen_recorder_process, SIGRTMIN+3);
|
||||
}
|
||||
|
||||
void Overlay::on_press_save_replay_10_min_replay() {
|
||||
if(recording_status != RecordingStatus::REPLAY || gpu_screen_recorder_process <= 0)
|
||||
return;
|
||||
|
||||
replay_save_duration_min = 10;
|
||||
replay_save_show_notification = true;
|
||||
replay_save_clock.restart();
|
||||
kill(gpu_screen_recorder_process, SIGRTMIN+5);
|
||||
}
|
||||
|
||||
bool Overlay::on_press_start_replay(bool disable_notification, bool finished_region_selection) {
|
||||
if(region_selector.is_started())
|
||||
return false;
|
||||
@@ -2072,10 +2219,10 @@ namespace gsr {
|
||||
case RecordingStatus::REPLAY:
|
||||
break;
|
||||
case RecordingStatus::RECORD:
|
||||
show_notification("Unable to start replay when recording.\nStop recording before starting replay.", notification_error_timeout_seconds, mgl::Color(255, 255, 255), get_color_theme().tint_color, NotificationType::RECORD);
|
||||
show_notification("Unable to start replay when recording.\nStop recording before starting replay.", notification_error_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), NotificationType::RECORD);
|
||||
return false;
|
||||
case RecordingStatus::STREAM:
|
||||
show_notification("Unable to start replay when streaming.\nStop streaming before starting replay.", notification_error_timeout_seconds, mgl::Color(255, 255, 255), get_color_theme().tint_color, NotificationType::STREAM);
|
||||
show_notification("Unable to start replay when streaming.\nStop streaming before starting replay.", notification_error_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), NotificationType::STREAM);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -2083,9 +2230,6 @@ namespace gsr {
|
||||
replay_save_show_notification = false;
|
||||
try_replay_startup = false;
|
||||
|
||||
// window->close();
|
||||
// usleep(1000 * 50); // 50 milliseconds
|
||||
|
||||
close_gpu_screen_recorder_output();
|
||||
|
||||
if(gpu_screen_recorder_process > 0) {
|
||||
@@ -2098,6 +2242,7 @@ namespace gsr {
|
||||
|
||||
gpu_screen_recorder_process = -1;
|
||||
recording_status = RecordingStatus::NONE;
|
||||
replay_save_duration_min = 0;
|
||||
update_ui_replay_stopped();
|
||||
|
||||
// TODO: Show this with a slight delay to make sure it doesn't show up in the video
|
||||
@@ -2108,11 +2253,11 @@ namespace gsr {
|
||||
}
|
||||
|
||||
const SupportedCaptureOptions capture_options = get_supported_capture_options(gsr_info);
|
||||
replay_capture_target = get_capture_target(config.replay_config.record_options.record_area_option, capture_options);
|
||||
if(!validate_capture_target(replay_capture_target, capture_options)) {
|
||||
recording_capture_target = get_capture_target(config.replay_config.record_options.record_area_option, capture_options);
|
||||
if(!validate_capture_target(recording_capture_target, capture_options)) {
|
||||
char err_msg[256];
|
||||
snprintf(err_msg, sizeof(err_msg), "Failed to start replay, capture target \"%s\" is invalid. Please change capture target in settings", replay_capture_target.c_str());
|
||||
show_notification(err_msg, notification_error_timeout_seconds, mgl::Color(255, 0, 0, 0), mgl::Color(255, 0, 0, 0), NotificationType::REPLAY);
|
||||
snprintf(err_msg, sizeof(err_msg), "Failed to start replay, capture target \"%s\" is invalid. Please change capture target in settings", recording_capture_target.c_str());
|
||||
show_notification(err_msg, notification_error_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), NotificationType::REPLAY);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -2147,7 +2292,7 @@ namespace gsr {
|
||||
snprintf(size, sizeof(size), "%dx%d", (int)config.replay_config.record_options.video_width, (int)config.replay_config.record_options.video_height);
|
||||
|
||||
std::vector<const char*> args = {
|
||||
"gpu-screen-recorder", "-w", replay_capture_target.c_str(),
|
||||
"gpu-screen-recorder", "-w", recording_capture_target.c_str(),
|
||||
"-c", config.replay_config.container.c_str(),
|
||||
"-ac", config.replay_config.record_options.audio_codec.c_str(),
|
||||
"-cursor", config.replay_config.record_options.record_cursor ? "yes" : "no",
|
||||
@@ -2166,24 +2311,31 @@ namespace gsr {
|
||||
args.push_back("yes");
|
||||
}
|
||||
|
||||
if(gsr_info.system_info.gsr_version >= GsrVersion{5, 5, 0}) {
|
||||
args.push_back("-replay-storage");
|
||||
args.push_back(config.replay_config.replay_storage.c_str());
|
||||
}
|
||||
|
||||
char region_str[128];
|
||||
add_common_gpu_screen_recorder_args(args, config.replay_config.record_options, audio_tracks, video_bitrate, size, region_str, sizeof(region_str), region_selector);
|
||||
|
||||
if(gsr_info.system_info.gsr_version >= GsrVersion{5, 4, 0}) {
|
||||
args.push_back("-ro");
|
||||
args.push_back(config.record_config.save_directory.c_str());
|
||||
}
|
||||
|
||||
args.push_back(nullptr);
|
||||
|
||||
gpu_screen_recorder_process = exec_program(args.data(), &gpu_screen_recorder_process_output_fd);
|
||||
if(gpu_screen_recorder_process == -1) {
|
||||
// TODO: Show notification failed to start
|
||||
show_notification("Failed to launch gpu-screen-recorder to start replay", notification_error_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), NotificationType::REPLAY);
|
||||
return false;
|
||||
} else {
|
||||
recording_status = RecordingStatus::REPLAY;
|
||||
update_ui_replay_started();
|
||||
}
|
||||
|
||||
const int fdl = fcntl(gpu_screen_recorder_process_output_fd, F_GETFL);
|
||||
fcntl(gpu_screen_recorder_process_output_fd, F_SETFL, fdl | O_NONBLOCK);
|
||||
gpu_screen_recorder_process_output_file = fdopen(gpu_screen_recorder_process_output_fd, "r");
|
||||
if(gpu_screen_recorder_process_output_file)
|
||||
gpu_screen_recorder_process_output_fd = -1;
|
||||
prepare_gsr_output_for_reading();
|
||||
|
||||
// TODO: Start recording after this notification has disappeared to make sure it doesn't show up in the video.
|
||||
// Make clear to the user that the recording starts after the notification is gone.
|
||||
@@ -2196,11 +2348,11 @@ namespace gsr {
|
||||
// to see when the program has exit.
|
||||
if(!disable_notification && config.replay_config.show_replay_started_notifications) {
|
||||
char msg[256];
|
||||
if(is_capture_target_monitor(replay_capture_target.c_str()))
|
||||
if(is_capture_target_monitor(recording_capture_target.c_str()))
|
||||
snprintf(msg, sizeof(msg), "Started replaying this monitor");
|
||||
else
|
||||
snprintf(msg, sizeof(msg), "Started replaying %s", replay_capture_target.c_str());
|
||||
show_notification(msg, notification_timeout_seconds, get_color_theme().tint_color, get_color_theme().tint_color, NotificationType::REPLAY, replay_capture_target.c_str());
|
||||
snprintf(msg, sizeof(msg), "Started replaying %s", recording_capture_target.c_str());
|
||||
show_notification(msg, notification_timeout_seconds, get_color_theme().tint_color, get_color_theme().tint_color, NotificationType::REPLAY, recording_capture_target.c_str());
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -2214,18 +2366,45 @@ namespace gsr {
|
||||
case RecordingStatus::NONE:
|
||||
case RecordingStatus::RECORD:
|
||||
break;
|
||||
case RecordingStatus::REPLAY:
|
||||
show_notification("Unable to start recording when replay is turned on.\nTurn off replay before starting recording.", notification_error_timeout_seconds, mgl::Color(255, 255, 255), get_color_theme().tint_color, NotificationType::REPLAY);
|
||||
case RecordingStatus::REPLAY: {
|
||||
if(gpu_screen_recorder_process <= 0)
|
||||
return;
|
||||
|
||||
if(gsr_info.system_info.gsr_version >= GsrVersion{5, 4, 0}) {
|
||||
if(!replay_recording) {
|
||||
if(config.record_config.show_recording_started_notifications)
|
||||
show_notification("Started recording in the replay session", notification_timeout_seconds, get_color_theme().tint_color, get_color_theme().tint_color, NotificationType::RECORD);
|
||||
update_ui_recording_started();
|
||||
}
|
||||
replay_recording = true;
|
||||
kill(gpu_screen_recorder_process, SIGRTMIN);
|
||||
} else {
|
||||
show_notification("Unable to start recording when replay is turned on.\nTurn off replay before starting recording.", notification_error_timeout_seconds, mgl::Color(255, 0, 0), get_color_theme().tint_color, NotificationType::REPLAY);
|
||||
}
|
||||
return;
|
||||
case RecordingStatus::STREAM:
|
||||
show_notification("Unable to start recording when streaming.\nStop streaming before starting recording.", notification_error_timeout_seconds, mgl::Color(255, 255, 255), get_color_theme().tint_color, NotificationType::STREAM);
|
||||
}
|
||||
case RecordingStatus::STREAM: {
|
||||
if(gpu_screen_recorder_process <= 0)
|
||||
return;
|
||||
|
||||
if(gsr_info.system_info.gsr_version >= GsrVersion{5, 4, 0}) {
|
||||
if(!replay_recording) {
|
||||
if(config.record_config.show_recording_started_notifications)
|
||||
show_notification("Started recording in the streaming session", notification_timeout_seconds, get_color_theme().tint_color, get_color_theme().tint_color, NotificationType::RECORD);
|
||||
update_ui_recording_started();
|
||||
}
|
||||
replay_recording = true;
|
||||
kill(gpu_screen_recorder_process, SIGRTMIN);
|
||||
} else {
|
||||
show_notification("Unable to start recording when streaming.\nStop streaming before starting recording.", notification_error_timeout_seconds, mgl::Color(255, 0, 0), get_color_theme().tint_color, NotificationType::STREAM);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
paused = false;
|
||||
|
||||
// window->close();
|
||||
// usleep(1000 * 50); // 50 milliseconds
|
||||
close_gpu_screen_recorder_output();
|
||||
|
||||
if(gpu_screen_recorder_process > 0) {
|
||||
kill(gpu_screen_recorder_process, SIGINT);
|
||||
@@ -2237,7 +2416,7 @@ namespace gsr {
|
||||
int exit_code = -1;
|
||||
if(WIFEXITED(status))
|
||||
exit_code = WEXITSTATUS(status);
|
||||
on_stop_recording(exit_code);
|
||||
on_stop_recording(exit_code, record_filepath);
|
||||
}
|
||||
|
||||
gpu_screen_recorder_process = -1;
|
||||
@@ -2252,7 +2431,7 @@ namespace gsr {
|
||||
if(!validate_capture_target(config.record_config.record_options.record_area_option, capture_options)) {
|
||||
char err_msg[256];
|
||||
snprintf(err_msg, sizeof(err_msg), "Failed to start recording, capture target \"%s\" is invalid. Please change capture target in settings", recording_capture_target.c_str());
|
||||
show_notification(err_msg, notification_error_timeout_seconds, mgl::Color(255, 0, 0, 0), mgl::Color(255, 0, 0, 0), NotificationType::RECORD);
|
||||
show_notification(err_msg, notification_error_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), NotificationType::RECORD);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2307,14 +2486,17 @@ namespace gsr {
|
||||
args.push_back(nullptr);
|
||||
|
||||
record_filepath = output_file;
|
||||
gpu_screen_recorder_process = exec_program(args.data(), nullptr);
|
||||
gpu_screen_recorder_process = exec_program(args.data(), &gpu_screen_recorder_process_output_fd);
|
||||
if(gpu_screen_recorder_process == -1) {
|
||||
// TODO: Show notification failed to start
|
||||
show_notification("Failed to launch gpu-screen-recorder to start recording", notification_error_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), NotificationType::RECORD);
|
||||
return;
|
||||
} else {
|
||||
recording_status = RecordingStatus::RECORD;
|
||||
update_ui_recording_started();
|
||||
}
|
||||
|
||||
prepare_gsr_output_for_reading();
|
||||
|
||||
// TODO: Start recording after this notification has disappeared to make sure it doesn't show up in the video.
|
||||
// Make clear to the user that the recording starts after the notification is gone.
|
||||
// Maybe have the option in notification to show timer until its getting hidden, then the notification can say:
|
||||
@@ -2372,17 +2554,16 @@ namespace gsr {
|
||||
case RecordingStatus::STREAM:
|
||||
break;
|
||||
case RecordingStatus::REPLAY:
|
||||
show_notification("Unable to start streaming when replay is turned on.\nTurn off replay before starting streaming.", notification_error_timeout_seconds, mgl::Color(255, 255, 255), get_color_theme().tint_color, NotificationType::REPLAY);
|
||||
show_notification("Unable to start streaming when replay is turned on.\nTurn off replay before starting streaming.", notification_error_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), NotificationType::REPLAY);
|
||||
return;
|
||||
case RecordingStatus::RECORD:
|
||||
show_notification("Unable to start streaming when recording.\nStop recording before starting streaming.", notification_error_timeout_seconds, mgl::Color(255, 255, 255), get_color_theme().tint_color, NotificationType::RECORD);
|
||||
show_notification("Unable to start streaming when recording.\nStop recording before starting streaming.", notification_error_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), NotificationType::RECORD);
|
||||
return;
|
||||
}
|
||||
|
||||
paused = false;
|
||||
|
||||
// window->close();
|
||||
// usleep(1000 * 50); // 50 milliseconds
|
||||
close_gpu_screen_recorder_output();
|
||||
|
||||
if(gpu_screen_recorder_process > 0) {
|
||||
kill(gpu_screen_recorder_process, SIGINT);
|
||||
@@ -2403,11 +2584,11 @@ namespace gsr {
|
||||
}
|
||||
|
||||
const SupportedCaptureOptions capture_options = get_supported_capture_options(gsr_info);
|
||||
const std::string capture_target = get_capture_target(config.streaming_config.record_options.record_area_option, capture_options);
|
||||
recording_capture_target = get_capture_target(config.streaming_config.record_options.record_area_option, capture_options);
|
||||
if(!validate_capture_target(config.streaming_config.record_options.record_area_option, capture_options)) {
|
||||
char err_msg[256];
|
||||
snprintf(err_msg, sizeof(err_msg), "Failed to start streaming, capture target \"%s\" is invalid. Please change capture target in settings", capture_target.c_str());
|
||||
show_notification(err_msg, notification_error_timeout_seconds, mgl::Color(255, 0, 0, 0), mgl::Color(255, 0, 0, 0), NotificationType::STREAM);
|
||||
snprintf(err_msg, sizeof(err_msg), "Failed to start streaming, capture target \"%s\" is invalid. Please change capture target in settings", recording_capture_target.c_str());
|
||||
show_notification(err_msg, notification_error_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), NotificationType::STREAM);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2450,7 +2631,7 @@ namespace gsr {
|
||||
snprintf(size, sizeof(size), "%dx%d", (int)config.streaming_config.record_options.video_width, (int)config.streaming_config.record_options.video_height);
|
||||
|
||||
std::vector<const char*> args = {
|
||||
"gpu-screen-recorder", "-w", capture_target.c_str(),
|
||||
"gpu-screen-recorder", "-w", recording_capture_target.c_str(),
|
||||
"-c", container.c_str(),
|
||||
"-ac", config.streaming_config.record_options.audio_codec.c_str(),
|
||||
"-cursor", config.streaming_config.record_options.record_cursor ? "yes" : "no",
|
||||
@@ -2465,16 +2646,24 @@ namespace gsr {
|
||||
char region_str[128];
|
||||
add_common_gpu_screen_recorder_args(args, config.streaming_config.record_options, audio_tracks, video_bitrate, size, region_str, sizeof(region_str), region_selector);
|
||||
|
||||
if(gsr_info.system_info.gsr_version >= GsrVersion{5, 4, 0}) {
|
||||
args.push_back("-ro");
|
||||
args.push_back(config.record_config.save_directory.c_str());
|
||||
}
|
||||
|
||||
args.push_back(nullptr);
|
||||
|
||||
gpu_screen_recorder_process = exec_program(args.data(), nullptr);
|
||||
gpu_screen_recorder_process = exec_program(args.data(), &gpu_screen_recorder_process_output_fd);
|
||||
if(gpu_screen_recorder_process == -1) {
|
||||
// TODO: Show notification failed to start
|
||||
show_notification("Failed to launch gpu-screen-recorder to start streaming", notification_error_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), NotificationType::STREAM);
|
||||
return;
|
||||
} else {
|
||||
recording_status = RecordingStatus::STREAM;
|
||||
update_ui_streaming_started();
|
||||
}
|
||||
|
||||
prepare_gsr_output_for_reading();
|
||||
|
||||
// TODO: Start recording after this notification has disappeared to make sure it doesn't show up in the video.
|
||||
// Make clear to the user that the recording starts after the notification is gone.
|
||||
// Maybe have the option in notification to show timer until its getting hidden, then the notification can say:
|
||||
@@ -2486,11 +2675,11 @@ namespace gsr {
|
||||
// to see when the program has exit.
|
||||
if(config.streaming_config.show_streaming_started_notifications) {
|
||||
char msg[256];
|
||||
if(is_capture_target_monitor(capture_target.c_str()))
|
||||
if(is_capture_target_monitor(recording_capture_target.c_str()))
|
||||
snprintf(msg, sizeof(msg), "Started streaming this monitor");
|
||||
else
|
||||
snprintf(msg, sizeof(msg), "Started streaming %s", capture_target.c_str());
|
||||
show_notification(msg, notification_timeout_seconds, get_color_theme().tint_color, get_color_theme().tint_color, NotificationType::STREAM, capture_target.c_str());
|
||||
snprintf(msg, sizeof(msg), "Started streaming %s", recording_capture_target.c_str());
|
||||
show_notification(msg, notification_timeout_seconds, get_color_theme().tint_color, get_color_theme().tint_color, NotificationType::STREAM, recording_capture_target.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2510,7 +2699,7 @@ namespace gsr {
|
||||
if(!validate_capture_target(record_area_option, capture_options)) {
|
||||
char err_msg[256];
|
||||
snprintf(err_msg, sizeof(err_msg), "Failed to take a screenshot, capture target \"%s\" is invalid. Please change capture target in settings", screenshot_capture_target.c_str());
|
||||
show_notification(err_msg, notification_error_timeout_seconds, mgl::Color(255, 0, 0, 0), mgl::Color(255, 0, 0, 0), NotificationType::SCREENSHOT);
|
||||
show_notification(err_msg, notification_error_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), NotificationType::SCREENSHOT);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2556,7 +2745,7 @@ namespace gsr {
|
||||
screenshot_filepath = output_file;
|
||||
gpu_screen_recorder_screenshot_process = exec_program(args.data(), nullptr);
|
||||
if(gpu_screen_recorder_screenshot_process == -1) {
|
||||
// TODO: Show notification failed to start
|
||||
show_notification("Failed to launch gpu-screen-recorder to take a screenshot", notification_error_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), NotificationType::SCREENSHOT);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -130,8 +130,6 @@ namespace gsr {
|
||||
exit_status = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
buffer[bytes_read] = '\0';
|
||||
result.append(buffer, bytes_read);
|
||||
}
|
||||
|
||||
|
||||
@@ -135,6 +135,12 @@ namespace gsr {
|
||||
if(!theme->ps4_dpad_right_texture.load_from_file((resources_path + "images/ps4_dpad_right.png").c_str(), mgl::Texture::LoadOptions{false, false, MGL_TEXTURE_SCALE_LINEAR_MIPMAP}))
|
||||
goto error;
|
||||
|
||||
if(!theme->ps4_cross_texture.load_from_file((resources_path + "images/ps4_cross.png").c_str(), mgl::Texture::LoadOptions{false, false, MGL_TEXTURE_SCALE_LINEAR_MIPMAP}))
|
||||
goto error;
|
||||
|
||||
if(!theme->ps4_triangle_texture.load_from_file((resources_path + "images/ps4_triangle.png").c_str(), mgl::Texture::LoadOptions{false, false, MGL_TEXTURE_SCALE_LINEAR_MIPMAP}))
|
||||
goto error;
|
||||
|
||||
return true;
|
||||
|
||||
error:
|
||||
|
||||
@@ -27,6 +27,11 @@ namespace gsr {
|
||||
return str.size() >= len && memcmp(str.data(), substr, len) == 0;
|
||||
}
|
||||
|
||||
bool ends_with(std::string_view str, const char *substr) {
|
||||
size_t len = strlen(substr);
|
||||
return str.size() >= len && memcmp(str.data() + str.size() - len, substr, len) == 0;
|
||||
}
|
||||
|
||||
std::string get_home_dir() {
|
||||
const char *home_dir = getenv("HOME");
|
||||
if(!home_dir) {
|
||||
|
||||
@@ -85,7 +85,7 @@ namespace gsr {
|
||||
|
||||
void ComboBox::add_item(const std::string &text, const std::string &id) {
|
||||
items.push_back({mgl::Text(text, *font), id, {0.0f, 0.0f}});
|
||||
items.back().text.set_max_width(font->get_character_size() * 22); // TODO: Make a proper solution
|
||||
items.back().text.set_max_width(font->get_character_size() * 20); // TODO: Make a proper solution
|
||||
//items.back().text.set_max_rows(1);
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
@@ -110,6 +110,14 @@ namespace gsr {
|
||||
window.draw(rect);
|
||||
}
|
||||
|
||||
if(activated) {
|
||||
description.set_color(get_color_theme().tint_color);
|
||||
icon_sprite.set_color(get_color_theme().tint_color);
|
||||
} else {
|
||||
description.set_color(mgl::Color(150, 150, 150));
|
||||
icon_sprite.set_color(mgl::Color(255, 255, 255));
|
||||
}
|
||||
|
||||
const int text_margin = size.y * 0.085;
|
||||
|
||||
const auto title_bounds = title.get_bounds();
|
||||
@@ -148,7 +156,7 @@ namespace gsr {
|
||||
window.draw(separator);
|
||||
}
|
||||
|
||||
if(mouse_inside_item == -1) {
|
||||
if(mouse_inside_item == -1 && item.enabled) {
|
||||
const bool inside = mgl::FloatRect(item_position, item_size).contains({ (float)mouse_pos.x, (float)mouse_pos.y });
|
||||
if(inside) {
|
||||
draw_rectangle_outline(window, item_position, item_size, get_color_theme().tint_color, border_size);
|
||||
@@ -161,16 +169,18 @@ namespace gsr {
|
||||
mgl::Sprite icon(item.icon_texture);
|
||||
icon.set_height((int)(item_size.y * 0.4f));
|
||||
icon.set_position((item_position + mgl::vec2f(padding_left, item_size.y * 0.5f - icon.get_size().y * 0.5f)).floor());
|
||||
icon.set_color(item.enabled ? mgl::Color(255, 255, 255, 255) : mgl::Color(255, 255, 255, 80));
|
||||
window.draw(icon);
|
||||
icon_offset = icon.get_size().x + icon_spacing;
|
||||
}
|
||||
|
||||
item.text.set_position((item_position + mgl::vec2f(padding_left + icon_offset, item_size.y * 0.5f - text_bounds.size.y * 0.5f)).floor());
|
||||
item.text.set_color(item.enabled ? mgl::Color(255, 255, 255, 255) : mgl::Color(255, 255, 255, 80));
|
||||
window.draw(item.text);
|
||||
|
||||
const auto description_bounds = item.description_text.get_bounds();
|
||||
item.description_text.set_position((item_position + mgl::vec2f(item_size.x - description_bounds.size.x - padding_right, item_size.y * 0.5f - description_bounds.size.y * 0.5f)).floor());
|
||||
item.description_text.set_color(mgl::Color(255, 255, 255, 120));
|
||||
item.description_text.set_color(item.enabled ? mgl::Color(255, 255, 255, 120) : mgl::Color(255, 255, 255, 40));
|
||||
window.draw(item.description_text);
|
||||
|
||||
item_position.y += item_size.y;
|
||||
@@ -179,6 +189,10 @@ namespace gsr {
|
||||
}
|
||||
|
||||
void DropdownButton::add_item(const std::string &text, const std::string &id, const std::string &description) {
|
||||
for(auto &item : items) {
|
||||
if(item.id == id)
|
||||
return;
|
||||
}
|
||||
items.push_back({mgl::Text(text, *title_font), mgl::Text(description, *description_font), nullptr, id});
|
||||
dirty = true;
|
||||
}
|
||||
@@ -210,6 +224,15 @@ namespace gsr {
|
||||
}
|
||||
}
|
||||
|
||||
void DropdownButton::set_item_enabled(const std::string &id, bool enabled) {
|
||||
for(auto &item : items) {
|
||||
if(item.id == id) {
|
||||
item.enabled = enabled;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DropdownButton::set_description(std::string description_text) {
|
||||
description.set_string(std::move(description_text));
|
||||
}
|
||||
@@ -219,14 +242,6 @@ namespace gsr {
|
||||
return;
|
||||
|
||||
this->activated = activated;
|
||||
|
||||
if(activated) {
|
||||
description.set_color(get_color_theme().tint_color);
|
||||
icon_sprite.set_color(get_color_theme().tint_color);
|
||||
} else {
|
||||
description.set_color(mgl::Color(150, 150, 150));
|
||||
icon_sprite.set_color(mgl::Color(255, 255, 255));
|
||||
}
|
||||
}
|
||||
|
||||
void DropdownButton::update_if_dirty() {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#include "../../include/gui/GlobalSettingsPage.hpp"
|
||||
|
||||
#include "../../include/Overlay.hpp"
|
||||
#include "../../include/GlobalHotkeys.hpp"
|
||||
#include "../../include/Theme.hpp"
|
||||
#include "../../include/Process.hpp"
|
||||
#include "../../include/gui/GsrPage.hpp"
|
||||
@@ -256,6 +255,30 @@ namespace gsr {
|
||||
return list;
|
||||
}
|
||||
|
||||
std::unique_ptr<List> GlobalSettingsPage::create_replay_partial_save_hotkey_options() {
|
||||
auto list = std::make_unique<List>(List::Orientation::HORIZONTAL, List::Alignment::CENTER);
|
||||
|
||||
list->add_widget(std::make_unique<Label>(&get_theme().body_font, "Save 1 minute replay:", get_color_theme().text_color));
|
||||
auto save_replay_1_min_button = std::make_unique<Button>(&get_theme().body_font, "", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
|
||||
save_replay_1_min_button_ptr = save_replay_1_min_button.get();
|
||||
list->add_widget(std::move(save_replay_1_min_button));
|
||||
|
||||
list->add_widget(std::make_unique<Label>(&get_theme().body_font, "Save 10 minute replay:", get_color_theme().text_color));
|
||||
auto save_replay_10_min_button = std::make_unique<Button>(&get_theme().body_font, "", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
|
||||
save_replay_10_min_button_ptr = save_replay_10_min_button.get();
|
||||
list->add_widget(std::move(save_replay_10_min_button));
|
||||
|
||||
save_replay_1_min_button_ptr->on_click = [this] {
|
||||
configure_hotkey_start(ConfigureHotkeyType::REPLAY_SAVE_1_MIN);
|
||||
};
|
||||
|
||||
save_replay_10_min_button_ptr->on_click = [this] {
|
||||
configure_hotkey_start(ConfigureHotkeyType::REPLAY_SAVE_10_MIN);
|
||||
};
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
std::unique_ptr<List> GlobalSettingsPage::create_record_hotkey_options() {
|
||||
auto list = std::make_unique<List>(List::Orientation::HORIZONTAL, List::Alignment::CENTER);
|
||||
|
||||
@@ -335,6 +358,8 @@ namespace gsr {
|
||||
config.record_config.pause_unpause_hotkey = {mgl::Keyboard::Unknown, 0};
|
||||
config.replay_config.start_stop_hotkey = {mgl::Keyboard::Unknown, 0};
|
||||
config.replay_config.save_hotkey = {mgl::Keyboard::Unknown, 0};
|
||||
config.replay_config.save_1_min_hotkey = {mgl::Keyboard::Unknown, 0};
|
||||
config.replay_config.save_10_min_hotkey = {mgl::Keyboard::Unknown, 0};
|
||||
config.screenshot_config.take_screenshot_hotkey = {mgl::Keyboard::Unknown, 0};
|
||||
config.screenshot_config.take_screenshot_region_hotkey = {mgl::Keyboard::Unknown, 0};
|
||||
config.main_config.show_hide_hotkey = {mgl::Keyboard::Unknown, 0};
|
||||
@@ -374,6 +399,7 @@ namespace gsr {
|
||||
list_ptr->add_widget(std::make_unique<LineSeparator>(LineSeparator::Orientation::HORIZONTAL, subsection->get_inner_size().x));
|
||||
list_ptr->add_widget(create_show_hide_hotkey_options());
|
||||
list_ptr->add_widget(create_replay_hotkey_options());
|
||||
list_ptr->add_widget(create_replay_partial_save_hotkey_options());
|
||||
list_ptr->add_widget(create_record_hotkey_options());
|
||||
list_ptr->add_widget(create_stream_hotkey_options());
|
||||
list_ptr->add_widget(create_screenshot_hotkey_options());
|
||||
@@ -393,6 +419,8 @@ namespace gsr {
|
||||
list_ptr->add_widget(create_joystick_hotkey_text(&get_theme().ps4_home_texture, &get_theme().ps4_options_texture, get_theme().body_font.get_character_size(), "to show/hide the UI"));
|
||||
list_ptr->add_widget(create_joystick_hotkey_text(&get_theme().ps4_home_texture, &get_theme().ps4_dpad_up_texture, get_theme().body_font.get_character_size(), "to take a screenshot"));
|
||||
list_ptr->add_widget(create_joystick_hotkey_text(&get_theme().ps4_home_texture, &get_theme().ps4_dpad_down_texture, get_theme().body_font.get_character_size(), "to save a replay"));
|
||||
list_ptr->add_widget(create_joystick_hotkey_text(&get_theme().ps4_home_texture, &get_theme().ps4_cross_texture, get_theme().body_font.get_character_size(), "to save a 1 minute replay"));
|
||||
list_ptr->add_widget(create_joystick_hotkey_text(&get_theme().ps4_home_texture, &get_theme().ps4_triangle_texture, get_theme().body_font.get_character_size(), "to save a 10 minute replay"));
|
||||
list_ptr->add_widget(create_joystick_hotkey_text(&get_theme().ps4_home_texture, &get_theme().ps4_dpad_left_texture, get_theme().body_font.get_character_size(), "to start/stop recording"));
|
||||
list_ptr->add_widget(create_joystick_hotkey_text(&get_theme().ps4_home_texture, &get_theme().ps4_dpad_right_texture, get_theme().body_font.get_character_size(), "to turn replay on/off"));
|
||||
return subsection;
|
||||
@@ -490,6 +518,8 @@ namespace gsr {
|
||||
void GlobalSettingsPage::load_hotkeys() {
|
||||
turn_replay_on_off_button_ptr->set_text(config.replay_config.start_stop_hotkey.to_string());
|
||||
save_replay_button_ptr->set_text(config.replay_config.save_hotkey.to_string());
|
||||
save_replay_1_min_button_ptr->set_text(config.replay_config.save_1_min_hotkey.to_string());
|
||||
save_replay_10_min_button_ptr->set_text(config.replay_config.save_10_min_hotkey.to_string());
|
||||
|
||||
start_stop_recording_button_ptr->set_text(config.record_config.start_stop_hotkey.to_string());
|
||||
pause_unpause_recording_button_ptr->set_text(config.record_config.pause_unpause_hotkey.to_string());
|
||||
@@ -567,6 +597,10 @@ namespace gsr {
|
||||
return turn_replay_on_off_button_ptr;
|
||||
case ConfigureHotkeyType::REPLAY_SAVE:
|
||||
return save_replay_button_ptr;
|
||||
case ConfigureHotkeyType::REPLAY_SAVE_1_MIN:
|
||||
return save_replay_1_min_button_ptr;
|
||||
case ConfigureHotkeyType::REPLAY_SAVE_10_MIN:
|
||||
return save_replay_10_min_button_ptr;
|
||||
case ConfigureHotkeyType::RECORD_START_STOP:
|
||||
return start_stop_recording_button_ptr;
|
||||
case ConfigureHotkeyType::RECORD_PAUSE_UNPAUSE:
|
||||
@@ -591,6 +625,10 @@ namespace gsr {
|
||||
return &config.replay_config.start_stop_hotkey;
|
||||
case ConfigureHotkeyType::REPLAY_SAVE:
|
||||
return &config.replay_config.save_hotkey;
|
||||
case ConfigureHotkeyType::REPLAY_SAVE_1_MIN:
|
||||
return &config.replay_config.save_1_min_hotkey;
|
||||
case ConfigureHotkeyType::REPLAY_SAVE_10_MIN:
|
||||
return &config.replay_config.save_10_min_hotkey;
|
||||
case ConfigureHotkeyType::RECORD_START_STOP:
|
||||
return &config.record_config.start_stop_hotkey;
|
||||
case ConfigureHotkeyType::RECORD_PAUSE_UNPAUSE:
|
||||
@@ -643,6 +681,12 @@ namespace gsr {
|
||||
case ConfigureHotkeyType::REPLAY_SAVE:
|
||||
hotkey_configure_action_name = "Save replay";
|
||||
break;
|
||||
case ConfigureHotkeyType::REPLAY_SAVE_1_MIN:
|
||||
hotkey_configure_action_name = "Save 1 minute replay";
|
||||
break;
|
||||
case ConfigureHotkeyType::REPLAY_SAVE_10_MIN:
|
||||
hotkey_configure_action_name = "Save 10 minute replay";
|
||||
break;
|
||||
case ConfigureHotkeyType::RECORD_START_STOP:
|
||||
hotkey_configure_action_name = "Start/stop recording";
|
||||
break;
|
||||
|
||||
@@ -169,7 +169,7 @@ namespace gsr {
|
||||
}
|
||||
}
|
||||
|
||||
const std::string RadioButton::get_selected_id() const {
|
||||
const std::string& RadioButton::get_selected_id() const {
|
||||
if(items.empty()) {
|
||||
static std::string dummy;
|
||||
return dummy;
|
||||
@@ -177,4 +177,13 @@ namespace gsr {
|
||||
return items[selected_item].id;
|
||||
}
|
||||
}
|
||||
|
||||
const std::string& RadioButton::get_selected_text() const {
|
||||
if(items.empty()) {
|
||||
static std::string dummy;
|
||||
return dummy;
|
||||
} else {
|
||||
return items[selected_item].text.get_string();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -196,10 +196,20 @@ namespace gsr {
|
||||
return std::make_unique<Subsection>("Record area", std::move(ll), mgl::vec2f(settings_scrollable_page_ptr->get_inner_size().x, 0.0f));
|
||||
}
|
||||
|
||||
std::unique_ptr<ComboBox> SettingsPage::create_audio_device_selection_combobox() {
|
||||
static bool audio_device_is_output(const std::string &audio_device_id) {
|
||||
return audio_device_id == "default_output" || ends_with(audio_device_id, ".monitor");
|
||||
}
|
||||
|
||||
std::unique_ptr<ComboBox> SettingsPage::create_audio_device_selection_combobox(AudioDeviceType device_type) {
|
||||
auto audio_device_box = std::make_unique<ComboBox>(&get_theme().body_font);
|
||||
for(const auto &audio_device : audio_devices) {
|
||||
audio_device_box->add_item(audio_device.description, audio_device.name);
|
||||
const bool device_is_output = audio_device_is_output(audio_device.name);
|
||||
if((device_type == AudioDeviceType::OUTPUT && device_is_output) || (device_type == AudioDeviceType::INPUT && !device_is_output)) {
|
||||
std::string description = audio_device.description;
|
||||
if(starts_with(description, "Monitor of "))
|
||||
description.erase(0, 11);
|
||||
audio_device_box->add_item(description, audio_device.name);
|
||||
}
|
||||
}
|
||||
return audio_device_box;
|
||||
}
|
||||
@@ -211,7 +221,7 @@ namespace gsr {
|
||||
List *audio_track_items_list = dynamic_cast<List*>(audio_track_subsection->get_inner_widget());
|
||||
|
||||
List *buttons_list = dynamic_cast<List*>(audio_track_items_list->get_child_widget_by_index(1));
|
||||
Button *add_application_audio_button = dynamic_cast<Button*>(buttons_list->get_child_widget_by_index(1));
|
||||
Button *add_application_audio_button = dynamic_cast<Button*>(buttons_list->get_child_widget_by_index(2));
|
||||
add_application_audio_button->set_visible(visible);
|
||||
|
||||
CheckBox *invert_app_audio_checkbox = dynamic_cast<CheckBox*>(audio_track_items_list->get_child_widget_by_index(3));
|
||||
@@ -236,11 +246,11 @@ namespace gsr {
|
||||
return remove_audio_track_button;
|
||||
}
|
||||
|
||||
std::unique_ptr<List> SettingsPage::create_audio_device(List *audio_input_list_ptr) {
|
||||
std::unique_ptr<List> SettingsPage::create_audio_device(AudioDeviceType device_type, List *audio_input_list_ptr) {
|
||||
auto audio_device_list = std::make_unique<List>(List::Orientation::HORIZONTAL, List::Alignment::CENTER);
|
||||
audio_device_list->userdata = (void*)(uintptr_t)AudioTrackType::DEVICE;
|
||||
audio_device_list->add_widget(std::make_unique<Label>(&get_theme().body_font, "Device:", get_color_theme().text_color));
|
||||
audio_device_list->add_widget(create_audio_device_selection_combobox());
|
||||
audio_device_list->add_widget(std::make_unique<Label>(&get_theme().body_font, device_type == AudioDeviceType::OUTPUT ? "Output device:" : "Input device: ", get_color_theme().text_color));
|
||||
audio_device_list->add_widget(create_audio_device_selection_combobox(device_type));
|
||||
audio_device_list->add_widget(create_remove_audio_device_button(audio_input_list_ptr, audio_device_list.get()));
|
||||
return audio_device_list;
|
||||
}
|
||||
@@ -254,13 +264,22 @@ namespace gsr {
|
||||
return button;
|
||||
}
|
||||
|
||||
std::unique_ptr<Button> SettingsPage::create_add_audio_device_button(List *audio_input_list_ptr) {
|
||||
auto add_audio_track_button = std::make_unique<Button>(&get_theme().body_font, "Add audio device", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
|
||||
add_audio_track_button->on_click = [this, audio_input_list_ptr]() {
|
||||
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(audio_input_list_ptr));
|
||||
audio_input_list_ptr->add_widget(create_audio_device(AudioDeviceType::OUTPUT, audio_input_list_ptr));
|
||||
};
|
||||
return add_audio_track_button;
|
||||
return button;
|
||||
}
|
||||
|
||||
std::unique_ptr<Button> SettingsPage::create_add_audio_input_device_button(List *audio_input_list_ptr) {
|
||||
auto button = std::make_unique<Button>(&get_theme().body_font, "Add input 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::INPUT, audio_input_list_ptr));
|
||||
};
|
||||
return button;
|
||||
}
|
||||
|
||||
std::unique_ptr<ComboBox> SettingsPage::create_application_audio_selection_combobox(List *application_audio_row) {
|
||||
@@ -285,7 +304,7 @@ namespace gsr {
|
||||
std::unique_ptr<List> SettingsPage::create_application_audio(List *audio_input_list_ptr) {
|
||||
auto application_audio_list = std::make_unique<List>(List::Orientation::HORIZONTAL, List::Alignment::CENTER);
|
||||
application_audio_list->userdata = (void*)(uintptr_t)AudioTrackType::APPLICATION;
|
||||
application_audio_list->add_widget(std::make_unique<Label>(&get_theme().body_font, "App: ", get_color_theme().text_color));
|
||||
application_audio_list->add_widget(std::make_unique<Label>(&get_theme().body_font, "Application: ", get_color_theme().text_color));
|
||||
application_audio_list->add_widget(create_application_audio_selection_combobox(application_audio_list.get()));
|
||||
application_audio_list->add_widget(create_remove_audio_device_button(audio_input_list_ptr, application_audio_list.get()));
|
||||
return application_audio_list;
|
||||
@@ -294,7 +313,7 @@ namespace gsr {
|
||||
std::unique_ptr<List> SettingsPage::create_custom_application_audio(List *audio_input_list_ptr) {
|
||||
auto application_audio_list = std::make_unique<List>(List::Orientation::HORIZONTAL, List::Alignment::CENTER);
|
||||
application_audio_list->userdata = (void*)(uintptr_t)AudioTrackType::APPLICATION_CUSTOM;
|
||||
application_audio_list->add_widget(std::make_unique<Label>(&get_theme().body_font, "App: ", get_color_theme().text_color));
|
||||
application_audio_list->add_widget(std::make_unique<Label>(&get_theme().body_font, "Application: ", get_color_theme().text_color));
|
||||
application_audio_list->add_widget(std::make_unique<Entry>(&get_theme().body_font, "", (int)(get_theme().body_font.get_character_size() * 10.0f)));
|
||||
application_audio_list->add_widget(create_remove_audio_device_button(audio_input_list_ptr, application_audio_list.get()));
|
||||
return application_audio_list;
|
||||
@@ -314,7 +333,8 @@ namespace gsr {
|
||||
|
||||
std::unique_ptr<List> SettingsPage::create_add_audio_buttons(List *audio_input_list_ptr) {
|
||||
auto list = std::make_unique<List>(List::Orientation::HORIZONTAL, List::Alignment::CENTER);
|
||||
list->add_widget(create_add_audio_device_button(audio_input_list_ptr));
|
||||
list->add_widget(create_add_audio_output_device_button(audio_input_list_ptr));
|
||||
list->add_widget(create_add_audio_input_device_button(audio_input_list_ptr));
|
||||
list->add_widget(create_add_application_audio_button(audio_input_list_ptr));
|
||||
return list;
|
||||
}
|
||||
@@ -715,7 +735,7 @@ namespace gsr {
|
||||
auto list = std::make_unique<List>(List::Orientation::HORIZONTAL, List::Alignment::CENTER);
|
||||
|
||||
auto replay_time_entry = std::make_unique<Entry>(&get_theme().body_font, "60", get_theme().body_font.get_character_size() * 3);
|
||||
replay_time_entry->validate_handler = create_entry_validator_integer_in_range(1, 10800);
|
||||
replay_time_entry->validate_handler = create_entry_validator_integer_in_range(2, 86400);
|
||||
replay_time_entry_ptr = replay_time_entry.get();
|
||||
list->add_widget(std::move(replay_time_entry));
|
||||
|
||||
@@ -733,6 +753,24 @@ namespace gsr {
|
||||
return replay_time_list;
|
||||
}
|
||||
|
||||
std::unique_ptr<List> SettingsPage::create_replay_storage() {
|
||||
auto list = std::make_unique<List>(List::Orientation::VERTICAL);
|
||||
list->add_widget(std::make_unique<Label>(&get_theme().body_font, "Where should temporary replay data be stored?", get_color_theme().text_color));
|
||||
auto replay_storage_button = std::make_unique<RadioButton>(&get_theme().body_font, RadioButton::Orientation::HORIZONTAL);
|
||||
replay_storage_button_ptr = replay_storage_button.get();
|
||||
replay_storage_button->add_item("RAM", "ram");
|
||||
replay_storage_button->add_item("Disk (not recommended on SSDs)", "disk");
|
||||
|
||||
replay_storage_button->on_selection_changed = [this](const std::string&, const std::string &id) {
|
||||
update_estimated_replay_file_size(id);
|
||||
return true;
|
||||
};
|
||||
|
||||
list->add_widget(std::move(replay_storage_button));
|
||||
list->set_visible(gsr_info->system_info.gsr_version >= GsrVersion{5, 5, 0});
|
||||
return list;
|
||||
}
|
||||
|
||||
std::unique_ptr<RadioButton> SettingsPage::create_start_replay_automatically() {
|
||||
char fullscreen_text[256];
|
||||
snprintf(fullscreen_text, sizeof(fullscreen_text), "Turn on replay when starting a fullscreen application%s", gsr_info->system_info.display_server == DisplayServer::X11 ? "" : " (X11 applications only)");
|
||||
@@ -766,13 +804,13 @@ namespace gsr {
|
||||
return label;
|
||||
}
|
||||
|
||||
void SettingsPage::update_estimated_replay_file_size() {
|
||||
void SettingsPage::update_estimated_replay_file_size(const std::string &replay_storage_type) {
|
||||
const int64_t replay_time_seconds = atoi(replay_time_entry_ptr->get_text().c_str());
|
||||
const int64_t video_bitrate_bps = atoi(video_bitrate_entry_ptr->get_text().c_str()) * 1000LL / 8LL;
|
||||
const double video_filesize_mb = ((double)replay_time_seconds * (double)video_bitrate_bps) / 1000.0 / 1000.0 * 1.024;
|
||||
|
||||
char buffer[256];
|
||||
snprintf(buffer, sizeof(buffer), "Estimated video max file size in RAM: %.2fMB.\nChange video bitrate or replay duration to change file size.", video_filesize_mb);
|
||||
snprintf(buffer, sizeof(buffer), "Estimated video max file size %s: %.2fMB.\nChange video bitrate or replay duration to change file size.", replay_storage_type == "ram" ? "in RAM" : "on disk", video_filesize_mb);
|
||||
estimated_file_size_ptr->set_text(buffer);
|
||||
}
|
||||
|
||||
@@ -811,12 +849,14 @@ namespace gsr {
|
||||
settings_list_ptr->add_widget(std::make_unique<Subsection>("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>(List::Orientation::VERTICAL);
|
||||
general_list->add_widget(create_start_replay_automatically());
|
||||
general_list->add_widget(create_replay_storage());
|
||||
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());
|
||||
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>("Autostart", create_start_replay_automatically(), mgl::vec2f(settings_scrollable_page_ptr->get_inner_size().x, 0.0f)));
|
||||
|
||||
auto checkboxes_list = std::make_unique<List>(List::Orientation::VERTICAL);
|
||||
|
||||
auto show_replay_started_notification_checkbox = std::make_unique<CheckBox>(&get_theme().body_font, "Show replay started notification");
|
||||
@@ -845,12 +885,12 @@ namespace gsr {
|
||||
view_radio_button_ptr->on_selection_changed("Simple", "simple");
|
||||
|
||||
replay_time_entry_ptr->on_changed = [this](const std::string&) {
|
||||
update_estimated_replay_file_size();
|
||||
update_estimated_replay_file_size(replay_storage_button_ptr->get_selected_id());
|
||||
update_replay_time_text();
|
||||
};
|
||||
|
||||
video_bitrate_entry_ptr->on_changed = [this](const std::string&) {
|
||||
update_estimated_replay_file_size();
|
||||
update_estimated_replay_file_size(replay_storage_button_ptr->get_selected_id());
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1095,12 +1135,15 @@ namespace gsr {
|
||||
audio_input_list_ptr->add_widget(std::move(application_audio_widget));
|
||||
}
|
||||
} else if(starts_with(audio_input, "device:")) {
|
||||
std::unique_ptr<List> audio_track_widget = create_audio_device(audio_input_list_ptr);
|
||||
const std::string device_name = audio_input.substr(7);
|
||||
const AudioDeviceType audio_device_type = audio_device_is_output(device_name) ? AudioDeviceType::OUTPUT : AudioDeviceType::INPUT;
|
||||
std::unique_ptr<List> audio_track_widget = create_audio_device(audio_device_type, audio_input_list_ptr);
|
||||
ComboBox *audio_device_box = dynamic_cast<ComboBox*>(audio_track_widget->get_child_widget_by_index(1));
|
||||
audio_device_box->set_selected_item(audio_input.substr(7));
|
||||
audio_device_box->set_selected_item(device_name);
|
||||
audio_input_list_ptr->add_widget(std::move(audio_track_widget));
|
||||
} else {
|
||||
std::unique_ptr<List> audio_track_widget = create_audio_device(audio_input_list_ptr);
|
||||
const AudioDeviceType audio_device_type = audio_device_is_output(audio_input) ? AudioDeviceType::OUTPUT : AudioDeviceType::INPUT;
|
||||
std::unique_ptr<List> audio_track_widget = create_audio_device(audio_device_type, audio_input_list_ptr);
|
||||
ComboBox *audio_device_box = dynamic_cast<ComboBox*>(audio_track_widget->get_child_widget_by_index(1));
|
||||
audio_device_box->set_selected_item(audio_input);
|
||||
audio_input_list_ptr->add_widget(std::move(audio_track_widget));
|
||||
@@ -1173,6 +1216,7 @@ namespace gsr {
|
||||
|
||||
void SettingsPage::load_replay() {
|
||||
load_common(config.replay_config.record_options);
|
||||
replay_storage_button_ptr->set_selected_item(config.replay_config.replay_storage);
|
||||
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);
|
||||
if(restart_replay_on_save)
|
||||
@@ -1185,8 +1229,8 @@ namespace gsr {
|
||||
|
||||
if(config.replay_config.replay_time < 2)
|
||||
config.replay_config.replay_time = 2;
|
||||
if(config.replay_config.replay_time > 10800)
|
||||
config.replay_config.replay_time = 10800;
|
||||
if(config.replay_config.replay_time > 86400)
|
||||
config.replay_config.replay_time = 86400;
|
||||
replay_time_entry_ptr->set_text(std::to_string(config.replay_config.replay_time));
|
||||
}
|
||||
|
||||
@@ -1322,6 +1366,7 @@ namespace gsr {
|
||||
config.replay_config.save_directory = save_directory_button_ptr->get_text();
|
||||
config.replay_config.container = container_box_ptr->get_selected_id();
|
||||
config.replay_config.replay_time = atoi(replay_time_entry_ptr->get_text().c_str());
|
||||
config.replay_config.replay_storage = replay_storage_button_ptr->get_selected_id();
|
||||
|
||||
if(config.replay_config.replay_time < 5) {
|
||||
config.replay_config.replay_time = 5;
|
||||
|
||||
64
src/main.cpp
64
src/main.cpp
@@ -30,6 +30,10 @@ static void sigint_handler(int signal) {
|
||||
running = 0;
|
||||
}
|
||||
|
||||
static void signal_ignore(int) {
|
||||
|
||||
}
|
||||
|
||||
static void disable_prime_run() {
|
||||
unsetenv("__NV_PRIME_RENDER_OFFLOAD");
|
||||
unsetenv("__NV_PRIME_RENDER_OFFLOAD_PROVIDER");
|
||||
@@ -74,6 +78,16 @@ static void rpc_add_commands(gsr::Rpc *rpc, gsr::Overlay *overlay) {
|
||||
overlay->save_replay();
|
||||
});
|
||||
|
||||
rpc->add_handler("replay-save-1-min", [overlay](const std::string &name) {
|
||||
fprintf(stderr, "rpc command executed: %s\n", name.c_str());
|
||||
overlay->save_replay_1_min();
|
||||
});
|
||||
|
||||
rpc->add_handler("replay-save-10-min", [overlay](const std::string &name) {
|
||||
fprintf(stderr, "rpc command executed: %s\n", name.c_str());
|
||||
overlay->save_replay_10_min();
|
||||
});
|
||||
|
||||
rpc->add_handler("take-screenshot", [overlay](const std::string &name) {
|
||||
fprintf(stderr, "rpc command executed: %s\n", name.c_str());
|
||||
overlay->take_screenshot();
|
||||
@@ -145,18 +159,35 @@ static bool is_flatpak() {
|
||||
return getenv("FLATPAK_ID") != nullptr;
|
||||
}
|
||||
|
||||
static void set_display_server_environment_variables() {
|
||||
// Some users dont have properly setup environments (no display manager that does systemctl --user import-environment DISPLAY WAYLAND_DISPLAY)
|
||||
const char *display = getenv("DISPLAY");
|
||||
if(!display) {
|
||||
display = ":0";
|
||||
setenv("DISPLAY", display, true);
|
||||
}
|
||||
|
||||
const char *wayland_display = getenv("WAYLAND_DISPLAY");
|
||||
if(!wayland_display) {
|
||||
wayland_display = "wayland-1";
|
||||
setenv("WAYLAND_DISPLAY", wayland_display, true);
|
||||
}
|
||||
}
|
||||
|
||||
static void usage() {
|
||||
printf("usage: gsr-ui [action]\n");
|
||||
printf("OPTIONS:\n");
|
||||
printf(" action The launch action. Should be either \"launch-show\" or \"launch-hide\". Optional, defaults to \"launch-hide\".\n");
|
||||
printf(" action The launch action. Should be either \"launch-show\", \"launch-hide\" or \"launch-daemon\". Optional, defaults to \"launch-hide\".\n");
|
||||
printf(" If \"launch-show\" is used then the program starts and the UI is immediately opened and can be shown/hidden with Alt+Z.\n");
|
||||
printf(" If \"launch-hide\" is used then the program starts but the UI is not opened until Alt+Z is pressed.\n");
|
||||
printf(" If \"launch-hide\" is used then the program starts but the UI is not opened until Alt+Z is pressed. The UI will be opened if the program is already running in another process.\n");
|
||||
printf(" If \"launch-daemon\" is used then the program starts but the UI is not opened until Alt+Z is pressed. The UI will not be opened if the program is already running in another process.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
enum class LaunchAction {
|
||||
LAUNCH_SHOW,
|
||||
LAUNCH_HIDE
|
||||
LAUNCH_HIDE,
|
||||
LAUNCH_DAEMON
|
||||
};
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
@@ -177,18 +208,17 @@ int main(int argc, char **argv) {
|
||||
launch_action = LaunchAction::LAUNCH_SHOW;
|
||||
} else if(strcmp(launch_action_opt, "launch-hide") == 0) {
|
||||
launch_action = LaunchAction::LAUNCH_HIDE;
|
||||
} else if(strcmp(launch_action_opt, "launch-daemon") == 0) {
|
||||
launch_action = LaunchAction::LAUNCH_DAEMON;
|
||||
} else {
|
||||
printf("error: invalid action \"%s\", expected \"launch-show\" or \"launch-hide\".\n", launch_action_opt);
|
||||
printf("error: invalid action \"%s\", expected \"launch-show\", \"launch-hide\" or \"launch-daemon\".\n", launch_action_opt);
|
||||
usage();
|
||||
}
|
||||
} else {
|
||||
usage();
|
||||
}
|
||||
|
||||
if(is_flatpak())
|
||||
install_flatpak_systemd_service();
|
||||
else
|
||||
remove_flatpak_systemd_service();
|
||||
set_display_server_environment_variables();
|
||||
|
||||
// TODO: This is a shitty method to detect if multiple instances of gsr-ui is running but this will work properly even in flatpak
|
||||
// that uses pid sandboxing. Replace this with a better method once we no longer rely on linux global hotkeys on some platform.
|
||||
@@ -196,6 +226,9 @@ int main(int argc, char **argv) {
|
||||
// What do? creating a pid file doesn't work in flatpak either.
|
||||
// TODO: This doesn't work in flatpak when disabling hotkeys.
|
||||
if(is_gsr_ui_virtual_keyboard_running() || gsr::pidof("gsr-ui", getpid()) != -1) {
|
||||
if(launch_action == LaunchAction::LAUNCH_DAEMON)
|
||||
return 1;
|
||||
|
||||
gsr::Rpc rpc;
|
||||
if(rpc.open("gsr-ui") && rpc.write("show_ui\n", 8)) {
|
||||
fprintf(stderr, "Error: another instance of gsr-ui is already running, opening that one instead\n");
|
||||
@@ -207,6 +240,11 @@ int main(int argc, char **argv) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(is_flatpak())
|
||||
install_flatpak_systemd_service();
|
||||
else
|
||||
remove_flatpak_systemd_service();
|
||||
|
||||
// Stop nvidia driver from buffering frames
|
||||
setenv("__GL_MaxFramesAllowed", "1", true);
|
||||
// If this is set to 1 then cuGraphicsGLRegisterImage will fail for egl context with error: invalid OpenGL or DirectX context,
|
||||
@@ -218,6 +256,16 @@ int main(int argc, char **argv) {
|
||||
unsetenv("vblank_mode");
|
||||
|
||||
signal(SIGINT, sigint_handler);
|
||||
signal(SIGTERM, sigint_handler);
|
||||
signal(SIGUSR1, signal_ignore);
|
||||
signal(SIGUSR2, signal_ignore);
|
||||
signal(SIGRTMIN, signal_ignore);
|
||||
signal(SIGRTMIN+1, signal_ignore);
|
||||
signal(SIGRTMIN+2, signal_ignore);
|
||||
signal(SIGRTMIN+3, signal_ignore);
|
||||
signal(SIGRTMIN+4, signal_ignore);
|
||||
signal(SIGRTMIN+5, signal_ignore);
|
||||
signal(SIGRTMIN+6, signal_ignore);
|
||||
|
||||
gsr::GsrInfo gsr_info;
|
||||
// TODO: Show the error in ui
|
||||
|
||||
@@ -404,8 +404,10 @@ static void keyboard_event_remove_event(keyboard_event *self, int index) {
|
||||
if(index < 0 || index >= self->num_event_polls)
|
||||
return;
|
||||
|
||||
ioctl(self->event_polls[index].fd, EVIOCGRAB, 0);
|
||||
close(self->event_polls[index].fd);
|
||||
if(self->event_polls[index].fd > 0) {
|
||||
ioctl(self->event_polls[index].fd, EVIOCGRAB, 0);
|
||||
close(self->event_polls[index].fd);
|
||||
}
|
||||
free(self->event_extra_data[index].key_states);
|
||||
free(self->event_extra_data[index].key_presses_grabbed);
|
||||
|
||||
@@ -435,7 +437,7 @@ static int setup_virtual_keyboard_input(const char *name) {
|
||||
success &= (ioctl(fd, UI_SET_EVBIT, EV_KEY) != -1);
|
||||
success &= (ioctl(fd, UI_SET_EVBIT, EV_REP) != -1);
|
||||
success &= (ioctl(fd, UI_SET_EVBIT, EV_REL) != -1);
|
||||
success &= (ioctl(fd, UI_SET_EVBIT, EV_LED) != -1);
|
||||
//success &= (ioctl(fd, UI_SET_EVBIT, EV_LED) != -1);
|
||||
|
||||
success &= (ioctl(fd, UI_SET_MSCBIT, MSC_SCAN) != -1);
|
||||
for(int i = 1; i < KEY_MAX; ++i) {
|
||||
@@ -445,9 +447,9 @@ static int setup_virtual_keyboard_input(const char *name) {
|
||||
for(int i = 0; i < REL_MAX; ++i) {
|
||||
success &= (ioctl(fd, UI_SET_RELBIT, i) != -1);
|
||||
}
|
||||
for(int i = 0; i < LED_MAX; ++i) {
|
||||
success &= (ioctl(fd, UI_SET_LEDBIT, i) != -1);
|
||||
}
|
||||
// for(int i = 0; i < LED_MAX; ++i) {
|
||||
// success &= (ioctl(fd, UI_SET_LEDBIT, i) != -1);
|
||||
// }
|
||||
|
||||
// success &= (ioctl(fd, UI_SET_EVBIT, EV_ABS) != -1);
|
||||
// success &= (ioctl(fd, UI_SET_ABSBIT, ABS_X) != -1);
|
||||
@@ -566,8 +568,10 @@ void keyboard_event_deinit(keyboard_event *self) {
|
||||
}
|
||||
|
||||
for(int i = 0; i < self->num_event_polls; ++i) {
|
||||
ioctl(self->event_polls[i].fd, EVIOCGRAB, 0);
|
||||
close(self->event_polls[i].fd);
|
||||
if(self->event_polls[i].fd > 0) {
|
||||
ioctl(self->event_polls[i].fd, EVIOCGRAB, 0);
|
||||
close(self->event_polls[i].fd);
|
||||
}
|
||||
free(self->event_extra_data[i].key_states);
|
||||
free(self->event_extra_data[i].key_presses_grabbed);
|
||||
}
|
||||
|
||||
@@ -56,6 +56,10 @@ static void usage(void) {
|
||||
printf(" Start/stop replay.\n");
|
||||
printf(" replay-save\n");
|
||||
printf(" Save replay.\n");
|
||||
printf(" replay-save-1-min\n");
|
||||
printf(" Save 1 minute replay.\n");
|
||||
printf(" replay-save-10-min\n");
|
||||
printf(" Save 10 minute replay.\n");
|
||||
printf(" take-screenshot\n");
|
||||
printf(" Take a screenshot.\n");
|
||||
printf(" take-screenshot-region\n");
|
||||
@@ -75,6 +79,8 @@ static bool is_valid_command(const char *command) {
|
||||
"toggle-stream",
|
||||
"toggle-replay",
|
||||
"replay-save",
|
||||
"replay-save-1-min",
|
||||
"replay-save-10-min",
|
||||
"take-screenshot",
|
||||
"take-screenshot-region",
|
||||
NULL
|
||||
|
||||
Reference in New Issue
Block a user