]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.1.1726: Patch v9.1.1725 causes problems v9.1.1726
authorChristian Brabandt <cb@256bit.org>
Tue, 2 Sep 2025 17:06:06 +0000 (19:06 +0200)
committerChristian Brabandt <cb@256bit.org>
Tue, 2 Sep 2025 17:13:31 +0000 (19:13 +0200)
Problem:  Patch v9.1.1725 causes problems
Solution: Revert the patch for now

fixes: #18187

Signed-off-by: Christian Brabandt <cb@256bit.org>
29 files changed:
Filelist
runtime/doc/builtin.txt
runtime/doc/gui_x11.txt
runtime/doc/options.txt
runtime/doc/tags
runtime/doc/various.txt
runtime/doc/wayland.txt
runtime/optwin.vim
src/Makefile
src/auto/configure
src/clipboard.c
src/config.h.in
src/configure.ac
src/evalfunc.c
src/feature.h
src/globals.h
src/main.c
src/option.c
src/option.h
src/optiondefs.h
src/optionstr.c
src/os_unix.c
src/proto/clipboard.pro
src/proto/wayland.pro
src/structs.h
src/testdir/test_wayland.vim
src/version.c
src/wayland.c
src/wayland.h [deleted file]

index 3a2e066e33d8cd79973e4c1e860e24fdfb1d4079..e97e17ec0e89487b51a20638722cb72371b871fb 100644 (file)
--- a/Filelist
+++ b/Filelist
@@ -537,7 +537,6 @@ SRC_UNIX =  \
                src/vimtutor \
                src/gvimtutor \
                src/wayland.c \
-               src/wayland.h \
                src/which.sh \
                src/gen-wayland-protocols.sh \
                src/xxd/Makefile \
index e020d8852b9cd6d6f1fb0eb5557496be0f7f2419..40e6d93eb5f8a7d07a4ce6f555c2bef3f1a34676 100644 (file)
@@ -1,4 +1,4 @@
-*builtin.txt*  For Vim version 9.1.  Last change: 2025 Sep 01
+*builtin.txt*  For Vim version 9.1.  Last change: 2025 Sep 02
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -13161,9 +13161,7 @@ vreplace                Compiled with |gR| and |gr| commands. (always true)
 vtp                    Compiled for vcon support |+vtp| (check vcon to find
                        out if it works in the current console).
 wayland                        Compiled with Wayland protocol support.
-wayland_clipboard      Compiled with support for Wayland clipboard.
-wayland_focus_steal    Compiled with support for Wayland clipboard focus
-                       stealing.
+wayland_clipboard      Compiled with support for Wayland selections/clipboard
 wildignore             Compiled with 'wildignore' option.
 wildmenu               Compiled with 'wildmenu' option.
 win16                  old version for MS-Windows 3.1 (always false)
index 2925a33e19fbb529fd9572f7e9d863422b8924c2..89ba7c2ee4fb146e5f3b99e4bfb5398cc6834f3c 100644 (file)
@@ -1,4 +1,4 @@
-*gui_x11.txt*   For Vim version 9.1.  Last change: 2025 Sep 01
+*gui_x11.txt*   For Vim version 9.1.  Last change: 2025 Sep 02
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -709,8 +709,8 @@ overwriting selected text.
 
                                                                *W23*
 When you are yanking into the "* or "+ register and Vim cannot establish a
-connection to the X11 selection (or X11/Wayland clipboard), it will use
-register 0 and output a warning:
+connection to the X11 selection (or clipboard), it will use register 0 and
+output a warning:
 
   Warning: Clipboard register not available, using register 0 ~
 
index 23607a3ad313725e16c486a43a8f5764eb18dba6..20fe85b84db94698d54ccf3c7e3c4b3337fa9369 100644 (file)
@@ -1,4 +1,4 @@
-*options.txt*  For Vim version 9.1.  Last change: 2025 Sep 01
+*options.txt*  For Vim version 9.1.  Last change: 2025 Sep 02
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -10195,8 +10195,7 @@ A jump table for the options with a short description can be found at |Q_op|.
                                *'wlsteal'* *'wst'* *'nowlsteal'* *'nowst'*
 'wlsteal' 'wst'                boolean  (default off)
                        global
-                       {only when the |+wayland_focus_steal| feature is
-                       included}
+                       {only when the |+wayland_clipboard| feature is included}
        When enabled, then allow Vim to steal focus by creating a temporary
        surface, in order to access the clipboard.  For more information see
        |wayland-focus-steal|.
