From 252bcce2f373bc5f0e29a4820ce23373b836e3bb Mon Sep 17 00:00:00 2001 From: Las Date: Sat, 4 Aug 2018 23:22:21 +0200 Subject: [PATCH 01/17] Add focus change event for seats --- include/wlr/types/wlr_seat.h | 22 ++++++++++++++++++++++ types/seat/wlr_seat.c | 4 ++++ types/seat/wlr_seat_keyboard.c | 10 ++++++++++ types/seat/wlr_seat_pointer.c | 9 ++++++++- 4 files changed, 44 insertions(+), 1 deletion(-) diff --git a/include/wlr/types/wlr_seat.h b/include/wlr/types/wlr_seat.h index b3c02cf2..d850ba52 100644 --- a/include/wlr/types/wlr_seat.h +++ b/include/wlr/types/wlr_seat.h @@ -146,6 +146,10 @@ struct wlr_seat_pointer_state { uint32_t grab_time; struct wl_listener surface_destroy; + + struct { + struct wl_signal focus_change; + } events; }; // TODO: May be useful to be able to simulate keyboard input events @@ -164,6 +168,10 @@ struct wlr_seat_keyboard_state { struct wlr_seat_keyboard_grab *grab; struct wlr_seat_keyboard_grab *default_grab; + + struct { + struct wl_signal focus_change; + } events; }; struct wlr_seat_touch_state { @@ -238,6 +246,20 @@ struct wlr_seat_pointer_request_set_cursor_event { int32_t hotspot_x, hotspot_y; }; +struct wlr_seat_pointer_focus_change_event { + struct wlr_seat *seat; + struct wlr_surface *old_surface, *new_surface; + double sx, sy; +}; + +struct wlr_seat_keyboard_focus_change_event { + struct wlr_seat *seat; + struct wlr_surface *old_surface, *new_surface; + size_t num_keycodes; + uint32_t *keycodes; + struct wlr_keyboard_modifiers *modifiers; +}; + /** * Allocates a new wlr_seat and adds a wl_seat global to the display. */ diff --git a/types/seat/wlr_seat.c b/types/seat/wlr_seat.c index 2e7da0a4..c4da29e2 100644 --- a/types/seat/wlr_seat.c +++ b/types/seat/wlr_seat.c @@ -225,6 +225,8 @@ struct wlr_seat *wlr_seat_create(struct wl_display *display, const char *name) { seat->pointer_state.default_grab = pointer_grab; seat->pointer_state.grab = pointer_grab; + wl_signal_init(&seat->pointer_state.events.focus_change); + // keyboard state struct wlr_seat_keyboard_grab *keyboard_grab = calloc(1, sizeof(struct wlr_seat_keyboard_grab)); @@ -241,6 +243,8 @@ struct wlr_seat *wlr_seat_create(struct wl_display *display, const char *name) { seat->keyboard_state.seat = seat; wl_list_init(&seat->keyboard_state.surface_destroy.link); + wl_signal_init(&seat->keyboard_state.events.focus_change); + // touch state struct wlr_seat_touch_grab *touch_grab = calloc(1, sizeof(struct wlr_seat_touch_grab)); diff --git a/types/seat/wlr_seat_keyboard.c b/types/seat/wlr_seat_keyboard.c index e8ea300e..4f95b265 100644 --- a/types/seat/wlr_seat_keyboard.c +++ b/types/seat/wlr_seat_keyboard.c @@ -291,6 +291,16 @@ void wlr_seat_keyboard_enter(struct wlr_seat *seat, // as it targets seat->keyboard_state.focused_client wlr_seat_keyboard_send_modifiers(seat, modifiers); } + + struct wlr_seat_keyboard_focus_change_event event = { + .seat = seat, + .new_surface = surface, + .old_surface = focused_surface, + .num_keycodes = num_keycodes, + .keycodes = keycodes, + .modifiers = modifiers, + }; + wlr_signal_emit_safe(&seat->keyboard_state.events.focus_change, &event); } void wlr_seat_keyboard_notify_enter(struct wlr_seat *seat, diff --git a/types/seat/wlr_seat_pointer.c b/types/seat/wlr_seat_pointer.c index 899fb64f..d6df0365 100644 --- a/types/seat/wlr_seat_pointer.c +++ b/types/seat/wlr_seat_pointer.c @@ -183,7 +183,14 @@ void wlr_seat_pointer_enter(struct wlr_seat *wlr_seat, wlr_seat->pointer_state.focused_client = client; wlr_seat->pointer_state.focused_surface = surface; - // TODO: send focus change event + struct wlr_seat_pointer_focus_change_event event = { + .seat = wlr_seat, + .new_surface = surface, + .old_surface = focused_surface, + .sx = sx, + .sy = sy, + }; + wlr_signal_emit_safe(&wlr_seat->pointer_state.events.focus_change, &event); } void wlr_seat_pointer_clear_focus(struct wlr_seat *wlr_seat) { From cf9debf82e9b01b11b82c2a65426b909ac222340 Mon Sep 17 00:00:00 2001 From: Las Date: Mon, 6 Aug 2018 11:39:34 +0200 Subject: [PATCH 02/17] Make roots_cursor::pointer_view available even if there is a surface To find out whether there was a surface or not before a movement, the member roots_cursor::wlr_surface has been added. --- include/rootston/cursor.h | 1 + rootston/cursor.c | 16 +++++++++++----- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/include/rootston/cursor.h b/include/rootston/cursor.h index 2c687a39..52c72a44 100644 --- a/include/rootston/cursor.h +++ b/include/rootston/cursor.h @@ -28,6 +28,7 @@ struct roots_cursor { uint32_t resize_edges; struct roots_seat_view *pointer_view; + struct wlr_surface *wlr_surface; struct wl_listener motion; struct wl_listener motion_absolute; diff --git a/rootston/cursor.c b/rootston/cursor.c index 2b8d9a3e..239acf9c 100644 --- a/rootston/cursor.c +++ b/rootston/cursor.c @@ -125,17 +125,23 @@ static void roots_passthrough_cursor(struct roots_cursor *cursor, if (view) { struct roots_seat_view *seat_view = roots_seat_view_from_view(seat, view); - if (cursor->pointer_view && (surface || - seat_view != cursor->pointer_view)) { + + if (cursor->pointer_view && + !cursor->wlr_surface && (surface || seat_view != cursor->pointer_view)) { seat_view_deco_leave(cursor->pointer_view); - cursor->pointer_view = NULL; } + + cursor->pointer_view = seat_view; + if (!surface) { - cursor->pointer_view = seat_view; seat_view_deco_motion(seat_view, sx, sy); } + } else { + cursor->pointer_view = NULL; } + cursor->wlr_surface = surface; + if (surface) { focus_changed = (seat->seat->pointer_state.focused_surface != surface); wlr_seat_pointer_notify_enter(seat->seat, surface, sx, sy); @@ -262,7 +268,7 @@ static void roots_cursor_press_button(struct roots_cursor *cursor, } else { if (view && !surface && cursor->pointer_view) { seat_view_deco_button(cursor->pointer_view, - sx, sy, button, state); + sx, sy, button, state); } if (state == WLR_BUTTON_RELEASED && From d446c53fe1794625b9f0c0df9eee00e198401ff8 Mon Sep 17 00:00:00 2001 From: Las Date: Wed, 8 Aug 2018 12:39:33 +0200 Subject: [PATCH 03/17] Make (wlr_)seat_client_from_pointer_resource public --- include/wlr/types/wlr_seat.h | 5 ++++- types/seat/wlr_seat_pointer.c | 16 ++++++++-------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/include/wlr/types/wlr_seat.h b/include/wlr/types/wlr_seat.h index d850ba52..4daefff1 100644 --- a/include/wlr/types/wlr_seat.h +++ b/include/wlr/types/wlr_seat.h @@ -568,6 +568,9 @@ bool wlr_seat_touch_has_grab(struct wlr_seat *seat); bool wlr_seat_validate_grab_serial(struct wlr_seat *seat, uint32_t serial); struct wlr_seat_client *wlr_seat_client_from_resource( - struct wl_resource *resource); + struct wl_resource *resource); + +struct wlr_seat_client *wlr_seat_client_from_pointer_resource( + struct wl_resource *resource); #endif diff --git a/types/seat/wlr_seat_pointer.c b/types/seat/wlr_seat_pointer.c index d6df0365..594a5b81 100644 --- a/types/seat/wlr_seat_pointer.c +++ b/types/seat/wlr_seat_pointer.c @@ -53,7 +53,7 @@ static void pointer_send_frame(struct wl_resource *resource) { static const struct wl_pointer_interface pointer_impl; -static struct wlr_seat_client *seat_client_from_pointer_resource( +struct wlr_seat_client *wlr_seat_client_from_pointer_resource( struct wl_resource *resource) { assert(wl_resource_instance_of(resource, &wl_pointer_interface, &pointer_impl)); @@ -69,7 +69,7 @@ static void pointer_set_cursor(struct wl_client *client, struct wl_resource *surface_resource, int32_t hotspot_x, int32_t hotspot_y) { struct wlr_seat_client *seat_client = - seat_client_from_pointer_resource(pointer_resource); + wlr_seat_client_from_pointer_resource(pointer_resource); if (seat_client == NULL) { return; } @@ -146,7 +146,7 @@ void wlr_seat_pointer_enter(struct wlr_seat *wlr_seat, uint32_t serial = wl_display_next_serial(wlr_seat->display); struct wl_resource *resource; wl_resource_for_each(resource, &focused_client->pointers) { - if (seat_client_from_pointer_resource(resource) == NULL) { + if (wlr_seat_client_from_pointer_resource(resource) == NULL) { continue; } @@ -160,7 +160,7 @@ void wlr_seat_pointer_enter(struct wlr_seat *wlr_seat, uint32_t serial = wl_display_next_serial(wlr_seat->display); struct wl_resource *resource; wl_resource_for_each(resource, &client->pointers) { - if (seat_client_from_pointer_resource(resource) == NULL) { + if (wlr_seat_client_from_pointer_resource(resource) == NULL) { continue; } @@ -206,7 +206,7 @@ void wlr_seat_pointer_send_motion(struct wlr_seat *wlr_seat, uint32_t time, struct wl_resource *resource; wl_resource_for_each(resource, &client->pointers) { - if (seat_client_from_pointer_resource(resource) == NULL) { + if (wlr_seat_client_from_pointer_resource(resource) == NULL) { continue; } @@ -226,7 +226,7 @@ uint32_t wlr_seat_pointer_send_button(struct wlr_seat *wlr_seat, uint32_t time, uint32_t serial = wl_display_next_serial(wlr_seat->display); struct wl_resource *resource; wl_resource_for_each(resource, &client->pointers) { - if (seat_client_from_pointer_resource(resource) == NULL) { + if (wlr_seat_client_from_pointer_resource(resource) == NULL) { continue; } @@ -246,7 +246,7 @@ void wlr_seat_pointer_send_axis(struct wlr_seat *wlr_seat, uint32_t time, struct wl_resource *resource; wl_resource_for_each(resource, &client->pointers) { - if (seat_client_from_pointer_resource(resource) == NULL) { + if (wlr_seat_client_from_pointer_resource(resource) == NULL) { continue; } @@ -356,7 +356,7 @@ void seat_client_create_pointer(struct wlr_seat_client *seat_client, void seat_client_destroy_pointer(struct wl_resource *resource) { struct wlr_seat_client *seat_client = - seat_client_from_pointer_resource(resource); + wlr_seat_client_from_pointer_resource(resource); if (seat_client == NULL) { return; } From 59dd1dddbabb93569d06fb5612cb8178a9a50c1e Mon Sep 17 00:00:00 2001 From: Las Date: Wed, 8 Aug 2018 13:43:44 +0200 Subject: [PATCH 04/17] Update pointer focus when cycling focus --- include/rootston/cursor.h | 2 ++ rootston/cursor.c | 12 +++++++++--- rootston/seat.c | 4 ++++ 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/include/rootston/cursor.h b/include/rootston/cursor.h index 52c72a44..bcbc1c07 100644 --- a/include/rootston/cursor.h +++ b/include/rootston/cursor.h @@ -84,4 +84,6 @@ void roots_cursor_handle_request_set_cursor(struct roots_cursor *cursor, void roots_cursor_update_position(struct roots_cursor *cursor, uint32_t time); +void roots_cursor_update_focus(struct roots_cursor *cursor); + #endif diff --git a/rootston/cursor.c b/rootston/cursor.c index 239acf9c..b7dd98ed 100644 --- a/rootston/cursor.c +++ b/rootston/cursor.c @@ -100,7 +100,7 @@ static void seat_view_deco_button(struct roots_seat_view *view, double sx, } static void roots_passthrough_cursor(struct roots_cursor *cursor, - uint32_t time) { + int64_t time) { bool focus_changed; double sx, sy; struct roots_view *view = NULL; @@ -108,11 +108,13 @@ static void roots_passthrough_cursor(struct roots_cursor *cursor, struct roots_desktop *desktop = seat->input->server->desktop; struct wlr_surface *surface = desktop_surface_at(desktop, cursor->cursor->x, cursor->cursor->y, &sx, &sy, &view); + struct wl_client *client = NULL; if (surface) { client = wl_resource_get_client(surface->resource); } - if (surface && !roots_seat_allow_input(cursor->seat, surface->resource)) { + + if (surface && !roots_seat_allow_input(seat, surface->resource)) { return; } @@ -145,7 +147,7 @@ static void roots_passthrough_cursor(struct roots_cursor *cursor, if (surface) { focus_changed = (seat->seat->pointer_state.focused_surface != surface); wlr_seat_pointer_notify_enter(seat->seat, surface, sx, sy); - if (!focus_changed) { + if (!focus_changed && time > 0) { wlr_seat_pointer_notify_motion(seat->seat, time, sx, sy); } } else { @@ -158,6 +160,10 @@ static void roots_passthrough_cursor(struct roots_cursor *cursor, } } +void roots_cursor_update_focus(struct roots_cursor *cursor) { + roots_passthrough_cursor(cursor, -1); +} + void roots_cursor_update_position(struct roots_cursor *cursor, uint32_t time) { struct roots_seat *seat = cursor->seat; diff --git a/rootston/seat.c b/rootston/seat.c index 507254d4..e888bfa5 100644 --- a/rootston/seat.c +++ b/rootston/seat.c @@ -1186,6 +1186,8 @@ void roots_seat_set_focus(struct roots_seat *seat, struct roots_view *view) { wlr_seat_keyboard_notify_enter(seat->seat, view->wlr_surface, NULL, 0, NULL); } + + roots_cursor_update_focus(seat->cursor); } /** @@ -1220,6 +1222,8 @@ void roots_seat_set_focus_layer(struct roots_seat *seat, wlr_seat_keyboard_notify_enter(seat->seat, layer->surface, NULL, 0, NULL); } + + roots_cursor_update_focus(seat->cursor); } void roots_seat_set_exclusive_client(struct roots_seat *seat, From 57690594f497cbc9cc552eee75a94e76226e433e Mon Sep 17 00:00:00 2001 From: Las Date: Wed, 8 Aug 2018 13:59:03 +0200 Subject: [PATCH 05/17] Remove extraneous indentation and conform to coding style --- include/rootston/cursor.h | 22 +++++++++++----------- rootston/cursor.c | 2 +- rootston/desktop.c | 4 ++-- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/include/rootston/cursor.h b/include/rootston/cursor.h index bcbc1c07..547fe333 100644 --- a/include/rootston/cursor.h +++ b/include/rootston/cursor.h @@ -52,37 +52,37 @@ struct roots_cursor *roots_cursor_create(struct roots_seat *seat); void roots_cursor_destroy(struct roots_cursor *cursor); void roots_cursor_handle_motion(struct roots_cursor *cursor, - struct wlr_event_pointer_motion *event); + struct wlr_event_pointer_motion *event); void roots_cursor_handle_motion_absolute(struct roots_cursor *cursor, - struct wlr_event_pointer_motion_absolute *event); + struct wlr_event_pointer_motion_absolute *event); void roots_cursor_handle_button(struct roots_cursor *cursor, - struct wlr_event_pointer_button *event); + struct wlr_event_pointer_button *event); void roots_cursor_handle_axis(struct roots_cursor *cursor, - struct wlr_event_pointer_axis *event); + struct wlr_event_pointer_axis *event); void roots_cursor_handle_touch_down(struct roots_cursor *cursor, - struct wlr_event_touch_down *event); + struct wlr_event_touch_down *event); void roots_cursor_handle_touch_up(struct roots_cursor *cursor, - struct wlr_event_touch_up *event); + struct wlr_event_touch_up *event); void roots_cursor_handle_touch_motion(struct roots_cursor *cursor, - struct wlr_event_touch_motion *event); + struct wlr_event_touch_motion *event); void roots_cursor_handle_tool_axis(struct roots_cursor *cursor, - struct wlr_event_tablet_tool_axis *event); + struct wlr_event_tablet_tool_axis *event); void roots_cursor_handle_tool_tip(struct roots_cursor *cursor, - struct wlr_event_tablet_tool_tip *event); + struct wlr_event_tablet_tool_tip *event); void roots_cursor_handle_request_set_cursor(struct roots_cursor *cursor, - struct wlr_seat_pointer_request_set_cursor_event *event); + struct wlr_seat_pointer_request_set_cursor_event *event); void roots_cursor_update_position(struct roots_cursor *cursor, - uint32_t time); + uint32_t time); void roots_cursor_update_focus(struct roots_cursor *cursor); diff --git a/rootston/cursor.c b/rootston/cursor.c index b7dd98ed..ee9e5c6b 100644 --- a/rootston/cursor.c +++ b/rootston/cursor.c @@ -336,7 +336,7 @@ void roots_cursor_handle_touch_down(struct roots_cursor *cursor, double sx, sy; struct wlr_surface *surface = desktop_surface_at( - desktop, lx, ly, &sx, &sy, NULL); + desktop, lx, ly, &sx, &sy, NULL); uint32_t serial = 0; if (surface && roots_seat_allow_input(cursor->seat, surface->resource)) { diff --git a/rootston/desktop.c b/rootston/desktop.c index 3f6d977e..e120508d 100644 --- a/rootston/desktop.c +++ b/rootston/desktop.c @@ -887,10 +887,10 @@ struct roots_desktop *desktop_create(struct roots_server *server, wlr_input_inhibit_manager_create(server->wl_display); desktop->input_inhibit_activate.notify = input_inhibit_activate; wl_signal_add(&desktop->input_inhibit->events.activate, - &desktop->input_inhibit_activate); + &desktop->input_inhibit_activate); desktop->input_inhibit_deactivate.notify = input_inhibit_deactivate; wl_signal_add(&desktop->input_inhibit->events.deactivate, - &desktop->input_inhibit_deactivate); + &desktop->input_inhibit_deactivate); desktop->virtual_keyboard = wlr_virtual_keyboard_manager_v1_create( server->wl_display); From 6367e0bc93f33868371fc443808a1753625faea6 Mon Sep 17 00:00:00 2001 From: Las Date: Fri, 10 Aug 2018 18:13:41 +0200 Subject: [PATCH 06/17] Add wlr_surface::input_region member This is analogous to the opaque_region member. In addition the code for setting the opaque region is cleaned up. --- include/wlr/types/wlr_surface.h | 5 +++++ types/wlr_surface.c | 18 ++++++++++++------ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/include/wlr/types/wlr_surface.h b/include/wlr/types/wlr_surface.h index 063f9e26..bfbfbcc8 100644 --- a/include/wlr/types/wlr_surface.h +++ b/include/wlr/types/wlr_surface.h @@ -81,6 +81,11 @@ struct wlr_surface { * format, this is set to the whole surface. */ pixman_region32_t opaque_region; + /** + * The current input region, in surface-local coordinates. It is clipped to + * the surface bounds. + */ + pixman_region32_t input_region; /** * `current` contains the current, committed surface state. `pending` * accumulates state changes from the client between commits and shouldn't diff --git a/types/wlr_surface.c b/types/wlr_surface.c index ab1dfc2d..773de460 100644 --- a/types/wlr_surface.c +++ b/types/wlr_surface.c @@ -113,9 +113,6 @@ static void surface_set_opaque_region(struct wl_client *client, struct wl_resource *resource, struct wl_resource *region_resource) { struct wlr_surface *surface = wlr_surface_from_resource(resource); - if ((surface->pending.committed & WLR_SURFACE_STATE_OPAQUE_REGION)) { - pixman_region32_clear(&surface->pending.opaque); - } surface->pending.committed |= WLR_SURFACE_STATE_OPAQUE_REGION; if (region_resource) { pixman_region32_t *region = wlr_region_from_resource(region_resource); @@ -126,7 +123,8 @@ static void surface_set_opaque_region(struct wl_client *client, } static void surface_set_input_region(struct wl_client *client, - struct wl_resource *resource, struct wl_resource *region_resource) { + struct wl_resource *resource, + struct wl_resource *region_resource) { struct wlr_surface *surface = wlr_surface_from_resource(resource); surface->pending.committed |= WLR_SURFACE_STATE_INPUT_REGION; if (region_resource) { @@ -353,9 +351,14 @@ static void surface_update_opaque_region(struct wlr_surface *surface) { return; } - pixman_region32_copy(&surface->opaque_region, &surface->current.opaque); pixman_region32_intersect_rect(&surface->opaque_region, - &surface->opaque_region, + &surface->current.opaque, + 0, 0, surface->current.width, surface->current.height); +} + +static void surface_update_input_region(struct wlr_surface *surface) { + pixman_region32_intersect_rect(&surface->input_region, + &surface->current.input, 0, 0, surface->current.width, surface->current.height); } @@ -380,6 +383,7 @@ static void surface_commit_pending(struct wlr_surface *surface) { surface_apply_damage(surface); } surface_update_opaque_region(surface); + surface_update_input_region(surface); // commit subsurface order struct wlr_subsurface *subsurface; @@ -587,6 +591,7 @@ static void surface_handle_resource_destroy(struct wl_resource *resource) { surface_state_finish(&surface->previous); pixman_region32_fini(&surface->buffer_damage); pixman_region32_fini(&surface->opaque_region); + pixman_region32_fini(&surface->input_region); wlr_buffer_unref(surface->buffer); free(surface); } @@ -633,6 +638,7 @@ struct wlr_surface *wlr_surface_create(struct wl_client *client, wl_list_init(&surface->subsurface_pending_list); pixman_region32_init(&surface->buffer_damage); pixman_region32_init(&surface->opaque_region); + pixman_region32_init(&surface->input_region); wl_signal_add(&renderer->events.destroy, &surface->renderer_destroy); surface->renderer_destroy.notify = surface_handle_renderer_destroy; From 437f5387728703aca859f6fcec021a700472be33 Mon Sep 17 00:00:00 2001 From: Las Date: Fri, 10 Aug 2018 18:18:55 +0200 Subject: [PATCH 07/17] Make wlr_seat->data in rootston point to the wrapping roots_seat --- rootston/seat.c | 1 + 1 file changed, 1 insertion(+) diff --git a/rootston/seat.c b/rootston/seat.c index e888bfa5..929fe504 100644 --- a/rootston/seat.c +++ b/rootston/seat.c @@ -567,6 +567,7 @@ struct roots_seat *roots_seat_create(struct roots_input *input, char *name) { free(seat); return NULL; } + seat->seat->data = seat; roots_seat_init_cursor(seat); if (!seat->cursor) { From fa2e6e7d9d5ffbd782063c89e460a915b29d4a58 Mon Sep 17 00:00:00 2001 From: Las Date: Fri, 10 Aug 2018 18:19:16 +0200 Subject: [PATCH 08/17] Implement pointer-constraints protocol in wlroots and rootston --- examples/meson.build | 4 + examples/pointer-constraints.c | 254 ++++++++++++ include/rootston/cursor.h | 16 + include/rootston/desktop.h | 3 + include/rootston/input.h | 2 +- include/rootston/seat.h | 2 +- include/wlr/types/wlr_box.h | 3 + include/wlr/types/wlr_cursor.h | 12 + .../wlr/types/wlr_pointer_constraints_v1.h | 88 +++++ include/wlr/types/wlr_region.h | 3 +- include/wlr/util/region.h | 5 + protocol/meson.build | 2 + rootston/config.c | 1 + rootston/cursor.c | 212 +++++++++- rootston/desktop.c | 56 +++ rootston/keyboard.c | 8 + rootston/rootston.ini.example | 2 + rootston/seat.c | 32 +- types/meson.build | 1 + types/wlr_box.c | 9 + types/wlr_cursor.c | 6 +- types/wlr_pointer_constraints_v1.c | 362 ++++++++++++++++++ util/region.c | 71 ++++ 23 files changed, 1134 insertions(+), 20 deletions(-) create mode 100644 examples/pointer-constraints.c create mode 100644 include/wlr/types/wlr_pointer_constraints_v1.h create mode 100644 types/wlr_pointer_constraints_v1.c diff --git a/examples/meson.build b/examples/meson.build index b5ad6c98..86c0ddbb 100644 --- a/examples/meson.build +++ b/examples/meson.build @@ -70,6 +70,10 @@ examples = { 'src': 'gamma-control.c', 'dep': [wayland_client, wayland_cursor, wlr_protos, wlroots], }, + 'pointer-constraints': { + 'src': 'pointer-constraints.c', + 'dep': [wayland_client, wlr_protos, wlroots], + }, 'dmabuf-capture': { 'src': 'dmabuf-capture.c', 'dep': [ diff --git a/examples/pointer-constraints.c b/examples/pointer-constraints.c new file mode 100644 index 00000000..8738d475 --- /dev/null +++ b/examples/pointer-constraints.c @@ -0,0 +1,254 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "xdg-shell-client-protocol.h" +#include "pointer-constraints-unstable-v1-client-protocol.h" + +static int width = 512, height = 512; + +static struct wl_compositor *compositor = NULL; +static struct wl_seat *seat = NULL; +static struct xdg_wm_base *wm_base = NULL; +static struct zwp_pointer_constraints_v1 *pointer_constraints = NULL; + +struct wlr_egl egl; +struct wl_egl_window *egl_window; +struct wlr_egl_surface *egl_surface; +struct zwp_locked_pointer_v1* locked_pointer; +struct zwp_confined_pointer_v1* confined_pointer; + +enum { + REGION_TYPE_NONE, + REGION_TYPE_DISJOINT, + REGION_TYPE_JOINT, + REGION_TYPE_MAX +} region_type = REGION_TYPE_NONE; + +struct wl_region *regions[3]; + +static void draw(void) { + eglMakeCurrent(egl.display, egl_surface, egl_surface, egl.context); + + float color[] = {1.0, 1.0, 0.0, 1.0}; + + glViewport(0, 0, width, height); + glClearColor(color[0], color[1], color[2], 1.0); + glClear(GL_COLOR_BUFFER_BIT); + + eglSwapBuffers(egl.display, egl_surface); +} + +static void pointer_handle_button(void *data, struct wl_pointer *pointer, uint32_t serial, + uint32_t time, uint32_t button, uint32_t state_w) { + struct wl_surface *surface = data; + + if (button == BTN_LEFT && state_w == WL_POINTER_BUTTON_STATE_PRESSED) { + region_type = (region_type + 1) % REGION_TYPE_MAX; + + if (locked_pointer) { + zwp_locked_pointer_v1_set_region(locked_pointer, regions[region_type]); + } else if (confined_pointer) { + zwp_confined_pointer_v1_set_region(confined_pointer, regions[region_type]); + } + + wl_surface_commit(surface); + } +} + +static void pointer_handle_enter(void *data, struct wl_pointer *wl_pointer, + uint32_t serial, struct wl_surface *surface, + wl_fixed_t surface_x, wl_fixed_t surface_y) { + // This space intentionally left blank +} + +static void pointer_handle_leave(void *data, struct wl_pointer *wl_pointer, + uint32_t serial, struct wl_surface *surface) { + // This space intentionally left blank +} + +static void pointer_handle_motion(void *data, struct wl_pointer *wl_pointer, + uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) { + // This space intentionally left blank +} + +static void pointer_handle_axis(void *data, struct wl_pointer *wl_pointer, + uint32_t time, uint32_t axis, wl_fixed_t value) { + // This space intentionally left blank +} + +static void pointer_handle_frame(void *data, struct wl_pointer *wl_pointer) { + // This space intentionally left blank +} + +static void pointer_handle_axis_source(void *data, + struct wl_pointer *wl_pointer, uint32_t axis_source) { + // This space intentionally left blank +} + +static void pointer_handle_axis_stop(void *data, + struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis) { + // This space intentionally left blank +} + +static void pointer_handle_axis_discrete(void *data, + struct wl_pointer *wl_pointer, uint32_t axis, int32_t discrete) { + // This space intentionally left blank +} + +static const struct wl_pointer_listener pointer_listener = { + .enter = pointer_handle_enter, + .leave = pointer_handle_leave, + .motion = pointer_handle_motion, + .button = pointer_handle_button, + .axis = pointer_handle_axis, + .frame = pointer_handle_frame, + .axis_source = pointer_handle_axis_source, + .axis_stop = pointer_handle_axis_stop, + .axis_discrete = pointer_handle_axis_discrete, +}; + +static void xdg_surface_handle_configure(void *data, + struct xdg_surface *xdg_surface, uint32_t serial) { + xdg_surface_ack_configure(xdg_surface, serial); + wl_egl_window_resize(egl_window, width, height, 0, 0); + draw(); +} + +static const struct xdg_surface_listener xdg_surface_listener = { + .configure = xdg_surface_handle_configure, +}; + +static void xdg_toplevel_handle_configure(void *data, + struct xdg_toplevel *xdg_toplevel, int32_t w, int32_t h, + struct wl_array *states) { + width = w; + height = h; +} + +static void xdg_toplevel_handle_close(void *data, + struct xdg_toplevel *xdg_toplevel) { + exit(EXIT_SUCCESS); +} + +static const struct xdg_toplevel_listener xdg_toplevel_listener = { + .configure = xdg_toplevel_handle_configure, + .close = xdg_toplevel_handle_close, +}; + +static void handle_global(void *data, struct wl_registry *registry, + uint32_t name, const char *interface, uint32_t version) { + if (strcmp(interface, wl_compositor_interface.name) == 0) { + compositor = wl_registry_bind(registry, name, + &wl_compositor_interface, 1); + } else if (strcmp(interface, xdg_wm_base_interface.name) == 0) { + wm_base = wl_registry_bind(registry, name, &xdg_wm_base_interface, 1); + } else if (strcmp(interface, wl_seat_interface.name) == 0) { + seat = wl_registry_bind(registry, name, &wl_seat_interface, version); + } else if (strcmp(interface, zwp_pointer_constraints_v1_interface.name) == 0) { + pointer_constraints = wl_registry_bind(registry, name, + &zwp_pointer_constraints_v1_interface, version); + } +} + +static const struct wl_registry_listener registry_listener = { + .global = handle_global, + .global_remove = NULL, +}; + +int main(int argc, char **argv) { + if (argc != 4) { + goto invalid_args; + } + + bool lock; + if (strcmp(argv[1], "lock") == 0) { + lock = true; + } else if (strcmp(argv[1], "confine") == 0) { + lock = false; + } else { + goto invalid_args; + } + + enum zwp_pointer_constraints_v1_lifetime lifetime; + if (strcmp(argv[2], "oneshot") == 0) { + lifetime = ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_ONESHOT; + } else if (strcmp(argv[2], "persistent") == 0) { + lifetime = ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT; + } else { + goto invalid_args; + } + + if (strcmp(argv[3], "no-region") == 0) { + region_type = REGION_TYPE_NONE; + } else if (strcmp(argv[3], "disjoint-region") == 0) { + region_type = REGION_TYPE_DISJOINT; + } else if (strcmp(argv[3], "joint-region") == 0) { + region_type = REGION_TYPE_JOINT; + } + + struct wl_display *display = wl_display_connect(NULL); + + struct wl_registry *registry = wl_display_get_registry(display); + wl_registry_add_listener(registry, ®istry_listener, NULL); + wl_display_dispatch(display); + wl_display_roundtrip(display); + + struct wl_region *disjoint_region = wl_compositor_create_region(compositor); + wl_region_add(disjoint_region, 0, 0, 255, 256); + wl_region_add(disjoint_region, 257, 0, 255, 256); + regions[REGION_TYPE_DISJOINT] = disjoint_region; + + struct wl_region *joint_region = wl_compositor_create_region(compositor); + wl_region_add(joint_region, 0, 0, 256, 256); + wl_region_add(joint_region, 256, 0, 256, 256); + wl_region_add(joint_region, 256, 256, 256, 256); + regions[REGION_TYPE_JOINT] = joint_region; + + wlr_egl_init(&egl, EGL_PLATFORM_WAYLAND_EXT, display, NULL, + WL_SHM_FORMAT_ARGB8888); + + struct wl_surface *surface = wl_compositor_create_surface(compositor); + struct xdg_surface *xdg_surface = + xdg_wm_base_get_xdg_surface(wm_base, surface); + struct xdg_toplevel *xdg_toplevel = xdg_surface_get_toplevel(xdg_surface); + + xdg_surface_add_listener(xdg_surface, &xdg_surface_listener, NULL); + xdg_toplevel_add_listener(xdg_toplevel, &xdg_toplevel_listener, NULL); + + struct wl_pointer *pointer = wl_seat_get_pointer(seat); + wl_pointer_add_listener(pointer, &pointer_listener, surface); + + if (lock) { + locked_pointer = zwp_pointer_constraints_v1_lock_pointer( + pointer_constraints, surface, pointer, regions[region_type], lifetime); + + zwp_locked_pointer_v1_set_cursor_position_hint(locked_pointer, wl_fixed_from_int(128), wl_fixed_from_int(128)); + } else { + confined_pointer = zwp_pointer_constraints_v1_confine_pointer( + pointer_constraints, surface, pointer, regions[region_type], lifetime); + } + + wl_surface_commit(surface); + + egl_window = wl_egl_window_create(surface, width, height); + egl_surface = wlr_egl_create_surface(&egl, egl_window); + + wl_display_roundtrip(display); + + draw(); + + while (wl_display_dispatch(display) != -1) {} + + return EXIT_SUCCESS; + + invalid_args: { + fprintf(stderr, "pointer-constraints " + "\n"); + exit(EXIT_FAILURE); + } +} diff --git a/include/rootston/cursor.h b/include/rootston/cursor.h index 547fe333..b5bb682f 100644 --- a/include/rootston/cursor.h +++ b/include/rootston/cursor.h @@ -1,6 +1,7 @@ #ifndef ROOTSTON_CURSOR_H #define ROOTSTON_CURSOR_H +#include #include "rootston/seat.h" enum roots_cursor_mode { @@ -14,6 +15,9 @@ struct roots_cursor { struct roots_seat *seat; struct wlr_cursor *cursor; + struct wlr_pointer_constraint_v1 *active_constraint; + pixman_region32_t confine; // invalid if active_constraint == NULL + const char *default_xcursor; enum roots_cursor_mode mode; @@ -45,6 +49,10 @@ struct roots_cursor { struct wl_listener tool_button; struct wl_listener request_set_cursor; + + struct wl_listener focus_change; + + struct wl_listener constraint_commit; }; struct roots_cursor *roots_cursor_create(struct roots_seat *seat); @@ -81,9 +89,17 @@ void roots_cursor_handle_tool_tip(struct roots_cursor *cursor, void roots_cursor_handle_request_set_cursor(struct roots_cursor *cursor, struct wlr_seat_pointer_request_set_cursor_event *event); +void roots_cursor_handle_focus_change(struct roots_cursor *cursor, + struct wlr_seat_pointer_focus_change_event *event); + +void roots_cursor_handle_constraint_commit(struct roots_cursor *cursor); + void roots_cursor_update_position(struct roots_cursor *cursor, uint32_t time); void roots_cursor_update_focus(struct roots_cursor *cursor); +void roots_cursor_constrain(struct roots_cursor *cursor, + struct wlr_pointer_constraint_v1 *constraint, double sx, double sy); + #endif diff --git a/include/rootston/desktop.h b/include/rootston/desktop.h index 89d8af4a..dac694d1 100644 --- a/include/rootston/desktop.h +++ b/include/rootston/desktop.h @@ -56,6 +56,7 @@ struct roots_desktop { struct wlr_virtual_keyboard_manager_v1 *virtual_keyboard; struct wlr_screencopy_manager_v1 *screencopy; struct wlr_tablet_manager_v2 *tablet_v2; + struct wlr_pointer_constraints_v1 *pointer_constraints; struct wl_listener new_output; struct wl_listener layout_change; @@ -67,6 +68,8 @@ struct roots_desktop { struct wl_listener input_inhibit_activate; struct wl_listener input_inhibit_deactivate; struct wl_listener virtual_keyboard_new; + struct wl_listener constraint_create; + struct wl_listener constraint_destroy; #ifdef WLR_HAS_XWAYLAND struct wlr_xwayland *xwayland; diff --git a/include/rootston/input.h b/include/rootston/input.h index 2cdb13e6..31810b4d 100644 --- a/include/rootston/input.h +++ b/include/rootston/input.h @@ -16,7 +16,7 @@ struct roots_input { struct wl_listener new_input; - struct wl_list seats; + struct wl_list seats; // roots_seat::link }; struct roots_input *input_create(struct roots_server *server, diff --git a/include/rootston/seat.h b/include/rootston/seat.h index c5e584b6..a3192d26 100644 --- a/include/rootston/seat.h +++ b/include/rootston/seat.h @@ -10,7 +10,7 @@ struct roots_seat { struct roots_input *input; struct wlr_seat *seat; struct roots_cursor *cursor; - struct wl_list link; + struct wl_list link; // roots_input::seats // coordinates of the first touch point if it exists int32_t touch_id; diff --git a/include/wlr/types/wlr_box.h b/include/wlr/types/wlr_box.h index 11a53b56..8e25918b 100644 --- a/include/wlr/types/wlr_box.h +++ b/include/wlr/types/wlr_box.h @@ -9,6 +9,7 @@ #ifndef WLR_TYPES_WLR_BOX_H #define WLR_TYPES_WLR_BOX_H +#include #include #include @@ -40,4 +41,6 @@ void wlr_box_transform(const struct wlr_box *box, void wlr_box_rotated_bounds(const struct wlr_box *box, float rotation, struct wlr_box *dest); +void wlr_box_from_pixman_box32(const pixman_box32_t box, struct wlr_box *dest); + #endif diff --git a/include/wlr/types/wlr_cursor.h b/include/wlr/types/wlr_cursor.h index ba582be9..44ced1f0 100644 --- a/include/wlr/types/wlr_cursor.h +++ b/include/wlr/types/wlr_cursor.h @@ -91,6 +91,18 @@ bool wlr_cursor_warp(struct wlr_cursor *cur, struct wlr_input_device *dev, void wlr_cursor_absolute_to_layout_coords(struct wlr_cursor *cur, struct wlr_input_device *dev, double x, double y, double *lx, double *ly); + +/** + * Warp the cursor to the given x and y coordinates. If the given point is out + * of the layout boundaries or constraints, the closest point will be used. + * If one coordinate is NAN, it will be ignored. + * + * `dev` may be passed to respect device mapping constraints. If `dev` is NULL, + * device mapping constraints will be ignored. + */ +void wlr_cursor_warp_closest(struct wlr_cursor *cur, + struct wlr_input_device *dev, double x, double y); + /** * Warp the cursor to the given x and y in absolute 0..1 coordinates. If the * given point is out of the layout boundaries or constraints, the closest point diff --git a/include/wlr/types/wlr_pointer_constraints_v1.h b/include/wlr/types/wlr_pointer_constraints_v1.h new file mode 100644 index 00000000..b8015a57 --- /dev/null +++ b/include/wlr/types/wlr_pointer_constraints_v1.h @@ -0,0 +1,88 @@ +#ifndef WLR_TYPES_WLR_POINTER_CONSTRAINTS_V1_H +#define WLR_TYPES_WLR_POINTER_CONSTRAINTS_V1_H + +#include +#include +#include +#include +#include +#include "pointer-constraints-unstable-v1-protocol.h" + +struct wlr_seat; + +enum wlr_pointer_constraint_v1_type { + WLR_POINTER_CONSTRAINT_V1_LOCKED, + WLR_POINTER_CONSTRAINT_V1_CONFINED, +}; + +struct wlr_pointer_constraint_v1_state { + pixman_region32_t *region; + + // only valid for locked_pointer + struct { + double x, y; + bool valid; + } cursor_hint; +}; + +struct wlr_pointer_constraint_v1 { + struct wlr_pointer_constraints_v1 *pointer_constraints; + + struct wl_resource *resource; + struct wlr_surface *surface; + struct wlr_seat *seat; + enum zwp_pointer_constraints_v1_lifetime lifetime; + enum wlr_pointer_constraint_v1_type type; + pixman_region32_t region; + + struct wlr_pointer_constraint_v1_state current, pending; + + struct wl_listener surface_commit; + struct wl_listener surface_destroy; + struct wl_listener seat_destroy; + + struct wl_list link; // wlr_pointer_constraints_v1::constraints + + void *data; +}; + +struct wlr_pointer_constraints_v1 { + struct wl_list wl_resources; // wl_resource_get_link + struct wl_global *wl_global; + + struct { + /** + * Called when a new pointer constraint is created. + * + * data: wlr_pointer_constraint_v1* + */ + struct wl_signal constraint_create; + /** + * Called when a pointer constraint is destroyed. + * + * data: wlr_pointer_constraint_v1* + */ + struct wl_signal constraint_destroy; + } events; + + struct wl_list constraints; // wlr_pointer_constraint_v1::link + + void *data; +}; + +struct wlr_pointer_constraints_v1 *wlr_pointer_constraints_v1_create( + struct wl_display *display); +void wlr_pointer_constraints_v1_destroy( + struct wlr_pointer_constraints_v1 *wlr_pointer_constraints_v1); + +struct wlr_pointer_constraint_v1 *wlr_pointer_constraints_v1_constraint_for_surface( + struct wlr_pointer_constraints_v1 *wlr_pointer_constraints_v1, + struct wlr_surface *surface, struct wlr_seat *seat); + +void wlr_pointer_constraint_v1_send_activated( + struct wlr_pointer_constraint_v1 *constraint); +void wlr_pointer_constraint_v1_send_deactivated( + struct wlr_pointer_constraint_v1 *constraint); + + +#endif diff --git a/include/wlr/types/wlr_region.h b/include/wlr/types/wlr_region.h index 3c4a0532..ec7f73aa 100644 --- a/include/wlr/types/wlr_region.h +++ b/include/wlr/types/wlr_region.h @@ -10,8 +10,7 @@ #define WLR_TYPES_WLR_REGION_H #include - -struct wl_resource; +#include /* * Creates a new region resource with the provided new ID. If `resource_list` is diff --git a/include/wlr/util/region.h b/include/wlr/util/region.h index 32387bfb..4aca07e1 100644 --- a/include/wlr/util/region.h +++ b/include/wlr/util/region.h @@ -16,6 +16,8 @@ #ifndef WLR_UTIL_REGION_H #define WLR_UTIL_REGION_H + +#include #include #include @@ -48,4 +50,7 @@ void wlr_region_expand(pixman_region32_t *dst, pixman_region32_t *src, void wlr_region_rotated_bounds(pixman_region32_t *dst, pixman_region32_t *src, float rotation, int ox, int oy); +bool wlr_region_confine(pixman_region32_t *region, double x1, double y1, double x2, + double y2, double *x2_out, double *y2_out); + #endif diff --git a/protocol/meson.build b/protocol/meson.build index 2d2a73ed..a02ca5f5 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -18,6 +18,7 @@ protocols = [ [wl_protocol_dir, 'unstable/xdg-decoration/xdg-decoration-unstable-v1.xml'], [wl_protocol_dir, 'unstable/xdg-output/xdg-output-unstable-v1.xml'], [wl_protocol_dir, 'unstable/xdg-shell/xdg-shell-unstable-v6.xml'], + [wl_protocol_dir, 'unstable/pointer-constraints/pointer-constraints-unstable-v1.xml'], 'gamma-control.xml', 'gtk-primary-selection.xml', 'idle.xml', @@ -36,6 +37,7 @@ client_protocols = [ [wl_protocol_dir, 'unstable/idle-inhibit/idle-inhibit-unstable-v1.xml'], [wl_protocol_dir, 'unstable/xdg-decoration/xdg-decoration-unstable-v1.xml'], [wl_protocol_dir, 'unstable/xdg-shell/xdg-shell-unstable-v6.xml'], + [wl_protocol_dir, 'unstable/pointer-constraints/pointer-constraints-unstable-v1.xml'], 'idle.xml', 'screenshooter.xml', 'wlr-export-dmabuf-unstable-v1.xml', diff --git a/rootston/config.c b/rootston/config.c index 92d90de1..5c168679 100644 --- a/rootston/config.c +++ b/rootston/config.c @@ -506,6 +506,7 @@ struct roots_config *roots_config_create_from_args(int argc, char *argv[]) { add_binding_config(&config->bindings, "Logo+Shift+E", "exit"); add_binding_config(&config->bindings, "Ctrl+q", "close"); add_binding_config(&config->bindings, "Alt+Tab", "next_window"); + add_binding_config(&config->bindings, "Logo+Escape", "break_pointer_constraint"); struct roots_keyboard_config *kc = calloc(1, sizeof(struct roots_keyboard_config)); kc->meta_key = WLR_MODIFIER_LOGO; diff --git a/rootston/cursor.c b/rootston/cursor.c index ee9e5c6b..0aaff132 100644 --- a/rootston/cursor.c +++ b/rootston/cursor.c @@ -1,9 +1,12 @@ #define _XOPEN_SOURCE 700 +#include #include #include +#include #include #include #include +#include #ifdef __linux__ #include #elif __FreeBSD__ @@ -11,6 +14,7 @@ #endif #include "rootston/cursor.h" #include "rootston/desktop.h" +#include "rootston/view.h" #include "rootston/xcursor.h" struct roots_cursor *roots_cursor_create(struct roots_seat *seat) { @@ -303,15 +307,86 @@ static void roots_cursor_press_button(struct roots_cursor *cursor, void roots_cursor_handle_motion(struct roots_cursor *cursor, struct wlr_event_pointer_motion *event) { - wlr_cursor_move(cursor->cursor, event->device, - event->delta_x, event->delta_y); + double dx = event->delta_x; + double dy = event->delta_y; + + if (cursor->active_constraint) { + struct roots_view *view = cursor->pointer_view->view; + assert(view); + + double center_x = view->x + view->width / 2.; + double center_y = view->y + view->height / 2.; + + double lx1 = cursor->cursor->x; + double ly1 = cursor->cursor->y; + + double lx2 = lx1 + dx; + double ly2 = ly1 + dy; + + // Optimization for most common case. + // This also makes sure that we don't encounter + // precision bugs in the most common case. + if (view->rotation == 0.0) { + double sx1 = lx1 - view->x; + double sy1 = ly1 - view->y; + + double sx2 = lx2 - view->x; + double sy2 = ly2 - view->y; + + double sx2_confined, sy2_confined; + if (!wlr_region_confine(&cursor->confine, sx1, sy1, sx2, sy2, &sx2_confined, &sy2_confined)) { + return; + } + + dx = sx2_confined - sx1; + dy = sy2_confined - sy1; + } else { + assert(false); + double c = cos(view->rotation); + double s = sin(view->rotation); + + double sx1 = c * (lx1 - center_x) - s * (ly1 - center_y) + view->width / 2.; + double sy1 = s * (lx1 - center_x) + c * (ly1 - center_y) + view->height / 2.; + + double sx2 = c * (lx2 - center_x) - s * (ly2 - center_y) + view->width / 2.; + double sy2 = s * (lx2 - center_x) + c * (ly2 - center_y) + view->height / 2.; + + double sx2_confined, sy2_confined; + if (!wlr_region_confine(&cursor->confine, sx1, sy1, sx2, sy2, &sx2_confined, &sy2_confined)) { + return; + } + + // avoid NaNs + double fraction = (sx2 - sx1) > (sy2 - sy1) ? + (sx2_confined - sx1) / (sx2 - sx1) : + (sy2_confined - sy1) / (sy2 - sy1); + + dx *= fraction; + dy *= fraction; + } + } + + wlr_cursor_move(cursor->cursor, event->device, dx, dy); roots_cursor_update_position(cursor, event->time_msec); } void roots_cursor_handle_motion_absolute(struct roots_cursor *cursor, struct wlr_event_pointer_motion_absolute *event) { - wlr_cursor_warp_absolute(cursor->cursor, - event->device, event->x, event->y); + double lx, ly; + wlr_cursor_absolute_to_layout_coords(cursor->cursor, event->device, event->x, + event->y, &lx, &ly); + + if (cursor->pointer_view) { + struct roots_view *view = cursor->pointer_view->view; + + if (cursor->active_constraint && + !pixman_region32_contains_point(&cursor->confine, + lx - view->x, ly - view->y, NULL)) { + return; + } + } + + wlr_cursor_warp_closest(cursor->cursor, event->device, lx, ly); roots_cursor_update_position(cursor, event->time_msec); } @@ -405,18 +480,34 @@ void roots_cursor_handle_touch_motion(struct roots_cursor *cursor, void roots_cursor_handle_tool_axis(struct roots_cursor *cursor, struct wlr_event_tablet_tool_axis *event) { + double x = NAN, y = NAN; if ((event->updated_axes & WLR_TABLET_TOOL_AXIS_X) && (event->updated_axes & WLR_TABLET_TOOL_AXIS_Y)) { - wlr_cursor_warp_absolute(cursor->cursor, event->device, - event->x, event->y); - roots_cursor_update_position(cursor, event->time_msec); + x = event->x; + y = event->y; } else if ((event->updated_axes & WLR_TABLET_TOOL_AXIS_X)) { - wlr_cursor_warp_absolute(cursor->cursor, event->device, event->x, -1); - roots_cursor_update_position(cursor, event->time_msec); + x = event->x; } else if ((event->updated_axes & WLR_TABLET_TOOL_AXIS_Y)) { - wlr_cursor_warp_absolute(cursor->cursor, event->device, -1, event->y); - roots_cursor_update_position(cursor, event->time_msec); + y = event->y; } + + double lx, ly; + wlr_cursor_absolute_to_layout_coords(cursor->cursor, event->device, + x, y, &lx, &ly); + + + if (cursor->pointer_view) { + struct roots_view *view = cursor->pointer_view->view; + + if (cursor->active_constraint && + !pixman_region32_contains_point(&cursor->confine, + lx - view->x, ly - view->y, NULL)) { + return; + } + } + + wlr_cursor_warp_closest(cursor->cursor, event->device, lx, ly); + roots_cursor_update_position(cursor, event->time_msec); } void roots_cursor_handle_tool_tip(struct roots_cursor *cursor, @@ -446,3 +537,102 @@ void roots_cursor_handle_request_set_cursor(struct roots_cursor *cursor, event->hotspot_y); cursor->cursor_client = event->seat_client->client; } + +void roots_cursor_handle_focus_change(struct roots_cursor *cursor, + struct wlr_seat_pointer_focus_change_event *event) { + double sx = event->sx; + double sy = event->sy; + + double lx = cursor->cursor->x; + double ly = cursor->cursor->y; + + wlr_log(WLR_DEBUG, "entered surface %p, lx: %f, ly: %f, sx: %f, sy: %f", + event->new_surface, lx, ly, sx, sy); + + roots_cursor_constrain(cursor, + wlr_pointer_constraints_v1_constraint_for_surface( + cursor->seat->input->server->desktop->pointer_constraints, + event->new_surface, cursor->seat->seat), + sx, sy); +} + +void roots_cursor_handle_constraint_commit(struct roots_cursor *cursor) { + struct roots_desktop *desktop = cursor->seat->input->server->desktop; + + struct roots_view *view; + double sx, sy; + struct wlr_surface *surface = desktop_surface_at(desktop, + cursor->cursor->x, cursor->cursor->y, &sx, &sy, &view); + // This should never happen but views move around right when they're + // created from (0, 0) to their actual coordinates. + if (surface != cursor->active_constraint->surface) { + roots_cursor_update_focus(cursor); + } else { + roots_cursor_constrain(cursor, cursor->active_constraint, sx, sy); + } +} + +void roots_cursor_constrain(struct roots_cursor *cursor, + struct wlr_pointer_constraint_v1 *constraint, double sx, double sy) { + if (cursor->active_constraint != constraint) { + wlr_log(WLR_DEBUG, "roots_cursor_constrain(%p, %p)", cursor, constraint); + wlr_log(WLR_DEBUG, "cursor->active_constraint: %p", cursor->active_constraint); + + if (cursor->active_constraint) { + wlr_pointer_constraint_v1_send_deactivated(cursor->active_constraint); + if (cursor->constraint_commit.link.next) { + wl_list_remove(&cursor->constraint_commit.link); + } + } + + cursor->active_constraint = constraint; + + if (!constraint) { + return; + } + + wlr_pointer_constraint_v1_send_activated(constraint); + wl_signal_add(&constraint->surface->events.commit, + &cursor->constraint_commit); + } else if (constraint == NULL) { + return; + } + + pixman_region32_clear(&cursor->confine); + + pixman_region32_t *region = &constraint->region; + + if (!pixman_region32_contains_point(region, sx, sy, NULL)) { + // Warp into region if possible + int nboxes; + pixman_box32_t *boxes = pixman_region32_rectangles(region, &nboxes); + if (nboxes > 0) { + struct roots_view *view = cursor->pointer_view->view; + + double sx = (boxes[0].x1 + boxes[0].x2) / 2.; + double sy = (boxes[0].y1 + boxes[0].y2) / 2.; + + double lx, ly; + if (view->rotation == 0.0) { + lx = sx + view->x; + ly = sy + view->y; + } else { + double c = cos(view->rotation); + double s = sin(view->rotation); + + double center_x = view->width / 2.; + double center_y = view->height / 2.; + + lx = c * (sx - center_x) - s * (sy - center_y) + center_x + view->x; + ly = s * (sx - center_x) + c * (sy - center_y) + center_y + view->y; + } + + wlr_cursor_warp_closest(cursor->cursor, NULL, lx, ly); + } + } + + // A locked pointer will result in an empty region, thus disallowing all movement + if (constraint->type == WLR_POINTER_CONSTRAINT_V1_CONFINED) { + pixman_region32_copy(&cursor->confine, region); + } +} diff --git a/rootston/desktop.c b/rootston/desktop.c index e120508d..d469c263 100644 --- a/rootston/desktop.c +++ b/rootston/desktop.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -776,6 +777,53 @@ static void input_inhibit_deactivate(struct wl_listener *listener, void *data) { } } +static void handle_constraint_create( + struct wl_listener *listener, + struct wlr_pointer_constraint_v1 *constraint) { + struct roots_seat* seat = constraint->seat->data; + + double sx, sy; + struct wlr_surface *surface = desktop_surface_at(seat->input->server->desktop, + seat->cursor->cursor->x, seat->cursor->cursor->y, &sx, &sy, NULL); + + if (surface == constraint->surface) { + assert(!seat->cursor->active_constraint); + roots_cursor_constrain(seat->cursor, constraint, sx, sy); + } +} + +static void handle_constraint_destroy( + struct wl_listener *listener, + struct wlr_pointer_constraint_v1 *constraint) { + struct roots_seat* seat = constraint->seat->data; + if (seat->cursor->active_constraint == constraint) { + roots_cursor_constrain(seat->cursor, NULL, NAN, NAN); + if (constraint->current.cursor_hint.valid && seat->cursor->pointer_view) { + double sx = constraint->current.cursor_hint.x; + double sy = constraint->current.cursor_hint.y; + + struct roots_view *view = seat->cursor->pointer_view->view; + + double lx, ly; + if (view->rotation == 0.0) { + lx = sx + view->x; + ly = sy + view->y; + } else { + double c = cos(view->rotation); + double s = sin(view->rotation); + + double center_x = view->width / 2.; + double center_y = view->height / 2.; + + lx = c * (sx - center_x) - s * (sy - center_y) + center_x + view->x; + ly = s * (sx - center_x) + c * (sy - center_y) + center_y + view->y; + } + + wlr_cursor_warp(seat->cursor->cursor, NULL, lx, ly); + } + } +} + struct roots_desktop *desktop_create(struct roots_server *server, struct roots_config *config) { wlr_log(WLR_DEBUG, "Initializing roots desktop"); @@ -906,6 +954,14 @@ struct roots_desktop *desktop_create(struct roots_server *server, &desktop->xdg_toplevel_decoration); desktop->xdg_toplevel_decoration.notify = handle_xdg_toplevel_decoration; + desktop->pointer_constraints = wlr_pointer_constraints_v1_create(server->wl_display); + desktop->constraint_destroy.notify = (wl_notify_func_t)handle_constraint_destroy; + wl_signal_add(&desktop->pointer_constraints->events.constraint_destroy, + &desktop->constraint_destroy); + desktop->constraint_create.notify = (wl_notify_func_t)handle_constraint_create; + wl_signal_add(&desktop->pointer_constraints->events.constraint_create, + &desktop->constraint_create); + return desktop; } diff --git a/rootston/keyboard.c b/rootston/keyboard.c index b5a8093b..6ba0bd6d 100644 --- a/rootston/keyboard.c +++ b/rootston/keyboard.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -176,6 +177,13 @@ static void keyboard_binding_execute(struct roots_keyboard *keyboard, decoration->wlr_decoration, mode); } } + } else if (strcmp(command, "break_pointer_constraint") == 0) { + struct wl_list *list = + &keyboard->input->seats; + struct roots_seat *seat; + wl_list_for_each(seat, list, link) { + roots_cursor_constrain(seat->cursor, NULL, NAN, NAN); + } } else { wlr_log(WLR_ERROR, "unknown binding command: %s", command); } diff --git a/rootston/rootston.ini.example b/rootston/rootston.ini.example index bb0efa44..4b75e9c6 100644 --- a/rootston/rootston.ini.example +++ b/rootston/rootston.ini.example @@ -53,9 +53,11 @@ meta-key = Logo # - "close" to close the current view # - "next_window" to cycle through windows # - "alpha" to cycle a window's alpha channel +# - "break_pointer_constraint" to decline and deactivate all pointer constraints [bindings] Logo+Shift+e = exit Logo+q = close Logo+m = maximize +Logo+Escape = break_pointer_constraint Alt+Tab = next_window Ctrl+Shift+a = alpha diff --git a/rootston/seat.c b/rootston/seat.c index 929fe504..466f1429 100644 --- a/rootston/seat.c +++ b/rootston/seat.c @@ -299,6 +299,22 @@ static void handle_request_set_cursor(struct wl_listener *listener, roots_cursor_handle_request_set_cursor(cursor, event); } +static void handle_pointer_focus_change(struct wl_listener *listener, + void *data) { + struct roots_cursor *cursor = + wl_container_of(listener, cursor, focus_change); + struct wlr_seat_pointer_focus_change_event *event = data; + roots_cursor_handle_focus_change(cursor, event); +} + +static void handle_constraint_commit(struct wl_listener *listener, + void *data) { + struct roots_cursor *cursor = + wl_container_of(listener, cursor, constraint_commit); + assert(cursor->active_constraint->surface == data); + roots_cursor_handle_constraint_commit(cursor); +} + static void seat_reset_device_mappings(struct roots_seat *seat, struct wlr_input_device *device) { struct wlr_cursor *cursor = seat->cursor->cursor; @@ -434,6 +450,13 @@ static void roots_seat_init_cursor(struct roots_seat *seat) { wl_signal_add(&seat->seat->events.request_set_cursor, &seat->cursor->request_set_cursor); seat->cursor->request_set_cursor.notify = handle_request_set_cursor; + + wl_signal_add(&seat->seat->pointer_state.events.focus_change, + &seat->cursor->focus_change); + seat->cursor->focus_change.notify = handle_pointer_focus_change; + + wl_list_init(&seat->cursor->constraint_commit.link); + seat->cursor->constraint_commit.notify = handle_constraint_commit; } static void roots_drag_icon_handle_surface_commit(struct wl_listener *listener, @@ -1188,7 +1211,9 @@ void roots_seat_set_focus(struct roots_seat *seat, struct roots_view *view) { NULL, 0, NULL); } - roots_cursor_update_focus(seat->cursor); + if (seat->cursor) { + roots_cursor_update_focus(seat->cursor); + } } /** @@ -1224,7 +1249,10 @@ void roots_seat_set_focus_layer(struct roots_seat *seat, NULL, 0, NULL); } - roots_cursor_update_focus(seat->cursor); + + if (seat->cursor) { + roots_cursor_update_focus(seat->cursor); + } } void roots_seat_set_exclusive_client(struct roots_seat *seat, diff --git a/types/meson.build b/types/meson.build index 1329060e..038f7de4 100644 --- a/types/meson.build +++ b/types/meson.build @@ -39,6 +39,7 @@ lib_wlr_types = static_library( 'wlr_output_layout.c', 'wlr_output.c', 'wlr_pointer.c', + 'wlr_pointer_constraints_v1.c', 'wlr_primary_selection.c', 'wlr_region.c', 'wlr_screenshooter.c', diff --git a/types/wlr_box.c b/types/wlr_box.c index ada6a733..44a80dc2 100644 --- a/types/wlr_box.c +++ b/types/wlr_box.c @@ -143,3 +143,12 @@ void wlr_box_rotated_bounds(const struct wlr_box *box, float rotation, dest->y = floor(fmin(y1, y2)); dest->height = ceil(fmax(y1, y2) - fmin(y1, y2)); } + +void wlr_box_from_pixman_box32(const pixman_box32_t box, struct wlr_box *dest) { + *dest = (struct wlr_box){ + .x = box.x1, + .y = box.y1, + .width = box.x2 - box.x1, + .height = box.y2 - box.y1, + }; +} diff --git a/types/wlr_cursor.c b/types/wlr_cursor.c index 49f11b20..e97dd840 100644 --- a/types/wlr_cursor.c +++ b/types/wlr_cursor.c @@ -252,7 +252,7 @@ bool wlr_cursor_warp(struct wlr_cursor *cur, struct wlr_input_device *dev, return result; } -static void cursor_warp_closest(struct wlr_cursor *cur, +void wlr_cursor_warp_closest(struct wlr_cursor *cur, struct wlr_input_device *dev, double lx, double ly) { struct wlr_box *mapping = get_mapping(cur, dev); if (mapping) { @@ -286,7 +286,7 @@ void wlr_cursor_warp_absolute(struct wlr_cursor *cur, double lx, ly; wlr_cursor_absolute_to_layout_coords(cur, dev, x, y, &lx, &ly); - cursor_warp_closest(cur, dev, lx, ly); + wlr_cursor_warp_closest(cur, dev, lx, ly); } void wlr_cursor_move(struct wlr_cursor *cur, struct wlr_input_device *dev, @@ -296,7 +296,7 @@ void wlr_cursor_move(struct wlr_cursor *cur, struct wlr_input_device *dev, double lx = !isnan(delta_x) ? cur->x + delta_x : cur->x; double ly = !isnan(delta_y) ? cur->y + delta_y : cur->y; - cursor_warp_closest(cur, dev, lx, ly); + wlr_cursor_warp_closest(cur, dev, lx, ly); } void wlr_cursor_set_image(struct wlr_cursor *cur, const uint8_t *pixels, diff --git a/types/wlr_pointer_constraints_v1.c b/types/wlr_pointer_constraints_v1.c new file mode 100644 index 00000000..2d2750b5 --- /dev/null +++ b/types/wlr_pointer_constraints_v1.c @@ -0,0 +1,362 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "util/signal.h" + +static const struct zwp_locked_pointer_v1_interface locked_pointer_impl; +static const struct zwp_confined_pointer_v1_interface confined_pointer_impl; +static const struct zwp_pointer_constraints_v1_interface pointer_constraints_impl; +static void pointer_constraint_destroy(struct wlr_pointer_constraint_v1 *constraint); + +static struct wlr_pointer_constraint_v1 *pointer_constraint_from_resource( + struct wl_resource *resource) { + assert( + wl_resource_instance_of( + resource, &zwp_confined_pointer_v1_interface, + &confined_pointer_impl) || + wl_resource_instance_of( + resource, &zwp_locked_pointer_v1_interface, + &locked_pointer_impl)); + return wl_resource_get_user_data(resource); +} + +static struct wlr_pointer_constraints_v1 *pointer_constraints_from_resource( + struct wl_resource *resource) { + assert(wl_resource_instance_of(resource, &zwp_pointer_constraints_v1_interface, + &pointer_constraints_impl)); + return wl_resource_get_user_data(resource); +} + +static void resource_destroy(struct wl_client *client, + struct wl_resource *resource) { + wl_resource_destroy(resource); +} + +static void pointer_constraint_destroy(struct wlr_pointer_constraint_v1 *constraint) { + if (constraint == NULL) { + return; + } + + wlr_log(WLR_DEBUG, "destroying constraint %p", constraint); + + wl_resource_set_user_data(constraint->resource, NULL); + wl_list_remove(&constraint->link); + wl_list_remove(&constraint->surface_commit.link); + wl_list_remove(&constraint->surface_destroy.link); + wl_list_remove(&constraint->seat_destroy.link); + + wlr_signal_emit_safe( + &constraint->pointer_constraints->events.constraint_destroy, + constraint); + + if (constraint->current.region) { + pixman_region32_fini(constraint->current.region); + free(constraint->current.region); + } + if (constraint->pending.region && + constraint->current.region != constraint->pending.region) { + pixman_region32_fini(constraint->pending.region); + free(constraint->pending.region); + } + pixman_region32_fini(&constraint->region); + free(constraint); +} + +static void pointer_constraint_destroy_resource(struct wl_resource *resource) { + struct wlr_pointer_constraint_v1 *constraint = + pointer_constraint_from_resource(resource); + + pointer_constraint_destroy(constraint); +} + +static void pointer_constraint_set_region( + struct wlr_pointer_constraint_v1 *constraint, + struct wl_resource *region_resource) { + if (constraint->pending.region && + constraint->current.region != constraint->pending.region) { + pixman_region32_fini(constraint->pending.region); + free(constraint->pending.region); + } + + if (region_resource) { + constraint->pending.region = calloc(1, sizeof(pixman_region32_t)); + pixman_region32_init(constraint->pending.region); + + pixman_region32_t *region = wlr_region_from_resource(region_resource); + pixman_region32_copy(constraint->pending.region, region); + } else { + constraint->pending.region = NULL; + } +} + +static void pointer_constraint_set_region_wrapper(struct wl_client *client, + struct wl_resource *resource, struct wl_resource *region_resource) { + struct wlr_pointer_constraint_v1 *constraint = + pointer_constraint_from_resource(resource); + + pointer_constraint_set_region(constraint, region_resource); +} + +static void pointer_constraint_set_cursor_position_hint(struct wl_client *client, + struct wl_resource *resource, wl_fixed_t x, wl_fixed_t y) { + struct wlr_pointer_constraint_v1 *constraint = + pointer_constraint_from_resource(resource); + + constraint->pending.cursor_hint.x = wl_fixed_to_double(x); + constraint->pending.cursor_hint.y = wl_fixed_to_double(y); + constraint->pending.cursor_hint.valid = true; +} + +static void pointer_constraint_handle_commit( + struct wlr_pointer_constraint_v1 *constraint) { + constraint->current.cursor_hint = constraint->pending.cursor_hint; + + if (constraint->current.region != constraint->pending.region) { + if (constraint->current.region) { + pixman_region32_fini(constraint->current.region); + free(constraint->current.region); + } + + constraint->current.region = constraint->pending.region; + } + + pixman_region32_clear(&constraint->region); + + if (constraint->current.region) { + pixman_region32_intersect(&constraint->region, + &constraint->surface->input_region, constraint->current.region); + } else { + pixman_region32_copy(&constraint->region, + &constraint->surface->input_region); + } +} + +static void handle_surface_commit(struct wl_listener *listener, void *data) { + struct wlr_pointer_constraint_v1 *constraint = + wl_container_of(listener, constraint, surface_commit); + + pointer_constraint_handle_commit(constraint); +} + +static void handle_surface_destroy(struct wl_listener *listener, void *data) { + struct wlr_pointer_constraint_v1 *constraint = + wl_container_of(listener, constraint, surface_destroy); + + pointer_constraint_destroy(constraint); +} + +static void handle_seat_destroy(struct wl_listener *listener, void *data) { + struct wlr_pointer_constraint_v1 *constraint = + wl_container_of(listener, constraint, seat_destroy); + + pointer_constraint_destroy(constraint); +} + +static const struct zwp_confined_pointer_v1_interface confined_pointer_impl = { + .destroy = resource_destroy, + .set_region = pointer_constraint_set_region_wrapper, +}; + +static const struct zwp_locked_pointer_v1_interface locked_pointer_impl = { + .destroy = resource_destroy, + .set_region = pointer_constraint_set_region_wrapper, + .set_cursor_position_hint = pointer_constraint_set_cursor_position_hint, +}; + +static void pointer_constraint_create(struct wl_client *client, + struct wl_resource *pointer_constraints_resource, uint32_t id, + struct wl_resource *surface_resource, struct wl_resource *pointer_resource, + struct wl_resource *region_resource, + enum zwp_pointer_constraints_v1_lifetime lifetime, + enum wlr_pointer_constraint_v1_type type) { + struct wlr_pointer_constraints_v1 *pointer_constraints = + pointer_constraints_from_resource(pointer_constraints_resource); + + struct wlr_surface *surface = wlr_surface_from_resource(surface_resource); + struct wlr_seat *seat = wlr_seat_client_from_pointer_resource(pointer_resource)->seat; + + if (wlr_pointer_constraints_v1_constraint_for_surface(pointer_constraints, + surface, seat)) { + wl_resource_post_error(pointer_constraints_resource, + ZWP_POINTER_CONSTRAINTS_V1_ERROR_ALREADY_CONSTRAINED, + "a pointer constraint with a wl_pointer of the same wl_seat" + " is already on this surface"); + return; + } + + uint32_t version = wl_resource_get_version(pointer_constraints_resource); + + bool locked_pointer = type == WLR_POINTER_CONSTRAINT_V1_LOCKED; + + struct wl_resource *resource = locked_pointer ? + wl_resource_create(client, &zwp_locked_pointer_v1_interface, version, id) : + wl_resource_create(client, &zwp_confined_pointer_v1_interface, version, id); + + if (resource == NULL) { + wl_client_post_no_memory(client); + return; + } + + struct wlr_pointer_constraint_v1 *constraint = calloc(1, sizeof(*constraint)); + if (constraint == NULL) { + wl_client_post_no_memory(client); + return; + } + + constraint->resource = resource; + constraint->surface = surface; + constraint->seat = seat; + constraint->lifetime = lifetime; + constraint->type = type; + constraint->pointer_constraints = pointer_constraints; + + pixman_region32_init(&constraint->region); + + pointer_constraint_set_region(constraint, region_resource); + pointer_constraint_handle_commit(constraint); + + constraint->surface_commit.notify = handle_surface_commit; + wl_signal_add(&surface->events.commit, &constraint->surface_commit); + + constraint->surface_destroy.notify = handle_surface_destroy; + wl_signal_add(&surface->events.destroy, &constraint->surface_destroy); + + constraint->seat_destroy.notify = handle_seat_destroy; + wl_signal_add(&seat->events.destroy, &constraint->seat_destroy); + + wl_resource_set_implementation(constraint->resource, + locked_pointer ? (void*)&locked_pointer_impl : (void*)&confined_pointer_impl, + constraint, pointer_constraint_destroy_resource); + + wlr_log(WLR_DEBUG, "new %s_pointer %p (res %p)", + locked_pointer ? "locked" : "confined", constraint, constraint->resource); + + wl_list_insert(&pointer_constraints->constraints, &constraint->link); + + wlr_signal_emit_safe(&pointer_constraints->events.constraint_create, + constraint); +} + +static void pointer_constraints_lock_pointer(struct wl_client *client, + struct wl_resource *cons_resource, uint32_t id, + struct wl_resource *surface, struct wl_resource *pointer, + struct wl_resource *region, enum zwp_pointer_constraints_v1_lifetime lifetime) { + pointer_constraint_create(client, cons_resource, id, surface, pointer, + region, lifetime, WLR_POINTER_CONSTRAINT_V1_LOCKED); +} + +static void pointer_constraints_confine_pointer(struct wl_client *client, + struct wl_resource *cons_resource, uint32_t id, + struct wl_resource *surface, struct wl_resource *pointer, + struct wl_resource *region, enum zwp_pointer_constraints_v1_lifetime lifetime) { + pointer_constraint_create(client, cons_resource, id, surface, pointer, + region, lifetime, WLR_POINTER_CONSTRAINT_V1_CONFINED); +} + +static const struct zwp_pointer_constraints_v1_interface pointer_constraints_impl = { + .destroy = resource_destroy, + .lock_pointer = pointer_constraints_lock_pointer, + .confine_pointer = pointer_constraints_confine_pointer, +}; + +static void pointer_constraints_destroy(struct wl_resource *resource) { + wl_list_remove(wl_resource_get_link(resource)); +} + +static void pointer_constraints_bind(struct wl_client *client, void *data, + uint32_t version, uint32_t id) { + struct wlr_pointer_constraints_v1 *pointer_constraints = data; + assert(client && pointer_constraints); + + struct wl_resource *resource = wl_resource_create(client, + &zwp_pointer_constraints_v1_interface, version, id); + if (resource == NULL) { + wl_client_post_no_memory(client); + return; + } + + wl_list_insert(&pointer_constraints->wl_resources, wl_resource_get_link(resource)); + wl_resource_set_implementation(resource, &pointer_constraints_impl, + pointer_constraints, pointer_constraints_destroy); +} + +struct wlr_pointer_constraints_v1 *wlr_pointer_constraints_v1_create( + struct wl_display *display) { + struct wlr_pointer_constraints_v1 *pointer_constraints = + calloc(1, sizeof(*pointer_constraints)); + + if (!pointer_constraints) { + return NULL; + } + + struct wl_global *wl_global = wl_global_create(display, + &zwp_pointer_constraints_v1_interface, 1, pointer_constraints, + pointer_constraints_bind); + if (!wl_global) { + free(pointer_constraints); + return NULL; + } + pointer_constraints->wl_global = wl_global; + + wl_list_init(&pointer_constraints->wl_resources); + wl_list_init(&pointer_constraints->constraints); + wl_signal_init(&pointer_constraints->events.constraint_create); + wl_signal_init(&pointer_constraints->events.constraint_destroy); + + return pointer_constraints; +} + +void wlr_pointer_constraints_v1_destroy( + struct wlr_pointer_constraints_v1 *pointer_constraints) { + struct wl_resource *resource, *_tmp_res; + wl_resource_for_each_safe(resource, _tmp_res, &pointer_constraints->wl_resources) { + wl_resource_destroy(resource); + } + + struct wlr_pointer_constraint_v1 *constraint, *_tmp_cons; + wl_list_for_each_safe(constraint, _tmp_cons, &pointer_constraints->constraints, link) { + wl_resource_destroy(constraint->resource); + } + + wl_global_destroy(pointer_constraints->wl_global); + free(pointer_constraints); +} + +struct wlr_pointer_constraint_v1 *wlr_pointer_constraints_v1_constraint_for_surface( + struct wlr_pointer_constraints_v1 *pointer_constraints, + struct wlr_surface *surface, struct wlr_seat *seat) { + struct wlr_pointer_constraint_v1 *constraint; + wl_list_for_each(constraint, &pointer_constraints->constraints, link) { + if (constraint->surface == surface && constraint->seat == seat) { + return constraint; + } + } + + return NULL; +} + +// Thankfully zwp_confined_pointer_v1_send_{un,}confined work +// on both locked and confined pointer constraints. +void wlr_pointer_constraint_v1_send_activated( + struct wlr_pointer_constraint_v1 *constraint) { + wlr_log(WLR_DEBUG, "constrained %p", constraint); + zwp_confined_pointer_v1_send_confined(constraint->resource); +} + +void wlr_pointer_constraint_v1_send_deactivated(struct wlr_pointer_constraint_v1 *constraint) { + if (wl_resource_get_user_data(constraint->resource)) { + wlr_log(WLR_DEBUG, "unconstrained %p", constraint); + zwp_confined_pointer_v1_send_unconfined(constraint->resource); + if (constraint->lifetime == ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_ONESHOT) { + pointer_constraint_destroy(constraint); + } + } +} diff --git a/util/region.c b/util/region.c index 38f84c5e..76df71f3 100644 --- a/util/region.c +++ b/util/region.c @@ -1,5 +1,8 @@ +#include #include +#include #include +#include #include void wlr_region_scale(pixman_region32_t *dst, pixman_region32_t *src, @@ -177,3 +180,71 @@ void wlr_region_rotated_bounds(pixman_region32_t *dst, pixman_region32_t *src, pixman_region32_init_rects(dst, dst_rects, nrects); free(dst_rects); } + +static void region_confine(pixman_region32_t *region, double x1, double y1, double x2, + double y2, double *x2_out, double *y2_out, pixman_box32_t box) { + double x_clamped = fmax(fmin(x2, box.x2 - 1), box.x1); + double y_clamped = fmax(fmin(y2, box.y2 - 1), box.y1); + + // If the target coordinates are above box.{x,y}2 - 1, but less than + // box.{x,y}2, then they are still within the box. + if (floor(x_clamped) == floor(x2) && floor(y_clamped) == floor(y2)) { + *x2_out = x2; + *y2_out = y2; + return; + } + + double dx = x2 - x1; + double dy = y2 - y1; + + // We use fabs to avoid negative zeroes and thus avoid a bug + // with negative infinity. + double delta = fmin(fabs(x_clamped - x1) / fabs(dx), fabs(y_clamped - y1) / fabs(dy)); + + // We clamp it again due to precision errors. + double x = fmax(fmin(delta * dx + x1, box.x2 - 1), box.x1); + double y = fmax(fmin(delta * dy + y1, box.y2 - 1), box.y1); + + // Go one unit past the boundary to find an adjacent box. + int x_ext = floor(x) + (dx == 0 ? 0 : dx > 0 ? 1 : -1); + int y_ext = floor(y) + (dy == 0 ? 0 : dy > 0 ? 1 : -1); + + if (pixman_region32_contains_point(region, x_ext, y_ext, &box)) { + return region_confine(region, x1, y1, x2, y2, x2_out, y2_out, box); + } else if (dx == 0 || dy == 0) { + *x2_out = x; + *y2_out = y; + } else { + bool bordering_x = x == box.x1 || x == box.x2 - 1; + bool bordering_y = y == box.y1 || y == box.y2 - 1; + + if ((bordering_x && bordering_y) || (!bordering_x && !bordering_y)) { + double x2_potential, y2_potential; + double tmp1, tmp2; + region_confine(region, x, y, x, y2, &tmp1, &y2_potential, box); + region_confine(region, x, y, x2, y, &x2_potential, &tmp2, box); + if (fabs(x2_potential - x) > fabs(y2_potential - y)) { + *x2_out = x2_potential; + *y2_out = y; + } else { + *x2_out = x; + *y2_out = y2_potential; + } + } else if (bordering_x) { + return region_confine(region, x, y, x, y2, x2_out, y2_out, box); + } else if (bordering_y) { + return region_confine(region, x, y, x2, y, x2_out, y2_out, box); + } + } +} + +bool wlr_region_confine(pixman_region32_t *region, double x1, double y1, double x2, + double y2, double *x2_out, double *y2_out) { + pixman_box32_t box; + if (pixman_region32_contains_point(region, x1, y1, &box)) { + region_confine(region, x1, y1, x2, y2, x2_out, y2_out, box); + return true; + } else { + return false; + } +} From afa2e399aa9c19c250a03cce1cb466f54348a97a Mon Sep 17 00:00:00 2001 From: Las Date: Tue, 18 Sep 2018 13:05:44 +0200 Subject: [PATCH 09/17] Fix implicit conversion of floats to ints in calls to pixman_region32_contains_point I do not think the conversion is specifically defined, but on my system and SirCmpwn's the floats are rounded instead of floored, which is incorrect in this case, since for a range from 0 to 256, any value greater or equal to 0 and less than 256 is valid. I.e. [0;256[, or 0 <= x < 256, but if x is e.g. -0.1, then it will be rounded to 0, which is invalid. The correct behavior would be to floor to -1. --- rootston/cursor.c | 6 +++--- types/wlr_surface.c | 2 +- util/region.c | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/rootston/cursor.c b/rootston/cursor.c index 0aaff132..4ec600e1 100644 --- a/rootston/cursor.c +++ b/rootston/cursor.c @@ -381,7 +381,7 @@ void roots_cursor_handle_motion_absolute(struct roots_cursor *cursor, if (cursor->active_constraint && !pixman_region32_contains_point(&cursor->confine, - lx - view->x, ly - view->y, NULL)) { + floor(lx - view->x), floor(ly - view->y), NULL)) { return; } } @@ -501,7 +501,7 @@ void roots_cursor_handle_tool_axis(struct roots_cursor *cursor, if (cursor->active_constraint && !pixman_region32_contains_point(&cursor->confine, - lx - view->x, ly - view->y, NULL)) { + floor(lx - view->x), floor(ly - view->y), NULL)) { return; } } @@ -602,7 +602,7 @@ void roots_cursor_constrain(struct roots_cursor *cursor, pixman_region32_t *region = &constraint->region; - if (!pixman_region32_contains_point(region, sx, sy, NULL)) { + if (!pixman_region32_contains_point(region, floor(sx), floor(sy), NULL)) { // Warp into region if possible int nboxes; pixman_box32_t *boxes = pixman_region32_rectangles(region, &nboxes); diff --git a/types/wlr_surface.c b/types/wlr_surface.c index 773de460..408f38d1 100644 --- a/types/wlr_surface.c +++ b/types/wlr_surface.c @@ -943,7 +943,7 @@ bool wlr_surface_point_accepts_input(struct wlr_surface *surface, double sx, double sy) { return sx >= 0 && sx < surface->current.width && sy >= 0 && sy < surface->current.height && - pixman_region32_contains_point(&surface->current.input, sx, sy, NULL); + pixman_region32_contains_point(&surface->current.input, floor(sx), floor(sy), NULL); } struct wlr_surface *wlr_surface_surface_at(struct wlr_surface *surface, diff --git a/util/region.c b/util/region.c index 76df71f3..61f9c7c7 100644 --- a/util/region.c +++ b/util/region.c @@ -241,7 +241,7 @@ static void region_confine(pixman_region32_t *region, double x1, double y1, doub bool wlr_region_confine(pixman_region32_t *region, double x1, double y1, double x2, double y2, double *x2_out, double *y2_out) { pixman_box32_t box; - if (pixman_region32_contains_point(region, x1, y1, &box)) { + if (pixman_region32_contains_point(region, floor(x1), floor(y1), &box)) { region_confine(region, x1, y1, x2, y2, x2_out, y2_out, box); return true; } else { From e3736d4acc38e3c174cffe54d1e123bdb8d4d99b Mon Sep 17 00:00:00 2001 From: emersion Date: Wed, 26 Sep 2018 17:34:09 +0200 Subject: [PATCH 10/17] examples/pointer-constraints: style fixes --- examples/pointer-constraints.c | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/examples/pointer-constraints.c b/examples/pointer-constraints.c index 8738d475..1df9f6ce 100644 --- a/examples/pointer-constraints.c +++ b/examples/pointer-constraints.c @@ -43,17 +43,19 @@ static void draw(void) { eglSwapBuffers(egl.display, egl_surface); } -static void pointer_handle_button(void *data, struct wl_pointer *pointer, uint32_t serial, - uint32_t time, uint32_t button, uint32_t state_w) { +static void pointer_handle_button(void *data, struct wl_pointer *pointer, + uint32_t serial, uint32_t time, uint32_t button, uint32_t state_w) { struct wl_surface *surface = data; if (button == BTN_LEFT && state_w == WL_POINTER_BUTTON_STATE_PRESSED) { region_type = (region_type + 1) % REGION_TYPE_MAX; if (locked_pointer) { - zwp_locked_pointer_v1_set_region(locked_pointer, regions[region_type]); + zwp_locked_pointer_v1_set_region(locked_pointer, + regions[region_type]); } else if (confined_pointer) { - zwp_confined_pointer_v1_set_region(confined_pointer, regions[region_type]); + zwp_confined_pointer_v1_set_region(confined_pointer, + regions[region_type]); } wl_surface_commit(surface); @@ -149,9 +151,10 @@ static void handle_global(void *data, struct wl_registry *registry, wm_base = wl_registry_bind(registry, name, &xdg_wm_base_interface, 1); } else if (strcmp(interface, wl_seat_interface.name) == 0) { seat = wl_registry_bind(registry, name, &wl_seat_interface, version); - } else if (strcmp(interface, zwp_pointer_constraints_v1_interface.name) == 0) { + } else if (strcmp(interface, + zwp_pointer_constraints_v1_interface.name) == 0) { pointer_constraints = wl_registry_bind(registry, name, - &zwp_pointer_constraints_v1_interface, version); + &zwp_pointer_constraints_v1_interface, version); } } @@ -225,12 +228,15 @@ int main(int argc, char **argv) { if (lock) { locked_pointer = zwp_pointer_constraints_v1_lock_pointer( - pointer_constraints, surface, pointer, regions[region_type], lifetime); + pointer_constraints, surface, pointer, + regions[region_type], lifetime); - zwp_locked_pointer_v1_set_cursor_position_hint(locked_pointer, wl_fixed_from_int(128), wl_fixed_from_int(128)); + zwp_locked_pointer_v1_set_cursor_position_hint(locked_pointer, + wl_fixed_from_int(128), wl_fixed_from_int(128)); } else { confined_pointer = zwp_pointer_constraints_v1_confine_pointer( - pointer_constraints, surface, pointer, regions[region_type], lifetime); + pointer_constraints, surface, pointer, + regions[region_type], lifetime); } wl_surface_commit(surface); @@ -246,9 +252,9 @@ int main(int argc, char **argv) { return EXIT_SUCCESS; - invalid_args: { - fprintf(stderr, "pointer-constraints " - "\n"); - exit(EXIT_FAILURE); - } +invalid_args: + fprintf(stderr, "pointer-constraints " + " " + "\n"); + return EXIT_FAILURE; } From c89cd4945be9a719ef8d0ff3fbd1736b573f92bc Mon Sep 17 00:00:00 2001 From: emersion Date: Wed, 26 Sep 2018 17:38:41 +0200 Subject: [PATCH 11/17] pointer-constraints: rename wl_{global,resources} --- include/wlr/types/wlr_pointer_constraints_v1.h | 4 ++-- types/wlr_pointer_constraints_v1.c | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/include/wlr/types/wlr_pointer_constraints_v1.h b/include/wlr/types/wlr_pointer_constraints_v1.h index b8015a57..8a73efcc 100644 --- a/include/wlr/types/wlr_pointer_constraints_v1.h +++ b/include/wlr/types/wlr_pointer_constraints_v1.h @@ -47,8 +47,8 @@ struct wlr_pointer_constraint_v1 { }; struct wlr_pointer_constraints_v1 { - struct wl_list wl_resources; // wl_resource_get_link - struct wl_global *wl_global; + struct wl_list resources; // wl_resource_get_link + struct wl_global *global; struct { /** diff --git a/types/wlr_pointer_constraints_v1.c b/types/wlr_pointer_constraints_v1.c index 2d2750b5..dbcbe3ee 100644 --- a/types/wlr_pointer_constraints_v1.c +++ b/types/wlr_pointer_constraints_v1.c @@ -283,7 +283,7 @@ static void pointer_constraints_bind(struct wl_client *client, void *data, return; } - wl_list_insert(&pointer_constraints->wl_resources, wl_resource_get_link(resource)); + wl_list_insert(&pointer_constraints->resources, wl_resource_get_link(resource)); wl_resource_set_implementation(resource, &pointer_constraints_impl, pointer_constraints, pointer_constraints_destroy); } @@ -304,9 +304,9 @@ struct wlr_pointer_constraints_v1 *wlr_pointer_constraints_v1_create( free(pointer_constraints); return NULL; } - pointer_constraints->wl_global = wl_global; + pointer_constraints->global = wl_global; - wl_list_init(&pointer_constraints->wl_resources); + wl_list_init(&pointer_constraints->resources); wl_list_init(&pointer_constraints->constraints); wl_signal_init(&pointer_constraints->events.constraint_create); wl_signal_init(&pointer_constraints->events.constraint_destroy); @@ -317,7 +317,7 @@ struct wlr_pointer_constraints_v1 *wlr_pointer_constraints_v1_create( void wlr_pointer_constraints_v1_destroy( struct wlr_pointer_constraints_v1 *pointer_constraints) { struct wl_resource *resource, *_tmp_res; - wl_resource_for_each_safe(resource, _tmp_res, &pointer_constraints->wl_resources) { + wl_resource_for_each_safe(resource, _tmp_res, &pointer_constraints->resources) { wl_resource_destroy(resource); } @@ -326,7 +326,7 @@ void wlr_pointer_constraints_v1_destroy( wl_resource_destroy(constraint->resource); } - wl_global_destroy(pointer_constraints->wl_global); + wl_global_destroy(pointer_constraints->global); free(pointer_constraints); } From 31cc2fa4f9eae8f6c884b35c500aef4225b3f9ef Mon Sep 17 00:00:00 2001 From: emersion Date: Wed, 26 Sep 2018 23:08:52 +0200 Subject: [PATCH 12/17] pointer-constraints: make region not a pointer, add committed bitfield --- .../wlr/types/wlr_pointer_constraints_v1.h | 9 +- rootston/desktop.c | 4 +- types/wlr_pointer_constraints_v1.c | 92 +++++++++---------- 3 files changed, 56 insertions(+), 49 deletions(-) diff --git a/include/wlr/types/wlr_pointer_constraints_v1.h b/include/wlr/types/wlr_pointer_constraints_v1.h index 8a73efcc..6fdae9c4 100644 --- a/include/wlr/types/wlr_pointer_constraints_v1.h +++ b/include/wlr/types/wlr_pointer_constraints_v1.h @@ -15,13 +15,18 @@ enum wlr_pointer_constraint_v1_type { WLR_POINTER_CONSTRAINT_V1_CONFINED, }; +enum wlr_pointer_constraint_v1_state_field { + WLR_POINTER_CONSTRAINT_V1_STATE_REGION = 1 << 0, + WLR_POINTER_CONSTRAINT_V1_STATE_CURSOR_HINT = 1 << 1, +}; + struct wlr_pointer_constraint_v1_state { - pixman_region32_t *region; + uint32_t committed; // enum wlr_pointer_constraint_v1_state_field + pixman_region32_t region; // only valid for locked_pointer struct { double x, y; - bool valid; } cursor_hint; }; diff --git a/rootston/desktop.c b/rootston/desktop.c index d469c263..a1e61738 100644 --- a/rootston/desktop.c +++ b/rootston/desktop.c @@ -798,7 +798,9 @@ static void handle_constraint_destroy( struct roots_seat* seat = constraint->seat->data; if (seat->cursor->active_constraint == constraint) { roots_cursor_constrain(seat->cursor, NULL, NAN, NAN); - if (constraint->current.cursor_hint.valid && seat->cursor->pointer_view) { + if (constraint->current.committed & + WLR_POINTER_CONSTRAINT_V1_STATE_CURSOR_HINT && + seat->cursor->pointer_view) { double sx = constraint->current.cursor_hint.x; double sy = constraint->current.cursor_hint.y; diff --git a/types/wlr_pointer_constraints_v1.c b/types/wlr_pointer_constraints_v1.c index dbcbe3ee..e281277f 100644 --- a/types/wlr_pointer_constraints_v1.c +++ b/types/wlr_pointer_constraints_v1.c @@ -57,15 +57,8 @@ static void pointer_constraint_destroy(struct wlr_pointer_constraint_v1 *constra &constraint->pointer_constraints->events.constraint_destroy, constraint); - if (constraint->current.region) { - pixman_region32_fini(constraint->current.region); - free(constraint->current.region); - } - if (constraint->pending.region && - constraint->current.region != constraint->pending.region) { - pixman_region32_fini(constraint->pending.region); - free(constraint->pending.region); - } + pixman_region32_fini(&constraint->current.region); + pixman_region32_fini(&constraint->pending.region); pixman_region32_fini(&constraint->region); free(constraint); } @@ -80,24 +73,17 @@ static void pointer_constraint_destroy_resource(struct wl_resource *resource) { static void pointer_constraint_set_region( struct wlr_pointer_constraint_v1 *constraint, struct wl_resource *region_resource) { - if (constraint->pending.region && - constraint->current.region != constraint->pending.region) { - pixman_region32_fini(constraint->pending.region); - free(constraint->pending.region); - } + pixman_region32_clear(&constraint->pending.region); if (region_resource) { - constraint->pending.region = calloc(1, sizeof(pixman_region32_t)); - pixman_region32_init(constraint->pending.region); - pixman_region32_t *region = wlr_region_from_resource(region_resource); - pixman_region32_copy(constraint->pending.region, region); - } else { - constraint->pending.region = NULL; + pixman_region32_copy(&constraint->pending.region, region); } + + constraint->pending.committed |= WLR_POINTER_CONSTRAINT_V1_STATE_REGION; } -static void pointer_constraint_set_region_wrapper(struct wl_client *client, +static void pointer_constraint_handle_set_region(struct wl_client *client, struct wl_resource *resource, struct wl_resource *region_resource) { struct wlr_pointer_constraint_v1 *constraint = pointer_constraint_from_resource(resource); @@ -112,27 +98,28 @@ static void pointer_constraint_set_cursor_position_hint(struct wl_client *client constraint->pending.cursor_hint.x = wl_fixed_to_double(x); constraint->pending.cursor_hint.y = wl_fixed_to_double(y); - constraint->pending.cursor_hint.valid = true; + constraint->pending.committed |= WLR_POINTER_CONSTRAINT_V1_STATE_CURSOR_HINT; } static void pointer_constraint_handle_commit( struct wlr_pointer_constraint_v1 *constraint) { - constraint->current.cursor_hint = constraint->pending.cursor_hint; - - if (constraint->current.region != constraint->pending.region) { - if (constraint->current.region) { - pixman_region32_fini(constraint->current.region); - free(constraint->current.region); - } - - constraint->current.region = constraint->pending.region; + if (constraint->pending.committed & + WLR_POINTER_CONSTRAINT_V1_STATE_REGION) { + pixman_region32_copy(&constraint->current.region, + &constraint->pending.region); } + if (constraint->pending.committed & + WLR_POINTER_CONSTRAINT_V1_STATE_CURSOR_HINT) { + constraint->current.cursor_hint = constraint->pending.cursor_hint; + } + constraint->current.committed |= constraint->pending.committed; + + constraint->pending.committed = 0; pixman_region32_clear(&constraint->region); - - if (constraint->current.region) { + if (pixman_region32_not_empty(&constraint->current.region)) { pixman_region32_intersect(&constraint->region, - &constraint->surface->input_region, constraint->current.region); + &constraint->surface->input_region, &constraint->current.region); } else { pixman_region32_copy(&constraint->region, &constraint->surface->input_region); @@ -162,18 +149,19 @@ static void handle_seat_destroy(struct wl_listener *listener, void *data) { static const struct zwp_confined_pointer_v1_interface confined_pointer_impl = { .destroy = resource_destroy, - .set_region = pointer_constraint_set_region_wrapper, + .set_region = pointer_constraint_handle_set_region, }; static const struct zwp_locked_pointer_v1_interface locked_pointer_impl = { .destroy = resource_destroy, - .set_region = pointer_constraint_set_region_wrapper, + .set_region = pointer_constraint_handle_set_region, .set_cursor_position_hint = pointer_constraint_set_cursor_position_hint, }; static void pointer_constraint_create(struct wl_client *client, struct wl_resource *pointer_constraints_resource, uint32_t id, - struct wl_resource *surface_resource, struct wl_resource *pointer_resource, + struct wl_resource *surface_resource, + struct wl_resource *pointer_resource, struct wl_resource *region_resource, enum zwp_pointer_constraints_v1_lifetime lifetime, enum wlr_pointer_constraint_v1_type type) { @@ -181,7 +169,8 @@ static void pointer_constraint_create(struct wl_client *client, pointer_constraints_from_resource(pointer_constraints_resource); struct wlr_surface *surface = wlr_surface_from_resource(surface_resource); - struct wlr_seat *seat = wlr_seat_client_from_pointer_resource(pointer_resource)->seat; + struct wlr_seat *seat = + wlr_seat_client_from_pointer_resource(pointer_resource)->seat; if (wlr_pointer_constraints_v1_constraint_for_surface(pointer_constraints, surface, seat)) { @@ -220,6 +209,9 @@ static void pointer_constraint_create(struct wl_client *client, pixman_region32_init(&constraint->region); + pixman_region32_init(&constraint->pending.region); + pixman_region32_init(&constraint->current.region); + pointer_constraint_set_region(constraint, region_resource); pointer_constraint_handle_commit(constraint); @@ -256,12 +248,14 @@ static void pointer_constraints_lock_pointer(struct wl_client *client, static void pointer_constraints_confine_pointer(struct wl_client *client, struct wl_resource *cons_resource, uint32_t id, struct wl_resource *surface, struct wl_resource *pointer, - struct wl_resource *region, enum zwp_pointer_constraints_v1_lifetime lifetime) { + struct wl_resource *region, + enum zwp_pointer_constraints_v1_lifetime lifetime) { pointer_constraint_create(client, cons_resource, id, surface, pointer, region, lifetime, WLR_POINTER_CONSTRAINT_V1_CONFINED); } -static const struct zwp_pointer_constraints_v1_interface pointer_constraints_impl = { +static const struct zwp_pointer_constraints_v1_interface + pointer_constraints_impl = { .destroy = resource_destroy, .lock_pointer = pointer_constraints_lock_pointer, .confine_pointer = pointer_constraints_confine_pointer, @@ -283,7 +277,8 @@ static void pointer_constraints_bind(struct wl_client *client, void *data, return; } - wl_list_insert(&pointer_constraints->resources, wl_resource_get_link(resource)); + wl_list_insert(&pointer_constraints->resources, + wl_resource_get_link(resource)); wl_resource_set_implementation(resource, &pointer_constraints_impl, pointer_constraints, pointer_constraints_destroy); } @@ -317,12 +312,14 @@ struct wlr_pointer_constraints_v1 *wlr_pointer_constraints_v1_create( void wlr_pointer_constraints_v1_destroy( struct wlr_pointer_constraints_v1 *pointer_constraints) { struct wl_resource *resource, *_tmp_res; - wl_resource_for_each_safe(resource, _tmp_res, &pointer_constraints->resources) { + wl_resource_for_each_safe(resource, _tmp_res, + &pointer_constraints->resources) { wl_resource_destroy(resource); } struct wlr_pointer_constraint_v1 *constraint, *_tmp_cons; - wl_list_for_each_safe(constraint, _tmp_cons, &pointer_constraints->constraints, link) { + wl_list_for_each_safe(constraint, _tmp_cons, + &pointer_constraints->constraints, link) { wl_resource_destroy(constraint->resource); } @@ -330,7 +327,8 @@ void wlr_pointer_constraints_v1_destroy( free(pointer_constraints); } -struct wlr_pointer_constraint_v1 *wlr_pointer_constraints_v1_constraint_for_surface( +struct wlr_pointer_constraint_v1 * + wlr_pointer_constraints_v1_constraint_for_surface( struct wlr_pointer_constraints_v1 *pointer_constraints, struct wlr_surface *surface, struct wlr_seat *seat) { struct wlr_pointer_constraint_v1 *constraint; @@ -351,11 +349,13 @@ void wlr_pointer_constraint_v1_send_activated( zwp_confined_pointer_v1_send_confined(constraint->resource); } -void wlr_pointer_constraint_v1_send_deactivated(struct wlr_pointer_constraint_v1 *constraint) { +void wlr_pointer_constraint_v1_send_deactivated( + struct wlr_pointer_constraint_v1 *constraint) { if (wl_resource_get_user_data(constraint->resource)) { wlr_log(WLR_DEBUG, "unconstrained %p", constraint); zwp_confined_pointer_v1_send_unconfined(constraint->resource); - if (constraint->lifetime == ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_ONESHOT) { + if (constraint->lifetime == + ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_ONESHOT) { pointer_constraint_destroy(constraint); } } From dac4f8e19ffd19dfeee2c65270476a52c13d0f8f Mon Sep 17 00:00:00 2001 From: emersion Date: Thu, 27 Sep 2018 10:25:59 +0200 Subject: [PATCH 13/17] pointer-constraints: refactoring * Rename the constraint_create signal to new_constraint for consistency * Move the constraint_destroy signal to the constraint itself * Use rotate_child_position instead of duplicating logic * Fix inert constraint resource handling * Style fixes --- include/rootston/desktop.h | 3 +- include/rootston/output.h | 3 + include/rootston/seat.h | 6 ++ .../wlr/types/wlr_pointer_constraints_v1.h | 22 ++--- rootston/cursor.c | 72 ++++++++------- rootston/desktop.c | 91 ++++++++++--------- rootston/output.c | 2 +- rootston/seat.c | 9 -- types/wlr_pointer_constraints_v1.c | 41 +++++---- 9 files changed, 130 insertions(+), 119 deletions(-) diff --git a/include/rootston/desktop.h b/include/rootston/desktop.h index dac694d1..90851a17 100644 --- a/include/rootston/desktop.h +++ b/include/rootston/desktop.h @@ -68,8 +68,7 @@ struct roots_desktop { struct wl_listener input_inhibit_activate; struct wl_listener input_inhibit_deactivate; struct wl_listener virtual_keyboard_new; - struct wl_listener constraint_create; - struct wl_listener constraint_destroy; + struct wl_listener pointer_constraint; #ifdef WLR_HAS_XWAYLAND struct wlr_xwayland *xwayland; diff --git a/include/rootston/output.h b/include/rootston/output.h index f78ee81d..69bc5126 100644 --- a/include/rootston/output.h +++ b/include/rootston/output.h @@ -28,6 +28,9 @@ struct roots_output { struct wl_listener damage_destroy; }; +void rotate_child_position(double *sx, double *sy, double sw, double sh, + double pw, double ph, float rotation); + void handle_new_output(struct wl_listener *listener, void *data); struct roots_view; diff --git a/include/rootston/seat.h b/include/rootston/seat.h index a3192d26..1248918a 100644 --- a/include/rootston/seat.h +++ b/include/rootston/seat.h @@ -122,6 +122,12 @@ struct roots_tablet_tool { struct wl_listener tablet_destroy; }; +struct roots_pointer_constraint { + struct wlr_pointer_constraint_v1 *constraint; + + struct wl_listener destroy; +}; + struct roots_seat *roots_seat_create(struct roots_input *input, char *name); void roots_seat_destroy(struct roots_seat *seat); diff --git a/include/wlr/types/wlr_pointer_constraints_v1.h b/include/wlr/types/wlr_pointer_constraints_v1.h index 6fdae9c4..97edf0ea 100644 --- a/include/wlr/types/wlr_pointer_constraints_v1.h +++ b/include/wlr/types/wlr_pointer_constraints_v1.h @@ -48,6 +48,10 @@ struct wlr_pointer_constraint_v1 { struct wl_list link; // wlr_pointer_constraints_v1::constraints + struct { + struct wl_signal destroy; + } events; + void *data; }; @@ -59,15 +63,9 @@ struct wlr_pointer_constraints_v1 { /** * Called when a new pointer constraint is created. * - * data: wlr_pointer_constraint_v1* + * data: struct wlr_pointer_constraint_v1 * */ - struct wl_signal constraint_create; - /** - * Called when a pointer constraint is destroyed. - * - * data: wlr_pointer_constraint_v1* - */ - struct wl_signal constraint_destroy; + struct wl_signal new_constraint; } events; struct wl_list constraints; // wlr_pointer_constraint_v1::link @@ -78,10 +76,11 @@ struct wlr_pointer_constraints_v1 { struct wlr_pointer_constraints_v1 *wlr_pointer_constraints_v1_create( struct wl_display *display); void wlr_pointer_constraints_v1_destroy( - struct wlr_pointer_constraints_v1 *wlr_pointer_constraints_v1); + struct wlr_pointer_constraints_v1 *pointer_constraints); -struct wlr_pointer_constraint_v1 *wlr_pointer_constraints_v1_constraint_for_surface( - struct wlr_pointer_constraints_v1 *wlr_pointer_constraints_v1, +struct wlr_pointer_constraint_v1 * + wlr_pointer_constraints_v1_constraint_for_surface( + struct wlr_pointer_constraints_v1 *pointer_constraints, struct wlr_surface *surface, struct wlr_seat *seat); void wlr_pointer_constraint_v1_send_activated( @@ -89,5 +88,4 @@ void wlr_pointer_constraint_v1_send_activated( void wlr_pointer_constraint_v1_send_deactivated( struct wlr_pointer_constraint_v1 *constraint); - #endif diff --git a/rootston/cursor.c b/rootston/cursor.c index 4ec600e1..08a71ec4 100644 --- a/rootston/cursor.c +++ b/rootston/cursor.c @@ -572,32 +572,45 @@ void roots_cursor_handle_constraint_commit(struct roots_cursor *cursor) { } } +static void handle_constraint_commit(struct wl_listener *listener, + void *data) { + struct roots_cursor *cursor = + wl_container_of(listener, cursor, constraint_commit); + assert(cursor->active_constraint->surface == data); + roots_cursor_handle_constraint_commit(cursor); +} + void roots_cursor_constrain(struct roots_cursor *cursor, struct wlr_pointer_constraint_v1 *constraint, double sx, double sy) { - if (cursor->active_constraint != constraint) { - wlr_log(WLR_DEBUG, "roots_cursor_constrain(%p, %p)", cursor, constraint); - wlr_log(WLR_DEBUG, "cursor->active_constraint: %p", cursor->active_constraint); - - if (cursor->active_constraint) { - wlr_pointer_constraint_v1_send_deactivated(cursor->active_constraint); - if (cursor->constraint_commit.link.next) { - wl_list_remove(&cursor->constraint_commit.link); - } - } - - cursor->active_constraint = constraint; - - if (!constraint) { - return; - } - - wlr_pointer_constraint_v1_send_activated(constraint); - wl_signal_add(&constraint->surface->events.commit, - &cursor->constraint_commit); - } else if (constraint == NULL) { + if (cursor->active_constraint == constraint) { return; } + wlr_log(WLR_DEBUG, "roots_cursor_constrain(%p, %p)", + cursor, constraint); + wlr_log(WLR_DEBUG, "cursor->active_constraint: %p", + cursor->active_constraint); + + wl_list_remove(&cursor->constraint_commit.link); + wl_list_init(&cursor->constraint_commit.link); + if (cursor->active_constraint) { + wlr_pointer_constraint_v1_send_deactivated( + cursor->active_constraint); + } + + cursor->active_constraint = constraint; + + if (constraint == NULL) { + return; + } + + wlr_pointer_constraint_v1_send_activated(constraint); + + wl_list_remove(&cursor->constraint_commit.link); + wl_signal_add(&constraint->surface->events.commit, + &cursor->constraint_commit); + cursor->constraint_commit.notify = handle_constraint_commit; + pixman_region32_clear(&cursor->confine); pixman_region32_t *region = &constraint->region; @@ -612,20 +625,11 @@ void roots_cursor_constrain(struct roots_cursor *cursor, double sx = (boxes[0].x1 + boxes[0].x2) / 2.; double sy = (boxes[0].y1 + boxes[0].y2) / 2.; - double lx, ly; - if (view->rotation == 0.0) { - lx = sx + view->x; - ly = sy + view->y; - } else { - double c = cos(view->rotation); - double s = sin(view->rotation); + rotate_child_position(&sx, &sy, 0, 0, view->width, view->height, + view->rotation); - double center_x = view->width / 2.; - double center_y = view->height / 2.; - - lx = c * (sx - center_x) - s * (sy - center_y) + center_x + view->x; - ly = s * (sx - center_x) + c * (sy - center_y) + center_y + view->y; - } + double lx = view->x + sx; + double ly = view->y + sy; wlr_cursor_warp_closest(cursor->cursor, NULL, lx, ly); } diff --git a/rootston/desktop.c b/rootston/desktop.c index a1e61738..c180c839 100644 --- a/rootston/desktop.c +++ b/rootston/desktop.c @@ -777,53 +777,60 @@ static void input_inhibit_deactivate(struct wl_listener *listener, void *data) { } } -static void handle_constraint_create( - struct wl_listener *listener, - struct wlr_pointer_constraint_v1 *constraint) { - struct roots_seat* seat = constraint->seat->data; +static void handle_constraint_destroy(struct wl_listener *listener, + void *data) { + struct roots_pointer_constraint *constraint = + wl_container_of(listener, constraint, destroy); + struct wlr_pointer_constraint_v1 *wlr_constraint = data; + struct roots_seat *seat = wlr_constraint->seat->data; - double sx, sy; - struct wlr_surface *surface = desktop_surface_at(seat->input->server->desktop, - seat->cursor->cursor->x, seat->cursor->cursor->y, &sx, &sy, NULL); + wl_list_remove(&constraint->destroy.link); - if (surface == constraint->surface) { - assert(!seat->cursor->active_constraint); - roots_cursor_constrain(seat->cursor, constraint, sx, sy); - } -} + if (seat->cursor->active_constraint == wlr_constraint) { + wl_list_remove(&seat->cursor->constraint_commit.link); + wl_list_init(&seat->cursor->constraint_commit.link); + seat->cursor->active_constraint = NULL; -static void handle_constraint_destroy( - struct wl_listener *listener, - struct wlr_pointer_constraint_v1 *constraint) { - struct roots_seat* seat = constraint->seat->data; - if (seat->cursor->active_constraint == constraint) { - roots_cursor_constrain(seat->cursor, NULL, NAN, NAN); - if (constraint->current.committed & + if (wlr_constraint->current.committed & WLR_POINTER_CONSTRAINT_V1_STATE_CURSOR_HINT && seat->cursor->pointer_view) { - double sx = constraint->current.cursor_hint.x; - double sy = constraint->current.cursor_hint.y; + double sx = wlr_constraint->current.cursor_hint.x; + double sy = wlr_constraint->current.cursor_hint.y; struct roots_view *view = seat->cursor->pointer_view->view; - - double lx, ly; - if (view->rotation == 0.0) { - lx = sx + view->x; - ly = sy + view->y; - } else { - double c = cos(view->rotation); - double s = sin(view->rotation); - - double center_x = view->width / 2.; - double center_y = view->height / 2.; - - lx = c * (sx - center_x) - s * (sy - center_y) + center_x + view->x; - ly = s * (sx - center_x) + c * (sy - center_y) + center_y + view->y; - } + rotate_child_position(&sx, &sy, 0, 0, view->width, view->height, + view->rotation); + double lx = view->x + sx; + double ly = view->y + sy; wlr_cursor_warp(seat->cursor->cursor, NULL, lx, ly); } } + + free(constraint); +} + +static void handle_pointer_constraint(struct wl_listener *listener, + void *data) { + struct wlr_pointer_constraint_v1 *wlr_constraint = data; + struct roots_seat *seat = wlr_constraint->seat->data; + + struct roots_pointer_constraint *constraint = + calloc(1, sizeof(struct roots_pointer_constraint)); + constraint->constraint = wlr_constraint; + + constraint->destroy.notify = handle_constraint_destroy; + wl_signal_add(&wlr_constraint->events.destroy, &constraint->destroy); + + double sx, sy; + struct wlr_surface *surface = desktop_surface_at( + seat->input->server->desktop, + seat->cursor->cursor->x, seat->cursor->cursor->y, &sx, &sy, NULL); + + if (surface == wlr_constraint->surface) { + assert(!seat->cursor->active_constraint); + roots_cursor_constrain(seat->cursor, wlr_constraint, sx, sy); + } } struct roots_desktop *desktop_create(struct roots_server *server, @@ -956,13 +963,11 @@ struct roots_desktop *desktop_create(struct roots_server *server, &desktop->xdg_toplevel_decoration); desktop->xdg_toplevel_decoration.notify = handle_xdg_toplevel_decoration; - desktop->pointer_constraints = wlr_pointer_constraints_v1_create(server->wl_display); - desktop->constraint_destroy.notify = (wl_notify_func_t)handle_constraint_destroy; - wl_signal_add(&desktop->pointer_constraints->events.constraint_destroy, - &desktop->constraint_destroy); - desktop->constraint_create.notify = (wl_notify_func_t)handle_constraint_create; - wl_signal_add(&desktop->pointer_constraints->events.constraint_create, - &desktop->constraint_create); + desktop->pointer_constraints = + wlr_pointer_constraints_v1_create(server->wl_display); + desktop->pointer_constraint.notify = handle_pointer_constraint; + wl_signal_add(&desktop->pointer_constraints->events.new_constraint, + &desktop->pointer_constraint); return desktop; } diff --git a/rootston/output.c b/rootston/output.c index 4207f0d0..e85612fa 100644 --- a/rootston/output.c +++ b/rootston/output.c @@ -22,7 +22,7 @@ * Rotate a child's position relative to a parent. The parent size is (pw, ph), * the child position is (*sx, *sy) and its size is (sw, sh). */ -static void rotate_child_position(double *sx, double *sy, double sw, double sh, +void rotate_child_position(double *sx, double *sy, double sw, double sh, double pw, double ph, float rotation) { if (rotation != 0.0) { // Coordinates relative to the center of the subsurface diff --git a/rootston/seat.c b/rootston/seat.c index 466f1429..e3336fde 100644 --- a/rootston/seat.c +++ b/rootston/seat.c @@ -307,14 +307,6 @@ static void handle_pointer_focus_change(struct wl_listener *listener, roots_cursor_handle_focus_change(cursor, event); } -static void handle_constraint_commit(struct wl_listener *listener, - void *data) { - struct roots_cursor *cursor = - wl_container_of(listener, cursor, constraint_commit); - assert(cursor->active_constraint->surface == data); - roots_cursor_handle_constraint_commit(cursor); -} - static void seat_reset_device_mappings(struct roots_seat *seat, struct wlr_input_device *device) { struct wlr_cursor *cursor = seat->cursor->cursor; @@ -456,7 +448,6 @@ static void roots_seat_init_cursor(struct roots_seat *seat) { seat->cursor->focus_change.notify = handle_pointer_focus_change; wl_list_init(&seat->cursor->constraint_commit.link); - seat->cursor->constraint_commit.notify = handle_constraint_commit; } static void roots_drag_icon_handle_surface_commit(struct wl_listener *listener, diff --git a/types/wlr_pointer_constraints_v1.c b/types/wlr_pointer_constraints_v1.c index e281277f..ce69a856 100644 --- a/types/wlr_pointer_constraints_v1.c +++ b/types/wlr_pointer_constraints_v1.c @@ -1,14 +1,13 @@ #include #include +#include #include #include -#include #include #include #include #include #include - #include "util/signal.h" static const struct zwp_locked_pointer_v1_interface locked_pointer_impl; @@ -47,16 +46,13 @@ static void pointer_constraint_destroy(struct wlr_pointer_constraint_v1 *constra wlr_log(WLR_DEBUG, "destroying constraint %p", constraint); + wlr_signal_emit_safe(&constraint->events.destroy, constraint); + wl_resource_set_user_data(constraint->resource, NULL); wl_list_remove(&constraint->link); wl_list_remove(&constraint->surface_commit.link); wl_list_remove(&constraint->surface_destroy.link); wl_list_remove(&constraint->seat_destroy.link); - - wlr_signal_emit_safe( - &constraint->pointer_constraints->events.constraint_destroy, - constraint); - pixman_region32_fini(&constraint->current.region); pixman_region32_fini(&constraint->pending.region); pixman_region32_fini(&constraint->region); @@ -87,6 +83,9 @@ static void pointer_constraint_handle_set_region(struct wl_client *client, struct wl_resource *resource, struct wl_resource *region_resource) { struct wlr_pointer_constraint_v1 *constraint = pointer_constraint_from_resource(resource); + if (constraint == NULL) { + return; + } pointer_constraint_set_region(constraint, region_resource); } @@ -95,13 +94,16 @@ static void pointer_constraint_set_cursor_position_hint(struct wl_client *client struct wl_resource *resource, wl_fixed_t x, wl_fixed_t y) { struct wlr_pointer_constraint_v1 *constraint = pointer_constraint_from_resource(resource); + if (constraint == NULL) { + return; + } constraint->pending.cursor_hint.x = wl_fixed_to_double(x); constraint->pending.cursor_hint.y = wl_fixed_to_double(y); constraint->pending.committed |= WLR_POINTER_CONSTRAINT_V1_STATE_CURSOR_HINT; } -static void pointer_constraint_handle_commit( +static void pointer_constraint_commit( struct wlr_pointer_constraint_v1 *constraint) { if (constraint->pending.committed & WLR_POINTER_CONSTRAINT_V1_STATE_REGION) { @@ -130,7 +132,7 @@ static void handle_surface_commit(struct wl_listener *listener, void *data) { struct wlr_pointer_constraint_v1 *constraint = wl_container_of(listener, constraint, surface_commit); - pointer_constraint_handle_commit(constraint); + pointer_constraint_commit(constraint); } static void handle_surface_destroy(struct wl_listener *listener, void *data) { @@ -188,7 +190,6 @@ static void pointer_constraint_create(struct wl_client *client, struct wl_resource *resource = locked_pointer ? wl_resource_create(client, &zwp_locked_pointer_v1_interface, version, id) : wl_resource_create(client, &zwp_confined_pointer_v1_interface, version, id); - if (resource == NULL) { wl_client_post_no_memory(client); return; @@ -196,6 +197,7 @@ static void pointer_constraint_create(struct wl_client *client, struct wlr_pointer_constraint_v1 *constraint = calloc(1, sizeof(*constraint)); if (constraint == NULL) { + wl_resource_destroy(resource); wl_client_post_no_memory(client); return; } @@ -207,13 +209,15 @@ static void pointer_constraint_create(struct wl_client *client, constraint->type = type; constraint->pointer_constraints = pointer_constraints; + wl_signal_init(&constraint->events.destroy); + pixman_region32_init(&constraint->region); pixman_region32_init(&constraint->pending.region); pixman_region32_init(&constraint->current.region); pointer_constraint_set_region(constraint, region_resource); - pointer_constraint_handle_commit(constraint); + pointer_constraint_commit(constraint); constraint->surface_commit.notify = handle_surface_commit; wl_signal_add(&surface->events.commit, &constraint->surface_commit); @@ -224,16 +228,18 @@ static void pointer_constraint_create(struct wl_client *client, constraint->seat_destroy.notify = handle_seat_destroy; wl_signal_add(&seat->events.destroy, &constraint->seat_destroy); - wl_resource_set_implementation(constraint->resource, - locked_pointer ? (void*)&locked_pointer_impl : (void*)&confined_pointer_impl, - constraint, pointer_constraint_destroy_resource); + void *impl = locked_pointer ? + (void *)&locked_pointer_impl : (void *)&confined_pointer_impl; + wl_resource_set_implementation(constraint->resource, impl, constraint, + pointer_constraint_destroy_resource); wlr_log(WLR_DEBUG, "new %s_pointer %p (res %p)", - locked_pointer ? "locked" : "confined", constraint, constraint->resource); + locked_pointer ? "locked" : "confined", + constraint, constraint->resource); wl_list_insert(&pointer_constraints->constraints, &constraint->link); - wlr_signal_emit_safe(&pointer_constraints->events.constraint_create, + wlr_signal_emit_safe(&pointer_constraints->events.new_constraint, constraint); } @@ -303,8 +309,7 @@ struct wlr_pointer_constraints_v1 *wlr_pointer_constraints_v1_create( wl_list_init(&pointer_constraints->resources); wl_list_init(&pointer_constraints->constraints); - wl_signal_init(&pointer_constraints->events.constraint_create); - wl_signal_init(&pointer_constraints->events.constraint_destroy); + wl_signal_init(&pointer_constraints->events.new_constraint); return pointer_constraints; } From 792b1f5cbf98fab1ba1080b0bf7c5c592d6f0301 Mon Sep 17 00:00:00 2001 From: emersion Date: Thu, 27 Sep 2018 10:42:35 +0200 Subject: [PATCH 14/17] rootston: remove broken rotated pointer constraint handling --- rootston/cursor.c | 45 +++++++++------------------------------------ 1 file changed, 9 insertions(+), 36 deletions(-) diff --git a/rootston/cursor.c b/rootston/cursor.c index 08a71ec4..9a163c63 100644 --- a/rootston/cursor.c +++ b/rootston/cursor.c @@ -314,19 +314,14 @@ void roots_cursor_handle_motion(struct roots_cursor *cursor, struct roots_view *view = cursor->pointer_view->view; assert(view); - double center_x = view->x + view->width / 2.; - double center_y = view->y + view->height / 2.; - - double lx1 = cursor->cursor->x; - double ly1 = cursor->cursor->y; - - double lx2 = lx1 + dx; - double ly2 = ly1 + dy; - - // Optimization for most common case. - // This also makes sure that we don't encounter - // precision bugs in the most common case. + // TODO: handle rotated views if (view->rotation == 0.0) { + double lx1 = cursor->cursor->x; + double ly1 = cursor->cursor->y; + + double lx2 = lx1 + dx; + double ly2 = ly1 + dy; + double sx1 = lx1 - view->x; double sy1 = ly1 - view->y; @@ -334,35 +329,13 @@ void roots_cursor_handle_motion(struct roots_cursor *cursor, double sy2 = ly2 - view->y; double sx2_confined, sy2_confined; - if (!wlr_region_confine(&cursor->confine, sx1, sy1, sx2, sy2, &sx2_confined, &sy2_confined)) { + if (!wlr_region_confine(&cursor->confine, sx1, sy1, sx2, sy2, + &sx2_confined, &sy2_confined)) { return; } dx = sx2_confined - sx1; dy = sy2_confined - sy1; - } else { - assert(false); - double c = cos(view->rotation); - double s = sin(view->rotation); - - double sx1 = c * (lx1 - center_x) - s * (ly1 - center_y) + view->width / 2.; - double sy1 = s * (lx1 - center_x) + c * (ly1 - center_y) + view->height / 2.; - - double sx2 = c * (lx2 - center_x) - s * (ly2 - center_y) + view->width / 2.; - double sy2 = s * (lx2 - center_x) + c * (ly2 - center_y) + view->height / 2.; - - double sx2_confined, sy2_confined; - if (!wlr_region_confine(&cursor->confine, sx1, sy1, sx2, sy2, &sx2_confined, &sy2_confined)) { - return; - } - - // avoid NaNs - double fraction = (sx2 - sx1) > (sy2 - sy1) ? - (sx2_confined - sx1) / (sx2 - sx1) : - (sy2_confined - sy1) / (sy2 - sy1); - - dx *= fraction; - dy *= fraction; } } From d98181dab8f994d651d0a1efc9f24e5d3ca0779b Mon Sep 17 00:00:00 2001 From: emersion Date: Thu, 27 Sep 2018 10:53:11 +0200 Subject: [PATCH 15/17] pointer-constraints: use proper wayland-scanner functions --- .../wlr/types/wlr_pointer_constraints_v1.h | 3 +++ types/wlr_pointer_constraints_v1.c | 23 +++++++++++-------- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/include/wlr/types/wlr_pointer_constraints_v1.h b/include/wlr/types/wlr_pointer_constraints_v1.h index 97edf0ea..98442241 100644 --- a/include/wlr/types/wlr_pointer_constraints_v1.h +++ b/include/wlr/types/wlr_pointer_constraints_v1.h @@ -85,6 +85,9 @@ struct wlr_pointer_constraint_v1 * void wlr_pointer_constraint_v1_send_activated( struct wlr_pointer_constraint_v1 *constraint); +/** + * Deactivate the constraint. May destroy the constraint. + */ void wlr_pointer_constraint_v1_send_deactivated( struct wlr_pointer_constraint_v1 *constraint); diff --git a/types/wlr_pointer_constraints_v1.c b/types/wlr_pointer_constraints_v1.c index ce69a856..196af92e 100644 --- a/types/wlr_pointer_constraints_v1.c +++ b/types/wlr_pointer_constraints_v1.c @@ -346,22 +346,27 @@ struct wlr_pointer_constraint_v1 * return NULL; } -// Thankfully zwp_confined_pointer_v1_send_{un,}confined work -// on both locked and confined pointer constraints. void wlr_pointer_constraint_v1_send_activated( struct wlr_pointer_constraint_v1 *constraint) { wlr_log(WLR_DEBUG, "constrained %p", constraint); - zwp_confined_pointer_v1_send_confined(constraint->resource); + if (constraint->type == WLR_POINTER_CONSTRAINT_V1_LOCKED) { + zwp_locked_pointer_v1_send_locked(constraint->resource); + } else { + zwp_confined_pointer_v1_send_confined(constraint->resource); + } } void wlr_pointer_constraint_v1_send_deactivated( struct wlr_pointer_constraint_v1 *constraint) { - if (wl_resource_get_user_data(constraint->resource)) { - wlr_log(WLR_DEBUG, "unconstrained %p", constraint); + wlr_log(WLR_DEBUG, "unconstrained %p", constraint); + if (constraint->type == WLR_POINTER_CONSTRAINT_V1_LOCKED) { + zwp_locked_pointer_v1_send_unlocked(constraint->resource); + } else { zwp_confined_pointer_v1_send_unconfined(constraint->resource); - if (constraint->lifetime == - ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_ONESHOT) { - pointer_constraint_destroy(constraint); - } + } + + if (constraint->lifetime == + ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_ONESHOT) { + pointer_constraint_destroy(constraint); } } From fadbdcd13c7fcbbad0eebc4867e23cf71c01df1d Mon Sep 17 00:00:00 2001 From: emersion Date: Thu, 27 Sep 2018 12:21:30 +0200 Subject: [PATCH 16/17] seat: remove unused keyboard focus_change event --- include/wlr/types/wlr_seat.h | 14 +------------- types/seat/wlr_seat.c | 2 -- types/seat/wlr_seat_keyboard.c | 10 ---------- 3 files changed, 1 insertion(+), 25 deletions(-) diff --git a/include/wlr/types/wlr_seat.h b/include/wlr/types/wlr_seat.h index 4daefff1..59de8207 100644 --- a/include/wlr/types/wlr_seat.h +++ b/include/wlr/types/wlr_seat.h @@ -148,7 +148,7 @@ struct wlr_seat_pointer_state { struct wl_listener surface_destroy; struct { - struct wl_signal focus_change; + struct wl_signal focus_change; // wlr_seat_pointer_focus_change_event } events; }; @@ -168,10 +168,6 @@ struct wlr_seat_keyboard_state { struct wlr_seat_keyboard_grab *grab; struct wlr_seat_keyboard_grab *default_grab; - - struct { - struct wl_signal focus_change; - } events; }; struct wlr_seat_touch_state { @@ -252,14 +248,6 @@ struct wlr_seat_pointer_focus_change_event { double sx, sy; }; -struct wlr_seat_keyboard_focus_change_event { - struct wlr_seat *seat; - struct wlr_surface *old_surface, *new_surface; - size_t num_keycodes; - uint32_t *keycodes; - struct wlr_keyboard_modifiers *modifiers; -}; - /** * Allocates a new wlr_seat and adds a wl_seat global to the display. */ diff --git a/types/seat/wlr_seat.c b/types/seat/wlr_seat.c index c4da29e2..1940d7d6 100644 --- a/types/seat/wlr_seat.c +++ b/types/seat/wlr_seat.c @@ -243,8 +243,6 @@ struct wlr_seat *wlr_seat_create(struct wl_display *display, const char *name) { seat->keyboard_state.seat = seat; wl_list_init(&seat->keyboard_state.surface_destroy.link); - wl_signal_init(&seat->keyboard_state.events.focus_change); - // touch state struct wlr_seat_touch_grab *touch_grab = calloc(1, sizeof(struct wlr_seat_touch_grab)); diff --git a/types/seat/wlr_seat_keyboard.c b/types/seat/wlr_seat_keyboard.c index 4f95b265..e8ea300e 100644 --- a/types/seat/wlr_seat_keyboard.c +++ b/types/seat/wlr_seat_keyboard.c @@ -291,16 +291,6 @@ void wlr_seat_keyboard_enter(struct wlr_seat *seat, // as it targets seat->keyboard_state.focused_client wlr_seat_keyboard_send_modifiers(seat, modifiers); } - - struct wlr_seat_keyboard_focus_change_event event = { - .seat = seat, - .new_surface = surface, - .old_surface = focused_surface, - .num_keycodes = num_keycodes, - .keycodes = keycodes, - .modifiers = modifiers, - }; - wlr_signal_emit_safe(&seat->keyboard_state.events.focus_change, &event); } void wlr_seat_keyboard_notify_enter(struct wlr_seat *seat, From 1b598f67ca28dce6b61829694ba1a55d05e35678 Mon Sep 17 00:00:00 2001 From: emersion Date: Thu, 27 Sep 2018 12:39:29 +0200 Subject: [PATCH 17/17] pointer-constraints: add unstable header --- include/wlr/types/wlr_pointer_constraints_v1.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/include/wlr/types/wlr_pointer_constraints_v1.h b/include/wlr/types/wlr_pointer_constraints_v1.h index 98442241..fef8c2e9 100644 --- a/include/wlr/types/wlr_pointer_constraints_v1.h +++ b/include/wlr/types/wlr_pointer_constraints_v1.h @@ -1,3 +1,11 @@ +/* + * This an unstable interface of wlroots. No guarantees are made regarding the + * future consistency of this API. + */ +#ifndef WLR_USE_UNSTABLE +#error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" +#endif + #ifndef WLR_TYPES_WLR_POINTER_CONSTRAINTS_V1_H #define WLR_TYPES_WLR_POINTER_CONSTRAINTS_V1_H