/**************************************************************************/
/*  wayland_thread.h                                                      */
/**************************************************************************/
/*                         This file is part of:                          */
/*                             GODOT ENGINE                               */
/*                        https://godotengine.org                         */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */
/*                                                                        */
/* 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 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.                 */
/**************************************************************************/

#pragma once

#ifdef WAYLAND_ENABLED

#include "key_mapping_xkb.h"

#ifdef SOWRAP_ENABLED
#include "wayland/dynwrappers/wayland-client-core-so_wrap.h"
#include "wayland/dynwrappers/wayland-cursor-so_wrap.h"
#include "wayland/dynwrappers/wayland-egl-core-so_wrap.h"
#include "xkbcommon-so_wrap.h"
#else
#include <wayland-client-core.h>
#include <wayland-cursor.h>
#ifdef GLES3_ENABLED
#include <wayland-egl-core.h>
#endif
#include <xkbcommon/xkbcommon-compose.h>
#include <xkbcommon/xkbcommon.h>
#endif // SOWRAP_ENABLED

// These must go after the Wayland client include to work properly.
#include "wayland/protocol/idle_inhibit.gen.h"
#include "wayland/protocol/primary_selection.gen.h"

// These protocol headers name wl_pointer method arguments as `pointer`,
// which is the same name as X11's pointer typedef. This trips some very
// annoying shadowing warnings. A `#define` works around this issue.
#define pointer wl_pointer
#include "wayland/protocol/cursor_shape.gen.h"
#include "wayland/protocol/pointer_constraints.gen.h"
#include "wayland/protocol/pointer_gestures.gen.h"
#include "wayland/protocol/pointer_warp.gen.h"
#include "wayland/protocol/relative_pointer.gen.h"
#undef pointer

#include "wayland/protocol/color_management.gen.h"
#include "wayland/protocol/fractional_scale.gen.h"
#include "wayland/protocol/tablet.gen.h"
#include "wayland/protocol/text_input.gen.h"
#include "wayland/protocol/viewporter.gen.h"
#include "wayland/protocol/wayland.gen.h"
#include "wayland/protocol/xdg_activation.gen.h"
#include "wayland/protocol/xdg_decoration.gen.h"
#include "wayland/protocol/xdg_foreign_v2.gen.h"
#include "wayland/protocol/xdg_shell.gen.h"
#include "wayland/protocol/xdg_system_bell.gen.h"
#include "wayland/protocol/xdg_toplevel_icon.gen.h"

// NOTE: Deprecated.
#include "wayland/protocol/xdg_foreign_v1.gen.h"

// Custom internal protocol.
#include "wayland/protocol/godot_embedding_compositor.gen.h"

#ifdef LIBDECOR_ENABLED
#ifdef SOWRAP_ENABLED
#include "dynwrappers/libdecor-so_wrap.h"
#else
#include <libdecor.h>
#endif // SOWRAP_ENABLED
#endif // LIBDECOR_ENABLED

#include "wayland_embedder.h"

#include "core/input/input_event.h"
#include "core/os/process_id.h"
#include "core/os/thread.h"
#include "servers/display/display_server_enums.h"

class Image;

class WaylandThread {
public:
	struct ColorProfile {
		uint32_t named_primary = WP_COLOR_MANAGER_V1_PRIMARIES_SRGB;
		uint32_t named_transfer_function = WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_GAMMA22;

		// The luminances the compositor recommends.
		float target_min_luminance = 0;
		float target_max_luminance = 0;
		float reference_luminance = 0;
	};

	// Messages used for exchanging information between Godot's and Wayland's thread.
	class Message : public RefCounted {
		GDSOFTCLASS(Message, RefCounted);

	public:
		Message() {}
		virtual ~Message() = default;
	};

	class WindowMessage : public Message {
		GDSOFTCLASS(WindowMessage, Message);

	public:
		DisplayServerEnums::WindowID id = DisplayServerEnums::INVALID_WINDOW_ID;
	};

	// Message data for window rect changes.
	class WindowRectMessage : public WindowMessage {
		GDSOFTCLASS(WindowRectMessage, WindowMessage);

	public:
		// The window size in "absolute units" (pre-scaled). Basically the desired
		// resolution of the underlying buffer.
		Rect2i rect;

		// Used to synchronize buffer size and scale together, otherwise we risk a
		// protocol error. Note that this doesn't control scaling in general, only the
		// concept of "surface buffer scale". See the protocol documentation of
		// `wl_surface::set_buffer_scale` for more details.
		//
		// Defaults to 0 so that we can catch malformed messages.
		int buffer_scale = 0;
	};

	class WindowEventMessage : public WindowMessage {
		GDSOFTCLASS(WindowEventMessage, WindowMessage);

	public:
		DisplayServerEnums::WindowEvent event;
	};

	class WindowHoverMessage : public WindowMessage {
		GDSOFTCLASS(WindowHoverMessage, WindowMessage);
	};

	class InputEventMessage : public Message {
		GDSOFTCLASS(InputEventMessage, Message);

	public:
		Ref<InputEvent> event;
	};

	class DropFilesEventMessage : public WindowMessage {
		GDSOFTCLASS(DropFilesEventMessage, WindowMessage);

	public:
		Vector<String> files;
	};

	class IMEUpdateEventMessage : public WindowMessage {
		GDSOFTCLASS(IMEUpdateEventMessage, WindowMessage);

	public:
		String text;
		Vector2i selection;
	};

	class IMECommitEventMessage : public WindowMessage {
		GDSOFTCLASS(IMECommitEventMessage, WindowMessage);

	public:
		String text;
	};

	class ColorProfileMessage : public WindowMessage {
		GDSOFTCLASS(ColorProfileMessage, WindowMessage);

	public:
		WaylandThread *wayland_thread;
		ColorProfile color_profile;
	};

	struct RegistryState {
		HashSet<uint32_t> global_names;

		WaylandThread *wayland_thread;

		// Core Wayland globals.
		struct wl_shm *wl_shm = nullptr;
		uint32_t wl_shm_name = 0;

		struct wl_compositor *wl_compositor = nullptr;
		uint32_t wl_compositor_name = 0;

		struct wl_subcompositor *wl_subcompositor = nullptr;
		uint32_t wl_subcompositor_name = 0;

		struct wl_data_device_manager *wl_data_device_manager = nullptr;
		uint32_t wl_data_device_manager_name = 0;

		List<struct wl_output *> wl_outputs;
		List<struct wl_seat *> wl_seats;

		// xdg-shell globals.

		struct xdg_wm_base *xdg_wm_base = nullptr;
		uint32_t xdg_wm_base_name = 0;

		// NOTE: Deprecated.
		struct zxdg_exporter_v1 *xdg_exporter_v1 = nullptr;
		uint32_t xdg_exporter_v1_name = 0;

		uint32_t xdg_exporter_v2_name = 0;
		struct zxdg_exporter_v2 *xdg_exporter_v2 = nullptr;

		// wayland-protocols globals.

		struct wp_viewporter *wp_viewporter = nullptr;
		uint32_t wp_viewporter_name = 0;

		struct wp_color_manager_v1 *wp_color_manager = nullptr;
		uint32_t wp_color_manager_name = 0;

		struct wp_fractional_scale_manager_v1 *wp_fractional_scale_manager = nullptr;
		uint32_t wp_fractional_scale_manager_name = 0;

		struct wp_cursor_shape_manager_v1 *wp_cursor_shape_manager = nullptr;
		uint32_t wp_cursor_shape_manager_name = 0;

		struct zxdg_decoration_manager_v1 *xdg_decoration_manager = nullptr;
		uint32_t xdg_decoration_manager_name = 0;

		struct xdg_system_bell_v1 *xdg_system_bell = nullptr;
		uint32_t xdg_system_bell_name = 0;

		struct xdg_toplevel_icon_manager_v1 *xdg_toplevel_icon_manager = nullptr;
		uint32_t xdg_toplevel_icon_manager_name = 0;

		struct xdg_activation_v1 *xdg_activation = nullptr;
		uint32_t xdg_activation_name = 0;

		struct zwp_primary_selection_device_manager_v1 *wp_primary_selection_device_manager = nullptr;
		uint32_t wp_primary_selection_device_manager_name = 0;

		struct zwp_relative_pointer_manager_v1 *wp_relative_pointer_manager = nullptr;
		uint32_t wp_relative_pointer_manager_name = 0;

