9front - general discussion about 9front
 help / color / mirror / Atom feed
* [9front] [drawterm] Wayland support and misc patches
@ 2021-10-14  1:59 Jacob Moody
  2021-10-15  2:22 ` Stanley Lieber
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: Jacob Moody @ 2021-10-14  1:59 UTC (permalink / raw)
  To: 9front

[-- Attachment #1: Type: text/plain, Size: 1056 bytes --]

Hello,

I've written a wayland graphical backend for drawterm and have been dogfooding it for
a little bit now. I think most of the larger bugs have been worked out of it.
There is also a patch for adding pipewire support for devaudio.
For both of these there is a patch for adding a CONF=linux target

While working on these I found that there is code in libc/ that expects
to be compiled with -DPTHREAD to avoid resorting to spin locks.
I think we might want to add -DPTHREAD to the unix target CFLAGS.
However adding the define on my machine caused gcc to get confused about
the ordering of the members for kern/devmouse.c:/^Cursorinfo\tarrow/
There is a patch that specifies the members, which seemed to fix the issue. But I am sure what is causing this.

Also I am not sure why there was a sleep within the kern/term.c:/^resizeproc/ loop, but I noticed the delay
when working on the wayland backend. There is a patch for removing that sleep.

Not sure which, if any, of these are desirable to have upstream but wanted to offer them up.


Thanks,
moody

[-- Attachment #2: drawterm-wl.patch --]
[-- Type: text/x-patch, Size: 25298 bytes --]

diff --git a/gui-wl/Makefile b/gui-wl/Makefile
new file mode 100644
index 0000000..bc15a51
--- /dev/null
+++ b/gui-wl/Makefile
@@ -0,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)
+
diff --git a/gui-wl/wl-cb.c b/gui-wl/wl-cb.c
new file mode 100644
index 0000000..e50cf05
--- /dev/null
+++ b/gui-wl/wl-cb.c
@@ -0,0 +1,583 @@
+#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 struct {
+	Rendez z;
+	QLock lk;
+	int active;
+	long keytime;
+	int32_t key;
+	int32_t rate;
+	int32_t delay;
+} repeatstate;
+
+static int
+isactive(void *arg)
+{
+	return repeatstate.active;
+}
+
+void
+repeatproc(void*)
+{
+	int ms;
+	long keytime;
+
+	for(;;){
+		ksleep(&repeatstate.z, isactive, 0);
+		qlock(&repeatstate.lk);
+		keytime = repeatstate.keytime;
+		qunlock(&repeatstate.lk);
+		osmsleep(repeatstate.delay);
+
+repeat:
+		qlock(&repeatstate.lk);
+		if(repeatstate.active == 0 || keytime != repeatstate.keytime){
+			qunlock(&repeatstate.lk);
+			continue;
+		}
+		ms = 1000/repeatstate.rate;
+		kbdkey(repeatstate.key, 0);
+		kbdkey(repeatstate.key, 1);
+		qunlock(&repeatstate.lk);
+		osmsleep(ms);
+		goto repeat;
+	}
+}
+
+static void
+keyboard_repeat_info(void *data, struct wl_keyboard *wl_keyboard, int32_t rate, int32_t delay)
+{
+	qlock(&repeatstate.lk);
+	repeatstate.rate = rate;
+	repeatstate.delay = delay;
+	qunlock(&repeatstate.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;
+	case XKB_KEY_Up:
+		utf32 = Kup;
+		break;
+	case XKB_KEY_Down:
+		utf32 = Kdown;
+		break;
+	case XKB_KEY_Left:
+		utf32 = Kleft;
+		break;
+	case XKB_KEY_Right:
+		utf32 = Kright;
+		break;
+	default:
+		utf32 = xkb_keysym_to_utf32(keysym);
+		break;
+	}
+	if(utf32 == 0)
+		return;
+
+	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);
+
+	kbdkey(utf32, state);
+	qlock(&repeatstate.lk);
+	repeatstate.active = state;
+	repeatstate.keytime = time;
+	repeatstate.key = utf32;
+	qunlock(&repeatstate.lk);
+	wakeup(&repeatstate.z);
+}
+
+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 = {
+	.keymap = keyboard_keymap,
+	.enter = keyboard_enter,
+	.leave = keyboard_leave,
+	.key = keyboard_key,
+	.modifiers = keyboard_modifiers,
+	.repeat_info = keyboard_repeat_info,
+};
+
+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;
+	if(wl->cursorsurface == nil)
+		return;
+	wl_pointer_set_cursor(wl->pointer, wl->pointerserial, wl->cursorsurface, -cursor.offset.x, -cursor.offset.y);
+}
+
+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 void
+seat_handle_name(void *data, struct wl_seat *seat, const char *name)
+{
+
+}
+
+static const struct wl_seat_listener seat_listener = {
+	.capabilities = seat_handle_capabilities,
+	.name = seat_handle_name,
+};
+
+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, 4);
+		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;
+
+	//Wayland doesn't do keyboard repeat, but also may
+	//not tell us what the user would like, so we
+	//pick some sane defaults.
+	repeatstate.delay = 200;
+	repeatstate.rate = 20;
+	kproc("keyboard repeat", repeatproc, 0);
+
+	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, "drawterm");
+
+	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;
+}
diff --git a/gui-wl/wl-inc.h b/gui-wl/wl-inc.h
new file mode 100644
index 0000000..ea056b5
--- /dev/null
+++ b/gui-wl/wl-inc.h
@@ -0,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*);
diff --git a/gui-wl/wl-screen.c b/gui-wl/wl-screen.c
new file mode 100644
index 0000000..c5f322d
--- /dev/null
+++ b/gui-wl/wl-screen.c
@@ -0,0 +1,199 @@
+#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 *gwin;
+
+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();
+	gwin = 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);
+
+	wl->runing = 1;
+	kproc("wldispatch", dispatchproc, wl);
+	terminit();
+	qlock(&drawlock);
+	wlflush(wl);
+	qunlock(&drawlock);
+	return wl;
+}
+
+void
+screeninit(void)
+{
+	wlattach("drawterm");
+}
+
+void
+guimain(void)
+{
+	cpubody();
+}
+
+Memdata*
+attachscreen(Rectangle *r, ulong *chan, int *depth, int *width, int *softscreen)
+{
+	*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 = gwin;
+	wl->dirty = 1;
+	wlflush(wl);
+}
+
+void
+screensize(Rectangle r, ulong chan)
+{
+	gwin->dirty = 1;
+}
+
+void
+setcursor(void)
+{
+	qlock(&drawlock);
+	wldrawcursor(gwin, &cursor);
+	qunlock(&drawlock);
+}
+
+void
+mouseset(Point p)
+{
+}
+
+char*
+clipread(void)
+{
+	return wlgetsnarf(gwin);
+}
+
+int
+clipwrite(char *data)
+{
+	wlsetsnarf(gwin, data);
+	return strlen(data);
+}
+
+void
+getcolor(ulong i, ulong *r, ulong *g, ulong *b)
+{
+}
+
+void
+setcolor(ulong index, ulong red, ulong green, ulong blue)
+{
+}
diff --git a/gui-wl/wl-util.c b/gui-wl/wl-util.c
new file mode 100644
index 0000000..47e0b45
--- /dev/null
+++ b/gui-wl/wl-util.c
@@ -0,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[] = "/drawterm--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);
+}
+
+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, -c->offset.x, -c->offset.y);
+}

[-- Attachment #3: drawterm-pipweireaudio.patch --]
[-- Type: text/x-patch, Size: 3611 bytes --]

diff --git a/kern/devaudio-pipewire.c b/kern/devaudio-pipewire.c
new file mode 100644
index 0000000..7d754fb
--- /dev/null
+++ b/kern/devaudio-pipewire.c
@@ -0,0 +1,177 @@
+#include	"u.h"
+#include	"lib.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"error.h"
+#include	"devaudio.h"
+
+#undef long
+#include <pipewire/pipewire.h>
+#include <spa/param/audio/format-utils.h>
+
+static struct {
+	Lock lk;
+	Rendez z;
+	int init;
+	struct pw_main_loop *loop;
+	struct pw_stream *output;
+
+	char buf[8192];
+	int written; /* 0 means empty buffer */
+} pwstate;
+
+static char *argv[] = { "drawterm" };
+static int argc = 1;
+
+static void
+on_process(void *data)
+{
+	struct pw_buffer *b;
+	struct spa_buffer *buf;
+	int16_t *dst;
+
+	lock(&pwstate.lk);
+	if(pwstate.written == 0){
+		unlock(&pwstate.lk);
+		return;
+	}
+	b = pw_stream_dequeue_buffer(pwstate.output);
+
+	buf = b->buffer;
+	dst = buf->datas[0].data;
+
+	memcpy(dst, pwstate.buf, pwstate.written);
+	buf->datas[0].chunk->offset = 0;
+	buf->datas[0].chunk->stride = sizeof(int16_t) * 2;
+	buf->datas[0].chunk->size = pwstate.written;
+
+	pw_stream_queue_buffer(pwstate.output, b);
+	pwstate.written = 0;
+	unlock(&pwstate.lk);
+	wakeup(&pwstate.z);
+}
+
+static const struct pw_stream_events stream_events = {
+	PW_VERSION_STREAM_EVENTS,
+	.process = on_process,
+};
+
+static void
+pwproc(void *arg)
+{
+	struct pw_main_loop *loop;
+
+	loop = arg;
+	pw_main_loop_run(loop);
+}
+
+void
+audiodevopen(void)
+{
+	const struct spa_pod *params[1];
+	struct spa_pod_builder b = SPA_POD_BUILDER_INIT(pwstate.buf, sizeof(pwstate.buf));
+	int err;
+
+	lock(&pwstate.lk);
+	if(pwstate.init > 0){
+		kproc("pipewire main loop", pwproc, pwstate.loop);
+		unlock(&pwstate.lk);
+		return;
+	}
+
+	pwstate.init++;
+	pw_init(&argc, (char***)&argv);
+	pwstate.loop = pw_main_loop_new(NULL);
+	if(pwstate.loop == NULL)
+		sysfatal("could not create loop");
+	pwstate.output = pw_stream_new_simple(
+		pw_main_loop_get_loop(pwstate.loop),
+		"drawterm",
+		pw_properties_new(
+			PW_KEY_MEDIA_TYPE, "Audio",
+			PW_KEY_MEDIA_CATEGORY, "Playback",
+			PW_KEY_MEDIA_ROLE, "Music",
+			NULL),
+		&stream_events,
+		NULL);
+
+	if(pwstate.output == NULL){
+		unlock(&pwstate.lk);
+		error("could not create pipewire output");
+		return;
+	}
+	params[0] = spa_format_audio_raw_build(&b, SPA_PARAM_EnumFormat,
+		&SPA_AUDIO_INFO_RAW_INIT(
+			.format = SPA_AUDIO_FORMAT_S16_LE,
+			.channels = 2,
+			.rate = 44100 ));
+
+	err = pw_stream_connect(pwstate.output,
+		PW_DIRECTION_OUTPUT,
+		PW_ID_ANY,
+		PW_STREAM_FLAG_AUTOCONNECT |
+		PW_STREAM_FLAG_MAP_BUFFERS |
+		PW_STREAM_FLAG_RT_PROCESS,
+		params, 1);
+
+	unlock(&pwstate.lk);
+	if(err < 0){
+		error("could not connect pipewire stream");
+		return;
+	}
+
+	kproc("pipewire main loop", pwproc, pwstate.loop);
+}
+
+void
+audiodevclose(void)
+{
+	lock(&pwstate.lk);
+	pw_main_loop_quit(pwstate.loop);
+	unlock(&pwstate.lk);
+}
+
+int
+audiodevread(void *a, int n)
+{
+	error("no record support");
+	return -1;
+}
+
+static int
+canwrite(void *arg)
+{
+	return pwstate.written == 0;
+}
+
+int
+audiodevwrite(void *a, int n)
+{
+	if(n > sizeof(pwstate.buf)){
+		error("write too large");
+		return -1;
+	}
+	lock(&pwstate.lk);
+	if(pwstate.written != 0){
+		unlock(&pwstate.lk);
+		sleep(&pwstate.z, canwrite, 0);
+		lock(&pwstate.lk);
+	}
+	memcpy(pwstate.buf, a, n);
+	pwstate.written = n;
+	unlock(&pwstate.lk);
+	return n;
+}
+
+void
+audiodevsetvol(int what, int left, int right)
+{
+	error("no volume support");
+}
+
+void
+audiodevgetvol(int what, int *left, int *right)
+{
+	error("no volume support");
+}
+

[-- Attachment #4.1: Type: text/plain, Size: 381 bytes --]

from postmaster@4ess:
The following attachment had content that we can't
prove to be harmless.  To avoid possible automatic
execution, we changed the content headers.
The original header was:

	Content-Type: text/x-patch; charset=UTF-8; name="drawterm-pthreadcursor.patch"
	Content-Disposition: attachment; filename="drawterm-pthreadcursor.patch"
	Content-Transfer-Encoding: base64

[-- Attachment #4.2: drawterm-pthreadcursor.patch.suspect --]
[-- Type: application/octet-stream, Size: 799 bytes --]

[-- Attachment #5.1: Type: text/plain, Size: 373 bytes --]

from postmaster@4ess:
The following attachment had content that we can't
prove to be harmless.  To avoid possible automatic
execution, we changed the content headers.
The original header was:

	Content-Type: text/x-patch; charset=UTF-8; name="drawterm-termsleep.patch"
	Content-Disposition: attachment; filename="drawterm-termsleep.patch"
	Content-Transfer-Encoding: base64

[-- Attachment #5.2: drawterm-termsleep.patch.suspect --]
[-- Type: application/octet-stream, Size: 238 bytes --]

[-- Attachment #6.1: Type: text/plain, Size: 373 bytes --]

from postmaster@4ess:
The following attachment had content that we can't
prove to be harmless.  To avoid possible automatic
execution, we changed the content headers.
The original header was:

	Content-Type: text/x-patch; charset=UTF-8; name="drawterm-linuxmake.patch"
	Content-Disposition: attachment; filename="drawterm-linuxmake.patch"
	Content-Transfer-Encoding: base64

[-- Attachment #6.2: drawterm-linuxmake.patch.suspect --]
[-- Type: application/octet-stream, Size: 718 bytes --]

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [9front] [drawterm] Wayland support and misc patches
  2021-10-14  1:59 [9front] [drawterm] Wayland support and misc patches Jacob Moody
@ 2021-10-15  2:22 ` Stanley Lieber
  2021-10-15 11:20 ` cinap_lenrek
  2021-10-17  3:49 ` Stephen Gregoratto
  2 siblings, 0 replies; 4+ messages in thread
