pax_global_header00006660000000000000000000000064144403115510014510gustar00rootroot0000000000000052 comment=43cc17bc5333c41b4d6b7b1708191426126cb21a wayland-utils-1.2.0/000077500000000000000000000000001444031155100143055ustar00rootroot00000000000000wayland-utils-1.2.0/.gitlab-ci.yml000066400000000000000000000033531444031155100167450ustar00rootroot00000000000000.templates_sha: &templates_sha 0a0c8db6589472e3ff58e8c3feec518a43167286 include: - project: 'freedesktop/ci-templates' ref: *templates_sha file: '/templates/ci-fairy.yml' - project: 'freedesktop/ci-templates' ref: *templates_sha file: '/templates/alpine.yml' variables: FDO_UPSTREAM_REPO: 'wayland/wayland-utils' FDO_DISTRIBUTION_TAG: '2022-05-26.0' #FDO_FORCE_REBUILD: 1 stages: - "Contribution checks" - "Prepare container" - "Build and test" check-mr: extends: .fdo.ci-fairy stage: "Contribution checks" script: - ci-fairy check-commits --signed-off-by - ci-fairy check-merge-request --require-allow-collaboration rules: - if: '$CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "main"' when: always - when: never prepare-container: extends: .fdo.container-build@alpine stage: "Prepare container" variables: FDO_DISTRIBUTION_PACKAGES: 'build-base meson libdrm-dev wayland-protocols git libffi-dev expat-dev' FDO_DISTRIBUTION_EXEC: | git clone https://gitlab.freedesktop.org/wayland/wayland.git --depth 1 --branch=1.20.0 cd wayland meson build/ -Dtests=false -Ddocumentation=false -Ddtd_validation=false ninja -C build/ install cd .. rm -rf wayland git clone https://gitlab.freedesktop.org/wayland/wayland-protocols cd wayland-protocols git checkout -q 1.24 meson build/ -Dtests=false ninja -C build/ install cd .. rm -rf wayland-protocols rules: - when: on_success build: extends: .fdo.distribution-image@alpine stage: "Build and test" script: - meson build/ -Dwerror=true - ninja -C build/ - ninja -C build/ test artifacts: paths: - build/meson-logs/ rules: - when: on_success wayland-utils-1.2.0/COPYING000066400000000000000000000023141444031155100153400ustar00rootroot00000000000000Copyright © 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. --- The above is the version of the MIT "Expat" License used by X.org: http://cgit.freedesktop.org/xorg/xserver/tree/COPYING wayland-utils-1.2.0/README.md000066400000000000000000000047471444031155100156000ustar00rootroot00000000000000wayland-info ============ `wayland-info` is a utility for displaying information about the Wayland protocols supported by a Wayland compositor. It can be used to check which Wayland protocols and versions are advertised by the Wayland compositor. `wayland-info` also provides additional information for a subset of Wayland protocols it knows about, namely Linux DMABUF, presentation time, tablet and XDG output protocols. An example output is: ``` $ wayland-info interface: 'wl_drm', version: 2, name: 1 interface: 'wl_compositor', version: 4, name: 2 interface: 'wl_shm', version: 1, name: 3 formats: XRGB8888 ARGB8888 interface: 'wl_output', version: 2, name: 4 x: 0, y: 0, scale: 1, physical_width: 310 mm, physical_height: 170 mm, make: 'AUO', model: '0x123d', subpixel_orientation: unknown, output_transform: normal, mode: width: 1920 px, height: 1080 px, refresh: 60.049 Hz, flags: current preferred [...] ``` `wayland-info` uses the Wayland protocol only and only shows features that the compositor implements via Wayland protocols. Building ======== `wayland-info` is built using [Meson](https://mesonbuild.com/) and depends on [Wayland](https://gitlab.freedesktop.org/wayland/wayland) and [wayland-protocols](https://gitlab.freedesktop.org/wayland/wayland-protocols). $ git clone https://gitlab.freedesktop.org/wayland/wayland-utils.git $ cd wayland-utils $ meson build/ --prefix=... $ ninja -C build/ install $ cd .. Running ======= `wayland-info` does not accept any command line option. $ wayland-info Reporting issues and contributing ================================= `wayland-info` is hosted on [freedesktop.org's GitLab instance](https://gitlab.freedesktop.org/wayland/wayland-utils). It uses the same contributing guidelines as the Wayland project, please refer to [Wayland's contributing document](https://gitlab.freedesktop.org/wayland/wayland/-/blob/main/CONTRIBUTING.md) for more information. Credit ====== The code base of `wayland-info` is identical to `weston-info` and therefore all credits for `wayland-info` go to the authors and contributors of `weston-info`. Because `weston-info` is a generally useful tool, even outside of weston, and gives interesting information on any Wayland compositor, this is basically a standalone version of `weston-info` as found in [weston](https://gitlab.freedesktop.org/wayland/weston/) repository. wayland-utils-1.2.0/man/000077500000000000000000000000001444031155100150605ustar00rootroot00000000000000wayland-utils-1.2.0/man/meson.build000066400000000000000000000003371444031155100172250ustar00rootroot00000000000000man_conf = configuration_data() man_conf.set('version', meson.project_version()) configure_file( input: 'wayland-info.man', output: 'wayland-info.1', install_dir: join_paths(dir_man, 'man1'), configuration: man_conf ) wayland-utils-1.2.0/man/wayland-info.man000066400000000000000000000026161444031155100201520ustar00rootroot00000000000000.TH WAYLAND-INFO 1 "2020-07-08" "@version@" .SH NAME wayland-info \- display information utility for Wayland .SH SYNOPSIS .B wayland-info [option ...] . .\" *************************************************************** .SH DESCRIPTION .B wayland-info is a utility for displaying information about the Wayland protocols supported by a Wayland compositor. It can be used to check which Wayland protocols and versions are advertised by the Wayland compositor. .B wayland-info also provides additional information for a subset of Wayland protocols it knows about, namely Linux DMABUF, presentation time, tablet and XDG output protocols. . .\" *************************************************************** .SH OPTIONS . .B wayland-info accepts the following line options: .TP 8 .B \-h, \-\-help Print a summary of command line options, and quit. .TP 8 .B \-i, \-\-interface \fIinterface\fR Only print information about the Wayland globals containing the string \fIinterface\fR in their name. . .\" *************************************************************** .SH ENVIRONMENT . .TP .B WAYLAND_DISPLAY The name of the display (socket) of an already running Wayland server, without the path. The directory path is always taken from .BR XDG_RUNTIME_DIR . If .B WAYLAND_DISPLAY is not set, the socket name is "wayland-0". . .\" *************************************************************** .SH "SEE ALSO" .BR weston (1) wayland-utils-1.2.0/meson.build000066400000000000000000000016161444031155100164530ustar00rootroot00000000000000project('wayland-utils', 'c', version: '1.2.0', default_options: [ 'warning_level=3', 'c_std=c99', 'b_lundef=true', ], meson_version: '>= 0.47', license: 'MIT/Expat', ) dir_prefix = get_option('prefix') dir_man = join_paths(dir_prefix, get_option('mandir')) dir_inc = include_directories('.') pkgconfig = import('pkgconfig') cc = meson.get_compiler('c') add_project_arguments(cc.get_supported_arguments([ '-Wundef', '-Wno-unused-parameter', ]), language: 'c') dep_wayland_client = dependency('wayland-client', version: '>= 1.20.0') dep_libdrm = dependency('libdrm', version: '>=2.4.109', required: get_option('drm')) dep_libdrm_version = dep_libdrm.version() config_h = configuration_data() config_h.set('_POSIX_C_SOURCE', '200809L') config_h.set('HAVE_LIBDRM', dep_libdrm.found().to_int()) configure_file(output: 'config.h', configuration: config_h) subdir('wayland-info') subdir('man') wayland-utils-1.2.0/meson_options.txt000066400000000000000000000001431444031155100177400ustar00rootroot00000000000000option( 'drm', description: 'Print extra DRM information (requires libdrm)', type: 'feature', ) wayland-utils-1.2.0/releasing.md000066400000000000000000000040061444031155100166000ustar00rootroot00000000000000Releasing ========= To make a release of wayland-utils, follow these steps. 1. Update the first stanza of `meson.build` to the intended version. Then commit your changes: $ RELEASE_NUMBER="x.y.z" $ RELEASE_NAME="[alpha|beta|RC1|RC2|official|point]" $ git status $ git commit meson.build -s -m "build: bump to version $RELEASE_NUMBER for the $RELEASE_NAME release" $ git push 2. Run the `release.sh` script to generate the tarballs, sign and upload them, and generate a release announcement template. This script can be obtained from the Wayland repository: https://gitlab.freedesktop.org/wayland/wayland/-/blob/main/release.sh 3. Compose the release announcements. The script will generate a *-x.y.z-announce.eml file with a list of changes and tags. Prepend these with a human-readable listing of the most notable changes. 4. PGP sign the release announcement and send it to . 5. Update `releases.html` in wayland.freedesktop.org with links to tarballs and the release email URL. Once satisfied: $ git commit -am "releases: add ${RELEASE_NUMBER} release" $ git push For x.y.0 releases, also create the release series x.y branch. The x.y branch is for bug fixes and conservative changes to the x.y.0 release, and is where we create x.y.z releases from. Creating the x.y branch opens up master for new development and lets new development move on. We've done this both after the x.y.0 release (to focus development on bug fixing for the x.y.1 release for a little longer) or before the x.y.0 release (like we did with the 1.5.0 release, to unblock master development early). $ git branch x.y [sha] $ git push origin x.y The master branch's `meson.build` version should always be (at least) x.y.90, with x.y being the most recent stable branch. The stable branch's `meson.build` version is just whatever was most recently released from that branch. For stable branches, we commit fixes to master first, then `git cherry-pick -x` them back to the stable branch. wayland-utils-1.2.0/wayland-info/000077500000000000000000000000001444031155100166755ustar00rootroot00000000000000wayland-utils-1.2.0/wayland-info/meson.build000066400000000000000000000031061444031155100210370ustar00rootroot00000000000000dep_scanner = dependency('wayland-scanner', native: true) prog_scanner = find_program(dep_scanner.get_pkgconfig_variable('wayland_scanner')) dep_wp = dependency('wayland-protocols', version: '>= 1.24') dir_wp_base = dep_wp.get_pkgconfig_variable('pkgdatadir') generated_protocols = { # stable 'presentation-time': dir_wp_base / 'stable/presentation-time/presentation-time.xml', # staging 'drm-lease-v1': dir_wp_base / 'staging/drm-lease/drm-lease-v1.xml', # unstable 'linux-dmabuf-unstable-v1': dir_wp_base / 'unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml', 'tablet-unstable-v2': dir_wp_base / 'unstable/tablet/tablet-unstable-v2.xml', 'xdg-output-unstable-v1': dir_wp_base / 'unstable/xdg-output/xdg-output-unstable-v1.xml', } generated_protocols_sources = [] foreach base_file, xml_path: generated_protocols foreach output_type: [ 'client-header', 'private-code' ] if output_type == 'client-header' output_file = '@0@-client-protocol.h'.format(base_file) else output_file = '@0@-protocol.c'.format(base_file) if dep_scanner.version().version_compare('< 1.14.91') output_type = 'code' endif endif target = custom_target( '@0@ @1@'.format(base_file, output_type), command: [ prog_scanner, output_type, '@INPUT@', '@OUTPUT@' ], input: xml_path, output: output_file, ) generated_protocols_sources += target endforeach endforeach wayland_info_src = [ 'wayland-info.c', ] executable('wayland-info', wayland_info_src + generated_protocols_sources, include_directories: dir_inc, dependencies: [dep_wayland_client, dep_libdrm], install : true) wayland-utils-1.2.0/wayland-info/wayland-info.c000066400000000000000000001713251444031155100214420ustar00rootroot00000000000000/* * 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 #include #if HAVE_LIBDRM #include #include #endif #include "presentation-time-client-protocol.h" #include "drm-lease-v1-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" #ifndef ARRAY_LENGTH #define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0]) #endif #ifndef MIN #define MIN(x,y) (((x) < (y)) ? (x) : (y)) #endif #define xmalloc(s) (fail_on_null(malloc(s), (s), __FILE__, __LINE__)) #define xzalloc(s) (fail_on_null(zalloc(s), (s), __FILE__, __LINE__)) #define xstrdup(s) (fail_on_null(strdup(s), 0, __FILE__, __LINE__)) 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; char *name, *description; 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_tranche { struct wl_list link; dev_t target_device; uint32_t flags; struct wl_array format_indices; }; struct linux_dmabuf_info { struct global_info global; struct zwp_linux_dmabuf_v1 *dmabuf; /* Deprecated, only for use in v3 */ struct wl_list modifiers; /* For use in v4 */ struct zwp_linux_dmabuf_feedback_v1 *feedback; void *format_map; uint32_t format_size; dev_t main_device; struct linux_dmabuf_tranche current_tranche; struct wl_list tranches; }; struct seat_info { struct global_info global; struct wl_list global_link; struct wl_seat *seat; struct wayland_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 wayland_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 wayland_info *info; struct wl_list outputs; }; struct presentation_info { struct global_info global; struct wp_presentation *presentation; clockid_t clk_id; }; struct drm_lease_connector { struct wl_list link; struct wp_drm_lease_connector_v1 *connector; uint32_t id; char *name, *description; }; struct drm_lease_device_info { struct global_info global; struct wl_list link; struct wp_drm_lease_device_v1 *device; char *path; struct wl_list connectors; }; struct wayland_info { struct wl_display *display; struct wl_registry *registry; char *interface; 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 * fail_on_null(void *p, size_t size, char *file, int32_t line) { if (p == NULL) { fprintf(stderr, "[wayland-info] "); if (file) fprintf(stderr, "%s:%d: ", file, line); fprintf(stderr, "out of memory"); if (size) fprintf(stderr, " (%zd)", size); fprintf(stderr, "\n"); exit(EXIT_FAILURE); } return p; } static inline void * zalloc(size_t size) { return calloc(1, size); } static void print_global_info(void *data) { char buf[512]; struct global_info *global = data; snprintf(buf, sizeof(buf), "'%s',", global->interface); printf("interface: %-45s version: %2u, name: %2u\n", buf, global->version, global->id); } static void init_global_info(struct wayland_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); if (output->name != NULL) printf("\tname: %s\n", output->name); if (output->description != NULL) printf("\tdescription: %s\n", output->description); 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 (fourcc):"); wl_list_for_each(format, &shm->formats, link) switch (format->format) { case WL_SHM_FORMAT_ARGB8888: printf("\n\t 0 = 'AR24'"); break; case WL_SHM_FORMAT_XRGB8888: printf("\n\t 1 = 'XR24'"); break; default: fourcc2str(format->format, str, sizeof(str)); printf("\n\t0x%08x = '%s'", format->format, str); break; } printf("\n"); } static void print_format(uint32_t format) { char str[5]; fourcc2str(format, str, sizeof(str)); printf("0x%08x = '%s'", format, str); } static void print_format_and_modifier(uint32_t format, uint64_t modifier) { char str[5]; fourcc2str(format, str, sizeof(str)); #if HAVE_LIBDRM char *name = drmGetFormatModifierName(modifier); char *vendor = drmGetFormatModifierVendor(modifier); if (!vendor) printf("0x%08x = '%s'; 0x%016"PRIx64" = UNKNOWN", format, str, modifier); else if (!name) printf("0x%08x = '%s'; 0x%016"PRIx64" = %s_UNKNOWN_MODIFIER", format, str, modifier, vendor); else if (fourcc_mod_get_vendor(modifier) == DRM_FORMAT_MOD_NONE) printf("0x%08x = '%s'; 0x%016"PRIx64" = %s", format, str, modifier, name); else printf("0x%08x = '%s'; 0x%016"PRIx64" = %s_%s", format, str, modifier, vendor, name); free(vendor); free(name); #else printf("0x%08x = '%s'; 0x%016"PRIx64"", format, str, modifier); #endif } static void print_devid(dev_t devid) { printf("0x%" PRIX64, (uint64_t) devid); #if HAVE_LIBDRM drmDevice *dev = NULL; if (drmGetDeviceFromDevId(devid, 0, &dev) != 0) { perror("drmGetDeviceFromDevId failed"); return; } if (!(dev->available_nodes & (1 << DRM_NODE_PRIMARY))) { /* Should never happen */ fprintf(stderr, "Device has no primary node\n"); return; } printf(" (%s", dev->nodes[DRM_NODE_PRIMARY]); if (dev->available_nodes & (1 << DRM_NODE_RENDER)) { printf(" or %s", dev->nodes[DRM_NODE_RENDER]); } printf(")"); drmFreeDevice(&dev); #endif } static void print_linux_dmabuf_info(void *data) { struct linux_dmabuf_info *dmabuf = data; struct linux_dmabuf_modifier *modifier; struct linux_dmabuf_tranche *tranche; print_global_info(data); if (dmabuf->main_device) { printf("\tmain device: "); print_devid(dmabuf->main_device); if (dmabuf->format_map == MAP_FAILED) { printf("\tError: failed to map format table\n"); return; } const struct { uint32_t format; uint32_t padding; uint64_t modifier; } *formats = dmabuf->format_map; wl_list_for_each(tranche, &dmabuf->tranches, link) { printf("\n\ttranche"); printf("\n\t\ttarget device: "); print_devid(tranche->target_device); printf("\n\t\tflags: "); if (tranche->flags == 0) printf("none"); if (tranche->flags & ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_FLAGS_SCANOUT) printf("scanout"); printf("\n"); #if HAVE_LIBDRM printf("\t\tformats (fourcc) and modifiers (names):"); #else printf("\t\tformats (fourcc) and modifiers:"); #endif const uint16_t *index; wl_array_for_each(index, &tranche->format_indices) { printf("\n\t\t"); print_format_and_modifier(formats[*index].format, formats[*index].modifier); } } munmap(dmabuf->format_map, dmabuf->format_size); } else if (dmabuf->global.version > 2) { #if HAVE_LIBDRM printf("\tformats (fourcc) and modifiers (names):"); #else printf("\tformats (fourcc) and modifiers:"); #endif wl_list_for_each(modifier, &dmabuf->modifiers, link) { printf("\n\t"); print_format_and_modifier(modifier->format, modifier->modifier); } } else { printf("\tformats (fourcc):"); wl_list_for_each(modifier, &dmabuf->modifiers, link) { printf("\n\t"); print_format(modifier->format); } } 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 wayland_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 = xstrdup(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 = xstrdup(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 wayland_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 wayland_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 wayland_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) { struct linux_dmabuf_info *dmabuf = data; struct linux_dmabuf_modifier *linux_dmabuf_modifier; if (dmabuf->global.version > 2) return; linux_dmabuf_modifier = xzalloc(sizeof *linux_dmabuf_modifier); wl_list_insert(&dmabuf->modifiers, &linux_dmabuf_modifier->link); linux_dmabuf_modifier->format = format; } 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, *modifier_tmp; struct linux_dmabuf_tranche *tranche, *tranche_tmp; wl_list_for_each_safe(modifier, modifier_tmp, &dmabuf->modifiers, link) { wl_list_remove(&modifier->link); free(modifier); } wl_list_for_each_safe(tranche, tranche_tmp, &dmabuf->tranches, link) { wl_list_remove(&tranche->link); wl_array_release(&tranche->format_indices); free(tranche); } if (dmabuf->feedback) zwp_linux_dmabuf_feedback_v1_destroy(dmabuf->feedback); zwp_linux_dmabuf_v1_destroy(dmabuf->dmabuf); } static void linux_dmabuf_feedback_handle_done(void *data, struct zwp_linux_dmabuf_feedback_v1 *feedback) { } static void linux_dmabuf_feedback_handle_format_table(void *data, struct zwp_linux_dmabuf_feedback_v1 *feedback, int32_t fd, uint32_t size) { struct linux_dmabuf_info *dmabuf = data; void *map = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); close(fd); dmabuf->format_map = map; dmabuf->format_size = size; } static void linux_dmabuf_feedback_handle_main_device(void *data, struct zwp_linux_dmabuf_feedback_v1 *feedback, struct wl_array *device) { struct linux_dmabuf_info *dmabuf = data; assert(device->size == sizeof(dev_t)); memcpy(&dmabuf->main_device, device->data, sizeof(dev_t)); } static void linux_dmabuf_feedback_handle_tranche_done(void *data, struct zwp_linux_dmabuf_feedback_v1 *feedback) { struct linux_dmabuf_info *dmabuf = data; struct linux_dmabuf_tranche *tranche = xzalloc(sizeof(struct linux_dmabuf_tranche)); memcpy(tranche, &dmabuf->current_tranche, sizeof(struct linux_dmabuf_tranche)); wl_list_insert(&dmabuf->tranches, &tranche->link); memset(&dmabuf->current_tranche, 0, sizeof(struct linux_dmabuf_tranche)); } static void linux_dmabuf_feedback_handle_tranche_target_device(void *data, struct zwp_linux_dmabuf_feedback_v1 *feedback, struct wl_array *device) { struct linux_dmabuf_info *dmabuf = data; assert(device->size == sizeof(dev_t)); memcpy(&dmabuf->current_tranche.target_device, device->data, sizeof(dev_t)); } static void linux_dmabuf_feedback_handle_tranche_formats(void *data, struct zwp_linux_dmabuf_feedback_v1 *feedback, struct wl_array *indices) { struct linux_dmabuf_info *dmabuf = data; wl_array_copy(&dmabuf->current_tranche.format_indices, indices); } static void linux_dmabuf_feedback_handle_tranche_flags(void *data, struct zwp_linux_dmabuf_feedback_v1 *feedback, uint32_t flags) { struct linux_dmabuf_info *dmabuf = data; dmabuf->current_tranche.flags = flags; } static const struct zwp_linux_dmabuf_feedback_v1_listener linux_dmabuf_feedback_listener = { linux_dmabuf_feedback_handle_done, linux_dmabuf_feedback_handle_format_table, linux_dmabuf_feedback_handle_main_device, linux_dmabuf_feedback_handle_tranche_done, linux_dmabuf_feedback_handle_tranche_target_device, linux_dmabuf_feedback_handle_tranche_formats, linux_dmabuf_feedback_handle_tranche_flags, }; static void add_linux_dmabuf_info(struct wayland_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->tranches); wl_list_init(&dmabuf->modifiers); if (version <= 3) { dmabuf->dmabuf = wl_registry_bind(info->registry, id, &zwp_linux_dmabuf_v1_interface, MIN(version, 3)); zwp_linux_dmabuf_v1_add_listener(dmabuf->dmabuf, &linux_dmabuf_listener, dmabuf); info->roundtrip_needed = true; } else { dmabuf->dmabuf = wl_registry_bind(info->registry, id, &zwp_linux_dmabuf_v1_interface, 4); dmabuf->feedback = zwp_linux_dmabuf_v1_get_default_feedback(dmabuf->dmabuf); zwp_linux_dmabuf_feedback_v1_add_listener(dmabuf->feedback, &linux_dmabuf_feedback_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 void output_handle_name(void *data, struct wl_output *wl_output, const char *name) { struct output_info *output = data; output->name = strdup(name); } static void output_handle_description(void *data, struct wl_output *wl_output, const char *description) { struct output_info *output = data; output->description = strdup(description); } static const struct wl_output_listener output_listener = { output_handle_geometry, output_handle_mode, output_handle_done, output_handle_scale, output_handle_name, output_handle_description, }; static void destroy_output_info(void *data) { struct output_info *output = data; struct output_mode *mode, *tmp; wl_output_destroy(output->output); free(output->name); free(output->description); 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 wayland_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, 4); 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", #ifdef CLOCK_MONOTONIC_RAW [CLOCK_MONOTONIC_RAW] = "CLOCK_MONOTONIC_RAW", #endif #ifdef CLOCK_REALTIME_COARSE [CLOCK_REALTIME_COARSE] = "CLOCK_REALTIME_COARSE", #endif #ifdef CLOCK_MONOTONIC_COARSE [CLOCK_MONOTONIC_COARSE] = "CLOCK_MONOTONIC_COARSE", #endif #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 wayland_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 drm_lease_connector_name(void *data, struct wp_drm_lease_connector_v1 *connector, const char *name) { struct drm_lease_connector *con = data; con->name = strdup(name); } static void drm_lease_connector_description(void *data, struct wp_drm_lease_connector_v1 *connector, const char *desc) { struct drm_lease_connector *con = data; con->description = strdup(desc); } static void drm_lease_connector_id(void *data, struct wp_drm_lease_connector_v1 *connector, uint32_t id) { struct drm_lease_connector *con = data; con->id = id; } static void drm_lease_connector_done(void *data, struct wp_drm_lease_connector_v1 *connector) { /* no-op */ } static void drm_lease_connector_withdrawn(void *data, struct wp_drm_lease_connector_v1 *connector) { /* no-op */ } static const struct wp_drm_lease_connector_v1_listener drm_lease_connector_listener = { .name = drm_lease_connector_name, .description = drm_lease_connector_description, .connector_id = drm_lease_connector_id, .done = drm_lease_connector_done, .withdrawn = drm_lease_connector_withdrawn, }; static void drm_lease_device_drm_fd(void *data, struct wp_drm_lease_device_v1 *device, int32_t fd) { struct drm_lease_device_info *dev = data; #if HAVE_LIBDRM dev->path = drmGetDeviceNameFromFd(fd); #else dev->path = NULL; #endif close(fd); } static void drm_lease_device_connector(void *data, struct wp_drm_lease_device_v1 *device, struct wp_drm_lease_connector_v1 *connector) { struct drm_lease_device_info *dev = data; struct drm_lease_connector *conn = xzalloc(sizeof *conn); conn->connector = connector; wp_drm_lease_connector_v1_add_listener(connector, &drm_lease_connector_listener, conn); wl_list_insert(&dev->connectors, &conn->link); } static void drm_lease_device_done(void *data, struct wp_drm_lease_device_v1 *device) { /* no-op */ } static void drm_lease_device_released(void *data, struct wp_drm_lease_device_v1 *device) { /* no-op */ } static const struct wp_drm_lease_device_v1_listener drm_lease_device_listener = { .drm_fd = drm_lease_device_drm_fd, .connector = drm_lease_device_connector, .done = drm_lease_device_done, .released = drm_lease_device_released, }; static void destroy_drm_lease_device_info(void *info) { struct drm_lease_device_info *dev = info; free(dev->path); wp_drm_lease_device_v1_destroy(dev->device); struct drm_lease_connector *conn, *tmp; wl_list_for_each_safe(conn, tmp, &dev->connectors, link) { wl_list_remove(&conn->link); wp_drm_lease_connector_v1_destroy(conn->connector); free(conn->name); free(conn->description); free(conn); } } static void print_drm_lease_device_info(void *info) { print_global_info(info); struct drm_lease_device_info *dev = info; printf("\tpath: %s\n", dev->path); struct drm_lease_connector *conn; wl_list_for_each(conn, &dev->connectors, link) { printf("\tconnector:\n"); printf("\t\tid: %u\n", conn->id); printf("\t\tname: %s\n", conn->name); printf("\t\tdescription: %s\n", conn->description); } } static void add_drm_lease_device_v1_info(struct wayland_info *info, uint32_t id, uint32_t version) { struct drm_lease_device_info *dev = xzalloc(sizeof *dev); init_global_info(info, &dev->global, id, wp_drm_lease_device_v1_interface.name, version); dev->global.print = print_drm_lease_device_info; dev->global.destroy = destroy_drm_lease_device_info; dev->device = wl_registry_bind(info->registry, id, &wp_drm_lease_device_v1_interface, 1); wp_drm_lease_device_v1_add_listener(dev->device, &drm_lease_device_listener, dev); wl_list_init(&dev->connectors); info->roundtrip_needed = true; } static void destroy_global_info(void *info) { } static void add_global_info(struct wayland_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 wayland_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 if (!strcmp(interface, wp_drm_lease_device_v1_interface.name)) add_drm_lease_device_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 bool should_print_info(struct wayland_info *wayland_info, struct global_info *global) { return !wayland_info->interface || strstr(global->interface, wayland_info->interface); } static void print_infos(struct wayland_info *wayland_info) { struct wl_list *infos = &wayland_info->infos; struct global_info *info; wl_list_for_each(info, infos, link) if (should_print_info(wayland_info, info)) 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); } static void print_usage(const char *name, int exit_status) { fprintf(stderr, "Usage: %s [options...] \n", name); fprintf(stderr, "\n"); fprintf(stderr, " -h|--help: Print this help and exit\n"); fprintf(stderr, " -i|--interface : Only print information about the" " Wayland globals containing in their name\n"); fprintf(stderr, "\n"); exit(exit_status); } static void parse_cmdline(struct wayland_info *info, int argc, char **argv) { int i; info->interface = NULL; for (i = 1; i < argc; i++) { if (!strcmp(argv[i], "--help") || !strcmp(argv[i], "-h")) { print_usage(argv[0], EXIT_SUCCESS); } else if (!strcmp(argv[i], "--interface") || !strcmp(argv[i], "-i")) { if (i + 1 >= argc) { fprintf(stderr, "Missing interface name\n"); print_usage(argv[0], EXIT_FAILURE); } info->interface = strdup(argv[++i]); } else { fprintf(stderr, "Unknown option %s\n", argv[i]); print_usage(argv[0], EXIT_FAILURE); } } } int main(int argc, char **argv) { struct wayland_info info; parse_cmdline(&info, argc, argv); info.display = wl_display_connect(NULL); if (!info.display) { fprintf(stderr, "failed to create display: %s\n", strerror(errno)); 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); destroy_infos(&info.infos); wl_registry_destroy(info.registry); wl_display_disconnect(info.display); return 0; }