Compare commits

..

12 commits

Author SHA1 Message Date
Levi Neely
ab8c5116d5 short comment explaining the wayland.c include 2025-04-13 16:38:31 -04:00
Levi Neely
abad9e2dec fix build when WSYSTYPE=wayland in local cfg 2025-04-13 16:38:31 -04:00
David Arroyo
08a4f8abe4 move declarations to first use 2025-03-19 17:33:04 -04:00
David Arroyo
70a7a1c036 Receive key repeat info from compositor
Upgrade to version 4 of the wl_seat interface which adds the
wl_keyboard::repeat_info event allowing the compositor to configure
key repeat delay and rate.
2025-03-19 17:33:04 -04:00
David Arroyo
12e55a0733 Only register repeat callbacks when repeat active
This change splits scroll repeat and key repeat into separate
callbacks, and only registers them when a key is pressed or
scrolling is active, respectively. The key repeat logic is
changed to support key repetitions higher than the frame rate.

The new logic will also accomodate key repeat parameters set by
the compositor, although the comment about us not receiving that
configuration event is still valid.
2025-03-19 17:33:04 -04:00
Ethan Burns
464c522746
Merge branch '9fans:master' into wayland 2025-02-04 21:51:52 -05:00
Ethan Burns
f9430786f0
Merge pull request #3 from droyo/wayland-segfault
Fix unexpected devdraw crashes on sway
2025-02-02 09:11:09 -05:00
David Arroyo
3fdc76f128 Fix unexpected devdraw crashes on sway
I found sway 1.10 and wlroots 0.18.2 will occasionally generate
wl_keyboard::keymap events with an empty keymap. I do not have
a reliable reproduction for this, but it seems to occur when
switching back and forth between workspaces with the keyboard.

This also fixes the issue where devdraw can go into a busy loop if
the compositor goes away and wl_display_dispatch() returns -1.
2025-02-01 22:11:11 +01:00
Anthony Sorace
a5d6857a3b man: don't paginate when using nroff
This tells bin/man to set the register L to very high to avoid pagination
and updates tmac/tmac.an to use that value, if it's set, to set the page
length. This is per Plan 9's rc/bin/man and sys/lib/tmac/tmac.an.
2025-01-29 15:55:23 -05:00
Ethan Burns
5636471dff
Merge pull request #2 from droyo/wl-ack-configure
Fix race condition on attach and mouse tracking after warp
2024-11-28 10:56:46 -05:00
David Arroyo
6dffdd6b08 Fix race condition on attach and mouse track on warp
I tried running the wayland devdraw implementation on Sway 1.10,
wlroots 0.18 and encountered the error:

	xdg_surface#13: error 3: xdg_surface has never been configured

According to https://wayland.app/protocols/xdg-shell#xdg_surface ,
a client must commit a surface without a buffer, *wait* for the first
configure request from the compositor, ack it, and *only then* can
it proceed to attach a buffer to the surface and tell wayland to display it.

This was caused by the following sequence of events:

1. devdraw starts, enters gfx_main
2. `gfx_main` calls `gfx_started`, which spawns the `serveproc` thread
3. `gfx_main` enters `wl_display_dispatch`, flushing any buffered requests to
   the compositor, and enters `wl_display_poll()` to wait for incoming messages
4. `serveproc` calls `rpc_attach`, sets up the surface, and buffers a commit.

The race is between #3 and #4. If #3 happens first, the buffered commit
just sits there until `rpc_flush` is called, which calls `wl_display_flush()`,
but at that point a buffer is attached too quickly for the configure to happen.

This commit fixes the race by adding a `configured` field to the WaylandClient
and using it to guard `rpc_flush`.

In addition, I found that mouse warping, at least in sway, would move
the cursor but future mouse presses would register at the old location
until I moved the mouse. So I added a call to gfx_mousetrack to the end
of `rpc_setmouse`.
2024-11-28 16:30:25 +01:00
Russ Cox
61e362add9 mac: arrange for arm64 run on arm64
Makes 9term.app work again (not put you in an x86_64 jail).
2024-10-22 08:11:37 -04:00
8 changed files with 146 additions and 55 deletions

11
bin/man
View file

