diff --git a/src/meson.build b/src/meson.build
index 796282e86..8e72dd2da 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -504,6 +504,8 @@ if have_wayland
'wayland/meta-wayland-data-device.h',
'wayland/meta-wayland-data-device-primary.c',
'wayland/meta-wayland-data-device-primary.h',
+ 'wayland/meta-wayland-data-control.c',
+ 'wayland/meta-wayland-data-control.h',
'wayland/meta-wayland-data-offer.c',
'wayland/meta-wayland-data-offer.h',
'wayland/meta-wayland-data-offer-primary.c',
@@ -858,6 +860,7 @@ if have_wayland
['xapp-shell', 'private', ],
['xwayland-keyboard-grab', 'unstable', 'v1', ],
['wayland-drm', 'private', ],
+ ['ext-data-control', 'staging', 'v1', ],
]
if have_wayland_eglstream
wayland_eglstream_protocols_dir = wayland_eglstream_protocols_dep.get_variable(pkgconfig: 'pkgdatadir')
diff --git a/src/wayland/meta-wayland-data-control.c b/src/wayland/meta-wayland-data-control.c
new file mode 100644
index 000000000..0e1b47090
--- /dev/null
+++ b/src/wayland/meta-wayland-data-control.c
@@ -0,0 +1,574 @@
+/*
+ * Copyright (C) 2026 Linux Mint
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see .
+ */
+
+#include "config.h"
+
+#include "wayland/meta-wayland-data-control.h"
+
+#include
+#include
+#include
+
+#include "meta/meta-selection-source-memory.h"
+#include "wayland/meta-selection-source-wayland-private.h"
+#include "wayland/meta-wayland-data-offer.h"
+#include "wayland/meta-wayland-data-source.h"
+#include "wayland/meta-wayland-private.h"
+#include "wayland/meta-wayland-seat.h"
+
+#include "ext-data-control-v1-server-protocol.h"
+
+typedef struct _MetaWaylandDataSourceDataControl MetaWaylandDataSourceDataControl;
+typedef struct _MetaWaylandDataSourceDataControlClass MetaWaylandDataSourceDataControlClass;
+
+struct _MetaWaylandDataSourceDataControl
+{
+ MetaWaylandDataSource parent;
+};
+
+struct _MetaWaylandDataSourceDataControlClass
+{
+ MetaWaylandDataSourceClass parent_class;
+};
+
+G_DEFINE_TYPE (MetaWaylandDataSourceDataControl,
+ meta_wayland_data_source_data_control,
+ META_TYPE_WAYLAND_DATA_SOURCE);
+
+#define META_TYPE_WAYLAND_DATA_SOURCE_DATA_CONTROL (meta_wayland_data_source_data_control_get_type ())
+
+static void
+data_control_source_offer (struct wl_client *client,
+ struct wl_resource *resource,
+ const char *type)
+{
+ MetaWaylandDataSource *source = wl_resource_get_user_data (resource);
+
+ if (!meta_wayland_data_source_add_mime_type (source, type))
+ wl_resource_post_no_memory (resource);
+}
+
+static void
+data_control_source_destroy (struct wl_client *client,
+ struct wl_resource *resource)
+{
+ wl_resource_destroy (resource);
+}
+
+static const struct ext_data_control_source_v1_interface data_control_source_interface = {
+ data_control_source_offer,
+ data_control_source_destroy,
+};
+
+static void
+destroy_data_control_source (struct wl_resource *resource)
+{
+ MetaWaylandDataSource *source = wl_resource_get_user_data (resource);
+
+ meta_wayland_data_source_set_resource (source, NULL);
+ g_object_unref (source);
+}
+
+static void
+meta_wayland_data_source_data_control_send (MetaWaylandDataSource *source,
+ const gchar *mime_type,
+ gint fd)
+{
+ struct wl_resource *resource = meta_wayland_data_source_get_resource (source);
+
+ ext_data_control_source_v1_send_send (resource, mime_type, fd);
+ close (fd);
+}
+
+static void
+meta_wayland_data_source_data_control_cancel (MetaWaylandDataSource *source)
+{
+ struct wl_resource *resource = meta_wayland_data_source_get_resource (source);
+
+ if (resource)
+ ext_data_control_source_v1_send_cancelled (resource);
+}
+
+static void
+meta_wayland_data_source_data_control_init (MetaWaylandDataSourceDataControl *source)
+{
+}
+
+static void
+meta_wayland_data_source_data_control_class_init (MetaWaylandDataSourceDataControlClass *klass)
+{
+ MetaWaylandDataSourceClass *data_source_class =
+ META_WAYLAND_DATA_SOURCE_CLASS (klass);
+
+ data_source_class->send = meta_wayland_data_source_data_control_send;
+ data_source_class->cancel = meta_wayland_data_source_data_control_cancel;
+}
+
+static MetaWaylandDataSource *
+meta_wayland_data_source_data_control_new (struct wl_resource *resource)
+{
+ MetaWaylandDataSource *source =
+ g_object_new (META_TYPE_WAYLAND_DATA_SOURCE_DATA_CONTROL, NULL);
+
+ meta_wayland_data_source_set_resource (source, resource);
+ wl_resource_set_implementation (resource, &data_control_source_interface,
+ source, destroy_data_control_source);
+
+ return source;
+}
+
+static void
+move_resources (struct wl_list *destination,
+ struct wl_list *source)
+{
+ wl_list_insert_list (destination, source);
+ wl_list_init (source);
+}
+
+static void
+move_resources_for_client (struct wl_list *destination,
+ struct wl_list *source,
+ struct wl_client *client)
+{
+ struct wl_resource *resource, *tmp;
+ wl_resource_for_each_safe (resource, tmp, source)
+ {
+ if (wl_resource_get_client (resource) == client)
+ {
+ wl_list_remove (wl_resource_get_link (resource));
+ wl_list_insert (destination, wl_resource_get_link (resource));
+ }
+ }
+}
+
+static void
+unbind_resource (struct wl_resource *resource)
+{
+ wl_list_remove (wl_resource_get_link (resource));
+}
+
+static void
+default_destructor (struct wl_client *client,
+ struct wl_resource *resource)
+{
+ wl_resource_destroy (resource);
+}
+
+static void
+clipboard_source_destroyed (gpointer data,
+ GObject *object_was_here)
+{
+ MetaWaylandDataControlDevice *data_control = data;
+ MetaDisplay *display = meta_get_display ();
+
+ data_control->clipboard_data_source = NULL;
+
+ if (data_control->clipboard_owner)
+ {
+ meta_selection_unset_owner (meta_display_get_selection (display),
+ META_SELECTION_CLIPBOARD,
+ data_control->clipboard_owner);
+ g_clear_object (&data_control->clipboard_owner);
+ }
+}
+
+static void
+primary_source_destroyed (gpointer data,
+ GObject *object_was_here)
+{
+ MetaWaylandDataControlDevice *data_control = data;
+ MetaDisplay *display = meta_get_display ();
+
+ data_control->primary_data_source = NULL;
+
+ if (data_control->primary_owner)
+ {
+ meta_selection_unset_owner (meta_display_get_selection (display),
+ META_SELECTION_PRIMARY,
+ data_control->primary_owner);
+ g_clear_object (&data_control->primary_owner);
+ }
+}
+
+static struct wl_resource *
+create_and_send_offer (MetaWaylandDataControlDevice *data_control,
+ struct wl_resource *target,
+ MetaSelectionType selection_type)
+{
+ MetaWaylandDataOffer *offer;
+ MetaDisplay *display = meta_get_display ();
+ struct wl_resource *resource;
+ GList *mimetypes, *l;
+
+ mimetypes = meta_selection_get_mimetypes (meta_display_get_selection (display),
+ selection_type);
+ if (!mimetypes)
+ return NULL;
+
+ offer = g_slice_new0 (MetaWaylandDataOffer);
+ offer->selection_type = selection_type;
+ offer->resource = wl_resource_create (wl_resource_get_client (target),
+ &ext_data_control_offer_v1_interface,
+ wl_resource_get_version (target), 0);
+
+ {
+ static const struct ext_data_control_offer_v1_interface offer_interface = {
+ NULL, /* receive - handled below */
+ default_destructor,
+ };
+ wl_resource_set_implementation (offer->resource,
+ &offer_interface,
+ offer, NULL);
+ }
+
+ resource = offer->resource;
+
+ ext_data_control_device_v1_send_data_offer (target, resource);
+
+ for (l = mimetypes; l; l = l->next)
+ ext_data_control_offer_v1_send_offer (resource, l->data);
+
+ g_list_free_full (mimetypes, g_free);
+
+ return resource;
+}
+
+static void
+clipboard_owner_changed_cb (MetaSelection *selection,
+ MetaSelectionType selection_type,
+ MetaSelectionSource *new_owner,
+ MetaWaylandDataControlDevice *data_control)
+{
+ struct wl_resource *resource;
+
+ if (selection_type != META_SELECTION_CLIPBOARD)
+ return;
+
+ wl_resource_for_each (resource, &data_control->focus_resource_list)
+ {
+ struct wl_resource *offer = NULL;
+
+ if (new_owner)
+ {
+ offer = create_and_send_offer (data_control, resource,
+ META_SELECTION_CLIPBOARD);
+ }
+
+ ext_data_control_device_v1_send_selection (resource, offer);
+ }
+}
+
+static void
+primary_owner_changed_cb (MetaSelection *selection,
+ MetaSelectionType selection_type,
+ MetaSelectionSource *new_owner,
+ MetaWaylandDataControlDevice *data_control)
+{
+ struct wl_resource *resource;
+
+ if (selection_type != META_SELECTION_PRIMARY)
+ return;
+
+ wl_resource_for_each (resource, &data_control->focus_resource_list)
+ {
+ struct wl_resource *offer = NULL;
+
+ if (new_owner)
+ {
+ offer = create_and_send_offer (data_control, resource,
+ META_SELECTION_PRIMARY);
+ }
+
+ ext_data_control_device_v1_send_primary_selection (resource, offer);
+ }
+}
+
+static void
+ensure_owners_changed_handler_connected (MetaWaylandDataControlDevice *data_control)
+{
+ if (data_control->clipboard_selection_owner_signal_id != 0)
+ return;
+
+ data_control->clipboard_selection_owner_signal_id =
+ g_signal_connect (meta_display_get_selection (meta_get_display ()),
+ "owner-changed",
+ G_CALLBACK (clipboard_owner_changed_cb), data_control);
+}
+
+static void
+ensure_primary_owners_changed_handler_connected (MetaWaylandDataControlDevice *data_control)
+{
+ if (data_control->primary_selection_owner_signal_id != 0)
+ return;
+
+ data_control->primary_selection_owner_signal_id =
+ g_signal_connect (meta_display_get_selection (meta_get_display ()),
+ "owner-changed",
+ G_CALLBACK (primary_owner_changed_cb), data_control);
+}
+
+static void
+data_control_device_set_selection (struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *source_resource)
+{
+ MetaWaylandDataControlDevice *data_control =
+ wl_resource_get_user_data (resource);
+ MetaWaylandSeat *seat = wl_container_of (data_control, seat, data_control_device);
+ MetaWaylandDataSource *source = NULL;
+ MetaDisplay *display = meta_get_display ();
+
+ if (source_resource)
+ source = wl_resource_get_user_data (source_resource);
+
+ if (wl_resource_get_client (resource) !=
+ meta_wayland_keyboard_get_focus_client (seat->keyboard))
+ return;
+
+ if (data_control->clipboard_data_source)
+ {
+ g_object_weak_unref (G_OBJECT (data_control->clipboard_data_source),
+ clipboard_source_destroyed,
+ data_control);
+ data_control->clipboard_data_source = NULL;
+ }
+
+ if (data_control->clipboard_owner)
+ {
+ meta_selection_unset_owner (meta_display_get_selection (display),
+ META_SELECTION_CLIPBOARD,
+ data_control->clipboard_owner);
+ g_clear_object (&data_control->clipboard_owner);
+ }
+
+ data_control->clipboard_data_source = source;
+
+ if (source)
+ {
+ meta_wayland_data_source_set_seat (source, seat);
+ g_object_weak_ref (G_OBJECT (source),
+ clipboard_source_destroyed,
+ data_control);
+
+ data_control->clipboard_owner = meta_selection_source_wayland_new (source);
+ meta_selection_set_owner (meta_display_get_selection (display),
+ META_SELECTION_CLIPBOARD,
+ data_control->clipboard_owner);
+ }
+ else
+ {
+ data_control->clipboard_owner =
+ g_object_new (META_TYPE_SELECTION_SOURCE_MEMORY, NULL);
+ meta_selection_set_owner (meta_display_get_selection (display),
+ META_SELECTION_CLIPBOARD,
+ data_control->clipboard_owner);
+ g_clear_object (&data_control->clipboard_owner);
+ }
+
+ ensure_owners_changed_handler_connected (data_control);
+}
+
+static void
+data_control_device_set_primary_selection (struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *source_resource)
+{
+ MetaWaylandDataControlDevice *data_control =
+ wl_resource_get_user_data (resource);
+ MetaWaylandSeat *seat = wl_container_of (data_control, seat, data_control_device);
+ MetaWaylandDataSource *source = NULL;
+ MetaDisplay *display = meta_get_display ();
+
+ if (source_resource)
+ source = wl_resource_get_user_data (source_resource);
+
+ if (wl_resource_get_client (resource) !=
+ meta_wayland_keyboard_get_focus_client (seat->keyboard))
+ return;
+
+ if (data_control->primary_data_source)
+ {
+ g_object_weak_unref (G_OBJECT (data_control->primary_data_source),
+ primary_source_destroyed,
+ data_control);
+ data_control->primary_data_source = NULL;
+ }
+
+ if (data_control->primary_owner)
+ {
+ meta_selection_unset_owner (meta_display_get_selection (display),
+ META_SELECTION_PRIMARY,
+ data_control->primary_owner);
+ g_clear_object (&data_control->primary_owner);
+ }
+
+ data_control->primary_data_source = source;
+
+ if (source)
+ {
+ meta_wayland_data_source_set_seat (source, seat);
+ g_object_weak_ref (G_OBJECT (source),
+ primary_source_destroyed,
+ data_control);
+
+ data_control->primary_owner = meta_selection_source_wayland_new (source);
+ meta_selection_set_owner (meta_display_get_selection (display),
+ META_SELECTION_PRIMARY,
+ data_control->primary_owner);
+ }
+ else
+ {
+ data_control->primary_owner =
+ g_object_new (META_TYPE_SELECTION_SOURCE_MEMORY, NULL);
+ meta_selection_set_owner (meta_display_get_selection (display),
+ META_SELECTION_PRIMARY,
+ data_control->primary_owner);
+ g_clear_object (&data_control->primary_owner);
+ }
+
+ ensure_primary_owners_changed_handler_connected (data_control);
+}
+
+static const struct ext_data_control_device_v1_interface data_control_device_interface = {
+ data_control_device_set_selection,
+ default_destructor,
+ data_control_device_set_primary_selection,
+};
+
+static void
+data_control_manager_create_source (struct wl_client *client,
+ struct wl_resource *manager_resource,
+ guint32 id)
+{
+ struct wl_resource *source_resource;
+
+ source_resource =
+ wl_resource_create (client, &ext_data_control_source_v1_interface,
+ wl_resource_get_version (manager_resource), id);
+ meta_wayland_data_source_data_control_new (source_resource);
+}
+
+static void
+data_control_manager_get_device (struct wl_client *client,
+ struct wl_resource *manager_resource,
+ guint32 id,
+ struct wl_resource *seat_resource)
+{
+ MetaWaylandSeat *seat = wl_resource_get_user_data (seat_resource);
+ struct wl_resource *cr;
+
+ cr = wl_resource_create (client, &ext_data_control_device_v1_interface,
+ wl_resource_get_version (manager_resource), id);
+ wl_resource_set_implementation (cr, &data_control_device_interface,
+ &seat->data_control_device, unbind_resource);
+ wl_list_insert (&seat->data_control_device.resource_list,
+ wl_resource_get_link (cr));
+
+ ensure_owners_changed_handler_connected (&seat->data_control_device);
+ ensure_primary_owners_changed_handler_connected (&seat->data_control_device);
+}
+
+static const struct ext_data_control_manager_v1_interface data_control_manager_interface = {
+ data_control_manager_create_source,
+ data_control_manager_get_device,
+ default_destructor,
+};
+
+static void
+bind_data_control_manager (struct wl_client *client,
+ void *data,
+ uint32_t version,
+ uint32_t id)
+{
+ struct wl_resource *resource;
+
+ resource = wl_resource_create (client,
+ &ext_data_control_manager_v1_interface,
+ version, id);
+ wl_resource_set_implementation (resource, &data_control_manager_interface,
+ NULL, NULL);
+}
+
+void
+meta_wayland_data_control_manager_init (MetaWaylandCompositor *compositor)
+{
+ if (wl_global_create (compositor->wayland_display,
+ &ext_data_control_manager_v1_interface,
+ META_EXT_DATA_CONTROL_MANAGER_V1_VERSION,
+ NULL, bind_data_control_manager) == NULL)
+ g_error ("Could not create ext_data_control_manager");
+}
+
+void
+meta_wayland_data_control_device_init (MetaWaylandDataControlDevice *data_control)
+{
+ wl_list_init (&data_control->resource_list);
+ wl_list_init (&data_control->focus_resource_list);
+}
+
+void
+meta_wayland_data_control_device_set_keyboard_focus (MetaWaylandDataControlDevice *data_control)
+{
+ MetaWaylandSeat *seat = wl_container_of (data_control, seat, data_control_device);
+ struct wl_client *focus_client;
+ struct wl_resource *resource;
+
+ focus_client = meta_wayland_keyboard_get_focus_client (seat->keyboard);
+
+ if (focus_client == data_control->focus_client)
+ return;
+
+ data_control->focus_client = focus_client;
+ move_resources (&data_control->resource_list,
+ &data_control->focus_resource_list);
+
+ if (!focus_client)
+ return;
+
+ move_resources_for_client (&data_control->focus_resource_list,
+ &data_control->resource_list,
+ focus_client);
+
+ wl_resource_for_each (resource, &data_control->focus_resource_list)
+ {
+ struct wl_resource *clipboard_offer = NULL;
+ struct wl_resource *primary_offer = NULL;
+ GList *mimetypes;
+
+ mimetypes = meta_selection_get_mimetypes (meta_display_get_selection (meta_get_display ()),
+ META_SELECTION_CLIPBOARD);
+ if (mimetypes)
+ {
+ clipboard_offer = create_and_send_offer (data_control, resource,
+ META_SELECTION_CLIPBOARD);
+ g_list_free_full (mimetypes, g_free);
+ }
+
+ ext_data_control_device_v1_send_selection (resource, clipboard_offer);
+
+ mimetypes = meta_selection_get_mimetypes (meta_display_get_selection (meta_get_display ()),
+ META_SELECTION_PRIMARY);
+ if (mimetypes)
+ {
+ primary_offer = create_and_send_offer (data_control, resource,
+ META_SELECTION_PRIMARY);
+ g_list_free_full (mimetypes, g_free);
+ }
+
+ ext_data_control_device_v1_send_primary_selection (resource, primary_offer);
+ }
+}
diff --git a/src/wayland/meta-wayland-data-control.h b/src/wayland/meta-wayland-data-control.h
new file mode 100644
index 000000000..922a4c5cb
--- /dev/null
+++ b/src/wayland/meta-wayland-data-control.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2026 Linux Mint
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see .
+ */
+
+#ifndef META_WAYLAND_DATA_CONTROL_H
+#define META_WAYLAND_DATA_CONTROL_H
+
+#include
+#include
+
+#include "meta/meta-selection-source.h"
+#include "wayland/meta-wayland-data-offer.h"
+#include "wayland/meta-wayland-data-source.h"
+#include "wayland/meta-wayland-types.h"
+
+struct _MetaWaylandDataControlDevice
+{
+ struct wl_list resource_list;
+ struct wl_list focus_resource_list;
+ struct wl_client *focus_client;
+
+ MetaWaylandDataSource *clipboard_data_source;
+ guint clipboard_selection_owner_signal_id;
+ MetaSelectionSource *clipboard_owner;
+
+ MetaWaylandDataSource *primary_data_source;
+ guint primary_selection_owner_signal_id;
+ MetaSelectionSource *primary_owner;
+};
+
+void meta_wayland_data_control_manager_init (MetaWaylandCompositor *compositor);
+
+void meta_wayland_data_control_device_init (MetaWaylandDataControlDevice *data_control);
+
+void meta_wayland_data_control_device_set_keyboard_focus (MetaWaylandDataControlDevice *data_control);
+
+#endif /* META_WAYLAND_DATA_CONTROL_H */
diff --git a/src/wayland/meta-wayland-seat.c b/src/wayland/meta-wayland-seat.c
index 770bfffe9..2352faf68 100644
--- a/src/wayland/meta-wayland-seat.c
+++ b/src/wayland/meta-wayland-seat.c
@@ -244,6 +244,7 @@ meta_wayland_seat_new (MetaWaylandCompositor *compositor,
meta_wayland_data_device_init (&seat->data_device);
meta_wayland_data_device_primary_init (&seat->primary_data_device);
+ meta_wayland_data_control_device_init (&seat->data_control_device);
clutter_seat = clutter_backend_get_default_seat (clutter_get_default_backend ());
meta_wayland_seat_update_capabilities (seat, clutter_seat);
@@ -458,6 +459,7 @@ meta_wayland_seat_set_input_focus (MetaWaylandSeat *seat,
meta_wayland_keyboard_set_focus (seat->keyboard, surface);
meta_wayland_data_device_set_keyboard_focus (&seat->data_device);
meta_wayland_data_device_primary_set_keyboard_focus (&seat->primary_data_device);
+ meta_wayland_data_control_device_set_keyboard_focus (&seat->data_control_device);
}
tablet_seat = meta_wayland_tablet_manager_ensure_seat (compositor->tablet_manager, seat);
diff --git a/src/wayland/meta-wayland-seat.h b/src/wayland/meta-wayland-seat.h
index 1cce57f6d..25d62e469 100644
--- a/src/wayland/meta-wayland-seat.h
+++ b/src/wayland/meta-wayland-seat.h
@@ -25,6 +25,7 @@
#include
#include "clutter/clutter.h"
+#include "wayland/meta-wayland-data-control.h"
#include "wayland/meta-wayland-data-device.h"
#include "wayland/meta-wayland-data-device-primary.h"
#include "wayland/meta-wayland-input-device.h"
@@ -47,6 +48,7 @@ struct _MetaWaylandSeat
MetaWaylandDataDevice data_device;
MetaWaylandDataDevicePrimary primary_data_device;
+ MetaWaylandDataControlDevice data_control_device;
MetaWaylandTextInput *text_input;
MetaWaylandInputMethod *input_method;
diff --git a/src/wayland/meta-wayland-types.h b/src/wayland/meta-wayland-types.h
index 8ab2472ed..2a5dec4f1 100644
--- a/src/wayland/meta-wayland-types.h
+++ b/src/wayland/meta-wayland-types.h
@@ -38,6 +38,7 @@ typedef struct _MetaWaylandDragDestFuncs MetaWaylandDragDestFuncs;
typedef struct _MetaWaylandDataOffer MetaWaylandDataOffer;
typedef struct _MetaWaylandDataDevice MetaWaylandDataDevice;
typedef struct _MetaWaylandDataDevicePrimary MetaWaylandDataDevicePrimary;
+typedef struct _MetaWaylandDataControlDevice MetaWaylandDataControlDevice;
typedef struct _MetaWaylandInputMethod MetaWaylandInputMethod;
typedef struct _MetaWaylandVirtualKeyboardManager MetaWaylandVirtualKeyboardManager;
diff --git a/src/wayland/meta-wayland-versions.h b/src/wayland/meta-wayland-versions.h
index b4b3ef2f7..7694b823c 100644
--- a/src/wayland/meta-wayland-versions.h
+++ b/src/wayland/meta-wayland-versions.h
@@ -68,5 +68,6 @@
#define META_ZWLR_LAYER_SHELL_V1_VERSION 4
#define META_XDG_TOPLEVEL_ICON_V1_VERSION 1
#define META_XAPP_SHELL_V1_VERSION 1
+#define META_EXT_DATA_CONTROL_MANAGER_V1_VERSION 1
#endif
diff --git a/src/wayland/meta-wayland.c b/src/wayland/meta-wayland.c
index f0eba38ed..7c192ec5d 100644
--- a/src/wayland/meta-wayland.c
+++ b/src/wayland/meta-wayland.c
@@ -453,6 +453,7 @@ meta_wayland_compositor_setup (MetaWaylandCompositor *wayland_compositor)
meta_wayland_outputs_init (compositor);
meta_wayland_data_device_manager_init (compositor);
meta_wayland_data_device_primary_manager_init (compositor);
+ meta_wayland_data_control_manager_init (compositor);
meta_wayland_subsurfaces_init (compositor);
meta_wayland_shell_init (compositor);
meta_wayland_pointer_gestures_init (compositor);