		struct zwp_pointer_constraints_v1 *wp_pointer_constraints = nullptr;
		uint32_t wp_pointer_constraints_name = 0;

		struct zwp_pointer_gestures_v1 *wp_pointer_gestures = nullptr;
		uint32_t wp_pointer_gestures_name = 0;

		struct zwp_idle_inhibit_manager_v1 *wp_idle_inhibit_manager = nullptr;
		uint32_t wp_idle_inhibit_manager_name = 0;

		struct zwp_tablet_manager_v2 *wp_tablet_manager = nullptr;
		uint32_t wp_tablet_manager_name = 0;

		struct zwp_text_input_manager_v3 *wp_text_input_manager = nullptr;
		uint32_t wp_text_input_manager_name = 0;

		struct wp_pointer_warp_v1 *wp_pointer_warp = nullptr;
		uint32_t wp_pointer_warp_name = 0;

		// We're really not meant to use this one directly but we still need to know
		// whether it's available.
		uint32_t wp_fifo_manager_name = 0;

		struct godot_embedding_compositor *godot_embedding_compositor = nullptr;
		uint32_t godot_embedding_compositor_name = 0;
	};

	// General Wayland-specific states. Shouldn't be accessed directly.
	// TODO: Make private?

	struct WindowState {
		DisplayServerEnums::WindowID id = DisplayServerEnums::INVALID_WINDOW_ID;
		DisplayServerEnums::WindowID parent_id = DisplayServerEnums::INVALID_WINDOW_ID;

		Rect2i rect;
		DisplayServerEnums::WindowMode mode = DisplayServerEnums::WINDOW_MODE_WINDOWED;

		bool ready = false; // Is configured or otherwise ready to be mapped.

		// Toplevel states.
		bool maximized = false; // MUST obey configure size.
		bool fullscreen = false; // Can be smaller than configure size.
		bool resizing = false; // Configure size is a max.
		// No need for `activated` (yet)
		bool tiled_left = false;
		bool tiled_right = false;
		bool tiled_top = false;
		bool tiled_bottom = false;
		bool suspended = false; // We can stop drawing.

		// These are true by default as it isn't guaranteed that we'll find an
		// xdg-shell implementation with wm_capabilities available. If and once we
		// receive a wm_capabilities event these will get reset and updated with
		// whatever the compositor says.
		bool can_minimize = true;
		bool can_maximize = true;
		bool can_fullscreen = true;

		xdg_toplevel_icon_v1 *xdg_icon = nullptr;
		wl_buffer *icon_buffer = nullptr;
		bool icon_set = false;

		HashSet<struct wl_output *> wl_outputs;

		// NOTE: If for whatever reason this callback is destroyed _while_ the event
		// thread is still running, it might be a good idea to set its user data to
		// `nullptr`. From some initial testing of mine, it looks like it might still
		// be called even after being destroyed, pointing to probably invalid window
		// data by then and segfaulting hard.
		struct wl_callback *frame_callback = nullptr;
		uint64_t last_frame_time = 0;

		struct wl_surface *wl_surface = nullptr;
		struct xdg_surface *xdg_surface = nullptr;
		struct xdg_toplevel *xdg_toplevel = nullptr;

		struct wp_viewport *wp_viewport = nullptr;
		struct wp_fractional_scale_v1 *wp_fractional_scale = nullptr;

		// NOTE: Deprecated.
		struct zxdg_exported_v1 *xdg_exported_v1 = nullptr;

		struct zxdg_exported_v2 *xdg_exported_v2 = nullptr;

		struct xdg_popup *xdg_popup = nullptr;

		String exported_handle;

		// Currently applied buffer scale.
		int buffer_scale = 1;

		// NOTE: The preferred buffer scale is currently only dynamically calculated.
		// It can be accessed by calling `window_state_get_preferred_buffer_scale`.

		// NOTE: Popups manually inherit the parent's scale on creation. Make sure to
		// sync them up with any new fields.

		// Override used by the fractional scale add-on object. If less or equal to 0
		// (default) then the normal output-based scale is used instead.
		double fractional_scale = 0;

		// What the compositor is recommending us.
		double preferred_fractional_scale = 0;

		struct wp_color_management_surface_feedback_v1 *wp_color_management_surface_feedback = nullptr;
		struct wp_color_management_surface_v1 *wp_color_management_surface = nullptr;

		struct zxdg_toplevel_decoration_v1 *xdg_toplevel_decoration = nullptr;

		struct zwp_idle_inhibitor_v1 *wp_idle_inhibitor = nullptr;

#ifdef LIBDECOR_ENABLED
		// If this is null the xdg_* variables must be set and vice-versa. This way we
		// can handle this mess gracefully enough to hopefully being able of getting
		// rid of this cleanly once we have our own CSDs.
		struct libdecor_frame *libdecor_frame = nullptr;
		struct libdecor_configuration *pending_libdecor_configuration = nullptr;
#endif

		RegistryState *registry;
		WaylandThread *wayland_thread;
	};

	// "High level" Godot-side screen data.
	struct ScreenData {
		// Geometry data.
		Point2i position;

		String make;
		String model;

		Size2i size;
		Size2i physical_size;

		float refresh_rate = -1;
		int scale = 1;
	};

	struct ScreenState {
		uint32_t wl_output_name = 0;

		ScreenData pending_data;
		ScreenData data;

		WaylandThread *wayland_thread;
	};

	enum class Gesture {
		NONE,
		MAGNIFY,
	};

	enum class PointerConstraint {
		NONE,
		LOCKED,
		CONFINED,
	};

	struct PointerData {
		Point2 position;
		uint32_t motion_time = 0;

		// Relative motion has its own optional event and so needs its own time.
		Vector2 relative_motion;
		uint32_t relative_motion_time = 0;

		BitField<MouseButtonMask> pressed_button_mask = MouseButtonMask::NONE;

		MouseButton last_button_pressed = MouseButton::NONE;
		Point2 last_pressed_position;

		DisplayServerEnums::WindowID pointed_id = DisplayServerEnums::INVALID_WINDOW_ID;
		DisplayServerEnums::WindowID last_pointed_id = DisplayServerEnums::INVALID_WINDOW_ID;

		// This is needed to check for a new double click every time.
		bool double_click_begun = false;

		uint32_t button_time = 0;
		uint32_t button_serial = 0;

		uint32_t scroll_type = WL_POINTER_AXIS_SOURCE_WHEEL;

		// The amount "scrolled" in pixels, in each direction.
		Vector2 scroll_vector;

		// The amount of scroll "clicks" in each direction, in fractions of 120.
		Vector2i discrete_scroll_vector_120;

		uint32_t pinch_scale = 1;
	};

	struct TabletToolData {
		Point2 position;
		Vector2 tilt;
		uint32_t pressure = 0;

		BitField<MouseButtonMask> pressed_button_mask = MouseButtonMask::NONE;

		MouseButton last_button_pressed = MouseButton::NONE;
		Point2 last_pressed_position;

		bool double_click_begun = false;

		uint64_t button_time = 0;
		uint64_t motion_time = 0;

		DisplayServerEnums::WindowID proximal_id = DisplayServerEnums::INVALID_WINDOW_ID;
		DisplayServerEnums::WindowID last_proximal_id = DisplayServerEnums::INVALID_WINDOW_ID;
		uint32_t proximity_serial = 0;
	};

	struct TabletToolState {
		struct wl_seat *wl_seat = nullptr;

		bool is_eraser = false;

		TabletToolData data_pending;
		TabletToolData data;
	};

	struct OfferState {
		HashSet<String> mime_types;
	};

	struct TouchPoint {
		DisplayServerEnums::WindowID touched_id = DisplayServerEnums::INVALID_WINDOW_ID;
		Point2 position;
		uint32_t down_time = 0;
		uint32_t motion_time = 0;
	};

	struct SeatState {
		RegistryState *registry = nullptr;

		WaylandThread *wayland_thread = nullptr;

		struct wl_seat *wl_seat = nullptr;
		uint32_t wl_seat_name = 0;

		// Pointer.
		struct wl_pointer *wl_pointer = nullptr;

		uint32_t pointer_enter_serial = 0;

		struct wp_cursor_shape_device_v1 *wp_cursor_shape_device = nullptr;

