aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2023-07-18 04:29:09 +0200
committerdec05eba <dec05eba@protonmail.com>2023-07-18 04:29:09 +0200
commitcbd62bda0252c919556b733e3db94fa431dbc29a (patch)
tree6290303f3e9d6ad1b03bfff3b04b967323c3c03e /src
parent32a194dec5cf807a71e689ce9b51f269890d8b4b (diff)
Fix for wayland
Diffstat (limited to 'src')
-rw-r--r--src/egl.c350
-rw-r--r--src/egl.h51
-rw-r--r--src/main.cpp610
3 files changed, 776 insertions, 235 deletions
diff --git a/src/egl.c b/src/egl.c
index ecefdf4..1daed3c 100644
--- a/src/egl.c
+++ b/src/egl.c
@@ -1,35 +1,211 @@
#include "egl.h"
#include "library_loader.h"
#include <string.h>
+#include <stdio.h>
+#include <dlfcn.h>
+#include <assert.h>
-static bool gsr_egl_create_window(gsr_egl *self) {
+#include <wayland-client.h>
+#include <wayland-egl.h>
+//#include "../external/wlr-export-dmabuf-unstable-v1-client-protocol.h"
+#include <unistd.h>
+
+#if 0
+static struct wl_compositor *compositor = NULL;
+static struct wl_output *output = NULL;
+static struct zwlr_export_dmabuf_manager_v1 *export_manager = NULL;
+static struct zwlr_export_dmabuf_frame_v1 *current_frame = NULL;
+//static struct wl_shell *shell = NULL;
+
+struct window {
+ EGLContext egl_context;
+ struct wl_surface *surface;
+ //struct wl_shell_surface *shell_surface;
+ struct wl_egl_window *egl_window;
+ EGLSurface egl_surface;
+};
+
+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) {
+ fprintf(stderr, "output geometry, make: %s, model: %s\n", make, model);
+}
+
+static void output_handle_mode(void *data, struct wl_output *wl_output,
+ uint32_t flags, int32_t width, int32_t height, int32_t refresh) {
+
+}
+
+static void output_handle_done(void* data, struct wl_output *wl_output) {
+ /* Nothing to do */
+}
+
+static void output_handle_scale(void* data, struct wl_output *wl_output,
+ int32_t factor) {
+ /* Nothing to do */
+}
+
+static const struct wl_output_listener output_listener = {
+ .geometry = output_handle_geometry,
+ .mode = output_handle_mode,
+ .done = output_handle_done,
+ .scale = output_handle_scale,
+};
+#endif
+
+static void registry_add_object (void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) {
+ (void)version;
+ struct wl_compositor **wayland_compositor = data;
+ if (strcmp(interface, "wl_compositor") == 0) {
+ if(*wayland_compositor) {
+ wl_compositor_destroy(*wayland_compositor);
+ *wayland_compositor = NULL;
+ }
+ *wayland_compositor = wl_registry_bind(registry, name, &wl_compositor_interface, 1);
+ }/* else if(strcmp(interface, wl_output_interface.name) == 0) {
+ fprintf(stderr, "wayland output, name: %u\n", name);
+ output = wl_registry_bind(registry, name, &wl_output_interface, 1);
+ wl_output_add_listener(output, &output_listener, NULL);
+ } else if(strcmp(interface, zwlr_export_dmabuf_manager_v1_interface.name) == 0) {
+ export_manager = wl_registry_bind(registry, name, &zwlr_export_dmabuf_manager_v1_interface, 1);
+ }*/
+ //fprintf(stderr, "interface: %s\n", interface);
+}
+
+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 = {&registry_add_object, &registry_remove_object};
+
+#if 0
+static void register_cb(gsr_egl *egl);
+
+static void frame_start(void *data, struct zwlr_export_dmabuf_frame_v1 *frame,
+ uint32_t width, uint32_t height, uint32_t offset_x, uint32_t offset_y,
+ uint32_t buffer_flags, uint32_t flags, uint32_t format,
+ uint32_t mod_high, uint32_t mod_low, uint32_t num_objects) {
+ gsr_egl *egl = data;
+ //fprintf(stderr, "frame start, width: %u, height: %u, offset x: %u, offset y: %u, format: %u, num objects: %u\n", width, height, offset_x, offset_y, format, num_objects);
+ egl->width = width;
+ egl->height = height;
+ egl->pixel_format = format;
+ egl->modifier = ((uint64_t)mod_high << 32) | mod_low;
+ current_frame = frame;
+}
+
+static void frame_object(void *data, struct zwlr_export_dmabuf_frame_v1 *frame,
+ uint32_t index, int32_t fd, uint32_t size, uint32_t offset,
+ uint32_t stride, uint32_t plane_index) {
+ // TODO: What if we get multiple objects? then we get multiple fd per frame
+ gsr_egl *egl = data;
+ //egl->fd = fd;
+ egl->pitch = stride;
+ egl->offset = offset;
+ //fprintf(stderr, "new frame, fd: %d, index: %u, size: %u, offset: %u, stride: %u, plane_index: %u\n", fd, index, size, offset, stride, plane_index);
+ close(fd);
+}
+
+
+static void frame_ready(void *data, struct zwlr_export_dmabuf_frame_v1 *frame,
+ uint32_t tv_sec_hi, uint32_t tv_sec_lo, uint32_t tv_nsec) {
+ register_cb(data);
+}
+
+static void frame_cancel(void *data, struct zwlr_export_dmabuf_frame_v1 *frame,
+ uint32_t reason) {
+ register_cb(data);
+}
+
+
+static const struct zwlr_export_dmabuf_frame_v1_listener frame_listener = {
+ .frame = frame_start,
+ .object = frame_object,
+ .ready = frame_ready,
+ .cancel = frame_cancel,
+};
+
+static struct zwlr_export_dmabuf_frame_v1 *frame_callback = NULL;
+static void register_cb(gsr_egl *egl) {
+ bool with_cursor = false;
+ frame_callback = zwlr_export_dmabuf_manager_v1_capture_output(export_manager, with_cursor, output);
+ zwlr_export_dmabuf_frame_v1_add_listener(frame_callback, &frame_listener, egl);
+}
+#endif
+
+// 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) {
EGLConfig ecfg;
int32_t num_config = 0;
+
EGLDisplay egl_display = NULL;
EGLSurface egl_surface = NULL;
EGLContext egl_context = NULL;
- Window window = None;
-
- int32_t attr[] = {
+
+ Window x11_window = None;
+
+ struct wl_registry *wayland_registry = NULL;
+ struct wl_compositor *wayland_compositor = NULL;
+ struct wl_surface *wayland_surface = NULL;
+ void *wayland_dpy = NULL;
+ void *wayland_window = NULL;
+
+ const int32_t attr[] = {
EGL_BUFFER_SIZE, 24,
- EGL_RENDERABLE_TYPE,
- EGL_OPENGL_ES2_BIT,
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_NONE
};
- int32_t ctxattr[] = {
+ const int32_t ctxattr[] = {
EGL_CONTEXT_CLIENT_VERSION, 2,
EGL_NONE
};
- window = XCreateWindow(self->dpy, DefaultRootWindow(self->dpy), 0, 0, 1, 1, 0, CopyFromParent, InputOutput, CopyFromParent, 0, NULL);
+ if(wayland) {
+ wayland_dpy = wl_display_connect(NULL);
+ if(!wayland_dpy) {
+ fprintf(stderr, "gsr error: gsr_egl_create_window failed: wl_display_connect failed\n");
+ goto fail;
+ }
- if(!window) {
- fprintf(stderr, "gsr error: gsr_gl_create_window failed: failed to create gl window\n");
- goto fail;
+ wayland_registry = wl_display_get_registry(wayland_dpy); // TODO: Error checking
+ wl_registry_add_listener(wayland_registry, &registry_listener, &wayland_compositor); // TODO: Error checking
+
+ // Fetch globals
+ wl_display_roundtrip(wayland_dpy);
+
+ // fetch wl_output
+ wl_display_roundtrip(wayland_dpy);
+
+ if(!wayland_compositor) {
+ fprintf(stderr, "gsr error: gsr_gl_create_window failed: failed to find compositor\n");
+ goto fail;
+ }
+
+ /*if(!output) {
+ fprintf(stderr, "gsr error: gsr_gl_create_window failed: failed to find output\n");
+ goto fail;
+ }
+
+ if(!export_manager) {
+ fprintf(stderr, "gsr error: gsr_gl_create_window failed: failed to find export manager\n");
+ goto fail;
+ }*/
+ } else {
+ x11_window = XCreateWindow(self->x11_dpy, DefaultRootWindow(self->x11_dpy), 0, 0, 16, 16, 0, CopyFromParent, InputOutput, CopyFromParent, 0, NULL);
+
+ if(!x11_window) {
+ fprintf(stderr, "gsr error: gsr_gl_create_window failed: failed to create gl window\n");
+ goto fail;
+ }
}
- egl_display = self->eglGetDisplay(self->dpy);
+ self->eglBindAPI(EGL_OPENGL_ES_API);
+
+ egl_display = self->eglGetDisplay(wayland_dpy ? (EGLNativeDisplayType)wayland_dpy : (EGLNativeDisplayType)self->x11_dpy);
if(!egl_display) {
fprintf(stderr, "gsr error: gsr_egl_create_window failed: eglGetDisplay failed\n");
goto fail;
@@ -45,24 +221,41 @@ static bool gsr_egl_create_window(gsr_egl *self) {
goto fail;
}
- egl_surface = self->eglCreateWindowSurface(egl_display, ecfg, (EGLNativeWindowType)window, NULL);
- if(!egl_surface) {
- fprintf(stderr, "gsr error: gsr_egl_create_window failed: failed to create window surface\n");
- goto fail;
- }
-
egl_context = self->eglCreateContext(egl_display, ecfg, NULL, ctxattr);
if(!egl_context) {
fprintf(stderr, "gsr error: gsr_egl_create_window failed: failed to create egl context\n");
goto fail;
}
- self->eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context);
+ if(wayland) {
+ wayland_surface = wl_compositor_create_surface(wayland_compositor);
+ wayland_window = wl_egl_window_create(wayland_surface, 16, 16);
+ egl_surface = self->eglCreateWindowSurface(egl_display, ecfg, (EGLNativeWindowType)wayland_window, NULL);
+ } else {
+ egl_surface = self->eglCreateWindowSurface(egl_display, ecfg, (EGLNativeWindowType)x11_window, NULL);
+ }
+
+ if(!egl_surface) {
+ fprintf(stderr, "gsr error: gsr_egl_create_window failed: failed to create window surface\n");
+ goto fail;
+ }
+
+ if(!self->eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context)) {
+ fprintf(stderr, "gsr error: gsr_egl_create_window failed: failed to make context current\n");
+ goto fail;
+ }
self->egl_display = egl_display;
self->egl_surface = egl_surface;
self->egl_context = egl_context;
- self->window = window;
+
+ self->x11_window = x11_window;
+
+ self->wayland_dpy = wayland_dpy;
+ self->wayland_window = wayland_window;
+ self->wayland_surface = wayland_surface;
+ self->wayland_compositor = wayland_compositor;
+ self->wayland_registry = wayland_registry;
return true;
fail:
@@ -72,8 +265,18 @@ static bool gsr_egl_create_window(gsr_egl *self) {
self->eglDestroySurface(egl_display, egl_surface);
if(egl_display)
self->eglTerminate(egl_display);
- if(window)
- XDestroyWindow(self->dpy, window);
+ if(x11_window)
+ XDestroyWindow(self->x11_dpy, x11_window);
+ if(wayland_window)
+ wl_egl_window_destroy(wayland_window);
+ if(wayland_surface)
+ wl_surface_destroy(wayland_surface);
+ if(wayland_compositor)
+ wl_compositor_destroy(wayland_compositor);
+ if(wayland_registry)
+ wl_registry_destroy(wayland_registry);
+ if(wayland_dpy)
+ wl_display_disconnect(wayland_dpy);
return false;
}
@@ -88,6 +291,7 @@ static bool gsr_egl_load_egl(gsr_egl *self, void *library) {
{ (void**)&self->eglMakeCurrent, "eglMakeCurrent" },
{ (void**)&self->eglDestroyContext, "eglDestroyContext" },
{ (void**)&self->eglDestroySurface, "eglDestroySurface" },
+ { (void**)&self->eglBindAPI, "eglBindAPI" },
{ NULL, NULL }
};
@@ -115,49 +319,46 @@ static bool gsr_egl_load_gl(gsr_egl *self, void *library) {
return true;
}
-bool gsr_egl_load(gsr_egl *self, Display *dpy) {
+bool gsr_egl_load(gsr_egl *self, Display *dpy, bool wayland) {
memset(self, 0, sizeof(gsr_egl));
- self->dpy = dpy;
+ self->x11_dpy = dpy;
+
+ void *egl_lib = NULL;
+ void *gl_lib = NULL;
dlerror(); /* clear */
- void *egl_lib = dlopen("libEGL.so.1", RTLD_LAZY);
+ egl_lib = dlopen("libEGL.so.1", RTLD_LAZY);
if(!egl_lib) {
fprintf(stderr, "gsr error: gsr_egl_load: failed to load libEGL.so.1, error: %s\n", dlerror());
- return false;
+ goto fail;
}
- void *gl_lib = dlopen("libGL.so.1", RTLD_LAZY);
+ gl_lib = dlopen("libGL.so.1", RTLD_LAZY);
if(!egl_lib) {
fprintf(stderr, "gsr error: gsr_egl_load: failed to load libGL.so.1, error: %s\n", dlerror());
- dlclose(egl_lib);
- memset(self, 0, sizeof(gsr_egl));
- return false;
+ goto fail;
}
- if(!gsr_egl_load_egl(self, egl_lib)) {
- dlclose(egl_lib);
- dlclose(gl_lib);
- memset(self, 0, sizeof(gsr_egl));
- return false;
- }
+ if(!gsr_egl_load_egl(self, egl_lib))
+ goto fail;
- if(!gsr_egl_load_gl(self, gl_lib)) {
- dlclose(egl_lib);
- dlclose(gl_lib);
- memset(self, 0, sizeof(gsr_egl));
- return false;
- }
+ if(!gsr_egl_load_gl(self, gl_lib))
+ goto fail;
- if(!gsr_egl_create_window(self)) {
- dlclose(egl_lib);
- dlclose(gl_lib);
- memset(self, 0, sizeof(gsr_egl));
- return false;
- }
+ if(!gsr_egl_create_window(self, wayland))
+ goto fail;
self->egl_library = egl_lib;
self->gl_library = gl_lib;
return true;
+
+ fail:
+ if(egl_lib)
+ dlclose(egl_lib);
+ if(gl_lib)
+ dlclose(gl_lib);
+ memset(self, 0, sizeof(gsr_egl));
+ return false;
}
void gsr_egl_unload(gsr_egl *self) {
@@ -176,9 +377,34 @@ void gsr_egl_unload(gsr_egl *self) {
self->egl_display = NULL;
}
- if(self->window) {
- XDestroyWindow(self->dpy, self->window);
- self->window = None;
+ if(self->x11_window) {
+ XDestroyWindow(self->x11_dpy, self->x11_window);
+ self->x11_window = None;
+ }
+
+ 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;
+ }
+
+ 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) {
@@ -193,3 +419,25 @@ void gsr_egl_unload(gsr_egl *self) {
memset(self, 0, sizeof(gsr_egl));
}
+
+void gsr_egl_update(gsr_egl *self) {
+ if(!self->wayland_dpy)
+ return;
+
+ wl_display_dispatch(self->wayland_dpy);
+}
+
+void gsr_egl_cleanup_frame(gsr_egl *self) {
+ if(!self->wayland_dpy)
+ return;
+
+ if(self->fd > 0) {
+ close(self->fd);
+ self->fd = 0;
+ }
+
+ /*if(current_frame) {
+ zwlr_export_dmabuf_frame_v1_destroy(current_frame);
+ current_frame = NULL;
+ }*/
+}
diff --git a/src/egl.h b/src/egl.h
index 52422bc..6a05f5f 100644
--- a/src/egl.h
+++ b/src/egl.h
@@ -8,13 +8,32 @@
#include <stdbool.h>
#include <stdint.h>
+#ifdef _WIN64
+typedef signed long long int khronos_intptr_t;
+typedef unsigned long long int khronos_uintptr_t;
+typedef signed long long int khronos_ssize_t;
+typedef unsigned long long int khronos_usize_t;
+#else
+typedef signed long int khronos_intptr_t;
+typedef unsigned long int khronos_uintptr_t;
+typedef signed long int khronos_ssize_t;
+typedef unsigned long int khronos_usize_t;
+#endif
+
typedef void* EGLDisplay;
-typedef void* EGLSurface;
-typedef void* EGLContext;
-typedef void* EGLConfig;
typedef void* EGLNativeDisplayType;
typedef uintptr_t EGLNativeWindowType;
+typedef uintptr_t EGLNativePixmapType;
+typedef void* EGLConfig;
+typedef void* EGLSurface;
+typedef void* EGLContext;
+typedef void* EGLClientBuffer;
+typedef void* EGLImage;
+typedef void* EGLImageKHR;
+typedef void *GLeglImageOES;
+typedef void (*__eglMustCastToProperFunctionPointerType)(void);
+#define EGL_OPENGL_ES_API 0x30A0
#define EGL_BUFFER_SIZE 0x3020
#define EGL_RENDERABLE_TYPE 0x3040
#define EGL_OPENGL_ES2_BIT 0x0004
@@ -27,11 +46,27 @@ typedef uintptr_t EGLNativeWindowType;
typedef struct {
void *egl_library;
void *gl_library;
- Display *dpy;
+
EGLDisplay egl_display;
EGLSurface egl_surface;
EGLContext egl_context;
- Window window;
+
+ Display *x11_dpy;
+ Window x11_window;
+
+ void *wayland_dpy;
+ void *wayland_window;
+ void *wayland_registry;
+ void *wayland_surface;
+ void *wayland_compositor;
+
+ int fd;
+ uint32_t width;
+ uint32_t height;
+ uint32_t pitch;
+ uint32_t offset;
+ uint32_t pixel_format;
+ uint64_t modifier;
EGLDisplay (*eglGetDisplay)(EGLNativeDisplayType display_id);
unsigned int (*eglInitialize)(EGLDisplay dpy, int32_t *major, int32_t *minor);
@@ -42,11 +77,15 @@ typedef struct {
unsigned int (*eglMakeCurrent)(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx);
unsigned int (*eglDestroyContext)(EGLDisplay dpy, EGLContext ctx);
unsigned int (*eglDestroySurface)(EGLDisplay dpy, EGLSurface surface);
+ unsigned int (*eglBindAPI)(unsigned int api);
const unsigned char* (*glGetString)(unsigned int name);
} gsr_egl;
-bool gsr_egl_load(gsr_egl *self, Display *dpy);
+bool gsr_egl_load(gsr_egl *self, Display *dpy, bool wayland);
void gsr_egl_unload(gsr_egl *self);
+void gsr_egl_update(gsr_egl *self);
+void gsr_egl_cleanup_frame(gsr_egl *self);
+
#endif /* GSR_EGL_H */
diff --git a/src/main.cpp b/src/main.cpp
index a1d1671..ddb2378 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -19,6 +19,8 @@
extern "C" {
#include "egl.h"
}
+#include <xf86drmMode.h>
+#include <xf86drm.h>
typedef struct {
Display *display;
@@ -94,6 +96,9 @@ static int num_audio_inputs_addable = 0;
static std::string record_file_current_filename;
static bool nvfbc_installed = false;
+static bool wayland = false;
+char drm_card_path[128];
+
enum class HotkeyMode {
NoAction,
NewHotkey,
@@ -257,12 +262,14 @@ static void drag_data_received (GtkWidget *widget, GdkDragContext*,
}
static void enable_stream_record_button_if_info_filled() {
- const gchar *selected_window_area = gtk_combo_box_get_active_id(GTK_COMBO_BOX(record_area_selection_menu));
- if(strcmp(selected_window_area, "window") == 0 && select_window_userdata.selected_window == None) {
- gtk_widget_set_sensitive(GTK_WIDGET(replay_button), false);
- gtk_widget_set_sensitive(GTK_WIDGET(record_button), false);
- gtk_widget_set_sensitive(GTK_WIDGET(stream_button), false);
- return;
+ if(!wayland) {
+ const gchar *selected_window_area = gtk_combo_box_get_active_id(GTK_COMBO_BOX(record_area_selection_menu));
+ if(strcmp(selected_window_area, "window") == 0 && select_window_userdata.selected_window == None) {
+ gtk_widget_set_sensitive(GTK_WIDGET(replay_button), false);
+ gtk_widget_set_sensitive(GTK_WIDGET(record_button), false);
+ gtk_widget_set_sensitive(GTK_WIDGET(stream_button), false);
+ return;
+ }
}
gtk_widget_set_sensitive(GTK_WIDGET(replay_button), true);
@@ -403,8 +410,10 @@ static std::string get_date_str() {
static void save_configs() {
config.main_config.record_area_option = gtk_combo_box_get_active_id(GTK_COMBO_BOX(record_area_selection_menu));
- config.main_config.record_area_width = gtk_spin_button_get_value_as_int(area_width_entry);
- config.main_config.record_area_height = gtk_spin_button_get_value_as_int(area_height_entry);
+ if(!wayland) {
+ config.main_config.record_area_width = gtk_spin_button_get_value_as_int(area_width_entry);
+ config.main_config.record_area_height = gtk_spin_button_get_value_as_int(area_height_entry);
+ }
config.main_config.fps = gtk_spin_button_get_value_as_int(fps_entry);
config.main_config.merge_audio_tracks = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(merge_audio_tracks_button));
@@ -420,46 +429,86 @@ static void save_configs() {
config.streaming_config.streaming_service = gtk_combo_box_get_active_id(GTK_COMBO_BOX(stream_service_input_menu));
config.streaming_config.stream_key = gtk_entry_get_text(stream_id_entry);
- config.streaming_config.start_recording_hotkey.keysym = streaming_hotkey.keysym;
- config.streaming_config.start_recording_hotkey.modifiers = streaming_hotkey.modkey_mask;
+ if(!wayland) {
+ config.streaming_config.start_recording_hotkey.keysym = streaming_hotkey.keysym;
+ config.streaming_config.start_recording_hotkey.modifiers = streaming_hotkey.modkey_mask;
+ }
config.record_config.save_directory = gtk_button_get_label(record_file_chooser_button);
config.record_config.container = gtk_combo_box_get_active_id(GTK_COMBO_BOX(record_container));
- config.record_config.start_recording_hotkey.keysym = record_hotkey.keysym;
- config.record_config.start_recording_hotkey.modifiers = record_hotkey.modkey_mask;
+ if(!wayland) {
+ config.record_config.start_recording_hotkey.keysym = record_hotkey.keysym;
+ config.record_config.start_recording_hotkey.modifiers = record_hotkey.modkey_mask;
+ }
config.replay_config.save_directory = gtk_button_get_label(replay_file_chooser_button);
config.replay_config.container = gtk_combo_box_get_active_id(GTK_COMBO_BOX(replay_container));
config.replay_config.replay_time = gtk_spin_button_get_value_as_int(replay_time_entry);
- config.replay_config.start_recording_hotkey.keysym = replay_start_stop_hotkey.keysym;
- config.replay_config.start_recording_hotkey.modifiers = replay_start_stop_hotkey.modkey_mask;
- config.replay_config.save_recording_hotkey.keysym = replay_save_hotkey.keysym;
- config.replay_config.save_recording_hotkey.modifiers = replay_save_hotkey.modkey_mask;
+ if(!wayland) {
+ config.replay_config.start_recording_hotkey.keysym = replay_start_stop_hotkey.keysym;
+ config.replay_config.start_recording_hotkey.modifiers = replay_start_stop_hotkey.modkey_mask;
+
+ config.replay_config.save_recording_hotkey.keysym = replay_save_hotkey.keysym;
+ config.replay_config.save_recording_hotkey.modifiers = replay_save_hotkey.modkey_mask;
+ }
save_config(config);
}
+typedef struct {
+ int x, y;
+} vec2i;
+
+typedef struct {
+ const char *name;
+ int name_len;
+ vec2i pos;
+ vec2i size;
+ XRRCrtcInfo *crt_info; /* Only on x11 */
+ uint32_t connector_id; /* Only on drm */
+} gsr_monitor;
+
+typedef enum {
+ GSR_CONNECTION_X11,
+ GSR_CONNECTION_WAYLAND,
+ GSR_CONNECTION_DRM
+} gsr_connection_type;
+
+using active_monitor_callback = std::function<void(const gsr_monitor *monitor, void *userdata)>;
+
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)
return &sr->modes[i];
}
- return nullptr;
+ return NULL;
}
-static void for_each_active_monitor_output(Display *display, std::function<void(const XRROutputInfo*, const XRRCrtcInfo*, const XRRModeInfo*)> callback_func) {
+static void for_each_active_monitor_output_x11(Display *display, active_monitor_callback &callback, void *userdata) {
XRRScreenResources *screen_res = XRRGetScreenResources(display, DefaultRootWindow(display));
if(!screen_res)
return;
+ char display_name[256];
for(int i = 0; i < screen_res->noutput; ++i) {
XRROutputInfo *out_info = XRRGetOutputInfo(display, screen_res, screen_res->outputs[i]);
if(out_info && out_info->crtc && out_info->connection == RR_Connected) {
XRRCrtcInfo *crt_info = XRRGetCrtcInfo(display, screen_res, out_info->crtc);
if(crt_info && crt_info->mode) {
const XRRModeInfo *mode_info = get_mode_info(screen_res, crt_info->mode);
- if(mode_info)
- callback_func(out_info, crt_info, mode_info);
+ if(mode_info && out_info->nameLen < (int)sizeof(display_name)) {
+ memcpy(display_name, out_info->name, out_info->nameLen);
+ display_name[out_info->nameLen] = '\0';
+
+ gsr_monitor monitor;
+ monitor.name = display_name;
+ monitor.name_len = out_info->nameLen;
+ monitor.pos = { (int)crt_info->x, (int)crt_info->y };
+ monitor.size = { (int)crt_info->width, (int)crt_info->height };
+ monitor.crt_info = crt_info;
+ monitor.connector_id = 0; // TODO: Get connector id
+ callback(&monitor, userdata);
+ }
}
if(crt_info)
XRRFreeCrtcInfo(crt_info);
@@ -471,6 +520,151 @@ static void for_each_active_monitor_output(Display *display, std::function<void(
XRRFreeScreenResources(screen_res);
}
+typedef struct {
+ int type;
+ int count;
+} 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) {
+ for(int i = 0; i < *num_type_counts; ++i) {
+ if(type_counts[i].type == connector_type)
+ return &type_counts[i];
+ }
+
+ if(*num_type_counts == CONNECTOR_TYPE_COUNTS)
+ return NULL;
+
+ const int index = *num_type_counts;
+ type_counts[index].type = connector_type;
+ type_counts[index].count = 0;
+ ++*num_type_counts;
+ return &type_counts[index];
+}
+
+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]);
+ if(prop) {
+ if(strcmp(name, prop->name) == 0) {
+ *result = props->prop_values[i];
+ drmModeFreeProperty(prop);
+ return true;
+ }
+ drmModeFreeProperty(prop);
+ }
+ }
+ return false;
+}
+
+static void for_each_active_monitor_output_drm(const char *drm_card_path, active_monitor_callback callback, void *userdata) {
+ int fd = open(drm_card_path, O_RDONLY);
+ if(fd == -1)
+ return;
+
+ drmSetClientCap(fd, DRM_CLIENT_CAP_ATOMIC, 1);
+
+ drm_connector_type_count type_counts[CONNECTOR_TYPE_COUNTS];
+ int num_type_counts = 0;
+
+ char display_name[256];
+ drmModeResPtr resources = drmModeGetResources(fd);
+ if(resources) {
+ for(int i = 0; i < resources->count_connectors; ++i) {
+ drmModeConnectorPtr connector = drmModeGetConnectorCurrent(fd, resources->connectors[i]);
+ if(!connector)
+ continue;
+
+ if(connector->connection != DRM_MODE_CONNECTED) {
+ drmModeFreeConnector(connector);
+ continue;
+ }
+
+ drm_connector_type_count *connector_type = drm_connector_types_get_index(type_counts, &num_type_counts, connector->connector_type);
+ const char *connection_name = drmModeGetConnectorTypeName(connector->connector_type);
+ const int connection_name_len = strlen(connection_name);
+ if(connector_type)
+ ++connector_type->count;
+
+ uint64_t crtc_id = 0;
+ connector_get_property_by_name(fd, connector, "CRTC_ID", &crtc_id);
+
+ drmModeCrtcPtr crtc = drmModeGetCrtc(fd, crtc_id);
+ if(connector_type && crtc_id > 0 && crtc && connection_name_len + 5 < (int)sizeof(display_name)) {
+ const int display_name_len = snprintf(display_name, sizeof(display_name), "%s-%d", connection_name, connector_type->count);
+ gsr_monitor monitor;
+ monitor.name = display_name;
+ monitor.name_len = display_name_len;
+ monitor.pos = { (int)crtc->x, (int)crtc->y };
+ monitor.size = { (int)crtc->width, (int)crtc->height };
+ monitor.crt_info = NULL;
+ monitor.connector_id = connector->connector_id;
+ callback(&monitor, userdata);
+ }
+
+ if(crtc)
+ drmModeFreeCrtc(crtc);
+
+ drmModeFreeConnector(connector);
+ }
+ drmModeFreeResources(resources);
+ }
+
+ close(fd);
+}
+
+static void for_each_active_monitor_output(void *connection, gsr_connection_type connection_type, active_monitor_callback callback, void *userdata) {
+ switch(connection_type) {
+ case GSR_CONNECTION_X11:
+ for_each_active_monitor_output_x11((Display*)connection, callback, userdata);
+ break;
+ case GSR_CONNECTION_WAYLAND:
+ // TODO: use gsr_egl here (connection)
+ break;
+ case GSR_CONNECTION_DRM:
+ for_each_active_monitor_output_drm((const char*)connection, callback, userdata);
+ break;
+ }
+}
+
+/* output should be >= 128 bytes */
+static bool gsr_get_valid_card_path(char *output) {
+ for(int i = 0; i < 10; ++i) {
+ sprintf(output, "/dev/dri/card%d", i);
+ int fd = open(output, O_RDONLY);
+ if(fd == -1)
+ continue;
+
+ drmSetClientCap(fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
+
+ drmModePlaneResPtr planes = drmModeGetPlaneResources(fd);
+ if(!planes) {
+ close(fd);
+ continue;
+ }
+
+ bool found_screen_card = false;
+ for(uint32_t i = 0; i < planes->count_planes; ++i) {
+ drmModePlanePtr plane = drmModeGetPlane(fd, planes->planes[i]);
+ if(!plane)
+ continue;
+
+ if(plane->fb_id)
+ found_screen_card = true;
+
+ drmModeFreePlane(plane);
+ if(found_screen_card)
+ break;
+ }
+
+ close(fd);
+ if(found_screen_card)
+ return true;
+ }
+ return false;
+}
+
static void show_notification(GtkApplication *app, const char *title, const char *body, GNotificationPriority priority) {
fprintf(stderr, "Notification: title: %s, body: %s\n", title, body);
GNotification *notification = g_notification_new(title);
@@ -731,6 +925,9 @@ static int xerror_grab_error(Display*, XErrorEvent*) {
}
static void ungrab_keyboard(Display *display) {
+ if(wayland)
+ return;
+
if(current_hotkey) {
gtk_grab_remove(current_hotkey->hotkey_entry);
gtk_widget_set_visible(current_hotkey->hotkey_active_label, false);
@@ -743,6 +940,9 @@ static void ungrab_keyboard(Display *display) {
}
static bool grab_ungrab_hotkey_combo(Display *display, Hotkey hotkey, bool grab) {
+ if(wayland)
+ return true;
+
if(hotkey.keysym == None && hotkey.modkey_mask == 0)
return true;
@@ -798,6 +998,9 @@ static bool grab_ungrab_hotkey_combo(Display *display, Hotkey hotkey, bool grab)
}
static void ungrab_keys(Display *display) {
+ if(wayland)
+ return;
+
grab_ungrab_hotkey_combo(display, streaming_hotkey, false);
grab_ungrab_hotkey_combo(display, record_hotkey, false);
grab_ungrab_hotkey_combo(display, replay_start_stop_hotkey, false);
@@ -904,16 +1107,18 @@ static gboolean on_start_replay_click(GtkButton*, gpointer userdata) {
PageNavigationUserdata *page_navigation_userdata = (PageNavigationUserdata*)userdata;
gtk_stack_set_visible_child(page_navigation_userdata->stack, page_navigation_userdata->replay_page);
- HotkeyResult hotkey_result = replace_grabbed_keys_depending_on_active_page();
- if(!hotkey_result.replay_start_stop_hotkey_success) {
- gtk_entry_set_text(GTK_ENTRY(replay_start_stop_hotkey.hotkey_entry), "");
- replay_start_stop_hotkey.keysym = 0;
- replay_start_stop_hotkey.modkey_mask = 0;
- }
- if(!hotkey_result.replay_save_hotkey_success) {
- gtk_entry_set_text(GTK_ENTRY(replay_save_hotkey.hotkey_entry), "");
- replay_save_hotkey.keysym = 0;
- replay_save_hotkey.modkey_mask = 0;
+ if(!wayland) {
+ HotkeyResult hotkey_result = replace_grabbed_keys_depending_on_active_page();
+ if(!hotkey_result.replay_start_stop_hotkey_success) {
+ gtk_entry_set_text(GTK_ENTRY(replay_start_stop_hotkey.hotkey_entry), "");
+ replay_start_stop_hotkey.keysym = 0;
+ replay_start_stop_hotkey.modkey_mask = 0;
+ }
+ if(!hotkey_result.replay_save_hotkey_success) {
+ gtk_entry_set_text(GTK_ENTRY(replay_save_hotkey.hotkey_entry), "");
+ replay_save_hotkey.keysym = 0;
+ replay_save_hotkey.modkey_mask = 0;
+ }
}
return true;
}
@@ -924,11 +1129,13 @@ static gboolean on_start_recording_click(GtkButton*, gpointer userdata) {
PageNavigationUserdata *page_navigation_userdata = (PageNavigationUserdata*)userdata;
gtk_stack_set_visible_child(page_navigation_userdata->stack, page_navigation_userdata->recording_page);
- HotkeyResult hotkey_result = replace_grabbed_keys_depending_on_active_page();
- if(!hotkey_result.record_hotkey_success) {
- gtk_entry_set_text(GTK_ENTRY(record_hotkey.hotkey_entry), "");
- record_hotkey.keysym = 0;
- record_hotkey.modkey_mask = 0;
+ if(!wayland) {
+ HotkeyResult hotkey_result = replace_grabbed_keys_depending_on_active_page();
+ if(!hotkey_result.record_hotkey_success) {
+ gtk_entry_set_text(GTK_ENTRY(record_hotkey.hotkey_entry), "");
+ record_hotkey.keysym = 0;
+ record_hotkey.modkey_mask = 0;
+ }
}
return true;
}
@@ -959,11 +1166,13 @@ static gboolean on_start_streaming_click(GtkButton*, gpointer userdata) {
PageNavigationUserdata *page_navigation_userdata = (PageNavigationUserdata*)userdata;
gtk_stack_set_visible_child(page_navigation_userdata->stack, page_navigation_userdata->streaming_page);
- HotkeyResult hotkey_result = replace_grabbed_keys_depending_on_active_page();
- if(!hotkey_result.streaming_hotkey_success) {
- gtk_entry_set_text(GTK_ENTRY(streaming_hotkey.hotkey_entry), "");
- streaming_hotkey.keysym = 0;
- streaming_hotkey.modkey_mask = 0;
+ if(!wayland) {
+ HotkeyResult hotkey_result = replace_grabbed_keys_depending_on_active_page();
+ if(!hotkey_result.streaming_hotkey_success) {
+ gtk_entry_set_text(GTK_ENTRY(streaming_hotkey.hotkey_entry), "");
+ streaming_hotkey.keysym = 0;
+ streaming_hotkey.modkey_mask = 0;
+ }
}
return true;
}
@@ -1056,8 +1265,8 @@ static gboolean on_start_replay_button_click(GtkButton *button, gpointer userdat
int fps = gtk_spin_button_get_value_as_int(fps_entry);
int replay_time = gtk_spin_button_get_value_as_int(replay_time_entry);
- int record_width = gtk_spin_button_get_value_as_int(area_width_entry);
- int record_height = gtk_spin_button_get_value_as_int(area_height_entry);
+ int record_width = wayland ? 0 : gtk_spin_button_get_value_as_int(area_width_entry);
+ int record_height = wayland ? 0 : gtk_spin_button_get_value_as_int(area_height_entry);
char dir_tmp[PATH_MAX];
strcpy(dir_tmp, dir);
@@ -1177,8 +1386,8 @@ static gboolean on_start_recording_button_click(GtkButton *button, gpointer user
save_configs();
int fps = gtk_spin_button_get_value_as_int(fps_entry);
- int record_width = gtk_spin_button_get_value_as_int(area_width_entry);
- int record_height = gtk_spin_button_get_value_as_int(area_height_entry);
+ int record_width = wayland ? 0 : gtk_spin_button_get_value_as_int(area_width_entry);
+ int record_height = wayland ? 0 : gtk_spin_button_get_value_as_int(area_height_entry);
char dir_tmp[PATH_MAX];
strcpy(dir_tmp, dir);
@@ -1292,8 +1501,8 @@ static gboolean on_start_streaming_button_click(GtkButton *button, gpointer user
const char *stream_id_str = gtk_entry_get_text(stream_id_entry);
int fps = gtk_spin_button_get_value_as_int(fps_entry);
- int record_width = gtk_spin_button_get_value_as_int(area_width_entry);
- int record_height = gtk_spin_button_get_value_as_int(area_height_entry);
+ int record_width = wayland ? 0 : gtk_spin_button_get_value_as_int(area_width_entry);
+ int record_height = wayland ? 0 : gtk_spin_button_get_value_as_int(area_height_entry);
bool follow_focused = false;
std::string window_str = gtk_combo_box_get_active_id(GTK_COMBO_BOX(record_area_selection_menu));
@@ -1546,7 +1755,7 @@ static void view_combo_box_change_callback(GtkComboBox *widget, gpointer userdat
const gchar *selected_view = gtk_combo_box_get_active_id(widget);
gtk_widget_set_visible(GTK_WIDGET(video_codec_grid), strcmp(selected_view, "advanced") == 0);
gtk_widget_set_visible(GTK_WIDGET(audio_codec_grid), strcmp(selected_view, "advanced") == 0);
- gtk_widget_set_visible(GTK_WIDGET(overclock_grid), strcmp(selected_view, "advanced") == 0 && gpu_inf.vendor == GPU_VENDOR_NVIDIA);
+ gtk_widget_set_visible(GTK_WIDGET(overclock_grid), strcmp(selected_view, "advanced") == 0 && gpu_inf.vendor == GPU_VENDOR_NVIDIA && !wayland);
}
static void stream_service_item_change_callback(GtkComboBox *widget, gpointer userdata) {
@@ -1596,6 +1805,9 @@ static void keypress_toggle_recording(bool recording_state, GtkButton *record_bu
}
static GdkFilterReturn hotkey_filter_callback(GdkXEvent *xevent, GdkEvent*, gpointer userdata) {
+ if(wayland)
+ return GDK_FILTER_CONTINUE;
+
if(hotkey_mode == HotkeyMode::NoAction)
return GDK_FILTER_CONTINUE;
@@ -1725,6 +1937,9 @@ static GdkFilterReturn hotkey_filter_callback(GdkXEvent *xevent, GdkEvent*, gpoi
}
static gboolean on_hotkey_entry_click(GtkWidget *button, gpointer) {
+ if(wayland)
+ return true;
+
hotkey_mode = HotkeyMode::NewHotkey;
pressed_hotkey.hotkey_entry = nullptr;
@@ -1802,70 +2017,82 @@ static GtkWidget* create_common_settings_page(GtkStack *stack, GtkApplication *a
gtk_container_add(GTK_CONTAINER(record_area_frame), GTK_WIDGET(record_area_grid));
record_area_selection_menu = GTK_COMBO_BOX_TEXT(gtk_combo_box_text_new());
- gtk_combo_box_text_append(record_area_selection_menu, "window", "Window");
- gtk_combo_box_text_append(record_area_selection_menu, "focused", "Follow focused window");
- const bool allow_screen_capture = nvfbc_installed || gpu_inf.vendor != GPU_VENDOR_NVIDIA;
+ if(!wayland) {
+ gtk_combo_box_text_append(record_area_selection_menu, "window", "Window");
+ gtk_combo_box_text_append(record_area_selection_menu, "focused", "Follow focused window");
+ }
+ const bool allow_screen_capture = wayland || nvfbc_installed || gpu_inf.vendor != GPU_VENDOR_NVIDIA;
if(allow_screen_capture) {
- if(gpu_inf.vendor == GPU_VENDOR_NVIDIA)
- gtk_combo_box_text_append(record_area_selection_menu, "screen", "All monitors");
- else
- gtk_combo_box_text_append(record_area_selection_menu, "screen", "All monitors (requires root access, may perform better)");
+ if(!wayland) {
+ if(gpu_inf.vendor == GPU_VENDOR_NVIDIA)
+ gtk_combo_box_text_append(record_area_selection_menu, "screen", "All monitors");
+ else
+ gtk_combo_box_text_append(record_area_selection_menu, "screen", "All monitors (requires root access, may perform better)");
+
+ if(gpu_inf.vendor == GPU_VENDOR_NVIDIA)
+ gtk_combo_box_text_append(record_area_selection_menu, "screen-direct-force", "All monitors (for VRR. No cursor, may have driver issues. Only use with VRR monitors!)");
+ }
- if(gpu_inf.vendor == GPU_VENDOR_NVIDIA)
- gtk_combo_box_text_append(record_area_selection_menu, "screen-direct-force", "All monitors (for VRR. No cursor, may have driver issues. Only use with VRR monitors!)");
+ void *connection = wayland ? (void*)drm_card_path : gdk_x11_get_default_xdisplay();
+ const gsr_connection_type connection_type = wayland ? GSR_CONNECTION_DRM : GSR_CONNECTION_X11;
- for_each_active_monitor_output(gdk_x11_get_default_xdisplay(), [&](const XRROutputInfo *output_info, const XRRCrtcInfo *crtc_info, const XRRModeInfo*) {
+ for_each_active_monitor_output(connection, connection_type, [&](const gsr_monitor *monitor, void*) {
std::string label = "Monitor ";
- label.append(output_info->name, output_info->nameLen);
+ label.append(monitor->name, monitor->name_len);
label += " (";
- label += std::to_string(crtc_info->width);
+ label += std::to_string(monitor->size.x);
label += "x";
- label += std::to_string(crtc_info->height);
+ label += std::to_string(monitor->size.y);
if(gpu_inf.vendor != GPU_VENDOR_NVIDIA)
label += ", requires root access, may perform better";
label += ")";
// Leak on purpose, what are you gonna do? stab me?
- char *id = (char*)malloc(output_info->nameLen + 1);
+ char *id = (char*)malloc(monitor->name_len + 1);
if(!id) {
fprintf(stderr, "Failed to allocate memory\n");
abort();
}
- memcpy(id, output_info->name, output_info->nameLen);
- id[output_info->nameLen] = '\0';
+ memcpy(id, monitor->name, monitor->name_len);
+ id[monitor->name_len] = '\0';
gtk_combo_box_text_append(record_area_selection_menu, id, label.c_str());
- });
+ }, NULL);
}
- gtk_combo_box_set_active(GTK_COMBO_BOX(record_area_selection_menu), allow_screen_capture ? 2 : 0);
+ if(wayland)
+ gtk_combo_box_set_active(GTK_COMBO_BOX(record_area_selection_menu), 0);
+ else
+ gtk_combo_box_set_active(GTK_COMBO_BOX(record_area_selection_menu), allow_screen_capture ? 2 : 0);
gtk_widget_set_hexpand(GTK_WIDGET(record_area_selection_menu), true);
gtk_grid_attach(record_area_grid, GTK_WIDGET(record_area_selection_menu), 0, record_area_row++, 3, 1);
- select_window_button = GTK_BUTTON(gtk_button_new_with_label("Select window..."));
- gtk_widget_set_hexpand(GTK_WIDGET(select_window_button), true);
- g_signal_connect(select_window_button, "clicked", G_CALLBACK(on_select_window_button_click), app);
- gtk_grid_attach(record_area_grid, GTK_WIDGET(select_window_button), 0, record_area_row++, 3, 1);
+ if(!wayland) {
+ select_window_button = GTK_BUTTON(gtk_button_new_with_label("Select window..."));
+ gtk_widget_set_hexpand(GTK_WIDGET(select_window_button), true);
+ g_signal_connect(select_window_button, "clicked", G_CALLBACK(on_select_window_button_click), app);
+ gtk_grid_attach(record_area_grid, GTK_WIDGET(select_window_button), 0, record_area_row++, 3, 1);
- g_signal_connect(record_area_selection_menu, "changed", G_CALLBACK(record_area_item_change_callback), select_window_button);
+ g_signal_connect(record_area_selection_menu, "changed", G_CALLBACK(record_area_item_change_callback), select_window_button);
- area_size_label = GTK_LABEL(gtk_label_new("Area size: "));
- gtk_label_set_xalign(area_size_label, 0.0f);
- gtk_grid_attach(record_area_grid, GTK_WIDGET(area_size_label), 0, record_area_row++, 2, 1);
+ area_size_label = GTK_LABEL(gtk_label_new("Area size: "));
+ gtk_label_set_xalign(area_size_label, 0.0f);
+ gtk_grid_attach(record_area_grid, GTK_WIDGET(area_size_label), 0, record_area_row++, 2, 1);
- area_size_grid = GTK_GRID(gtk_grid_new());
- gtk_grid_attach(record_area_grid, GTK_WIDGET(area_size_grid), 0, record_area_row++, 3, 1);
+ area_size_grid = GTK_GRID(gtk_grid_new());
+ gtk_grid_attach(record_area_grid, GTK_WIDGET(area_size_grid), 0, record_area_row++, 3, 1);
- area_width_entry = GTK_SPIN_BUTTON(gtk_spin_button_new_with_range(5.0, 10000.0, 1.0));
- gtk_spin_button_set_value(area_width_entry, 1920.0);
- gtk_widget_set_hexpand(GTK_WIDGET(area_width_entry), true);
- gtk_grid_attach(area_size_grid, GTK_WIDGET(area_width_entry), 0, 0, 1, 1);
+ area_width_entry = GTK_SPIN_BUTTON(gtk_spin_button_new_with_range(5.0, 10000.0, 1.0));
+ gtk_spin_button_set_value(area_width_entry, 1920.0);
+ gtk_widget_set_hexpand(GTK_WIDGET(area_width_entry), true);
+ gtk_grid_attach(area_size_grid, GTK_WIDGET(area_width_entry), 0, 0, 1, 1);
- gtk_grid_attach(area_size_grid, gtk_label_new("x"), 1, 0, 1, 1);
+ gtk_grid_attach(area_size_grid, gtk_label_new("x"), 1, 0, 1, 1);
- area_height_entry = GTK_SPIN_BUTTON(gtk_spin_button_new_with_range(5.0, 10000.0, 1.0));
- gtk_spin_button_set_value(area_height_entry, 1080.0);
- gtk_widget_set_hexpand(GTK_WIDGET(area_height_entry), true);
- gtk_grid_attach(area_size_grid, GTK_WIDGET(area_height_entry), 2, 0, 1, 1);
+ area_height_entry = GTK_SPIN_BUTTON(gtk_spin_button_new_with_range(5.0, 10000.0, 1.0));
+ gtk_spin_button_set_value(area_height_entry, 1080.0);
+ gtk_widget_set_hexpand(GTK_WIDGET(area_height_entry), true);
+ gtk_grid_attach(area_size_grid, GTK_WIDGET(area_height_entry), 2, 0, 1, 1);
+ }
GtkFrame *audio_input_frame = GTK_FRAME(gtk_frame_new("Audio input"));
gtk_grid_attach(grid, GTK_WIDGET(audio_input_frame), 0, grid_row++, 2, 1);
@@ -2057,34 +2284,37 @@ static GtkWidget* create_replay_page(GtkApplication *app, GtkStack *stack) {
gtk_grid_set_column_spacing(grid, 10);
gtk_widget_set_margin(GTK_WIDGET(grid), 10, 10, 10, 10);
- GtkWidget *hotkey_active_label = gtk_label_new("Press a key combination to set a new hotkey or Esc to cancel");
- gtk_grid_attach(grid, hotkey_active_label, 0, row++, 5, 1);
+ GtkWidget *hotkey_active_label = NULL;
+ if(!wayland) {
+ hotkey_active_label = gtk_label_new("Press a key combination to set a new hotkey or Esc to cancel");
+ gtk_grid_attach(grid, hotkey_active_label, 0, row++, 5, 1);
- GtkWidget *a = gtk_label_new("Press");
- gtk_widget_set_halign(a, GTK_ALIGN_END);
+ GtkWidget *a = gtk_label_new("Press");
+ gtk_widget_set_halign(a, GTK_ALIGN_END);
- GtkWidget *b = gtk_label_new("to start/stop the replay and ");
- gtk_widget_set_halign(b, GTK_ALIGN_START);
+ GtkWidget *b = gtk_label_new("to start/stop the replay and ");
+ gtk_widget_set_halign(b, GTK_ALIGN_START);
- GtkWidget *c = gtk_label_new("to save");
- gtk_widget_set_halign(c, GTK_ALIGN_START);
+ GtkWidget *c = gtk_label_new("to save");
+ gtk_widget_set_halign(c, GTK_ALIGN_START);
- replay_start_stop_hotkey_button = gtk_entry_new();
- gtk_entry_set_text(GTK_ENTRY(replay_start_stop_hotkey_button), "Alt + F1");
- g_signal_connect(replay_start_stop_hotkey_button, "button-press-event", G_CALLBACK(on_hotkey_entry_click), replay_start_stop_hotkey_button);
+ replay_start_stop_hotkey_button = gtk_entry_new();
+ gtk_entry_set_text(GTK_ENTRY(replay_start_stop_hotkey_button), "Alt + F1");
+ g_signal_connect(replay_start_stop_hotkey_button, "button-press-event", G_CALLBACK(on_hotkey_entry_click), replay_start_stop_hotkey_button);
- replay_save_hotkey_button = gtk_entry_new();
- gtk_entry_set_text(GTK_ENTRY(replay_save_hotkey_button), "Alt + F2");
- gtk_widget_set_halign(replay_save_hotkey_button, GTK_ALIGN_START);
- g_signal_connect(replay_save_hotkey_button, "button-press-event", G_CALLBACK(on_hotkey_entry_click), replay_save_hotkey_button);
+ replay_save_hotkey_button = gtk_entry_new();
+ gtk_entry_set_text(GTK_ENTRY(replay_save_hotkey_button), "Alt + F2");
+ gtk_widget_set_halign(replay_save_hotkey_button, GTK_ALIGN_START);
+ g_signal_connect(replay_save_hotkey_button, "button-press-event", G_CALLBACK(on_hotkey_entry_click), replay_save_hotkey_button);
- gtk_grid_attach(grid, a, 0, row, 1, 1);
- gtk_grid_attach(grid, replay_start_stop_hotkey_button, 1, row, 1, 1);
- gtk_grid_attach(grid, b, 2, row, 1, 1);
- gtk_grid_attach(grid, replay_save_hotkey_button, 3, row, 1, 1);
- gtk_grid_attach(grid, c, 4, row, 1, 1);
- ++row;
- gtk_grid_attach(grid, gtk_separator_new(GTK_ORIENTATION_HORIZONTAL), 0, row++, 5, 1);
+ gtk_grid_attach(grid, a, 0, row, 1, 1);
+ gtk_grid_attach(grid, replay_start_stop_hotkey_button, 1, row, 1, 1);
+ gtk_grid_attach(grid, b, 2, row, 1, 1);
+ gtk_grid_attach(grid, replay_save_hotkey_button, 3, row, 1, 1);
+ gtk_grid_attach(grid, c, 4, row, 1, 1);
+ ++row;
+ gtk_grid_attach(grid, gtk_separator_new(GTK_ORIENTATION_HORIZONTAL), 0, row++, 5, 1);
+ }
replay_start_stop_hotkey.modkey_mask = modkey_to_mask(XK_Alt_L);
replay_start_stop_hotkey.keysym = XK_F1;
@@ -2166,23 +2396,26 @@ static GtkWidget* create_recording_page(GtkApplication *app, GtkStack *stack) {
gtk_grid_set_column_spacing(grid, 10);
gtk_widget_set_margin(GTK_WIDGET(grid), 10, 10, 10, 10);
- GtkWidget *hotkey_active_label = gtk_label_new("Press a key combination to set a new hotkey or Esc to cancel");
- gtk_grid_attach(grid, hotkey_active_label, 0, row++, 3, 1);
+ GtkWidget *hotkey_active_label = NULL;
+ if(!wayland) {
+ hotkey_active_label = gtk_label_new("Press a key combination to set a new hotkey or Esc to cancel");
+ gtk_grid_attach(grid, hotkey_active_label, 0, row++, 3, 1);
- GtkWidget *a = gtk_label_new("Press");
- gtk_widget_set_halign(a, GTK_ALIGN_END);
+ GtkWidget *a = gtk_label_new("Press");
+ gtk_widget_set_halign(a, GTK_ALIGN_END);
- GtkWidget *b = gtk_label_new("to start/stop recording");
- gtk_widget_set_halign(b, GTK_ALIGN_START);
+ GtkWidget *b = gtk_label_new("to start/stop recording");
+ gtk_widget_set_halign(b, GTK_ALIGN_START);
- record_hotkey_button = gtk_entry_new();
- gtk_entry_set_text(GTK_ENTRY(record_hotkey_button), "Alt + F1");
- g_signal_connect(record_hotkey_button, "button-press-event", G_CALLBACK(on_hotkey_entry_click), record_hotkey_button);
- gtk_grid_attach(grid, a, 0, row, 1, 1);
- gtk_grid_attach(grid, record_hotkey_button, 1, row, 1, 1);
- gtk_grid_attach(grid, b, 2, row, 1, 1);
- ++row;
- gtk_grid_attach(grid, gtk_separator_new(GTK_ORIENTATION_HORIZONTAL), 0, row++, 3, 1);
+ record_hotkey_button = gtk_entry_new();
+ gtk_entry_set_text(GTK_ENTRY(record_hotkey_button), "Alt + F1");
+ g_signal_connect(record_hotkey_button, "button-press-event", G_CALLBACK(on_hotkey_entry_click), record_hotkey_button);
+ gtk_grid_attach(grid, a, 0, row, 1, 1);
+ gtk_grid_attach(grid, record_hotkey_button, 1, row, 1, 1);
+ gtk_grid_attach(grid, b, 2, row, 1, 1);
+ ++row;
+ gtk_grid_attach(grid, gtk_separator_new(GTK_ORIENTATION_HORIZONTAL), 0, row++, 3, 1);
+ }
record_hotkey.modkey_mask = modkey_to_mask(XK_Alt_L);
record_hotkey.keysym = XK_F1;
@@ -2240,23 +2473,26 @@ static GtkWidget* create_streaming_page(GtkApplication *app, GtkStack *stack) {
gtk_grid_set_column_spacing(grid, 10);
gtk_widget_set_margin(GTK_WIDGET(grid), 10, 10, 10, 10);
- GtkWidget *hotkey_active_label = gtk_label_new("Press a key combination to set a new hotkey or Esc to cancel");
- gtk_grid_attach(grid, hotkey_active_label, 0, row++, 3, 1);
+ GtkWidget *hotkey_active_label = NULL;
+ if(!wayland) {
+ hotkey_active_label = gtk_label_new("Press a key combination to set a new hotkey or Esc to cancel");
+ gtk_grid_attach(grid, hotkey_active_label, 0, row++, 3, 1);
- GtkWidget *a = gtk_label_new("Press");
- gtk_widget_set_halign(a, GTK_ALIGN_END);
+ GtkWidget *a = gtk_label_new("Press");
+ gtk_widget_set_halign(a, GTK_ALIGN_END);
- GtkWidget *b = gtk_label_new("to start/stop streaming");
- gtk_widget_set_halign(b, GTK_ALIGN_START);
+ GtkWidget *b = gtk_label_new("to start/stop streaming");
+ gtk_widget_set_halign(b, GTK_ALIGN_START);
- streaming_hotkey_button = gtk_entry_new();
- gtk_entry_set_text(GTK_ENTRY(streaming_hotkey_button), "Alt + F1");
- g_signal_connect(streaming_hotkey_button, "button-press-event", G_CALLBACK(on_hotkey_entry_click), streaming_hotkey_button);
- gtk_grid_attach(grid, a, 0, row, 1, 1);
- gtk_grid_attach(grid, streaming_hotkey_button, 1, row, 1, 1);
- gtk_grid_attach(grid, b, 2, row, 1, 1);
- ++row;
- gtk_grid_attach(grid, gtk_separator_new(GTK_ORIENTATION_HORIZONTAL), 0, row++, 3, 1);
+ streaming_hotkey_button = gtk_entry_new();
+ gtk_entry_set_text(GTK_ENTRY(streaming_hotkey_button), "Alt + F1");
+ g_signal_connect(streaming_hotkey_button, "button-press-event", G_CALLBACK(on_hotkey_entry_click), streaming_hotkey_button);
+ gtk_grid_attach(grid, a, 0, row, 1, 1);
+ gtk_grid_attach(grid, streaming_hotkey_button, 1, row, 1, 1);
+ gtk_grid_attach(grid, b, 2, row, 1, 1);
+ ++row;
+ gtk_grid_attach(grid, gtk_separator_new(GTK_ORIENTATION_HORIZONTAL), 0, row++, 3, 1);
+ }
streaming_hotkey.modkey_mask = modkey_to_mask(XK_Alt_L);
streaming_hotkey.keysym = XK_F1;
@@ -2353,36 +2589,41 @@ static void add_audio_input_track(const char *name) {
static void load_config(const gpu_info &gpu_inf) {
config = read_config();
- if(strcmp(config.main_config.record_area_option.c_str(), "window") == 0) {
+ if(!wayland && strcmp(config.main_config.record_area_option.c_str(), "window") == 0) {
//
- } else if(strcmp(config.main_config.record_area_option.c_str(), "focused") == 0) {
+ } else if(!wayland && strcmp(config.main_config.record_area_option.c_str(), "focused") == 0) {
//
- } else if(strcmp(config.main_config.record_area_option.c_str(), "screen") == 0 || strcmp(config.main_config.record_area_option.c_str(), "screen-direct-force") == 0) {
+ } else if(!wayland && (strcmp(config.main_config.record_area_option.c_str(), "screen") == 0 || strcmp(config.main_config.record_area_option.c_str(), "screen-direct-force") == 0)) {
//
} else {
+ void *connection = wayland ? (void*)drm_card_path : gdk_x11_get_default_xdisplay();
+ const gsr_connection_type connection_type = wayland ? GSR_CONNECTION_DRM : GSR_CONNECTION_X11;
+
bool found_monitor = false;
int monitor_name_size = strlen(config.main_config.record_area_option.c_str());
- for_each_active_monitor_output(gdk_x11_get_default_xdisplay(), [&](const XRROutputInfo *output_info, const XRRCrtcInfo*, const XRRModeInfo*) {
- if(monitor_name_size == output_info->nameLen && strncmp(config.main_config.record_area_option.c_str(), output_info->name, output_info->nameLen) == 0) {
+ for_each_active_monitor_output(connection, connection_type, [&](const gsr_monitor *monitor, void*) {
+ if(monitor_name_size == monitor->name_len && strncmp(config.main_config.record_area_option.c_str(), monitor->name, monitor->name_len) == 0) {
found_monitor = true;
}
- });
+ }, NULL);
if(!found_monitor)
config.main_config.record_area_option.clear();
}
if(config.main_config.record_area_option.empty()) {
- const bool allow_screen_capture = nvfbc_installed || gpu_inf.vendor != GPU_VENDOR_NVIDIA;
+ const bool allow_screen_capture = wayland || nvfbc_installed || gpu_inf.vendor != GPU_VENDOR_NVIDIA;
if(allow_screen_capture)
config.main_config.record_area_option = "screen";
else
config.main_config.record_area_option = "window";
}
- gtk_widget_set_visible(GTK_WIDGET(select_window_button), strcmp(config.main_config.record_area_option.c_str(), "window") == 0);
- gtk_widget_set_visible(GTK_WIDGET(area_size_label), strcmp(config.main_config.record_area_option.c_str(), "focused") == 0);
- gtk_widget_set_visible(GTK_WIDGET(area_size_grid), strcmp(config.main_config.record_area_option.c_str(), "focused") == 0);
+ if(!wayland) {
+ gtk_widget_set_visible(GTK_WIDGET(select_window_button), strcmp(config.main_config.record_area_option.c_str(), "window") == 0);
+ gtk_widget_set_visible(GTK_WIDGET(area_size_label), strcmp(config.main_config.record_area_option.c_str(), "focused") == 0);
+ gtk_widget_set_visible(GTK_WIDGET(area_size_grid), strcmp(config.main_config.record_area_option.c_str(), "focused") == 0);
+ }
if(config.main_config.record_area_width == 0)
config.main_config.record_area_width = 1920;
@@ -2422,8 +2663,10 @@ static void load_config(const gpu_info &gpu_inf) {
config.replay_config.replay_time = 1200;
gtk_combo_box_set_active_id(GTK_COMBO_BOX(record_area_selection_menu), config.main_config.record_area_option.c_str());
- gtk_spin_button_set_value(area_width_entry, config.main_config.record_area_width);
- gtk_spin_button_set_value(area_height_entry, config.main_config.record_area_height);
+ if(!wayland) {
+ gtk_spin_button_set_value(area_width_entry, config.main_config.record_area_width);
+ gtk_spin_button_set_value(area_height_entry, config.main_config.record_area_height);
+ }
gtk_spin_button_set_value(fps_entry, config.main_config.fps);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(merge_audio_tracks_button), config.main_config.merge_audio_tracks);
for(const std::string &audio_input : config.main_config.audio_input) {
@@ -2436,7 +2679,7 @@ static void load_config(const gpu_info &gpu_inf) {
gtk_combo_box_set_active_id(GTK_COMBO_BOX(stream_service_input_menu), config.streaming_config.streaming_service.c_str());
gtk_entry_set_text(stream_id_entry, config.streaming_config.stream_key.c_str());
- if(config.streaming_config.start_recording_hotkey.keysym) {
+ if(!wayland && config.streaming_config.start_recording_hotkey.keysym) {
streaming_hotkey.keysym = config.streaming_config.start_recording_hotkey.keysym;
streaming_hotkey.modkey_mask = config.streaming_config.start_recording_hotkey.modifiers;
set_hotkey_text_from_hotkey_data(GTK_ENTRY(streaming_hotkey_button), streaming_hotkey);
@@ -2444,7 +2687,7 @@ static void load_config(const gpu_info &gpu_inf) {
gtk_button_set_label(record_file_chooser_button, config.record_config.save_directory.c_str());
gtk_combo_box_set_active_id(GTK_COMBO_BOX(record_container), config.record_config.container.c_str());
- if(config.record_config.start_recording_hotkey.keysym) {
+ if(!wayland && config.record_config.start_recording_hotkey.keysym) {
record_hotkey.keysym = config.record_config.start_recording_hotkey.keysym;
record_hotkey.modkey_mask = config.record_config.start_recording_hotkey.modifiers;
set_hotkey_text_from_hotkey_data(GTK_ENTRY(record_hotkey_button), record_hotkey);
@@ -2453,12 +2696,12 @@ static void load_config(const gpu_info &gpu_inf) {
gtk_button_set_label(replay_file_chooser_button, config.replay_config.save_directory.c_str());
gtk_combo_box_set_active_id(GTK_COMBO_BOX(replay_container), config.replay_config.container.c_str());
gtk_spin_button_set_value(replay_time_entry, config.replay_config.replay_time);
- if(config.replay_config.start_recording_hotkey.keysym) {
+ if(!wayland && config.replay_config.start_recording_hotkey.keysym) {
replay_start_stop_hotkey.keysym = config.replay_config.start_recording_hotkey.keysym;
replay_start_stop_hotkey.modkey_mask = config.replay_config.start_recording_hotkey.modifiers;
set_hotkey_text_from_hotkey_data(GTK_ENTRY(replay_start_stop_hotkey_button), replay_start_stop_hotkey);
}
- if(config.replay_config.save_recording_hotkey.keysym) {
+ if(!wayland && config.replay_config.save_recording_hotkey.keysym) {
replay_save_hotkey.keysym = config.replay_config.save_recording_hotkey.keysym;
replay_save_hotkey.modkey_mask = config.replay_config.save_recording_hotkey.modifiers;
set_hotkey_text_from_hotkey_data(GTK_ENTRY(replay_save_hotkey_button), replay_save_hotkey);
@@ -2466,16 +2709,18 @@ static void load_config(const gpu_info &gpu_inf) {
gtk_combo_box_set_active_id(GTK_COMBO_BOX(view_combo_box), config.main_config.advanced_view ? "advanced" : "simple");
view_combo_box_change_callback(GTK_COMBO_BOX(view_combo_box), view_combo_box);
- gtk_widget_set_visible(record_hotkey.hotkey_active_label, false);
- gtk_widget_set_visible(streaming_hotkey.hotkey_active_label, false);
- gtk_widget_set_visible(replay_start_stop_hotkey.hotkey_active_label, false);
- gtk_widget_set_visible(replay_save_hotkey.hotkey_active_label, false);
+ if(!wayland) {
+ gtk_widget_set_visible(record_hotkey.hotkey_active_label, false);
+ gtk_widget_set_visible(streaming_hotkey.hotkey_active_label, false);
+ gtk_widget_set_visible(replay_start_stop_hotkey.hotkey_active_label, false);
+ gtk_widget_set_visible(replay_save_hotkey.hotkey_active_label, false);
+ }
enable_stream_record_button_if_info_filled();
}
-static bool gl_get_gpu_info(Display *dpy, gpu_info *info) {
+static bool gl_get_gpu_info(Display *dpy, gpu_info *info, bool wayland) {
gsr_egl gl;
- if(!gsr_egl_load(&gl, dpy)) {
+ if(!gsr_egl_load(&gl, dpy, wayland)) {
fprintf(stderr, "Error: failed to load opengl\n");
return false;
}
@@ -2520,10 +2765,12 @@ static bool is_xwayland(Display *dpy) {
return true;
bool xwayland_found = false;
- for_each_active_monitor_output(dpy, [&xwayland_found](const XRROutputInfo *output_info, const XRRCrtcInfo*, const XRRModeInfo*) {
- if(output_info->nameLen >= 8 && strncmp(output_info->name, "XWAYLAND", 8) == 0)
+ for_each_active_monitor_output(dpy, GSR_CONNECTION_X11, [&xwayland_found](const gsr_monitor *monitor, void*) {
+ if(monitor->name_len >= 8 && strncmp(monitor->name, "XWAYLAND", 8) == 0)
xwayland_found = true;
- });
+ else if(memmem(monitor->name, monitor->name_len, "X11", 3))
+ xwayland_found = true;
+ }, NULL);
return xwayland_found;
}
@@ -2537,25 +2784,29 @@ static const char* gpu_vendor_to_name(gpu_vendor vendor) {
}
static void activate(GtkApplication *app, gpointer) {
- nvfbc_installed = is_nv_fbc_installed();
-
Display *dpy = XOpenDisplay(NULL);
- const bool is_wayland = !dpy;
-
- if(is_wayland || is_xwayland(dpy)) {
+ wayland = !dpy || is_xwayland(dpy);
+ if(dpy)
XCloseDisplay(dpy);
- GtkWidget *dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
- "GPU Screen Recorder only works in a pure X11 session. Xwayland is not supported.");
- gtk_dialog_run(GTK_DIALOG(dialog));
- gtk_widget_destroy(dialog);
- g_application_quit(G_APPLICATION(app));
- return;
+
+ nvfbc_installed = !wayland && is_nv_fbc_installed();
+
+ if(wayland) {
+ if(!gsr_get_valid_card_path(drm_card_path)) {
+ GtkWidget *dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
+ "Failed to find a valid DRM card.");
+ gtk_dialog_run(GTK_DIALOG(dialog));
+ gtk_widget_destroy(dialog);
+ g_application_quit(G_APPLICATION(app));
+ return;
+ }
+ } else {
+ drm_card_path[0] = '\0';
}
- XCloseDisplay(dpy);
- if(!gl_get_gpu_info(gdk_x11_get_default_xdisplay(), &gpu_inf)) {
+ if(!gl_get_gpu_info(gdk_x11_get_default_xdisplay(), &gpu_inf, wayland)) {
GtkWidget *dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
- "Failed to get OpenGL information. Make sure your are using a NVIDIA GPU and that you have NVIDIA proprietary drivers installed.");
+ "Failed to get OpenGL information. Make sure your GPU drivers are properly installed.");
gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_destroy(dialog);
g_application_quit(G_APPLICATION(app));
@@ -2592,7 +2843,8 @@ static void activate(GtkApplication *app, gpointer) {
select_window_userdata.app = app;
- crosshair_cursor = XCreateFontCursor(gdk_x11_get_default_xdisplay(), XC_crosshair);
+ if(!wayland)
+ crosshair_cursor = XCreateFontCursor(gdk_x11_get_default_xdisplay(), XC_crosshair);
GtkStack *stack = GTK_STACK(gtk_stack_new());
gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(stack));
@@ -2621,11 +2873,13 @@ static void activate(GtkApplication *app, gpointer) {
g_signal_connect(stream_button, "clicked", G_CALLBACK(on_start_streaming_click), &page_navigation_userdata);
g_signal_connect(stream_back_button, "clicked", G_CALLBACK(on_streaming_recording_replay_page_back_click), &page_navigation_userdata);
- xim = XOpenIM(gdk_x11_get_default_xdisplay(), NULL, NULL, NULL);
- xic = XCreateIC(xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, NULL);
+ if(!wayland) {
+ xim = XOpenIM(gdk_x11_get_default_xdisplay(), NULL, NULL, NULL);
+ xic = XCreateIC(xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, NULL);
- GdkWindow *root_window = gdk_get_default_root_window();
- gdk_window_add_filter(root_window, hotkey_filter_callback, &page_navigation_userdata);
+ GdkWindow *root_window = gdk_get_default_root_window();
+ gdk_window_add_filter(root_window, hotkey_filter_callback, &page_navigation_userdata);
+ }
g_timeout_add(1000, handle_child_process_death, app);