index e988ffb5786349f2e69e0ebe10b1f0a5a5a819b0..b00bf835b4ab8628a1085b85cf186cf774ae906b 100644 (file)
@@ -1542,7 +1542,6 @@ $quote    eval.txt        /*$quote*
 +vtp   various.txt     /*+vtp*
 +wayland       various.txt     /*+wayland*
 +wayland_clipboard     various.txt     /*+wayland_clipboard*
-+wayland_focus_steal   various.txt     /*+wayland_focus_steal*
 +wildignore    various.txt     /*+wildignore*
 +wildmenu      various.txt     /*+wildmenu*
 +windows       various.txt     /*+windows*
index 16bab26932765788d2e4421ea077080e56358a24..e29fafdf72c53758588952e630779748053e7837 100644 (file)
@@ -1,4 +1,4 @@
-*various.txt*   For Vim version 9.1.  Last change: 2025 Sep 01
+*various.txt*   For Vim version 9.1.  Last change: 2025 Sep 02
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -528,8 +528,6 @@ T  *+vreplace*              |gR| and |gr|
    *+vtp*              on MS-Windows console: support for 'termguicolors'
 N  *+wayland*          Unix only: support for the Wayland protocol.
 N  *+wayland_clipboard*        Unix only: support for Wayland selections/clipboard.
-N  *+wayland_focus_steal* Unix only: support for Wayland clipboard on
-                       compositors without a data control protocol.
 T  *+wildignore*       'wildignore'  Always enabled since 9.0.0278
 T  *+wildmenu*         'wildmenu'  Always enabled since 9.0.0279
 T  *+windows*          more than one window; Always enabled since 8.0.1118.
index eda3e25752e67883e8ba7c4e0b86ed1c0688a419..ef70f67c403f0870e1bad8ba479f98dbc1b87e3b 100644 (file)
@@ -1,4 +1,4 @@
-*wayland.txt*   For Vim version 9.1.  Last change: 2025 Sep 01
+*wayland.txt*   For Vim version 9.1.  Last change: 2025 Sep 02
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -105,8 +105,7 @@ To solve this problem, Vim implements a way of gaining focus in order to
 access the clipboard, by creating a temporary transparent top-level surface.
 This is by default disabled and can be enabled via the 'wlsteal' option.
 Moreover, a seat that has a keyboard is also required, see 'wlseat', and the
-xdg-shell protocol must be available.  Additionally, Vim must be compiled with
-the |+wayland_focus_steal| feature.
+xdg-shell protocol must be available.
 
 Note that this method can have several side effects from the result of focus
 stealing.  For example, if you have a taskbar that shows currently opened apps
index faa90bb121dbf3db3bd61f0696206a4c3157e987..cf743aa7a7a7dfca190e639855e8104341d38ece 100644 (file)
@@ -1,7 +1,7 @@
 " These commands create the option window.
 "
 " Maintainer:  The Vim Project <https://github.com/vim/vim>
-" Last Change: 2025 Sep 01
+" Last Change: 2025 Sep 02
 " Former Maintainer:   Bram Moolenaar <Bram@vim.org>
 
 " If there already is an option window, jump to that one.
@@ -822,7 +822,7 @@ if has('wayland')
   call <SID>AddOption("wlseat", gettext("Wayland seat to use"))
   call <SID>OptionG("wse", &wse)
 endif
-if has("wayland_focus_steal")
+if has("wayland_clipboard")
   call <SID>AddOption("wlsteal", gettext("Enable wayland focus stealing functionality in order to access the clipboard"))
   call <SID>BinOptionG("wst", &wst)
 endif
index 2bebd50081378188131d4c844a91b7599422ef78..d1b08be257d01757287d5bfa3a7fd77637fdb35e 100644 (file)
@@ -1646,9 +1646,7 @@ MESSAGE_TEST_TARGET = message_test$(EXEEXT)
 
 UNITTEST_SRC = $(JSON_TEST_SRC) $(KWORD_TEST_SRC) $(MEMFILE_TEST_SRC) $(MESSAGE_TEST_SRC)
 UNITTEST_TARGETS = $(JSON_TEST_TARGET) $(KWORD_TEST_TARGET) $(MEMFILE_TEST_TARGET) $(MESSAGE_TEST_TARGET)
-# We need to put WAYLAND_SRC because the protocol files need to be generated
-# else wayland.h will error
-RUN_UNITTESTS = $(WAYLAND_SRC) run_json_test run_kword_test run_memfile_test run_message_test
+RUN_UNITTESTS = run_json_test run_kword_test run_memfile_test run_message_test
 
 # All sources, also the ones that are not configured
 ALL_LOCAL_SRC = $(BASIC_SRC) $(ALL_GUI_SRC) $(UNITTEST_SRC) $(EXTRA_SRC) \
@@ -3863,9 +3861,9 @@ objects/clientserver.o: clientserver.c vim.h protodef.h auto/config.h feature.h
  ex_cmds.h spell.h proto.h globals.h errors.h
 objects/clipboard.o: clipboard.c vim.h protodef.h auto/config.h feature.h \
  os_unix.h auto/osdef.h ascii.h keymap.h termdefs.h macros.h option.h \
- beval.h structs.h regexp.h gui.h libvterm/include/vterm.h \
- libvterm/include/vterm_keycodes.h xdiff/xdiff.h xdiff/../vim.h alloc.h \
- ex_cmds.h spell.h proto.h globals.h errors.h wayland.h
+ beval.h proto/gui_beval.pro structs.h regexp.h gui.h \
+ libvterm/include/vterm.h libvterm/include/vterm_keycodes.h alloc.h \
+ ex_cmds.h spell.h proto.h globals.h errors.h
 objects/cmdexpand.o: cmdexpand.c vim.h protodef.h auto/config.h feature.h \
  os_unix.h auto/osdef.h ascii.h keymap.h termdefs.h macros.h option.h \
  beval.h proto/gui_beval.pro structs.h regexp.h gui.h \
@@ -4566,7 +4564,7 @@ objects/wayland.o: wayland.c vim.h protodef.h auto/config.h feature.h os_unix.h
  auto/osdef.h ascii.h keymap.h termdefs.h macros.h option.h beval.h \
  structs.h regexp.h gui.h libvterm/include/vterm.h \
  libvterm/include/vterm_keycodes.h xdiff/xdiff.h xdiff/../vim.h alloc.h \
- ex_cmds.h spell.h proto.h globals.h errors.h wayland.h \
+ ex_cmds.h spell.h proto.h globals.h errors.h \
  auto/wayland/wlr-data-control-unstable-v1.h \
  auto/wayland/ext-data-control-v1.h auto/wayland/xdg-shell.h \
  auto/wayland/primary-selection-unstable-v1.h
index 24bbebc9fd0481d41e6142fe61c87c7c015e505e..5c0d3d614f90d9feaf9bf4e59f3afc9a190b4ded 100755 (executable)
@@ -862,7 +862,6 @@ enable_farsi
 enable_xim
 enable_fontset
 with_wayland
-enable_wayland_focus_steal
 with_x
 enable_gui
 enable_gtk2_check
@@ -1543,9 +1542,6 @@ Optional Features:
   --disable-farsi         Deprecated.
   --enable-xim            Include XIM input support.
   --enable-fontset        Include X fontset output support.
-  --enable-wayland-focus-steal
-                          Include focus stealing support for Wayland
-                          clipboard.
   --enable-gui=OPTS       X11 GUI. default=auto OPTS=auto/no/gtk2/gnome2/gtk3/motif/haiku/photon/carbon
   --enable-gtk2-check     If auto-select GUI, check for GTK+ 2 default=yes
   --enable-gnome-check    If GTK GUI, check for GNOME default=no
@@ -9275,39 +9271,13 @@ fi
 
 
 if test "$with_wayland" = yes; then
-  cppflags_save=$CPPFLAGS
-  cflags_save=$CFLAGS
-
+cppflags_save=$CPPFLAGS
+cflags_save=$CFLAGS
   { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for wayland" >&5
 printf %s "checking for wayland... " >&6; }
   if "$PKG_CONFIG" --exists 'wayland-client'; then
     { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
 printf "%s\n" "yes" >&6; }
-
-    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking --enable-wayland-focus-steal argument" >&5
-printf %s "checking --enable-wayland-focus-steal argument... " >&6; }
-    # Check whether --enable-wayland-focus-steal was given.
-if test ${enable_wayland_focus_steal+y}
-then :
-  enableval=$enable_wayland_focus_steal; enable_wayland_fs=$enableval
-else case e in #(
-  e) enable_wayland_fs="yes" ;;
-esac
-fi
-
-
-    if test "$enable_wayland_fs" = "yes"
-then :
-  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-printf "%s\n" "yes" >&6; }
-          printf "%s\n" "#define FEAT_WAYLAND_CLIPBOARD_FS 1" >>confdefs.h
-
-else case e in #(
-  e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
-printf "%s\n" "no" >&6; } ;;
-esac
-fi
-
     printf "%s\n" "#define HAVE_WAYLAND 1" >>confdefs.h
 
     WAYLAND_CPPFLAGS=`$PKG_CONFIG --cflags-only-I wayland-client`
@@ -9318,23 +9288,16 @@ fi
     WAYLAND_SRC=" \
       auto/wayland/wlr-data-control-unstable-v1.c \
       auto/wayland/ext-data-control-v1.c \
+      auto/wayland/xdg-shell.c \
+      auto/wayland/primary-selection-unstable-v1.c \
       wayland.c"
     WAYLAND_OBJ=" \
       objects/wlr-data-control-unstable-v1.o \
       objects/ext-data-control-v1.o \
+      objects/xdg-shell.o \
+      objects/primary-selection-unstable-v1.o \
       objects/wayland.o"
 
-    if test "$enable_wayland_fs" = "yes"
-then :
-  WAYLAND_SRC+=" \
-            auto/wayland/xdg-shell.c \
-            auto/wayland/primary-selection-unstable-v1.c"
-           WAYLAND_OBJ+=" \
-            objects/xdg-shell.o \
-            objects/primary-selection-unstable-v1.o"
-fi
-
-
 
 
 
index b96080207bcdade544559721d57c3590b1163e51..4b2e6874a94a759cee067d0a7829e3ea7cafbcdc 100644 (file)
 #if defined(FEAT_CLIPBOARD) || defined(PROTO)
 
 #if defined(FEAT_WAYLAND_CLIPBOARD)
-
-# include "wayland.h"
-
-# ifdef FEAT_WAYLAND_CLIPBOARD_FS
-
-// Structures used for focus stealing
-typedef struct {
-    struct wl_shm_pool *pool;
-    int                        fd;
-
-    struct wl_buffer   *buffer;
-    bool               available;
-
-    int                        width;
-    int                        height;
-    int                        stride;
-    int                        size;
-} clip_wl_buffer_store_T;
-
-typedef struct {
-    void                   *user_data;
-    void                   (*on_focus)(void *data, uint32_t serial);
-
-    struct wl_surface      *surface;
-    struct wl_keyboard     *keyboard;
-
-    struct {
-       struct xdg_surface  *surface;
-       struct xdg_toplevel *toplevel;
-    } shell;
-
-    bool got_focus;
-} clip_wl_fs_surface_T; // fs = focus steal
-
-# endif // FEAT_WAYLAND_CLIPBOARD_FS
-
-// Represents either the regular or primary selection
-typedef struct {
-    char_u             *contents;      // Non-null if we own selection,
-                                       // contains the data to send to other
-                                       // clients.
-    vwl_data_source_T  *source;        // Non-NULL if we own the selection,
-                                       // else NULL if we don't.
-    vwl_data_offer_T   *offer;         // Current offer for the selection
-
-# ifdef FEAT_WAYLAND_CLIPBOARD_FS
-    bool               requires_focus; // If focus needs to be given to us to
-                                       // work
-# endif
-    bool               own_success;    // Used by clip_wl_own_selection()
-    bool               available;      // If selection is ready to serve/use
-
-    // These may point to the same proxy as the other selection
-    vwl_data_device_manager_T  *manager;
-    vwl_data_device_T          *device;
-} clip_wl_selection_T;
-
-// Represents the clipboard for the global Wayland connection, for the chosen
-// seat (using the 'wl_seat' option)
-typedef struct {
-    vwl_seat_T *seat;
-
-#ifdef FEAT_WAYLAND_CLIPBOARD_FS
-    clip_wl_buffer_store_T *fs_buffer;
-#endif
-
-    clip_wl_selection_T regular;
-    clip_wl_selection_T primary;
-
-    // Array of file descriptors of clients we are sending data to. These should
-    // be polled for POLLOUT and have the respective callback called for each.
-    garray_T write_fds;
-} clip_wl_T;
-
 // Mime types we support sending and receiving
 // Mimes with a lower index in the array are prioritized first when we are
 // receiving data.
@@ -119,20 +45,21 @@ static const char *supported_mimes[] = {
     "TEXT"
 };
 
-clip_wl_T clip_wl;
-
-static void
-clip_wl_receive_data(Clipboard_T *cbd, const char *mime_type, int fd);
+static void clip_wl_receive_data(Clipboard_T *cbd,
+       const char *mime_type, int fd);
 static void clip_wl_request_selection(Clipboard_T *cbd);
+static void clip_wl_send_data(const char *mime_type, int fd,
+       wayland_selection_T);
 static int clip_wl_own_selection(Clipboard_T *cbd);
 static void clip_wl_lose_selection(Clipboard_T *cbd);
 static void clip_wl_set_selection(Clipboard_T *cbd);
+static void clip_wl_selection_cancelled(wayland_selection_T selection);
 
-# if defined(USE_SYSTEM) && defined(PROTO)
-static bool clip_wl_owner_exists(Clipboard_T *cbd);
-# endif
+#if defined(USE_SYSTEM) && defined(PROTO)
+static int clip_wl_owner_exists(Clipboard_T *cbd);
+#endif
 
-#endif // FEAT_WAYLAND_CLIPBOARD
+#endif
 
 /*
  * Selection stuff using Visual mode, for cutting and pasting text to other
@@ -172,24 +99,6 @@ skip:
     }
 }
 
-#ifdef FEAT_WAYLAND_CLIPBOARD
-    static void
-clip_init_single(Clipboard_T *cb, int can_use)
-{
-    // No need to init again if cbd is already available
-    if (can_use && cb->available)
-       return;
-
-    cb->available  = can_use;
-    cb->owned      = FALSE;
-    cb->start.lnum = 0;
-    cb->start.col  = 0;
-    cb->end.lnum   = 0;
-    cb->end.col    = 0;
-    cb->state      = SELECT_CLEARED;
-}
-#endif
-
 /*
  * Check whether the VIsual area has changed, and if so try to become the owner
  * of the selection, and free any old converted selection we may still have
@@ -2298,13 +2207,13 @@ clip_yank_selection(
     str_to_reg(y_ptr, type, str, len, -1, FALSE);
 }
 
-    static int
-clip_convert_selection_offset(
-       char_u      **str,
-       long_u      *len,
-       int         offset, // Extra space to add in *str and the offset to
-                           // place the actual string in *str.
-       Clipboard_T *cbd)
+/*
+ * Convert the '*'/'+' register into a GUI selection string returned in *str
+ * with length *len.
+ * Returns the motion type, or -1 for failure.
+ */
+    int
+clip_convert_selection(char_u **str, long_u *len, Clipboard_T *cbd)
 {
     char_u     *p;
     int                lnum;
@@ -2335,13 +2244,11 @@ clip_convert_selection_offset(
     if (y_ptr->y_type == MCHAR && *len >= eolsize)
        *len -= eolsize;
 
-    *len += offset;
     p = *str = alloc(*len + 1);        // add one to avoid zero
     if (p == NULL)
        return -1;
-    p += offset;
     lnum = 0;
-    for (i = 0, j = 0; i < (int)*len - offset; i++, j++)
+    for (i = 0, j = 0; i < (int)*len; i++, j++)
     {
        if (y_ptr->y_array[lnum].string[j] == '\n')
            p[i] = NUL;
@@ -2360,17 +2267,6 @@ clip_convert_selection_offset(
     return y_ptr->y_type;
 }
 
-/*
- * Convert the '*'/'+' register into a GUI selection string returned in *str
- * with length *len.
- * Returns the motion type, or -1 for failure.
- */
-    int
-clip_convert_selection(char_u **str, long_u *len, Clipboard_T *cbd)
-{
-    return clip_convert_selection_offset(str, len, 0, cbd);
-}
-
 /*
  * When "regname" is a clipboard register, obtain the selection.  If it's not
  * available return zero, otherwise return "regname".
@@ -2441,594 +2337,7 @@ adjust_clip_reg(int *rp)
     }
 }
 
-#if defined(FEAT_WAYLAND_CLIPBOARD)
-
-    static clip_wl_selection_T *
-clip_wl_get_selection(wayland_selection_T sel)
-{
-    switch (sel)
-    {
-       case WAYLAND_SELECTION_REGULAR:
-           return &clip_wl.regular;
-       case WAYLAND_SELECTION_PRIMARY:
-           return &clip_wl.primary;
-       default:
-           return NULL;
-    }
-}
-
-    static clip_wl_selection_T *
-clip_wl_get_selection_from_cbd(Clipboard_T *cbd)
-{
-    if (cbd == &clip_plus)
-       return &clip_wl.regular;
-    else if (cbd == &clip_star)
-       return &clip_wl.primary;
-    else
-       return NULL;
-}
-
-    static Clipboard_T *
-clip_wl_get_cbd_from_selection(clip_wl_selection_T *sel)
-{
-    if (sel == &clip_wl.regular)
-       return &clip_plus;
-    else if (sel == &clip_wl.primary)
-       return &clip_star;
-    else
-       return NULL;
-}
-
-    static wayland_selection_T
-clip_wl_get_selection_type(clip_wl_selection_T *sel)
-{
-    if (sel == &clip_wl.regular)
-       return WAYLAND_SELECTION_REGULAR;
-    else if (sel == &clip_wl.primary)
-       return WAYLAND_SELECTION_PRIMARY;
-    else
-       return WAYLAND_SELECTION_NONE;
-}
-
-#ifdef FEAT_WAYLAND_CLIPBOARD_FS
-/*
- * If globals required for focus stealing method are available.
- */
-    static bool
-clip_wl_focus_stealing_available(void)
-{
-    return wayland_ct->gobjects.wl_compositor != NULL &&
-       wayland_ct->gobjects.wl_shm != NULL &&
-       wayland_ct->gobjects.xdg_wm_base != NULL;
-}
-
-/*
- * Called when compositor isn't using the buffer anymore, we can reuse it
- * again.
- */
-    static void
-wl_buffer_listener_release(
-       void                *data,
-       struct wl_buffer    *buffer UNUSED)
-{
-    clip_wl_buffer_store_T *store = data;
-
-    store->available = true;
-}
-
-static struct wl_buffer_listener    wl_buffer_listener = {
-    .release       = wl_buffer_listener_release
-};
-
-/*
- * Destroy a buffer store structure.
- */
-    static void
-clip_wl_destroy_buffer_store(clip_wl_buffer_store_T *store)
-{
-    if (store == NULL)
-       return;
-    if (store->buffer != NULL)
-       wl_buffer_destroy(store->buffer);
-    if (store->pool != NULL)
-       wl_shm_pool_destroy(store->pool);
-
-    close(store->fd);
-    vim_free(store);
-}
-
-/*
- * Initialize a buffer and its backing memory pool.
- */
-    static clip_wl_buffer_store_T *
-clip_wl_init_buffer_store(int width, int height)
-{
-    int                            fd, r;
-    clip_wl_buffer_store_T  *store;
-
-    store = alloc(sizeof(*store));
-
-    if (store == NULL)
-       return NULL;
-
-    store->available = false;
-    store->width = width;
-    store->height = height;
-    store->stride = store->width * 4;
-    store->size = store->stride * store->height;
-
-    fd = mch_create_anon_file();
-    r = ftruncate(fd, store->size);
-
-    if (r == -1)
-    {
-       if (fd >= 0)
-           close(fd);
-       return NULL;
-    }
-
-    store->pool = wl_shm_create_pool(
-           wayland_ct->gobjects.wl_shm,
-           fd,
-           store->size);
-    store->buffer = wl_shm_pool_create_buffer(
-           store->pool,
-           0,
-           store->width,
-           store->height,
-           store->stride,
-           WL_SHM_FORMAT_ARGB8888);
-
-    store->fd = fd;
-
-    wl_buffer_add_listener(store->buffer, &wl_buffer_listener, store);
-
-    if (vwl_connection_roundtrip(wayland_ct) == FAIL)
-    {
-       clip_wl_destroy_buffer_store(store);
-       return NULL;
-    }
-
-    store->available = true;
-
-    return store;
-}
-
-/*
- * Configure xdg_surface
- */
-    static void
-xdg_surface_listener_configure(
-       void                *data UNUSED,
-       struct xdg_surface  *surface,
-       uint32_t            serial)
-{
-    xdg_surface_ack_configure(surface, serial);
-}
-
-
-static struct xdg_surface_listener  xdg_surface_listener = {
-    .configure = xdg_surface_listener_configure
-};
-
-/*
- * Destroy a focus stealing structure.
- */
-    static void
-clip_wl_destroy_fs_surface(clip_wl_fs_surface_T *store)
-{
-    if (store == NULL)
-       return;
-    if (store->shell.toplevel != NULL)
-       xdg_toplevel_destroy(store->shell.toplevel);
-    if (store->shell.surface != NULL)
-       xdg_surface_destroy(store->shell.surface);
-    if (store->surface != NULL)
-       wl_surface_destroy(store->surface);
-    if (store->keyboard != NULL)
-    {
-       if (wl_keyboard_get_version(store->keyboard) >= 3)
-           wl_keyboard_release(store->keyboard);
-       else
-           wl_keyboard_destroy(store->keyboard);
-    }
-    vim_free(store);
-}
-
-VWL_FUNCS_DUMMY_KEYBOARD_EVENTS()
-
-/*
- * Called when the keyboard focus is on our surface
- */
-    static void
-clip_wl_fs_keyboard_listener_enter(
-    void               *data,
-    struct wl_keyboard *keyboard UNUSED,
-    uint32_t           serial,
-    struct wl_surface  *surface UNUSED,
-    struct wl_array    *keys UNUSED)
-{
-    clip_wl_fs_surface_T *store = data;
-
-    store->got_focus = true;
-
-    if (store->on_focus != NULL)
-       store->on_focus(store->user_data, serial);
-}
-
-
-static struct wl_keyboard_listener  vwl_fs_keyboard_listener = {
-    .enter         = clip_wl_fs_keyboard_listener_enter,
-    .key           = clip_wl_fs_keyboard_listener_key,
-    .keymap        = clip_wl_fs_keyboard_listener_keymap,
-    .leave         = clip_wl_fs_keyboard_listener_leave,
-    .modifiers     = clip_wl_fs_keyboard_listener_modifiers,
-    .repeat_info    = clip_wl_fs_keyboard_listener_repeat_info
-};
-
-/*
- * Create an invisible surface in order to gain focus and call on_focus() with
- * serial that was given.
- */
-    static int
-clip_wl_init_fs_surface(
-       vwl_seat_T              *seat,
-       clip_wl_buffer_store_T  *buffer_store,
-       void                    (*on_focus)(void *, uint32_t),
-       void                    *user_data)
-{
-    clip_wl_fs_surface_T    *store;
-#ifdef ELAPSED_FUNC
-    elapsed_T              start_tv;
-#endif
-
-    if (wayland_ct->gobjects.wl_compositor == NULL
-           || wayland_ct->gobjects.xdg_wm_base == NULL
-           || buffer_store == NULL
-           || seat == NULL)
-       return FAIL;
-
-    store = ALLOC_CLEAR_ONE(clip_wl_fs_surface_T);
-
-    if (store == NULL)
-       return FAIL;
-
-    // Get keyboard
-    store->keyboard = vwl_seat_get_keyboard(seat);
-
-    if (store->keyboard == NULL)
-       goto fail;
-
-    wl_keyboard_add_listener(store->keyboard, &vwl_fs_keyboard_listener, store);
-
-    if (vwl_connection_dispatch(wayland_ct) < 0)
-       goto fail;
-
-    store->surface = wl_compositor_create_surface(
-           wayland_ct->gobjects.wl_compositor);
-    store->shell.surface = xdg_wm_base_get_xdg_surface(
-           wayland_ct->gobjects.xdg_wm_base, store->surface);
-    store->shell.toplevel = xdg_surface_get_toplevel(store->shell.surface);
-
-    xdg_toplevel_set_title(store->shell.toplevel, "Vim clipboard");
-
-    xdg_surface_add_listener(store->shell.surface,
-           &xdg_surface_listener, NULL);
-
-    wl_surface_commit(store->surface);
-
-    store->on_focus = on_focus;
-    store->user_data = user_data;
-    store->got_focus = FALSE;
-
-    if (vwl_connection_roundtrip(wayland_ct) == FAIL)
-       goto fail;
-
-    // We may get the enter event early, if we do then we will set `got_focus`
-    // to TRUE.
-    if (store->got_focus)
-       goto early_exit;
-
-    // Buffer hasn't been released yet, abort. This shouldn't happen but still
-    // check for it.
-    if (!buffer_store->available)
-       goto fail;
-
-    buffer_store->available = false;
-
-    wl_surface_attach(store->surface, buffer_store->buffer, 0, 0);
-    wl_surface_damage(store->surface, 0, 0,
-           buffer_store->width, buffer_store->height);
-    wl_surface_commit(store->surface);
-
-    // Dispatch events until we receive the enter event. Add a max delay of
-    // 'p_wtm' when waiting for it (may be longer depending on how long we poll
-    // when dispatching events)
-#ifdef ELAPSED_FUNC
-    ELAPSED_INIT(start_tv);
-#endif
-
-    while (vwl_connection_dispatch(wayland_ct) >= 0)
-    {
-       if (store->got_focus)
-           break;
-
-#ifdef ELAPSED_FUNC
-       if (ELAPSED_FUNC(start_tv) >= p_wtm)
-           goto fail;
-#endif
-    }
-early_exit:
-    clip_wl_destroy_fs_surface(store);
-    vwl_connection_flush(wayland_ct);
-
-    return OK;
-fail:
-    clip_wl_destroy_fs_surface(store);
-    vwl_connection_flush(wayland_ct);
-
-    return FAIL;
-}
-
-#endif // FEAT_WAYLAND_CLIPBOARD_FS
-
-    static bool
-wl_data_offer_listener_event_offer(
-    void *data UNUSED,
-    vwl_data_offer_T *offer UNUSED,
-    const char *mime_type
-)
-{
-    // Only accept mime type if we support it
-    for (int i = 0; i < (int)ARRAY_LENGTH(supported_mimes); i++)
-       if (STRCMP(mime_type, supported_mimes[i]) == 0)
-           return true;
-    return FALSE;
-}
-
-static const vwl_data_offer_listener_T vwl_data_offer_listener = {
-    .offer = wl_data_offer_listener_event_offer
-};
-
-    static void
-vwl_data_device_listener_event_data_offer(
-       void *data UNUSED,
-       vwl_data_device_T *device UNUSED,
-       vwl_data_offer_T *offer)
-{
-    // Immediately start listening for offer events from the data offer
-    vwl_data_offer_add_listener(offer, &vwl_data_offer_listener, NULL);
-}
-
-    static void
-vwl_data_device_listener_event_selection(
-       void *data UNUSED,
-       vwl_data_device_T *device UNUSED,
-       vwl_data_offer_T *offer,
-       wayland_selection_T selection)
-{
-    clip_wl_selection_T *sel = clip_wl_get_selection(selection);
-
-    // Destroy previous offer if any, it is now invalid
-    vwl_data_offer_destroy(sel->offer);
-
-    // There are two cases when sel->offer is NULL
-    // 1. No one owns the selection
-    // 2. We own the selection (we'll just access the register directly)
-    if (offer == NULL || offer->from_vim)
-    {
-       // Selection event is from us, so we are the source client. Therefore
-       // ignore it. Or the selection is cleared, so set sel->offer to NULL
-       vwl_data_offer_destroy(offer);
-       sel->offer = NULL;
-       return;
-    }
-
-    // Save offer. When we want to request data, then we'll actually call the
-    // receive method.
-    sel->offer = offer;
-}
-
-    static void
-vwl_data_device_listener_event_finished(
-       void *data UNUSED,
-       vwl_data_device_T *device)
-{
-    clip_wl_selection_T *sel;
-    // Device finished, guessing this can happen is when the seat becomes
-    // invalid? If so, let the user call :wlrestore! to reset. There wouldn't be
-    // any point in trying to create another data device for the same seat,
-    // since the seat is in an invalid state.
-    if (device == clip_wl.regular.device)
-    {
-       sel = &clip_wl.regular;
-       clip_wl.regular.device = NULL;
-       clip_init_single(&clip_plus, FALSE);
-    }
-    else if (device == clip_wl.primary.device)
-    {
-       sel = &clip_wl.primary;
-       clip_wl.primary.device = NULL;
-       clip_init_single(&clip_star, FALSE);
-    }
-    else
-       // Shouldn't happen
-       return;
-
-    vim_free(sel->contents);
-    vwl_data_source_destroy(sel->source);
-    vwl_data_offer_destroy(sel->offer);
-    sel->available = FALSE;
-
-    vwl_data_device_destroy(device);
-}
-
-static const vwl_data_device_listener_T vwl_data_device_listener = {
-    .data_offer = vwl_data_device_listener_event_data_offer,
-    .selection = vwl_data_device_listener_event_selection,
-    .finished = vwl_data_device_listener_event_finished
-};
-
-/*
- * Initialize the clipboard for Wayland using the global Wayland connection.
- * Returns OK on success and FAIL on failure.
- */
-    int
-clip_init_wayland(void)
-{
-    int_u supported = WAYLAND_SELECTION_NONE;
-
-    if (wayland_ct == NULL)
-       return FAIL;
-
-    clip_wl.seat = vwl_connection_get_seat(wayland_ct, (char *)p_wse);
-
-    if (clip_wl.seat == NULL)
-       return FAIL;
-
-    clip_wl.regular.manager = vwl_connection_get_data_device_manager(
-           wayland_ct, WAYLAND_SELECTION_REGULAR, &supported);
-
-    if (clip_wl.regular.manager != NULL)
-    {
-       clip_wl.regular.device = vwl_data_device_manager_get_data_device(
-               clip_wl.regular.manager, clip_wl.seat);
-
-       if (clip_wl.regular.device != NULL)
-           clip_wl.regular.available = true;
-       else
-       {
-           vwl_data_device_manager_discard(clip_wl.regular.manager);
-           clip_wl.regular.manager = NULL;
-       }
-    }
-
-    // If we still don't support the primary selection, find one for it
-    // specifically.
-    if (!(supported & WAYLAND_SELECTION_PRIMARY))
-    {
-       clip_wl.primary.manager = vwl_connection_get_data_device_manager(
-               wayland_ct, WAYLAND_SELECTION_PRIMARY, &supported);
-
-       if (clip_wl.primary.manager != NULL)
-       {
-           clip_wl.primary.device = vwl_data_device_manager_get_data_device(
-                   clip_wl.primary.manager, clip_wl.seat);
-
-           if (clip_wl.primary.device != NULL)
-               clip_wl.primary.available = true;
-           else
-           {
-               vwl_data_device_manager_discard(clip_wl.primary.manager);
-               clip_wl.primary.manager = NULL;
-           }
-       }
-    }
-    else if (clip_wl.regular.available)
-    {
-       // The protocol supports both regular and primary selections, just use
-       // one data device manager and one data device.
-       clip_wl.primary.available = true;
-       clip_wl.primary.manager = clip_wl.regular.manager;
-       clip_wl.primary.device = clip_wl.regular.device;
-    }
-
-#ifdef FEAT_WAYLAND_CLIPBOARD_FS
-    if (clip_wl.regular.available
-           && clip_wl.regular.manager->protocol == VWL_DATA_PROTOCOL_CORE
-           && clip_wl_focus_stealing_available())
-       clip_wl.regular.requires_focus = true;
-    if (clip_wl.primary.available
-           && clip_wl.primary.manager->protocol == VWL_DATA_PROTOCOL_PRIMARY
-           && clip_wl_focus_stealing_available())
-       clip_wl.primary.requires_focus = true;
-
-    if (clip_wl.regular.requires_focus || clip_wl.primary.requires_focus)
-    {
-       // Initialize buffer to use for focus stealing
-       clip_wl.fs_buffer = clip_wl_init_buffer_store(1, 1);
-    }
-#endif
-
-    if (!clip_wl.regular.available && !clip_wl.primary.available)
-       return FAIL;
-
-    // Start listening for selection updates
-    if (clip_wl.regular.device != NULL)
-       vwl_data_device_add_listener(clip_wl.regular.device,
-               &vwl_data_device_listener, NULL);
-    // Don't want to listen to the same data device twice
-    if (clip_wl.primary.device != NULL
-           && clip_wl.primary.device != clip_wl.regular.device)
-       vwl_data_device_add_listener(clip_wl.primary.device,
-               &vwl_data_device_listener, NULL);
-
-    if (clipmethod == CLIPMETHOD_WAYLAND)
-    {
-       if (clip_wl.regular.available)
-           clip_init_single(&clip_plus, TRUE);
-       if (clip_wl.primary.available)
-           clip_init_single(&clip_star, TRUE);
-    }
-
-    return OK;
-}
-
-    void
-clip_uninit_wayland(void)
-{
-    clip_wl_selection_T *sel;
-
-#ifdef FEAT_WAYLAND_CLIPBOARD_FS
-    clip_wl_destroy_buffer_store(clip_wl.fs_buffer);
-#endif
-
-    // Don't want to double free
-    if (clip_wl.regular.manager != clip_wl.primary.manager)
-       vwl_data_device_manager_discard(clip_wl.primary.manager);
-    vwl_data_device_manager_discard(clip_wl.regular.manager);
-
-    if (clip_wl.regular.device != clip_wl.primary.device)
-       vwl_data_device_destroy(clip_wl.primary.device);
-    vwl_data_device_destroy(clip_wl.regular.device);
-
-    sel = &clip_wl.regular;
-    while (true)
-    {
-       vim_free(sel->contents);
-       vwl_data_source_destroy(sel->source);
-       vwl_data_offer_destroy(sel->offer);
-       sel->available = false;
-
-       if (sel == &clip_wl.primary)
-           break;
-       sel = &clip_wl.primary;
-    }
-
-    if (clipmethod == CLIPMETHOD_WAYLAND)
-       clip_init(FALSE);
-
-    vim_memset(&clip_wl, 0, sizeof(clip_wl));
-}
-
-    int
-clip_reset_wayland(void)
-{
-    if (clipmethod == CLIPMETHOD_WAYLAND)
-    {
-       if (clip_star.owned)
-           clip_lose_selection(&clip_star);
-       if (clip_plus.owned)
-           clip_lose_selection(&clip_plus);
-    }
-    clip_uninit_wayland();
-
-    if (clip_init_wayland() == FAIL)
-       return FAIL;
-
-    choose_clipmethod();
-    return OK;
-}
+#if defined(FEAT_WAYLAND_CLIPBOARD) || defined(PROTO)
 
 /*
  * Read data from a file descriptor and write it to the given clipboard.
@@ -3041,10 +2350,10 @@ clip_wl_receive_data(Clipboard_T *cbd, const char *mime_type, int fd)
     int                motion_type = MAUTO;
     ssize_t    r = 0;
 #ifndef HAVE_SELECT
-    struct pollfd   pfd;
+    struct pollfd   pfd
 
-    pfd.fd = fd;
-    pfd.events = POLLIN;
+    pfd.fd = fd,
+    pfd.events = POLLIN
 #else
     fd_set rfds;
     struct timeval  tv;
@@ -3059,19 +2368,17 @@ clip_wl_receive_data(Clipboard_T *cbd, const char *mime_type, int fd)
 
     ga_init2(&buf, 1, 4096);
 
-    // 4096 bytes seems reasonable for initial buffer size, memory is cheap
-    // anyways.
+    // 4096 bytes seems reasonable for initial buffer size
     if (ga_grow(&buf, 4096) == FAIL)
        return;
 
     start = buf.ga_data;
 
-#ifndef HAVE_SELECT
-    while (poll(&pfd, 1, p_wtm) > 0)
-#else
-    while (tv.tv_sec = p_wtm / 1000, tv.tv_usec = (p_wtm % 1000) * 1000,
-           select(fd + 1, &rfds, NULL, NULL, &tv) > 0)
-#endif
+    // Only poll before reading when we first start, then we do non-blocking
+    // reads and check for EAGAIN or EINTR to signal to poll again.
+    goto poll_data;
+
+    while (errno = 0, TRUE)
     {
        r = read(fd, start, buf.ga_maxlen - 1 - buf.ga_len);
 
@@ -3080,7 +2387,18 @@ clip_wl_receive_data(Clipboard_T *cbd, const char *mime_type, int fd)
        else if (r < 0)
        {
            if (errno == EAGAIN || errno == EINTR)
-               continue;
+           {
+poll_data:
+#ifndef HAVE_SELECT
+               if (poll(&pfd, 1, p_wtm) > 0)
+                   continue;
+#else
+               tv.tv_sec = 0;
+               tv.tv_usec = p_wtm * 1000;
+               if (select(fd + 1, &rfds, NULL, NULL, &tv) > 0)
+                   continue;
+#endif
+           }
            break;
        }
 
@@ -3154,182 +2472,182 @@ clip_wl_receive_data(Clipboard_T *cbd, const char *mime_type, int fd)
     static void
 clip_wl_request_selection(Clipboard_T *cbd)
 {
-    clip_wl_selection_T *sel = clip_wl_get_selection_from_cbd(cbd);
-    int                        fds[2];
-    int                        mime_types_len;
-    const char         **mime_types;
-    const char         *chosen_mime = NULL;
+    wayland_selection_T            selection;
+    garray_T               *mime_types;
+    int                            len;
+    int                            fd;
+    const char             *chosen_mime = NULL;
 
-    if (!sel->available)
-       goto clear;
-
-#ifdef FEAT_WAYLAND_CLIPBOARD_FS
-    if (sel->requires_focus)
-    {
-       // We don't care about the on_focus callback since once we gain
-       // focus the data offer events will come immediately.
-       if (clip_wl_init_fs_surface(clip_wl.seat,
-                   clip_wl.fs_buffer, NULL, NULL) == FAIL)
-           goto clear;
-    }
+    if (cbd == &clip_star)
+       selection = WAYLAND_SELECTION_PRIMARY;
+    else if (cbd == &clip_plus)
+       selection = WAYLAND_SELECTION_REGULAR;
     else
-#endif
+       return;
+
+    // Get mime types that the source client offers
+    mime_types = wayland_cb_get_mime_types(selection);
+
+    if (mime_types == NULL || mime_types->ga_len == 0)
     {
-       // Dispatch any events that still queued up before checking for a data
-       // offer.
-       if (vwl_connection_roundtrip(wayland_ct) == FAIL)
-           goto clear;
+       // Selection is empty/cleared
+       clip_free_selection(cbd);
+       return;
     }
 
-    if (sel->offer == NULL)
-       goto clear;
-
-    mime_types_len = sel->offer->mime_types.ga_len;
-    mime_types = sel->offer->mime_types.ga_data;
+    len = ARRAY_LENGTH(supported_mimes);
 
-    // Choose mime type to receive from. Mime types with a lower index in the
-    // "supported_mimes" array are prioritized over ones after it.
-    for (int i = 0; i < (int)ARRAY_LENGTH(supported_mimes)
-           && chosen_mime == NULL; i++)
+    // Loop through and pick the one we want to receive from
+    for (int i = 0; i < len && chosen_mime == NULL; i++)
     {
-       for (int k = 0; k < mime_types_len && chosen_mime == NULL; k++)
-           if (STRCMP(mime_types[k], supported_mimes[i]) == 0)
+       for (int k = 0; k < mime_types->ga_len && chosen_mime == NULL; k++)
+       {
+           char *mime_type = ((char**)mime_types->ga_data)[k];
+
+           if (STRCMP(mime_type, supported_mimes[i]) == 0)
                chosen_mime = supported_mimes[i];
+       }
     }
+    if (chosen_mime == NULL)
+       return;
 
-    if (chosen_mime == NULL || pipe(fds) == -1)
-       goto clear;
-
-    vwl_data_offer_receive(sel->offer, chosen_mime, fds[1]);
-
-    close(fds[1]); // Close before we read data so that when the source client
-                  // closes their end we receive an EOF.
+    fd = wayland_cb_receive_data(chosen_mime, selection);
 
-    if (vwl_connection_flush(wayland_ct) >= 0)
-       clip_wl_receive_data(cbd, chosen_mime, fds[0]);
+    if (fd == -1)
+       return;
 
-    close(fds[0]);
+    // Start reading the file descriptor returned
+    clip_wl_receive_data(cbd, chosen_mime, fd);
 
-    return;
-clear:
-    clip_free_selection(cbd);
+    close(fd);
 }
 
+/*
+ * Write data from either the clip or plus register, depending on the given
+ * selection, to the file descriptor that the receiving client will read from.
+ */
     static void
-vwl_data_source_listener_event_send(
-    void *data,
-    vwl_data_source_T *source UNUSED,
-    const char *mime_type,
-    int32_t fd
-)
-{
-    clip_wl_selection_T *sel = data;
-    Clipboard_T                *cbd = clip_wl_get_cbd_from_selection(sel);
-    bool               have_mime = false;
-    int                        motion_type;
-    long_u             length;
-    char_u             *string; // Will be reallocated to a bigger size if
-                                // needed.
-    int                        offset = 0;
-    bool               is_vim, is_vimenc;
-    size_t             total = 0;
+clip_wl_send_data(
+       const char          *mime_type,
+       int                 fd,
+       wayland_selection_T selection)
+{
+    Clipboard_T            *cbd;
+    long_u         length;
+    char_u         *string;
+    ssize_t        written = 0;
+    size_t         total = 0;
+    int                    did_vimenc = TRUE;
+    int                    did_motion_type = TRUE;
+    int                    motion_type;
+    int                    skip_len_check = FALSE;
 #ifndef HAVE_SELECT
-    struct pollfd   pfd;
+    struct pollfd   pfd
 
-    pfd.fd = fd;
-    pfd.events = POLLOUT;
+    pfd.fd = fd,
+    pfd.events = POLLOUT
 #else
     fd_set         wfds;
     struct timeval  tv;
 
     FD_ZERO(&wfds);
     FD_SET(fd, &wfds);
+    tv.tv_sec = 0;
+    tv.tv_usec = p_wtm * 1000;
 #endif
+    if (selection == WAYLAND_SELECTION_REGULAR)
+       cbd = &clip_plus;
+    else if (selection == WAYLAND_SELECTION_PRIMARY)
+       cbd = &clip_star;
+    else
+       return;
 
-    // Check if we actually have mime type
-    for (int i = 0; i < (int)ARRAY_LENGTH(supported_mimes); i++)
-       if (STRCMP(supported_mimes[i], mime_type) == 0)
-       {
-           have_mime = true;
-           break;
-       }
-
-    if (!have_mime)
-       goto exit;
-
-    // First byte sent is motion type for vim specific formats. For the vimenc
-    // format, after the first byte is the encoding type, which is null
-    // terminated.
-
-    is_vimenc = STRCMP(mime_type, VIMENC_ATOM_NAME) == 0;
-    is_vim = STRCMP(mime_type, VIM_ATOM_NAME) == 0;
-
-    if (is_vimenc)
-       offset += 2 + STRLEN(p_enc);
-    else if (is_vim)
-       offset += 1;
+    // Shouldn't happen unless there is a bug.
+    if (!cbd->owned)
+       return;
 
+    // Get the current selection
     clip_get_selection(cbd);
-    motion_type = clip_convert_selection_offset(&string, &length, offset, cbd);
+    motion_type = clip_convert_selection(&string, &length, cbd);
 
     if (motion_type < 0)
        goto exit;
 
-    if (is_vimenc)
+    if (STRCMP(mime_type, VIMENC_ATOM_NAME) == 0)
     {
-       string[0] = (char_u)motion_type;
-       // strcpy copies the NUL terminator too
-       strcpy((char *)string + 1, (char *)p_enc);
+       did_vimenc = FALSE;
+       did_motion_type = FALSE;
     }
-    else if (is_vim)
-       string[0] = (char_u)motion_type;
+    else if (STRCMP(mime_type, VIM_ATOM_NAME) == 0)
+       did_motion_type = FALSE;
 
-
-    while (total < (size_t)length &&
+    while ((total < (size_t)length || skip_len_check) &&
 #ifndef HAVE_SELECT
-           poll(&pfd, 1, p_wtm) > 0)
+          poll(&pfd, 1, p_wtm) > 0)
 #else
-           ((tv.tv_sec = p_wtm / 1000, tv.tv_usec = (p_wtm % 1000) * 1000),
-           select(fd + 1, NULL, &wfds, NULL, &tv) > 0))
+          select(fd + 1, NULL, &wfds, NULL, &tv) > 0)
 #endif
     {
-       ssize_t w = write(fd, string + total, length - total);
+       // First byte sent is motion type for vim specific formats
+       if (!did_motion_type)
+       {
+           if (total == 1)
+           {
+               total = 0;
+               did_motion_type = TRUE;
+               continue;
+           }
+           // We cast to char so that we only send one byte
+           written = write( fd, (char_u*)&motion_type, 1);
+          skip_len_check = TRUE;
+       }
+       else if (!did_vimenc)
+       {
+           // For the vimenc format, after the first byte is the encoding type,
+           // which is null terminated. Make sure we write that before writing
+           // the actual selection.
+          if (total == STRLEN(p_enc) + 1)
+          {
+               total = 0;
+               did_vimenc = TRUE;
+               continue;
+          }
+          // Include null terminator
+          written = write(fd, p_enc + total, STRLEN(p_enc) + 1 - total);
+          skip_len_check = TRUE;
+       }
+       else
+       {
+          // write the actual selection to the fd
+          written = write(fd, string + total, length - total);
+          if (skip_len_check)
+              skip_len_check = FALSE;
+       }
 
-       if (w == -1)
-           break;
-       total += w;
-    }
+       if (written == -1)
+          break;
+       total += written;
 
-    vim_free(string);
+#ifdef HAVE_SELECT
+       tv.tv_sec = 0;
+       tv.tv_usec = p_wtm * 1000;
+#endif
+    }
 exit:
-    close(fd);
-}
-
-    static void
-vwl_data_source_listener_event_cancelled(
-       void *data,
-       vwl_data_source_T *source UNUSED)
-{
-    clip_wl_selection_T *sel = data;
-    Clipboard_T                *cbd = clip_wl_get_cbd_from_selection(sel);
-
-    clip_lose_selection(cbd);
+    vim_free(string);
 }
 
-static const vwl_data_source_listener_T vwl_data_source_listener = {
-    .send = vwl_data_source_listener_event_send,
-    .cancelled = vwl_data_source_listener_event_cancelled
-};
-
+/*
+ * Called if another client gains ownership of the given selection. If so then
+ * lose the selection internally.
+ */
     static void
-clip_wl_do_set_selection(void *data, uint32_t serial)
+clip_wl_selection_cancelled(wayland_selection_T selection)
 {
-    clip_wl_selection_T *sel = data;
-    wayland_selection_T sel_type = clip_wl_get_selection_type(sel);
-
-    vwl_data_device_set_selection(sel->device, sel->source, serial, sel_type);
-
-    sel->own_success = (vwl_connection_roundtrip(wayland_ct) == OK);
+    if (selection == WAYLAND_SELECTION_REGULAR)
+       clip_lose_selection(&clip_plus);
+    else if (selection == WAYLAND_SELECTION_PRIMARY)
+       clip_lose_selection(&clip_star);
 }
 
 /*
@@ -3340,81 +2658,36 @@ clip_wl_do_set_selection(void *data, uint32_t serial)
     static int
 clip_wl_own_selection(Clipboard_T *cbd)
 {
-    clip_wl_selection_T *sel = clip_wl_get_selection_from_cbd(cbd);
-    wayland_selection_T sel_type = clip_wl_get_selection_type(sel);
-
-    if (!sel->available || vwl_connection_roundtrip(wayland_ct) == FAIL)
-       return FAIL;
+    wayland_selection_T selection;
 
-    if (sel->source != NULL)
-    {
-       if (sel_type == WAYLAND_SELECTION_PRIMARY)
-           // We already own the selection, ignore (only do this for primary
-           // selection). We don't re set the selection because then we would
-           // be setting the selection every time the user moves the visual
-           // selection cursor, which is messy and inefficient. Some
-           // applications like Google Chrome do it this way however.
-           return OK;
-       else if (sel_type == WAYLAND_SELECTION_REGULAR)
-       {
-           // Technically we don't need to do this as we already own the
-           // selection, however if a user yanks text a second time, the
-           // text yanked won't appear in their clipboard manager if they are
-           // using one.
-           //
-           // This can be unexpected behaviour for the user so its probably
-           // better to do it this way. Additionally other Wayland applications
-           // seem to set the selection every time.
-           vwl_data_source_destroy(sel->source);
-       }
-       else
-           // Shouldn't happen
-           return FAIL;
-    }
-
-    sel->source = vwl_data_device_manager_create_data_source(sel->manager);
-    vwl_data_source_add_listener(sel->source, &vwl_data_source_listener, sel);
-
-    // Advertise mime types
-    vwl_data_source_offer(sel->source, wayland_vim_special_mime);
-    for (int i = 0; i < (int)ARRAY_LENGTH(supported_mimes); i++)
-       vwl_data_source_offer(sel->source, supported_mimes[i]);
-
-    sel->own_success = false;
-#ifdef FEAT_WAYLAND_CLIPBOARD_FS
-    if (sel->requires_focus)
-    {
-       if (clip_wl_init_fs_surface(clip_wl.seat, clip_wl.fs_buffer,
-                   clip_wl_do_set_selection, sel) == FAIL)
-           goto fail;
-    }
+    if (cbd == &clip_star)
+       selection = WAYLAND_SELECTION_PRIMARY;
+    else if (cbd == &clip_plus)
+       selection = WAYLAND_SELECTION_REGULAR;
     else
-#endif
-       clip_wl_do_set_selection(sel, 0);
-
-    if (!sel->own_success)
-       goto fail;
+       return FAIL;
 
-    return OK;
-fail:
-    vwl_data_source_destroy(sel->source);
-    sel->source = NULL;
-    return FAIL;
+    return wayland_cb_own_selection(
+       clip_wl_send_data,
+       clip_wl_selection_cancelled,
+       supported_mimes,
+       sizeof(supported_mimes)/sizeof(*supported_mimes),
+       selection);
 }
 
 /*
- * Disown the selection that cbd corresponds to.
+ * Disown the selection that cbd corresponds to. Note that the the cancelled
+ * event is not sent when the data source is destroyed.
  */
     static void
 clip_wl_lose_selection(Clipboard_T *cbd)
 {
-    clip_wl_selection_T *sel = clip_wl_get_selection_from_cbd(cbd);
-
-    if (!sel->available)
-       return;
+    if (cbd == &clip_plus)
+       wayland_cb_lose_selection(WAYLAND_SELECTION_REGULAR);
+    else if (cbd == &clip_star)
+       wayland_cb_lose_selection(WAYLAND_SELECTION_PRIMARY);
 
-    vwl_data_source_destroy(sel->source);
-    sel->source = NULL;
+    /* wayland_cb_lose_selection(selection); */
 }
 
 /*
@@ -3428,18 +2701,15 @@ clip_wl_set_selection(Clipboard_T *cbd UNUSED)
 
 #if defined(USE_SYSTEM) && defined(PROTO)
 /*
- * Return true if we own the selection corresponding to cbd or another client
- * does.
+ * Return TRUE if we own the selection corresponding to cbd
  */
-    static bool
+    static int
 clip_wl_owner_exists(Clipboard_T *cbd)
 {
-    clip_wl_selection_T *sel = clip_wl_get_selection_from_cbd(cbd);
-
-    if (vwl_connection_roundtrip(wayland_ct) == FAIL)
-       return false;
-
-    return sel->available && (sel->source != NULL || sel->offer != NULL);
+    if (cbd == &clip_plus)
+       return wayland_cb_selection_is_owned(WAYLAND_SELECTION_REGULAR);
+    else if (cbd == &clip_star)
+       return wayland_cb_selection_is_owned(WAYLAND_SELECTION_PRIMARY);
 }
 #endif
 
@@ -3475,7 +2745,7 @@ get_clipmethod(char_u *str)
 #endif
            {
 #ifdef FEAT_WAYLAND_CLIPBOARD
-               if (clip_wl.regular.available || clip_wl.primary.available)
+               if (wayland_cb_is_ready())
                    method = CLIPMETHOD_WAYLAND;
 #endif
            }
@@ -3573,7 +2843,7 @@ choose_clipmethod(void)
     if (method == CLIPMETHOD_GUI)
        // We only interact with Wayland for the clipboard, we can just deinit
        // everything.
-       wayland_uninit_connection();
+       wayland_uninit_client();
 #endif
 
     // Deinitialize clipboard if there is no way to access clipboard
index 983f186b8d1d7225dcbeb3ad8721bd495a8038b0..e8775d98c38f3c804b2c4e553f2d6f67694e606d 100644 (file)
@@ -12,9 +12,6 @@
 /* Define unless no Wayland support found */
 #undef HAVE_WAYLAND
 
-/* Define if you want focus stealing support with Wayland clipboard  */
-#undef FEAT_WAYLAND_CLIPBOARD_FS
-
 /* Define when terminfo support found */
 #undef TERMINFO
 
index c00c063eeaeae40ba67c269562ff1fcece403f24..983c5822471f5088ab6c7cd8929082d1425d3876 100644 (file)
@@ -2464,25 +2464,11 @@ AC_ARG_WITH(wayland,
                         AC_MSG_RESULT([yes])]))
 
 if test "$with_wayland" = yes; then
