Refactor windowing from egl to window_x11/window_wayland, yolo

This commit is contained in:
dec05eba
2024-12-08 02:17:41 +01:00
parent 655fd3756b
commit c259a19b9d
18 changed files with 801 additions and 534 deletions

View File

@@ -17,6 +17,7 @@ typedef enum {
typedef struct {
gsr_egl *egl;
Display *display;
bool track_cursor;
gsr_damage_track_type track_type;

View File

@@ -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 */

View File

@@ -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);

37
include/window/window.h Normal file
View File

@@ -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 */

View File

@@ -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 */

View File

@@ -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 */

View File

@@ -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',

View File

@@ -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,

View File

@@ -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) {

View File

@@ -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,

View File

@@ -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) {

388
src/egl.c
View File

@@ -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.
#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 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);
}
}
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;
}

View File

@@ -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;

View File

@@ -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,

View File

@@ -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;
}

30
src/window/window.c Normal file
View File

@@ -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);
}

319
src/window/window_wayland.c Normal file
View File

@@ -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;
}

162
src/window/window_x11.c Normal file
View File

@@ -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;
}