The pointer_grab call for Alt+drag (move/resize via MOD_MASK on client
surface) was passing owner_events=0. wl_pointer_grab only sets
grab_type=3 and clears seat pointer focus when owner_events is True;
with 0 the grab returned success but left no grab state, so motion
events continued flowing to the client instead of the WM drag loop.
Pass 1 (True) to match the calls in resizebarMouseDown and
titlebarMouseDown, which work correctly.
When the WM modifier key (Alt by default, configurable via ModifierKey)
is held during a button press over a client surface, do not forward the
event to the client via wlr_seat_pointer_notify_button. The event has
already been enqueued for the WM policy layer via W_WL_EnqueueEvent;
frameMouseDown will call wMouseMoveWindow (LMB) or wMouseResizeWindow
(RMB) as appropriate. Without this suppression the client application
receives the Alt+click and may act on it instead of the WM.
Three related fixes for the Wayland backend:
wl_handlers.c: populate u.button.state with keyboard modifiers
Button events were built with state=0 after memset, so all modifier-
dependent logic in window.c (resizebarMouseDown, frameMouseDown,
titlebarMouseDown) received no modifier information. Read the current
depressed modifier mask from the seat keyboard and copy it directly --
wlroots WLR_MODIFIER_* bits match the WM_MOD_* layout exactly.
wl_framebuf.c: fix frame_buf_hit to resolve child sub-windows
frame_buf_at() uses wlr_scene_node_at() which skips frame_buf children
whose scene_buf has no committed buffer (resizebar, titlebar pixels are
composited into the parent frame buffer). The hit always resolved to
the top-level frame, so events were dispatched to frameMouseDown instead
of resizebarMouseDown, and resize never started. After frame_buf_at
returns a top-level frame, do a coordinate-based child search and return
the smallest (most specific) child that contains the pointer -- the same
strategy frame_buf_button_at uses for buttons.
wl_events.c: fix wl_coords_translate to return frame-relative coords
The stub was a pass-through returning root-relative coordinates unchanged.
wMouseResizeWindow calls coords_translate(root, frame->core->window, ...)
to get frame-relative click coordinates for getResizeDirection(). Look up
the dst window in the frame_buf list and subtract its absolute position.
Three interrelated bugs caused clicks to land in the wrong place when
the compositor spanned multiple outputs (e.g. two 1920x1080 monitors
side by side in nested Wayland mode):
1. real_pointer_update_global() added lo->x to coordinates that were
already in layout space from wlr_cursor_absolute_to_layout_coords,
double-counting the output offset. It also omitted lo->y entirely,
breaking vertical monitor layouts. Fix: read position from
wlr_cursor->x/y which is always authoritative layout-space.
2. real_pointer_handle_motion() accumulated ev->delta into local_x/y
then set pointer_x/y, after which handle_pointer_motion() added
the same delta again -- doubling every relative motion. Fix: let
handle_pointer_motion() be the sole authority; use wlr_cursor_move()
which respects output layout geometry.
3. Pointer devices from nested Wayland outputs (WL-1, WL-2) were never
registered with wlr_cursor via wlr_cursor_attach_input_device() or
mapped to their output via wlr_cursor_map_input_to_output(). Without
this mapping, wlr_cursor_absolute_to_layout_coords treated the 0..1
range as spanning the entire layout (0..3840) instead of the
device's specific output (e.g. 1920..3840 for WL-2). A previous
workaround disabled absolute motion entirely for nested multi-output,
which left only relative deltas that had no concept of which host
window the mouse was in. Fix: register devices with wlr_cursor and
map them to their outputs; remove the early-return.
All pointer paths now follow the same pattern: mutate position via
wlr_cursor_move()/wlr_cursor_warp(), then read back cursor->x/y into
wl_state.pointer_x/y. Manual bounding-box clamping is retained only
for the no-cursor fallback (headless/test mode).