diff options
author | dec05eba <dec05eba@protonmail.com> | 2024-12-08 02:17:41 +0100 |
---|---|---|
committer | dec05eba <dec05eba@protonmail.com> | 2024-12-08 02:17:41 +0100 |
commit | c259a19b9d8ca646824acad60b599b03252bc1c0 (patch) | |
tree | d3c7f3090735b3ae225868d514a9163c02941489 | |
parent | 655fd3756be4507030aac1d497e579cfc0070f3c (diff) |
Refactor windowing from egl to window_x11/window_wayland, yolo
-rw-r--r-- | include/damage.h | 1 | ||||
-rw-r--r-- | include/egl.h | 55 | ||||
-rw-r--r-- | include/utils.h | 13 | ||||
-rw-r--r-- | include/window/window.h | 37 | ||||
-rw-r--r-- | include/window/window_wayland.h | 8 | ||||
-rw-r--r-- | include/window/window_x11.h | 10 | ||||
-rw-r--r-- | meson.build | 3 | ||||
-rw-r--r-- | src/capture/kms.c | 14 | ||||
-rw-r--r-- | src/capture/nvfbc.c | 14 | ||||
-rw-r--r-- | src/capture/xcomposite.c | 29 | ||||
-rw-r--r-- | src/damage.c | 54 | ||||
-rw-r--r-- | src/egl.c | 388 | ||||
-rw-r--r-- | src/encoder/video/nvenc.c | 7 | ||||
-rw-r--r-- | src/main.cpp | 76 | ||||
-rw-r--r-- | src/utils.c | 115 | ||||
-rw-r--r-- | src/window/window.c | 30 | ||||
-rw-r--r-- | src/window/window_wayland.c | 319 | ||||
-rw-r--r-- | src/window/window_x11.c | 162 |
18 files changed, 801 insertions, 534 deletions
diff --git a/include/damage.h b/include/damage.h index 7229418..4b10e58 100644 --- a/include/damage.h +++ b/include/damage.h @@ -17,6 +17,7 @@ typedef enum { typedef struct { gsr_egl *egl; + Display *display; bool track_cursor; gsr_damage_track_type track_type; diff --git a/include/egl.h b/include/egl.h index 8958f31..41ad906 100644 --- a/include/egl.h +++ b/include/egl.h @@ -10,6 +10,8 @@ #include "vec2.h" #include "defs.h" +typedef struct gsr_window gsr_window; + #ifdef _WIN64 typedef signed long long int khronos_intptr_t; typedef unsigned long long int khronos_uintptr_t; @@ -152,54 +154,11 @@ typedef int (*FUNC_eglQueryDisplayAttribEXT)(EGLDisplay dpy, int32_t attribute, typedef const char* (*FUNC_eglQueryDeviceStringEXT)(void *device, int32_t name); typedef int (*FUNC_eglQueryDmaBufModifiersEXT)(EGLDisplay dpy, int32_t format, int32_t max_modifiers, uint64_t *modifiers, int *external_only, int32_t *num_modifiers); -#define GSR_MAX_OUTPUTS 32 - -typedef struct { - char *name; - vec2i pos; - vec2i size; - uint32_t connector_id; - gsr_monitor_rotation rotation; - uint32_t monitor_identifier; /* crtc id */ -} gsr_x11_output; - -typedef struct { - Display *dpy; - Window window; - gsr_x11_output outputs[GSR_MAX_OUTPUTS]; - int num_outputs; - XEvent xev; -} gsr_x11; - -typedef struct { - uint32_t wl_name; - void *output; - vec2i pos; - vec2i size; - int32_t transform; - char *name; -} gsr_wayland_output; - -typedef struct { - void *dpy; - void *window; - void *registry; - void *surface; - void *compositor; - gsr_wayland_output outputs[GSR_MAX_OUTPUTS]; - int num_outputs; -} gsr_wayland; - typedef enum { GSR_GL_CONTEXT_TYPE_EGL, 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; @@ -207,6 +166,7 @@ struct gsr_egl { void *gl_library; gsr_gl_context_type context_type; + gsr_window *window; EGLDisplay egl_display; EGLSurface egl_surface; @@ -218,8 +178,6 @@ struct gsr_egl { gsr_gpu_info gpu_info; - gsr_x11 x11; - gsr_wayland wayland; char card_path[128]; int32_t (*eglGetError)(void); @@ -319,15 +277,10 @@ struct gsr_egl { unsigned char (*glUnmapBuffer)(unsigned int target); }; -bool gsr_egl_load(gsr_egl *self, Display *dpy, bool wayland, bool is_monitor_capture); +bool gsr_egl_load(gsr_egl *self, gsr_window *window, bool is_monitor_capture); void gsr_egl_unload(gsr_egl *self); -/* Returns true if an event is available */ -bool gsr_egl_process_event(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 *self); -XEvent* gsr_egl_get_event_data(gsr_egl *self); - #endif /* GSR_EGL_H */ diff --git a/include/utils.h b/include/utils.h index 5b771ba..d3af640 100644 --- a/include/utils.h +++ b/include/utils.h @@ -7,6 +7,8 @@ #include <stdbool.h> #include <stdint.h> +#define CONNECTOR_TYPE_COUNTS 32 + typedef struct AVCodecContext AVCodecContext; typedef struct AVFrame AVFrame; @@ -27,17 +29,26 @@ typedef struct { bool found_monitor; } get_monitor_by_name_userdata; +typedef struct { + int type; + int count; + int count_active; +} drm_connector_type_count; + double clock_get_monotonic_seconds(void); bool generate_random_characters(char *buffer, int buffer_size, const char *alphabet, size_t alphabet_size); bool generate_random_characters_standard_alphabet(char *buffer, int buffer_size); typedef void (*active_monitor_callback)(const gsr_monitor *monitor, 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); +int get_connector_type_by_name(const char *name); +drm_connector_type_count* drm_connector_types_get_index(drm_connector_type_count *type_counts, int *num_type_counts, int connector_type); +uint32_t monitor_identifier_from_type_and_count(int monitor_type_index, int monitor_type_count); + bool gl_get_gpu_info(gsr_egl *egl, gsr_gpu_info *info); bool gl_driver_version_greater_than(const gsr_egl *egl, int major, int minor, int patch); diff --git a/include/window/window.h b/include/window/window.h new file mode 100644 index 0000000..7839f6a --- /dev/null +++ b/include/window/window.h @@ -0,0 +1,37 @@ +#ifndef GSR_WINDOW_H +#define GSR_WINDOW_H + +#include "../utils.h" +#include <stdbool.h> + +typedef union _XEvent XEvent; +typedef struct gsr_window gsr_window; + +typedef enum { + GSR_DISPLAY_SERVER_X11, + GSR_DISPLAY_SERVER_WAYLAND +} gsr_display_server; + +struct gsr_window { + void (*destroy)(gsr_window *self); + /* Returns true if an event is available */ + bool (*process_event)(gsr_window *self); + XEvent* (*get_event_data)(gsr_window *self); /* can be NULL */ + gsr_display_server (*get_display_server)(void); + void* (*get_display)(gsr_window *self); + void* (*get_window)(gsr_window *self); + void (*for_each_active_monitor_output_cached)(const gsr_window *self, active_monitor_callback callback, void *userdata); + void *priv; +}; + +void gsr_window_destroy(gsr_window *self); + +/* Returns true if an event is available */ +bool gsr_window_process_event(gsr_window *self); +XEvent* gsr_window_get_event_data(gsr_window *self); +gsr_display_server gsr_window_get_display_server(const gsr_window *self); +void* gsr_window_get_display(gsr_window *self); +void* gsr_window_get_window(gsr_window *self); +void gsr_window_for_each_active_monitor_output_cached(const gsr_window *self, active_monitor_callback callback, void *userdata); + +#endif /* GSR_WINDOW_H */ diff --git a/include/window/window_wayland.h b/include/window/window_wayland.h new file mode 100644 index 0000000..3535b0f --- /dev/null +++ b/include/window/window_wayland.h @@ -0,0 +1,8 @@ +#ifndef GSR_WINDOW_WAYLAND_H +#define GSR_WINDOW_WAYLAND_H + +#include "window.h" + +gsr_window* gsr_window_wayland_create(void); + +#endif /* GSR_WINDOW_WAYLAND_H */ diff --git a/include/window/window_x11.h b/include/window/window_x11.h new file mode 100644 index 0000000..e0c2948 --- /dev/null +++ b/include/window/window_x11.h @@ -0,0 +1,10 @@ +#ifndef GSR_WINDOW_X11_H +#define GSR_WINDOW_X11_H + +#include "window.h" + +typedef struct _XDisplay Display; + +gsr_window* gsr_window_x11_create(Display *display); + +#endif /* GSR_WINDOW_X11_H */ diff --git a/meson.build b/meson.build index 30c763e..1f6aca7 100644 --- a/meson.build +++ b/meson.build @@ -21,6 +21,9 @@ src = [ 'src/codec_query/nvenc.c', 'src/codec_query/vaapi.c', 'src/codec_query/vulkan.c', + 'src/window/window.c', + 'src/window/window_x11.c', + 'src/window/window_wayland.c', 'src/egl.c', 'src/cuda.c', 'src/xnvctrl.c', diff --git a/src/capture/kms.c b/src/capture/kms.c index fcf3b85..d692929 100644 --- a/src/capture/kms.c +++ b/src/capture/kms.c @@ -2,6 +2,7 @@ #include "../../include/utils.h" #include "../../include/color_conversion.h" #include "../../include/cursor.h" +#include "../../include/window/window.h" #include "../../kms/client/kms_client.h" #include <stdlib.h> @@ -183,10 +184,12 @@ static int gsr_capture_kms_start(gsr_capture *cap, AVCodecContext *video_codec_c if(kms_init_res != 0) return kms_init_res; - self->is_x11 = gsr_egl_get_display_server(self->params.egl) == GSR_DISPLAY_SERVER_X11; + self->is_x11 = gsr_window_get_display_server(self->params.egl->window) == GSR_DISPLAY_SERVER_X11; const gsr_connection_type connection_type = self->is_x11 ? GSR_CONNECTION_X11 : GSR_CONNECTION_DRM; - if(self->is_x11) - gsr_cursor_init(&self->x11_cursor, self->params.egl, self->params.egl->x11.dpy); + if(self->is_x11) { + Display *display = gsr_window_get_display(self->params.egl->window); + gsr_cursor_init(&self->x11_cursor, self->params.egl, display); + } MonitorCallbackUserdata monitor_callback_userdata = { &self->monitor_id, @@ -240,7 +243,7 @@ static void gsr_capture_kms_on_event(gsr_capture *cap, gsr_egl *egl) { if(!self->is_x11) return; - XEvent *xev = gsr_egl_get_event_data(egl); + XEvent *xev = gsr_window_get_event_data(egl->window); gsr_cursor_on_event(&self->x11_cursor, xev); } @@ -525,7 +528,8 @@ static void render_x11_cursor(gsr_capture_kms *self, gsr_color_conversion *color self->capture_size.y == 0 ? 0 : (double)output_size.y / (double)self->capture_size.y }; - gsr_cursor_tick(&self->x11_cursor, DefaultRootWindow(self->params.egl->x11.dpy)); + Display *display = gsr_window_get_display(self->params.egl->window); + gsr_cursor_tick(&self->x11_cursor, DefaultRootWindow(display)); const vec2i cursor_pos = { target_pos.x + (self->x11_cursor.position.x - self->x11_cursor.hotspot.x - capture_pos.x) * scale.x, diff --git a/src/capture/nvfbc.c b/src/capture/nvfbc.c index d5a270e..676d269 100644 --- a/src/capture/nvfbc.c +++ b/src/capture/nvfbc.c @@ -3,12 +3,14 @@ #include "../../include/egl.h" #include "../../include/utils.h" #include "../../include/color_conversion.h" +#include "../../include/window/window.h" #include <dlfcn.h> #include <stdlib.h> #include <string.h> #include <stdio.h> #include <math.h> +#include <assert.h> #include <X11/Xlib.h> #include <libavcodec/avcodec.h> @@ -136,7 +138,10 @@ static void set_vertical_sync_enabled(gsr_egl *egl, int enabled) { int result = 0; if(egl->glXSwapIntervalEXT) { - egl->glXSwapIntervalEXT(egl->x11.dpy, egl->x11.window, enabled ? 1 : 0); + assert(gsr_window_get_display_server(egl->window) == GSR_DISPLAY_SERVER_X11); + Display *display = gsr_window_get_display(egl->window); + const Window window = (Window)gsr_window_get_window(egl->window); + egl->glXSwapIntervalEXT(display, window, enabled ? 1 : 0); } else if(egl->glXSwapIntervalMESA) { result = egl->glXSwapIntervalMESA(enabled ? 1 : 0); } else if(egl->glXSwapIntervalSGI) { @@ -219,8 +224,11 @@ static int gsr_capture_nvfbc_setup_handle(gsr_capture_nvfbc *self) { goto error_cleanup; } - self->tracking_width = XWidthOfScreen(DefaultScreenOfDisplay(self->params.egl->x11.dpy)); - self->tracking_height = XHeightOfScreen(DefaultScreenOfDisplay(self->params.egl->x11.dpy)); + assert(gsr_window_get_display_server(self->params.egl->window) == GSR_DISPLAY_SERVER_X11); + Display *display = gsr_window_get_display(self->params.egl->window); + + self->tracking_width = XWidthOfScreen(DefaultScreenOfDisplay(display)); + self->tracking_height = XHeightOfScreen(DefaultScreenOfDisplay(display)); self->tracking_type = strcmp(self->params.display_to_capture, "screen") == 0 ? NVFBC_TRACKING_SCREEN : NVFBC_TRACKING_OUTPUT; if(self->tracking_type == NVFBC_TRACKING_OUTPUT) { if(!status_params.bXRandRAvailable) { diff --git a/src/capture/xcomposite.c b/src/capture/xcomposite.c index 79cd60a..66c700d 100644 --- a/src/capture/xcomposite.c +++ b/src/capture/xcomposite.c @@ -3,6 +3,7 @@ #include "../../include/utils.h" #include "../../include/cursor.h" #include "../../include/color_conversion.h" +#include "../../include/window/window.h" #include <stdlib.h> #include <stdio.h> @@ -16,6 +17,7 @@ typedef struct { gsr_capture_xcomposite_params params; + Display *display; bool should_stop; bool stop_is_error; @@ -66,12 +68,12 @@ static int gsr_capture_xcomposite_start(gsr_capture *cap, AVCodecContext *video_ gsr_capture_xcomposite *self = cap->priv; if(self->params.follow_focused) { - self->net_active_window_atom = XInternAtom(self->params.egl->x11.dpy, "_NET_ACTIVE_WINDOW", False); + self->net_active_window_atom = XInternAtom(self->display, "_NET_ACTIVE_WINDOW", False); if(!self->net_active_window_atom) { fprintf(stderr, "gsr error: gsr_capture_xcomposite_start failed: failed to get _NET_ACTIVE_WINDOW atom\n"); return -1; } - self->window = get_focused_window(self->params.egl->x11.dpy, self->net_active_window_atom); + self->window = get_focused_window(self->display, self->net_active_window_atom); } else { self->window = self->params.window; } @@ -79,7 +81,7 @@ static int gsr_capture_xcomposite_start(gsr_capture *cap, AVCodecContext *video_ /* TODO: Do these in tick, and allow error if follow_focused */ XWindowAttributes attr; - if(!XGetWindowAttributes(self->params.egl->x11.dpy, self->window, &attr) && !self->params.follow_focused) { + if(!XGetWindowAttributes(self->display, self->window, &attr) && !self->params.follow_focused) { fprintf(stderr, "gsr error: gsr_capture_xcomposite_start failed: invalid window id: %lu\n", self->window); return -1; } @@ -88,19 +90,19 @@ static int gsr_capture_xcomposite_start(gsr_capture *cap, AVCodecContext *video_ self->window_size.y = max_int(attr.height, 0); if(self->params.follow_focused) - XSelectInput(self->params.egl->x11.dpy, DefaultRootWindow(self->params.egl->x11.dpy), PropertyChangeMask); + XSelectInput(self->display, DefaultRootWindow(self->display), PropertyChangeMask); // TODO: Get select and add these on top of it and then restore at the end. Also do the same in other xcomposite - XSelectInput(self->params.egl->x11.dpy, self->window, StructureNotifyMask | ExposureMask); + XSelectInput(self->display, self->window, StructureNotifyMask | ExposureMask); /* Disable vsync */ self->params.egl->eglSwapInterval(self->params.egl->egl_display, 0); - if(window_texture_init(&self->window_texture, self->params.egl->x11.dpy, self->window, self->params.egl) != 0 && !self->params.follow_focused) { + if(window_texture_init(&self->window_texture, self->display, self->window, self->params.egl) != 0 && !self->params.follow_focused) { fprintf(stderr, "gsr error: gsr_capture_xcomposite_start: failed to get window texture for window %ld\n", self->window); return -1; } - if(gsr_cursor_init(&self->cursor, self->params.egl, self->params.egl->x11.dpy) != 0) { + if(gsr_cursor_init(&self->cursor, self->params.egl, self->display) != 0) { gsr_capture_xcomposite_stop(self); return -1; } @@ -143,24 +145,24 @@ static void gsr_capture_xcomposite_tick(gsr_capture *cap) { if(self->init_new_window) { self->init_new_window = false; - Window focused_window = get_focused_window(self->params.egl->x11.dpy, self->net_active_window_atom); + Window focused_window = get_focused_window(self->display, self->net_active_window_atom); if(focused_window != self->window || !self->follow_focused_initialized) { self->follow_focused_initialized = true; - XSelectInput(self->params.egl->x11.dpy, self->window, 0); + XSelectInput(self->display, self->window, 0); self->window = focused_window; - XSelectInput(self->params.egl->x11.dpy, self->window, StructureNotifyMask | ExposureMask); + XSelectInput(self->display, self->window, StructureNotifyMask | ExposureMask); XWindowAttributes attr; attr.width = 0; attr.height = 0; - if(!XGetWindowAttributes(self->params.egl->x11.dpy, self->window, &attr)) + if(!XGetWindowAttributes(self->display, self->window, &attr)) fprintf(stderr, "gsr error: gsr_capture_xcomposite_tick failed: invalid window id: %lu\n", self->window); self->window_size.x = max_int(attr.width, 0); self->window_size.y = max_int(attr.height, 0); window_texture_deinit(&self->window_texture); - window_texture_init(&self->window_texture, self->params.egl->x11.dpy, self->window, self->params.egl); // TODO: Do not do the below window_texture_on_resize after this + window_texture_init(&self->window_texture, self->display, self->window, self->params.egl); // TODO: Do not do the below window_texture_on_resize after this self->texture_size.x = 0; self->texture_size.y = 0; @@ -200,7 +202,7 @@ static void gsr_capture_xcomposite_tick(gsr_capture *cap) { static void gsr_capture_xcomposite_on_event(gsr_capture *cap, gsr_egl *egl) { gsr_capture_xcomposite *self = cap->priv; - XEvent *xev = gsr_egl_get_event_data(egl); + XEvent *xev = gsr_window_get_event_data(egl->window); switch(xev->type) { case DestroyNotify: { /* Window died (when not following focused window), so we stop recording */ @@ -350,6 +352,7 @@ gsr_capture* gsr_capture_xcomposite_create(const gsr_capture_xcomposite_params * } cap_xcomp->params = *params; + cap_xcomp->display = gsr_window_get_display(params->egl->window); *cap = (gsr_capture) { .start = gsr_capture_xcomposite_start, diff --git a/src/damage.c b/src/damage.c index 10f4ccd..baf714e 100644 --- a/src/damage.c +++ b/src/damage.c @@ -1,5 +1,6 @@ #include "../include/damage.h" #include "../include/utils.h" +#include "../include/window/window.h" #include <stdio.h> #include <string.h> @@ -30,33 +31,34 @@ bool gsr_damage_init(gsr_damage *self, gsr_egl *egl, bool track_cursor) { self->egl = egl; self->track_cursor = track_cursor; - if(gsr_egl_get_display_server(egl) != GSR_DISPLAY_SERVER_X11) { + if(gsr_window_get_display_server(egl->window) != GSR_DISPLAY_SERVER_X11) { fprintf(stderr, "gsr warning: gsr_damage_init: damage tracking is not supported on wayland\n"); return false; } + self->display = gsr_window_get_display(egl->window); - if(!XDamageQueryExtension(self->egl->x11.dpy, &self->damage_event, &self->damage_error)) { + if(!XDamageQueryExtension(self->display, &self->damage_event, &self->damage_error)) { fprintf(stderr, "gsr warning: gsr_damage_init: XDamage is not supported by your X11 server\n"); gsr_damage_deinit(self); return false; } - if(!XRRQueryExtension(self->egl->x11.dpy, &self->randr_event, &self->randr_error)) { + if(!XRRQueryExtension(self->display, &self->randr_event, &self->randr_error)) { fprintf(stderr, "gsr warning: gsr_damage_init: XRandr is not supported by your X11 server\n"); gsr_damage_deinit(self); return false; } - if(!xrandr_is_supported(self->egl->x11.dpy)) { + if(!xrandr_is_supported(self->display)) { fprintf(stderr, "gsr warning: gsr_damage_init: your X11 randr version is too old\n"); gsr_damage_deinit(self); return false; } if(self->track_cursor) - self->track_cursor = gsr_cursor_init(&self->cursor, self->egl, self->egl->x11.dpy) == 0; + self->track_cursor = gsr_cursor_init(&self->cursor, self->egl, self->display) == 0; - XRRSelectInput(self->egl->x11.dpy, DefaultRootWindow(self->egl->x11.dpy), RRScreenChangeNotifyMask | RRCrtcChangeNotifyMask | RROutputChangeNotifyMask); + XRRSelectInput(self->display, DefaultRootWindow(self->display), RRScreenChangeNotifyMask | RRCrtcChangeNotifyMask | RROutputChangeNotifyMask); self->damaged = true; return true; @@ -64,7 +66,7 @@ bool gsr_damage_init(gsr_damage *self, gsr_egl *egl, bool track_cursor) { void gsr_damage_deinit(gsr_damage *self) { if(self->damage) { - XDamageDestroy(self->egl->x11.dpy, self->damage); + XDamageDestroy(self->display, self->damage); self->damage = None; } @@ -85,22 +87,22 @@ bool gsr_damage_set_target_window(gsr_damage *self, uint64_t window) { return true; if(self->damage) { - XDamageDestroy(self->egl->x11.dpy, self->damage); + XDamageDestroy(self->display, self->damage); self->damage = None; } if(self->window) - XSelectInput(self->egl->x11.dpy, self->window, 0); + XSelectInput(self->display, self->window, 0); self->window = window; - XSelectInput(self->egl->x11.dpy, self->window, StructureNotifyMask | ExposureMask); + XSelectInput(self->display, self->window, StructureNotifyMask | ExposureMask); XWindowAttributes win_attr; win_attr.x = 0; win_attr.y = 0; win_attr.width = 0; win_attr.height = 0; - if(!XGetWindowAttributes(self->egl->x11.dpy, self->window, &win_attr)) + if(!XGetWindowAttributes(self->display, self->window, &win_attr)) fprintf(stderr, "gsr warning: gsr_damage_set_target_window failed: failed to get window attributes: %ld\n", (long)self->window); //self->window_pos.x = win_attr.x; @@ -109,9 +111,9 @@ bool gsr_damage_set_target_window(gsr_damage *self, uint64_t window) { self->window_size.x = win_attr.width; self->window_size.y = win_attr.height; - self->damage = XDamageCreate(self->egl->x11.dpy, window, XDamageReportNonEmpty); + self->damage = XDamageCreate(self->display, window, XDamageReportNonEmpty); if(self->damage) { - XDamageSubtract(self->egl->x11.dpy, self->damage, None, None); + XDamageSubtract(self->display, self->damage, None, None); self->damaged = true; self->track_type = GSR_DAMAGE_TRACK_WINDOW; return true; @@ -130,7 +132,7 @@ bool gsr_damage_set_target_monitor(gsr_damage *self, const char *monitor_name) { return true; if(self->damage) { - XDamageDestroy(self->egl->x11.dpy, self->damage); + XDamageDestroy(self->display, self->damage); self->damage = None; } @@ -141,12 +143,12 @@ bool gsr_damage_set_target_monitor(gsr_damage *self, const char *monitor_name) { } if(self->window) - XSelectInput(self->egl->x11.dpy, self->window, 0); + XSelectInput(self->display, self->window, 0); - self->window = DefaultRootWindow(self->egl->x11.dpy); - self->damage = XDamageCreate(self->egl->x11.dpy, self->window, XDamageReportNonEmpty); + self->window = DefaultRootWindow(self->display); + self->damage = XDamageCreate(self->display, self->window, XDamageReportNonEmpty); if(self->damage) { - XDamageSubtract(self->egl->x11.dpy, self->damage, None, None); + XDamageSubtract(self->display, self->damage, None, None); self->damaged = true; snprintf(self->monitor_name, sizeof(self->monitor_name), "%s", monitor_name); self->track_type = GSR_DAMAGE_TRACK_MONITOR; @@ -184,14 +186,14 @@ static void gsr_damage_on_output_change(gsr_damage *self, XEvent *xev) { if(!rr_output_change_event->output || self->monitor.monitor_identifier == 0) return; - XRRScreenResources *screen_res = XRRGetScreenResources(self->egl->x11.dpy, DefaultRootWindow(self->egl->x11.dpy)); + XRRScreenResources *screen_res = XRRGetScreenResources(self->display, DefaultRootWindow(self->display)); if(!screen_res) return; // TODO: What about scaled output? look at for_each_active_monitor_output_x11_not_cached - XRROutputInfo *out_info = XRRGetOutputInfo(self->egl->x11.dpy, screen_res, rr_output_change_event->output); + XRROutputInfo *out_info = XRRGetOutputInfo(self->display, screen_res, rr_output_change_event->output); if(out_info && out_info->crtc && out_info->crtc == self->monitor.monitor_identifier) { - XRRCrtcInfo *crtc_info = XRRGetCrtcInfo(self->egl->x11.dpy, screen_res, out_info->crtc); + XRRCrtcInfo *crtc_info = XRRGetCrtcInfo(self->display, screen_res, out_info->crtc); if(crtc_info && (crtc_info->x != self->monitor.pos.x || crtc_info->y != self->monitor.pos.y || (int)crtc_info->width != self->monitor.size.x || (int)crtc_info->height != self->monitor.size.y)) { @@ -226,15 +228,15 @@ static void gsr_damage_on_randr_event(gsr_damage *self, XEvent *xev) { static void gsr_damage_on_damage_event(gsr_damage *self, XEvent *xev) { const XDamageNotifyEvent *de = (XDamageNotifyEvent*)xev; - XserverRegion region = XFixesCreateRegion(self->egl->x11.dpy, NULL, 0); + XserverRegion region = XFixesCreateRegion(self->display, NULL, 0); /* Subtract all the damage, repairing the window */ - XDamageSubtract(self->egl->x11.dpy, de->damage, None, region); + XDamageSubtract(self->display, de->damage, None, region); if(self->track_type == GSR_DAMAGE_TRACK_WINDOW || (self->track_type == GSR_DAMAGE_TRACK_MONITOR && self->monitor.connector_id == 0)) { self->damaged = true; } else { int num_rectangles = 0; - XRectangle *rectangles = XFixesFetchRegion(self->egl->x11.dpy, region, &num_rectangles); + XRectangle *rectangles = XFixesFetchRegion(self->display, region, &num_rectangles); if(rectangles) { const gsr_rectangle monitor_region = { self->monitor.pos, self->monitor.size }; for(int i = 0; i < num_rectangles; ++i) { @@ -247,8 +249,8 @@ static void gsr_damage_on_damage_event(gsr_damage *self, XEvent *xev) { } } - XFixesDestroyRegion(self->egl->x11.dpy, region); - XFlush(self->egl->x11.dpy); + XFixesDestroyRegion(self->display, region); + XFlush(self->display); } static void gsr_damage_on_tick_cursor(gsr_damage *self) { @@ -1,4 +1,5 @@ #include "../include/egl.h" +#include "../include/window/window.h" #include "../include/library_loader.h" #include "../include/utils.h" @@ -10,118 +11,28 @@ #include <unistd.h> #include <sys/capability.h> -#include <wayland-client.h> -#include <wayland-egl.h> - // TODO: rename gsr_egl to something else since this includes both egl and glx and in the future maybe vulkan too -// TODO: Move this shit to a separate wayland file, and have a separate file for x11. - -static void output_handle_geometry(void *data, struct wl_output *wl_output, - int32_t x, int32_t y, int32_t phys_width, int32_t phys_height, - int32_t subpixel, const char *make, const char *model, - int32_t transform) { - (void)wl_output; - (void)phys_width; - (void)phys_height; - (void)subpixel; - (void)make; - (void)model; - gsr_wayland_output *gsr_output = data; - gsr_output->pos.x = x; - gsr_output->pos.y = y; - gsr_output->transform = transform; -} - -static void output_handle_mode(void *data, struct wl_output *wl_output, uint32_t flags, int32_t width, int32_t height, int32_t refresh) { - (void)wl_output; - (void)flags; - (void)refresh; - gsr_wayland_output *gsr_output = data; - gsr_output->size.x = width; - gsr_output->size.y = height; -} - -static void output_handle_done(void *data, struct wl_output *wl_output) { - (void)data; - (void)wl_output; -} - -static void output_handle_scale(void* data, struct wl_output *wl_output, int32_t factor) { - (void)data; - (void)wl_output; - (void)factor; -} - -static void output_handle_name(void *data, struct wl_output *wl_output, const char *name) { - (void)wl_output; - gsr_wayland_output *gsr_output = data; - if(gsr_output->name) { - free(gsr_output->name); - gsr_output->name = NULL; - } - gsr_output->name = strdup(name); -} - -static void output_handle_description(void *data, struct wl_output *wl_output, const char *description) { - (void)data; - (void)wl_output; - (void)description; -} - -static const struct wl_output_listener output_listener = { - .geometry = output_handle_geometry, - .mode = output_handle_mode, - .done = output_handle_done, - .scale = output_handle_scale, - .name = output_handle_name, - .description = output_handle_description, -}; - -static void registry_add_object(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { - (void)version; - gsr_egl *egl = data; - if (strcmp(interface, "wl_compositor") == 0) { - if(egl->wayland.compositor) { - wl_compositor_destroy(egl->wayland.compositor); - egl->wayland.compositor = NULL; - } - egl->wayland.compositor = wl_registry_bind(registry, name, &wl_compositor_interface, 1); - } else if(strcmp(interface, wl_output_interface.name) == 0) { - if(version < 4) { - fprintf(stderr, "gsr warning: wl output interface version is < 4, expected >= 4 to capture a monitor. Using KMS capture instead\n"); - return; - } - - if(egl->wayland.num_outputs == GSR_MAX_OUTPUTS) { - fprintf(stderr, "gsr warning: reached maximum outputs (%d), ignoring output %u\n", GSR_MAX_OUTPUTS, name); - return; - } - - gsr_wayland_output *gsr_output = &egl->wayland.outputs[egl->wayland.num_outputs]; - egl->wayland.num_outputs++; - *gsr_output = (gsr_wayland_output) { - .wl_name = name, - .output = wl_registry_bind(registry, name, &wl_output_interface, 4), - .pos = { .x = 0, .y = 0 }, - .size = { .x = 0, .y = 0 }, - .transform = 0, - .name = NULL, - }; - wl_output_add_listener(gsr_output->output, &output_listener, gsr_output); - } -} +#define GLX_DRAWABLE_TYPE 0x8010 +#define GLX_RENDER_TYPE 0x8011 +#define GLX_RGBA_BIT 0x00000001 +#define GLX_WINDOW_BIT 0x00000001 +#define GLX_PIXMAP_BIT 0x00000002 +#define GLX_BIND_TO_TEXTURE_RGBA_EXT 0x20D1 +#define GLX_BIND_TO_TEXTURE_TARGETS_EXT 0x20D3 +#define GLX_TEXTURE_2D_BIT_EXT 0x00000002 +#define GLX_DOUBLEBUFFER 5 +#define GLX_RED_SIZE 8 +#define GLX_GREEN_SIZE 9 +#define GLX_BLUE_SIZE 10 +#define GLX_ALPHA_SIZE 11 +#define GLX_DEPTH_SIZE 12 +#define GLX_RGBA_TYPE 0x8014 -static void registry_remove_object(void *data, struct wl_registry *registry, uint32_t name) { - (void)data; - (void)registry; - (void)name; -} - -static struct wl_registry_listener registry_listener = { - .global = registry_add_object, - .global_remove = registry_remove_object, -}; +#define GLX_CONTEXT_PRIORITY_LEVEL_EXT 0x3100 +#define GLX_CONTEXT_PRIORITY_HIGH_EXT 0x3101 +#define GLX_CONTEXT_PRIORITY_MEDIUM_EXT 0x3102 +#define GLX_CONTEXT_PRIORITY_LOW_EXT 0x3103 static void reset_cap_nice(void) { cap_t caps = cap_get_proc(); @@ -135,75 +46,8 @@ 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.outputs[index].monitor_identifier = monitor->monitor_identifier; - ++egl->x11.num_outputs; -} - -#define GLX_DRAWABLE_TYPE 0x8010 -#define GLX_RENDER_TYPE 0x8011 -#define GLX_RGBA_BIT 0x00000001 -#define GLX_WINDOW_BIT 0x00000001 -#define GLX_PIXMAP_BIT 0x00000002 -#define GLX_BIND_TO_TEXTURE_RGBA_EXT 0x20D1 -#define GLX_BIND_TO_TEXTURE_TARGETS_EXT 0x20D3 -#define GLX_TEXTURE_2D_BIT_EXT 0x00000002 -#define GLX_DOUBLEBUFFER 5 -#define GLX_RED_SIZE 8 -#define GLX_GREEN_SIZE 9 -#define GLX_BLUE_SIZE 10 -#define GLX_ALPHA_SIZE 11 -#define GLX_DEPTH_SIZE 12 -#define GLX_RGBA_TYPE 0x8014 - -#define GLX_CONTEXT_PRIORITY_LEVEL_EXT 0x3100 -#define GLX_CONTEXT_PRIORITY_HIGH_EXT 0x3101 -#define GLX_CONTEXT_PRIORITY_MEDIUM_EXT 0x3102 -#define GLX_CONTEXT_PRIORITY_LOW_EXT 0x3103 - -static GLXFBConfig glx_fb_config_choose(gsr_egl *self) { - const int glx_visual_attribs[] = { - GLX_RENDER_TYPE, GLX_RGBA_BIT, - GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, - // TODO: - //GLX_BIND_TO_TEXTURE_RGBA_EXT, 1, - //GLX_BIND_TO_TEXTURE_TARGETS_EXT, GLX_TEXTURE_2D_BIT_EXT, - GLX_DOUBLEBUFFER, True, - GLX_RED_SIZE, 8, - GLX_GREEN_SIZE, 8, - GLX_BLUE_SIZE, 8, - GLX_ALPHA_SIZE, 0, - GLX_DEPTH_SIZE, 0, - None, None - }; - - // TODO: Cleanup - int c = 0; - GLXFBConfig *fb_configs = self->glXChooseFBConfig(self->x11.dpy, DefaultScreen(self->x11.dpy), glx_visual_attribs, &c); - if(c == 0 || !fb_configs) - return NULL; - - return fb_configs[0]; -} - // TODO: Create egl context without surface (in other words, x11/wayland agnostic, doesn't require x11/wayland dependency) -static bool gsr_egl_create_window(gsr_egl *self, bool wayland) { +static bool gsr_egl_create_window(gsr_egl *self) { EGLConfig ecfg; int32_t num_config = 0; @@ -220,39 +64,10 @@ static bool gsr_egl_create_window(gsr_egl *self, bool wayland) { EGL_NONE, EGL_NONE }; - if(wayland) { - self->wayland.dpy = wl_display_connect(NULL); - if(!self->wayland.dpy) { - fprintf(stderr, "gsr error: gsr_egl_create_window failed: failed to connect to the Wayland server\n"); - goto fail; - } - - self->wayland.registry = wl_display_get_registry(self->wayland.dpy); // TODO: Error checking - wl_registry_add_listener(self->wayland.registry, ®istry_listener, self); // TODO: Error checking - - // Fetch globals - wl_display_roundtrip(self->wayland.dpy); - - // Fetch wl_output - wl_display_roundtrip(self->wayland.dpy); - - if(!self->wayland.compositor) { - fprintf(stderr, "gsr error: gsr_gl_create_window failed: failed to find compositor\n"); - goto fail; - } - } else { - self->x11.window = XCreateWindow(self->x11.dpy, DefaultRootWindow(self->x11.dpy), 0, 0, 16, 16, 0, CopyFromParent, InputOutput, CopyFromParent, 0, NULL); - - if(!self->x11.window) { - fprintf(stderr, "gsr error: gsr_gl_create_window failed: failed to create gl window\n"); - goto fail; - } - } - // TODO: Use EGL_OPENGL_ES_API as amd requires that for external texture, but that breaks software encoding self->eglBindAPI(EGL_OPENGL_API); - self->egl_display = self->eglGetDisplay(self->wayland.dpy ? (EGLNativeDisplayType)self->wayland.dpy : (EGLNativeDisplayType)self->x11.dpy); + self->egl_display = self->eglGetDisplay((EGLNativeDisplayType)gsr_window_get_display(self->window)); if(!self->egl_display) { fprintf(stderr, "gsr error: gsr_egl_create_window failed: eglGetDisplay failed\n"); goto fail; @@ -274,15 +89,7 @@ static bool gsr_egl_create_window(gsr_egl *self, bool wayland) { goto fail; } - if(wayland) { - // TODO: Error check? - self->wayland.surface = wl_compositor_create_surface(self->wayland.compositor); - self->wayland.window = wl_egl_window_create(self->wayland.surface, 16, 16); - self->egl_surface = self->eglCreateWindowSurface(self->egl_display, ecfg, (EGLNativeWindowType)self->wayland.window, NULL); - } else { - self->egl_surface = self->eglCreateWindowSurface(self->egl_display, ecfg, (EGLNativeWindowType)self->x11.window, NULL); - } - + self->egl_surface = self->eglCreateWindowSurface(self->egl_display, ecfg, (EGLNativeWindowType)gsr_window_get_window(self->window), NULL); if(!self->egl_surface) { fprintf(stderr, "gsr error: gsr_egl_create_window failed: failed to create window surface\n"); goto fail; @@ -293,11 +100,6 @@ 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; @@ -307,8 +109,36 @@ static bool gsr_egl_create_window(gsr_egl *self, bool wayland) { return false; } +static GLXFBConfig glx_fb_config_choose(gsr_egl *self, Display *display) { + const int glx_visual_attribs[] = { + GLX_RENDER_TYPE, GLX_RGBA_BIT, + GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, + // TODO: + //GLX_BIND_TO_TEXTURE_RGBA_EXT, 1, + //GLX_BIND_TO_TEXTURE_TARGETS_EXT, GLX_TEXTURE_2D_BIT_EXT, + GLX_DOUBLEBUFFER, True, + GLX_RED_SIZE, 8, + GLX_GREEN_SIZE, 8, + GLX_BLUE_SIZE, 8, + GLX_ALPHA_SIZE, 0, + GLX_DEPTH_SIZE, 0, + None, None + }; + + // TODO: Cleanup + int c = 0; + GLXFBConfig *fb_configs = self->glXChooseFBConfig(display, DefaultScreen(display), glx_visual_attribs, &c); + if(c == 0 || !fb_configs) + return NULL; + + return fb_configs[0]; +} + static bool gsr_egl_switch_to_glx_context(gsr_egl *self) { // TODO: Cleanup + assert(gsr_window_get_display_server(self->window) == GSR_DISPLAY_SERVER_X11); + Display *display = gsr_window_get_display(self->window); + const Window window = (Window)gsr_window_get_window(self->window); if(self->egl_context) { self->eglMakeCurrent(self->egl_display, NULL, NULL, NULL); @@ -326,21 +156,21 @@ static bool gsr_egl_switch_to_glx_context(gsr_egl *self) { self->egl_display = NULL; } - self->glx_fb_config = glx_fb_config_choose(self); + self->glx_fb_config = glx_fb_config_choose(self, display); if(!self->glx_fb_config) { fprintf(stderr, "gsr error: gsr_egl_create_window failed: failed to find a suitable fb config\n"); goto fail; } // TODO: - //self->glx_context = self->glXCreateContextAttribsARB(self->x11.dpy, self->glx_fb_config, NULL, True, context_attrib_list); - self->glx_context = self->glXCreateNewContext(self->x11.dpy, self->glx_fb_config, GLX_RGBA_TYPE, NULL, True); + //self->glx_context = self->glXCreateContextAttribsARB(display, self->glx_fb_config, NULL, True, context_attrib_list); + self->glx_context = self->glXCreateNewContext(display, self->glx_fb_config, GLX_RGBA_TYPE, NULL, True); if(!self->glx_context) { fprintf(stderr, "gsr error: gsr_egl_create_window failed: failed to create glx context\n"); goto fail; } - if(!self->glXMakeContextCurrent(self->x11.dpy, self->x11.window, self->x11.window, self->glx_context)) { + if(!self->glXMakeContextCurrent(display, window, window, self->glx_context)) { fprintf(stderr, "gsr error: gsr_egl_create_window failed: failed to make glx context current\n"); goto fail; } @@ -349,8 +179,8 @@ static bool gsr_egl_switch_to_glx_context(gsr_egl *self) { fail: if(self->glx_context) { - self->glXMakeContextCurrent(self->x11.dpy, None, None, NULL); - self->glXDestroyContext(self->x11.dpy, self->glx_context); + self->glXMakeContextCurrent(display, None, None, NULL); + self->glXDestroyContext(display, self->glx_context); self->glx_context = NULL; self->glx_fb_config = NULL; } @@ -532,10 +362,10 @@ static bool gsr_egl_load_gl(gsr_egl *self, void *library) { // type, severity, message ); // } -bool gsr_egl_load(gsr_egl *self, Display *dpy, bool wayland, bool is_monitor_capture) { +bool gsr_egl_load(gsr_egl *self, gsr_window *window, bool is_monitor_capture) { memset(self, 0, sizeof(gsr_egl)); - self->x11.dpy = dpy; self->context_type = GSR_GL_CONTEXT_TYPE_EGL; + self->window = window; dlerror(); /* clear */ self->egl_library = dlopen("libEGL.so.1", RTLD_LAZY); @@ -565,7 +395,7 @@ bool gsr_egl_load(gsr_egl *self, Display *dpy, bool wayland, bool is_monitor_cap if(!gsr_egl_proc_load_egl(self)) goto fail; - if(!gsr_egl_create_window(self, wayland)) + if(!gsr_egl_create_window(self)) goto fail; if(!gl_get_gpu_info(self, &self->gpu_info)) @@ -578,7 +408,7 @@ bool gsr_egl_load(gsr_egl *self, Display *dpy, bool wayland, bool is_monitor_cap } /* Nvfbc requires glx */ - if(!wayland && is_monitor_capture && self->gpu_info.vendor == GSR_GPU_VENDOR_NVIDIA) { + if(gsr_window_get_display_server(self->window) == GSR_DISPLAY_SERVER_X11 && is_monitor_capture && self->gpu_info.vendor == GSR_GPU_VENDOR_NVIDIA) { self->context_type = GSR_GL_CONTEXT_TYPE_GLX; self->dri_card_path = NULL; if(!gsr_egl_switch_to_glx_context(self)) @@ -616,63 +446,14 @@ void gsr_egl_unload(gsr_egl *self) { } if(self->glx_context) { - self->glXMakeContextCurrent(self->x11.dpy, None, None, NULL); - self->glXDestroyContext(self->x11.dpy, self->glx_context); + assert(gsr_window_get_display_server(self->window) == GSR_DISPLAY_SERVER_X11); + Display *display = gsr_window_get_display(self->window); + self->glXMakeContextCurrent(display, None, None, NULL); + self->glXDestroyContext(display, self->glx_context); self->glx_context = NULL; self->glx_fb_config = NULL; } - if(self->x11.window) { - XDestroyWindow(self->x11.dpy, self->x11.window); - 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; - } - - if(self->wayland.surface) { - wl_surface_destroy(self->wayland.surface); - self->wayland.surface = NULL; - } - - for(int i = 0; i < self->wayland.num_outputs; ++i) { - if(self->wayland.outputs[i].output) { - wl_output_destroy(self->wayland.outputs[i].output); - self->wayland.outputs[i].output = NULL; - } - - if(self->wayland.outputs[i].name) { - free(self->wayland.outputs[i].name); - self->wayland.outputs[i].name = NULL; - } - } - self->wayland.num_outputs = 0; - - if(self->wayland.compositor) { - wl_compositor_destroy(self->wayland.compositor); - self->wayland.compositor = NULL; - } - - if(self->wayland.registry) { - wl_registry_destroy(self->wayland.registry); - self->wayland.registry = NULL; - } - - if(self->wayland.dpy) { - wl_display_disconnect(self->wayland.dpy); - self->wayland.dpy = NULL; - } - if(self->egl_library) { dlclose(self->egl_library); self->egl_library = NULL; @@ -691,25 +472,6 @@ void gsr_egl_unload(gsr_egl *self) { memset(self, 0, sizeof(gsr_egl)); } -bool gsr_egl_process_event(gsr_egl *self) { - switch(gsr_egl_get_display_server(self)) { - case GSR_DISPLAY_SERVER_X11: { - if(XPending(self->x11.dpy)) { - XNextEvent(self->x11.dpy, &self->x11.xev); - return true; - } - return false; - } - case GSR_DISPLAY_SERVER_WAYLAND: { - // TODO: pselect on wl_display_get_fd before doing dispatch - const bool events_available = wl_display_dispatch_pending(self->wayland.dpy) > 0; - wl_display_flush(self->wayland.dpy); - return events_available; - } - } - return false; -} - void gsr_egl_swap_buffers(gsr_egl *self) { /* This uses less cpu than swap buffer on nvidia */ // TODO: Do these and remove swap @@ -717,21 +479,9 @@ void gsr_egl_swap_buffers(gsr_egl *self) { //self->glFinish(); if(self->egl_display) { self->eglSwapBuffers(self->egl_display, self->egl_surface); - } else if(self->x11.window) { - self->glXSwapBuffers(self->x11.dpy, self->x11.window); + } else if(gsr_window_get_display_server(self->window) == GSR_DISPLAY_SERVER_X11) { + Display *display = gsr_window_get_display(self->window); + const Window window = (Window)gsr_window_get_window(self->window); + self->glXSwapBuffers(display, window); } } - -gsr_display_server gsr_egl_get_display_server(const gsr_egl *self) { - if(self->wayland.dpy) - return GSR_DISPLAY_SERVER_WAYLAND; - else - return GSR_DISPLAY_SERVER_X11; -} - -XEvent* gsr_egl_get_event_data(gsr_egl *self) { - if(gsr_egl_get_display_server(self) == GSR_DISPLAY_SERVER_X11) - return &self->x11.xev; - else - return NULL; -} diff --git a/src/encoder/video/nvenc.c b/src/encoder/video/nvenc.c index 718560d..e83d0e8 100644 --- a/src/encoder/video/nvenc.c +++ b/src/encoder/video/nvenc.c @@ -1,6 +1,7 @@ #include "../../../include/encoder/video/nvenc.h" #include "../../../include/egl.h" #include "../../../include/cuda.h" +#include "../../../include/window/window.h" #include <libavcodec/avcodec.h> #include <libavutil/hwcontext_cuda.h> @@ -128,8 +129,10 @@ static void gsr_video_encoder_nvenc_stop(gsr_video_encoder_nvenc *self, AVCodecC static bool gsr_video_encoder_nvenc_start(gsr_video_encoder *encoder, AVCodecContext *video_codec_context, AVFrame *frame) { gsr_video_encoder_nvenc *self = encoder->priv; - const bool overclock = gsr_egl_get_display_server(self->params.egl) == GSR_DISPLAY_SERVER_X11 ? self->params.overclock : false; - if(!gsr_cuda_load(&self->cuda, self->params.egl->x11.dpy, overclock)) { + const bool is_x11 = gsr_window_get_display_server(self->params.egl->window) == GSR_DISPLAY_SERVER_X11; + const bool overclock = is_x11 ? self->params.overclock : false; + Display *display = is_x11 ? gsr_window_get_display(self->params.egl->window) : NULL; + if(!gsr_cuda_load(&self->cuda, display, overclock)) { fprintf(stderr, "gsr error: gsr_video_encoder_nvenc_start: failed to load cuda\n"); gsr_video_encoder_nvenc_stop(self, video_codec_context); return false; diff --git a/src/main.cpp b/src/main.cpp index e70646a..119da89 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -16,6 +16,8 @@ extern "C" { #include "../include/codec_query/nvenc.h" #include "../include/codec_query/vaapi.h" #include "../include/codec_query/vulkan.h" +#include "../include/window/window_x11.h" +#include "../include/window/window_wayland.h" #include "../include/egl.h" #include "../include/utils.h" #include "../include/damage.h" @@ -1902,6 +1904,13 @@ static void disable_prime_run() { unsetenv("__VK_LAYER_NV_optimus"); } +static gsr_window* gsr_window_create(Display *display, bool wayland) { + if(wayland) + return gsr_window_wayland_create(); + else + return gsr_window_x11_create(display); +} + static void list_system_info(bool wayland) { printf("display_server|%s\n", wayland ? "wayland" : "x11"); bool supports_app_audio = false; @@ -2028,18 +2037,17 @@ static void list_supported_video_codecs(gsr_egl *egl, bool wayland) { // puts("hevc_vulkan"); // TODO: hdr, 10 bit } -static bool monitor_capture_use_drm(gsr_egl *egl, bool wayland) { - return wayland || egl->gpu_info.vendor != GSR_GPU_VENDOR_NVIDIA; +static bool monitor_capture_use_drm(gsr_egl *egl) { + return gsr_window_get_display_server(egl->window) == GSR_DISPLAY_SERVER_WAYLAND || egl->gpu_info.vendor != GSR_GPU_VENDOR_NVIDIA; } typedef struct { - bool wayland; gsr_egl *egl; } capture_options_callback; static void output_monitor_info(const gsr_monitor *monitor, void *userdata) { const capture_options_callback *options = (capture_options_callback*)userdata; - if(options->wayland && monitor_capture_use_drm(options->egl, options->wayland)) { + if(gsr_window_get_display_server(options->egl->window) == GSR_DISPLAY_SERVER_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) @@ -2050,7 +2058,8 @@ static void output_monitor_info(const gsr_monitor *monitor, void *userdata) { } } -static void list_supported_capture_options(gsr_egl *egl, bool wayland, bool list_monitors) { +static void list_supported_capture_options(gsr_egl *egl, bool list_monitors) { + const bool wayland = gsr_window_get_display_server(egl->window) == GSR_DISPLAY_SERVER_WAYLAND; if(!wayland) { puts("window"); puts("focused"); @@ -2058,10 +2067,9 @@ static void list_supported_capture_options(gsr_egl *egl, bool wayland, bool list if(list_monitors) { capture_options_callback options; - options.wayland = wayland; options.egl = egl; - if(monitor_capture_use_drm(egl, wayland)) { - const bool is_x11 = gsr_egl_get_display_server(egl) == GSR_DISPLAY_SERVER_X11; + if(monitor_capture_use_drm(egl)) { + const bool is_x11 = gsr_window_get_display_server(egl->window) == 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 { @@ -2110,15 +2118,21 @@ static void info_command() { disable_prime_run(); } + gsr_window *window = gsr_window_create(dpy, wayland); + if(!window) { + fprintf(stderr, "Error: failed to create window\n"); + _exit(1); + } + gsr_egl egl; - if(!gsr_egl_load(&egl, dpy, wayland, false)) { + if(!gsr_egl_load(&egl, window, false)) { fprintf(stderr, "gsr error: failed to load opengl\n"); _exit(22); } bool list_monitors = true; egl.card_path[0] = '\0'; - if(monitor_capture_use_drm(&egl, wayland)) { + if(monitor_capture_use_drm(&egl)) { // TODO: Allow specifying another card, and in other places if(!gsr_get_valid_card_path(&egl, egl.card_path, true)) { fprintf(stderr, "Error: no /dev/dri/cardX device found. Make sure that you have at least one monitor connected\n"); @@ -2139,7 +2153,7 @@ static void info_command() { puts("section=video_codecs"); list_supported_video_codecs(&egl, wayland); puts("section=capture_options"); - list_supported_capture_options(&egl, wayland, list_monitors); + list_supported_capture_options(&egl, list_monitors); fflush(stdout); @@ -2210,15 +2224,21 @@ static void list_capture_options_command() { disable_prime_run(); } + gsr_window *window = gsr_window_create(dpy, wayland); + if(!window) { + fprintf(stderr, "Error: failed to create window\n"); + _exit(1); + } + gsr_egl egl; - if(!gsr_egl_load(&egl, dpy, wayland, false)) { + if(!gsr_egl_load(&egl, window, false)) { fprintf(stderr, "gsr error: failed to load opengl\n"); _exit(1); } bool list_monitors = true; egl.card_path[0] = '\0'; - if(monitor_capture_use_drm(&egl, wayland)) { + if(monitor_capture_use_drm(&egl)) { // TODO: Allow specifying another card, and in other places if(!gsr_get_valid_card_path(&egl, egl.card_path, true)) { fprintf(stderr, "Error: no /dev/dri/cardX device found. Make sure that you have at least one monitor connected\n"); @@ -2227,7 +2247,7 @@ static void list_capture_options_command() { } av_log_set_level(AV_LOG_FATAL); - list_supported_capture_options(&egl, wayland, list_monitors); + list_supported_capture_options(&egl, list_monitors); fflush(stdout); @@ -2283,8 +2303,8 @@ static gsr_capture* create_capture_impl(std::string &window_str, vec2i output_re _exit(2); #endif } else if(contains_non_hex_number(window_str.c_str())) { - if(monitor_capture_use_drm(egl, wayland)) { - const bool is_x11 = gsr_egl_get_display_server(egl) == GSR_DISPLAY_SERVER_X11; + if(monitor_capture_use_drm(egl)) { + const bool is_x11 = gsr_window_get_display_server(egl->window) == GSR_DISPLAY_SERVER_X11; const gsr_connection_type connection_type = is_x11 ? GSR_CONNECTION_X11 : GSR_CONNECTION_DRM; if(strcmp(window_str.c_str(), "screen") == 0) { @@ -2311,8 +2331,9 @@ static gsr_capture* create_capture_impl(std::string &window_str, vec2i output_re if(strcmp(window_str.c_str(), "screen") != 0 && strcmp(window_str.c_str(), "screen-direct") != 0 && strcmp(window_str.c_str(), "screen-direct-force") != 0) { gsr_monitor gmon; if(!get_monitor_by_name(egl, GSR_CONNECTION_X11, window_str.c_str(), &gmon)) { - const int screens_width = XWidthOfScreen(DefaultScreenOfDisplay(egl->x11.dpy)); - const int screens_height = XWidthOfScreen(DefaultScreenOfDisplay(egl->x11.dpy)); + Display *display = (Display*)gsr_window_get_display(egl->window); + const int screens_width = XWidthOfScreen(DefaultScreenOfDisplay(display)); + const int screens_height = XWidthOfScreen(DefaultScreenOfDisplay(display)); fprintf(stderr, "gsr error: display \"%s\" not found, expected one of:\n", window_str.c_str()); fprintf(stderr, " \"screen\" (%dx%d+%d+%d)\n", screens_width, screens_height, 0, 0); fprintf(stderr, " \"screen-direct\" (%dx%d+%d+%d)\n", screens_width, screens_height, 0, 0); @@ -3354,6 +3375,12 @@ int main(int argc, char **argv) { disable_prime_run(); } + gsr_window *window = gsr_window_create(dpy, wayland); + if(!window) { + fprintf(stderr, "Error: failed to create window\n"); + _exit(1); + } + if(is_portal_capture && is_using_prime_run()) { fprintf(stderr, "Warning: use of prime-run with -w portal option is currently not supported. Disabling prime-run\n"); disable_prime_run(); @@ -3366,7 +3393,7 @@ int main(int argc, char **argv) { const bool is_monitor_capture = strcmp(window_str.c_str(), "focused") != 0 && !is_portal_capture && contains_non_hex_number(window_str.c_str()); gsr_egl egl; - if(!gsr_egl_load(&egl, dpy, wayland, is_monitor_capture)) { + if(!gsr_egl_load(&egl, window, is_monitor_capture)) { fprintf(stderr, "gsr error: failed to load opengl\n"); _exit(1); } @@ -3394,7 +3421,7 @@ int main(int argc, char **argv) { } egl.card_path[0] = '\0'; - if(monitor_capture_use_drm(&egl, wayland)) { + if(monitor_capture_use_drm(&egl)) { // TODO: Allow specifying another card, and in other places if(!gsr_get_valid_card_path(&egl, egl.card_path, is_monitor_capture)) { fprintf(stderr, "Error: no /dev/dri/cardX device found. Make sure that you have at least one monitor connected or record a single window instead on X11 or record with the -w portal option\n"); @@ -4019,7 +4046,7 @@ int main(int argc, char **argv) { bool use_damage_tracking = false; gsr_damage damage; memset(&damage, 0, sizeof(damage)); - if(gsr_egl_get_display_server(&egl) == GSR_DISPLAY_SERVER_X11) { + if(gsr_window_get_display_server(window) == GSR_DISPLAY_SERVER_X11) { gsr_damage_init(&damage, &egl, record_cursor); use_damage_tracking = true; } @@ -4033,8 +4060,8 @@ int main(int argc, char **argv) { while(running) { const double frame_start = clock_get_monotonic_seconds(); - while(gsr_egl_process_event(&egl)) { - gsr_damage_on_event(&damage, gsr_egl_get_event_data(&egl)); + while(gsr_window_process_event(window)) { + gsr_damage_on_event(&damage, gsr_window_get_event_data(window)); gsr_capture_on_event(capture, &egl); } gsr_damage_tick(&damage); @@ -4244,6 +4271,9 @@ int main(int argc, char **argv) { //XCloseDisplay(dpy); } + //gsr_egl_unload(&egl); + //gsr_window_destroy(&window); + //av_frame_free(&video_frame); free(empty_audio); // We do an _exit here because cuda uses at_exit to do _something_ that causes the program to freeze, diff --git a/src/utils.c b/src/utils.c index be5e58b..d14dd23 100644 --- a/src/utils.c +++ b/src/utils.c @@ -1,4 +1,5 @@ #include "../include/utils.h" +#include "../include/window/window.h" #include <time.h> #include <string.h> @@ -47,16 +48,6 @@ bool generate_random_characters_standard_alphabet(char *buffer, int buffer_size) return generate_random_characters(buffer, buffer_size, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", 62); } -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) @@ -146,31 +137,22 @@ void for_each_active_monitor_output_x11_not_cached(Display *display, active_moni 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 = output->monitor_identifier - }; - callback(&monitor, userdata); - } +/* TODO: Support more connector types */ +int get_connector_type_by_name(const char *name) { + int len = strlen(name); + if(len >= 5 && strncmp(name, "HDMI-", 5) == 0) + return 1; + else if(len >= 3 && strncmp(name, "DP-", 3) == 0) + return 2; + else if(len >= 12 && strncmp(name, "DisplayPort-", 12) == 0) + return 3; + else if(len >= 4 && strncmp(name, "eDP-", 4) == 0) + return 4; + else + return -1; } -typedef struct { - int type; - int count; - int count_active; -} drm_connector_type_count; - -#define CONNECTOR_TYPE_COUNTS 32 - -static drm_connector_type_count* drm_connector_types_get_index(drm_connector_type_count *type_counts, int *num_type_counts, int connector_type) { +drm_connector_type_count* drm_connector_types_get_index(drm_connector_type_count *type_counts, int *num_type_counts, int connector_type) { for(int i = 0; i < *num_type_counts; ++i) { if(type_counts[i].type == connector_type) return &type_counts[i]; @@ -187,6 +169,10 @@ static drm_connector_type_count* drm_connector_types_get_index(drm_connector_typ return &type_counts[index]; } +uint32_t monitor_identifier_from_type_and_count(int monitor_type_index, int monitor_type_count) { + return ((uint32_t)monitor_type_index << 16) | ((uint32_t)monitor_type_count); +} + static bool connector_get_property_by_name(int drmfd, drmModeConnectorPtr props, const char *name, uint64_t *result) { for(int i = 0; i < props->count_props; ++i) { drmModePropertyPtr prop = drmModeGetProperty(drmfd, props->props[i]); @@ -202,57 +188,6 @@ static bool connector_get_property_by_name(int drmfd, drmModeConnectorPtr props, return false; } -/* TODO: Support more connector types */ -static int get_connector_type_by_name(const char *name) { - int len = strlen(name); - if(len >= 5 && strncmp(name, "HDMI-", 5) == 0) - return 1; - else if(len >= 3 && strncmp(name, "DP-", 3) == 0) - return 2; - else if(len >= 12 && strncmp(name, "DisplayPort-", 12) == 0) - return 3; - else if(len >= 4 && strncmp(name, "eDP-", 4) == 0) - return 4; - else - return -1; -} - -static uint32_t monitor_identifier_from_type_and_count(int monitor_type_index, int monitor_type_count) { - return ((uint32_t)monitor_type_index << 16) | ((uint32_t)monitor_type_count); -} - -static void for_each_active_monitor_output_wayland(const gsr_egl *egl, active_monitor_callback callback, void *userdata) { - drm_connector_type_count type_counts[CONNECTOR_TYPE_COUNTS]; - int num_type_counts = 0; - - for(int i = 0; i < egl->wayland.num_outputs; ++i) { - const gsr_wayland_output *output = &egl->wayland.outputs[i]; - if(!output->name) - continue; - - const int connector_type_index = get_connector_type_by_name(output->name); - drm_connector_type_count *connector_type = NULL; - if(connector_type_index != -1) - connector_type = drm_connector_types_get_index(type_counts, &num_type_counts, connector_type_index); - - if(connector_type) { - ++connector_type->count; - ++connector_type->count_active; - } - - const gsr_monitor monitor = { - .name = output->name, - .name_len = strlen(output->name), - .pos = { .x = output->pos.x, .y = output->pos.y }, - .size = { .x = output->size.x, .y = output->size.y }, - .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 - }; - callback(&monitor, userdata); - } -} - static void for_each_active_monitor_output_drm(const gsr_egl *egl, active_monitor_callback callback, void *userdata) { int fd = open(egl->card_path, O_RDONLY); if(fd == -1) @@ -318,10 +253,8 @@ 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, callback, userdata); - break; case GSR_CONNECTION_WAYLAND: - for_each_active_monitor_output_wayland(egl, callback, userdata); + gsr_window_for_each_active_monitor_output_cached(egl->window, callback, userdata); break; case GSR_CONNECTION_DRM: for_each_active_monitor_output_drm(egl, callback, userdata); @@ -380,13 +313,13 @@ 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(gsr_egl_get_display_server(egl) == GSR_DISPLAY_SERVER_WAYLAND) { + if(gsr_window_get_display_server(egl->window) == GSR_DISPLAY_SERVER_WAYLAND) { { get_monitor_by_connector_id_userdata userdata; userdata.monitor = monitor; userdata.rotation = GSR_MONITOR_ROT_0; userdata.match_found = false; - for_each_active_monitor_output_wayland(egl, get_monitor_by_name_and_size_callback, &userdata); + gsr_window_for_each_active_monitor_output_cached(egl->window, get_monitor_by_name_and_size_callback, &userdata); if(userdata.match_found) return userdata.rotation; } @@ -395,7 +328,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_wayland(egl, get_monitor_by_connector_id_callback, &userdata); + gsr_window_for_each_active_monitor_output_cached(egl->window, get_monitor_by_connector_id_callback, &userdata); return userdata.rotation; } } else { @@ -403,7 +336,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, get_monitor_by_connector_id_callback, &userdata); + gsr_window_for_each_active_monitor_output_cached(egl->window, get_monitor_by_connector_id_callback, &userdata); return userdata.rotation; } diff --git a/src/window/window.c b/src/window/window.c new file mode 100644 index 0000000..1c6a24e --- /dev/null +++ b/src/window/window.c @@ -0,0 +1,30 @@ +#include "../../include/window/window.h" +#include <stddef.h> + +void gsr_window_destroy(gsr_window *self); + +bool gsr_window_process_event(gsr_window *self) { + return self->process_event(self); +} + +XEvent* gsr_window_get_event_data(gsr_window *self) { + if(self->get_event_data) + return self->get_event_data(self); + return NULL; +} + +gsr_display_server gsr_window_get_display_server(const gsr_window *self) { + return self->get_display_server(); +} + +void* gsr_window_get_display(gsr_window *self) { + return self->get_display(self); +} + +void* gsr_window_get_window(gsr_window *self) { + return self->get_window(self); +} + +void gsr_window_for_each_active_monitor_output_cached(const gsr_window *self, active_monitor_callback callback, void *userdata) { + self->for_each_active_monitor_output_cached(self, callback, userdata); +} diff --git a/src/window/window_wayland.c b/src/window/window_wayland.c new file mode 100644 index 0000000..b5557ee --- /dev/null +++ b/src/window/window_wayland.c @@ -0,0 +1,319 @@ +#include "../../include/window/window_wayland.h" + +#include "../../include/vec2.h" +#include "../../include/defs.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdint.h> +#include <wayland-client.h> +#include <wayland-egl.h> + +#define GSR_MAX_OUTPUTS 32 + +typedef struct { + uint32_t wl_name; + void *output; + vec2i pos; + vec2i size; + int32_t transform; + char *name; +} gsr_wayland_output; + +typedef struct { + void *display; + void *window; + void *registry; + void *surface; + void *compositor; + gsr_wayland_output outputs[GSR_MAX_OUTPUTS]; + int num_outputs; +} gsr_window_wayland; + +static void output_handle_geometry(void *data, struct wl_output *wl_output, + int32_t x, int32_t y, int32_t phys_width, int32_t phys_height, + int32_t subpixel, const char *make, const char *model, + int32_t transform) { + (void)wl_output; + (void)phys_width; + (void)phys_height; + (void)subpixel; + (void)make; + (void)model; + gsr_wayland_output *gsr_output = data; + gsr_output->pos.x = x; + gsr_output->pos.y = y; + gsr_output->transform = transform; +} + +static void output_handle_mode(void *data, struct wl_output *wl_output, uint32_t flags, int32_t width, int32_t height, int32_t refresh) { + (void)wl_output; + (void)flags; + (void)refresh; + gsr_wayland_output *gsr_output = data; + gsr_output->size.x = width; + gsr_output->size.y = height; +} + +static void output_handle_done(void *data, struct wl_output *wl_output) { + (void)data; + (void)wl_output; +} + +static void output_handle_scale(void* data, struct wl_output *wl_output, int32_t factor) { + (void)data; + (void)wl_output; + (void)factor; +} + +static void output_handle_name(void *data, struct wl_output *wl_output, const char *name) { + (void)wl_output; + gsr_wayland_output *gsr_output = data; + if(gsr_output->name) { + free(gsr_output->name); + gsr_output->name = NULL; + } + gsr_output->name = strdup(name); +} + +static void output_handle_description(void *data, struct wl_output *wl_output, const char *description) { + (void)data; + (void)wl_output; + (void)description; +} + +static const struct wl_output_listener output_listener = { + .geometry = output_handle_geometry, + .mode = output_handle_mode, + .done = output_handle_done, + .scale = output_handle_scale, + .name = output_handle_name, + .description = output_handle_description, +}; + +static void registry_add_object(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { + (void)version; + gsr_window_wayland *window_wayland = data; + if (strcmp(interface, "wl_compositor") == 0) { + if(window_wayland->compositor) { + wl_compositor_destroy(window_wayland->compositor); + window_wayland->compositor = NULL; + } + window_wayland->compositor = wl_registry_bind(registry, name, &wl_compositor_interface, 1); + } else if(strcmp(interface, wl_output_interface.name) == 0) { + if(version < 4) { + fprintf(stderr, "gsr warning: wl output interface version is < 4, expected >= 4 to capture a monitor. Using KMS capture instead\n"); + return; + } + + if(window_wayland->num_outputs == GSR_MAX_OUTPUTS) { + fprintf(stderr, "gsr warning: reached maximum outputs (%d), ignoring output %u\n", GSR_MAX_OUTPUTS, name); + return; + } + + gsr_wayland_output *gsr_output = &window_wayland->outputs[window_wayland->num_outputs]; + window_wayland->num_outputs++; + *gsr_output = (gsr_wayland_output) { + .wl_name = name, + .output = wl_registry_bind(registry, name, &wl_output_interface, 4), + .pos = { .x = 0, .y = 0 }, + .size = { .x = 0, .y = 0 }, + .transform = 0, + .name = NULL, + }; + wl_output_add_listener(gsr_output->output, &output_listener, gsr_output); + } +} + +static void registry_remove_object(void *data, struct wl_registry *registry, uint32_t name) { + (void)data; + (void)registry; + (void)name; +} + +static struct wl_registry_listener registry_listener = { + .global = registry_add_object, + .global_remove = registry_remove_object, +}; + +static void gsr_window_wayland_deinit(gsr_window_wayland *self) { + if(self->window) { + wl_egl_window_destroy(self->window); + self->window = NULL; + } + + if(self->surface) { + wl_surface_destroy(self->surface); + self->surface = NULL; + } + + for(int i = 0; i < self->num_outputs; ++i) { + if(self->outputs[i].output) { + wl_output_destroy(self->outputs[i].output); + self->outputs[i].output = NULL; + } + + if(self->outputs[i].name) { + free(self->outputs[i].name); + self->outputs[i].name = NULL; + } + } + self->num_outputs = 0; + + if(self->compositor) { + wl_compositor_destroy(self->compositor); + self->compositor = NULL; + } + + if(self->registry) { + wl_registry_destroy(self->registry); + self->registry = NULL; + } + + if(self->display) { + wl_display_disconnect(self->display); + self->display = NULL; + } +} + +static bool gsr_window_wayland_init(gsr_window_wayland *self) { + self->display = wl_display_connect(NULL); + if(!self->display) { + fprintf(stderr, "gsr error: gsr_window_wayland_init failed: failed to connect to the Wayland server\n"); + goto fail; + } + + self->registry = wl_display_get_registry(self->display); // TODO: Error checking + wl_registry_add_listener(self->registry, ®istry_listener, self); // TODO: Error checking + + // Fetch globals + wl_display_roundtrip(self->display); + + // Fetch wl_output + wl_display_roundtrip(self->display); + + if(!self->compositor) { + fprintf(stderr, "gsr error: gsr_window_wayland_init failed: failed to find compositor\n"); + goto fail; + } + + self->surface = wl_compositor_create_surface(self->compositor); + if(!self->surface) { + fprintf(stderr, "gsr error: gsr_window_wayland_init failed: failed to create surface\n"); + goto fail; + } + + self->window = wl_egl_window_create(self->surface, 16, 16); + if(!self->window) { + fprintf(stderr, "gsr error: gsr_window_wayland_init failed: failed to create window\n"); + goto fail; + } + + fail: + gsr_window_wayland_deinit(self); + return false; +} + +static void gsr_window_wayland_destroy(gsr_window *window) { + gsr_window_wayland *self = window->priv; + gsr_window_wayland_deinit(self); + free(self); + free(window); +} + +static bool gsr_window_wayland_process_event(gsr_window *window) { + gsr_window_wayland *self = window->priv; + // TODO: pselect on wl_display_get_fd before doing dispatch + const bool events_available = wl_display_dispatch_pending(self->display) > 0; + wl_display_flush(self->display); + return events_available; +} + +static gsr_display_server gsr_wayland_get_display_server(void) { + return GSR_DISPLAY_SERVER_WAYLAND; +} + +static void* gsr_window_wayland_get_display(gsr_window *window) { + gsr_window_wayland *self = window->priv; + return self->display; +} + +static void* gsr_window_wayland_get_window(gsr_window *window) { + gsr_window_wayland *self = window->priv; + return self->window; +} + +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 void gsr_window_wayland_for_each_active_monitor_output_cached(const gsr_window *window, active_monitor_callback callback, void *userdata) { + const gsr_window_wayland *self = window->priv; + drm_connector_type_count type_counts[CONNECTOR_TYPE_COUNTS]; + int num_type_counts = 0; + + for(int i = 0; i < self->num_outputs; ++i) { + const gsr_wayland_output *output = &self->outputs[i]; + if(!output->name) + continue; + + const int connector_type_index = get_connector_type_by_name(output->name); + drm_connector_type_count *connector_type = NULL; + if(connector_type_index != -1) + connector_type = drm_connector_types_get_index(type_counts, &num_type_counts, connector_type_index); + + if(connector_type) { + ++connector_type->count; + ++connector_type->count_active; + } + + const gsr_monitor monitor = { + .name = output->name, + .name_len = strlen(output->name), + .pos = { .x = output->pos.x, .y = output->pos.y }, + .size = { .x = output->size.x, .y = output->size.y }, + .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 + }; + callback(&monitor, userdata); + } +} + +gsr_window* gsr_window_wayland_create(void) { + gsr_window *window = calloc(1, sizeof(gsr_window)); + if(!window) + return window; + + gsr_window_wayland *window_wayland = calloc(1, sizeof(gsr_window_wayland)); + if(!window_wayland) { + free(window); + return NULL; + } + + if(!gsr_window_wayland_init(window_wayland)) { + free(window_wayland); + free(window); + return NULL; + } + + *window = (gsr_window) { + .destroy = gsr_window_wayland_destroy, + .process_event = gsr_window_wayland_process_event, + .get_event_data = NULL, + .get_display_server = gsr_wayland_get_display_server, + .get_display = gsr_window_wayland_get_display, + .get_window = gsr_window_wayland_get_window, + .for_each_active_monitor_output_cached = gsr_window_wayland_for_each_active_monitor_output_cached, + .priv = window_wayland + }; + + return window; +} diff --git a/src/window/window_x11.c b/src/window/window_x11.c new file mode 100644 index 0000000..55b0a75 --- /dev/null +++ b/src/window/window_x11.c @@ -0,0 +1,162 @@ +#include "../../include/window/window_x11.h" + +#include "../../include/vec2.h" +#include "../../include/defs.h" +#include "../../include/utils.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdint.h> +#include <X11/Xlib.h> + +#define GSR_MAX_OUTPUTS 32 + +typedef struct { + char *name; + vec2i pos; + vec2i size; + uint32_t connector_id; + gsr_monitor_rotation rotation; + uint32_t monitor_identifier; /* crtc id */ +} gsr_x11_output; + +typedef struct { + Display *display; + Window window; + gsr_x11_output outputs[GSR_MAX_OUTPUTS]; + int num_outputs; + XEvent xev; +} gsr_window_x11; + +static void store_x11_monitor(const gsr_monitor *monitor, void *userdata) { + gsr_window_x11 *window_x11 = userdata; + if(window_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 = window_x11->num_outputs; + window_x11->outputs[index].name = monitor_name; + window_x11->outputs[index].pos = monitor->pos; + window_x11->outputs[index].size = monitor->size; + window_x11->outputs[index].connector_id = monitor->connector_id; + window_x11->outputs[index].rotation = monitor->rotation; + window_x11->outputs[index].monitor_identifier = monitor->monitor_identifier; + ++window_x11->num_outputs; +} + +static void gsr_window_x11_deinit(gsr_window_x11 *self) { + if(self->window) { + XDestroyWindow(self->display, self->window); + self->window = None; + } + + for(int i = 0; i < self->num_outputs; ++i) { + if(self->outputs[i].name) { + free(self->outputs[i].name); + self->outputs[i].name = NULL; + } + } + self->num_outputs = 0; +} + +static bool gsr_window_x11_init(gsr_window_x11 *self) { + self->window = XCreateWindow(self->display, DefaultRootWindow(self->display), 0, 0, 16, 16, 0, CopyFromParent, InputOutput, CopyFromParent, 0, NULL); + if(!self->window) { + fprintf(stderr, "gsr error: gsr_window_x11_init failed: failed to create gl window\n"); + return false; + } + + self->num_outputs = 0; + for_each_active_monitor_output_x11_not_cached(self->display, store_x11_monitor, self); + return true; +} + +static void gsr_window_x11_destroy(gsr_window *window) { + gsr_window_x11 *self = window->priv; + gsr_window_x11_deinit(self); + free(self); + free(window); +} + +static bool gsr_window_x11_process_event(gsr_window *window) { + gsr_window_x11 *self = window->priv; + if(XPending(self->display)) { + XNextEvent(self->display, &self->xev); + return true; + } + return false; +} + +static XEvent* gsr_window_x11_get_event_data(gsr_window *window) { + gsr_window_x11 *self = window->priv; + return &self->xev; +} + +static gsr_display_server gsr_window_x11_get_display_server(void) { + return GSR_DISPLAY_SERVER_X11; +} + +static void* gsr_window_x11_get_display(gsr_window *window) { + gsr_window_x11 *self = window->priv; + return self->display; +} + +static void* gsr_window_x11_get_window(gsr_window *window) { + gsr_window_x11 *self = window->priv; + return (void*)self->window; +} + +static void gsr_window_x11_for_each_active_monitor_output_cached(const gsr_window *window, active_monitor_callback callback, void *userdata) { + const gsr_window_x11 *self = window->priv; + for(int i = 0; i < self->num_outputs; ++i) { + const gsr_x11_output *output = &self->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 = output->monitor_identifier + }; + callback(&monitor, userdata); + } +} + +gsr_window* gsr_window_x11_create(Display *display) { + gsr_window *window = calloc(1, sizeof(gsr_window)); + if(!window) + return window; + + gsr_window_x11 *window_x11 = calloc(1, sizeof(gsr_window_x11)); + if(!window_x11) { + free(window); + return NULL; + } + + window_x11->display = display; + if(!gsr_window_x11_init(window_x11)) { + free(window_x11); + free(window); + return NULL; + } + + *window = (gsr_window) { + .destroy = gsr_window_x11_destroy, + .process_event = gsr_window_x11_process_event, + .get_event_data = gsr_window_x11_get_event_data, + .get_display_server = gsr_window_x11_get_display_server, + .get_display = gsr_window_x11_get_display, + .get_window = gsr_window_x11_get_window, + .for_each_active_monitor_output_cached = gsr_window_x11_for_each_active_monitor_output_cached, + .priv = window_x11 + }; + + return window; +} |