1/* swc: libswc/pointer.c
2 *
3 * Copyright (c) 2013-2020 Michael Forney
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to deal
7 * in the Software without restriction, including without limitation the rights
8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 * copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 * SOFTWARE.
22 */
23
24#include "pointer.h"
25#include "compositor.h"
26#include "cursor/cursor_data.h"
27#include "event.h"
28#include "internal.h"
29#include "plane.h"
30#include "screen.h"
31#include "seat.h"
32#include "shm.h"
33#include "surface.h"
34#include "util.h"
35
36#include <assert.h>
37#include <stdio.h>
38#include <string.h>
39#include <wld/wld.h>
40
41static enum swc_cursor_kind cursor_override = SWC_CURSOR_DEFAULT;
42static enum swc_cursor_mode cursor_mode = SWC_CURSOR_MODE_CLIENT;
43
44static struct {
45 const uint32_t *data;
46 uint32_t width, height;
47 int32_t hotspot_x, hotspot_y;
48 bool active;
49} cursor_images[6];
50
51EXPORT void
52swc_pointer_send_button(uint32_t time, uint32_t button, uint32_t state)
53{
54 struct pointer *pointer = swc.seat ? swc.seat->pointer : NULL;
55 struct wl_resource *resource;
56 uint32_t serial;
57
58 if (!pointer || wl_list_empty(&pointer->focus.active)) {
59 return;
60 }
61
62 serial = wl_display_next_serial(swc.display);
63 wl_resource_for_each(resource, &pointer->focus.active)
64 wl_pointer_send_button(resource, serial, time, button, state);
65 wl_resource_for_each(resource, &pointer->focus.active)
66 {
67 if (wl_resource_get_version(resource) >=
68 WL_POINTER_FRAME_SINCE_VERSION) {
69 wl_pointer_send_frame(resource);
70 }
71 }
72 pointer->client_axis_source = -1;
73}
74
75EXPORT void
76swc_pointer_send_axis(uint32_t time, uint32_t axis, int32_t value120)
77{
78 struct pointer *pointer = swc.seat ? swc.seat->pointer : NULL;
79 struct wl_resource *resource;
80 wl_fixed_t value;
81
82 if (!pointer || wl_list_empty(&pointer->focus.active)) {
83 return;
84 }
85
86 value = wl_fixed_from_double((double)value120 / 120.0);
87
88 wl_resource_for_each(resource, &pointer->focus.active)
89 {
90 int ver = wl_resource_get_version(resource);
91
92 if (ver >= WL_POINTER_AXIS_SOURCE_SINCE_VERSION) {
93 wl_pointer_send_axis_source(resource, WL_POINTER_AXIS_SOURCE_WHEEL);
94 }
95 if (value120) {
96 if (ver >= WL_POINTER_AXIS_VALUE120_SINCE_VERSION) {
97 wl_pointer_send_axis_value120(resource, axis, value120);
98 } else if (ver >= WL_POINTER_AXIS_DISCRETE_SINCE_VERSION) {
99 wl_pointer_send_axis_discrete(resource, axis, value120 / 120);
100 }
101 }
102
103 if (value) {
104 wl_pointer_send_axis(resource, time, axis, value);
105 } else if (ver >= WL_POINTER_AXIS_STOP_SINCE_VERSION) {
106 wl_pointer_send_axis_stop(resource, time, axis);
107 }
108 }
109
110 wl_resource_for_each(resource, &pointer->focus.active)
111 {
112 if (wl_resource_get_version(resource) >=
113 WL_POINTER_FRAME_SINCE_VERSION) {
114 wl_pointer_send_frame(resource);
115 }
116 }
117 pointer->client_axis_source = -1;
118}
119
120static void
121enter(struct input_focus_handler *handler, struct wl_list *resources,
122 struct compositor_view *view)
123{
124 struct pointer *pointer = wl_container_of(handler, pointer, focus_handler);
125 struct wl_resource *resource;
126 uint32_t serial;
127 wl_fixed_t surface_x, surface_y;
128 int32_t origin_x, origin_y;
129
130 if (wl_list_empty(resources)) {
131 pointer_set_cursor(pointer, cursor_left_ptr);
132 return;
133 }
134 serial = wl_display_next_serial(swc.display);
135 /* do based on buffer origin, holy fuck */
136 origin_x = view->base.geometry.x - view->buffer_offset_x;
137 origin_y = view->base.geometry.y - view->buffer_offset_y;
138 surface_x = pointer->x - wl_fixed_from_int(origin_x);
139 surface_y = pointer->y - wl_fixed_from_int(origin_y);
140 wl_resource_for_each(resource, resources) wl_pointer_send_enter(
141 resource, serial, view->surface->resource, surface_x, surface_y);
142}
143
144static void
145leave(struct input_focus_handler *handler, struct wl_list *resources,
146 struct compositor_view *view)
147{
148 struct wl_resource *resource;
149 uint32_t serial;
150
151 serial = wl_display_next_serial(swc.display);
152 wl_resource_for_each(resource, resources)
153 wl_pointer_send_leave(resource, serial, view->surface->resource);
154}
155
156static void
157handle_cursor_surface_destroy(struct wl_listener *listener, void *data)
158{
159 struct pointer *pointer =
160 wl_container_of(listener, pointer, cursor.destroy_listener);
161
162 view_attach(&pointer->cursor.view, NULL);
163 pointer->cursor.surface = NULL;
164}
165
166static bool
167update(struct view *view)
168{
169 view_frame(view, get_time());
170 return true;
171}
172
173static int
174attach(struct view *view, struct wld_buffer *buffer)
175{
176 struct pointer *pointer = wl_container_of(view, pointer, cursor.view);
177 struct surface *surface = pointer->cursor.surface;
178 struct screen *screen;
179
180 if (surface && !pixman_region32_not_empty(&surface->state.damage)) {
181 return 0;
182 }
183
184 wld_set_target_buffer(swc.shm->renderer, pointer->cursor.buffer);
185 wld_fill_rectangle(swc.shm->renderer, 0x00000000, 0, 0,
186 pointer->cursor.buffer->width,
187 pointer->cursor.buffer->height);
188
189 if (buffer) {
190 wld_copy_rectangle(swc.shm->renderer, buffer, 0, 0, 0, 0, buffer->width,
191 buffer->height);
192 }
193
194 wld_flush(swc.shm->renderer);
195
196 if (surface) {
197 pixman_region32_clear(&surface->state.damage);
198 }
199
200 /* TODO: Send an early release to the buffer */
201
202 if (view_set_size_from_buffer(view, buffer)) {
203 view_update_screens(view);
204 }
205
206 wl_list_for_each(screen, &swc.screens, link)
207 {
208 view_attach(&screen->planes.cursor->view,
209 buffer ? pointer->cursor.buffer : NULL);
210 view_update(&screen->planes.cursor->view);
211 }
212
213 return 0;
214}
215
216static bool
217move(struct view *view, int32_t x, int32_t y)
218{
219 struct screen *screen;
220
221 if (view_set_position(view, x, y)) {
222 view_update_screens(view);
223 }
224
225 wl_list_for_each(screen, &swc.screens, link)
226 {
227 view_move(&screen->planes.cursor->view, view->geometry.x,
228 view->geometry.y);
229 view_update(&screen->planes.cursor->view);
230 }
231
232 return true;
233}
234
235static const struct view_impl view_impl = {
236 .update = update,
237 .attach = attach,
238 .move = move,
239};
240
241static inline void
242update_cursor(struct pointer *pointer)
243{
244 int32_t x = wl_fixed_to_int(pointer->x) - pointer->cursor.hotspot.x,
245 y = wl_fixed_to_int(pointer->y) - pointer->cursor.hotspot.y;
246
247 view_move(&pointer->cursor.view, x, y);
248}
249
250static void
251drop_client_cursor_surface(struct pointer *pointer)
252{
253 if (!pointer || !pointer->cursor.surface) {
254 return;
255 }
256 surface_set_view(pointer->cursor.surface, NULL);
257 wl_list_remove(&pointer->cursor.destroy_listener.link);
258 pointer->cursor.surface = NULL;
259}
260
261static void
262apply_cursor_override(struct pointer *pointer)
263{
264 if (!pointer || pointer->cursor.surface) {
265 return;
266 }
267
268 pointer_set_cursor(pointer, cursor_left_ptr);
269}
270
271EXPORT void
272swc_set_cursor(enum swc_cursor_kind kind)
273{
274 struct pointer *pointer = swc.seat ? swc.seat->pointer : NULL;
275
276 cursor_override = kind;
277
278 drop_client_cursor_surface(pointer);
279
280 apply_cursor_override(pointer);
281}
282
283EXPORT void
284swc_set_cursor_mode(enum swc_cursor_mode mode)
285{
286 struct pointer *pointer = swc.seat ? swc.seat->pointer : NULL;
287
288 cursor_mode = mode;
289 if (cursor_mode == SWC_CURSOR_MODE_COMPOSITOR) {
290 drop_client_cursor_surface(pointer);
291 }
292 apply_cursor_override(pointer);
293}
294
295EXPORT void
296swc_set_cursor_image(enum swc_cursor_kind kind, const uint32_t *argb8888,
297 uint32_t width, uint32_t height, int32_t hotspot_x,
298 int32_t hotspot_y)
299{
300 struct pointer *pointer = swc.seat ? swc.seat->pointer : NULL;
301
302 if (kind < 0 || kind >= (int)ARRAY_LENGTH(cursor_images)) {
303 return;
304 }
305 if (!argb8888 || width == 0 || height == 0) {
306 return;
307 }
308
309 cursor_images[kind].data = argb8888;
310 cursor_images[kind].width = width;
311 cursor_images[kind].height = height;
312 cursor_images[kind].hotspot_x = hotspot_x;
313 cursor_images[kind].hotspot_y = hotspot_y;
314 cursor_images[kind].active = true;
315
316 if (cursor_mode == SWC_CURSOR_MODE_COMPOSITOR) {
317 drop_client_cursor_surface(pointer);
318 }
319 apply_cursor_override(pointer);
320}
321
322EXPORT void
323swc_clear_cursor_image(enum swc_cursor_kind kind)
324{
325 struct pointer *pointer = swc.seat ? swc.seat->pointer : NULL;
326
327 if (kind < 0 || kind >= (int)ARRAY_LENGTH(cursor_images)) {
328 return;
329 }
330
331 cursor_images[kind].active = false;
332 cursor_images[kind].data = NULL;
333
334 apply_cursor_override(pointer);
335}
336
337void
338pointer_set_cursor(struct pointer *pointer, uint32_t id)
339{
340 struct cursor *cursor = &cursor_metadata[id];
341 const uint32_t *data = cursor_data;
342 union wld_object object = {.ptr = &cursor_data[cursor->offset]};
343 struct wld_buffer *buffer;
344
345 if (id == cursor_left_ptr) {
346 enum swc_cursor_kind kind = cursor_override;
347 if (kind < 0 || kind >= (int)ARRAY_LENGTH(cursor_images)) {
348 kind = SWC_CURSOR_DEFAULT;
349 }
350
351 if (cursor_images[kind].active) {
352 static struct cursor custom_cursor;
353 custom_cursor.width = (int)cursor_images[kind].width;
354 custom_cursor.height = (int)cursor_images[kind].height;
355 custom_cursor.hotspot_x = (int)cursor_images[kind].hotspot_x;
356 custom_cursor.hotspot_y = (int)cursor_images[kind].hotspot_y;
357 custom_cursor.offset = 0;
358
359 cursor = &custom_cursor;
360 data = cursor_images[kind].data;
361 object.ptr = (void *)data;
362 }
363 }
364
365 if (pointer->cursor.internal_buffer) {
366 wld_buffer_unreference(pointer->cursor.internal_buffer);
367 }
368 if (pointer->cursor.surface) {
369 surface_set_view(pointer->cursor.surface, NULL);
370 wl_list_remove(&pointer->cursor.destroy_listener.link);
371 pointer->cursor.surface = NULL;
372 }
373
374 buffer = wld_import_buffer(swc.shm->context, WLD_OBJECT_DATA, object,
375 cursor->width, cursor->height,
376 WLD_FORMAT_ARGB8888, cursor->width * 4);
377 if (!buffer) {
378 WARNING("Failed to create cursor buffer\n");
379 }
380 pointer->cursor.internal_buffer = buffer;
381 pointer->cursor.hotspot.x = cursor->hotspot_x;
382 pointer->cursor.hotspot.y = cursor->hotspot_y;
383 update_cursor(pointer);
384 view_attach(&pointer->cursor.view, buffer);
385}
386
387static bool
388client_handle_motion(struct pointer_handler *handler, uint32_t time,
389 wl_fixed_t x, wl_fixed_t y)
390{
391 struct pointer *pointer = wl_container_of(handler, pointer, client_handler);
392 struct wl_resource *resource;
393 wl_fixed_t sx, sy;
394 int32_t origin_x, origin_y;
395
396 if (wl_list_empty(&pointer->focus.active)) {
397 return false;
398 }
399
400 origin_x = pointer->focus.view->base.geometry.x -
401 pointer->focus.view->buffer_offset_x;
402 origin_y = pointer->focus.view->base.geometry.y -
403 pointer->focus.view->buffer_offset_y;
404 sx = x - wl_fixed_from_int(origin_x);
405 sy = y - wl_fixed_from_int(origin_y);
406 wl_resource_for_each(resource, &pointer->focus.active)
407 wl_pointer_send_motion(resource, time, sx, sy);
408 return true;
409}
410
411static bool
412client_handle_button(struct pointer_handler *handler, uint32_t time,
413 struct button *button, uint32_t state)
414{
415 struct pointer *pointer = wl_container_of(handler, pointer, client_handler);
416 struct wl_resource *resource;
417
418 if (wl_list_empty(&pointer->focus.active)) {
419 return false;
420 }
421
422 wl_resource_for_each(resource, &pointer->focus.active)
423 wl_pointer_send_button(resource, button->press.serial, time,
424 button->press.value, state);
425 return true;
426}
427
428static bool
429client_handle_axis(struct pointer_handler *handler, uint32_t time,
430 enum wl_pointer_axis axis,
431 enum wl_pointer_axis_source source, wl_fixed_t value,
432 int value120)
433{
434 struct pointer *pointer = wl_container_of(handler, pointer, client_handler);
435 struct wl_resource *resource;
436 int ver;
437
438 if (wl_list_empty(&pointer->focus.active)) {
439 return false;
440 }
441
442 if (pointer->client_axis_source != -1) {
443 assert(pointer->client_axis_source == source);
444 source = -1;
445 } else {
446 pointer->client_axis_source = source;
447 }
448
449 wl_resource_for_each(resource, &pointer->focus.active)
450 {
451 ver = wl_resource_get_version(resource);
452 if (source != -1 && ver >= WL_POINTER_AXIS_SOURCE_SINCE_VERSION) {
453 wl_pointer_send_axis_source(resource, source);
454 }
455 if (value120) {
456 if (ver >= WL_POINTER_AXIS_VALUE120_SINCE_VERSION) {
457 wl_pointer_send_axis_value120(resource, axis, value120);
458 } else if (ver >= WL_POINTER_AXIS_DISCRETE_SINCE_VERSION) {
459 wl_pointer_send_axis_discrete(resource, axis, value120 / 120);
460 }
461 }
462 if (value) {
463 wl_pointer_send_axis(resource, time, axis, value);
464 } else if (ver >= WL_POINTER_AXIS_STOP_SINCE_VERSION) {
465 wl_pointer_send_axis_stop(resource, time, axis);
466 }
467 }
468 return true;
469}
470
471static void
472client_handle_frame(struct pointer_handler *handler)
473{
474 struct pointer *pointer = wl_container_of(handler, pointer, client_handler);
475 struct wl_resource *resource;
476
477 wl_resource_for_each(resource, &pointer->focus.active)
478 {
479 if (wl_resource_get_version(resource) >=
480 WL_POINTER_FRAME_SINCE_VERSION) {
481 wl_pointer_send_frame(resource);
482 }
483 }
484 pointer->client_axis_source = -1;
485}
486
487bool
488pointer_initialize(struct pointer *pointer)
489{
490 struct screen *screen = wl_container_of(swc.screens.next, screen, link);
491 struct swc_rectangle *geom = &screen->base.geometry;
492
493 /* Center cursor in the geometry of the first screen. */
494 pointer->x = wl_fixed_from_int(geom->x + geom->width / 2);
495 pointer->y = wl_fixed_from_int(geom->y + geom->height / 2);
496 pointer->focus_handler.enter = enter;
497 pointer->focus_handler.leave = leave;
498 pointer->client_handler.motion = client_handle_motion;
499 pointer->client_handler.button = client_handle_button;
500 pointer->client_handler.axis = client_handle_axis;
501 pointer->client_handler.frame = client_handle_frame;
502 pointer->client_handler.pending = false;
503 pointer->client_axis_source = -1;
504 wl_list_init(&pointer->handlers);
505 wl_list_insert(&pointer->handlers, &pointer->client_handler.link);
506 wl_array_init(&pointer->buttons);
507
508 view_initialize(&pointer->cursor.view, &view_impl);
509 pointer->cursor.surface = NULL;
510 pointer->cursor.destroy_listener.notify = &handle_cursor_surface_destroy;
511 pointer->cursor.buffer = wld_create_buffer(
512 swc.drm->context, swc.drm->cursor_w, swc.drm->cursor_h,
513 WLD_FORMAT_ARGB8888, WLD_FLAG_MAP | WLD_FLAG_CURSOR);
514 pointer->cursor.internal_buffer = NULL;
515
516 if (!pointer->cursor.buffer) {
517 return false;
518 }
519
520 pointer_set_cursor(pointer, cursor_left_ptr);
521
522 wl_list_for_each(screen, &swc.screens, link)
523 view_attach(&screen->planes.cursor->view, pointer->cursor.buffer);
524
525 input_focus_initialize(&pointer->focus, &pointer->focus_handler);
526 pixman_region32_init(&pointer->region);
527
528 return true;
529}
530
531void
532pointer_finalize(struct pointer *pointer)
533{
534 input_focus_finalize(&pointer->focus);
535 pixman_region32_fini(&pointer->region);
536}
537
538void
539pointer_set_focus(struct pointer *pointer, struct compositor_view *view)
540{
541 input_focus_set(&pointer->focus, view);
542}
543
544static void
545clip_position(struct pointer *pointer, wl_fixed_t fx, wl_fixed_t fy)
546{
547 int32_t x, y, last_x, last_y;
548 pixman_box32_t box;
549
550 x = wl_fixed_to_int(fx);
551 y = wl_fixed_to_int(fy);
552 last_x = wl_fixed_to_int(pointer->x);
553 last_y = wl_fixed_to_int(pointer->y);
554
555 if (!pixman_region32_contains_point(&pointer->region, x, y, NULL)) {
556 if (!pixman_region32_contains_point(&pointer->region, last_x, last_y,
557 &box)) {
558 WARNING("cursor is not in the visible screen area\n");
559 pointer->x = 0;
560 pointer->y = 0;
561 return;
562 }
563
564 /* Do some clipping. */
565 fx = wl_fixed_from_int(MAX(MIN(x, box.x2 - 1), box.x1));
566 fy = wl_fixed_from_int(MAX(MIN(y, box.y2 - 1), box.y1));
567 }
568
569 pointer->x = fx;
570 pointer->y = fy;
571}
572
573void
574pointer_set_region(struct pointer *pointer, pixman_region32_t *region)
575{
576 pixman_region32_copy(&pointer->region, region);
577 clip_position(pointer, pointer->x, pointer->y);
578}
579
580static void
581set_cursor(struct wl_client *client, struct wl_resource *resource,
582 uint32_t serial, struct wl_resource *surface_resource,
583 int32_t hotspot_x, int32_t hotspot_y)
584{
585 struct pointer *pointer = wl_resource_get_user_data(resource);
586 struct surface *surface;
587
588 (void)serial;
589
590 if (client != pointer->focus.client) {
591 return;
592 }
593
594 /* If forcing compositor cursor, ignore client cursor surfaces. */
595 if (cursor_mode == SWC_CURSOR_MODE_COMPOSITOR ||
596 cursor_override != SWC_CURSOR_DEFAULT) {
597 return;
598 }
599
600 if (pointer->cursor.surface) {
601 surface_set_view(pointer->cursor.surface, NULL);
602 wl_list_remove(&pointer->cursor.destroy_listener.link);
603 }
604
605 surface =
606 surface_resource ? wl_resource_get_user_data(surface_resource) : NULL;
607 pointer->cursor.surface = surface;
608 pointer->cursor.hotspot.x = hotspot_x;
609 pointer->cursor.hotspot.y = hotspot_y;
610
611 if (surface) {
612 surface_set_view(surface, &pointer->cursor.view);
613 wl_resource_add_destroy_listener(surface->resource,
614 &pointer->cursor.destroy_listener);
615 update_cursor(pointer);
616 }
617}
618
619static const struct wl_pointer_interface pointer_impl = {
620 .set_cursor = set_cursor,
621 .release = destroy_resource,
622};
623
624static void
625unbind(struct wl_resource *resource)
626{
627 struct pointer *pointer = wl_resource_get_user_data(resource);
628 input_focus_remove_resource(&pointer->focus, resource);
629}
630
631struct wl_resource *
632pointer_bind(struct pointer *pointer, struct wl_client *client,
633 uint32_t version, uint32_t id)
634{
635 struct wl_resource *client_resource;
636
637 client_resource =
638 wl_resource_create(client, &wl_pointer_interface, version, id);
639 if (!client_resource) {
640 return NULL;
641 }
642 wl_resource_set_implementation(client_resource, &pointer_impl, pointer,
643 &unbind);
644 input_focus_add_resource(&pointer->focus, client_resource);
645
646 return client_resource;
647}
648
649struct button *
650pointer_get_button(struct pointer *pointer, uint32_t serial)
651{
652 struct button *button;
653
654 wl_array_for_each(button, &pointer->buttons)
655 {
656 if (button->press.serial == serial) {
657 return button;
658 }
659 }
660
661 return NULL;
662}
663
664void
665pointer_handle_button(struct pointer *pointer, uint32_t time, uint32_t value,
666 uint32_t state)
667{
668 struct pointer_handler *handler;
669 struct button *button;
670 uint32_t serial;
671
672 serial = wl_display_next_serial(swc.display);
673
674 if (state == WL_POINTER_BUTTON_STATE_RELEASED) {
675 wl_array_for_each(button, &pointer->buttons)
676 {
677 if (button->press.value == value) {
678 if (button->handler) {
679 button->press.serial = serial;
680 button->handler->button(button->handler, time, button,
681 state);
682 button->handler->pending = true;
683 }
684
685 array_remove(&pointer->buttons, button, sizeof(*button));
686 break;
687 }
688 }
689 } else {
690 button = wl_array_add(&pointer->buttons, sizeof(*button));
691
692 if (!button) {
693 return;
694 }
695
696 button->press.value = value;
697 button->press.serial = serial;
698 button->handler = NULL;
699
700 wl_list_for_each(handler, &pointer->handlers, link)
701 {
702 if (handler->button &&
703 handler->button(handler, time, button, state)) {
704 button->handler = handler;
705 handler->pending = true;
706 break;
707 }
708 }
709 }
710}
711
712void
713pointer_handle_axis(struct pointer *pointer, uint32_t time,
714 enum wl_pointer_axis axis,
715 enum wl_pointer_axis_source source, wl_fixed_t value,
716 int value120)
717{
718 struct pointer_handler *handler;
719
720 wl_list_for_each(handler, &pointer->handlers, link)
721 {
722 if (handler->axis &&
723 handler->axis(handler, time, axis, source, value, value120)) {
724 handler->pending = true;
725 break;
726 }
727 }
728}
729
730void
731pointer_handle_relative_motion(struct pointer *pointer, uint32_t time,
732 wl_fixed_t dx, wl_fixed_t dy)
733{
734 pointer_handle_absolute_motion(pointer, time, pointer->x + dx,
735 pointer->y + dy);
736}
737
738void
739pointer_handle_absolute_motion(struct pointer *pointer, uint32_t time,
740 wl_fixed_t x, wl_fixed_t y)
741{
742 struct pointer_handler *handler;
743
744 clip_position(pointer, x, y);
745
746 wl_list_for_each(handler, &pointer->handlers, link)
747 {
748 if (handler->motion &&
749 handler->motion(handler, time, pointer->x, pointer->y)) {
750 handler->pending = true;
751 break;
752 }
753 }
754
755 update_cursor(pointer);
756}
757
758void
759pointer_handle_frame(struct pointer *pointer)
760{
761 struct pointer_handler *handler;
762
763 wl_list_for_each(handler, &pointer->handlers, link)
764 {
765 if (handler->pending && handler->frame) {
766 handler->frame(handler);
767 handler->pending = false;
768 }
769 }
770
771 update_cursor(pointer);
772}