fteqw/engine/gl/gl_vidwayland.c

836 lines
28 KiB
C

//This is my attempt at wayland support for both opengl and vulkan.
//Note that this is sorely under-tested - I haven't tested vulkan-on-wayland at all as none of the drivers for nvidia support it.
//in no particular order...
//TODO: leaks on shutdown
//TODO: hardware cursors
//TODO: mouse grabs
//TODO: window decorations...
//TODO: kb autorepeat
//TODO: generic keymap (and not just UK)
//TODO: system clipboard
//TODO: drag+drop
#include "bothdefs.h"
#ifdef WAYLANDQUAKE
#include <wayland-client.h>
#include <wayland-egl.h>
#include <linux/input.h> //this is shite.
#include "quakedef.h"
#if defined(GLQUAKE) && defined(USE_EGL)
#include "gl_draw.h"
#include "gl_videgl.h"
#endif
#if defined(VKQUAKE)
#include "vk/vkrenderer.h"
#endif
#if WAYLAND_VERSION_MAJOR < 1
#error "wayland headers are too old"
#endif
#include "glquake.h"
#include "shader.h"
#if 1
static struct wl_display *(*pwl_display_connect)(const char *name);
static int (*pwl_display_dispatch)(struct wl_display *display);
static int (*pwl_display_dispatch_pending)(struct wl_display *display);
static int (*pwl_display_roundtrip)(struct wl_display *display);
static struct wl_proxy *(*pwl_proxy_marshal_constructor)(struct wl_proxy *proxy, uint32_t opcode, const struct wl_interface *interface, ...);
static struct wl_proxy *(*pwl_proxy_marshal_constructor_versioned)(struct wl_proxy *proxy, uint32_t opcode, const struct wl_interface *interface, uint32_t version, ...);
static void (*pwl_proxy_destroy)(struct wl_proxy *proxy);
static void (*pwl_proxy_marshal)(struct wl_proxy *p, uint32_t opcode, ...);
static int (*pwl_proxy_add_listener)(struct wl_proxy *proxy, void (**implementation)(void), void *data);
static const struct wl_interface *pwl_keyboard_interface;
static const struct wl_interface *pwl_pointer_interface;
static const struct wl_interface *pwl_compositor_interface;
static const struct wl_interface *pwl_region_interface;
static const struct wl_interface *pwl_surface_interface;
static const struct wl_interface *pwl_shell_surface_interface;
static const struct wl_interface *pwl_shell_interface;
static const struct wl_interface *pwl_seat_interface;
static const struct wl_interface *pwl_registry_interface;
static dllfunction_t waylandexports_wl[] =
{
{(void**)&pwl_display_connect, "wl_display_connect"},
{(void**)&pwl_display_dispatch, "wl_display_dispatch"},
{(void**)&pwl_display_dispatch_pending, "wl_display_dispatch_pending"},
{(void**)&pwl_display_roundtrip, "wl_display_roundtrip"},
{(void**)&pwl_proxy_marshal_constructor, "wl_proxy_marshal_constructor"},
{(void**)&pwl_proxy_marshal_constructor_versioned,"wl_proxy_marshal_constructor_versioned"},
{(void**)&pwl_proxy_destroy, "wl_proxy_destroy"},
{(void**)&pwl_proxy_marshal, "wl_proxy_marshal"},
{(void**)&pwl_proxy_add_listener, "wl_proxy_add_listener"},
{(void**)&pwl_keyboard_interface, "wl_keyboard_interface"},
{(void**)&pwl_pointer_interface, "wl_pointer_interface"},
{(void**)&pwl_compositor_interface, "wl_compositor_interface"},
{(void**)&pwl_region_interface, "wl_region_interface"},
{(void**)&pwl_surface_interface, "wl_surface_interface"},
{(void**)&pwl_shell_surface_interface, "wl_shell_surface_interface"},
{(void**)&pwl_shell_interface, "wl_shell_interface"},
{(void**)&pwl_seat_interface, "wl_seat_interface"},
{(void**)&pwl_registry_interface, "wl_registry_interface"},
{NULL, NULL}
};
static dllhandle_t *lib_wayland_wl;
static qboolean WL_InitLibrary(void)
{
lib_wayland_wl = Sys_LoadLibrary("libwayland-client.so.0", waylandexports_wl);
if (!lib_wayland_wl)
return false;
return true;
}
#if defined(GLQUAKE) && defined(USE_EGL)
static struct wl_egl_window *(*pwl_egl_window_create)(struct wl_surface *surface, int width, int height);
static void (*pwl_egl_window_destroy)(struct wl_egl_window *egl_window);
static void (*pwl_egl_window_resize)(struct wl_egl_window *egl_window, int width, int height, int dx, int dy);
//static void (*pwl_egl_window_get_attached_size(struct wl_egl_window *egl_window, int *width, int *height);
static dllfunction_t waylandexports_egl[] =
{
{(void**)&pwl_egl_window_create, "wl_egl_window_create"},
{(void**)&pwl_egl_window_destroy, "wl_egl_window_destroy"},
{(void**)&pwl_egl_window_resize, "wl_egl_window_resize"},
// {(void**)&pwl_egl_window_get_attached_size, "wl_egl_window_get_attached_size"},
{NULL, NULL}
};
static dllhandle_t *lib_wayland_egl;
#endif
//I hate wayland.
static inline struct wl_region *pwl_compositor_create_region(struct wl_compositor *wl_compositor) {return (struct wl_region*)(struct wl_proxy *) pwl_proxy_marshal_constructor((struct wl_proxy *) wl_compositor, WL_COMPOSITOR_CREATE_REGION, pwl_region_interface, NULL);}
static inline struct wl_surface *pwl_compositor_create_surface(struct wl_compositor *wl_compositor) {return (struct wl_surface *)(struct wl_proxy *) pwl_proxy_marshal_constructor((struct wl_proxy *) wl_compositor, WL_COMPOSITOR_CREATE_SURFACE, pwl_surface_interface, NULL);}
static inline void pwl_surface_set_opaque_region(struct wl_surface *wl_surface, struct wl_region *region) {pwl_proxy_marshal((struct wl_proxy *) wl_surface, WL_SURFACE_SET_OPAQUE_REGION, region);}
static inline void pwl_region_add(struct wl_region *wl_region, int32_t x, int32_t y, int32_t width, int32_t height) {pwl_proxy_marshal((struct wl_proxy *) wl_region, WL_REGION_ADD, x, y, width, height);}
static inline struct wl_shell_surface *pwl_shell_get_shell_surface(struct wl_shell *wl_shell, struct wl_surface *surface) {return (struct wl_shell_surface *)(struct wl_proxy *) pwl_proxy_marshal_constructor((struct wl_proxy *) wl_shell, WL_SHELL_GET_SHELL_SURFACE, pwl_shell_surface_interface, NULL, surface);}
static inline void pwl_shell_surface_set_toplevel(struct wl_shell_surface *wl_shell_surface) {pwl_proxy_marshal((struct wl_proxy *) wl_shell_surface, WL_SHELL_SURFACE_SET_TOPLEVEL);}
static inline void pwl_shell_surface_set_fullscreen(struct wl_shell_surface *wl_shell_surface, uint32_t method, uint32_t framerate, struct wl_output *output) {pwl_proxy_marshal((struct wl_proxy *) wl_shell_surface, WL_SHELL_SURFACE_SET_FULLSCREEN, method, framerate, output);}
static inline int pwl_shell_surface_add_listener(struct wl_shell_surface *wl_shell_surface, const struct wl_shell_surface_listener *listener, void *data) {return pwl_proxy_add_listener((struct wl_proxy *) wl_shell_surface, (void (**)(void)) listener, data);}
static inline void pwl_shell_surface_pong(struct wl_shell_surface *wl_shell_surface, uint32_t serial) {pwl_proxy_marshal((struct wl_proxy *) wl_shell_surface, WL_SHELL_SURFACE_PONG, serial);}
static inline void pwl_shell_surface_set_title(struct wl_shell_surface *wl_shell_surface, const char *title) {pwl_proxy_marshal((struct wl_proxy *) wl_shell_surface, WL_SHELL_SURFACE_SET_TITLE, title);}
static inline struct wl_registry *pwl_display_get_registry(struct wl_display *wl_display) {return (struct wl_registry *)pwl_proxy_marshal_constructor((struct wl_proxy *) wl_display, WL_DISPLAY_GET_REGISTRY, pwl_registry_interface, NULL);}
static inline void *pwl_registry_bind(struct wl_registry *wl_registry, uint32_t name, const struct wl_interface *interface, uint32_t version) {return (void*)pwl_proxy_marshal_constructor_versioned((struct wl_proxy *) wl_registry, WL_REGISTRY_BIND, interface, version, name, interface->name, version, NULL);}
static inline int pwl_registry_add_listener(struct wl_registry *wl_registry, const struct wl_registry_listener *listener, void *data) {return pwl_proxy_add_listener((struct wl_proxy *) wl_registry, (void (**)(void)) listener, data);}
static inline void pwl_keyboard_destroy(struct wl_keyboard *wl_keyboard) {pwl_proxy_destroy((struct wl_proxy *) wl_keyboard);}
static inline int pwl_keyboard_add_listener(struct wl_keyboard *wl_keyboard, const struct wl_keyboard_listener *listener, void *data) {return pwl_proxy_add_listener((struct wl_proxy *) wl_keyboard, (void (**)(void)) listener, data);}
static inline void pwl_pointer_destroy(struct wl_pointer *wl_pointer) {pwl_proxy_destroy((struct wl_proxy *) wl_pointer);}
static inline int pwl_pointer_add_listener(struct wl_pointer *wl_pointer, const struct wl_pointer_listener *listener, void *data) {return pwl_proxy_add_listener((struct wl_proxy *) wl_pointer, (void (**)(void)) listener, data);}
static inline struct wl_pointer *pwl_seat_get_pointer(struct wl_seat *wl_seat) {return (struct wl_pointer *)(struct wl_proxy *) pwl_proxy_marshal_constructor((struct wl_proxy *) wl_seat, WL_SEAT_GET_POINTER, pwl_pointer_interface, NULL);}
static inline struct wl_keyboard *pwl_seat_get_keyboard(struct wl_seat *wl_seat) {return (struct wl_keyboard *)(struct wl_proxy *) pwl_proxy_marshal_constructor((struct wl_proxy *) wl_seat, WL_SEAT_GET_KEYBOARD, pwl_keyboard_interface, NULL);}
static inline int pwl_seat_add_listener(struct wl_seat *wl_seat, const struct wl_seat_listener *listener, void *data) {return pwl_proxy_add_listener((struct wl_proxy *) wl_seat, (void (**)(void)) listener, data);}
#else
#define pwl_keyboard_interface &wl_keyboard_interface
#define pwl_pointer_interface &wl_pointer_interface
#define pwl_compositor_interface &wl_compositor_interface
#define pwl_registry_interface &wl_registry_interface
#define pwl_region_interface &wl_region_interface
#define pwl_surface_interface &wl_surface_interface
#define pwl_shell_surface_interface &wl_shell_surface_interface
#define pwl_shell_interface &wl_shell_interface
#define pwl_seat_interface &wl_seat_interface
#endif
static const struct wl_interface *pzwp_relative_pointer_v1_interface;
static struct wdisplay_s
{
//display stuff
struct wl_display *display;
struct wl_registry *registry;
struct wl_compositor *compositor;
struct wl_shell *shell;
//seat stuff
void *pointer;
void *keyboard;
void *seat;
qboolean absmouse;
struct zwp_relative_pointer_manager_v1 *relative_pointer_manager;
struct zwp_relative_pointer_v1 *relative_pointer;
//window stuff
#if defined(GLQUAKE) && defined(USE_EGL)
struct wl_egl_window *enwindow;
#endif
struct wl_surface *surface;
struct wl_shell_surface *ssurface;
} w;
static void WL_shell_handle_ping(void *data, struct wl_shell_surface *shell_surface, uint32_t serial)
{
pwl_shell_surface_pong(shell_surface, serial);
}
static void WL_shell_handle_configure(void *data, struct wl_shell_surface *shell_surface, uint32_t edges, int32_t width, int32_t height)
{
#if defined(GLQUAKE) && defined(USE_EGL)
if (w.enwindow)
pwl_egl_window_resize(w.enwindow, width, height, 0, 0);
#endif
vid.pixelwidth = width;
vid.pixelheight = height;
}
static void WL_shell_handle_popup_done(void *data, struct wl_shell_surface *shell_surface)
{
}
static const struct wl_shell_surface_listener shell_surface_listener =
{
WL_shell_handle_ping,
WL_shell_handle_configure,
WL_shell_handle_popup_done
};
//qkeys are ascii-compatible for the most part.
static qbyte waylandinputsucksbighairydonkeyballs[] =
{
0, K_ESCAPE,'1','2','3','4','5','6', //0x
'7','8','9','0','-','=',K_BACKSPACE,K_TAB,
'q','w','e','r','t','y','u','i', //1x
'o','p','[',']',K_ENTER,K_LCTRL,'a', 's',
'd','f','g','h','j','k','l',';', //2x
'\'','`',K_LSHIFT,'#','z','x','c','v',
'b','n','m',',','.','/',K_RSHIFT,K_KP_STAR, //3x
K_LALT,' ',K_CAPSLOCK,K_F1,K_F2,K_F3,K_F4,K_F5,
K_F6,K_F7,K_F8,K_F9,K_F10,K_KP_NUMLOCK,K_SCRLCK,K_KP_HOME,//4x
K_KP_UPARROW,K_KP_PGUP,K_KP_MINUS,K_KP_LEFTARROW,K_KP_5,K_KP_RIGHTARROW,K_KP_PLUS,K_KP_END,
K_KP_DOWNARROW,K_KP_PGDN,K_KP_INS,K_KP_DEL,0,0,'\\',K_F11, //5x
K_F12,0,0,0,0,0,0,0,
K_KP_ENTER,0,K_KP_SLASH,0,K_RALT,0,K_HOME,K_UPARROW, //6x
K_PGUP,K_LEFTARROW,K_RIGHTARROW,K_END,K_DOWNARROW,K_PGDN,K_INS,K_DEL,
0,0,0,0,0,0,0,K_PAUSE, //7x
0,0,0,0,0,K_LWIN,K_RWIN,K_APP
};
static qbyte waylandinputsucksbighairydonkeyballsshift[] =
{
0, K_ESCAPE,'!','\"','3','$','%','^', //0x
'&','*','(',')','_','+',K_BACKSPACE,K_TAB,
'Q','W','E','R','T','Y','U','I', //1x
'O','P','{','}',K_ENTER,K_LCTRL,'A', 'S',
'D','F','G','H','J','K','L',':', //2x
'@','`',K_LSHIFT,'~','Z','X','C','V',
'B','N','M','<','>','?',K_RSHIFT,K_KP_STAR, //3x
K_LALT,' ',K_CAPSLOCK,K_F1,K_F2,K_F3,K_F4,K_F5,
K_F6,K_F7,K_F8,K_F9,K_F10,K_KP_NUMLOCK,K_SCRLCK,K_KP_HOME,//4x
K_KP_UPARROW,K_KP_PGUP,K_KP_MINUS,K_KP_LEFTARROW,K_KP_5,K_KP_RIGHTARROW,K_KP_PLUS,K_KP_END,
K_KP_DOWNARROW,K_KP_PGDN,K_KP_INS,K_KP_DEL,0,0,'|',K_F11, //5x
K_F12,0,0,0,0,0,0,0,
K_KP_ENTER,0,K_KP_SLASH,0,K_RALT,0,K_HOME,K_UPARROW, //6x
K_PGUP,K_LEFTARROW,K_RIGHTARROW,K_END,K_DOWNARROW,K_PGDN,K_INS,K_DEL,
0,0,0,0,0,0,0,K_PAUSE, //7x
0,0,0,0,0,K_LWIN,K_RWIN,K_APP
};
static void WL_pointer_handle_enter(void *data, struct wl_pointer *pointer, uint32_t serial, struct wl_surface *surface, wl_fixed_t sx, wl_fixed_t sy)
{
/*
struct display *display = data;
struct wl_buffer *buffer;
struct wl_cursor *cursor = display->default_cursor;
struct wl_cursor_image *image;
if (display->window->fullscreen)
pwl_pointer_set_cursor(pointer, serial, NULL, 0, 0);
else if (cursor) {
image = display->default_cursor->images[0];
buffer = pwl_cursor_image_get_buffer(image);
pwl_pointer_set_cursor(pointer, serial,
display->cursor_surface,
image->hotspot_x,
image->hotspot_y);
pwl_surface_attach(display->cursor_surface, buffer, 0, 0);
pwl_surface_damage(display->cursor_surface, 0, 0,
image->width, image->height);
pwl_surface_commit(display->cursor_surface);
}
*/
}
static void WL_pointer_handle_leave(void *data, struct wl_pointer *pointer, uint32_t serial, struct wl_surface *surface)
{
}
static void WL_pointer_handle_motion(void *data, struct wl_pointer *pointer, uint32_t time, wl_fixed_t sx, wl_fixed_t sy)
{
//wayland is shite shite shite.
//1.4 still has no relative mouse motion.
if (w.absmouse)
IN_MouseMove(0, true, wl_fixed_to_double(sx), wl_fixed_to_double(sy), 0, 0);
}
static void WL_pointer_handle_button(void *data, struct wl_pointer *wl_pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t state)
{
// struct wdisplay *display = data;
int qkey;
switch(button)
{
default:
return; //blurgh.
case BTN_LEFT:
qkey = K_MOUSE1;
break;
case BTN_RIGHT:
qkey = K_MOUSE2;
break;
case BTN_MIDDLE:
qkey = K_MOUSE3;
break;
case BTN_SIDE:
qkey = K_MOUSE4;
break;
case BTN_EXTRA:
qkey = K_MOUSE5;
break;
case BTN_FORWARD:
qkey = K_MOUSE6;
break;
case BTN_BACK:
qkey = K_MOUSE7;
break;
case BTN_TASK:
qkey = K_MOUSE8;
break;
}
IN_KeyEvent(0, !!state, qkey, 0);
// pwl_shell_surface_move(display->window->shell_surface, display->seat, serial);
}
static void WL_pointer_handle_axis(void *data, struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis, wl_fixed_t value)
{
if (value < 0)
{
IN_KeyEvent(0, 1, K_MWHEELUP, 0);
IN_KeyEvent(0, 0, K_MWHEELUP, 0);
}
else
{
IN_KeyEvent(0, 1, K_MWHEELDOWN, 0);
IN_KeyEvent(0, 0, K_MWHEELDOWN, 0);
}
}
static const struct wl_pointer_listener pointer_listener =
{
WL_pointer_handle_enter,
WL_pointer_handle_leave,
WL_pointer_handle_motion,
WL_pointer_handle_button,
WL_pointer_handle_axis,
};
struct zwp_relative_pointer_v1;
#define ZWP_RELATIVE_POINTER_MANAGER_V1_DESTROY 0
#define ZWP_RELATIVE_POINTER_MANAGER_V1_GET_RELATIVE_POINTER 1
#define ZWP_RELATIVE_POINTER_V1_DESTROY 0
static void WL_pointer_handle_delta(void *data, struct zwp_relative_pointer_v1 *pointer, uint32_t time_hi, uint32_t time_lo, wl_fixed_t dx_w, wl_fixed_t dy_w, wl_fixed_t dx_raw_w, wl_fixed_t dy_raw_w)
{
if (!w.absmouse)
IN_MouseMove(0, false, wl_fixed_to_double(dx_raw_w), wl_fixed_to_double(dy_raw_w), 0, 0);
}
struct zwp_relative_pointer_v1_listener
{
void (*delta)(void *data, struct zwp_relative_pointer_v1 *pointer, uint32_t time_hi, uint32_t time_lo, wl_fixed_t dx_w, wl_fixed_t dy_w, wl_fixed_t dx_raw_w, wl_fixed_t dy_raw_w);
};
static const struct zwp_relative_pointer_v1_listener relative_pointer_listener =
{
WL_pointer_handle_delta,
};
static void WL_BindRelativePointerManager(struct wl_registry *registry, uint32_t id)
{ /*oh hey, I wrote lots of code! pay me more! fuck that shit.*/
static const struct wl_interface *types[8];
static const struct wl_message zwp_relative_pointer_manager_v1_requests[] = {
{ "destroy", "", types + 0 },
{ "get_relative_pointer", "no", types + 6 },
};
static const struct wl_interface zwp_relative_pointer_manager_v1_interface = {
"zwp_relative_pointer_manager_v1", 1,
2, zwp_relative_pointer_manager_v1_requests,
0, NULL,
};
static const struct wl_message zwp_relative_pointer_v1_requests[] = {
{ "destroy", "", types + 0 },
};
static const struct wl_message zwp_relative_pointer_v1_events[] = {
{ "relative_motion", "uuffff", types + 0 },
};
static const struct wl_interface zwp_relative_pointer_v1_interface = {
"zwp_relative_pointer_v1", 1,
1, zwp_relative_pointer_v1_requests,
1, zwp_relative_pointer_v1_events,
};
//fix up types...
types[6] = &zwp_relative_pointer_v1_interface;
types[7] = pwl_pointer_interface;
pzwp_relative_pointer_v1_interface = &zwp_relative_pointer_v1_interface;
w.relative_pointer_manager = pwl_registry_bind(registry, id, &zwp_relative_pointer_manager_v1_interface, 1);
}
/*
struct zwp_locked_pointer_v1;
static void WL_locked_pointer_locked(void *data, struct zwp_locked_pointer_v1 *locked_pointer)
{
}
static void WL_locked_pointer_unlocked(void *data, struct zwp_locked_pointer_v1 *locked_pointer)
{
}
struct zwp_locked_pointer_v1_listener
{
void (*pointer_locked)(void *data, struct zwp_locked_pointer_v1 *locked_pointer);
void (*pointer_unlocked)(void *data, struct zwp_locked_pointer_v1 *locked_pointer);
};
static const struct zwp_locked_pointer_v1_listener locked_pointer_listener =
{
WL_locked_pointer_locked,
WL_locked_pointer_unlocked,
};
*/
static void WL_keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard, uint32_t format, int fd, uint32_t size)
{
}
static void WL_keyboard_handle_enter(void *data, struct wl_keyboard *keyboard, uint32_t serial, struct wl_surface *surface, struct wl_array *keys)
{
vid.activeapp = true;
}
static void WL_keyboard_handle_leave(void *data, struct wl_keyboard *keyboard, uint32_t serial, struct wl_surface *surface)
{
vid.activeapp = false;
}
static void WL_keyboard_handle_key(void *data, struct wl_keyboard *keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state)
{
extern int shift_down;
// struct display *d = data;
uint32_t qkey;
uint32_t ukey;
//FIXME: this stuff is fucked, especially the ukey stuff.
if (key < sizeof(waylandinputsucksbighairydonkeyballs)/sizeof(waylandinputsucksbighairydonkeyballs[0]))
{
qkey = waylandinputsucksbighairydonkeyballs[key];
if (shift_down)
ukey = waylandinputsucksbighairydonkeyballsshift[key];
else
ukey = waylandinputsucksbighairydonkeyballs[key];
}
else
ukey = qkey = 0;
if (ukey < ' ' || ukey > 127)
ukey = 0;
if (state)
IN_KeyEvent(0, 1, qkey, ukey);
else
IN_KeyEvent(0, 0, qkey, 0);
}
static void WL_keyboard_handle_modifiers(void *data, struct wl_keyboard *keyboard, uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, uint32_t group)
{
}
static const struct wl_keyboard_listener keyboard_listener =
{
WL_keyboard_handle_keymap,
WL_keyboard_handle_enter,
WL_keyboard_handle_leave,
WL_keyboard_handle_key,
WL_keyboard_handle_modifiers
};
static void WL_seat_handle_capabilities(void *data, struct wl_seat *seat, enum wl_seat_capability caps)
{
struct wdisplay_s *s = data;
if ((caps & WL_SEAT_CAPABILITY_POINTER) && !s->pointer)
{
s->pointer = pwl_seat_get_pointer(seat);
pwl_pointer_add_listener(s->pointer, &pointer_listener, s);
if (w.relative_pointer_manager)
{ //and try and get relative pointer events too. so much fucking boilerplate.
w.relative_pointer = (struct zwp_relative_pointer_v1 *)pwl_proxy_marshal_constructor((struct wl_proxy *) w.relative_pointer_manager, ZWP_RELATIVE_POINTER_MANAGER_V1_GET_RELATIVE_POINTER, pzwp_relative_pointer_v1_interface, NULL, w.pointer);
pwl_proxy_add_listener((struct wl_proxy *) w.relative_pointer, (void (**)(void)) &relative_pointer_listener, &w);
}
}
else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && s->pointer)
{
pwl_pointer_destroy(s->pointer);
s->pointer = NULL;
if (w.relative_pointer)
{
pwl_proxy_marshal((struct wl_proxy *) w.relative_pointer, ZWP_RELATIVE_POINTER_V1_DESTROY);
pwl_proxy_destroy((struct wl_proxy *) w.relative_pointer);
}
}
if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !s->keyboard)
{
s->keyboard = pwl_seat_get_keyboard(seat);
pwl_keyboard_add_listener(s->keyboard, &keyboard_listener, s);
}
else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && s->keyboard)
{
pwl_keyboard_destroy(s->keyboard);
s->keyboard = NULL;
}
}
static const struct wl_seat_listener seat_listener =
{
WL_seat_handle_capabilities
};
static void WL_handle_global(void *data, struct wl_registry *registry, uint32_t id, const char *interface, uint32_t version)
{
struct wdisplay_s *d = data;
//Sys_Printf("Interface %s id %u\n", interface, id);
if (strcmp(interface, "wl_compositor") == 0)
d->compositor = pwl_registry_bind(registry, id, pwl_compositor_interface, 1);
else if (strcmp(interface, "wl_shell") == 0)
d->shell = pwl_registry_bind(registry, id, pwl_shell_interface, 1);
else if (strcmp(interface, "wl_seat") == 0 && !d->seat)
{
d->seat = pwl_registry_bind(registry, id, pwl_seat_interface, 1);
pwl_seat_add_listener(d->seat, &seat_listener, d);
}
else if (strcmp(interface, "zwp_relative_pointer_manager_v1") == 0)
WL_BindRelativePointerManager(registry, id);
// else if (strcmp(interface, "zwp_pointer_constraints_v1") == 0)
// d->shell = pwl_registry_bind(registry, id, pwl_shell_interface, 1);
/* else if (!strcmp(interface, "input_device"))
display_add_input(id);
*/
}
static const struct wl_registry_listener WL_registry_listener = {
WL_handle_global
};
static void WL_SwapBuffers(void)
{
TRACE(("WL_SwapBuffers\n"));
switch(qrenderer)
{
#if defined(GLQUAKE) && defined(USE_EGL)
case QR_OPENGL:
EGL_SwapBuffers();
//wl_surface_damage(w.surface, 0, 0, vid.pixelwidth, vid.pixelheight);
pwl_display_dispatch_pending(w.display);
break;
#endif
case QR_VULKAN: //the vulkan stuff handles this itself. FIXME: are we still receiving inputs? no idea!
default:
break;
}
w.absmouse = Key_MouseShouldBeFree() || !w.relative_pointer;
}
#ifdef VKQUAKE
static qboolean WLVK_SetupSurface()
{
VkWaylandSurfaceCreateInfoKHR inf = {VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR};
inf.flags = 0;
inf.display = w.display;
inf.surface = w.surface;
if (VK_SUCCESS == vkCreateWaylandSurfaceKHR(vk.instance, &inf, vkallocationcb, &vk.surface))
return true;
return false;
}
#endif
static qboolean WL_Init (rendererstate_t *info, unsigned char *palette)
{
if (!WL_InitLibrary())
{
Con_Printf("couldn't load wayland client libraries\n");
return false;
}
switch(qrenderer)
{
#if defined(GLQUAKE) && defined(USE_EGL)
case QR_OPENGL:
lib_wayland_egl = Sys_LoadLibrary("libwayland-egl.so.1", waylandexports_egl);
if (!lib_wayland_egl)
{
Con_Printf("couldn't load libwayland-egl.so.1 library\n");
return false;
}
// setenv("EGL_PLATFORM", "wayland", 1); //if this actually matters then we're kinda screwed
if (!EGL_LoadLibrary(info->subrenderer))
{
Con_Printf("couldn't load EGL library\n");
return false;
}
break;
#endif
#ifdef VKQUAKE
case QR_VULKAN:
#ifdef VK_NO_PROTOTYPES
{ //vulkan requires that vkGetInstanceProcAddr is set in advance.
dllfunction_t func[] =
{
{(void*)&vkGetInstanceProcAddr, "vkGetInstanceProcAddr"},
{NULL, NULL}
};
if (!Sys_LoadLibrary("libvulkan.so.1", func))
{
if (!Sys_LoadLibrary("libvulkan.so", func))
{
Con_Printf("Couldn't intialise libvulkan.so\nvulkan loader is not installed\n");
return false;
}
}
}
#endif
break;
#endif
default:
return false; //not supported dude...
}
memset(&w, 0, sizeof(w));
w.display = pwl_display_connect(NULL);
if (!w.display)
{
Con_Printf("couldn't connect to wayland server\n");
return false;
}
w.registry = pwl_display_get_registry(w.display);
pwl_registry_add_listener(w.registry, &WL_registry_listener, &w); //w.compositor =
pwl_display_dispatch(w.display);
pwl_display_roundtrip(w.display);
if (!w.compositor)
{
Con_Printf("no compositor running, apparently\n");
return false;
}
w.surface = pwl_compositor_create_surface(w.compositor);
if (!w.surface)
{
Con_Printf("no compositor running, apparently\n");
return false;
}
w.ssurface = pwl_shell_get_shell_surface(w.shell, w.surface);
if (info->fullscreen)
pwl_shell_surface_set_fullscreen(w.ssurface, WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT, 60, NULL);
else
pwl_shell_surface_set_toplevel(w.ssurface);
{
struct wl_region *region = pwl_compositor_create_region(w.compositor);
pwl_region_add(region, 0, 0, info->width, info->height);
pwl_surface_set_opaque_region(w.surface, region);
//FIXME: leaks region?
}
pwl_shell_surface_add_listener(w.ssurface, &shell_surface_listener, &w);
vid.pixelwidth = info->width;
vid.pixelheight = info->height;
vid.activeapp = true;
//window_set_keyboard_focus_handler(window, WL_handler_keyfocus);
//window_set_resize_handler(w.surface, WL_handler_resize);
switch(qrenderer)
{
default:
return false;
#ifdef VKQUAKE
case QR_VULKAN:
{
const char *extnames[] = {VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME, NULL};
return VK_Init(info, extnames, WLVK_SetupSurface, NULL);
}
break;
#endif
#if defined(GLQUAKE) && defined(USE_EGL)
case QR_OPENGL:
w.enwindow = pwl_egl_window_create(w.surface, info->width, info->height);
if (!EGL_Init(info, palette, EGL_PLATFORM_WAYLAND_KHR, w.enwindow, w.display, (EGLNativeWindowType)w.enwindow, (EGLNativeDisplayType)w.display))
{
Con_Printf("couldn't initialise EGL context\n");
return false;
}
pwl_display_dispatch_pending(w.display);
return GL_Init(info, &EGL_Proc);
break;
#endif
}
return true;
}
static void WL_DeInit(void)
{
#if defined(GLQUAKE) && defined(USE_EGL)
EGL_Shutdown();
if (w.enwindow)
pwl_egl_window_destroy(w.enwindow);
#endif
}
static qboolean WL_ApplyGammaRamps(unsigned int gammarampsize, unsigned short *ramps)
{
//not supported
return false;
}
static void WL_SetCaption(const char *text)
{
pwl_shell_surface_set_title(w.ssurface, text);
}
static int WL_GetPriority(void)
{
char *dpyname = getenv("WAYLAND_DISPLAY");
if (dpyname && *dpyname)
return 2; //something above X11.
return 0; //default.
}
#if defined(GLQUAKE) && defined(USE_EGL)
rendererinfo_t rendererinfo_wayland_gl =
{
"OpenGL (Wayland)",
{
"wlgl",
"wayland",
},
QR_OPENGL,
GLDraw_Init,
GLDraw_DeInit,
GL_UpdateFiltering,
GL_LoadTextureMips,
GL_DestroyTexture,
GLR_Init,
GLR_DeInit,
GLR_RenderView,
WL_Init,
WL_DeInit,
WL_SwapBuffers,
WL_ApplyGammaRamps,
NULL, //CreateCursor
NULL, //SetCursor
NULL, //DestroyCursor
WL_SetCaption, //setcaption
GLVID_GetRGBInfo,
GLSCR_UpdateScreen,
GLBE_SelectMode,
GLBE_DrawMesh_List,
GLBE_DrawMesh_Single,
GLBE_SubmitBatch,
GLBE_GetTempBatch,
GLBE_DrawWorld,
GLBE_Init,
GLBE_GenBrushModelVBO,
GLBE_ClearVBO,
GLBE_UploadAllLightmaps,
GLBE_SelectEntity,
GLBE_SelectDLight,
GLBE_Scissor,
GLBE_LightCullModel,
GLBE_VBO_Begin,
GLBE_VBO_Data,
GLBE_VBO_Finish,
GLBE_VBO_Destroy,
GLBE_RenderToTextureUpdate2d,
"",
WL_GetPriority
};
#endif
#ifdef VKQUAKE
rendererinfo_t rendererinfo_wayland_vk =
{
"Vulkan (Wayland)",
{
"wlvk",
"vk",
"vulkan",
"wayland"
},
QR_VULKAN,
VK_Draw_Init,
VK_Draw_Shutdown,
VK_UpdateFiltering,
VK_LoadTextureMips,
VK_DestroyTexture,
VK_R_Init,
VK_R_DeInit,
VK_R_RenderView,
WL_Init,
WL_DeInit,
WL_SwapBuffers,
WL_ApplyGammaRamps,
NULL,
NULL,
NULL,
WL_SetCaption, //setcaption
VKVID_GetRGBInfo,
VK_SCR_UpdateScreen,
VKBE_SelectMode,
VKBE_DrawMesh_List,
VKBE_DrawMesh_Single,
VKBE_SubmitBatch,
VKBE_GetTempBatch,
VKBE_DrawWorld,
VKBE_Init,
VKBE_GenBrushModelVBO,
VKBE_ClearVBO,
VKBE_UploadAllLightmaps,
VKBE_SelectEntity,
VKBE_SelectDLight,
VKBE_Scissor,
VKBE_LightCullModel,
VKBE_VBO_Begin,
VKBE_VBO_Data,
VKBE_VBO_Finish,
VKBE_VBO_Destroy,
VKBE_RenderToTextureUpdate2d,
"",
WL_GetPriority
};
#endif
#endif