From eb0f5c05ef46a4576a9f41fe0572039a4240f5e5 Mon Sep 17 00:00:00 2001 From: Andrew Hamilton Date: Thu, 6 Aug 2026 11:19:35 +1000 Subject: [PATCH] Add tabbed-windows nested compositor with EGL/dmabuf client support --- .gitignore | 1 + Makefile | 19 +- linux-dmabuf-client.h | 1078 ++++++++++++++++ linux-dmabuf-protocol.c | 110 ++ linux-dmabuf-server.h | 918 +++++++++++++ tab-commands | 3 - tabbed-windows.c | 2713 +++++++++++++++++++++++++++++++++++++++ wl-drm-protocol.c | 100 ++ wl-drm-server.h | 434 +++++++ wl-drm.xml | 184 +++ xdg-shell-client.h | 2384 ++++++++++++++++++++++++++++++++++ 11 files changed, 7939 insertions(+), 5 deletions(-) create mode 100644 linux-dmabuf-client.h create mode 100644 linux-dmabuf-protocol.c create mode 100644 linux-dmabuf-server.h delete mode 100644 tab-commands create mode 100644 tabbed-windows.c create mode 100644 wl-drm-protocol.c create mode 100644 wl-drm-server.h create mode 100644 wl-drm.xml create mode 100644 xdg-shell-client.h diff --git a/.gitignore b/.gitignore index 59e8b67..4a40247 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ *.o *.so tabbed-wayland +tabbed-windows core.* diff --git a/Makefile b/Makefile index ca169c0..673c137 100644 --- a/Makefile +++ b/Makefile @@ -5,16 +5,31 @@ SERVER_LIBS = $(shell pkg-config --libs gtk+-3.0 wayland-server xkbcommon) -lm .PHONY: all clean -all: tabbed-wayland +all: tabbed-wayland tabbed-windows xdg-shell-protocol.o: xdg-shell-protocol.c xdg-shell-server.h $(CC) $(CFLAGS) -c -o $@ $< +linux-dmabuf-protocol.o: linux-dmabuf-protocol.c linux-dmabuf-server.h + $(CC) $(CFLAGS) -c -o $@ $< + +wl-drm-protocol.o: wl-drm-protocol.c wl-drm-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) +WINDOWS_CFLAGS = $(CFLAGS) $(shell pkg-config --cflags wayland-server wayland-client wayland-egl xkbcommon egl) -I/usr/include/libdrm +WINDOWS_LIBS = $(shell pkg-config --libs wayland-server wayland-client wayland-egl xkbcommon) -lEGL -lGLESv2 $(SAN) + +tabbed-windows.o: tabbed-windows.c xdg-shell-server.h xdg-shell-client.h linux-dmabuf-server.h wl-drm-server.h + $(CC) $(WINDOWS_CFLAGS) -c -o $@ $< + +tabbed-windows: tabbed-windows.o xdg-shell-protocol.o linux-dmabuf-protocol.o wl-drm-protocol.o + $(CC) -o $@ $^ $(WINDOWS_LIBS) + clean: - rm -f *.o tabbed-wayland + rm -f *.o tabbed-wayland tabbed-windows diff --git a/linux-dmabuf-client.h b/linux-dmabuf-client.h new file mode 100644 index 0000000..0a99e13 --- /dev/null +++ b/linux-dmabuf-client.h @@ -0,0 +1,1078 @@ +/* Generated by wayland-scanner 1.24.0 */ + +#ifndef LINUX_DMABUF_V1_CLIENT_PROTOCOL_H +#define LINUX_DMABUF_V1_CLIENT_PROTOCOL_H + +#include +#include +#include "wayland-client.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @page page_linux_dmabuf_v1 The linux_dmabuf_v1 protocol + * @section page_ifaces_linux_dmabuf_v1 Interfaces + * - @subpage page_iface_zwp_linux_dmabuf_v1 - factory for creating dmabuf-based wl_buffers + * - @subpage page_iface_zwp_linux_buffer_params_v1 - parameters for creating a dmabuf-based wl_buffer + * - @subpage page_iface_zwp_linux_dmabuf_feedback_v1 - dmabuf feedback + * @section page_copyright_linux_dmabuf_v1 Copyright + *
+ *
+ * Copyright © 2014, 2015 Collabora, Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ */ +struct wl_buffer; +struct wl_surface; +struct zwp_linux_buffer_params_v1; +struct zwp_linux_dmabuf_feedback_v1; +struct zwp_linux_dmabuf_v1; + +#ifndef ZWP_LINUX_DMABUF_V1_INTERFACE +#define ZWP_LINUX_DMABUF_V1_INTERFACE +/** + * @page page_iface_zwp_linux_dmabuf_v1 zwp_linux_dmabuf_v1 + * @section page_iface_zwp_linux_dmabuf_v1_desc Description + * + * This interface offers ways to create generic dmabuf-based wl_buffers. + * + * For more information about dmabuf, see: + * https://www.kernel.org/doc/html/next/userspace-api/dma-buf-alloc-exchange.html + * + * Clients can use the get_surface_feedback request to get dmabuf feedback + * for a particular surface. If the client wants to retrieve feedback not + * tied to a surface, they can use the get_default_feedback request. + * + * The following are required from clients: + * + * - Clients must ensure that either all data in the dma-buf is + * coherent for all subsequent read access or that coherency is + * correctly handled by the underlying kernel-side dma-buf + * implementation. + * + * - Don't make any more attachments after sending the buffer to the + * compositor. Making more attachments later increases the risk of + * the compositor not being able to use (re-import) an existing + * dmabuf-based wl_buffer. + * + * The underlying graphics stack must ensure the following: + * + * - The dmabuf file descriptors relayed to the server will stay valid + * for the whole lifetime of the wl_buffer. This means the server may + * at any time use those fds to import the dmabuf into any kernel + * sub-system that might accept it. + * + * However, when the underlying graphics stack fails to deliver the + * promise, because of e.g. a device hot-unplug which raises internal + * errors, after the wl_buffer has been successfully created the + * compositor must not raise protocol errors to the client when dmabuf + * import later fails. + * + * To create a wl_buffer from one or more dmabufs, a client creates a + * zwp_linux_dmabuf_params_v1 object with a zwp_linux_dmabuf_v1.create_params + * request. All planes required by the intended format are added with + * the 'add' request. Finally, a 'create' or 'create_immed' request is + * issued, which has the following outcome depending on the import success. + * + * The 'create' request, + * - on success, triggers a 'created' event which provides the final + * wl_buffer to the client. + * - on failure, triggers a 'failed' event to convey that the server + * cannot use the dmabufs received from the client. + * + * For the 'create_immed' request, + * - on success, the server immediately imports the added dmabufs to + * create a wl_buffer. No event is sent from the server in this case. + * - on failure, the server can choose to either: + * - terminate the client by raising a fatal error. + * - mark the wl_buffer as failed, and send a 'failed' event to the + * client. If the client uses a failed wl_buffer as an argument to any + * request, the behaviour is compositor implementation-defined. + * + * For all DRM formats and unless specified in another protocol extension, + * pre-multiplied alpha is used for pixel values. + * + * Unless specified otherwise in another protocol extension, implicit + * synchronization is used. In other words, compositors and clients must + * wait and signal fences implicitly passed via the DMA-BUF's reservation + * mechanism. + * @section page_iface_zwp_linux_dmabuf_v1_api API + * See @ref iface_zwp_linux_dmabuf_v1. + */ +/** + * @defgroup iface_zwp_linux_dmabuf_v1 The zwp_linux_dmabuf_v1 interface + * + * This interface offers ways to create generic dmabuf-based wl_buffers. + * + * For more information about dmabuf, see: + * https://www.kernel.org/doc/html/next/userspace-api/dma-buf-alloc-exchange.html + * + * Clients can use the get_surface_feedback request to get dmabuf feedback + * for a particular surface. If the client wants to retrieve feedback not + * tied to a surface, they can use the get_default_feedback request. + * + * The following are required from clients: + * + * - Clients must ensure that either all data in the dma-buf is + * coherent for all subsequent read access or that coherency is + * correctly handled by the underlying kernel-side dma-buf + * implementation. + * + * - Don't make any more attachments after sending the buffer to the + * compositor. Making more attachments later increases the risk of + * the compositor not being able to use (re-import) an existing + * dmabuf-based wl_buffer. + * + * The underlying graphics stack must ensure the following: + * + * - The dmabuf file descriptors relayed to the server will stay valid + * for the whole lifetime of the wl_buffer. This means the server may + * at any time use those fds to import the dmabuf into any kernel + * sub-system that might accept it. + * + * However, when the underlying graphics stack fails to deliver the + * promise, because of e.g. a device hot-unplug which raises internal + * errors, after the wl_buffer has been successfully created the + * compositor must not raise protocol errors to the client when dmabuf + * import later fails. + * + * To create a wl_buffer from one or more dmabufs, a client creates a + * zwp_linux_dmabuf_params_v1 object with a zwp_linux_dmabuf_v1.create_params + * request. All planes required by the intended format are added with + * the 'add' request. Finally, a 'create' or 'create_immed' request is + * issued, which has the following outcome depending on the import success. + * + * The 'create' request, + * - on success, triggers a 'created' event which provides the final + * wl_buffer to the client. + * - on failure, triggers a 'failed' event to convey that the server + * cannot use the dmabufs received from the client. + * + * For the 'create_immed' request, + * - on success, the server immediately imports the added dmabufs to + * create a wl_buffer. No event is sent from the server in this case. + * - on failure, the server can choose to either: + * - terminate the client by raising a fatal error. + * - mark the wl_buffer as failed, and send a 'failed' event to the + * client. If the client uses a failed wl_buffer as an argument to any + * request, the behaviour is compositor implementation-defined. + * + * For all DRM formats and unless specified in another protocol extension, + * pre-multiplied alpha is used for pixel values. + * + * Unless specified otherwise in another protocol extension, implicit + * synchronization is used. In other words, compositors and clients must + * wait and signal fences implicitly passed via the DMA-BUF's reservation + * mechanism. + */ +extern const struct wl_interface zwp_linux_dmabuf_v1_interface; +#endif +#ifndef ZWP_LINUX_BUFFER_PARAMS_V1_INTERFACE +#define ZWP_LINUX_BUFFER_PARAMS_V1_INTERFACE +/** + * @page page_iface_zwp_linux_buffer_params_v1 zwp_linux_buffer_params_v1 + * @section page_iface_zwp_linux_buffer_params_v1_desc Description + * + * This temporary object is a collection of dmabufs and other + * parameters that together form a single logical buffer. The temporary + * object may eventually create one wl_buffer unless cancelled by + * destroying it before requesting 'create'. + * + * Single-planar formats only require one dmabuf, however + * multi-planar formats may require more than one dmabuf. For all + * formats, an 'add' request must be called once per plane (even if the + * underlying dmabuf fd is identical). + * + * You must use consecutive plane indices ('plane_idx' argument for 'add') + * from zero to the number of planes used by the drm_fourcc format code. + * All planes required by the format must be given exactly once, but can + * be given in any order. Each plane index can only be set once; subsequent + * calls with a plane index which has already been set will result in a + * plane_set error being generated. + * @section page_iface_zwp_linux_buffer_params_v1_api API + * See @ref iface_zwp_linux_buffer_params_v1. + */ +/** + * @defgroup iface_zwp_linux_buffer_params_v1 The zwp_linux_buffer_params_v1 interface + * + * This temporary object is a collection of dmabufs and other + * parameters that together form a single logical buffer. The temporary + * object may eventually create one wl_buffer unless cancelled by + * destroying it before requesting 'create'. + * + * Single-planar formats only require one dmabuf, however + * multi-planar formats may require more than one dmabuf. For all + * formats, an 'add' request must be called once per plane (even if the + * underlying dmabuf fd is identical). + * + * You must use consecutive plane indices ('plane_idx' argument for 'add') + * from zero to the number of planes used by the drm_fourcc format code. + * All planes required by the format must be given exactly once, but can + * be given in any order. Each plane index can only be set once; subsequent + * calls with a plane index which has already been set will result in a + * plane_set error being generated. + */ +extern const struct wl_interface zwp_linux_buffer_params_v1_interface; +#endif +#ifndef ZWP_LINUX_DMABUF_FEEDBACK_V1_INTERFACE +#define ZWP_LINUX_DMABUF_FEEDBACK_V1_INTERFACE +/** + * @page page_iface_zwp_linux_dmabuf_feedback_v1 zwp_linux_dmabuf_feedback_v1 + * @section page_iface_zwp_linux_dmabuf_feedback_v1_desc Description + * + * This object advertises dmabuf parameters feedback. This includes the + * preferred devices and the supported formats/modifiers. + * + * The parameters are sent once when this object is created and whenever they + * change. The done event is always sent once after all parameters have been + * sent. When a single parameter changes, all parameters are re-sent by the + * compositor. + * + * Compositors can re-send the parameters when the current client buffer + * allocations are sub-optimal. Compositors should not re-send the + * parameters if re-allocating the buffers would not result in a more optimal + * configuration. In particular, compositors should avoid sending the exact + * same parameters multiple times in a row. + * + * The tranche_target_device and tranche_formats events are grouped by + * tranches of preference. For each tranche, a tranche_target_device, one + * tranche_flags and one or more tranche_formats events are sent, followed + * by a tranche_done event finishing the list. The tranches are sent in + * descending order of preference. All formats and modifiers in the same + * tranche have the same preference. + * + * To send parameters, the compositor sends one main_device event, tranches + * (each consisting of one tranche_target_device event, one tranche_flags + * event, tranche_formats events and then a tranche_done event), then one + * done event. + * @section page_iface_zwp_linux_dmabuf_feedback_v1_api API + * See @ref iface_zwp_linux_dmabuf_feedback_v1. + */ +/** + * @defgroup iface_zwp_linux_dmabuf_feedback_v1 The zwp_linux_dmabuf_feedback_v1 interface + * + * This object advertises dmabuf parameters feedback. This includes the + * preferred devices and the supported formats/modifiers. + * + * The parameters are sent once when this object is created and whenever they + * change. The done event is always sent once after all parameters have been + * sent. When a single parameter changes, all parameters are re-sent by the + * compositor. + * + * Compositors can re-send the parameters when the current client buffer + * allocations are sub-optimal. Compositors should not re-send the + * parameters if re-allocating the buffers would not result in a more optimal + * configuration. In particular, compositors should avoid sending the exact + * same parameters multiple times in a row. + * + * The tranche_target_device and tranche_formats events are grouped by + * tranches of preference. For each tranche, a tranche_target_device, one + * tranche_flags and one or more tranche_formats events are sent, followed + * by a tranche_done event finishing the list. The tranches are sent in + * descending order of preference. All formats and modifiers in the same + * tranche have the same preference. + * + * To send parameters, the compositor sends one main_device event, tranches + * (each consisting of one tranche_target_device event, one tranche_flags + * event, tranche_formats events and then a tranche_done event), then one + * done event. + */ +extern const struct wl_interface zwp_linux_dmabuf_feedback_v1_interface; +#endif + +/** + * @ingroup iface_zwp_linux_dmabuf_v1 + * @struct zwp_linux_dmabuf_v1_listener + */ +struct zwp_linux_dmabuf_v1_listener { + /** + * supported buffer format + * + * This event advertises one buffer format that the server + * supports. All the supported formats are advertised once when the + * client binds to this interface. A roundtrip after binding + * guarantees that the client has received all supported formats. + * + * For the definition of the format codes, see the + * zwp_linux_buffer_params_v1::create request. + * + * Starting version 4, the format event is deprecated and must not + * be sent by compositors. Instead, use get_default_feedback or + * get_surface_feedback. + * @param format DRM_FORMAT code + * @deprecated Deprecated since version 4 + */ + void (*format)(void *data, + struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf_v1, + uint32_t format); + /** + * supported buffer format modifier + * + * This event advertises the formats that the server supports, + * along with the modifiers supported for each format. All the + * supported modifiers for all the supported formats are advertised + * once when the client binds to this interface. A roundtrip after + * binding guarantees that the client has received all supported + * format-modifier pairs. + * + * For legacy support, DRM_FORMAT_MOD_INVALID (that is, modifier_hi + * == 0x00ffffff and modifier_lo == 0xffffffff) is allowed in this + * event. It indicates that the server can support the format with + * an implicit modifier. When a plane has DRM_FORMAT_MOD_INVALID as + * its modifier, it is as if no explicit modifier is specified. The + * effective modifier will be derived from the dmabuf. + * + * A compositor that sends valid modifiers and + * DRM_FORMAT_MOD_INVALID for a given format supports both explicit + * modifiers and implicit modifiers. + * + * For the definition of the format and modifier codes, see the + * zwp_linux_buffer_params_v1::create and + * zwp_linux_buffer_params_v1::add requests. + * + * Starting version 4, the modifier event is deprecated and must + * not be sent by compositors. Instead, use get_default_feedback or + * get_surface_feedback. + * @param format DRM_FORMAT code + * @param modifier_hi high 32 bits of layout modifier + * @param modifier_lo low 32 bits of layout modifier + * @since 3 + * @deprecated Deprecated since version 4 + */ + void (*modifier)(void *data, + struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf_v1, + uint32_t format, + uint32_t modifier_hi, + uint32_t modifier_lo); +}; + +/** + * @ingroup iface_zwp_linux_dmabuf_v1 + */ +static inline int +zwp_linux_dmabuf_v1_add_listener(struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf_v1, + const struct zwp_linux_dmabuf_v1_listener *listener, void *data) +{ + return wl_proxy_add_listener((struct wl_proxy *) zwp_linux_dmabuf_v1, + (void (**)(void)) listener, data); +} + +#define ZWP_LINUX_DMABUF_V1_DESTROY 0 +#define ZWP_LINUX_DMABUF_V1_CREATE_PARAMS 1 +#define ZWP_LINUX_DMABUF_V1_GET_DEFAULT_FEEDBACK 2 +#define ZWP_LINUX_DMABUF_V1_GET_SURFACE_FEEDBACK 3 + +/** + * @ingroup iface_zwp_linux_dmabuf_v1 + */ +#define ZWP_LINUX_DMABUF_V1_FORMAT_SINCE_VERSION 1 +/** + * @ingroup iface_zwp_linux_dmabuf_v1 + */ +#define ZWP_LINUX_DMABUF_V1_MODIFIER_SINCE_VERSION 3 + +/** + * @ingroup iface_zwp_linux_dmabuf_v1 + */ +#define ZWP_LINUX_DMABUF_V1_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_zwp_linux_dmabuf_v1 + */ +#define ZWP_LINUX_DMABUF_V1_CREATE_PARAMS_SINCE_VERSION 1 +/** + * @ingroup iface_zwp_linux_dmabuf_v1 + */ +#define ZWP_LINUX_DMABUF_V1_GET_DEFAULT_FEEDBACK_SINCE_VERSION 4 +/** + * @ingroup iface_zwp_linux_dmabuf_v1 + */ +#define ZWP_LINUX_DMABUF_V1_GET_SURFACE_FEEDBACK_SINCE_VERSION 4 + +/** @ingroup iface_zwp_linux_dmabuf_v1 */ +static inline void +zwp_linux_dmabuf_v1_set_user_data(struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf_v1, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) zwp_linux_dmabuf_v1, user_data); +} + +/** @ingroup iface_zwp_linux_dmabuf_v1 */ +static inline void * +zwp_linux_dmabuf_v1_get_user_data(struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf_v1) +{ + return wl_proxy_get_user_data((struct wl_proxy *) zwp_linux_dmabuf_v1); +} + +static inline uint32_t +zwp_linux_dmabuf_v1_get_version(struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf_v1) +{ + return wl_proxy_get_version((struct wl_proxy *) zwp_linux_dmabuf_v1); +} + +/** + * @ingroup iface_zwp_linux_dmabuf_v1 + * + * Objects created through this interface, especially wl_buffers, will + * remain valid. + */ +static inline void +zwp_linux_dmabuf_v1_destroy(struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf_v1) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwp_linux_dmabuf_v1, + ZWP_LINUX_DMABUF_V1_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) zwp_linux_dmabuf_v1), WL_MARSHAL_FLAG_DESTROY); +} + +/** + * @ingroup iface_zwp_linux_dmabuf_v1 + * + * This temporary object is used to collect multiple dmabuf handles into + * a single batch to create a wl_buffer. It can only be used once and + * should be destroyed after a 'created' or 'failed' event has been + * received. + */ +static inline struct zwp_linux_buffer_params_v1 * +zwp_linux_dmabuf_v1_create_params(struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf_v1) +{ + struct wl_proxy *params_id; + + params_id = wl_proxy_marshal_flags((struct wl_proxy *) zwp_linux_dmabuf_v1, + ZWP_LINUX_DMABUF_V1_CREATE_PARAMS, &zwp_linux_buffer_params_v1_interface, wl_proxy_get_version((struct wl_proxy *) zwp_linux_dmabuf_v1), 0, NULL); + + return (struct zwp_linux_buffer_params_v1 *) params_id; +} + +/** + * @ingroup iface_zwp_linux_dmabuf_v1 + * + * This request creates a new wp_linux_dmabuf_feedback object not bound + * to a particular surface. This object will deliver feedback about dmabuf + * parameters to use if the client doesn't support per-surface feedback + * (see get_surface_feedback). + */ +static inline struct zwp_linux_dmabuf_feedback_v1 * +zwp_linux_dmabuf_v1_get_default_feedback(struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf_v1) +{ + struct wl_proxy *id; + + id = wl_proxy_marshal_flags((struct wl_proxy *) zwp_linux_dmabuf_v1, + ZWP_LINUX_DMABUF_V1_GET_DEFAULT_FEEDBACK, &zwp_linux_dmabuf_feedback_v1_interface, wl_proxy_get_version((struct wl_proxy *) zwp_linux_dmabuf_v1), 0, NULL); + + return (struct zwp_linux_dmabuf_feedback_v1 *) id; +} + +/** + * @ingroup iface_zwp_linux_dmabuf_v1 + * + * This request creates a new wp_linux_dmabuf_feedback object for the + * specified wl_surface. This object will deliver feedback about dmabuf + * parameters to use for buffers attached to this surface. + * + * If the surface is destroyed before the wp_linux_dmabuf_feedback object, + * the feedback object becomes inert. + */ +static inline struct zwp_linux_dmabuf_feedback_v1 * +zwp_linux_dmabuf_v1_get_surface_feedback(struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf_v1, struct wl_surface *surface) +{ + struct wl_proxy *id; + + id = wl_proxy_marshal_flags((struct wl_proxy *) zwp_linux_dmabuf_v1, + ZWP_LINUX_DMABUF_V1_GET_SURFACE_FEEDBACK, &zwp_linux_dmabuf_feedback_v1_interface, wl_proxy_get_version((struct wl_proxy *) zwp_linux_dmabuf_v1), 0, NULL, surface); + + return (struct zwp_linux_dmabuf_feedback_v1 *) id; +} + +#ifndef ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ENUM +#define ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ENUM +enum zwp_linux_buffer_params_v1_error { + /** + * the dmabuf_batch object has already been used to create a wl_buffer + */ + ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ALREADY_USED = 0, + /** + * plane index out of bounds + */ + ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_PLANE_IDX = 1, + /** + * the plane index was already set + */ + ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_PLANE_SET = 2, + /** + * missing or too many planes to create a buffer + */ + ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE = 3, + /** + * format not supported + */ + ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_FORMAT = 4, + /** + * invalid width or height + */ + ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_DIMENSIONS = 5, + /** + * offset + stride * height goes out of dmabuf bounds + */ + ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS = 6, + /** + * invalid wl_buffer resulted from importing dmabufs via the create_immed request on given buffer_params + */ + ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_WL_BUFFER = 7, +}; +#endif /* ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ENUM */ + +#ifndef ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_ENUM +#define ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_ENUM +enum zwp_linux_buffer_params_v1_flags { + /** + * contents are y-inverted + */ + ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_Y_INVERT = 1, + /** + * content is interlaced + */ + ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_INTERLACED = 2, + /** + * bottom field first + */ + ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_BOTTOM_FIRST = 4, +}; +#endif /* ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_ENUM */ + +/** + * @ingroup iface_zwp_linux_buffer_params_v1 + * @struct zwp_linux_buffer_params_v1_listener + */ +struct zwp_linux_buffer_params_v1_listener { + /** + * buffer creation succeeded + * + * This event indicates that the attempted buffer creation was + * successful. It provides the new wl_buffer referencing the + * dmabuf(s). + * + * Upon receiving this event, the client should destroy the + * zwp_linux_buffer_params_v1 object. + * @param buffer the newly created wl_buffer + */ + void (*created)(void *data, + struct zwp_linux_buffer_params_v1 *zwp_linux_buffer_params_v1, + struct wl_buffer *buffer); + /** + * buffer creation failed + * + * This event indicates that the attempted buffer creation has + * failed. It usually means that one of the dmabuf constraints has + * not been fulfilled. + * + * Upon receiving this event, the client should destroy the + * zwp_linux_buffer_params_v1 object. + */ + void (*failed)(void *data, + struct zwp_linux_buffer_params_v1 *zwp_linux_buffer_params_v1); +}; + +/** + * @ingroup iface_zwp_linux_buffer_params_v1 + */ +static inline int +zwp_linux_buffer_params_v1_add_listener(struct zwp_linux_buffer_params_v1 *zwp_linux_buffer_params_v1, + const struct zwp_linux_buffer_params_v1_listener *listener, void *data) +{ + return wl_proxy_add_listener((struct wl_proxy *) zwp_linux_buffer_params_v1, + (void (**)(void)) listener, data); +} + +#define ZWP_LINUX_BUFFER_PARAMS_V1_DESTROY 0 +#define ZWP_LINUX_BUFFER_PARAMS_V1_ADD 1 +#define ZWP_LINUX_BUFFER_PARAMS_V1_CREATE 2 +#define ZWP_LINUX_BUFFER_PARAMS_V1_CREATE_IMMED 3 + +/** + * @ingroup iface_zwp_linux_buffer_params_v1 + */ +#define ZWP_LINUX_BUFFER_PARAMS_V1_CREATED_SINCE_VERSION 1 +/** + * @ingroup iface_zwp_linux_buffer_params_v1 + */ +#define ZWP_LINUX_BUFFER_PARAMS_V1_FAILED_SINCE_VERSION 1 + +/** + * @ingroup iface_zwp_linux_buffer_params_v1 + */ +#define ZWP_LINUX_BUFFER_PARAMS_V1_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_zwp_linux_buffer_params_v1 + */ +#define ZWP_LINUX_BUFFER_PARAMS_V1_ADD_SINCE_VERSION 1 +/** + * @ingroup iface_zwp_linux_buffer_params_v1 + */ +#define ZWP_LINUX_BUFFER_PARAMS_V1_CREATE_SINCE_VERSION 1 +/** + * @ingroup iface_zwp_linux_buffer_params_v1 + */ +#define ZWP_LINUX_BUFFER_PARAMS_V1_CREATE_IMMED_SINCE_VERSION 2 + +/** @ingroup iface_zwp_linux_buffer_params_v1 */ +static inline void +zwp_linux_buffer_params_v1_set_user_data(struct zwp_linux_buffer_params_v1 *zwp_linux_buffer_params_v1, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) zwp_linux_buffer_params_v1, user_data); +} + +/** @ingroup iface_zwp_linux_buffer_params_v1 */ +static inline void * +zwp_linux_buffer_params_v1_get_user_data(struct zwp_linux_buffer_params_v1 *zwp_linux_buffer_params_v1) +{ + return wl_proxy_get_user_data((struct wl_proxy *) zwp_linux_buffer_params_v1); +} + +static inline uint32_t +zwp_linux_buffer_params_v1_get_version(struct zwp_linux_buffer_params_v1 *zwp_linux_buffer_params_v1) +{ + return wl_proxy_get_version((struct wl_proxy *) zwp_linux_buffer_params_v1); +} + +/** + * @ingroup iface_zwp_linux_buffer_params_v1 + * + * Cleans up the temporary data sent to the server for dmabuf-based + * wl_buffer creation. + */ +static inline void +zwp_linux_buffer_params_v1_destroy(struct zwp_linux_buffer_params_v1 *zwp_linux_buffer_params_v1) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwp_linux_buffer_params_v1, + ZWP_LINUX_BUFFER_PARAMS_V1_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) zwp_linux_buffer_params_v1), WL_MARSHAL_FLAG_DESTROY); +} + +/** + * @ingroup iface_zwp_linux_buffer_params_v1 + * + * This request adds one dmabuf to the set in this + * zwp_linux_buffer_params_v1. + * + * The 64-bit unsigned value combined from modifier_hi and modifier_lo + * is the dmabuf layout modifier. DRM AddFB2 ioctl calls this the + * fb modifier, which is defined in drm_mode.h of Linux UAPI. + * This is an opaque token. Drivers use this token to express tiling, + * compression, etc. driver-specific modifications to the base format + * defined by the DRM fourcc code. + * + * Starting from version 4, the invalid_format protocol error is sent if + * the format + modifier pair was not advertised as supported. + * + * Starting from version 5, the invalid_format protocol error is sent if + * all planes don't use the same modifier. + * + * This request raises the PLANE_IDX error if plane_idx is too large. + * The error PLANE_SET is raised if attempting to set a plane that + * was already set. + */ +static inline void +zwp_linux_buffer_params_v1_add(struct zwp_linux_buffer_params_v1 *zwp_linux_buffer_params_v1, int32_t fd, uint32_t plane_idx, uint32_t offset, uint32_t stride, uint32_t modifier_hi, uint32_t modifier_lo) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwp_linux_buffer_params_v1, + ZWP_LINUX_BUFFER_PARAMS_V1_ADD, NULL, wl_proxy_get_version((struct wl_proxy *) zwp_linux_buffer_params_v1), 0, fd, plane_idx, offset, stride, modifier_hi, modifier_lo); +} + +/** + * @ingroup iface_zwp_linux_buffer_params_v1 + * + * This asks for creation of a wl_buffer from the added dmabuf + * buffers. The wl_buffer is not created immediately but returned via + * the 'created' event if the dmabuf sharing succeeds. The sharing + * may fail at runtime for reasons a client cannot predict, in + * which case the 'failed' event is triggered. + * + * The 'format' argument is a DRM_FORMAT code, as defined by the + * libdrm's drm_fourcc.h. The Linux kernel's DRM sub-system is the + * authoritative source on how the format codes should work. + * + * The 'flags' is a bitfield of the flags defined in enum "flags". + * 'y_invert' means the that the image needs to be y-flipped. + * + * Flag 'interlaced' means that the frame in the buffer is not + * progressive as usual, but interlaced. An interlaced buffer as + * supported here must always contain both top and bottom fields. + * The top field always begins on the first pixel row. The temporal + * ordering between the two fields is top field first, unless + * 'bottom_first' is specified. It is undefined whether 'bottom_first' + * is ignored if 'interlaced' is not set. + * + * This protocol does not convey any information about field rate, + * duration, or timing, other than the relative ordering between the + * two fields in one buffer. A compositor may have to estimate the + * intended field rate from the incoming buffer rate. It is undefined + * whether the time of receiving wl_surface.commit with a new buffer + * attached, applying the wl_surface state, wl_surface.frame callback + * trigger, presentation, or any other point in the compositor cycle + * is used to measure the frame or field times. There is no support + * for detecting missed or late frames/fields/buffers either, and + * there is no support whatsoever for cooperating with interlaced + * compositor output. + * + * The composited image quality resulting from the use of interlaced + * buffers is explicitly undefined. A compositor may use elaborate + * hardware features or software to deinterlace and create progressive + * output frames from a sequence of interlaced input buffers, or it + * may produce substandard image quality. However, compositors that + * cannot guarantee reasonable image quality in all cases are recommended + * to just reject all interlaced buffers. + * + * Any argument errors, including non-positive width or height, + * mismatch between the number of planes and the format, bad + * format, bad offset or stride, may be indicated by fatal protocol + * errors: INCOMPLETE, INVALID_FORMAT, INVALID_DIMENSIONS, + * OUT_OF_BOUNDS. + * + * Dmabuf import errors in the server that are not obvious client + * bugs are returned via the 'failed' event as non-fatal. This + * allows attempting dmabuf sharing and falling back in the client + * if it fails. + * + * This request can be sent only once in the object's lifetime, after + * which the only legal request is destroy. This object should be + * destroyed after issuing a 'create' request. Attempting to use this + * object after issuing 'create' raises ALREADY_USED protocol error. + * + * It is not mandatory to issue 'create'. If a client wants to + * cancel the buffer creation, it can just destroy this object. + */ +static inline void +zwp_linux_buffer_params_v1_create(struct zwp_linux_buffer_params_v1 *zwp_linux_buffer_params_v1, int32_t width, int32_t height, uint32_t format, uint32_t flags) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwp_linux_buffer_params_v1, + ZWP_LINUX_BUFFER_PARAMS_V1_CREATE, NULL, wl_proxy_get_version((struct wl_proxy *) zwp_linux_buffer_params_v1), 0, width, height, format, flags); +} + +/** + * @ingroup iface_zwp_linux_buffer_params_v1 + * + * This asks for immediate creation of a wl_buffer by importing the + * added dmabufs. + * + * In case of import success, no event is sent from the server, and the + * wl_buffer is ready to be used by the client. + * + * Upon import failure, either of the following may happen, as seen fit + * by the implementation: + * - the client is terminated with one of the following fatal protocol + * errors: + * - INCOMPLETE, INVALID_FORMAT, INVALID_DIMENSIONS, OUT_OF_BOUNDS, + * in case of argument errors such as mismatch between the number + * of planes and the format, bad format, non-positive width or + * height, or bad offset or stride. + * - INVALID_WL_BUFFER, in case the cause for failure is unknown or + * platform specific. + * - the server creates an invalid wl_buffer, marks it as failed and + * sends a 'failed' event to the client. The result of using this + * invalid wl_buffer as an argument in any request by the client is + * defined by the compositor implementation. + * + * This takes the same arguments as a 'create' request, and obeys the + * same restrictions. + */ +static inline struct wl_buffer * +zwp_linux_buffer_params_v1_create_immed(struct zwp_linux_buffer_params_v1 *zwp_linux_buffer_params_v1, int32_t width, int32_t height, uint32_t format, uint32_t flags) +{ + struct wl_proxy *buffer_id; + + buffer_id = wl_proxy_marshal_flags((struct wl_proxy *) zwp_linux_buffer_params_v1, + ZWP_LINUX_BUFFER_PARAMS_V1_CREATE_IMMED, &wl_buffer_interface, wl_proxy_get_version((struct wl_proxy *) zwp_linux_buffer_params_v1), 0, NULL, width, height, format, flags); + + return (struct wl_buffer *) buffer_id; +} + +#ifndef ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_FLAGS_ENUM +#define ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_FLAGS_ENUM +enum zwp_linux_dmabuf_feedback_v1_tranche_flags { + /** + * direct scan-out tranche + */ + ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_FLAGS_SCANOUT = 1, +}; +#endif /* ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_FLAGS_ENUM */ + +/** + * @ingroup iface_zwp_linux_dmabuf_feedback_v1 + * @struct zwp_linux_dmabuf_feedback_v1_listener + */ +struct zwp_linux_dmabuf_feedback_v1_listener { + /** + * all feedback has been sent + * + * This event is sent after all parameters of a + * wp_linux_dmabuf_feedback object have been sent. + * + * This allows changes to the wp_linux_dmabuf_feedback parameters + * to be seen as atomic, even if they happen via multiple events. + */ + void (*done)(void *data, + struct zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1); + /** + * format and modifier table + * + * This event provides a file descriptor which can be + * memory-mapped to access the format and modifier table. + * + * The table contains a tightly packed array of consecutive format + * + modifier pairs. Each pair is 16 bytes wide. It contains a + * format as a 32-bit unsigned integer, followed by 4 bytes of + * unused padding, and a modifier as a 64-bit unsigned integer. The + * native endianness is used. + * + * The client must map the file descriptor in read-only private + * mode. + * + * Compositors are not allowed to mutate the table file contents + * once this event has been sent. Instead, compositors must create + * a new, separate table file and re-send feedback parameters. + * Compositors are allowed to store duplicate format + modifier + * pairs in the table. + * @param fd table file descriptor + * @param size table size, in bytes + */ + void (*format_table)(void *data, + struct zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1, + int32_t fd, + uint32_t size); + /** + * preferred main device + * + * This event advertises the main device that the server prefers + * to use when direct scan-out to the target device isn't possible. + * The advertised main device may be different for each + * wp_linux_dmabuf_feedback object, and may change over time. + * + * There is exactly one main device. The compositor must send at + * least one preference tranche with tranche_target_device equal to + * main_device. + * + * Clients need to create buffers that the main device can import + * and read from, otherwise creating the dmabuf wl_buffer will fail + * (see the wp_linux_buffer_params.create and create_immed requests + * for details). The main device will also likely be kept active by + * the compositor, so clients can use it instead of waking up + * another device for power savings. + * + * In general the device is a DRM node. The DRM node type (primary + * vs. render) is unspecified. Clients must not rely on the + * compositor sending a particular node type. Clients cannot check + * two devices for equality by comparing the dev_t value. + * + * If explicit modifiers are not supported and the client performs + * buffer allocations on a different device than the main device, + * then the client must force the buffer to have a linear layout. + * @param device device dev_t value + */ + void (*main_device)(void *data, + struct zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1, + struct wl_array *device); + /** + * a preference tranche has been sent + * + * This event splits tranche_target_device and tranche_formats + * events in preference tranches. It is sent after a set of + * tranche_target_device and tranche_formats events; it represents + * the end of a tranche. The next tranche will have a lower + * preference. + */ + void (*tranche_done)(void *data, + struct zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1); + /** + * target device + * + * This event advertises the target device that the server + * prefers to use for a buffer created given this tranche. The + * advertised target device may be different for each preference + * tranche, and may change over time. + * + * There is exactly one target device per tranche. + * + * The target device may be a scan-out device, for example if the + * compositor prefers to directly scan-out a buffer created given + * this tranche. The target device may be a rendering device, for + * example if the compositor prefers to texture from said buffer. + * + * The client can use this hint to allocate the buffer in a way + * that makes it accessible from the target device, ideally + * directly. The buffer must still be accessible from the main + * device, either through direct import or through a potentially + * more expensive fallback path. If the buffer can't be directly + * imported from the main device then clients must be prepared for + * the compositor changing the tranche priority or making wl_buffer + * creation fail (see the wp_linux_buffer_params.create and + * create_immed requests for details). + * + * If the device is a DRM node, the DRM node type (primary vs. + * render) is unspecified. Clients must not rely on the compositor + * sending a particular node type. Clients cannot check two devices + * for equality by comparing the dev_t value. + * + * This event is tied to a preference tranche, see the tranche_done + * event. + * @param device device dev_t value + */ + void (*tranche_target_device)(void *data, + struct zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1, + struct wl_array *device); + /** + * supported buffer format modifier + * + * This event advertises the format + modifier combinations that + * the compositor supports. + * + * It carries an array of indices, each referring to a format + + * modifier pair in the last received format table (see the + * format_table event). Each index is a 16-bit unsigned integer in + * native endianness. + * + * For legacy support, DRM_FORMAT_MOD_INVALID is an allowed + * modifier. It indicates that the server can support the format + * with an implicit modifier. When a buffer has + * DRM_FORMAT_MOD_INVALID as its modifier, it is as if no explicit + * modifier is specified. The effective modifier will be derived + * from the dmabuf. + * + * A compositor that sends valid modifiers and + * DRM_FORMAT_MOD_INVALID for a given format supports both explicit + * modifiers and implicit modifiers. + * + * Compositors must not send duplicate format + modifier pairs + * within the same tranche or across two different tranches with + * the same target device and flags. + * + * This event is tied to a preference tranche, see the tranche_done + * event. + * + * For the definition of the format and modifier codes, see the + * wp_linux_buffer_params.create request. + * @param indices array of 16-bit indexes + */ + void (*tranche_formats)(void *data, + struct zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1, + struct wl_array *indices); + /** + * tranche flags + * + * This event sets tranche-specific flags. + * + * The scanout flag is a hint that direct scan-out may be attempted + * by the compositor on the target device if the client + * appropriately allocates a buffer. How to allocate a buffer that + * can be scanned out on the target device is + * implementation-defined. + * + * This event is tied to a preference tranche, see the tranche_done + * event. + * @param flags tranche flags + */ + void (*tranche_flags)(void *data, + struct zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1, + uint32_t flags); +}; + +/** + * @ingroup iface_zwp_linux_dmabuf_feedback_v1 + */ +static inline int +zwp_linux_dmabuf_feedback_v1_add_listener(struct zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1, + const struct zwp_linux_dmabuf_feedback_v1_listener *listener, void *data) +{ + return wl_proxy_add_listener((struct wl_proxy *) zwp_linux_dmabuf_feedback_v1, + (void (**)(void)) listener, data); +} + +#define ZWP_LINUX_DMABUF_FEEDBACK_V1_DESTROY 0 + +/** + * @ingroup iface_zwp_linux_dmabuf_feedback_v1 + */ +#define ZWP_LINUX_DMABUF_FEEDBACK_V1_DONE_SINCE_VERSION 1 +/** + * @ingroup iface_zwp_linux_dmabuf_feedback_v1 + */ +#define ZWP_LINUX_DMABUF_FEEDBACK_V1_FORMAT_TABLE_SINCE_VERSION 1 +/** + * @ingroup iface_zwp_linux_dmabuf_feedback_v1 + */ +#define ZWP_LINUX_DMABUF_FEEDBACK_V1_MAIN_DEVICE_SINCE_VERSION 1 +/** + * @ingroup iface_zwp_linux_dmabuf_feedback_v1 + */ +#define ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_DONE_SINCE_VERSION 1 +/** + * @ingroup iface_zwp_linux_dmabuf_feedback_v1 + */ +#define ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_TARGET_DEVICE_SINCE_VERSION 1 +/** + * @ingroup iface_zwp_linux_dmabuf_feedback_v1 + */ +#define ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_FORMATS_SINCE_VERSION 1 +/** + * @ingroup iface_zwp_linux_dmabuf_feedback_v1 + */ +#define ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_FLAGS_SINCE_VERSION 1 + +/** + * @ingroup iface_zwp_linux_dmabuf_feedback_v1 + */ +#define ZWP_LINUX_DMABUF_FEEDBACK_V1_DESTROY_SINCE_VERSION 1 + +/** @ingroup iface_zwp_linux_dmabuf_feedback_v1 */ +static inline void +zwp_linux_dmabuf_feedback_v1_set_user_data(struct zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) zwp_linux_dmabuf_feedback_v1, user_data); +} + +/** @ingroup iface_zwp_linux_dmabuf_feedback_v1 */ +static inline void * +zwp_linux_dmabuf_feedback_v1_get_user_data(struct zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1) +{ + return wl_proxy_get_user_data((struct wl_proxy *) zwp_linux_dmabuf_feedback_v1); +} + +static inline uint32_t +zwp_linux_dmabuf_feedback_v1_get_version(struct zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1) +{ + return wl_proxy_get_version((struct wl_proxy *) zwp_linux_dmabuf_feedback_v1); +} + +/** + * @ingroup iface_zwp_linux_dmabuf_feedback_v1 + * + * Using this request a client can tell the server that it is not going to + * use the wp_linux_dmabuf_feedback object anymore. + */ +static inline void +zwp_linux_dmabuf_feedback_v1_destroy(struct zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwp_linux_dmabuf_feedback_v1, + ZWP_LINUX_DMABUF_FEEDBACK_V1_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) zwp_linux_dmabuf_feedback_v1), WL_MARSHAL_FLAG_DESTROY); +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/linux-dmabuf-protocol.c b/linux-dmabuf-protocol.c new file mode 100644 index 0000000..0ebe67c --- /dev/null +++ b/linux-dmabuf-protocol.c @@ -0,0 +1,110 @@ +/* Generated by wayland-scanner 1.24.0 */ + +/* + * Copyright © 2014, 2015 Collabora, Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include "wayland-util.h" + +extern const struct wl_interface wl_buffer_interface; +extern const struct wl_interface wl_surface_interface; +extern const struct wl_interface zwp_linux_buffer_params_v1_interface; +extern const struct wl_interface zwp_linux_dmabuf_feedback_v1_interface; + +static const struct wl_interface *linux_dmabuf_unstable_v1_types[] = { + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + &zwp_linux_buffer_params_v1_interface, + &zwp_linux_dmabuf_feedback_v1_interface, + &zwp_linux_dmabuf_feedback_v1_interface, + &wl_surface_interface, + &wl_buffer_interface, + NULL, + NULL, + NULL, + NULL, + &wl_buffer_interface, +}; + +static const struct wl_message zwp_linux_dmabuf_v1_requests[] = { + { "destroy", "", linux_dmabuf_unstable_v1_types + 0 }, + { "create_params", "n", linux_dmabuf_unstable_v1_types + 6 }, + { "get_default_feedback", "4n", linux_dmabuf_unstable_v1_types + 7 }, + { "get_surface_feedback", "4no", linux_dmabuf_unstable_v1_types + 8 }, +}; + +static const struct wl_message zwp_linux_dmabuf_v1_events[] = { + { "format", "u", linux_dmabuf_unstable_v1_types + 0 }, + { "modifier", "3uuu", linux_dmabuf_unstable_v1_types + 0 }, +}; + +WL_EXPORT const struct wl_interface zwp_linux_dmabuf_v1_interface = { + "zwp_linux_dmabuf_v1", 5, + 4, zwp_linux_dmabuf_v1_requests, + 2, zwp_linux_dmabuf_v1_events, +}; + +static const struct wl_message zwp_linux_buffer_params_v1_requests[] = { + { "destroy", "", linux_dmabuf_unstable_v1_types + 0 }, + { "add", "huuuuu", linux_dmabuf_unstable_v1_types + 0 }, + { "create", "iiuu", linux_dmabuf_unstable_v1_types + 0 }, + { "create_immed", "2niiuu", linux_dmabuf_unstable_v1_types + 10 }, +}; + +static const struct wl_message zwp_linux_buffer_params_v1_events[] = { + { "created", "n", linux_dmabuf_unstable_v1_types + 15 }, + { "failed", "", linux_dmabuf_unstable_v1_types + 0 }, +}; + +WL_EXPORT const struct wl_interface zwp_linux_buffer_params_v1_interface = { + "zwp_linux_buffer_params_v1", 5, + 4, zwp_linux_buffer_params_v1_requests, + 2, zwp_linux_buffer_params_v1_events, +}; + +static const struct wl_message zwp_linux_dmabuf_feedback_v1_requests[] = { + { "destroy", "", linux_dmabuf_unstable_v1_types + 0 }, +}; + +static const struct wl_message zwp_linux_dmabuf_feedback_v1_events[] = { + { "done", "", linux_dmabuf_unstable_v1_types + 0 }, + { "format_table", "hu", linux_dmabuf_unstable_v1_types + 0 }, + { "main_device", "a", linux_dmabuf_unstable_v1_types + 0 }, + { "tranche_done", "", linux_dmabuf_unstable_v1_types + 0 }, + { "tranche_target_device", "a", linux_dmabuf_unstable_v1_types + 0 }, + { "tranche_formats", "a", linux_dmabuf_unstable_v1_types + 0 }, + { "tranche_flags", "u", linux_dmabuf_unstable_v1_types + 0 }, +}; + +WL_EXPORT const struct wl_interface zwp_linux_dmabuf_feedback_v1_interface = { + "zwp_linux_dmabuf_feedback_v1", 5, + 1, zwp_linux_dmabuf_feedback_v1_requests, + 7, zwp_linux_dmabuf_feedback_v1_events, +}; + diff --git a/linux-dmabuf-server.h b/linux-dmabuf-server.h new file mode 100644 index 0000000..80eafd3 --- /dev/null +++ b/linux-dmabuf-server.h @@ -0,0 +1,918 @@ +/* Generated by wayland-scanner 1.24.0 */ + +#ifndef LINUX_DMABUF_V1_SERVER_PROTOCOL_H +#define LINUX_DMABUF_V1_SERVER_PROTOCOL_H + +#include +#include +#include "wayland-server.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct wl_client; +struct wl_resource; + +/** + * @page page_linux_dmabuf_v1 The linux_dmabuf_v1 protocol + * @section page_ifaces_linux_dmabuf_v1 Interfaces + * - @subpage page_iface_zwp_linux_dmabuf_v1 - factory for creating dmabuf-based wl_buffers + * - @subpage page_iface_zwp_linux_buffer_params_v1 - parameters for creating a dmabuf-based wl_buffer + * - @subpage page_iface_zwp_linux_dmabuf_feedback_v1 - dmabuf feedback + * @section page_copyright_linux_dmabuf_v1 Copyright + *
+ *
+ * Copyright © 2014, 2015 Collabora, Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ */ +struct wl_buffer; +struct wl_surface; +struct zwp_linux_buffer_params_v1; +struct zwp_linux_dmabuf_feedback_v1; +struct zwp_linux_dmabuf_v1; + +#ifndef ZWP_LINUX_DMABUF_V1_INTERFACE +#define ZWP_LINUX_DMABUF_V1_INTERFACE +/** + * @page page_iface_zwp_linux_dmabuf_v1 zwp_linux_dmabuf_v1 + * @section page_iface_zwp_linux_dmabuf_v1_desc Description + * + * This interface offers ways to create generic dmabuf-based wl_buffers. + * + * For more information about dmabuf, see: + * https://www.kernel.org/doc/html/next/userspace-api/dma-buf-alloc-exchange.html + * + * Clients can use the get_surface_feedback request to get dmabuf feedback + * for a particular surface. If the client wants to retrieve feedback not + * tied to a surface, they can use the get_default_feedback request. + * + * The following are required from clients: + * + * - Clients must ensure that either all data in the dma-buf is + * coherent for all subsequent read access or that coherency is + * correctly handled by the underlying kernel-side dma-buf + * implementation. + * + * - Don't make any more attachments after sending the buffer to the + * compositor. Making more attachments later increases the risk of + * the compositor not being able to use (re-import) an existing + * dmabuf-based wl_buffer. + * + * The underlying graphics stack must ensure the following: + * + * - The dmabuf file descriptors relayed to the server will stay valid + * for the whole lifetime of the wl_buffer. This means the server may + * at any time use those fds to import the dmabuf into any kernel + * sub-system that might accept it. + * + * However, when the underlying graphics stack fails to deliver the + * promise, because of e.g. a device hot-unplug which raises internal + * errors, after the wl_buffer has been successfully created the + * compositor must not raise protocol errors to the client when dmabuf + * import later fails. + * + * To create a wl_buffer from one or more dmabufs, a client creates a + * zwp_linux_dmabuf_params_v1 object with a zwp_linux_dmabuf_v1.create_params + * request. All planes required by the intended format are added with + * the 'add' request. Finally, a 'create' or 'create_immed' request is + * issued, which has the following outcome depending on the import success. + * + * The 'create' request, + * - on success, triggers a 'created' event which provides the final + * wl_buffer to the client. + * - on failure, triggers a 'failed' event to convey that the server + * cannot use the dmabufs received from the client. + * + * For the 'create_immed' request, + * - on success, the server immediately imports the added dmabufs to + * create a wl_buffer. No event is sent from the server in this case. + * - on failure, the server can choose to either: + * - terminate the client by raising a fatal error. + * - mark the wl_buffer as failed, and send a 'failed' event to the + * client. If the client uses a failed wl_buffer as an argument to any + * request, the behaviour is compositor implementation-defined. + * + * For all DRM formats and unless specified in another protocol extension, + * pre-multiplied alpha is used for pixel values. + * + * Unless specified otherwise in another protocol extension, implicit + * synchronization is used. In other words, compositors and clients must + * wait and signal fences implicitly passed via the DMA-BUF's reservation + * mechanism. + * @section page_iface_zwp_linux_dmabuf_v1_api API + * See @ref iface_zwp_linux_dmabuf_v1. + */ +/** + * @defgroup iface_zwp_linux_dmabuf_v1 The zwp_linux_dmabuf_v1 interface + * + * This interface offers ways to create generic dmabuf-based wl_buffers. + * + * For more information about dmabuf, see: + * https://www.kernel.org/doc/html/next/userspace-api/dma-buf-alloc-exchange.html + * + * Clients can use the get_surface_feedback request to get dmabuf feedback + * for a particular surface. If the client wants to retrieve feedback not + * tied to a surface, they can use the get_default_feedback request. + * + * The following are required from clients: + * + * - Clients must ensure that either all data in the dma-buf is + * coherent for all subsequent read access or that coherency is + * correctly handled by the underlying kernel-side dma-buf + * implementation. + * + * - Don't make any more attachments after sending the buffer to the + * compositor. Making more attachments later increases the risk of + * the compositor not being able to use (re-import) an existing + * dmabuf-based wl_buffer. + * + * The underlying graphics stack must ensure the following: + * + * - The dmabuf file descriptors relayed to the server will stay valid + * for the whole lifetime of the wl_buffer. This means the server may + * at any time use those fds to import the dmabuf into any kernel + * sub-system that might accept it. + * + * However, when the underlying graphics stack fails to deliver the + * promise, because of e.g. a device hot-unplug which raises internal + * errors, after the wl_buffer has been successfully created the + * compositor must not raise protocol errors to the client when dmabuf + * import later fails. + * + * To create a wl_buffer from one or more dmabufs, a client creates a + * zwp_linux_dmabuf_params_v1 object with a zwp_linux_dmabuf_v1.create_params + * request. All planes required by the intended format are added with + * the 'add' request. Finally, a 'create' or 'create_immed' request is + * issued, which has the following outcome depending on the import success. + * + * The 'create' request, + * - on success, triggers a 'created' event which provides the final + * wl_buffer to the client. + * - on failure, triggers a 'failed' event to convey that the server + * cannot use the dmabufs received from the client. + * + * For the 'create_immed' request, + * - on success, the server immediately imports the added dmabufs to + * create a wl_buffer. No event is sent from the server in this case. + * - on failure, the server can choose to either: + * - terminate the client by raising a fatal error. + * - mark the wl_buffer as failed, and send a 'failed' event to the + * client. If the client uses a failed wl_buffer as an argument to any + * request, the behaviour is compositor implementation-defined. + * + * For all DRM formats and unless specified in another protocol extension, + * pre-multiplied alpha is used for pixel values. + * + * Unless specified otherwise in another protocol extension, implicit + * synchronization is used. In other words, compositors and clients must + * wait and signal fences implicitly passed via the DMA-BUF's reservation + * mechanism. + */ +extern const struct wl_interface zwp_linux_dmabuf_v1_interface; +#endif +#ifndef ZWP_LINUX_BUFFER_PARAMS_V1_INTERFACE +#define ZWP_LINUX_BUFFER_PARAMS_V1_INTERFACE +/** + * @page page_iface_zwp_linux_buffer_params_v1 zwp_linux_buffer_params_v1 + * @section page_iface_zwp_linux_buffer_params_v1_desc Description + * + * This temporary object is a collection of dmabufs and other + * parameters that together form a single logical buffer. The temporary + * object may eventually create one wl_buffer unless cancelled by + * destroying it before requesting 'create'. + * + * Single-planar formats only require one dmabuf, however + * multi-planar formats may require more than one dmabuf. For all + * formats, an 'add' request must be called once per plane (even if the + * underlying dmabuf fd is identical). + * + * You must use consecutive plane indices ('plane_idx' argument for 'add') + * from zero to the number of planes used by the drm_fourcc format code. + * All planes required by the format must be given exactly once, but can + * be given in any order. Each plane index can only be set once; subsequent + * calls with a plane index which has already been set will result in a + * plane_set error being generated. + * @section page_iface_zwp_linux_buffer_params_v1_api API + * See @ref iface_zwp_linux_buffer_params_v1. + */ +/** + * @defgroup iface_zwp_linux_buffer_params_v1 The zwp_linux_buffer_params_v1 interface + * + * This temporary object is a collection of dmabufs and other + * parameters that together form a single logical buffer. The temporary + * object may eventually create one wl_buffer unless cancelled by + * destroying it before requesting 'create'. + * + * Single-planar formats only require one dmabuf, however + * multi-planar formats may require more than one dmabuf. For all + * formats, an 'add' request must be called once per plane (even if the + * underlying dmabuf fd is identical). + * + * You must use consecutive plane indices ('plane_idx' argument for 'add') + * from zero to the number of planes used by the drm_fourcc format code. + * All planes required by the format must be given exactly once, but can + * be given in any order. Each plane index can only be set once; subsequent + * calls with a plane index which has already been set will result in a + * plane_set error being generated. + */ +extern const struct wl_interface zwp_linux_buffer_params_v1_interface; +#endif +#ifndef ZWP_LINUX_DMABUF_FEEDBACK_V1_INTERFACE +#define ZWP_LINUX_DMABUF_FEEDBACK_V1_INTERFACE +/** + * @page page_iface_zwp_linux_dmabuf_feedback_v1 zwp_linux_dmabuf_feedback_v1 + * @section page_iface_zwp_linux_dmabuf_feedback_v1_desc Description + * + * This object advertises dmabuf parameters feedback. This includes the + * preferred devices and the supported formats/modifiers. + * + * The parameters are sent once when this object is created and whenever they + * change. The done event is always sent once after all parameters have been + * sent. When a single parameter changes, all parameters are re-sent by the + * compositor. + * + * Compositors can re-send the parameters when the current client buffer + * allocations are sub-optimal. Compositors should not re-send the + * parameters if re-allocating the buffers would not result in a more optimal + * configuration. In particular, compositors should avoid sending the exact + * same parameters multiple times in a row. + * + * The tranche_target_device and tranche_formats events are grouped by + * tranches of preference. For each tranche, a tranche_target_device, one + * tranche_flags and one or more tranche_formats events are sent, followed + * by a tranche_done event finishing the list. The tranches are sent in + * descending order of preference. All formats and modifiers in the same + * tranche have the same preference. + * + * To send parameters, the compositor sends one main_device event, tranches + * (each consisting of one tranche_target_device event, one tranche_flags + * event, tranche_formats events and then a tranche_done event), then one + * done event. + * @section page_iface_zwp_linux_dmabuf_feedback_v1_api API + * See @ref iface_zwp_linux_dmabuf_feedback_v1. + */ +/** + * @defgroup iface_zwp_linux_dmabuf_feedback_v1 The zwp_linux_dmabuf_feedback_v1 interface + * + * This object advertises dmabuf parameters feedback. This includes the + * preferred devices and the supported formats/modifiers. + * + * The parameters are sent once when this object is created and whenever they + * change. The done event is always sent once after all parameters have been + * sent. When a single parameter changes, all parameters are re-sent by the + * compositor. + * + * Compositors can re-send the parameters when the current client buffer + * allocations are sub-optimal. Compositors should not re-send the + * parameters if re-allocating the buffers would not result in a more optimal + * configuration. In particular, compositors should avoid sending the exact + * same parameters multiple times in a row. + * + * The tranche_target_device and tranche_formats events are grouped by + * tranches of preference. For each tranche, a tranche_target_device, one + * tranche_flags and one or more tranche_formats events are sent, followed + * by a tranche_done event finishing the list. The tranches are sent in + * descending order of preference. All formats and modifiers in the same + * tranche have the same preference. + * + * To send parameters, the compositor sends one main_device event, tranches + * (each consisting of one tranche_target_device event, one tranche_flags + * event, tranche_formats events and then a tranche_done event), then one + * done event. + */ +extern const struct wl_interface zwp_linux_dmabuf_feedback_v1_interface; +#endif + +/** + * @ingroup iface_zwp_linux_dmabuf_v1 + * @struct zwp_linux_dmabuf_v1_interface + */ +struct zwp_linux_dmabuf_v1_interface { + /** + * unbind the factory + * + * Objects created through this interface, especially wl_buffers, + * will remain valid. + */ + void (*destroy)(struct wl_client *client, + struct wl_resource *resource); + /** + * create a temporary object for buffer parameters + * + * This temporary object is used to collect multiple dmabuf + * handles into a single batch to create a wl_buffer. It can only + * be used once and should be destroyed after a 'created' or + * 'failed' event has been received. + * @param params_id the new temporary + */ + void (*create_params)(struct wl_client *client, + struct wl_resource *resource, + uint32_t params_id); + /** + * get default feedback + * + * This request creates a new wp_linux_dmabuf_feedback object not + * bound to a particular surface. This object will deliver feedback + * about dmabuf parameters to use if the client doesn't support + * per-surface feedback (see get_surface_feedback). + * @since 4 + */ + void (*get_default_feedback)(struct wl_client *client, + struct wl_resource *resource, + uint32_t id); + /** + * get feedback for a surface + * + * This request creates a new wp_linux_dmabuf_feedback object for + * the specified wl_surface. This object will deliver feedback + * about dmabuf parameters to use for buffers attached to this + * surface. + * + * If the surface is destroyed before the wp_linux_dmabuf_feedback + * object, the feedback object becomes inert. + * @since 4 + */ + void (*get_surface_feedback)(struct wl_client *client, + struct wl_resource *resource, + uint32_t id, + struct wl_resource *surface); +}; + +#define ZWP_LINUX_DMABUF_V1_FORMAT 0 +#define ZWP_LINUX_DMABUF_V1_MODIFIER 1 + +/** + * @ingroup iface_zwp_linux_dmabuf_v1 + */ +#define ZWP_LINUX_DMABUF_V1_FORMAT_SINCE_VERSION 1 +/** + * @ingroup iface_zwp_linux_dmabuf_v1 + */ +#define ZWP_LINUX_DMABUF_V1_MODIFIER_SINCE_VERSION 3 + +/** + * @ingroup iface_zwp_linux_dmabuf_v1 + */ +#define ZWP_LINUX_DMABUF_V1_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_zwp_linux_dmabuf_v1 + */ +#define ZWP_LINUX_DMABUF_V1_CREATE_PARAMS_SINCE_VERSION 1 +/** + * @ingroup iface_zwp_linux_dmabuf_v1 + */ +#define ZWP_LINUX_DMABUF_V1_GET_DEFAULT_FEEDBACK_SINCE_VERSION 4 +/** + * @ingroup iface_zwp_linux_dmabuf_v1 + */ +#define ZWP_LINUX_DMABUF_V1_GET_SURFACE_FEEDBACK_SINCE_VERSION 4 + +/** + * @ingroup iface_zwp_linux_dmabuf_v1 + * Sends an format event to the client owning the resource. + * @param resource_ The client's resource + * @param format DRM_FORMAT code + */ +static inline void +zwp_linux_dmabuf_v1_send_format(struct wl_resource *resource_, uint32_t format) +{ + wl_resource_post_event(resource_, ZWP_LINUX_DMABUF_V1_FORMAT, format); +} + +/** + * @ingroup iface_zwp_linux_dmabuf_v1 + * Sends an modifier event to the client owning the resource. + * @param resource_ The client's resource + * @param format DRM_FORMAT code + * @param modifier_hi high 32 bits of layout modifier + * @param modifier_lo low 32 bits of layout modifier + */ +static inline void +zwp_linux_dmabuf_v1_send_modifier(struct wl_resource *resource_, uint32_t format, uint32_t modifier_hi, uint32_t modifier_lo) +{ + wl_resource_post_event(resource_, ZWP_LINUX_DMABUF_V1_MODIFIER, format, modifier_hi, modifier_lo); +} + +#ifndef ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ENUM +#define ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ENUM +enum zwp_linux_buffer_params_v1_error { + /** + * the dmabuf_batch object has already been used to create a wl_buffer + */ + ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ALREADY_USED = 0, + /** + * plane index out of bounds + */ + ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_PLANE_IDX = 1, + /** + * the plane index was already set + */ + ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_PLANE_SET = 2, + /** + * missing or too many planes to create a buffer + */ + ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE = 3, + /** + * format not supported + */ + ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_FORMAT = 4, + /** + * invalid width or height + */ + ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_DIMENSIONS = 5, + /** + * offset + stride * height goes out of dmabuf bounds + */ + ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS = 6, + /** + * invalid wl_buffer resulted from importing dmabufs via the create_immed request on given buffer_params + */ + ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_WL_BUFFER = 7, +}; +#endif /* ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ENUM */ + +#ifndef ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ENUM_IS_VALID +#define ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ENUM_IS_VALID +/** + * @ingroup iface_zwp_linux_buffer_params_v1 + * Validate a zwp_linux_buffer_params_v1 error value. + * + * @return true on success, false on error. + * @ref zwp_linux_buffer_params_v1_error + */ +static inline bool +zwp_linux_buffer_params_v1_error_is_valid(uint32_t value, uint32_t version) { + switch (value) { + case ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ALREADY_USED: + return version >= 1; + case ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_PLANE_IDX: + return version >= 1; + case ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_PLANE_SET: + return version >= 1; + case ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE: + return version >= 1; + case ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_FORMAT: + return version >= 1; + case ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_DIMENSIONS: + return version >= 1; + case ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS: + return version >= 1; + case ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_WL_BUFFER: + return version >= 1; + default: + return false; + } +} +#endif /* ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ENUM_IS_VALID */ + +#ifndef ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_ENUM +#define ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_ENUM +enum zwp_linux_buffer_params_v1_flags { + /** + * contents are y-inverted + */ + ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_Y_INVERT = 1, + /** + * content is interlaced + */ + ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_INTERLACED = 2, + /** + * bottom field first + */ + ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_BOTTOM_FIRST = 4, +}; +#endif /* ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_ENUM */ + +#ifndef ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_ENUM_IS_VALID +#define ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_ENUM_IS_VALID +/** + * @ingroup iface_zwp_linux_buffer_params_v1 + * Validate a zwp_linux_buffer_params_v1 flags value. + * + * @return true on success, false on error. + * @ref zwp_linux_buffer_params_v1_flags + */ +static inline bool +zwp_linux_buffer_params_v1_flags_is_valid(uint32_t value, uint32_t version) { + uint32_t valid = 0; + if (version >= 1) + valid |= ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_Y_INVERT; + if (version >= 1) + valid |= ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_INTERLACED; + if (version >= 1) + valid |= ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_BOTTOM_FIRST; + return (value & ~valid) == 0; +} +#endif /* ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_ENUM_IS_VALID */ + +/** + * @ingroup iface_zwp_linux_buffer_params_v1 + * @struct zwp_linux_buffer_params_v1_interface + */ +struct zwp_linux_buffer_params_v1_interface { + /** + * delete this object, used or not + * + * Cleans up the temporary data sent to the server for + * dmabuf-based wl_buffer creation. + */ + void (*destroy)(struct wl_client *client, + struct wl_resource *resource); + /** + * add a dmabuf to the temporary set + * + * This request adds one dmabuf to the set in this + * zwp_linux_buffer_params_v1. + * + * The 64-bit unsigned value combined from modifier_hi and + * modifier_lo is the dmabuf layout modifier. DRM AddFB2 ioctl + * calls this the fb modifier, which is defined in drm_mode.h of + * Linux UAPI. This is an opaque token. Drivers use this token to + * express tiling, compression, etc. driver-specific modifications + * to the base format defined by the DRM fourcc code. + * + * Starting from version 4, the invalid_format protocol error is + * sent if the format + modifier pair was not advertised as + * supported. + * + * Starting from version 5, the invalid_format protocol error is + * sent if all planes don't use the same modifier. + * + * This request raises the PLANE_IDX error if plane_idx is too + * large. The error PLANE_SET is raised if attempting to set a + * plane that was already set. + * @param fd dmabuf fd + * @param plane_idx plane index + * @param offset offset in bytes + * @param stride stride in bytes + * @param modifier_hi high 32 bits of layout modifier + * @param modifier_lo low 32 bits of layout modifier + */ + void (*add)(struct wl_client *client, + struct wl_resource *resource, + int32_t fd, + uint32_t plane_idx, + uint32_t offset, + uint32_t stride, + uint32_t modifier_hi, + uint32_t modifier_lo); + /** + * create a wl_buffer from the given dmabufs + * + * This asks for creation of a wl_buffer from the added dmabuf + * buffers. The wl_buffer is not created immediately but returned + * via the 'created' event if the dmabuf sharing succeeds. The + * sharing may fail at runtime for reasons a client cannot predict, + * in which case the 'failed' event is triggered. + * + * The 'format' argument is a DRM_FORMAT code, as defined by the + * libdrm's drm_fourcc.h. The Linux kernel's DRM sub-system is the + * authoritative source on how the format codes should work. + * + * The 'flags' is a bitfield of the flags defined in enum "flags". + * 'y_invert' means the that the image needs to be y-flipped. + * + * Flag 'interlaced' means that the frame in the buffer is not + * progressive as usual, but interlaced. An interlaced buffer as + * supported here must always contain both top and bottom fields. + * The top field always begins on the first pixel row. The temporal + * ordering between the two fields is top field first, unless + * 'bottom_first' is specified. It is undefined whether + * 'bottom_first' is ignored if 'interlaced' is not set. + * + * This protocol does not convey any information about field rate, + * duration, or timing, other than the relative ordering between + * the two fields in one buffer. A compositor may have to estimate + * the intended field rate from the incoming buffer rate. It is + * undefined whether the time of receiving wl_surface.commit with a + * new buffer attached, applying the wl_surface state, + * wl_surface.frame callback trigger, presentation, or any other + * point in the compositor cycle is used to measure the frame or + * field times. There is no support for detecting missed or late + * frames/fields/buffers either, and there is no support whatsoever + * for cooperating with interlaced compositor output. + * + * The composited image quality resulting from the use of + * interlaced buffers is explicitly undefined. A compositor may use + * elaborate hardware features or software to deinterlace and + * create progressive output frames from a sequence of interlaced + * input buffers, or it may produce substandard image quality. + * However, compositors that cannot guarantee reasonable image + * quality in all cases are recommended to just reject all + * interlaced buffers. + * + * Any argument errors, including non-positive width or height, + * mismatch between the number of planes and the format, bad + * format, bad offset or stride, may be indicated by fatal protocol + * errors: INCOMPLETE, INVALID_FORMAT, INVALID_DIMENSIONS, + * OUT_OF_BOUNDS. + * + * Dmabuf import errors in the server that are not obvious client + * bugs are returned via the 'failed' event as non-fatal. This + * allows attempting dmabuf sharing and falling back in the client + * if it fails. + * + * This request can be sent only once in the object's lifetime, + * after which the only legal request is destroy. This object + * should be destroyed after issuing a 'create' request. Attempting + * to use this object after issuing 'create' raises ALREADY_USED + * protocol error. + * + * It is not mandatory to issue 'create'. If a client wants to + * cancel the buffer creation, it can just destroy this object. + * @param width base plane width in pixels + * @param height base plane height in pixels + * @param format DRM_FORMAT code + * @param flags see enum flags + */ + void (*create)(struct wl_client *client, + struct wl_resource *resource, + int32_t width, + int32_t height, + uint32_t format, + uint32_t flags); + /** + * immediately create a wl_buffer from the given dmabufs + * + * This asks for immediate creation of a wl_buffer by importing + * the added dmabufs. + * + * In case of import success, no event is sent from the server, and + * the wl_buffer is ready to be used by the client. + * + * Upon import failure, either of the following may happen, as seen + * fit by the implementation: - the client is terminated with one + * of the following fatal protocol errors: - INCOMPLETE, + * INVALID_FORMAT, INVALID_DIMENSIONS, OUT_OF_BOUNDS, in case of + * argument errors such as mismatch between the number of planes + * and the format, bad format, non-positive width or height, or bad + * offset or stride. - INVALID_WL_BUFFER, in case the cause for + * failure is unknown or platform specific. - the server creates an + * invalid wl_buffer, marks it as failed and sends a 'failed' event + * to the client. The result of using this invalid wl_buffer as an + * argument in any request by the client is defined by the + * compositor implementation. + * + * This takes the same arguments as a 'create' request, and obeys + * the same restrictions. + * @param buffer_id id for the newly created wl_buffer + * @param width base plane width in pixels + * @param height base plane height in pixels + * @param format DRM_FORMAT code + * @param flags see enum flags + * @since 2 + */ + void (*create_immed)(struct wl_client *client, + struct wl_resource *resource, + uint32_t buffer_id, + int32_t width, + int32_t height, + uint32_t format, + uint32_t flags); +}; + +#define ZWP_LINUX_BUFFER_PARAMS_V1_CREATED 0 +#define ZWP_LINUX_BUFFER_PARAMS_V1_FAILED 1 + +/** + * @ingroup iface_zwp_linux_buffer_params_v1 + */ +#define ZWP_LINUX_BUFFER_PARAMS_V1_CREATED_SINCE_VERSION 1 +/** + * @ingroup iface_zwp_linux_buffer_params_v1 + */ +#define ZWP_LINUX_BUFFER_PARAMS_V1_FAILED_SINCE_VERSION 1 + +/** + * @ingroup iface_zwp_linux_buffer_params_v1 + */ +#define ZWP_LINUX_BUFFER_PARAMS_V1_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_zwp_linux_buffer_params_v1 + */ +#define ZWP_LINUX_BUFFER_PARAMS_V1_ADD_SINCE_VERSION 1 +/** + * @ingroup iface_zwp_linux_buffer_params_v1 + */ +#define ZWP_LINUX_BUFFER_PARAMS_V1_CREATE_SINCE_VERSION 1 +/** + * @ingroup iface_zwp_linux_buffer_params_v1 + */ +#define ZWP_LINUX_BUFFER_PARAMS_V1_CREATE_IMMED_SINCE_VERSION 2 + +/** + * @ingroup iface_zwp_linux_buffer_params_v1 + * Sends an created event to the client owning the resource. + * @param resource_ The client's resource + * @param buffer the newly created wl_buffer + */ +static inline void +zwp_linux_buffer_params_v1_send_created(struct wl_resource *resource_, struct wl_resource *buffer) +{ + wl_resource_post_event(resource_, ZWP_LINUX_BUFFER_PARAMS_V1_CREATED, buffer); +} + +/** + * @ingroup iface_zwp_linux_buffer_params_v1 + * Sends an failed event to the client owning the resource. + * @param resource_ The client's resource + */ +static inline void +zwp_linux_buffer_params_v1_send_failed(struct wl_resource *resource_) +{ + wl_resource_post_event(resource_, ZWP_LINUX_BUFFER_PARAMS_V1_FAILED); +} + +#ifndef ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_FLAGS_ENUM +#define ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_FLAGS_ENUM +enum zwp_linux_dmabuf_feedback_v1_tranche_flags { + /** + * direct scan-out tranche + */ + ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_FLAGS_SCANOUT = 1, +}; +#endif /* ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_FLAGS_ENUM */ + +#ifndef ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_FLAGS_ENUM_IS_VALID +#define ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_FLAGS_ENUM_IS_VALID +/** + * @ingroup iface_zwp_linux_dmabuf_feedback_v1 + * Validate a zwp_linux_dmabuf_feedback_v1 tranche_flags value. + * + * @return true on success, false on error. + * @ref zwp_linux_dmabuf_feedback_v1_tranche_flags + */ +static inline bool +zwp_linux_dmabuf_feedback_v1_tranche_flags_is_valid(uint32_t value, uint32_t version) { + uint32_t valid = 0; + if (version >= 1) + valid |= ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_FLAGS_SCANOUT; + return (value & ~valid) == 0; +} +#endif /* ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_FLAGS_ENUM_IS_VALID */ + +/** + * @ingroup iface_zwp_linux_dmabuf_feedback_v1 + * @struct zwp_linux_dmabuf_feedback_v1_interface + */ +struct zwp_linux_dmabuf_feedback_v1_interface { + /** + * destroy the feedback object + * + * Using this request a client can tell the server that it is not + * going to use the wp_linux_dmabuf_feedback object anymore. + */ + void (*destroy)(struct wl_client *client, + struct wl_resource *resource); +}; + +#define ZWP_LINUX_DMABUF_FEEDBACK_V1_DONE 0 +#define ZWP_LINUX_DMABUF_FEEDBACK_V1_FORMAT_TABLE 1 +#define ZWP_LINUX_DMABUF_FEEDBACK_V1_MAIN_DEVICE 2 +#define ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_DONE 3 +#define ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_TARGET_DEVICE 4 +#define ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_FORMATS 5 +#define ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_FLAGS 6 + +/** + * @ingroup iface_zwp_linux_dmabuf_feedback_v1 + */ +#define ZWP_LINUX_DMABUF_FEEDBACK_V1_DONE_SINCE_VERSION 1 +/** + * @ingroup iface_zwp_linux_dmabuf_feedback_v1 + */ +#define ZWP_LINUX_DMABUF_FEEDBACK_V1_FORMAT_TABLE_SINCE_VERSION 1 +/** + * @ingroup iface_zwp_linux_dmabuf_feedback_v1 + */ +#define ZWP_LINUX_DMABUF_FEEDBACK_V1_MAIN_DEVICE_SINCE_VERSION 1 +/** + * @ingroup iface_zwp_linux_dmabuf_feedback_v1 + */ +#define ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_DONE_SINCE_VERSION 1 +/** + * @ingroup iface_zwp_linux_dmabuf_feedback_v1 + */ +#define ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_TARGET_DEVICE_SINCE_VERSION 1 +/** + * @ingroup iface_zwp_linux_dmabuf_feedback_v1 + */ +#define ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_FORMATS_SINCE_VERSION 1 +/** + * @ingroup iface_zwp_linux_dmabuf_feedback_v1 + */ +#define ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_FLAGS_SINCE_VERSION 1 + +/** + * @ingroup iface_zwp_linux_dmabuf_feedback_v1 + */ +#define ZWP_LINUX_DMABUF_FEEDBACK_V1_DESTROY_SINCE_VERSION 1 + +/** + * @ingroup iface_zwp_linux_dmabuf_feedback_v1 + * Sends an done event to the client owning the resource. + * @param resource_ The client's resource + */ +static inline void +zwp_linux_dmabuf_feedback_v1_send_done(struct wl_resource *resource_) +{ + wl_resource_post_event(resource_, ZWP_LINUX_DMABUF_FEEDBACK_V1_DONE); +} + +/** + * @ingroup iface_zwp_linux_dmabuf_feedback_v1 + * Sends an format_table event to the client owning the resource. + * @param resource_ The client's resource + * @param fd table file descriptor + * @param size table size, in bytes + */ +static inline void +zwp_linux_dmabuf_feedback_v1_send_format_table(struct wl_resource *resource_, int32_t fd, uint32_t size) +{ + wl_resource_post_event(resource_, ZWP_LINUX_DMABUF_FEEDBACK_V1_FORMAT_TABLE, fd, size); +} + +/** + * @ingroup iface_zwp_linux_dmabuf_feedback_v1 + * Sends an main_device event to the client owning the resource. + * @param resource_ The client's resource + * @param device device dev_t value + */ +static inline void +zwp_linux_dmabuf_feedback_v1_send_main_device(struct wl_resource *resource_, struct wl_array *device) +{ + wl_resource_post_event(resource_, ZWP_LINUX_DMABUF_FEEDBACK_V1_MAIN_DEVICE, device); +} + +/** + * @ingroup iface_zwp_linux_dmabuf_feedback_v1 + * Sends an tranche_done event to the client owning the resource. + * @param resource_ The client's resource + */ +static inline void +zwp_linux_dmabuf_feedback_v1_send_tranche_done(struct wl_resource *resource_) +{ + wl_resource_post_event(resource_, ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_DONE); +} + +/** + * @ingroup iface_zwp_linux_dmabuf_feedback_v1 + * Sends an tranche_target_device event to the client owning the resource. + * @param resource_ The client's resource + * @param device device dev_t value + */ +static inline void +zwp_linux_dmabuf_feedback_v1_send_tranche_target_device(struct wl_resource *resource_, struct wl_array *device) +{ + wl_resource_post_event(resource_, ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_TARGET_DEVICE, device); +} + +/** + * @ingroup iface_zwp_linux_dmabuf_feedback_v1 + * Sends an tranche_formats event to the client owning the resource. + * @param resource_ The client's resource + * @param indices array of 16-bit indexes + */ +static inline void +zwp_linux_dmabuf_feedback_v1_send_tranche_formats(struct wl_resource *resource_, struct wl_array *indices) +{ + wl_resource_post_event(resource_, ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_FORMATS, indices); +} + +/** + * @ingroup iface_zwp_linux_dmabuf_feedback_v1 + * Sends an tranche_flags event to the client owning the resource. + * @param resource_ The client's resource + * @param flags tranche flags + */ +static inline void +zwp_linux_dmabuf_feedback_v1_send_tranche_flags(struct wl_resource *resource_, uint32_t flags) +{ + wl_resource_post_event(resource_, ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_FLAGS, flags); +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/tab-commands b/tab-commands deleted file mode 100644 index a20eaf6..0000000 --- a/tab-commands +++ /dev/null @@ -1,3 +0,0 @@ -foot -galculator -geany diff --git a/tabbed-windows.c b/tabbed-windows.c new file mode 100644 index 0000000..e5af24a --- /dev/null +++ b/tabbed-windows.c @@ -0,0 +1,2713 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include "xdg-shell-server.h" +#include "xdg-shell-client.h" +#include "linux-dmabuf-server.h" +#include "wl-drm-server.h" +#include +#include +#include +#include +#include + +#define MAX_APPS 8 +#define SOCKET_NAME "tabby-wayland-0" +#define MAX_WIN_W 2560 +#define MAX_WIN_H 1600 + +struct tabby_surface; +struct tabby_compositor; + +struct tabby_pool { + int fd; + size_t size; + unsigned char *data; + int refs; + bool destroyed; +}; + +struct tabby_buffer { + struct wl_resource *resource; + struct wl_list buffer_link; + /* shm path */ + struct tabby_pool *pool; + int32_t offset; + int32_t width, height, stride; + uint32_t format; + /* dmabuf path */ + bool is_dmabuf; + int n_planes; + int fd[4]; + uint32_t dma_offset[4]; + uint32_t dma_stride[4]; + uint32_t dma_mod_hi[4]; + uint32_t dma_mod_lo[4]; +}; + +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; + struct tabby_buffer *pending_release; + 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; + int tab_index; + /* shm cached pixels */ + unsigned char *pixels; + int pixel_width, pixel_height, pixel_stride; + /* GPU texture (dmabuf imported, or shm uploaded at render) */ + GLuint tex; + EGLImageKHR egl_image; + bool has_tex; + int tex_w, tex_h; + /* window geometry (CSD shadow handling) */ + int geom_x, geom_y, geom_width, geom_height; +}; + +struct tabby_app { + char *cmd; + pid_t pid; + struct tabby_surface *surface; +}; + +struct tabby_compositor { + struct wl_display *display; + struct wl_event_loop *loop; + struct wl_list surfaces; + struct wl_list buffers; + struct tabby_output output; + struct tabby_surface *active_surface; + struct tabby_app *apps[MAX_APPS]; + int num_apps; + struct wl_list input_tracks; + uint32_t serial; + bool clients_launched; + /* xkb keymap shared with our clients */ + struct xkb_context *xkb_ctx; + struct xkb_keymap *xkb_keymap; + char *xkb_keymap_str; + int xkb_keymap_fd; + int xkb_keymap_size; + /* keyboard focus */ + struct tabby_surface *kbd_focus; + struct wl_resource *kbd_resource; + uint32_t input_serial; + /* pointer tracking */ + bool pointer_entered; + double pointer_x, pointer_y; +}; + +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; +}; + +/* ===================== Host-side (nested) state ===================== */ + +struct tabby_host { + struct wl_display *display; + struct wl_registry *registry; + struct wl_compositor *compositor; + struct wl_surface *surface; + struct xdg_wm_base *wm_base; + struct xdg_surface *xdg_surface; + struct xdg_toplevel *toplevel; + struct wl_seat *seat; + struct wl_keyboard *keyboard; + struct wl_pointer *pointer; + struct wl_shm *shm; + struct wl_shm_pool *shm_pool; + struct wl_buffer *shm_buffer; + void *shm_data; + size_t shm_size; + bool configured; + int width, height; + /* EGL */ + bool egl_ok; + EGLDisplay egl_display; + EGLConfig egl_config; + EGLContext egl_context; + EGLSurface egl_surface; + struct wl_egl_window *egl_window; + /* host modifiers (for tab-switch detection) */ + uint32_t host_mods_depressed; + /* GL program */ + GLuint gl_program; + GLint gl_pos_loc, gl_uv_loc, gl_tex_loc; +}; + +static struct tabby_compositor compositor; +static struct tabby_host host; +static bool needs_redraw; +static int drm_fd = -1; + +/* Forward declarations */ +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_buffer_interface buffer_impl; +static const struct xdg_positioner_interface positioner_impl; +static const struct zwp_linux_dmabuf_v1_interface dmabuf_impl; +static const struct zwp_linux_buffer_params_v1_interface dmabuf_params_impl; +static const struct wl_drm_interface drm_impl; + +static void launch_client(int idx); +static void set_active_surface(struct tabby_surface *surface); +static void schedule_redraw(void); +static void host_resize(int width, int height); +static void fit_host_window(void); +static void dmabuf_get_feedback(struct wl_client *client, + struct wl_resource *resource, + uint32_t id); +static void dmabuf_get_surface_feedback(struct wl_client *client, + struct wl_resource *resource, + uint32_t id, + struct wl_resource *surface); + +/* ===================== EGL helpers ===================== */ + +static PFNEGLCREATEIMAGEKHRPROC pfn_eglCreateImageKHR; +static PFNEGLDESTROYIMAGEKHRPROC pfn_eglDestroyImageKHR; +static PFNEGLGETPLATFORMDISPLAYEXTPROC pfn_eglGetPlatformDisplayEXT; +static PFNGLEGLIMAGETARGETTEXTURE2DOESPROC pfn_glEGLImageTargetTexture2DOES; + +static void *egl_proc(const char *name) { + void *p = (void *)eglGetProcAddress(name); + if (!p) fprintf(stderr, "tabbed-windows: EGL proc %s unavailable\n", name); + return p; +} + +/* ===================== 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; + if (compositor.kbd_focus == surface) + compositor.kbd_focus = NULL; + if (surface->pending_release) + wl_buffer_send_release(surface->pending_release->resource); + if (host.egl_ok) { + if (surface->tex) + glDeleteTextures(1, &surface->tex); + if (surface->egl_image) + pfn_eglDestroyImageKHR(host.egl_display, surface->egl_image); + } + 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; +} + +/* Compute the source rectangle (geometry rect clamped to buffer) to blit + from the buffer/texture, mirroring tabbed-wayland's margin handling. */ +static void content_rect(struct tabby_surface *surface, int *x, int *y, + int *w, int *h) +{ + int bw = surface->tex_w ? surface->tex_w : surface->pixel_width; + int bh = surface->tex_h ? surface->tex_h : surface->pixel_height; + int src_x = 0, src_y = 0, src_w = bw, src_h = bh; + if (surface->geom_width > 0 && surface->geom_height > 0) { + src_x = (surface->geom_x > 0) ? surface->geom_x : 0; + src_y = (surface->geom_y > 0) ? surface->geom_y : 0; + src_w = surface->geom_width; + src_h = surface->geom_height; + if (src_x >= bw) src_x = bw - 1; + if (src_y >= bh) src_y = bh - 1; + if (src_w > bw - src_x) src_w = bw - src_x; + if (src_h > bh - src_y) src_h = bh - src_y; + } + *x = src_x; *y = src_y; *w = src_w; *h = src_h; +} + +static void send_frame_done(struct tabby_surface *surface) { + if (!surface->frame_callback) return; + 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 bool ensure_texture(struct tabby_surface *s, int w, int h) { + if (!host.egl_ok) return false; + if (!s->tex) { + glGenTextures(1, &s->tex); + if (!s->tex) return false; + glBindTexture(GL_TEXTURE_2D, s->tex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } + glBindTexture(GL_TEXTURE_2D, s->tex); + s->tex_w = w; + s->tex_h = h; + return true; +} + +static bool import_dmabuf(struct tabby_surface *s, struct tabby_buffer *buf) { + if (!host.egl_ok || !pfn_eglCreateImageKHR || !pfn_glEGLImageTargetTexture2DOES) + return false; + static int nimport; + fprintf(stderr, "[import] dmabuf %ux%u fmt=%x planes=%d (n=%d)\n", + buf->width, buf->height, buf->format, buf->n_planes, ++nimport); + if (s->egl_image) { + pfn_eglDestroyImageKHR(host.egl_display, s->egl_image); + s->egl_image = EGL_NO_IMAGE; + } + s->has_tex = false; + + EGLint attrs[64]; + int n = 0; + attrs[n++] = EGL_WIDTH; attrs[n++] = buf->width; + attrs[n++] = EGL_HEIGHT; attrs[n++] = buf->height; + attrs[n++] = EGL_LINUX_DRM_FOURCC_EXT; attrs[n++] = (EGLint)buf->format; + for (int i = 0; i < buf->n_planes && i < 4; i++) { + attrs[n++] = EGL_DMA_BUF_PLANE0_FD_EXT + i * 3; attrs[n++] = (EGLint)buf->fd[i]; + attrs[n++] = EGL_DMA_BUF_PLANE0_OFFSET_EXT + i * 3; attrs[n++] = (EGLint)buf->dma_offset[i]; + attrs[n++] = EGL_DMA_BUF_PLANE0_PITCH_EXT + i * 3; attrs[n++] = (EGLint)buf->dma_stride[i]; + uint32_t mhi = buf->dma_mod_hi[i], mlo = buf->dma_mod_lo[i]; + bool invalid = (mhi == 0xffffffffu && mlo == 0xffffffffu); + bool set = (mhi || mlo) && !invalid; + if (set) { + attrs[n++] = EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT + i * 3; attrs[n++] = (EGLint)mlo; + attrs[n++] = EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT + i * 3; attrs[n++] = (EGLint)mhi; + } + } + attrs[n++] = EGL_NONE; + + EGLImageKHR img = pfn_eglCreateImageKHR(host.egl_display, EGL_NO_CONTEXT, + EGL_LINUX_DMA_BUF_EXT, NULL, attrs); + if (img == EGL_NO_IMAGE) { + fprintf(stderr, "tabbed-windows: dmabuf import failed (fourcc %x)\n", + buf->format); + return false; + } + s->egl_image = img; + if (!ensure_texture(s, buf->width, buf->height)) + return false; + pfn_glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, img); + s->has_tex = true; + s->tex_w = buf->width; + s->tex_h = buf->height; + return true; +} + +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); + + send_frame_done(surface); + return; + } + + if (surface->has_buffer && surface->buffer) { + struct tabby_buffer *buf = surface->buffer; + bool handled = false; + + if (buf->is_dmabuf) { + if (import_dmabuf(surface, buf)) { + /* texture owns the imported memory; buffer can be released */ + free(surface->pixels); + surface->pixels = NULL; + surface->pixel_width = surface->pixel_height = 0; + if (surface->pending_release) { + wl_buffer_send_release(surface->pending_release->resource); + surface->pending_release = NULL; + } + wl_buffer_send_release(buf->resource); + handled = true; + } else { + /* failed import: drop frame, release buffer */ + wl_buffer_send_release(buf->resource); + surface->has_tex = false; + handled = true; + } + } else if (buf->pool && buf->pool->data) { + static bool logged_pixels; + if (!logged_pixels) { + fprintf(stderr, "[shm] buffer %dx%d stride=%d fmt=%x\n", + buf->width, buf->height, buf->stride, buf->format); + logged_pixels = true; + } + const size_t bpp = 4; + 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; + unsigned char *np = realloc(surface->pixels, size); + if (np) { + surface->pixels = np; + memcpy(surface->pixels, buf->pool->data + buf->offset, size); + } + surface->has_tex = false; + } else { + free(surface->pixels); + surface->pixels = NULL; + surface->pixel_width = surface->pixel_height = 0; + } + /* deferred release (GTK3 staging-buffer safety) */ + if (surface->pending_release && + surface->pending_release != buf) + wl_buffer_send_release(surface->pending_release->resource); + surface->pending_release = buf; + handled = true; + } + + if (handled && surface == compositor.active_surface) { + schedule_redraw(); + fit_host_window(); + } + } else { + if (surface->pending_release) + wl_buffer_send_release(surface->pending_release->resource); + surface->pending_release = NULL; + free(surface->pixels); + surface->pixels = NULL; + surface->has_tex = false; + if (surface == compositor.active_surface) + schedule_redraw(); + } + + send_frame_done(surface); +} + +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; + pool->destroyed = true; + /* Buffers may still reference this pool (the client is allowed to + * destroy the pool before the buffers); keep the mapping alive until + * the last referencing buffer is destroyed. */ + if (pool->refs <= 0) { + 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); + wl_list_remove(&buf->buffer_link); + struct tabby_surface *s; + wl_list_for_each(s, &compositor.surfaces, link) { + if (s->pending_release == buf) + s->pending_release = NULL; + if (s->buffer == buf) { + s->buffer = NULL; + s->has_buffer = false; + } + } + if (buf->is_dmabuf) { + for (int i = 0; i < buf->n_planes && i < 4; i++) + if (buf->fd[i] >= 0) close(buf->fd[i]); + } + if (buf->pool) { + buf->pool->refs--; + if (buf->pool->refs <= 0 && buf->pool->destroyed) { + munmap(buf->pool->data, buf->pool->size); + close(buf->pool->fd); + free(buf->pool); + } + } + 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; + } + 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; + pool->refs++; + buf->offset = offset; + buf->width = width; + buf->height = height; + buf->stride = stride; + buf->format = format; + wl_list_insert(&compositor.buffers, &buf->buffer_link); + 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 = calloc(1, 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, +}; + +/* ===================== linux-dmabuf ===================== */ + +struct dmabuf_params { + int fd[4]; + uint32_t offset[4]; + uint32_t stride[4]; + uint32_t mod_hi[4]; + uint32_t mod_lo[4]; + int n_planes; + bool used; +}; + +static void dmabuf_destroy(struct wl_client *client, struct wl_resource *resource) { + (void)client; + wl_resource_destroy(resource); +} + +static void dmabuf_params_cleanup(struct dmabuf_params *p) { + if (!p) return; + for (int i = 0; i < p->n_planes && i < 4; i++) + if (p->fd[i] >= 0) close(p->fd[i]); + free(p); +} + +static void dmabuf_params_destroy(struct wl_client *client, struct wl_resource *resource) { + (void)client; + wl_resource_destroy(resource); +} + +static void dmabuf_params_resource_destroy(struct wl_resource *resource) { + struct dmabuf_params *p = wl_resource_get_user_data(resource); + dmabuf_params_cleanup(p); + wl_resource_set_user_data(resource, NULL); +} + +static void dmabuf_params_add(struct wl_client *client, struct wl_resource *resource, + int32_t fd, uint32_t plane_idx, uint32_t offset, + uint32_t stride, uint32_t modifier_hi, uint32_t modifier_lo) +{ + (void)client; + struct dmabuf_params *p = wl_resource_get_user_data(resource); + if (!p) return; + if (plane_idx >= 4) { + wl_resource_post_error(resource, ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_PLANE_IDX, + "plane index %u too large", plane_idx); + close(fd); + return; + } + p->fd[plane_idx] = fd; + p->offset[plane_idx] = offset; + p->stride[plane_idx] = stride; + p->mod_hi[plane_idx] = modifier_hi; + p->mod_lo[plane_idx] = modifier_lo; + if (plane_idx + 1 > (uint32_t)p->n_planes) p->n_planes = plane_idx + 1; +} + +static struct wl_resource *dmabuf_make_buffer(struct wl_client *client, + struct wl_resource *params_res, + struct dmabuf_params *p, + int32_t width, int32_t height, + uint32_t format, uint32_t res_id, + bool immed) +{ + struct tabby_buffer *buf = calloc(1, sizeof(*buf)); + if (!buf) { + if (!immed) zwp_linux_buffer_params_v1_send_failed(params_res); + return NULL; + } + + buf->is_dmabuf = true; + buf->width = width; + buf->height = height; + buf->format = format; + buf->n_planes = p->n_planes; + wl_list_insert(&compositor.buffers, &buf->buffer_link); + for (int i = 0; i < p->n_planes && i < 4; i++) { + buf->fd[i] = p->fd[i]; + buf->dma_offset[i] = p->offset[i]; + buf->dma_stride[i] = p->stride[i]; + buf->dma_mod_hi[i] = p->mod_hi[i]; + buf->dma_mod_lo[i] = p->mod_lo[i]; + p->fd[i] = -1; /* ownership transferred to buffer */ + } + + struct wl_resource *buf_res = wl_resource_create(client, + &wl_buffer_interface, wl_resource_get_version(params_res), res_id); + if (!buf_res) { + for (int i = 0; i < buf->n_planes && i < 4; i++) + if (buf->fd[i] >= 0) close(buf->fd[i]); + wl_list_remove(&buf->buffer_link); + free(buf); + if (!immed) zwp_linux_buffer_params_v1_send_failed(params_res); + return NULL; + } + + buf->resource = buf_res; + wl_resource_set_user_data(buf_res, buf); + wl_resource_set_implementation(buf_res, &buffer_impl, buf, + buffer_destroy_resource); + if (!immed) + zwp_linux_buffer_params_v1_send_created(params_res, buf_res); + return buf_res; +} + +static void dmabuf_params_create(struct wl_client *client, struct wl_resource *resource, + int32_t width, int32_t height, + uint32_t format, uint32_t flags) +{ + (void)client; (void)flags; + struct dmabuf_params *p = wl_resource_get_user_data(resource); + if (!p) return; + if (p->used) { + wl_resource_post_error(resource, ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ALREADY_USED, + "params already used"); + return; + } + p->used = true; + + if (width <= 0 || height <= 0 || p->n_planes < 1) { + zwp_linux_buffer_params_v1_send_failed(resource); + return; + } + + dmabuf_make_buffer(client, resource, p, width, height, format, 0, false); +} + +static void dmabuf_params_create_immed(struct wl_client *client, + struct wl_resource *resource, + uint32_t buffer_id, + int32_t width, int32_t height, + uint32_t format, uint32_t flags) +{ + (void)flags; + struct dmabuf_params *p = wl_resource_get_user_data(resource); + if (!p) return; + if (p->used) { + wl_resource_post_error(resource, ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ALREADY_USED, + "params already used"); + return; + } + p->used = true; + + if (width <= 0 || height <= 0 || p->n_planes < 1) { + /* fatal errors are preferred; just make a failed buffer */ + wl_resource_post_error(resource, ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE, + "incomplete params"); + return; + } + + dmabuf_make_buffer(client, resource, p, width, height, format, buffer_id, true); +} + +static const struct zwp_linux_buffer_params_v1_interface dmabuf_params_impl = { + .destroy = dmabuf_params_destroy, + .add = dmabuf_params_add, + .create = dmabuf_params_create, + .create_immed = dmabuf_params_create_immed, +}; + +static void dmabuf_create_params(struct wl_client *client, struct wl_resource *resource, + uint32_t params_id) +{ + struct wl_resource *params = wl_resource_create(client, + &zwp_linux_buffer_params_v1_interface, wl_resource_get_version(resource), + params_id); + if (!params) { wl_client_post_no_memory(client); return; } + struct dmabuf_params *p = calloc(1, sizeof(*p)); + if (!p) { wl_resource_destroy(params); return; } + for (int i = 0; i < 4; i++) p->fd[i] = -1; + wl_resource_set_implementation(params, &dmabuf_params_impl, p, + dmabuf_params_resource_destroy); +} + +static const struct zwp_linux_dmabuf_v1_interface dmabuf_impl = { + .destroy = dmabuf_destroy, + .create_params = dmabuf_create_params, + .get_default_feedback = dmabuf_get_feedback, + .get_surface_feedback = dmabuf_get_surface_feedback, +}; + +static void dmabuf_feedback_destroy(struct wl_client *client, + struct wl_resource *resource) +{ + (void)client; + wl_resource_destroy(resource); +} + +static const struct zwp_linux_dmabuf_feedback_v1_interface feedback_impl = { + .destroy = dmabuf_feedback_destroy, +}; + +/* drm_fourcc + linear modifier table and device helpers */ +struct dmabuf_format { + uint32_t format; + uint32_t pad; + uint64_t modifier; +}; + +static bool build_format_table(int *out_fd, uint32_t *out_size) +{ + static const uint32_t fmts[] = { + WL_DRM_FORMAT_ARGB8888, + WL_DRM_FORMAT_XRGB8888, + WL_DRM_FORMAT_XRGB2101010, + WL_DRM_FORMAT_XBGR2101010, + }; + const size_t count = sizeof(fmts) / sizeof(fmts[0]); + struct dmabuf_format table[sizeof(fmts) / sizeof(fmts[0])] = {0}; + for (size_t i = 0; i < count; i++) { + table[i].format = fmts[i]; + table[i].modifier = 0; /* DRM_FORMAT_MOD_LINEAR */ + } + const size_t size = count * sizeof(struct dmabuf_format); + + int fd = memfd_create("linux-dmabuf-format-table", 0); + if (fd < 0) return false; + if (ftruncate(fd, (off_t)size) < 0) { close(fd); return false; } + unsigned char *map = mmap(NULL, size, PROT_READ | PROT_WRITE, + MAP_SHARED, fd, 0); + if (map == MAP_FAILED) { close(fd); return false; } + memcpy(map, table, size); + munmap(map, size); + + *out_fd = fd; + *out_size = (uint32_t)size; + return true; +} + +static void feedback_send_all(struct wl_resource *res) +{ + int fd = -1; + uint32_t size = 0; + if (!build_format_table(&fd, &size)) { + wl_resource_destroy(res); + return; + } + zwp_linux_dmabuf_feedback_v1_send_format_table(res, fd, size); + close(fd); + + struct stat st; + if (fstat(drm_fd, &st) == 0) { + struct wl_array device; + wl_array_init(&device); + dev_t dev = st.st_rdev; + dev_t *p = wl_array_add(&device, sizeof(dev)); + if (p) *p = dev; + zwp_linux_dmabuf_feedback_v1_send_main_device(res, &device); + zwp_linux_dmabuf_feedback_v1_send_tranche_target_device(res, &device); + wl_array_release(&device); + + zwp_linux_dmabuf_feedback_v1_send_tranche_flags(res, 0); + struct wl_array indices; + wl_array_init(&indices); + const uint16_t idx[] = { 0, 1, 2, 3 }; + uint16_t *q = wl_array_add(&indices, sizeof(idx)); + if (q) memcpy(q, idx, sizeof(idx)); + zwp_linux_dmabuf_feedback_v1_send_tranche_formats(res, &indices); + wl_array_release(&indices); + } + zwp_linux_dmabuf_feedback_v1_send_tranche_done(res); + zwp_linux_dmabuf_feedback_v1_send_done(res); +} + +static void dmabuf_create_feedback(struct wl_client *client, + struct wl_resource *resource, + uint32_t id) +{ + struct wl_resource *res = wl_resource_create(client, + &zwp_linux_dmabuf_feedback_v1_interface, wl_resource_get_version(resource), + id); + if (!res) { wl_client_post_no_memory(client); return; } + wl_resource_set_implementation(res, &feedback_impl, NULL, NULL); + feedback_send_all(res); +} + +static void dmabuf_get_feedback(struct wl_client *client, + struct wl_resource *resource, + uint32_t id) +{ + dmabuf_create_feedback(client, resource, id); +} + +static void dmabuf_get_surface_feedback(struct wl_client *client, + struct wl_resource *resource, + uint32_t id, + struct wl_resource *surface) +{ + (void)surface; + dmabuf_create_feedback(client, resource, id); +} + +static void bind_dmabuf(struct wl_client *client, void *data, + uint32_t version, uint32_t id) +{ + (void)data; + struct wl_resource *res = wl_resource_create(client, + &zwp_linux_dmabuf_v1_interface, version < 5 ? version : 5, id); + if (!res) { wl_client_post_no_memory(client); return; } + wl_resource_set_implementation(res, &dmabuf_impl, NULL, NULL); +} + +/* ===================== wl_drm (legacy) ===================== */ + +static void drm_authenticate(struct wl_client *client, struct wl_resource *resource, + uint32_t name) +{ + (void)client; (void)name; + /* render nodes require no authentication */ + wl_drm_send_authenticated(resource); +} + +static void drm_create_buffer(struct wl_client *client, struct wl_resource *resource, + uint32_t name, int32_t width, int32_t height, + uint32_t stride, uint32_t format, uint32_t id) +{ + (void)name; + /* Not used by clients when the PRIME capability is advertised; create a + * harmless empty buffer so the protocol object exists. */ + 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->width = width; + buf->height = height; + buf->format = format; + buf->is_dmabuf = false; + wl_list_insert(&compositor.buffers, &buf->buffer_link); + wl_resource_set_user_data(buf_res, buf); + wl_resource_set_implementation(buf_res, &buffer_impl, buf, buffer_destroy_resource); +} + +static void drm_create_planar_buffer(struct wl_client *client, struct wl_resource *resource, + uint32_t name, int32_t width, int32_t height, + uint32_t format, int32_t o0, int32_t s0, + int32_t o1, int32_t s1, int32_t o2, int32_t s2, + uint32_t id) +{ + (void)name; (void)o0; (void)o1; (void)s1; (void)o2; (void)s2; + drm_create_buffer(client, resource, name, width, height, s0, format, id); +} + +static void drm_create_prime_buffer(struct wl_client *client, struct wl_resource *resource, + uint32_t name, int32_t fd, int32_t width, int32_t height, + uint32_t format, int32_t o0, int32_t s0, + int32_t o1, int32_t s1, int32_t o2, int32_t s2, + uint32_t id) +{ + (void)name; + struct wl_resource *buf_res = wl_resource_create(client, + &wl_buffer_interface, wl_resource_get_version(resource), id); + if (!buf_res) { close(fd); wl_client_post_no_memory(client); return; } + + struct tabby_buffer *buf = calloc(1, sizeof(*buf)); + if (!buf) { close(fd); wl_resource_destroy(buf_res); return; } + + int nplanes = 1; + if (s1 > 0) nplanes = 2; + if (s2 > 0) nplanes = 3; + + buf->is_dmabuf = true; + buf->width = width; + buf->height = height; + buf->format = format; + buf->n_planes = nplanes; + buf->fd[0] = fd; + buf->dma_offset[0] = o0; + buf->dma_stride[0] = s0; + if (nplanes >= 2) { + buf->fd[1] = dup(fd); + buf->dma_offset[1] = o1; + buf->dma_stride[1] = s1; + } + if (nplanes >= 3) { + buf->fd[2] = dup(fd); + buf->dma_offset[2] = o2; + buf->dma_stride[2] = s2; + } + wl_list_insert(&compositor.buffers, &buf->buffer_link); + wl_resource_set_user_data(buf_res, buf); + wl_resource_set_implementation(buf_res, &buffer_impl, buf, buffer_destroy_resource); +} + +static const struct wl_drm_interface drm_impl = { + .authenticate = drm_authenticate, + .create_buffer = drm_create_buffer, + .create_planar_buffer = drm_create_planar_buffer, + .create_prime_buffer = drm_create_prime_buffer, +}; + +static void bind_drm(struct wl_client *client, void *data, + uint32_t version, uint32_t id) +{ + (void)data; + struct wl_resource *res = wl_resource_create(client, + &wl_drm_interface, version < 2 ? version : 2, id); + if (!res) { wl_client_post_no_memory(client); return; } + wl_resource_set_implementation(res, &drm_impl, NULL, NULL); + + wl_drm_send_device(res, drm_fd, "/dev/dri/renderD128"); + static const uint32_t formats[] = { + WL_DRM_FORMAT_ARGB8888, WL_DRM_FORMAT_XRGB8888, + WL_DRM_FORMAT_XRGB2101010, WL_DRM_FORMAT_XBGR2101010, + WL_DRM_FORMAT_ARGB2101010, WL_DRM_FORMAT_RGB565, + WL_DRM_FORMAT_RGB888, WL_DRM_FORMAT_AYUV, + }; + for (unsigned i = 0; i < sizeof(formats) / sizeof(formats[0]); i++) + wl_drm_send_format(res, formats[i]); + if (wl_resource_get_version(res) >= 2) + wl_drm_send_capabilities(res, WL_DRM_CAPABILITY_PRIME); +} + +/* ===================== xdg_wm_base ===================== */ + +static void srv_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 srv_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 srv_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 srv_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 = srv_xdg_wm_base_destroy, + .create_positioner = srv_xdg_wm_base_create_positioner, + .get_xdg_surface = srv_xdg_wm_base_get_xdg_surface, + .pong = srv_xdg_wm_base_pong, +}; + +/* ===================== xdg_surface ===================== */ + +static void srv_xdg_surface_destroy(struct wl_client *client, struct wl_resource *resource) { + (void)client; + wl_resource_destroy(resource); +} + +static void srv_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 = i; + break; + } + } + + if (!compositor.active_surface) + compositor.active_surface = surface; +} + +static void srv_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 srv_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 srv_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 = srv_xdg_surface_destroy, + .get_toplevel = srv_xdg_surface_get_toplevel, + .get_popup = srv_xdg_surface_get_popup, + .set_window_geometry = srv_xdg_surface_set_window_geometry, + .ack_configure = srv_xdg_surface_ack_configure, +}; + +/* ===================== xdg_toplevel ===================== */ + +static void srv_xdg_toplevel_destroy(struct wl_client *client, struct wl_resource *resource) { + (void)client; + wl_resource_destroy(resource); +} +static void srv_xdg_toplevel_set_parent(struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *parent) +{ (void)client; (void)resource; (void)parent; } + +static void srv_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 = title ? strdup(title) : NULL; +} + +static void srv_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 = app_id ? strdup(app_id) : NULL; +} + +static void srv_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 srv_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 srv_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 srv_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 srv_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 srv_xdg_toplevel_set_maximized(struct wl_client *client, + struct wl_resource *resource) +{ (void)client; (void)resource; } +static void srv_xdg_toplevel_unset_maximized(struct wl_client *client, + struct wl_resource *resource) +{ (void)client; (void)resource; } +static void srv_xdg_toplevel_set_fullscreen(struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *output) +{ (void)client; (void)resource; (void)output; } +static void srv_xdg_toplevel_unset_fullscreen(struct wl_client *client, + struct wl_resource *resource) +{ (void)client; (void)resource; } +static void srv_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 = srv_xdg_toplevel_destroy, + .set_parent = srv_xdg_toplevel_set_parent, + .set_title = srv_xdg_toplevel_set_title, + .set_app_id = srv_xdg_toplevel_set_app_id, + .show_window_menu = srv_xdg_toplevel_show_window_menu, + .move = srv_xdg_toplevel_move, + .resize = srv_xdg_toplevel_resize, + .set_max_size = srv_xdg_toplevel_set_max_size, + .set_min_size = srv_xdg_toplevel_set_min_size, + .set_maximized = srv_xdg_toplevel_set_maximized, + .unset_maximized = srv_xdg_toplevel_unset_maximized, + .set_fullscreen = srv_xdg_toplevel_set_fullscreen, + .unset_fullscreen = srv_xdg_toplevel_unset_fullscreen, + .set_minimized = srv_xdg_toplevel_set_minimized, +}; + +/* ===================== xdg_popup ===================== */ + +static void srv_xdg_popup_destroy(struct wl_client *client, struct wl_resource *resource) { + (void)client; + wl_resource_destroy(resource); +} +static void srv_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 srv_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 = srv_xdg_popup_destroy, + .grab = srv_xdg_popup_grab, + .reposition = srv_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", "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); +} + +/* ===================== Input tracking ===================== */ + +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); +} + +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; } +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); +} + +/* ===================== 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; + if (out->width == width && out->height == height) + return; + 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); + + struct wl_resource *res; + wl_list_for_each(res, &out->resources, link) + output_send_all(res); + + 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 schedule_redraw(void) { + needs_redraw = true; +} + +/* Resize the host (nested) window to fit active content, 1:1 (no scaling). */ +static void fit_host_window(void) { + struct tabby_surface *a = compositor.active_surface; + if (!a || !host.configured) return; + int bw = 0, bh = 0; + if (a->has_tex) { bw = a->tex_w; bh = a->tex_h; } + else if (a->pixels) { bw = a->pixel_width; bh = a->pixel_height; } + if (bw <= 0 || bh <= 0) return; + if (bw > MAX_WIN_W) bw = MAX_WIN_W; + if (bh > MAX_WIN_H) bh = MAX_WIN_H; + if (bw == host.width && bh == host.height) return; + host_resize(bw, bh); +} + +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); + schedule_redraw(); + fit_host_window(); + wl_display_flush_clients(compositor.display); +} + +static void cycle_tab(int dir) { + if (compositor.num_apps == 0) return; + int idx = -1; + for (int i = 0; i < compositor.num_apps; i++) + if (compositor.apps[i]->surface == compositor.active_surface) { + idx = i; break; + } + for (int k = 1; k <= compositor.num_apps; k++) { + int j = ((idx < 0 ? 0 : idx) + k * dir + compositor.num_apps) % compositor.num_apps; + if (compositor.apps[j]->surface) { + set_active_surface(compositor.apps[j]->surface); + return; + } + } +} + +/* ===================== Rendering ===================== */ + +static GLuint compile_shader(GLenum type, const char *src) { + GLuint sh = glCreateShader(type); + glShaderSource(sh, 1, &src, NULL); + glCompileShader(sh); + GLint ok; + glGetShaderiv(sh, GL_COMPILE_STATUS, &ok); + if (!ok) { + char log[512]; + glGetShaderInfoLog(sh, sizeof(log), NULL, log); + fprintf(stderr, "tabbed-windows: shader error: %s\n", log); + glDeleteShader(sh); + return 0; + } + return sh; +} + +static bool gl_setup(void) { + const char *vs = + "attribute vec2 a_pos;\n" + "attribute vec2 a_uv;\n" + "varying vec2 v_uv;\n" + "void main() {\n" + " v_uv = a_uv;\n" + " gl_Position = vec4(a_pos, 0.0, 1.0);\n" + "}\n"; + const char *fs = + "precision mediump float;\n" + "varying vec2 v_uv;\n" + "uniform sampler2D u_tex;\n" + "void main() {\n" + " gl_FragColor = texture2D(u_tex, v_uv);\n" + "}\n"; + GLuint vsh = compile_shader(GL_VERTEX_SHADER, vs); + GLuint fsh = compile_shader(GL_FRAGMENT_SHADER, fs); + if (!vsh || !fsh) return false; + host.gl_program = glCreateProgram(); + glAttachShader(host.gl_program, vsh); + glAttachShader(host.gl_program, fsh); + glBindAttribLocation(host.gl_program, 0, "a_pos"); + glBindAttribLocation(host.gl_program, 1, "a_uv"); + glLinkProgram(host.gl_program); + GLint ok; + glGetProgramiv(host.gl_program, GL_LINK_STATUS, &ok); + glDeleteShader(vsh); + glDeleteShader(fsh); + if (!ok) { + char log[512]; + glGetProgramInfoLog(host.gl_program, sizeof(log), NULL, log); + fprintf(stderr, "tabbed-windows: link error: %s\n", log); + return false; + } + host.gl_pos_loc = glGetAttribLocation(host.gl_program, "a_pos"); + host.gl_uv_loc = glGetAttribLocation(host.gl_program, "a_uv"); + host.gl_tex_loc = glGetUniformLocation(host.gl_program, "u_tex"); + return true; +} + +static void draw_texture_1to1(GLuint tex, int tex_w, int tex_h, + int src_x, int src_y, int src_w, int src_h, + int win_w, int win_h) +{ + if (win_w <= 0 || win_h <= 0) return; + if (src_w <= 0 || src_h <= 0) return; + if (src_x >= tex_w || src_y >= tex_h) return; + if (src_w > tex_w - src_x) src_w = tex_w - src_x; + if (src_h > tex_h - src_y) src_h = tex_h - src_y; + /* clip to window */ + int cw = src_w < win_w ? src_w : win_w; + int ch = src_h < win_h ? src_h : win_h; + if (cw <= 0 || ch <= 0) return; + + float u0 = (float)src_x / (float)tex_w; + float u1 = (float)(src_x + cw) / (float)tex_w; + /* v=0 is the first stored row = image top; window y=0 is top. */ + float v0 = (float)src_y / (float)tex_h; + float v1 = (float)(src_y + ch) / (float)tex_h; + + float nx0 = -1.0f; + float nx1 = -1.0f + 2.0f * (float)cw / (float)win_w; + float ny0 = 1.0f; /* top of window */ + float ny1 = 1.0f - 2.0f * (float)ch / (float)win_h; + + GLfloat verts[8] = { + nx0, ny0, /* top-left */ + nx1, ny0, /* top-right */ + nx1, ny1, /* bottom-right */ + nx0, ny1, /* bottom-left */ + }; + GLfloat uvs[8] = { + u0, v0, + u1, v0, + u1, v1, + u0, v1, + }; + GLushort idx[6] = { 0, 1, 2, 0, 2, 3 }; + + glUseProgram(host.gl_program); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, tex); + glUniform1i(host.gl_tex_loc, 0); + + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, verts); + glEnableVertexAttribArray(0); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, uvs); + glEnableVertexAttribArray(1); + + glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, idx); + + glDisableVertexAttribArray(0); + glDisableVertexAttribArray(1); +} + +static void render_egl(void) { + int w = host.width, h = host.height; + glViewport(0, 0, w, h); + glClearColor(0.13f, 0.13f, 0.13f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + + struct tabby_surface *a = compositor.active_surface; + if (a && (a->has_tex || a->pixels)) { + int bw = a->tex_w ? a->tex_w : a->pixel_width; + int bh = a->tex_h ? a->tex_h : a->pixel_height; + if (a->has_tex) { + int sx, sy, sw, sh; + content_rect(a, &sx, &sy, &sw, &sh); + draw_texture_1to1(a->tex, bw, bh, sx, sy, sw, sh, w, h); + } else { + /* shm: upload to texture then draw */ + if (!ensure_texture(a, a->pixel_width, a->pixel_height)) + goto present; + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, + a->pixel_width, a->pixel_height, 0, + GL_BGRA_EXT, GL_UNSIGNED_BYTE, a->pixels); + int sx, sy, sw, sh; + content_rect(a, &sx, &sy, &sw, &sh); + draw_texture_1to1(a->tex, bw, bh, sx, sy, sw, sh, w, h); + } + } +present: + eglSwapBuffers(host.egl_display, host.egl_surface); + static int dframe; + if ((dframe++ % 90) == 0) { + const char *cmd = "(none)"; + for (int i = 0; i < compositor.num_apps; i++) + if (compositor.apps[i] && compositor.apps[i]->surface == a) + cmd = compositor.apps[i]->cmd; + fprintf(stderr, "[render] frame=%d active=%s size=%dx%d\n", + dframe, cmd, w, h); + } +} + +static void present_shm_fallback(void) { + struct tabby_output *out = &compositor.output; + int w = host.width, h = host.height; + if (w <= 0 || h <= 0 || !host.shm) return; + size_t size = (size_t)w * h * 4; + if (size != host.shm_size) { + if (host.shm_pool) wl_shm_pool_destroy(host.shm_pool); + if (host.shm_buffer) wl_buffer_destroy(host.shm_buffer); + if (host.shm_data) munmap(host.shm_data, host.shm_size); + int fd = memfd_create("tabby-host", MFD_CLOEXEC); + if (fd < 0) return; + if (ftruncate(fd, size) < 0) { close(fd); return; } + void *data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (data == MAP_FAILED) { close(fd); return; } + host.shm_pool = wl_shm_create_pool(host.shm, fd, size); + host.shm_buffer = wl_shm_pool_create_buffer(host.shm_pool, 0, + w, h, w * 4, WL_SHM_FORMAT_XRGB8888); + close(fd); + host.shm_data = data; + host.shm_size = size; + } + /* composite CPU pixels into output->data */ + memset(out->data, 0x22, (size_t)h * out->stride); + struct tabby_surface *a = compositor.active_surface; + if (a && a->pixels) { + int sx, sy, sw, sh; + content_rect(a, &sx, &sy, &sw, &sh); + int cw = sw < w ? sw : w; + int ch = sh < h ? sh : h; + for (int y = 0; y < ch; y++) { + memcpy(out->data + y * out->stride, + a->pixels + (sy + y) * a->pixel_stride + sx * 4, cw * 4); + } + } + memcpy(host.shm_data, out->data, size); + wl_surface_attach(host.surface, host.shm_buffer, 0, 0); + wl_surface_damage(host.surface, 0, 0, w, h); + wl_surface_commit(host.surface); +} + +static void render_and_present(void) { + if (!host.configured) return; + if (host.egl_ok) + render_egl(); + else + present_shm_fallback(); + wl_display_flush(host.display); +} + +/* ===================== Host resize ===================== */ + +static void host_resize(int width, int height) { + if (width < 50) width = 50; + if (height < 50) height = 50; + host.width = width; + host.height = height; + if (host.egl_window) + wl_egl_window_resize(host.egl_window, width, height, 0, 0); + if (host.egl_ok) { + /* inform the context of the new window size */ + eglMakeCurrent(host.egl_display, host.egl_surface, host.egl_surface, + host.egl_context); + } + output_resize(width, height); + schedule_redraw(); +} + +/* ===================== Host-side listeners ===================== */ + +static void host_registry_global(void *data, struct wl_registry *registry, + uint32_t name, const char *interface, + uint32_t version) +{ + (void)data; + if (strcmp(interface, wl_compositor_interface.name) == 0) { + host.compositor = wl_registry_bind(registry, name, + &wl_compositor_interface, version < 4 ? version : 4); + } else if (strcmp(interface, xdg_wm_base_interface.name) == 0) { + host.wm_base = wl_registry_bind(registry, name, + &xdg_wm_base_interface, version < 1 ? version : 1); + } else if (strcmp(interface, wl_shm_interface.name) == 0) { + host.shm = wl_registry_bind(registry, name, + &wl_shm_interface, version < 1 ? version : 1); + } else if (strcmp(interface, wl_seat_interface.name) == 0) { + host.seat = wl_registry_bind(registry, name, + &wl_seat_interface, version < 5 ? version : 5); + } +} + +static void host_registry_global_remove(void *data, struct wl_registry *registry, + uint32_t name) +{ + (void)data; (void)registry; (void)name; +} + +static const struct wl_registry_listener host_registry_listener = { + .global = host_registry_global, + .global_remove = host_registry_global_remove, +}; + +static void host_xdg_wm_ping(void *data, struct xdg_wm_base *wm, uint32_t serial) { + (void)data; + xdg_wm_base_pong(wm, serial); +} + +static const struct xdg_wm_base_listener host_xdg_wm_listener = { + .ping = host_xdg_wm_ping, +}; + +static void host_xdg_surface_configure(void *data, struct xdg_surface *s, + uint32_t serial) +{ + (void)data; + xdg_surface_ack_configure(s, serial); +} + +static const struct xdg_surface_listener host_xdg_surface_listener = { + .configure = host_xdg_surface_configure, +}; + +static void host_xdg_toplevel_configure(void *data, struct xdg_toplevel *t, + int32_t width, int32_t height, + struct wl_array *states) +{ + (void)data; (void)t; (void)states; + if (width > 0 && height > 0) + host_resize(width, height); + host.configured = true; +} + +static void host_xdg_toplevel_close(void *data, struct xdg_toplevel *t) { + (void)data; (void)t; + fprintf(stderr, "tabbed-windows: window closed\n"); + exit(0); +} + +static const struct xdg_toplevel_listener host_xdg_toplevel_listener = { + .configure = host_xdg_toplevel_configure, + .close = host_xdg_toplevel_close, +}; + +/* ---------- host keyboard ---------- */ + +static void host_kbd_keymap(void *data, struct wl_keyboard *kbd, uint32_t format, + int32_t fd, uint32_t size) +{ + (void)data; (void)kbd; (void)format; (void)fd; (void)size; +} + +static void host_kbd_enter(void *data, struct wl_keyboard *kbd, uint32_t serial, + struct wl_surface *surface, struct wl_array *keys) +{ + (void)data; (void)kbd; (void)serial; (void)surface; (void)keys; + if (compositor.active_surface && !compositor.kbd_focus) + send_kbd_enter(compositor.active_surface); +} + +static void host_kbd_leave(void *data, struct wl_keyboard *kbd, uint32_t serial, + struct wl_surface *surface) +{ + (void)data; (void)kbd; (void)serial; (void)surface; +} + +static void host_kbd_key(void *data, struct wl_keyboard *kbd, uint32_t serial, + uint32_t time, uint32_t key, uint32_t state) +{ + (void)data; (void)kbd; (void)serial; + /* Ctrl+PageUp / Ctrl+PageDown cycle tabs */ + bool ctrl = (host.host_mods_depressed & (1u << 2)) != 0; + if (state == WL_KEYBOARD_KEY_STATE_PRESSED && ctrl && + (key == 104 || key == 109)) { + cycle_tab(key == 109 ? 1 : -1); + return; + } + + struct tabby_surface *a = compositor.active_surface; + if (!a) return; + if (compositor.kbd_focus != a) + send_kbd_enter(a); + struct wl_resource *kbd_res = kbd_for_client(a->client); + if (!kbd_res) return; + compositor.input_serial++; + wl_keyboard_send_key(kbd_res, compositor.input_serial, time, key, state); + wl_display_flush_clients(compositor.display); +} + +static void host_kbd_modifiers(void *data, struct wl_keyboard *kbd, uint32_t serial, + uint32_t mods_depressed, uint32_t mods_latched, + uint32_t mods_locked, uint32_t group) +{ + (void)data; (void)kbd; (void)serial; + host.host_mods_depressed = mods_depressed; + struct tabby_surface *a = compositor.active_surface; + if (!a) return; + struct wl_resource *kbd_res = kbd_for_client(a->client); + if (!kbd_res) return; + compositor.input_serial++; + wl_keyboard_send_modifiers(kbd_res, compositor.input_serial, + mods_depressed, mods_latched, mods_locked, group); + wl_display_flush_clients(compositor.display); +} + +static void host_kbd_repeat_info(void *data, struct wl_keyboard *kbd, + int32_t rate, int32_t delay) +{ + (void)data; (void)kbd; (void)rate; (void)delay; +} + +static const struct wl_keyboard_listener host_kbd_listener = { + .keymap = host_kbd_keymap, + .enter = host_kbd_enter, + .leave = host_kbd_leave, + .key = host_kbd_key, + .modifiers = host_kbd_modifiers, + .repeat_info = host_kbd_repeat_info, +}; + +/* ---------- host pointer ---------- */ + +static void host_ptr_enter(void *data, struct wl_pointer *p, uint32_t serial, + struct wl_surface *surface, wl_fixed_t sx, wl_fixed_t sy) +{ + (void)data; (void)p; (void)serial; (void)surface; + struct tabby_surface *a = compositor.active_surface; + if (!a) return; + struct wl_resource *ptr = ptr_for_client(a->client); + if (!ptr) return; + compositor.input_serial++; + wl_pointer_send_enter(ptr, compositor.input_serial, a->resource, sx, sy); + compositor.pointer_entered = true; + compositor.pointer_x = wl_fixed_to_double(sx); + compositor.pointer_y = wl_fixed_to_double(sy); + 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); +} + +static void host_ptr_leave(void *data, struct wl_pointer *p, uint32_t serial, + struct wl_surface *surface) +{ + (void)data; (void)p; (void)serial; (void)surface; + struct tabby_surface *a = compositor.active_surface; + if (!a) return; + struct wl_resource *ptr = ptr_for_client(a->client); + if (!ptr) return; + compositor.input_serial++; + wl_pointer_send_leave(ptr, compositor.input_serial, a->resource); + compositor.pointer_entered = false; + 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); +} + +static void host_ptr_motion(void *data, struct wl_pointer *p, uint32_t time, + wl_fixed_t sx, wl_fixed_t sy) +{ + (void)data; (void)p; + struct tabby_surface *a = compositor.active_surface; + if (!a) return; + struct wl_resource *ptr = ptr_for_client(a->client); + if (!ptr) return; + wl_pointer_send_motion(ptr, time, sx, sy); + compositor.pointer_x = wl_fixed_to_double(sx); + compositor.pointer_y = wl_fixed_to_double(sy); + 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); +} + +static void host_ptr_button(void *data, struct wl_pointer *p, uint32_t serial, + uint32_t time, uint32_t button, uint32_t state) +{ + (void)data; (void)p; (void)serial; + struct tabby_surface *a = compositor.active_surface; + if (!a) return; + struct wl_resource *ptr = ptr_for_client(a->client); + if (!ptr) return; + compositor.input_serial++; + wl_pointer_send_button(ptr, compositor.input_serial, time, button, state); + 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); +} + +static void host_ptr_axis(void *data, struct wl_pointer *p, uint32_t time, + uint32_t axis, wl_fixed_t value) +{ + (void)data; (void)p; + struct tabby_surface *a = compositor.active_surface; + if (!a) return; + struct wl_resource *ptr = ptr_for_client(a->client); + if (!ptr) return; + wl_pointer_send_axis(ptr, time, axis, value); + 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); +} + +static void host_ptr_noop_frame(void *data, struct wl_pointer *p) { + (void)data; (void)p; +} + +static void host_ptr_noop_axis_source(void *data, struct wl_pointer *p, + uint32_t axis_source) { + (void)data; (void)p; (void)axis_source; +} + +static void host_ptr_noop_axis_stop(void *data, struct wl_pointer *p, + uint32_t time, uint32_t axis) { + (void)data; (void)p; (void)time; (void)axis; +} + +static void host_ptr_noop_axis_discrete(void *data, struct wl_pointer *p, + uint32_t axis, int32_t discrete) { + (void)data; (void)p; (void)axis; (void)discrete; +} + +static const struct wl_pointer_listener host_ptr_listener = { + .enter = host_ptr_enter, + .leave = host_ptr_leave, + .motion = host_ptr_motion, + .button = host_ptr_button, + .axis = host_ptr_axis, + .frame = host_ptr_noop_frame, + .axis_source = host_ptr_noop_axis_source, + .axis_stop = host_ptr_noop_axis_stop, + .axis_discrete = host_ptr_noop_axis_discrete, +}; + +/* ===================== Host setup ===================== */ + +static bool host_init(void) { + const char *disp = getenv("WAYLAND_DISPLAY"); + if (!disp) + disp = "wayland-0"; + host.display = wl_display_connect(NULL); + if (!host.display) { + fprintf(stderr, "tabbed-windows: cannot connect to host compositor " + "(WAYLAND_DISPLAY=%s). Run me inside a running Wayland session.\n", + disp); + return false; + } + + host.registry = wl_display_get_registry(host.display); + wl_registry_add_listener(host.registry, &host_registry_listener, NULL); + wl_display_roundtrip(host.display); + + if (!host.compositor || !host.wm_base) { + fprintf(stderr, "tabbed-windows: host lacks compositor/xdg_wm_base\n"); + return false; + } + + host.surface = wl_compositor_create_surface(host.compositor); + host.xdg_surface = xdg_wm_base_get_xdg_surface(host.wm_base, host.surface); + host.toplevel = xdg_surface_get_toplevel(host.xdg_surface); + xdg_toplevel_set_title(host.toplevel, "tabbed-windows"); + xdg_surface_add_listener(host.xdg_surface, &host_xdg_surface_listener, NULL); + xdg_toplevel_add_listener(host.toplevel, &host_xdg_toplevel_listener, NULL); + xdg_wm_base_add_listener(host.wm_base, &host_xdg_wm_listener, NULL); + wl_surface_commit(host.surface); + wl_display_roundtrip(host.display); + + if (host.seat) { + host.keyboard = wl_seat_get_keyboard(host.seat); + host.pointer = wl_seat_get_pointer(host.seat); + if (host.keyboard) + wl_keyboard_add_listener(host.keyboard, &host_kbd_listener, NULL); + if (host.pointer) + wl_pointer_add_listener(host.pointer, &host_ptr_listener, NULL); + } + + /* Try EGL on the host wayland platform */ + pfn_eglGetPlatformDisplayEXT = (PFNEGLGETPLATFORMDISPLAYEXTPROC) + eglGetProcAddress("eglGetPlatformDisplayEXT"); + if (!pfn_eglGetPlatformDisplayEXT) { + fprintf(stderr, "tabbed-windows: no EGL platform support; " + "falling back to software rendering\n"); + host.egl_ok = false; + return true; + } + + host.egl_display = pfn_eglGetPlatformDisplayEXT(EGL_PLATFORM_WAYLAND_EXT, + host.display, NULL); + if (host.egl_display == EGL_NO_DISPLAY) { + fprintf(stderr, "tabbed-windows: EGL display failed; " + "falling back to software rendering\n"); + host.egl_ok = false; + return true; + } + if (!eglInitialize(host.egl_display, NULL, NULL)) { + fprintf(stderr, "tabbed-windows: eglInitialize failed; " + "falling back to software rendering\n"); + host.egl_ok = false; + return true; + } + + eglBindAPI(EGL_OPENGL_ES_API); + + const EGLint config_attribs[] = { + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 0, + EGL_NONE + }; + EGLint ncfg = 0; + if (!eglChooseConfig(host.egl_display, config_attribs, &host.egl_config, + 1, &ncfg) || ncfg < 1) { + fprintf(stderr, "tabbed-windows: no EGL config; software fallback\n"); + host.egl_ok = false; + return true; + } + + const EGLint ctx_attribs[] = { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE + }; + host.egl_context = eglCreateContext(host.egl_display, host.egl_config, + EGL_NO_CONTEXT, ctx_attribs); + if (host.egl_context == EGL_NO_CONTEXT) { + fprintf(stderr, "tabbed-windows: EGL context failed; software fallback\n"); + host.egl_ok = false; + return true; + } + + host.width = 1280; + host.height = 800; + host.egl_window = wl_egl_window_create(host.surface, host.width, host.height); + host.egl_surface = eglCreateWindowSurface(host.egl_display, host.egl_config, + (EGLNativeWindowType)host.egl_window, NULL); + if (host.egl_surface == EGL_NO_SURFACE) { + fprintf(stderr, "tabbed-windows: EGL window surface failed; software fallback\n"); + host.egl_ok = false; + return true; + } + + if (!eglMakeCurrent(host.egl_display, host.egl_surface, host.egl_surface, + host.egl_context)) { + fprintf(stderr, "tabbed-windows: eglMakeCurrent failed; software fallback\n"); + host.egl_ok = false; + return true; + } + eglSwapInterval(host.egl_display, 0); + + pfn_eglCreateImageKHR = (PFNEGLCREATEIMAGEKHRPROC)egl_proc("eglCreateImageKHR"); + pfn_eglDestroyImageKHR = (PFNEGLDESTROYIMAGEKHRPROC)egl_proc("eglDestroyImageKHR"); + pfn_glEGLImageTargetTexture2DOES = (PFNGLEGLIMAGETARGETTEXTURE2DOESPROC) + egl_proc("glEGLImageTargetTexture2DOES"); + + if (!gl_setup()) { + fprintf(stderr, "tabbed-windows: GL setup failed; software fallback\n"); + host.egl_ok = false; + return true; + } + + host.egl_ok = true; + fprintf(stderr, "tabbed-windows: EGL (GPU) rendering enabled\n"); + return true; +} + +/* ===================== Launch client ===================== */ + +static void launch_client(int idx) { + pid_t pid = fork(); + if (pid == 0) { + const char *xdg_runtime = getenv("XDG_RUNTIME_DIR"); + if (!xdg_runtime) xdg_runtime = "/tmp"; + setenv("WAYLAND_DISPLAY", SOCKET_NAME, 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", compositor.apps[idx]->cmd, NULL); + _exit(127); + } + if (pid > 0) + compositor.apps[idx]->pid = pid; +} + +/* ===================== 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); +} + +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)); + memset(&host, 0, sizeof(host)); + + 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); + + /* Initialize server display first so host configure events during + host_init() can safely resize the compositor output. */ + compositor.display = wl_display_create(); + if (!compositor.display) { + fprintf(stderr, "Failed to create Wayland display\n"); + return 1; + } + drm_fd = open("/dev/dri/renderD128", O_RDWR); + if (drm_fd < 0) + fprintf(stderr, "tabbed-windows: warning: cannot open render node\n"); + compositor.loop = wl_display_get_event_loop(compositor.display); + compositor.xkb_keymap_fd = -1; + wl_list_init(&compositor.surfaces); + wl_list_init(&compositor.buffers); + wl_list_init(&compositor.input_tracks); + wl_list_init(&compositor.output.resources); + + /* Generate xkb keymap for our clients */ + 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_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); + } + } + } + } + + /* Initial output size defaults; host configure will resize it. */ + compositor.output.width = 1280; + compositor.output.height = 800; + compositor.output.stride = 1280 * 4; + compositor.output.data = calloc(1, (size_t)1280 * 800 * 4); + if (compositor.output.data) + memset(compositor.output.data, 0x22, (size_t)1280 * 800 * 4); + compositor.output.global = wl_global_create(compositor.display, + &wl_output_interface, 2, &compositor.output, output_bind); + + 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, &zwp_linux_dmabuf_v1_interface, 5, + NULL, bind_dmabuf); + wl_global_create(compositor.display, &wl_drm_interface, 2, NULL, bind_drm); + 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); + + if (wl_display_add_socket(compositor.display, SOCKET_NAME) < 0) { + fprintf(stderr, "Failed to add socket '%s': %s\n", SOCKET_NAME, + strerror(errno)); + return 1; + } + + /* Now connect to the host compositor and present inside it. */ + if (!host_init()) { + fprintf(stderr, "tabbed-windows: could not start nested session\n"); + return 1; + } + + printf("Wayland socket: %s\n", SOCKET_NAME); + + /* Programs to launch come from command-line arguments */ + int total_apps = argc - 1; + if (total_apps == 0) { + fprintf(stderr, "usage: %s [prog...]\n", argv[0]); + return 1; + } + if (total_apps > MAX_APPS) total_apps = MAX_APPS; + + for (int i = 0; i < total_apps; i++) { + struct tabby_app *app = calloc(1, sizeof(*app)); + app->cmd = strdup(argv[i + 1]); + app->pid = -1; + app->surface = NULL; + compositor.apps[i] = app; + } + compositor.num_apps = total_apps; + + for (int i = 0; i < compositor.num_apps; i++) + launch_client(i); + compositor.clients_launched = true; + + /* Main event loop: service both the server and the host displays */ + int server_fd = wl_event_loop_get_fd(compositor.loop); + int host_fd = wl_display_get_fd(host.display); + + fprintf(stderr, "tabbed-windows: running. Ctrl+PageUp/PageDown switches tabs.\n"); + + for (;;) { + wl_display_dispatch_pending(host.display); + + struct pollfd fds[2] = { + { .fd = server_fd, .events = POLLIN }, + { .fd = host_fd, .events = POLLIN }, + }; + int timeout = needs_redraw ? 0 : -1; + if (poll(fds, 2, timeout) < 0) { + if (errno == EINTR) continue; + break; + } + + if (fds[0].revents & (POLLIN | POLLERR)) + wl_event_loop_dispatch(compositor.loop, 0); + wl_display_flush_clients(compositor.display); + + if (fds[1].revents & (POLLIN | POLLERR)) + wl_display_dispatch(host.display); + + if (needs_redraw) { + needs_redraw = false; + /* first visible frame may arrive before host configure */ + if (host.configured) + render_and_present(); + } + wl_display_flush(host.display); + } + + 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_keymap) xkb_keymap_unref(compositor.xkb_keymap); + if (compositor.xkb_ctx) xkb_context_unref(compositor.xkb_ctx); + return 0; +} diff --git a/wl-drm-protocol.c b/wl-drm-protocol.c new file mode 100644 index 0000000..76eca96 --- /dev/null +++ b/wl-drm-protocol.c @@ -0,0 +1,100 @@ +/* Generated by wayland-scanner 1.24.0 */ + +/* + * Copyright © 2008-2011 Kristian Høgsberg + * Copyright © 2010-2011 Intel Corporation + * Copyright © 2012-2013 Collabora, Ltd. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#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_buffer_interface; + +static const struct wl_interface *wl_drm_types[] = { + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + &wl_buffer_interface, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + &wl_buffer_interface, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + &wl_buffer_interface, +}; + +static const struct wl_message wl_drm_requests[] = { + { "authenticate", "u", wl_drm_types + 0 }, + { "create_buffer", "uiiuun", wl_drm_types + 2 }, + { "create_planar_buffer", "uiiuiiiiiin", wl_drm_types + 8 }, + { "create_prime_buffer", "uhiiuiiiiiin", wl_drm_types + 19 }, +}; + +static const struct wl_message wl_drm_events[] = { + { "device", "hs", wl_drm_types + 0 }, + { "format", "u", wl_drm_types + 0 }, + { "authenticated", "", wl_drm_types + 0 }, + { "capabilities", "u", wl_drm_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface wl_drm_interface = { + "wl_drm", 2, + 4, wl_drm_requests, + 4, wl_drm_events, +}; + diff --git a/wl-drm-server.h b/wl-drm-server.h new file mode 100644 index 0000000..846ba80 --- /dev/null +++ b/wl-drm-server.h @@ -0,0 +1,434 @@ +/* Generated by wayland-scanner 1.24.0 */ + +#ifndef WL_DRM_SERVER_PROTOCOL_H +#define WL_DRM_SERVER_PROTOCOL_H + +#include +#include +#include "wayland-server.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct wl_client; +struct wl_resource; + +/** + * @page page_wl_drm The wl_drm protocol + * @section page_ifaces_wl_drm Interfaces + * - @subpage page_iface_wl_drm - Protocol for using DRM buffers + * @section page_copyright_wl_drm Copyright + *
+ *
+ * Copyright © 2008-2011 Kristian Høgsberg
+ * Copyright © 2010-2011 Intel Corporation
+ * Copyright © 2012-2013 Collabora, Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ * 
+ */ +struct wl_buffer; +struct wl_drm; + +#ifndef WL_DRM_INTERFACE +#define WL_DRM_INTERFACE +/** + * @page page_iface_wl_drm wl_drm + * @section page_iface_wl_drm_desc Description + * + * This protocol is used to create buffers that are backed by DRM + * buffers. + * + * This protocol is deprecated and not intended for new clients. + * @section page_iface_wl_drm_api API + * See @ref iface_wl_drm. + */ +/** + * @defgroup iface_wl_drm The wl_drm interface + * + * This protocol is used to create buffers that are backed by DRM + * buffers. + * + * This protocol is deprecated and not intended for new clients. + */ +extern const struct wl_interface wl_drm_interface; +#endif + +#ifndef WL_DRM_ERROR_ENUM +#define WL_DRM_ERROR_ENUM +/** + * @ingroup iface_wl_drm + * wl_drm error values + * + * These errors can be emitted in response to wl_drm requests. + */ +enum wl_drm_error { + WL_DRM_ERROR_AUTHENTICATE_FAIL = 0, + WL_DRM_ERROR_INVALID_FORMAT = 1, + WL_DRM_ERROR_INVALID_NAME = 2, +}; +#endif /* WL_DRM_ERROR_ENUM */ + +#ifndef WL_DRM_ERROR_ENUM_IS_VALID +#define WL_DRM_ERROR_ENUM_IS_VALID +/** + * @ingroup iface_wl_drm + * Validate a wl_drm error value. + * + * @return true on success, false on error. + * @ref wl_drm_error + */ +static inline bool +wl_drm_error_is_valid(uint32_t value, uint32_t version) { + switch (value) { + case WL_DRM_ERROR_AUTHENTICATE_FAIL: + return version >= 1; + case WL_DRM_ERROR_INVALID_FORMAT: + return version >= 1; + case WL_DRM_ERROR_INVALID_NAME: + return version >= 1; + default: + return false; + } +} +#endif /* WL_DRM_ERROR_ENUM_IS_VALID */ + +#ifndef WL_DRM_FORMAT_ENUM +#define WL_DRM_FORMAT_ENUM +/** + * @ingroup iface_wl_drm + * DRM format values + * + * DRM formats are defined by the DRM_FORMAT_* macros in + * drm_fourcc.h. A list of formats is also available at: + * https://dri.freedesktop.org/docs/drm/graphics/drm-fourcc.html + */ +enum wl_drm_format { + WL_DRM_FORMAT_C8 = 0x20203843, + WL_DRM_FORMAT_RGB332 = 0x38424752, + WL_DRM_FORMAT_BGR233 = 0x38524742, + WL_DRM_FORMAT_XRGB2101010 = 0x30335258, + WL_DRM_FORMAT_XBGR2101010 = 0x30334258, + WL_DRM_FORMAT_RGB565 = 0x36314752, + WL_DRM_FORMAT_BGR565 = 0x36314742, + WL_DRM_FORMAT_ABGR2101010 = 0x30334241, + WL_DRM_FORMAT_ARGB2101010 = 0x30335241, + WL_DRM_FORMAT_YUYV = 0x56595559, + WL_DRM_FORMAT_ARGB8888 = 0x34325241, + WL_DRM_FORMAT_XRGB8888 = 0x34325258, + WL_DRM_FORMAT_RGB888 = 0x34324752, + WL_DRM_FORMAT_BGR888 = 0x34324742, + WL_DRM_FORMAT_YV12 = 0x32315659, + WL_DRM_FORMAT_NV12 = 0x3231564e, + WL_DRM_FORMAT_NV21 = 0x3132564e, + WL_DRM_FORMAT_NV16 = 0x3631564e, + WL_DRM_FORMAT_NV61 = 0x3136564e, + WL_DRM_FORMAT_YU12 = 0x32315559, + WL_DRM_FORMAT_AYUV = 0x56555941, + WL_DRM_FORMAT_ARGB4444 = 0x32315241, + WL_DRM_FORMAT_RGBA4444 = 0x32315247, + WL_DRM_FORMAT_ABGR4444 = 0x32314241, + WL_DRM_FORMAT_XBGR4444 = 0x32314258, + WL_DRM_FORMAT_BGRA4444 = 0x32314247, + WL_DRM_FORMAT_ARGB1555 = 0x35315241, + WL_DRM_FORMAT_RGBA5551 = 0x35315547, + WL_DRM_FORMAT_ABGR1555 = 0x35314241, + WL_DRM_FORMAT_BGRA5551 = 0x35314247, + WL_DRM_FORMAT_ARGB8565 = 0x36355241, + WL_DRM_FORMAT_BGRA8565 = 0x36355242, +}; +#endif /* WL_DRM_FORMAT_ENUM */ + +#ifndef WL_DRM_FORMAT_ENUM_IS_VALID +#define WL_DRM_FORMAT_ENUM_IS_VALID +/** + * @ingroup iface_wl_drm + * Validate a wl_drm format value. + * + * @return true on success, false on error. + * @ref wl_drm_format + */ +static inline bool +wl_drm_format_is_valid(uint32_t value, uint32_t version) { + switch (value) { + case WL_DRM_FORMAT_C8: + return version >= 1; + case WL_DRM_FORMAT_RGB332: + return version >= 1; + case WL_DRM_FORMAT_BGR233: + return version >= 1; + case WL_DRM_FORMAT_XRGB2101010: + return version >= 1; + case WL_DRM_FORMAT_XBGR2101010: + return version >= 1; + case WL_DRM_FORMAT_RGB565: + return version >= 1; + case WL_DRM_FORMAT_BGR565: + return version >= 1; + case WL_DRM_FORMAT_ABGR2101010: + return version >= 1; + case WL_DRM_FORMAT_ARGB2101010: + return version >= 1; + case WL_DRM_FORMAT_YUYV: + return version >= 1; + case WL_DRM_FORMAT_ARGB8888: + return version >= 1; + case WL_DRM_FORMAT_XRGB8888: + return version >= 1; + case WL_DRM_FORMAT_RGB888: + return version >= 1; + case WL_DRM_FORMAT_BGR888: + return version >= 1; + case WL_DRM_FORMAT_YV12: + return version >= 1; + case WL_DRM_FORMAT_NV12: + return version >= 1; + case WL_DRM_FORMAT_NV21: + return version >= 1; + case WL_DRM_FORMAT_NV16: + return version >= 1; + case WL_DRM_FORMAT_NV61: + return version >= 1; + case WL_DRM_FORMAT_YU12: + return version >= 1; + case WL_DRM_FORMAT_AYUV: + return version >= 1; + case WL_DRM_FORMAT_ARGB4444: + return version >= 1; + case WL_DRM_FORMAT_RGBA4444: + return version >= 1; + case WL_DRM_FORMAT_ABGR4444: + return version >= 1; + case WL_DRM_FORMAT_XBGR4444: + return version >= 1; + case WL_DRM_FORMAT_BGRA4444: + return version >= 1; + case WL_DRM_FORMAT_ARGB1555: + return version >= 1; + case WL_DRM_FORMAT_RGBA5551: + return version >= 1; + case WL_DRM_FORMAT_ABGR1555: + return version >= 1; + case WL_DRM_FORMAT_BGRA5551: + return version >= 1; + case WL_DRM_FORMAT_ARGB8565: + return version >= 1; + case WL_DRM_FORMAT_BGRA8565: + return version >= 1; + default: + return false; + } +} +#endif /* WL_DRM_FORMAT_ENUM_IS_VALID */ + +#ifndef WL_DRM_CAPABILITY_ENUM +#define WL_DRM_CAPABILITY_ENUM +/** + * @ingroup iface_wl_drm + * wl_drm capability bitmask + * + * Bitmask of capabilities. + */ +enum wl_drm_capability { + WL_DRM_CAPABILITY_PRIME = 1, +}; +#endif /* WL_DRM_CAPABILITY_ENUM */ + +#ifndef WL_DRM_CAPABILITY_ENUM_IS_VALID +#define WL_DRM_CAPABILITY_ENUM_IS_VALID +/** + * @ingroup iface_wl_drm + * Validate a wl_drm capability value. + * + * @return true on success, false on error. + * @ref wl_drm_capability + */ +static inline bool +wl_drm_capability_is_valid(uint32_t value, uint32_t version) { + switch (value) { + case WL_DRM_CAPABILITY_PRIME: + return version >= 1; + default: + return false; + } +} +#endif /* WL_DRM_CAPABILITY_ENUM_IS_VALID */ + +/** + * @ingroup iface_wl_drm + * @struct wl_drm_interface + */ +struct wl_drm_interface { + /** + * Authenticate the DRM device + * + * This request authenticates the DRM device. The device name is + * sent with the device event. + */ + void (*authenticate)(struct wl_client *client, + struct wl_resource *resource, + uint32_t name); + /** + * Create a drm buffer object + * + * This request creates a wl_buffer object backed by a DRM + * buffer. The DRM buffer is created by the server. + */ + void (*create_buffer)(struct wl_client *client, + struct wl_resource *resource, + uint32_t name, + int32_t width, + int32_t height, + uint32_t stride, + uint32_t format, + uint32_t id); + /** + * Create a planar drm buffer object + * + * This request creates a wl_buffer object backed by a DRM + * buffer. The DRM buffer is created by the server. The buffer has + * multiple planes. + */ + void (*create_planar_buffer)(struct wl_client *client, + struct wl_resource *resource, + uint32_t name, + int32_t width, + int32_t height, + uint32_t format, + int32_t offset0, + int32_t stride0, + int32_t offset1, + int32_t stride1, + int32_t offset2, + int32_t stride2, + uint32_t id); + /** + * Create a buffer from a DMA-BUF file descriptor + * + * This request creates a wl_buffer object backed by a DMA-BUF + * file descriptor. The buffer can have one, two or three planes. + */ + void (*create_prime_buffer)(struct wl_client *client, + struct wl_resource *resource, + uint32_t name, + int32_t fd, + int32_t width, + int32_t height, + uint32_t format, + int32_t offset0, + int32_t stride0, + int32_t offset1, + int32_t stride1, + int32_t offset2, + int32_t stride2, + uint32_t id); +}; + +#define WL_DRM_DEVICE 0 +#define WL_DRM_FORMAT 1 +#define WL_DRM_AUTHENTICATED 2 +#define WL_DRM_CAPABILITIES 3 + +/** + * @ingroup iface_wl_drm + */ +#define WL_DRM_DEVICE_SINCE_VERSION 1 +/** + * @ingroup iface_wl_drm + */ +#define WL_DRM_FORMAT_SINCE_VERSION 1 +/** + * @ingroup iface_wl_drm + */ +#define WL_DRM_AUTHENTICATED_SINCE_VERSION 1 +/** + * @ingroup iface_wl_drm + */ +#define WL_DRM_CAPABILITIES_SINCE_VERSION 1 + +/** + * @ingroup iface_wl_drm + */ +#define WL_DRM_AUTHENTICATE_SINCE_VERSION 1 +/** + * @ingroup iface_wl_drm + */ +#define WL_DRM_CREATE_BUFFER_SINCE_VERSION 1 +/** + * @ingroup iface_wl_drm + */ +#define WL_DRM_CREATE_PLANAR_BUFFER_SINCE_VERSION 1 +/** + * @ingroup iface_wl_drm + */ +#define WL_DRM_CREATE_PRIME_BUFFER_SINCE_VERSION 1 + +/** + * @ingroup iface_wl_drm + * Sends an device event to the client owning the resource. + * @param resource_ The client's resource + */ +static inline void +wl_drm_send_device(struct wl_resource *resource_, int32_t fd, const char *name) +{ + wl_resource_post_event(resource_, WL_DRM_DEVICE, fd, name); +} + +/** + * @ingroup iface_wl_drm + * Sends an format event to the client owning the resource. + * @param resource_ The client's resource + */ +static inline void +wl_drm_send_format(struct wl_resource *resource_, uint32_t format) +{ + wl_resource_post_event(resource_, WL_DRM_FORMAT, format); +} + +/** + * @ingroup iface_wl_drm + * Sends an authenticated event to the client owning the resource. + * @param resource_ The client's resource + */ +static inline void +wl_drm_send_authenticated(struct wl_resource *resource_) +{ + wl_resource_post_event(resource_, WL_DRM_AUTHENTICATED); +} + +/** + * @ingroup iface_wl_drm + * Sends an capabilities event to the client owning the resource. + * @param resource_ The client's resource + */ +static inline void +wl_drm_send_capabilities(struct wl_resource *resource_, uint32_t value) +{ + wl_resource_post_event(resource_, WL_DRM_CAPABILITIES, value); +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/wl-drm.xml b/wl-drm.xml new file mode 100644 index 0000000..20f6581 --- /dev/null +++ b/wl-drm.xml @@ -0,0 +1,184 @@ + + + + Copyright © 2008-2011 Kristian Høgsberg + Copyright © 2010-2011 Intel Corporation + Copyright © 2012-2013 Collabora, Ltd. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice (including the + next paragraph) shall be included in all copies or substantial + portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + + + + + This protocol is used to create buffers that are backed by DRM + buffers. + + This protocol is deprecated and not intended for new clients. + + + + + These errors can be emitted in response to wl_drm requests. + + + + + + + + + DRM formats are defined by the DRM_FORMAT_* macros in + drm_fourcc.h. A list of formats is also available at: + https://dri.freedesktop.org/docs/drm/graphics/drm-fourcc.html + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This request authenticates the DRM device. The device name is + sent with the device event. + + + + + + + This request creates a wl_buffer object backed by a DRM buffer. + The DRM buffer is created by the server. + + + + + + + + + + + + This request creates a wl_buffer object backed by a DRM buffer. + The DRM buffer is created by the server. The buffer has + multiple planes. + + + + + + + + + + + + + + + + + This request creates a wl_buffer object backed by a DMA-BUF file + descriptor. The buffer can have one, two or three planes. + + + + + + + + + + + + + + + + + + This event advertises the DRM device file descriptor and name. + It is sent to the client when it binds to the wl_drm interface. + + + + + + + + This event advertises a DRM format. It is sent to the client + when it binds to the wl_drm interface. + + + + + + + This event is sent after a successful wl_drm.authenticate + request. + + + + + + Bitmask of capabilities. + + + + + + + This event advertises the capabilities of the DRM device. + + + + + diff --git a/xdg-shell-client.h b/xdg-shell-client.h new file mode 100644 index 0000000..5a2ed89 --- /dev/null +++ b/xdg-shell-client.h @@ -0,0 +1,2384 @@ +/* Generated by wayland-scanner 1.24.0 */ + +#ifndef XDG_SHELL_CLIENT_PROTOCOL_H +#define XDG_SHELL_CLIENT_PROTOCOL_H + +#include +#include +#include "wayland-client.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @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 */ + +/** + * @ingroup iface_xdg_wm_base + * @struct xdg_wm_base_listener + */ +struct xdg_wm_base_listener { + /** + * check if the client is alive + * + * The ping event asks the client if it's still alive. Pass the + * serial specified in the event back to the compositor by sending + * a "pong" request back with the specified serial. See + * xdg_wm_base.pong. + * + * Compositors can use this to determine if the client is still + * alive. It's unspecified what will happen if the client doesn't + * respond to the ping request, or in what timeframe. Clients + * should try to respond in a reasonable amount of time. The + * “unresponsive” error is provided for compositors that wish + * to disconnect unresponsive clients. + * + * A compositor is free to ping in any way it wants, but a client + * must always respond to any xdg_wm_base object it created. + * @param serial pass this to the pong request + */ + void (*ping)(void *data, + struct xdg_wm_base *xdg_wm_base, + uint32_t serial); +}; + +/** + * @ingroup iface_xdg_wm_base + */ +static inline int +xdg_wm_base_add_listener(struct xdg_wm_base *xdg_wm_base, + const struct xdg_wm_base_listener *listener, void *data) +{ + return wl_proxy_add_listener((struct wl_proxy *) xdg_wm_base, + (void (**)(void)) listener, data); +} + +#define XDG_WM_BASE_DESTROY 0 +#define XDG_WM_BASE_CREATE_POSITIONER 1 +#define XDG_WM_BASE_GET_XDG_SURFACE 2 +#define XDG_WM_BASE_PONG 3 + +/** + * @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 */ +static inline void +xdg_wm_base_set_user_data(struct xdg_wm_base *xdg_wm_base, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) xdg_wm_base, user_data); +} + +/** @ingroup iface_xdg_wm_base */ +static inline void * +xdg_wm_base_get_user_data(struct xdg_wm_base *xdg_wm_base) +{ + return wl_proxy_get_user_data((struct wl_proxy *) xdg_wm_base); +} + +static inline uint32_t +xdg_wm_base_get_version(struct xdg_wm_base *xdg_wm_base) +{ + return wl_proxy_get_version((struct wl_proxy *) xdg_wm_base); +} + +/** + * @ingroup iface_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. + */ +static inline void +xdg_wm_base_destroy(struct xdg_wm_base *xdg_wm_base) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_wm_base, + XDG_WM_BASE_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_wm_base), WL_MARSHAL_FLAG_DESTROY); +} + +/** + * @ingroup iface_xdg_wm_base + * + * 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. + */ +static inline struct xdg_positioner * +xdg_wm_base_create_positioner(struct xdg_wm_base *xdg_wm_base) +{ + struct wl_proxy *id; + + id = wl_proxy_marshal_flags((struct wl_proxy *) xdg_wm_base, + XDG_WM_BASE_CREATE_POSITIONER, &xdg_positioner_interface, wl_proxy_get_version((struct wl_proxy *) xdg_wm_base), 0, NULL); + + return (struct xdg_positioner *) id; +} + +/** + * @ingroup iface_xdg_wm_base + * + * 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. + */ +static inline struct xdg_surface * +xdg_wm_base_get_xdg_surface(struct xdg_wm_base *xdg_wm_base, struct wl_surface *surface) +{ + struct wl_proxy *id; + + id = wl_proxy_marshal_flags((struct wl_proxy *) xdg_wm_base, + XDG_WM_BASE_GET_XDG_SURFACE, &xdg_surface_interface, wl_proxy_get_version((struct wl_proxy *) xdg_wm_base), 0, NULL, surface); + + return (struct xdg_surface *) id; +} + +/** + * @ingroup iface_xdg_wm_base + * + * 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. + */ +static inline void +xdg_wm_base_pong(struct xdg_wm_base *xdg_wm_base, uint32_t serial) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_wm_base, + XDG_WM_BASE_PONG, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_wm_base), 0, 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_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_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_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 */ + +#define XDG_POSITIONER_DESTROY 0 +#define XDG_POSITIONER_SET_SIZE 1 +#define XDG_POSITIONER_SET_ANCHOR_RECT 2 +#define XDG_POSITIONER_SET_ANCHOR 3 +#define XDG_POSITIONER_SET_GRAVITY 4 +#define XDG_POSITIONER_SET_CONSTRAINT_ADJUSTMENT 5 +#define XDG_POSITIONER_SET_OFFSET 6 +#define XDG_POSITIONER_SET_REACTIVE 7 +#define XDG_POSITIONER_SET_PARENT_SIZE 8 +#define XDG_POSITIONER_SET_PARENT_CONFIGURE 9 + + +/** + * @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 + +/** @ingroup iface_xdg_positioner */ +static inline void +xdg_positioner_set_user_data(struct xdg_positioner *xdg_positioner, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) xdg_positioner, user_data); +} + +/** @ingroup iface_xdg_positioner */ +static inline void * +xdg_positioner_get_user_data(struct xdg_positioner *xdg_positioner) +{ + return wl_proxy_get_user_data((struct wl_proxy *) xdg_positioner); +} + +static inline uint32_t +xdg_positioner_get_version(struct xdg_positioner *xdg_positioner) +{ + return wl_proxy_get_version((struct wl_proxy *) xdg_positioner); +} + +/** + * @ingroup iface_xdg_positioner + * + * Notify the compositor that the xdg_positioner will no longer be used. + */ +static inline void +xdg_positioner_destroy(struct xdg_positioner *xdg_positioner) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, + XDG_POSITIONER_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), WL_MARSHAL_FLAG_DESTROY); +} + +/** + * @ingroup iface_xdg_positioner + * + * 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. + */ +static inline void +xdg_positioner_set_size(struct xdg_positioner *xdg_positioner, int32_t width, int32_t height) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, + XDG_POSITIONER_SET_SIZE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, width, height); +} + +/** + * @ingroup iface_xdg_positioner + * + * 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. + */ +static inline void +xdg_positioner_set_anchor_rect(struct xdg_positioner *xdg_positioner, int32_t x, int32_t y, int32_t width, int32_t height) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, + XDG_POSITIONER_SET_ANCHOR_RECT, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, x, y, width, height); +} + +/** + * @ingroup iface_xdg_positioner + * + * 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. + */ +static inline void +xdg_positioner_set_anchor(struct xdg_positioner *xdg_positioner, uint32_t anchor) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, + XDG_POSITIONER_SET_ANCHOR, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, anchor); +} + +/** + * @ingroup iface_xdg_positioner + * + * 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. + */ +static inline void +xdg_positioner_set_gravity(struct xdg_positioner *xdg_positioner, uint32_t gravity) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, + XDG_POSITIONER_SET_GRAVITY, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, gravity); +} + +/** + * @ingroup iface_xdg_positioner + * + * 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. + */ +static inline void +xdg_positioner_set_constraint_adjustment(struct xdg_positioner *xdg_positioner, uint32_t constraint_adjustment) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, + XDG_POSITIONER_SET_CONSTRAINT_ADJUSTMENT, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, constraint_adjustment); +} + +/** + * @ingroup iface_xdg_positioner + * + * 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. + */ +static inline void +xdg_positioner_set_offset(struct xdg_positioner *xdg_positioner, int32_t x, int32_t y) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, + XDG_POSITIONER_SET_OFFSET, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, x, y); +} + +/** + * @ingroup iface_xdg_positioner + * + * 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. + */ +static inline void +xdg_positioner_set_reactive(struct xdg_positioner *xdg_positioner) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, + XDG_POSITIONER_SET_REACTIVE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0); +} + +/** + * @ingroup iface_xdg_positioner + * + * 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. + */ +static inline void +xdg_positioner_set_parent_size(struct xdg_positioner *xdg_positioner, int32_t parent_width, int32_t parent_height) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, + XDG_POSITIONER_SET_PARENT_SIZE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, parent_width, parent_height); +} + +/** + * @ingroup iface_xdg_positioner + * + * 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. + */ +static inline void +xdg_positioner_set_parent_configure(struct xdg_positioner *xdg_positioner, uint32_t serial) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, + XDG_POSITIONER_SET_PARENT_CONFIGURE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, serial); +} + +#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 */ + +/** + * @ingroup iface_xdg_surface + * @struct xdg_surface_listener + */ +struct xdg_surface_listener { + /** + * suggest a surface change + * + * The configure event marks the end of a configure sequence. A + * configure sequence is a set of one or more events configuring + * the state of the xdg_surface, including the final + * xdg_surface.configure event. + * + * Where applicable, xdg_surface surface roles will during a + * configure sequence extend this event as a latched state sent as + * events before the xdg_surface.configure event. Such events + * should be considered to make up a set of atomically applied + * configuration states, where the xdg_surface.configure commits + * the accumulated state. + * + * Clients should arrange their surface for the new states, and + * then send an ack_configure request with the serial sent in this + * configure event at some point before committing the new surface. + * + * If the client receives multiple configure events before it can + * respond to one, it is free to discard all but the last event it + * received. + * @param serial serial of the configure event + */ + void (*configure)(void *data, + struct xdg_surface *xdg_surface, + uint32_t serial); +}; + +/** + * @ingroup iface_xdg_surface + */ +static inline int +xdg_surface_add_listener(struct xdg_surface *xdg_surface, + const struct xdg_surface_listener *listener, void *data) +{ + return wl_proxy_add_listener((struct wl_proxy *) xdg_surface, + (void (**)(void)) listener, data); +} + +#define XDG_SURFACE_DESTROY 0 +#define XDG_SURFACE_GET_TOPLEVEL 1 +#define XDG_SURFACE_GET_POPUP 2 +#define XDG_SURFACE_SET_WINDOW_GEOMETRY 3 +#define XDG_SURFACE_ACK_CONFIGURE 4 + +/** + * @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 */ +static inline void +xdg_surface_set_user_data(struct xdg_surface *xdg_surface, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) xdg_surface, user_data); +} + +/** @ingroup iface_xdg_surface */ +static inline void * +xdg_surface_get_user_data(struct xdg_surface *xdg_surface) +{ + return wl_proxy_get_user_data((struct wl_proxy *) xdg_surface); +} + +static inline uint32_t +xdg_surface_get_version(struct xdg_surface *xdg_surface) +{ + return wl_proxy_get_version((struct wl_proxy *) xdg_surface); +} + +/** + * @ingroup iface_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. + */ +static inline void +xdg_surface_destroy(struct xdg_surface *xdg_surface) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_surface, + XDG_SURFACE_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_surface), WL_MARSHAL_FLAG_DESTROY); +} + +/** + * @ingroup iface_xdg_surface + * + * 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. + */ +static inline struct xdg_toplevel * +xdg_surface_get_toplevel(struct xdg_surface *xdg_surface) +{ + struct wl_proxy *id; + + id = wl_proxy_marshal_flags((struct wl_proxy *) xdg_surface, + XDG_SURFACE_GET_TOPLEVEL, &xdg_toplevel_interface, wl_proxy_get_version((struct wl_proxy *) xdg_surface), 0, NULL); + + return (struct xdg_toplevel *) id; +} + +/** + * @ingroup iface_xdg_surface + * + * 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. + */ +static inline struct xdg_popup * +xdg_surface_get_popup(struct xdg_surface *xdg_surface, struct xdg_surface *parent, struct xdg_positioner *positioner) +{ + struct wl_proxy *id; + + id = wl_proxy_marshal_flags((struct wl_proxy *) xdg_surface, + XDG_SURFACE_GET_POPUP, &xdg_popup_interface, wl_proxy_get_version((struct wl_proxy *) xdg_surface), 0, NULL, parent, positioner); + + return (struct xdg_popup *) id; +} + +/** + * @ingroup iface_xdg_surface + * + * 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. + */ +static inline void +xdg_surface_set_window_geometry(struct xdg_surface *xdg_surface, int32_t x, int32_t y, int32_t width, int32_t height) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_surface, + XDG_SURFACE_SET_WINDOW_GEOMETRY, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_surface), 0, x, y, width, height); +} + +/** + * @ingroup iface_xdg_surface + * + * 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. + */ +static inline void +xdg_surface_ack_configure(struct xdg_surface *xdg_surface, uint32_t serial) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_surface, + XDG_SURFACE_ACK_CONFIGURE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_surface), 0, 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_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_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_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 */ + +/** + * @ingroup iface_xdg_toplevel + * @struct xdg_toplevel_listener + */ +struct xdg_toplevel_listener { + /** + * suggest a surface change + * + * This configure event asks the client to resize its toplevel + * surface or to change its state. The configured state should not + * be applied immediately. See xdg_surface.configure for details. + * + * The width and height arguments specify a hint to the window + * about how its surface should be resized in window geometry + * coordinates. See set_window_geometry. + * + * If the width or height arguments are zero, it means the client + * should decide its own window dimension. This may happen when the + * compositor needs to configure the state of the surface but + * doesn't have any information about any previous or expected + * dimension. + * + * The states listed in the event specify how the width/height + * arguments should be interpreted, and possibly how it should be + * drawn. + * + * Clients must send an ack_configure in response to this event. + * See xdg_surface.configure and xdg_surface.ack_configure for + * details. + */ + void (*configure)(void *data, + struct xdg_toplevel *xdg_toplevel, + int32_t width, + int32_t height, + struct wl_array *states); + /** + * surface wants to be closed + * + * The close event is sent by the compositor when the user wants + * the surface to be closed. This should be equivalent to the user + * clicking the close button in client-side decorations, if your + * application has any. + * + * This is only a request that the user intends to close the + * window. The client may choose to ignore this request, or show a + * dialog to ask the user to save their data, etc. + */ + void (*close)(void *data, + struct xdg_toplevel *xdg_toplevel); + /** + * recommended window geometry bounds + * + * The configure_bounds event may be sent prior to a + * xdg_toplevel.configure event to communicate the bounds a window + * geometry size is recommended to constrain to. + * + * The passed width and height are in surface coordinate space. If + * width and height are 0, it means bounds is unknown and + * equivalent to as if no configure_bounds event was ever sent for + * this surface. + * + * The bounds can for example correspond to the size of a monitor + * excluding any panels or other shell components, so that a + * surface isn't created in a way that it cannot fit. + * + * The bounds may change at any point, and in such a case, a new + * xdg_toplevel.configure_bounds will be sent, followed by + * xdg_toplevel.configure and xdg_surface.configure. + * @since 4 + */ + void (*configure_bounds)(void *data, + struct xdg_toplevel *xdg_toplevel, + int32_t width, + int32_t height); + /** + * compositor capabilities + * + * This event advertises the capabilities supported by the + * compositor. If a capability isn't supported, clients should hide + * or disable the UI elements that expose this functionality. For + * instance, if the compositor doesn't advertise support for + * minimized toplevels, a button triggering the set_minimized + * request should not be displayed. + * + * The compositor will ignore requests it doesn't support. For + * instance, a compositor which doesn't advertise support for + * minimized will ignore set_minimized requests. + * + * Compositors must send this event once before the first + * xdg_surface.configure event. When the capabilities change, + * compositors must send this event again and then send an + * xdg_surface.configure event. + * + * The configured state should not be applied immediately. See + * xdg_surface.configure for details. + * + * The capabilities are sent as an array of 32-bit unsigned + * integers in native endianness. + * @param capabilities array of 32-bit capabilities + * @since 5 + */ + void (*wm_capabilities)(void *data, + struct xdg_toplevel *xdg_toplevel, + struct wl_array *capabilities); +}; + +/** + * @ingroup iface_xdg_toplevel + */ +static inline int +xdg_toplevel_add_listener(struct xdg_toplevel *xdg_toplevel, + const struct xdg_toplevel_listener *listener, void *data) +{ + return wl_proxy_add_listener((struct wl_proxy *) xdg_toplevel, + (void (**)(void)) listener, data); +} + +#define XDG_TOPLEVEL_DESTROY 0 +#define XDG_TOPLEVEL_SET_PARENT 1 +#define XDG_TOPLEVEL_SET_TITLE 2 +#define XDG_TOPLEVEL_SET_APP_ID 3 +#define XDG_TOPLEVEL_SHOW_WINDOW_MENU 4 +#define XDG_TOPLEVEL_MOVE 5 +#define XDG_TOPLEVEL_RESIZE 6 +#define XDG_TOPLEVEL_SET_MAX_SIZE 7 +#define XDG_TOPLEVEL_SET_MIN_SIZE 8 +#define XDG_TOPLEVEL_SET_MAXIMIZED 9 +#define XDG_TOPLEVEL_UNSET_MAXIMIZED 10 +#define XDG_TOPLEVEL_SET_FULLSCREEN 11 +#define XDG_TOPLEVEL_UNSET_FULLSCREEN 12 +#define XDG_TOPLEVEL_SET_MINIMIZED 13 + +/** + * @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 */ +static inline void +xdg_toplevel_set_user_data(struct xdg_toplevel *xdg_toplevel, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) xdg_toplevel, user_data); +} + +/** @ingroup iface_xdg_toplevel */ +static inline void * +xdg_toplevel_get_user_data(struct xdg_toplevel *xdg_toplevel) +{ + return wl_proxy_get_user_data((struct wl_proxy *) xdg_toplevel); +} + +static inline uint32_t +xdg_toplevel_get_version(struct xdg_toplevel *xdg_toplevel) +{ + return wl_proxy_get_version((struct wl_proxy *) xdg_toplevel); +} + +/** + * @ingroup iface_xdg_toplevel + * + * This request destroys the role surface and unmaps the surface; + * see "Unmapping" behavior in interface section for details. + */ +static inline void +xdg_toplevel_destroy(struct xdg_toplevel *xdg_toplevel) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), WL_MARSHAL_FLAG_DESTROY); +} + +/** + * @ingroup iface_xdg_toplevel + * + * 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. + */ +static inline void +xdg_toplevel_set_parent(struct xdg_toplevel *xdg_toplevel, struct xdg_toplevel *parent) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_SET_PARENT, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, parent); +} + +/** + * @ingroup iface_xdg_toplevel + * + * 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. + */ +static inline void +xdg_toplevel_set_title(struct xdg_toplevel *xdg_toplevel, const char *title) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_SET_TITLE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, title); +} + +/** + * @ingroup iface_xdg_toplevel + * + * 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/ + */ +static inline void +xdg_toplevel_set_app_id(struct xdg_toplevel *xdg_toplevel, const char *app_id) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_SET_APP_ID, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, app_id); +} + +/** + * @ingroup iface_xdg_toplevel + * + * 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. + */ +static inline void +xdg_toplevel_show_window_menu(struct xdg_toplevel *xdg_toplevel, struct wl_seat *seat, uint32_t serial, int32_t x, int32_t y) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_SHOW_WINDOW_MENU, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, seat, serial, x, y); +} + +/** + * @ingroup iface_xdg_toplevel + * + * 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. + */ +static inline void +xdg_toplevel_move(struct xdg_toplevel *xdg_toplevel, struct wl_seat *seat, uint32_t serial) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_MOVE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, seat, serial); +} + +/** + * @ingroup iface_xdg_toplevel + * + * 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. + */ +static inline void +xdg_toplevel_resize(struct xdg_toplevel *xdg_toplevel, struct wl_seat *seat, uint32_t serial, uint32_t edges) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_RESIZE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, seat, serial, edges); +} + +/** + * @ingroup iface_xdg_toplevel + * + * 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. + */ +static inline void +xdg_toplevel_set_max_size(struct xdg_toplevel *xdg_toplevel, int32_t width, int32_t height) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_SET_MAX_SIZE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, width, height); +} + +/** + * @ingroup iface_xdg_toplevel + * + * 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. + */ +static inline void +xdg_toplevel_set_min_size(struct xdg_toplevel *xdg_toplevel, int32_t width, int32_t height) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_SET_MIN_SIZE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, width, height); +} + +/** + * @ingroup iface_xdg_toplevel + * + * 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. + */ +static inline void +xdg_toplevel_set_maximized(struct xdg_toplevel *xdg_toplevel) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_SET_MAXIMIZED, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0); +} + +/** + * @ingroup iface_xdg_toplevel + * + * 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. + */ +static inline void +xdg_toplevel_unset_maximized(struct xdg_toplevel *xdg_toplevel) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_UNSET_MAXIMIZED, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0); +} + +/** + * @ingroup iface_xdg_toplevel + * + * 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. + */ +static inline void +xdg_toplevel_set_fullscreen(struct xdg_toplevel *xdg_toplevel, struct wl_output *output) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_SET_FULLSCREEN, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, output); +} + +/** + * @ingroup iface_xdg_toplevel + * + * 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). + */ +static inline void +xdg_toplevel_unset_fullscreen(struct xdg_toplevel *xdg_toplevel) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_UNSET_FULLSCREEN, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0); +} + +/** + * @ingroup iface_xdg_toplevel + * + * 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. + */ +static inline void +xdg_toplevel_set_minimized(struct xdg_toplevel *xdg_toplevel) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, + XDG_TOPLEVEL_SET_MINIMIZED, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0); +} + +#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 */ + +/** + * @ingroup iface_xdg_popup + * @struct xdg_popup_listener + */ +struct xdg_popup_listener { + /** + * configure the popup surface + * + * This event asks the popup surface to configure itself given + * the configuration. The configured state should not be applied + * immediately. See xdg_surface.configure for details. + * + * The x and y arguments represent the position the popup was + * placed at given the xdg_positioner rule, relative to the upper + * left corner of the window geometry of the parent surface. + * + * For version 2 or older, the configure event for an xdg_popup is + * only ever sent once for the initial configuration. Starting with + * version 3, it may be sent again if the popup is setup with an + * xdg_positioner with set_reactive requested, or in response to + * xdg_popup.reposition requests. + * @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 + */ + void (*configure)(void *data, + struct xdg_popup *xdg_popup, + int32_t x, + int32_t y, + int32_t width, + int32_t height); + /** + * popup interaction is done + * + * The popup_done event is sent out when a popup is dismissed by + * the compositor. The client should destroy the xdg_popup object + * at this point. + */ + void (*popup_done)(void *data, + struct xdg_popup *xdg_popup); + /** + * signal the completion of a repositioned request + * + * The repositioned event is sent as part of a popup + * configuration sequence, together with xdg_popup.configure and + * lastly xdg_surface.configure to notify the completion of a + * reposition request. + * + * The repositioned event is to notify about the completion of a + * xdg_popup.reposition request. The token argument is the token + * passed in the xdg_popup.reposition request. + * + * Immediately after this event is emitted, xdg_popup.configure and + * xdg_surface.configure will be sent with the updated size and + * position, as well as a new configure serial. + * + * The client should optionally update the content of the popup, + * but must acknowledge the new popup configuration for the new + * position to take effect. See xdg_surface.ack_configure for + * details. + * @param token reposition request token + * @since 3 + */ + void (*repositioned)(void *data, + struct xdg_popup *xdg_popup, + uint32_t token); +}; + +/** + * @ingroup iface_xdg_popup + */ +static inline int +xdg_popup_add_listener(struct xdg_popup *xdg_popup, + const struct xdg_popup_listener *listener, void *data) +{ + return wl_proxy_add_listener((struct wl_proxy *) xdg_popup, + (void (**)(void)) listener, data); +} + +#define XDG_POPUP_DESTROY 0 +#define XDG_POPUP_GRAB 1 +#define XDG_POPUP_REPOSITION 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 */ +static inline void +xdg_popup_set_user_data(struct xdg_popup *xdg_popup, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) xdg_popup, user_data); +} + +/** @ingroup iface_xdg_popup */ +static inline void * +xdg_popup_get_user_data(struct xdg_popup *xdg_popup) +{ + return wl_proxy_get_user_data((struct wl_proxy *) xdg_popup); +} + +static inline uint32_t +xdg_popup_get_version(struct xdg_popup *xdg_popup) +{ + return wl_proxy_get_version((struct wl_proxy *) xdg_popup); +} + +/** + * @ingroup iface_xdg_popup + * + * 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. + */ +static inline void +xdg_popup_destroy(struct xdg_popup *xdg_popup) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_popup, + XDG_POPUP_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_popup), WL_MARSHAL_FLAG_DESTROY); +} + +/** + * @ingroup iface_xdg_popup + * + * 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. + */ +static inline void +xdg_popup_grab(struct xdg_popup *xdg_popup, struct wl_seat *seat, uint32_t serial) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_popup, + XDG_POPUP_GRAB, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_popup), 0, seat, serial); +} + +/** + * @ingroup iface_xdg_popup + * + * 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. + */ +static inline void +xdg_popup_reposition(struct xdg_popup *xdg_popup, struct xdg_positioner *positioner, uint32_t token) +{ + wl_proxy_marshal_flags((struct wl_proxy *) xdg_popup, + XDG_POPUP_REPOSITION, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_popup), 0, positioner, token); +} + +#ifdef __cplusplus +} +#endif + +#endif