		struct zwp_relative_pointer_v1 *wp_relative_pointer = nullptr;
		struct zwp_locked_pointer_v1 *wp_locked_pointer = nullptr;
		struct zwp_confined_pointer_v1 *wp_confined_pointer = nullptr;

		struct zwp_pointer_gesture_pinch_v1 *wp_pointer_gesture_pinch = nullptr;

		// NOTE: According to the wp_pointer_gestures protocol specification, there
		// can be only one active gesture at a time.
		Gesture active_gesture = Gesture::NONE;

		// Used for delta calculations.
		// NOTE: The wp_pointer_gestures protocol keeps track of the total scale of
		// the pinch gesture, while godot instead wants its delta.
		wl_fixed_t old_pinch_scale = 0;

		struct wl_surface *cursor_surface = nullptr;
		struct wl_callback *cursor_frame_callback = nullptr;
		uint32_t cursor_time_ms = 0;

		// This variable is needed to buffer all pointer changes until a
		// wl_pointer.frame event, as per Wayland's specification. Everything is
		// first set in `data_buffer` and then `data` is set with its contents on
		// an input frame event. All methods should generally read from
		// `pointer_data` and write to `data_buffer`.
		PointerData pointer_data_buffer;
		PointerData pointer_data;

		// Keyboard.
		struct wl_keyboard *wl_keyboard = nullptr;

		// For key events.
		DisplayServerEnums::WindowID focused_id = DisplayServerEnums::INVALID_WINDOW_ID;

		struct xkb_context *xkb_context = nullptr;
		struct xkb_keymap *xkb_keymap = nullptr;
		struct xkb_state *xkb_state = nullptr;
		struct xkb_compose_state *xkb_compose_state = nullptr;
		struct xkb_compose_table *xkb_compose_table = nullptr;

		const char *keymap_buffer = nullptr;
		uint32_t keymap_buffer_size = 0;

		HashMap<xkb_keycode_t, Key> pressed_keycodes;

		xkb_layout_index_t current_layout_index = 0;

		// Clients with `wl_seat`s older than version 4 do not support
		// `wl_keyboard::repeat_info`, so we'll provide a reasonable default of 25
		// keys per second, with a start delay of 600 milliseconds.
		int32_t repeat_key_delay_msec = 1000 / 25;
		int32_t repeat_start_delay_msec = 600;

		xkb_keycode_t repeating_keycode = XKB_KEYCODE_INVALID;
		uint64_t last_repeat_start_msec = 0;
		uint64_t last_repeat_msec = 0;

		uint32_t mods_depressed = 0;
		uint32_t mods_latched = 0;
		uint32_t mods_locked = 0;

		bool shift_pressed = false;
		bool ctrl_pressed = false;
		bool alt_pressed = false;
		bool meta_pressed = false;

		uint32_t last_key_pressed_serial = 0;

		struct wl_data_device *wl_data_device = nullptr;

		// Touch.
		struct wl_touch *wl_touch = nullptr;
		AHashMap<int32_t, TouchPoint> touch_points_buffer;
		AHashMap<int32_t, TouchPoint> touch_points;
		int32_t last_touch_id = -1;

		// Drag and drop.
		DisplayServerEnums::WindowID dnd_id = DisplayServerEnums::INVALID_WINDOW_ID;
		struct wl_data_offer *wl_data_offer_dnd = nullptr;
		uint32_t dnd_enter_serial = 0;

		// Clipboard.
		struct wl_data_source *wl_data_source_selection = nullptr;
		Vector<uint8_t> selection_data;

		struct wl_data_offer *wl_data_offer_selection = nullptr;

		// Primary selection.
		struct zwp_primary_selection_device_v1 *wp_primary_selection_device = nullptr;

		struct zwp_primary_selection_source_v1 *wp_primary_selection_source = nullptr;
		Vector<uint8_t> primary_data;

		struct zwp_primary_selection_offer_v1 *wp_primary_selection_offer = nullptr;

		// Tablet.
		struct zwp_tablet_seat_v2 *wp_tablet_seat = nullptr;

		List<struct zwp_tablet_tool_v2 *> tablet_tools;

		// IME.
		struct zwp_text_input_v3 *wp_text_input = nullptr;
		DisplayServerEnums::WindowID ime_window_id = DisplayServerEnums::INVALID_WINDOW_ID;
		bool ime_enabled = false;
		bool ime_active = false;
		String ime_text;
		String ime_text_commit;
		Vector2i ime_cursor;
		Rect2i ime_rect;
	};

	struct CustomCursor {
		struct wl_buffer *wl_buffer = nullptr;
		uint32_t *buffer_data = nullptr;
		uint32_t buffer_data_size = 0;

		Point2i hotspot;
	};

	struct ColorManagementState {
		// Compositor features.
		uint32_t supported_render_intents = 0;
		uint32_t supported_render_feature = 0;
		uint32_t supported_transfer_function = 0;
		uint32_t supported_primaries = 0;
		bool supports_hdr = false;
	};

	struct EmbeddingCompositorState {
		LocalVector<struct godot_embedded_client *> clients;

		// Only a client per PID can create a window.
		HashMap<int, struct godot_embedded_client *> mapped_clients;

		ProcessID focused_pid = -1;
	};

	struct EmbeddedClientState {
		struct godot_embedding_compositor *embedding_compositor = nullptr;

		uint32_t pid = 0;
		bool window_mapped = false;
	};

private:
	struct ThreadData {
		SafeFlag thread_done;
		Mutex mutex;

		struct wl_display *wl_display = nullptr;
	};

	// FIXME: Is this the right thing to do?
	inline static const char *proxy_tag = "godot";

	Thread events_thread;
	ThreadData thread_data;

	HashMap<DisplayServerEnums::WindowID, WindowState> windows;

	List<Ref<Message>> messages;

	xdg_toplevel_icon_v1 *xdg_icon = nullptr;
	wl_buffer *icon_buffer = nullptr;

	String cursor_theme_name;
	int unscaled_cursor_size = 24;

	// NOTE: Regarding screen scale handling, the cursor cache is currently
	// "static", by which I mean that we try to change it as little as possible and
	// thus will be as big as the largest screen. This is mainly due to the fact
	// that doing it dynamically doesn't look like it's worth it to me currently,
	// especially as usually screen scales don't change continuously.
	int cursor_scale = 1;

	// Use cursor-shape-v1 protocol if the compositor supports it.
	wp_cursor_shape_device_v1_shape standard_cursors[DisplayServerEnums::CURSOR_MAX] = {
		wp_cursor_shape_device_v1_shape::WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DEFAULT, //DisplayServerEnums::CURSOR_ARROW
		wp_cursor_shape_device_v1_shape::WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_TEXT, //DisplayServerEnums::CURSOR_IBEAM
		wp_cursor_shape_device_v1_shape::WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_POINTER, //DisplayServerEnums::CURSOR_POINTING_HAND
		wp_cursor_shape_device_v1_shape::WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_CROSSHAIR, //DisplayServerEnums::CURSOR_CROSS
		wp_cursor_shape_device_v1_shape::WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_WAIT, //DisplayServerEnums::CURSOR_WAIT
		wp_cursor_shape_device_v1_shape::WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_PROGRESS, //DisplayServerEnums::CURSOR_BUSY
		wp_cursor_shape_device_v1_shape::WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_GRAB, //DisplayServerEnums::CURSOR_DRAG
		wp_cursor_shape_device_v1_shape::WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_GRABBING, //DisplayServerEnums::CURSOR_CAN_DROP
		wp_cursor_shape_device_v1_shape::WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NO_DROP, //DisplayServerEnums::CURSOR_FORBIDDEN
		wp_cursor_shape_device_v1_shape::WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NS_RESIZE, //DisplayServerEnums::CURSOR_VSIZE
		wp_cursor_shape_device_v1_shape::WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_EW_RESIZE, //DisplayServerEnums::CURSOR_HSIZE
		wp_cursor_shape_device_v1_shape::WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NESW_RESIZE, //DisplayServerEnums::CURSOR_BDIAGSIZE
		wp_cursor_shape_device_v1_shape::WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NWSE_RESIZE, //DisplayServerEnums::CURSOR_FDIAGSIZE
		wp_cursor_shape_device_v1_shape::WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_MOVE, //DisplayServerEnums::CURSOR_MOVE
		wp_cursor_shape_device_v1_shape::WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_ROW_RESIZE, //DisplayServerEnums::CURSOR_VSPLIT
		wp_cursor_shape_device_v1_shape::WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_COL_RESIZE, //DisplayServerEnums::CURSOR_HSPLIT
		wp_cursor_shape_device_v1_shape::WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_HELP, //DisplayServerEnums::CURSOR_HELP
	};