-  cppflags_save=$CPPFLAGS
-  cflags_save=$CFLAGS
-
+cppflags_save=$CPPFLAGS
+cflags_save=$CFLAGS
   AC_MSG_CHECKING(for wayland)
   if "$PKG_CONFIG" --exists 'wayland-client'; then
     AC_MSG_RESULT([yes])
-
-    AC_MSG_CHECKING(--enable-wayland-focus-steal argument)
-    AC_ARG_ENABLE(wayland-focus-steal,
-                 [AS_HELP_STRING([--enable-wayland-focus-steal],
-                          [Include focus stealing support for Wayland clipboard.])],
-                 [enable_wayland_fs=$enableval],
-                 enable_wayland_fs="yes")
-
-    AS_IF([test "$enable_wayland_fs" = "yes"],
-          [AC_MSG_RESULT([yes])
-          AC_DEFINE(FEAT_WAYLAND_CLIPBOARD_FS)],
-          [AC_MSG_RESULT([no])])
-
     AC_DEFINE(HAVE_WAYLAND)
     WAYLAND_CPPFLAGS=`$PKG_CONFIG --cflags-only-I wayland-client`
     WAYLAND_CFLAGS=`$PKG_CONFIG --cflags-only-other wayland-client`
@@ -2492,20 +2478,15 @@ if test "$with_wayland" = yes; then
     WAYLAND_SRC=" \
       auto/wayland/wlr-data-control-unstable-v1.c \
       auto/wayland/ext-data-control-v1.c \
+      auto/wayland/xdg-shell.c \
+      auto/wayland/primary-selection-unstable-v1.c \
       wayland.c"
     WAYLAND_OBJ=" \
       objects/wlr-data-control-unstable-v1.o \
       objects/ext-data-control-v1.o \
+      objects/xdg-shell.o \
+      objects/primary-selection-unstable-v1.o \
       objects/wayland.o"
-
-    AS_IF([test "$enable_wayland_fs" = "yes"],
-          [WAYLAND_SRC+=" \
-            auto/wayland/xdg-shell.c \
-            auto/wayland/primary-selection-unstable-v1.c"
-           WAYLAND_OBJ+=" \
-            objects/xdg-shell.o \
-            objects/primary-selection-unstable-v1.o"])
-
     AC_SUBST(WAYLAND_CPPFLAGS)
     AC_SUBST(WAYLAND_CFLAGS)
     AC_SUBST(WAYLAND_LIBS)
index c788a2bb65f79a08d3bd63b8b62d16cb6a6de38f..3a0e9a77af0079170e91d026a44e26212e58f0dc 100644 (file)
@@ -7615,13 +7615,6 @@ f_has(typval_T *argvars, typval_T *rettv)
                1
 #else
                0
-#endif
-               },
-       {"wayland_focus_steal",
-#ifdef FEAT_WAYLAND_CLIPBOARD_FS
-               1
-#else
-               0
 #endif
                },
        {"wildignore", 1},
index d7db96cd57810082b6360912afa88a7f2a350e48..826adbe2bdcd43b25efb6cf8fb4a196982fa5a82 100644 (file)
 # endif
 #endif
 
-/*
- * +wayland_focus_steal            Focus stealing support for Wayland clipboard
- */
-#if !defined(FEAT_WAYLAND_CLIPBOARD) && defined(FEAT_WAYLAND_CLIPBOARD_FS)
-# undef FEAT_WAYLAND_CLIPBOARD_FS
-#endif
-
 /*
  * +dnd                Drag'n'drop support.  Always used for the GTK+ GUI.
  */
index 022176d904699451c59a7688d8f2e943fbad55d8..8d031028eae201da23545d0b507d1233cd2c803b 100644 (file)
@@ -2080,18 +2080,15 @@ EXTERN clipmethod_T clipmethod INIT(= CLIPMETHOD_NONE);
 
 #ifdef FEAT_WAYLAND
 
-// Wayland display name for global connection (ex. wayland-0). Can be NULL
-EXTERN char *wayland_display_name INIT(= NULL);
-
-// Special mime type used to identify selection events that came from us setting
-// the selection. Is in format of "application/x-vim-instance-<pid>" where <pid>
-// is the PID of the Vim process. Set in main.c
-EXTERN char wayland_vim_special_mime[
-    sizeof("application/x-vim-instance-") + NUMBUFLEN - 1]; // Includes NUL
-
 // Don't connect to Wayland compositor if TRUE
 EXTERN int wayland_no_connect INIT(= FALSE);
 
+// Wayland display name (ex. wayland-0). Can be NULL
+EXTERN char *wayland_display_name INIT(= NULL);
+
+// Wayland display file descriptor; set by wayland_init_client()
+EXTERN int wayland_display_fd;
+
 #endif
 
 #if defined(FEAT_CLIENTSERVER) && !defined(MSWIN)
index eddfb9e3dd85c5002e6d43cdb5e37d80a9ee4eb8..942a7e5f8ffb58de66dac094324b76d300711f8a 100644 (file)
@@ -682,18 +682,15 @@ vim_main2(void)
     if (!gui.in_use)
 # endif
     {
-       sprintf(wayland_vim_special_mime, "application/x-vim-instance-%ld",
-               mch_get_pid());
-
-       if (wayland_init_connection(wayland_display_name) == OK)
+       if (wayland_init_client(wayland_display_name) == OK)
        {
            TIME_MSG("connected to Wayland display");
 
 # ifdef FEAT_WAYLAND_CLIPBOARD
-           if (clip_init_wayland() == OK)
+           if (wayland_cb_init((char*)p_wse) == OK)
                TIME_MSG("setup Wayland clipboard");
-# endif
        }
+# endif
     }
 #endif
 
index 99083d0d3a6e345819762507264fb279a8ca0a54..f258836ec4aee98a9c4d35803290a9d2b78070e5 100644 (file)
@@ -4770,7 +4770,7 @@ did_set_winwidth(optset_T *args UNUSED)
     char *
 did_set_wlsteal(optset_T *args UNUSED)
 {
-    clip_reset_wayland();
+    wayland_cb_reload();
 
     return NULL;
 }
index 2963c2a05e80982b999479b77c4f5e9a02ee5e5e..ce75d6862e64ecbd8d91b16c25fe061a069ad929 100644 (file)
@@ -1144,7 +1144,7 @@ EXTERN long       p_wmw;          // 'winminwidth'
 EXTERN long    p_wiw;          // 'winwidth'
 #ifdef FEAT_WAYLAND
 EXTERN char_u  *p_wse;         // 'wlseat'
-# ifdef FEAT_WAYLAND_CLIPBOARD_FS
+# ifdef FEAT_WAYLAND_CLIPBOARD
 EXTERN int     p_wst;          // 'wlsteal'
 # endif
 EXTERN long     p_wtm;         // 'wltimeoutlen'
index fd2d5330fb3ede1a100021fdfba1bfa50c0470b4..2de320b9c72abe91e2ca7c2664df33f3cd94e5f8 100644 (file)
@@ -3009,7 +3009,7 @@ static struct vimoption options[] =
 #endif
                            SCTX_INIT},
     {"wlsteal",            "wst",  P_BOOL|P_VI_DEF,
-#ifdef FEAT_WAYLAND_CLIPBOARD_FS
+#ifdef FEAT_WAYLAND_CLIPBOARD
                            (char_u *)&p_wst, PV_NONE, did_set_wlsteal, NULL,
                            {(char_u *)FALSE, (char_u *)0L}
 #else
index 55cde57166265a6b2b898890d2fcddabc3a5fce4..628842ef1a6ddd4e71e1f8fadc7bbed802a3c326 100644 (file)
@@ -3689,7 +3689,7 @@ did_set_wlseat(optset_T *args UNUSED)
 #ifdef FEAT_WAYLAND_CLIPBOARD
     // If there isn't any seat named 'wlseat', then let the Wayland clipboard be
     // unavailable. Ignore errors returned.
-    clip_reset_wayland();
+    wayland_cb_reload();
 #endif
 
     return NULL;
index 73032fb547d717e8c2347d7a98a2dea57bb1275c..1094899390d19a41a6b94a6892363c2eb66df7a3 100644 (file)
@@ -5731,7 +5731,7 @@ mch_call_shell_fork(
 #ifdef FEAT_WAYLAND
                    // Handle Wayland events such as sending data as the source
                    // client.
-                   wayland_update();
+                   wayland_client_update();
 #endif
                }
 finished:
@@ -5805,7 +5805,7 @@ finished:
 #ifdef FEAT_WAYLAND
                    // Handle Wayland events such as sending data as the source
                    // client.
-                   wayland_update();
+                   wayland_client_update();
 #endif
 
                    // Wait for 1 to 10 msec. 1 is faster but gives the child
@@ -6657,9 +6657,6 @@ RealWaitForChar(int fd, long msec, int *check_for_gpm UNUSED, int *interrupted)
        int             mzquantum_used = FALSE;
 # endif
 #endif
-#ifdef FEAT_WAYLAND
-       int             wayland_fd = -1;
-#endif
 #ifndef HAVE_SELECT
                        // each channel may use in, out and err
        struct pollfd   fds[7 + 3 * MAX_OPEN_CHANNELS];
@@ -6703,11 +6700,11 @@ RealWaitForChar(int fd, long msec, int *check_for_gpm UNUSED, int *interrupted)
        }
 # endif
 
-# ifdef FEAT_WAYLAND
-       if ((wayland_fd = wayland_prepare_read()) >= 0)
+# ifdef FEAT_WAYLAND_CLIPBOARD
+       if (wayland_may_restore_connection())
        {
            wayland_idx = nfd;
-           fds[nfd].fd = wayland_fd;
+           fds[nfd].fd = wayland_display_fd;
            fds[nfd].events = POLLIN;
            nfd++;
        }
@@ -6771,9 +6768,13 @@ RealWaitForChar(int fd, long msec, int *check_for_gpm UNUSED, int *interrupted)
        }
 # endif
 
-# ifdef FEAT_WAYLAND
-       if (wayland_idx >= 0)
-           wayland_poll_check(fds[wayland_idx].revents);
+# ifdef FEAT_WAYLAND_CLIPBOARD
+       // Technically we should first call wl_display_prepare_read() before
+       // polling the fd, then read and dispatch after we poll. However that is
+       // only needed for multi threaded environments to prevent deadlocks so
+       // we are fine.
+       if (fds[wayland_idx].revents & POLLIN)
+           wayland_client_update();
 # endif
 
 # ifdef FEAT_XCLIPBOARD
@@ -6866,13 +6867,14 @@ select_eintr:
        }
 # endif
 
-# ifdef FEAT_WAYLAND
-       if ((wayland_fd = wayland_prepare_read()) >= 0)
+# ifdef FEAT_WAYLAND_CLIPBOARD
+
+       if (wayland_may_restore_connection())
        {
-           FD_SET(wayland_fd, &rfds);
+           FD_SET(wayland_display_fd, &rfds);
 
-           if (maxfd < wayland_fd)
-               maxfd = wayland_fd;
+           if (maxfd < wayland_display_fd)
+               maxfd = wayland_display_fd;
        }
 # endif
 
@@ -6972,9 +6974,13 @@ select_eintr:
            socket_server_uninit();
 # endif
 
-# ifdef FEAT_WAYLAND
-       if (wayland_fd != -1)
-           wayland_select_check(ret > 0 && FD_ISSET(wayland_fd, &rfds));
+# ifdef FEAT_WAYLAND_CLIPBOARD
+       // Technically we should first call wl_display_prepare_read() before
+       // polling the fd, then read and dispatch after we poll. However that is
+       // only needed for multi threaded environments to prevent deadlocks so
+       // we are fine.
+       if (ret > 0 && FD_ISSET(wayland_display_fd, &rfds))
+           wayland_client_update();
 # endif
 
 # ifdef FEAT_XCLIPBOARD
index 5727aa8b77e39004c7ccdd5f72510bfbb58f0b60..9ccfea90a63d24a1c8e1329739de038a6e6a2004 100644 (file)
@@ -35,9 +35,6 @@ int clip_convert_selection(char_u **str, long_u *len, Clipboard_T *cbd);
 int may_get_selection(int regname);
 void may_set_selection(void);
 void adjust_clip_reg(int *rp);
-int clip_init_wayland(void);
-void clip_uninit_wayland(void);
-int clip_reset_wayland(void);
 char *choose_clipmethod(void);
 void ex_clipreset(exarg_T *eap);
 /* vim: set ft=c : */
index 113bf82e478868a0ba3f71cad3b671adf0c04de8..990bd045881f02ee16fbe20f32ab581d7bf2010b 100644 (file)
@@ -1,27 +1,17 @@
 /* wayland.c */
-int vwl_connection_flush(vwl_connection_T *self);
-int vwl_connection_dispatch(vwl_connection_T *self);
-int vwl_connection_roundtrip(vwl_connection_T *self);
-int wayland_init_connection(const char *display);
-void wayland_uninit_connection(void);
-int wayland_prepare_read(void);
-int wayland_update(void);
-void wayland_poll_check(int revents);
-void wayland_select_check(bool is_set);
+int wayland_init_client(const char *display);
+void wayland_uninit_client(void);
+int wayland_client_is_connected(int quiet);
+int wayland_client_update(void);
+int wayland_cb_init(const char *seat);
+void wayland_cb_uninit(void);
+garray_T * wayland_cb_get_mime_types(wayland_selection_T selection);
+int wayland_cb_receive_data(const char *mime_type, wayland_selection_T selection);
+int wayland_cb_own_selection( wayland_cb_send_data_func_T send_cb, wayland_cb_selection_cancelled_func_T cancelled_cb, const char **mime_types, int len, wayland_selection_T selection);
+void wayland_cb_lose_selection(wayland_selection_T selection);
+int wayland_cb_selection_is_owned(wayland_selection_T selection);
+int wayland_cb_is_ready(void);
+int wayland_cb_reload(void);
+int wayland_may_restore_connection(void);
 void ex_wlrestore(exarg_T *eap);
-vwl_seat_T *vwl_connection_get_seat(vwl_connection_T *ct, const char *label);
-struct wl_keyboard *vwl_seat_get_keyboard(vwl_seat_T *seat);
-vwl_data_device_manager_T *vwl_connection_get_data_device_manager(vwl_connection_T *self,wayland_selection_T req_sel, unsigned int *supported);
-vwl_data_device_T *vwl_data_device_manager_get_data_device(vwl_data_device_manager_T *self,vwl_seat_T *seat);
-vwl_data_source_T *vwl_data_device_manager_create_data_source(vwl_data_device_manager_T *self);
-void vwl_data_device_destroy(vwl_data_device_T *self);
-void vwl_data_source_destroy(vwl_data_source_T *self);
-void vwl_data_offer_destroy(vwl_data_offer_T *self);
-void vwl_data_device_manager_discard(vwl_data_device_manager_T *self);
-void vwl_data_device_add_listener(vwl_data_device_T *self,const vwl_data_device_listener_T *listener, void *data);
-void vwl_data_source_add_listener(vwl_data_source_T *self,const vwl_data_source_listener_T *listener, void *data);
-void vwl_data_offer_add_listener(vwl_data_offer_T *self,const vwl_data_offer_listener_T *listener, void *data);
-void vwl_data_device_set_selection(vwl_data_device_T *self, vwl_data_source_T *source, uint32_t serial, wayland_selection_T selection);
-void vwl_data_source_offer(vwl_data_source_T *self, const char *mime_type);
-void vwl_data_offer_receive(vwl_data_offer_T *self, const char *mime_type, int32_t fd);
 /* vim: set ft=c : */
index 9fe15d978b8beed7d14b9b292aa0105befb750d7..0968b3c9a9e6a15365fc42d07c9973859fbc3dbe 100644 (file)
@@ -5237,26 +5237,20 @@ struct cellsize {
 
 #ifdef FEAT_WAYLAND
 
-typedef struct vwl_connection_S vwl_connection_T;
-typedef struct vwl_seat_S vwl_seat_T;
-
-# ifdef FEAT_WAYLAND_CLIPBOARD
-
-typedef struct vwl_data_offer_S vwl_data_offer_T;
-typedef struct vwl_data_source_S vwl_data_source_T;
-typedef struct vwl_data_device_S vwl_data_device_T;
-typedef struct vwl_data_device_manager_S vwl_data_device_manager_T;
-
-typedef struct vwl_data_device_listener_S vwl_data_device_listener_T;
-typedef struct vwl_data_source_listener_S vwl_data_source_listener_T;
-typedef struct vwl_data_offer_listener_S vwl_data_offer_listener_T;
-
 // Wayland selections
 typedef enum {
-    WAYLAND_SELECTION_NONE     0,
-    WAYLAND_SELECTION_REGULAR  = 1 << 0,
-    WAYLAND_SELECTION_PRIMARY  = 1 << 1,
+    WAYLAND_SELECTION_NONE         = 0x0,
+    WAYLAND_SELECTION_REGULAR      = 0x1,
+    WAYLAND_SELECTION_PRIMARY      = 0x2,
 } wayland_selection_T;
 
-# endif
-#endif
+// Callback when another client wants us to send data to them
+typedef void (*wayland_cb_send_data_func_T)(
+       const char *mime_type,
+       int fd,
+       wayland_selection_T type);
+
+// Callback when the selection is lost (data source object overwritten)
+typedef void (*wayland_cb_selection_cancelled_func_T)(wayland_selection_T type);
+
+#endif // FEAT_WAYLAND
index 32b04e630bf13f4b8dd7aef6a9db3871119df8ad..3308b2a9363704a0b2b6881abe94cb97e8c02aa1 100644 (file)
@@ -21,17 +21,12 @@ let s:old_wayland_display = $WAYLAND_DISPLAY
 " every test function
 func s:PreTest()
   let $WAYLAND_DISPLAY=s:global_wayland_display
-  " Always reconnect so we have a clean state each time and clear both
-  " selections.
-  call system('wl-copy -c')
-  call system('wl-copy -p -c')
-  exe 'wlrestore! ' .. $WAYLAND_DISPLAY
+  exe 'wlrestore ' .. $WAYLAND_DISPLAY
 
   set cpm=wayland
 endfunc
 
 func s:SetupFocusStealing()
-  CheckFeature wayland_focus_steal
   if !executable('wayland-info')
     throw "Skipped: wayland-info program not available"
   endif
@@ -40,7 +35,7 @@ func s:SetupFocusStealing()
   " seat, so we must use the user's existing Wayland session if they are in one.
   let $WAYLAND_DISPLAY = s:old_wayland_display
 
-  exe 'wlrestore! ' .. $WAYLAND_DISPLAY
+  exe 'wlrestore ' .. $WAYLAND_DISPLAY
 
   " Check if we have keyboard capability for seat
   if system("wayland-info -i wl_seat | grep capabilities") !~? "keyboard"
@@ -55,14 +50,17 @@ func s:UnsetupFocusStealing()
   unlet $VIM_WAYLAND_FORCE_FS
 endfunc
 
-func s:CheckClientserver()
-  CheckFeature clientserver
-
-  if has('socketserver') && !has('x11')
-    if v:servername == ""
-      call remote_startserver('VIMSOCKETSERVER')
+" Need X connection for tests that use client server communication
+func s:CheckXConnection()
+  CheckFeature x11
+  try
+    call remote_send('xxx', '')
+  catch
+    if v:exception =~ 'E240:'
+      thrclientserverow 'Skipped: no connection to the X server'
     endif
-  endif
+    " ignore other errors
+  endtry
 endfunc
 
 func s:EndRemoteVim(name, job)
@@ -79,7 +77,11 @@ endfunc
 
 func Test_wayland_startup()
   call s:PreTest()
-  call s:CheckClientserver()
+  call s:CheckXConnection()
+
+  if v:servername == ""
+    call remote_startserver('VIMSOCKETSERVER')
+  endif
 
   let l:name = 'WLVIMTEST'
   let l:cmd = GetVimCommand() .. ' --servername ' .. l:name
@@ -167,12 +169,18 @@ func Test_wayland_connection_lost()
   call EndWaylandCompositor(l:wayland_display)
 
   call assert_equal('', getreg('+'))
+  call assert_fails('put +', 'E353:')
+  call assert_fails('yank +', 'E1548:')
 endfunc
 
 " Basic paste tests
 func Test_wayland_paste()
   call s:PreTest()
 
+  " Prevent 'Register changed while using it' error, guessing this works because
+  " it makes Vim lose the selection?
+  wlrestore!
+
   " Regular selection
   new
 
@@ -211,6 +219,8 @@ endfunc
 func Test_wayland_yank()
   call s:PreTest()
 
+  wlrestore!
+
   new
 
   call setline(1, 'testing')
@@ -255,8 +265,7 @@ func Test_wayland_mime_types_correct()
         \ 'text/plain',
         \ 'UTF8_STRING',
         \ 'STRING',
-        \ 'TEXT',
-        \ 'application/x-vim-instance-' .. getpid()
+        \ 'TEXT'
         \ ]
 
   call setreg('+', 'text', 'c')
@@ -351,7 +360,7 @@ endfunc
 " Test if autoselect option in 'clipboard' works properly for Wayland
 func Test_wayland_autoselect_works()
   call s:PreTest()
-  call s:CheckClientserver()
+  call s:CheckXConnection()
 
   let l:lines =<< trim END
   set cpm=wayland
@@ -367,6 +376,10 @@ func Test_wayland_autoselect_works()
 
   call writefile(l:lines, 'Wltester', 'D')
 
+  if v:servername == ""
+    call remote_startserver('VIMSOCKETSERVER')
+  endif
+
   let l:name = 'WLVIMTEST'
   let l:cmd = GetVimCommand() .. ' -S Wltester --servername ' .. l:name
   let l:job = job_start(cmd, {'stoponexit': 'kill', 'out_io': 'null'})
@@ -395,13 +408,24 @@ func Test_wayland_autoselect_works()
 
   eval remote_send(l:name, "\<Esc>:qa!\<CR>")
 
-  call s:EndRemoteVim(l:name, l:job)
+  try
+    call WaitForAssert({-> assert_equal("dead", job_status(l:job))})
+  finally
+    if job_status(l:job) != 'dead'
+      call assert_report('Vim instance did not exit')
+      call job_stop(l:job, 'kill')
+    endif
+  endtry
 endfunc
 
 " Check if the -Y flag works properly
 func Test_no_wayland_connect_cmd_flag()
   call s:PreTest()
-  call s:CheckClientserver()
+  call s:CheckXConnection()
+
+  if v:servername == ""
+    call remote_startserver('VIMSOCKETSERVER')
+  endif
 
   let l:name = 'WLFLAGVIMTEST'
   let l:cmd = GetVimCommand() .. ' -Y --servername ' .. l:name
@@ -425,13 +449,25 @@ func Test_no_wayland_connect_cmd_flag()
   call WaitForAssert({-> assert_equal('',
         \ remote_expr(l:name, 'v:wayland_display'))})
 
-  call s:EndRemoteVim(l:name, l:job)
+  eval remote_send(l:name, "\<Esc>:qa!\<CR>")
+  try
+    call WaitForAssert({-> assert_equal("dead", job_status(l:job))})
+  finally
+    if job_status(l:job) != 'dead'
+      call assert_report('Vim instance did not exit')
+      call job_stop(l:job, 'kill')
+    endif
+  endtry
 endfunc
 
 " Test behaviour when we do something like suspend Vim
 func Test_wayland_become_inactive()
   call s:PreTest()
-  call s:CheckClientserver()
+  call s:CheckXConnection()
+
+  if v:servername == ""
+    call remote_startserver('VIMSOCKETSERVER')
+  endif
 
   let l:name = 'WLLOSEVIMTEST'
   let l:cmd = GetVimCommand() .. ' --servername ' .. l:name
@@ -453,7 +489,15 @@ func Test_wayland_become_inactive()
   call WaitForAssert({-> assert_equal("Nothing is copied\n",
         \ system('wl-paste -n'))})
 
-  call s:EndRemoteVim(l:name, l:job)
+  eval remote_send(l:name, "\<Esc>:qa!\<CR>")
+  try
+    call WaitForAssert({-> assert_equal("dead", job_status(l:job))})
+  finally
+    if job_status(l:job) != 'dead'
+      call assert_report('Vim instance did not exit')
+      call job_stop(l:job, 'kill')
+    endif
+  endtry
 endfunc
 
 " Test wlseat option
@@ -484,7 +528,6 @@ endfunc
 
 " Test focus stealing
 func Test_wayland_focus_steal()
-  CheckFeature wayland_focus_steal
   call s:PreTest()
   call s:SetupFocusStealing()
 
@@ -510,13 +553,17 @@ endfunc
 " Test when environment is not suitable for Wayland
 func Test_wayland_bad_environment()
   call s:PreTest()
-  call s:CheckClientserver()
+  call s:CheckXConnection()
 
   unlet $WAYLAND_DISPLAY
 
   let l:old = $XDG_RUNTIME_DIR
   unlet $XDG_RUNTIME_DIR
 
