weston-info.c 47.4 KB
Newer Older
1
2
3
/*
 * Copyright © 2012 Philipp Brüschweiler
 *
4
5
6
7
8
9
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
10
 *
11
12
13
14
15
16
17
18
19
20
21
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
22
23
 */

24
25
26
#include "config.h"

#include <errno.h>
27
#include <stdbool.h>
28
#include <stdint.h>
29
30
31
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
32
#include <time.h>
33
34
#include <assert.h>
#include <ctype.h>
35
#include <unistd.h>
36
37
38

#include <wayland-client.h>

39
#include "shared/helpers.h"
40
#include "shared/os-compatibility.h"
41
#include "shared/xalloc.h"
42
#include <libweston/zalloc.h>
43
#include "presentation-time-client-protocol.h"
44
#include "linux-dmabuf-unstable-v1-client-protocol.h"
45
#include "tablet-unstable-v2-client-protocol.h"
46
#include "xdg-output-unstable-v1-client-protocol.h"
47

48
typedef void (*print_info_t)(void *info);
49
typedef void (*destroy_info_t)(void *info);
50
51
52
53
54
55
56
57
58

struct global_info {
	struct wl_list link;

	uint32_t id;
	uint32_t version;
	char *interface;

	print_info_t print;
59
	destroy_info_t destroy;
60
61
};

62
63
64
65
66
67
68
69
struct output_mode {
	struct wl_list link;

	uint32_t flags;
	int32_t width, height;
	int32_t refresh;
};

70
71
struct output_info {
	struct global_info global;
72
	struct wl_list global_link;
73
74
75

	struct wl_output *output;

Jonny Lamb's avatar
Jonny Lamb committed
76
77
	int32_t version;

78
79
	struct {
		int32_t x, y;
Jonny Lamb's avatar
Jonny Lamb committed
80
		int32_t scale;
81
82
83
84
85
86
87
		int32_t physical_width, physical_height;
		enum wl_output_subpixel subpixel;
		enum wl_output_transform output_transform;
		char *make;
		char *model;
	} geometry;

88
	struct wl_list modes;
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
};

struct shm_format {
	struct wl_list link;

	uint32_t format;
};

struct shm_info {
	struct global_info global;
	struct wl_shm *shm;

	struct wl_list formats;
};

104
105
106
107
108
109
110
111
112
113
114
115
116
117
struct linux_dmabuf_modifier {
	struct wl_list link;

	uint32_t format;
	uint64_t modifier;
};

struct linux_dmabuf_info {
	struct global_info global;
	struct zwp_linux_dmabuf_v1 *dmabuf;

	struct wl_list modifiers;
};

118
119
struct seat_info {
	struct global_info global;
120
	struct wl_list global_link;
121
	struct wl_seat *seat;
122
	struct weston_info *info;
123

Ongy's avatar
Ongy committed
124
	struct wl_keyboard *keyboard;
125
	uint32_t capabilities;
126
	char *name;
127
128
129

	int32_t repeat_rate;
	int32_t repeat_delay;
130
131
};

132
133
134
135
136
137
138
139
140
141
142
143
struct tablet_v2_path {
	struct wl_list link;
	char *path;
};

struct tablet_tool_info {
	struct wl_list link;
	struct zwp_tablet_tool_v2 *tool;

	uint64_t hardware_serial;
	uint64_t hardware_id_wacom;
	enum zwp_tablet_tool_v2_type type;
144

145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
	bool has_tilt;
	bool has_pressure;
	bool has_distance;
	bool has_rotation;
	bool has_slider;
	bool has_wheel;
};

struct tablet_pad_group_info {
	struct wl_list link;
	struct zwp_tablet_pad_group_v2 *group;

	uint32_t modes;
	size_t button_count;
	int *buttons;
	size_t strips;
	size_t rings;
};

struct tablet_pad_info {
	struct wl_list link;
	struct zwp_tablet_pad_v2 *pad;