	// Fallback to reading $XCURSOR and system themes if the compositor does not.
	struct wl_cursor_theme *wl_cursor_theme = nullptr;
	struct wl_cursor *wl_cursors[DisplayServerEnums::CURSOR_MAX] = {};

	// User-defined cursor overrides. Take precedence over standard and wl cursors.
	HashMap<DisplayServerEnums::CursorShape, CustomCursor> custom_cursors;

	DisplayServerEnums::CursorShape cursor_shape = DisplayServerEnums::CURSOR_ARROW;
	bool cursor_visible = true;

	PointerConstraint pointer_constraint = PointerConstraint::NONE;

	struct wl_display *wl_display = nullptr;
	struct wl_registry *wl_registry = nullptr;

	struct wl_seat *wl_seat_current = nullptr;
	bool has_touch = false;

	// We got plenty of different pointing devices but Godot can only hover a
	// single window at a time. This helps track that and gives us a way to avoid
	// invalid mouse enter/leave event combinations.
	DisplayServerEnums::WindowID hovered_window_id = DisplayServerEnums::INVALID_WINDOW_ID;

	bool frame = true;

	RegistryState registry;

	bool initialized = false;

#ifdef TOOLS_ENABLED
	WaylandEmbedder embedder;
#endif

#ifdef LIBDECOR_ENABLED
	struct libdecor *libdecor_context = nullptr;
#endif // LIBDECOR_ENABLED

	static void _clipboard_send(Vector<uint8_t> &p_data, const char *p_media_type, int32_t p_fd);

	// Main polling method.
	static void _poll_events_thread(void *p_data);

	// Core Wayland event handlers.
	static void _wl_registry_on_global(void *data, struct wl_registry *wl_registry, uint32_t name, const char *interface, uint32_t version);
	static void _wl_registry_on_global_remove(void *data, struct wl_registry *wl_registry, uint32_t name);

	static void _wl_surface_on_enter(void *data, struct wl_surface *wl_surface, struct wl_output *wl_output);
	static void _wl_surface_on_leave(void *data, struct wl_surface *wl_surface, struct wl_output *wl_output);
	static void _wl_surface_on_preferred_buffer_scale(void *data, struct wl_surface *wl_surface, int32_t factor);
	static void _wl_surface_on_preferred_buffer_transform(void *data, struct wl_surface *wl_surface, uint32_t transform);

	static void _frame_wl_callback_on_done(void *data, struct wl_callback *wl_callback, uint32_t callback_data);

	static void _wl_output_on_geometry(void *data, struct wl_output *wl_output, int32_t x, int32_t y, int32_t physical_width, int32_t physical_height, int32_t subpixel, const char *make, const char *model, int32_t transform);
	static void _wl_output_on_mode(void *data, struct wl_output *wl_output, uint32_t flags, int32_t width, int32_t height, int32_t refresh);
	static void _wl_output_on_done(void *data, struct wl_output *wl_output);
	static void _wl_output_on_scale(void *data, struct wl_output *wl_output, int32_t factor);
	static void _wl_output_on_name(void *data, struct wl_output *wl_output, const char *name);
	static void _wl_output_on_description(void *data, struct wl_output *wl_output, const char *description);

	static void _wl_seat_on_capabilities(void *data, struct wl_seat *wl_seat, uint32_t capabilities);
	static void _wl_seat_on_name(void *data, struct wl_seat *wl_seat, const char *name);

	static void _cursor_frame_callback_on_done(void *data, struct wl_callback *wl_callback, uint32_t time_ms);

	static void _wl_pointer_on_enter(void *data, struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface, wl_fixed_t surface_x, wl_fixed_t surface_y);
	static void _wl_pointer_on_leave(void *data, struct wl_pointer *wl_pointer, uint32_t serial, struct wl_surface *surface);
	static void _wl_pointer_on_motion(void *data, struct wl_pointer *wl_pointer, uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y);
	static void _wl_pointer_on_button(void *data, struct wl_pointer *wl_pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t state);
	static void _wl_pointer_on_axis(void *data, struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis, wl_fixed_t value);
	static void _wl_pointer_on_frame(void *data, struct wl_pointer *wl_pointer);
	static void _wl_pointer_on_axis_source(void *data, struct wl_pointer *wl_pointer, uint32_t axis_source);
	static void _wl_pointer_on_axis_stop(void *data, struct wl_pointer *wl_pointer, uint32_t time, uint32_t axis);
	static void _wl_pointer_on_axis_discrete(void *data, struct wl_pointer *wl_pointer, uint32_t axis, int32_t discrete);
	static void _wl_pointer_on_axis_value120(void *data, struct wl_pointer *wl_pointer, uint32_t axis, int32_t value120);
	static void _wl_pointer_on_axis_relative_direction(void *data, struct wl_pointer *wl_pointer, uint32_t axis, uint32_t direction);

