mirror of
https://github.com/hyprwm/wlroots-hyprland.git
synced 2024-11-02 20:05:58 +01:00
4b03bdc3ab
This callback allowed compositors to customize the EGL config used by the renderer. However with renderer v6 EGL configs aren't used anymore. Instead, buffers are allocated via GBM and GL FBOs are rendered to. So customizing the EGL config is a no-op.
399 lines
12 KiB
C
399 lines
12 KiB
C
#define _POSIX_C_SOURCE 200809L
|
|
#include <assert.h>
|
|
#include <fcntl.h>
|
|
#include <limits.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
|
|
#include <wlr/config.h>
|
|
|
|
#include <drm_fourcc.h>
|
|
#include <wayland-server-core.h>
|
|
|
|
#include <wlr/backend/interface.h>
|
|
#include <wlr/interfaces/wlr_input_device.h>
|
|
#include <wlr/interfaces/wlr_output.h>
|
|
#include <wlr/render/egl.h>
|
|
#include <wlr/render/gles2.h>
|
|
#include <wlr/util/log.h>
|
|
|
|
#include "backend/wayland.h"
|
|
#include "render/drm_format_set.h"
|
|
#include "render/gbm_allocator.h"
|
|
#include "render/wlr_renderer.h"
|
|
#include "util/signal.h"
|
|
|
|
#include "linux-dmabuf-unstable-v1-client-protocol.h"
|
|
#include "pointer-gestures-unstable-v1-client-protocol.h"
|
|
#include "presentation-time-client-protocol.h"
|
|
#include "xdg-decoration-unstable-v1-client-protocol.h"
|
|
#include "xdg-shell-client-protocol.h"
|
|
#include "tablet-unstable-v2-client-protocol.h"
|
|
#include "relative-pointer-unstable-v1-client-protocol.h"
|
|
|
|
struct wlr_wl_backend *get_wl_backend_from_backend(struct wlr_backend *backend) {
|
|
assert(wlr_backend_is_wl(backend));
|
|
return (struct wlr_wl_backend *)backend;
|
|
}
|
|
|
|
static int dispatch_events(int fd, uint32_t mask, void *data) {
|
|
struct wlr_wl_backend *wl = data;
|
|
|
|
if ((mask & WL_EVENT_HANGUP) || (mask & WL_EVENT_ERROR)) {
|
|
if (mask & WL_EVENT_ERROR) {
|
|
wlr_log(WLR_ERROR, "Failed to read from remote Wayland display");
|
|
}
|
|
wl_display_terminate(wl->local_display);
|
|
return 0;
|
|
}
|
|
|
|
int count = 0;
|
|
if (mask & WL_EVENT_READABLE) {
|
|
count = wl_display_dispatch(wl->remote_display);
|
|
}
|
|
if (mask & WL_EVENT_WRITABLE) {
|
|
wl_display_flush(wl->remote_display);
|
|
}
|
|
if (mask == 0) {
|
|
count = wl_display_dispatch_pending(wl->remote_display);
|
|
wl_display_flush(wl->remote_display);
|
|
}
|
|
|
|
if (count < 0) {
|
|
wlr_log(WLR_ERROR, "Failed to dispatch remote Wayland display");
|
|
wl_display_terminate(wl->local_display);
|
|
return 0;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
static void xdg_wm_base_handle_ping(void *data,
|
|
struct xdg_wm_base *base, uint32_t serial) {
|
|
xdg_wm_base_pong(base, serial);
|
|
}
|
|
|
|
static const struct xdg_wm_base_listener xdg_wm_base_listener = {
|
|
xdg_wm_base_handle_ping,
|
|
};
|
|
|
|
static void linux_dmabuf_v1_handle_format(void *data,
|
|
struct zwp_linux_dmabuf_v1 *linux_dmabuf_v1, uint32_t format) {
|
|
// Note, this event is deprecated
|
|
struct wlr_wl_backend *wl = data;
|
|
|
|
wlr_drm_format_set_add(&wl->linux_dmabuf_v1_formats, format,
|
|
DRM_FORMAT_MOD_INVALID);
|
|
}
|
|
|
|
static void linux_dmabuf_v1_handle_modifier(void *data,
|
|
struct zwp_linux_dmabuf_v1 *linux_dmabuf_v1, uint32_t format,
|
|
uint32_t modifier_hi, uint32_t modifier_lo) {
|
|
struct wlr_wl_backend *wl = data;
|
|
|
|
uint64_t modifier = ((uint64_t)modifier_hi << 32) | modifier_lo;
|
|
wlr_drm_format_set_add(&wl->linux_dmabuf_v1_formats, format, modifier);
|
|
}
|
|
|
|
static const struct zwp_linux_dmabuf_v1_listener linux_dmabuf_v1_listener = {
|
|
.format = linux_dmabuf_v1_handle_format,
|
|
.modifier = linux_dmabuf_v1_handle_modifier,
|
|
};
|
|
|
|
static void registry_global(void *data, struct wl_registry *registry,
|
|
uint32_t name, const char *iface, uint32_t version) {
|
|
struct wlr_wl_backend *wl = data;
|
|
|
|
wlr_log(WLR_DEBUG, "Remote wayland global: %s v%" PRIu32, iface, version);
|
|
|
|
if (strcmp(iface, wl_compositor_interface.name) == 0) {
|
|
wl->compositor = wl_registry_bind(registry, name,
|
|
&wl_compositor_interface, 4);
|
|
} else if (strcmp(iface, wl_seat_interface.name) == 0) {
|
|
struct wl_seat *wl_seat = wl_registry_bind(registry, name,
|
|
&wl_seat_interface, 5);
|
|
if (!create_wl_seat(wl_seat, wl)) {
|
|
wl_seat_destroy(wl_seat);
|
|
}
|
|
} else if (strcmp(iface, xdg_wm_base_interface.name) == 0) {
|
|
wl->xdg_wm_base = wl_registry_bind(registry, name,
|
|
&xdg_wm_base_interface, 1);
|
|
xdg_wm_base_add_listener(wl->xdg_wm_base, &xdg_wm_base_listener, NULL);
|
|
} else if (strcmp(iface, zxdg_decoration_manager_v1_interface.name) == 0) {
|
|
wl->zxdg_decoration_manager_v1 = wl_registry_bind(registry, name,
|
|
&zxdg_decoration_manager_v1_interface, 1);
|
|
} else if (strcmp(iface, zwp_pointer_gestures_v1_interface.name) == 0) {
|
|
wl->zwp_pointer_gestures_v1 = wl_registry_bind(registry, name,
|
|
&zwp_pointer_gestures_v1_interface, 1);
|
|
} else if (strcmp(iface, wp_presentation_interface.name) == 0) {
|
|
wl->presentation = wl_registry_bind(registry, name,
|
|
&wp_presentation_interface, 1);
|
|
} else if (strcmp(iface, zwp_tablet_manager_v2_interface.name) == 0) {
|
|
wl->tablet_manager = wl_registry_bind(registry, name,
|
|
&zwp_tablet_manager_v2_interface, 1);
|
|
} else if (strcmp(iface, zwp_linux_dmabuf_v1_interface.name) == 0 &&
|
|
version >= 3) {
|
|
wl->zwp_linux_dmabuf_v1 = wl_registry_bind(registry, name,
|
|
&zwp_linux_dmabuf_v1_interface, 3);
|
|
zwp_linux_dmabuf_v1_add_listener(wl->zwp_linux_dmabuf_v1,
|
|
&linux_dmabuf_v1_listener, wl);
|
|
} else if (strcmp(iface, zwp_relative_pointer_manager_v1_interface.name) == 0) {
|
|
wl->zwp_relative_pointer_manager_v1 = wl_registry_bind(registry, name,
|
|
&zwp_relative_pointer_manager_v1_interface, 1);
|
|
}
|
|
}
|
|
|
|
static void registry_global_remove(void *data, struct wl_registry *registry,
|
|
uint32_t name) {
|
|
// TODO
|
|
}
|
|
|
|
static const struct wl_registry_listener registry_listener = {
|
|
.global = registry_global,
|
|
.global_remove = registry_global_remove
|
|
};
|
|
|
|
/*
|
|
* Initializes the wayland backend. Opens a connection to a remote wayland
|
|
* compositor and creates surfaces for each output, then registers globals on
|
|
* the specified display.
|
|
*/
|
|
static bool backend_start(struct wlr_backend *backend) {
|
|
struct wlr_wl_backend *wl = get_wl_backend_from_backend(backend);
|
|
wlr_log(WLR_INFO, "Initializating wayland backend");
|
|
|
|
wl->started = true;
|
|
|
|
struct wlr_wl_seat *seat;
|
|
wl_list_for_each(seat, &wl->seats, link) {
|
|
if (seat->keyboard) {
|
|
create_wl_keyboard(seat);
|
|
}
|
|
|
|
if (wl->tablet_manager) {
|
|
wl_add_tablet_seat(wl->tablet_manager, seat);
|
|
}
|
|
}
|
|
|
|
for (size_t i = 0; i < wl->requested_outputs; ++i) {
|
|
wlr_wl_output_create(&wl->backend);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void backend_destroy(struct wlr_backend *backend) {
|
|
if (!backend) {
|
|
return;
|
|
}
|
|
|
|
struct wlr_wl_backend *wl = get_wl_backend_from_backend(backend);
|
|
|
|
struct wlr_wl_output *output, *tmp_output;
|
|
wl_list_for_each_safe(output, tmp_output, &wl->outputs, link) {
|
|
wlr_output_destroy(&output->wlr_output);
|
|
}
|
|
|
|
struct wlr_input_device *input_device, *tmp_input_device;
|
|
wl_list_for_each_safe(input_device, tmp_input_device, &wl->devices, link) {
|
|
wlr_input_device_destroy(input_device);
|
|
}
|
|
|
|
wlr_signal_emit_safe(&wl->backend.events.destroy, &wl->backend);
|
|
|
|
wl_list_remove(&wl->local_display_destroy.link);
|
|
|
|
wl_event_source_remove(wl->remote_display_src);
|
|
|
|
wlr_renderer_destroy(wl->renderer);
|
|
wlr_egl_finish(&wl->egl);
|
|
|
|
wlr_drm_format_set_finish(&wl->linux_dmabuf_v1_formats);
|
|
|
|
struct wlr_wl_buffer *buffer, *tmp_buffer;
|
|
wl_list_for_each_safe(buffer, tmp_buffer, &wl->buffers, link) {
|
|
destroy_wl_buffer(buffer);
|
|
}
|
|
|
|
destroy_wl_seats(wl);
|
|
if (wl->zxdg_decoration_manager_v1) {
|
|
zxdg_decoration_manager_v1_destroy(wl->zxdg_decoration_manager_v1);
|
|
}
|
|
if (wl->zwp_pointer_gestures_v1) {
|
|
zwp_pointer_gestures_v1_destroy(wl->zwp_pointer_gestures_v1);
|
|
}
|
|
if (wl->presentation) {
|
|
wp_presentation_destroy(wl->presentation);
|
|
}
|
|
if (wl->zwp_linux_dmabuf_v1) {
|
|
zwp_linux_dmabuf_v1_destroy(wl->zwp_linux_dmabuf_v1);
|
|
}
|
|
if (wl->zwp_relative_pointer_manager_v1) {
|
|
zwp_relative_pointer_manager_v1_destroy(wl->zwp_relative_pointer_manager_v1);
|
|
}
|
|
xdg_wm_base_destroy(wl->xdg_wm_base);
|
|
wl_compositor_destroy(wl->compositor);
|
|
wl_registry_destroy(wl->registry);
|
|
wl_display_disconnect(wl->remote_display);
|
|
free(wl);
|
|
}
|
|
|
|
static struct wlr_renderer *backend_get_renderer(struct wlr_backend *backend) {
|
|
struct wlr_wl_backend *wl = get_wl_backend_from_backend(backend);
|
|
return wl->renderer;
|
|
}
|
|
|
|
static struct wlr_backend_impl backend_impl = {
|
|
.start = backend_start,
|
|
.destroy = backend_destroy,
|
|
.get_renderer = backend_get_renderer,
|
|
};
|
|
|
|
bool wlr_backend_is_wl(struct wlr_backend *b) {
|
|
return b->impl == &backend_impl;
|
|
}
|
|
|
|
static void handle_display_destroy(struct wl_listener *listener, void *data) {
|
|
struct wlr_wl_backend *wl =
|
|
wl_container_of(listener, wl, local_display_destroy);
|
|
backend_destroy(&wl->backend);
|
|
}
|
|
|
|
struct wlr_backend *wlr_wl_backend_create(struct wl_display *display,
|
|
const char *remote) {
|
|
wlr_log(WLR_INFO, "Creating wayland backend");
|
|
|
|
struct wlr_wl_backend *wl = calloc(1, sizeof(*wl));
|
|
if (!wl) {
|
|
wlr_log_errno(WLR_ERROR, "Allocation failed");
|
|
return NULL;
|
|
}
|
|
|
|
wlr_backend_init(&wl->backend, &backend_impl);
|
|
|
|
wl->local_display = display;
|
|
wl_list_init(&wl->devices);
|
|
wl_list_init(&wl->outputs);
|
|
wl_list_init(&wl->seats);
|
|
wl_list_init(&wl->buffers);
|
|
|
|
wl->remote_display = wl_display_connect(remote);
|
|
if (!wl->remote_display) {
|
|
wlr_log_errno(WLR_ERROR, "Could not connect to remote display");
|
|
goto error_wl;
|
|
}
|
|
|
|
wl->registry = wl_display_get_registry(wl->remote_display);
|
|
if (!wl->registry) {
|
|
wlr_log_errno(WLR_ERROR, "Could not obtain reference to remote registry");
|
|
goto error_display;
|
|
}
|
|
|
|
wl_registry_add_listener(wl->registry, ®istry_listener, wl);
|
|
wl_display_roundtrip(wl->remote_display); // get globals
|
|
wl_display_roundtrip(wl->remote_display); // get linux-dmabuf formats
|
|
|
|
if (!wl->compositor) {
|
|
wlr_log(WLR_ERROR,
|
|
"Remote Wayland compositor does not support wl_compositor");
|
|
goto error_registry;
|
|
}
|
|
if (!wl->xdg_wm_base) {
|
|
wlr_log(WLR_ERROR,
|
|
"Remote Wayland compositor does not support xdg-shell");
|
|
goto error_registry;
|
|
}
|
|
|
|
struct wl_event_loop *loop = wl_display_get_event_loop(wl->local_display);
|
|
int fd = wl_display_get_fd(wl->remote_display);
|
|
int events = WL_EVENT_READABLE | WL_EVENT_ERROR | WL_EVENT_HANGUP;
|
|
wl->remote_display_src = wl_event_loop_add_fd(loop, fd, events,
|
|
dispatch_events, wl);
|
|
if (!wl->remote_display_src) {
|
|
wlr_log(WLR_ERROR, "Failed to create event source");
|
|
goto error_registry;
|
|
}
|
|
wl_event_source_check(wl->remote_display_src);
|
|
|
|
wl->renderer = wlr_renderer_autocreate(&wl->egl, EGL_PLATFORM_WAYLAND_EXT,
|
|
wl->remote_display, NULL, 0);
|
|
if (!wl->renderer) {
|
|
wlr_log(WLR_ERROR, "Could not create renderer");
|
|
goto error_event;
|
|
}
|
|
|
|
// TODO: get FD from linux-dmabuf hints instead
|
|
int drm_fd = wlr_renderer_get_drm_fd(wl->renderer);
|
|
if (drm_fd < 0) {
|
|
wlr_log(WLR_ERROR, "Failed to get DRM device FD from renderer");
|
|
goto error_event;
|
|
}
|
|
|
|
drm_fd = fcntl(drm_fd, F_DUPFD_CLOEXEC, 0);
|
|
if (drm_fd < 0) {
|
|
wlr_log_errno(WLR_ERROR, "fcntl(F_DUPFD_CLOEXEC) failed");
|
|
goto error_event;
|
|
}
|
|
|
|
struct wlr_gbm_allocator *alloc = wlr_gbm_allocator_create(drm_fd);
|
|
if (alloc == NULL) {
|
|
wlr_log(WLR_ERROR, "Failed to create GBM allocator");
|
|
goto error_event;
|
|
}
|
|
wl->allocator = &alloc->base;
|
|
|
|
uint32_t fmt = DRM_FORMAT_ARGB8888;
|
|
const struct wlr_drm_format *remote_format =
|
|
wlr_drm_format_set_get(&wl->linux_dmabuf_v1_formats, fmt);
|
|
if (remote_format == NULL) {
|
|
wlr_log(WLR_ERROR, "Remote compositor doesn't support format "
|
|
"0x%"PRIX32" via linux-dmabuf-unstable-v1", fmt);
|
|
goto error_event;
|
|
}
|
|
|
|
const struct wlr_drm_format_set *render_formats =
|
|
wlr_renderer_get_dmabuf_render_formats(wl->renderer);
|
|
if (render_formats == NULL) {
|
|
wlr_log(WLR_ERROR, "Failed to get available DMA-BUF formats from renderer");
|
|
return false;
|
|
}
|
|
const struct wlr_drm_format *render_format =
|
|
wlr_drm_format_set_get(render_formats, fmt);
|
|
if (render_format == NULL) {
|
|
wlr_log(WLR_ERROR, "Renderer doesn't support DRM format 0x%"PRIX32, fmt);
|
|
return false;
|
|
}
|
|
|
|
wl->format = wlr_drm_format_intersect(remote_format, render_format);
|
|
if (wl->format == NULL) {
|
|
wlr_log(WLR_ERROR, "Failed to intersect remote and render modifiers "
|
|
"for format 0x%"PRIX32, fmt);
|
|
return false;
|
|
}
|
|
|
|
wl->local_display_destroy.notify = handle_display_destroy;
|
|
wl_display_add_destroy_listener(display, &wl->local_display_destroy);
|
|
|
|
return &wl->backend;
|
|
|
|
error_event:
|
|
wl_event_source_remove(wl->remote_display_src);
|
|
error_registry:
|
|
if (wl->compositor) {
|
|
wl_compositor_destroy(wl->compositor);
|
|
}
|
|
if (wl->xdg_wm_base) {
|
|
xdg_wm_base_destroy(wl->xdg_wm_base);
|
|
}
|
|
wl_registry_destroy(wl->registry);
|
|
error_display:
|
|
wl_display_disconnect(wl->remote_display);
|
|
error_wl:
|
|
free(wl);
|
|
return NULL;
|
|
}
|
|
|
|
struct wl_display *wlr_wl_backend_get_remote_display(struct wlr_backend *backend) {
|
|
struct wlr_wl_backend *wl = get_wl_backend_from_backend(backend);
|
|
return wl->remote_display;
|
|
}
|