[-- 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, ®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, "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 --]
diff --git a/kern/devmouse.c b/kern/devmouse.c
index 85825a9..dfbabf8 100644
--- a/kern/devmouse.c
+++ b/kern/devmouse.c
@@ -11,14 +11,14 @@
Mouseinfo mouse;
Cursorinfo cursor;
Cursorinfo arrow = {
- 0,
- { -1, -1 },
- { 0xFF, 0xFF, 0x80, 0x01, 0x80, 0x02, 0x80, 0x0C,
+ .lk = 0,
+ .offset = { -1, -1 },
+ .clr = { 0xFF, 0xFF, 0x80, 0x01, 0x80, 0x02, 0x80, 0x0C,
0x80, 0x10, 0x80, 0x10, 0x80, 0x08, 0x80, 0x04,
0x80, 0x02, 0x80, 0x01, 0x80, 0x02, 0x8C, 0x04,
0x92, 0x08, 0x91, 0x10, 0xA0, 0xA0, 0xC0, 0x40,
},
- { 0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFC, 0x7F, 0xF0,
+ .set = { 0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFC, 0x7F, 0xF0,
0x7F, 0xE0, 0x7F, 0xE0, 0x7F, 0xF0, 0x7F, 0xF8,
0x7F, 0xFC, 0x7F, 0xFE, 0x7F, 0xFC, 0x73, 0xF8,
0x61, 0xF0, 0x60, 0xE0, 0x40, 0x40, 0x00, 0x00,
[-- 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 --]
diff --git a/kern/term.c b/kern/term.c
index afc012a..39db71a 100644
--- a/kern/term.c
+++ b/kern/term.c
@@ -117,7 +117,6 @@ resizeproc(void *arg)
screenwin();
deletescreenimage();
resetscreenimage();
- osmsleep(1000);
}
}
[-- 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 --]
diff --git a/Make.linux b/Make.linux
new file mode 100644
index 0000000..c9ad231
--- /dev/null
+++ b/Make.linux
@@ -0,0 +1,21 @@
+# Linux
+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 -DPTHREAD $(PTHREAD) -I/usr/include/pipewire-0.3 -I/usr/include/spa-0.2 -D_REENTRANT -O2
+O=o
+OS=posix
+GUI=wl
+LDADD=-lwayland-client -lxkbcommon -ggdb -lm -lrt -lpipewire-0.3
+LDFLAGS=$(PTHREAD)
+TARG=drawterm
+# AUDIO=none
+AUDIO=pipewire
+
+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)