Commit 9bcfa56f authored by Drew DeVault's avatar Drew DeVault
Browse files

compositor: overhaul Wayland backend

This simplifies the Wayland backend, fixes the build configuration for
it, ports it to xdg-shell stable, and reworks some false assumptions
from the original port.
parent 55cbd03a
......@@ -59,7 +59,17 @@ endif
x11 = dependency('x11', required: false)
xcb = dependency('xcb', required: false)
xcb_randr = dependency('xcb-randr', required: false)
wayland = dependency('wayland-client', required: false)
wayland = dependency('wayland-client', required: false)
wayland_protos = dependency('wayland-protocols', required: false)
wayland_scanner = dependency('wayland-scanner', required: false)
if wayland_scanner.found()
wayland_scanner = find_program(
wayland_scanner.get_pkgconfig_variable('wayland_scanner'),
native: true,
)
endif
hidapi_required = false
openhmd_required = false
......
......@@ -37,7 +37,6 @@ set(GL_SOURCE_FILES
main/comp_swapchain.c
main/comp_window.h
main/comp_window_direct_mode.cpp
main/comp_window_wayland.cpp
main/comp_window_xcb.cpp
)
......
......@@ -535,6 +535,12 @@ compositor_init_window_pre_vulkan(struct comp_compositor *c)
switch (c->settings.window_type) {
case WINDOW_AUTO:
#ifdef VK_USE_PLATFORM_WAYLAND_KHR
if (compositor_try_window(c, comp_window_wayland_create(c))) {
c->settings.window_type = WINDOW_WAYLAND;
return true;
}
#endif
#ifdef VK_USE_PLATFORM_XLIB_XRANDR_EXT
if (compositor_try_window(c, comp_window_direct_create(c))) {
c->settings.window_type = WINDOW_DIRECT_RANDR;
......@@ -546,12 +552,6 @@ compositor_init_window_pre_vulkan(struct comp_compositor *c)
c->settings.window_type = WINDOW_XCB;
return true;
}
#endif
#ifdef VK_USE_PLATFORM_WAYLAND_KHR
if (compositor_try_window(c, comp_window_wayland_create(c))) {
c->settings.window_type = WINDOW_WAYLAND;
return true;
}
#endif
COMP_ERROR(c, "Failed to auto detect window support!");
break;
......
......@@ -30,7 +30,6 @@ comp_settings_init(struct comp_settings *s, struct xrt_device *xdev)
}
s->display = -1;
s->mode = -1;
s->color_format = VK_FORMAT_B8G8R8A8_UNORM;
s->color_space = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
s->present_mode = VK_PRESENT_MODE_FIFO_KHR;
......
......@@ -53,7 +53,6 @@ enum window_type
struct comp_settings
{
int display;
int mode;
VkFormat color_format;
VkColorSpaceKHR color_space;
......
......@@ -10,17 +10,14 @@
#ifdef VK_USE_PLATFORM_WAYLAND_KHR
#include <poll.h>
#include <errno.h>
#include <linux/input.h>
#include <poll.h>
#include <stdlib.h>
#include <string.h>
#include <wayland-client.h>
#include "xdg-shell-unstable-v6.h"
#include <map>
#include <vector>
#include <string>
#include <utility>
#include <cstring>
#include "xdg-shell-client-protocol.h"
#include "xrt/xrt_compiler.h"
#include "main/comp_window.h"
......@@ -31,48 +28,22 @@
*
*/
/*!
* Wayland display mode.
*/
struct comp_window_wayland_mode
{
std::pair<int, int> size;
int refresh;
};
/*!
* A single Wayland display.
*/
struct comp_window_wayland_display
{
wl_output *output;
std::string make;
std::string model;
std::vector<comp_window_wayland_mode> modes;
std::pair<int, int> physical_size_mm;
std::pair<int, int> position;
};
/*!
* A Wayland connection and window.
*/
struct comp_window_wayland
{
struct comp_window base = comp_window();
wl_display *display = nullptr;
wl_compositor *compositor = nullptr;
wl_surface *surface = nullptr;
struct comp_window base;
zxdg_shell_v6 *shell = nullptr;
zxdg_surface_v6 *xdg_surface = nullptr;
zxdg_toplevel_v6 *xdg_toplevel = nullptr;
struct wl_display *display;
struct wl_compositor *compositor;
struct wl_surface *surface;
std::vector<comp_window_wayland_display> displays = {};
struct xdg_wm_base *wm_base;
struct xdg_surface *xdg_surface;
struct xdg_toplevel *xdg_toplevel;
bool is_configured = false;
bool first_configure = true;
bool fullscreen_requested = false;
bool fullscreen_requested;
};
......@@ -94,17 +65,13 @@ comp_window_wayland_update_window_title(struct comp_window *w,
static void
comp_window_wayland_registry_global(struct comp_window_wayland *w,
wl_registry *registry,
struct wl_registry *registry,
uint32_t name,
const char *interface);
static void
comp_window_wayland_fullscreen(struct comp_window_wayland *w);
static void
comp_window_wayland_fullscreen(struct comp_window_wayland *w,
wl_output *output);
static bool
comp_window_wayland_init_swapchain(struct comp_window *w,
uint32_t width,
......@@ -117,36 +84,6 @@ comp_window_wayland_create_surface(struct comp_window_wayland *w,
static void
comp_window_wayland_flush(struct comp_window *w);
static void
comp_window_wayland_output_mode(struct comp_window_wayland *w,
wl_output *output,
unsigned int flags,
int width,
int height,
int refresh);
static comp_window_wayland_display *
comp_window_wayland_get_display_from_output(struct comp_window_wayland *w,
wl_output *output);
XRT_MAYBE_UNUSED static void
comp_window_wayland_print_displays(struct comp_window_wayland *w);
static comp_window_wayland_display *
comp_window_wayland_current_display(struct comp_window_wayland *w);
static comp_window_wayland_mode *
comp_window_wayland_current_mode(struct comp_window_wayland *w);
static std::string
mode_to_string(comp_window_wayland_mode *m);
static void
comp_window_wayland_validate_display(struct comp_window_wayland *w);
static void
validate_mode(struct comp_window_wayland *w);
static void
comp_window_wayland_configure(struct comp_window_wayland *w,
int32_t width,
......@@ -159,10 +96,11 @@ comp_window_wayland_configure(struct comp_window_wayland *w,
*
*/
extern "C" struct comp_window *
struct comp_window *
comp_window_wayland_create(struct comp_compositor *c)
{
auto w = new comp_window_wayland();
struct comp_window_wayland *w =
calloc(1, sizeof(struct comp_window_wayland));
w->base.name = "wayland";
w->base.destroy = comp_window_wayland_destroy;
......@@ -182,18 +120,18 @@ comp_window_wayland_destroy(struct comp_window *w)
if (w_wayland->surface) {
wl_surface_destroy(w_wayland->surface);
w_wayland->surface = nullptr;
w_wayland->surface = NULL;
}
if (w_wayland->compositor) {
wl_compositor_destroy(w_wayland->compositor);
w_wayland->compositor = nullptr;
w_wayland->compositor = NULL;
}
if (w_wayland->display) {
wl_display_disconnect(w_wayland->display);
w_wayland->display = nullptr;
w_wayland->display = NULL;
}
delete w;
free(w);
}
static void
......@@ -201,61 +139,56 @@ comp_window_wayland_update_window_title(struct comp_window *w,
const char *title)
{
struct comp_window_wayland *w_wayland = (struct comp_window_wayland *)w;
zxdg_toplevel_v6_set_title(w_wayland->xdg_toplevel, title);
xdg_toplevel_set_title(w_wayland->xdg_toplevel, title);
}
static void
comp_window_wayland_fullscreen(struct comp_window_wayland *w)
{
comp_window_wayland_fullscreen(
w, comp_window_wayland_current_display(w)->output);
}
static void
comp_window_wayland_fullscreen(struct comp_window_wayland *w, wl_output *output)
{
zxdg_toplevel_v6_set_fullscreen(w->xdg_toplevel, output);
xdg_toplevel_set_fullscreen(w->xdg_toplevel, NULL);
wl_surface_commit(w->surface);
}
static void
_xdg_surface_configure_cb(void *data, zxdg_surface_v6 *surface, uint32_t serial)
_xdg_surface_configure_cb(void *data,
struct xdg_surface *surface,
uint32_t serial)
{
zxdg_surface_v6_ack_configure(surface, serial);
xdg_surface_ack_configure(surface, serial);
}
static void
_xdg_toplevel_configure_cb(void *data,
zxdg_toplevel_v6 *toplevel,
struct xdg_toplevel *toplevel,
int32_t width,
int32_t height,
struct wl_array *states)
{
comp_window_wayland *w = (comp_window_wayland *)data;
struct comp_window_wayland *w = (struct comp_window_wayland *)data;
comp_window_wayland_configure(w, width, height);
}
static const zxdg_surface_v6_listener xdg_surface_listener = {
static const struct xdg_surface_listener xdg_surface_listener = {
_xdg_surface_configure_cb,
};
static void
_xdg_toplevel_close_cb(void *data, zxdg_toplevel_v6 *toplevel)
_xdg_toplevel_close_cb(void *data, struct xdg_toplevel *toplevel)
{}
static const zxdg_toplevel_v6_listener xdg_toplevel_listener = {
static const struct xdg_toplevel_listener xdg_toplevel_listener = {
_xdg_toplevel_configure_cb,
_xdg_toplevel_close_cb,
};
static void
_xdg_shell_ping_cb(void *data, zxdg_shell_v6 *shell, uint32_t serial)
_xdg_wm_base_ping_cb(void *data, struct xdg_wm_base *wm_base, uint32_t serial)
{
zxdg_shell_v6_pong(shell, serial);
xdg_wm_base_pong(wm_base, serial);
}
const zxdg_shell_v6_listener xdg_shell_listener = {
_xdg_shell_ping_cb,
static const struct xdg_wm_base_listener xdg_wm_base_listener = {
_xdg_wm_base_ping_cb,
};
static bool
......@@ -289,7 +222,7 @@ comp_window_wayland_create_surface(struct comp_window_wayland *w,
VkWaylandSurfaceCreateInfoKHR surface_info = {
.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR,
.pNext = nullptr,
.pNext = NULL,
.flags = 0,
.display = w->display,
.surface = w->surface,
......@@ -335,117 +268,41 @@ comp_window_wayland_flush(struct comp_window *w)
}
static void
comp_window_wayland_output_mode(struct comp_window_wayland *w,
wl_output *output,
unsigned int flags,
int width,
int height,
int refresh)
{
comp_window_wayland_mode m = {};
m.size = {width, height};
m.refresh = refresh;
comp_window_wayland_display *d =
comp_window_wayland_get_display_from_output(w, output);
if (d == nullptr) {
COMP_ERROR(w->base.c, "Output mode callback before geomentry!");
return;
}
d->modes.push_back(m);
}
static void
_output_done_cb(void *data, wl_output *output)
{}
static void
_output_scale_cb(void *data, wl_output *output, int scale)
{}
static void
_registry_global_remove_cb(void *data, wl_registry *registry, uint32_t name)
_registry_global_remove_cb(void *data,
struct wl_registry *registry,
uint32_t name)
{}
static void
_registry_global_cb(void *data,
wl_registry *registry,
struct wl_registry *registry,
uint32_t name,
const char *interface,
uint32_t version)
{
comp_window_wayland *w = (comp_window_wayland *)data;
struct comp_window_wayland *w = (struct comp_window_wayland *)data;
// vik_log_d("Interface: %s Version %d", interface, version);
comp_window_wayland_registry_global(w, registry, name, interface);
}
static void
_output_mode_cb(void *data,
wl_output *output,
unsigned int flags,
int width,
int height,
int refresh)
{
comp_window_wayland *w = (comp_window_wayland *)data;
comp_window_wayland_output_mode(w, output, flags, width, height,
refresh);
}
static void
_output_geometry_cb(void *data,
wl_output *output,
int x,
int y,
int w,
int h,
int subpixel,
const char *make,
const char *model,
int transform)
{
comp_window_wayland_display d = {};
d.output = output;
d.make = std::string(make);
d.model = std::string(model);
d.physical_size_mm = {w, h};
d.position = {x, y};
comp_window_wayland *self = (comp_window_wayland *)data;
self->displays.push_back(d);
}
// listeners
static const wl_registry_listener registry_listener = {
static const struct wl_registry_listener registry_listener = {
_registry_global_cb,
_registry_global_remove_cb,
};
static const wl_output_listener output_listener = {
_output_geometry_cb,
_output_mode_cb,
_output_done_cb,
_output_scale_cb,
};
static void
comp_window_wayland_registry_global(struct comp_window_wayland *w,
wl_registry *registry,
struct wl_registry *registry,
uint32_t name,
const char *interface)
{
if (strcmp(interface, "wl_compositor") == 0) {
w->compositor = (wl_compositor *)wl_registry_bind(
w->compositor = (struct wl_compositor *)wl_registry_bind(
registry, name, &wl_compositor_interface, 4);
} else if (strcmp(interface, "zxdg_shell_v6") == 0) {
w->shell = (zxdg_shell_v6 *)wl_registry_bind(
registry, name, &zxdg_shell_v6_interface, 1);
zxdg_shell_v6_add_listener(w->shell, &xdg_shell_listener, w);
} else if (strcmp(interface, "wl_output") == 0) {
wl_output *_output = (wl_output *)wl_registry_bind(
registry, name, &wl_output_interface, 2);
wl_output_add_listener(_output, &output_listener, w);
} else if (strcmp(interface, "xdg_wm_base") == 0) {
w->wm_base = (struct xdg_wm_base *)wl_registry_bind(
registry, name, &xdg_wm_base_interface, 1);
xdg_wm_base_add_listener(w->wm_base, &xdg_wm_base_listener, w);
}
}
......@@ -459,7 +316,8 @@ comp_window_wayland_init(struct comp_window *w)
return false;
}
wl_registry *registry = wl_display_get_registry(w_wayland->display);
struct wl_registry *registry =
wl_display_get_registry(w_wayland->display);
wl_registry_add_listener(registry, &registry_listener, w_wayland);
wl_display_roundtrip(w_wayland->display);
......@@ -469,161 +327,42 @@ comp_window_wayland_init(struct comp_window *w)
w_wayland->surface =
wl_compositor_create_surface(w_wayland->compositor);
if (!w_wayland->shell) {
COMP_ERROR(
w->c,
"Compositor is missing unstable zxdg_shell_v6 support");
if (!w_wayland->wm_base) {
COMP_ERROR(w->c, "Compositor is missing xdg-shell support");
}
w_wayland->xdg_surface =
zxdg_shell_v6_get_xdg_surface(w_wayland->shell, w_wayland->surface);
xdg_wm_base_get_xdg_surface(w_wayland->wm_base, w_wayland->surface);
zxdg_surface_v6_add_listener(w_wayland->xdg_surface,
&xdg_surface_listener, w_wayland);
xdg_surface_add_listener(w_wayland->xdg_surface, &xdg_surface_listener,
w_wayland);
w_wayland->xdg_toplevel =
zxdg_surface_v6_get_toplevel(w_wayland->xdg_surface);
xdg_surface_get_toplevel(w_wayland->xdg_surface);
zxdg_toplevel_v6_add_listener(w_wayland->xdg_toplevel,
&xdg_toplevel_listener, w_wayland);
xdg_toplevel_add_listener(w_wayland->xdg_toplevel,
&xdg_toplevel_listener, w_wayland);
/* Sane defaults */
xdg_toplevel_set_app_id(w_wayland->xdg_toplevel, "openxr");
xdg_toplevel_set_title(w_wayland->xdg_toplevel, "OpenXR application");
wl_surface_commit(w_wayland->surface);
return true;
}
static comp_window_wayland_display *
comp_window_wayland_get_display_from_output(struct comp_window_wayland *w,
wl_output *output)
{
for (int i = 0; i < (int)w->displays.size(); i++) {
if (w->displays[i].output == output)
return &w->displays[i];
}
return nullptr;
}
XRT_MAYBE_UNUSED static void
comp_window_wayland_print_displays(struct comp_window_wayland *w)
{
int i_d = 0;
COMP_DEBUG(w->base.c, "Available displays:");
for (auto d : w->displays) {
COMP_DEBUG(w->base.c, "%d: %s %s [%d, %d] %dx%dmm (%d Modes)",
i_d, d.make.c_str(), d.model.c_str(),
d.position.first, d.position.second,
d.physical_size_mm.first, d.physical_size_mm.second,
(int)d.modes.size());
int i_m = 0;
for (auto m : d.modes) {
COMP_DEBUG(w->base.c, "\t%d: %s", i_m,
mode_to_string(&m).c_str());
i_m++;
}
i_d++;
}
}
static comp_window_wayland_display *
comp_window_wayland_current_display(struct comp_window_wayland *w)
{
return &w->displays[w->base.c->settings.display];
}
static comp_window_wayland_mode *
comp_window_wayland_current_mode(struct comp_window_wayland *w)
{
return &comp_window_wayland_current_display(w)
->modes[w->base.c->settings.mode];
}
static std::string
mode_to_string(comp_window_wayland_mode *m)
{
auto size = std::snprintf(nullptr, 0, "%d x %d @ %.2fHz", m->size.first,
m->size.second, (float)m->refresh / 1000.0);
std::string output(size + 1, '\0');
std::snprintf(&output[0], size, "%d x %d @ %.2fHz", m->size.first,
m->size.second, (float)m->refresh / 1000.0);
return std::string(output);
}
static void
comp_window_wayland_validate_display(struct comp_window_wayland *w)
{
comp_window_wayland_display *d;
if (w->base.c->settings.display < 0)
w->base.c->settings.display = 0;
if (w->base.c->settings.display > (int)w->displays.size()) {
COMP_DEBUG(w->base.c,
"Requested display %d, but only %d displays are "
"available.",
w->base.c->settings.display,
(int)w->displays.size());
w->base.c->settings.display = 0;
d = comp_window_wayland_current_display(w);
COMP_DEBUG(w->base.c, "Selecting '%s %s' instead.",
d->make.c_str(), d->model.c_str());
}
}
static void
validate_mode(struct comp_window_wayland *w)
{
comp_window_wayland_display *d = comp_window_wayland_current_display(w);
if (w->base.c->settings.mode < 0)
w->base.c->settings.mode = 0;
if (w->base.c->settings.mode > (int)d->modes.size()) {
COMP_DEBUG(w->base.c,
"Requested mode %d, but only %d modes"
" are available on display %d.",
w->base.c->settings.mode, (int)d->modes.size(),
w->base.c->settings.display);
w->base.c->settings.mode = 0;
COMP_DEBUG(w->base.c, "Selecting '%s' instead",
mode_to_string(comp_window_wayland_current_mode(w))
.c_str());
}