From: Stanley Lieber @ 2021-10-15  2:22 UTC (permalink / raw)
  To: 9front

On October 14, 2021 1:59:54 AM UTC, Jacob Moody <moody@mail.posixcafe.org> wrote:
>Hello,
>
>I've written a wayland graphical backend for drawterm and have been dogfooding it for
>a little bit now. I think most of the larger bugs have been worked out of it.
>There is also a patch for adding pipewire support for devaudio.
>For both of these there is a patch for adding a CONF=linux target
>
>While working on these I found that there is code in libc/ that expects
>to be compiled with -DPTHREAD to avoid resorting to spin locks.
>I think we might want to add -DPTHREAD to the unix target CFLAGS.
>However adding the define on my machine caused gcc to get confused about
>the ordering of the members for kern/devmouse.c:/^Cursorinfo\tarrow/
>There is a patch that specifies the members, which seemed to fix the issue. But I am sure what is causing this.
>
>Also I am not sure why there was a sleep within the kern/term.c:/^resizeproc/ loop, but I noticed the delay
>when working on the wayland backend. There is a patch for removing that sleep.
>
>Not sure which, if any, of these are desirable to have upstream but wanted to offer them up.
>
>
>Thanks,
>moody
don't have a machine to test with yet but this will come in handy around december.