+  if v:servername == ""
+    call remote_startserver('VIMSOCKETSERVER')
+  endif
+
   let l:name = 'WLVIMTEST'
   let l:cmd = GetVimCommand() .. ' --servername ' .. l:name
   let l:job = job_start(cmd, {
@@ -530,7 +577,15 @@ func Test_wayland_bad_environment()
   call WaitForAssert({-> assert_equal('',
         \ remote_expr(l:name, 'v:wayland_display'))})
 
-  call s:EndRemoteVim(l:name, l:job)
+  eval remote_send(l:name, "\<Esc>:qa!\<CR>")
+  try
+    call WaitForAssert({-> assert_equal("dead", job_status(l:job))})
+  finally
+    if job_status(l:job) != 'dead'
+      call assert_report('Vim instance did not exit')
+      call job_stop(l:job, 'kill')
+    endif
+  endtry
 
   let $XDG_RUNTIME_DIR = l:old
 endfunc
@@ -557,11 +612,7 @@ func Test_wayland_lost_selection()
   call assert_equal('regular', getreg('+'))
   call assert_equal('primary', getreg('*'))
 
-endfunc
-
-" Same as above but for the focus stealing method
-func Test_wayland_lost_selection_focus_steal()
-  call s:PreTest()
+  " Test focus stealing
   call s:SetupFocusStealing()
 
   call setreg('+', 'regular')
@@ -585,14 +636,14 @@ func Test_wayland_lost_selection_focus_steal()
   call s:UnsetupFocusStealing()
 endfunc
 
-" Test when there are no supported mime types for the selection
+" Test when there are no supported mime types for the selecftion
 func Test_wayland_no_mime_types_supported()
   call s:PreTest()
 
-  call system('wl-copy tester')
-  call assert_equal('tester', getreg('+'))
+  wlrestore!
 
   call system('wl-copy -t image/png testing')
+
   call assert_equal('', getreg('+'))
   call assert_fails('put +', 'E353:')
 endfunc
@@ -601,17 +652,28 @@ endfunc
 func Test_wayland_handle_large_data()
   call s:PreTest()
 
-  let l:file = tempname()
-  let l:contents = repeat('c', 1000000) " ~ 1 MB
+  call writefile([''], 'data_file', 'D')
+  call writefile([''], 'data_file_cmp', 'D')
+  call system('yes c | head -5000000 > data_file') " ~ 10 MB
+  call system('wl-copy -t TEXT < data_file')
+
+  edit data_file_cmp
 
-  call writefile([l:contents], l:file, 'b')
-  call system('cat ' .. l:file .. ' | wl-copy -t TEXT')
+  put! +
 
-  call assert_equal(l:contents, getreg('+'))
+  write
 
-  call setreg('+', l:contents, 'c')
+  call system('truncate -s -1 data_file_cmp') " Remove newline at the end
+  call system('cmp --silent data_file data_file_cmp')
+  call assert_equal(0, v:shell_error)
 
-  call assert_equal(l:contents, system('wl-paste -n -t TEXT'))
+  call feedkeys('gg0v$G"+yy', 'x')
+  call system('wl-paste -n -t TEXT > data_file')
+
+  call system('cmp --silent data_file data_file_cmp')
+  call assert_equal(0, v:shell_error)
+
+  bw!
 endfunc
 
 " vim: shiftwidth=2 sts=2 expandtab
index 28133a07f2a70cd4af88b7dc48323cf09f57ac13..ffa7bf10e416bf1b9897f4a2df7d4fe6ff967ce6 100644 (file)
@@ -658,11 +658,6 @@ static char *(features[]) =
        "+wayland_clipboard",
 #else
        "-wayland_clipboard",
-#endif
-#ifdef FEAT_WAYLAND_CLIPBOARD_FS
-       "+wayland_focus_steal",
-#else
-       "-wayland_focus_steal",
 #endif
        "+wildignore",
        "+wildmenu",
@@ -729,6 +724,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1726,
 /**/
     1725,
 /**/
index 802f7de6d5237d1c9df16f238f6ed5939139ce80..e1eee19940a3a3e931c2bdd819fc7939e0bcdfbc 100644 (file)
  */
 
 /*
- * wayland.c: Stuff related to Wayland. Functions that prefixed with "vwl_"
- *           handle/provide abstractions and building blocks to create more
- *           complex things. The "wayland_" functions handle the global
- *           Wayland connection.
- *
- *           At the end of this file, there are a bunch of macro definitions
- *           that abstract away all the different protocols for the clipboard
- *           we need to support under one single interface. This is then used
- *           by clipboard.c to implement the Wayland clipboard functionality.
- *
- *           The clipboard functionality monitors the Wayland display at all
- *           times, and saves new selections/offers as events come in. When we
- *           want retrieve the selection, the currently saved data offer is
- *           used from the respective data device.
- *
- *           The focus stealing code is implemented in clipboard.c, and is
- *           based off of wl-clipboard's implementation. The idea using of
- *           extensive macros to reduce boilerplate code also comes from
- *           wl-clipboard as well. The project page for wl-clipboard can be
- *           found here: https://github.com/bugaevc/wl-clipboard
+ * wayland.c: Stuff related to Wayland
  */
 
 #include "vim.h"
 
 #ifdef FEAT_WAYLAND
 
-#include "wayland.h"
+#include <wayland-client.h>
+
+#ifdef FEAT_WAYLAND_CLIPBOARD
+# include "auto/wayland/wlr-data-control-unstable-v1.h"
+# include "auto/wayland/ext-data-control-v1.h"
+# include "auto/wayland/xdg-shell.h"
+# include "auto/wayland/primary-selection-unstable-v1.h"
+#endif
+
+// Struct that represents a seat. (Should be accessed via
+// vwl_get_seat()).
+typedef struct {
+    struct wl_seat  *proxy;
+    char           *label;         // Name of seat as text (e.g. seat0,
+                                   // seat1...).
+    uint32_t       capabilities;   // Bitmask of the capabilites of the seat
+                                   // (pointer, keyboard, touch).
+} vwl_seat_T;
+
+// Global objects
+typedef struct {
+#ifdef FEAT_WAYLAND_CLIPBOARD
+    // Data control protocols
+    struct zwlr_data_control_manager_v1 *zwlr_data_control_manager_v1;
+    struct ext_data_control_manager_v1 *ext_data_control_manager_v1;
+    struct wl_data_device_manager      *wl_data_device_manager;
+    struct wl_shm                      *wl_shm;
+    struct wl_compositor               *wl_compositor;
+    struct xdg_wm_base                 *xdg_wm_base;
+    struct zwp_primary_selection_device_manager_v1
+       *zwp_primary_selection_device_manager_v1;
+#endif
+} vwl_global_objects_T;
+
+// Struct wrapper for Wayland display and registry
+typedef struct {
+    struct wl_display  *proxy;
+    int                        fd;     // File descriptor for display
+
+    struct {
+       struct wl_registry *proxy;
+    } registry;
+} vwl_display_T;
+
+#ifdef FEAT_WAYLAND_CLIPBOARD
+
+typedef struct {
+    struct wl_shm_pool *pool;
+    int                        fd;
+
+    struct wl_buffer   *buffer;
+    int                        available;
+
+    int                        width;
+    int                        height;
+    int                        stride;
+    int                        size;
+} vwl_buffer_store_T;
+
+typedef struct {
+    void                   *user_data;
+    void                   (*on_focus)(void *data, uint32_t serial);
+
+    struct wl_surface      *surface;
+    struct wl_keyboard     *keyboard;
+
+    struct {
+       struct xdg_surface  *surface;
+       struct xdg_toplevel *toplevel;
+    } shell;
+
+    int got_focus;
+} vwl_fs_surface_T; // fs = focus steal
+
+// Wayland protocols for accessing the selection
+typedef enum {
+    VWL_DATA_PROTOCOL_NONE,
+    VWL_DATA_PROTOCOL_EXT,
+    VWL_DATA_PROTOCOL_WLR,
+    VWL_DATA_PROTOCOL_CORE,
+    VWL_DATA_PROTOCOL_PRIMARY
+} vwl_data_protocol_T;
+
+// DATA RELATED OBJECT WRAPPERS
+// These wrap around a proxy and act as a generic container.
+// The `data` member is used to pass other needed stuff around such as a
+// vwl_clipboard_selection_T pointer.
+
+typedef struct {
+    void               *proxy;
+    void               *data; // Is not set when a new offer is created on a
+                              // data_offer event. Only set when listening to a
+                              // data offer.
+    vwl_data_protocol_T protocol;
+} vwl_data_offer_T;
+
+typedef struct {
+    void               *proxy;
+    void               *data;
+    vwl_data_protocol_T protocol;
+} vwl_data_source_T;
+
+typedef struct {
+    void               *proxy;
+    void               *data;
+    vwl_data_protocol_T protocol;
+} vwl_data_device_T;
+
+typedef struct {
+    void               *proxy;
+    vwl_data_protocol_T protocol;
+} vwl_data_device_manager_T;
+
+// LISTENER WRAPPERS
+
+typedef struct {
+    void (*data_offer)(vwl_data_device_T *device, vwl_data_offer_T *offer);
+
+    // If the protocol that the data device uses doesn't support a specific
+    // selection, then this callback will never be called with that selection.
+    void (*selection)(
+           vwl_data_device_T *device,
+           vwl_data_offer_T *offer,
+           wayland_selection_T selection);
+
+    // This event is only relevant for data control protocols
+    void (*finished)(vwl_data_device_T *device);
+} vwl_data_device_listener_T;
+
+typedef struct {
+    void (*send)(vwl_data_source_T *source, const char *mime_type, int fd);
+    void (*cancelled)(vwl_data_source_T *source);
+} vwl_data_source_listener_T;
+
+typedef struct {
+    void (*offer)(vwl_data_offer_T *offer, const char *mime_type);
+} vwl_data_offer_listener_T;
+
+typedef struct
+{
+    // What selection this refers to
+    wayland_selection_T                selection;
+
+    // Do not destroy here
+    vwl_data_device_manager_T  manager;
+
+    vwl_data_device_T          device;
+    vwl_data_source_T          source;
+    vwl_data_offer_T           *offer; // Current offer for the selection
+
+    garray_T                   mime_types;     // Mime types supported by the
+                                               // current offer
+
+    garray_T                   tmp_mime_types; // Temporary array for mime
+                                               // types when we are receiving
+                                               // them. When the selection
+                                               // event arrives and it is the
+                                               // one we want, then copy it
+                                               // over to mime_types
+
+    // To be populated by callbacks from outside this file
+    wayland_cb_send_data_func_T                    send_cb;
+    wayland_cb_selection_cancelled_func_T   cancelled_cb;
+
+    int requires_focus;                // If focus needs to be given to us to work
+} vwl_clipboard_selection_T;
+
+// Holds stuff related to the clipboard/selections
+typedef struct {
+    // Do not destroy here, will be destroyed when vwl_disconnect_display() is
+    // called.
+    vwl_seat_T                 *seat;
+
+    vwl_clipboard_selection_T  regular;
+    vwl_clipboard_selection_T  primary;
+
+    vwl_buffer_store_T         *fs_buffer;
+} vwl_clipboard_T;
+
+#endif // FEAT_WAYLAND_CLIPBOARD
+
+static int     vwl_display_flush(vwl_display_T *display);
+static void    vwl_callback_done(void *data, struct wl_callback *callback,
+                   uint32_t cb_data);
+static int     vwl_display_roundtrip(vwl_display_T *display);
+static int     vwl_display_dispatch(vwl_display_T *display);
+static int vwl_display_dispatch_any(vwl_display_T *display);
+
+static void    vwl_log_handler(const char *fmt, va_list args);
+static int     vwl_connect_display(const char *display);
+static void    vwl_disconnect_display(void);
+
+static void    vwl_xdg_wm_base_listener_ping(void *data,
+                   struct xdg_wm_base *base, uint32_t serial);
+static int     vwl_listen_to_registry(void);
+
+static void    vwl_registry_listener_global(void *data,
+                   struct wl_registry *registry, uint32_t name,
+                   const char *interface, uint32_t version);
+static void    vwl_registry_listener_global_remove(void *data,
+                   struct wl_registry *registry,  uint32_t name);
+
+static void    vwl_add_seat(struct wl_seat *seat);
+static void    vwl_seat_listener_name(void *data, struct wl_seat *seat,
+                   const char *name);
+static void    vwl_seat_listener_capabilities(void *data, struct wl_seat *seat,
+                   uint32_t capabilities);
+static void    vwl_destroy_seat(vwl_seat_T *seat);
+
+static vwl_seat_T          *vwl_get_seat(const char *label);
+static struct wl_keyboard   *vwl_seat_get_keyboard(vwl_seat_T *seat);
+
+#ifdef FEAT_WAYLAND_CLIPBOARD
+
+static int     vwl_focus_stealing_available(void);
+static void    vwl_xdg_surface_listener_configure(void *data,
+                   struct xdg_surface *surface, uint32_t serial);
+
+static void    vwl_bs_buffer_listener_release(void *data,
+                   struct wl_buffer *buffer);
+static void    vwl_destroy_buffer_store(vwl_buffer_store_T *store);
+static vwl_buffer_store_T *vwl_init_buffer_store(int width, int height);
+
+static void    vwl_destroy_fs_surface(vwl_fs_surface_T *store);
+static int     vwl_init_fs_surface(vwl_seat_T *seat,
+                   vwl_buffer_store_T *buffer_store,
+                   void (*on_focus)(void *, uint32_t), void *user_data);
+
+static void    vwl_fs_keyboard_listener_enter(void *data,
+                   struct wl_keyboard *keyboard, uint32_t serial,
+                   struct wl_surface *surface, struct wl_array *keys);
+static void    vwl_fs_keyboard_listener_keymap(void *data,
+                   struct wl_keyboard *keyboard, uint32_t format,
+                   int fd, uint32_t size);
+static void    vwl_fs_keyboard_listener_leave(void *data,
+                   struct wl_keyboard *keyboard, uint32_t serial,
+                   struct wl_surface *surface);
+static void    vwl_fs_keyboard_listener_key(void *data,
+                   struct wl_keyboard *keyboard, uint32_t serial,
+                   uint32_t time, uint32_t key, uint32_t state);
+static void    vwl_fs_keyboard_listener_modifiers(void *data,
+                   struct wl_keyboard *keyboard, uint32_t serial,
+                   uint32_t mods_depressed, uint32_t mods_latched,
+                   uint32_t mods_locked, uint32_t group);
+static void    vwl_fs_keyboard_listener_repeat_info(void *data,
+                   struct wl_keyboard *keyboard, int32_t rate, int32_t delay);
+
+static void    vwl_gen_data_device_listener_data_offer(void *data,
+                   void *offer_proxy);
+static void    vwl_gen_data_device_listener_selection(void *data,
+                   void *offer_proxy, wayland_selection_T selection,
+                   vwl_data_protocol_T protocol);
+
+static void    vwl_data_device_destroy(vwl_data_device_T *device, int alloced);
+static void    vwl_data_offer_destroy(vwl_data_offer_T *offer, int alloced);
+static void    vwl_data_source_destroy(vwl_data_source_T *source, int alloced);
+
+static void    vwl_data_device_add_listener(vwl_data_device_T *device,
+                   void *data);
+static void    vwl_data_source_add_listener(vwl_data_source_T *source,
+                   void *data);
+static void    vwl_data_offer_add_listener(vwl_data_offer_T *offer,
+                   void *data);
+
+static void    vwl_data_device_set_selection(vwl_data_device_T *device,
+                   vwl_data_source_T *source, uint32_t serial,
+                   wayland_selection_T selection);
+static void    vwl_data_offer_receive(vwl_data_offer_T *offer,
+                   const char *mime_type, int fd);
+static int     vwl_get_data_device_manager(vwl_data_device_manager_T *manager,
+                   wayland_selection_T selection);
+static void    vwl_get_data_device(vwl_data_device_manager_T *manager,
+                   vwl_seat_T *seat, vwl_data_device_T *device);
+static void    vwl_create_data_source(vwl_data_device_manager_T *manager,
+                   vwl_data_source_T *source);
+static void    vwl_data_source_offer(vwl_data_source_T *source,
+                   const char *mime_type);
+
+static void    vwl_clipboard_free_mime_types(
+                   vwl_clipboard_selection_T *clip_sel);
+static int     vwl_clipboard_selection_is_ready(
+                   vwl_clipboard_selection_T *clip_sel);
+
+static void    vwl_data_device_listener_data_offer(
+                   vwl_data_device_T *device, vwl_data_offer_T *offer);
+static void    vwl_data_offer_listener_offer(vwl_data_offer_T *offer,
+                   const char *mime_type);
+static void    vwl_data_device_listener_selection(vwl_data_device_T *device,
+                   vwl_data_offer_T *offer, wayland_selection_T selection);
+static void    vwl_data_device_listener_finished(vwl_data_device_T *device);
+
+static void    vwl_data_source_listener_send(vwl_data_source_T *source,
+                   const char *mime_type, int fd);
+static void    vwl_data_source_listener_cancelled(vwl_data_source_T *source);
+
+static void    vwl_on_focus_set_selection(void *data, uint32_t serial);
+
+static void    wayland_set_display(const char *display);
+
+static vwl_data_device_listener_T   vwl_data_device_listener = {
+    .data_offer            = vwl_data_device_listener_data_offer,
+    .selection     = vwl_data_device_listener_selection,
+    .finished      = vwl_data_device_listener_finished
+};
+
+static vwl_data_source_listener_T   vwl_data_source_listener = {
+    .send          = vwl_data_source_listener_send,
+    .cancelled     = vwl_data_source_listener_cancelled
+};
+
+static vwl_data_offer_listener_T    vwl_data_offer_listener = {
+    .offer         = vwl_data_offer_listener_offer
+};
+
+static struct xdg_wm_base_listener  vwl_xdg_wm_base_listener = {
+    .ping          = vwl_xdg_wm_base_listener_ping
+};
+
+static struct xdg_surface_listener  vwl_xdg_surface_listener = {
+    .configure     = vwl_xdg_surface_listener_configure
+};
+
+static struct wl_buffer_listener    vwl_cb_buffer_listener = {
+    .release       = vwl_bs_buffer_listener_release
+};
+
+static struct wl_keyboard_listener  vwl_fs_keyboard_listener = {
+    .enter         = vwl_fs_keyboard_listener_enter,
+    .key           = vwl_fs_keyboard_listener_key,
+    .keymap        = vwl_fs_keyboard_listener_keymap,
+    .leave         = vwl_fs_keyboard_listener_leave,
+    .modifiers     = vwl_fs_keyboard_listener_modifiers,
+    .repeat_info    = vwl_fs_keyboard_listener_repeat_info
+};
+
+#endif // FEAT_WAYLAND_CLIPBOARD
+
+static struct wl_callback_listener  vwl_callback_listener = {
+    .done          = vwl_callback_done
+};
+
+static struct wl_registry_listener  vwl_registry_listener = {
+    .global        = vwl_registry_listener_global,
+    .global_remove  = vwl_registry_listener_global_remove
+};
+
+static struct wl_seat_listener     vwl_seat_listener = {
+    .name          = vwl_seat_listener_name,
+    .capabilities   = vwl_seat_listener_capabilities
+};
+
+static vwl_display_T               vwl_display;
+static vwl_global_objects_T        vwl_gobjects;
+static garray_T                            vwl_seats;
 
-vwl_connection_T *wayland_ct;
+#ifdef FEAT_WAYLAND_CLIPBOARD
+// Make sure to sync this with vwl_cb_uninit since it memsets this to zero
+static vwl_clipboard_T vwl_clipboard = {
+    .regular.selection = WAYLAND_SELECTION_REGULAR,
+    .primary.selection = WAYLAND_SELECTION_PRIMARY,
+};
+
+// Only really used for debugging/testing purposes in order to force focus
+// stealing even when a data control protocol is available.
+static int force_fs  = FALSE;
+#endif
 
 /*
  * Like wl_display_flush but always writes all the data in the buffer to the
  * display fd. Returns FAIL on failure and OK on success.
  */
-    int
-vwl_connection_flush(vwl_connection_T *self)
+    static int
+vwl_display_flush(vwl_display_T *display)
 {
+    int ret;
+
 #ifndef HAVE_SELECT
     struct pollfd fds;
 
-    fds.fd = self->display.fd;
-    fds.events = POLLOUT;
+    fds.fd     = display->fd;
+    fds.events = POLLOUT;
 #else
     fd_set         wfds;
     struct timeval  tv;
 
     FD_ZERO(&wfds);
-    FD_SET(self->display.fd, &wfds);
+    FD_SET(display->fd, &wfds);
+
+    tv.tv_sec  = p_wtm / 1000;
+    tv.tv_usec = (p_wtm % 1000) * 1000;
 #endif
 
-    if (self->display.proxy == NULL)
+    if (display->proxy == NULL)
        return FAIL;
 
     // Send the requests we have made to the compositor, until we have written
     // all the data. Poll in order to check if the display fd is writable, if
     // not, then wait until it is and continue writing or until we timeout.
-    while (true)
+    while (errno = 0, (ret = wl_display_flush(display->proxy)) == -1
+           && errno == EAGAIN)
     {
-       int ret = wl_display_flush(self->display.proxy);
-
-       if (ret == -1 && errno == EAGAIN)
-       {
 #ifndef HAVE_SELECT
-           if (poll(&fds, 1, p_wtm) <= 0)
+       if (poll(&fds, 1, p_wtm) <= 0)
 #else
-           tv.tv_sec = p_wtm / 1000;
-           tv.tv_usec = (p_wtm % 1000) * 1000;
-           if (select(self->display.fd + 1, NULL, &wfds, NULL, &tv) <= 0)
+           if (select(display->fd + 1, NULL, &wfds, NULL, &tv) <= 0)
+#endif
                return FAIL;
+#ifdef HAVE_SELECT
+       tv.tv_sec       = 0;
+       tv.tv_usec      = p_wtm * 1000;
 #endif
-       }
-       else if (ret == -1)
-           return FAIL;
-       else
-           break;
     }
+    // Return FAIL on error or timeout
+    if ((errno != 0 && errno != EAGAIN) || ret == -1)
+       return FAIL;
 
     return OK;
 }
 
-/*
- * Like wl_display_roundtrip but polls the display fd with a timeout. Returns
- * number of events dispatched on success else -1 on failure.
- */
-    int
-vwl_connection_dispatch(vwl_connection_T *self)
-{
-#ifndef HAVE_SELECT
-    struct pollfd   fds;
-
-    fds.fd = self->display.fd;
-    fds.events = POLLIN;
-#else
-    fd_set         rfds;
-    struct timeval  tv;
-
-    FD_ZERO(&rfds);
-    FD_SET(self->display.fd, &rfds);
-#endif
-
-    if (self->display.proxy == NULL)
-       return -1;
-
-    while (wl_display_prepare_read(self->display.proxy) == -1)
-       // Dispatch any queued events so that we can start reading
-       if (wl_display_dispatch_pending(self->display.proxy) == -1)
-           return -1;
-
-    // Send any requests before we starting blocking to read display fd
-    if (vwl_connection_flush(self) == FAIL)
-    {
-       wl_display_cancel_read(self->display.proxy);
-       return -1;
-    }
-
-    // Poll until there is data to read from the display fd.
-#ifndef HAVE_SELECT
-    if (poll(&fds, 1, p_wtm) <= 0)
-#else
-    tv.tv_sec = p_wtm / 1000;
-    tv.tv_usec = (p_wtm % 1000) * 1000;
-    if (select(self->display.fd + 1, &rfds, NULL, NULL, &tv) <= 0)
-#endif
-    {
-       wl_display_cancel_read(self->display.proxy);
-       return -1;
-    }
-
-    // Read events into the queue
-    if (wl_display_read_events(self->display.proxy) == -1)
-       // No need to cancel
-       return -1;
-
-    // Dispatch those events (call the handlers associated for each event)
-    return wl_display_dispatch_pending(self->display.proxy);
-}
-
 /*
  * Called when compositor is done processing requests/events.
  */
     static void
-vwl_callback_event_done(void *data, struct wl_callback *callback,
-       uint32_t callback_data UNUSED)
+vwl_callback_done(void *data, struct wl_callback *callback,
+       uint32_t cb_data UNUSED)
 {
-    *((bool*)data) = true;
+    *((int*)data) = TRUE;
     wl_callback_destroy(callback);
 }
 
-static const struct wl_callback_listener vwl_callback_listener = {
-    .done = vwl_callback_event_done
-};
-
 /*
  * Like wl_display_roundtrip but polls the display fd with a timeout. Returns
  * FAIL on failure and OK on success.
  */
-    int
-vwl_connection_roundtrip(vwl_connection_T *self)
+    static int
+vwl_display_roundtrip(vwl_display_T *display)
 {
     struct wl_callback *callback;
-    int                        ret;
-    bool               done = false;
-#ifdef ELAPSED_FUNC
-    elapsed_T          start_tv;
-#endif
+    int                        ret, done = FALSE;
+    struct timeval start, now;
 
-    if (self->display.proxy == NULL)
+    if (display->proxy == NULL)
        return FAIL;
 
     // Tell compositor to emit 'done' event after processing all requests we
     // have sent and handling events.
-    callback = wl_display_sync(self->display.proxy);
+    callback = wl_display_sync(display->proxy);
 
     if (callback == NULL)
        return FAIL;
 
     wl_callback_add_listener(callback, &vwl_callback_listener, &done);
 
-#ifdef ELAPSED_FUNC
-    ELAPSED_INIT(start_tv);
-#endif
+    gettimeofday(&start, NULL);
 
     // Wait till we get the done event (which will set `done` to TRUE), unless
     // we timeout
-    while (true)
+    while (TRUE)
     {
-       ret = vwl_connection_dispatch(self);
+       ret = vwl_display_dispatch(display);
 
        if (done || ret == -1)
            break;
 
-#ifdef ELAPSED_FUNC
-       if (ELAPSED_FUNC(start_tv) >= p_wtm)
+       gettimeofday(&now, NULL);
+
+       if ((now.tv_sec * 1000000 + now.tv_usec) -
+               (start.tv_sec * 1000000 + start.tv_usec) >= p_wtm * 1000)
        {
            ret = -1;
            break;
        }
-#endif
     }
 
     if (ret == -1)
@@ -218,6 +499,94 @@ vwl_connection_roundtrip(vwl_connection_T *self)
     return OK;
 }
 
+/*
+ * Like wl_display_roundtrip but polls the display fd with a timeout. Returns
+ * number of events dispatched on success else -1 on failure.
+ */
+    static int
+vwl_display_dispatch(vwl_display_T *display)
+{
+#ifndef HAVE_SELECT
+    struct pollfd   fds;
+
+    fds.fd         = display->fd;
+    fds.events     = POLLIN;
+#else
+    fd_set          rfds;
+    struct timeval  tv;
+
+    FD_ZERO(&rfds);
+    FD_SET(display->fd, &rfds);
+
+    tv.tv_sec      = p_wtm / 1000;
+    tv.tv_usec     = (p_wtm % 1000) * 1000;
+#endif
+
+    if (display->proxy == NULL)
+       return -1;
+
+    while (wl_display_prepare_read(display->proxy) == -1)
+       // Dispatch any queued events so that we can start reading
+       if (wl_display_dispatch_pending(display->proxy) == -1)
+           return -1;
+
+    // Send any requests before we starting blocking to read display fd
+    if (vwl_display_flush(display) == FAIL)
+    {
+       wl_display_cancel_read(display->proxy);
+       return -1;
+    }
+
+    // Poll until there is data to read from the display fd.
+#ifndef HAVE_SELECT
+    if (poll(&fds, 1, p_wtm) <= 0)
+#else
+    if (select(display->fd + 1, &rfds, NULL, NULL, &tv) <= 0)
+#endif
+       {
+           wl_display_cancel_read(display->proxy);
+           return -1;
+       }
+
+    // Read events into the queue
+    if (wl_display_read_events(display->proxy) == -1)
+       return -1;
+
+    // Dispatch those events (call the handlers associated for each event)
+    return wl_display_dispatch_pending(display->proxy);
+}
+
+/*
+ * Same as vwl_display_dispatch but poll/select is never called. This is useful
+ * is poll/select was already called before or if you just want to dispatch any
+ * events that happen to be waiting to be dispatched on the display fd.
+ */
+    static int
+vwl_display_dispatch_any(vwl_display_T *display)
+{
+    if (display->proxy == NULL)
+       return -1;
+
+    while (wl_display_prepare_read(display->proxy) == -1)
+       // Dispatch any queued events so that we can start reading
+       if (wl_display_dispatch_pending(display->proxy) == -1)
+           return -1;
+
+    // Send any requests before we starting blocking to read display fd
+    if (vwl_display_flush(display) == FAIL)
+    {
+       wl_display_cancel_read(display->proxy);
+       return -1;
+    }
+
+    // Read events into the queue
+    if (wl_display_read_events(display->proxy) == -1)
+       return -1;
+
+    // Dispatch those events (call the handlers associated for each event)
+    return wl_display_dispatch_pending(display->proxy);
+}
+
 /*
  * Redirect libwayland logging to use ch_log + emsg instead.
  */
@@ -247,128 +616,210 @@ vwl_log_handler(const char *fmt, va_list args)
 }
 
 /*
- * Callback for seat text label/name
+ * Connect to the display with name; passing NULL will use libwayland's way of
+ * getting the display. Additionally get the registry object but will not
+ * starting listening. Returns OK on sucess and FAIL on failure.
  */
-    static void
-wl_seat_listener_event_name(
-       void            *data,
-       struct wl_seat  *seat_proxy UNUSED,
-       const char      *name)
+    static int
+vwl_connect_display(const char *display)
 {
-    vwl_seat_T *seat = data;
+    if (wayland_no_connect)
+       return FAIL;
 
-    seat->label = (char *)vim_strsave((char_u *)name);
+    // We will get an error if XDG_RUNTIME_DIR is not set.
+    if (mch_getenv("XDG_RUNTIME_DIR") == NULL)
+       return FAIL;
+
+    // Must set log handler before we connect display in order to work.
+    wl_log_set_handler_client(vwl_log_handler);
+
+    vwl_display.proxy = wl_display_connect(display);
+
+    if (vwl_display.proxy == NULL)
+       return FAIL;
+
+    wayland_set_display(display);
+    vwl_display.fd = wl_display_get_fd(vwl_display.proxy);
+
+    vwl_display.registry.proxy = wl_display_get_registry(vwl_display.proxy);
+
+    if (vwl_display.registry.proxy == NULL)
+    {
+       vwl_disconnect_display();
+       return FAIL;
+    }
+
+    return OK;
 }
 
+#define destroy_gobject(object) \
+    if (vwl_gobjects.object != NULL) \
+    { \
+       object##_destroy(vwl_gobjects.object); \
+       vwl_gobjects.object = NULL; \
+    }
+
 /*
- * Callback for seat capabilities
+ * Disconnects the display and frees up all resources, including all global
+ * objects.
  */
     static void
-wl_seat_listener_event_capabilities(
-       void            *data,
-       struct wl_seat  *seat_proxy UNUSED,
-       uint32_t        capabilities)
+vwl_disconnect_display(void)
 {
-    vwl_seat_T *seat = data;
 
-    seat->capabilities = capabilities;
+    destroy_gobject(ext_data_control_manager_v1)
+    destroy_gobject(zwlr_data_control_manager_v1)
+    destroy_gobject(wl_data_device_manager)
+    destroy_gobject(wl_shm)
+    destroy_gobject(wl_compositor)
+    destroy_gobject(xdg_wm_base)
+    destroy_gobject(zwp_primary_selection_device_manager_v1)
+
+    for (int i = 0; i < vwl_seats.ga_len; i++)
+       vwl_destroy_seat(&((vwl_seat_T *)vwl_seats.ga_data)[i]);
+    ga_clear(&vwl_seats);
+    vwl_seats.ga_len = 0;
+
+    if (vwl_display.registry.proxy != NULL)
+    {
+       wl_registry_destroy(vwl_display.registry.proxy);
+       vwl_display.registry.proxy = NULL;
+    }
+    if (vwl_display.proxy != NULL)
+    {
+       wl_display_disconnect(vwl_display.proxy);
+       vwl_display.proxy = NULL;
+    }
 }
 
-static const struct wl_seat_listener wl_seat_listener = {
-    .name = wl_seat_listener_event_name,
-    .capabilities = wl_seat_listener_event_capabilities
-};
+/*
+ * Tells the compositor we are still responsive.
+ */
+    static void
+vwl_xdg_wm_base_listener_ping(
+       void *data UNUSED,
+       struct xdg_wm_base *base,
+       uint32_t serial)
+{
+    xdg_wm_base_pong(base, serial);
+}
+
+/*
+ * Start listening to the registry and get initial set of global
+ * objects/interfaces.
+ */
+    static int
+vwl_listen_to_registry(void)
+{
+    // Only meant for debugging/testing purposes
+    char_u *env = mch_getenv("VIM_WAYLAND_FORCE_FS");
+
+    if (env != NULL && STRCMP(env, "1") == 0)
+       force_fs = TRUE;
+    else
+       force_fs = FALSE;
+
+    ga_init2(&vwl_seats, sizeof(vwl_seat_T), 1);
+
+    wl_registry_add_listener(
+           vwl_display.registry.proxy,
+           &vwl_registry_listener,
+           NULL);
+
+    if (vwl_display_roundtrip(&vwl_display) == FAIL)
+       return FAIL;
+
+#ifdef FEAT_WAYLAND_CLIPBOARD
+    // If we have a suitable data control protocol discard the rest. If we only
+    // have wlr data control protocol but its version is 1, then don't discard
+    // globals if we also have the primary selection protocol.
+    if (!force_fs &&
+           (vwl_gobjects.ext_data_control_manager_v1 != NULL ||
+            (vwl_gobjects.zwlr_data_control_manager_v1 != NULL &&
+             zwlr_data_control_manager_v1_get_version(
+                 vwl_gobjects.zwlr_data_control_manager_v1) > 1)))
+    {
+       destroy_gobject(wl_data_device_manager)
+       destroy_gobject(wl_shm)
+       destroy_gobject(wl_compositor)
+       destroy_gobject(xdg_wm_base)
+    }
+    else
+       // Be ready for ping events
+       xdg_wm_base_add_listener(
+               vwl_gobjects.xdg_wm_base,
+               &vwl_xdg_wm_base_listener,
+               NULL);
+#endif
+    return OK;
+}
 
-static void vwl_seat_destroy(vwl_seat_T *self);
+#define SET_GOBJECT(object, min_ver) \
+    do { \
+       chosen_interface = &object##_interface; \
+       object_member = (void*)&vwl_gobjects.object; \
+       min_version = min_ver; \
+    } while (0)
 
 /*
  * Callback for global event, for each global interface the compositor supports.
  * Keep in sync with vwl_disconnect_display().
  */
     static void
-wl_registry_listener_event_global(
-       void                *data,
-       struct wl_registry  *registry,
+vwl_registry_listener_global(
+       void                *data UNUSED,
+       struct wl_registry  *registry UNUSED,
        uint32_t            name,
        const char          *interface,
        uint32_t            version)
 {
-    vwl_connection_T *ct = data;
+
+    const struct wl_interface  *chosen_interface = NULL;
+    void                       *proxy;
+    uint32_t                   min_version;
+    void                       **object_member;
 
     if (STRCMP(interface, wl_seat_interface.name) == 0)
     {
-       struct wl_seat  *seat_proxy = wl_registry_bind(registry, name,
-               &wl_seat_interface, version > 5 ? 5 : version);
-       vwl_seat_T      *seat;
+       chosen_interface = &wl_seat_interface;
+       min_version = 2;
+    }
+#ifdef FEAT_WAYLAND_CLIPBOARD
+    else if (STRCMP(interface, zwlr_data_control_manager_v1_interface.name) == 0)
+       SET_GOBJECT(zwlr_data_control_manager_v1, 1);
 
-       if (seat_proxy == NULL)
-           return;
+    else if (STRCMP(interface, ext_data_control_manager_v1_interface.name) == 0)
+       SET_GOBJECT(ext_data_control_manager_v1, 1);
 
-       seat = ALLOC_CLEAR_ONE(vwl_seat_T);
+    else if (STRCMP(interface, wl_data_device_manager_interface.name) == 0)
+       SET_GOBJECT(wl_data_device_manager, 1);
 
-       if (seat == NULL || ga_grow(&ct->gobjects.seats, 1) == FAIL)
-       {
-           vwl_seat_destroy(seat);
-           return;
-       }
+    else if (STRCMP(interface, wl_shm_interface.name) == 0)
+       SET_GOBJECT(wl_shm, 1);
 
-       seat->proxy = seat_proxy;
-       wl_seat_add_listener(seat_proxy, &wl_seat_listener, seat);
+    else if (STRCMP(interface, wl_compositor_interface.name) == 0)
+       SET_GOBJECT(wl_compositor, 2);
 
-       if (vwl_connection_roundtrip(ct) == FAIL || seat->label == NULL)
-       {
-           vwl_seat_destroy(seat);
-           return;
-       }
+    else if (STRCMP(interface, xdg_wm_base_interface.name) == 0)
+       SET_GOBJECT(xdg_wm_base, 1);
 
-       ((vwl_seat_T **)ct->gobjects.seats.ga_data)[ct->gobjects.seats.ga_len++]
-           = seat;
-    }
-#ifdef FEAT_WAYLAND_CLIPBOARD
-    else if (STRCMP(interface, zwlr_data_control_manager_v1_interface.name) == 0)
-       ct->gobjects.zwlr_data_control_manager_v1 =
-           wl_registry_bind(registry, name,
-                   &zwlr_data_control_manager_v1_interface,
-                   version > 2 ? 2 : version);
+    else if (STRCMP(interface,
+               zwp_primary_selection_device_manager_v1_interface.name) == 0)
+       SET_GOBJECT(zwp_primary_selection_device_manager_v1, 1);
+#endif
 
-    else if (STRCMP(interface, ext_data_control_manager_v1_interface.name) == 0)
-       ct->gobjects.ext_data_control_manager_v1 =
-           wl_registry_bind(registry, name,
-                   &ext_data_control_manager_v1_interface, 1);
+    if (chosen_interface == NULL || version < min_version)
+       return;
 
-# ifdef FEAT_WAYLAND_CLIPBOARD_FS
-    else if (p_wst)
-    {
-       if (STRCMP(interface, wl_data_device_manager_interface.name) == 0)
-           ct->gobjects.wl_data_device_manager =
-               wl_registry_bind(registry, name,
-                       &wl_data_device_manager_interface, 1);
-
-       else if (STRCMP(interface, wl_shm_interface.name) == 0)
-           ct->gobjects.wl_shm =
-               wl_registry_bind(registry, name,
-                       &wl_shm_interface, 1);
-
-       else if (STRCMP(interface, wl_compositor_interface.name) == 0)
-           ct->gobjects.wl_compositor =
-               wl_registry_bind(registry, name,
-                       &wl_compositor_interface, 1);
-
-       else if (STRCMP(interface, xdg_wm_base_interface.name) == 0)
-           ct->gobjects.xdg_wm_base =
-               wl_registry_bind(registry, name,
-                       &xdg_wm_base_interface, 1);
-
-       else if (STRCMP(interface,
-                   zwp_primary_selection_device_manager_v1_interface.name)
-                   == 0)
-           ct->gobjects.zwp_primary_selection_device_manager_v1 =
-               wl_registry_bind(registry, name,
-                       &zwp_primary_selection_device_manager_v1_interface, 1);
-    }
-# endif // FEAT_WAYLAND_CLIPBOARD_FS
-#endif // FEAT_WAYLAND_CLIPBOARD
+    proxy = wl_registry_bind(vwl_display.registry.proxy, name, chosen_interface,
+           version);
 
+    if (chosen_interface == &wl_seat_interface)
+       // Add seat to vwl_seats array, as we can have multiple seats.
+       vwl_add_seat(proxy);
+    else
+       // Hold proxy & name in the vwl_gobject struct
+       *object_member = proxy;
 }
 
 /*
@@ -378,196 +829,86 @@ wl_registry_listener_event_global(
  * global will just be ignored on the compositor side.
  */
     static void
-wl_registry_listener_event_global_remove(
+vwl_registry_listener_global_remove(
        void                *data UNUSED,
        struct wl_registry  *registry UNUSED,
        uint32_t            name UNUSED)
 {
 }
 
-#ifdef FEAT_WAYLAND_CLIPBOARD_FS
+/*
+ * Add a new seat given its proxy to the global grow array
+ */
     static void
-xdg_wm_base_listener_event_ping(
-       void *data UNUSED,
-       struct xdg_wm_base *xdg_base,
-       uint32_t serial)
+vwl_add_seat(struct wl_seat *seat_proxy)
 {
-    xdg_wm_base_pong(xdg_base, serial);
-}
-#endif
+    vwl_seat_T *seat;
 
-static const struct wl_registry_listener wl_registry_listener = {
-    .global = wl_registry_listener_event_global,
-    .global_remove = wl_registry_listener_event_global_remove
-};
+    if (ga_grow(&vwl_seats, 1) == FAIL)
+       return;
 
-#ifdef FEAT_WAYLAND_CLIPBOARD_FS
-static const struct xdg_wm_base_listener xdg_wm_base_listener  = {
-    .ping = xdg_wm_base_listener_event_ping
-};
-#endif
+    seat = &((vwl_seat_T *)vwl_seats.ga_data)[vwl_seats.ga_len];
 
-static void vwl_connection_destroy(vwl_connection_T *self);
+    seat->proxy = seat_proxy;
 
-#ifdef FEAT_WAYLAND_CLIPBOARD
+    // Get label and capabilities
+    wl_seat_add_listener(seat_proxy, &vwl_seat_listener, seat);
 
-# define VWL_DESTROY_GOBJECT(ct, object) \
-    if (ct->gobjects.object != NULL) \
-    { \
-       object##_destroy(ct->gobjects.object); \
-       ct->gobjects.object = NULL; \
-    }
+    if (vwl_display_roundtrip(&vwl_display) == FAIL)
+       return;
 
-# define VWL_GOBJECT_AVAIL(ct, object) (ct->gobjects.object != NULL)
+    // Check if label has been allocated
+    if (seat->label == NULL)
+       return;
 
-#endif
+    vwl_seats.ga_len++;
+}
 
-// Make sure to call wayland_set_display(display);
-    static vwl_connection_T *
-vwl_connection_new(const char *display)
+/*
+ * Callback for seat text label/name
+ */
+    static void
+vwl_seat_listener_name(
+       void            *data,
+       struct wl_seat  *seat_proxy UNUSED,
+       const char      *name)
 {
-    vwl_connection_T   *ct;
-#ifdef FEAT_WAYLAND_CLIPBOARD_FS
-    const char_u       *env;
-    bool               force_fs;
-#endif
-    if (wayland_no_connect)
-       return NULL;
-
-    // We will get an error if XDG_RUNTIME_DIR is not set.
-    if (mch_getenv("XDG_RUNTIME_DIR") == NULL)
-       return NULL;
-
-    ct = ALLOC_CLEAR_ONE(vwl_connection_T);
-
-    if (ct == NULL)
-       return NULL;
-
-    // Must set log handler before we connect display in order to work.
-    wl_log_set_handler_client(vwl_log_handler);
-
-    ct->display.proxy = wl_display_connect(display);
-
-    if (ct->display.proxy == NULL)
-    {
-       vim_free(ct);
-       return NULL;
-    }
-
-    ct->display.fd = wl_display_get_fd(ct->display.proxy);
-    ct->registry.proxy = wl_display_get_registry(ct->display.proxy);
-
-    if (ct->registry.proxy == NULL)
-    {
-       wl_display_disconnect(ct->display.proxy);
-       vim_free(ct);
-       return NULL;
-    }
-
-    ga_init2(&ct->gobjects.seats, sizeof(vwl_seat_T *), 1);
-
-    wl_registry_add_listener(ct->registry.proxy, &wl_registry_listener, ct);
-
-#ifdef FEAT_WAYLAND_CLIPBOARD_FS
-    env = mch_getenv("VIM_WAYLAND_FORCE_FS");
-    force_fs = (env != NULL && STRCMP(env, "1") == 0);
-
-    if (force_fs)
-       p_wst = TRUE;
-#endif
-
-    if (vwl_connection_roundtrip(ct) == FAIL)
-    {
-       vwl_connection_destroy(ct);
-       return NULL;
-    }
-
-#ifdef FEAT_WAYLAND_CLIPBOARD_FS
-    if (force_fs)
-    {
-       // Force using focus stealing method
-       VWL_DESTROY_GOBJECT(ct, ext_data_control_manager_v1)
-       VWL_DESTROY_GOBJECT(ct, zwlr_data_control_manager_v1)
-    }
-
-    // If data control protocols are available, we don't need the other global
-    // objects.
-    else if (VWL_GOBJECT_AVAIL(ct, ext_data_control_manager_v1)
-           || VWL_GOBJECT_AVAIL(ct, zwlr_data_control_manager_v1))
-    {
-       VWL_DESTROY_GOBJECT(ct, wl_data_device_manager)
-       VWL_DESTROY_GOBJECT(ct, wl_shm)
-       VWL_DESTROY_GOBJECT(ct, wl_compositor)
-       VWL_DESTROY_GOBJECT(ct, xdg_wm_base)
-       VWL_DESTROY_GOBJECT(ct, zwp_primary_selection_device_manager_v1)
-    }
-
-    // Start responding to pings from the compositor if we have xdg_wm_base
-    if (VWL_GOBJECT_AVAIL(ct, xdg_wm_base))
-       xdg_wm_base_add_listener(ct->gobjects.xdg_wm_base,
-               &xdg_wm_base_listener, NULL);
-#endif
+    vwl_seat_T *seat = data;
 
-    return ct;
+    seat->label = (char *)vim_strsave((char_u *)name);
 }
 
-#ifdef FEAT_WAYLAND_CLIPBOARD
-
 /*
- * Destroy/free seat.
+ * Callback for seat capabilities
  */
     static void
-vwl_seat_destroy(vwl_seat_T *self)
+vwl_seat_listener_capabilities(
+       void            *data,
+       struct wl_seat  *seat_proxy UNUSED,
+       uint32_t        capabilities)
 {
-    if (self == NULL)
-       return;
-    if (self->proxy != NULL)
-    {
-       if (wl_seat_get_version(self->proxy) >= 5)
-           // Helpful for the compositor
-           wl_seat_release(self->proxy);
-       else
-           wl_seat_destroy(self->proxy);
-    }
-    vim_free(self->label);
-    vim_free(self);
+    vwl_seat_T *seat = data;
+
+    seat->capabilities = capabilities;
 }
 
 /*
- * Disconnects the display and frees up all resources, including all global
- * objects.
+ * Destroy/free seat.
  */
     static void
-vwl_connection_destroy(vwl_connection_T *self)
+vwl_destroy_seat(vwl_seat_T *seat)
 {
-#ifdef FEAT_WAYLAND_CLIPBOARD
-    VWL_DESTROY_GOBJECT(self, ext_data_control_manager_v1)
-    VWL_DESTROY_GOBJECT(self, zwlr_data_control_manager_v1)
-# ifdef FEAT_WAYLAND_CLIPBOARD_FS
-    VWL_DESTROY_GOBJECT(self, wl_data_device_manager)
-    VWL_DESTROY_GOBJECT(self, wl_shm)
-    VWL_DESTROY_GOBJECT(self, wl_compositor)
-    VWL_DESTROY_GOBJECT(self, xdg_wm_base)
-    VWL_DESTROY_GOBJECT(self, zwp_primary_selection_device_manager_v1)
-# endif
-#endif
-
-    for (int i = 0; i < self->gobjects.seats.ga_len; i++)
-       vwl_seat_destroy(((vwl_seat_T **)self->gobjects.seats.ga_data)[i]);
-    ga_clear(&self->gobjects.seats);
-    self->gobjects.seats.ga_len = 0;
-
-    if (self->registry.proxy != NULL)
-    {
-       wl_registry_destroy(self->registry.proxy);
-       self->registry.proxy = NULL;
-    }
-    if (self->display.proxy != NULL)
+    if (seat->proxy != NULL)
     {
-       wl_display_disconnect(self->display.proxy);
-       self->display.proxy = NULL;
+       if (wl_seat_get_version(seat->proxy) >= 5)
+           // Helpful for the compositor
+           wl_seat_release(seat->proxy);
+       else
+           wl_seat_destroy(seat->proxy);
+       seat->proxy = NULL;
     }
-    vim_free(self);
+    vim_free(seat->label);
+    seat->label = NULL;
 }
 
 /*
@@ -575,88 +916,51 @@ vwl_connection_destroy(vwl_connection_T *self)
  * If NULL or an empty string is passed as the label then the first available
  * seat found is used.
  */
-    vwl_seat_T *
-vwl_connection_get_seat(vwl_connection_T *self, const char *label)
+    static vwl_seat_T *
+vwl_get_seat(const char *label)
 {
-    if ((STRCMP(label, "") == 0 || label == NULL)
-           && self->gobjects.seats.ga_len > 0)
-       return ((vwl_seat_T **)self->gobjects.seats.ga_data)[0];
+    if ((STRCMP(label, "") == 0 || label == NULL) && vwl_seats.ga_len > 0)
+           return &((vwl_seat_T *)vwl_seats.ga_data)[0];
 
-    for (int i = 0; i < self->gobjects.seats.ga_len; i++)
+    for (int i = 0; i < vwl_seats.ga_len; i++)
     {
-       vwl_seat_T *seat = ((vwl_seat_T **)self->gobjects.seats.ga_data)[i];
+       vwl_seat_T *seat = &((vwl_seat_T *)vwl_seats.ga_data)[i];
        if (STRCMP(seat->label, label) == 0)
            return seat;
     }
     return NULL;
 }
 
-#ifdef FEAT_WAYLAND_CLIPBOARD_FS
 /*
  * Get keyboard object from seat and return it. NULL is returned on
  * failure such as when a keyboard is not available for seat.
  */
-    struct wl_keyboard *
-vwl_seat_get_keyboard(vwl_seat_T *self)
+    static struct wl_keyboard *
+vwl_seat_get_keyboard(vwl_seat_T *seat)
 {
-    if (!(self->capabilities & WL_SEAT_CAPABILITY_KEYBOARD))
+    if (!(seat->capabilities & WL_SEAT_CAPABILITY_KEYBOARD))
        return NULL;
 
-    return wl_seat_get_keyboard(self->proxy);
-}
-#endif
-
-#endif
-
-/*
- * Set wayland_display_name to display. Note that this allocate a copy of the
- * string, unless NULL is passed. If NULL is passed then v:wayland_display is
- * set to $WAYLAND_DISPLAY, but wayland_display_name is set to NULL.
- */
-    static void
-wayland_set_display(const char *display)
-{
-    if (display == NULL)
-       display = (char*)mch_getenv((char_u*)"WAYLAND_DISPLAY");
-    else if (display == wayland_display_name)
-       // Don't want to be freeing vwl_display_strname then trying to copy it
-       // after.
-       goto exit;
-
-    if (display == NULL)
-       // $WAYLAND_DISPLAY is not set
-       display = "";
-
-    // Leave unchanged if display is empty (but not NULL)
-    if (STRCMP(display, "") != 0)
-    {
-       vim_free(wayland_display_name);
-       wayland_display_name = (char*)vim_strsave((char_u*)display);
-    }
-
-exit:
-#ifdef FEAT_EVAL
-    set_vim_var_string(VV_WAYLAND_DISPLAY, (char_u*)display, -1);
-#endif
+    return wl_seat_get_keyboard(seat->proxy);
 }
 
 /*
- * Initializes the global Wayland connection. Connects to the Wayland display
- * with given name and binds to global objects as needed. If display is NULL
- * then the $WAYLAND_DISPLAY environment variable will be used (handled by
- * libwayland). Returns FAIL on failure and OK on
+ * Connects to the Wayland display with given name and binds to global objects
+ * as needed. If display is NULL then the $WAYLAND_DISPLAY environment variable
+ * will be used (handled by libwayland). Returns FAIL on failure and OK on
  * success
  */
     int
-wayland_init_connection(const char *display)
+wayland_init_client(const char *display)
 {
     wayland_set_display(display);
 
-    wayland_ct = vwl_connection_new(display);
-
-    if (wayland_ct == NULL)
+    if (vwl_connect_display(display) == FAIL ||
+           vwl_listen_to_registry() == FAIL)
        goto fail;
 
+    wayland_display_fd = vwl_display.fd;
+
     return OK;
 fail:
     // Set v:wayland_display to empty string (but not wayland_display_name)
@@ -665,787 +969,1545 @@ fail:
 }
 
 /*
- * Disconnect global Wayland connection and free up all resources used.
+ * Disconnect Wayland client and free up all resources used.
  */
     void
-wayland_uninit_connection(void)
+wayland_uninit_client(void)
 {
-    if (wayland_ct == NULL)
-       return;
 #ifdef FEAT_WAYLAND_CLIPBOARD
-    clip_uninit_wayland();
+    wayland_cb_uninit();
 #endif
-    vwl_connection_destroy(wayland_ct);
-    wayland_ct = NULL;
+    vwl_disconnect_display();
+
     wayland_set_display("");
 }
 
-static int wayland_ct_restore_count = 0;
-
 /*
- * Attempts to restore the Wayland display connection.
+ * Return TRUE if Wayland display connection is valid and ready.
  */
-    static void
-wayland_restore_connection(void)
+    int
+wayland_client_is_connected(int quiet)
 {
-    // No point in restoring the connection if we are exiting or dying.
-    if (exiting || v_dying || wayland_ct_restore_count <= 0)
-       wayland_set_display("");
-
-    --wayland_ct_restore_count;
-    wayland_uninit_connection();
-
-    if (wayland_init_connection(wayland_display_name) == OK)
-    {
-#ifdef FEAT_WAYLAND_CLIPBOARD
-       clip_init_wayland();
-#endif
-    }
+    if (vwl_display.proxy == NULL)
+       goto error;
+
+    // Display errors are always fatal
+    if (wl_display_get_error(vwl_display.proxy) != 0
+           || vwl_display_flush(&vwl_display) == FAIL)
+       goto error;
+
+    return TRUE;
+error:
+    if (!quiet)
+       emsg(e_wayland_connection_unavailable);
+    return FALSE;
 }
 
 /*
- * Should be called before polling (select or poll) the global Wayland
- * connection display fd. Returns fd on success and -1 on failure.
+ * Flush requests and process new Wayland events, does not poll the display file
+ * descriptor.
  */
     int
-wayland_prepare_read(void)
+wayland_client_update(void)
 {
-    if (wayland_ct == NULL)
-       return -1;
+    return vwl_display_dispatch_any(&vwl_display) == -1 ? FAIL : OK;
+}
 
-    while (wl_display_prepare_read(wayland_ct->display.proxy) == -1)
-       // Event queue not empty, dispatch the events
-       if (wl_display_dispatch_pending(wayland_ct->display.proxy) == -1)
-           return -1;
+#ifdef FEAT_WAYLAND_CLIPBOARD
 
-    if (vwl_connection_flush(wayland_ct) < 0)
-    {
-       wl_display_cancel_read(wayland_ct->display.proxy);
-       return -1;
-    }
-    return wayland_ct->display.fd;
+/*
+ * If globals required for focus stealing method is available.
+ */
+    static int
+vwl_focus_stealing_available(void)
+{
+    return (p_wst || force_fs) &&
+       vwl_gobjects.wl_compositor != NULL &&
+       vwl_gobjects.wl_shm != NULL &&
+       vwl_gobjects.xdg_wm_base != NULL;
 }
 
 /*
- * Catch up on any qeueued events
+ * Configure xdg_surface
  */
-    int
-wayland_update(void)
+    static void
+vwl_xdg_surface_listener_configure(
+       void                *data UNUSED,
+       struct xdg_surface  *surface,
+       uint32_t            serial)
 {
-    if (wayland_ct == NULL)
-       return FAIL;
-    return vwl_connection_roundtrip(wayland_ct);
+    xdg_surface_ack_configure(surface, serial);
 }
 
-#ifndef HAVE_SELECT
-
-    void
-wayland_poll_check(int revents)
+/*
+ * Called when compositor isn't using the buffer anymore, we can reuse it again.
+ */
+    static void
+vwl_bs_buffer_listener_release(
+       void                *data,
+       struct wl_buffer    *buffer UNUSED)
 {
-    if (wayland_ct == NULL)
-       return;
+    vwl_buffer_store_T *store = data;
 
-    if (revents & POLLIN)
-       if (wl_display_read_events(wayland_ct->display.proxy) != -1)
-       {
-           wl_display_dispatch_pending(wayland_ct->display.proxy);
-           return;
-       }
-    else if (revents & (POLLHUP | POLLERR))
-       wl_display_cancel_read(wayland_ct->display.proxy);
-    else
-    {
-       // Nothing happened
-       wl_display_cancel_read(wayland_ct->display.proxy);
-       return;
-    }
-    wayland_restore_connection();
+    store->available = TRUE;
 }
 
-#else // ifdef HAVE_SELECT
-
-    void
-wayland_select_check(bool is_set)
+/*
+ * Destroy a buffer store structure.
+ */
+    static void
+vwl_destroy_buffer_store(vwl_buffer_store_T *store)
 {
-    if (wayland_ct == NULL)
-       return;
+    if (store->buffer != NULL)
+       wl_buffer_destroy(store->buffer);
+    if (store->pool != NULL)
+       wl_shm_pool_destroy(store->pool);
 
-    if (is_set)
-    {
-       if (wl_display_read_events(wayland_ct->display.proxy) != -1)
-           wl_display_dispatch_pending(wayland_ct->display.proxy);
-       else
-           wayland_restore_connection();
-    }
-    else
-       wl_display_cancel_read(wayland_ct->display.proxy);
-}
+    close(store->fd);
 
-#endif // !HAVE_SELECT
+    vim_free(store);
+}
 
 /*
- * Disconnect then reconnect Wayland connection, and update clipmethod.
+ * Initialize a buffer and its backing memory pool.
  */