	static void _wl_keyboard_on_keymap(void *data, struct wl_keyboard *wl_keyboard, uint32_t format, int32_t fd, uint32_t size);
	static void _wl_keyboard_on_enter(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, struct wl_surface *surface, struct wl_array *keys);
	static void _wl_keyboard_on_leave(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, struct wl_surface *surface);
	static void _wl_keyboard_on_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state);
	static void _wl_keyboard_on_modifiers(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, uint32_t group);
	static void _wl_keyboard_on_repeat_info(void *data, struct wl_keyboard *wl_keyboard, int32_t rate, int32_t delay);

	static void _wl_touch_on_down(void *data, struct wl_touch *wl_touch, uint32_t serial, uint32_t time, struct wl_surface *surface, int32_t id, wl_fixed_t x, wl_fixed_t y);
	static void _wl_touch_on_up(void *data, struct wl_touch *wl_touch, uint32_t serial, uint32_t time, int32_t id);
	static void _wl_touch_on_motion(void *data, struct wl_touch *wl_touch, uint32_t time, int32_t id, wl_fixed_t x, wl_fixed_t y);
	static void _wl_touch_on_frame(void *data, struct wl_touch *wl_touch);
	static void _wl_touch_on_cancel(void *data, struct wl_touch *wl_touch);
	static void _wl_touch_on_shape(void *data, struct wl_touch *wl_touch, int32_t id, wl_fixed_t major, wl_fixed_t minor);
	static void _wl_touch_on_orientation(void *data, struct wl_touch *wl_touch, int32_t id, wl_fixed_t orientation);

	static void _wl_data_device_on_data_offer(void *data, struct wl_data_device *wl_data_device, struct wl_data_offer *id);
	static void _wl_data_device_on_enter(void *data, struct wl_data_device *wl_data_device, uint32_t serial, struct wl_surface *surface, wl_fixed_t x, wl_fixed_t y, struct wl_data_offer *id);
	static void _wl_data_device_on_leave(void *data, struct wl_data_device *wl_data_device);
	static void _wl_data_device_on_motion(void *data, struct wl_data_device *wl_data_device, uint32_t time, wl_fixed_t x, wl_fixed_t y);
	static void _wl_data_device_on_drop(void *data, struct wl_data_device *wl_data_device);
	static void _wl_data_device_on_selection(void *data, struct wl_data_device *wl_data_device, struct wl_data_offer *id);

	static void _wl_data_offer_on_offer(void *data, struct wl_data_offer *wl_data_offer, const char *mime_type);
	static void _wl_data_offer_on_source_actions(void *data, struct wl_data_offer *wl_data_offer, uint32_t source_actions);
	static void _wl_data_offer_on_action(void *data, struct wl_data_offer *wl_data_offer, uint32_t dnd_action);

	static void _wl_data_source_on_target(void *data, struct wl_data_source *wl_data_source, const char *mime_type);
	static void _wl_data_source_on_send(void *data, struct wl_data_source *wl_data_source, const char *mime_type, int32_t fd);
	static void _wl_data_source_on_cancelled(void *data, struct wl_data_source *wl_data_source);
	static void _wl_data_source_on_dnd_drop_performed(void *data, struct wl_data_source *wl_data_source);
	static void _wl_data_source_on_dnd_finished(void *data, struct wl_data_source *wl_data_source);
	static void _wl_data_source_on_action(void *data, struct wl_data_source *wl_data_source, uint32_t dnd_action);

	// xdg-shell event handlers.
	static void _xdg_wm_base_on_ping(void *data, struct xdg_wm_base *xdg_wm_base, uint32_t serial);

	static void _xdg_surface_on_configure(void *data, struct xdg_surface *xdg_surface, uint32_t serial);

	static void _xdg_toplevel_on_configure(void *data, struct xdg_toplevel *xdg_toplevel, int32_t width, int32_t height, struct wl_array *states);
	static void _xdg_toplevel_on_close(void *data, struct xdg_toplevel *xdg_toplevel);
	static void _xdg_toplevel_on_configure_bounds(void *data, struct xdg_toplevel *xdg_toplevel, int32_t width, int32_t height);
	static void _xdg_toplevel_on_wm_capabilities(void *data, struct xdg_toplevel *xdg_toplevel, struct wl_array *capabilities);

	static void _xdg_popup_on_configure(void *data, struct xdg_popup *xdg_popup, int32_t x, int32_t y, int32_t width, int32_t height);
	static void _xdg_popup_on_popup_done(void *data, struct xdg_popup *xdg_popup);
	static void _xdg_popup_on_repositioned(void *data, struct xdg_popup *xdg_popup, uint32_t token);

	// wayland-protocols event handlers.
	static void _wp_color_manager_on_supported_intent(void *data, struct wp_color_manager_v1 *wp_color_manager_v1, uint32_t render_intent);
	static void _wp_color_manager_on_supported_feature(void *data, struct wp_color_manager_v1 *wp_color_manager_v1, uint32_t feature);
	static void _wp_color_manager_on_supported_tf_named(void *data, struct wp_color_manager_v1 *wp_color_manager_v1, uint32_t tf);
	static void _wp_color_manager_on_supported_primaries_named(void *data, struct wp_color_manager_v1 *wp_color_manager_v1, uint32_t primaries);
	static void _wp_color_manager_on_done(void *data, struct wp_color_manager_v1 *wp_color_manager_v1);

	static void _wp_color_management_surface_feedback_on_preferred_changed(void *data, struct wp_color_management_surface_feedback_v1 *wp_color_management_surface_feedback_v1, uint32_t identity);
	static void _wp_color_management_surface_feedback_on_preferred_changed2(void *data, struct wp_color_management_surface_feedback_v1 *wp_color_management_surface_feedback_v1, uint32_t identity_high, uint32_t identity_low);

	static void _wp_image_description_on_failed(void *data, struct wp_image_description_v1 *wp_image_description_v1, uint32_t cause, const char *msg);
	static void _wp_image_description_on_ready(void *data, struct wp_image_description_v1 *wp_image_description_v1, uint32_t identity);
	static void _wp_image_description_on_ready2(void *data, struct wp_image_description_v1 *wp_image_description_v1, uint32_t identity_high, uint32_t indentity_low);

	static void _wp_image_description_info_on_done(void *data, struct wp_image_description_info_v1 *wp_image_description_info_v1);
	static void _wp_image_description_info_on_icc_file(void *data, struct wp_image_description_info_v1 *wp_image_description_info_v1, int32_t icc, uint32_t icc_size);
	static void _wp_image_description_info_on_primaries(void *data, struct wp_image_description_info_v1 *wp_image_description_info_v1, int32_t r_x, int32_t r_y, int32_t g_x, int32_t g_y, int32_t b_x, int32_t b_y, int32_t w_x, int32_t w_y);
	static void _wp_image_description_info_on_primaries_named(void *data, struct wp_image_description_info_v1 *wp_image_description_info_v1, uint32_t primaries);
	static void _wp_image_description_info_on_tf_power(void *data, struct wp_image_description_info_v1 *wp_image_description_info_v1, uint32_t eexp);
	static void _wp_image_description_info_on_tf_named(void *data, struct wp_image_description_info_v1 *wp_image_description_info_v1, uint32_t tf);
	static void _wp_image_description_info_on_luminances(void *data, struct wp_image_description_info_v1 *wp_image_description_info_v1, uint32_t min_lum, uint32_t max_lum, uint32_t reference_lum);
	static void _wp_image_description_info_on_target_primaries(void *data, struct wp_image_description_info_v1 *wp_image_description_info_v1, int32_t r_x, int32_t r_y, int32_t g_x, int32_t g_y, int32_t b_x, int32_t b_y, int32_t w_x, int32_t w_y);
	static void _wp_image_description_info_on_target_luminance(void *data, struct wp_image_description_info_v1 *wp_image_description_info_v1, uint32_t min_lum, uint32_t max_lum);
	static void _wp_image_description_info_on_target_max_cll(void *data, struct wp_image_description_info_v1 *wp_image_description_info_v1, uint32_t max_cll);
	static void _wp_image_description_info_on_target_max_fall(void *data, struct wp_image_description_info_v1 *wp_image_description_info_v1, uint32_t max_fall);

	static void _wp_fractional_scale_on_preferred_scale(void *data, struct wp_fractional_scale_v1 *wp_fractional_scale_v1, uint32_t scale);

	static void _wp_relative_pointer_on_relative_motion(void *data, struct zwp_relative_pointer_v1 *wp_relative_pointer_v1, uint32_t uptime_hi, uint32_t uptime_lo, wl_fixed_t dx, wl_fixed_t dy, wl_fixed_t dx_unaccel, wl_fixed_t dy_unaccel);

	static void _wp_pointer_gesture_pinch_on_begin(void *data, struct zwp_pointer_gesture_pinch_v1 *wp_pointer_gesture_pinch_v1, uint32_t serial, uint32_t time, struct wl_surface *surface, uint32_t fingers);
	static void _wp_pointer_gesture_pinch_on_update(void *data, struct zwp_pointer_gesture_pinch_v1 *wp_pointer_gesture_pinch_v1, uint32_t time, wl_fixed_t dx, wl_fixed_t dy, wl_fixed_t scale, wl_fixed_t rotation);
	static void _wp_pointer_gesture_pinch_on_end(void *data, struct zwp_pointer_gesture_pinch_v1 *zp_pointer_gesture_pinch_v1, uint32_t serial, uint32_t time, int32_t cancelled);

	static void _wp_primary_selection_device_on_data_offer(void *data, struct zwp_primary_selection_device_v1 *wp_primary_selection_device_v1, struct zwp_primary_selection_offer_v1 *offer);
	static void _wp_primary_selection_device_on_selection(void *data, struct zwp_primary_selection_device_v1 *wp_primary_selection_device_v1, struct zwp_primary_selection_offer_v1 *id);

	static void _wp_primary_selection_offer_on_offer(void *data, struct zwp_primary_selection_offer_v1 *wp_primary_selection_offer_v1, const char *mime_type);

	static void _wp_primary_selection_source_on_send(void *data, struct zwp_primary_selection_source_v1 *wp_primary_selection_source_v1, const char *mime_type, int32_t fd);
	static void _wp_primary_selection_source_on_cancelled(void *data, struct zwp_primary_selection_source_v1 *wp_primary_selection_source_v1);

	static void _wp_tablet_seat_on_tablet_added(void *data, struct zwp_tablet_seat_v2 *wp_tablet_seat_v2, struct zwp_tablet_v2 *id);
	static void _wp_tablet_seat_on_tool_added(void *data, struct zwp_tablet_seat_v2 *wp_tablet_seat_v2, struct zwp_tablet_tool_v2 *id);
	static void _wp_tablet_seat_on_pad_added(void *data, struct zwp_tablet_seat_v2 *wp_tablet_seat_v2, struct zwp_tablet_pad_v2 *id);

	static void _wp_tablet_tool_on_type(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t tool_type);
	static void _wp_tablet_tool_on_hardware_serial(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t hardware_serial_hi, uint32_t hardware_serial_lo);
	static void _wp_tablet_tool_on_hardware_id_wacom(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t hardware_id_hi, uint32_t hardware_id_lo);
	static void _wp_tablet_tool_on_capability(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t capability);
	static void _wp_tablet_tool_on_done(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2);
	static void _wp_tablet_tool_on_removed(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2);
	static void _wp_tablet_tool_on_proximity_in(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t serial, struct zwp_tablet_v2 *tablet, struct wl_surface *surface);
	static void _wp_tablet_tool_on_proximity_out(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2);
	static void _wp_tablet_tool_on_down(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t serial);
	static void _wp_tablet_tool_on_up(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2);
	static void _wp_tablet_tool_on_motion(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, wl_fixed_t x, wl_fixed_t y);
	static void _wp_tablet_tool_on_pressure(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t pressure);
	static void _wp_tablet_tool_on_distance(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t distance);
	static void _wp_tablet_tool_on_tilt(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, wl_fixed_t tilt_x, wl_fixed_t tilt_y);
	static void _wp_tablet_tool_on_rotation(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, wl_fixed_t degrees);
	static void _wp_tablet_tool_on_slider(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, int32_t position);
	static void _wp_tablet_tool_on_wheel(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, wl_fixed_t degrees, int32_t clicks);
	static void _wp_tablet_tool_on_button(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t serial, uint32_t button, uint32_t state);
	static void _wp_tablet_tool_on_frame(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t time);

	static void _wp_text_input_on_enter(void *data, struct zwp_text_input_v3 *wp_text_input_v3, struct wl_surface *surface);
	static void _wp_text_input_on_leave(void *data, struct zwp_text_input_v3 *wp_text_input_v3, struct wl_surface *surface);
	static void _wp_text_input_on_preedit_string(void *data, struct zwp_text_input_v3 *wp_text_input_v3, const char *text, int32_t cursor_begin, int32_t cursor_end);
	static void _wp_text_input_on_commit_string(void *data, struct zwp_text_input_v3 *wp_text_input_v3, const char *text);
	static void _wp_text_input_on_delete_surrounding_text(void *data, struct zwp_text_input_v3 *wp_text_input_v3, uint32_t before_length, uint32_t after_length);
	static void _wp_text_input_on_done(void *data, struct zwp_text_input_v3 *wp_text_input_v3, uint32_t serial);

	static void _xdg_toplevel_decoration_on_configure(void *data, struct zxdg_toplevel_decoration_v1 *xdg_toplevel_decoration, uint32_t mode);

	// NOTE: Deprecated.
	static void _xdg_exported_v1_on_handle(void *data, zxdg_exported_v1 *exported, const char *handle);

	static void _xdg_exported_v2_on_handle(void *data, zxdg_exported_v2 *exported, const char *handle);

	static void _xdg_activation_token_on_done(void *data, struct xdg_activation_token_v1 *xdg_activation_token, const char *token);

	static void _godot_embedding_compositor_on_client(void *data, struct godot_embedding_compositor *godot_embedding_compositor, struct godot_embedded_client *godot_embedded_client, int32_t pid);

	static void _godot_embedded_client_on_disconnected(void *data, struct godot_embedded_client *godot_embedded_client);
	static void _godot_embedded_client_on_window_embedded(void *data, struct godot_embedded_client *godot_embedded_client);
	static void _godot_embedded_client_on_window_focus_in(void *data, struct godot_embedded_client *godot_embedded_client);
	static void _godot_embedded_client_on_window_focus_out(void *data, struct godot_embedded_client *godot_embedded_client);

	// Core Wayland event listeners.
	static constexpr struct wl_registry_listener wl_registry_listener = {
		.global = _wl_registry_on_global,
		.global_remove = _wl_registry_on_global_remove,
	};

	static constexpr struct wl_surface_listener wl_surface_listener = {
		.enter = _wl_surface_on_enter,
		.leave = _wl_surface_on_leave,
		.preferred_buffer_scale = _wl_surface_on_preferred_buffer_scale,
		.preferred_buffer_transform = _wl_surface_on_preferred_buffer_transform,
	};

	static constexpr struct wl_callback_listener frame_wl_callback_listener = {
		.done = _frame_wl_callback_on_done,
	};

	static constexpr struct wl_output_listener wl_output_listener = {
		.geometry = _wl_output_on_geometry,
		.mode = _wl_output_on_mode,
		.done = _wl_output_on_done,
		.scale = _wl_output_on_scale,
		.name = _wl_output_on_name,
		.description = _wl_output_on_description,
	};

	static constexpr struct wl_seat_listener wl_seat_listener = {
		.capabilities = _wl_seat_on_capabilities,
		.name = _wl_seat_on_name,
	};

	static constexpr struct wl_callback_listener cursor_frame_callback_listener = {
		.done = _cursor_frame_callback_on_done,
	};

	static constexpr struct wl_pointer_listener wl_pointer_listener = {
		.enter = _wl_pointer_on_enter,
		.leave = _wl_pointer_on_leave,
		.motion = _wl_pointer_on_motion,
		.button = _wl_pointer_on_button,
		.axis = _wl_pointer_on_axis,
		.frame = _wl_pointer_on_frame,
		.axis_source = _wl_pointer_on_axis_source,
		.axis_stop = _wl_pointer_on_axis_stop,
		.axis_discrete = _wl_pointer_on_axis_discrete,
		.axis_value120 = _wl_pointer_on_axis_value120,
		.axis_relative_direction = _wl_pointer_on_axis_relative_direction,
	};

	static constexpr struct wl_keyboard_listener wl_keyboard_listener = {
		.keymap = _wl_keyboard_on_keymap,
		.enter = _wl_keyboard_on_enter,
		.leave = _wl_keyboard_on_leave,
		.key = _wl_keyboard_on_key,
		.modifiers = _wl_keyboard_on_modifiers,
		.repeat_info = _wl_keyboard_on_repeat_info,
	};

	static constexpr struct wl_touch_listener wl_touch_listener = {
		.down = _wl_touch_on_down,
		.up = _wl_touch_on_up,
		.motion = _wl_touch_on_motion,
		.frame = _wl_touch_on_frame,
		.cancel = _wl_touch_on_cancel,
		.shape = _wl_touch_on_shape,
		.orientation = _wl_touch_on_orientation,
	};

	static constexpr struct wl_data_device_listener wl_data_device_listener = {
		.data_offer = _wl_data_device_on_data_offer,
		.enter = _wl_data_device_on_enter,
		.leave = _wl_data_device_on_leave,
		.motion = _wl_data_device_on_motion,
		.drop = _wl_data_device_on_drop,
		.selection = _wl_data_device_on_selection,
	};

	static constexpr struct wl_data_offer_listener wl_data_offer_listener = {
		.offer = _wl_data_offer_on_offer,
		.source_actions = _wl_data_offer_on_source_actions,
		.action = _wl_data_offer_on_action,
	};

	static constexpr struct wl_data_source_listener wl_data_source_listener = {
		.target = _wl_data_source_on_target,
		.send = _wl_data_source_on_send,
		.cancelled = _wl_data_source_on_cancelled,
		.dnd_drop_performed = _wl_data_source_on_dnd_drop_performed,
		.dnd_finished = _wl_data_source_on_dnd_finished,
		.action = _wl_data_source_on_action,
	};

	// xdg-shell event listeners.
	static constexpr struct xdg_wm_base_listener xdg_wm_base_listener = {
		.ping = _xdg_wm_base_on_ping,
	};

	static constexpr struct xdg_surface_listener xdg_surface_listener = {
		.configure = _xdg_surface_on_configure,
	};

	static constexpr struct xdg_toplevel_listener xdg_toplevel_listener = {
		.configure = _xdg_toplevel_on_configure,
		.close = _xdg_toplevel_on_close,
		.configure_bounds = _xdg_toplevel_on_configure_bounds,
		.wm_capabilities = _xdg_toplevel_on_wm_capabilities,
	};

	static constexpr struct xdg_popup_listener xdg_popup_listener = {
		.configure = _xdg_popup_on_configure,
		.popup_done = _xdg_popup_on_popup_done,
		.repositioned = _xdg_popup_on_repositioned,
	};

	// wayland-protocols event listeners.
	static constexpr struct wp_color_manager_v1_listener wp_color_manager_listener = {
		.supported_intent = _wp_color_manager_on_supported_intent,
		.supported_feature = _wp_color_manager_on_supported_feature,
		.supported_tf_named = _wp_color_manager_on_supported_tf_named,
		.supported_primaries_named = _wp_color_manager_on_supported_primaries_named,
		.done = _wp_color_manager_on_done,
	};

	static constexpr struct wp_color_management_surface_feedback_v1_listener wp_color_management_surface_feedback_listener = {
		.preferred_changed = _wp_color_management_surface_feedback_on_preferred_changed,
		.preferred_changed2 = _wp_color_management_surface_feedback_on_preferred_changed2,
	};

	static constexpr struct wp_image_description_v1_listener wp_image_description_listener = {
		.failed = _wp_image_description_on_failed,
		.ready = _wp_image_description_on_ready,
		.ready2 = _wp_image_description_on_ready2,
	};

	static constexpr struct wp_image_description_info_v1_listener wp_image_description_info_listener = {
		.done = _wp_image_description_info_on_done,
		.icc_file = _wp_image_description_info_on_icc_file,
		.primaries = _wp_image_description_info_on_primaries,
		.primaries_named = _wp_image_description_info_on_primaries_named,
		.tf_power = _wp_image_description_info_on_tf_power,
		.tf_named = _wp_image_description_info_on_tf_named,
		.luminances = _wp_image_description_info_on_luminances,
		.target_primaries = _wp_image_description_info_on_target_primaries,
		.target_luminance = _wp_image_description_info_on_target_luminance,
		.target_max_cll = _wp_image_description_info_on_target_max_cll,
		.target_max_fall = _wp_image_description_info_on_target_max_fall,
	};

	static constexpr struct wp_fractional_scale_v1_listener wp_fractional_scale_listener = {
		.preferred_scale = _wp_fractional_scale_on_preferred_scale,
	};

	static constexpr struct zwp_relative_pointer_v1_listener wp_relative_pointer_listener = {
		.relative_motion = _wp_relative_pointer_on_relative_motion,
	};

	static constexpr struct zwp_pointer_gesture_pinch_v1_listener wp_pointer_gesture_pinch_listener = {
		.begin = _wp_pointer_gesture_pinch_on_begin,
		.update = _wp_pointer_gesture_pinch_on_update,
		.end = _wp_pointer_gesture_pinch_on_end,
	};

	static constexpr struct zwp_primary_selection_device_v1_listener wp_primary_selection_device_listener = {
		.data_offer = _wp_primary_selection_device_on_data_offer,
		.selection = _wp_primary_selection_device_on_selection,
	};

	static constexpr struct zwp_primary_selection_offer_v1_listener wp_primary_selection_offer_listener = {
		.offer = _wp_primary_selection_offer_on_offer,
	};

	static constexpr struct zwp_primary_selection_source_v1_listener wp_primary_selection_source_listener = {
		.send = _wp_primary_selection_source_on_send,
		.cancelled = _wp_primary_selection_source_on_cancelled,
	};

	static constexpr struct zwp_tablet_seat_v2_listener wp_tablet_seat_listener = {
		.tablet_added = _wp_tablet_seat_on_tablet_added,
		.tool_added = _wp_tablet_seat_on_tool_added,
		.pad_added = _wp_tablet_seat_on_pad_added,
	};

	static constexpr struct zwp_tablet_tool_v2_listener wp_tablet_tool_listener = {
		.type = _wp_tablet_tool_on_type,
		.hardware_serial = _wp_tablet_tool_on_hardware_serial,
		.hardware_id_wacom = _wp_tablet_tool_on_hardware_id_wacom,
		.capability = _wp_tablet_tool_on_capability,
		.done = _wp_tablet_tool_on_done,
		.removed = _wp_tablet_tool_on_removed,
		.proximity_in = _wp_tablet_tool_on_proximity_in,
		.proximity_out = _wp_tablet_tool_on_proximity_out,
		.down = _wp_tablet_tool_on_down,
		.up = _wp_tablet_tool_on_up,
		.motion = _wp_tablet_tool_on_motion,
		.pressure = _wp_tablet_tool_on_pressure,
		.distance = _wp_tablet_tool_on_distance,
		.tilt = _wp_tablet_tool_on_tilt,
		.rotation = _wp_tablet_tool_on_rotation,
		.slider = _wp_tablet_tool_on_slider,
		.wheel = _wp_tablet_tool_on_wheel,
		.button = _wp_tablet_tool_on_button,
		.frame = _wp_tablet_tool_on_frame,
	};

	static constexpr struct zwp_text_input_v3_listener wp_text_input_listener = {
		.enter = _wp_text_input_on_enter,
		.leave = _wp_text_input_on_leave,
		.preedit_string = _wp_text_input_on_preedit_string,
		.commit_string = _wp_text_input_on_commit_string,
		.delete_surrounding_text = _wp_text_input_on_delete_surrounding_text,
		.done = _wp_text_input_on_done,
	};

	// NOTE: Deprecated.
	static constexpr struct zxdg_exported_v1_listener xdg_exported_v1_listener = {
		.handle = _xdg_exported_v1_on_handle,
	};

	static constexpr struct zxdg_exported_v2_listener xdg_exported_v2_listener = {
		.handle = _xdg_exported_v2_on_handle,
	};

	static constexpr struct zxdg_toplevel_decoration_v1_listener xdg_toplevel_decoration_listener = {
		.configure = _xdg_toplevel_decoration_on_configure,
	};

	static constexpr struct xdg_activation_token_v1_listener xdg_activation_token_listener = {
		.done = _xdg_activation_token_on_done,
	};

	// Godot interfaces.
	static constexpr struct godot_embedding_compositor_listener godot_embedding_compositor_listener = {
		.client = _godot_embedding_compositor_on_client,
	};

	static constexpr struct godot_embedded_client_listener godot_embedded_client_listener = {
		.disconnected = _godot_embedded_client_on_disconnected,
		.window_embedded = _godot_embedded_client_on_window_embedded,
		.window_focus_in = _godot_embedded_client_on_window_focus_in,
		.window_focus_out = _godot_embedded_client_on_window_focus_out,
	};