	uint32_t buttons;
	struct wl_list paths;
	struct wl_list groups;
};

struct tablet_info {
	struct wl_list link;
	struct zwp_tablet_v2 *tablet;

	char *name;
	uint32_t vid, pid;
	struct wl_list paths;
};

struct tablet_seat_info {
	struct wl_list link;

	struct zwp_tablet_seat_v2 *seat;
	struct seat_info *seat_info;

	struct wl_list tablets;
	struct wl_list tools;
	struct wl_list pads;
};

struct tablet_v2_info {
	struct global_info global;
	struct zwp_tablet_manager_v2 *manager;
	struct weston_info *info;

	struct wl_list seats;
};

201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
struct xdg_output_v1_info {
	struct wl_list link;

	struct zxdg_output_v1 *xdg_output;
	struct output_info *output;

	struct {
		int32_t x, y;
		int32_t width, height;
	} logical;

	char *name, *description;
};

struct xdg_output_manager_v1_info {
	struct global_info global;
	struct zxdg_output_manager_v1 *manager;
	struct weston_info *info;

	struct wl_list outputs;
};

223
224
struct presentation_info {
	struct global_info global;
225
	struct wp_presentation *presentation;
226
227
228
229

	clockid_t clk_id;
};

230
231
struct weston_info {
	struct wl_display *display;
232
	struct wl_registry *registry;
233
234

	struct wl_list infos;
235
	bool roundtrip_needed;
236
237
238
239

	/* required for tablet-unstable-v2 */
	struct wl_list seats;
	struct tablet_v2_info *tablet_info;
240
241
242
243

	/* required for xdg-output-unstable-v1 */
	struct wl_list outputs;
	struct xdg_output_manager_v1_info *xdg_output_manager_v1_info;
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
};

static void
print_global_info(void *data)
{
	struct global_info *global = data;

	printf("interface: '%s', version: %u, name: %u\n",
	       global->interface, global->version, global->id);
}

static void
init_global_info(struct weston_info *info,
		 struct global_info *global, uint32_t id,
		 const char *interface, uint32_t version)
{
	global->id = id;
	global->version = version;
262
	global->interface = xstrdup(interface);
263
264
265
266
267
268
269
270

	wl_list_insert(info->infos.prev, &global->link);
}