-    void
-ex_wlrestore(exarg_T *eap)
+    static vwl_buffer_store_T *
+vwl_init_buffer_store(int width, int height)
 {
-    char *display;
+    int                        fd, r;
+    vwl_buffer_store_T *store;
 
-    if (eap->arg == NULL || STRLEN(eap->arg) == 0)
-       // Use current display name if none given
-       display = wayland_display_name;
-    else
-       display = (char*)eap->arg;
+    if (vwl_gobjects.wl_shm == NULL)
+       return NULL;
 
-    // Return early if shebang is not passed, we are still connected, and if not
-    // changing to a new Wayland display.
-    if (!eap->forceit && wayland_ct != NULL &&
-           (display == wayland_display_name ||
-            (wayland_display_name != NULL &&
-             STRCMP(wayland_display_name, display) == 0)))
-       return;
+    store = alloc(sizeof(*store));
 
-#ifdef FEAT_WAYLAND_CLIPBOARD
-    if (clipmethod == CLIPMETHOD_WAYLAND)
+    if (store == NULL)
+       return NULL;
+
+    store->available = FALSE;
+
+    store->width = width;
+    store->height = height;
+    store->stride = store->width * 4;
+    store->size = store->stride * store->height;
+
+    fd = mch_create_anon_file();
+    r = ftruncate(fd, store->size);
+
+    if (r == -1)
     {
-       // Lose any selections we own
-       if (clip_star.owned)
-           clip_lose_selection(&clip_star);
-       if (clip_plus.owned)
-           clip_lose_selection(&clip_plus);
+       if (fd >= 0)
+           close(fd);
+       return NULL;
     }
-#endif
 
-    if (display != NULL)
-       display = (char*)vim_strsave((char_u*)display);
+    store->pool = wl_shm_create_pool(vwl_gobjects.wl_shm, fd, store->size);
+    store->buffer = wl_shm_pool_create_buffer(
+           store->pool,
+           0,
+           store->width,
+           store->height,
+           store->stride,
+           WL_SHM_FORMAT_ARGB8888);
 
-    // Will lose any selections we own
-    wayland_uninit_connection();
+    store->fd = fd;
 
-    // Reset amount of available tries to reconnect the display to 5
-    wayland_ct_restore_count = 5;
+    wl_buffer_add_listener(store->buffer, &vwl_cb_buffer_listener, store);
 
-    if (wayland_init_connection(display) == OK)
+    if (vwl_display_roundtrip(&vwl_display) == -1)
     {
-       smsg(_("restoring Wayland display %s"), wayland_display_name);
-
-#ifdef FEAT_WAYLAND_CLIPBOARD
-       did_warn_clipboard = FALSE;
-       clip_init_wayland();
-#endif
+       vwl_destroy_buffer_store(store);
+       return NULL;
     }
-    else
-       msg(_("failed restoring, lost connection to Wayland display"));
 
-    vim_free(display);
+    store->available = TRUE;
 
-    choose_clipmethod();
+    return store;
 }
 
