ref: f3e0b4598f147e1bdc75d7a3557de29d71e8c2d6
parent: bee4db6507fdf31bddfa98d26a673e135dfd28ad
author: Jacob Moody <moody@posixcafe.org>
date: Sun Oct 10 03:48:35 EDT 2021
add wayland backend
--- /dev/null
+++ b/Make.linux
@@ -1,0 +1,22 @@
+# Unix
+#PTHREAD= # for Mac
+PTHREAD=-pthread
+AR=ar
+AS=as
+RANLIB=ranlib
+CC=gcc
+CFLAGS=-Wall -Wno-missing-braces -ggdb -I$(ROOT) -I$(ROOT)/include -I$(ROOT)/kern -c -D_THREAD_SAFE $(PTHREAD) -O2
+O=o
+OS=posix
+GUI=wl
+LDADD=-lwayland-client -lxkbcommon -ggdb -lm -lrt
+LDFLAGS=$(PTHREAD)
+TARG=drawterm
+# AUDIO=none
+AUDIO=unix
+
+all: default
+
+libmachdep.a:
+ arch=`uname -m|sed 's/i.86/386/;s/Power Macintosh/power/; s/x86_64/amd64/; s/armv[567].*/arm/; s/aarch64/arm64/'`; \
+ (cd posix-$$arch && make)
--- /dev/null
+++ b/gui-wl/Makefile
@@ -1,0 +1,36 @@
+ROOT=..
+include ../Make.config
+LIB=libgui.a
+
+XDG_SHELL=/usr/share/wayland-protocols/stable/xdg-shell/xdg-shell.xml
+XDG_DECO=/usr/share/wayland-protocols/unstable/xdg-decoration/xdg-decoration-unstable-v1.xml
+
+HFILES=\
+ xdg-shell-protocol.h\
+ xdg-decoration-protocol.h\
+ wl-inc.h\
+
+OFILES=\
+ xdg-shell-protocol.$O\
+ xdg-decoration-protocol.$O\
+ wl-cb.$O\
+ wl-screen.$O\
+ wl-util.$O\
+
+xdg-shell-protocol.c:
+ wayland-scanner private-code < $(XDG_SHELL) > xdg-shell-protocol.c
+
+xdg-shell-protocol.h:
+ wayland-scanner client-header < $(XDG_SHELL) > xdg-shell-protocol.h
+
+xdg-decoration-protocol.c:
+ wayland-scanner private-code < $(XDG_DECO) > xdg-decoration-protocol.c
+
+xdg-decoration-protocol.h:
+ wayland-scanner client-header < $(XDG_DECO) > xdg-decoration-protocol.h
+
+default: $(LIB)
+$(LIB): $(HFILES) $(OFILES)
+ $(AR) r $(LIB) $(OFILES)
+ $(RANLIB) $(LIB)
+
--- /dev/null
+++ b/gui-wl/wl-cb.c
@@ -1,0 +1,493 @@
+#define _POSIX_C_SOURCE 200809L
+#include <sys/mman.h>
+#include <wayland-client.h>
+#include <wayland-client-protocol.h>
+#include <linux/input-event-codes.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <xkbcommon/xkbcommon.h>
+#include "xdg-shell-protocol.h"
+#include "xdg-decoration-protocol.h"
+
+#include "u.h"
+#include "lib.h"
+#include "kern/dat.h"
+#include "kern/fns.h"
+#include "error.h"
+#include "user.h"
+#include <draw.h>
+#include <memdraw.h>
+#include <keyboard.h>
+#include "screen.h"
+#include "wl-inc.h"
+
+#undef close
+#undef send
+#undef pipe
+#undef write
+#undef read
+
+static void
+xdg_surface_handle_configure(void *data, struct xdg_surface *xdg_surface, uint32_t serial)
+{
+ Wlwin *wl;
+
+ wl = data;
+ xdg_surface_ack_configure(xdg_surface, serial);
+ wl_surface_commit(wl->surface);
+}
+
+const struct xdg_surface_listener xdg_surface_listener = {
+ .configure = xdg_surface_handle_configure,
+};
+
+static void
+xdg_toplevel_handle_close(void *data, struct xdg_toplevel *xdg_toplevel)
+{
+ Wlwin *wl;
+ wl = data;
+ wl->runing = 0;
+ exits(nil);
+}
+
+static void
+xdg_toplevel_handle_configure(void *data, struct xdg_toplevel *xdg_toplevel, int32_t width, int32_t height, struct wl_array *states)
+{
+ Wlwin *wl;
+
+ wl = data;
+ if(width == 0 || height == 0 || (width == wl->dx && height == wl->dy))
+ return;
+ wlresize(wl, width, height);
+}
+
+const struct xdg_toplevel_listener xdg_toplevel_listener = {
+ .configure = xdg_toplevel_handle_configure,
+ .close = xdg_toplevel_handle_close,
+};
+
+static const struct wl_callback_listener wl_surface_frame_listener;
+
+static void
+wl_surface_frame_done(void *data, struct wl_callback *cb, uint32_t time)
+{
+ Wlwin *wl;
+
+ wl = data;
+ wl_callback_destroy(cb);
+ cb = wl_surface_frame(wl->surface);
+ qlock(&drawlock);
+ wlflush(wl);
+ qunlock(&drawlock);
+ wl_callback_add_listener(cb, &wl_surface_frame_listener, wl);
+}
+
+static void
+keyboard_keymap(void *data, struct wl_keyboard *keyboard, uint32_t format, int32_t fd, uint32_t size)
+{
+ static struct xkb_keymap *keymap = nil;
+ char *keymap_string;
+ Wlwin *wl;
+
+ wl = data;
+ keymap_string = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
+ xkb_keymap_unref(keymap);
+ keymap = xkb_keymap_new_from_string(wl->xkb_context, keymap_string, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS);
+ munmap(keymap_string, size);
+ close(fd);
+ xkb_state_unref(wl->xkb_state);
+ wl->xkb_state = xkb_state_new(keymap);
+}
+
+static void
+keyboard_enter (void *data, struct wl_keyboard *keyboard, uint32_t serial, struct wl_surface *surface, struct wl_array *keys)
+{
+ Wlwin *wl;
+
+ wl = data;
+ qlock(&wl->clip.lk);
+ wl->clip.serial = serial;
+ qunlock(&wl->clip.lk);
+}
+
+static void
+keyboard_leave (void *data, struct wl_keyboard *keyboard, uint32_t serial, struct wl_surface *surface)
+{
+}
+
+static void
+keyboard_key(void *data, struct wl_keyboard *keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state)
+{
+ Wlwin *wl;
+ uint32_t utf32;
+
+ wl = data;
+ xkb_keysym_t keysym = xkb_state_key_get_one_sym(wl->xkb_state, key+8);
+ switch(keysym) {
+ case XKB_KEY_Return:
+ utf32 = '\n';
+ break;
+ case XKB_KEY_Tab:
+ utf32 = '\t';
+ break;
+ default:
+ utf32 = xkb_keysym_to_utf32(keysym);
+ break;
+ }
+ if(xkb_state_mod_name_is_active(wl->xkb_state, XKB_MOD_NAME_CTRL, XKB_STATE_MODS_EFFECTIVE) > 0)
+ if(utf32 >= 'a' && utf32 <= 'z')
+ utf32 -= ('a' - 1);
+ if(utf32){
+ kbdkey(utf32, state);
+ }
+}
+
+static void
+keyboard_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)
+{
+ Wlwin *wl;
+
+ wl = data;
+ xkb_state_update_mask(wl->xkb_state, mods_depressed, mods_latched, mods_locked, 0, 0, group);
+}
+
+static const struct wl_callback_listener wl_surface_frame_listener = {
+ .done = wl_surface_frame_done,
+};
+
+static struct wl_keyboard_listener keyboard_listener = {
+ &keyboard_keymap,
+ &keyboard_enter,
+ &keyboard_leave,
+ &keyboard_key,
+ &keyboard_modifiers
+};
+
+enum{
+ WlMouse1 = 272,
+ WlMouse2 = 274,
+ WlMouse3 = 273,
+
+ P9Mouse1 = 1,
+ P9Mouse2 = 2,
+ P9Mouse3 = 4,
+};
+
+static void
+pointer_handle_button(void *data, struct wl_pointer *pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t state)
+{
+ Wlwin *wl;
+
+ wl = data;
+ if(state)
+ switch(button){
+ case WlMouse1: /* M1 */
+ wl->mouse.buttons |= P9Mouse1;
+ break;
+ case WlMouse2: /* M2 */
+ wl->mouse.buttons |= P9Mouse2;
+ break;
+ case WlMouse3: /* M3 */
+ wl->mouse.buttons |= P9Mouse3;
+ break;
+ }
+ else
+ switch(button){
+ case WlMouse1: /* M1 */
+ wl->mouse.buttons &= ~P9Mouse1;
+ break;
+ case WlMouse2: /* M2 */
+ wl->mouse.buttons &= ~P9Mouse2;
+ break;
+ case WlMouse3: /* M3 */
+ wl->mouse.buttons &= ~P9Mouse3;
+ break;
+ }
+
+ wl->mouse.msec = time;
+ absmousetrack(wl->mouse.xy.x, wl->mouse.xy.y, wl->mouse.buttons, wl->mouse.msec);
+}
+
+static void
+pointer_handle_motion(void *data, struct wl_pointer *wl_pointer, uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y)
+{
+ Wlwin *wl;
+
+ wl = data;
+ wl->mouse.xy.x = surface_x / 256;
+ wl->mouse.xy.y = surface_y / 256;
+ wl->mouse.msec = time;
+ absmousetrack(wl->mouse.xy.x, wl->mouse.xy.y, wl->mouse.buttons, wl->mouse.msec);
+}
+
+static void
+pointer_handle_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)
+{
+ Wlwin *wl;
+
+ wl = data;
+ wl->pointerserial = serial;
+ wl_pointer_set_cursor(wl->pointer, wl->pointerserial, wl->cursorsurface, 0, 0);
+}
+
+static void
+pointer_handle_leave(void *data, struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface)
+{
+}
+
+static void
+pointer_handle_axis(void *data, struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis, wl_fixed_t value)
+{
+ Wlwin *wl;
+ int buttons;
+
+ if(axis == 1)
+ return; /* Horizontal scroll */
+ wl = data;
+ buttons = wl->mouse.buttons;
+ if(value < 0){
+ buttons |= 8;
+ } else {
+ buttons |= 16;
+ }
+ wl->mouse.msec = time;
+ /* p9 expects a scroll event to work like a button, a set and a release */
+ absmousetrack(wl->mouse.xy.x, wl->mouse.xy.y, buttons, wl->mouse.msec);
+ absmousetrack(wl->mouse.xy.x, wl->mouse.xy.y, wl->mouse.buttons, wl->mouse.msec);
+}
+
+static const struct wl_pointer_listener pointer_listener = {
+ .enter = pointer_handle_enter,
+ .leave = pointer_handle_leave,
+ .motion = pointer_handle_motion,
+ .button = pointer_handle_button,
+ .axis = pointer_handle_axis,
+};
+
+static void
+seat_handle_capabilities(void *data, struct wl_seat *seat, uint32_t capabilities)
+{
+ Wlwin *wl;
+
+ wl = data;
+ if(capabilities & WL_SEAT_CAPABILITY_POINTER) {
+ wl->pointer = wl_seat_get_pointer(seat);
+ wl_pointer_add_listener(wl->pointer, &pointer_listener, wl);
+ }
+ if(capabilities & WL_SEAT_CAPABILITY_KEYBOARD) {
+ struct wl_keyboard *keyboard = wl_seat_get_keyboard(seat);
+ wl_keyboard_add_listener(keyboard, &keyboard_listener, wl);
+ }
+}
+
+static const struct wl_seat_listener seat_listener = {
+ .capabilities = seat_handle_capabilities,
+};
+
+static void
+data_source_handle_send(void *data, struct wl_data_source *source, const char *mime_type, int fd)
+{
+ ulong n;
+ ulong pos;
+ ulong len;
+ Wlwin *wl;
+
+ if(strcmp(mime_type, "text/plain;charset=utf-8") != 0)
+ return;
+
+ wl = data;
+ qlock(&wl->clip.lk);
+ len = strlen(wl->clip.content);
+ for(pos = 0; (n = write(fd, wl->clip.content+pos, len-pos)) > 0 && pos < len; pos += n)
+ ;
+ wl->clip.posted = 0;
+ close(fd);
+ qunlock(&wl->clip.lk);
+}
+
+static void
+data_source_handle_cancelled(void *data, struct wl_data_source *source)
+{
+ Wlwin *wl;
+
+ wl = data;
+ qlock(&wl->clip.lk);
+ wl->clip.posted = 0;
+ qunlock(&wl->clip.lk);
+ wl_data_source_destroy(source);
+}
+
+static const struct wl_data_source_listener data_source_listener = {
+ .send = data_source_handle_send,
+ .cancelled = data_source_handle_cancelled,
+};
+
+static void
+data_device_handle_data_offer(void *data, struct wl_data_device *data_device, struct wl_data_offer *offer)
+{
+}
+
+static void
+data_device_handle_selection(void *data, struct wl_data_device *data_device, struct wl_data_offer *offer)
+{
+ Wlwin *wl;
+ ulong n;
+ ulong size;
+ ulong pos;
+ int fds[2];
+
+ // An application has set the clipboard contents
+ if (offer == NULL) {
+ return;
+ }
+
+ wl = data;
+ pipe(fds);
+ wl_data_offer_receive(offer, "text/plain;charset=utf-8", fds[1]);
+ close(fds[1]);
+
+ wl_display_roundtrip(wl->display);
+
+ qlock(&wl->clip.lk);
+ size = 8192;
+ wl->clip.content = realloc(wl->clip.content, size+1);
+ memset(wl->clip.content, 0, size+1);
+ for(pos = 0; (n = read(fds[0], wl->clip.content+pos, size-pos)) > 0;){
+ pos += n;
+ if(pos >= size){
+ size *= 2;
+ wl->clip.content = realloc(wl->clip.content, size+1);
+ memset(wl->clip.content+pos, 0, (size-pos)+1);
+ }
+ }
+ close(fds[0]);
+ qunlock(&wl->clip.lk);
+ wl_data_offer_destroy(offer);
+}
+
+static const struct wl_data_device_listener data_device_listener = {
+ .data_offer = data_device_handle_data_offer,
+ .selection = data_device_handle_selection,
+};
+
+static void
+xdg_wm_base_ping(void *data, struct xdg_wm_base *xdg_wm_base, uint32_t serial)
+{
+ xdg_wm_base_pong(xdg_wm_base, serial);
+}
+
+static const struct xdg_wm_base_listener xdg_wm_base_listener = {
+ .ping = xdg_wm_base_ping,
+};
+
+static void
+handle_global(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version)
+{
+ Wlwin *wl;
+
+ wl = data;
+ if(strcmp(interface, wl_shm_interface.name) == 0) {
+ wl->shm = wl_registry_bind(registry, name, &wl_shm_interface, 1);
+ } else if(strcmp(interface, wl_seat_interface.name) == 0) {
+ wl->seat = wl_registry_bind(registry, name, &wl_seat_interface, 1);
+ wl_seat_add_listener(wl->seat, &seat_listener, wl);
+ } else if(strcmp(interface, wl_compositor_interface.name) == 0) {
+ wl->compositor = wl_registry_bind(registry, name, &wl_compositor_interface, 1);
+ } else if(strcmp(interface, 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, wl);
+ } else if(strcmp(interface, wl_data_device_manager_interface.name) == 0) {
+ wl->data_device_manager = wl_registry_bind(registry, name, &wl_data_device_manager_interface, 3);
+ } else if(strcmp(interface, zxdg_decoration_manager_v1_interface.name) == 0) {
+ wl->decoman = wl_registry_bind(registry, name, &zxdg_decoration_manager_v1_interface, 1);
+ }
+}
+
+static void
+handle_global_remove(void *data, struct wl_registry *registry, uint32_t name)
+{
+}
+
+const struct wl_registry_listener registry_listener = {
+ .global = handle_global,
+ .global_remove = handle_global_remove,
+};
+
+void
+wlsetcb(Wlwin *wl)
+{
+ struct wl_registry *registry;
+ struct xdg_surface *xdg_surface;
+ struct wl_callback *cb;
+ struct zxdg_toplevel_decoration_v1 *deco;
+
+ registry = wl_display_get_registry(wl->display);
+ wl_registry_add_listener(registry, ®istry_listener, wl);
+ wl_display_roundtrip(wl->display);
+ wl->xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
+
+ if(wl->shm == nil || wl->compositor == nil || wl->xdg_wm_base == nil || wl->seat == nil || wl->decoman == nil)
+ sysfatal("Registration fell short");
+
+
+ wl->data_device = wl_data_device_manager_get_data_device(wl->data_device_manager, wl->seat);
+ wl_data_device_add_listener(wl->data_device, &data_device_listener, wl);
+ wlallocbuffer(wl);
+ wl->surface = wl_compositor_create_surface(wl->compositor);
+
+ xdg_surface = xdg_wm_base_get_xdg_surface(wl->xdg_wm_base, wl->surface);
+ wl->xdg_toplevel = xdg_surface_get_toplevel(xdg_surface);
+ deco = zxdg_decoration_manager_v1_get_toplevel_decoration(wl->decoman, wl->xdg_toplevel);
+ zxdg_toplevel_decoration_v1_set_mode(deco, ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
+ xdg_surface_add_listener(xdg_surface, &xdg_surface_listener, wl);
+ xdg_toplevel_add_listener(wl->xdg_toplevel, &xdg_toplevel_listener, wl);
+
+ wl_surface_commit(wl->surface);
+ wl_display_roundtrip(wl->display);
+
+ xdg_toplevel_set_app_id(wl->xdg_toplevel, "devdraw");
+
+ cb = wl_surface_frame(wl->surface);
+ wl_callback_add_listener(cb, &wl_surface_frame_listener, wl);
+}
+
+void
+wlsettitle(Wlwin *wl, char *s)
+{
+ xdg_toplevel_set_title(wl->xdg_toplevel, s);
+}
+
+void
+wlsetsnarf(Wlwin *wl, char *s)
+{
+ struct wl_data_source *source;
+
+ qlock(&wl->clip.lk);
+ if(wl->clip.content != nil)
+ free(wl->clip.content);
+
+ wl->clip.content = strdup(s);
+ /* Do we still own the clipboard? */
+ if(wl->clip.posted == 1)
+ goto done;
+
+ source = wl_data_device_manager_create_data_source(wl->data_device_manager);
+ wl_data_source_add_listener(source, &data_source_listener, wl);
+ wl_data_source_offer(source, "text/plain;charset=utf-8");
+ wl_data_device_set_selection(wl->data_device, source, wl->clip.serial);
+ wl->clip.posted = 1;
+done:
+ qunlock(&wl->clip.lk);
+}
+
+char*
+wlgetsnarf(Wlwin *wl)
+{
+ char *s;
+ qlock(&wl->clip.lk);
+ s = strdup(wl->clip.content);
+ qunlock(&wl->clip.lk);
+ return s;
+}
--- /dev/null
+++ b/gui-wl/wl-inc.h
@@ -1,0 +1,78 @@
+typedef struct Wlwin Wlwin;
+typedef struct Clipboard Clipboard;
+
+/* The contents of the clipboard
+ * are not stored in the compositor.
+ * Instead we signal that we have content
+ * and the compositor gives us a pipe
+ * to the program that wants it when
+ * the content is pasted. */
+struct Clipboard {
+ QLock lk;
+ char *content;
+
+ /* Wayland requires that in order
+ * to put data in to the clipboard
+ * you must be the focused application.
+ * So we must provide the serial we get
+ * on keyboard.enter. */
+ u32int serial;
+
+ /* Because we dont actually cough
+ * up the buffer until someone else
+ * asks, we can change the contents
+ * locally without a round trip.
+ * Posted stores if we already made
+ * our round trip */
+ int posted;
+};
+
+struct Mouse {
+ Point xy;
+ int buttons;
+ ulong msec;
+};
+
+struct Wlwin {
+ int dx;
+ int dy;
+ int monx;
+ int mony;
+ Mouse mouse;
+ Clipboard clip;
+ int dirty;
+
+ /* Wayland State */
+ int runing;
+ int poolsize;
+ int pointerserial;
+ void *shm_data;
+ struct wl_compositor *compositor;
+ struct wl_display *display;
+ struct wl_surface *surface;
+ struct wl_surface *cursorsurface;
+ struct xdg_wm_base *xdg_wm_base;
+ struct xdg_toplevel *xdg_toplevel;
+ struct wl_shm_pool *pool;
+ struct wl_buffer *screenbuffer;
+ struct wl_buffer *cursorbuffer;
+ struct wl_shm *shm;
+ struct wl_seat *seat;
+ struct wl_data_device_manager *data_device_manager;
+ struct wl_data_device *data_device;
+ struct wl_pointer *pointer;
+ /* Keyboard state */
+ struct xkb_state *xkb_state;
+ struct xkb_context *xkb_context;
+
+ struct zxdg_decoration_manager_v1 *decoman;
+};
+
+void wlallocbuffer(Wlwin*);
+void wlsetcb(Wlwin*);
+void wlsettitle(Wlwin*, char*);
+char* wlgetsnarf(Wlwin*);
+void wlsetsnarf(Wlwin*, char*);
+void wldrawcursor(Wlwin*, Cursorinfo*);
+void wlresize(Wlwin*, int, int);
+void wlflush(Wlwin*);
--- /dev/null
+++ b/gui-wl/wl-screen.c
@@ -1,0 +1,201 @@
+#define _POSIX_C_SOURCE 200809L
+#include <sys/mman.h>
+#include <wayland-client.h>
+#include <wayland-client-protocol.h>
+#include <linux/input-event-codes.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <xkbcommon/xkbcommon.h>
+#include "xdg-shell-protocol.h"
+
+#include "u.h"
+#include "lib.h"
+#include "kern/dat.h"
+#include "kern/fns.h"
+#include "error.h"
+#include "user.h"
+#include <draw.h>
+#include <memdraw.h>
+#include <keyboard.h>
+#include <cursor.h>
+#include "screen.h"
+#include "wl-inc.h"
+
+#undef close
+
+static Wlwin *snarfwin;
+
+static int clientruning;
+
+Memimage *gscreen;
+
+static Wlwin*
+newwlwin(void)
+{
+ Wlwin *wl;
+
+ wl = mallocz(sizeof *wl, 1);
+ if(wl == nil)
+ sysfatal("malloc Wlwin");
+ wl->dx = 1024;
+ wl->dy = 1024;
+ wl->monx = 1920;
+ wl->mony = 1080;
+ return wl;
+}
+
+void
+wlflush(Wlwin *wl)
+{
+ if(wl->dirty == 1)
+ memcpy(wl->shm_data, gscreen->data->bdata, wl->dx*wl->dy*4);
+
+ wl_surface_attach(wl->surface, wl->screenbuffer, 0, 0);
+ wl_surface_damage(wl->surface, 0, 0, wl->dx, wl->dy);
+ wl_surface_commit(wl->surface);
+ wl->dirty = 0;
+}
+
+void
+wlresize(Wlwin *wl, int x, int y)
+{
+ Rectangle r;
+
+ wl->dx = x;
+ wl->dy = y;
+
+ qlock(&drawlock);
+ wlallocbuffer(wl);
+ r = Rect(0, 0, wl->dx, wl->dy);
+ gscreen = allocmemimage(r, XRGB32);
+ gscreen->clipr = ZR;
+ qunlock(&drawlock);
+
+ screenresize(r);
+
+ qlock(&drawlock);
+ wl->dirty = 1;
+ wlflush(wl);
+ qunlock(&drawlock);
+}
+
+void
+dispatchproc(void *a)
+{
+ Wlwin *wl;
+ wl = a;
+ for(;wl->runing == 1;){
+ wl_display_dispatch(wl->display);
+ }
+}
+
+static Wlwin*
+wlattach(char *label)
+{
+ Rectangle r;
+ Wlwin *wl;
+
+ wl = newwlwin();
+ snarfwin = wl;
+ wl->display = wl_display_connect(NULL);
+ if(wl->display == nil)
+ sysfatal("could not connect to display");
+
+ memimageinit();
+ wlsetcb(wl);
+ wlflush(wl);
+ wlsettitle(wl, label);
+
+ r = Rect(0, 0, wl->dx, wl->dy);
+ gscreen = allocmemimage(r, XRGB32);
+ gscreen->clipr = r;
+ gscreen->r = r;
+ rectclip(&(gscreen->clipr), gscreen->r);
+
+ wldrawcursor(wl, &arrow);
+ wl->runing = 1;
+ kproc("wldispatch", dispatchproc, wl);
+ terminit();
+ wlflush(wl);
+ return wl;
+}
+
+void
+screeninit(void)
+{
+ wlattach("drawterm");
+}
+
+void
+guimain(void)
+{
+ cpubody();
+}
+
+Memdata*
+attachscreen(Rectangle *r, ulong *chan, int *depth, int *width, int *softscreen)
+{
+ Wlwin *wl;
+
+ wl = snarfwin;
+ *r = gscreen->clipr;
+ *chan = gscreen->chan;
+ *depth = gscreen->depth;
+ *width = gscreen->width;
+ *softscreen = 1;
+
+ gscreen->data->ref++;
+ return gscreen->data;
+}
+
+void
+flushmemscreen(Rectangle r)
+{
+ Wlwin *wl;
+
+ wl = snarfwin;
+ wl->dirty = 1;
+ wlflush(wl);
+}
+
+void
+screensize(Rectangle r, ulong chan)
+{
+ snarfwin->dirty = 1;
+}
+
+void
+setcursor(void)
+{
+ wldrawcursor(snarfwin, &cursor);
+}
+
+void
+mouseset(Point p)
+{
+}
+
+char*
+clipread(void)
+{
+ return wlgetsnarf(snarfwin);
+}
+
+int
+clipwrite(char *data)
+{
+ wlsetsnarf(snarfwin, data);
+ return strlen(data);
+}
+
+void
+getcolor(ulong i, ulong *r, ulong *g, ulong *b)
+{
+}
+
+void
+setcolor(ulong index, ulong red, ulong green, ulong blue)
+{
+}
--- /dev/null
+++ b/gui-wl/wl-util.c
@@ -1,0 +1,153 @@
+#define _POSIX_C_SOURCE 200809L
+#include <sys/mman.h>
+#include <wayland-client.h>
+#include <wayland-client-protocol.h>
+#include <linux/input-event-codes.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <xkbcommon/xkbcommon.h>
+#include "xdg-shell-protocol.h"
+
+#include "u.h"
+#include "lib.h"
+#include "kern/dat.h"
+#include "kern/fns.h"
+#include "error.h"
+#include "user.h"
+#include <draw.h>
+#include <memdraw.h>
+#include <keyboard.h>
+#include <cursor.h>
+#include "screen.h"
+#include "wl-inc.h"
+
+#undef close
+
+static void
+randname(char *buf)
+{
+ struct timespec ts;
+ int i;
+
+ clock_gettime(CLOCK_REALTIME, &ts);
+ long r = ts.tv_nsec;
+ for(i=0; i < 6; i++) {
+ buf[i] = 'A'+(r&15)+(r+16)*2;
+ r >>= 5;
+ }
+}
+
+static int
+wlcreateshm(off_t size)
+{
+ char name[] = "/devdraw--XXXXXX";
+ int retries = 100;
+ int fd;
+
+ do {
+ randname(name + strlen(name) - 6);
+ --retries;
+ fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600);
+ if(fd >= 0){
+ shm_unlink(name);
+ if(ftruncate(fd, size) < 0){
+ close(fd);
+ return -1;
+ }
+ return fd;
+ }
+ } while (retries > 0 && errno == EEXIST);
+ return -1;
+}
+
+void
+wlallocpool(Wlwin *wl)
+{
+ int screenx, screeny;
+ int screensize, cursorsize;
+ int depth;
+ int fd;
+
+ if(wl->pool != nil)
+ wl_shm_pool_destroy(wl->pool);
+
+ depth = 4;
+ screenx = wl->dx > wl->monx ? wl->dx : wl->monx;
+ screeny = wl->dy > wl->mony ? wl->dy : wl->mony;
+ screensize = screenx * screeny * depth;
+ cursorsize = 16 * 16 * depth;
+
+ fd = wlcreateshm(screensize+cursorsize);
+ if(fd < 0)
+ sysfatal("could not mk_shm_fd");
+
+ wl->shm_data = mmap(nil, screensize+cursorsize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ if(wl->shm_data == MAP_FAILED)
+ sysfatal("could not mmap shm_data");
+
+ wl->pool = wl_shm_create_pool(wl->shm, fd, screensize+cursorsize);
+ wl->poolsize = screensize+cursorsize;
+ close(fd);
+}
+
+void
+wlallocbuffer(Wlwin *wl)
+{
+ int depth;
+ int size;
+
+ depth = 4;
+ size = wl->dx * wl->dy * depth;
+ if(wl->pool == nil || size+(16*16*depth) > wl->poolsize)
+ wlallocpool(wl);
+
+ if(wl->screenbuffer != nil)
+ wl_buffer_destroy(wl->screenbuffer);
+ if(wl->cursorbuffer != nil)
+ wl_buffer_destroy(wl->cursorbuffer);
+
+ wl->screenbuffer = wl_shm_pool_create_buffer(wl->pool, 0, wl->dx, wl->dy, wl->dx*4, WL_SHM_FORMAT_XRGB8888);
+ wl->cursorbuffer = wl_shm_pool_create_buffer(wl->pool, size, 16, 16, 16*4, WL_SHM_FORMAT_ARGB8888);
+}
+
+static enum {
+ White = 0xFFFFFFFF,
+ Black = 0xFF000000,
+ Green = 0xFF00FF00,
+ Transparent = 0x00000000,
+};
+
+void
+wldrawcursor(Wlwin *wl, Cursorinfo *c)
+{
+ int i, j;
+ int pos, mask;
+ u32int *buf;
+ uint16_t clr[16], set[16];
+
+ buf = wl->shm_data+(wl->dx*wl->dy*4);
+ for(i=0,j=0; i < 16; i++,j+=2){
+ clr[i] = c->clr[j]<<8 | c->clr[j+1];
+ set[i] = c->set[j]<<8 | c->set[j+1];
+ }
+ for(i=0; i < 16; i++){
+ for(j = 0; j < 16; j++){
+ pos = i*16 + j;
+ mask = (1<<16) >> j;
+
+ buf[pos] = Transparent;
+ if(clr[i] & mask)
+ buf[pos] = White;
+ if(set[i] & mask)
+ buf[pos] = Black;
+ }
+ }
+ if(wl->cursorsurface != nil)
+ wl_surface_destroy(wl->cursorsurface);
+ wl->cursorsurface = wl_compositor_create_surface(wl->compositor);
+ wl_surface_attach(wl->cursorsurface, wl->cursorbuffer, 0, 0);
+ wl_surface_commit(wl->cursorsurface);
+ wl_pointer_set_cursor(wl->pointer, wl->pointerserial, wl->cursorsurface, 0, 0);
+}