blob: 93329c3b7ec9c8cf906b8a00d52c35a172f83fc8 [file] [log] [blame]
Neil Robertsf428d252013-09-19 17:32:01 +01001/*
2 * Copyright © 2011 Benjamin Franzke
3 * Copyright © 2010, 2013 Intel Corporation
Marius Vladeb53d7c2021-08-13 23:12:08 +03004 * Copyright © 2021 Collabora, Ltd.
Neil Robertsf428d252013-09-19 17:32:01 +01005 *
Bryce Harrington1f6b0d12015-06-10 22:48:59 -07006 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
Neil Robertsf428d252013-09-19 17:32:01 +010012 *
Bryce Harrington1f6b0d12015-06-10 22:48:59 -070013 * The above copyright notice and this permission notice (including the next
14 * paragraph) shall be included in all copies or substantial portions of the
15 * Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23 * DEALINGS IN THE SOFTWARE.
Neil Robertsf428d252013-09-19 17:32:01 +010024 */
25
Bryce Harringtonb4dae9b2016-06-15 18:13:07 -070026#include "config.h"
Neil Robertsf428d252013-09-19 17:32:01 +010027
Jussi Kukkonen649bbce2016-07-19 14:16:27 +030028#include <stdint.h>
Neil Robertsf428d252013-09-19 17:32:01 +010029#include <stdio.h>
30#include <stdlib.h>
31#include <errno.h>
32#include <string.h>
33#include <stdbool.h>
34#include <assert.h>
35#include <unistd.h>
36#include <sys/mman.h>
37#include <signal.h>
38#include <time.h>
Luca Weiss923a1e92019-04-14 10:38:25 +000039#include <poll.h>
Neil Robertsf428d252013-09-19 17:32:01 +010040#include <float.h>
Stefan Schmidt639fd862013-09-23 11:25:29 +010041#include <math.h>
Neil Robertsf428d252013-09-19 17:32:01 +010042
43#include <wayland-client.h>
Jon Cruz4678bab2015-06-15 15:37:07 -070044#include "shared/os-compatibility.h"
Bryce Harringtone99e4bf2016-03-16 14:15:18 -070045#include "shared/xalloc.h"
Pekka Paalanenecbdcfd2019-04-04 14:46:00 +030046#include <libweston/zalloc.h>
Neil Robertsf428d252013-09-19 17:32:01 +010047
Marius Vladeb53d7c2021-08-13 23:12:08 +030048#include "xdg-shell-client-protocol.h"
49
50static int running = 1;
51
Neil Robertsf428d252013-09-19 17:32:01 +010052struct device {
53 enum { KEYBOARD, POINTER } type;
54
55 int start_time;
56 int end_time;
57 struct wl_list link;
58
59 union {
60 struct wl_keyboard *keyboard;
61 struct wl_pointer *pointer;
62 } p;
63};
64
65struct display {
66 struct wl_display *display;
67 struct wl_registry *registry;
68 struct wl_compositor *compositor;
Neil Robertsf428d252013-09-19 17:32:01 +010069 struct wl_seat *seat;
70 struct wl_shm *shm;
Marius Vladeb53d7c2021-08-13 23:12:08 +030071 struct xdg_wm_base *wm_base;
Neil Robertsf428d252013-09-19 17:32:01 +010072 uint32_t formats;
73 struct wl_list devices;
74};
75
76struct window {
77 struct display *display;
78 int width, height;
79 struct wl_surface *surface;
Marius Vladeb53d7c2021-08-13 23:12:08 +030080 struct xdg_toplevel *xdg_toplevel;
81 struct xdg_surface *xdg_surface;
82 bool wait_for_configure;
Neil Robertsf428d252013-09-19 17:32:01 +010083};
84
85static void
86buffer_release(void *data, struct wl_buffer *buffer)
87{
88 wl_buffer_destroy(buffer);
89}
90
91static const struct wl_buffer_listener buffer_listener = {
92 buffer_release
93};
94
95static int
96attach_buffer(struct window *window, int width, int height)
97{
98 struct wl_shm_pool *pool;
99 struct wl_buffer *buffer;
100 int fd, size, stride;
101
102 stride = width * 4;
103 size = stride * height;
104
105 fd = os_create_anonymous_file(size);
106 if (fd < 0) {
Antonio Borneo39578632019-04-26 23:57:31 +0200107 fprintf(stderr, "creating a buffer file for %d B failed: %s\n",
108 size, strerror(errno));
Neil Robertsf428d252013-09-19 17:32:01 +0100109 return -1;
110 }
111
112 pool = wl_shm_create_pool(window->display->shm, fd, size);
113 buffer = wl_shm_pool_create_buffer(pool, 0,
114 width, height,
115 stride,
116 WL_SHM_FORMAT_XRGB8888);
117 wl_surface_attach(window->surface, buffer, 0, 0);
118 wl_buffer_add_listener(buffer, &buffer_listener, buffer);
119 wl_shm_pool_destroy(pool);
120 close(fd);
121
122 return 0;
123}
124
125static void
Marius Vladeb53d7c2021-08-13 23:12:08 +0300126handle_xdg_surface_configure(void *data, struct xdg_surface *surface,
127 uint32_t serial)
Neil Robertsf428d252013-09-19 17:32:01 +0100128{
Marius Vladeb53d7c2021-08-13 23:12:08 +0300129 struct window *window = data;
130
131 xdg_surface_ack_configure(surface, serial);
132
133 if (window->wait_for_configure) {
134
135 attach_buffer(window, window->width, window->height);
136 wl_surface_damage(window->surface, 0, 0, window->width, window->height);
137 wl_surface_commit(window->surface);
138
139 window->wait_for_configure = false;
140 }
Neil Robertsf428d252013-09-19 17:32:01 +0100141}
142
Marius Vladeb53d7c2021-08-13 23:12:08 +0300143static const struct xdg_surface_listener xdg_surface_listener = {
144 handle_xdg_surface_configure,
145};
146
Neil Robertsf428d252013-09-19 17:32:01 +0100147static void
Marius Vladeb53d7c2021-08-13 23:12:08 +0300148xdg_wm_base_ping(void *data, struct xdg_wm_base *shell, uint32_t serial)
149{
150 xdg_wm_base_pong(shell, serial);
151}
152
153static const struct xdg_wm_base_listener wm_base_listener = {
154 xdg_wm_base_ping,
155};
156
157
158static void
159handle_toplevel_configure(void *data, struct xdg_toplevel *xdg_toplevel,
160 int32_t width, int32_t height,
161 struct wl_array *state)
Neil Robertsf428d252013-09-19 17:32:01 +0100162{
163}
164
165static void
Marius Vladeb53d7c2021-08-13 23:12:08 +0300166handle_toplevel_close(void *data, struct xdg_toplevel *xdg_toplevel)
Neil Robertsf428d252013-09-19 17:32:01 +0100167{
Marius Vladeb53d7c2021-08-13 23:12:08 +0300168 running = 0;
Neil Robertsf428d252013-09-19 17:32:01 +0100169}
170
Marius Vladeb53d7c2021-08-13 23:12:08 +0300171static const struct xdg_toplevel_listener xdg_toplevel_listener = {
172 handle_toplevel_configure,
173 handle_toplevel_close,
Neil Robertsf428d252013-09-19 17:32:01 +0100174};
175
176static struct window *
177create_window(struct display *display, int width, int height)
178{
179 struct window *window;
180
Kristian Høgsbergb24d5902013-10-09 13:09:51 -0700181 window = xzalloc(sizeof *window);
Neil Robertsf428d252013-09-19 17:32:01 +0100182 window->display = display;
183 window->width = width;
184 window->height = height;
185 window->surface = wl_compositor_create_surface(display->compositor);
Neil Robertsf428d252013-09-19 17:32:01 +0100186
Marius Vladeb53d7c2021-08-13 23:12:08 +0300187 window->xdg_surface =
188 xdg_wm_base_get_xdg_surface(display->wm_base, window->surface);
189 assert(window->xdg_surface);
Neil Robertsf428d252013-09-19 17:32:01 +0100190
Marius Vladeb53d7c2021-08-13 23:12:08 +0300191 xdg_surface_add_listener(window->xdg_surface, &xdg_surface_listener, window);
Neil Robertsf428d252013-09-19 17:32:01 +0100192
Marius Vladeb53d7c2021-08-13 23:12:08 +0300193 window->xdg_toplevel = xdg_surface_get_toplevel(window->xdg_surface);
194 assert(window->xdg_toplevel);
195 xdg_toplevel_add_listener(window->xdg_toplevel,
196 &xdg_toplevel_listener, window);
197 xdg_toplevel_set_title(window->xdg_toplevel, "multi-resource");
198 window->wait_for_configure = true;
Neil Robertsf428d252013-09-19 17:32:01 +0100199 wl_surface_commit(window->surface);
200
201 return window;
202}
203
204static void
205destroy_window(struct window *window)
206{
Marius Vladeb53d7c2021-08-13 23:12:08 +0300207 if (window->xdg_surface)
208 xdg_surface_destroy(window->xdg_surface);
209 if (window->xdg_toplevel)
210 xdg_toplevel_destroy(window->xdg_toplevel);
211
Neil Robertsf428d252013-09-19 17:32:01 +0100212 wl_surface_destroy(window->surface);
213 free(window);
214}
215
216static void
217shm_format(void *data, struct wl_shm *wl_shm, uint32_t format)
218{
219 struct display *d = data;
220
221 d->formats |= (1 << format);
222}
223
224struct wl_shm_listener shm_listener = {
225 shm_format
226};
227
228static void
229registry_handle_global(void *data, struct wl_registry *registry,
230 uint32_t id, const char *interface, uint32_t version)
231{
232 struct display *d = data;
233
234 if (strcmp(interface, "wl_compositor") == 0) {
235 d->compositor =
236 wl_registry_bind(registry,
237 id, &wl_compositor_interface, 1);
Marius Vladeb53d7c2021-08-13 23:12:08 +0300238 } else if (strcmp(interface, "xdg_wm_base") == 0) {
239 d->wm_base = wl_registry_bind(registry,
240 id, &xdg_wm_base_interface, 1);
241 xdg_wm_base_add_listener(d->wm_base, &wm_base_listener, d);
Neil Robertsf428d252013-09-19 17:32:01 +0100242 } else if (strcmp(interface, "wl_shm") == 0) {
243 d->shm = wl_registry_bind(registry,
244 id, &wl_shm_interface, 1);
245 wl_shm_add_listener(d->shm, &shm_listener, d);
246 } else if (strcmp(interface, "wl_seat") == 0 &&
247 d->seat == NULL) {
248 d->seat = wl_registry_bind(registry,
249 id, &wl_seat_interface, 3);
250 }
251}
252
253static void
254registry_handle_global_remove(void *data, struct wl_registry *registry,
255 uint32_t name)
256{
257}
258
259static const struct wl_registry_listener registry_listener = {
260 registry_handle_global,
261 registry_handle_global_remove
262};
263
264static struct display *
265create_display(void)
266{
267 struct display *display;
268
Kristian Høgsbergb24d5902013-10-09 13:09:51 -0700269 display = xzalloc(sizeof *display);
Neil Robertsf428d252013-09-19 17:32:01 +0100270 display->display = wl_display_connect(NULL);
271 assert(display->display);
272
273 display->formats = 0;
274 display->registry = wl_display_get_registry(display->display);
275 wl_registry_add_listener(display->registry,
276 &registry_listener, display);
277 wl_display_roundtrip(display->display);
278 if (display->shm == NULL) {
279 fprintf(stderr, "No wl_shm global\n");
280 exit(1);
281 }
282
283 wl_display_roundtrip(display->display);
284
285 if (!(display->formats & (1 << WL_SHM_FORMAT_XRGB8888))) {
286 fprintf(stderr, "WL_SHM_FORMAT_XRGB32 not available\n");
287 exit(1);
288 }
289
Marius Vladeb53d7c2021-08-13 23:12:08 +0300290 if (!display->wm_base) {
291 fprintf(stderr, "xdg-shell required!\n");
292 exit(1);
293 }
294
Neil Robertsf428d252013-09-19 17:32:01 +0100295 wl_list_init(&display->devices);
296
297 return display;
298}
299
300static void
301pointer_handle_enter(void *data, struct wl_pointer *pointer,
302 uint32_t serial, struct wl_surface *surface,
303 wl_fixed_t sx_w, wl_fixed_t sy_w)
304{
305}
306
307static void
308pointer_handle_leave(void *data, struct wl_pointer *pointer,
309 uint32_t serial, struct wl_surface *surface)
310{
311}
312
313static void
314pointer_handle_motion(void *data, struct wl_pointer *pointer,
315 uint32_t time, wl_fixed_t sx_w, wl_fixed_t sy_w)
316{
317}
318
319static void
320pointer_handle_button(void *data, struct wl_pointer *pointer, uint32_t serial,
321 uint32_t time, uint32_t button, uint32_t state_w)
322{
323}
324
325static void
326pointer_handle_axis(void *data, struct wl_pointer *pointer,
327 uint32_t time, uint32_t axis, wl_fixed_t value)
328{
329}
330
331static const struct wl_pointer_listener pointer_listener = {
332 pointer_handle_enter,
333 pointer_handle_leave,
334 pointer_handle_motion,
335 pointer_handle_button,
336 pointer_handle_axis,
337};
338
339static void
340keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard,
341 uint32_t format, int fd, uint32_t size)
342{
Antonio Borneo40712252019-04-29 17:54:10 +0200343 /* Just so we don’t leak the keymap fd */
344 close(fd);
Neil Robertsf428d252013-09-19 17:32:01 +0100345}
346
347static void
348keyboard_handle_enter(void *data, struct wl_keyboard *keyboard,
349 uint32_t serial, struct wl_surface *surface,
350 struct wl_array *keys)
351{
352}
353
354static void
355keyboard_handle_leave(void *data, struct wl_keyboard *keyboard,
356 uint32_t serial, struct wl_surface *surface)
357{
358}
359
360static void
361keyboard_handle_key(void *data, struct wl_keyboard *keyboard,
362 uint32_t serial, uint32_t time, uint32_t key,
363 uint32_t state_w)
364{
365}
366
367static void
368keyboard_handle_modifiers(void *data, struct wl_keyboard *keyboard,
369 uint32_t serial, uint32_t mods_depressed,
370 uint32_t mods_latched, uint32_t mods_locked,
371 uint32_t group)
372{
373}
374
375static const struct wl_keyboard_listener keyboard_listener = {
376 keyboard_handle_keymap,
377 keyboard_handle_enter,
378 keyboard_handle_leave,
379 keyboard_handle_key,
380 keyboard_handle_modifiers,
381};
382
383static void
384start_device(struct display *display, struct device *device)
385{
386 if (display->seat == NULL)
387 return;
388
389 switch (device->type) {
390 case KEYBOARD:
391 if (device->p.keyboard == NULL) {
392 device->p.keyboard =
393 wl_seat_get_keyboard(display->seat);
394 wl_keyboard_add_listener(device->p.keyboard,
395 &keyboard_listener,
396 NULL);
397 }
398 break;
399 case POINTER:
400 if (device->p.pointer == NULL) {
401 device->p.pointer =
402 wl_seat_get_pointer(display->seat);
403 wl_pointer_add_listener(device->p.pointer,
404 &pointer_listener,
405 NULL);
406 }
407 break;
408 }
409}
410
411static void
412destroy_device(struct device *device)
413{
414 switch (device->type) {
415 case KEYBOARD:
416 if (device->p.keyboard)
417 wl_keyboard_release(device->p.keyboard);
418 break;
419 case POINTER:
420 if (device->p.pointer)
421 wl_pointer_release(device->p.pointer);
422 break;
423 }
424
425 wl_list_remove(&device->link);
426 free(device);
427}
428
429static void
430destroy_devices(struct display *display)
431{
432 struct device *device, *tmp;
433
434 wl_list_for_each_safe(device, tmp, &display->devices, link)
435 destroy_device(device);
436}
437
438static void
439destroy_display(struct display *display)
440{
441 destroy_devices(display);
442
443 if (display->shm)
444 wl_shm_destroy(display->shm);
445
Marius Vladeb53d7c2021-08-13 23:12:08 +0300446 if (display->wm_base)
447 xdg_wm_base_destroy(display->wm_base);
Neil Robertsf428d252013-09-19 17:32:01 +0100448
449 if (display->seat)
450 wl_seat_destroy(display->seat);
451
452 if (display->compositor)
453 wl_compositor_destroy(display->compositor);
454
455 wl_registry_destroy(display->registry);
456 wl_display_flush(display->display);
457 wl_display_disconnect(display->display);
458 free(display);
459}
460
Neil Robertsf428d252013-09-19 17:32:01 +0100461
462static void
463signal_int(int signum)
464{
465 running = 0;
466}
467
468static int
469create_device(struct display *display, const char *time_desc, int type)
470{
471 int start_time;
472 int end_time = -1;
473 char *tail;
474 struct device *device;
475
476 if (time_desc == NULL) {
477 fprintf(stderr, "missing time description\n");
478 return -1;
479 }
480
481 errno = 0;
482 start_time = strtoul(time_desc, &tail, 10);
Bryce Harrington9c09fe72016-07-08 18:05:56 -0700483 if (errno || tail == time_desc)
Neil Robertsf428d252013-09-19 17:32:01 +0100484 goto error;
485
486 if (*tail == ':') {
Bryce Harrington9c09fe72016-07-08 18:05:56 -0700487 time_desc = tail + 1;
488 end_time = strtoul(time_desc, &tail, 10);
489 if (errno || tail == time_desc || *tail != '\0')
Neil Robertsf428d252013-09-19 17:32:01 +0100490 goto error;
491 } else if (*tail != '\0') {
492 goto error;
493 }
494
Kristian Høgsbergb24d5902013-10-09 13:09:51 -0700495 device = xzalloc(sizeof *device);
Neil Robertsf428d252013-09-19 17:32:01 +0100496 device->type = type;
497 device->start_time = start_time;
498 device->end_time = end_time;
499 wl_list_insert(&display->devices, &device->link);
500
501 return 0;
502
503error:
504 fprintf(stderr, "invalid time description\n");
505 return -1;
506}
507
508static struct timespec begin_time;
509
510static void
511reset_timer(void)
512{
513 clock_gettime(CLOCK_MONOTONIC, &begin_time);
514}
515
516static double
517read_timer(void)
518{
519 struct timespec t;
520
521 clock_gettime(CLOCK_MONOTONIC, &t);
522 return (double)(t.tv_sec - begin_time.tv_sec) +
523 1e-9 * (t.tv_nsec - begin_time.tv_nsec);
524}
525
526static void
527main_loop(struct display *display)
528{
529 reset_timer();
530
531 while (running) {
532 struct device *device, *tmp;
533 struct pollfd fds[1];
534 double sleep_time = DBL_MAX;
535 double now;
536
537 if (wl_display_dispatch_pending(display->display) == -1)
538 break;
539 if (wl_display_flush(display->display) == -1)
540 break;
541
542 now = read_timer();
543
544 wl_list_for_each(device, &display->devices, link) {
545 double next_time = device->start_time - now;
546 if (next_time < 0.0) {
547 sleep_time = 0.0;
548 break;
549 } else if (next_time < sleep_time) {
550 sleep_time = next_time;
551 }
552 next_time = device->end_time - now;
553 if (next_time < 0.0) {
554 sleep_time = 0.0;
555 break;
556 } else if (next_time < sleep_time) {
557 sleep_time = next_time;
558 }
559 }
560
561 fds[0].fd = wl_display_get_fd(display->display);
562 fds[0].events = POLLIN;
563 fds[0].revents = 0;
564
565 poll(fds,
566 sizeof fds / sizeof fds[0],
567 sleep_time == DBL_MAX ? -1 : ceil(sleep_time * 1000.0));
568
569 if (fds[0].revents &&
570 wl_display_dispatch(display->display) == -1)
571 break;
572
573 now = read_timer();
574
575 wl_list_for_each_safe(device, tmp, &display->devices, link) {
576 if (device->start_time <= now)
577 start_device(display, device);
578 if (device->end_time >= 0 && device->end_time <= now)
579 destroy_device(device);
580 }
581 }
582}
583
584int
585main(int argc, char **argv)
586{
587 struct sigaction sigint;
588 struct display *display;
589 struct window *window;
590 int i;
591
592 display = create_display();
593 window = create_window(display, 250, 250);
Neil Robertsf428d252013-09-19 17:32:01 +0100594
595 for (i = 1; i < argc; i++) {
596 if (!strncmp(argv[i], "-p", 2)) {
597 char *arg;
598 if (argv[i][2]) {
599 arg = argv[i] + 2;
600 } else {
601 arg = argv[i + 1];
602 i++;
603 }
604 if (create_device(display, arg, POINTER) == -1)
605 return 1;
606 } else if (!strncmp(argv[i], "-k", 2)) {
607 char *arg;
608 if (argv[i][2]) {
609 arg = argv[i] + 2;
610 } else {
611 arg = argv[i + 1];
612 i++;
613 }
614 if (create_device(display, arg, KEYBOARD) == -1)
615 return 1;
616 } else {
617 fprintf(stderr, "unknown argument %s\n", argv[i]);
618 return 1;
619 }
620 }
621
622 sigint.sa_handler = signal_int;
623 sigemptyset(&sigint.sa_mask);
624 sigint.sa_flags = SA_RESETHAND;
625 sigaction(SIGINT, &sigint, NULL);
626
627 main_loop(display);
628
629 fprintf(stderr, "multi-resource exiting\n");
630 destroy_window(window);
631 destroy_display(display);
632
633 return 0;
634}