-#ifdef FEAT_WAYLAND_CLIPBOARD
-
 /*
- * Get a suitable data device manager from connection. "supported" should be
- * iniitialized to VWL_DATA_PROTOCOL_NONE beforehand. Returns NULL if there are
- * no data device manager available with the required selection.
+ * Destroy a focus stealing store structure.
  */
-    vwl_data_device_manager_T *
-vwl_connection_get_data_device_manager(
-       vwl_connection_T *self,
-       wayland_selection_T req_sel,
-       int_u *supported)
-{
-    vwl_data_device_manager_T *manager =
-       ALLOC_CLEAR_ONE(vwl_data_device_manager_T);
-
-    // Prioritize ext-data-control-v1 over wlr-data-control-unstable-v1 because
-    // it is newer.
-    if (self->gobjects.ext_data_control_manager_v1 != NULL)
+    static void
+vwl_destroy_fs_surface(vwl_fs_surface_T *store)
+{
+    if (store->shell.toplevel != NULL)
+       xdg_toplevel_destroy(store->shell.toplevel);
+    if (store->shell.surface != NULL)
+       xdg_surface_destroy(store->shell.surface);
+    if (store->surface != NULL)
+       wl_surface_destroy(store->surface);
+    if (store->keyboard != NULL)
     {
-       manager->proxy = self->gobjects.ext_data_control_manager_v1;
-       manager->protocol = VWL_DATA_PROTOCOL_EXT;
-
-       *supported |= (WAYLAND_SELECTION_REGULAR | WAYLAND_SELECTION_PRIMARY);
+       if (wl_keyboard_get_version(store->keyboard) >= 3)
+           wl_keyboard_release(store->keyboard);
+       else
+           wl_keyboard_destroy(store->keyboard);
     }
-    else if (self->gobjects.zwlr_data_control_manager_v1 != NULL)
-    {
-       manager->proxy = self->gobjects.zwlr_data_control_manager_v1;
-       manager->protocol = VWL_DATA_PROTOCOL_WLR;
+    vim_free(store);
+}
 
-       *supported |= WAYLAND_SELECTION_REGULAR;
+/*
+ * Create an invisible surface in order to gain focus and call on_focus() with
+ * serial that was given.
+ */
+    static int
+vwl_init_fs_surface(
+       vwl_seat_T          *seat,
+       vwl_buffer_store_T  *buffer_store,
+       void                (*on_focus)(void *, uint32_t),
+       void                *user_data)
+{
+    vwl_fs_surface_T *store;
 
-       // Only version 2 or greater supports the primary selection
-       if (zwlr_data_control_manager_v1_get_version(manager->proxy) >= 2)
-           *supported |= WAYLAND_SELECTION_PRIMARY;
-    }
-#ifdef FEAT_WAYLAND_CLIPBOARD_FS
-    else if (self->gobjects.wl_data_device_manager != NULL
-           && req_sel == WAYLAND_SELECTION_REGULAR)
-    {
-       manager->proxy = self->gobjects.wl_data_device_manager;
-       manager->protocol = VWL_DATA_PROTOCOL_CORE;
+    if (vwl_gobjects.wl_compositor == NULL || vwl_gobjects.xdg_wm_base == NULL)
+       return FAIL;
+    if (buffer_store == NULL || seat == NULL)
+       return FAIL;
 
-       *supported |= WAYLAND_SELECTION_REGULAR;
-    }
+    store = alloc_clear(sizeof(*store));
 
-    if (req_sel == WAYLAND_SELECTION_PRIMARY
-           && !(*supported & WAYLAND_SELECTION_PRIMARY))
-       if (self->gobjects.zwp_primary_selection_device_manager_v1 != NULL)
-       {
-           manager->proxy =
-               self->gobjects.zwp_primary_selection_device_manager_v1;
-           manager->protocol = VWL_DATA_PROTOCOL_PRIMARY;
+    if (store == NULL)
+       return FAIL;
 
-           *supported |= WAYLAND_SELECTION_PRIMARY;
-       }
-#endif
+    // Get keyboard
+    store->keyboard = vwl_seat_get_keyboard(seat);
 
-    if (!(*supported & req_sel))
-    {
-       vim_free(manager);
-       return NULL;
-    }
+    if (store->keyboard == NULL)
+       goto fail;
 
-    return manager;
-}
+    wl_keyboard_add_listener(store->keyboard, &vwl_fs_keyboard_listener, store);
 
-    vwl_data_device_T *
-vwl_data_device_manager_get_data_device(
-       vwl_data_device_manager_T *self,
-       vwl_seat_T *seat)
-{
-    vwl_data_device_T *device = ALLOC_CLEAR_ONE(vwl_data_device_T);
+    if (vwl_display_dispatch(&vwl_display) == -1)
+       goto fail;
 
-    switch (self->protocol)
-    {
-       case VWL_DATA_PROTOCOL_EXT:
-           device->proxy = ext_data_control_manager_v1_get_data_device(
-                   self->proxy, seat->proxy);
-           break;
-       case VWL_DATA_PROTOCOL_WLR:
-           device->proxy = zwlr_data_control_manager_v1_get_data_device(
-                   self->proxy, seat->proxy);
-           break;
-#ifdef FEAT_WAYLAND_CLIPBOARD_FS
-       case VWL_DATA_PROTOCOL_CORE:
-           device->proxy = wl_data_device_manager_get_data_device(
-                   self->proxy, seat->proxy);
-           break;
-       case VWL_DATA_PROTOCOL_PRIMARY:
-           device->proxy = zwp_primary_selection_device_manager_v1_get_device(
-                   self->proxy, seat->proxy);
-           break;
-#endif
-       default:
-           vim_free(device);
-           return NULL;
-    }
-    device->protocol = self->protocol;
+    store->surface = wl_compositor_create_surface(vwl_gobjects.wl_compositor);
+    store->shell.surface = xdg_wm_base_get_xdg_surface(
+           vwl_gobjects.xdg_wm_base, store->surface);
+    store->shell.toplevel = xdg_surface_get_toplevel(store->shell.surface);
 
-    return device;
-}
+    xdg_toplevel_set_title(store->shell.toplevel, "Vim clipboard");
 
-    vwl_data_source_T *
-vwl_data_device_manager_create_data_source(vwl_data_device_manager_T *self)
-{
-    vwl_data_source_T *source = ALLOC_CLEAR_ONE(vwl_data_source_T);
+    xdg_surface_add_listener(store->shell.surface,
+           &vwl_xdg_surface_listener, NULL);
+
+    wl_surface_commit(store->surface);
+
+    store->on_focus = on_focus;
+    store->user_data = user_data;
+    store->got_focus = FALSE;
+
+    if (vwl_display_roundtrip(&vwl_display) == FAIL)
+       goto fail;
+
+    // We may get the enter event early, if we do then we will set `got_focus`
+    // to TRUE.
+    if (store->got_focus)
+       goto early_exit;
+
+    // Buffer hasn't been released yet, abort. This shouldn't happen but still
+    // check for it.
+    if (!buffer_store->available)
+       goto fail;
+
+    buffer_store->available = FALSE;
+
+    wl_surface_attach(store->surface, buffer_store->buffer, 0, 0);
+    wl_surface_damage(store->surface, 0, 0,
+           buffer_store->width, buffer_store->height);
+    wl_surface_commit(store->surface);
 
-    switch (self->protocol)
     {
-       case VWL_DATA_PROTOCOL_EXT:
-           source->proxy = ext_data_control_manager_v1_create_data_source(
-                   self->proxy);
-           break;
-       case VWL_DATA_PROTOCOL_WLR:
-           source->proxy = zwlr_data_control_manager_v1_create_data_source(
-                   self->proxy);
-           break;
-#ifdef FEAT_WAYLAND_CLIPBOARD_FS
-       case VWL_DATA_PROTOCOL_CORE:
-           source->proxy = wl_data_device_manager_create_data_source(
-                   self->proxy);
-           break;
-       case VWL_DATA_PROTOCOL_PRIMARY:
-           source->proxy =
-               zwp_primary_selection_device_manager_v1_create_source(
-                       self->proxy);
-           break;
-#endif
-       default:
-           vim_free(source);
-           return NULL;
+       // Dispatch events until we receive the enter event. Add a max delay of
+       // 'p_wtm' when waiting for it (may be longer depending on how long we
+       // poll when dispatching events)
+       struct timeval start, now;
+
+       gettimeofday(&start, NULL);
+
+       while (vwl_display_dispatch(&vwl_display) != -1)
+       {
+           if (store->got_focus)
+               break;
+
+           gettimeofday(&now, NULL);
+
+           if ((now.tv_sec * 1000000 + now.tv_usec) -
+                   (start.tv_sec * 1000000 + start.tv_usec)
+                   >= p_wtm * 1000)
+               goto fail;
+       }
     }
-    source->protocol = self->protocol;
+early_exit:
+    vwl_destroy_fs_surface(store);
+    vwl_display_flush(&vwl_display);
 
-    return source;
+    return OK;
+fail:
+    vwl_destroy_fs_surface(store);
+    vwl_display_flush(&vwl_display);
+
+    return FAIL;
 }
 
-    static vwl_data_offer_T *
-vwl_data_device_wrap_offer_proxy(vwl_data_device_T *self, void *proxy)
+/*
+ * Called when the keyboard focus is on our surface
+ */
+    static void
+vwl_fs_keyboard_listener_enter(
+    void               *data,
+    struct wl_keyboard *keyboard UNUSED,
+    uint32_t           serial,
+    struct wl_surface  *surface UNUSED,
+    struct wl_array    *keys UNUSED)
 {
-    vwl_data_offer_T *offer = ALLOC_CLEAR_ONE(vwl_data_offer_T);
-
-    if (offer == NULL)
-       return NULL;
+    vwl_fs_surface_T *store = data;
 
-    offer->proxy = proxy;
-    offer->protocol = self->protocol;
-    offer->data = self->data;
-    ga_init2(&offer->mime_types, sizeof(char *), 10);
+    store->got_focus = TRUE;
 
-    // Try pre allocating the array, 10 mime types seems to usually be the
-    // maximum from experience.
-    ga_grow(&offer->mime_types, 10);
-
-    return offer;
+    if (store->on_focus != NULL)
+       store->on_focus(store->user_data, serial);
 }
 
-#ifdef FEAT_WAYLAND_CLIPBOARD_FS
-# define VWL_CODE_DATA_PROXY_FS_DESTROY(type) \
-    case VWL_DATA_PROTOCOL_CORE: \
-       wl_data_##type##_destroy(self->proxy); \
-       break; \
-    case VWL_DATA_PROTOCOL_PRIMARY: \
-       zwp_primary_selection_##type##_v1_destroy(self->proxy); \
-       break;
-#else
-# define VWL_CODE_DATA_PROXY_FS_DESTROY(type)
-#endif
+// Dummy functions to handle keyboard events we don't care about.
 
-#define VWL_FUNC_DATA_PROXY_DESTROY(type) \
-       void \
-    vwl_data_##type##_destroy(vwl_data_##type##_T *self) \
-    { \
-       if (self == NULL) \
-           return; \
-       switch (self->protocol) \
-       { \
-           case VWL_DATA_PROTOCOL_EXT: \
-               ext_data_control_##type##_v1_destroy(self->proxy); \
-               break; \
-           case VWL_DATA_PROTOCOL_WLR: \
-               zwlr_data_control_##type##_v1_destroy(self->proxy); \
-               break; \
-           VWL_CODE_DATA_PROXY_FS_DESTROY(type) \
-           default: \
-               break; \
-       } \
-       vim_free(self); \
-    }
+    static void
+vwl_fs_keyboard_listener_keymap(
+    void               *data UNUSED,
+    struct wl_keyboard *keyboard UNUSED,
+    uint32_t           format UNUSED,
+    int                        fd,
+    uint32_t           size UNUSED)
+{
+    close(fd);
+}
 
-VWL_FUNC_DATA_PROXY_DESTROY(device)
-VWL_FUNC_DATA_PROXY_DESTROY(source)
+    static void
+vwl_fs_keyboard_listener_leave(
+    void               *data UNUSED,
+    struct wl_keyboard *keyboard UNUSED,
+    uint32_t           serial UNUSED,
+    struct wl_surface  *surface UNUSED)
+{
+}
 
-    void
-vwl_data_offer_destroy(vwl_data_offer_T *self)
+    static void
+vwl_fs_keyboard_listener_key(
+    void               *data UNUSED,
+    struct wl_keyboard *keyboard UNUSED,
+    uint32_t           serial UNUSED,
+    uint32_t           time UNUSED,
+    uint32_t           key UNUSED,
+    uint32_t           state UNUSED)
 {
-    if (self == NULL)
-       return;
-    switch (self->protocol)
-    {
-       case VWL_DATA_PROTOCOL_EXT:
-           ext_data_control_offer_v1_destroy(self->proxy);
-           break;
-       case VWL_DATA_PROTOCOL_WLR:
-           zwlr_data_control_offer_v1_destroy(self->proxy);
-           break;
-#ifdef FEAT_WAYLAND_CLIPBOARD_FS
-       case VWL_DATA_PROTOCOL_CORE:
-           wl_data_offer_destroy(self->proxy);
-           break;
-       case VWL_DATA_PROTOCOL_PRIMARY:
-           zwp_primary_selection_offer_v1_destroy(self->proxy);
-           break;
-#endif
-       default:
-           break;
-    }
-    ga_clear_strings(&self->mime_types);
-    vim_free(self);
 }
 
-/*
- * Doesn't destroy the actual global object proxy, only frees the structure.
- */
-void
-vwl_data_device_manager_discard(vwl_data_device_manager_T *self)
+    static void
+vwl_fs_keyboard_listener_modifiers(
+    void               *data UNUSED,
+    struct wl_keyboard *keyboard UNUSED,
+    uint32_t           serial UNUSED,
+    uint32_t           mods_depressed UNUSED,
+    uint32_t           mods_latched UNUSED,
+    uint32_t           mods_locked UNUSED,
+    uint32_t           group UNUSED)
 {
-    if (self == NULL)
-       return;
-    vim_free(self);
 }
 
-#define VWL_FUNC_DATA_DEVICE_EVENT_DATA_OFFER(device_type, offer_type) \
-       static void \
-    device_type##_listener_event_data_offer( \
-           void *data, \
-           struct device_type *device UNUSED, \
-           struct offer_type *offer) \
-    { \
-       vwl_data_device_T *self = data; \
-       self->offer = vwl_data_device_wrap_offer_proxy(self, offer); \
-       self->listener->data_offer(self->data, self, self->offer); \
-    }
+    static void
+vwl_fs_keyboard_listener_repeat_info(
+    void               *data UNUSED,
+    struct wl_keyboard *keyboard UNUSED,
+    int32_t            rate UNUSED,
+    int32_t            delay UNUSED)
+{
+}
 
-// We want to set the offer to NULL after the selection callback, because the
-// callback may free the offer, and we don't want a dangling pointer.
-#define VWL_FUNC_DATA_DEVICE_EVENT_SELECTION(device_type, offer_type) \
-       static void \
-    device_type##_listener_event_selection( \
-           void *data, \
-           struct device_type *device UNUSED, \
-           struct offer_type *offer UNUSED) \
+#define VWL_CODE_DATA_OBJECT_DESTROY(type) \
+do { \
+    if (type == NULL || type->proxy == NULL) \
+       return; \
+    switch (type->protocol) \
     { \
-       vwl_data_device_T *self = data; \
-       self->listener->selection(self->data, self, self->offer, \
-               WAYLAND_SELECTION_REGULAR); \
-       self->offer = NULL; \
+       case VWL_DATA_PROTOCOL_WLR: \
+           zwlr_data_control_##type##_v1_destroy(type->proxy); \
+           break; \
+       case VWL_DATA_PROTOCOL_EXT:  \
+           ext_data_control_##type##_v1_destroy(type->proxy); \
+           break; \
+       case VWL_DATA_PROTOCOL_CORE: \
+           wl_data_##type##_destroy(type->proxy); \
+           break; \
+       case VWL_DATA_PROTOCOL_PRIMARY: \
+           zwp_primary_selection_##type##_v1_destroy(type->proxy); \
+           break; \
+       default: \
+           break; \
     } \
+    if (alloced) \
+       vim_free(type); \
+    else \
+       type->proxy = NULL; \
+} while (0)
 
-#define VWL_FUNC_DATA_DEVICE_EVENT_PRIMARY_SELECTION(device_type, offer_type) \
-       static void \
-    device_type##_listener_event_primary_selection( \
-           void *data, \
-           struct device_type *device UNUSED, \
-           struct offer_type *offer UNUSED) \
-    { \
-       vwl_data_device_T *self = data; \
-       self->listener->selection(self->data, self, self->offer, \
-               WAYLAND_SELECTION_PRIMARY); \
-       self->offer = NULL; \
-    }
+    static void
+vwl_data_device_destroy(vwl_data_device_T *device, int alloced)
+{
+    VWL_CODE_DATA_OBJECT_DESTROY(device);
+}
 
-#define VWL_FUNC_DATA_DEVICE_EVENT_FINISHED(device_type) \
-       static void \
-    device_type##_listener_event_finished( \
-           void *data, \
-           struct device_type *device UNUSED) \
-    { \
-       vwl_data_device_T *self = data; \
-       self->listener->finished(self->data, self); \
-    }
+    static void
+vwl_data_offer_destroy(vwl_data_offer_T *offer, int alloced)
+{
+    VWL_CODE_DATA_OBJECT_DESTROY(offer);
+}
 
-VWL_FUNC_DATA_DEVICE_EVENT_DATA_OFFER(
-    ext_data_control_device_v1, ext_data_control_offer_v1)
-VWL_FUNC_DATA_DEVICE_EVENT_DATA_OFFER(
-    zwlr_data_control_device_v1, zwlr_data_control_offer_v1)
-#ifdef FEAT_WAYLAND_CLIPBOARD_FS
-VWL_FUNC_DATA_DEVICE_EVENT_DATA_OFFER(wl_data_device, wl_data_offer)
-VWL_FUNC_DATA_DEVICE_EVENT_DATA_OFFER(
-       zwp_primary_selection_device_v1, zwp_primary_selection_offer_v1)
-#endif
+    static void
+vwl_data_source_destroy(vwl_data_source_T *source, int alloced)
+{
+    VWL_CODE_DATA_OBJECT_DESTROY(source);
+}
 
-VWL_FUNC_DATA_DEVICE_EVENT_SELECTION(
-    ext_data_control_device_v1, ext_data_control_offer_v1
-)
-VWL_FUNC_DATA_DEVICE_EVENT_SELECTION(
-    zwlr_data_control_device_v1, zwlr_data_control_offer_v1
-)
-VWL_FUNC_DATA_DEVICE_EVENT_PRIMARY_SELECTION(
-    ext_data_control_device_v1, ext_data_control_offer_v1
-)
-VWL_FUNC_DATA_DEVICE_EVENT_PRIMARY_SELECTION(
-    zwlr_data_control_device_v1, zwlr_data_control_offer_v1
-)
-#ifdef FEAT_WAYLAND_CLIPBOARD_FS
-VWL_FUNC_DATA_DEVICE_EVENT_SELECTION(
-    wl_data_device, wl_data_offer
-)
-VWL_FUNC_DATA_DEVICE_EVENT_PRIMARY_SELECTION(
-       zwp_primary_selection_device_v1, zwp_primary_selection_offer_v1)
-#endif
 
-VWL_FUNC_DATA_DEVICE_EVENT_FINISHED(ext_data_control_device_v1)
-VWL_FUNC_DATA_DEVICE_EVENT_FINISHED(zwlr_data_control_device_v1)
+// Used to pass a vwl_data_offer_T struct from the data_offer event to the offer
+// event and to the selection event.
+static vwl_data_offer_T *tmp_vwl_offer;
 
-static struct ext_data_control_device_v1_listener
-    ext_data_control_device_v1_listener = {
-       .data_offer = ext_data_control_device_v1_listener_event_data_offer,
-       .selection = ext_data_control_device_v1_listener_event_selection,
-       .primary_selection =
-           ext_data_control_device_v1_listener_event_primary_selection,
-       .finished = ext_data_control_device_v1_listener_event_finished
-};
-static const struct zwlr_data_control_device_v1_listener
-    zwlr_data_control_device_v1_listener = {
-       .data_offer = zwlr_data_control_device_v1_listener_event_data_offer,
-       .selection = zwlr_data_control_device_v1_listener_event_selection,
-       .primary_selection =
-           zwlr_data_control_device_v1_listener_event_primary_selection,
-       .finished = zwlr_data_control_device_v1_listener_event_finished
-};
-#ifdef FEAT_WAYLAND_CLIPBOARD_FS
-static const struct wl_data_device_listener wl_data_device_listener = {
-    .data_offer = wl_data_device_listener_event_data_offer,
-    .selection = wl_data_device_listener_event_selection,
-};
-static const struct zwp_primary_selection_device_v1_listener
-    zwp_primary_selection_device_v1_listener = {
-       .data_offer = zwp_primary_selection_device_v1_listener_event_data_offer,
-       .selection =
-           zwp_primary_selection_device_v1_listener_event_primary_selection,
-};
-#  endif
+// These functions handle the more complicated data_offer and selection events.
 
