mirror of
https://repo.dec05eba.com/gpu-screen-recorder-ui
synced 2026-03-31 09:17:04 +09:00
Make the ui the daemon instead, add hotkey for recording/pause
This commit is contained in:
4
TODO
4
TODO
@@ -52,4 +52,6 @@ Have different modes. Overlay, window and side menu. Overlay can be used on x11,
|
|||||||
|
|
||||||
Look at /home/dec05eba/git/global-hotkeys for hotkeys.
|
Look at /home/dec05eba/git/global-hotkeys for hotkeys.
|
||||||
|
|
||||||
Show navigation breadcrumbs for settings and deeper navigation (such as selecting a directory to save videos).
|
Show navigation breadcrumbs for settings and deeper navigation (such as selecting a directory to save videos).
|
||||||
|
|
||||||
|
If recording stops because of an error then the ui wont be running and we wont get a notification that recording failed.
|
||||||
@@ -1,116 +0,0 @@
|
|||||||
#include <stdio.h>
|
|
||||||
#include <signal.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <sys/wait.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include <X11/Xlib.h>
|
|
||||||
#include <X11/keysym.h>
|
|
||||||
|
|
||||||
bool exec_program(const char **args, pid_t *process_id) {
|
|
||||||
*process_id = -1;
|
|
||||||
|
|
||||||
/* 1 argument */
|
|
||||||
if(args[0] == NULL)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
pid_t pid = vfork();
|
|
||||||
if(pid == -1) {
|
|
||||||
perror("Failed to vfork");
|
|
||||||
return false;
|
|
||||||
} else if(pid == 0) { /* child */
|
|
||||||
execvp(args[0], (char* const*)args);
|
|
||||||
perror("execvp");
|
|
||||||
_exit(127);
|
|
||||||
} else { /* parent */
|
|
||||||
*process_id = pid;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int ignore_xerror(Display *display, XErrorEvent *ee) {
|
|
||||||
(void)display;
|
|
||||||
(void)ee;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void sigterm_handler(int dummy) {
|
|
||||||
(void)dummy;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const KeySym toggle_overlay_key = XK_Z;
|
|
||||||
static unsigned int toggle_overlay_modifiers = Mod1Mask;
|
|
||||||
|
|
||||||
static void grab_keys(Display *display) {
|
|
||||||
unsigned int numlockmask = 0;
|
|
||||||
KeyCode numlock_keycode = XKeysymToKeycode(display, XK_Num_Lock);
|
|
||||||
XModifierKeymap *modmap = XGetModifierMapping(display);
|
|
||||||
for(int i = 0; i < 8; ++i) {
|
|
||||||
for(int j = 0; j < modmap->max_keypermod; ++j) {
|
|
||||||
if(modmap->modifiermap[i * modmap->max_keypermod + j] == numlock_keycode)
|
|
||||||
numlockmask = (1 << i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
XFreeModifiermap(modmap);
|
|
||||||
|
|
||||||
XErrorHandler prev_error_handler = XSetErrorHandler(ignore_xerror);
|
|
||||||
|
|
||||||
Window root_window = DefaultRootWindow(display);
|
|
||||||
unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask };
|
|
||||||
for(int i = 0; i < 4; ++i) {
|
|
||||||
XGrabKey(display, XKeysymToKeycode(display, toggle_overlay_key), toggle_overlay_modifiers|modifiers[i], root_window, False, GrabModeAsync, GrabModeAsync);
|
|
||||||
}
|
|
||||||
|
|
||||||
XSync(display, False);
|
|
||||||
XSetErrorHandler(prev_error_handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(void) {
|
|
||||||
Display *display = XOpenDisplay(NULL);
|
|
||||||
if(!display) {
|
|
||||||
fprintf(stderr, "Error: XOpenDisplay failed\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
grab_keys(display);
|
|
||||||
const KeyCode overlay_keycode = XKeysymToKeycode(display, toggle_overlay_key);
|
|
||||||
|
|
||||||
XSetErrorHandler(ignore_xerror);
|
|
||||||
/* Killing gpu-screen-recorder with SIGTERM also gives us SIGTERM. We want to ignore that as that has no meaning here */
|
|
||||||
signal(SIGTERM, sigterm_handler);
|
|
||||||
|
|
||||||
pid_t overlay_pid = -1;
|
|
||||||
|
|
||||||
fprintf(stderr, "gsr ui is now ready, waiting for inputs. Press alt+z to show/hide the ui\n");
|
|
||||||
|
|
||||||
XEvent xev;
|
|
||||||
for(;;) {
|
|
||||||
XNextEvent(display, &xev);
|
|
||||||
if(xev.type == KeyPress && xev.xkey.keycode == overlay_keycode) {
|
|
||||||
if(overlay_pid != -1) {
|
|
||||||
int status;
|
|
||||||
if(waitpid(overlay_pid, &status, WNOHANG) == 0) {
|
|
||||||
kill(overlay_pid, SIGINT);
|
|
||||||
int status;
|
|
||||||
if(waitpid(overlay_pid, &status, 0) == -1) {
|
|
||||||
perror("waitpid failed");
|
|
||||||
/* Ignore... */
|
|
||||||
}
|
|
||||||
overlay_pid = -1;
|
|
||||||
continue;
|
|
||||||
} else {
|
|
||||||
overlay_pid = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(overlay_pid == -1) {
|
|
||||||
fprintf(stderr, "launch ui\n");
|
|
||||||
// TODO: window_with_input_focus
|
|
||||||
const char *args[] = { "gsr-ui", NULL };
|
|
||||||
exec_program(args, &overlay_pid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "gpu-screen-recorder-overlay-daemon"
|
|
||||||
type = "executable"
|
|
||||||
version = "0.1.0"
|
|
||||||
platforms = ["posix"]
|
|
||||||
|
|
||||||
[config]
|
|
||||||
ignore_dirs = ["build"]
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
x11 = ">=1"
|
|
||||||
BIN
images/pause.png
Normal file
BIN
images/pause.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 392 B |
@@ -23,6 +23,13 @@ namespace gsr {
|
|||||||
STREAM
|
STREAM
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class NotificationType {
|
||||||
|
NONE,
|
||||||
|
RECORD,
|
||||||
|
REPLAY,
|
||||||
|
STREAM
|
||||||
|
};
|
||||||
|
|
||||||
class Overlay {
|
class Overlay {
|
||||||
public:
|
public:
|
||||||
Overlay(mgl::Window &window, std::string resources_path, GsrInfo gsr_info, egl_functions egl_funcs, mgl::Color bg_color);
|
Overlay(mgl::Window &window, std::string resources_path, GsrInfo gsr_info, egl_functions egl_funcs, mgl::Color bg_color);
|
||||||
@@ -36,17 +43,17 @@ namespace gsr {
|
|||||||
void show();
|
void show();
|
||||||
void hide();
|
void hide();
|
||||||
void toggle_show();
|
void toggle_show();
|
||||||
|
void toggle_record();
|
||||||
|
void toggle_pause();
|
||||||
|
void show_notification(const char *str, double timeout_seconds, mgl::Color icon_color, mgl::Color bg_color, NotificationType notification_type);
|
||||||
bool is_open() const;
|
bool is_open() const;
|
||||||
private:
|
private:
|
||||||
void process_key_bindings(mgl::Event &event);
|
void process_key_bindings(mgl::Event &event);
|
||||||
|
|
||||||
void update_gsr_process_status();
|
void update_gsr_process_status();
|
||||||
|
|
||||||
void load_program_status();
|
void update_ui_recording_paused();
|
||||||
void save_program_status();
|
void update_ui_recording_unpaused();
|
||||||
void load_program_pid();
|
|
||||||
void save_program_pid();
|
|
||||||
void recording_stopped_remove_runtime_files();
|
|
||||||
|
|
||||||
void update_ui_recording_started();
|
void update_ui_recording_started();
|
||||||
void update_ui_recording_stopped();
|
void update_ui_recording_stopped();
|
||||||
@@ -89,6 +96,7 @@ namespace gsr {
|
|||||||
DropdownButton *stream_dropdown_button_ptr = nullptr;
|
DropdownButton *stream_dropdown_button_ptr = nullptr;
|
||||||
|
|
||||||
RecordingStatus recording_status = RecordingStatus::NONE;
|
RecordingStatus recording_status = RecordingStatus::NONE;
|
||||||
|
bool paused = false;
|
||||||
|
|
||||||
std::array<KeyBinding, 1> key_bindings;
|
std::array<KeyBinding, 1> key_bindings;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ namespace gsr {
|
|||||||
mgl::Texture checkbox_background_texture;
|
mgl::Texture checkbox_background_texture;
|
||||||
mgl::Texture play_texture;
|
mgl::Texture play_texture;
|
||||||
mgl::Texture stop_texture;
|
mgl::Texture stop_texture;
|
||||||
|
mgl::Texture pause_texture;
|
||||||
|
|
||||||
double double_click_timeout_seconds = 0.4;
|
double double_click_timeout_seconds = 0.4;
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,4 @@ namespace gsr {
|
|||||||
// Returns the path to the parent directory (ignoring trailing /)
|
// Returns the path to the parent directory (ignoring trailing /)
|
||||||
// of "." if there is no parent directory and the directory path is relative
|
// of "." if there is no parent directory and the directory path is relative
|
||||||
std::string get_parent_directory(std::string_view directory);
|
std::string get_parent_directory(std::string_view directory);
|
||||||
|
|
||||||
std::optional<std::string> get_gsr_runtime_dir();
|
|
||||||
}
|
}
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
namespace gsr {
|
namespace gsr {
|
||||||
class DropdownButton : public Widget {
|
class DropdownButton : public Widget {
|
||||||
public:
|
public:
|
||||||
DropdownButton(mgl::Font *title_font, mgl::Font *description_font, const char *title, const char *description_activated, const char *description_deactivated, mgl::Texture *icon_texture, mgl::vec2f size);
|
DropdownButton(mgl::Font *title_font, mgl::Font *description_font, const char *title, const char *description, mgl::Texture *icon_texture, mgl::vec2f size);
|
||||||
DropdownButton(const DropdownButton&) = delete;
|
DropdownButton(const DropdownButton&) = delete;
|
||||||
DropdownButton& operator=(const DropdownButton&) = delete;
|
DropdownButton& operator=(const DropdownButton&) = delete;
|
||||||
|
|
||||||
@@ -21,6 +21,7 @@ namespace gsr {
|
|||||||
void set_item_label(const std::string &id, const std::string &new_label);
|
void set_item_label(const std::string &id, const std::string &new_label);
|
||||||
void set_item_icon(const std::string &id, mgl::Texture *texture);
|
void set_item_icon(const std::string &id, mgl::Texture *texture);
|
||||||
|
|
||||||
|
void set_description(std::string description_text);
|
||||||
void set_activated(bool activated);
|
void set_activated(bool activated);
|
||||||
|
|
||||||
mgl::vec2f get_size() override;
|
mgl::vec2f get_size() override;
|
||||||
@@ -50,8 +51,6 @@ namespace gsr {
|
|||||||
mgl::Text description;
|
mgl::Text description;
|
||||||
mgl::Sprite icon_sprite;
|
mgl::Sprite icon_sprite;
|
||||||
|
|
||||||
std::string description_activated;
|
|
||||||
std::string description_deactivated;
|
|
||||||
bool activated = false;
|
bool activated = false;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -54,6 +54,7 @@ namespace gsr {
|
|||||||
|
|
||||||
bool on_event(mgl::Event &event, mgl::Window &window, mgl::vec2f offset) override;
|
bool on_event(mgl::Event &event, mgl::Window &window, mgl::vec2f offset) override;
|
||||||
void draw(mgl::Window &window, mgl::vec2f offset) override;
|
void draw(mgl::Window &window, mgl::vec2f offset) override;
|
||||||
|
void draw_navigation(mgl::Window &window, mgl::vec2f draw_pos);
|
||||||
|
|
||||||
mgl::vec2f get_size() override;
|
mgl::vec2f get_size() override;
|
||||||
|
|
||||||
|
|||||||
@@ -57,12 +57,5 @@ executable(
|
|||||||
cpp_args : '-DGSR_UI_RESOURCES_PATH="' + gsr_ui_resources_path + '"',
|
cpp_args : '-DGSR_UI_RESOURCES_PATH="' + gsr_ui_resources_path + '"',
|
||||||
)
|
)
|
||||||
|
|
||||||
executable(
|
|
||||||
'gsr-ui-daemon',
|
|
||||||
['gsr-ui-daemon/main.c'],
|
|
||||||
install : true,
|
|
||||||
dependencies : [dependency('x11')],
|
|
||||||
)
|
|
||||||
|
|
||||||
install_subdir('images', install_dir : gsr_ui_resources_path)
|
install_subdir('images', install_dir : gsr_ui_resources_path)
|
||||||
install_subdir('fonts', install_dir : gsr_ui_resources_path)
|
install_subdir('fonts', install_dir : gsr_ui_resources_path)
|
||||||
283
src/Overlay.cpp
283
src/Overlay.cpp
@@ -197,32 +197,32 @@ namespace gsr {
|
|||||||
close_button_widget({0.0f, 0.0f})
|
close_button_widget({0.0f, 0.0f})
|
||||||
{
|
{
|
||||||
memset(&window_texture, 0, sizeof(window_texture));
|
memset(&window_texture, 0, sizeof(window_texture));
|
||||||
load_program_status();
|
|
||||||
load_program_pid();
|
|
||||||
|
|
||||||
key_bindings[0].key_event.code = mgl::Keyboard::Z;
|
key_bindings[0].key_event.code = mgl::Keyboard::Escape;
|
||||||
key_bindings[0].key_event.alt = true;
|
key_bindings[0].key_event.alt = false;
|
||||||
key_bindings[0].key_event.control = false;
|
key_bindings[0].key_event.control = false;
|
||||||
key_bindings[0].key_event.shift = false;
|
key_bindings[0].key_event.shift = false;
|
||||||
key_bindings[0].key_event.system = false;
|
key_bindings[0].key_event.system = false;
|
||||||
key_bindings[0].callback = [this]() {
|
key_bindings[0].callback = [this]() {
|
||||||
while(!page_stack.empty()) {
|
page_stack.pop();
|
||||||
page_stack.pop();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
Overlay::~Overlay() {
|
Overlay::~Overlay() {
|
||||||
hide();
|
hide();
|
||||||
// if(gpu_screen_recorder_process > 0) {
|
if(gpu_screen_recorder_process > 0) {
|
||||||
// kill(gpu_screen_recorder_process, SIGINT);
|
kill(gpu_screen_recorder_process, SIGINT);
|
||||||
// int status;
|
int status;
|
||||||
// if(waitpid(gpu_screen_recorder_process, &status, 0) == -1) {
|
if(waitpid(gpu_screen_recorder_process, &status, 0) == -1) {
|
||||||
// perror("waitpid failed");
|
perror("waitpid failed");
|
||||||
// /* Ignore... */
|
/* Ignore... */
|
||||||
// }
|
}
|
||||||
// gpu_screen_recorder_process = -1;
|
gpu_screen_recorder_process = -1;
|
||||||
// }
|
|
||||||
|
// TODO: Show this with a slight delay to make sure it doesn't show up in the video
|
||||||
|
if(config->record_config.show_video_saved_notifications)
|
||||||
|
show_notification("Recording has been saved", 3.0, mgl::Color(255, 255, 255), get_theme().tint_color, NotificationType::RECORD);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint32_t key_event_to_bitmask(mgl::Event::KeyEvent key_event) {
|
static uint32_t key_event_to_bitmask(mgl::Event::KeyEvent key_event) {
|
||||||
@@ -248,12 +248,10 @@ namespace gsr {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
close_button_widget.on_event(event, window, mgl::vec2f(0.0f, 0.0f));
|
close_button_widget.on_event(event, window, mgl::vec2f(0.0f, 0.0f));
|
||||||
page_stack.on_event(event, window, mgl::vec2f(0.0f, 0.0f));
|
if(!page_stack.on_event(event, window, mgl::vec2f(0.0f, 0.0f)))
|
||||||
if(event.type == mgl::Event::KeyReleased) {
|
return;
|
||||||
if(event.key.code == mgl::Keyboard::Escape)
|
|
||||||
page_stack.pop();
|
process_key_bindings(event);
|
||||||
}
|
|
||||||
//process_key_bindings(event);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Overlay::draw(mgl::Window &window) {
|
void Overlay::draw(mgl::Window &window) {
|
||||||
@@ -337,7 +335,7 @@ namespace gsr {
|
|||||||
auto main_buttons_list = std::make_unique<List>(List::Orientation::HORIZONTAL);
|
auto main_buttons_list = std::make_unique<List>(List::Orientation::HORIZONTAL);
|
||||||
main_buttons_list->set_spacing(0.0f);
|
main_buttons_list->set_spacing(0.0f);
|
||||||
{
|
{
|
||||||
auto button = std::make_unique<DropdownButton>(&get_theme().title_font, &get_theme().body_font, "Instant Replay", "On", "Off", &get_theme().replay_button_texture,
|
auto button = std::make_unique<DropdownButton>(&get_theme().title_font, &get_theme().body_font, "Instant Replay", "Off", &get_theme().replay_button_texture,
|
||||||
mgl::vec2f(button_width, button_height));
|
mgl::vec2f(button_width, button_height));
|
||||||
replay_dropdown_button_ptr = button.get();
|
replay_dropdown_button_ptr = button.get();
|
||||||
button->add_item("Turn on", "start", "Alt+Shift+F10");
|
button->add_item("Turn on", "start", "Alt+Shift+F10");
|
||||||
@@ -347,18 +345,19 @@ namespace gsr {
|
|||||||
main_buttons_list->add_widget(std::move(button));
|
main_buttons_list->add_widget(std::move(button));
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto button = std::make_unique<DropdownButton>(&get_theme().title_font, &get_theme().body_font, "Record", "Recording", "Not recording", &get_theme().record_button_texture,
|
auto button = std::make_unique<DropdownButton>(&get_theme().title_font, &get_theme().body_font, "Record", "Not recording", &get_theme().record_button_texture,
|
||||||
mgl::vec2f(button_width, button_height));
|
mgl::vec2f(button_width, button_height));
|
||||||
record_dropdown_button_ptr = button.get();
|
record_dropdown_button_ptr = button.get();
|
||||||
button->add_item("Start", "start", "Alt+F9");
|
button->add_item("Start", "start", "Alt+F9");
|
||||||
button->add_item("Pause", "pause", "Alt+F7");
|
button->add_item("Pause", "pause", "Alt+F7");
|
||||||
button->add_item("Settings", "settings");
|
button->add_item("Settings", "settings");
|
||||||
button->set_item_icon("start", &get_theme().play_texture);
|
button->set_item_icon("start", &get_theme().play_texture);
|
||||||
|
button->set_item_icon("pause", &get_theme().pause_texture);
|
||||||
button->on_click = std::bind(&Overlay::on_press_start_record, this, std::placeholders::_1);
|
button->on_click = std::bind(&Overlay::on_press_start_record, this, std::placeholders::_1);
|
||||||
main_buttons_list->add_widget(std::move(button));
|
main_buttons_list->add_widget(std::move(button));
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto button = std::make_unique<DropdownButton>(&get_theme().title_font, &get_theme().body_font, "Livestream", "Streaming", "Not streaming", &get_theme().stream_button_texture,
|
auto button = std::make_unique<DropdownButton>(&get_theme().title_font, &get_theme().body_font, "Livestream", "Not streaming", &get_theme().stream_button_texture,
|
||||||
mgl::vec2f(button_width, button_height));
|
mgl::vec2f(button_width, button_height));
|
||||||
stream_dropdown_button_ptr = button.get();
|
stream_dropdown_button_ptr = button.get();
|
||||||
button->add_item("Start", "start", "Alt+F8");
|
button->add_item("Start", "start", "Alt+F8");
|
||||||
@@ -439,6 +438,9 @@ namespace gsr {
|
|||||||
|
|
||||||
if(gpu_screen_recorder_process > 0 && recording_status == RecordingStatus::RECORD)
|
if(gpu_screen_recorder_process > 0 && recording_status == RecordingStatus::RECORD)
|
||||||
update_ui_recording_started();
|
update_ui_recording_started();
|
||||||
|
|
||||||
|
if(paused)
|
||||||
|
update_ui_recording_paused();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Overlay::hide() {
|
void Overlay::hide() {
|
||||||
@@ -466,6 +468,58 @@ namespace gsr {
|
|||||||
show();
|
show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Overlay::toggle_record() {
|
||||||
|
on_press_start_record("start");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Overlay::toggle_pause() {
|
||||||
|
if(recording_status != RecordingStatus::RECORD || gpu_screen_recorder_process <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if(paused) {
|
||||||
|
update_ui_recording_unpaused();
|
||||||
|
show_notification("Recording has been unpaused", 3.0, mgl::Color(255, 255, 255), get_theme().tint_color, NotificationType::RECORD);
|
||||||
|
} else {
|
||||||
|
update_ui_recording_paused();
|
||||||
|
show_notification("Recording has been paused", 3.0, mgl::Color(255, 255, 255), get_theme().tint_color, NotificationType::RECORD);
|
||||||
|
}
|
||||||
|
|
||||||
|
kill(gpu_screen_recorder_process, SIGUSR2);
|
||||||
|
paused = !paused;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char* notification_type_to_string(NotificationType notification_type) {
|
||||||
|
switch(notification_type) {
|
||||||
|
case NotificationType::NONE: return nullptr;
|
||||||
|
case NotificationType::RECORD: return "record";
|
||||||
|
case NotificationType::REPLAY: return "replay";
|
||||||
|
case NotificationType::STREAM: return "stream";
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Overlay::show_notification(const char *str, double timeout_seconds, mgl::Color icon_color, mgl::Color bg_color, NotificationType notification_type) {
|
||||||
|
char timeout_seconds_str[32];
|
||||||
|
snprintf(timeout_seconds_str, sizeof(timeout_seconds_str), "%f", timeout_seconds);
|
||||||
|
|
||||||
|
const std::string icon_color_str = color_to_hex_str(icon_color);
|
||||||
|
const std::string bg_color_str = color_to_hex_str(bg_color);
|
||||||
|
const char *notification_args[12] = {
|
||||||
|
"gsr-notify", "--text", str, "--timeout", timeout_seconds_str,
|
||||||
|
"--icon-color", icon_color_str.c_str(), "--bg-color", bg_color_str.c_str(),
|
||||||
|
};
|
||||||
|
|
||||||
|
const char *notification_type_str = notification_type_to_string(notification_type);
|
||||||
|
if(notification_type_str) {
|
||||||
|
notification_args[9] = "--icon";
|
||||||
|
notification_args[10] = "record",
|
||||||
|
notification_args[11] = nullptr;
|
||||||
|
} else {
|
||||||
|
notification_args[9] = nullptr;
|
||||||
|
}
|
||||||
|
exec_program_daemonized(notification_args);
|
||||||
|
}
|
||||||
|
|
||||||
bool Overlay::is_open() const {
|
bool Overlay::is_open() const {
|
||||||
return visible;
|
return visible;
|
||||||
}
|
}
|
||||||
@@ -499,153 +553,52 @@ namespace gsr {
|
|||||||
|
|
||||||
gpu_screen_recorder_process = -1;
|
gpu_screen_recorder_process = -1;
|
||||||
recording_status = RecordingStatus::NONE;
|
recording_status = RecordingStatus::NONE;
|
||||||
recording_stopped_remove_runtime_files();
|
|
||||||
update_ui_recording_stopped();
|
update_ui_recording_stopped();
|
||||||
|
|
||||||
if(exit_code == 0) {
|
if(exit_code == 0) {
|
||||||
if(config->record_config.show_video_saved_notifications) {
|
if(config->record_config.show_video_saved_notifications)
|
||||||
const std::string tint_color_as_hex = color_to_hex_str(get_theme().tint_color);
|
show_notification("Recording has been saved", 3.0, mgl::Color(255, 255, 255), get_theme().tint_color, NotificationType::RECORD);
|
||||||
const char *notification_args[] = {
|
|
||||||
"gsr-notify", "--text", "Recording has been saved", "--timeout", "3.0",
|
|
||||||
"--icon", "record",
|
|
||||||
"--icon-color", "ffffff", "--bg-color", tint_color_as_hex.c_str(),
|
|
||||||
nullptr
|
|
||||||
};
|
|
||||||
exec_program_daemonized(notification_args);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
fprintf(stderr, "Warning: gpu-screen-recorder (%d) exited with exit status %d\n", (int)gpu_screen_recorder_process, exit_code);
|
fprintf(stderr, "Warning: gpu-screen-recorder (%d) exited with exit status %d\n", (int)gpu_screen_recorder_process, exit_code);
|
||||||
const char *notification_args[] = {
|
show_notification("Failed to start/save recording", 3.0, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), NotificationType::RECORD);
|
||||||
"gsr-notify", "--text", "Failed to start/save recording", "--timeout", "3.0",
|
|
||||||
"--icon", "record",
|
|
||||||
"--icon-color", "ff0000", "--bg-color", "ff0000",
|
|
||||||
nullptr
|
|
||||||
};
|
|
||||||
exec_program_daemonized(notification_args);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static RecordingStatus recording_status_from_string(const char *status) {
|
void Overlay::update_ui_recording_paused() {
|
||||||
RecordingStatus recording_status = RecordingStatus::NONE;
|
if(!visible || recording_status != RecordingStatus::RECORD)
|
||||||
if(strcmp(status, "none") == 0)
|
|
||||||
recording_status = RecordingStatus::NONE;
|
|
||||||
else if(strcmp(status, "replay") == 0)
|
|
||||||
recording_status = RecordingStatus::REPLAY;
|
|
||||||
else if(strcmp(status, "record") == 0)
|
|
||||||
recording_status = RecordingStatus::RECORD;
|
|
||||||
else if(strcmp(status, "stream") == 0)
|
|
||||||
recording_status = RecordingStatus::STREAM;
|
|
||||||
return recording_status;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char* recording_status_to_string(RecordingStatus status) {
|
|
||||||
switch(status) {
|
|
||||||
case RecordingStatus::NONE: return "none";
|
|
||||||
case RecordingStatus::REPLAY: return "replay";
|
|
||||||
case RecordingStatus::RECORD: return "record";
|
|
||||||
case RecordingStatus::STREAM: return "stream";
|
|
||||||
}
|
|
||||||
return "none";
|
|
||||||
}
|
|
||||||
|
|
||||||
void Overlay::load_program_status() {
|
|
||||||
recording_status = RecordingStatus::NONE;
|
|
||||||
|
|
||||||
std::optional<std::string> status_filepath = get_gsr_runtime_dir();
|
|
||||||
if(!status_filepath)
|
|
||||||
throw std::runtime_error("Failed to find/create runtime directory /run/user/.../gsr-ui or /tmp/gsr-ui");
|
|
||||||
|
|
||||||
status_filepath.value() += "/status";
|
|
||||||
|
|
||||||
std::string file_content;
|
|
||||||
if(!file_get_content(status_filepath.value().c_str(), file_content))
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
recording_status = recording_status_from_string(file_content.c_str());
|
record_dropdown_button_ptr->set_description("Paused");
|
||||||
|
record_dropdown_button_ptr->set_item_label("pause", "Unpause");
|
||||||
|
record_dropdown_button_ptr->set_item_icon("pause", &get_theme().play_texture);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Overlay::save_program_status() {
|
void Overlay::update_ui_recording_unpaused() {
|
||||||
std::optional<std::string> status_filepath = get_gsr_runtime_dir();
|
if(!visible || recording_status != RecordingStatus::RECORD)
|
||||||
if(!status_filepath)
|
|
||||||
throw std::runtime_error("Failed to find/create runtime directory /run/user/.../gsr-ui or /tmp/gsr-ui");
|
|
||||||
|
|
||||||
status_filepath.value() += "/status";
|
|
||||||
if(!file_overwrite(status_filepath.value().c_str(), recording_status_to_string(recording_status)))
|
|
||||||
fprintf(stderr, "Error: failed to update status to file %s\n", status_filepath.value().c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
void Overlay::load_program_pid() {
|
|
||||||
gpu_screen_recorder_process = -1;
|
|
||||||
|
|
||||||
std::optional<std::string> status_filepath = get_gsr_runtime_dir();
|
|
||||||
if(!status_filepath)
|
|
||||||
throw std::runtime_error("Failed to find/create runtime directory /run/user/.../gsr-ui or /tmp/gsr-ui");
|
|
||||||
|
|
||||||
status_filepath.value() += "/pid";
|
|
||||||
|
|
||||||
std::string file_content;
|
|
||||||
if(!file_get_content(status_filepath.value().c_str(), file_content))
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
int pid = -1;
|
record_dropdown_button_ptr->set_description("Recording");
|
||||||
if(sscanf(file_content.c_str(), "%d", &pid) != 1) {
|
record_dropdown_button_ptr->set_item_label("pause", "Pause");
|
||||||
fprintf(stderr, "Error: failed to read pid from file %s, content: %s\n", status_filepath.value().c_str(), file_content.c_str());
|
record_dropdown_button_ptr->set_item_icon("pause", &get_theme().pause_texture);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
char cmdline_path[256];
|
|
||||||
snprintf(cmdline_path, sizeof(cmdline_path), "/proc/%d/cmdline", pid);
|
|
||||||
|
|
||||||
char program_arg0[PATH_MAX];
|
|
||||||
program_arg0[0] = '\0';
|
|
||||||
if(!read_cmdline_arg0(cmdline_path, program_arg0)) {
|
|
||||||
fprintf(stderr, "Error: failed to parse arg0 from file %s. Was the gpu-screen-recorder process that was started by gsr-ui closed by another program or the user?\n", cmdline_path);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(strcmp(program_arg0, "gpu-screen-recorder") != 0) {
|
|
||||||
fprintf(stderr, "Warning: process %d exists but doesn't belong to gpu-screen-recorder (is instead %s). Was the gpu-screen-recorder process that was started by gsr-ui closed by another program or the user?\n", pid, program_arg0);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
gpu_screen_recorder_process = pid;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Overlay::save_program_pid() {
|
|
||||||
std::optional<std::string> status_filepath = get_gsr_runtime_dir();
|
|
||||||
if(!status_filepath)
|
|
||||||
throw std::runtime_error("Failed to find/create runtime directory /run/user/.../gsr-ui or /tmp/gsr-ui");
|
|
||||||
|
|
||||||
status_filepath.value() += "/pid";
|
|
||||||
|
|
||||||
char str[32];
|
|
||||||
snprintf(str, sizeof(str), "%d", (int)gpu_screen_recorder_process);
|
|
||||||
if(!file_overwrite(status_filepath.value().c_str(), str))
|
|
||||||
fprintf(stderr, "Error: failed to update pid to file %s\n", status_filepath.value().c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
void Overlay::recording_stopped_remove_runtime_files() {
|
|
||||||
std::optional<std::string> status_filepath = get_gsr_runtime_dir();
|
|
||||||
if(!status_filepath) {
|
|
||||||
fprintf(stderr, "Error: Failed to find/create runtime directory /run/user/.../gsr-ui or /tmp/gsr-ui");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string status_file = status_filepath.value() + "/status";
|
|
||||||
const std::string pid_file = status_filepath.value() + "/pid";
|
|
||||||
remove(status_file.c_str());
|
|
||||||
remove(pid_file.c_str());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Overlay::update_ui_recording_started() {
|
void Overlay::update_ui_recording_started() {
|
||||||
|
if(!visible)
|
||||||
|
return;
|
||||||
|
|
||||||
record_dropdown_button_ptr->set_item_label("start", "Stop and save");
|
record_dropdown_button_ptr->set_item_label("start", "Stop and save");
|
||||||
record_dropdown_button_ptr->set_activated(true);
|
record_dropdown_button_ptr->set_activated(true);
|
||||||
|
record_dropdown_button_ptr->set_description("Recording");
|
||||||
record_dropdown_button_ptr->set_item_icon("start", &get_theme().stop_texture);
|
record_dropdown_button_ptr->set_item_icon("start", &get_theme().stop_texture);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Overlay::update_ui_recording_stopped() {
|
void Overlay::update_ui_recording_stopped() {
|
||||||
|
if(!visible)
|
||||||
|
return;
|
||||||
|
|
||||||
record_dropdown_button_ptr->set_item_label("start", "Start");
|
record_dropdown_button_ptr->set_item_label("start", "Start");
|
||||||
record_dropdown_button_ptr->set_activated(false);
|
record_dropdown_button_ptr->set_activated(false);
|
||||||
|
record_dropdown_button_ptr->set_description("Not recording");
|
||||||
record_dropdown_button_ptr->set_item_icon("start", &get_theme().play_texture);
|
record_dropdown_button_ptr->set_item_icon("start", &get_theme().play_texture);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -700,23 +653,30 @@ namespace gsr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Overlay::on_press_start_record(const std::string &id) {
|
void Overlay::on_press_start_record(const std::string &id) {
|
||||||
|
audio_devices = get_audio_devices();
|
||||||
|
|
||||||
if(id == "settings") {
|
if(id == "settings") {
|
||||||
auto record_settings_page = std::make_unique<SettingsPage>(SettingsPage::Type::RECORD, gsr_info, audio_devices, config, &page_stack);
|
auto record_settings_page = std::make_unique<SettingsPage>(SettingsPage::Type::RECORD, gsr_info, audio_devices, config, &page_stack);
|
||||||
page_stack.push(std::move(record_settings_page));
|
page_stack.push(std::move(record_settings_page));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(id == "pause") {
|
||||||
|
toggle_pause();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if(id != "start")
|
if(id != "start")
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if(!config)
|
if(!config)
|
||||||
config = Config();
|
config = Config();
|
||||||
|
|
||||||
|
paused = false;
|
||||||
|
|
||||||
// window.close();
|
// window.close();
|
||||||
// usleep(1000 * 50); // 50 milliseconds
|
// usleep(1000 * 50); // 50 milliseconds
|
||||||
|
|
||||||
const std::string tint_color_as_hex = color_to_hex_str(get_theme().tint_color);
|
|
||||||
|
|
||||||
if(gpu_screen_recorder_process > 0) {
|
if(gpu_screen_recorder_process > 0) {
|
||||||
kill(gpu_screen_recorder_process, SIGINT);
|
kill(gpu_screen_recorder_process, SIGINT);
|
||||||
int status;
|
int status;
|
||||||
@@ -730,19 +690,11 @@ namespace gsr {
|
|||||||
//exit(0);
|
//exit(0);
|
||||||
gpu_screen_recorder_process = -1;
|
gpu_screen_recorder_process = -1;
|
||||||
recording_status = RecordingStatus::NONE;
|
recording_status = RecordingStatus::NONE;
|
||||||
recording_stopped_remove_runtime_files();
|
|
||||||
update_ui_recording_stopped();
|
update_ui_recording_stopped();
|
||||||
|
|
||||||
// TODO: Show this with a slight delay to make sure it doesn't show up in the video
|
// TODO: Show this with a slight delay to make sure it doesn't show up in the video
|
||||||
if(config->record_config.show_video_saved_notifications) {
|
if(config->record_config.show_video_saved_notifications)
|
||||||
const char *notification_args[] = {
|
show_notification("Recording has been saved", 3.0, mgl::Color(255, 255, 255), get_theme().tint_color, NotificationType::RECORD);
|
||||||
"gsr-notify", "--text", "Recording has been saved", "--timeout", "3.0",
|
|
||||||
"--icon", "record",
|
|
||||||
"--icon-color", "ffffff", "--bg-color", tint_color_as_hex.c_str(),
|
|
||||||
nullptr
|
|
||||||
};
|
|
||||||
exec_program_daemonized(notification_args);
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -799,8 +751,6 @@ namespace gsr {
|
|||||||
// TODO: Show notification failed to start
|
// TODO: Show notification failed to start
|
||||||
} else {
|
} else {
|
||||||
recording_status = RecordingStatus::RECORD;
|
recording_status = RecordingStatus::RECORD;
|
||||||
save_program_status();
|
|
||||||
save_program_pid();
|
|
||||||
update_ui_recording_started();
|
update_ui_recording_started();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -813,15 +763,8 @@ namespace gsr {
|
|||||||
// TODO: Do not run this is a daemon. Instead get the pid and when launching another notification close the current notification
|
// TODO: Do not run this is a daemon. Instead get the pid and when launching another notification close the current notification
|
||||||
// program and start another one. This can also be used to check when the notification has finished by checking with waitpid NOWAIT
|
// program and start another one. This can also be used to check when the notification has finished by checking with waitpid NOWAIT
|
||||||
// to see when the program has exit.
|
// to see when the program has exit.
|
||||||
if(config->record_config.show_recording_started_notifications) {
|
if(config->record_config.show_recording_started_notifications)
|
||||||
const char *notification_args[] = {
|
show_notification("Recording has started", 3.0, get_theme().tint_color, get_theme().tint_color, NotificationType::RECORD);
|
||||||
"gsr-notify", "--text", "Recording has started", "--timeout", "3.0",
|
|
||||||
"--icon", "record",
|
|
||||||
"--icon-color", tint_color_as_hex.c_str(), "--bg-color", tint_color_as_hex.c_str(),
|
|
||||||
nullptr
|
|
||||||
};
|
|
||||||
exec_program_daemonized(notification_args);
|
|
||||||
}
|
|
||||||
//exit(0);
|
//exit(0);
|
||||||
// window.set_visible(false);
|
// window.set_visible(false);
|
||||||
// window.close();
|
// window.close();
|
||||||
|
|||||||
@@ -94,6 +94,9 @@ namespace gsr {
|
|||||||
if(!theme->stop_texture.load_from_file((resources_path + "images/stop.png").c_str()))
|
if(!theme->stop_texture.load_from_file((resources_path + "images/stop.png").c_str()))
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
|
if(!theme->pause_texture.load_from_file((resources_path + "images/pause.png").c_str()))
|
||||||
|
goto error;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
|
|||||||
@@ -198,21 +198,4 @@ namespace gsr {
|
|||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<std::string> get_gsr_runtime_dir() {
|
|
||||||
std::optional<std::string> result;
|
|
||||||
char runtime_dir_path[256];
|
|
||||||
snprintf(runtime_dir_path, sizeof(runtime_dir_path), "/run/user/%u", (unsigned int)getuid());
|
|
||||||
|
|
||||||
struct stat st;
|
|
||||||
if(stat(runtime_dir_path, &st) == -1 || !S_ISDIR(st.st_mode))
|
|
||||||
snprintf(runtime_dir_path, sizeof(runtime_dir_path), "/tmp");
|
|
||||||
|
|
||||||
strcat(runtime_dir_path, "/gsr-ui");
|
|
||||||
if(create_directory_recursive(runtime_dir_path) != 0)
|
|
||||||
return result;
|
|
||||||
|
|
||||||
result = runtime_dir_path;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -15,9 +15,8 @@ namespace gsr {
|
|||||||
static const float icon_spacing_scale = 0.008f;
|
static const float icon_spacing_scale = 0.008f;
|
||||||
static const float border_scale = 0.003f;
|
static const float border_scale = 0.003f;
|
||||||
|
|
||||||
DropdownButton::DropdownButton(mgl::Font *title_font, mgl::Font *description_font, const char *title, const char *description_activated, const char *description_deactivated, mgl::Texture *icon_texture, mgl::vec2f size) :
|
DropdownButton::DropdownButton(mgl::Font *title_font, mgl::Font *description_font, const char *title, const char *description, mgl::Texture *icon_texture, mgl::vec2f size) :
|
||||||
title_font(title_font), description_font(description_font), size(size), title(title, *title_font), description(description_deactivated, *description_font),
|
title_font(title_font), description_font(description_font), size(size), title(title, *title_font), description(description, *description_font)
|
||||||
description_activated(description_activated), description_deactivated(description_deactivated)
|
|
||||||
{
|
{
|
||||||
if(icon_texture && icon_texture->is_valid()) {
|
if(icon_texture && icon_texture->is_valid()) {
|
||||||
icon_sprite.set_texture(icon_texture);
|
icon_sprite.set_texture(icon_texture);
|
||||||
@@ -202,6 +201,10 @@ namespace gsr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DropdownButton::set_description(std::string description_text) {
|
||||||
|
description.set_string(std::move(description_text));
|
||||||
|
}
|
||||||
|
|
||||||
void DropdownButton::set_activated(bool activated) {
|
void DropdownButton::set_activated(bool activated) {
|
||||||
if(this->activated == activated)
|
if(this->activated == activated)
|
||||||
return;
|
return;
|
||||||
@@ -209,11 +212,9 @@ namespace gsr {
|
|||||||
this->activated = activated;
|
this->activated = activated;
|
||||||
|
|
||||||
if(activated) {
|
if(activated) {
|
||||||
description = mgl::Text(description_activated, *description_font);
|
|
||||||
description.set_color(get_theme().tint_color);
|
description.set_color(get_theme().tint_color);
|
||||||
icon_sprite.set_color(get_theme().tint_color);
|
icon_sprite.set_color(get_theme().tint_color);
|
||||||
} else {
|
} else {
|
||||||
description = mgl::Text(description_deactivated, *description_font);
|
|
||||||
description.set_color(mgl::Color(150, 150, 150));
|
description.set_color(mgl::Color(150, 150, 150));
|
||||||
icon_sprite.set_color(mgl::Color(255, 255, 255));
|
icon_sprite.set_color(mgl::Color(255, 255, 255));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -223,25 +223,9 @@ namespace gsr {
|
|||||||
current_directory_padding_top_scale * get_theme().window_height + current_directory_padding_bottom_scale * get_theme().window_height
|
current_directory_padding_top_scale * get_theme().window_height + current_directory_padding_bottom_scale * get_theme().window_height
|
||||||
);
|
);
|
||||||
|
|
||||||
mgl::vec2f current_directory_background_size = mgl::vec2f(size.x, current_directory_text.get_bounds().size.y + current_directory_padding.y).floor();
|
const float current_directory_background_height = (int)(current_directory_text.get_bounds().size.y + current_directory_padding.y);
|
||||||
|
|
||||||
up_arrow_sprite.set_height((int)(current_directory_background_size.y * 0.8f));
|
draw_pos += mgl::vec2f(0.0f, current_directory_background_height + spacing_between_current_directory_and_content * get_theme().window_height);
|
||||||
up_arrow_sprite.set_position((draw_pos + mgl::vec2f(size.x - up_arrow_sprite.get_size().x, current_directory_background_size.y * 0.5f - up_arrow_sprite.get_size().y * 0.5f)).floor());
|
|
||||||
const bool mouse_inside_up_arrow = mgl::FloatRect(up_arrow_sprite.get_position(), up_arrow_sprite.get_size()).contains(window.get_mouse_position().to_vec2f()) && !has_parent_with_selected_child_widget();
|
|
||||||
up_arrow_sprite.set_color(mouse_inside_up_arrow ? get_theme().tint_color : mgl::Color(255, 255, 255));
|
|
||||||
window.draw(up_arrow_sprite);
|
|
||||||
|
|
||||||
current_directory_background_size.x -= (up_arrow_sprite.get_size().x + up_button_spacing_scale * get_theme().window_height);
|
|
||||||
mgl::Rectangle current_directory_background(current_directory_background_size.floor());
|
|
||||||
current_directory_background.set_color(mgl::Color(0, 0, 0, 120));
|
|
||||||
current_directory_background.set_position(draw_pos.floor());
|
|
||||||
window.draw(current_directory_background);
|
|
||||||
|
|
||||||
current_directory_text.set_color(get_theme().text_color);
|
|
||||||
current_directory_text.set_position((draw_pos + mgl::vec2f(current_directory_padding.x, current_directory_background_size.y * 0.5f - current_directory_text.get_bounds().size.y * 0.5f)).floor());
|
|
||||||
window.draw(current_directory_text);
|
|
||||||
|
|
||||||
draw_pos += mgl::vec2f(0.0f, current_directory_background_size.y + spacing_between_current_directory_and_content * get_theme().window_height);
|
|
||||||
const mgl::vec2f body_size = mgl::vec2f(size.x, size.y - (draw_pos.y - draw_pos_start.y)).floor();
|
const mgl::vec2f body_size = mgl::vec2f(size.x, size.y - (draw_pos.y - draw_pos_start.y)).floor();
|
||||||
scrollable_page.set_size(body_size);
|
scrollable_page.set_size(body_size);
|
||||||
file_chooser_body_ptr->set_size(scrollable_page.get_inner_size());
|
file_chooser_body_ptr->set_size(scrollable_page.get_inner_size());
|
||||||
@@ -251,9 +235,34 @@ namespace gsr {
|
|||||||
content_background.set_color(mgl::Color(0, 0, 0, 120));
|
content_background.set_color(mgl::Color(0, 0, 0, 120));
|
||||||
window.draw(content_background);
|
window.draw(content_background);
|
||||||
|
|
||||||
|
draw_navigation(window, draw_pos_start);
|
||||||
|
|
||||||
scrollable_page.draw(window, draw_pos.floor());
|
scrollable_page.draw(window, draw_pos.floor());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FileChooser::draw_navigation(mgl::Window &window, mgl::vec2f draw_pos) {
|
||||||
|
const mgl::vec2f current_directory_padding(
|
||||||
|
current_directory_padding_left_scale * get_theme().window_height + current_directory_padding_right_scale * get_theme().window_height,
|
||||||
|
current_directory_padding_top_scale * get_theme().window_height + current_directory_padding_bottom_scale * get_theme().window_height
|
||||||
|
);
|
||||||
|
mgl::vec2f current_directory_background_size = mgl::vec2f(size.x, current_directory_text.get_bounds().size.y + current_directory_padding.y).floor();
|
||||||
|
up_arrow_sprite.set_height((int)(current_directory_background_size.y * 0.8f));
|
||||||
|
up_arrow_sprite.set_position((draw_pos + mgl::vec2f(file_chooser_body_ptr->get_size().x - up_arrow_sprite.get_size().x, current_directory_background_size.y * 0.5f - up_arrow_sprite.get_size().y * 0.5f)).floor());
|
||||||
|
const bool mouse_inside_up_arrow = mgl::FloatRect(up_arrow_sprite.get_position(), up_arrow_sprite.get_size()).contains(window.get_mouse_position().to_vec2f()) && !has_parent_with_selected_child_widget();
|
||||||
|
up_arrow_sprite.set_color(mouse_inside_up_arrow ? get_theme().tint_color : mgl::Color(255, 255, 255));
|
||||||
|
window.draw(up_arrow_sprite);
|
||||||
|
|
||||||
|
current_directory_background_size.x = file_chooser_body_ptr->get_size().x - up_arrow_sprite.get_size().x - up_button_spacing_scale * get_theme().window_height;
|
||||||
|
mgl::Rectangle current_directory_background(current_directory_background_size.floor());
|
||||||
|
current_directory_background.set_color(mgl::Color(0, 0, 0, 120));
|
||||||
|
current_directory_background.set_position(draw_pos.floor());
|
||||||
|
window.draw(current_directory_background);
|
||||||
|
|
||||||
|
current_directory_text.set_color(get_theme().text_color);
|
||||||
|
current_directory_text.set_position((draw_pos + mgl::vec2f(current_directory_padding.x, current_directory_background_size.y * 0.5f - current_directory_text.get_bounds().size.y * 0.5f)).floor());
|
||||||
|
window.draw(current_directory_text);
|
||||||
|
}
|
||||||
|
|
||||||
mgl::vec2f FileChooser::get_size() {
|
mgl::vec2f FileChooser::get_size() {
|
||||||
if(!visible)
|
if(!visible)
|
||||||
return {0.0f, 0.0f};
|
return {0.0f, 0.0f};
|
||||||
|
|||||||
53
src/main.cpp
53
src/main.cpp
@@ -10,8 +10,7 @@
|
|||||||
|
|
||||||
#include <X11/Xlib.h>
|
#include <X11/Xlib.h>
|
||||||
#include <X11/Xatom.h>
|
#include <X11/Xatom.h>
|
||||||
#define XK_LATIN1
|
#include <X11/keysym.h>
|
||||||
#include <X11/keysymdef.h>
|
|
||||||
#include <mglpp/mglpp.hpp>
|
#include <mglpp/mglpp.hpp>
|
||||||
#include <mglpp/window/Window.hpp>
|
#include <mglpp/window/Window.hpp>
|
||||||
#include <mglpp/window/Event.hpp>
|
#include <mglpp/window/Event.hpp>
|
||||||
@@ -33,7 +32,7 @@ extern "C" {
|
|||||||
const mgl::Color bg_color(0, 0, 0, 100);
|
const mgl::Color bg_color(0, 0, 0, 100);
|
||||||
|
|
||||||
static void usage() {
|
static void usage() {
|
||||||
fprintf(stderr, "usage: window-overlay\n");
|
fprintf(stderr, "usage: gsr-ui [toggle-record|toggle-pause]\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,10 +48,15 @@ static void sigint_handler(int signal) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
(void)argv;
|
setlocale(LC_ALL, "C"); // Sigh... stupid C
|
||||||
if(argc != 1)
|
|
||||||
|
if(argc > 3)
|
||||||
usage();
|
usage();
|
||||||
|
|
||||||
|
const char *action = NULL;
|
||||||
|
if(argc > 1)
|
||||||
|
action = argv[1];
|
||||||
|
|
||||||
signal(SIGINT, sigint_handler);
|
signal(SIGINT, sigint_handler);
|
||||||
|
|
||||||
gsr::GsrInfo gsr_info;
|
gsr::GsrInfo gsr_info;
|
||||||
@@ -124,25 +128,44 @@ int main(int argc, char **argv) {
|
|||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, "info: gsr ui is now ready, waiting for inputs. Press alt+z to show/hide the overlay\n");
|
||||||
|
|
||||||
gsr::Overlay overlay(window, resources_path, gsr_info, egl_funcs, bg_color);
|
gsr::Overlay overlay(window, resources_path, gsr_info, egl_funcs, bg_color);
|
||||||
overlay.show();
|
//overlay.show();
|
||||||
|
|
||||||
// gsr::GlobalHotkeysX11 global_hotkeys;
|
gsr::GlobalHotkeysX11 global_hotkeys;
|
||||||
// global_hotkeys.bind_key_press({ XK_z, Mod1Mask }, "open/hide", [&](const std::string &id) {
|
const bool show_hotkey_registered = global_hotkeys.bind_key_press({ XK_z, Mod1Mask }, "open/hide", [&](const std::string &id) {
|
||||||
// fprintf(stderr, "pressed %s\n", id.c_str());
|
fprintf(stderr, "pressed %s\n", id.c_str());
|
||||||
// overlay.toggle_show();
|
overlay.toggle_show();
|
||||||
// });
|
});
|
||||||
|
|
||||||
//fprintf(stderr, "info: gsr ui is now ready, waiting for inputs. Press alt+z to show/hide the overlay\n");
|
const bool record_hotkey_registered = global_hotkeys.bind_key_press({ XK_F9, Mod1Mask }, "record/save", [&](const std::string &id) {
|
||||||
|
fprintf(stderr, "pressed %s\n", id.c_str());
|
||||||
|
overlay.toggle_record();
|
||||||
|
});
|
||||||
|
|
||||||
|
const bool pause_hotkey_registered = global_hotkeys.bind_key_press({ XK_F7, Mod1Mask }, "pause/unpause", [&](const std::string &id) {
|
||||||
|
fprintf(stderr, "pressed %s\n", id.c_str());
|
||||||
|
overlay.toggle_pause();
|
||||||
|
});
|
||||||
|
|
||||||
|
if(!show_hotkey_registered)
|
||||||
|
fprintf(stderr, "error: failed to register hotkey alt+z for showing the overlay because the hotkey is registed by another program\n");
|
||||||
|
|
||||||
|
if(!record_hotkey_registered)
|
||||||
|
fprintf(stderr, "error: failed to register hotkey alt+f9 for recording because the hotkey is registed by another program\n");
|
||||||
|
|
||||||
|
if(!pause_hotkey_registered)
|
||||||
|
fprintf(stderr, "error: failed to register hotkey alt+f7 for pausing because the hotkey is registed by another program\n");
|
||||||
|
|
||||||
mgl::Event event;
|
mgl::Event event;
|
||||||
mgl::Clock frame_delta_clock;
|
mgl::Clock frame_delta_clock;
|
||||||
while(window.is_open() && overlay.is_open() && running) {
|
while(window.is_open() && running) {
|
||||||
const double frame_delta_seconds = frame_delta_clock.get_elapsed_time_seconds();
|
const double frame_delta_seconds = frame_delta_clock.get_elapsed_time_seconds();
|
||||||
frame_delta_clock.restart();
|
frame_delta_clock.restart();
|
||||||
gsr::set_frame_delta_seconds(frame_delta_seconds);
|
gsr::set_frame_delta_seconds(frame_delta_seconds);
|
||||||
|
|
||||||
//global_hotkeys.poll_events();
|
global_hotkeys.poll_events();
|
||||||
while(window.poll_event(event)) {
|
while(window.poll_event(event)) {
|
||||||
overlay.on_event(event, window);
|
overlay.on_event(event, window);
|
||||||
}
|
}
|
||||||
@@ -152,7 +175,7 @@ int main(int argc, char **argv) {
|
|||||||
window.display();
|
window.display();
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf(stderr, "shutting down!\n");
|
fprintf(stderr, "info: shutting down!\n");
|
||||||
gsr::deinit_theme();
|
gsr::deinit_theme();
|
||||||
window.close();
|
window.close();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user