]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.1.1485: missing Wayland clipboard support v9.1.1485
authorFoxe Chen <chen.foxe@gmail.com>
Fri, 27 Jun 2025 19:10:35 +0000 (21:10 +0200)
committerChristian Brabandt <cb@256bit.org>
Fri, 27 Jun 2025 19:10:35 +0000 (21:10 +0200)
Problem:  missing Wayland clipboard support
Solution: make it work (Foxe Chen)

fixes: #5157
closes: #17097

Signed-off-by: Foxe Chen <chen.foxe@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
68 files changed:
.github/workflows/ci.yml
.gitignore
.hgignore
Filelist
ci/setup-sway.sh [deleted file]
runtime/doc/Make_all.mak
runtime/doc/builtin.txt
runtime/doc/eval.txt
runtime/doc/help.txt
runtime/doc/index.txt
runtime/doc/options.txt
runtime/doc/quickref.txt
runtime/doc/starting.txt
runtime/doc/tags
runtime/doc/various.txt
runtime/doc/version9.txt
runtime/doc/vim.1
runtime/doc/vim.man
runtime/doc/wayland.txt [new file with mode: 0644]
runtime/optwin.vim
runtime/syntax/vim.vim
src/Makefile
src/auto/configure
src/auto/wayland/Makefile [new file with mode: 0644]
src/auto/wayland/README.txt [new file with mode: 0644]
src/auto/wayland/protocols/ext-data-control-v1.xml [new file with mode: 0644]
src/auto/wayland/protocols/primary-selection-unstable-v1.xml [new file with mode: 0644]
src/auto/wayland/protocols/wlr-data-control-unstable-v1.xml [new file with mode: 0644]
src/auto/wayland/protocols/xdg-shell.xml [new file with mode: 0644]
src/clipboard.c
src/config.h.in
src/config.mk.in
src/configure.ac
src/errors.h
src/evalfunc.c
src/evalvars.c
src/ex_cmdidxs.h
src/ex_cmds.h
src/ex_docmd.c
src/feature.h
src/globals.h
src/gui.c
src/main.c
src/message.c
src/option.c
src/option.h
src/optiondefs.h
src/optionstr.c
src/os_unix.c
src/proto.h
src/proto/clipboard.pro
src/proto/option.pro
src/proto/optionstr.pro
src/proto/os_unix.pro
src/proto/wayland.pro [new file with mode: 0644]
src/register.c
src/structs.h
src/testdir/Make_all.mak
src/testdir/gen_opt_test.vim
src/testdir/test_clipmethod.vim [new file with mode: 0644]
src/testdir/test_options.vim
src/testdir/test_registers.vim
src/testdir/test_startup.vim
src/testdir/test_wayland.vim [new file with mode: 0644]
src/testdir/window_manager.vim [new file with mode: 0644]
src/version.c
src/vim.h
src/wayland.c [new file with mode: 0644]

index 47fdc0e2e903f51fac20d36c1309fdea5bb31023..5c9446bb8d6dd3debc6072420b95516a5457934a 100644 (file)
@@ -29,7 +29,6 @@ jobs:
       LOG_DIR: ${{ github.workspace }}/logs
       TERM: xterm
       DISPLAY: ':99'
-      WAYLAND_DISPLAY: 'wayland-1'
       DEBIAN_FRONTEND: noninteractive
 
     strategy:
@@ -125,6 +124,10 @@ jobs:
           sudo rm -f /etc/apt/sources.list.d/microsoft-prod.list
           PKGS=( \
             gettext \
+            x11-utils \
+            labwc \
+            wl-clipboard \
+            wayland-utils \
             libgtk-3-dev:${{ matrix.architecture }} \
             libgtk-3-bin:${{ matrix.architecture }} \
             desktop-file-utils \
@@ -141,7 +144,6 @@ jobs:
             libwayland-cursor0:${{ matrix.architecture }} \
             locales-all \
             software-properties-common \
-            sway \
           )
           if ${{ contains(matrix.extra, 'asan') }} && ${{ contains(matrix.architecture, 'native') }}; then
           PKGS+=( \
@@ -270,8 +272,6 @@ jobs:
           sudo sysctl -w net.ipv6.conf.lo.disable_ipv6=0
           sudo usermod -a -G audio "${USER}"
           sudo bash ci/setup-xvfb.sh
-          # Sway requires user session
-          bash ci/setup-sway.sh
 
       - name: Check autoconf
         if: contains(matrix.extra, 'unittests')
index aee4e9944c4a6ab0534dcff2dd2523be40ef36d2..d849604796a5f8f48cc78fad8c6f398196892f91 100644 (file)
@@ -22,6 +22,8 @@ src/auto/osdef.h
 src/auto/link.log
 src/auto/link.sed
 src/auto/pathdef.c
+src/auto/wayland/*.c
+src/auto/wayland/*.h
 
 # Windows
 *.exe
index 38388a53098d3459c95a8c9972116fdbb52538ce..0e01e5f3ded4171cca73c4633f86d55131943905 100644 (file)
--- a/.hgignore
+++ b/.hgignore
@@ -24,6 +24,8 @@ src/auto/osdef.h
 src/auto/link.log
 src/auto/link.sed
 src/auto/pathdef.c
+src/auto/wayland/*.c
+src/auto/wayland/*.h
 
 # Windows
 *.exe
index 22c631415a5d3a6c5281651f7a8cc3dc40e95e6e..8a14cd811facb2568d1e2ee9701c225992d3874c 100644 (file)
--- a/Filelist
+++ b/Filelist
@@ -241,6 +241,7 @@ SRC_ALL =   \
                src/testdir/ru_RU/LC_MESSAGES/Makefile \
                src/testdir/ru_RU/LC_MESSAGES/__PACKAGE__.po \
                src/testdir/ru_RU/LC_MESSAGES/__PACKAGE__.mo \
+               src/testdir/window_manager.vim \
                src/proto.h \
                src/protodef.h \
                src/proto/alloc.pro \
@@ -490,6 +491,12 @@ SRC_UNIX = \
                src/gui_xmebwp.h \
                src/gui_x11.c \
                src/gui_x11_pm.h \
+               src/auto/wayland/README.txt \
+               src/auto/wayland/Makefile \
+               src/auto/wayland/protocols/ext-data-control-v1.xml \
+               src/auto/wayland/protocols/wlr-data-control-unstable-v1.xml \
+               src/auto/wayland/protocols/xdg-shell.xml \
+               src/auto/wayland/protocols/primary-selection-unstable-v1.xml \
                src/if_xcmdsrv.c \
                src/link.sh \
                src/installman.sh \
@@ -508,6 +515,7 @@ SRC_UNIX =  \
                src/proto/gui_motif.pro \
                src/proto/gui_xmdlg.pro \
                src/proto/gui_x11.pro \
+               src/proto/wayland.pro \
                src/proto/if_xcmdsrv.pro \
                src/proto/os_unix.pro \
                src/proto/pty.pro \
@@ -519,7 +527,9 @@ SRC_UNIX =  \
                src/vim_mask.xbm \
                src/vimtutor \
                src/gvimtutor \
+               src/wayland.c \
                src/which.sh \
+               src/gen-wayland-protocols.sh \
                src/xxd/Makefile \
 
 # Source files for both MS Windows and Unix-like.
diff --git a/ci/setup-sway.sh b/ci/setup-sway.sh
deleted file mode 100644 (file)
index 060b7f9..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-#!/bin/bash
-set -e
-
-# Using a systemd user service doesn't work because it seems like github actions
-# doesn't support user sessions? Just run sway in the background and disown it.
-WLR_BACKENDS=headless sway &
-disown
index 1715382b38e355cd99eb9946c30d359a6ec0cfac..938ee6929e0a12b509a8512146d8410cb0556eb4 100644 (file)
@@ -153,6 +153,7 @@ DOCS = \
        vim9class.txt \
        visual.txt \
        vietnamese.txt \
+       wayland.txt \
        windows.txt \
        workshop.txt
 
@@ -309,6 +310,7 @@ HTMLS = \
        vim9.html \
        vim9class.html \
        visual.html \
+       wayland.html \
        windows.html \
        workshop.html
 
index 8bd914bfb185646b89dd0d66ea2e4e1574a57de2..969b9c7c515dbf266bedcc15d2b6fe0b5e63c2f0 100644 (file)
@@ -1,4 +1,4 @@
-*builtin.txt*  For Vim version 9.1.  Last change: 2025 Jun 23
+*builtin.txt*  For Vim version 9.1.  Last change: 2025 Jun 27
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -13038,6 +13038,8 @@ vms                     VMS version of Vim.
 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 selections/clipboard
 wildignore             Compiled with 'wildignore' option.
 wildmenu               Compiled with 'wildmenu' option.
 win16                  old version for MS-Windows 3.1 (always false)
index 492a4444af9961bfa783573f570fc30d10f070fc..4ea7a5c4cbb0808339506391ccd0fc6e65e9c69f 100644 (file)
@@ -1,4 +1,4 @@
-*eval.txt*     For Vim version 9.1.  Last change: 2025 Jun 04
+*eval.txt*     For Vim version 9.1.  Last change: 2025 Jun 27
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -2240,6 +2240,15 @@ v:charconvert_to
                The name of the character encoding of a file after conversion.
                Only valid while evaluating the 'charconvert' option.
 
+                                       *v:clipmethod*
+v:clipmethod   The current method of accessing the clipboard that is being
+               used. Can either have the value of:
+                       wayland         The Wayland protocol is being used.
+                       x11             X11 selections are being used.
+                       none            The above methods are unavailable
+                                       or cannot be used.
+               See 'clipmethod' for more details.
+
                                        *v:cmdarg* *cmdarg-variable*
 v:cmdarg       This variable is used for two purposes:
                1. The extra arguments given to a file read/write command.
@@ -2969,6 +2978,12 @@ v:vim_did_enter  Zero until most of startup is done.  It is set to one just
                                        *v:warningmsg* *warningmsg-variable*
 v:warningmsg   Last given warning message.  It's allowed to set this variable.
 
+                                       *v:wayland_display*
+v:wayland_display
+               The name of the Wayland display that Vim is connected to.
+               Equivalent to the $WAYLAND_DISPLAY environment variable.
+               If this is empty, then Vim is not connected to any display.
+
                                        *v:windowid* *windowid-variable*
 v:windowid     When any X11/Wayland based GUI is running or when running in a
                terminal and Vim connects to the X server (|-X|) this will be
index a7986851e3b86fbbf622f612ee407d7f8f9eb182..e8f15c737ab36d76a93365fbed76fb2a72d297f4 100644 (file)
@@ -1,4 +1,4 @@
-*help.txt*     For Vim version 9.1.  Last change: 2024 Dec 06
+*help.txt*     For Vim version 9.1.  Last change: 2025 Jun 27
 
                        VIM - main help file
                                                                         k
@@ -187,6 +187,9 @@ GUI ~
 |gui_w32.txt|  Win32 GUI
 |gui_x11.txt|  X11 GUI
 
+System Integration ~
+|wayland.txt|  Wayland protocol support
+
 Interfaces ~
 |if_cscop.txt| using Cscope with Vim
 |if_lua.txt|   Lua interface
index 11c0efd104a169c392278d89bbc967d442622131..820577781fde0bc12c4e59c7bc47c20c1d58ab6d 100644 (file)
@@ -1,4 +1,4 @@
-*index.txt*     For Vim version 9.1.  Last change: 2025 Jun 23
+*index.txt*     For Vim version 9.1.  Last change: 2025 Jun 27
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -1587,6 +1587,7 @@ tag               command         action ~
 |:redrawtabpanel| :redrawtabp[anel] force a redraw of the tabpanel
 |:registers|   :reg[isters]    display the contents of registers
 |:resize|      :res[ize]       change current window height
+|:clipreset|   :clip[reset]    reset 'clipmethod'
 |:retab|       :ret[ab]        change tab size
 |:return|      :retu[rn]       return from a user function
 |:rewind|      :rew[ind]       go to the first file in the argument list
@@ -1777,6 +1778,7 @@ tag               command         action ~
 |:winsize|     :wi[nsize]      get or set window size (obsolete)
 |:wincmd|      :winc[md]       execute a Window (CTRL-W) command
 |:winpos|      :winp[os]       get or set window position
+|:wlrestore|   :wl[restore]    restore the Wayland compositor connection
 |:wnext|       :wn[ext]        write to a file and go to next file in
                                argument list
 |:wprevious|   :wp[revious]    write to a file and go to previous file in
index 1f2f400f6d66b1770bcd48f04a4098feb68f1032..d4fb51ea2751a7cb4890b1136046615b15782d77 100644 (file)
@@ -1,4 +1,4 @@
-*options.txt*  For Vim version 9.1.  Last change: 2025 Jun 18
+*options.txt*  For Vim version 9.1.  Last change: 2025 Jun 27
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -1785,7 +1785,7 @@ A jump table for the options with a short description can be found at |Q_op|.
                                                  for X-windows, "" otherwise)
                        global
                        {only in GUI versions or when the |+xterm_clipboard|
-                       feature is included}
+                       or |+wayland_clipboard| features are included}
        This option is a list of comma-separated names.
        Note: if one of the items is "exclude:", then you can't add an item
        after that.  Therefore do not append an item with += but use ^= to
@@ -1812,10 +1812,13 @@ A jump table for the options with a short description can be found at |Q_op|.
                        register.  When "unnamed" is also included to the
                        option, yank operations (but not delete, change or
                        put) will additionally copy the text into register
-                       '*'.
-                       Only available with the |+X11| feature.
-                       Availability can be checked with: >
-                               if has('unnamedplus')
+                       '*'. If wayland is being used and the compositor does
+                       not support the primary-selection-unstable-v1
+                       protocol, then the regular selection is used in its
+                       place.  Only available with the |+X11| or
+                       |+wayland_clipboard| feature.  Availability can be
+                       checked with: >
+                       if has('unnamedplus')
 <
                                                *clipboard-autoselect*
        autoselect      Works like the 'a' flag in 'guioptions': If present,
@@ -1852,24 +1855,54 @@ A jump table for the options with a short description can be found at |Q_op|.
        exclude:{pattern}
                        Defines a pattern that is matched against the name of
                        the terminal 'term'.  If there is a match, no
-                       connection will be made to the X server.  This is
-                       useful in this situation:
+                       connection will be made to the X server or wayland
+                       compositor.  This is useful in this situation:
                        - Running Vim in a console.
-                       - $DISPLAY is set to start applications on another
-                         display.
-                       - You do not want to connect to the X server in the
-                         console, but do want this in a terminal emulator.
-                       To never connect to the X server use: >
+                       - $DISPLAY/$WAYLAND_DISPLAY is set to start
+                          applications on another display.
+                       - You do not want to connect to the X server/Wayland
+                         compositor in the console, but do want this in a
+                         terminal emulator.
+                       To never connect to the X server/Wayland compositor
+                       use: >
                                exclude:.*
-<                      This has the same effect as using the |-X| argument.
+<                      This has the same effect as using the |-X| or |-Y| argument.
                        Note that when there is no connection to the X server
                        the window title won't be restored and the clipboard
-                       cannot be accessed.
+                       cannot be accessed. This is the same for Wayland,
+                       except there is no title restoring.
                        The value of 'magic' is ignored, {pattern} is
                        interpreted as if 'magic' was on.
                        The rest of the option value will be used for
                        {pattern}, this must be the last entry.
 
+                                               *'clipmethod'* *'cpm'*
+'clipmethod' 'cpm'     string  (default for Unix: "wayland,x11",
+                                for VMS: "x11",
+                                otherwise: "")
+
+                       global
+                       {only when the |+xterm_clipboard| or |+wayland_clipboard|
+                       features are included}
+       Specifies which method of accessing the system clipboard is used,
+       depending on which method works first or is available.  Supported
+       methods are:
+               wayland         Wayland selections
+               x11             X11 selections
+
+       Note: This option is ignored when either the GUI is running or if Vim
+       is run on a system without wayland or X11 support, such as Windows or
+       macOS.  The GUI or system way of accessing the clipboard is always
+       used instead.
+
+       The option value is a list of comma separated items.  The list is parsed
+       left to right in order, and the first method that Vim determines is
+       available or is working is used as the actual method for accessing the
+       clipboard.
+
+       The current method that is being used can be found in the |v:clipmethod|
+       variable.
+
                                                *'cmdheight'* *'ch'*
 'cmdheight' 'ch'       number  (default 1)
                        global or local to tab page
@@ -4586,9 +4619,9 @@ A jump table for the options with a short description can be found at |Q_op|.
        |hl-Title|       t  Titles for output from ":set all", ":autocmd" etc.
        |hl-VertSplit|   c  column used to separate vertically split windows
        |hl-Visual|      v  Visual mode
-       |hl-VisualNOS|   V  Visual mode when Vim does is "Not Owning the
-                           Selection" Only X11 Gui's |gui-x11| and
-                           |xterm-clipboard|.
+       |hl-VisualNOS|   V  Visual mode when Vim is "Not Owning the
+                           Selection" Only X11 Gui's |gui-x11|,
+                           |xterm-clipboard| and |wayland-selections|
        |hl-WarningMsg|  w  warning messages
        |hl-WildMenu|    W  wildcard matches displayed for 'wildmenu'
        |hl-Folded|      f  line used for closed folds
@@ -10043,6 +10076,40 @@ A jump table for the options with a short description can be found at |Q_op|.
        'winwidth' applies to the current window.  Use 'winminwidth' to set
        the minimal width for other windows.
 
+                                               *'wlseat'* *'wse'*
+'wlseat' 'wse'         string  (default "")
+                       global
+                       {only when the |+wayland| feature is included}
+       Specifies the Wayland seat to use for Wayland functionality,
+       specifically the clipboard. If the seat does not exist, then the
+       option will still be set to the new value, with the Wayland clipboard
+       being unavailable as a result. If an empty value is passed then Vim
+       will attempt to use the value of $XDG_SEAT if it exists, if not then
+       it resorts to using the first seat found available. Updating this
+       option will also update |v:clipmethod|.
+
+                                       *'wlsteal'* *'wst'* *'nowlsteal'* *'nowst'*
+'wlsteal' 'wst'                boolean  (default off)
+                       global
+                       {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|.
+
+                                               *'wltimeoutlen'* *'wtm'*
+'wltimeoutlen' 'wtm'   number  (default 500)
+                       global
+                       {only when the |+wayland| feature is included}
+       The timeout in milliseconds before Vim gives up on waiting for the
+       Wayland compositor.  While Vim waits on the compositor, it is
+       unresponsive to input and does not update the screen.  Therefore
+       setting this to a lower value may make Vim feel more responsive in
+       some cases.  On the other hand, it may also mean you receive errors
+       when the compositor takes more time to respond than usual.
+
+       Additionally, this option is also used as the maximum timeout when
+       waiting for a surface to gain focus, see |wayland-focus-steal|.
+
                                                *'wrap'* *'nowrap'*
 'wrap'                 boolean (default on)
                        local to window
index ecabac54abecd9e2a014a853b53e24173a7744dc..55e7cc3bb4cc2e39c44f0133e82f1c88f0a55882 100644 (file)
@@ -1,4 +1,4 @@
-*quickref.txt*  For Vim version 9.1.  Last change: 2025 Jun 12
+*quickref.txt*  For Vim version 9.1.  Last change: 2025 Jun 27
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -646,6 +646,7 @@ Short explanation of each option:           *option-list*
 'cinscopedecls'          'cinsd'   words that are recognized by 'cino-g'
 'cinwords'       'cinw'    words where 'si' and 'cin' add an indent
 'clipboard'      'cb'      use the clipboard as the unnamed register
+'clipmethod'      'cpm'     specify order of what clipboard methods to use
 'cmdheight'      'ch'      number of lines to use for the command-line
 'cmdwinheight'   'cwh'     height of the command-line window
 'colorcolumn'    'cc'      columns to highlight
@@ -1018,6 +1019,9 @@ Short explanation of each option:         *option-list*
 'winminwidth'    'wmw'     minimal number of columns for any window
 'winptydll'                name of the winpty dynamic library
 'winwidth'       'wiw'     minimal number of columns for current window
+'wlseat'         'wse'     the wayland seat to use
+'wlsteal'        'wst'     allow focus stealing functionality for wayland
+'wltimeoutlen'    'wtm'     timeout to use when polling in wayland
 'wrap'                     long lines wrap and continue on the next line
 'wrapmargin'     'wm'      chars from the right where wrapping starts
 'wrapscan'       'ws'      searches wrap around the end of the file
index ccedc1ad4185acbeacd277f31b398b0210944d59..7d3cd2b2039c80ea4b4ea916ba1bd317c618cf41 100644 (file)
@@ -1,4 +1,4 @@
-*starting.txt*  For Vim version 9.1.  Last change: 2025 Feb 27
+*starting.txt*  For Vim version 9.1.  Last change: 2025 Jun 27
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -559,6 +559,12 @@ a slash.  Thus "-R" means recovery and "-/R" readonly.
                client-server messages), call the |serverlist()| function.
                This does not enable the XSMP handler though.
 
+                                                       *-Y*
+-Y             Do not try connecting to the Wayland compositor. Is only
+               relevant for Unix when compiled with the |+wayland| feature,
+               otherwise it's ignored. Note that this will make any feature
+               that uses Wayland unavailable, such as the clipboard.
+
                                                        *-s*
 -s {scriptin}  The script file "scriptin" is read.  The characters in the
                file are interpreted as if you had typed them.  The same can
index 2ec4979f6b0bf7cb7167349d98e7e365b1407f66..c96a8b6ffe3fce45296a3f0aa764ce93532500a9 100644 (file)
@@ -149,6 +149,7 @@ $quote      eval.txt        /*$quote*
 'cinw' options.txt     /*'cinw'*
 'cinwords'     options.txt     /*'cinwords'*
 'clipboard'    options.txt     /*'clipboard'*
+'clipmethod'   options.txt     /*'clipmethod'*
 'cm'   options.txt     /*'cm'*
 'cmdheight'    options.txt     /*'cmdheight'*
 'cmdwinheight' options.txt     /*'cmdwinheight'*
@@ -178,6 +179,7 @@ $quote      eval.txt        /*$quote*
 'copyindent'   options.txt     /*'copyindent'*
 'cot'  options.txt     /*'cot'*
 'cp'   options.txt     /*'cp'*
+'cpm'  options.txt     /*'cpm'*
 'cpo'  options.txt     /*'cpo'*
 'cpoptions'    options.txt     /*'cpoptions'*
 'cpp'  options.txt     /*'cpp'*
@@ -803,6 +805,7 @@ $quote      eval.txt        /*$quote*
 'nowinfixheight'       options.txt     /*'nowinfixheight'*
 'nowinfixwidth'        options.txt     /*'nowinfixwidth'*
 'nowiv'        options.txt     /*'nowiv'*
+'nowlsteal'    options.txt     /*'nowlsteal'*
 'nowmnu'       options.txt     /*'nowmnu'*
 'nowrap'       options.txt     /*'nowrap'*
 'nowrapscan'   options.txt     /*'nowrapscan'*
@@ -810,6 +813,7 @@ $quote      eval.txt        /*$quote*
 'nowriteany'   options.txt     /*'nowriteany'*
 'nowritebackup'        options.txt     /*'nowritebackup'*
 'nows' options.txt     /*'nows'*
+'nowst'        options.txt     /*'nowst'*
 'noxtermcodes' options.txt     /*'noxtermcodes'*
 'nrformats'    options.txt     /*'nrformats'*
 'nu'   options.txt     /*'nu'*
@@ -1334,6 +1338,9 @@ $quote    eval.txt        /*$quote*
 'winwidth'     options.txt     /*'winwidth'*
 'wiv'  options.txt     /*'wiv'*
 'wiw'  options.txt     /*'wiw'*
+'wlseat'       options.txt     /*'wlseat'*
+'wlsteal'      options.txt     /*'wlsteal'*
+'wltimeoutlen' options.txt     /*'wltimeoutlen'*
 'wm'   options.txt     /*'wm'*
 'wmh'  options.txt     /*'wmh'*
 'wmnu' options.txt     /*'wmnu'*
@@ -1347,6 +1354,9 @@ $quote    eval.txt        /*$quote*
 'writebackup'  options.txt     /*'writebackup'*
 'writedelay'   options.txt     /*'writedelay'*
 'ws'   options.txt     /*'ws'*
+'wse'  options.txt     /*'wse'*
+'wst'  options.txt     /*'wst'*
+'wtm'  options.txt     /*'wtm'*
 'ww'   options.txt     /*'ww'*
 'xtermcodes'   options.txt     /*'xtermcodes'*
 '{     motion.txt      /*'{*
@@ -1512,6 +1522,8 @@ $quote    eval.txt        /*$quote*
 +visualextra   various.txt     /*+visualextra*
 +vreplace      various.txt     /*+vreplace*
 +vtp   various.txt     /*+vtp*
++wayland       various.txt     /*+wayland*
++wayland_clipboard     various.txt     /*+wayland_clipboard*
 +wildignore    various.txt     /*+wildignore*
 +wildmenu      various.txt     /*+wildmenu*
 +windows       various.txt     /*+windows*
@@ -1582,6 +1594,7 @@ $quote    eval.txt        /*$quote*
 -V     starting.txt    /*-V*
 -W     starting.txt    /*-W*
 -X     starting.txt    /*-X*
+-Y     starting.txt    /*-Y*
 -Z     starting.txt    /*-Z*
 -b     starting.txt    /*-b*
 -background    gui_x11.txt     /*-background*
@@ -2404,6 +2417,8 @@ $quote    eval.txt        /*$quote*
 :clast quickfix.txt    /*:clast*
 :cle   motion.txt      /*:cle*
 :clearjumps    motion.txt      /*:clearjumps*
+:clip  various.txt     /*:clip*
+:clipreset     various.txt     /*:clipreset*
 :clist quickfix.txt    /*:clist*
 :clo   windows.txt     /*:clo*
 :close windows.txt     /*:close*
@@ -3652,6 +3667,8 @@ $quote    eval.txt        /*$quote*
 :winp  gui.txt /*:winp*
 :winpos        gui.txt /*:winpos*
 :winsize       gui.txt /*:winsize*
+:wl    wayland.txt     /*:wl*
+:wlrestore     wayland.txt     /*:wlrestore*
 :wn    editing.txt     /*:wn*
 :wnext editing.txt     /*:wnext*
 :wp    editing.txt     /*:wp*
@@ -4646,6 +4663,7 @@ E154      helphelp.txt    /*E154*
 E1540  eval.txt        /*E1540*
 E1541  vi_diff.txt     /*E1541*
 E1547  various.txt     /*E1547*
+E1548  wayland.txt     /*E1548*
 E155   sign.txt        /*E155*
 E156   sign.txt        /*E156*
 E157   sign.txt        /*E157*
@@ -11094,6 +11112,7 @@ v:beval_winnr   eval.txt        /*v:beval_winnr*
 v:char eval.txt        /*v:char*
 v:charconvert_from     eval.txt        /*v:charconvert_from*
 v:charconvert_to       eval.txt        /*v:charconvert_to*
+v:clipmethod   eval.txt        /*v:clipmethod*
 v:cmdarg       eval.txt        /*v:cmdarg*
 v:cmdbang      eval.txt        /*v:cmdbang*
 v:collate      eval.txt        /*v:collate*
@@ -11197,6 +11216,7 @@ v:version       eval.txt        /*v:version*
 v:versionlong  eval.txt        /*v:versionlong*
 v:vim_did_enter        eval.txt        /*v:vim_did_enter*
 v:warningmsg   eval.txt        /*v:warningmsg*
+v:wayland_display      eval.txt        /*v:wayland_display*
 v:windowid     eval.txt        /*v:windowid*
 v_!    change.txt      /*v_!*
 v_$    visual.txt      /*v_$*
@@ -11555,6 +11575,17 @@ w:quickfix_title       quickfix.txt    /*w:quickfix_title*
 w:var  eval.txt        /*w:var*
 waittime       channel.txt     /*waittime*
 warningmsg-variable    eval.txt        /*warningmsg-variable*
+wayland        wayland.txt     /*wayland*
+wayland-and-x11        wayland.txt     /*wayland-and-x11*
+wayland-focus-steal    wayland.txt     /*wayland-focus-steal*
+wayland-gnome  wayland.txt     /*wayland-gnome*
+wayland-gui    wayland.txt     /*wayland-gui*
+wayland-persist        wayland.txt     /*wayland-persist*
+wayland-primary-selection      wayland.txt     /*wayland-primary-selection*
+wayland-seat   wayland.txt     /*wayland-seat*
+wayland-selections     wayland.txt     /*wayland-selections*
+wayland-useful wayland.txt     /*wayland-useful*
+wayland.txt    wayland.txt     /*wayland.txt*
 wdl-syntax     syntax.txt      /*wdl-syntax*
 wdl.vim        syntax.txt      /*wdl.vim*
 white-space    pattern.txt     /*white-space*
index d6fb6418f0d5a5aff22259f6e9ae1c34c4dedada..7a097be1066313caa5d21399c7c6114344db1ed2 100644 (file)
@@ -1,4 +1,4 @@
-*various.txt*   For Vim version 9.1.  Last change: 2025 Jun 10
+*various.txt*   For Vim version 9.1.  Last change: 2025 Jun 27
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -524,6 +524,8 @@ T  *+visual*                Visual mode |Visual-mode| Always enabled since 7.4.200.
 T  *+visualextra*      extra Visual mode commands |blockwise-operators|
 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.
 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.
@@ -791,7 +793,15 @@ K                  Run a program to lookup the keyword under the
                        was used for the previous execution of this command.
                        If the value was never specified, then it uses the
                        value of $DISPLAY environment variable as it was when
-                       Vim was started.
+                       Vim was started.  This will also update |v:clipmethod|.
+                       {only available when compiled with the |+xterm_clipboard|
+                       feature}
+
+                                                       *:clipreset* *:clip*
+:clip[reset]           Attempts to choose a new method for accessing the
+                       clipboard, using the 'clipmethod' option. This is
+                       useful when the current method has become unavailable,
+                       and you want to try using another method.
                        {only available when compiled with the |+clipboard|
                        feature}
 
index 606ec790c1ca54fbb57897d4b55c2b1a5a8f3eac..f2618e5cdf9b6ec01610582d70f0276218dc94e9 100644 (file)
@@ -1,4 +1,4 @@
-*version9.txt*  For Vim version 9.1.  Last change: 2025 Jun 23
+*version9.txt*  For Vim version 9.1.  Last change: 2025 Jun 27
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -41571,7 +41571,7 @@ Other new features ~
 The new packages |package-comment|, |package-nohlsearch| and |package-hlyank|
 are included.
 
-Support for Wayland UI.
+Support for Wayland UI and support for the Wayland clipboard
 
 Support for the XDG Desktop Specification |xdg-base-dir|
 
index efef7bb7cc93b47618deec1c63bc7c39ebc2aaa6..1371cb1fe0b22f8b6e55a0f68b21d8da4a950bef 100644 (file)
@@ -1,4 +1,4 @@
-.TH VIM 1 "2024 Aug 12"
+.TH VIM 1 "2025 Jun 27"
 .SH NAME
 vim \- Vi IMproved, a programmer's text editor
 .SH SYNOPSIS
@@ -404,6 +404,9 @@ Will prompt for a crypt key.
 Don't connect to the X server.  Shortens startup time in a terminal, but the
 window title and clipboard will not be used.
 .TP
+\-Y
+Don't connect to the wayland compositor
+.TP
 \-y
 Start
 .B Vim
@@ -559,7 +562,7 @@ initializations (first one found is used).
 System wide gvim initializations.
 .TP
 ~/.gvimrc, ~/.vim/gvimrc, $XDG_CONFIG_HOME/vim/gvimrc
-Your personal 
+Your personal
 .B gVim
 initializations (first one found is used).
 .TP
@@ -567,7 +570,7 @@ initializations (first one found is used).
 Script used for the ":options" command, a nice way to view and set options.
 .TP
 /usr/local/share/vim/vim??/menu.vim
-System wide menu initializations for 
+System wide menu initializations for
 .B gVim.
 .TP
 /usr/local/share/vim/vim??/bugreport.vim
index ce1cc6364a05746bf76fe4eca6c15fa2db97adfa..6d9cfe626c5050c802b627decea5f9dec91a994d 100644 (file)
@@ -36,11 +36,11 @@ DESCRIPTION
             vim [options] [filelist]
 
        If the filelist is missing, the editor will start with an empty buffer.
-       Otherwise exactly one out of the following four may be used  to  choose
+       Otherwise  exactly  one out of the following four may be used to choose
        one or more files to be edited.
 
-       file ..     A  list  of  filenames.   The first one will be the current
-                   file and read into the buffer.  The cursor  will  be  posi‐
+       file ..     A list of filenames.  The first one  will  be  the  current
+                   file  and  read  into the buffer.  The cursor will be posi‐
                    tioned on the first line of the buffer.  You can get to the
                    other files with the ":next" command.  To edit a file  that
                    starts with a dash, precede the filelist with "--".
@@ -49,18 +49,18 @@ DESCRIPTION
                    from stderr, which should be a tty.
 
        -t {tag}    The file to edit and the initial cursor position depends on
-                   a  "tag",  a sort of goto label.  {tag} is looked up in the
+                   a "tag", a sort of goto label.  {tag} is looked up  in  the
                    tags file, the associated file becomes the current file and
                    the  associated  command  is executed.  Mostly this is used
                    for C programs, in which case {tag}  could  be  a  function
                    name.  The effect is that the file containing that function
-                   becomes the current file and the cursor  is  positioned  on
+                   becomes  the  current  file and the cursor is positioned on
                    the start of the function.  See ":help tag-commands".
 
        -q [errorfile]
-                   Start  in  quickFix mode.  The file [errorfile] is read and
-                   the first error is displayed.  If [errorfile]  is  omitted,
-                   the  filename  is obtained from the 'errorfile' option (de‐
+                   Start in quickFix mode.  The file [errorfile] is  read  and
+                   the  first  error is displayed.  If [errorfile] is omitted,
+                   the filename is obtained from the 'errorfile'  option  (de‐
                    faults to "AztecC.Err" for the Amiga, "errors.err" on other
                    systems).   Further  errors can be jumped to with the ":cn"
                    command.  See ":help quickfix".
@@ -70,10 +70,10 @@ DESCRIPTION
 
        vim       The "normal" way, everything is default.
 
-       ex        Start  in Ex mode.  Go to Normal mode with the ":vi" command.
+       ex        Start in Ex mode.  Go to Normal mode with the ":vi"  command.
                  Can also be done with the "-e" argument.
 
-       view      Start in read-only mode.  You will be protected from  writing
+       view      Start  in read-only mode.  You will be protected from writing
                  the files.  Can also be done with the "-R" argument.
 
        gvim gview
@@ -124,72 +124,72 @@ OPTIONS
                    ists.
 
        -d          Start in diff mode.  There should between two to eight file
-                   name  arguments.  Vim will open all the files and show dif‐
+                   name arguments.  Vim will open all the files and show  dif‐
                    ferences between them.  Works like vimdiff(1).
 
        -d {device}, -dev {device}
-                   Open {device} for use as a terminal.  Only  on  the  Amiga.
+                   Open  {device}  for  use as a terminal.  Only on the Amiga.
                    Example: "-d con:20/30/600/150".
 
-       -D          Debugging.   Go  to debugging mode when executing the first
+       -D          Debugging.  Go to debugging mode when executing  the  first
                    command from a script.
 
-       -e          Start Vim in Ex mode, just like the executable  was  called
+       -e          Start  Vim  in Ex mode, just like the executable was called
                    "ex".
 
        -E          Start Vim in improved Ex mode, just like the executable was
                    called "exim".
 
        -f          Foreground.  For the GUI version, Vim will not fork and de‐
-                   tach  from  the shell it was started in.  On the Amiga, Vim
-                   is not restarted to open a new window.  This option  should
-                   be  used  when  Vim is executed by a program that will wait
-                   for the edit session to finish (e.g. mail).  On  the  Amiga
+                   tach from the shell it was started in.  On the  Amiga,  Vim
+                   is  not restarted to open a new window.  This option should
+                   be used when Vim is executed by a program  that  will  wait
+                   for  the  edit session to finish (e.g. mail).  On the Amiga
                    the ":sh" and ":!" commands will not work.
 
-       -F          If  Vim  has  been  compiled with FKMAP support for editing
-                   right-to-left oriented files and  Farsi  keyboard  mapping,
-                   this  option  starts  Vim  in  Farsi mode, i.e. 'fkmap' and
-                   'rightleft' are set.  Otherwise an error message  is  given
+       -F          If Vim has been compiled with  FKMAP  support  for  editing
+                   right-to-left  oriented  files  and Farsi keyboard mapping,
+                   this option starts Vim in  Farsi  mode,  i.e.  'fkmap'  and
+                   'rightleft'  are  set.  Otherwise an error message is given
                    and Vim aborts.
                    Note: Farsi support has been removed in patch 8.1.0932.
 
-       -g          If  Vim has been compiled with GUI support, this option en‐
+       -g          If Vim has been compiled with GUI support, this option  en‐
                    ables the GUI.  If no GUI support was compiled in, an error
                    message is given and Vim aborts.
 
        -H          If Vim has been compiled with RIGHTLEFT support for editing
-                   right-to-left oriented files and Hebrew  keyboard  mapping,
-                   this  option  starts  Vim  in Hebrew mode, i.e. 'hkmap' and
-                   'rightleft' are set.  Otherwise an error message  is  given
+                   right-to-left  oriented  files and Hebrew keyboard mapping,
+                   this option starts Vim in Hebrew  mode,  i.e.  'hkmap'  and
+                   'rightleft'  are  set.  Otherwise an error message is given
                    and Vim aborts.
 
        -i {viminfo}
-                   Specifies  the  filename to use when reading or writing the
-                   viminfo file, instead of the  default  "~/.viminfo".   This
-                   can  also  be used to skip the use of the .viminfo file, by
+                   Specifies the filename to use when reading or  writing  the
+                   viminfo  file,  instead  of the default "~/.viminfo".  This
+                   can also be used to skip the use of the .viminfo  file,  by
                    giving the name "NONE".
 
        -l          Lisp mode.  Sets the 'lisp' and 'showmatch' options on.
 
        -L          Same as -r.
 
-       -m          Modifying files is disabled.  Resets  the  'write'  option.
-                   You  can still modify the buffer, but writing a file is not
+       -m          Modifying  files  is  disabled.  Resets the 'write' option.
+                   You can still modify the buffer, but writing a file is  not
                    possible.
 
-       -M          Modifications not allowed.  The  'modifiable'  and  'write'
-                   options  will be unset, so that changes are not allowed and
-                   files can not be written.  Note that these options  can  be
+       -M          Modifications  not  allowed.   The 'modifiable' and 'write'
+                   options will be unset, so that changes are not allowed  and
+                   files  can  not be written.  Note that these options can be
                    set to enable making modifications.
 
-       -n          No  swap file will be used.  Recovery after a crash will be
-                   impossible.  Handy if you want to edit a  file  on  a  very
-                   slow  medium  (e.g.  floppy).   Can also be done with ":set
+       -n          No swap file will be used.  Recovery after a crash will  be
+                   impossible.   Handy  if  you  want to edit a file on a very
+                   slow medium (e.g. floppy).  Can also  be  done  with  ":set
                    uc=0".  Can be undone with ":set uc=200".
 
-       -N          No-compatible mode.  Resets the 'compatible' option.   This
-                   will  make Vim behave a bit better, but less Vi compatible,
+       -N          No-compatible  mode.  Resets the 'compatible' option.  This
+                   will make Vim behave a bit better, but less Vi  compatible,
                    even though a .vimrc file does not exist.
 
        -nb         Become an editor server for NetBeans.  See the docs for de‐
@@ -198,7 +198,7 @@ OPTIONS
        -o[N]       Open N windows stacked.  When N is omitted, open one window
                    for each file.
 
-       -O[N]       Open N windows side by side.  When N is omitted,  open  one
+       -O[N]       Open  N  windows side by side.  When N is omitted, open one
                    window for each file.
 
        -p[N]       Open N tab pages.  When N is omitted, open one tab page for
@@ -209,15 +209,15 @@ OPTIONS
                    tion.   When possible, Vim will run in an MDI window inside
                    the application. {parent-title} must appear in  the  window
                    title of the parent application.  Make sure that it is spe‐
-                   cific enough. Note that the implementation is still  primi‐
-                   tive.   It  won't  work  with all applications and the menu
+                   cific  enough. Note that the implementation is still primi‐
+                   tive.  It won't work with all  applications  and  the  menu
                    doesn't work.
 
-       -r          List swap files, with information about using them for  re‐
+       -r          List  swap files, with information about using them for re‐
                    covery.
 
-       -r {file}   Recovery  mode.  The swap file is used to recover a crashed
-                   editing session.  The swap file is a  file  with  the  same
+       -r {file}   Recovery mode.  The swap file is used to recover a  crashed
+                   editing  session.   The  swap  file is a file with the same
                    filename as the text file with ".swp" appended.  See ":help
                    recovery".
 
@@ -272,15 +272,15 @@ OPTIONS
 
        -V[N]{filename}
                    Like -V and set 'verbosefile' to {filename}.  The result is
-                   that messages are not displayed but  written  to  the  file
+                   that  messages  are  not  displayed but written to the file
                    {filename}.  {filename} must not start with a digit.
 
        -w{number}  Set the 'window' option to {number}.
 
        -w {scriptout}
-                   All  the  characters that you type are recorded in the file
-                   {scriptout}, until you exit Vim.  This  is  useful  if  you
-                   want  to  create  a script file to be used with "vim -s" or
+                   All the characters that you type are recorded in  the  file
+                   {scriptout},  until  you  exit  Vim.  This is useful if you
+                   want to create a script file to be used with  "vim  -s"  or
                    ":source!".  If the {scriptout} file exists, characters are
                    appended.
 
@@ -294,33 +294,35 @@ OPTIONS
                    terminal,  but  the  window title and clipboard will not be
                    used.
 
+       -Y          Don't connect to the wayland compositor
+
        -y          Start Vim in easy mode, just like the executable was called
-                   "evim"  or "eview".  Makes Vim behave like a click-and-type
+                   "evim" or "eview".  Makes Vim behave like a  click-and-type
                    editor.
 
-       -Z          Restricted mode.  Works like  the  executable  starts  with
+       -Z          Restricted  mode.   Works  like  the executable starts with
                    "r".
 
-       --          Denotes  the end of the options.  Arguments after this will
-                   be handled as a file name.  This can  be  used  to  edit  a
+       --          Denotes the end of the options.  Arguments after this  will
+                   be  handled  as  a  file  name.  This can be used to edit a
                    filename that starts with a '-'.
 
-       --clean     Do  not  use  any  personal  configuration (vimrc, plugins,
-                   etc.).  Useful to see if a problem reproduces with a  clean
+       --clean     Do not use  any  personal  configuration  (vimrc,  plugins,
+                   etc.).   Useful to see if a problem reproduces with a clean
                    Vim setup.
 
        --cmd {command}
-                   Like  using  "-c",  but the command is executed just before
-                   processing any vimrc file.  You can use up to 10  of  these
+                   Like using "-c", but the command is  executed  just  before
+                   processing  any  vimrc file.  You can use up to 10 of these
                    commands, independently from "-c" commands.
 
        --echo-wid  GTK GUI only: Echo the Window ID on stdout.
 
        --gui-dialog-file {name}
-                   When  using the GUI, instead of showing a dialog, write the
-                   title and message of the dialog to file {name}.   The  file
-                   is  created  or  appended  to.  Only useful for testing, to
-                   avoid that the test gets stuck on a dialog  that  can't  be
+                   When using the GUI, instead of showing a dialog, write  the
+                   title  and  message of the dialog to file {name}.  The file
+                   is created or appended to.  Only  useful  for  testing,  to
+                   avoid  that  the  test gets stuck on a dialog that can't be
                    seen.  Without the GUI the argument is ignored.
 
        --help, -h, -?
@@ -374,7 +376,7 @@ OPTIONS
                    List the names of all Vim servers that can be found.
 
        --servername {name}
-                   Use {name} as the server name.  Used for the  current  Vim,
+                   Use  {name}  as the server name.  Used for the current Vim,
                    unless used with a --remote argument, then it's the name of
                    the server to connect to.
 
@@ -404,12 +406,12 @@ ON-LINE HELP
 
 FILES
        /usr/local/share/vim/vim??/doc/*.txt
-                      The Vim documentation files.  Use ":help  doc-file-list"
+                      The  Vim documentation files.  Use ":help doc-file-list"
                       to get the complete list.
                       vim??  is short version number, like vim91 for Vim 9.1
 
        /usr/local/share/vim/vim??/doc/tags
-                      The  tags file used for finding information in the docu‐
+                      The tags file used for finding information in the  docu‐
                       mentation files.
 
        /usr/local/share/vim/vim??/syntax/syntax.vim
@@ -422,18 +424,18 @@ FILES
                       System wide Vim initializations.
 
        ~/.vimrc, ~/.vim/vimrc, $XDG_CONFIG_HOME/vim/vimrc
-                      Your personal Vim initializations (first  one  found  is
+                      Your  personal  Vim  initializations (first one found is
                       used).
 
        /usr/local/share/vim/gvimrc
                       System wide gvim initializations.
 
        ~/.gvimrc, ~/.vim/gvimrc, $XDG_CONFIG_HOME/vim/gvimrc
-                      Your  personal  gVim initializations (first one found is
+                      Your personal gVim initializations (first one  found  is
                       used).
 
        /usr/local/share/vim/vim??/optwin.vim
-                      Script used for the ":options" command, a  nice  way  to
+                      Script  used  for  the ":options" command, a nice way to
                       view and set options.
 
        /usr/local/share/vim/vim??/menu.vim
@@ -443,11 +445,11 @@ FILES
                       Script to generate a bug report.  See ":help bugs".
 
        /usr/local/share/vim/vim??/filetype.vim
-                      Script  to  detect  the type of a file by its name.  See
+                      Script to detect the type of a file by  its  name.   See
                       ":help 'filetype'".
 
        /usr/local/share/vim/vim??/scripts.vim
-                      Script to detect the type of a  file  by  its  contents.
+                      Script  to  detect  the  type of a file by its contents.
                       See ":help 'filetype'".
 
        /usr/local/share/vim/vim??/print/*.ps
@@ -475,4 +477,4 @@ BUGS
        vi_diff.txt  when  in  Vim).   Also have a look at the 'compatible' and
        'cpoptions' options.
 
-                                  2024 Aug 12                           VIM(1)
+                                  2025 Jun 27                           VIM(1)
diff --git a/runtime/doc/wayland.txt b/runtime/doc/wayland.txt
new file mode 100644 (file)
index 0000000..b228dc1
--- /dev/null
@@ -0,0 +1,117 @@
+*wayland.txt*   For Vim version 9.1.  Last change: 2025 Jun 27
+
+
+                 VIM REFERENCE MANUAL    by Bram Moolenaar
+
+
+Wayland Protocol Support                               *wayland*
+
+1. Useful Wayland information  |wayland-useful|
+2. Wayland selections          |wayland-selections|
+
+==============================================================================
+1. Useful Wayland information                          *wayland-useful*
+
+                                                       *wayland-seat*
+Functionality such as the clipboard for Wayland requires a seat to use.  A
+Wayland seat can consist of a keyboard, pointer, and touch device(s).  The
+seat to use can be set with the 'wlseat' option. Only useful if you use
+multiple Wayland seats in the same Wayland session.
+
+                                                       *wayland-gui*
+See |gui-wayland|.  Please note that when using the GUI, Vim uses the toolkit
+such as GTK for accessing the clipboard, and does not access the clipboard
+though Wayland.  You can check this though the |v:clipmethod| variable, which
+should equal to "none" when running the GUI.
+
+Wayland commands:
+                                                       *:wlrestore* *:wl*
+:wl[estore] [display]  Reinitializes the connection to the wayland compositor.
+                       Useful when running Vim in a screen/tmux session that
+                       continues running after the Wayland compositor
+                       restarts.
+
+                       [display] should be in the format of the
+                       $WAYLAND_DISPLAY environment variable (e.g.
+                       "wayland-0"). If [display] is omitted, then it
+                       reinitializes the connection using the same value as
+                       was used for the previous execution of this command.
+                       If the value was never specified, then it uses the
+                       value of $WAYLAND_DISPLAY environment variable.  This
+                       will also update |v:clipmethod|.
+                       {only available when compiled with the |+wayland| feature}
+
+Wayland errors:
+                                                       *E1548*
+Vim failed communicating with the wayland compositor.  This is likely due to
+the Wayland compositor process being killed.  Try the `:wlrestore` command to
+try connecting again.
+
+==============================================================================
+2. Wayland Selections                                  *wayland-selections*
+
+Vim supports the wlr-data-control-unstable-v1 and ext-data-control-v1
+protocols, for accessing the current Wayland selection.  These are the best
+case scenario protocols, see |wayland-focus-steal|.  Selection in this case
+essentially means the "clipboard."  You can check if your Wayland compositor
+supports either of these protocols by running the wayland-info command, which
+should be bunded with libwayland on your system: >
+       wayland-info | grep -E '(ext_data_control|zwlr_data_control)'
+<If grep finds a match, then you have either or both protocols on your system.
+If you don't get any match, then please see |wayland-focus-steal| for more
+information.
+
+If you come from X11, then the regular wayland selection is equivalent to the
+CLIPBOARD selection in X11, and the primary wayland selection equates to the
+X11 primary selection.  Accessing these selections is the same as X11 in Vim,
+in which the + register is the regular selection, and the * register is the
+primary selection, note that your compositor may not support primary
+selections, see |wayland-primary-selection| for more details.
+
+                                                       *wayland-persist*
+If you use X11 cut buffers, no such things exist on Wayland. Instead to
+emulate such functionality, a separate clipboard manager must be used in order
+to persist selection data when a Wayland client exists.
+
+                                                       *wayland-and-x11*
+If your version of Vim comes compiled with both X11 and Wayland support, then
+Vim determines which one to use when accessing the clipboard using the
+'clipmethod' option.
+
+                                                       *wayland-primary-selection*
+If you find X11 style primary selections useful, Wayland also implements this
+behaviour in its own protocols:
+
+- The primary selection protocol is the most widely supported, but requires
+  focus in order to be used, see |wayland-focus-steal|.
+
+- Data control protocol available on your system, such as the ext or wlr
+  protocols, then primary selection is also supported.  This is unless you are
+  using version 1 (not the same as the 'v1' in the protocol name), of the
+  wlr-data-control protocol. Then the primary selection protocol will be used
+  as a fallback.
+
+                                               *wayland-focus-steal* *wayland-gnome*
+If you are using the GNOME desktop environment on Wayland, as of this writing,
+there is no method of accessing/modifying the clipboard for external clients
+such as Vim without being focused.  Focused in this case means the client has
+received some sort of input event, such as a window being focused.  This is
+what the wlr-data-control-unstable-v1 and ext-data-control-v1 protocols solve.
+If your Wayland compositor does not support the above protocols, then the
+above explanation applies.
+
+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.
+
+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
+in your desktop environment, then when Vim attempts to steal focus, it may
+"flicker," as if a window was opened then immediately closed after.
+Additionally, if you are in fullscreen mode, this focus stealing won't work,
+because the created surface won't ever gain focus.  If this happens, Vim will
+seem to freeze temporarily, see 'wltimeoutlen' for more information.
+
+ vim:tw=78:sw=4:ts=8:noet:ft=help:norl:js
index 1aabf6919ab1291d17e056199deedd1422741b04..5e7d87ac2077e67d5c8e87f89f52b2be4e233cc0 100644 (file)
@@ -806,6 +806,20 @@ call <SID>OptionG("slm", &slm)
 if has("clipboard")
   call <SID>AddOption("clipboard", gettext("\"unnamed\" to use the * register like unnamed register\n\"autoselect\" to always put selected text on the clipboard"))
   call <SID>OptionG("cb", &cb)
+  call <SID>AddOption("clipmethod", gettext("Ordered list of possible methods for accessing the clipboard"))
+  call <SID>OptionG("cpm", &cpm)
+endif
+if has("wayland_clipboard")
+  call <SID>AddOption("wltimeoutlen", gettext("Timeout to use when polling for data to read or write in wayland"))
+  call <SID>OptionG("wtm", &wtm)
+endif
+if has('wayland')
+  call <SID>AddOption("wlseat", gettext("Wayland seat to use"))
+  call <SID>OptionG("wse", &wse)
+endif
+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
 call <SID>AddOption("keymodel", gettext("\"startsel\" and/or \"stopsel\"; what special keys can do"))
 call <SID>OptionG("km", &km)
index a9c030be622219648f7602cbca86d7b033fbe376..5da4320ab03602d83b5c210ae30a2b5398c98aca 100644 (file)
@@ -2,7 +2,7 @@
 " Language:       Vim script
 " Maintainer:     Hirohito Higashi <h.east.727 ATMARK gmail.com>
 "         Doug Kearns <dougkearns@gmail.com>
-" Last Change:    2025 Jun 25
+" Last Change:    2025 Jun 27
 " Former Maintainer: Charles E. Campbell
 
 " DO NOT CHANGE DIRECTLY.
@@ -33,12 +33,12 @@ syn cluster vimCommentGroup contains=vimTodo,@Spell
 
 " regular vim commands {{{2
 " GEN_SYN_VIM: vimCommand normal, START_STR='syn keyword vimCommand contained', END_STR='nextgroup=vimBang'
-syn keyword vimCommand contained abo[veleft] al[l] ar[gs] arga[dd] argd[elete] argdo argded[upe] arge[dit] argg[lobal] argl[ocal] argu[ment] as[cii] b[uffer] bN[ext] ba[ll] bad[d] balt bd[elete] bel[owright] bf[irst] bl[ast] bm[odified] bn[ext] bo[tright] bp[revious] br[ewind] brea[k] breaka[dd] breakd[el] breakl[ist] bro[wse] buffers bufd[o] bun[load] bw[ipeout] c[hange] cN[ext] cNf[ile] cabo[ve] cad[dbuffer] cadde[xpr] caddf[ile] caf[ter] cb[uffer] cbe[fore] cbel[ow] cbo[ttom] cc ccl[ose] cd cdo ce[nter] cex[pr] cf[ile] cfd[o] cfir[st] cg[etfile] cgetb[uffer] cgete[xpr] chd[ir] changes che[ckpath] checkt[ime] chi[story] cl[ist] cla[st] clo[se] cle[arjumps] cn[ext] cnew[er] cnf[ile] co[py] col[der] colo[rscheme] com[mand] comc[lear] comp[iler] con[tinue] conf[irm] nextgroup=vimBang
-syn keyword vimCommand contained cons[t] cope[n] cp[revious] cpf[ile] cq[uit] cr[ewind] cs[cope] cst[ag] cw[indow] d[elete] delm[arks] deb[ug] defc[ompile] defe[r] di[splay] dif[fupdate] diffg[et] diffo[ff] diffp[atch] diffpu[t] diffs[plit] difft[his] dig[raphs] disa[ssemble] dj[ump] dli[st] dr[op] ds[earch] dsp[lit] e[dit] ea[rlier] em[enu] endfo[r] endt[ry] endw[hile] ene[w] ev[al] ex exi[t] exu[sage] f[ile] files filet[ype] fin[d] fina[lly] fini[sh] fir[st] fix[del] fo[ld] foldc[lose] foldd[oopen] folddoc[losed] foldo[pen] g[lobal] go[to] gu[i] gv[im] h[elp] helpc[lose] helpf[ind] helpt[ags] ha[rdcopy] hi[ghlight] hid[e] his[tory] hor[izontal] ij[ump] il[ist] int[ro] ip[ut] is[earch] isp[lit] j[oin] ju[mps] kee[pmarks] keepj[umps] keepp[atterns] keepa[lt] l[ist] nextgroup=vimBang
-syn keyword vimCommand contained lN[ext] lNf[ile] la[st] lab[ove] lan[guage] lad[dexpr] laddb[uffer] laddf[ile] laf[ter] lat[er] lb[uffer] lbe[fore] lbel[ow] lbo[ttom] lc[d] lch[dir] lcl[ose] lcs[cope] ld[o] le[ft] lefta[bove] lex[pr] leg[acy] lf[ile] lfd[o] lfir[st] lg[etfile] lgetb[uffer] lgete[xpr] lgr[ep] lgrepa[dd] lhi[story] ll lla[st] lli[st] lmak[e] lne[xt] lnew[er] lnf[ile] lo[adview] loc[kmarks] lockv[ar] lol[der] lop[en] lp[revious] lpf[ile] lr[ewind] lt[ag] lw[indow] ls m[ove] marks menut[ranslate] mes[sages] mk[exrc] mks[ession] mksp[ell] mkv[imrc] mkvie[w] mod[e] n[ext] nb[key] nbc[lose] nbs[tart] noa[utocmd] noh[lsearch] nos[wapfile] nu[mber] o[pen] ol[dfiles] on[ly] opt[ions] ow[nsyntax] p[rint] pa[ckadd] packl[oadall] pb[uffer] pc[lose] ped[it] nextgroup=vimBang
-syn keyword vimCommand contained po[p] pp[op] pre[serve] prev[ious] pro[mptfind] promptr[epl] ps[earch] pt[ag] ptN[ext] ptf[irst] ptj[ump] ptl[ast] ptn[ext] ptp[revious] ptr[ewind] pts[elect] pu[t] pw[d] q[uit] quita[ll] qa[ll] r[ead] rec[over] red[o] redr[aw] redraws[tatus] redrawt[abline] redrawtabp[anel] reg[isters] res[ize] ret[ab] rew[ind] ri[ght] rightb[elow] ru[ntime] rub[y] rubyd[o] rubyf[ile] rund[o] rv[iminfo] sN[ext] sa[rgument] sal[l] san[dbox] sav[eas] sb[uffer] sbN[ext] sba[ll] sbf[irst] sbl[ast] sbm[odified] sbn[ext] sbp[revious] sbr[ewind] scr[iptnames] scripte[ncoding] scriptv[ersion] scs[cope] setf[iletype] sf[ind] sfir[st] sh[ell] sim[alt] sig[n] sil[ent] sla[st] sn[ext] so[urce] sp[lit] spe[llgood] spelld[ump] spelli[nfo] spellr[epall] spellra[re] nextgroup=vimBang
-syn keyword vimCommand contained spellu[ndo] spellw[rong] spr[evious] sre[wind] st[op] sta[g] star[tinsert] startg[replace] startr[eplace] stopi[nsert] stj[ump] sts[elect] sun[hide] sus[pend] sv[iew] sw[apname] synti[me] sync[bind] smi[le] t tN[ext] ta[g] tags tab tabc[lose] tabd[o] tabe[dit] tabf[ind] tabfir[st] tabm[ove] tabl[ast] tabn[ext] tabnew tabo[nly] tabp[revious] tabN[ext] tabr[ewind] tabs tc[d] tch[dir] te[aroff] ter[minal] tf[irst] tj[ump] tl[ast] tn[ext] to[pleft] tp[revious] tr[ewind] try ts[elect] u[ndo] undoj[oin] undol[ist] unh[ide] unlo[ckvar] uns[ilent] up[date] v[global] ve[rsion] verb[ose] vert[ical] vi[sual] vie[w] vim9[cmd] viu[sage] vne[w] vs[plit] w[rite] wN[ext] wa[ll] wi[nsize] winc[md] wind[o] winp[os] wn[ext] wp[revious] wq wqa[ll] nextgroup=vimBang
-syn keyword vimCommand contained wu[ndo] wv[iminfo] x[it] xa[ll] xr[estore] y[ank] z dl dell delel deletl deletel dp dep delp delep deletp deletep a i nextgroup=vimBang
+syn keyword vimCommand contained abo[veleft] al[l] ar[gs] arga[dd] argd[elete] argdo argded[upe] arge[dit] argg[lobal] argl[ocal] argu[ment] as[cii] b[uffer] bN[ext] ba[ll] bad[d] balt bd[elete] bel[owright] bf[irst] bl[ast] bm[odified] bn[ext] bo[tright] bp[revious] br[ewind] brea[k] breaka[dd] breakd[el] breakl[ist] bro[wse] buffers bufd[o] bun[load] bw[ipeout] c[hange] cN[ext] cNf[ile] cabo[ve] cad[dbuffer] cadde[xpr] caddf[ile] caf[ter] cb[uffer] cbe[fore] cbel[ow] cbo[ttom] cc ccl[ose] cd cdo ce[nter] cex[pr] cf[ile] cfd[o] cfir[st] cg[etfile] cgetb[uffer] cgete[xpr] chd[ir] changes che[ckpath] checkt[ime] chi[story] cl[ipreset] clis[t] cla[st] clo[se] cle[arjumps] cn[ext] cnew[er] cnf[ile] co[py] col[der] colo[rscheme] com[mand] comc[lear] comp[iler] con[tinue] nextgroup=vimBang
+syn keyword vimCommand contained conf[irm] cons[t] cope[n] cp[revious] cpf[ile] cq[uit] cr[ewind] cs[cope] cst[ag] cw[indow] d[elete] delm[arks] deb[ug] defc[ompile] defe[r] di[splay] dif[fupdate] diffg[et] diffo[ff] diffp[atch] diffpu[t] diffs[plit] difft[his] dig[raphs] disa[ssemble] dj[ump] dli[st] dr[op] ds[earch] dsp[lit] e[dit] ea[rlier] em[enu] endfo[r] endt[ry] endw[hile] ene[w] ev[al] ex exi[t] exu[sage] f[ile] files filet[ype] fin[d] fina[lly] fini[sh] fir[st] fix[del] fo[ld] foldc[lose] foldd[oopen] folddoc[losed] foldo[pen] g[lobal] go[to] gu[i] gv[im] h[elp] helpc[lose] helpf[ind] helpt[ags] ha[rdcopy] hi[ghlight] hid[e] his[tory] hor[izontal] ij[ump] il[ist] int[ro] ip[ut] is[earch] isp[lit] j[oin] ju[mps] kee[pmarks] keepj[umps] keepp[atterns] keepa[lt] nextgroup=vimBang
+syn keyword vimCommand contained l[ist] lN[ext] lNf[ile] la[st] lab[ove] lan[guage] lad[dexpr] laddb[uffer] laddf[ile] laf[ter] lat[er] lb[uffer] lbe[fore] lbel[ow] lbo[ttom] lc[d] lch[dir] lcl[ose] lcs[cope] ld[o] le[ft] lefta[bove] lex[pr] leg[acy] lf[ile] lfd[o] lfir[st] lg[etfile] lgetb[uffer] lgete[xpr] lgr[ep] lgrepa[dd] lhi[story] ll lla[st] lli[st] lmak[e] lne[xt] lnew[er] lnf[ile] lo[adview] loc[kmarks] lockv[ar] lol[der] lop[en] lp[revious] lpf[ile] lr[ewind] lt[ag] lw[indow] ls m[ove] marks menut[ranslate] mes[sages] mk[exrc] mks[ession] mksp[ell] mkv[imrc] mkvie[w] mod[e] n[ext] nb[key] nbc[lose] nbs[tart] noa[utocmd] noh[lsearch] nos[wapfile] nu[mber] o[pen] ol[dfiles] on[ly] opt[ions] ow[nsyntax] p[rint] pa[ckadd] packl[oadall] pb[uffer] pc[lose] nextgroup=vimBang
+syn keyword vimCommand contained ped[it] po[p] pp[op] pre[serve] prev[ious] pro[mptfind] promptr[epl] ps[earch] pt[ag] ptN[ext] ptf[irst] ptj[ump] ptl[ast] ptn[ext] ptp[revious] ptr[ewind] pts[elect] pu[t] pw[d] q[uit] quita[ll] qa[ll] r[ead] rec[over] red[o] redr[aw] redraws[tatus] redrawt[abline] redrawtabp[anel] reg[isters] res[ize] ret[ab] rew[ind] ri[ght] rightb[elow] ru[ntime] rub[y] rubyd[o] rubyf[ile] rund[o] rv[iminfo] sN[ext] sa[rgument] sal[l] san[dbox] sav[eas] sb[uffer] sbN[ext] sba[ll] sbf[irst] sbl[ast] sbm[odified] sbn[ext] sbp[revious] sbr[ewind] scr[iptnames] scripte[ncoding] scriptv[ersion] scs[cope] setf[iletype] sf[ind] sfir[st] sh[ell] sim[alt] sig[n] sil[ent] sla[st] sn[ext] so[urce] sp[lit] spe[llgood] spelld[ump] spelli[nfo] spellr[epall] nextgroup=vimBang
+syn keyword vimCommand contained spellra[re] spellu[ndo] spellw[rong] spr[evious] sre[wind] st[op] sta[g] star[tinsert] startg[replace] startr[eplace] stopi[nsert] stj[ump] sts[elect] sun[hide] sus[pend] sv[iew] sw[apname] synti[me] sync[bind] smi[le] t tN[ext] ta[g] tags tab tabc[lose] tabd[o] tabe[dit] tabf[ind] tabfir[st] tabm[ove] tabl[ast] tabn[ext] tabnew tabo[nly] tabp[revious] tabN[ext] tabr[ewind] tabs tc[d] tch[dir] te[aroff] ter[minal] tf[irst] tj[ump] tl[ast] tn[ext] to[pleft] tp[revious] tr[ewind] try ts[elect] u[ndo] undoj[oin] undol[ist] unh[ide] unlo[ckvar] uns[ilent] up[date] v[global] ve[rsion] verb[ose] vert[ical] vi[sual] vie[w] vim9[cmd] viu[sage] vne[w] vs[plit] w[rite] wN[ext] wa[ll] wi[nsize] winc[md] wind[o] winp[os] wl[restore] wn[ext] nextgroup=vimBang
+syn keyword vimCommand contained wp[revious] wq wqa[ll] wu[ndo] wv[iminfo] x[it] xa[ll] xr[estore] y[ank] z dl dell delel deletl deletel dp dep delp delep deletp deletep a i nextgroup=vimBang
 
 " Lower priority for _new_ to distinguish constructors from the command.
 syn match   vimCommand contained       "\<new\>(\@!"
@@ -47,29 +47,29 @@ syn keyword vimStdPlugin contained  Arguments Asm Break Cfilter Clear Continue Di
 
 " vimOptions are caught only when contained in a vimSet {{{2
 " GEN_SYN_VIM: vimOption normal, START_STR='syn keyword vimOption contained', END_STR='skipwhite nextgroup=vimSetEqual,vimSetMod'
-syn keyword vimOption contained al aleph ari allowrevins ambw ambiwidth arab arabic arshape arabicshape acd autochdir ai autoindent ar autoread asd autoshelldir aw autowrite awa autowriteall bg background bs backspace bk backup bkc backupcopy bdir backupdir bex backupext bsk backupskip bdlay balloondelay beval ballooneval bevalterm balloonevalterm bexpr balloonexpr bo belloff bin binary bomb brk breakat bri breakindent briopt breakindentopt bsdir browsedir bh bufhidden bl buflisted bt buftype cmp casemap cdh cdhome cd cdpath cedit ccv charconvert chi chistory cin cindent cink cinkeys cino cinoptions cinsd cinscopedecls cinw cinwords cb clipboard ch cmdheight cwh cmdwinheight cc colorcolumn co columns com comments cms commentstring cp compatible cpt complete cfu completefunc skipwhite nextgroup=vimSetEqual,vimSetMod
-syn keyword vimOption contained cfc completefuzzycollect cia completeitemalign cot completeopt cpp completepopup csl completeslash cocu concealcursor cole conceallevel cf confirm ci copyindent cpo cpoptions cm cryptmethod cspc cscopepathcomp csprg cscopeprg csqf cscopequickfix csre cscoperelative cst cscopetag csto cscopetagorder csverb cscopeverbose crb cursorbind cuc cursorcolumn cul cursorline culopt cursorlineopt debug def define deco delcombine dict dictionary diff dex diffexpr dip diffopt dg digraph dir directory dy display ead eadirection ed edcompatible emo emoji enc encoding eof endoffile eol endofline ea equalalways ep equalprg eb errorbells ef errorfile efm errorformat ek esckeys ei eventignore eiw eventignorewin et expandtab ex exrc fenc fileencoding skipwhite nextgroup=vimSetEqual,vimSetMod
-syn keyword vimOption contained fencs fileencodings ff fileformat ffs fileformats fic fileignorecase ft filetype fcs fillchars ffu findfunc fixeol fixendofline fcl foldclose fdc foldcolumn fen foldenable fde foldexpr fdi foldignore fdl foldlevel fdls foldlevelstart fmr foldmarker fdm foldmethod fml foldminlines fdn foldnestmax fdo foldopen fdt foldtext fex formatexpr flp formatlistpat fo formatoptions fp formatprg fs fsync gd gdefault gfm grepformat gp grepprg gcr guicursor gfn guifont gfs guifontset gfw guifontwide ghr guiheadroom gli guiligatures go guioptions guipty gtl guitablabel gtt guitabtooltip hf helpfile hh helpheight hlg helplang hid hidden hl highlight hi history hk hkmap hkp hkmapp hls hlsearch icon iconstring ic ignorecase imaf imactivatefunc imak imactivatekey skipwhite nextgroup=vimSetEqual,vimSetMod
-syn keyword vimOption contained imc imcmdline imd imdisable imi iminsert ims imsearch imsf imstatusfunc imst imstyle inc include inex includeexpr is incsearch inde indentexpr indk indentkeys inf infercase im insertmode ise isexpand isf isfname isi isident isk iskeyword isp isprint js joinspaces jop jumpoptions key kmp keymap km keymodel kpc keyprotocol kp keywordprg lmap langmap lm langmenu lnr langnoremap lrm langremap ls laststatus lz lazyredraw lhi lhistory lbr linebreak lines lsp linespace lisp lop lispoptions lw lispwords list lcs listchars lpl loadplugins luadll magic mef makeef menc makeencoding mp makeprg mps matchpairs mat matchtime mco maxcombine mfd maxfuncdepth mmd maxmapdepth mm maxmem mmp maxmempattern mmt maxmemtot mis menuitems mopt messagesopt skipwhite nextgroup=vimSetEqual,vimSetMod
-syn keyword vimOption contained msm mkspellmem ml modeline mle modelineexpr mls modelines ma modifiable mod modified more mouse mousef mousefocus mh mousehide mousem mousemodel mousemev mousemoveevent mouses mouseshape mouset mousetime mzq mzquantum mzschemedll mzschemegcdll nf nrformats nu number nuw numberwidth ofu omnifunc odev opendevice opfunc operatorfunc pp packpath para paragraphs paste pt pastetoggle pex patchexpr pm patchmode pa path perldll pi preserveindent pvh previewheight pvp previewpopup pvw previewwindow pdev printdevice penc printencoding pexpr printexpr pfn printfont pheader printheader pmbcs printmbcharset pmbfn printmbfont popt printoptions prompt ph pumheight pmw pummaxwidth pw pumwidth pythondll pythonhome pythonthreedll pythonthreehome skipwhite nextgroup=vimSetEqual,vimSetMod
-syn keyword vimOption contained pyx pyxversion qftf quickfixtextfunc qe quoteescape ro readonly rdt redrawtime re regexpengine rnu relativenumber remap rop renderoptions report rs restorescreen ri revins rl rightleft rlc rightleftcmd rubydll ru ruler ruf rulerformat rtp runtimepath scr scroll scb scrollbind scf scrollfocus sj scrolljump so scrolloff sbo scrollopt sect sections secure sel selection slm selectmode ssop sessionoptions sh shell shcf shellcmdflag sp shellpipe shq shellquote srr shellredir ssl shellslash stmp shelltemp st shelltype sxe shellxescape sxq shellxquote sr shiftround sw shiftwidth shm shortmess sn shortname sbr showbreak sc showcmd sloc showcmdloc sft showfulltag sm showmatch smd showmode stal showtabline stpl showtabpanel ss sidescroll siso sidescrolloff skipwhite nextgroup=vimSetEqual,vimSetMod
-syn keyword vimOption contained scl signcolumn scs smartcase si smartindent sta smarttab sms smoothscroll sts softtabstop spell spc spellcapcheck spf spellfile spl spelllang spo spelloptions sps spellsuggest sb splitbelow spk splitkeep spr splitright sol startofline stl statusline su suffixes sua suffixesadd swf swapfile sws swapsync swb switchbuf smc synmaxcol syn syntax tcl tabclose tal tabline tpm tabpagemax tpl tabpanel tplo tabpanelopt ts tabstop tbs tagbsearch tc tagcase tfu tagfunc tl taglength tr tagrelative tag tags tgst tagstack tcldll term tbidi termbidi tenc termencoding tgc termguicolors twk termwinkey twsl termwinscroll tws termwinsize twt termwintype terse ta textauto tx textmode tw textwidth tsr thesaurus tsrfu thesaurusfunc top tildeop to timeout skipwhite nextgroup=vimSetEqual,vimSetMod
-syn keyword vimOption contained tm timeoutlen title titlelen titleold titlestring tb toolbar tbis toolbariconsize ttimeout ttm ttimeoutlen tbi ttybuiltin tf ttyfast ttym ttymouse tsl ttyscroll tty ttytype udir undodir udf undofile ul undolevels ur undoreload uc updatecount ut updatetime vsts varsofttabstop vts vartabstop vbs verbose vfile verbosefile vdir viewdir vop viewoptions vi viminfo vif viminfofile ve virtualedit vb visualbell warn wiv weirdinvert ww whichwrap wc wildchar wcm wildcharm wig wildignore wic wildignorecase wmnu wildmenu wim wildmode wop wildoptions wak winaltkeys wcr wincolor wi window wfb winfixbuf wfh winfixheight wfw winfixwidth wh winheight wmh winminheight wmw winminwidth winptydll wiw winwidth wrap wm wrapmargin ws wrapscan write wa writeany skipwhite nextgroup=vimSetEqual,vimSetMod
-syn keyword vimOption contained wb writebackup wd writedelay xtermcodes skipwhite nextgroup=vimSetEqual,vimSetMod
+syn keyword vimOption contained al aleph ari allowrevins ambw ambiwidth arab arabic arshape arabicshape acd autochdir ai autoindent ar autoread asd autoshelldir aw autowrite awa autowriteall bg background bs backspace bk backup bkc backupcopy bdir backupdir bex backupext bsk backupskip bdlay balloondelay beval ballooneval bevalterm balloonevalterm bexpr balloonexpr bo belloff bin binary bomb brk breakat bri breakindent briopt breakindentopt bsdir browsedir bh bufhidden bl buflisted bt buftype cmp casemap cdh cdhome cd cdpath cedit ccv charconvert chi chistory cin cindent cink cinkeys cino cinoptions cinsd cinscopedecls cinw cinwords cb clipboard cpm clipmethod ch cmdheight cwh cmdwinheight cc colorcolumn co columns com comments cms commentstring cp compatible skipwhite nextgroup=vimSetEqual,vimSetMod
+syn keyword vimOption contained cpt complete cfu completefunc cfc completefuzzycollect cia completeitemalign cot completeopt cpp completepopup csl completeslash cocu concealcursor cole conceallevel cf confirm ci copyindent cpo cpoptions cm cryptmethod cspc cscopepathcomp csprg cscopeprg csqf cscopequickfix csre cscoperelative cst cscopetag csto cscopetagorder csverb cscopeverbose crb cursorbind cuc cursorcolumn cul cursorline culopt cursorlineopt debug def define deco delcombine dict dictionary diff dex diffexpr dip diffopt dg digraph dir directory dy display ead eadirection ed edcompatible emo emoji enc encoding eof endoffile eol endofline ea equalalways ep equalprg eb errorbells ef errorfile efm errorformat ek esckeys ei eventignore eiw eventignorewin et expandtab skipwhite nextgroup=vimSetEqual,vimSetMod
+syn keyword vimOption contained ex exrc fenc fileencoding fencs fileencodings ff fileformat ffs fileformats fic fileignorecase ft filetype fcs fillchars ffu findfunc fixeol fixendofline fcl foldclose fdc foldcolumn fen foldenable fde foldexpr fdi foldignore fdl foldlevel fdls foldlevelstart fmr foldmarker fdm foldmethod fml foldminlines fdn foldnestmax fdo foldopen fdt foldtext fex formatexpr flp formatlistpat fo formatoptions fp formatprg fs fsync gd gdefault gfm grepformat gp grepprg gcr guicursor gfn guifont gfs guifontset gfw guifontwide ghr guiheadroom gli guiligatures go guioptions guipty gtl guitablabel gtt guitabtooltip hf helpfile hh helpheight hlg helplang hid hidden hl highlight hi history hk hkmap hkp hkmapp hls hlsearch icon iconstring ic ignorecase skipwhite nextgroup=vimSetEqual,vimSetMod
+syn keyword vimOption contained imaf imactivatefunc imak imactivatekey imc imcmdline imd imdisable imi iminsert ims imsearch imsf imstatusfunc imst imstyle inc include inex includeexpr is incsearch inde indentexpr indk indentkeys inf infercase im insertmode ise isexpand isf isfname isi isident isk iskeyword isp isprint js joinspaces jop jumpoptions key kmp keymap km keymodel kpc keyprotocol kp keywordprg lmap langmap lm langmenu lnr langnoremap lrm langremap ls laststatus lz lazyredraw lhi lhistory lbr linebreak lines lsp linespace lisp lop lispoptions lw lispwords list lcs listchars lpl loadplugins luadll magic mef makeef menc makeencoding mp makeprg mps matchpairs mat matchtime mco maxcombine mfd maxfuncdepth mmd maxmapdepth mm maxmem mmp maxmempattern mmt maxmemtot skipwhite nextgroup=vimSetEqual,vimSetMod
+syn keyword vimOption contained mis menuitems mopt messagesopt msm mkspellmem ml modeline mle modelineexpr mls modelines ma modifiable mod modified more mouse mousef mousefocus mh mousehide mousem mousemodel mousemev mousemoveevent mouses mouseshape mouset mousetime mzq mzquantum mzschemedll mzschemegcdll nf nrformats nu number nuw numberwidth ofu omnifunc odev opendevice opfunc operatorfunc pp packpath para paragraphs paste pt pastetoggle pex patchexpr pm patchmode pa path perldll pi preserveindent pvh previewheight pvp previewpopup pvw previewwindow pdev printdevice penc printencoding pexpr printexpr pfn printfont pheader printheader pmbcs printmbcharset pmbfn printmbfont popt printoptions prompt ph pumheight pmw pummaxwidth pw pumwidth pythondll pythonhome skipwhite nextgroup=vimSetEqual,vimSetMod
+syn keyword vimOption contained pythonthreedll pythonthreehome pyx pyxversion qftf quickfixtextfunc qe quoteescape ro readonly rdt redrawtime re regexpengine rnu relativenumber remap rop renderoptions report rs restorescreen ri revins rl rightleft rlc rightleftcmd rubydll ru ruler ruf rulerformat rtp runtimepath scr scroll scb scrollbind scf scrollfocus sj scrolljump so scrolloff sbo scrollopt sect sections secure sel selection slm selectmode ssop sessionoptions sh shell shcf shellcmdflag sp shellpipe shq shellquote srr shellredir ssl shellslash stmp shelltemp st shelltype sxe shellxescape sxq shellxquote sr shiftround sw shiftwidth shm shortmess sn shortname sbr showbreak sc showcmd sloc showcmdloc sft showfulltag sm showmatch smd showmode stal showtabline stpl showtabpanel skipwhite nextgroup=vimSetEqual,vimSetMod
+syn keyword vimOption contained ss sidescroll siso sidescrolloff scl signcolumn scs smartcase si smartindent sta smarttab sms smoothscroll sts softtabstop spell spc spellcapcheck spf spellfile spl spelllang spo spelloptions sps spellsuggest sb splitbelow spk splitkeep spr splitright sol startofline stl statusline su suffixes sua suffixesadd swf swapfile sws swapsync swb switchbuf smc synmaxcol syn syntax tcl tabclose tal tabline tpm tabpagemax tpl tabpanel tplo tabpanelopt ts tabstop tbs tagbsearch tc tagcase tfu tagfunc tl taglength tr tagrelative tag tags tgst tagstack tcldll term tbidi termbidi tenc termencoding tgc termguicolors twk termwinkey twsl termwinscroll tws termwinsize twt termwintype terse ta textauto tx textmode tw textwidth tsr thesaurus tsrfu thesaurusfunc skipwhite nextgroup=vimSetEqual,vimSetMod
+syn keyword vimOption contained top tildeop to timeout tm timeoutlen title titlelen titleold titlestring tb toolbar tbis toolbariconsize ttimeout ttm ttimeoutlen tbi ttybuiltin tf ttyfast ttym ttymouse tsl ttyscroll tty ttytype udir undodir udf undofile ul undolevels ur undoreload uc updatecount ut updatetime vsts varsofttabstop vts vartabstop vbs verbose vfile verbosefile vdir viewdir vop viewoptions vi viminfo vif viminfofile ve virtualedit vb visualbell warn wiv weirdinvert ww whichwrap wc wildchar wcm wildcharm wig wildignore wic wildignorecase wmnu wildmenu wim wildmode wop wildoptions wak winaltkeys wcr wincolor wi window wfb winfixbuf wfh winfixheight wfw winfixwidth wh winheight wmh winminheight wmw winminwidth winptydll wiw winwidth wse wlseat wst wlsteal skipwhite nextgroup=vimSetEqual,vimSetMod
+syn keyword vimOption contained wtm wltimeoutlen wrap wm wrapmargin ws wrapscan write wa writeany wb writebackup wd writedelay xtermcodes skipwhite nextgroup=vimSetEqual,vimSetMod
 
 " vimOptions: These are the turn-off setting variants {{{2
 " GEN_SYN_VIM: vimOption turn-off, START_STR='syn keyword vimOption contained', END_STR=''
 syn keyword vimOption contained noari noallowrevins noarab noarabic noarshape noarabicshape noacd noautochdir noai noautoindent noar noautoread noasd noautoshelldir noaw noautowrite noawa noautowriteall nobk nobackup nobeval noballooneval nobevalterm noballoonevalterm nobin nobinary nobomb nobri nobreakindent nobl nobuflisted nocdh nocdhome nocin nocindent nocp nocompatible nocf noconfirm noci nocopyindent nocsre nocscoperelative nocst nocscopetag nocsverb nocscopeverbose nocrb nocursorbind nocuc nocursorcolumn nocul nocursorline nodeco nodelcombine nodiff nodg nodigraph noed noedcompatible noemo noemoji noeof noendoffile noeol noendofline noea noequalalways noeb noerrorbells noek noesckeys noet noexpandtab noex noexrc nofic nofileignorecase nofixeol nofixendofline
 syn keyword vimOption contained nofen nofoldenable nofs nofsync nogd nogdefault noguipty nohid nohidden nohk nohkmap nohkp nohkmapp nohls nohlsearch noicon noic noignorecase noimc noimcmdline noimd noimdisable nois noincsearch noinf noinfercase noim noinsertmode nojs nojoinspaces nolnr nolangnoremap nolrm nolangremap nolz nolazyredraw nolbr nolinebreak nolisp nolist nolpl noloadplugins nomagic noml nomodeline nomle nomodelineexpr noma nomodifiable nomod nomodified nomore nomousef nomousefocus nomh nomousehide nomousemev nomousemoveevent nonu nonumber noodev noopendevice nopaste nopi nopreserveindent nopvw nopreviewwindow noprompt noro noreadonly nornu norelativenumber noremap nors norestorescreen nori norevins norl norightleft noru noruler noscb noscrollbind noscf noscrollfocus
-syn keyword vimOption contained nosecure nossl noshellslash nostmp noshelltemp nosr noshiftround nosn noshortname nosc noshowcmd nosft noshowfulltag nosm noshowmatch nosmd noshowmode noscs nosmartcase nosi nosmartindent nosta nosmarttab nosms nosmoothscroll nospell nosb nosplitbelow nospr nosplitright nosol nostartofline noswf noswapfile notbs notagbsearch notr notagrelative notgst notagstack notbidi notermbidi notgc notermguicolors noterse nota notextauto notx notextmode notop notildeop noto notimeout notitle nottimeout notbi nottybuiltin notf nottyfast noudf noundofile novb novisualbell nowarn nowiv noweirdinvert nowic nowildignorecase nowmnu nowildmenu nowfb nowinfixbuf nowfh nowinfixheight nowfw nowinfixwidth nowrap nows nowrapscan nowrite nowa nowriteany
-syn keyword vimOption contained nowb nowritebackup noxtermcodes
+syn keyword vimOption contained nosecure nossl noshellslash nostmp noshelltemp nosr noshiftround nosn noshortname nosc noshowcmd nosft noshowfulltag nosm noshowmatch nosmd noshowmode noscs nosmartcase nosi nosmartindent nosta nosmarttab nosms nosmoothscroll nospell nosb nosplitbelow nospr nosplitright nosol nostartofline noswf noswapfile notbs notagbsearch notr notagrelative notgst notagstack notbidi notermbidi notgc notermguicolors noterse nota notextauto notx notextmode notop notildeop noto notimeout notitle nottimeout notbi nottybuiltin notf nottyfast noudf noundofile novb novisualbell nowarn nowiv noweirdinvert nowic nowildignorecase nowmnu nowildmenu nowfb nowinfixbuf nowfh nowinfixheight nowfw nowinfixwidth nowst nowlsteal nowrap nows nowrapscan nowrite
+syn keyword vimOption contained nowa nowriteany nowb nowritebackup noxtermcodes
 
 " vimOptions: These are the invertible variants {{{2
 " GEN_SYN_VIM: vimOption invertible, START_STR='syn keyword vimOption contained', END_STR=''
 syn keyword vimOption contained invari invallowrevins invarab invarabic invarshape invarabicshape invacd invautochdir invai invautoindent invar invautoread invasd invautoshelldir invaw invautowrite invawa invautowriteall invbk invbackup invbeval invballooneval invbevalterm invballoonevalterm invbin invbinary invbomb invbri invbreakindent invbl invbuflisted invcdh invcdhome invcin invcindent invcp invcompatible invcf invconfirm invci invcopyindent invcsre invcscoperelative invcst invcscopetag invcsverb invcscopeverbose invcrb invcursorbind invcuc invcursorcolumn invcul invcursorline invdeco invdelcombine invdiff invdg invdigraph inved invedcompatible invemo invemoji inveof invendoffile inveol invendofline invea invequalalways inveb inverrorbells invek invesckeys
 syn keyword vimOption contained invet invexpandtab invex invexrc invfic invfileignorecase invfixeol invfixendofline invfen invfoldenable invfs invfsync invgd invgdefault invguipty invhid invhidden invhk invhkmap invhkp invhkmapp invhls invhlsearch invicon invic invignorecase invimc invimcmdline invimd invimdisable invis invincsearch invinf invinfercase invim invinsertmode invjs invjoinspaces invlnr invlangnoremap invlrm invlangremap invlz invlazyredraw invlbr invlinebreak invlisp invlist invlpl invloadplugins invmagic invml invmodeline invmle invmodelineexpr invma invmodifiable invmod invmodified invmore invmousef invmousefocus invmh invmousehide invmousemev invmousemoveevent invnu invnumber invodev invopendevice invpaste invpi invpreserveindent invpvw invpreviewwindow
 syn keyword vimOption contained invprompt invro invreadonly invrnu invrelativenumber invremap invrs invrestorescreen invri invrevins invrl invrightleft invru invruler invscb invscrollbind invscf invscrollfocus invsecure invssl invshellslash invstmp invshelltemp invsr invshiftround invsn invshortname invsc invshowcmd invsft invshowfulltag invsm invshowmatch invsmd invshowmode invscs invsmartcase invsi invsmartindent invsta invsmarttab invsms invsmoothscroll invspell invsb invsplitbelow invspr invsplitright invsol invstartofline invswf invswapfile invtbs invtagbsearch invtr invtagrelative invtgst invtagstack invtbidi invtermbidi invtgc invtermguicolors invterse invta invtextauto invtx invtextmode invtop invtildeop invto invtimeout invtitle invttimeout invtbi invttybuiltin
-syn keyword vimOption contained invtf invttyfast invudf invundofile invvb invvisualbell invwarn invwiv invweirdinvert invwic invwildignorecase invwmnu invwildmenu invwfb invwinfixbuf invwfh invwinfixheight invwfw invwinfixwidth invwrap invws invwrapscan invwrite invwa invwriteany invwb invwritebackup invxtermcodes
+syn keyword vimOption contained invtf invttyfast invudf invundofile invvb invvisualbell invwarn invwiv invweirdinvert invwic invwildignorecase invwmnu invwildmenu invwfb invwinfixbuf invwfh invwinfixheight invwfw invwinfixwidth invwst invwlsteal invwrap invws invwrapscan invwrite invwa invwriteany invwb invwritebackup invxtermcodes
 " termcap codes (which can also be set) {{{2
 " GEN_SYN_VIM: vimOption term output code, START_STR='syn keyword vimOption contained', END_STR='skipwhite nextgroup=vimSetEqual,vimSetMod'
 syn keyword vimOption contained t_AB t_AF t_AU t_AL t_al t_bc t_BE t_BD t_cd t_ce t_Ce t_CF t_cl t_cm t_Co t_CS t_Cs t_cs t_CV t_da t_db t_DL t_dl t_ds t_Ds t_EC t_EI t_fs t_fd t_fe t_GP t_IE t_IS t_ke t_ks t_le t_mb t_md t_me t_mr t_ms t_nd t_op t_RF t_RB t_RC t_RI t_Ri t_RK t_RS t_RT t_RV t_Sb t_SC t_se t_Sf t_SH t_SI t_Si t_so t_SR t_sr t_ST t_Te t_te t_TE t_ti t_TI t_Ts t_ts t_u7 t_ue t_us t_Us t_ut t_vb t_ve t_vi t_VS t_vs t_WP t_WS t_XM t_xn t_xs t_ZH t_ZR t_8f t_8b t_8u t_xo skipwhite nextgroup=vimSetEqual,vimSetMod
@@ -86,15 +86,15 @@ syn match   vimOption contained     "t_k;"
 
 " vimOptions: These are the variable names {{{2
 " GEN_SYN_VIM: vimOption normal variable,           START_STR='syn keyword vimOptionVarName contained', END_STR=''
-syn keyword vimOptionVarName contained al aleph ari allowrevins ambw ambiwidth arab arabic arshape arabicshape acd autochdir ai autoindent ar autoread asd autoshelldir aw autowrite awa autowriteall bg background bs backspace bk backup bkc backupcopy bdir backupdir bex backupext bsk backupskip bdlay balloondelay beval ballooneval bevalterm balloonevalterm bexpr balloonexpr bo belloff bin binary bomb brk breakat bri breakindent briopt breakindentopt bsdir browsedir bh bufhidden bl buflisted bt buftype cmp casemap cdh cdhome cd cdpath cedit ccv charconvert chi chistory cin cindent cink cinkeys cino cinoptions cinsd cinscopedecls cinw cinwords cb clipboard ch cmdheight cwh cmdwinheight cc colorcolumn co columns com comments cms commentstring cp compatible cpt complete
-syn keyword vimOptionVarName contained cfu completefunc cfc completefuzzycollect cia completeitemalign cot completeopt cpp completepopup csl completeslash cocu concealcursor cole conceallevel cf confirm ci copyindent cpo cpoptions cm cryptmethod cspc cscopepathcomp csprg cscopeprg csqf cscopequickfix csre cscoperelative cst cscopetag csto cscopetagorder csverb cscopeverbose crb cursorbind cuc cursorcolumn cul cursorline culopt cursorlineopt debug def define deco delcombine dict dictionary diff dex diffexpr dip diffopt dg digraph dir directory dy display ead eadirection ed edcompatible emo emoji enc encoding eof endoffile eol endofline ea equalalways ep equalprg eb errorbells ef errorfile efm errorformat ek esckeys ei eventignore eiw eventignorewin et expandtab
-syn keyword vimOptionVarName contained ex exrc fenc fileencoding fencs fileencodings ff fileformat ffs fileformats fic fileignorecase ft filetype fcs fillchars ffu findfunc fixeol fixendofline fcl foldclose fdc foldcolumn fen foldenable fde foldexpr fdi foldignore fdl foldlevel fdls foldlevelstart fmr foldmarker fdm foldmethod fml foldminlines fdn foldnestmax fdo foldopen fdt foldtext fex formatexpr flp formatlistpat fo formatoptions fp formatprg fs fsync gd gdefault gfm grepformat gp grepprg gcr guicursor gfn guifont gfs guifontset gfw guifontwide ghr guiheadroom gli guiligatures go guioptions guipty gtl guitablabel gtt guitabtooltip hf helpfile hh helpheight hlg helplang hid hidden hl highlight hi history hk hkmap hkp hkmapp hls hlsearch icon iconstring ic ignorecase
-syn keyword vimOptionVarName contained imaf imactivatefunc imak imactivatekey imc imcmdline imd imdisable imi iminsert ims imsearch imsf imstatusfunc imst imstyle inc include inex includeexpr is incsearch inde indentexpr indk indentkeys inf infercase im insertmode ise isexpand isf isfname isi isident isk iskeyword isp isprint js joinspaces jop jumpoptions key kmp keymap km keymodel kpc keyprotocol kp keywordprg lmap langmap lm langmenu lnr langnoremap lrm langremap ls laststatus lz lazyredraw lhi lhistory lbr linebreak lines lsp linespace lisp lop lispoptions lw lispwords list lcs listchars lpl loadplugins luadll magic mef makeef menc makeencoding mp makeprg mps matchpairs mat matchtime mco maxcombine mfd maxfuncdepth mmd maxmapdepth mm maxmem mmp maxmempattern
+syn keyword vimOptionVarName contained al aleph ari allowrevins ambw ambiwidth arab arabic arshape arabicshape acd autochdir ai autoindent ar autoread asd autoshelldir aw autowrite awa autowriteall bg background bs backspace bk backup bkc backupcopy bdir backupdir bex backupext bsk backupskip bdlay balloondelay beval ballooneval bevalterm balloonevalterm bexpr balloonexpr bo belloff bin binary bomb brk breakat bri breakindent briopt breakindentopt bsdir browsedir bh bufhidden bl buflisted bt buftype cmp casemap cdh cdhome cd cdpath cedit ccv charconvert chi chistory cin cindent cink cinkeys cino cinoptions cinsd cinscopedecls cinw cinwords cb clipboard cpm clipmethod ch cmdheight cwh cmdwinheight cc colorcolumn co columns com comments cms commentstring cp compatible
+syn keyword vimOptionVarName contained cpt complete cfu completefunc cfc completefuzzycollect cia completeitemalign cot completeopt cpp completepopup csl completeslash cocu concealcursor cole conceallevel cf confirm ci copyindent cpo cpoptions cm cryptmethod cspc cscopepathcomp csprg cscopeprg csqf cscopequickfix csre cscoperelative cst cscopetag csto cscopetagorder csverb cscopeverbose crb cursorbind cuc cursorcolumn cul cursorline culopt cursorlineopt debug def define deco delcombine dict dictionary diff dex diffexpr dip diffopt dg digraph dir directory dy display ead eadirection ed edcompatible emo emoji enc encoding eof endoffile eol endofline ea equalalways ep equalprg eb errorbells ef errorfile efm errorformat ek esckeys ei eventignore eiw eventignorewin
+syn keyword vimOptionVarName contained et expandtab ex exrc fenc fileencoding fencs fileencodings ff fileformat ffs fileformats fic fileignorecase ft filetype fcs fillchars ffu findfunc fixeol fixendofline fcl foldclose fdc foldcolumn fen foldenable fde foldexpr fdi foldignore fdl foldlevel fdls foldlevelstart fmr foldmarker fdm foldmethod fml foldminlines fdn foldnestmax fdo foldopen fdt foldtext fex formatexpr flp formatlistpat fo formatoptions fp formatprg fs fsync gd gdefault gfm grepformat gp grepprg gcr guicursor gfn guifont gfs guifontset gfw guifontwide ghr guiheadroom gli guiligatures go guioptions guipty gtl guitablabel gtt guitabtooltip hf helpfile hh helpheight hlg helplang hid hidden hl highlight hi history hk hkmap hkp hkmapp hls hlsearch icon iconstring
+syn keyword vimOptionVarName contained ic ignorecase imaf imactivatefunc imak imactivatekey imc imcmdline imd imdisable imi iminsert ims imsearch imsf imstatusfunc imst imstyle inc include inex includeexpr is incsearch inde indentexpr indk indentkeys inf infercase im insertmode ise isexpand isf isfname isi isident isk iskeyword isp isprint js joinspaces jop jumpoptions key kmp keymap km keymodel kpc keyprotocol kp keywordprg lmap langmap lm langmenu lnr langnoremap lrm langremap ls laststatus lz lazyredraw lhi lhistory lbr linebreak lines lsp linespace lisp lop lispoptions lw lispwords list lcs listchars lpl loadplugins luadll magic mef makeef menc makeencoding mp makeprg mps matchpairs mat matchtime mco maxcombine mfd maxfuncdepth mmd maxmapdepth mm maxmem mmp maxmempattern
 syn keyword vimOptionVarName contained mmt maxmemtot mis menuitems mopt messagesopt msm mkspellmem ml modeline mle modelineexpr mls modelines ma modifiable mod modified more mouse mousef mousefocus mh mousehide mousem mousemodel mousemev mousemoveevent mouses mouseshape mouset mousetime mzq mzquantum mzschemedll mzschemegcdll nf nrformats nu number nuw numberwidth ofu omnifunc odev opendevice opfunc operatorfunc pp packpath para paragraphs paste pt pastetoggle pex patchexpr pm patchmode pa path perldll pi preserveindent pvh previewheight pvp previewpopup pvw previewwindow pdev printdevice penc printencoding pexpr printexpr pfn printfont pheader printheader pmbcs printmbcharset pmbfn printmbfont popt printoptions prompt ph pumheight pmw pummaxwidth pw pumwidth
 syn keyword vimOptionVarName contained pythondll pythonhome pythonthreedll pythonthreehome pyx pyxversion qftf quickfixtextfunc qe quoteescape ro readonly rdt redrawtime re regexpengine rnu relativenumber remap rop renderoptions report rs restorescreen ri revins rl rightleft rlc rightleftcmd rubydll ru ruler ruf rulerformat rtp runtimepath scr scroll scb scrollbind scf scrollfocus sj scrolljump so scrolloff sbo scrollopt sect sections secure sel selection slm selectmode ssop sessionoptions sh shell shcf shellcmdflag sp shellpipe shq shellquote srr shellredir ssl shellslash stmp shelltemp st shelltype sxe shellxescape sxq shellxquote sr shiftround sw shiftwidth shm shortmess sn shortname sbr showbreak sc showcmd sloc showcmdloc sft showfulltag sm showmatch smd showmode
 syn keyword vimOptionVarName contained stal showtabline stpl showtabpanel ss sidescroll siso sidescrolloff scl signcolumn scs smartcase si smartindent sta smarttab sms smoothscroll sts softtabstop spell spc spellcapcheck spf spellfile spl spelllang spo spelloptions sps spellsuggest sb splitbelow spk splitkeep spr splitright sol startofline stl statusline su suffixes sua suffixesadd swf swapfile sws swapsync swb switchbuf smc synmaxcol syn syntax tcl tabclose tal tabline tpm tabpagemax tpl tabpanel tplo tabpanelopt ts tabstop tbs tagbsearch tc tagcase tfu tagfunc tl taglength tr tagrelative tag tags tgst tagstack tcldll term tbidi termbidi tenc termencoding tgc termguicolors twk termwinkey twsl termwinscroll tws termwinsize twt termwintype terse ta textauto tx textmode
 syn keyword vimOptionVarName contained tw textwidth tsr thesaurus tsrfu thesaurusfunc top tildeop to timeout tm timeoutlen title titlelen titleold titlestring tb toolbar tbis toolbariconsize ttimeout ttm ttimeoutlen tbi ttybuiltin tf ttyfast ttym ttymouse tsl ttyscroll tty ttytype udir undodir udf undofile ul undolevels ur undoreload uc updatecount ut updatetime vsts varsofttabstop vts vartabstop vbs verbose vfile verbosefile vdir viewdir vop viewoptions vi viminfo vif viminfofile ve virtualedit vb visualbell warn wiv weirdinvert ww whichwrap wc wildchar wcm wildcharm wig wildignore wic wildignorecase wmnu wildmenu wim wildmode wop wildoptions wak winaltkeys wcr wincolor wi window wfb winfixbuf wfh winfixheight wfw winfixwidth wh winheight wmh winminheight wmw winminwidth
-syn keyword vimOptionVarName contained winptydll wiw winwidth wrap wm wrapmargin ws wrapscan write wa writeany wb writebackup wd writedelay xtermcodes
+syn keyword vimOptionVarName contained winptydll wiw winwidth wse wlseat wst wlsteal wtm wltimeoutlen wrap wm wrapmargin ws wrapscan write wa writeany wb writebackup wd writedelay xtermcodes
 " GEN_SYN_VIM: vimOption term output code variable, START_STR='syn keyword vimOptionVarName contained', END_STR=''
 syn keyword vimOptionVarName contained t_AB t_AF t_AU t_AL t_al t_bc t_BE t_BD t_cd t_ce t_Ce t_CF t_cl t_cm t_Co t_CS t_Cs t_cs t_CV t_da t_db t_DL t_dl t_ds t_Ds t_EC t_EI t_fs t_fd t_fe t_GP t_IE t_IS t_ke t_ks t_le t_mb t_md t_me t_mr t_ms t_nd t_op t_RF t_RB t_RC t_RI t_Ri t_RK t_RS t_RT t_RV t_Sb t_SC t_se t_Sf t_SH t_SI t_Si t_so t_SR t_sr t_ST t_Te t_te t_TE t_ti t_TI t_Ts t_ts t_u7 t_ue t_us t_Us t_ut t_vb t_ve t_vi t_VS t_vs t_WP t_WS t_XM t_xn t_xs t_ZH t_ZR t_8f t_8b t_8u t_xo
 syn keyword vimOptionVarName contained t_F1 t_F2 t_F3 t_F4 t_F5 t_F6 t_F7 t_F8 t_F9 t_k1 t_K1 t_k2 t_k3 t_K3 t_k4 t_K4 t_k5 t_K5 t_k6 t_K6 t_k7 t_K7 t_k8 t_K8 t_k9 t_K9 t_KA t_kb t_kB t_KB t_KC t_kd t_kD t_KD t_KE t_KF t_KG t_kh t_KH t_kI t_KI t_KJ t_KK t_kl t_KL t_kN t_kP t_kr t_ku
@@ -147,7 +147,7 @@ syn keyword vimFuncName contained win_id2tabwin win_id2win win_move_separator wi
 " Predefined variable names {{{2
 " GEN_SYN_VIM: vimVarName, START_STR='syn keyword vimVimVarName contained', END_STR=''
 syn keyword vimVimVarName contained count count1 prevcount errmsg warningmsg statusmsg shell_error this_session version lnum termresponse fname lang lc_time ctype charconvert_from charconvert_to fname_in fname_out fname_new fname_diff cmdarg foldstart foldend folddashes foldlevel progname servername dying exception throwpoint register cmdbang insertmode val key profiling fcs_reason fcs_choice beval_bufnr beval_winnr beval_winid beval_lnum beval_col beval_text scrollstart swapname swapchoice swapcommand char mouse_win mouse_winid mouse_lnum mouse_col operator searchforward hlsearch oldfiles windowid progpath completed_item option_new option_old option_oldlocal option_oldglobal option_command option_type errors false true none null numbermax numbermin numbersize
-syn keyword vimVimVarName contained vim_did_enter testing t_number t_string t_func t_list t_dict t_float t_bool t_none t_job t_channel t_blob t_class t_object termrfgresp termrbgresp termu7resp termstyleresp termblinkresp event versionlong echospace argv collate exiting colornames sizeofint sizeoflong sizeofpointer maxcol python3_version t_typealias t_enum t_enumvalue stacktrace t_tuple
+syn keyword vimVimVarName contained vim_did_enter testing t_number t_string t_func t_list t_dict t_float t_bool t_none t_job t_channel t_blob t_class t_object termrfgresp termrbgresp termu7resp termstyleresp termblinkresp event versionlong echospace argv collate exiting colornames sizeofint sizeoflong sizeofpointer maxcol python3_version t_typealias t_enum t_enumvalue stacktrace t_tuple wayland_display clipmethod
 
 "--- syntax here and above generated by runtime/syntax/generator/gen_syntax_vim.vim ---
 
index 9a1a1ccf9f6c5e2a9e9b07b13a8385667b7a03de..3f305752deab6fb842e08056ad2f445b6eba7b67 100644 (file)
@@ -1404,6 +1404,7 @@ ALL_LIBS = \
           $(GUI_LIBS2) \
           $(X_PRE_LIBS) \
           $(X_LIBS) \
+          $(WAYLAND_LIBS) \
           $(X_EXTRA_LIBS) \
           $(MZSCHEME_LIBS) \
           $(LIBS) \
@@ -1614,13 +1615,23 @@ SRC =   $(BASIC_SRC) \
        $(PERL_SRC) \
        $(PYTHON_SRC) $(PYTHON3_SRC) \
        $(TCL_SRC) \
-       $(RUBY_SRC)
+       $(RUBY_SRC) \
+       $(WAYLAND_SRC)
 
 EXTRA_SRC = if_lua.c if_mzsch.c auto/if_perl.c if_perlsfio.c \
            if_python.c if_python3.c if_tcl.c if_ruby.c \
            gui_beval.c netbeans.c job.c channel.c \
            $(GRESOURCE_SRC)
 
+$(WAYLAND_SRC):
+       cd auto/wayland; $(MAKE)
+
+# Needed for parallel jobs to work
+auto/wayland/ext-data-control-v1.h: auto/wayland/ext-data-control-v1.c
+auto/wayland/wlr-data-control-unstable-v1.h: auto/wayland/wlr-data-control-unstable-v1.c
+auto/wayland/primary-selection-unstable-v1.h: auto/wayland/primary-selection-unstable-v1.c
+auto/wayland/xdg-shell.h: auto/wayland/xdg-shell.c
+
 # Unittest files
 JSON_TEST_SRC = json_test.c
 JSON_TEST_TARGET = json_test$(EXEEXT)
@@ -1636,7 +1647,8 @@ UNITTEST_TARGETS = $(JSON_TEST_TARGET) $(KWORD_TEST_TARGET) $(MEMFILE_TEST_TARGE
 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)
+ALL_LOCAL_SRC = $(BASIC_SRC) $(ALL_GUI_SRC) $(UNITTEST_SRC) $(EXTRA_SRC) \
+               $(WAYLAND_SRC)
 ALL_SRC = $(ALL_LOCAL_SRC) $(TERM_SRC) $(XDIFF_SRC)
 
 # Which files to check with lint.  Select one of these three lines.  ALL_SRC
@@ -1777,7 +1789,8 @@ OBJ_COMMON = \
        $(OS_EXTRA_OBJ) \
        $(NETBEANS_OBJ) \
        $(CHANNEL_OBJ) \
-       $(XDIFF_OBJS_USED)
+       $(XDIFF_OBJS_USED) \
+       $(WAYLAND_OBJ)
 
 # The files included by tests are not in OBJ_COMMON.
 OBJ_MAIN = \
@@ -2954,6 +2967,7 @@ clean celan: testclean
                cd $(PODIR); $(MAKE) prefix=$(DESTDIR)$(prefix) clean; \
        fi
        cd xxd; $(MAKE) clean
+       cd auto/wayland; $(MAKE) clean
 
 # Make a shadow directory for compilation on another system or with different
 # features:
@@ -2982,6 +2996,8 @@ shadow:   runtime pixmaps
        $(MKDIR_P) $(SHADOWDIR)
        cd $(SHADOWDIR); ln -s $(LINKEDFILES) .
        mkdir $(SHADOWDIR)/auto
+       mkdir $(SHADOWDIR)/auto/wayland
+       cd $(SHADOWDIR)/auto/wayland; ln -s ../../../auto/wayland/* .
        cd $(SHADOWDIR)/auto; ln -s ../../auto/configure .
        $(MKDIR_P) $(SHADOWDIR)/po
        cd $(SHADOWDIR)/po; ln -s ../../po/*.po ../../po/*.mak ../../po/*.vim ../../po/*.in ../../po/Makefile ../../po/*.c .
@@ -3628,6 +3644,21 @@ objects/viminfo.o: viminfo.c
 objects/window.o: window.c
        $(CCC) -o $@ window.c
 
+objects/wayland.o: wayland.c
+       $(CCC) -o $@ wayland.c
+
+objects/wlr-data-control-unstable-v1.o: auto/wayland/wlr-data-control-unstable-v1.c
+       $(CCC) -o $@ auto/wayland/wlr-data-control-unstable-v1.c
+
+objects/ext-data-control-v1.o: auto/wayland/ext-data-control-v1.c
+       $(CCC) -o $@ auto/wayland/ext-data-control-v1.c
+
+objects/xdg-shell.o: auto/wayland/xdg-shell.c
+       $(CCC) -o $@ auto/wayland/xdg-shell.c
+
+objects/primary-selection-unstable-v1.o: auto/wayland/primary-selection-unstable-v1.c
+       $(CCC) -o $@ auto/wayland/primary-selection-unstable-v1.c
+
 objects/netbeans.o: netbeans.c
        $(CCC) -o $@ netbeans.c
 
@@ -4500,6 +4531,20 @@ objects/channel.o: channel.c vim.h protodef.h auto/config.h feature.h os_unix.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/wlr-data-control-unstable-v1.o: \
+ auto/wayland/wlr-data-control-unstable-v1.c
+objects/ext-data-control-v1.o: auto/wayland/ext-data-control-v1.c
+objects/xdg-shell.o: auto/wayland/xdg-shell.c
+objects/primary-selection-unstable-v1.o: \
+ auto/wayland/primary-selection-unstable-v1.c
+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 \
+ 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
 objects/gui_gtk_gresources.o: auto/gui_gtk_gresources.c
 objects/vterm_encoding.o: libvterm/src/encoding.c libvterm/src/vterm_internal.h \
  libvterm/include/vterm.h libvterm/include/vterm_keycodes.h \
index 8fb31e7904bfe5d33055ef7da4049faabd2ca3e1..731e0f143c531b01e68c8e901a800f5b7245036d 100755 (executable)
@@ -676,6 +676,9 @@ X_PRE_LIBS
 X_CFLAGS
 XMKMF
 xmkmfpath
+WAYLAND_OBJ
+WAYLAND_SRC
+WAYLAND_LIBS
 TERM_TEST
 TERM_OBJ
 TERM_SRC
@@ -854,6 +857,7 @@ enable_arabic
 enable_farsi
 enable_xim
 enable_fontset
+with_wayland
 with_x
 enable_gui
 enable_gtk2_check
@@ -1575,6 +1579,7 @@ Optional Packages:
   --with-python3-config-dir=PATH  Python's config directory (deprecated)
   --with-tclsh=PATH       which tclsh to use (default: tclsh8.0)
   --with-ruby-command=RUBY  name of the Ruby command (default: ruby)
+  --with-wayland         Include support for the Wayland protocol.
   --with-x                use the X Window System
   --with-gnome-includes=DIR Specify location of GNOME headers
   --with-gnome-libs=DIR   Specify location of GNOME libs
@@ -9058,6 +9063,158 @@ fi
 { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $enable_fontset" >&5
 printf "%s\n" "$enable_fontset" >&6; }
 
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if shm_open is available" >&5
+printf %s "checking if shm_open is available... " >&6; }
+cppflags_save=$CPPFLAGS
+CPPFLAGS="$CPPFLAGS $X_CFLAGS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <sys/mman.h>
+                   #include <sys/stat.h>
+                   #include <fcntl.h>
+int
+main (void)
+{
+shm_open("/test", O_CREAT, 0600);
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }; printf "%s\n" "#define HAVE_SHM_OPEN 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
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+CPPFLAGS=$cppflags_save
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking --with-wayland argument" >&5
+printf %s "checking --with-wayland argument... " >&6; }
+
+# Check whether --with-wayland was given.
+if test ${with_wayland+y}
+then :
+  withval=$with_wayland;
+fi
+
+
+test -z "$with_wayland" && with_wayland=yes
+if test "$with_wayland" = yes; 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 if wayland client header files can be found" >&5
+printf %s "checking if wayland client header files can be found... " >&6; }
+  cppflags_save=$CPPFLAGS
+  CPPFLAGS="$CPPFLAGS $X_CFLAGS"
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <wayland-client.h>
+int
+main (void)
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+else case e in #(
+  e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }; no_wl=yes ;;
+esac
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+  CPPFLAGS=$cppflags_save
+
+  if test "$no_wl" = yes; then
+    with_wayland=no
+  else
+    { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for wl_display_connect in -lwayland-client" >&5
+printf %s "checking for wl_display_connect in -lwayland-client... " >&6; }
+if test ${ac_cv_lib_wayland_client_wl_display_connect+y}
+then :
+  printf %s "(cached) " >&6
+else case e in #(
+  e) ac_check_lib_save_LIBS=$LIBS
+LIBS="-lwayland-client  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.
+   The 'extern "C"' is for builds by C++ compilers;
+   although this is not generally supported in C code supporting it here
+   has little cost and some practical benefit (sr 110532).  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char wl_display_connect (void);
+int
+main (void)
+{
+return wl_display_connect ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"
+then :
+  ac_cv_lib_wayland_client_wl_display_connect=yes
+else case e in #(
+  e) ac_cv_lib_wayland_client_wl_display_connect=no ;;
+esac
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS ;;
+esac
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_wayland_client_wl_display_connect" >&5
+printf "%s\n" "$ac_cv_lib_wayland_client_wl_display_connect" >&6; }
+if test "x$ac_cv_lib_wayland_client_wl_display_connect" = xyes
+then :
+  no_wl=no
+fi
+
+
+    if test "$no_wl" = no; then
+      printf "%s\n" "#define HAVE_WAYLAND 1" >>confdefs.h
+
+      WAYLAND_LIBS="-lwayland-client";
+
+      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"
+
+
+    else
+      with_wayland=no
+    fi
+  fi
+else
+  { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
 test -z "$with_x" && with_x=yes
 test "${enable_gui-yes}" != no -a "x$MACOS_X" != "xyes" -a "x$QNX" != "xyes" && with_x=yes
 if test "$with_x" = no; then
diff --git a/src/auto/wayland/Makefile b/src/auto/wayland/Makefile
new file mode 100644 (file)
index 0000000..e88b3b8
--- /dev/null
@@ -0,0 +1,52 @@
+# List of files to generate
+GEN_XML = protocols/ext-data-control-v1.xml \
+           protocols/primary-selection-unstable-v1.xml \
+           protocols/wlr-data-control-unstable-v1.xml \
+           protocols/xdg-shell.xml
+GEN_SRC = ext-data-control-v1.c \
+           primary-selection-unstable-v1.c \
+           wlr-data-control-unstable-v1.c \
+           xdg-shell.c
+GEN_INCLUDE = ext-data-control-v1.h \
+           primary-selection-unstable-v1.h \
+           wlr-data-control-unstable-v1.h \
+           xdg-shell.h
+
+# Default target
+all: $(GEN_SRC) $(GEN_INCLUDE)
+       @if ! command -v wayland-scanner 2>&1 > /dev/null ; then \
+               echo "wayland-scanner not available, cannot generate protocol files"; \
+               false; \
+       fi
+
+ext-data-control-v1.c:
+       wayland-scanner private-code protocols/ext-data-control-v1.xml $@
+ext-data-control-v1.h:
+       wayland-scanner client-header protocols/ext-data-control-v1.xml $@
+
+wlr-data-control-unstable-v1.c:
+       wayland-scanner private-code protocols/wlr-data-control-unstable-v1.xml $@
+wlr-data-control-unstable-v1.h:
+       wayland-scanner client-header protocols/wlr-data-control-unstable-v1.xml $@
+
+primary-selection-unstable-v1.c:
+       wayland-scanner private-code protocols/primary-selection-unstable-v1.xml $@
+primary-selection-unstable-v1.h:
+       wayland-scanner client-header protocols/primary-selection-unstable-v1.xml $@
+
+xdg-shell.c:
+       wayland-scanner private-code protocols/xdg-shell.xml $@
+xdg-shell.h:
+       wayland-scanner client-header protocols/xdg-shell.xml $@
+
+$(GEN_SRC) $(GEN_INCLUDE): $(GEN_XML)
+
+$(GEN_XML):
+
+# Clean rule
+clean:
+       rm -f *.c *.h
+
+.PHONY: all clean
+
+# vim:ts=8:sw=8:tw=78
diff --git a/src/auto/wayland/README.txt b/src/auto/wayland/README.txt
new file mode 100644 (file)
index 0000000..ee0de0a
--- /dev/null
@@ -0,0 +1,11 @@
+This directory contains the auto-generated protocol files for the Wayland
+System Integration.
+
+To re-generate them run make.
+
+It requires wayland-scanner to be installed, which is generally found as
+wayland-utils package in Linux distributions.
+
+Included as of Vim patch v9.1.1485 (2025 Jun 27).
+
+Initial work done by Foxe Chen.
diff --git a/src/auto/wayland/protocols/ext-data-control-v1.xml b/src/auto/wayland/protocols/ext-data-control-v1.xml
new file mode 100644 (file)
index 0000000..37ee577
--- /dev/null
@@ -0,0 +1,276 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<protocol name="ext_data_control_v1">
+  <copyright>
+    Copyright © 2018 Simon Ser
+    Copyright © 2019 Ivan Molodetskikh
+    Copyright © 2024 Neal Gompa
+
+    Permission to use, copy, modify, distribute, and sell this
+    software and its documentation for any purpose is hereby granted
+    without fee, provided that the above copyright notice appear in
+    all copies and that both that copyright notice and this permission
+    notice appear in supporting documentation, and that the name of
+    the copyright holders not be used in advertising or publicity
+    pertaining to distribution of the software without specific,
+    written prior permission.  The copyright holders make no
+    representations about the suitability of this software for any
+    purpose.  It is provided "as is" without express or implied
+    warranty.
+
+    THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+    SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+    FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+    SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+    WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+    AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+    ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+    THIS SOFTWARE.
+  </copyright>
+
+  <description summary="control data devices">
+    This protocol allows a privileged client to control data devices. In
+    particular, the client will be able to manage the current selection and take
+    the role of a clipboard manager.
+
+    Warning! The protocol described in this file is currently in the testing
+    phase. Backward compatible changes may be added together with the
+    corresponding interface version bump. Backward incompatible changes can
+    only be done by creating a new major version of the extension.
+  </description>
+
+  <interface name="ext_data_control_manager_v1" version="1">
+    <description summary="manager to control data devices">
+      This interface is a manager that allows creating per-seat data device
+      controls.
+    </description>
+
+    <request name="create_data_source">
+      <description summary="create a new data source">
+        Create a new data source.
+      </description>
+      <arg name="id" type="new_id" interface="ext_data_control_source_v1"
+        summary="data source to create"/>
+    </request>
+
+    <request name="get_data_device">
+      <description summary="get a data device for a seat">
+        Create a data device that can be used to manage a seat's selection.
+      </description>
+      <arg name="id" type="new_id" interface="ext_data_control_device_v1"/>
+      <arg name="seat" type="object" interface="wl_seat"/>
+    </request>
+
+    <request name="destroy" type="destructor">
+      <description summary="destroy the manager">
+        All objects created by the manager will still remain valid, until their
+        appropriate destroy request has been called.
+      </description>
+    </request>
+  </interface>
+
+  <interface name="ext_data_control_device_v1" version="1">
+    <description summary="manage a data device for a seat">
+      This interface allows a client to manage a seat's selection.
+
+      When the seat is destroyed, this object becomes inert.
+    </description>
+
+    <request name="set_selection">
+      <description summary="copy data to the selection">
+        This request asks the compositor to set the selection to the data from
+        the source on behalf of the client.
+
+        The given source may not be used in any further set_selection or
+        set_primary_selection requests. Attempting to use a previously used
+        source triggers the used_source protocol error.
+
+        To unset the selection, set the source to NULL.
+      </description>
+      <arg name="source" type="object" interface="ext_data_control_source_v1"
+        allow-null="true"/>
+    </request>
+
+    <request name="destroy" type="destructor">
+      <description summary="destroy this data device">
+        Destroys the data device object.
+      </description>
+    </request>
+
+    <event name="data_offer">
+      <description summary="introduce a new ext_data_control_offer">
+        The data_offer event introduces a new ext_data_control_offer object,
+        which will subsequently be used in either the
+        ext_data_control_device.selection event (for the regular clipboard
+        selections) or the ext_data_control_device.primary_selection event (for
+        the primary clipboard selections). Immediately following the
+        ext_data_control_device.data_offer event, the new data_offer object
+        will send out ext_data_control_offer.offer events to describe the MIME
+        types it offers.
+      </description>
+      <arg name="id" type="new_id" interface="ext_data_control_offer_v1"/>
+    </event>
+
+    <event name="selection">
+      <description summary="advertise new selection">
+        The selection event is sent out to notify the client of a new
+        ext_data_control_offer for the selection for this device. The
+        ext_data_control_device.data_offer and the ext_data_control_offer.offer
+        events are sent out immediately before this event to introduce the data
+        offer object. The selection event is sent to a client when a new
+        selection is set. The ext_data_control_offer is valid until a new
+        ext_data_control_offer or NULL is received. The client must destroy the
+        previous selection ext_data_control_offer, if any, upon receiving this
+        event. Regardless, the previous selection will be ignored once a new
+        selection ext_data_control_offer is received.
+
+        The first selection event is sent upon binding the
+        ext_data_control_device object.
+      </description>
+      <arg name="id" type="object" interface="ext_data_control_offer_v1"
+        allow-null="true"/>
+    </event>
+
+    <event name="finished">
+      <description summary="this data control is no longer valid">
+        This data control object is no longer valid and should be destroyed by
+        the client.
+      </description>
+    </event>
+
+    <event name="primary_selection">
+      <description summary="advertise new primary selection">
+        The primary_selection event is sent out to notify the client of a new
+        ext_data_control_offer for the primary selection for this device. The
+        ext_data_control_device.data_offer and the ext_data_control_offer.offer
+        events are sent out immediately before this event to introduce the data
+        offer object. The primary_selection event is sent to a client when a
+        new primary selection is set. The ext_data_control_offer is valid until
+        a new ext_data_control_offer or NULL is received. The client must
+        destroy the previous primary selection ext_data_control_offer, if any,
+        upon receiving this event. Regardless, the previous primary selection
+        will be ignored once a new primary selection ext_data_control_offer is
+        received.
+
+        If the compositor supports primary selection, the first
+        primary_selection event is sent upon binding the
+        ext_data_control_device object.
+      </description>
+      <arg name="id" type="object" interface="ext_data_control_offer_v1"
+        allow-null="true"/>
+    </event>
+
+    <request name="set_primary_selection">
+      <description summary="copy data to the primary selection">
+        This request asks the compositor to set the primary selection to the
+        data from the source on behalf of the client.
+
+        The given source may not be used in any further set_selection or
+        set_primary_selection requests. Attempting to use a previously used
+        source triggers the used_source protocol error.
+
+        To unset the primary selection, set the source to NULL.
+
+        The compositor will ignore this request if it does not support primary
+        selection.
+      </description>
+      <arg name="source" type="object" interface="ext_data_control_source_v1"
+        allow-null="true"/>
+    </request>
+
+    <enum name="error">
+      <entry name="used_source" value="1"
+        summary="source given to set_selection or set_primary_selection was already used before"/>
+    </enum>
+  </interface>
+
+  <interface name="ext_data_control_source_v1" version="1">
+    <description summary="offer to transfer data">
+      The ext_data_control_source object is the source side of a
+      ext_data_control_offer. It is created by the source client in a data
+      transfer and provides a way to describe the offered data and a way to
+      respond to requests to transfer the data.
+    </description>
+
+    <enum name="error">
+      <entry name="invalid_offer" value="1"
+        summary="offer sent after ext_data_control_device.set_selection"/>
+    </enum>
+
+    <request name="offer">
+      <description summary="add an offered MIME type">
+        This request adds a MIME type to the set of MIME types advertised to
+        targets. Can be called several times to offer multiple types.
+
+        Calling this after ext_data_control_device.set_selection is a protocol
+        error.
+      </description>
+      <arg name="mime_type" type="string"
+        summary="MIME type offered by the data source"/>
+    </request>
+
+    <request name="destroy" type="destructor">
+      <description summary="destroy this source">
+        Destroys the data source object.
+      </description>
+    </request>
+
+    <event name="send">
+      <description summary="send the data">
+        Request for data from the client. Send the data as the specified MIME
+        type over the passed file descriptor, then close it.
+      </description>
+      <arg name="mime_type" type="string" summary="MIME type for the data"/>
+      <arg name="fd" type="fd" summary="file descriptor for the data"/>
+    </event>
+
+    <event name="cancelled">
+      <description summary="selection was cancelled">
+        This data source is no longer valid. The data source has been replaced
+        by another data source.
+
+        The client should clean up and destroy this data source.
+      </description>
+    </event>
+  </interface>
+
+  <interface name="ext_data_control_offer_v1" version="1">
+    <description summary="offer to transfer data">
+      A ext_data_control_offer represents a piece of data offered for transfer
+      by another client (the source client). The offer describes the different
+      MIME types that the data can be converted to and provides the mechanism
+      for transferring the data directly from the source client.
+    </description>
+
+    <request name="receive">
+      <description summary="request that the data is transferred">
+        To transfer the offered data, the client issues this request and
+        indicates the MIME type it wants to receive. The transfer happens
+        through the passed file descriptor (typically created with the pipe
+        system call). The source client writes the data in the MIME type
+        representation requested and then closes the file descriptor.
+
+        The receiving client reads from the read end of the pipe until EOF and
+        then closes its end, at which point the transfer is complete.
+
+        This request may happen multiple times for different MIME types.
+      </description>
+      <arg name="mime_type" type="string"
+        summary="MIME type desired by receiver"/>
+      <arg name="fd" type="fd" summary="file descriptor for data transfer"/>
+    </request>
+
+    <request name="destroy" type="destructor">
+      <description summary="destroy this offer">
+        Destroys the data offer object.
+      </description>
+    </request>
+
+    <event name="offer">
+      <description summary="advertise offered MIME type">
+        Sent immediately after creating the ext_data_control_offer object.
+        One event per offered MIME type.
+      </description>
+      <arg name="mime_type" type="string" summary="offered MIME type"/>
+    </event>
+  </interface>
+</protocol>
diff --git a/src/auto/wayland/protocols/primary-selection-unstable-v1.xml b/src/auto/wayland/protocols/primary-selection-unstable-v1.xml
new file mode 100644 (file)
index 0000000..e5a39e3
--- /dev/null
@@ -0,0 +1,225 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<protocol name="wp_primary_selection_unstable_v1">
+  <copyright>
+    Copyright © 2015, 2016 Red Hat
+
+    Permission is hereby granted, free of charge, to any person obtaining a
+    copy of this software and associated documentation files (the "Software"),
+    to deal in the Software without restriction, including without limitation
+    the rights to use, copy, modify, merge, publish, distribute, sublicense,
+    and/or sell copies of the Software, and to permit persons to whom the
+    Software is furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice (including the next
+    paragraph) shall be included in all copies or substantial portions of the
+    Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+    THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+    DEALINGS IN THE SOFTWARE.
+  </copyright>
+
+  <description summary="Primary selection protocol">
+    This protocol provides the ability to have a primary selection device to
+    match that of the X server. This primary selection is a shortcut to the
+    common clipboard selection, where text just needs to be selected in order
+    to allow copying it elsewhere. The de facto way to perform this action
+    is the middle mouse button, although it is not limited to this one.
+
+    Clients wishing to honor primary selection should create a primary
+    selection source and set it as the selection through
+    wp_primary_selection_device.set_selection whenever the text selection
+    changes. In order to minimize calls in pointer-driven text selection,
+    it should happen only once after the operation finished. Similarly,
+    a NULL source should be set when text is unselected.
+
+    wp_primary_selection_offer objects are first announced through the
+    wp_primary_selection_device.data_offer event. Immediately after this event,
+    the primary data offer will emit wp_primary_selection_offer.offer events
+    to let know of the mime types being offered.
+
+    When the primary selection changes, the client with the keyboard focus
+    will receive wp_primary_selection_device.selection events. Only the client
+    with the keyboard focus will receive such events with a non-NULL
+    wp_primary_selection_offer. Across keyboard focus changes, previously
+    focused clients will receive wp_primary_selection_device.events with a
+    NULL wp_primary_selection_offer.
+
+    In order to request the primary selection data, the client must pass
+    a recent serial pertaining to the press event that is triggering the
+    operation, if the compositor deems the serial valid and recent, the
+    wp_primary_selection_source.send event will happen in the other end
+    to let the transfer begin. The client owning the primary selection
+    should write the requested data, and close the file descriptor
+    immediately.
+
+    If the primary selection owner client disappeared during the transfer,
+    the client reading the data will receive a
+    wp_primary_selection_device.selection event with a NULL
+    wp_primary_selection_offer, the client should take this as a hint
+    to finish the reads related to the no longer existing offer.
+
+    The primary selection owner should be checking for errors during
+    writes, merely cancelling the ongoing transfer if any happened.
+  </description>
+
+  <interface name="zwp_primary_selection_device_manager_v1" version="1">
+    <description summary="X primary selection emulation">
+      The primary selection device manager is a singleton global object that
+      provides access to the primary selection. It allows to create
+      wp_primary_selection_source objects, as well as retrieving the per-seat
+      wp_primary_selection_device objects.
+    </description>
+
+    <request name="create_source">
+      <description summary="create a new primary selection source">
+        Create a new primary selection source.
+      </description>
+      <arg name="id" type="new_id" interface="zwp_primary_selection_source_v1"/>
+    </request>
+
+    <request name="get_device">
+      <description summary="create a new primary selection device">
+        Create a new data device for a given seat.
+      </description>
+      <arg name="id" type="new_id" interface="zwp_primary_selection_device_v1"/>
+      <arg name="seat" type="object" interface="wl_seat"/>
+    </request>
+
+    <request name="destroy" type="destructor">
+      <description summary="destroy the primary selection device manager">
+        Destroy the primary selection device manager.
+      </description>
+    </request>
+  </interface>
+
+  <interface name="zwp_primary_selection_device_v1" version="1">
+    <request name="set_selection">
+      <description summary="set the primary selection">
+        Replaces the current selection. The previous owner of the primary
+        selection will receive a wp_primary_selection_source.cancelled event.
+
+        To unset the selection, set the source to NULL.
+      </description>
+      <arg name="source" type="object" interface="zwp_primary_selection_source_v1" allow-null="true"/>
+      <arg name="serial" type="uint" summary="serial of the event that triggered this request"/>
+    </request>
+
+    <event name="data_offer">
+      <description summary="introduce a new wp_primary_selection_offer">
+        Introduces a new wp_primary_selection_offer object that may be used
+        to receive the current primary selection. Immediately following this
+        event, the new wp_primary_selection_offer object will send
+        wp_primary_selection_offer.offer events to describe the offered mime
+        types.
+      </description>
+      <arg name="offer" type="new_id" interface="zwp_primary_selection_offer_v1"/>
+    </event>
+
+    <event name="selection">
+      <description summary="advertise a new primary selection">
+        The wp_primary_selection_device.selection event is sent to notify the
+        client of a new primary selection. This event is sent after the
+        wp_primary_selection.data_offer event introducing this object, and after
+        the offer has announced its mimetypes through
+        wp_primary_selection_offer.offer.
+
+        The data_offer is valid until a new offer or NULL is received
+        or until the client loses keyboard focus. The client must destroy the
+        previous selection data_offer, if any, upon receiving this event.
+      </description>
+      <arg name="id" type="object" interface="zwp_primary_selection_offer_v1" allow-null="true"/>
+    </event>
+
+    <request name="destroy" type="destructor">
+      <description summary="destroy the primary selection device">
+        Destroy the primary selection device.
+      </description>
+    </request>
+  </interface>
+
+  <interface name="zwp_primary_selection_offer_v1" version="1">
+    <description summary="offer to transfer primary selection contents">
+      A wp_primary_selection_offer represents an offer to transfer the contents
+      of the primary selection clipboard to the client. Similar to
+      wl_data_offer, the offer also describes the mime types that the data can
+      be converted to and provides the mechanisms for transferring the data
+      directly to the client.
+    </description>
+
+    <request name="receive">
+      <description summary="request that the data is transferred">
+        To transfer the contents of the primary selection clipboard, the client
+        issues this request and indicates the mime type that it wants to
+        receive. The transfer happens through the passed file descriptor
+        (typically created with the pipe system call). The source client writes
+        the data in the mime type representation requested and then closes the
+        file descriptor.
+
+        The receiving client reads from the read end of the pipe until EOF and
+        closes its end, at which point the transfer is complete.
+      </description>
+      <arg name="mime_type" type="string"/>
+      <arg name="fd" type="fd"/>
+    </request>
+
+    <request name="destroy" type="destructor">
+      <description summary="destroy the primary selection offer">
+        Destroy the primary selection offer.
+      </description>
+    </request>
+
+    <event name="offer">
+      <description summary="advertise offered mime type">
+        Sent immediately after creating announcing the
+        wp_primary_selection_offer through
+        wp_primary_selection_device.data_offer. One event is sent per offered
+        mime type.
+      </description>
+      <arg name="mime_type" type="string"/>
+    </event>
+  </interface>
+
+  <interface name="zwp_primary_selection_source_v1" version="1">
+    <description summary="offer to replace the contents of the primary selection">
+      The source side of a wp_primary_selection_offer, it provides a way to
+      describe the offered data and respond to requests to transfer the
+      requested contents of the primary selection clipboard.
+    </description>
+
+    <request name="offer">
+      <description summary="add an offered mime type">
+        This request adds a mime type to the set of mime types advertised to
+        targets. Can be called several times to offer multiple types.
+      </description>
+      <arg name="mime_type" type="string"/>
+    </request>
+
+    <request name="destroy" type="destructor">
+      <description summary="destroy the primary selection source">
+        Destroy the primary selection source.
+      </description>
+    </request>
+
+    <event name="send">
+      <description summary="send the primary selection contents">
+        Request for the current primary selection contents from the client.
+        Send the specified mime type over the passed file descriptor, then
+        close it.
+      </description>
+      <arg name="mime_type" type="string"/>
+      <arg name="fd" type="fd"/>
+    </event>
+
+    <event name="cancelled">
+      <description summary="request for primary selection contents was canceled">
+        This primary selection source is no longer valid. The client should
+        clean up and destroy this primary selection source.
+      </description>
+    </event>
+  </interface>
+</protocol>
diff --git a/src/auto/wayland/protocols/wlr-data-control-unstable-v1.xml b/src/auto/wayland/protocols/wlr-data-control-unstable-v1.xml
new file mode 100644 (file)
index 0000000..75e8671
--- /dev/null
@@ -0,0 +1,278 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<protocol name="wlr_data_control_unstable_v1">
+  <copyright>
+    Copyright © 2018 Simon Ser
+    Copyright © 2019 Ivan Molodetskikh
+
+    Permission to use, copy, modify, distribute, and sell this
+    software and its documentation for any purpose is hereby granted
+    without fee, provided that the above copyright notice appear in
+    all copies and that both that copyright notice and this permission
+    notice appear in supporting documentation, and that the name of
+    the copyright holders not be used in advertising or publicity
+    pertaining to distribution of the software without specific,
+    written prior permission.  The copyright holders make no
+    representations about the suitability of this software for any
+    purpose.  It is provided "as is" without express or implied
+    warranty.
+
+    THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+    SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+    FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+    SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+    WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+    AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+    ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+    THIS SOFTWARE.
+  </copyright>
+
+  <description summary="control data devices">
+    This protocol allows a privileged client to control data devices. In
+    particular, the client will be able to manage the current selection and take
+    the role of a clipboard manager.
+
+    Warning! The protocol described in this file is experimental and
+    backward incompatible changes may be made. Backward compatible changes
+    may be added together with the corresponding interface version bump.
+    Backward incompatible changes are done by bumping the version number in
+    the protocol and interface names and resetting the interface version.
+    Once the protocol is to be declared stable, the 'z' prefix and the
+    version number in the protocol and interface names are removed and the
+    interface version number is reset.
+  </description>
+
+  <interface name="zwlr_data_control_manager_v1" version="2">
+    <description summary="manager to control data devices">
+      This interface is a manager that allows creating per-seat data device
+      controls.
+    </description>
+
+    <request name="create_data_source">
+      <description summary="create a new data source">
+        Create a new data source.
+      </description>
+      <arg name="id" type="new_id" interface="zwlr_data_control_source_v1"
+        summary="data source to create"/>
+    </request>
+
+    <request name="get_data_device">
+      <description summary="get a data device for a seat">
+        Create a data device that can be used to manage a seat's selection.
+      </description>
+      <arg name="id" type="new_id" interface="zwlr_data_control_device_v1"/>
+      <arg name="seat" type="object" interface="wl_seat"/>
+    </request>
+
+    <request name="destroy" type="destructor">
+      <description summary="destroy the manager">
+        All objects created by the manager will still remain valid, until their
+        appropriate destroy request has been called.
+      </description>
+    </request>
+  </interface>
+
+  <interface name="zwlr_data_control_device_v1" version="2">
+    <description summary="manage a data device for a seat">
+      This interface allows a client to manage a seat's selection.
+
+      When the seat is destroyed, this object becomes inert.
+    </description>
+
+    <request name="set_selection">
+      <description summary="copy data to the selection">
+        This request asks the compositor to set the selection to the data from
+        the source on behalf of the client.
+
+        The given source may not be used in any further set_selection or
+        set_primary_selection requests. Attempting to use a previously used
+        source is a protocol error.
+
+        To unset the selection, set the source to NULL.
+      </description>
+      <arg name="source" type="object" interface="zwlr_data_control_source_v1"
+        allow-null="true"/>
+    </request>
+
+    <request name="destroy" type="destructor">
+      <description summary="destroy this data device">
+        Destroys the data device object.
+      </description>
+    </request>
+
+    <event name="data_offer">
+      <description summary="introduce a new wlr_data_control_offer">
+        The data_offer event introduces a new wlr_data_control_offer object,
+        which will subsequently be used in either the
+        wlr_data_control_device.selection event (for the regular clipboard
+        selections) or the wlr_data_control_device.primary_selection event (for
+        the primary clipboard selections). Immediately following the
+        wlr_data_control_device.data_offer event, the new data_offer object
+        will send out wlr_data_control_offer.offer events to describe the MIME
+        types it offers.
+      </description>
+      <arg name="id" type="new_id" interface="zwlr_data_control_offer_v1"/>
+    </event>
+
+    <event name="selection">
+      <description summary="advertise new selection">
+        The selection event is sent out to notify the client of a new
+        wlr_data_control_offer for the selection for this device. The
+        wlr_data_control_device.data_offer and the wlr_data_control_offer.offer
+        events are sent out immediately before this event to introduce the data
+        offer object. The selection event is sent to a client when a new
+        selection is set. The wlr_data_control_offer is valid until a new
+        wlr_data_control_offer or NULL is received. The client must destroy the
+        previous selection wlr_data_control_offer, if any, upon receiving this
+        event.
+
+        The first selection event is sent upon binding the
+        wlr_data_control_device object.
+      </description>
+      <arg name="id" type="object" interface="zwlr_data_control_offer_v1"
+        allow-null="true"/>
+    </event>
+
+    <event name="finished">
+      <description summary="this data control is no longer valid">
+        This data control object is no longer valid and should be destroyed by
+        the client.
+      </description>
+    </event>
+
+    <!-- Version 2 additions -->
+
+    <event name="primary_selection" since="2">
+      <description summary="advertise new primary selection">
+        The primary_selection event is sent out to notify the client of a new
+        wlr_data_control_offer for the primary selection for this device. The
+        wlr_data_control_device.data_offer and the wlr_data_control_offer.offer
+        events are sent out immediately before this event to introduce the data
+        offer object. The primary_selection event is sent to a client when a
+        new primary selection is set. The wlr_data_control_offer is valid until
+        a new wlr_data_control_offer or NULL is received. The client must
+        destroy the previous primary selection wlr_data_control_offer, if any,
+        upon receiving this event.
+
+        If the compositor supports primary selection, the first
+        primary_selection event is sent upon binding the
+        wlr_data_control_device object.
+      </description>
+      <arg name="id" type="object" interface="zwlr_data_control_offer_v1"
+        allow-null="true"/>
+    </event>
+
+    <request name="set_primary_selection" since="2">
+      <description summary="copy data to the primary selection">
+        This request asks the compositor to set the primary selection to the
+        data from the source on behalf of the client.
+
+        The given source may not be used in any further set_selection or
+        set_primary_selection requests. Attempting to use a previously used
+        source is a protocol error.
+
+        To unset the primary selection, set the source to NULL.
+
+        The compositor will ignore this request if it does not support primary
+        selection.
+      </description>
+      <arg name="source" type="object" interface="zwlr_data_control_source_v1"
+        allow-null="true"/>
+    </request>
+
+    <enum name="error" since="2">
+      <entry name="used_source" value="1"
+        summary="source given to set_selection or set_primary_selection was already used before"/>
+    </enum>
+  </interface>
+
+  <interface name="zwlr_data_control_source_v1" version="1">
+    <description summary="offer to transfer data">
+      The wlr_data_control_source object is the source side of a
+      wlr_data_control_offer. It is created by the source client in a data
+      transfer and provides a way to describe the offered data and a way to
+      respond to requests to transfer the data.
+    </description>
+
+    <enum name="error">
+      <entry name="invalid_offer" value="1"
+        summary="offer sent after wlr_data_control_device.set_selection"/>
+    </enum>
+
+    <request name="offer">
+      <description summary="add an offered MIME type">
+        This request adds a MIME type to the set of MIME types advertised to
+        targets. Can be called several times to offer multiple types.
+
+        Calling this after wlr_data_control_device.set_selection is a protocol
+        error.
+      </description>
+      <arg name="mime_type" type="string"
+        summary="MIME type offered by the data source"/>
+    </request>
+
+    <request name="destroy" type="destructor">
+      <description summary="destroy this source">
+        Destroys the data source object.
+      </description>
+    </request>
+
+    <event name="send">
+      <description summary="send the data">
+        Request for data from the client. Send the data as the specified MIME
+        type over the passed file descriptor, then close it.
+      </description>
+      <arg name="mime_type" type="string" summary="MIME type for the data"/>
+      <arg name="fd" type="fd" summary="file descriptor for the data"/>
+    </event>
+
+    <event name="cancelled">
+      <description summary="selection was cancelled">
+        This data source is no longer valid. The data source has been replaced
+        by another data source.
+
+        The client should clean up and destroy this data source.
+      </description>
+    </event>
+  </interface>
+
+  <interface name="zwlr_data_control_offer_v1" version="1">
+    <description summary="offer to transfer data">
+      A wlr_data_control_offer represents a piece of data offered for transfer
+      by another client (the source client). The offer describes the different
+      MIME types that the data can be converted to and provides the mechanism
+      for transferring the data directly from the source client.
+    </description>
+
+    <request name="receive">
+      <description summary="request that the data is transferred">
+        To transfer the offered data, the client issues this request and
+        indicates the MIME type it wants to receive. The transfer happens
+        through the passed file descriptor (typically created with the pipe
+        system call). The source client writes the data in the MIME type
+        representation requested and then closes the file descriptor.
+
+        The receiving client reads from the read end of the pipe until EOF and
+        then closes its end, at which point the transfer is complete.
+
+        This request may happen multiple times for different MIME types.
+      </description>
+      <arg name="mime_type" type="string"
+        summary="MIME type desired by receiver"/>
+      <arg name="fd" type="fd" summary="file descriptor for data transfer"/>
+    </request>
+
+    <request name="destroy" type="destructor">
+      <description summary="destroy this offer">
+        Destroys the data offer object.
+      </description>
+    </request>
+
+    <event name="offer">
+      <description summary="advertise offered MIME type">
+        Sent immediately after creating the wlr_data_control_offer object.
+        One event per offered MIME type.
+      </description>
+      <arg name="mime_type" type="string" summary="offered MIME type"/>
+    </event>
+  </interface>
+</protocol>
diff --git a/src/auto/wayland/protocols/xdg-shell.xml b/src/auto/wayland/protocols/xdg-shell.xml
new file mode 100644 (file)
index 0000000..c4d4685
--- /dev/null
@@ -0,0 +1,1415 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<protocol name="xdg_shell">
+
+  <copyright>
+    Copyright © 2008-2013 Kristian Høgsberg
+    Copyright © 2013      Rafael Antognolli
+    Copyright © 2013      Jasper St. Pierre
+    Copyright © 2010-2013 Intel Corporation
+    Copyright © 2015-2017 Samsung Electronics Co., Ltd
+    Copyright © 2015-2017 Red Hat Inc.
+
+    Permission is hereby granted, free of charge, to any person obtaining a
+    copy of this software and associated documentation files (the "Software"),
+    to deal in the Software without restriction, including without limitation
+    the rights to use, copy, modify, merge, publish, distribute, sublicense,
+    and/or sell copies of the Software, and to permit persons to whom the
+    Software is furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice (including the next
+    paragraph) shall be included in all copies or substantial portions of the
+    Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+    THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+    DEALINGS IN THE SOFTWARE.
+  </copyright>
+
+  <interface name="xdg_wm_base" version="7">
+    <description summary="create desktop-style surfaces">
+      The xdg_wm_base interface is exposed as a global object enabling clients
+      to turn their wl_surfaces into windows in a desktop environment. It
+      defines the basic functionality needed for clients and the compositor to
+      create windows that can be dragged, resized, maximized, etc, as well as
+      creating transient windows such as popup menus.
+    </description>
+
+    <enum name="error">
+      <entry name="role" value="0" summary="given wl_surface has another role"/>
+      <entry name="defunct_surfaces" value="1"
+            summary="xdg_wm_base was destroyed before children"/>
+      <entry name="not_the_topmost_popup" value="2"
+            summary="the client tried to map or destroy a non-topmost popup"/>
+      <entry name="invalid_popup_parent" value="3"
+            summary="the client specified an invalid popup parent surface"/>
+      <entry name="invalid_surface_state" value="4"
+            summary="the client provided an invalid surface state"/>
+      <entry name="invalid_positioner" value="5"
+            summary="the client provided an invalid positioner"/>
+      <entry name="unresponsive" value="6"
+            summary="the client didn’t respond to a ping event in time"/>
+    </enum>
+
+    <request name="destroy" type="destructor">
+      <description summary="destroy xdg_wm_base">
+       Destroy this xdg_wm_base object.
+
+       Destroying a bound xdg_wm_base object while there are surfaces
+       still alive created by this xdg_wm_base object instance is illegal
+       and will result in a defunct_surfaces error.
+      </description>
+    </request>
+
+    <request name="create_positioner">
+      <description summary="create a positioner object">
+       Create a positioner object. A positioner object is used to position
+       surfaces relative to some parent surface. See the interface description
+       and xdg_surface.get_popup for details.
+      </description>
+      <arg name="id" type="new_id" interface="xdg_positioner"/>
+    </request>
+
+    <request name="get_xdg_surface">
+      <description summary="create a shell surface from a surface">
+       This creates an xdg_surface for the given surface. While xdg_surface
+       itself is not a role, the corresponding surface may only be assigned
+       a role extending xdg_surface, such as xdg_toplevel or xdg_popup. It is
+       illegal to create an xdg_surface for a wl_surface which already has an
+       assigned role and this will result in a role error.
+
+       This creates an xdg_surface for the given surface. An xdg_surface is
+       used as basis to define a role to a given surface, such as xdg_toplevel
+       or xdg_popup. It also manages functionality shared between xdg_surface
+       based surface roles.
+
+       See the documentation of xdg_surface for more details about what an
+       xdg_surface is and how it is used.
+      </description>
+      <arg name="id" type="new_id" interface="xdg_surface"/>
+      <arg name="surface" type="object" interface="wl_surface"/>
+    </request>
+
+    <request name="pong">
+      <description summary="respond to a ping event">
+       A client must respond to a ping event with a pong request or
+       the client may be deemed unresponsive. See xdg_wm_base.ping
+       and xdg_wm_base.error.unresponsive.
+      </description>
+      <arg name="serial" type="uint" summary="serial of the ping event"/>
+    </request>
+
+    <event name="ping">
+      <description summary="check if the client is alive">
+       The ping event asks the client if it's still alive. Pass the
+       serial specified in the event back to the compositor by sending
+       a "pong" request back with the specified serial. See xdg_wm_base.pong.
+
+       Compositors can use this to determine if the client is still
+       alive. It's unspecified what will happen if the client doesn't
+       respond to the ping request, or in what timeframe. Clients should
+       try to respond in a reasonable amount of time. The “unresponsive”
+       error is provided for compositors that wish to disconnect unresponsive
+       clients.
+
+       A compositor is free to ping in any way it wants, but a client must
+       always respond to any xdg_wm_base object it created.
+      </description>
+      <arg name="serial" type="uint" summary="pass this to the pong request"/>
+    </event>
+  </interface>
+
+  <interface name="xdg_positioner" version="7">
+    <description summary="child surface positioner">
+      The xdg_positioner provides a collection of rules for the placement of a
+      child surface relative to a parent surface. Rules can be defined to ensure
+      the child surface remains within the visible area's borders, and to
+      specify how the child surface changes its position, such as sliding along
+      an axis, or flipping around a rectangle. These positioner-created rules are
+      constrained by the requirement that a child surface must intersect with or
+      be at least partially adjacent to its parent surface.
+
+      See the various requests for details about possible rules.
+
+      At the time of the request, the compositor makes a copy of the rules
+      specified by the xdg_positioner. Thus, after the request is complete the
+      xdg_positioner object can be destroyed or reused; further changes to the
+      object will have no effect on previous usages.
+
+      For an xdg_positioner object to be considered complete, it must have a
+      non-zero size set by set_size, and a non-zero anchor rectangle set by
+      set_anchor_rect. Passing an incomplete xdg_positioner object when
+      positioning a surface raises an invalid_positioner error.
+    </description>
+
+    <enum name="error">
+      <entry name="invalid_input" value="0" summary="invalid input provided"/>
+    </enum>
+
+    <request name="destroy" type="destructor">
+      <description summary="destroy the xdg_positioner object">
+       Notify the compositor that the xdg_positioner will no longer be used.
+      </description>
+    </request>
+
+    <request name="set_size">
+      <description summary="set the size of the to-be positioned rectangle">
+       Set the size of the surface that is to be positioned with the positioner
+       object. The size is in surface-local coordinates and corresponds to the
+       window geometry. See xdg_surface.set_window_geometry.
+
+       If a zero or negative size is set the invalid_input error is raised.
+      </description>
+      <arg name="width" type="int" summary="width of positioned rectangle"/>
+      <arg name="height" type="int" summary="height of positioned rectangle"/>
+    </request>
+
+    <request name="set_anchor_rect">
+      <description summary="set the anchor rectangle within the parent surface">
+       Specify the anchor rectangle within the parent surface that the child
+       surface will be placed relative to. The rectangle is relative to the
+       window geometry as defined by xdg_surface.set_window_geometry of the
+       parent surface.
+
+       When the xdg_positioner object is used to position a child surface, the
+       anchor rectangle may not extend outside the window geometry of the
+       positioned child's parent surface.
+
+       If a negative size is set the invalid_input error is raised.
+      </description>
+      <arg name="x" type="int" summary="x position of anchor rectangle"/>
+      <arg name="y" type="int" summary="y position of anchor rectangle"/>
+      <arg name="width" type="int" summary="width of anchor rectangle"/>
+      <arg name="height" type="int" summary="height of anchor rectangle"/>
+    </request>
+
+    <enum name="anchor">
+      <entry name="none" value="0"/>
+      <entry name="top" value="1"/>
+      <entry name="bottom" value="2"/>
+      <entry name="left" value="3"/>
+      <entry name="right" value="4"/>
+      <entry name="top_left" value="5"/>
+      <entry name="bottom_left" value="6"/>
+      <entry name="top_right" value="7"/>
+      <entry name="bottom_right" value="8"/>
+    </enum>
+
+    <request name="set_anchor">
+      <description summary="set anchor rectangle anchor">
+       Defines the anchor point for the anchor rectangle. The specified anchor
+       is used derive an anchor point that the child surface will be
+       positioned relative to. If a corner anchor is set (e.g. 'top_left' or
+       'bottom_right'), the anchor point will be at the specified corner;
+       otherwise, the derived anchor point will be centered on the specified
+       edge, or in the center of the anchor rectangle if no edge is specified.
+      </description>
+      <arg name="anchor" type="uint" enum="anchor"
+          summary="anchor"/>
+    </request>
+
+    <enum name="gravity">
+      <entry name="none" value="0"/>
+      <entry name="top" value="1"/>
+      <entry name="bottom" value="2"/>
+      <entry name="left" value="3"/>
+      <entry name="right" value="4"/>
+      <entry name="top_left" value="5"/>
+      <entry name="bottom_left" value="6"/>
+      <entry name="top_right" value="7"/>
+      <entry name="bottom_right" value="8"/>
+    </enum>
+
+    <request name="set_gravity">
+      <description summary="set child surface gravity">
+       Defines in what direction a surface should be positioned, relative to
+       the anchor point of the parent surface. If a corner gravity is
+       specified (e.g. 'bottom_right' or 'top_left'), then the child surface
+       will be placed towards the specified gravity; otherwise, the child
+       surface will be centered over the anchor point on any axis that had no
+       gravity specified. If the gravity is not in the ‘gravity’ enum, an
+       invalid_input error is raised.
+      </description>
+      <arg name="gravity" type="uint" enum="gravity"
+          summary="gravity direction"/>
+    </request>
+
+    <enum name="constraint_adjustment" bitfield="true">
+      <description summary="constraint adjustments">
+       The constraint adjustment value define ways the compositor will adjust
+       the position of the surface, if the unadjusted position would result
+       in the surface being partly constrained.
+
+       Whether a surface is considered 'constrained' is left to the compositor
+       to determine. For example, the surface may be partly outside the
+       compositor's defined 'work area', thus necessitating the child surface's
+       position be adjusted until it is entirely inside the work area.
+
+       The adjustments can be combined, according to a defined precedence: 1)
+       Flip, 2) Slide, 3) Resize.
+      </description>
+      <entry name="none" value="0">
+       <description summary="don't move the child surface when constrained">
+         Don't alter the surface position even if it is constrained on some
+         axis, for example partially outside the edge of an output.
+       </description>
+      </entry>
+      <entry name="slide_x" value="1">
+       <description summary="move along the x axis until unconstrained">
+         Slide the surface along the x axis until it is no longer constrained.
+
+         First try to slide towards the direction of the gravity on the x axis
+         until either the edge in the opposite direction of the gravity is
+         unconstrained or the edge in the direction of the gravity is
+         constrained.
+
+         Then try to slide towards the opposite direction of the gravity on the
+         x axis until either the edge in the direction of the gravity is
+         unconstrained or the edge in the opposite direction of the gravity is
+         constrained.
+       </description>
+      </entry>
+      <entry name="slide_y" value="2">
+       <description summary="move along the y axis until unconstrained">
+         Slide the surface along the y axis until it is no longer constrained.
+
+         First try to slide towards the direction of the gravity on the y axis
+         until either the edge in the opposite direction of the gravity is
+         unconstrained or the edge in the direction of the gravity is
+         constrained.
+
+         Then try to slide towards the opposite direction of the gravity on the
+         y axis until either the edge in the direction of the gravity is
+         unconstrained or the edge in the opposite direction of the gravity is
+         constrained.
+       </description>
+      </entry>
+      <entry name="flip_x" value="4">
+       <description summary="invert the anchor and gravity on the x axis">
+         Invert the anchor and gravity on the x axis if the surface is
+         constrained on the x axis. For example, if the left edge of the
+         surface is constrained, the gravity is 'left' and the anchor is
+         'left', change the gravity to 'right' and the anchor to 'right'.
+
+         If the adjusted position also ends up being constrained, the resulting
+         position of the flip_x adjustment will be the one before the
+         adjustment.
+       </description>
+      </entry>
+      <entry name="flip_y" value="8">
+       <description summary="invert the anchor and gravity on the y axis">
+         Invert the anchor and gravity on the y axis if the surface is
+         constrained on the y axis. For example, if the bottom edge of the
+         surface is constrained, the gravity is 'bottom' and the anchor is
+         'bottom', change the gravity to 'top' and the anchor to 'top'.
+
+         The adjusted position is calculated given the original anchor
+         rectangle and offset, but with the new flipped anchor and gravity
+         values.
+
+         If the adjusted position also ends up being constrained, the resulting
+         position of the flip_y adjustment will be the one before the
+         adjustment.
+       </description>
+      </entry>
+      <entry name="resize_x" value="16">
+       <description summary="horizontally resize the surface">
+         Resize the surface horizontally so that it is completely
+         unconstrained.
+       </description>
+      </entry>
+      <entry name="resize_y" value="32">
+       <description summary="vertically resize the surface">
+         Resize the surface vertically so that it is completely unconstrained.
+       </description>
+      </entry>
+    </enum>
+
+    <request name="set_constraint_adjustment">
+      <description summary="set the adjustment to be done when constrained">
+       Specify how the window should be positioned if the originally intended
+       position caused the surface to be constrained, meaning at least
+       partially outside positioning boundaries set by the compositor. The
+       adjustment is set by constructing a bitmask describing the adjustment to
+       be made when the surface is constrained on that axis.
+
+       If no bit for one axis is set, the compositor will assume that the child
+       surface should not change its position on that axis when constrained.
+
+       If more than one bit for one axis is set, the order of how adjustments
+       are applied is specified in the corresponding adjustment descriptions.
+
+       The default adjustment is none.
+      </description>
+      <arg name="constraint_adjustment" type="uint" enum="constraint_adjustment"
+          summary="bit mask of constraint adjustments"/>
+    </request>
+
+    <request name="set_offset">
+      <description summary="set surface position offset">
+       Specify the surface position offset relative to the position of the
+       anchor on the anchor rectangle and the anchor on the surface. For
+       example if the anchor of the anchor rectangle is at (x, y), the surface
+       has the gravity bottom|right, and the offset is (ox, oy), the calculated
+       surface position will be (x + ox, y + oy). The offset position of the
+       surface is the one used for constraint testing. See
+       set_constraint_adjustment.
+
+       An example use case is placing a popup menu on top of a user interface
+       element, while aligning the user interface element of the parent surface
+       with some user interface element placed somewhere in the popup surface.
+      </description>
+      <arg name="x" type="int" summary="surface position x offset"/>
+      <arg name="y" type="int" summary="surface position y offset"/>
+    </request>
+
+    <!-- Version 3 additions -->
+
+    <request name="set_reactive" since="3">
+      <description summary="continuously reconstrain the surface">
+       When set reactive, the surface is reconstrained if the conditions used
+       for constraining changed, e.g. the parent window moved.
+
+       If the conditions changed and the popup was reconstrained, an
+       xdg_popup.configure event is sent with updated geometry, followed by an
+       xdg_surface.configure event.
+      </description>
+    </request>
+
+    <request name="set_parent_size" since="3">
+      <description summary="">
+       Set the parent window geometry the compositor should use when
+       positioning the popup. The compositor may use this information to
+       determine the future state the popup should be constrained using. If
+       this doesn't match the dimension of the parent the popup is eventually
+       positioned against, the behavior is undefined.
+
+       The arguments are given in the surface-local coordinate space.
+      </description>
+      <arg name="parent_width" type="int"
+          summary="future window geometry width of parent"/>
+      <arg name="parent_height" type="int"
+          summary="future window geometry height of parent"/>
+    </request>
+
+    <request name="set_parent_configure" since="3">
+      <description summary="set parent configure this is a response to">
+       Set the serial of an xdg_surface.configure event this positioner will be
+       used in response to. The compositor may use this information together
+       with set_parent_size to determine what future state the popup should be
+       constrained using.
+      </description>
+      <arg name="serial" type="uint"
+          summary="serial of parent configure event"/>
+    </request>
+  </interface>
+
+  <interface name="xdg_surface" version="7">
+    <description summary="desktop user interface surface base interface">
+      An interface that may be implemented by a wl_surface, for
+      implementations that provide a desktop-style user interface.
+
+      It provides a base set of functionality required to construct user
+      interface elements requiring management by the compositor, such as
+      toplevel windows, menus, etc. The types of functionality are split into
+      xdg_surface roles.
+
+      Creating an xdg_surface does not set the role for a wl_surface. In order
+      to map an xdg_surface, the client must create a role-specific object
+      using, e.g., get_toplevel, get_popup. The wl_surface for any given
+      xdg_surface can have at most one role, and may not be assigned any role
+      not based on xdg_surface.
+
+      A role must be assigned before any other requests are made to the
+      xdg_surface object.
+
+      The client must call wl_surface.commit on the corresponding wl_surface
+      for the xdg_surface state to take effect.
+
+      Creating an xdg_surface from a wl_surface which has a buffer attached or
+      committed is a client error, and any attempts by a client to attach or
+      manipulate a buffer prior to the first xdg_surface.configure call must
+      also be treated as errors.
+
+      After creating a role-specific object and setting it up (e.g. by sending
+      the title, app ID, size constraints, parent, etc), the client must
+      perform an initial commit without any buffer attached. The compositor
+      will reply with initial wl_surface state such as
+      wl_surface.preferred_buffer_scale followed by an xdg_surface.configure
+      event. The client must acknowledge it and is then allowed to attach a
+      buffer to map the surface.
+
+      Mapping an xdg_surface-based role surface is defined as making it
+      possible for the surface to be shown by the compositor. Note that
+      a mapped surface is not guaranteed to be visible once it is mapped.
+
+      For an xdg_surface to be mapped by the compositor, the following
+      conditions must be met:
+      (1) the client has assigned an xdg_surface-based role to the surface
+      (2) the client has set and committed the xdg_surface state and the
+         role-dependent state to the surface
+      (3) the client has committed a buffer to the surface
+
+      A newly-unmapped surface is considered to have met condition (1) out
+      of the 3 required conditions for mapping a surface if its role surface
+      has not been destroyed, i.e. the client must perform the initial commit
+      again before attaching a buffer.
+    </description>
+
+    <enum name="error">
+      <entry name="not_constructed" value="1"
+            summary="Surface was not fully constructed"/>
+      <entry name="already_constructed" value="2"
+            summary="Surface was already constructed"/>
+      <entry name="unconfigured_buffer" value="3"
+            summary="Attaching a buffer to an unconfigured surface"/>
+      <entry name="invalid_serial" value="4"
+            summary="Invalid serial number when acking a configure event"/>
+      <entry name="invalid_size" value="5"
+            summary="Width or height was zero or negative"/>
+      <entry name="defunct_role_object" value="6"
+            summary="Surface was destroyed before its role object"/>
+    </enum>
+
+    <request name="destroy" type="destructor">
+      <description summary="destroy the xdg_surface">
+       Destroy the xdg_surface object. An xdg_surface must only be destroyed
+       after its role object has been destroyed, otherwise
+       a defunct_role_object error is raised.
+      </description>
+    </request>
+
+    <request name="get_toplevel">
+      <description summary="assign the xdg_toplevel surface role">
+       This creates an xdg_toplevel object for the given xdg_surface and gives
+       the associated wl_surface the xdg_toplevel role.
+
+       See the documentation of xdg_toplevel for more details about what an
+       xdg_toplevel is and how it is used.
+      </description>
+      <arg name="id" type="new_id" interface="xdg_toplevel"/>
+    </request>
+
+    <request name="get_popup">
+      <description summary="assign the xdg_popup surface role">
+       This creates an xdg_popup object for the given xdg_surface and gives
+       the associated wl_surface the xdg_popup role.
+
+       If null is passed as a parent, a parent surface must be specified using
+       some other protocol, before committing the initial state.
+
+       See the documentation of xdg_popup for more details about what an
+       xdg_popup is and how it is used.
+      </description>
+      <arg name="id" type="new_id" interface="xdg_popup"/>
+      <arg name="parent" type="object" interface="xdg_surface" allow-null="true"/>
+      <arg name="positioner" type="object" interface="xdg_positioner"/>
+    </request>
+
+    <request name="set_window_geometry">
+      <description summary="set the new window geometry">
+       The window geometry of a surface is its "visible bounds" from the
+       user's perspective. Client-side decorations often have invisible
+       portions like drop-shadows which should be ignored for the
+       purposes of aligning, placing and constraining windows.
+
+       The window geometry is double-buffered state, see wl_surface.commit.
+
+       When maintaining a position, the compositor should treat the (x, y)
+       coordinate of the window geometry as the top left corner of the window.
+       A client changing the (x, y) window geometry coordinate should in
+       general not alter the position of the window.
+
+       Once the window geometry of the surface is set, it is not possible to
+       unset it, and it will remain the same until set_window_geometry is
+       called again, even if a new subsurface or buffer is attached.
+
+       If never set, the value is the full bounds of the surface,
+       including any subsurfaces. This updates dynamically on every
+       commit. This unset is meant for extremely simple clients.
+
+       The arguments are given in the surface-local coordinate space of
+       the wl_surface associated with this xdg_surface, and may extend outside
+       of the wl_surface itself to mark parts of the subsurface tree as part of
+       the window geometry.
+
+       When applied, the effective window geometry will be the set window
+       geometry clamped to the bounding rectangle of the combined
+       geometry of the surface of the xdg_surface and the associated
+       subsurfaces.
+
+       The effective geometry will not be recalculated unless a new call to
+       set_window_geometry is done and the new pending surface state is
+       subsequently applied.
+
+       The width and height of the effective window geometry must be
+       greater than zero. Setting an invalid size will raise an
+       invalid_size error.
+      </description>
+      <arg name="x" type="int"/>
+      <arg name="y" type="int"/>
+      <arg name="width" type="int"/>
+      <arg name="height" type="int"/>
+    </request>
+
+    <request name="ack_configure">
+      <description summary="ack a configure event">
+       When a configure event is received, if a client commits the
+       surface in response to the configure event, then the client
+       must make an ack_configure request sometime before the commit
+       request, passing along the serial of the configure event.
+
+       For instance, for toplevel surfaces the compositor might use this
+       information to move a surface to the top left only when the client has
+       drawn itself for the maximized or fullscreen state.
+
+       If the client receives multiple configure events before it
+       can respond to one, it only has to ack the last configure event.
+       Acking a configure event that was never sent raises an invalid_serial
+       error.
+
+       A client is not required to commit immediately after sending
+       an ack_configure request - it may even ack_configure several times
+       before its next surface commit.
+
+       A client may send multiple ack_configure requests before committing, but
+       only the last request sent before a commit indicates which configure
+       event the client really is responding to.
+
+       Sending an ack_configure request consumes the serial number sent with
+       the request, as well as serial numbers sent by all configure events
+       sent on this xdg_surface prior to the configure event referenced by
+       the committed serial.
+
+       It is an error to issue multiple ack_configure requests referencing a
+       serial from the same configure event, or to issue an ack_configure
+       request referencing a serial from a configure event issued before the
+       event identified by the last ack_configure request for the same
+       xdg_surface. Doing so will raise an invalid_serial error.
+      </description>
+      <arg name="serial" type="uint" summary="the serial from the configure event"/>
+    </request>
+
+    <event name="configure">
+      <description summary="suggest a surface change">
+       The configure event marks the end of a configure sequence. A configure
+       sequence is a set of one or more events configuring the state of the
+       xdg_surface, including the final xdg_surface.configure event.
+
+       Where applicable, xdg_surface surface roles will during a configure
+       sequence extend this event as a latched state sent as events before the
+       xdg_surface.configure event. Such events should be considered to make up
+       a set of atomically applied configuration states, where the
+       xdg_surface.configure commits the accumulated state.
+
+       Clients should arrange their surface for the new states, and then send
+       an ack_configure request with the serial sent in this configure event at
+       some point before committing the new surface.
+
+       If the client receives multiple configure events before it can respond
+       to one, it is free to discard all but the last event it received.
+      </description>
+      <arg name="serial" type="uint" summary="serial of the configure event"/>
+    </event>
+
+  </interface>
+
+  <interface name="xdg_toplevel" version="7">
+    <description summary="toplevel surface">
+      This interface defines an xdg_surface role which allows a surface to,
+      among other things, set window-like properties such as maximize,
+      fullscreen, and minimize, set application-specific metadata like title and
+      id, and well as trigger user interactive operations such as interactive
+      resize and move.
+
+      A xdg_toplevel by default is responsible for providing the full intended
+      visual representation of the toplevel, which depending on the window
+      state, may mean things like a title bar, window controls and drop shadow.
+
+      Unmapping an xdg_toplevel means that the surface cannot be shown
+      by the compositor until it is explicitly mapped again.
+      All active operations (e.g., move, resize) are canceled and all
+      attributes (e.g. title, state, stacking, ...) are discarded for
+      an xdg_toplevel surface when it is unmapped. The xdg_toplevel returns to
+      the state it had right after xdg_surface.get_toplevel. The client
+      can re-map the toplevel by performing a commit without any buffer
+      attached, waiting for a configure event and handling it as usual (see
+      xdg_surface description).
+
+      Attaching a null buffer to a toplevel unmaps the surface.
+    </description>
+
+    <request name="destroy" type="destructor">
+      <description summary="destroy the xdg_toplevel">
+       This request destroys the role surface and unmaps the surface;
+       see "Unmapping" behavior in interface section for details.
+      </description>
+    </request>
+
+    <enum name="error">
+      <entry name="invalid_resize_edge" value="0" summary="provided value is
+        not a valid variant of the resize_edge enum"/>
+      <entry name="invalid_parent" value="1"
+        summary="invalid parent toplevel"/>
+      <entry name="invalid_size" value="2"
+       summary="client provided an invalid min or max size"/>
+    </enum>
+
+    <request name="set_parent">
+      <description summary="set the parent of this surface">
+       Set the "parent" of this surface. This surface should be stacked
+       above the parent surface and all other ancestor surfaces.
+
+       Parent surfaces should be set on dialogs, toolboxes, or other
+       "auxiliary" surfaces, so that the parent is raised when the dialog
+       is raised.
+
+       Setting a null parent for a child surface unsets its parent. Setting
+       a null parent for a surface which currently has no parent is a no-op.
+
+       Only mapped surfaces can have child surfaces. Setting a parent which
+       is not mapped is equivalent to setting a null parent. If a surface
+       becomes unmapped, its children's parent is set to the parent of
+       the now-unmapped surface. If the now-unmapped surface has no parent,
+       its children's parent is unset. If the now-unmapped surface becomes
+       mapped again, its parent-child relationship is not restored.
+
+       The parent toplevel must not be one of the child toplevel's
+       descendants, and the parent must be different from the child toplevel,
+       otherwise the invalid_parent protocol error is raised.
+      </description>
+      <arg name="parent" type="object" interface="xdg_toplevel" allow-null="true"/>
+    </request>
+
+    <request name="set_title">
+      <description summary="set surface title">
+       Set a short title for the surface.
+
+       This string may be used to identify the surface in a task bar,
+       window list, or other user interface elements provided by the
+       compositor.
+
+       The string must be encoded in UTF-8.
+      </description>
+      <arg name="title" type="string"/>
+    </request>
+
+    <request name="set_app_id">
+      <description summary="set application ID">
+       Set an application identifier for the surface.
+
+       The app ID identifies the general class of applications to which
+       the surface belongs. The compositor can use this to group multiple
+       surfaces together, or to determine how to launch a new application.
+
+       For D-Bus activatable applications, the app ID is used as the D-Bus
+       service name.
+
+       The compositor shell will try to group application surfaces together
+       by their app ID. As a best practice, it is suggested to select app
+       ID's that match the basename of the application's .desktop file.
+       For example, "org.freedesktop.FooViewer" where the .desktop file is
+       "org.freedesktop.FooViewer.desktop".
+
+       Like other properties, a set_app_id request can be sent after the
+       xdg_toplevel has been mapped to update the property.
+
+       See the desktop-entry specification [0] for more details on
+       application identifiers and how they relate to well-known D-Bus
+       names and .desktop files.
+
+       [0] https://standards.freedesktop.org/desktop-entry-spec/
+      </description>
+      <arg name="app_id" type="string"/>
+    </request>
+
+    <request name="show_window_menu">
+      <description summary="show the window menu">
+       Clients implementing client-side decorations might want to show
+       a context menu when right-clicking on the decorations, giving the
+       user a menu that they can use to maximize or minimize the window.
+
+       This request asks the compositor to pop up such a window menu at
+       the given position, relative to the local surface coordinates of
+       the parent surface. There are no guarantees as to what menu items
+       the window menu contains, or even if a window menu will be drawn
+       at all.
+
+       This request must be used in response to some sort of user action
+       like a button press, key press, or touch down event.
+      </description>
+      <arg name="seat" type="object" interface="wl_seat" summary="the wl_seat of the user event"/>
+      <arg name="serial" type="uint" summary="the serial of the user event"/>
+      <arg name="x" type="int" summary="the x position to pop up the window menu at"/>
+      <arg name="y" type="int" summary="the y position to pop up the window menu at"/>
+    </request>
+
+    <request name="move">
+      <description summary="start an interactive move">
+       Start an interactive, user-driven move of the surface.
+
+       This request must be used in response to some sort of user action
+       like a button press, key press, or touch down event. The passed
+       serial is used to determine the type of interactive move (touch,
+       pointer, etc).
+
+       The server may ignore move requests depending on the state of
+       the surface (e.g. fullscreen or maximized), or if the passed serial
+       is no longer valid.
+
+       If triggered, the surface will lose the focus of the device
+       (wl_pointer, wl_touch, etc) used for the move. It is up to the
+       compositor to visually indicate that the move is taking place, such as
+       updating a pointer cursor, during the move. There is no guarantee
+       that the device focus will return when the move is completed.
+      </description>
+      <arg name="seat" type="object" interface="wl_seat" summary="the wl_seat of the user event"/>
+      <arg name="serial" type="uint" summary="the serial of the user event"/>
+    </request>
+
+    <enum name="resize_edge">
+      <description summary="edge values for resizing">
+       These values are used to indicate which edge of a surface
+       is being dragged in a resize operation.
+      </description>
+      <entry name="none" value="0"/>
+      <entry name="top" value="1"/>
+      <entry name="bottom" value="2"/>
+      <entry name="left" value="4"/>
+      <entry name="top_left" value="5"/>
+      <entry name="bottom_left" value="6"/>
+      <entry name="right" value="8"/>
+      <entry name="top_right" value="9"/>
+      <entry name="bottom_right" value="10"/>
+    </enum>
+
+    <request name="resize">
+      <description summary="start an interactive resize">
+       Start a user-driven, interactive resize of the surface.
+
+       This request must be used in response to some sort of user action
+       like a button press, key press, or touch down event. The passed
+       serial is used to determine the type of interactive resize (touch,
+       pointer, etc).
+
+       The server may ignore resize requests depending on the state of
+       the surface (e.g. fullscreen or maximized).
+
+       If triggered, the client will receive configure events with the
+       "resize" state enum value and the expected sizes. See the "resize"
+       enum value for more details about what is required. The client
+       must also acknowledge configure events using "ack_configure". After
+       the resize is completed, the client will receive another "configure"
+       event without the resize state.
+
+       If triggered, the surface also will lose the focus of the device
+       (wl_pointer, wl_touch, etc) used for the resize. It is up to the
+       compositor to visually indicate that the resize is taking place,
+       such as updating a pointer cursor, during the resize. There is no
+       guarantee that the device focus will return when the resize is
+       completed.
+
+       The edges parameter specifies how the surface should be resized, and
+       is one of the values of the resize_edge enum. Values not matching
+       a variant of the enum will cause the invalid_resize_edge protocol error.
+       The compositor may use this information to update the surface position
+       for example when dragging the top left corner. The compositor may also
+       use this information to adapt its behavior, e.g. choose an appropriate
+       cursor image.
+      </description>
+      <arg name="seat" type="object" interface="wl_seat" summary="the wl_seat of the user event"/>
+      <arg name="serial" type="uint" summary="the serial of the user event"/>
+      <arg name="edges" type="uint" enum="resize_edge" summary="which edge or corner is being dragged"/>
+    </request>
+
+    <enum name="state">
+      <description summary="types of state on the surface">
+       The different state values used on the surface. This is designed for
+       state values like maximized, fullscreen. It is paired with the
+       configure event to ensure that both the client and the compositor
+       setting the state can be synchronized.
+
+       States set in this way are double-buffered, see wl_surface.commit.
+      </description>
+      <entry name="maximized" value="1" summary="the surface is maximized">
+       <description summary="the surface is maximized">
+         The surface is maximized. The window geometry specified in the configure
+         event must be obeyed by the client, or the xdg_wm_base.invalid_surface_state
+         error is raised.
+
+         The client should draw without shadow or other
+         decoration outside of the window geometry.
+       </description>
+      </entry>
+      <entry name="fullscreen" value="2" summary="the surface is fullscreen">
+       <description summary="the surface is fullscreen">
+         The surface is fullscreen. The window geometry specified in the
+         configure event is a maximum; the client cannot resize beyond it. For
+         a surface to cover the whole fullscreened area, the geometry
+         dimensions must be obeyed by the client. For more details, see
+         xdg_toplevel.set_fullscreen.
+       </description>
+      </entry>
+      <entry name="resizing" value="3" summary="the surface is being resized">
+       <description summary="the surface is being resized">
+         The surface is being resized. The window geometry specified in the
+         configure event is a maximum; the client cannot resize beyond it.
+         Clients that have aspect ratio or cell sizing configuration can use
+         a smaller size, however.
+       </description>
+      </entry>
+      <entry name="activated" value="4" summary="the surface is now activated">
+       <description summary="the surface is now activated">
+         Client window decorations should be painted as if the window is
+         active. Do not assume this means that the window actually has
+         keyboard or pointer focus.
+       </description>
+      </entry>
+      <entry name="tiled_left" value="5" since="2">
+       <description summary="the surface’s left edge is tiled">
+         The window is currently in a tiled layout and the left edge is
+         considered to be adjacent to another part of the tiling grid.
+
+         The client should draw without shadow or other decoration outside of
+         the window geometry on the left edge.
+       </description>
+      </entry>
+      <entry name="tiled_right" value="6" since="2">
+       <description summary="the surface’s right edge is tiled">
+         The window is currently in a tiled layout and the right edge is
+         considered to be adjacent to another part of the tiling grid.
+
+         The client should draw without shadow or other decoration outside of
+         the window geometry on the right edge.
+       </description>
+      </entry>
+      <entry name="tiled_top" value="7" since="2">
+       <description summary="the surface’s top edge is tiled">
+         The window is currently in a tiled layout and the top edge is
+         considered to be adjacent to another part of the tiling grid.
+
+         The client should draw without shadow or other decoration outside of
+         the window geometry on the top edge.
+       </description>
+      </entry>
+      <entry name="tiled_bottom" value="8" since="2">
+       <description summary="the surface’s bottom edge is tiled">
+         The window is currently in a tiled layout and the bottom edge is
+         considered to be adjacent to another part of the tiling grid.
+
+         The client should draw without shadow or other decoration outside of
+         the window geometry on the bottom edge.
+       </description>
+      </entry>
+      <entry name="suspended" value="9" since="6">
+        <description summary="surface repaint is suspended">
+         The surface is currently not ordinarily being repainted; for
+         example because its content is occluded by another window, or its
+         outputs are switched off due to screen locking.
+       </description>
+      </entry>
+      <entry name="constrained_left" value="10" since="7">
+       <description summary="the surface’s left edge is constrained">
+          The left edge of the window is currently constrained, meaning it
+          shouldn't attempt to resize from that edge. It can for example mean
+          it's tiled next to a monitor edge on the constrained side of the
+          window.
+       </description>
+      </entry>
+      <entry name="constrained_right" value="11" since="7">
+       <description summary="the surface’s right edge is constrained">
+          The right edge of the window is currently constrained, meaning it
+          shouldn't attempt to resize from that edge. It can for example mean
+          it's tiled next to a monitor edge on the constrained side of the
+          window.
+       </description>
+      </entry>
+      <entry name="constrained_top" value="12" since="7">
+       <description summary="the surface’s top edge is constrained">
+          The top edge of the window is currently constrained, meaning it
+          shouldn't attempt to resize from that edge. It can for example mean
+          it's tiled next to a monitor edge on the constrained side of the
+          window.
+       </description>
+      </entry>
+      <entry name="constrained_bottom" value="13" since="7">
+       <description summary="the surface’s bottom edge is tiled">
+          The bottom edge of the window is currently constrained, meaning it
+          shouldn't attempt to resize from that edge. It can for example mean
+          it's tiled next to a monitor edge on the constrained side of the
+          window.
+       </description>
+      </entry>
+    </enum>
+
+    <request name="set_max_size">
+      <description summary="set the maximum size">
+       Set a maximum size for the window.
+
+       The client can specify a maximum size so that the compositor does
+       not try to configure the window beyond this size.
+
+       The width and height arguments are in window geometry coordinates.
+       See xdg_surface.set_window_geometry.
+
+       Values set in this way are double-buffered, see wl_surface.commit.
+
+       The compositor can use this information to allow or disallow
+       different states like maximize or fullscreen and draw accurate
+       animations.
+
+       Similarly, a tiling window manager may use this information to
+       place and resize client windows in a more effective way.
+
+       The client should not rely on the compositor to obey the maximum
+       size. The compositor may decide to ignore the values set by the
+       client and request a larger size.
+
+       If never set, or a value of zero in the request, means that the
+       client has no expected maximum size in the given dimension.
+       As a result, a client wishing to reset the maximum size
+       to an unspecified state can use zero for width and height in the
+       request.
+
+       Requesting a maximum size to be smaller than the minimum size of
+       a surface is illegal and will result in an invalid_size error.
+
+       The width and height must be greater than or equal to zero. Using
+       strictly negative values for width or height will result in a
+       invalid_size error.
+      </description>
+      <arg name="width" type="int"/>
+      <arg name="height" type="int"/>
+    </request>
+
+    <request name="set_min_size">
+      <description summary="set the minimum size">
+       Set a minimum size for the window.
+
+       The client can specify a minimum size so that the compositor does
+       not try to configure the window below this size.
+
+       The width and height arguments are in window geometry coordinates.
+       See xdg_surface.set_window_geometry.
+
+       Values set in this way are double-buffered, see wl_surface.commit.
+
+       The compositor can use this information to allow or disallow
+       different states like maximize or fullscreen and draw accurate
+       animations.
+
+       Similarly, a tiling window manager may use this information to
+       place and resize client windows in a more effective way.
+
+       The client should not rely on the compositor to obey the minimum
+       size. The compositor may decide to ignore the values set by the
+       client and request a smaller size.
+
+       If never set, or a value of zero in the request, means that the
+       client has no expected minimum size in the given dimension.
+       As a result, a client wishing to reset the minimum size
+       to an unspecified state can use zero for width and height in the
+       request.
+
+       Requesting a minimum size to be larger than the maximum size of
+       a surface is illegal and will result in an invalid_size error.
+
+       The width and height must be greater than or equal to zero. Using
+       strictly negative values for width and height will result in a
+       invalid_size error.
+      </description>
+      <arg name="width" type="int"/>
+      <arg name="height" type="int"/>
+    </request>
+
+    <request name="set_maximized">
+      <description summary="maximize the window">
+       Maximize the surface.
+
+       After requesting that the surface should be maximized, the compositor
+       will respond by emitting a configure event. Whether this configure
+       actually sets the window maximized is subject to compositor policies.
+       The client must then update its content, drawing in the configured
+       state. The client must also acknowledge the configure when committing
+       the new content (see ack_configure).
+
+       It is up to the compositor to decide how and where to maximize the
+       surface, for example which output and what region of the screen should
+       be used.
+
+       If the surface was already maximized, the compositor will still emit
+       a configure event with the "maximized" state.
+
+       If the surface is in a fullscreen state, this request has no direct
+       effect. It may alter the state the surface is returned to when
+       unmaximized unless overridden by the compositor.
+      </description>
+    </request>
+
+    <request name="unset_maximized">
+      <description summary="unmaximize the window">
+       Unmaximize the surface.
+
+       After requesting that the surface should be unmaximized, the compositor
+       will respond by emitting a configure event. Whether this actually
+       un-maximizes the window is subject to compositor policies.
+       If available and applicable, the compositor will include the window
+       geometry dimensions the window had prior to being maximized in the
+       configure event. The client must then update its content, drawing it in
+       the configured state. The client must also acknowledge the configure
+       when committing the new content (see ack_configure).
+
+       It is up to the compositor to position the surface after it was
+       unmaximized; usually the position the surface had before maximizing, if
+       applicable.
+
+       If the surface was already not maximized, the compositor will still
+       emit a configure event without the "maximized" state.
+
+       If the surface is in a fullscreen state, this request has no direct
+       effect. It may alter the state the surface is returned to when
+       unmaximized unless overridden by the compositor.
+      </description>
+    </request>
+
+    <request name="set_fullscreen">
+      <description summary="set the window as fullscreen on an output">
+       Make the surface fullscreen.
+
+       After requesting that the surface should be fullscreened, the
+       compositor will respond by emitting a configure event. Whether the
+       client is actually put into a fullscreen state is subject to compositor
+       policies. The client must also acknowledge the configure when
+       committing the new content (see ack_configure).
+
+       The output passed by the request indicates the client's preference as
+       to which display it should be set fullscreen on. If this value is NULL,
+       it's up to the compositor to choose which display will be used to map
+       this surface.
+
+       If the surface doesn't cover the whole output, the compositor will
+       position the surface in the center of the output and compensate with
+       with border fill covering the rest of the output. The content of the
+       border fill is undefined, but should be assumed to be in some way that
+       attempts to blend into the surrounding area (e.g. solid black).
+
+       If the fullscreened surface is not opaque, the compositor must make
+       sure that other screen content not part of the same surface tree (made
+       up of subsurfaces, popups or similarly coupled surfaces) are not
+       visible below the fullscreened surface.
+      </description>
+      <arg name="output" type="object" interface="wl_output" allow-null="true"/>
+    </request>
+
+    <request name="unset_fullscreen">
+      <description summary="unset the window as fullscreen">
+       Make the surface no longer fullscreen.
+
+       After requesting that the surface should be unfullscreened, the
+       compositor will respond by emitting a configure event.
+       Whether this actually removes the fullscreen state of the client is
+       subject to compositor policies.
+
+       Making a surface unfullscreen sets states for the surface based on the following:
+       * the state(s) it may have had before becoming fullscreen
+       * any state(s) decided by the compositor
+       * any state(s) requested by the client while the surface was fullscreen
+
+       The compositor may include the previous window geometry dimensions in
+       the configure event, if applicable.
+
+       The client must also acknowledge the configure when committing the new
+       content (see ack_configure).
+      </description>
+    </request>
+
+    <request name="set_minimized">
+      <description summary="set the window as minimized">
+       Request that the compositor minimize your surface. There is no
+       way to know if the surface is currently minimized, nor is there
+       any way to unset minimization on this surface.
+
+       If you are looking to throttle redrawing when minimized, please
+       instead use the wl_surface.frame event for this, as this will
+       also work with live previews on windows in Alt-Tab, Expose or
+       similar compositor features.
+      </description>
+    </request>
+
+    <event name="configure">
+      <description summary="suggest a surface change">
+       This configure event asks the client to resize its toplevel surface or
+       to change its state. The configured state should not be applied
+       immediately. See xdg_surface.configure for details.
+
+       The width and height arguments specify a hint to the window
+       about how its surface should be resized in window geometry
+       coordinates. See set_window_geometry.
+
+       If the width or height arguments are zero, it means the client
+       should decide its own window dimension. This may happen when the
+       compositor needs to configure the state of the surface but doesn't
+       have any information about any previous or expected dimension.
+
+       The states listed in the event specify how the width/height
+       arguments should be interpreted, and possibly how it should be
+       drawn.
+
+       Clients must send an ack_configure in response to this event. See
+       xdg_surface.configure and xdg_surface.ack_configure for details.
+      </description>
+      <arg name="width" type="int"/>
+      <arg name="height" type="int"/>
+      <arg name="states" type="array"/>
+    </event>
+
+    <event name="close">
+      <description summary="surface wants to be closed">
+       The close event is sent by the compositor when the user
+       wants the surface to be closed. This should be equivalent to
+       the user clicking the close button in client-side decorations,
+       if your application has any.
+
+       This is only a request that the user intends to close the
+       window. The client may choose to ignore this request, or show
+       a dialog to ask the user to save their data, etc.
+      </description>
+    </event>
+
+    <!-- Version 4 additions -->
+
+    <event name="configure_bounds" since="4">
+      <description summary="recommended window geometry bounds">
+       The configure_bounds event may be sent prior to a xdg_toplevel.configure
+       event to communicate the bounds a window geometry size is recommended
+       to constrain to.
+
+       The passed width and height are in surface coordinate space. If width
+       and height are 0, it means bounds is unknown and equivalent to as if no
+       configure_bounds event was ever sent for this surface.
+
+       The bounds can for example correspond to the size of a monitor excluding
+       any panels or other shell components, so that a surface isn't created in
+       a way that it cannot fit.
+
+       The bounds may change at any point, and in such a case, a new
+       xdg_toplevel.configure_bounds will be sent, followed by
+       xdg_toplevel.configure and xdg_surface.configure.
+      </description>
+      <arg name="width" type="int"/>
+      <arg name="height" type="int"/>
+    </event>
+
+    <!-- Version 5 additions -->
+
+    <enum name="wm_capabilities" since="5">
+      <entry name="window_menu" value="1" summary="show_window_menu is available"/>
+      <entry name="maximize" value="2" summary="set_maximized and unset_maximized are available"/>
+      <entry name="fullscreen" value="3" summary="set_fullscreen and unset_fullscreen are available"/>
+      <entry name="minimize" value="4" summary="set_minimized is available"/>
+    </enum>
+
+    <event name="wm_capabilities" since="5">
+      <description summary="compositor capabilities">
+       This event advertises the capabilities supported by the compositor. If
+       a capability isn't supported, clients should hide or disable the UI
+       elements that expose this functionality. For instance, if the
+       compositor doesn't advertise support for minimized toplevels, a button
+       triggering the set_minimized request should not be displayed.
+
+       The compositor will ignore requests it doesn't support. For instance,
+       a compositor which doesn't advertise support for minimized will ignore
+       set_minimized requests.
+
+       Compositors must send this event once before the first
+       xdg_surface.configure event. When the capabilities change, compositors
+       must send this event again and then send an xdg_surface.configure
+       event.
+
+       The configured state should not be applied immediately. See
+       xdg_surface.configure for details.
+
+       The capabilities are sent as an array of 32-bit unsigned integers in
+       native endianness.
+      </description>
+      <arg name="capabilities" type="array" summary="array of 32-bit capabilities"/>
+    </event>
+  </interface>
+
+  <interface name="xdg_popup" version="7">
+    <description summary="short-lived, popup surfaces for menus">
+      A popup surface is a short-lived, temporary surface. It can be used to
+      implement for example menus, popovers, tooltips and other similar user
+      interface concepts.
+
+      A popup can be made to take an explicit grab. See xdg_popup.grab for
+      details.
+
+      When the popup is dismissed, a popup_done event will be sent out, and at
+      the same time the surface will be unmapped. See the xdg_popup.popup_done
+      event for details.
+
+      Explicitly destroying the xdg_popup object will also dismiss the popup and
+      unmap the surface. Clients that want to dismiss the popup when another
+      surface of their own is clicked should dismiss the popup using the destroy
+      request.
+
+      A newly created xdg_popup will be stacked on top of all previously created
+      xdg_popup surfaces associated with the same xdg_toplevel.
+
+      The parent of an xdg_popup must be mapped (see the xdg_surface
+      description) before the xdg_popup itself.
+
+      The client must call wl_surface.commit on the corresponding wl_surface
+      for the xdg_popup state to take effect.
+    </description>
+
+    <enum name="error">
+      <entry name="invalid_grab" value="0"
+            summary="tried to grab after being mapped"/>
+    </enum>
+
+    <request name="destroy" type="destructor">
+      <description summary="remove xdg_popup interface">
+       This destroys the popup. Explicitly destroying the xdg_popup
+       object will also dismiss the popup, and unmap the surface.
+
+       If this xdg_popup is not the "topmost" popup, the
+       xdg_wm_base.not_the_topmost_popup protocol error will be sent.
+      </description>
+    </request>
+
+    <request name="grab">
+      <description summary="make the popup take an explicit grab">
+       This request makes the created popup take an explicit grab. An explicit
+       grab will be dismissed when the user dismisses the popup, or when the
+       client destroys the xdg_popup. This can be done by the user clicking
+       outside the surface, using the keyboard, or even locking the screen
+       through closing the lid or a timeout.
+
+       If the compositor denies the grab, the popup will be immediately
+       dismissed.
+
+       This request must be used in response to some sort of user action like a
+       button press, key press, or touch down event. The serial number of the
+       event should be passed as 'serial'.
+
+       The parent of a grabbing popup must either be an xdg_toplevel surface or
+       another xdg_popup with an explicit grab. If the parent is another
+       xdg_popup it means that the popups are nested, with this popup now being
+       the topmost popup.
+
+       Nested popups must be destroyed in the reverse order they were created
+       in, e.g. the only popup you are allowed to destroy at all times is the
+       topmost one.
+
+       When compositors choose to dismiss a popup, they may dismiss every
+       nested grabbing popup as well. When a compositor dismisses popups, it
+       will follow the same dismissing order as required from the client.
+
+       If the topmost grabbing popup is destroyed, the grab will be returned to
+       the parent of the popup, if that parent previously had an explicit grab.
+
+       If the parent is a grabbing popup which has already been dismissed, this
+       popup will be immediately dismissed. If the parent is a popup that did
+       not take an explicit grab, an error will be raised.
+
+       During a popup grab, the client owning the grab will receive pointer
+       and touch events for all their surfaces as normal (similar to an
+       "owner-events" grab in X11 parlance), while the top most grabbing popup
+       will always have keyboard focus.
+      </description>
+      <arg name="seat" type="object" interface="wl_seat"
+          summary="the wl_seat of the user event"/>
+      <arg name="serial" type="uint" summary="the serial of the user event"/>
+    </request>
+
+    <event name="configure">
+      <description summary="configure the popup surface">
+       This event asks the popup surface to configure itself given the
+       configuration. The configured state should not be applied immediately.
+       See xdg_surface.configure for details.
+
+       The x and y arguments represent the position the popup was placed at
+       given the xdg_positioner rule, relative to the upper left corner of the
+       window geometry of the parent surface.
+
+       For version 2 or older, the configure event for an xdg_popup is only
+       ever sent once for the initial configuration. Starting with version 3,
+       it may be sent again if the popup is setup with an xdg_positioner with
+       set_reactive requested, or in response to xdg_popup.reposition requests.
+      </description>
+      <arg name="x" type="int"
+          summary="x position relative to parent surface window geometry"/>
+      <arg name="y" type="int"
+          summary="y position relative to parent surface window geometry"/>
+      <arg name="width" type="int" summary="window geometry width"/>
+      <arg name="height" type="int" summary="window geometry height"/>
+    </event>
+
+    <event name="popup_done">
+      <description summary="popup interaction is done">
+       The popup_done event is sent out when a popup is dismissed by the
+       compositor. The client should destroy the xdg_popup object at this
+       point.
+      </description>
+    </event>
+
+    <!-- Version 3 additions -->
+
+    <request name="reposition" since="3">
+      <description summary="recalculate the popup's location">
+       Reposition an already-mapped popup. The popup will be placed given the
+       details in the passed xdg_positioner object, and a
+       xdg_popup.repositioned followed by xdg_popup.configure and
+       xdg_surface.configure will be emitted in response. Any parameters set
+       by the previous positioner will be discarded.
+
+       The passed token will be sent in the corresponding
+       xdg_popup.repositioned event. The new popup position will not take
+       effect until the corresponding configure event is acknowledged by the
+       client. See xdg_popup.repositioned for details. The token itself is
+       opaque, and has no other special meaning.
+
+       If multiple reposition requests are sent, the compositor may skip all
+       but the last one.
+
+       If the popup is repositioned in response to a configure event for its
+       parent, the client should send an xdg_positioner.set_parent_configure
+       and possibly an xdg_positioner.set_parent_size request to allow the
+       compositor to properly constrain the popup.
+
+       If the popup is repositioned together with a parent that is being
+       resized, but not in response to a configure event, the client should
+       send an xdg_positioner.set_parent_size request.
+      </description>
+      <arg name="positioner" type="object" interface="xdg_positioner"/>
+      <arg name="token" type="uint" summary="reposition request token"/>
+    </request>
+
+    <event name="repositioned" since="3">
+      <description summary="signal the completion of a repositioned request">
+       The repositioned event is sent as part of a popup configuration
+       sequence, together with xdg_popup.configure and lastly
+       xdg_surface.configure to notify the completion of a reposition request.
+
+       The repositioned event is to notify about the completion of a
+       xdg_popup.reposition request. The token argument is the token passed
+       in the xdg_popup.reposition request.
+
+       Immediately after this event is emitted, xdg_popup.configure and
+       xdg_surface.configure will be sent with the updated size and position,
+       as well as a new configure serial.
+
+       The client should optionally update the content of the popup, but must
+       acknowledge the new popup configuration for the new position to take
+       effect. See xdg_surface.ack_configure for details.
+      </description>
+      <arg name="token" type="uint" summary="reposition request token"/>
+    </event>
+
+  </interface>
+</protocol>
index fb967dc400dc180c27e105bb806c6157aea786f9..4d9455a98c0a21d576034e5dd7ad4b1fe9bc5f16 100644 (file)
 
 #if defined(FEAT_CLIPBOARD) || defined(PROTO)
 
+#if defined(FEAT_WAYLAND_CLIPBOARD)
+// Mime types we support sending and receiving
+// Mimes with a lower index in the array are prioritized first when we are
+// receiving data.
+static const char *supported_mimes[] = {
+    VIMENC_ATOM_NAME,
+    VIM_ATOM_NAME,
+    "text/plain;charset=utf-8",
+    "text/plain",
+    "UTF8_STRING",
+    "STRING",
+    "TEXT"
+};
+
+static void clip_wl_receive_data(Clipboard_T *cbd,
+       const char *mime_type, int fd);
+static void clip_wl_send_data(const char *mime_type, int fd,
+       wayland_selection_T);
+static void clip_wl_selection_cancelled(wayland_selection_T selection);
+
+#if defined(USE_SYSTEM) && defined(PROTO)
+static int clip_wl_owner_exists(Clipboard_T *cbd);
+#endif
+
+#endif
+
 /*
  * Selection stuff using Visual mode, for cutting and pasting text to other
  * windows.
@@ -50,6 +76,10 @@ clip_init(int can_use)
     cb = &clip_star;
     for (;;)
     {
+       // No need to init again if cbd is already available
+       if (can_use && cb->available)
+           goto skip;
+
        cb->available  = can_use;
        cb->owned      = FALSE;
        cb->start.lnum = 0;
@@ -58,6 +88,7 @@ clip_init(int can_use)
        cb->end.col    = 0;
        cb->state      = SELECT_CLEARED;
 
+skip:
        if (cb == &clip_plus)
            break;
        cb = &clip_plus;
@@ -109,13 +140,27 @@ clip_update_selection(Clipboard_T *clip)
     static int
 clip_gen_own_selection(Clipboard_T *cbd)
 {
-#ifdef FEAT_XCLIPBOARD
+#if defined(FEAT_XCLIPBOARD) || defined(FEAT_WAYLAND_CLIPBOARD)
 # ifdef FEAT_GUI
     if (gui.in_use)
        return clip_mch_own_selection(cbd);
     else
 # endif
-       return clip_xterm_own_selection(cbd);
+    {
+       if (clipmethod == CLIPMETHOD_WAYLAND)
+       {
+#ifdef FEAT_WAYLAND_CLIPBOARD
+           return clip_wl_own_selection(cbd);
+#endif
+       }
+       else if (clipmethod == CLIPMETHOD_X11)
+       {
+#ifdef FEAT_XCLIPBOARD
+           return clip_xterm_own_selection(cbd);
+#endif
+       }
+    }
+    return FAIL;
 #else
     return clip_mch_own_selection(cbd);
 #endif
@@ -128,7 +173,7 @@ clip_own_selection(Clipboard_T *cbd)
      * Also want to check somehow that we are reading from the keyboard rather
      * than a mapping etc.
      */
-#ifdef FEAT_X11
+#if defined(FEAT_X11) || defined(FEAT_WAYLAND_CLIPBOARD)
     // Always own the selection, we might have lost it without being
     // notified, e.g. during a ":sh" command.
     if (cbd->available)
@@ -160,13 +205,26 @@ clip_own_selection(Clipboard_T *cbd)
     static void
 clip_gen_lose_selection(Clipboard_T *cbd)
 {
-#ifdef FEAT_XCLIPBOARD
+#if defined(FEAT_XCLIPBOARD) || defined(FEAT_WAYLAND_CLIPBOARD)
 # ifdef FEAT_GUI
     if (gui.in_use)
        clip_mch_lose_selection(cbd);
     else
 # endif
-       clip_xterm_lose_selection(cbd);
+    {
+       if (clipmethod == CLIPMETHOD_WAYLAND)
+       {
+#ifdef FEAT_WAYLAND_CLIPBOARD
+           clip_wl_lose_selection(cbd);
+#endif
+       }
+       else if (clipmethod == CLIPMETHOD_X11)
+       {
+#ifdef FEAT_XCLIPBOARD
+           clip_xterm_lose_selection(cbd);
+#endif
+       }
+    }
 #else
     clip_mch_lose_selection(cbd);
 #endif
@@ -196,9 +254,9 @@ clip_lose_selection(Clipboard_T *cbd)
        // windows on the current buffer.
        if (was_owned
                && (get_real_state() == MODE_VISUAL
-                                           || get_real_state() == MODE_SELECT)
+                   || get_real_state() == MODE_SELECT)
                && (cbd == &clip_star ?
-                               clip_isautosel_star() : clip_isautosel_plus())
+                   clip_isautosel_star() : clip_isautosel_plus())
                && HL_ATTR(HLF_V) != HL_ATTR(HLF_VNC)
                && !exiting)
        {
@@ -1195,13 +1253,26 @@ clip_gen_set_selection(Clipboard_T *cbd)
            return;
        }
     }
-#ifdef FEAT_XCLIPBOARD
+#if defined(FEAT_XCLIPBOARD) || defined(FEAT_WAYLAND_CLIPBOARD)
 # ifdef FEAT_GUI
     if (gui.in_use)
        clip_mch_set_selection(cbd);
     else
 # endif
-       clip_xterm_set_selection(cbd);
+    {
+       if (clipmethod == CLIPMETHOD_WAYLAND)
+       {
+#ifdef FEAT_WAYLAND_CLIPBOARD
+           clip_wl_set_selection(cbd);
+#endif
+       }
+       else if (clipmethod == CLIPMETHOD_X11)
+       {
+#ifdef FEAT_XCLIPBOARD
+           clip_xterm_set_selection(cbd);
+#endif
+       }
+    }
 #else
     clip_mch_set_selection(cbd);
 #endif
@@ -1210,13 +1281,26 @@ clip_gen_set_selection(Clipboard_T *cbd)
     static void
 clip_gen_request_selection(Clipboard_T *cbd)
 {
-#ifdef FEAT_XCLIPBOARD
+#if defined(FEAT_XCLIPBOARD) || defined(FEAT_WAYLAND_CLIPBOARD)
 # ifdef FEAT_GUI
     if (gui.in_use)
        clip_mch_request_selection(cbd);
     else
 # endif
-       clip_xterm_request_selection(cbd);
+    {
+       if (clipmethod == CLIPMETHOD_WAYLAND)
+       {
+#ifdef FEAT_WAYLAND_CLIPBOARD
+           clip_wl_request_selection(cbd);
+#endif
+       }
+       else if (clipmethod == CLIPMETHOD_X11)
+       {
+#ifdef FEAT_XCLIPBOARD
+           clip_xterm_request_selection(cbd);
+#endif
+       }
+    }
 #else
     clip_mch_request_selection(cbd);
 #endif
@@ -1231,7 +1315,8 @@ clip_x11_owner_exists(Clipboard_T *cbd)
 }
 #endif
 
-#if (defined(FEAT_X11) && defined(USE_SYSTEM)) || defined(PROTO)
+#if ((defined(FEAT_X11) || defined(FEAT_WAYLAND_CLIPBOARD)) \
+       && defined(USE_SYSTEM)) || defined(PROTO)
     int
 clip_gen_owner_exists(Clipboard_T *cbd UNUSED)
 {
@@ -1241,7 +1326,22 @@ clip_gen_owner_exists(Clipboard_T *cbd UNUSED)
        return clip_gtk_owner_exists(cbd);
     else
 # endif
-       return clip_x11_owner_exists(cbd);
+    {
+       if (clipmethod == CLIPMETHOD_WAYLAND)
+       {
+#ifdef FEAT_WAYLAND_CLIPBOARD
+           return clip_wl_owner_exists(cbd);
+#endif
+       }
+       else if (clipmethod == CLIPMETHOD_X11)
+       {
+#ifdef FEAT_XCLIPBOARD
+           return clip_x11_owner_exists(cbd);
+#endif
+       }
+       else
+           return FALSE;
+    }
 #else
     return TRUE;
 #endif
@@ -2228,4 +2328,550 @@ adjust_clip_reg(int *rp)
     }
 }
 
+#if defined(FEAT_WAYLAND_CLIPBOARD) || defined(PROTO)
+
+/*
+ * Read data from a file descriptor and write it to the given clipboard.
+ */
+    static void
+clip_wl_receive_data(Clipboard_T *cbd, const char *mime_type, int fd)
+{
+    char_u         *start, *buf, *tmp, *final, *enc;
+    int                    motion_type = MAUTO;
+    ssize_t        r = 0;
+    size_t         total = 0, max_total = 4096; // Initial buffer size, 4096
+                                                // bytes seems reasonable.
+#ifndef HAVE_SELECT
+    struct pollfd   pfd
+
+    pfd.fd = fd,
+    pfd.events = POLLIN
+#else
+    fd_set rfds;
+    struct timeval  tv;
+
+    FD_ZERO(&rfds);
+    FD_SET(fd, &rfds);
+    tv.tv_sec = 0;
+    tv.tv_usec = p_wtm * 1000;
+#endif
+
+    // Make pipe (read end) non-blocking
+    if (fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK) == -1)
+       return;
+
+    if ((buf = alloc_clear(max_total)) == NULL)
+       return;
+    start = buf;
+
+    // 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, max_total - 1 - total);
+
+       if (r == 0)
+           break;
+       else if (r < 0)
+       {
+           if (errno == EAGAIN || errno == EINTR)
+           {
+poll_data:
+#ifndef HAVE_SELECT
+               if (poll(&pfd, 1, p_wtm) > 0)
+#else
+               if (select(fd + 1, &rfds, NULL, NULL, &tv) > 0)
+#endif
+                   continue;
+           }
+           break;
+       }
+
+       start += r;
+       total += (size_t)r;
+
+       // Realloc if we are at the end of the buffer
+       if (total >= max_total - 1)
+       {
+           tmp = vim_realloc(buf, max_total * 2);
+           if (tmp == NULL)
+               break;
+           max_total *= 2; // Double buffer size each time
+           buf = tmp;
+           start = buf + total;
+           // Zero out the newly allocated memory part
+           vim_memset(buf + total, 0, max_total - total);
+       }
+    }
+
+    if (total == 0)
+    {
+       clip_free_selection(cbd); // Nothing received, clear register
+       vim_free(buf);
+       return;
+    }
+
+    final = buf;
+
+    if (STRCMP(mime_type, VIM_ATOM_NAME) == 0 && total >= 2)
+    {
+       motion_type = *final++;;
+       total--;
+    }
+    else if (STRCMP(mime_type, VIMENC_ATOM_NAME) == 0 && total >= 3)
+    {
+       vimconv_T conv;
+       int convlen;
+
+       // first byte is motion type
+       motion_type = *final++;
+       total--;
+
+       // Get encoding of selection
+       enc = final;
+
+       // Skip the encoding type including null terminator in final text
+       final += STRLEN(final) + 1;
+
+       // Subtract pointers to get length of encoding;
+       total -= final - enc;
+
+       conv.vc_type = CONV_NONE;
+       convert_setup(&conv, enc, p_enc);
+       if (conv.vc_type != CONV_NONE)
+       {
+          convlen = total;
+          tmp = string_convert(&conv, final, &convlen);
+          total = convlen;
+          if (tmp != NULL)
+               final = tmp;
+          convert_setup(&conv, NULL, NULL);
+       }
+    }
+
+    clip_yank_selection(motion_type, final, (long)total, cbd);
+    vim_free(buf);
+}
+
+/*
+ * Get the current selection and fill the respective register for cbd with the
+ * data.
+ */
+    void
+clip_wl_request_selection(Clipboard_T *cbd)
+{
+    wayland_selection_T            selection;
+    garray_T               *mime_types;
+    int                            len;
+    int                            fd;
+    const char             *chosen_mime = NULL;
+
+    if (cbd == &clip_star)
+       selection = WAYLAND_SELECTION_PRIMARY;
+    else if (cbd == &clip_plus)
+       selection = WAYLAND_SELECTION_REGULAR;
+    else
+       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)
+    {
+       // Selection is empty/cleared
+       clip_free_selection(cbd);
+       return;
+    }
+
+    len = ARRAY_LENGTH(supported_mimes);
+
+    // 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->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;
+
+    fd = wayland_cb_receive_data(chosen_mime, selection);
+
+    if (fd == -1)
+       return;
+
+    // Start reading the file descriptor returned
+    clip_wl_receive_data(cbd, chosen_mime, fd);
+
+    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
+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
+
+    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;
+
+    // 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(&string, &length, cbd);
+
+    if (motion_type < 0)
+       goto exit;
+
+    if (STRCMP(mime_type, VIMENC_ATOM_NAME) == 0)
+    {
+       did_vimenc = FALSE;
+       did_motion_type = FALSE;
+    }
+    else if (STRCMP(mime_type, VIM_ATOM_NAME) == 0)
+       did_motion_type = FALSE;
+
+    while ((total < (size_t)length || skip_len_check) &&
+#ifndef HAVE_SELECT
+          poll(&pfd, 1, p_wtm) > 0)
+#else
+          select(fd + 1, NULL, &wfds, NULL, &tv) > 0)
+#endif
+    {
+       // 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 (written == -1)
+          break;
+       total += written;
+    }
+exit:
+    vim_free(string);
+}
+
+/*
+ * Called if another client gains ownership of the given selection. If so then
+ * lose the selection internally.
+ */
+    static void
+clip_wl_selection_cancelled(wayland_selection_T selection)
+{
+    if (selection == WAYLAND_SELECTION_REGULAR)
+       clip_lose_selection(&clip_plus);
+    else if (selection == WAYLAND_SELECTION_PRIMARY)
+       clip_lose_selection(&clip_star);
+}
+
+/*
+ * Own the selection that cbd corresponds to. Start listening for requests from
+ * other Wayland clients so they can receive data from us. Returns OK on success
+ * and FAIL on failure.
+ */
+    int
+clip_wl_own_selection(Clipboard_T *cbd)
+{
+    wayland_selection_T selection;
+
+    if (cbd == &clip_star)
+       selection = WAYLAND_SELECTION_PRIMARY;
+    else if (cbd == &clip_plus)
+       selection = WAYLAND_SELECTION_REGULAR;
+    else
+       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. Note that the the cancelled
+ * event is not sent when the data source is destroyed.
+ */
+    void
+clip_wl_lose_selection(Clipboard_T *cbd)
+{
+    if (cbd == &clip_plus)
+       wayland_cb_lose_selection(WAYLAND_SELECTION_REGULAR);
+    else if (cbd == &clip_star)
+       wayland_cb_lose_selection(WAYLAND_SELECTION_PRIMARY);
+
+    /* wayland_cb_lose_selection(selection); */
+}
+
+/*
+ * Send the current selection to the clipboard. Do nothing for wayland because
+ * we will fill in the selection only when requested by another client.
+ */
+    void
+clip_wl_set_selection(Clipboard_T *cbd UNUSED)
+{
+}
+
+#if defined(USE_SYSTEM) && defined(PROTO)
+/*
+ * Return TRUE if we own the selection corresponding to cbd
+ */
+    static int
+clip_wl_owner_exists(Clipboard_T *cbd)
+{
+    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
+
+#endif // FEAT_WAYLAND_CLIPBOARD
+
+
+/*
+ * Returns the first method for accessing the clipboard that is available/works,
+ * depending on the order of values in str.
+ */
+    static clipmethod_T
+get_clipmethod(char_u *str)
+{
+    int                len     = (int)STRLEN(str) + 1;
+    char_u     *buf    = alloc(len);
+
+    if (buf == NULL)
+       return CLIPMETHOD_FAIL;
+
+    clipmethod_T ret = CLIPMETHOD_FAIL;
+    char_u     *p = str;
+
+    while (*p != NUL)
+    {
+       clipmethod_T method = CLIPMETHOD_NONE;
+
+       (void)copy_option_part(&p, buf, len, ",");
+
+       if (STRCMP(buf, "wayland") == 0)
+       {
+#ifdef FEAT_WAYLAND_CLIPBOARD
+           if (wayland_cb_is_ready())
+               method = CLIPMETHOD_WAYLAND;
+#endif
+       }
+       else if (STRCMP(buf, "x11") == 0)
+       {
+#ifdef FEAT_XCLIPBOARD
+           // x_IOerror_handler() in os_unix.c should set xterm_dpy to NULL if
+           // we lost connection to the X server.
+           if (xterm_dpy != NULL)
+           {
+               // If the X connection is lost then that handler will longjmp
+               // somewhere else, in that case we will call choose_clipmethod()
+               // again from there, and this if block won't be executed since
+               // xterm_dpy will be set to NULL.
+               xterm_update();
+               method = CLIPMETHOD_X11;
+           }
+#endif
+       }
+       else
+       {
+           ret = CLIPMETHOD_FAIL;
+           goto exit;
+       }
+
+       // Keep on going in order to catch errors
+       if (method != CLIPMETHOD_NONE && ret == CLIPMETHOD_FAIL)
+           ret = method;
+    }
+
+    // No match found, use "none".
+    ret = (ret == CLIPMETHOD_FAIL) ? CLIPMETHOD_NONE : ret;
+
+exit:
+    vim_free(buf);
+    return ret;
+}
+
+
+/*
+ * Returns name of clipmethod in a statically allocated string.
+ */
+    static char *
+clipmethod_to_str(clipmethod_T method)
+{
+    switch(method)
+    {
+       case CLIPMETHOD_WAYLAND:
+           return "wayland";
+       case CLIPMETHOD_X11:
+           return "x11";
+       default:
+           return "none";
+    }
+}
+
+/*
+ * Sets the current clipmethod to use given by `get_clipmethod()`. Returns an
+ * error message on failure else NULL.
+ */
+    char *
+choose_clipmethod(void)
+{
+    // We call get_clipmethod first so that we can catch any errors, even if
+    // clipmethod is useless
+    clipmethod_T method = get_clipmethod(p_cpm);
+
+    if (method == CLIPMETHOD_FAIL)
+       return e_invalid_argument;
+
+// If GUI is running or we are not on a system with wayland or x11, then always
+// return CLIPMETHOD_NONE. System or GUI clipboard handling always overrides.
+#if defined(FEAT_XCLIPBOARD) || defined(FEAT_WAYLAND_CLIPBOARD)
+#if defined(FEAT_GUI)
+    if (gui.in_use)
+    {
+#ifdef FEAT_WAYLAND
+       // We only interact with wayland for the clipboard, we can just deinit
+       // everything.
+       wayland_uninit_client();
+#endif
+
+       method = CLIPMETHOD_NONE;
+       goto lose_sel_exit;
+    }
+#endif
+#else
+    // If on a system like windows or macos, then clipmethod is irrelevant, we
+    // use their way of accessing the clipboard.
+    method = CLIPMETHOD_NONE;
+    goto exit;
+#endif
+
+    // Deinitialize clipboard if there is no way to access clipboard
+    if (method == CLIPMETHOD_NONE)
+       clip_init(FALSE);
+    // If we have a clipmethod that works now, then initialize clipboard
+    else if (clipmethod == CLIPMETHOD_NONE
+           && method != CLIPMETHOD_NONE)
+    {
+       clip_init(TRUE);
+       did_warn_clipboard = FALSE;
+    }
+
+    // Disown clipboard if we are switching to a new method
+    if (clipmethod != CLIPMETHOD_NONE && method != clipmethod)
+    {
+#if (defined(FEAT_XCLIPBOARD) || defined(FEAT_WAYLAND_CLIPBOARD)) \
+       && defined(FEAT_GUI)
+lose_sel_exit:
+#endif
+       if (clip_star.owned)
+           clip_lose_selection(&clip_star);
+       if (clip_plus.owned)
+           clip_lose_selection(&clip_plus);
+    }
+
+#if !defined(FEAT_XCLIPBOARD) && !defined(FEAT_WAYLAND_CLIPBOARD)
+exit:
+#endif
+
+    clipmethod = method;
+
+#ifdef FEAT_EVAL
+    set_vim_var_string(VV_CLIPMETHOD, (char_u*)clipmethod_to_str(method), -1);
+#endif
+
+    return NULL;
+}
+
+/*
+ * Call choose_clipmethod().
+ */
+    void
+ex_clipreset(exarg_T *eap UNUSED)
+{
+    clipmethod_T prev = clipmethod;
+
+    choose_clipmethod();
+
+    if (clipmethod == CLIPMETHOD_NONE)
+       smsg(_("Could not find a way to access the clipboard."));
+    else if (clipmethod != prev)
+       smsg(_("Switched to clipboard method '%s'."),
+               clipmethod_to_str(clipmethod));
+}
+
 #endif // FEAT_CLIPBOARD
index 79cb37c467f348fa187be839ee8c42a5b374cfbc..1b8495515ff484e8d0377fe278d11e384230e4a3 100644 (file)
@@ -9,6 +9,9 @@
 /* Define unless no X support found */
 #undef HAVE_X11
 
+/* Define unless no Wayland support found */
+#undef HAVE_WAYLAND
+
 /* Define when terminfo support found */
 #undef TERMINFO
 
 /* Define if we have flock() */
 #undef HAVE_FLOCK
 
+/* Define if we have shm_open() */
+#undef HAVE_SHM_OPEN
+
 /* Define to inline symbol or empty */
 #undef inline
 
index 2aaaf69f4343f31b41b6897465646693d48a0fa1..f893e8f0b1024f2622402b82a9dfc2a61af55587 100644 (file)
@@ -37,6 +37,10 @@ X_PRE_LIBS   = @X_PRE_LIBS@
 X_EXTRA_LIBS   = @X_EXTRA_LIBS@
 X_LIBS         = @X_LIB@
 
+WAYLAND_LIBS    = @WAYLAND_LIBS@
+WAYLAND_SRC    = @WAYLAND_SRC@
+WAYLAND_OBJ    = @WAYLAND_OBJ@
+
 XDIFF_OBJS_USED        = @XDIFF_OBJS_USED@
 
 LUA_LIBS       = @LUA_LIBS@
index 8abec36b07ffc54c063a2f900cd724f8015dd5d3..c91c841efcd59533ac7947010fb2b31558e7a98c 100644 (file)
@@ -2389,6 +2389,64 @@ AC_ARG_ENABLE(fontset,
 AC_MSG_RESULT($enable_fontset)
 dnl defining FEAT_XFONTSET is delayed, so that it can be disabled for no GUI
 
+AC_MSG_CHECKING(if shm_open is available)
+cppflags_save=$CPPFLAGS
+CPPFLAGS="$CPPFLAGS $X_CFLAGS"
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
+                  [#include <sys/mman.h>
+                   #include <sys/stat.h>
+                   #include <fcntl.h>], [shm_open("/test", O_CREAT, 0600);])],
+                  AC_MSG_RESULT(yes); AC_DEFINE(HAVE_SHM_OPEN),
+                  AC_MSG_RESULT(no))
+CPPFLAGS=$cppflags_save
+
+AC_MSG_CHECKING(--with-wayland argument)
+AC_ARG_WITH(wayland,
+       [  --with-wayland         Include support for the Wayland protocol.])
+
+test -z "$with_wayland" && with_wayland=yes
+if test "$with_wayland" = yes; then
+  AC_MSG_RESULT(yes)
+  AC_MSG_CHECKING(if wayland client header files can be found)
+  cppflags_save=$CPPFLAGS
+  CPPFLAGS="$CPPFLAGS $X_CFLAGS"
+  AC_COMPILE_IFELSE([AC_LANG_PROGRAM([#include <wayland-client.h>], )],
+       AC_MSG_RESULT(yes),
+       AC_MSG_RESULT(no); no_wl=yes)
+  CPPFLAGS=$cppflags_save
+
+  if test "$no_wl" = yes; then
+    with_wayland=no
+  else
+    AC_CHECK_LIB(wayland-client, wl_display_connect,[no_wl=no])
+
+    if test "$no_wl" = no; then
+      AC_DEFINE(HAVE_WAYLAND)
+      WAYLAND_LIBS="-lwayland-client";
+      AC_SUBST(WAYLAND_LIBS)
+      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"
+      AC_SUBST(WAYLAND_SRC)
+      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"
+      AC_SUBST(WAYLAND_OBJ)
+
+    else
+      with_wayland=no
+    fi
+  fi
+else
+  AC_MSG_RESULT(no)
+fi
+
 test -z "$with_x" && with_x=yes
 test "${enable_gui-yes}" != no -a "x$MACOS_X" != "xyes" -a "x$QNX" != "xyes" && with_x=yes
 if test "$with_x" = no; then
index eddfc1b55a346c620a88e50ae3d7d50d7fea064e..042ab287c992fc744cfcc1a3d8bd90b23793eebf 100644 (file)
@@ -3732,3 +3732,7 @@ EXTERN char e_cannot_switch_to_a_closing_buffer[]
        INIT(= N_("E1546: Cannot switch to a closing buffer"));
 EXTERN char e_cannot_not_support_redrawtabpanel[]
        INIT(= N_("E1547: This version of Vim does support :redrawtabpanel"));
+#ifdef FEAT_WAYLAND
+EXTERN char e_wayland_connection_unavailable[]
+       INIT(= N_("E1548: Wayland connection is unavailable"));
+#endif
index 2e864f74fcf5dc1515fec6adc35e74f9a43eb4cc..a31ad1341fb6b1d1c72bb11137ff7546a4de6d3c 100644 (file)
@@ -7489,7 +7489,8 @@ f_has(typval_T *argvars, typval_T *rettv)
 #endif
                },
        {"unnamedplus",
-#if defined(FEAT_CLIPBOARD) && defined(FEAT_X11)
+#if defined(FEAT_CLIPBOARD) && (defined(FEAT_X11) \
+       || defined(FEAT_WAYLAND_CLIPBOARD))
                1
 #else
                0
@@ -7526,6 +7527,20 @@ f_has(typval_T *argvars, typval_T *rettv)
                1
 #else
                0
+#endif
+               },
+       {"wayland",
+#ifdef FEAT_WAYLAND
+               1
+#else
+               0
+#endif
+               },
+       {"wayland_clipboard",
+#ifdef FEAT_WAYLAND_CLIPBOARD
+               1
+#else
+               0
 #endif
                },
        {"wildignore", 1},
index 9382842f37f46e6d6233b8bf31af627efdddcdf3..d94d3e860857b7758fa5be1fe0e1081a88a67c50 100644 (file)
@@ -163,6 +163,8 @@ static struct vimvar
     {VV_NAME("t_enumvalue",     VAR_NUMBER), NULL, VV_RO},
     {VV_NAME("stacktrace",      VAR_LIST), &t_list_dict_any, VV_RO},
     {VV_NAME("t_tuple",                 VAR_NUMBER), NULL, VV_RO},
+    {VV_NAME("wayland_display",  VAR_STRING), NULL, VV_RO},
+    {VV_NAME("clipmethod",  VAR_STRING), NULL, VV_RO},
 };
 
 // shorthand
index 92176ce6142190781041ed33952a146f4bbd87df..a406f397d047091f9c4ad4b72d0621975bea5805 100644 (file)
@@ -8,29 +8,29 @@ static const unsigned short cmdidxs1[26] =
   /* a */ 0,
   /* b */ 21,
   /* c */ 45,
-  /* d */ 112,
-  /* e */ 138,
-  /* f */ 167,
-  /* g */ 184,
-  /* h */ 190,
-  /* i */ 200,
-  /* j */ 221,
-  /* k */ 223,
-  /* l */ 228,
-  /* m */ 291,
-  /* n */ 309,
-  /* o */ 329,
-  /* p */ 341,
-  /* q */ 382,
-  /* r */ 385,
-  /* s */ 406,
-  /* t */ 476,
-  /* u */ 523,
-  /* v */ 535,
-  /* w */ 556,
-  /* x */ 570,
-  /* y */ 580,
-  /* z */ 581
+  /* d */ 113,
+  /* e */ 139,
+  /* f */ 168,
+  /* g */ 185,
+  /* h */ 191,
+  /* i */ 201,
+  /* j */ 222,
+  /* k */ 224,
+  /* l */ 229,
+  /* m */ 292,
+  /* n */ 310,
+  /* o */ 330,
+  /* p */ 342,
+  /* q */ 383,
+  /* r */ 386,
+  /* s */ 407,
+  /* t */ 477,
+  /* u */ 524,
+  /* v */ 536,
+  /* w */ 557,
+  /* x */ 572,
+  /* y */ 582,
+  /* z */ 583
 };
 
 /*
@@ -43,7 +43,7 @@ static const unsigned char cmdidxs2[26][26] =
 { /*         a   b   c   d   e   f   g   h   i   j   k   l   m   n   o   p   q   r   s   t   u   v   w   x   y   z */
   /* a */ {  0,  1,  0,  0,  0,  0,  0,  0,  0,  0,  0,  5,  6,  7,  0,  0,  0,  8, 17,  0, 18,  0,  0,  0,  0,  0 },
   /* b */ {  2,  0,  0,  5,  6,  8,  0,  0,  0,  0,  0,  9, 10, 11, 12, 13,  0, 14,  0,  0,  0,  0, 23,  0,  0,  0 },
-  /* c */ {  3, 12, 16, 18, 20, 22, 25,  0,  0,  0,  0, 33, 38, 41, 47, 57, 59, 60, 61,  0, 63,  0, 66,  0,  0,  0 },
+  /* c */ {  3, 12, 16, 18, 20, 22, 25,  0,  0,  0,  0, 33, 39, 42, 48, 58, 60, 61, 62,  0, 64,  0, 67,  0,  0,  0 },
   /* d */ {  0,  0,  0,  0,  0,  0,  0,  0,  9, 19,  0, 20,  0,  0, 21,  0,  0, 23, 24,  0,  0,  0,  0,  0,  0,  0 },
   /* e */ {  1,  0,  2,  0,  0,  0,  0,  0,  0,  0,  0,  9, 11, 12,  0,  0,  0,  0,  0,  0,  0, 23,  0, 24,  0,  0 },
   /* f */ {  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 10,  0,  0,  0,  0,  0, 16,  0,  0,  0,  0,  0 },
@@ -63,10 +63,10 @@ static const unsigned char cmdidxs2[26][26] =
   /* t */ {  2,  0, 19,  0, 24, 26,  0, 27,  0, 29,  0, 30, 34, 37, 39, 40,  0, 41, 43,  0, 44,  0,  0,  0, 46,  0 },
   /* u */ {  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 11,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0 },
   /* v */ {  1,  0,  0,  0,  2,  0,  0,  0,  5,  0,  0,  0, 12, 15,  0,  0,  0,  0, 18,  0, 19,  0,  0,  0,  0,  0 },
-  /* w */ {  2,  0,  0,  0,  0,  0,  0,  3,  4,  0,  0,  0,  0,  8,  0,  9, 10,  0,  0,  0, 12, 13,  0,  0,  0,  0 },
+  /* w */ {  2,  0,  0,  0,  0,  0,  0,  3,  4,  0,  0,  8,  0,  9,  0, 10, 11,  0,  0,  0, 13, 14,  0,  0,  0,  0 },
   /* x */ {  1,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  2,  5,  0,  0,  0,  7,  0,  0,  8,  0,  0,  0,  0,  0 },
   /* y */ {  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0 },
   /* z */ {  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0 }
 };
 
-static const int command_count = 598;
+static const int command_count = 600;
index 3cb8a18fa241e474bebc0c55200af5c68a227694..a42ad7ddfeab5b259ed91a3b54b3924d61c742b2 100644 (file)
@@ -350,6 +350,9 @@ EXCMD(CMD_checktime,        "checktime",    ex_checktime,
 EXCMD(CMD_chistory,    "chistory",     qf_history,
        EX_RANGE|EX_COUNT|EX_TRLBAR,
        ADDR_UNSIGNED),
+EXCMD(CMD_clipreset,   "clipreset",    ex_clipreset,
+       EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK,
+       ADDR_NONE),
 EXCMD(CMD_clist,       "clist",        qf_list,
        EX_BANG|EX_EXTRA|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK,
        ADDR_NONE),
@@ -1808,6 +1811,9 @@ EXCMD(CMD_windo,  "windo",        ex_listdo,
 EXCMD(CMD_winpos,      "winpos",       ex_winpos,
        EX_EXTRA|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK,
        ADDR_NONE),
+EXCMD(CMD_wlrestore,   "wlrestore",    ex_wlrestore,
+       EX_EXTRA|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK|EX_BANG,
+       ADDR_NONE),
 EXCMD(CMD_wnext,       "wnext",        ex_wnext,
        EX_RANGE|EX_BANG|EX_FILE1|EX_ARGOPT|EX_TRLBAR,
        ADDR_OTHER),
index 4ccc5f7592992c9adffb261f147267ab7c7983bf..ba0bff46687a0dc95043173333c422eea6e2d0f1 100644 (file)
@@ -373,6 +373,12 @@ static void        ex_folddo(exarg_T *eap);
 #if !defined(FEAT_X11) || !defined(FEAT_XCLIPBOARD)
 # define ex_xrestore           ex_ni
 #endif
+#if !defined(FEAT_WAYLAND)
+# define ex_wlrestore          ex_ni
+#endif
+#if !defined(FEAT_CLIPBOARD)
+# define ex_clipreset          ex_ni
+#endif
 #if !defined(FEAT_PROP_POPUP)
 # define ex_popupclear         ex_ni
 #endif
index 56d19585c2dc3f053a2c91ccdee0743d7d2d1817..07db96cf132c421322006b0f1dcd2ee5e105e15f 100644 (file)
 # define WANT_X11
 #endif
 
+/*
+ * +wayland            Unix only.  Include code for the wayland protocol,
+ *                      only works if HAVE_WAYLAND is defined.
+ */
+#if defined(FEAT_NORMAL) && defined(UNIX)
+# define WANT_WAYLAND
+#endif
+
 /*
  * XSMP - X11 Session Management Protocol
  * It may be preferred to disable this if the GUI supports it (e.g.,
 # endif
 #endif
 
+#if defined(FEAT_NORMAL) && defined(UNIX) \
+    && defined(HAVE_WAYLAND) && defined(WANT_WAYLAND)
+# define FEAT_WAYLAND_CLIPBOARD
+# ifndef FEAT_CLIPBOARD
+#  define FEAT_CLIPBOARD
+# endif
+#endif
+
 /*
  * +dnd                Drag'n'drop support.  Always used for the GTK+ GUI.
  */
index 38e9b8bbf5f40a02441d2004bd84c49dfa2fb46d..31fbf4f0d4b74079e4d77d4ffd2e40a959d48cad 100644 (file)
@@ -971,9 +971,9 @@ EXTERN int  gui_win_y INIT(= -1);
 #endif
 
 #ifdef FEAT_CLIPBOARD
-EXTERN Clipboard_T clip_star;  // PRIMARY selection in X11
-# ifdef FEAT_X11
-EXTERN Clipboard_T clip_plus;  // CLIPBOARD selection in X11
+EXTERN Clipboard_T clip_star;  // PRIMARY selection in X11/Wayland
+# if defined(FEAT_X11) || defined(FEAT_WAYLAND_CLIPBOARD)
+EXTERN Clipboard_T clip_plus;  // CLIPBOARD selection in X11/Wayland
 # else
 #  define clip_plus clip_star  // there is only one clipboard
 #  define ONE_CLIPBOARD
@@ -2069,3 +2069,23 @@ EXTERN char_u showcmd_buf[SHOWCMD_BUFLEN];
 #ifdef FEAT_TERMGUICOLORS
 EXTERN int     p_tgc_set INIT(= FALSE);
 #endif
+
+// If we've already warned about missing/unavailable clipboard
+EXTERN int did_warn_clipboard INIT(= FALSE);
+
+#ifdef FEAT_CLIPBOARD
+EXTERN clipmethod_T clipmethod INIT(= CLIPMETHOD_NONE);
+#endif
+
+#ifdef FEAT_WAYLAND
+
+// 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
index fbbfb8721404376a8668528019435fbc0a89dfb3..4d596519323da193b068fdf6dcb59fbd04ff3055 100644 (file)
--- a/src/gui.c
+++ b/src/gui.c
@@ -146,6 +146,9 @@ gui_start(char_u *arg UNUSED)
            emsg(msg);
 #endif
     }
+    else
+       // Reset clipmethod to CLIPMETHOD_NONE
+       choose_clipmethod();
 
     vim_free(old_term);
 
index 31494e3e8e19271a189670616be7903a02f9fe9d..98a17bc3764b06681fec3ea5f07462f1bf68a883 100644 (file)
@@ -449,7 +449,7 @@ main
 #endif // NO_VIM_MAIN
 #endif // PROTO
 
-#if defined(FEAT_X11) && defined(FEAT_XCLIPBOARD)
+#if defined(FEAT_X11) && defined(FEAT_XCLIPBOARD) && defined(FEAT_CLIPBOARD)
 /*
  * Restore the state after a fatal X error.
  */
@@ -475,6 +475,7 @@ x_restore_state(void)
     starttermcap();
     scroll_start();
     redraw_later_clear();
+    choose_clipmethod();
 }
 #endif
 
@@ -667,7 +668,7 @@ vim_main2(void)
 # endif
     {
        setup_term_clip();
-       TIME_MSG("setup clipboard");
+       TIME_MSG("setup x11 clipboard");
     }
 #endif
 
@@ -676,6 +677,27 @@ vim_main2(void)
     prepare_server(&params);
 #endif
 
+#ifdef FEAT_WAYLAND
+# ifdef FEAT_GUI
+    if (!gui.in_use)
+# endif
+    {
+       if (wayland_init_client(wayland_display_name) == OK)
+       {
+           TIME_MSG("connected to wayland display");
+
+# ifdef FEAT_WAYLAND_CLIPBOARD
+           if (wayland_cb_init((char*)p_wse) == OK)
+               TIME_MSG("setup wayland clipboard");
+       }
+# endif
+    }
+#endif
+
+#ifdef FEAT_CLIPBOARD
+    choose_clipmethod();
+#endif
+
     /*
      * If "-" argument given: Read file from stdin.
      * Do this before starting Raw mode, because it may change things that the
@@ -2458,6 +2480,11 @@ command_line_scan(mparm_T *parmp)
            case 'X':           // "-X"  don't connect to X server
 #if (defined(UNIX) || defined(VMS)) && defined(FEAT_X11)
                x_no_connect = TRUE;
+#endif
+               break;
+           case 'Y':           // "-Y" don't connect to wayland compositor
+#if defined(FEAT_WAYLAND)
+               wayland_no_connect = TRUE;
 #endif
                break;
 
@@ -3665,6 +3692,9 @@ usage(void)
 # endif
     main_msg(_("-X\t\t\tDo not connect to X server"));
 #endif
+#if defined(FEAT_WAYLAND)
+    main_msg(_("-Y\t\t\tDo not connect to wayland compositor"));
+#endif
 #ifdef FEAT_CLIENTSERVER
     main_msg(_("--remote <files>\tEdit <files> in a Vim server if possible"));
     main_msg(_("--remote-silent <files>  Same, don't complain if there is no server"));
index 0f799b8f1bdb2d816d52363a0f14facd9e8aedee..db2376ecc6ae170a5b3d31af118c7149f9733e5e 100644 (file)
@@ -71,8 +71,6 @@ static int msg_wait = 0;
 static FILE *verbose_fd = NULL;
 static int  verbose_did_open = FALSE;
 
-static int  did_warn_clipboard = FALSE;
-
 /*
  * When writing messages to the screen, there are many different situations.
  * A number of variables is used to remember the current state:
index 150e5627f5dffed1f2f46d34df8711f02a79a20a..af07664df819101704879362b1140ae7cc5cbc7f 100644 (file)
@@ -4723,6 +4723,36 @@ did_set_winwidth(optset_T *args UNUSED)
     return errmsg;
 }
 
+#ifdef FEAT_WAYLAND_CLIPBOARD
+/*
+ * Process the new 'wlsteal' option value.
+ */
+    char *
+did_set_wlsteal(optset_T *args UNUSED)
+{
+    wayland_cb_reload();
+
+    return NULL;
+}
+#endif
+
+#ifdef FEAT_WAYLAND
+/*
+ * Process the new 'wltimeoutlen' option value.
+ */
+    char *
+did_set_wltimeoutlen(optset_T *args)
+{
+    if (p_wtm < 0)
+    {
+       p_wtm = args->os_oldval.number;
+       return e_argument_must_be_positive;
+    }
+
+    return NULL;
+}
+#endif
+
 /*
  * Process the updated 'wrap' option value.
  */
index 740f6eed5678fe9e14cb8fb47f30c69ef13a5e70..4ed117c705edde5fa57d4446695d0e485e4d3eda 100644 (file)
@@ -504,6 +504,7 @@ EXTERN char_u       *p_cedit;       // 'cedit'
 EXTERN long    p_cwh;          // 'cmdwinheight'
 #ifdef FEAT_CLIPBOARD
 EXTERN char_u  *p_cb;          // 'clipboard'
+EXTERN char_u  *p_cpm;         // 'clipmethod'
 #endif
 EXTERN long    p_ch;           // 'cmdheight'
 #ifdef FEAT_FOLDING
@@ -1132,6 +1133,13 @@ EXTERN long      p_wh;           // 'winheight'
 EXTERN long    p_wmh;          // 'winminheight'
 EXTERN long    p_wmw;          // 'winminwidth'
 EXTERN long    p_wiw;          // 'winwidth'
+#ifdef FEAT_WAYLAND
+EXTERN char_u  *p_wse;         // 'wlseat'
+# ifdef FEAT_WAYLAND_CLIPBOARD
+EXTERN int     p_wst;          // 'wlsteal'
+# endif
+EXTERN long     p_wtm;         // 'wltimeoutlen'
+#endif
 #if defined(MSWIN) && defined(FEAT_TERMINAL)
 EXTERN char_u  *p_winptydll;   // 'winptydll'
 #endif
index 5d9f388aa22840cbb0f1ddc35fc63a84f1d68ec4..3074181831da6596ff77021563046df28e4f302c 100644 (file)
@@ -608,7 +608,7 @@ static struct vimoption options[] =
     {"clipboard",   "cb",   P_STRING|P_VI_DEF|P_ONECOMMA|P_NODUP,
 #ifdef FEAT_CLIPBOARD
                            (char_u *)&p_cb, PV_NONE, did_set_clipboard, expand_set_clipboard,
-# ifdef FEAT_XCLIPBOARD
+# if defined(FEAT_XCLIPBOARD) || defined(FEAT_WAYLAND_CLIPBOARD)
                            {(char_u *)"autoselect,exclude:cons\\|linux",
                                                               (char_u *)0L}
 # else
@@ -617,6 +617,21 @@ static struct vimoption options[] =
 #else
                            (char_u *)NULL, PV_NONE, NULL, NULL,
                            {(char_u *)"", (char_u *)0L}
+#endif
+                           SCTX_INIT},
+    {"clipmethod", "cpm",   P_STRING|P_VI_DEF|P_ONECOMMA|P_NODUP,
+#ifdef FEAT_CLIPBOARD
+                           (char_u *)&p_cpm, PV_NONE, did_set_clipmethod, expand_set_clipmethod,
+# ifdef UNIX
+                           {(char_u *)"wayland,x11", (char_u *)0L}
+# elif defined(VMS)
+                           {(char_u *)"x11", (char_u *)0L}
+# else
+                           {(char_u *)"", (char_u *)0L}
+# endif
+#else
+                           (char_u *)NULL, PV_NONE, NULL, NULL,
+                           {(char_u *)NULL, (char_u *)0L}
 #endif
                            SCTX_INIT},
     {"cmdheight",   "ch",   P_NUM|P_VI_DEF|P_RALL,
@@ -2960,6 +2975,33 @@ static struct vimoption options[] =
     {"winwidth",   "wiw",   P_NUM|P_VI_DEF,
                            (char_u *)&p_wiw, PV_NONE, did_set_winwidth, NULL,
                            {(char_u *)20L, (char_u *)0L} SCTX_INIT},
+    {"wlseat",     "wse",  P_STRING|P_VI_DEF,
+#ifdef FEAT_WAYLAND
+                           (char_u *)&p_wse, PV_NONE, did_set_wlseat, NULL,
+                           {(char_u *)"", (char_u *)0L}
+#else
+                           (char_u *)NULL, PV_NONE, NULL, NULL,
+                           {(char_u *)NULL, (char_u *)0L}
+#endif
+                           SCTX_INIT},
+    {"wlsteal",            "wst",  P_BOOL|P_VI_DEF,
+#ifdef FEAT_WAYLAND_CLIPBOARD
+                           (char_u *)&p_wst, PV_NONE, did_set_wlsteal, NULL,
+                           {(char_u *)FALSE, (char_u *)0L}
+#else
+                           (char_u *)NULL, PV_NONE, NULL, NULL,
+                           {(char_u *)NULL, (char_u *)0L}
+#endif
+                           SCTX_INIT},
+    {"wltimeoutlen", "wtm", P_NUM|P_VI_DEF,
+#ifdef FEAT_WAYLAND
+                           (char_u *)&p_wtm, PV_NONE, did_set_wltimeoutlen, NULL,
+                           {(char_u *)500L, (char_u *)0L}
+#else
+                           (char_u *)NULL, PV_NONE, NULL, NULL,
+                           {(char_u *)NULL, (char_u *)0L}
+#endif
+                           SCTX_INIT},
     {"wrap",       NULL,   P_BOOL|P_VI_DEF|P_RWIN,
                            (char_u *)VAR_WIN, PV_WRAP, did_set_wrap, NULL,
                            {(char_u *)TRUE, (char_u *)0L} SCTX_INIT},
index e82654a8ef048349d60c12e71878792c6ae02184..c72c5d26934fbb3520bd9af4eff7d743e5f9caf7 100644 (file)
@@ -44,6 +44,8 @@ static char *(p_ff_values[]) = {FF_UNIX, FF_DOS, FF_MAC, NULL};
 #ifdef FEAT_CLIPBOARD
 // Note: Keep this in sync with did_set_clipboard()
 static char *(p_cb_values[]) = {"unnamed", "unnamedplus", "autoselect", "autoselectplus", "autoselectml", "html", "exclude:", NULL};
+// Note: Keep this in sync with get_clipmethod()
+static char *(p_cpm_values[]) = {"wayland", "x11", NULL};
 #endif
 #ifdef FEAT_CRYPT
 static char *(p_cm_values[]) = {"zip", "blowfish", "blowfish2",
@@ -1384,6 +1386,23 @@ expand_set_clipboard(optexpand_T *args, int *numMatches, char_u ***matches)
            numMatches,
            matches);
 }
+
+    char *
+did_set_clipmethod(optset_T *args UNUSED)
+{
+    return choose_clipmethod();
+}
+
+    int
+expand_set_clipmethod(optexpand_T *args, int *numMatches, char_u ***matches)
+{
+    return expand_set_opt_string(
+           args,
+           p_cpm_values,
+           ARRAY_LENGTH(p_cpm_values) - 1,
+           numMatches,
+           matches);
+}
 #endif
 
 /*
@@ -3624,6 +3643,21 @@ expand_set_scrollopt(optexpand_T *args, int *numMatches, char_u ***matches)
            matches);
 }
 
+/*
+ * The 'wlseat' option is changed
+ */
+    char *
+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.
+    wayland_cb_reload();
+#endif
+
+    return NULL;
+}
+
 /*
  * The 'selection' option is changed.
  */
index f5dea37f9908b58b1b9a6172f69f93aa45c62923..bf07b3881badc4b73d78a3f929555264d408a074 100644 (file)
 
 #include "os_unixx.h"      // unix includes for os_unix.c only
 
+#ifdef HAVE_SHM_OPEN
+# include <sys/mman.h>
+# include <sys/stat.h>
+# include <fcntl.h>
+#endif
+
 #ifdef USE_XSMP
 # include <X11/SM/SMlib.h>
 #endif
@@ -135,7 +141,6 @@ static void sig_sysmouse SIGPROTOARG;
 #  include <X11/StringDefs.h>
 static Widget  xterm_Shell = (Widget)0;
 static void clip_update(void);
-static void xterm_update(void);
 # endif
 
 Window     x11_window = 0;
@@ -1303,33 +1308,43 @@ sigcont_handler SIGDEFARG(sigarg)
 }
 #endif
 
-#if defined(FEAT_CLIPBOARD) && defined(FEAT_X11)
-# ifdef USE_SYSTEM
+#if defined(FEAT_CLIPBOARD)
+# if defined(USE_SYSTEM) && (defined(FEAT_X11) \
+       || defined(FEAT_WAYLAND_CLIPBOARD))
 static void *clip_star_save = NULL;
 static void *clip_plus_save = NULL;
 # endif
 
+# if defined(FEAT_CLIPBOARD) && (defined(FEAT_X11) \
+       || defined(FEAT_WAYLAND_CLIPBOARD))
 /*
  * Called when Vim is going to sleep or execute a shell command.
- * We can't respond to requests for the X selections.  Lose them, otherwise
- * other applications will hang.  But first copy the text to cut buffer 0.
+ * We can't respond to requests for the X or wayland selections.
+ * Lose them, otherwise other applications will hang.  But first
+ * copy the text to cut buffer 0 (for X11). Wayland users must have
+ * a clipboard manager to replicate such behaviour.
  */
     static void
 loose_clipboard(void)
 {
     if (clip_star.owned || clip_plus.owned)
     {
+#ifdef FEAT_X11
        x11_export_final_selection();
+#endif
        if (clip_star.owned)
            clip_lose_selection(&clip_star);
        if (clip_plus.owned)
            clip_lose_selection(&clip_plus);
+#ifdef FEAT_X11
        if (x11_display != NULL)
            XFlush(x11_display);
+#endif
     }
 }
+#endif
 
-# ifdef USE_SYSTEM
+# if defined(USE_SYSTEM) && (defined(FEAT_X11) || defined(FEAT_WAYLAND_CLIPBOARD))
 /*
  * Save clipboard text to restore later.
  */
@@ -1343,7 +1358,7 @@ save_clipboard(void)
 }
 
 /*
- * Restore clipboard text if no one own the X selection.
+ * Restore clipboard text if no one own the X/Wayland selection.
  */
     static void
 restore_clipboard(void)
@@ -1385,7 +1400,8 @@ mch_suspend(void)
     settmode(TMODE_COOK);
     out_flush();           // needed to disable mouse on some systems
 
-# if defined(FEAT_CLIPBOARD) && defined(FEAT_X11)
+# if defined(FEAT_CLIPBOARD) && (defined(FEAT_X11) \
+       || defined(FEAT_WAYLAND_CLIPBOARD))
     loose_clipboard();
 # endif
 # if defined(SIGCONT)
@@ -1810,7 +1826,7 @@ x_IOerror_handler(Display *dpy UNUSED)
  * (e.g. through tmux).
  */
     static void
-may_restore_clipboard(void)
+may_restore_x11_clipboard(void)
 {
     // No point in restoring the connecting if we are exiting or dying.
     if (!exiting && !v_dying && xterm_dpy_retry_count > 0)
@@ -1844,13 +1860,14 @@ ex_xrestore(exarg_T *eap)
        xterm_display = (char *)vim_strnsave(eap->arg, arglen);
        xterm_display_allocated = TRUE;
     }
-    smsg(_("restoring display %s"), xterm_display == NULL
+    smsg(_("restoring X11 display %s"), xterm_display == NULL
                    ? (char *)mch_getenv((char_u *)"DISPLAY") : xterm_display);
 
     clear_xterm_clip();
     x11_window = 0;
     xterm_dpy_retry_count = 5;  // Try reconnecting five times
-    may_restore_clipboard();
+    may_restore_x11_clipboard();
+    choose_clipmethod();
 }
 #endif
 
@@ -4836,8 +4853,11 @@ mch_call_shell_system(
     if (options & SHELL_COOKED)
        settmode(TMODE_COOK);       // set to normal mode
 
-# if defined(FEAT_CLIPBOARD) && defined(FEAT_X11)
+# if defined(FEAT_CLIPBOARD) && (defined(FEAT_X11) \
+       || defined(FEAT_WAYLAND_CLIPBOARD))
+# if defined(FEAT_X11) || defined(FEAT_WAYLAND_CLIPBOARD)
     save_clipboard();
+#endif
     loose_clipboard();
 # endif
 
@@ -4899,7 +4919,8 @@ mch_call_shell_system(
        settmode(TMODE_RAW);    // set to raw mode
     }
     resettitle();
-# if defined(FEAT_CLIPBOARD) && defined(FEAT_X11)
+# if defined(FEAT_CLIPBOARD) && (defined(FEAT_X11) \
+       || defined(FEAT_WAYLAND_CLIPBOARD))
     restore_clipboard();
 # endif
     return x;
@@ -5168,7 +5189,7 @@ mch_call_shell_fork(
             * different on different machines. This may cause a warning
             * message with strict compilers, don't worry about it.
             * Call _exit() instead of exit() to avoid closing the connection
-            * to the X server (esp. with GTK, which uses atexit()).
+            * to the X/Wayland server (esp. with GTK, which uses atexit()).
             */
            execvp(argv[0], argv);
            _exit(EXEC_FAILED);     // exec failed, return failure code
@@ -5586,6 +5607,11 @@ mch_call_shell_fork(
                    // Handle any X events, e.g. serving the clipboard.
                    clip_update();
 # endif
+#ifdef FEAT_WAYLAND
+                   // Handle wayland events such as sending data as the source
+                   // client.
+                   wayland_client_update();
+#endif
                }
 finished:
                p_more = p_more_save;
@@ -5612,7 +5638,7 @@ finished:
                    close(toshell_fd);
                close(fromshell_fd);
            }
-# if defined(FEAT_XCLIPBOARD) && defined(FEAT_X11)
+# if (defined(FEAT_XCLIPBOARD) && defined(FEAT_X11)) || defined(FEAT_WAYLAND)
            else
            {
                long delay_msec = 1;
@@ -5623,8 +5649,8 @@ finished:
                    out_str_t_TE();
 
                /*
-                * Similar to the loop above, but only handle X events, no
-                * I/O.
+                * Similar to the loop above, but only handle X and Wayland
+                * events, no I/O.
                 */
                for (;;)
                {
@@ -5651,8 +5677,15 @@ finished:
                        break;
                    }
 
+#if defined(FEAT_XCLIPBOARD) && defined(FEAT_X11)
                    // Handle any X events, e.g. serving the clipboard.
                    clip_update();
+#endif
+#ifdef FEAT_WAYLAND
+                   // Handle wayland events such as sending data as the source
+                   // client.
+                   wayland_client_update();
+#endif
 
                    // Wait for 1 to 10 msec. 1 is faster but gives the child
                    // less time, gradually wait longer.
@@ -6505,8 +6538,11 @@ RealWaitForChar(int fd, long msec, int *check_for_gpm UNUSED, int *interrupted)
 #endif
 #ifndef HAVE_SELECT
                        // each channel may use in, out and err
-       struct pollfd   fds[6 + 3 * MAX_OPEN_CHANNELS];
+       struct pollfd   fds[7 + 3 * MAX_OPEN_CHANNELS];
        int             nfd;
+# ifdef FEAT_WAYLAND_CLIPBOARD
+       int             wayland_idx = -1;
+# endif
 # ifdef FEAT_XCLIPBOARD
        int             xterm_idx = -1;
 # endif
@@ -6530,6 +6566,15 @@ RealWaitForChar(int fd, long msec, int *check_for_gpm UNUSED, int *interrupted)
        fds[0].events = POLLIN;
        nfd = 1;
 
+# ifdef FEAT_WAYLAND_CLIPBOARD
+       if (wayland_may_restore_connection())
+       {
+           wayland_idx = nfd;
+           fds[nfd].fd = vwl_display_fd;
+           fds[nfd].events = POLLIN;
+           nfd++;
+       }
+# endif
 # ifdef FEAT_XCLIPBOARD
        may_restore_clipboard();
        if (xterm_Shell != (Widget)0)
@@ -6558,9 +6603,9 @@ RealWaitForChar(int fd, long msec, int *check_for_gpm UNUSED, int *interrupted)
            nfd++;
        }
 # endif
-#ifdef FEAT_JOB_CHANNEL
+# ifdef FEAT_JOB_CHANNEL
        nfd = channel_poll_setup(nfd, &fds, &towait);
-#endif
+# endif
        if (interrupted != NULL)
            *interrupted = FALSE;
 
@@ -6576,6 +6621,15 @@ RealWaitForChar(int fd, long msec, int *check_for_gpm UNUSED, int *interrupted)
            finished = FALSE;
 # endif
 
+# 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
        if (xterm_Shell != (Widget)0 && (fds[xterm_idx].revents & POLLIN))
        {
@@ -6608,11 +6662,11 @@ RealWaitForChar(int fd, long msec, int *check_for_gpm UNUSED, int *interrupted)
                finished = FALSE;       // Try again
        }
 # endif
-#ifdef FEAT_JOB_CHANNEL
+# ifdef FEAT_JOB_CHANNEL
        // also call when ret == 0, we may be polling a keep-open channel
        if (ret >= 0)
            channel_poll_check(ret, &fds);
-#endif
+# endif
 
 #else // HAVE_SELECT
 
@@ -6656,8 +6710,19 @@ select_eintr:
 # endif
        maxfd = fd;
 
+# ifdef FEAT_WAYLAND_CLIPBOARD
+
+       if (wayland_may_restore_connection())
+       {
+           FD_SET(wayland_display_fd, &rfds);
+
+           if (maxfd < wayland_display_fd)
+               maxfd = wayland_display_fd;
+       }
+# endif
+
 # ifdef FEAT_XCLIPBOARD
-       may_restore_clipboard();
+       may_restore_x11_clipboard();
        if (xterm_Shell != (Widget)0)
        {
            FD_SET(ConnectionNumber(xterm_dpy), &rfds);
@@ -6745,6 +6810,15 @@ select_eintr:
            finished = FALSE;
 # endif
 
+# 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
        if (ret > 0 && xterm_Shell != (Widget)0
                && FD_ISSET(ConnectionNumber(xterm_dpy), &rfds))
@@ -6789,11 +6863,11 @@ select_eintr:
            }
        }
 # endif
-#ifdef FEAT_JOB_CHANNEL
+# ifdef FEAT_JOB_CHANNEL
        // also call when ret == 0, we may be polling a keep-open channel
        if (ret >= 0)
            (void)channel_select_check(ret, &rfds, &wfds);
-#endif
+# endif
 
 #endif // HAVE_SELECT
 
@@ -8295,7 +8369,7 @@ clip_update(void)
  * nothing in the X event queue (& no timers pending), then we return
  * immediately.
  */
-    static void
+    void
 xterm_update(void)
 {
     XEvent event;
@@ -8846,3 +8920,33 @@ start_timeout(long msec)
 }
 # endif // PROF_NSEC
 #endif  // FEAT_RELTIME
+
+/*
+ * Create an anonymous/temporary file/object and return its file descriptor.
+ * Returns -1 on error.
+ */
+    int
+mch_create_anon_file(void)
+{
+    int fd = -1;
+#ifdef HAVE_SHM_OPEN
+    const char template[] = "/vimXXXXXX";
+
+    for (int i = 0; i < 100; i++)
+    {
+       mch_get_random((char_u*)template + 4, 6);
+
+       errno = 0;
+       fd = shm_open(template, O_CREAT | O_RDWR | O_EXCL, 0600);
+
+       if (fd >= 0 || errno != EEXIST)
+           break; }
+    // Remove object name from namespace
+    shm_unlink(template);
+#endif
+    if (fd == -1)
+       // Last resort
+       fd = fileno(tmpfile());
+
+    return fd;
+}
index 8282dc34346e2d137c262d9cca6ab094f43c9899..28cb809d4091611f3a9fe07a108dc8a1439de104 100644 (file)
@@ -177,6 +177,9 @@ void mbyte_im_set_active(int active_arg);
 #  include "profiler.pro"
 # endif
 # include "quickfix.pro"
+#ifdef FEAT_WAYLAND
+# include "wayland.pro"
+#endif
 # include "regexp.pro"
 # include "register.pro"
 # include "scriptfile.pro"
index f3e8cab979128135d5db614d322c328ec98881d3..e21f1e27eaeeea7e4712e3e0e6c18198fcc04f17 100644 (file)
@@ -35,4 +35,10 @@ 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);
+void clip_wl_request_selection(Clipboard_T *cbd);
+int clip_wl_own_selection(Clipboard_T *cbd);
+void clip_wl_lose_selection(Clipboard_T *cbd);
+void clip_wl_set_selection(Clipboard_T *cbd);
+char *choose_clipmethod(void);
+void ex_clipreset(exarg_T *eap);
 /* vim: set ft=c : */
index f9ae7c402b2ff01800308baa542770fd98bf983e..b93a252f47fa4406ac077bc00eaf21756fba704d 100644 (file)
@@ -88,6 +88,8 @@ char *did_set_winheight_helpheight(optset_T *args);
 char *did_set_winminheight(optset_T *args);
 char *did_set_winminwidth(optset_T *args);
 char *did_set_winwidth(optset_T *args);
+char *did_set_wlsteal(optset_T *args);
+char *did_set_wltimeoutlen(optset_T *args);
 char *did_set_wrap(optset_T *args);
 char *did_set_xhistory(optset_T *args);
 void check_redraw(long_u flags);
index 84dd1fb8ff5e933cb98d5251340b63a92100c22e..2d5e17a5c5d22f019d6d194240a90ee5f56a95cf 100644 (file)
@@ -34,6 +34,8 @@ int expand_set_buftype(optexpand_T *args, int *numMatches, char_u ***matches);
 char *did_set_casemap(optset_T *args);
 int expand_set_casemap(optexpand_T *args, int *numMatches, char_u ***matches);
 int expand_set_clipboard(optexpand_T *args, int *numMatches, char_u ***matches);
+char * did_set_clipmethod(optset_T *args);
+int expand_set_clipmethod(optexpand_T *args, int *numMatches, char_u ***matches);
 char *did_set_chars_option(optset_T *args);
 int expand_set_chars_option(optexpand_T *args, int *numMatches, char_u ***matches);
 char *did_set_cinoptions(optset_T *args);
@@ -138,6 +140,7 @@ int expand_set_rightleftcmd(optexpand_T *args, int *numMatches, char_u ***matche
 char *did_set_rulerformat(optset_T *args);
 char *did_set_scrollopt(optset_T *args);
 int expand_set_scrollopt(optexpand_T *args, int *numMatches, char_u ***matches);
+char *did_set_wlseat(optset_T *args);
 char *did_set_selection(optset_T *args);
 int expand_set_selection(optexpand_T *args, int *numMatches, char_u ***matches);
 char *did_set_selectmode(optset_T *args);
index 5fee8a5a9a412ee65ee19a2555b591b50f6a3bc4..ae0cdbba415f29372273167dd87290bc1c6a8ee2 100644 (file)
@@ -82,6 +82,7 @@ void setup_term_clip(void);
 void start_xterm_trace(int button);
 void stop_xterm_trace(void);
 void clear_xterm_clip(void);
+void xterm_update(void);
 int clip_xterm_own_selection(Clipboard_T *cbd);
 void clip_xterm_lose_selection(Clipboard_T *cbd);
 void clip_xterm_request_selection(Clipboard_T *cbd);
@@ -92,4 +93,5 @@ void xsmp_close(void);
 void stop_timeout(void);
 volatile sig_atomic_t *start_timeout(long msec);
 void delete_timer(void);
+int mch_create_anon_file(void);
 /* vim: set ft=c : */
diff --git a/src/proto/wayland.pro b/src/proto/wayland.pro
new file mode 100644 (file)
index 0000000..990bd04
--- /dev/null
@@ -0,0 +1,17 @@
+/* wayland.c */
+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);
+/* vim: set ft=c : */
index 72d3135395e60196587108c50f9d5b83dba6627a..818166df5e1711207549a2fe8f7d1841f7cebb64 100644 (file)
@@ -20,7 +20,8 @@
  * 10..35 = registers 'a' to 'z' ('A' to 'Z' for appending)
  *     36 = delete register '-'
  *     37 = Selection register '*'. Only if FEAT_CLIPBOARD defined
- *     38 = Clipboard register '+'. Only if FEAT_CLIPBOARD and FEAT_X11 defined
+ *     38 = Clipboard register '+'. Only if FEAT_CLIPBOARD and FEAT_X11
+ *                                  or FEAT_WAYLAND_CLIPBOARD defined
  */
 static yankreg_T       y_regs[NUM_REGISTERS];
 
@@ -1170,7 +1171,7 @@ op_yank(oparg_T *oap, int deleting, int mess)
     linenr_T           yankendlnum = oap->end.lnum;
     char_u             *pnew;
     struct block_def   bd;
-#if defined(FEAT_CLIPBOARD) && defined(FEAT_X11)
+#if defined(FEAT_CLIPBOARD) && (defined(FEAT_X11) || defined(FEAT_WAYLAND_CLIPBOARD))
     int                        did_star = FALSE;
 #endif
 
@@ -1396,12 +1397,12 @@ op_yank(oparg_T *oap, int deleting, int mess)
 
        clip_own_selection(&clip_star);
        clip_gen_set_selection(&clip_star);
-# ifdef FEAT_X11
+# if defined(FEAT_X11) || defined(FEAT_WAYLAND_CLIPBOARD)
        did_star = TRUE;
 # endif
     }
 
-# ifdef FEAT_X11
+# if defined(FEAT_X11) || defined(FEAT_WAYLAND_CLIPBOARD)
     // If we were yanking to the '+' register, send result to selection.
     // Also copy to the '*' register, in case auto-select is off.  But not when
     // 'clipboard' has "unnamedplus" and not "unnamed"; and not when
index 250dc2fbdde8bc2fc511b9fb5d881818ee4ff602..423757adb580c80373af1a14695318039f6720f7 100644 (file)
@@ -4831,7 +4831,7 @@ typedef enum {
 #define DELETION_REGISTER      36
 #ifdef FEAT_CLIPBOARD
 # define STAR_REGISTER         37
-#  ifdef FEAT_X11
+#  if defined(FEAT_X11) || defined(FEAT_WAYLAND)
 #   define PLUS_REGISTER       38
 #  else
 #   define PLUS_REGISTER       STAR_REGISTER       // there is only one
@@ -5187,3 +5187,23 @@ struct cellsize {
     int cs_ypixel;
 };
 #endif
+
+#ifdef FEAT_WAYLAND
+
+// Wayland selections
+typedef enum {
+    WAYLAND_SELECTION_NONE         = 0x0,
+    WAYLAND_SELECTION_REGULAR      = 0x1,
+    WAYLAND_SELECTION_PRIMARY      = 0x2,
+} wayland_selection_T;
+
+// 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 1cbede6a495a5d602e707c73b92a86670b4e8260..497248754df95bc398f613c58fc52e728d845711 100644 (file)
@@ -102,6 +102,7 @@ NEW_TESTS = \
        test_cindent \
        test_cjk_linebreak \
        test_clientserver \
+       test_clipmethod \
        test_close_count \
        test_cmd_lists \
        test_cmdline \
@@ -344,6 +345,7 @@ NEW_TESTS = \
        test_vimscript \
        test_virtualedit \
        test_visual \
+       test_wayland \
        test_winbar \
        test_winbuf_close \
        test_window_cmd \
@@ -388,6 +390,7 @@ NEW_TESTS_RES = \
        test_cindent.res \
        test_cjk_linebreak.res \
        test_clientserver.res \
+       test_clipmethod.res \
        test_close_count.res \
        test_cmd_lists.res \
        test_cmdline.res \
@@ -596,6 +599,7 @@ NEW_TESTS_RES = \
        test_vimscript.res \
        test_virtualedit.res \
        test_visual.res \
+       test_wayland.res \
        test_winbar.res \
        test_winbuf_close.res \
        test_window_cmd.res \
index 5b9616f6f523da1bae958747ee23ad113b611dec..78a783e508b771cdb7402eaa7685e1df227a38bc 100644 (file)
@@ -115,6 +115,7 @@ let test_values = {
       \ 'winminheight': [[0, 1], [-1]],
       \ 'winminwidth': [[0, 1, 10], [-1]],
       \ 'winwidth': [[1, 10, 999], [-1, 0]],
+      \ 'wltimeoutlen': [[1, 10, 999],[-1]],
       \
       "\ string options
       \ 'ambiwidth': [['', 'single', 'double'], ['xxx']],
@@ -148,6 +149,7 @@ let test_values = {
       \                'autoselectplus', 'autoselectml', 'html', 'exclude:vimdisplay',
       \                'autoselect,unnamed', 'unnamed,exclude:.*'],
       \                ['xxx', 'exclude:\\ze*', 'exclude:\\%(']],
+      \ 'clipmethod': [['wayland', 'x11', 'wayland,x11', ''],['xxx', '--', 'wayland,,', ',x11']],
       \ 'colorcolumn': [['', '8', '+2', '1,+1,+3'], ['xxx', '-a', '1,', '1;']],
       \ 'comments': [['', 'b:#', 'b:#,:%'], ['xxx', '-']],
       \ 'commentstring': [['', '/*\ %s\ */'], ['xxx']],
diff --git a/src/testdir/test_clipmethod.vim b/src/testdir/test_clipmethod.vim
new file mode 100644 (file)
index 0000000..7573ce2
--- /dev/null
@@ -0,0 +1,169 @@
+" Tests for clipmethod
+
+source check.vim
+source shared.vim
+source window_manager.vim
+
+CheckFeature clipboard_working
+CheckFeature xterm_clipboard
+CheckFeature wayland_clipboard
+CheckUnix
+
+" Test if no available clipmethod sets v:clipmethod to none and deinits clipboard
+func Test_no_clipmethod_sets_v_clipmethod_none()
+  CheckNotGui
+
+  set clipmethod=
+  call assert_equal("none", v:clipmethod)
+  call assert_equal(0, has('clipboard_working'))
+endfunc
+
+" Test if method chosen is in line with clipmethod order
+func Test_clipmethod_order()
+  CheckNotGui
+
+  set cpm=wayland,x11
+
+  let l:wayland_display = StartWaylandCompositor()
+
+  let $WAYLAND_DISPLAY = l:wayland_display
+  exe 'wlrestore ' .. l:wayland_display
+
+  call assert_equal("wayland", v:clipmethod)
+
+  :wlrestore 1239
+  clipreset
+
+  call assert_equal("x11", v:clipmethod)
+
+  :xrestore 1239
+  clipreset
+
+  call assert_equal("none", v:clipmethod)
+  call assert_equal(0, has('clipboard_working'))
+
+  exe ":wlrestore " . $WAYLAND_DISPLAY
+  exe ":xrestore " . $DISPLAY
+  clipreset
+
+  call assert_equal("wayland", v:clipmethod)
+  call assert_equal(1, has('clipboard_working'))
+
+  set cpm=x11
+
+  call assert_equal("x11", v:clipmethod)
+
+  set cpm=wayland
+
+  call assert_equal("wayland", v:clipmethod)
+
+  call EndWaylandCompositor(l:wayland_display)
+endfunc
+
+" Test if clipmethod is set to 'none' when gui is started
+func Test_clipmethod_is_none_when_gui()
+  CheckCanRunGui
+
+  let lines =<< trim END
+    set cpm=wayland,x11
+    call writefile([v:clipmethod != ""], 'Cbdscript')
+    gui -f
+    call writefile([v:clipmethod], 'Cbdscript', 'a')
+    clipreset
+    call writefile([v:clipmethod], 'Cbdscript', 'a')
+    quit
+  END
+
+  call writefile(lines, 'Cbdscript', 'D')
+  call system($'{GetVimCommand()} -S Cbdscript')
+  call assert_equal(['1', 'none', 'none'], readfile('Cbdscript'))
+endfunc
+
+" Test if :clipreset switches methods when current one doesn't work
+func Test_clipreset_switches()
+  CheckNotGui
+  CheckFeature clientserver
+  CheckXServer
+  CheckWaylandCompositor
+
+  let l:wayland_display = StartWaylandCompositor()
+
+  set cpm=wayland,x11
+
+  exe 'wlrestore ' .. l:wayland_display
+
+  call assert_equal(l:wayland_display, v:wayland_display)
+  call assert_equal("wayland", v:clipmethod)
+
+  call EndWaylandCompositor(l:wayland_display)
+
+  " wlrestore updates clipmethod as well
+  wlrestore!
+
+  call assert_equal("", v:wayland_display)
+  call assert_equal("x11", v:clipmethod)
+
+  " Do the same but kill a X11 server
+
+  " X11 error handling relies on longjmp magic, but essentially if the X server
+  " is killed then it will simply abandon the current commands, making the test
+  " hang.
+
+  " This will only happen for commands given from the command line, which
+  " is why we cannot just directly call Vim or use the actual Vim instance thats
+  " doing all the testing, since main_loop() is never executed.
+
+  " Therefore we should start a separate Vim instance and communicate with it
+  " remotely, so we can execute the actual testing stuff with main_loop()
+  " running.
+
+  let l:lines =<< trim END
+    set cpm=x11
+    source shared.vim
+
+    func Test()
+      clipreset
+
+      if v:clipmethod ==# 'none'
+        return 1
+      endif
+      return 0
+    endfunc
+
+    func DoIt()
+      call WaitFor(function('Test'))
+
+      if v:clipmethod == 'none'
+        call writefile(['SUCCESS'], 'Xtest')
+      else
+        call writefile(['FAIL'], 'Xtest')
+      endif
+      quitall
+    endfunc
+  END
+  call writefile(l:lines, 'Xtester', 'D')
+
+  let l:xdisplay = StartXServer()
+
+  let l:name = 'XVIMTEST'
+  let l:cmd = GetVimCommand() .. ' -S Xtester --servername ' .. l:name
+  let l:job = job_start(l:cmd, { 'stoponexit': 'kill', 'out_io': 'null'})
+
+  call WaitForAssert({-> assert_equal("run", job_status(l:job))})
+  call WaitForAssert({-> assert_match(l:name, serverlist())})
+
+  " Change x server to the one that will be killed, then block until
+  " v:clipmethod is none.
+  call remote_send(l:name, ":xrestore " .. l:xdisplay ..
+        \ ' | call DoIt()' .. "\<CR>")
+
+  call EndXServer(l:xdisplay)
+
+  call WaitFor({-> filereadable('Xtest')})
+
+  " For some reason readfile sometimes returns an empty list despite the file
+  " existing, this why WaitForAssert() is used.
+  call WaitForAssert({-> assert_equal(['SUCCESS'], readfile('Xtest'))}, 1000)
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
index e85dd6a9cc42d6da15c4208e78bf1156260aa29e..cb5636ee1d8ffb9e5cadae1bd4323db2ad54e0d7 100644 (file)
@@ -523,6 +523,13 @@ func Test_set_completion_string_values()
   if exists('+clipboard')
     call assert_match('unnamed', getcompletion('set clipboard=', 'cmdline')[1])
   endif
+  if exists('+clipmethod')
+    if has('unix') || has('vms')
+      call assert_match('wayland', getcompletion('set clipmethod=', 'cmdline')[1])
+    else
+      call assert_match('wayland', getcompletion('set clipmethod=', 'cmdline')[0])
+    endif
+  endif
   call assert_equal('.', getcompletion('set complete=', 'cmdline')[1])
   call assert_equal('menu', getcompletion('set completeopt=', 'cmdline')[1])
   call assert_equal('keyword', getcompletion('set completefuzzycollect=', 'cmdline')[0])
@@ -2593,6 +2600,7 @@ func Test_string_option_revert_on_failure()
   endif
   if has('clipboard_working')
     call add(optlist, ['clipboard', 'unnamed', 'a123'])
+    call add(optlist, ['clipmethod', 'wayland', 'a123'])
   endif
   if has('win32')
     call add(optlist, ['completeslash', 'slash', 'a123'])
index b597ab326eb751eae01e828d77200d831f054b5f..3615e1ece95cfa4a92d43c1178519dd7ef0174b2 100644 (file)
@@ -1092,20 +1092,20 @@ func Test_clipboard_regs_not_working()
 endfunc
 
 " Check for W23 with a Vim with clipboard support,
-" but when the connection to the X11 server does not work
+" but when there is no available clipmethod
 func Test_clipboard_regs_not_working2()
   CheckNotMac
   CheckRunVimInTerminal
   CheckFeature clipboard
-  let display=$DISPLAY
-  unlet $DISPLAY
+  set clipmethod=
   " Run in a separate Vim instance because changing 'encoding' may cause
   " trouble for later tests.
   let lines =<< trim END
-      unlet $DISPLAY
+      set clipmethod=
       call setline(1, 'abcdefg')
       let a=execute(':norm! "+yy')
       call writefile([a], 'Xclipboard_result.txt')
+      set clipmethod&
   END
   call writefile(lines, 'XTest_clipboard', 'D')
   let buf = RunVimInTerminal('-S XTest_clipboard', {})
@@ -1113,7 +1113,7 @@ func Test_clipboard_regs_not_working2()
   call StopVimInTerminal(buf)
   let result = readfile('Xclipboard_result.txt')
   call assert_match("^\\nW23:", result[0])
-  let $DISPLAY=display
+  set clipmethod&
 endfunc
 
 " This caused use-after-free
index 6d43487017e04e6a058bc5ee6b8f0f9c0b321d03..c56173e263fbe62d60fd12abb4e9c0c61ea6fe69 100644 (file)
@@ -559,7 +559,7 @@ func Test_invalid_args()
   CheckUnix
   CheckNotGui
 
-  for opt in ['-Y', '--does-not-exist']
+  for opt in ['-K', '--does-not-exist']
     let out = split(system(GetVimCommand() .. ' ' .. opt), "\n")
     call assert_equal(1, v:shell_error)
     call assert_match('^VIM - Vi IMproved .* (.*)$',              out[0])
diff --git a/src/testdir/test_wayland.vim b/src/testdir/test_wayland.vim
new file mode 100644 (file)
index 0000000..ef4d8fb
--- /dev/null
@@ -0,0 +1,663 @@
+source check.vim
+source shared.vim
+source window_manager.vim
+
+CheckFeature wayland
+CheckFeature wayland_clipboard
+CheckUnix
+CheckFeature job
+CheckWaylandCompositor
+CheckNotGui
+
+if !executable('wl-paste') || !executable('wl-copy')
+  throw "Skipped: wl-clipboard is not available"
+endif
+
+" Process will be killed when the test ends
+let s:global_wayland_display = StartWaylandCompositor()
+let s:old_wayland_display = $WAYLAND_DISPLAY
+
+" For some reason if $WAYLAND_DISPLAY is set in the global namespace (not in a
+" function), it won't actually be set if $WAYLAND_DISPLAY was not set before
+" (such as in a CI environment) ? Solution is to just set it before the code of
+" every test function
+func s:PreTest()
+  let $WAYLAND_DISPLAY=s:global_wayland_display
+  exe 'wlrestore ' .. $WAYLAND_DISPLAY
+
+  set cpm=wayland
+endfunc
+
+func s:SetupFocusStealing()
+  if !executable('wayland-info')
+    throw "Skipped: wayland-info program not available"
+  endif
+
+  " Starting a headless compositor won't expose a keyboard capability for its
+  " 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
+
+  " Check if we have keyboard capability for seat
+  if system("wayland-info -i wl_seat | grep capabilities") !~? "keyboard"
+    throw "Skipped: seat does not have keyboard"
+  endif
+
+  let $VIM_WAYLAND_FORCE_FS=1
+  wlrestore!
+endfunc
+
+func s:UnsetupFocusStealing()
+  unlet $VIM_WAYLAND_FORCE_FS
+endfunc
+
+" Need X connection for tests that use client server communication
+func s:CheckXConnection()
+  if has('x11')
+    try
+      call remote_send('xxx', '')
+    catch
+      if v:exception =~ 'E240:'
+        throw 'Skipped: no connection to the X server'
+      endif
+      " ignore other errors
+    endtry
+  endif
+endfunc
+
+func s:EndRemoteVim(name, job)
+  eval remote_send(a:name, "\<Esc>:qa!\<CR>")
+  try
+    call WaitForAssert({-> assert_equal("dead", job_status(a:job))})
+  finally
+    if job_status(a:job) != 'dead'
+      call assert_report('Vim instance did not exit')
+      call job_stop(a:job, 'kill')
+    endif
+  endtry
+endfunc
+
+func Test_wayland_startup()
+  call s:PreTest()
+  call s:CheckXConnection()
+
+  let l:name = 'WLVIMTEST'
+  let l:cmd = GetVimCommand() .. ' --servername ' .. l:name
+  let l:job = job_start(cmd, {'stoponexit': 'kill', 'out_io': 'null'})
+
+  call WaitForAssert({-> assert_equal("run", job_status(l:job))})
+  call WaitForAssert({-> assert_match(name, serverlist())})
+
+  call WaitForAssert({-> assert_equal($WAYLAND_DISPLAY,
+        \ remote_expr(l:name, 'v:wayland_display'))})
+
+  call s:EndRemoteVim(l:name, l:job)
+
+  " When $WAYLAND_DISPLAY is invalid
+  let l:job = job_start(cmd, { 'stoponexit': 'kill', 'out_io': 'null',
+        \ 'env': {'WAYLAND_DISPLAY': 'UNKNOWN'}})
+
+  call WaitForAssert({-> assert_equal("run", job_status(l:job))})
+  call WaitForAssert({-> assert_match(name, serverlist())})
+
+  call assert_equal('', remote_expr(l:name, 'v:wayland_display'))
+  call s:EndRemoteVim(l:name, l:job)
+endfunc
+
+func Test_wayland_wlrestore()
+  call s:PreTest()
+
+  let l:wayland_display = StartWaylandCompositor()
+  let l:env_cmd = 'WAYLAND_DISPLAY=' .. l:wayland_display .. ' '
+
+  exe "wlrestore " .. l:wayland_display
+
+  call assert_equal(l:wayland_display, v:wayland_display)
+
+  " Check if calling wlrestore without arguments uses the existing wayland
+  " display.
+  wlrestore!
+  call assert_equal(l:wayland_display, v:wayland_display)
+
+  " If called with invalid display
+  wlrestore IDONTEXIST
+  call assert_equal("", v:wayland_display)
+
+  wlrestore!
+  call assert_equal("", v:wayland_display)
+
+  exe "wlrestore " .. l:wayland_display
+  call assert_equal(l:wayland_display, v:wayland_display)
+
+  " Actually check if connected display is different in case of regression with
+  " v:wayland_display
+  call system('wl-copy "1"')
+  call system(l:env_cmd .. 'wl-copy "2"')
+
+  call assert_equal('2', getreg('+'))
+
+  " Check if wlrestore doesn't disconnect the display if not nessecary by seeing
+  " if Vim doesn't lose the selection
+  call setreg('+', 'testing', 'c')
+
+  wlrestore
+  call assert_match('_VIM_TEXT', system(l:env_cmd .. 'wl-paste -l'))
+
+  " Forcibly disconnect and reconnect the display
+  wlrestore!
+  call assert_notmatch('_VIM_TEXT', system(l:env_cmd .. 'wl-paste -l'))
+
+  call EndWaylandCompositor(l:wayland_display)
+endfunc
+
+" Test behaviour when wayland display connection is lost
+func Test_wayland_connection_lost()
+  call s:PreTest()
+
+  let l:wayland_display = StartWaylandCompositor()
+  let l:env_cmd = 'WAYLAND_DISPLAY=' .. l:wayland_display .. ' '
+
+  exe "wlrestore " .. l:wayland_display
+
+  call system(l:env_cmd .. 'wl-copy test')
+
+  call assert_equal(l:wayland_display, v:wayland_display)
+  call assert_equal('test', getreg('+'))
+
+  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
+
+  call system('wl-copy "TESTING"')
+  put +
+
+  call assert_equal("TESTING", getline(2))
+
+  call system('printf "LINE1\nLINE2\nLINE3" | wl-copy -n')
+  put +
+
+  call assert_equal(["LINE1", "LINE2", "LINE3"], getline(3, 5))
+  bw!
+
+  " Primary selection
+  new
+
+  call system('wl-copy -p "TESTING"')
+  put *
+
+  call assert_equal("TESTING", getline(2))
+
+  call system('printf "LINE1\nLINE2\nLINE3" | wl-copy -p')
+  put *
+
+  call assert_equal(["LINE1", "LINE2", "LINE3"], getline(3, 5))
+
+  bw!
+
+  " Check behaviour when selecton is cleared (empty)
+  call system('wl-copy --clear')
+  call assert_fails('put +', 'E353:')
+endfunc
+
+" Basic yank/copy tests
+func Test_wayland_yank()
+  call s:PreTest()
+
+  wlrestore!
+
+  new
+
+  call setline(1, 'testing')
+  yank +
+
+  call assert_equal("testing\n", system('wl-paste -n'))
+
+  call setline(2, 'testing2')
+  call setline(3, 'testing3')
+  exe '1,3yank +'
+
+  call assert_equal("testing\ntesting2\ntesting3\n", system('wl-paste -n'))
+
+  bw!
+
+  " Primary selection
+  new
+
+  call setline(1, 'testing')
+  yank *
+
+  call assert_equal("testing\n", system('wl-paste -p -n'))
+
+  call setline(2, 'testing2')
+  call setline(3, 'testing3')
+  exe '1,3yank *'
+
+  call assert_equal("testing\ntesting2\ntesting3\n", system('wl-paste -p -n'))
+
+  bw!
+endfunc
+
+
+" Check if correct mime types are advertised when we own the selection
+func Test_wayland_mime_types_correct()
+  call s:PreTest()
+
+  let l:mimes = [
+        \ '_VIMENC_TEXT',
+        \ '_VIM_TEXT',
+        \ 'text/plain;charset=utf-8',
+        \ 'text/plain',
+        \ 'UTF8_STRING',
+        \ 'STRING',
+        \ 'TEXT'
+        \ ]
+
+  call setreg('+', 'text', 'c')
+
+  for mime in split(system('wl-paste -l'), "\n")
+    if index(l:mimes, mime) == -1
+      call assert_report("'" .. mime .. "' is not a supported mime type")
+    endif
+  endfor
+
+  call setreg('*', 'text', 'c')
+
+  for mime in split(system('wl-paste -p -l'), "\n")
+    if index(l:mimes, mime) == -1
+      call assert_report("'" .. mime .. "' is not a supported mime type")
+    endif
+  endfor
+endfunc
+
+" Test if the _VIM_TEXT and _VIMENC_TEXT formats are correct:
+" _VIM_TEXT: preserves motion type (line/char/block wise)
+" _VIMENC_TEXT: same but also indicates the encoding type
+func Test_wayland_paste_vim_format_correct()
+  call s:PreTest()
+
+  " Vim doesn't support null characters in strings, so we use the -v flag of the
+  " cat program to show them in a printable way, if it is available.
+  call system("cat -v")
+  if v:shell_error != 0
+    throw 'Skipped: cat program does not have -v command-line flag'
+  endif
+
+  set encoding=utf-8
+
+  let l:GetSel = {type -> system('wl-paste -t ' .. type .. ' | cat -v')}
+  let l:GetSelP = {type -> system('wl-paste -p -t ' .. type .. ' | cat -v')}
+
+  " Regular selection
+  call setreg('+', 'text', 'c')
+  call assert_equal("^@text", l:GetSel('_VIM_TEXT'))
+  call setreg('+', 'text', 'c')
+  call assert_equal("^@utf-8^@text", l:GetSel('_VIMENC_TEXT'))
+
+  call setreg('+', 'text', 'l')
+  call assert_equal("^Atext\n", l:GetSel('_VIM_TEXT'))
+  call setreg('+', 'text', 'l')
+  call assert_equal("^Autf-8^@text\n",l:GetSel('_VIMENC_TEXT'))
+
+  call setreg('+', 'text', 'b')
+  call assert_equal("^Btext\n", l:GetSel('_VIM_TEXT'))
+  call setreg('+', 'text', 'b')
+  call assert_equal("^Butf-8^@text\n", l:GetSel('_VIMENC_TEXT'))
+
+  " Primary selection
+  call setreg('*', 'text', 'c')
+  call assert_equal("^@text", l:GetSelP('_VIM_TEXT'))
+  call setreg('*', 'text', 'c')
+  call assert_equal("^@utf-8^@text", l:GetSelP('_VIMENC_TEXT'))
+
+  call setreg('*', 'text', 'l')
+  call assert_equal("^Atext\n", l:GetSelP('_VIM_TEXT'))
+  call setreg('*', 'text', 'l')
+  call assert_equal("^Autf-8^@text\n",l:GetSelP('_VIMENC_TEXT'))
+
+  call setreg('*', 'text', 'b')
+  call assert_equal("^Btext\n", l:GetSelP('_VIM_TEXT'))
+  call setreg('*', 'text', 'b')
+  call assert_equal("^Butf-8^@text\n", l:GetSelP('_VIMENC_TEXT'))
+
+  set encoding&
+endfunc
+
+" Test checking if * and + registers are not the same
+func Test_wayland_plus_star_not_same()
+  call s:PreTest()
+  new
+
+  call system('wl-copy "regular"')
+  call system('wl-copy -p "primary"')
+
+  call assert_notequal(getreg('+'), getreg('*'))
+
+  " Check if when we are the source client
+  call setreg('+', 'REGULAR')
+  call setreg('*', 'PRIMARY')
+
+  call assert_notequal(system('wl-paste -p'), system('wl-paste'))
+
+  bw!
+endfunc
+
+" Test if autoselect option in 'clipboard' works properly for wayland
+func Test_wayland_autoselect_works()
+  call s:PreTest()
+  call s:CheckXConnection()
+
+  let l:lines =<< trim END
+  set cpm=wayland
+  set clipboard=autoselect
+
+  new
+  call setline(1, 'LINE 1')
+  call setline(2, 'LINE 2')
+  call setline(3, 'LINE 3')
+
+  call cursor(1, 1)
+  END
+
+  call writefile(l:lines, 'Wltester', 'D')
+
+  let l:name = 'WLVIMTEST'
+  let l:cmd = GetVimCommand() .. ' -S Wltester --servername ' .. l:name
+  let l:job = job_start(cmd, {'stoponexit': 'kill', 'out_io': 'null'})
+
+  call WaitForAssert({-> assert_equal("run", job_status(l:job))})
+  call WaitForAssert({-> assert_match(name, serverlist())})
+  1
+  call remote_send(l:name, "ve")
+  call WaitForAssert({-> assert_equal('LINE', system('wl-paste -p -n'))})
+
+  call remote_send(l:name, "w")
+  call WaitForAssert({-> assert_equal('LINE 1', system('wl-paste -p -n'))})
+
+  call remote_send(l:name, "V")
+  call WaitForAssert({-> assert_equal("LINE 1\n", system('wl-paste -p -n'))})
+
+  " Reset cursor
+  call remote_send(l:name, "\<Esc>:call cursor(1, 1)\<CR>")
+  call WaitForAssert({-> assert_equal("LINE 1\n", system('wl-paste -p -n'))})
+
+  " Test visual block mode
+  call remote_send(l:name, "\<C-q>jjj") " \<C-v> doesn't seem to work but \<C-q>
+                                        " does...
+
+  call WaitForAssert({-> assert_equal("L\nL\nL\n", system('wl-paste -p -n'))})
+
+  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
+
+" Check if the -Y flag works properly
+func Test_no_wayland_connect_cmd_flag()
+  call s:PreTest()
+  call s:CheckXConnection()
+
+  let l:name = 'WLFLAGVIMTEST'
+  let l:cmd = GetVimCommand() .. ' -Y --servername ' .. l:name
+  let l:job = job_start(cmd, {'stoponexit': 'kill', 'out_io': 'null'})
+
+  call WaitForAssert({-> assert_equal("run", job_status(l:job))})
+  call WaitForAssert({-> assert_match(name, serverlist())})
+
+  call WaitForAssert({->assert_equal('',
+        \ remote_expr(l:name, 'v:wayland_display'))})
+
+  call remote_send(l:name, ":wlrestore\<CR>")
+  call WaitForAssert({-> assert_equal('',
+        \ remote_expr(l:name, 'v:wayland_display'))})
+
+  call remote_send(l:name, ":wlrestore " .. $WAYLAND_DISPLAY .. "\<CR>")
+  call WaitForAssert({-> assert_equal('',
+        \ remote_expr(l:name, 'v:wayland_display'))})
+
+  call remote_send(l:name, ":wlrestore IDONTEXIST\<CR>")
+  call WaitForAssert({-> assert_equal('',
+        \ remote_expr(l:name, 'v:wayland_display'))})
+
+  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:CheckXConnection()
+
+  let l:name = 'WLLOSEVIMTEST'
+  let l:cmd = GetVimCommand() .. ' --servername ' .. l:name
+  let l:job = job_start(cmd, {
+        \ 'stoponexit': 'kill',
+        \ 'out_io': 'null',
+        \ })
+
+  call WaitForAssert({-> assert_equal("run", job_status(l:job))})
+  call WaitForAssert({-> assert_match(name, serverlist())})
+
+  call remote_send(l:name, "iSOME TEXT\<Esc>\"+yy")
+
+  call WaitForAssert({-> assert_equal("SOME TEXT\n",
+        \ system('wl-paste -n'))})
+
+  call remote_send(l:name, "\<C-z>")
+
+  call WaitForAssert({-> assert_equal("Nothing is copied\n",
+        \ system('wl-paste -n'))})
+
+  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
+func Test_wayland_seat()
+  call s:PreTest()
+
+  " Don't know a way to create a virtual seat so just test using the existing
+  " one only
+  set wlseat=
+
+  call system('wl-copy "TESTING"')
+  call assert_equal('TESTING', getreg('+'))
+
+  set wlseat=UNKNOWN
+
+  call assert_equal('', getreg('+'))
+
+  set wlseat=idontexist
+
+  call assert_equal('', getreg('+'))
+
+  set wlseat=
+
+  call assert_equal('TESTING', getreg('+'))
+
+  set wlseat&
+endfunc
+
+" Test focus stealing
+func Test_wayland_focus_steal()
+  call s:PreTest()
+  call s:SetupFocusStealing()
+
+  call system('wl-copy regular')
+
+  call assert_equal('regular', getreg('+'))
+
+  call system('wl-copy -p primary')
+
+  call assert_equal('primary', getreg('*'))
+
+  call setreg('+', 'REGULAR')
+
+  call assert_equal('REGULAR', system('wl-paste -n'))
+
+  call setreg('*', 'PRIMARY')
+
+  call assert_equal('PRIMARY', system('wl-paste -p -n'))
+
+  call s:UnsetupFocusStealing()
+endfunc
+
+" Test when environment is not suitable for Wayland
+func Test_wayland_bad_environment()
+  call s:PreTest()
+  call s:CheckXConnection()
+
+  unlet $WAYLAND_DISPLAY
+
+  let l:old = $XDG_RUNTIME_DIR
+  unlet $XDG_RUNTIME_DIR
+
+  let l:name = 'WLVIMTEST'
+  let l:cmd = GetVimCommand() .. ' --servername ' .. l:name
+  let l:job = job_start(cmd, {
+        \ 'stoponexit': 'kill',
+        \ 'out_io': 'null',
+        \ })
+
+  call WaitForAssert({-> assert_equal("run", job_status(l:job))})
+  call WaitForAssert({-> assert_match(name, serverlist())})
+
+  call WaitForAssert({-> assert_equal('',
+        \ remote_expr(l:name, 'v:wayland_display'))})
+
+  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
+
+" Test if Vim still works properly after losing the selection
+func Test_wayland_lost_selection()
+  call s:PreTest()
+
+  call setreg('+', 'regular')
+  call setreg('*', 'primary')
+
+  call assert_equal('regular', getreg('+'))
+  call assert_equal('primary', getreg('*'))
+
+  call system('wl-copy overwrite')
+  call system('wl-copy -p overwrite')
+
+  call assert_equal('overwrite', getreg('+'))
+  call assert_equal('overwrite', getreg('*'))
+
+  call setreg('+', 'regular')
+  call setreg('*', 'primary')
+
+  call assert_equal('regular', getreg('+'))
+  call assert_equal('primary', getreg('*'))
+
+  " Test focus stealing
+  call s:SetupFocusStealing()
+
+  call setreg('+', 'regular')
+  call setreg('*', 'primary')
+
+  call assert_equal('regular', getreg('+'))
+  call assert_equal('primary', getreg('*'))
+
+  call system('wl-copy overwrite')
+  call system('wl-copy -p overwrite')
+
+  call assert_equal('overwrite', getreg('+'))
+  call assert_equal('overwrite', getreg('*'))
+
+  call setreg('+', 'regular')
+  call setreg('*', 'primary')
+
+  call assert_equal('regular', getreg('+'))
+  call assert_equal('primary', getreg('*'))
+
+  call s:UnsetupFocusStealing()
+endfunc
+
+" Test when there are no supported mime types for the selecftion
+func Test_wayland_no_mime_types_supported()
+  call s:PreTest()
+
+  wlrestore!
+
+  call system('wl-copy -t image/png testing')
+
+  call assert_equal('', getreg('+'))
+  call assert_fails('put +', 'E353:')
+endfunc
+
+" Test behaviour with large selections in terms of data size
+func Test_wayland_handle_large_data()
+  call s:PreTest()
+
+  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
+
+  put! +
+
+  write
+
+  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)
+
+  " copy the 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
diff --git a/src/testdir/window_manager.vim b/src/testdir/window_manager.vim
new file mode 100644 (file)
index 0000000..39e643e
--- /dev/null
@@ -0,0 +1,110 @@
+source check.vim
+source shared.vim
+
+CheckFeature job
+CheckUnix
+
+let g:xdisplay_num = 100
+
+" Each key is the display name and its value is the compositor/wm job
+let s:wayland_displays = {}
+let s:x11_displays = {}
+
+command -nargs=0 CheckWaylandCompositor call CheckWaylandCompositor()
+command -nargs=0 CheckXServer call CheckXServer()
+
+func CheckWaylandCompositor()
+  CheckFeature wayland
+
+  if executable("labwc") != 1
+    throw "Skipped: labwc is not available"
+  endif
+endfunc
+
+func CheckXServer()
+  CheckFeature x11
+
+  if executable("Xvfb") != 1
+    throw "Skipped: Xvfb is not available"
+  endif
+  if executable("xdpyinfo") != 1
+    throw "Skipped: xdpyinfo is not available"
+  endif
+endfunc
+
+func s:StartCompositorOutput(channel, msg)
+  let l:display = matchstr(a:msg, 'WAYLAND_DISPLAY=\zs.\+')
+
+  if !empty(l:display)
+    let s:wayland_display_name = l:display
+  endif
+endfunc
+
+func s:StartCompositorExit(job, status)
+    if s:wayland_display_name == ""
+      throw "Error: Wayland compositor exited when starting up"
+    endif
+endfunc
+
+func StartWaylandCompositor()
+  let s:wayland_display_name = ""
+
+  let l:wayland_compositor_job = job_start(
+        \ ['labwc', '-c', 'NONE', '-d'], {
+        \ 'err_io': 'pipe',
+        \ 'err_cb': function('s:StartCompositorOutput'),
+        \ 'err_mode': 'nl',
+        \ 'exit_cb': function('s:StartCompositorExit'),
+        \ 'env': { 'WLR_BACKENDS': 'headless' }
+        \ })
+
+  call WaitForAssert({-> assert_equal("run",
+        \ job_status(l:wayland_compositor_job))})
+  call WaitForAssert({-> assert_match('.\+', s:wayland_display_name)})
+
+  let s:wayland_displays[s:wayland_display_name] = l:wayland_compositor_job
+
+  return s:wayland_display_name
+endfunc
+
+func EndWaylandCompositor(display)
+  let l:job = s:wayland_displays[a:display]
+
+  call job_stop(l:job, 'term')
+
+  " Block until compositor is actually gone
+  call WaitForAssert({-> assert_equal("dead", job_status(l:job))})
+
+  unlet s:wayland_displays[a:display]
+endfunc
+
+" Start a separate X11 server instance
+func StartXServer()
+  let l:xdisplay = ':' .. g:xdisplay_num
+
+  let l:x11_server_job = job_start(['Xvfb', l:xdisplay], {})
+
+  call WaitForAssert({-> assert_equal("run", job_status(l:x11_server_job))})
+  " Check if server is ready. Not sure if this is the best way though...
+  call WaitFor({-> system("DISPLAY=" .. l:xdisplay .. " xdpyinfo 2> /dev/null")
+        \ =~? '.\+'})
+
+  g:xdisplay_num += 1
+
+  let s:x11_displays[l:xdisplay] = l:x11_server_job
+
+  return l:xdisplay
+endfunc
+
+func EndXServer(display)
+  let l:job = s:x11_displays[a:display]
+
+  call job_stop(l:job)
+
+  " Block until X server is actually gone
+  call WaitForAssert({-> assert_equal("dead", job_status(l:job))})
+
+  unlet s:x11_displays[a:display]
+endfunc
+
+" vim: shiftwidth=2 sts=2 expandtab
index 8d6eb47e00a0f263edcef7448d9ec40b04476e67..b38ca083ac96120d50389f601d3fd9fe4f5efdc7 100644 (file)
@@ -643,6 +643,16 @@ static char *(features[]) =
 # else
        "-vtp",
 # endif
+#endif
+#ifdef FEAT_WAYLAND
+       "+wayland",
+#else
+       "-wayland",
+#endif
+#ifdef FEAT_WAYLAND_CLIPBOARD
+       "+wayland_clipboard",
+#else
+       "-wayland_clipboard",
 #endif
        "+wildignore",
        "+wildmenu",
@@ -709,6 +719,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1485,
 /**/
     1484,
 /**/
index 88caf0ba8aa637602ccbd035231ba1c8f2ba0b5c..7f79e6855bd29ddf995782b16e567bdff55da230 100644 (file)
--- a/src/vim.h
+++ b/src/vim.h
 # define FEAT_X11
 #endif
 
+#if defined(HAVE_WAYLAND) && defined(WANT_WAYLAND)
+#define FEAT_WAYLAND
+#endif
+
 #ifdef NO_X11_INCLUDES
     // In os_mac_conv.c and os_macosx.m NO_X11_INCLUDES is defined to avoid
     // X11 headers.  Disable all X11 related things to avoid conflicts.
@@ -2224,7 +2228,9 @@ typedef int sock_T;
 #define VV_TYPE_ENUMVALUE 109
 #define VV_STACKTRACE  110
 #define VV_TYPE_TUPLE  111
-#define VV_LEN         112     // number of v: vars
+#define VV_WAYLAND_DISPLAY 112
+#define VV_CLIPMETHOD 113
+#define VV_LEN         114     // number of v: vars
 
 // used for v_number in VAR_BOOL and VAR_SPECIAL
 #define VVAL_FALSE     0L      // VAR_BOOL
@@ -2279,6 +2285,13 @@ typedef int sock_T;
 #  endif
 # endif
 
+typedef enum {
+    CLIPMETHOD_FAIL,
+    CLIPMETHOD_NONE,
+    CLIPMETHOD_WAYLAND,
+    CLIPMETHOD_X11,
+} clipmethod_T;
+
 // Info about selected text
 typedef struct
 {
diff --git a/src/wayland.c b/src/wayland.c
new file mode 100644 (file)
index 0000000..aca3e07
--- /dev/null
@@ -0,0 +1,2483 @@
+/* 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.c: Stuff related to wayland
+ */
+
+#include "vim.h"
+
+#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"
+# 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;
+
+#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.
+ */
+    static int
+vwl_display_flush(vwl_display_T *display)
+{
+    int ret;
+
+#ifndef HAVE_SELECT
+    struct pollfd fds;
+
+    fds.fd     = display->fd;
+    fds.events = POLLOUT;
+#else
+    fd_set         wfds;
+    struct timeval  tv;
+
+    FD_ZERO(&wfds);
+    FD_SET(display->fd, &wfds);
+
+    tv.tv_sec  = 0;
+    tv.tv_usec = p_wtm * 1000;
+#endif
+
+    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 (errno = 0, (ret = wl_display_flush(display->proxy)) == -1
+           && errno == EAGAIN)
+    {
+#ifndef HAVE_SELECT
+       if (poll(&fds, 1, p_wtm) <= 0)
+#else
+           if (select(display->fd + 1, NULL, &wfds, NULL, &tv) <= 0)
+#endif
+               return FAIL;
+    }
+    // Return FAIL on error or timeout
+    if ((errno != 0 && errno != EAGAIN) || ret == -1)
+       return FAIL;
+
+    return OK;
+}
+
+/*
+ * Called when compositor is done processing requests/events.
+ */
+    static void
+vwl_callback_done(void *data, struct wl_callback *callback,
+       uint32_t cb_data UNUSED)
+{
+    *((int*)data) = TRUE;
+    wl_callback_destroy(callback);
+}
+
+/*
+ * Like wl_display_roundtrip but polls the display fd with a timeout. Returns
+ * FAIL on failure and OK on success.
+ */
+    static int
+vwl_display_roundtrip(vwl_display_T *display)
+{
+    struct wl_callback *callback;
+    int                        ret, done = FALSE;
+    struct timeval start, now;
+
+    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(display->proxy);
+
+    if (callback == NULL)
+       return FAIL;
+
+    wl_callback_add_listener(callback, &vwl_callback_listener, &done);
+
+    gettimeofday(&start, NULL);
+
+    // Wait till we get the done event (which will set `done` to TRUE), unless
+    // we timeout
+    while (TRUE)
+    {
+       ret = vwl_display_dispatch(display);
+
+       if (done || ret == -1)
+           break;
+
+       gettimeofday(&now, NULL);
+
+       if ((now.tv_sec * 1000000 + now.tv_usec) -
+               (start.tv_sec * 1000000 + start.tv_usec) >= p_wtm * 1000)
+       {
+           ret = -1;
+           break;
+       }
+    }
+
+    if (ret == -1)
+    {
+       if (!done)
+           wl_callback_destroy(callback);
+       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.
+ */
+    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      = 0;
+    tv.tv_usec     = p_wtm * 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.
+ */
+    static void
+vwl_log_handler(const char *fmt, va_list args)
+{
+    // 512 bytes should be big enough
+    char       *buf    = alloc(512);
+    char       *prefix = _("wayland protocol error -> ");
+    size_t     len     = STRLEN(prefix);
+
+    if (buf == NULL)
+       return;
+
+    vim_strncpy((char_u*)buf, (char_u*)prefix, len);
+    vim_vsnprintf(buf + len, 4096 - len, fmt, args);
+
+    // Remove newline that libwayland puts
+    buf[STRLEN(buf) - 1] = NUL;
+
+    ch_log(NULL, "%s", buf);
+    emsg(buf);
+
+    vim_free(buf);
+}
+
+/*
+ * 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 int
+vwl_connect_display(const char *display)
+{
+    if (wayland_no_connect)
+       return FAIL;
+
+    // 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; \
+    }
+
+/*
+ * Disconnects the display and frees up all resources, including all global
+ * objects.
+ */
+    static void
+vwl_disconnect_display(void)
+{
+
+    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;
+    }
+}
+
+/*
+ * 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;
+}
+
+#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
+vwl_registry_listener_global(
+       void                *data UNUSED,
+       struct wl_registry  *registry UNUSED,
+       uint32_t            name,
+       const char          *interface,
+       uint32_t            version)
+{
+
+    const struct wl_interface  *chosen_interface = NULL;
+    void                       *proxy;
+    uint32_t                   min_version;
+    void                       **object_member;
+
+    if (STRCMP(interface, wl_seat_interface.name) == 0)
+    {
+       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);
+
+    else if (STRCMP(interface, ext_data_control_manager_v1_interface.name) == 0)
+       SET_GOBJECT(ext_data_control_manager_v1, 1);
+
+    else if (STRCMP(interface, wl_data_device_manager_interface.name) == 0)
+       SET_GOBJECT(wl_data_device_manager, 1);
+
+    else if (STRCMP(interface, wl_shm_interface.name) == 0)
+       SET_GOBJECT(wl_shm, 1);
+
+    else if (STRCMP(interface, wl_compositor_interface.name) == 0)
+       SET_GOBJECT(wl_compositor, 2);
+
+    else if (STRCMP(interface, xdg_wm_base_interface.name) == 0)
+       SET_GOBJECT(xdg_wm_base, 1);
+
+    else if (STRCMP(interface,
+               zwp_primary_selection_device_manager_v1_interface.name) == 0)
+       SET_GOBJECT(zwp_primary_selection_device_manager_v1, 1);
+#endif
+
+    if (chosen_interface == NULL || version < min_version)
+       return;
+
+    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;
+}
+
+/*
+ * Called when a global object is removed, if so, then do nothing. This is to
+ * avoid a global being removed while it is in the process of being used. Let
+ * the user call :wlrestore in order to reset everything. Requests to that
+ * global will just be ignored on the compositor side.
+ */
+    static void
+vwl_registry_listener_global_remove(
+       void                *data UNUSED,
+       struct wl_registry  *registry UNUSED,
+       uint32_t            name UNUSED)
+{
+}
+
+/*
+ * Add a new seat given its proxy to the global grow array
+ */
+    static void
+vwl_add_seat(struct wl_seat *seat_proxy)
+{
+    vwl_seat_T *seat;
+
+    if (ga_grow(&vwl_seats, 1) == FAIL)
+       return;
+
+    seat = &((vwl_seat_T *)vwl_seats.ga_data)[vwl_seats.ga_len];
+
+    seat->proxy = seat_proxy;
+
+    // Get label and capabilities
+    wl_seat_add_listener(seat_proxy, &vwl_seat_listener, seat);
+
+    if (vwl_display_roundtrip(&vwl_display) == FAIL)
+       return;
+
+    // Check if label has been allocated
+    if (seat->label == NULL)
+       return;
+
+    vwl_seats.ga_len++;
+}
+
+/*
+ * Callback for seat text label/name
+ */
+    static void
+vwl_seat_listener_name(
+       void            *data,
+       struct wl_seat  *seat_proxy UNUSED,
+       const char      *name)
+{
+    vwl_seat_T *seat = data;
+
+    seat->label = (char *)vim_strsave((char_u *)name);
+}
+
+/*
+ * Callback for seat capabilities
+ */
+    static void
+vwl_seat_listener_capabilities(
+       void            *data,
+       struct wl_seat  *seat_proxy UNUSED,
+       uint32_t        capabilities)
+{
+    vwl_seat_T *seat = data;
+
+    seat->capabilities = capabilities;
+}
+
+/*
+ * Destroy/free seat.
+ */
+    static void
+vwl_destroy_seat(vwl_seat_T *seat)
+{
+    if (seat->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(seat->label);
+    seat->label = NULL;
+}
+
+/*
+ * Return a seat with the give name/label. If none exists then NULL is returned.
+ * If NULL or an empty string is passed as the label then $XDG_SEAT is used
+ * else the first available seat found is used.
+ */
+    static vwl_seat_T *
+vwl_get_seat(const char *label)
+{
+    if ((STRCMP(label, "") == 0 || label == NULL) && vwl_seats.ga_len > 0)
+    {
+       const char *xdg_seat = (char*)mch_getenv("XDG_SEAT");
+
+       if (xdg_seat == NULL)
+           return &((vwl_seat_T *)vwl_seats.ga_data)[0];
+       else
+           label = xdg_seat;
+    }
+
+    for (int i = 0; i < vwl_seats.ga_len; i++)
+    {
+       vwl_seat_T *seat = &((vwl_seat_T *)vwl_seats.ga_data)[i];
+       if (STRCMP(seat->label, label) == 0)
+           return seat;
+    }
+    return NULL;
+}
+
+/*
+ * Get keyboard object from seat and return it. NULL is returned on
+ * failure such as when a keyboard is not available for seat.
+ */
+    static struct wl_keyboard *
+vwl_seat_get_keyboard(vwl_seat_T *seat)
+{
+    if (!(seat->capabilities & WL_SEAT_CAPABILITY_KEYBOARD))
+       return NULL;
+
+    return wl_seat_get_keyboard(seat->proxy);
+}
+
+/*
+ * 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_client(const char *display)
+{
+    wayland_set_display(display);
+
+    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)
+    wayland_set_display("");
+    return FAIL;
+}
+
+/*
+ * Disconnect wayland client and free up all resources used.
+ */
+    void
+wayland_uninit_client(void)
+{
+#ifdef FEAT_WAYLAND_CLIPBOARD
+    wayland_cb_uninit();
+#endif
+    vwl_disconnect_display();
+
+    wayland_set_display("");
+}
+
+/*
+ * Return TRUE if wayland display connection is valid and ready.
+ */
+    int
+wayland_client_is_connected(int quiet)
+{
+    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;
+}
+
+/*
+ * Flush requests and process new Wayland events, does not poll the display file
+ * descriptor.
+ */
+    int
+wayland_client_update(void)
+{
+    return vwl_display_dispatch_any(&vwl_display) == -1 ? FAIL : OK;
+}
+
+#ifdef FEAT_WAYLAND_CLIPBOARD
+
+/*
+ * 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;
+}
+
+/*
+ * Configure xdg_surface
+ */
+    static void
+vwl_xdg_surface_listener_configure(
+       void                *data UNUSED,
+       struct xdg_surface  *surface,
+       uint32_t            serial)
+{
+    xdg_surface_ack_configure(surface, serial);
+}
+
+/*
+ * 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)
+{
+    vwl_buffer_store_T *store = data;
+
+    store->available = TRUE;
+}
+
+/*
+ * Destroy a buffer store structure.
+ */
+    static void
+vwl_destroy_buffer_store(vwl_buffer_store_T *store)
+{
+    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 vwl_buffer_store_T *
+vwl_init_buffer_store(int width, int height)
+{
+    int                        fd, r;
+    vwl_buffer_store_T *store;
+
+    if (vwl_gobjects.wl_shm == NULL)
+       return NULL;
+
+    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(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);
+
+    store->fd = fd;
+
+    wl_buffer_add_listener(store->buffer, &vwl_cb_buffer_listener, store);
+
+    if (vwl_display_roundtrip(&vwl_display) == -1)
+    {
+       vwl_destroy_buffer_store(store);
+       return NULL;
+    }
+
+    store->available = TRUE;
+
+    return store;
+}
+
+/*
+ * Destroy a focus stealing store structure.
+ */
+    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)
+    {
+       if (wl_keyboard_get_version(store->keyboard) >= 3)
+           wl_keyboard_release(store->keyboard);
+       else
+           wl_keyboard_destroy(store->keyboard);
+    }
+    vim_free(store);
+}
+
+/*
+ * 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;
+
+    if (vwl_gobjects.wl_compositor == NULL || vwl_gobjects.xdg_wm_base == NULL)
+       return FAIL;
+    if (buffer_store == NULL || seat == NULL)
+       return FAIL;
+
+    store = alloc_clear(sizeof(*store));
+
+    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_display_dispatch(&vwl_display) == -1)
+       goto fail;
+
+    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);
+
+    xdg_toplevel_set_title(store->shell.toplevel, "Vim clipboard");
+
+    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);
+
+    {
+       // 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;
+       }
+    }
+early_exit:
+    vwl_destroy_fs_surface(store);
+    vwl_display_flush(&vwl_display);
+
+    return OK;
+fail:
+    vwl_destroy_fs_surface(store);
+    vwl_display_flush(&vwl_display);
+
+    return FAIL;
+}
+
+/*
+ * 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_fs_surface_T *store = data;
+
+    store->got_focus = TRUE;
+
+    if (store->on_focus != NULL)
+       store->on_focus(store->user_data, serial);
+}
+
+// Dummy functions to handle keyboard events we don't care about.
+
+    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);
+}
+
+    static void
+vwl_fs_keyboard_listener_leave(
+    void               *data UNUSED,
+    struct wl_keyboard *keyboard UNUSED,
+    uint32_t           serial UNUSED,
+    struct wl_surface  *surface UNUSED)
+{
+}
+
+    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)
+{
+}
+
+    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)
+{
+}
+
+    static void
+vwl_fs_keyboard_listener_repeat_info(
+    void               *data UNUSED,
+    struct wl_keyboard *keyboard UNUSED,
+    int32_t            rate UNUSED,
+    int32_t            delay UNUSED)
+{
+}
+
+#define VWL_CODE_DATA_OBJECT_DESTROY(type) \
+do { \
+    if (type == NULL || type->proxy == NULL) \
+       return; \
+    switch (type->protocol) \
+    { \
+       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)
+
+    static void
+vwl_data_device_destroy(vwl_data_device_T *device, int alloced)
+{
+    VWL_CODE_DATA_OBJECT_DESTROY(device);
+}
+
+    static void
+vwl_data_offer_destroy(vwl_data_offer_T *offer, int alloced)
+{
+    VWL_CODE_DATA_OBJECT_DESTROY(offer);
+}
+
+    static void
+vwl_data_source_destroy(vwl_data_source_T *source, int alloced)
+{
+    VWL_CODE_DATA_OBJECT_DESTROY(source);
+}
+
+
+// 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;
+
+// These functions handle the more complicated data_offer and selection events.
+
+    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);
+    }
+}
+
+    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;
+    }
+}
+
+// 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_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)
+
+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
+};
+
+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
+};
+
+struct wl_data_device_listener wl_data_device_listener = {
+    .data_offer            = wl_data_device_listener_data_offer,
+    .selection     = wl_data_device_listener_selection,
+};
+
+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
+};
+
+// 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
+};
+
+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
+};
+
+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,
+};
+
+// OFFERS
+struct zwlr_data_control_offer_v1_listener
+zwlr_data_control_offer_v1_listener = {
+    .offer         = zwlr_data_control_offer_v1_listener_offer
+};
+
+struct ext_data_control_offer_v1_listener
+ext_data_control_offer_v1_listener = {
+    .offer         = ext_data_control_offer_v1_listener_offer
+};
+
+struct wl_data_offer_listener wl_data_offer_listener = {
+    .offer         = wl_data_offer_listener_offer
+};
+
+struct zwp_primary_selection_offer_v1_listener
+zwp_primary_selection_offer_v1_listener = {
+    .offer         = zwp_primary_selection_offer_v1_listener_offer
+};
+
+// `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) \
+    { \
+       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);
+}
+
+    static void
+vwl_data_offer_add_listener(vwl_data_offer_T *offer, void *data)
+{
+    VWL_CODE_DATA_OBJECT_ADD_LISTENER(offer);
+}
+
+/*
+ * 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.
+ */
+    static void
+vwl_data_device_set_selection(
+       vwl_data_device_T   *device,
+       vwl_data_source_T   *source,
+       uint32_t            serial,
+       wayland_selection_T selection)
+{
+    if (selection == WAYLAND_SELECTION_REGULAR)
+    {
+       switch (device->protocol)
+       {
+           case VWL_DATA_PROTOCOL_WLR:
+               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;
+           case VWL_DATA_PROTOCOL_CORE:
+               wl_data_device_set_selection(
+                       device->proxy, source->proxy, serial);
+               break;
+           default:
+               break;
+       }
+    }
+    else if (selection == WAYLAND_SELECTION_PRIMARY)
+    {
+       switch (device->protocol)
+       {
+           case VWL_DATA_PROTOCOL_WLR:
+               zwlr_data_control_device_v1_set_primary_selection(
+                       device->proxy, source->proxy);
+               break;
+           case VWL_DATA_PROTOCOL_EXT:
+               ext_data_control_device_v1_set_primary_selection(
+                       device->proxy, source->proxy);
+               break;
+           case VWL_DATA_PROTOCOL_PRIMARY:
+               zwp_primary_selection_device_v1_set_selection(
+                       device->proxy, source->proxy, serial);
+               break;
+           default:
+               break;
+       }
+    }
+}
+
+/*
+ * 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 (offer->protocol)
+    {
+       case VWL_DATA_PROTOCOL_WLR:
+           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;
+       case VWL_DATA_PROTOCOL_CORE:
+           wl_data_offer_receive(offer->proxy, mime_type, fd);
+           break;
+       case VWL_DATA_PROTOCOL_PRIMARY:
+           zwp_primary_selection_offer_v1_receive(offer->proxy, mime_type, fd);
+           break;
+       default:
+           break;
+    }
+}
+
+#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)
+{
+    // 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_WLR:
+           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;
+       case VWL_DATA_PROTOCOL_CORE:
+           device->proxy = wl_data_device_manager_get_data_device(
+                   manager->proxy, seat->proxy);
+           break;
+       case VWL_DATA_PROTOCOL_PRIMARY:
+           device->proxy = zwp_primary_selection_device_manager_v1_get_device(
+                   manager->proxy, seat->proxy);
+           break;
+       default:
+           device->protocol = VWL_DATA_PROTOCOL_NONE;
+           return;
+    }
+    device->protocol = manager->protocol;
+}
+
+/*
+ * 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(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)
+       // We already own the selection
+       return OK;
+
+    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