-#  define VWL_FUNC_DATA_SOURCE_EVENT_SEND(source_type) \
-       static void \
-    source_type##_listener_event_send( \
-           void *data, struct source_type *source UNUSED, \
-           const char *mime_type, int fd) \
-    { \
-       vwl_data_source_T *self = data; \
-       self->listener->send(self->data, self, mime_type, fd); \
+    static void
+vwl_gen_data_device_listener_data_offer(void *data, void *offer_proxy)
+{
+    vwl_data_device_T *device = data;
+
+    tmp_vwl_offer = alloc(sizeof(*tmp_vwl_offer));
+
+    if (tmp_vwl_offer != NULL)
+    {
+       tmp_vwl_offer->proxy = offer_proxy;
+       tmp_vwl_offer->protocol = device->protocol;
+
+       vwl_data_device_listener.data_offer(device, tmp_vwl_offer);
     }
+}
 
-#  define VWL_FUNC_DATA_SOURCE_EVENT_CANCELLED(source_type) \
-       static void \
-    source_type##_listener_event_cancelled( \
-           void *data, struct source_type *source UNUSED) \
-    { \
-       vwl_data_source_T *self = data; \
-       self->listener->cancelled(self->data, self); \
+    static void
+vwl_gen_data_device_listener_selection(
+       void                *data,
+       void                *offer_proxy,
+       wayland_selection_T selection,
+       vwl_data_protocol_T protocol)
+{
+    if (tmp_vwl_offer == NULL)
+    {
+       // Memory allocation failed or selection cleared (data_offer is never
+       // sent when selection is cleared/empty).
+       vwl_data_offer_T tmp = {
+           .proxy = offer_proxy,
+           .protocol = protocol
+       };
+
+       vwl_data_offer_destroy(&tmp, FALSE);
+
+       // If offer proxy is NULL then we know the selection has been cleared.
+       if (offer_proxy == NULL)
+           vwl_data_device_listener.selection(data, NULL, selection);
+    }
+    else
+    {
+       vwl_data_device_listener.selection(data, tmp_vwl_offer, selection);
+       tmp_vwl_offer = NULL;
     }
+}
 
-VWL_FUNC_DATA_SOURCE_EVENT_SEND(ext_data_control_source_v1)
-VWL_FUNC_DATA_SOURCE_EVENT_SEND(zwlr_data_control_source_v1)
-#ifdef FEAT_WAYLAND_CLIPBOARD_FS
-VWL_FUNC_DATA_SOURCE_EVENT_SEND(wl_data_source)
-VWL_FUNC_DATA_SOURCE_EVENT_SEND(zwp_primary_selection_source_v1)
-#endif
+// Boilerplate macros. Each just calls its respective generic callback.
+//
+#define VWL_FUNC_DATA_DEVICE_DATA_OFFER(device_name, offer_name) \
+    static void device_name##_listener_data_offer( \
+           void *data, struct device_name *device_proxy UNUSED, \
+           struct offer_name *offer_proxy) \
+{ \
+    vwl_gen_data_device_listener_data_offer(data, offer_proxy); \
+}
+#define VWL_FUNC_DATA_DEVICE_SELECTION( \
+       device_name, offer_name, type, selection_type, protocol) \
+       static void device_name##_listener_##type( \
+               void *data, struct device_name *device_proxy UNUSED, \
+               struct offer_name *offer_proxy UNUSED) \
+{ \
+    vwl_gen_data_device_listener_selection( \
+           data, offer_proxy, selection_type, protocol); \
+}
+#define VWL_FUNC_DATA_DEVICE_FINISHED(device_name) \
+    static void device_name##_listener_finished( \
+           void *data, struct device_name *device_proxy UNUSED) \
+{ \
+    vwl_data_device_listener.finished(data); \
+}
+#define VWL_FUNC_DATA_SOURCE_SEND(source_name) \
+    static void source_name##_listener_send(void *data, \
+           struct source_name *source_proxy UNUSED, \
+           const char *mime_type, int fd) \
+{ \
+    vwl_data_source_listener.send(data, mime_type, fd); \
+}
+#define VWL_FUNC_DATA_SOURCE_CANCELLED(source_name) \
+    static void source_name##_listener_cancelled(void *data, \
+           struct source_name *source_proxy UNUSED) \
+{ \
+    vwl_data_source_listener.cancelled(data); \
+}
+#define VWL_FUNC_DATA_OFFER_OFFER(offer_name) \
+    static void offer_name##_listener_offer(void *data, \
+           struct offer_name *offer_proxy UNUSED, \
+           const char *mime_type) \
+{ \
+    vwl_data_offer_listener.offer(data, mime_type); \
+}
 
-VWL_FUNC_DATA_SOURCE_EVENT_CANCELLED(ext_data_control_source_v1)
-VWL_FUNC_DATA_SOURCE_EVENT_CANCELLED(zwlr_data_control_source_v1)
-#ifdef FEAT_WAYLAND_CLIPBOARD_FS
-VWL_FUNC_DATA_SOURCE_EVENT_CANCELLED(wl_data_source)
-VWL_FUNC_DATA_SOURCE_EVENT_CANCELLED(zwp_primary_selection_source_v1)
-#endif
+VWL_FUNC_DATA_DEVICE_DATA_OFFER(
+       ext_data_control_device_v1, ext_data_control_offer_v1)
+VWL_FUNC_DATA_DEVICE_DATA_OFFER(
+       zwlr_data_control_device_v1, zwlr_data_control_offer_v1)
+VWL_FUNC_DATA_DEVICE_DATA_OFFER(wl_data_device, wl_data_offer)
+VWL_FUNC_DATA_DEVICE_DATA_OFFER(
+       zwp_primary_selection_device_v1, zwp_primary_selection_offer_v1)
 
