commit 1206ab41dba85f454be3d762897937317b456317 Author: Andrew Hamilton Date: Wed Aug 5 11:52:07 2026 +1000 Tabby: C Wayland compositor with xdg-shell tabbed windows Single-file Wayland compositor presenting client surfaces as tabs in a GTK3 window. Implements wl_compositor, wl_shm/shared memory, xdg-shell, wl_seat/keyboard/pointer, and launches apps from tab-commands. - Validate client buffer geometry against pool size at create_buffer and commit time to prevent out-of-bounds reads from malformed buffers. - Release client shm buffers immediately after deep-copying pixels, replacing the fragile foot/GTK3 deferred-release state machine. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..59e8b67 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +*.o +*.so +tabbed-wayland +core.* diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..ca169c0 --- /dev/null +++ b/Makefile @@ -0,0 +1,20 @@ +SAN ?= +CFLAGS = -Wall -Wextra -std=c11 -g $(SAN) +SERVER_CFLAGS = $(CFLAGS) $(shell pkg-config --cflags gtk+-3.0 wayland-server xkbcommon) +SERVER_LIBS = $(shell pkg-config --libs gtk+-3.0 wayland-server xkbcommon) -lm $(SAN) + +.PHONY: all clean + +all: tabbed-wayland + +xdg-shell-protocol.o: xdg-shell-protocol.c xdg-shell-server.h + $(CC) $(CFLAGS) -c -o $@ $< + +tabbed-wayland.o: tabbed-wayland.c xdg-shell-server.h + $(CC) $(SERVER_CFLAGS) -c -o $@ $< + +tabbed-wayland: tabbed-wayland.o xdg-shell-protocol.o + $(CC) -o $@ $^ $(SERVER_LIBS) + +clean: + rm -f *.o tabbed-wayland diff --git a/tab-commands b/tab-commands new file mode 100644 index 0000000..a20eaf6 --- /dev/null +++ b/tab-commands @@ -0,0 +1,3 @@ +foot +galculator +geany diff --git a/tabbed-wayland.c b/tabbed-wayland.c new file mode 100644 index 0000000..04ccc82 --- /dev/null +++ b/tabbed-wayland.c @@ -0,0 +1,1784 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "xdg-shell-server.h" +#include +#include + +#define MAX_APPS 8 + +static const char *default_apps[] = { + NULL /* no default apps */ +}; + +struct tabby_surface; +struct tabby_compositor; + +struct tabby_pool { + int fd; + size_t size; + unsigned char *data; +}; + +struct tabby_buffer { + struct wl_resource *resource; + struct tabby_pool *pool; + int32_t offset; + int32_t width, height, stride; + uint32_t format; +}; + +struct tabby_output { + struct wl_global *global; + struct wl_list resources; + int width, height; + unsigned char *data; + size_t stride; +}; + +struct tabby_surface { + struct wl_resource *resource; + struct wl_list link; + struct tabby_buffer *buffer; + bool has_buffer; + struct wl_listener surface_destroy_listener; + struct wl_resource *xdg_surface; + struct wl_resource *xdg_toplevel; + char *title; + char *app_id; + bool configured; + uint32_t configure_serial; + struct wl_resource *frame_callback; + struct wl_client *client; + GtkWidget *tab_label; + int tab_index; + unsigned char *pixels; + int pixel_width, pixel_height, pixel_stride; + int geom_x, geom_y, geom_width, geom_height; +}; + +struct tabby_app { + char *cmd; + pid_t pid; + GtkWidget *tab_label; + int tab_index; + struct tabby_surface *surface; +}; + +struct tabby_compositor { + struct wl_display *display; + struct wl_event_loop *loop; + struct wl_list surfaces; + struct tabby_output output; + struct tabby_surface *active_surface; + struct tabby_app *apps[MAX_APPS]; + int num_apps; + struct wl_list input_tracks; + GtkWidget *window; + GtkWidget *notebook; + GtkWidget *canvas; + GtkWidget *statusbar; + uint32_t serial; + bool clients_launched; + bool resize_pending; + int resize_w, resize_h; + /* xkb */ + struct xkb_context *xkb_ctx; + struct xkb_keymap *xkb_keymap; + struct xkb_state *xkb_state; + char *xkb_keymap_str; + int xkb_keymap_fd; + int xkb_keymap_size; + /* keyboard focus */ + struct tabby_surface *kbd_focus; + struct wl_resource *kbd_resource; + /* pointer tracking */ + bool pointer_entered; + double pointer_x, pointer_y; + uint32_t input_serial; +}; + +static struct tabby_compositor compositor; + +/* Forward declarations for interface implementations */ +static const struct wl_compositor_interface compositor_impl; +static const struct wl_surface_interface surface_impl; +static const struct wl_region_interface region_impl; +static const struct wl_shm_interface shm_impl; +static const struct wl_shm_pool_interface pool_impl; +static const struct xdg_wm_base_interface xdg_wm_base_impl; +static const struct xdg_surface_interface xdg_surface_impl; +static const struct xdg_toplevel_interface xdg_toplevel_impl; +static const struct xdg_popup_interface xdg_popup_impl; +static const struct wl_seat_interface seat_impl; +static const struct wl_subcompositor_interface subcompositor_impl; +static const struct wl_data_device_manager_interface data_device_manager_impl; +static const struct wl_pointer_interface pointer_impl; +static const struct wl_keyboard_interface keyboard_impl; +static const struct wl_touch_interface touch_impl; +static const struct wl_subsurface_interface subsurface_impl; +static const struct wl_seat_interface seat_impl; +static const struct wl_buffer_interface buffer_impl; +static const struct xdg_positioner_interface positioner_impl; +static void launch_client(const char *cmd, int tab_index); + +/* surface destroy callback */ +static void surface_destroyed_cb(struct wl_listener *listener, void *data) { + (void)data; + struct tabby_surface *surface = wl_container_of(listener, surface, surface_destroy_listener); + wl_list_remove(&surface->link); + if (compositor.active_surface == surface) + compositor.active_surface = NULL; + free(surface->title); + free(surface->app_id); + free(surface->pixels); + free(surface); +} + +/* ===================== wl_compositor ===================== */ + +static void compositor_create_surface(struct wl_client *client, + struct wl_resource *resource, + uint32_t id) +{ + struct wl_resource *surf = wl_resource_create(client, + &wl_surface_interface, wl_resource_get_version(resource), id); + if (!surf) { wl_client_post_no_memory(client); return; } + + struct tabby_surface *surface = calloc(1, sizeof(*surface)); + if (!surface) { wl_resource_destroy(surf); return; } + + surface->resource = surf; + surface->client = client; + wl_resource_set_user_data(surf, surface); + wl_resource_set_implementation(surf, &surface_impl, surface, NULL); + + surface->surface_destroy_listener.notify = surface_destroyed_cb; + wl_resource_add_destroy_listener(surf, &surface->surface_destroy_listener); + wl_list_insert(&compositor.surfaces, &surface->link); +} + +static void compositor_create_region(struct wl_client *client, + struct wl_resource *resource, + uint32_t id) +{ + struct wl_resource *reg = wl_resource_create(client, + &wl_region_interface, wl_resource_get_version(resource), id); + if (!reg) { wl_client_post_no_memory(client); return; } + wl_resource_set_implementation(reg, ®ion_impl, NULL, NULL); +} + +static const struct wl_compositor_interface compositor_impl = { + .create_surface = compositor_create_surface, + .create_region = compositor_create_region, +}; + +/* ===================== wl_surface ===================== */ + +static void surface_destroy(struct wl_client *client, struct wl_resource *resource) { + (void)client; + wl_resource_destroy(resource); +} + +static void surface_attach(struct wl_client *client, struct wl_resource *resource, + struct wl_resource *buffer, int32_t x, int32_t y) +{ + (void)client; (void)x; (void)y; + struct tabby_surface *surface = wl_resource_get_user_data(resource); + if (buffer) { + struct tabby_buffer *b = wl_resource_get_user_data(buffer); + if (b) { + surface->buffer = b; + surface->has_buffer = true; + } else { + surface->buffer = NULL; + surface->has_buffer = false; + } + } else { + surface->buffer = NULL; + surface->has_buffer = false; + } +} + +static void surface_damage(struct wl_client *client, struct wl_resource *resource, + int32_t x, int32_t y, int32_t w, int32_t h) +{ + (void)client; (void)resource; (void)x; (void)y; (void)w; (void)h; +} + +static void surface_frame(struct wl_client *client, struct wl_resource *resource, + uint32_t callback) +{ + struct tabby_surface *surface = wl_resource_get_user_data(resource); + if (surface->frame_callback) { + wl_resource_destroy(surface->frame_callback); + surface->frame_callback = NULL; + } + surface->frame_callback = wl_resource_create(client, + &wl_callback_interface, wl_resource_get_version(resource), callback); + if (!surface->frame_callback) + wl_client_post_no_memory(client); +} + +static void surface_set_opaque_region(struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *region) +{ + (void)client; (void)resource; (void)region; +} + +static void surface_set_input_region(struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *region) +{ + (void)client; (void)resource; (void)region; +} + +static void copy_buffer_to_display(struct tabby_surface *surface) { + struct tabby_output *out = &compositor.output; + if (!out->data || !surface->pixels || out->width <= 0 || out->height <= 0 || out->stride <= 0) + return; + + size_t out_size = (size_t)out->height * (size_t)out->stride; + memset(out->data, 0x22, out_size); + + int src_x = (surface->geom_width > 0) ? surface->geom_x : 0; + int src_y = (surface->geom_height > 0) ? surface->geom_y : 0; + int src_w = (surface->geom_width > 0) ? surface->geom_width : surface->pixel_width; + int src_h = (surface->geom_height > 0) ? surface->geom_height : surface->pixel_height; + + if (src_x + src_w > surface->pixel_width) src_w = surface->pixel_width - src_x; + if (src_y + src_h > surface->pixel_height) src_h = surface->pixel_height - src_y; + if (src_w < 0) src_w = 0; + if (src_h < 0) src_h = 0; + + int cw = (src_w < out->width) ? src_w : out->width; + int ch = (src_h < out->height) ? src_h : out->height; + + if (cw <= 0 || ch <= 0) return; + if (surface->pixel_stride <= 0 || out->stride <= 0) return; + if (surface->pixels == NULL || out->data == NULL) return; + if (src_y + ch > surface->pixel_height) return; + + for (int y = 0; y < ch; y++) { + void *dst = out->data + y * out->stride; + void *src = surface->pixels + (src_y + y) * surface->pixel_stride + src_x * 4; + if ((unsigned char*)dst < (unsigned char*)out->data || + (unsigned char*)dst + cw*4 > (unsigned char*)out->data + out->height*out->stride) break; + if ((unsigned char*)src < (unsigned char*)surface->pixels || + (unsigned char*)src + cw*4 > (unsigned char*)surface->pixels + surface->pixel_height*surface->pixel_stride) break; + memcpy(dst, src, cw * 4); + } +} + +static void surface_commit(struct wl_client *client, struct wl_resource *resource) { + (void)client; + struct tabby_surface *surface = wl_resource_get_user_data(resource); + if (!surface) return; + /* xdg_shell: first commit without buffer sends configure */ + if (surface->xdg_toplevel && !surface->configured) { + surface->configured = true; + surface->configure_serial = ++compositor.serial; + + if (surface->xdg_surface) + xdg_surface_send_configure(surface->xdg_surface, surface->configure_serial); + + struct wl_array states; + wl_array_init(&states); + uint32_t *s = wl_array_add(&states, sizeof(uint32_t)); + if (s) *s = XDG_TOPLEVEL_STATE_ACTIVATED; + xdg_toplevel_send_configure(surface->xdg_toplevel, + compositor.output.width, compositor.output.height, &states); + wl_array_release(&states); + + if (surface->tab_label && surface->title) + gtk_label_set_text(GTK_LABEL(surface->tab_label), surface->title); + return; + } + + /* Buffer commit — cache pixels for safe access later */ + if (surface->has_buffer && surface->buffer) { + struct tabby_buffer *buf = surface->buffer; + if (buf->pool && buf->pool->data) { + const size_t bpp = 4; + /* Defensive check: never read outside the mapped pool even if a + client sent a bad attach after a valid create_buffer. */ + if (buf->offset >= 0 && buf->stride > 0 && + buf->width > 0 && buf->height > 0 && + buf->stride >= buf->width * (int)bpp && + (size_t)buf->offset + + (size_t)buf->stride * (size_t)buf->height <= buf->pool->size) { + surface->pixel_width = buf->width; + surface->pixel_height = buf->height; + surface->pixel_stride = buf->stride; + size_t size = (size_t)buf->stride * (size_t)buf->height; + surface->pixels = malloc(size); + if (surface->pixels) + memcpy(surface->pixels, buf->pool->data + buf->offset, size); + } else { + /* Invalid geometry: drop the frame, keep a clean surface. */ + free(surface->pixels); + surface->pixels = NULL; + wl_client_post_implementation_error(surface->client, + "invalid buffer geometry on commit"); + } + /* We deep-copy the pixels above, so the client's buffer is no + longer referenced after commit. Release it immediately: per the + wl_shm contract this is valid and lets single- and multi-buffer + clients (foot, GTK3, etc.) recycle buffers without us tracking + a fragile deferred-release state. */ + wl_buffer_send_release(buf->resource); + } + if (surface == compositor.active_surface) { + copy_buffer_to_display(surface); + gtk_widget_queue_draw(compositor.canvas); + } + } else { + free(surface->pixels); + surface->pixels = NULL; + if (surface == compositor.active_surface) { + struct tabby_output *o = &compositor.output; + if (o->data && o->height > 0 && o->stride > 0) + memset(o->data, 0x22, (size_t)o->height * (size_t)o->stride); + gtk_widget_queue_draw(compositor.canvas); + } + } + + /* frame callback */ + if (surface->frame_callback) { + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + uint32_t ms = ts.tv_sec * 1000 + ts.tv_nsec / 1000000; + wl_callback_send_done(surface->frame_callback, ms); + wl_resource_destroy(surface->frame_callback); + surface->frame_callback = NULL; + } +} + +static void surface_set_buffer_scale(struct wl_client *client, + struct wl_resource *resource, + int32_t scale) +{ + (void)client; (void)resource; (void)scale; +} + +static void surface_set_buffer_transform(struct wl_client *client, + struct wl_resource *resource, + int32_t transform) +{ + (void)client; (void)resource; (void)transform; +} + +static void surface_damage_buffer(struct wl_client *client, + struct wl_resource *resource, + int32_t x, int32_t y, int32_t w, int32_t h) +{ + (void)client; (void)resource; (void)x; (void)y; (void)w; (void)h; +} + +static const struct wl_surface_interface surface_impl = { + .destroy = surface_destroy, + .attach = surface_attach, + .damage = surface_damage, + .frame = surface_frame, + .set_opaque_region = surface_set_opaque_region, + .set_input_region = surface_set_input_region, + .commit = surface_commit, + .set_buffer_transform = surface_set_buffer_transform, + .set_buffer_scale = surface_set_buffer_scale, + .damage_buffer = surface_damage_buffer, +}; + +/* ===================== wl_region ===================== */ + +static void region_destroy(struct wl_client *client, struct wl_resource *resource) { + (void)client; + wl_resource_destroy(resource); +} + +static void region_add(struct wl_client *client, struct wl_resource *resource, + int32_t x, int32_t y, int32_t w, int32_t h) +{ + (void)client; (void)resource; (void)x; (void)y; (void)w; (void)h; +} + +static void region_subtract(struct wl_client *client, struct wl_resource *resource, + int32_t x, int32_t y, int32_t w, int32_t h) +{ + (void)client; (void)resource; (void)x; (void)y; (void)w; (void)h; +} + +static const struct wl_region_interface region_impl = { + .destroy = region_destroy, + .add = region_add, + .subtract = region_subtract, +}; + +/* ===================== wl_shm ===================== */ + +static void pool_destroy_resource(struct wl_resource *resource) { + struct tabby_pool *pool = wl_resource_get_user_data(resource); + if (!pool) return; + /* Clear any surface caches that reference this pool's buffers */ + struct tabby_surface *s; + wl_list_for_each(s, &compositor.surfaces, link) { + if (s->buffer && s->buffer->pool == pool) { + s->buffer = NULL; + s->has_buffer = false; + free(s->pixels); + s->pixels = NULL; + } + } + munmap(pool->data, pool->size); + close(pool->fd); + free(pool); +} + +static void buffer_destroy_request(struct wl_client *client, struct wl_resource *resource) { + (void)client; + wl_resource_destroy(resource); +} + +static void buffer_destroy_resource(struct wl_resource *resource) { + struct tabby_buffer *buf = wl_resource_get_user_data(resource); + struct tabby_surface *s; + wl_list_for_each(s, &compositor.surfaces, link) { + if (s->buffer == buf) { + s->buffer = NULL; + s->has_buffer = false; + } + } + free(buf); +} + +static const struct wl_buffer_interface buffer_impl = { + .destroy = buffer_destroy_request, +}; + +static void pool_destroy(struct wl_client *client, struct wl_resource *resource) { + (void)client; + wl_resource_destroy(resource); +} + +static void pool_resize(struct wl_client *client, struct wl_resource *resource, + int32_t size) +{ + (void)client; + struct tabby_pool *pool = wl_resource_get_user_data(resource); + if (size <= (int32_t)pool->size) return; + unsigned char *ndata = mremap(pool->data, pool->size, size, MREMAP_MAYMOVE); + if (ndata == MAP_FAILED) return; + pool->data = ndata; + pool->size = size; +} + +static void pool_create_buffer(struct wl_client *client, struct wl_resource *resource, + uint32_t id, int32_t offset, + int32_t width, int32_t height, + int32_t stride, uint32_t format) +{ + (void)client; + struct tabby_pool *pool = wl_resource_get_user_data(resource); + + if (format != WL_SHM_FORMAT_ARGB8888 && format != WL_SHM_FORMAT_XRGB8888) { + wl_resource_post_error(resource, WL_SHM_ERROR_INVALID_FORMAT, + "unsupported format %u", format); + return; + } + + /* Validate the client-supplied layout against the pool before accepting + it. Any negative size/offset or a stride smaller than one scanline per + BPP is malformed; the buffer must also fit inside the pool. Rejecting + here prevents out-of-bounds reads at commit time (see surface_commit). */ + const int bpp = 4; + if (offset < 0 || width <= 0 || height <= 0 || + stride < width * bpp) { + wl_resource_post_error(resource, WL_SHM_ERROR_INVALID_STRIDE, + "invalid buffer geometry (%d,%d,%d,off %d)", + width, height, stride, offset); + return; + } + if ((size_t)stride * (size_t)height > SIZE_MAX || + (size_t)offset > (size_t)pool->size || + (size_t)offset + (size_t)stride * (size_t)height > (size_t)pool->size) { + wl_resource_post_error(resource, WL_SHM_ERROR_INVALID_STRIDE, + "buffer exceeds pool size"); + return; + } + + struct wl_resource *buf_res = wl_resource_create(client, + &wl_buffer_interface, wl_resource_get_version(resource), id); + if (!buf_res) { wl_client_post_no_memory(client); return; } + + struct tabby_buffer *buf = calloc(1, sizeof(*buf)); + if (!buf) { wl_resource_destroy(buf_res); return; } + + buf->resource = buf_res; + buf->pool = pool; + buf->offset = offset; + buf->width = width; + buf->height = height; + buf->stride = stride; + buf->format = format; + wl_resource_set_user_data(buf_res, buf); + wl_resource_set_implementation(buf_res, &buffer_impl, buf, buffer_destroy_resource); +} + +static const struct wl_shm_pool_interface pool_impl = { + .destroy = pool_destroy, + .resize = pool_resize, + .create_buffer = pool_create_buffer, +}; + +static void shm_create_pool(struct wl_client *client, struct wl_resource *resource, + uint32_t id, int32_t fd, int32_t size) +{ + (void)client; + struct tabby_pool *pool = malloc(sizeof(*pool)); + if (!pool) { close(fd); wl_client_post_no_memory(client); return; } + + pool->fd = fd; + pool->size = size; + pool->data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (pool->data == MAP_FAILED) { + close(fd); + free(pool); + wl_client_post_no_memory(client); + return; + } + + struct wl_resource *pool_res = wl_resource_create(client, + &wl_shm_pool_interface, wl_resource_get_version(resource), id); + if (!pool_res) { + munmap(pool->data, size); close(fd); free(pool); + wl_client_post_no_memory(client); + return; + } + wl_resource_set_implementation(pool_res, &pool_impl, pool, pool_destroy_resource); +} + +static const struct wl_shm_interface shm_impl = { + .create_pool = shm_create_pool, +}; + +/* ===================== xdg_wm_base ===================== */ + +static void xdg_wm_base_destroy(struct wl_client *client, struct wl_resource *resource) { + (void)client; + wl_resource_destroy(resource); +} + +static void positioner_destroy(struct wl_client *c, struct wl_resource *r) { (void)c; wl_resource_destroy(r); } +static void positioner_set_size(struct wl_client *c, struct wl_resource *r, int32_t w, int32_t h) { (void)c;(void)r;(void)w;(void)h; } +static void positioner_set_anchor_rect(struct wl_client *c, struct wl_resource *r, int32_t x, int32_t y, int32_t w, int32_t h) { (void)c;(void)r;(void)x;(void)y;(void)w;(void)h; } +static void positioner_set_anchor(struct wl_client *c, struct wl_resource *r, uint32_t a) { (void)c;(void)r;(void)a; } +static void positioner_set_gravity(struct wl_client *c, struct wl_resource *r, uint32_t g) { (void)c;(void)r;(void)g; } +static void positioner_set_constraint_adjustment(struct wl_client *c, struct wl_resource *r, uint32_t ca) { (void)c;(void)r;(void)ca; } +static void positioner_set_offset(struct wl_client *c, struct wl_resource *r, int32_t x, int32_t y) { (void)c;(void)r;(void)x;(void)y; } +static void positioner_set_reactive(struct wl_client *c, struct wl_resource *r) { (void)c;(void)r; } +static void positioner_set_parent_size(struct wl_client *c, struct wl_resource *r, int32_t w, int32_t h) { (void)c;(void)r;(void)w;(void)h; } +static void positioner_set_parent_configure(struct wl_client *c, struct wl_resource *r, uint32_t s) { (void)c;(void)r;(void)s; } + +static const struct xdg_positioner_interface positioner_impl = { + .destroy = positioner_destroy, + .set_size = positioner_set_size, + .set_anchor_rect = positioner_set_anchor_rect, + .set_anchor = positioner_set_anchor, + .set_gravity = positioner_set_gravity, + .set_constraint_adjustment = positioner_set_constraint_adjustment, + .set_offset = positioner_set_offset, + .set_reactive = positioner_set_reactive, + .set_parent_size = positioner_set_parent_size, + .set_parent_configure = positioner_set_parent_configure, +}; + +static void xdg_wm_base_create_positioner(struct wl_client *client, + struct wl_resource *resource, + uint32_t id) +{ + struct wl_resource *pos = wl_resource_create(client, + &xdg_positioner_interface, wl_resource_get_version(resource), id); + if (!pos) { wl_client_post_no_memory(client); return; } + wl_resource_set_implementation(pos, &positioner_impl, NULL, NULL); +} + +static void xdg_wm_base_get_xdg_surface(struct wl_client *client, + struct wl_resource *resource, + uint32_t id, + struct wl_resource *surface_resource) +{ + (void)client; + struct tabby_surface *surface = wl_resource_get_user_data(surface_resource); + if (!surface) return; + + if (surface->xdg_surface) { + wl_resource_post_error(surface_resource, + XDG_WM_BASE_ERROR_ROLE, "surface already has a role"); + return; + } + + struct wl_resource *xdg_res = wl_resource_create(client, + &xdg_surface_interface, wl_resource_get_version(resource), id); + if (!xdg_res) { wl_client_post_no_memory(client); return; } + + surface->xdg_surface = xdg_res; + wl_resource_set_user_data(xdg_res, surface); + wl_resource_set_implementation(xdg_res, &xdg_surface_impl, surface, NULL); +} + +static void xdg_wm_base_pong(struct wl_client *client, + struct wl_resource *resource, + uint32_t serial) +{ + (void)client; (void)resource; (void)serial; +} + +static const struct xdg_wm_base_interface xdg_wm_base_impl = { + .destroy = xdg_wm_base_destroy, + .create_positioner = xdg_wm_base_create_positioner, + .get_xdg_surface = xdg_wm_base_get_xdg_surface, + .pong = xdg_wm_base_pong, +}; + + + +/* ===================== xdg_surface ===================== */ + +static void xdg_surface_destroy(struct wl_client *client, struct wl_resource *resource) { + (void)client; + wl_resource_destroy(resource); +} + +static void xdg_surface_get_toplevel(struct wl_client *client, + struct wl_resource *resource, + uint32_t id) +{ + struct tabby_surface *surface = wl_resource_get_user_data(resource); + if (!surface) return; + + struct wl_resource *toplevel = wl_resource_create(client, + &xdg_toplevel_interface, wl_resource_get_version(resource), id); + if (!toplevel) { wl_client_post_no_memory(client); return; } + + surface->xdg_toplevel = toplevel; + wl_resource_set_user_data(toplevel, surface); + wl_resource_set_implementation(toplevel, &xdg_toplevel_impl, surface, NULL); + + /* Associate with an empty app slot */ + for (int i = 0; i < compositor.num_apps; i++) { + if (compositor.apps[i]->surface == NULL) { + compositor.apps[i]->surface = surface; + surface->tab_index = compositor.apps[i]->tab_index; + surface->tab_label = compositor.apps[i]->tab_label; + break; + } + } + + /* Make first surface active */ + if (!compositor.active_surface) + compositor.active_surface = surface; +} + +static void xdg_surface_get_popup(struct wl_client *client, + struct wl_resource *resource, + uint32_t id, + struct wl_resource *parent, + struct wl_resource *positioner) +{ + (void)parent; (void)positioner; + struct wl_resource *popup = wl_resource_create(client, + &xdg_popup_interface, wl_resource_get_version(resource), id); + if (!popup) { wl_client_post_no_memory(client); return; } + wl_resource_set_implementation(popup, &xdg_popup_impl, NULL, NULL); +} + +static void xdg_surface_set_window_geometry(struct wl_client *client, + struct wl_resource *resource, + int32_t x, int32_t y, + int32_t width, int32_t height) +{ + (void)client; + struct tabby_surface *surface = wl_resource_get_user_data(resource); + if (!surface) return; + surface->geom_x = x; + surface->geom_y = y; + surface->geom_width = width; + surface->geom_height = height; +} + +static void xdg_surface_ack_configure(struct wl_client *client, + struct wl_resource *resource, + uint32_t serial) +{ + (void)client; (void)resource; (void)serial; +} + +static const struct xdg_surface_interface xdg_surface_impl = { + .destroy = xdg_surface_destroy, + .get_toplevel = xdg_surface_get_toplevel, + .get_popup = xdg_surface_get_popup, + .set_window_geometry = xdg_surface_set_window_geometry, + .ack_configure = xdg_surface_ack_configure, +}; + +/* ===================== xdg_toplevel ===================== */ + +static void xdg_toplevel_destroy(struct wl_client *client, struct wl_resource *resource) { + (void)client; + wl_resource_destroy(resource); +} + +static void xdg_toplevel_set_parent(struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *parent) +{ + (void)client; (void)resource; (void)parent; +} + +static void xdg_toplevel_set_title(struct wl_client *client, + struct wl_resource *resource, + const char *title) +{ + (void)client; + struct tabby_surface *surface = wl_resource_get_user_data(resource); + if (!surface) return; + free(surface->title); + surface->title = g_strdup(title); + + if (surface->tab_label && title && title[0]) + gtk_label_set_text(GTK_LABEL(surface->tab_label), title); +} + +static void xdg_toplevel_set_app_id(struct wl_client *client, + struct wl_resource *resource, + const char *app_id) +{ + (void)client; + struct tabby_surface *surface = wl_resource_get_user_data(resource); + if (!surface) return; + free(surface->app_id); + surface->app_id = g_strdup(app_id); +} + +static void xdg_toplevel_show_window_menu(struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *seat, + uint32_t serial, + int32_t x, int32_t y) +{ + (void)client; (void)resource; (void)seat; (void)serial; (void)x; (void)y; +} + +static void xdg_toplevel_move(struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *seat, + uint32_t serial) +{ + (void)client; (void)resource; (void)seat; (void)serial; +} + +static void xdg_toplevel_resize(struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *seat, + uint32_t serial, + uint32_t edges) +{ + (void)client; (void)resource; (void)seat; (void)serial; (void)edges; +} + +static void xdg_toplevel_set_max_size(struct wl_client *client, + struct wl_resource *resource, + int32_t width, int32_t height) +{ + (void)client; (void)resource; (void)width; (void)height; +} + +static void xdg_toplevel_set_min_size(struct wl_client *client, + struct wl_resource *resource, + int32_t width, int32_t height) +{ + (void)client; (void)resource; (void)width; (void)height; +} + +static void xdg_toplevel_set_maximized(struct wl_client *client, + struct wl_resource *resource) +{ + (void)client; (void)resource; +} +static void xdg_toplevel_unset_maximized(struct wl_client *client, + struct wl_resource *resource) +{ + (void)client; (void)resource; +} +static void xdg_toplevel_set_fullscreen(struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *output) +{ + (void)client; (void)resource; (void)output; +} +static void xdg_toplevel_unset_fullscreen(struct wl_client *client, + struct wl_resource *resource) +{ + (void)client; (void)resource; +} +static void xdg_toplevel_set_minimized(struct wl_client *client, + struct wl_resource *resource) +{ + (void)client; (void)resource; +} + +static const struct xdg_toplevel_interface xdg_toplevel_impl = { + .destroy = xdg_toplevel_destroy, + .set_parent = xdg_toplevel_set_parent, + .set_title = xdg_toplevel_set_title, + .set_app_id = xdg_toplevel_set_app_id, + .show_window_menu = xdg_toplevel_show_window_menu, + .move = xdg_toplevel_move, + .resize = xdg_toplevel_resize, + .set_max_size = xdg_toplevel_set_max_size, + .set_min_size = xdg_toplevel_set_min_size, + .set_maximized = xdg_toplevel_set_maximized, + .unset_maximized = xdg_toplevel_unset_maximized, + .set_fullscreen = xdg_toplevel_set_fullscreen, + .unset_fullscreen = xdg_toplevel_unset_fullscreen, + .set_minimized = xdg_toplevel_set_minimized, +}; + +/* ===================== xdg_popup ===================== */ + +static void xdg_popup_destroy(struct wl_client *client, struct wl_resource *resource) { + (void)client; + wl_resource_destroy(resource); +} + +static void xdg_popup_grab(struct wl_client *client, struct wl_resource *resource, + struct wl_resource *seat, uint32_t serial) +{ + (void)client; (void)resource; (void)seat; (void)serial; +} + +static void xdg_popup_reposition(struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *positioner, + uint32_t token) +{ + (void)client; (void)resource; (void)positioner; (void)token; +} + +static const struct xdg_popup_interface xdg_popup_impl = { + .destroy = xdg_popup_destroy, + .grab = xdg_popup_grab, + .reposition = xdg_popup_reposition, +}; + +/* ===================== wl_output ===================== */ + +static void output_send_all(struct wl_resource *res) { + wl_output_send_geometry(res, 0, 0, + compositor.output.width * 6, compositor.output.height * 6, + WL_OUTPUT_SUBPIXEL_UNKNOWN, + "Tabby Compositor", "Virtual Output", + WL_OUTPUT_TRANSFORM_NORMAL); + wl_output_send_mode(res, + WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED, + compositor.output.width, compositor.output.height, 60000); + if (wl_resource_get_version(res) >= WL_OUTPUT_SCALE_SINCE_VERSION) + wl_output_send_scale(res, 1); + if (wl_resource_get_version(res) >= WL_OUTPUT_DONE_SINCE_VERSION) + wl_output_send_done(res); +} + +static void output_bind(struct wl_client *client, void *data, + uint32_t version, uint32_t id) +{ + (void)data; + struct wl_resource *res = wl_resource_create(client, + &wl_output_interface, version < 2 ? version : 2, id); + if (!res) { wl_client_post_no_memory(client); return; } + wl_resource_set_user_data(res, &compositor.output); + wl_list_insert(&compositor.output.resources, wl_resource_get_link(res)); + output_send_all(res); +} + +/* ===================== wl_seat ===================== */ + +static void seat_destroy(struct wl_client *client, struct wl_resource *resource) { + (void)client; + wl_resource_destroy(resource); +} + +/* ===================== Input tracking ===================== */ + +struct input_track { + struct wl_client *client; + struct wl_resource *kbd; + struct wl_resource *ptr; + struct wl_listener kbd_destroy; + struct wl_listener ptr_destroy; + struct wl_list link; +}; + +static void input_track_kbd_destroy(struct wl_listener *l, void *data) { + (void)data; + struct input_track *t = wl_container_of(l, t, kbd_destroy); + t->kbd = NULL; +} +static void input_track_ptr_destroy(struct wl_listener *l, void *data) { + (void)data; + struct input_track *t = wl_container_of(l, t, ptr_destroy); + t->ptr = NULL; +} + +static struct input_track *get_input_track(struct wl_client *client) { + struct input_track *t; + wl_list_for_each(t, &compositor.input_tracks, link) { + if (t->client == client) return t; + } + t = calloc(1, sizeof(*t)); + if (!t) return NULL; + t->client = client; + t->kbd_destroy.notify = input_track_kbd_destroy; + t->ptr_destroy.notify = input_track_ptr_destroy; + wl_list_insert(&compositor.input_tracks, &t->link); + return t; +} + +/* ===================== wl_pointer ===================== */ + +static void pointer_set_cursor(struct wl_client *c, struct wl_resource *r, + uint32_t serial, struct wl_resource *surface, + int32_t hx, int32_t hy) +{ (void)c;(void)r;(void)serial;(void)surface;(void)hx;(void)hy; } + +static void pointer_release(struct wl_client *c, struct wl_resource *r) { (void)c; wl_resource_destroy(r); } + +static const struct wl_pointer_interface pointer_impl = { + .set_cursor = pointer_set_cursor, + .release = pointer_release, +}; + +/* ===================== wl_keyboard ===================== */ + +static void keyboard_release(struct wl_client *c, struct wl_resource *r) { (void)c; wl_resource_destroy(r); } + +static const struct wl_keyboard_interface keyboard_impl = { + .release = keyboard_release, +}; + +/* ===================== wl_touch ===================== */ + +static void touch_release(struct wl_client *c, struct wl_resource *r) { (void)c; wl_resource_destroy(r); } + +static const struct wl_touch_interface touch_impl = { + .release = touch_release, +}; + +/* ===================== wl_seat ===================== */ + +static void seat_get_pointer(struct wl_client *client, struct wl_resource *resource, + uint32_t id) +{ + struct wl_resource *ptr = wl_resource_create(client, + &wl_pointer_interface, wl_resource_get_version(resource), id); + if (!ptr) { wl_client_post_no_memory(client); return; } + wl_resource_set_implementation(ptr, &pointer_impl, NULL, NULL); + + struct input_track *t = get_input_track(client); + if (t) { + t->ptr = ptr; + wl_resource_add_destroy_listener(ptr, &t->ptr_destroy); + } +} + +static void seat_get_keyboard(struct wl_client *client, struct wl_resource *resource, + uint32_t id) +{ + struct wl_resource *kbd = wl_resource_create(client, + &wl_keyboard_interface, wl_resource_get_version(resource), id); + if (!kbd) { wl_client_post_no_memory(client); return; } + wl_resource_set_implementation(kbd, &keyboard_impl, NULL, NULL); + + struct input_track *t = get_input_track(client); + if (t) { + t->kbd = kbd; + wl_resource_add_destroy_listener(kbd, &t->kbd_destroy); + } + + if (compositor.xkb_keymap_fd >= 0) { + lseek(compositor.xkb_keymap_fd, 0, SEEK_SET); + wl_keyboard_send_keymap(kbd, WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1, + compositor.xkb_keymap_fd, + compositor.xkb_keymap_size); + } +} + +static void seat_get_touch(struct wl_client *client, struct wl_resource *resource, + uint32_t id) +{ + struct wl_resource *touch = wl_resource_create(client, + &wl_touch_interface, wl_resource_get_version(resource), id); + if (!touch) { wl_client_post_no_memory(client); return; } + wl_resource_set_implementation(touch, &touch_impl, NULL, NULL); +} + +static void seat_release(struct wl_client *client, struct wl_resource *resource) { + (void)client; + wl_resource_destroy(resource); +} + +static const struct wl_seat_interface seat_impl = { + .get_pointer = seat_get_pointer, + .get_keyboard = seat_get_keyboard, + .get_touch = seat_get_touch, + .release = seat_release, +}; + +static void seat_bind(struct wl_client *client, void *data, + uint32_t version, uint32_t id) +{ + (void)data; + struct wl_resource *res = wl_resource_create(client, + &wl_seat_interface, version < 7 ? version : 7, id); + if (!res) { wl_client_post_no_memory(client); return; } + wl_resource_set_implementation(res, &seat_impl, NULL, NULL); + wl_seat_send_capabilities(res, WL_SEAT_CAPABILITY_POINTER | WL_SEAT_CAPABILITY_KEYBOARD); + if (wl_resource_get_version(res) >= 4) + wl_seat_send_name(res, "default"); +} + +/* ===================== subcompositor ===================== */ + +static void subcompositor_destroy(struct wl_client *client, struct wl_resource *res) { + (void)client; + wl_resource_destroy(res); +} + +static void subcompositor_get_subsurface(struct wl_client *client, + struct wl_resource *resource, + uint32_t id, + struct wl_resource *surface, + struct wl_resource *parent) +{ + (void)resource; (void)surface; (void)parent; + struct wl_resource *sub = wl_resource_create(client, + &wl_subsurface_interface, 1, id); + if (!sub) { wl_client_post_no_memory(client); return; } + wl_resource_set_implementation(sub, &subsurface_impl, NULL, NULL); +} + +/* ===================== wl_subsurface ===================== */ + +static void subsurface_destroy(struct wl_client *c, struct wl_resource *r) { (void)c; wl_resource_destroy(r); } +static void subsurface_set_position(struct wl_client *c, struct wl_resource *r, int32_t x, int32_t y) { (void)c;(void)r;(void)x;(void)y; } +static void subsurface_place_above(struct wl_client *c, struct wl_resource *r, struct wl_resource *sibling) { (void)c;(void)r;(void)sibling; } +static void subsurface_place_below(struct wl_client *c, struct wl_resource *r, struct wl_resource *sibling) { (void)c;(void)r;(void)sibling; } +static void subsurface_set_sync(struct wl_client *c, struct wl_resource *r) { (void)c;(void)r; } +static void subsurface_set_desync(struct wl_client *c, struct wl_resource *r) { (void)c;(void)r; } + +static const struct wl_subsurface_interface subsurface_impl = { + .destroy = subsurface_destroy, + .set_position = subsurface_set_position, + .place_above = subsurface_place_above, + .place_below = subsurface_place_below, + .set_sync = subsurface_set_sync, + .set_desync = subsurface_set_desync, +}; + +static const struct wl_subcompositor_interface subcompositor_impl = { + .destroy = subcompositor_destroy, + .get_subsurface = subcompositor_get_subsurface, +}; + +/* ===================== data device manager ===================== */ + +static void data_offer_destroy(struct wl_client *client, struct wl_resource *res) { + (void)client; wl_resource_destroy(res); +} +static void data_offer_accept(struct wl_client *c, struct wl_resource *r, + uint32_t s, const char *t) { (void)c;(void)r;(void)s;(void)t; } +static void data_offer_receive(struct wl_client *c, struct wl_resource *r, + const char *t, int32_t fd) { (void)c;(void)r;(void)t;close(fd); } +static void data_offer_finish(struct wl_client *c, struct wl_resource *r) { (void)c;(void)r; } +static void data_offer_set_actions(struct wl_client *c, struct wl_resource *r, + uint32_t d, uint32_t a) { (void)c;(void)r;(void)d;(void)a; } +static const struct wl_data_offer_interface data_offer_impl = { + .destroy = data_offer_destroy, + .accept = data_offer_accept, + .receive = data_offer_receive, + .finish = data_offer_finish, + .set_actions = data_offer_set_actions, +}; + +static void data_source_destroy(struct wl_client *c, struct wl_resource *r) { (void)c; wl_resource_destroy(r); } +static void data_source_offer(struct wl_client *c, struct wl_resource *r, const char *t) { (void)c;(void)r;(void)t; } +static void data_source_set_actions(struct wl_client *c, struct wl_resource *r, uint32_t a) { (void)c;(void)r;(void)a; } +static const struct wl_data_source_interface data_source_impl = { + .destroy = data_source_destroy, + .offer = data_source_offer, + .set_actions = data_source_set_actions, +}; + +static void data_device_start_drag(struct wl_client *c, struct wl_resource *r, + struct wl_resource *s, struct wl_resource *o, + struct wl_resource *i, uint32_t serial) +{ (void)c;(void)r;(void)s;(void)o;(void)i;(void)serial; } +static void data_device_set_selection(struct wl_client *c, struct wl_resource *r, + struct wl_resource *s, uint32_t serial) +{ (void)c;(void)r;(void)s;(void)serial; } +static void data_device_release(struct wl_client *c, struct wl_resource *r) { (void)c; wl_resource_destroy(r); } +static const struct wl_data_device_interface data_device_impl = { + .start_drag = data_device_start_drag, + .set_selection = data_device_set_selection, + .release = data_device_release, +}; + +static void data_device_manager_create_data_source(struct wl_client *c, + struct wl_resource *r, + uint32_t id) +{ + struct wl_resource *src = wl_resource_create(c, + &wl_data_source_interface, wl_resource_get_version(r), id); + if (!src) { wl_client_post_no_memory(c); return; } + wl_resource_set_implementation(src, &data_source_impl, NULL, NULL); +} + +static void data_device_manager_get_data_device(struct wl_client *c, + struct wl_resource *r, + uint32_t id, + struct wl_resource *seat) +{ + (void)seat; + struct wl_resource *dev = wl_resource_create(c, + &wl_data_device_interface, wl_resource_get_version(r), id); + if (!dev) { wl_client_post_no_memory(c); return; } + wl_resource_set_implementation(dev, &data_device_impl, NULL, NULL); +} + +static const struct wl_data_device_manager_interface data_device_manager_impl = { + .create_data_source = data_device_manager_create_data_source, + .get_data_device = data_device_manager_get_data_device, +}; + +/* ===================== Global bind functions ===================== */ + +static void bind_compositor(struct wl_client *client, void *data, + uint32_t version, uint32_t id) +{ + (void)data; + struct wl_resource *res = wl_resource_create(client, + &wl_compositor_interface, version < 5 ? version : 5, id); + if (!res) { wl_client_post_no_memory(client); return; } + wl_resource_set_implementation(res, &compositor_impl, NULL, NULL); +} + +static void bind_shm(struct wl_client *client, void *data, + uint32_t version, uint32_t id) +{ + (void)data; + struct wl_resource *res = wl_resource_create(client, + &wl_shm_interface, version < 2 ? version : 2, id); + if (!res) { wl_client_post_no_memory(client); return; } + wl_resource_set_implementation(res, &shm_impl, NULL, NULL); + wl_shm_send_format(res, WL_SHM_FORMAT_ARGB8888); + wl_shm_send_format(res, WL_SHM_FORMAT_XRGB8888); +} + +static void bind_xdg_wm_base(struct wl_client *client, void *data, + uint32_t version, uint32_t id) +{ + (void)data; + struct wl_resource *res = wl_resource_create(client, + &xdg_wm_base_interface, version < 6 ? version : 6, id); + if (!res) { wl_client_post_no_memory(client); return; } + wl_resource_set_implementation(res, &xdg_wm_base_impl, NULL, NULL); +} + +static void bind_subcompositor(struct wl_client *client, void *data, + uint32_t version, uint32_t id) +{ + (void)data; + struct wl_resource *res = wl_resource_create(client, + &wl_subcompositor_interface, version, id); + if (!res) { wl_client_post_no_memory(client); return; } + wl_resource_set_implementation(res, &subcompositor_impl, NULL, NULL); +} + +static void bind_data_device_manager(struct wl_client *client, void *data, + uint32_t version, uint32_t id) +{ + (void)data; + struct wl_resource *res = wl_resource_create(client, + &wl_data_device_manager_interface, version < 3 ? version : 3, id); + if (!res) { wl_client_post_no_memory(client); return; } + wl_resource_set_implementation(res, &data_device_manager_impl, NULL, NULL); +} + +static gboolean flush_resize_cb(gpointer data); + +/* ===================== Output resize ===================== */ + +static void output_resize(int width, int height) { + if (width < 50) width = 50; + if (height < 50) height = 50; + struct tabby_output *out = &compositor.output; + out->width = width; + out->height = height; + out->stride = width * 4; + free(out->data); + out->data = calloc(1, height * out->stride); + if (out->data) + memset(out->data, 0x22, height * out->stride); + + /* Notify all outputs */ + struct wl_resource *res; + wl_list_for_each(res, &out->resources, link) + output_send_all(res); + + /* Reconfigure ALL surfaces with new size */ + struct tabby_surface *surf; + struct wl_array states; + wl_array_init(&states); + uint32_t *s = wl_array_add(&states, sizeof(uint32_t)); + if (s) *s = XDG_TOPLEVEL_STATE_ACTIVATED; + + wl_list_for_each(surf, &compositor.surfaces, link) { + if (surf->xdg_toplevel) { + compositor.serial++; + xdg_toplevel_send_configure(surf->xdg_toplevel, + width, height, &states); + if (surf->xdg_surface) { + xdg_surface_send_configure(surf->xdg_surface, compositor.serial); + surf->configure_serial = compositor.serial; + } + } + } + + wl_array_release(&states); + + wl_display_flush_clients(compositor.display); +} + +/* ===================== Set active surface ===================== */ + +static struct wl_resource *kbd_for_client(struct wl_client *client) { + struct input_track *t; + wl_list_for_each(t, &compositor.input_tracks, link) + if (t->client == client) return t->kbd; + return NULL; +} + +static struct wl_resource *ptr_for_client(struct wl_client *client) { + struct input_track *t; + wl_list_for_each(t, &compositor.input_tracks, link) + if (t->client == client) return t->ptr; + return NULL; +} + +static void send_kbd_enter(struct tabby_surface *surface) { + if (!surface) return; + struct wl_resource *kbd = kbd_for_client(surface->client); + if (!kbd) return; + struct wl_array keys; + wl_array_init(&keys); + compositor.input_serial++; + wl_keyboard_send_enter(kbd, compositor.input_serial, + surface->resource, &keys); + wl_array_release(&keys); + compositor.kbd_focus = surface; + compositor.kbd_resource = kbd; + wl_display_flush_clients(compositor.display); +} + +static void send_kbd_leave(struct tabby_surface *surface) { + if (!surface || !compositor.kbd_focus) return; + if (compositor.kbd_focus != surface) return; + struct wl_resource *kbd = kbd_for_client(surface->client); + if (!kbd) return; + compositor.input_serial++; + wl_keyboard_send_leave(kbd, compositor.input_serial, + surface->resource); + compositor.kbd_focus = NULL; + wl_display_flush_clients(compositor.display); +} + +static void set_active_surface(struct tabby_surface *surface) { + if (compositor.active_surface == surface) return; + send_kbd_leave(compositor.kbd_focus); + compositor.active_surface = surface; + send_kbd_enter(surface); + + if (surface && surface->pixels) { + copy_buffer_to_display(surface); + } else { + struct tabby_output *o = &compositor.output; + if (o->data && o->height > 0 && o->stride > 0) + memset(o->data, 0x22, (size_t)o->height * (size_t)o->stride); + } + gtk_widget_queue_draw(compositor.canvas); + wl_display_flush_clients(compositor.display); +} + +/* ===================== GTK canvas draw ===================== */ + +static gboolean canvas_draw_cb(GtkWidget *widget, cairo_t *cr, gpointer user_data) { + (void)widget; (void)user_data; + struct tabby_output *out = &compositor.output; + if (!out->data || out->width <= 0 || out->height <= 0) { + cairo_set_source_rgb(cr, 0x22 / 255.0, 0x22 / 255.0, 0x22 / 255.0); + cairo_paint(cr); + return FALSE; + } + + cairo_surface_t *image = cairo_image_surface_create_for_data( + out->data, CAIRO_FORMAT_ARGB32, out->width, out->height, out->stride); + cairo_set_source_surface(cr, image, 0, 0); + cairo_paint(cr); + cairo_surface_destroy(image); + return FALSE; +} + +static gboolean canvas_resize_cb(GtkWidget *w, GtkAllocation *alloc, gpointer data) { + (void)w; (void)data; + if (alloc->width < 50 || alloc->height < 50) return FALSE; + compositor.resize_pending = true; + compositor.resize_w = alloc->width; + compositor.resize_h = alloc->height; + g_idle_add(flush_resize_cb, NULL); + return FALSE; +} + +/* ===================== Input forwarding ===================== */ + +static gboolean canvas_key_cb(GtkWidget *w, GdkEventKey *ev, gpointer data) { + (void)w; (void)data; + if (!compositor.active_surface || !compositor.active_surface->client) return FALSE; + struct wl_resource *kbd = kbd_for_client(compositor.active_surface->client); + if (!kbd) { + fprintf(stderr, "DEBUG: no kbd for active client\n"); + return FALSE; + } + + if (compositor.kbd_focus != compositor.active_surface) + send_kbd_enter(compositor.active_surface); + + uint32_t keycode = ev->hardware_keycode; + if (keycode >= 8) keycode -= 8; + compositor.input_serial++; + uint32_t time = ev->time; + uint32_t wl_state = (ev->type == GDK_KEY_PRESS) ? + WL_KEYBOARD_KEY_STATE_PRESSED : WL_KEYBOARD_KEY_STATE_RELEASED; + fprintf(stderr, "DEBUG: key %s hw=%u evdev=%u\n", + wl_state ? "press" : "release", + (unsigned)ev->hardware_keycode, (unsigned)keycode); + + /* Track modifiers manually — xkb indices: 0=Shift,1=Lock,2=Ctrl,3=Alt */ + static uint32_t mod_depressed = 0; + static uint32_t mod_locked = 0; + if (keycode == 42 || keycode == 54) /* shift */ + mod_depressed = (mod_depressed & ~(1<<0)) | (wl_state << 0); + if (keycode == 29 || keycode == 97) /* ctrl */ + mod_depressed = (mod_depressed & ~(1<<2)) | (wl_state << 2); + if (keycode == 56 || keycode == 100) /* alt */ + mod_depressed = (mod_depressed & ~(1<<3)) | (wl_state << 3); + if (keycode == 58) { /* caps lock — toggles on release, index 1 */ + if (!wl_state) mod_locked ^= (1 << 1); + } + wl_keyboard_send_modifiers(kbd, compositor.input_serial, + mod_depressed, 0, mod_locked, 0); + + wl_keyboard_send_key(kbd, compositor.input_serial, + time, keycode, wl_state); + + wl_display_flush_clients(compositor.display); + return TRUE; +} + +static gboolean canvas_button_cb(GtkWidget *w, GdkEventButton *ev, gpointer data) { + (void)w; (void)data; + gtk_widget_grab_focus(w); + if (!compositor.active_surface || !compositor.active_surface->client) return FALSE; + struct wl_resource *ptr = ptr_for_client(compositor.active_surface->client); + if (!ptr) { + fprintf(stderr, "DEBUG: no ptr for active client\n"); + return FALSE; + } + + compositor.input_serial++; + wl_pointer_send_enter(ptr, compositor.input_serial, + compositor.active_surface->resource, + wl_fixed_from_double(ev->x), + wl_fixed_from_double(ev->y)); + + uint32_t button = 0; + if (ev->button == 1) button = 0x110; + else if (ev->button == 2) button = 0x111; + else if (ev->button == 3) button = 0x112; + + if (button) { + compositor.input_serial++; + wl_pointer_send_button(ptr, compositor.input_serial, ev->time, + button, ev->type == GDK_BUTTON_PRESS ? 1 : 0); + } + + int ver = wl_resource_get_version(ptr); + if (ver >= WL_POINTER_FRAME_SINCE_VERSION) + wl_pointer_send_frame(ptr); + + wl_display_flush_clients(compositor.display); + fprintf(stderr, "DEBUG: button %u %s at (%.0f,%.0f)\n", + (unsigned)ev->button, + ev->type == GDK_BUTTON_PRESS ? "press" : "release", + ev->x, ev->y); + return TRUE; +} + +static gboolean canvas_motion_cb(GtkWidget *w, GdkEventMotion *ev, gpointer data) { + (void)w; (void)data; + if (!compositor.active_surface || !compositor.active_surface->client) return FALSE; + struct wl_resource *ptr = ptr_for_client(compositor.active_surface->client); + if (!ptr) return FALSE; + + wl_pointer_send_motion(ptr, ev->time, + wl_fixed_from_double(ev->x), + wl_fixed_from_double(ev->y)); + + int ver = wl_resource_get_version(ptr); + if (ver >= WL_POINTER_FRAME_SINCE_VERSION) + wl_pointer_send_frame(ptr); + wl_display_flush_clients(compositor.display); + return TRUE; +} + +/* ===================== GTK tab switch ===================== */ + +static void tab_switch_cb(GtkNotebook *nb, GtkWidget *page, gint page_num, + gpointer user_data) +{ + (void)nb; (void)page; (void)user_data; + for (int i = 0; i < compositor.num_apps; i++) { + if (compositor.apps[i]->tab_index == page_num) { + set_active_surface(compositor.apps[i]->surface); + break; + } + } +} + +/* ===================== Launch client ===================== */ + +static void launch_client(const char *cmd, int tab_index) { + (void)tab_index; + pid_t pid = fork(); + if (pid == 0) { + const char *xdg_runtime = getenv("XDG_RUNTIME_DIR"); + if (!xdg_runtime) xdg_runtime = "/tmp"; + setenv("WAYLAND_DISPLAY", "tabby-wayland-0", 1); + setenv("XDG_RUNTIME_DIR", xdg_runtime, 1); + setenv("GDK_BACKEND", "wayland", 1); + setenv("QT_QPA_PLATFORM", "wayland", 1); + unsetenv("DISPLAY"); + + execlp("/bin/sh", "sh", "-c", cmd, NULL); + _exit(127); + } + + if (pid > 0) + compositor.apps[tab_index]->pid = pid; +} + +/* ===================== Wayland event loop integration ===================== */ + +/* Idle callback to apply pending resize & launch clients */ +static gboolean flush_resize_cb(gpointer data) { + (void)data; + if (!compositor.resize_pending) return FALSE; + compositor.resize_pending = false; + output_resize(compositor.resize_w, compositor.resize_h); + if (!compositor.clients_launched) { + compositor.clients_launched = true; + for (int i = 0; i < compositor.num_apps; i++) + launch_client(compositor.apps[i]->cmd, i); + } + return FALSE; +} + +static gboolean wayland_io_cb(GIOChannel *channel, GIOCondition condition, + gpointer data) +{ + (void)channel; (void)condition; + struct wl_display *display = data; + wl_event_loop_dispatch(wl_display_get_event_loop(display), 0); + wl_display_flush_clients(display); + if (compositor.resize_pending) + flush_resize_cb(NULL); + return TRUE; +} + +/* ===================== Crash handler ===================== */ + +static void crash_handler(int sig, siginfo_t *info, void *ctx) { + (void)sig; (void)info; (void)ctx; + void *bt[64]; + int n = backtrace(bt, 64); + write(2, "\n=== BACKTRACE ===\n", 19); + backtrace_symbols_fd(bt, n, 2); + _exit(139); +} + +/* ===================== SIGCHLD handler ===================== */ + +static void sigchld_handler(int sig) { + (void)sig; + while (waitpid(-1, NULL, WNOHANG) > 0); +} + +/* ===================== Main ===================== */ + +int main(int argc, char *argv[]) { + memset(&compositor, 0, sizeof(compositor)); + + struct sigaction crash_sa = { + .sa_sigaction = crash_handler, + .sa_flags = SA_SIGINFO | SA_NODEFER, + }; + sigaction(SIGSEGV, &crash_sa, NULL); + sigaction(SIGABRT, &crash_sa, NULL); + signal(SIGCHLD, sigchld_handler); + + /* Parse CLI: apps after -- */ + int num_requested = 0; + const char *requested_apps[MAX_APPS] = {0}; + bool found_ddash = false; + for (int i = 1; i < argc; i++) { + if (strcmp(argv[i], "--") == 0) { found_ddash = true; continue; } + if (found_ddash && num_requested < MAX_APPS) + requested_apps[num_requested++] = argv[i]; + } + + /* Initialize Wayland display */ + compositor.display = wl_display_create(); + if (!compositor.display) { + fprintf(stderr, "Failed to create Wayland display\n"); + return 1; + } + compositor.loop = wl_display_get_event_loop(compositor.display); + compositor.xkb_keymap_fd = -1; + + /* Generate xkb keymap */ + compositor.xkb_ctx = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + if (compositor.xkb_ctx) { + struct xkb_rule_names names = { + .rules = NULL, .model = "pc105", + .layout = "us", .variant = NULL, .options = NULL, + }; + compositor.xkb_keymap = xkb_keymap_new_from_names( + compositor.xkb_ctx, &names, XKB_KEYMAP_COMPILE_NO_FLAGS); + if (compositor.xkb_keymap) { + compositor.xkb_state = xkb_state_new(compositor.xkb_keymap); + compositor.xkb_keymap_str = xkb_keymap_get_as_string( + compositor.xkb_keymap, XKB_KEYMAP_FORMAT_TEXT_V1); + if (compositor.xkb_keymap_str) { + compositor.xkb_keymap_size = strlen(compositor.xkb_keymap_str); + compositor.xkb_keymap_fd = memfd_create("xkb-keymap", MFD_CLOEXEC); + if (compositor.xkb_keymap_fd >= 0) { + write(compositor.xkb_keymap_fd, compositor.xkb_keymap_str, + compositor.xkb_keymap_size); + lseek(compositor.xkb_keymap_fd, 0, SEEK_SET); + } + } + } + } + + /* Set up output — minimal size, grows on first size-allocate */ + compositor.output.width = 320; + compositor.output.height = 240; + compositor.output.stride = 320 * 4; + compositor.output.data = calloc(1, 320 * 240 * 4); + if (compositor.output.data) + memset(compositor.output.data, 0x22, 320 * 240 * 4); + wl_list_init(&compositor.output.resources); + compositor.output.global = wl_global_create(compositor.display, + &wl_output_interface, 2, &compositor.output, output_bind); + + /* Register globals */ + wl_global_create(compositor.display, &wl_compositor_interface, 5, + NULL, bind_compositor); + wl_global_create(compositor.display, &wl_shm_interface, 2, + NULL, bind_shm); + wl_global_create(compositor.display, &xdg_wm_base_interface, 6, + NULL, bind_xdg_wm_base); + wl_global_create(compositor.display, &wl_subcompositor_interface, 1, + NULL, bind_subcompositor); + wl_global_create(compositor.display, &wl_data_device_manager_interface, 3, + NULL, bind_data_device_manager); + wl_global_create(compositor.display, &wl_seat_interface, 7, + NULL, seat_bind); + + wl_list_init(&compositor.surfaces); + wl_list_init(&compositor.input_tracks); + + /* Add Wayland socket */ + if (wl_display_add_socket(compositor.display, "tabby-wayland-0") < 0) { + fprintf(stderr, "Failed to add socket 'tabby-wayland-0': %s\n", + strerror(errno)); + return 1; + } + + printf("Wayland socket: tabby-wayland-0\n"); + printf("Set WAYLAND_DISPLAY=tabby-wayland-0 to connect\n"); + + /* Read apps from tab-commands file, CLI --args, or defaults */ + char *file_apps[MAX_APPS]; + int total_apps = 0; + const char **apps_to_launch = NULL; + + FILE *fp = fopen("tab-commands", "r"); + if (fp) { + char line[512]; + while (fgets(line, sizeof(line), fp) && total_apps < MAX_APPS) { + char *nl = strchr(line, '\n'); + if (nl) *nl = '\0'; + char *cr = strchr(line, '\r'); + if (cr) *cr = '\0'; + if (line[0] == '#' || line[0] == '\0') continue; + file_apps[total_apps] = strdup(line); + total_apps++; + } + fclose(fp); + apps_to_launch = (const char **)file_apps; + } + + if (num_requested > 0) { + apps_to_launch = requested_apps; + total_apps = num_requested; + } + + if (!apps_to_launch) { + apps_to_launch = default_apps; + total_apps = 0; + while (apps_to_launch[total_apps]) total_apps++; + } + + if (total_apps > MAX_APPS) total_apps = MAX_APPS; + + /* Force X11 backend — compositor must not use Wayland for its own UI */ + gdk_set_allowed_backends("x11"); + gtk_init(&argc, &argv); + + /* Build UI */ + compositor.window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_title(GTK_WINDOW(compositor.window), "Tabbed Wayland"); + gtk_window_set_default_size(GTK_WINDOW(compositor.window), 900, 700); + g_signal_connect(compositor.window, "destroy", G_CALLBACK(gtk_main_quit), NULL); + + GtkWidget *vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); + gtk_container_add(GTK_CONTAINER(compositor.window), vbox); + + compositor.notebook = gtk_notebook_new(); + gtk_notebook_set_scrollable(GTK_NOTEBOOK(compositor.notebook), TRUE); + gtk_notebook_set_show_border(GTK_NOTEBOOK(compositor.notebook), FALSE); + gtk_box_pack_start(GTK_BOX(vbox), compositor.notebook, FALSE, FALSE, 0); + + compositor.canvas = gtk_drawing_area_new(); + gtk_widget_set_hexpand(compositor.canvas, TRUE); + gtk_widget_set_vexpand(compositor.canvas, TRUE); + gtk_widget_set_can_focus(compositor.canvas, TRUE); + gtk_widget_add_events(compositor.canvas, + GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | + GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | + GDK_POINTER_MOTION_MASK); + g_signal_connect(compositor.canvas, "draw", + G_CALLBACK(canvas_draw_cb), NULL); + g_signal_connect(compositor.canvas, "size-allocate", + G_CALLBACK(canvas_resize_cb), NULL); + g_signal_connect(compositor.canvas, "key-press-event", + G_CALLBACK(canvas_key_cb), NULL); + g_signal_connect(compositor.canvas, "key-release-event", + G_CALLBACK(canvas_key_cb), NULL); + g_signal_connect(compositor.canvas, "button-press-event", + G_CALLBACK(canvas_button_cb), NULL); + g_signal_connect(compositor.canvas, "button-release-event", + G_CALLBACK(canvas_button_cb), NULL); + g_signal_connect(compositor.canvas, "motion-notify-event", + G_CALLBACK(canvas_motion_cb), NULL); + gtk_box_pack_start(GTK_BOX(vbox), compositor.canvas, TRUE, TRUE, 0); + + compositor.statusbar = gtk_label_new("Tabbed Wayland Compositor"); + gtk_widget_set_margin_top(compositor.statusbar, 2); + gtk_widget_set_margin_bottom(compositor.statusbar, 2); + gtk_box_pack_start(GTK_BOX(vbox), compositor.statusbar, FALSE, FALSE, 0); + + /* Apply dark theme */ + GtkCssProvider *css = gtk_css_provider_new(); + gtk_css_provider_load_from_data(css, + "window { background-color: #222; color: #ccc; }" + "notebook header { background-color: #333; }" + "notebook tab { background-color: #444; color: #ddd; padding: 4px 12px; }" + "notebook tab:checked { background-color: #555; }" + "label { color: #ccc; }", -1, NULL); + gtk_style_context_add_provider_for_screen(gdk_screen_get_default(), + GTK_STYLE_PROVIDER(css), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); + + /* Create app entries and tabs (defer launch to after first resize) */ + compositor.num_apps = total_apps; + for (int i = 0; i < total_apps; i++) { + struct tabby_app *app = calloc(1, sizeof(*app)); + app->cmd = g_strdup(apps_to_launch[i]); + app->pid = -1; + app->tab_index = i; + + GtkWidget *label = gtk_label_new(apps_to_launch[i]); + app->tab_label = label; + + GtkWidget *placeholder = gtk_label_new("Waiting for client..."); + gtk_notebook_append_page(GTK_NOTEBOOK(compositor.notebook), + placeholder, label); + + compositor.apps[i] = app; + } + + g_signal_connect(compositor.notebook, "switch-page", + G_CALLBACK(tab_switch_cb), NULL); + + /* Register Wayland watch — clients can connect, but won't be processed + until gtk_main runs (no blocking, no corruption) */ + int wayland_fd = wl_event_loop_get_fd(compositor.loop); + GIOChannel *channel = g_io_channel_unix_new(wayland_fd); + g_io_add_watch(channel, G_IO_IN, wayland_io_cb, compositor.display); + g_io_channel_unref(channel); + + gtk_widget_show_all(compositor.window); + gtk_widget_grab_focus(compositor.canvas); + /* Clients are launched by canvas_resize_cb on first size-allocate */ + gtk_main(); + + /* Cleanup */ + for (int i = 0; i < compositor.num_apps; i++) { + if (compositor.apps[i]->pid > 0) + kill(compositor.apps[i]->pid, SIGTERM); + free(compositor.apps[i]->cmd); + free(compositor.apps[i]); + } + free(compositor.output.data); + if (compositor.xkb_keymap_fd >= 0) close(compositor.xkb_keymap_fd); + free(compositor.xkb_keymap_str); + if (compositor.xkb_state) xkb_state_unref(compositor.xkb_state); + if (compositor.xkb_keymap) xkb_keymap_unref(compositor.xkb_keymap); + if (compositor.xkb_ctx) xkb_context_unref(compositor.xkb_ctx); + + return 0; +} diff --git a/xdg-shell-protocol.c b/xdg-shell-protocol.c new file mode 100644 index 0000000..5a50433 --- /dev/null +++ b/xdg-shell-protocol.c @@ -0,0 +1,184 @@ +/* Generated by wayland-scanner 1.24.0 */ + +/* + * Copyright © 2008-2013 Kristian Høgsberg + * Copyright © 2013 Rafael Antognolli + * Copyright © 2013 Jasper St. Pierre + * Copyright © 2010-2013 Intel Corporation + * Copyright © 2015-2017 Samsung Electronics Co., Ltd + * Copyright © 2015-2017 Red Hat Inc. + * + * 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 +#include +#include +#include "wayland-util.h" + +#ifndef __has_attribute +# define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */ +#endif + +#if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4) +#define WL_PRIVATE __attribute__ ((visibility("hidden"))) +#else +#define WL_PRIVATE +#endif + +extern const struct wl_interface wl_output_interface; +extern const struct wl_interface wl_seat_interface; +extern const struct wl_interface wl_surface_interface; +extern const struct wl_interface xdg_popup_interface; +extern const struct wl_interface xdg_positioner_interface; +extern const struct wl_interface xdg_surface_interface; +extern const struct wl_interface xdg_toplevel_interface; + +static const struct wl_interface *xdg_shell_types[] = { + NULL, + NULL, + NULL, + NULL, + &xdg_positioner_interface, + &xdg_surface_interface, + &wl_surface_interface, + &xdg_toplevel_interface, + &xdg_popup_interface, + &xdg_surface_interface, + &xdg_positioner_interface, + &xdg_toplevel_interface, + &wl_seat_interface, + NULL, + NULL, + NULL, + &wl_seat_interface, + NULL, + &wl_seat_interface, + NULL, + NULL, + &wl_output_interface, + &wl_seat_interface, + NULL, + &xdg_positioner_interface, + NULL, +}; + +static const struct wl_message xdg_wm_base_requests[] = { + { "destroy", "", xdg_shell_types + 0 }, + { "create_positioner", "n", xdg_shell_types + 4 }, + { "get_xdg_surface", "no", xdg_shell_types + 5 }, + { "pong", "u", xdg_shell_types + 0 }, +}; + +static const struct wl_message xdg_wm_base_events[] = { + { "ping", "u", xdg_shell_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface xdg_wm_base_interface = { + "xdg_wm_base", 7, + 4, xdg_wm_base_requests, + 1, xdg_wm_base_events, +}; + +static const struct wl_message xdg_positioner_requests[] = { + { "destroy", "", xdg_shell_types + 0 }, + { "set_size", "ii", xdg_shell_types + 0 }, + { "set_anchor_rect", "iiii", xdg_shell_types + 0 }, + { "set_anchor", "u", xdg_shell_types + 0 }, + { "set_gravity", "u", xdg_shell_types + 0 }, + { "set_constraint_adjustment", "u", xdg_shell_types + 0 }, + { "set_offset", "ii", xdg_shell_types + 0 }, + { "set_reactive", "3", xdg_shell_types + 0 }, + { "set_parent_size", "3ii", xdg_shell_types + 0 }, + { "set_parent_configure", "3u", xdg_shell_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface xdg_positioner_interface = { + "xdg_positioner", 7, + 10, xdg_positioner_requests, + 0, NULL, +}; + +static const struct wl_message xdg_surface_requests[] = { + { "destroy", "", xdg_shell_types + 0 }, + { "get_toplevel", "n", xdg_shell_types + 7 }, + { "get_popup", "n?oo", xdg_shell_types + 8 }, + { "set_window_geometry", "iiii", xdg_shell_types + 0 }, + { "ack_configure", "u", xdg_shell_types + 0 }, +}; + +static const struct wl_message xdg_surface_events[] = { + { "configure", "u", xdg_shell_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface xdg_surface_interface = { + "xdg_surface", 7, + 5, xdg_surface_requests, + 1, xdg_surface_events, +}; + +static const struct wl_message xdg_toplevel_requests[] = { + { "destroy", "", xdg_shell_types + 0 }, + { "set_parent", "?o", xdg_shell_types + 11 }, + { "set_title", "s", xdg_shell_types + 0 }, + { "set_app_id", "s", xdg_shell_types + 0 }, + { "show_window_menu", "ouii", xdg_shell_types + 12 }, + { "move", "ou", xdg_shell_types + 16 }, + { "resize", "ouu", xdg_shell_types + 18 }, + { "set_max_size", "ii", xdg_shell_types + 0 }, + { "set_min_size", "ii", xdg_shell_types + 0 }, + { "set_maximized", "", xdg_shell_types + 0 }, + { "unset_maximized", "", xdg_shell_types + 0 }, + { "set_fullscreen", "?o", xdg_shell_types + 21 }, + { "unset_fullscreen", "", xdg_shell_types + 0 }, + { "set_minimized", "", xdg_shell_types + 0 }, +}; + +static const struct wl_message xdg_toplevel_events[] = { + { "configure", "iia", xdg_shell_types + 0 }, + { "close", "", xdg_shell_types + 0 }, + { "configure_bounds", "4ii", xdg_shell_types + 0 }, + { "wm_capabilities", "5a", xdg_shell_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface xdg_toplevel_interface = { + "xdg_toplevel", 7, + 14, xdg_toplevel_requests, + 4, xdg_toplevel_events, +}; + +static const struct wl_message xdg_popup_requests[] = { + { "destroy", "", xdg_shell_types + 0 }, + { "grab", "ou", xdg_shell_types + 22 }, + { "reposition", "3ou", xdg_shell_types + 24 }, +}; + +static const struct wl_message xdg_popup_events[] = { + { "configure", "iiii", xdg_shell_types + 0 }, + { "popup_done", "", xdg_shell_types + 0 }, + { "repositioned", "3u", xdg_shell_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface xdg_popup_interface = { + "xdg_popup", 7, + 3, xdg_popup_requests, + 3, xdg_popup_events, +}; + diff --git a/xdg-shell-server.h b/xdg-shell-server.h new file mode 100644 index 0000000..8c11cd0 --- /dev/null +++ b/xdg-shell-server.h @@ -0,0 +1,2374 @@ +/* Generated by wayland-scanner 1.24.0 */ + +#ifndef XDG_SHELL_SERVER_PROTOCOL_H +#define XDG_SHELL_SERVER_PROTOCOL_H + +#include +#include +#include "wayland-server.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct wl_client; +struct wl_resource; + +/** + * @page page_xdg_shell The xdg_shell protocol + * @section page_ifaces_xdg_shell Interfaces + * - @subpage page_iface_xdg_wm_base - create desktop-style surfaces + * - @subpage page_iface_xdg_positioner - child surface positioner + * - @subpage page_iface_xdg_surface - desktop user interface surface base interface + * - @subpage page_iface_xdg_toplevel - toplevel surface + * - @subpage page_iface_xdg_popup - short-lived, popup surfaces for menus + * @section page_copyright_xdg_shell Copyright + *
+ *
+ * Copyright © 2008-2013 Kristian Høgsberg
+ * Copyright © 2013      Rafael Antognolli
+ * Copyright © 2013      Jasper St. Pierre
+ * Copyright © 2010-2013 Intel Corporation
+ * Copyright © 2015-2017 Samsung Electronics Co., Ltd
+ * Copyright © 2015-2017 Red Hat Inc.
+ *
+ * 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.
+ * 
+ */ +struct wl_output; +struct wl_seat; +struct wl_surface; +struct xdg_popup; +struct xdg_positioner; +struct xdg_surface; +struct xdg_toplevel; +struct xdg_wm_base; + +#ifndef XDG_WM_BASE_INTERFACE +#define XDG_WM_BASE_INTERFACE +/** + * @page page_iface_xdg_wm_base xdg_wm_base + * @section page_iface_xdg_wm_base_desc Description + * + * The xdg_wm_base interface is exposed as a global object enabling clients + * to turn their wl_surfaces into windows in a desktop environment. It + * defines the basic functionality needed for clients and the compositor to + * create windows that can be dragged, resized, maximized, etc, as well as + * creating transient windows such as popup menus. + * @section page_iface_xdg_wm_base_api API + * See @ref iface_xdg_wm_base. + */ +/** + * @defgroup iface_xdg_wm_base The xdg_wm_base interface + * + * The xdg_wm_base interface is exposed as a global object enabling clients + * to turn their wl_surfaces into windows in a desktop environment. It + * defines the basic functionality needed for clients and the compositor to + * create windows that can be dragged, resized, maximized, etc, as well as + * creating transient windows such as popup menus. + */ +extern const struct wl_interface xdg_wm_base_interface; +#endif +#ifndef XDG_POSITIONER_INTERFACE +#define XDG_POSITIONER_INTERFACE +/** + * @page page_iface_xdg_positioner xdg_positioner + * @section page_iface_xdg_positioner_desc Description + * + * The xdg_positioner provides a collection of rules for the placement of a + * child surface relative to a parent surface. Rules can be defined to ensure + * the child surface remains within the visible area's borders, and to + * specify how the child surface changes its position, such as sliding along + * an axis, or flipping around a rectangle. These positioner-created rules are + * constrained by the requirement that a child surface must intersect with or + * be at least partially adjacent to its parent surface. + * + * See the various requests for details about possible rules. + * + * At the time of the request, the compositor makes a copy of the rules + * specified by the xdg_positioner. Thus, after the request is complete the + * xdg_positioner object can be destroyed or reused; further changes to the + * object will have no effect on previous usages. + * + * For an xdg_positioner object to be considered complete, it must have a + * non-zero size set by set_size, and a non-zero anchor rectangle set by + * set_anchor_rect. Passing an incomplete xdg_positioner object when + * positioning a surface raises an invalid_positioner error. + * @section page_iface_xdg_positioner_api API + * See @ref iface_xdg_positioner. + */ +/** + * @defgroup iface_xdg_positioner The xdg_positioner interface + * + * The xdg_positioner provides a collection of rules for the placement of a + * child surface relative to a parent surface. Rules can be defined to ensure + * the child surface remains within the visible area's borders, and to + * specify how the child surface changes its position, such as sliding along + * an axis, or flipping around a rectangle. These positioner-created rules are + * constrained by the requirement that a child surface must intersect with or + * be at least partially adjacent to its parent surface. + * + * See the various requests for details about possible rules. + * + * At the time of the request, the compositor makes a copy of the rules + * specified by the xdg_positioner. Thus, after the request is complete the + * xdg_positioner object can be destroyed or reused; further changes to the + * object will have no effect on previous usages. + * + * For an xdg_positioner object to be considered complete, it must have a + * non-zero size set by set_size, and a non-zero anchor rectangle set by + * set_anchor_rect. Passing an incomplete xdg_positioner object when + * positioning a surface raises an invalid_positioner error. + */ +extern const struct wl_interface xdg_positioner_interface; +#endif +#ifndef XDG_SURFACE_INTERFACE +#define XDG_SURFACE_INTERFACE +/** + * @page page_iface_xdg_surface xdg_surface + * @section page_iface_xdg_surface_desc Description + * + * An interface that may be implemented by a wl_surface, for + * implementations that provide a desktop-style user interface. + * + * It provides a base set of functionality required to construct user + * interface elements requiring management by the compositor, such as + * toplevel windows, menus, etc. The types of functionality are split into + * xdg_surface roles. + * + * Creating an xdg_surface does not set the role for a wl_surface. In order + * to map an xdg_surface, the client must create a role-specific object + * using, e.g., get_toplevel, get_popup. The wl_surface for any given + * xdg_surface can have at most one role, and may not be assigned any role + * not based on xdg_surface. + * + * A role must be assigned before any other requests are made to the + * xdg_surface object. + * + * The client must call wl_surface.commit on the corresponding wl_surface + * for the xdg_surface state to take effect. + * + * Creating an xdg_surface from a wl_surface which has a buffer attached or + * committed is a client error, and any attempts by a client to attach or + * manipulate a buffer prior to the first xdg_surface.configure call must + * also be treated as errors. + * + * After creating a role-specific object and setting it up (e.g. by sending + * the title, app ID, size constraints, parent, etc), the client must + * perform an initial commit without any buffer attached. The compositor + * will reply with initial wl_surface state such as + * wl_surface.preferred_buffer_scale followed by an xdg_surface.configure + * event. The client must acknowledge it and is then allowed to attach a + * buffer to map the surface. + * + * Mapping an xdg_surface-based role surface is defined as making it + * possible for the surface to be shown by the compositor. Note that + * a mapped surface is not guaranteed to be visible once it is mapped. + * + * For an xdg_surface to be mapped by the compositor, the following + * conditions must be met: + * (1) the client has assigned an xdg_surface-based role to the surface + * (2) the client has set and committed the xdg_surface state and the + * role-dependent state to the surface + * (3) the client has committed a buffer to the surface + * + * A newly-unmapped surface is considered to have met condition (1) out + * of the 3 required conditions for mapping a surface if its role surface + * has not been destroyed, i.e. the client must perform the initial commit + * again before attaching a buffer. + * @section page_iface_xdg_surface_api API + * See @ref iface_xdg_surface. + */ +/** + * @defgroup iface_xdg_surface The xdg_surface interface + * + * An interface that may be implemented by a wl_surface, for + * implementations that provide a desktop-style user interface. + * + * It provides a base set of functionality required to construct user + * interface elements requiring management by the compositor, such as + * toplevel windows, menus, etc. The types of functionality are split into + * xdg_surface roles. + * + * Creating an xdg_surface does not set the role for a wl_surface. In order + * to map an xdg_surface, the client must create a role-specific object + * using, e.g., get_toplevel, get_popup. The wl_surface for any given + * xdg_surface can have at most one role, and may not be assigned any role + * not based on xdg_surface. + * + * A role must be assigned before any other requests are made to the + * xdg_surface object. + * + * The client must call wl_surface.commit on the corresponding wl_surface + * for the xdg_surface state to take effect. + * + * Creating an xdg_surface from a wl_surface which has a buffer attached or + * committed is a client error, and any attempts by a client to attach or + * manipulate a buffer prior to the first xdg_surface.configure call must + * also be treated as errors. + * + * After creating a role-specific object and setting it up (e.g. by sending + * the title, app ID, size constraints, parent, etc), the client must + * perform an initial commit without any buffer attached. The compositor + * will reply with initial wl_surface state such as + * wl_surface.preferred_buffer_scale followed by an xdg_surface.configure + * event. The client must acknowledge it and is then allowed to attach a + * buffer to map the surface. + * + * Mapping an xdg_surface-based role surface is defined as making it + * possible for the surface to be shown by the compositor. Note that + * a mapped surface is not guaranteed to be visible once it is mapped. + * + * For an xdg_surface to be mapped by the compositor, the following + * conditions must be met: + * (1) the client has assigned an xdg_surface-based role to the surface + * (2) the client has set and committed the xdg_surface state and the + * role-dependent state to the surface + * (3) the client has committed a buffer to the surface + * + * A newly-unmapped surface is considered to have met condition (1) out + * of the 3 required conditions for mapping a surface if its role surface + * has not been destroyed, i.e. the client must perform the initial commit + * again before attaching a buffer. + */ +extern const struct wl_interface xdg_surface_interface; +#endif +#ifndef XDG_TOPLEVEL_INTERFACE +#define XDG_TOPLEVEL_INTERFACE +/** + * @page page_iface_xdg_toplevel xdg_toplevel + * @section page_iface_xdg_toplevel_desc Description + * + * This interface defines an xdg_surface role which allows a surface to, + * among other things, set window-like properties such as maximize, + * fullscreen, and minimize, set application-specific metadata like title and + * id, and well as trigger user interactive operations such as interactive + * resize and move. + * + * A xdg_toplevel by default is responsible for providing the full intended + * visual representation of the toplevel, which depending on the window + * state, may mean things like a title bar, window controls and drop shadow. + * + * Unmapping an xdg_toplevel means that the surface cannot be shown + * by the compositor until it is explicitly mapped again. + * All active operations (e.g., move, resize) are canceled and all + * attributes (e.g. title, state, stacking, ...) are discarded for + * an xdg_toplevel surface when it is unmapped. The xdg_toplevel returns to + * the state it had right after xdg_surface.get_toplevel. The client + * can re-map the toplevel by performing a commit without any buffer + * attached, waiting for a configure event and handling it as usual (see + * xdg_surface description). + * + * Attaching a null buffer to a toplevel unmaps the surface. + * @section page_iface_xdg_toplevel_api API + * See @ref iface_xdg_toplevel. + */ +/** + * @defgroup iface_xdg_toplevel The xdg_toplevel interface + * + * This interface defines an xdg_surface role which allows a surface to, + * among other things, set window-like properties such as maximize, + * fullscreen, and minimize, set application-specific metadata like title and + * id, and well as trigger user interactive operations such as interactive + * resize and move. + * + * A xdg_toplevel by default is responsible for providing the full intended + * visual representation of the toplevel, which depending on the window + * state, may mean things like a title bar, window controls and drop shadow. + * + * Unmapping an xdg_toplevel means that the surface cannot be shown + * by the compositor until it is explicitly mapped again. + * All active operations (e.g., move, resize) are canceled and all + * attributes (e.g. title, state, stacking, ...) are discarded for + * an xdg_toplevel surface when it is unmapped. The xdg_toplevel returns to + * the state it had right after xdg_surface.get_toplevel. The client + * can re-map the toplevel by performing a commit without any buffer + * attached, waiting for a configure event and handling it as usual (see + * xdg_surface description). + * + * Attaching a null buffer to a toplevel unmaps the surface. + */ +extern const struct wl_interface xdg_toplevel_interface; +#endif +#ifndef XDG_POPUP_INTERFACE +#define XDG_POPUP_INTERFACE +/** + * @page page_iface_xdg_popup xdg_popup + * @section page_iface_xdg_popup_desc Description + * + * A popup surface is a short-lived, temporary surface. It can be used to + * implement for example menus, popovers, tooltips and other similar user + * interface concepts. + * + * A popup can be made to take an explicit grab. See xdg_popup.grab for + * details. + * + * When the popup is dismissed, a popup_done event will be sent out, and at + * the same time the surface will be unmapped. See the xdg_popup.popup_done + * event for details. + * + * Explicitly destroying the xdg_popup object will also dismiss the popup and + * unmap the surface. Clients that want to dismiss the popup when another + * surface of their own is clicked should dismiss the popup using the destroy + * request. + * + * A newly created xdg_popup will be stacked on top of all previously created + * xdg_popup surfaces associated with the same xdg_toplevel. + * + * The parent of an xdg_popup must be mapped (see the xdg_surface + * description) before the xdg_popup itself. + * + * The client must call wl_surface.commit on the corresponding wl_surface + * for the xdg_popup state to take effect. + * @section page_iface_xdg_popup_api API + * See @ref iface_xdg_popup. + */ +/** + * @defgroup iface_xdg_popup The xdg_popup interface + * + * A popup surface is a short-lived, temporary surface. It can be used to + * implement for example menus, popovers, tooltips and other similar user + * interface concepts. + * + * A popup can be made to take an explicit grab. See xdg_popup.grab for + * details. + * + * When the popup is dismissed, a popup_done event will be sent out, and at + * the same time the surface will be unmapped. See the xdg_popup.popup_done + * event for details. + * + * Explicitly destroying the xdg_popup object will also dismiss the popup and + * unmap the surface. Clients that want to dismiss the popup when another + * surface of their own is clicked should dismiss the popup using the destroy + * request. + * + * A newly created xdg_popup will be stacked on top of all previously created + * xdg_popup surfaces associated with the same xdg_toplevel. + * + * The parent of an xdg_popup must be mapped (see the xdg_surface + * description) before the xdg_popup itself. + * + * The client must call wl_surface.commit on the corresponding wl_surface + * for the xdg_popup state to take effect. + */ +extern const struct wl_interface xdg_popup_interface; +#endif + +#ifndef XDG_WM_BASE_ERROR_ENUM +#define XDG_WM_BASE_ERROR_ENUM +enum xdg_wm_base_error { + /** + * given wl_surface has another role + */ + XDG_WM_BASE_ERROR_ROLE = 0, + /** + * xdg_wm_base was destroyed before children + */ + XDG_WM_BASE_ERROR_DEFUNCT_SURFACES = 1, + /** + * the client tried to map or destroy a non-topmost popup + */ + XDG_WM_BASE_ERROR_NOT_THE_TOPMOST_POPUP = 2, + /** + * the client specified an invalid popup parent surface + */ + XDG_WM_BASE_ERROR_INVALID_POPUP_PARENT = 3, + /** + * the client provided an invalid surface state + */ + XDG_WM_BASE_ERROR_INVALID_SURFACE_STATE = 4, + /** + * the client provided an invalid positioner + */ + XDG_WM_BASE_ERROR_INVALID_POSITIONER = 5, + /** + * the client didn’t respond to a ping event in time + */ + XDG_WM_BASE_ERROR_UNRESPONSIVE = 6, +}; +#endif /* XDG_WM_BASE_ERROR_ENUM */ + +#ifndef XDG_WM_BASE_ERROR_ENUM_IS_VALID +#define XDG_WM_BASE_ERROR_ENUM_IS_VALID +/** + * @ingroup iface_xdg_wm_base + * Validate a xdg_wm_base error value. + * + * @return true on success, false on error. + * @ref xdg_wm_base_error + */ +static inline bool +xdg_wm_base_error_is_valid(uint32_t value, uint32_t version) { + switch (value) { + case XDG_WM_BASE_ERROR_ROLE: + return version >= 1; + case XDG_WM_BASE_ERROR_DEFUNCT_SURFACES: + return version >= 1; + case XDG_WM_BASE_ERROR_NOT_THE_TOPMOST_POPUP: + return version >= 1; + case XDG_WM_BASE_ERROR_INVALID_POPUP_PARENT: + return version >= 1; + case XDG_WM_BASE_ERROR_INVALID_SURFACE_STATE: + return version >= 1; + case XDG_WM_BASE_ERROR_INVALID_POSITIONER: + return version >= 1; + case XDG_WM_BASE_ERROR_UNRESPONSIVE: + return version >= 1; + default: + return false; + } +} +#endif /* XDG_WM_BASE_ERROR_ENUM_IS_VALID */ + +/** + * @ingroup iface_xdg_wm_base + * @struct xdg_wm_base_interface + */ +struct xdg_wm_base_interface { + /** + * destroy xdg_wm_base + * + * Destroy this xdg_wm_base object. + * + * Destroying a bound xdg_wm_base object while there are surfaces + * still alive created by this xdg_wm_base object instance is + * illegal and will result in a defunct_surfaces error. + */ + void (*destroy)(struct wl_client *client, + struct wl_resource *resource); + /** + * create a positioner object + * + * Create a positioner object. A positioner object is used to + * position surfaces relative to some parent surface. See the + * interface description and xdg_surface.get_popup for details. + */ + void (*create_positioner)(struct wl_client *client, + struct wl_resource *resource, + uint32_t id); + /** + * create a shell surface from a surface + * + * This creates an xdg_surface for the given surface. While + * xdg_surface itself is not a role, the corresponding surface may + * only be assigned a role extending xdg_surface, such as + * xdg_toplevel or xdg_popup. It is illegal to create an + * xdg_surface for a wl_surface which already has an assigned role + * and this will result in a role error. + * + * This creates an xdg_surface for the given surface. An + * xdg_surface is used as basis to define a role to a given + * surface, such as xdg_toplevel or xdg_popup. It also manages + * functionality shared between xdg_surface based surface roles. + * + * See the documentation of xdg_surface for more details about what + * an xdg_surface is and how it is used. + */ + void (*get_xdg_surface)(struct wl_client *client, + struct wl_resource *resource, + uint32_t id, + struct wl_resource *surface); + /** + * respond to a ping event + * + * A client must respond to a ping event with a pong request or + * the client may be deemed unresponsive. See xdg_wm_base.ping and + * xdg_wm_base.error.unresponsive. + * @param serial serial of the ping event + */ + void (*pong)(struct wl_client *client, + struct wl_resource *resource, + uint32_t serial); +}; + +#define XDG_WM_BASE_PING 0 + +/** + * @ingroup iface_xdg_wm_base + */ +#define XDG_WM_BASE_PING_SINCE_VERSION 1 + +/** + * @ingroup iface_xdg_wm_base + */ +#define XDG_WM_BASE_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_wm_base + */ +#define XDG_WM_BASE_CREATE_POSITIONER_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_wm_base + */ +#define XDG_WM_BASE_GET_XDG_SURFACE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_wm_base + */ +#define XDG_WM_BASE_PONG_SINCE_VERSION 1 + +/** + * @ingroup iface_xdg_wm_base + * Sends an ping event to the client owning the resource. + * @param resource_ The client's resource + * @param serial pass this to the pong request + */ +static inline void +xdg_wm_base_send_ping(struct wl_resource *resource_, uint32_t serial) +{ + wl_resource_post_event(resource_, XDG_WM_BASE_PING, serial); +} + +#ifndef XDG_POSITIONER_ERROR_ENUM +#define XDG_POSITIONER_ERROR_ENUM +enum xdg_positioner_error { + /** + * invalid input provided + */ + XDG_POSITIONER_ERROR_INVALID_INPUT = 0, +}; +#endif /* XDG_POSITIONER_ERROR_ENUM */ + +#ifndef XDG_POSITIONER_ERROR_ENUM_IS_VALID +#define XDG_POSITIONER_ERROR_ENUM_IS_VALID +/** + * @ingroup iface_xdg_positioner + * Validate a xdg_positioner error value. + * + * @return true on success, false on error. + * @ref xdg_positioner_error + */ +static inline bool +xdg_positioner_error_is_valid(uint32_t value, uint32_t version) { + switch (value) { + case XDG_POSITIONER_ERROR_INVALID_INPUT: + return version >= 1; + default: + return false; + } +} +#endif /* XDG_POSITIONER_ERROR_ENUM_IS_VALID */ + +#ifndef XDG_POSITIONER_ANCHOR_ENUM +#define XDG_POSITIONER_ANCHOR_ENUM +enum xdg_positioner_anchor { + XDG_POSITIONER_ANCHOR_NONE = 0, + XDG_POSITIONER_ANCHOR_TOP = 1, + XDG_POSITIONER_ANCHOR_BOTTOM = 2, + XDG_POSITIONER_ANCHOR_LEFT = 3, + XDG_POSITIONER_ANCHOR_RIGHT = 4, + XDG_POSITIONER_ANCHOR_TOP_LEFT = 5, + XDG_POSITIONER_ANCHOR_BOTTOM_LEFT = 6, + XDG_POSITIONER_ANCHOR_TOP_RIGHT = 7, + XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT = 8, +}; +#endif /* XDG_POSITIONER_ANCHOR_ENUM */ + +#ifndef XDG_POSITIONER_ANCHOR_ENUM_IS_VALID +#define XDG_POSITIONER_ANCHOR_ENUM_IS_VALID +/** + * @ingroup iface_xdg_positioner + * Validate a xdg_positioner anchor value. + * + * @return true on success, false on error. + * @ref xdg_positioner_anchor + */ +static inline bool +xdg_positioner_anchor_is_valid(uint32_t value, uint32_t version) { + switch (value) { + case XDG_POSITIONER_ANCHOR_NONE: + return version >= 1; + case XDG_POSITIONER_ANCHOR_TOP: + return version >= 1; + case XDG_POSITIONER_ANCHOR_BOTTOM: + return version >= 1; + case XDG_POSITIONER_ANCHOR_LEFT: + return version >= 1; + case XDG_POSITIONER_ANCHOR_RIGHT: + return version >= 1; + case XDG_POSITIONER_ANCHOR_TOP_LEFT: + return version >= 1; + case XDG_POSITIONER_ANCHOR_BOTTOM_LEFT: + return version >= 1; + case XDG_POSITIONER_ANCHOR_TOP_RIGHT: + return version >= 1; + case XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT: + return version >= 1; + default: + return false; + } +} +#endif /* XDG_POSITIONER_ANCHOR_ENUM_IS_VALID */ + +#ifndef XDG_POSITIONER_GRAVITY_ENUM +#define XDG_POSITIONER_GRAVITY_ENUM +enum xdg_positioner_gravity { + XDG_POSITIONER_GRAVITY_NONE = 0, + XDG_POSITIONER_GRAVITY_TOP = 1, + XDG_POSITIONER_GRAVITY_BOTTOM = 2, + XDG_POSITIONER_GRAVITY_LEFT = 3, + XDG_POSITIONER_GRAVITY_RIGHT = 4, + XDG_POSITIONER_GRAVITY_TOP_LEFT = 5, + XDG_POSITIONER_GRAVITY_BOTTOM_LEFT = 6, + XDG_POSITIONER_GRAVITY_TOP_RIGHT = 7, + XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT = 8, +}; +#endif /* XDG_POSITIONER_GRAVITY_ENUM */ + +#ifndef XDG_POSITIONER_GRAVITY_ENUM_IS_VALID +#define XDG_POSITIONER_GRAVITY_ENUM_IS_VALID +/** + * @ingroup iface_xdg_positioner + * Validate a xdg_positioner gravity value. + * + * @return true on success, false on error. + * @ref xdg_positioner_gravity + */ +static inline bool +xdg_positioner_gravity_is_valid(uint32_t value, uint32_t version) { + switch (value) { + case XDG_POSITIONER_GRAVITY_NONE: + return version >= 1; + case XDG_POSITIONER_GRAVITY_TOP: + return version >= 1; + case XDG_POSITIONER_GRAVITY_BOTTOM: + return version >= 1; + case XDG_POSITIONER_GRAVITY_LEFT: + return version >= 1; + case XDG_POSITIONER_GRAVITY_RIGHT: + return version >= 1; + case XDG_POSITIONER_GRAVITY_TOP_LEFT: + return version >= 1; + case XDG_POSITIONER_GRAVITY_BOTTOM_LEFT: + return version >= 1; + case XDG_POSITIONER_GRAVITY_TOP_RIGHT: + return version >= 1; + case XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT: + return version >= 1; + default: + return false; + } +} +#endif /* XDG_POSITIONER_GRAVITY_ENUM_IS_VALID */ + +#ifndef XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_ENUM +#define XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_ENUM +/** + * @ingroup iface_xdg_positioner + * constraint adjustments + * + * The constraint adjustment value define ways the compositor will adjust + * the position of the surface, if the unadjusted position would result + * in the surface being partly constrained. + * + * Whether a surface is considered 'constrained' is left to the compositor + * to determine. For example, the surface may be partly outside the + * compositor's defined 'work area', thus necessitating the child surface's + * position be adjusted until it is entirely inside the work area. + * + * The adjustments can be combined, according to a defined precedence: 1) + * Flip, 2) Slide, 3) Resize. + */ +enum xdg_positioner_constraint_adjustment { + /** + * don't move the child surface when constrained + * + * Don't alter the surface position even if it is constrained on + * some axis, for example partially outside the edge of an output. + */ + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_NONE = 0, + /** + * move along the x axis until unconstrained + * + * Slide the surface along the x axis until it is no longer + * constrained. + * + * First try to slide towards the direction of the gravity on the x + * axis until either the edge in the opposite direction of the + * gravity is unconstrained or the edge in the direction of the + * gravity is constrained. + * + * Then try to slide towards the opposite direction of the gravity + * on the x axis until either the edge in the direction of the + * gravity is unconstrained or the edge in the opposite direction + * of the gravity is constrained. + */ + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X = 1, + /** + * move along the y axis until unconstrained + * + * Slide the surface along the y axis until it is no longer + * constrained. + * + * First try to slide towards the direction of the gravity on the y + * axis until either the edge in the opposite direction of the + * gravity is unconstrained or the edge in the direction of the + * gravity is constrained. + * + * Then try to slide towards the opposite direction of the gravity + * on the y axis until either the edge in the direction of the + * gravity is unconstrained or the edge in the opposite direction + * of the gravity is constrained. + */ + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y = 2, + /** + * invert the anchor and gravity on the x axis + * + * Invert the anchor and gravity on the x axis if the surface is + * constrained on the x axis. For example, if the left edge of the + * surface is constrained, the gravity is 'left' and the anchor is + * 'left', change the gravity to 'right' and the anchor to 'right'. + * + * If the adjusted position also ends up being constrained, the + * resulting position of the flip_x adjustment will be the one + * before the adjustment. + */ + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_X = 4, + /** + * invert the anchor and gravity on the y axis + * + * Invert the anchor and gravity on the y axis if the surface is + * constrained on the y axis. For example, if the bottom edge of + * the surface is constrained, the gravity is 'bottom' and the + * anchor is 'bottom', change the gravity to 'top' and the anchor + * to 'top'. + * + * The adjusted position is calculated given the original anchor + * rectangle and offset, but with the new flipped anchor and + * gravity values. + * + * If the adjusted position also ends up being constrained, the + * resulting position of the flip_y adjustment will be the one + * before the adjustment. + */ + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y = 8, + /** + * horizontally resize the surface + * + * Resize the surface horizontally so that it is completely + * unconstrained. + */ + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_X = 16, + /** + * vertically resize the surface + * + * Resize the surface vertically so that it is completely + * unconstrained. + */ + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_Y = 32, +}; +#endif /* XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_ENUM */ + +#ifndef XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_ENUM_IS_VALID +#define XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_ENUM_IS_VALID +/** + * @ingroup iface_xdg_positioner + * Validate a xdg_positioner constraint_adjustment value. + * + * @return true on success, false on error. + * @ref xdg_positioner_constraint_adjustment + */ +static inline bool +xdg_positioner_constraint_adjustment_is_valid(uint32_t value, uint32_t version) { + uint32_t valid = 0; + if (version >= 1) + valid |= XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_NONE; + if (version >= 1) + valid |= XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X; + if (version >= 1) + valid |= XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y; + if (version >= 1) + valid |= XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_X; + if (version >= 1) + valid |= XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y; + if (version >= 1) + valid |= XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_X; + if (version >= 1) + valid |= XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_Y; + return (value & ~valid) == 0; +} +#endif /* XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_ENUM_IS_VALID */ + +/** + * @ingroup iface_xdg_positioner + * @struct xdg_positioner_interface + */ +struct xdg_positioner_interface { + /** + * destroy the xdg_positioner object + * + * Notify the compositor that the xdg_positioner will no longer + * be used. + */ + void (*destroy)(struct wl_client *client, + struct wl_resource *resource); + /** + * set the size of the to-be positioned rectangle + * + * Set the size of the surface that is to be positioned with the + * positioner object. The size is in surface-local coordinates and + * corresponds to the window geometry. See + * xdg_surface.set_window_geometry. + * + * If a zero or negative size is set the invalid_input error is + * raised. + * @param width width of positioned rectangle + * @param height height of positioned rectangle + */ + void (*set_size)(struct wl_client *client, + struct wl_resource *resource, + int32_t width, + int32_t height); + /** + * set the anchor rectangle within the parent surface + * + * Specify the anchor rectangle within the parent surface that + * the child surface will be placed relative to. The rectangle is + * relative to the window geometry as defined by + * xdg_surface.set_window_geometry of the parent surface. + * + * When the xdg_positioner object is used to position a child + * surface, the anchor rectangle may not extend outside the window + * geometry of the positioned child's parent surface. + * + * If a negative size is set the invalid_input error is raised. + * @param x x position of anchor rectangle + * @param y y position of anchor rectangle + * @param width width of anchor rectangle + * @param height height of anchor rectangle + */ + void (*set_anchor_rect)(struct wl_client *client, + struct wl_resource *resource, + int32_t x, + int32_t y, + int32_t width, + int32_t height); + /** + * set anchor rectangle anchor + * + * Defines the anchor point for the anchor rectangle. The + * specified anchor is used derive an anchor point that the child + * surface will be positioned relative to. If a corner anchor is + * set (e.g. 'top_left' or 'bottom_right'), the anchor point will + * be at the specified corner; otherwise, the derived anchor point + * will be centered on the specified edge, or in the center of the + * anchor rectangle if no edge is specified. + * @param anchor anchor + */ + void (*set_anchor)(struct wl_client *client, + struct wl_resource *resource, + uint32_t anchor); + /** + * set child surface gravity + * + * Defines in what direction a surface should be positioned, + * relative to the anchor point of the parent surface. If a corner + * gravity is specified (e.g. 'bottom_right' or 'top_left'), then + * the child surface will be placed towards the specified gravity; + * otherwise, the child surface will be centered over the anchor + * point on any axis that had no gravity specified. If the gravity + * is not in the ‘gravity’ enum, an invalid_input error is + * raised. + * @param gravity gravity direction + */ + void (*set_gravity)(struct wl_client *client, + struct wl_resource *resource, + uint32_t gravity); + /** + * set the adjustment to be done when constrained + * + * Specify how the window should be positioned if the originally + * intended position caused the surface to be constrained, meaning + * at least partially outside positioning boundaries set by the + * compositor. The adjustment is set by constructing a bitmask + * describing the adjustment to be made when the surface is + * constrained on that axis. + * + * If no bit for one axis is set, the compositor will assume that + * the child surface should not change its position on that axis + * when constrained. + * + * If more than one bit for one axis is set, the order of how + * adjustments are applied is specified in the corresponding + * adjustment descriptions. + * + * The default adjustment is none. + * @param constraint_adjustment bit mask of constraint adjustments + */ + void (*set_constraint_adjustment)(struct wl_client *client, + struct wl_resource *resource, + uint32_t constraint_adjustment); + /** + * set surface position offset + * + * Specify the surface position offset relative to the position + * of the anchor on the anchor rectangle and the anchor on the + * surface. For example if the anchor of the anchor rectangle is at + * (x, y), the surface has the gravity bottom|right, and the offset + * is (ox, oy), the calculated surface position will be (x + ox, y + * + oy). The offset position of the surface is the one used for + * constraint testing. See set_constraint_adjustment. + * + * An example use case is placing a popup menu on top of a user + * interface element, while aligning the user interface element of + * the parent surface with some user interface element placed + * somewhere in the popup surface. + * @param x surface position x offset + * @param y surface position y offset + */ + void (*set_offset)(struct wl_client *client, + struct wl_resource *resource, + int32_t x, + int32_t y); + /** + * continuously reconstrain the surface + * + * When set reactive, the surface is reconstrained if the + * conditions used for constraining changed, e.g. the parent window + * moved. + * + * If the conditions changed and the popup was reconstrained, an + * xdg_popup.configure event is sent with updated geometry, + * followed by an xdg_surface.configure event. + * @since 3 + */ + void (*set_reactive)(struct wl_client *client, + struct wl_resource *resource); + /** + * + * + * Set the parent window geometry the compositor should use when + * positioning the popup. The compositor may use this information + * to determine the future state the popup should be constrained + * using. If this doesn't match the dimension of the parent the + * popup is eventually positioned against, the behavior is + * undefined. + * + * The arguments are given in the surface-local coordinate space. + * @param parent_width future window geometry width of parent + * @param parent_height future window geometry height of parent + * @since 3 + */ + void (*set_parent_size)(struct wl_client *client, + struct wl_resource *resource, + int32_t parent_width, + int32_t parent_height); + /** + * set parent configure this is a response to + * + * Set the serial of an xdg_surface.configure event this + * positioner will be used in response to. The compositor may use + * this information together with set_parent_size to determine what + * future state the popup should be constrained using. + * @param serial serial of parent configure event + * @since 3 + */ + void (*set_parent_configure)(struct wl_client *client, + struct wl_resource *resource, + uint32_t serial); +}; + + +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_SET_SIZE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_SET_ANCHOR_RECT_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_SET_ANCHOR_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_SET_GRAVITY_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_SET_CONSTRAINT_ADJUSTMENT_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_SET_OFFSET_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_SET_REACTIVE_SINCE_VERSION 3 +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_SET_PARENT_SIZE_SINCE_VERSION 3 +/** + * @ingroup iface_xdg_positioner + */ +#define XDG_POSITIONER_SET_PARENT_CONFIGURE_SINCE_VERSION 3 + +#ifndef XDG_SURFACE_ERROR_ENUM +#define XDG_SURFACE_ERROR_ENUM +enum xdg_surface_error { + /** + * Surface was not fully constructed + */ + XDG_SURFACE_ERROR_NOT_CONSTRUCTED = 1, + /** + * Surface was already constructed + */ + XDG_SURFACE_ERROR_ALREADY_CONSTRUCTED = 2, + /** + * Attaching a buffer to an unconfigured surface + */ + XDG_SURFACE_ERROR_UNCONFIGURED_BUFFER = 3, + /** + * Invalid serial number when acking a configure event + */ + XDG_SURFACE_ERROR_INVALID_SERIAL = 4, + /** + * Width or height was zero or negative + */ + XDG_SURFACE_ERROR_INVALID_SIZE = 5, + /** + * Surface was destroyed before its role object + */ + XDG_SURFACE_ERROR_DEFUNCT_ROLE_OBJECT = 6, +}; +#endif /* XDG_SURFACE_ERROR_ENUM */ + +#ifndef XDG_SURFACE_ERROR_ENUM_IS_VALID +#define XDG_SURFACE_ERROR_ENUM_IS_VALID +/** + * @ingroup iface_xdg_surface + * Validate a xdg_surface error value. + * + * @return true on success, false on error. + * @ref xdg_surface_error + */ +static inline bool +xdg_surface_error_is_valid(uint32_t value, uint32_t version) { + switch (value) { + case XDG_SURFACE_ERROR_NOT_CONSTRUCTED: + return version >= 1; + case XDG_SURFACE_ERROR_ALREADY_CONSTRUCTED: + return version >= 1; + case XDG_SURFACE_ERROR_UNCONFIGURED_BUFFER: + return version >= 1; + case XDG_SURFACE_ERROR_INVALID_SERIAL: + return version >= 1; + case XDG_SURFACE_ERROR_INVALID_SIZE: + return version >= 1; + case XDG_SURFACE_ERROR_DEFUNCT_ROLE_OBJECT: + return version >= 1; + default: + return false; + } +} +#endif /* XDG_SURFACE_ERROR_ENUM_IS_VALID */ + +/** + * @ingroup iface_xdg_surface + * @struct xdg_surface_interface + */ +struct xdg_surface_interface { + /** + * destroy the xdg_surface + * + * Destroy the xdg_surface object. An xdg_surface must only be + * destroyed after its role object has been destroyed, otherwise a + * defunct_role_object error is raised. + */ + void (*destroy)(struct wl_client *client, + struct wl_resource *resource); + /** + * assign the xdg_toplevel surface role + * + * This creates an xdg_toplevel object for the given xdg_surface + * and gives the associated wl_surface the xdg_toplevel role. + * + * See the documentation of xdg_toplevel for more details about + * what an xdg_toplevel is and how it is used. + */ + void (*get_toplevel)(struct wl_client *client, + struct wl_resource *resource, + uint32_t id); + /** + * assign the xdg_popup surface role + * + * This creates an xdg_popup object for the given xdg_surface and + * gives the associated wl_surface the xdg_popup role. + * + * If null is passed as a parent, a parent surface must be + * specified using some other protocol, before committing the + * initial state. + * + * See the documentation of xdg_popup for more details about what + * an xdg_popup is and how it is used. + */ + void (*get_popup)(struct wl_client *client, + struct wl_resource *resource, + uint32_t id, + struct wl_resource *parent, + struct wl_resource *positioner); + /** + * set the new window geometry + * + * The window geometry of a surface is its "visible bounds" from + * the user's perspective. Client-side decorations often have + * invisible portions like drop-shadows which should be ignored for + * the purposes of aligning, placing and constraining windows. Note + * that in some situations, compositors may clip rendering to the + * window geometry, so the client should avoid putting functional + * elements outside of it. + * + * The window geometry is double-buffered state, see + * wl_surface.commit. + * + * When maintaining a position, the compositor should treat the (x, + * y) coordinate of the window geometry as the top left corner of + * the window. A client changing the (x, y) window geometry + * coordinate should in general not alter the position of the + * window. + * + * Once the window geometry of the surface is set, it is not + * possible to unset it, and it will remain the same until + * set_window_geometry is called again, even if a new subsurface or + * buffer is attached. + * + * If never set, the value is the full bounds of the surface, + * including any subsurfaces. This updates dynamically on every + * commit. This unset is meant for extremely simple clients. + * + * The arguments are given in the surface-local coordinate space of + * the wl_surface associated with this xdg_surface, and may extend + * outside of the wl_surface itself to mark parts of the subsurface + * tree as part of the window geometry. + * + * When applied, the effective window geometry will be the set + * window geometry clamped to the bounding rectangle of the + * combined geometry of the surface of the xdg_surface and the + * associated subsurfaces. + * + * The effective geometry will not be recalculated unless a new + * call to set_window_geometry is done and the new pending surface + * state is subsequently applied. + * + * The width and height of the effective window geometry must be + * greater than zero. Setting an invalid size will raise an + * invalid_size error. + */ + void (*set_window_geometry)(struct wl_client *client, + struct wl_resource *resource, + int32_t x, + int32_t y, + int32_t width, + int32_t height); + /** + * ack a configure event + * + * When a configure event is received, if a client commits the + * surface in response to the configure event, then the client must + * make an ack_configure request sometime before the commit + * request, passing along the serial of the configure event. + * + * For instance, for toplevel surfaces the compositor might use + * this information to move a surface to the top left only when the + * client has drawn itself for the maximized or fullscreen state. + * + * If the client receives multiple configure events before it can + * respond to one, it only has to ack the last configure event. + * Acking a configure event that was never sent raises an + * invalid_serial error. + * + * A client is not required to commit immediately after sending an + * ack_configure request - it may even ack_configure several times + * before its next surface commit. + * + * A client may send multiple ack_configure requests before + * committing, but only the last request sent before a commit + * indicates which configure event the client really is responding + * to. + * + * Sending an ack_configure request consumes the serial number sent + * with the request, as well as serial numbers sent by all + * configure events sent on this xdg_surface prior to the configure + * event referenced by the committed serial. + * + * It is an error to issue multiple ack_configure requests + * referencing a serial from the same configure event, or to issue + * an ack_configure request referencing a serial from a configure + * event issued before the event identified by the last + * ack_configure request for the same xdg_surface. Doing so will + * raise an invalid_serial error. + * @param serial the serial from the configure event + */ + void (*ack_configure)(struct wl_client *client, + struct wl_resource *resource, + uint32_t serial); +}; + +#define XDG_SURFACE_CONFIGURE 0 + +/** + * @ingroup iface_xdg_surface + */ +#define XDG_SURFACE_CONFIGURE_SINCE_VERSION 1 + +/** + * @ingroup iface_xdg_surface + */ +#define XDG_SURFACE_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_surface + */ +#define XDG_SURFACE_GET_TOPLEVEL_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_surface + */ +#define XDG_SURFACE_GET_POPUP_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_surface + */ +#define XDG_SURFACE_SET_WINDOW_GEOMETRY_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_surface + */ +#define XDG_SURFACE_ACK_CONFIGURE_SINCE_VERSION 1 + +/** + * @ingroup iface_xdg_surface + * Sends an configure event to the client owning the resource. + * @param resource_ The client's resource + * @param serial serial of the configure event + */ +static inline void +xdg_surface_send_configure(struct wl_resource *resource_, uint32_t serial) +{ + wl_resource_post_event(resource_, XDG_SURFACE_CONFIGURE, serial); +} + +#ifndef XDG_TOPLEVEL_ERROR_ENUM +#define XDG_TOPLEVEL_ERROR_ENUM +enum xdg_toplevel_error { + /** + * provided value is not a valid variant of the resize_edge enum + */ + XDG_TOPLEVEL_ERROR_INVALID_RESIZE_EDGE = 0, + /** + * invalid parent toplevel + */ + XDG_TOPLEVEL_ERROR_INVALID_PARENT = 1, + /** + * client provided an invalid min or max size + */ + XDG_TOPLEVEL_ERROR_INVALID_SIZE = 2, +}; +#endif /* XDG_TOPLEVEL_ERROR_ENUM */ + +#ifndef XDG_TOPLEVEL_ERROR_ENUM_IS_VALID +#define XDG_TOPLEVEL_ERROR_ENUM_IS_VALID +/** + * @ingroup iface_xdg_toplevel + * Validate a xdg_toplevel error value. + * + * @return true on success, false on error. + * @ref xdg_toplevel_error + */ +static inline bool +xdg_toplevel_error_is_valid(uint32_t value, uint32_t version) { + switch (value) { + case XDG_TOPLEVEL_ERROR_INVALID_RESIZE_EDGE: + return version >= 1; + case XDG_TOPLEVEL_ERROR_INVALID_PARENT: + return version >= 1; + case XDG_TOPLEVEL_ERROR_INVALID_SIZE: + return version >= 1; + default: + return false; + } +} +#endif /* XDG_TOPLEVEL_ERROR_ENUM_IS_VALID */ + +#ifndef XDG_TOPLEVEL_RESIZE_EDGE_ENUM +#define XDG_TOPLEVEL_RESIZE_EDGE_ENUM +/** + * @ingroup iface_xdg_toplevel + * edge values for resizing + * + * These values are used to indicate which edge of a surface + * is being dragged in a resize operation. + */ +enum xdg_toplevel_resize_edge { + XDG_TOPLEVEL_RESIZE_EDGE_NONE = 0, + XDG_TOPLEVEL_RESIZE_EDGE_TOP = 1, + XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM = 2, + XDG_TOPLEVEL_RESIZE_EDGE_LEFT = 4, + XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT = 5, + XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT = 6, + XDG_TOPLEVEL_RESIZE_EDGE_RIGHT = 8, + XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT = 9, + XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT = 10, +}; +#endif /* XDG_TOPLEVEL_RESIZE_EDGE_ENUM */ + +#ifndef XDG_TOPLEVEL_RESIZE_EDGE_ENUM_IS_VALID +#define XDG_TOPLEVEL_RESIZE_EDGE_ENUM_IS_VALID +/** + * @ingroup iface_xdg_toplevel + * Validate a xdg_toplevel resize_edge value. + * + * @return true on success, false on error. + * @ref xdg_toplevel_resize_edge + */ +static inline bool +xdg_toplevel_resize_edge_is_valid(uint32_t value, uint32_t version) { + switch (value) { + case XDG_TOPLEVEL_RESIZE_EDGE_NONE: + return version >= 1; + case XDG_TOPLEVEL_RESIZE_EDGE_TOP: + return version >= 1; + case XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM: + return version >= 1; + case XDG_TOPLEVEL_RESIZE_EDGE_LEFT: + return version >= 1; + case XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT: + return version >= 1; + case XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT: + return version >= 1; + case XDG_TOPLEVEL_RESIZE_EDGE_RIGHT: + return version >= 1; + case XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT: + return version >= 1; + case XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT: + return version >= 1; + default: + return false; + } +} +#endif /* XDG_TOPLEVEL_RESIZE_EDGE_ENUM_IS_VALID */ + +#ifndef XDG_TOPLEVEL_STATE_ENUM +#define XDG_TOPLEVEL_STATE_ENUM +/** + * @ingroup iface_xdg_toplevel + * types of state on the surface + * + * The different state values used on the surface. This is designed for + * state values like maximized, fullscreen. It is paired with the + * configure event to ensure that both the client and the compositor + * setting the state can be synchronized. + * + * States set in this way are double-buffered, see wl_surface.commit. + */ +enum xdg_toplevel_state { + /** + * the surface is maximized + * the surface is maximized + * + * The surface is maximized. The window geometry specified in the + * configure event must be obeyed by the client, or the + * xdg_wm_base.invalid_surface_state error is raised. + * + * The client should draw without shadow or other decoration + * outside of the window geometry. + */ + XDG_TOPLEVEL_STATE_MAXIMIZED = 1, + /** + * the surface is fullscreen + * the surface is fullscreen + * + * The surface is fullscreen. The window geometry specified in + * the configure event is a maximum; the client cannot resize + * beyond it. For a surface to cover the whole fullscreened area, + * the geometry dimensions must be obeyed by the client. For more + * details, see xdg_toplevel.set_fullscreen. + */ + XDG_TOPLEVEL_STATE_FULLSCREEN = 2, + /** + * the surface is being resized + * the surface is being resized + * + * The surface is being resized. The window geometry specified in + * the configure event is a maximum; the client cannot resize + * beyond it. Clients that have aspect ratio or cell sizing + * configuration can use a smaller size, however. + */ + XDG_TOPLEVEL_STATE_RESIZING = 3, + /** + * the surface is now activated + * the surface is now activated + * + * Client window decorations should be painted as if the window + * is active. Do not assume this means that the window actually has + * keyboard or pointer focus. + */ + XDG_TOPLEVEL_STATE_ACTIVATED = 4, + /** + * the surface’s left edge is tiled + * + * The window is currently in a tiled layout and the left edge is + * considered to be adjacent to another part of the tiling grid. + * + * The client should draw without shadow or other decoration + * outside of the window geometry on the left edge. + * @since 2 + */ + XDG_TOPLEVEL_STATE_TILED_LEFT = 5, + /** + * the surface’s right edge is tiled + * + * The window is currently in a tiled layout and the right edge + * is considered to be adjacent to another part of the tiling grid. + * + * The client should draw without shadow or other decoration + * outside of the window geometry on the right edge. + * @since 2 + */ + XDG_TOPLEVEL_STATE_TILED_RIGHT = 6, + /** + * the surface’s top edge is tiled + * + * The window is currently in a tiled layout and the top edge is + * considered to be adjacent to another part of the tiling grid. + * + * The client should draw without shadow or other decoration + * outside of the window geometry on the top edge. + * @since 2 + */ + XDG_TOPLEVEL_STATE_TILED_TOP = 7, + /** + * the surface’s bottom edge is tiled + * + * The window is currently in a tiled layout and the bottom edge + * is considered to be adjacent to another part of the tiling grid. + * + * The client should draw without shadow or other decoration + * outside of the window geometry on the bottom edge. + * @since 2 + */ + XDG_TOPLEVEL_STATE_TILED_BOTTOM = 8, + /** + * surface repaint is suspended + * + * The surface is currently not ordinarily being repainted; for + * example because its content is occluded by another window, or + * its outputs are switched off due to screen locking. + * @since 6 + */ + XDG_TOPLEVEL_STATE_SUSPENDED = 9, + /** + * the surface’s left edge is constrained + * + * The left edge of the window is currently constrained, meaning + * it shouldn't attempt to resize from that edge. It can for + * example mean it's tiled next to a monitor edge on the + * constrained side of the window. + * @since 7 + */ + XDG_TOPLEVEL_STATE_CONSTRAINED_LEFT = 10, + /** + * the surface’s right edge is constrained + * + * The right edge of the window is currently constrained, meaning + * it shouldn't attempt to resize from that edge. It can for + * example mean it's tiled next to a monitor edge on the + * constrained side of the window. + * @since 7 + */ + XDG_TOPLEVEL_STATE_CONSTRAINED_RIGHT = 11, + /** + * the surface’s top edge is constrained + * + * The top edge of the window is currently constrained, meaning + * it shouldn't attempt to resize from that edge. It can for + * example mean it's tiled next to a monitor edge on the + * constrained side of the window. + * @since 7 + */ + XDG_TOPLEVEL_STATE_CONSTRAINED_TOP = 12, + /** + * the surface’s bottom edge is constrained + * + * The bottom edge of the window is currently constrained, + * meaning it shouldn't attempt to resize from that edge. It can + * for example mean it's tiled next to a monitor edge on the + * constrained side of the window. + * @since 7 + */ + XDG_TOPLEVEL_STATE_CONSTRAINED_BOTTOM = 13, +}; +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_STATE_TILED_LEFT_SINCE_VERSION 2 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_STATE_TILED_RIGHT_SINCE_VERSION 2 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_STATE_TILED_TOP_SINCE_VERSION 2 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_STATE_TILED_BOTTOM_SINCE_VERSION 2 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_STATE_SUSPENDED_SINCE_VERSION 6 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_STATE_CONSTRAINED_LEFT_SINCE_VERSION 7 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_STATE_CONSTRAINED_RIGHT_SINCE_VERSION 7 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_STATE_CONSTRAINED_TOP_SINCE_VERSION 7 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_STATE_CONSTRAINED_BOTTOM_SINCE_VERSION 7 +#endif /* XDG_TOPLEVEL_STATE_ENUM */ + +#ifndef XDG_TOPLEVEL_STATE_ENUM_IS_VALID +#define XDG_TOPLEVEL_STATE_ENUM_IS_VALID +/** + * @ingroup iface_xdg_toplevel + * Validate a xdg_toplevel state value. + * + * @return true on success, false on error. + * @ref xdg_toplevel_state + */ +static inline bool +xdg_toplevel_state_is_valid(uint32_t value, uint32_t version) { + switch (value) { + case XDG_TOPLEVEL_STATE_MAXIMIZED: + return version >= 1; + case XDG_TOPLEVEL_STATE_FULLSCREEN: + return version >= 1; + case XDG_TOPLEVEL_STATE_RESIZING: + return version >= 1; + case XDG_TOPLEVEL_STATE_ACTIVATED: + return version >= 1; + case XDG_TOPLEVEL_STATE_TILED_LEFT: + return version >= 2; + case XDG_TOPLEVEL_STATE_TILED_RIGHT: + return version >= 2; + case XDG_TOPLEVEL_STATE_TILED_TOP: + return version >= 2; + case XDG_TOPLEVEL_STATE_TILED_BOTTOM: + return version >= 2; + case XDG_TOPLEVEL_STATE_SUSPENDED: + return version >= 6; + case XDG_TOPLEVEL_STATE_CONSTRAINED_LEFT: + return version >= 7; + case XDG_TOPLEVEL_STATE_CONSTRAINED_RIGHT: + return version >= 7; + case XDG_TOPLEVEL_STATE_CONSTRAINED_TOP: + return version >= 7; + case XDG_TOPLEVEL_STATE_CONSTRAINED_BOTTOM: + return version >= 7; + default: + return false; + } +} +#endif /* XDG_TOPLEVEL_STATE_ENUM_IS_VALID */ + +#ifndef XDG_TOPLEVEL_WM_CAPABILITIES_ENUM +#define XDG_TOPLEVEL_WM_CAPABILITIES_ENUM +enum xdg_toplevel_wm_capabilities { + /** + * show_window_menu is available + */ + XDG_TOPLEVEL_WM_CAPABILITIES_WINDOW_MENU = 1, + /** + * set_maximized and unset_maximized are available + */ + XDG_TOPLEVEL_WM_CAPABILITIES_MAXIMIZE = 2, + /** + * set_fullscreen and unset_fullscreen are available + */ + XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN = 3, + /** + * set_minimized is available + */ + XDG_TOPLEVEL_WM_CAPABILITIES_MINIMIZE = 4, +}; +#endif /* XDG_TOPLEVEL_WM_CAPABILITIES_ENUM */ + +#ifndef XDG_TOPLEVEL_WM_CAPABILITIES_ENUM_IS_VALID +#define XDG_TOPLEVEL_WM_CAPABILITIES_ENUM_IS_VALID +/** + * @ingroup iface_xdg_toplevel + * Validate a xdg_toplevel wm_capabilities value. + * + * @return true on success, false on error. + * @ref xdg_toplevel_wm_capabilities + */ +static inline bool +xdg_toplevel_wm_capabilities_is_valid(uint32_t value, uint32_t version) { + switch (value) { + case XDG_TOPLEVEL_WM_CAPABILITIES_WINDOW_MENU: + return version >= 1; + case XDG_TOPLEVEL_WM_CAPABILITIES_MAXIMIZE: + return version >= 1; + case XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN: + return version >= 1; + case XDG_TOPLEVEL_WM_CAPABILITIES_MINIMIZE: + return version >= 1; + default: + return false; + } +} +#endif /* XDG_TOPLEVEL_WM_CAPABILITIES_ENUM_IS_VALID */ + +/** + * @ingroup iface_xdg_toplevel + * @struct xdg_toplevel_interface + */ +struct xdg_toplevel_interface { + /** + * destroy the xdg_toplevel + * + * This request destroys the role surface and unmaps the surface; + * see "Unmapping" behavior in interface section for details. + */ + void (*destroy)(struct wl_client *client, + struct wl_resource *resource); + /** + * set the parent of this surface + * + * Set the "parent" of this surface. This surface should be + * stacked above the parent surface and all other ancestor + * surfaces. + * + * Parent surfaces should be set on dialogs, toolboxes, or other + * "auxiliary" surfaces, so that the parent is raised when the + * dialog is raised. + * + * Setting a null parent for a child surface unsets its parent. + * Setting a null parent for a surface which currently has no + * parent is a no-op. + * + * Only mapped surfaces can have child surfaces. Setting a parent + * which is not mapped is equivalent to setting a null parent. If a + * surface becomes unmapped, its children's parent is set to the + * parent of the now-unmapped surface. If the now-unmapped surface + * has no parent, its children's parent is unset. If the + * now-unmapped surface becomes mapped again, its parent-child + * relationship is not restored. + * + * The parent toplevel must not be one of the child toplevel's + * descendants, and the parent must be different from the child + * toplevel, otherwise the invalid_parent protocol error is raised. + */ + void (*set_parent)(struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *parent); + /** + * set surface title + * + * Set a short title for the surface. + * + * This string may be used to identify the surface in a task bar, + * window list, or other user interface elements provided by the + * compositor. + * + * The string must be encoded in UTF-8. + */ + void (*set_title)(struct wl_client *client, + struct wl_resource *resource, + const char *title); + /** + * set application ID + * + * Set an application identifier for the surface. + * + * The app ID identifies the general class of applications to which + * the surface belongs. The compositor can use this to group + * multiple surfaces together, or to determine how to launch a new + * application. + * + * For D-Bus activatable applications, the app ID is used as the + * D-Bus service name. + * + * The compositor shell will try to group application surfaces + * together by their app ID. As a best practice, it is suggested to + * select app ID's that match the basename of the application's + * .desktop file. For example, "org.freedesktop.FooViewer" where + * the .desktop file is "org.freedesktop.FooViewer.desktop". + * + * Like other properties, a set_app_id request can be sent after + * the xdg_toplevel has been mapped to update the property. + * + * See the desktop-entry specification [0] for more details on + * application identifiers and how they relate to well-known D-Bus + * names and .desktop files. + * + * [0] https://standards.freedesktop.org/desktop-entry-spec/ + */ + void (*set_app_id)(struct wl_client *client, + struct wl_resource *resource, + const char *app_id); + /** + * show the window menu + * + * Clients implementing client-side decorations might want to + * show a context menu when right-clicking on the decorations, + * giving the user a menu that they can use to maximize or minimize + * the window. + * + * This request asks the compositor to pop up such a window menu at + * the given position, relative to the local surface coordinates of + * the parent surface. There are no guarantees as to what menu + * items the window menu contains, or even if a window menu will be + * drawn at all. + * + * This request must be used in response to some sort of user + * action like a button press, key press, or touch down event. + * @param seat the wl_seat of the user event + * @param serial the serial of the user event + * @param x the x position to pop up the window menu at + * @param y the y position to pop up the window menu at + */ + void (*show_window_menu)(struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *seat, + uint32_t serial, + int32_t x, + int32_t y); + /** + * start an interactive move + * + * Start an interactive, user-driven move of the surface. + * + * This request must be used in response to some sort of user + * action like a button press, key press, or touch down event. The + * passed serial is used to determine the type of interactive move + * (touch, pointer, etc). + * + * The server may ignore move requests depending on the state of + * the surface (e.g. fullscreen or maximized), or if the passed + * serial is no longer valid. + * + * If triggered, the surface will lose the focus of the device + * (wl_pointer, wl_touch, etc) used for the move. It is up to the + * compositor to visually indicate that the move is taking place, + * such as updating a pointer cursor, during the move. There is no + * guarantee that the device focus will return when the move is + * completed. + * @param seat the wl_seat of the user event + * @param serial the serial of the user event + */ + void (*move)(struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *seat, + uint32_t serial); + /** + * start an interactive resize + * + * Start a user-driven, interactive resize of the surface. + * + * This request must be used in response to some sort of user + * action like a button press, key press, or touch down event. The + * passed serial is used to determine the type of interactive + * resize (touch, pointer, etc). + * + * The server may ignore resize requests depending on the state of + * the surface (e.g. fullscreen or maximized). + * + * If triggered, the client will receive configure events with the + * "resize" state enum value and the expected sizes. See the + * "resize" enum value for more details about what is required. The + * client must also acknowledge configure events using + * "ack_configure". After the resize is completed, the client will + * receive another "configure" event without the resize state. + * + * If triggered, the surface also will lose the focus of the device + * (wl_pointer, wl_touch, etc) used for the resize. It is up to the + * compositor to visually indicate that the resize is taking place, + * such as updating a pointer cursor, during the resize. There is + * no guarantee that the device focus will return when the resize + * is completed. + * + * The edges parameter specifies how the surface should be resized, + * and is one of the values of the resize_edge enum. Values not + * matching a variant of the enum will cause the + * invalid_resize_edge protocol error. The compositor may use this + * information to update the surface position for example when + * dragging the top left corner. The compositor may also use this + * information to adapt its behavior, e.g. choose an appropriate + * cursor image. + * @param seat the wl_seat of the user event + * @param serial the serial of the user event + * @param edges which edge or corner is being dragged + */ + void (*resize)(struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *seat, + uint32_t serial, + uint32_t edges); + /** + * set the maximum size + * + * Set a maximum size for the window. + * + * The client can specify a maximum size so that the compositor + * does not try to configure the window beyond this size. + * + * The width and height arguments are in window geometry + * coordinates. See xdg_surface.set_window_geometry. + * + * Values set in this way are double-buffered, see + * wl_surface.commit. + * + * The compositor can use this information to allow or disallow + * different states like maximize or fullscreen and draw accurate + * animations. + * + * Similarly, a tiling window manager may use this information to + * place and resize client windows in a more effective way. + * + * The client should not rely on the compositor to obey the maximum + * size. The compositor may decide to ignore the values set by the + * client and request a larger size. + * + * If never set, or a value of zero in the request, means that the + * client has no expected maximum size in the given dimension. As a + * result, a client wishing to reset the maximum size to an + * unspecified state can use zero for width and height in the + * request. + * + * Requesting a maximum size to be smaller than the minimum size of + * a surface is illegal and will result in an invalid_size error. + * + * The width and height must be greater than or equal to zero. + * Using strictly negative values for width or height will result + * in a invalid_size error. + */ + void (*set_max_size)(struct wl_client *client, + struct wl_resource *resource, + int32_t width, + int32_t height); + /** + * set the minimum size + * + * Set a minimum size for the window. + * + * The client can specify a minimum size so that the compositor + * does not try to configure the window below this size. + * + * The width and height arguments are in window geometry + * coordinates. See xdg_surface.set_window_geometry. + * + * Values set in this way are double-buffered, see + * wl_surface.commit. + * + * The compositor can use this information to allow or disallow + * different states like maximize or fullscreen and draw accurate + * animations. + * + * Similarly, a tiling window manager may use this information to + * place and resize client windows in a more effective way. + * + * The client should not rely on the compositor to obey the minimum + * size. The compositor may decide to ignore the values set by the + * client and request a smaller size. + * + * If never set, or a value of zero in the request, means that the + * client has no expected minimum size in the given dimension. As a + * result, a client wishing to reset the minimum size to an + * unspecified state can use zero for width and height in the + * request. + * + * Requesting a minimum size to be larger than the maximum size of + * a surface is illegal and will result in an invalid_size error. + * + * The width and height must be greater than or equal to zero. + * Using strictly negative values for width and height will result + * in a invalid_size error. + */ + void (*set_min_size)(struct wl_client *client, + struct wl_resource *resource, + int32_t width, + int32_t height); + /** + * maximize the window + * + * Maximize the surface. + * + * After requesting that the surface should be maximized, the + * compositor will respond by emitting a configure event. Whether + * this configure actually sets the window maximized is subject to + * compositor policies. The client must then update its content, + * drawing in the configured state. The client must also + * acknowledge the configure when committing the new content (see + * ack_configure). + * + * It is up to the compositor to decide how and where to maximize + * the surface, for example which output and what region of the + * screen should be used. + * + * If the surface was already maximized, the compositor will still + * emit a configure event with the "maximized" state. + * + * If the surface is in a fullscreen state, this request has no + * direct effect. It may alter the state the surface is returned to + * when unmaximized unless overridden by the compositor. + */ + void (*set_maximized)(struct wl_client *client, + struct wl_resource *resource); + /** + * unmaximize the window + * + * Unmaximize the surface. + * + * After requesting that the surface should be unmaximized, the + * compositor will respond by emitting a configure event. Whether + * this actually un-maximizes the window is subject to compositor + * policies. If available and applicable, the compositor will + * include the window geometry dimensions the window had prior to + * being maximized in the configure event. The client must then + * update its content, drawing it in the configured state. The + * client must also acknowledge the configure when committing the + * new content (see ack_configure). + * + * It is up to the compositor to position the surface after it was + * unmaximized; usually the position the surface had before + * maximizing, if applicable. + * + * If the surface was already not maximized, the compositor will + * still emit a configure event without the "maximized" state. + * + * If the surface is in a fullscreen state, this request has no + * direct effect. It may alter the state the surface is returned to + * when unmaximized unless overridden by the compositor. + */ + void (*unset_maximized)(struct wl_client *client, + struct wl_resource *resource); + /** + * set the window as fullscreen on an output + * + * Make the surface fullscreen. + * + * After requesting that the surface should be fullscreened, the + * compositor will respond by emitting a configure event. Whether + * the client is actually put into a fullscreen state is subject to + * compositor policies. The client must also acknowledge the + * configure when committing the new content (see ack_configure). + * + * The output passed by the request indicates the client's + * preference as to which display it should be set fullscreen on. + * If this value is NULL, it's up to the compositor to choose which + * display will be used to map this surface. + * + * If the surface doesn't cover the whole output, the compositor + * will position the surface in the center of the output and + * compensate with with border fill covering the rest of the + * output. The content of the border fill is undefined, but should + * be assumed to be in some way that attempts to blend into the + * surrounding area (e.g. solid black). + * + * If the fullscreened surface is not opaque, the compositor must + * make sure that other screen content not part of the same surface + * tree (made up of subsurfaces, popups or similarly coupled + * surfaces) are not visible below the fullscreened surface. + */ + void (*set_fullscreen)(struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *output); + /** + * unset the window as fullscreen + * + * Make the surface no longer fullscreen. + * + * After requesting that the surface should be unfullscreened, the + * compositor will respond by emitting a configure event. Whether + * this actually removes the fullscreen state of the client is + * subject to compositor policies. + * + * Making a surface unfullscreen sets states for the surface based + * on the following: * the state(s) it may have had before becoming + * fullscreen * any state(s) decided by the compositor * any + * state(s) requested by the client while the surface was + * fullscreen + * + * The compositor may include the previous window geometry + * dimensions in the configure event, if applicable. + * + * The client must also acknowledge the configure when committing + * the new content (see ack_configure). + */ + void (*unset_fullscreen)(struct wl_client *client, + struct wl_resource *resource); + /** + * set the window as minimized + * + * Request that the compositor minimize your surface. There is no + * way to know if the surface is currently minimized, nor is there + * any way to unset minimization on this surface. + * + * If you are looking to throttle redrawing when minimized, please + * instead use the wl_surface.frame event for this, as this will + * also work with live previews on windows in Alt-Tab, Expose or + * similar compositor features. + */ + void (*set_minimized)(struct wl_client *client, + struct wl_resource *resource); +}; + +#define XDG_TOPLEVEL_CONFIGURE 0 +#define XDG_TOPLEVEL_CLOSE 1 +#define XDG_TOPLEVEL_CONFIGURE_BOUNDS 2 +#define XDG_TOPLEVEL_WM_CAPABILITIES 3 + +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_CONFIGURE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_CLOSE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_CONFIGURE_BOUNDS_SINCE_VERSION 4 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION 5 + +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_SET_PARENT_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_SET_TITLE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_SET_APP_ID_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_SHOW_WINDOW_MENU_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_MOVE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_RESIZE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_SET_MAX_SIZE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_SET_MIN_SIZE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_SET_MAXIMIZED_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_UNSET_MAXIMIZED_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_SET_FULLSCREEN_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_UNSET_FULLSCREEN_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_toplevel + */ +#define XDG_TOPLEVEL_SET_MINIMIZED_SINCE_VERSION 1 + +/** + * @ingroup iface_xdg_toplevel + * Sends an configure event to the client owning the resource. + * @param resource_ The client's resource + */ +static inline void +xdg_toplevel_send_configure(struct wl_resource *resource_, int32_t width, int32_t height, struct wl_array *states) +{ + wl_resource_post_event(resource_, XDG_TOPLEVEL_CONFIGURE, width, height, states); +} + +/** + * @ingroup iface_xdg_toplevel + * Sends an close event to the client owning the resource. + * @param resource_ The client's resource + */ +static inline void +xdg_toplevel_send_close(struct wl_resource *resource_) +{ + wl_resource_post_event(resource_, XDG_TOPLEVEL_CLOSE); +} + +/** + * @ingroup iface_xdg_toplevel + * Sends an configure_bounds event to the client owning the resource. + * @param resource_ The client's resource + */ +static inline void +xdg_toplevel_send_configure_bounds(struct wl_resource *resource_, int32_t width, int32_t height) +{ + wl_resource_post_event(resource_, XDG_TOPLEVEL_CONFIGURE_BOUNDS, width, height); +} + +/** + * @ingroup iface_xdg_toplevel + * Sends an wm_capabilities event to the client owning the resource. + * @param resource_ The client's resource + * @param capabilities array of 32-bit capabilities + */ +static inline void +xdg_toplevel_send_wm_capabilities(struct wl_resource *resource_, struct wl_array *capabilities) +{ + wl_resource_post_event(resource_, XDG_TOPLEVEL_WM_CAPABILITIES, capabilities); +} + +#ifndef XDG_POPUP_ERROR_ENUM +#define XDG_POPUP_ERROR_ENUM +enum xdg_popup_error { + /** + * tried to grab after being mapped + */ + XDG_POPUP_ERROR_INVALID_GRAB = 0, +}; +#endif /* XDG_POPUP_ERROR_ENUM */ + +#ifndef XDG_POPUP_ERROR_ENUM_IS_VALID +#define XDG_POPUP_ERROR_ENUM_IS_VALID +/** + * @ingroup iface_xdg_popup + * Validate a xdg_popup error value. + * + * @return true on success, false on error. + * @ref xdg_popup_error + */ +static inline bool +xdg_popup_error_is_valid(uint32_t value, uint32_t version) { + switch (value) { + case XDG_POPUP_ERROR_INVALID_GRAB: + return version >= 1; + default: + return false; + } +} +#endif /* XDG_POPUP_ERROR_ENUM_IS_VALID */ + +/** + * @ingroup iface_xdg_popup + * @struct xdg_popup_interface + */ +struct xdg_popup_interface { + /** + * remove xdg_popup interface + * + * This destroys the popup. Explicitly destroying the xdg_popup + * object will also dismiss the popup, and unmap the surface. + * + * If this xdg_popup is not the "topmost" popup, the + * xdg_wm_base.not_the_topmost_popup protocol error will be sent. + */ + void (*destroy)(struct wl_client *client, + struct wl_resource *resource); + /** + * make the popup take an explicit grab + * + * This request makes the created popup take an explicit grab. An + * explicit grab will be dismissed when the user dismisses the + * popup, or when the client destroys the xdg_popup. This can be + * done by the user clicking outside the surface, using the + * keyboard, or even locking the screen through closing the lid or + * a timeout. + * + * If the compositor denies the grab, the popup will be immediately + * dismissed. + * + * This request must be used in response to some sort of user + * action like a button press, key press, or touch down event. The + * serial number of the event should be passed as 'serial'. + * + * The parent of a grabbing popup must either be an xdg_toplevel + * surface or another xdg_popup with an explicit grab. If the + * parent is another xdg_popup it means that the popups are nested, + * with this popup now being the topmost popup. + * + * Nested popups must be destroyed in the reverse order they were + * created in, e.g. the only popup you are allowed to destroy at + * all times is the topmost one. + * + * When compositors choose to dismiss a popup, they may dismiss + * every nested grabbing popup as well. When a compositor dismisses + * popups, it will follow the same dismissing order as required + * from the client. + * + * If the topmost grabbing popup is destroyed, the grab will be + * returned to the parent of the popup, if that parent previously + * had an explicit grab. + * + * If the parent is a grabbing popup which has already been + * dismissed, this popup will be immediately dismissed. If the + * parent is a popup that did not take an explicit grab, an error + * will be raised. + * + * During a popup grab, the client owning the grab will receive + * pointer and touch events for all their surfaces as normal + * (similar to an "owner-events" grab in X11 parlance), while the + * top most grabbing popup will always have keyboard focus. + * @param seat the wl_seat of the user event + * @param serial the serial of the user event + */ + void (*grab)(struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *seat, + uint32_t serial); + /** + * recalculate the popup's location + * + * Reposition an already-mapped popup. The popup will be placed + * given the details in the passed xdg_positioner object, and a + * xdg_popup.repositioned followed by xdg_popup.configure and + * xdg_surface.configure will be emitted in response. Any + * parameters set by the previous positioner will be discarded. + * + * The passed token will be sent in the corresponding + * xdg_popup.repositioned event. The new popup position will not + * take effect until the corresponding configure event is + * acknowledged by the client. See xdg_popup.repositioned for + * details. The token itself is opaque, and has no other special + * meaning. + * + * If multiple reposition requests are sent, the compositor may + * skip all but the last one. + * + * If the popup is repositioned in response to a configure event + * for its parent, the client should send an + * xdg_positioner.set_parent_configure and possibly an + * xdg_positioner.set_parent_size request to allow the compositor + * to properly constrain the popup. + * + * If the popup is repositioned together with a parent that is + * being resized, but not in response to a configure event, the + * client should send an xdg_positioner.set_parent_size request. + * @param token reposition request token + * @since 3 + */ + void (*reposition)(struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *positioner, + uint32_t token); +}; + +#define XDG_POPUP_CONFIGURE 0 +#define XDG_POPUP_POPUP_DONE 1 +#define XDG_POPUP_REPOSITIONED 2 + +/** + * @ingroup iface_xdg_popup + */ +#define XDG_POPUP_CONFIGURE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_popup + */ +#define XDG_POPUP_POPUP_DONE_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_popup + */ +#define XDG_POPUP_REPOSITIONED_SINCE_VERSION 3 + +/** + * @ingroup iface_xdg_popup + */ +#define XDG_POPUP_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_popup + */ +#define XDG_POPUP_GRAB_SINCE_VERSION 1 +/** + * @ingroup iface_xdg_popup + */ +#define XDG_POPUP_REPOSITION_SINCE_VERSION 3 + +/** + * @ingroup iface_xdg_popup + * Sends an configure event to the client owning the resource. + * @param resource_ The client's resource + * @param x x position relative to parent surface window geometry + * @param y y position relative to parent surface window geometry + * @param width window geometry width + * @param height window geometry height + */ +static inline void +xdg_popup_send_configure(struct wl_resource *resource_, int32_t x, int32_t y, int32_t width, int32_t height) +{ + wl_resource_post_event(resource_, XDG_POPUP_CONFIGURE, x, y, width, height); +} + +/** + * @ingroup iface_xdg_popup + * Sends an popup_done event to the client owning the resource. + * @param resource_ The client's resource + */ +static inline void +xdg_popup_send_popup_done(struct wl_resource *resource_) +{ + wl_resource_post_event(resource_, XDG_POPUP_POPUP_DONE); +} + +/** + * @ingroup iface_xdg_popup + * Sends an repositioned event to the client owning the resource. + * @param resource_ The client's resource + * @param token reposition request token + */ +static inline void +xdg_popup_send_repositioned(struct wl_resource *resource_, uint32_t token) +{ + wl_resource_post_event(resource_, XDG_POPUP_REPOSITIONED, token); +} + +#ifdef __cplusplus +} +#endif + +#endif