static void
print_output_info(void *data)
{
	struct output_info *output = data;
271
	struct output_mode *mode;
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
	const char *subpixel_orientation;
	const char *transform;

	print_global_info(data);

	switch (output->geometry.subpixel) {
	case WL_OUTPUT_SUBPIXEL_UNKNOWN:
		subpixel_orientation = "unknown";
		break;
	case WL_OUTPUT_SUBPIXEL_NONE:
		subpixel_orientation = "none";
		break;
	case WL_OUTPUT_SUBPIXEL_HORIZONTAL_RGB:
		subpixel_orientation = "horizontal rgb";
		break;
	case WL_OUTPUT_SUBPIXEL_HORIZONTAL_BGR:
		subpixel_orientation = "horizontal bgr";
		break;
	case WL_OUTPUT_SUBPIXEL_VERTICAL_RGB:
		subpixel_orientation = "vertical rgb";
		break;
	case WL_OUTPUT_SUBPIXEL_VERTICAL_BGR:
		subpixel_orientation = "vertical bgr";
		break;
	default:
		fprintf(stderr, "unknown subpixel orientation %u\n",
			output->geometry.subpixel);
		subpixel_orientation = "unexpected value";
		break;
	}

	switch (output->geometry.output_transform) {
	case WL_OUTPUT_TRANSFORM_NORMAL:
		transform = "normal";
		break;
	case WL_OUTPUT_TRANSFORM_90:
		transform = "90°";
		break;
	case WL_OUTPUT_TRANSFORM_180:
		transform = "180°";
		break;
	case WL_OUTPUT_TRANSFORM_270:
		transform = "270°";
		break;
	case WL_OUTPUT_TRANSFORM_FLIPPED:
		transform = "flipped";
		break;
	case WL_OUTPUT_TRANSFORM_FLIPPED_90:
		transform = "flipped 90°";
		break;
	case WL_OUTPUT_TRANSFORM_FLIPPED_180:
		transform = "flipped 180°";
		break;
	case WL_OUTPUT_TRANSFORM_FLIPPED_270:
		transform = "flipped 270°";
		break;
	default:
		fprintf(stderr, "unknown output transform %u\n",
			output->geometry.output_transform);
		transform = "unexpected value";
		break;
	}

Jonny Lamb's avatar
Jonny Lamb committed
335
	printf("\tx: %d, y: %d,",
336
	       output->geometry.x, output->geometry.y);
Jonny Lamb's avatar
Jonny Lamb committed
337
338
339
340
	if (output->version >= 2)
		printf(" scale: %d,", output->geometry.scale);
	printf("\n");

341
	printf("\tphysical_width: %d mm, physical_height: %d mm,\n",
342
	       output->geometry.physical_width,
343
	       output->geometry.physical_height);
344
345
	printf("\tmake: '%s', model: '%s',\n",
	       output->geometry.make, output->geometry.model);
346
	printf("\tsubpixel_orientation: %s, output_transform: %s,\n",
347
	       subpixel_orientation, transform);
348
349
350
351

	wl_list_for_each(mode, &output->modes, link) {
		printf("\tmode:\n");

352
		printf("\t\twidth: %d px, height: %d px, refresh: %.3f Hz,\n",
353
354
355
356
357
358
359
360
361
362
		       mode->width, mode->height,
		       (float) mode->refresh / 1000);

		printf("\t\tflags:");
		if (mode->flags & WL_OUTPUT_MODE_CURRENT)
			printf(" current");
		if (mode->flags & WL_OUTPUT_MODE_PREFERRED)
			printf(" preferred");
		printf("\n");
	}
363
364
}

365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
static char
bits2graph(uint32_t value, unsigned bitoffset)
{
	int c = (value >> bitoffset) & 0xff;

	if (isgraph(c) || isspace(c))
		return c;

	return '?';
}

static void
fourcc2str(uint32_t format, char *str, int len)
{
	int i;

	assert(len >= 5);

	for (i = 0; i < 4; i++)
		str[i] = bits2graph(format, i * 8);
	str[i] = '\0';
}

388
389
390
static void
print_shm_info(void *data)
{
391
	char str[5];
392
393
394
395
396
397
398
399
	struct shm_info *shm = data;
	struct shm_format *format;

	print_global_info(data);

	printf("\tformats:");

	wl_list_for_each(format, &shm->formats, link)
400
401
402
403
404
405
406
407
408
409
410
		switch (format->format) {
		case WL_SHM_FORMAT_ARGB8888:
			printf(" ARGB8888");
			break;
		case WL_SHM_FORMAT_XRGB8888:
			printf(" XRGB8888");
			break;
		case WL_SHM_FORMAT_RGB565:
			printf(" RGB565");
			break;
		default:
411
412
			fourcc2str(format->format, str, sizeof(str));
			printf(" '%s'(0x%08x)", str, format->format);
413
414
			break;
		}
415
416
417
418

	printf("\n");
}

419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
static void
print_linux_dmabuf_info(void *data)
{
	char str[5];
	struct linux_dmabuf_info *dmabuf = data;
	struct linux_dmabuf_modifier *modifier;

	print_global_info(data);

	printf("\tformats:");

	wl_list_for_each(modifier, &dmabuf->modifiers, link) {
		fourcc2str(modifier->format, str, sizeof(str));
		printf("\n\t'%s'(0x%08x), modifier: 0x%016"PRIx64, str, modifier->format, modifier->modifier);
	}

	printf("\n");
}

