mirror of
https://repo.dec05eba.com/gpu-screen-recorder-ui
synced 2026-04-23 02:05:02 +09:00
Compare commits
50 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eb6aed9cfd | ||
|
|
c815cf08fb | ||
|
|
15544699dc | ||
|
|
b58c141abd | ||
|
|
e5c9c490f4 | ||
|
|
1152c0edb0 | ||
|
|
cf7df867ab | ||
|
|
163cd3f5a1 | ||
|
|
adb4ff9ec7 | ||
|
|
9a62e2bb22 | ||
|
|
527820bd40 | ||
|
|
247c8fb86c | ||
|
|
99fa6337f6 | ||
|
|
532c50ce6d | ||
|
|
0c6d887def | ||
|
|
606272c451 | ||
|
|
b8c1bbafd5 | ||
|
|
735e3a0b4f | ||
|
|
17e5d17415 | ||
|
|
1b87bf88d7 | ||
|
|
6bb45fabba | ||
|
|
d109cce5af | ||
|
|
8a49ea991b | ||
|
|
82453684b1 | ||
|
|
a1a0736af5 | ||
|
|
d01698eaba | ||
|
|
c60f670b65 | ||
|
|
010787854e | ||
|
|
72c8c79896 | ||
|
|
aec687bf22 | ||
|
|
d545de687c | ||
|
|
2cabdf7089 | ||
|
|
6a12efec50 | ||
|
|
388a3500e2 | ||
|
|
24806ecf31 | ||
|
|
33a1e9e3bd | ||
|
|
4fd05d613b | ||
|
|
b80e864bbb | ||
|
|
33bc121bc8 | ||
|
|
106e7febe5 | ||
|
|
3c6e72350e | ||
|
|
43c16a7865 | ||
|
|
264a838e1f | ||
|
|
b927cb7f21 | ||
|
|
8e35de9e8b | ||
|
|
13984f8636 | ||
|
|
5f3ace0c47 | ||
|
|
651782a3a3 | ||
|
|
4e5a073854 | ||
|
|
1442016a18 |
18
README.md
18
README.md
@@ -10,10 +10,15 @@ If you are running another distro then you can run `sudo ./install.sh`, but you
|
||||
You can also install gpu screen recorder from [flathub](https://flathub.org/apps/details/com.dec05eba.gpu_screen_recorder) which includes this UI.
|
||||
|
||||
# Usage
|
||||
Press `Left Alt+Z` to show/hide the UI. Go into settings (the icon on the right) to view all of the different hotkeys configured.\
|
||||
You can start the overlay UI and make it start automatically on system startup by running `systemctl enable --now --user gpu-screen-recorder-ui`.
|
||||
Alternatively you can run `gsr-ui` and go into settings and enable start on system startup setting.\
|
||||
If you use a non-systemd distro and want to start the UI on system startup then you have to manually add `gsr-ui launch-daemon` to your system startup script.\
|
||||
Start the program by running `gsr-ui` or clicking on `GPU Screen Recorder` on your desktop or in your application launcher.
|
||||
Press `Left Alt+Z` to show/hide the UI. Go into the settings (the icon on the right) to view all of the different hotkeys configured.
|
||||
|
||||
If you want the program to start on system startup and have it running in the background where it can be controlled with the hotkeys at any time
|
||||
then open the UI and go into the settings (the icon on the right) and enable "Start program on system startup?".
|
||||
The application will be added to `~/.config/autostart/gpu-screen-recorder-ui.desktop`. This will be launched automatically when you use a desktop environment,
|
||||
but if you use a minimal window manager then you need to use a XDG autostart program such as `dex` (for example run `dex -a -s ~/.config/autostart` at startup),
|
||||
or launch the program manually from your window manager startup script.
|
||||
|
||||
A program called `gsr-ui-cli` is also installed when installing this software. This can be used to remotely control the UI. Run `gsr-ui-cli --help` to list the available commands.
|
||||
|
||||
# Dependencies
|
||||
@@ -29,6 +34,7 @@ These are the dependencies needed to build GPU Screen Recorder UI:
|
||||
* libdrm
|
||||
* libdbus
|
||||
* wayland (wayland-client, wayland-egl, wayland-scanner)
|
||||
* libpango (pangoft2)
|
||||
* setcap (libcap)
|
||||
|
||||
## Runtime dependencies
|
||||
@@ -50,7 +56,7 @@ as gpu screen recorder tries to grab keys and keyd grabs gpu screen recorder, le
|
||||
If you are stuck in such a lock where you cant press and keyboard keys you can press (left) ctrl+shift+alt+esc to close gpu screen recorder and remove it from system startup.
|
||||
|
||||
# License
|
||||
This software is licensed under GPL-3.0-only, see the LICENSE file for more information. Files under `fonts/` directory belong to the Noto Sans Google fonts project and they are licensed under `SIL Open Font License`.\
|
||||
This software is licensed under GPL-3.0-only, see the LICENSE file for more information.
|
||||
`images/default.cur` it part of the [Adwaita icon theme](https://gitlab.gnome.org/GNOME/adwaita-icon-theme/-/tree/master) which is licensed under `CC BY-SA 3.0`.\
|
||||
The controller buttons under `images/` were created by [Julio Cacko](https://juliocacko.itch.io/free-input-prompts) and they are licensed under `CC0 1.0 Universal`.\
|
||||
The PlayStation logo under `images/` was created by [ArksDigital](https://arks.itch.io/ps4-buttons) and it's licensed under `CC BY 4.0`.
|
||||
@@ -73,8 +79,6 @@ I'm looking for somebody that can create sound effects for the notifications.
|
||||
* Unable to close the region selection with escape key while a Wayland application is focused on COSMIC. This is an issue with COSMIC.
|
||||
|
||||
# FAQ
|
||||
## I get an error when trying to start the gpu-screen-recorder-ui.service systemd service
|
||||
If you have previously used the flatpak version of GPU Screen Recorder with the new UI then the non-flatpak version of the systemd service will conflict with that. Run `gsr-ui` to fix that.
|
||||
## I use a non-qwerty keyboard layout and I have an issue with incorrect keys registered in the software
|
||||
This is a KDE Plasma Wayland issue. Use `setxkbmap <language>` command, for example `setxkbmap se` to make sure X11 applications (such as this one) gets updated to use your languages keyboard layout.
|
||||
## "Save to clipboard" option doesn't work for screenshots
|
||||
|
||||
21
TODO
21
TODO
@@ -62,8 +62,6 @@ Play camera shutter sound when saving recording. When another sound when startin
|
||||
|
||||
Some games such as "The Finals" crashes/freezes when they lose focus when running them on x11 on a laptop with prime setup and the monitor runs on the iGPU while the game runs on the dGPU.
|
||||
|
||||
Run `systemctl status --user gpu-screen-recorder` when starting recording and give a notification warning if it returns 0 (running). Or run pidof gpu-screen-recorder.
|
||||
|
||||
Add option to select which gpu to record with, or list all monitors and automatically use the gpu associated with the monitor. Do the same in gtk application.
|
||||
|
||||
Use global shortcuts desktop portal protocol on wayland when available.
|
||||
@@ -82,8 +80,6 @@ All steam game names by ID are available at https://api.steampowered.com/ISteamA
|
||||
|
||||
Dont put widget position to int position when scrolling. This makes the UI jitter when it's coming to a halt.
|
||||
|
||||
Show warning if another instance of gpu screen recorder is already running when starting recording?
|
||||
|
||||
Make gsr-ui flatpak systemd work nicely with non-flatpak gsr-ui. Maybe change ExecStart to do flatpak run ... || gsr-ui, but make it run as a shell command first with /bin/sh -c "".
|
||||
|
||||
When enabling X11 global hotkey again only grab lalt, not ralt.
|
||||
@@ -133,8 +129,6 @@ If CursorTrackerWayland fails then fallback to getting focused monitor by window
|
||||
Maybe automatically switch to recording with the device that controls the monitor.
|
||||
In that case also add all monitors available to capture in the capture list and automatically choose the gpu that controls the monitor.
|
||||
|
||||
Support cjk font. Use fc-match to find the location of the font. This also works in flatpak, in which case the fonts are in /run/host/..., where it lists system fonts.
|
||||
|
||||
Keyboard layout is incorrect on wayland when using kde plasma keyboard settings to setup multiple keyboards, for example when changing to french.
|
||||
Text input is correct, but hotkey is incorrect.
|
||||
Need to use "setxkbmap fr" as well.
|
||||
@@ -168,8 +162,6 @@ Show message that replay/streaming has to be restarted if recording settings are
|
||||
|
||||
Support vector graphics. Maybe support svg, rendering it to a texture for better performance.
|
||||
|
||||
Support freetype for text rendering. Maybe load freetype as runtime (with dlopen) and use that when available and fallback to stb_freetype if not available.
|
||||
|
||||
Show .webm container option. It's currently chosen automatically if vp8/vp9 is chosen. The available containers should automatically switch depending on the video codec.
|
||||
|
||||
In settings show audio levels for each audio. Maybe show audio level image beside the audio name in the dropdown box and switch to a different image (have 3-4 different images for each level) depending on the volume.
|
||||
@@ -194,8 +186,6 @@ Disable hotkeys if virtual keyboard is found (either at startup or after running
|
||||
But wont keyboard remapping software grab the keyboard first if they detect it quickly?
|
||||
If we fail to grab it because some other software did then dont grab any keyboards nor gsr-ui virtual keyboards, just listen to them.
|
||||
|
||||
Support localization.
|
||||
|
||||
Add option to not capture cursor in screenshot when doing region/window capture.
|
||||
|
||||
Window selection doesn't work when a window is fullscreen on x11.
|
||||
@@ -242,8 +232,6 @@ Remove all mgl::Clock usage in Overlay. We only need to get the time once per up
|
||||
|
||||
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.
|
||||
|
||||
Sometimes when opening gpu screen recorder ui gsr-global-hotkeys incorrectly detects that keyboard input is locked.
|
||||
|
||||
When running replay for a long time and then stopping it it takes a while. Improve this.
|
||||
@@ -263,3 +251,12 @@ Add option to set preset on nvidia. Use -ffmpeg-video-opts for that.
|
||||
Webcam resolution list is too long for some people. Fix it by separating resolution and framerate. Sort resolution and framerate from highest to lowest. Add scrollbar for dropdown list. POOP. Add --filesystem=xdg-run/hypr and run the gsr-hyprland-helper directly instead of flatpak spawn or use wlr foreign top level window protocol. Nvidia webcam yuyv capture doesn't seem to work on x11?
|
||||
|
||||
Allow settings page to select input capture option/audio, to make sure it doesn't blindly select the default option when the sources aren't temporary available when opening the settings.
|
||||
|
||||
Add simple video cutting in the ui.
|
||||
|
||||
Keep replay turned on when opening a fullscreen application and alt-tabbing. Only stop replay when closing the application (when closing all fullscreen applications).
|
||||
When alt-tabbing check if the previously fullscreen window still exists.
|
||||
|
||||
Show vulkan codec options.
|
||||
|
||||
Use wlroots protocol for region selection, if the protocol is available.
|
||||
|
||||
Submodule depends/mglpp updated: f69b0d3ee0...0542df4974
@@ -1,11 +0,0 @@
|
||||
[Unit]
|
||||
Description=GPU Screen Recorder UI Service
|
||||
|
||||
[Service]
|
||||
ExecStart=gsr-ui launch-daemon
|
||||
KillSignal=SIGINT
|
||||
Restart=on-failure
|
||||
RestartSec=5s
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
@@ -1,11 +0,0 @@
|
||||
[Unit]
|
||||
Description=GPU Screen Recorder UI Service
|
||||
|
||||
[Service]
|
||||
ExecStart=flatpak run com.dec05eba.gpu_screen_recorder gsr-ui
|
||||
KillSignal=SIGINT
|
||||
Restart=on-failure
|
||||
RestartSec=5s
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
Binary file not shown.
Binary file not shown.
@@ -2,9 +2,12 @@
|
||||
Type=Application
|
||||
Name=GPU Screen Recorder
|
||||
GenericName=Screen recorder
|
||||
GenericName[hu]=Képernyőrögzítő
|
||||
Comment=A ShadowPlay-like screen recorder for Linux
|
||||
Comment[hu]=ShadowPlay-szerű képernyőrögzítő Linuxra
|
||||
Icon=gpu-screen-recorder
|
||||
Exec=gsr-ui launch-hide-announce
|
||||
Terminal=false
|
||||
Keywords=gpu-screen-recorder;gsr-ui;screen recorder;streaming;twitch;replay;shadowplay;
|
||||
Keywords[hu]=gpu-screen-recorder;gsr-ui;képernyőrögzítő;streamelés;közvetítés;twitch;visszajátszás;shadowplay;
|
||||
Categories=AudioVideo;Recorder;
|
||||
|
||||
@@ -14,8 +14,7 @@ namespace gsr {
|
||||
enum class ReplayStartupMode {
|
||||
DONT_TURN_ON_AUTOMATICALLY,
|
||||
TURN_ON_AT_SYSTEM_STARTUP,
|
||||
TURN_ON_AT_FULLSCREEN,
|
||||
TURN_ON_AT_POWER_SUPPLY_CONNECTED
|
||||
TURN_ON_AT_GAME_LAUNCH
|
||||
};
|
||||
|
||||
ReplayStartupMode replay_startup_string_to_type(const char *startup_mode_str);
|
||||
@@ -46,10 +45,7 @@ namespace gsr {
|
||||
int32_t video_height = 0;
|
||||
int32_t fps = 60;
|
||||
int32_t video_bitrate = 8000;
|
||||
bool merge_audio_tracks = true; // TODO: Remove in the future
|
||||
bool application_audio_invert = false; // TODO: Remove in the future
|
||||
bool change_video_resolution = false;
|
||||
std::vector<std::string> audio_tracks; // ids, TODO: Remove in the future
|
||||
std::vector<AudioTrack> audio_tracks_list;
|
||||
std::string color_range = "limited";
|
||||
std::string video_quality = "very_high";
|
||||
@@ -61,6 +57,7 @@ namespace gsr {
|
||||
bool record_cursor = true;
|
||||
bool restore_portal_session = true;
|
||||
bool low_power_mode = false;
|
||||
bool enable_vulkan_video_encoding = false;
|
||||
|
||||
std::string webcam_source = "";
|
||||
bool webcam_flip_horizontally = false;
|
||||
@@ -139,6 +136,7 @@ namespace gsr {
|
||||
std::string turn_on_replay_automatically_mode = "dont_turn_on_automatically";
|
||||
bool save_video_in_game_folder = false;
|
||||
bool restart_replay_on_save = false;
|
||||
bool only_start_replay_if_power_supply_connected = false;
|
||||
std::string save_directory;
|
||||
std::string container = "mp4";
|
||||
int32_t replay_time = 60;
|
||||
|
||||
@@ -18,6 +18,13 @@ namespace gsr {
|
||||
bool av1_10bit = false;
|
||||
bool vp8 = false;
|
||||
bool vp9 = false;
|
||||
bool h264_vulkan = false;
|
||||
bool hevc_vulkan = false;
|
||||
bool hevc_hdr_vulkan = false;
|
||||
bool hevc_10bit_vulkan = false;
|
||||
bool av1_vulkan = false;
|
||||
bool av1_hdr_vulkan = false;
|
||||
bool av1_10bit_vulkan = false;
|
||||
};
|
||||
|
||||
struct SupportedImageFormats {
|
||||
|
||||
@@ -67,6 +67,12 @@ namespace gsr {
|
||||
FAST
|
||||
};
|
||||
|
||||
enum class GameReplayAction {
|
||||
IDLE,
|
||||
START,
|
||||
STOP
|
||||
};
|
||||
|
||||
class Overlay {
|
||||
public:
|
||||
Overlay(std::string resources_path, GsrInfo gsr_info, SupportedCaptureOptions capture_options, egl_functions egl_funcs);
|
||||
@@ -114,7 +120,7 @@ namespace gsr {
|
||||
void handle_keyboard_mapping_event();
|
||||
void on_event(mgl::Event &event);
|
||||
|
||||
void recreate_global_hotkeys(const char *hotkey_option);
|
||||
void recreate_global_hotkeys(std::string_view hotkey_option);
|
||||
void update_led_indicator_after_settings_change();
|
||||
void recreate_frontpage_ui_components();
|
||||
void xi_setup();
|
||||
@@ -123,21 +129,23 @@ namespace gsr {
|
||||
void grab_mouse_and_keyboard();
|
||||
void xi_setup_fake_cursor();
|
||||
|
||||
void close_gsr_game_tracker_output();
|
||||
void close_gpu_screen_recorder_output();
|
||||
|
||||
double get_time_passed_in_replay_buffer_seconds();
|
||||
void update_notification_process_status();
|
||||
void save_video_in_current_game_directory(std::string &video_filepath, NotificationType notification_type);
|
||||
void on_replay_saved(const char *replay_saved_filepath);
|
||||
void process_gsr_game_tracker_output();
|
||||
void process_gsr_output();
|
||||
void on_gsr_process_error(int exit_code, NotificationType notification_type);
|
||||
void update_gsr_process_status();
|
||||
void update_gsr_screenshot_process_status();
|
||||
|
||||
void replay_status_update_status();
|
||||
void update_focused_fullscreen_status();
|
||||
void update_power_supply_status();
|
||||
void update_system_startup_status();
|
||||
void update_gsr_game_tracker_replay_status();
|
||||
|
||||
void on_stop_recording(int exit_code, std::string &video_filepath);
|
||||
|
||||
@@ -157,7 +165,7 @@ namespace gsr {
|
||||
void on_press_save_replay();
|
||||
void on_press_save_replay_1_min_replay();
|
||||
void on_press_save_replay_10_min_replay();
|
||||
bool on_press_start_replay(bool disable_notification, bool finished_selection, std::string monitor_to_capture = "");
|
||||
bool on_press_start_replay(bool disable_notification, bool finished_selection, bool launched_manually = false);
|
||||
void on_press_start_record(bool finished_selection, RecordForceType force_type);
|
||||
void on_press_start_stream(bool finished_selection);
|
||||
void on_press_take_screenshot(bool finished_selection, ScreenshotForceType force_type);
|
||||
@@ -227,7 +235,6 @@ namespace gsr {
|
||||
mgl::Clock replay_status_update_clock;
|
||||
std::string power_supply_online_filepath;
|
||||
bool power_supply_connected = false;
|
||||
bool focused_window_is_fullscreen = false;
|
||||
|
||||
std::string record_filepath;
|
||||
std::string screenshot_filepath;
|
||||
@@ -252,7 +259,13 @@ namespace gsr {
|
||||
std::unique_ptr<GlobalHotkeys> global_hotkeys = nullptr;
|
||||
std::unique_ptr<GlobalHotkeysJoystick> global_hotkeys_js = nullptr;
|
||||
Display *x11_dpy = nullptr;
|
||||
XEvent x11_mapping_xev;
|
||||
XEvent x11_xev;
|
||||
|
||||
int gsr_game_tracker_process_output_fd = -1;
|
||||
FILE *gsr_game_tracker_process_output_file = nullptr;
|
||||
pid_t gsr_game_tracker_process_id = -1;
|
||||
bool game_running = false;
|
||||
GameReplayAction game_replay_action = GameReplayAction::IDLE;
|
||||
|
||||
struct wl_display *wayland_dpy = nullptr;
|
||||
|
||||
@@ -260,6 +273,7 @@ namespace gsr {
|
||||
bool replay_save_show_notification = false;
|
||||
ReplayStartupMode replay_startup_mode = ReplayStartupMode::TURN_ON_AT_SYSTEM_STARTUP;
|
||||
bool try_replay_startup = true;
|
||||
bool replay_launched_manually = false;
|
||||
bool replay_recording = false;
|
||||
int replay_save_duration_min = 0;
|
||||
mgl::Clock replay_duration_clock;
|
||||
@@ -290,6 +304,5 @@ namespace gsr {
|
||||
std::unique_ptr<LedIndicator> led_indicator = nullptr;
|
||||
|
||||
bool supports_window_title = false;
|
||||
bool supports_window_fullscreen_state = false;
|
||||
};
|
||||
}
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
#include <mglpp/system/MemoryMappedFile.hpp>
|
||||
#include <mglpp/graphics/Color.hpp>
|
||||
#include <mglpp/graphics/Font.hpp>
|
||||
#include <mglpp/graphics/Texture.hpp>
|
||||
|
||||
#include <string>
|
||||
@@ -19,12 +18,10 @@ namespace gsr {
|
||||
float window_width = 0.0f;
|
||||
float window_height = 0.0f;
|
||||
|
||||
mgl::MemoryMappedFile body_font_file;
|
||||
mgl::MemoryMappedFile title_font_file;
|
||||
mgl::Font body_font;
|
||||
mgl::Font title_font;
|
||||
mgl::Font top_bar_font;
|
||||
mgl::Font camera_setup_font;
|
||||
std::string body_font_desc;
|
||||
std::string title_font_desc;
|
||||
std::string top_bar_font_desc;
|
||||
std::string camera_setup_font_desc;
|
||||
|
||||
mgl::Texture combobox_arrow_texture;
|
||||
mgl::Texture settings_texture;
|
||||
|
||||
@@ -8,8 +8,8 @@ namespace gsr {
|
||||
public:
|
||||
static Translation& instance();
|
||||
void init(const char* translations_directory, const char* initial_language = nullptr);
|
||||
bool load_language(const char* lang);
|
||||
bool is_language_supported(const char* lang);
|
||||
bool load_language(std::string_view lang);
|
||||
bool is_language_supported(std::string_view lang);
|
||||
bool plural_numbers_are_complex();
|
||||
const char* translate(const char* key);
|
||||
|
||||
|
||||
@@ -36,4 +36,15 @@ namespace gsr {
|
||||
// Returns the path to the parent directory (ignoring trailing /)
|
||||
// of "." if there is no parent directory and the directory path is relative
|
||||
std::string get_parent_directory(std::string_view directory);
|
||||
|
||||
// XDG Autostart helpers — toggle ~/.config/autostart/gpu-screen-recorder-ui.desktop
|
||||
bool is_xdg_autostart_enabled();
|
||||
// Returns 0 on success
|
||||
int set_xdg_autostart(bool enable);
|
||||
void replace_xdg_autostart_with_current_gsr_type();
|
||||
|
||||
// Systemd user service helpers
|
||||
bool wait_until_systemd_user_service_available();
|
||||
bool is_systemd_service_enabled(const char *service_name);
|
||||
bool disable_systemd_service(const char *service_name);
|
||||
}
|
||||
@@ -12,7 +12,7 @@ namespace gsr {
|
||||
public:
|
||||
// If width is 0 then the width of the text is used instead (with padding).
|
||||
// If height is 0 then the height of the text is used instead (with padding).
|
||||
Button(mgl::Font *font, const char *text, mgl::vec2f size, mgl::Color bg_color);
|
||||
Button(const char *font_desc, const char *text, mgl::vec2f size, mgl::Color bg_color);
|
||||
Button(const Button&) = delete;
|
||||
Button& operator=(const Button&) = delete;
|
||||
|
||||
@@ -25,8 +25,8 @@ namespace gsr {
|
||||
void set_bg_hover_color(mgl::Color color);
|
||||
void set_icon(mgl::Texture *texture);
|
||||
|
||||
const std::string& get_text() const;
|
||||
void set_text(std::string str);
|
||||
std::string_view get_text() const;
|
||||
void set_text(std::string_view str);
|
||||
|
||||
std::function<void()> on_click;
|
||||
private:
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
namespace gsr {
|
||||
class CheckBox : public Widget {
|
||||
public:
|
||||
CheckBox(mgl::Font *font, const char *text);
|
||||
CheckBox(const char *font_desc, const char *text);
|
||||
CheckBox(const CheckBox&) = delete;
|
||||
CheckBox& operator=(const CheckBox&) = delete;
|
||||
|
||||
|
||||
@@ -11,24 +11,24 @@
|
||||
namespace gsr {
|
||||
class ComboBox : public Widget {
|
||||
public:
|
||||
ComboBox(mgl::Font *font);
|
||||
ComboBox(const char *font_desc);
|
||||
ComboBox(const ComboBox&) = delete;
|
||||
ComboBox& operator=(const ComboBox&) = delete;
|
||||
|
||||
bool on_event(mgl::Event &event, mgl::Window &window, mgl::vec2f offset) override;
|
||||
void draw(mgl::Window &window, mgl::vec2f offset) override;
|
||||
|
||||
void add_item(const std::string &text, const std::string &id, bool allow_duplicate = true);
|
||||
void add_item(std::string_view text, const std::string &id, bool allow_duplicate = true);
|
||||
void clear_items();
|
||||
|
||||
// The item can only be selected if it's enabled
|
||||
void set_selected_item(const std::string &id, bool trigger_event = true, bool trigger_event_even_if_selection_not_changed = true);
|
||||
void set_item_enabled(const std::string &id, bool enabled);
|
||||
const std::string& get_selected_id() const;
|
||||
void set_selected_item(std::string_view id, bool trigger_event = true, bool trigger_event_even_if_selection_not_changed = true);
|
||||
void set_item_enabled(std::string_view id, bool enabled);
|
||||
std::string_view get_selected_id() const;
|
||||
|
||||
mgl::vec2f get_size() override;
|
||||
|
||||
std::function<void(const std::string &text, const std::string &id)> on_selection_changed;
|
||||
std::function<void(std::string_view text, std::string_view id)> on_selection_changed;
|
||||
private:
|
||||
void draw_selected(mgl::Window &window, mgl::vec2f draw_pos);
|
||||
void draw_unselected(mgl::Window &window, mgl::vec2f draw_pos);
|
||||
@@ -44,7 +44,8 @@ namespace gsr {
|
||||
};
|
||||
|
||||
mgl::vec2f max_size;
|
||||
mgl::Font *font;
|
||||
std::string font_desc;
|
||||
int font_size = 0;
|
||||
std::vector<Item> items;
|
||||
mgl::Sprite dropdown_arrow;
|
||||
bool dirty = true;
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
namespace gsr {
|
||||
class DropdownButton : public Widget {
|
||||
public:
|
||||
DropdownButton(mgl::Font *title_font, mgl::Font *description_font, const char *title, const char *description, mgl::Texture *icon_texture, mgl::vec2f size);
|
||||
DropdownButton(const char *title_font_desc, const char *description_font_desc, const char *title, const char *description, mgl::Texture *icon_texture, mgl::vec2f size);
|
||||
DropdownButton(const DropdownButton&) = delete;
|
||||
DropdownButton& operator=(const DropdownButton&) = delete;
|
||||
|
||||
@@ -18,10 +18,10 @@ namespace gsr {
|
||||
void draw(mgl::Window &window, mgl::vec2f offset) override;
|
||||
|
||||
void add_item(const std::string &text, const std::string &id, const std::string &description = "");
|
||||
void set_item_label(const std::string &id, const std::string &new_label);
|
||||
void set_item_icon(const std::string &id, mgl::Texture *texture);
|
||||
void set_item_description(const std::string &id, const std::string &new_description);
|
||||
void set_item_enabled(const std::string &id, bool enabled);
|
||||
void set_item_label(std::string_view id, const std::string &new_label);
|
||||
void set_item_icon(std::string_view id, mgl::Texture *texture);
|
||||
void set_item_description(std::string_view id, const std::string &new_description);
|
||||
void set_item_enabled(std::string_view id, bool enabled);
|
||||
|
||||
void set_description(std::string description_text);
|
||||
void set_activated(bool activated);
|
||||
@@ -41,8 +41,8 @@ namespace gsr {
|
||||
};
|
||||
|
||||
std::vector<Item> items;
|
||||
mgl::Font *title_font;
|
||||
mgl::Font *description_font;
|
||||
std::string title_font_desc;
|
||||
std::string description_font_desc;
|
||||
mgl::vec2f size;
|
||||
bool mouse_inside = false;
|
||||
bool show_dropdown = false;
|
||||
|
||||
@@ -4,32 +4,16 @@
|
||||
#include <functional>
|
||||
|
||||
#include <mglpp/graphics/Color.hpp>
|
||||
#include <mglpp/graphics/Text32.hpp>
|
||||
#include <mglpp/graphics/TextEdit.hpp>
|
||||
#include <mglpp/graphics/Text.hpp>
|
||||
#include <mglpp/graphics/Rectangle.hpp>
|
||||
|
||||
namespace gsr {
|
||||
class Entry;
|
||||
|
||||
enum class EntryValidateHandlerResult {
|
||||
DENY,
|
||||
ALLOW,
|
||||
REPLACED
|
||||
};
|
||||
using EntryValidateHandler = std::function<EntryValidateHandlerResult(Entry &entry, const std::u32string &str)>;
|
||||
|
||||
struct CaretIndexPos {
|
||||
int index;
|
||||
mgl::vec2f pos;
|
||||
};
|
||||
|
||||
class Entry : public Widget {
|
||||
public:
|
||||
enum class Direction {
|
||||
LEFT,
|
||||
RIGHT
|
||||
};
|
||||
|
||||
Entry(mgl::Font *font, const char *text, float max_width);
|
||||
Entry(const char *font_desc, const char *text, float max_width);
|
||||
Entry(const Entry&) = delete;
|
||||
Entry& operator=(const Entry&) = delete;
|
||||
|
||||
@@ -38,44 +22,18 @@ namespace gsr {
|
||||
|
||||
mgl::vec2f get_size() override;
|
||||
|
||||
EntryValidateHandlerResult set_text(const std::string &str);
|
||||
std::string get_text() const;
|
||||
void set_text(std::string_view str);
|
||||
std::string_view get_text() const;
|
||||
|
||||
void set_masked(bool masked);
|
||||
bool is_masked() const;
|
||||
|
||||
// Return false to specify that the string should not be accepted. This reverts the string back to its previous value.
|
||||
// The input can be changed by changing the input parameter and returning true.
|
||||
EntryValidateHandler validate_handler;
|
||||
void set_number_mode(bool enabled, int min_val, int max_val);
|
||||
|
||||
std::function<void(const std::string &text)> on_changed;
|
||||
std::function<void(std::string_view text)> on_changed;
|
||||
private:
|
||||
// Also updates the cursor position
|
||||
void replace_text(size_t index, size_t size, const std::u32string &replacement);
|
||||
void move_caret_word(Direction direction, size_t max_codepoints);
|
||||
EntryValidateHandlerResult set_text_internal(std::u32string str);
|
||||
void draw_caret(mgl::Window &window, mgl::vec2f draw_pos, mgl::vec2f caret_size);
|
||||
void draw_caret_selection(mgl::Window &window, mgl::vec2f draw_pos, mgl::vec2f caret_size);
|
||||
CaretIndexPos find_closest_caret_index_by_position(mgl::vec2f position);
|
||||
private:
|
||||
struct Caret {
|
||||
float offset_x = 0.0f;
|
||||
int index = 0;
|
||||
};
|
||||
|
||||
mgl::Rectangle background;
|
||||
mgl::Text32 text;
|
||||
mgl::Text32 masked_text;
|
||||
float max_width;
|
||||
bool selected = false;
|
||||
bool selecting_text = false;
|
||||
bool selecting_with_keyboard = false;
|
||||
bool show_selection = false;
|
||||
bool masked = false;
|
||||
Caret caret;
|
||||
Caret selection_start_caret;
|
||||
float text_overflow = 0.0f;
|
||||
mgl::TextEdit text_edit;
|
||||
float max_width = 0.0f;
|
||||
};
|
||||
|
||||
EntryValidateHandler create_entry_validator_integer_in_range(int min, int max);
|
||||
}
|
||||
@@ -29,7 +29,7 @@ namespace gsr {
|
||||
mgl::vec2f get_inner_size() override;
|
||||
void set_size(mgl::vec2f size);
|
||||
private:
|
||||
void set_current_directory(const char *directory);
|
||||
void set_current_directory(std::string_view directory);
|
||||
private:
|
||||
struct Folder {
|
||||
mgl::Text text;
|
||||
@@ -48,7 +48,7 @@ namespace gsr {
|
||||
|
||||
class FileChooser : public Widget {
|
||||
public:
|
||||
FileChooser(const char *start_directory, mgl::vec2f size);
|
||||
FileChooser(std::string_view start_directory, mgl::vec2f size);
|
||||
FileChooser(const FileChooser&) = delete;
|
||||
FileChooser& operator=(const FileChooser&) = delete;
|
||||
|
||||
@@ -58,10 +58,10 @@ namespace gsr {
|
||||
|
||||
mgl::vec2f get_size() override;
|
||||
|
||||
void set_current_directory(const char *directory);
|
||||
void open_subdirectory(const char *name);
|
||||
void set_current_directory(std::string_view directory);
|
||||
void open_subdirectory(std::string_view name);
|
||||
void open_parent_directory();
|
||||
const std::string& get_current_directory() const;
|
||||
std::string_view get_current_directory() const;
|
||||
private:
|
||||
struct Folder {
|
||||
mgl::Text text;
|
||||
|
||||
@@ -49,9 +49,9 @@ namespace gsr {
|
||||
bool on_event(mgl::Event &event, mgl::Window &window, mgl::vec2f offset) override;
|
||||
|
||||
std::function<void(bool enable, int exit_status)> on_startup_changed;
|
||||
std::function<void(const char *reason)> on_click_exit_program_button;
|
||||
std::function<void(const char *hotkey_option)> on_keyboard_hotkey_changed;
|
||||
std::function<void(const char *hotkey_option)> on_joystick_hotkey_changed;
|
||||
std::function<void(std::string_view reason)> on_click_exit_program_button;
|
||||
std::function<void(std::string_view hotkey_option)> on_keyboard_hotkey_changed;
|
||||
std::function<void(std::string_view hotkey_option)> on_joystick_hotkey_changed;
|
||||
std::function<void()> on_page_closed;
|
||||
private:
|
||||
void load_hotkeys();
|
||||
|
||||
@@ -8,8 +8,7 @@
|
||||
namespace gsr {
|
||||
class Label : public Widget {
|
||||
public:
|
||||
// TODO: Allow specifying max width, at which either a line-break should occur or elipses should show
|
||||
Label(mgl::Font *font, const char *text, mgl::Color color);
|
||||
Label(const char *font_desc, const char *text, mgl::Color color);
|
||||
Label(const Label&) = delete;
|
||||
Label& operator=(const Label&) = delete;
|
||||
|
||||
@@ -18,8 +17,14 @@ namespace gsr {
|
||||
|
||||
mgl::vec2f get_size() override;
|
||||
|
||||
void set_text(std::string str);
|
||||
const std::string& get_text() const;
|
||||
void set_text(std::string_view str);
|
||||
std::string_view get_text() const;
|
||||
|
||||
// Set to 0 to disable
|
||||
void set_wrap_width(int width);
|
||||
|
||||
// Set to 0 to disable
|
||||
void set_max_rows(int max_rows);
|
||||
private:
|
||||
mgl::Text text;
|
||||
};
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace gsr {
|
||||
HORIZONTAL
|
||||
};
|
||||
|
||||
RadioButton(mgl::Font *font, Orientation orientation);
|
||||
RadioButton(const char *font_desc, Orientation orientation);
|
||||
RadioButton(const RadioButton&) = delete;
|
||||
RadioButton& operator=(const RadioButton&) = delete;
|
||||
|
||||
@@ -22,14 +22,14 @@ namespace gsr {
|
||||
void draw(mgl::Window &window, mgl::vec2f offset) override;
|
||||
|
||||
void add_item(const std::string &text, const std::string &id);
|
||||
void set_selected_item(const std::string &id, bool trigger_event = true, bool trigger_event_even_if_selection_not_changed = true);
|
||||
const std::string& get_selected_id() const;
|
||||
const std::string& get_selected_text() const;
|
||||
void set_selected_item(std::string_view id, bool trigger_event = true, bool trigger_event_even_if_selection_not_changed = true);
|
||||
std::string_view get_selected_id() const;
|
||||
std::string_view get_selected_text() const;
|
||||
|
||||
mgl::vec2f get_size() override;
|
||||
|
||||
// Return false to revert the change
|
||||
std::function<bool(const std::string &text, const std::string &id)> on_selection_changed;
|
||||
std::function<bool(std::string_view text, std::string_view id)> on_selection_changed;
|
||||
private:
|
||||
void update_if_dirty();
|
||||
private:
|
||||
@@ -38,7 +38,7 @@ namespace gsr {
|
||||
std::string id;
|
||||
};
|
||||
|
||||
mgl::Font *font;
|
||||
std::string font_desc;
|
||||
Orientation orientation;
|
||||
std::vector<Item> items;
|
||||
size_t selected_item = 0;
|
||||
|
||||
@@ -41,7 +41,7 @@ namespace gsr {
|
||||
STREAM
|
||||
};
|
||||
|
||||
SettingsPage(Type type, const GsrInfo *gsr_info, Config &config, PageStack *page_stack, bool supports_window_title, bool supports_window_fullscreen_state);
|
||||
SettingsPage(Type type, const GsrInfo *gsr_info, Config &config, PageStack *page_stack, bool supports_window_title);
|
||||
SettingsPage(const SettingsPage&) = delete;
|
||||
SettingsPage& operator=(const SettingsPage&) = delete;
|
||||
|
||||
@@ -109,6 +109,7 @@ namespace gsr {
|
||||
std::unique_ptr<List> create_framerate_mode();
|
||||
std::unique_ptr<List> create_framerate_section();
|
||||
std::unique_ptr<Widget> create_record_cursor_section();
|
||||
std::unique_ptr<Widget> create_enable_vulkan_video_encoding_section();
|
||||
std::unique_ptr<Widget> create_video_section();
|
||||
std::unique_ptr<Widget> create_settings();
|
||||
void add_widgets();
|
||||
@@ -122,17 +123,18 @@ namespace gsr {
|
||||
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<Widget> create_start_replay_automatically_section();
|
||||
std::unique_ptr<CheckBox> create_save_replay_in_game_folder();
|
||||
std::unique_ptr<CheckBox> create_restart_replay_on_save();
|
||||
std::unique_ptr<Label> create_estimated_replay_file_size();
|
||||
void update_estimated_replay_file_size(const std::string &replay_storage_type);
|
||||
void update_estimated_replay_file_size(std::string_view replay_storage_type);
|
||||
void update_replay_time_text();
|
||||
std::unique_ptr<CheckBox> create_save_recording_in_game_folder();
|
||||
std::unique_ptr<Label> create_estimated_record_file_size();
|
||||
void update_estimated_record_file_size();
|
||||
std::unique_ptr<CheckBox> create_led_indicator(const char *type);
|
||||
std::unique_ptr<CheckBox> create_notifications(const char *type);
|
||||
std::unique_ptr<List> create_indicator(const char *type);
|
||||
std::unique_ptr<CheckBox> create_led_indicator();
|
||||
std::unique_ptr<CheckBox> create_notifications();
|
||||
std::unique_ptr<List> create_indicator();
|
||||
std::unique_ptr<Widget> create_low_power_mode();
|
||||
void add_replay_widgets();
|
||||
void add_record_widgets();
|
||||
@@ -140,6 +142,9 @@ namespace gsr {
|
||||
std::unique_ptr<ComboBox> create_streaming_service_box();
|
||||
std::unique_ptr<List> create_streaming_service_section();
|
||||
std::unique_ptr<List> create_stream_key_section();
|
||||
std::unique_ptr<List> create_stream_kick_url();
|
||||
std::unique_ptr<List> create_stream_kick_key();
|
||||
std::unique_ptr<List> create_stream_kick_section();
|
||||
std::unique_ptr<List> create_stream_custom_url();
|
||||
std::unique_ptr<List> create_stream_custom_key();
|
||||
std::unique_ptr<List> create_stream_custom_section();
|
||||
@@ -200,6 +205,7 @@ namespace gsr {
|
||||
ComboBox *streaming_service_box_ptr = nullptr;
|
||||
List *stream_key_list_ptr = nullptr;
|
||||
List *custom_stream_list_ptr = nullptr;
|
||||
List *kick_stream_list_ptr = nullptr;
|
||||
CheckBox *save_replay_in_game_folder_ptr = nullptr;
|
||||
CheckBox *restart_replay_on_save = nullptr;
|
||||
Label *estimated_file_size_ptr = nullptr;
|
||||
@@ -226,6 +232,9 @@ namespace gsr {
|
||||
List *webcam_body_list_ptr = nullptr;
|
||||
CheckBox *flip_camera_horizontally_checkbox_ptr = nullptr;
|
||||
CheckBox *low_power_mode_checkbox_ptr = nullptr;
|
||||
CheckBox *replay_power_supply_checkbox_ptr = nullptr;
|
||||
CheckBox *enable_vulkan_checkbox_ptr = nullptr;
|
||||
List *vulkan_video_list_ptr = nullptr;
|
||||
|
||||
PageStack *page_stack = nullptr;
|
||||
|
||||
@@ -249,6 +258,5 @@ namespace gsr {
|
||||
std::optional<GsrCameraSetup> selected_camera_setup;
|
||||
|
||||
bool supports_window_title = false;
|
||||
bool supports_window_fullscreen_state = false;
|
||||
};
|
||||
}
|
||||
@@ -6,7 +6,7 @@
|
||||
namespace gsr {
|
||||
class Tooltip : public Widget {
|
||||
public:
|
||||
Tooltip(mgl::Font *font);
|
||||
Tooltip(const char *font_desc);
|
||||
Tooltip(const Tooltip&) = delete;
|
||||
Tooltip& operator=(const Tooltip&) = delete;
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace gsr {
|
||||
|
||||
mgl::vec2f get_size() override;
|
||||
|
||||
void set_text(std::string text);
|
||||
void set_text(std::string_view text);
|
||||
private:
|
||||
mgl::Text label;
|
||||
};
|
||||
|
||||
17
meson.build
17
meson.build
@@ -1,4 +1,4 @@
|
||||
project('gsr-ui', ['c', 'cpp'], version : '1.10.9', default_options : ['warning_level=2', 'cpp_std=c++17'], subproject_dir : 'depends')
|
||||
project('gsr-ui', ['c', 'cpp'], version : '1.11.5', default_options : ['warning_level=2', 'cpp_std=c++17'], subproject_dir : 'depends')
|
||||
|
||||
add_project_arguments('-D_FILE_OFFSET_BITS=64', language : ['c', 'cpp'])
|
||||
|
||||
@@ -70,7 +70,7 @@ gsr_ui_resources_path = join_paths(prefix, datadir, 'gsr-ui')
|
||||
icons_path = join_paths(prefix, datadir, 'icons')
|
||||
|
||||
add_project_arguments('-DGSR_UI_VERSION="' + meson.project_version() + '"', language: ['c', 'cpp'])
|
||||
add_project_arguments('-DGSR_FLATPAK_VERSION="5.12.2"', language: ['c', 'cpp'])
|
||||
add_project_arguments('-DGSR_FLATPAK_VERSION="5.13.2"', language: ['c', 'cpp'])
|
||||
|
||||
add_project_arguments('-DKWIN_HELPER_SCRIPT_PATH="' + gsr_ui_resources_path + '/gsrkwinhelper.js"', language: ['c', 'cpp'])
|
||||
|
||||
@@ -140,8 +140,15 @@ executable(
|
||||
install : true
|
||||
)
|
||||
|
||||
executable(
|
||||
'gsr-game-tracker',
|
||||
[
|
||||
'tools/gsr-game-tracker/main.c'
|
||||
],
|
||||
install : true
|
||||
)
|
||||
|
||||
install_subdir('images', install_dir : gsr_ui_resources_path)
|
||||
install_subdir('fonts', install_dir : gsr_ui_resources_path)
|
||||
install_subdir('translations', install_dir : gsr_ui_resources_path)
|
||||
|
||||
if get_option('desktop-files') == true
|
||||
@@ -152,10 +159,6 @@ if get_option('desktop-files') == true
|
||||
gnome.post_install(update_desktop_database : true)
|
||||
endif
|
||||
|
||||
if get_option('systemd') == true
|
||||
install_data(files('extra/gpu-screen-recorder-ui.service'), install_dir : 'lib/systemd/user')
|
||||
endif
|
||||
|
||||
if get_option('capabilities') == true
|
||||
meson.add_install_script('meson_post_install.sh')
|
||||
endif
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
option('systemd', type : 'boolean', value : true, description : 'Install systemd service file')
|
||||
option('capabilities', type : 'boolean', value : true, description : 'Set binary setuid capability on gsr-global-hotkeys binary to allow global hotkeys')
|
||||
option('desktop-files', type : 'boolean', value : true, description : 'Install desktop files')
|
||||
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "gsr-ui"
|
||||
type = "executable"
|
||||
version = "1.10.9"
|
||||
version = "1.11.5"
|
||||
platforms = ["posix"]
|
||||
|
||||
[lang.cpp]
|
||||
|
||||
@@ -53,10 +53,8 @@ namespace gsr {
|
||||
return ReplayStartupMode::DONT_TURN_ON_AUTOMATICALLY;
|
||||
else if(strcmp(startup_mode_str, "turn_on_at_system_startup") == 0)
|
||||
return ReplayStartupMode::TURN_ON_AT_SYSTEM_STARTUP;
|
||||
else if(strcmp(startup_mode_str, "turn_on_at_fullscreen") == 0)
|
||||
return ReplayStartupMode::TURN_ON_AT_FULLSCREEN;
|
||||
else if(strcmp(startup_mode_str, "turn_on_at_power_supply_connected") == 0)
|
||||
return ReplayStartupMode::TURN_ON_AT_POWER_SUPPLY_CONNECTED;
|
||||
else if(strcmp(startup_mode_str, "turn_on_at_fullscreen") == 0 || strcmp(startup_mode_str, "turn_on_at_game_launch") == 0)
|
||||
return ReplayStartupMode::TURN_ON_AT_GAME_LAUNCH;
|
||||
else
|
||||
return ReplayStartupMode::DONT_TURN_ON_AUTOMATICALLY;
|
||||
}
|
||||
@@ -188,10 +186,7 @@ namespace gsr {
|
||||
{"streaming.record_options.video_height", &config.streaming_config.record_options.video_height},
|
||||
{"streaming.record_options.fps", &config.streaming_config.record_options.fps},
|
||||
{"streaming.record_options.video_bitrate", &config.streaming_config.record_options.video_bitrate},
|
||||
{"streaming.record_options.merge_audio_tracks", &config.streaming_config.record_options.merge_audio_tracks},
|
||||
{"streaming.record_options.application_audio_invert", &config.streaming_config.record_options.application_audio_invert},
|
||||
{"streaming.record_options.change_video_resolution", &config.streaming_config.record_options.change_video_resolution},
|
||||
{"streaming.record_options.audio_track", &config.streaming_config.record_options.audio_tracks},
|
||||
{"streaming.record_options.audio_track_item", &config.streaming_config.record_options.audio_tracks_list},
|
||||
{"streaming.record_options.color_range", &config.streaming_config.record_options.color_range},
|
||||
{"streaming.record_options.video_quality", &config.streaming_config.record_options.video_quality},
|
||||
@@ -203,6 +198,7 @@ namespace gsr {
|
||||
{"streaming.record_options.record_cursor", &config.streaming_config.record_options.record_cursor},
|
||||
{"streaming.record_options.restore_portal_session", &config.streaming_config.record_options.restore_portal_session},
|
||||
{"streaming.record_options.low_power_mode", &config.streaming_config.record_options.low_power_mode},
|
||||
{"streaming.record_options.enable_vulkan_video_encoding", &config.streaming_config.record_options.enable_vulkan_video_encoding},
|
||||
{"streaming.record_options.webcam_source", &config.streaming_config.record_options.webcam_source},
|
||||
{"streaming.record_options.webcam_flip_horizontally", &config.streaming_config.record_options.webcam_flip_horizontally},
|
||||
{"streaming.record_options.webcam_video_format", &config.streaming_config.record_options.webcam_video_format},
|
||||
@@ -233,10 +229,7 @@ namespace gsr {
|
||||
{"record.record_options.video_height", &config.record_config.record_options.video_height},
|
||||
{"record.record_options.fps", &config.record_config.record_options.fps},
|
||||
{"record.record_options.video_bitrate", &config.record_config.record_options.video_bitrate},
|
||||
{"record.record_options.merge_audio_tracks", &config.record_config.record_options.merge_audio_tracks},
|
||||
{"record.record_options.application_audio_invert", &config.record_config.record_options.application_audio_invert},
|
||||
{"record.record_options.change_video_resolution", &config.record_config.record_options.change_video_resolution},
|
||||
{"record.record_options.audio_track", &config.record_config.record_options.audio_tracks},
|
||||
{"record.record_options.audio_track_item", &config.record_config.record_options.audio_tracks_list},
|
||||
{"record.record_options.color_range", &config.record_config.record_options.color_range},
|
||||
{"record.record_options.video_quality", &config.record_config.record_options.video_quality},
|
||||
@@ -248,6 +241,7 @@ namespace gsr {
|
||||
{"record.record_options.record_cursor", &config.record_config.record_options.record_cursor},
|
||||
{"record.record_options.restore_portal_session", &config.record_config.record_options.restore_portal_session},
|
||||
{"record.record_options.low_power_mode", &config.record_config.record_options.low_power_mode},
|
||||
{"record.record_options.enable_vulkan_video_encoding", &config.record_config.record_options.enable_vulkan_video_encoding},
|
||||
{"record.record_options.webcam_source", &config.record_config.record_options.webcam_source},
|
||||
{"record.record_options.webcam_flip_horizontally", &config.record_config.record_options.webcam_flip_horizontally},
|
||||
{"record.record_options.webcam_video_format", &config.record_config.record_options.webcam_video_format},
|
||||
@@ -275,10 +269,7 @@ namespace gsr {
|
||||
{"replay.record_options.video_height", &config.replay_config.record_options.video_height},
|
||||
{"replay.record_options.fps", &config.replay_config.record_options.fps},
|
||||
{"replay.record_options.video_bitrate", &config.replay_config.record_options.video_bitrate},
|
||||
{"replay.record_options.merge_audio_tracks", &config.replay_config.record_options.merge_audio_tracks},
|
||||
{"replay.record_options.application_audio_invert", &config.replay_config.record_options.application_audio_invert},
|
||||
{"replay.record_options.change_video_resolution", &config.replay_config.record_options.change_video_resolution},
|
||||
{"replay.record_options.audio_track", &config.replay_config.record_options.audio_tracks},
|
||||
{"replay.record_options.audio_track_item", &config.replay_config.record_options.audio_tracks_list},
|
||||
{"replay.record_options.color_range", &config.replay_config.record_options.color_range},
|
||||
{"replay.record_options.video_quality", &config.replay_config.record_options.video_quality},
|
||||
@@ -290,6 +281,7 @@ namespace gsr {
|
||||
{"replay.record_options.record_cursor", &config.replay_config.record_options.record_cursor},
|
||||
{"replay.record_options.restore_portal_session", &config.replay_config.record_options.restore_portal_session},
|
||||
{"replay.record_options.low_power_mode", &config.replay_config.record_options.low_power_mode},
|
||||
{"replay.record_options.enable_vulkan_video_encoding", &config.replay_config.record_options.enable_vulkan_video_encoding},
|
||||
{"replay.record_options.webcam_source", &config.replay_config.record_options.webcam_source},
|
||||
{"replay.record_options.webcam_flip_horizontally", &config.replay_config.record_options.webcam_flip_horizontally},
|
||||
{"replay.record_options.webcam_video_format", &config.replay_config.record_options.webcam_video_format},
|
||||
@@ -305,6 +297,7 @@ namespace gsr {
|
||||
{"replay.turn_on_replay_automatically_mode", &config.replay_config.turn_on_replay_automatically_mode},
|
||||
{"replay.save_video_in_game_folder", &config.replay_config.save_video_in_game_folder},
|
||||
{"replay.restart_replay_on_save", &config.replay_config.restart_replay_on_save},
|
||||
{"replay.only_start_replay_if_power_supply_connected", &config.replay_config.only_start_replay_if_power_supply_connected},
|
||||
{"replay.save_directory", &config.replay_config.save_directory},
|
||||
{"replay.container", &config.replay_config.container},
|
||||
{"replay.time", &config.replay_config.replay_time},
|
||||
@@ -372,17 +365,6 @@ namespace gsr {
|
||||
return !operator==(other);
|
||||
}
|
||||
|
||||
static void populate_new_audio_track_from_old(RecordOptions &record_options) {
|
||||
if(record_options.merge_audio_tracks) {
|
||||
record_options.audio_tracks_list.push_back({std::move(record_options.audio_tracks), record_options.application_audio_invert});
|
||||
} else {
|
||||
for(const std::string &audio_input : record_options.audio_tracks) {
|
||||
record_options.audio_tracks_list.push_back({std::vector<std::string>{audio_input}, record_options.application_audio_invert});
|
||||
}
|
||||
}
|
||||
record_options.audio_tracks.clear();
|
||||
}
|
||||
|
||||
std::optional<Config> read_config(const SupportedCaptureOptions &capture_options) {
|
||||
std::optional<Config> config;
|
||||
|
||||
@@ -395,10 +377,6 @@ namespace gsr {
|
||||
|
||||
config = Config(capture_options);
|
||||
|
||||
config->streaming_config.record_options.audio_tracks.clear();
|
||||
config->record_config.record_options.audio_tracks.clear();
|
||||
config->replay_config.record_options.audio_tracks.clear();
|
||||
|
||||
config->streaming_config.record_options.audio_tracks_list.clear();
|
||||
config->record_config.record_options.audio_tracks_list.clear();
|
||||
config->replay_config.record_options.audio_tracks_list.clear();
|
||||
@@ -465,15 +443,9 @@ namespace gsr {
|
||||
return true;
|
||||
});
|
||||
|
||||
if(config->main_config.config_file_version == 1) {
|
||||
populate_new_audio_track_from_old(config->streaming_config.record_options);
|
||||
populate_new_audio_track_from_old(config->record_config.record_options);
|
||||
populate_new_audio_track_from_old(config->replay_config.record_options);
|
||||
}
|
||||
|
||||
config->streaming_config.record_options.audio_tracks.clear();
|
||||
config->record_config.record_options.audio_tracks.clear();
|
||||
config->replay_config.record_options.audio_tracks.clear();
|
||||
// TODO: Remove in the future
|
||||
if(config->replay_config.turn_on_replay_automatically_mode == "turn_on_at_fullscreen")
|
||||
config->replay_config.turn_on_replay_automatically_mode = "turn_on_at_game_launch";
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
@@ -39,13 +39,13 @@ namespace gsr {
|
||||
|
||||
static uint32_t mgl_key_modifiers_to_x11_modifier_mask(const mgl::Event::KeyEvent &key_event) {
|
||||
uint32_t mask = 0;
|
||||
if(key_event.shift)
|
||||
if(key_event.key_states.shift)
|
||||
mask |= ShiftMask;
|
||||
if(key_event.control)
|
||||
if(key_event.key_states.control)
|
||||
mask |= ControlMask;
|
||||
if(key_event.alt)
|
||||
if(key_event.key_states.alt)
|
||||
mask |= Mod1Mask;
|
||||
if(key_event.system)
|
||||
if(key_event.key_states.system)
|
||||
mask |= Mod4Mask;
|
||||
return mask;
|
||||
}
|
||||
|
||||
@@ -157,6 +157,20 @@ namespace gsr {
|
||||
gsr_info->supported_video_codecs.vp8 = true;
|
||||
else if(line == "vp9")
|
||||
gsr_info->supported_video_codecs.vp9 = true;
|
||||
else if(line == "h264_vulkan")
|
||||
gsr_info->supported_video_codecs.h264_vulkan = true;
|
||||
else if(line == "hevc_vulkan")
|
||||
gsr_info->supported_video_codecs.hevc_vulkan = true;
|
||||
else if(line == "hevc_hdr_vulkan")
|
||||
gsr_info->supported_video_codecs.hevc_hdr_vulkan = true;
|
||||
else if(line == "hevc_10bit_vulkan")
|
||||
gsr_info->supported_video_codecs.hevc_10bit_vulkan = true;
|
||||
else if(line == "av1_vulkan")
|
||||
gsr_info->supported_video_codecs.av1_vulkan = true;
|
||||
else if(line == "av1_hdr_vulkan")
|
||||
gsr_info->supported_video_codecs.av1_hdr_vulkan = true;
|
||||
else if(line == "av1_10bit_vulkan")
|
||||
gsr_info->supported_video_codecs.av1_10bit_vulkan = true;
|
||||
}
|
||||
|
||||
static void parse_image_formats_line(GsrInfo *gsr_info, std::string_view line) {
|
||||
|
||||
@@ -4,10 +4,12 @@
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
|
||||
namespace gsr {
|
||||
static ActiveHyprlandWindow active_hyprland_window;
|
||||
static bool hyprland_listener_thread_started = false;
|
||||
static std::mutex active_window_mutex;
|
||||
|
||||
static bool get_hyprland_socket_path(char *path, int path_len) {
|
||||
const char* xdg_runtime_dir = getenv("XDG_RUNTIME_DIR");
|
||||
@@ -83,6 +85,7 @@ namespace gsr {
|
||||
|
||||
size_t pos = line.find(prefix);
|
||||
if (pos != std::string::npos) {
|
||||
std::lock_guard<std::mutex> lock(active_window_mutex);
|
||||
active_hyprland_window.title = line.substr(pos + prefix.length());
|
||||
}
|
||||
}
|
||||
@@ -105,6 +108,7 @@ namespace gsr {
|
||||
}
|
||||
|
||||
std::string get_current_hyprland_window_title() {
|
||||
std::lock_guard<std::mutex> lock(active_window_mutex);
|
||||
return active_hyprland_window.title;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,15 +5,12 @@
|
||||
#include <string>
|
||||
#include <sys/types.h>
|
||||
#include <thread>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <mutex>
|
||||
|
||||
namespace gsr {
|
||||
static ActiveKwinWindow active_kwin_window;
|
||||
static bool kwin_helper_thread_started = false;
|
||||
static std::mutex active_window_mutex;
|
||||
|
||||
void kwin_script_thread() {
|
||||
FILE* pipe = popen("gsr-kwin-helper", "r");
|
||||
@@ -37,6 +34,7 @@ namespace gsr {
|
||||
line.pop_back();
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(active_window_mutex);
|
||||
size_t pos = std::string::npos;
|
||||
if ((pos = line.find(prefix_title)) != std::string::npos) {
|
||||
std::string title = line.substr(pos + prefix_title.length());
|
||||
@@ -54,14 +52,17 @@ namespace gsr {
|
||||
}
|
||||
|
||||
std::string get_current_kwin_window_title() {
|
||||
std::lock_guard<std::mutex> lock(active_window_mutex);
|
||||
return active_kwin_window.title;
|
||||
}
|
||||
|
||||
bool get_current_kwin_window_fullscreen() {
|
||||
std::lock_guard<std::mutex> lock(active_window_mutex);
|
||||
return active_kwin_window.fullscreen;
|
||||
}
|
||||
|
||||
std::string get_current_kwin_window_monitor_name() {
|
||||
std::lock_guard<std::mutex> lock(active_window_mutex);
|
||||
return active_kwin_window.monitorName;
|
||||
}
|
||||
|
||||
|
||||
470
src/Overlay.cpp
470
src/Overlay.cpp
@@ -264,6 +264,42 @@ namespace gsr {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool are_all_audio_tracks_available_to_capture(const std::vector<AudioTrack> &audio_tracks) {
|
||||
const auto audio_devices = get_audio_devices();
|
||||
for(const AudioTrack &audio_track : audio_tracks) {
|
||||
for(const std::string &audio_input : audio_track.audio_inputs) {
|
||||
std::string_view audio_track_name(audio_input.c_str());
|
||||
const bool is_app_audio = starts_with(audio_track_name, "app:");
|
||||
if(is_app_audio)
|
||||
continue;
|
||||
|
||||
if(starts_with(audio_track_name, "device:"))
|
||||
audio_track_name.remove_prefix(7);
|
||||
|
||||
auto it = std::find_if(audio_devices.begin(), audio_devices.end(), [&](const auto &audio_device) {
|
||||
return audio_device.name == audio_track_name;
|
||||
});
|
||||
if(it == audio_devices.end()) {
|
||||
//fprintf(stderr, "Audio not ready\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool is_webcam_available_to_capture(const RecordOptions &record_options) {
|
||||
if(record_options.webcam_source.empty())
|
||||
return true;
|
||||
|
||||
const auto cameras = get_v4l2_devices();
|
||||
for(const GsrCamera &camera : cameras) {
|
||||
if(camera.path == record_options.webcam_source)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Note that this doesn't work in the flatpak right now because of this flatpak bug:
|
||||
// https://github.com/flatpak/flatpak/issues/6486
|
||||
static bool is_hyprland_waybar_running_as_dock() {
|
||||
@@ -466,6 +502,17 @@ namespace gsr {
|
||||
}
|
||||
}
|
||||
|
||||
static pid_t launch_gsr_game_tracker(int *stdout_fd) {
|
||||
const bool is_flatpak = getenv("FLATPAK_ID") != nullptr;
|
||||
if(is_flatpak) {
|
||||
const char *args[] = { "flatpak-spawn", "--host", "--", "/var/lib/flatpak/app/com.dec05eba.gpu_screen_recorder/current/active/files/bin/gsr-game-tracker", NULL };
|
||||
return exec_program(args, stdout_fd, false);
|
||||
} else {
|
||||
const char *args[] = { "gsr-game-tracker", NULL };
|
||||
return exec_program(args, stdout_fd, false);
|
||||
}
|
||||
}
|
||||
|
||||
Overlay::Overlay(std::string resources_path, GsrInfo gsr_info, SupportedCaptureOptions capture_options, egl_functions egl_funcs) :
|
||||
resources_path(std::move(resources_path)),
|
||||
gsr_info(std::move(gsr_info)),
|
||||
@@ -487,10 +534,10 @@ namespace gsr {
|
||||
gsr_icon_path = this->resources_path + "images/gpu_screen_recorder_logo.png";
|
||||
|
||||
key_bindings[0].key_event.code = mgl::Keyboard::Escape;
|
||||
key_bindings[0].key_event.alt = false;
|
||||
key_bindings[0].key_event.control = false;
|
||||
key_bindings[0].key_event.shift = false;
|
||||
key_bindings[0].key_event.system = false;
|
||||
key_bindings[0].key_event.key_states.alt = false;
|
||||
key_bindings[0].key_event.key_states.control = false;
|
||||
key_bindings[0].key_event.key_states.shift = false;
|
||||
key_bindings[0].key_event.key_states.system = false;
|
||||
key_bindings[0].callback = [this]() {
|
||||
page_stack.pop();
|
||||
};
|
||||
@@ -518,15 +565,15 @@ namespace gsr {
|
||||
global_hotkeys_js = register_joystick_hotkeys(this);
|
||||
|
||||
x11_dpy = XOpenDisplay(nullptr);
|
||||
if(x11_dpy)
|
||||
if(x11_dpy) {
|
||||
XKeysymToKeycode(x11_dpy, XK_F1); // If we dont call we will never get a MappingNotify
|
||||
else
|
||||
} else {
|
||||
fprintf(stderr, "Warning: XOpenDisplay failed to mapping notify\n");
|
||||
}
|
||||
|
||||
if(this->gsr_info.system_info.display_server == DisplayServer::X11) {
|
||||
cursor_tracker = std::make_unique<CursorTrackerX11>((Display*)mgl_get_context()->connection);
|
||||
supports_window_title = true;
|
||||
supports_window_fullscreen_state = true;
|
||||
} else if(this->gsr_info.system_info.display_server == DisplayServer::WAYLAND) {
|
||||
if(!this->gsr_info.gpu_info.card_path.empty())
|
||||
cursor_tracker = std::make_unique<CursorTrackerWayland>(this->gsr_info.gpu_info.card_path.c_str(), wayland_dpy);
|
||||
@@ -534,22 +581,22 @@ namespace gsr {
|
||||
if(!config.main_config.wayland_warning_shown) {
|
||||
config.main_config.wayland_warning_shown = true;
|
||||
save_config(config);
|
||||
show_notification(TR("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, 255, 255), mgl::Color(255, 0, 0), NotificationType::NOTICE, nullptr, NotificationLevel::ERROR);
|
||||
}
|
||||
|
||||
const std::string wm_name = get_window_manager_name(x11_dpy);
|
||||
|
||||
if (wm_name.find("Hyprland") != std::string::npos) {
|
||||
start_hyprland_listener_thread();
|
||||
supports_window_title = true;
|
||||
} else if (wm_name == "KWin") {
|
||||
start_kwin_helper_thread();
|
||||
supports_window_title = true;
|
||||
supports_window_fullscreen_state = true;
|
||||
show_notification(TR("Wayland doesn't support GPU Screen Recorder UI properly, things may not work as expected. Use X11 if you experience issues."), notification_error_timeout_seconds, mgl::Color(255, 255, 255), mgl::Color(255, 0, 0), NotificationType::NOTICE, nullptr, NotificationLevel::ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
update_led_indicator_after_settings_change();
|
||||
|
||||
gsr_game_tracker_process_id = launch_gsr_game_tracker(&gsr_game_tracker_process_output_fd);
|
||||
if(gsr_game_tracker_process_id > 0) {
|
||||
const int fdl = fcntl(gsr_game_tracker_process_output_fd, F_GETFL);
|
||||
fcntl(gsr_game_tracker_process_output_fd, F_SETFL, fdl | O_NONBLOCK);
|
||||
gsr_game_tracker_process_output_file = fdopen(gsr_game_tracker_process_output_fd, "r");
|
||||
if(gsr_game_tracker_process_output_file)
|
||||
gsr_game_tracker_process_output_fd = -1;
|
||||
} else {
|
||||
fprintf(stderr, "Warning: failed to launch gsr-game-tracker. The feature to start replay when a game starts will not work\n");
|
||||
}
|
||||
}
|
||||
|
||||
Overlay::~Overlay() {
|
||||
@@ -585,8 +632,19 @@ namespace gsr {
|
||||
gpu_screen_recorder_screenshot_process = -1;
|
||||
}
|
||||
|
||||
if(gsr_game_tracker_process_id > 0) {
|
||||
kill(gsr_game_tracker_process_id, SIGINT);
|
||||
int status;
|
||||
if(waitpid(gsr_game_tracker_process_id, &status, 0) == -1) {
|
||||
perror("waitpid failed");
|
||||
/* Ignore... */
|
||||
}
|
||||
gsr_game_tracker_process_id = -1;
|
||||
}
|
||||
|
||||
led_indicator.reset();
|
||||
|
||||
close_gsr_game_tracker_output();
|
||||
close_gpu_screen_recorder_output();
|
||||
deinit_color_theme();
|
||||
|
||||
@@ -647,6 +705,18 @@ namespace gsr {
|
||||
}
|
||||
}
|
||||
|
||||
void Overlay::close_gsr_game_tracker_output() {
|
||||
if(gsr_game_tracker_process_output_file) {
|
||||
fclose(gsr_game_tracker_process_output_file);
|
||||
gsr_game_tracker_process_output_file = nullptr;
|
||||
}
|
||||
|
||||
if(gsr_game_tracker_process_output_fd > 0) {
|
||||
close(gsr_game_tracker_process_output_fd);
|
||||
gsr_game_tracker_process_output_fd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
void Overlay::close_gpu_screen_recorder_output() {
|
||||
if(gpu_screen_recorder_process_output_file) {
|
||||
fclose(gpu_screen_recorder_process_output_file);
|
||||
@@ -720,10 +790,10 @@ namespace gsr {
|
||||
}
|
||||
|
||||
static uint32_t key_event_to_bitmask(mgl::Event::KeyEvent key_event) {
|
||||
return ((uint32_t)key_event.alt << (uint32_t)0)
|
||||
| ((uint32_t)key_event.control << (uint32_t)1)
|
||||
| ((uint32_t)key_event.shift << (uint32_t)2)
|
||||
| ((uint32_t)key_event.system << (uint32_t)3);
|
||||
return ((uint32_t)key_event.key_states.alt << (uint32_t)0)
|
||||
| ((uint32_t)key_event.key_states.control << (uint32_t)1)
|
||||
| ((uint32_t)key_event.key_states.shift << (uint32_t)2)
|
||||
| ((uint32_t)key_event.key_states.system << (uint32_t)3);
|
||||
}
|
||||
|
||||
void Overlay::process_key_bindings(mgl::Event &event) {
|
||||
@@ -743,10 +813,13 @@ namespace gsr {
|
||||
|
||||
bool mapping_updated = false;
|
||||
while(XPending(x11_dpy)) {
|
||||
XNextEvent(x11_dpy, &x11_mapping_xev);
|
||||
if(x11_mapping_xev.type == MappingNotify) {
|
||||
XRefreshKeyboardMapping(&x11_mapping_xev.xmapping);
|
||||
mapping_updated = true;
|
||||
XNextEvent(x11_dpy, &x11_xev);
|
||||
switch(x11_xev.type) {
|
||||
case MappingNotify: {
|
||||
XRefreshKeyboardMapping(&x11_xev.xmapping);
|
||||
mapping_updated = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -783,6 +856,7 @@ namespace gsr {
|
||||
}
|
||||
|
||||
handle_keyboard_mapping_event();
|
||||
|
||||
region_selector.poll_events();
|
||||
if(region_selector.take_canceled()) {
|
||||
on_region_selected = nullptr;
|
||||
@@ -848,11 +922,14 @@ namespace gsr {
|
||||
}
|
||||
|
||||
update_notification_process_status();
|
||||
process_gsr_game_tracker_output();
|
||||
process_gsr_output();
|
||||
update_gsr_process_status();
|
||||
update_gsr_screenshot_process_status();
|
||||
replay_status_update_status();
|
||||
|
||||
update_gsr_game_tracker_replay_status();
|
||||
|
||||
if(hide_ui) {
|
||||
hide_ui = false;
|
||||
hide();
|
||||
@@ -1184,15 +1261,15 @@ namespace gsr {
|
||||
draw();
|
||||
}
|
||||
|
||||
void Overlay::recreate_global_hotkeys(const char *hotkey_option) {
|
||||
void Overlay::recreate_global_hotkeys(std::string_view hotkey_option) {
|
||||
global_hotkeys.reset();
|
||||
if(strcmp(hotkey_option, "enable_hotkeys") == 0)
|
||||
if(hotkey_option == "enable_hotkeys")
|
||||
global_hotkeys = register_linux_hotkeys(this, GlobalHotkeysLinux::GrabType::ALL);
|
||||
else if(strcmp(hotkey_option, "enable_hotkeys_virtual_devices") == 0)
|
||||
else if(hotkey_option == "enable_hotkeys_virtual_devices")
|
||||
global_hotkeys = register_linux_hotkeys(this, GlobalHotkeysLinux::GrabType::VIRTUAL);
|
||||
else if(strcmp(hotkey_option, "enable_hotkeys_no_grab") == 0)
|
||||
else if(hotkey_option == "enable_hotkeys_no_grab")
|
||||
global_hotkeys = register_linux_hotkeys(this, GlobalHotkeysLinux::GrabType::NO_GRAB);
|
||||
else if(strcmp(hotkey_option, "disable_hotkeys") == 0)
|
||||
else if(hotkey_option == "disable_hotkeys")
|
||||
global_hotkeys.reset();
|
||||
}
|
||||
|
||||
@@ -1208,7 +1285,7 @@ namespace gsr {
|
||||
void Overlay::recreate_frontpage_ui_components() {
|
||||
bg_screenshot_overlay = mgl::Rectangle(mgl::vec2f(get_theme().window_width, get_theme().window_height));
|
||||
top_bar_background = mgl::Rectangle(mgl::vec2f(get_theme().window_width, get_theme().window_height*0.06f).floor());
|
||||
top_bar_text = mgl::Text("GPU Screen Recorder", get_theme().top_bar_font);
|
||||
top_bar_text = mgl::Text("GPU Screen Recorder", get_theme().top_bar_font_desc.c_str());
|
||||
logo_sprite = mgl::Sprite(&get_theme().logo_texture);
|
||||
close_button_widget.set_size(mgl::vec2f(top_bar_background.get_size().y * 0.35f, top_bar_background.get_size().y * 0.35f).floor());
|
||||
|
||||
@@ -1240,7 +1317,7 @@ namespace gsr {
|
||||
List * main_buttons_list_ptr = main_buttons_list.get();
|
||||
main_buttons_list->set_spacing(0.0f);
|
||||
{
|
||||
auto button = std::make_unique<DropdownButton>(&get_theme().title_font, &get_theme().body_font, TR("Instant Replay"), TR("Off"), &get_theme().replay_button_texture,
|
||||
auto button = std::make_unique<DropdownButton>(get_theme().title_font_desc.c_str(), get_theme().body_font_desc.c_str(), TR("Instant Replay"), TR("Off"), &get_theme().replay_button_texture,
|
||||
mgl::vec2f(button_width, button_height));
|
||||
replay_dropdown_button_ptr = button.get();
|
||||
button->add_item(TR("Turn on"), "start", config.replay_config.start_stop_hotkey.to_string(false, false));
|
||||
@@ -1257,11 +1334,11 @@ namespace gsr {
|
||||
button->set_item_icon("settings", &get_theme().settings_extra_small_texture);
|
||||
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, supports_window_title, supports_window_fullscreen_state);
|
||||
auto replay_settings_page = std::make_unique<SettingsPage>(SettingsPage::Type::REPLAY, &gsr_info, config, &page_stack, supports_window_title);
|
||||
replay_settings_page->on_config_changed = [this]() {
|
||||
replay_startup_mode = replay_startup_string_to_type(config.replay_config.turn_on_replay_automatically_mode.c_str());
|
||||
if(recording_status == RecordingStatus::REPLAY)
|
||||
show_notification(TR("Replay settings have been modified.\nYou may need to restart replay to apply the changes."), notification_timeout_seconds, mgl::Color(255, 255, 255), get_color_theme().tint_color, NotificationType::REPLAY);
|
||||
show_notification(TR("Replay settings have been modified. You may need to restart replay to apply the changes."), notification_timeout_seconds, mgl::Color(255, 255, 255), get_color_theme().tint_color, NotificationType::REPLAY);
|
||||
};
|
||||
page_stack.push(std::move(replay_settings_page));
|
||||
} else if(id == "save") {
|
||||
@@ -1271,7 +1348,7 @@ namespace gsr {
|
||||
} else if(id == "save_10_min") {
|
||||
on_press_save_replay_10_min_replay();
|
||||
} else if(id == "start") {
|
||||
on_press_start_replay(false, false);
|
||||
on_press_start_replay(false, false, true);
|
||||
}
|
||||
};
|
||||
button->set_item_enabled("save", false);
|
||||
@@ -1280,7 +1357,7 @@ namespace gsr {
|
||||
main_buttons_list->add_widget(std::move(button));
|
||||
}
|
||||
{
|
||||
auto button = std::make_unique<DropdownButton>(&get_theme().title_font, &get_theme().body_font, TR("Record"), TR("Not recording"), &get_theme().record_button_texture,
|
||||
auto button = std::make_unique<DropdownButton>(get_theme().title_font_desc.c_str(), get_theme().body_font_desc.c_str(), TR("Record"), TR("Not recording"), &get_theme().record_button_texture,
|
||||
mgl::vec2f(button_width, button_height));
|
||||
record_dropdown_button_ptr = button.get();
|
||||
button->add_item(TR("Start"), "start", config.record_config.start_stop_hotkey.to_string(false, false));
|
||||
@@ -1291,10 +1368,10 @@ namespace gsr {
|
||||
button->set_item_icon("settings", &get_theme().settings_extra_small_texture);
|
||||
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, supports_window_title, supports_window_fullscreen_state);
|
||||
auto record_settings_page = std::make_unique<SettingsPage>(SettingsPage::Type::RECORD, &gsr_info, config, &page_stack, supports_window_title);
|
||||
record_settings_page->on_config_changed = [this]() {
|
||||
if(recording_status == RecordingStatus::RECORD)
|
||||
show_notification(TR("Recording settings have been modified.\nYou may need to restart recording to apply the changes."), notification_timeout_seconds, mgl::Color(255, 255, 255), get_color_theme().tint_color, NotificationType::RECORD);
|
||||
show_notification(TR("Recording settings have been modified. You may need to restart recording to apply the changes."), notification_timeout_seconds, mgl::Color(255, 255, 255), get_color_theme().tint_color, NotificationType::RECORD);
|
||||
|
||||
update_led_indicator_after_settings_change();
|
||||
};
|
||||
@@ -1309,7 +1386,7 @@ namespace gsr {
|
||||
main_buttons_list->add_widget(std::move(button));
|
||||
}
|
||||
{
|
||||
auto button = std::make_unique<DropdownButton>(&get_theme().title_font, &get_theme().body_font, TR("Livestream"), TR("Not streaming"), &get_theme().stream_button_texture,
|
||||
auto button = std::make_unique<DropdownButton>(get_theme().title_font_desc.c_str(), get_theme().body_font_desc.c_str(), TR("Livestream"), TR("Not streaming"), &get_theme().stream_button_texture,
|
||||
mgl::vec2f(button_width, button_height));
|
||||
stream_dropdown_button_ptr = button.get();
|
||||
button->add_item(TR("Start"), "start", config.streaming_config.start_stop_hotkey.to_string(false, false));
|
||||
@@ -1318,10 +1395,10 @@ namespace gsr {
|
||||
button->set_item_icon("settings", &get_theme().settings_extra_small_texture);
|
||||
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, supports_window_title, supports_window_fullscreen_state);
|
||||
auto stream_settings_page = std::make_unique<SettingsPage>(SettingsPage::Type::STREAM, &gsr_info, config, &page_stack, supports_window_title);
|
||||
stream_settings_page->on_config_changed = [this]() {
|
||||
if(recording_status == RecordingStatus::STREAM)
|
||||
show_notification(TR("Streaming settings have been modified.\nYou may need to restart streaming to apply the changes."), notification_timeout_seconds, mgl::Color(255, 255, 255), get_color_theme().tint_color, NotificationType::STREAM);
|
||||
show_notification(TR("Streaming settings have been modified. You may need to restart streaming to apply the changes."), notification_timeout_seconds, mgl::Color(255, 255, 255), get_color_theme().tint_color, NotificationType::STREAM);
|
||||
|
||||
update_led_indicator_after_settings_change();
|
||||
};
|
||||
@@ -1340,7 +1417,7 @@ namespace gsr {
|
||||
{
|
||||
const mgl::vec2f main_buttons_size = main_buttons_list_ptr->get_size();
|
||||
const int settings_button_size = main_buttons_size.y * 0.33f;
|
||||
auto button = std::make_unique<Button>(&get_theme().title_font, "", mgl::vec2f(settings_button_size, settings_button_size), mgl::Color(0, 0, 0, 180));
|
||||
auto button = std::make_unique<Button>(get_theme().title_font_desc.c_str(), "", mgl::vec2f(settings_button_size, settings_button_size), mgl::Color(0, 0, 0, 180));
|
||||
button->set_position((main_buttons_list_ptr->get_position() + main_buttons_size - mgl::vec2f(0.0f, settings_button_size) + mgl::vec2f(settings_button_size * 0.333f, 0.0f)).floor());
|
||||
button->set_bg_hover_color(mgl::Color(0, 0, 0, 255));
|
||||
button->set_icon(&get_theme().settings_small_texture);
|
||||
@@ -1352,31 +1429,41 @@ namespace gsr {
|
||||
if(exit_status == 0)
|
||||
return;
|
||||
|
||||
if(exit_status == 127) {
|
||||
if(enable)
|
||||
show_notification(TR("Failed to add GPU Screen Recorder to system startup.\nThis option only works on systems that use systemd.\nYou have to manually add \"gsr-ui\" to system startup on systems that uses another init system."), 7.0, mgl::Color(255, 255, 255), mgl::Color(255, 0, 0), NotificationType::NOTICE, nullptr, NotificationLevel::ERROR);
|
||||
} else {
|
||||
if(enable)
|
||||
show_notification(TR("Failed to add GPU Screen Recorder to system startup"), notification_timeout_seconds, mgl::Color(255, 255, 255), mgl::Color(255, 0, 0), NotificationType::NOTICE, nullptr, NotificationLevel::ERROR);
|
||||
else
|
||||
show_notification(TR("Failed to remove GPU Screen Recorder from system startup"), notification_timeout_seconds, mgl::Color(255, 255, 255), mgl::Color(255, 0, 0), NotificationType::NOTICE, nullptr, NotificationLevel::ERROR);
|
||||
if(exit_status == 67) {
|
||||
const bool is_flatpak = getenv("FLATPAK_ID") != nullptr;
|
||||
const char *startup_command = is_flatpak ? "flatpak run com.dec05eba.gpu_screen_recorder gsr-ui" : "gsr-ui launch-daemon";
|
||||
show_notification(
|
||||
TRF("To enable autorun: install and configure 'dex' (recommended), or manually add '%s' to your desktop autostart entries.", startup_command).c_str(),
|
||||
10.0,
|
||||
mgl::Color(255, 255, 255),
|
||||
mgl::Color(255, 0, 0),
|
||||
NotificationType::NOTICE,
|
||||
nullptr,
|
||||
NotificationLevel::ERROR
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if(enable)
|
||||
show_notification(TR("Failed to add GPU Screen Recorder to system startup"), notification_timeout_seconds, mgl::Color(255, 255, 255), mgl::Color(255, 0, 0), NotificationType::NOTICE, nullptr, NotificationLevel::ERROR);
|
||||
else
|
||||
show_notification(TR("Failed to remove GPU Screen Recorder from system startup"), notification_timeout_seconds, mgl::Color(255, 255, 255), mgl::Color(255, 0, 0), NotificationType::NOTICE, nullptr, NotificationLevel::ERROR);
|
||||
};
|
||||
|
||||
settings_page->on_click_exit_program_button = [this](const char *reason) {
|
||||
settings_page->on_click_exit_program_button = [this](std::string_view reason) {
|
||||
do_exit = true;
|
||||
exit_reason = reason;
|
||||
};
|
||||
|
||||
settings_page->on_keyboard_hotkey_changed = [this](const char *hotkey_option) {
|
||||
settings_page->on_keyboard_hotkey_changed = [this](std::string_view hotkey_option) {
|
||||
recreate_global_hotkeys(hotkey_option);
|
||||
};
|
||||
|
||||
settings_page->on_joystick_hotkey_changed = [this](const char *hotkey_option) {
|
||||
settings_page->on_joystick_hotkey_changed = [this](std::string_view hotkey_option) {
|
||||
global_hotkeys_js.reset();
|
||||
if(strcmp(hotkey_option, "enable_hotkeys") == 0)
|
||||
if(hotkey_option == "enable_hotkeys")
|
||||
global_hotkeys_js = register_joystick_hotkeys(this);
|
||||
else if(strcmp(hotkey_option, "disable_hotkeys") == 0)
|
||||
else if(hotkey_option == "disable_hotkeys")
|
||||
global_hotkeys_js.reset();
|
||||
};
|
||||
|
||||
@@ -1403,7 +1490,7 @@ namespace gsr {
|
||||
{
|
||||
const mgl::vec2f main_buttons_size = main_buttons_list_ptr->get_size();
|
||||
const int settings_button_size = main_buttons_size.y * 0.33f;
|
||||
auto button = std::make_unique<Button>(&get_theme().title_font, "", mgl::vec2f(settings_button_size, settings_button_size), mgl::Color(0, 0, 0, 180));
|
||||
auto button = std::make_unique<Button>(get_theme().title_font_desc.c_str(), "", mgl::vec2f(settings_button_size, settings_button_size), mgl::Color(0, 0, 0, 180));
|
||||
button->set_position((main_buttons_list_ptr->get_position() + main_buttons_size - mgl::vec2f(0.0f, settings_button_size*2) + mgl::vec2f(settings_button_size * 0.333f, 0.0f)).floor());
|
||||
button->set_bg_hover_color(mgl::Color(0, 0, 0, 255));
|
||||
button->set_icon(&get_theme().screenshot_texture);
|
||||
@@ -1611,7 +1698,7 @@ namespace gsr {
|
||||
}
|
||||
|
||||
void Overlay::toggle_replay() {
|
||||
on_press_start_replay(false, false);
|
||||
on_press_start_replay(false, false, true);
|
||||
}
|
||||
|
||||
void Overlay::save_replay() {
|
||||
@@ -1856,10 +1943,6 @@ namespace gsr {
|
||||
exit_reason = "back-to-old-ui";
|
||||
else
|
||||
exit_reason = "exit";
|
||||
|
||||
const char *args[] = { "systemctl", "disable", "--user", "gpu-screen-recorder-ui", nullptr };
|
||||
std::string stdout_str;
|
||||
exec_program_on_host_get_stdout(args, stdout_str);
|
||||
exit();
|
||||
}
|
||||
|
||||
@@ -2003,27 +2086,35 @@ namespace gsr {
|
||||
return ClipboardFile::FileType::PNG;
|
||||
}
|
||||
|
||||
// Returns an empty string on failure
|
||||
static std::string gamescope_get_game_name() {
|
||||
std::string focused_window_name;
|
||||
Display *gamescope_dpy = XOpenDisplay(":2"); // This is assuming gamescope is :2, which may not always be true
|
||||
if(!gamescope_dpy)
|
||||
return focused_window_name;
|
||||
|
||||
if(get_window_manager_name(gamescope_dpy) == "steamcompmgr")
|
||||
focused_window_name = get_focused_window_name(gamescope_dpy, WindowCaptureType::FOCUSED, false);
|
||||
|
||||
XCloseDisplay(gamescope_dpy);
|
||||
return focused_window_name;
|
||||
}
|
||||
|
||||
void Overlay::save_video_in_current_game_directory(std::string &video_filepath, NotificationType notification_type) {
|
||||
mgl_context *context = mgl_get_context();
|
||||
Display *display = (Display*)context->connection;
|
||||
const std::string video_filename = filepath_get_filename(video_filepath.c_str());
|
||||
|
||||
const std::string wm_name = get_window_manager_name(display);
|
||||
const bool is_hyprland = wm_name.find("Hyprland") != std::string::npos;
|
||||
const bool is_kwin_wayland = wm_name == "KWin" && gsr_info.system_info.display_server == DisplayServer::WAYLAND;
|
||||
const Window gsr_ui_window = window ? (Window)window->get_system_handle() : None;
|
||||
// Checking for gamescope window name is not needed on x11 because on x11 gamescope uses the host x11 so applications
|
||||
// running inside gamescope appears in the hosts x11 server
|
||||
std::string focused_window_name = gsr_info.system_info.display_server != DisplayServer::X11 ? gamescope_get_game_name() : "";
|
||||
|
||||
std::string focused_window_name;
|
||||
if (is_hyprland) {
|
||||
focused_window_name = get_current_hyprland_window_title();
|
||||
} else if (is_kwin_wayland) {
|
||||
focused_window_name = get_current_kwin_window_title();
|
||||
} else {
|
||||
const Window gsr_ui_window = window ? (Window)window->get_system_handle() : None;
|
||||
if(focused_window_name.empty())
|
||||
focused_window_name = get_window_name_at_cursor_position(display, gsr_ui_window);
|
||||
|
||||
if(focused_window_name.empty())
|
||||
focused_window_name = get_focused_window_name(display, WindowCaptureType::FOCUSED, false);
|
||||
}
|
||||
if(focused_window_name.empty())
|
||||
focused_window_name = get_focused_window_name(display, WindowCaptureType::FOCUSED, false);
|
||||
|
||||
if(focused_window_name.empty())
|
||||
focused_window_name = "Game";
|
||||
@@ -2048,7 +2139,7 @@ namespace gsr {
|
||||
return;
|
||||
|
||||
const std::string duration_str = to_duration_string(recording_duration_clock.get_elapsed_time_seconds() - paused_total_time_seconds - (paused ? paused_clock.get_elapsed_time_seconds() : 0.0));
|
||||
snprintf(msg, sizeof(msg), TR("Saved a %s recording of %s\nto \"%s\""),
|
||||
snprintf(msg, sizeof(msg), TR("Saved a %s recording of %s to \"%s\""),
|
||||
duration_str.c_str(),
|
||||
capture_target_get_notification_name(recording_capture_target.c_str(), true).c_str(), focused_window_name.c_str());
|
||||
capture_target = recording_capture_target.c_str();
|
||||
@@ -2059,7 +2150,7 @@ namespace gsr {
|
||||
return;
|
||||
|
||||
const std::string duration_str = to_duration_string(get_time_passed_in_replay_buffer_seconds());
|
||||
snprintf(msg, sizeof(msg), TR("Saved a %s replay of %s\nto \"%s\""),
|
||||
snprintf(msg, sizeof(msg), TR("Saved a %s replay of %s to \"%s\""),
|
||||
duration_str.c_str(),
|
||||
capture_target_get_notification_name(recording_capture_target.c_str(), true).c_str(), focused_window_name.c_str());
|
||||
capture_target = recording_capture_target.c_str();
|
||||
@@ -2069,7 +2160,7 @@ namespace gsr {
|
||||
if(!config.screenshot_config.show_notifications)
|
||||
return;
|
||||
|
||||
snprintf(msg, sizeof(msg), TR("Saved a screenshot of %s\nto \"%s\""),
|
||||
snprintf(msg, sizeof(msg), TR("Saved a screenshot of %s to \"%s\""),
|
||||
capture_target_get_notification_name(screenshot_capture_target.c_str(), true).c_str(), focused_window_name.c_str());
|
||||
capture_target = screenshot_capture_target.c_str();
|
||||
break;
|
||||
@@ -2111,6 +2202,26 @@ namespace gsr {
|
||||
led_indicator->blink();
|
||||
}
|
||||
|
||||
void Overlay::process_gsr_game_tracker_output() {
|
||||
char buffer[1024];
|
||||
if(gsr_game_tracker_process_output_file) {
|
||||
char *line = fgets(buffer, sizeof(buffer), gsr_game_tracker_process_output_file);
|
||||
if(!line || line[0] == '\0')
|
||||
return;
|
||||
|
||||
if(strncmp(line, "Game launched", 13) == 0) {
|
||||
game_running = true;
|
||||
game_replay_action = GameReplayAction::START;
|
||||
} else if(strncmp(line, "Game exited", 11) == 0) {
|
||||
game_running = false;
|
||||
game_replay_action = GameReplayAction::STOP;
|
||||
}
|
||||
|
||||
} else if(gsr_game_tracker_process_output_fd > 0) {
|
||||
read(gsr_game_tracker_process_output_fd, buffer, sizeof(buffer));
|
||||
}
|
||||
}
|
||||
|
||||
void Overlay::process_gsr_output() {
|
||||
if(replay_save_show_notification && replay_save_clock.get_elapsed_time_seconds() >= replay_saving_notification_timeout_seconds) {
|
||||
replay_save_show_notification = false;
|
||||
@@ -2118,8 +2229,8 @@ namespace gsr {
|
||||
show_notification(TR("Saving replay, this might take some time"), notification_timeout_seconds, mgl::Color(255, 255, 255), get_color_theme().tint_color, NotificationType::REPLAY);
|
||||
}
|
||||
|
||||
char buffer[1024];
|
||||
if(gpu_screen_recorder_process_output_file) {
|
||||
char buffer[1024];
|
||||
char *line = fgets(buffer, sizeof(buffer), gpu_screen_recorder_process_output_file);
|
||||
if(!line || line[0] == '\0')
|
||||
return;
|
||||
@@ -2152,7 +2263,6 @@ namespace gsr {
|
||||
break;
|
||||
}
|
||||
} else if(gpu_screen_recorder_process_output_fd > 0) {
|
||||
char buffer[1024];
|
||||
read(gpu_screen_recorder_process_output_fd, buffer, sizeof(buffer));
|
||||
}
|
||||
}
|
||||
@@ -2160,15 +2270,15 @@ namespace gsr {
|
||||
void Overlay::on_gsr_process_error(int exit_code, NotificationType notification_type) {
|
||||
fprintf(stderr, "Warning: gpu-screen-recorder (%d) exited with exit status %d\n", (int)gpu_screen_recorder_process, exit_code);
|
||||
if(exit_code == 50) {
|
||||
show_notification(TR("Desktop portal capture failed.\nEither you canceled the desktop portal or your Wayland compositor doesn't support desktop portal capture\nor it's incorrectly setup on your system."), notification_error_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), notification_type, nullptr, NotificationLevel::ERROR);
|
||||
show_notification(TR("Desktop portal capture failed. Either you canceled the desktop portal or your Wayland compositor doesn't support desktop portal capture or it's incorrectly setup on your system."), notification_error_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), notification_type, nullptr, NotificationLevel::ERROR);
|
||||
} else if(exit_code == 51) {
|
||||
show_notification(TR("Monitor capture failed.\nThe monitor you are trying to capture is invalid.\nPlease validate your capture settings."), notification_error_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), notification_type, nullptr, NotificationLevel::ERROR);
|
||||
show_notification(TR("Monitor capture failed. The monitor you are trying to capture is invalid. Please validate your capture settings."), notification_error_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), notification_type, nullptr, NotificationLevel::ERROR);
|
||||
} else if(exit_code == 52) {
|
||||
show_notification(TR("Capture failed. Neither H264, HEVC nor AV1 video codecs are supported\non your system or you are trying to capture at a resolution higher than your\nsystem supports for each video codec."), 10.0, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), notification_type, nullptr, NotificationLevel::ERROR);
|
||||
show_notification(TR("Capture failed. Neither H264, HEVC nor AV1 video codecs are supported on your system or you are trying to capture at a resolution higher than your system supports for each video codec."), 10.0, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), notification_type, nullptr, NotificationLevel::ERROR);
|
||||
} else if(exit_code == 53) {
|
||||
show_notification(TR("Capture failed. Your system doesn't support the resolution you are trying to\nrecord at with the video codec you have chosen.\nChange capture resolution or video codec and try again.\nNote: AV1 supports the highest resolution, then HEVC and then H264."), 10.0, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), notification_type, nullptr, NotificationLevel::ERROR);
|
||||
show_notification(TR("Capture failed. Your system doesn't support the resolution you are trying to record at with the video codec you have chosen. Change capture resolution or video codec and try again. Note: AV1 supports the highest resolution, then HEVC and then H264."), 10.0, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), notification_type, nullptr, NotificationLevel::ERROR);
|
||||
} else if(exit_code == 54) {
|
||||
show_notification(TR("Capture failed. Your system doesn't support the video codec you have chosen.\nChange video codec and try again."), notification_error_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), notification_type, nullptr, NotificationLevel::ERROR);
|
||||
show_notification(TR("Capture failed. Your system doesn't support the video codec you have chosen. Change video codec and try again."), notification_error_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), notification_type, nullptr, NotificationLevel::ERROR);
|
||||
} else if(exit_code == 60) {
|
||||
show_notification(TR("Stopped capture because the user canceled the desktop portal"), notification_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), notification_type, nullptr, NotificationLevel::ERROR);
|
||||
} else {
|
||||
@@ -2256,6 +2366,7 @@ namespace gsr {
|
||||
|
||||
gpu_screen_recorder_process = -1;
|
||||
recording_status = RecordingStatus::NONE;
|
||||
replay_launched_manually = true;
|
||||
}
|
||||
|
||||
void Overlay::update_gsr_screenshot_process_status() {
|
||||
@@ -2302,119 +2413,55 @@ namespace gsr {
|
||||
gpu_screen_recorder_screenshot_process = -1;
|
||||
}
|
||||
|
||||
static bool are_all_audio_tracks_available_to_capture(const std::vector<AudioTrack> &audio_tracks) {
|
||||
const auto audio_devices = get_audio_devices();
|
||||
for(const AudioTrack &audio_track : audio_tracks) {
|
||||
for(const std::string &audio_input : audio_track.audio_inputs) {
|
||||
std::string_view audio_track_name(audio_input.c_str());
|
||||
const bool is_app_audio = starts_with(audio_track_name, "app:");
|
||||
if(is_app_audio)
|
||||
continue;
|
||||
|
||||
if(starts_with(audio_track_name, "device:"))
|
||||
audio_track_name.remove_prefix(7);
|
||||
|
||||
auto it = std::find_if(audio_devices.begin(), audio_devices.end(), [&](const auto &audio_device) {
|
||||
return audio_device.name == audio_track_name;
|
||||
});
|
||||
if(it == audio_devices.end()) {
|
||||
//fprintf(stderr, "Audio not ready\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool is_webcam_available_to_capture(const RecordOptions &record_options) {
|
||||
if(record_options.webcam_source.empty())
|
||||
return true;
|
||||
|
||||
const auto cameras = get_v4l2_devices();
|
||||
for(const GsrCamera &camera : cameras) {
|
||||
if(camera.path == record_options.webcam_source)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Overlay::replay_status_update_status() {
|
||||
if(replay_status_update_clock.get_elapsed_time_seconds() < replay_status_update_check_timeout_seconds)
|
||||
return;
|
||||
|
||||
replay_status_update_clock.restart();
|
||||
update_focused_fullscreen_status();
|
||||
update_power_supply_status();
|
||||
update_system_startup_status();
|
||||
}
|
||||
|
||||
void Overlay::update_focused_fullscreen_status() {
|
||||
if(replay_startup_mode != ReplayStartupMode::TURN_ON_AT_FULLSCREEN)
|
||||
return;
|
||||
|
||||
mgl_context *context = mgl_get_context();
|
||||
Display *display = (Display*)context->connection;
|
||||
|
||||
const std::string wm_name = get_window_manager_name(display);
|
||||
const bool is_kwin_wayland = wm_name == "KWin" && gsr_info.system_info.display_server == DisplayServer::WAYLAND;
|
||||
|
||||
const bool prev_focused_window_is_fullscreen = focused_window_is_fullscreen;
|
||||
Window focused_window = None;
|
||||
|
||||
if (is_kwin_wayland) {
|
||||
focused_window_is_fullscreen = get_current_kwin_window_fullscreen();
|
||||
} else {
|
||||
focused_window = get_focused_window(display, WindowCaptureType::FOCUSED, false);
|
||||
if(window && focused_window == (Window)window->get_system_handle())
|
||||
return;
|
||||
|
||||
focused_window_is_fullscreen = focused_window != 0 && window_is_fullscreen(display, focused_window);
|
||||
}
|
||||
|
||||
if(focused_window_is_fullscreen != prev_focused_window_is_fullscreen) {
|
||||
std::string fullscreen_window_monitor;
|
||||
if(is_kwin_wayland) {
|
||||
fullscreen_window_monitor = get_current_kwin_window_monitor_name();
|
||||
} else {
|
||||
auto window_monitor = get_monitor_by_window_center(display, focused_window);
|
||||
if(window_monitor.has_value())
|
||||
fullscreen_window_monitor = std::move(window_monitor->name);
|
||||
else
|
||||
fullscreen_window_monitor.clear();
|
||||
}
|
||||
|
||||
if(recording_status == RecordingStatus::NONE && focused_window_is_fullscreen) {
|
||||
if(are_all_audio_tracks_available_to_capture(config.replay_config.record_options.audio_tracks_list) && is_webcam_available_to_capture(config.replay_config.record_options))
|
||||
on_press_start_replay(false, false, fullscreen_window_monitor);
|
||||
} else if(recording_status == RecordingStatus::REPLAY && !focused_window_is_fullscreen) {
|
||||
on_press_start_replay(true, false, fullscreen_window_monitor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Instead of checking power supply status periodically listen to power supply event
|
||||
void Overlay::update_power_supply_status() {
|
||||
if(replay_startup_mode != ReplayStartupMode::TURN_ON_AT_POWER_SUPPLY_CONNECTED)
|
||||
if(replay_startup_mode == ReplayStartupMode::DONT_TURN_ON_AUTOMATICALLY)
|
||||
return;
|
||||
|
||||
const bool prev_power_supply_status = power_supply_connected;
|
||||
power_supply_connected = power_supply_online_filepath.empty() || power_supply_is_connected(power_supply_online_filepath.c_str());
|
||||
if(power_supply_connected != prev_power_supply_status) {
|
||||
if(recording_status == RecordingStatus::NONE && power_supply_connected) {
|
||||
if(are_all_audio_tracks_available_to_capture(config.replay_config.record_options.audio_tracks_list) && is_webcam_available_to_capture(config.replay_config.record_options))
|
||||
on_press_start_replay(false, false);
|
||||
} else if(recording_status == RecordingStatus::REPLAY && !power_supply_connected) {
|
||||
on_press_start_replay(false, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Overlay::update_system_startup_status() {
|
||||
if(replay_startup_mode != ReplayStartupMode::TURN_ON_AT_SYSTEM_STARTUP || recording_status != RecordingStatus::NONE || !try_replay_startup)
|
||||
if(replay_startup_mode != ReplayStartupMode::TURN_ON_AT_SYSTEM_STARTUP || !try_replay_startup)
|
||||
return;
|
||||
|
||||
if(are_all_audio_tracks_available_to_capture(config.replay_config.record_options.audio_tracks_list) && is_webcam_available_to_capture(config.replay_config.record_options))
|
||||
on_press_start_replay(true, false);
|
||||
const bool power_supply_allows_start = !config.replay_config.only_start_replay_if_power_supply_connected || power_supply_connected;
|
||||
const bool power_supply_disconnected = config.replay_config.only_start_replay_if_power_supply_connected && !power_supply_connected;
|
||||
|
||||
if(recording_status == RecordingStatus::NONE && power_supply_allows_start) {
|
||||
if(are_all_audio_tracks_available_to_capture(config.replay_config.record_options.audio_tracks_list) && is_webcam_available_to_capture(config.replay_config.record_options))
|
||||
on_press_start_replay(true, false);
|
||||
} else if(recording_status == RecordingStatus::REPLAY && power_supply_disconnected) {
|
||||
on_press_start_replay(false, false);
|
||||
}
|
||||
}
|
||||
|
||||
void Overlay::update_gsr_game_tracker_replay_status() {
|
||||
if(replay_startup_mode != ReplayStartupMode::TURN_ON_AT_GAME_LAUNCH)
|
||||
return;
|
||||
|
||||
const bool power_supply_allows_start = !config.replay_config.only_start_replay_if_power_supply_connected || power_supply_connected;
|
||||
const bool power_supply_disconnected = config.replay_config.only_start_replay_if_power_supply_connected && !power_supply_connected;
|
||||
|
||||
if(replay_launched_manually)
|
||||
game_replay_action = GameReplayAction::IDLE;
|
||||
|
||||
if(recording_status == RecordingStatus::NONE && game_replay_action == GameReplayAction::START && power_supply_allows_start) {
|
||||
on_press_start_replay(false, false);
|
||||
} else if(recording_status == RecordingStatus::REPLAY && (game_replay_action == GameReplayAction::STOP || power_supply_disconnected)) {
|
||||
on_press_start_replay(false, false);
|
||||
}
|
||||
|
||||
game_replay_action = GameReplayAction::IDLE;
|
||||
}
|
||||
|
||||
void Overlay::on_stop_recording(int exit_code, std::string &video_filepath) {
|
||||
@@ -2793,7 +2840,7 @@ namespace gsr {
|
||||
return container;
|
||||
}
|
||||
|
||||
static void choose_video_codec_and_container_with_fallback(const GsrInfo &gsr_info, const char **video_codec, const char **container, const char **encoder) {
|
||||
static void choose_video_codec_and_container_with_fallback(const GsrInfo &gsr_info, const RecordOptions &record_options, const char **video_codec, const char **container, const char **encoder) {
|
||||
*encoder = "gpu";
|
||||
if(strcmp(*video_codec, "h264_software") == 0) {
|
||||
*video_codec = "h264";
|
||||
@@ -2805,6 +2852,25 @@ namespace gsr {
|
||||
}
|
||||
}
|
||||
*container = change_container_if_codec_not_supported(*video_codec, *container);
|
||||
|
||||
if(record_options.enable_vulkan_video_encoding && strcmp(*encoder, "gpu") == 0) {
|
||||
if(strcmp(*video_codec, "auto") == 0)
|
||||
*video_codec = "h264_vulkan";
|
||||
else if(strcmp(*video_codec, "h264") == 0)
|
||||
*video_codec = "h264_vulkan";
|
||||
else if(strcmp(*video_codec, "hevc") == 0)
|
||||
*video_codec = "hevc_vulkan";
|
||||
else if(strcmp(*video_codec, "hevc_hdr") == 0)
|
||||
*video_codec = "hevc_hdr_vulkan";
|
||||
else if(strcmp(*video_codec, "hevc_10bit") == 0)
|
||||
*video_codec = "hevc_10bit_vulkan";
|
||||
else if(strcmp(*video_codec, "av1") == 0)
|
||||
*video_codec = "av1_vulkan";
|
||||
else if(strcmp(*video_codec, "av1_hdr") == 0)
|
||||
*video_codec = "av1_hdr_vulkan";
|
||||
else if(strcmp(*video_codec, "av1_10bit") == 0)
|
||||
*video_codec = "av1_10bit_vulkan";
|
||||
}
|
||||
}
|
||||
|
||||
static std::string get_framerate_mode_validate(const RecordOptions &record_options, const GsrInfo &gsr_info) {
|
||||
@@ -2878,7 +2944,7 @@ namespace gsr {
|
||||
return capture_source_arg;
|
||||
}
|
||||
|
||||
bool Overlay::on_press_start_replay(bool disable_notification, bool finished_selection, std::string monitor_to_capture) {
|
||||
bool Overlay::on_press_start_replay(bool disable_notification, bool finished_selection, bool launched_manually) {
|
||||
if(region_selector.is_started())
|
||||
return false;
|
||||
|
||||
@@ -2887,15 +2953,16 @@ namespace gsr {
|
||||
case RecordingStatus::REPLAY:
|
||||
break;
|
||||
case RecordingStatus::RECORD:
|
||||
show_notification(TR("Unable to start replay when recording.\nStop recording before starting replay."), notification_error_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), NotificationType::RECORD, nullptr, NotificationLevel::ERROR);
|
||||
show_notification(TR("Unable to start replay when recording. Stop recording before starting replay."), notification_error_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), NotificationType::RECORD, nullptr, NotificationLevel::ERROR);
|
||||
return false;
|
||||
case RecordingStatus::STREAM:
|
||||
show_notification(TR("Unable to start replay when streaming.\nStop streaming before starting replay."), notification_error_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), NotificationType::STREAM, nullptr, NotificationLevel::ERROR);
|
||||
show_notification(TR("Unable to start replay when streaming. Stop streaming before starting replay."), notification_error_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), NotificationType::STREAM, nullptr, NotificationLevel::ERROR);
|
||||
return false;
|
||||
}
|
||||
|
||||
update_upause_status();
|
||||
try_replay_startup = false;
|
||||
replay_launched_manually = launched_manually;
|
||||
|
||||
close_gpu_screen_recorder_output();
|
||||
|
||||
@@ -2911,6 +2978,7 @@ namespace gsr {
|
||||
recording_status = RecordingStatus::NONE;
|
||||
replay_save_duration_min = 0;
|
||||
update_ui_replay_stopped();
|
||||
replay_launched_manually = false;
|
||||
|
||||
if(led_indicator)
|
||||
led_indicator->set_led(false);
|
||||
@@ -2924,25 +2992,25 @@ namespace gsr {
|
||||
|
||||
if(config.replay_config.record_options.record_area_option == "region" && !finished_selection) {
|
||||
start_region_capture = true;
|
||||
on_region_selected = [disable_notification, this]() {
|
||||
on_press_start_replay(disable_notification, true);
|
||||
on_region_selected = [disable_notification, launched_manually, this]() {
|
||||
on_press_start_replay(disable_notification, true, launched_manually);
|
||||
};
|
||||
return false;
|
||||
}
|
||||
|
||||
if(config.replay_config.record_options.record_area_option == "window" && !finished_selection) {
|
||||
start_window_capture = true;
|
||||
on_region_selected = [disable_notification, this]() {
|
||||
on_press_start_replay(disable_notification, true);
|
||||
on_region_selected = [disable_notification, launched_manually, this]() {
|
||||
on_press_start_replay(disable_notification, true, launched_manually);
|
||||
};
|
||||
return false;
|
||||
}
|
||||
|
||||
const SupportedCaptureOptions capture_options = get_supported_capture_options(gsr_info);
|
||||
recording_capture_target = !monitor_to_capture.empty() ? monitor_to_capture : get_capture_target(config.replay_config.record_options.record_area_option, capture_options);
|
||||
recording_capture_target = get_capture_target(config.replay_config.record_options.record_area_option, capture_options);
|
||||
if(!validate_capture_target(config.replay_config.record_options.record_area_option, capture_options)) {
|
||||
char err_msg[256];
|
||||
snprintf(err_msg, sizeof(err_msg), TR("Failed to start replay, capture target \"%s\" is invalid.\nPlease change capture target in settings"), recording_capture_target.c_str());
|
||||
snprintf(err_msg, sizeof(err_msg), TR("Failed to start replay, capture target \"%s\" is invalid. Please change capture target in settings"), recording_capture_target.c_str());
|
||||
show_notification(err_msg, notification_error_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), NotificationType::REPLAY, nullptr, NotificationLevel::ERROR);
|
||||
return false;
|
||||
}
|
||||
@@ -2957,7 +3025,7 @@ namespace gsr {
|
||||
const char *container = config.replay_config.container.c_str();
|
||||
const char *video_codec = config.replay_config.record_options.video_codec.c_str();
|
||||
const char *encoder = "gpu";
|
||||
choose_video_codec_and_container_with_fallback(gsr_info, &video_codec, &container, &encoder);
|
||||
choose_video_codec_and_container_with_fallback(gsr_info, config.replay_config.record_options, &video_codec, &container, &encoder);
|
||||
|
||||
char size[64];
|
||||
size[0] = '\0';
|
||||
@@ -3083,7 +3151,7 @@ namespace gsr {
|
||||
replay_recording = true;
|
||||
kill(gpu_screen_recorder_process, SIGRTMIN);
|
||||
} else {
|
||||
show_notification(TR("Unable to start recording when replay is turned on.\nTurn off replay before starting recording."), notification_error_timeout_seconds, mgl::Color(255, 0, 0), get_color_theme().tint_color, NotificationType::REPLAY, nullptr, NotificationLevel::ERROR);
|
||||
show_notification(TR("Unable to start recording when replay is turned on. Turn off replay before starting recording."), notification_error_timeout_seconds, mgl::Color(255, 0, 0), get_color_theme().tint_color, NotificationType::REPLAY, nullptr, NotificationLevel::ERROR);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -3115,7 +3183,7 @@ namespace gsr {
|
||||
replay_recording = true;
|
||||
kill(gpu_screen_recorder_process, SIGRTMIN);
|
||||
} else {
|
||||
show_notification(TR("Unable to start recording when streaming.\nStop streaming before starting recording."), notification_error_timeout_seconds, mgl::Color(255, 0, 0), get_color_theme().tint_color, NotificationType::STREAM, nullptr, NotificationLevel::ERROR);
|
||||
show_notification(TR("Unable to start recording when streaming. Stop streaming before starting recording."), notification_error_timeout_seconds, mgl::Color(255, 0, 0), get_color_theme().tint_color, NotificationType::STREAM, nullptr, NotificationLevel::ERROR);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -3182,7 +3250,7 @@ namespace gsr {
|
||||
recording_capture_target = get_capture_target(record_area_option, capture_options);
|
||||
if(!validate_capture_target(record_area_option, capture_options)) {
|
||||
char err_msg[256];
|
||||
snprintf(err_msg, sizeof(err_msg), TR("Failed to start recording, capture target \"%s\" is invalid.\nPlease change capture target in settings"), recording_capture_target.c_str());
|
||||
snprintf(err_msg, sizeof(err_msg), TR("Failed to start recording, capture target \"%s\" is invalid. Please change capture target in settings"), recording_capture_target.c_str());
|
||||
show_notification(err_msg, notification_error_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), NotificationType::RECORD, nullptr, NotificationLevel::ERROR);
|
||||
return;
|
||||
}
|
||||
@@ -3198,7 +3266,7 @@ namespace gsr {
|
||||
const char *container = config.record_config.container.c_str();
|
||||
const char *video_codec = config.record_config.record_options.video_codec.c_str();
|
||||
const char *encoder = "gpu";
|
||||
choose_video_codec_and_container_with_fallback(gsr_info, &video_codec, &container, &encoder);
|
||||
choose_video_codec_and_container_with_fallback(gsr_info, config.record_config.record_options, &video_codec, &container, &encoder);
|
||||
|
||||
char size[64];
|
||||
size[0] = '\0';
|
||||
@@ -3328,10 +3396,10 @@ namespace gsr {
|
||||
case RecordingStatus::STREAM:
|
||||
break;
|
||||
case RecordingStatus::REPLAY:
|
||||
show_notification(TR("Unable to start streaming when replay is turned on.\nTurn off replay before starting streaming."), notification_error_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), NotificationType::REPLAY, nullptr, NotificationLevel::ERROR);
|
||||
show_notification(TR("Unable to start streaming when replay is turned on. Turn off replay before starting streaming."), notification_error_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), NotificationType::REPLAY, nullptr, NotificationLevel::ERROR);
|
||||
return;
|
||||
case RecordingStatus::RECORD:
|
||||
show_notification(TR("Unable to start streaming when recording.\nStop recording before starting streaming."), notification_error_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), NotificationType::RECORD, nullptr, NotificationLevel::ERROR);
|
||||
show_notification(TR("Unable to start streaming when recording. Stop recording before starting streaming."), notification_error_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), NotificationType::RECORD, nullptr, NotificationLevel::ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -3380,7 +3448,7 @@ namespace gsr {
|
||||
recording_capture_target = get_capture_target(config.streaming_config.record_options.record_area_option, capture_options);
|
||||
if(!validate_capture_target(config.streaming_config.record_options.record_area_option, capture_options)) {
|
||||
char err_msg[256];
|
||||
snprintf(err_msg, sizeof(err_msg), TR("Failed to start streaming, capture target \"%s\" is invalid.\nPlease change capture target in settings"), recording_capture_target.c_str());
|
||||
snprintf(err_msg, sizeof(err_msg), TR("Failed to start streaming, capture target \"%s\" is invalid. Please change capture target in settings"), recording_capture_target.c_str());
|
||||
show_notification(err_msg, notification_error_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), NotificationType::STREAM, nullptr, NotificationLevel::ERROR);
|
||||
return;
|
||||
}
|
||||
@@ -3399,12 +3467,12 @@ namespace gsr {
|
||||
container = config.streaming_config.custom.container.c_str();
|
||||
const char *video_codec = config.streaming_config.record_options.video_codec.c_str();
|
||||
const char *encoder = "gpu";
|
||||
choose_video_codec_and_container_with_fallback(gsr_info, &video_codec, &container, &encoder);
|
||||
choose_video_codec_and_container_with_fallback(gsr_info, config.streaming_config.record_options, &video_codec, &container, &encoder);
|
||||
|
||||
const std::string url = streaming_get_url(config);
|
||||
if(config.streaming_config.streaming_service == "rumble" || config.streaming_config.streaming_service == "kick") {
|
||||
fprintf(stderr, "Info: forcing video codec to h264 as rumble/kick supports only h264\n");
|
||||
video_codec = "h264";
|
||||
video_codec = "h264"; // TODO: Vulkan
|
||||
}
|
||||
|
||||
char size[64];
|
||||
@@ -3518,7 +3586,7 @@ namespace gsr {
|
||||
screenshot_capture_target = get_capture_target(record_area_option, capture_options);
|
||||
if(!validate_capture_target(record_area_option, capture_options)) {
|
||||
char err_msg[256];
|
||||
snprintf(err_msg, sizeof(err_msg), TR("Failed to take a screenshot, capture target \"%s\" is invalid.\nPlease change capture target in settings"), screenshot_capture_target.c_str());
|
||||
snprintf(err_msg, sizeof(err_msg), TR("Failed to take a screenshot, capture target \"%s\" is invalid. Please change capture target in settings"), screenshot_capture_target.c_str());
|
||||
show_notification(err_msg, notification_error_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), NotificationType::SCREENSHOT, nullptr, NotificationLevel::ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -39,17 +39,10 @@ namespace gsr {
|
||||
window_width = window_size.x;
|
||||
window_height = window_size.y;
|
||||
|
||||
if(!theme->title_font.load_from_file(theme->title_font_file, std::max(16.0f, window_size.y * 0.019f)))
|
||||
return false;
|
||||
|
||||
if(!theme->top_bar_font.load_from_file(theme->title_font_file, std::max(23.0f, window_size.y * 0.03f)))
|
||||
return false;
|
||||
|
||||
if(!theme->body_font.load_from_file(theme->body_font_file, std::max(13.0f, window_size.y * 0.015f)))
|
||||
return false;
|
||||
|
||||
if(!theme->camera_setup_font.load_from_file(theme->body_font_file, 24))
|
||||
return false;
|
||||
theme->title_font_desc = std::string("Noto Sans Bold ") + std::to_string((int)(std::max(16.0f, window_size.y * 0.019f)/1.8));
|
||||
theme->top_bar_font_desc = std::string("Noto Sans Bold ") + std::to_string((int)(std::max(23.0f, window_size.y * 0.03f)/1.8));
|
||||
theme->body_font_desc = std::string("Noto Sans ") + std::to_string((int)(std::max(13.0f, window_size.y * 0.015f)/1.8));
|
||||
theme->camera_setup_font_desc = "Noto Sans 14";
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -60,12 +53,6 @@ namespace gsr {
|
||||
|
||||
theme = new Theme();
|
||||
|
||||
if(!theme->body_font_file.load((resources_path + "fonts/NotoSans-Regular.ttf").c_str(), mgl::MemoryMappedFile::LoadOptions{true, false}))
|
||||
goto error;
|
||||
|
||||
if(!theme->title_font_file.load((resources_path + "fonts/NotoSans-Bold.ttf").c_str(), mgl::MemoryMappedFile::LoadOptions{true, false}))
|
||||
goto error;
|
||||
|
||||
if(!theme->combobox_arrow_texture.load_from_file((resources_path + "images/combobox_arrow.png").c_str(), mgl::Texture::LoadOptions{false, false, MGL_TEXTURE_SCALE_LINEAR_MIPMAP}))
|
||||
goto error;
|
||||
|
||||
|
||||
@@ -7,14 +7,18 @@
|
||||
|
||||
namespace gsr {
|
||||
std::string Translation::get_system_language() {
|
||||
const char* lang = getenv("LANGUAGE");
|
||||
if (!lang || !lang[0]) lang = getenv("LC_ALL");
|
||||
if (!lang || !lang[0]) lang = getenv("LC_MESSAGES");
|
||||
if (!lang || !lang[0]) lang = getenv("LANG");
|
||||
const char* env_vars[] = { "LANGUAGE", "LC_ALL", "LC_MESSAGES", "LANG" };
|
||||
for (const char* env_var : env_vars) {
|
||||
const char* lang = getenv(env_var);
|
||||
if (!lang || !lang[0])
|
||||
continue;
|
||||
|
||||
if (lang && lang[0]) {
|
||||
std::string lang_str(lang);
|
||||
|
||||
// Ignore non-language locales such as C/POSIX and fall back to the next variable
|
||||
if (lang_str == "C" || lang_str == "C.UTF-8" || lang_str == "POSIX")
|
||||
continue;
|
||||
|
||||
// we usually need only two symbols
|
||||
size_t underscore = lang_str.find('_');
|
||||
if (underscore != std::string::npos) {
|
||||
@@ -38,13 +42,13 @@ namespace gsr {
|
||||
}
|
||||
}
|
||||
|
||||
bool Translation::is_language_supported(const char* lang) {
|
||||
if(strcmp(lang, "en") == 0)
|
||||
bool Translation::is_language_supported(std::string_view lang) {
|
||||
if(lang == "en")
|
||||
return true;
|
||||
|
||||
std::string paths[] = {
|
||||
std::string("translations/") + lang + ".txt",
|
||||
std::string(this->translations_directory) + lang + ".txt"
|
||||
std::string("translations/") + std::string(lang) + ".txt",
|
||||
std::string(this->translations_directory) + std::string(lang) + ".txt"
|
||||
};
|
||||
|
||||
for (const auto& path : paths) {
|
||||
@@ -57,26 +61,27 @@ namespace gsr {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Translation::load_language(const char* lang) {
|
||||
bool Translation::load_language(std::string_view lang) {
|
||||
translations.clear();
|
||||
|
||||
if(lang[0] == '\0')
|
||||
lang = "en";
|
||||
const std::string system_language = get_system_language();
|
||||
if(lang.empty())
|
||||
lang = system_language;
|
||||
|
||||
if (!is_language_supported(lang)) {
|
||||
fprintf(stderr, "Warning: language '%s' is not supported\n", lang);
|
||||
fprintf(stderr, "Warning: language '%.*s' is not supported\n", (int)lang.size(), lang.data());
|
||||
return false;
|
||||
}
|
||||
|
||||
current_language = lang;
|
||||
|
||||
if (strcmp(lang, "en") == 0) {
|
||||
if (lang == "en") {
|
||||
return true; // english is the base
|
||||
}
|
||||
|
||||
std::string paths[] = {
|
||||
std::string("translations/") + lang + ".txt",
|
||||
std::string(this->translations_directory) + lang + ".txt"
|
||||
std::string("translations/") + std::string(lang) + ".txt",
|
||||
std::string(this->translations_directory) + std::string(lang) + ".txt"
|
||||
};
|
||||
|
||||
for (const auto& path : paths) {
|
||||
@@ -100,11 +105,11 @@ namespace gsr {
|
||||
translations[key] = value;
|
||||
}
|
||||
|
||||
fprintf(stderr, "Info: Loaded translation file for '%s' from %s\n", lang, path.c_str());
|
||||
fprintf(stderr, "Info: Loaded translation file for '%.*s' from %s\n", (int)lang.size(), lang.data(), path.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
fprintf(stderr, "Warning: translation file for '%s' not found\n", lang);
|
||||
fprintf(stderr, "Warning: translation file for '%.*s' not found\n", (int)lang.size(), lang.data());
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -119,7 +124,7 @@ namespace gsr {
|
||||
|
||||
this->translations_directory = translations_directory;
|
||||
|
||||
load_language(initial_language == nullptr ? get_system_language().c_str() : initial_language);
|
||||
load_language(initial_language == nullptr ? "" : initial_language);
|
||||
}
|
||||
|
||||
bool Translation::plural_numbers_are_complex() {
|
||||
|
||||
133
src/Utils.cpp
133
src/Utils.cpp
@@ -1,13 +1,74 @@
|
||||
#include "../include/Utils.hpp"
|
||||
#include "../include/Process.hpp"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <optional>
|
||||
#include <unistd.h>
|
||||
#include <pwd.h>
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
extern "C" {
|
||||
#include <mgl/system/clock.h>
|
||||
}
|
||||
|
||||
namespace gsr {
|
||||
static std::optional<std::string> get_xdg_autostart_content() {
|
||||
const char *args[] = {
|
||||
"/bin/sh", "-c",
|
||||
"cat \"${XDG_CONFIG_HOME:-$HOME/.config}/autostart/gpu-screen-recorder-ui.desktop\"",
|
||||
nullptr
|
||||
};
|
||||
std::string output;
|
||||
if(exec_program_on_host_get_stdout(args, output, false) != 0)
|
||||
return std::nullopt;
|
||||
return output;
|
||||
}
|
||||
|
||||
// Returns the exit status or -1 on timeout
|
||||
static int run_command_timeout(const char **args, double sleep_time_sec, double timeout_sec) {
|
||||
mgl_clock clock;
|
||||
mgl_clock_init(&clock);
|
||||
|
||||
do {
|
||||
int read_fd = 0;
|
||||
const pid_t process_id = exec_program(args, &read_fd, false);
|
||||
if(process_id <= 0)
|
||||
continue;
|
||||
|
||||
const double time_elapsed_sleep_start = mgl_clock_get_elapsed_time_seconds(&clock);
|
||||
pid_t waitpid_result = 0;
|
||||
do {
|
||||
int status = 0;
|
||||
waitpid_result = waitpid(process_id, &status, WNOHANG);
|
||||
if(waitpid_result > 0)
|
||||
break;
|
||||
|
||||
usleep(30 * 1000); // 30ms
|
||||
} while(mgl_clock_get_elapsed_time_seconds(&clock) - time_elapsed_sleep_start < sleep_time_sec);
|
||||
|
||||
int status = 0;
|
||||
if(waitpid_result > 0) {
|
||||
int exit_status = -0;
|
||||
if(WIFEXITED(status))
|
||||
exit_status = -1;
|
||||
|
||||
if(exit_status == 0)
|
||||
exit_status = WEXITSTATUS(status);
|
||||
|
||||
close(read_fd);
|
||||
return exit_status;
|
||||
} else {
|
||||
kill(process_id, SIGKILL);
|
||||
waitpid(process_id, &status, 0);
|
||||
close(read_fd);
|
||||
}
|
||||
} while(mgl_clock_get_elapsed_time_seconds(&clock) < timeout_sec);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void string_split_char(std::string_view str, char delimiter, StringSplitCallback callback_func) {
|
||||
size_t index = 0;
|
||||
while(index < str.size()) {
|
||||
@@ -238,4 +299,76 @@ namespace gsr {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool is_xdg_autostart_enabled() {
|
||||
const std::optional<std::string> output = get_xdg_autostart_content();
|
||||
return output.has_value() && output.value().find("Hidden=true") == std::string::npos;
|
||||
}
|
||||
|
||||
int set_xdg_autostart(bool enable) {
|
||||
const char *xdg_current_desktop = getenv("XDG_CURRENT_DESKTOP");
|
||||
if(!xdg_current_desktop || strlen(xdg_current_desktop) == 0) {
|
||||
std::string output;
|
||||
const char *check_dex_args[] = { "/bin/sh", "-c", "command -v dex", nullptr };
|
||||
if(exec_program_on_host_get_stdout(check_dex_args, output, true) != 0)
|
||||
return 67;
|
||||
}
|
||||
|
||||
const bool is_flatpak = getenv("FLATPAK_ID") != nullptr;
|
||||
const char *exec_line = is_flatpak
|
||||
? "Exec=flatpak run com.dec05eba.gpu_screen_recorder gsr-ui"
|
||||
: "Exec=gsr-ui launch-daemon";
|
||||
|
||||
std::string content =
|
||||
"[Desktop Entry]\n"
|
||||
"Type=Application\n"
|
||||
"Name=GPU Screen Recorder\n"
|
||||
"GenericName=Screen recorder\n"
|
||||
"Comment=A ShadowPlay-like screen recorder for Linux\n"
|
||||
"Icon=gpu-screen-recorder\n" +
|
||||
std::string(exec_line) + "\n" +
|
||||
"Terminal=false\n" +
|
||||
"Hidden=" + (enable ? "false" : "true") + "\n";
|
||||
|
||||
std::string shell_cmd =
|
||||
"p=\"${XDG_CONFIG_HOME:-$HOME/.config}/autostart/gpu-screen-recorder-ui.desktop\" && "
|
||||
"mkdir -p \"$(dirname \"$p\")\" && "
|
||||
"printf '" + content + "' > \"$p\"";
|
||||
|
||||
const char *args[] = { "/bin/sh", "-c", shell_cmd.c_str(), nullptr };
|
||||
std::string dummy;
|
||||
return exec_program_on_host_get_stdout(args, dummy, true);
|
||||
}
|
||||
|
||||
void replace_xdg_autostart_with_current_gsr_type() {
|
||||
const std::optional<std::string> output = get_xdg_autostart_content();
|
||||
if(!output.has_value())
|
||||
return;
|
||||
|
||||
const bool is_flatpak = getenv("FLATPAK_ID") != nullptr;
|
||||
const bool is_exec_flatpak = output.value().find("flatpak run") != std::string::npos;
|
||||
if(is_flatpak != is_exec_flatpak) {
|
||||
const bool is_autostart_enabled = output.value().find("Hidden=true") == std::string::npos;
|
||||
set_xdg_autostart(is_autostart_enabled);
|
||||
}
|
||||
}
|
||||
|
||||
bool wait_until_systemd_user_service_available() {
|
||||
const char *args[] = { "systemctl", "--user", "-q", "is-enabled", "gpu-screen-recorder-ui.service", nullptr };
|
||||
const char *flatpak_args[] = { "flatpak-spawn", "--host", "--", "systemctl", "--user", "-q", "is-enabled", "gpu-screen-recorder-ui.service", nullptr };
|
||||
const bool is_flatpak = getenv("FLATPAK_ID") != nullptr;
|
||||
return run_command_timeout(is_flatpak ? flatpak_args : args, 1.0, 5.0) >= 0;
|
||||
}
|
||||
|
||||
bool is_systemd_service_enabled(const char *service_name) {
|
||||
const char *args[] = { "systemctl", "--user", "is-enabled", service_name, nullptr };
|
||||
std::string output;
|
||||
return exec_program_on_host_get_stdout(args, output, false) == 0;
|
||||
}
|
||||
|
||||
bool disable_systemd_service(const char *service_name) {
|
||||
const char *args[] = { "systemctl", "--user", "disable", service_name, nullptr };
|
||||
std::string output;
|
||||
return exec_program_on_host_get_stdout(args, output, false) == 0;
|
||||
}
|
||||
}
|
||||
@@ -18,8 +18,8 @@ namespace gsr {
|
||||
//static const float padding_left_icon_scale = 0.25f;
|
||||
static const float padding_right_icon_scale = 0.15f;
|
||||
|
||||
Button::Button(mgl::Font *font, const char *text, mgl::vec2f size, mgl::Color bg_color) :
|
||||
size(size), bg_color(bg_color), bg_hover_color(bg_color), text(text, *font)
|
||||
Button::Button(const char *font_desc, const char *text, mgl::vec2f size, mgl::Color bg_color) :
|
||||
size(size), bg_color(bg_color), bg_hover_color(bg_color), text(text, font_desc)
|
||||
{
|
||||
|
||||
}
|
||||
@@ -117,12 +117,12 @@ namespace gsr {
|
||||
sprite.set_texture(texture);
|
||||
}
|
||||
|
||||
const std::string& Button::get_text() const {
|
||||
std::string_view Button::get_text() const {
|
||||
return text.get_string();
|
||||
}
|
||||
|
||||
void Button::set_text(std::string str) {
|
||||
text.set_string(std::move(str));
|
||||
void Button::set_text(std::string_view str) {
|
||||
text.set_string(str);
|
||||
}
|
||||
|
||||
void Button::scale_sprite_to_button_size() {
|
||||
|
||||
@@ -27,8 +27,8 @@ namespace gsr {
|
||||
return color;
|
||||
}
|
||||
|
||||
CheckBox::CheckBox(mgl::Font *font, const char *text) :
|
||||
text(text, *font),
|
||||
CheckBox::CheckBox(const char *font_desc, const char *text) :
|
||||
text(text, font_desc),
|
||||
background_sprite(&get_theme().checkbox_background_texture),
|
||||
circle_sprite(&get_theme().checkbox_circle_texture)
|
||||
{
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
#include "../../include/gui/Utils.hpp"
|
||||
#include "../../include/Theme.hpp"
|
||||
#include <mglpp/graphics/Rectangle.hpp>
|
||||
#include <mglpp/graphics/Font.hpp>
|
||||
#include <mglpp/window/Window.hpp>
|
||||
#include <mglpp/window/Event.hpp>
|
||||
#include <assert.h>
|
||||
@@ -14,8 +13,9 @@ namespace gsr {
|
||||
static const float padding_right_scale = 0.007f;
|
||||
static const float border_scale = 0.0015f;
|
||||
|
||||
ComboBox::ComboBox(mgl::Font *font) : font(font), dropdown_arrow(&get_theme().combobox_arrow_texture) {
|
||||
assert(font);
|
||||
ComboBox::ComboBox(const char *font_desc) : font_desc(font_desc), dropdown_arrow(&get_theme().combobox_arrow_texture) {
|
||||
assert(font_desc);
|
||||
font_size = mgl::Text::get_font_size_from_font_description(font_desc);
|
||||
}
|
||||
|
||||
bool ComboBox::on_event(mgl::Event &event, mgl::Window&, mgl::vec2f offset) {
|
||||
@@ -83,7 +83,7 @@ namespace gsr {
|
||||
draw_unselected(window, draw_pos);
|
||||
}
|
||||
|
||||
void ComboBox::add_item(const std::string &text, const std::string &id, bool allow_duplicate) {
|
||||
void ComboBox::add_item(std::string_view text, const std::string &id, bool allow_duplicate) {
|
||||
if(!allow_duplicate) {
|
||||
for(const auto &item : items) {
|
||||
if(item.id == id)
|
||||
@@ -91,9 +91,9 @@ namespace gsr {
|
||||
}
|
||||
}
|
||||
|
||||
items.push_back({mgl::Text(text, *font), id, {0.0f, 0.0f}});
|
||||
items.back().text.set_max_width(font->get_character_size() * 20); // TODO: Make a proper solution
|
||||
//items.back().text.set_max_rows(1);
|
||||
items.push_back({mgl::Text(text, font_desc.c_str()), id, {0.0f, 0.0f}});
|
||||
items.back().text.set_wrap_width(items.back().text.get_font_size() * 40); // TODO: Make a proper solution
|
||||
items.back().text.set_max_rows(2);
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
@@ -104,7 +104,7 @@ namespace gsr {
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
void ComboBox::set_selected_item(const std::string &id, bool trigger_event, bool trigger_event_even_if_selection_not_changed) {
|
||||
void ComboBox::set_selected_item(std::string_view id, bool trigger_event, bool trigger_event_even_if_selection_not_changed) {
|
||||
for(size_t i = 0; i < items.size(); ++i) {
|
||||
auto &item = items[i];
|
||||
if(item.id == id && item.enabled) {
|
||||
@@ -120,7 +120,7 @@ namespace gsr {
|
||||
}
|
||||
}
|
||||
|
||||
void ComboBox::set_item_enabled(const std::string &id, bool enabled) {
|
||||
void ComboBox::set_item_enabled(std::string_view id, bool enabled) {
|
||||
for(size_t i = 0; i < items.size(); ++i) {
|
||||
auto &item = items[i];
|
||||
if(item.id == id) {
|
||||
@@ -136,10 +136,9 @@ namespace gsr {
|
||||
}
|
||||
}
|
||||
|
||||
const std::string& ComboBox::get_selected_id() const {
|
||||
std::string_view ComboBox::get_selected_id() const {
|
||||
if(items.empty()) {
|
||||
static std::string dummy;
|
||||
return dummy;
|
||||
return "";
|
||||
} else {
|
||||
return items[selected_item].id;
|
||||
}
|
||||
@@ -240,7 +239,7 @@ namespace gsr {
|
||||
const int padding_right = padding_right_scale * get_theme().window_height;
|
||||
|
||||
Item *selected_item_ptr = (selected_item < items.size()) ? &items[selected_item] : nullptr;
|
||||
max_size = { 0.0f, padding_top + padding_bottom + (selected_item_ptr ? selected_item_ptr->text.get_bounds().size.y : font->get_character_size()) };
|
||||
max_size = { 0.0f, padding_top + padding_bottom + (selected_item_ptr ? selected_item_ptr->text.get_bounds().size.y : font_size) };
|
||||
for(Item &item : items) {
|
||||
const mgl::vec2f bounds = item.text.get_bounds().size;
|
||||
max_size.x = std::max(max_size.x, bounds.x + padding_left + padding_right);
|
||||
@@ -263,12 +262,12 @@ namespace gsr {
|
||||
const int padding_top = padding_top_scale * get_theme().window_height;
|
||||
const int padding_bottom = padding_bottom_scale * get_theme().window_height;
|
||||
Item *selected_item_ptr = (selected_item < items.size()) ? &items[selected_item] : nullptr;
|
||||
return { max_size.x, padding_top + padding_bottom + (selected_item_ptr ? selected_item_ptr->text.get_bounds().size.y : font->get_character_size()) };
|
||||
return { max_size.x, padding_top + padding_bottom + (selected_item_ptr ? selected_item_ptr->text.get_bounds().size.y : font_size) };
|
||||
}
|
||||
|
||||
float ComboBox::get_dropdown_arrow_height() const {
|
||||
const int padding_top = padding_top_scale * get_theme().window_height;
|
||||
const int padding_bottom = padding_bottom_scale * get_theme().window_height;
|
||||
return (font->get_character_size() + padding_top + padding_bottom) * 0.4f;
|
||||
return (font_size * 2.0f + padding_top + padding_bottom) * 0.4f;
|
||||
}
|
||||
}
|
||||
@@ -15,8 +15,12 @@ namespace gsr {
|
||||
static const float icon_spacing_scale = 0.008f;
|
||||
static const float border_scale = 0.003f;
|
||||
|
||||
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, *description_font)
|
||||
DropdownButton::DropdownButton(const char *title_font_desc, const char *description_font_desc, const char *title, const char *description, mgl::Texture *icon_texture, mgl::vec2f size) :
|
||||
title_font_desc(title_font_desc),
|
||||
description_font_desc(description_font_desc),
|
||||
size(size),
|
||||
title(title, title_font_desc),
|
||||
description(description, description_font_desc)
|
||||
{
|
||||
if(icon_texture && icon_texture->is_valid()) {
|
||||
icon_sprite.set_texture(icon_texture);
|
||||
@@ -193,11 +197,11 @@ namespace gsr {
|
||||
if(item.id == id)
|
||||
return;
|
||||
}
|
||||
items.push_back({mgl::Text(text, *title_font), mgl::Text(description, *description_font), nullptr, id});
|
||||
items.push_back({mgl::Text(text, title_font_desc.c_str()), mgl::Text(description, description_font_desc.c_str()), nullptr, id});
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
void DropdownButton::set_item_label(const std::string &id, const std::string &new_label) {
|
||||
void DropdownButton::set_item_label(std::string_view id, const std::string &new_label) {
|
||||
for(auto &item : items) {
|
||||
if(item.id == id) {
|
||||
item.text.set_string(new_label);
|
||||
@@ -206,7 +210,7 @@ namespace gsr {
|
||||
}
|
||||
}
|
||||
|
||||
void DropdownButton::set_item_icon(const std::string &id, mgl::Texture *texture) {
|
||||
void DropdownButton::set_item_icon(std::string_view id, mgl::Texture *texture) {
|
||||
for(auto &item : items) {
|
||||
if(item.id == id) {
|
||||
item.icon_texture = texture;
|
||||
@@ -215,7 +219,7 @@ namespace gsr {
|
||||
}
|
||||
}
|
||||
|
||||
void DropdownButton::set_item_description(const std::string &id, const std::string &new_description) {
|
||||
void DropdownButton::set_item_description(std::string_view id, const std::string &new_description) {
|
||||
for(auto &item : items) {
|
||||
if(item.id == id) {
|
||||
item.description_text.set_string(new_description);
|
||||
@@ -224,7 +228,7 @@ namespace gsr {
|
||||
}
|
||||
}
|
||||
|
||||
void DropdownButton::set_item_enabled(const std::string &id, bool enabled) {
|
||||
void DropdownButton::set_item_enabled(std::string_view id, bool enabled) {
|
||||
for(auto &item : items) {
|
||||
if(item.id == id) {
|
||||
item.enabled = enabled;
|
||||
|
||||
@@ -3,164 +3,42 @@
|
||||
#include "../../include/Theme.hpp"
|
||||
#include <mglpp/window/Window.hpp>
|
||||
#include <mglpp/window/Event.hpp>
|
||||
#include <mglpp/system/FloatRect.hpp>
|
||||
#include <mglpp/system/Utf8.hpp>
|
||||
#include <optional>
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
|
||||
namespace gsr {
|
||||
static const float padding_top_scale = 0.004629f;
|
||||
static const float padding_top_scale = 0.004629f;
|
||||
static const float padding_bottom_scale = 0.004629f;
|
||||
static const float padding_left_scale = 0.007f;
|
||||
static const float padding_right_scale = 0.007f;
|
||||
static const float border_scale = 0.0015f;
|
||||
static const float caret_width_scale = 0.001f;
|
||||
static const float padding_left_scale = 0.007f;
|
||||
static const float padding_right_scale = 0.007f;
|
||||
static const float border_scale = 0.0015f;
|
||||
|
||||
static void string_replace_all(std::string &str, char old_char, char new_char) {
|
||||
for(char &c : str) {
|
||||
if(c == old_char)
|
||||
c = new_char;
|
||||
}
|
||||
}
|
||||
|
||||
Entry::Entry(mgl::Font *font, const char *text, float max_width) :
|
||||
text(std::u32string(), *font),
|
||||
masked_text(std::u32string(), *font),
|
||||
Entry::Entry(const char *font_desc, const char *text, float max_width) :
|
||||
text_edit(font_desc, std::max(0.0f, max_width - (padding_left_scale * get_theme().window_height) - (padding_right_scale * get_theme().window_height))),
|
||||
max_width(max_width)
|
||||
{
|
||||
this->text.set_color(get_color_theme().text_color);
|
||||
this->masked_text.set_color(get_color_theme().text_color);
|
||||
const int padding_top = padding_top_scale * get_theme().window_height;
|
||||
const int padding_bottom = padding_bottom_scale * get_theme().window_height;
|
||||
const int padding_left = padding_left_scale * get_theme().window_height;
|
||||
const int padding_right = padding_right_scale * get_theme().window_height;
|
||||
|
||||
text_edit.set_single_paragraph_mode(true);
|
||||
text_edit.set_color(get_color_theme().text_color);
|
||||
text_edit.set_margins(padding_left, padding_top, padding_right, padding_bottom);
|
||||
set_text(text);
|
||||
text_edit.sync();
|
||||
}
|
||||
|
||||
bool Entry::on_event(mgl::Event &event, mgl::Window &window, mgl::vec2f offset) {
|
||||
bool Entry::on_event(mgl::Event &event, mgl::Window &/*window*/, mgl::vec2f /*offset*/) {
|
||||
if(!visible)
|
||||
return true;
|
||||
|
||||
mgl::Text32 &active_text = masked ? masked_text : text;
|
||||
|
||||
if(event.type == mgl::Event::MouseButtonPressed && event.mouse_button.button == mgl::Mouse::Left) {
|
||||
const mgl::vec2f mouse_pos = { (float)event.mouse_button.x, (float)event.mouse_button.y };
|
||||
selected = mgl::FloatRect(position + offset, get_size()).contains(mouse_pos);
|
||||
if(selected) {
|
||||
selecting_text = true;
|
||||
|
||||
const auto caret_index_mouse = find_closest_caret_index_by_position(mouse_pos);
|
||||
caret.index = caret_index_mouse.index;
|
||||
caret.offset_x = caret_index_mouse.pos.x - active_text.get_position().x;
|
||||
selection_start_caret = caret;
|
||||
show_selection = true;
|
||||
} else {
|
||||
selecting_text = false;
|
||||
selecting_with_keyboard = false;
|
||||
show_selection = false;
|
||||
if(text_edit.handle_event(event)) {
|
||||
if(event.type == mgl::Event::MouseButtonPressed && event.mouse_button.button == mgl::Mouse::Left)
|
||||
return true;
|
||||
else if(event.type == mgl::Event::KeyPressed || event.type == mgl::Event::TextEntered) {
|
||||
if(on_changed)
|
||||
on_changed(text_edit.get_text());
|
||||
}
|
||||
} else if(event.type == mgl::Event::MouseButtonReleased && event.mouse_button.button == mgl::Mouse::Left) {
|
||||
selecting_text = false;
|
||||
if(caret.index == selection_start_caret.index)
|
||||
show_selection = false;
|
||||
} else if(event.type == mgl::Event::MouseMoved && selected) {
|
||||
if(selecting_text) {
|
||||
const auto caret_index_mouse = find_closest_caret_index_by_position(mgl::vec2f(event.mouse_move.x, event.mouse_move.y));
|
||||
caret.index = caret_index_mouse.index;
|
||||
caret.offset_x = caret_index_mouse.pos.x - active_text.get_position().x;
|
||||
return false;
|
||||
}
|
||||
} else if(event.type == mgl::Event::KeyPressed && selected) {
|
||||
int selection_start_byte = caret.index;
|
||||
int selection_end_byte = caret.index;
|
||||
if(show_selection) {
|
||||
selection_start_byte = std::min(caret.index, selection_start_caret.index);
|
||||
selection_end_byte = std::max(caret.index, selection_start_caret.index);
|
||||
}
|
||||
|
||||
if(event.key.code == mgl::Keyboard::Backspace) {
|
||||
if(selection_start_byte == selection_end_byte && caret.index > 0)
|
||||
selection_start_byte -= 1;
|
||||
|
||||
replace_text(selection_start_byte, selection_end_byte - selection_start_byte, std::u32string());
|
||||
} else if(event.key.code == mgl::Keyboard::Delete) {
|
||||
if(selection_start_byte == selection_end_byte && caret.index < (int)active_text.get_string().size())
|
||||
selection_end_byte += 1;
|
||||
|
||||
replace_text(selection_start_byte, selection_end_byte - selection_start_byte, std::u32string());
|
||||
} else if(event.key.code == mgl::Keyboard::C && event.key.control) {
|
||||
const size_t selection_num_bytes = selection_end_byte - selection_start_byte;
|
||||
if(selection_num_bytes > 0)
|
||||
window.set_clipboard(mgl::utf32_to_utf8(text.get_string().substr(selection_start_byte, selection_num_bytes)));
|
||||
} else if(event.key.code == mgl::Keyboard::V && event.key.control) {
|
||||
std::string clipboard_string = window.get_clipboard_string();
|
||||
string_replace_all(clipboard_string, '\n', ' ');
|
||||
replace_text(selection_start_byte, selection_end_byte - selection_start_byte, mgl::utf8_to_utf32(clipboard_string));
|
||||
} else if(event.key.code == mgl::Keyboard::A && event.key.control) {
|
||||
selection_start_caret.index = 0;
|
||||
selection_start_caret.offset_x = 0.0f;
|
||||
|
||||
caret.index = active_text.get_string().size();
|
||||
// TODO: Optimize
|
||||
caret.offset_x = active_text.find_character_pos(caret.index).x - active_text.get_position().x;
|
||||
|
||||
show_selection = true;
|
||||
} else if(event.key.code == mgl::Keyboard::Left) {
|
||||
if(!selecting_with_keyboard && show_selection)
|
||||
show_selection = false;
|
||||
else
|
||||
move_caret_word(Direction::LEFT, event.key.control ? 999999 : 1);
|
||||
|
||||
if(!selecting_with_keyboard) {
|
||||
selection_start_caret = caret;
|
||||
show_selection = false;
|
||||
}
|
||||
} else if(event.key.code == mgl::Keyboard::Right) {
|
||||
if(!selecting_with_keyboard && show_selection)
|
||||
show_selection = false;
|
||||
else
|
||||
move_caret_word(Direction::RIGHT, event.key.control ? 999999 : 1);
|
||||
|
||||
if(!selecting_with_keyboard) {
|
||||
selection_start_caret = caret;
|
||||
show_selection = false;
|
||||
}
|
||||
} else if(event.key.code == mgl::Keyboard::Home) {
|
||||
caret.index = 0;
|
||||
caret.offset_x = 0.0f;
|
||||
|
||||
if(!selecting_with_keyboard) {
|
||||
selection_start_caret = caret;
|
||||
show_selection = false;
|
||||
}
|
||||
} else if(event.key.code == mgl::Keyboard::End) {
|
||||
caret.index = active_text.get_string().size();
|
||||
// TODO: Optimize
|
||||
caret.offset_x = active_text.find_character_pos(caret.index).x - active_text.get_position().x;
|
||||
|
||||
if(!selecting_with_keyboard) {
|
||||
selection_start_caret = caret;
|
||||
show_selection = false;
|
||||
}
|
||||
} else if(event.key.code == mgl::Keyboard::LShift || event.key.code == mgl::Keyboard::RShift) {
|
||||
if(!show_selection)
|
||||
selection_start_caret = caret;
|
||||
selecting_with_keyboard = true;
|
||||
show_selection = true;
|
||||
}
|
||||
|
||||
return false;
|
||||
} else if(event.type == mgl::Event::KeyReleased && selected) {
|
||||
if(event.key.code == mgl::Keyboard::LShift || event.key.code == mgl::Keyboard::RShift) {
|
||||
selecting_with_keyboard = false;
|
||||
}
|
||||
|
||||
return false;
|
||||
} else if(event.type == mgl::Event::TextEntered && selected && event.text.codepoint >= 32 && event.text.codepoint != 127) {
|
||||
int selection_start_byte = caret.index;
|
||||
int selection_end_byte = caret.index;
|
||||
if(show_selection) {
|
||||
selection_start_byte = std::min(caret.index, selection_start_caret.index);
|
||||
selection_end_byte = std::max(caret.index, selection_start_caret.index);
|
||||
}
|
||||
|
||||
replace_text(selection_start_byte, selection_end_byte - selection_start_byte, mgl::utf8_to_utf32((const unsigned char*)event.text.str, event.text.size));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -173,297 +51,49 @@ namespace gsr {
|
||||
|
||||
const mgl::vec2f draw_pos = position + offset;
|
||||
|
||||
const int padding_top = padding_top_scale * get_theme().window_height;
|
||||
const int padding_bottom = padding_bottom_scale * get_theme().window_height;
|
||||
const int padding_left = padding_left_scale * get_theme().window_height;
|
||||
const int padding_right = padding_right_scale * get_theme().window_height;
|
||||
const mgl::vec2f size = get_size();
|
||||
|
||||
mgl::Text32 &active_text = masked ? masked_text : text;
|
||||
|
||||
background.set_size(get_size());
|
||||
background.set_size(size);
|
||||
background.set_position(draw_pos.floor());
|
||||
background.set_color(selected ? mgl::Color(0, 0, 0, 255) : mgl::Color(0, 0, 0, 120));
|
||||
background.set_color(text_edit.is_focused() ? mgl::Color(0, 0, 0, 255) : mgl::Color(0, 0, 0, 120));
|
||||
window.draw(background);
|
||||
|
||||
const int caret_width = std::max(1.0f, caret_width_scale * get_theme().window_height);
|
||||
const mgl::vec2f caret_size = mgl::vec2f(caret_width, active_text.get_bounds().size.y).floor();
|
||||
|
||||
const float overflow_left = (caret.offset_x + padding_left) - (padding_left + text_overflow);
|
||||
if(overflow_left < 0.0f)
|
||||
text_overflow += overflow_left;
|
||||
|
||||
const float overflow_right = (caret.offset_x + padding_left) - (background.get_size().x - padding_right);
|
||||
if(overflow_right - text_overflow > 0.0f)
|
||||
text_overflow = overflow_right;
|
||||
|
||||
active_text.set_position((draw_pos + mgl::vec2f(padding_left, get_size().y * 0.5f - active_text.get_bounds().size.y * 0.5f) - mgl::vec2f(text_overflow, 0.0f)).floor());
|
||||
|
||||
const auto text_bounds = active_text.get_bounds();
|
||||
const bool text_larger_than_background = text_bounds.size.x > (background.get_size().x - padding_left - padding_right);
|
||||
const float text_overflow_right = (text_bounds.position.x + text_bounds.size.x) - (background.get_position().x + background.get_size().x - padding_right);
|
||||
if(text_larger_than_background) {
|
||||
if(text_overflow_right < 0.0f) {
|
||||
text_overflow += text_overflow_right;
|
||||
active_text.set_position(active_text.get_position() + mgl::vec2f(-text_overflow_right, 0.0f));
|
||||
}
|
||||
} else {
|
||||
active_text.set_position(active_text.get_position() + mgl::vec2f(-text_overflow, 0.0f));
|
||||
text_overflow = 0.0f;
|
||||
}
|
||||
|
||||
if(selected) {
|
||||
if(text_edit.is_focused()) {
|
||||
const int border_size = std::max(1.0f, border_scale * get_theme().window_height);
|
||||
draw_rectangle_outline(window, draw_pos.floor(), get_size().floor(), get_color_theme().tint_color, border_size);
|
||||
draw_caret(window, draw_pos, caret_size);
|
||||
draw_rectangle_outline(window, draw_pos.floor(), size.floor(), get_color_theme().tint_color, border_size);
|
||||
}
|
||||
|
||||
const mgl::Scissor parent_scissor = window.get_scissor();
|
||||
const mgl::Scissor scissor = scissor_get_sub_area(parent_scissor,
|
||||
mgl::Scissor{
|
||||
(background.get_position() + mgl::vec2f(padding_left, padding_top)).to_vec2i(),
|
||||
(background.get_size() - mgl::vec2f(padding_left + padding_right, padding_top + padding_bottom)).to_vec2i()
|
||||
});
|
||||
window.set_scissor(scissor);
|
||||
|
||||
window.draw(active_text);
|
||||
|
||||
if(show_selection)
|
||||
draw_caret_selection(window, draw_pos, caret_size);
|
||||
|
||||
window.set_scissor(parent_scissor);
|
||||
}
|
||||
|
||||
void Entry::draw_caret(mgl::Window &window, mgl::vec2f draw_pos, mgl::vec2f caret_size) {
|
||||
const int padding_top = padding_top_scale * get_theme().window_height;
|
||||
const int padding_left = padding_left_scale * get_theme().window_height;
|
||||
|
||||
mgl::Rectangle caret_rect(caret_size);
|
||||
mgl::vec2f caret_draw_pos = draw_pos + mgl::vec2f(padding_left + caret.offset_x - text_overflow, padding_top);
|
||||
caret_rect.set_position(caret_draw_pos.floor());
|
||||
caret_rect.set_color(mgl::Color(255, 255, 255));
|
||||
window.draw(caret_rect);
|
||||
}
|
||||
|
||||
void Entry::draw_caret_selection(mgl::Window &window, mgl::vec2f draw_pos, mgl::vec2f caret_size) {
|
||||
if(selection_start_caret.index == caret.index)
|
||||
return;
|
||||
|
||||
const int padding_top = padding_top_scale * get_theme().window_height;
|
||||
const int padding_left = padding_left_scale * get_theme().window_height;
|
||||
const int caret_width = std::max(1.0f, caret_width_scale * get_theme().window_height);
|
||||
const int offset = caret.index < selection_start_caret.index ? caret_width : 0;
|
||||
|
||||
mgl::Rectangle caret_selection_rect(mgl::vec2f(std::abs(selection_start_caret.offset_x - caret.offset_x) - offset, caret_size.y).floor());
|
||||
caret_selection_rect.set_position((draw_pos + mgl::vec2f(padding_left + std::min(caret.offset_x, selection_start_caret.offset_x) - text_overflow + offset, padding_top)).floor());
|
||||
mgl::Color caret_select_color = get_color_theme().tint_color;
|
||||
caret_select_color.a = 100;
|
||||
caret_selection_rect.set_color(caret_select_color);
|
||||
window.draw(caret_selection_rect);
|
||||
text_edit.set_position(draw_pos.floor());
|
||||
window.draw(text_edit);
|
||||
}
|
||||
|
||||
mgl::vec2f Entry::get_size() {
|
||||
if(!visible)
|
||||
return {0.0f, 0.0f};
|
||||
|
||||
const int padding_top = padding_top_scale * get_theme().window_height;
|
||||
const int padding_bottom = padding_bottom_scale * get_theme().window_height;
|
||||
return { max_width, text.get_bounds().size.y + padding_top + padding_bottom };
|
||||
const mgl::vec2i text_size = text_edit.get_size(true);
|
||||
return text_size.to_vec2f();
|
||||
}
|
||||
|
||||
void Entry::move_caret_word(Direction direction, size_t max_codepoints) {
|
||||
mgl::Text32 &active_text = masked ? masked_text : text;
|
||||
const int dir_step = direction == Direction::LEFT ? -1 : 1;
|
||||
const int num_delimiter_chars = 15;
|
||||
const char delimiter_chars[num_delimiter_chars + 1] = " \t\n/.,:;\\[](){}";
|
||||
const char32_t *text_str = active_text.get_string().data();
|
||||
|
||||
int num_non_delimiter_chars_found = 0;
|
||||
|
||||
for(size_t i = 0; i < max_codepoints; ++i) {
|
||||
const uint32_t codepoint = text_str[caret.index];
|
||||
|
||||
const bool is_delimiter_char = codepoint < 127 && !!memchr(delimiter_chars, codepoint, num_delimiter_chars);
|
||||
if(is_delimiter_char) {
|
||||
if(num_non_delimiter_chars_found > 0)
|
||||
break;
|
||||
} else {
|
||||
++num_non_delimiter_chars_found;
|
||||
}
|
||||
|
||||
if(caret.index + dir_step < 0 || caret.index + dir_step > (int)active_text.get_string().size())
|
||||
break;
|
||||
|
||||
caret.index += dir_step;
|
||||
}
|
||||
|
||||
// TODO: Move right by some characters instead of calculating every character to caret index
|
||||
caret.offset_x = active_text.find_character_pos(caret.index).x - active_text.get_position().x;
|
||||
void Entry::set_text(std::string_view str) {
|
||||
text_edit.set_text(std::string(str).c_str());
|
||||
if(on_changed)
|
||||
on_changed(text_edit.get_text());
|
||||
}
|
||||
|
||||
EntryValidateHandlerResult Entry::set_text(const std::string &str) {
|
||||
EntryValidateHandlerResult validate_result = set_text_internal(mgl::utf8_to_utf32(str));
|
||||
if(validate_result == EntryValidateHandlerResult::ALLOW) {
|
||||
mgl::Text32 &active_text = masked ? masked_text : text;
|
||||
caret.index = active_text.get_string().size();
|
||||
// TODO: Optimize
|
||||
caret.offset_x = active_text.find_character_pos(caret.index).x - active_text.get_position().x;
|
||||
selection_start_caret = caret;
|
||||
|
||||
selecting_text = false;
|
||||
selecting_with_keyboard = false;
|
||||
show_selection = false;
|
||||
}
|
||||
return validate_result;
|
||||
}
|
||||
|
||||
EntryValidateHandlerResult Entry::set_text_internal(std::u32string str) {
|
||||
EntryValidateHandlerResult validate_result = EntryValidateHandlerResult::ALLOW;
|
||||
if(validate_handler)
|
||||
validate_result = validate_handler(*this, str);
|
||||
|
||||
if(validate_result == EntryValidateHandlerResult::ALLOW) {
|
||||
text.set_string(std::move(str));
|
||||
if(masked)
|
||||
masked_text.set_string(std::u32string(text.get_string().size(), '*'));
|
||||
// TODO: Call callback with utf32 instead?
|
||||
if(on_changed)
|
||||
on_changed(mgl::utf32_to_utf8(text.get_string()));
|
||||
}
|
||||
|
||||
return validate_result;
|
||||
}
|
||||
|
||||
std::string Entry::get_text() const {
|
||||
return mgl::utf32_to_utf8(text.get_string());
|
||||
std::string_view Entry::get_text() const {
|
||||
return text_edit.get_text();
|
||||
}
|
||||
|
||||
void Entry::set_masked(bool masked) {
|
||||
if(masked == this->masked)
|
||||
return;
|
||||
|
||||
this->masked = masked;
|
||||
|
||||
if(masked)
|
||||
masked_text.set_string(std::u32string(text.get_string().size(), '*'));
|
||||
else
|
||||
masked_text.set_string(std::u32string());
|
||||
|
||||
mgl::Text32 &active_text = masked ? masked_text : text;
|
||||
caret.offset_x = active_text.find_character_pos(caret.index).x - active_text.get_position().x;
|
||||
selection_start_caret.offset_x = active_text.find_character_pos(selection_start_caret.index).x - active_text.get_position().x;
|
||||
text_edit.set_masked(masked);
|
||||
}
|
||||
|
||||
bool Entry::is_masked() const {
|
||||
return masked;
|
||||
return text_edit.is_masked();
|
||||
}
|
||||
|
||||
void Entry::replace_text(size_t index, size_t size, const std::u32string &replacement) {
|
||||
if(index + size > text.get_string().size())
|
||||
return;
|
||||
|
||||
const auto prev_caret = caret;
|
||||
|
||||
if((int)index >= caret.index)
|
||||
caret.index += replacement.size();
|
||||
else
|
||||
caret.index = caret.index - size + replacement.size();
|
||||
|
||||
std::u32string str = text.get_string();
|
||||
str.replace(index, size, replacement);
|
||||
const EntryValidateHandlerResult validate_result = set_text_internal(std::move(str));
|
||||
if(validate_result == EntryValidateHandlerResult::DENY) {
|
||||
caret = prev_caret;
|
||||
return;
|
||||
} else if(validate_result == EntryValidateHandlerResult::REPLACED) {
|
||||
return;
|
||||
}
|
||||
|
||||
mgl::Text32 &active_text = masked ? masked_text : text;
|
||||
// TODO: Optimize
|
||||
caret.offset_x = active_text.find_character_pos(caret.index).x - active_text.get_position().x;
|
||||
selection_start_caret = caret;
|
||||
|
||||
selecting_text = false;
|
||||
selecting_with_keyboard = false;
|
||||
show_selection = false;
|
||||
}
|
||||
|
||||
CaretIndexPos Entry::find_closest_caret_index_by_position(mgl::vec2f position) {
|
||||
mgl::Text32 &active_text = masked ? masked_text : text;
|
||||
const std::u32string &str = active_text.get_string();
|
||||
mgl::Font *font = active_text.get_font();
|
||||
|
||||
CaretIndexPos result = {0, {active_text.get_position().x, active_text.get_position().y}};
|
||||
for(result.index = 0; result.index < (int)str.size(); ++result.index) {
|
||||
const uint32_t codepoint = str[result.index];
|
||||
|
||||
float glyph_width = 0.0f;
|
||||
if(codepoint == '\t') {
|
||||
const auto glyph = font->get_glyph(' ');
|
||||
const int tab_width = 4;
|
||||
glyph_width = glyph.advance * tab_width;
|
||||
} else {
|
||||
const auto glyph = font->get_glyph(codepoint);
|
||||
glyph_width = glyph.advance;
|
||||
}
|
||||
|
||||
if(result.pos.x + glyph_width * 0.5f >= position.x)
|
||||
break;
|
||||
|
||||
result.pos.x += glyph_width;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool is_number(uint8_t c) {
|
||||
return c >= '0' && c <= '9';
|
||||
}
|
||||
|
||||
static std::optional<int> to_integer(const std::u32string &str) {
|
||||
if(str.empty())
|
||||
return std::nullopt;
|
||||
|
||||
size_t i = 0;
|
||||
const bool negative = str[0] == '-';
|
||||
if(negative)
|
||||
i = 1;
|
||||
|
||||
int number = 0;
|
||||
for(; i < str.size(); ++i) {
|
||||
if(!is_number(str[i]))
|
||||
return std::nullopt;
|
||||
|
||||
const int new_number = number * 10 + (str[i] - '0');
|
||||
if(new_number < number)
|
||||
return std::nullopt; // Overflow
|
||||
number = new_number;
|
||||
}
|
||||
|
||||
if(negative)
|
||||
number = -number;
|
||||
|
||||
return number;
|
||||
}
|
||||
|
||||
EntryValidateHandler create_entry_validator_integer_in_range(int min, int max) {
|
||||
return [min, max](Entry &entry, const std::u32string &str) {
|
||||
if(str.empty())
|
||||
return EntryValidateHandlerResult::ALLOW;
|
||||
|
||||
const std::optional<int> number = to_integer(str);
|
||||
if(!number)
|
||||
return EntryValidateHandlerResult::DENY;
|
||||
|
||||
if(number.value() < min) {
|
||||
entry.set_text(std::to_string(min));
|
||||
return EntryValidateHandlerResult::REPLACED;
|
||||
} else if(number.value() > max) {
|
||||
entry.set_text(std::to_string(max));
|
||||
return EntryValidateHandlerResult::REPLACED;
|
||||
}
|
||||
|
||||
return EntryValidateHandlerResult::ALLOW;
|
||||
};
|
||||
void Entry::set_number_mode(bool enabled, int min_val, int max_val) {
|
||||
text_edit.set_number_mode(enabled, min_val, max_val);
|
||||
}
|
||||
}
|
||||
@@ -54,7 +54,7 @@ namespace gsr {
|
||||
times_clicked_within_timer = 1;
|
||||
|
||||
if(selected_item != -1 && times_clicked_within_timer > 0 && times_clicked_within_timer % 2 == 0) {
|
||||
file_chooser->open_subdirectory(folders[selected_item].text.get_string().c_str());
|
||||
file_chooser->open_subdirectory(folders[selected_item].text.get_string());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
@@ -115,7 +115,7 @@ namespace gsr {
|
||||
window.draw(folder_sprite);
|
||||
|
||||
// TODO: Dont allow text to go further left/right than item_pos (on the left side) and item_pos + item_size (on the right side).
|
||||
folder.text.set_max_width(item_size.x);
|
||||
folder.text.set_wrap_width(item_size.x);
|
||||
folder.text.set_max_rows(2);
|
||||
folder.text.set_position((folder_sprite.get_position() + mgl::vec2f(folder_sprite.get_size().x * 0.5f - folder.text.get_bounds().size.x * 0.5f, folder_sprite.get_size().y + folder_text_spacing_scale * get_theme().window_height)).floor());
|
||||
window.draw(folder.text);
|
||||
@@ -136,14 +136,17 @@ namespace gsr {
|
||||
inner_size = mgl::vec2f(size.x, folder_pos.y - draw_pos.y);
|
||||
}
|
||||
|
||||
void FileChooserBody::set_current_directory(const char *directory) {
|
||||
void FileChooserBody::set_current_directory(std::string_view directory) {
|
||||
folders.clear();
|
||||
selected_item = -1;
|
||||
mouse_over_item = -1;
|
||||
|
||||
DIR *d = opendir(directory);
|
||||
char path[4096];
|
||||
snprintf(path, sizeof(path), "%.*s", (int)directory.size(), directory.data());
|
||||
|
||||
DIR *d = opendir(path);
|
||||
if(!d) {
|
||||
fprintf(stderr, "gsr-ui error: failed to open directory: %s, error: %s\n", directory, strerror(errno));
|
||||
fprintf(stderr, "gsr-ui error: failed to open directory: %s, error: %s\n", path, strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -154,7 +157,7 @@ namespace gsr {
|
||||
if(dir->d_name[0] == '.')
|
||||
continue;
|
||||
|
||||
snprintf(filepath, sizeof(filepath), "%s/%s", directory, dir->d_name);
|
||||
snprintf(filepath, sizeof(filepath), "%s/%s", path, dir->d_name);
|
||||
|
||||
struct stat st;
|
||||
if(stat(filepath, &st) == -1)
|
||||
@@ -163,7 +166,7 @@ namespace gsr {
|
||||
if(!S_ISDIR(st.st_mode))
|
||||
continue;
|
||||
|
||||
folders.push_back({mgl::Text(dir->d_name, get_theme().body_font), st.st_mtim.tv_sec});
|
||||
folders.push_back({mgl::Text(dir->d_name, get_theme().body_font_desc.c_str()), st.st_mtim.tv_sec});
|
||||
}
|
||||
|
||||
closedir(d);
|
||||
@@ -191,9 +194,9 @@ namespace gsr {
|
||||
this->size = size;
|
||||
}
|
||||
|
||||
FileChooser::FileChooser(const char *start_directory, mgl::vec2f size) :
|
||||
FileChooser::FileChooser(std::string_view start_directory, mgl::vec2f size) :
|
||||
size(size),
|
||||
current_directory_text(start_directory, get_theme().body_font),
|
||||
current_directory_text(start_directory, get_theme().body_font_desc.c_str()),
|
||||
up_arrow_sprite(&get_theme().up_arrow_texture),
|
||||
scrollable_page(size)
|
||||
{
|
||||
@@ -275,18 +278,20 @@ namespace gsr {
|
||||
return size;
|
||||
}
|
||||
|
||||
void FileChooser::set_current_directory(const char *directory) {
|
||||
void FileChooser::set_current_directory(std::string_view directory) {
|
||||
current_directory_text.set_string(directory);
|
||||
file_chooser_body_ptr->set_current_directory(directory);
|
||||
scrollable_page.reset_scroll();
|
||||
}
|
||||
|
||||
void FileChooser::open_subdirectory(const char *name) {
|
||||
void FileChooser::open_subdirectory(std::string_view name) {
|
||||
char filepath[PATH_MAX];
|
||||
if(current_directory_text.get_string() == "/")
|
||||
snprintf(filepath, sizeof(filepath), "/%s", name);
|
||||
else
|
||||
snprintf(filepath, sizeof(filepath), "%s/%s", current_directory_text.get_string().c_str(), name);
|
||||
const std::string_view current_dir = current_directory_text.get_string();
|
||||
if(current_dir == "/") {
|
||||
snprintf(filepath, sizeof(filepath), "/%.*s", (int)name.size(), name.data());
|
||||
} else {
|
||||
snprintf(filepath, sizeof(filepath), "%.*s/%.*s", (int)current_dir.size(), current_dir.data(), (int)name.size(), name.data());
|
||||
}
|
||||
set_current_directory(filepath);
|
||||
}
|
||||
|
||||
@@ -294,7 +299,7 @@ namespace gsr {
|
||||
set_current_directory(get_parent_directory(current_directory_text.get_string()).c_str());
|
||||
}
|
||||
|
||||
const std::string& FileChooser::get_current_directory() const {
|
||||
std::string_view FileChooser::get_current_directory() const {
|
||||
return current_directory_text.get_string();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "../../include/Overlay.hpp"
|
||||
#include "../../include/Theme.hpp"
|
||||
#include "../../include/Process.hpp"
|
||||
#include "../../include/Utils.hpp"
|
||||
#include "../../include/Translation.hpp"
|
||||
#include "../../include/gui/GsrPage.hpp"
|
||||
#include "../../include/gui/PageStack.hpp"
|
||||
@@ -100,9 +101,9 @@ namespace gsr {
|
||||
if(!configure_hotkey_button)
|
||||
return;
|
||||
|
||||
mgl::Text title_text(TRF("Press a key combination to use for the hotkey: \"%s\"", hotkey_configure_action_name.c_str()), get_theme().title_font);
|
||||
mgl::Text hotkey_text(configure_hotkey_button->get_text(), get_theme().top_bar_font);
|
||||
mgl::Text description_text(TR("Alpha-numerical keys can't be used alone in hotkeys, they have to be used one or more of these keys: Alt, Ctrl, Shift and Super.\nPress Esc to cancel or Backspace to remove the hotkey."), get_theme().body_font);
|
||||
mgl::Text title_text(TRF("Press a key combination to use for the hotkey: \"%s\"", hotkey_configure_action_name.c_str()), get_theme().title_font_desc.c_str());
|
||||
mgl::Text hotkey_text(configure_hotkey_button->get_text(), get_theme().top_bar_font_desc.c_str());
|
||||
mgl::Text description_text(TR("Alpha-numerical keys can't be used alone in hotkeys, they have to be used one or more of these keys: Alt, Ctrl, Shift and Super.\nPress Esc to cancel or Backspace to remove the hotkey."), get_theme().body_font_desc.c_str());
|
||||
const float text_max_width = std::max(title_text.get_bounds().size.x, std::max(hotkey_text.get_bounds().size.x, description_text.get_bounds().size.x));
|
||||
|
||||
const float padding_horizontal = int(get_theme().window_height * 0.01f);
|
||||
@@ -145,13 +146,13 @@ namespace gsr {
|
||||
|
||||
std::unique_ptr<Subsection> GlobalSettingsPage::create_appearance_subsection(ScrollablePage *parent_page) {
|
||||
auto list = std::make_unique<List>(List::Orientation::VERTICAL);
|
||||
list->add_widget(std::make_unique<Label>(&get_theme().body_font, TR("Accent color"), get_color_theme().text_color));
|
||||
auto tint_color_radio_button = std::make_unique<RadioButton>(&get_theme().body_font, RadioButton::Orientation::HORIZONTAL);
|
||||
list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("Accent color"), get_color_theme().text_color));
|
||||
auto tint_color_radio_button = std::make_unique<RadioButton>(get_theme().body_font_desc.c_str(), RadioButton::Orientation::HORIZONTAL);
|
||||
tint_color_radio_button_ptr = tint_color_radio_button.get();
|
||||
tint_color_radio_button->add_item(TR("Red"), "amd");
|
||||
tint_color_radio_button->add_item(TR("Green"), "nvidia");
|
||||
tint_color_radio_button->add_item(TR("Blue"), "intel");
|
||||
tint_color_radio_button->on_selection_changed = [](const std::string&, const std::string &id) {
|
||||
tint_color_radio_button->on_selection_changed = [](std::string_view, std::string_view id) {
|
||||
if(id == "amd")
|
||||
get_color_theme().tint_color = mgl::Color(221, 0, 49);
|
||||
else if(id == "nvidia")
|
||||
@@ -166,12 +167,12 @@ namespace gsr {
|
||||
|
||||
std::unique_ptr<Subsection> GlobalSettingsPage::create_startup_subsection(ScrollablePage *parent_page) {
|
||||
auto list = std::make_unique<List>(List::Orientation::VERTICAL);
|
||||
list->add_widget(std::make_unique<Label>(&get_theme().body_font, TR("Start program on system startup?"), get_color_theme().text_color));
|
||||
auto startup_radio_button = std::make_unique<RadioButton>(&get_theme().body_font, RadioButton::Orientation::HORIZONTAL);
|
||||
list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("Start program on system startup?"), get_color_theme().text_color));
|
||||
auto startup_radio_button = std::make_unique<RadioButton>(get_theme().body_font_desc.c_str(), RadioButton::Orientation::HORIZONTAL);
|
||||
startup_radio_button_ptr = startup_radio_button.get();
|
||||
startup_radio_button->add_item(TR("Yes"), "start_on_system_startup");
|
||||
startup_radio_button->add_item(TR("No"), "dont_start_on_system_startup");
|
||||
startup_radio_button->on_selection_changed = [&](const std::string&, const std::string &id) {
|
||||
startup_radio_button->on_selection_changed = [&](std::string_view, std::string_view id) {
|
||||
bool enable = false;
|
||||
if(id == "dont_start_on_system_startup")
|
||||
enable = false;
|
||||
@@ -180,9 +181,7 @@ namespace gsr {
|
||||
else
|
||||
return false;
|
||||
|
||||
const char *args[] = { "systemctl", enable ? "enable" : "disable", "--user", "gpu-screen-recorder-ui", nullptr };
|
||||
std::string stdout_str;
|
||||
const int exit_status = exec_program_on_host_get_stdout(args, stdout_str);
|
||||
const int exit_status = set_xdg_autostart(enable);
|
||||
if(on_startup_changed)
|
||||
on_startup_changed(enable, exit_status);
|
||||
return exit_status == 0;
|
||||
@@ -192,28 +191,28 @@ namespace gsr {
|
||||
}
|
||||
|
||||
std::unique_ptr<RadioButton> GlobalSettingsPage::create_enable_keyboard_hotkeys_button() {
|
||||
auto enable_hotkeys_radio_button = std::make_unique<RadioButton>(&get_theme().body_font, RadioButton::Orientation::VERTICAL);
|
||||
auto enable_hotkeys_radio_button = std::make_unique<RadioButton>(get_theme().body_font_desc.c_str(), RadioButton::Orientation::VERTICAL);
|
||||
enable_keyboard_hotkeys_radio_button_ptr = enable_hotkeys_radio_button.get();
|
||||
enable_hotkeys_radio_button->add_item(TR("Yes"), "enable_hotkeys");
|
||||
enable_hotkeys_radio_button->add_item(TR("Yes, but only grab virtual devices (supports some input remapping software)"), "enable_hotkeys_virtual_devices");
|
||||
enable_hotkeys_radio_button->add_item(TR("Yes, but don't grab devices (supports all input remapping software)"), "enable_hotkeys_no_grab");
|
||||
enable_hotkeys_radio_button->add_item(TR("No"), "disable_hotkeys");
|
||||
enable_hotkeys_radio_button->on_selection_changed = [&](const std::string&, const std::string &id) {
|
||||
enable_hotkeys_radio_button->on_selection_changed = [&](std::string_view, std::string_view id) {
|
||||
if(on_keyboard_hotkey_changed)
|
||||
on_keyboard_hotkey_changed(id.c_str());
|
||||
on_keyboard_hotkey_changed(id);
|
||||
return true;
|
||||
};
|
||||
return enable_hotkeys_radio_button;
|
||||
}
|
||||
|
||||
std::unique_ptr<RadioButton> GlobalSettingsPage::create_enable_joystick_hotkeys_button() {
|
||||
auto enable_hotkeys_radio_button = std::make_unique<RadioButton>(&get_theme().body_font, RadioButton::Orientation::HORIZONTAL);
|
||||
auto enable_hotkeys_radio_button = std::make_unique<RadioButton>(get_theme().body_font_desc.c_str(), RadioButton::Orientation::HORIZONTAL);
|
||||
enable_joystick_hotkeys_radio_button_ptr = enable_hotkeys_radio_button.get();
|
||||
enable_hotkeys_radio_button->add_item(TR("Yes"), "enable_hotkeys");
|
||||
enable_hotkeys_radio_button->add_item(TR("No"), "disable_hotkeys");
|
||||
enable_hotkeys_radio_button->on_selection_changed = [&](const std::string&, const std::string &id) {
|
||||
enable_hotkeys_radio_button->on_selection_changed = [&](std::string_view, std::string_view id) {
|
||||
if(on_joystick_hotkey_changed)
|
||||
on_joystick_hotkey_changed(id.c_str());
|
||||
on_joystick_hotkey_changed(id);
|
||||
return true;
|
||||
};
|
||||
return enable_hotkeys_radio_button;
|
||||
@@ -222,8 +221,8 @@ namespace gsr {
|
||||
std::unique_ptr<List> GlobalSettingsPage::create_show_hide_hotkey_options() {
|
||||
auto list = std::make_unique<List>(List::Orientation::HORIZONTAL, List::Alignment::CENTER);
|
||||
|
||||
list->add_widget(std::make_unique<Label>(&get_theme().body_font, TR("Show/hide UI:"), get_color_theme().text_color));
|
||||
auto show_hide_button = std::make_unique<Button>(&get_theme().body_font, "", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
|
||||
list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("Show/hide UI:"), get_color_theme().text_color));
|
||||
auto show_hide_button = std::make_unique<Button>(get_theme().body_font_desc.c_str(), "", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
|
||||
show_hide_button_ptr = show_hide_button.get();
|
||||
list->add_widget(std::move(show_hide_button));
|
||||
|
||||
@@ -237,13 +236,13 @@ namespace gsr {
|
||||
std::unique_ptr<List> GlobalSettingsPage::create_replay_hotkey_options() {
|
||||
auto list = std::make_unique<List>(List::Orientation::HORIZONTAL, List::Alignment::CENTER);
|
||||
|
||||
list->add_widget(std::make_unique<Label>(&get_theme().body_font, TR("Turn replay on/off:"), get_color_theme().text_color));
|
||||
auto turn_replay_on_off_button = std::make_unique<Button>(&get_theme().body_font, "", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
|
||||
list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("Turn replay on/off:"), get_color_theme().text_color));
|
||||
auto turn_replay_on_off_button = std::make_unique<Button>(get_theme().body_font_desc.c_str(), "", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
|
||||
turn_replay_on_off_button_ptr = turn_replay_on_off_button.get();
|
||||
list->add_widget(std::move(turn_replay_on_off_button));
|
||||
|
||||
list->add_widget(std::make_unique<Label>(&get_theme().body_font, TR("Save replay:"), get_color_theme().text_color));
|
||||
auto save_replay_button = std::make_unique<Button>(&get_theme().body_font, "", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
|
||||
list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("Save replay:"), get_color_theme().text_color));
|
||||
auto save_replay_button = std::make_unique<Button>(get_theme().body_font_desc.c_str(), "", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
|
||||
save_replay_button_ptr = save_replay_button.get();
|
||||
list->add_widget(std::move(save_replay_button));
|
||||
|
||||
@@ -261,13 +260,13 @@ namespace gsr {
|
||||
std::unique_ptr<List> GlobalSettingsPage::create_replay_partial_save_hotkey_options() {
|
||||
auto list = std::make_unique<List>(List::Orientation::HORIZONTAL, List::Alignment::CENTER);
|
||||
|
||||
list->add_widget(std::make_unique<Label>(&get_theme().body_font, TR("Save 1 minute replay:"), get_color_theme().text_color));
|
||||
auto save_replay_1_min_button = std::make_unique<Button>(&get_theme().body_font, "", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
|
||||
list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("Save 1 minute replay:"), get_color_theme().text_color));
|
||||
auto save_replay_1_min_button = std::make_unique<Button>(get_theme().body_font_desc.c_str(), "", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
|
||||
save_replay_1_min_button_ptr = save_replay_1_min_button.get();
|
||||
list->add_widget(std::move(save_replay_1_min_button));
|
||||
|
||||
list->add_widget(std::make_unique<Label>(&get_theme().body_font, TR("Save 10 minute replay:"), get_color_theme().text_color));
|
||||
auto save_replay_10_min_button = std::make_unique<Button>(&get_theme().body_font, "", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
|
||||
list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("Save 10 minute replay:"), get_color_theme().text_color));
|
||||
auto save_replay_10_min_button = std::make_unique<Button>(get_theme().body_font_desc.c_str(), "", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
|
||||
save_replay_10_min_button_ptr = save_replay_10_min_button.get();
|
||||
list->add_widget(std::move(save_replay_10_min_button));
|
||||
|
||||
@@ -285,13 +284,13 @@ namespace gsr {
|
||||
std::unique_ptr<List> GlobalSettingsPage::create_record_hotkey_options() {
|
||||
auto list = std::make_unique<List>(List::Orientation::HORIZONTAL, List::Alignment::CENTER);
|
||||
|
||||
list->add_widget(std::make_unique<Label>(&get_theme().body_font, TR("Start/stop recording:"), get_color_theme().text_color));
|
||||
auto start_stop_recording_button = std::make_unique<Button>(&get_theme().body_font, "", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
|
||||
list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("Start/stop recording:"), get_color_theme().text_color));
|
||||
auto start_stop_recording_button = std::make_unique<Button>(get_theme().body_font_desc.c_str(), "", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
|
||||
start_stop_recording_button_ptr = start_stop_recording_button.get();
|
||||
list->add_widget(std::move(start_stop_recording_button));
|
||||
|
||||
list->add_widget(std::make_unique<Label>(&get_theme().body_font, TR("Pause/unpause recording:"), get_color_theme().text_color));
|
||||
auto pause_unpause_recording_button = std::make_unique<Button>(&get_theme().body_font, "", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
|
||||
list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("Pause/unpause recording:"), get_color_theme().text_color));
|
||||
auto pause_unpause_recording_button = std::make_unique<Button>(get_theme().body_font_desc.c_str(), "", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
|
||||
pause_unpause_recording_button_ptr = pause_unpause_recording_button.get();
|
||||
list->add_widget(std::move(pause_unpause_recording_button));
|
||||
|
||||
@@ -309,8 +308,8 @@ namespace gsr {
|
||||
std::unique_ptr<List> GlobalSettingsPage::create_record_hotkey_window_region_options() {
|
||||
auto list = std::make_unique<List>(List::Orientation::HORIZONTAL, List::Alignment::CENTER);
|
||||
|
||||
list->add_widget(std::make_unique<Label>(&get_theme().body_font, TR("Start/stop recording a region:"), get_color_theme().text_color));
|
||||
auto start_stop_recording_region_button = std::make_unique<Button>(&get_theme().body_font, "", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
|
||||
list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("Start/stop recording a region:"), get_color_theme().text_color));
|
||||
auto start_stop_recording_region_button = std::make_unique<Button>(get_theme().body_font_desc.c_str(), "", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
|
||||
start_stop_recording_region_button_ptr = start_stop_recording_region_button.get();
|
||||
list->add_widget(std::move(start_stop_recording_region_button));
|
||||
|
||||
@@ -330,8 +329,8 @@ namespace gsr {
|
||||
else
|
||||
snprintf(str, sizeof(str), "%s", TR("Start/stop recording with desktop portal:"));
|
||||
|
||||
list->add_widget(std::make_unique<Label>(&get_theme().body_font, str, get_color_theme().text_color));
|
||||
auto start_stop_recording_window_button = std::make_unique<Button>(&get_theme().body_font, "", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
|
||||
list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), str, get_color_theme().text_color));
|
||||
auto start_stop_recording_window_button = std::make_unique<Button>(get_theme().body_font_desc.c_str(), "", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
|
||||
start_stop_recording_window_button_ptr = start_stop_recording_window_button.get();
|
||||
list->add_widget(std::move(start_stop_recording_window_button));
|
||||
|
||||
@@ -345,8 +344,8 @@ namespace gsr {
|
||||
std::unique_ptr<List> GlobalSettingsPage::create_stream_hotkey_options() {
|
||||
auto list = std::make_unique<List>(List::Orientation::HORIZONTAL, List::Alignment::CENTER);
|
||||
|
||||
list->add_widget(std::make_unique<Label>(&get_theme().body_font, TR("Start/stop streaming:"), get_color_theme().text_color));
|
||||
auto start_stop_streaming_button = std::make_unique<Button>(&get_theme().body_font, "", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
|
||||
list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("Start/stop streaming:"), get_color_theme().text_color));
|
||||
auto start_stop_streaming_button = std::make_unique<Button>(get_theme().body_font_desc.c_str(), "", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
|
||||
start_stop_streaming_button_ptr = start_stop_streaming_button.get();
|
||||
list->add_widget(std::move(start_stop_streaming_button));
|
||||
|
||||
@@ -360,8 +359,8 @@ namespace gsr {
|
||||
std::unique_ptr<List> GlobalSettingsPage::create_screenshot_hotkey_options() {
|
||||
auto list = std::make_unique<List>(List::Orientation::HORIZONTAL, List::Alignment::CENTER);
|
||||
|
||||
list->add_widget(std::make_unique<Label>(&get_theme().body_font, TR("Take a screenshot:"), get_color_theme().text_color));
|
||||
auto take_screenshot_button = std::make_unique<Button>(&get_theme().body_font, "", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
|
||||
list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("Take a screenshot:"), get_color_theme().text_color));
|
||||
auto take_screenshot_button = std::make_unique<Button>(get_theme().body_font_desc.c_str(), "", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
|
||||
take_screenshot_button_ptr = take_screenshot_button.get();
|
||||
list->add_widget(std::move(take_screenshot_button));
|
||||
|
||||
@@ -375,8 +374,8 @@ namespace gsr {
|
||||
std::unique_ptr<List> GlobalSettingsPage::create_screenshot_region_hotkey_options() {
|
||||
auto list = std::make_unique<List>(List::Orientation::HORIZONTAL, List::Alignment::CENTER);
|
||||
|
||||
list->add_widget(std::make_unique<Label>(&get_theme().body_font, TR("Take a screenshot of a region:"), get_color_theme().text_color));
|
||||
auto take_screenshot_region_button = std::make_unique<Button>(&get_theme().body_font, "", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
|
||||
list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("Take a screenshot of a region:"), get_color_theme().text_color));
|
||||
auto take_screenshot_region_button = std::make_unique<Button>(get_theme().body_font_desc.c_str(), "", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
|
||||
take_screenshot_region_button_ptr = take_screenshot_region_button.get();
|
||||
list->add_widget(std::move(take_screenshot_region_button));
|
||||
|
||||
@@ -396,8 +395,8 @@ namespace gsr {
|
||||
else
|
||||
snprintf(str, sizeof(str), "%s", TR("Take a screenshot with desktop portal:"));
|
||||
|
||||
list->add_widget(std::make_unique<Label>(&get_theme().body_font, str, get_color_theme().text_color));
|
||||
auto take_screenshot_window_button = std::make_unique<Button>(&get_theme().body_font, "", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
|
||||
list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), str, get_color_theme().text_color));
|
||||
auto take_screenshot_window_button = std::make_unique<Button>(get_theme().body_font_desc.c_str(), "", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
|
||||
take_screenshot_window_button_ptr = take_screenshot_window_button.get();
|
||||
list->add_widget(std::move(take_screenshot_window_button));
|
||||
|
||||
@@ -411,7 +410,7 @@ namespace gsr {
|
||||
std::unique_ptr<List> GlobalSettingsPage::create_hotkey_control_buttons() {
|
||||
auto list = std::make_unique<List>(List::Orientation::HORIZONTAL, List::Alignment::CENTER);
|
||||
|
||||
auto clear_hotkeys_button = std::make_unique<Button>(&get_theme().body_font, TR("Clear hotkeys"), mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
|
||||
auto clear_hotkeys_button = std::make_unique<Button>(get_theme().body_font_desc.c_str(), TR("Clear hotkeys"), mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
|
||||
clear_hotkeys_button->on_click = [this] {
|
||||
for_each_config_hotkey([&](ConfigHotkey *config_hotkey_item) {
|
||||
*config_hotkey_item = {mgl::Keyboard::Unknown, 0};
|
||||
@@ -421,7 +420,7 @@ namespace gsr {
|
||||
};
|
||||
list->add_widget(std::move(clear_hotkeys_button));
|
||||
|
||||
auto reset_hotkeys_button = std::make_unique<Button>(&get_theme().body_font, TR("Reset hotkeys to default"), mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
|
||||
auto reset_hotkeys_button = std::make_unique<Button>(get_theme().body_font_desc.c_str(), TR("Reset hotkeys to default"), mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
|
||||
reset_hotkeys_button->on_click = [this] {
|
||||
config.set_hotkeys_to_default();
|
||||
load_hotkeys();
|
||||
@@ -434,11 +433,11 @@ namespace gsr {
|
||||
|
||||
static std::unique_ptr<List> create_joystick_hotkey_text(mgl::Texture *image1, mgl::Texture *image2, float max_height, const char *suffix) {
|
||||
auto list = std::make_unique<List>(List::Orientation::HORIZONTAL, List::Alignment::CENTER);
|
||||
list->add_widget(std::make_unique<Label>(&get_theme().body_font, TR("Press"), get_color_theme().text_color));
|
||||
list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("Press"), get_color_theme().text_color));
|
||||
list->add_widget(std::make_unique<Image>(image1, mgl::vec2f{max_height, 1000.0f}, Image::ScaleBehavior::SCALE));
|
||||
list->add_widget(std::make_unique<Label>(&get_theme().body_font, TR("and"), get_color_theme().text_color));
|
||||
list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("and"), get_color_theme().text_color));
|
||||
list->add_widget(std::make_unique<Image>(image2, mgl::vec2f{max_height, 1000.0f}, Image::ScaleBehavior::SCALE));
|
||||
list->add_widget(std::make_unique<Label>(&get_theme().body_font, suffix, get_color_theme().text_color));
|
||||
list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), suffix, get_color_theme().text_color));
|
||||
return list;
|
||||
}
|
||||
|
||||
@@ -447,7 +446,7 @@ namespace gsr {
|
||||
List *list_ptr = list.get();
|
||||
auto subsection = std::make_unique<Subsection>(TR("Keyboard hotkeys"), std::move(list), mgl::vec2f(parent_page->get_inner_size().x, 0.0f));
|
||||
|
||||
list_ptr->add_widget(std::make_unique<Label>(&get_theme().body_font, TR("Enable keyboard hotkeys?"), get_color_theme().text_color));
|
||||
list_ptr->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("Enable keyboard hotkeys?"), get_color_theme().text_color));
|
||||
list_ptr->add_widget(create_enable_keyboard_hotkeys_button());
|
||||
list_ptr->add_widget(std::make_unique<LineSeparator>(LineSeparator::Orientation::HORIZONTAL, subsection->get_inner_size().x));
|
||||
list_ptr->add_widget(create_show_hide_hotkey_options());
|
||||
@@ -460,6 +459,7 @@ namespace gsr {
|
||||
list_ptr->add_widget(create_screenshot_hotkey_options());
|
||||
list_ptr->add_widget(create_screenshot_region_hotkey_options());
|
||||
list_ptr->add_widget(create_screenshot_window_hotkey_options());
|
||||
list_ptr->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("Press ESC to go back to the previous page/close the UI."), get_color_theme().text_color));
|
||||
list_ptr->add_widget(create_hotkey_control_buttons());
|
||||
return subsection;
|
||||
}
|
||||
@@ -469,21 +469,21 @@ namespace gsr {
|
||||
List *list_ptr = list.get();
|
||||
auto subsection = std::make_unique<Subsection>(TR("Controller hotkeys"), std::move(list), mgl::vec2f(parent_page->get_inner_size().x, 0.0f));
|
||||
|
||||
list_ptr->add_widget(std::make_unique<Label>(&get_theme().body_font, TR("Enable controller hotkeys?"), get_color_theme().text_color));
|
||||
list_ptr->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("Enable controller hotkeys?"), get_color_theme().text_color));
|
||||
list_ptr->add_widget(create_enable_joystick_hotkeys_button());
|
||||
list_ptr->add_widget(std::make_unique<LineSeparator>(LineSeparator::Orientation::HORIZONTAL, subsection->get_inner_size().x));
|
||||
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(), TR("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(), TR("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(), TR("to save a 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(), TR("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(), TR("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(), TR("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(), TR("to save a 10 minute replay")));
|
||||
list_ptr->add_widget(create_joystick_hotkey_text(&get_theme().ps4_home_texture, &get_theme().ps4_options_texture, 2.0f*mgl::Text::get_font_size_from_font_description(get_theme().body_font_desc.c_str()), TR("to show/hide the UI")));
|
||||
list_ptr->add_widget(create_joystick_hotkey_text(&get_theme().ps4_home_texture, &get_theme().ps4_dpad_up_texture, 2.0f*mgl::Text::get_font_size_from_font_description(get_theme().body_font_desc.c_str()), TR("to take a screenshot")));
|
||||
list_ptr->add_widget(create_joystick_hotkey_text(&get_theme().ps4_home_texture, &get_theme().ps4_dpad_down_texture, 2.0f*mgl::Text::get_font_size_from_font_description(get_theme().body_font_desc.c_str()), TR("to save a replay")));
|
||||
list_ptr->add_widget(create_joystick_hotkey_text(&get_theme().ps4_home_texture, &get_theme().ps4_dpad_left_texture, 2.0f*mgl::Text::get_font_size_from_font_description(get_theme().body_font_desc.c_str()), TR("to start/stop recording")));
|
||||
list_ptr->add_widget(create_joystick_hotkey_text(&get_theme().ps4_home_texture, &get_theme().ps4_dpad_right_texture, 2.0f*mgl::Text::get_font_size_from_font_description(get_theme().body_font_desc.c_str()), TR("to turn replay on/off")));
|
||||
list_ptr->add_widget(create_joystick_hotkey_text(&get_theme().ps4_home_texture, &get_theme().ps4_cross_texture, 2.0f*mgl::Text::get_font_size_from_font_description(get_theme().body_font_desc.c_str()), TR("to save a 1 minute replay")));
|
||||
list_ptr->add_widget(create_joystick_hotkey_text(&get_theme().ps4_home_texture, &get_theme().ps4_triangle_texture, 2.0f*mgl::Text::get_font_size_from_font_description(get_theme().body_font_desc.c_str()), TR("to save a 10 minute replay")));
|
||||
return subsection;
|
||||
}
|
||||
|
||||
std::unique_ptr<Button> GlobalSettingsPage::create_exit_program_button() {
|
||||
auto exit_program_button = std::make_unique<Button>(&get_theme().body_font, TR("Exit program"), mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
|
||||
auto exit_program_button = std::make_unique<Button>(get_theme().body_font_desc.c_str(), TR("Exit program"), mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
|
||||
exit_program_button->on_click = [&]() {
|
||||
if(on_click_exit_program_button)
|
||||
on_click_exit_program_button("exit");
|
||||
@@ -492,7 +492,7 @@ namespace gsr {
|
||||
}
|
||||
|
||||
std::unique_ptr<Button> GlobalSettingsPage::create_go_back_to_old_ui_button() {
|
||||
auto exit_program_button = std::make_unique<Button>(&get_theme().body_font, TR("Go back to the old UI"), mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
|
||||
auto exit_program_button = std::make_unique<Button>(get_theme().body_font_desc.c_str(), TR("Go back to the old UI"), mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
|
||||
exit_program_button->on_click = [&]() {
|
||||
if(on_click_exit_program_button)
|
||||
on_click_exit_program_button("back-to-old-ui");
|
||||
@@ -502,13 +502,13 @@ namespace gsr {
|
||||
|
||||
std::unique_ptr<List> GlobalSettingsPage::create_notification_speed() {
|
||||
auto list = std::make_unique<List>(List::Orientation::VERTICAL);
|
||||
list->add_widget(std::make_unique<Label>(&get_theme().body_font, TR("Notification speed"), get_color_theme().text_color));
|
||||
list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("Notification speed"), get_color_theme().text_color));
|
||||
|
||||
auto radio_button = std::make_unique<RadioButton>(&get_theme().body_font, RadioButton::Orientation::HORIZONTAL);
|
||||
auto radio_button = std::make_unique<RadioButton>(get_theme().body_font_desc.c_str(), RadioButton::Orientation::HORIZONTAL);
|
||||
notification_speed_button_ptr = radio_button.get();
|
||||
radio_button->add_item(TR("Normal"), "normal");
|
||||
radio_button->add_item(TR("Fast"), "fast");
|
||||
radio_button->on_selection_changed = [this](const std::string&, const std::string &id) {
|
||||
radio_button->on_selection_changed = [this](std::string_view, std::string_view id) {
|
||||
if(id == "normal")
|
||||
overlay->set_notification_speed(NotificationSpeed::NORMAL);
|
||||
else if(id == "fast")
|
||||
@@ -522,18 +522,20 @@ namespace gsr {
|
||||
|
||||
std::unique_ptr<List> GlobalSettingsPage::create_language() {
|
||||
auto list = std::make_unique<List>(List::Orientation::VERTICAL);
|
||||
list->add_widget(std::make_unique<Label>(&get_theme().body_font, TR("Language"), get_color_theme().text_color));
|
||||
list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("Language"), get_color_theme().text_color));
|
||||
|
||||
auto combo_box = std::make_unique<ComboBox>(&get_theme().body_font);
|
||||
auto combo_box = std::make_unique<ComboBox>(get_theme().body_font_desc.c_str());
|
||||
language_combo_box_ptr = combo_box.get();
|
||||
combo_box->add_item(TR("System language"), "");
|
||||
combo_box->add_item("English", "en");
|
||||
combo_box->add_item("Español", "es");
|
||||
combo_box->add_item("Français", "fr");
|
||||
combo_box->add_item("Magyar", "hu");
|
||||
combo_box->add_item("日本語", "ja");
|
||||
combo_box->add_item("Русский", "ru");
|
||||
combo_box->add_item("Українська", "uk");
|
||||
combo_box->on_selection_changed = [](const std::string&, const std::string &id) {
|
||||
Translation::instance().load_language(id.c_str());
|
||||
combo_box->on_selection_changed = [](std::string_view, std::string_view id) {
|
||||
Translation::instance().load_language(id);
|
||||
return true;
|
||||
};
|
||||
list->add_widget(std::move(combo_box));
|
||||
@@ -572,27 +574,27 @@ namespace gsr {
|
||||
char str[128];
|
||||
const std::string gsr_version = gsr_info->system_info.gsr_version.to_string();
|
||||
snprintf(str, sizeof(str), TR("GSR version: %s"), gsr_version.c_str());
|
||||
list->add_widget(std::make_unique<Label>(&get_theme().body_font, str, get_color_theme().text_color));
|
||||
list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), str, get_color_theme().text_color));
|
||||
|
||||
snprintf(str, sizeof(str), TR("GSR-UI version: %s"), GSR_UI_VERSION);
|
||||
list->add_widget(std::make_unique<Label>(&get_theme().body_font, str, get_color_theme().text_color));
|
||||
list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), str, get_color_theme().text_color));
|
||||
|
||||
if(inside_flatpak) {
|
||||
snprintf(str, sizeof(str), TR("Flatpak version: %s"), GSR_FLATPAK_VERSION);
|
||||
list->add_widget(std::make_unique<Label>(&get_theme().body_font, str, get_color_theme().text_color));
|
||||
list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), str, get_color_theme().text_color));
|
||||
}
|
||||
|
||||
snprintf(str, sizeof(str), TR("GPU vendor: %s"), gpu_vendor_to_string(gsr_info->gpu_info.vendor));
|
||||
list->add_widget(std::make_unique<Label>(&get_theme().body_font, str, get_color_theme().text_color));
|
||||
list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), str, get_color_theme().text_color));
|
||||
|
||||
return std::make_unique<Subsection>(TR("Application info"), std::move(list), mgl::vec2f(parent_page->get_inner_size().x, 0.0f));
|
||||
}
|
||||
|
||||
std::unique_ptr<Subsection> GlobalSettingsPage::create_donate_subsection(ScrollablePage *parent_page) {
|
||||
auto list = std::make_unique<List>(List::Orientation::VERTICAL);
|
||||
list->add_widget(std::make_unique<Label>(&get_theme().body_font, TR("If you would like to donate you can do so by donating at https://buymeacoffee.com/dec05eba:"), get_color_theme().text_color));
|
||||
list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("If you would like to donate you can do so by donating at https://buymeacoffee.com/dec05eba:"), get_color_theme().text_color));
|
||||
|
||||
auto donate_button = std::make_unique<Button>(&get_theme().body_font, TR("Donate"), mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
|
||||
auto donate_button = std::make_unique<Button>(get_theme().body_font_desc.c_str(), TR("Donate"), mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
|
||||
donate_button->on_click = [this] {
|
||||
const char *args[] = { "xdg-open", "https://buymeacoffee.com/dec05eba", nullptr };
|
||||
exec_program_daemonized(args);
|
||||
@@ -600,7 +602,7 @@ namespace gsr {
|
||||
};
|
||||
list->add_widget(std::move(donate_button));
|
||||
|
||||
list->add_widget(std::make_unique<Label>(&get_theme().body_font, TR("All donations go toward developing software (including GPU Screen Recorder)\nand buying hardware to test the software."), get_color_theme().text_color));
|
||||
list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("All donations go toward developing software (including GPU Screen Recorder)\nand buying hardware to test the software."), get_color_theme().text_color));
|
||||
return std::make_unique<Subsection>(TR("Donate"), std::move(list), mgl::vec2f(parent_page->get_inner_size().x, 0.0f));
|
||||
}
|
||||
|
||||
@@ -633,10 +635,7 @@ namespace gsr {
|
||||
else
|
||||
tint_color_radio_button_ptr->set_selected_item(config.main_config.tint_color);
|
||||
|
||||
const char *args[] = { "systemctl", "is-enabled", "--quiet", "--user", "gpu-screen-recorder-ui", nullptr };
|
||||
std::string stdout_str;
|
||||
const int exit_status = exec_program_on_host_get_stdout(args, stdout_str);
|
||||
startup_radio_button_ptr->set_selected_item(exit_status == 0 ? "start_on_system_startup" : "dont_start_on_system_startup", false, false);
|
||||
startup_radio_button_ptr->set_selected_item(is_xdg_autostart_enabled() ? "start_on_system_startup" : "dont_start_on_system_startup", false, false);
|
||||
|
||||
enable_keyboard_hotkeys_radio_button_ptr->set_selected_item(config.main_config.hotkeys_enable_option, false, false);
|
||||
enable_joystick_hotkeys_radio_button_ptr->set_selected_item(config.main_config.joystick_hotkeys_enable_option, false, false);
|
||||
@@ -904,7 +903,7 @@ namespace gsr {
|
||||
}
|
||||
|
||||
if(hotkey_used_by_another_action) {
|
||||
const std::string error_msg = TR("The hotkey \"") + configure_config_hotkey.to_string() + TR("\" is already used for something else");
|
||||
const std::string error_msg = TRF("The hotkey %s is already used for something else", configure_config_hotkey.to_string().c_str());
|
||||
overlay->show_notification(error_msg.c_str(), 3.0, mgl::Color(255, 0, 0, 255), mgl::Color(255, 0, 0, 255), NotificationType::NONE);
|
||||
config_hotkey_button->set_text(config_hotkey->to_string());
|
||||
configure_config_hotkey = {0, 0};
|
||||
|
||||
@@ -9,8 +9,8 @@ namespace gsr {
|
||||
static const float button_spacing_scale = 0.015f;
|
||||
|
||||
GsrPage::GsrPage(const char *top_text, const char *bottom_text) :
|
||||
top_text(top_text, get_theme().title_font),
|
||||
bottom_text(bottom_text, get_theme().title_font)
|
||||
top_text(top_text, get_theme().title_font_desc.c_str()),
|
||||
bottom_text(bottom_text, get_theme().title_font_desc.c_str())
|
||||
{
|
||||
const float margin = 0.02f;
|
||||
set_margins(margin, margin, margin, margin);
|
||||
@@ -151,7 +151,7 @@ namespace gsr {
|
||||
}
|
||||
|
||||
void GsrPage::add_button(const std::string &text, const std::string &id, mgl::Color color) {
|
||||
auto button = std::make_unique<Button>(&get_theme().title_font, text.c_str(),
|
||||
auto button = std::make_unique<Button>(get_theme().title_font_desc.c_str(), text.c_str(),
|
||||
mgl::vec2f(get_theme().window_width / 10, get_theme().window_height / 15).floor(), color);
|
||||
button->set_border_scale(0.003f);
|
||||
button->on_click = [this, id]() {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#include <mglpp/window/Window.hpp>
|
||||
|
||||
namespace gsr {
|
||||
Label::Label(mgl::Font *font, const char *text, mgl::Color color) : text(text, *font) {
|
||||
Label::Label(const char *font_desc, const char *text, mgl::Color color) : text(text, font_desc) {
|
||||
this->text.set_color(color);
|
||||
}
|
||||
|
||||
@@ -18,11 +18,11 @@ namespace gsr {
|
||||
window.draw(text);
|
||||
}
|
||||
|
||||
void Label::set_text(std::string str) {
|
||||
text.set_string(std::move(str));
|
||||
void Label::set_text(std::string_view str) {
|
||||
text.set_string(str);
|
||||
}
|
||||
|
||||
const std::string& Label::get_text() const {
|
||||
std::string_view Label::get_text() const {
|
||||
return text.get_string();
|
||||
}
|
||||
|
||||
@@ -32,4 +32,14 @@ namespace gsr {
|
||||
|
||||
return text.get_bounds().size;
|
||||
}
|
||||
|
||||
// Set to 0 to disable
|
||||
void Label::set_wrap_width(int width) {
|
||||
text.set_wrap_width(width);
|
||||
}
|
||||
|
||||
// Set to 0 to disable
|
||||
void Label::set_max_rows(int max_rows) {
|
||||
text.set_max_rows(max_rows);
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@ namespace gsr {
|
||||
static const float spacing_scale = 0.007f;
|
||||
static const float border_scale = 0.0015f;
|
||||
|
||||
RadioButton::RadioButton(mgl::Font *font, Orientation orientation) : font(font), orientation(orientation) {
|
||||
RadioButton::RadioButton(const char *font_desc, Orientation orientation) : font_desc(font_desc), orientation(orientation) {
|
||||
|
||||
}
|
||||
|
||||
@@ -122,7 +122,7 @@ namespace gsr {
|
||||
break;
|
||||
case Orientation::HORIZONTAL:
|
||||
size.x += bounds.x + padding_left + padding_right;
|
||||
size.y = font->get_character_size() + (float)padding_top + (float)padding_bottom;
|
||||
size.y = item.text.get_font_size()*2.0f + (float)padding_top + (float)padding_bottom;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -150,11 +150,11 @@ namespace gsr {
|
||||
}
|
||||
|
||||
void RadioButton::add_item(const std::string &text, const std::string &id) {
|
||||
items.push_back({mgl::Text(text, *font), id});
|
||||
items.push_back({mgl::Text(text, font_desc.c_str()), id});
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
void RadioButton::set_selected_item(const std::string &id, bool trigger_event, bool trigger_event_even_if_selection_not_changed) {
|
||||
void RadioButton::set_selected_item(std::string_view id, bool trigger_event, bool trigger_event_even_if_selection_not_changed) {
|
||||
for(size_t i = 0; i < items.size(); ++i) {
|
||||
auto &item = items[i];
|
||||
if(item.id == id) {
|
||||
@@ -169,19 +169,17 @@ namespace gsr {
|
||||
}
|
||||
}
|
||||
|
||||
const std::string& RadioButton::get_selected_id() const {
|
||||
std::string_view RadioButton::get_selected_id() const {
|
||||
if(items.empty()) {
|
||||
static std::string dummy;
|
||||
return dummy;
|
||||
return "";
|
||||
} else {
|
||||
return items[selected_item].id;
|
||||
}
|
||||
}
|
||||
|
||||
const std::string& RadioButton::get_selected_text() const {
|
||||
std::string_view RadioButton::get_selected_text() const {
|
||||
if(items.empty()) {
|
||||
static std::string dummy;
|
||||
return dummy;
|
||||
return "";
|
||||
} else {
|
||||
return items[selected_item].text.get_string();
|
||||
}
|
||||
|
||||
@@ -11,7 +11,16 @@
|
||||
#include "../../include/gui/Subsection.hpp"
|
||||
#include "../../include/gui/FileChooser.hpp"
|
||||
|
||||
#include <charconv>
|
||||
|
||||
namespace gsr {
|
||||
template <typename T>
|
||||
static T sv_to_int(std::string_view str) {
|
||||
T result = 0;
|
||||
std::from_chars(str.data(), str.data() + str.size(), result);
|
||||
return result;
|
||||
}
|
||||
|
||||
ScreenshotSettingsPage::ScreenshotSettingsPage(const GsrInfo *gsr_info, Config &config, PageStack *page_stack, bool supports_window_title) :
|
||||
StaticPage(mgl::vec2f(get_theme().window_width, get_theme().window_height).floor()),
|
||||
config(config),
|
||||
@@ -35,7 +44,7 @@ namespace gsr {
|
||||
}
|
||||
|
||||
std::unique_ptr<ComboBox> ScreenshotSettingsPage::create_record_area_box() {
|
||||
auto record_area_box = std::make_unique<ComboBox>(&get_theme().body_font);
|
||||
auto record_area_box = std::make_unique<ComboBox>(get_theme().body_font_desc.c_str());
|
||||
// TODO: Show options not supported but disable them
|
||||
if(capture_options.window)
|
||||
record_area_box->add_item(TR("Window"), "window");
|
||||
@@ -56,21 +65,21 @@ namespace gsr {
|
||||
|
||||
std::unique_ptr<Widget> ScreenshotSettingsPage::create_record_area() {
|
||||
auto record_area_list = std::make_unique<List>(List::Orientation::VERTICAL);
|
||||
record_area_list->add_widget(std::make_unique<Label>(&get_theme().body_font, TR("Capture source:"), get_color_theme().text_color));
|
||||
record_area_list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("Capture source:"), get_color_theme().text_color));
|
||||
record_area_list->add_widget(create_record_area_box());
|
||||
return record_area_list;
|
||||
}
|
||||
|
||||
std::unique_ptr<Entry> ScreenshotSettingsPage::create_image_width_entry() {
|
||||
auto image_width_entry = std::make_unique<Entry>(&get_theme().body_font, "1920", get_theme().body_font.get_character_size() * 3);
|
||||
image_width_entry->validate_handler = create_entry_validator_integer_in_range(1, 1 << 15);
|
||||
auto image_width_entry = std::make_unique<Entry>(get_theme().body_font_desc.c_str(), "1920", 2.0f*mgl::Text::get_font_size_from_font_description(get_theme().body_font_desc.c_str()) * 4);
|
||||
image_width_entry->set_number_mode(true, 1, 1 << 15);
|
||||
image_width_entry_ptr = image_width_entry.get();
|
||||
return image_width_entry;
|
||||
}
|
||||
|
||||
std::unique_ptr<Entry> ScreenshotSettingsPage::create_image_height_entry() {
|
||||
auto image_height_entry = std::make_unique<Entry>(&get_theme().body_font, "1080", get_theme().body_font.get_character_size() * 3);
|
||||
image_height_entry->validate_handler = create_entry_validator_integer_in_range(1, 1 << 15);
|
||||
auto image_height_entry = std::make_unique<Entry>(get_theme().body_font_desc.c_str(), "1080", 2.0f*mgl::Text::get_font_size_from_font_description(get_theme().body_font_desc.c_str()) * 4);
|
||||
image_height_entry->set_number_mode(true, 1, 1 << 15);
|
||||
image_height_entry_ptr = image_height_entry.get();
|
||||
return image_height_entry;
|
||||
}
|
||||
@@ -78,21 +87,21 @@ namespace gsr {
|
||||
std::unique_ptr<List> ScreenshotSettingsPage::create_image_resolution() {
|
||||
auto area_size_params_list = std::make_unique<List>(List::Orientation::HORIZONTAL, List::Alignment::CENTER);
|
||||
area_size_params_list->add_widget(create_image_width_entry());
|
||||
area_size_params_list->add_widget(std::make_unique<Label>(&get_theme().body_font, "x", get_color_theme().text_color));
|
||||
area_size_params_list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), "x", get_color_theme().text_color));
|
||||
area_size_params_list->add_widget(create_image_height_entry());
|
||||
return area_size_params_list;
|
||||
}
|
||||
|
||||
std::unique_ptr<List> ScreenshotSettingsPage::create_image_resolution_section() {
|
||||
auto image_resolution_list = std::make_unique<List>(List::Orientation::VERTICAL);
|
||||
image_resolution_list->add_widget(std::make_unique<Label>(&get_theme().body_font, TR("Image resolution limit:"), get_color_theme().text_color));
|
||||
image_resolution_list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("Image resolution limit:"), get_color_theme().text_color));
|
||||
image_resolution_list->add_widget(create_image_resolution());
|
||||
image_resolution_list_ptr = image_resolution_list.get();
|
||||
return image_resolution_list;
|
||||
}
|
||||
|
||||
std::unique_ptr<CheckBox> ScreenshotSettingsPage::create_restore_portal_session_checkbox() {
|
||||
auto restore_portal_session_checkbox = std::make_unique<CheckBox>(&get_theme().body_font, TR("Restore portal session"));
|
||||
auto restore_portal_session_checkbox = std::make_unique<CheckBox>(get_theme().body_font_desc.c_str(), TR("Restore portal session"));
|
||||
restore_portal_session_checkbox->set_checked(true);
|
||||
restore_portal_session_checkbox_ptr = restore_portal_session_checkbox.get();
|
||||
return restore_portal_session_checkbox;
|
||||
@@ -100,14 +109,14 @@ namespace gsr {
|
||||
|
||||
std::unique_ptr<List> ScreenshotSettingsPage::create_restore_portal_session_section() {
|
||||
auto restore_portal_session_list = std::make_unique<List>(List::Orientation::VERTICAL);
|
||||
restore_portal_session_list->add_widget(std::make_unique<Label>(&get_theme().body_font, " ", get_color_theme().text_color));
|
||||
restore_portal_session_list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), " ", get_color_theme().text_color));
|
||||
restore_portal_session_list->add_widget(create_restore_portal_session_checkbox());
|
||||
restore_portal_session_list_ptr = restore_portal_session_list.get();
|
||||
return restore_portal_session_list;
|
||||
}
|
||||
|
||||
std::unique_ptr<Widget> ScreenshotSettingsPage::create_change_image_resolution_section() {
|
||||
auto checkbox = std::make_unique<CheckBox>(&get_theme().body_font, TR("Change image resolution"));
|
||||
auto checkbox = std::make_unique<CheckBox>(get_theme().body_font_desc.c_str(), TR("Change image resolution"));
|
||||
change_image_resolution_checkbox_ptr = checkbox.get();
|
||||
return checkbox;
|
||||
}
|
||||
@@ -127,9 +136,9 @@ namespace gsr {
|
||||
|
||||
std::unique_ptr<List> ScreenshotSettingsPage::create_image_quality_section() {
|
||||
auto list = std::make_unique<List>(List::Orientation::VERTICAL);
|
||||
list->add_widget(std::make_unique<Label>(&get_theme().body_font, TR("Image quality:"), get_color_theme().text_color));
|
||||
list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("Image quality:"), get_color_theme().text_color));
|
||||
|
||||
auto image_quality_box = std::make_unique<ComboBox>(&get_theme().body_font);
|
||||
auto image_quality_box = std::make_unique<ComboBox>(get_theme().body_font_desc.c_str());
|
||||
image_quality_box->add_item(TR("Medium"), "medium");
|
||||
image_quality_box->add_item(TR("High"), "high");
|
||||
image_quality_box->add_item(TR("Very high (Recommended)"), "very_high");
|
||||
@@ -143,7 +152,7 @@ namespace gsr {
|
||||
}
|
||||
|
||||
std::unique_ptr<Widget> ScreenshotSettingsPage::create_record_cursor_section() {
|
||||
auto record_cursor_checkbox = std::make_unique<CheckBox>(&get_theme().body_font, TR("Record cursor"));
|
||||
auto record_cursor_checkbox = std::make_unique<CheckBox>(get_theme().body_font_desc.c_str(), TR("Record cursor"));
|
||||
record_cursor_checkbox->set_checked(true);
|
||||
record_cursor_checkbox_ptr = record_cursor_checkbox.get();
|
||||
return record_cursor_checkbox;
|
||||
@@ -158,15 +167,15 @@ namespace gsr {
|
||||
|
||||
std::unique_ptr<List> ScreenshotSettingsPage::create_save_directory(const char *label) {
|
||||
auto save_directory_list = std::make_unique<List>(List::Orientation::VERTICAL);
|
||||
save_directory_list->add_widget(std::make_unique<Label>(&get_theme().body_font, label, get_color_theme().text_color));
|
||||
auto save_directory_button = std::make_unique<Button>(&get_theme().body_font, get_pictures_dir().c_str(), mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
|
||||
save_directory_list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), label, get_color_theme().text_color));
|
||||
auto save_directory_button = std::make_unique<Button>(get_theme().body_font_desc.c_str(), get_pictures_dir().c_str(), mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
|
||||
save_directory_button_ptr = save_directory_button.get();
|
||||
save_directory_button->on_click = [this]() {
|
||||
auto select_directory_page = std::make_unique<GsrPage>(TR("File"), TR("Settings"));
|
||||
select_directory_page->add_button(TR("Save"), "save", get_color_theme().tint_color);
|
||||
select_directory_page->add_button(TR("Cancel"), "cancel", get_color_theme().page_bg_color);
|
||||
|
||||
auto file_chooser = std::make_unique<FileChooser>(save_directory_button_ptr->get_text().c_str(), select_directory_page->get_inner_size());
|
||||
auto file_chooser = std::make_unique<FileChooser>(save_directory_button_ptr->get_text(), select_directory_page->get_inner_size());
|
||||
FileChooser *file_chooser_ptr = file_chooser.get();
|
||||
select_directory_page->add_widget(std::move(file_chooser));
|
||||
|
||||
@@ -186,7 +195,7 @@ namespace gsr {
|
||||
}
|
||||
|
||||
std::unique_ptr<ComboBox> ScreenshotSettingsPage::create_image_format_box() {
|
||||
auto box = std::make_unique<ComboBox>(&get_theme().body_font);
|
||||
auto box = std::make_unique<ComboBox>(get_theme().body_font_desc.c_str());
|
||||
if(gsr_info->supported_image_formats.jpeg)
|
||||
box->add_item("jpg", "jpg");
|
||||
if(gsr_info->supported_image_formats.png)
|
||||
@@ -197,7 +206,7 @@ namespace gsr {
|
||||
|
||||
std::unique_ptr<List> ScreenshotSettingsPage::create_image_format_section() {
|
||||
auto list = std::make_unique<List>(List::Orientation::VERTICAL);
|
||||
list->add_widget(std::make_unique<Label>(&get_theme().body_font, TR("Image format:"), get_color_theme().text_color));
|
||||
list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("Image format:"), get_color_theme().text_color));
|
||||
list->add_widget(create_image_format_box());
|
||||
return list;
|
||||
}
|
||||
@@ -212,33 +221,33 @@ namespace gsr {
|
||||
std::unique_ptr<CheckBox> ScreenshotSettingsPage::create_save_screenshot_in_game_folder() {
|
||||
char text[256];
|
||||
snprintf(text, sizeof(text), "%s%s", TR("Save screenshot in a folder based on the games name"), supports_window_title ? "" : " (X11 applications only)");
|
||||
auto checkbox = std::make_unique<CheckBox>(&get_theme().body_font, text);
|
||||
auto checkbox = std::make_unique<CheckBox>(get_theme().body_font_desc.c_str(), text);
|
||||
save_screenshot_in_game_folder_checkbox_ptr = checkbox.get();
|
||||
return checkbox;
|
||||
}
|
||||
|
||||
std::unique_ptr<CheckBox> ScreenshotSettingsPage::create_save_screenshot_to_clipboard() {
|
||||
auto checkbox = std::make_unique<CheckBox>(&get_theme().body_font, gsr_info->system_info.display_server == DisplayServer::X11 ? TR("Save screenshot to clipboard") : TR("Save screenshot to clipboard (Not supported properly by Wayland)"));
|
||||
auto checkbox = std::make_unique<CheckBox>(get_theme().body_font_desc.c_str(), gsr_info->system_info.display_server == DisplayServer::X11 ? TR("Save screenshot to clipboard") : TR("Save screenshot to clipboard (Not supported properly by Wayland)"));
|
||||
save_screenshot_to_clipboard_checkbox_ptr = checkbox.get();
|
||||
return checkbox;
|
||||
}
|
||||
|
||||
std::unique_ptr<CheckBox> ScreenshotSettingsPage::create_save_screenshot_to_disk() {
|
||||
auto checkbox = std::make_unique<CheckBox>(&get_theme().body_font, TR("Save screenshot to disk"));
|
||||
auto checkbox = std::make_unique<CheckBox>(get_theme().body_font_desc.c_str(), TR("Save screenshot to disk"));
|
||||
save_screenshot_to_disk_checkbox_ptr = checkbox.get();
|
||||
checkbox->set_checked(true);
|
||||
return checkbox;
|
||||
}
|
||||
|
||||
std::unique_ptr<Widget> ScreenshotSettingsPage::create_notifications() {
|
||||
auto checkbox = std::make_unique<CheckBox>(&get_theme().body_font, TR("Show screenshot notifications"));
|
||||
auto checkbox = std::make_unique<CheckBox>(get_theme().body_font_desc.c_str(), TR("Show screenshot notifications"));
|
||||
checkbox->set_checked(true);
|
||||
show_notification_checkbox_ptr = checkbox.get();
|
||||
return checkbox;
|
||||
}
|
||||
|
||||
std::unique_ptr<Widget> ScreenshotSettingsPage::create_led_indicator() {
|
||||
auto checkbox = std::make_unique<CheckBox>(&get_theme().body_font, TR("Blink scroll lock led when taking a screenshot"));
|
||||
auto checkbox = std::make_unique<CheckBox>(get_theme().body_font_desc.c_str(), TR("Blink scroll lock led when taking a screenshot"));
|
||||
checkbox->set_checked(true);
|
||||
led_indicator_checkbox_ptr = checkbox.get();
|
||||
return checkbox;
|
||||
@@ -262,7 +271,7 @@ namespace gsr {
|
||||
std::unique_ptr<List> ScreenshotSettingsPage::create_custom_script_screenshot_entry() {
|
||||
auto list = std::make_unique<List>(List::Orientation::VERTICAL, List::Alignment::CENTER);
|
||||
|
||||
auto create_custom_script_screenshot_entry = std::make_unique<Entry>(&get_theme().body_font, "", get_theme().body_font.get_character_size() * 20);
|
||||
auto create_custom_script_screenshot_entry = std::make_unique<Entry>(get_theme().body_font_desc.c_str(), "", 2.0f*mgl::Text::get_font_size_from_font_description(get_theme().body_font_desc.c_str()) * 20);
|
||||
create_custom_script_screenshot_entry_ptr = create_custom_script_screenshot_entry.get();
|
||||
list->add_widget(std::move(create_custom_script_screenshot_entry));
|
||||
|
||||
@@ -271,7 +280,7 @@ namespace gsr {
|
||||
|
||||
std::unique_ptr<List> ScreenshotSettingsPage::create_custom_script_screenshot() {
|
||||
auto custom_script_screenshot_list = std::make_unique<List>(List::Orientation::VERTICAL);
|
||||
custom_script_screenshot_list->add_widget(std::make_unique<Label>(&get_theme().body_font, TR("Command to open the screenshot with:"), get_color_theme().text_color));
|
||||
custom_script_screenshot_list->add_widget(std::make_unique<Label>(get_theme().body_font_desc.c_str(), TR("Command to open the screenshot with:"), get_color_theme().text_color));
|
||||
custom_script_screenshot_list->add_widget(create_custom_script_screenshot_entry());
|
||||
return custom_script_screenshot_list;
|
||||
}
|
||||
@@ -302,7 +311,7 @@ namespace gsr {
|
||||
void ScreenshotSettingsPage::add_widgets() {
|
||||
content_page_ptr->add_widget(create_settings());
|
||||
|
||||
record_area_box_ptr->on_selection_changed = [this](const std::string&, const std::string &id) {
|
||||
record_area_box_ptr->on_selection_changed = [this](std::string_view, std::string_view id) {
|
||||
const bool portal_selected = id == "portal";
|
||||
image_resolution_list_ptr->set_visible(change_image_resolution_checkbox_ptr->is_checked());
|
||||
restore_portal_session_list_ptr->set_visible(portal_selected);
|
||||
@@ -362,8 +371,8 @@ namespace gsr {
|
||||
Config prev_config = config;
|
||||
|
||||
config.screenshot_config.record_area_option = record_area_box_ptr->get_selected_id();
|
||||
config.screenshot_config.image_width = atoi(image_width_entry_ptr->get_text().c_str());
|
||||
config.screenshot_config.image_height = atoi(image_height_entry_ptr->get_text().c_str());
|
||||
config.screenshot_config.image_width = sv_to_int<int32_t>(image_width_entry_ptr->get_text());
|
||||
config.screenshot_config.image_height = sv_to_int<int32_t>(image_height_entry_ptr->get_text());
|
||||
config.screenshot_config.change_image_resolution = change_image_resolution_checkbox_ptr->is_checked();
|
||||
config.screenshot_config.image_quality = image_quality_box_ptr->get_selected_id();
|
||||
config.screenshot_config.image_format = image_format_box_ptr->get_selected_id();
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -12,7 +12,7 @@ namespace gsr {
|
||||
static const float title_spacing_scale = 0.010f;
|
||||
|
||||
Subsection::Subsection(const char *title, std::unique_ptr<Widget> inner_widget, mgl::vec2f size) :
|
||||
label(&get_theme().title_font, title ? title : "", get_color_theme().text_color),
|
||||
label(get_theme().title_font_desc.c_str(), title ? title : "", get_color_theme().text_color),
|
||||
inner_widget(std::move(inner_widget)),
|
||||
size(size)
|
||||
{
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace gsr {
|
||||
static const float padding_right_scale = 0.008f;
|
||||
static const float accent_scale = 0.0025f;
|
||||
|
||||
Tooltip::Tooltip(mgl::Font *font) : label("", *font) {}
|
||||
Tooltip::Tooltip(const char *font_desc) : label("", font_desc) {}
|
||||
|
||||
bool Tooltip::on_event(mgl::Event&, mgl::Window&, mgl::vec2f) {
|
||||
return true;
|
||||
@@ -27,7 +27,7 @@ namespace gsr {
|
||||
const int padding_top = get_theme().window_height * padding_top_scale;
|
||||
const int padding_left = get_theme().window_height * padding_left_scale;
|
||||
const int accent_height = get_theme().window_height * accent_scale;
|
||||
const int icon_height = label.get_font()->get_character_size();
|
||||
const int icon_height = label.get_font_size()*2.0f;
|
||||
|
||||
mgl::Rectangle background(get_size());
|
||||
background.set_position(draw_pos - mgl::vec2f(0.0f, background.get_size().y));
|
||||
@@ -56,12 +56,12 @@ namespace gsr {
|
||||
const int padding_right = get_theme().window_height * padding_right_scale;
|
||||
const int accent_height = get_theme().window_height * accent_scale;
|
||||
const mgl::vec2f text_size = label.get_bounds().size.floor();
|
||||
const int icon_height = label.get_font()->get_character_size();
|
||||
const int icon_height = label.get_font_size()*2.0f;
|
||||
|
||||
return mgl::vec2f(padding_left + text_size.x + padding_right, accent_height + padding_top + icon_height + text_size.y + padding_bottom).floor();
|
||||
}
|
||||
|
||||
void Tooltip::set_text(std::string text) {
|
||||
label.set_string(std::move(text));
|
||||
void Tooltip::set_text(std::string_view text) {
|
||||
label.set_string(text);
|
||||
}
|
||||
}
|
||||
@@ -128,7 +128,7 @@ namespace gsr {
|
||||
if(widget && !widget->get_tooltip_text().empty()) {
|
||||
current_tooltip_widget = widget;
|
||||
if(!tooltip)
|
||||
tooltip = std::make_unique<Tooltip>(&get_theme().body_font);
|
||||
tooltip = std::make_unique<Tooltip>(get_theme().body_font_desc.c_str());
|
||||
tooltip->set_text(current_tooltip_widget->get_tooltip_text());
|
||||
} else {
|
||||
current_tooltip_widget = nullptr;
|
||||
|
||||
85
src/main.cpp
85
src/main.cpp
@@ -1,5 +1,6 @@
|
||||
#include "../include/GsrInfo.hpp"
|
||||
#include "../include/Overlay.hpp"
|
||||
#include "../include/Utils.hpp"
|
||||
#include "../include/gui/Utils.hpp"
|
||||
#include "../include/Process.hpp"
|
||||
#include "../include/Rpc.hpp"
|
||||
@@ -117,47 +118,6 @@ static void rpc_add_commands(gsr::Rpc *rpc, gsr::Overlay *overlay) {
|
||||
});
|
||||
}
|
||||
|
||||
static void install_flatpak_systemd_service() {
|
||||
const bool systemd_service_exists = system(
|
||||
"data_home=$(flatpak-spawn --host -- /bin/sh -c 'echo \"${XDG_DATA_HOME:-$HOME/.local/share}\"') && "
|
||||
"flatpak-spawn --host -- ls \"$data_home/systemd/user/gpu-screen-recorder-ui.service\"") == 0;
|
||||
if(systemd_service_exists)
|
||||
return;
|
||||
|
||||
bool service_install_successful = (system(
|
||||
"data_home=$(flatpak-spawn --host -- /bin/sh -c 'echo \"${XDG_DATA_HOME:-$HOME/.local/share}\"') && "
|
||||
"flatpak-spawn --host -- install -Dm644 /var/lib/flatpak/app/com.dec05eba.gpu_screen_recorder/current/active/files/share/gpu-screen-recorder/gpu-screen-recorder-ui.service \"$data_home/systemd/user/gpu-screen-recorder-ui.service\"") == 0);
|
||||
service_install_successful &= (system("flatpak-spawn --host -- systemctl --user daemon-reload") == 0);
|
||||
if(service_install_successful)
|
||||
fprintf(stderr, "Info: the systemd service file was missing. It has now been installed\n");
|
||||
else
|
||||
fprintf(stderr, "Error: the systemd service file is missing and failed to install it again\n");
|
||||
}
|
||||
|
||||
static void remove_flatpak_systemd_service() {
|
||||
char systemd_service_path[PATH_MAX];
|
||||
const char *xdg_data_home = getenv("XDG_DATA_HOME");
|
||||
const char *home = getenv("HOME");
|
||||
if(xdg_data_home) {
|
||||
snprintf(systemd_service_path, sizeof(systemd_service_path), "%s/systemd/user/gpu-screen-recorder-ui.service", xdg_data_home);
|
||||
} else if(home) {
|
||||
snprintf(systemd_service_path, sizeof(systemd_service_path), "%s/.local/share/systemd/user/gpu-screen-recorder-ui.service", home);
|
||||
} else {
|
||||
fprintf(stderr, "Error: failed to get user home directory\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if(access(systemd_service_path, F_OK) != 0)
|
||||
return;
|
||||
|
||||
remove(systemd_service_path);
|
||||
system("systemctl --user daemon-reload");
|
||||
fprintf(stderr, "Info: conflicting flatpak version of the systemd service for gsr-ui was found at \"%s\", it has now been removed\n", systemd_service_path);
|
||||
}
|
||||
|
||||
static bool is_flatpak() {
|
||||
return getenv("FLATPAK_ID") != nullptr;
|
||||
}
|
||||
|
||||
static void set_display_server_environment_variables() {
|
||||
// Some users dont have properly setup environments (no display manager that does systemctl --user import-environment DISPLAY WAYLAND_DISPLAY)
|
||||
@@ -189,7 +149,8 @@ enum class LaunchAction {
|
||||
LAUNCH_SHOW,
|
||||
LAUNCH_HIDE,
|
||||
LAUNCH_HIDE_ANNOUNCE,
|
||||
LAUNCH_DAEMON
|
||||
LAUNCH_DAEMON,
|
||||
INSTALL_STARTUP
|
||||
};
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
@@ -230,6 +191,8 @@ int main(int argc, char **argv) {
|
||||
launch_action = LaunchAction::LAUNCH_HIDE_ANNOUNCE;
|
||||
} else if(strcmp(launch_action_opt, "launch-daemon") == 0) {
|
||||
launch_action = LaunchAction::LAUNCH_DAEMON;
|
||||
} else if(strcmp(launch_action_opt, "install-startup") == 0) {
|
||||
launch_action = LaunchAction::INSTALL_STARTUP;
|
||||
} else {
|
||||
printf("error: invalid action \"%s\", expected \"launch-show\", \"launch-hide\", \"launch-hide-announce\" or \"launch-daemon\".\n", launch_action_opt);
|
||||
usage();
|
||||
@@ -238,6 +201,9 @@ int main(int argc, char **argv) {
|
||||
usage();
|
||||
}
|
||||
|
||||
if(launch_action == LaunchAction::INSTALL_STARTUP)
|
||||
return gsr::set_xdg_autostart(true);
|
||||
|
||||
set_display_server_environment_variables();
|
||||
|
||||
const std::string gsr_icon_path = resources_path + "images/gpu_screen_recorder_logo.png";
|
||||
@@ -254,7 +220,7 @@ int main(int argc, char **argv) {
|
||||
} else {
|
||||
fprintf(stderr, "Error: failed to send command to running gsr-ui instance, user will have to open the UI manually with Alt+Z\n");
|
||||
const char *args[] = {
|
||||
"gsr-notify", "--text", TR("Another instance of GPU Screen Recorder UI is already running.\nPress Alt+Z to open the UI."), "--timeout", "5.0",
|
||||
"gsr-notify", "--text", TR("Another instance of GPU Screen Recorder UI is already running. Press Alt+Z to open the UI."), "--timeout", "5.0",
|
||||
"--icon-color", "ffffff", "--icon", gsr_icon_path.c_str(), "--bg-color", "ff0000", nullptr
|
||||
};
|
||||
gsr::exec_program_daemonized(args);
|
||||
@@ -267,7 +233,7 @@ int main(int argc, char **argv) {
|
||||
|
||||
if(gsr::pidof("gpu-screen-recorder", -1) != -1) {
|
||||
const char *args[] = {
|
||||
"gsr-notify", "--text", TR("GPU Screen Recorder is already running in another process.\nPlease close it before using GPU Screen Recorder UI."),
|
||||
"gsr-notify", "--text", TR("GPU Screen Recorder is already running in another process. Please close it before using GPU Screen Recorder UI."),
|
||||
"--timeout", "5.0", "--icon-color", "ffffff", "--icon", gsr_icon_path.c_str(), "--bg-color", "ff0000", nullptr
|
||||
};
|
||||
gsr::exec_program_daemonized(args);
|
||||
@@ -278,11 +244,6 @@ int main(int argc, char **argv) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(is_flatpak())
|
||||
install_flatpak_systemd_service();
|
||||
else
|
||||
remove_flatpak_systemd_service();
|
||||
|
||||
// Stop nvidia driver from buffering frames
|
||||
setenv("__GL_MaxFramesAllowed", "1", true);
|
||||
// If this is set to 1 then cuGraphicsGLRegisterImage will fail for egl context with error: invalid OpenGL or DirectX context,
|
||||
@@ -346,6 +307,31 @@ int main(int argc, char **argv) {
|
||||
|
||||
rpc_add_commands(rpc.get(), overlay.get());
|
||||
|
||||
// Evacuating from lennart's botnet to XDG (a.k.a. 'the spec that nobody actually follows').
|
||||
// TODO: Remove all this garbage related to systemd in 1-2 months and remove the systemd service file as well.
|
||||
// This garbage shit is needed because the systemd user daemon isn't always available at xdg autostart o algo
|
||||
const bool systemd_service_ready = gsr::wait_until_systemd_user_service_available();
|
||||
constexpr const char *deprecated_systemd_service_name = "gpu-screen-recorder-ui.service";
|
||||
if(systemd_service_ready && gsr::is_systemd_service_enabled(deprecated_systemd_service_name)) {
|
||||
const int autostart_result = gsr::set_xdg_autostart(true);
|
||||
if(autostart_result == 67) {
|
||||
const bool is_flatpak = getenv("FLATPAK_ID") != nullptr;
|
||||
const char *startup_command = is_flatpak ? "flatpak run com.dec05eba.gpu_screen_recorder gsr-ui" : "gsr-ui launch-daemon";
|
||||
overlay->show_notification(
|
||||
TRF("GPU Screen Recorder UI autostart via systemd is deprecated. To migrate: install and configure 'dex' (recommended), or manually add '%s' to your desktop autostart entries.", startup_command).c_str(),
|
||||
10.0, mgl::Color(255, 255, 255), mgl::Color(255, 0, 0),
|
||||
gsr::NotificationType::NOTICE, nullptr, gsr::NotificationLevel::ERROR);
|
||||
} else {
|
||||
gsr::disable_systemd_service(deprecated_systemd_service_name);
|
||||
overlay->show_notification(
|
||||
TR("GPU Screen Recorder UI startup has been switched from systemd service to XDG autostart."),
|
||||
8.0, mgl::Color(255, 255, 255), gsr::get_color_theme().tint_color,
|
||||
gsr::NotificationType::NOTICE, nullptr, gsr::NotificationLevel::INFO);
|
||||
}
|
||||
}
|
||||
|
||||
gsr::replace_xdg_autostart_with_current_gsr_type();
|
||||
|
||||
// TODO: Add hotkeys in Overlay when using x11 global hotkeys. The hotkeys in Overlay should duplicate each key that is used for x11 global hotkeys.
|
||||
|
||||
std::string exit_reason;
|
||||
@@ -371,6 +357,7 @@ int main(int argc, char **argv) {
|
||||
mgl_deinit();
|
||||
|
||||
if(exit_reason == "back-to-old-ui") {
|
||||
gsr::set_xdg_autostart(false);
|
||||
const char *args[] = { "gpu-screen-recorder-gtk", "use-old-ui", nullptr };
|
||||
execvp(args[0], (char* const*)args);
|
||||
return 0;
|
||||
|
||||
325
tools/gsr-game-tracker/main.c
Normal file
325
tools/gsr-game-tracker/main.c
Normal file
@@ -0,0 +1,325 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/socket.h>
|
||||
#include <linux/netlink.h>
|
||||
#include <linux/connector.h>
|
||||
#include <linux/cn_proc.h>
|
||||
|
||||
#define MAX_GAMES 32
|
||||
#define ENVIRON_BUF_SIZE (64 * 1024)
|
||||
#define CMDLINE_BUF_SIZE 4096
|
||||
#define RECV_BUF_SIZE 8192
|
||||
|
||||
static pid_t games[MAX_GAMES]; /* 0 = empty slot */
|
||||
static int game_count = 0;
|
||||
static char environ_buf[ENVIRON_BUF_SIZE];
|
||||
static char cmdline_buf[CMDLINE_BUF_SIZE];
|
||||
static char recv_buf[RECV_BUF_SIZE];
|
||||
|
||||
static int find_slot_by_pid(pid_t pid) {
|
||||
for (int i = 0; i < MAX_GAMES; i++) {
|
||||
if (games[i] == pid)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int find_free_slot(void) {
|
||||
for (int i = 0; i < MAX_GAMES; i++) {
|
||||
if (!games[i])
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void add_game(pid_t pid) {
|
||||
int slot = find_free_slot();
|
||||
if (slot < 0) return;
|
||||
games[slot] = pid;
|
||||
if (game_count++ == 0) {
|
||||
printf("Game launched\n");
|
||||
fflush(stdout);
|
||||
}
|
||||
}
|
||||
|
||||
static void remove_game(pid_t pid) {
|
||||
int slot = find_slot_by_pid(pid);
|
||||
if (slot < 0) return;
|
||||
games[slot] = 0;
|
||||
if (--game_count == 0) {
|
||||
printf("Game exited\n");
|
||||
fflush(stdout);
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t read_file(const char *path, char *buf, size_t size) {
|
||||
int fd = open(path, O_RDONLY);
|
||||
if (fd < 0) return -1;
|
||||
ssize_t n = read(fd, buf, size - 1);
|
||||
close(fd);
|
||||
if (n > 0) buf[n] = '\0';
|
||||
return n;
|
||||
}
|
||||
|
||||
/* Return pointer to value inside null-separated environ block, or NULL. */
|
||||
static const char *env_get(const char *env, size_t size, const char *key) {
|
||||
const size_t klen = strlen(key);
|
||||
size_t index = 0;
|
||||
while (index < size) {
|
||||
const char *env_start = env + index;
|
||||
const char *env_end = memchr(env_start, '\0', size - index);
|
||||
if(!env_end)
|
||||
break;
|
||||
|
||||
const size_t env_len = env_end - env_start;
|
||||
if(env_len >= klen + 1 && memcmp(env_start, key, klen) == 0 && env_start[klen] == '=')
|
||||
return env_start + klen + 1;
|
||||
|
||||
index += env_len + 1;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static size_t get_argv_len(const char *cmdline, size_t size) {
|
||||
const char *argv0_end = memchr(cmdline, '\0', size);
|
||||
if(argv0_end)
|
||||
return argv0_end - cmdline;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char* memchr_reverse(const char *p, int c, size_t size) {
|
||||
for(size_t i = 0; i < size; ++i) {
|
||||
if(p[size - 1 - i] == c)
|
||||
return p + i;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char* get_arg_by_index(const char *cmdline, size_t size, size_t index) {
|
||||
size_t current_index = 0;
|
||||
size_t cmdline_index = 0;
|
||||
while (index < size) {
|
||||
const char *arg_start = cmdline + cmdline_index;
|
||||
const char *arg_end = memchr(arg_start, '\0', size - cmdline_index);
|
||||
if(!arg_end)
|
||||
break;
|
||||
|
||||
const size_t arg_len = arg_end - arg_start;
|
||||
if(current_index == index)
|
||||
return arg_start;
|
||||
|
||||
cmdline_index += arg_len + 1;
|
||||
current_index += 1;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool memeql(const char *haystack, size_t haystack_size, const char *needle) {
|
||||
const size_t needle_size = strlen(needle);
|
||||
return haystack_size == needle_size && memcmp(haystack, needle, needle_size) == 0;
|
||||
}
|
||||
|
||||
static bool starts_with(const char *haystack, size_t haystack_size, const char *needle) {
|
||||
const size_t needle_size = strlen(needle);
|
||||
return haystack_size >= needle_size && memcmp(haystack, needle, needle_size) == 0;
|
||||
}
|
||||
|
||||
static int is_wine_binary(const char *argv0, size_t size) {
|
||||
const char *base = memchr_reverse(argv0, '/', size);
|
||||
base = base ? base + 1 : argv0;
|
||||
const size_t base_len = argv0 + size - base;
|
||||
return memeql(base, base_len, "wine") ||
|
||||
memeql(base, base_len, "wine64") ||
|
||||
memeql(base, base_len, "wine-preloader") ||
|
||||
memeql(base, base_len, "wine64-preloader");
|
||||
}
|
||||
|
||||
static int has_game_arch_suffix(const char *argv0, size_t size) {
|
||||
static const char *suffixes[] = { ".x86_64", ".x64", ".x86" };
|
||||
for (int i = 0; i < 3; i++) {
|
||||
const size_t slen = strlen(suffixes[i]);
|
||||
if (size >= slen && memcmp(argv0 + size - slen, suffixes[i], slen) == 0)
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void check_process(pid_t pid) {
|
||||
if (find_slot_by_pid(pid) >= 0) return;
|
||||
|
||||
char path[64];
|
||||
ssize_t env_n, cmd_n;
|
||||
bool is_steam_fossilize = false;
|
||||
bool is_stream_script = false;
|
||||
/* Proton launched outside steam has this while it doesn't have SteamAppId nor is the process called wine */
|
||||
bool has_wine_prefix_env = false;
|
||||
|
||||
snprintf(path, sizeof(path), "/proc/%d/environ", pid);
|
||||
env_n = read_file(path, environ_buf, sizeof(environ_buf));
|
||||
|
||||
if (env_n > 0) {
|
||||
is_steam_fossilize = env_get(environ_buf, env_n, "STEAM_FOSSILIZE_DUMP_PATH_READ_ONLY");
|
||||
is_stream_script = env_get(environ_buf, env_n, "STEAMSCRIPT_VERSION");
|
||||
has_wine_prefix_env = env_get(environ_buf, env_n, "WINEPREFIX");
|
||||
const char *appid = env_get(environ_buf, env_n, "SteamAppId");
|
||||
if (!appid) appid = env_get(environ_buf, env_n, "SteamGameId");
|
||||
if (appid && appid[0] >= '1' && appid[0] <= '9') {
|
||||
add_game(pid);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
snprintf(path, sizeof(path), "/proc/%d/cmdline", pid);
|
||||
cmd_n = read_file(path, cmdline_buf, sizeof(cmdline_buf));
|
||||
if(cmd_n <= 0)
|
||||
return;
|
||||
|
||||
const char *argv0 = cmdline_buf;
|
||||
const size_t argv0_len = get_argv_len(cmdline_buf, cmd_n);
|
||||
|
||||
const char *argv1 = get_arg_by_index(cmdline_buf, cmd_n, 1);
|
||||
const size_t argv1_len = argv1 ? get_argv_len(argv1, cmdline_buf + cmd_n - argv1) : 0;
|
||||
|
||||
if (cmd_n > 0 && (is_wine_binary(argv0, argv0_len) || has_game_arch_suffix(argv0, argv0_len) || has_wine_prefix_env)
|
||||
&& !is_steam_fossilize
|
||||
&& !is_stream_script
|
||||
&& !memeql(argv1, argv1_len, "--version")
|
||||
&& !starts_with(argv0, argv0_len, "C:\\windows\\system32"))
|
||||
{
|
||||
// fprintf(stderr, "env: ");
|
||||
// write(STDOUT_FILENO, environ_buf, env_n);
|
||||
// fprintf(stderr, "\ncmdline: ");
|
||||
// write(STDOUT_FILENO, cmdline_buf, cmd_n);
|
||||
// fprintf(stderr, "\n");
|
||||
add_game(pid);
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_proc_event(const struct proc_event *ev) {
|
||||
switch (ev->what) {
|
||||
case PROC_EVENT_EXEC:
|
||||
check_process(ev->event_data.exec.process_tgid);
|
||||
break;
|
||||
case PROC_EVENT_EXIT:
|
||||
/* Only act when the whole process (not just a thread) exits */
|
||||
if (ev->event_data.exit.process_pid == ev->event_data.exit.process_tgid)
|
||||
remove_game(ev->event_data.exit.process_tgid);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void process_netlink_msg(const char *buf, size_t len) {
|
||||
const struct nlmsghdr *nl = (const struct nlmsghdr *)buf;
|
||||
for (; NLMSG_OK(nl, (unsigned int)len); nl = NLMSG_NEXT(nl, len)) {
|
||||
if (nl->nlmsg_type == NLMSG_NOOP ||
|
||||
nl->nlmsg_type == NLMSG_ERROR ||
|
||||
nl->nlmsg_type == NLMSG_OVERRUN)
|
||||
continue;
|
||||
if (nl->nlmsg_len < NLMSG_HDRLEN + sizeof(struct cn_msg))
|
||||
continue;
|
||||
const struct cn_msg *cn = (const struct cn_msg *)NLMSG_DATA(nl);
|
||||
if (cn->id.idx != CN_IDX_PROC || cn->id.val != CN_VAL_PROC)
|
||||
continue;
|
||||
if (cn->len < sizeof(struct proc_event))
|
||||
continue;
|
||||
handle_proc_event((const struct proc_event *)cn->data);
|
||||
}
|
||||
}
|
||||
|
||||
static int send_mcast_op(int sock, enum proc_cn_mcast_op op) {
|
||||
/* cn_msg ends with data[0], so we can't place fields after it in a struct.
|
||||
* Use a flat buffer and fill via pointers instead. */
|
||||
char buf[NLMSG_SPACE(sizeof(struct cn_msg) + sizeof(enum proc_cn_mcast_op))];
|
||||
memset(buf, 0, sizeof(buf));
|
||||
|
||||
struct nlmsghdr *nl = (struct nlmsghdr *)buf;
|
||||
nl->nlmsg_len = sizeof(buf);
|
||||
nl->nlmsg_type = NLMSG_DONE;
|
||||
nl->nlmsg_pid = (unsigned int)getpid();
|
||||
|
||||
struct cn_msg *cn = (struct cn_msg *)NLMSG_DATA(nl);
|
||||
cn->id.idx = CN_IDX_PROC;
|
||||
cn->id.val = CN_VAL_PROC;
|
||||
cn->len = sizeof(enum proc_cn_mcast_op);
|
||||
memcpy(cn->data, &op, sizeof(op));
|
||||
|
||||
return send(sock, buf, sizeof(buf), 0) < 0 ? -1 : 0;
|
||||
}
|
||||
|
||||
static int setup_netlink(void) {
|
||||
int sock = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR);
|
||||
if (sock < 0) {
|
||||
perror("socket(AF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR)");
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct sockaddr_nl sa;
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sa.nl_family = AF_NETLINK;
|
||||
sa.nl_groups = CN_IDX_PROC;
|
||||
sa.nl_pid = (unsigned int)getpid();
|
||||
|
||||
if (bind(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
|
||||
perror("bind");
|
||||
close(sock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (send_mcast_op(sock, PROC_CN_MCAST_LISTEN) < 0) {
|
||||
perror("send PROC_CN_MCAST_LISTEN");
|
||||
close(sock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return sock;
|
||||
}
|
||||
|
||||
static void scan_existing_processes(void) {
|
||||
DIR *d = opendir("/proc");
|
||||
if (!d) return;
|
||||
|
||||
struct dirent *ent;
|
||||
while ((ent = readdir(d)) != NULL) {
|
||||
const char *s = ent->d_name;
|
||||
if (*s < '1' || *s > '9') continue;
|
||||
pid_t pid = 0;
|
||||
for (; *s; s++) {
|
||||
if (*s < '0' || *s > '9') { pid = 0; break; }
|
||||
pid = pid * 10 + (*s - '0');
|
||||
}
|
||||
if (pid > 0) check_process(pid);
|
||||
}
|
||||
closedir(d);
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
memset(games, 0, sizeof(games));
|
||||
|
||||
int sock = setup_netlink();
|
||||
if (sock < 0) return 1;
|
||||
|
||||
scan_existing_processes();
|
||||
|
||||
for (;;) {
|
||||
ssize_t n = recv(sock, recv_buf, sizeof(recv_buf), 0);
|
||||
if (n < 0) {
|
||||
if (errno == EINTR) continue;
|
||||
if (errno == ENOBUFS) { scan_existing_processes(); continue; }
|
||||
perror("recv");
|
||||
break;
|
||||
}
|
||||
process_netlink_msg(recv_buf, (size_t)n);
|
||||
}
|
||||
|
||||
send_mcast_op(sock, PROC_CN_MCAST_IGNORE);
|
||||
close(sock);
|
||||
return 0;
|
||||
}
|
||||
@@ -31,7 +31,7 @@ Turn off=Desactivar
|
||||
Recording has been paused=La grabación se ha pausado
|
||||
Recording has been unpaused=La grabación se ha reanudado
|
||||
Started recording %s=Iniciando la grabación %s
|
||||
Saved a %s recording of %s\nto "%s"=Se guardó una grabación de %s de %s\nen "%s"
|
||||
Saved a %s recording of %s to "%s"=Se guardó una grabación de %s de %s en "%s"
|
||||
Saved a %s recording of %s=Se guardó una grabación de %s de %s
|
||||
Failed to start/save recording=Fallo al iniciar/guardar grabación
|
||||
|
||||
@@ -39,36 +39,36 @@ Failed to start/save recording=Fallo al iniciar/guardar grabación
|
||||
Replay stopped=Repetición detenida
|
||||
Started replaying %s=Iniciando la repetición %s
|
||||
Saving replay, this might take some time=Guardando repetición, esto podría tardar un rato
|
||||
Saved a %s replay of %s\nto "%s"=Se guardó una repetición de %s de %s\nen "%s"
|
||||
Saved a %s replay of %s to "%s"=Se guardó una repetición de %s de %s en "%s"
|
||||
Saved a %s replay of %s=Se guardó una repetición de %s de %s
|
||||
Replay stopped because of an error=Repetición detenida debido a un error
|
||||
Replay settings have been modified.\nYou may need to restart replay to apply the changes.=Los ajustes de repetición han sido modificados.\nUn reinicio puede ser necesario para aplicar los cambios.
|
||||
Replay settings have been modified. You may need to restart replay to apply the changes.=Los ajustes de repetición han sido modificados. Un reinicio puede ser necesario para aplicar los cambios.
|
||||
|
||||
# Notifications - Streaming
|
||||
Streaming has stopped=La transmisión se ha detenido
|
||||
Started streaming %s=Iniciando la transmisión %s
|
||||
Streaming stopped because of an error=Transmisión detenida debido a un error
|
||||
Streaming settings have been modified.\nYou may need to restart streaming to apply the changes.=Los ajustes de transmisión han sido modificados.\nUn reinicio puede ser necesario para aplicar los cambios.
|
||||
Streaming settings have been modified. You may need to restart streaming to apply the changes.=Los ajustes de transmisión han sido modificados. Un reinicio puede ser necesario para aplicar los cambios.
|
||||
|
||||
# Notifications - Screenshot
|
||||
Saved a screenshot of %s\nto "%s"=Captura de pantalla de %s\nguardada en "%s"
|
||||
Saved a screenshot of %s to "%s"=Captura de pantalla de %s guardada en "%s"
|
||||
Saved a screenshot of %s=Captura de pantalla de %s guardada
|
||||
Failed to take a screenshot=Fallo al tomar la captura de pantalla
|
||||
|
||||
# Error messages
|
||||
Another instance of GPU Screen Recorder UI is already running.\nPress Alt+Z to open the UI.=Ya hay otra instancia de GPU Screen Recorder UI en ejecución.\nPulsa Alt-Z para abrir la interfaz.
|
||||
GPU Screen Recorder is already running in another process.\nPlease close it before using GPU Screen Recorder UI.=GPU Screen Recorder ya está en ejecución en otro proceso.\nPor favor, ciérralo antes de usar GPU Screen Recorder UI.
|
||||
Failed to start replay, capture target "%s" is invalid.\nPlease change capture target in settings=Fallo al iniciar la repetición, el objetivo de captura "%s" es inválido.\nPor favor, cambia el objetivo de captura en los ajustes
|
||||
Failed to start recording, capture target "%s" is invalid.\nPlease change capture target in settings=Fallo al iniciar la grabación, el objetivo de captura "%s" es inválido.\nPor favor, cambia el objetivo de captura en los ajustes
|
||||
Failed to start streaming, capture target "%s" is invalid.\nPlease change capture target in settings=Fallo al iniciar la transmisión, el objetivo de captura "%s" es inválido.\nPor favor, cambia el objetivo de captura en los ajustes
|
||||
Failed to take a screenshot, capture target "%s" is invalid.\nPlease change capture target in settings=Fallo al tomar la captura de pantalla, el objetivo de captura "%s" es inválido.\nPor favor, cambia el objetivo de captura en los ajustes
|
||||
Another instance of GPU Screen Recorder UI is already running. Press Alt+Z to open the UI.=Ya hay otra instancia de GPU Screen Recorder UI en ejecución. Pulsa Alt-Z para abrir la interfaz.
|
||||
GPU Screen Recorder is already running in another process. Please close it before using GPU Screen Recorder UI.=GPU Screen Recorder ya está en ejecución en otro proceso. Por favor, ciérralo antes de usar GPU Screen Recorder UI.
|
||||
Failed to start replay, capture target "%s" is invalid. Please change capture target in settings=Fallo al iniciar la repetición, el objetivo de captura "%s" es inválido. Por favor, cambia el objetivo de captura en los ajustes
|
||||
Failed to start recording, capture target "%s" is invalid. Please change capture target in settings=Fallo al iniciar la grabación, el objetivo de captura "%s" es inválido. Por favor, cambia el objetivo de captura en los ajustes
|
||||
Failed to start streaming, capture target "%s" is invalid. Please change capture target in settings=Fallo al iniciar la transmisión, el objetivo de captura "%s" es inválido. Por favor, cambia el objetivo de captura en los ajustes
|
||||
Failed to take a screenshot, capture target "%s" is invalid. Please change capture target in settings=Fallo al tomar la captura de pantalla, el objetivo de captura "%s" es inválido. Por favor, cambia el objetivo de captura en los ajustes
|
||||
|
||||
Unable to start recording when replay is turned on.\nTurn off replay before starting recording.=No se puede grabar cuando la repetición está activada.\nApaga la repetición antes de empezar a grabar.
|
||||
Unable to start streaming when replay is turned on.\nTurn off replay before starting streaming.=No se puede transmitir cuando la repetición está activada.\nApaga la repetición antes de empezar a transmitir.
|
||||
Unable to start streaming when recording.\nStop recording before starting streaming.=No se puede transmitir mientras se está grabando. Detén la grabación antes de empezar a transmitir.
|
||||
Unable to start recording when streaming.\nStop streaming before starting recording.=No se puede grabar mientras se está transmitiendo. Detén la transmisión antes de empezar a grabar.
|
||||
Unable to start replay when recording.\nStop recording before starting replay.=No se puede iniciar la repetición mientras se está grabando. Detén la grabación antes de iniciar la repetición.
|
||||
Unable to start replay when streaming.\nStop streaming before starting replay.=No se puede iniciar la repetición mientras se está transmitiendo. Detén la transmisión antes de iniciar la repetición.
|
||||
Unable to start recording when replay is turned on. Turn off replay before starting recording.=No se puede grabar cuando la repetición está activada. Apaga la repetición antes de empezar a grabar.
|
||||
Unable to start streaming when replay is turned on. Turn off replay before starting streaming.=No se puede transmitir cuando la repetición está activada. Apaga la repetición antes de empezar a transmitir.
|
||||
Unable to start streaming when recording. Stop recording before starting streaming.=No se puede transmitir mientras se está grabando. Detén la grabación antes de empezar a transmitir.
|
||||
Unable to start recording when streaming. Stop streaming before starting recording.=No se puede grabar mientras se está transmitiendo. Detén la transmisión antes de empezar a grabar.
|
||||
Unable to start replay when recording. Stop recording before starting replay.=No se puede iniciar la repetición mientras se está grabando. Detén la grabación antes de iniciar la repetición.
|
||||
Unable to start replay when streaming. Stop streaming before starting replay.=No se puede iniciar la repetición mientras se está transmitiendo. Detén la transmisión antes de iniciar la repetición.
|
||||
|
||||
Started recording in the replay session=Grabación iniciada en la sesión de repetición
|
||||
Started recording in the streaming session=Grabación iniciada en la sesión de transmisión
|
||||
@@ -81,11 +81,11 @@ Streaming stopped because of an error. Verify if settings are correct=Transmisi
|
||||
%s. Verify if settings are correct=%s. Comprueba que los ajustes sean correctos
|
||||
|
||||
# GPU Screen Recorder errors
|
||||
Desktop portal capture failed.\nEither you canceled the desktop portal or your Wayland compositor doesn't support desktop portal capture\nor it's incorrectly setup on your system.=Falló la captura del portal de escritorio.\nO cancelaste el portal de escritorio, o tu compositor de Wayland no admite la captura del portal de escritorio\no está configurado incorrectamente en tu sistema.
|
||||
Monitor capture failed.\nThe monitor you are trying to capture is invalid.\nPlease validate your capture settings.=Falló la captura del monitor.\nEl monitor que intentas capturar no es válido.\nPor favor, valida la configuración de captura.
|
||||
Capture failed. Neither H264, HEVC nor AV1 video codecs are supported\non your system or you are trying to capture at a resolution higher than your\nsystem supports for each video codec.=Falló la captura. Ni los códecs de video H264, HEVC ni AV1 son compatibles\ncon tu sistema o estás intentando capturar a una resolución superior a la que tu\nsistema admite para cada códec de video.
|
||||
Capture failed. Your system doesn't support the resolution you are trying to\nrecord at with the video codec you have chosen.\nChange capture resolution or video codec and try again.\nNote: AV1 supports the highest resolution, then HEVC and then H264.=Falló la captura. Tu sistema no admite la resolución a la que intentas\ngrabar con el códec de video que has elegido.\nCambia la resolución de captura o el códec de video e inténtalo de nuevo.\nNota: AV1 admite la resolución más alta, luego HEVC y después H264.
|
||||
Capture failed. Your system doesn't support the video codec you have chosen.\nChange video codec and try again.=Falló la captura. Tu sistema no es compatible con el códec de video que has elegido.\nCambia el códec de video e inténtalo de nuevo
|
||||
Desktop portal capture failed. Either you canceled the desktop portal or your Wayland compositor doesn't support desktop portal capture or it's incorrectly setup on your system.=Falló la captura del portal de escritorio. O cancelaste el portal de escritorio, o tu compositor de Wayland no admite la captura del portal de escritorio o está configurado incorrectamente en tu sistema.
|
||||
Monitor capture failed. The monitor you are trying to capture is invalid. Please validate your capture settings.=Falló la captura del monitor. El monitor que intentas capturar no es válido. Por favor, valida la configuración de captura.
|
||||
Capture failed. Neither H264, HEVC nor AV1 video codecs are supported on your system or you are trying to capture at a resolution higher than your system supports for each video codec.=Falló la captura. Ni los códecs de video H264, HEVC ni AV1 son compatibles con tu sistema o estás intentando capturar a una resolución superior a la que tu sistema admite para cada códec de video.
|
||||
Capture failed. Your system doesn't support the resolution you are trying to record at with the video codec you have chosen. Change capture resolution or video codec and try again. Note: AV1 supports the highest resolution, then HEVC and then H264.=Falló la captura. Tu sistema no admite la resolución a la que intentas grabar con el códec de video que has elegido. Cambia la resolución de captura o el códec de video e inténtalo de nuevo. Nota: AV1 admite la resolución más alta, luego HEVC y después H264.
|
||||
Capture failed. Your system doesn't support the video codec you have chosen. Change video codec and try again.=Falló la captura. Tu sistema no es compatible con el códec de video que has elegido. Cambia el códec de video e inténtalo de nuevo
|
||||
Stopped capture because the user canceled the desktop portal=Se detuvo la captura porque el usuario canceló el portal de escritorio
|
||||
Failed to take a screenshot. Verify if settings are correct=Fallo al tomar una captura de pantalla. Verifica si la configuración es correcta
|
||||
|
||||
@@ -98,10 +98,12 @@ Failed to launch gpu-screen-recorder to take a screenshot=Fallo al lanzar gpu-sc
|
||||
# System startup notifications
|
||||
Failed to add GPU Screen Recorder to system startup=Fallo al añadir GPU Screen Recorder al inicio del sistema
|
||||
Failed to remove GPU Screen Recorder from system startup=Fallo al eliminar GPU Screen Recorder del inicio del sistema
|
||||
Failed to add GPU Screen Recorder to system startup.\nThis option only works on systems that use systemd.\nYou have to manually add "gsr-ui" to system startup on systems that uses another init system.=Fallo al añadir GPU Screen Recorder al inicio del sistema.\nEsta opción solo funciona en sistemas que usan systemd.\nTienes que añadir "gsr-ui" manualmente al inicio del sistema en aquellos que usen otro sistema de arranque.
|
||||
To enable autorun: install and configure 'dex' (recommended), or manually add '%s' to your desktop autostart entries.=Para activar el inicio automático: instala y configura 'dex' (recomendado) o añade manualmente '%s' a las entradas de inicio automático del escritorio.
|
||||
GPU Screen Recorder UI startup has been switched from systemd service to XDG autostart.=El inicio de GPU Screen Recorder UI ha cambiado del servicio systemd al inicio automático XDG.
|
||||
GPU Screen Recorder UI autostart via systemd is deprecated. To migrate: install and configure 'dex' (recommended), or manually add '%s' to your desktop autostart entries.=El inicio automático de GPU Screen Recorder UI mediante systemd está obsoleto. Para migrar: instala y configura 'dex' (recomendado) o añade manualmente '%s' a las entradas de inicio automático del escritorio.
|
||||
|
||||
# Wayland warning
|
||||
Wayland doesn't support GPU Screen Recorder UI properly,\nthings may not work as expected. Use X11 if you experience issues.=Wayland no ofrece soporte adecuado para GPU Screen Recorder UI;\nes posible que el funcionamiento no sea el esperado. Si experimentas problemas, utiliza X11.
|
||||
Wayland doesn't support GPU Screen Recorder UI properly, things may not work as expected. Use X11 if you experience issues.=Wayland no ofrece soporte adecuado para GPU Screen Recorder UI; es posible que el funcionamiento no sea el esperado. Si experimentas problemas, utiliza X11.
|
||||
|
||||
# Hotkey conflicts
|
||||
Some keyboard remapping software conflicts with GPU Screen Recorder on your system.\nKeyboards have been ungrabbed, applications will now receive the hotkeys you press.=Algún programa de reasignación de teclas entra en conflicto con GPU Screen Recorder en tu sistema.\nSe ha liberado la captura del teclado, las aplicaciones ahora recibirán las teclas de acceso rápido que presiones.
|
||||
@@ -153,6 +155,7 @@ Start/stop recording with desktop portal:=Iniciar/detener grabación con portal
|
||||
Take a screenshot with desktop portal:=Tomar una captura de pantalla con portal de escritorio
|
||||
Start/stop recording a window:=Iniciar/detener grabación de una ventana
|
||||
Take a screenshot of a window:=Tomar una captura de pantalla de una ventana
|
||||
Press ESC to go back to the previous page/close the UI.=Pulse ESC para volver a la página anterior/cerrar la interfaz de usuario.
|
||||
|
||||
Clear hotkeys=Borrar teclas de acceso rápido
|
||||
Reset hotkeys to default=Reestablecer teclas de acceso rápido a los valores predeterminados
|
||||
@@ -369,11 +372,16 @@ Record in low-power mode=Grabar en modo de bajo consumo
|
||||
Record cursor=Grabar cursor
|
||||
|
||||
Do not force the GPU to go into high performance mode when recording.\nMay affect recording performance, especially when playing a video at the same time.\nIf enabled then it's recommended to use sync to content frame rate mode to reduce power usage when idle.=No forzar a la GPU a entrar en modo de alto rendimiento al grabar.\nPuede afectar al rendimiento de la grabación, especialmente al reproducir un vídeo al mismo tiempo.\nSi se activa, se recomienda usar el modo de sincronización con la velocidad de fotogramas del contenido para reducir el consumo de energía cuando está inactiva.
|
||||
Use vulkan video encoding instead of VAAPI/NVENC.\nEnabling this may result in better game performance while recording, especially on NVIDIA.\nNote that this option is experimental. There may be GPU driver issues that causes issues when this is enabled.=Utiliza la codificación de vídeo Vulkan en lugar de VAAPI/NVENC.\nHabilitar esta opción puede mejorar el rendimiento de los juegos durante la grabación, especialmente en tarjetas NVIDIA.\nTen en cuenta que esta opción es experimental. Podría haber problemas con los controladores de la GPU al activarla.
|
||||
Enable vulkan video encoding (experimental)=Habilitar la codificación de vídeo Vulkan (experimental)
|
||||
|
||||
Show %s notifications=Mostrar notificación de %s
|
||||
Show %s status with scroll lock LED=Mostrar estado de %s con el LED de Bloq Despl
|
||||
Show replay notifications=Mostrar notificaciones de repetición
|
||||
Show recording notifications=Mostrar notificaciones de grabación
|
||||
Show streaming notifications=Mostrar notificaciones de transmisión
|
||||
Show replay status with scroll lock LED=Mostrar estado de repetición con LED de bloqueo de desplazamiento
|
||||
Show recording status with scroll lock LED=Mostrar estado de grabación con LED de bloqueo de desplazamiento
|
||||
Show streaming status with scroll lock LED=Mostrar estado de transmisión con LED de bloqueo de desplazamiento
|
||||
Recording indicator=Indicador de grabación
|
||||
recording=grabando
|
||||
|
||||
Simple=Simple
|
||||
Audio track #%d=Pista de sonido #%d
|
||||
@@ -384,16 +392,16 @@ Estimated video file size per minute (excluding audio): %.2fMB=Tamaño estimado
|
||||
# Replay settings
|
||||
Directory to save replays:=Directorio para guardar las repeticiones:
|
||||
Replay indicator=Indicador de repetición
|
||||
replay=repetición
|
||||
Turn on replay when starting a fullscreen application%s=Activar la repetición al iniciar una aplicación a pantalla completa%s
|
||||
Turn on replay when starting a game=Activa la repetición al iniciar un juego
|
||||
Autostart=Inicio automático
|
||||
in RAM=en RAM
|
||||
on disk=en disco
|
||||
Replay duration in seconds:=Duración de la repetición en segundos:
|
||||
Where should temporary replay data be stored?=¿Dónde se deben almacenar los archivos temporales de la repetición?
|
||||
RAM=RAM
|
||||
Disk (Not recommended on SSDs)=Disco (no recomendado en SSDs)
|
||||
Turn on replay when this program starts=Activar la repetición cuando se inicie este programa
|
||||
Turn on replay when power supply is connected=Activar la repetición cuando se conecte la fuente de alimentación
|
||||
Only turn on replay if a power supply is connected=Solo active la reproducción si hay una fuente de alimentación conectada
|
||||
Don't turn on replay automatically=No activar la repetición automáticamente
|
||||
Restart replay on save=Reiniciar la repetición al guardar
|
||||
Estimated video max file size %s: %.2fMB.\nChange video bitrate or replay duration to change file size.=Tamaño máximo estimado del archivo de vídeo %s: %.2fMB.\nCambia la tasa de bits del vídeo o la duración de la repetición para cambiar el tamaño del archivo.
|
||||
@@ -409,4 +417,3 @@ Stream URL:=URL de la transmisión
|
||||
Stream key:=Clave de transmisión
|
||||
Streaming info=Información de transmisión
|
||||
Streaming indicator=Indicador de transmisión
|
||||
streaming=transmisión
|
||||
@@ -34,7 +34,7 @@ Turn off=Arrêter
|
||||
Recording has been paused=L’enregistrement a été mis en pause
|
||||
Recording has been unpaused=L’enregistrement a repris
|
||||
Started recording %s=Enregistrement démarré %s
|
||||
Saved a %s recording of %s\nto "%s"=Enregistrement %s de %s sauvegardé dans "%s"
|
||||
Saved a %s recording of %s to "%s"=Enregistrement %s de %s sauvegardé dans "%s"
|
||||
Saved a %s recording of %s=Enregistrement %s de %s sauvegardé
|
||||
Failed to start/save recording=Échec du démarrage/sauvegarde de l’enregistrement
|
||||
|
||||
@@ -42,36 +42,36 @@ Failed to start/save recording=Échec du démarrage/sauvegarde de l’enregistre
|
||||
Replay stopped=Replay arrêté
|
||||
Started replaying %s=Replay démarré %s
|
||||
Saving replay, this might take some time=Sauvegarde du replay, cela peut prendre un certain temps
|
||||
Saved a %s replay of %s\nto "%s"=Replay %s de %s sauvegardé dans "%s"
|
||||
Saved a %s replay of %s to "%s"=Replay %s de %s sauvegardé dans "%s"
|
||||
Saved a %s replay of %s=Replay %s de %s sauvegardé
|
||||
Replay stopped because of an error=Replay arrêté en raison d’une erreur
|
||||
Replay settings have been modified.\nYou may need to restart replay to apply the changes.=Les paramètres de replays ont été modifiés.\nVous devez peut-être redémarrer le replay pour appliquer les changements.
|
||||
Replay settings have been modified. You may need to restart replay to apply the changes.=Les paramètres de replays ont été modifiés. Vous devez peut-être redémarrer le replay pour appliquer les changements.
|
||||
|
||||
# Notifications - Streaming
|
||||
Streaming has stopped=Diffusion arrêtée
|
||||
Started streaming %s=Diffusion démarrée %s
|
||||
Streaming stopped because of an error=Diffusion arrêtée en raison d’une erreur
|
||||
Streaming settings have been modified.\nYou may need to restart streaming to apply the changes.=Les paramètres de diffusion ont été modifiés.\nVous devez peut-être redémarrer la diffusion pour appliquer les changements.
|
||||
Streaming settings have been modified. You may need to restart streaming to apply the changes.=Les paramètres de diffusion ont été modifiés. Vous devez peut-être redémarrer la diffusion pour appliquer les changements.
|
||||
|
||||
# Notifications - Screenshot
|
||||
Saved a screenshot of %s\nto "%s"=Capture d’écran de %s sauvegardée dans "%s"
|
||||
Saved a screenshot of %s to "%s"=Capture d’écran de %s sauvegardée dans "%s"
|
||||
Saved a screenshot of %s=Capture d’écran de %s sauvegardée
|
||||
Failed to take a screenshot=Échec de la capture d’écran
|
||||
|
||||
# Error messages
|
||||
Another instance of GPU Screen Recorder UI is already running.\nPress Alt+Z to open the UI.=Une autre instance de GPU Screen Recorder UI est déjà en cours.\nAppuyez sur Alt+Z pour ouvrir l’UI.
|
||||
GPU Screen Recorder is already running in another process.\nPlease close it before using GPU Screen Recorder UI.=GPU Screen Recorder est déjà en cours dans un autre processus.\nVeuillez le fermer avant d’utiliser l’UI.
|
||||
Failed to start replay, capture target "%s" is invalid.\nPlease change capture target in settings=Échec du démarrage du replay, la cible "%s" est invalide.\nVeuillez modifier la cible dans les paramètres.
|
||||
Failed to start recording, capture target "%s" is invalid.\nPlease change capture target in settings=Échec du démarrage de l’enregistrement, la cible "%s" est invalide.\nVeuillez modifier la cible dans les paramètres.
|
||||
Failed to start streaming, capture target "%s" is invalid.\nPlease change capture target in settings=Échec du démarrage de la diffusion, la cible "%s" est invalide.\nVeuillez modifier la cible dans les paramètres.
|
||||
Failed to take a screenshot, capture target "%s" is invalid.\nPlease change capture target in settings=Échec de la capture d’écran, la cible "%s" est invalide.\nVeuillez modifier la cible dans les paramètres.
|
||||
Another instance of GPU Screen Recorder UI is already running. Press Alt+Z to open the UI.=Une autre instance de GPU Screen Recorder UI est déjà en cours. Appuyez sur Alt+Z pour ouvrir l’UI.
|
||||
GPU Screen Recorder is already running in another process. Please close it before using GPU Screen Recorder UI.=GPU Screen Recorder est déjà en cours dans un autre processus. Veuillez le fermer avant d’utiliser l’UI.
|
||||
Failed to start replay, capture target "%s" is invalid. Please change capture target in settings=Échec du démarrage du replay, la cible "%s" est invalide. Veuillez modifier la cible dans les paramètres.
|
||||
Failed to start recording, capture target "%s" is invalid. Please change capture target in settings=Échec du démarrage de l’enregistrement, la cible "%s" est invalide. Veuillez modifier la cible dans les paramètres.
|
||||
Failed to start streaming, capture target "%s" is invalid. Please change capture target in settings=Échec du démarrage de la diffusion, la cible "%s" est invalide. Veuillez modifier la cible dans les paramètres.
|
||||
Failed to take a screenshot, capture target "%s" is invalid. Please change capture target in settings=Échec de la capture d’écran, la cible "%s" est invalide. Veuillez modifier la cible dans les paramètres.
|
||||
|
||||
Unable to start recording when replay is turned on.\nTurn off replay before starting recording.=Impossible de démarrer l’enregistrement lorsque le replay est activé.\nDésactivez le replay avant de commencer l’enregistrement.
|
||||
Unable to start streaming when replay is turned on.\nTurn off replay before starting streaming.=Impossible de démarrer la diffusion lorsque le replay est activé.\nDésactivez le replay avant de commencer la diffusion.
|
||||
Unable to start streaming when recording.\nStop recording before starting streaming.=Impossible de démarrer la diffusion pendant l’enregistrement.\nArrêtez l’enregistrement avant de commencer la diffusion.
|
||||
Unable to start recording when streaming.\nStop streaming before starting recording.=Impossible de démarrer l’enregistrement pendant la diffusion.\nArrêtez la diffusion avant de commencer l’enregistrement.
|
||||
Unable to start replay when recording.\nStop recording before starting replay.=Impossible de démarrer le replay pendant l’enregistrement.\nArrêtez l’enregistrement avant de commencer le replay.
|
||||
Unable to start replay when streaming.\nStop streaming before starting replay.=Impossible de démarrer le replay pendant la diffusion.\nArrêtez la diffusion avant de commencer le replay.
|
||||
Unable to start recording when replay is turned on. Turn off replay before starting recording.=Impossible de démarrer l’enregistrement lorsque le replay est activé. Désactivez le replay avant de commencer l’enregistrement.
|
||||
Unable to start streaming when replay is turned on. Turn off replay before starting streaming.=Impossible de démarrer la diffusion lorsque le replay est activé. Désactivez le replay avant de commencer la diffusion.
|
||||
Unable to start streaming when recording. Stop recording before starting streaming.=Impossible de démarrer la diffusion pendant l’enregistrement. Arrêtez l’enregistrement avant de commencer la diffusion.
|
||||
Unable to start recording when streaming. Stop streaming before starting recording.=Impossible de démarrer l’enregistrement pendant la diffusion. Arrêtez la diffusion avant de commencer l’enregistrement.
|
||||
Unable to start replay when recording. Stop recording before starting replay.=Impossible de démarrer le replay pendant l’enregistrement. Arrêtez l’enregistrement avant de commencer le replay.
|
||||
Unable to start replay when streaming. Stop streaming before starting replay.=Impossible de démarrer le replay pendant la diffusion. Arrêtez la diffusion avant de commencer le replay.
|
||||
|
||||
Started recording in the replay session=Enregistrement démarré pendant la session de replay
|
||||
Started recording in the streaming session=Enregistrement démarré pendant la session de diffusion
|
||||
@@ -84,11 +84,11 @@ Streaming stopped because of an error. Verify if settings are correct=Diffusion
|
||||
%s. Verify if settings are correct=%s. Vérifiez les paramètres
|
||||
|
||||
# GPU Screen Recorder errors
|
||||
Desktop portal capture failed.\nEither you canceled the desktop portal or your Wayland compositor doesn't support desktop portal capture\nor it's incorrectly setup on your system.=Échec de la capture via le portail du bureau.\nVous avez annulé le portail ou votre compositeur Wayland ne supporte pas la capture via le portail ou il est mal configuré.
|
||||
Monitor capture failed.\nThe monitor you are trying to capture is invalid.\nPlease validate your capture settings.=Échec de la capture du moniteur.\nLe moniteur que vous essayez de capturer est invalide.\nVeuillez vérifier vos paramètres de capture.
|
||||
Capture failed. Neither H264, HEVC nor AV1 video codecs are supported\non your system or you are trying to capture at a resolution higher than your\nsystem supports for each video codec.=Échec de la capture. Aucun codec vidéo H264, HEVC ou AV1 n’est supporté sur votre système ou vous essayez de capturer à une résolution plus élevée que celle supportée par votre système pour chaque codec.
|
||||
Capture failed. Your system doesn't support the resolution you are trying to\nrecord at with the video codec you have chosen.\nChange capture resolution or video codec and try again.\nNote: AV1 supports the highest resolution, then HEVC and then H264.=Échec de la capture. Votre système ne supporte pas la résolution choisie pour le codec sélectionné.\nModifiez la résolution ou le codec et réessayez.\nNote : AV1 supporte la résolution la plus élevée, puis HEVC et H264.
|
||||
Capture failed. Your system doesn't support the video codec you have chosen.\nChange video codec and try again.=Échec de la capture. Votre système ne supporte pas le codec vidéo sélectionné.\nChangez de codec et réessayez.
|
||||
Desktop portal capture failed. Either you canceled the desktop portal or your Wayland compositor doesn’t support desktop portal capture or it’s incorrectly setup on your system.=Échec de la capture via le portail du bureau. Vous avez annulé le portail ou votre compositeur Wayland ne supporte pas la capture via le portail ou il est mal configuré.
|
||||
Monitor capture failed. The monitor you are trying to capture is invalid. Please validate your capture settings.=Échec de la capture du moniteur. Le moniteur que vous essayez de capturer est invalide. Veuillez vérifier vos paramètres de capture.
|
||||
Capture failed. Neither H264, HEVC nor AV1 video codecs are supported on your system or you are trying to capture at a resolution higher than your system supports for each video codec.=Échec de la capture. Aucun codec vidéo H264, HEVC ou AV1 n’est supporté sur votre système ou vous essayez de capturer à une résolution plus élevée que celle supportée par votre système pour chaque codec.
|
||||
Capture failed. Your system doesn’t support the resolution you are trying to record at with the video codec you have chosen. Change capture resolution or video codec and try again. Note: AV1 supports the highest resolution, then HEVC and then H264.=Échec de la capture. Votre système ne supporte pas la résolution choisie pour le codec sélectionné. Modifiez la résolution ou le codec et réessayez. Note : AV1 supporte la résolution la plus élevée, puis HEVC et H264.
|
||||
Capture failed. Your system doesn’t support the video codec you have chosen. Change video codec and try again.=Échec de la capture. Votre système ne supporte pas le codec vidéo sélectionné. Changez de codec et réessayez.
|
||||
Stopped capture because the user canceled the desktop portal=Capture arrêtée car l’utilisateur a annulé le portail du bureau
|
||||
Failed to take a screenshot. Verify if settings are correct=Échec de la capture d’écran. Vérifiez les paramètres.
|
||||
|
||||
@@ -101,10 +101,12 @@ Failed to launch gpu-screen-recorder to take a screenshot=Échec du lancement de
|
||||
# System startup notifications
|
||||
Failed to add GPU Screen Recorder to system startup=Échec de l’ajout de GPU Screen Recorder au démarrage du système
|
||||
Failed to remove GPU Screen Recorder from system startup=Échec de la suppression de GPU Screen Recorder du démarrage du système
|
||||
Failed to add GPU Screen Recorder to system startup.\nThis option only works on systems that use systemd.\nYou have to manually add "gsr-ui" to system startup on systems that uses another init system.=Échec de l’ajout de GPU Screen Recorder au démarrage.\nCette option fonctionne uniquement sur les systèmes utilisant systemd.\nVous devez ajouter manuellement "gsr-ui" au démarrage sur les autres systèmes.
|
||||
To enable autorun: install and configure 'dex' (recommended), or manually add '%s' to your desktop autostart entries.=Pour activer le démarrage automatique : installez et configurez 'dex' (recommandé) ou ajoutez manuellement '%s' aux entrées de démarrage automatique du bureau.
|
||||
GPU Screen Recorder UI startup has been switched from systemd service to XDG autostart.=Le démarrage de GPU Screen Recorder UI a été basculé du service systemd vers le démarrage automatique XDG.
|
||||
GPU Screen Recorder UI autostart via systemd is deprecated. To migrate: install and configure 'dex' (recommended), or manually add '%s' to your desktop autostart entries.=Le démarrage automatique de GPU Screen Recorder UI via systemd est obsolète. Pour migrer : installez et configurez 'dex' (recommandé) ou ajoutez manuellement '%s' aux entrées de démarrage automatique du bureau.
|
||||
|
||||
# Wayland warning
|
||||
Wayland doesn't support GPU Screen Recorder UI properly,\nthings may not work as expected. Use X11 if you experience issues.=Wayland ne supporte pas correctement l’UI de GPU Screen Recorder,\ncertains éléments peuvent mal fonctionner. Utilisez X11 si vous rencontrez des problèmes.
|
||||
Wayland doesn't support GPU Screen Recorder UI properly, things may not work as expected. Use X11 if you experience issues.=Wayland ne supporte pas correctement l’UI de GPU Screen Recorder, certains éléments peuvent mal fonctionner. Utilisez X11 si vous rencontrez des problèmes.
|
||||
|
||||
# Hotkey conflicts
|
||||
Some keyboard remapping software conflicts with GPU Screen Recorder on your system.\nKeyboards have been ungrabbed, applications will now receive the hotkeys you press.=Certaines applications de remappage clavier entrent en conflit avec GPU Screen Recorder.\nLes claviers ont été libérés, les applications recevront désormais vos touches rapides.
|
||||
@@ -166,6 +168,7 @@ Start/stop recording with desktop portal:=Lancer/arrêter l’enregistrement via
|
||||
Take a screenshot with desktop portal:=Prendre une capture via le portail du bureau
|
||||
Start/stop recording a window:=Lancer/arrêter l’enregistrement d’une fenêtre
|
||||
Take a screenshot of a window:=Prendre une capture d’écran d’une fenêtre
|
||||
Press ESC to go back to the previous page/close the UI.=Appuyez sur ÉCHAP pour revenir à la page précédente/fermer l'interface utilisateur.
|
||||
|
||||
Clear hotkeys=Effacer les raccourcis
|
||||
Reset hotkeys to default=Réinitialiser les raccourcis par défaut
|
||||
@@ -382,11 +385,16 @@ Record in low-power mode=Enregistrer en mode basse consommation
|
||||
Record cursor=Enregistrer le curseur
|
||||
|
||||
Do not force the GPU to go into high performance mode when recording.\nMay affect recording performance, especially when playing a video at the same time.\nIf enabled then it's recommended to use sync to content frame rate mode to reduce power usage when idle.=Ne forcez pas le GPU en mode haute performance lors de l’enregistrement.\nPeut affecter les performances, surtout en lisant une vidéo en parallèle.\nSi activé, il est recommandé d’utiliser le mode “Synchroniser au contenu” pour réduire la consommation d’énergie au repos.
|
||||
Use vulkan video encoding instead of VAAPI/NVENC.\nEnabling this may result in better game performance while recording, especially on NVIDIA.\nNote that this option is experimental. There may be GPU driver issues that causes issues when this is enabled.=Utilisez l'encodage vidéo Vulkan au lieu de VAAPI/NVENC.\nL'activation de cette option peut améliorer les performances de jeu lors de l'enregistrement, notamment sur les cartes graphiques NVIDIA.\nNotez que cette option est expérimentale. Des problèmes de pilotes GPU peuvent survenir lorsqu'elle est activée.
|
||||
Enable vulkan video encoding (experimental)=Activer l'encodage vidéo Vulkan (expérimental)
|
||||
|
||||
Show %s notifications=Afficher les notifications %s
|
||||
Show %s status with scroll lock LED=Afficher le statut %s via la LED Arrêt Défil
|
||||
Show replay notifications=Afficher les notifications de relecture
|
||||
Show recording notifications=Afficher les notifications d'enregistrement
|
||||
Show streaming notifications=Afficher les notifications de diffusion
|
||||
Show replay status with scroll lock LED=Afficher l'état de relecture avec la LED de verrouillage du défilement
|
||||
Show recording status with scroll lock LED=Afficher l'état d'enregistrement avec la LED de verrouillage du défilement
|
||||
Show streaming status with scroll lock LED=Afficher l'état de diffusion avec la LED de verrouillage du défilement
|
||||
Recording indicator=Indicateur d’enregistrement
|
||||
recording=Enregistrement
|
||||
|
||||
Simple=Simple
|
||||
Audio track #%d=Piste audio #%d
|
||||
@@ -397,16 +405,16 @@ Estimated video file size per minute (excluding audio): %.2fMB=Taille estimée d
|
||||
# Replay settings
|
||||
Directory to save replays:=Répertoire pour sauvegarder les replays
|
||||
Replay indicator=Indicateur de replay
|
||||
replay=replay
|
||||
Turn on replay when starting a fullscreen application%s=Activer le replay lors du lancement d’une application plein écran %s
|
||||
Turn on replay when starting a game=Activez la rediffusion au début d'une partie
|
||||
Autostart=Démarrage automatique
|
||||
in RAM=En RAM
|
||||
on disk=Sur le disque
|
||||
Replay duration in seconds:=Durée du replay (secondes)
|
||||
Where should temporary replay data be stored?=Où stocker les données temporaires des replays ?
|
||||
RAM=RAM
|
||||
Disk (Not recommended on SSDs)=Disque (Non recommandé sur SSD)
|
||||
Turn on replay when this program starts=Activer le replay au démarrage du programme
|
||||
Turn on replay when power supply is connected=Activer le replay lorsque l’alimentation est branchée
|
||||
Only turn on replay if a power supply is connected=N'activez la lecture que si une alimentation est branchée
|
||||
Don't turn on replay automatically=Ne pas activer automatiquement le replay
|
||||
Restart replay on save=Redémarrer le replay après sauvegarde
|
||||
Estimated video max file size %s: %.2fMB.\nChange video bitrate or replay duration to change file size.=Taille maximale estimée du fichier vidéo %s : %.2f Mo.\nModifiez le débit ou la durée de replay pour changer la taille du fichier.
|
||||
@@ -422,4 +430,3 @@ Stream URL:=URL du flux
|
||||
Stream key:=Clé de diffusion
|
||||
Streaming info=Infos diffusion
|
||||
Streaming indicator=Indicateur de diffusion
|
||||
streaming=Diffusion
|
||||
|
||||
424
translations/hu.txt
Normal file
424
translations/hu.txt
Normal file
@@ -0,0 +1,424 @@
|
||||
# GPU Screen Recorder UI - Hungarian translation
|
||||
|
||||
# Important warning: we f'ed up a little bit and used %s for both strings and numbers in some places, such as time durations (they're fixed by the moment).
|
||||
# When translating, be careful to use the %d format specifier for numbers in those places.
|
||||
# Note that all translation strings should be on one line in these translation files. Some translations need to be on multiple lines and the newline character
|
||||
# should be replaced with \n.
|
||||
|
||||
# General UI
|
||||
Record=Felvétel
|
||||
Instant Replay=Azonnali visszajátszás
|
||||
Livestream=Élő közvetítés
|
||||
Settings=Beállítások
|
||||
|
||||
# Status messages
|
||||
Off=Kikapcsolva
|
||||
On=Bekapcsolva
|
||||
Not recording=Nincs felvétel
|
||||
Recording=Felvétel folyamatban
|
||||
Not streaming=Nincs közvetítés
|
||||
Streaming=Közvetítés folyamatban
|
||||
Paused=Szüneteltetve
|
||||
|
||||
# Button labels
|
||||
Start=Indítás
|
||||
Stop=Leállítás
|
||||
Stop and save=Leállítás és mentés
|
||||
Pause=Szüneteltetés
|
||||
Unpause=Folytatás
|
||||
Save=Mentés
|
||||
Save 1 min=1 perc mentése
|
||||
Save 10 min=10 perc mentése
|
||||
Turn on=Bekapcsolás
|
||||
Turn off=Kikapcsolás
|
||||
|
||||
# Notifications - Recording
|
||||
Recording has been paused=A felvétel szüneteltetve lett
|
||||
Recording has been unpaused=A felvétel folytatva lett
|
||||
Started recording %s=Felvétel elindítva: %s
|
||||
Saved a %s recording of %s to "%s"=%s hosszú felvétel mentve erről: %s ide: "%s"
|
||||
Saved a %s recording of %s=%s hosszú felvétel mentve erről: %s
|
||||
Failed to start/save recording=Nem sikerült elindítani vagy menteni a felvételt
|
||||
|
||||
# Notifications - Replay
|
||||
Replay stopped=A visszajátszás leállt
|
||||
Started replaying %s=Visszajátszás elindítva: %s
|
||||
Saving replay, this might take some time=Visszajátszás mentése, ez eltarthat egy ideig
|
||||
Saved a %s replay of %s to "%s"=%s hosszúságú visszajátszás mentve erről: %s ide: "%s"
|
||||
Saved a %s replay of %s=%s hosszúságú visszajátszás mentve erről: %s
|
||||
Replay stopped because of an error=A visszajátszás hiba miatt leállt
|
||||
Replay settings have been modified. You may need to restart replay to apply the changes.=A visszajátszás beállításai módosultak. A változtatások alkalmazásához újra kell indítani a visszajátszást
|
||||
|
||||
# Notifications - Streaming
|
||||
Streaming has stopped=A közvetítés leállt
|
||||
Started streaming %s=Közvetítés elindítva: %s
|
||||
Streaming stopped because of an error=A közvetítés hiba miatt leállt
|
||||
Streaming settings have been modified. You may need to restart streaming to apply the changes.=A közvetítés beállításai módosultak. A változtatások alkalmazásához újra kell indítani a közvetítést
|
||||
|
||||
# Notifications - Screenshot
|
||||
Saved a screenshot of %s to "%s"=Képernyőkép mentve (%s) ide: "%s"
|
||||
Saved a screenshot of %s=Képernyőkép mentve (%s)
|
||||
Failed to take a screenshot=Nem sikerült a képernyőkép elkészítése
|
||||
|
||||
# Error messages
|
||||
Another instance of GPU Screen Recorder UI is already running. Press Alt+Z to open the UI.=A GPU Screen Recorder UI már fut. Nyomd meg az Alt+Z-t a felület megnyitásához.
|
||||
GPU Screen Recorder is already running in another process. Please close it before using GPU Screen Recorder UI.=A GPU Screen Recorder már fut egy másik folyamatban. Zárd be, mielőtt használod a felületet.
|
||||
Failed to start replay, capture target "%s" is invalid. Please change capture target in settings=Nem sikerült elindítani a visszajátszást, a rögzítési cél "%s" érvénytelen. Módosítsd a rögzítési célt a beállításokban.
|
||||
Failed to start recording, capture target "%s" is invalid. Please change capture target in settings=Nem sikerült elindítani a felvételt, a rögzítési cél "%s" érvénytelen. Módosítsd a rögzítési célt a beállításokban.
|
||||
Failed to start streaming, capture target "%s" is invalid. Please change capture target in settings=Nem sikerült elindítani a közvetítést, a rögzítési cél "%s" érvénytelen. Módosítsd a rögzítési célt a beállításokban.
|
||||
Failed to take a screenshot, capture target "%s" is invalid. Please change capture target in settings=Nem sikerült a képernyőkép elkészítése, a rögzítési cél "%s" érvénytelen. Módosítsd a rögzítési célt a beállításokban.
|
||||
|
||||
Unable to start recording when replay is turned on. Turn off replay before starting recording.=Nem lehet felvételt indítani, ha a visszajátszás be van kapcsolva. A felvétel előtt kapcsold ki a visszajátszást.
|
||||
Unable to start streaming when replay is turned on. Turn off replay before starting streaming.=Nem lehet közvetítést indítani, ha a visszajátszás be van kapcsolva. A közvetítés előtt kapcsold ki a visszajátszást.
|
||||
Unable to start streaming when recording. Stop recording before starting streaming.=Nem lehet közvetítést indítani felvétel közben. Állítsd le a felvételt a közvetítéshez.
|
||||
Unable to start recording when streaming. Stop streaming before starting recording.=Nem lehet felvételt indítani közvetítés közben. Állítsd le a közvetítést a felvételhez.
|
||||
Unable to start replay when recording. Stop recording before starting replay.=Nem lehet visszajátszást indítani felvétel közben. Állítsd le a felvételt a visszajátszáshoz.
|
||||
Unable to start replay when streaming. Stop streaming before starting replay.=Nem lehet visszajátszást indítani közvetítés közben. Állítsd le a közvetítést a visszajátszáshoz.
|
||||
|
||||
Started recording in the replay session=Felvétel indítva visszajátszás közben
|
||||
Started recording in the streaming session=Felvétel indítva közvetítés közben
|
||||
|
||||
Failed to start region capture=Nem sikerült a terület rögzítése
|
||||
Failed to start window capture=Nem sikerült az ablak rögzítése
|
||||
No window selected=Nincs kiválasztott ablak
|
||||
|
||||
Streaming stopped because of an error. Verify if settings are correct=A közvetítés hiba miatt leállt. Ellenőrizd a beállításokat
|
||||
%s. Verify if settings are correct=%s. Ellenőrizd a beállításokat
|
||||
|
||||
# GPU Screen Recorder errors
|
||||
Desktop portal capture failed. Either you canceled the desktop portal or your Wayland compositor doesn't support desktop portal capture or it's incorrectly setup on your system.=Az asztali portál rögzítése sikertelen. Lehet, hogy megszakítottad, vagy a Wayland nem támogatja, vagy nincs megfelelően beállítva.
|
||||
Monitor capture failed. The monitor you are trying to capture is invalid. Please validate your capture settings.=A monitor rögzítése sikertelen. A kiválasztott monitor érvénytelen. Ellenőrizd a beállításokat.
|
||||
Capture failed. Neither H264, HEVC nor AV1 video codecs are supported on your system or you are trying to capture at a resolution higher than your system supports for each video codec.=A rögzítés sikertelen. A H264, HEVC és AV1 kodekek nem támogatottak vagy túl nagy felbontást választottál.
|
||||
Capture failed. Your system doesn't support the resolution you are trying to record at with the video codec you have chosen. Change capture resolution or video codec and try again. Note: AV1 supports the highest resolution, then HEVC and then H264.=A rögzítés sikertelen. A rendszer nem támogatja a választott felbontást. Válts felbontást vagy kodeket. Megjegyzés: A nagyobb felbontásokat leginkább az AV1 támogatja, ezt követi a HEVC, majd a H264.
|
||||
Capture failed. Your system doesn't support the video codec you have chosen. Change video codec and try again.=A rögzítés sikertelen. A választott kodek nem támogatott. Válassz másikat.
|
||||
Stopped capture because the user canceled the desktop portal=A rögzítés leállt, mert a felhasználó megszakította a portált
|
||||
Failed to take a screenshot. Verify if settings are correct=Nem sikerült a képernyőkép elkészítése. Ellenőrizd a beállításokat
|
||||
|
||||
# Launch errors
|
||||
Failed to launch gpu-screen-recorder to start replay=Nem sikerült elindítani a gpu-screen-recorder-t a visszajátszáshoz
|
||||
Failed to launch gpu-screen-recorder to start recording=Nem sikerült elindítani a gpu-screen-recorder-t a felvételhez
|
||||
Failed to launch gpu-screen-recorder to start streaming=Nem sikerült elindítani a gpu-screen-recorder-t a közvetítéshez
|
||||
Failed to launch gpu-screen-recorder to take a screenshot=Nem sikerült elindítani a gpu-screen-recorder-t képernyőképhez
|
||||
|
||||
# System startup notifications
|
||||
Failed to add GPU Screen Recorder to system startup=Nem sikerült hozzáadni a GPU Screen Recordert az automatikus indításhoz
|
||||
Failed to remove GPU Screen Recorder from system startup=Nem sikerült eltávolítani a GPU Screen Recorder-t az automatikus indításból
|
||||
To enable autorun: install and configure 'dex' (recommended), or manually add '%s' to your desktop autostart entries.=Automatikus indításhoz: telepítsd és konfiguráld a 'dex'-et (ajánlott), vagy add hozzá manuálisan: '%s'
|
||||
GPU Screen Recorder UI startup has been switched from systemd service to XDG autostart.=Az automatikus indítás systemd-ről XDG autostartra váltott át
|
||||
GPU Screen Recorder UI autostart via systemd is deprecated. To migrate: install and configure 'dex' (recommended), or manually add '%s' to your desktop autostart entries.=A GPU Screen Recorder systemd-alapú automatikus indítása elavult. Az átköltöztetéshez telepítsd és konfiguráld a 'dex'-et, vagy add hozzá manuálisan: '%s'
|
||||
|
||||
# Wayland warning
|
||||
Wayland doesn't support GPU Screen Recorder UI properly, things may not work as expected. Use X11 if you experience issues.=A Wayland nem támogatja megfelelően a GPU Screen Recordert. Használj X11-et, ha problémát észlelsz.
|
||||
|
||||
# Hotkey conflicts
|
||||
Some keyboard remapping software conflicts with GPU Screen Recorder on your system.\nKeyboards have been ungrabbed, applications will now receive the hotkeys you press.=Egyes billentyűzet-átkötő programok ütköznek a GPU Screen Recorderrel.\nA billentyűzet lefoglalása megszűnt, a gyorsbillentyűket mostantól az alkalmazások fogják megkapni.
|
||||
|
||||
# Capture targets
|
||||
this monitor=Jelenlegi monitor
|
||||
window=Ablak
|
||||
window "%s"="%s" ablak
|
||||
window %s=%s ablak
|
||||
focused=Fókuszált ablak
|
||||
region=Kijelölt terület
|
||||
portal=Asztali portál
|
||||
|
||||
# if your language has simple plural forms, you can just use:
|
||||
%d second=%d másodperc
|
||||
%d minute=%d perc
|
||||
%d hour=%d óra
|
||||
%d seconds=%d másodperc
|
||||
%d minutes=%d perc
|
||||
%d hours=%d óra
|
||||
|
||||
|
||||
# Global Settings Page UI elements
|
||||
Accent color=Kiemelőszín
|
||||
Red=Piros
|
||||
Green=Zöld
|
||||
Blue=Kék
|
||||
|
||||
Start program on system startup?=Program indítása rendszerindításkor?
|
||||
Yes=Igen
|
||||
No=Nem
|
||||
|
||||
Enable keyboard hotkeys?=Gyorsbillentyűk engedélyezése?
|
||||
Yes, but only grab virtual devices (supports some input remapping software)=Igen, de csak virtuális eszközök kezelése (egyes bemenet-átirányító szoftvereket támogat)
|
||||
Yes, but don't grab devices (supports all input remapping software)=Igen, eszközök lefoglalása nélkül (minden bemenet-átirányító szoftvert támogat)
|
||||
|
||||
Show/hide UI:=Felület megjelenítése/elrejtése:
|
||||
Turn replay on/off:=Visszajátszás be/ki:
|
||||
Save replay:=Visszajátszás mentése:
|
||||
Save 1 minute replay:=1 perces visszajátszás mentése:
|
||||
Save 10 minute replay:=10 perces visszajátszás mentése:
|
||||
Start/stop recording:=Felvétel indítása/leállítása:
|
||||
Pause/unpause recording:=Felvétel szüneteltetése/folytatása:
|
||||
Start/stop recording a region:=Kijelölt terület felvételének indítása/leállítása:
|
||||
Start/stop streaming:=Közvetítés indítása/leállítása:
|
||||
Take a screenshot:=Képernyőkép készítése:
|
||||
Take a screenshot of a region:=Képernyőkép készítése kijelölt területről:
|
||||
Start/stop recording with desktop portal:=Felvétel indítása/leállítása asztali portállal:
|
||||
Take a screenshot with desktop portal:=Képernyőkép készítése asztali portállal:
|
||||
Start/stop recording a window:=Ablak felvételének indítása/leállítása:
|
||||
Take a screenshot of a window:=Képernyőkép készítése ablakról:
|
||||
Press ESC to go back to the previous page/close the UI.=Az előző oldalra való visszatéréshez/a felhasználói felület bezárásához nyomja meg az ESC billentyűt.
|
||||
|
||||
Clear hotkeys=Gyorsbillentyűk törlése
|
||||
Reset hotkeys to default=Gyorsbillentyűk visszaállítása alapértelmezettre
|
||||
|
||||
Enable controller hotkeys?=Kontrolleres gyorsbillentyűk engedélyezése?
|
||||
Press=Nyomd meg
|
||||
and=és
|
||||
|
||||
Notification speed=Értesítések sebessége
|
||||
Normal=Normál
|
||||
Fast=Gyors
|
||||
|
||||
Language=Nyelv
|
||||
System language=Rendszer nyelve
|
||||
|
||||
Exit program=Kilépés
|
||||
Go back to the old UI=Visszatérés a régi felülethez
|
||||
|
||||
If you would like to donate you can do so by donating at https://buymeacoffee.com/dec05eba:=Ha szeretnél támogatni, megteheted itt: https://buymeacoffee.com/dec05eba
|
||||
Donate=Adományozás
|
||||
All donations go toward developing software (including GPU Screen Recorder)\nand buying hardware to test the software.=Minden adomány a szoftverek fejlesztésére (beleértve a GPU Screen Recordert)\nés a teszteléshez szükséges hardverek beszerzésére fordítódik.
|
||||
|
||||
# Subsection headers
|
||||
Global=Általános
|
||||
Back=Vissza
|
||||
|
||||
Appearance=Megjelenés
|
||||
Startup=Indítás
|
||||
Keyboard hotkeys=Gyorsbillentyűk
|
||||
Controller hotkeys=Kontrolleres gyorsbillentyűk
|
||||
Application options=Alkalmazás beállításai
|
||||
Application info=Alkalmazásinformációk
|
||||
Donate=Adományozás
|
||||
|
||||
# Version info strings
|
||||
GSR version: %s=GSR verzió: %s
|
||||
GSR-UI version: %s=GSR-UI verzió: %s
|
||||
Flatpak version: %s=Flatpak verzió: %s
|
||||
GPU vendor: %s=GPU-gyártó: %s
|
||||
|
||||
# Hotkey configuration dialog
|
||||
Press a key combination to use for the hotkey: "%s"=Nyomj meg egy billentyűkombinációt a következőhöz: „%s”
|
||||
Alpha-numerical keys can't be used alone in hotkeys, they have to be used one or more of these keys: Alt, Ctrl, Shift and Super.\nPress Esc to cancel or Backspace to remove the hotkey.=Az alfanumerikus billentyűk önmagukban nem használhatók gyorsbillentyűként, csak az alábbiakkal együtt: Alt, Ctrl, Shift vagy Super.\nNyomd meg az Esc-et a megszakításhoz vagy a Backspace-t a törléshez.
|
||||
|
||||
# Hotkey action names (without colons - these appear in the dialog)
|
||||
Show/hide UI=Felület megjelenítése/elrejtése
|
||||
Turn replay on/off=Visszajátszás be/ki
|
||||
Save replay=Visszajátszás mentése
|
||||
Save 1 minute replay=1 perces visszajátszás mentése
|
||||
Save 10 minute replay=10 perces visszajátszás mentése
|
||||
Start/stop recording=Felvétel indítása/leállítása
|
||||
Pause/unpause recording=Felvétel szüneteltetése/folytatása
|
||||
Start/stop recording a region=Kijelölt terület felvételének indítása/leállítása
|
||||
Start/stop recording a window=Ablak felvételének indítása/leállítása
|
||||
Start/stop recording with desktop portal=Felvétel indítása/leállítása asztali portállal
|
||||
Start/stop streaming=Közvetítés indítása/leállítása
|
||||
Take a screenshot=Képernyőkép készítése
|
||||
Take a screenshot of a region=Képernyőkép készítése kijelölt területről
|
||||
Take a screenshot of a window=Képernyőkép készítése ablakról
|
||||
Take a screenshot with desktop portal=Képernyőkép készítése asztali portállal
|
||||
|
||||
# Controller hotkey descriptions
|
||||
to show/hide the UI=a felület megjelenítéséhez/elrejtéséhez
|
||||
to take a screenshot=képernyőkép készítéséhez
|
||||
to save a replay=visszajátszás mentéséhez
|
||||
to start/stop recording=felvétel indításához/leállításához
|
||||
to turn replay on/off=visszajátszás be-/kikapcsolásához
|
||||
to save a 1 minute replay=1 perces visszajátszás mentéséhez
|
||||
to save a 10 minute replay=10 perces visszajátszás mentéséhez
|
||||
|
||||
# Error message for duplicate hotkey
|
||||
The hotkey %s is already used for something else=A(z) %s gyorsbillentyű már használatban van más művelethez
|
||||
|
||||
|
||||
# Screenshot settings page
|
||||
Screenshot=Képernyőkép
|
||||
Capture=Rögzítés
|
||||
Image=Kép
|
||||
File info=Fájlinformációk
|
||||
General=Általános
|
||||
Screenshot indicator=Képernyőkép-jelző
|
||||
Script=Parancsfájl
|
||||
File=Fájl
|
||||
|
||||
Back=Vissza
|
||||
Save=Mentés
|
||||
Cancel=Mégse
|
||||
|
||||
Capture source:=Rögzítési forrás:
|
||||
Window=Ablak
|
||||
Region=Kijelölt terület
|
||||
Desktop portal=Asztali portál
|
||||
Monitor %s (%dx%d)=%s monitor (%dx%d)
|
||||
Screen=Képernyő
|
||||
|
||||
Image resolution limit:=Képfelbontás korlát:
|
||||
Change image resolution=Képfelbontás módosítása
|
||||
Restore portal session=Portál munkamenet visszaállítása
|
||||
|
||||
Image quality:=Képminőség:
|
||||
Medium=Közepes
|
||||
High=Magas
|
||||
Very high (Recommended)=Nagyon magas (ajánlott)
|
||||
Ultra=Ultra
|
||||
|
||||
Record cursor=Egérmutató felvétele
|
||||
|
||||
Directory to save screenshots:=Képernyőképek mentési mappája:
|
||||
Image format:=Képformátum:
|
||||
|
||||
Save screenshot in a folder based on the games name=Képernyőkép mentése a játék neve szerinti mappába
|
||||
|
||||
Save screenshot to clipboard=Képernyőkép mentése vágólapra
|
||||
Save screenshot to clipboard (Not supported properly by Wayland)=Képernyőkép mentése vágólapra (Wayland alatt nem teljesen támogatott)
|
||||
Save screenshot to disk=Képernyőkép mentése lemezre
|
||||
|
||||
Show screenshot notifications=Képernyőkép értesítések megjelenítése
|
||||
Blink scroll lock led when taking a screenshot=Scroll Lock LED villogtatása képernyőkép készítésekor
|
||||
|
||||
Command to open the screenshot with:=Parancs a képernyőkép megnyitásához:
|
||||
|
||||
|
||||
# Settings Page UI elements - дополнения
|
||||
|
||||
# View modes
|
||||
Simple view=Egyszerű nézet
|
||||
Advanced view=Speciális nézet
|
||||
|
||||
# Capture settings
|
||||
Follow focused window=Fókuszált ablak követése
|
||||
Focused monitor=Fókuszált monitor
|
||||
|
||||
Area size:=Terület mérete:
|
||||
Video resolution limit:=Videófelbontás korlát:
|
||||
Change video resolution=Videófelbontás módosítása
|
||||
Restore portal session=Portál munkamenet visszaállítása
|
||||
|
||||
# Webcam settings
|
||||
Webcam=Webkamera
|
||||
Webcam source:=Webkamera forrása:
|
||||
None=Nincs
|
||||
Video format:=Videóformátum:
|
||||
Auto (recommended)=Automatikus (ajánlott)
|
||||
YUYV=YUYV
|
||||
Motion-JPEG=Motion-JPEG
|
||||
Video setup:=Videóbeállítás
|
||||
* Right click in the bottom right corner to resize the webcam=* Jobb kattintás a jobb alsó sarokban a webkamera átméretezéséhez
|
||||
Flip camera horizontally=Kamera tükrözése vízszintesen
|
||||
|
||||
# Audio settings
|
||||
Audio=Hang
|
||||
Audio codec:=Hangkodek:
|
||||
Opus (Recommended)=Opus (ajánlott)
|
||||
AAC=AAC
|
||||
|
||||
Directory to save videos:=Videók mentési mappája:
|
||||
Output device:=Kimeneti eszköz:
|
||||
Input device: =Bemeneti eszköz:
|
||||
# yes, these spaces are intentional
|
||||
Application: =Alkalmazás:
|
||||
Custom...=Egyéni...
|
||||
|
||||
Save video in a folder based on the games name%s=Videó mentése a játék neve szerinti mappába%s
|
||||
(X11 applications only)= (csak X11 alkalmazásoknál)
|
||||
|
||||
Add audio track=Hangsáv hozzáadása
|
||||
Add input device=Bemeneti eszköz hozzáadása
|
||||
Add output device=Kimeneti eszköz hozzáadása
|
||||
Add application audio=Alkalmazáshang hozzáadása
|
||||
Record audio from all applications except the selected ones=Hang rögzítése minden alkalmazásból a kiválasztottak kivételével
|
||||
Recording output devices and application audio may record all output audio, which is likely\nnot what you want to do. Remove the output devices.=A kimeneti eszközök és az alkalmazáshang rögzítése minden kimeneti hangot felvehet, ami valószínűleg\nnem kívánt. Távolítsd el a kimeneti eszközöket.
|
||||
|
||||
Video=Videó
|
||||
|
||||
# Video codec settings
|
||||
Video codec:=Videókodek:
|
||||
H264=H264
|
||||
HEVC=HEVC
|
||||
HEVC (10 bit, reduces banding)=HEVC (10 bit, csökkenti a sávosodást)
|
||||
HEVC (HDR)=HEVC (HDR)
|
||||
AV1=AV1
|
||||
AV1 (10 bit, reduces banding)=AV1 (10 bit, csökkenti a sávosodást)
|
||||
AV1 (HDR)=AV1 (HDR)
|
||||
VP8=VP8
|
||||
VP9=VP9
|
||||
H264 Software Encoder (Slow, not recommended)=H264 szoftveres kódoló (lassú, nem ajánlott)
|
||||
|
||||
# Video quality and bitrate
|
||||
Video quality:=Videóminőség:
|
||||
Very high=Nagyon magas
|
||||
Video bitrate (Kbps):=Videó bitráta (Kbps):
|
||||
Constant bitrate=Állandó bitráta
|
||||
Constant bitrate (Recommended)=Állandó bitráta (ajánlott)
|
||||
|
||||
# Frame rate settings
|
||||
Frame rate:=Képkockasebesség:
|
||||
Frame rate mode:=Képkockasebesség mód:
|
||||
Auto (Recommended)=Automatikus (ajánlott)
|
||||
Constant=Állandó
|
||||
Variable=Változó
|
||||
Sync to content=Tartalomhoz igazítás
|
||||
Sync to content (Only X11 or desktop portal capture)=Tartalomhoz igazítás (csak X11 vagy asztali portál rögzítésnél)
|
||||
|
||||
# Color range
|
||||
Color range:=Színtartomány:
|
||||
Limited=Korlátozott
|
||||
Full=Teljes
|
||||
|
||||
# Container format
|
||||
Container:=Konténer:
|
||||
|
||||
# Recording settings
|
||||
Record in low-power mode=Felvétel energiatakarékos módban
|
||||
Record cursor=Egérmutató felvétele
|
||||
|
||||
Do not force the GPU to go into high performance mode when recording.\nMay affect recording performance, especially when playing a video at the same time.\nIf enabled then it's recommended to use sync to content frame rate mode to reduce power usage when idle.=Ne kényszerítse a GPU-t nagy teljesítményű módba felvétel közben.\nEz befolyásolhatja a felvétel teljesítményét, különösen videólejátszás közben.\nHa engedélyezve van, ajánlott a tartalomhoz igazított képkockasebesség mód használata az energiafogyasztás csökkentésére üresjáratban.
|
||||
Use vulkan video encoding instead of VAAPI/NVENC.\nEnabling this may result in better game performance while recording, especially on NVIDIA.\nNote that this option is experimental. There may be GPU driver issues that causes issues when this is enabled.=Vulkan videokódolás használata VAAPI/NVENC helyett.\nEnnek engedélyezése jobb játékteljesítményt eredményezhet felvétel közben, különösen NVIDIA-n.\nVegye figyelembe, hogy ez a beállítás kísérleti jellegű. Engedélyezése esetén előfordulhatnak GPU-illesztőprogram problémák.
|
||||
Enable vulkan video encoding (experimental)=Vulkan videókódolás engedélyezése (kísérleti)
|
||||
|
||||
Show replay notifications=Visszajátszási értesítések megjelenítése
|
||||
Show recording notifications=Felvételi értesítések megjelenítése
|
||||
Show streaming notifications=Streamelési értesítések megjelenítése
|
||||
Show replay status with scroll lock LED=Visszajátszás állapotának megjelenítése a Scroll Lock LED-del
|
||||
Show recording status with scroll lock LED=Felvétel állapotának megjelenítése a Scroll Lock LED-del
|
||||
Show streaming status with scroll lock LED=Streamelés állapotának megjelenítése a Scroll Lock LED-del
|
||||
Recording indicator=Felvétel-jelző
|
||||
|
||||
Simple=Egyszerű
|
||||
Audio track #%d=Hangsáv #%d
|
||||
Output device=Kimeneti eszköz
|
||||
Input device: =Bemeneti eszköz:
|
||||
Estimated video file size per minute (excluding audio): %.2fMB=Várható videófájl-méret percenként (hang nélkül): %.2fMB
|
||||
|
||||
# Replay settings
|
||||
Directory to save replays:=Visszajátszások mentési mappája:
|
||||
Replay indicator=Visszajátszás-jelző
|
||||
Turn on replay when starting a game=Kapcsolja be az újrajátszást játék indításakor
|
||||
Autostart=Automatikus indítás
|
||||
in RAM=RAM-ban
|
||||
on disk=lemezen
|
||||
Replay duration in seconds:=Visszajátszás hossza másodpercben:
|
||||
Where should temporary replay data be stored?=Hol legyenek tárolva az ideiglenes visszajátszási adatok?
|
||||
RAM=RAM
|
||||
Disk (Not recommended on SSDs)=Lemez (SSD-n nem ajánlott)
|
||||
Turn on replay when this program starts=Visszajátszás bekapcsolása a program indításakor
|
||||
Only turn on replay if a power supply is connected=Csak akkor kapcsolja be a visszajátszást, ha a tápegység csatlakoztatva van
|
||||
Don't turn on replay automatically=Ne kapcsolja be automatikusan a visszajátszást
|
||||
Restart replay on save=Visszajátszás újraindítása mentéskor
|
||||
Estimated video max file size %s: %.2fMB.\nChange video bitrate or replay duration to change file size.=Becsült maximális videófájl-méret %s: %.2fMB.\nA fájlméret módosításához változtasd meg a bitrátát vagy a visszajátszás hosszát.
|
||||
|
||||
# Streaming settings
|
||||
Stream service:=Közvetítési szolgáltatás:
|
||||
Twitch=Twitch
|
||||
YouTube=YouTube
|
||||
Kick=Kick
|
||||
Rumble=Rumble
|
||||
Custom=Egyéni
|
||||
Stream URL:=Közvetítési URL:
|
||||
Stream key:=Közvetítési kulcs:
|
||||
Streaming info=Közvetítési információk
|
||||
Streaming indicator=Közvetítés-jelző
|
||||
436
translations/ja.txt
Normal file
436
translations/ja.txt
Normal file
@@ -0,0 +1,436 @@
|
||||
# GPU Screen Recorder UI - Japanese translation
|
||||
|
||||
# Important warning: we f'ed up a little bit and used %s for both strings and numbers in some places, such as time durations (they're fixed by the moment).
|
||||
# When translating, be careful to use the %d format specifier for numbers in those places.
|
||||
# Note that all translation strings should be on one line in these translation files. Some translations need to be on multiple lines and the newline character
|
||||
# should be replaced with \n.
|
||||
|
||||
# General UI
|
||||
Record=録画
|
||||
Instant Replay=インスタントリプレイ
|
||||
Livestream=ライブ配信
|
||||
Settings=設定
|
||||
|
||||
# Status messages
|
||||
Off=オフ
|
||||
On=オン
|
||||
Not recording=録画していません
|
||||
Recording=録画中
|
||||
Not streaming=配信していません
|
||||
Streaming=配信中
|
||||
Paused=一時停止中
|
||||
|
||||
# Button labels
|
||||
Start=開始
|
||||
Stop=停止
|
||||
Stop and save=停止して保存
|
||||
Pause=一時停止
|
||||
Unpause=再開
|
||||
Save=保存
|
||||
Save 1 min=1分を保存
|
||||
Save 10 min=10分を保存
|
||||
Turn on=オンにする
|
||||
Turn off=オフにする
|
||||
|
||||
# Notifications - Recording
|
||||
Recording has been paused=録画を一時停止しました
|
||||
Recording has been unpaused=録画を再開しました
|
||||
Started recording %s=%s の録画を開始しました
|
||||
Saved a %s recording of %s to "%s"=%s の %s を "%s" に保存しました
|
||||
Saved a %s recording of %s=%s の %s を保存しました
|
||||
Failed to start/save recording=録画の開始または保存に失敗しました
|
||||
|
||||
# Notifications - Replay
|
||||
Replay stopped=リプレイを停止しました
|
||||
Started replaying %s=%s のリプレイを開始しました
|
||||
Saving replay, this might take some time=リプレイを保存しています 少し時間がかかる場合があります
|
||||
Saved a %s replay of %s to "%s"=%s の %s を "%s" に保存しました
|
||||
Saved a %s replay of %s=%s の %s を保存しました
|
||||
Replay stopped because of an error=エラーのためリプレイを停止しました
|
||||
Replay settings have been modified. You may need to restart replay to apply the changes.=リプレイ設定が変更されました変更を反映するにはリプレイの再起動が必要な場合があります
|
||||
|
||||
# Notifications - Streaming
|
||||
Streaming has stopped=配信を停止しました
|
||||
Started streaming %s=%s の配信を開始しました
|
||||
Streaming stopped because of an error=エラーのため配信を停止しました
|
||||
Streaming settings have been modified. You may need to restart streaming to apply the changes.=配信設定が変更されました変更を反映するには配信の再起動が必要な場合があります
|
||||
|
||||
# Notifications - Screenshot
|
||||
Saved a screenshot of %s to "%s"=%s のスクリーンショットを "%s" に保存しました
|
||||
Saved a screenshot of %s=%s のスクリーンショットを保存しました
|
||||
Failed to take a screenshot=スクリーンショットの取得に失敗しました
|
||||
|
||||
# Error messages
|
||||
Another instance of GPU Screen Recorder UI is already running. Press Alt+Z to open the UI.=GPU Screen Recorder UI はすでに起動しています Alt+Z で UI を開けます
|
||||
GPU Screen Recorder is already running in another process. Please close it before using GPU Screen Recorder UI.=GPU Screen Recorder が別プロセスですでに動作しています GPU Screen Recorder UI を使う前に終了してください
|
||||
Failed to start replay, capture target "%s" is invalid. Please change capture target in settings=リプレイを開始できません キャプチャ対象 "%s" が無効です 設定でキャプチャ対象を変更してください
|
||||
Failed to start recording, capture target "%s" is invalid. Please change capture target in settings=録画を開始できません キャプチャ対象 "%s" が無効です 設定でキャプチャ対象を変更してください
|
||||
Failed to start streaming, capture target "%s" is invalid. Please change capture target in settings=配信を開始できません キャプチャ対象 "%s" が無効です 設定でキャプチャ対象を変更してください
|
||||
Failed to take a screenshot, capture target "%s" is invalid. Please change capture target in settings=スクリーンショットを取得できません キャプチャ対象 "%s" が無効です 設定でキャプチャ対象を変更してください
|
||||
|
||||
Unable to start recording when replay is turned on. Turn off replay before starting recording.=リプレイがオンの間は録画を開始できません 録画前にリプレイをオフにしてください
|
||||
Unable to start streaming when replay is turned on. Turn off replay before starting streaming.=リプレイがオンの間は配信を開始できません 配信前にリプレイをオフにしてください
|
||||
Unable to start streaming when recording. Stop recording before starting streaming.=録画中は配信を開始できません 配信前に録画を停止してください
|
||||
Unable to start recording when streaming. Stop streaming before starting recording.=配信中は録画を開始できません 録画前に配信を停止してください
|
||||
Unable to start replay when recording. Stop recording before starting replay.=録画中はリプレイを開始できません リプレイ前に録画を停止してください
|
||||
Unable to start replay when streaming. Stop streaming before starting replay.=配信中はリプレイを開始できません リプレイ前に配信を停止してください
|
||||
|
||||
Started recording in the replay session=リプレイセッション内で録画を開始しました
|
||||
Started recording in the streaming session=配信セッション内で録画を開始しました
|
||||
|
||||
Failed to start region capture=範囲キャプチャの開始に失敗しました
|
||||
Failed to start window capture=ウィンドウキャプチャの開始に失敗しました
|
||||
No window selected=ウィンドウが選択されていません
|
||||
|
||||
Streaming stopped because of an error. Verify if settings are correct=エラーのため配信を停止しました 設定が正しいか確認してください
|
||||
%s. Verify if settings are correct=%s 設定が正しいか確認してください
|
||||
|
||||
# GPU Screen Recorder errors
|
||||
Desktop portal capture failed. Either you canceled the desktop portal or your Wayland compositor doesn't support desktop portal capture or it's incorrectly setup on your system.=デスクトップポータルでのキャプチャに失敗しました ポータルをキャンセルしたか 使用中の Wayland コンポジタがデスクトップポータルキャプチャに対応していないか システム設定が正しくありません
|
||||
Monitor capture failed. The monitor you are trying to capture is invalid. Please validate your capture settings.=モニタキャプチャに失敗しました 選択したモニタが無効です キャプチャ設定を確認してください
|
||||
Capture failed. Neither H264, HEVC nor AV1 video codecs are supported on your system or you are trying to capture at a resolution higher than your system supports for each video codec.=キャプチャに失敗しました システムが H264 HEVC AV1 をサポートしていないか 選択した解像度が各コーデックの上限を超えています
|
||||
Capture failed. Your system doesn't support the resolution you are trying to record at with the video codec you have chosen. Change capture resolution or video codec and try again. Note: AV1 supports the highest resolution, then HEVC and then H264.=キャプチャに失敗しました 選択したコーデックではこの解像度を録画できません 解像度またはコーデックを変更して再試行してください 注記 AV1 が最も高解像度に対応し 次に HEVC その次に H264 です
|
||||
Capture failed. Your system doesn't support the video codec you have chosen. Change video codec and try again.=キャプチャに失敗しました 選択したビデオコーデックはこのシステムでサポートされていません コーデックを変更して再試行してください
|
||||
Stopped capture because the user canceled the desktop portal=ユーザーがデスクトップポータルをキャンセルしたためキャプチャを停止しました
|
||||
Failed to take a screenshot. Verify if settings are correct=スクリーンショットの取得に失敗しました 設定が正しいか確認してください
|
||||
|
||||
# Launch errors
|
||||
Failed to launch gpu-screen-recorder to start replay=リプレイ開始のための gpu-screen-recorder 起動に失敗しました
|
||||
Failed to launch gpu-screen-recorder to start recording=録画開始のための gpu-screen-recorder 起動に失敗しました
|
||||
Failed to launch gpu-screen-recorder to start streaming=配信開始のための gpu-screen-recorder 起動に失敗しました
|
||||
Failed to launch gpu-screen-recorder to take a screenshot=スクリーンショット取得のための gpu-screen-recorder 起動に失敗しました
|
||||
|
||||
# System startup notifications
|
||||
Failed to add GPU Screen Recorder to system startup=GPU Screen Recorder をシステム起動時に追加できませんでした
|
||||
Failed to remove GPU Screen Recorder from system startup=GPU Screen Recorder をシステム起動時設定から削除できませんでした
|
||||
Failed to add GPU Screen Recorder to system startup.\nThis option only works on systems that use systemd.\nYou have to manually add "gsr-ui" to system startup on systems that uses another init system.=GPU Screen Recorder をシステム起動時に追加できませんでした\nこの機能は systemd を使うシステムでのみ動作します\n別の init system を使う場合は "gsr-ui" を手動で自動起動に追加してください
|
||||
To enable autorun: install and configure 'dex' (recommended), or manually add '%s' to your desktop autostart entries.=自動起動を有効にするには 'dex' をインストールして設定するか(推奨) '%s' をデスクトップの自動起動項目に手動で追加してください
|
||||
GPU Screen Recorder UI startup has been switched from systemd service to XDG autostart.=GPU Screen Recorder UI の起動方式は systemd service から XDG autostart に切り替わりました
|
||||
GPU Screen Recorder UI autostart via systemd is deprecated. To migrate: install and configure 'dex' (recommended), or manually add '%s' to your desktop autostart entries.=systemd 経由の GPU Screen Recorder UI 自動起動は非推奨です移行するには 'dex' をインストールして設定するか(推奨) '%s' をデスクトップの自動起動項目に手動で追加してください
|
||||
|
||||
# Wayland warning
|
||||
Wayland doesn't support GPU Screen Recorder UI properly, things may not work as expected. Use X11 if you experience issues.=Wayland では GPU Screen Recorder UI が完全にはサポートされていません\n期待通りに動かない場合があります 問題があれば X11 を使ってください
|
||||
|
||||
# Hotkey conflicts
|
||||
Some keyboard remapping software conflicts with GPU Screen Recorder on your system.\nKeyboards have been ungrabbed, applications will now receive the hotkeys you press.=キーボードリマップソフトの一部が GPU Screen Recorder と競合しています\nキーボードの grab を解除したため 押したホットキーはアプリ側でも受け取られます
|
||||
|
||||
# Capture targets
|
||||
this monitor=このモニタ
|
||||
window=ウィンドウ
|
||||
window "%s"=ウィンドウ "%s"
|
||||
window %s=ウィンドウ %s
|
||||
focused=フォーカス中
|
||||
region=範囲
|
||||
portal=ポータル
|
||||
|
||||
# Time durations (used in recording/replay saved notifications, remember to use %d for numbers)
|
||||
# if you have complex plural forms in your language, please use the following format:
|
||||
%d second_one=%d 秒
|
||||
%d second_few=%d 秒
|
||||
%d second_many=%d 秒
|
||||
%d minute_one=%d 分
|
||||
%d minute_few=%d 分
|
||||
%d minute_many=%d 分
|
||||
%d hour_one=%d 時間
|
||||
%d hour_few=%d 時間
|
||||
%d hour_many=%d 時間
|
||||
# if your language has simple plural forms, you can just use:
|
||||
%d second=%d 秒
|
||||
%d minute=%d 分
|
||||
%d hour=%d 時間
|
||||
%d seconds=%d 秒
|
||||
%d minutes=%d 分
|
||||
%d hours=%d 時間
|
||||
|
||||
|
||||
# Global Settings Page UI elements
|
||||
Accent color=アクセントカラー
|
||||
Red=赤
|
||||
Green=緑
|
||||
Blue=青
|
||||
|
||||
Start program on system startup?=システム起動時にプログラムを開始しますか
|
||||
Yes=はい
|
||||
No=いいえ
|
||||
|
||||
Enable keyboard hotkeys?=キーボードホットキーを有効にしますか
|
||||
Yes, but only grab virtual devices (supports some input remapping software)=はい ただし仮想デバイスのみ grab します(一部の入力リマップソフトに対応)
|
||||
Yes, but don't grab devices (supports all input remapping software)=はい ただしデバイスを grab しません(すべての入力リマップソフトに対応)
|
||||
|
||||
Show/hide UI:=UI の表示/非表示:
|
||||
Turn replay on/off:=リプレイのオン/オフ:
|
||||
Save replay:=リプレイを保存:
|
||||
Save 1 minute replay:=1分のリプレイを保存:
|
||||
Save 10 minute replay:=10分のリプレイを保存:
|
||||
Start/stop recording:=録画の開始/停止:
|
||||
Pause/unpause recording:=録画の一時停止/再開:
|
||||
Start/stop recording a region:=範囲録画の開始/停止:
|
||||
Start/stop streaming:=配信の開始/停止:
|
||||
Take a screenshot:=スクリーンショットを撮る:
|
||||
Take a screenshot of a region:=範囲のスクリーンショットを撮る:
|
||||
Start/stop recording with desktop portal:=デスクトップポータル録画の開始/停止:
|
||||
Take a screenshot with desktop portal:=デスクトップポータルでスクリーンショットを撮る:
|
||||
Start/stop recording a window:=ウィンドウ録画の開始/停止:
|
||||
Take a screenshot of a window:=ウィンドウのスクリーンショットを撮る:
|
||||
Press ESC to go back to the previous page/close the UI.=前のページに戻るか、UIを閉じるにはESCキーを押してください
|
||||
|
||||
Clear hotkeys=ホットキーを消去
|
||||
Reset hotkeys to default=ホットキーを既定値に戻す
|
||||
|
||||
Enable controller hotkeys?=コントローラホットキーを有効にしますか
|
||||
Press=押す
|
||||
and=と
|
||||
|
||||
Notification speed=通知の速度
|
||||
Normal=標準
|
||||
Fast=高速
|
||||
|
||||
Language=言語
|
||||
System language=システムの言語
|
||||
|
||||
Exit program=プログラムを終了
|
||||
Go back to the old UI=旧 UI に戻る
|
||||
|
||||
If you would like to donate you can do so by donating at https://buymeacoffee.com/dec05eba:=支援したい場合は https://buymeacoffee.com/dec05eba から寄付できます:
|
||||
Donate=寄付する
|
||||
All donations go toward developing software (including GPU Screen Recorder)\nand buying hardware to test the software.=寄付は GPU Screen Recorder を含むソフトウェア開発と テスト用ハードウェア購入に使われます
|
||||
|
||||
# Subsection headers
|
||||
Global=全体
|
||||
Back=戻る
|
||||
|
||||
Appearance=外観
|
||||
Startup=起動
|
||||
Keyboard hotkeys=キーボードホットキー
|
||||
Controller hotkeys=コントローラホットキー
|
||||
Application options=アプリ設定
|
||||
Application info=アプリ情報
|
||||
Donate=寄付
|
||||
|
||||
# Version info strings
|
||||
GSR version: %s=GSR バージョン: %s
|
||||
GSR-UI version: %s=GSR-UI バージョン: %s
|
||||
Flatpak version: %s=Flatpak バージョン: %s
|
||||
GPU vendor: %s=GPU ベンダ: %s
|
||||
|
||||
# Hotkey configuration dialog
|
||||
Press a key combination to use for the hotkey: "%s"=ホットキー "%s" に使うキーの組み合わせを押してください
|
||||
Alpha-numerical keys can't be used alone in hotkeys, they have to be used one or more of these keys: Alt, Ctrl, Shift and Super.\nPress Esc to cancel or Backspace to remove the hotkey.=英数字キーは単独でホットキーに使えません Alt Ctrl Shift Super のいずれかと組み合わせてください\nEsc でキャンセル Backspace でホットキーを削除します
|
||||
|
||||
# Hotkey action names (without colons - these appear in the dialog)
|
||||
Show/hide UI=UI の表示/非表示
|
||||
Turn replay on/off=リプレイのオン/オフ
|
||||
Save replay=リプレイを保存
|
||||
Save 1 minute replay=1分のリプレイを保存
|
||||
Save 10 minute replay=10分のリプレイを保存
|
||||
Start/stop recording=録画の開始/停止
|
||||
Pause/unpause recording=録画の一時停止/再開
|
||||
Start/stop recording a region=範囲録画の開始/停止
|
||||
Start/stop recording a window=ウィンドウ録画の開始/停止
|
||||
Start/stop recording with desktop portal=デスクトップポータル録画の開始/停止
|
||||
Start/stop streaming=配信の開始/停止
|
||||
Take a screenshot=スクリーンショットを撮る
|
||||
Take a screenshot of a region=範囲のスクリーンショットを撮る
|
||||
Take a screenshot of a window=ウィンドウのスクリーンショットを撮る
|
||||
Take a screenshot with desktop portal=デスクトップポータルでスクリーンショットを撮る
|
||||
|
||||
# Controller hotkey descriptions
|
||||
to show/hide the UI=UI を表示/非表示する
|
||||
to take a screenshot=スクリーンショットを撮る
|
||||
to save a replay=リプレイを保存する
|
||||
to start/stop recording=録画を開始/停止する
|
||||
to turn replay on/off=リプレイをオン/オフする
|
||||
to save a 1 minute replay=1分のリプレイを保存する
|
||||
to save a 10 minute replay=10分のリプレイを保存する
|
||||
|
||||
# Error message for duplicate hotkey
|
||||
The hotkey %s is already used for something else=ホットキー %s は別の操作ですでに使われています
|
||||
|
||||
|
||||
# Screenshot settings page
|
||||
Screenshot=スクリーンショット
|
||||
Capture=キャプチャ
|
||||
Image=画像
|
||||
File info=ファイル情報
|
||||
General=一般
|
||||
Screenshot indicator=スクリーンショットインジケータ
|
||||
Script=スクリプト
|
||||
File=ファイル
|
||||
|
||||
Back=戻る
|
||||
Save=保存
|
||||
Cancel=キャンセル
|
||||
|
||||
Capture source:=キャプチャ元:
|
||||
Window=ウィンドウ
|
||||
Region=範囲
|
||||
Desktop portal=デスクトップポータル
|
||||
Monitor %s (%dx%d)=モニタ %s (%dx%d)
|
||||
Screen=画面
|
||||
|
||||
Image resolution limit:=画像解像度の上限:
|
||||
Change image resolution=画像解像度を変更
|
||||
Restore portal session=ポータルセッションを復元
|
||||
|
||||
Image quality:=画質:
|
||||
Medium=中
|
||||
High=高
|
||||
Very high (Recommended)=非常に高い(推奨)
|
||||
Ultra=最高
|
||||
|
||||
Record cursor=カーソルを記録
|
||||
|
||||
Directory to save screenshots:=スクリーンショットの保存先:
|
||||
Image format:=画像形式:
|
||||
|
||||
Save screenshot in a folder based on the games name=ゲーム名ベースのフォルダにスクリーンショットを保存
|
||||
|
||||
Save screenshot to clipboard=スクリーンショットをクリップボードに保存
|
||||
Save screenshot to clipboard (Not supported properly by Wayland)=スクリーンショットをクリップボードに保存(Wayland では完全には対応していません)
|
||||
Save screenshot to disk=スクリーンショットをディスクに保存
|
||||
|
||||
Show screenshot notifications=スクリーンショット通知を表示
|
||||
Blink scroll lock led when taking a screenshot=スクリーンショット時に Scroll Lock LED を点滅
|
||||
|
||||
Command to open the screenshot with:=スクリーンショットを開くコマンド:
|
||||
|
||||
|
||||
# Settings Page UI elements - дополнения
|
||||
|
||||
# View modes
|
||||
Simple view=シンプル表示
|
||||
Advanced view=詳細表示
|
||||
|
||||
# Capture settings
|
||||
Follow focused window=フォーカス中のウィンドウを追従
|
||||
Focused monitor=フォーカス中のモニタ
|
||||
|
||||
Area size:=領域サイズ:
|
||||
Video resolution limit:=動画解像度の上限:
|
||||
Change video resolution=動画解像度を変更
|
||||
Restore portal session=ポータルセッションを復元
|
||||
|
||||
# Webcam settings
|
||||
Webcam=Webカメラ
|
||||
Webcam source:=Webカメラ入力:
|
||||
None=なし
|
||||
Video format:=動画形式:
|
||||
Auto (recommended)=自動(推奨)
|
||||
YUYV=YUYV
|
||||
Motion-JPEG=Motion-JPEG
|
||||
Video setup:=映像設定:
|
||||
* Right click in the bottom right corner to resize the webcam=* 右下を右クリックして Webカメラサイズを変更
|
||||
Flip camera horizontally=カメラを左右反転
|
||||
|
||||
# Audio settings
|
||||
Audio=音声
|
||||
Audio codec:=音声コーデック:
|
||||
Opus (Recommended)=Opus(推奨)
|
||||
AAC=AAC
|
||||
|
||||
Directory to save videos:=動画の保存先:
|
||||
Output device:=出力デバイス:
|
||||
Input device: =入力デバイス:
|
||||
# yes, these spaces are intentional
|
||||
Application: =アプリケーション:
|
||||
Custom...=カスタム...
|
||||
|
||||
Save video in a folder based on the games name%s=ゲーム名ベースのフォルダに動画を保存%s
|
||||
(X11 applications only)= (X11 アプリのみ)
|
||||
|
||||
Add audio track=音声トラックを追加
|
||||
Add input device=入力デバイスを追加
|
||||
Add output device=出力デバイスを追加
|
||||
Add application audio=アプリ音声を追加
|
||||
Record audio from all applications except the selected ones=選択したアプリ以外のすべてのアプリ音声を録音
|
||||
Recording output devices and application audio may record all output audio, which is likely\nnot what you want to do. Remove the output devices.=出力デバイスとアプリ音声を同時に録音すると すべての出力音声が録音される可能性があります\n通常は意図した動作ではないため 出力デバイスを削除してください
|
||||
|
||||
Video=映像
|
||||
|
||||
# Video codec settings
|
||||
Video codec:=ビデオコーデック:
|
||||
H264=H264
|
||||
HEVC=HEVC
|
||||
HEVC (10 bit, reduces banding)=HEVC (10bit バンディング軽減)
|
||||
HEVC (HDR)=HEVC (HDR)
|
||||
AV1=AV1
|
||||
AV1 (10 bit, reduces banding)=AV1 (10bit バンディング軽減)
|
||||
AV1 (HDR)=AV1 (HDR)
|
||||
VP8=VP8
|
||||
VP9=VP9
|
||||
H264 Software Encoder (Slow, not recommended)=H264 ソフトウェアエンコーダ(低速 非推奨)
|
||||
|
||||
# Video quality and bitrate
|
||||
Video quality:=画質:
|
||||
Very high=非常に高い
|
||||
Video bitrate (Kbps):=ビットレート(Kbps):
|
||||
Constant bitrate=固定ビットレート
|
||||
Constant bitrate (Recommended)=固定ビットレート(推奨)
|
||||
|
||||
# Frame rate settings
|
||||
Frame rate:=フレームレート:
|
||||
Frame rate mode:=フレームレートモード:
|
||||
Auto (Recommended)=自動(推奨)
|
||||
Constant=固定
|
||||
Variable=可変
|
||||
Sync to content=コンテンツに同期
|
||||
Sync to content (Only X11 or desktop portal capture)=コンテンツに同期(X11 またはデスクトップポータルキャプチャのみ)
|
||||
|
||||
# Color range
|
||||
Color range:=色域:
|
||||
Limited=限定
|
||||
Full=フル
|
||||
|
||||
# Container format
|
||||
Container:=コンテナ:
|
||||
|
||||
# Recording settings
|
||||
Record in low-power mode=省電力モードで録画
|
||||
Record cursor=カーソルを記録
|
||||
|
||||
Do not force the GPU to go into high performance mode when recording.\nMay affect recording performance, especially when playing a video at the same time.\nIf enabled then it's recommended to use sync to content frame rate mode to reduce power usage when idle.=録画時に GPU を高パフォーマンスモードへ強制しません\n特に動画再生と同時に使うと録画性能に影響する場合があります\n有効時は アイドル時の消費電力削減のため コンテンツ同期フレームレートモードの使用を推奨します
|
||||
Use vulkan video encoding instead of VAAPI/NVENC.\nEnabling this may result in better game performance while recording, especially on NVIDIA.\nNote that this option is experimental. There may be GPU driver issues that causes issues when this is enabled.=VAAPI/NVENCの代わりにVulkanビデオエンコーディングを使用します。\nこれを有効にすると、特にNVIDIA環境で、録画中のゲームパフォーマンスが向上する可能性があります。\nただし、このオプションは実験的なものであり、有効にするとGPUドライバの問題が発生する場合がありますのでご注意ください。
|
||||
Enable vulkan video encoding (experimental)=Vulkanビデオエンコードを有効にする(実験的機能)
|
||||
|
||||
Show replay notifications=リプレイ通知を表示
|
||||
Show recording notifications=録画通知を表示
|
||||
Show streaming notifications=配信通知を表示
|
||||
Show replay status with scroll lock LED=スクロールロックLEDでリプレイ状態を表示
|
||||
Show recording status with scroll lock LED=スクロールロックLEDで録画状態を表示
|
||||
Show streaming status with scroll lock LED=スクロールロックLEDで配信状態を表示
|
||||
Recording indicator=録画インジケータ
|
||||
|
||||
Simple=シンプル
|
||||
Audio track #%d=音声トラック #%d
|
||||
Output device=出力デバイス
|
||||
Input device: =入力デバイス:
|
||||
Estimated video file size per minute (excluding audio): %.2fMB=推定動画サイズ(音声除く 1分あたり): %.2fMB
|
||||
|
||||
# Replay settings
|
||||
Directory to save replays:=リプレイの保存先:
|
||||
Replay indicator=リプレイインジケータ
|
||||
Turn on replay when starting a game=ゲーム開始時にリプレイをオンにする
|
||||
Autostart=自動開始
|
||||
in RAM=RAM 内
|
||||
on disk=ディスク上
|
||||
Replay duration in seconds:=リプレイ秒数:
|
||||
Where should temporary replay data be stored?=一時的なリプレイデータの保存先:
|
||||
RAM=RAM
|
||||
Disk (Not recommended on SSDs)=ディスク(SSD では非推奨)
|
||||
Turn on replay when this program starts=このプログラム起動時にリプレイをオン
|
||||
Only turn on replay if a power supply is connected=電源が接続されている場合にのみリプレイをオンにしてください
|
||||
Don't turn on replay automatically=自動でリプレイをオンにしない
|
||||
Restart replay on save=保存時にリプレイを再起動
|
||||
Estimated video max file size %s: %.2fMB.\nChange video bitrate or replay duration to change file size.=推定動画最大サイズ %s: %.2fMB\nファイルサイズを変えるにはビットレートまたはリプレイ秒数を変更してください
|
||||
|
||||
# Streaming settings
|
||||
Stream service:=配信サービス:
|
||||
Twitch=Twitch
|
||||
YouTube=YouTube
|
||||
Kick=Kick
|
||||
Rumble=Rumble
|
||||
Custom=カスタム
|
||||
Stream URL:=配信 URL:
|
||||
Stream key:=ストリームキー:
|
||||
Streaming info=配信情報
|
||||
Streaming indicator=配信インジケータ
|
||||
@@ -31,7 +31,7 @@ Turn off=Выключить
|
||||
Recording has been paused=Запись приостановлена
|
||||
Recording has been unpaused=Запись возобновлена
|
||||
Started recording %s=Начата запись %s
|
||||
Saved a %s recording of %s\nto "%s"=Сохранено %s записи %s\nв "%s"
|
||||
Saved a %s recording of %s to "%s"=Сохранено %s записи %s в "%s"
|
||||
Saved a %s recording of %s=Сохранено %s записи %s
|
||||
Failed to start/save recording=Не удалось начать/сохранить запись
|
||||
|
||||
@@ -39,36 +39,36 @@ Failed to start/save recording=Не удалось начать/сохранит
|
||||
Replay stopped=Повтор остановлен
|
||||
Started replaying %s=Начат повтор %s
|
||||
Saving replay, this might take some time=Сохранение повтора, это может занять некоторое время
|
||||
Saved a %s replay of %s\nto "%s"=Сохранено %s повтора %s\nв "%s"
|
||||
Saved a %s replay of %s to "%s"=Сохранено %s повтора %s в "%s"
|
||||
Saved a %s replay of %s=Сохранено %s повтора %s
|
||||
Replay stopped because of an error=Повтор остановлен из-за ошибки
|
||||
Replay settings have been modified.\nYou may need to restart replay to apply the changes.=Настройки повтора изменены.\nВозможно, потребуется перезапустить повтор для применения изменений.
|
||||
Replay settings have been modified. You may need to restart replay to apply the changes.=Настройки повтора изменены. Возможно, потребуется перезапустить повтор для применения изменений.
|
||||
|
||||
# Notifications - Streaming
|
||||
Streaming has stopped=Трансляция остановлена
|
||||
Started streaming %s=Начата трансляция %s
|
||||
Streaming stopped because of an error=Трансляция остановлена из-за ошибки
|
||||
Streaming settings have been modified.\nYou may need to restart streaming to apply the changes.=Настройки трансляции изменены.\nВозможно, потребуется перезапустить трансляцию для применения изменений.
|
||||
Streaming settings have been modified. You may need to restart streaming to apply the changes.=Настройки трансляции изменены. Возможно, потребуется перезапустить трансляцию для применения изменений.
|
||||
|
||||
# Notifications - Screenshot
|
||||
Saved a screenshot of %s\nto "%s"=Сохранён снимок %s\nв "%s"
|
||||
Saved a screenshot of %s to "%s"=Сохранён снимок %s в "%s"
|
||||
Saved a screenshot of %s=Сохранён снимок %s
|
||||
Failed to take a screenshot=Не удалось сделать снимок экрана
|
||||
|
||||
# Error messages
|
||||
Another instance of GPU Screen Recorder UI is already running.\nPress Alt+Z to open the UI.=Один экземпляр GPU Screen Recorder UI уже запущен.\nНажмите Alt+Z, чтобы открыть интерфейс.
|
||||
GPU Screen Recorder is already running in another process.\nPlease close it before using GPU Screen Recorder UI.=GPU Screen Recorder CLI уже запущен в другом процессе.\nПожалуйста, закройте его перед использованием GPU Screen Recorder UI.
|
||||
Failed to start replay, capture target "%s" is invalid.\nPlease change capture target in settings=Не удалось начать повтор, цель захвата "%s" недействительна.\nПожалуйста, измените цель захвата в настройках
|
||||
Failed to start recording, capture target "%s" is invalid.\nPlease change capture target in settings=Не удалось начать запись, цель захвата "%s" недействительна.\nПожалуйста, измените цель захвата в настройках
|
||||
Failed to start streaming, capture target "%s" is invalid.\nPlease change capture target in settings=Не удалось начать трансляцию, цель захвата "%s" недействительна.\nПожалуйста, измените цель захвата в настройках
|
||||
Failed to take a screenshot, capture target "%s" is invalid.\nPlease change capture target in settings=Не удалось сделать снимок экрана, цель захвата "%s" недействительна.\nПожалуйста, измените цель захвата в настройках
|
||||
Another instance of GPU Screen Recorder UI is already running. Press Alt+Z to open the UI.=Один экземпляр GPU Screen Recorder UI уже запущен. Нажмите Alt+Z, чтобы открыть интерфейс.
|
||||
GPU Screen Recorder is already running in another process. Please close it before using GPU Screen Recorder UI.=GPU Screen Recorder CLI уже запущен в другом процессе. Пожалуйста, закройте его перед использованием GPU Screen Recorder UI.
|
||||
Failed to start replay, capture target "%s" is invalid. Please change capture target in settings=Не удалось начать повтор, цель захвата "%s" недействительна. Пожалуйста, измените цель захвата в настройках
|
||||
Failed to start recording, capture target "%s" is invalid. Please change capture target in settings=Не удалось начать запись, цель захвата "%s" недействительна. Пожалуйста, измените цель захвата в настройках
|
||||
Failed to start streaming, capture target "%s" is invalid. Please change capture target in settings=Не удалось начать трансляцию, цель захвата "%s" недействительна. Пожалуйста, измените цель захвата в настройках
|
||||
Failed to take a screenshot, capture target "%s" is invalid. Please change capture target in settings=Не удалось сделать снимок экрана, цель захвата "%s" недействительна. Пожалуйста, измените цель захвата в настройках
|
||||
|
||||
Unable to start recording when replay is turned on.\nTurn off replay before starting recording.=Невозможно начать запись при включённом повторе.\nВыключите повтор перед началом записи.
|
||||
Unable to start streaming when replay is turned on.\nTurn off replay before starting streaming.=Невозможно начать трансляцию при включённом повторе.\nВыключите повтор перед началом трансляции.
|
||||
Unable to start streaming when recording.\nStop recording before starting streaming.=Невозможно начать трансляцию при записи.\nОстановите запись перед началом трансляции.
|
||||
Unable to start recording when streaming.\nStop streaming before starting recording.=Невозможно начать запись при трансляции.\nОстановите трансляцию перед началом записи.
|
||||
Unable to start replay when recording.\nStop recording before starting replay.=Невозможно начать повтор при записи.\nОстановите запись перед началом повтора.
|
||||
Unable to start replay when streaming.\nStop streaming before starting replay.=Невозможно начать повтор при трансляции.\nОстановите трансляцию перед началом повтора.
|
||||
Unable to start recording when replay is turned on. Turn off replay before starting recording.=Невозможно начать запись при включённом повторе. Выключите повтор перед началом записи.
|
||||
Unable to start streaming when replay is turned on. Turn off replay before starting streaming.=Невозможно начать трансляцию при включённом повторе. Выключите повтор перед началом трансляции.
|
||||
Unable to start streaming when recording. Stop recording before starting streaming.=Невозможно начать трансляцию при записи. Остановите запись перед началом трансляции.
|
||||
Unable to start recording when streaming. Stop streaming before starting recording.=Невозможно начать запись при трансляции. Остановите трансляцию перед началом записи.
|
||||
Unable to start replay when recording. Stop recording before starting replay.=Невозможно начать повтор при записи. Остановите запись перед началом повтора.
|
||||
Unable to start replay when streaming. Stop streaming before starting replay.=Невозможно начать повтор при трансляции. Остановите трансляцию перед началом повтора.
|
||||
|
||||
Started recording in the replay session=Начата запись в сеансе повтора
|
||||
Started recording in the streaming session=Начата запись в сеансе трансляции
|
||||
@@ -81,11 +81,11 @@ Streaming stopped because of an error. Verify if settings are correct=Транс
|
||||
%s. Verify if settings are correct=%s. Проверьте правильность настроек
|
||||
|
||||
# GPU Screen Recorder errors
|
||||
Desktop portal capture failed.\nEither you canceled the desktop portal or your Wayland compositor doesn't support desktop portal capture\nor it's incorrectly setup on your system.=Захват портала рабочего стола не удался.\nВы либо отменили портал рабочего стола, либо ваш композитор Wayland не поддерживает захват портала рабочего стола\nили он неправильно настроен в вашей системе.
|
||||
Monitor capture failed.\nThe monitor you are trying to capture is invalid.\nPlease validate your capture settings.=Захват монитора не удался.\nМонитор, который вы пытаетесь захватить, недействителен.\nПожалуйста, проверьте настройки захвата.
|
||||
Capture failed. Neither H264, HEVC nor AV1 video codecs are supported\non your system or you are trying to capture at a resolution higher than your\nsystem supports for each video codec.=Захват не удался. Ни один из видеокодеков H264, HEVC или AV1 не поддерживается\nвашей системой или вы пытаетесь захватить видео с разрешением выше, чем\nподдерживает ваша система для каждого видеокодека.
|
||||
Capture failed. Your system doesn't support the resolution you are trying to\nrecord at with the video codec you have chosen.\nChange capture resolution or video codec and try again.\nNote: AV1 supports the highest resolution, then HEVC and then H264.=Захват не удался. Ваша система не поддерживает разрешение, с которым вы пытаетесь\nзаписывать с выбранным видеокодеком.\nИзмените разрешение захвата или видеокодек и попробуйте снова.\nПримечание: AV1 поддерживает максимальное разрешение, затем HEVC, затем H264.
|
||||
Capture failed. Your system doesn't support the video codec you have chosen.\nChange video codec and try again.=Захват не удался. Ваша система не поддерживает выбранный видеокодек.\nИзмените видеокодек и попробуйте снова.
|
||||
Desktop portal capture failed. Either you canceled the desktop portal or your Wayland compositor doesn't support desktop portal capture or it's incorrectly setup on your system.=Захват портала рабочего стола не удался. Вы либо отменили портал рабочего стола, либо ваш композитор Wayland не поддерживает захват портала рабочего стола или он неправильно настроен в вашей системе.
|
||||
Monitor capture failed. The monitor you are trying to capture is invalid. Please validate your capture settings.=Захват монитора не удался. Монитор, который вы пытаетесь захватить, недействителен. Пожалуйста, проверьте настройки захвата.
|
||||
Capture failed. Neither H264, HEVC nor AV1 video codecs are supported on your system or you are trying to capture at a resolution higher than your system supports for each video codec.=Захват не удался. Ни один из видеокодеков H264, HEVC или AV1 не поддерживается вашей системой или вы пытаетесь захватить видео с разрешением выше, чем поддерживает ваша система для каждого видеокодека.
|
||||
Capture failed. Your system doesn't support the resolution you are trying to record at with the video codec you have chosen. Change capture resolution or video codec and try again. Note: AV1 supports the highest resolution, then HEVC and then H264.=Захват не удался. Ваша система не поддерживает разрешение, с которым вы пытаетесь записывать с выбранным видеокодеком. Измените разрешение захвата или видеокодек и попробуйте снова. Примечание: AV1 поддерживает максимальное разрешение, затем HEVC, затем H264.
|
||||
Capture failed. Your system doesn't support the video codec you have chosen. Change video codec and try again.=Захват не удался. Ваша система не поддерживает выбранный видеокодек. Измените видеокодек и попробуйте снова.
|
||||
Stopped capture because the user canceled the desktop portal=Захват остановлен, так как пользователь отменил портал рабочего стола
|
||||
Failed to take a screenshot. Verify if settings are correct=Не удалось сделать снимок экрана. Проверьте правильность настроек
|
||||
|
||||
@@ -98,10 +98,12 @@ Failed to launch gpu-screen-recorder to take a screenshot=Не удалось з
|
||||
# System startup notifications
|
||||
Failed to add GPU Screen Recorder to system startup=Не удалось добавить GPU Screen Recorder в автозагрузку системы
|
||||
Failed to remove GPU Screen Recorder from system startup=Не удалось удалить GPU Screen Recorder из автозагрузки системы
|
||||
Failed to add GPU Screen Recorder to system startup.\nThis option only works on systems that use systemd.\nYou have to manually add "gsr-ui" to system startup on systems that uses another init system.=Не удалось добавить GPU Screen Recorder в автозагрузку системы.\nЭта опция работает только на системах, использующих systemd.\nВы должны вручную добавить "gsr-ui" в автозагрузку на системах с другой init-системой.
|
||||
To enable autorun: install and configure 'dex' (recommended), or manually add '%s' to your desktop autostart entries.=Для включения автозапуска: установите и настройте 'dex' (рекомендуется) или вручную добавьте '%s' в записи автозапуска рабочего стола.
|
||||
GPU Screen Recorder UI startup has been switched from systemd service to XDG autostart.=Автозапуск GPU Screen Recorder UI переключён с сервиса systemd на XDG-автозапуск.
|
||||
GPU Screen Recorder UI autostart via systemd is deprecated. To migrate: install and configure 'dex' (recommended), or manually add '%s' to your desktop autostart entries.=Автозапуск GPU Screen Recorder UI через systemd устарел. Для миграции: установите и настройте 'dex' (рекомендуется) или вручную добавьте '%s' в записи автозапуска рабочего стола.
|
||||
|
||||
# Wayland warning
|
||||
Wayland doesn't support GPU Screen Recorder UI properly,\nthings may not work as expected. Use X11 if you experience issues.=Wayland не поддерживает интерфейс GPU Screen Recorder должным образом,\nнекоторые функции могут не работать. Используйте X11, если возникнут проблемы.
|
||||
Wayland doesn't support GPU Screen Recorder UI properly, things may not work as expected. Use X11 if you experience issues.=Wayland не поддерживает интерфейс GPU Screen Recorder должным образом, некоторые функции могут не работать. Используйте X11, если возникнут проблемы.
|
||||
|
||||
# Hotkey conflicts
|
||||
Some keyboard remapping software conflicts with GPU Screen Recorder on your system.\nKeyboards have been ungrabbed, applications will now receive the hotkeys you press.=Некоторое программное обеспечение переназначения клавиатуры конфликтует с GPU Screen Recorder в вашей системе.\nЗахват клавиатуры отключён, приложения теперь будут получать нажатия горячих клавиш.
|
||||
@@ -156,6 +158,7 @@ Start/stop recording with desktop portal:=Запись через портал:
|
||||
Take a screenshot with desktop portal:=Сделать снимок через портал рабочего стола:
|
||||
Start/stop recording a window:=Начать/остановить запись окна:
|
||||
Take a screenshot of a window:=Сделать снимок окна:
|
||||
Press ESC to go back to the previous page/close the UI.=Нажмите ESC, чтобы вернуться на предыдущую страницу/закрыть интерфейс.
|
||||
|
||||
Clear hotkeys=Очистить горячие клавиши
|
||||
Reset hotkeys to default=Сбросить горячие клавиши по умолчанию
|
||||
@@ -371,9 +374,15 @@ Record in low-power mode=Записывать в режиме низкого э
|
||||
Record cursor=Записывать курсор
|
||||
|
||||
Do not force the GPU to go into high performance mode when recording.\nMay affect recording performance, especially when playing a video at the same time.\nIf enabled then it's recommended to use sync to content frame rate mode to reduce power usage when idle.=Не заставлять GPU переходить в режим высокой производительности при записи.\nМожет повлиять на производительность записи, особенно при одновременном воспроизведении видео.\nЕсли включено, рекомендуется использовать режим частоты кадров синхронизации с контентом\nдля снижения энергопотребления в режиме ожидания.
|
||||
Use vulkan video encoding instead of VAAPI/NVENC.\nEnabling this may result in better game performance while recording, especially on NVIDIA.\nNote that this option is experimental. There may be GPU driver issues that causes issues when this is enabled.=Используйте кодирование видео Vulkan вместо VAAPI/NVENC.\nВключение этой опции может улучшить производительность игр во время записи, особенно на видеокартах NVIDIA.\nОбратите внимание, что эта опция является экспериментальной. При её включении могут возникать проблемы с драйверами\nвидеокарт.
|
||||
Enable vulkan video encoding (experimental)=Включить кодирование видео Vulkan (экспериментально)
|
||||
|
||||
Show %s notifications=Показывать уведомления %s
|
||||
Show %s status with scroll lock LED=Показывать статус %s с помощью индикатора Scroll Lock
|
||||
Show replay notifications=Показывать уведомления повтора
|
||||
Show recording notifications=Показывать уведомления записи
|
||||
Show streaming notifications=Показывать уведомления трансляции
|
||||
Show replay status with scroll lock LED=Показывать статус повтора с помощью индикатора Scroll Lock
|
||||
Show recording status with scroll lock LED=Показывать статус записи с помощью индикатора Scroll Lock
|
||||
Show streaming status with scroll lock LED=Показывать статус трансляции с помощью индикатора Scroll Lock
|
||||
Recording indicator=Индикатор записи
|
||||
|
||||
Simple=Простой
|
||||
@@ -385,15 +394,16 @@ Estimated video file size per minute (excluding audio): %.2fMB=Примерны
|
||||
# Replay settings
|
||||
Directory to save replays:=Каталог для сохранения повторов:
|
||||
Replay indicator=Индикатор повтора
|
||||
Turn on replay when starting a fullscreen application%s=Включать повтор при запуске полноэкранного приложения%s
|
||||
Turn on replay when starting a game=Включите воспроизведение при запуске игры
|
||||
Autostart=Автозапуск
|
||||
in RAM=в ОЗУ
|
||||
on disk=на диске
|
||||
Replay duration in seconds:=Длительность повтора в секундах:
|
||||
Where should temporary replay data be stored?=Где должны храниться временные данные повтора?
|
||||
RAM=ОЗУ
|
||||
Disk (Not recommended on SSDs)=Диск (не рекомендуется для SSD)
|
||||
Turn on replay when this program starts=Включать повтор при запуске программы
|
||||
Turn on replay when power supply is connected=Включать повтор при подключении источника питания
|
||||
Only turn on replay if a power supply is connected=Включайте воспроизведение только при подключенном источнике питания
|
||||
Don't turn on replay automatically=Не включать повтор автоматически
|
||||
Restart replay on save=Перезапускать повтор при сохранении
|
||||
Estimated video max file size %s: %.2fMB.\nChange video bitrate or replay duration to change file size.=Примерный максимальный размер видео файла %s: %.2fMB.\nИзмените битрейт видео или длительность повтора, чтобы изменить размер файла.
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
# Important warning: we f'ed up a little bit and used %s for both strings and numbers in some places, such as time durations (they're fixed by the moment).
|
||||
# When translating, be careful to use the %d format specifier for numbers in those places.
|
||||
# Note that all translation strings should be on one line in these translation files. Some translations need to be on multiple lines and the newline character
|
||||
# should be replaced with \n.
|
||||
|
||||
# General UI
|
||||
Record=
|
||||
@@ -34,7 +36,7 @@ Turn off=
|
||||
Recording has been paused=
|
||||
Recording has been unpaused=
|
||||
Started recording %s=
|
||||
Saved a %s recording of %s\nto "%s"=
|
||||
Saved a %s recording of %s to "%s"=
|
||||
Saved a %s recording of %s=
|
||||
Failed to start/save recording=
|
||||
|
||||
@@ -42,36 +44,36 @@ Failed to start/save recording=
|
||||
Replay stopped=
|
||||
Started replaying %s=
|
||||
Saving replay, this might take some time=
|
||||
Saved a %s replay of %s\nto "%s"=
|
||||
Saved a %s replay of %s to "%s"=
|
||||
Saved a %s replay of %s=
|
||||
Replay stopped because of an error=
|
||||
Replay settings have been modified.\nYou may need to restart replay to apply the changes.=
|
||||
Replay settings have been modified. You may need to restart replay to apply the changes.=
|
||||
|
||||
# Notifications - Streaming
|
||||
Streaming has stopped=
|
||||
Started streaming %s=
|
||||
Streaming stopped because of an error=
|
||||
Streaming settings have been modified.\nYou may need to restart streaming to apply the changes.=
|
||||
Streaming settings have been modified. You may need to restart streaming to apply the changes.=
|
||||
|
||||
# Notifications - Screenshot
|
||||
Saved a screenshot of %s\nto "%s"=
|
||||
Saved a screenshot of %s to "%s"=
|
||||
Saved a screenshot of %s=
|
||||
Failed to take a screenshot=
|
||||
|
||||
# Error messages
|
||||
Another instance of GPU Screen Recorder UI is already running.\nPress Alt+Z to open the UI.=
|
||||
GPU Screen Recorder is already running in another process.\nPlease close it before using GPU Screen Recorder UI.=
|
||||
Failed to start replay, capture target "%s" is invalid.\nPlease change capture target in settings=
|
||||
Failed to start recording, capture target "%s" is invalid.\nPlease change capture target in settings=
|
||||
Failed to start streaming, capture target "%s" is invalid.\nPlease change capture target in settings=
|
||||
Failed to take a screenshot, capture target "%s" is invalid.\nPlease change capture target in settings=
|
||||
Another instance of GPU Screen Recorder UI is already running. Press Alt+Z to open the UI.=
|
||||
GPU Screen Recorder is already running in another process. Please close it before using GPU Screen Recorder UI.=
|
||||
Failed to start replay, capture target "%s" is invalid. Please change capture target in settings=
|
||||
Failed to start recording, capture target "%s" is invalid. Please change capture target in settings=
|
||||
Failed to start streaming, capture target "%s" is invalid. Please change capture target in settings=
|
||||
Failed to take a screenshot, capture target "%s" is invalid. Please change capture target in settings=
|
||||
|
||||
Unable to start recording when replay is turned on.\nTurn off replay before starting recording.=
|
||||
Unable to start streaming when replay is turned on.\nTurn off replay before starting streaming.=
|
||||
Unable to start streaming when recording.\nStop recording before starting streaming.=
|
||||
Unable to start recording when streaming.\nStop streaming before starting recording.=
|
||||
Unable to start replay when recording.\nStop recording before starting replay.=
|
||||
Unable to start replay when streaming.\nStop streaming before starting replay.=
|
||||
Unable to start recording when replay is turned on. Turn off replay before starting recording.=
|
||||
Unable to start streaming when replay is turned on. Turn off replay before starting streaming.=
|
||||
Unable to start streaming when recording. Stop recording before starting streaming.=
|
||||
Unable to start recording when streaming. Stop streaming before starting recording.=
|
||||
Unable to start replay when recording. Stop recording before starting replay.=
|
||||
Unable to start replay when streaming. Stop streaming before starting replay.=
|
||||
|
||||
Started recording in the replay session=
|
||||
Started recording in the streaming session=
|
||||
@@ -84,11 +86,11 @@ Streaming stopped because of an error. Verify if settings are correct=
|
||||
%s. Verify if settings are correct=
|
||||
|
||||
# GPU Screen Recorder errors
|
||||
Desktop portal capture failed.\nEither you canceled the desktop portal or your Wayland compositor doesn't support desktop portal capture\nor it's incorrectly setup on your system.=
|
||||
Monitor capture failed.\nThe monitor you are trying to capture is invalid.\nPlease validate your capture settings.=
|
||||
Capture failed. Neither H264, HEVC nor AV1 video codecs are supported\non your system or you are trying to capture at a resolution higher than your\nsystem supports for each video codec.=
|
||||
Capture failed. Your system doesn't support the resolution you are trying to\nrecord at with the video codec you have chosen.\nChange capture resolution or video codec and try again.\nNote: AV1 supports the highest resolution, then HEVC and then H264.=
|
||||
Capture failed. Your system doesn't support the video codec you have chosen.\nChange video codec and try again.=
|
||||
Desktop portal capture failed. Either you canceled the desktop portal or your Wayland compositor doesn't support desktop portal capture or it's incorrectly setup on your system.=
|
||||
Monitor capture failed. The monitor you are trying to capture is invalid. Please validate your capture settings.=
|
||||
Capture failed. Neither H264, HEVC nor AV1 video codecs are supported on your system or you are trying to capture at a resolution higher than your system supports for each video codec.=
|
||||
Capture failed. Your system doesn't support the resolution you are trying to record at with the video codec you have chosen. Change capture resolution or video codec and try again. Note: AV1 supports the highest resolution, then HEVC and then H264.=
|
||||
Capture failed. Your system doesn't support the video codec you have chosen. Change video codec and try again.=
|
||||
Stopped capture because the user canceled the desktop portal=
|
||||
Failed to take a screenshot. Verify if settings are correct=
|
||||
|
||||
@@ -101,10 +103,12 @@ Failed to launch gpu-screen-recorder to take a screenshot=
|
||||
# System startup notifications
|
||||
Failed to add GPU Screen Recorder to system startup=
|
||||
Failed to remove GPU Screen Recorder from system startup=
|
||||
Failed to add GPU Screen Recorder to system startup.\nThis option only works on systems that use systemd.\nYou have to manually add "gsr-ui" to system startup on systems that uses another init system.=
|
||||
To enable autorun: install and configure 'dex' (recommended), or manually add '%s' to your desktop autostart entries.=
|
||||
GPU Screen Recorder UI startup has been switched from systemd service to XDG autostart.=
|
||||
GPU Screen Recorder UI autostart via systemd is deprecated. To migrate: install and configure 'dex' (recommended), or manually add '%s' to your desktop autostart entries.=
|
||||
|
||||
# Wayland warning
|
||||
Wayland doesn't support GPU Screen Recorder UI properly,\nthings may not work as expected. Use X11 if you experience issues.=
|
||||
Wayland doesn't support GPU Screen Recorder UI properly, things may not work as expected. Use X11 if you experience issues.=
|
||||
|
||||
# Hotkey conflicts
|
||||
Some keyboard remapping software conflicts with GPU Screen Recorder on your system.\nKeyboards have been ungrabbed, applications will now receive the hotkeys you press.=
|
||||
@@ -167,6 +171,7 @@ Start/stop recording with desktop portal:=
|
||||
Take a screenshot with desktop portal:=
|
||||
Start/stop recording a window:=
|
||||
Take a screenshot of a window:=
|
||||
Press ESC to go back to the previous page/close the UI.=
|
||||
|
||||
Clear hotkeys=
|
||||
Reset hotkeys to default=
|
||||
@@ -383,11 +388,16 @@ Record in low-power mode=
|
||||
Record cursor=
|
||||
|
||||
Do not force the GPU to go into high performance mode when recording.\nMay affect recording performance, especially when playing a video at the same time.\nIf enabled then it's recommended to use sync to content frame rate mode to reduce power usage when idle.=
|
||||
Use vulkan video encoding instead of VAAPI/NVENC.\nEnabling this may result in better game performance while recording, especially on NVIDIA.\nNote that this option is experimental. There may be GPU driver issues that causes issues when this is enabled.=
|
||||
Enable vulkan video encoding (experimental)=
|
||||
|
||||
Show %s notifications=
|
||||
Show %s status with scroll lock LED=
|
||||
Show replay notifications=
|
||||
Show recording notifications=
|
||||
Show streaming notifications=
|
||||
Show replay status with scroll lock LED=
|
||||
Show recording status with scroll lock LED=
|
||||
Show streaming status with scroll lock LED=
|
||||
Recording indicator=
|
||||
recording=
|
||||
|
||||
Simple=
|
||||
Audio track #%d=
|
||||
@@ -398,16 +408,16 @@ Estimated video file size per minute (excluding audio): %.2fMB=
|
||||
# Replay settings
|
||||
Directory to save replays:=
|
||||
Replay indicator=
|
||||
replay=
|
||||
Turn on replay when starting a fullscreen application%s=
|
||||
Turn on replay when starting a game=
|
||||
Autostart=
|
||||
in RAM=
|
||||
on disk=
|
||||
Replay duration in seconds:=
|
||||
Where should temporary replay data be stored?=
|
||||
RAM=
|
||||
Disk (Not recommended on SSDs)=
|
||||
Turn on replay when this program starts=
|
||||
Turn on replay when power supply is connected=
|
||||
Only turn on replay if a power supply is connected=
|
||||
Don't turn on replay automatically=
|
||||
Restart replay on save=
|
||||
Estimated video max file size %s: %.2fMB.\nChange video bitrate or replay duration to change file size.=
|
||||
@@ -423,4 +433,3 @@ Stream URL:=
|
||||
Stream key:=
|
||||
Streaming info=
|
||||
Streaming indicator=
|
||||
streaming=
|
||||
@@ -31,7 +31,7 @@ Turn off=Вимкнути
|
||||
Recording has been paused=Запис призупинено
|
||||
Recording has been unpaused=Запис відновлено
|
||||
Started recording %s=Розпочато запис %s
|
||||
Saved a %s recording of %s\nto "%s"=Збережено %s запису %s\nу "%s"
|
||||
Saved a %s recording of %s to "%s"=Збережено %s запису %s у "%s"
|
||||
Saved a %s recording of %s=Збережено %s запису %s
|
||||
Failed to start/save recording=Не вдалося розпочати/зберегти запис
|
||||
|
||||
@@ -39,7 +39,7 @@ Failed to start/save recording=Не вдалося розпочати/збере
|
||||
Replay stopped=Повтор зупинено
|
||||
Started replaying %s=Розпочато повтор %s
|
||||
Saving replay, this might take some time=Збереження повтору, це може зайняти деякий час
|
||||
Saved a %s replay of %s\nto "%s"=Збережено %s повтору %s\nу "%s"
|
||||
Saved a %s replay of %s to "%s"=Збережено %s повтору %s у "%s"
|
||||
Saved a %s replay of %s=Збережено %s повтору %s
|
||||
Replay stopped because of an error=Повтор зупинено через помилку
|
||||
Replay settings have been modified.\nYou may need to restart replay to apply the changes.=Налаштування повтору змінено.\nМожливо, знадобиться перезапустити повтор для застосування змін.
|
||||
@@ -51,24 +51,24 @@ Streaming stopped because of an error=Трансляцію зупинено че
|
||||
Streaming settings have been modified.\nYou may need to restart streaming to apply the changes.=Налаштування трансляції змінено.\nМожливо, знадобиться перезапустити трансляцію для застосування змін.
|
||||
|
||||
# Notifications - Screenshot
|
||||
Saved a screenshot of %s\nto "%s"=Збережено знімок %s\nу "%s"
|
||||
Saved a screenshot of %s to "%s"=Збережено знімок %s у "%s"
|
||||
Saved a screenshot of %s=Збережено знімок %s
|
||||
Failed to take a screenshot=Не вдалося зробити знімок екрана
|
||||
|
||||
# Error messages
|
||||
Another instance of GPU Screen Recorder UI is already running.\nPress Alt+Z to open the UI.=Один екземпляр GPU Screen Recorder UI вже запущено.\nНатисніть Alt+Z, щоб відкрити інтерфейс.
|
||||
GPU Screen Recorder is already running in another process.\nPlease close it before using GPU Screen Recorder UI.=GPU Screen Recorder CLI вже запущено в іншому процесі.\nБудь ласка, закрийте його перед використанням GPU Screen Recorder UI.
|
||||
Failed to start replay, capture target "%s" is invalid.\nPlease change capture target in settings=Не вдалося розпочати повтор, ціль захоплення "%s" недійсна.\nБудь ласка, змініть ціль захоплення в налаштуваннях
|
||||
Failed to start recording, capture target "%s" is invalid.\nPlease change capture target in settings=Не вдалося розпочати запис, ціль захоплення "%s" недійсна.\nБудь ласка, змініть ціль захоплення в налаштуваннях
|
||||
Failed to start streaming, capture target "%s" is invalid.\nPlease change capture target in settings=Не вдалося розпочати трансляцію, ціль захоплення "%s" недійсна.\nБудь ласка, змініть ціль захоплення в налаштуваннях
|
||||
Failed to take a screenshot, capture target "%s" is invalid.\nPlease change capture target in settings=Не вдалося зробити знімок екрана, ціль захоплення "%s" недійсна.\nБудь ласка, змініть ціль захоплення в налаштуваннях
|
||||
Another instance of GPU Screen Recorder UI is already running. Press Alt+Z to open the UI.=Один екземпляр GPU Screen Recorder UI вже запущено. Натисніть Alt+Z, щоб відкрити інтерфейс.
|
||||
GPU Screen Recorder is already running in another process. Please close it before using GPU Screen Recorder UI.=GPU Screen Recorder CLI вже запущено в іншому процесі. Будь ласка, закрийте його перед використанням GPU Screen Recorder UI.
|
||||
Failed to start replay, capture target "%s" is invalid. Please change capture target in settings=Не вдалося розпочати повтор, ціль захоплення "%s" недійсна. Будь ласка, змініть ціль захоплення в налаштуваннях
|
||||
Failed to start recording, capture target "%s" is invalid. Please change capture target in settings=Не вдалося розпочати запис, ціль захоплення "%s" недійсна. Будь ласка, змініть ціль захоплення в налаштуваннях
|
||||
Failed to start streaming, capture target "%s" is invalid. Please change capture target in settings=Не вдалося розпочати трансляцію, ціль захоплення "%s" недійсна. Будь ласка, змініть ціль захоплення в налаштуваннях
|
||||
Failed to take a screenshot, capture target "%s" is invalid. Please change capture target in settings=Не вдалося зробити знімок екрана, ціль захоплення "%s" недійсна. Будь ласка, змініть ціль захоплення в налаштуваннях
|
||||
|
||||
Unable to start recording when replay is turned on.\nTurn off replay before starting recording.=Неможливо розпочати запис при увімкненому повторі.\nВимкніть повтор перед початком запису.
|
||||
Unable to start streaming when replay is turned on.\nTurn off replay before starting streaming.=Неможливо розпочати трансляцію при увімкненому повторі.\nВимкніть повтор перед початком трансляції.
|
||||
Unable to start streaming when recording.\nStop recording before starting streaming.=Неможливо розпочати трансляцію при записі.\nЗупиніть запис перед початком трансляції.
|
||||
Unable to start recording when streaming.\nStop streaming before starting recording.=Неможливо розпочати запис при трансляції.\nЗупиніть трансляцію перед початком запису.
|
||||
Unable to start replay when recording.\nStop recording before starting replay.=Неможливо розпочати повтор при записі.\nЗупиніть запис перед початком повтору.
|
||||
Unable to start replay when streaming.\nStop streaming before starting replay.=Неможливо розпочати повтор при трансляції.\nЗупиніть трансляцію перед початком повтору.
|
||||
Unable to start recording when replay is turned on. Turn off replay before starting recording.=Неможливо розпочати запис при увімкненому повторі. Вимкніть повтор перед початком запису.
|
||||
Unable to start streaming when replay is turned on. Turn off replay before starting streaming.=Неможливо розпочати трансляцію при увімкненому повторі. Вимкніть повтор перед початком трансляції.
|
||||
Unable to start streaming when recording. Stop recording before starting streaming.=Неможливо розпочати трансляцію при записі. Зупиніть запис перед початком трансляції.
|
||||
Unable to start recording when streaming. Stop streaming before starting recording.=Неможливо розпочати запис при трансляції. Зупиніть трансляцію перед початком запису.
|
||||
Unable to start replay when recording. Stop recording before starting replay.=Неможливо розпочати повтор при записі. Зупиніть запис перед початком повтору.
|
||||
Unable to start replay when streaming. Stop streaming before starting replay.=Неможливо розпочати повтор при трансляції. Зупиніть трансляцію перед початком повтору.
|
||||
|
||||
Started recording in the replay session=Розпочато запис у сеансі повтору
|
||||
Started recording in the streaming session=Розпочато запис у сеансі трансляції
|
||||
@@ -81,11 +81,11 @@ Streaming stopped because of an error. Verify if settings are correct=Транс
|
||||
%s. Verify if settings are correct=%s. Перевірте правильність налаштувань
|
||||
|
||||
# GPU Screen Recorder errors
|
||||
Desktop portal capture failed.\nEither you canceled the desktop portal or your Wayland compositor doesn't support desktop portal capture\nor it's incorrectly setup on your system.=Захоплення порталу робочого стола не вдалося.\nВи або скасували портал робочого стола, або ваш композитор Wayland не підтримує захоплення порталу робочого стола\nабо він неправильно налаштований у вашій системі.
|
||||
Monitor capture failed.\nThe monitor you are trying to capture is invalid.\nPlease validate your capture settings.=Захоплення монітора не вдалося.\nМонітор, який ви намагаєтесь захопити, недійсний.\nБудь ласка, перевірте налаштування захоплення.
|
||||
Capture failed. Neither H264, HEVC nor AV1 video codecs are supported\non your system or you are trying to capture at a resolution higher than your\nsystem supports for each video codec.=Захоплення не вдалося. Жоден з відеокодеків H264, HEVC або AV1 не підтримується\nвашою системою або ви намагаєтесь захопити відео з роздільною здатністю вище, ніж\nпідтримує ваша система для кожного відеокодека.
|
||||
Capture failed. Your system doesn't support the resolution you are trying to\nrecord at with the video codec you have chosen.\nChange capture resolution or video codec and try again.\nNote: AV1 supports the highest resolution, then HEVC and then H264.=Захоплення не вдалося. Ваша система не підтримує роздільну здатність, з якою ви намагаєтесь\nзаписувати з обраним відеокодеком.\nЗмініть роздільну здатність захоплення або відеокодек і спробуйте знову.\nПримітка: AV1 підтримує максимальну роздільну здатність, потім HEVC, потім H264.
|
||||
Capture failed. Your system doesn't support the video codec you have chosen.\nChange video codec and try again.=Захоплення не вдалося. Ваша система не підтримує обраний відеокодек.\nЗмініть відеокодек і спробуйте знову.
|
||||
Desktop portal capture failed. Either you canceled the desktop portal or your Wayland compositor doesn't support desktop portal capture or it's incorrectly setup on your system.=Захоплення порталу робочого стола не вдалося. Ви або скасували портал робочого стола, або ваш композитор Wayland не підтримує захоплення порталу робочого стола або він неправильно налаштований у вашій системі.
|
||||
Monitor capture failed. The monitor you are trying to capture is invalid. Please validate your capture settings.=Захоплення монітора не вдалося. Монітор, який ви намагаєтесь захопити, недійсний. Будь ласка, перевірте налаштування захоплення.
|
||||
Capture failed. Neither H264, HEVC nor AV1 video codecs are supported on your system or you are trying to capture at a resolution higher than your system supports for each video codec.=Захоплення не вдалося. Жоден з відеокодеків H264, HEVC або AV1 не підтримується вашою системою або ви намагаєтесь захопити відео з роздільною здатністю вище, ніж підтримує ваша система для кожного відеокодека.
|
||||
Capture failed. Your system doesn't support the resolution you are trying to record at with the video codec you have chosen. Change capture resolution or video codec and try again. Note: AV1 supports the highest resolution, then HEVC and then H264.=Захоплення не вдалося. Ваша система не підтримує роздільну здатність, з якою ви намагаєтесь записувати з обраним відеокодеком. Змініть роздільну здатність захоплення або відеокодек і спробуйте знову. Примітка: AV1 підтримує максимальну роздільну здатність, потім HEVC, потім H264.
|
||||
Capture failed. Your system doesn't support the video codec you have chosen. Change video codec and try again.=Захоплення не вдалося. Ваша система не підтримує обраний відеокодек. Змініть відеокодек і спробуйте знову.
|
||||
Stopped capture because the user canceled the desktop portal=Захоплення зупинено, оскільки користувач скасував портал робочого стола
|
||||
Failed to take a screenshot. Verify if settings are correct=Не вдалося зробити знімок екрана. Перевірте правильність налаштувань
|
||||
|
||||
@@ -98,10 +98,12 @@ Failed to launch gpu-screen-recorder to take a screenshot=Не вдалося з
|
||||
# System startup notifications
|
||||
Failed to add GPU Screen Recorder to system startup=Не вдалося додати GPU Screen Recorder до автозавантаження системи
|
||||
Failed to remove GPU Screen Recorder from system startup=Не вдалося видалити GPU Screen Recorder з автозавантаження системи
|
||||
Failed to add GPU Screen Recorder to system startup.\nThis option only works on systems that use systemd.\nYou have to manually add "gsr-ui" to system startup on systems that uses another init system.=Не вдалося додати GPU Screen Recorder до автозавантаження системи.\nЦя опція працює лише на системах, що використовують systemd.\nВи маєте вручну додати "gsr-ui" до автозавантаження на системах з іншою init-системою.
|
||||
To enable autorun: install and configure 'dex' (recommended), or manually add '%s' to your desktop autostart entries.=Для увімкнення автозапуску: встановіть та налаштуйте 'dex' (рекомендовано) або вручну додайте '%s' до записів автозапуску робочого столу.
|
||||
GPU Screen Recorder UI startup has been switched from systemd service to XDG autostart.=Автозапуск GPU Screen Recorder UI переключено зі служби systemd на XDG-автозапуск.
|
||||
GPU Screen Recorder UI autostart via systemd is deprecated. To migrate: install and configure 'dex' (recommended), or manually add '%s' to your desktop autostart entries.=Автозапуск GPU Screen Recorder UI через systemd застарів. Для міграції: встановіть та налаштуйте 'dex' (рекомендовано) або вручну додайте '%s' до записів автозапуску робочого столу.
|
||||
|
||||
# Wayland warning
|
||||
Wayland doesn't support GPU Screen Recorder UI properly,\nthings may not work as expected. Use X11 if you experience issues.=Wayland не підтримує інтерфейс GPU Screen Recorder належним чином,\nдеякі функції можуть не працювати. Використовуйте X11, якщо виникнуть проблеми.
|
||||
Wayland doesn't support GPU Screen Recorder UI properly, things may not work as expected. Use X11 if you experience issues.=Wayland не підтримує інтерфейс GPU Screen Recorder належним чином, деякі функції можуть не працювати. Використовуйте X11, якщо виникнуть проблеми.
|
||||
|
||||
# Hotkey conflicts
|
||||
Some keyboard remapping software conflicts with GPU Screen Recorder on your system.\nKeyboards have been ungrabbed, applications will now receive the hotkeys you press.=Деяке програмне забезпечення перепризначення клавіатури конфліктує з GPU Screen Recorder у вашій системі.\nЗахоплення клавіатури вимкнено, додатки тепер отримуватимуть натискання гарячих клавіш.
|
||||
@@ -156,6 +158,7 @@ Start/stop recording with desktop portal:=Запис через портал:
|
||||
Take a screenshot with desktop portal:=Зробити знімок через портал робочого стола:
|
||||
Start/stop recording a window:=Розпочати/зупинити запис вікна:
|
||||
Take a screenshot of a window:=Зробити знімок вікна:
|
||||
Press ESC to go back to the previous page/close the UI.=Натисніть ESC, щоб повернутися на попередню сторінку/закрити інтерфейс користувача.
|
||||
|
||||
Clear hotkeys=Очистити гарячі клавіші
|
||||
Reset hotkeys to default=Скинути гарячі клавіші за замовчуванням
|
||||
@@ -369,9 +372,15 @@ Record in low-power mode=Записувати в режимі низького
|
||||
Record cursor=Записувати курсор
|
||||
|
||||
Do not force the GPU to go into high performance mode when recording.\nMay affect recording performance, especially when playing a video at the same time.\nIf enabled then it's recommended to use sync to content frame rate mode to reduce power usage when idle.=Не змушувати GPU переходити в режим високої продуктивності при записі.\nМоже вплинути на продуктивність запису, особливо при одночасному відтворенні відео.\nЯкщо увімкнено, рекомендується використовувати режим частоти кадрів синхронізації з контентом\nдля зниження енергоспоживання в режимі очікування.
|
||||
Use vulkan video encoding instead of VAAPI/NVENC.\nEnabling this may result in better game performance while recording, especially on NVIDIA.\nNote that this option is experimental. There may be GPU driver issues that causes issues when this is enabled.=Використовуйте відеокодування Vulkan замість VAAPI/NVENC.\nУвімкнення цієї опції може призвести до кращої продуктивності гри під час запису, особливо на NVIDIA.\nЗверніть увагу, що ця опція є експериментальною. Можливі проблеми з драйвером графічного процесора, які можуть спричиняти проблеми, коли ця опція ввімкнена.
|
||||
Enable vulkan video encoding (experimental)=Увімкнути кодування відео Vulkan (експериментально)
|
||||
|
||||
Show %s notifications=Показувати сповіщення %s
|
||||
Show %s status with scroll lock LED=Показувати статус %s за допомогою індикатора Scroll Lock
|
||||
Show replay notifications=Показувати сповіщення повтору
|
||||
Show recording notifications=Показувати сповіщення запису
|
||||
Show streaming notifications=Показувати сповіщення трансляції
|
||||
Show replay status with scroll lock LED=Показувати статус повтору за допомогою індикатора Scroll Lock
|
||||
Show recording status with scroll lock LED=Показувати статус запису за допомогою індикатора Scroll Lock
|
||||
Show streaming status with scroll lock LED=Показувати статус трансляції за допомогою індикатора Scroll Lock
|
||||
Recording indicator=Індикатор запису
|
||||
|
||||
Simple=Простий
|
||||
@@ -383,15 +392,16 @@ Estimated video file size per minute (excluding audio): %.2fMB=Приблизн
|
||||
# Replay settings
|
||||
Directory to save replays:=Каталог для збереження повторів:
|
||||
Replay indicator=Індикатор повтору
|
||||
Turn on replay when starting a fullscreen application%s=Увімкнути повтор при запуску повноекранної програми%s
|
||||
Turn on replay when starting a game=Увімкнути повтор під час початку гри
|
||||
Autostart=Автозапуск
|
||||
in RAM=в ОЗП
|
||||
on disk=на диску
|
||||
Replay duration in seconds:=Тривалість повтору в секундах:
|
||||
Where should temporary replay data be stored?=Де мають зберігатися тимчасові дані повтору?
|
||||
RAM=ОЗП
|
||||
Disk (Not recommended on SSDs)=Диск (не рекомендовано для SSD)
|
||||
Turn on replay when this program starts=Увімкнути повтор при запуску програми
|
||||
Turn on replay when power supply is connected=Увімкнути повтор при підключенні джерела живлення
|
||||
Only turn on replay if a power supply is connected=Вмикати повтор лише за наявності підключення до джерела живлення
|
||||
Don't turn on replay automatically=Не вмикати повтор автоматично
|
||||
Restart replay on save=Перезапускати повтор при збереженні
|
||||
Estimated video max file size %s: %.2fMB.\nChange video bitrate or replay duration to change file size.=Приблизний максимальний розмір відео файлу %s: %.2fMB.\nЗмініть бітрейт відео або тривалість повтору, щоб змінити розмір файлу.
|
||||
|
||||
Reference in New Issue
Block a user