aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2024-12-26 15:22:57 +0100
committerdec05eba <dec05eba@protonmail.com>2024-12-26 15:22:57 +0100
commite5b745d696c3ea14bfa9f51f75825befaa94a924 (patch)
tree99007ec672793771419dd78ecd90bf3554a3dab7
parentec6d4090af22db59991e9c621238c96795814379 (diff)
Mention that recording has to be restarted to apply changes. Fix stuck in repeat state if pressed while gsr-global-hotkey starts
-rw-r--r--TODO5
m---------depends/mglpp0
-rw-r--r--include/Config.hpp5
-rw-r--r--include/GsrInfo.hpp1
-rw-r--r--include/gui/SettingsPage.hpp2
-rw-r--r--src/Config.cpp41
-rw-r--r--src/GsrInfo.cpp2
-rw-r--r--src/Overlay.cpp20
-rw-r--r--src/gui/SettingsPage.cpp7
-rw-r--r--tools/gsr-global-hotkeys/keyboard_event.c50
-rw-r--r--tools/gsr-global-hotkeys/keyboard_event.h2
11 files changed, 118 insertions, 17 deletions
diff --git a/TODO b/TODO
index 7037227..5a8c031 100644
--- a/TODO
+++ b/TODO
@@ -109,4 +109,7 @@ Move ui hover code from ::draw to ::on_event, to properly handle widget event st
Save audio devices by name instead of id. This is more robust since audio id can change(?).
-Test gsr-global-hotkey on laptop keyboard (that has touchpad). \ No newline at end of file
+Improve linux global hotkeys startup time by parsing /proc/bus/input/devices instead of ioctl.
+
+Keyboard grabbing has some issues. If a key is grabbed while its being held down that it will be kept in held-down state (a hack exists to workaround this, but it may not work in all environments).
+ This also causes an issue where is a key is pressed before key is grabbed and then released while grabbed and then key is ungrabbed then the key will have to be pressed twice to register in the display server. \ No newline at end of file
diff --git a/depends/mglpp b/depends/mglpp
-Subproject 0c8ccb86a55e9b5b98ab68ca538ad7374308bae
+Subproject 05ba259af78f879d344883c0edb7318a6eec265
diff --git a/include/Config.hpp b/include/Config.hpp
index c61ca10..de96722 100644
--- a/include/Config.hpp
+++ b/include/Config.hpp
@@ -12,6 +12,9 @@ namespace gsr {
struct ConfigHotkey {
int64_t keysym = 0;
uint32_t modifiers = 0;
+
+ bool operator==(const ConfigHotkey &other) const;
+ bool operator!=(const ConfigHotkey &other) const;
};
struct RecordOptions {
@@ -94,6 +97,8 @@ namespace gsr {
struct Config {
Config(const SupportedCaptureOptions &capture_options);
+ bool operator==(const Config &other);
+ bool operator!=(const Config &other);
MainConfig main_config;
StreamingConfig streaming_config;
diff --git a/include/GsrInfo.hpp b/include/GsrInfo.hpp
index 6ec8e23..86df0b7 100644
--- a/include/GsrInfo.hpp
+++ b/include/GsrInfo.hpp
@@ -27,7 +27,6 @@ namespace gsr {
struct SupportedCaptureOptions {
bool window = false;
bool focused = false;
- bool screen = false;
bool portal = false;
std::vector<GsrMonitor> monitors;
};
diff --git a/include/gui/SettingsPage.hpp b/include/gui/SettingsPage.hpp
index 981c99a..1ab0cb2 100644
--- a/include/gui/SettingsPage.hpp
+++ b/include/gui/SettingsPage.hpp
@@ -32,6 +32,8 @@ namespace gsr {
void load();
void save();
void on_navigate_away_from_page() override;
+
+ std::function<void()> on_config_changed;
private:
std::unique_ptr<RadioButton> create_view_radio_button();
std::unique_ptr<ComboBox> create_record_area_box();
diff --git a/src/Config.cpp b/src/Config.cpp
index 4deaaf4..a9c8843 100644
--- a/src/Config.cpp
+++ b/src/Config.cpp
@@ -5,6 +5,7 @@
#include <limits.h>
#include <inttypes.h>
#include <libgen.h>
+#include <iostream>
#define FORMAT_I32 "%" PRIi32
#define FORMAT_I64 "%" PRIi64
@@ -13,6 +14,14 @@
#define CONFIG_FILE_VERSION 1
namespace gsr {
+ bool ConfigHotkey::operator==(const ConfigHotkey &other) const {
+ return keysym == other.keysym && modifiers == other.modifiers;
+ }
+
+ bool ConfigHotkey::operator!=(const ConfigHotkey &other) const {
+ return !operator==(other);
+ }
+
Config::Config(const SupportedCaptureOptions &capture_options) {
const std::string default_save_directory = get_videos_dir();
@@ -141,6 +150,38 @@ namespace gsr {
};
}
+ bool Config::operator==(const Config &other) {
+ const auto config_options = get_config_options(*this);
+ const auto config_options_other = get_config_options(const_cast<Config&>(other));
+ for(auto it : config_options) {
+ auto it_other = config_options_other.find(it.first);
+ if(it_other == config_options_other.end() || it_other->second.index() != it.second.index())
+ return false;
+
+ if(std::holds_alternative<bool*>(it.second)) {
+ if(*std::get<bool*>(it.second) != *std::get<bool*>(it_other->second))
+ return false;
+ } else if(std::holds_alternative<std::string*>(it.second)) {
+ if(*std::get<std::string*>(it.second) != *std::get<std::string*>(it_other->second))
+ return false;
+ } else if(std::holds_alternative<int32_t*>(it.second)) {
+ if(*std::get<int32_t*>(it.second) != *std::get<int32_t*>(it_other->second))
+ return false;
+ } else if(std::holds_alternative<ConfigHotkey*>(it.second)) {
+ if(*std::get<ConfigHotkey*>(it.second) != *std::get<ConfigHotkey*>(it_other->second))
+ return false;
+ } else if(std::holds_alternative<std::vector<std::string>*>(it.second)) {
+ if(*std::get<std::vector<std::string>*>(it.second) != *std::get<std::vector<std::string>*>(it_other->second))
+ return false;
+ }
+ }
+ return true;
+ }
+
+ bool Config::operator!=(const Config &other) {
+ return !operator==(other);
+ }
+
std::optional<Config> read_config(const SupportedCaptureOptions &capture_options) {
std::optional<Config> config;
diff --git a/src/GsrInfo.cpp b/src/GsrInfo.cpp
index c35ccfb..6665dc9 100644
--- a/src/GsrInfo.cpp
+++ b/src/GsrInfo.cpp
@@ -209,8 +209,6 @@ namespace gsr {
capture_options.window = true;
else if(line == "focused")
capture_options.focused = true;
- else if(line == "screen")
- capture_options.screen = true;
else if(line == "portal")
capture_options.portal = true;
else {
diff --git a/src/Overlay.cpp b/src/Overlay.cpp
index 628a862..3d6a603 100644
--- a/src/Overlay.cpp
+++ b/src/Overlay.cpp
@@ -906,6 +906,10 @@ namespace gsr {
button->on_click = [this](const std::string &id) {
if(id == "settings") {
auto replay_settings_page = std::make_unique<SettingsPage>(SettingsPage::Type::REPLAY, &gsr_info, config, &page_stack);
+ replay_settings_page->on_config_changed = [this]() {
+ if(recording_status == RecordingStatus::REPLAY)
+ show_notification("Replay settings have been modified.\nYou may need to restart replay to apply the changes.", 5.0, mgl::Color(255, 255, 255), get_color_theme().tint_color, NotificationType::REPLAY);
+ };
page_stack.push(std::move(replay_settings_page));
} else if(id == "save") {
on_press_save_replay();
@@ -927,6 +931,10 @@ namespace gsr {
button->on_click = [this](const std::string &id) {
if(id == "settings") {
auto record_settings_page = std::make_unique<SettingsPage>(SettingsPage::Type::RECORD, &gsr_info, config, &page_stack);
+ record_settings_page->on_config_changed = [this]() {
+ if(recording_status == RecordingStatus::RECORD)
+ show_notification("Recording settings have been modified.\nYou may need to restart recording to apply the changes.", 5.0, mgl::Color(255, 255, 255), get_color_theme().tint_color, NotificationType::RECORD);
+ };
page_stack.push(std::move(record_settings_page));
} else if(id == "pause") {
toggle_pause();
@@ -946,6 +954,10 @@ namespace gsr {
button->on_click = [this](const std::string &id) {
if(id == "settings") {
auto stream_settings_page = std::make_unique<SettingsPage>(SettingsPage::Type::STREAM, &gsr_info, config, &page_stack);
+ stream_settings_page->on_config_changed = [this]() {
+ if(recording_status == RecordingStatus::STREAM)
+ show_notification("Streaming settings have been modified.\nYou may need to restart streaming to apply the changes.", 5.0, mgl::Color(255, 255, 255), get_color_theme().tint_color, NotificationType::STREAM);
+ };
page_stack.push(std::move(stream_settings_page));
} else if(id == "start") {
on_press_start_stream();
@@ -1318,7 +1330,7 @@ namespace gsr {
show_notification("Replay stopped", 3.0, mgl::Color(255, 255, 255), get_color_theme().tint_color, NotificationType::REPLAY);
} else {
fprintf(stderr, "Warning: gpu-screen-recorder (%d) exited with exit status %d\n", (int)gpu_screen_recorder_process, exit_code);
- show_notification("Replay stopped because of an error", 3.0, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), NotificationType::REPLAY);
+ show_notification("Replay stopped because of an error. Verify if settings are correct", 3.0, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), NotificationType::REPLAY);
}
break;
}
@@ -1334,7 +1346,7 @@ namespace gsr {
show_notification("Streaming has stopped", 3.0, mgl::Color(255, 255, 255), get_color_theme().tint_color, NotificationType::STREAM);
} else {
fprintf(stderr, "Warning: gpu-screen-recorder (%d) exited with exit status %d\n", (int)gpu_screen_recorder_process, exit_code);
- show_notification("Streaming stopped because of an error", 3.0, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), NotificationType::STREAM);
+ show_notification("Streaming stopped because of an error. Verify if settings are correct", 3.0, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), NotificationType::STREAM);
}
break;
}
@@ -1399,7 +1411,7 @@ namespace gsr {
}
} 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", 3.0, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), NotificationType::RECORD);
+ show_notification("Failed to start/save recording. Verify if settings are correct", 3.0, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), NotificationType::RECORD);
}
}
@@ -1574,8 +1586,6 @@ namespace gsr {
// TODO: Also check x11 window when enabled (check if capture_target is a decminal/hex number)
if(capture_target == "focused") {
return capture_options.focused;
- } else if(capture_target == "screen") {
- return capture_options.screen;
} else if(capture_target == "portal") {
return capture_options.portal;
} else {
diff --git a/src/gui/SettingsPage.cpp b/src/gui/SettingsPage.cpp
index 28821d1..7613d11 100644
--- a/src/gui/SettingsPage.cpp
+++ b/src/gui/SettingsPage.cpp
@@ -64,9 +64,6 @@ namespace gsr {
// record_area_box->add_item("Window", "window");
if(capture_options.focused)
record_area_box->add_item("Follow focused window", "focused");
- // Do we really need this? it's only available on nvidia x11
- //if(capture_options.screen)
- // record_area_box->add_item("All monitors", "screen");
for(const auto &monitor : capture_options.monitors) {
char name[256];
snprintf(name, sizeof(name), "Monitor %s (%dx%d)", monitor.name.c_str(), monitor.size.x, monitor.size.y);
@@ -896,6 +893,7 @@ namespace gsr {
}
void SettingsPage::save() {
+ Config prev_config = config;
switch(type) {
case Type::REPLAY:
save_replay();
@@ -908,6 +906,9 @@ namespace gsr {
break;
}
save_config(config);
+
+ if(on_config_changed && config != prev_config)
+ on_config_changed();
}
static const std::string* get_application_audio_by_name_case_insensitive(const std::vector<std::string> &application_audio, const std::string &name) {
diff --git a/tools/gsr-global-hotkeys/keyboard_event.c b/tools/gsr-global-hotkeys/keyboard_event.c
index 5c2183f..8d27806 100644
--- a/tools/gsr-global-hotkeys/keyboard_event.c
+++ b/tools/gsr-global-hotkeys/keyboard_event.c
@@ -18,6 +18,10 @@
#define GSR_UI_VIRTUAL_KEYBOARD_NAME "gsr-ui virtual keyboard"
+#define KEY_RELEASE 0
+#define KEY_PRESS 1
+#define KEY_REPEAT 2
+
/*
We could get initial keyboard state with:
unsigned char key_states[KEY_MAX/8 + 1];
@@ -28,6 +32,22 @@ static bool keyboard_event_has_exclusive_grab(const keyboard_event *self) {
return self->uinput_fd > 0;
}
+static void keyboard_event_send_virtual_keyboard_event(keyboard_event *self, uint16_t code, int32_t value) {
+ if(self->uinput_fd <= 0)
+ return;
+
+ struct input_event event = {0};
+ event.type = EV_KEY;
+ event.code = code;
+ event.value = value;
+ write(self->uinput_fd, &event, sizeof(event));
+
+ event.type = EV_SYN;
+ event.code = 0;
+ event.value = SYN_REPORT;
+ write(self->uinput_fd, &event, sizeof(event));
+}
+
static void keyboard_event_process_input_event_data(keyboard_event *self, const event_extra_data *extra_data, int fd, key_callback callback, void *userdata) {
struct input_event event;
if(read(fd, &event, sizeof(event)) != sizeof(event)) {
@@ -35,12 +55,31 @@ static void keyboard_event_process_input_event_data(keyboard_event *self, const
return;
}
- // value = 1 == key pressed
- //if(event.type == EV_KEY && event.code == KEY_A && event.value == 1) {
+ //if(event.type == EV_KEY && event.code == KEY_A && event.value == KEY_PRESS) {
//fprintf(stderr, "fd: %d, type: %d, pressed %d, value: %d\n", fd, event.type, event.code, event.value);
//}
if(event.type == EV_KEY) {
+ /*
+ TODO: This is a hack! if a keyboard is grabbed while a key is being repeated then the key release will not be registered properly.
+ To deal with this we ignore repeat key that is sent without without pressed first and when the key is released we send key press and key release.
+ Maybe this needs to be done for all keys? Find a better solution if there is one!
+ */
+ const bool first_key_event = !self->has_received_key_event;
+ self->has_received_key_event = true;
+
+ if(first_key_event && event.value == KEY_REPEAT)
+ self->repeat_key_to_ignore = (int32_t)event.code;
+
+ if(self->repeat_key_to_ignore > 0 && event.code == self->repeat_key_to_ignore) {
+ if(event.value == KEY_RELEASE) {
+ self->repeat_key_to_ignore = 0;
+ keyboard_event_send_virtual_keyboard_event(self, event.code, KEY_PRESS);
+ keyboard_event_send_virtual_keyboard_event(self, event.code, KEY_RELEASE);
+ }
+ return;
+ }
+
switch(event.code) {
case KEY_LEFTSHIFT:
self->lshift_button_state = event.value >= 1 ? KEYBOARD_BUTTON_PRESSED : KEYBOARD_BUTTON_RELEASED;
@@ -141,10 +180,11 @@ static bool keyboard_event_try_add_device_if_keyboard(keyboard_event *self, cons
unsigned char key_bits[KEY_MAX/8 + 1] = {0};
ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(key_bits)), &key_bits);
- const bool supports_key_events = key_bits[KEY_A/8] & (1 << (KEY_A % 8));
- const bool supports_mouse_events = key_bits[BTN_MOUSE/8] & (1 << (BTN_MOUSE % 8));
+ const bool supports_key_events = key_bits[KEY_A/8] & (1 << (KEY_A % 8));
+ 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));
+ const bool supports_wheel_events = key_bits[BTN_WHEEL/8] & (1 << (BTN_WHEEL % 8));
if(supports_key_events && !supports_mouse_events && !supports_joystick_events && !supports_wheel_events) {
if(self->num_event_polls < MAX_EVENT_POLLS) {
bool grabbed = false;
diff --git a/tools/gsr-global-hotkeys/keyboard_event.h b/tools/gsr-global-hotkeys/keyboard_event.h
index ca96cab..67251fc 100644
--- a/tools/gsr-global-hotkeys/keyboard_event.h
+++ b/tools/gsr-global-hotkeys/keyboard_event.h
@@ -43,6 +43,8 @@ typedef struct {
int hotplug_event_index;
int uinput_fd;
bool stdout_failed;
+ int32_t repeat_key_to_ignore;
+ bool has_received_key_event;
hotplug_event hotplug_ev;