438
439
440
441
442
443
444
static void
print_seat_info(void *data)
{
	struct seat_info *seat = data;

	print_global_info(data);

445
	printf("\tname: %s\n", seat->name);
446
447
448
449
450
451
452
453
454
455
	printf("\tcapabilities:");

	if (seat->capabilities & WL_SEAT_CAPABILITY_POINTER)
		printf(" pointer");
	if (seat->capabilities & WL_SEAT_CAPABILITY_KEYBOARD)
		printf(" keyboard");
	if (seat->capabilities & WL_SEAT_CAPABILITY_TOUCH)
		printf(" touch");

	printf("\n");
456
457
458
459
460
461
462
463
464
465
466

	if (seat->repeat_rate > 0)
		printf("\tkeyboard repeat rate: %d\n", seat->repeat_rate);
	if (seat->repeat_delay > 0)
		printf("\tkeyboard repeat delay: %d\n", seat->repeat_delay);
}

static void
keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard,
		       uint32_t format, int fd, uint32_t size)
{
467
468
	/* Just so we don’t leak the keymap fd */
	close(fd);
469
470
471
472
473
474
475
476
477
478
479
480
481
}

static void
keyboard_handle_enter(void *data, struct wl_keyboard *keyboard,
		      uint32_t serial, struct wl_surface *surface,
		      struct wl_array *keys)
{
}

static void
keyboard_handle_leave(void *data, struct wl_keyboard *keyboard,
		      uint32_t serial, struct wl_surface *surface)
{
482
483
}

484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
static void
keyboard_handle_key(void *data, struct wl_keyboard *keyboard,
		    uint32_t serial, uint32_t time, uint32_t key,
		    uint32_t state)
{
}

static void
keyboard_handle_modifiers(void *data, struct wl_keyboard *keyboard,
			  uint32_t serial, uint32_t mods_depressed,
			  uint32_t mods_latched, uint32_t mods_locked,
			  uint32_t group)
{
}

static void
keyboard_handle_repeat_info(void *data, struct wl_keyboard *keyboard,
			    int32_t rate, int32_t delay)
{
	struct seat_info *seat = data;

	seat->repeat_rate = rate;
	seat->repeat_delay = delay;
}

static const struct wl_keyboard_listener keyboard_listener = {
	keyboard_handle_keymap,
	keyboard_handle_enter,
	keyboard_handle_leave,
	keyboard_handle_key,
	keyboard_handle_modifiers,
	keyboard_handle_repeat_info,
};

518
519
520
521
522
static void
seat_handle_capabilities(void *data, struct wl_seat *wl_seat,
			 enum wl_seat_capability caps)
{
	struct seat_info *seat = data;
523

524
	seat->capabilities = caps;
525
526
527
528
529
530
531
532

	/* we want listen for repeat_info from wl_keyboard, but only
	 * do so if the seat info is >= 4 and if we actually have a
	 * keyboard */
	if (seat->global.version < 4)
		return;

	if (caps & WL_SEAT_CAPABILITY_KEYBOARD) {
Ongy's avatar
Ongy committed
533
534
		seat->keyboard = wl_seat_get_keyboard(seat->seat);
		wl_keyboard_add_listener(seat->keyboard, &keyboard_listener,
535
536
537
538
					 seat);

		seat->info->roundtrip_needed = true;
	}
539
540
}

541
542
543
544
545
static void
seat_handle_name(void *data, struct wl_seat *wl_seat,
		 const char *name)
{
	struct seat_info *seat = data;
546
	seat->name = xstrdup(name);
547
548
}

549
550
static const struct wl_seat_listener seat_listener = {
	seat_handle_capabilities,
551
	seat_handle_name,
552
553
};

554
555
556
557
558
559
560
561
562
static void
destroy_seat_info(void *data)
{
	struct seat_info *seat = data;

	wl_seat_destroy(seat->seat);

	if (seat->name != NULL)
		free(seat->name);
563

Ongy's avatar
Ongy committed
564
565
566
	if (seat->keyboard)
		wl_keyboard_destroy(seat->keyboard);

567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
	wl_list_remove(&seat->global_link);
}

