/* * 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 #include #include #include #include #include #include #include #include #include #include #include "shared/helpers.h" #include "shared/os-compatibility.h" #include "shared/xalloc.h" #include #include "presentation-time-client-protocol.h" #include "linux-dmabuf-unstable-v1-client-protocol.h" #include "tablet-unstable-v2-client-protocol.h" #include "xdg-output-unstable-v1-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_list global_link; 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 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; }; 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; /* required for xdg-output-unstable-v1 */ struct wl_list outputs; struct xdg_output_manager_v1_info *xdg_output_manager_v1_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) { /* Just so we don’t leak the keymap fd */ close(fd); } 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: %" PRIx64 "\n", info->hardware_serial); } if (info->hardware_id_wacom) { printf("\t\t\thardware wacom: %" PRIx64 "\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 destroy_xdg_output_v1_info(struct xdg_output_v1_info *info) { wl_list_remove(&info->link); zxdg_output_v1_destroy(info->xdg_output); free(info->name); free(info->description); free(info); } static void print_xdg_output_v1_info(const struct xdg_output_v1_info *info) { printf("\txdg_output_v1\n"); printf("\t\toutput: %d\n", info->output->global.id); if (info->name) printf("\t\tname: '%s'\n", info->name); if (info->description) printf("\t\tdescription: '%s'\n", info->description); printf("\t\tlogical_x: %d, logical_y: %d\n", info->logical.x, info->logical.y); printf("\t\tlogical_width: %d, logical_height: %d\n", info->logical.width, info->logical.height); } static void print_xdg_output_manager_v1_info(void *data) { struct xdg_output_manager_v1_info *info = data; struct xdg_output_v1_info *output; print_global_info(data); wl_list_for_each(output, &info->outputs, link) print_xdg_output_v1_info(output); } static void destroy_xdg_output_manager_v1_info(void *data) { struct xdg_output_manager_v1_info *info = data; struct xdg_output_v1_info *output, *tmp; zxdg_output_manager_v1_destroy(info->manager); wl_list_for_each_safe(output, tmp, &info->outputs, link) destroy_xdg_output_v1_info(output); } static void handle_xdg_output_v1_logical_position(void *data, struct zxdg_output_v1 *output, int32_t x, int32_t y) { struct xdg_output_v1_info *xdg_output = data; xdg_output->logical.x = x; xdg_output->logical.y = y; } static void handle_xdg_output_v1_logical_size(void *data, struct zxdg_output_v1 *output, int32_t width, int32_t height) { struct xdg_output_v1_info *xdg_output = data; xdg_output->logical.width = width; xdg_output->logical.height = height; } static void handle_xdg_output_v1_done(void *data, struct zxdg_output_v1 *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 handle_xdg_output_v1_name(void *data, struct zxdg_output_v1 *output, const char *name) { struct xdg_output_v1_info *xdg_output = data; xdg_output->name = strdup(name); } static void handle_xdg_output_v1_description(void *data, struct zxdg_output_v1 *output, const char *description) { struct xdg_output_v1_info *xdg_output = data; xdg_output->description = strdup(description); } static const struct zxdg_output_v1_listener xdg_output_v1_listener = { .logical_position = handle_xdg_output_v1_logical_position, .logical_size = handle_xdg_output_v1_logical_size, .done = handle_xdg_output_v1_done, .name = handle_xdg_output_v1_name, .description = handle_xdg_output_v1_description, }; static void add_xdg_output_v1_info(struct xdg_output_manager_v1_info *manager_info, struct output_info *output) { struct xdg_output_v1_info *xdg_output = xzalloc(sizeof *xdg_output); wl_list_insert(&manager_info->outputs, &xdg_output->link); xdg_output->xdg_output = zxdg_output_manager_v1_get_xdg_output( manager_info->manager, output->output); zxdg_output_v1_add_listener(xdg_output->xdg_output, &xdg_output_v1_listener, xdg_output); xdg_output->output = output; manager_info->info->roundtrip_needed = true; } static void add_xdg_output_manager_v1_info(struct weston_info *info, uint32_t id, uint32_t version) { struct output_info *output; struct xdg_output_manager_v1_info *manager = xzalloc(sizeof *manager); wl_list_init(&manager->outputs); manager->info = info; init_global_info(info, &manager->global, id, zxdg_output_manager_v1_interface.name, version); manager->global.print = print_xdg_output_manager_v1_info; manager->global.destroy = destroy_xdg_output_manager_v1_info; manager->manager = wl_registry_bind(info->registry, id, &zxdg_output_manager_v1_interface, version > 2 ? 2 : version); wl_list_for_each(output, &info->outputs, global_link) add_xdg_output_v1_info(manager, output); info->xdg_output_manager_v1_info = manager; } 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, don’t 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; wl_list_insert(&info->outputs, &output->global_link); if (info->xdg_output_manager_v1_info) add_xdg_output_v1_info(info->xdg_output_manager_v1_info, output); } 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 if (!strcmp(interface, zxdg_output_manager_v1_interface.name)) add_xdg_output_manager_v1_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; info.xdg_output_manager_v1_info = NULL; wl_list_init(&info.infos); wl_list_init(&info.seats); wl_list_init(&info.outputs); info.registry = wl_display_get_registry(info.display); wl_registry_add_listener(info.registry, ®istry_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; }