From da4925b23e7ebd6df35cdb0ba39ac8cc1701a102 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Fri, 16 Aug 2024 19:37:00 +0200 Subject: Allow capture of external monitors on a laptop with dedicated gpu (prime) on x11, fix cursor not visible on some wayland compositors (hyprland) with multiple monitors --- README.md | 6 ++--- TODO | 8 ------ include/egl.h | 17 ++++++++++++ include/utils.h | 6 ++--- src/capture/kms.c | 67 +++++++++++++++++++++++++++++++++++++++++------- src/capture/xcomposite.c | 10 ++++---- src/egl.c | 47 ++++++++++++++++++++++++++++++--- src/main.cpp | 35 +++++++++++++++---------- src/utils.c | 54 +++++++++++++++++++++++--------------- 9 files changed, 183 insertions(+), 67 deletions(-) diff --git a/README.md b/README.md index cd52858..1cd9eac 100644 --- a/README.md +++ b/README.md @@ -25,10 +25,10 @@ This software works on X11 and Wayland on AMD, Intel and NVIDIA. 2) Videos are in variable framerate format. Use MPV to play such videos, otherwise you might experience stuttering in the video if you are using a buggy video player. You can try saving the video into a .mkv file instead as some software may have better support for .mkv files (such as kdenlive). You can use the "-fm cfr" option to to use constant framerate mode. 3) FLAC audio codec is disabled at the moment because of temporary issues. ### AMD/Intel/Wayland root permission -When recording a window under AMD/Intel no special user permission is required, however when recording a monitor (or when using wayland) the program needs root permission (to access KMS).\ +When recording a window or when using the `-w portal` option under AMD/Intel no special user permission is required, +however when recording a monitor (or when using wayland) the program needs root permission (to access KMS).\ This is safe in GPU Screen Recorder as the part that needs root access has been moved to its own small program that only does one thing.\ -For you as a user this only means that if you installed GPU Screen Recorder as a flatpak then a prompt asking for root password will show up when you start recording.\ -Note that this only applies to when recording a monitor. On Wayland you can use the `-w portal` option to record a monitor without root permission. +For you as a user this only means that if you installed GPU Screen Recorder as a flatpak then a prompt asking for root password will show up when you start recording. # Performance On a system with a i5 4690k CPU and a GTX 1080 GPU:\ When recording Legend of Zelda Breath of the Wild at 4k, fps drops from 30 to 7 when using OBS Studio + nvenc, however when using this screen recorder the fps remains at 30.\ diff --git a/TODO b/TODO index d79eace..32e5da0 100644 --- a/TODO +++ b/TODO @@ -6,9 +6,6 @@ Allow setting a different output resolution than the input resolution. Use mov+faststart. Allow recording all monitors/selected monitor without nvfbc by recording the compositor proxy window and only recording the part that matches the monitor(s). Allow recording a region by recording the compositor proxy window / nvfbc window and copying part of it. -Use nvenc directly, which allows removing the use of cuda. -Handle xrandr monitor change in nvfbc. -Implement follow focused in drm. Support amf and qsv. Disable flipping on nvidia? this might fix some stuttering issues on some setups. See NvCtrlGetAttribute/NvCtrlSetAttributeAndGetStatus NV_CTRL_SYNC_TO_VBLANK https://github.com/NVIDIA/nvidia-settings/blob/d5f022976368cbceb2f20b838ddb0bf992f0cfb9/src/gtk%2B-2.x/ctkopengl.c. Replays seem to have some issues with audio/video. Why? @@ -31,7 +28,6 @@ https://djdallmann.github.io/GamingPCSetup/CONTENT/RESEARCH/FINDINGS/registrykey The video output will be black if if the system is suspended on nvidia and NVreg_PreserveVideoMemoryAllocations is not set to 1. This happens because I think that the driver invalidates textures/cuda buffers? To fix this we could try and recreate gsr capture when gsr_capture_capture fails (with timeout to retry again). NVreg_RegistryDwords. -Restore nvfbc screen recording on monitor reconfiguration. Window capture doesn't work properly in _control_ game after going from pause menu to in-game (and back to pause menu). There might be some x11 event we need to catch. Same for vr-video-player. Monitor capture on steam deck is slightly below the game fps, but only when capturing on the steam deck screen. If capturing on another monitor, there is no issue. @@ -150,12 +146,8 @@ Show rotated window size in monitor list when using incorrect monitor name. Desktop portal capture on kde plasma makes notifications not show up unless the notification is set as urgent. How to fix this? do we have to make our own notification system? -Add the option to specify a path to desktop portal session token, to allow different recording modes (streaming, recording, replay) to restore with different setup. - Explicit sync is done with the drm property IN_FENCE_FD (see https://drmdb.emersion.fr/properties/4008636142/IN_FENCE_FD). Check if this needs to be used on wayland (especially on nvidia) when capturing a monitor directly without desktop portal. The update fps appear to be lower when recording a monitor instead of using portal on intel. Does this reflect in game framerate? -Recording a monitor belonging to another gpu might be possible by using GL_TEXTURE_EXTERNAL_OES instead for the texture. - Fix glitches when using prime-run with desktop portal. It happens when moving a window around. It's probably a syncing issue. \ No newline at end of file diff --git a/include/egl.h b/include/egl.h index 6a44d7e..720be35 100644 --- a/include/egl.h +++ b/include/egl.h @@ -150,9 +150,19 @@ typedef int (*FUNC_eglQueryDmaBufModifiersEXT)(EGLDisplay dpy, int32_t format, i #define GSR_MAX_OUTPUTS 32 +typedef struct { + char *name; + vec2i pos; + vec2i size; + uint32_t connector_id; + gsr_monitor_rotation rotation; +} gsr_x11_output; + typedef struct { Display *dpy; Window window; + gsr_x11_output outputs[GSR_MAX_OUTPUTS]; + int num_outputs; } gsr_x11; typedef struct { @@ -179,6 +189,11 @@ typedef enum { GSR_GL_CONTEXT_TYPE_GLX } gsr_gl_context_type; +typedef enum { + GSR_DISPLAY_SERVER_X11, + GSR_DISPLAY_SERVER_WAYLAND +} gsr_display_server; + typedef struct gsr_egl gsr_egl; struct gsr_egl { void *egl_library; @@ -302,4 +317,6 @@ void gsr_egl_update(gsr_egl *self); /* Does opengl swap with egl or glx, depending on which one is active */ void gsr_egl_swap_buffers(gsr_egl *self); +gsr_display_server gsr_egl_get_display_server(const gsr_egl *egl); + #endif /* GSR_EGL_H */ diff --git a/include/utils.h b/include/utils.h index 3921dad..cadde8f 100644 --- a/include/utils.h +++ b/include/utils.h @@ -7,14 +7,11 @@ #include #include -typedef struct _XRRCrtcInfo XRRCrtcInfo; - typedef struct { const char *name; int name_len; vec2i pos; vec2i size; - XRRCrtcInfo *crt_info; /* Only on x11 */ uint32_t connector_id; /* Only on x11 and drm */ gsr_monitor_rotation rotation; /* Only on x11 and wayland */ uint32_t monitor_identifier; /* Only on drm and wayland */ @@ -30,7 +27,8 @@ typedef struct { double clock_get_monotonic_seconds(void); typedef void (*active_monitor_callback)(const gsr_monitor *monitor, void *userdata); -void for_each_active_monitor_output_x11(Display *display, active_monitor_callback callback, void *userdata); +void for_each_active_monitor_output_x11_not_cached(Display *display, active_monitor_callback callback, void *userdata); +void for_each_active_monitor_output_x11(const gsr_egl *egl, active_monitor_callback callback, void *userdata); void for_each_active_monitor_output(const gsr_egl *egl, gsr_connection_type connection_type, active_monitor_callback callback, void *userdata); bool get_monitor_by_name(const gsr_egl *egl, gsr_connection_type connection_type, const char *name, gsr_monitor *monitor); gsr_monitor_rotation drm_monitor_get_display_server_rotation(const gsr_egl *egl, const gsr_monitor *monitor); diff --git a/src/capture/kms.c b/src/capture/kms.c index 1616bd7..c677bbb 100644 --- a/src/capture/kms.c +++ b/src/capture/kms.c @@ -1,8 +1,10 @@ #include "../../include/capture/kms.h" #include "../../include/utils.h" #include "../../include/color_conversion.h" +#include "../../include/cursor.h" #include "../../kms/client/kms_client.h" +#include #include #include #include @@ -45,6 +47,9 @@ typedef struct { struct hdr_output_metadata hdr_metadata; bool hdr_metadata_set; + + gsr_cursor x11_cursor; + XEvent xev; } gsr_capture_kms; static void gsr_capture_kms_cleanup_kms_fds(gsr_capture_kms *self) { @@ -79,6 +84,7 @@ static void gsr_capture_kms_stop(gsr_capture_kms *self) { gsr_capture_kms_cleanup_kms_fds(self); gsr_kms_client_deinit(&self->kms_client); + gsr_cursor_deinit(&self->x11_cursor); } static int max_int(int a, int b) { @@ -151,14 +157,19 @@ static int gsr_capture_kms_start(gsr_capture *cap, AVCodecContext *video_codec_c if(kms_init_res != 0) return kms_init_res; + const bool is_x11 = gsr_egl_get_display_server(self->params.egl) == GSR_DISPLAY_SERVER_X11; + const gsr_connection_type connection_type = is_x11 ? GSR_CONNECTION_X11 : GSR_CONNECTION_DRM; + if(is_x11) + gsr_cursor_init(&self->x11_cursor, self->params.egl, self->params.egl->x11.dpy); + MonitorCallbackUserdata monitor_callback_userdata = { &self->monitor_id, self->params.display_to_capture, strlen(self->params.display_to_capture), 0, }; - for_each_active_monitor_output(self->params.egl, GSR_CONNECTION_DRM, monitor_callback, &monitor_callback_userdata); + for_each_active_monitor_output(self->params.egl, connection_type, monitor_callback, &monitor_callback_userdata); - if(!get_monitor_by_name(self->params.egl, GSR_CONNECTION_DRM, self->params.display_to_capture, &monitor)) { + if(!get_monitor_by_name(self->params.egl, connection_type, self->params.display_to_capture, &monitor)) { fprintf(stderr, "gsr error: gsr_capture_kms_start: failed to find monitor by name \"%s\"\n", self->params.display_to_capture); gsr_capture_kms_stop(self); return -1; @@ -168,7 +179,8 @@ static int gsr_capture_kms_start(gsr_capture *cap, AVCodecContext *video_codec_c self->monitor_rotation = drm_monitor_get_display_server_rotation(self->params.egl, &monitor); self->capture_pos = monitor.pos; - if(self->monitor_rotation == GSR_MONITOR_ROT_90 || self->monitor_rotation == GSR_MONITOR_ROT_270) { + /* Monitor size is already rotated on x11 when the monitor is rotated, no need to apply it ourselves */ + if(!is_x11 && (self->monitor_rotation == GSR_MONITOR_ROT_90 || self->monitor_rotation == GSR_MONITOR_ROT_270)) { self->capture_size.x = monitor.size.y; self->capture_size.y = monitor.size.x; } else { @@ -186,6 +198,16 @@ static int gsr_capture_kms_start(gsr_capture *cap, AVCodecContext *video_codec_c return 0; } +static void gsr_capture_kms_tick(gsr_capture *cap, AVCodecContext *video_codec_context) { + (void)video_codec_context; + gsr_capture_kms *self = cap->priv; + + while(XPending(self->params.egl->x11.dpy)) { + XNextEvent(self->params.egl->x11.dpy, &self->xev); + gsr_cursor_update(&self->x11_cursor, &self->xev); + } +} + static float monitor_rotation_to_radians(gsr_monitor_rotation rot) { switch(rot) { case GSR_MONITOR_ROT_0: return 0.0f; @@ -238,12 +260,16 @@ static gsr_kms_response_item* find_largest_drm(gsr_kms_response *kms_response) { return largest_drm; } -static gsr_kms_response_item* find_cursor_drm(gsr_kms_response *kms_response) { +static gsr_kms_response_item* find_cursor_drm(gsr_kms_response *kms_response, uint32_t connector_id) { + gsr_kms_response_item *cursor_drm = NULL; for(int i = 0; i < kms_response->num_items; ++i) { - if(kms_response->items[i].is_cursor) - return &kms_response->items[i]; + if(kms_response->items[i].is_cursor) { + cursor_drm = &kms_response->items[i]; + if(kms_response->items[i].connector_id == connector_id) + break; + } } - return NULL; + return cursor_drm; } static bool hdr_metadata_is_supported_format(const struct hdr_output_metadata *hdr_metadata) { @@ -329,7 +355,7 @@ static int gsr_capture_kms_capture(gsr_capture *cap, AVFrame *frame, gsr_color_c capture_is_combined_plane = true; } - cursor_drm_fd = find_cursor_drm(&self->kms_response); + cursor_drm_fd = find_cursor_drm(&self->kms_response, drm_fd->connector_id); if(!drm_fd) return -1; @@ -337,6 +363,10 @@ static int gsr_capture_kms_capture(gsr_capture *cap, AVFrame *frame, gsr_color_c if(!capture_is_combined_plane && cursor_drm_fd && cursor_drm_fd->connector_id != drm_fd->connector_id) cursor_drm_fd = NULL; + const bool is_x11 = gsr_egl_get_display_server(self->params.egl) == GSR_DISPLAY_SERVER_X11; + if(is_x11) + cursor_drm_fd = NULL; + if(drm_fd->has_hdr_metadata && self->params.hdr && hdr_metadata_is_supported_format(&drm_fd->hdr_metadata)) gsr_kms_set_hdr_metadata(self, drm_fd); @@ -471,6 +501,25 @@ static int gsr_capture_kms_capture(gsr_capture *cap, AVFrame *frame, gsr_color_c self->params.egl->glDisable(GL_SCISSOR_TEST); } + if(self->params.record_cursor && !cursor_drm_fd && is_x11) { + gsr_cursor_tick(&self->x11_cursor, DefaultRootWindow(self->params.egl->x11.dpy)); + + const vec2i cursor_pos = { + target_x + self->x11_cursor.position.x - self->x11_cursor.hotspot.x - capture_pos.x, + target_y + self->x11_cursor.position.y - self->x11_cursor.hotspot.y - capture_pos.y + }; + + self->params.egl->glEnable(GL_SCISSOR_TEST); + self->params.egl->glScissor(target_x, target_y, self->capture_size.x, self->capture_size.y); + + gsr_color_conversion_draw(color_conversion, self->x11_cursor.texture_id, + cursor_pos, self->x11_cursor.size, + (vec2i){0, 0}, self->x11_cursor.size, + 0.0f, false); + + self->params.egl->glDisable(GL_SCISSOR_TEST); + } + //self->params.egl->glFlush(); //self->params.egl->glFinish(); @@ -566,7 +615,7 @@ gsr_capture* gsr_capture_kms_create(const gsr_capture_kms_params *params) { *cap = (gsr_capture) { .start = gsr_capture_kms_start, - .tick = NULL, + .tick = gsr_capture_kms_tick, .should_stop = gsr_capture_kms_should_stop, .capture = gsr_capture_kms_capture, .capture_end = gsr_capture_kms_capture_end, diff --git a/src/capture/xcomposite.c b/src/capture/xcomposite.c index a81c19f..899ffe0 100644 --- a/src/capture/xcomposite.c +++ b/src/capture/xcomposite.c @@ -325,11 +325,6 @@ static int gsr_capture_xcomposite_capture(gsr_capture *cap, AVFrame *frame, gsr_ const int target_x = max_int(0, frame->width / 2 - self->texture_size.x / 2); const int target_y = max_int(0, frame->height / 2 - self->texture_size.y / 2); - const vec2i cursor_pos = { - target_x + self->cursor.position.x - self->cursor.hotspot.x, - target_y + self->cursor.position.y - self->cursor.hotspot.y - }; - gsr_color_conversion_draw(color_conversion, window_texture_get_opengl_texture_id(&self->window_texture), (vec2i){target_x, target_y}, self->texture_size, (vec2i){0, 0}, self->texture_size, @@ -338,6 +333,11 @@ static int gsr_capture_xcomposite_capture(gsr_capture *cap, AVFrame *frame, gsr_ if(self->params.record_cursor && self->cursor.visible) { gsr_cursor_tick(&self->cursor, self->window); + const vec2i cursor_pos = { + target_x + self->cursor.position.x - self->cursor.hotspot.x, + target_y + self->cursor.position.y - self->cursor.hotspot.y + }; + self->params.egl->glEnable(GL_SCISSOR_TEST); self->params.egl->glScissor(target_x, target_y, self->texture_size.x, self->texture_size.y); diff --git a/src/egl.c b/src/egl.c index 3dda33d..2f5230f 100644 --- a/src/egl.c +++ b/src/egl.c @@ -1,16 +1,17 @@ #include "../include/egl.h" #include "../include/library_loader.h" #include "../include/utils.h" + #include #include #include #include #include +#include +#include #include #include -#include -#include // TODO: rename gsr_egl to something else since this includes both egl and glx and in the future maybe vulkan too @@ -93,7 +94,7 @@ static void registry_add_object(void *data, struct wl_registry *registry, uint32 } if(egl->wayland.num_outputs == GSR_MAX_OUTPUTS) { - fprintf(stderr, "gsr warning: reached maximum outputs (32), ignoring output %u\n", name); + fprintf(stderr, "gsr warning: reached maximum outputs (%d), ignoring output %u\n", GSR_MAX_OUTPUTS, name); return; } @@ -134,6 +135,26 @@ static void reset_cap_nice(void) { cap_free(caps); } +static void store_x11_monitor(const gsr_monitor *monitor, void *userdata) { + gsr_egl *egl = userdata; + if(egl->x11.num_outputs == GSR_MAX_OUTPUTS) { + fprintf(stderr, "gsr warning: reached maximum outputs (%d), ignoring output %s\n", GSR_MAX_OUTPUTS, monitor->name); + return; + } + + char *monitor_name = strdup(monitor->name); + if(!monitor_name) + return; + + const int index = egl->x11.num_outputs; + egl->x11.outputs[index].name = monitor_name; + egl->x11.outputs[index].pos = monitor->pos; + egl->x11.outputs[index].size = monitor->size; + egl->x11.outputs[index].connector_id = monitor->connector_id; + egl->x11.outputs[index].rotation = monitor->rotation; + ++egl->x11.num_outputs; +} + #define GLX_DRAWABLE_TYPE 0x8010 #define GLX_RENDER_TYPE 0x8011 #define GLX_RGBA_BIT 0x00000001 @@ -271,6 +292,11 @@ static bool gsr_egl_create_window(gsr_egl *self, bool wayland) { goto fail; } + if(!wayland) { + self->x11.num_outputs = 0; + for_each_active_monitor_output_x11_not_cached(self->x11.dpy, store_x11_monitor, self); + } + reset_cap_nice(); return true; @@ -587,6 +613,14 @@ void gsr_egl_unload(gsr_egl *self) { self->x11.window = None; } + for(int i = 0; i < self->x11.num_outputs; ++i) { + if(self->x11.outputs[i].name) { + free(self->x11.outputs[i].name); + self->x11.outputs[i].name = NULL; + } + } + self->x11.num_outputs = 0; + if(self->wayland.window) { wl_egl_window_destroy(self->wayland.window); self->wayland.window = NULL; @@ -658,3 +692,10 @@ void gsr_egl_swap_buffers(gsr_egl *self) { self->glXSwapBuffers(self->x11.dpy, self->x11.window); } } + +gsr_display_server gsr_egl_get_display_server(const gsr_egl *egl) { + if(egl->wayland.dpy) + return GSR_DISPLAY_SERVER_WAYLAND; + else + return GSR_DISPLAY_SERVER_X11; +} diff --git a/src/main.cpp b/src/main.cpp index 760dcd6..cd1ec5d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1650,7 +1650,7 @@ static bool is_xwayland(Display *display) { return true; bool xwayland_found = false; - for_each_active_monitor_output_x11(display, xwayland_check_callback, &xwayland_found); + for_each_active_monitor_output_x11_not_cached(display, xwayland_check_callback, &xwayland_found); return xwayland_found; } @@ -1716,7 +1716,7 @@ typedef struct { static void output_monitor_info(const gsr_monitor *monitor, void *userdata) { const capture_options_callback *options = (capture_options_callback*)userdata; - if(monitor_capture_use_drm(options->egl, options->wayland)) { + if(options->wayland && monitor_capture_use_drm(options->egl, options->wayland)) { vec2i monitor_size = monitor->size; const gsr_monitor_rotation rot = drm_monitor_get_display_server_rotation(options->egl, monitor); if(rot == GSR_MONITOR_ROT_90 || rot == GSR_MONITOR_ROT_270) @@ -1737,7 +1737,9 @@ static void list_supported_capture_options(gsr_egl *egl, bool wayland) { options.wayland = wayland; options.egl = egl; if(monitor_capture_use_drm(egl, wayland)) { - for_each_active_monitor_output(egl, GSR_CONNECTION_DRM, output_monitor_info, &options); + const bool is_x11 = gsr_egl_get_display_server(egl) == GSR_DISPLAY_SERVER_X11; + const gsr_connection_type connection_type = is_x11 ? GSR_CONNECTION_X11 : GSR_CONNECTION_DRM; + for_each_active_monitor_output(egl, connection_type, output_monitor_info, &options); } else { puts("screen"); // All monitors in one, only available on Nvidia X11 for_each_active_monitor_output(egl, GSR_CONNECTION_X11, output_monitor_info, &options); @@ -1775,10 +1777,11 @@ static void info_command() { if(!wayland) wayland = is_xwayland(dpy); - if(!wayland) { + if(!wayland && is_using_prime_run()) { // Disable prime-run and similar options as it doesn't work, the monitor to capture has to be run on the same device. // This is fine on wayland since nvidia uses drm interface there and the monitor query checks the monitors connected // to the drm device. + fprintf(stderr, "Warning: use of prime-run on X11 is not supported. Disabling prime-run\n"); disable_prime_run(); } @@ -1890,10 +1893,13 @@ static gsr_capture* create_capture_impl(const char *window_str, const char *scre #endif } else if(contains_non_hex_number(window_str)) { if(monitor_capture_use_drm(egl, wayland)) { + const bool is_x11 = gsr_egl_get_display_server(egl) == GSR_DISPLAY_SERVER_X11; + const gsr_connection_type connection_type = is_x11 ? GSR_CONNECTION_X11 : GSR_CONNECTION_DRM; + if(strcmp(window_str, "screen") == 0) { FirstOutputCallback first_output; first_output.output_name = NULL; - for_each_active_monitor_output(egl, GSR_CONNECTION_DRM, get_first_output, &first_output); + for_each_active_monitor_output(egl, connection_type, get_first_output, &first_output); if(first_output.output_name) { window_str = first_output.output_name; @@ -1901,14 +1907,14 @@ static gsr_capture* create_capture_impl(const char *window_str, const char *scre fprintf(stderr, "Error: no usable output found\n"); _exit(1); } - } - - gsr_monitor gmon; - if(!get_monitor_by_name(egl, GSR_CONNECTION_DRM, window_str, &gmon)) { - fprintf(stderr, "gsr error: display \"%s\" not found, expected one of:\n", window_str); - fprintf(stderr, " \"screen\"\n"); - for_each_active_monitor_output(egl, GSR_CONNECTION_DRM, monitor_output_callback_print, NULL); - _exit(1); + } else { + gsr_monitor gmon; + if(!get_monitor_by_name(egl, connection_type, window_str, &gmon)) { + fprintf(stderr, "gsr error: display \"%s\" not found, expected one of:\n", window_str); + fprintf(stderr, " \"screen\"\n"); + for_each_active_monitor_output(egl, connection_type, monitor_output_callback_print, NULL); + _exit(1); + } } } else { if(strcmp(window_str, "screen") != 0 && strcmp(window_str, "screen-direct") != 0 && strcmp(window_str, "screen-direct-force") != 0) { @@ -2464,10 +2470,11 @@ int main(int argc, char **argv) { if(!wayland) wayland = is_xwayland(dpy); - if(!wayland) { + if(!wayland && is_using_prime_run()) { // Disable prime-run and similar options as it doesn't work, the monitor to capture has to be run on the same device. // This is fine on wayland since nvidia uses drm interface there and the monitor query checks the monitors connected // to the drm device. + fprintf(stderr, "Warning: use of prime-run on X11 is not supported. Disabling prime-run\n"); disable_prime_run(); } diff --git a/src/utils.c b/src/utils.c index 28c66e0..7cd57cb 100644 --- a/src/utils.c +++ b/src/utils.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -24,6 +25,16 @@ double clock_get_monotonic_seconds(void) { return (double)ts.tv_sec + (double)ts.tv_nsec * 0.000000001; } +static gsr_monitor_rotation wayland_transform_to_gsr_rotation(int32_t rot) { + switch(rot) { + case 0: return GSR_MONITOR_ROT_0; + case 1: return GSR_MONITOR_ROT_90; + case 2: return GSR_MONITOR_ROT_180; + case 3: return GSR_MONITOR_ROT_270; + } + return GSR_MONITOR_ROT_0; +} + static const XRRModeInfo* get_mode_info(const XRRScreenResources *sr, RRMode id) { for(int i = 0; i < sr->nmode; ++i) { if(sr->modes[i].id == id) @@ -42,16 +53,6 @@ static gsr_monitor_rotation x11_rotation_to_gsr_rotation(int rot) { return GSR_MONITOR_ROT_0; } -static gsr_monitor_rotation wayland_transform_to_gsr_rotation(int32_t rot) { - switch(rot) { - case 0: return GSR_MONITOR_ROT_0; - case 1: return GSR_MONITOR_ROT_90; - case 2: return GSR_MONITOR_ROT_180; - case 3: return GSR_MONITOR_ROT_270; - } - return GSR_MONITOR_ROT_0; -} - static uint32_t x11_output_get_connector_id(Display *dpy, RROutput output, Atom randr_connector_id_atom) { Atom type = 0; int format = 0; @@ -68,7 +69,7 @@ static uint32_t x11_output_get_connector_id(Display *dpy, RROutput output, Atom return result; } -void for_each_active_monitor_output_x11(Display *display, active_monitor_callback callback, void *userdata) { +void for_each_active_monitor_output_x11_not_cached(Display *display, active_monitor_callback callback, void *userdata) { XRRScreenResources *screen_res = XRRGetScreenResources(display, DefaultRootWindow(display)); if(!screen_res) return; @@ -83,15 +84,12 @@ void for_each_active_monitor_output_x11(Display *display, active_monitor_callbac if(crt_info && crt_info->mode) { const XRRModeInfo *mode_info = get_mode_info(screen_res, crt_info->mode); if(mode_info && out_info->nameLen < (int)sizeof(display_name)) { - memcpy(display_name, out_info->name, out_info->nameLen); - display_name[out_info->nameLen] = '\0'; - + snprintf(display_name, sizeof(display_name), "%.*s", (int)out_info->nameLen, out_info->name); const gsr_monitor monitor = { .name = display_name, .name_len = out_info->nameLen, .pos = { .x = crt_info->x, .y = crt_info->y }, .size = { .x = (int)crt_info->width, .y = (int)crt_info->height }, - .crt_info = crt_info, .connector_id = x11_output_get_connector_id(display, screen_res->outputs[i], randr_connector_id_atom), .rotation = x11_rotation_to_gsr_rotation(crt_info->rotation), .monitor_identifier = 0 @@ -109,6 +107,22 @@ void for_each_active_monitor_output_x11(Display *display, active_monitor_callbac XRRFreeScreenResources(screen_res); } +void for_each_active_monitor_output_x11(const gsr_egl *egl, active_monitor_callback callback, void *userdata) { + for(int i = 0; i < egl->x11.num_outputs; ++i) { + const gsr_x11_output *output = &egl->x11.outputs[i]; + const gsr_monitor monitor = { + .name = output->name, + .name_len = strlen(output->name), + .pos = output->pos, + .size = output->size, + .connector_id = output->connector_id, + .rotation = output->rotation, + .monitor_identifier = 0 + }; + callback(&monitor, userdata); + } +} + typedef struct { int type; int count; @@ -192,7 +206,6 @@ static void for_each_active_monitor_output_wayland(const gsr_egl *egl, active_mo .name_len = strlen(output->name), .pos = { .x = output->pos.x, .y = output->pos.y }, .size = { .x = output->size.x, .y = output->size.y }, - .crt_info = NULL, .connector_id = 0, .rotation = wayland_transform_to_gsr_rotation(output->transform), .monitor_identifier = connector_type ? monitor_identifier_from_type_and_count(connector_type_index, connector_type->count_active) : 0 @@ -240,12 +253,11 @@ static void for_each_active_monitor_output_drm(const gsr_egl *egl, active_monito if(connector_type && crtc_id > 0 && crtc && connection_name_len + 5 < (int)sizeof(display_name)) { const int display_name_len = snprintf(display_name, sizeof(display_name), "%s-%d", connection_name, connector_type->count); const int connector_type_index_name = get_connector_type_by_name(display_name); - const gsr_monitor monitor = { + gsr_monitor monitor = { .name = display_name, .name_len = display_name_len, .pos = { .x = crtc->x, .y = crtc->y }, .size = { .x = (int)crtc->width, .y = (int)crtc->height }, - .crt_info = NULL, .connector_id = connector->connector_id, .rotation = GSR_MONITOR_ROT_0, .monitor_identifier = connector_type_index_name != -1 ? monitor_identifier_from_type_and_count(connector_type_index_name, connector_type->count_active) : 0 @@ -267,7 +279,7 @@ static void for_each_active_monitor_output_drm(const gsr_egl *egl, active_monito void for_each_active_monitor_output(const gsr_egl *egl, gsr_connection_type connection_type, active_monitor_callback callback, void *userdata) { switch(connection_type) { case GSR_CONNECTION_X11: - for_each_active_monitor_output_x11(egl->x11.dpy, callback, userdata); + for_each_active_monitor_output_x11(egl, callback, userdata); break; case GSR_CONNECTION_WAYLAND: for_each_active_monitor_output_wayland(egl, callback, userdata); @@ -329,7 +341,7 @@ static void get_monitor_by_connector_id_callback(const gsr_monitor *monitor, voi } gsr_monitor_rotation drm_monitor_get_display_server_rotation(const gsr_egl *egl, const gsr_monitor *monitor) { - if(egl->wayland.dpy) { + if(gsr_egl_get_display_server(egl) == GSR_DISPLAY_SERVER_WAYLAND) { { get_monitor_by_connector_id_userdata userdata; userdata.monitor = monitor; @@ -352,7 +364,7 @@ gsr_monitor_rotation drm_monitor_get_display_server_rotation(const gsr_egl *egl, userdata.monitor = monitor; userdata.rotation = GSR_MONITOR_ROT_0; userdata.match_found = false; - for_each_active_monitor_output_x11(egl->x11.dpy, get_monitor_by_connector_id_callback, &userdata); + for_each_active_monitor_output_x11(egl, get_monitor_by_connector_id_callback, &userdata); return userdata.rotation; } -- cgit v1.2.3