decompose wl_backend.c: extract wl_pointer_motion.c

This commit is contained in:
Window Maker 2026-06-02 14:35:02 +02:00
parent 28fb61668f
commit 2e22c290ac
3 changed files with 240 additions and 224 deletions

View File

@ -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

View File

@ -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)

View File

@ -0,0 +1,232 @@
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <string.h>
#include <wayland-server-core.h>
#include <wlr/types/wlr_cursor.h>
#include <wlr/types/wlr_pointer.h>
#include <wlr/types/wlr_seat.h>
#include <wlr/util/log.h>
#include "../../screen.h"
#include "wl_types.h"
#include <WINGs/WINGsP.h>
/* 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);
}