static const char *
tablet_tool_type_to_str(enum zwp_tablet_tool_v2_type type)
{
	switch (type) {
	case ZWP_TABLET_TOOL_V2_TYPE_PEN:
		return "pen";
	case ZWP_TABLET_TOOL_V2_TYPE_ERASER:
		return "eraser";
	case ZWP_TABLET_TOOL_V2_TYPE_BRUSH:
		return "brush";
	case ZWP_TABLET_TOOL_V2_TYPE_PENCIL:
		return "pencil";
	case ZWP_TABLET_TOOL_V2_TYPE_AIRBRUSH:
		return "airbrush";
	case ZWP_TABLET_TOOL_V2_TYPE_FINGER:
		return "finger";
	case ZWP_TABLET_TOOL_V2_TYPE_MOUSE:
		return "mouse";
	case ZWP_TABLET_TOOL_V2_TYPE_LENS:
		return "lens";
	}

	return "Unknown type";
}

static void
print_tablet_tool_info(const struct tablet_tool_info *info)
{
	printf("\t\ttablet_tool: %s\n", tablet_tool_type_to_str(info->type));
	if (info->hardware_serial) {
600
		printf("\t\t\thardware serial: %" PRIx64 "\n", info->hardware_serial);
601
602
	}
	if (info->hardware_id_wacom) {
603
		printf("\t\t\thardware wacom: %" PRIx64 "\n", info->hardware_id_wacom);
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
	}

	printf("\t\t\tcapabilities:");

	if (info->has_tilt) {
		printf(" tilt");
	}
	if (info->has_pressure) {
		printf(" pressure");
	}
	if (info->has_distance) {
		printf(" distance");
	}
	if (info->has_rotation) {
		printf(" rotation");
	}
	if (info->has_slider) {
		printf(" slider");
	}
	if (info->has_wheel) {
		printf(" wheel");
	}
	printf("\n");
}

static void
destroy_tablet_tool_info(struct tablet_tool_info *info)
{
	wl_list_remove(&info->link);
	zwp_tablet_tool_v2_destroy(info->tool);
	free(info);
}

static void
print_tablet_pad_group_info(const struct tablet_pad_group_info *info)
{
	size_t i;
	printf("\t\t\tgroup:\n");
	printf("\t\t\t\tmodes: %u\n", info->modes);
	printf("\t\t\t\tstrips: %zu\n", info->strips);
	printf("\t\t\t\trings: %zu\n", info->rings);
	printf("\t\t\t\tbuttons:");

	for (i = 0; i < info->button_count; ++i) {
		printf(" %d", info->buttons[i]);
	}

	printf("\n");
}

static void
destroy_tablet_pad_group_info(struct tablet_pad_group_info *info)
{
	wl_list_remove(&info->link);
	zwp_tablet_pad_group_v2_destroy(info->group);

	if (info->buttons) {
		free(info->buttons);
	}
	free(info);
}

static void
print_tablet_pad_info(const struct tablet_pad_info *info)
{
	const struct tablet_v2_path *path;
	const struct tablet_pad_group_info *group;

	printf("\t\tpad:\n");
	printf("\t\t\tbuttons: %u\n", info->buttons);

	wl_list_for_each(path, &info->paths, link) {
		printf("\t\t\tpath: %s\n", path->path);
	}

	wl_list_for_each(group, &info->groups, link) {
		print_tablet_pad_group_info(group);
	}
}

static void
destroy_tablet_pad_info(struct tablet_pad_info *info)
{
	struct tablet_v2_path *path;
	struct tablet_v2_path *tmp_path;
	struct tablet_pad_group_info *group;
	struct tablet_pad_group_info *tmp_group;

	wl_list_remove(&info->link);
	zwp_tablet_pad_v2_destroy(info->pad);

	wl_list_for_each_safe(path, tmp_path, &info->paths, link) {
		wl_list_remove(&path->link);
		free(path->path);
		free(path);
	}

	wl_list_for_each_safe(group, tmp_group, &info->groups, link) {
		destroy_tablet_pad_group_info(group);
	}

	free(info);
}

