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