diff --git a/src/Makefile.am b/src/Makefile.am index 260144a..1f9edc1 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -184,7 +184,8 @@ wmaker_SOURCES += \ backend/wayland/wl_dnd.c \ backend/wayland/wl_grabs.c \ backend/wayland/wl_grabstate.c \ - backend/wayland/wl_dragicon.c + backend/wayland/wl_dragicon.c \ + backend/wayland/wl_pointer_motion.c endif if USE_NESTED_FUNC diff --git a/src/backend/wayland/wl_backend.c b/src/backend/wayland/wl_backend.c index dfa89c0..d45d95b 100644 --- a/src/backend/wayland/wl_backend.c +++ b/src/backend/wayland/wl_backend.c @@ -291,17 +291,17 @@ void wl_focus_set(WNativeWindow win, int revert_to, unsigned long timestamp); /* Phase 50: forward declaration — defined in client section below. */ int wl_client_get_pid(WNativeWindow win); /* Phase 13: forward declarations — defined in pointer section below. */ -static void wl_update_pointer_focus(uint32_t time_msec); +void wl_update_pointer_focus(uint32_t time_msec); void wl_pointer_warp_to(int x, int y); /* wmaker-popup-v1 helper — defined in protocol section below. */ void wmaker_popup_apply(struct wl_toplevel_view *view); /* Phase 14: pointer event handler forward declarations. */ -static void handle_pointer_motion(struct wl_listener *listener, void *data); +extern 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); +extern 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 25b: drag-and-drop request handler. */ @@ -347,7 +347,7 @@ static void handle_backend_new_input(struct wl_listener *listener, void *data); /* Phase 18: output lifecycle and frame rendering forward declarations. */ static void handle_backend_new_output(struct wl_listener *listener, void *data); static void handle_output_frame(struct wl_listener *listener, void *data); -static void handle_pointer_motion_absolute(struct wl_listener *listener, void *data); +extern void handle_pointer_motion_absolute(struct wl_listener *listener, void *data); static void handle_pointer_button(struct wl_listener *listener, void *data); static void handle_pointer_frame(struct wl_listener *listener, void *data); @@ -524,7 +524,7 @@ wl_grab_begin_resize(struct wl_toplevel_view *view, uint32_t edges) } /* Called from both pointer-motion handlers (relative + absolute). */ -static void +void wl_grab_apply_motion(void) { if (!wl_state.grab_type || !wl_state.grab_view) @@ -3586,223 +3586,6 @@ wm_dock_app_remove(WNativeWindow id) } -/* ------------------------------------------------------------------ */ -/* Frame buffer (pixman + wlr_scene_buffer) */ - -/* ------------------------------------------------------------------ */ -/* Phase 14: pointer input event handlers */ -/* ------------------------------------------------------------------ */ - -static void -handle_pointer_motion(struct wl_listener *listener, void *data) -{ - (void)listener; - struct wlr_pointer_motion_event *ev = data; - int ow = wl_state.output ? (int)wl_state.output->width : 1920; - int oh = wl_state.output ? (int)wl_state.output->height : 1080; - wl_state.pointer_x += (int)ev->delta_x; - wl_state.pointer_y += (int)ev->delta_y; - if (wl_state.pointer_x < 0) wl_state.pointer_x = 0; - if (wl_state.pointer_y < 0) wl_state.pointer_y = 0; - if (wl_state.pointer_x >= ow) wl_state.pointer_x = ow - 1; - if (wl_state.pointer_y >= oh) wl_state.pointer_y = oh - 1; - /* Phase 23: update hardware cursor position. */ - if (wl_state.cursor) - wlr_cursor_warp(wl_state.cursor, NULL, - (double)wl_state.pointer_x, (double)wl_state.pointer_y); - /* Phase A: enqueue motion event for WINGs. - * Use frame_buf_hit so button sub-windows (which have no active scene_buf - * buffer and are invisible to wlr_scene_node_at) are resolved correctly. */ - WMScreen *wmscr = wl_get_wmscreen(); - if (wmscr) { - WMEvent wev; - memset(&wev, 0, sizeof(wev)); - wev.type = WME_MOTION; - struct wl_frame_buf *fb = frame_buf_hit(wl_state.pointer_x, - wl_state.pointer_y); - WNativeWindow new_id; - if (fb) { - wev.window = fb->id; - wev.u.motion.window = fb->id; - wev.u.motion.x = wl_state.pointer_x - fb->x; - wev.u.motion.y = wl_state.pointer_y - fb->y; - new_id = fb->id; - } else { - WScreen *scr0 = wScreenWithNumber(0); - WNativeWindow root = scr0 ? scr0->root_win : (WNativeWindow)0x0FFFFFFF; - wev.window = root; - wev.u.motion.window = root; - wev.u.motion.x = wl_state.pointer_x; - wev.u.motion.y = wl_state.pointer_y; - new_id = root; - } - wev.u.motion.time = ev->time_msec; - wev.u.motion.x_root = wl_state.pointer_x; - wev.u.motion.y_root = wl_state.pointer_y; - /* Resolve WINGs child view (slider, etc.) at pointer for - * motion routing during drag operations. */ - struct W_View *mtarget = NULL; - if (fb && fb->id >= 0x20000000) { - extern W_View *W_GetViewForWindow(unsigned long id); - extern struct W_View *W_FindViewAtPoint(struct W_View *parent, int x, int y); - W_View *toplevel = W_GetViewForWindow((unsigned long)fb->id); - if (toplevel) { - mtarget = W_FindViewAtPoint(toplevel, - wev.u.motion.x, wev.u.motion.y); - /* Adjust event coordinates to target view's local space */ - if (mtarget && mtarget != toplevel) { - W_View *p = mtarget; - int ox = 0, oy = 0; - while (p && p != toplevel) { - ox += p->pos.x; - oy += p->pos.y; - p = p->parent; - } - wev.u.motion.x -= ox; - wev.u.motion.y -= oy; - } - } - } - W_WL_EnqueueEvent(wmscr, &wev, mtarget); - /* Emit synthetic LEAVE/ENTER when pointer crosses WM sub-window - * boundaries (titlebar ↔ button, etc.). These feed the inner - * press-track loop in framewin.c:buttonMouseDown so the cancel- - * on-leave behaviour works correctly. */ - if (new_id != wl_state.last_hovered_id) { - WMEvent xev; - memset(&xev, 0, sizeof(xev)); - if (wl_state.last_hovered_id) { - xev.type = WME_LEAVE; - xev.window = wl_state.last_hovered_id; - xev.u.crossing.window = wl_state.last_hovered_id; - xev.u.crossing.x_root = wl_state.pointer_x; - xev.u.crossing.y_root = wl_state.pointer_y; - xev.u.crossing.mode = WM_NOTIFY_NORMAL; - xev.u.crossing.detail = WM_NOTIFY_ANCESTOR; - W_WL_EnqueueEvent(wmscr, &xev, NULL); - } - xev.type = WME_ENTER; - xev.window = new_id; - xev.u.crossing.window = new_id; - xev.u.crossing.x_root = wl_state.pointer_x; - xev.u.crossing.y_root = wl_state.pointer_y; - xev.u.crossing.mode = WM_NOTIFY_NORMAL; - xev.u.crossing.detail = WM_NOTIFY_ANCESTOR; - W_WL_EnqueueEvent(wmscr, &xev, NULL); - wl_state.last_hovered_id = new_id; - } - } - /* Phase 22: apply grab before updating normal pointer focus. */ - if (wl_state.grab_type) { wl_grab_apply_motion(); return; } - wl_update_pointer_focus(ev->time_msec); -} - -static void -handle_pointer_motion_absolute(struct wl_listener *listener, void *data) -{ - (void)listener; - struct wlr_pointer_motion_absolute_event *ev = data; - /* Phase 23: use wlr_cursor_warp_absolute so wlroots maps the 0..1 - * device-normalized coordinates to layout pixels and updates the - * hardware cursor in the nested window automatically. */ - if (wl_state.cursor) { - wlr_cursor_warp_absolute(wl_state.cursor, &ev->pointer->base, ev->x, ev->y); - wl_state.pointer_x = (int)wl_state.cursor->x; - wl_state.pointer_y = (int)wl_state.cursor->y; - } else { - int ow = wl_state.output ? (int)wl_state.output->width : 1920; - int oh = wl_state.output ? (int)wl_state.output->height : 1080; - wl_state.pointer_x = (int)(ev->x * (double)ow); - wl_state.pointer_y = (int)(ev->y * (double)oh); - if (wl_state.pointer_x < 0) wl_state.pointer_x = 0; - if (wl_state.pointer_y < 0) wl_state.pointer_y = 0; - if (wl_state.pointer_x >= ow) wl_state.pointer_x = ow - 1; - if (wl_state.pointer_y >= oh) wl_state.pointer_y = oh - 1; - } - /* Phase A: enqueue motion event for WINGs. - * Use frame_buf_hit so button sub-windows (which have no active scene_buf - * buffer and are invisible to wlr_scene_node_at) are resolved correctly. */ - WMScreen *wmscr2 = wl_get_wmscreen(); - if (wmscr2) { - WMEvent wev; - memset(&wev, 0, sizeof(wev)); - wev.type = WME_MOTION; - struct wl_frame_buf *fb = frame_buf_hit(wl_state.pointer_x, - wl_state.pointer_y); - WNativeWindow new_id; - if (fb) { - wev.window = fb->id; - wev.u.motion.window = fb->id; - wev.u.motion.x = wl_state.pointer_x - fb->x; - wev.u.motion.y = wl_state.pointer_y - fb->y; - new_id = fb->id; - } else { - WScreen *scr0 = wScreenWithNumber(0); - WNativeWindow root = scr0 ? scr0->root_win : (WNativeWindow)0x0FFFFFFF; - wev.window = root; - wev.u.motion.window = root; - wev.u.motion.x = wl_state.pointer_x; - wev.u.motion.y = wl_state.pointer_y; - new_id = root; - } - wev.u.motion.time = ev->time_msec; - wev.u.motion.x_root = wl_state.pointer_x; - wev.u.motion.y_root = wl_state.pointer_y; - W_WL_EnqueueEvent(wmscr2, &wev, NULL); - /* Emit synthetic LEAVE/ENTER on sub-window boundary crossings. */ - if (new_id != wl_state.last_hovered_id) { - WMEvent xev; - memset(&xev, 0, sizeof(xev)); - if (wl_state.last_hovered_id) { - xev.type = WME_LEAVE; - xev.window = wl_state.last_hovered_id; - xev.u.crossing.window = wl_state.last_hovered_id; - xev.u.crossing.x_root = wl_state.pointer_x; - xev.u.crossing.y_root = wl_state.pointer_y; - xev.u.crossing.mode = WM_NOTIFY_NORMAL; - xev.u.crossing.detail = WM_NOTIFY_ANCESTOR; - W_WL_EnqueueEvent(wmscr2, &xev, NULL); - } - xev.type = WME_ENTER; - xev.window = new_id; - xev.u.crossing.window = new_id; - xev.u.crossing.x_root = wl_state.pointer_x; - xev.u.crossing.y_root = wl_state.pointer_y; - xev.u.crossing.mode = WM_NOTIFY_NORMAL; - xev.u.crossing.detail = WM_NOTIFY_ANCESTOR; - W_WL_EnqueueEvent(wmscr2, &xev, NULL); - wl_state.last_hovered_id = new_id; - } - } - /* Phase 22: apply grab before updating normal pointer focus. */ - if (wl_state.grab_type) { wl_grab_apply_motion(); return; } - 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); -} - /* ------------------------------------------------------------------ */ /* Drag icon overlay for WINGs compositor-internal DND */ /* ------------------------------------------------------------------ */ @@ -4503,7 +4286,7 @@ view_surface_at(int px, int py, double *sx_out, double *sy_out, * pointer position. Called after every pointer position change so clients * receive wl_pointer.enter / wl_pointer.leave events correctly. */ -static void +void wl_update_pointer_focus(uint32_t time_msec) { if (!wl_state.seat) diff --git a/src/backend/wayland/wl_pointer_motion.c b/src/backend/wayland/wl_pointer_motion.c new file mode 100644 index 0000000..d3a01ed --- /dev/null +++ b/src/backend/wayland/wl_pointer_motion.c @@ -0,0 +1,232 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#include +#include +#include +#include "../../screen.h" +#include "wl_types.h" +#include + +/* Forward declarations for functions defined in other wl_*.c files */ +void W_WL_EnqueueEvent(struct W_Screen *scr, const WMEvent *ev, struct W_View *target); +void wl_grab_apply_motion(void); +void wl_update_pointer_focus(uint32_t time_msec); +WMScreen *wl_get_wmscreen(void); + +/* ------------------------------------------------------------------ */ +/* Phase 14: pointer input event handlers */ +/* ------------------------------------------------------------------ */ + +void +handle_pointer_motion(struct wl_listener *listener, void *data) +{ + (void)listener; + struct wlr_pointer_motion_event *ev = data; + int ow = wl_state.output ? (int)wl_state.output->width : 1920; + int oh = wl_state.output ? (int)wl_state.output->height : 1080; + wl_state.pointer_x += (int)ev->delta_x; + wl_state.pointer_y += (int)ev->delta_y; + if (wl_state.pointer_x < 0) wl_state.pointer_x = 0; + if (wl_state.pointer_y < 0) wl_state.pointer_y = 0; + if (wl_state.pointer_x >= ow) wl_state.pointer_x = ow - 1; + if (wl_state.pointer_y >= oh) wl_state.pointer_y = oh - 1; + /* Phase 23: update hardware cursor position. */ + if (wl_state.cursor) + wlr_cursor_warp(wl_state.cursor, NULL, + (double)wl_state.pointer_x, (double)wl_state.pointer_y); + /* Phase A: enqueue motion event for WINGs. + * Use frame_buf_hit so button sub-windows (which have no active scene_buf + * buffer and are invisible to wlr_scene_node_at) are resolved correctly. */ + WMScreen *wmscr = wl_get_wmscreen(); + if (wmscr) { + WMEvent wev; + memset(&wev, 0, sizeof(wev)); + wev.type = WME_MOTION; + struct wl_frame_buf *fb = frame_buf_hit(wl_state.pointer_x, + wl_state.pointer_y); + WNativeWindow new_id; + if (fb) { + wev.window = fb->id; + wev.u.motion.window = fb->id; + wev.u.motion.x = wl_state.pointer_x - fb->x; + wev.u.motion.y = wl_state.pointer_y - fb->y; + new_id = fb->id; + } else { + WScreen *scr0 = wScreenWithNumber(0); + WNativeWindow root = scr0 ? scr0->root_win : (WNativeWindow)0x0FFFFFFF; + wev.window = root; + wev.u.motion.window = root; + wev.u.motion.x = wl_state.pointer_x; + wev.u.motion.y = wl_state.pointer_y; + new_id = root; + } + wev.u.motion.time = ev->time_msec; + wev.u.motion.x_root = wl_state.pointer_x; + wev.u.motion.y_root = wl_state.pointer_y; + /* Resolve WINGs child view (slider, etc.) at pointer for + * motion routing during drag operations. */ + struct W_View *mtarget = NULL; + if (fb && fb->id >= 0x20000000) { + extern W_View *W_GetViewForWindow(unsigned long id); + extern struct W_View *W_FindViewAtPoint(struct W_View *parent, int x, int y); + W_View *toplevel = W_GetViewForWindow((unsigned long)fb->id); + if (toplevel) { + mtarget = W_FindViewAtPoint(toplevel, + wev.u.motion.x, wev.u.motion.y); + /* Adjust event coordinates to target view's local space */ + if (mtarget && mtarget != toplevel) { + W_View *p = mtarget; + int ox = 0, oy = 0; + while (p && p != toplevel) { + ox += p->pos.x; + oy += p->pos.y; + p = p->parent; + } + wev.u.motion.x -= ox; + wev.u.motion.y -= oy; + } + } + } + W_WL_EnqueueEvent(wmscr, &wev, mtarget); + /* Emit synthetic LEAVE/ENTER when pointer crosses WM sub-window + * boundaries (titlebar ↔ button, etc.). These feed the inner + * press-track loop in framewin.c:buttonMouseDown so the cancel- + * on-leave behaviour works correctly. */ + if (new_id != wl_state.last_hovered_id) { + WMEvent xev; + memset(&xev, 0, sizeof(xev)); + if (wl_state.last_hovered_id) { + xev.type = WME_LEAVE; + xev.window = wl_state.last_hovered_id; + xev.u.crossing.window = wl_state.last_hovered_id; + xev.u.crossing.x_root = wl_state.pointer_x; + xev.u.crossing.y_root = wl_state.pointer_y; + xev.u.crossing.mode = WM_NOTIFY_NORMAL; + xev.u.crossing.detail = WM_NOTIFY_ANCESTOR; + W_WL_EnqueueEvent(wmscr, &xev, NULL); + } + xev.type = WME_ENTER; + xev.window = new_id; + xev.u.crossing.window = new_id; + xev.u.crossing.x_root = wl_state.pointer_x; + xev.u.crossing.y_root = wl_state.pointer_y; + xev.u.crossing.mode = WM_NOTIFY_NORMAL; + xev.u.crossing.detail = WM_NOTIFY_ANCESTOR; + W_WL_EnqueueEvent(wmscr, &xev, NULL); + wl_state.last_hovered_id = new_id; + } + } + /* Phase 22: apply grab before updating normal pointer focus. */ + if (wl_state.grab_type) { wl_grab_apply_motion(); return; } + wl_update_pointer_focus(ev->time_msec); +} + +void +handle_pointer_motion_absolute(struct wl_listener *listener, void *data) +{ + (void)listener; + struct wlr_pointer_motion_absolute_event *ev = data; + /* Phase 23: use wlr_cursor_warp_absolute so wlroots maps the 0..1 + * device-normalized coordinates to layout pixels and updates the + * hardware cursor in the nested window automatically. */ + if (wl_state.cursor) { + wlr_cursor_warp_absolute(wl_state.cursor, &ev->pointer->base, ev->x, ev->y); + wl_state.pointer_x = (int)wl_state.cursor->x; + wl_state.pointer_y = (int)wl_state.cursor->y; + } else { + int ow = wl_state.output ? (int)wl_state.output->width : 1920; + int oh = wl_state.output ? (int)wl_state.output->height : 1080; + wl_state.pointer_x = (int)(ev->x * (double)ow); + wl_state.pointer_y = (int)(ev->y * (double)oh); + if (wl_state.pointer_x < 0) wl_state.pointer_x = 0; + if (wl_state.pointer_y < 0) wl_state.pointer_y = 0; + if (wl_state.pointer_x >= ow) wl_state.pointer_x = ow - 1; + if (wl_state.pointer_y >= oh) wl_state.pointer_y = oh - 1; + } + /* Phase A: enqueue motion event for WINGs. + * Use frame_buf_hit so button sub-windows (which have no active scene_buf + * buffer and are invisible to wlr_scene_node_at) are resolved correctly. */ + WMScreen *wmscr2 = wl_get_wmscreen(); + if (wmscr2) { + WMEvent wev; + memset(&wev, 0, sizeof(wev)); + wev.type = WME_MOTION; + struct wl_frame_buf *fb = frame_buf_hit(wl_state.pointer_x, + wl_state.pointer_y); + WNativeWindow new_id; + if (fb) { + wev.window = fb->id; + wev.u.motion.window = fb->id; + wev.u.motion.x = wl_state.pointer_x - fb->x; + wev.u.motion.y = wl_state.pointer_y - fb->y; + new_id = fb->id; + } else { + WScreen *scr0 = wScreenWithNumber(0); + WNativeWindow root = scr0 ? scr0->root_win : (WNativeWindow)0x0FFFFFFF; + wev.window = root; + wev.u.motion.window = root; + wev.u.motion.x = wl_state.pointer_x; + wev.u.motion.y = wl_state.pointer_y; + new_id = root; + } + wev.u.motion.time = ev->time_msec; + wev.u.motion.x_root = wl_state.pointer_x; + wev.u.motion.y_root = wl_state.pointer_y; + W_WL_EnqueueEvent(wmscr2, &wev, NULL); + /* Emit synthetic LEAVE/ENTER on sub-window boundary crossings. */ + if (new_id != wl_state.last_hovered_id) { + WMEvent xev; + memset(&xev, 0, sizeof(xev)); + if (wl_state.last_hovered_id) { + xev.type = WME_LEAVE; + xev.window = wl_state.last_hovered_id; + xev.u.crossing.window = wl_state.last_hovered_id; + xev.u.crossing.x_root = wl_state.pointer_x; + xev.u.crossing.y_root = wl_state.pointer_y; + xev.u.crossing.mode = WM_NOTIFY_NORMAL; + xev.u.crossing.detail = WM_NOTIFY_ANCESTOR; + W_WL_EnqueueEvent(wmscr2, &xev, NULL); + } + xev.type = WME_ENTER; + xev.window = new_id; + xev.u.crossing.window = new_id; + xev.u.crossing.x_root = wl_state.pointer_x; + xev.u.crossing.y_root = wl_state.pointer_y; + xev.u.crossing.mode = WM_NOTIFY_NORMAL; + xev.u.crossing.detail = WM_NOTIFY_ANCESTOR; + W_WL_EnqueueEvent(wmscr2, &xev, NULL); + wl_state.last_hovered_id = new_id; + } + } + /* Phase 22: apply grab before updating normal pointer focus. */ + if (wl_state.grab_type) { wl_grab_apply_motion(); return; } + 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). + */ +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); +}