thanks!

sl

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [9front] [drawterm] Wayland support and misc patches
  2021-10-14  1:59 [9front] [drawterm] Wayland support and misc patches Jacob Moody
  2021-10-15  2:22 ` Stanley Lieber
@ 2021-10-15 11:20 ` cinap_lenrek
  2021-10-17  3:49 ` Stephen Gregoratto
  2 siblings, 0 replies; 4+ messages in thread
From: cinap_lenrek @ 2021-10-15 11:20 UTC (permalink / raw)
  To: 9front


> Also I am not sure why there was a sleep within the
> kern/term.c:/^resizeproc/ loop, but I noticed the delay
> when working on the wayland backend. There is a patch for
> removing that sleep.

it is for debouncing.

that is, when we get a zillion window resize events per
second (which can happen with some window managers),
we do not want to flood that to the remote side as it
is a very expensive operation in devdraw model,
as every graphical program needs to be notified
and re allocate a its screen and then redraw.

so with the sleep, we limit the rate of devdraw
resize to once per second.

also note the location of the delay. it will do the
first resize *immediately*. but then wait at least
a second until we check if it got resized again.

so this is only affecting window managers that
continuously generate resize events for the window.

--
cinap

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [9front] [drawterm] Wayland support and misc patches
  2021-10-14  1:59 [9front] [drawterm] Wayland support and misc patches Jacob Moody
  2021-10-15  2:22 ` Stanley Lieber
  2021-10-15 11:20 ` cinap_lenrek
@ 2021-10-17  3:49 ` Stephen Gregoratto
  2 siblings, 0 replies; 4+ messages in thread
From: Stephen Gregoratto @ 2021-10-17  3:49 UTC (permalink / raw)
  To: 9front

This patch works really well for me on sway 1.6.1, with only one issue.
When mousing over the drawterm window, the mouse pointer won't show up
until rio is open and I click into the window. This doesn't happen on
the X11 backend.

Also, are you thinking of sending a similar patch to plan9port? I would
be interested in that happening since their X11 backend is a bit buggy
when run under a wayland compositor.
-- 
Stephen Gregoratto

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2021-10-17  3:57 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-10-14  1:59 [9front] [drawterm] Wayland support and misc patches Jacob Moody
2021-10-15  2:22 ` Stanley Lieber
2021-10-15 11:20 ` cinap_lenrek
2021-10-17  3:49 ` Stephen Gregoratto

9front - general discussion about 9front

This inbox may be cloned and mirrored by anyone:

	git clone --mirror https://inbox.vuxu.org/9front

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V1 9front 9front/ https://inbox.vuxu.org/9front \
		9front@9front.org
	public-inbox-index 9front

Example config snippet for mirrors.
Newsgroup available over NNTP:
	nntp://inbox.vuxu.org/vuxu.archive.9front


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git