| /* |
| * 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" |
| |
| 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_seat *seat; |
| struct weston_info *info; |
| |
| uint32_t capabilities; |
| char *name; |
| |
| int32_t repeat_rate; |
| int32_t repeat_delay; |
| }; |
| |
| 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; |
| }; |
| |
| 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) { |
| struct wl_keyboard *keyboard; |
| |
| keyboard = wl_seat_get_keyboard(seat->seat); |
| wl_keyboard_add_listener(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); |
| } |
| |
| 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; |
| } |
| |
| 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; |
| } |
| |
| 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 |
| 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; |
| } |
| |
| wl_list_init(&info.infos); |
| |
| 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; |
| } |