diff --git a/src/backend/wayland/wl_backend.c b/src/backend/wayland/wl_backend.c index c1c80e2..be3373f 100644 --- a/src/backend/wayland/wl_backend.c +++ b/src/backend/wayland/wl_backend.c @@ -113,6 +113,7 @@ #include /* Phase 18: buffer allocator */ #include /* Phase 23: hardware cursor tracking */ #include /* Phase 23: xcursor theme */ +#include /* Phase 25: clipboard / selection */ #include /* Generated xdg-shell protocol implementation (Wayland glue code) */ #include "protocols/xdg-shell-protocol.c" @@ -124,6 +125,7 @@ #include "../../screen.h" #include "../../window.h" #include "../../actions.h" /* Phase 16: wMaximizeWindow, wIconifyWindow, etc. */ +#include "../../stacking.h" /* Phase 24: wRaiseFrame for click-to-raise */ #include "../../xinerama.h" /* Phase 16: wGetHeadForWindow */ #include "../../xmodifier.h" /* Phase 77: wSetModifierMasks */ #include "../../event.h" /* Phase 78: wWaylandKeyPress */ @@ -257,6 +259,10 @@ struct wl_compositor_state { /* Phase 23: hardware cursor and xcursor theme. */ struct wlr_cursor *cursor; struct wlr_xcursor_manager *xcursor_mgr; + /* Phase 24: seat cursor-surface request listener. */ + struct wl_listener seat_request_cursor_listener; + /* Phase 25: clipboard — seat selection request listener. */ + struct wl_listener seat_request_set_selection_listener; }; static struct wl_compositor_state wl_state; @@ -312,6 +318,13 @@ static void wl_update_pointer_focus(uint32_t time_msec); static void wl_pointer_warp_to(int x, int y); /* Phase 14: pointer event handler forward declarations. */ static void handle_pointer_motion(struct wl_listener *listener, void *data); +/* Phase 24: click-to-focus helper — defined in Phase 13 pointer section. */ +static struct wlr_surface *view_surface_at(int px, int py, double *sx_out, + double *sy_out, struct wl_toplevel_view **view_out); +/* Phase 24: client cursor-surface request handler. */ +static void handle_seat_request_cursor(struct wl_listener *listener, void *data); +/* Phase 25: client clipboard selection request handler. */ +static void handle_seat_request_set_selection(struct wl_listener *listener, void *data); /* Phase 17: XWayland new surface forward declaration. */ static void handle_xwayland_new_surface(struct wl_listener *listener, void *data); /* Phase 20: nested backend real input device forward declaration. */ @@ -1434,6 +1447,23 @@ wl_display_open(const char *name) wl_state.seat = wlr_seat_create(wl_state.display, "seat0"); wl_state.data_device_mgr = wlr_data_device_manager_create(wl_state.display); + + /* Phase 24: handle client cursor-surface requests on the seat. */ + if (wl_state.seat) { + wl_state.seat_request_cursor_listener.notify = handle_seat_request_cursor; + wl_signal_add(&wl_state.seat->events.request_set_cursor, + &wl_state.seat_request_cursor_listener); + } + /* Phase 25: handle clipboard set-selection requests on the seat so + * Ctrl-C / copy operations from clients propagate to the seat selection + * and become available for paste in other clients. */ + if (wl_state.seat) { + wl_state.seat_request_set_selection_listener.notify = + handle_seat_request_set_selection; + wl_signal_add(&wl_state.seat->events.request_set_selection, + &wl_state.seat_request_set_selection_listener); + wwarning("wl_backend: clipboard selection wired"); + } wl_state.xdg_shell = wlr_xdg_shell_create(wl_state.display); /* Phase 64: advertise zxdg_decoration_manager_v1 so clients that @@ -1723,6 +1753,9 @@ wl_display_close(void) WL_LISTENER_REMOVE(wl_state.real_pointer_motion_abs_listener); WL_LISTENER_REMOVE(wl_state.real_pointer_button_listener); WL_LISTENER_REMOVE(wl_state.real_pointer_frame_listener); + /* Phase 24/25: seat cursor + selection listeners */ + WL_LISTENER_REMOVE(wl_state.seat_request_cursor_listener); + WL_LISTENER_REMOVE(wl_state.seat_request_set_selection_listener); #undef WL_LISTENER_REMOVE /* Phase 23: destroy cursor and xcursor manager before display teardown. */ @@ -2020,6 +2053,45 @@ handle_pointer_motion_absolute(struct wl_listener *listener, void *data) wl_update_pointer_focus(ev->time_msec); } +/* + * Phase 24: seat request_set_cursor — client wants to set a custom cursor + * image (e.g. I-beam in a text field, hand over a link). Validate that + * the request comes from the currently focused pointer client, then hand + * the surface to wlr_cursor so the hardware cursor is updated on all + * outputs (which in nested mode sends wl_pointer.set_cursor to the host). + */ +static void +handle_seat_request_cursor(struct wl_listener *listener, void *data) +{ + (void)listener; + struct wlr_seat_pointer_request_set_cursor_event *ev = data; + if (!wl_state.seat || !wl_state.cursor) + return; + /* Only honour cursor requests from the client that currently has + * pointer focus; reject requests from unfocused clients. */ + struct wlr_seat_client *focused = + wl_state.seat->pointer_state.focused_client; + if (ev->seat_client != focused) + return; + wlr_cursor_set_surface(wl_state.cursor, ev->surface, + ev->hotspot_x, ev->hotspot_y); +} + +/* + * Phase 25: seat request_set_selection — client calls wl_data_device.set_selection + * (i.e. Ctrl-C or other clipboard write). Forward to wlr_seat so the + * selection becomes available to the next client that reads it. + */ +static void +handle_seat_request_set_selection(struct wl_listener *listener, void *data) +{ + (void)listener; + struct wlr_seat_request_set_selection_event *ev = data; + if (!wl_state.seat) + return; + wlr_seat_set_selection(wl_state.seat, ev->source, ev->serial); +} + static void handle_pointer_button(struct wl_listener *listener, void *data) { @@ -2035,6 +2107,33 @@ handle_pointer_button(struct wl_listener *listener, void *data) wl_update_pointer_focus(ev->time_msec); return; } + /* Phase 24: click-to-focus — on button press, find the view under the + * pointer and focus + raise it via the WM policy layer. + * This mirrors what the X11 backend does via ButtonPress on frame windows. */ + if (ev->state == WLR_BUTTON_PRESSED) { + double sx, sy; + struct wl_toplevel_view *view = NULL; + view_surface_at(wl_state.pointer_x, wl_state.pointer_y, + &sx, &sy, &view); + if (view) { + WObjDescriptor *desc = + wm_backend->context_find(view->id, WM_CTX_CLIENT_WIN); + if (desc && desc->parent_type == WCLASS_WINDOW) { + WWindow *wwin = (WWindow *)desc->parent; + WScreen *scr = wScreenWithNumber(0); + wSetFocusTo(scr, wwin); + /* Raise the window in scene Z-order by moving its scene node + * to the top of the scene root's child list. */ + if (view->scene_node) + wlr_scene_node_raise_to_top(view->scene_node); + /* Also raise it in our toplevel list so view_surface_at() + * returns it first on subsequent hits. */ + wl_list_remove(&view->link); + wl_list_insert(wl_state.toplevel_list.prev, &view->link); + wwarning("wl_backend: click focus id=0x%lx", view->id); + } + } + } wlr_seat_pointer_notify_button(wl_state.seat, ev->time_msec, ev->button, ev->state); wlr_seat_pointer_notify_frame(wl_state.seat); diff --git a/tests/wl-smoke-test.sh b/tests/wl-smoke-test.sh index 1c0c07d..b1bf7c3 100755 --- a/tests/wl-smoke-test.sh +++ b/tests/wl-smoke-test.sh @@ -123,6 +123,16 @@ # Compositor logs "XWayland DISPLAY=: (lazy: starts on first X11 client)". # DISPLAY env var is set from the pre-allocated display number. # Verified by "XWayland DISPLAY=.*lazy" log. +# 43. (Phase 24) Click-to-focus: on WLR_BUTTON_PRESSED, view_surface_at() +# finds the view under the pointer; wSetFocusTo delivers WM keyboard +# focus and wlr_scene_node_raise_to_top raises it in Z-order. +# seat->events.request_set_cursor wired: clients can set custom cursor +# images (I-beam, hand, etc.) via wl_pointer.set_cursor. +# (Build-only check — no multi-window focus test here.) +# 44. (Phase 25) Clipboard: seat->events.request_set_selection wired so +# Ctrl-C / copy operations propagate to the seat selection and become +# available for paste in other clients via wl_data_device. +# Verified by "clipboard selection wired" log line. # 42. (Phase 23) Cursor image rendering: wlr_cursor + wlr_xcursor_manager # created and attached to output_layout; xcursor theme (default, 24px) # loaded at scale 1.0; "left_ptr" image set on each output after it joins @@ -282,6 +292,11 @@ grep -q 'dock/clip disabled in Wayland mode' /tmp/wl-smoke-stderr \ || die "compositor did not log dock/clip disabled (noclip/nodock not set in wl_screen_create_internals)" echo " nodock check: dock/clip disabled in Wayland mode (noclip=1 nodock=1)" +# -- 4f2b. Phase 25: clipboard selection wired ---------------------------- +grep -q 'clipboard selection wired' /tmp/wl-smoke-stderr \ + || die "compositor did not log clipboard wired (seat request_set_selection listener missing)" +echo " clipboard check: clipboard selection wired" + # -- 4f3. Phase 23: cursor created and xcursor theme loaded --------------- grep -q 'cursor created, xcursor theme loaded' /tmp/wl-smoke-stderr \ || die "compositor did not log cursor creation (wlr_cursor_create or xcursor_manager_create failed)"