#ifdef LIBDECOR_ENABLED
	// libdecor event handlers.
	static void libdecor_on_error(struct libdecor *context, enum libdecor_error error, const char *message);

	static void libdecor_frame_on_configure(struct libdecor_frame *frame, struct libdecor_configuration *configuration, void *user_data);

	static void libdecor_frame_on_close(struct libdecor_frame *frame, void *user_data);

	static void libdecor_frame_on_commit(struct libdecor_frame *frame, void *user_data);

	static void libdecor_frame_on_dismiss_popup(struct libdecor_frame *frame, const char *seat_name, void *user_data);

	// libdecor event listeners.
	static constexpr struct libdecor_interface libdecor_interface = {
		.error = libdecor_on_error,
		.reserved0 = nullptr,
		.reserved1 = nullptr,
		.reserved2 = nullptr,
		.reserved3 = nullptr,
		.reserved4 = nullptr,
		.reserved5 = nullptr,
		.reserved6 = nullptr,
		.reserved7 = nullptr,
		.reserved8 = nullptr,
		.reserved9 = nullptr,
	};

	static constexpr struct libdecor_frame_interface libdecor_frame_interface = {
		.configure = libdecor_frame_on_configure,
		.close = libdecor_frame_on_close,
		.commit = libdecor_frame_on_commit,
		.dismiss_popup = libdecor_frame_on_dismiss_popup,
		.reserved0 = nullptr,
		.reserved1 = nullptr,
		.reserved2 = nullptr,
		.reserved3 = nullptr,
		.reserved4 = nullptr,
		.reserved5 = nullptr,
		.reserved6 = nullptr,
		.reserved7 = nullptr,
		.reserved8 = nullptr,
		.reserved9 = nullptr,
	};
