mirror of
https://repo.dec05eba.com/gpu-screen-recorder-ui
synced 2026-01-31 01:13:04 +09:00
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:
@@ -27,6 +27,7 @@ These are the dependencies needed to build GPU Screen Recorder UI:
|
||||
* libxcursor
|
||||
* libglvnd (which provides libgl, libglx and libegl)
|
||||
* linux-api-headers
|
||||
* libpulse (libpulse-simple)
|
||||
|
||||
## Runtime dependencies
|
||||
There are also additional dependencies needed at runtime:
|
||||
|
||||
5
TODO
5
TODO
@@ -116,4 +116,7 @@ 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?).
|
||||
|
||||
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
22
include/AudioPlayer.hpp
Normal 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;
|
||||
};
|
||||
}
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "WindowUtils.hpp"
|
||||
#include "GlobalHotkeysLinux.hpp"
|
||||
#include "GlobalHotkeysJoystick.hpp"
|
||||
#include "AudioPlayer.hpp"
|
||||
|
||||
#include <mglpp/window/Window.hpp>
|
||||
#include <mglpp/window/Event.hpp>
|
||||
@@ -189,5 +190,7 @@ namespace gsr {
|
||||
|
||||
mgl::Clock replay_save_clock;
|
||||
bool replay_save_show_notification = false;
|
||||
|
||||
AudioPlayer audio_player;
|
||||
};
|
||||
}
|
||||
@@ -18,6 +18,8 @@ namespace gsr {
|
||||
|
||||
Window get_focused_window(Display *dpy, WindowCaptureType cap_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 create_window_get_center_position(Display *display);
|
||||
std::string get_window_manager_name(Display *display);
|
||||
|
||||
@@ -39,6 +39,7 @@ src = [
|
||||
'src/GlobalHotkeysX11.cpp',
|
||||
'src/GlobalHotkeysLinux.cpp',
|
||||
'src/GlobalHotkeysJoystick.cpp',
|
||||
'src/AudioPlayer.cpp',
|
||||
'src/Hotplug.cpp',
|
||||
'src/Rpc.cpp',
|
||||
'src/main.cpp',
|
||||
@@ -65,6 +66,7 @@ executable(
|
||||
dependency('xfixes'),
|
||||
dependency('xi'),
|
||||
dependency('xcursor'),
|
||||
dependency('libpulse-simple'),
|
||||
],
|
||||
cpp_args : '-DGSR_UI_RESOURCES_PATH="' + gsr_ui_resources_path + '"',
|
||||
)
|
||||
|
||||
@@ -15,3 +15,4 @@ xcomposite = ">=0"
|
||||
xfixes = ">=0"
|
||||
xi = ">=0"
|
||||
xcursor = ">=1"
|
||||
libpulse-simple = ">=0"
|
||||
87
src/AudioPlayer.cpp
Normal file
87
src/AudioPlayer.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -1450,7 +1450,8 @@ namespace gsr {
|
||||
Display *display = (Display*)context->connection;
|
||||
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())
|
||||
focused_window_name = "Game";
|
||||
|
||||
|
||||
@@ -105,8 +105,7 @@ namespace gsr {
|
||||
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);
|
||||
if(window)
|
||||
*window = window_get_target_window_child(dpy, *window);
|
||||
*window = window_get_target_window_child(dpy, *window);
|
||||
return root_pos;
|
||||
}
|
||||
|
||||
@@ -236,6 +235,44 @@ namespace gsr {
|
||||
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 {
|
||||
unsigned long flags;
|
||||
unsigned long functions;
|
||||
|
||||
Reference in New Issue
Block a user