static void
print_tablet_info(const struct tablet_info *info)
{
	const struct tablet_v2_path *path;

	printf("\t\ttablet: %s\n", info->name);
	printf("\t\t\tvendor: %u\n", info->vid);
	printf("\t\t\tproduct: %u\n", info->pid);

	wl_list_for_each(path, &info->paths, link) {
		printf("\t\t\tpath: %s\n", path->path);
	}
}

static void
destroy_tablet_info(struct tablet_info *info)
{
	struct tablet_v2_path *path;
	struct tablet_v2_path *tmp;

	wl_list_remove(&info->link);
	zwp_tablet_v2_destroy(info->tablet);

	if (info->name) {
		free(info->name);
	}

	wl_list_for_each_safe(path, tmp, &info->paths, link) {
		wl_list_remove(&path->link);
		free(path->path);
		free(path);
	}

	free(info);
}

static void
print_tablet_seat_info(const struct tablet_seat_info *info)
{
	const struct tablet_info *tablet;
	const struct tablet_pad_info *pad;
	const struct tablet_tool_info *tool;

	printf("\ttablet_seat: %s\n", info->seat_info->name);

	wl_list_for_each(tablet, &info->tablets, link) {
		print_tablet_info(tablet);
	}

	wl_list_for_each(pad, &info->pads, link) {
		print_tablet_pad_info(pad);
	}

	wl_list_for_each(tool, &info->tools, link) {
		print_tablet_tool_info(tool);
	}
}

static void
destroy_tablet_seat_info(struct tablet_seat_info *info)
{
	struct tablet_info *tablet;
	struct tablet_info *tmp_tablet;
	struct tablet_pad_info *pad;
	struct tablet_pad_info *tmp_pad;
	struct tablet_tool_info *tool;
	struct tablet_tool_info *tmp_tool;

	wl_list_remove(&info->link);
	zwp_tablet_seat_v2_destroy(info->seat);

	wl_list_for_each_safe(tablet, tmp_tablet, &info->tablets, link) {
		destroy_tablet_info(tablet);
	}

	wl_list_for_each_safe(pad, tmp_pad, &info->pads, link) {
		destroy_tablet_pad_info(pad);
	}

	wl_list_for_each_safe(tool, tmp_tool, &info->tools, link) {
		destroy_tablet_tool_info(tool);
	}

	free(info);
}

static void
print_tablet_v2_info(void *data)
{
	struct tablet_v2_info *info = data;
	struct tablet_seat_info *seat;
	print_global_info(data);

	wl_list_for_each(seat, &info->seats, link) {
		/* Skip tablet_seats without a tablet, they are irrelevant */
		if (wl_list_empty(&seat->pads) &&
		    wl_list_empty(&seat->tablets) &&
		    wl_list_empty(&seat->tools)) {
			continue;
		}

		print_tablet_seat_info(seat);
	}
}

static void
destroy_tablet_v2_info(void *data)
{
	struct tablet_v2_info *info = data;
	struct tablet_seat_info *seat;
	struct tablet_seat_info *tmp;

	zwp_tablet_manager_v2_destroy(info->manager);

	wl_list_for_each_safe(seat, tmp, &info->seats, link) {
		destroy_tablet_seat_info(seat);
	}
}

static void
handle_tablet_v2_tablet_tool_done(void *data, struct zwp_tablet_tool_v2 *tool)
{
	/* don't bother waiting for this; there's no good reason a
	 * compositor will wait more than one roundtrip before sending
	 * these initial events. */
}

static void
handle_tablet_v2_tablet_tool_removed(void *data, struct zwp_tablet_tool_v2 *tool)
{
	/* don't bother waiting for this; we never make any request either way. */
}

