aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/capture/kms.c14
-rw-r--r--src/capture/nvfbc.c14
-rw-r--r--src/capture/xcomposite.c29
-rw-r--r--src/damage.c54
-rw-r--r--src/egl.c388
-rw-r--r--src/encoder/video/nvenc.c7
-rw-r--r--src/main.cpp76
-rw-r--r--src/utils.c115
-rw-r--r--src/window/window.c30
-rw-r--r--src/window/window_wayland.c319
-rw-r--r--src/window/window_x11.c162
11 files changed, 726 insertions, 482 deletions
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) {
diff --git a/src/egl.c b/src/egl.c
index 4e7c95b..20537a2 100644
--- a/src/egl.c
+++ b/src/egl.c
@@ -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, &registry_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, &registry_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;
+}