aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2025-01-03 01:57:30 +0100
committerdec05eba <dec05eba@protonmail.com>2025-01-03 01:57:30 +0100
commit2a07c741121e737aec9bdd5214ba66efbf252da0 (patch)
treea717b3328ec4bfcf87d7aa7281261f3e42519537
parentf526c175f6e2b852c8fe19b666505cfeac36b667 (diff)
Fix some application (using xi) receiving mouse input when UI is shown
-rw-r--r--README.md2
-rw-r--r--TODO30
-rw-r--r--include/Overlay.hpp5
-rw-r--r--meson.build1
-rw-r--r--project.conf3
-rw-r--r--src/GlobalHotkeysLinux.cpp2
-rw-r--r--src/Overlay.cpp182
-rw-r--r--src/Process.cpp4
-rw-r--r--src/WindowUtils.cpp47
-rw-r--r--src/gui/SettingsPage.cpp16
10 files changed, 162 insertions, 130 deletions
diff --git a/README.md b/README.md
index 125a01d..5cb9824 100644
--- a/README.md
+++ b/README.md
@@ -24,6 +24,7 @@ GPU Screen Recorder UI uses meson build system so you need to install `meson` to
These are the dependencies needed to build GPU Screen Recorder UI:
* x11 (libx11, libxrandr, libxrender, libxcomposite, libxfixes, libxi)
+* libxcursor
* libglvnd (which provides libgl, libglx and libegl)
* linux-api-headers
@@ -49,6 +50,5 @@ If you want to donate you can donate via bitcoin or monero.
* Monero: 4An9kp2qW1C9Gah7ewv4JzcNFQ5TAX7ineGCqXWK6vQnhsGGcRpNgcn8r9EC3tMcgY7vqCKs3nSRXhejMHBaGvFdN2egYet
# Known issues
-* Some games receive mouse input while the UI is open
* When the UI is open the wallpaper is shown instead of the game on Hyprland and Sway. This is an issue with Hyprland and Sway. It cant be fixed until the UI is redesigned to not be a fullscreen overlay.
* Different keyboard layouts are not supported at the moment. The physical layout of keys are used for global hotkeys. If your Z and Y keys are swapped for example then you need to press Alt+Y instead of Alt+Z to open/hide the UI. \ No newline at end of file
diff --git a/TODO b/TODO
index 631764c..c8d095a 100644
--- a/TODO
+++ b/TODO
@@ -1,12 +1,4 @@
-setcap nice for good performance when opening overlay when game is running below 60 fps.
-Maybe grab cursor with xi, as that will prevent games from detecting movement with xi2 api.
-
-Fullscreen on wayland doesn't render windows behind because it's a compositor optimization, to not draw anything behind (only draw the window directly without compositing).
-Fix this by drawing the window smaller, or have two windows (left and right half monitor width).
-Maybe change design to have black triangles appear and get larger until they fill the screen, with even spaces being left with no triangles.
- Exclude triangles from a diagonal line across the screen.
- Have buttons appear slanted in 3D.
- All of these things should be done with vertex buffer, for real 3D.
+setcap nice for good performance when opening overlay when game is running below 60 fps (on amd).
WAYLAND_DISPLAY gamescope-0, DISPLAY=:1 (gamescope xwayland)
@@ -47,7 +39,7 @@ Add global setting. In that setting there should be an option to enable/disable
Add profiles and hotkey to switch between profiles (show notification when switching profile).
-Fix first frame being black.
+Fix first frame being black when running without a compositor.
Add support for systray.
@@ -70,24 +62,17 @@ On nvidia check if suspend fix is applied. If not, show a popup asking the user
Show warning when using steam deck or when trying to capture hevc/av1 on amd (the same warnings as gpu screen recorder gtk).
-Add option to capture application audio. This should show a popup where you can use one of the available applications or a custom one and choose to record that application or all applications except that one.
-
Add profile option. Convert view to profile, add an option at the bottom that says "Edit profiles..." which should show a popup where you can create/remove profiles. New profiles should always be in advanced view.
Verify monitor/audio when starting recording. Give an error if the options are no longer valid.
-Get focused window when opening gsr-ui and pass that to the save replay script, to ignore gsr-ui when getting game name.
-
-gsr ui window has _NET_WM_STATE _NET_WM_STATE_ABOVE, not _NET_WM_STATE_FULLSCREEN
+gsr ui window has _NET_WM_STATE _NET_WM_STATE_ABOVE, not _NET_WM_STATE_FULLSCREEN.
For replay on fullscreen detect focused fullscreen window by checking if the window size is the same as the monitor size instead of _NET_WM_STATE_FULLSCREEN.
-Add audio devices/app refresh button.
-
Play camera shutter sound when saving recording. When another sound when starting recording.
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.
-Try to reproduce this and if it happens try grab cursor and keyboard instead of setting gsr ui focus and make gsr ui click through like gsr notify. This might fix the issue.
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.
@@ -95,8 +80,6 @@ Add option to select which gpu to record with, or list all monitors and automati
Test global hotkeys with azerty instead of qwerty.
-Fix cursor grab not working in owlboy, need to use xigrab.
-
Dont allow autostart of replay if capture option is window recording (when window recording is added).
Use global shortcuts desktop portal protocol on wayland when available.
@@ -117,10 +100,9 @@ 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 a popup asking if the user wants to add the program to system startup when launching the program, with a dismiss option and "Do not show again".
-
Show warning if another instance of gpu screen recorder is already running when starting recording?
-Change gsr-global-hotkeys to outputs keys pressed instead of the command. This can be done pretty safely by only output keys when modifiers (such as alt) is pressed.
+Keyboard leds get turned off when stopping gsr-global-hotkeys (for example numlock). The numlock key has to be pressed twice again to make it look correct to match its state.
-Keyboard leds get turned off when stopping gsr-global-hotkeys (for example numlock). The numlock key has to be pressed twice again to make it look correct to match its state. \ No newline at end of file
+Implement hotkey changing in global settings by getting mgl key events. During this time gsr-global-hotkey would either need to be paused or add code in the callback handler for the existing hotkeys since they are grabbing hotkeys.
+ This can only be done after gsr-global-hotkeys properly handle different keyboard layouts to make sure mgl keys match gsr-global-hotkey keys. \ No newline at end of file
diff --git a/include/Overlay.hpp b/include/Overlay.hpp
index 50a38cd..596d0fc 100644
--- a/include/Overlay.hpp
+++ b/include/Overlay.hpp
@@ -65,7 +65,6 @@ namespace gsr {
void grab_mouse_and_keyboard();
void xi_setup_fake_cursor();
void xi_grab_all_devices();
- void xi_warp_pointer(mgl::vec2i position);
void close_gpu_screen_recorder_output();
@@ -124,7 +123,6 @@ namespace gsr {
mgl::Texture cursor_texture;
mgl::Sprite cursor_sprite;
mgl::vec2i cursor_hotspot;
- bool cursor_drawn = false;
WindowTexture window_texture;
PageStack page_stack;
@@ -166,5 +164,8 @@ namespace gsr {
bool do_exit = false;
std::string exit_reason;
+
+ mgl::vec2i window_size = { 1280, 720 };
+ mgl::vec2i window_pos = { 0, 0 };
};
} \ No newline at end of file
diff --git a/meson.build b/meson.build
index 321d0c3..00612e3 100644
--- a/meson.build
+++ b/meson.build
@@ -58,6 +58,7 @@ executable(
dependency('xcomposite'),
dependency('xfixes'),
dependency('xi'),
+ dependency('xcursor'),
],
cpp_args : '-DGSR_UI_RESOURCES_PATH="' + gsr_ui_resources_path + '"',
)
diff --git a/project.conf b/project.conf
index 3d84f6a..98f6720 100644
--- a/project.conf
+++ b/project.conf
@@ -13,4 +13,5 @@ ignore_dirs = ["build", "tools"]
[dependencies]
xcomposite = ">=0"
xfixes = ">=0"
-xi = ">=0" \ No newline at end of file
+xi = ">=0"
+xcursor = ">=1" \ No newline at end of file
diff --git a/src/GlobalHotkeysLinux.cpp b/src/GlobalHotkeysLinux.cpp
index d16cc06..b1f59b1 100644
--- a/src/GlobalHotkeysLinux.cpp
+++ b/src/GlobalHotkeysLinux.cpp
@@ -68,7 +68,7 @@ namespace gsr {
execvp(args[0], (char* const*)args);
}
- perror("execvp");
+ perror("gsr-global-hotkeys");
_exit(127);
} else { /* parent */
process_id = pid;
diff --git a/src/Overlay.cpp b/src/Overlay.cpp
index 268e264..896f611 100644
--- a/src/Overlay.cpp
+++ b/src/Overlay.cpp
@@ -29,6 +29,7 @@
#include <X11/extensions/Xfixes.h>
#include <X11/extensions/XInput2.h>
#include <X11/extensions/shape.h>
+#include <X11/Xcursor/Xcursor.h>
#include <mglpp/system/Rect.hpp>
#include <mglpp/window/Event.hpp>
@@ -45,6 +46,10 @@ namespace gsr {
return get_focused_window(dpy, WindowCaptureType::FOCUSED) == 0;
}
+ static bool is_cursor_hovering_application_wayland(Display *dpy) {
+ return get_focused_window(dpy, WindowCaptureType::CURSOR) == 0;
+ }
+
static mgl::Texture texture_from_ximage(XImage *img) {
uint8_t *texture_data = (uint8_t*)malloc(img->width * img->height * 3);
// TODO:
@@ -70,17 +75,17 @@ namespace gsr {
return texture;
}
- static bool texture_from_x11_cursor(XFixesCursorImage *x11_cursor_image, bool *visible, mgl::vec2i *hotspot, mgl::Texture &texture) {
+ static bool texture_from_x11_cursor(XcursorImage *x11_cursor_image, bool *visible, mgl::vec2i *hotspot, mgl::Texture &texture) {
uint8_t *cursor_data = NULL;
uint8_t *out = NULL;
- const unsigned long *pixels = NULL;
+ const unsigned int *pixels = NULL;
*visible = false;
if(!x11_cursor_image)
- goto err;
+ return false;
if(!x11_cursor_image->pixels)
- goto err;
+ return false;
hotspot->x = x11_cursor_image->xhot;
hotspot->y = x11_cursor_image->yhot;
@@ -88,12 +93,12 @@ namespace gsr {
pixels = x11_cursor_image->pixels;
cursor_data = (uint8_t*)malloc((int)x11_cursor_image->width * (int)x11_cursor_image->height * 4);
if(!cursor_data)
- goto err;
+ return false;
out = cursor_data;
/* Un-premultiply alpha */
- for(int y = 0; y < x11_cursor_image->height; ++y) {
- for(int x = 0; x < x11_cursor_image->width; ++x) {
+ for(uint32_t y = 0; y < x11_cursor_image->height; ++y) {
+ for(uint32_t x = 0; x < x11_cursor_image->width; ++x) {
uint32_t pixel = *pixels++;
uint8_t *in = (uint8_t*)&pixel;
uint8_t alpha = in[3];
@@ -114,13 +119,7 @@ namespace gsr {
texture.load_from_memory(cursor_data, x11_cursor_image->width, x11_cursor_image->height, MGL_IMAGE_FORMAT_RGBA);
free(cursor_data);
- XFree(x11_cursor_image);
return true;
-
- err:
- if(x11_cursor_image)
- XFree(x11_cursor_image);
- return false;
}
static char hex_value_to_str(uint8_t v) {
@@ -299,6 +298,16 @@ namespace gsr {
return &win->monitors[0];
}
+ static mgl::vec2i get_cursor_position(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 root_pos;
+ }
+
static mgl::vec2i create_window_get_center_position(Display *display) {
const int size = 16;
XSetWindowAttributes window_attr;
@@ -431,9 +440,6 @@ namespace gsr {
top_bar_background({0.0f, 0.0f}),
close_button_widget({0.0f, 0.0f})
{
- // TODO:
- //xi_setup();
-
key_bindings[0].key_event.code = mgl::Keyboard::Escape;
key_bindings[0].key_event.alt = false;
key_bindings[0].key_event.control = false;
@@ -481,11 +487,6 @@ namespace gsr {
}
close_gpu_screen_recorder_output();
-
- free(xi_input_xev);
- free(xi_output_xev);
- if(xi_display)
- XCloseDisplay(xi_display);
}
void Overlay::xi_setup() {
@@ -509,6 +510,7 @@ namespace gsr {
unsigned char mask[XIMaskLen(XI_LASTEVENT)];
memset(mask, 0, sizeof(mask));
XISetMask(mask, XI_Motion);
+ //XISetMask(mask, XI_RawMotion);
XISetMask(mask, XI_ButtonPress);
XISetMask(mask, XI_ButtonRelease);
XISetMask(mask, XI_KeyPress);
@@ -567,8 +569,8 @@ namespace gsr {
xi_output_xev->xmotion.display = display;
xi_output_xev->xmotion.window = window->get_system_handle();
xi_output_xev->xmotion.subwindow = window->get_system_handle();
- xi_output_xev->xmotion.x = de->event_x;
- xi_output_xev->xmotion.y = de->event_y;
+ xi_output_xev->xmotion.x = de->root_x - window_pos.x;
+ xi_output_xev->xmotion.y = de->root_y - window_pos.y;
xi_output_xev->xmotion.x_root = de->root_x;
xi_output_xev->xmotion.y_root = de->root_y;
//xi_output_xev->xmotion.state = // modifiers // TODO:
@@ -580,8 +582,8 @@ namespace gsr {
xi_output_xev->xbutton.display = display;
xi_output_xev->xbutton.window = window->get_system_handle();
xi_output_xev->xbutton.subwindow = window->get_system_handle();
- xi_output_xev->xbutton.x = de->event_x;
- xi_output_xev->xbutton.y = de->event_y;
+ xi_output_xev->xbutton.x = de->root_x - window_pos.x;
+ xi_output_xev->xbutton.y = de->root_y - window_pos.y;
xi_output_xev->xbutton.x_root = de->root_x;
xi_output_xev->xbutton.y_root = de->root_y;
//xi_output_xev->xbutton.state = // modifiers // TODO:
@@ -594,8 +596,8 @@ namespace gsr {
xi_output_xev->xkey.display = display;
xi_output_xev->xkey.window = window->get_system_handle();
xi_output_xev->xkey.subwindow = window->get_system_handle();
- xi_output_xev->xkey.x = de->event_x;
- xi_output_xev->xkey.y = de->event_y;
+ xi_output_xev->xkey.x = de->root_x - window_pos.x;
+ xi_output_xev->xkey.y = de->root_y - window_pos.y;
xi_output_xev->xkey.x_root = de->root_x;
xi_output_xev->xkey.y_root = de->root_y;
xi_output_xev->xkey.state = de->mods.effective;
@@ -694,12 +696,6 @@ namespace gsr {
page_stack.draw(*window, mgl::vec2f(0.0f, 0.0f));
if(cursor_texture.is_valid()) {
- if(!cursor_drawn) {
- cursor_drawn = true;
- XFixesHideCursor(xi_display, DefaultRootWindow(xi_display));
- XFlush(xi_display);
- }
-
cursor_sprite.set_position((window->get_mouse_position() - cursor_hotspot).to_vec2f());
window->draw(cursor_sprite);
}
@@ -737,45 +733,35 @@ namespace gsr {
if(!xi_display)
return;
- XFixesShowCursor(xi_display, DefaultRootWindow(xi_display));
+ XFixesHideCursor(xi_display, DefaultRootWindow(xi_display));
XFlush(xi_display);
- bool cursor_visible = false;
- texture_from_x11_cursor(XFixesGetCursorImage(xi_display), &cursor_visible, &cursor_hotspot, cursor_texture);
- if(cursor_texture.is_valid())
- cursor_sprite.set_texture(&cursor_texture);
- }
+ // TODO: XCURSOR_SIZE and XCURSOR_THEME environment variables
+ const char *cursor_theme = XcursorGetTheme(xi_display);
+ if(!cursor_theme) {
+ //fprintf(stderr, "Warning: failed to get cursor theme, using \"default\" theme instead\n");
+ cursor_theme = "default";
+ }
- void Overlay::xi_grab_all_devices() {
- if(!xi_display)
- return;
+ int cursor_size = XcursorGetDefaultSize(xi_display);
+ if(cursor_size <= 1)
+ cursor_size = 24;
- int num_devices = 0;
- XIDeviceInfo *info = XIQueryDevice(xi_display, XIAllDevices, &num_devices);
- if(!info)
+ XcursorImage *cursor_image = XcursorShapeLoadImage(XC_left_ptr, cursor_theme, cursor_size);
+ if(!cursor_image) {
+ fprintf(stderr, "Error: failed to get cursor\n");
return;
-
- for (int i = 0; i < num_devices; ++i) {
- const XIDeviceInfo *dev = &info[i];
- XIEventMask masks[1];
- unsigned char mask0[XIMaskLen(XI_LASTEVENT)];
- memset(mask0, 0, sizeof(mask0));
- XISetMask(mask0, XI_Motion);
- XISetMask(mask0, XI_ButtonPress);
- XISetMask(mask0, XI_ButtonRelease);
- XISetMask(mask0, XI_KeyPress);
- XISetMask(mask0, XI_KeyRelease);
- masks[0].deviceid = dev->deviceid;
- masks[0].mask_len = sizeof(mask0);
- masks[0].mask = mask0;
- XIGrabDevice(xi_display, dev->deviceid, window->get_system_handle(), CurrentTime, None, XIGrabModeAsync, XIGrabModeAsync, XIOwnerEvents, masks);
}
- XFlush(xi_display);
- XIFreeDeviceInfo(info);
+ bool cursor_visible = false;
+ texture_from_x11_cursor(cursor_image, &cursor_visible, &cursor_hotspot, cursor_texture);
+ if(cursor_texture.is_valid())
+ cursor_sprite.set_texture(&cursor_texture);
+
+ XcursorImageDestroy(cursor_image);
}
- void Overlay::xi_warp_pointer(mgl::vec2i position) {
+ void Overlay::xi_grab_all_devices() {
if(!xi_display)
return;
@@ -784,9 +770,22 @@ namespace gsr {
if(!info)
return;
+ unsigned char mask[XIMaskLen(XI_LASTEVENT)];
+ memset(mask, 0, sizeof(mask));
+ XISetMask(mask, XI_Motion);
+ //XISetMask(mask, XI_RawMotion);
+ XISetMask(mask, XI_ButtonPress);
+ XISetMask(mask, XI_ButtonRelease);
+ XISetMask(mask, XI_KeyPress);
+ XISetMask(mask, XI_KeyRelease);
+
for (int i = 0; i < num_devices; ++i) {
const XIDeviceInfo *dev = &info[i];
- XIWarpPointer(xi_display, dev->deviceid, DefaultRootWindow(xi_display), DefaultRootWindow(xi_display), 0, 0, 0, 0, position.x, position.y);
+ XIEventMask xi_masks;
+ xi_masks.deviceid = dev->deviceid;
+ xi_masks.mask_len = sizeof(mask);
+ xi_masks.mask = mask;
+ XIGrabDevice(xi_display, dev->deviceid, window->get_system_handle(), CurrentTime, None, XIGrabModeAsync, XIGrabModeAsync, XIOwnerEvents, &xi_masks);
}
XFlush(xi_display);
@@ -810,8 +809,8 @@ namespace gsr {
// a fullscreen window for the ui.
const bool prevent_game_minimizing = gsr_info.system_info.display_server != DisplayServer::WAYLAND || !is_focused_application_wayland(display);
- mgl::vec2i window_size = { 1280, 720 };
- mgl::vec2i window_pos = { 0, 0 };
+ window_size = { 1280, 720 };
+ window_pos = { 0, 0 };
mgl::Window::CreateParams window_create_params;
window_create_params.size = window_size;
@@ -846,7 +845,8 @@ namespace gsr {
}
// The cursor position is wrong on wayland if an x11 window is not focused. On wayland we instead create a window and get the position where the wayland compositor puts it
- const mgl::vec2i monitor_position_query_value = gsr_info.system_info.display_server == DisplayServer::WAYLAND ? create_window_get_center_position(display) : mgl::vec2i(win->cursor_position.x, win->cursor_position.y);
+ const mgl::vec2i cursor_position = get_cursor_position(display);
+ const mgl::vec2i monitor_position_query_value = gsr_info.system_info.display_server == DisplayServer::WAYLAND ? create_window_get_center_position(display) : cursor_position;
const mgl_monitor *focused_monitor = find_monitor_at_position(*window, monitor_position_query_value);
window_pos = {focused_monitor->pos.x, focused_monitor->pos.y};
window_size = {focused_monitor->size.x, focused_monitor->size.y};
@@ -856,6 +856,9 @@ namespace gsr {
window->set_size_limits(window_size, window_size);
window->set_position(window_pos);
+ win->cursor_position.x = cursor_position.x - window_pos.x;
+ win->cursor_position.y = cursor_position.y - window_pos.y;
+
update_compositor_texture(focused_monitor);
top_bar_text = mgl::Text("GPU Screen Recorder", get_theme().top_bar_font);
@@ -1032,6 +1035,11 @@ namespace gsr {
return true;
};
+ // The focused application can be an xwayland application but the cursor can hover over a wayland application.
+ // This is even the case when hovering over the titlebar of the xwayland application.
+ if(!is_cursor_hovering_application_wayland(display))
+ xi_setup();
+
//window->set_fullscreen(true);
if(gsr_info.system_info.display_server == DisplayServer::X11)
make_window_click_through(display, window->get_system_handle());
@@ -1045,7 +1053,7 @@ namespace gsr {
XFreeCursor(display, default_cursor);
default_cursor = 0;
}
- default_cursor = XCreateFontCursor(display, XC_arrow);
+ default_cursor = XCreateFontCursor(display, XC_left_ptr);
XFlush(display);
grab_mouse_and_keyboard();
@@ -1107,10 +1115,8 @@ namespace gsr {
XFlush(display);
if(xi_display) {
- // TODO: Only show cursor if it wasn't hidden before the ui was shown
- cursor_drawn = false;
- //XFixesShowCursor(xi_display, DefaultRootWindow(xi_display));
- //XFlush(xi_display);
+ XFixesShowCursor(xi_display, DefaultRootWindow(xi_display));
+ XFlush(xi_display);
cursor_texture.clear();
cursor_sprite.set_texture(nullptr);
}
@@ -1123,20 +1129,24 @@ namespace gsr {
visible = false;
drawn_first_frame = false;
+ if(xi_input_xev) {
+ free(xi_input_xev);
+ xi_input_xev = nullptr;
+ }
+
+ if(xi_output_xev) {
+ free(xi_output_xev);
+ xi_output_xev = nullptr;
+ }
+
+ if(xi_display) {
+ XCloseDisplay(xi_display);
+ xi_display = nullptr;
+ }
+
if(window) {
- const mgl::vec2i new_cursor_position = mgl::vec2i(window->internal_window()->pos.x, window->internal_window()->pos.y) + window->get_mouse_position();
window->set_visible(false);
window.reset();
-
- if(xi_display) {
- XFlush(display);
- XWarpPointer(display, DefaultRootWindow(display), DefaultRootWindow(display), 0, 0, 0, 0, new_cursor_position.x, new_cursor_position.y);
- XFlush(display);
- //xi_warp_pointer(new_cursor_position);
-
- XFixesShowCursor(xi_display, DefaultRootWindow(xi_display));
- XFlush(xi_display);
- }
}
deinit_theme();
@@ -1145,7 +1155,7 @@ namespace gsr {
void Overlay::toggle_show() {
if(visible) {
//hide();
- // We dont want to hide immediately because hide is called in event callback, in which it destroys the window.
+ // We dont want to hide immediately because hide is called in mgl event callback, in which it destroys the mgl window.
// Instead remove all pages and wait until next iteration to close the UI (which happens when there are no pages to render).
while(!page_stack.empty()) {
page_stack.pop();
@@ -2021,8 +2031,8 @@ namespace gsr {
return false;
bool window_texture_loaded = false;
- const Window focused_window = get_focused_window(display, WindowCaptureType::FOCUSED);
- if(is_window_fullscreen_on_monitor(display, focused_window, monitor) && focused_window)
+ const Window focused_window = get_focused_window(display, WindowCaptureType::CURSOR);
+ if(focused_window && is_window_fullscreen_on_monitor(display, focused_window, monitor))
window_texture_loaded = window_texture_init(&window_texture, display, mgl_window_get_egl_display(window->internal_window()), focused_window, egl_funcs) == 0;
if(window_texture_loaded && window_texture.texture_id) {
diff --git a/src/Process.cpp b/src/Process.cpp
index 347c485..c5692f5 100644
--- a/src/Process.cpp
+++ b/src/Process.cpp
@@ -59,7 +59,7 @@ namespace gsr {
const pid_t second_child = vfork();
if(second_child == 0) { // child
execvp(args[0], (char* const*)args);
- perror("execvp");
+ perror(args[0]);
_exit(127);
} else if(second_child != -1) {
// TODO:
@@ -98,7 +98,7 @@ namespace gsr {
close(fds[PIPE_WRITE]);
execvp(args[0], (char* const*)args);
- perror("execvp");
+ perror(args[0]);
_exit(127);
} else { /* parent */
close(fds[PIPE_WRITE]);
diff --git a/src/WindowUtils.cpp b/src/WindowUtils.cpp
index 1560d3e..057e95f 100644
--- a/src/WindowUtils.cpp
+++ b/src/WindowUtils.cpp
@@ -29,19 +29,56 @@ namespace gsr {
return window_has_atom(dpy, window, net_wm_state_atom) || window_has_atom(dpy, window, wm_state_atom);
}
+ static Window window_get_target_window_child(Display *display, Window window) {
+ if(window == None)
+ return None;
+
+ if(window_is_user_program(display, window))
+ return window;
+
+ Window root;
+ Window parent;
+ Window *children = nullptr;
+ unsigned int num_children = 0;
+ if(!XQueryTree(display, window, &root, &parent, &children, &num_children) || !children)
+ return None;
+
+ Window found_window = None;
+ for(int i = 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) {
+ if(children[i]) {
+ Window win = window_get_target_window_child(display, children[i]);
+ if(win) {
+ found_window = win;
+ goto finished;
+ }
+ }
+ }
+
+ finished:
+ XFree(children);
+ return found_window;
+ }
+
static Window get_window_at_cursor_position(Display *dpy) {
Window root_window = None;
Window window = None;
int dummy_i;
unsigned int dummy_u;
- int cursor_pos_x = 0;
- int cursor_pos_y = 0;
- XQueryPointer(dpy, DefaultRootWindow(dpy), &root_window, &window, &dummy_i, &dummy_i, &cursor_pos_x, &cursor_pos_y, &dummy_u);
+ XQueryPointer(dpy, DefaultRootWindow(dpy), &root_window, &window, &dummy_i, &dummy_i, &dummy_i, &dummy_i, &dummy_u);
+ if(window)
+ window = window_get_target_window_child(dpy, window);
return window;
}
Window get_focused_window(Display *dpy, WindowCaptureType cap_type) {
- const Atom net_active_window_atom = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False);
+ //const Atom net_active_window_atom = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False);
Window focused_window = None;
if(cap_type == WindowCaptureType::FOCUSED) {
@@ -68,7 +105,7 @@ namespace gsr {
}
focused_window = get_window_at_cursor_position(dpy);
- if(focused_window && focused_window != DefaultRootWindow(dpy) && window_is_user_program(dpy, focused_window))
+ if(focused_window && focused_window != DefaultRootWindow(dpy))
return focused_window;
return None;
diff --git a/src/gui/SettingsPage.cpp b/src/gui/SettingsPage.cpp
index 0474b91..fa745df 100644
--- a/src/gui/SettingsPage.cpp
+++ b/src/gui/SettingsPage.cpp
@@ -387,20 +387,20 @@ namespace gsr {
video_codec_box->add_item("H264", "h264");
if(gsr_info->supported_video_codecs.hevc)
video_codec_box->add_item("HEVC", "hevc");
+ if(gsr_info->supported_video_codecs.hevc_10bit)
+ video_codec_box->add_item("HEVC (10 bit, reduces banding)", "hevc_10bit");
+ if(gsr_info->supported_video_codecs.hevc_hdr)
+ video_codec_box->add_item("HEVC (HDR)", "hevc_hdr");
if(gsr_info->supported_video_codecs.av1)
video_codec_box->add_item("AV1", "av1");
+ if(gsr_info->supported_video_codecs.av1_10bit)
+ video_codec_box->add_item("AV1 (10 bit, reduces banding)", "av1_10bit");
+ if(gsr_info->supported_video_codecs.av1_hdr)
+ video_codec_box->add_item("AV1 (HDR)", "av1_hdr");
if(gsr_info->supported_video_codecs.vp8)
video_codec_box->add_item("VP8", "vp8");
if(gsr_info->supported_video_codecs.vp9)
video_codec_box->add_item("VP9", "vp9");
- if(gsr_info->supported_video_codecs.hevc_hdr)
- video_codec_box->add_item("HEVC (HDR)", "hevc_hdr");
- if(gsr_info->supported_video_codecs.hevc_10bit)
- video_codec_box->add_item("HEVC (10 bit, reduces banding)", "hevc_10bit");
- if(gsr_info->supported_video_codecs.av1_hdr)
- video_codec_box->add_item("AV1 (HDR)", "av1_hdr");
- if(gsr_info->supported_video_codecs.av1_10bit)
- video_codec_box->add_item("AV1 (10 bit, reduces banding)", "av1_10bit");
if(gsr_info->supported_video_codecs.h264_software)
video_codec_box->add_item("H264 Software Encoder (Slow, not recommended)", "h264_software");
video_codec_box_ptr = video_codec_box.get();