/* * Copyright © 2023 Sergio Gómez for Collabora Ltd * * 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 "shared/helpers.h" #include "shared/xalloc.h" #include "shared/os-compatibility.h" #include "shared/cairo-util.h" #include "xdg-shell-client-protocol.h" #include "pointer-constraints-unstable-v1-client-protocol.h" #include "relative-pointer-unstable-v1-client-protocol.h" #define NUM_BUFFERS 3 #define SURFACE_WIDTH 500 #define SURFACE_HEIGHT 400 #define SUBSURFACE_WIDTH (SURFACE_WIDTH / 4) #define SUBSURFACE_HEIGHT (SURFACE_HEIGHT / 4) #define SUBSURFACE_X_POS ((SURFACE_WIDTH-SUBSURFACE_WIDTH) / 2) #define SUBSURFACE_Y_POS ((SURFACE_HEIGHT-SUBSURFACE_HEIGHT) / 2) static float rgb_surface[3] = { 1, 1, 1 }; static float rgb_subsurface[3] = { 0.5, 0.5, 0.5 }; struct buffer { struct wl_buffer *wl_buffer; bool used; void *data; cairo_surface_t *cairo_surface; }; struct shm_pool { struct wl_shm_pool *wl_shm_pool; size_t size; void *data; struct buffer buffers[NUM_BUFFERS]; }; struct surface { struct constraints *constraints; struct wl_surface *wl_surface; struct wl_callback *cb; struct wl_subsurface *wl_subsurface; struct wl_surface *wl_surface_cursor; uint32_t serial; int width, height, stride; float rgb[3]; struct { int32_t x1, y1; int32_t x2, y2; } line; struct shm_pool pool; struct buffer *current_buffer; bool needs_reset; bool needs_redraw; }; struct window { struct wl_display *wl_display; struct wl_registry *wl_registry; struct wl_compositor *wl_compositor; struct wl_subcompositor *wl_subcompositor; struct wl_seat *wl_seat; struct wl_pointer *wl_pointer; struct wl_keyboard *wl_keyboard; struct wl_shm *wl_shm; struct wl_cursor_theme *wl_cursor_theme; struct wl_cursor *wl_cursor; struct xdg_wm_base *xdg_wm_base; struct xdg_surface *xdg_surface; struct xdg_toplevel *xdg_toplevel; struct surface surface; struct surface subsurface; struct surface *surface_constrained; }; enum constraints_state { STATE_UNCONSTRAINED, STATE_CONFINED, STATE_LOCKED }; struct constraints { struct window window; struct zwp_relative_pointer_manager_v1 *zwp_relative_pointer_manager; struct zwp_pointer_constraints_v1 *zwp_pointer_constraints; struct zwp_confined_pointer_v1 *zwp_confined_pointer; struct zwp_relative_pointer_v1 *zwp_relative_pointer; struct zwp_locked_pointer_v1 *zwp_locked_pointer; bool running; bool argb8888_supported; enum constraints_state state; }; /* * Forward declarations for functions used by Wayland event handlers (since * these events are defined first). */ static void surface_redraw(struct surface *surface); static void surface_set_cursor(const struct surface *surface, bool hide); static void constraints_reset_state(struct constraints *constraints); void (*constraints_transition_state[])(struct constraints *constraints); static void relative_pointer_handle_motion(void *data, struct zwp_relative_pointer_v1 *pointer, uint32_t utime_hi, uint32_t utime_lo, wl_fixed_t dx, wl_fixed_t dy, wl_fixed_t dx_unaccel, wl_fixed_t dy_unaccel) { struct constraints *c = data; struct surface *sc = c->window.surface_constrained; sc->line.x2 = sc->line.x1 + wl_fixed_to_double(dx); sc->line.y2 = sc->line.y1 + wl_fixed_to_double(dy); c->window.surface_constrained->needs_reset = true; c->window.surface_constrained->needs_redraw = true; } static const struct zwp_relative_pointer_v1_listener relative_pointer_listener = { relative_pointer_handle_motion, }; static void pointer_locked(void *data, struct zwp_locked_pointer_v1 *locked_pointer) { struct constraints *c = data; struct surface *sc = c->window.surface_constrained; c->state = STATE_LOCKED; c->zwp_locked_pointer = locked_pointer; sc->line.x1 = sc->line.x2; sc->line.y1 = sc->line.y2; sc->needs_reset = true; sc->needs_redraw = true; surface_set_cursor(sc, true); c->zwp_relative_pointer = zwp_relative_pointer_manager_v1_get_relative_pointer( c->zwp_relative_pointer_manager, c->window.wl_pointer); zwp_relative_pointer_v1_add_listener(c->zwp_relative_pointer, &relative_pointer_listener, c); } static void pointer_unlocked(void *data, struct zwp_locked_pointer_v1 *locked_pointer) { struct constraints *c = data; constraints_reset_state(c); } static const struct zwp_locked_pointer_v1_listener locked_pointer_listener = { pointer_locked, pointer_unlocked, }; static void pointer_confined(void *data, struct zwp_confined_pointer_v1 *confined_pointer) { struct constraints *c = data; c->state = STATE_CONFINED; c->zwp_confined_pointer = confined_pointer; } static void pointer_unconfined(void *data, struct zwp_confined_pointer_v1 *confined_pointer) { struct constraints *c = data; constraints_reset_state(c); } static const struct zwp_confined_pointer_v1_listener confined_pointer_listener = { pointer_confined, pointer_unconfined, }; static void pointer_handle_enter(void *data, struct wl_pointer *pointer, uint32_t serial, struct wl_surface *surface, wl_fixed_t sx_w, wl_fixed_t sy_w) { struct constraints *c = data; struct surface *surface_constrained = (surface == c->window.surface.wl_surface) ? &c->window.surface : &c->window.subsurface; surface_constrained->serial = serial; surface_set_cursor(surface_constrained, false); c->window.surface_constrained = surface_constrained; } static void pointer_handle_leave(void *data, struct wl_pointer *pointer, uint32_t serial, struct wl_surface *wl_surface) { struct constraints *c = data; struct surface *sc = c->window.surface_constrained; sc->line.x1 = -1; sc->line.y1 = -1; sc->line.x2 = -1; sc->line.y2 = -1; sc->needs_reset = true; sc->needs_redraw = true; if (c->state != STATE_UNCONSTRAINED) constraints_reset_state(c); } static void pointer_handle_motion(void *data, struct wl_pointer *pointer, uint32_t time, wl_fixed_t sx_w, wl_fixed_t sy_w) { struct surface *sc = ((struct constraints *)data)->window.surface_constrained; sc->line.x2 = wl_fixed_to_double(sx_w); sc->line.y2 = wl_fixed_to_double(sy_w); sc->needs_redraw = true; } static void pointer_handle_button(void *data, struct wl_pointer *pointer, uint32_t serial, uint32_t time, uint32_t btn, uint32_t button_state) { struct constraints *c = data; if (button_state == WL_POINTER_BUTTON_STATE_RELEASED || btn != BTN_LEFT) return; constraints_transition_state[c->state](c); } static void pointer_handle_axis(void *data, struct wl_pointer *pointer, uint32_t time, uint32_t axis, wl_fixed_t value) { } static void pointer_handle_frame(void *data, struct wl_pointer *pointer) { } static void pointer_handle_axis_source(void *data, struct wl_pointer *pointer, uint32_t source) { } static void pointer_handle_axis_stop(void *data, struct wl_pointer *pointer, uint32_t time, uint32_t axis) { } static void pointer_handle_axis_discrete(void *data, struct wl_pointer *pointer, uint32_t axis, int32_t discrete) { } static const struct wl_pointer_listener pointer_listener = { pointer_handle_enter, pointer_handle_leave, pointer_handle_motion, pointer_handle_button, pointer_handle_axis, pointer_handle_frame, pointer_handle_axis_source, pointer_handle_axis_stop, pointer_handle_axis_discrete, }; static void keyboard_handle_keymap(void *data, struct wl_keyboard *keyboard, uint32_t format, int fd, uint32_t size) { 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) { struct constraints *c = data; if (key == KEY_ESC && state) c->running = false; } 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 const struct wl_keyboard_listener keyboard_listener = { keyboard_handle_keymap, keyboard_handle_enter, keyboard_handle_leave, keyboard_handle_key, keyboard_handle_modifiers, }; static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat, enum wl_seat_capability caps) { struct constraints *c = data; if ((caps & WL_SEAT_CAPABILITY_POINTER) && !c->window.wl_pointer) { c->window.wl_pointer = wl_seat_get_pointer(wl_seat); wl_pointer_add_listener(c->window.wl_pointer, &pointer_listener, c); } if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !c->window.wl_keyboard) { c->window.wl_keyboard = wl_seat_get_keyboard(wl_seat); wl_keyboard_add_listener(c->window.wl_keyboard, &keyboard_listener, c); } } static const struct wl_seat_listener seat_listener = { seat_handle_capabilities, }; static void handle_xdg_surface_configure(void *data, struct xdg_surface *surface, uint32_t serial) { struct constraints *c = data; xdg_surface_ack_configure(surface, serial); surface_redraw(&c->window.surface); surface_redraw(&c->window.subsurface); } static const struct xdg_surface_listener xdg_surface_listener = { handle_xdg_surface_configure, }; 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 shm_format(void *data, struct wl_shm *wl_shm, uint32_t format) { struct constraints *c = data; if (format == WL_SHM_FORMAT_ARGB8888) c->argb8888_supported = true; } struct wl_shm_listener shm_listener = { shm_format }; static void handle_global(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { struct constraints *c = data; if (strcmp(interface, "wl_compositor") == 0) { c->window.wl_compositor = wl_registry_bind(registry, name, &wl_compositor_interface, 1); } else if (strcmp(interface, "xdg_wm_base") == 0) { c->window.xdg_wm_base = wl_registry_bind(registry, name, &xdg_wm_base_interface, 1); xdg_wm_base_add_listener(c->window.xdg_wm_base, &wm_base_listener, c); } else if (strcmp(interface, "wl_shm") == 0) { c->window.wl_shm = wl_registry_bind(registry, name, &wl_shm_interface, 1); wl_shm_add_listener(c->window.wl_shm, &shm_listener, c); c->window.wl_cursor_theme = wl_cursor_theme_load(NULL, 32, c->window.wl_shm); c->window.wl_cursor = wl_cursor_theme_get_cursor(c->window.wl_cursor_theme, "left_ptr"); } else if (strcmp(interface, "wl_seat") == 0) { c->window.wl_seat = wl_registry_bind(c->window.wl_registry, name, &wl_seat_interface, 1); wl_seat_add_listener(c->window.wl_seat, &seat_listener, c); } else if (strcmp(interface, "zwp_relative_pointer_manager_v1") == 0) { c->zwp_relative_pointer_manager = wl_registry_bind(registry, name, &zwp_relative_pointer_manager_v1_interface, 1); } else if (strcmp(interface, "zwp_pointer_constraints_v1") == 0) { c->zwp_pointer_constraints = wl_registry_bind(registry, name, &zwp_pointer_constraints_v1_interface, 1); } else if (strcmp(interface, wl_subcompositor_interface.name) == 0) { c->window.wl_subcompositor = wl_registry_bind(registry, name, &wl_subcompositor_interface, 1); } } static void handle_global_remove(void *data, struct wl_registry *registry, uint32_t name) { } static const struct wl_registry_listener registry_listener = { handle_global, handle_global_remove }; static void handle_toplevel_configure(void *data, struct xdg_toplevel *xdg_toplevel, int32_t width, int32_t height, struct wl_array *state) { } static void handle_toplevel_close(void *data, struct xdg_toplevel *xdg_toplevel) { struct constraints *c = data; c->running = false; } static const struct xdg_toplevel_listener xdg_toplevel_listener = { handle_toplevel_configure, handle_toplevel_close, }; static void buffer_release(void *data, struct wl_buffer *wl_buffer) { struct buffer *buffer = data; buffer->used = false; } static const struct wl_buffer_listener buffer_listener = { buffer_release }; static void frame_done(void *data, struct wl_callback *cb, uint32_t time) { struct surface *surface = data; wl_callback_destroy(cb); surface_redraw(surface); } static const struct wl_callback_listener frame_listener = { .done = frame_done, }; static struct buffer * shm_pool_get_buffer(struct shm_pool *pool, const struct surface *surface) { size_t i, offset; struct buffer *buffer = surface->current_buffer; cairo_surface_t *prev_surface = buffer ? buffer->cairo_surface : NULL; cairo_t *cr; /* reuse current buffer if possible */ if (buffer && !buffer->used) return buffer; buffer = NULL; for (i = 0; i < ARRAY_LENGTH(pool->buffers); i++) { if (!pool->buffers[i].used) { buffer = &pool->buffers[i]; /* ARRAY_LENGTH(pool->buffers) is a factor of pool->size */ offset = pool->size / ARRAY_LENGTH(pool->buffers) * i; break; } } if (!buffer) { fprintf(stderr, "no buffers available\n"); return NULL; } if (!buffer->wl_buffer) { buffer->wl_buffer = wl_shm_pool_create_buffer(pool->wl_shm_pool, offset, surface->width, surface->height, surface->stride, WL_SHM_FORMAT_ARGB8888); wl_buffer_add_listener(buffer->wl_buffer, &buffer_listener, buffer); buffer->data = (char *)pool->data + offset; buffer->cairo_surface = cairo_image_surface_create_for_data(buffer->data, CAIRO_FORMAT_ARGB32, surface->width, surface->height, surface->stride); if (cairo_surface_status(buffer->cairo_surface) != CAIRO_STATUS_SUCCESS) { fprintf(stderr, "cairo failed to create surface\n"); wl_buffer_destroy(buffer->wl_buffer); buffer->data = NULL; return NULL; } } /* we need to preserve the contents of the old buffer, if any */ if (prev_surface) { cr = cairo_create(buffer->cairo_surface); cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); cairo_set_source_surface(cr, prev_surface, 0, 0); cairo_rectangle(cr, 0, 0, surface->width, surface->height); cairo_clip(cr); cairo_paint(cr); cairo_destroy(cr); } return buffer; } static void shm_pool_deinit(const struct shm_pool *pool) { size_t i; for (i = 0; i < ARRAY_LENGTH(pool->buffers); i++) { const struct buffer *b = &pool->buffers[i]; if (b->wl_buffer) { wl_buffer_destroy(b->wl_buffer); cairo_surface_destroy(b->cairo_surface); } } if (pool->data) { if (munmap(pool->data, pool->size) < 0) { fprintf(stderr, "failed to unmap: %s\n", strerror(errno)); } wl_shm_pool_destroy(pool->wl_shm_pool); } } static int shm_pool_init(struct shm_pool *pool, size_t size, struct wl_shm *wl_shm) { size_t pool_size = size * ARRAY_LENGTH(pool->buffers); int fd = os_create_anonymous_file(pool_size); if (fd < 0) { fprintf(stderr, "creating a buffer file for %zu B failed: %s\n", pool_size, strerror(errno)); return -1; } pool->data = mmap(NULL, pool_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (pool->data == MAP_FAILED) { fprintf(stderr, "mmap failed: %s\n", strerror(errno)); close(fd); return -1; } pool->wl_shm_pool = wl_shm_create_pool(wl_shm, fd, pool_size); close(fd); pool->size = pool_size; return 0; } static void surface_set_cursor(const struct surface *surface, bool hide) { const struct constraints *c = surface->constraints; struct wl_cursor_image *image = c->window.wl_cursor->images[0]; struct wl_buffer *buffer = wl_cursor_image_get_buffer(image); wl_pointer_set_cursor(c->window.wl_pointer, surface->serial, hide ? NULL : surface->wl_surface_cursor, image->hotspot_x, image->hotspot_y); if (!hide) { wl_surface_attach(surface->wl_surface_cursor, buffer, 0, 0); wl_surface_damage(surface->wl_surface_cursor, 0, 0, image->width, image->height); } wl_surface_commit(surface->wl_surface_cursor); } static void surface_draw_line(struct surface *surface, const enum constraints_state state) { if (surface->line.x1 != -1 && surface->line.y1 != -1) { cairo_t *cr = cairo_create(surface->current_buffer->cairo_surface); cairo_set_line_width(cr, 2.0); cairo_set_source_rgb(cr, 0, 0, 0); cairo_move_to(cr, surface->line.x1, surface->line.y1); cairo_line_to(cr, surface->line.x2, surface->line.y2); cairo_stroke(cr); cairo_destroy(cr); } if (state != STATE_LOCKED) { surface->line.x1 = surface->line.x2; surface->line.y1 = surface->line.y2; } } static void surface_reset(const struct surface *surface) { cairo_t *cr = cairo_create(surface->current_buffer->cairo_surface); cairo_rectangle(cr, 0, 0, surface->width, surface->height); cairo_set_source_rgb(cr, surface->rgb[0], surface->rgb[1], surface->rgb[2]); cairo_fill(cr); cairo_destroy(cr); } static void surface_redraw(struct surface *surface) { struct buffer *buffer; if (!(buffer = shm_pool_get_buffer(&surface->pool, surface))) return; surface->current_buffer = buffer; surface->cb = wl_surface_frame(surface->wl_surface); wl_callback_add_listener(surface->cb, &frame_listener, surface); if (surface->needs_redraw) { if (surface->needs_reset) { surface_reset(surface); surface->needs_reset = false; } surface_draw_line(surface, surface->constraints->state); surface->needs_redraw = false; buffer->used = true; wl_surface_attach(surface->wl_surface, buffer->wl_buffer, 0, 0); wl_surface_damage(surface->wl_surface, 0, 0, surface->width, surface->height); } wl_surface_commit(surface->wl_surface); } static void surface_deinit(const struct surface *surface) { shm_pool_deinit(&surface->pool); if (surface->wl_subsurface) wl_subsurface_destroy(surface->wl_subsurface); if (surface->wl_surface) wl_surface_destroy(surface->wl_surface); if (surface->wl_surface_cursor) wl_surface_destroy(surface->wl_surface_cursor); if (surface->cb) wl_callback_destroy(surface->cb); } static int surface_init(struct surface *surface, struct constraints *constraints, const float *rgb, int width, int height, const struct surface *parent_surface, int pos_x, int pos_y) { struct window *window = &constraints->window; surface->wl_surface = wl_compositor_create_surface(window->wl_compositor); surface->rgb[0] = rgb[0]; surface->rgb[1] = rgb[1]; surface->rgb[2] = rgb[2]; surface->width = width; surface->height = height; surface->stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, surface->width); surface->line.x1 = surface->line.y1 = -1; surface->line.x2 = surface->line.y2 = -1; surface->needs_reset = true; surface->needs_redraw = true; if (parent_surface) { surface->wl_subsurface = wl_subcompositor_get_subsurface(window->wl_subcompositor, surface->wl_surface, parent_surface->wl_surface); wl_subsurface_set_position(window->subsurface.wl_subsurface, pos_x, pos_y); } surface->wl_surface_cursor = wl_compositor_create_surface(window->wl_compositor); surface->constraints = constraints; return shm_pool_init(&surface->pool, surface->stride * surface->height, window->wl_shm); } static void constraints_reset_state(struct constraints *constraints) { if (constraints->zwp_confined_pointer) { zwp_confined_pointer_v1_destroy(constraints->zwp_confined_pointer); constraints->zwp_confined_pointer = NULL; } if (constraints->zwp_relative_pointer) { zwp_relative_pointer_v1_destroy(constraints->zwp_relative_pointer); constraints->zwp_relative_pointer = NULL; } if (constraints->zwp_locked_pointer) { zwp_locked_pointer_v1_destroy(constraints->zwp_locked_pointer); constraints->zwp_locked_pointer = NULL; } constraints->state = STATE_UNCONSTRAINED; } static void constraints_transition_unconstrained(struct constraints *constraints) { const struct surface *sc = constraints->window.surface_constrained; struct zwp_confined_pointer_v1 *confined_pointer; confined_pointer = zwp_pointer_constraints_v1_confine_pointer(constraints->zwp_pointer_constraints, sc->wl_surface, constraints->window.wl_pointer, NULL, ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_ONESHOT); zwp_confined_pointer_v1_add_listener(confined_pointer, &confined_pointer_listener, constraints); } static void constraints_transition_confined(struct constraints *constraints) { const struct surface *sc = constraints->window.surface_constrained; struct zwp_locked_pointer_v1 *locked_pointer; zwp_confined_pointer_v1_destroy(constraints->zwp_confined_pointer); constraints->zwp_confined_pointer = NULL; locked_pointer = zwp_pointer_constraints_v1_lock_pointer(constraints->zwp_pointer_constraints, sc->wl_surface, constraints->window.wl_pointer, NULL, ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_ONESHOT); zwp_locked_pointer_v1_add_listener(locked_pointer, &locked_pointer_listener, constraints); } static void constraints_transition_locked(struct constraints *constraints) { struct surface *sc = constraints->window.surface_constrained; constraints_reset_state(constraints); surface_set_cursor(sc, false); } void (*constraints_transition_state[])(struct constraints *constraints) = { constraints_transition_unconstrained, constraints_transition_confined, constraints_transition_locked }; static void constraints_deinit(const struct constraints *constraints) { surface_deinit(&constraints->window.subsurface); surface_deinit(&constraints->window.surface); if (constraints->zwp_locked_pointer) zwp_locked_pointer_v1_destroy(constraints->zwp_locked_pointer); if (constraints->zwp_confined_pointer) zwp_confined_pointer_v1_destroy(constraints->zwp_confined_pointer); if (constraints->zwp_relative_pointer) zwp_relative_pointer_v1_destroy(constraints->zwp_relative_pointer); if (constraints->zwp_relative_pointer_manager) zwp_relative_pointer_manager_v1_destroy(constraints->zwp_relative_pointer_manager); if (constraints->zwp_pointer_constraints) zwp_pointer_constraints_v1_destroy(constraints->zwp_pointer_constraints); if (constraints->window.xdg_toplevel) xdg_toplevel_destroy(constraints->window.xdg_toplevel); if (constraints->window.xdg_surface) xdg_surface_destroy(constraints->window.xdg_surface); if (constraints->window.xdg_wm_base) xdg_wm_base_destroy(constraints->window.xdg_wm_base); if (constraints->window.wl_cursor_theme) wl_cursor_theme_destroy(constraints->window.wl_cursor_theme); if (constraints->window.wl_shm) wl_shm_destroy(constraints->window.wl_shm); if (constraints->window.wl_keyboard) wl_keyboard_destroy(constraints->window.wl_keyboard); if (constraints->window.wl_pointer) wl_pointer_destroy(constraints->window.wl_pointer); if (constraints->window.wl_seat) wl_seat_destroy(constraints->window.wl_seat); if (constraints->window.wl_subcompositor) wl_subcompositor_destroy(constraints->window.wl_subcompositor); if (constraints->window.wl_compositor) wl_compositor_destroy(constraints->window.wl_compositor); if (constraints->window.wl_registry) wl_registry_destroy(constraints->window.wl_registry); if (constraints->window.wl_display) wl_display_disconnect(constraints->window.wl_display); } static int constraints_check_globals(const struct constraints *c) { if (!c->window.xdg_wm_base) { fprintf(stderr, "no xdg-shell found\n"); return -1; } if (!c->zwp_pointer_constraints) { fprintf(stderr, "no pointer constraints found\n"); return -1; } if (!c->zwp_relative_pointer_manager) { fprintf(stderr, "no relative pointer manager found\n"); return -1; } if (!c->window.wl_seat || !c->window.wl_pointer || !c->window.wl_keyboard) { fprintf(stderr, "no valid seat found\n"); return -1; } if (!c->argb8888_supported) { fprintf(stderr, "WL_SHM_FORMAT_ARGB8888 not supported\n"); return -1; } return 0; } static int constraints_init(struct constraints *constraints) { struct window *window = &constraints->window; window->wl_display = wl_display_connect(NULL); if (!window->wl_display) { fprintf(stderr, "failed to connect to Wayland display: %s\n", strerror(errno)); goto fail; } /* get globals */ window->wl_registry = wl_display_get_registry(window->wl_display); wl_registry_add_listener(window->wl_registry, ®istry_listener, constraints); /* double roundtrip to get all initial events */ if (wl_display_roundtrip(window->wl_display) == -1 || wl_display_roundtrip(window->wl_display) == -1) { fprintf(stderr, "wl_display_roundtrip() failed: %s\n", strerror(errno)); goto fail; } if (constraints_check_globals(constraints) == -1) { goto fail; } /* surface setup */ if (surface_init(&window->surface, constraints, rgb_surface, SURFACE_WIDTH, SURFACE_HEIGHT, NULL, 0, 0) == -1) { fprintf(stderr, "failed to initialize surface\n"); goto fail; } /* xdg surface setup */ window->xdg_surface = xdg_wm_base_get_xdg_surface(window->xdg_wm_base, window->surface.wl_surface); xdg_surface_add_listener(window->xdg_surface, &xdg_surface_listener, constraints); window->xdg_toplevel = xdg_surface_get_toplevel(window->xdg_surface); xdg_toplevel_add_listener(window->xdg_toplevel, &xdg_toplevel_listener, constraints); xdg_toplevel_set_title(window->xdg_toplevel, "simple-constraints"); xdg_toplevel_set_app_id(window->xdg_toplevel, "org.freedesktop.weston.simple-contrains"); /* sub-surface setup */ if (surface_init(&window->subsurface, constraints, rgb_subsurface, SUBSURFACE_WIDTH, SUBSURFACE_HEIGHT, &window->surface, SUBSURFACE_X_POS, SUBSURFACE_Y_POS) == -1) { fprintf(stderr, "failed to initialize sub-surface\n"); goto fail; } wl_surface_commit(window->surface.wl_surface); constraints->running = true; return 0; fail: if (constraints) constraints_deinit(constraints); return -1; } static void print_usage(void) { printf("\nDemo client that showcases the Wayland Pointer Constraints" " protocol:\n\n"); printf("The gray rectangle represents a Wayland subsurface for the" " surface in white.\n"); printf("Click on any surface to cycle between unconstrained-confined" "-locked states.\n\n"); } int main() { static struct constraints constraints; int ret = 0; print_usage(); if (constraints_init(&constraints) < 0) return -1; while (ret != -1 && constraints.running) ret = wl_display_dispatch(constraints.window.wl_display); constraints_deinit(&constraints); return ret; }