mirror of
https://repo.dec05eba.com/gpu-screen-recorder-ui
synced 2026-04-20 00:45:51 +09:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
71d28f8ba3 | ||
|
|
bb1e9c6616 | ||
|
|
e14bb0cbcf | ||
|
|
5a13bd2491 | ||
|
|
b875f96885 | ||
|
|
0d3d4229bf | ||
|
|
ed23f56a29 | ||
|
|
a9a1f9d01c |
2
TODO
2
TODO
@@ -245,3 +245,5 @@ Show a question mark beside options. When hovering the question mark show a tool
|
|||||||
Remove all mgl::Clock usage in Overlay. We only need to get the time once per update in Overlay::handle_events. Also get time in other places outside handle_events.
|
Remove all mgl::Clock usage in Overlay. We only need to get the time once per update in Overlay::handle_events. Also get time in other places outside handle_events.
|
||||||
|
|
||||||
Handle stopping replay/stream when recording is running (show notification that the video is saved and move the video to folder with game name).
|
Handle stopping replay/stream when recording is running (show notification that the video is saved and move the video to folder with game name).
|
||||||
|
|
||||||
|
Support translations.
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
#include <vector>
|
||||||
#include <mglpp/system/Clock.hpp>
|
#include <mglpp/system/Clock.hpp>
|
||||||
|
|
||||||
namespace gsr {
|
namespace gsr {
|
||||||
@@ -17,11 +18,16 @@ namespace gsr {
|
|||||||
private:
|
private:
|
||||||
bool run_gsr_global_hotkeys_set_leds(bool enabled);
|
bool run_gsr_global_hotkeys_set_leds(bool enabled);
|
||||||
void update_led(bool new_state);
|
void update_led(bool new_state);
|
||||||
|
void update_led_with_active_status();
|
||||||
|
void check_led_status_outdated();
|
||||||
private:
|
private:
|
||||||
pid_t gsr_global_hotkeys_pid = -1;
|
pid_t gsr_global_hotkeys_pid = -1;
|
||||||
bool led_indicator_on = false;
|
bool led_indicator_on = false;
|
||||||
bool led_enabled = false;
|
bool led_enabled = false;
|
||||||
bool perform_blink = false;
|
bool perform_blink = false;
|
||||||
mgl::Clock blink_timer;
|
mgl::Clock blink_timer;
|
||||||
|
|
||||||
|
std::vector<int> led_brightness_files;
|
||||||
|
mgl::Clock read_led_brightness_timer;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
project('gsr-ui', ['c', 'cpp'], version : '1.8.0', default_options : ['warning_level=2', 'cpp_std=c++17'], subproject_dir : 'depends')
|
project('gsr-ui', ['c', 'cpp'], version : '1.8.2', default_options : ['warning_level=2', 'cpp_std=c++17'], subproject_dir : 'depends')
|
||||||
|
|
||||||
add_project_arguments('-D_FILE_OFFSET_BITS=64', language : ['c', 'cpp'])
|
add_project_arguments('-D_FILE_OFFSET_BITS=64', language : ['c', 'cpp'])
|
||||||
|
|
||||||
@@ -66,7 +66,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.8.5"', language: ['c', 'cpp'])
|
add_project_arguments('-DGSR_FLATPAK_VERSION="5.9.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.8.0"
|
version = "1.8.2"
|
||||||
platforms = ["posix"]
|
platforms = ["posix"]
|
||||||
|
|
||||||
[lang.cpp]
|
[lang.cpp]
|
||||||
|
|||||||
@@ -193,7 +193,7 @@ namespace gsr {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(hotkey.key == 0) {
|
if(hotkey.key == 0 || hotkey.key == XK_VoidSymbol) {
|
||||||
//fprintf(stderr, "Error: GlobalHotkeysLinux::bind_key_press: hotkey requires a key\n");
|
//fprintf(stderr, "Error: GlobalHotkeysLinux::bind_key_press: hotkey requires a key\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,64 @@
|
|||||||
#include "../include/LedIndicator.hpp"
|
#include "../include/LedIndicator.hpp"
|
||||||
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <dirent.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
// TODO: Support hotplug for led indicator check (led_brightness_files)
|
||||||
|
|
||||||
namespace gsr {
|
namespace gsr {
|
||||||
|
static bool string_starts_with(const char *str, const char *sub) {
|
||||||
|
const int str_len = strlen(str);
|
||||||
|
const int sub_len = strlen(sub);
|
||||||
|
return str_len >= sub_len && memcmp(str, sub, sub_len) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool string_ends_with(const char *str, const char *sub) {
|
||||||
|
const int str_len = strlen(str);
|
||||||
|
const int sub_len = strlen(sub);
|
||||||
|
return str_len >= sub_len && memcmp(str + str_len - sub_len, sub, sub_len) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::vector<int> open_device_leds_brightness_files(const char *led_name_path) {
|
||||||
|
std::vector<int> files;
|
||||||
|
|
||||||
|
DIR *dir = opendir("/sys/class/leds");
|
||||||
|
if(!dir)
|
||||||
|
return files;
|
||||||
|
|
||||||
|
char brightness_filepath[1024];
|
||||||
|
struct dirent *entry;
|
||||||
|
while((entry = readdir(dir)) != NULL) {
|
||||||
|
if(entry->d_name[0] == '.')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if(!string_starts_with(entry->d_name, "input") || !string_ends_with(entry->d_name, led_name_path))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
snprintf(brightness_filepath, sizeof(brightness_filepath), "/sys/class/leds/%s/brightness", entry->d_name);
|
||||||
|
const int led_brightness_file_fd = open(brightness_filepath, O_RDONLY | O_NONBLOCK);
|
||||||
|
if(led_brightness_file_fd > 0)
|
||||||
|
files.push_back(led_brightness_file_fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
closedir(dir);
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
LedIndicator::LedIndicator() {
|
LedIndicator::LedIndicator() {
|
||||||
|
led_brightness_files = open_device_leds_brightness_files("scrolllock");
|
||||||
run_gsr_global_hotkeys_set_leds(false);
|
run_gsr_global_hotkeys_set_leds(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
LedIndicator::~LedIndicator() {
|
LedIndicator::~LedIndicator() {
|
||||||
|
for(int led_brightness_file_fd : led_brightness_files) {
|
||||||
|
close(led_brightness_file_fd);
|
||||||
|
}
|
||||||
|
|
||||||
run_gsr_global_hotkeys_set_leds(false);
|
run_gsr_global_hotkeys_set_leds(false);
|
||||||
if(gsr_global_hotkeys_pid > 0) {
|
if(gsr_global_hotkeys_pid > 0) {
|
||||||
int status;
|
int status;
|
||||||
@@ -58,7 +106,7 @@ namespace gsr {
|
|||||||
|
|
||||||
perror("gsr-global-hotkeys");
|
perror("gsr-global-hotkeys");
|
||||||
_exit(127);
|
_exit(127);
|
||||||
return true;
|
return false;
|
||||||
} else { // Parent
|
} else { // Parent
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -73,6 +121,11 @@ namespace gsr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void LedIndicator::update() {
|
void LedIndicator::update() {
|
||||||
|
update_led_with_active_status();
|
||||||
|
check_led_status_outdated();
|
||||||
|
}
|
||||||
|
|
||||||
|
void LedIndicator::update_led_with_active_status() {
|
||||||
if(perform_blink) {
|
if(perform_blink) {
|
||||||
const double blink_elapsed_sec = blink_timer.get_elapsed_time_seconds();
|
const double blink_elapsed_sec = blink_timer.get_elapsed_time_seconds();
|
||||||
if(blink_elapsed_sec < 0.2) {
|
if(blink_elapsed_sec < 0.2) {
|
||||||
@@ -90,4 +143,28 @@ namespace gsr {
|
|||||||
update_led(led_enabled);
|
update_led(led_enabled);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LedIndicator::check_led_status_outdated() {
|
||||||
|
// The display server will unset our scroll lock led when pressing capslock/numlock as it updates
|
||||||
|
// all leds at the same time (not just the button pressed) (or at least xorg server does that).
|
||||||
|
// When that is done we want to set the scroll lock led on again if it should be on.
|
||||||
|
// TODO: Improve this. Dont do this with a timer.. but inotify doesn't work sysfs. netlink should work (man 7 netlink).
|
||||||
|
if(read_led_brightness_timer.get_elapsed_time_seconds() > 0.2) {
|
||||||
|
read_led_brightness_timer.restart();
|
||||||
|
|
||||||
|
bool led_status_outdated = false;
|
||||||
|
char buffer[32];
|
||||||
|
for(int led_brightness_file_fd : led_brightness_files) {
|
||||||
|
const ssize_t bytes_read = read(led_brightness_file_fd, buffer, sizeof(buffer));
|
||||||
|
if(bytes_read > 0) {
|
||||||
|
if(buffer[0] == '0')
|
||||||
|
led_status_outdated = true;
|
||||||
|
lseek(led_brightness_file_fd, 0, SEEK_SET);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(led_status_outdated && led_enabled)
|
||||||
|
run_gsr_global_hotkeys_set_leds(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -518,7 +518,7 @@ namespace gsr {
|
|||||||
if(!config.main_config.wayland_warning_shown) {
|
if(!config.main_config.wayland_warning_shown) {
|
||||||
config.main_config.wayland_warning_shown = true;
|
config.main_config.wayland_warning_shown = true;
|
||||||
save_config(config);
|
save_config(config);
|
||||||
show_notification("Wayland doesn't support GPU Screen Recorder properly,\nthings may not work as expected. Use X11 if you experience issues.", notification_error_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), NotificationType::NONE, nullptr, NotificationLevel::ERROR);
|
show_notification("Wayland doesn't support GPU Screen Recorder UI properly,\nthings may not work as expected. Use X11 if you experience issues.", notification_error_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), NotificationType::NONE, nullptr, NotificationLevel::ERROR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2024,7 +2024,8 @@ namespace gsr {
|
|||||||
void Overlay::process_gsr_output() {
|
void Overlay::process_gsr_output() {
|
||||||
if(replay_save_show_notification && replay_save_clock.get_elapsed_time_seconds() >= replay_saving_notification_timeout_seconds) {
|
if(replay_save_show_notification && replay_save_clock.get_elapsed_time_seconds() >= replay_saving_notification_timeout_seconds) {
|
||||||
replay_save_show_notification = false;
|
replay_save_show_notification = false;
|
||||||
show_notification("Saving replay, this might take some time", notification_timeout_seconds, mgl::Color(255, 255, 255), get_color_theme().tint_color, NotificationType::REPLAY);
|
if(config.replay_config.record_options.show_notifications)
|
||||||
|
show_notification("Saving replay, this might take some time", notification_timeout_seconds, mgl::Color(255, 255, 255), get_color_theme().tint_color, NotificationType::REPLAY);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(gpu_screen_recorder_process_output_file) {
|
if(gpu_screen_recorder_process_output_file) {
|
||||||
@@ -3174,10 +3175,10 @@ namespace gsr {
|
|||||||
|
|
||||||
char size[64];
|
char size[64];
|
||||||
size[0] = '\0';
|
size[0] = '\0';
|
||||||
if(config.record_config.record_options.record_area_option == "focused")
|
if(config.streaming_config.record_options.record_area_option == "focused")
|
||||||
snprintf(size, sizeof(size), "%dx%d", (int)config.streaming_config.record_options.record_area_width, (int)config.streaming_config.record_options.record_area_height);
|
snprintf(size, sizeof(size), "%dx%d", (int)config.streaming_config.record_options.record_area_width, (int)config.streaming_config.record_options.record_area_height);
|
||||||
|
|
||||||
if(config.record_config.record_options.record_area_option != "focused" && config.streaming_config.record_options.change_video_resolution)
|
if(config.streaming_config.record_options.record_area_option != "focused" && config.streaming_config.record_options.change_video_resolution)
|
||||||
snprintf(size, sizeof(size), "%dx%d", (int)config.streaming_config.record_options.video_width, (int)config.streaming_config.record_options.video_height);
|
snprintf(size, sizeof(size), "%dx%d", (int)config.streaming_config.record_options.video_width, (int)config.streaming_config.record_options.video_height);
|
||||||
|
|
||||||
std::vector<const char*> args = {
|
std::vector<const char*> args = {
|
||||||
@@ -3187,6 +3188,7 @@ namespace gsr {
|
|||||||
"-cursor", config.streaming_config.record_options.record_cursor ? "yes" : "no",
|
"-cursor", config.streaming_config.record_options.record_cursor ? "yes" : "no",
|
||||||
"-cr", config.streaming_config.record_options.color_range.c_str(),
|
"-cr", config.streaming_config.record_options.color_range.c_str(),
|
||||||
"-fm", framerate_mode.c_str(),
|
"-fm", framerate_mode.c_str(),
|
||||||
|
"-k", video_codec,
|
||||||
"-encoder", encoder,
|
"-encoder", encoder,
|
||||||
"-f", fps.c_str(),
|
"-f", fps.c_str(),
|
||||||
"-v", "no",
|
"-v", "no",
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <X11/Xatom.h>
|
||||||
#include <X11/extensions/XInput2.h>
|
#include <X11/extensions/XInput2.h>
|
||||||
#include <X11/extensions/Xrandr.h>
|
#include <X11/extensions/Xrandr.h>
|
||||||
#include <X11/extensions/shape.h>
|
#include <X11/extensions/shape.h>
|
||||||
@@ -221,6 +222,9 @@ namespace gsr {
|
|||||||
}
|
}
|
||||||
set_window_size_not_resizable(dpy, region_window, XWidthOfScreen(screen), XHeightOfScreen(screen));
|
set_window_size_not_resizable(dpy, region_window, XWidthOfScreen(screen), XHeightOfScreen(screen));
|
||||||
|
|
||||||
|
unsigned char data = 2; // Prefer being composed to allow transparency. Do this to prevent the compositor from getting turned on/off when taking a screenshot
|
||||||
|
XChangeProperty(dpy, region_window, XInternAtom(dpy, "_NET_WM_BYPASS_COMPOSITOR", False), XA_CARDINAL, 32, PropModeReplace, &data, 1);
|
||||||
|
|
||||||
if(!is_wayland) {
|
if(!is_wayland) {
|
||||||
cursor_window = create_cursor_window(dpy, cursor_window_size, cursor_window_size, &vinfo, border_color_x11);
|
cursor_window = create_cursor_window(dpy, cursor_window_size, cursor_window_size, &vinfo, border_color_x11);
|
||||||
if(!cursor_window)
|
if(!cursor_window)
|
||||||
@@ -309,6 +313,9 @@ namespace gsr {
|
|||||||
region_window = 0;
|
region_window = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
XFlush(dpy);
|
||||||
|
XSync(dpy, False);
|
||||||
|
|
||||||
XCloseDisplay(dpy);
|
XCloseDisplay(dpy);
|
||||||
dpy = nullptr;
|
dpy = nullptr;
|
||||||
selecting_region = false;
|
selecting_region = false;
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <X11/Xatom.h>
|
||||||
#include <X11/extensions/shape.h>
|
#include <X11/extensions/shape.h>
|
||||||
#include <X11/cursorfont.h>
|
#include <X11/cursorfont.h>
|
||||||
#include <X11/keysym.h>
|
#include <X11/keysym.h>
|
||||||
@@ -122,6 +123,10 @@ namespace gsr {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
set_window_size_not_resizable(dpy, border_window, XWidthOfScreen(screen), XHeightOfScreen(screen));
|
set_window_size_not_resizable(dpy, border_window, XWidthOfScreen(screen), XHeightOfScreen(screen));
|
||||||
|
|
||||||
|
unsigned char data = 2; // Prefer being composed to allow transparency. Do this to prevent the compositor from getting turned on/off when taking a screenshot
|
||||||
|
XChangeProperty(dpy, border_window, XInternAtom(dpy, "_NET_WM_BYPASS_COMPOSITOR", False), XA_CARDINAL, 32, PropModeReplace, &data, 1);
|
||||||
|
|
||||||
if(cursor_window && cursor_window != DefaultRootWindow(dpy))
|
if(cursor_window && cursor_window != DefaultRootWindow(dpy))
|
||||||
set_region_rectangle(dpy, border_window, cursor_window_pos.x, cursor_window_pos.y, cursor_window_size.x, cursor_window_size.y, rectangle_border_size);
|
set_region_rectangle(dpy, border_window, cursor_window_pos.x, cursor_window_pos.y, cursor_window_size.x, cursor_window_size.y, rectangle_border_size);
|
||||||
else
|
else
|
||||||
@@ -163,6 +168,8 @@ namespace gsr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
XFlush(dpy);
|
XFlush(dpy);
|
||||||
|
XSync(dpy, False);
|
||||||
|
|
||||||
XCloseDisplay(dpy);
|
XCloseDisplay(dpy);
|
||||||
dpy = nullptr;
|
dpy = nullptr;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -202,7 +202,7 @@ namespace gsr {
|
|||||||
|
|
||||||
std::unique_ptr<Widget> ScreenshotSettingsPage::create_file_info_section() {
|
std::unique_ptr<Widget> ScreenshotSettingsPage::create_file_info_section() {
|
||||||
auto file_info_data_list = std::make_unique<List>(List::Orientation::HORIZONTAL);
|
auto file_info_data_list = std::make_unique<List>(List::Orientation::HORIZONTAL);
|
||||||
file_info_data_list->add_widget(create_save_directory("Directory to save the screenshot:"));
|
file_info_data_list->add_widget(create_save_directory("Directory to save screenshots:"));
|
||||||
file_info_data_list->add_widget(create_image_format_section());
|
file_info_data_list->add_widget(create_image_format_section());
|
||||||
return std::make_unique<Subsection>("File info", std::move(file_info_data_list), mgl::vec2f(settings_scrollable_page_ptr->get_inner_size().x, 0.0f));
|
return std::make_unique<Subsection>("File info", std::move(file_info_data_list), mgl::vec2f(settings_scrollable_page_ptr->get_inner_size().x, 0.0f));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -918,7 +918,7 @@ namespace gsr {
|
|||||||
void SettingsPage::add_record_widgets() {
|
void SettingsPage::add_record_widgets() {
|
||||||
auto file_info_list = std::make_unique<List>(List::Orientation::VERTICAL);
|
auto file_info_list = std::make_unique<List>(List::Orientation::VERTICAL);
|
||||||
auto file_info_data_list = std::make_unique<List>(List::Orientation::HORIZONTAL);
|
auto file_info_data_list = std::make_unique<List>(List::Orientation::HORIZONTAL);
|
||||||
file_info_data_list->add_widget(create_save_directory("Directory to save the video:"));
|
file_info_data_list->add_widget(create_save_directory("Directory to save videos:"));
|
||||||
file_info_data_list->add_widget(create_container_section());
|
file_info_data_list->add_widget(create_container_section());
|
||||||
file_info_list->add_widget(std::move(file_info_data_list));
|
file_info_list->add_widget(std::move(file_info_data_list));
|
||||||
file_info_list->add_widget(create_estimated_record_file_size());
|
file_info_list->add_widget(create_estimated_record_file_size());
|
||||||
|
|||||||
Reference in New Issue
Block a user