static void
handle_tablet_v2_tablet_tool_type(void *data, struct zwp_tablet_tool_v2 *tool,
                                  uint32_t tool_type)
{
	struct tablet_tool_info *info = data;
	info->type = tool_type;
}

static void
handle_tablet_v2_tablet_tool_hardware_serial(void *data,
                                             struct zwp_tablet_tool_v2 *tool,
                                             uint32_t serial_hi,
                                             uint32_t serial_lo)
{
	struct tablet_tool_info *info = data;

	info->hardware_serial = ((uint64_t) serial_hi) << 32 |
		(uint64_t) serial_lo;
}

static void
handle_tablet_v2_tablet_tool_hardware_id_wacom(void *data,
                                               struct zwp_tablet_tool_v2 *tool,
                                               uint32_t id_hi, uint32_t id_lo)
{
	struct tablet_tool_info *info = data;

	info->hardware_id_wacom = ((uint64_t) id_hi) << 32 | (uint64_t) id_lo;
}

static void
handle_tablet_v2_tablet_tool_capability(void *data,
                                        struct zwp_tablet_tool_v2 *tool,
                                        uint32_t capability)
{
	struct tablet_tool_info *info = data;
	enum zwp_tablet_tool_v2_capability cap = capability;
878

879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
	switch(cap) {
	case ZWP_TABLET_TOOL_V2_CAPABILITY_TILT:
		info->has_tilt = true;
		break;
	case ZWP_TABLET_TOOL_V2_CAPABILITY_PRESSURE:
		info->has_pressure = true;
		break;
	case ZWP_TABLET_TOOL_V2_CAPABILITY_DISTANCE:
		info->has_distance = true;
		break;
	case ZWP_TABLET_TOOL_V2_CAPABILITY_ROTATION:
		info->has_rotation = true;
		break;
	case ZWP_TABLET_TOOL_V2_CAPABILITY_SLIDER:
		info->has_slider = true;
		break;
	case ZWP_TABLET_TOOL_V2_CAPABILITY_WHEEL:
		info->has_wheel = true;
		break;
	}
}

static void
handle_tablet_v2_tablet_tool_proximity_in(void *data,
                                 struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2,
                                 uint32_t serial, struct zwp_tablet_v2 *tablet,
                                 struct wl_surface *surface)
{

}

static void
handle_tablet_v2_tablet_tool_proximity_out(void *data,
                                 struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2)
{

}

static void
handle_tablet_v2_tablet_tool_down(void *data,
                                 struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2,
                                 uint32_t serial)
{

}

static void
handle_tablet_v2_tablet_tool_up(void *data,
                                struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2)
{

}


static void
handle_tablet_v2_tablet_tool_motion(void *data,
                                 struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2,
                                 wl_fixed_t x,
                                 wl_fixed_t y)
{

}

static void
handle_tablet_v2_tablet_tool_pressure(void *data,
                                 struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2,
                                 uint32_t pressure)
{

}

static void
handle_tablet_v2_tablet_tool_distance(void *data,
                                 struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2,
                                 uint32_t distance)
{

}

static void
handle_tablet_v2_tablet_tool_tilt(void *data,
                                 struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2,
                                 wl_fixed_t tilt_x,
                                 wl_fixed_t tilt_y)
{

}

static void
handle_tablet_v2_tablet_tool_rotation(void *data,
                                 struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2,
                                 wl_fixed_t degrees)
{

}

static void
handle_tablet_v2_tablet_tool_slider(void *data,
                                 struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2,
                                 int32_t position)
{

}

static void
handle_tablet_v2_tablet_tool_wheel(void *data,
                                 struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2,
                                 wl_fixed_t degrees,
                                 int32_t clicks)
{

}

static void
handle_tablet_v2_tablet_tool_button(void *data,
                                 struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2,
                                 uint32_t serial,
                                 uint32_t button,
                                 uint32_t state)
{

}