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 screen = wl_container_of(swc.screens.next, screen, link);
495 pointer->x = wl_fixed_from_int(geom->x + geom->width / 2);
496 pointer->y = wl_fixed_from_int(geom->y + geom->height / 2);
497 pointer->focus_handler.enter = enter;
498 pointer->focus_handler.leave = leave;
499 pointer->client_handler.motion = client_handle_motion;
500 pointer->client_handler.button = client_handle_button;
501 pointer->client_handler.axis = client_handle_axis;
502 pointer->client_handler.frame = client_handle_frame;
503 pointer->client_handler.pending = false;
504 pointer->client_axis_source = -1;
505 wl_list_init(&pointer->handlers);
506 wl_list_insert(&pointer->handlers, &pointer->client_handler.link);
507 wl_array_init(&pointer->buttons);
508
509 view_initialize(&pointer->cursor.view, &view_impl);
510 pointer->cursor.surface = NULL;
511 pointer->cursor.destroy_listener.notify = &handle_cursor_surface_destroy;
512 pointer->cursor.buffer = wld_create_buffer(
513 swc.drm->context, swc.drm->cursor_w, swc.drm->cursor_h,
514 WLD_FORMAT_ARGB8888, WLD_FLAG_MAP | WLD_FLAG_CURSOR);
515 pointer->cursor.internal_buffer = NULL;
516
517 if (!pointer->cursor.buffer) {
518 return false;
519 }
520
521 pointer_set_cursor(pointer, cursor_left_ptr);
522
523 wl_list_for_each(screen, &swc.screens, link)
524 view_attach(&screen->planes.cursor->view, pointer->cursor.buffer);
525
526 input_focus_initialize(&pointer->focus, &pointer->focus_handler);
527 pixman_region32_init(&pointer->region);
528
529 return true;
530}
531
532void
533pointer_finalize(struct pointer *pointer)
534{
535 input_focus_finalize(&pointer->focus);
536 pixman_region32_fini(&pointer->region);
537}
538
539void
540pointer_set_focus(struct pointer *pointer, struct compositor_view *view)
541{
542 input_focus_set(&pointer->focus, view);
543}
544
545static void
546clip_position(struct pointer *pointer, wl_fixed_t fx, wl_fixed_t fy)
547{
548 int32_t x, y, last_x, last_y;
549 pixman_box32_t box;
550
551 x = wl_fixed_to_int(fx);
552 y = wl_fixed_to_int(fy);
553 last_x = wl_fixed_to_int(pointer->x);
554 last_y = wl_fixed_to_int(pointer->y);
555
556 if (!pixman_region32_contains_point(&pointer->region, x, y, NULL)) {
557 if (!pixman_region32_contains_point(&pointer->region, last_x, last_y,
558 &box)) {
559 WARNING("cursor is not in the visible screen area\n");
560 pointer->x = 0;
561 pointer->y = 0;
562 return;
563 }
564
565 /* Do some clipping. */
566 fx = wl_fixed_from_int(MAX(MIN(x, box.x2 - 1), box.x1));
567 fy = wl_fixed_from_int(MAX(MIN(y, box.y2 - 1), box.y1));
568 }
569
570 pointer->x = fx;
571 pointer->y = fy;
572}
573
574void
575pointer_set_region(struct pointer *pointer, pixman_region32_t *region)
576{
577 pixman_region32_copy(&pointer->region, region);
578 clip_position(pointer, pointer->x, pointer->y);
579}
580
581static void
582set_cursor(struct wl_client *client, struct wl_resource *resource,
583 uint32_t serial, struct wl_resource *surface_resource,
584 int32_t hotspot_x, int32_t hotspot_y)
585{
586 struct pointer *pointer = wl_resource_get_user_data(resource);
587 struct surface *surface;
588
589 (void)serial;
590
591 if (client != pointer->focus.client) {
592 return;
593 }
594
595 /* If forcing compositor cursor, ignore client cursor surfaces. */
596 if (cursor_mode == SWC_CURSOR_MODE_COMPOSITOR ||
597 cursor_override != SWC_CURSOR_DEFAULT) {
598 return;
599 }
600
601 if (pointer->cursor.surface) {
602 surface_set_view(pointer->cursor.surface, NULL);
603 wl_list_remove(&pointer->cursor.destroy_listener.link);
604 }
605
606 surface =
607 surface_resource ? wl_resource_get_user_data(surface_resource) : NULL;
608 pointer->cursor.surface = surface;
609 pointer->cursor.hotspot.x = hotspot_x;
610 pointer->cursor.hotspot.y = hotspot_y;
611
612 if (surface) {
613 surface_set_view(surface, &pointer->cursor.view);
614 wl_resource_add_destroy_listener(surface->resource,
615 &pointer->cursor.destroy_listener);
616 update_cursor(pointer);
617 }
618}
619
620static const struct wl_pointer_interface pointer_impl = {
621 .set_cursor = set_cursor,
622 .release = destroy_resource,
623};
624
625static void
626unbind(struct wl_resource *resource)
627{
628 struct pointer *pointer = wl_resource_get_user_data(resource);
629 input_focus_remove_resource(&pointer->focus, resource);
630}
631
632struct wl_resource *
633pointer_bind(struct pointer *pointer, struct wl_client *client,
634 uint32_t version, uint32_t id)
635{
636 struct wl_resource *client_resource;
637
638 client_resource =
639 wl_resource_create(client, &wl_pointer_interface, version, id);
640 if (!client_resource) {
641 return NULL;
642 }
643 wl_resource_set_implementation(client_resource, &pointer_impl, pointer,
644 &unbind);
645 input_focus_add_resource(&pointer->focus, client_resource);
646
647 return client_resource;
648}
649
650struct button *
651pointer_get_button(struct pointer *pointer, uint32_t serial)
652{
653 struct button *button;
654
655 wl_array_for_each(button, &pointer->buttons)
656 {
657 if (button->press.serial == serial) {
658 return button;
659 }
660 }
661
662 return NULL;
663}
664
665void
666pointer_handle_button(struct pointer *pointer, uint32_t time, uint32_t value,
667 uint32_t state)
668{
669 struct pointer_handler *handler;
670 struct button *button;
671 uint32_t serial;
672
673 serial = wl_display_next_serial(swc.display);
674
675 if (state == WL_POINTER_BUTTON_STATE_RELEASED) {
676 wl_array_for_each(button, &pointer->buttons)
677 {
678 if (button->press.value == value) {
679 if (button->handler) {
680 button->press.serial = serial;
681 button->handler->button(button->handler, time, button,
682 state);
683 button->handler->pending = true;
684 }
685
686 array_remove(&pointer->buttons, button, sizeof(*button));
687 break;
688 }
689 }
690 } else {
691 button = wl_array_add(&pointer->buttons, sizeof(*button));
692
693 if (!button) {
694 return;
695 }
696
697 button->press.value = value;
698 button->press.serial = serial;
699 button->handler = NULL;
700
701 wl_list_for_each(handler, &pointer->handlers, link)
702 {
703 if (handler->button &&
704 handler->button(handler, time, button, state)) {
705 button->handler = handler;
706 handler->pending = true;
707 break;
708 }
709 }
710 }
711}
712
713void
714pointer_handle_axis(struct pointer *pointer, uint32_t time,
715 enum wl_pointer_axis axis,
716 enum wl_pointer_axis_source source, wl_fixed_t value,
717 int value120)
718{
719 struct pointer_handler *handler;
720
721 wl_list_for_each(handler, &pointer->handlers, link)
722 {
723 if (handler->axis &&
724 handler->axis(handler, time, axis, source, value, value120)) {
725 handler->pending = true;
726 break;
727 }
728 }
729}
730
731void
732pointer_handle_relative_motion(struct pointer *pointer, uint32_t time,
733 wl_fixed_t dx, wl_fixed_t dy)
734{
735 pointer_handle_absolute_motion(pointer, time, pointer->x + dx,
736 pointer->y + dy);
737}
738
739void
740pointer_handle_absolute_motion(struct pointer *pointer, uint32_t time,
741 wl_fixed_t x, wl_fixed_t y)
742{
743 struct pointer_handler *handler;
744
745 clip_position(pointer, x, y);
746
747 wl_list_for_each(handler, &pointer->handlers, link)
748 {
749 if (handler->motion &&
750 handler->motion(handler, time, pointer->x, pointer->y)) {
751 handler->pending = true;
752 break;
753 }
754 }
755
756 update_cursor(pointer);
757}
758
759void
760pointer_handle_frame(struct pointer *pointer)
761{
762 struct pointer_handler *handler;
763
764 wl_list_for_each(handler, &pointer->handlers, link)
765 {
766 if (handler->pending && handler->frame) {
767 handler->frame(handler);
768 handler->pending = false;
769 }
770 }
771
772 update_cursor(pointer);
773}