1055 lines
31 KiB
C
1055 lines
31 KiB
C
#include <Mw/Milsko.h>
|
|
|
|
#include <assert.h>
|
|
#include <fcntl.h>
|
|
#include <errno.h>
|
|
#include <linux/input-event-codes.h>
|
|
#include <poll.h>
|
|
#include <signal.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <assert.h>
|
|
#include <wayland-client-protocol.h>
|
|
|
|
#include "../../external/stb_ds.h"
|
|
#include "Mw/BaseTypes.h"
|
|
#include "Mw/LowLevel.h"
|
|
|
|
#include <sys/mman.h>
|
|
#include <wayland-egl-core.h>
|
|
#include <wayland-util.h>
|
|
|
|
/* TODO: find out what FreeBSD and such wants us to include */
|
|
#include <linux/input.h>
|
|
|
|
PFNGLGENFRAMEBUFFERSPROC glGenFramebuffers;
|
|
PFNGLFRAMEBUFFERTEXTURE2DPROC glFramebufferTexture2D;
|
|
PFNGLGENRENDERBUFFERSPROC glGenRenderbuffers;
|
|
PFNGLFRAMEBUFFERRENDERBUFFERPROC glFramebufferRenderbuffer;
|
|
PFNGLBINDRENDERBUFFERPROC glBindRenderbuffer;
|
|
PFNGLBINDFRAMEBUFFERPROC glBindFramebuffer;
|
|
PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC glCheckFramebufferStatus;
|
|
|
|
/*
|
|
callback todo:
|
|
void (*up)(MwLL handle, void* data);
|
|
void (*down)(MwLL handle, void* data);
|
|
void (*key)(MwLL handle, void* data);
|
|
void (*key_released)(MwLL handle, void* data);
|
|
void (*focus_in)(MwLL handle, void* data);
|
|
void (*focus_out)(MwLL handle, void* data);
|
|
*/
|
|
|
|
#define WAYLAND_GET_INTERFACE(r, inter) shget(r.wl_protocol_map, inter##_interface.name)
|
|
|
|
static void new_protocol(void* data, struct wl_registry* registry,
|
|
MwU32 name, const char* interface,
|
|
MwU32 version) {
|
|
struct _MwLLWayland* wayland = data;
|
|
|
|
wl_setup_func* func = shget(wayland->wl_protocol_setup_map, interface);
|
|
if(func != NULL) {
|
|
// printf("registering interface %s\n", interface);
|
|
shput(wayland->wl_protocol_map, interface, func(name, data));
|
|
} else {
|
|
// printf("unknown interface %s\n", interface);
|
|
}
|
|
};
|
|
|
|
static void protocol_removed(void* data,
|
|
struct wl_registry* registry,
|
|
MwU32 name) {
|
|
|
|
};
|
|
|
|
static void pointer_enter(void* data, struct wl_pointer* wl_pointer, uint32_t serial,
|
|
struct wl_surface* surface, wl_fixed_t surface_x,
|
|
wl_fixed_t surface_y) {};
|
|
static void pointer_leave(void* data, struct wl_pointer* wl_pointer, uint32_t serial,
|
|
struct wl_surface* surface) {};
|
|
static void pointer_motion(void* data, struct wl_pointer* wl_pointer, uint32_t time,
|
|
wl_fixed_t surface_x, wl_fixed_t surface_y) {
|
|
MwLL self = data;
|
|
MwLLMouse p;
|
|
|
|
self->wayland.cur_mouse_pos.x = wl_fixed_to_double(surface_x);
|
|
self->wayland.cur_mouse_pos.y = wl_fixed_to_double(surface_y);
|
|
p.point = self->wayland.cur_mouse_pos;
|
|
MwLLDispatch(self, move, &p);
|
|
};
|
|
static void pointer_button(void* data, struct wl_pointer* wl_pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t state) {
|
|
MwLL self = data;
|
|
MwLLMouse p;
|
|
p.point = self->wayland.cur_mouse_pos;
|
|
if(p.point.x > self->wayland.x && p.point.x < self->wayland.x + self->wayland.ww && p.point.y > self->wayland.y && p.point.y < self->wayland.y + self->wayland.wh) {
|
|
switch(button) {
|
|
case BTN_LEFT:
|
|
p.button = MwLLMouseLeft;
|
|
break;
|
|
case BTN_MIDDLE:
|
|
p.button = MwLLMouseMiddle;
|
|
break;
|
|
case BTN_RIGHT:
|
|
p.button = MwLLMouseRight;
|
|
break;
|
|
}
|
|
switch(state) {
|
|
case WL_POINTER_BUTTON_STATE_PRESSED:
|
|
MwLLDispatch(self, down, &p);
|
|
break;
|
|
case WL_POINTER_BUTTON_STATE_RELEASED:
|
|
MwLLDispatch(self, up, &p);
|
|
break;
|
|
}
|
|
}
|
|
};
|
|
static void pointer_axis(void* data, struct wl_pointer* wl_pointer, uint32_t time,
|
|
uint32_t axis, wl_fixed_t value) {};
|
|
|
|
struct wl_pointer_listener pointer_listener = {
|
|
.enter = pointer_enter,
|
|
.leave = pointer_leave,
|
|
.motion = pointer_motion,
|
|
.button = pointer_button,
|
|
.axis = pointer_axis,
|
|
};
|
|
|
|
static void
|
|
wl_seat_name(void* data, struct wl_seat* wl_seat, const char* name) {};
|
|
static void wl_seat_capabilities(void* data, struct wl_seat* wl_seat,
|
|
MwU32 capabilities) {
|
|
MwLL self = data;
|
|
if(capabilities & WL_SEAT_CAPABILITY_KEYBOARD) {
|
|
struct wl_keyboard* keyboard = wl_seat_get_keyboard(wl_seat);
|
|
// wl_keyboard_add_listener(keyboard, const struct wl_keyboard_listener* listener, data);
|
|
}
|
|
if(capabilities & WL_SEAT_CAPABILITY_POINTER && !self->wayland.pointer) {
|
|
self->wayland.pointer = wl_seat_get_pointer(wl_seat);
|
|
wl_pointer_add_listener(self->wayland.pointer, &pointer_listener, data);
|
|
return;
|
|
}
|
|
};
|
|
|
|
void xdg_wm_base_ping(void* data,
|
|
struct xdg_wm_base* xdg_wm_base,
|
|
MwU32 serial) {
|
|
// The compositor will send us a ping event to check that we're responsive.
|
|
// We need to send back a pong request immediately.
|
|
xdg_wm_base_pong(xdg_wm_base, serial);
|
|
};
|
|
|
|
static wayland_protocol_t* wl_seat_setup(MwU32 name, MwLL ll) {
|
|
wayland_protocol_t* proto = malloc(sizeof(wayland_protocol_t));
|
|
proto->listener = malloc(sizeof(struct wl_seat_listener));
|
|
|
|
((struct wl_seat_listener*)proto->listener)->name = wl_seat_name;
|
|
((struct wl_seat_listener*)proto->listener)->capabilities = wl_seat_capabilities;
|
|
|
|
proto->context = wl_registry_bind(ll->wayland.registry, name, &wl_seat_interface, 1);
|
|
wl_seat_add_listener(proto->context, proto->listener, ll);
|
|
|
|
return proto;
|
|
}
|
|
|
|
static wayland_protocol_t* wl_compositor_setup(MwU32 name, struct _MwLLWayland* wayland) {
|
|
wayland->compositor = wl_registry_bind(wayland->registry, name, &wl_compositor_interface, 1);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static wayland_protocol_t* wl_subcompositor_setup(MwU32 name, struct _MwLLWayland* wayland) {
|
|
wayland->subcompositor = wl_registry_bind(wayland->registry, name, &wl_subcompositor_interface, 1);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static wayland_protocol_t* xdg_wm_base_setup(MwU32 name, struct _MwLLWayland* wayland) {
|
|
wayland_protocol_t* proto = malloc(sizeof(wayland_protocol_t));
|
|
proto->listener = malloc(sizeof(struct xdg_wm_base_listener));
|
|
|
|
((struct xdg_wm_base_listener*)proto->listener)->ping = xdg_wm_base_ping;
|
|
|
|
proto->context = wl_registry_bind(wayland->registry, name, &xdg_wm_base_interface, 1);
|
|
xdg_wm_base_add_listener(proto->context, proto->listener, wayland);
|
|
|
|
return proto;
|
|
}
|
|
|
|
static wayland_protocol_t* wp_cursor_shape_manager_v1_setup(MwU32 name, struct _MwLLWayland* wayland) {
|
|
wayland_protocol_t* proto = malloc(sizeof(wayland_protocol_t));
|
|
proto->listener = NULL;
|
|
|
|
proto->context = wl_registry_bind(wayland->registry, name, &wp_cursor_shape_manager_v1_interface, 1);
|
|
|
|
return proto;
|
|
}
|
|
|
|
typedef struct zxdg_decoration_manager_v1_context {
|
|
struct zxdg_decoration_manager_v1* manager;
|
|
// struct zxdg_decoration_toplevel_v1* toplevel;
|
|
struct zxdg_toplevel_decoration_v1* decoration;
|
|
} zxdg_decoration_manager_v1_context_t;
|
|
|
|
static wayland_protocol_t* zxdg_decoration_manager_v1_setup(MwU32 name, struct _MwLLWayland* wayland) {
|
|
wayland_protocol_t* proto = malloc(sizeof(wayland_protocol_t));
|
|
zxdg_decoration_manager_v1_context_t* ctx = malloc(sizeof(zxdg_decoration_manager_v1_context_t));
|
|
proto->listener = NULL;
|
|
|
|
ctx->manager = wl_registry_bind(wayland->registry, name, &zxdg_decoration_manager_v1_interface, 1);
|
|
;
|
|
|
|
proto->context = ctx;
|
|
|
|
return proto;
|
|
}
|
|
|
|
static MwBool egl_setup(MwLL self, int width, int height) {
|
|
int err;
|
|
EGLint numConfigs;
|
|
EGLint majorVersion;
|
|
EGLint minorVersion;
|
|
EGLContext context;
|
|
EGLSurface surface;
|
|
EGLint fbAttribs[] =
|
|
{
|
|
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
|
|
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
|
|
EGL_RED_SIZE, 8,
|
|
EGL_GREEN_SIZE, 8,
|
|
EGL_BLUE_SIZE, 8,
|
|
EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,
|
|
EGL_NONE};
|
|
EGLint contextAttribs[] = {
|
|
EGL_CONTEXT_CLIENT_VERSION, 1,
|
|
EGL_CONTEXT_MAJOR_VERSION, 1,
|
|
EGL_CONTEXT_MINOR_VERSION, 1,
|
|
EGL_NONE};
|
|
|
|
EGLDisplay display = eglGetDisplay(self->wayland.display);
|
|
if(display == EGL_NO_DISPLAY) {
|
|
printf("ERROR: eglGetDisplay, %0X\n", eglGetError());
|
|
return MwFALSE;
|
|
}
|
|
|
|
// Initialize EGL
|
|
if(!eglInitialize(display, &majorVersion, &minorVersion)) {
|
|
printf("ERROR: eglInitialize, %0X\n", eglGetError());
|
|
return MwFALSE;
|
|
}
|
|
|
|
// Get configs
|
|
if((eglGetConfigs(display, NULL, 0, &numConfigs) != EGL_TRUE) || (numConfigs == 0)) {
|
|
printf("ERROR: eglGetConfigs, %0X\n", eglGetError());
|
|
return MwFALSE;
|
|
}
|
|
|
|
// Choose config
|
|
if((eglChooseConfig(display, fbAttribs, &self->wayland.egl_config, 1, &numConfigs) != EGL_TRUE) || (numConfigs != 1)) {
|
|
printf("ERROR: eglChooseConfig, %0X\n", eglGetError());
|
|
return MwFALSE;
|
|
}
|
|
|
|
self->wayland.egl_window_native =
|
|
wl_egl_window_create(self->wayland.surface, width, height);
|
|
if(self->wayland.egl_window_native == EGL_NO_SURFACE) {
|
|
printf("ERROR: wl_egl_window_create, EGL_NO_SURFACE\n");
|
|
return MwFALSE;
|
|
}
|
|
|
|
// Create a surface
|
|
surface = eglCreateWindowSurface(display, self->wayland.egl_config, self->wayland.egl_window_native, NULL);
|
|
if(surface == EGL_NO_SURFACE) {
|
|
printf("ERROR: eglCreateWindowSurface, %0X\n", eglGetError());
|
|
return MwFALSE;
|
|
}
|
|
|
|
eglBindAPI(EGL_OPENGL_API);
|
|
|
|
// Create a GL context
|
|
context = eglCreateContext(display, self->wayland.egl_config, EGL_NO_CONTEXT, contextAttribs);
|
|
if(context == EGL_NO_CONTEXT) {
|
|
printf("ERROR: eglCreateContext, %0X\n", eglGetError());
|
|
return MwFALSE;
|
|
}
|
|
|
|
// Make the context current
|
|
if(!eglMakeCurrent(display, surface, surface, context)) {
|
|
printf("ERROR: eglMakeCurrent, %0X\n", eglGetError());
|
|
return MwFALSE;
|
|
}
|
|
|
|
glGenFramebuffers = (PFNGLGENFRAMEBUFFERSPROC)eglGetProcAddress("glGenFramebuffers");
|
|
glFramebufferTexture2D = (PFNGLFRAMEBUFFERTEXTURE2DPROC)eglGetProcAddress("glFramebufferTexture2D");
|
|
glGenRenderbuffers = (PFNGLGENRENDERBUFFERSPROC)eglGetProcAddress("glGenRenderbuffers");
|
|
// glRenderbufferStorage = (PFNGLRENDERBUFFERSTORAGEPROC)eglGetProcAddress("glRenderbufferStorage");
|
|
glFramebufferRenderbuffer = (PFNGLFRAMEBUFFERRENDERBUFFERPROC)eglGetProcAddress("glFramebufferRenderbuffer");
|
|
glBindRenderbuffer = (PFNGLBINDRENDERBUFFERPROC)eglGetProcAddress("glBindRenderbuffer");
|
|
glBindFramebuffer = (PFNGLBINDFRAMEBUFFERPROC)eglGetProcAddress("glBindFramebuffer");
|
|
glCheckFramebufferStatus = (PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC)eglGetProcAddress("glCheckFramebufferStatus");
|
|
|
|
glGenTextures(1, &self->wayland.fbo_texture);
|
|
glBindTexture(GL_TEXTURE_2D, self->wayland.fbo_texture);
|
|
|
|
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
|
|
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
glGenFramebuffers(1, &self->wayland.fbo);
|
|
glBindFramebuffer(GL_FRAMEBUFFER_BINDING, self->wayland.fbo);
|
|
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, self->wayland.fbo_texture, 0);
|
|
|
|
if((err = glCheckFramebufferStatus(GL_FRAMEBUFFER)) != GL_FRAMEBUFFER_COMPLETE) {
|
|
printf("Error binding framebuffer: %0X\n", err);
|
|
return MwFALSE;
|
|
}
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER_BINDING, 0);
|
|
|
|
glMatrixMode(GL_PROJECTION);
|
|
glLoadIdentity();
|
|
glOrtho(0, width, height, 0, -1, 1);
|
|
glEnable(GL_TEXTURE_2D);
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
glEnable(GL_BLEND);
|
|
|
|
self->wayland.egl_display = display;
|
|
self->wayland.egl_surface = surface;
|
|
self->wayland.egl_context = context;
|
|
return MwTRUE;
|
|
}
|
|
|
|
static void egl_resetup(MwLL handle) {
|
|
int err;
|
|
|
|
glDeleteTextures(1, &handle->wayland.fbo_texture);
|
|
glGenTextures(1, &handle->wayland.fbo_texture);
|
|
glBindTexture(GL_TEXTURE_2D, handle->wayland.fbo_texture);
|
|
|
|
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, handle->wayland.ww, handle->wayland.wh, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
|
|
|
|
glGenFramebuffers(1, &handle->wayland.fbo);
|
|
glBindFramebuffer(GL_FRAMEBUFFER_BINDING, handle->wayland.fbo);
|
|
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, handle->wayland.fbo_texture, 0);
|
|
|
|
if((err = glCheckFramebufferStatus(GL_FRAMEBUFFER)) != GL_FRAMEBUFFER_COMPLETE) {
|
|
printf("Error binding framebuffer: %0X\n", err);
|
|
return;
|
|
}
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER_BINDING, 0);
|
|
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
glMatrixMode(GL_PROJECTION);
|
|
glLoadIdentity();
|
|
glOrtho(0, handle->wayland.ww, handle->wayland.wh, 0, -1, 1);
|
|
glViewport(0, 0, handle->wayland.ww, handle->wayland.wh);
|
|
|
|
handle->wayland.egl_do_resetup = MwFALSE;
|
|
}
|
|
|
|
static void recursive_draw(MwLL handle) {
|
|
int i;
|
|
if(handle->wayland.subwidgets != NULL) {
|
|
for(i = 0; i < arrlen(handle->wayland.subwidgets); i++) {
|
|
MwLLDispatch(handle->wayland.subwidgets[i], draw, NULL);
|
|
recursive_draw(handle->wayland.subwidgets[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void recursive_resize(MwLL handle) {
|
|
int i;
|
|
if(handle->wayland.subwidgets != NULL) {
|
|
for(i = 0; i < arrlen(handle->wayland.subwidgets); i++) {
|
|
MwLLDispatch(handle->wayland.subwidgets[i], resize, NULL);
|
|
recursive_resize(handle->wayland.subwidgets[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void xdg_toplevel_configure(void* data,
|
|
struct xdg_toplevel* xdg_toplevel,
|
|
MwI32 width, MwI32 height,
|
|
struct wl_array* states) {
|
|
MwLL self = data;
|
|
PFNGLFRAMEBUFFERTEXTURE2DPROC glFramebufferTexture2D = (PFNGLFRAMEBUFFERTEXTURE2DPROC)eglGetProcAddress("glFramebufferTexture2D");
|
|
|
|
if(width == 0 || height == 0) {
|
|
width = self->wayland.ww;
|
|
height = self->wayland.wh;
|
|
// if it's still 0 then bail
|
|
if(width == 0 || height == 0) {
|
|
return;
|
|
}
|
|
}
|
|
self->wayland.ww = width;
|
|
self->wayland.wh = height;
|
|
xdg_surface_set_window_geometry(self->wayland.toplevel->xdg_surface, 0, 0, self->wayland.ww, self->wayland.wh);
|
|
|
|
MwLLSetWH(self, width, height);
|
|
MwLLDispatch(self, resize, NULL);
|
|
recursive_resize(self);
|
|
|
|
if(!self->wayland.egl_setup) {
|
|
self->wayland.egl_setup = egl_setup(self, width, height);
|
|
MwLLForceRender(self);
|
|
} else {
|
|
wl_egl_window_resize(self->wayland.egl_window_native, width, height, 0, 0);
|
|
self->wayland.egl_do_resetup = MwTRUE;
|
|
egl_resetup(self);
|
|
}
|
|
|
|
return;
|
|
};
|
|
|
|
static void decoration_configure(
|
|
void* data, struct zxdg_toplevel_decoration_v1* zxdg_toplevel_decoration_v1,
|
|
MwU32 mode) {
|
|
}
|
|
|
|
static void xdg_toplevel_close(
|
|
void* data, struct xdg_toplevel* xdg_toplevel) {
|
|
MwLL self = data;
|
|
MwLLDispatch(self, close, NULL);
|
|
};
|
|
|
|
static void xdg_surface_configure(
|
|
void* data, struct xdg_surface* xdg_surface, MwU32 serial) {
|
|
MwLL self = data;
|
|
|
|
// The compositor configures our surface, acknowledge the configure event
|
|
xdg_surface_ack_configure(xdg_surface, serial);
|
|
|
|
if(self->wayland.configured) {
|
|
// If this isn't the first configure event we've received, we already
|
|
// have a buffer attached, so no need to do anything. Commit the
|
|
// surface to apply the configure acknowledgement.
|
|
// wl_surface_attach(self->surface, self->buffer, 0, 0);
|
|
wl_surface_commit(self->wayland.surface);
|
|
}
|
|
|
|
self->wayland.configured = MwTRUE;
|
|
}
|
|
|
|
static int event_loop(MwLL handle) {
|
|
enum {
|
|
DISPLAY_FD,
|
|
KEYREPEAT_FD,
|
|
CURSOR_FD
|
|
};
|
|
struct pollfd fd;
|
|
int timeout = 100;
|
|
struct _MwLLWayland* wayland = &handle->wayland;
|
|
MwLL topmost_parent = handle->wayland.parent;
|
|
|
|
if(wayland->display == NULL) {
|
|
return 0;
|
|
}
|
|
fd.fd = wl_display_get_fd(wayland->display);
|
|
fd.events = POLLIN;
|
|
|
|
/* If an error other than EAGAIN happens, we have likely been disconnected from the Wayland session */
|
|
while(wl_display_flush(wayland->display) == -1) {
|
|
if(errno != EAGAIN) {
|
|
return MwFALSE;
|
|
}
|
|
|
|
while(poll(&fd, 1, -1) == -1) {
|
|
if(errno != EINTR && errno != EAGAIN) {
|
|
wl_display_cancel_read(wayland->display);
|
|
|
|
MwLLDispatch(handle, close, NULL);
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
IOI:
|
|
I took this from ioilib which I in turn adapted from SDL. I think this is meant to be required?
|
|
But doing this on my main machine causes the decorations manager to be null...? This doesn't show up on the project currently using ioilib.
|
|
Race condition? Random KDE bug? Who knows, but it seems that not polling for this works anyways (possibly because we already flush events above and then cancel reads when we actually need to close).
|
|
*/
|
|
/*
|
|
|
|
// Condition where no events are being sent.
|
|
if(!poll(&fd, 1, timeout)) {
|
|
wl_display_cancel_read(wayland->display);
|
|
// In this case, we need to commit the surface for any animations, etc.
|
|
wl_surface_commit(wayland->surface);
|
|
return 0;
|
|
}
|
|
|
|
*/
|
|
|
|
if(fd.revents & POLLIN) {
|
|
wl_display_read_events(wayland->display);
|
|
if(wl_display_dispatch_pending(wayland->display) > 0) {
|
|
}
|
|
} else {
|
|
if(wl_display_dispatch_pending(wayland->display) == 0) {
|
|
MwLLDispatch(handle, draw, NULL);
|
|
recursive_draw(handle);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#define WL_INTERFACE(interface) \
|
|
shput(wayland->wl_protocol_setup_map, interface##_interface.name, (wl_setup_func*)interface##_setup);
|
|
|
|
static void setup_callbacks(struct _MwLLWayland* wayland) {
|
|
wayland->registry_listener.global = new_protocol;
|
|
wayland->registry_listener.global_remove = protocol_removed;
|
|
wayland->wl_protocol_setup_map = NULL;
|
|
wayland->wl_protocol_map = NULL;
|
|
|
|
WL_INTERFACE(wl_compositor);
|
|
WL_INTERFACE(wl_subcompositor);
|
|
WL_INTERFACE(wl_seat);
|
|
if(wayland->type == MWLL_WAYLAND_TOPLEVEL) {
|
|
WL_INTERFACE(xdg_wm_base);
|
|
WL_INTERFACE(zxdg_decoration_manager_v1);
|
|
WL_INTERFACE(wp_cursor_shape_manager_v1);
|
|
}
|
|
}
|
|
|
|
#undef WL_INTERFACE
|
|
|
|
static void setup_toplevel(MwLL r, int x, int y) {
|
|
int i;
|
|
struct wl_region* region;
|
|
|
|
r->wayland.type = MWLL_WAYLAND_TOPLEVEL;
|
|
r->wayland.toplevel = malloc(sizeof(struct _MwLLWaylandTopLevel));
|
|
|
|
setup_callbacks(&r->wayland);
|
|
|
|
// Connect to the Wayland compositor
|
|
r->wayland.display = wl_display_connect(NULL);
|
|
if(r->wayland.display == NULL) {
|
|
printf("wayland: failed to create display\n");
|
|
raise(SIGTRAP);
|
|
return;
|
|
}
|
|
|
|
// Do a roundtrip to ensure all interfaces are setup.
|
|
r->wayland.registry = wl_display_get_registry(r->wayland.display);
|
|
wl_registry_add_listener(r->wayland.registry, &r->wayland.registry_listener, r);
|
|
if(wl_display_roundtrip(r->wayland.display) == -1) {
|
|
printf("roundtrip failed\n");
|
|
raise(SIGTRAP);
|
|
return;
|
|
}
|
|
|
|
// Check that all globals we require are available
|
|
if(
|
|
r->wayland.compositor == NULL ||
|
|
WAYLAND_GET_INTERFACE(r->wayland, xdg_wm_base)->context == NULL ||
|
|
WAYLAND_GET_INTERFACE(r->wayland, wl_seat)->context == NULL) {
|
|
|
|
printf("no wl_shm, wl_compositor, wl_seat, or xdg_wm_base support\n");
|
|
raise(SIGTRAP);
|
|
return;
|
|
}
|
|
|
|
r->wayland.toplevel->xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
|
|
|
|
// Create a wl_surface, a xdg_surface and a xdg_toplevel
|
|
r->wayland.surface = wl_compositor_create_surface(r->wayland.compositor);
|
|
r->wayland.toplevel->xdg_surface =
|
|
xdg_wm_base_get_xdg_surface(WAYLAND_GET_INTERFACE(r->wayland, xdg_wm_base)->context, r->wayland.surface);
|
|
r->wayland.toplevel->xdg_top_level = xdg_surface_get_toplevel(r->wayland.toplevel->xdg_surface);
|
|
|
|
// setup mandatory listeners
|
|
r->wayland.toplevel->xdg_surface_listener.configure = xdg_surface_configure;
|
|
|
|
r->wayland.toplevel->xdg_toplevel_listener.configure = xdg_toplevel_configure;
|
|
r->wayland.toplevel->xdg_toplevel_listener.close = xdg_toplevel_close;
|
|
|
|
xdg_surface_add_listener(r->wayland.toplevel->xdg_surface, &r->wayland.toplevel->xdg_surface_listener, r);
|
|
xdg_toplevel_add_listener(r->wayland.toplevel->xdg_top_level, &r->wayland.toplevel->xdg_toplevel_listener,
|
|
r);
|
|
|
|
xdg_toplevel_set_title(r->wayland.toplevel->xdg_top_level, "Milsko App");
|
|
xdg_toplevel_set_app_id(r->wayland.toplevel->xdg_top_level, "MilskoWaylandApp");
|
|
|
|
// Perform the initial commit and wait for the first configure event
|
|
wl_surface_commit(r->wayland.surface);
|
|
event_loop(r);
|
|
|
|
// setup decorations if we can
|
|
if(shget(r->wayland.wl_protocol_map, zxdg_decoration_manager_v1_interface.name) != NULL) {
|
|
zxdg_decoration_manager_v1_context_t* dec = WAYLAND_GET_INTERFACE(r->wayland, zxdg_decoration_manager_v1)->context;
|
|
|
|
dec->decoration = zxdg_decoration_manager_v1_get_toplevel_decoration(
|
|
dec->manager, r->wayland.toplevel->xdg_top_level);
|
|
|
|
zxdg_toplevel_decoration_v1_set_mode(
|
|
dec->decoration, ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
|
|
} else {
|
|
printf("zxdg null\n");
|
|
}
|
|
|
|
// buffer_setup(&r->wayland);
|
|
|
|
// glGenBuffers(1, &vertex_buffer);
|
|
}
|
|
|
|
static void setup_subsurface(MwLL parent, MwLL r, int x, int y) {
|
|
struct wl_compositor* compositor = parent->wayland.compositor;
|
|
struct wl_subcompositor* subcompositor = parent->wayland.subcompositor;
|
|
struct wl_surface* parent_surface = parent->wayland.surface;
|
|
MwLL topmost_parent = parent;
|
|
struct wl_region* region;
|
|
|
|
while(topmost_parent->wayland.type != MWLL_WAYLAND_TOPLEVEL) {
|
|
topmost_parent = topmost_parent->wayland.parent;
|
|
}
|
|
|
|
r->wayland.type = MWLL_WAYLAND_SUBSURFACE;
|
|
|
|
r->wayland.display = parent->wayland.display;
|
|
|
|
r->wayland.surface = wl_compositor_create_surface(compositor);
|
|
|
|
region = wl_compositor_create_region(compositor);
|
|
wl_region_add(region, 0, 0, r->wayland.ww, r->wayland.wh);
|
|
wl_surface_set_opaque_region(r->wayland.surface, region);
|
|
wl_surface_commit(r->wayland.surface);
|
|
event_loop(r);
|
|
wl_region_destroy(region);
|
|
|
|
r->wayland.subsurface = wl_subcompositor_get_subsurface(subcompositor, r->wayland.surface, parent_surface);
|
|
|
|
wl_subsurface_set_position(r->wayland.subsurface, x, y);
|
|
|
|
// printf("position %d %d\n", x, y);
|
|
|
|
setup_callbacks(&r->wayland);
|
|
|
|
r->wayland.registry = wl_display_get_registry(topmost_parent->wayland.display);
|
|
r->wayland.display = topmost_parent->wayland.display;
|
|
|
|
wl_registry_add_listener(r->wayland.registry, &r->wayland.registry_listener, r);
|
|
if(wl_display_roundtrip(r->wayland.display) == -1) {
|
|
printf("roundtrip failed\n");
|
|
raise(SIGTRAP);
|
|
return;
|
|
}
|
|
|
|
// arrpush(parent->wayland.subsurfaces, r);
|
|
|
|
r->wayland.configured = MwTRUE;
|
|
|
|
r->wayland.egl_context = topmost_parent->wayland.egl_context;
|
|
r->wayland.egl_display = topmost_parent->wayland.egl_display;
|
|
r->wayland.egl_surface = topmost_parent->wayland.egl_surface;
|
|
r->wayland.fbo = topmost_parent->wayland.fbo;
|
|
r->wayland.fbo_texture = topmost_parent->wayland.fbo_texture;
|
|
|
|
arrpush(parent->wayland.subwidgets, r);
|
|
}
|
|
|
|
static void framebuffer_action_begin(MwLL handle) {
|
|
glBindFramebuffer(GL_FRAMEBUFFER, handle->wayland.fbo);
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, handle->wayland.fbo_texture, 0);
|
|
}
|
|
|
|
static void framebuffer_action_end(MwLL handle) {
|
|
glFinish();
|
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
|
}
|
|
|
|
static MwLL MwLLCreateImpl(MwLL parent, int x, int y, int width, int height) {
|
|
MwLL r;
|
|
r = malloc(sizeof(*r));
|
|
MwLLCreateCommon(r);
|
|
|
|
r->common.type = MwLLBackendWayland;
|
|
|
|
if(width == 0) width = 1;
|
|
if(height == 0) height = 1;
|
|
|
|
if(x == MwDEFAULT) {
|
|
x = 0;
|
|
}
|
|
if(y == MwDEFAULT) {
|
|
y = 0;
|
|
}
|
|
|
|
r->wayland.ww = width;
|
|
r->wayland.wh = height;
|
|
r->wayland.x = x;
|
|
r->wayland.y = y;
|
|
|
|
r->wayland.parent = parent;
|
|
|
|
r->wayland.force_redraw = MwFALSE;
|
|
r->wayland.subwidgets = NULL;
|
|
r->wayland.pointer = NULL;
|
|
if(parent == NULL) {
|
|
setup_toplevel(r, x, y);
|
|
} else {
|
|
setup_subsurface(parent, r, x, y);
|
|
}
|
|
|
|
return r;
|
|
}
|
|
|
|
static void MwLLDestroyImpl(MwLL handle) {
|
|
MwLLDestroyCommon(handle);
|
|
|
|
free(handle);
|
|
}
|
|
|
|
static void MwLLGetXYWHImpl(MwLL handle, int* x, int* y, unsigned int* w, unsigned int* h) {
|
|
*x = handle->wayland.x;
|
|
*y = handle->wayland.y;
|
|
*w = handle->wayland.ww;
|
|
*h = handle->wayland.wh;
|
|
}
|
|
|
|
static void MwLLSetXYImpl(MwLL handle, int x, int y) {
|
|
if(handle->wayland.type != MWLL_WAYLAND_TOPLEVEL) {
|
|
handle->wayland.x = x;
|
|
handle->wayland.y = y;
|
|
wl_subsurface_set_position(handle->wayland.subsurface, x, y);
|
|
MwLLForceRender(handle);
|
|
}
|
|
}
|
|
|
|
static void MwLLSetWHImpl(MwLL handle, int w, int h) {
|
|
/* Prevent an integer underflow when the w/h is too low */
|
|
if((w < 10 || h < 10)) {
|
|
handle->wayland.ww = 10;
|
|
handle->wayland.wh = 10;
|
|
return;
|
|
}
|
|
handle->wayland.ww = w;
|
|
handle->wayland.wh = h;
|
|
|
|
if(handle->wayland.type == MWLL_WAYLAND_TOPLEVEL) {
|
|
xdg_surface_set_window_geometry(handle->wayland.toplevel->xdg_surface, 0, 0, handle->wayland.ww, handle->wayland.wh);
|
|
} else {
|
|
}
|
|
}
|
|
|
|
static void MwLLPolygonImpl(MwLL handle, MwPoint* points, int points_count, MwLLColor color) {
|
|
int i, n;
|
|
float maxX = 0, maxY = 0;
|
|
float centerX, centerY;
|
|
int doLoopback = MwFALSE;
|
|
|
|
if(points_count > 3) {
|
|
/* To avoid having to do a real triangulation algorithim:
|
|
1.) loop through the points once to determine the bounds of the polygon
|
|
2.) calculate the center of the polygon
|
|
|
|
*/
|
|
for(i = 0; i < points_count; i++) {
|
|
float x = round(handle->wayland.x) + round(points[i].x);
|
|
float y = round(handle->wayland.y) + round(points[i].y);
|
|
if(x >= maxX) {
|
|
maxX = x;
|
|
}
|
|
if(y >= maxY) {
|
|
maxY = y;
|
|
}
|
|
}
|
|
}
|
|
centerX = (handle->wayland.x + maxX / 2);
|
|
centerY = (handle->wayland.y + maxY / 2);
|
|
|
|
/* Efficient? No. But at the end of the day it's still not that many polygons, and if a computer can't handle 12 polygons over 6 then it should not be using Wayland anyways */
|
|
|
|
framebuffer_action_begin(handle);
|
|
|
|
if(points_count == 3) {
|
|
glBegin(GL_TRIANGLES);
|
|
} else if((points_count % 3) == 0) {
|
|
glBegin(GL_POLYGON);
|
|
} else {
|
|
glBegin(GL_TRIANGLE_FAN);
|
|
doLoopback = MwTRUE;
|
|
}
|
|
|
|
for(i = 0; i < points_count; i++) {
|
|
float x = round(handle->wayland.x) + round(points[i].x);
|
|
float y = round(handle->wayland.y) + round(points[i].y);
|
|
|
|
glColor3f(color->common.red / 255.0, color->common.green / 255.0, color->common.blue / 255.0);
|
|
|
|
glVertex2f(x, y);
|
|
if(doLoopback) {
|
|
glVertex2f(maxX, maxY);
|
|
glVertex2f(x, y);
|
|
}
|
|
}
|
|
glEnd();
|
|
glColor3f(0, 0, 0);
|
|
|
|
framebuffer_action_end(handle);
|
|
}
|
|
|
|
static void MwLLLineImpl(MwLL handle, MwPoint* points, MwLLColor color) {
|
|
int i;
|
|
|
|
glLineWidth(1);
|
|
|
|
framebuffer_action_begin(handle);
|
|
glBegin(GL_LINES);
|
|
for(i = 0; i < 2; i++) {
|
|
float x = round(handle->wayland.x) + round(points[i].x);
|
|
float y = round(handle->wayland.y) + round(points[i].y);
|
|
|
|
glColor3f(color->common.red / 255.0, color->common.green / 255.0, color->common.blue / 255.0);
|
|
|
|
glVertex2f(x, y);
|
|
}
|
|
|
|
glEnd();
|
|
glColor3f(0, 0, 0);
|
|
|
|
framebuffer_action_end(handle);
|
|
}
|
|
|
|
static void MwLLEndDrawImpl(MwLL handle) {
|
|
int w, h;
|
|
|
|
if(handle->wayland.type == MWLL_WAYLAND_TOPLEVEL) {
|
|
w = handle->wayland.ww;
|
|
h = handle->wayland.wh;
|
|
} else {
|
|
MwLL parent = handle->wayland.parent;
|
|
w = parent->wayland.ww;
|
|
h = parent->wayland.wh;
|
|
}
|
|
|
|
recursive_draw(handle);
|
|
|
|
glBindTexture(GL_TEXTURE_2D, handle->wayland.fbo_texture);
|
|
// glClear(GL_COLOR_BUFFER_BIT);
|
|
// glClearColor(1.0, 0, 0, 1);
|
|
|
|
glColor3f(1.0, 1.0, 1.0);
|
|
|
|
glBegin(GL_QUADS);
|
|
glTexCoord2f(0.0f, 1);
|
|
glVertex2f(0, 0);
|
|
glTexCoord2f(1, 1);
|
|
glVertex2f(w, 0);
|
|
glTexCoord2f(1, 0.0f);
|
|
glVertex2f(w, h);
|
|
glTexCoord2f(0.0f, 0.0f);
|
|
glVertex2f(0, h);
|
|
glEnd();
|
|
glFinish();
|
|
|
|
wl_surface_damage(handle->wayland.surface, 0, 0, handle->wayland.ww, handle->wayland.wh);
|
|
if(!eglSwapBuffers(handle->wayland.egl_display, handle->wayland.egl_surface)) {
|
|
printf("error swapping buffers! %0X\n", eglGetError());
|
|
} else {
|
|
wl_surface_commit(handle->wayland.surface);
|
|
}
|
|
}
|
|
|
|
static MwLLColor MwLLAllocColorImpl(MwLL handle, int r, int g, int b) {
|
|
MwLLColor c = malloc(sizeof(*c));
|
|
|
|
c->common.red = r;
|
|
c->common.blue = b;
|
|
c->common.green = g;
|
|
|
|
return c;
|
|
}
|
|
|
|
static void MwLLColorUpdateImpl(MwLL handle, MwLLColor c, int r, int g, int b) {
|
|
c->common.red = r;
|
|
c->common.blue = b;
|
|
c->common.green = g;
|
|
}
|
|
|
|
static void MwLLFreeColorImpl(MwLLColor color) {
|
|
free(color);
|
|
}
|
|
|
|
static int MwLLPendingImpl(MwLL handle) {
|
|
if(wl_display_dispatch_pending(handle->wayland.display) == 0) {
|
|
}
|
|
return event_loop(handle) || handle->wayland.force_redraw;
|
|
}
|
|
|
|
static void MwLLNextEventImpl(MwLL handle) {
|
|
MwBool render = handle->wayland.force_redraw;
|
|
|
|
if(render) {
|
|
handle->wayland.force_redraw = MwFALSE;
|
|
}
|
|
}
|
|
|
|
static void MwLLSetTitleImpl(MwLL handle, const char* title) {
|
|
if(handle->wayland.type == MWLL_WAYLAND_TOPLEVEL) {
|
|
xdg_toplevel_set_title(handle->wayland.toplevel->xdg_top_level, title);
|
|
}
|
|
}
|
|
|
|
static MwLLPixmap MwLLCreatePixmapImpl(MwLL handle, unsigned char* data, int width, int height) {
|
|
MwLLPixmap r = malloc(sizeof(*r));
|
|
|
|
r->common.width = width;
|
|
r->common.height = height;
|
|
r->common.raw = data;
|
|
r->wayland.texture = 0;
|
|
|
|
glGenTextures(1, &r->wayland.texture);
|
|
|
|
glBindTexture(GL_TEXTURE_2D, r->wayland.texture);
|
|
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
MwLLPixmapUpdate(r);
|
|
|
|
return r;
|
|
}
|
|
|
|
static void MwLLPixmapUpdateImpl(MwLLPixmap r) {
|
|
glBindTexture(GL_TEXTURE_2D, r->wayland.texture);
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, r->common.width, r->common.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, r->common.raw);
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
}
|
|
|
|
static void MwLLDestroyPixmapImpl(MwLLPixmap pixmap) {
|
|
glDeleteTextures(1, &pixmap->wayland.texture);
|
|
free(pixmap);
|
|
}
|
|
|
|
static void MwLLDrawPixmapImpl(MwLL handle, MwRect* rect, MwLLPixmap pixmap) {
|
|
int x, y;
|
|
glColor3f(1.0, 1.0, 1.0);
|
|
|
|
x = handle->wayland.x + rect->x;
|
|
y = handle->wayland.y + rect->y;
|
|
|
|
glBindTexture(GL_TEXTURE_2D, pixmap->wayland.texture);
|
|
glActiveTexture(pixmap->wayland.texture);
|
|
|
|
framebuffer_action_begin(handle);
|
|
glBegin(GL_QUADS);
|
|
glTexCoord2f(0.0f, 0);
|
|
glVertex2f(x, y);
|
|
glTexCoord2f(1, 0);
|
|
glVertex2f(x + rect->width, y);
|
|
glTexCoord2f(1, 1);
|
|
glVertex2f(x + rect->width, y + rect->height);
|
|
glTexCoord2f(0.0f, 1);
|
|
glVertex2f(x, y + rect->height);
|
|
glEnd();
|
|
glFinish();
|
|
framebuffer_action_end(handle);
|
|
}
|
|
|
|
static void MwLLSetIconImpl(MwLL handle, MwLLPixmap pixmap) {
|
|
}
|
|
|
|
static void MwLLForceRenderImpl(MwLL handle) {
|
|
wl_surface_damage(handle->wayland.surface, 0, 0, handle->wayland.ww, handle->wayland.wh);
|
|
|
|
wl_surface_commit(handle->wayland.surface);
|
|
|
|
handle->wayland.force_redraw = MwTRUE;
|
|
}
|
|
|
|
static void MwLLSetCursorImpl(MwLL handle, MwCursor* image, MwCursor* mask) {
|
|
}
|
|
|
|
static void MwLLDetachImpl(MwLL handle, MwPoint* point) {
|
|
}
|
|
|
|
static void MwLLShowImpl(MwLL handle, int show) {
|
|
}
|
|
|
|
static void MwLLMakePopupImpl(MwLL handle, MwLL parent) {
|
|
}
|
|
|
|
static void MwLLSetSizeHintsImpl(MwLL handle, int minx, int miny, int maxx, int maxy) {
|
|
if(handle->wayland.type == MWLL_WAYLAND_TOPLEVEL) {
|
|
xdg_toplevel_set_min_size(handle->wayland.toplevel->xdg_top_level, minx, miny);
|
|
xdg_toplevel_set_max_size(handle->wayland.toplevel->xdg_top_level, maxx, maxy);
|
|
}
|
|
}
|
|
|
|
static void MwLLMakeBorderlessImpl(MwLL handle, int toggle) {
|
|
if(handle->wayland.type == MWLL_WAYLAND_TOPLEVEL) {
|
|
if(WAYLAND_GET_INTERFACE(handle->wayland, zxdg_decoration_manager_v1)->context != NULL) {
|
|
zxdg_decoration_manager_v1_context_t* dec = WAYLAND_GET_INTERFACE(handle->wayland, zxdg_decoration_manager_v1)->context;
|
|
|
|
dec->decoration = zxdg_decoration_manager_v1_get_toplevel_decoration(
|
|
dec->manager, handle->wayland.toplevel->xdg_top_level);
|
|
|
|
zxdg_toplevel_decoration_v1_set_mode(
|
|
dec->decoration, ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE);
|
|
} else {
|
|
// TODO: hide our custom window decorations when we have them.
|
|
printf("zxdg null\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
static void MwLLFocusImpl(MwLL handle) {
|
|
}
|
|
|
|
static void MwLLGrabPointerImpl(MwLL handle, int toggle) {
|
|
}
|
|
|
|
static void MwLLSetClipboardImpl(MwLL handle, const char* text) {
|
|
}
|
|
|
|
static char* MwLLGetClipboardImpl(MwLL handle) {
|
|
char* r = NULL;
|
|
return r;
|
|
}
|
|
|
|
static void MwLLMakeToolWindowImpl(MwLL handle) {
|
|
}
|
|
|
|
static void MwLLGetCursorCoordImpl(MwLL handle, MwPoint* point) {
|
|
}
|
|
|
|
static void MwLLGetScreenSizeImpl(MwLL handle, MwRect* rect) {
|
|
}
|
|
|
|
static void MwLLBeginStateChangeImpl(MwLL handle) {
|
|
MwLLShow(handle, 0);
|
|
}
|
|
|
|
static void MwLLEndStateChangeImpl(MwLL handle) {
|
|
MwLLShow(handle, 1);
|
|
}
|
|
|
|
static int MwLLWaylandCallInitImpl(void) {
|
|
#ifdef __linux__
|
|
if(strcmp(getenv("XDG_SESSION_TYPE"), "wayland")) {
|
|
return 1;
|
|
}
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
#include "call.c"
|
|
CALL(Wayland);
|