Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--intern/ghost/intern/GHOST_SystemWayland.cpp890
1 files changed, 698 insertions, 192 deletions
diff --git a/intern/ghost/intern/GHOST_SystemWayland.cpp b/intern/ghost/intern/GHOST_SystemWayland.cpp
index 67f475a2963..65b4e890d88 100644
--- a/intern/ghost/intern/GHOST_SystemWayland.cpp
+++ b/intern/ghost/intern/GHOST_SystemWayland.cpp
@@ -89,6 +89,15 @@ static void gwl_seat_capability_pointer_disable(GWL_Seat *seat);
static void gwl_seat_capability_keyboard_disable(GWL_Seat *seat);
static void gwl_seat_capability_touch_disable(GWL_Seat *seat);
+static bool gwl_registry_entry_remove_by_name(GWL_Display *display,
+ uint32_t name,
+ int *r_interface_slot);
+
+struct GWL_RegistryHandler;
+static int gwl_registry_handler_interface_slot_max();
+static const struct GWL_RegistryHandler *gwl_registry_handler_from_interface_slot(
+ int interface_slot);
+
/* -------------------------------------------------------------------- */
/** \name Local Defines
*
@@ -138,6 +147,8 @@ static bool use_gnome_confine_hack = false;
# define USE_GNOME_NEEDS_LIBDECOR_HACK
#endif
+#define WL_NAME_UNSET uint32_t(-1)
+
/** \} */
/* -------------------------------------------------------------------- */
@@ -527,6 +538,7 @@ static void gwl_libdecor_system_destroy(GWL_LibDecor_System *decor)
{
if (decor->context) {
libdecor_unref(decor->context);
+ decor->context = nullptr;
}
delete decor;
}
@@ -534,16 +546,21 @@ static void gwl_libdecor_system_destroy(GWL_LibDecor_System *decor)
struct GWL_XDG_Decor_System {
struct xdg_wm_base *shell = nullptr;
+ uint32_t shell_name = WL_NAME_UNSET;
+
struct zxdg_decoration_manager_v1 *manager = nullptr;
+ uint32_t manager_name = WL_NAME_UNSET;
};
-static void gwl_xdg_decor_system_destroy(GWL_XDG_Decor_System *decor)
+static void gwl_xdg_decor_system_destroy(struct GWL_Display *display, GWL_XDG_Decor_System *decor)
{
if (decor->manager) {
- zxdg_decoration_manager_v1_destroy(decor->manager);
+ gwl_registry_entry_remove_by_name(display, decor->manager_name, nullptr);
+ GHOST_ASSERT(decor->manager == nullptr, "Internal registry error");
}
if (decor->shell) {
- xdg_wm_base_destroy(decor->shell);
+ gwl_registry_entry_remove_by_name(display, decor->shell_name, nullptr);
+ GHOST_ASSERT(decor->shell == nullptr, "Internal registry error");
}
delete decor;
}
@@ -707,9 +724,13 @@ struct GWL_Seat {
/** \name Internal #GWL_Display Type (#wl_display & #wl_compositor wrapper)
* \{ */
+struct GWL_RegistryEntry;
+
struct GWL_Display {
GHOST_SystemWayland *system = nullptr;
+ struct GWL_RegistryEntry *registry_entry = nullptr;
+
struct wl_display *wl_display = nullptr;
struct wl_compositor *wl_compositor = nullptr;
@@ -739,6 +760,178 @@ struct GWL_Display {
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Internal #GWL_RegistryHandler
+ * \{ */
+
+struct GWL_RegisteryAdd_Params {
+ struct GWL_Display *display = nullptr;
+ struct wl_registry *wl_registry = nullptr;
+ uint32_t name = 0;
+ uint32_t version = 0;
+ /** Index within `gwl_registry_handlers`. */
+ int interface_slot = 0;
+};
+
+/**
+ * Add callback for object registry.
+ * \param display: The display which holes a reference to the global object.
+ * \param params: Various arguments needed for registration.
+ */
+using GWL_RegistryHandler_AddFn = void (*)(GWL_Display *display,
+ const GWL_RegisteryAdd_Params *params);
+
+/**
+ * Remove callback for object registry.
+ * \param display: The display which holes a reference to the global object.
+ * \param user_data: Optional reference to a sub element of `display`,
+ * use for outputs or seats for e.g. when the display may hold multiple references.
+ * \param on_exit: Enabled when freeing on exit.
+ * When true the consistency of references between objects should be kept valid.
+ * Otherwise it can be assumed that all objects will be freed and none will be used again,
+ * so there is no need to ensure a valid state.
+ */
+using GWL_RegistryEntry_FreeFn = void (*)(GWL_Display *display, void *user_data, bool on_exit);
+
+struct GWL_RegistryHandler {
+ /** Pointer to the name (not the name it's self), needed as the values aren't set on startup. */
+ const char *const *interface_p = nullptr;
+
+ GWL_RegistryHandler_AddFn add_fn = nullptr;
+ GWL_RegistryEntry_FreeFn remove_fn = nullptr;
+};
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Internal #GWL_RegistryEntry
+ * \{ */
+
+/**
+ * Registered global objects can be removed by the compositor,
+ * these entries are a registry of objects and callbacks to properly remove them.
+ * These are also used to remove all registered objects before exiting.
+ */
+struct GWL_RegistryEntry {
+ GWL_RegistryEntry *next = nullptr;
+ /**
+ * Optional pointer passed to `remove_fn`, typically the container in #GWL_Display
+ * in cases multiple instances of the same interface are supported.
+ */
+ void *user_data = nullptr;
+ /**
+ * A unique identifier used as a handle by `wl_registry_listener.global_remove`.
+ */
+ uint32_t name = WL_NAME_UNSET;
+ /**
+ * The index in `gwl_registry_handlers`,
+ * useful for accessing the interface name (for logging for example).
+ */
+ int interface_slot = 0;
+};
+
+static void gwl_registry_entry_add(GWL_Display *display,
+ const int interface_slot,
+ const uint32_t name,
+ void *user_data)
+{
+ GWL_RegistryEntry *reg = new GWL_RegistryEntry;
+
+ reg->interface_slot = interface_slot;
+ reg->name = name;
+ reg->user_data = user_data;
+
+ reg->next = display->registry_entry;
+ display->registry_entry = reg;
+}
+
+static bool gwl_registry_entry_remove_by_name(GWL_Display *display,
+ uint32_t name,
+ int *r_interface_slot)
+{
+ GWL_RegistryEntry *reg = display->registry_entry;
+ GWL_RegistryEntry **reg_link_p = &display->registry_entry;
+ bool found = false;
+
+ if (r_interface_slot) {
+ *r_interface_slot = -1;
+ }
+
+ while (reg) {
+ if (reg->name == name) {
+ GWL_RegistryEntry *reg_next = reg->next;
+ const GWL_RegistryHandler *handler = gwl_registry_handler_from_interface_slot(
+ reg->interface_slot);
+ handler->remove_fn(display, reg->user_data, false);
+ if (r_interface_slot) {
+ *r_interface_slot = reg->interface_slot;
+ }
+ delete reg;
+ *reg_link_p = reg_next;
+ found = true;
+ break;
+ }
+ reg_link_p = &reg->next;
+ reg = reg->next;
+ }
+ return found;
+}
+
+static bool gwl_registry_entry_remove_by_interface_slot(GWL_Display *display,
+ int interface_slot,
+ bool on_exit)
+{
+ GWL_RegistryEntry *reg = display->registry_entry;
+ GWL_RegistryEntry **reg_link_p = &display->registry_entry;
+ bool found = false;
+
+ while (reg) {
+ if (reg->interface_slot == interface_slot) {
+ GWL_RegistryEntry *reg_next = reg->next;
+ const GWL_RegistryHandler *handler = gwl_registry_handler_from_interface_slot(
+ interface_slot);
+ handler->remove_fn(display, reg->user_data, on_exit);
+ delete reg;
+ *reg_link_p = reg_next;
+ reg = reg_next;
+ found = true;
+ continue;
+ }
+ reg_link_p = &reg->next;
+ reg = reg->next;
+ }
+ return found;
+}
+
+/**
+ * Remove all global objects (on exit).
+ */
+static void gwl_registry_entry_remove_all(GWL_Display *display)
+{
+ const bool on_exit = true;
+
+ /* NOTE(@campbellbarton): Free by slot instead of simply looping over
+ * `display->registry_entry` so the order of freeing is always predictable.
+ * Otherwise global objects would be feed in the order they are registered.
+ * While this works in my tests, it could cause difficult to reproduce bugs
+ * where lesser used compositors or changes to existing compositors could
+ * crash on exit based on the order of freeing objects is out of our control.
+ *
+ * To give a concrete example of how this could fail, it's possible removing
+ * a tablet interface could reference the pointer interface, or the output interface.
+ * Even though references between interfaces shouldn't be necessary in most cases
+ * when `on_exit` is enabled. */
+ int interface_slot = gwl_registry_handler_interface_slot_max();
+ while (interface_slot--) {
+ gwl_registry_entry_remove_by_interface_slot(display, interface_slot, on_exit);
+ }
+
+ GHOST_ASSERT(display->registry_entry == nullptr, "Failed to remove all entries!");
+ display->registry_entry = nullptr;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Private Utility Functions
* \{ */
@@ -790,125 +983,22 @@ static GWL_SeatStatePointer *seat_state_pointer_from_cursor_surface(GWL_Seat *se
static void display_destroy(GWL_Display *display)
{
- if (display->wl_data_device_manager) {
- wl_data_device_manager_destroy(display->wl_data_device_manager);
- }
-
- if (display->wp_tablet_manager) {
- zwp_tablet_manager_v2_destroy(display->wp_tablet_manager);
- }
-
- for (GWL_Output *output : display->outputs) {
- wl_output_destroy(output->wl_output);
- delete output;
- }
-
- for (GWL_Seat *seat : display->seats) {
-
- /* First handle members that require locking.
- * While highly unlikely, it's possible they are being used while this function runs. */
- {
- std::lock_guard lock{seat->data_source_mutex};
- if (seat->data_source) {
- gwl_simple_buffer_free_data(&seat->data_source->buffer_out);
- if (seat->data_source->wl_source) {
- wl_data_source_destroy(seat->data_source->wl_source);
- }
- delete seat->data_source;
- }
- }
-
- {
- std::lock_guard lock{seat->data_offer_dnd_mutex};
- if (seat->data_offer_dnd) {
- wl_data_offer_destroy(seat->data_offer_dnd->id);
- delete seat->data_offer_dnd;
- }
- }
-
- {
- std::lock_guard lock{seat->data_offer_copy_paste_mutex};
- if (seat->data_offer_copy_paste) {
- wl_data_offer_destroy(seat->data_offer_copy_paste->id);
- delete seat->data_offer_copy_paste;
- }
- }
-
- {
- GWL_PrimarySelection *primary = &seat->primary_selection;
- std::lock_guard lock{primary->data_offer_mutex};
- gwl_primary_selection_discard_offer(primary);
- }
-
- {
- GWL_PrimarySelection *primary = &seat->primary_selection;
- std::lock_guard lock{primary->data_source_mutex};
- gwl_primary_selection_discard_source(primary);
- }
-
- if (seat->wp_primary_selection_device) {
- zwp_primary_selection_device_v1_destroy(seat->wp_primary_selection_device);
- }
-
- if (seat->wl_data_device) {
- wl_data_device_release(seat->wl_data_device);
- }
-
- if (seat->cursor.custom_data) {
- munmap(seat->cursor.custom_data, seat->cursor.custom_data_size);
- }
-
- /* Disable all capabilities as a way to free:
- * - `seat.wl_pointer` (and related cursor variables).
- * - `seat.wl_touch`.
- * - `seat.wl_keyboard`.
- */
- gwl_seat_capability_pointer_disable(seat);
- gwl_seat_capability_keyboard_disable(seat);
- gwl_seat_capability_touch_disable(seat);
-
- /* Un-referencing checks for NULL case. */
- xkb_state_unref(seat->xkb_state);
- xkb_state_unref(seat->xkb_state_empty);
- xkb_state_unref(seat->xkb_state_empty_with_numlock);
-
- xkb_context_unref(seat->xkb_context);
-
- wl_seat_destroy(seat->wl_seat);
- delete seat;
- }
-
- if (display->wl_shm) {
- wl_shm_destroy(display->wl_shm);
- }
-
- if (display->wp_relative_pointer_manager) {
- zwp_relative_pointer_manager_v1_destroy(display->wp_relative_pointer_manager);
- }
-
- if (display->wp_pointer_constraints) {
- zwp_pointer_constraints_v1_destroy(display->wp_pointer_constraints);
- }
-
- if (display->wp_pointer_gestures) {
- zwp_pointer_gestures_v1_destroy(display->wp_pointer_gestures);
- }
-
- if (display->wl_compositor) {
- wl_compositor_destroy(display->wl_compositor);
- }
+ /* Unregister items in reverse order. */
+ gwl_registry_entry_remove_all(display);
#ifdef WITH_GHOST_WAYLAND_LIBDECOR
if (use_libdecor) {
if (display->libdecor) {
gwl_libdecor_system_destroy(display->libdecor);
+ display->libdecor = nullptr;
}
}
else
#endif
{
if (display->xdg_decor) {
- gwl_xdg_decor_system_destroy(display->xdg_decor);
+ gwl_xdg_decor_system_destroy(display, display->xdg_decor);
+ display->xdg_decor = nullptr;
}
}
@@ -4043,100 +4133,508 @@ static struct libdecor_interface libdecor_interface = {
static CLG_LogRef LOG_WL_REGISTRY = {"ghost.wl.handle.registry"};
#define LOG (&LOG_WL_REGISTRY)
-static void global_handle_add(void *data,
- struct wl_registry *wl_registry,
- const uint32_t name,
- const char *interface,
- const uint32_t version)
+/* #GWL_Display.wl_compositor */
+
+static void gwl_registry_compositor_add(GWL_Display *display,
+ const GWL_RegisteryAdd_Params *params)
{
- /* Log last since it can be noted if the interface was handled or not. */
- bool found = true;
-
- struct GWL_Display *display = static_cast<struct GWL_Display *>(data);
- if (STREQ(interface, wl_compositor_interface.name)) {
- display->wl_compositor = static_cast<wl_compositor *>(
- wl_registry_bind(wl_registry, name, &wl_compositor_interface, 3));
- }
- else if (STREQ(interface, xdg_wm_base_interface.name)) {
- GWL_XDG_Decor_System &decor = *display->xdg_decor;
- decor.shell = static_cast<xdg_wm_base *>(
- wl_registry_bind(wl_registry, name, &xdg_wm_base_interface, 1));
- xdg_wm_base_add_listener(decor.shell, &shell_listener, nullptr);
- }
- else if (STREQ(interface, zxdg_decoration_manager_v1_interface.name)) {
- GWL_XDG_Decor_System &decor = *display->xdg_decor;
- decor.manager = static_cast<zxdg_decoration_manager_v1 *>(
- wl_registry_bind(wl_registry, name, &zxdg_decoration_manager_v1_interface, 1));
- }
- else if (STREQ(interface, zxdg_output_manager_v1_interface.name)) {
- display->xdg_output_manager = static_cast<zxdg_output_manager_v1 *>(
- wl_registry_bind(wl_registry, name, &zxdg_output_manager_v1_interface, 2));
- for (GWL_Output *output : display->outputs) {
- output->xdg_output = zxdg_output_manager_v1_get_xdg_output(display->xdg_output_manager,
- output->wl_output);
- zxdg_output_v1_add_listener(output->xdg_output, &xdg_output_listener, output);
- }
+ display->wl_compositor = static_cast<wl_compositor *>(
+ wl_registry_bind(params->wl_registry, params->name, &wl_compositor_interface, 3));
+
+ gwl_registry_entry_add(display, params->interface_slot, params->name, nullptr);
+}
+static void gwl_registry_compositor_remove(GWL_Display *display,
+ void * /*user_data*/,
+ const bool /*on_exit*/)
+{
+ struct wl_compositor **value_p = &display->wl_compositor;
+ wl_compositor_destroy(*value_p);
+ *value_p = nullptr;
+}
+
+/* #GWL_Display.xdg_decor.shell */
+
+static void gwl_registry_xdg_wm_base_add(GWL_Display *display,
+ const GWL_RegisteryAdd_Params *params)
+{
+ GWL_XDG_Decor_System &decor = *display->xdg_decor;
+ decor.shell = static_cast<xdg_wm_base *>(
+ wl_registry_bind(params->wl_registry, params->name, &xdg_wm_base_interface, 1));
+ xdg_wm_base_add_listener(decor.shell, &shell_listener, nullptr);
+ decor.shell_name = params->name;
+
+ gwl_registry_entry_add(display, params->interface_slot, params->name, nullptr);
+}
+static void gwl_registry_xdg_wm_base_remove(GWL_Display *display,
+ void * /*user_data*/,
+ const bool /*on_exit*/)
+{
+ GWL_XDG_Decor_System &decor = *display->xdg_decor;
+ struct xdg_wm_base **value_p = &decor.shell;
+ uint32_t *name_p = &decor.shell_name;
+ xdg_wm_base_destroy(*value_p);
+ *value_p = nullptr;
+ *name_p = WL_NAME_UNSET;
+}
+
+/* #GWL_Display.xdg_decor.manager */
+
+static void gwl_registry_xdg_decoration_manager_add(GWL_Display *display,
+ const GWL_RegisteryAdd_Params *params)
+{
+ GWL_XDG_Decor_System &decor = *display->xdg_decor;
+ decor.manager = static_cast<zxdg_decoration_manager_v1 *>(wl_registry_bind(
+ params->wl_registry, params->name, &zxdg_decoration_manager_v1_interface, 1));
+ decor.manager_name = params->name;
+
+ gwl_registry_entry_add(display, params->interface_slot, params->name, nullptr);
+}
+static void gwl_registry_xdg_decoration_manager_remove(GWL_Display *display,
+ void * /*user_data*/,
+ const bool /*on_exit*/)
+{
+ GWL_XDG_Decor_System &decor = *display->xdg_decor;
+ struct zxdg_decoration_manager_v1 **value_p = &decor.manager;
+ uint32_t *name_p = &decor.manager_name;
+ zxdg_decoration_manager_v1_destroy(*value_p);
+ *value_p = nullptr;
+ *name_p = WL_NAME_UNSET;
+}
+
+/* #GWL_Display.xdg_output_manager */
+
+static void gwl_registry_xdg_output_manager_add(GWL_Display *display,
+ const GWL_RegisteryAdd_Params *params)
+{
+ display->xdg_output_manager = static_cast<zxdg_output_manager_v1 *>(
+ wl_registry_bind(params->wl_registry, params->name, &zxdg_output_manager_v1_interface, 2));
+ for (GWL_Output *output : display->outputs) {
+ output->xdg_output = zxdg_output_manager_v1_get_xdg_output(display->xdg_output_manager,
+ output->wl_output);
+ zxdg_output_v1_add_listener(output->xdg_output, &xdg_output_listener, output);
}
- else if (STREQ(interface, wl_output_interface.name)) {
- GWL_Output *output = new GWL_Output;
- output->wl_output = static_cast<wl_output *>(
- wl_registry_bind(wl_registry, name, &wl_output_interface, 2));
- ghost_wl_output_tag(output->wl_output);
- wl_output_set_user_data(output->wl_output, output);
- display->outputs.push_back(output);
- wl_output_add_listener(output->wl_output, &output_listener, output);
+ gwl_registry_entry_add(display, params->interface_slot, params->name, nullptr);
+}
+static void gwl_registry_xdg_output_manager_remove(GWL_Display *display,
+ void * /*user_data*/,
+ const bool /*on_exit*/)
+{
+ struct zxdg_output_manager_v1 **value_p = &display->xdg_output_manager;
+ zxdg_output_manager_v1_destroy(*value_p);
+ *value_p = nullptr;
+
+ for (GWL_Output *output : display->outputs) {
+ output->xdg_output = nullptr;
+ }
+}
+
+/* #GWL_Display.wl_output */
- if (display->xdg_output_manager) {
- output->xdg_output = zxdg_output_manager_v1_get_xdg_output(display->xdg_output_manager,
- output->wl_output);
- zxdg_output_v1_add_listener(output->xdg_output, &xdg_output_listener, output);
+static void gwl_registry_wl_output_add(GWL_Display *display, const GWL_RegisteryAdd_Params *params)
+{
+ GWL_Output *output = new GWL_Output;
+ output->wl_output = static_cast<wl_output *>(
+ wl_registry_bind(params->wl_registry, params->name, &wl_output_interface, 2));
+ ghost_wl_output_tag(output->wl_output);
+ wl_output_set_user_data(output->wl_output, output);
+
+ display->outputs.push_back(output);
+ wl_output_add_listener(output->wl_output, &output_listener, output);
+
+ if (display->xdg_output_manager) {
+ output->xdg_output = zxdg_output_manager_v1_get_xdg_output(display->xdg_output_manager,
+ output->wl_output);
+ zxdg_output_v1_add_listener(output->xdg_output, &xdg_output_listener, output);
+ }
+
+ gwl_registry_entry_add(
+ display, params->interface_slot, params->name, static_cast<void *>(output));
+}
+static void gwl_registry_wl_output_remove(GWL_Display *display,
+ void *user_data,
+ const bool /*on_exit*/)
+{
+ GWL_Output *output = static_cast<GWL_Output *>(user_data);
+ wl_output_destroy(output->wl_output);
+ std::vector<GWL_Output *>::iterator iter = std::find(
+ display->outputs.begin(), display->outputs.end(), output);
+ const int index = (iter != display->outputs.cend()) ?
+ std::distance(display->outputs.begin(), iter) :
+ -1;
+ GHOST_ASSERT(index != -1, "invalid internal state");
+ /* NOTE: always erase even when `on_exit` because `output->xdg_output` is cleared later. */
+ display->outputs.erase(display->outputs.begin() + index);
+ delete output;
+}
+
+/* #GWL_Display.seats */
+
+static void gwl_registry_wl_seat_add(GWL_Display *display, const GWL_RegisteryAdd_Params *params)
+{
+ GWL_Seat *seat = new GWL_Seat;
+ seat->system = display->system;
+ seat->xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
+ seat->data_source = new GWL_DataSource;
+ seat->wl_seat = static_cast<wl_seat *>(
+ wl_registry_bind(params->wl_registry, params->name, &wl_seat_interface, 5));
+ display->seats.push_back(seat);
+ wl_seat_add_listener(seat->wl_seat, &seat_listener, seat);
+
+ gwl_registry_entry_add(display, params->interface_slot, params->name, static_cast<void *>(seat));
+}
+static void gwl_registry_wl_seat_remove(GWL_Display *display, void *user_data, const bool on_exit)
+{
+ GWL_Seat *seat = static_cast<GWL_Seat *>(user_data);
+
+ /* First handle members that require locking.
+ * While highly unlikely, it's possible they are being used while this function runs. */
+ {
+ std::lock_guard lock{seat->data_source_mutex};
+ if (seat->data_source) {
+ gwl_simple_buffer_free_data(&seat->data_source->buffer_out);
+ if (seat->data_source->wl_source) {
+ wl_data_source_destroy(seat->data_source->wl_source);
+ }
+ delete seat->data_source;
}
}
- else if (STREQ(interface, wl_seat_interface.name)) {
- GWL_Seat *seat = new GWL_Seat;
- seat->system = display->system;
- seat->xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
- seat->data_source = new GWL_DataSource;
- seat->wl_seat = static_cast<wl_seat *>(
- wl_registry_bind(wl_registry, name, &wl_seat_interface, 5));
- display->seats.push_back(seat);
- wl_seat_add_listener(seat->wl_seat, &seat_listener, seat);
+
+ {
+ std::lock_guard lock{seat->data_offer_dnd_mutex};
+ if (seat->data_offer_dnd) {
+ wl_data_offer_destroy(seat->data_offer_dnd->id);
+ delete seat->data_offer_dnd;
+ }
}
- else if (STREQ(interface, wl_shm_interface.name)) {
- display->wl_shm = static_cast<wl_shm *>(
- wl_registry_bind(wl_registry, name, &wl_shm_interface, 1));
+
+ {
+ std::lock_guard lock{seat->data_offer_copy_paste_mutex};
+ if (seat->data_offer_copy_paste) {
+ wl_data_offer_destroy(seat->data_offer_copy_paste->id);
+ delete seat->data_offer_copy_paste;
+ }
}
- else if (STREQ(interface, wl_data_device_manager_interface.name)) {
- display->wl_data_device_manager = static_cast<wl_data_device_manager *>(
- wl_registry_bind(wl_registry, name, &wl_data_device_manager_interface, 3));
+
+ {
+ GWL_PrimarySelection *primary = &seat->primary_selection;
+ std::lock_guard lock{primary->data_offer_mutex};
+ gwl_primary_selection_discard_offer(primary);
}
- else if (STREQ(interface, zwp_tablet_manager_v2_interface.name)) {
- display->wp_tablet_manager = static_cast<zwp_tablet_manager_v2 *>(
- wl_registry_bind(wl_registry, name, &zwp_tablet_manager_v2_interface, 1));
+
+ {
+ GWL_PrimarySelection *primary = &seat->primary_selection;
+ std::lock_guard lock{primary->data_source_mutex};
+ gwl_primary_selection_discard_source(primary);
}
- else if (STREQ(interface, zwp_relative_pointer_manager_v1_interface.name)) {
- display->wp_relative_pointer_manager = static_cast<zwp_relative_pointer_manager_v1 *>(
- wl_registry_bind(wl_registry, name, &zwp_relative_pointer_manager_v1_interface, 1));
+
+ if (seat->wp_primary_selection_device) {
+ zwp_primary_selection_device_v1_destroy(seat->wp_primary_selection_device);
}
- else if (STREQ(interface, zwp_pointer_constraints_v1_interface.name)) {
- display->wp_pointer_constraints = static_cast<zwp_pointer_constraints_v1 *>(
- wl_registry_bind(wl_registry, name, &zwp_pointer_constraints_v1_interface, 1));
+
+ if (seat->wl_data_device) {
+ wl_data_device_release(seat->wl_data_device);
}
- else if (STREQ(interface, zwp_pointer_gestures_v1_interface.name)) {
- display->wp_pointer_gestures = static_cast<zwp_pointer_gestures_v1 *>(
- wl_registry_bind(wl_registry, name, &zwp_pointer_gestures_v1_interface, 3));
+
+ if (seat->cursor.custom_data) {
+ munmap(seat->cursor.custom_data, seat->cursor.custom_data_size);
}
- else if (!strcmp(interface, zwp_primary_selection_device_manager_v1_interface.name)) {
- display->wp_primary_selection_device_manager =
- static_cast<zwp_primary_selection_device_manager_v1 *>(wl_registry_bind(
- wl_registry, name, &zwp_primary_selection_device_manager_v1_interface, 1));
+ /* Disable all capabilities as a way to free:
+ * - `seat.wl_pointer` (and related cursor variables).
+ * - `seat.wl_touch`.
+ * - `seat.wl_keyboard`.
+ */
+ gwl_seat_capability_pointer_disable(seat);
+ gwl_seat_capability_keyboard_disable(seat);
+ gwl_seat_capability_touch_disable(seat);
+
+ /* Un-referencing checks for NULL case. */
+ xkb_state_unref(seat->xkb_state);
+ xkb_state_unref(seat->xkb_state_empty);
+ xkb_state_unref(seat->xkb_state_empty_with_numlock);
+
+ xkb_context_unref(seat->xkb_context);
+
+ /* Remove the seat. */
+ wl_seat_destroy(seat->wl_seat);
+
+ std::vector<GWL_Seat *>::iterator iter = std::find(
+ display->seats.begin(), display->seats.end(), seat);
+ const int index = (iter != display->seats.cend()) ? std::distance(display->seats.begin(), iter) :
+ -1;
+ GHOST_ASSERT(index != -1, "invalid internal state");
+
+ if (!on_exit) {
+ display->seats.erase(display->seats.begin() + index);
}
+ delete seat;
+}
- else {
- found = false;
+/* #GWL_Display.wl_shm */
+
+static void gwl_registry_wl_shm_add(GWL_Display *display, const GWL_RegisteryAdd_Params *params)
+{
+ display->wl_shm = static_cast<wl_shm *>(
+ wl_registry_bind(params->wl_registry, params->name, &wl_shm_interface, 1));
+
+ gwl_registry_entry_add(display, params->interface_slot, params->name, nullptr);
+}
+static void gwl_registry_wl_shm_remove(GWL_Display *display,
+ void * /*user_data*/,
+ const bool /*on_exit*/)
+{
+ struct wl_shm **value_p = &display->wl_shm;
+ wl_shm_destroy(*value_p);
+ *value_p = nullptr;
+}
+
+/* #GWL_Display.wl_data_device_manager */
+
+static void gwl_registry_wl_data_device_manager_add(GWL_Display *display,
+ const GWL_RegisteryAdd_Params *params)
+{
+ display->wl_data_device_manager = static_cast<wl_data_device_manager *>(
+ wl_registry_bind(params->wl_registry, params->name, &wl_data_device_manager_interface, 3));
+
+ gwl_registry_entry_add(display, params->interface_slot, params->name, nullptr);
+}
+static void gwl_registry_wl_data_device_manager_remove(GWL_Display *display,
+ void * /*user_data*/,
+ const bool /*on_exit*/)
+{
+ struct wl_data_device_manager **value_p = &display->wl_data_device_manager;
+ wl_data_device_manager_destroy(*value_p);
+ *value_p = nullptr;
+}
+
+/* #GWL_Display.wp_tablet_manager */
+
+static void gwl_registry_wp_tablet_manager_add(GWL_Display *display,
+ const GWL_RegisteryAdd_Params *params)
+{
+ display->wp_tablet_manager = static_cast<zwp_tablet_manager_v2 *>(
+ wl_registry_bind(params->wl_registry, params->name, &zwp_tablet_manager_v2_interface, 1));
+
+ gwl_registry_entry_add(display, params->interface_slot, params->name, nullptr);
+}
+static void gwl_registry_wp_tablet_manager_remove(GWL_Display *display,
+ void * /*user_data*/,
+ const bool /*on_exit*/)
+{
+ struct zwp_tablet_manager_v2 **value_p = &display->wp_tablet_manager;
+ zwp_tablet_manager_v2_destroy(*value_p);
+ *value_p = nullptr;
+}
+
+/* #GWL_Display.wp_relative_pointer_manager */
+
+static void gwl_registry_wp_relative_pointer_manager_add(GWL_Display *display,
+ const GWL_RegisteryAdd_Params *params)
+{
+ display->wp_relative_pointer_manager = static_cast<zwp_relative_pointer_manager_v1 *>(
+ wl_registry_bind(
+ params->wl_registry, params->name, &zwp_relative_pointer_manager_v1_interface, 1));
+
+ gwl_registry_entry_add(display, params->interface_slot, params->name, nullptr);
+}
+static void gwl_registry_wp_relative_pointer_manager_remove(GWL_Display *display,
+ void * /*user_data*/,
+ const bool /*on_exit*/)
+{
+ struct zwp_relative_pointer_manager_v1 **value_p = &display->wp_relative_pointer_manager;
+ zwp_relative_pointer_manager_v1_destroy(*value_p);
+ *value_p = nullptr;
+}
+
+/* #GWL_Display.wp_pointer_constraints */
+
+static void gwl_registry_wp_pointer_constraints_add(GWL_Display *display,
+ const GWL_RegisteryAdd_Params *params)
+{
+ display->wp_pointer_constraints = static_cast<zwp_pointer_constraints_v1 *>(wl_registry_bind(
+ params->wl_registry, params->name, &zwp_pointer_constraints_v1_interface, 1));
+ gwl_registry_entry_add(display, params->interface_slot, params->name, nullptr);
+}
+static void gwl_registry_wp_pointer_constraints_remove(GWL_Display *display,
+ void * /*user_data*/,
+ const bool /*on_exit*/)
+{
+ struct zwp_pointer_constraints_v1 **value_p = &display->wp_pointer_constraints;
+ zwp_pointer_constraints_v1_destroy(*value_p);
+ *value_p = nullptr;
+}
+
+/* #GWL_Display.wp_pointer_gestures */
+
+static void gwl_registry_wp_pointer_gestures_add(GWL_Display *display,
+ const GWL_RegisteryAdd_Params *params)
+{
+ display->wp_pointer_gestures = static_cast<zwp_pointer_gestures_v1 *>(
+ wl_registry_bind(params->wl_registry, params->name, &zwp_pointer_gestures_v1_interface, 3));
+
+ gwl_registry_entry_add(display, params->interface_slot, params->name, nullptr);
+}
+static void gwl_registry_wp_pointer_gestures_remove(GWL_Display *display,
+ void * /*user_data*/,
+ const bool /*on_exit*/)
+{
+ struct zwp_pointer_gestures_v1 **value_p = &display->wp_pointer_gestures;
+ zwp_pointer_gestures_v1_destroy(*value_p);
+ *value_p = nullptr;
+}
+
+/* #GWL_Display.wp_primary_selection_device_manager */
+
+static void gwl_registry_wp_primary_selection_device_manager_add(
+ struct GWL_Display *display, const GWL_RegisteryAdd_Params *params)
+{
+ display->wp_primary_selection_device_manager =
+ static_cast<zwp_primary_selection_device_manager_v1 *>(
+ wl_registry_bind(params->wl_registry,
+ params->name,
+ &zwp_primary_selection_device_manager_v1_interface,
+ 1));
+
+ gwl_registry_entry_add(display, params->interface_slot, params->name, nullptr);
+}
+static void gwl_registry_wp_primary_selection_device_manager_remove(GWL_Display *display,
+ void * /*user_data*/,
+ const bool /*on_exit*/)
+{
+ struct zwp_primary_selection_device_manager_v1 **value_p =
+ &display->wp_primary_selection_device_manager;
+ zwp_primary_selection_device_manager_v1_destroy(*value_p);
+ *value_p = nullptr;
+}
+
+/**
+ * Map interfaces to to initialization functions.
+ *
+ * \note This list also defines the order interfaces are freed: from last to first,
+ * so the most fundamental objects such as the compositor are freed last.
+ */
+static const GWL_RegistryHandler gwl_registry_handlers[] = {
+ {
+ &wl_compositor_interface.name,
+ gwl_registry_compositor_add,
+ gwl_registry_compositor_remove,
+ },
+ {
+ &xdg_wm_base_interface.name,
+ gwl_registry_xdg_wm_base_add,
+ gwl_registry_xdg_wm_base_remove,
+ },
+ {
+ &zxdg_decoration_manager_v1_interface.name,
+ gwl_registry_xdg_decoration_manager_add,
+ gwl_registry_xdg_decoration_manager_remove,
+ },
+ {
+ &zxdg_output_manager_v1_interface.name,
+ gwl_registry_xdg_output_manager_add,
+ gwl_registry_xdg_output_manager_remove,
+ },
+ {
+ &wl_output_interface.name,
+ gwl_registry_wl_output_add,
+ gwl_registry_wl_output_remove,
+ },
+ {
+ &wl_seat_interface.name,
+ gwl_registry_wl_seat_add,
+ gwl_registry_wl_seat_remove,
+ },
+ {
+ &wl_shm_interface.name,
+ gwl_registry_wl_shm_add,
+ gwl_registry_wl_shm_remove,
+ },
+ {
+ &wl_data_device_manager_interface.name,
+ gwl_registry_wl_data_device_manager_add,
+ gwl_registry_wl_data_device_manager_remove,
+ },
+ {
+ &zwp_tablet_manager_v2_interface.name,
+ gwl_registry_wp_tablet_manager_add,
+ gwl_registry_wp_tablet_manager_remove,
+ },
+ {
+ &zwp_relative_pointer_manager_v1_interface.name,
+ gwl_registry_wp_relative_pointer_manager_add,
+ gwl_registry_wp_relative_pointer_manager_remove,
+ },
+ {
+ &zwp_pointer_constraints_v1_interface.name,
+ gwl_registry_wp_pointer_constraints_add,
+ gwl_registry_wp_pointer_constraints_remove,
+ },
+ {
+ &zwp_pointer_gestures_v1_interface.name,
+ gwl_registry_wp_pointer_gestures_add,
+ gwl_registry_wp_pointer_gestures_remove,
+ },
+ {
+ &zwp_primary_selection_device_manager_v1_interface.name,
+ gwl_registry_wp_primary_selection_device_manager_add,
+ gwl_registry_wp_primary_selection_device_manager_remove,
+ },
+ {nullptr, nullptr, nullptr},
+};
+
+/**
+ * Workaround for `gwl_registry_handlers` order of declaration,
+ * preventing `ARRAY_SIZE(gwl_registry_handlers) - 1` being used.
+ */
+static int gwl_registry_handler_interface_slot_max()
+{
+ return ARRAY_SIZE(gwl_registry_handlers) - 1;
+}
+
+static const GWL_RegistryHandler *gwl_registry_handler_from_interface_slot(int interface_slot)
+{
+ GHOST_ASSERT(uint32_t(interface_slot) < uint32_t(gwl_registry_handler_interface_slot_max()),
+ "Index out of range");
+ return &gwl_registry_handlers[interface_slot];
+}
+
+static void global_handle_add(void *data,
+ struct wl_registry *wl_registry,
+ const uint32_t name,
+ const char *interface,
+ const uint32_t version)
+{
+ /* Log last since it's useful to know if the interface was handled or not. */
+
+ GWL_Display *display = static_cast<GWL_Display *>(data);
+ /* The interface name that is ensured not to be freed. */
+ GWL_RegisteryAdd_Params params = {
+ .wl_registry = wl_registry,
+ .name = name,
+ .version = version,
+ .interface_slot = 0,
+ };
+
+ bool found = false, added = false;
+ for (const GWL_RegistryHandler *handler = gwl_registry_handlers; handler->interface_p != nullptr;
+ handler++) {
+ if (!STREQ(interface, *handler->interface_p)) {
+ continue;
+ }
+ const GWL_RegistryEntry *registry_entry_prev = display->registry_entry;
+ params.interface_slot = int(handler - gwl_registry_handlers);
+
+ handler->add_fn(display, &params);
+
+ added = display->registry_entry != registry_entry_prev;
+ found = true;
+ break;
+ }
+
+ /* Not found. */
+ if (!found) {
#ifdef USE_GNOME_NEEDS_LIBDECOR_HACK
if (STRPREFIX(interface, "gtk_shell")) { /* `gtk_shell1` at time of writing. */
/* Only require `libdecor` when built with X11 support,
@@ -4149,7 +4647,7 @@ static void global_handle_add(void *data,
CLOG_INFO(LOG,
2,
"add %s(interface=%s, version=%u, name=%u)",
- found ? "" : "(skipped), ",
+ found ? (added ? "" : "(found but not added)") : "(skipped), ",
interface,
version,
name);
@@ -4164,11 +4662,19 @@ static void global_handle_add(void *data,
* name is no longer available. If the client bound to the global
* using the bind request, the client should now destroy that object.
*/
-static void global_handle_remove(void * /*data*/,
+static void global_handle_remove(void *data,
struct wl_registry * /*wl_registry*/,
const uint32_t name)
{
- CLOG_INFO(LOG, 2, "remove (name=%u)", name);
+ GWL_Display *display = static_cast<GWL_Display *>(data);
+ int interface_slot = 0;
+ bool found = gwl_registry_entry_remove_by_name(display, name, &interface_slot);
+ CLOG_INFO(LOG,
+ 2,
+ "remove (name=%u, interface=%s), %s",
+ name,
+ *gwl_registry_handlers[interface_slot].interface_p,
+ found ? "(known)" : "(unknown)");
}
static const struct wl_registry_listener registry_listener = {
@@ -4221,7 +4727,7 @@ GHOST_SystemWayland::GHOST_SystemWayland(bool background)
}
if (display_->libdecor_required) {
- gwl_xdg_decor_system_destroy(display_->xdg_decor);
+ gwl_xdg_decor_system_destroy(display_, display_->xdg_decor);
display_->xdg_decor = nullptr;
if (!has_libdecor) {