Prepare for sound. Fix game name being gsr-ui on wayland in some cases when saving video when the ui is open

This commit is contained in:
dec05eba
2025-02-10 18:22:21 +01:00
parent 3d6354c642
commit f4e44cbef5
10 changed files with 163 additions and 4 deletions

View File

@@ -27,6 +27,7 @@ These are the dependencies needed to build GPU Screen Recorder UI:
* libxcursor * libxcursor
* libglvnd (which provides libgl, libglx and libegl) * libglvnd (which provides libgl, libglx and libegl)
* linux-api-headers * linux-api-headers
* libpulse (libpulse-simple)
## Runtime dependencies ## Runtime dependencies
There are also additional dependencies needed at runtime: There are also additional dependencies needed at runtime:

3
TODO
View File

@@ -117,3 +117,6 @@ Instead of installing gsr-global-hotkeys in flatpak use kms-server-proxy to laun
Check if "modprobe uinput" is needed on some systems (old fedora?). Check if "modprobe uinput" is needed on some systems (old fedora?).
Add recording timer to see duration of recording/streaming. Add recording timer to see duration of recording/streaming.
Saving video into a folder with the name of the game doesn't always work on wayland. This happens when trying to save a video with the ui open and the ui opens without override redirect.
Maybe a solution would be to query all windows (top to bottom) and check which window the cursor is inside, ignoring the gsr-ui window

22
include/AudioPlayer.hpp Normal file
View File

@@ -0,0 +1,22 @@
#pragma once
#include <thread>
namespace gsr {
// Only plays raw stereo PCM audio in 48000hz in s16le format.
// Use this command to convert an audio file (input.wav) to a format playable by this class (output.pcm):
// ffmpeg -i input.wav -f s16le -acodec pcm_s16le -ar 48000 output.pcm
class AudioPlayer {
public:
AudioPlayer() = default;
~AudioPlayer();
AudioPlayer(const AudioPlayer&) = delete;
AudioPlayer& operator=(const AudioPlayer&) = delete;
bool play(const char *filepath);
private:
std::thread thread;
bool stop_playing_audio = false;
int audio_file_fd = -1;
};
}

View File

@@ -8,6 +8,7 @@
#include "WindowUtils.hpp" #include "WindowUtils.hpp"
#include "GlobalHotkeysLinux.hpp" #include "GlobalHotkeysLinux.hpp"
#include "GlobalHotkeysJoystick.hpp" #include "GlobalHotkeysJoystick.hpp"
#include "AudioPlayer.hpp"
#include <mglpp/window/Window.hpp> #include <mglpp/window/Window.hpp>
#include <mglpp/window/Event.hpp> #include <mglpp/window/Event.hpp>
@@ -189,5 +190,7 @@ namespace gsr {
mgl::Clock replay_save_clock; mgl::Clock replay_save_clock;
bool replay_save_show_notification = false; bool replay_save_show_notification = false;
AudioPlayer audio_player;
}; };
} }

View File

@@ -18,6 +18,8 @@ namespace gsr {
Window get_focused_window(Display *dpy, WindowCaptureType cap_type); Window get_focused_window(Display *dpy, WindowCaptureType cap_type);
std::string get_focused_window_name(Display *dpy, WindowCaptureType window_capture_type); std::string get_focused_window_name(Display *dpy, WindowCaptureType window_capture_type);
std::string get_window_name_at_position(Display *dpy, mgl::vec2i position, Window ignore_window);
std::string get_window_name_at_cursor_position(Display *dpy, Window ignore_window);
mgl::vec2i get_cursor_position(Display *dpy, Window *window); mgl::vec2i get_cursor_position(Display *dpy, Window *window);
mgl::vec2i create_window_get_center_position(Display *display); mgl::vec2i create_window_get_center_position(Display *display);
std::string get_window_manager_name(Display *display); std::string get_window_manager_name(Display *display);

View File

@@ -39,6 +39,7 @@ src = [
'src/GlobalHotkeysX11.cpp', 'src/GlobalHotkeysX11.cpp',
'src/GlobalHotkeysLinux.cpp', 'src/GlobalHotkeysLinux.cpp',
'src/GlobalHotkeysJoystick.cpp', 'src/GlobalHotkeysJoystick.cpp',
'src/AudioPlayer.cpp',
'src/Hotplug.cpp', 'src/Hotplug.cpp',
'src/Rpc.cpp', 'src/Rpc.cpp',
'src/main.cpp', 'src/main.cpp',
@@ -65,6 +66,7 @@ executable(
dependency('xfixes'), dependency('xfixes'),
dependency('xi'), dependency('xi'),
dependency('xcursor'), dependency('xcursor'),
dependency('libpulse-simple'),
], ],
cpp_args : '-DGSR_UI_RESOURCES_PATH="' + gsr_ui_resources_path + '"', cpp_args : '-DGSR_UI_RESOURCES_PATH="' + gsr_ui_resources_path + '"',
) )

View File