@ -18,6 +18,7 @@ fn roff {
}
if not {
Nflag=-N
Lflag='-rL1000i'
}
if(~ $x *eqn*)
preproc=($preproc eqn)
@ -26,15 +27,15 @@ fn roff {
switch($#preproc) {
case 0
{echo -n $FONTS; cat $2< /dev/null} | troff $Nflag -$MAN
{echo -n $FONTS; cat $2< /dev/null} | troff $Nflag $Lflag -$MAN
case 1
{echo -n $FONTS; cat $2< /dev/null} | $preproc | troff $Nflag -$MAN
{echo -n $FONTS; cat $2< /dev/null} | $preproc | troff $Nflag $Lflag -$MAN
case 2
{echo -n $FONTS; cat $2< /dev/null} | $preproc(1) | $preproc(2) | troff $Nflag -$MAN
{echo -n $FONTS; cat $2< /dev/null} | $preproc(1) | $preproc(2) | troff $Nflag $Lflag -$MAN
case 3
{echo -n $FONTS; cat $2< /dev/null} | $preproc(1) | $preproc(2) | $preproc(3) | | troff $Nflag -$MAN
{echo -n $FONTS; cat $2< /dev/null} | $preproc(1) | $preproc(2) | $preproc(3) | | troff $Nflag $Lflag -$MAN
case *
{echo -n $FONTS; cat $2< /dev/null} | $preproc(1) | $preproc(2) | $preproc(3) | | $preproc(4) | troff $Nflag -$MAN
{echo -n $FONTS; cat $2< /dev/null} | $preproc(1) | $preproc(2) | $preproc(3) | | $preproc(4) | troff $Nflag $Lflag -$MAN
}
}

View file

@ -3,12 +3,16 @@ fshell=$(finger $(whoami) | sed -n 's/.*Shell: //p' | sed 1q)
SHELL=${fshell:-$SHELL}
PLAN9=${PLAN9:-/usr/local/plan9}
cd $HOME
arch=x86_64
if arch -arch arm64 date >/dev/null 2>&1; then
arch=arm64
fi
case "$SHELL" in
*/rc)
echo '
if(! ~ $PLAN9/bin $path)
path=($path $PLAN9/bin)
$PLAN9/bin/9term -l -W600x800 &
arch -arch '$arch' $PLAN9/bin/9term -l -W600x800 &
' | $SHELL -l
exit 0
;;
@ -24,6 +28,6 @@ if ! [[ :$PATH: =~ :$PLAN9/bin: ]]
then
PATH=$PATH:$PLAN9/bin
fi
$PLAN9/bin/9term -l -W600x800 &
arch -arch $arch $PLAN9/bin/9term -l -W600x800 &
exit 0

View file

@ -3,6 +3,10 @@
if [ -e ~/.bashrc ] ; then
. ~/.bashrc
fi
arch=x86_64
if arch -arch arm64 date >/dev/null 2>&1; then
arch=arm64
fi
PLAN9=${PLAN9:-/usr/local/plan9}
bin=$PLAN9/bin
@ -10,5 +14,5 @@ IFS=$'\n'
for file in $($bin/macargv)
do
$bin/macedit "$file"
arch -arch $arch $bin/macedit "$file"
done

View file

