mirror of
https://repo.dec05eba.com/gpu-screen-recorder-ui
synced 2026-03-31 09:17:04 +09:00
Fix window not being fullscreen on multi monitor systems on cinnamon. Fix some applications getting minimized when opening the ui
This commit is contained in:
@@ -19,7 +19,7 @@ GPU Screen Recorder UI uses meson build system so you need to install `meson` to
|
|||||||
## Build dependencies
|
## Build dependencies
|
||||||
These are the dependencies needed to build GPU Screen Recorder UI:
|
These are the dependencies needed to build GPU Screen Recorder UI:
|
||||||
|
|
||||||
* x11 (libx11, libxrandr, libxrender, libxfixes, libxcomposite)
|
* x11 (libx11, libxrandr, libxrender, libxcomposite, libxfixes, libxi)
|
||||||
* libglvnd (which provides libgl, libglx and libegl)
|
* libglvnd (which provides libgl, libglx and libegl)
|
||||||
* libevdev
|
* libevdev
|
||||||
* libudev (systemd-libs)
|
* libudev (systemd-libs)
|
||||||
|
|||||||
4
TODO
4
TODO
@@ -97,4 +97,6 @@ Add option to select which gpu to record with, or list all monitors and automati
|
|||||||
|
|
||||||
Remove all dependencies from tools/gsr-global-hotkeys and roll our own keyboard events code.
|
Remove all dependencies from tools/gsr-global-hotkeys and roll our own keyboard events code.
|
||||||
|
|
||||||
Test global hotkeys with azerty instead of qwerty.
|
Test global hotkeys with azerty instead of qwerty.
|
||||||
|
|
||||||
|
Fix cursor grab not working in owlboy, need to use xigrab.
|
||||||
Submodule depends/mglpp updated: 7f0a03d90e...4dbee5ac57
@@ -54,6 +54,12 @@ namespace gsr {
|
|||||||
void show_notification(const char *str, double timeout_seconds, mgl::Color icon_color, mgl::Color bg_color, NotificationType notification_type);
|
void show_notification(const char *str, double timeout_seconds, mgl::Color icon_color, mgl::Color bg_color, NotificationType notification_type);
|
||||||
bool is_open() const;
|
bool is_open() const;
|
||||||
private:
|
private:
|
||||||
|
void xi_setup();
|
||||||
|
void handle_xi_events();
|
||||||
|
void xi_setup_fake_cursor();
|
||||||
|
void xi_grab_all_devices();
|
||||||
|
void xi_warp_pointer(mgl::vec2i position);
|
||||||
|
|
||||||
void process_key_bindings(mgl::Event &event);
|
void process_key_bindings(mgl::Event &event);
|
||||||
|
|
||||||
void update_notification_process_status();
|
void update_notification_process_status();
|
||||||
@@ -99,6 +105,10 @@ namespace gsr {
|
|||||||
mgl::Texture screenshot_texture;
|
mgl::Texture screenshot_texture;
|
||||||
mgl::Sprite screenshot_sprite;
|
mgl::Sprite screenshot_sprite;
|
||||||
mgl::Rectangle bg_screenshot_overlay;
|
mgl::Rectangle bg_screenshot_overlay;
|
||||||
|
mgl::Texture cursor_texture;
|
||||||
|
mgl::Sprite cursor_sprite;
|
||||||
|
mgl::vec2i cursor_hotspot;
|
||||||
|
bool cursor_drawn = false;
|
||||||
WindowTexture window_texture;
|
WindowTexture window_texture;
|
||||||
PageStack page_stack;
|
PageStack page_stack;
|
||||||
mgl::Rectangle top_bar_background;
|
mgl::Rectangle top_bar_background;
|
||||||
@@ -125,5 +135,10 @@ namespace gsr {
|
|||||||
bool focused_window_is_fullscreen = false;
|
bool focused_window_is_fullscreen = false;
|
||||||
|
|
||||||
std::array<KeyBinding, 1> key_bindings;
|
std::array<KeyBinding, 1> key_bindings;
|
||||||
|
|
||||||
|
Display *xi_display = nullptr;
|
||||||
|
int xi_opcode = 0;
|
||||||
|
XEvent *xi_input_xev = nullptr;
|
||||||
|
XEvent *xi_output_xev = nullptr;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
12
meson.build
12
meson.build
@@ -42,11 +42,6 @@ src = [
|
|||||||
mglpp_proj = subproject('mglpp')
|
mglpp_proj = subproject('mglpp')
|
||||||
mglpp_dep = mglpp_proj.get_variable('mglpp_dep')
|
mglpp_dep = mglpp_proj.get_variable('mglpp_dep')
|
||||||
|
|
||||||
dep = [
|
|
||||||
mglpp_dep,
|
|
||||||
dependency('xcomposite'),
|
|
||||||
]
|
|
||||||
|
|
||||||
prefix = get_option('prefix')
|
prefix = get_option('prefix')
|
||||||
datadir = get_option('datadir')
|
datadir = get_option('datadir')
|
||||||
gsr_ui_resources_path = join_paths(prefix, datadir, 'gsr-ui')
|
gsr_ui_resources_path = join_paths(prefix, datadir, 'gsr-ui')
|
||||||
@@ -55,7 +50,12 @@ executable(
|
|||||||
meson.project_name(),
|
meson.project_name(),
|
||||||
src,
|
src,
|
||||||
install : true,
|
install : true,
|
||||||
dependencies : dep,
|
dependencies : [
|
||||||
|
mglpp_dep,
|
||||||
|
dependency('xcomposite'),
|
||||||
|
dependency('xfixes'),
|
||||||
|
dependency('xi'),
|
||||||
|
],
|
||||||
cpp_args : '-DGSR_UI_RESOURCES_PATH="' + gsr_ui_resources_path + '"',
|
cpp_args : '-DGSR_UI_RESOURCES_PATH="' + gsr_ui_resources_path + '"',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -11,4 +11,6 @@ version = "c++17"
|
|||||||
ignore_dirs = ["build", "tools"]
|
ignore_dirs = ["build", "tools"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
xcomposite = ">=0"
|
xcomposite = ">=0"
|
||||||
|
xfixes = ">=0"
|
||||||
|
xi = ">=0"
|
||||||
382
src/Overlay.cpp
382
src/Overlay.cpp
@@ -21,6 +21,9 @@
|
|||||||
#include <X11/Xutil.h>
|
#include <X11/Xutil.h>
|
||||||
#include <X11/Xatom.h>
|
#include <X11/Xatom.h>
|
||||||
#include <X11/cursorfont.h>
|
#include <X11/cursorfont.h>
|
||||||
|
#include <X11/extensions/Xfixes.h>
|
||||||
|
#include <X11/extensions/XInput2.h>
|
||||||
|
#include <X11/extensions/shape.h>
|
||||||
#include <mglpp/system/Rect.hpp>
|
#include <mglpp/system/Rect.hpp>
|
||||||
#include <mglpp/window/Event.hpp>
|
#include <mglpp/window/Event.hpp>
|
||||||
|
|
||||||
@@ -117,6 +120,59 @@ namespace gsr {
|
|||||||
return texture;
|
return texture;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool texture_from_x11_cursor(XFixesCursorImage *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;
|
||||||
|
*visible = false;
|
||||||
|
|
||||||
|
if(!x11_cursor_image)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
if(!x11_cursor_image->pixels)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
hotspot->x = x11_cursor_image->xhot;
|
||||||
|
hotspot->y = x11_cursor_image->yhot;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
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) {
|
||||||
|
uint32_t pixel = *pixels++;
|
||||||
|
uint8_t *in = (uint8_t*)&pixel;
|
||||||
|
uint8_t alpha = in[3];
|
||||||
|
if(alpha == 0) {
|
||||||
|
alpha = 1;
|
||||||
|
} else {
|
||||||
|
*visible = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
out[0] = (float)in[2] * 255.0/(float)alpha;
|
||||||
|
out[1] = (float)in[1] * 255.0/(float)alpha;
|
||||||
|
out[2] = (float)in[0] * 255.0/(float)alpha;
|
||||||
|
out[3] = in[3];
|
||||||
|
out += 4;
|
||||||
|
in += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
static char hex_value_to_str(uint8_t v) {
|
||||||
if(v <= 9)
|
if(v <= 9)
|
||||||
return '0' + v;
|
return '0' + v;
|
||||||
@@ -265,6 +321,14 @@ namespace gsr {
|
|||||||
return True;
|
return True;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void make_window_click_through(Display *display, Window window) {
|
||||||
|
XRectangle rect;
|
||||||
|
memset(&rect, 0, sizeof(rect));
|
||||||
|
XserverRegion region = XFixesCreateRegion(display, &rect, 1);
|
||||||
|
XFixesSetWindowShapeRegion(display, window, ShapeInput, 0, 0, region);
|
||||||
|
XFixesDestroyRegion(display, region);
|
||||||
|
}
|
||||||
|
|
||||||
static Bool make_window_sticky(Display *dpy, Window window) {
|
static Bool make_window_sticky(Display *dpy, Window window) {
|
||||||
return set_window_wm_state(dpy, window, XInternAtom(dpy, "_NET_WM_STATE_STICKY", False));
|
return set_window_wm_state(dpy, window, XInternAtom(dpy, "_NET_WM_STATE_STICKY", False));
|
||||||
}
|
}
|
||||||
@@ -320,6 +384,26 @@ namespace gsr {
|
|||||||
return is_connected;
|
return is_connected;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool xinput_is_supported(Display *dpy, int *xi_opcode) {
|
||||||
|
*xi_opcode = 0;
|
||||||
|
int query_event = 0;
|
||||||
|
int query_error = 0;
|
||||||
|
if(!XQueryExtension(dpy, "XInputExtension", xi_opcode, &query_event, &query_error)) {
|
||||||
|
fprintf(stderr, "gsr-ui error: X Input extension not available\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int major = 2;
|
||||||
|
int minor = 1;
|
||||||
|
int retval = XIQueryVersion(dpy, &major, &minor);
|
||||||
|
if (retval != Success) {
|
||||||
|
fprintf(stderr, "gsr-ui error: XInput 2.1 is not supported\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
Overlay::Overlay(std::string resources_path, GsrInfo gsr_info, egl_functions egl_funcs) :
|
Overlay::Overlay(std::string resources_path, GsrInfo gsr_info, egl_functions egl_funcs) :
|
||||||
resources_path(std::move(resources_path)),
|
resources_path(std::move(resources_path)),
|
||||||
gsr_info(gsr_info),
|
gsr_info(gsr_info),
|
||||||
@@ -329,6 +413,9 @@ namespace gsr {
|
|||||||
close_button_widget({0.0f, 0.0f}),
|
close_button_widget({0.0f, 0.0f}),
|
||||||
config(gsr_info)
|
config(gsr_info)
|
||||||
{
|
{
|
||||||
|
// TODO:
|
||||||
|
//xi_setup();
|
||||||
|
|
||||||
memset(&window_texture, 0, sizeof(window_texture));
|
memset(&window_texture, 0, sizeof(window_texture));
|
||||||
|
|
||||||
key_bindings[0].key_event.code = mgl::Keyboard::Escape;
|
key_bindings[0].key_event.code = mgl::Keyboard::Escape;
|
||||||
@@ -377,6 +464,60 @@ namespace gsr {
|
|||||||
}
|
}
|
||||||
gpu_screen_recorder_process = -1;
|
gpu_screen_recorder_process = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
free(xi_input_xev);
|
||||||
|
free(xi_output_xev);
|
||||||
|
if(xi_display)
|
||||||
|
XCloseDisplay(xi_display);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Overlay::xi_setup() {
|
||||||
|
xi_display = XOpenDisplay(nullptr);
|
||||||
|
if(!xi_display) {
|
||||||
|
fprintf(stderr, "gsr-ui error: failed to setup XI connection\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!xinput_is_supported(xi_display, &xi_opcode))
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
xi_input_xev = (XEvent*)calloc(1, sizeof(XEvent));
|
||||||
|
if(!xi_input_xev)
|
||||||
|
throw std::runtime_error("gsr-ui error: failed to allocate XEvent data");
|
||||||
|
|
||||||
|
xi_output_xev = (XEvent*)calloc(1, sizeof(XEvent));
|
||||||
|
if(!xi_output_xev)
|
||||||
|
throw std::runtime_error("gsr-ui error: failed to allocate XEvent data");
|
||||||
|
|
||||||
|
unsigned char mask[XIMaskLen(XI_LASTEVENT)];
|
||||||
|
memset(mask, 0, sizeof(mask));
|
||||||
|
XISetMask(mask, XI_Motion);
|
||||||
|
XISetMask(mask, XI_ButtonPress);
|
||||||
|
XISetMask(mask, XI_ButtonRelease);
|
||||||
|
XISetMask(mask, XI_KeyPress);
|
||||||
|
XISetMask(mask, XI_KeyRelease);
|
||||||
|
|
||||||
|
XIEventMask xi_masks;
|
||||||
|
xi_masks.deviceid = XIAllMasterDevices;
|
||||||
|
xi_masks.mask_len = sizeof(mask);
|
||||||
|
xi_masks.mask = mask;
|
||||||
|
if(XISelectEvents(xi_display, DefaultRootWindow(xi_display), &xi_masks, 1) != Success) {
|
||||||
|
fprintf(stderr, "gsr-ui error: XISelectEvents failed\n");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
XFlush(xi_display);
|
||||||
|
return;
|
||||||
|
|
||||||
|
error:
|
||||||
|
free(xi_input_xev);
|
||||||
|
xi_input_xev = nullptr;
|
||||||
|
free(xi_output_xev);
|
||||||
|
xi_output_xev = nullptr;
|
||||||
|
if(xi_display) {
|
||||||
|
XCloseDisplay(xi_display);
|
||||||
|
xi_display = nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint32_t key_event_to_bitmask(mgl::Event::KeyEvent key_event) {
|
static uint32_t key_event_to_bitmask(mgl::Event::KeyEvent key_event) {
|
||||||
@@ -397,10 +538,72 @@ namespace gsr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Overlay::handle_xi_events() {
|
||||||
|
if(!xi_display)
|
||||||
|
return;
|
||||||
|
|
||||||
|
mgl_context *context = mgl_get_context();
|
||||||
|
Display *display = (Display*)context->connection;
|
||||||
|
|
||||||
|
while(XPending(xi_display)) {
|
||||||
|
XNextEvent(xi_display, xi_input_xev);
|
||||||
|
XGenericEventCookie *cookie = &xi_input_xev->xcookie;
|
||||||
|
if(cookie->type == GenericEvent && cookie->extension == xi_opcode && XGetEventData(xi_display, cookie)) {
|
||||||
|
const XIDeviceEvent *de = (XIDeviceEvent*)cookie->data;
|
||||||
|
if(cookie->evtype == XI_Motion) {
|
||||||
|
memset(xi_output_xev, 0, sizeof(*xi_output_xev));
|
||||||
|
xi_output_xev->type = MotionNotify;
|
||||||
|
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_root = de->root_x;
|
||||||
|
xi_output_xev->xmotion.y_root = de->root_y;
|
||||||
|
//xi_output_xev->xmotion.state = // modifiers // TODO:
|
||||||
|
if(window->inject_x11_event(xi_output_xev, event))
|
||||||
|
on_event(event);
|
||||||
|
} else if(cookie->evtype == XI_ButtonPress || cookie->evtype == XI_ButtonRelease) {
|
||||||
|
memset(xi_output_xev, 0, sizeof(*xi_output_xev));
|
||||||
|
xi_output_xev->type = cookie->evtype == XI_ButtonPress ? ButtonPress : ButtonRelease;
|
||||||
|
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_root = de->root_x;
|
||||||
|
xi_output_xev->xbutton.y_root = de->root_y;
|
||||||
|
//xi_output_xev->xbutton.state = // modifiers // TODO:
|
||||||
|
xi_output_xev->xbutton.button = de->detail;
|
||||||
|
if(window->inject_x11_event(xi_output_xev, event))
|
||||||
|
on_event(event);
|
||||||
|
} else if(cookie->evtype == XI_KeyPress || cookie->evtype == XI_KeyRelease) {
|
||||||
|
memset(xi_output_xev, 0, sizeof(*xi_output_xev));
|
||||||
|
xi_output_xev->type = cookie->evtype == XI_KeyPress ? KeyPress : KeyRelease;
|
||||||
|
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_root = de->root_x;
|
||||||
|
xi_output_xev->xkey.y_root = de->root_y;
|
||||||
|
xi_output_xev->xkey.state = de->mods.effective;
|
||||||
|
xi_output_xev->xkey.keycode = de->detail;
|
||||||
|
if(window->inject_x11_event(xi_output_xev, event))
|
||||||
|
on_event(event);
|
||||||
|
}
|
||||||
|
//fprintf(stderr, "got xi event: %d\n", cookie->evtype);
|
||||||
|
XFreeEventData(xi_display, cookie);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Overlay::handle_events() {
|
void Overlay::handle_events() {
|
||||||
if(!visible || !window)
|
if(!visible || !window)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
handle_xi_events();
|
||||||
|
|
||||||
while(window->poll_event(event)) {
|
while(window->poll_event(event)) {
|
||||||
on_event(event);
|
on_event(event);
|
||||||
}
|
}
|
||||||
@@ -452,12 +655,86 @@ namespace gsr {
|
|||||||
close_button_widget.draw(*window, mgl::vec2f(0.0f, 0.0f));
|
close_button_widget.draw(*window, mgl::vec2f(0.0f, 0.0f));
|
||||||
page_stack.draw(*window, mgl::vec2f(0.0f, 0.0f));
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
window->display();
|
window->display();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Overlay::xi_setup_fake_cursor() {
|
||||||
|
if(!xi_display)
|
||||||
|
return;
|
||||||
|
|
||||||
|
XFixesShowCursor(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);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Overlay::xi_grab_all_devices() {
|
||||||
|
if(!xi_display)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int num_devices = 0;
|
||||||
|
XIDeviceInfo *info = XIQueryDevice(xi_display, XIAllDevices, &num_devices);
|
||||||
|
if(!info)
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Overlay::xi_warp_pointer(mgl::vec2i position) {
|
||||||
|
if(!xi_display)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int num_devices = 0;
|
||||||
|
XIDeviceInfo *info = XIQueryDevice(xi_display, XIAllDevices, &num_devices);
|
||||||
|
if(!info)
|
||||||
|
return;
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
XFlush(xi_display);
|
||||||
|
XIFreeDeviceInfo(info);
|
||||||
|
}
|
||||||
|
|
||||||
void Overlay::show() {
|
void Overlay::show() {
|
||||||
|
if(visible)
|
||||||
|
return;
|
||||||
|
|
||||||
window.reset();
|
window.reset();
|
||||||
window = std::make_unique<mgl::Window>();
|
window = std::make_unique<mgl::Window>();
|
||||||
deinit_theme();
|
deinit_theme();
|
||||||
@@ -471,7 +748,7 @@ namespace gsr {
|
|||||||
window_create_params.max_size = window_size;
|
window_create_params.max_size = window_size;
|
||||||
window_create_params.position = window_pos;
|
window_create_params.position = window_pos;
|
||||||
window_create_params.hidden = true;
|
window_create_params.hidden = true;
|
||||||
window_create_params.override_redirect = false;
|
window_create_params.override_redirect = true;
|
||||||
window_create_params.background_color = bg_color;
|
window_create_params.background_color = bg_color;
|
||||||
window_create_params.support_alpha = true;
|
window_create_params.support_alpha = true;
|
||||||
window_create_params.window_type = MGL_WINDOW_TYPE_NORMAL;
|
window_create_params.window_type = MGL_WINDOW_TYPE_NORMAL;
|
||||||
@@ -637,33 +914,47 @@ namespace gsr {
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
window->set_fullscreen(true);
|
//window->set_fullscreen(true);
|
||||||
|
if(gsr_info.system_info.display_server == DisplayServer::X11)
|
||||||
|
make_window_click_through(display, window->get_system_handle());
|
||||||
|
|
||||||
window->set_visible(true);
|
window->set_visible(true);
|
||||||
|
|
||||||
make_window_sticky(display, window->get_system_handle());
|
make_window_sticky(display, window->get_system_handle());
|
||||||
hide_window_from_taskbar(display, window->get_system_handle());
|
hide_window_from_taskbar(display, window->get_system_handle());
|
||||||
|
|
||||||
// if(default_cursor) {
|
if(default_cursor) {
|
||||||
// XFreeCursor(display, default_cursor);
|
XFreeCursor(display, default_cursor);
|
||||||
// default_cursor = 0;
|
default_cursor = 0;
|
||||||
// }
|
}
|
||||||
// default_cursor = XCreateFontCursor(display, XC_arrow);
|
default_cursor = XCreateFontCursor(display, XC_arrow);
|
||||||
|
|
||||||
// TODO: Retry if these fail.
|
// TODO: Remove these grabs when debugging with a debugger, or your X11 session will appear frozen.
|
||||||
// TODO: Hmm, these dont work in owlboy. Maybe owlboy uses xi2 and that breaks this (does it?).
|
// There should be a debug mode to not use these
|
||||||
// Remove these grabs when debugging with a debugger, or your X11 session will appear frozen
|
|
||||||
|
|
||||||
// XGrabPointer(display, window->get_system_handle(), True,
|
XGrabPointer(display, window->get_system_handle(), True,
|
||||||
// ButtonPressMask | ButtonReleaseMask | PointerMotionMask |
|
ButtonPressMask | ButtonReleaseMask | PointerMotionMask |
|
||||||
// Button1MotionMask | Button2MotionMask | Button3MotionMask | Button4MotionMask | Button5MotionMask |
|
Button1MotionMask | Button2MotionMask | Button3MotionMask | Button4MotionMask | Button5MotionMask |
|
||||||
// ButtonMotionMask,
|
ButtonMotionMask,
|
||||||
// GrabModeAsync, GrabModeAsync, None, default_cursor, CurrentTime);
|
GrabModeAsync, GrabModeAsync, None, default_cursor, CurrentTime);
|
||||||
// TODO: This breaks global hotkeys
|
// TODO: This breaks global hotkeys (when using x11 global hotkeys)
|
||||||
//XGrabKeyboard(display, window->get_system_handle(), True, GrabModeAsync, GrabModeAsync, CurrentTime);
|
XGrabKeyboard(display, window->get_system_handle(), True, GrabModeAsync, GrabModeAsync, CurrentTime);
|
||||||
|
|
||||||
set_focused_window(display, window->get_system_handle());
|
|
||||||
XFlush(display);
|
XFlush(display);
|
||||||
|
|
||||||
window->set_fullscreen(true);
|
// The real cursor doesn't move when all devices are grabbed, so we create our own cursor and diplay that while grabbed
|
||||||
|
xi_setup_fake_cursor();
|
||||||
|
|
||||||
|
// We want to grab all devices to prevent any other application below from receiving events.
|
||||||
|
// Owlboy seems to use xi events and XGrabPointer doesn't prevent owlboy from receiving events.
|
||||||
|
xi_grab_all_devices();
|
||||||
|
|
||||||
|
// if(gsr_info.system_info.display_server == DisplayServer::WAYLAND) {
|
||||||
|
// set_focused_window(display, window->get_system_handle());
|
||||||
|
// XFlush(display);
|
||||||
|
// }
|
||||||
|
|
||||||
|
//window->set_fullscreen(true);
|
||||||
|
|
||||||
visible = true;
|
visible = true;
|
||||||
|
|
||||||
@@ -694,6 +985,9 @@ namespace gsr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Overlay::hide() {
|
void Overlay::hide() {
|
||||||
|
if(!visible)
|
||||||
|
return;
|
||||||
|
|
||||||
mgl_context *context = mgl_get_context();
|
mgl_context *context = mgl_get_context();
|
||||||
Display *display = (Display*)context->connection;
|
Display *display = (Display*)context->connection;
|
||||||
|
|
||||||
@@ -701,14 +995,23 @@ namespace gsr {
|
|||||||
page_stack.pop();
|
page_stack.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
// if(default_cursor) {
|
if(default_cursor) {
|
||||||
// XFreeCursor(display, default_cursor);
|
XFreeCursor(display, default_cursor);
|
||||||
// default_cursor = 0;
|
default_cursor = 0;
|
||||||
// }
|
}
|
||||||
|
|
||||||
// XUngrabKeyboard(display, CurrentTime);
|
XUngrabKeyboard(display, CurrentTime);
|
||||||
// XUngrabPointer(display, CurrentTime);
|
XUngrabPointer(display, CurrentTime);
|
||||||
// XFlush(display);
|
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);
|
||||||
|
cursor_texture.clear();
|
||||||
|
cursor_sprite.set_texture(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
window_texture_deinit(&window_texture);
|
window_texture_deinit(&window_texture);
|
||||||
window_texture_sprite.set_texture(nullptr);
|
window_texture_sprite.set_texture(nullptr);
|
||||||
@@ -717,8 +1020,19 @@ namespace gsr {
|
|||||||
|
|
||||||
visible = false;
|
visible = false;
|
||||||
if(window) {
|
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->set_visible(false);
|
||||||
window.reset();
|
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();
|
deinit_theme();
|
||||||
@@ -831,20 +1145,8 @@ namespace gsr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int exit_code = -1;
|
int exit_code = -1;
|
||||||
// The process is no longer a child process since gsr ui has restarted
|
if(WIFEXITED(status))
|
||||||
if(errno == ECHILD) {
|
exit_code = WEXITSTATUS(status);
|
||||||
errno = 0;
|
|
||||||
kill(gpu_screen_recorder_process, 0);
|
|
||||||
if(errno != ESRCH) {
|
|
||||||
// Still running
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// We cant know the exit status, so we assume it succeeded
|
|
||||||
exit_code = 0;
|
|
||||||
} else {
|
|
||||||
if(WIFEXITED(status))
|
|
||||||
exit_code = WEXITSTATUS(status);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch(recording_status) {
|
switch(recording_status) {
|
||||||
case RecordingStatus::NONE:
|
case RecordingStatus::NONE:
|
||||||
|
|||||||
Reference in New Issue
Block a user