Fixed pointer constraint - FINALLY

This commit is contained in:
rozodru 2025-07-22 15:00:05 -04:00
parent bdad659d7c
commit e86e24c48e
5 changed files with 155 additions and 96 deletions

79
nedm.c
View File

@ -65,49 +65,6 @@
#include "xwayland.h" #include "xwayland.h"
#endif #endif
static void handle_constraint_destroy(struct wl_listener *listener, __attribute__((unused)) void *data) {
struct nedm_seat *seat = wl_container_of(listener, seat, constraint_destroy);
seat->active_constraint = NULL;
wl_list_remove(&seat->constraint_destroy.link);
}
static void handle_new_pointer_constraint(struct wl_listener *listener, void *data) {
struct nedm_server *server = wl_container_of(listener, server, new_pointer_constraint);
struct wlr_pointer_constraint_v1 *constraint = data;
// Check if constraint is for the focused surface
struct wlr_surface *focused_surface = server->seat->seat->pointer_state.focused_surface;
if (focused_surface == constraint->surface) {
// Clear any existing constraint
if (server->seat->active_constraint) {
wlr_pointer_constraint_v1_send_deactivated(server->seat->active_constraint);
wl_list_remove(&server->seat->constraint_destroy.link);
}
// Set as active constraint
server->seat->active_constraint = constraint;
server->seat->constraint_destroy.notify = handle_constraint_destroy;
wl_signal_add(&constraint->events.destroy, &server->seat->constraint_destroy);
wlr_pointer_constraint_v1_send_activated(constraint);
// For locked pointer, warp cursor to surface center for now
if (constraint->type == WLR_POINTER_CONSTRAINT_V1_LOCKED) {
// Get view position and size
if (server->seat->focused_view) {
int gx, gy;
wlr_scene_node_coords(&server->seat->focused_view->scene_tree->node, &gx, &gy);
// Warp to center of view (simplified - not using constraint region)
struct wlr_surface *surface = constraint->surface;
double cx = (double)gx + surface->current.width / 2.0;
double cy = (double)gy + surface->current.height / 2.0;
wlr_cursor_warp(server->seat->cursor, NULL, cx, cy);
}
}
}
}
#ifndef WAIT_ANY #ifndef WAIT_ANY
#define WAIT_ANY -1 #define WAIT_ANY -1
#endif #endif
@ -562,6 +519,21 @@ main(int argc, char *argv[]) {
server.new_output.notify = handle_new_output; server.new_output.notify = handle_new_output;
wl_signal_add(&backend->events.new_output, &server.new_output); wl_signal_add(&backend->events.new_output, &server.new_output);
// Initialize pointer constraints and relative pointer protocols
server.pointer_constraints = wlr_pointer_constraints_v1_create(server.wl_display);
if(!server.pointer_constraints) {
wlr_log(WLR_ERROR, "Unable to create pointer constraints manager");
ret = 1;
goto end;
}
server.relative_pointer_manager = wlr_relative_pointer_manager_v1_create(server.wl_display);
if(!server.relative_pointer_manager) {
wlr_log(WLR_ERROR, "Unable to create relative pointer manager");
ret = 1;
goto end;
}
server.seat = seat_create(&server); server.seat = seat_create(&server);
if(!server.seat) { if(!server.seat) {
wlr_log(WLR_ERROR, "Unable to create the seat"); wlr_log(WLR_ERROR, "Unable to create the seat");
@ -664,25 +636,6 @@ main(int argc, char *argv[]) {
// Initialize layer shell // Initialize layer shell
nedm_layer_shell_init(&server); nedm_layer_shell_init(&server);
// Initialize pointer constraints and relative pointer protocols
server.pointer_constraints = wlr_pointer_constraints_v1_create(server.wl_display);
if(!server.pointer_constraints) {
wlr_log(WLR_ERROR, "Unable to create pointer constraints manager");
ret = 1;
goto end;
}
server.relative_pointer_manager = wlr_relative_pointer_manager_v1_create(server.wl_display);
if(!server.relative_pointer_manager) {
wlr_log(WLR_ERROR, "Unable to create relative pointer manager");
ret = 1;
goto end;
}
// Set up pointer constraint event handler
server.new_pointer_constraint.notify = handle_new_pointer_constraint;
wl_signal_add(&server.pointer_constraints->events.new_constraint, &server.new_pointer_constraint);
#if NEDM_HAS_XWAYLAND #if NEDM_HAS_XWAYLAND
server.xwayland = wlr_xwayland_create(server.wl_display, compositor, true); server.xwayland = wlr_xwayland_create(server.wl_display, compositor, true);
if(!server.xwayland) { if(!server.xwayland) {
@ -891,4 +844,4 @@ end:
FcFini(); FcFini();
return ret; return ret;
} }

149
seat.c
View File

@ -689,6 +689,26 @@ handle_cursor_button(struct wl_listener *listener, void *data) {
wlr_seat_pointer_notify_button(seat->seat, event->time_msec, event->button, wlr_seat_pointer_notify_button(seat->seat, event->time_msec, event->button,
event->state); event->state);
if(event->state == WL_POINTER_BUTTON_STATE_PRESSED) {
double sx, sy;
struct wlr_scene_node *node = wlr_scene_node_at(
&seat->server->scene->tree.node, seat->cursor->x, seat->cursor->y,
&sx, &sy);
if(node && node->type == WLR_SCENE_NODE_BUFFER) {
struct wlr_scene_surface *scene_surface =
wlr_scene_surface_try_from_buffer(
wlr_scene_buffer_from_node(node));
if(scene_surface) {
struct nedm_view *view = view_from_wlr_surface(
seat->server, scene_surface->surface);
if(view) {
seat_set_focus(seat, view);
}
}
}
}
wlr_idle_notifier_v1_notify_activity(seat->server->idle, seat->seat); wlr_idle_notifier_v1_notify_activity(seat->server->idle, seat->seat);
} }
@ -710,45 +730,62 @@ process_cursor_motion(struct nedm_seat *seat, uint32_t time) {
} }
} }
// Check for active pointer constraint bool skip_update = false;
if (seat->active_constraint) { if(seat->active_constraint) {
struct wlr_pointer_constraint_v1 *constraint = seat->active_constraint; struct wlr_pointer_constraint_v1 *constraint = seat->active_constraint;
if(constraint->surface != surface) {
// Deactivate constraint if surface changed
if (constraint->surface != surface) {
wlr_pointer_constraint_v1_send_deactivated(constraint); wlr_pointer_constraint_v1_send_deactivated(constraint);
wl_list_remove(&seat->constraint_destroy.link); wl_list_remove(&seat->constraint_destroy.link);
seat->active_constraint = NULL; seat->active_constraint = NULL;
} else { } else if(constraint->type == WLR_POINTER_CONSTRAINT_V1_LOCKED) {
// Handle constraint behavior // For locked pointer, do not move or warp the cursor, just notify enter/motion
if (constraint->type == WLR_POINTER_CONSTRAINT_V1_LOCKED) { if(surface) {
// For locked pointer, don't move cursor but still process enter/motion wlr_seat_pointer_notify_enter(wlr_seat, surface, sx, sy);
if (surface && time > 0) { if(time > 0) {
wlr_seat_pointer_notify_enter(wlr_seat, surface, sx, sy);
wlr_seat_pointer_notify_motion(wlr_seat, time, sx, sy); wlr_seat_pointer_notify_motion(wlr_seat, time, sx, sy);
} }
goto skip_cursor_update;
}
// For confined constraints, clamp cursor to region
if (constraint->type == WLR_POINTER_CONSTRAINT_V1_CONFINED) {
// TODO: Implement proper confinement
// For now, just allow the motion
} }
// Do not warp or clamp the cursor for locked pointer
skip_update = true;
} }
} }
if(surface != NULL) { if(!skip_update) {
wlr_seat_pointer_notify_enter(wlr_seat, surface, sx, sy); if(surface) {
bool focus_changed = wlr_seat->pointer_state.focused_surface != surface; bool focus_changed =
if(!focus_changed && time > 0) { wlr_seat->pointer_state.focused_surface != surface;
wlr_seat_pointer_notify_motion(wlr_seat, time, sx, sy); if(!focus_changed && time > 0) {
wlr_seat_pointer_notify_motion(wlr_seat, time, sx, sy);
}
wlr_seat_pointer_notify_enter(wlr_seat, surface, sx, sy);
} else {
wlr_seat_pointer_clear_focus(wlr_seat);
} }
} else {
wlr_seat_pointer_clear_focus(wlr_seat);
} }
skip_cursor_update: if(seat->active_constraint &&
seat->active_constraint->type == WLR_POINTER_CONSTRAINT_V1_CONFINED) {
struct wlr_box surface_box;
wlr_surface_get_extents(seat->active_constraint->surface, &surface_box);
double sx = seat->cursor->x - surface_box.x;
double sy = seat->cursor->y - surface_box.y;
if(sx < 0) {
sx = 0;
}
if(sy < 0) {
sy = 0;
}
if(sx > surface_box.width) {
sx = surface_box.width;
}
if(sy > surface_box.height) {
sy = surface_box.height;
}
wlr_cursor_warp(seat->cursor, NULL, surface_box.x + sx,
surface_box.y + sy);
}
struct nedm_drag_icon *drag_icon; struct nedm_drag_icon *drag_icon;
wl_list_for_each(drag_icon, &seat->drag_icons, link) { wl_list_for_each(drag_icon, &seat->drag_icons, link) {
@ -788,10 +825,7 @@ skip_cursor_update:
seat->server->running) { seat->server->running) {
ipc_send_event( ipc_send_event(
seat->server, seat->server,
"{\"event_name\":\"cursor_switch_tile\",\"old_output\":" "{'event_name':'cursor_switch_tile','old_output':'%s','old_output_id':%d,'old_tile':%d,'new_output':'%s','new_output_id':%d,'new_tile':%d}",
"\"%s\",\"old_output_id\":%d,"
"\"old_tile\":%d,\"new_output\":\"%s\",\"new_output_"
"id\":%d,\"new_tile\":%d}",
seat->cursor_tile->workspace->output->name, seat->cursor_tile->workspace->output->name,
output_get_num(seat->cursor_tile->workspace->output), output_get_num(seat->cursor_tile->workspace->output),
seat->cursor_tile->id, c_outp->name, output_get_num(nedm_outp), seat->cursor_tile->id, c_outp->name, output_get_num(nedm_outp),
@ -818,6 +852,21 @@ handle_cursor_motion(struct wl_listener *listener, void *data) {
struct nedm_seat *seat = wl_container_of(listener, seat, cursor_motion); struct nedm_seat *seat = wl_container_of(listener, seat, cursor_motion);
struct wlr_pointer_motion_event *event = data; struct wlr_pointer_motion_event *event = data;
// If pointer is locked, do not move the visible cursor, only send relative motion
if (seat->active_constraint && seat->active_constraint->type == WLR_POINTER_CONSTRAINT_V1_LOCKED) {
// Only send relative motion to the client
if (seat->server->relative_pointer_manager) {
wlr_relative_pointer_manager_v1_send_relative_motion(
seat->server->relative_pointer_manager, seat->seat,
(uint64_t)event->time_msec * 1000, event->delta_x, event->delta_y,
event->unaccel_dx, event->unaccel_dy);
}
// Notify the surface of motion (for button state, etc)
process_cursor_motion(seat, event->time_msec);
wlr_idle_notifier_v1_notify_activity(seat->server->idle, seat->seat);
return;
}
wlr_cursor_move(seat->cursor, &event->pointer->base, event->delta_x, wlr_cursor_move(seat->cursor, &event->pointer->base, event->delta_x,
event->delta_y); event->delta_y);
process_cursor_motion(seat, event->time_msec); process_cursor_motion(seat, event->time_msec);
@ -825,9 +874,9 @@ handle_cursor_motion(struct wl_listener *listener, void *data) {
// Send relative motion AFTER cursor position is updated // Send relative motion AFTER cursor position is updated
if (seat->server->relative_pointer_manager) { if (seat->server->relative_pointer_manager) {
wlr_relative_pointer_manager_v1_send_relative_motion( wlr_relative_pointer_manager_v1_send_relative_motion(
seat->server->relative_pointer_manager, seat->seat, seat->server->relative_pointer_manager, seat->seat,
(uint64_t)event->time_msec * 1000, event->delta_x, event->delta_y, (uint64_t)event->time_msec * 1000, event->delta_x, event->delta_y,
event->unaccel_dx, event->unaccel_dy); event->unaccel_dx, event->unaccel_dy);
} }
wlr_idle_notifier_v1_notify_activity(seat->server->idle, seat->seat); wlr_idle_notifier_v1_notify_activity(seat->server->idle, seat->seat);
@ -927,6 +976,35 @@ handle_start_drag(struct wl_listener *listener, void *data) {
drag_icon_update_position(drag_icon); drag_icon_update_position(drag_icon);
} }
static void
handle_constraint_destroy(struct wl_listener *listener,
__attribute__((unused)) void *data) {
struct nedm_seat *seat = wl_container_of(listener, seat, constraint_destroy);
seat->active_constraint = NULL;
wl_list_remove(&seat->constraint_destroy.link);
}
static void
handle_new_constraint(struct wl_listener *listener, void *data) {
struct nedm_seat *seat = wl_container_of(listener, seat, new_constraint);
struct wlr_pointer_constraint_v1 *constraint = data;
// If there's an existing constraint, destroy it
if (seat->active_constraint) {
wlr_pointer_constraint_v1_send_deactivated(seat->active_constraint);
wl_list_remove(&seat->constraint_destroy.link);
seat->active_constraint = NULL;
}
seat->active_constraint = constraint;
wl_signal_add(&constraint->events.destroy, &seat->constraint_destroy);
seat->constraint_destroy.notify = handle_constraint_destroy;
// Activate the constraint
wlr_pointer_constraint_v1_send_activated(constraint);
process_cursor_motion(seat, -1);
}
static void static void
handle_destroy(struct wl_listener *listener, handle_destroy(struct wl_listener *listener,
__attribute__((unused)) void *_data) { __attribute__((unused)) void *_data) {
@ -1040,6 +1118,10 @@ seat_create(struct nedm_server *server) {
wl_signal_add(&seat->seat->events.request_set_primary_selection, wl_signal_add(&seat->seat->events.request_set_primary_selection,
&seat->request_set_primary_selection); &seat->request_set_primary_selection);
seat->new_constraint.notify = handle_new_constraint;
wl_signal_add(&server->pointer_constraints->events.new_constraint,
&seat->new_constraint);
wl_list_init(&seat->keyboard_groups); wl_list_init(&seat->keyboard_groups);
seat->num_keyboards = 0; seat->num_keyboards = 0;
seat->num_pointers = 0; seat->num_pointers = 0;
@ -1072,6 +1154,7 @@ seat_destroy(struct nedm_seat *seat) {
seat->active_constraint = NULL; seat->active_constraint = NULL;
} }
wl_list_remove(&seat->new_constraint.link);
wl_list_remove(&seat->request_start_drag.link); wl_list_remove(&seat->request_start_drag.link);
wl_list_remove(&seat->start_drag.link); wl_list_remove(&seat->start_drag.link);

1
seat.h
View File

@ -37,6 +37,7 @@ struct nedm_seat {
struct wlr_xcursor_manager *xcursor_manager; struct wlr_xcursor_manager *xcursor_manager;
struct wlr_pointer_constraint_v1 *active_constraint; struct wlr_pointer_constraint_v1 *active_constraint;
struct wl_listener constraint_destroy; struct wl_listener constraint_destroy;
struct wl_listener new_constraint;
struct wl_listener cursor_motion; struct wl_listener cursor_motion;
struct wl_listener cursor_motion_absolute; struct wl_listener cursor_motion_absolute;
struct wl_listener cursor_button; struct wl_listener cursor_button;

19
view.c
View File

@ -238,3 +238,22 @@ view_init(struct nedm_view *view, enum nedm_view_type type,
server->curr_output->workspaces[server->curr_output->curr_workspace] server->curr_output->workspaces[server->curr_output->curr_workspace]
->scene); ->scene);
} }
struct nedm_view *
view_from_wlr_surface(struct nedm_server *server,
struct wlr_surface *surface) {
struct nedm_view *view = NULL;
struct nedm_workspace *ws =
server->curr_output->workspaces[server->curr_output->curr_workspace];
wl_list_for_each(view, &ws->views, link) {
if(view->wlr_surface == surface) {
return view;
}
}
wl_list_for_each(view, &ws->unmanaged_views, link) {
if(view->wlr_surface == surface) {
return view;
}
}
return NULL;
}

3
view.h
View File

@ -73,5 +73,8 @@ view_init(struct nedm_view *view, enum nedm_view_type type,
const struct nedm_view_impl *impl, struct nedm_server *server); const struct nedm_view_impl *impl, struct nedm_server *server);
struct nedm_view * struct nedm_view *
view_get_prev_view(struct nedm_view *view); view_get_prev_view(struct nedm_view *view);
struct nedm_view *
view_from_wlr_surface(struct nedm_server *server,
struct wlr_surface *surface);
#endif #endif