Path: blob/master/platform/linuxbsd/wayland/wayland_embedder.h
14772 views
/**************************************************************************/1/* wayland_embedder.h */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#pragma once3132#ifdef WAYLAND_ENABLED3334#ifdef TOOLS_ENABLED3536#include "core/templates/a_hash_map.h"37#include "core/templates/pooled_list.h"3839#ifdef SOWRAP_ENABLED40#include "wayland/dynwrappers/wayland-client-core-so_wrap.h"41#else42#include <wayland-client-core.h>43#endif4445#include "protocol/wayland.gen.h"4647#include "protocol/linux_dmabuf_v1.gen.h"48#include "protocol/xdg_shell.gen.h"4950#include "protocol/commit_timing_v1.gen.h"51#include "protocol/cursor_shape.gen.h"52#include "protocol/fifo_v1.gen.h"53#include "protocol/fractional_scale.gen.h"54#include "protocol/godot_embedding_compositor.gen.h"55#include "protocol/idle_inhibit.gen.h"56#include "protocol/linux_drm_syncobj_v1.gen.h"57#include "protocol/linux_explicit_synchronization_unstable_v1.gen.h"58#include "protocol/pointer_constraints.gen.h"59#include "protocol/pointer_gestures.gen.h"60#include "protocol/primary_selection.gen.h"61#include "protocol/relative_pointer.gen.h"62#include "protocol/tablet.gen.h"63#include "protocol/tearing_control_v1.gen.h"64#include "protocol/text_input.gen.h"65#include "protocol/viewporter.gen.h"66#include "protocol/wayland-drm.gen.h"67#include "protocol/xdg_activation.gen.h"68#include "protocol/xdg_decoration.gen.h"69#include "protocol/xdg_foreign_v1.gen.h"70#include "protocol/xdg_foreign_v2.gen.h"71#include "protocol/xdg_shell.gen.h"72#include "protocol/xdg_system_bell.gen.h"73#include "protocol/xdg_toplevel_icon.gen.h"7475#include <errno.h>76#include <stdint.h>77#include <stdio.h>78#include <stdlib.h>79#include <string.h>80#include <sys/socket.h>81#include <sys/un.h>8283#include <poll.h>8485#include "core/io/dir_access.h"86#include "core/os/thread.h"8788// TODO: Consider resizing the ancillary buffer dynamically.89#define EMBED_ANCILLARY_BUF_SIZE 40969091class WaylandEmbedder {92enum class ProxyDirection {93CLIENT,94COMPOSITOR,95};9697enum class MessageStatus {98HANDLED,99UNHANDLED,100INVALID,101ERROR,102};103104struct msg_info {105uint32_t raw_id = 0;106uint16_t size = 0;107uint16_t opcode = 0;108109pid_t pid = 0;110111ProxyDirection direction = ProxyDirection::CLIENT;112113constexpr size_t words() const { return (size / sizeof(uint32_t)); }114};115116struct WaylandObjectData {117virtual ~WaylandObjectData() = default;118};119120struct WaylandObject {121const struct wl_interface *interface = nullptr;122int version = 0;123124// Inert, awaiting confirmation from server.125bool destroyed = false;126127// Other objects might depend on it and must not be destroyed.128bool shared = false;129130WaylandObjectData *data = nullptr;131};132133struct WaylandDrmGlobalData : WaylandObjectData {134String device;135LocalVector<uint32_t> formats;136bool authenticated;137uint32_t capabilities;138};139140struct WaylandShmGlobalData : WaylandObjectData {141LocalVector<uint32_t> formats;142};143144struct Client {145struct GlobalIdInfo {146uint32_t id = INVALID_ID;147List<uint32_t>::Element *history_elem = nullptr;148149GlobalIdInfo() = default;150GlobalIdInfo(uint32_t p_id, List<uint32_t>::Element *p_history_elem) :151id(p_id), history_elem(p_history_elem) {}152};153154WaylandEmbedder *embedder = nullptr;155156int socket = -1;157158// NOTE: PIDs are not unique per client!159pid_t pid = 0;160161// FIXME: Names suck.162AHashMap<uint32_t, HashSet<uint32_t>> registry_globals_instances;163HashSet<uint32_t> wl_registry_instances;164165List<uint32_t> global_id_history;166AHashMap<uint32_t, GlobalIdInfo> global_ids;167AHashMap<uint32_t, uint32_t> local_ids;168169// Objects with no equivalent on the real compositor.170AHashMap<uint32_t, WaylandObject> fake_objects;171172// Objects which mirror events of a global object.173AHashMap<uint32_t, WaylandObject> global_instances;174175uint32_t embedded_client_id = INVALID_ID;176uint32_t embedded_window_id = INVALID_ID;177178List<int> fds;179180// Clients obviously expect properly packed server IDs, so we need to allocate181// them somehow. This approach mimics the one used in PooledList.182uint32_t allocated_server_ids = INVALID_ID;183LocalVector<uint32_t> free_server_ids;184185uint32_t get_global_id(uint32_t p_local_id) const { return global_ids.has(p_local_id) ? global_ids[p_local_id].id : INVALID_ID; }186uint32_t get_local_id(uint32_t p_global_id) const { return local_ids.has(p_global_id) ? local_ids[p_global_id] : INVALID_ID; }187188uint32_t allocate_server_id();189WaylandObject *get_object(uint32_t p_local_id);190Error delete_object(uint32_t p_local_id);191192Error bind_global_id(uint32_t p_global_id, uint32_t p_local_id);193194uint32_t new_object(uint32_t p_local_id, const struct wl_interface *p_interface, int p_version = 1, WaylandObjectData *p_data = nullptr);195uint32_t new_server_object(uint32_t p_global_id, const struct wl_interface *p_interface, int p_version = 1, WaylandObjectData *p_data = nullptr);196WaylandObject *new_fake_object(uint32_t p_local_id, const struct wl_interface *p_interface, int p_version = 1, WaylandObjectData *p_data = nullptr);197WaylandObject *new_global_instance(uint32_t p_local_id, uint32_t p_global_id, const struct wl_interface *p_interface, int p_version = 1, WaylandObjectData *p_data = nullptr);198199Error send_wl_drm_state(uint32_t p_id, WaylandDrmGlobalData *p_state);200};201202// Local IDs are a mess to handle as they strictly depend on their client of203// origin. This wrapper helps with that.204class LocalObjectHandle {205Client *client = nullptr;206uint32_t local_id = INVALID_ID;207208public:209constexpr LocalObjectHandle() = default;210211constexpr LocalObjectHandle(Client *p_client, uint32_t p_id) :212client(p_client), local_id(p_id) {}213214void invalidate() {215client = nullptr;216local_id = INVALID_ID;217}218constexpr bool is_valid() const { return client != nullptr && local_id != INVALID_ID; }219220WaylandObject *get() { return is_valid() ? client->get_object(local_id) : nullptr; }221constexpr Client *get_client() const { return client; }222constexpr uint32_t get_local_id() const { return local_id; }223uint32_t get_global_id() const { return (is_valid() && client->global_ids.has(local_id)) ? client->global_ids[local_id].id : INVALID_ID; }224};225226struct WaylandSeatInstanceData : WaylandObjectData {227uint32_t wl_keyboard_id = INVALID_ID;228uint32_t wl_pointer_id = INVALID_ID;229};230231struct WaylandSeatGlobalData : WaylandObjectData {232uint32_t capabilities = 0;233234uint32_t pointed_surface_id = INVALID_ID;235uint32_t focused_surface_id = INVALID_ID;236};237238struct WaylandKeyboardData : WaylandObjectData {239uint32_t wl_seat_id = INVALID_ID;240};241242struct WaylandPointerData : WaylandObjectData {243uint32_t wl_seat_id = INVALID_ID;244};245246struct WaylandSurfaceData : WaylandObjectData {247Client *client = nullptr;248LocalObjectHandle role_object_handle;249};250251struct XdgSurfaceData : WaylandObjectData {252uint32_t wl_surface_id = INVALID_ID;253};254255struct WaylandSubsurfaceData : WaylandObjectData {256Point2i position;257};258259struct XdgToplevelData : WaylandObjectData {260LocalObjectHandle xdg_surface_handle;261LocalObjectHandle parent_handle;262uint32_t wl_subsurface_id = INVALID_ID;263264Size2i size;265266bool configured = false;267268constexpr bool is_embedded() const { return wl_subsurface_id != INVALID_ID; }269};270271struct XdgPopupData : WaylandObjectData {272LocalObjectHandle parent_handle;273};274275struct XdgPositionerData : WaylandObjectData {276Rect2i anchor_rect;277};278279struct EmbeddedClientData : WaylandObjectData {280Client *client = nullptr;281bool disconnected = false;282};283284struct RegistryGlobalInfo {285const struct wl_interface *interface = nullptr;286uint32_t version = 0;287uint32_t compositor_name = 0;288289// The specs requires for us to ignore requests for destroyed global290// objects until all instances are gone, to avoid races.291bool destroyed = false;292int instance_counter = 0;293294// Key is version.295HashMap<uint32_t, uint32_t> reusable_objects;296297WaylandObjectData *data = nullptr;298};299300// These are the interfaces that the embedder understands and exposes. We do301// not implement handlers for all of them (that's the point), but we need to302// list them anyways to query their signatures at runtime, which include file303// descriptors count. Additionally, even if we could go without specifying304// them, having a "known good" list avoids unpleasant incompatibilities with305// future compositors.306const static constexpr struct wl_interface *interfaces[] = {307// wayland308&wl_buffer_interface,309&wl_callback_interface,310&wl_compositor_interface,311&wl_data_device_interface,312&wl_data_device_manager_interface,313&wl_data_offer_interface,314&wl_data_source_interface,315&wl_display_interface,316&wl_keyboard_interface,317&wl_output_interface,318&wl_pointer_interface,319&wl_region_interface,320&wl_registry_interface,321&wl_seat_interface,322//&wl_shell_interface, // Deprecated.323//&wl_shell_surface_interface, // Deprecated.324&wl_shm_interface,325&wl_shm_pool_interface,326&wl_subcompositor_interface,327&wl_subsurface_interface,328&wl_surface_interface,329//&wl_touch_interface, // Unused (at the moment).330331// xdg-shell332&xdg_wm_base_interface,333&xdg_positioner_interface,334&xdg_surface_interface,335&xdg_toplevel_interface,336&xdg_popup_interface,337338// linux-dmabuf-v1339&zwp_linux_dmabuf_v1_interface,340&zwp_linux_buffer_params_v1_interface,341&zwp_linux_dmabuf_feedback_v1_interface,342343// linux-explicit-synchronization-unstable-v1344&zwp_linux_explicit_synchronization_v1_interface,345&zwp_linux_surface_synchronization_v1_interface,346&zwp_linux_buffer_release_v1_interface,347348// fractional-scale349&wp_fractional_scale_manager_v1_interface,350&wp_fractional_scale_v1_interface,351352// idle-inhibit353&zwp_idle_inhibit_manager_v1_interface,354&zwp_idle_inhibitor_v1_interface,355356// pointer-constraints357&zwp_pointer_constraints_v1_interface,358&zwp_locked_pointer_v1_interface,359&zwp_confined_pointer_v1_interface,360361// pointer-gestures362&zwp_pointer_gestures_v1_interface,363&zwp_pointer_gesture_swipe_v1_interface,364&zwp_pointer_gesture_pinch_v1_interface,365&zwp_pointer_gesture_hold_v1_interface,366367// primary-selection368&zwp_primary_selection_device_manager_v1_interface,369&zwp_primary_selection_device_v1_interface,370&zwp_primary_selection_offer_v1_interface,371&zwp_primary_selection_source_v1_interface,372373// relative-pointer374&zwp_relative_pointer_manager_v1_interface,375&zwp_relative_pointer_v1_interface,376377// tablet378// TODO: Needs some extra work379//&zwp_tablet_manager_v2_interface,380//&zwp_tablet_seat_v2_interface,381//&zwp_tablet_tool_v2_interface,382//&zwp_tablet_v2_interface,383//&zwp_tablet_pad_ring_v2_interface,384//&zwp_tablet_pad_strip_v2_interface,385//&zwp_tablet_pad_group_v2_interface,386//&zwp_tablet_pad_v2_interface,387388// text-input389&zwp_text_input_v3_interface,390&zwp_text_input_manager_v3_interface,391392// viewporter393&wp_viewporter_interface,394&wp_viewport_interface,395396// xdg-activation397&xdg_activation_v1_interface,398&xdg_activation_token_v1_interface,399400// xdg-decoration401&zxdg_decoration_manager_v1_interface,402&zxdg_toplevel_decoration_v1_interface,403404// xdg-foreign405&zxdg_exporter_v1_interface,406&zxdg_importer_v1_interface,407408// xdg-foreign-v1409&zxdg_exporter_v1_interface,410&zxdg_importer_v1_interface,411412// xdg-foreign-v2413&zxdg_exporter_v2_interface,414&zxdg_importer_v2_interface,415416// xdg-shell417&xdg_wm_base_interface,418&xdg_positioner_interface,419&xdg_surface_interface,420&xdg_toplevel_interface,421&xdg_popup_interface,422423// xdg-system-bell424&xdg_system_bell_v1_interface,425426// xdg-toplevel-icon-v1427&xdg_toplevel_icon_manager_v1_interface,428&xdg_toplevel_icon_v1_interface,429430// wp-cursor-shape-v1431&wp_cursor_shape_manager_v1_interface,432433// wayland-drm434&wl_drm_interface,435436// linux-drm-syncobj-v1437&wp_linux_drm_syncobj_manager_v1_interface,438&wp_linux_drm_syncobj_surface_v1_interface,439&wp_linux_drm_syncobj_timeline_v1_interface,440441// fifo-v1442&wp_fifo_manager_v1_interface,443&wp_fifo_v1_interface,444445// commit-timing-v1446&wp_commit_timing_manager_v1_interface,447&wp_commit_timer_v1_interface,448449// tearing-control-v1450&wp_tearing_control_manager_v1_interface,451&wp_tearing_control_v1_interface,452453// Our custom things.454&godot_embedding_compositor_interface,455&godot_embedded_client_interface,456};457458// These interfaces will not be reported to embedded clients. This includes459// stuff that interacts with toplevels or other emulated objects that would460// have been filtered out manually anyways.461HashSet<const struct wl_interface *> embedded_interface_deny_list = HashSet({462&zxdg_decoration_manager_v1_interface,463&zxdg_decoration_manager_v1_interface,464&zxdg_exporter_v1_interface,465&zxdg_exporter_v2_interface,466&xdg_toplevel_icon_manager_v1_interface,467&godot_embedding_compositor_interface,468});469470static constexpr uint32_t INVALID_ID = 0;471static constexpr uint32_t DISPLAY_ID = 1;472static constexpr uint32_t REGISTRY_ID = 2;473474int proxy_socket = -1;475int compositor_socket = -1;476477// NOTE: First element must be the listening socket! This allows us to process478// it last, cleaning up closed sockets before it reuses their handles.479LocalVector<struct pollfd> pollfds;480481// Key is socket.482AHashMap<int, Client> clients;483484Client *main_client = nullptr;485486PooledList<WaylandObject> objects;487// Proxies allocated by the compositor. Their ID starts from 0xff000000.488LocalVector<WaylandObject> server_objects;489490uint32_t wl_compositor_id = 0;491uint32_t wl_subcompositor_id = 0;492uint32_t main_toplevel_id = 0;493uint32_t xdg_wm_base_id = 0;494495// Global id to name496HashMap<uint32_t, uint32_t> registry_globals_names;497498HashMap<uint32_t, RegistryGlobalInfo> registry_globals;499uint32_t registry_globals_counter = 0;500501uint32_t godot_embedding_compositor_name = 0;502503LocalVector<uint32_t> wl_seat_names;504505Thread proxy_thread;506507List<int> client_fds;508List<int> compositor_fds;509510uint32_t serial_counter = 0;511uint32_t configure_serial_counter = 0;512513uint32_t sync_callback_id = 0;514515Ref<DirAccess> runtime_dir;516int lock_fd = -1;517String socket_path;518String socket_lock_path;519520LocalVector<uint32_t> msg_buf;521LocalVector<uint8_t> ancillary_buf;522523SafeFlag thread_done;524525static size_t wl_array_word_offset(uint32_t p_size);526const static struct wl_interface *wl_interface_from_string(const char *name, size_t size);527static int wl_interface_get_destructor_opcode(const struct wl_interface *p_iface, uint32_t p_version);528529static Error send_raw_message(int p_socket, std::initializer_list<struct iovec> p_vecs, const LocalVector<int> &p_fds = LocalVector<int>());530531static Error send_wayland_message(int p_socket, uint32_t p_id, uint32_t p_opcode, const uint32_t *p_args, const size_t p_args_words);532static Error send_wayland_message(ProxyDirection p_direction, int p_socket, uint32_t p_id, const struct wl_interface &p_interface, uint32_t p_opcode, const LocalVector<union wl_argument> &p_args);533534// Utility aliases.535536static Error send_wayland_message(int p_socket, uint32_t p_id, uint32_t p_opcode, std::initializer_list<uint32_t> p_args) {537return send_wayland_message(p_socket, p_id, p_opcode, p_args.begin(), p_args.size());538}539540static Error send_wayland_method(int p_socket, uint32_t p_id, const struct wl_interface &p_interface, uint32_t p_opcode, const LocalVector<union wl_argument> &p_args) {541return send_wayland_message(ProxyDirection::COMPOSITOR, p_socket, p_id, p_interface, p_opcode, p_args);542}543544static Error send_wayland_event(int p_socket, uint32_t p_id, const struct wl_interface &p_interface, uint32_t p_opcode, const LocalVector<union wl_argument> &p_args) {545return send_wayland_message(ProxyDirection::CLIENT, p_socket, p_id, p_interface, p_opcode, p_args);546}547548// Closes the socket.549static void socket_error(int p_socket, uint32_t p_object_id, uint32_t p_code, const String &p_message);550551// NOTE: Yes, in our case object arguments are actually uints for now.552// Best way I found to reuse the Wayland stuff. Might need to make our553// own eventually.554static constexpr union wl_argument wl_arg_int(int32_t p_value) {555union wl_argument arg = {};556arg.i = p_value;557return arg;558}559static constexpr union wl_argument wl_arg_uint(uint32_t p_value) {560union wl_argument arg = {};561arg.u = p_value;562return arg;563}564static constexpr union wl_argument wl_arg_fixed(wl_fixed_t p_value) {565union wl_argument arg = {};566arg.f = p_value;567return arg;568}569static constexpr union wl_argument wl_arg_string(const char *p_value) {570union wl_argument arg = {};571arg.s = p_value;572return arg;573}574static constexpr union wl_argument wl_arg_object(uint32_t p_value) {575union wl_argument arg = {};576arg.u = p_value;577return arg;578}579static constexpr union wl_argument wl_arg_new_id(uint32_t p_value) {580union wl_argument arg = {};581arg.n = p_value;582return arg;583}584585uint32_t new_object(const struct wl_interface *p_interface, int p_version = 1, WaylandObjectData *p_data = nullptr);586WaylandObject *new_server_object(uint32_t p_global_id, const struct wl_interface *p_interface, int p_version = 1, WaylandObjectData *p_data = nullptr);587588void poll_sockets();589590int allocate_global_id();591592bool global_surface_is_window(uint32_t p_global_surface_id);593594WaylandObject *get_object(uint32_t id);595Error delete_object(uint32_t id);596597void cleanup_socket(int p_socket);598599void sync();600601uint32_t wl_registry_bind(uint32_t p_registry_id, uint32_t p_name, int p_version);602603void seat_name_enter_surface(uint32_t p_seat_name, uint32_t p_global_surface_id);604void seat_name_leave_surface(uint32_t p_seat_name, uint32_t p_global_surface_id);605606MessageStatus handle_request(LocalObjectHandle p_object, uint32_t p_opcode, const uint32_t *msg_data, size_t msg_len);607MessageStatus handle_event(uint32_t p_global_id, LocalObjectHandle p_local_handle, uint32_t p_opcode, const uint32_t *msg_data, size_t msg_len);608609void shutdown();610611bool handle_generic_msg(Client *client, const WaylandObject *p_object, const struct wl_message *message, const struct msg_info *info, uint32_t *buf, uint32_t instance_id = INVALID_ID);612Error handle_msg_info(Client *client, const struct msg_info *info, uint32_t *buf, int *fds_requested);613Error handle_sock(int p_fd);614void handle_fd(int p_fd, int p_revents);615616static void _thread_loop(void *p_data);617618public:619// Returns path to socket.620Error init();621622String get_socket_path() const { return socket_path; }623624~WaylandEmbedder();625};626627#endif // TOOLS_ENABLED628629#endif // WAYLAND_ENABLED630631632