weston/clients/weston-info.c

858 lines
20 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

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

/*
* Copyright © 2012 Philipp Brüschweiler
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include "config.h"
#include <errno.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <assert.h>
#include <ctype.h>
#include <wayland-client.h>
#include "shared/helpers.h"
#include "shared/os-compatibility.h"
#include "shared/xalloc.h"
#include "shared/zalloc.h"
#include "presentation-time-client-protocol.h"
#include "linux-dmabuf-unstable-v1-client-protocol.h"
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, dont use it. */
}
static void
linux_dmabuf_handle_modifier(void *data, struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf_v1, uint32_t format, uint32_t modifier_hi, uint32_t modifier_lo)
{
struct linux_dmabuf_info *dmabuf = data;
struct linux_dmabuf_modifier *linux_dmabuf_modifier = xzalloc(sizeof *linux_dmabuf_modifier);
wl_list_insert(&dmabuf->modifiers, &linux_dmabuf_modifier->link);
linux_dmabuf_modifier->format = format;
linux_dmabuf_modifier->modifier = ((uint64_t)modifier_hi) << 32 | modifier_lo;
}
static const struct zwp_linux_dmabuf_v1_listener linux_dmabuf_listener = {
linux_dmabuf_handle_format,
linux_dmabuf_handle_modifier,
};
static void
destroy_linux_dmabuf_info(void *data)
{
struct linux_dmabuf_info *dmabuf = data;
struct linux_dmabuf_modifier *modifier, *tmp;
wl_list_for_each_safe(modifier, tmp, &dmabuf->modifiers, link) {
wl_list_remove(&modifier->link);
free(modifier);
}
zwp_linux_dmabuf_v1_destroy(dmabuf->dmabuf);
}
static void
add_linux_dmabuf_info(struct weston_info *info, uint32_t id, uint32_t version)
{
struct linux_dmabuf_info *dmabuf = xzalloc(sizeof *dmabuf);
init_global_info(info, &dmabuf->global, id, "zwp_linux_dmabuf_v1", version);
dmabuf->global.print = print_linux_dmabuf_info;
dmabuf->global.destroy = destroy_linux_dmabuf_info;
wl_list_init(&dmabuf->modifiers);
if (version >= 3) {
dmabuf->dmabuf = wl_registry_bind(info->registry,
id, &zwp_linux_dmabuf_v1_interface, 3);
zwp_linux_dmabuf_v1_add_listener(dmabuf->dmabuf, &linux_dmabuf_listener, dmabuf);
info->roundtrip_needed = true;
}
}
static void
output_handle_geometry(void *data, struct wl_output *wl_output,
int32_t x, int32_t y,
int32_t physical_width, int32_t physical_height,
int32_t subpixel,
const char *make, const char *model,
int32_t output_transform)
{
struct output_info *output = data;
output->geometry.x = x;
output->geometry.y = y;
output->geometry.physical_width = physical_width;
output->geometry.physical_height = physical_height;
output->geometry.subpixel = subpixel;
output->geometry.make = xstrdup(make);
output->geometry.model = xstrdup(model);
output->geometry.output_transform = output_transform;
}
static void
output_handle_mode(void *data, struct wl_output *wl_output,
uint32_t flags, int32_t width, int32_t height,
int32_t refresh)
{
struct output_info *output = data;
struct output_mode *mode = xmalloc(sizeof *mode);
mode->flags = flags;
mode->width = width;
mode->height = height;
mode->refresh = refresh;
wl_list_insert(output->modes.prev, &mode->link);
}
static void
output_handle_done(void *data, struct wl_output *wl_output)
{
/* don't bother waiting for this; there's no good reason a
* compositor will wait more than one roundtrip before sending
* these initial events. */
}
static void
output_handle_scale(void *data, struct wl_output *wl_output,
int32_t scale)
{
struct output_info *output = data;
output->geometry.scale = scale;
}
static const struct wl_output_listener output_listener = {
output_handle_geometry,
output_handle_mode,
output_handle_done,
output_handle_scale,
};
static void
destroy_output_info(void *data)
{
struct output_info *output = data;
struct output_mode *mode, *tmp;
wl_output_destroy(output->output);
if (output->geometry.make != NULL)
free(output->geometry.make);
if (output->geometry.model != NULL)
free(output->geometry.model);
wl_list_for_each_safe(mode, tmp, &output->modes, link) {
wl_list_remove(&mode->link);
free(mode);
}
}
static void
add_output_info(struct weston_info *info, uint32_t id, uint32_t version)
{
struct output_info *output = xzalloc(sizeof *output);
init_global_info(info, &output->global, id, "wl_output", version);
output->global.print = print_output_info;
output->global.destroy = destroy_output_info;
output->version = MIN(version, 2);
output->geometry.scale = 1;
wl_list_init(&output->modes);
output->output = wl_registry_bind(info->registry, id,
&wl_output_interface, output->version);
wl_output_add_listener(output->output, &output_listener,
output);
info->roundtrip_needed = true;
}
static void
destroy_presentation_info(void *info)
{
struct presentation_info *prinfo = info;
wp_presentation_destroy(prinfo->presentation);
}
static const char *
clock_name(clockid_t clk_id)
{
static const char *names[] = {
[CLOCK_REALTIME] = "CLOCK_REALTIME",
[CLOCK_MONOTONIC] = "CLOCK_MONOTONIC",
[CLOCK_MONOTONIC_RAW] = "CLOCK_MONOTONIC_RAW",
[CLOCK_REALTIME_COARSE] = "CLOCK_REALTIME_COARSE",
[CLOCK_MONOTONIC_COARSE] = "CLOCK_MONOTONIC_COARSE",
#ifdef CLOCK_BOOTTIME
[CLOCK_BOOTTIME] = "CLOCK_BOOTTIME",
#endif
};
if (clk_id < 0 || (unsigned)clk_id >= ARRAY_LENGTH(names))
return "unknown";
return names[clk_id];
}
static void
print_presentation_info(void *info)
{
struct presentation_info *prinfo = info;
print_global_info(info);
printf("\tpresentation clock id: %d (%s)\n",
prinfo->clk_id, clock_name(prinfo->clk_id));
}
static void
presentation_handle_clock_id(void *data, struct wp_presentation *presentation,
uint32_t clk_id)
{
struct presentation_info *prinfo = data;
prinfo->clk_id = clk_id;
}
static const struct wp_presentation_listener presentation_listener = {
presentation_handle_clock_id
};
static void
add_presentation_info(struct weston_info *info, uint32_t id, uint32_t version)
{
struct presentation_info *prinfo = xzalloc(sizeof *prinfo);
init_global_info(info, &prinfo->global, id,
wp_presentation_interface.name, version);
prinfo->global.print = print_presentation_info;
prinfo->global.destroy = destroy_presentation_info;
prinfo->clk_id = -1;
prinfo->presentation = wl_registry_bind(info->registry, id,
&wp_presentation_interface, 1);
wp_presentation_add_listener(prinfo->presentation,
&presentation_listener, prinfo);
info->roundtrip_needed = true;
}
static void
destroy_global_info(void *data)
{
}
static void
add_global_info(struct weston_info *info, uint32_t id,
const char *interface, uint32_t version)
{
struct global_info *global = xzalloc(sizeof *global);
init_global_info(info, global, id, interface, version);
global->print = print_global_info;
global->destroy = destroy_global_info;
}
static void
global_handler(void *data, struct wl_registry *registry, uint32_t id,
const char *interface, uint32_t version)
{
struct weston_info *info = data;
if (!strcmp(interface, "wl_seat"))
add_seat_info(info, id, version);
else if (!strcmp(interface, "wl_shm"))
add_shm_info(info, id, version);
else if (!strcmp(interface, "zwp_linux_dmabuf_v1"))
add_linux_dmabuf_info(info, id, version);
else if (!strcmp(interface, "wl_output"))
add_output_info(info, id, version);
else if (!strcmp(interface, wp_presentation_interface.name))
add_presentation_info(info, id, version);
else
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, &registry_listener, &info);
do {
info.roundtrip_needed = false;
wl_display_roundtrip(info.display);
} while (info.roundtrip_needed);
print_infos(&info.infos);
destroy_infos(&info.infos);
wl_registry_destroy(info.registry);
wl_display_disconnect(info.display);
return 0;
}