diff options
Diffstat (limited to 'src/window')
-rw-r--r-- | src/window/window.c | 30 | ||||
-rw-r--r-- | src/window/window_wayland.c | 319 | ||||
-rw-r--r-- | src/window/window_x11.c | 162 |
3 files changed, 511 insertions, 0 deletions
diff --git a/src/window/window.c b/src/window/window.c new file mode 100644 index 0000000..1c6a24e --- /dev/null +++ b/src/window/window.c @@ -0,0 +1,30 @@ +#include "../../include/window/window.h" +#include <stddef.h> + +void gsr_window_destroy(gsr_window *self); + +bool gsr_window_process_event(gsr_window *self) { + return self->process_event(self); +} + +XEvent* gsr_window_get_event_data(gsr_window *self) { + if(self->get_event_data) + return self->get_event_data(self); + return NULL; +} + +gsr_display_server gsr_window_get_display_server(const gsr_window *self) { + return self->get_display_server(); +} + +void* gsr_window_get_display(gsr_window *self) { + return self->get_display(self); +} + +void* gsr_window_get_window(gsr_window *self) { + return self->get_window(self); +} + +void gsr_window_for_each_active_monitor_output_cached(const gsr_window *self, active_monitor_callback callback, void *userdata) { + self->for_each_active_monitor_output_cached(self, callback, userdata); +} diff --git a/src/window/window_wayland.c b/src/window/window_wayland.c new file mode 100644 index 0000000..b5557ee --- /dev/null +++ b/src/window/window_wayland.c @@ -0,0 +1,319 @@ +#include "../../include/window/window_wayland.h" + +#include "../../include/vec2.h" +#include "../../include/defs.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdint.h> +#include <wayland-client.h> +#include <wayland-egl.h> + +#define GSR_MAX_OUTPUTS 32 + +typedef struct { + uint32_t wl_name; + void *output; + vec2i pos; + vec2i size; + int32_t transform; + char *name; +} gsr_wayland_output; + +typedef struct { + void *display; + void *window; + void *registry; + void *surface; + void *compositor; + gsr_wayland_output outputs[GSR_MAX_OUTPUTS]; + int num_outputs; +} gsr_window_wayland; + +static void output_handle_geometry(void *data, struct wl_output *wl_output, + int32_t x, int32_t y, int32_t phys_width, int32_t phys_height, + int32_t subpixel, const char *make, const char *model, + int32_t transform) { + (void)wl_output; + (void)phys_width; + (void)phys_height; + (void)subpixel; + (void)make; + (void)model; + gsr_wayland_output *gsr_output = data; + gsr_output->pos.x = x; + gsr_output->pos.y = y; + gsr_output->transform = transform; +} + +static void output_handle_mode(void *data, struct wl_output *wl_output, uint32_t flags, int32_t width, int32_t height, int32_t refresh) { + (void)wl_output; + (void)flags; + (void)refresh; + gsr_wayland_output *gsr_output = data; + gsr_output->size.x = width; + gsr_output->size.y = height; +} + +static void output_handle_done(void *data, struct wl_output *wl_output) { + (void)data; + (void)wl_output; +} + +static void output_handle_scale(void* data, struct wl_output *wl_output, int32_t factor) { + (void)data; + (void)wl_output; + (void)factor; +} + +static void output_handle_name(void *data, struct wl_output *wl_output, const char *name) { + (void)wl_output; + gsr_wayland_output *gsr_output = data; + if(gsr_output->name) { + free(gsr_output->name); + gsr_output->name = NULL; + } + gsr_output->name = strdup(name); +} + +static void output_handle_description(void *data, struct wl_output *wl_output, const char *description) { + (void)data; + (void)wl_output; + (void)description; +} + +static const struct wl_output_listener output_listener = { + .geometry = output_handle_geometry, + .mode = output_handle_mode, + .done = output_handle_done, + .scale = output_handle_scale, + .name = output_handle_name, + .description = output_handle_description, +}; + +static void registry_add_object(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { + (void)version; + gsr_window_wayland *window_wayland = data; + if (strcmp(interface, "wl_compositor") == 0) { + if(window_wayland->compositor) { + wl_compositor_destroy(window_wayland->compositor); + window_wayland->compositor = NULL; + } + window_wayland->compositor = wl_registry_bind(registry, name, &wl_compositor_interface, 1); + } else if(strcmp(interface, wl_output_interface.name) == 0) { + if(version < 4) { + fprintf(stderr, "gsr warning: wl output interface version is < 4, expected >= 4 to capture a monitor. Using KMS capture instead\n"); + return; + } + + if(window_wayland->num_outputs == GSR_MAX_OUTPUTS) { + fprintf(stderr, "gsr warning: reached maximum outputs (%d), ignoring output %u\n", GSR_MAX_OUTPUTS, name); + return; + } + + gsr_wayland_output *gsr_output = &window_wayland->outputs[window_wayland->num_outputs]; + window_wayland->num_outputs++; + *gsr_output = (gsr_wayland_output) { + .wl_name = name, + .output = wl_registry_bind(registry, name, &wl_output_interface, 4), + .pos = { .x = 0, .y = 0 }, + .size = { .x = 0, .y = 0 }, + .transform = 0, + .name = NULL, + }; + wl_output_add_listener(gsr_output->output, &output_listener, gsr_output); + } +} + +static void registry_remove_object(void *data, struct wl_registry *registry, uint32_t name) { + (void)data; + (void)registry; + (void)name; +} + +static struct wl_registry_listener registry_listener = { + .global = registry_add_object, + .global_remove = registry_remove_object, +}; + +static void gsr_window_wayland_deinit(gsr_window_wayland *self) { + if(self->window) { + wl_egl_window_destroy(self->window); + self->window = NULL; + } + + if(self->surface) { + wl_surface_destroy(self->surface); + self->surface = NULL; + } + + for(int i = 0; i < self->num_outputs; ++i) { + if(self->outputs[i].output) { + wl_output_destroy(self->outputs[i].output); + self->outputs[i].output = NULL; + } + + if(self->outputs[i].name) { + free(self->outputs[i].name); + self->outputs[i].name = NULL; + } + } + self->num_outputs = 0; + + if(self->compositor) { + wl_compositor_destroy(self->compositor); + self->compositor = NULL; + } + + if(self->registry) { + wl_registry_destroy(self->registry); + self->registry = NULL; + } + + if(self->display) { + wl_display_disconnect(self->display); + self->display = NULL; + } +} + +static bool gsr_window_wayland_init(gsr_window_wayland *self) { + self->display = wl_display_connect(NULL); + if(!self->display) { + fprintf(stderr, "gsr error: gsr_window_wayland_init failed: failed to connect to the Wayland server\n"); + goto fail; + } + + self->registry = wl_display_get_registry(self->display); // TODO: Error checking + wl_registry_add_listener(self->registry, ®istry_listener, self); // TODO: Error checking + + // Fetch globals + wl_display_roundtrip(self->display); + + // Fetch wl_output + wl_display_roundtrip(self->display); + + if(!self->compositor) { + fprintf(stderr, "gsr error: gsr_window_wayland_init failed: failed to find compositor\n"); + goto fail; + } + + self->surface = wl_compositor_create_surface(self->compositor); + if(!self->surface) { + fprintf(stderr, "gsr error: gsr_window_wayland_init failed: failed to create surface\n"); + goto fail; + } + + self->window = wl_egl_window_create(self->surface, 16, 16); + if(!self->window) { + fprintf(stderr, "gsr error: gsr_window_wayland_init failed: failed to create window\n"); + goto fail; + } + + fail: + gsr_window_wayland_deinit(self); + return false; +} + +static void gsr_window_wayland_destroy(gsr_window *window) { + gsr_window_wayland *self = window->priv; + gsr_window_wayland_deinit(self); + free(self); + free(window); +} + +static bool gsr_window_wayland_process_event(gsr_window *window) { + gsr_window_wayland *self = window->priv; + // TODO: pselect on wl_display_get_fd before doing dispatch + const bool events_available = wl_display_dispatch_pending(self->display) > 0; + wl_display_flush(self->display); + return events_available; +} + +static gsr_display_server gsr_wayland_get_display_server(void) { + return GSR_DISPLAY_SERVER_WAYLAND; +} + +static void* gsr_window_wayland_get_display(gsr_window *window) { + gsr_window_wayland *self = window->priv; + return self->display; +} + +static void* gsr_window_wayland_get_window(gsr_window *window) { + gsr_window_wayland *self = window->priv; + return self->window; +} + +static gsr_monitor_rotation wayland_transform_to_gsr_rotation(int32_t rot) { + switch(rot) { + case 0: return GSR_MONITOR_ROT_0; + case 1: return GSR_MONITOR_ROT_90; + case 2: return GSR_MONITOR_ROT_180; + case 3: return GSR_MONITOR_ROT_270; + } + return GSR_MONITOR_ROT_0; +} + +static void gsr_window_wayland_for_each_active_monitor_output_cached(const gsr_window *window, active_monitor_callback callback, void *userdata) { + const gsr_window_wayland *self = window->priv; + drm_connector_type_count type_counts[CONNECTOR_TYPE_COUNTS]; + int num_type_counts = 0; + + for(int i = 0; i < self->num_outputs; ++i) { + const gsr_wayland_output *output = &self->outputs[i]; + if(!output->name) + continue; + + const int connector_type_index = get_connector_type_by_name(output->name); + drm_connector_type_count *connector_type = NULL; + if(connector_type_index != -1) + connector_type = drm_connector_types_get_index(type_counts, &num_type_counts, connector_type_index); + + if(connector_type) { + ++connector_type->count; + ++connector_type->count_active; + } + + const gsr_monitor monitor = { + .name = output->name, + .name_len = strlen(output->name), + .pos = { .x = output->pos.x, .y = output->pos.y }, + .size = { .x = output->size.x, .y = output->size.y }, + .connector_id = 0, + .rotation = wayland_transform_to_gsr_rotation(output->transform), + .monitor_identifier = connector_type ? monitor_identifier_from_type_and_count(connector_type_index, connector_type->count_active) : 0 + }; + callback(&monitor, userdata); + } +} + +gsr_window* gsr_window_wayland_create(void) { + gsr_window *window = calloc(1, sizeof(gsr_window)); + if(!window) + return window; + + gsr_window_wayland *window_wayland = calloc(1, sizeof(gsr_window_wayland)); + if(!window_wayland) { + free(window); + return NULL; + } + + if(!gsr_window_wayland_init(window_wayland)) { + free(window_wayland); + free(window); + return NULL; + } + + *window = (gsr_window) { + .destroy = gsr_window_wayland_destroy, + .process_event = gsr_window_wayland_process_event, + .get_event_data = NULL, + .get_display_server = gsr_wayland_get_display_server, + .get_display = gsr_window_wayland_get_display, + .get_window = gsr_window_wayland_get_window, + .for_each_active_monitor_output_cached = gsr_window_wayland_for_each_active_monitor_output_cached, + .priv = window_wayland + }; + + return window; +} diff --git a/src/window/window_x11.c b/src/window/window_x11.c new file mode 100644 index 0000000..55b0a75 --- /dev/null +++ b/src/window/window_x11.c @@ -0,0 +1,162 @@ +#include "../../include/window/window_x11.h" + +#include "../../include/vec2.h" +#include "../../include/defs.h" +#include "../../include/utils.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdint.h> +#include <X11/Xlib.h> + +#define GSR_MAX_OUTPUTS 32 + +typedef struct { + char *name; + vec2i pos; + vec2i size; + uint32_t connector_id; + gsr_monitor_rotation rotation; + uint32_t monitor_identifier; /* crtc id */ +} gsr_x11_output; + +typedef struct { + Display *display; + Window window; + gsr_x11_output outputs[GSR_MAX_OUTPUTS]; + int num_outputs; + XEvent xev; +} gsr_window_x11; + +static void store_x11_monitor(const gsr_monitor *monitor, void *userdata) { + gsr_window_x11 *window_x11 = userdata; + if(window_x11->num_outputs == GSR_MAX_OUTPUTS) { + fprintf(stderr, "gsr warning: reached maximum outputs (%d), ignoring output %s\n", GSR_MAX_OUTPUTS, monitor->name); + return; + } + + char *monitor_name = strdup(monitor->name); + if(!monitor_name) + return; + + const int index = window_x11->num_outputs; + window_x11->outputs[index].name = monitor_name; + window_x11->outputs[index].pos = monitor->pos; + window_x11->outputs[index].size = monitor->size; + window_x11->outputs[index].connector_id = monitor->connector_id; + window_x11->outputs[index].rotation = monitor->rotation; + window_x11->outputs[index].monitor_identifier = monitor->monitor_identifier; + ++window_x11->num_outputs; +} + +static void gsr_window_x11_deinit(gsr_window_x11 *self) { + if(self->window) { + XDestroyWindow(self->display, self->window); + self->window = None; + } + + for(int i = 0; i < self->num_outputs; ++i) { + if(self->outputs[i].name) { + free(self->outputs[i].name); + self->outputs[i].name = NULL; + } + } + self->num_outputs = 0; +} + +static bool gsr_window_x11_init(gsr_window_x11 *self) { + self->window = XCreateWindow(self->display, DefaultRootWindow(self->display), 0, 0, 16, 16, 0, CopyFromParent, InputOutput, CopyFromParent, 0, NULL); + if(!self->window) { + fprintf(stderr, "gsr error: gsr_window_x11_init failed: failed to create gl window\n"); + return false; + } + + self->num_outputs = 0; + for_each_active_monitor_output_x11_not_cached(self->display, store_x11_monitor, self); + return true; +} + +static void gsr_window_x11_destroy(gsr_window *window) { + gsr_window_x11 *self = window->priv; + gsr_window_x11_deinit(self); + free(self); + free(window); +} + +static bool gsr_window_x11_process_event(gsr_window *window) { + gsr_window_x11 *self = window->priv; + if(XPending(self->display)) { + XNextEvent(self->display, &self->xev); + return true; + } + return false; +} + +static XEvent* gsr_window_x11_get_event_data(gsr_window *window) { + gsr_window_x11 *self = window->priv; + return &self->xev; +} + +static gsr_display_server gsr_window_x11_get_display_server(void) { + return GSR_DISPLAY_SERVER_X11; +} + +static void* gsr_window_x11_get_display(gsr_window *window) { + gsr_window_x11 *self = window->priv; + return self->display; +} + +static void* gsr_window_x11_get_window(gsr_window *window) { + gsr_window_x11 *self = window->priv; + return (void*)self->window; +} + +static void gsr_window_x11_for_each_active_monitor_output_cached(const gsr_window *window, active_monitor_callback callback, void *userdata) { + const gsr_window_x11 *self = window->priv; + for(int i = 0; i < self->num_outputs; ++i) { + const gsr_x11_output *output = &self->outputs[i]; + const gsr_monitor monitor = { + .name = output->name, + .name_len = strlen(output->name), + .pos = output->pos, + .size = output->size, + .connector_id = output->connector_id, + .rotation = output->rotation, + .monitor_identifier = output->monitor_identifier + }; + callback(&monitor, userdata); + } +} + +gsr_window* gsr_window_x11_create(Display *display) { + gsr_window *window = calloc(1, sizeof(gsr_window)); + if(!window) + return window; + + gsr_window_x11 *window_x11 = calloc(1, sizeof(gsr_window_x11)); + if(!window_x11) { + free(window); + return NULL; + } + + window_x11->display = display; + if(!gsr_window_x11_init(window_x11)) { + free(window_x11); + free(window); + return NULL; + } + + *window = (gsr_window) { + .destroy = gsr_window_x11_destroy, + .process_event = gsr_window_x11_process_event, + .get_event_data = gsr_window_x11_get_event_data, + .get_display_server = gsr_window_x11_get_display_server, + .get_display = gsr_window_x11_get_display, + .get_window = gsr_window_x11_get_window, + .for_each_active_monitor_output_cached = gsr_window_x11_for_each_active_monitor_output_cached, + .priv = window_x11 + }; + + return window; +} |