#endif // LIBDECOR_ENABLED

	static Vector<uint8_t> _read_fd(int fd);
	static int _allocate_shm_file(size_t size);

	static Vector<uint8_t> _wl_data_offer_read(struct wl_display *wl_display, const char *p_mime, struct wl_data_offer *wl_data_offer);
	static Vector<uint8_t> _wp_primary_selection_offer_read(struct wl_display *wl_display, const char *p_mime, struct zwp_primary_selection_offer_v1 *wp_primary_selection_offer);

	// Crashes if there's an error in the display and crashes if so.
	static void _wl_display_check_error(struct wl_display *wl_display);

	static void _seat_state_set_current(WaylandThread::SeatState &p_ss);
	static Ref<InputEventKey> _seat_state_get_key_event(SeatState *p_ss, xkb_keycode_t p_keycode, bool p_pressed);
	static Ref<InputEventKey> _seat_state_get_unstuck_key_event(SeatState *p_ss, xkb_keycode_t p_keycode, bool p_pressed, Key p_key);

	static void _seat_state_handle_xkb_keycode(SeatState *p_ss, xkb_keycode_t p_xkb_keycode, bool p_pressed, bool p_echo = false);

	static void _wayland_state_update_cursor();

	void _set_current_seat(struct wl_seat *p_seat);

	void _window_hover(DisplayServerEnums::WindowID p_window_id);
	void _window_hover();

	bool _load_cursor_theme(int p_cursor_size);

	void _update_scale(int p_scale);

