weston/clients/weston-info.c
Markus Ongyerth 995bf51b2c weston-info: destroy wl_keyboard
Fixes a memory leak by calling wl_keyboard_destroy on any keyboard
that was used to listen for events.

Signed-off-by: Markus Ongyerth <wl@ongy.net>
Acked-by: Pekka Paalanen <pekka.paalanen@collabora.co.uk>
Reviewed-by: Peter Hutterer <peter.hutterer@who-t.net>
2018-05-29 13:34:46 +03:00

1705 lines
42 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright © 2012 Philipp Brüschweiler
*
* 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:
*
* 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.
*/
#include "config.h"
#include <errno.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <assert.h>
#include <ctype.h>
#include <wayland-client.h>
#include "shared/helpers.h"
#include "shared/os-compatibility.h"
#include "shared/xalloc.h"
#include "shared/zalloc.h"
#include "presentation-time-client-protocol.h"
#include "linux-dmabuf-unstable-v1-client-protocol.h"
#include "tablet-unstable-v2-client-protocol.h"
typedef void (*print_info_t)(void *info);
typedef void (*destroy_info_t)(void *info);
struct global_info {
struct wl_list link;
uint32_t id;
uint32_t version;
char *interface;
print_info_t print;
destroy_info_t destroy;
};
struct output_mode {
struct wl_list link;
uint32_t flags;
int32_t width, height;
int32_t refresh;
};
struct output_info {
struct global_info global;
struct wl_output *output;
int32_t version;
struct {
int32_t x, y;
int32_t scale;
int32_t physical_width, physical_height;
enum wl_output_subpixel subpixel;
enum wl_output_transform output_transform;
char *make;
char *model;
} geometry;
struct wl_list modes;
};
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;
};
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;
};
struct seat_info {
struct global_info global;
struct wl_list global_link;
struct wl_seat *seat;
struct weston_info *info;
struct wl_keyboard *keyboard;
uint32_t capabilities;
char *name;
int32_t repeat_rate;
int32_t repeat_delay;
};
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;
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;
};
struct presentation_info {
struct global_info global;
struct wp_presentation *presentation;
clockid_t clk_id;
};
struct weston_info {
struct wl_display *display;
struct wl_registry *registry;
struct wl_list infos;
bool roundtrip_needed;
/* required for tablet-unstable-v2 */
struct wl_list seats;
struct tablet_v2_info *tablet_info;
};
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;
global->interface = xstrdup(interface);
wl_list_insert(info->infos.prev, &global->link);
}
static void
print_output_info(void *data)
{
struct output_info *output = data;
struct output_mode *mode;
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;
}
printf("\tx: %d, y: %d,",
output->geometry.x, output->geometry.y);
if (output->version >= 2)
printf(" scale: %d,", output->geometry.scale);
printf("\n");
printf("\tphysical_width: %d mm, physical_height: %d mm,\n",
output->geometry.physical_width,
output->geometry.physical_height);
printf("\tmake: '%s', model: '%s',\n",
output->geometry.make, output->geometry.model);
printf("\tsubpixel_orientation: %s, output_transform: %s,\n",
subpixel_orientation, transform);
wl_list_for_each(mode, &output->modes, link) {
printf("\tmode:\n");
printf("\t\twidth: %d px, height: %d px, refresh: %.3f Hz,\n",
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");
}
}
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';
}
static void
print_shm_info(void *data)
{
char str[5];
struct shm_info *shm = data;
struct shm_format *format;
print_global_info(data);
printf("\tformats:");
wl_list_for_each(format, &shm->formats, link)
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:
fourcc2str(format->format, str, sizeof(str));
printf(" '%s'(0x%08x)", str, format->format);
break;
}
printf("\n");
}
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");
}
static void
print_seat_info(void *data)
{
struct seat_info *seat = data;
print_global_info(data);
printf("\tname: %s\n", seat->name);
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");
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)
{
}
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)
{
}
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,
};
static void
seat_handle_capabilities(void *data, struct wl_seat *wl_seat,
enum wl_seat_capability caps)
{
struct seat_info *seat = data;
seat->capabilities = caps;
/* 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) {
seat->keyboard = wl_seat_get_keyboard(seat->seat);
wl_keyboard_add_listener(seat->keyboard, &keyboard_listener,
seat);
seat->info->roundtrip_needed = true;
}
}
static void
seat_handle_name(void *data, struct wl_seat *wl_seat,
const char *name)
{
struct seat_info *seat = data;
seat->name = xstrdup(name);
}
static const struct wl_seat_listener seat_listener = {
seat_handle_capabilities,
seat_handle_name,
};
static void
destroy_seat_info(void *data)
{
struct seat_info *seat = data;
wl_seat_destroy(seat->seat);
if (seat->name != NULL)
free(seat->name);
if (seat->keyboard)
wl_keyboard_destroy(seat->keyboard);
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) {
printf("\t\t\thardware serial: %lx\n", info->hardware_serial);
}
if (info->hardware_id_wacom) {
printf("\t\t\thardware wacom: %lx\n", info->hardware_id_wacom);
}
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;
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)
{
}
static void
handle_tablet_v2_tablet_tool_frame(void *data,
struct zwp_tablet_tool_v2 *zwp_tablet_tool_v2,
uint32_t time)
{
}
static const struct zwp_tablet_tool_v2_listener tablet_tool_listener = {
.removed = handle_tablet_v2_tablet_tool_removed,
.done = handle_tablet_v2_tablet_tool_done,
.type = handle_tablet_v2_tablet_tool_type,
.hardware_serial = handle_tablet_v2_tablet_tool_hardware_serial,
.hardware_id_wacom = handle_tablet_v2_tablet_tool_hardware_id_wacom,
.capability = handle_tablet_v2_tablet_tool_capability,
.proximity_in = handle_tablet_v2_tablet_tool_proximity_in,
.proximity_out = handle_tablet_v2_tablet_tool_proximity_out,
.down = handle_tablet_v2_tablet_tool_down,
.up = handle_tablet_v2_tablet_tool_up,
.motion = handle_tablet_v2_tablet_tool_motion,
.pressure = handle_tablet_v2_tablet_tool_pressure,
.distance = handle_tablet_v2_tablet_tool_distance,
.tilt = handle_tablet_v2_tablet_tool_tilt,
.rotation = handle_tablet_v2_tablet_tool_rotation,
.slider = handle_tablet_v2_tablet_tool_slider,
.wheel = handle_tablet_v2_tablet_tool_wheel,
.button = handle_tablet_v2_tablet_tool_button,
.frame = handle_tablet_v2_tablet_tool_frame,
};
static void add_tablet_v2_tablet_tool_info(void *data,
struct zwp_tablet_seat_v2 *tablet_seat_v2,
struct zwp_tablet_tool_v2 *tool)
{
struct tablet_seat_info *tablet_seat = data;
struct tablet_tool_info *tool_info = xzalloc(sizeof *tool_info);
tool_info->tool = tool;
wl_list_insert(&tablet_seat->tools, &tool_info->link);
zwp_tablet_tool_v2_add_listener(tool, &tablet_tool_listener, tool_info);
}
static void
handle_tablet_v2_tablet_pad_group_mode_switch(void *data,
struct zwp_tablet_pad_group_v2 *zwp_tablet_pad_group_v2,
uint32_t time, uint32_t serial, uint32_t mode)
{
/* This shouldn't ever happen */
}
static void
handle_tablet_v2_tablet_pad_group_done(void *data,
struct zwp_tablet_pad_group_v2 *group)
{
/* 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_pad_group_modes(void *data,
struct zwp_tablet_pad_group_v2 *group,
uint32_t modes)
{
struct tablet_pad_group_info *info = data;
info->modes = modes;
}
static void
handle_tablet_v2_tablet_pad_group_buttons(void *data,
struct zwp_tablet_pad_group_v2 *group,
struct wl_array *buttons)
{
struct tablet_pad_group_info *info = data;
info->button_count = buttons->size / sizeof(int);
info->buttons = xzalloc(buttons->size);
memcpy(info->buttons, buttons->data, buttons->size);
}
static void
handle_tablet_v2_tablet_pad_group_ring(void *data,
struct zwp_tablet_pad_group_v2 *group,
struct zwp_tablet_pad_ring_v2 *ring)
{
struct tablet_pad_group_info *info = data;
++info->rings;
zwp_tablet_pad_ring_v2_destroy(ring);
}
static void
handle_tablet_v2_tablet_pad_group_strip(void *data,
struct zwp_tablet_pad_group_v2 *group,
struct zwp_tablet_pad_strip_v2 *strip)
{
struct tablet_pad_group_info *info = data;
++info->strips;
zwp_tablet_pad_strip_v2_destroy(strip);
}
static const struct zwp_tablet_pad_group_v2_listener tablet_pad_group_listener = {
.buttons = handle_tablet_v2_tablet_pad_group_buttons,
.modes = handle_tablet_v2_tablet_pad_group_modes,
.ring = handle_tablet_v2_tablet_pad_group_ring,
.strip = handle_tablet_v2_tablet_pad_group_strip,
.done = handle_tablet_v2_tablet_pad_group_done,
.mode_switch = handle_tablet_v2_tablet_pad_group_mode_switch,
};
static void
handle_tablet_v2_tablet_pad_group(void *data,
struct zwp_tablet_pad_v2 *zwp_tablet_pad_v2,
struct zwp_tablet_pad_group_v2 *pad_group)
{
struct tablet_pad_info *pad_info = data;
struct tablet_pad_group_info *group = xzalloc(sizeof *group);
wl_list_insert(&pad_info->groups, &group->link);
group->group = pad_group;
zwp_tablet_pad_group_v2_add_listener(pad_group,
&tablet_pad_group_listener, group);
}
static void
handle_tablet_v2_tablet_pad_path(void *data, struct zwp_tablet_pad_v2 *pad,
const char *path)
{
struct tablet_pad_info *pad_info = data;
struct tablet_v2_path *path_elem = xzalloc(sizeof *path_elem);
path_elem->path = xstrdup(path);
wl_list_insert(&pad_info->paths, &path_elem->link);
}
static void
handle_tablet_v2_tablet_pad_buttons(void *data, struct zwp_tablet_pad_v2 *pad,
uint32_t buttons)
{
struct tablet_pad_info *pad_info = data;
pad_info->buttons = buttons;
}
static void
handle_tablet_v2_tablet_pad_done(void *data, struct zwp_tablet_pad_v2 *pad)
{
/* 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_pad_removed(void *data, struct zwp_tablet_pad_v2 *pad)
{
/* don't bother waiting for this; We never make any request that's not
* allowed to be issued either way. */
}
static void
handle_tablet_v2_tablet_pad_button(void *data, struct zwp_tablet_pad_v2 *pad,
uint32_t time, uint32_t button, uint32_t state)
{
/* we don't have a surface, so this can't ever happen */
}
static void
handle_tablet_v2_tablet_pad_enter(void *data, struct zwp_tablet_pad_v2 *pad,
uint32_t serial,
struct zwp_tablet_v2 *tablet,
struct wl_surface *surface)
{
/* we don't have a surface, so this can't ever happen */
}
static void
handle_tablet_v2_tablet_pad_leave(void *data, struct zwp_tablet_pad_v2 *pad,
uint32_t serial, struct wl_surface *surface)
{
/* we don't have a surface, so this can't ever happen */
}
static const struct zwp_tablet_pad_v2_listener tablet_pad_listener = {
.group = handle_tablet_v2_tablet_pad_group,
.path = handle_tablet_v2_tablet_pad_path,
.buttons = handle_tablet_v2_tablet_pad_buttons,
.done = handle_tablet_v2_tablet_pad_done,
.removed = handle_tablet_v2_tablet_pad_removed,
.button = handle_tablet_v2_tablet_pad_button,
.enter = handle_tablet_v2_tablet_pad_enter,
.leave = handle_tablet_v2_tablet_pad_leave,
};
static void add_tablet_v2_tablet_pad_info(void *data,
struct zwp_tablet_seat_v2 *tablet_seat_v2,
struct zwp_tablet_pad_v2 *pad)
{
struct tablet_seat_info *tablet_seat = data;
struct tablet_pad_info *pad_info = xzalloc(sizeof *pad_info);
wl_list_init(&pad_info->paths);
wl_list_init(&pad_info->groups);
pad_info->pad = pad;
wl_list_insert(&tablet_seat->pads, &pad_info->link);
zwp_tablet_pad_v2_add_listener(pad, &tablet_pad_listener, pad_info);
}
static void
handle_tablet_v2_tablet_name(void *data, struct zwp_tablet_v2 *zwp_tablet_v2,
const char *name)
{
struct tablet_info *tablet_info = data;
tablet_info->name = xstrdup(name);
}
static void
handle_tablet_v2_tablet_path(void *data, struct zwp_tablet_v2 *zwp_tablet_v2,
const char *path)
{
struct tablet_info *tablet_info = data;
struct tablet_v2_path *path_elem = xzalloc(sizeof *path_elem);
path_elem->path = xstrdup(path);
wl_list_insert(&tablet_info->paths, &path_elem->link);
}
static void
handle_tablet_v2_tablet_id(void *data, struct zwp_tablet_v2 *zwp_tablet_v2,
uint32_t vid, uint32_t pid)
{
struct tablet_info *tablet_info = data;
tablet_info->vid = vid;
tablet_info->pid = pid;
}
static void
handle_tablet_v2_tablet_done(void *data, struct zwp_tablet_v2 *zwp_tablet_v2)
{
/* 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_removed(void *data, struct zwp_tablet_v2 *zwp_tablet_v2)
{
/* don't bother waiting for this; We never make any request that's not
* allowed to be issued either way. */
}
static const struct zwp_tablet_v2_listener tablet_listener = {
.name = handle_tablet_v2_tablet_name,
.id = handle_tablet_v2_tablet_id,
.path = handle_tablet_v2_tablet_path,
.done = handle_tablet_v2_tablet_done,
.removed = handle_tablet_v2_tablet_removed
};
static void
add_tablet_v2_tablet_info(void *data, struct zwp_tablet_seat_v2 *tablet_seat_v2,
struct zwp_tablet_v2 *tablet)
{
struct tablet_seat_info *tablet_seat = data;
struct tablet_info *tablet_info = xzalloc(sizeof *tablet_info);
wl_list_init(&tablet_info->paths);
tablet_info->tablet = tablet;
wl_list_insert(&tablet_seat->tablets, &tablet_info->link);
zwp_tablet_v2_add_listener(tablet, &tablet_listener, tablet_info);
}
static const struct zwp_tablet_seat_v2_listener tablet_seat_listener = {
.tablet_added = add_tablet_v2_tablet_info,
.pad_added = add_tablet_v2_tablet_pad_info,
.tool_added = add_tablet_v2_tablet_tool_info,
};
static void
add_tablet_seat_info(struct tablet_v2_info *tablet_info, struct seat_info *seat)
{
struct tablet_seat_info *tablet_seat = xzalloc(sizeof *tablet_seat);
wl_list_insert(&tablet_info->seats, &tablet_seat->link);
tablet_seat->seat = zwp_tablet_manager_v2_get_tablet_seat(
tablet_info->manager, seat->seat);
zwp_tablet_seat_v2_add_listener(tablet_seat->seat,
&tablet_seat_listener, tablet_seat);
wl_list_init(&tablet_seat->pads);
wl_list_init(&tablet_seat->tablets);
wl_list_init(&tablet_seat->tools);
tablet_seat->seat_info = seat;
tablet_info->info->roundtrip_needed = true;
}
static void
add_tablet_v2_info(struct weston_info *info, uint32_t id, uint32_t version)
{
struct seat_info *seat;
struct tablet_v2_info *tablet = xzalloc(sizeof *tablet);
wl_list_init(&tablet->seats);
tablet->info = info;
init_global_info(info, &tablet->global, id,
zwp_tablet_manager_v2_interface.name, version);
tablet->global.print = print_tablet_v2_info;
tablet->global.destroy = destroy_tablet_v2_info;
tablet->manager = wl_registry_bind(info->registry,
id, &zwp_tablet_manager_v2_interface, 1);
wl_list_for_each(seat, &info->seats, global_link) {
add_tablet_seat_info(tablet, seat);
}
info->tablet_info = tablet;
}
static void
add_seat_info(struct weston_info *info, uint32_t id, uint32_t version)
{
struct seat_info *seat = xzalloc(sizeof *seat);
/* required to set roundtrip_needed to true in capabilities
* handler */
seat->info = info;
init_global_info(info, &seat->global, id, "wl_seat", version);
seat->global.print = print_seat_info;
seat->global.destroy = destroy_seat_info;
seat->seat = wl_registry_bind(info->registry,
id, &wl_seat_interface, MIN(version, 4));
wl_seat_add_listener(seat->seat, &seat_listener, seat);
seat->repeat_rate = seat->repeat_delay = -1;
info->roundtrip_needed = true;
wl_list_insert(&info->seats, &seat->global_link);
if (info->tablet_info) {
add_tablet_seat_info(info->tablet_info, seat);
}
}
static void
shm_handle_format(void *data, struct wl_shm *wl_shm, uint32_t format)
{
struct shm_info *shm = data;
struct shm_format *shm_format = xzalloc(sizeof *shm_format);
wl_list_insert(&shm->formats, &shm_format->link);
shm_format->format = format;
}
static const struct wl_shm_listener shm_listener = {
shm_handle_format,
};
static void
destroy_shm_info(void *data)
{
struct shm_info *shm = data;
struct shm_format *format, *tmp;
wl_list_for_each_safe(format, tmp, &shm->formats, link) {
wl_list_remove(&format->link);
free(format);
}
wl_shm_destroy(shm->shm);
}
static void
add_shm_info(struct weston_info *info, uint32_t id, uint32_t version)
{
struct shm_info *shm = xzalloc(sizeof *shm);
init_global_info(info, &shm->global, id, "wl_shm", version);
shm->global.print = print_shm_info;
shm->global.destroy = destroy_shm_info;
wl_list_init(&shm->formats);
shm->shm = wl_registry_bind(info->registry,
id, &wl_shm_interface, 1);
wl_shm_add_listener(shm->shm, &shm_listener, shm);
info->roundtrip_needed = true;
}
static void
linux_dmabuf_handle_format(void *data, struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf_v1, uint32_t format)
{
/* This is a deprecated event, dont use it. */
}
static void
linux_dmabuf_handle_modifier(void *data, struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf_v1, uint32_t format, uint32_t modifier_hi, uint32_t modifier_lo)
{
struct linux_dmabuf_info *dmabuf = data;
struct linux_dmabuf_modifier *linux_dmabuf_modifier = xzalloc(sizeof *linux_dmabuf_modifier);
wl_list_insert(&dmabuf->modifiers, &linux_dmabuf_modifier->link);
linux_dmabuf_modifier->format = format;
linux_dmabuf_modifier->modifier = ((uint64_t)modifier_hi) << 32 | modifier_lo;
}
static const struct zwp_linux_dmabuf_v1_listener linux_dmabuf_listener = {
linux_dmabuf_handle_format,
linux_dmabuf_handle_modifier,
};
static void
destroy_linux_dmabuf_info(void *data)
{
struct linux_dmabuf_info *dmabuf = data;
struct linux_dmabuf_modifier *modifier, *tmp;
wl_list_for_each_safe(modifier, tmp, &dmabuf->modifiers, link) {
wl_list_remove(&modifier->link);
free(modifier);
}
zwp_linux_dmabuf_v1_destroy(dmabuf->dmabuf);
}
static void
add_linux_dmabuf_info(struct weston_info *info, uint32_t id, uint32_t version)
{
struct linux_dmabuf_info *dmabuf = xzalloc(sizeof *dmabuf);
init_global_info(info, &dmabuf->global, id, "zwp_linux_dmabuf_v1", version);
dmabuf->global.print = print_linux_dmabuf_info;
dmabuf->global.destroy = destroy_linux_dmabuf_info;
wl_list_init(&dmabuf->modifiers);
if (version >= 3) {
dmabuf->dmabuf = wl_registry_bind(info->registry,
id, &zwp_linux_dmabuf_v1_interface, 3);
zwp_linux_dmabuf_v1_add_listener(dmabuf->dmabuf, &linux_dmabuf_listener, dmabuf);
info->roundtrip_needed = true;
}
}
static void
output_handle_geometry(void *data, struct wl_output *wl_output,
int32_t x, int32_t y,
int32_t physical_width, int32_t physical_height,
int32_t subpixel,
const char *make, const char *model,
int32_t output_transform)
{
struct output_info *output = data;
output->geometry.x = x;
output->geometry.y = y;
output->geometry.physical_width = physical_width;
output->geometry.physical_height = physical_height;
output->geometry.subpixel = subpixel;
output->geometry.make = xstrdup(make);
output->geometry.model = xstrdup(model);
output->geometry.output_transform = output_transform;
}
static void
output_handle_mode(void *data, struct wl_output *wl_output,
uint32_t flags, int32_t width, int32_t height,
int32_t refresh)
{
struct output_info *output = data;
struct output_mode *mode = xmalloc(sizeof *mode);
mode->flags = flags;
mode->width = width;
mode->height = height;
mode->refresh = refresh;
wl_list_insert(output->modes.prev, &mode->link);
}
static void
output_handle_done(void *data, struct wl_output *wl_output)
{
/* 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
output_handle_scale(void *data, struct wl_output *wl_output,
int32_t scale)
{
struct output_info *output = data;
output->geometry.scale = scale;
}
static const struct wl_output_listener output_listener = {
output_handle_geometry,
output_handle_mode,
output_handle_done,
output_handle_scale,
};
static void
destroy_output_info(void *data)
{
struct output_info *output = data;
struct output_mode *mode, *tmp;
wl_output_destroy(output->output);
if (output->geometry.make != NULL)
free(output->geometry.make);
if (output->geometry.model != NULL)
free(output->geometry.model);
wl_list_for_each_safe(mode, tmp, &output->modes, link) {
wl_list_remove(&mode->link);
free(mode);
}
}
static void
add_output_info(struct weston_info *info, uint32_t id, uint32_t version)
{
struct output_info *output = xzalloc(sizeof *output);
init_global_info(info, &output->global, id, "wl_output", version);
output->global.print = print_output_info;
output->global.destroy = destroy_output_info;
output->version = MIN(version, 2);
output->geometry.scale = 1;
wl_list_init(&output->modes);
output->output = wl_registry_bind(info->registry, id,
&wl_output_interface, output->version);
wl_output_add_listener(output->output, &output_listener,
output);
info->roundtrip_needed = true;
}
static void
destroy_presentation_info(void *info)
{
struct presentation_info *prinfo = info;
wp_presentation_destroy(prinfo->presentation);
}
static const char *
clock_name(clockid_t clk_id)
{
static const char *names[] = {
[CLOCK_REALTIME] = "CLOCK_REALTIME",
[CLOCK_MONOTONIC] = "CLOCK_MONOTONIC",
[CLOCK_MONOTONIC_RAW] = "CLOCK_MONOTONIC_RAW",
[CLOCK_REALTIME_COARSE] = "CLOCK_REALTIME_COARSE",
[CLOCK_MONOTONIC_COARSE] = "CLOCK_MONOTONIC_COARSE",
#ifdef CLOCK_BOOTTIME
[CLOCK_BOOTTIME] = "CLOCK_BOOTTIME",
#endif
};
if (clk_id < 0 || (unsigned)clk_id >= ARRAY_LENGTH(names))
return "unknown";
return names[clk_id];
}
static void
print_presentation_info(void *info)
{
struct presentation_info *prinfo = info;
print_global_info(info);
printf("\tpresentation clock id: %d (%s)\n",
prinfo->clk_id, clock_name(prinfo->clk_id));
}
static void
presentation_handle_clock_id(void *data, struct wp_presentation *presentation,
uint32_t clk_id)
{
struct presentation_info *prinfo = data;
prinfo->clk_id = clk_id;
}
static const struct wp_presentation_listener presentation_listener = {
presentation_handle_clock_id
};
static void
add_presentation_info(struct weston_info *info, uint32_t id, uint32_t version)
{
struct presentation_info *prinfo = xzalloc(sizeof *prinfo);
init_global_info(info, &prinfo->global, id,
wp_presentation_interface.name, version);
prinfo->global.print = print_presentation_info;
prinfo->global.destroy = destroy_presentation_info;
prinfo->clk_id = -1;
prinfo->presentation = wl_registry_bind(info->registry, id,
&wp_presentation_interface, 1);
wp_presentation_add_listener(prinfo->presentation,
&presentation_listener, prinfo);
info->roundtrip_needed = true;
}
static void
destroy_global_info(void *data)
{
}
static void
add_global_info(struct weston_info *info, uint32_t id,
const char *interface, uint32_t version)
{
struct global_info *global = xzalloc(sizeof *global);
init_global_info(info, global, id, interface, version);
global->print = print_global_info;
global->destroy = destroy_global_info;
}
static void
global_handler(void *data, struct wl_registry *registry, uint32_t id,
const char *interface, uint32_t version)
{
struct weston_info *info = data;
if (!strcmp(interface, "wl_seat"))
add_seat_info(info, id, version);
else if (!strcmp(interface, "wl_shm"))
add_shm_info(info, id, version);
else if (!strcmp(interface, "zwp_linux_dmabuf_v1"))
add_linux_dmabuf_info(info, id, version);
else if (!strcmp(interface, "wl_output"))
add_output_info(info, id, version);
else if (!strcmp(interface, wp_presentation_interface.name))
add_presentation_info(info, id, version);
else if (!strcmp(interface, zwp_tablet_manager_v2_interface.name))
add_tablet_v2_info(info, id, version);
else
add_global_info(info, id, interface, version);
}
static void
global_remove_handler(void *data, struct wl_registry *registry, uint32_t name)
{
}
static const struct wl_registry_listener registry_listener = {
global_handler,
global_remove_handler
};
static void
print_infos(struct wl_list *infos)
{
struct global_info *info;
wl_list_for_each(info, infos, link)
info->print(info);
}
static void
destroy_info(void *data)
{
struct global_info *global = data;
global->destroy(data);
wl_list_remove(&global->link);
free(global->interface);
free(data);
}
static void
destroy_infos(struct wl_list *infos)
{
struct global_info *info, *tmp;
wl_list_for_each_safe(info, tmp, infos, link)
destroy_info(info);
}
int
main(int argc, char **argv)
{
struct weston_info info;
info.display = wl_display_connect(NULL);
if (!info.display) {
fprintf(stderr, "failed to create display: %m\n");
return -1;
}
info.tablet_info = NULL;
wl_list_init(&info.infos);
wl_list_init(&info.seats);
info.registry = wl_display_get_registry(info.display);
wl_registry_add_listener(info.registry, &registry_listener, &info);
do {
info.roundtrip_needed = false;
wl_display_roundtrip(info.display);
} while (info.roundtrip_needed);
print_infos(&info.infos);
destroy_infos(&info.infos);
wl_registry_destroy(info.registry);
wl_display_disconnect(info.display);
return 0;
}