wl_backend: standalone DRM/KMS mode, fix pointer focus and keyboard delivery

- Use wlr_backend_autocreate for standalone mode (from TTY) instead of
  headless-only. Gives real DRM/KMS output + libinput.
- wl_update_pointer_focus: skip client surface focus when pointer is
  over an internal frame_buf (menu, titlebar). Prevents client from
  stealing cursor/events from WM chrome.
- wl_focus_set: ensure seat keyboard is set and flush clients before
  sending keyboard.enter so client has processed wl_seat.capabilities.
- Renderer and allocator use the autocreated backend in standalone mode.
This commit is contained in:
Levi Neely 2026-05-14 18:39:18 +02:00
parent 213f5a6528
commit 62b8e4254e
2 changed files with 45 additions and 21 deletions

View File

@ -1768,10 +1768,13 @@ handle_backend_new_input(struct wl_listener *listener, void *data)
}
case WLR_INPUT_DEVICE_POINTER: {
struct wlr_pointer *ptr = wlr_pointer_from_input_device(dev);
/* Nested backend delivers absolute motion events (host cursor
* position normalised to [0,1]); no relative motion needed. */
wl_state.real_pointer_dev = dev;
wl_state.pointer_dev = dev;
/* Relative motion (libinput / standalone DRM) */
wl_state.pointer_motion_listener.notify = handle_pointer_motion;
wl_signal_add(&ptr->events.motion,
&wl_state.pointer_motion_listener);
/* Absolute motion (nested Wayland backend) */
wl_state.real_pointer_motion_abs_listener.notify =
handle_pointer_motion_absolute;
wl_signal_add(&ptr->events.motion_absolute,
@ -1782,11 +1785,10 @@ handle_backend_new_input(struct wl_listener *listener, void *data)
wl_state.real_pointer_frame_listener.notify = handle_pointer_frame;
wl_signal_add(&ptr->events.frame,
&wl_state.real_pointer_frame_listener);
/* Phase 29: axis (scroll wheel) from real nested pointer. */
wl_state.real_pointer_axis_listener.notify = handle_pointer_axis;
wl_signal_add(&ptr->events.axis,
&wl_state.real_pointer_axis_listener);
wwarning("wl_backend: real pointer wired from nested backend");
wwarning("wl_backend: real pointer wired");
break;
}
default:
@ -2070,7 +2072,7 @@ wl_display_open(const char *name)
wl_state.wl_backend_inner = wlr_wl_backend_create(
wl_display_get_event_loop(wl_state.display), NULL);
if (!wl_state.wl_backend_inner) {
wwarning("wl_backend: failed to create nested Wayland backend; falling back to headless");
wwarning("wl_backend: failed to create nested Wayland backend; falling back to standalone");
wl_state.is_nested = 0;
}
}
@ -2083,15 +2085,28 @@ wl_display_open(const char *name)
wl_state.backend = multi;
wwarning("wl_backend: nested Wayland compositor mode");
} else {
wl_state.backend = wl_state.headless_inner;
wwarning("wl_backend: headless compositor mode");
/* Standalone: use autocreate to get DRM/KMS + libinput on real hardware.
* Clear DISPLAY and WAYLAND_DISPLAY so wlroots goes straight to DRM. */
unsetenv("DISPLAY");
unsetenv("WAYLAND_DISPLAY");
struct wlr_session *session = NULL;
wl_state.backend = wlr_backend_autocreate(
wl_display_get_event_loop(wl_state.display), &session);
if (!wl_state.backend) {
wwarning("wl_backend: wlr_backend_autocreate failed; falling back to headless");
wl_state.backend = wl_state.headless_inner;
} else {
wwarning("wl_backend: standalone DRM/KMS compositor mode");
}
}
/* Renderer: prefer the Wayland backend (EGL) in nested mode; fall back
* to headless (pixman software) for the pure-headless case. */
/* Renderer: use the appropriate backend as source. */
{
struct wlr_backend *render_src = wl_state.wl_backend_inner
? wl_state.wl_backend_inner : wl_state.headless_inner;
struct wlr_backend *render_src;
if (wl_state.wl_backend_inner)
render_src = wl_state.wl_backend_inner;
else
render_src = wl_state.backend;
wl_state.renderer = wlr_renderer_autocreate(render_src);
}
if (!wl_state.renderer) {
@ -2103,10 +2118,10 @@ wl_display_open(const char *name)
return NULL;
}
/* Allocator (Phase 18) — needed for wlr_output_init_render. */
/* Allocator — needed for wlr_output_init_render. */
{
struct wlr_backend *alloc_src = wl_state.wl_backend_inner
? wl_state.wl_backend_inner : wl_state.headless_inner;
? wl_state.wl_backend_inner : wl_state.backend;
wl_state.allocator = wlr_allocator_autocreate(alloc_src, wl_state.renderer);
}
if (!wl_state.allocator)
@ -4135,6 +4150,13 @@ wl_focus_set(WNativeWindow win, int revert_to, unsigned long timestamp)
}
if (!v->xdg_surface || !v->xdg_surface->surface)
return;
if (wl_state.keyboard_dev) {
struct wlr_keyboard *kb = wlr_keyboard_from_input_device(wl_state.keyboard_dev);
wlr_seat_set_keyboard(wl_state.seat, kb);
}
/* Flush pending events to client so it has processed wl_seat.capabilities
* and bound wl_keyboard before we send keyboard.enter. */
wl_display_flush_clients(wl_state.display);
wlr_seat_keyboard_notify_enter(wl_state.seat,
v->xdg_surface->surface,
NULL, 0, NULL);
@ -4902,17 +4924,19 @@ wl_update_pointer_focus(uint32_t time_msec)
double sx, sy;
struct wl_toplevel_view *view = NULL;
struct wlr_surface *surface = view_surface_at(
wl_state.pointer_x, wl_state.pointer_y, &sx, &sy, &view);
struct wlr_surface *surface = NULL;
/* If pointer is over an internal frame_buf (menu, titlebar, etc.),
* don't send pointer events to any client surface. */
struct wl_frame_buf *fb = frame_buf_at(wl_state.pointer_x, wl_state.pointer_y);
if (!fb)
surface = view_surface_at(
wl_state.pointer_x, wl_state.pointer_y, &sx, &sy, &view);
static struct wlr_surface *last_focused_surface = NULL;
if (surface) {
if (surface != last_focused_surface) {
wwarning("wl_backend: pointer focus entered surface %p at %.1f,%.1f",
(void *)surface, sx, sy);
last_focused_surface = surface;
/* Reset to default cursor; client will override via
* request_set_cursor after receiving wl_pointer.enter. */
wl_set_cursor_image("left_ptr");
}
wlr_seat_pointer_notify_enter(wl_state.seat, surface, sx, sy);
@ -4920,7 +4944,6 @@ wl_update_pointer_focus(uint32_t time_msec)
wlr_seat_pointer_notify_frame(wl_state.seat);
} else {
if (last_focused_surface) {
wwarning("wl_backend: pointer left all surfaces");
last_focused_surface = NULL;
}
wlr_seat_pointer_clear_focus(wl_state.seat);

View File

@ -915,8 +915,9 @@ void wMenuMapAt(WMenu * menu, int x, int y, int keyboard)
wm_backend->window_move((WNativeWindow)menu->frame->core->window, x, y);
menu->frame_x = x;
menu->frame_y = y;
if (menu->frame->core->window)
if (menu->frame->core->window) {
wm_backend->window_map((WNativeWindow)menu->frame->core->window);
}
wRaiseFrame(menu->frame->core);
menu->flags.mapped = 1;
wMenuPaint(menu);