public:
	Mutex &mutex = thread_data.mutex;

	struct wl_display *get_wl_display() const;

	// Core Wayland utilities for integrating with our own data structures.
	static bool wl_proxy_is_godot(struct wl_proxy *p_proxy);
	static void wl_proxy_tag_godot(struct wl_proxy *p_proxy);

	static WindowState *wl_surface_get_window_state(struct wl_surface *p_surface);
	static ScreenState *wl_output_get_screen_state(struct wl_output *p_output);
	static SeatState *wl_seat_get_seat_state(struct wl_seat *p_seat);
	static TabletToolState *wp_tablet_tool_get_state(struct zwp_tablet_tool_v2 *p_tool);
	static OfferState *wl_data_offer_get_offer_state(struct wl_data_offer *p_offer);

	static OfferState *wp_primary_selection_offer_get_offer_state(struct zwp_primary_selection_offer_v1 *p_offer);
	static ColorManagementState *wp_color_manager_get_state(wp_color_manager_v1 *p_color_manager);

	static EmbeddingCompositorState *godot_embedding_compositor_get_state(struct godot_embedding_compositor *p_compositor);

	void seat_state_unlock_pointer(SeatState *p_ss);
	void seat_state_lock_pointer(SeatState *p_ss);
	void seat_state_set_hint(SeatState *p_ss, int p_x, int p_y);
	void seat_state_warp_pointer(SeatState *p_ss, int p_x, int p_y);
	void seat_state_confine_pointer(SeatState *p_ss);

	static void seat_state_update_cursor(SeatState *p_ss);

	void seat_state_echo_keys(SeatState *p_ss);

	static int window_state_get_preferred_buffer_scale(WindowState *p_ws);
	static double window_state_get_scale_factor(const WindowState *p_ws);
	static void window_state_update_size(WindowState *p_ws, int p_width, int p_height);
	static void window_state_set_buffer_scale(WindowState *p_ws, int p_buffer_scale);

	static Vector2i scale_vector2i(const Vector2i &p_vector, double p_amount);

	void push_message(Ref<Message> message);
	bool has_message();
	Ref<Message> pop_message();

	void beep() const;

	void set_icon(const Ref<Image> &p_icon, DisplayServerEnums::WindowID p_window_id);
	void set_default_icon(const Ref<Image> &p_icon);

	void window_create(DisplayServerEnums::WindowID p_window_id, const Size2i &p_size, DisplayServerEnums::WindowID p_parent_id = DisplayServerEnums::INVALID_WINDOW_ID);
	void window_create_popup(DisplayServerEnums::WindowID p_window_id, DisplayServerEnums::WindowID p_parent_id, Rect2i p_rect);
	void window_destroy(DisplayServerEnums::WindowID p_window_Id);

	// Checks if a window exists for this ID (NOT if its data is valid). Useful to
	// detect deleted windows.
	bool window_exists(DisplayServerEnums::WindowID p_window_id) const;

	void window_set_parent(DisplayServerEnums::WindowID p_window_id, DisplayServerEnums::WindowID p_parent_id);

	struct wl_surface *window_get_wl_surface(DisplayServerEnums::WindowID p_window_id) const;
	WindowState *window_get_state(DisplayServerEnums::WindowID p_window_id);
	const WindowState *window_get_state(DisplayServerEnums::WindowID p_window_id) const;
	Size2i window_set_size(DisplayServerEnums::WindowID p_window_id, const Size2i &p_size);

	void window_start_resize(DisplayServerEnums::WindowResizeEdge p_edge, DisplayServerEnums::WindowID p_window);

	void window_set_max_size(DisplayServerEnums::WindowID p_window_id, const Size2i &p_size);
	void window_set_min_size(DisplayServerEnums::WindowID p_window_id, const Size2i &p_size);

	bool window_can_set_mode(DisplayServerEnums::WindowID p_window_id, DisplayServerEnums::WindowMode p_window_mode) const;
	void window_try_set_mode(DisplayServerEnums::WindowID p_window_id, DisplayServerEnums::WindowMode p_window_mode);
	DisplayServerEnums::WindowMode window_get_mode(DisplayServerEnums::WindowID p_window_id) const;

	void window_set_borderless(DisplayServerEnums::WindowID p_window_id, bool p_borderless);
	void window_set_title(DisplayServerEnums::WindowID p_window_id, const String &p_title);
	void window_set_app_id(DisplayServerEnums::WindowID p_window_id, const String &p_app_id);

	bool window_is_focused(DisplayServerEnums::WindowID p_window_id);

	// Optional - requires xdg_activation_v1
	void window_request_attention(DisplayServerEnums::WindowID p_window_id);

	void window_start_drag(DisplayServerEnums::WindowID p_window_id);

	// Optional - require idle_inhibit_unstable_v1
	void window_set_idle_inhibition(DisplayServerEnums::WindowID p_window_id, bool p_enable);
	bool window_get_idle_inhibition(DisplayServerEnums::WindowID p_window_id) const;

	// Optional - require wp_color_management_v1
	void window_set_color_profile(DisplayServerEnums::WindowID p_window_id, ColorProfile p_profile);

	ScreenData screen_get_data(int p_screen) const;
	int get_screen_count() const;

	bool input_has_touch() const;

	void pointer_set_constraint(PointerConstraint p_constraint);
	void pointer_set_hint(const Point2i &p_hint);
	void pointer_warp(const Point2i &p_to);
	PointerConstraint pointer_get_constraint() const;
	DisplayServerEnums::WindowID pointer_get_pointed_window_id() const;
	DisplayServerEnums::WindowID pointer_get_last_pointed_window_id() const;
	BitField<MouseButtonMask> pointer_get_button_mask() const;

	void cursor_set_visible(bool p_visible);
	void cursor_set_shape(DisplayServerEnums::CursorShape p_cursor_shape);

	void cursor_set_custom_shape(DisplayServerEnums::CursorShape p_cursor_shape);
	void cursor_shape_set_custom_image(DisplayServerEnums::CursorShape p_cursor_shape, Ref<Image> p_image, const Point2i &p_hotspot);
	void cursor_shape_clear_custom_image(DisplayServerEnums::CursorShape p_cursor_shape);

	void window_set_ime_active(const bool p_active, DisplayServerEnums::WindowID p_window_id);
	void window_set_ime_position(const Point2i &p_pos, DisplayServerEnums::WindowID p_window_id);

	int keyboard_get_layout_count() const;
	int keyboard_get_current_layout_index() const;
	void keyboard_set_current_layout_index(int p_index);
	String keyboard_get_layout_name(int p_index) const;

	Key keyboard_get_key_from_physical(Key p_key) const;
	Key keyboard_get_label_from_physical(Key p_key) const;

	void keyboard_echo_keys();

	bool selection_has_mime(const String &p_mime) const;
	Vector<uint8_t> selection_get_mime(const String &p_mime) const;

	void selection_set_text(const String &p_text);

	// Optional primary support - requires wp_primary_selection_unstable_v1
	bool primary_has_mime(const String &p_mime) const;
	Vector<uint8_t> primary_get_mime(const String &p_mime) const;

	void primary_set_text(const String &p_text);

	bool supports_hdr() const;

	void commit_surfaces();

	// Returns handled events.
	int wait_events(int p_timeout = -1);

	void set_frame();
	bool get_reset_frame();
	bool wait_frame_suspend_ms(int p_timeout);
	bool is_fifo_available() const;

	uint64_t window_get_last_frame_time(DisplayServerEnums::WindowID p_window_id) const;
	bool window_is_suspended(DisplayServerEnums::WindowID p_window_id) const;
	bool is_suspended() const;

	bool window_wait_ready(DisplayServerEnums::WindowID p_window_id, int p_timeout_ms);

	struct godot_embedding_compositor *get_embedding_compositor();

	ProcessID embedded_compositor_get_focused_pid();

	Error init();
	void destroy();
};

#endif // WAYLAND_ENABLED
