/******************************************************************************* * Copyright (c) 2021 OpenSynergy GmbH. * * This software may not be used in any way or distributed without * permission. All rights reserved. ******************************************************************************/ #include #include #include #include #include #include #include "agl-shell-client-protocol.h" #include "xdg-output-unstable-v1-client-protocol.h" #include "xdg-shell-client-protocol.h" struct output { struct wl_output *output; struct zxdg_output_v1 *xdg_output; char *name; struct wl_list link; }; struct wl_compositor *compositor; struct zxdg_output_manager_v1 *xdg_output_manager; struct xdg_wm_base *wm_base; struct agl_shell *agl_shell; struct wl_list output_list; struct output *output_array; unsigned output_count; static int compare_outputs(const void* a, const void* b) { struct output *output_a = (struct output *)a; struct output *output_b = (struct output *)b; return strcmp(output_a->name, output_b->name); } static void xdg_wm_base_ping(void *data, struct xdg_wm_base *shell, uint32_t serial) { xdg_wm_base_pong(shell, serial); } static const struct xdg_wm_base_listener wm_base_listener = { xdg_wm_base_ping, }; static void registry_handle_global(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { if (strcmp(interface, "wl_compositor") == 0) { compositor = wl_registry_bind(registry, name, &wl_compositor_interface, version); } else if (strcmp(interface, wl_output_interface.name) == 0) { struct output *output = calloc(1, sizeof(*output)); output->output = wl_registry_bind(registry, name, &wl_output_interface, 1); wl_list_insert(&output_list, &output->link); } else if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0) xdg_output_manager = wl_registry_bind(registry, name, &zxdg_output_manager_v1_interface, 2); else if (strcmp(interface, "xdg_wm_base") == 0) { wm_base = wl_registry_bind(registry, name, &xdg_wm_base_interface, 1); xdg_wm_base_add_listener(wm_base, &wm_base_listener, NULL); } else if (strcmp(interface, agl_shell_interface.name) == 0) agl_shell = wl_registry_bind(registry, name, &agl_shell_interface, 1); } static void registry_handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) { } static const struct wl_registry_listener registry_listener = { .global = registry_handle_global, .global_remove = registry_handle_global_remove, }; void output_logical_position(void *data, struct zxdg_output_v1 *zxdg_output_v1, int32_t x, int32_t y) {} void output_logical_size(void *data, struct zxdg_output_v1 *zxdg_output_v1, int32_t width, int32_t height) {} void output_done(void *data, struct zxdg_output_v1 *zxdg_output_v1) {} void output_name(void *data, struct zxdg_output_v1 *zxdg_output_v1, const char *name) { struct output *output = data; output->name = strdup(name); } void output_description(void *data, struct zxdg_output_v1 *zxdg_output_v1, const char *description) {} struct zxdg_output_v1_listener xdg_output_v1_listener = { .logical_position = output_logical_position, .logical_size = output_logical_size, .done = output_done, .name = output_name, .description = output_description, }; static void handle_surface_configure(void *data, struct xdg_surface *surface, uint32_t serial) { xdg_surface_ack_configure(surface, serial); } static const struct xdg_surface_listener xdg_surface_listener = { handle_surface_configure }; int main(int argc, char *argv[]) { int retval = EXIT_SUCCESS; struct wl_display *display = wl_display_connect(NULL); struct wl_registry *registry = NULL; struct wl_surface *surface = NULL; struct xdg_surface *xdg_surface = NULL; struct xdg_toplevel *toplevel = NULL; if (!display) { fprintf(stderr, "wl_display_connect failed\n"); retval = EXIT_FAILURE; goto error; } registry = wl_display_get_registry(display); if (!registry) { fprintf(stderr, "wl_display_get_registry failed\n"); retval = EXIT_FAILURE; goto error; } wl_registry_add_listener(registry, ®istry_listener, NULL); /* receive registry events */ wl_list_init(&output_list); wl_display_roundtrip(display); if (agl_shell == NULL) { fprintf(stderr, "No agl_shell extension present!\n"); retval = EXIT_FAILURE; goto error; } if (wl_list_empty(&output_list)) { fprintf(stderr, "No wayland outputs present!\n"); retval = EXIT_FAILURE; goto error; } /* receive outputs names */ struct output *output; wl_list_for_each(output, &output_list, link) { output->xdg_output = zxdg_output_manager_v1_get_xdg_output(xdg_output_manager, output->output); zxdg_output_v1_add_listener(output->xdg_output, &xdg_output_v1_listener, output); } wl_display_roundtrip(display); wl_list_for_each(output, &output_list, link) { zxdg_output_v1_destroy(output->xdg_output); output->xdg_output = NULL; } /* for sorting, convert wl_list to array */ output_array = malloc(wl_list_length(&output_list) * sizeof(*output_array)); output_count = 0; wl_list_for_each(output, &output_list, link) output_array[output_count++] = *output; struct output *tmp; wl_list_for_each_safe(output, tmp, &output_list, link) wl_list_remove(&output->link); qsort(output_array, output_count, sizeof(*output_array), compare_outputs); /* without background set, activate by default does not work */ surface = wl_compositor_create_surface(compositor); xdg_surface = xdg_wm_base_get_xdg_surface(wm_base, surface); xdg_surface_add_listener(xdg_surface, &xdg_surface_listener, NULL); /* xdg surface must have a role: assign toplevel */ toplevel = xdg_surface_get_toplevel(xdg_surface); xdg_toplevel_set_app_id(toplevel, "background"); wl_surface_commit(surface); agl_shell_set_background(agl_shell, surface, output_array[0].output); for (unsigned i = 0; i < output_count; i++) { wl_output_destroy(output_array[i].output); free(output_array[i].name); } free(output_array); agl_shell_ready(agl_shell); while (wl_display_dispatch(display) != -1) { /* This space deliberately left blank */ } error: if (toplevel) xdg_toplevel_destroy(toplevel); if (xdg_surface) xdg_surface_destroy(xdg_surface); if (surface) wl_surface_destroy(surface); if (agl_shell) agl_shell_destroy(agl_shell); if (registry) wl_registry_destroy(registry); wl_display_disconnect(display); return retval; }