mirror of
https://repo.dec05eba.com/gpu-screen-recorder
synced 2026-03-31 09:07:13 +09:00
Refactor windowing from egl to window_x11/window_wayland, yolo
This commit is contained in:
@@ -17,6 +17,7 @@ typedef enum {
|
||||
|
||||
typedef struct {
|
||||
gsr_egl *egl;
|
||||
Display *display;
|
||||
bool track_cursor;
|
||||
gsr_damage_track_type track_type;
|
||||
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
37
include/window/window.h
Normal 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 */
|
||||
8
include/window/window_wayland.h
Normal file
8
include/window/window_wayland.h
Normal 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 */
|
||||
10
include/window/window_x11.h
Normal file
10
include/window/window_x11.h
Normal 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 */
|
||||
@@ -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',
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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,
|
||||
|
||||
54
src/damage.c
54
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) {
|
||||
|
||||
368
src/egl.c
368
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,152 +11,8 @@
|
||||
#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);
|
||||
}
|
||||
}
|
||||
|
||||
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 reset_cap_nice(void) {
|
||||
cap_t caps = cap_get_proc();
|
||||
if(!caps)
|
||||
return;
|
||||
|
||||
const cap_value_t cap_to_remove = CAP_SYS_NICE;
|
||||
cap_set_flag(caps, CAP_EFFECTIVE, 1, &cap_to_remove, CAP_CLEAR);
|
||||
cap_set_flag(caps, CAP_PERMITTED, 1, &cap_to_remove, CAP_CLEAR);
|
||||
cap_set_proc(caps);
|
||||
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
|
||||
@@ -177,33 +34,20 @@ static void store_x11_monitor(const gsr_monitor *monitor, void *userdata) {
|
||||
#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
|
||||
};
|
||||
static void reset_cap_nice(void) {
|
||||
cap_t caps = cap_get_proc();
|
||||
if(!caps)
|
||||
return;
|
||||
|
||||
// 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];
|
||||
const cap_value_t cap_to_remove = CAP_SYS_NICE;
|
||||
cap_set_flag(caps, CAP_EFFECTIVE, 1, &cap_to_remove, CAP_CLEAR);
|
||||
cap_set_flag(caps, CAP_PERMITTED, 1, &cap_to_remove, CAP_CLEAR);
|
||||
cap_set_proc(caps);
|
||||
cap_free(caps);
|
||||
}
|
||||
|
||||
// TODO: Create egl context without surface (in other words, x11/wayland agnostic, doesn't require x11/wayland dependency)
|
||||
static bool gsr_egl_create_window(gsr_egl *self, bool wayland) {
|
||||
static bool gsr_egl_create_window(gsr_egl *self) {
|
||||
EGLConfig ecfg;
|
||||
int32_t num_config = 0;
|
||||
|
||||
@@ -220,39 +64,10 @@ static bool gsr_egl_create_window(gsr_egl *self, bool wayland) {
|
||||
EGL_NONE, EGL_NONE
|
||||
};
|
||||
|
||||
if(wayland) {
|
||||
self->wayland.dpy = wl_display_connect(NULL);
|
||||
if(!self->wayland.dpy) {
|
||||
fprintf(stderr, "gsr error: gsr_egl_create_window failed: failed to connect to the Wayland server\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
self->wayland.registry = wl_display_get_registry(self->wayland.dpy); // TODO: Error checking
|
||||
wl_registry_add_listener(self->wayland.registry, ®istry_listener, self); // TODO: Error checking
|
||||
|
||||
// Fetch globals
|
||||
wl_display_roundtrip(self->wayland.dpy);
|
||||
|
||||
// Fetch wl_output
|
||||
wl_display_roundtrip(self->wayland.dpy);
|
||||
|
||||
if(!self->wayland.compositor) {
|
||||
fprintf(stderr, "gsr error: gsr_gl_create_window failed: failed to find compositor\n");
|
||||
goto fail;
|
||||
}
|
||||
} else {
|
||||
self->x11.window = XCreateWindow(self->x11.dpy, DefaultRootWindow(self->x11.dpy), 0, 0, 16, 16, 0, CopyFromParent, InputOutput, CopyFromParent, 0, NULL);
|
||||
|
||||
if(!self->x11.window) {
|
||||
fprintf(stderr, "gsr error: gsr_gl_create_window failed: failed to create gl window\n");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Use EGL_OPENGL_ES_API as amd requires that for external texture, but that breaks software encoding
|
||||
self->eglBindAPI(EGL_OPENGL_API);
|
||||
|
||||
self->egl_display = self->eglGetDisplay(self->wayland.dpy ? (EGLNativeDisplayType)self->wayland.dpy : (EGLNativeDisplayType)self->x11.dpy);
|
||||
self->egl_display = self->eglGetDisplay((EGLNativeDisplayType)gsr_window_get_display(self->window));
|
||||
if(!self->egl_display) {
|
||||
fprintf(stderr, "gsr error: gsr_egl_create_window failed: eglGetDisplay failed\n");
|
||||
goto fail;
|
||||
@@ -274,15 +89,7 @@ static bool gsr_egl_create_window(gsr_egl *self, bool wayland) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if(wayland) {
|
||||
// TODO: Error check?
|
||||
self->wayland.surface = wl_compositor_create_surface(self->wayland.compositor);
|
||||
self->wayland.window = wl_egl_window_create(self->wayland.surface, 16, 16);
|
||||
self->egl_surface = self->eglCreateWindowSurface(self->egl_display, ecfg, (EGLNativeWindowType)self->wayland.window, NULL);
|
||||
} else {
|
||||
self->egl_surface = self->eglCreateWindowSurface(self->egl_display, ecfg, (EGLNativeWindowType)self->x11.window, NULL);
|
||||
}
|
||||
|
||||
self->egl_surface = self->eglCreateWindowSurface(self->egl_display, ecfg, (EGLNativeWindowType)gsr_window_get_window(self->window), NULL);
|
||||
if(!self->egl_surface) {
|
||||
fprintf(stderr, "gsr error: gsr_egl_create_window failed: failed to create window surface\n");
|
||||
goto fail;
|
||||
@@ -293,11 +100,6 @@ static bool gsr_egl_create_window(gsr_egl *self, bool wayland) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if(!wayland) {
|
||||
self->x11.num_outputs = 0;
|
||||
for_each_active_monitor_output_x11_not_cached(self->x11.dpy, store_x11_monitor, self);
|
||||
}
|
||||
|
||||
reset_cap_nice();
|
||||
return true;
|
||||
|
||||
@@ -307,8 +109,36 @@ static bool gsr_egl_create_window(gsr_egl *self, bool wayland) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static GLXFBConfig glx_fb_config_choose(gsr_egl *self, Display *display) {
|
||||
const int glx_visual_attribs[] = {
|
||||
GLX_RENDER_TYPE, GLX_RGBA_BIT,
|
||||
GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
|
||||
// TODO:
|
||||
//GLX_BIND_TO_TEXTURE_RGBA_EXT, 1,
|
||||
//GLX_BIND_TO_TEXTURE_TARGETS_EXT, GLX_TEXTURE_2D_BIT_EXT,
|
||||
GLX_DOUBLEBUFFER, True,
|
||||
GLX_RED_SIZE, 8,
|
||||
GLX_GREEN_SIZE, 8,
|
||||
GLX_BLUE_SIZE, 8,
|
||||
GLX_ALPHA_SIZE, 0,
|
||||
GLX_DEPTH_SIZE, 0,
|
||||
None, None
|
||||
};
|
||||
|
||||
// TODO: Cleanup
|
||||
int c = 0;
|
||||
GLXFBConfig *fb_configs = self->glXChooseFBConfig(display, DefaultScreen(display), glx_visual_attribs, &c);
|
||||
if(c == 0 || !fb_configs)
|
||||
return NULL;
|
||||
|
||||
return fb_configs[0];
|
||||
}
|
||||
|
||||
static bool gsr_egl_switch_to_glx_context(gsr_egl *self) {
|
||||
// TODO: Cleanup
|
||||
assert(gsr_window_get_display_server(self->window) == GSR_DISPLAY_SERVER_X11);
|
||||
Display *display = gsr_window_get_display(self->window);
|
||||
const Window window = (Window)gsr_window_get_window(self->window);
|
||||
|
||||
if(self->egl_context) {
|
||||
self->eglMakeCurrent(self->egl_display, NULL, NULL, NULL);
|
||||
@@ -326,21 +156,21 @@ static bool gsr_egl_switch_to_glx_context(gsr_egl *self) {
|
||||
self->egl_display = NULL;
|
||||
}
|
||||
|
||||
self->glx_fb_config = glx_fb_config_choose(self);
|
||||
self->glx_fb_config = glx_fb_config_choose(self, display);
|
||||
if(!self->glx_fb_config) {
|
||||
fprintf(stderr, "gsr error: gsr_egl_create_window failed: failed to find a suitable fb config\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
// TODO:
|
||||
//self->glx_context = self->glXCreateContextAttribsARB(self->x11.dpy, self->glx_fb_config, NULL, True, context_attrib_list);
|
||||
self->glx_context = self->glXCreateNewContext(self->x11.dpy, self->glx_fb_config, GLX_RGBA_TYPE, NULL, True);
|
||||
//self->glx_context = self->glXCreateContextAttribsARB(display, self->glx_fb_config, NULL, True, context_attrib_list);
|
||||
self->glx_context = self->glXCreateNewContext(display, self->glx_fb_config, GLX_RGBA_TYPE, NULL, True);
|
||||
if(!self->glx_context) {
|
||||
fprintf(stderr, "gsr error: gsr_egl_create_window failed: failed to create glx context\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if(!self->glXMakeContextCurrent(self->x11.dpy, self->x11.window, self->x11.window, self->glx_context)) {
|
||||
if(!self->glXMakeContextCurrent(display, window, window, self->glx_context)) {
|
||||
fprintf(stderr, "gsr error: gsr_egl_create_window failed: failed to make glx context current\n");
|
||||
goto fail;
|
||||
}
|
||||
@@ -349,8 +179,8 @@ static bool gsr_egl_switch_to_glx_context(gsr_egl *self) {
|
||||
|
||||
fail:
|
||||
if(self->glx_context) {
|
||||
self->glXMakeContextCurrent(self->x11.dpy, None, None, NULL);
|
||||
self->glXDestroyContext(self->x11.dpy, self->glx_context);
|
||||
self->glXMakeContextCurrent(display, None, None, NULL);
|
||||
self->glXDestroyContext(display, self->glx_context);
|
||||
self->glx_context = NULL;
|
||||
self->glx_fb_config = NULL;
|
||||
}
|
||||
@@ -532,10 +362,10 @@ static bool gsr_egl_load_gl(gsr_egl *self, void *library) {
|
||||
// type, severity, message );
|
||||
// }
|
||||
|
||||
bool gsr_egl_load(gsr_egl *self, Display *dpy, bool wayland, bool is_monitor_capture) {
|
||||
bool gsr_egl_load(gsr_egl *self, gsr_window *window, bool is_monitor_capture) {
|
||||
memset(self, 0, sizeof(gsr_egl));
|
||||
self->x11.dpy = dpy;
|
||||
self->context_type = GSR_GL_CONTEXT_TYPE_EGL;
|
||||
self->window = window;
|
||||
|
||||
dlerror(); /* clear */
|
||||
self->egl_library = dlopen("libEGL.so.1", RTLD_LAZY);
|
||||
@@ -565,7 +395,7 @@ bool gsr_egl_load(gsr_egl *self, Display *dpy, bool wayland, bool is_monitor_cap
|
||||
if(!gsr_egl_proc_load_egl(self))
|
||||
goto fail;
|
||||
|
||||
if(!gsr_egl_create_window(self, wayland))
|
||||
if(!gsr_egl_create_window(self))
|
||||
goto fail;
|
||||
|
||||
if(!gl_get_gpu_info(self, &self->gpu_info))
|
||||
@@ -578,7 +408,7 @@ bool gsr_egl_load(gsr_egl *self, Display *dpy, bool wayland, bool is_monitor_cap
|
||||
}
|
||||
|
||||
/* Nvfbc requires glx */
|
||||
if(!wayland && is_monitor_capture && self->gpu_info.vendor == GSR_GPU_VENDOR_NVIDIA) {
|
||||
if(gsr_window_get_display_server(self->window) == GSR_DISPLAY_SERVER_X11 && is_monitor_capture && self->gpu_info.vendor == GSR_GPU_VENDOR_NVIDIA) {
|
||||
self->context_type = GSR_GL_CONTEXT_TYPE_GLX;
|
||||
self->dri_card_path = NULL;
|
||||
if(!gsr_egl_switch_to_glx_context(self))
|
||||
@@ -616,63 +446,14 @@ void gsr_egl_unload(gsr_egl *self) {
|
||||
}
|
||||
|
||||
if(self->glx_context) {
|
||||
self->glXMakeContextCurrent(self->x11.dpy, None, None, NULL);
|
||||
self->glXDestroyContext(self->x11.dpy, self->glx_context);
|
||||
assert(gsr_window_get_display_server(self->window) == GSR_DISPLAY_SERVER_X11);
|
||||
Display *display = gsr_window_get_display(self->window);
|
||||
self->glXMakeContextCurrent(display, None, None, NULL);
|
||||
self->glXDestroyContext(display, self->glx_context);
|
||||
self->glx_context = NULL;
|
||||
self->glx_fb_config = NULL;
|
||||
}
|
||||
|
||||
if(self->x11.window) {
|
||||
XDestroyWindow(self->x11.dpy, self->x11.window);
|
||||
self->x11.window = None;
|
||||
}
|
||||
|
||||
for(int i = 0; i < self->x11.num_outputs; ++i) {
|
||||
if(self->x11.outputs[i].name) {
|
||||
free(self->x11.outputs[i].name);
|
||||
self->x11.outputs[i].name = NULL;
|
||||
}
|
||||
}
|
||||
self->x11.num_outputs = 0;
|
||||
|
||||
if(self->wayland.window) {
|
||||
wl_egl_window_destroy(self->wayland.window);
|
||||
self->wayland.window = NULL;
|
||||
}
|
||||
|
||||
if(self->wayland.surface) {
|
||||
wl_surface_destroy(self->wayland.surface);
|
||||
self->wayland.surface = NULL;
|
||||
}
|
||||
|
||||
for(int i = 0; i < self->wayland.num_outputs; ++i) {
|
||||
if(self->wayland.outputs[i].output) {
|
||||
wl_output_destroy(self->wayland.outputs[i].output);
|
||||
self->wayland.outputs[i].output = NULL;
|
||||
}
|
||||
|
||||
if(self->wayland.outputs[i].name) {
|
||||
free(self->wayland.outputs[i].name);
|
||||
self->wayland.outputs[i].name = NULL;
|
||||
}
|
||||
}
|
||||
self->wayland.num_outputs = 0;
|
||||
|
||||
if(self->wayland.compositor) {
|
||||
wl_compositor_destroy(self->wayland.compositor);
|
||||
self->wayland.compositor = NULL;
|
||||
}
|
||||
|
||||
if(self->wayland.registry) {
|
||||
wl_registry_destroy(self->wayland.registry);
|
||||
self->wayland.registry = NULL;
|
||||
}
|
||||
|
||||
if(self->wayland.dpy) {
|
||||
wl_display_disconnect(self->wayland.dpy);
|
||||
self->wayland.dpy = NULL;
|
||||
}
|
||||
|
||||
if(self->egl_library) {
|
||||
dlclose(self->egl_library);
|
||||
self->egl_library = NULL;
|
||||
@@ -691,25 +472,6 @@ void gsr_egl_unload(gsr_egl *self) {
|
||||
memset(self, 0, sizeof(gsr_egl));
|
||||
}
|
||||
|
||||
bool gsr_egl_process_event(gsr_egl *self) {
|
||||
switch(gsr_egl_get_display_server(self)) {
|
||||
case GSR_DISPLAY_SERVER_X11: {
|
||||
if(XPending(self->x11.dpy)) {
|
||||
XNextEvent(self->x11.dpy, &self->x11.xev);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
case GSR_DISPLAY_SERVER_WAYLAND: {
|
||||
// TODO: pselect on wl_display_get_fd before doing dispatch
|
||||
const bool events_available = wl_display_dispatch_pending(self->wayland.dpy) > 0;
|
||||
wl_display_flush(self->wayland.dpy);
|
||||
return events_available;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void gsr_egl_swap_buffers(gsr_egl *self) {
|
||||
/* This uses less cpu than swap buffer on nvidia */
|
||||
// TODO: Do these and remove swap
|
||||
@@ -717,21 +479,9 @@ void gsr_egl_swap_buffers(gsr_egl *self) {
|
||||
//self->glFinish();
|
||||
if(self->egl_display) {
|
||||
self->eglSwapBuffers(self->egl_display, self->egl_surface);
|
||||
} else if(self->x11.window) {
|
||||
self->glXSwapBuffers(self->x11.dpy, self->x11.window);
|
||||
} else if(gsr_window_get_display_server(self->window) == GSR_DISPLAY_SERVER_X11) {
|
||||
Display *display = gsr_window_get_display(self->window);
|
||||
const Window window = (Window)gsr_window_get_window(self->window);
|
||||
self->glXSwapBuffers(display, window);
|
||||
}
|
||||
}
|
||||
|
||||
gsr_display_server gsr_egl_get_display_server(const gsr_egl *self) {
|
||||
if(self->wayland.dpy)
|
||||
return GSR_DISPLAY_SERVER_WAYLAND;
|
||||
else
|
||||
return GSR_DISPLAY_SERVER_X11;
|
||||
}
|
||||
|
||||
XEvent* gsr_egl_get_event_data(gsr_egl *self) {
|
||||
if(gsr_egl_get_display_server(self) == GSR_DISPLAY_SERVER_X11)
|
||||
return &self->x11.xev;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
76
src/main.cpp
76
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,
|
||||
|
||||
115
src/utils.c
115
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;
|
||||
}
|
||||
|
||||
|
||||
30
src/window/window.c
Normal file
30
src/window/window.c
Normal 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
319
src/window/window_wayland.c
Normal 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, ®istry_listener, self); // TODO: Error checking
|
||||
|
||||
// Fetch globals
|
||||
wl_display_roundtrip(self->display);
|
||||
|
||||
// Fetch wl_output
|
||||
wl_display_roundtrip(self->display);
|
||||
|
||||
if(!self->compositor) {
|
||||
fprintf(stderr, "gsr error: gsr_window_wayland_init failed: failed to find compositor\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
self->surface = wl_compositor_create_surface(self->compositor);
|
||||
if(!self->surface) {
|
||||
fprintf(stderr, "gsr error: gsr_window_wayland_init failed: failed to create surface\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
self->window = wl_egl_window_create(self->surface, 16, 16);
|
||||
if(!self->window) {
|
||||
fprintf(stderr, "gsr error: gsr_window_wayland_init failed: failed to create window\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
fail:
|
||||
gsr_window_wayland_deinit(self);
|
||||
return false;
|
||||
}
|
||||
|
||||
static void gsr_window_wayland_destroy(gsr_window *window) {
|
||||
gsr_window_wayland *self = window->priv;
|
||||
gsr_window_wayland_deinit(self);
|
||||
free(self);
|
||||
free(window);
|
||||
}
|
||||
|
||||
static bool gsr_window_wayland_process_event(gsr_window *window) {
|
||||
gsr_window_wayland *self = window->priv;
|
||||
// TODO: pselect on wl_display_get_fd before doing dispatch
|
||||
const bool events_available = wl_display_dispatch_pending(self->display) > 0;
|
||||
wl_display_flush(self->display);
|
||||
return events_available;
|
||||
}
|
||||
|
||||
static gsr_display_server gsr_wayland_get_display_server(void) {
|
||||
return GSR_DISPLAY_SERVER_WAYLAND;
|
||||
}
|
||||
|
||||
static void* gsr_window_wayland_get_display(gsr_window *window) {
|
||||
gsr_window_wayland *self = window->priv;
|
||||
return self->display;
|
||||
}
|
||||
|
||||
static void* gsr_window_wayland_get_window(gsr_window *window) {
|
||||
gsr_window_wayland *self = window->priv;
|
||||
return self->window;
|
||||
}
|
||||
|
||||
static gsr_monitor_rotation wayland_transform_to_gsr_rotation(int32_t rot) {
|
||||
switch(rot) {
|
||||
case 0: return GSR_MONITOR_ROT_0;
|
||||
case 1: return GSR_MONITOR_ROT_90;
|
||||
case 2: return GSR_MONITOR_ROT_180;
|
||||
case 3: return GSR_MONITOR_ROT_270;
|
||||
}
|
||||
return GSR_MONITOR_ROT_0;
|
||||
}
|
||||
|
||||
static void gsr_window_wayland_for_each_active_monitor_output_cached(const gsr_window *window, active_monitor_callback callback, void *userdata) {
|
||||
const gsr_window_wayland *self = window->priv;
|
||||
drm_connector_type_count type_counts[CONNECTOR_TYPE_COUNTS];
|
||||
int num_type_counts = 0;
|
||||
|
||||
for(int i = 0; i < self->num_outputs; ++i) {
|
||||
const gsr_wayland_output *output = &self->outputs[i];
|
||||
if(!output->name)
|
||||
continue;
|
||||
|
||||
const int connector_type_index = get_connector_type_by_name(output->name);
|
||||
drm_connector_type_count *connector_type = NULL;
|
||||
if(connector_type_index != -1)
|
||||
connector_type = drm_connector_types_get_index(type_counts, &num_type_counts, connector_type_index);
|
||||
|
||||
if(connector_type) {
|
||||
++connector_type->count;
|
||||
++connector_type->count_active;
|
||||
}
|
||||
|
||||
const gsr_monitor monitor = {
|
||||
.name = output->name,
|
||||
.name_len = strlen(output->name),
|
||||
.pos = { .x = output->pos.x, .y = output->pos.y },
|
||||
.size = { .x = output->size.x, .y = output->size.y },
|
||||
.connector_id = 0,
|
||||
.rotation = wayland_transform_to_gsr_rotation(output->transform),
|
||||
.monitor_identifier = connector_type ? monitor_identifier_from_type_and_count(connector_type_index, connector_type->count_active) : 0
|
||||
};
|
||||
callback(&monitor, userdata);
|
||||
}
|
||||
}
|
||||
|
||||
gsr_window* gsr_window_wayland_create(void) {
|
||||
gsr_window *window = calloc(1, sizeof(gsr_window));
|
||||
if(!window)
|
||||
return window;
|
||||
|
||||
gsr_window_wayland *window_wayland = calloc(1, sizeof(gsr_window_wayland));
|
||||
if(!window_wayland) {
|
||||
free(window);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(!gsr_window_wayland_init(window_wayland)) {
|
||||
free(window_wayland);
|
||||
free(window);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*window = (gsr_window) {
|
||||
.destroy = gsr_window_wayland_destroy,
|
||||
.process_event = gsr_window_wayland_process_event,
|
||||
.get_event_data = NULL,
|
||||
.get_display_server = gsr_wayland_get_display_server,
|
||||
.get_display = gsr_window_wayland_get_display,
|
||||
.get_window = gsr_window_wayland_get_window,
|
||||
.for_each_active_monitor_output_cached = gsr_window_wayland_for_each_active_monitor_output_cached,
|
||||
.priv = window_wayland
|
||||
};
|
||||
|
||||
return window;
|
||||
}
|
||||
162
src/window/window_x11.c
Normal file
162
src/window/window_x11.c
Normal 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;
|
||||
}
|
||||
Reference in New Issue
Block a user