mirror of
https://repo.dec05eba.com/gpu-screen-recorder-ui
synced 2026-04-20 00:45:51 +09:00
Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
de1ed58f8d | ||
|
|
ff564fcb52 | ||
|
|
aabe190bf1 | ||
|
|
320d368699 | ||
|
|
af4fc84ef7 | ||
|
|
c7bfaf90ec | ||
|
|
61f8c666fe | ||
|
|
28be9d1c6f | ||
|
|
305c9df7ac | ||
|
|
d08ea69277 | ||
|
|
180a3b73db | ||
|
|
ac1d57e8ba | ||
|
|
5a32c469d3 | ||
|
|
5a17aae0ab |
25
TODO
25
TODO
@@ -161,4 +161,27 @@ Support cjk font. Use fc-match to find the location of the font. This also works
|
|||||||
|
|
||||||
Keyboard layout is incorrect on wayland when using kde plasma keyboard settings to setup multiple keyboards, for example when changing to french.
|
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.
|
Text input is correct, but hotkey is incorrect.
|
||||||
Need to use "setxkbmap fr" as well.
|
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.
|
||||||
|
|
||||||
|
Add restart program button, in global settings. It should do almost the same thing as exit program, execept execv gsr-ui.
|
||||||
|
|
||||||
|
When gpu screen recorder ui can run as a regular window (and supports tray icon and global shortcut portal) remove gpu screen recorder gtk. Then all error checking needs to be moved from that project to this project.
|
||||||
|
May need support for multi windows, or create a small project to display dialogs.
|
||||||
|
|
||||||
|
Add a bug report page that automatically includes system info (make this clear to the user).
|
||||||
|
Do this by sending the report to a custom server that stores that data.
|
||||||
|
The server should limit reports per IP to prevent spam.
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
Description=GPU Screen Recorder UI Service
|
Description=GPU Screen Recorder UI Service
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
ExecStart=gsr-ui
|
ExecStart=gsr-ui launch-daemon
|
||||||
KillSignal=SIGINT
|
KillSignal=SIGINT
|
||||||
Restart=on-failure
|
Restart=on-failure
|
||||||
RestartSec=5s
|
RestartSec=5s
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Description=GPU Screen Recorder UI Service
|
Description=GPU Screen Recorder UI Service
|
||||||
|
|
||||||
[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
|
KillSignal=SIGINT
|
||||||
Restart=on-failure
|
Restart=on-failure
|
||||||
RestartSec=5s
|
RestartSec=5s
|
||||||
|
|||||||
@@ -117,6 +117,7 @@ namespace gsr {
|
|||||||
std::string save_directory;
|
std::string save_directory;
|
||||||
std::string container = "mp4";
|
std::string container = "mp4";
|
||||||
int32_t replay_time = 60;
|
int32_t replay_time = 60;
|
||||||
|
std::string replay_storage = "ram";
|
||||||
ConfigHotkey start_stop_hotkey;
|
ConfigHotkey start_stop_hotkey;
|
||||||
ConfigHotkey save_hotkey;
|
ConfigHotkey save_hotkey;
|
||||||
ConfigHotkey save_1_min_hotkey;
|
ConfigHotkey save_1_min_hotkey;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "GlobalHotkeys.hpp"
|
#include "GlobalHotkeys.hpp"
|
||||||
#include "Hotplug.hpp"
|
#include "../Hotplug.hpp"
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <poll.h>
|
#include <poll.h>
|
||||||
@@ -6,10 +6,10 @@
|
|||||||
#include "Config.hpp"
|
#include "Config.hpp"
|
||||||
#include "window_texture.h"
|
#include "window_texture.h"
|
||||||
#include "WindowUtils.hpp"
|
#include "WindowUtils.hpp"
|
||||||
#include "GlobalHotkeysJoystick.hpp"
|
#include "GlobalHotkeys/GlobalHotkeysJoystick.hpp"
|
||||||
#include "AudioPlayer.hpp"
|
#include "AudioPlayer.hpp"
|
||||||
#include "RegionSelector.hpp"
|
#include "RegionSelector.hpp"
|
||||||
#include "CursorTracker.hpp"
|
#include "CursorTracker/CursorTracker.hpp"
|
||||||
|
|
||||||
#include <mglpp/window/Window.hpp>
|
#include <mglpp/window/Window.hpp>
|
||||||
#include <mglpp/window/Event.hpp>
|
#include <mglpp/window/Event.hpp>
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ namespace gsr {
|
|||||||
|
|
||||||
void string_split_char(std::string_view str, char delimiter, StringSplitCallback callback_func);
|
void string_split_char(std::string_view str, char delimiter, StringSplitCallback callback_func);
|
||||||
bool starts_with(std::string_view str, const char *substr);
|
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_home_dir();
|
||||||
std::string get_config_dir();
|
std::string get_config_dir();
|
||||||
|
|||||||
@@ -23,7 +23,8 @@ namespace gsr {
|
|||||||
|
|
||||||
void add_item(const std::string &text, const std::string &id);
|
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);
|
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;
|
mgl::vec2f get_size() override;
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,11 @@ namespace gsr {
|
|||||||
class LineSeparator;
|
class LineSeparator;
|
||||||
class Subsection;
|
class Subsection;
|
||||||
|
|
||||||
|
enum class AudioDeviceType {
|
||||||
|
OUTPUT,
|
||||||
|
INPUT
|
||||||
|
};
|
||||||
|
|
||||||
class SettingsPage : public StaticPage {
|
class SettingsPage : public StaticPage {
|
||||||
public:
|
public:
|
||||||
enum class Type {
|
enum class Type {
|
||||||
@@ -54,11 +59,12 @@ namespace gsr {
|
|||||||
std::unique_ptr<List> create_restore_portal_session_section();
|
std::unique_ptr<List> create_restore_portal_session_section();
|
||||||
std::unique_ptr<Widget> create_change_video_resolution_section();
|
std::unique_ptr<Widget> create_change_video_resolution_section();
|
||||||
std::unique_ptr<Widget> create_capture_target_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<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_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<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_application_audio(List *audio_input_list_ptr);
|
||||||
std::unique_ptr<List> create_custom_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_container_section();
|
||||||
std::unique_ptr<List> create_replay_time_entry();
|
std::unique_ptr<List> create_replay_time_entry();
|
||||||
std::unique_ptr<List> create_replay_time();
|
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<RadioButton> create_start_replay_automatically();
|
||||||
std::unique_ptr<CheckBox> create_save_replay_in_game_folder();
|
std::unique_ptr<CheckBox> create_save_replay_in_game_folder();
|
||||||
std::unique_ptr<CheckBox> create_restart_replay_on_save();
|
std::unique_ptr<CheckBox> create_restart_replay_on_save();
|
||||||
std::unique_ptr<Label> create_estimated_replay_file_size();
|
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();
|
void update_replay_time_text();
|
||||||
std::unique_ptr<CheckBox> create_save_recording_in_game_folder();
|
std::unique_ptr<CheckBox> create_save_recording_in_game_folder();
|
||||||
std::unique_ptr<Label> create_estimated_record_file_size();
|
std::unique_ptr<Label> create_estimated_record_file_size();
|
||||||
@@ -186,6 +193,7 @@ namespace gsr {
|
|||||||
Entry *youtube_stream_key_entry_ptr = nullptr;
|
Entry *youtube_stream_key_entry_ptr = nullptr;
|
||||||
Entry *stream_url_entry_ptr = nullptr;
|
Entry *stream_url_entry_ptr = nullptr;
|
||||||
Entry *replay_time_entry_ptr = nullptr;
|
Entry *replay_time_entry_ptr = nullptr;
|
||||||
|
RadioButton *replay_storage_button_ptr = nullptr;
|
||||||
Label *replay_time_label_ptr = nullptr;
|
Label *replay_time_label_ptr = nullptr;
|
||||||
RadioButton *turn_on_replay_automatically_mode_ptr = nullptr;
|
RadioButton *turn_on_replay_automatically_mode_ptr = nullptr;
|
||||||
Subsection *audio_section_ptr = nullptr;
|
Subsection *audio_section_ptr = nullptr;
|
||||||
|
|||||||
14
meson.build
14
meson.build
@@ -1,4 +1,4 @@
|
|||||||
project('gsr-ui', ['c', 'cpp'], version : '1.5.0', default_options : ['warning_level=2', 'cpp_std=c++17'], subproject_dir : 'depends')
|
project('gsr-ui', ['c', 'cpp'], version : '1.6.3', default_options : ['warning_level=2', 'cpp_std=c++17'], subproject_dir : 'depends')
|
||||||
|
|
||||||
if get_option('buildtype') == 'debug'
|
if get_option('buildtype') == 'debug'
|
||||||
add_project_arguments('-g3', language : ['c', 'cpp'])
|
add_project_arguments('-g3', language : ['c', 'cpp'])
|
||||||
@@ -32,6 +32,11 @@ src = [
|
|||||||
'src/gui/GlobalSettingsPage.cpp',
|
'src/gui/GlobalSettingsPage.cpp',
|
||||||
'src/gui/GsrPage.cpp',
|
'src/gui/GsrPage.cpp',
|
||||||
'src/gui/Subsection.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/Utils.cpp',
|
||||||
'src/WindowUtils.cpp',
|
'src/WindowUtils.cpp',
|
||||||
'src/RegionSelector.cpp',
|
'src/RegionSelector.cpp',
|
||||||
@@ -39,11 +44,6 @@ src = [
|
|||||||
'src/GsrInfo.cpp',
|
'src/GsrInfo.cpp',
|
||||||
'src/Process.cpp',
|
'src/Process.cpp',
|
||||||
'src/Overlay.cpp',
|
'src/Overlay.cpp',
|
||||||
'src/GlobalHotkeysX11.cpp',
|
|
||||||
'src/GlobalHotkeysLinux.cpp',
|
|
||||||
'src/GlobalHotkeysJoystick.cpp',
|
|
||||||
'src/CursorTrackerX11.cpp',
|
|
||||||
'src/CursorTrackerWayland.cpp',
|
|
||||||
'src/AudioPlayer.cpp',
|
'src/AudioPlayer.cpp',
|
||||||
'src/Hotplug.cpp',
|
'src/Hotplug.cpp',
|
||||||
'src/Rpc.cpp',
|
'src/Rpc.cpp',
|
||||||
@@ -61,7 +61,7 @@ datadir = get_option('datadir')
|
|||||||
gsr_ui_resources_path = join_paths(prefix, datadir, 'gsr-ui')
|
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_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(
|
executable(
|
||||||
meson.project_name(),
|
meson.project_name(),
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "gsr-ui"
|
name = "gsr-ui"
|
||||||
type = "executable"
|
type = "executable"
|
||||||
version = "1.5.0"
|
version = "1.6.3"
|
||||||
platforms = ["posix"]
|
platforms = ["posix"]
|
||||||
|
|
||||||
[lang.cpp]
|
[lang.cpp]
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#include "../include/Config.hpp"
|
#include "../include/Config.hpp"
|
||||||
#include "../include/Utils.hpp"
|
#include "../include/Utils.hpp"
|
||||||
#include "../include/GsrInfo.hpp"
|
#include "../include/GsrInfo.hpp"
|
||||||
#include "../include/GlobalHotkeys.hpp"
|
#include "../include/GlobalHotkeys/GlobalHotkeys.hpp"
|
||||||
#include <variant>
|
#include <variant>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
@@ -123,12 +123,12 @@ namespace gsr {
|
|||||||
|
|
||||||
record_config.save_directory = default_videos_save_directory;
|
record_config.save_directory = default_videos_save_directory;
|
||||||
record_config.record_options.audio_tracks_list.push_back({std::vector<std::string>{"default_output"}, false});
|
record_config.record_options.audio_tracks_list.push_back({std::vector<std::string>{"default_output"}, false});
|
||||||
record_config.record_options.video_bitrate = 45000;
|
record_config.record_options.video_bitrate = 40000;
|
||||||
|
|
||||||
replay_config.record_options.video_quality = "custom";
|
replay_config.record_options.video_quality = "custom";
|
||||||
replay_config.save_directory = default_videos_save_directory;
|
replay_config.save_directory = default_videos_save_directory;
|
||||||
replay_config.record_options.audio_tracks_list.push_back({std::vector<std::string>{"default_output"}, false});
|
replay_config.record_options.audio_tracks_list.push_back({std::vector<std::string>{"default_output"}, false});
|
||||||
replay_config.record_options.video_bitrate = 45000;
|
replay_config.record_options.video_bitrate = 40000;
|
||||||
|
|
||||||
screenshot_config.save_directory = default_pictures_save_directory;
|
screenshot_config.save_directory = default_pictures_save_directory;
|
||||||
|
|
||||||
@@ -264,6 +264,7 @@ namespace gsr {
|
|||||||
{"replay.save_directory", &config.replay_config.save_directory},
|
{"replay.save_directory", &config.replay_config.save_directory},
|
||||||
{"replay.container", &config.replay_config.container},
|
{"replay.container", &config.replay_config.container},
|
||||||
{"replay.time", &config.replay_config.replay_time},
|
{"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.start_stop_hotkey", &config.replay_config.start_stop_hotkey},
|
||||||
{"replay.save_hotkey", &config.replay_config.save_hotkey},
|
{"replay.save_hotkey", &config.replay_config.save_hotkey},
|
||||||
{"replay.save_1_min_hotkey", &config.replay_config.save_1_min_hotkey},
|
{"replay.save_1_min_hotkey", &config.replay_config.save_1_min_hotkey},
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#include "../include/CursorTrackerWayland.hpp"
|
#include "../../include/CursorTracker/CursorTrackerWayland.hpp"
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
@@ -27,19 +27,20 @@ namespace gsr {
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
uint64_t crtc_id;
|
uint64_t crtc_id;
|
||||||
mgl::vec2i size;
|
mgl::vec2i size;
|
||||||
|
bool vrr_enabled;
|
||||||
} drm_connector;
|
} drm_connector;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
drm_connector connectors[MAX_CONNECTORS];
|
drm_connector connectors[MAX_CONNECTORS];
|
||||||
int num_connectors;
|
int num_connectors;
|
||||||
|
bool has_any_crtc_with_vrr_enabled;
|
||||||
} drm_connectors;
|
} drm_connectors;
|
||||||
|
|
||||||
/* Returns plane_property_mask */
|
/* 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_x = 0;
|
||||||
*crtc_y = 0;
|
*crtc_y = 0;
|
||||||
*crtc_id = 0;
|
*crtc_id = 0;
|
||||||
*is_cursor = false;
|
|
||||||
|
|
||||||
uint32_t property_mask = 0;
|
uint32_t property_mask = 0;
|
||||||
|
|
||||||
@@ -80,8 +81,8 @@ namespace gsr {
|
|||||||
return property_mask;
|
return property_mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool connector_get_property_by_name(int drm_fd, drmModeConnectorPtr props, const char *name, uint64_t *result) {
|
static bool get_drm_property_by_name(int drm_fd, drmModeObjectPropertiesPtr props, const char *name, uint64_t *result) {
|
||||||
for(int i = 0; i < props->count_props; ++i) {
|
for(uint32_t i = 0; i < props->count_props; ++i) {
|
||||||
drmModePropertyPtr prop = drmModeGetProperty(drm_fd, props->props[i]);
|
drmModePropertyPtr prop = drmModeGetProperty(drm_fd, props->props[i]);
|
||||||
if(!prop)
|
if(!prop)
|
||||||
continue;
|
continue;
|
||||||
@@ -96,6 +97,14 @@ namespace gsr {
|
|||||||
return false;
|
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) {
|
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) {
|
for(int i = 0; i < *num_type_counts; ++i) {
|
||||||
if(type_counts[i].type == connector_type)
|
if(type_counts[i].type == connector_type)
|
||||||
@@ -325,7 +334,7 @@ namespace gsr {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/* Returns nullptr if not found */
|
/* 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) {
|
for(int i = 0; i < connectors->num_connectors; ++i) {
|
||||||
if(connectors->connectors[i].crtc_id == crtc_id)
|
if(connectors->connectors[i].crtc_id == crtc_id)
|
||||||
return &connectors->connectors[i];
|
return &connectors->connectors[i];
|
||||||
@@ -335,6 +344,8 @@ namespace gsr {
|
|||||||
|
|
||||||
static void get_drm_connectors(int drm_fd, drm_connectors *drm_connectors) {
|
static void get_drm_connectors(int drm_fd, drm_connectors *drm_connectors) {
|
||||||
drm_connectors->num_connectors = 0;
|
drm_connectors->num_connectors = 0;
|
||||||
|
drm_connectors->has_any_crtc_with_vrr_enabled = false;
|
||||||
|
|
||||||
drmModeResPtr resources = drmModeGetResources(drm_fd);
|
drmModeResPtr resources = drmModeGetResources(drm_fd);
|
||||||
if(!resources)
|
if(!resources)
|
||||||
return;
|
return;
|
||||||
@@ -350,23 +361,59 @@ namespace gsr {
|
|||||||
uint64_t crtc_id = 0;
|
uint64_t crtc_id = 0;
|
||||||
connector_get_property_by_name(drm_fd, connector, "CRTC_ID", &crtc_id);
|
connector_get_property_by_name(drm_fd, connector, "CRTC_ID", &crtc_id);
|
||||||
if(crtc_id == 0)
|
if(crtc_id == 0)
|
||||||
goto next;
|
goto next_connector;
|
||||||
|
|
||||||
crtc = drmModeGetCrtc(drm_fd, crtc_id);
|
crtc = drmModeGetCrtc(drm_fd, crtc_id);
|
||||||
if(!crtc)
|
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].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].size = mgl::vec2i{(int)crtc->width, (int)crtc->height};
|
||||||
|
drm_connectors->connectors[drm_connectors->num_connectors].vrr_enabled = false;
|
||||||
++drm_connectors->num_connectors;
|
++drm_connectors->num_connectors;
|
||||||
|
|
||||||
next:
|
next_connector:
|
||||||
if(crtc)
|
if(crtc)
|
||||||
drmModeFreeCrtc(crtc);
|
drmModeFreeCrtc(crtc);
|
||||||
|
|
||||||
if(connector)
|
if(connector)
|
||||||
drmModeFreeConnector(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);
|
drmModeFreeResources(resources);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -392,19 +439,20 @@ namespace gsr {
|
|||||||
|
|
||||||
drm_connectors connectors;
|
drm_connectors connectors;
|
||||||
connectors.num_connectors = 0;
|
connectors.num_connectors = 0;
|
||||||
|
connectors.has_any_crtc_with_vrr_enabled = false;
|
||||||
get_drm_connectors(drm_fd, &connectors);
|
get_drm_connectors(drm_fd, &connectors);
|
||||||
|
|
||||||
drmModePlaneResPtr planes = drmModeGetPlaneResources(drm_fd);
|
drmModePlaneResPtr planes = drmModeGetPlaneResources(drm_fd);
|
||||||
if(!planes)
|
if(!planes)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
bool found_cursor = false;
|
||||||
for(uint32_t i = 0; i < planes->count_planes; ++i) {
|
for(uint32_t i = 0; i < planes->count_planes; ++i) {
|
||||||
drmModePlanePtr plane = nullptr;
|
drmModePlanePtr plane = nullptr;
|
||||||
const drm_connector *connector = nullptr;
|
const drm_connector *connector = nullptr;
|
||||||
int crtc_x = 0;
|
int crtc_x = 0;
|
||||||
int crtc_y = 0;
|
int crtc_y = 0;
|
||||||
int crtc_id = 0;
|
int crtc_id = 0;
|
||||||
bool is_cursor = false;
|
|
||||||
uint32_t property_mask = 0;
|
uint32_t property_mask = 0;
|
||||||
|
|
||||||
plane = drmModeGetPlane(drm_fd, planes->planes[i]);
|
plane = drmModeGetPlane(drm_fd, planes->planes[i]);
|
||||||
@@ -414,7 +462,7 @@ namespace gsr {
|
|||||||
if(!plane->fb_id)
|
if(!plane->fb_id)
|
||||||
goto next;
|
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)
|
if(property_mask != plane_property_all || crtc_id <= 0)
|
||||||
goto next;
|
goto next;
|
||||||
|
|
||||||
@@ -426,6 +474,7 @@ namespace gsr {
|
|||||||
latest_cursor_position.x = crtc_x;
|
latest_cursor_position.x = crtc_x;
|
||||||
latest_cursor_position.y = crtc_y;
|
latest_cursor_position.y = crtc_y;
|
||||||
latest_crtc_id = crtc_id;
|
latest_crtc_id = crtc_id;
|
||||||
|
found_cursor = true;
|
||||||
drmModeFreePlane(plane);
|
drmModeFreePlane(plane);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -434,6 +483,11 @@ namespace gsr {
|
|||||||
drmModeFreePlane(plane);
|
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);
|
drmModeFreePlaneResources(planes);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
#include "../include/CursorTrackerX11.hpp"
|
#include "../../include/CursorTracker/CursorTrackerX11.hpp"
|
||||||
#include "../include/WindowUtils.hpp"
|
#include "../../include/WindowUtils.hpp"
|
||||||
|
|
||||||
namespace gsr {
|
namespace gsr {
|
||||||
CursorTrackerX11::CursorTrackerX11(Display *dpy) : dpy(dpy) {
|
CursorTrackerX11::CursorTrackerX11(Display *dpy) : dpy(dpy) {
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
#include "../include/GlobalHotkeysJoystick.hpp"
|
#include "../../include/GlobalHotkeys/GlobalHotkeysJoystick.hpp"
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
#include "../include/GlobalHotkeysLinux.hpp"
|
#include "../../include/GlobalHotkeys/GlobalHotkeysLinux.hpp"
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
#include "../include/GlobalHotkeysX11.hpp"
|
#include "../../include/GlobalHotkeys/GlobalHotkeysX11.hpp"
|
||||||
#include <X11/keysym.h>
|
#include <X11/keysym.h>
|
||||||
#include <mglpp/window/Event.hpp>
|
#include <mglpp/window/Event.hpp>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
@@ -12,10 +12,10 @@
|
|||||||
#include "../include/gui/Utils.hpp"
|
#include "../include/gui/Utils.hpp"
|
||||||
#include "../include/gui/PageStack.hpp"
|
#include "../include/gui/PageStack.hpp"
|
||||||
#include "../include/WindowUtils.hpp"
|
#include "../include/WindowUtils.hpp"
|
||||||
#include "../include/GlobalHotkeys.hpp"
|
#include "../include/GlobalHotkeys/GlobalHotkeys.hpp"
|
||||||
#include "../include/GlobalHotkeysLinux.hpp"
|
#include "../include/GlobalHotkeys/GlobalHotkeysLinux.hpp"
|
||||||
#include "../include/CursorTrackerX11.hpp"
|
#include "../include/CursorTracker/CursorTrackerX11.hpp"
|
||||||
#include "../include/CursorTrackerWayland.hpp"
|
#include "../include/CursorTracker/CursorTrackerWayland.hpp"
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
@@ -207,24 +207,20 @@ namespace gsr {
|
|||||||
return false;
|
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) {
|
static const Monitor* find_monitor_at_position(const std::vector<Monitor> &monitors, mgl::vec2i pos) {
|
||||||
assert(!monitors.empty());
|
|
||||||
for(const Monitor &monitor : monitors) {
|
for(const Monitor &monitor : monitors) {
|
||||||
if(mgl::IntRect(monitor.position, monitor.size).contains(pos))
|
if(mgl::IntRect(monitor.position, monitor.size).contains(pos))
|
||||||
return &monitor;
|
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) {
|
static const Monitor* find_monitor_by_name(const std::vector<Monitor> &monitors, const std::string &name) {
|
||||||
assert(!monitors.empty());
|
|
||||||
for(const Monitor &monitor : monitors) {
|
for(const Monitor &monitor : monitors) {
|
||||||
if(monitor.name == name)
|
if(monitor.name == name)
|
||||||
return &monitor;
|
return &monitor;
|
||||||
}
|
}
|
||||||
return &monitors.front();
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string get_power_supply_online_filepath() {
|
static std::string get_power_supply_online_filepath() {
|
||||||
@@ -894,10 +890,14 @@ namespace gsr {
|
|||||||
const Monitor *focused_monitor = nullptr;
|
const Monitor *focused_monitor = nullptr;
|
||||||
if(cursor_info) {
|
if(cursor_info) {
|
||||||
focused_monitor = find_monitor_by_name(monitors, cursor_info->monitor_name);
|
focused_monitor = find_monitor_by_name(monitors, cursor_info->monitor_name);
|
||||||
|
if(!focused_monitor)
|
||||||
|
focused_monitor = &monitors.front();
|
||||||
cursor_position = cursor_info->position;
|
cursor_position = cursor_info->position;
|
||||||
} else {
|
} 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);
|
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);
|
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.
|
// Wayland doesn't allow XGrabPointer/XGrabKeyboard when a wayland application is focused.
|
||||||
@@ -933,8 +933,11 @@ namespace gsr {
|
|||||||
// when a compositor isn't running.
|
// 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;
|
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");
|
fprintf(stderr, "error: failed to create window\n");
|
||||||
|
window.reset();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
//window->set_low_latency(true);
|
//window->set_low_latency(true);
|
||||||
|
|
||||||
@@ -1028,6 +1031,9 @@ namespace gsr {
|
|||||||
if(paused)
|
if(paused)
|
||||||
update_ui_recording_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
|
// Wayland compositors have retarded fullscreen animations that we cant disable in a proper way
|
||||||
// without messing up window position.
|
// without messing up window position.
|
||||||
show_overlay_timeout_seconds = prevent_game_minimizing ? 0.0 : 0.15;
|
show_overlay_timeout_seconds = prevent_game_minimizing ? 0.0 : 0.15;
|
||||||
@@ -1620,9 +1626,9 @@ namespace gsr {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if(is_capture_target_monitor(recording_capture_target.c_str()))
|
if(is_capture_target_monitor(recording_capture_target.c_str()))
|
||||||
snprintf(msg, sizeof(msg), "Saved a recording of this monitor to %s", focused_window_name.c_str());
|
snprintf(msg, sizeof(msg), "Saved a recording of this monitor to \"%s\"", focused_window_name.c_str());
|
||||||
else
|
else
|
||||||
snprintf(msg, sizeof(msg), "Saved a recording of %s to '%s'", recording_capture_target.c_str(), focused_window_name.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();
|
capture_target = recording_capture_target.c_str();
|
||||||
break;
|
break;
|
||||||
@@ -1638,9 +1644,9 @@ namespace gsr {
|
|||||||
snprintf(duration, sizeof(duration), " ");
|
snprintf(duration, sizeof(duration), " ");
|
||||||
|
|
||||||
if(is_capture_target_monitor(recording_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());
|
snprintf(msg, sizeof(msg), "Saved a%sreplay of this monitor to \"%s\"", duration, focused_window_name.c_str());
|
||||||
else
|
else
|
||||||
snprintf(msg, sizeof(msg), "Saved a%sreplay of %s to '%s'", duration, recording_capture_target.c_str(), focused_window_name.c_str());
|
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();
|
capture_target = recording_capture_target.c_str();
|
||||||
break;
|
break;
|
||||||
@@ -1650,9 +1656,9 @@ namespace gsr {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if(is_capture_target_monitor(screenshot_capture_target.c_str()))
|
if(is_capture_target_monitor(screenshot_capture_target.c_str()))
|
||||||
snprintf(msg, sizeof(msg), "Saved a screenshot of this monitor to %s", focused_window_name.c_str());
|
snprintf(msg, sizeof(msg), "Saved a screenshot of this monitor to \"%s\"", focused_window_name.c_str());
|
||||||
else
|
else
|
||||||
snprintf(msg, sizeof(msg), "Saved a screenshot of %s to %s", screenshot_capture_target.c_str(), focused_window_name.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();
|
capture_target = screenshot_capture_target.c_str();
|
||||||
break;
|
break;
|
||||||
@@ -1968,6 +1974,7 @@ namespace gsr {
|
|||||||
record_dropdown_button_ptr->set_item_icon("pause", &get_theme().pause_texture);
|
record_dropdown_button_ptr->set_item_icon("pause", &get_theme().pause_texture);
|
||||||
record_dropdown_button_ptr->set_item_enabled("pause", false);
|
record_dropdown_button_ptr->set_item_enabled("pause", false);
|
||||||
paused = false;
|
paused = false;
|
||||||
|
replay_recording = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Overlay::update_ui_streaming_started() {
|
void Overlay::update_ui_streaming_started() {
|
||||||
@@ -2133,8 +2140,25 @@ namespace gsr {
|
|||||||
cursor_info = cursor_tracker->get_latest_cursor_info();
|
cursor_info = cursor_tracker->get_latest_cursor_info();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(cursor_info)
|
std::string focused_monitor_name;
|
||||||
return cursor_info->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())
|
else if(!capture_options.monitors.empty())
|
||||||
return capture_options.monitors.front().name;
|
return capture_options.monitors.front().name;
|
||||||
else
|
else
|
||||||
@@ -2286,6 +2310,11 @@ namespace gsr {
|
|||||||
args.push_back("yes");
|
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];
|
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);
|
add_common_gpu_screen_recorder_args(args, config.replay_config.record_options, audio_tracks, video_bitrate, size, region_str, sizeof(region_str), region_selector);
|
||||||
|
|
||||||
|
|||||||
@@ -130,8 +130,6 @@ namespace gsr {
|
|||||||
exit_status = -1;
|
exit_status = -1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer[bytes_read] = '\0';
|
|
||||||
result.append(buffer, bytes_read);
|
result.append(buffer, bytes_read);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,11 @@ namespace gsr {
|
|||||||
return str.size() >= len && memcmp(str.data(), substr, len) == 0;
|
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() {
|
std::string get_home_dir() {
|
||||||
const char *home_dir = getenv("HOME");
|
const char *home_dir = getenv("HOME");
|
||||||
if(!home_dir) {
|
if(!home_dir) {
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ namespace gsr {
|
|||||||
|
|
||||||
void ComboBox::add_item(const std::string &text, const std::string &id) {
|
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.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);
|
//items.back().text.set_max_rows(1);
|
||||||
dirty = true;
|
dirty = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
#include "../../include/gui/GlobalSettingsPage.hpp"
|
#include "../../include/gui/GlobalSettingsPage.hpp"
|
||||||
|
|
||||||
#include "../../include/Overlay.hpp"
|
#include "../../include/Overlay.hpp"
|
||||||
#include "../../include/GlobalHotkeys.hpp"
|
|
||||||
#include "../../include/Theme.hpp"
|
#include "../../include/Theme.hpp"
|
||||||
#include "../../include/Process.hpp"
|
#include "../../include/Process.hpp"
|
||||||
#include "../../include/gui/GsrPage.hpp"
|
#include "../../include/gui/GsrPage.hpp"
|
||||||
@@ -420,10 +419,10 @@ 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_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_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_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_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"));
|
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"));
|
||||||
|
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"));
|
||||||
return subsection;
|
return subsection;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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()) {
|
if(items.empty()) {
|
||||||
static std::string dummy;
|
static std::string dummy;
|
||||||
return dummy;
|
return dummy;
|
||||||
@@ -177,4 +177,13 @@ namespace gsr {
|
|||||||
return items[selected_item].id;
|
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));
|
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);
|
auto audio_device_box = std::make_unique<ComboBox>(&get_theme().body_font);
|
||||||
for(const auto &audio_device : audio_devices) {
|
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;
|
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 *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));
|
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);
|
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));
|
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;
|
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);
|
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->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(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());
|
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()));
|
audio_device_list->add_widget(create_remove_audio_device_button(audio_input_list_ptr, audio_device_list.get()));
|
||||||
return audio_device_list;
|
return audio_device_list;
|
||||||
}
|
}
|
||||||
@@ -254,13 +264,22 @@ namespace gsr {
|
|||||||
return button;
|
return button;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<Button> SettingsPage::create_add_audio_device_button(List *audio_input_list_ptr) {
|
std::unique_ptr<Button> SettingsPage::create_add_audio_output_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));
|
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));
|
||||||
add_audio_track_button->on_click = [this, audio_input_list_ptr]() {
|
button->on_click = [this, audio_input_list_ptr]() {
|
||||||
audio_devices = get_audio_devices();
|
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) {
|
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) {
|
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);
|
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->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_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()));
|
application_audio_list->add_widget(create_remove_audio_device_button(audio_input_list_ptr, application_audio_list.get()));
|
||||||
return application_audio_list;
|
return application_audio_list;
|
||||||
@@ -294,7 +313,7 @@ namespace gsr {
|
|||||||
std::unique_ptr<List> SettingsPage::create_custom_application_audio(List *audio_input_list_ptr) {
|
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);
|
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->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(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()));
|
application_audio_list->add_widget(create_remove_audio_device_button(audio_input_list_ptr, application_audio_list.get()));
|
||||||
return application_audio_list;
|
return application_audio_list;
|
||||||
@@ -314,7 +333,8 @@ namespace gsr {
|
|||||||
|
|
||||||
std::unique_ptr<List> SettingsPage::create_add_audio_buttons(List *audio_input_list_ptr) {
|
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);
|
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));
|
list->add_widget(create_add_application_audio_button(audio_input_list_ptr));
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
@@ -715,7 +735,7 @@ namespace gsr {
|
|||||||
auto list = std::make_unique<List>(List::Orientation::HORIZONTAL, List::Alignment::CENTER);
|
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);
|
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(1, 86400);
|
||||||
replay_time_entry_ptr = replay_time_entry.get();
|
replay_time_entry_ptr = replay_time_entry.get();
|
||||||
list->add_widget(std::move(replay_time_entry));
|
list->add_widget(std::move(replay_time_entry));
|
||||||
|
|
||||||
@@ -733,6 +753,24 @@ namespace gsr {
|
|||||||
return replay_time_list;
|
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() {
|
std::unique_ptr<RadioButton> SettingsPage::create_start_replay_automatically() {
|
||||||
char fullscreen_text[256];
|
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)");
|
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;
|
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 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 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;
|
const double video_filesize_mb = ((double)replay_time_seconds * (double)video_bitrate_bps) / 1000.0 / 1000.0 * 1.024;
|
||||||
|
|
||||||
char buffer[256];
|
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);
|
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)));
|
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);
|
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());
|
general_list->add_widget(create_save_replay_in_game_folder());
|
||||||
if(gsr_info->system_info.gsr_version >= GsrVersion{5, 0, 3})
|
if(gsr_info->system_info.gsr_version >= GsrVersion{5, 0, 3})
|
||||||
general_list->add_widget(create_restart_replay_on_save());
|
general_list->add_widget(create_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>("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 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");
|
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");
|
view_radio_button_ptr->on_selection_changed("Simple", "simple");
|
||||||
|
|
||||||
replay_time_entry_ptr->on_changed = [this](const std::string&) {
|
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();
|
update_replay_time_text();
|
||||||
};
|
};
|
||||||
|
|
||||||
video_bitrate_entry_ptr->on_changed = [this](const std::string&) {
|
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));
|
audio_input_list_ptr->add_widget(std::move(application_audio_widget));
|
||||||
}
|
}
|
||||||
} else if(starts_with(audio_input, "device:")) {
|
} 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));
|
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));
|
audio_input_list_ptr->add_widget(std::move(audio_track_widget));
|
||||||
} else {
|
} 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));
|
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_device_box->set_selected_item(audio_input);
|
||||||
audio_input_list_ptr->add_widget(std::move(audio_track_widget));
|
audio_input_list_ptr->add_widget(std::move(audio_track_widget));
|
||||||
@@ -1173,6 +1216,7 @@ namespace gsr {
|
|||||||
|
|
||||||
void SettingsPage::load_replay() {
|
void SettingsPage::load_replay() {
|
||||||
load_common(config.replay_config.record_options);
|
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);
|
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);
|
save_replay_in_game_folder_ptr->set_checked(config.replay_config.save_video_in_game_folder);
|
||||||
if(restart_replay_on_save)
|
if(restart_replay_on_save)
|
||||||
@@ -1185,8 +1229,8 @@ namespace gsr {
|
|||||||
|
|
||||||
if(config.replay_config.replay_time < 2)
|
if(config.replay_config.replay_time < 2)
|
||||||
config.replay_config.replay_time = 2;
|
config.replay_config.replay_time = 2;
|
||||||
if(config.replay_config.replay_time > 10800)
|
if(config.replay_config.replay_time > 86400)
|
||||||
config.replay_config.replay_time = 10800;
|
config.replay_config.replay_time = 86400;
|
||||||
replay_time_entry_ptr->set_text(std::to_string(config.replay_config.replay_time));
|
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.save_directory = save_directory_button_ptr->get_text();
|
||||||
config.replay_config.container = container_box_ptr->get_selected_id();
|
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_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) {
|
if(config.replay_config.replay_time < 5) {
|
||||||
config.replay_config.replay_time = 5;
|
config.replay_config.replay_time = 5;
|
||||||
|
|||||||
40
src/main.cpp
40
src/main.cpp
@@ -159,18 +159,35 @@ static bool is_flatpak() {
|
|||||||
return getenv("FLATPAK_ID") != nullptr;
|
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() {
|
static void usage() {
|
||||||
printf("usage: gsr-ui [action]\n");
|
printf("usage: gsr-ui [action]\n");
|
||||||
printf("OPTIONS:\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-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);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class LaunchAction {
|
enum class LaunchAction {
|
||||||
LAUNCH_SHOW,
|
LAUNCH_SHOW,
|
||||||
LAUNCH_HIDE
|
LAUNCH_HIDE,
|
||||||
|
LAUNCH_DAEMON
|
||||||
};
|
};
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
@@ -191,18 +208,17 @@ int main(int argc, char **argv) {
|
|||||||
launch_action = LaunchAction::LAUNCH_SHOW;
|
launch_action = LaunchAction::LAUNCH_SHOW;
|
||||||
} else if(strcmp(launch_action_opt, "launch-hide") == 0) {
|
} else if(strcmp(launch_action_opt, "launch-hide") == 0) {
|
||||||
launch_action = LaunchAction::LAUNCH_HIDE;
|
launch_action = LaunchAction::LAUNCH_HIDE;
|
||||||
|
} else if(strcmp(launch_action_opt, "launch-daemon") == 0) {
|
||||||
|
launch_action = LaunchAction::LAUNCH_DAEMON;
|
||||||
} else {
|
} 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();
|
usage();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
usage();
|
usage();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(is_flatpak())
|
set_display_server_environment_variables();
|
||||||
install_flatpak_systemd_service();
|
|
||||||
else
|
|
||||||
remove_flatpak_systemd_service();
|
|
||||||
|
|
||||||
// TODO: This is a shitty method to detect if multiple instances of gsr-ui is running but this will work properly even in flatpak
|
// 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.
|
// that uses pid sandboxing. Replace this with a better method once we no longer rely on linux global hotkeys on some platform.
|
||||||
@@ -210,6 +226,9 @@ int main(int argc, char **argv) {
|
|||||||
// What do? creating a pid file doesn't work in flatpak either.
|
// What do? creating a pid file doesn't work in flatpak either.
|
||||||
// TODO: This doesn't work in flatpak when disabling hotkeys.
|
// TODO: This doesn't work in flatpak when disabling hotkeys.
|
||||||
if(is_gsr_ui_virtual_keyboard_running() || gsr::pidof("gsr-ui", getpid()) != -1) {
|
if(is_gsr_ui_virtual_keyboard_running() || gsr::pidof("gsr-ui", getpid()) != -1) {
|
||||||
|
if(launch_action == LaunchAction::LAUNCH_DAEMON)
|
||||||
|
return 1;
|
||||||
|
|
||||||
gsr::Rpc rpc;
|
gsr::Rpc rpc;
|
||||||
if(rpc.open("gsr-ui") && rpc.write("show_ui\n", 8)) {
|
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");
|
fprintf(stderr, "Error: another instance of gsr-ui is already running, opening that one instead\n");
|
||||||
@@ -221,6 +240,11 @@ int main(int argc, char **argv) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(is_flatpak())
|
||||||
|
install_flatpak_systemd_service();
|
||||||
|
else
|
||||||
|
remove_flatpak_systemd_service();
|
||||||
|
|
||||||
// Stop nvidia driver from buffering frames
|
// Stop nvidia driver from buffering frames
|
||||||
setenv("__GL_MaxFramesAllowed", "1", true);
|
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,
|
// If this is set to 1 then cuGraphicsGLRegisterImage will fail for egl context with error: invalid OpenGL or DirectX context,
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ static void keyboard_event_fetch_update_key_states(keyboard_event *self, event_e
|
|||||||
if(ioctl(fd, EVIOCGKEY(KEY_STATES_SIZE), extra_data->key_states) == -1)
|
if(ioctl(fd, EVIOCGKEY(KEY_STATES_SIZE), extra_data->key_states) == -1)
|
||||||
fprintf(stderr, "Warning: failed to fetch key states for device: /dev/input/event%d\n", extra_data->dev_input_id);
|
fprintf(stderr, "Warning: failed to fetch key states for device: /dev/input/event%d\n", extra_data->dev_input_id);
|
||||||
|
|
||||||
if(!keyboard_event_has_exclusive_grab(self) || extra_data->grabbed)
|
if(!keyboard_event_has_exclusive_grab(self) || extra_data->grabbed || extra_data->is_non_keyboard_device)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
extra_data->num_keys_pressed = keyboard_event_get_num_keys_pressed(extra_data->key_states);
|
extra_data->num_keys_pressed = keyboard_event_get_num_keys_pressed(extra_data->key_states);
|
||||||
@@ -106,7 +106,7 @@ static void keyboard_event_process_key_state_change(keyboard_event *self, const
|
|||||||
|
|
||||||
extra_data->key_states[byte_index] = key_byte_state;
|
extra_data->key_states[byte_index] = key_byte_state;
|
||||||
|
|
||||||
if(!keyboard_event_has_exclusive_grab(self) || extra_data->grabbed)
|
if(!keyboard_event_has_exclusive_grab(self) || extra_data->grabbed || extra_data->is_non_keyboard_device)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if(extra_data->num_keys_pressed == 0) {
|
if(extra_data->num_keys_pressed == 0) {
|
||||||
@@ -193,7 +193,8 @@ static void keyboard_event_process_input_event_data(keyboard_event *self, event_
|
|||||||
//fprintf(stderr, "fd: %d, type: %d, pressed %d, value: %d\n", fd, event.type, event.code, event.value);
|
//fprintf(stderr, "fd: %d, type: %d, pressed %d, value: %d\n", fd, event.type, event.code, event.value);
|
||||||
//}
|
//}
|
||||||
|
|
||||||
if(event.type == EV_KEY && is_keyboard_key(event.code)) {
|
const bool keyboard_key = is_keyboard_key(event.code);
|
||||||
|
if(event.type == EV_KEY && keyboard_key) {
|
||||||
keyboard_event_process_key_state_change(self, &event, extra_data, fd);
|
keyboard_event_process_key_state_change(self, &event, extra_data, fd);
|
||||||
const uint32_t modifier_bit = keycode_to_modifier_bit(event.code);
|
const uint32_t modifier_bit = keycode_to_modifier_bit(event.code);
|
||||||
if(modifier_bit == 0) {
|
if(modifier_bit == 0) {
|
||||||
@@ -214,6 +215,20 @@ static void keyboard_event_process_input_event_data(keyboard_event *self, event_
|
|||||||
if(write(self->uinput_fd, &event, sizeof(event)) != sizeof(event))
|
if(write(self->uinput_fd, &event, sizeof(event)) != sizeof(event))
|
||||||
fprintf(stderr, "Error: failed to write event data to virtual keyboard for exclusively grabbed device\n");
|
fprintf(stderr, "Error: failed to write event data to virtual keyboard for exclusively grabbed device\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(!extra_data->is_possibly_non_keyboard_device)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* TODO: What if some key is being pressed down while this is done? will it remain pressed down? */
|
||||||
|
if(!extra_data->is_non_keyboard_device && (event.type == EV_REL || event.type == EV_ABS || (event.type == EV_KEY && !keyboard_key))) {
|
||||||
|
fprintf(stderr, "Info: device /dev/input/event%d is likely a non-keyboard device as it received a non-keyboard event. This device will be ignored\n", extra_data->dev_input_id);
|
||||||
|
extra_data->is_non_keyboard_device = true;
|
||||||
|
if(extra_data->grabbed) {
|
||||||
|
extra_data->grabbed = false;
|
||||||
|
ioctl(fd, EVIOCGRAB, 0);
|
||||||
|
fprintf(stderr, "Info: ungrabbed device: /dev/input/event%d\n", extra_data->dev_input_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Retarded linux takes very long time to close /dev/input/eventN files, even though they are virtual and opened read-only */
|
/* Retarded linux takes very long time to close /dev/input/eventN files, even though they are virtual and opened read-only */
|
||||||
@@ -292,6 +307,46 @@ static bool dev_input_is_virtual(int dev_input_id) {
|
|||||||
return is_virtual;
|
return is_virtual;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline bool supports_key(unsigned char *key_bits, unsigned int key) {
|
||||||
|
return key_bits[key/8] & (1 << (key % 8));
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool supports_keyboard_keys(unsigned char *key_bits) {
|
||||||
|
const int keys[2] = { KEY_A, KEY_ESC };
|
||||||
|
for(int i = 0; i < 2; ++i) {
|
||||||
|
if(supports_key(key_bits, keys[i]))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool supports_mouse_keys(unsigned char *key_bits) {
|
||||||
|
const int keys[2] = { BTN_MOUSE, BTN_LEFT };
|
||||||
|
for(int i = 0; i < 2; ++i) {
|
||||||
|
if(supports_key(key_bits, keys[i]))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool supports_joystick_keys(unsigned char *key_bits) {
|
||||||
|
const int keys[9] = { BTN_JOYSTICK, BTN_A, BTN_B, BTN_X, BTN_Y, BTN_SELECT, BTN_START, BTN_SELECT, BTN_TRIGGER_HAPPY1 };
|
||||||
|
for(int i = 0; i < 9; ++i) {
|
||||||
|
if(supports_key(key_bits, keys[i]))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool supports_wheel_keys(unsigned char *key_bits) {
|
||||||
|
const int keys[2] = { BTN_WHEEL, BTN_GEAR_DOWN };
|
||||||
|
for(int i = 0; i < 2; ++i) {
|
||||||
|
if(supports_key(key_bits, keys[i]))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
static bool keyboard_event_try_add_device_if_keyboard(keyboard_event *self, const char *dev_input_filepath) {
|
static bool keyboard_event_try_add_device_if_keyboard(keyboard_event *self, const char *dev_input_filepath) {
|
||||||
const int dev_input_id = get_dev_input_id_from_filepath(dev_input_filepath);
|
const int dev_input_id = get_dev_input_id_from_filepath(dev_input_filepath);
|
||||||
if(dev_input_id == -1)
|
if(dev_input_id == -1)
|
||||||
@@ -320,15 +375,11 @@ static bool keyboard_event_try_add_device_if_keyboard(keyboard_event *self, cons
|
|||||||
unsigned char key_bits[KEY_MAX/8 + 1] = {0};
|
unsigned char key_bits[KEY_MAX/8 + 1] = {0};
|
||||||
ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(key_bits)), &key_bits);
|
ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(key_bits)), &key_bits);
|
||||||
|
|
||||||
const bool supports_key_a = key_bits[KEY_A/8] & (1 << (KEY_A % 8));
|
const bool supports_key_events = supports_keyboard_keys(key_bits);
|
||||||
const bool supports_key_esc = key_bits[KEY_ESC/8] & (1 << (KEY_ESC % 8));
|
const bool supports_mouse_events = supports_mouse_keys(key_bits);
|
||||||
const bool supports_key_volume_up = key_bits[KEY_VOLUMEUP/8] & (1 << (KEY_VOLUMEUP % 8));
|
const bool supports_joystick_events = supports_joystick_keys(key_bits);
|
||||||
const bool supports_key_events = supports_key_a || supports_key_esc || supports_key_volume_up;
|
const bool supports_wheel_events = supports_wheel_keys(key_bits);
|
||||||
|
|
||||||
const bool supports_mouse_events = key_bits[BTN_MOUSE/8] & (1 << (BTN_MOUSE % 8));
|
|
||||||
//const bool supports_touch_events = key_bits[BTN_TOUCH/8] & (1 << (BTN_TOUCH % 8));
|
|
||||||
const bool supports_joystick_events = key_bits[BTN_JOYSTICK/8] & (1 << (BTN_JOYSTICK % 8));
|
|
||||||
const bool supports_wheel_events = key_bits[BTN_WHEEL/8] & (1 << (BTN_WHEEL % 8));
|
|
||||||
if(supports_key_events && (is_virtual_device || (!supports_joystick_events && !supports_wheel_events))) {
|
if(supports_key_events && (is_virtual_device || (!supports_joystick_events && !supports_wheel_events))) {
|
||||||
unsigned char *key_states = calloc(1, KEY_STATES_SIZE);
|
unsigned char *key_states = calloc(1, KEY_STATES_SIZE);
|
||||||
unsigned char *key_presses_grabbed = calloc(1, KEY_STATES_SIZE);
|
unsigned char *key_presses_grabbed = calloc(1, KEY_STATES_SIZE);
|
||||||
@@ -349,6 +400,7 @@ static bool keyboard_event_try_add_device_if_keyboard(keyboard_event *self, cons
|
|||||||
};
|
};
|
||||||
|
|
||||||
if(supports_mouse_events || supports_joystick_events || supports_wheel_events) {
|
if(supports_mouse_events || supports_joystick_events || supports_wheel_events) {
|
||||||
|
self->event_extra_data[self->num_event_polls].is_possibly_non_keyboard_device = true;
|
||||||
fprintf(stderr, "Info: device not grabbed yet because it might be a mouse: /dev/input/event%d\n", dev_input_id);
|
fprintf(stderr, "Info: device not grabbed yet because it might be a mouse: /dev/input/event%d\n", dev_input_id);
|
||||||
fsync(fd);
|
fsync(fd);
|
||||||
if(ioctl(fd, EVIOCGKEY(KEY_STATES_SIZE), self->event_extra_data[self->num_event_polls].key_states) == -1)
|
if(ioctl(fd, EVIOCGKEY(KEY_STATES_SIZE), self->event_extra_data[self->num_event_polls].key_states) == -1)
|
||||||
@@ -404,8 +456,10 @@ static void keyboard_event_remove_event(keyboard_event *self, int index) {
|
|||||||
if(index < 0 || index >= self->num_event_polls)
|
if(index < 0 || index >= self->num_event_polls)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ioctl(self->event_polls[index].fd, EVIOCGRAB, 0);
|
if(self->event_polls[index].fd > 0) {
|
||||||
close(self->event_polls[index].fd);
|
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_states);
|
||||||
free(self->event_extra_data[index].key_presses_grabbed);
|
free(self->event_extra_data[index].key_presses_grabbed);
|
||||||
|
|
||||||
@@ -435,19 +489,20 @@ 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_KEY) != -1);
|
||||||
success &= (ioctl(fd, UI_SET_EVBIT, EV_REP) != -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_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);
|
success &= (ioctl(fd, UI_SET_MSCBIT, MSC_SCAN) != -1);
|
||||||
for(int i = 1; i < KEY_MAX; ++i) {
|
for(int i = 1; i < KEY_MAX; ++i) {
|
||||||
|
// TODO: Check for joystick button? if we accidentally grab joystick
|
||||||
if(is_keyboard_key(i) || is_mouse_button(i))
|
if(is_keyboard_key(i) || is_mouse_button(i))
|
||||||
success &= (ioctl(fd, UI_SET_KEYBIT, i) != -1);
|
success &= (ioctl(fd, UI_SET_KEYBIT, i) != -1);
|
||||||
}
|
}
|
||||||
for(int i = 0; i < REL_MAX; ++i) {
|
for(int i = 0; i < REL_MAX; ++i) {
|
||||||
success &= (ioctl(fd, UI_SET_RELBIT, i) != -1);
|
success &= (ioctl(fd, UI_SET_RELBIT, i) != -1);
|
||||||
}
|
}
|
||||||
for(int i = 0; i < LED_MAX; ++i) {
|
// for(int i = 0; i < LED_MAX; ++i) {
|
||||||
success &= (ioctl(fd, UI_SET_LEDBIT, i) != -1);
|
// success &= (ioctl(fd, UI_SET_LEDBIT, i) != -1);
|
||||||
}
|
// }
|
||||||
|
|
||||||
// success &= (ioctl(fd, UI_SET_EVBIT, EV_ABS) != -1);
|
// success &= (ioctl(fd, UI_SET_EVBIT, EV_ABS) != -1);
|
||||||
// success &= (ioctl(fd, UI_SET_ABSBIT, ABS_X) != -1);
|
// success &= (ioctl(fd, UI_SET_ABSBIT, ABS_X) != -1);
|
||||||
@@ -566,8 +621,10 @@ void keyboard_event_deinit(keyboard_event *self) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for(int i = 0; i < self->num_event_polls; ++i) {
|
for(int i = 0; i < self->num_event_polls; ++i) {
|
||||||
ioctl(self->event_polls[i].fd, EVIOCGRAB, 0);
|
if(self->event_polls[i].fd > 0) {
|
||||||
close(self->event_polls[i].fd);
|
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_states);
|
||||||
free(self->event_extra_data[i].key_presses_grabbed);
|
free(self->event_extra_data[i].key_presses_grabbed);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,6 +39,8 @@ typedef enum {
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
int dev_input_id;
|
int dev_input_id;
|
||||||
bool grabbed;
|
bool grabbed;
|
||||||
|
bool is_non_keyboard_device;
|
||||||
|
bool is_possibly_non_keyboard_device;
|
||||||
unsigned char *key_states;
|
unsigned char *key_states;
|
||||||
unsigned char *key_presses_grabbed;
|
unsigned char *key_presses_grabbed;
|
||||||
int num_keys_pressed;
|
int num_keys_pressed;
|
||||||
|
|||||||
Reference in New Issue
Block a user