plan9port/src/cmd/devdraw/wayland.c

1171 lines
32 KiB
C
Raw Normal View History

2024-08-18 09:21:03 -04:00
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <memdraw.h>
#include <memlayer.h>
#include <keyboard.h>
#include <mouse.h>
#include <cursor.h>
#include <thread.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <linux/input-event-codes.h>
#include <wayland-client.h>
#include <xkbcommon/xkbcommon.h>
#include "bigarrow.h"
#include "devdraw.h"
#include "wayland-pointer-constraints.h"
#include "wayland-xdg-decoration.h"
#include "wayland-xdg-shell.h"
// alt+click and ctl+click are mapped to mouse buttons
// to support single button mice.
#define ALT_BUTTON 1
#define CTL_BUTTON 2
struct WaylandBuffer {
int w;
int h;
int size;
char *data;
struct wl_buffer* wl_buffer;
};
typedef struct WaylandBuffer WaylandBuffer;
struct WaylandClient {
// The screen image written to by the client, and read by this driver.
Memimage *memimage;
// The current mouse coordinates and a bitmask of held buttons.
int mouse_x;
int mouse_y;
int buttons;
// Booleans indicating whether control or alt are currently held.
int ctl;
int alt;
// State for key repeat for keyboard keys.
int repeat_rune;
int repeat_next_ms;
// State for "key repeat" for the mouse scroll wheel.
// This allows touchpad devices to have accelerated scrolling.
int repeat_scroll_button;
int repeat_scroll_count;
int repeat_scroll_rate_ms;
int repeat_scroll_next_ms;
// The Wayland surface for this window
// and its corresponding xdg objects.
struct wl_surface *wl_surface;
struct xdg_surface *xdg_surface;
struct xdg_toplevel *xdg_toplevel;
// Initial configure call is complete
int configured;
2024-08-18 09:21:03 -04:00
// A callback when wl_surface is ready for the next frame.
// Currently this is not used for drawing,
// but as a hack for implementing key repeat
// without needing to implement our own timer thread/events.
struct wl_callback *wl_callback;
// The mouse pointer and the surface for the current cursor.
struct wl_pointer *wl_pointer;
struct wl_surface *wl_surface_cursor;
// The keyboard and xkb state used
// for mapping scan codes to key codes.
struct wl_keyboard *wl_keyboard;
struct xkb_context *xkb_context;
struct xkb_keymap *xkb_keymap;
struct xkb_state *xkb_state;
};
typedef struct WaylandClient WaylandClient;
static QLock wayland_lock;
// Required globals wayland objects.
static struct wl_display *wl_display;
static struct wl_output *wl_output;
static struct wl_registry *wl_registry;
static struct wl_shm *wl_shm;
static struct wl_compositor *wl_compositor;
static struct xdg_wm_base *xdg_wm_base;
static struct wl_seat *wl_seat;
static struct wl_data_device_manager *wl_data_device_manager;
static struct wl_data_device *wl_data_device;
static char *snarf;
uint32_t keyboard_enter_serial;
2024-08-18 09:21:03 -04:00
// Optional global wayland objects.
// Need to NULL check them before using.
static struct zxdg_decoration_manager_v1 *decoration_manager;
static struct zwp_pointer_constraints_v1 *pointer_constraints;
// The wl output scale factor reported by wl_output.
// We only set it if we get th event before entering the graphics loop.
// Once we enter the loop, we never change it to avoid the need
// to reason about which scale a buffer was created with.
int wl_output_scale_factor = 1;
int entered_gfx_loop = 0;
// The delay in ms which a key must be held to begin repeating..
int key_repeat_delay_ms = 500;
// The number of ms between repeats of a repeating key.
int key_repeat_ms = 100;
// The maximum number of ms between repeats of a scroll.
// This is scaled by the number of repeating scrolls pending.
int scroll_repeat_ms = 100;
// A pool of xrgb888 buffers used for drawing to the screen.
// When drawing, we give ownership of the buffer's memory to the compositor.
// The compositor notifies us asynchronously when it is done reading the buffer.
// In the case that we need to draw (rpc_flush) before the buffer is ready,
// we will need to allocate a whole new buffer to draw to.
// We use this pool to avoid the need to setup a new shared memory buffer
// each time this happens.
#define N_XRGB8888_BUFFERS 3
struct WaylandBuffer *xrgb8888_buffers[N_XRGB8888_BUFFERS];
int wayland_debug = 0;
#define DEBUG(...) \
do { \
if (wayland_debug) { \
fprint(2, __VA_ARGS__); \
} \
} while(0)
static void registry_global(void *data, struct wl_registry *wl_registry,
uint32_t name, const char *interface, uint32_t version) {
if (strcmp(interface, wl_output_interface.name) == 0) {
wl_output = wl_registry_bind(wl_registry, name, &wl_output_interface, 2);
} else if (strcmp(interface, wl_shm_interface.name) == 0) {
wl_shm = wl_registry_bind(wl_registry, name, &wl_shm_interface, 1);
} else if (strcmp(interface, wl_compositor_interface.name) == 0) {
wl_compositor = wl_registry_bind(wl_registry, name,
&wl_compositor_interface, 4);
} else if (strcmp(interface, xdg_wm_base_interface.name) == 0) {
xdg_wm_base = wl_registry_bind(wl_registry, name,
&xdg_wm_base_interface, 1);
} else if (strcmp(interface, wl_seat_interface.name) == 0) {
wl_seat = wl_registry_bind(wl_registry, name, &wl_seat_interface, 1);
} else if (strcmp(interface, wl_data_device_manager_interface.name) == 0) {
wl_data_device_manager = wl_registry_bind(wl_registry, name, &wl_data_device_manager_interface, 2);
2024-08-18 09:21:03 -04:00
} else if (strcmp(interface, zxdg_decoration_manager_v1_interface.name) == 0) {
decoration_manager = wl_registry_bind(wl_registry, name,
&zxdg_decoration_manager_v1_interface, 1);
} else if (strcmp(interface, zwp_pointer_constraints_v1_interface.name) == 0) {
pointer_constraints = wl_registry_bind(wl_registry, name,
&zwp_pointer_constraints_v1_interface, 1);
}
}
static void registry_global_remove(void *data, struct wl_registry *wl_registry,
uint32_t name) {}
static const struct wl_registry_listener wl_registry_listener = {
.global = registry_global,
.global_remove = registry_global_remove,
};
void wl_output_geometry(void *data, struct wl_output *wl_output,
int32_t x, int32_t y, int32_t physical_width, int32_t physical_height,
int32_t subpixel, const char *make, const char *model, int32_t transform) {}
void wl_output_mode(void *data, struct wl_output *wl_output, uint32_t flags,
int32_t width, int32_t height, int32_t refresh) {}
void wl_output_done(void *data, struct wl_output *wl_output) {}
void wl_output_scale(void *data, struct wl_output *wl_output, int32_t factor) {
DEBUG("wl_output_scale(factor=%d)\n", factor);
qlock(&wayland_lock);
if (!entered_gfx_loop) {
wl_output_scale_factor = factor;
}
qunlock(&wayland_lock);
}
static const struct wl_output_listener wl_output_listener = {
.geometry = wl_output_geometry,
.mode = wl_output_mode,
.done = wl_output_done,
.scale = wl_output_scale,
};
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,
};
void wl_data_device_listener_data_offer(void *data,
struct wl_data_device *wl_data_device, struct wl_data_offer *id) {}
void wl_data_device_listener_data_enter(void *data,
struct wl_data_device *wl_data_device, uint32_t serial,
struct wl_surface *surface, wl_fixed_t x, wl_fixed_t y,
struct wl_data_offer *id) {}
void wl_data_device_listener_data_leave(void *data,
struct wl_data_device *wl_data_device) {}
void wl_data_device_listener_data_motion(void *data,
struct wl_data_device *wl_data_device, uint32_t time,
wl_fixed_t x, wl_fixed_t y) {}
void wl_data_device_listener_data_drop(void *data,
struct wl_data_device *wl_data_device) {}
void wl_data_device_listener_selection(void *data,
struct wl_data_device *wl_data_device, struct wl_data_offer *id) {
DEBUG("wl_data_device_listener_selection\n");
if (id == NULL) {
qlock(&wayland_lock);
free(snarf);
snarf = NULL;
qunlock(&wayland_lock);
DEBUG("wl_data_device_listener_selection: no data\n");
return;
}
int fds[2];
if (pipe(fds) < 0) {
sysfatal("Failed to create pipe");
}
wl_data_offer_receive(id, "text/plain", fds[1]);
close(fds[1]);
wl_display_roundtrip(wl_display);
qlock(&wayland_lock);
int total = 0;
snarf = NULL;
for (; ;) {
char buf[128];
int n = read(fds[0], &buf, sizeof(buf));
if (n < 0 && errno == EAGAIN) {
continue;
}
if (n < 0) {
sysfatal("Read failed");
}
if (n == 0) {
break;
}
// +1 to ensure it's always at least null terminated.
char *tmp = calloc(1, total + n + 1);
if (snarf != NULL) {
strncpy(tmp, snarf, total);
}
memcpy(tmp+total, buf, n);
total += n;
snarf = tmp;
}
DEBUG("wl_data_device_listener_selection: read [%s]\n", snarf);
qunlock(&wayland_lock);
close(fds[0]);
}
static const struct wl_data_device_listener wl_data_device_listener = {
.data_offer = wl_data_device_listener_data_offer,
.enter = wl_data_device_listener_data_enter,
.leave = wl_data_device_listener_data_leave,
.motion = wl_data_device_listener_data_motion,
.drop = wl_data_device_listener_data_drop,
.selection = wl_data_device_listener_selection,
};
void wl_data_source_target(void *data,
struct wl_data_source *wl_data_source,
const char *mime_type) {}
void wl_data_source_send(void *data,
struct wl_data_source *wl_data_source,
const char *mime_type, int32_t fd) {
DEBUG("wl_data_source_send(mime_type=%s)\n", mime_type);
if (strcmp(mime_type, "text/plain") != 0) {
DEBUG("unknown mime type\n");
close(fd);
return;
}
qlock(&wayland_lock);
int total = 0;
if (snarf != NULL) {
total = strlen(snarf);
}
DEBUG("wl_data_source_send: writing %d bytes\n", total);
char *p = snarf;
while (total > 0) {
int n = write(fd, p, total);
if (n < 0 && errno == EAGAIN) {
continue;
}
if (n < 0) {
sysfatal("Write error");
}
p += n;
total -= n;
}
qunlock(&wayland_lock);
close(fd);
}
void wl_data_source_cancelled(void *data, struct wl_data_source *wl_data_source) {}
static const struct wl_data_source_listener wl_data_source_listener = {
.target = wl_data_source_target,
.send = wl_data_source_send,
.cancelled = wl_data_source_cancelled,
};
2024-08-18 09:21:03 -04:00
void delete_buffer(WaylandBuffer *b) {
munmap(b->data, b->size);
wl_buffer_destroy(b->wl_buffer);
free(b);
}
static void wl_buffer_release(void *data, struct wl_buffer *wl_buffer) {
if (data == NULL) {
wl_buffer_destroy(wl_buffer);
return;
}
for (int i = 0; i < N_XRGB8888_BUFFERS; i++) {
if (xrgb8888_buffers[i] == NULL) {
xrgb8888_buffers[i] = (WaylandBuffer*) data;
return;
}
}
delete_buffer((WaylandBuffer*) data);
}
static const struct wl_buffer_listener wl_buffer_listener = {
.release = wl_buffer_release,
};
static void xdg_surface_configure(void *data, struct xdg_surface *xdg_surface, uint32_t serial) {
DEBUG("xdg_surface_configure\n");
const Client* c = data;
WaylandClient *wl = (WaylandClient*) c->view;
2024-08-18 09:21:03 -04:00
qlock(&wayland_lock);
xdg_surface_ack_configure(wl->xdg_surface, serial);
wl->configured = 1;
2024-08-18 09:21:03 -04:00
qunlock(&wayland_lock);
DEBUG("xdg_surface_configure: returned\n");
}
static const struct xdg_surface_listener xdg_surface_listener = {
.configure = xdg_surface_configure,
};
void xdg_toplevel_configure(void *data, struct xdg_toplevel *xdg_toplevel,
int32_t width, int32_t height, struct wl_array *states) {
DEBUG("xdg_toplevel_configure(width=%d, height=%d)\n", width, height);
if (width == 0 || height == 0) {
return;
}
Client* c = data;
WaylandClient *wl = (WaylandClient*) c->view;
qlock(&wayland_lock);
width *= wl_output_scale_factor;
height *= wl_output_scale_factor;
Rectangle r = Rect(0, 0, width, height);
if (eqrect(r, wl->memimage->r)) {
// The size didn't change, so nothing to do.
qunlock(&wayland_lock);
return;
}
// The size changed, so allocate a new Memimage and notify the client.
wl->memimage = _allocmemimage(r, XRGB32);
c->mouserect = r;
qunlock(&wayland_lock);
gfx_replacescreenimage(c, wl->memimage);
}
void xdg_toplevel_close(void *data, struct xdg_toplevel *xdg_toplevel) {
DEBUG("xdg_toplevel_close\n");
threadexitsall(nil);
}
static const struct xdg_toplevel_listener xdg_toplevel_listener = {
.configure = xdg_toplevel_configure,
.close = xdg_toplevel_close,
};
static const struct wl_callback_listener wl_callback_listener;
// The callback is called per-frame.
// It is currently only used to implement key repeat.
static void wl_callback_done(void *data, struct wl_callback *wl_callback, uint32_t time) {
Client* c = data;
WaylandClient *wl = (WaylandClient*) c->view;
qlock(&wayland_lock);
// Request another callback for the next frame.
// TODO: Only request a callback if we are still repeating a key.
wl_callback_destroy(wl_callback);
wl_callback = wl_surface_frame(wl->wl_surface);
wl_callback_add_listener(wl_callback, &wl_callback_listener, c);
wl_surface_commit(wl->wl_surface);
int repeat_rune = 0;
if (wl->repeat_rune && time >= wl->repeat_next_ms) {
repeat_rune = wl->repeat_rune;
wl->repeat_next_ms = time + key_repeat_ms;
}
int x = wl->mouse_x;
int y = wl->mouse_y;
int repeat_scroll_button = 0;
if (wl->repeat_scroll_button && time >= wl->repeat_scroll_next_ms) {
repeat_scroll_button = wl->repeat_scroll_button | wl->buttons;
wl->repeat_scroll_count--;
if (wl->repeat_scroll_count == 0) {
wl->repeat_scroll_button = 0;
} else {
wl->repeat_scroll_next_ms = time + scroll_repeat_ms/wl->repeat_scroll_count;
2024-08-18 09:21:03 -04:00
}
}
qunlock(&wayland_lock);
if (repeat_rune) {
gfx_keystroke(c, repeat_rune);
}
if (repeat_scroll_button) {
gfx_mousetrack(c, x, y, repeat_scroll_button, (uint) time);
}
}
static const struct wl_callback_listener wl_callback_listener = {
.done = wl_callback_done,
};
void wl_pointer_enter(void *data,struct wl_pointer *wl_pointer, uint32_t serial,
struct wl_surface *surface, wl_fixed_t surface_x, wl_fixed_t surface_y) {
Client* c = data;
WaylandClient *wl = (WaylandClient*) c->view;
qlock(&wayland_lock);
wl->mouse_x = wl_fixed_to_int(surface_x) * wl_output_scale_factor;
wl->mouse_y = wl_fixed_to_int(surface_y) * wl_output_scale_factor;
wl_pointer_set_cursor(wl->wl_pointer, serial, wl->wl_surface_cursor, 0, 0);
qunlock(&wayland_lock);
// We don't call gfx_mousetrack here, since we don't have the time.
}
void wl_pointer_leave(void *data, struct wl_pointer *wl_pointer,
uint32_t serial, struct wl_surface *surface) {
Client* c = data;
WaylandClient *wl = (WaylandClient*) c->view;
qlock(&wayland_lock);
wl->buttons = 0;
wl->repeat_scroll_button = 0;
qunlock(&wayland_lock);
}
void wl_pointer_motion(void *data, struct wl_pointer *wl_pointer, uint32_t time,
wl_fixed_t surface_x, wl_fixed_t surface_y){
Client* c = data;
WaylandClient *wl = (WaylandClient*) c->view;
qlock(&wayland_lock);
wl->repeat_scroll_button = 0;
wl->repeat_scroll_count = 0;
wl->mouse_x = wl_fixed_to_int(surface_x) * wl_output_scale_factor;
wl->mouse_y = wl_fixed_to_int(surface_y) * wl_output_scale_factor;
int x = wl->mouse_x;
int y = wl->mouse_y;
int b = wl->buttons;
qunlock(&wayland_lock);
gfx_mousetrack(c, x, y, b, (uint) time);
}
void wl_pointer_button(void *data, struct wl_pointer *wl_pointer, uint32_t serial,
uint32_t time, uint32_t button, uint32_t state) {
DEBUG("wl_pointer_button(button=%d)\n", (int) button);
Client* c = data;
WaylandClient *wl = (WaylandClient*) c->view;
qlock(&wayland_lock);
wl->repeat_scroll_button = 0;
wl->repeat_scroll_count = 0;
int mask = 0;
switch (button) {
case BTN_LEFT:
mask = 1<<0;
break;
case BTN_MIDDLE:
mask = 1<<1;
break;
case BTN_RIGHT:
mask = 1<<2;
break;
case BTN_4:
mask = 1<<3;
break;
case BTN_5:
mask = 1<<4;
break;
default:
DEBUG("wl_pointer_button: unknown button: %d\n", button);
qunlock(&wayland_lock);
return;
}
if (button == BTN_LEFT) {
if (wl->ctl) {
mask = 1 << CTL_BUTTON;
} else if (wl->alt) {
mask = 1 << ALT_BUTTON;
}
}
DEBUG("wl_pointer_button: mask=%x\n", mask);
switch (state) {
case WL_POINTER_BUTTON_STATE_PRESSED:
wl->buttons |= mask;
break;
case WL_POINTER_BUTTON_STATE_RELEASED:
wl->buttons &= ~mask;
break;
default:
fprint(2, "Unknown button state: %d\n", state);
}
int x = wl->mouse_x;
int y = wl->mouse_y;
int b = wl->buttons;
qunlock(&wayland_lock);
DEBUG("wl_pointer_button: gfx_trackmouse(x=%d, y=%d, b=%d)\n", x, y, b);
gfx_mousetrack(c, x, y, b, (uint) time);
}
void wl_pointer_axis(void *data, struct wl_pointer *wl_pointer, uint32_t time,
uint32_t axis, wl_fixed_t value_fixed) {
double value = wl_fixed_to_double(value_fixed);
Client* c = data;
WaylandClient *wl = (WaylandClient*) c->view;
qlock(&wayland_lock);
int x = wl->mouse_x;
int y = wl->mouse_y;
wl->repeat_scroll_button = 0;
wl->repeat_scroll_count = 0;
int b = 0;
if (value < 0) {
b |= 1 << 3;
} else if (value > 0) {
b |= 1 << 4;
}
int mag = fabs(value);
if (mag > 1) {
wl->repeat_scroll_button = b;
wl->repeat_scroll_count = mag;
wl->repeat_scroll_next_ms = time + scroll_repeat_ms/wl->repeat_scroll_count;
}
b |= wl->buttons;
qunlock(&wayland_lock);
gfx_mousetrack(c, x, y, b, (uint) time);
}
static const struct wl_pointer_listener pointer_listener = {
.enter = wl_pointer_enter,
.leave = wl_pointer_leave,
.motion = wl_pointer_motion,
.button = wl_pointer_button,
.axis = wl_pointer_axis,
};
void wl_keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard,
uint32_t format, int32_t fd, uint32_t size) {
DEBUG("wl_keyboard_keymap\n");
char *keymap = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
Client* c = data;
WaylandClient *wl = (WaylandClient*) c->view;
qlock(&wayland_lock);
if (wl->xkb_keymap != NULL) {
xkb_keymap_unref(wl->xkb_keymap);
}
wl->xkb_keymap = xkb_keymap_new_from_string(wl->xkb_context, keymap,
XKB_KEYMAP_FORMAT_TEXT_V1,
XKB_KEYMAP_COMPILE_NO_FLAGS);
if (wl->xkb_state != NULL) {
xkb_state_unref(wl->xkb_state);
}
wl->xkb_state = xkb_state_new(wl->xkb_keymap);
qunlock(&wayland_lock);
munmap(keymap, size);
close(fd);
}
void wl_keyboard_enter(void *data, struct wl_keyboard *wl_keyboard,
uint32_t serial, struct wl_surface *surface, struct wl_array *keys) {
DEBUG("wl_keyboard_enter\n");
qlock(&wayland_lock);
keyboard_enter_serial = serial;
qunlock(&wayland_lock);
2024-08-18 09:21:03 -04:00
}
void wl_keyboard_leave(void *data, struct wl_keyboard *wl_keyboard,
uint32_t serial, struct wl_surface *surface) {
DEBUG("wl_keyboard_leave\n");
Client* c = data;
WaylandClient *wl = (WaylandClient*) c->view;
qlock(&wayland_lock);
wl->ctl = 0;
wl->alt = 0;
wl->repeat_rune = 0;
qunlock(&wayland_lock);
}
void wl_keyboard_key(void *data, struct wl_keyboard *wl_keyboard,
uint32_t serial, uint32_t time, uint32_t key, uint32_t state) {
Client* c = data;
WaylandClient *wl = (WaylandClient*) c->view;
qlock(&wayland_lock);
wl->repeat_rune = 0;
wl->repeat_scroll_button = 0;
wl->repeat_scroll_count = 0;
key += 8; // Add 8 to translate Linux scan code to xkb code.
uint32_t rune = xkb_state_key_get_utf32(wl->xkb_state, key);
xkb_keysym_t keysym = xkb_state_key_get_one_sym(wl->xkb_state, key);
if (wayland_debug) {
char name[256];
xkb_keysym_get_name(keysym, &name[0], 256);
char *state_str = WL_KEYBOARD_KEY_STATE_PRESSED ? "down" : "up";
DEBUG("wl_keyboard_key: keysym=%s, rune=0x%x, state=%s\n",
name, rune, state_str);
}
switch (keysym) {
case XKB_KEY_Return:
rune = '\n';
break;
case XKB_KEY_Alt_L:
case XKB_KEY_Alt_R:
rune = Kalt;
if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
wl->alt = 1;
} else {
wl->alt = 0;
}
if (wl->buttons) {
if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
wl->buttons |= 1 << ALT_BUTTON;
} else {
wl->buttons &= ~(1 << ALT_BUTTON);
}
int x = wl->mouse_x;
int y = wl->mouse_y;
int b = wl->buttons;
qunlock(&wayland_lock);
gfx_mousetrack(c, x, y, b, (uint) time);
return;
}
break;
case XKB_KEY_Control_L:
case XKB_KEY_Control_R:
// For some reason, Kctl is not used;
// it results in drawing a replacement character.
// Common ctl combos still work.
// For example ctl+w sends rune 0x17
// which erases the previous word.
rune = 0; // Kctl;
if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
wl->ctl = 1;
} else {
wl->ctl = 0;
}
if (wl->buttons) {
if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
wl->buttons |= 1 << CTL_BUTTON;
} else {
wl->buttons &= ~(1 << CTL_BUTTON);
}
int x = wl->mouse_x;
int y = wl->mouse_y;
int b = wl->buttons;
qunlock(&wayland_lock);
gfx_mousetrack(c, x, y, b, (uint) time);
return;
}
break;
case XKB_KEY_Delete:
rune = Kdel;
break;
case XKB_KEY_Escape:
rune = Kesc;
break;
case XKB_KEY_Home:
rune = Khome;
break;
case XKB_KEY_End:
rune = Kend;
break;
case XKB_KEY_Prior:
rune = Kpgup;
break;
case XKB_KEY_Next:
rune = Kpgdown;
break;
case XKB_KEY_Up:
rune = Kup;
break;
case XKB_KEY_Down:
rune = Kdown;
break;
case XKB_KEY_Left:
rune = Kleft;
break;
case XKB_KEY_Right:
rune = Kright;
break;
}
qunlock(&wayland_lock);
if (state == WL_KEYBOARD_KEY_STATE_PRESSED && rune != 0) {
wl->repeat_rune = rune;
wl->repeat_next_ms = time + key_repeat_delay_ms;
gfx_keystroke(c, rune);
}
}
void wl_keyboard_modifiers(void *data, struct wl_keyboard *wl_keyboard,
uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched,
uint32_t mods_locked, uint32_t group) {
DEBUG("wl_keyboard_modifiers\n");
Client* c = data;
WaylandClient *wl = (WaylandClient*) c->view;
qlock(&wayland_lock);
xkb_state_update_mask(wl->xkb_state, mods_depressed,
mods_latched, mods_locked, 0, 0, group);
qunlock(&wayland_lock);
}
// TODO: Use this to set key repeat.
// Currently we don't bind the keyboard with the correct version to get this event.
void wl_keyboard_repeat_info(void *data, struct wl_keyboard *wl_keyboard,
int32_t rate, int32_t delay) {
DEBUG("wl_keyboard_repeat_info(rate=%d, delay=%d)\n",
(int) rate, (int) delay);
}
static const struct wl_keyboard_listener keyboard_listener = {
.keymap = wl_keyboard_keymap,
.enter = wl_keyboard_enter,
.leave = wl_keyboard_leave,
.key = wl_keyboard_key,
.modifiers = wl_keyboard_modifiers,
.repeat_info = wl_keyboard_repeat_info,
};
void gfx_main(void) {
DEBUG("gfx_main called\n");
wl_display = wl_display_connect(NULL);
wl_registry = wl_display_get_registry(wl_display);
wl_registry_add_listener(wl_registry, &wl_registry_listener, NULL);
wl_display_roundtrip(wl_display);
// Ensure required globals were correctly bound.
if (wl_display == NULL) {
sysfatal("Unable to get Wayland display");
}
if (wl_registry == NULL) {
sysfatal("Unable to get Wayland registry");
}
if (wl_output == NULL) {
sysfatal("Unable to bind wl_output");
}
if (wl_shm == NULL) {
sysfatal("Unable to bind wl_shm");
}
if (wl_compositor == NULL) {
sysfatal("Unable to bind wl_compositor");
}
if (xdg_wm_base == NULL) {
sysfatal("Unable to bind xdg_wm_base");
}
if (wl_seat == NULL) {
sysfatal("Unable to bind wl_seat");
}
if (wl_data_device_manager == NULL) {
sysfatal("Unable to bind wl_data_device_manager");
}
2024-08-18 09:21:03 -04:00
wl_output_add_listener(wl_output, &wl_output_listener, NULL);
xdg_wm_base_add_listener(xdg_wm_base, &xdg_wm_base_listener, NULL);
wl_data_device = wl_data_device_manager_get_data_device(
wl_data_device_manager, wl_seat);
wl_data_device_add_listener(wl_data_device, &wl_data_device_listener, NULL);
2024-08-18 09:21:03 -04:00
wl_display_roundtrip(wl_display);
entered_gfx_loop = 1;
gfx_started();
DEBUG("gfx_main: entering loop\n");
while (wl_display_dispatch(wl_display))
;
}
static void rpc_resizeimg(Client*) {
DEBUG("rpc_resizeimg\n");
}
static void rpc_resizewindow(Client*, Rectangle) {
DEBUG("rpc_resizewindow\n");
}
static int next_shm = 0;
WaylandBuffer *new_buffer(int w, int h, int format) {
int stride = w * 4;
int size = stride * h;
// Create an anonymous shared memory file.
char name[128];
snprintf(name, 128, "/acme_wl_shm-%d-%d", getpid(), next_shm++);
int fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600);
if (fd < 0) {
sysfatal("shm_open failed");
}
shm_unlink(name);
// Set the file's size.
int ret;
do {
ret = ftruncate(fd, size);
} while (ret < 0 && errno == EINTR);
if (ret < 0) {
sysfatal("ftruncate failed");
}
char *d = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
if (d == MAP_FAILED) {
sysfatal("mmap failed");
}
WaylandBuffer *b = malloc(sizeof(WaylandBuffer));
b->w = w;
b->h = h;
b->size = size;
b->data = d;
struct wl_shm_pool *p = wl_shm_create_pool(wl_shm, fd, size);
b->wl_buffer = wl_shm_pool_create_buffer(p, 0, w, h, stride, format);
wl_shm_pool_destroy(p);
close(fd);
return b;
}
void wayland_set_cursor(WaylandClient *wl, Cursor *cursor) {
// Convert bitmap to ARGB.
// Yes, this is super clunky. Sorry about that.
const uint32_t fg = 0xFF000000;
const uint32_t a = 0x00FFFFFF;
uint32_t data[8*32];
int j = 0;
for (int i = 0; i < 32; i++) {
char c = cursor->set[i];
data[j++] = (c >>7) & 1 == 1 ? fg : a;
data[j++] = (c >> 6) & 1 == 1 ? fg : a;
data[j++] = (c >> 5) & 1 == 1 ? fg : a;
data[j++] = (c >> 4) & 1 == 1 ? fg : a;
data[j++] = (c >> 3) & 1 == 1 ? fg : a;
data[j++] = (c >> 2) & 1 == 1 ? fg : a;
data[j++] = (c >> 1) & 1 == 1 ? fg : a;
data[j++] = (c >> 0) & 1 == 1 ? fg : a;
}
WaylandBuffer *b = new_buffer(16, 16, WL_SHM_FORMAT_ARGB8888);
memcpy(b->data, (char*) &data[0], b->size);
// We don't want to bother saving this buffer in xrgb8888_buffers.
// Unmap and use NULL for it's listener data.
// This will cause it to be destroyed when it is released.
munmap(b->data, b->size);
wl_buffer_add_listener(b->wl_buffer, &wl_buffer_listener, NULL);
wl_surface_attach(wl->wl_surface_cursor, b->wl_buffer, 0, 0);
wl_surface_damage_buffer(wl->wl_surface_cursor, 0, 0, 16, 16);
wl_surface_commit(wl->wl_surface_cursor);
}
static void rpc_setcursor(Client *c, Cursor *cursor, Cursor2*) {
DEBUG("rpc_setcursor\n");
WaylandClient *wl = (WaylandClient*) c->view;
qlock(&wayland_lock);
if (cursor == NULL) {
cursor = &bigarrow;
}
wayland_set_cursor(wl, cursor);
qunlock(&wayland_lock);
}
static void rpc_setlabel(Client *c, char *label) {
WaylandClient *wl = (WaylandClient*) c->view;
qlock(&wayland_lock);
xdg_toplevel_set_title(wl->xdg_toplevel, label);
qunlock(&wayland_lock);
}
static void rpc_setmouse(Client *c, Point p) {
if (pointer_constraints == NULL) {
// If there is no pointer constraints extension,
// we cannot warp the mouse.
return;
}
WaylandClient *wl = (WaylandClient*) c->view;
qlock(&wayland_lock);
// Wayland does not directly support warping the pointer.
// Instead, we use (misuse?) the pointer constraints extension,
// which allows sending the compositor a new pointer location
// hint when the pointer is unlocked.
// We lock the pointer, and immediately unlock it with a hint
// of the desired wrap location.
struct zwp_locked_pointer_v1 *lock = zwp_pointer_constraints_v1_lock_pointer(
pointer_constraints, wl->wl_surface, wl->wl_pointer, NULL,
ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
int x = wl_fixed_from_int(p.x / wl_output_scale_factor);
int y = wl_fixed_from_int(p.y / wl_output_scale_factor);
zwp_locked_pointer_v1_set_cursor_position_hint(lock, x, y);
wl_surface_commit(wl->wl_surface);
zwp_locked_pointer_v1_destroy(lock);
wl->mouse_x = wl_fixed_to_int(x) * wl_output_scale_factor;
wl->mouse_y = wl_fixed_to_int(y) * wl_output_scale_factor;
int mx = wl->mouse_x;
int my = wl->mouse_y;
int mb = wl->buttons;
2024-08-18 09:21:03 -04:00
qunlock(&wayland_lock);
gfx_mousetrack(c, mx, my, mb, nsec()/1000000);
2024-08-18 09:21:03 -04:00
}
static void rpc_topwin(Client*) {
DEBUG("rpc_topwin\n");
}
static void rpc_bouncemouse(Client*, Mouse) {
DEBUG("rpc_bouncemouse\n");
}
// Must be called with the lock held.
WaylandBuffer *get_xrgb8888_buffer(int w, int h) {
for (int i = 0; i < N_XRGB8888_BUFFERS; i++) {
if (xrgb8888_buffers[i] == NULL) {
continue;
}
// Delete any cached buffers that are not the right size.
if (xrgb8888_buffers[i]->w != w || xrgb8888_buffers[i]->h != h) {
delete_buffer(xrgb8888_buffers[i]);
xrgb8888_buffers[i] = NULL;
continue;
}
WaylandBuffer *b = xrgb8888_buffers[i];
xrgb8888_buffers[i] = NULL;
return b;
}
WaylandBuffer *b = new_buffer(w, h, WL_SHM_FORMAT_XRGB8888);
wl_buffer_add_listener(b->wl_buffer, &wl_buffer_listener, b);
return b;
}
static void rpc_flush(Client *c, Rectangle r) {
WaylandClient *wl = (WaylandClient*) c->view;
qlock(&wayland_lock);
if (wl->configured) {
int w = Dx(wl->memimage->r);
int h = Dy(wl->memimage->r);
WaylandBuffer *b = get_xrgb8888_buffer(w, h);
memcpy(b->data, (char*) wl->memimage->data->bdata, b->size);
wl_surface_attach(wl->wl_surface, b->wl_buffer, 0, 0);
wl_surface_damage_buffer(wl->wl_surface, r.min.x, r.min.y, Dx(r), Dy(r));
wl_surface_commit(wl->wl_surface);
wl_display_flush(wl_display);
}
2024-08-18 09:21:03 -04:00
qunlock(&wayland_lock);
}
static ClientImpl wayland_impl = {
rpc_resizeimg,
rpc_resizewindow,
rpc_setcursor,
rpc_setlabel,
rpc_setmouse,
rpc_topwin,
rpc_bouncemouse,
rpc_flush
};
Memimage *rpc_attach(Client *c, char *label, char *winsize) {
DEBUG("rpc_attach(%s)\n", label);
qlock(&wayland_lock);
WaylandClient *wl = calloc(1, sizeof(WaylandClient));
c->impl = &wayland_impl;
c->view = wl;
wl->wl_surface = wl_compositor_create_surface(wl_compositor);
wl->xdg_surface = xdg_wm_base_get_xdg_surface(xdg_wm_base, wl->wl_surface);
xdg_surface_add_listener(wl->xdg_surface, &xdg_surface_listener, c);
wl->xdg_toplevel = xdg_surface_get_toplevel(wl->xdg_surface);
xdg_toplevel_add_listener(wl->xdg_toplevel, &xdg_toplevel_listener, c);
xdg_toplevel_set_title(wl->xdg_toplevel, label);
wl->wl_callback = wl_surface_frame(wl->wl_surface);
wl_callback_add_listener(wl->wl_callback, &wl_callback_listener, c);
wl->wl_pointer = wl_seat_get_pointer(wl_seat);
wl_pointer_add_listener(wl->wl_pointer, &pointer_listener, c);
wl->wl_surface_cursor = wl_compositor_create_surface(wl_compositor);
wayland_set_cursor(wl, &bigarrow);
wl->wl_keyboard = wl_seat_get_keyboard(wl_seat);
wl_keyboard_add_listener(wl->wl_keyboard, &keyboard_listener, c);
wl->xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
// If the xdg decorations extension is available,
// enable server-side decorations.
// Otherwise there will be no window decorations
// (title, resize, buttons, etc.).
if (decoration_manager != NULL) {
struct zxdg_toplevel_decoration_v1 *d =
zxdg_decoration_manager_v1_get_toplevel_decoration(
decoration_manager, wl->xdg_toplevel);
zxdg_toplevel_decoration_v1_set_mode(d,
ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
}
// TODO: parse winsize.
int w = 640*wl_output_scale_factor;
int h = 480*wl_output_scale_factor;
Rectangle r = Rect(0, 0, w, h);
wl->memimage = _allocmemimage(r, XRGB32);
c->mouserect = r;
c->displaydpi = 110 * wl_output_scale_factor;
wl_surface_set_buffer_scale(wl->wl_surface, wl_output_scale_factor);
wl_surface_commit(wl->wl_surface);
wl_display_flush(wl_display);
2024-08-18 09:21:03 -04:00
qunlock(&wayland_lock);
return wl->memimage;
}
char *rpc_getsnarf(void) {
DEBUG("rpc_getsnarf\n");
qlock(&wayland_lock);
if (snarf == NULL) {
qunlock(&wayland_lock);
return NULL;
}
int n = strlen(snarf);
char *copy = calloc(1, n+1);
strncpy(copy, snarf, n);
qunlock(&wayland_lock);
return copy;
2024-08-18 09:21:03 -04:00
}
void rpc_putsnarf(char *snarf_in) {
2024-08-18 09:21:03 -04:00
DEBUG("rpc_putsnarf\n");
qlock(&wayland_lock);
int n = strlen(snarf_in);
free(snarf);
snarf = calloc(1, n+1);
strncpy(snarf, snarf_in, n);
struct wl_data_source *source =
wl_data_device_manager_create_data_source(wl_data_device_manager);
wl_data_source_add_listener(source, &wl_data_source_listener, NULL);
wl_data_source_offer(source, "text/plain");
wl_data_device_set_selection(wl_data_device, source, keyboard_enter_serial);
qunlock(&wayland_lock);
2024-08-18 09:21:03 -04:00
}
void rpc_shutdown(void) {
DEBUG("rpc_shutdown\n");
}
void rpc_gfxdrawlock(void) {
qlock(&wayland_lock);
}
void rpc_gfxdrawunlock(void) {
qunlock(&wayland_lock);
}
int cloadmemimage(Memimage *i, Rectangle r, uchar *data, int ndata) {
return _cloadmemimage(i, r, data, ndata);
}
int loadmemimage(Memimage *i, Rectangle r, uchar *data, int ndata) {
return _loadmemimage(i, r, data, ndata);
}
int unloadmemimage(Memimage *i, Rectangle r, uchar *data, int ndata) {
return _unloadmemimage(i, r, data, ndata);
}