@ -50,7 +50,12 @@ struct WaylandClient {
// State for key repeat for keyboard keys.
int repeat_rune;
int repeat_next_ms;
int repeat_start_ms;
// Key repeat configuration. Can be changed by
// wl_surface_repeat_info events.
int repeat_interval_ms;
int repeat_delay_ms;
// State for "key repeat" for the mouse scroll wheel.
// This allows touchpad devices to have accelerated scrolling.
@ -65,11 +70,14 @@ struct WaylandClient {
struct xdg_surface *xdg_surface;
struct xdg_toplevel *xdg_toplevel;
// 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;
// Initial configure call is complete
int configured;
// These are called each frame while the key is pressed
// or scrolling is active, to implement key repeat and
// inertial scrolling.
struct wl_callback *wl_key_repeat_callback;
struct wl_callback *wl_scroll_repeat_callback;
// The mouse pointer and the surface for the current cursor.
struct wl_pointer *wl_pointer;
@ -156,7 +164,7 @@ static void registry_global(void *data, struct wl_registry *wl_registry,
&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);
wl_seat = wl_registry_bind(wl_registry, name, &wl_seat_interface, 4);
} 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);
@ -368,10 +376,11 @@ static const struct wl_buffer_listener wl_buffer_listener = {
static void xdg_surface_configure(void *data, struct xdg_surface *xdg_surface, uint32_t serial) {
DEBUG("xdg_surface_configure\n");
const Client* c = data;
const WaylandClient *wl = c->view;
WaylandClient *wl = (WaylandClient*) c->view;
qlock(&wayland_lock);
xdg_surface_ack_configure(wl->xdg_surface, serial);
wl->configured = 1;
qunlock(&wayland_lock);
DEBUG("xdg_surface_configure: returned\n");
@ -418,27 +427,57 @@ static const struct xdg_toplevel_listener xdg_toplevel_listener = {
.close = xdg_toplevel_close,
};
static const struct wl_callback_listener wl_callback_listener;
static const struct wl_callback_listener wl_callback_key_repeat_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) {
static void wl_callback_key_repeat(void *data, struct wl_callback *wl_callback, uint32_t time) {
Client* c = data;
WaylandClient *wl = (WaylandClient*) c->view;
wl_callback_destroy(wl_callback);
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);
int repetitions = 0;
int repeat_rune = wl->repeat_rune;
if (wl->repeat_interval_ms == 0 || wl->repeat_rune == 0) {
goto done;
}
int dt = time - wl->repeat_start_ms;
// There is an initial delay for repetition to start, so
// repeat_start_ms can be in the future.
if (wl->repeat_start_ms < time && wl->repeat_interval_ms <= dt) {
repetitions = dt / wl->repeat_interval_ms;
// Incrementing this way, rather than setting start to now,
// avoids losing fractional time to integer division.
wl->repeat_start_ms += repetitions * wl->repeat_interval_ms;
}
wl_callback = wl_surface_frame(wl->wl_surface);
wl_callback_add_listener(wl_callback, &wl_callback_listener, c);
wl_callback_add_listener(wl_callback, &wl_callback_key_repeat_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;
done:
qunlock(&wayland_lock);
for(int i = 0; i < repetitions; i++) {
gfx_keystroke(c, repeat_rune);
}
}
static const struct wl_callback_listener wl_callback_key_repeat_listener = {
.done = wl_callback_key_repeat,
};
static const struct wl_callback_listener wl_callback_scroll_listener;
static void wl_callback_scroll_repeat(void *data, struct wl_callback *wl_callback, uint32_t time) {
Client* c = data;
WaylandClient *wl = (WaylandClient*) c->view;
wl_callback_destroy(wl_callback);
qlock(&wayland_lock);
int x = wl->mouse_x;
int y = wl->mouse_y;
@ -453,17 +492,20 @@ static void wl_callback_done(void *data, struct wl_callback *wl_callback, uint32
}
}
qunlock(&wayland_lock);
if (repeat_rune) {
gfx_keystroke(c, repeat_rune);
if (wl->repeat_scroll_count > 0) {
wl_callback = wl_surface_frame(wl->wl_surface);
wl_callback_add_listener(wl_callback, &wl_callback_scroll_listener, c);
wl_surface_commit(wl->wl_surface);
}
qunlock(&wayland_lock);
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,
static const struct wl_callback_listener wl_callback_scroll_listener = {
.done = wl_callback_scroll_repeat,
};
void wl_pointer_enter(void *data,struct wl_pointer *wl_pointer, uint32_t serial,
@ -594,6 +636,9 @@ void wl_pointer_axis(void *data, struct wl_pointer *wl_pointer, uint32_t time,
wl->repeat_scroll_button = b;
wl->repeat_scroll_count = mag;
wl->repeat_scroll_next_ms = time + scroll_repeat_ms/wl->repeat_scroll_count;
wl->wl_scroll_repeat_callback = wl_surface_frame(wl->wl_surface);
wl_callback_add_listener(wl->wl_scroll_repeat_callback,
&wl_callback_scroll_listener, c);
}
b |= wl->buttons;
@ -612,8 +657,13 @@ static const struct wl_pointer_listener pointer_listener = {
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");
DEBUG("wl_keyboard_keymap(format=%d, fd=%d, size=%d)\n", format, fd, size);
char *keymap = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
if (keymap == MAP_FAILED) {
DEBUG("wl_keyboard_keymap: %s", strerror(errno));
return;
}
Client* c = data;
WaylandClient *wl = (WaylandClient*) c->view;
qlock(&wayland_lock);
@ -766,12 +816,16 @@ void wl_keyboard_key(void *data, struct wl_keyboard *wl_keyboard,
break;
}
qunlock(&wayland_lock);
if (state == WL_KEYBOARD_KEY_STATE_PRESSED && rune != 0) {
if (wl->repeat_interval_ms && 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);
wl->repeat_start_ms = time + wl->repeat_delay_ms;
wl->wl_key_repeat_callback = wl_surface_frame(wl->wl_surface);
wl_callback_add_listener(wl->wl_key_repeat_callback,
&wl_callback_key_repeat_listener, c);
}
qunlock(&wayland_lock);
if (state == WL_KEYBOARD_KEY_STATE_PRESSED && rune != 0)
gfx_keystroke(c, rune);
}
void wl_keyboard_modifiers(void *data, struct wl_keyboard *wl_keyboard,
@ -788,12 +842,24 @@ void wl_keyboard_modifiers(void *data, struct wl_keyboard *wl_keyboard,
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);
Client* c = data;
WaylandClient *wl = (WaylandClient*) c->view;
int interval = 0;
// rate is in keystrokes per second. Capping to 1k simplifies
// the code.
rate = rate > 1000 ? 1000 : rate;
if (rate > 0)
interval = 1000 / rate;
qlock(&wayland_lock);
wl->repeat_interval_ms = interval;
wl->repeat_delay_ms = delay;
qunlock(&wayland_lock);
}
static const struct wl_keyboard_listener keyboard_listener = {
@ -848,8 +914,9 @@ void gfx_main(void) {
entered_gfx_loop = 1;
gfx_started();
DEBUG("gfx_main: entering loop\n");
while (wl_display_dispatch(wl_display))
while (wl_display_dispatch(wl_display) > 0 || errno == EAGAIN)
;
sysfatal("wl_display_dispatch: %r");
}
static void rpc_resizeimg(Client*) {
@ -981,7 +1048,15 @@ static void rpc_setmouse(Client *c, Point p) {
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;
qunlock(&wayland_lock);
gfx_mousetrack(c, mx, my, mb, nsec()/1000000);
}
static void rpc_topwin(Client*) {
@ -1017,14 +1092,16 @@ static void rpc_flush(Client *c, Rectangle r) {
WaylandClient *wl = (WaylandClient*) c->view;
qlock(&wayland_lock);
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);
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);
}
qunlock(&wayland_lock);
}
@ -1049,6 +1126,8 @@ Memimage *rpc_attach(Client *c, char *label, char *winsize) {
c->impl = &wayland_impl;
c->view = wl;
wl->repeat_interval_ms = key_repeat_ms;
wl->repeat_delay_ms = key_repeat_ms;
wl->wl_surface = wl_compositor_create_surface(wl_compositor);
wl->xdg_surface = xdg_wm_base_get_xdg_surface(xdg_wm_base, wl->wl_surface);
@ -1058,9 +1137,6 @@ Memimage *rpc_attach(Client *c, char *label, char *winsize) {
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);
@ -1091,6 +1167,7 @@ Memimage *rpc_attach(Client *c, char *label, char *winsize) {
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);
qunlock(&wayland_lock);
return wl->memimage;

View file

@ -1,6 +1,6 @@
#!/bin/sh
if [ "x$1" = "xx11" ]; then
if [ "x$1" = "xx11" -o "x$1" = "xwayland" ]; then
if [ "x$2" = "x" ]; then
i="-I/usr/include"
else

View file

@ -48,7 +48,7 @@ echo 'WSYSTYPE='$WSYSTYPE
echo 'X11='$X11
echo 'X11H='$X11H
if [ $WSYSTYPE = x11 ]; then
if [ $WSYSTYPE = x11 -o $WSYSTYPE = wayland ]; then
echo 'CFLAGS=$CFLAGS '$X11H
echo 'HFILES=$HFILES $XHFILES'
XO=`ls x11-*.c 2>/dev/null | sed 's/\.c$/.o/'`

View file

@ -0,0 +1,3 @@
// note: the fontsrv implementation for X11 and wayland are the same.
// this non-standard include is an alternative to copying the file.
#include "x11.c"

View file

@ -479,7 +479,9 @@
.if t \{.ds R ®
.ds S \s\n()S
..\}
.if n \{.nr )L 11i
.if n \{.ie \nL<=0 .nr )L 11i
.el \{.nr )L \nLu
.nr V 0\}
.nr LL 6.5i
.nr )O .463i
.if '\*(.T'think' \{.nrLL 80n