shithub: drawterm

Download patch

ref: f3e0b4598f147e1bdc75d7a3557de29d71e8c2d6
parent: bee4db6507fdf31bddfa98d26a673e135dfd28ad
author: Jacob Moody <moody@posixcafe.org>
date: Sun Oct 10 03:48:35 EDT 2021

add wayland backend

diff: cannot open b/gui-wl//null: file does not exist: 'b/gui-wl//null'
--- /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, &registry_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);
+}