mirror of
https://repo.dec05eba.com/gpu-screen-recorder-ui
synced 2026-03-31 09:17:04 +09:00
Compare commits
48 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2cabdf7089 | ||
|
|
6a12efec50 | ||
|
|
388a3500e2 | ||
|
|
24806ecf31 | ||
|
|
33a1e9e3bd | ||
|
|
4fd05d613b | ||
|
|
b80e864bbb | ||
|
|
33bc121bc8 | ||
|
|
106e7febe5 | ||
|
|
3c6e72350e | ||
|
|
43c16a7865 | ||
|
|
264a838e1f | ||
|
|
b927cb7f21 | ||
|
|
8e35de9e8b | ||
|
|
13984f8636 | ||
|
|
5f3ace0c47 | ||
|
|
651782a3a3 | ||
|
|
4e5a073854 | ||
|
|
1442016a18 | ||
|
|
2adc462d94 | ||
|
|
9aea35200d | ||
|
|
5ef06a2466 | ||
|
|
c3e9aa0f81 | ||
|
|
444599c6ce | ||
|
|
6127995b36 | ||
|
|
83aa20a9e4 | ||
|
|
02e4e25b75 | ||
|
|
b32ae6e2f1 | ||
|
|
b8d29f0ac0 | ||
|
|
2395fbcf69 | ||
|
|
d6a64b03e0 | ||
|
|
1951fd7c20 | ||
|
|
4b47063406 | ||
|
|
48609e33c9 | ||
|
|
52afad5824 | ||
|
|
636eca0d0e | ||
|
|
8fd7064bff | ||
|
|
fde1b438df | ||
|
|
1d96b73e1a | ||
|
|
1ce12067aa | ||
|
|
728ccc40a6 | ||
|
|
02db186232 | ||
|
|
44123d35a5 | ||
|
|
a31bfbe288 | ||
|
|
f3d6d8bc53 | ||
|
|
74d6a05e2f | ||
|
|
89995b805e | ||
|
|
f921be46c0 |
15
README.md
15
README.md
@@ -5,15 +5,17 @@ A fullscreen overlay UI for [GPU Screen Recorder](https://git.dec05eba.com/gpu-s
|
||||
The application is currently primarly designed for X11 but it can run on Wayland as well through XWayland, with some caveats because of Wayland limitations.
|
||||
|
||||
# Installation
|
||||
If you are using an Arch Linux based distro then you can find gpu screen recorder ui on aur under the name gpu-screen-recorder-ui (`yay -S gpu-screen-recorder-ui`).\
|
||||
If you are running an Arch Linux based distro then you can find gpu screen recorder ui in the official repositories under the name gpu-screen-recorder-ui (`sudo pacman -S gpu-screen-recorder-ui`).\
|
||||
If you are running another distro then you can run `sudo ./install.sh`, but you need to manually install the dependencies, as described below.\
|
||||
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`, 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
|
||||
@@ -82,3 +84,6 @@ Some Wayland compositors don't support copying images on the clipboard between X
|
||||
since Wayland doesn't support a non-focused application from setting the clipboard, so it can't work with GPU Screen Recorder hotkey usage. Use X11 if you want a functioning desktop.
|
||||
## The controller hotkey and steam overlap (home button brings up steam overlay)
|
||||
You can either disable the steam overlay or in steam click Steam->Settings->Controller and then click "Begin Test" under "Test Device Inputs". Click on "Setup Device Inputs" and configure controller buttons there and when you get to the home button press X to unbind it from steam.
|
||||
## The UI looks messed up on my Wayland system
|
||||
Wayland doesn't support GPU Screen Recorder UI properly. Some Wayland environments can display GPU Screen Recorder UI pretty well (such as KDE Plasma and Gnome) while others cannot (such as Hyprland and Niri).
|
||||
This is an issue in Wayland and it may be the case that it will never be fixed. Use X11 if you experience issues.
|
||||
|
||||
16
TODO
16
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.
|
||||
@@ -117,8 +113,6 @@ System startup option should also support runit and some other init systems, not
|
||||
|
||||
Use x11 shm instead of XGetImage (https://stackoverflow.com/questions/43442675/how-to-use-xshmgetimage-and-xshmputimage).
|
||||
|
||||
Add a hotkey to record/stream/replay region.
|
||||
|
||||
Do xi grab for keys as well. Otherwise the ui cant be used for keyboard input if a program has grabbed the keyboard, and there could possibly be a game that grabs the keyboard as well.
|
||||
|
||||
Make inactive buttons gray (in dropdown boxes and in the front page with save, etc when replay is not running).
|
||||
@@ -196,8 +190,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.
|
||||
@@ -244,8 +236,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.
|
||||
@@ -261,3 +251,9 @@ Add option to choose video container (either flv or mpegts) for youtube livestre
|
||||
Get wayland cursor position for region selector, otherwise the start position before the cursor moves is off.
|
||||
|
||||
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.
|
||||
@@ -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;
|
||||
|
||||
@@ -5,8 +5,12 @@
|
||||
namespace gsr {
|
||||
struct ActiveKwinWindow {
|
||||
std::string title = "Game";
|
||||
bool fullscreen = false;
|
||||
std::string monitorName = "";
|
||||
};
|
||||
|
||||
void start_kwin_helper_thread();
|
||||
std::string get_current_kwin_window_title();
|
||||
bool get_current_kwin_window_fullscreen();
|
||||
std::string get_current_kwin_window_monitor_name();
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include "GlobalHotkeys/GlobalHotkeysJoystick.hpp"
|
||||
#include "AudioPlayer.hpp"
|
||||
#include "RegionSelector.hpp"
|
||||
#include "WindowSelector.hpp"
|
||||
#include "ClipboardFile.hpp"
|
||||
#include "LedIndicator.hpp"
|
||||
#include "CursorTracker/CursorTracker.hpp"
|
||||
@@ -158,7 +157,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);
|
||||
bool on_press_start_replay(bool disable_notification, bool finished_selection, std::string monitor_to_capture = "");
|
||||
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);
|
||||
@@ -275,9 +274,7 @@ namespace gsr {
|
||||
bool start_region_capture = false;
|
||||
std::function<void()> on_region_selected;
|
||||
|
||||
WindowSelector window_selector;
|
||||
bool start_window_capture = false;
|
||||
std::function<void()> on_window_selected;
|
||||
|
||||
std::string recording_capture_target;
|
||||
std::string screenshot_capture_target;
|
||||
@@ -293,5 +290,6 @@ namespace gsr {
|
||||
std::unique_ptr<LedIndicator> led_indicator = nullptr;
|
||||
|
||||
bool supports_window_title = false;
|
||||
bool supports_window_fullscreen_state = false;
|
||||
};
|
||||
}
|
||||
@@ -15,14 +15,26 @@ namespace gsr {
|
||||
mgl::vec2i size;
|
||||
};
|
||||
|
||||
struct RegionWindow {
|
||||
Window window = None;
|
||||
mgl::vec2i pos;
|
||||
mgl::vec2i size;
|
||||
};
|
||||
|
||||
class RegionSelector {
|
||||
public:
|
||||
enum class SelectionType {
|
||||
NONE,
|
||||
REGION,
|
||||
WINDOW
|
||||
};
|
||||
|
||||
RegionSelector();
|
||||
RegionSelector(const RegionSelector&) = delete;
|
||||
RegionSelector& operator=(const RegionSelector&) = delete;
|
||||
~RegionSelector();
|
||||
|
||||
bool start(mgl::Color border_color);
|
||||
bool start(SelectionType selection_type, mgl::Color border_color);
|
||||
void stop();
|
||||
bool is_started() const;
|
||||
|
||||
@@ -30,7 +42,11 @@ namespace gsr {
|
||||
bool poll_events();
|
||||
bool take_selection();
|
||||
bool take_canceled();
|
||||
Region get_selection(Display *x11_dpy, struct wl_display *wayland_dpy) const;
|
||||
Region get_region_selection(Display *x11_dpy, struct wl_display *wayland_dpy) const;
|
||||
// Returns None (0) if none is selected
|
||||
Window get_window_selection() const;
|
||||
|
||||
SelectionType get_selection_type() const;
|
||||
private:
|
||||
void on_button_press(const void *de);
|
||||
void on_button_release(const void *de);
|
||||
@@ -50,6 +66,10 @@ namespace gsr {
|
||||
bool canceled = false;
|
||||
bool is_wayland = false;
|
||||
std::vector<Monitor> monitors;
|
||||
std::vector<RegionWindow> windows; // First window is the window that is on top
|
||||
std::optional<RegionWindow> focused_window;
|
||||
mgl::vec2i cursor_pos;
|
||||
|
||||
SelectionType selection_type = SelectionType::NONE;
|
||||
};
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
|
||||
#include <mglpp/graphics/Color.hpp>
|
||||
|
||||
namespace gsr {
|
||||
class WindowSelector {
|
||||
public:
|
||||
WindowSelector();
|
||||
WindowSelector(const WindowSelector&) = delete;
|
||||
WindowSelector& operator=(const WindowSelector&) = delete;
|
||||
~WindowSelector();
|
||||
|
||||
bool start(mgl::Color border_color);
|
||||
void stop();
|
||||
bool is_started() const;
|
||||
|
||||
bool failed() const;
|
||||
bool poll_events();
|
||||
bool take_selection();
|
||||
bool take_canceled();
|
||||
Window get_selection() const;
|
||||
private:
|
||||
Display *dpy = nullptr;
|
||||
Cursor crosshair_cursor = None;
|
||||
Colormap border_window_colormap = None;
|
||||
Window border_window = None;
|
||||
Window selected_window = None;
|
||||
bool selected = false;
|
||||
bool canceled = false;
|
||||
};
|
||||
}
|
||||
@@ -20,6 +20,13 @@ namespace gsr {
|
||||
std::string name;
|
||||
};
|
||||
|
||||
struct DrawableGeometry {
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
};
|
||||
|
||||
std::optional<std::string> get_window_title(Display *dpy, Window window);
|
||||
Window get_focused_window(Display *dpy, WindowCaptureType cap_type, bool fallback_cursor_focused = true);
|
||||
std::string get_focused_window_name(Display *dpy, WindowCaptureType window_capture_type, bool fallback_cursor_focused = true);
|
||||
@@ -27,6 +34,7 @@ namespace gsr {
|
||||
std::string get_window_name_at_cursor_position(Display *dpy, Window ignore_window);
|
||||
void set_window_size_not_resizable(Display *dpy, Window window, int width, int height);
|
||||
Window window_get_target_window_child(Display *display, Window window);
|
||||
unsigned char* window_get_property(Display *dpy, Window window, Atom property_type, const char *property_name, unsigned int *property_size);
|
||||
mgl::vec2i get_cursor_position(Display *dpy, Window *window);
|
||||
mgl::vec2i create_window_get_center_position(Display *display);
|
||||
std::string get_window_manager_name(Display *display);
|
||||
@@ -38,6 +46,8 @@ namespace gsr {
|
||||
void xi_warp_all_mouse_devices(Display *dpy, mgl::vec2i position);
|
||||
void window_set_fullscreen(Display *dpy, Window window, bool fullscreen);
|
||||
bool window_is_fullscreen(Display *display, Window window);
|
||||
bool get_drawable_geometry(Display *display, Drawable drawable, DrawableGeometry *geometry);
|
||||
std::optional<Monitor> get_monitor_by_window_center(Display *display, Window window);
|
||||
bool set_window_wm_state(Display *dpy, Window window, Atom atom);
|
||||
void make_window_click_through(Display *display, Window window);
|
||||
bool make_window_sticky(Display *dpy, Window window);
|
||||
|
||||
@@ -41,7 +41,7 @@ namespace gsr {
|
||||
STREAM
|
||||
};
|
||||
|
||||
SettingsPage(Type type, const GsrInfo *gsr_info, Config &config, PageStack *page_stack, bool supports_window_title);
|
||||
SettingsPage(Type type, const GsrInfo *gsr_info, Config &config, PageStack *page_stack, bool supports_window_title, bool supports_window_fullscreen_state);
|
||||
SettingsPage(const SettingsPage&) = delete;
|
||||
SettingsPage& operator=(const SettingsPage&) = delete;
|
||||
|
||||
@@ -249,5 +249,6 @@ namespace gsr {
|
||||
std::optional<GsrCameraSetup> selected_camera_setup;
|
||||
|
||||
bool supports_window_title = false;
|
||||
bool supports_window_fullscreen_state = false;
|
||||
};
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
project('gsr-ui', ['c', 'cpp'], version : '1.10.5', default_options : ['warning_level=2', 'cpp_std=c++17'], subproject_dir : 'depends')
|
||||
project('gsr-ui', ['c', 'cpp'], version : '1.10.9', default_options : ['warning_level=2', 'cpp_std=c++17'], subproject_dir : 'depends')
|
||||
|
||||
add_project_arguments('-D_FILE_OFFSET_BITS=64', language : ['c', 'cpp'])
|
||||
|
||||
@@ -45,7 +45,6 @@ src = [
|
||||
'src/KwinWorkaround.cpp',
|
||||
'src/WindowUtils.cpp',
|
||||
'src/RegionSelector.cpp',
|
||||
'src/WindowSelector.cpp',
|
||||
'src/Config.cpp',
|
||||
'src/GsrInfo.cpp',
|
||||
'src/Process.cpp',
|
||||
@@ -71,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.1"', language: ['c', 'cpp'])
|
||||
add_project_arguments('-DGSR_FLATPAK_VERSION="5.12.2"', language: ['c', 'cpp'])
|
||||
|
||||
add_project_arguments('-DKWIN_HELPER_SCRIPT_PATH="' + gsr_ui_resources_path + '/gsrkwinhelper.js"', language: ['c', 'cpp'])
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "gsr-ui"
|
||||
type = "executable"
|
||||
version = "1.10.5"
|
||||
version = "1.10.9"
|
||||
platforms = ["posix"]
|
||||
|
||||
[lang.cpp]
|
||||
|
||||
@@ -43,6 +43,7 @@ namespace gsr {
|
||||
|
||||
XEvent xev;
|
||||
while(running) {
|
||||
poll_fds[0].revents = 0;
|
||||
poll(poll_fds, 1, 100);
|
||||
while(XPending(dpy)) {
|
||||
XNextEvent(dpy, &xev);
|
||||
@@ -299,8 +300,8 @@ namespace gsr {
|
||||
return;
|
||||
}
|
||||
|
||||
struct stat64 stat;
|
||||
if(fstat64(file_fd, &stat) == -1) {
|
||||
struct stat stat;
|
||||
if(fstat(file_fd, &stat) == -1) {
|
||||
fprintf(stderr, "gsr ui: error: ClipboardFile::set_current_file: failed to get file size for file %s, error: %s\n", filepath.c_str(), strerror(errno));
|
||||
close(file_fd);
|
||||
file_fd = -1;
|
||||
@@ -312,4 +313,4 @@ namespace gsr {
|
||||
XSetSelectionOwner(dpy, clipboard_atom, clipboard_window, CurrentTime);
|
||||
XFlush(dpy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,8 +213,10 @@ namespace gsr {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!(poll_fd[i].revents & POLLIN))
|
||||
if(!(poll_fd[i].revents & POLLIN)) {
|
||||
poll_fd[i].revents = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(i == event_index) {
|
||||
goto done;
|
||||
@@ -235,6 +237,8 @@ namespace gsr {
|
||||
} else {
|
||||
process_input_event(poll_fd[i].fd, event);
|
||||
}
|
||||
|
||||
poll_fd[i].revents = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <unistd.h>
|
||||
extern "C" {
|
||||
#include <mgl/mgl.h>
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,17 +2,15 @@
|
||||
|
||||
#include <cstddef>
|
||||
#include <iostream>
|
||||
#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");
|
||||
@@ -24,7 +22,9 @@ namespace gsr {
|
||||
std::cerr << "Started a KWin helper thread\n";
|
||||
|
||||
char buffer[4096];
|
||||
const std::string prefix = "Active window title set to: ";
|
||||
const std::string prefix_title = "Active window title set to: ";
|
||||
const std::string prefix_fullscreen = "Active window fullscreen state set to: ";
|
||||
const std::string prefix_monitor = "Active window monitor name set to: ";
|
||||
|
||||
std::string line;
|
||||
while (fgets(buffer, sizeof(buffer), pipe) != nullptr) {
|
||||
@@ -34,15 +34,17 @@ namespace gsr {
|
||||
line.pop_back();
|
||||
}
|
||||
|
||||
size_t pos = line.find(prefix);
|
||||
if (pos != std::string::npos) {
|
||||
std::string title = line.substr(pos + prefix.length());
|
||||
|
||||
if (title == "gsr ui" || title == "gsr notify") {
|
||||
continue; // ignore the overlay and notification
|
||||
}
|
||||
|
||||
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());
|
||||
active_kwin_window.title = std::move(title);
|
||||
} else if ((pos = line.find(prefix_fullscreen)) != std::string::npos) {
|
||||
std::string fullscreen = line.substr(pos + prefix_fullscreen.length());
|
||||
active_kwin_window.fullscreen = fullscreen == "1";
|
||||
} else if ((pos = line.find(prefix_monitor)) != std::string::npos) {
|
||||
std::string monitorName = line.substr(pos + prefix_monitor.length());
|
||||
active_kwin_window.monitorName = std::move(monitorName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,9 +52,20 @@ 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;
|
||||
}
|
||||
|
||||
void start_kwin_helper_thread() {
|
||||
if (kwin_helper_thread_started) {
|
||||
return;
|
||||
|
||||
248
src/Overlay.cpp
248
src/Overlay.cpp
@@ -32,6 +32,7 @@
|
||||
#include <algorithm>
|
||||
#include <inttypes.h>
|
||||
#include <math.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xutil.h>
|
||||
@@ -161,26 +162,6 @@ namespace gsr {
|
||||
return result;
|
||||
}
|
||||
|
||||
struct DrawableGeometry {
|
||||
int x, y, width, height;
|
||||
};
|
||||
|
||||
static bool get_drawable_geometry(Display *display, Drawable drawable, DrawableGeometry *geometry) {
|
||||
geometry->x = 0;
|
||||
geometry->y = 0;
|
||||
geometry->width = 0;
|
||||
geometry->height = 0;
|
||||
|
||||
Window root_window;
|
||||
unsigned int w, h;
|
||||
unsigned int dummy_border, dummy_depth;
|
||||
Status s = XGetGeometry(display, drawable, &root_window, &geometry->x, &geometry->y, &w, &h, &dummy_border, &dummy_depth);
|
||||
|
||||
geometry->width = w;
|
||||
geometry->height = h;
|
||||
return s != Success;
|
||||
}
|
||||
|
||||
static bool diff_int(int a, int b, int difference) {
|
||||
return std::abs(a - b) <= difference;
|
||||
}
|
||||
@@ -283,6 +264,8 @@ namespace gsr {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 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() {
|
||||
const char *args[] = { "hyprctl", "layers", nullptr };
|
||||
std::string stdout_str;
|
||||
@@ -543,6 +526,7 @@ namespace gsr {
|
||||
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);
|
||||
@@ -552,16 +536,6 @@ namespace gsr {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
update_led_indicator_after_settings_change();
|
||||
@@ -802,24 +776,28 @@ namespace gsr {
|
||||
if(region_selector.take_canceled()) {
|
||||
on_region_selected = nullptr;
|
||||
} else if(region_selector.take_selection() && on_region_selected) {
|
||||
on_region_selected();
|
||||
on_region_selected = nullptr;
|
||||
}
|
||||
switch(region_selector.get_selection_type()) {
|
||||
case RegionSelector::SelectionType::NONE: {
|
||||
break;
|
||||
}
|
||||
case RegionSelector::SelectionType::REGION: {
|
||||
on_region_selected();
|
||||
break;
|
||||
}
|
||||
case RegionSelector::SelectionType::WINDOW: {
|
||||
mgl_context *context = mgl_get_context();
|
||||
Display *display = (Display*)context->connection;
|
||||
|
||||
window_selector.poll_events();
|
||||
if(window_selector.take_canceled()) {
|
||||
on_window_selected = nullptr;
|
||||
} else if(window_selector.take_selection() && on_window_selected) {
|
||||
mgl_context *context = mgl_get_context();
|
||||
Display *display = (Display*)context->connection;
|
||||
|
||||
const Window selected_window = window_selector.get_selection();
|
||||
if(selected_window && selected_window != DefaultRootWindow(display)) {
|
||||
on_window_selected();
|
||||
} else {
|
||||
show_notification(TR("No window selected"), notification_timeout_seconds, mgl::Color(255, 255, 255), mgl::Color(255, 0, 0), NotificationType::NOTICE, nullptr, NotificationLevel::ERROR);
|
||||
const Window selected_window = region_selector.get_window_selection();
|
||||
if(selected_window && selected_window != DefaultRootWindow(display)) {
|
||||
on_region_selected();
|
||||
} else {
|
||||
show_notification(TR("No window selected"), notification_timeout_seconds, mgl::Color(255, 255, 255), mgl::Color(255, 0, 0), NotificationType::NOTICE, nullptr, NotificationLevel::ERROR);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
on_window_selected = nullptr;
|
||||
on_region_selected = nullptr;
|
||||
}
|
||||
|
||||
if(!visible || !window)
|
||||
@@ -873,7 +851,7 @@ namespace gsr {
|
||||
if(start_region_capture) {
|
||||
start_region_capture = false;
|
||||
hide();
|
||||
if(!region_selector.start(get_color_theme().tint_color)) {
|
||||
if(!region_selector.start(RegionSelector::SelectionType::REGION, get_color_theme().tint_color)) {
|
||||
show_notification(TR("Failed to start region capture"), notification_error_timeout_seconds, mgl::Color(255, 255, 255), mgl::Color(255, 0, 0), NotificationType::NOTICE, nullptr, NotificationLevel::ERROR);
|
||||
on_region_selected = nullptr;
|
||||
}
|
||||
@@ -882,13 +860,13 @@ namespace gsr {
|
||||
if(start_window_capture) {
|
||||
start_window_capture = false;
|
||||
hide();
|
||||
if(!window_selector.start(get_color_theme().tint_color)) {
|
||||
if(!region_selector.start(RegionSelector::SelectionType::WINDOW, get_color_theme().tint_color)) {
|
||||
show_notification(TR("Failed to start window capture"), notification_error_timeout_seconds, mgl::Color(255, 255, 255), mgl::Color(255, 0, 0), NotificationType::NOTICE, nullptr, NotificationLevel::ERROR);
|
||||
on_window_selected = nullptr;
|
||||
on_region_selected = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if(region_selector.is_started() || window_selector.is_started()) {
|
||||
if(region_selector.is_started()) {
|
||||
usleep(5 * 1000); // 5 ms
|
||||
return true;
|
||||
}
|
||||
@@ -1023,7 +1001,7 @@ namespace gsr {
|
||||
if(visible)
|
||||
return;
|
||||
|
||||
if(region_selector.is_started() || window_selector.is_started())
|
||||
if(region_selector.is_started())
|
||||
return;
|
||||
|
||||
drawn_first_frame = false;
|
||||
@@ -1268,7 +1246,7 @@ 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);
|
||||
auto replay_settings_page = std::make_unique<SettingsPage>(SettingsPage::Type::REPLAY, &gsr_info, config, &page_stack, supports_window_title, supports_window_fullscreen_state);
|
||||
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)
|
||||
@@ -1302,7 +1280,7 @@ 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);
|
||||
auto record_settings_page = std::make_unique<SettingsPage>(SettingsPage::Type::RECORD, &gsr_info, config, &page_stack, supports_window_title, supports_window_fullscreen_state);
|
||||
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);
|
||||
@@ -1329,7 +1307,7 @@ 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);
|
||||
auto stream_settings_page = std::make_unique<SettingsPage>(SettingsPage::Type::STREAM, &gsr_info, config, &page_stack, supports_window_title, supports_window_fullscreen_state);
|
||||
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);
|
||||
@@ -1363,15 +1341,25 @@ 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) {
|
||||
@@ -1867,10 +1855,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();
|
||||
}
|
||||
|
||||
@@ -2018,27 +2002,17 @@ namespace gsr {
|
||||
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;
|
||||
|
||||
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;
|
||||
focused_window_name = get_window_name_at_cursor_position(display, gsr_ui_window);
|
||||
const Window gsr_ui_window = window ? (Window)window->get_system_handle() : None;
|
||||
std::string 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";
|
||||
|
||||
focused_window_name = strip(focused_window_name);
|
||||
string_replace_characters(focused_window_name.data(), "/\\", ' ');
|
||||
|
||||
std::string video_directory = filepath_get_directory(video_filepath.c_str()) + "/" + focused_window_name;
|
||||
@@ -2365,18 +2339,28 @@ namespace gsr {
|
||||
mgl_context *context = mgl_get_context();
|
||||
Display *display = (Display*)context->connection;
|
||||
|
||||
const Window focused_window = get_focused_window(display, WindowCaptureType::FOCUSED, false);
|
||||
const bool prev_focused_window_is_fullscreen = focused_window_is_fullscreen;
|
||||
Window focused_window = None;
|
||||
|
||||
focused_window = get_focused_window(display, WindowCaptureType::FOCUSED, false);
|
||||
if(window && focused_window == (Window)window->get_system_handle())
|
||||
return;
|
||||
|
||||
const bool prev_focused_window_is_fullscreen = focused_window_is_fullscreen;
|
||||
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;
|
||||
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);
|
||||
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);
|
||||
on_press_start_replay(true, false, fullscreen_window_monitor);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2589,7 +2573,7 @@ namespace gsr {
|
||||
}
|
||||
|
||||
void Overlay::add_region_command(std::vector<const char*> &args, char *region_str, int region_str_size) {
|
||||
Region region = region_selector.get_selection(x11_dpy, wayland_dpy);
|
||||
Region region = region_selector.get_region_selection(x11_dpy, wayland_dpy);
|
||||
if(region.size.x <= 32 && region.size.y <= 32) {
|
||||
region.size.x = 0;
|
||||
region.size.y = 0;
|
||||
@@ -2673,7 +2657,7 @@ namespace gsr {
|
||||
|
||||
std::string Overlay::get_capture_target(const std::string &capture_target, const SupportedCaptureOptions &capture_options) {
|
||||
if(capture_target == "window") {
|
||||
return std::to_string(window_selector.get_selection());
|
||||
return std::to_string(region_selector.get_window_selection());
|
||||
} else if(capture_target == "focused_monitor") {
|
||||
std::optional<CursorInfo> cursor_info;
|
||||
if(cursor_tracker) {
|
||||
@@ -2867,8 +2851,8 @@ namespace gsr {
|
||||
return capture_source_arg;
|
||||
}
|
||||
|
||||
bool Overlay::on_press_start_replay(bool disable_notification, bool finished_selection) {
|
||||
if(region_selector.is_started() || window_selector.is_started())
|
||||
bool Overlay::on_press_start_replay(bool disable_notification, bool finished_selection, std::string monitor_to_capture) {
|
||||
if(region_selector.is_started())
|
||||
return false;
|
||||
|
||||
switch(recording_status) {
|
||||
@@ -2911,15 +2895,6 @@ namespace gsr {
|
||||
return true;
|
||||
}
|
||||
|
||||
const SupportedCaptureOptions capture_options = get_supported_capture_options(gsr_info);
|
||||
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());
|
||||
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;
|
||||
}
|
||||
|
||||
if(config.replay_config.record_options.record_area_option == "region" && !finished_selection) {
|
||||
start_region_capture = true;
|
||||
on_region_selected = [disable_notification, this]() {
|
||||
@@ -2930,12 +2905,21 @@ namespace gsr {
|
||||
|
||||
if(config.replay_config.record_options.record_area_option == "window" && !finished_selection) {
|
||||
start_window_capture = true;
|
||||
on_window_selected = [disable_notification, this]() {
|
||||
on_region_selected = [disable_notification, this]() {
|
||||
on_press_start_replay(disable_notification, true);
|
||||
};
|
||||
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);
|
||||
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());
|
||||
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;
|
||||
}
|
||||
|
||||
// TODO: Validate input, fallback to valid values
|
||||
const std::string fps = std::to_string(config.replay_config.record_options.fps);
|
||||
const std::string video_bitrate = std::to_string(config.replay_config.record_options.video_bitrate);
|
||||
@@ -3037,7 +3021,7 @@ namespace gsr {
|
||||
}
|
||||
|
||||
void Overlay::on_press_start_record(bool finished_selection, RecordForceType force_type) {
|
||||
if(region_selector.is_started() || window_selector.is_started())
|
||||
if(region_selector.is_started())
|
||||
return;
|
||||
|
||||
switch(recording_status) {
|
||||
@@ -3151,15 +3135,6 @@ namespace gsr {
|
||||
break;
|
||||
}
|
||||
|
||||
const SupportedCaptureOptions capture_options = get_supported_capture_options(gsr_info);
|
||||
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());
|
||||
show_notification(err_msg, notification_error_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), NotificationType::RECORD, nullptr, NotificationLevel::ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
if(record_area_option == "region" && !finished_selection) {
|
||||
start_region_capture = true;
|
||||
on_region_selected = [this, force_type]() {
|
||||
@@ -3170,12 +3145,21 @@ namespace gsr {
|
||||
|
||||
if(record_area_option == "window" && !finished_selection) {
|
||||
start_window_capture = true;
|
||||
on_window_selected = [this, force_type]() {
|
||||
on_region_selected = [this, force_type]() {
|
||||
on_press_start_record(true, force_type);
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
const SupportedCaptureOptions capture_options = get_supported_capture_options(gsr_info);
|
||||
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());
|
||||
show_notification(err_msg, notification_error_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), NotificationType::RECORD, nullptr, NotificationLevel::ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
record_filepath.clear();
|
||||
|
||||
// TODO: Validate input, fallback to valid values
|
||||
@@ -3309,7 +3293,7 @@ namespace gsr {
|
||||
}
|
||||
|
||||
void Overlay::on_press_start_stream(bool finished_selection) {
|
||||
if(region_selector.is_started() || window_selector.is_started())
|
||||
if(region_selector.is_started())
|
||||
return;
|
||||
|
||||
switch(recording_status) {
|
||||
@@ -3349,15 +3333,6 @@ namespace gsr {
|
||||
return;
|
||||
}
|
||||
|
||||
const SupportedCaptureOptions capture_options = get_supported_capture_options(gsr_info);
|
||||
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());
|
||||
show_notification(err_msg, notification_error_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), NotificationType::STREAM, nullptr, NotificationLevel::ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
if(config.streaming_config.record_options.record_area_option == "region" && !finished_selection) {
|
||||
start_region_capture = true;
|
||||
on_region_selected = [this]() {
|
||||
@@ -3368,12 +3343,21 @@ namespace gsr {
|
||||
|
||||
if(config.streaming_config.record_options.record_area_option == "window" && !finished_selection) {
|
||||
start_window_capture = true;
|
||||
on_window_selected = [this]() {
|
||||
on_region_selected = [this]() {
|
||||
on_press_start_stream(true);
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
const SupportedCaptureOptions capture_options = get_supported_capture_options(gsr_info);
|
||||
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());
|
||||
show_notification(err_msg, notification_error_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), NotificationType::STREAM, nullptr, NotificationLevel::ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Validate input, fallback to valid values
|
||||
const std::string fps = std::to_string(config.streaming_config.record_options.fps);
|
||||
const std::string video_bitrate = std::to_string(config.streaming_config.record_options.video_bitrate);
|
||||
@@ -3466,7 +3450,7 @@ namespace gsr {
|
||||
}
|
||||
|
||||
void Overlay::on_press_take_screenshot(bool finished_selection, ScreenshotForceType force_type) {
|
||||
if(region_selector.is_started() || window_selector.is_started())
|
||||
if(region_selector.is_started())
|
||||
return;
|
||||
|
||||
if(gpu_screen_recorder_screenshot_process > 0) {
|
||||
@@ -3487,15 +3471,6 @@ namespace gsr {
|
||||
break;
|
||||
}
|
||||
|
||||
const SupportedCaptureOptions capture_options = get_supported_capture_options(gsr_info);
|
||||
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());
|
||||
show_notification(err_msg, notification_error_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), NotificationType::SCREENSHOT, nullptr, NotificationLevel::ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
if(record_area_option == "region" && !finished_selection) {
|
||||
start_region_capture = true;
|
||||
on_region_selected = [this, force_type]() {
|
||||
@@ -3506,12 +3481,21 @@ namespace gsr {
|
||||
|
||||
if(record_area_option == "window" && !finished_selection) {
|
||||
start_window_capture = true;
|
||||
on_window_selected = [this, force_type]() {
|
||||
on_region_selected = [this, force_type]() {
|
||||
on_press_take_screenshot(true, force_type);
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
const SupportedCaptureOptions capture_options = get_supported_capture_options(gsr_info);
|
||||
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());
|
||||
show_notification(err_msg, notification_error_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), NotificationType::SCREENSHOT, nullptr, NotificationLevel::ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Validate input, fallback to valid values
|
||||
std::string output_file;
|
||||
if(config.screenshot_config.save_screenshot_to_disk)
|
||||
|
||||
@@ -2,11 +2,13 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include <X11/Xatom.h>
|
||||
#include <X11/extensions/XInput2.h>
|
||||
#include <X11/extensions/Xrandr.h>
|
||||
#include <X11/extensions/shape.h>
|
||||
#include <mglpp/system/Rect.hpp>
|
||||
|
||||
namespace gsr {
|
||||
static const int cursor_window_size = 32;
|
||||
@@ -66,7 +68,11 @@ namespace gsr {
|
||||
(unsigned short)max_int(0, width - border_size*2), (unsigned short)max_int(0, border_size)
|
||||
}, // Bottom
|
||||
};
|
||||
XShapeCombineRectangles(dpy, window, ShapeBounding, 0, 0, rectangles, 4, ShapeSet, Unsorted);
|
||||
|
||||
if(width == 0 && height == 0)
|
||||
XShapeCombineRectangles(dpy, window, ShapeBounding, 0, 0, rectangles, 0, ShapeSet, Unsorted);
|
||||
else
|
||||
XShapeCombineRectangles(dpy, window, ShapeBounding, 0, 0, rectangles, 4, ShapeSet, Unsorted);
|
||||
XFlush(dpy);
|
||||
}
|
||||
|
||||
@@ -96,7 +102,8 @@ namespace gsr {
|
||||
height = abs(height);
|
||||
}
|
||||
|
||||
XDrawRectangle(dpy, window, gc, x, y, width, height);
|
||||
if(width != 0 && height != 0)
|
||||
XDrawRectangle(dpy, window, gc, x, y, width, height);
|
||||
}
|
||||
|
||||
static Window create_cursor_window(Display *dpy, int width, int height, XVisualInfo *vinfo, unsigned long background_pixel) {
|
||||
@@ -115,6 +122,13 @@ namespace gsr {
|
||||
return window;
|
||||
}
|
||||
|
||||
static void draw_rectangle_or_region(Display *dpy, Window window, GC region_gc, int region_border_size, bool is_wayland, mgl::vec2i pos, mgl::vec2i size) {
|
||||
if(is_wayland)
|
||||
draw_rectangle(dpy, window, region_gc, pos.x, pos.y, size.x, size.y);
|
||||
else
|
||||
set_region_rectangle(dpy, window, pos.x, pos.y, size.x, size.y, region_border_size);
|
||||
}
|
||||
|
||||
static void draw_rectangle_around_selected_monitor(Display *dpy, Window window, GC region_gc, int region_border_size, bool is_wayland, const std::vector<Monitor> &monitors, mgl::vec2i cursor_pos) {
|
||||
const Monitor *focused_monitor = nullptr;
|
||||
for(const Monitor &monitor : monitors) {
|
||||
@@ -137,10 +151,7 @@ namespace gsr {
|
||||
height = focused_monitor->size.y;
|
||||
}
|
||||
|
||||
if(is_wayland)
|
||||
draw_rectangle(dpy, window, region_gc, x, y, width, height);
|
||||
else
|
||||
set_region_rectangle(dpy, window, x, y, width, height, region_border_size);
|
||||
draw_rectangle_or_region(dpy, window, region_gc, region_border_size, is_wayland, mgl::vec2i(x, y), mgl::vec2i(width, height));
|
||||
}
|
||||
|
||||
static void update_cursor_window(Display *dpy, Window window, Window cursor_window, bool is_wayland, int cursor_x, int cursor_y, int cursor_window_size, int thickness, GC cursor_gc) {
|
||||
@@ -222,6 +233,42 @@ namespace gsr {
|
||||
};
|
||||
}
|
||||
|
||||
static std::vector<RegionWindow> query_windows(Display *dpy) {
|
||||
std::vector<RegionWindow> windows;
|
||||
|
||||
Window root_return = None;
|
||||
Window parent_return = None;
|
||||
Window *children_return = nullptr;
|
||||
unsigned int num_children_return = 0;
|
||||
if(!XQueryTree(dpy, DefaultRootWindow(dpy), &root_return, &parent_return, &children_return, &num_children_return) || !children_return)
|
||||
return windows;
|
||||
|
||||
for(int i = (int)num_children_return - 1; i >= 0; --i) {
|
||||
const Window child_window = children_return[i];
|
||||
XWindowAttributes win_attr;
|
||||
if(XGetWindowAttributes(dpy, child_window, &win_attr) && !win_attr.override_redirect && win_attr.c_class == InputOutput && win_attr.map_state == IsViewable) {
|
||||
windows.push_back(
|
||||
RegionWindow{
|
||||
child_window,
|
||||
mgl::vec2i(win_attr.x, win_attr.y),
|
||||
mgl::vec2i(win_attr.width, win_attr.height)
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
XFree(children_return);
|
||||
return windows;
|
||||
}
|
||||
|
||||
static std::optional<RegionWindow> get_window_by_position(const std::vector<RegionWindow> &windows, mgl::vec2i pos) {
|
||||
for(const RegionWindow &window : windows) {
|
||||
if(mgl::IntRect(window.pos, window.size).contains(pos))
|
||||
return window;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
RegionSelector::RegionSelector() {
|
||||
|
||||
}
|
||||
@@ -230,7 +277,7 @@ namespace gsr {
|
||||
stop();
|
||||
}
|
||||
|
||||
bool RegionSelector::start(mgl::Color border_color) {
|
||||
bool RegionSelector::start(SelectionType selection_type, mgl::Color border_color) {
|
||||
if(dpy)
|
||||
return false;
|
||||
|
||||
@@ -328,11 +375,20 @@ namespace gsr {
|
||||
hide_window_from_taskbar(dpy, cursor_window);
|
||||
}
|
||||
|
||||
draw_rectangle_around_selected_monitor(dpy, region_window, region_gc, region_border_size, is_wayland, monitors, cursor_pos);
|
||||
windows = query_windows(dpy);
|
||||
|
||||
if(selection_type == SelectionType::WINDOW) {
|
||||
focused_window = get_window_by_position(windows, cursor_pos);
|
||||
if(focused_window)
|
||||
draw_rectangle_or_region(dpy, region_window, region_gc, region_border_size, is_wayland, focused_window->pos, focused_window->size);
|
||||
} else if(selection_type == SelectionType::REGION) {
|
||||
draw_rectangle_around_selected_monitor(dpy, region_window, region_gc, region_border_size, is_wayland, monitors, cursor_pos);
|
||||
}
|
||||
|
||||
XFlush(dpy);
|
||||
selected = false;
|
||||
canceled = false;
|
||||
this->selection_type = selection_type;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -375,6 +431,8 @@ namespace gsr {
|
||||
XCloseDisplay(dpy);
|
||||
dpy = nullptr;
|
||||
selecting_region = false;
|
||||
monitors.clear();
|
||||
windows.clear();
|
||||
}
|
||||
|
||||
bool RegionSelector::is_started() const {
|
||||
@@ -441,20 +499,35 @@ namespace gsr {
|
||||
return result;
|
||||
}
|
||||
|
||||
Region RegionSelector::get_selection(Display *x11_dpy, struct wl_display *wayland_dpy) const {
|
||||
Region RegionSelector::get_region_selection(Display *x11_dpy, struct wl_display *wayland_dpy) const {
|
||||
assert(selection_type == SelectionType::REGION);
|
||||
Region returned_region = region;
|
||||
if(is_wayland && x11_dpy && wayland_dpy)
|
||||
returned_region = x11_region_to_wayland_region(x11_dpy, wayland_dpy, returned_region);
|
||||
return returned_region;
|
||||
}
|
||||
|
||||
Window RegionSelector::get_window_selection() const {
|
||||
assert(selection_type == SelectionType::WINDOW);
|
||||
if(focused_window)
|
||||
return focused_window->window;
|
||||
else
|
||||
return None;
|
||||
}
|
||||
|
||||
RegionSelector::SelectionType RegionSelector::get_selection_type() const {
|
||||
return selection_type;
|
||||
}
|
||||
|
||||
void RegionSelector::on_button_press(const void *de) {
|
||||
const XIDeviceEvent *device_event = (XIDeviceEvent*)de;
|
||||
if(device_event->detail != Button1)
|
||||
return;
|
||||
|
||||
region.pos = { (int)device_event->root_x, (int)device_event->root_y };
|
||||
selecting_region = true;
|
||||
if(selection_type == SelectionType::REGION) {
|
||||
region.pos = { (int)device_event->root_x, (int)device_event->root_y };
|
||||
selecting_region = true;
|
||||
}
|
||||
}
|
||||
|
||||
void RegionSelector::on_button_release(const void *de) {
|
||||
@@ -462,8 +535,23 @@ namespace gsr {
|
||||
if(device_event->detail != Button1)
|
||||
return;
|
||||
|
||||
if(!selecting_region)
|
||||
return;
|
||||
if(selection_type == SelectionType::WINDOW) {
|
||||
focused_window = get_window_by_position(windows, mgl::vec2i(device_event->root_x, device_event->root_y));
|
||||
if(focused_window) {
|
||||
const Window real_window = window_get_target_window_child(dpy, focused_window->window);
|
||||
XWindowAttributes win_attr;
|
||||
if(XGetWindowAttributes(dpy, real_window, &win_attr)) {
|
||||
focused_window = RegionWindow{
|
||||
real_window,
|
||||
mgl::vec2i(win_attr.x, win_attr.y),
|
||||
mgl::vec2i(win_attr.width, win_attr.height)
|
||||
};
|
||||
}
|
||||
}
|
||||
} else if(selection_type == SelectionType::REGION) {
|
||||
if(!selecting_region)
|
||||
return;
|
||||
}
|
||||
|
||||
if(is_wayland) {
|
||||
XClearWindow(dpy, region_window);
|
||||
@@ -473,7 +561,11 @@ namespace gsr {
|
||||
}
|
||||
selecting_region = false;
|
||||
|
||||
cursor_pos = region.pos + region.size;
|
||||
if(selection_type == SelectionType::WINDOW) {
|
||||
cursor_pos = { (int)device_event->root_x, (int)device_event->root_y };
|
||||
} else if(selection_type == SelectionType::REGION) {
|
||||
cursor_pos = region.pos + region.size;
|
||||
}
|
||||
|
||||
if(region.size.x < 0) {
|
||||
region.pos.x += region.size.x;
|
||||
@@ -497,19 +589,26 @@ namespace gsr {
|
||||
void RegionSelector::on_mouse_motion(const void *de) {
|
||||
const XIDeviceEvent *device_event = (XIDeviceEvent*)de;
|
||||
XClearWindow(dpy, region_window);
|
||||
|
||||
if(selecting_region) {
|
||||
region.size.x = device_event->root_x - region.pos.x;
|
||||
region.size.y = device_event->root_y - region.pos.y;
|
||||
cursor_pos = region.pos + region.size;
|
||||
|
||||
if(is_wayland)
|
||||
draw_rectangle(dpy, region_window, region_gc, region.pos.x, region.pos.y, region.size.x, region.size.y);
|
||||
draw_rectangle_or_region(dpy, region_window, region_gc, region_border_size, is_wayland, region.pos, region.size);
|
||||
} else if(selection_type == SelectionType::WINDOW) {
|
||||
cursor_pos = { (int)device_event->root_x, (int)device_event->root_y };
|
||||
|
||||
focused_window = get_window_by_position(windows, cursor_pos);
|
||||
if(focused_window)
|
||||
draw_rectangle_or_region(dpy, region_window, region_gc, region_border_size, is_wayland, focused_window->pos, focused_window->size);
|
||||
else
|
||||
set_region_rectangle(dpy, region_window, region.pos.x, region.pos.y, region.size.x, region.size.y, region_border_size);
|
||||
} else {
|
||||
draw_rectangle_or_region(dpy, region_window, region_gc, region_border_size, is_wayland, mgl::vec2i(0, 0), mgl::vec2i(0, 0));
|
||||
} else if(selection_type == SelectionType::REGION) {
|
||||
cursor_pos = { (int)device_event->root_x, (int)device_event->root_y };
|
||||
draw_rectangle_around_selected_monitor(dpy, region_window, region_gc, region_border_size, is_wayland, monitors, cursor_pos);
|
||||
}
|
||||
|
||||
update_cursor_window(dpy, region_window, cursor_window, is_wayland, cursor_pos.x, cursor_pos.y, cursor_window_size, cursor_thickness, cursor_gc);
|
||||
XFlush(dpy);
|
||||
}
|
||||
|
||||
@@ -184,6 +184,8 @@ namespace gsr {
|
||||
--num_polls;
|
||||
--i;
|
||||
}
|
||||
|
||||
polls[i].revents = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#include "../include/Translation.hpp"
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <unordered_map>
|
||||
#include <fstream>
|
||||
|
||||
@@ -31,13 +30,6 @@ namespace gsr {
|
||||
return "en";
|
||||
}
|
||||
|
||||
std::string Translation::trim(const std::string& str) {
|
||||
size_t start = str.find_first_not_of(" \t\r\n");
|
||||
if (start == std::string::npos) return "";
|
||||
size_t end = str.find_last_not_of(" \t\r\n");
|
||||
return str.substr(start, end - start + 1);
|
||||
}
|
||||
|
||||
void Translation::process_escapes(std::string& str) {
|
||||
size_t pos = 0;
|
||||
while ((pos = str.find("\\n", pos)) != std::string::npos) {
|
||||
@@ -68,8 +60,9 @@ namespace gsr {
|
||||
bool Translation::load_language(const char* lang) {
|
||||
translations.clear();
|
||||
|
||||
if(lang[0] == '\0')
|
||||
lang = "en";
|
||||
const std::string system_language = get_system_language();
|
||||
if(!lang || lang[0] == '\0')
|
||||
lang = system_language.c_str();
|
||||
|
||||
if (!is_language_supported(lang)) {
|
||||
fprintf(stderr, "Warning: language '%s' is not supported\n", lang);
|
||||
@@ -93,8 +86,6 @@ namespace gsr {
|
||||
|
||||
std::string line;
|
||||
while (std::getline(file, line)) {
|
||||
line = trim(line);
|
||||
|
||||
if (line.empty() || line[0] == '#') continue;
|
||||
|
||||
size_t eq_pos = line.find('=');
|
||||
@@ -124,9 +115,12 @@ namespace gsr {
|
||||
}
|
||||
|
||||
void Translation::init(const char* translations_directory, const char* initial_language) {
|
||||
if(initial_language && initial_language[0] == '\0')
|
||||
initial_language = nullptr;
|
||||
|
||||
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() {
|
||||
|
||||
134
src/Utils.cpp
134
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,77 @@ 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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,236 +0,0 @@
|
||||
#include "../include/WindowSelector.hpp"
|
||||
#include "../include/WindowUtils.hpp"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <X11/Xatom.h>
|
||||
#include <X11/extensions/shape.h>
|
||||
#include <X11/cursorfont.h>
|
||||
#include <X11/keysym.h>
|
||||
|
||||
namespace gsr {
|
||||
static const int rectangle_border_size = 2;
|
||||
|
||||
static int max_int(int a, int b) {
|
||||
return a >= b ? a : b;
|
||||
}
|
||||
|
||||
static void set_region_rectangle(Display *dpy, Window window, int x, int y, int width, int height, int border_size) {
|
||||
if(width < 0) {
|
||||
x += width;
|
||||
width = abs(width);
|
||||
}
|
||||
|
||||
if(height < 0) {
|
||||
y += height;
|
||||
height = abs(height);
|
||||
}
|
||||
|
||||
XRectangle rectangles[] = {
|
||||
{
|
||||
(short)max_int(0, x), (short)max_int(0, y),
|
||||
(unsigned short)max_int(0, border_size), (unsigned short)max_int(0, height)
|
||||
}, // Left
|
||||
{
|
||||
(short)max_int(0, x + width - border_size), (short)max_int(0, y),
|
||||
(unsigned short)max_int(0, border_size), (unsigned short)max_int(0, height)
|
||||
}, // Right
|
||||
{
|
||||
(short)max_int(0, x + border_size), (short)max_int(0, y),
|
||||
(unsigned short)max_int(0, width - border_size*2), (unsigned short)max_int(0, border_size)
|
||||
}, // Top
|
||||
{
|
||||
(short)max_int(0, x + border_size), (short)max_int(0, y + height - border_size),
|
||||
(unsigned short)max_int(0, width - border_size*2), (unsigned short)max_int(0, border_size)
|
||||
}, // Bottom
|
||||
};
|
||||
XShapeCombineRectangles(dpy, window, ShapeBounding, 0, 0, rectangles, 4, ShapeSet, Unsorted);
|
||||
XFlush(dpy);
|
||||
}
|
||||
|
||||
static unsigned long mgl_color_to_x11_color(mgl::Color color) {
|
||||
if(color.a == 0)
|
||||
return 0;
|
||||
return ((uint32_t)color.a << 24) | (((uint32_t)color.r * color.a / 0xFF) << 16) | (((uint32_t)color.g * color.a / 0xFF) << 8) | ((uint32_t)color.b * color.a / 0xFF);
|
||||
}
|
||||
|
||||
static Window get_cursor_window(Display *dpy) {
|
||||
Window root_window = None;
|
||||
Window window = None;
|
||||
int dummy_i;
|
||||
unsigned int dummy_u;
|
||||
mgl::vec2i root_pos;
|
||||
XQueryPointer(dpy, DefaultRootWindow(dpy), &root_window, &window, &root_pos.x, &root_pos.y, &dummy_i, &dummy_i, &dummy_u);
|
||||
return window;
|
||||
}
|
||||
|
||||
static void get_window_geometry(Display *dpy, Window window, mgl::vec2i &pos, mgl::vec2i &size) {
|
||||
Window root_window;
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
unsigned int w = 0;
|
||||
unsigned int h = 0;
|
||||
unsigned int dummy_border, dummy_depth;
|
||||
XGetGeometry(dpy, window, &root_window, &x, &y, &w, &h, &dummy_border, &dummy_depth);
|
||||
pos.x = x;
|
||||
pos.y = y;
|
||||
size.x = w;
|
||||
size.y = h;
|
||||
}
|
||||
|
||||
WindowSelector::WindowSelector() {
|
||||
|
||||
}
|
||||
|
||||
WindowSelector::~WindowSelector() {
|
||||
stop();
|
||||
}
|
||||
|
||||
bool WindowSelector::start(mgl::Color border_color) {
|
||||
if(dpy)
|
||||
return false;
|
||||
|
||||
const unsigned long border_color_x11 = mgl_color_to_x11_color(border_color);
|
||||
dpy = XOpenDisplay(nullptr);
|
||||
if(!dpy) {
|
||||
fprintf(stderr, "Error: WindowSelector::start: failed to connect to the X11 server\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
const Window cursor_window = get_cursor_window(dpy);
|
||||
mgl::vec2i cursor_window_pos, cursor_window_size;
|
||||
get_window_geometry(dpy, cursor_window, cursor_window_pos, cursor_window_size);
|
||||
|
||||
XVisualInfo vinfo;
|
||||
memset(&vinfo, 0, sizeof(vinfo));
|
||||
XMatchVisualInfo(dpy, DefaultScreen(dpy), 32, TrueColor, &vinfo);
|
||||
border_window_colormap = XCreateColormap(dpy, DefaultRootWindow(dpy), vinfo.visual, AllocNone);
|
||||
|
||||
XSetWindowAttributes window_attr;
|
||||
window_attr.background_pixel = border_color_x11;
|
||||
window_attr.border_pixel = 0;
|
||||
window_attr.override_redirect = true;
|
||||
window_attr.event_mask = StructureNotifyMask | PointerMotionMask | ButtonPressMask | ButtonReleaseMask;
|
||||
window_attr.colormap = border_window_colormap;
|
||||
|
||||
Screen *screen = XDefaultScreenOfDisplay(dpy);
|
||||
border_window = XCreateWindow(dpy, DefaultRootWindow(dpy), 0, 0, XWidthOfScreen(screen), XHeightOfScreen(screen), 0,
|
||||
vinfo.depth, InputOutput, vinfo.visual, CWBackPixel | CWBorderPixel | CWOverrideRedirect | CWEventMask | CWColormap, &window_attr);
|
||||
if(!border_window) {
|
||||
fprintf(stderr, "Error: WindowSelector::start: failed to create region window\n");
|
||||
stop();
|
||||
return false;
|
||||
}
|
||||
set_window_size_not_resizable(dpy, border_window, XWidthOfScreen(screen), XHeightOfScreen(screen));
|
||||
|
||||
unsigned char data = 2; // Prefer being composed to allow transparency. Do this to prevent the compositor from getting turned on/off when taking a screenshot
|
||||
XChangeProperty(dpy, border_window, XInternAtom(dpy, "_NET_WM_BYPASS_COMPOSITOR", False), XA_CARDINAL, 32, PropModeReplace, &data, 1);
|
||||
|
||||
if(cursor_window && cursor_window != DefaultRootWindow(dpy))
|
||||
set_region_rectangle(dpy, border_window, cursor_window_pos.x, cursor_window_pos.y, cursor_window_size.x, cursor_window_size.y, rectangle_border_size);
|
||||
else
|
||||
set_region_rectangle(dpy, border_window, 0, 0, 0, 0, 0);
|
||||
make_window_click_through(dpy, border_window);
|
||||
XMapWindow(dpy, border_window);
|
||||
|
||||
crosshair_cursor = XCreateFontCursor(dpy, XC_crosshair);
|
||||
XGrabPointer(dpy, DefaultRootWindow(dpy), True, PointerMotionMask | ButtonPressMask | ButtonReleaseMask | ButtonMotionMask, GrabModeAsync, GrabModeAsync, None, crosshair_cursor, CurrentTime);
|
||||
XGrabKeyboard(dpy, DefaultRootWindow(dpy), True, GrabModeAsync, GrabModeAsync, CurrentTime);
|
||||
XFlush(dpy);
|
||||
|
||||
selected = false;
|
||||
canceled = false;
|
||||
selected_window = None;
|
||||
return true;
|
||||
}
|
||||
|
||||
void WindowSelector::stop() {
|
||||
if(!dpy)
|
||||
return;
|
||||
|
||||
XUngrabPointer(dpy, CurrentTime);
|
||||
XUngrabKeyboard(dpy, CurrentTime);
|
||||
|
||||
if(border_window_colormap) {
|
||||
XFreeColormap(dpy, border_window_colormap);
|
||||
border_window_colormap = 0;
|
||||
}
|
||||
|
||||
if(border_window) {
|
||||
XDestroyWindow(dpy, border_window);
|
||||
border_window = 0;
|
||||
}
|
||||
|
||||
if(crosshair_cursor) {
|
||||
XFreeCursor(dpy, crosshair_cursor);
|
||||
crosshair_cursor = None;
|
||||
}
|
||||
|
||||
XFlush(dpy);
|
||||
XSync(dpy, False);
|
||||
|
||||
XCloseDisplay(dpy);
|
||||
dpy = nullptr;
|
||||
}
|
||||
|
||||
bool WindowSelector::is_started() const {
|
||||
return dpy != nullptr;
|
||||
}
|
||||
|
||||
bool WindowSelector::failed() const {
|
||||
return !dpy;
|
||||
}
|
||||
|
||||
bool WindowSelector::poll_events() {
|
||||
if(!dpy || selected)
|
||||
return false;
|
||||
|
||||
XEvent xev;
|
||||
while(XPending(dpy)) {
|
||||
XNextEvent(dpy, &xev);
|
||||
|
||||
if(xev.type == MotionNotify) {
|
||||
const Window motion_window = xev.xmotion.subwindow;
|
||||
mgl::vec2i motion_window_pos, motion_window_size;
|
||||
get_window_geometry(dpy, motion_window, motion_window_pos, motion_window_size);
|
||||
if(motion_window && motion_window != DefaultRootWindow(dpy))
|
||||
set_region_rectangle(dpy, border_window, motion_window_pos.x, motion_window_pos.y, motion_window_size.x, motion_window_size.y, rectangle_border_size);
|
||||
else
|
||||
set_region_rectangle(dpy, border_window, 0, 0, 0, 0, 0);
|
||||
XFlush(dpy);
|
||||
} else if(xev.type == ButtonRelease && xev.xbutton.button == Button1) {
|
||||
selected_window = xev.xbutton.subwindow;
|
||||
const Window clicked_window_real = window_get_target_window_child(dpy, selected_window);
|
||||
if(clicked_window_real)
|
||||
selected_window = clicked_window_real;
|
||||
selected = true;
|
||||
|
||||
stop();
|
||||
break;
|
||||
} else if(xev.type == KeyRelease && XKeycodeToKeysym(dpy, xev.xkey.keycode, 0) == XK_Escape) {
|
||||
canceled = true;
|
||||
selected = false;
|
||||
stop();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WindowSelector::take_selection() {
|
||||
const bool result = selected;
|
||||
selected = false;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool WindowSelector::take_canceled() {
|
||||
const bool result = canceled;
|
||||
canceled = false;
|
||||
return result;
|
||||
}
|
||||
|
||||
Window WindowSelector::get_selection() const {
|
||||
return selected_window;
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <wayland-client.h>
|
||||
#include "xdg-output-unstable-v1-client-protocol.h"
|
||||
|
||||
#include <mglpp/system/Rect.hpp>
|
||||
#include <mglpp/system/Utf8.hpp>
|
||||
|
||||
extern "C" {
|
||||
@@ -229,7 +230,7 @@ namespace gsr {
|
||||
wl_display_roundtrip(dpy);
|
||||
}
|
||||
|
||||
static unsigned char* window_get_property(Display *dpy, Window window, Atom property_type, const char *property_name, unsigned int *property_size) {
|
||||
unsigned char* window_get_property(Display *dpy, Window window, Atom property_type, const char *property_name, unsigned int *property_size) {
|
||||
Atom ret_property_type = None;
|
||||
int ret_format = 0;
|
||||
unsigned long num_items = 0;
|
||||
@@ -269,6 +270,30 @@ namespace gsr {
|
||||
return window_has_atom(dpy, window, net_wm_state_atom) || window_has_atom(dpy, window, wm_state_atom);
|
||||
}
|
||||
|
||||
static Window get_window_graphics_parent(Display *dpy, Window window) {
|
||||
if(window == DefaultRootWindow(dpy) || window == None)
|
||||
return window;
|
||||
|
||||
XWindowAttributes attr;
|
||||
memset(&attr, 0, sizeof(attr));
|
||||
XGetWindowAttributes(dpy, window, &attr);
|
||||
if(attr.override_redirect || attr.c_class != InputOutput || attr.map_state != IsViewable || !window_is_user_program(dpy, window)) {
|
||||
Window root;
|
||||
Window parent;
|
||||
Window *children = nullptr;
|
||||
unsigned int num_children = 0;
|
||||
if(!XQueryTree(dpy, window, &root, &parent, &children, &num_children))
|
||||
return None;
|
||||
|
||||
if(children)
|
||||
XFree(children);
|
||||
|
||||
if(parent)
|
||||
return get_window_graphics_parent(dpy, parent);
|
||||
}
|
||||
return window;
|
||||
}
|
||||
|
||||
Window window_get_target_window_child(Display *display, Window window) {
|
||||
if(window == None)
|
||||
return None;
|
||||
@@ -284,14 +309,14 @@ namespace gsr {
|
||||
return None;
|
||||
|
||||
Window found_window = None;
|
||||
for(int i = num_children - 1; i >= 0; --i) {
|
||||
for(int i = (int)num_children - 1; i >= 0; --i) {
|
||||
if(children[i] && window_is_user_program(display, children[i])) {
|
||||
found_window = children[i];
|
||||
goto finished;
|
||||
}
|
||||
}
|
||||
|
||||
for(int i = num_children - 1; i >= 0; --i) {
|
||||
for(int i = (int)num_children - 1; i >= 0; --i) {
|
||||
if(children[i]) {
|
||||
Window win = window_get_target_window_child(display, children[i]);
|
||||
if(win) {
|
||||
@@ -350,7 +375,9 @@ namespace gsr {
|
||||
|
||||
int revert_to = 0;
|
||||
XGetInputFocus(dpy, &focused_window, &revert_to);
|
||||
if(focused_window && focused_window != DefaultRootWindow(dpy) && window_is_user_program(dpy, focused_window))
|
||||
focused_window = get_window_graphics_parent(dpy, focused_window);
|
||||
|
||||
if(focused_window && focused_window != DefaultRootWindow(dpy))
|
||||
return focused_window;
|
||||
|
||||
if(!fallback_cursor_focused)
|
||||
@@ -893,6 +920,36 @@ namespace gsr {
|
||||
return is_fullscreen;
|
||||
}
|
||||
|
||||
bool get_drawable_geometry(Display *display, Drawable drawable, DrawableGeometry *geometry) {
|
||||
geometry->x = 0;
|
||||
geometry->y = 0;
|
||||
geometry->width = 0;
|
||||
geometry->height = 0;
|
||||
|
||||
Window root_window;
|
||||
unsigned int w, h;
|
||||
unsigned int dummy_border, dummy_depth;
|
||||
Status s = XGetGeometry(display, drawable, &root_window, &geometry->x, &geometry->y, &w, &h, &dummy_border, &dummy_depth);
|
||||
|
||||
geometry->width = w;
|
||||
geometry->height = h;
|
||||
return s == True;
|
||||
}
|
||||
|
||||
std::optional<Monitor> get_monitor_by_window_center(Display *display, Window window) {
|
||||
DrawableGeometry geometry;
|
||||
if(!get_drawable_geometry(display, window, &geometry))
|
||||
return std::nullopt;
|
||||
|
||||
const mgl::vec2i window_center = mgl::vec2i(geometry.x, geometry.y) + mgl::vec2i(geometry.width, geometry.height) / 2;
|
||||
auto monitors = get_monitors(display);
|
||||
for(auto &monitor : monitors) {
|
||||
if(mgl::IntRect(monitor.position, monitor.size).contains(window_center))
|
||||
return std::move(monitor);
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
#define _NET_WM_STATE_REMOVE 0
|
||||
#define _NET_WM_STATE_ADD 1
|
||||
#define _NET_WM_STATE_TOGGLE 2
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <mglpp/window/Event.hpp>
|
||||
#include <mglpp/system/FloatRect.hpp>
|
||||
|
||||
#include <limits.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/stat.h>
|
||||
#include <errno.h>
|
||||
@@ -296,4 +297,4 @@ namespace gsr {
|
||||
const std::string& 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"
|
||||
@@ -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;
|
||||
@@ -528,6 +527,9 @@ namespace gsr {
|
||||
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("Русский", "ru");
|
||||
combo_box->add_item("Українська", "uk");
|
||||
combo_box->on_selection_changed = [](const std::string&, const std::string &id) {
|
||||
@@ -631,10 +633,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);
|
||||
|
||||
@@ -211,7 +211,7 @@ 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 focused applications name"), supports_window_title ? "" : " (X11 applications only)");
|
||||
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);
|
||||
save_screenshot_in_game_folder_checkbox_ptr = checkbox.get();
|
||||
return checkbox;
|
||||
|
||||
@@ -37,13 +37,14 @@ namespace gsr {
|
||||
return "";
|
||||
}
|
||||
|
||||
SettingsPage::SettingsPage(Type type, const GsrInfo *gsr_info, Config &config, PageStack *page_stack, bool supports_window_title) :
|
||||
SettingsPage::SettingsPage(Type type, const GsrInfo *gsr_info, Config &config, PageStack *page_stack, bool supports_window_title, bool supports_window_fullscreen_state) :
|
||||
StaticPage(mgl::vec2f(get_theme().window_width, get_theme().window_height).floor()),
|
||||
type(type),
|
||||
config(config),
|
||||
gsr_info(gsr_info),
|
||||
page_stack(page_stack),
|
||||
supports_window_title(supports_window_title)
|
||||
supports_window_title(supports_window_title),
|
||||
supports_window_fullscreen_state(supports_window_fullscreen_state)
|
||||
{
|
||||
audio_devices = get_audio_devices();
|
||||
application_audio = get_application_audio();
|
||||
@@ -231,21 +232,10 @@ namespace gsr {
|
||||
if(!it->mjpeg_setups.empty())
|
||||
webcam_video_format_box_ptr->add_item(TR("Motion-JPEG"), "mjpeg");
|
||||
|
||||
webcam_video_format_box_ptr->set_selected_item("auto");
|
||||
webcam_video_format_box_ptr->set_selected_item(get_current_record_options().webcam_video_format);
|
||||
const std::string prev_webcam_video_format = get_current_record_options().webcam_video_format;
|
||||
webcam_video_format_box_ptr->set_selected_item(webcam_video_format_box_ptr->get_selected_id());
|
||||
webcam_video_format_box_ptr->set_selected_item(prev_webcam_video_format);
|
||||
selected_camera = *it;
|
||||
|
||||
// TODO: Set from config
|
||||
if(webcam_video_format_box_ptr->get_selected_id() == "yuyv" && !it->yuyv_setups.empty())
|
||||
selected_camera_setup = selected_camera->yuyv_setups.front();
|
||||
else if(webcam_video_format_box_ptr->get_selected_id() == "mjpeg" && !it->mjpeg_setups.empty())
|
||||
selected_camera_setup = selected_camera->mjpeg_setups.front();
|
||||
else if(webcam_video_format_box_ptr->get_selected_id() == "auto") {
|
||||
if(!it->mjpeg_setups.empty())
|
||||
selected_camera_setup = selected_camera->mjpeg_setups.front();
|
||||
else if(!it->yuyv_setups.empty())
|
||||
selected_camera_setup = selected_camera->yuyv_setups.front();
|
||||
}
|
||||
};
|
||||
|
||||
ll->add_widget(std::move(combobox));
|
||||
@@ -259,6 +249,20 @@ namespace gsr {
|
||||
auto combobox = std::make_unique<ComboBox>(&get_theme().body_font);
|
||||
webcam_video_setup_box_ptr = combobox.get();
|
||||
|
||||
webcam_video_setup_box_ptr->on_selection_changed = [this](const std::string&, const std::string &id) {
|
||||
int camera_width = 0;
|
||||
int camera_height = 0;
|
||||
int camera_fps = 0;
|
||||
sscanf(id.c_str(), "%dx%d@%dhz", &camera_width, &camera_height, &camera_fps);
|
||||
|
||||
RecordOptions ¤t_record_options = get_current_record_options();
|
||||
current_record_options.webcam_camera_width = camera_width;
|
||||
current_record_options.webcam_camera_height = camera_height;
|
||||
current_record_options.webcam_camera_fps = camera_fps;
|
||||
|
||||
selected_camera_setup = GsrCameraSetup{mgl::vec2i{camera_width, camera_height}, camera_fps};
|
||||
};
|
||||
|
||||
ll->add_widget(std::move(combobox));
|
||||
return ll;
|
||||
}
|
||||
@@ -315,6 +319,12 @@ namespace gsr {
|
||||
webcam_video_setup_box_ptr->add_item(setup_str, setup_str, false);
|
||||
}
|
||||
}
|
||||
|
||||
const RecordOptions ¤t_record_options = get_current_record_options();
|
||||
char webcam_setup_str[256];
|
||||
snprintf(webcam_setup_str, sizeof(webcam_setup_str), "%dx%d@%dhz", current_record_options.webcam_camera_width, current_record_options.webcam_camera_height, current_record_options.webcam_camera_fps);
|
||||
webcam_video_setup_box_ptr->set_selected_item(webcam_video_setup_box_ptr->get_selected_id());
|
||||
webcam_video_setup_box_ptr->set_selected_item(webcam_setup_str);
|
||||
};
|
||||
|
||||
ll->add_widget(std::move(combobox));
|
||||
@@ -1116,9 +1126,9 @@ namespace gsr {
|
||||
}
|
||||
|
||||
std::unique_ptr<RadioButton> SettingsPage::create_start_replay_automatically() {
|
||||
// TODO: Support kde plasma wayland and hyprland (same ones that support getting window title)
|
||||
// TODO: Support hyprland (same ones that support getting window title)
|
||||
char fullscreen_text[256];
|
||||
snprintf(fullscreen_text, sizeof(fullscreen_text), TR("Turn on replay when starting a fullscreen application%s"), gsr_info->system_info.display_server == DisplayServer::X11 ? "" : " (X11 applications only)");
|
||||
snprintf(fullscreen_text, sizeof(fullscreen_text), TR("Turn on replay when starting a fullscreen application%s"), supports_window_fullscreen_state ? "" : TR(" (X11 applications only)"));
|
||||
|
||||
auto radiobutton = std::make_unique<RadioButton>(&get_theme().body_font, RadioButton::Orientation::VERTICAL);
|
||||
radiobutton->add_item(TR("Don't turn on replay automatically"), "dont_turn_on_automatically");
|
||||
@@ -1131,7 +1141,7 @@ namespace gsr {
|
||||
|
||||
std::unique_ptr<CheckBox> SettingsPage::create_save_replay_in_game_folder() {
|
||||
char text[256];
|
||||
snprintf(text, sizeof(text), TR("Save video in a folder based on the focused applications name%s"), supports_window_title ? "" : " (X11 applications only)");
|
||||
snprintf(text, sizeof(text), TR("Save video in a folder based on the games name%s"), supports_window_title ? "" : TR(" (X11 applications only)"));
|
||||
auto checkbox = std::make_unique<CheckBox>(&get_theme().body_font, text);
|
||||
save_replay_in_game_folder_ptr = checkbox.get();
|
||||
return checkbox;
|
||||
@@ -1144,7 +1154,7 @@ namespace gsr {
|
||||
}
|
||||
|
||||
std::unique_ptr<Label> SettingsPage::create_estimated_replay_file_size() {
|
||||
auto label = std::make_unique<Label>(&get_theme().body_font, TR("Estimated video max file size in RAM: 57.60MB"), get_color_theme().text_color);
|
||||
auto label = std::make_unique<Label>(&get_theme().body_font, "Estimated video max file size in RAM: 57.60MB", get_color_theme().text_color);
|
||||
estimated_file_size_ptr = label.get();
|
||||
return label;
|
||||
}
|
||||
@@ -1194,7 +1204,7 @@ namespace gsr {
|
||||
|
||||
std::unique_ptr<CheckBox> SettingsPage::create_led_indicator(const char *type) {
|
||||
char label_str[256];
|
||||
snprintf(label_str, sizeof(label_str), TR("Show %s status with scroll lock led"), type);
|
||||
snprintf(label_str, sizeof(label_str), TR("Show %s status with scroll lock LED"), type);
|
||||
|
||||
auto checkbox = std::make_unique<CheckBox>(&get_theme().body_font, label_str);
|
||||
checkbox->set_checked(false);
|
||||
@@ -1263,7 +1273,7 @@ namespace gsr {
|
||||
general_list->add_widget(create_low_power_mode());
|
||||
|
||||
settings_list_ptr->add_widget(std::make_unique<Subsection>(TR("General"), std::move(general_list), mgl::vec2f(settings_scrollable_page_ptr->get_inner_size().x, 0.0f)));
|
||||
settings_list_ptr->add_widget(std::make_unique<Subsection>(TR("Replay indicator"), create_indicator("replay"), mgl::vec2f(settings_scrollable_page_ptr->get_inner_size().x, 0.0f)));
|
||||
settings_list_ptr->add_widget(std::make_unique<Subsection>(TR("Replay indicator"), create_indicator(TR("replay")), mgl::vec2f(settings_scrollable_page_ptr->get_inner_size().x, 0.0f)));
|
||||
settings_list_ptr->add_widget(std::make_unique<Subsection>(TR("Autostart"), create_start_replay_automatically(), mgl::vec2f(settings_scrollable_page_ptr->get_inner_size().x, 0.0f)));
|
||||
|
||||
view_radio_button_ptr->on_selection_changed = [this](const std::string&, const std::string &id) {
|
||||
@@ -1284,14 +1294,14 @@ namespace gsr {
|
||||
|
||||
std::unique_ptr<CheckBox> SettingsPage::create_save_recording_in_game_folder() {
|
||||
char text[256];
|
||||
snprintf(text, sizeof(text), TR("Save video in a folder based on the focused applications name%s"), supports_window_title ? "" : " (X11 applications only)");
|
||||
snprintf(text, sizeof(text), TR("Save video in a folder based on the games name%s"), supports_window_title ? "" : TR(" (X11 applications only)"));
|
||||
auto checkbox = std::make_unique<CheckBox>(&get_theme().body_font, text);
|
||||
save_recording_in_game_folder_ptr = checkbox.get();
|
||||
return checkbox;
|
||||
}
|
||||
|
||||
std::unique_ptr<Label> SettingsPage::create_estimated_record_file_size() {
|
||||
auto label = std::make_unique<Label>(&get_theme().body_font, TR("Estimated video file size per minute (excluding audio): 345.60MB"), get_color_theme().text_color);
|
||||
auto label = std::make_unique<Label>(&get_theme().body_font, "Estimated video file size per minute (excluding audio): 345.60MB", get_color_theme().text_color);
|
||||
estimated_file_size_ptr = label.get();
|
||||
return label;
|
||||
}
|
||||
@@ -1320,7 +1330,7 @@ namespace gsr {
|
||||
general_list->add_widget(create_low_power_mode());
|
||||
|
||||
settings_list_ptr->add_widget(std::make_unique<Subsection>(TR("General"), std::move(general_list), mgl::vec2f(settings_scrollable_page_ptr->get_inner_size().x, 0.0f)));
|
||||
settings_list_ptr->add_widget(std::make_unique<Subsection>(TR("Recording indicator"), create_indicator("recording"), mgl::vec2f(settings_scrollable_page_ptr->get_inner_size().x, 0.0f)));
|
||||
settings_list_ptr->add_widget(std::make_unique<Subsection>(TR("Recording indicator"), create_indicator(TR("recording")), mgl::vec2f(settings_scrollable_page_ptr->get_inner_size().x, 0.0f)));
|
||||
|
||||
view_radio_button_ptr->on_selection_changed = [this](const std::string&, const std::string &id) {
|
||||
view_changed(id == "advanced");
|
||||
@@ -1453,7 +1463,7 @@ namespace gsr {
|
||||
general_list->add_widget(create_low_power_mode());
|
||||
|
||||
settings_list_ptr->add_widget(std::make_unique<Subsection>(TR("General"), std::move(general_list), mgl::vec2f(settings_scrollable_page_ptr->get_inner_size().x, 0.0f)));
|
||||
settings_list_ptr->add_widget(std::make_unique<Subsection>(TR("Streaming indicator"), create_indicator("streaming"), mgl::vec2f(settings_scrollable_page_ptr->get_inner_size().x, 0.0f)));
|
||||
settings_list_ptr->add_widget(std::make_unique<Subsection>(TR("Streaming indicator"), create_indicator(TR("streaming")), mgl::vec2f(settings_scrollable_page_ptr->get_inner_size().x, 0.0f)));
|
||||
|
||||
streaming_service_box_ptr->on_selection_changed = [this](const std::string&, const std::string &id) {
|
||||
const bool twitch_option = id == "twitch";
|
||||
@@ -1755,17 +1765,8 @@ namespace gsr {
|
||||
if(selected_camera_setup.has_value())
|
||||
webcam_box_size = clamp_keep_aspect_ratio(selected_camera_setup->resolution.to_vec2f(), webcam_box_size);
|
||||
|
||||
int camera_width = 0;
|
||||
int camera_height = 0;
|
||||
int camera_fps = 0;
|
||||
sscanf(webcam_video_setup_box_ptr->get_selected_id().c_str(), "%dx%d@%dhz", &camera_width, &camera_height, &camera_fps);
|
||||
|
||||
record_options.webcam_source = webcam_sources_box_ptr->get_selected_id();
|
||||
record_options.webcam_flip_horizontally = flip_camera_horizontally_checkbox_ptr->is_checked();
|
||||
record_options.webcam_video_format = webcam_video_format_box_ptr->get_selected_id();
|
||||
record_options.webcam_camera_width = camera_width;
|
||||
record_options.webcam_camera_height = camera_height;
|
||||
record_options.webcam_camera_fps = camera_fps;
|
||||
record_options.webcam_x = std::round((webcam_box_pos.x / screen_inner_size.x) * 100.0f);
|
||||
record_options.webcam_y = std::round((webcam_box_pos.y / screen_inner_size.y) * 100.0f);
|
||||
record_options.webcam_width = std::round((webcam_box_size.x / screen_inner_size.x) * 100.0f);
|
||||
|
||||
84
src/main.cpp
84
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"
|
||||
@@ -10,6 +11,7 @@
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
#include <malloc.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <mglpp/mglpp.hpp>
|
||||
#include <mglpp/system/Clock.hpp>
|
||||
@@ -116,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)
|
||||
@@ -168,7 +129,7 @@ static void set_display_server_environment_variables() {
|
||||
|
||||
const char *wayland_display = getenv("WAYLAND_DISPLAY");
|
||||
if(!wayland_display) {
|
||||
wayland_display = "wayland-1";
|
||||
wayland_display = "wayland-0";
|
||||
setenv("WAYLAND_DISPLAY", wayland_display, true);
|
||||
}
|
||||
}
|
||||
@@ -188,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) {
|
||||
@@ -229,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();
|
||||
@@ -237,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";
|
||||
@@ -277,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,
|
||||
@@ -345,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.\nTo migrate: install and configure 'dex' (recommended),\nor 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;
|
||||
@@ -370,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;
|
||||
|
||||
@@ -927,7 +927,7 @@ static void keyboard_event_process_stdin_command_data(keyboard_event *self, int
|
||||
return;
|
||||
|
||||
const char *command_start = self->stdin_command_data;
|
||||
const char *search = self->stdin_command_data + self->stdin_command_data_size;
|
||||
char *search = self->stdin_command_data + self->stdin_command_data_size;
|
||||
const char *end = search + bytes_read;
|
||||
self->stdin_command_data_size += bytes_read;
|
||||
|
||||
@@ -968,8 +968,10 @@ void keyboard_event_poll_events(keyboard_event *self, int timeout_milliseconds)
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!(self->event_polls[i].revents & POLLIN))
|
||||
if(!(self->event_polls[i].revents & POLLIN)) {
|
||||
self->event_polls[i].revents = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(i == self->hotplug_event_index) {
|
||||
/* Device is added to end of |event_polls| so it's ok to add while iterating it via index */
|
||||
@@ -988,6 +990,8 @@ void keyboard_event_poll_events(keyboard_event *self, int timeout_milliseconds)
|
||||
} else {
|
||||
keyboard_event_process_input_event_data(self, &self->event_extra_data[i], self->event_polls[i].fd);
|
||||
}
|
||||
|
||||
self->event_polls[i].revents = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,52 +1,80 @@
|
||||
const DAEMON_DBUS_NAME = "com.dec05eba.gpu_screen_recorder.gsr_kwin_helper";
|
||||
const DAEMON_DBUS_NAME = "com.dec05eba.gpu_screen_recorder";
|
||||
|
||||
// utils
|
||||
function sendNewActiveWindowTitle(title) {
|
||||
function dbusSendUpdateActiveWindow(title, isFullscreen, monitorName) {
|
||||
callDBus(
|
||||
DAEMON_DBUS_NAME, "/", DAEMON_DBUS_NAME,
|
||||
"setActiveWindowTitle", title
|
||||
"updateActiveWindow",
|
||||
title, isFullscreen, monitorName,
|
||||
);
|
||||
}
|
||||
|
||||
function sendNewActiveWindowFullscreen(isFullscreen) {
|
||||
callDBus(
|
||||
DAEMON_DBUS_NAME, "/", DAEMON_DBUS_NAME,
|
||||
"setActiveWindowFullscreen", isFullscreen
|
||||
);
|
||||
let prevWindow = null;
|
||||
let prevEmitActiveWindowUpdate = null;
|
||||
|
||||
let prevCaption = null;
|
||||
let prevFullScreen = null;
|
||||
let prevMonitorName = null;
|
||||
|
||||
function dbusSafeDisconnect(window, signalName, callback) {
|
||||
try {
|
||||
const signal = window?.[signalName];
|
||||
if (signal && callback) {
|
||||
signal.disconnect(callback);
|
||||
}
|
||||
} catch (e) {
|
||||
// ignore stale/missing signal disconnects
|
||||
}
|
||||
}
|
||||
|
||||
// track handlers to avoid duplicates
|
||||
const windowEventHandlers = new Map();
|
||||
|
||||
function subscribeToClient(client) {
|
||||
if (!client || windowEventHandlers.has(client)) return;
|
||||
|
||||
const emitActiveTitle = () => {
|
||||
if (workspace.activeWindow === client) {
|
||||
sendNewActiveWindowTitle(client.caption || "");
|
||||
function dbusSafeConnect(window, signalName, callback) {
|
||||
try {
|
||||
const signal = window?.[signalName];
|
||||
if (signal && callback) {
|
||||
signal.connect(callback);
|
||||
}
|
||||
};
|
||||
|
||||
const emitActiveFullscreen = () => {
|
||||
if (workspace.activeWindow === client) {
|
||||
sendNewActiveWindowFullscreen(client.fullScreen);
|
||||
}
|
||||
};
|
||||
|
||||
windowEventHandlers.set(client, {
|
||||
title: emitActiveTitle,
|
||||
fs: emitActiveFullscreen,
|
||||
});
|
||||
|
||||
client.captionChanged.connect(emitActiveTitle);
|
||||
client.fullScreenChanged.connect(emitActiveFullscreen);
|
||||
} catch (e) {
|
||||
// ignore missing signals
|
||||
}
|
||||
}
|
||||
|
||||
function updateActiveWindow(client) {
|
||||
if (!client) return;
|
||||
sendNewActiveWindowTitle(client.caption || "");
|
||||
sendNewActiveWindowFullscreen(client.fullScreen);
|
||||
subscribeToClient(client);
|
||||
function emitActiveWindowUpdate(window) {
|
||||
if (workspace.activeWindow === window) {
|
||||
let caption = window.caption || "";
|
||||
let fullScreen = window.fullScreen || false;
|
||||
let monitorName = window.output?.name || "";
|
||||
if (caption !== prevCaption || fullScreen !== prevFullScreen || monitorName !== prevMonitorName) {
|
||||
dbusSendUpdateActiveWindow(caption, fullScreen, monitorName);
|
||||
prevCaption = caption;
|
||||
prevFullScreen = fullScreen;
|
||||
prevMonitorName = monitorName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function subscribeToWindow(window) {
|
||||
if (!window) return;
|
||||
if (prevWindow !== window) {
|
||||
if (prevWindow !== null) {
|
||||
dbusSafeDisconnect(prevWindow, "captionChanged", prevEmitActiveWindowUpdate);
|
||||
dbusSafeDisconnect(prevWindow, "fullScreenChanged", prevEmitActiveWindowUpdate);
|
||||
dbusSafeDisconnect(prevWindow, "outputChanged", prevEmitActiveWindowUpdate);
|
||||
}
|
||||
let emitActiveWindowUpdateBound = emitActiveWindowUpdate.bind(null, window);
|
||||
dbusSafeConnect(window, "captionChanged", emitActiveWindowUpdateBound);
|
||||
dbusSafeConnect(window, "fullScreenChanged", emitActiveWindowUpdateBound);
|
||||
dbusSafeConnect(window, "outputChanged", emitActiveWindowUpdateBound);
|
||||
prevWindow = window;
|
||||
prevEmitActiveWindowUpdate = emitActiveWindowUpdateBound;
|
||||
}
|
||||
}
|
||||
|
||||
function updateActiveWindow(window) {
|
||||
if (!window) return;
|
||||
if (!window.normalWindow) return;
|
||||
if (window.resourceName === "gsr-ui" || window.resourceName === "gsr-notify") return; // ignore the overlay and notification
|
||||
if (window.resourceClass === "org.kde.spectacle") return;
|
||||
emitActiveWindowUpdate(window);
|
||||
subscribeToWindow(window);
|
||||
}
|
||||
|
||||
// handle window focus changes
|
||||
@@ -55,4 +83,4 @@ workspace.windowActivated.connect(updateActiveWindow);
|
||||
// handle initial state
|
||||
if (workspace.activeWindow) {
|
||||
updateActiveWindow(workspace.activeWindow);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,9 +7,11 @@ static const char* INTROSPECTION_XML =
|
||||
"<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\"\n"
|
||||
"\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n"
|
||||
"<node>\n"
|
||||
" <interface name='com.dec05eba.gpu_screen_recorder.gsr_kwin_helper'>\n"
|
||||
" <method name='setActiveWindowTitle'>\n"
|
||||
" <interface name='com.dec05eba.gpu_screen_recorder'>\n"
|
||||
" <method name='updateActiveWindow'>\n"
|
||||
" <arg type='s' name='title' direction='in'/>\n"
|
||||
" <arg type='b' name='fullscreen' direction='in'/>\n"
|
||||
" <arg type='s' name='monitorName' direction='in'/>\n"
|
||||
" </method>\n"
|
||||
" </interface>\n"
|
||||
" <interface name='org.freedesktop.DBus.Introspectable'>\n"
|
||||
@@ -23,6 +25,8 @@ static const char* INTROSPECTION_XML =
|
||||
class GsrKwinHelper {
|
||||
public:
|
||||
std::string active_window_title;
|
||||
bool active_window_fullscreen;
|
||||
std::string active_window_monitor_name;
|
||||
DBusConnection* connection = nullptr;
|
||||
DBusError err;
|
||||
|
||||
@@ -41,7 +45,7 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
int ret = dbus_bus_request_name(connection, "com.dec05eba.gpu_screen_recorder.gsr_kwin_helper",
|
||||
int ret = dbus_bus_request_name(connection, "com.dec05eba.gpu_screen_recorder",
|
||||
DBUS_NAME_FLAG_REPLACE_EXISTING, &err);
|
||||
if (dbus_error_is_set(&err)) {
|
||||
std::cerr << "Error: gsr-kwin-helper: failed to request name: " << err.message << "\n";
|
||||
@@ -54,7 +58,7 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
std::cerr << "Info: gsr-kwin-helper: DBus server initialized on com.dec05eba.gpu_screen_recorder.gsr_kwin_helper\n";
|
||||
std::cerr << "Info: gsr-kwin-helper: DBus server initialized on com.dec05eba.gpu_screen_recorder\n";
|
||||
|
||||
const bool inside_flatpak = access("/app/manifest.json", F_OK) == 0;
|
||||
|
||||
@@ -83,8 +87,8 @@ public:
|
||||
|
||||
if (dbus_message_is_method_call(msg, "org.freedesktop.DBus.Introspectable", "Introspect")) {
|
||||
handle_introspect(msg);
|
||||
} else if (dbus_message_is_method_call(msg, "com.dec05eba.gpu_screen_recorder.gsr_kwin_helper", "setActiveWindowTitle")) {
|
||||
handle_set_title(msg);
|
||||
} else if (dbus_message_is_method_call(msg, "com.dec05eba.gpu_screen_recorder", "updateActiveWindow")) {
|
||||
handle_update_active_window(msg);
|
||||
}
|
||||
|
||||
dbus_message_unref(msg);
|
||||
@@ -108,9 +112,11 @@ public:
|
||||
dbus_message_unref(reply);
|
||||
}
|
||||
|
||||
void handle_set_title(DBusMessage* msg) {
|
||||
void handle_update_active_window(DBusMessage* msg) {
|
||||
DBusMessageIter args;
|
||||
const char* title = nullptr;
|
||||
DBusBasicValue title;
|
||||
DBusBasicValue fullscreen;
|
||||
DBusBasicValue monitorName;
|
||||
|
||||
if (!dbus_message_iter_init(msg, &args)) {
|
||||
send_error_reply(msg, "No arguments provided");
|
||||
@@ -123,15 +129,60 @@ public:
|
||||
}
|
||||
|
||||
dbus_message_iter_get_basic(&args, &title);
|
||||
|
||||
if (!dbus_message_iter_next(&args)) {
|
||||
send_error_reply(msg, "Not enough arguments provided");
|
||||
return;
|
||||
}
|
||||
|
||||
if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_BOOLEAN) {
|
||||
send_error_reply(msg, "Expected boolean argument");
|
||||
return;
|
||||
}
|
||||
|
||||
dbus_message_iter_get_basic(&args, &fullscreen);
|
||||
|
||||
if (!dbus_message_iter_next(&args)) {
|
||||
send_error_reply(msg, "Not enough arguments provided");
|
||||
return;
|
||||
}
|
||||
|
||||
if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_STRING) {
|
||||
send_error_reply(msg, "Expected string argument");
|
||||
return;
|
||||
}
|
||||
|
||||
dbus_message_iter_get_basic(&args, &monitorName);
|
||||
|
||||
if (title) {
|
||||
active_window_title = title;
|
||||
std::cout << "Active window title set to: " << active_window_title << "\n";
|
||||
std::cout.flush();
|
||||
send_success_reply(msg);
|
||||
if (title.str) {
|
||||
if (active_window_title != title.str) {
|
||||
active_window_title = title.str;
|
||||
std::cout << "Active window title set to: " << active_window_title << "\n";
|
||||
std::cout.flush();
|
||||
}
|
||||
} else {
|
||||
send_error_reply(msg, "Failed to read string");
|
||||
return;
|
||||
}
|
||||
|
||||
if (active_window_fullscreen != fullscreen.bool_val) {
|
||||
active_window_fullscreen = fullscreen.bool_val;
|
||||
std::cout << "Active window fullscreen state set to: " << active_window_fullscreen << "\n";
|
||||
std::cout.flush();
|
||||
}
|
||||
|
||||
if (monitorName.str) {
|
||||
if (active_window_monitor_name != monitorName.str) {
|
||||
active_window_monitor_name = monitorName.str;
|
||||
std::cout << "Active window monitor name set to: " << active_window_monitor_name << "\n";
|
||||
std::cout.flush();
|
||||
}
|
||||
} else {
|
||||
send_error_reply(msg, "Failed to read string");
|
||||
return;
|
||||
}
|
||||
|
||||
send_success_reply(msg);
|
||||
}
|
||||
|
||||
void send_success_reply(DBusMessage* msg) {
|
||||
@@ -144,7 +195,7 @@ public:
|
||||
}
|
||||
|
||||
void send_error_reply(DBusMessage* msg, const char* error_msg) {
|
||||
DBusMessage* reply = dbus_message_new_error(msg, "com.dec05eba.gpu_screen_recorder.gsr_kwin_helper.Error", error_msg);
|
||||
DBusMessage* reply = dbus_message_new_error(msg, "com.dec05eba.gpu_screen_recorder.Error", error_msg);
|
||||
if (reply) {
|
||||
dbus_connection_send(connection, reply, nullptr);
|
||||
dbus_connection_flush(connection);
|
||||
@@ -214,7 +265,7 @@ public:
|
||||
|
||||
~GsrKwinHelper() {
|
||||
if (connection) {
|
||||
dbus_bus_release_name(connection, "com.dec05eba.gpu_screen_recorder.gsr_kwin_helper", nullptr);
|
||||
dbus_bus_release_name(connection, "com.dec05eba.gpu_screen_recorder", nullptr);
|
||||
dbus_connection_unref(connection);
|
||||
}
|
||||
}
|
||||
|
||||
414
translations/es.txt
Normal file
414
translations/es.txt
Normal file
@@ -0,0 +1,414 @@
|
||||
# GPU Screen Recorder UI - Spanish translation
|
||||
|
||||
# General UI
|
||||
Record=Grabar
|
||||
Instant Replay=Repetición Instantánea
|
||||
Livestream=Transmisión
|
||||
Settings=Ajustes
|
||||
|
||||
# Status messages
|
||||
Off=Desactivado
|
||||
On=Activado
|
||||
Not recording=No grabando
|
||||
Recording=Grabando
|
||||
Not streaming=No transmitiendo
|
||||
Streaming=Transmitiendo
|
||||
Paused=Pausado
|
||||
|
||||
# Button labels
|
||||
Start=Iniciar
|
||||
Stop=Detener
|
||||
Stop and save=Detener y guardar
|
||||
Pause=Pausar
|
||||
Unpause=Reanudar
|
||||
Save=Guardar
|
||||
Save 1 min=Guardar 1 min
|
||||
Save 10 min=Guardar 10 min
|
||||
Turn on=Activar
|
||||
Turn off=Desactivar
|
||||
|
||||
# Notifications - Recording
|
||||
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=Se guardó una grabación de %s de %s
|
||||
Failed to start/save recording=Fallo al iniciar/guardar grabación
|
||||
|
||||
# Notifications - Replay
|
||||
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=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.
|
||||
|
||||
# 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.
|
||||
|
||||
# Notifications - Screenshot
|
||||
Saved a screenshot of %s\nto "%s"=Captura de pantalla de %s\nguardada 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
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
|
||||
Failed to start region capture=Fallo al iniciar la captura de región
|
||||
Failed to start window capture=Fallo al iniciar la captura de ventana
|
||||
No window selected=Ventana no seleccionada
|
||||
|
||||
Streaming stopped because of an error. Verify if settings are correct=Transmisión detenida debido a un error. Comprueba que los ajustes sean correctos
|
||||
%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
|
||||
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
|
||||
|
||||
# Launch errors
|
||||
Failed to launch gpu-screen-recorder to start replay=Fallo al lanzar gpu-screen-recorder para iniciar la repetición
|
||||
Failed to launch gpu-screen-recorder to start recording=Fallo al lanzar gpu-screen-recorder para iniciar la grabación
|
||||
Failed to launch gpu-screen-recorder to start streaming=Fallo al lanzar gpu-screen-recorder para iniciar la transmisión
|
||||
Failed to launch gpu-screen-recorder to take a screenshot=Fallo al lanzar gpu-screen-recorder para tomar una captura de pantalla
|
||||
|
||||
# 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
|
||||
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.\nTo migrate: install and configure 'dex' (recommended),\nor manually add '%s' to your desktop autostart entries.=El inicio automático de GPU Screen Recorder UI mediante systemd está obsoleto.\nPara migrar: instala y configura 'dex' (recomendado)\no 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.
|
||||
|
||||
# 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.
|
||||
|
||||
# Capture targets
|
||||
this monitor=este monitor
|
||||
window=ventana
|
||||
window "%s"=ventana "%s"
|
||||
window %s=ventana %s
|
||||
focused=activa
|
||||
region=región
|
||||
portal=portal
|
||||
|
||||
# Time durations (used in recording/replay saved notifications)
|
||||
%d second=%d segundo
|
||||
%d minute=%d minuto
|
||||
%d hour=%d hora
|
||||
%d seconds=%d segundos
|
||||
%d minutes=%d minutos
|
||||
%d hours=%d horas
|
||||
|
||||
|
||||
# Global Settings Page UI elements
|
||||
Accent color=Color de acento
|
||||
Red=Rojo
|
||||
Green=Verde
|
||||
Blue=Azul
|
||||
|
||||
Start program on system startup?=¿Iniciar programa al iniciar el sistema?
|
||||
Yes=Sí
|
||||
No=No
|
||||
|
||||
Enable keyboard hotkeys?=¿Habilitar las teclas de acceso rápido?
|
||||
Yes, but only grab virtual devices (supports some input remapping software)=Sí, pero capturar solo dispositivos virtuales (ofrece soporte para algunos prog. de reasig. de teclas)
|
||||
Yes, but don't grab devices (supports all input remapping software)=Sí, pero sin capturar dispositivos (ofrece soporte para todos los programas de reasignación de teclas)
|
||||
|
||||
Show/hide UI:=Mostrar/ocultar interfaz
|
||||
Turn replay on/off:=Activar/desactivar repetición
|
||||
Save replay:=Guardar repetición
|
||||
Save 1 minute replay:=Guardar repetición de 1 minuto
|
||||
Save 10 minute replay:=Guardar repetición de 10 minutos
|
||||
Start/stop recording:=Iniciar/detener grabación
|
||||
Pause/unpause recording:=Pausar/reanudar grabación
|
||||
Start/stop recording a region:=Iniciar/detener grabación de una región
|
||||
Start/stop streaming:=Iniciar/detener transmisión
|
||||
Take a screenshot:=Tomar una captura de pantalla
|
||||
Take a screenshot of a region:=Tomar una captura de pantalla de una región
|
||||
Start/stop recording with desktop portal:=Iniciar/detener grabación con portal de escritorio
|
||||
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
|
||||
|
||||
Clear hotkeys=Borrar teclas de acceso rápido
|
||||
Reset hotkeys to default=Reestablecer teclas de acceso rápido a los valores predeterminados
|
||||
|
||||
Enable controller hotkeys?=¿Habilitar las teclas de acceso rápido para mando?
|
||||
Press=Pulsa
|
||||
and=y
|
||||
|
||||
Notification speed=Velocidad de notificación
|
||||
Normal=Normal
|
||||
Fast=Rápida
|
||||
|
||||
Language=Idioma
|
||||
System language=Idioma del sistema
|
||||
|
||||
Exit program=Salir del programa
|
||||
Go back to the old UI=Volver a la interfaz antigua
|
||||
|
||||
If you would like to donate you can do so by donating at https://buymeacoffee.com/dec05eba:=Si deseas hacer una donación, puedes hacerlo en https://buymeacoffee.com/dec05eba:
|
||||
Donate=Donar
|
||||
All donations go toward developing software (including GPU Screen Recorder)\nand buying hardware to test the software.=Todas las donaciones se destinan al desarrollo de software (incluido GPU Screen Recorder)\ny a la compra de hardware para probar el software.
|
||||
|
||||
# Subsection headers
|
||||
Global=Global
|
||||
Back=Atrás
|
||||
|
||||
Appearance=Apariencia
|
||||
Startup=Inicio
|
||||
Keyboard hotkeys=Teclas de acceso rápido
|
||||
Controller hotkeys=Teclas de acceso rápido para mando
|
||||
Application options=Opciones de la aplicación
|
||||
Application info=Información de la aplicación
|
||||
Donate=Donar
|
||||
|
||||
# Version info strings
|
||||
GSR version: %s=GSR versión: %s
|
||||
GSR-UI version: %s=GSR-UI versión: %s
|
||||
Flatpak version: %s=Flatpak versión: %s
|
||||
GPU vendor: %s=Proveedor de GPU: %s
|
||||
|
||||
# Hotkey configuration dialog
|
||||
Press a key combination to use for the hotkey: "%s"=Pulsa una combinación de teclas para el acceso rápido: "%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.=Las teclas alfanuméricas no se pueden usar solas en los atajos de teclado, deben usarse con una o más de estas teclas: Alt, Ctrl, Mayús y Super.\nPulsa Esc para cancelar o Retroceso para eliminar el atajo de teclado.
|
||||
|
||||
# Hotkey action names (without colons - these appear in the dialog)
|
||||
Show/hide UI=Mostrar/ocultar interfaz
|
||||
Turn replay on/off=Activar/desactivar repetición
|
||||
Save replay=Guardar repetición
|
||||
Save 1 minute replay=Guardar repetición de 1 minuto
|
||||
Save 10 minute replay=Guardar repetición de 10 minutos
|
||||
Start/stop recording=Iniciar/detener grabación
|
||||
Pause/unpause recording=Pausar/reanudar grabación
|
||||
Start/stop recording a region=Iniciar/detener grabación de una región
|
||||
Start/stop recording a window=Iniciar/detener grabación de una ventana
|
||||
Start/stop recording with desktop portal=Iniciar/detener grabación con un portal de escritorio
|
||||
Start/stop streaming=Iniciar/detener transmisión
|
||||
Take a screenshot=Tomar una captura de pantalla
|
||||
Take a screenshot of a region=Tomar una captura de pantalla de una región
|
||||
Take a screenshot of a window=Tomar una captura de pantalla de una ventana
|
||||
Take a screenshot with desktop portal=Tomar una captura de pantalla con un portal de escritorio
|
||||
|
||||
# Controller hotkey descriptions
|
||||
to show/hide the UI=para mostrar/ocultar la interfaz
|
||||
to take a screenshot=para tomar una captura de pantalla
|
||||
to save a replay=para guardar una repetición
|
||||
to start/stop recording=para iniciar/detener una grabación
|
||||
to turn replay on/off=para activar/desactivar la repetición
|
||||
to save a 1 minute replay=para guardar una repetición de 1 minuto
|
||||
to save a 10 minute replay=para guardar una repetición de 10 minutos
|
||||
|
||||
# Error message for duplicate hotkey
|
||||
The hotkey %s is already used for something else=La combinación de teclas %s ya está en uso para otra cosa
|
||||
|
||||
|
||||
# Screenshot settings page
|
||||
Screenshot=Captura de pantalla
|
||||
Capture=Captura
|
||||
Image=Imagen
|
||||
File info=Información del archivo
|
||||
General=General
|
||||
Screenshot indicator=Indicador de captura de pantalla
|
||||
Script=Script
|
||||
File=Archivo
|
||||
|
||||
Back=Atrás
|
||||
Save=Guardar
|
||||
Cancel=Cancelar
|
||||
|
||||
Capture source:=Fuente de captura:
|
||||
Window=Ventana
|
||||
Region=Región
|
||||
Desktop portal=Portal de escritorio
|
||||
Monitor %s (%dx%d)=Monitor %s (%dx%d)
|
||||
Screen=Pantalla
|
||||
|
||||
Image resolution limit:=Límite de resolución de imagen:
|
||||
Change image resolution=Cambiar la resolución de imagen
|
||||
Restore portal session=Restaurar sesión de portal
|
||||
|
||||
Image quality:=Calidad de imagen
|
||||
Medium=Media
|
||||
High=Alta
|
||||
Very high (Recommended)=Muy alta (recomendado)
|
||||
Ultra=Ultra
|
||||
|
||||
Record cursor=Grabar cursor
|
||||
|
||||
Directory to save screenshots:=Directorio para guardar capturas de pantalla:
|
||||
Image format:=Formato de imagen:
|
||||
|
||||
Save screenshot in a folder based on the games name=Guardar captura de pantalla en una carpeta con el mismo nombre que el juego
|
||||
|
||||
Save screenshot to clipboard=Guardar captura de pantalla en el portapapeles
|
||||
Save screenshot to clipboard (Not supported properly by Wayland)=Guardar captura de pantalla en el portapapeles (no admitido por Wayland)
|
||||
Save screenshot to disk=Guardar la captura de pantalla en el disco
|
||||
|
||||
Show screenshot notifications=Mostrar notificaciones de captura de pantalla
|
||||
Blink scroll lock led when taking a screenshot=Hacer parpadear el LED de Bloq Despl al tomar una captura de pantalla
|
||||
|
||||
Command to open the screenshot with:=Abrir la captura de pantalla con el comando:
|
||||
|
||||
|
||||
# Settings Page UI elements - дополнения
|
||||
|
||||
# View modes
|
||||
Simple view=Vista simple
|
||||
Advanced view=Vista avanzada
|
||||
|
||||
# Capture settings
|
||||
Follow focused window=Seguir la ventana activa
|
||||
Focused monitor=Monitor activo
|
||||
|
||||
Area size:=Tamaño del área:
|
||||
Video resolution limit:=Límite de resolución de vídeo:
|
||||
Change video resolution=Cambiar la resolución de vídeo
|
||||
Restore portal session=Restaurar sesión de portal
|
||||
|
||||
# Webcam settings
|
||||
Webcam=Webcam
|
||||
Webcam source:=Fuente de la webcam:
|
||||
None=Ninguna
|
||||
Video format:=Formato de vídeo:
|
||||
Auto (recommended)=Auto (recomendado)
|
||||
YUYV=YUYV
|
||||
Motion-JPEG=Motion-JPEG
|
||||
Video setup:=Configuración de vídeo:
|
||||
* Right click in the bottom right corner to resize the webcam=* Clic derecho en la esquina inferior derecha para cambiar el tamaño de la webcam
|
||||
Flip camera horizontally=Voltear la cámara horizontalmente
|
||||
|
||||
# Audio settings
|
||||
Audio=Sonido
|
||||
Audio codec:=Códec de sonido
|
||||
Opus (Recommended)=Opus (recomendado)
|
||||
AAC=AAC
|
||||
|
||||
Directory to save videos:=Directorio donde guardar los vídeos:
|
||||
Output device:=Dispositivo de salida:
|
||||
Input device: =Dispositivo de entrada:
|
||||
# yes, these spaces are intentional
|
||||
Application: =Aplicación:
|
||||
Custom...=Personalizado...
|
||||
|
||||
Save video in a folder based on the games name%s=Guardar el vídeo en una carpeta con el mismo nombre que el juego%s
|
||||
(X11 applications only)= (solo para aplicaciones X11)
|
||||
|
||||
Add audio track=Añadir pista de sonido
|
||||
Add input device=Añadir dispositivo de entrada
|
||||
Add output device=Añadir dispositivo de salida
|
||||
Add application audio=Añadir sonido de aplicación
|
||||
Record audio from all applications except the selected ones=Grabar el sonido de todas las aplicaciones salvo las seleccionadas
|
||||
Recording output devices and application audio may record all output audio, which is likely\nnot what you want to do. Remove the output devices.=Grabar los dispositivos de salida y el audio de las aplicaciones puede que grabe todo el audio de salida, lo cual probablemente\nno es lo que quieres hacer. Elimina los dispositivos de salida.
|
||||
|
||||
Video=Vídeo
|
||||
|
||||
# Video codec settings
|
||||
Video codec:=Códec de vídeo:
|
||||
H264=H264
|
||||
HEVC=HEVC
|
||||
HEVC (10 bit, reduces banding)=HEVC (10 bits, reduce el banding)
|
||||
HEVC (HDR)=HEVC (HDR)
|
||||
AV1=AV1
|
||||
AV1 (10 bit, reduces banding)=AV1 (10 bits, reduce el banding)
|
||||
AV1 (HDR)=AV1 (HDR)
|
||||
VP8=VP8
|
||||
VP9=VP9
|
||||
H264 Software Encoder (Slow, not recommended)=Codificador por software H264 (lento, no recomendado)
|
||||
|
||||
# Video quality and bitrate
|
||||
Video quality:=Calidad de vídeo:
|
||||
Very high=Muy alta
|
||||
Video bitrate (Kbps):=Tasa de bits del vídeo (Kbps)
|
||||
Constant bitrate=Tasa de bits constante
|
||||
Constant bitrate (Recommended)=Tasa de bits constante (recomendado)
|
||||
|
||||
# Frame rate settings
|
||||
Frame rate:=Tasa de fotogramas:
|
||||
Frame rate mode:=Modo de tasa de fotogramas:
|
||||
Auto (Recommended)=Automático (ecomendado)
|
||||
Constant=Constante
|
||||
Variable=Variable
|
||||
Sync to content=Sincronizar con el contenido
|
||||
Sync to content (Only X11 or desktop portal capture)=Sincronizar con el contenido (solo para X11 o captura de portal de escritorio)
|
||||
|
||||
# Color range
|
||||
Color range:=Rango de color
|
||||
Limited=Limitado
|
||||
Full=Completo
|
||||
|
||||
# Container format
|
||||
Container:=Contenedor:
|
||||
|
||||
# Recording settings
|
||||
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.
|
||||
|
||||
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
|
||||
Recording indicator=Indicador de grabación
|
||||
recording=grabando
|
||||
|
||||
Simple=Simple
|
||||
Audio track #%d=Pista de sonido #%d
|
||||
Output device=Dispositivo de salida
|
||||
Input device: =Dispositivo de entrada:
|
||||
Estimated video file size per minute (excluding audio): %.2fMB=Tamaño estimado del archivo de vídeo por minuto (sin sonido): %.2fMB
|
||||
|
||||
# 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
|
||||
Autostart=Inicio automático
|
||||
in RAM=en RAM
|
||||
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
|
||||
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.
|
||||
|
||||
# Streaming settings
|
||||
Stream service:=Servicio de transmisión
|
||||
Twitch=Twitch
|
||||
YouTube=YouTube
|
||||
Kick=Kick
|
||||
Rumble=Rumble
|
||||
Custom=Personalizado
|
||||
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
|
||||
427
translations/fr.txt
Normal file
427
translations/fr.txt
Normal file
@@ -0,0 +1,427 @@
|
||||
# GPU Screen Recorder UI - translation template
|
||||
|
||||
# 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.
|
||||
|
||||
# General UI
|
||||
Record=Enregistrer
|
||||
Instant Replay=Replay instantané
|
||||
Livestream=Diffusion en direct
|
||||
Settings=Paramètres
|
||||
|
||||
# Status messages
|
||||
Off=Éteint
|
||||
On=Allumé
|
||||
Not recording=Non enregistré
|
||||
Recording=Enregistrement
|
||||
Not streaming=Non diffusé
|
||||
Streaming=Diffusion
|
||||
Paused=En pause
|
||||
|
||||
# Button labels
|
||||
Start=Lancer
|
||||
Stop=Arrêter
|
||||
Stop and save=Arrêter et sauvegarder
|
||||
Pause=Pause
|
||||
Unpause=Reprendre
|
||||
Save=Sauvegarder
|
||||
Save 1 min=Sauvegarder 1 min
|
||||
Save 10 min=Sauvegarder 10 min
|
||||
Turn on=Lancer
|
||||
Turn off=Arrêter
|
||||
|
||||
# Notifications - Recording
|
||||
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=Enregistrement %s de %s sauvegardé
|
||||
Failed to start/save recording=Échec du démarrage/sauvegarde de l’enregistrement
|
||||
|
||||
# Notifications - Replay
|
||||
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=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.
|
||||
|
||||
# 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.
|
||||
|
||||
# Notifications - Screenshot
|
||||
Saved a screenshot of %s\nto "%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.
|
||||
|
||||
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.
|
||||
|
||||
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
|
||||
|
||||
Failed to start region capture=Échec du démarrage de la capture de région
|
||||
Failed to start window capture=Échec du démarrage de la capture de fenêtre
|
||||
No window selected=Aucune fenêtre sélectionnée
|
||||
|
||||
Streaming stopped because of an error. Verify if settings are correct=Diffusion arrêtée en raison d’une erreur. Vérifiez les paramètres
|
||||
%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.
|
||||
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.
|
||||
|
||||
# Launch errors
|
||||
Failed to launch gpu-screen-recorder to start replay=Échec du lancement de gpu-screen-recorder pour démarrer le replay
|
||||
Failed to launch gpu-screen-recorder to start recording=Échec du lancement de gpu-screen-recorder pour démarrer l’enregistrement
|
||||
Failed to launch gpu-screen-recorder to start streaming=Échec du lancement de gpu-screen-recorder pour démarrer la diffusion
|
||||
Failed to launch gpu-screen-recorder to take a screenshot=Échec du lancement de gpu-screen-recorder pour capturer l’écran
|
||||
|
||||
# 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
|
||||
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.\nTo migrate: install and configure 'dex' (recommended),\nor manually add '%s' to your desktop autostart entries.=Le démarrage automatique de GPU Screen Recorder UI via systemd est obsolète.\nPour migrer : installez et configurez 'dex' (recommandé)\nou 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.
|
||||
|
||||
# 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.
|
||||
|
||||
# Capture targets
|
||||
this monitor=Ce moniteur
|
||||
window=Fenêtre
|
||||
window "%s"=Fenêtre "%s"
|
||||
window %s=Fenêtre %s
|
||||
focused=Fenêtre active
|
||||
region=Région
|
||||
portal=Portail
|
||||
|
||||
# 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 seconde
|
||||
%d second_few=%d secondes
|
||||
%d second_many=%d secondes
|
||||
%d minute_one=%d minute
|
||||
%d minute_few=%d minutes
|
||||
%d minute_many=%d minutes
|
||||
%d hour_one=%d heure
|
||||
%d hour_few=%d heures
|
||||
%d hour_many=%d heures
|
||||
%d second=%d seconde
|
||||
%d minute=%d minute
|
||||
%d hour=%d heure
|
||||
%d seconds=%d secondes
|
||||
%d minutes=%d minutes
|
||||
%d hours=%d heures
|
||||
|
||||
|
||||
# Global Settings Page UI elements
|
||||
Accent color=Couleur d’accent
|
||||
Red=Rouge
|
||||
Green=Vert
|
||||
Blue=Bleu
|
||||
|
||||
Start program on system startup?=Lancer le programme au démarrage du système ?
|
||||
Yes=Oui
|
||||
No=Non
|
||||
|
||||
Enable keyboard hotkeys?=Activer les raccourcis clavier ?
|
||||
Yes, but only grab virtual devices (supports some input remapping software)=Oui, mais capturer que les appareils virtuels (supporte certains logiciels de remappage)
|
||||
Yes, but don't grab devices (supports all input remapping software)=Oui, sans capturer les appareils (supporte tous les logiciels de remappage)
|
||||
|
||||
Show/hide UI:=Afficher/masquer l’UI
|
||||
Turn replay on/off:=Activer/désactiver le replay
|
||||
Save replay:=Sauvegarder le replay
|
||||
Save 1 minute replay:=Sauvegarder 1min de replay
|
||||
Save 10 minute replay:=Sauvegarder 10min de replay
|
||||
Start/stop recording:=Lancer/arrêter l’enregistrement
|
||||
Pause/unpause recording:=Pause/reprendre l’enregistrement
|
||||
Start/stop recording a region:=Lancer/arrêter l’enregistrement d’une région
|
||||
Start/stop streaming:=Lancer/arrêter la diffusion
|
||||
Take a screenshot:=Prendre une capture d’écran
|
||||
Take a screenshot of a region:=Prendre une capture d’écran d’une région
|
||||
Start/stop recording with desktop portal:=Lancer/arrêter l’enregistrement via le portail du bureau
|
||||
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
|
||||
|
||||
Clear hotkeys=Effacer les raccourcis
|
||||
Reset hotkeys to default=Réinitialiser les raccourcis par défaut
|
||||
|
||||
Enable controller hotkeys?=Activer les raccourcis manette ?
|
||||
Press=Appuyer sur
|
||||
and=et
|
||||
|
||||
Notification speed=Vitesse des notifications
|
||||
Normal=Normale
|
||||
Fast=Rapide
|
||||
|
||||
Language=Langue
|
||||
System language=Langue du système
|
||||
|
||||
Exit program=Quitter le programme
|
||||
Go back to the old UI=Revenir à l’ancienne UI
|
||||
|
||||
If you would like to donate you can do so by donating at https://buymeacoffee.com/dec05eba:=Si vous souhaitez faire un don, vous pouvez le faire sur https://buymeacoffee.com/dec05eba:
|
||||
Donate=Faire un don
|
||||
All donations go toward developing software (including GPU Screen Recorder)\nand buying hardware to test the software.=Tous les dons servent au développement du logiciel (y compris GPU Screen Recorder) \net à l’achat de matériel pour les tests.
|
||||
|
||||
# Subsection headers
|
||||
Global=Général
|
||||
Back=Retour
|
||||
|
||||
Appearance=Apparence
|
||||
Startup=Démarrage
|
||||
Keyboard hotkeys=Raccourcis clavier
|
||||
Controller hotkeys=Raccourcis manette
|
||||
Application options=Options de l’application
|
||||
Application info=Informations sur l’application
|
||||
Donate=Faire un don
|
||||
|
||||
# Version info strings
|
||||
GSR version: %s=Version GSR : %s
|
||||
GSR-UI version: %s=Version GSR-UI : %s
|
||||
Flatpak version: %s=Version Flatpak : %s
|
||||
GPU vendor: %s=Constructeur GPU : %s
|
||||
|
||||
# Hotkey configuration dialog
|
||||
Press a key combination to use for the hotkey: "%s"=Appuyez sur une combinaison de touches pour le raccourci : "%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.=Les touches alphanumériques ne peuvent pas être utilisées seules pour les raccourcis, elles doivent être combinées avec une ou plusieurs de ces touches : Alt, Ctrl, Shift, Super.\nAppuyez sur Échap pour annuler ou Retour arrière pour supprimer le raccourci.
|
||||
|
||||
# Hotkey action names (without colons - these appear in the dialog)
|
||||
Show/hide UI=Afficher/masquer l’UI
|
||||
Turn replay on/off=Activer/désactiver le replay
|
||||
Save replay=Sauvegarder le replay
|
||||
Save 1 minute replay=Sauvegarder 1min de replay
|
||||
Save 10 minute replay=Sauvegarder 10min de replay
|
||||
Start/stop recording=Lancer/arrêter l’enregistrement
|
||||
Pause/unpause recording=Pause/reprendre l’enregistrement
|
||||
Start/stop recording a region=Lancer/arrêter l’enregistrement d’une région
|
||||
Start/stop recording a window=Lancer/arrêter l’enregistrement d’une fenêtre
|
||||
Start/stop recording with desktop portal=Lancer/arrêter l’enregistrement via le portail du bureau
|
||||
Start/stop streaming=Lancer/arrêter la diffusion
|
||||
Take a screenshot=Prendre une capture d’écran
|
||||
Take a screenshot of a region=Prendre une capture d’écran d’une région
|
||||
Take a screenshot of a window=Prendre une capture d’écran d’une fenêtre
|
||||
Take a screenshot with desktop portal=Prendre une capture via le portail du bureau
|
||||
|
||||
# Controller hotkey descriptions
|
||||
to show/hide the UI=pour afficher/masquer l’UI
|
||||
to take a screenshot=pour prendre une capture d’écran
|
||||
to save a replay=pour sauvegarder le replay
|
||||
to start/stop recording=pour démarrer/arrêter l’enregistrement
|
||||
to turn replay on/off=pour activer/désactiver le replay
|
||||
to save a 1 minute replay=pour sauvegarder 1min de replay
|
||||
to save a 10 minute replay=pour sauvegarder 10min de replay
|
||||
|
||||
# Error message for duplicate hotkey
|
||||
The hotkey %s is already used for something else=Le raccourci %s est déjà utilisé pour une autre action
|
||||
|
||||
|
||||
# Screenshot settings page
|
||||
Screenshot=Capture d’écran
|
||||
Capture=Capture
|
||||
Image=Image
|
||||
File info=Infos fichier
|
||||
General=Général
|
||||
Screenshot indicator=Indicateur de capture
|
||||
Script=Script
|
||||
File=Fichier
|
||||
|
||||
Back=Retour
|
||||
Save=Enregistrer
|
||||
Cancel=Annuler
|
||||
|
||||
Capture source:=Source de capture
|
||||
Window=Fenêtre
|
||||
Region=Région
|
||||
Desktop portal=Portail du bureau
|
||||
Monitor %s (%dx%d)=Moniteur %s (%dx%d)
|
||||
Screen=Écran
|
||||
|
||||
Image resolution limit:=Limite de résolution de l’image
|
||||
Change image resolution=Modifier la résolution de l’image
|
||||
Restore portal session=Restaurer la session du portail
|
||||
|
||||
Image quality:=Qualité de l’image
|
||||
Medium=Moyenne
|
||||
High=Haute
|
||||
Very high (Recommended)=Très haute (Recommandé)
|
||||
Ultra=Ultra
|
||||
|
||||
Record cursor=Enregistrer le curseur
|
||||
|
||||
Directory to save screenshots:=Répertoire de sauvegarde des captures
|
||||
Image format:=Format de l’image
|
||||
|
||||
Save screenshot in a folder based on the games name=Enregistrer la capture dans un dossier nommé selon le jeu
|
||||
|
||||
Save screenshot to clipboard=Copier la capture dans le presse-papiers
|
||||
Save screenshot to clipboard (Not supported properly by Wayland)=Copier la capture dans le presse-papiers (non pleinement supporté par Wayland)
|
||||
Save screenshot to disk=Enregistrer la capture sur le disque
|
||||
|
||||
Show screenshot notifications=Afficher les notifications de capture
|
||||
Blink scroll lock led when taking a screenshot=Faire clignoter la LED d'Arrêt Défil. lors de la capture d'écran
|
||||
|
||||
Command to open the screenshot with:=Commande pour ouvrir la capture
|
||||
|
||||
|
||||
# Settings Page UI elements - дополнения
|
||||
|
||||
# View modes
|
||||
Simple view=Vue simple
|
||||
Advanced view=Vue avancée
|
||||
|
||||
# Capture settings
|
||||
Follow focused window=Suivre la fenêtre active
|
||||
Focused monitor=Moniteur actif
|
||||
|
||||
Area size:=
|
||||
Area size:=Taille de la zone
|
||||
Video resolution limit:=Limite de résolution vidéo
|
||||
Change video resolution=Modifier la résolution vidéo
|
||||
Restore portal session=Restaurer la session du portail
|
||||
|
||||
# Webcam settings
|
||||
Webcam=Webcam
|
||||
Webcam source=Source webcam
|
||||
None=Aucune
|
||||
Video format=Format vidéo
|
||||
Auto (recommended)=Automatique (recommandé)
|
||||
YUYV=YUYV
|
||||
Motion-JPEG=Motion-JPEG
|
||||
Video setup=Configuration vidéo
|
||||
* Right click in the bottom right corner to resize the webcam=* Clic droit en bas à droite pour redimensionner la webcam
|
||||
Flip camera horizontally=Inverser la caméra horizontalement
|
||||
|
||||
# Audio settings
|
||||
Audio=Audio
|
||||
Audio codec=Codec audio
|
||||
Opus (Recommended)=Opus (Recommandé)
|
||||
AAC=AAC
|
||||
|
||||
Directory to save videos:=Répertoire pour sauvegarder les vidéos
|
||||
Output device:=Périphérique de sortie
|
||||
Input device: =Périphérique d’entrée
|
||||
Application: =Application :
|
||||
Custom...=Personnalisé...
|
||||
|
||||
Save video in a folder based on the games name%s=Enregistrer la vidéo dans un dossier nommé selon le jeu %s
|
||||
(X11 applications only)=(Applications X11 uniquement)
|
||||
|
||||
Add audio track=Ajouter une piste audio
|
||||
Add input device=Ajouter appareil d’entrée
|
||||
Add output device=Ajouter appareil de sortie
|
||||
Add application audio=Ajouter l’audio d’application
|
||||
Record audio from all applications except the selected ones=Enregistrer l’audio de toutes les applications sauf celles sélectionnées
|
||||
Recording output devices and application audio may record all output audio, which is likely\nnot what you want to do. Remove the output devices.=L’enregistrement des périphériques de sortie et de l’audio d’application peut enregistrer tout l’audio de sortie, ce qui n’est probablement pas souhaité.\nSupprimez les périphériques de sortie.
|
||||
|
||||
Video=Vidéo
|
||||
|
||||
# Video codec settings
|
||||
Video codec:=Codec vidéo
|
||||
H264=H264
|
||||
HEVC=HEVC
|
||||
HEVC (10 bit, reduces banding)=HEVC (10 bits, réduit le banding)
|
||||
HEVC (HDR)=HEVC (HDR)
|
||||
AV1=AV1
|
||||
AV1 (10 bit, reduces banding)=AV1 (10 bits, réduit le banding)
|
||||
AV1 (HDR)=AV1 (HDR)
|
||||
VP8=VP8
|
||||
VP9=VP9
|
||||
H264 Software Encoder (Slow, not recommended)=Encodeur logiciel H264 (lent, non recommandé)
|
||||
|
||||
# Video quality and bitrate
|
||||
Video quality:=Qualité vidéo
|
||||
Very high=Très élevée
|
||||
Video bitrate (Kbps):=Débit vidéo (Kbps)
|
||||
Constant bitrate=Débit constant
|
||||
Constant bitrate (Recommended)=Débit constant (recommandé)
|
||||
|
||||
# Frame rate settings
|
||||
Frame rate:=Fréquence d’images
|
||||
Frame rate mode:=Mode de fréquence d’images
|
||||
Auto (Recommended)=Automatique (recommandé)
|
||||
Constant=Constant
|
||||
Variable=Variable
|
||||
Sync to content=Synchroniser au contenu
|
||||
Sync to content (Only X11 or desktop portal capture)=Synchroniser au contenu (Uniquement capture X11 ou portail du bureau)
|
||||
|
||||
# Color range
|
||||
Color range:=Plage de couleurs
|
||||
Limited=Limitée
|
||||
Full=Complète
|
||||
|
||||
# Container format
|
||||
Container:=Format
|
||||
|
||||
# Recording settings
|
||||
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.
|
||||
|
||||
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
|
||||
Recording indicator=Indicateur d’enregistrement
|
||||
recording=Enregistrement
|
||||
|
||||
Simple=Simple
|
||||
Audio track #%d=Piste audio #%d
|
||||
Output device=Périphérique de sortie
|
||||
Input device: =Périphérique d’entrée
|
||||
Estimated video file size per minute (excluding audio): %.2fMB=Taille estimée du fichier vidéo par minute (hors audio) : %.2f Mo
|
||||
|
||||
# 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
|
||||
Autostart=Démarrage automatique
|
||||
in RAM=En RAM
|
||||
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
|
||||
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.
|
||||
|
||||
# Streaming settings
|
||||
Stream service:=Service de diffusion
|
||||
Twitch=Twitch
|
||||
YouTube=YouTube
|
||||
Kick=Kick
|
||||
Rumble=Rumble
|
||||
Custom=Personnalisé
|
||||
Stream URL:=URL du flux
|
||||
Stream key:=Clé de diffusion
|
||||
Streaming info=Infos diffusion
|
||||
Streaming indicator=Indicateur de diffusion
|
||||
streaming=Diffusion
|
||||
419
translations/hu.txt
Normal file
419
translations/hu.txt
Normal file
@@ -0,0 +1,419 @@
|
||||
# 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\nto "%s"=%s hosszú felvétel mentve erről: %s\nide: "%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\nto "%s"=%s hosszúságú visszajátszás mentve erről: %s\nide: "%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.\nYou may need to restart replay to apply the changes.=A visszajátszás beállításai módosultak.\nA 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.\nYou may need to restart streaming to apply the changes.=A közvetítés beállításai módosultak.\nA változtatások alkalmazásához újra kell indítani a közvetítést
|
||||
|
||||
# Notifications - Screenshot
|
||||
Saved a screenshot of %s\nto "%s"=Képernyőkép mentve (%s)\nide: "%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.\nPress Alt+Z to open the UI.=A GPU Screen Recorder UI már fut.\nNyomd meg az Alt+Z-t a felület megnyitásához.
|
||||
GPU Screen Recorder is already running in another process.\nPlease close it before using GPU Screen Recorder UI.=A GPU Screen Recorder már fut egy másik folyamatban.\nZárd be, mielőtt használod a felületet.
|
||||
Failed to start replay, capture target "%s" is invalid.\nPlease change capture target in settings=Nem sikerült elindítani a visszajátszást, a rögzítési cél "%s" érvénytelen.\nMódosítsd a rögzítési célt a beállításokban.
|
||||
Failed to start recording, capture target "%s" is invalid.\nPlease change capture target in settings=Nem sikerült elindítani a felvételt, a rögzítési cél "%s" érvénytelen.\nMódosítsd a rögzítési célt a beállításokban.
|
||||
Failed to start streaming, capture target "%s" is invalid.\nPlease 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.\nMódosítsd a rögzítési célt a beállításokban.
|
||||
Failed to take a screenshot, capture target "%s" is invalid.\nPlease 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.\nMódosítsd a rögzítési célt a beállításokban.
|
||||
|
||||
Unable to start recording when replay is turned on.\nTurn off replay before starting recording.=Nem lehet felvételt indítani, ha a visszajátszás be van kapcsolva.\nA felvétel előtt kapcsold ki a visszajátszást.
|
||||
Unable to start streaming when replay is turned on.\nTurn off replay before starting streaming.=Nem lehet közvetítést indítani, ha a visszajátszás be van kapcsolva.\nA közvetítés előtt kapcsold ki a visszajátszást.
|
||||
Unable to start streaming when recording.\nStop recording before starting streaming.=Nem lehet közvetítést indítani felvétel közben.\nÁllítsd le a felvételt a közvetítéshez.
|
||||
Unable to start recording when streaming.\nStop streaming before starting recording.=Nem lehet felvételt indítani közvetítés közben.\nÁllítsd le a közvetítést a felvételhez.
|
||||
Unable to start replay when recording.\nStop recording before starting replay.=Nem lehet visszajátszást indítani felvétel közben.\nÁllítsd le a felvételt a visszajátszáshoz.
|
||||
Unable to start replay when streaming.\nStop streaming before starting replay.=Nem lehet visszajátszást indítani közvetítés közben.\nÁ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.\nEither you canceled the desktop portal or your Wayland compositor doesn't support desktop portal capture\nor it's incorrectly setup on your system.=Az asztali portál rögzítése sikertelen.\nLehet, hogy megszakítottad, vagy a Wayland nem támogatja,\nvagy nincs megfelelően beállítva.
|
||||
Monitor capture failed.\nThe monitor you are trying to capture is invalid.\nPlease validate your capture settings.=A monitor rögzítése sikertelen.\nA kiválasztott monitor érvénytelen.\nEllenőrizd a beállításokat.
|
||||
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.=A rögzítés sikertelen. A H264, HEVC és AV1 kodekek nem támogatottak\nvagy túl nagy felbontást választottál.
|
||||
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.=A rögzítés sikertelen. A rendszer nem támogatja a választott felbontást.\nVálts felbontást vagy kodeket.\nMegjegyzé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.\nChange video codec and try again.=A rögzítés sikertelen. A választott kodek nem támogatott.\nVá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.\nTo migrate: install and configure 'dex' (recommended),\nor manually add '%s' to your desktop autostart entries.=A GPU Screen Recorder systemd-alapú automatikus indítása elavult.\nAz á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,\nthings may not work as expected. Use X11 if you experience issues.=A Wayland nem támogatja megfelelően a GPU Screen Recordert.\nHaszná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:
|
||||
|
||||
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.
|
||||
|
||||
Show %s notifications=%s értesítések megjelenítése
|
||||
Show %s status with scroll lock LED=%s állapot jelzése Scroll Lock LED-del
|
||||
Recording indicator=Felvétel-jelző
|
||||
recording=felvétel
|
||||
|
||||
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ő
|
||||
replay=visszajátszás
|
||||
Turn on replay when starting a fullscreen application%s=Visszajátszás bekapcsolása teljes képernyős alkalmazás indításakor%s
|
||||
Autostart=Automatikus indítás
|
||||
in RAM=RAM-ban
|
||||
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
|
||||
Turn on replay when power supply is connected=Visszajátszás bekapcsolása tápellátás csatlakoztatásakor
|
||||
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ő
|
||||
streaming=közvetítés
|
||||
@@ -98,7 +98,9 @@ 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.\nTo migrate: install and configure 'dex' (recommended),\nor manually add '%s' to your desktop autostart entries.=Автозапуск GPU Screen Recorder UI через systemd устарел.\nДля миграции: установите и настройте 'dex' (рекомендуется)\nили вручную добавьте '%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, если возникнут проблемы.
|
||||
@@ -266,8 +268,7 @@ Record cursor=Записывать курсор
|
||||
Directory to save screenshots:=Каталог для сохранения снимков:
|
||||
Image format:=Формат изображения:
|
||||
|
||||
Save screenshot in a folder based on the focused applications name=Сохранять снимок в папку по имени активного приложения
|
||||
Save screenshot in a folder based on the focused applications name (X11 applications only)=Сохранять снимок в папку по имени активного приложения (только X11-приложения)
|
||||
Save screenshot in a folder based on the games name=Сохраните скриншот в папку, названную в честь игры
|
||||
|
||||
Save screenshot to clipboard=Сохранять снимок в буфер обмена
|
||||
Save screenshot to clipboard (Not supported properly by Wayland)=Сохранять снимок в буфер обмена (в Wayland поддерживается некорректно)
|
||||
@@ -318,7 +319,8 @@ Input device: =Входное устройство:
|
||||
Application: =Приложение:
|
||||
Custom...=Другое...
|
||||
|
||||
Save video in a folder based on the focused applications name%s=Сохранять видео в папку по имени активного приложения%s
|
||||
Save video in a folder based on the games name%s=Сохраните видео в папку, названную в честь игры%s
|
||||
(X11 applications only)= (только X11-приложения)
|
||||
|
||||
Add audio track=Добавить аудиодорожку
|
||||
Add input device=Добавить вход
|
||||
@@ -373,7 +375,7 @@ 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для снижения энергопотребления в режиме ожидания.
|
||||
|
||||
Show %s notifications=Показывать уведомления %s
|
||||
Show %s status with scroll lock led=Показывать статус %s с помощью индикатора Scroll Lock
|
||||
Show %s status with scroll lock LED=Показывать статус %s с помощью индикатора Scroll Lock
|
||||
Recording indicator=Индикатор записи
|
||||
|
||||
Simple=Простой
|
||||
@@ -408,4 +410,4 @@ Custom=Другое
|
||||
Stream URL:=URL трансляции:
|
||||
Stream key:=Ключ трансляции:
|
||||
Streaming info=Информация о трансляции
|
||||
Streaming indicator=Индикатор трансляции
|
||||
Streaming indicator=Индикатор трансляции
|
||||
@@ -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=
|
||||
@@ -101,7 +103,9 @@ 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.\nTo migrate: install and configure 'dex' (recommended),\nor 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.=
|
||||
@@ -277,8 +281,7 @@ Record cursor=
|
||||
Directory to save screenshots:=
|
||||
Image format:=
|
||||
|
||||
Save screenshot in a folder based on the focused applications name=
|
||||
Save screenshot in a folder based on the focused applications name (X11 applications only)=
|
||||
Save screenshot in a folder based on the games name=
|
||||
|
||||
Save screenshot to clipboard=
|
||||
Save screenshot to clipboard (Not supported properly by Wayland)=
|
||||
@@ -330,7 +333,8 @@ Input device: =
|
||||
Application: =
|
||||
Custom...=
|
||||
|
||||
Save video in a folder based on the focused applications name%s=
|
||||
Save video in a folder based on the games name%s=
|
||||
(X11 applications only)=
|
||||
|
||||
Add audio track=
|
||||
Add input device=
|
||||
@@ -385,8 +389,9 @@ 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.=
|
||||
|
||||
Show %s notifications=
|
||||
Show %s status with scroll lock led=
|
||||
Show %s status with scroll lock LED=
|
||||
Recording indicator=
|
||||
recording=
|
||||
|
||||
Simple=
|
||||
Audio track #%d=
|
||||
@@ -397,6 +402,7 @@ 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=
|
||||
Autostart=
|
||||
in RAM=
|
||||
@@ -421,3 +427,4 @@ Stream URL:=
|
||||
Stream key:=
|
||||
Streaming info=
|
||||
Streaming indicator=
|
||||
streaming=
|
||||
@@ -98,7 +98,9 @@ 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.\nTo migrate: install and configure 'dex' (recommended),\nor manually add '%s' to your desktop autostart entries.=Автозапуск GPU Screen Recorder UI через systemd застарів.\nДля міграції: встановіть та налаштуйте 'dex' (рекомендовано)\nабо вручну додайте '%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, якщо виникнуть проблеми.
|
||||
@@ -265,8 +267,7 @@ Record cursor=Записувати курсор
|
||||
Directory to save screenshots:=Каталог для збереження знімків:
|
||||
Image format:=Формат зображення:
|
||||
|
||||
Save screenshot in a folder based on the focused applications name=Зберігати знімок у папку за ім'ям активної програми
|
||||
Save screenshot in a folder based on the focused applications name (X11 applications only)=Зберігати знімок у папку за ім'ям активної програми (лише X11-програми)
|
||||
Save screenshot in a folder based on the games name=Зберегти скріншот у папці на основі назви гри
|
||||
|
||||
Save screenshot to clipboard=Зберігати знімок до буфера обміну
|
||||
Save screenshot to clipboard (Not supported properly by Wayland)=Зберігати знімок до буфера обміну (в Wayland підтримується некоректно)
|
||||
@@ -316,7 +317,8 @@ Input device: =Вхідний пристрій:
|
||||
Application: =Програма:
|
||||
Custom...=Інше...
|
||||
|
||||
Save video in a folder based on the focused applications name%s=Зберігати відео у папку за ім'ям активної програми%s
|
||||
Save video in a folder based on the games name%s=Збереження відео в папці на основі назви гри%s
|
||||
(X11 applications only)= (лише X11-програми)
|
||||
|
||||
Add audio track=Додати аудіодоріжку
|
||||
Add input device=Додати вхід
|
||||
@@ -371,7 +373,7 @@ 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для зниження енергоспоживання в режимі очікування.
|
||||
|
||||
Show %s notifications=Показувати сповіщення %s
|
||||
Show %s status with scroll lock led=Показувати статус %s за допомогою індикатора Scroll Lock
|
||||
Show %s status with scroll lock LED=Показувати статус %s за допомогою індикатора Scroll Lock
|
||||
Recording indicator=Індикатор запису
|
||||
|
||||
Simple=Простий
|
||||
|
||||
Reference in New Issue
Block a user