-static const struct ext_data_control_source_v1_listener
-    ext_data_control_source_v1_listener = {
-       .send = ext_data_control_source_v1_listener_event_send,
-       .cancelled = ext_data_control_source_v1_listener_event_cancelled
+VWL_FUNC_DATA_DEVICE_SELECTION(
+       ext_data_control_device_v1, ext_data_control_offer_v1,
+       selection, WAYLAND_SELECTION_REGULAR, VWL_DATA_PROTOCOL_EXT)
+VWL_FUNC_DATA_DEVICE_SELECTION(
+       zwlr_data_control_device_v1, zwlr_data_control_offer_v1,
+       selection, WAYLAND_SELECTION_REGULAR, VWL_DATA_PROTOCOL_WLR)
+VWL_FUNC_DATA_DEVICE_SELECTION(
+       wl_data_device, wl_data_offer, selection,
+       WAYLAND_SELECTION_REGULAR, VWL_DATA_PROTOCOL_CORE)
+
+VWL_FUNC_DATA_DEVICE_SELECTION(
+       ext_data_control_device_v1, ext_data_control_offer_v1,
+       primary_selection, WAYLAND_SELECTION_PRIMARY, VWL_DATA_PROTOCOL_EXT)
+VWL_FUNC_DATA_DEVICE_SELECTION(
+       zwlr_data_control_device_v1, zwlr_data_control_offer_v1,
+       primary_selection, WAYLAND_SELECTION_PRIMARY, VWL_DATA_PROTOCOL_WLR)
+VWL_FUNC_DATA_DEVICE_SELECTION(
+       zwp_primary_selection_device_v1, zwp_primary_selection_offer_v1,
+       primary_selection, WAYLAND_SELECTION_PRIMARY, VWL_DATA_PROTOCOL_PRIMARY)
+
+VWL_FUNC_DATA_DEVICE_FINISHED(ext_data_control_device_v1)
+VWL_FUNC_DATA_DEVICE_FINISHED(zwlr_data_control_device_v1)
+
+VWL_FUNC_DATA_SOURCE_SEND(ext_data_control_source_v1)
+VWL_FUNC_DATA_SOURCE_SEND(zwlr_data_control_source_v1)
+VWL_FUNC_DATA_SOURCE_SEND(wl_data_source)
+VWL_FUNC_DATA_SOURCE_SEND(zwp_primary_selection_source_v1)
+
+VWL_FUNC_DATA_SOURCE_CANCELLED(ext_data_control_source_v1)
+VWL_FUNC_DATA_SOURCE_CANCELLED(zwlr_data_control_source_v1)
+VWL_FUNC_DATA_SOURCE_CANCELLED(wl_data_source)
+VWL_FUNC_DATA_SOURCE_CANCELLED(zwp_primary_selection_source_v1)
+
+VWL_FUNC_DATA_OFFER_OFFER(ext_data_control_offer_v1)
+VWL_FUNC_DATA_OFFER_OFFER(zwlr_data_control_offer_v1)
+VWL_FUNC_DATA_OFFER_OFFER(wl_data_offer)
+VWL_FUNC_DATA_OFFER_OFFER(zwp_primary_selection_offer_v1)
+
+// Listener handlers
+
+// DATA DEVICES
+struct zwlr_data_control_device_v1_listener
+zwlr_data_control_device_v1_listener = {
+    .data_offer            = zwlr_data_control_device_v1_listener_data_offer,
+    .selection     = zwlr_data_control_device_v1_listener_selection,
+    .primary_selection = zwlr_data_control_device_v1_listener_primary_selection,
+    .finished      = zwlr_data_control_device_v1_listener_finished
 };
-static const struct zwlr_data_control_source_v1_listener
-    zwlr_data_control_source_v1_listener = {
-       .send = zwlr_data_control_source_v1_listener_event_send,
-       .cancelled = zwlr_data_control_source_v1_listener_event_cancelled
+
+struct ext_data_control_device_v1_listener
+ext_data_control_device_v1_listener = {
+    .data_offer            = ext_data_control_device_v1_listener_data_offer,
+    .selection     = ext_data_control_device_v1_listener_selection,
+    .primary_selection = ext_data_control_device_v1_listener_primary_selection,
+    .finished      = ext_data_control_device_v1_listener_finished
 };
-#ifdef FEAT_WAYLAND_CLIPBOARD_FS
-static const struct wl_data_source_listener wl_data_source_listener = {
-    .send = wl_data_source_listener_event_send,
-    .cancelled = wl_data_source_listener_event_cancelled
+
+struct wl_data_device_listener wl_data_device_listener = {
+    .data_offer            = wl_data_device_listener_data_offer,
+    .selection     = wl_data_device_listener_selection,
 };
-static const struct zwp_primary_selection_source_v1_listener
-    zwp_primary_selection_source_v1_listener = {
-       .send = zwp_primary_selection_source_v1_listener_event_send,
-       .cancelled = zwp_primary_selection_source_v1_listener_event_cancelled
+
+struct zwp_primary_selection_device_v1_listener
+zwp_primary_selection_device_v1_listener = {
+    .selection = zwp_primary_selection_device_v1_listener_primary_selection,
+    .data_offer            = zwp_primary_selection_device_v1_listener_data_offer
 };
-#endif
 
-#define VWL_FUNC_DATA_OFFER_EVENT_OFFER(offer_type) \
-       static void \
-    offer_type##_listener_event_offer( \
-           void *data, \
-           struct offer_type *offer UNUSED, \
-           const char *mime_type) \
-    { \
-       vwl_data_offer_T *self = data; \
-       if (STRCMP(mime_type, wayland_vim_special_mime) == 0) \
-           self->from_vim = true; \
-       else if (!self->from_vim && \
-               self->listener->offer(self->data, self, mime_type)) \
-       { \
-           char *mime = (char *)vim_strsave((char_u *)mime_type); \
-           if (ga_grow(&self->mime_types, 1) == FAIL) \
-               vim_free(mime); \
-           else \
-               if (mime != NULL) \
-                   ((char **)self->mime_types.ga_data) \
-                                       [self->mime_types.ga_len++] = mime; \
-       } \
-    }
+// DATA SOURCES
+struct zwlr_data_control_source_v1_listener
+zwlr_data_control_source_v1_listener = {
+    .send          = zwlr_data_control_source_v1_listener_send,
+    .cancelled     = zwlr_data_control_source_v1_listener_cancelled
+};
 
-VWL_FUNC_DATA_OFFER_EVENT_OFFER(ext_data_control_offer_v1)
-VWL_FUNC_DATA_OFFER_EVENT_OFFER(zwlr_data_control_offer_v1)
-#ifdef FEAT_WAYLAND_CLIPBOARD_FS
-VWL_FUNC_DATA_OFFER_EVENT_OFFER(wl_data_offer)
-VWL_FUNC_DATA_OFFER_EVENT_OFFER(zwp_primary_selection_offer_v1)
-#endif
+struct ext_data_control_source_v1_listener
+ext_data_control_source_v1_listener = {
+    .send          = ext_data_control_source_v1_listener_send,
+    .cancelled     = ext_data_control_source_v1_listener_cancelled
+};
+
+struct wl_data_source_listener wl_data_source_listener = {
+    .send          = wl_data_source_listener_send,
+    .cancelled     = wl_data_source_listener_cancelled
+};
 
-static const struct ext_data_control_offer_v1_listener
-    ext_data_control_offer_v1_listener = {
-       .offer = ext_data_control_offer_v1_listener_event_offer
+struct zwp_primary_selection_source_v1_listener
+zwp_primary_selection_source_v1_listener = {
+    .send          = zwp_primary_selection_source_v1_listener_send,
+    .cancelled     = zwp_primary_selection_source_v1_listener_cancelled,
 };
-static const struct zwlr_data_control_offer_v1_listener
-    zwlr_data_control_offer_v1_listener = {
-       .offer = zwlr_data_control_offer_v1_listener_event_offer
+
+// OFFERS
+struct zwlr_data_control_offer_v1_listener
+zwlr_data_control_offer_v1_listener = {
+    .offer         = zwlr_data_control_offer_v1_listener_offer
 };
-#ifdef FEAT_WAYLAND_CLIPBOARD_FS
-static const struct wl_data_offer_listener
-    wl_data_offer_listener = {
-       .offer = wl_data_offer_listener_event_offer
+
+struct ext_data_control_offer_v1_listener
+ext_data_control_offer_v1_listener = {
+    .offer         = ext_data_control_offer_v1_listener_offer
 };
-static const struct zwp_primary_selection_offer_v1_listener
-    zwp_primary_selection_offer_v1_listener = {
-       .offer = zwp_primary_selection_offer_v1_listener_event_offer
+
+struct wl_data_offer_listener wl_data_offer_listener = {
+    .offer         = wl_data_offer_listener_offer
 };
-#endif
 
-#ifdef FEAT_WAYLAND_CLIPBOARD_FS
-# define VWL_CODE_DATA_PROXY_FS_ADD_LISTENER(type) \
-    case VWL_DATA_PROTOCOL_CORE: \
-       wl_data_##type##_add_listener(self->proxy, \
-               &wl_data_##type##_listener, self); \
-       break; \
-    case VWL_DATA_PROTOCOL_PRIMARY: \
-       zwp_primary_selection_##type##_v1_add_listener(self->proxy, \
-               &zwp_primary_selection_##type##_v1_listener, self); \
-       break;
-#else
-# define VWL_CODE_DATA_PROXY_FS_ADD_LISTENER(type)
-#endif
+struct zwp_primary_selection_offer_v1_listener
+zwp_primary_selection_offer_v1_listener = {
+    .offer         = zwp_primary_selection_offer_v1_listener_offer
+};
 
-#define VWL_FUNC_DATA_PROXY_ADD_LISTENER(type) \
-       void \
-    vwl_data_##type##_add_listener( \
-           vwl_data_##type##_T *self, \
-           const vwl_data_##type##_listener_T *listener, \
-           void *data) \
+// `type` is also used as the user data
+#define VWL_CODE_DATA_OBJECT_ADD_LISTENER(type) \
+do { \
+    if (type->proxy == NULL) \
+       return; \
+    type->data = data; \
+    switch (type->protocol) \
     { \
-       if (self == NULL) \
-           return; \
-       self->data = data; \
-       self->listener = listener; \
-       switch (self->protocol) \
-       { \
-           case VWL_DATA_PROTOCOL_EXT: \
-               ext_data_control_##type##_v1_add_listener(self->proxy, \
-                       &ext_data_control_##type##_v1_listener, self); \
-               break; \
-           case VWL_DATA_PROTOCOL_WLR: \
-               zwlr_data_control_##type##_v1_add_listener(self->proxy, \
-                       &zwlr_data_control_##type##_v1_listener, self); \
-               break; \
-           VWL_CODE_DATA_PROXY_FS_ADD_LISTENER(type) \
-           default: \
-               break; \
-       } \
-    }
+       case VWL_DATA_PROTOCOL_WLR: \
+           zwlr_data_control_##type##_v1_add_listener( type->proxy, \
+                   &zwlr_data_control_##type##_v1_listener, type); \
+           break; \
+       case VWL_DATA_PROTOCOL_EXT: \
+           ext_data_control_##type##_v1_add_listener(type->proxy, \
+                   &ext_data_control_##type##_v1_listener, type); \
+           break; \
+       case VWL_DATA_PROTOCOL_CORE: \
+           wl_data_##type##_add_listener(type->proxy, \
+                   &wl_data_##type##_listener, type); \
+           break; \
+       case VWL_DATA_PROTOCOL_PRIMARY: \
+           zwp_primary_selection_##type##_v1_add_listener(type->proxy, \
+                   &zwp_primary_selection_##type##_v1_listener, type); \
+           break; \
+       default: \
+           break; \
+    } \
+} while (0)
+
+    static void
+vwl_data_device_add_listener(vwl_data_device_T *device, void *data)
+{
+    VWL_CODE_DATA_OBJECT_ADD_LISTENER(device);
+}
+
+    static void
+vwl_data_source_add_listener(vwl_data_source_T *source, void *data)
+{
+    VWL_CODE_DATA_OBJECT_ADD_LISTENER(source);
+}
 
-VWL_FUNC_DATA_PROXY_ADD_LISTENER(device)
-VWL_FUNC_DATA_PROXY_ADD_LISTENER(source)
-VWL_FUNC_DATA_PROXY_ADD_LISTENER(offer)
+    static void
+vwl_data_offer_add_listener(vwl_data_offer_T *offer, void *data)
+{
+    VWL_CODE_DATA_OBJECT_ADD_LISTENER(offer);
+}
 
 /*
- * Set the given selection to source. If a data control protocol is being used,
- * "serial" is ignored.
+ * Sets the selection using the given data device with the given selection. If
+ * the device does not support the selection then nothing happens. For data
+ * control protocols the serial argument is ignored.
  */
-    void
+    static void
 vwl_data_device_set_selection(
-    vwl_data_device_T *self,
-    vwl_data_source_T *source,
-    uint32_t serial UNUSED,
-    wayland_selection_T selection
-)
+       vwl_data_device_T   *device,
+       vwl_data_source_T   *source,
+       uint32_t            serial,
+       wayland_selection_T selection)
 {
-    void *proxy = source == NULL ? NULL : source->proxy;
-
     if (selection == WAYLAND_SELECTION_REGULAR)
     {
-       switch (self->protocol)
+       switch (device->protocol)
        {
-           case VWL_DATA_PROTOCOL_EXT:
-               ext_data_control_device_v1_set_selection(self->proxy, proxy);
-               break;
            case VWL_DATA_PROTOCOL_WLR:
-               zwlr_data_control_device_v1_set_selection(self->proxy, proxy);
+               zwlr_data_control_device_v1_set_selection(
+                       device->proxy, source->proxy);
+               break;
+           case VWL_DATA_PROTOCOL_EXT:
+               ext_data_control_device_v1_set_selection(
+                       device->proxy, source->proxy);
                break;
-#ifdef FEAT_WAYLAND_CLIPBOARD_FS
            case VWL_DATA_PROTOCOL_CORE:
-               wl_data_device_set_selection(self->proxy, proxy, serial);
+               wl_data_device_set_selection(
+                       device->proxy, source->proxy, serial);
                break;
-#endif
            default:
                break;
        }
     }
     else if (selection == WAYLAND_SELECTION_PRIMARY)
     {
-       switch (self->protocol)
+       switch (device->protocol)
        {
-           case VWL_DATA_PROTOCOL_EXT:
-               ext_data_control_device_v1_set_primary_selection(
-                       self->proxy, proxy
-                       );
-               break;
            case VWL_DATA_PROTOCOL_WLR:
                zwlr_data_control_device_v1_set_primary_selection(
-                       self->proxy, proxy
-                       );
+                       device->proxy, source->proxy);
+               break;
+           case VWL_DATA_PROTOCOL_EXT:
+               ext_data_control_device_v1_set_primary_selection(
+                       device->proxy, source->proxy);
                break;
-#ifdef FEAT_WAYLAND_CLIPBOARD_FS
            case VWL_DATA_PROTOCOL_PRIMARY:
                zwp_primary_selection_device_v1_set_selection(
-                       self->proxy, proxy, serial);
+                       device->proxy, source->proxy, serial);
                break;
-#endif
            default:
                break;
        }
     }
 }
 
-    void
-vwl_data_source_offer(vwl_data_source_T *self, const char *mime_type)
+/*
+ * Start receiving data from offer object, which sends the given fd to the
+ * source client to write into.
+ */
+    static void
+vwl_data_offer_receive(vwl_data_offer_T *offer, const char *mime_type, int fd)
 {
-    switch (self->protocol)
+    switch (offer->protocol)
     {
-       case VWL_DATA_PROTOCOL_EXT:
-           ext_data_control_source_v1_offer(self->proxy, mime_type);
-           break;
        case VWL_DATA_PROTOCOL_WLR:
-           zwlr_data_control_source_v1_offer(self->proxy, mime_type);
+           zwlr_data_control_offer_v1_receive(offer->proxy, mime_type, fd);
+           break;
+       case VWL_DATA_PROTOCOL_EXT:
+           ext_data_control_offer_v1_receive(offer->proxy, mime_type, fd);
            break;
-#ifdef FEAT_WAYLAND_CLIPBOARD_FS
        case VWL_DATA_PROTOCOL_CORE:
-           wl_data_source_offer(self->proxy, mime_type);
+           wl_data_offer_receive(offer->proxy, mime_type, fd);
            break;
        case VWL_DATA_PROTOCOL_PRIMARY:
-           zwp_primary_selection_source_v1_offer(self->proxy, mime_type);
+           zwp_primary_selection_offer_v1_receive(offer->proxy, mime_type, fd);
            break;
-#endif
        default:
            break;
     }
 }
 
-    void
-vwl_data_offer_receive(
-       vwl_data_offer_T *self,
-       const char *mime_type,
-       int32_t fd)
+#define SET_MANAGER(manager_name, protocol_enum, focus) \
+    do { \
+       manager->proxy = vwl_gobjects.manager_name; \
+       manager->protocol = protocol_enum; \
+       return focus; \
+    } while (0)
+
+/*
+ * Get a data device manager that supports the given selection. If none if found
+ * then the manager protocol is set to VWL_DATA_PROTOCOL_NONE. TRUE is returned
+ * if the given data device manager requires focus to work else FALSE.
+ */
+    static int
+vwl_get_data_device_manager(
+       vwl_data_device_manager_T   *manager,
+       wayland_selection_T         selection)
 {
-    switch (self->protocol)
+    // Prioritize data control protocols first then try using the focus steal
+    // method with the core protocol data objects.
+    if (force_fs)
+       goto focus_steal;
+
+    // Ext data control protocol supports both selections, try it first
+    if (vwl_gobjects.ext_data_control_manager_v1 != NULL)
+       SET_MANAGER(ext_data_control_manager_v1, VWL_DATA_PROTOCOL_EXT, FALSE);
+    if (vwl_gobjects.zwlr_data_control_manager_v1 != NULL)
+    {
+       int ver = zwlr_data_control_manager_v1_get_version(
+               vwl_gobjects.zwlr_data_control_manager_v1);
+
+       // version 2 or greater supports the primary selection
+       if ((selection == WAYLAND_SELECTION_PRIMARY && ver >= 2)
+               || selection == WAYLAND_SELECTION_REGULAR)
+           SET_MANAGER(zwlr_data_control_manager_v1,
+                   VWL_DATA_PROTOCOL_WLR, FALSE);
+    }
+
+focus_steal:
+    if (vwl_focus_stealing_available())
+    {
+       if (vwl_gobjects.wl_data_device_manager != NULL
+               && selection == WAYLAND_SELECTION_REGULAR)
+           SET_MANAGER(wl_data_device_manager, VWL_DATA_PROTOCOL_CORE, TRUE);
+
+       else if (vwl_gobjects.zwp_primary_selection_device_manager_v1 != NULL
+               && selection == WAYLAND_SELECTION_PRIMARY)
+           SET_MANAGER(zwp_primary_selection_device_manager_v1,
+                   VWL_DATA_PROTOCOL_PRIMARY, TRUE);
+    }
+
+    manager->protocol = VWL_DATA_PROTOCOL_NONE;
+
+    return FALSE;
+}
+
+/*
+ * Get a data device that manages the given seat's selection.
+ */
+    static void
+vwl_get_data_device(
+       vwl_data_device_manager_T   *manager,
+       vwl_seat_T                  *seat,
+       vwl_data_device_T           *device)
+{
+    switch (manager->protocol)
     {
-       case VWL_DATA_PROTOCOL_EXT:
-           ext_data_control_offer_v1_receive(self->proxy, mime_type, fd);
-           break;
        case VWL_DATA_PROTOCOL_WLR:
-           zwlr_data_control_offer_v1_receive(self->proxy, mime_type, fd);
+           device->proxy =
+               zwlr_data_control_manager_v1_get_data_device(
+                       manager->proxy, seat->proxy);
+           break;
+       case VWL_DATA_PROTOCOL_EXT:
+           device->proxy =
+               ext_data_control_manager_v1_get_data_device(
+                       manager->proxy, seat->proxy);
            break;
-#ifdef FEAT_WAYLAND_CLIPBOARD_FS
        case VWL_DATA_PROTOCOL_CORE:
-           wl_data_offer_receive(self->proxy, mime_type, fd);
+           device->proxy = wl_data_device_manager_get_data_device(
+                   manager->proxy, seat->proxy);
            break;
        case VWL_DATA_PROTOCOL_PRIMARY:
-           zwp_primary_selection_offer_v1_receive(self->proxy, mime_type, fd);
+           device->proxy = zwp_primary_selection_device_manager_v1_get_device(
+                   manager->proxy, seat->proxy);
            break;
-#endif
        default:
-           break;
+           device->protocol = VWL_DATA_PROTOCOL_NONE;
+           return;
     }
+    device->protocol = manager->protocol;
 }
 
-#endif // FEAT_WAYLAND_CLIPBOARD
+/*
+ * Create a data source
+ */
+    static void
+vwl_create_data_source(
+       vwl_data_device_manager_T   *manager,
+       vwl_data_source_T           *source)
+{
+    switch (manager->protocol)
+    {
+       case VWL_DATA_PROTOCOL_WLR:
+           source->proxy =
+               zwlr_data_control_manager_v1_create_data_source(manager->proxy);
+           break;
+       case VWL_DATA_PROTOCOL_EXT:
+           source->proxy =
+               ext_data_control_manager_v1_create_data_source(manager->proxy);
+           break;
+       case VWL_DATA_PROTOCOL_CORE:
+           source->proxy =
+               wl_data_device_manager_create_data_source(manager->proxy);
+           break;
+       case VWL_DATA_PROTOCOL_PRIMARY:
+           source->proxy =
+               zwp_primary_selection_device_manager_v1_create_source(
+                       manager->proxy);
+           break;
+       default:
+           source->protocol = VWL_DATA_PROTOCOL_NONE;
+           return;
+    }
+    source->protocol = manager->protocol;
+}
+
+/*
+ * Offer a new mime type to be advertised by us to other clients.
+ */
+    static void
+vwl_data_source_offer(vwl_data_source_T *source, const char *mime_type)
+{
+    switch (source->protocol)
+    {
+       case VWL_DATA_PROTOCOL_WLR:
+           zwlr_data_control_source_v1_offer(source->proxy, mime_type);
+           break;
+       case VWL_DATA_PROTOCOL_EXT:
+           ext_data_control_source_v1_offer(source->proxy, mime_type);
+           break;
+       case VWL_DATA_PROTOCOL_CORE:
+           wl_data_source_offer(source->proxy, mime_type);
+           break;
+       case VWL_DATA_PROTOCOL_PRIMARY:
+           zwp_primary_selection_source_v1_offer(source->proxy, mime_type);
+           break;
+       default:
+           break;
+    }
+}
+
+/*
+ * Free the mime types grow arrays in the given clip_sel struct.
+ */
+    static void
+vwl_clipboard_free_mime_types(vwl_clipboard_selection_T *clip_sel)
+{
+    // Don't want to be double freeing
+    if (clip_sel->mime_types.ga_data == clip_sel->tmp_mime_types.ga_data)
+    {
+       ga_clear_strings(&clip_sel->mime_types);
+       ga_init(&vwl_clipboard.primary.tmp_mime_types);
+    }
+    else
+    {
+       ga_clear_strings(&clip_sel->mime_types);
+       ga_clear_strings(&clip_sel->tmp_mime_types);
+    }
+}
+
+/*
+ * Setup required objects to interact with Wayland selections/clipboard on given
+ * seat. Returns OK on success and FAIL on failure.
+ */
+    int
+wayland_cb_init(const char *seat)
+{
+    vwl_clipboard.seat = vwl_get_seat(seat);
+
+    if (vwl_clipboard.seat == NULL)
+       return FAIL;
+
+    // Get data device managers for each selection. If there wasn't any manager
+    // that could be found that supports the given selection, then it will be
+    // unavailable.
+    vwl_clipboard.regular.requires_focus = vwl_get_data_device_manager(
+           &vwl_clipboard.regular.manager,
+           WAYLAND_SELECTION_REGULAR);
+    vwl_clipboard.primary.requires_focus = vwl_get_data_device_manager(
+           &vwl_clipboard.primary.manager,
+           WAYLAND_SELECTION_PRIMARY);
+
+    // Initialize shm pool and buffer if core data protocol is available
+    if (vwl_focus_stealing_available() &&
+           (vwl_clipboard.regular.requires_focus ||
+            vwl_clipboard.primary.requires_focus))
+       vwl_clipboard.fs_buffer = vwl_init_buffer_store(1, 1);
+
+    // Get data devices for each selection. If one of the above function calls
+    // results in an unavailable manager, then the device coming from it will
+    // have its protocol set to VWL_DATA_PROTOCOL_NONE.
+    vwl_get_data_device(
+           &vwl_clipboard.regular.manager,
+           vwl_clipboard.seat,
+           &vwl_clipboard.regular.device);
+    vwl_get_data_device(
+           &vwl_clipboard.primary.manager,
+           vwl_clipboard.seat,
+           &vwl_clipboard.primary.device);
+
+    // Initialize grow arrays for the offer mime types.
+    // I find most applications to have below 10 mime types that they offer.
+    ga_init2(&vwl_clipboard.regular.tmp_mime_types, sizeof(char*), 10);
+    ga_init2(&vwl_clipboard.primary.tmp_mime_types, sizeof(char*), 10);
+
+    // We dont need to use ga_init2 because tmp_mime_types will be copied over
+    // to mime_types anyways.
+    ga_init(&vwl_clipboard.regular.mime_types);
+    ga_init(&vwl_clipboard.primary.mime_types);
+
+    // Start listening for data offers/new selections. Don't do anything when we
+    // get a new data offer other than saving the mime types and saving the data
+    // offer. Then when we want the data we use the saved data offer to receive
+    // data from it along with the saved mime_types. For each new selection just
+    // destroy the previous offer/free mime_types, if any.
+    vwl_data_device_add_listener(
+           &vwl_clipboard.regular.device,
+           &vwl_clipboard.regular);
+    vwl_data_device_add_listener(
+           &vwl_clipboard.primary.device,
+           &vwl_clipboard.primary);
+
+    if (vwl_display_roundtrip(&vwl_display) == FAIL)
+    {
+       wayland_cb_uninit();
+       return FAIL;
+    }
+    clip_init(TRUE);
+
+    return OK;
+}
+
+/*
+ * Free up resources used for Wayland selections. Does not destroy global
+ * objects such as data device managers.
+ */
+    void
+wayland_cb_uninit(void)
+{
+    if (vwl_clipboard.fs_buffer != NULL)
+    {
+       vwl_destroy_buffer_store(vwl_clipboard.fs_buffer);
+       vwl_clipboard.fs_buffer = NULL;
+    }
+
+    // Destroy the current offer if it exists
+    vwl_data_offer_destroy(vwl_clipboard.regular.offer, TRUE);
+    vwl_data_offer_destroy(vwl_clipboard.primary.offer, TRUE);
+
+    // Destroy any devices or sources
+    vwl_data_device_destroy(&vwl_clipboard.regular.device, FALSE);
+    vwl_data_device_destroy(&vwl_clipboard.primary.device, FALSE);
+    vwl_data_source_destroy(&vwl_clipboard.regular.source, FALSE);
+    vwl_data_source_destroy(&vwl_clipboard.primary.source, FALSE);
+
+    // Free mime types
+    vwl_clipboard_free_mime_types(&vwl_clipboard.regular);
+    vwl_clipboard_free_mime_types(&vwl_clipboard.primary);
+
+    vwl_display_flush(&vwl_display);
+
+    vim_memset(&vwl_clipboard, 0, sizeof(vwl_clipboard));
+    vwl_clipboard.regular.selection = WAYLAND_SELECTION_REGULAR;
+    vwl_clipboard.primary.selection = WAYLAND_SELECTION_PRIMARY;
+}
+
+/*
+ * If the given selection can be used.
+ */
+    static int
+vwl_clipboard_selection_is_ready(vwl_clipboard_selection_T *clip_sel)
+{
+    return clip_sel->manager.protocol != VWL_DATA_PROTOCOL_NONE &&
+       clip_sel->device.protocol != VWL_DATA_PROTOCOL_NONE;
+}
+
+/*
+ * Callback for data offer event. Start listening to the given offer immediately
+ * in order to get mime types.
+ */
+    static void
+vwl_data_device_listener_data_offer(
+       vwl_data_device_T   *device,
+       vwl_data_offer_T    *offer)
+{
+    vwl_clipboard_selection_T *clip_sel = device->data;
+
+    // Get mime types and save them so we can use them when we want to paste the
+    // selection.
+    if (clip_sel->source.proxy != NULL)
+       // We own the selection, no point in getting mime types
+       return;
+
+    vwl_data_offer_add_listener(offer, device->data);
+}
+
+/*
+ * Callback for offer event. Save each mime type given to be used later.
+ */
+    static void
+vwl_data_offer_listener_offer(vwl_data_offer_T *offer, const char *mime_type)
+{
+    vwl_clipboard_selection_T *clip_sel = offer->data;
+
+    // Save string into temporary grow array, which will be finalized into the
+    // actual grow array if the selection matches with the selection that the
+    // device manages.
+    ga_copy_string(&clip_sel->tmp_mime_types, (char_u*)mime_type);
+}
+
+/*
+ * Callback for selection event, for either the regular or primary selection.
+ * Don't try receiving data from the offer, instead destroy the previous offer
+ * if any and set the current offer to the given offer, along with the
+ * respective mime types.
+ */
+    static void
+vwl_data_device_listener_selection(
+       vwl_data_device_T   *device UNUSED,
+       vwl_data_offer_T    *offer,
+       wayland_selection_T selection)
+{
+    vwl_clipboard_selection_T  *clip_sel = device->data;
+    vwl_data_offer_T           *prev_offer = clip_sel->offer;
+
+    // Save offer if it selection and clip_sel match, else discard it
+    if (clip_sel->selection == selection)
+       clip_sel->offer = offer;
+    else
+    {
+       // Example: selection event is for the primary selection but this device
+       // is only for the regular selection, if so then just discard the offer
+       // and tmp_mime_types.
+       vwl_data_offer_destroy(offer, TRUE);
+       tmp_vwl_offer = NULL;
+       ga_clear_strings(&clip_sel->tmp_mime_types);
+       return;
+    }
+
+    // There are two cases when clip_sel->offer is NULL
+    // 1. No one owns the selection
+    // 2. We own the selection (we'll just access the register directly)
+    if (offer == NULL)
+    {
+       // Selection cleared/empty
+       ga_clear_strings(&clip_sel->tmp_mime_types);
+       clip_sel->offer = NULL;
+       goto exit;
+    }
+    else if (clip_sel->source.proxy != NULL)
+    {
+       // We own the selection, ignore it
+       vwl_data_offer_destroy(offer, TRUE);
+       ga_clear_strings(&clip_sel->tmp_mime_types);
+       clip_sel->offer = NULL;
+       goto exit;
+    }
+
+exit:
+    // Destroy previous offer if any
+    vwl_data_offer_destroy(prev_offer, TRUE);
+    ga_clear_strings(&clip_sel->mime_types);
+
+    // Copy the grow array over
+    clip_sel->mime_types = clip_sel->tmp_mime_types;
+
+    // Clear tmp_mime_types so next data_offer doesn't try to resize/grow it
+    // (Don't free it though using ga_clear() because mime_types->ga_data is the
+    // same pointer)r
+    if (clip_sel->offer != NULL)
+       ga_init(&clip_sel->tmp_mime_types);
+}
+
+/*
+ * Callback for finished event. Destroy device and all related objects/resources
+ * such as offers and mime types.
+ */
+    static void
+vwl_data_device_listener_finished(vwl_data_device_T *device)
+{
+    vwl_clipboard_selection_T *clip_sel = device->data;
+
+    vwl_data_device_destroy(&clip_sel->device, FALSE);
+    vwl_data_offer_destroy(clip_sel->offer, TRUE);
+    vwl_data_source_destroy(&clip_sel->source, FALSE);
+    vwl_clipboard_free_mime_types(clip_sel);
+}
+
+/*
+ * Return a pointer to a grow array of mime types that the current offer
+ * supports sending. If the returned garray has NULL for ga_data or a ga_len of
+ * 0, then the selection is cleared. If focus stealing is required, a surface
+ * will be created to steal focus first.
+ */
+    garray_T *
+wayland_cb_get_mime_types(wayland_selection_T selection)
+{
+    vwl_clipboard_selection_T *clip_sel;
+
+    if (selection == WAYLAND_SELECTION_REGULAR)
+       clip_sel = &vwl_clipboard.regular;
+    else if (selection == WAYLAND_SELECTION_PRIMARY)
+       clip_sel = &vwl_clipboard.primary;
+    else
+       return NULL;
+
+    if (clip_sel->requires_focus)
+    {
+       // We don't care about the on_focus callback since once we gain focus
+       // the data offer events will come immediately.
+       if (vwl_init_fs_surface(vwl_clipboard.seat,
+                   vwl_clipboard.fs_buffer, NULL, NULL) == FAIL)
+           return NULL;
+    }
+    else if (vwl_display_roundtrip(&vwl_display) == FAIL)
+       return NULL;
+
+    return &clip_sel->mime_types;
+}
+
+/*
+ * Receive data from the given selection, and return the fd to read data from.
+ * On failure -1 is returned.
+ */
+    int
+wayland_cb_receive_data(const char *mime_type, wayland_selection_T selection)
+{
+    vwl_clipboard_selection_T *clip_sel;
+
+    // Create pipe that source client will write to
+    int fds[2];
+
+    if (selection == WAYLAND_SELECTION_REGULAR)
+       clip_sel = &vwl_clipboard.regular;
+    else if (selection == WAYLAND_SELECTION_PRIMARY)
+       clip_sel = &vwl_clipboard.primary;
+    else
+       return -1;
+
+    if (!wayland_client_is_connected(FALSE) ||
+           !vwl_clipboard_selection_is_ready(clip_sel))
+       return -1;
+
+    if (clip_sel->offer == NULL || clip_sel->offer->proxy == NULL)
+       return -1;
+
+    if (pipe(fds) == -1)
+       return -1;
+
+    vwl_data_offer_receive(clip_sel->offer, mime_type, fds[1]);
+
+    close(fds[1]); // Close before we read data so that when the source client
+                  // closes their end we receive an EOF.
+
+    if (vwl_display_flush(&vwl_display) == OK)
+       return fds[0];
+
+    close(fds[0]);
+
+    return -1;
+}
+
+/*
+ * Callback for send event. Just call the user callback which will handle it
+ * and do the writing stuff.
+ */
+    static void
+vwl_data_source_listener_send(
+       vwl_data_source_T   *source,
+       const char          *mime_type,
+       int32_t             fd)
+{
+    vwl_clipboard_selection_T *clip_sel = source->data;
+
+    if (clip_sel->send_cb != NULL)
+       clip_sel->send_cb(mime_type, fd, clip_sel->selection);
+    close(fd);
+}
+
+/*
+ * Callback for cancelled event, just call the user callback.
+ */
+    static void
+vwl_data_source_listener_cancelled(vwl_data_source_T *source)
+{
+    vwl_clipboard_selection_T *clip_sel = source->data;
+
+    if (clip_sel->send_cb != NULL)
+       clip_sel->cancelled_cb(clip_sel->selection);
+    vwl_data_source_destroy(source, FALSE);
+}
+
+/*
+ * Set the selection when we gain focus
+ */
+    static void
+vwl_on_focus_set_selection(void *data, uint32_t serial)
+{
+    vwl_clipboard_selection_T *clip_sel = data;
+
+    vwl_data_device_set_selection(
+           &clip_sel->device,
+           &clip_sel->source,
+           serial,
+           clip_sel->selection);
+    vwl_display_roundtrip(&vwl_display);
+}
+
+/*
+ * Become the given selection's owner, and advertise to other clients the mime
+ * types found in mime_types array. Returns FAIL on failure and OK on success.
+ */
+    int
+wayland_cb_own_selection(
+       wayland_cb_send_data_func_T             send_cb,
+       wayland_cb_selection_cancelled_func_T   cancelled_cb,
+       const char                              **mime_types,
+       int                                     len,
+       wayland_selection_T                     selection)
+{
+    vwl_clipboard_selection_T *clip_sel;
+
+    if (selection == WAYLAND_SELECTION_REGULAR)
+       clip_sel = &vwl_clipboard.regular;
+    else if (selection == WAYLAND_SELECTION_PRIMARY)
+       clip_sel = &vwl_clipboard.primary;
+    else
+       return FAIL;
+
+    if (clip_sel->source.proxy != NULL)
+    {
+       if (selection == WAYLAND_SELECTION_PRIMARY)
+           // We already own the selection, ignore (only do this for primary
+           // selection). We don't re set the selection because then we would
+           // be setting the selection every time the user moves the visual
+           // selection cursor, which is messy and inefficient.
+           //
+           // Vim doesn't have a mechanism to only set the selection
+           // when the user stops selecting (such as the user releasing the
+           // mouse button in graphical Wayland applications). So this
+           // behaviour in Vim differs from other Wayland applications.
+           return OK;
+       else if (selection == WAYLAND_SELECTION_REGULAR)
+       {
+           // Technically we don't need to do this as we already own the
+           // selection, however if a user yanks text a second time, the
+           // text yanked won't appear in their clipboard manager if they are
+           // using one.
+           //
+           // This can be unexpected behaviour for the user so its probably
+           // better to do it this way. Additionally other Wayland applications
+           // seem to set the selection every time.
+           //
+           // There should be no noticable performance change since its not
+           // like this is running in the background constantly in Vim, only
+           // runs once when the user yanks text to the system clipboard.
+           vwl_data_source_destroy(&clip_sel->source, FALSE);
+           vwl_display_flush(&vwl_display);
+       }
+       else
+           // Shouldn't happen
+           return FAIL;
+    }
+
+    if (!wayland_client_is_connected(FALSE) ||
+           !vwl_clipboard_selection_is_ready(clip_sel))
+       return FAIL;
+
+    clip_sel->send_cb = send_cb;
+    clip_sel->cancelled_cb = cancelled_cb;
+
+    vwl_create_data_source(&clip_sel->manager, &clip_sel->source);
+
+    vwl_data_source_add_listener(&clip_sel->source, clip_sel);
+
+    // Advertise mime types
+    for (int i = 0; i < len; i++)
+       vwl_data_source_offer(&clip_sel->source, mime_types[i]);
+
+    if (clip_sel->requires_focus)
+    {
+       // Call set_selection later when we gain focus
+       if (vwl_init_fs_surface(vwl_clipboard.seat, vwl_clipboard.fs_buffer,
+                   vwl_on_focus_set_selection, clip_sel) == FAIL)
+           goto fail;
+    }
+    else
+    {
+       vwl_data_device_set_selection(&clip_sel->device,
+               &clip_sel->source, 0, selection);
+       if (vwl_display_roundtrip(&vwl_display) == FAIL)
+           goto fail;
+    }
+
+    return OK;
+fail:
+    vwl_data_source_destroy(&clip_sel->source, FALSE);
+    return FAIL;
+}
+
+/*
+ * Disown the given selection, so that we are not the source client that other
+ * clients receive data from.
+ */
+    void
+wayland_cb_lose_selection(wayland_selection_T selection)
+{
+    if (selection == WAYLAND_SELECTION_REGULAR)
+       vwl_data_source_destroy(&vwl_clipboard.regular.source, FALSE);
+    else if (selection == WAYLAND_SELECTION_PRIMARY)
+       vwl_data_source_destroy(&vwl_clipboard.primary.source, FALSE);
+    vwl_display_flush(&vwl_display);
+}
+
+/*
+ * Return TRUE if the selection is owned by either us or another client.
+ */
+    int
+wayland_cb_selection_is_owned(wayland_selection_T selection)
+{
+    vwl_display_roundtrip(&vwl_display);
+
+    if (selection == WAYLAND_SELECTION_REGULAR)
+       return vwl_clipboard.regular.source.proxy != NULL
+           || vwl_clipboard.regular.offer != NULL;
+    else if (selection == WAYLAND_SELECTION_PRIMARY)
+       return vwl_clipboard.primary.source.proxy != NULL
+           || vwl_clipboard.primary.offer != NULL;
+    else
+       return FALSE;
+}
+
+/*
+ * Return TRUE if the Wayland clipboard/selections are ready to use.
+ */
+    int
+wayland_cb_is_ready(void)
+{
+    vwl_display_roundtrip(&vwl_display);
+
+    // Clipboard is ready if we have at least one selection available
+    return wayland_client_is_connected(TRUE) &&
+           (vwl_clipboard_selection_is_ready(&vwl_clipboard.regular) ||
+           vwl_clipboard_selection_is_ready(&vwl_clipboard.primary));
+}
+
+/*
+ * Reload Wayland clipboard, useful if changing seat.
+ */
+    int
+wayland_cb_reload(void)
+{
+    // Lose any selections we own
+    if (clipmethod == CLIPMETHOD_WAYLAND)
+    {
+       if (clip_star.owned)
+           clip_lose_selection(&clip_star);
+       if (clip_plus.owned)
+           clip_lose_selection(&clip_plus);
+    }
+
+    wayland_cb_uninit();
+
+    if (wayland_cb_init((char*)p_wse) == FAIL)
+       return FAIL;
+
+    choose_clipmethod();
+    return OK;
+}
+
+#endif // FEAT_WAYLAND_CLIPBOARD
+
+static int wayland_ct_restore_count = 0;
+
+/*
+ * Attempts to restore the Wayland display connection. Returns OK if display
+ * connection was/is now valid, else FAIL if the display connection is invalid.
+ */
+    int
+wayland_may_restore_connection(void)
+{
+    // No point if we still are already connected properly
+    if (wayland_client_is_connected(TRUE))
+       return OK;
+
+    // No point in restoring the connection if we are exiting or dying.
+    if (exiting || v_dying || wayland_ct_restore_count <= 0)
+    {
+       wayland_set_display("");
+       return FAIL;
+    }
+
+    --wayland_ct_restore_count;
+    wayland_uninit_client();
+
+    return wayland_init_client(wayland_display_name);
+}
+
+/*
+ * Disconnect then reconnect Wayland connection, and update clipmethod.
+ */
+    void
+ex_wlrestore(exarg_T *eap)
+{
+    char *display;
+
+    if (eap->arg == NULL || STRLEN(eap->arg) == 0)
+       // Use current display name if none given
+       display = wayland_display_name;
+    else
+       display = (char*)eap->arg;
+
+    // Return early if shebang is not passed, we are still connected, and if not
+    // changing to a new Wayland display.
+    if (!eap->forceit && wayland_client_is_connected(TRUE) &&
+           (display == wayland_display_name ||
+            (wayland_display_name != NULL &&
+             STRCMP(wayland_display_name, display) == 0)))
+       return;
+
+#ifdef FEAT_WAYLAND_CLIPBOARD
+    if (clipmethod == CLIPMETHOD_WAYLAND)
+    {
+       // Lose any selections we own
+       if (clip_star.owned)
+           clip_lose_selection(&clip_star);
+       if (clip_plus.owned)
+           clip_lose_selection(&clip_plus);
+    }
+#endif
+
+
+    if (display != NULL)
+       display = (char*)vim_strsave((char_u*)display);
+
+    wayland_uninit_client();
+
+    // Reset amount of available tries to reconnect the display to 5
+    wayland_ct_restore_count = 5;
+
+    if (wayland_init_client(display) == OK)
+    {
+       smsg(_("restoring Wayland display %s"), wayland_display_name);
+
+#ifdef FEAT_WAYLAND_CLIPBOARD
+       wayland_cb_init((char*)p_wse);
+#endif
+    }
+    else
+       msg(_("failed restoring, lost connection to Wayland display"));
+
+    vim_free(display);
+
+    choose_clipmethod();
+}
+
+/*
+ * Set wayland_display_name to display. Note that this allocate a copy of the
+ * string, unless NULL is passed. If NULL is passed then v:wayland_display is
+ * set to $WAYLAND_DISPLAY, but wayland_display_name is set to NULL.
+ */
+    static void
+wayland_set_display(const char *display)
+{
+    if (display == NULL)
+       display = (char*)mch_getenv((char_u*)"WAYLAND_DISPLAY");
+    else if (display == wayland_display_name)
+       // Don't want to be freeing vwl_display_strname then trying to copy it
+       // after.
+       goto exit;
+
+    if (display == NULL)
+       // $WAYLAND_DISPLAY is not set
+       display = "";
+
+    // Leave unchanged if display is empty (but not NULL)
+    if (STRCMP(display, "") != 0)
+    {
+       vim_free(wayland_display_name);
+       wayland_display_name = (char*)vim_strsave((char_u*)display);
+    }
+
+exit:
+#ifdef FEAT_EVAL
+    set_vim_var_string(VV_WAYLAND_DISPLAY, (char_u*)display, -1);
+#endif
+}
 
 #endif // FEAT_WAYLAND
diff --git a/src/wayland.h b/src/wayland.h
deleted file mode 100644 (file)
index d260af4..0000000
+++ /dev/null
@@ -1,214 +0,0 @@
-/* vi:set ts=8 sts=4 sw=4 noet:
- *
- * VIM - Vi IMproved   by Bram Moolenaar
- *
- * Do ":help uganda"  in Vim to read copying and usage conditions.
- * Do ":help credits" in Vim to see a list of people who contributed.
- * See README.txt for an overview of the Vim source code.
- */
-
-/*
- * wayland.h: Common definitions for Wayland code
- */
-
-
-#ifdef FEAT_WAYLAND
-
-#include <wayland-client.h>
-
-#ifdef FEAT_WAYLAND_CLIPBOARD
-# include "auto/wayland/wlr-data-control-unstable-v1.h"
-# include "auto/wayland/ext-data-control-v1.h"
-# ifdef FEAT_WAYLAND_CLIPBOARD_FS
-#  include "auto/wayland/xdg-shell.h"
-#  include "auto/wayland/primary-selection-unstable-v1.h"
-# endif
-#endif
-
-#ifdef FEAT_WAYLAND_CLIPBOARD
-
-// Wayland protocols for accessing the selection
-typedef enum {
-    VWL_DATA_PROTOCOL_NONE,
-    VWL_DATA_PROTOCOL_EXT,
-    VWL_DATA_PROTOCOL_WLR,
-#ifdef FEAT_WAYLAND_CLIPBOARD_FS
-    VWL_DATA_PROTOCOL_CORE,
-    VWL_DATA_PROTOCOL_PRIMARY
-#endif
-} vwl_data_protocol_T;
-
-#endif // FEAT_WAYLAND_CLIPBOARD
-
-// Struct that represents a seat. (Should be accessed via
-// vwl_get_seat()).
-struct vwl_seat_S {
-    struct wl_seat  *proxy;
-    char           *label;         // Name of seat as text (e.g. seat0,
-                                   // seat1...).
-    uint32_t       capabilities;   // Bitmask of the capabilites of the seat
-                                   // (pointer, keyboard, touch).
-};
-
-// Struct wrapper for a Wayland connection
-struct vwl_connection_S {
-    struct {
-       struct wl_display   *proxy;
-       int                 fd; // File descriptor for display
-    } display;
-
-    struct {
-       struct wl_registry *proxy;
-    } registry;
-
-    // Global objects
-    struct {
-       garray_T seats;
-
-#ifdef FEAT_WAYLAND_CLIPBOARD
-       struct zwlr_data_control_manager_v1 *zwlr_data_control_manager_v1;
-       struct ext_data_control_manager_v1  *ext_data_control_manager_v1;
-# ifdef FEAT_WAYLAND_CLIPBOARD_FS
-       struct wl_data_device_manager       *wl_data_device_manager;
-       struct wl_shm                       *wl_shm;
-       struct wl_compositor                *wl_compositor;
-       struct xdg_wm_base                  *xdg_wm_base;
-       struct zwp_primary_selection_device_manager_v1
-           *zwp_primary_selection_device_manager_v1;
-# endif
-#endif
-    } gobjects;
-};
-
-#ifdef FEAT_WAYLAND_CLIPBOARD
-
-// LISTENER WRAPPERS
-
-struct vwl_data_device_listener_S {
-    void (*data_offer)(void *data,
-                      vwl_data_device_T *device,
-                      vwl_data_offer_T *offer);
-    void (*selection)(void *data,
-                     vwl_data_device_T *device,
-                     vwl_data_offer_T *offer,
-                     wayland_selection_T selection);
-
-    // This event is only relevant for data control protocols
-    void (*finished)(void *data, vwl_data_device_T *device);
-};
-
-struct vwl_data_source_listener_S {
-    void (*send)(void *data,
-                vwl_data_source_T *source,
-                const char *mime_type,
-                int fd);
-    void (*cancelled)(void *data, vwl_data_source_T *source);
-};
-
-struct vwl_data_offer_listener_S {
-    // Return TRUE to add mime type to internal array in data offer. Note that
-    // this is not called for the special Vim mime type
-    // (wayland_vim_special_mime), but offer->from_vim is set to true.
-    // Additionally when the special mime type is received, any offer events
-    // after are ignored.
-    bool (*offer)(void *data, vwl_data_offer_T *offer, const char *mime_type);
-};
-
-// DATA RELATED OBJECT WRAPPERS
-// These wrap around a proxy and act as a generic container.
-// The `data` member is used to pass other needed stuff around such as a
-// vwl_clipboard_selection_T pointer.
-
-struct vwl_data_offer_S {
-    void                       *proxy;
-    void                       *data;      // Should be same as parent data
-                                           // device.
-    garray_T                   mime_types;
-    bool                       from_vim;   // If offer came from us setting the
-                                           // selection.
-
-    const vwl_data_offer_listener_T *listener;
-    vwl_data_protocol_T                    protocol;
-};
-
-struct vwl_data_source_S {
-    void                               *proxy;
-    void                               *data;
-    const vwl_data_source_listener_T   *listener;
-    vwl_data_protocol_T            protocol;
-};
-
-struct vwl_data_device_S {
-    void                               *proxy;
-    void                               *data;
-    vwl_data_offer_T                   *offer;
-    const vwl_data_device_listener_T   *listener;
-    vwl_data_protocol_T                        protocol;
-};
-
-struct vwl_data_device_manager_S {
-    void               *proxy;
-    vwl_data_protocol_T protocol;
-};
-
-#ifdef FEAT_WAYLAND_CLIPBOARD_FS
-
-// Dummy functions to handle keyboard events we don't care about.
-
-#define VWL_FUNCS_DUMMY_KEYBOARD_EVENTS() \
-    static void \
-clip_wl_fs_keyboard_listener_keymap( \
-    void               *data UNUSED, \
-    struct wl_keyboard *keyboard UNUSED, \
-    uint32_t           format UNUSED, \
-    int                        fd, \
-    uint32_t           size UNUSED) \
-{ \
-    close(fd); \
-} \
-    static void \
-clip_wl_fs_keyboard_listener_leave( \
-    void               *data UNUSED, \
-    struct wl_keyboard *keyboard UNUSED, \
-    uint32_t           serial UNUSED, \
-    struct wl_surface  *surface UNUSED) \
-{ \
-} \
-    static void \
-clip_wl_fs_keyboard_listener_key( \
-    void               *data UNUSED, \
-    struct wl_keyboard *keyboard UNUSED, \
-    uint32_t           serial UNUSED, \
-    uint32_t           time UNUSED, \
-    uint32_t           key UNUSED, \
-    uint32_t           state UNUSED) \
-{ \
-} \
-    static void \
-clip_wl_fs_keyboard_listener_modifiers( \
-    void               *data UNUSED, \
-    struct wl_keyboard *keyboard UNUSED, \
-    uint32_t           serial UNUSED, \
-    uint32_t           mods_depressed UNUSED, \
-    uint32_t           mods_latched UNUSED, \
-    uint32_t           mods_locked UNUSED, \
-    uint32_t           group UNUSED) \
-{ \
-} \
-    static void \
-clip_wl_fs_keyboard_listener_repeat_info( \
-    void               *data UNUSED, \
-    struct wl_keyboard *keyboard UNUSED, \
-    int32_t            rate UNUSED, \
-    int32_t            delay UNUSED) \
-{ \
-}
-
-#endif
-
-#endif // FEAT_WAYLAND_CLIPBOARD
-
-// Global Wayland connection. Is also set to NULL when the connection is lost.
-extern vwl_connection_T *wayland_ct;
-
-#endif // FEAT_WAYLAND