@@ -15,3 +15,4 @@ xcomposite = ">=0"
xfixes = ">=0" xfixes = ">=0"
xi = ">=0" xi = ">=0"
xcursor = ">=1" xcursor = ">=1"
libpulse-simple = ">=0"

87
src/AudioPlayer.cpp Normal file
View File

@@ -0,0 +1,87 @@
#include "../include/AudioPlayer.hpp"
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <pulse/simple.h>
#include <pulse/error.h>
#define BUFSIZE 4096
namespace gsr {
AudioPlayer::~AudioPlayer() {
if(thread.joinable()) {
stop_playing_audio = true;
thread.join();
}
if(audio_file_fd > 0)
close(audio_file_fd);
}
bool AudioPlayer::play(const char *filepath) {
if(thread.joinable()) {
stop_playing_audio = true;
thread.join();
}
stop_playing_audio = false;
audio_file_fd = open(filepath, O_RDONLY);
if(audio_file_fd == -1)
return false;
thread = std::thread([this]() {
const pa_sample_spec ss = {
.format = PA_SAMPLE_S16LE,
.rate = 48000,
.channels = 2
};
pa_simple *s = NULL;
int error;
/* Create a new playback stream */
if(!(s = pa_simple_new(NULL, "gsr-ui-audio-playback", PA_STREAM_PLAYBACK, NULL, "playback", &ss, NULL, NULL, &error))) {
fprintf(stderr, __FILE__": pa_simple_new() failed: %s\n", pa_strerror(error));
goto finish;
}
uint8_t buf[BUFSIZE];
for(;;) {
ssize_t r;
if(stop_playing_audio)
goto finish;
if((r = read(audio_file_fd, buf, sizeof(buf))) <= 0) {
if(r == 0) /* EOF */
break;
fprintf(stderr, __FILE__": read() failed: %s\n", strerror(errno));
goto finish;
}
if(pa_simple_write(s, buf, (size_t) r, &error) < 0) {
fprintf(stderr, __FILE__": pa_simple_write() failed: %s\n", pa_strerror(error));
goto finish;
}
}
if(pa_simple_drain(s, &error) < 0) {
fprintf(stderr, __FILE__": pa_simple_drain() failed: %s\n", pa_strerror(error));
goto finish;
}
finish:
if(s)
pa_simple_free(s);
close(audio_file_fd);
audio_file_fd = -1;
});
return true;
}
}

View File

@@ -1450,7 +1450,8 @@ namespace gsr {
Display *display = (Display*)context->connection; Display *display = (Display*)context->connection;
const std::string video_filename = filepath_get_filename(video_filepath); const std::string video_filename = filepath_get_filename(video_filepath);
std::string focused_window_name = get_focused_window_name(display, WindowCaptureType::FOCUSED); const Window gsr_ui_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()) if(focused_window_name.empty())
focused_window_name = "Game"; focused_window_name = "Game";

View File

@@ -105,7 +105,6 @@ namespace gsr {
unsigned int dummy_u; unsigned int dummy_u;
mgl::vec2i root_pos; mgl::vec2i root_pos;
XQueryPointer(dpy, DefaultRootWindow(dpy), &root_window, window, &root_pos.x, &root_pos.y, &dummy_i, &dummy_i, &dummy_u); XQueryPointer(dpy, DefaultRootWindow(dpy), &root_window, window, &root_pos.x, &root_pos.y, &dummy_i, &dummy_i, &dummy_u);
if(window)
*window = window_get_target_window_child(dpy, *window); *window = window_get_target_window_child(dpy, *window);
return root_pos; return root_pos;
} }
@@ -236,6 +235,44 @@ namespace gsr {
return result; return result;
} }
std::string get_window_name_at_position(Display *dpy, mgl::vec2i position, Window ignore_window) {
std::string result;
Window root;
Window parent;
Window *children = nullptr;
unsigned int num_children = 0;
if(!XQueryTree(dpy, DefaultRootWindow(dpy), &root, &parent, &children, &num_children) || !children)
return result;
for(int i = (int)num_children - 1; i >= 0; --i) {
if(children[i] == ignore_window)
continue;
XWindowAttributes attr;
memset(&attr, 0, sizeof(attr));
XGetWindowAttributes(dpy, children[i], &attr);
if(attr.override_redirect || attr.c_class != InputOutput)
continue;
if(position.x >= attr.x && position.x <= attr.x + attr.width && position.y >= attr.y && position.y <= attr.y + attr.height && window_is_user_program(dpy, children[i])) {
const std::optional<std::string> window_title = get_window_title(dpy, children[i]);
if(window_title)
result = strip(window_title.value());
break;
}
}
XFree(children);
return result;
}
std::string get_window_name_at_cursor_position(Display *dpy, Window ignore_window) {
Window cursor_window;
const mgl::vec2i cursor_position = get_cursor_position(dpy, &cursor_window);
return get_window_name_at_position(dpy, cursor_position, ignore_window);
}
typedef struct { typedef struct {
unsigned long flags; unsigned long flags;
unsigned long functions; unsigned long functions;