patch 9.1.1784: Wayland code can be improved
Commit:
https://github.com/vim/vim/commit/368710abcfaadc8370a07cdd58303f2704f10282
Author: Foxe Chen <
chen...@gmail.com>
Date: Mon Sep 22 19:06:58 2025 +0000
patch 9.1.1784: Wayland code can be improved
Problem: Wayland code can be improved
Solution: Refactor Wayland Clipboard code (Foxe Chen).
This the second attempt to refactor the Wayland code base:
- Move clipboard code from wayland.c to clipboard.c
- Use C99 bool type
- Properly poll the Wayland display file descriptor
- Instead of checking if the data source is not NULL in order to
determine if a selection event comes from us, use a special mime type
to identify selection events coming from ourselves. The problem with
the previous approach is that race conditions may occur.
- Put the focus stealing code under a new feature "wayland_focus_steal"
- Use ELAPSED_* macros instead of gettimeofday()
- Pass tests
- Reimplement commented out code
- Update docs
- Make Wayland clipboard behaviour more in line with X11 when connection
is lost
- add missing malloc checks and possible memory leaks + refactored some
tests.
closes: #18324
Signed-off-by: Foxe Chen <
chen...@gmail.com>
Signed-off-by: Christian Brabandt <
c...@256bit.org>
diff --git a/Filelist b/Filelist
index e97e17ec0..3a2e066e3 100644
--- a/Filelist
+++ b/Filelist
@@ -537,6 +537,7 @@ SRC_UNIX = \
src/vimtutor \
src/gvimtutor \
src/wayland.c \
+ src/wayland.h \
src/which.sh \
src/gen-wayland-protocols.sh \
src/xxd/Makefile \
diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt
index 2e5edbbdc..2e9535e65 100644
--- a/runtime/doc/builtin.txt
+++ b/runtime/doc/builtin.txt
@@ -1,4 +1,4 @@
-*builtin.txt* For Vim version 9.1. Last change: 2025 Sep 21
+*builtin.txt* For Vim version 9.1. Last change: 2025 Sep 22
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -13194,7 +13194,9 @@ 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
+wayland_clipboard Compiled with support for Wayland clipboard.
+wayland_focus_steal Compiled with support for Wayland clipboard focus
+ stealing.
wildignore Compiled with 'wildignore' option.
wildmenu Compiled with 'wildmenu' option.
win16 old version for MS-Windows 3.1 (always false)
diff --git a/runtime/doc/gui_x11.txt b/runtime/doc/gui_x11.txt
index 89ba7c2ee..3b618aa5e 100644
--- a/runtime/doc/gui_x11.txt
+++ b/runtime/doc/gui_x11.txt
@@ -1,4 +1,4 @@
-*gui_x11.txt* For Vim version 9.1. Last change: 2025 Sep 02
+*gui_x11.txt* For Vim version 9.1. Last change: 2025 Sep 22
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -714,6 +714,8 @@ output a warning:
Warning: Clipboard register not available, using register 0 ~
+Note: This also applies to the Wayland clipboard feature as well.
+
*W24*
Vim comes in different flavors, from a tiny build, that just tries to be
compatible to original Vi, to enhanced builds which include many improvements
diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt
index 7dad0c4ae..e1361d34d 100644
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -1,4 +1,4 @@
-*options.txt* For Vim version 9.1. Last change: 2025 Sep 20
+*options.txt* For Vim version 9.1. Last change: 2025 Sep 22
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -10204,7 +10204,8 @@ A jump table for the options with a short description can be found at |Q_op|.
*'wlsteal'* *'wst'* *'nowlsteal'* *'nowst'*
'wlsteal' 'wst' boolean (default off)
global
- {only when the |+wayland_clipboard| feature is included}
+ {only when the |+wayland_focus_steal| 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|.
diff --git a/runtime/doc/tags b/runtime/doc/tags
index b0662994e..7890b417e 100644
--- a/runtime/doc/tags
+++ b/runtime/doc/tags
@@ -1541,6 +1541,7 @@ $quote eval.txt /*$quote*
+vtp various.txt /*+vtp*
+wayland various.txt /*+wayland*
+wayland_clipboard various.txt /*+wayland_clipboard*
++wayland_focus_steal various.txt /*+wayland_focus_steal*
+wildignore various.txt /*+wildignore*
+wildmenu various.txt /*+wildmenu*
+windows various.txt /*+windows*
diff --git a/runtime/doc/various.txt b/runtime/doc/various.txt
index e29fafdf7..b91e3ef23 100644
--- a/runtime/doc/various.txt
+++ b/runtime/doc/various.txt
@@ -1,4 +1,4 @@
-*various.txt* For Vim version 9.1. Last change: 2025 Sep 02
+*various.txt* For Vim version 9.1. Last change: 2025 Sep 22
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -528,6 +528,9 @@ T *+vreplace* |gR| and |gr|
*+vtp* on MS-Windows console: support for 'termguicolors'
N *+wayland* Unix only: support for the Wayland protocol.
N *+wayland_clipboard* Unix only: support for Wayland selections/clipboard.
+N *+wayland_focus_steal*
+ Unix only: support for Wayland clipboard on
+ compositors without a data control protocol
T *+wildignore* 'wildignore' Always enabled since 9.0.0278
T *+wildmenu* 'wildmenu' Always enabled since 9.0.0279
T *+windows* more than one window; Always enabled since 8.0.1118.
diff --git a/runtime/doc/wayland.txt b/runtime/doc/wayland.txt
index adb7e694f..cbcdc0dc9 100644
--- a/runtime/doc/wayland.txt
+++ b/runtime/doc/wayland.txt
@@ -1,4 +1,4 @@
-*wayland.txt* For Vim version 9.1. Last change: 2025 Sep 15
+*wayland.txt* For Vim version 9.1. Last change: 2025 Sep 22
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -105,7 +105,8 @@ 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.
+xdg-shell protocol must be available. Additionally, Vim must be compiled with
+the |+wayland_focus_steal| feature.
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
diff --git a/runtime/optwin.vim b/runtime/optwin.vim
index 5b5727aa6..538b44dad 100644
--- a/runtime/optwin.vim
+++ b/runtime/optwin.vim
@@ -1,7 +1,7 @@
" These commands create the option window.
"
" Maintainer: The Vim Project <
https://github.com/vim/vim>
-" Last Change: 2025 Sep 20
+" Last Change: 2025 Sep 22
" Former Maintainer: Bram Moolenaar <
Br...@vim.org>
" If there already is an option window, jump to that one.
@@ -822,7 +822,7 @@ if has('wayland')
call <SID>AddOption("wlseat", gettext("Wayland seat to use"))
call <SID>OptionG("wse", &wse)
endif
-if has("wayland_clipboard")
+if has("wayland_focus_steal")
call <SID>AddOption("wlsteal", gettext("Enable wayland focus stealing functionality in order to access the clipboard"))
call <SID>BinOptionG("wst", &wst)
endif
diff --git a/src/Makefile b/src/Makefile
index 333a9121c..b2b7a83b8 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -1646,7 +1646,9 @@ MESSAGE_TEST_TARGET = message_test$(EXEEXT)
UNITTEST_SRC = $(JSON_TEST_SRC) $(KWORD_TEST_SRC) $(MEMFILE_TEST_SRC) $(MESSAGE_TEST_SRC)
UNITTEST_TARGETS = $(JSON_TEST_TARGET) $(KWORD_TEST_TARGET) $(MEMFILE_TEST_TARGET) $(MESSAGE_TEST_TARGET)
-RUN_UNITTESTS = run_json_test run_kword_test run_memfile_test run_message_test
+# We need to put WAYLAND_SRC because the protocol files need to be generated
+# else wayland.h will error
+RUN_UNITTESTS = $(WAYLAND_SRC) run_json_test run_kword_test run_memfile_test run_message_test
# All sources, also the ones that are not configured
ALL_LOCAL_SRC = $(BASIC_SRC) $(ALL_GUI_SRC) $(UNITTEST_SRC) $(EXTRA_SRC) \
@@ -3862,9 +3864,9 @@ objects/clientserver.o: clientserver.c vim.h protodef.h auto/config.h feature.h
ex_cmds.h spell.h proto.h globals.h errors.h
objects/clipboard.o: clipboard.c vim.h protodef.h auto/config.h feature.h \
os_unix.h auto/osdef.h ascii.h keymap.h termdefs.h macros.h option.h \
- beval.h 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
+ beval.h structs.h regexp.h gui.h libvterm/include/vterm.h \
+ libvterm/include/vterm_keycodes.h xdiff/xdiff.h xdiff/../vim.h alloc.h \
+ ex_cmds.h spell.h proto.h globals.h errors.h wayland.h
objects/cmdexpand.o: cmdexpand.c vim.h protodef.h auto/config.h feature.h \
os_unix.h auto/osdef.h ascii.h keymap.h termdefs.h macros.h option.h \
beval.h proto/
gui_beval.pro structs.h regexp.h gui.h \
@@ -4565,7 +4567,7 @@ objects/wayland.o: wayland.c vim.h protodef.h auto/config.h feature.h os_unix.h
auto/osdef.h ascii.h keymap.h termdefs.h macros.h option.h beval.h \
structs.h regexp.h gui.h libvterm/include/vterm.h \
libvterm/include/vterm_keycodes.h xdiff/xdiff.h xdiff/../vim.h alloc.h \
- ex_cmds.h spell.h proto.h globals.h errors.h \
+ ex_cmds.h spell.h proto.h globals.h errors.h wayland.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
diff --git a/src/auto/configure b/src/auto/configure
index 5c0d3d614..c99feb050 100755
--- a/src/auto/configure
+++ b/src/auto/configure
@@ -862,6 +862,7 @@ enable_farsi
enable_xim
enable_fontset
with_wayland
+enable_wayland_focus_steal
with_x
enable_gui
enable_gtk2_check
@@ -1542,6 +1543,9 @@ Optional Features:
--disable-farsi Deprecated.
--enable-xim Include XIM input support.
--enable-fontset Include X fontset output support.
+ --enable-wayland-focus-steal
+ Include focus stealing support for Wayland
+ clipboard.
--enable-gui=OPTS X11 GUI. default=auto OPTS=auto/no/gtk2/gnome2/gtk3/motif/haiku/photon/carbon
--enable-gtk2-check If auto-select GUI, check for GTK+ 2 default=yes
--enable-gnome-check If GTK GUI, check for GNOME default=no
@@ -9271,13 +9275,39 @@ fi
if test "$with_wayland" = yes; then
-cppflags_save=$CPPFLAGS
-cflags_save=$CFLAGS
+ cppflags_save=$CPPFLAGS
+ cflags_save=$CFLAGS
+
{ printf "%s
" "$as_me:${as_lineno-$LINENO}: checking for wayland" >&5
printf %s "checking for wayland... " >&6; }
if "$PKG_CONFIG" --exists 'wayland-client'; then
{ printf "%s
" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
printf "%s
" "yes" >&6; }
+
+ { printf "%s
" "$as_me:${as_lineno-$LINENO}: checking --enable-wayland-focus-steal argument" >&5
+printf %s "checking --enable-wayland-focus-steal argument... " >&6; }
+ # Check whether --enable-wayland-focus-steal was given.
+if test ${enable_wayland_focus_steal+y}
+then :
+ enableval=$enable_wayland_focus_steal; enable_wayland_fs=$enableval
+else case e in #(
+ e) enable_wayland_fs="yes" ;;
+esac
+fi
+
+
+ if test "$enable_wayland_fs" = "yes"
+then :
+ { printf "%s
" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s
" "yes" >&6; }
+ printf "%s
" "#define FEAT_WAYLAND_CLIPBOARD_FS 1" >>confdefs.h
+
+else case e in #(
+ e) { printf "%s
" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s
" "no" >&6; } ;;
+esac
+fi
+
printf "%s
" "#define HAVE_WAYLAND 1" >>confdefs.h
WAYLAND_CPPFLAGS=`$PKG_CONFIG --cflags-only-I wayland-client`
@@ -9288,16 +9318,23 @@ printf "%s
" "yes" >&6; }
WAYLAND_SRC=" \
auto/wayland/wlr-data-control-unstable-v1.c \
auto/wayland/ext-data-control-v1.c \
- auto/wayland/xdg-shell.c \
- auto/wayland/primary-selection-unstable-v1.c \
wayland.c"
WAYLAND_OBJ=" \
objects/wlr-data-control-unstable-v1.o \
objects/ext-data-control-v1.o \
- objects/xdg-shell.o \
- objects/primary-selection-unstable-v1.o \
objects/wayland.o"
+ if test "$enable_wayland_fs" = "yes"
+then :
+ as_fn_append WAYLAND_SRC " \
+ auto/wayland/xdg-shell.c \
+ auto/wayland/primary-selection-unstable-v1.c"
+ as_fn_append WAYLAND_OBJ " \
+ objects/xdg-shell.o \
+ objects/primary-selection-unstable-v1.o"
+fi
+
+
diff --git a/src/clipboard.c b/src/clipboard.c
index 4b2e6874a..66f3e321a 100644
--- a/src/clipboard.c
+++ b/src/clipboard.c
@@ -32,6 +32,80 @@
#if defined(FEAT_CLIPBOARD) || defined(PROTO)
#if defined(FEAT_WAYLAND_CLIPBOARD)
+
+# include "wayland.h"
+
+# ifdef FEAT_WAYLAND_CLIPBOARD_FS
+
+// Structures used for focus stealing
+typedef struct {
+ struct wl_shm_pool *pool;
+ int fd;
+
+ struct wl_buffer *buffer;
+ bool available;
+
+ int width;
+ int height;
+ int stride;
+ int size;
+} clip_wl_buffer_store_T;
+
+typedef struct {
+ void *user_data;
+ void (*on_focus)(void *data, uint32_t serial);
+
+ struct wl_surface *surface;
+ struct wl_keyboard *keyboard;
+
+ struct {
+ struct xdg_surface *surface;
+ struct xdg_toplevel *toplevel;
+ } shell;
+
+ bool got_focus;
+} clip_wl_fs_surface_T; // fs = focus steal
+
+# endif // FEAT_WAYLAND_CLIPBOARD_FS
+
+// Represents either the regular or primary selection
+typedef struct {
+ char_u *contents; // Non-null if we own selection,
+ // contains the data to send to other
+ // clients.
+ vwl_data_source_T *source; // Non-NULL if we own the selection,
+ // else NULL if we don't.
+ vwl_data_offer_T *offer; // Current offer for the selection
+
+# ifdef FEAT_WAYLAND_CLIPBOARD_FS
+ bool requires_focus; // If focus needs to be given to us to
+ // work
+# endif
+ bool own_success; // Used by clip_wl_own_selection()
+ bool available; // If selection is ready to serve/use
+
+ // These may point to the same proxy as the other selection
+ vwl_data_device_manager_T *manager;
+ vwl_data_device_T *device;
+} clip_wl_selection_T;
+
+// Represents the clipboard for the global Wayland connection, for the chosen
+// seat (using the 'wl_seat' option)
+typedef struct {
+ vwl_seat_T *seat;
+
+#ifdef FEAT_WAYLAND_CLIPBOARD_FS
+ clip_wl_buffer_store_T *fs_buffer;
+#endif
+
+ clip_wl_selection_T regular;
+ clip_wl_selection_T primary;
+
+ // Array of file descriptors of clients we are sending data to. These should
+ // be polled for POLLOUT and have the respective callback called for each.
+ garray_T write_fds;
+} clip_wl_T;
+
// Mime types we support sending and receiving
// Mimes with a lower index in the array are prioritized first when we are
// receiving data.
@@ -45,21 +119,20 @@ static const char *supported_mimes[] = {
"TEXT"
};
-static void clip_wl_receive_data(Clipboard_T *cbd,
- const char *mime_type, int fd);
+clip_wl_T clip_wl;
+
+static void
+clip_wl_receive_data(Clipboard_T *cbd, const char *mime_type, int fd);
static void clip_wl_request_selection(Clipboard_T *cbd);
-static void clip_wl_send_data(const char *mime_type, int fd,
- wayland_selection_T);
static int clip_wl_own_selection(Clipboard_T *cbd);
static void clip_wl_lose_selection(Clipboard_T *cbd);
static void clip_wl_set_selection(Clipboard_T *cbd);
-static void clip_wl_selection_cancelled(wayland_selection_T selection);
-#if defined(USE_SYSTEM) && defined(PROTO)
-static int clip_wl_owner_exists(Clipboard_T *cbd);
-#endif
+# if defined(USE_SYSTEM) || defined(PROTO)
+static bool clip_wl_owner_exists(Clipboard_T *cbd);
+# endif
-#endif
+#endif // FEAT_WAYLAND_CLIPBOARD
/*
* Selection stuff using Visual mode, for cutting and pasting text to other
@@ -99,6 +172,22 @@ skip:
}
}
+ static void
+clip_init_single(Clipboard_T *cb, int can_use)
+{
+ // No need to init again if cbd is already available
+ if (can_use && cb->available)
+ return;
+
+ cb->available = can_use;
+ cb->owned = FALSE;
+ cb->start.lnum = 0;
+ cb->start.col = 0;
+ cb->end.lnum = 0;
+ cb->end.col = 0;
+ cb->state = SELECT_CLEARED;
+}
+
/*
* Check whether the VIsual area has changed, and if so try to become the owner
* of the selection, and free any old converted selection we may still have
@@ -2207,13 +2296,13 @@ clip_yank_selection(
str_to_reg(y_ptr, type, str, len, -1, FALSE);
}
-/*
- * Convert the '*'/'+' register into a GUI selection string returned in *str
- * with length *len.
- * Returns the motion type, or -1 for failure.
- */
- int
-clip_convert_selection(char_u **str, long_u *len, Clipboard_T *cbd)
+ static int
+clip_convert_selection_offset(
+ char_u **str,
+ long_u *len,
+ int offset, // Extra space to add in *str and the offset to
+ // place the actual string in *str.
+ Clipboard_T *cbd)
{
char_u *p;
int lnum;
@@ -2244,11 +2333,13 @@ clip_convert_selection(char_u **str, long_u *len, Clipboard_T *cbd)
if (y_ptr->y_type == MCHAR && *len >= eolsize)
*len -= eolsize;
+ *len += offset;
p = *str = alloc(*len + 1); // add one to avoid zero
if (p == NULL)
return -1;
+ p += offset;
lnum = 0;
- for (i = 0, j = 0; i < (int)*len; i++, j++)
+ for (i = 0, j = 0; i < (int)*len - offset; i++, j++)
{
if (y_ptr->y_array[lnum].string[j] == '
')
p[i] = NUL;
@@ -2267,6 +2358,17 @@ clip_convert_selection(char_u **str, long_u *len, Clipboard_T *cbd)
return y_ptr->y_type;
}
+/*
+ * Convert the '*'/'+' register into a GUI selection string returned in *str
+ * with length *len.
+ * Returns the motion type, or -1 for failure.
+ */
+ int
+clip_convert_selection(char_u **str, long_u *len, Clipboard_T *cbd)
+{
+ return clip_convert_selection_offset(str, len, 0, cbd);
+}
+
/*
* When "regname" is a clipboard register, obtain the selection. If it's not
* available return zero, otherwise return "regname".
@@ -2332,12 +2434,591 @@ adjust_clip_reg(int *rp)
if ((!clip_star.available && *rp == '*') ||
(!clip_plus.available && *rp == '+'))
{
- msg_warn_missing_clipboard();
+ msg_warn_missing_clipboard(!clip_plus.available, !clip_star.available);
*rp = 0;
}
}
-#if defined(FEAT_WAYLAND_CLIPBOARD) || defined(PROTO)
+#if defined(FEAT_WAYLAND_CLIPBOARD)
+
+ static clip_wl_selection_T *
+clip_wl_get_selection(wayland_selection_T sel)
+{
+ switch (sel)
+ {
+ case WAYLAND_SELECTION_REGULAR:
+ return &clip_wl.regular;
+ case WAYLAND_SELECTION_PRIMARY:
+ return &clip_wl.primary;
+ default:
+ return NULL;
+ }
+}
+
+ static clip_wl_selection_T *
+clip_wl_get_selection_from_cbd(Clipboard_T *cbd)
+{
+ if (cbd == &clip_plus)
+ return &clip_wl.regular;
+ else if (cbd == &clip_star)
+ return &clip_wl.primary;
+ else
+ return NULL;
+}
+
+ static Clipboard_T *
+clip_wl_get_cbd_from_selection(clip_wl_selection_T *sel)
+{
+ if (sel == &clip_wl.regular)
+ return &clip_plus;
+ else if (sel == &clip_wl.primary)
+ return &clip_star;
+ else
+ return NULL;
+}
+
+ static wayland_selection_T
+clip_wl_get_selection_type(clip_wl_selection_T *sel)
+{
+ if (sel == &clip_wl.regular)
+ return WAYLAND_SELECTION_REGULAR;
+ else if (sel == &clip_wl.primary)
+ return WAYLAND_SELECTION_PRIMARY;
+ else
+ return WAYLAND_SELECTION_NONE;
+}
+
+#ifdef FEAT_WAYLAND_CLIPBOARD_FS
+/*
+ * If globals required for focus stealing method are available.
+ */
+ static bool
+clip_wl_focus_stealing_available(void)
+{
+ return wayland_ct->gobjects.wl_compositor != NULL &&
+ wayland_ct->gobjects.wl_shm != NULL &&
+ wayland_ct->gobjects.xdg_wm_base != NULL;
+}
+
+/*
+ * Called when compositor isn't using the buffer anymore, we can reuse it
+ * again.
+ */
+ static void
+wl_buffer_listener_release(
+ void *data,
+ struct wl_buffer *buffer UNUSED)
+{
+ clip_wl_buffer_store_T *store = data;
+
+ store->available = true;
+}
+
+static struct wl_buffer_listener wl_buffer_listener = {
+ .release = wl_buffer_listener_release
+};
+
+/*
+ * Destroy a buffer store structure.
+ */
+ static void
+clip_wl_destroy_buffer_store(clip_wl_buffer_store_T *store)
+{
+ if (store == NULL)
+ return;
+ if (store->buffer != NULL)
+ wl_buffer_destroy(store->buffer);
+ if (store->pool != NULL)
+ wl_shm_pool_destroy(store->pool);
+
+ close(store->fd);
+
+ vim_free(store);
+}
+
+/*
+ * Initialize a buffer and its backing memory pool.
+ */
+ static clip_wl_buffer_store_T *
+clip_wl_init_buffer_store(int width, int height)
+{
+ int fd, r;
+ clip_wl_buffer_store_T *store;
+
+ store = alloc(sizeof(*store));
+
+ if (store == NULL)
+ return NULL;
+
+ store->available = false;
+
+ store->width = width;
+ store->height = height;
+ store->stride = store->width * 4;
+ store->size = store->stride * store->height;
+
+ fd = mch_create_anon_file();
+ r = ftruncate(fd, store->size);
+
+ if (r == -1)
+ {
+ if (fd >= 0)
+ close(fd);
+ return NULL;
+ }
+
+ store->pool = wl_shm_create_pool(
+ wayland_ct->gobjects.wl_shm,
+ fd,
+ store->size);
+ store->buffer = wl_shm_pool_create_buffer(
+ store->pool,
+ 0,
+ store->width,
+ store->height,
+ store->stride,
+ WL_SHM_FORMAT_ARGB8888);
+
+ store->fd = fd;
+
+ wl_buffer_add_listener(store->buffer, &wl_buffer_listener, store);
+
+ if (vwl_connection_roundtrip(wayland_ct) == FAIL)
+ {
+ clip_wl_destroy_buffer_store(store);
+ return NULL;
+ }
+
+ store->available = true;
+
+ return store;
+}
+
+/*
+ * Configure xdg_surface
+ */
+ static void
+xdg_surface_listener_configure(
+ void *data UNUSED,
+ struct xdg_surface *surface,
+ uint32_t serial)
+{
+ xdg_surface_ack_configure(surface, serial);
+}
+
+
+static struct xdg_surface_listener xdg_surface_listener = {
+ .configure = xdg_surface_listener_configure
+};
+
+/*
+ * Destroy a focus stealing structure.
+ */
+ static void
+clip_wl_destroy_fs_surface(clip_wl_fs_surface_T *store)
+{
+ if (store == NULL)
+ return;
+ if (store->shell.toplevel != NULL)
+ xdg_toplevel_destroy(store->shell.toplevel);
+ if (store->shell.surface != NULL)
+ xdg_surface_destroy(store->shell.surface);
+ if (store->surface != NULL)
+ wl_surface_destroy(store->surface);
+ if (store->keyboard != NULL)
+ {
+ if (wl_keyboard_get_version(store->keyboard) >= 3)
+ wl_keyboard_release(store->keyboard);
+ else
+ wl_keyboard_destroy(store->keyboard);
+ }
+ vim_free(store);
+}
+
+VWL_FUNCS_DUMMY_KEYBOARD_EVENTS()
+
+/*
+ * Called when the keyboard focus is on our surface
+ */
+ static void
+clip_wl_fs_keyboard_listener_enter(
+ void *data,
+ struct wl_keyboard *keyboard UNUSED,
+ uint32_t serial,
+ struct wl_surface *surface UNUSED,
+ struct wl_array *keys UNUSED)
+{
+ clip_wl_fs_surface_T *store = data;
+
+ store->got_focus = true;
+
+ if (store->on_focus != NULL)
+ store->on_focus(store->user_data, serial);
+}
+
+
+static struct wl_keyboard_listener vwl_fs_keyboard_listener = {
+ .enter = clip_wl_fs_keyboard_listener_enter,
+ .key = clip_wl_fs_keyboard_listener_key,
+ .keymap = clip_wl_fs_keyboard_listener_keymap,
+ .leave = clip_wl_fs_keyboard_listener_leave,
+ .modifiers = clip_wl_fs_keyboard_listener_modifiers,
+ .repeat_info = clip_wl_fs_keyboard_listener_repeat_info
+};
+
+/*
+ * Create an invisible surface in order to gain focus and call on_focus() with
+ * serial that was given.
+ */
+ static int
+clip_wl_init_fs_surface(
+ vwl_seat_T *seat,
+ clip_wl_buffer_store_T *buffer_store,
+ void (*on_focus)(void *, uint32_t),
+ void *user_data)
+{
+ clip_wl_fs_surface_T *store;
+#ifdef ELAPSED_FUNC
+ elapsed_T start_tv;
+#endif
+
+ if (wayland_ct->gobjects.wl_compositor == NULL
+ || wayland_ct->gobjects.xdg_wm_base == NULL
+ || buffer_store == NULL
+ || seat == NULL)
+ return FAIL;
+
+ store = ALLOC_CLEAR_ONE(clip_wl_fs_surface_T);
+
+ if (store == NULL)
+ return FAIL;
+
+ // Get keyboard
+ store->keyboard = vwl_seat_get_keyboard(seat);
+
+ if (store->keyboard == NULL)
+ goto fail;
+
+ wl_keyboard_add_listener(store->keyboard, &vwl_fs_keyboard_listener, store);
+
+ if (vwl_connection_dispatch(wayland_ct) < 0)
+ goto fail;
+
+ store->surface = wl_compositor_create_surface(
+ wayland_ct->gobjects.wl_compositor);
+ store->shell.surface = xdg_wm_base_get_xdg_surface(
+ wayland_ct->gobjects.xdg_wm_base, store->surface);
+ store->shell.toplevel = xdg_surface_get_toplevel(store->shell.surface);
+
+ xdg_toplevel_set_title(store->shell.toplevel, "Vim clipboard");
+
+ xdg_surface_add_listener(store->shell.surface,
+ &xdg_surface_listener, NULL);
+
+ wl_surface_commit(store->surface);
+
+ store->on_focus = on_focus;
+ store->user_data = user_data;
+ store->got_focus = FALSE;
+
+ if (vwl_connection_roundtrip(wayland_ct) == FAIL)
+ goto fail;
+
+ // We may get the enter event early, if we do then we will set `got_focus`
+ // to TRUE.
+ if (store->got_focus)
+ goto early_exit;
+
+ // Buffer hasn't been released yet, abort. This shouldn't happen but still
+ // check for it.
+ if (!buffer_store->available)
+ goto fail;
+
+ buffer_store->available = false;
+
+ wl_surface_attach(store->surface, buffer_store->buffer, 0, 0);
+ wl_surface_damage(store->surface, 0, 0,
+ buffer_store->width, buffer_store->height);
+ wl_surface_commit(store->surface);
+
+ // Dispatch events until we receive the enter event. Add a max delay of
+ // 'p_wtm' when waiting for it (may be longer depending on how long we poll
+ // when dispatching events)
+#ifdef ELAPSED_FUNC
+ ELAPSED_INIT(start_tv);
+#endif
+
+ while (vwl_connection_dispatch(wayland_ct) >= 0)
+ {
+ if (store->got_focus)
+ break;
+
+#ifdef ELAPSED_FUNC
+ if (ELAPSED_FUNC(start_tv) >= p_wtm)
+ goto fail;
+#endif
+ }
+early_exit:
+ clip_wl_destroy_fs_surface(store);
+ vwl_connection_flush(wayland_ct);
+
+ return OK;
+fail:
+ clip_wl_destroy_fs_surface(store);
+ vwl_connection_flush(wayland_ct);
+
+ return FAIL;
+}
+
+#endif // FEAT_WAYLAND_CLIPBOARD_FS
+
+ static bool
+wl_data_offer_listener_event_offer(
+ void *data UNUSED,
+ vwl_data_offer_T *offer UNUSED,
+ const char *mime_type
+)
+{
+ // Only accept mime type if we support it
+ for (int i = 0; i < (int)ARRAY_LENGTH(supported_mimes); i++)
+ if (STRCMP(mime_type, supported_mimes[i]) == 0)
+ return true;
+ return FALSE;
+}
+
+static const vwl_data_offer_listener_T vwl_data_offer_listener = {
+ .offer = wl_data_offer_listener_event_offer
+};
+
+ static void
+vwl_data_device_listener_event_data_offer(
+ void *data UNUSED,
+ vwl_data_device_T *device UNUSED,
+ vwl_data_offer_T *offer)
+{
+ // Immediately start listening for offer events from the data offer
+ vwl_data_offer_add_listener(offer, &vwl_data_offer_listener, NULL);
+}
+
+ static void
+vwl_data_device_listener_event_selection(
+ void *data UNUSED,
+ vwl_data_device_T *device UNUSED,
+ vwl_data_offer_T *offer,
+ wayland_selection_T selection)
+{
+ clip_wl_selection_T *sel = clip_wl_get_selection(selection);
+
+ // Destroy previous offer if any, it is now invalid
+ vwl_data_offer_destroy(sel->offer);
+
+ // There are two cases when sel->offer is NULL
+ // 1. No one owns the selection
+ // 2. We own the selection (we'll just access the register directly)
+ if (offer == NULL || offer->from_vim)
+ {
+ // Selection event is from us, so we are the source client. Therefore
+ // ignore it. Or the selection is cleared, so set sel->offer to NULL
+ vwl_data_offer_destroy(offer);
+ sel->offer = NULL;
+ return;
+ }
+
+ // Save offer. When we want to request data, then we'll actually call the
+ // receive method.
+ sel->offer = offer;
+
+}
+
+ static void
+vwl_data_device_listener_event_finished(
+ void *data UNUSED,
+ vwl_data_device_T *device)
+{
+ clip_wl_selection_T *sel;
+ // Device finished, guessing this can happen is when the seat becomes
+ // invalid? If so, let the user call :wlrestore! to reset. There wouldn't be
+ // any point in trying to create another data device for the same seat,
+ // since the seat is in an invalid state.
+ if (device == clip_wl.regular.device)
+ {
+ sel = &clip_wl.regular;
+ clip_wl.regular.device = NULL;
+ }
+ else if (device == clip_wl.primary.device)
+ {
+ sel = &clip_wl.primary;
+ clip_wl.primary.device = NULL;
+ }
+ else
+ // Shouldn't happen
+ return;
+
+ vim_free(sel->contents);
+ vwl_data_source_destroy(sel->source);
+ vwl_data_offer_destroy(sel->offer);
+ sel->available = FALSE;
+
+ vwl_data_device_destroy(device);
+}
+
+static const vwl_data_device_listener_T vwl_data_device_listener = {
+ .data_offer = vwl_data_device_listener_event_data_offer,
+ .selection = vwl_data_device_listener_event_selection,
+ .finished = vwl_data_device_listener_event_finished
+};
+
+/*
+ * Initialize the clipboard for Wayland using the global Wayland connection.
+ * Returns OK on success and FAIL on failure.
+ */
+ int
+clip_init_wayland(void)
+{
+ int_u supported = WAYLAND_SELECTION_NONE;
+
+ if (wayland_ct == NULL)
+ return FAIL;
+
+ clip_wl.seat = vwl_connection_get_seat(wayland_ct, (char *)p_wse);
+
+ if (clip_wl.seat == NULL)
+ return FAIL;
+
+ clip_wl.regular.manager = vwl_connection_get_data_device_manager(
+ wayland_ct, WAYLAND_SELECTION_REGULAR, &supported);
+
+ if (clip_wl.regular.manager != NULL)
+ {
+ clip_wl.regular.device = vwl_data_device_manager_get_data_device(
+ clip_wl.regular.manager, clip_wl.seat);
+
+ if (clip_wl.regular.device != NULL)
+ clip_wl.regular.available = true;
+ else
+ {
+ vwl_data_device_manager_discard(clip_wl.regular.manager);
+ clip_wl.regular.manager = NULL;
+ }
+ }
+
+ // If we still don't support the primary selection, find one for it
+ // specifically.
+ if (!(supported & WAYLAND_SELECTION_PRIMARY))
+ {
+ clip_wl.primary.manager = vwl_connection_get_data_device_manager(
+ wayland_ct, WAYLAND_SELECTION_PRIMARY, &supported);
+
+ if (clip_wl.primary.manager != NULL)
+ {
+ clip_wl.primary.device = vwl_data_device_manager_get_data_device(
+ clip_wl.primary.manager, clip_wl.seat);
+
+ if (clip_wl.primary.device != NULL)
+ clip_wl.primary.available = true;
+ else
+ {
+ vwl_data_device_manager_discard(clip_wl.primary.manager);
+ clip_wl.primary.manager = NULL;
+ }
+ }
+ }
+ else if (clip_wl.regular.available)
+ {
+ // The protocol supports both regular and primary selections, just use
+ // one data device manager and one data device.
+ clip_wl.primary.available = true;
+ clip_wl.primary.manager = clip_wl.regular.manager;
+ clip_wl.primary.device = clip_wl.regular.device;
+ }
+
+#ifdef FEAT_WAYLAND_CLIPBOARD_FS
+ if (clip_wl.regular.available
+ && clip_wl.regular.manager->protocol == VWL_DATA_PROTOCOL_CORE
+ && clip_wl_focus_stealing_available())
+ clip_wl.regular.requires_focus = true;
+ if (clip_wl.primary.available
+ && clip_wl.primary.manager->protocol == VWL_DATA_PROTOCOL_PRIMARY
+ && clip_wl_focus_stealing_available())
+ clip_wl.primary.requires_focus = true;
+
+ if (clip_wl.regular.requires_focus || clip_wl.primary.requires_focus)
+ {
+ // Initialize buffer to use for focus stealing
+ clip_wl.fs_buffer = clip_wl_init_buffer_store(1, 1);
+ }
+#endif
+
+ if (!clip_wl.regular.available && !clip_wl.primary.available)
+ return FAIL;
+
+ // Start listening for selection updates
+ if (clip_wl.regular.device != NULL)
+ vwl_data_device_add_listener(clip_wl.regular.device,
+ &vwl_data_device_listener, NULL);
+ // Don't want to listen to the same data device twice
+ if (clip_wl.primary.device != NULL
+ && clip_wl.primary.device != clip_wl.regular.device)
+ vwl_data_device_add_listener(clip_wl.primary.device,
+ &vwl_data_device_listener, NULL);
+
+ return OK;
+}
+
+ void
+clip_uninit_wayland(void)
+{
+ clip_wl_selection_T *sel;
+
+ if (clipmethod == CLIPMETHOD_WAYLAND)
+ {
+ if (clip_star.owned)
+ clip_lose_selection(&clip_star);
+ if (clip_plus.owned)
+ clip_lose_selection(&clip_plus);
+ }
+
+#ifdef FEAT_WAYLAND_CLIPBOARD_FS
+ clip_wl_destroy_buffer_store(clip_wl.fs_buffer);
+#endif
+
+ // Don't want to double free
+ if (clip_wl.regular.manager != clip_wl.primary.manager)
+ vwl_data_device_manager_discard(clip_wl.primary.manager);
+ vwl_data_device_manager_discard(clip_wl.regular.manager);
+
+ if (clip_wl.regular.device != clip_wl.primary.device)
+ vwl_data_device_destroy(clip_wl.primary.device);
+ vwl_data_device_destroy(clip_wl.regular.device);
+
+ sel = &clip_wl.regular;
+ while (true)
+ {
+ vim_free(sel->contents);
+ vwl_data_source_destroy(sel->source);
+ vwl_data_offer_destroy(sel->offer);
+ sel->available = false;
+
+ if (sel == &clip_wl.primary)
+ break;
+ sel = &clip_wl.primary;
+ }
+
+ vim_memset(&clip_wl, 0, sizeof(clip_wl));
+}
+
+ int
+clip_reset_wayland(void)
+{
+ wayland_uninit_connection();
+
+ if (wayland_init_connection(wayland_display_name) == FAIL
+ || clip_init_wayland() == FAIL)
+ return FAIL;
+
+ choose_clipmethod();
+ return OK;
+}
/*
* Read data from a file descriptor and write it to the given clipboard.
@@ -2350,10 +3031,10 @@ clip_wl_receive_data(Clipboard_T *cbd, const char *mime_type, int fd)
int motion_type = MAUTO;
ssize_t r = 0;
#ifndef HAVE_SELECT
- struct pollfd pfd
+ struct pollfd pfd;
- pfd.fd = fd,
- pfd.events = POLLIN
+ pfd.fd = fd;
+ pfd.events = POLLIN;
#else
fd_set rfds;
struct timeval tv;
@@ -2368,17 +3049,19 @@ clip_wl_receive_data(Clipboard_T *cbd, const char *mime_type, int fd)
ga_init2(&buf, 1, 4096);
- // 4096 bytes seems reasonable for initial buffer size
+ // 4096 bytes seems reasonable for initial buffer size, memory is cheap
+ // anyways.
if (ga_grow(&buf, 4096) == FAIL)
return;
start = buf.ga_data;
- // 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)
+#ifndef HAVE_SELECT
+ while (poll(&pfd, 1, p_wtm) > 0)
+#else
+ while (tv.tv_sec = p_wtm / 1000, tv.tv_usec = (p_wtm % 1000) * 1000,
+ select(fd + 1, &rfds, NULL, NULL, &tv) > 0)
+#endif
{
r = read(fd, start, buf.ga_maxlen - 1 - buf.ga_len);
@@ -2387,18 +3070,7 @@ clip_wl_receive_data(Clipboard_T *cbd, const char *mime_type, int fd)
else if (r < 0)
{
if (errno == EAGAIN || errno == EINTR)
- {
-poll_data:
-#ifndef HAVE_SELECT
- if (poll(&pfd, 1, p_wtm) > 0)
- continue;
-#else
- tv.tv_sec = 0;
- tv.tv_usec = p_wtm * 1000;
- if (select(fd + 1, &rfds, NULL, NULL, &tv) > 0)
- continue;
-#endif
- }
+ continue;
break;
}
@@ -2472,182 +3144,182 @@ poll_data:
static 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;
+ clip_wl_selection_T *sel = clip_wl_get_selection_from_cbd(cbd);
+ int fds[2];
+ int mime_types_len;
+ const char **mime_types;
+ const char *chosen_mime = NULL;
- // Get mime types that the source client offers
- mime_types = wayland_cb_get_mime_types(selection);
+ if (!sel->available)
+ goto clear;
- if (mime_types == NULL || mime_types->ga_len == 0)
+#ifdef FEAT_WAYLAND_CLIPBOARD_FS
+ if (sel->requires_focus)
{
- // Selection is empty/cleared
- clip_free_selection(cbd);
- return;
+ // We don't care about the on_focus callback since once we gain
+ // focus the data offer events will come immediately.
+ if (clip_wl_init_fs_surface(clip_wl.seat,
+ clip_wl.fs_buffer, NULL, NULL) == FAIL)
+ goto clear;
+ }
+ else
+#endif
+ {
+ // Dispatch any events that still queued up before checking for a data
+ // offer.
+ if (vwl_connection_roundtrip(wayland_ct) == FAIL)
+ goto clear;
}
- len = ARRAY_LENGTH(supported_mimes);
+ if (sel->offer == NULL)
+ goto clear;
- // 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];
+ mime_types_len = sel->offer->mime_types.ga_len;
+ mime_types = sel->offer->mime_types.ga_data;
- if (STRCMP(mime_type, supported_mimes[i]) == 0)
+ // Choose mime type to receive from. Mime types with a lower index in the
+ // "supported_mimes" array are prioritized over ones after it.
+ for (int i = 0; i < (int)ARRAY_LENGTH(supported_mimes)
+ && chosen_mime == NULL; i++)
+ {
+ for (int k = 0; k < mime_types_len && chosen_mime == NULL; k++)
+ if (STRCMP(mime_types[k], supported_mimes[i]) == 0)
chosen_mime = supported_mimes[i];
- }
}
- if (chosen_mime == NULL)
- return;
- fd = wayland_cb_receive_data(chosen_mime, selection);
+ if (chosen_mime == NULL || pipe(fds) == -1)
+ goto clear;
- if (fd == -1)
- return;
+ vwl_data_offer_receive(sel->offer, chosen_mime, fds[1]);
- // Start reading the file descriptor returned
- clip_wl_receive_data(cbd, chosen_mime, fd);
+ close(fds[1]); // Close before we read data so that when the source client
+ // closes their end we receive an EOF.
- close(fd);
+ if (vwl_connection_flush(wayland_ct) >= 0)
+ clip_wl_receive_data(cbd, chosen_mime, fds[0]);
+
+ close(fds[0]);
+
+ return;
+clear:
+ clip_free_selection(cbd);
}
-/*
- * 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;
+vwl_data_source_listener_event_send(
+ void *data,
+ vwl_data_source_T *source UNUSED,
+ const char *mime_type,
+ int32_t fd
+)
+{
+ clip_wl_selection_T *sel = data;
+ Clipboard_T *cbd = clip_wl_get_cbd_from_selection(sel);
+ bool have_mime = false;
+ int motion_type;
+ long_u length;
+ char_u *string; // Will be reallocated to a bigger size if
+ // needed.
+ int offset = 0;
+ bool is_vim, is_vimenc;
+ size_t total = 0;
#ifndef HAVE_SELECT
- struct pollfd pfd
+ struct pollfd pfd;
- pfd.fd = fd,
- pfd.events = POLLOUT
+ pfd.fd = fd;
+ pfd.events = POLLOUT;
#else
fd_set wfds;
struct timeval tv;
FD_ZERO(&wfds);
FD_SET(fd, &wfds);
- tv.tv_sec = 0;
- tv.tv_usec = p_wtm * 1000;
#endif
- if (selection == WAYLAND_SELECTION_REGULAR)
- cbd = &clip_plus;
- else if (selection == WAYLAND_SELECTION_PRIMARY)
- cbd = &clip_star;
- else
- return;
- // Shouldn't happen unless there is a bug.
- if (!cbd->owned)
- return;
+ // Check if we actually have mime type
+ for (int i = 0; i < (int)ARRAY_LENGTH(supported_mimes); i++)
+ if (STRCMP(supported_mimes[i], mime_type) == 0)
+ {
+ have_mime = true;
+ break;
+ }
+
+ if (!have_mime)
+ goto exit;
+
+ // First byte sent is motion type for vim specific formats. For the vimenc
+ // format, after the first byte is the encoding type, which is null
+ // terminated.
+
+ is_vimenc = STRCMP(mime_type, VIMENC_ATOM_NAME) == 0;
+ is_vim = STRCMP(mime_type, VIM_ATOM_NAME) == 0;
+
+ if (is_vimenc)
+ offset += 2 + STRLEN(p_enc);
+ else if (is_vim)
+ offset += 1;
- // Get the current selection
clip_get_selection(cbd);
- motion_type = clip_convert_selection(&string, &length, cbd);
+ motion_type = clip_convert_selection_offset(&string, &length, offset, cbd);
if (motion_type < 0)
goto exit;
- if (STRCMP(mime_type, VIMENC_ATOM_NAME) == 0)
+ if (is_vimenc)
{
- did_vimenc = FALSE;
- did_motion_type = FALSE;
+ string[0] = (char_u)motion_type;
+ // strcpy copies the NUL terminator too
+ strcpy((char *)string + 1, (char *)p_enc);
}
- else if (STRCMP(mime_type, VIM_ATOM_NAME) == 0)
- did_motion_type = FALSE;
+ else if (is_vim)
+ string[0] = (char_u)motion_type;
+
- while ((total < (size_t)length || skip_len_check) &&
+ while (total < (size_t)length &&
#ifndef HAVE_SELECT
- poll(&pfd, 1, p_wtm) > 0)
+ poll(&pfd, 1, p_wtm) > 0)
#else
- select(fd + 1, NULL, &wfds, NULL, &tv) > 0)
+ ((tv.tv_sec = p_wtm / 1000, tv.tv_usec = (p_wtm % 1000) * 1000),
+ 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;
+ ssize_t w = write(fd, string + total, length - total);
-#ifdef HAVE_SELECT
- tv.tv_sec = 0;
- tv.tv_usec = p_wtm * 1000;
-#endif
+ if (w == -1)
+ break;
+ total += w;
}
-exit:
+
vim_free(string);
+exit:
+ close(fd);
}
-/*
- * 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)
+vwl_data_source_listener_event_cancelled(
+ void *data,
+ vwl_data_source_T *source UNUSED)
{
- if (selection == WAYLAND_SELECTION_REGULAR)
- clip_lose_selection(&clip_plus);
- else if (selection == WAYLAND_SELECTION_PRIMARY)
- clip_lose_selection(&clip_star);
+ clip_wl_selection_T *sel = data;
+ Clipboard_T *cbd = clip_wl_get_cbd_from_selection(sel);
+
+ clip_lose_selection(cbd);
+}
+
+static const vwl_data_source_listener_T vwl_data_source_listener = {
+ .send = vwl_data_source_listener_event_send,
+ .cancelled = vwl_data_source_listener_event_cancelled
+};
+
+ static void
+clip_wl_do_set_selection(void *data, uint32_t serial)
+{
+ clip_wl_selection_T *sel = data;
+ wayland_selection_T sel_type = clip_wl_get_selection_type(sel);
+
+ vwl_data_device_set_selection(sel->device, sel->source, serial, sel_type);
+
+ sel->own_success = (vwl_connection_roundtrip(wayland_ct) == OK);
}
/*
@@ -2658,36 +3330,81 @@ clip_wl_selection_cancelled(wayland_selection_T selection)
static int
clip_wl_own_selection(Clipboard_T *cbd)
{
- wayland_selection_T selection;
+ clip_wl_selection_T *sel = clip_wl_get_selection_from_cbd(cbd);
+ wayland_selection_T sel_type = clip_wl_get_selection_type(sel);
- if (cbd == &clip_star)
- selection = WAYLAND_SELECTION_PRIMARY;
- else if (cbd == &clip_plus)
- selection = WAYLAND_SELECTION_REGULAR;
- else
+ if (!sel->available || vwl_connection_roundtrip(wayland_ct) == FAIL)
return FAIL;
- return wayland_cb_own_selection(
- clip_wl_send_data,
- clip_wl_selection_cancelled,
- supported_mimes,
- sizeof(supported_mimes)/sizeof(*supported_mimes),
- selection);
+ if (sel->source != NULL)
+ {
+ if (sel_type == WAYLAND_SELECTION_PRIMARY)
+ // We already own the selection, ignore (only do this for primary
+ // selection). We don't re set the selection because then we would
+ // be setting the selection every time the user moves the visual
+ // selection cursor, which is messy and inefficient. Some
+ // applications like Google Chrome do it this way however.
+ return OK;
+ else if (sel_type == WAYLAND_SELECTION_REGULAR)
+ {
+ // Technically we don't need to do this as we already own the
+ // selection, however if a user yanks text a second time, the
+ // text yanked won't appear in their clipboard manager if they are
+ // using one.
+ //
+ // This can be unexpected behaviour for the user so its probably
+ // better to do it this way. Additionally other Wayland applications
+ // seem to set the selection every time.
+ vwl_data_source_destroy(sel->source);
+ }
+ else
+ // Shouldn't happen
+ return FAIL;
+ }
+
+ sel->source = vwl_data_device_manager_create_data_source(sel->manager);
+ vwl_data_source_add_listener(sel->source, &vwl_data_source_listener, sel);
+
+ // Advertise mime types
+ vwl_data_source_offer(sel->source, wayland_vim_special_mime);
+ for (int i = 0; i < (int)ARRAY_LENGTH(supported_mimes); i++)
+ vwl_data_source_offer(sel->source, supported_mimes[i]);
+
+ sel->own_success = false;
+#ifdef FEAT_WAYLAND_CLIPBOARD_FS
+ if (sel->requires_focus)
+ {
+ if (clip_wl_init_fs_surface(clip_wl.seat, clip_wl.fs_buffer,
+ clip_wl_do_set_selection, sel) == FAIL)
+ goto fail;
+ }
+ else
+#endif
+ clip_wl_do_set_selection(sel, 0);
+
+ if (!sel->own_success)
+ goto fail;
+
+ return OK;
+fail:
+ vwl_data_source_destroy(sel->source);
+ sel->source = NULL;
+ return FAIL;
}
/*
- * Disown the selection that cbd corresponds to. Note that the the cancelled
- * event is not sent when the data source is destroyed.
+ * Disown the selection that cbd corresponds to.
*/
static 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);
+ clip_wl_selection_T *sel = clip_wl_get_selection_from_cbd(cbd);
- /* wayland_cb_lose_selection(selection); */
+ if (!sel->available)
+ return;
+
+ vwl_data_source_destroy(sel->source);
+ sel->source = NULL;
}
/*
@@ -2699,17 +3416,20 @@ clip_wl_set_selection(Clipboard_T *cbd UNUSED)
{
}
-#if defined(USE_SYSTEM) && defined(PROTO)
+#if defined(USE_SYSTEM) || defined(PROTO)
/*
- * Return TRUE if we own the selection corresponding to cbd
+ * Return true if we own the selection corresponding to cbd or another client
+ * does.
*/
- static int
+ static bool
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);
+ clip_wl_selection_T *sel = clip_wl_get_selection_from_cbd(cbd);
+
+ if (vwl_connection_roundtrip(wayland_ct) == FAIL)
+ return false;
+
+ return sel->available && (sel->source != NULL || sel->offer != NULL);
}
#endif
@@ -2721,7 +3441,7 @@ clip_wl_owner_exists(Clipboard_T *cbd)
* depending on the order of values in str.
*/
static clipmethod_T
-get_clipmethod(char_u *str)
+get_clipmethod(char_u *str, bool *regular, bool *primary)
{
int len = (int)STRLEN(str) + 1;
char_u *buf = alloc(len);
@@ -2745,8 +3465,12 @@ get_clipmethod(char_u *str)
#endif
{
#ifdef FEAT_WAYLAND_CLIPBOARD
- if (wayland_cb_is_ready())
+ if (clip_wl.regular.available || clip_wl.primary.available)
+ {
method = CLIPMETHOD_WAYLAND;
+ *regular = clip_wl.regular.available;
+ *primary = clip_wl.primary.available;
+ }
#endif
}
}
@@ -2767,6 +3491,7 @@ get_clipmethod(char_u *str)
// won't be executed since xterm_dpy will be set to NULL.
xterm_update();
method = CLIPMETHOD_X11;
+ *regular = *primary = true;
}
#endif
}
@@ -2775,13 +3500,17 @@ get_clipmethod(char_u *str)
{
#ifdef FEAT_GUI
if (gui.in_use)
+ {
method = CLIPMETHOD_GUI;
+ *regular = *primary = true;
+ }
#endif
}
else if (STRCMP(buf, "other") == 0)
{
#if !defined(FEAT_XCLIPBOARD) && !defined(FEAT_WAYLAND_CLIPBOARD)
method = CLIPMETHOD_OTHER;
+ *regular = *primary = true;
#endif
}
else
@@ -2832,9 +3561,8 @@ clipmethod_to_str(clipmethod_T method)
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);
+ bool regular = false, primary = false;
+ clipmethod_T method = get_clipmethod(p_cpm, ®ular, &primary);
if (method == CLIPMETHOD_FAIL)
return e_invalid_argument;
@@ -2843,27 +3571,29 @@ choose_clipmethod(void)
if (method == CLIPMETHOD_GUI)
// We only interact with Wayland for the clipboard, we can just deinit
// everything.
- wayland_uninit_client();
+ wayland_uninit_connection();
#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)
+ else if (clipmethod == CLIPMETHOD_NONE && method != CLIPMETHOD_NONE)
{
- clip_init(TRUE);
- did_warn_clipboard = FALSE;
+ clip_init_single(&clip_plus, regular);
+ clip_init_single(&clip_star, primary);
+ clip_plus.did_warn = false;
+ clip_star.did_warn = false;
}
-
// Disown clipboard if we are switching to a new method
- if (clipmethod != CLIPMETHOD_NONE && method != clipmethod)
+ else if (clipmethod != CLIPMETHOD_NONE && method != clipmethod)
{
if (clip_star.owned)
clip_lose_selection(&clip_star);
if (clip_plus.owned)
clip_lose_selection(&clip_plus);
+ clip_init_single(&clip_plus, regular);
+ clip_init_single(&clip_star, primary);
}
clipmethod = method;
diff --git a/src/
config.h.in b/src/
config.h.in
index e8775d98c..983f186b8 100644
--- a/src/
config.h.in
+++ b/src/
config.h.in
@@ -12,6 +12,9 @@
/* Define unless no Wayland support found */
#undef HAVE_WAYLAND
+/* Define if you want focus stealing support with Wayland clipboard */
+#undef FEAT_WAYLAND_CLIPBOARD_FS
+
/* Define when terminfo support found */
#undef TERMINFO
diff --git a/src/
configure.ac b/src/
configure.ac
index 983c58224..6a641c2f2 100644
--- a/src/
configure.ac
+++ b/src/
configure.ac
@@ -2464,11 +2464,25 @@ AC_ARG_WITH(wayland,
AC_MSG_RESULT([yes])]))
if test "$with_wayland" = yes; then
-cppflags_save=$CPPFLAGS
-cflags_save=$CFLAGS
+ cppflags_save=$CPPFLAGS
+ cflags_save=$CFLAGS
+
AC_MSG_CHECKING(for wayland)
if "$PKG_CONFIG" --exists 'wayland-client'; then
AC_MSG_RESULT([yes])
+
+ AC_MSG_CHECKING(--enable-wayland-focus-steal argument)
+ AC_ARG_ENABLE(wayland-focus-steal,
+ [AS_HELP_STRING([--enable-wayland-focus-steal],
+ [Include focus stealing support for Wayland clipboard.])],
+ [enable_wayland_fs=$enableval],
+ enable_wayland_fs="yes")
+
+ AS_IF([test "$enable_wayland_fs" = "yes"],
+ [AC_MSG_RESULT([yes])
+ AC_DEFINE(FEAT_WAYLAND_CLIPBOARD_FS)],
+ [AC_MSG_RESULT([no])])
+
AC_DEFINE(HAVE_WAYLAND)
WAYLAND_CPPFLAGS=`$PKG_CONFIG --cflags-only-I wayland-client`
WAYLAND_CFLAGS=`$PKG_CONFIG --cflags-only-other wayland-client`
@@ -2478,15 +2492,20 @@ cflags_save=$CFLAGS
WAYLAND_SRC=" \
auto/wayland/wlr-data-control-unstable-v1.c \
auto/wayland/ext-data-control-v1.c \
- auto/wayland/xdg-shell.c \
- auto/wayland/primary-selection-unstable-v1.c \
wayland.c"
WAYLAND_OBJ=" \
objects/wlr-data-control-unstable-v1.o \
objects/ext-data-control-v1.o \
- objects/xdg-shell.o \
- objects/primary-selection-unstable-v1.o \
objects/wayland.o"
+
+ AS_IF([test "$enable_wayland_fs" = "yes"],
+ [AS_VAR_APPEND([WAYLAND_SRC], " \
+ auto/wayland/xdg-shell.c \
+ auto/wayland/primary-selection-unstable-v1.c")
+ AS_VAR_APPEND([WAYLAND_OBJ], " \
+ objects/xdg-shell.o \
+ objects/primary-selection-unstable-v1.o")])
+
AC_SUBST(WAYLAND_CPPFLAGS)
AC_SUBST(WAYLAND_CFLAGS)
AC_SUBST(WAYLAND_LIBS)
diff --git a/src/evalfunc.c b/src/evalfunc.c
index da5377799..a8d190638 100644
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -7616,6 +7616,13 @@ f_has(typval_T *argvars, typval_T *rettv)
1
#else
0
+#endif
+ },
+ {"wayland_focus_steal",
+#ifdef FEAT_WAYLAND_CLIPBOARD_FS
+ 1
+#else
+ 0
#endif
},
{"wildignore", 1},
diff --git a/src/feature.h b/src/feature.h
index 826adbe2b..d7db96cd5 100644
--- a/src/feature.h
+++ b/src/feature.h
@@ -928,6 +928,13 @@
# endif
#endif
+/*
+ * +wayland_focus_steal Focus stealing support for Wayland clipboard
+ */
+#if !defined(FEAT_WAYLAND_CLIPBOARD) && defined(FEAT_WAYLAND_CLIPBOARD_FS)
+# undef FEAT_WAYLAND_CLIPBOARD_FS
+#endif
+
/*
* +dnd Drag'n'drop support. Always used for the GTK+ GUI.
*/
diff --git a/src/globals.h b/src/globals.h
index 8d031028e..653690b00 100644
--- a/src/globals.h
+++ b/src/globals.h
@@ -2071,23 +2071,23 @@ EXTERN char_u showcmd_buf[SHOWCMD_BUFLEN];
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
+// Wayland display name for global connection (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;
+// Special mime type used to identify selection events that came from us setting
+// the selection. Is in format of "application/x-vim-instance-<pid>" where <pid>
+// is the PID of the Vim process. Set in main.c
+EXTERN char wayland_vim_special_mime[
+ sizeof("application/x-vim-instance-") + NUMBUFLEN - 1]; // Includes NUL
+
+// Don't connect to Wayland compositor if TRUE
+EXTERN int wayland_no_connect INIT(= FALSE);
#endif
diff --git a/src/main.c b/src/main.c
index c77454a26..50bc6f4f7 100644
--- a/src/main.c
+++ b/src/main.c
@@ -682,15 +682,18 @@ vim_main2(void)
if (!gui.in_use)
# endif
{
- if (wayland_init_client(wayland_display_name) == OK)
+ sprintf(wayland_vim_special_mime, "application/x-vim-instance-%ld",
+ mch_get_pid());
+
+ if (wayland_init_connection(wayland_display_name) == OK)
{
TIME_MSG("connected to Wayland display");
# ifdef FEAT_WAYLAND_CLIPBOARD
- if (wayland_cb_init((char*)p_wse) == OK)
+ if (clip_init_wayland() == OK)
TIME_MSG("setup Wayland clipboard");
- }
# endif
+ }
}
#endif
diff --git a/src/message.c b/src/message.c
index 35f57e8db..d5fc6b624 100644
--- a/src/message.c
+++ b/src/message.c
@@ -4166,17 +4166,37 @@ msg_advance(int col)
* Warn about missing Clipboard Support
*/
void
-msg_warn_missing_clipboard(void)
+msg_warn_missing_clipboard(bool plus UNUSED, bool star UNUSED)
{
- if (!global_busy && !did_warn_clipboard)
+#ifndef FEAT_CLIPBOARD
+ static bool did_warn;
+
+ if (!global_busy && !did_warn)
{
-#ifdef FEAT_CLIPBOARD
- msg(_("W23: Clipboard register not available, using register 0"));
-#else
msg(_("W24: Clipboard register not available. See :h W24"));
-#endif
- did_warn_clipboard = TRUE;
+ did_warn = true;
+ }
+#else
+ if (!global_busy)
+ {
+ if (plus && star && !clip_plus.did_warn && !clip_star.did_warn)
+ {
+ msg(_("W23: Clipboard register not available, using register 0"));
+ clip_plus.did_warn = true;
+ clip_star.did_warn = true;
+ }
+ else if (plus && !clip_plus.did_warn)
+ {
+ msg(_("W23: Clipboard register + not available, using register 0"));
+ clip_plus.did_warn = true;
+ }
+ else if (star && !clip_star.did_warn)
+ {
+ msg(_("W23: Clipboard register * not available, using register 0"));
+ clip_star.did_warn = true;
+ }
}
+#endif
}
#if defined(FEAT_CON_DIALOG) || defined(PROTO)
diff --git a/src/option.c b/src/option.c
index 7c5f4b99d..88da9c099 100644
--- a/src/option.c
+++ b/src/option.c
@@ -4774,7 +4774,7 @@ did_set_winwidth(optset_T *args UNUSED)
char *
did_set_wlsteal(optset_T *args UNUSED)
{
- wayland_cb_reload();
+ clip_reset_wayland();
return NULL;
}
diff --git a/src/option.h b/src/option.h
index 9e6aed355..77f3ceb3c 100644
--- a/src/option.h
+++ b/src/option.h
@@ -1144,7 +1144,7 @@ EXTERN long p_wmw; // 'winminwidth'
EXTERN long p_wiw; // 'winwidth'
#ifdef FEAT_WAYLAND
EXTERN char_u *p_wse; // 'wlseat'
-# ifdef FEAT_WAYLAND_CLIPBOARD
+# ifdef FEAT_WAYLAND_CLIPBOARD_FS
EXTERN int p_wst; // 'wlsteal'
# endif
EXTERN long p_wtm; // 'wltimeoutlen'
diff --git a/src/optiondefs.h b/src/optiondefs.h
index 572b7aff1..626823c16 100644
--- a/src/optiondefs.h
+++ b/src/optiondefs.h
@@ -3010,7 +3010,7 @@ static struct vimoption options[] =
#endif
SCTX_INIT},
{"wlsteal", "wst", P_BOOL|P_VI_DEF,
-#ifdef FEAT_WAYLAND_CLIPBOARD
+#ifdef FEAT_WAYLAND_CLIPBOARD_FS
(char_u *)&p_wst, PV_NONE, did_set_wlsteal, NULL,
{(char_u *)FALSE, (char_u *)0L}
#else
diff --git a/src/optionstr.c b/src/optionstr.c
index 628842ef1..55cde5716 100644
--- a/src/optionstr.c
+++ b/src/optionstr.c
@@ -3689,7 +3689,7 @@ did_set_wlseat(optset_T *args UNUSED)
#ifdef FEAT_WAYLAND_CLIPBOARD
// If there isn't any seat named 'wlseat', then let the Wayland clipboard be
// unavailable. Ignore errors returned.
- wayland_cb_reload();
+ clip_reset_wayland();
#endif
return NULL;
diff --git a/src/os_unix.c b/src/os_unix.c
index b2bd8adb3..02116ee8c 100644
--- a/src/os_unix.c
+++ b/src/os_unix.c
@@ -5740,7 +5740,7 @@ mch_call_shell_fork(
#ifdef FEAT_WAYLAND
// Handle Wayland events such as sending data as the source
// client.
- wayland_client_update();
+ wayland_update();
#endif
}
finished:
@@ -5814,7 +5814,7 @@ finished:
#ifdef FEAT_WAYLAND
// Handle Wayland events such as sending data as the source
// client.
- wayland_client_update();
+ wayland_update();
#endif
// Wait for 1 to 10 msec. 1 is faster but gives the child
@@ -6666,6 +6666,9 @@ RealWaitForChar(int fd, long msec, int *check_for_gpm UNUSED, int *interrupted)
int mzquantum_used = FALSE;
# endif
#endif
+#ifdef FEAT_WAYLAND
+ int wayland_fd = -1;
+#endif
#ifndef HAVE_SELECT
// each channel may use in, out and err
struct pollfd fds[7 + 3 * MAX_OPEN_CHANNELS];
@@ -6709,11 +6712,11 @@ RealWaitForChar(int fd, long msec, int *check_for_gpm UNUSED, int *interrupted)
}
# endif
-# ifdef FEAT_WAYLAND_CLIPBOARD
- if (wayland_may_restore_connection())
+# ifdef FEAT_WAYLAND
+ if ((wayland_fd = wayland_prepare_read()) >= 0)
{
wayland_idx = nfd;
- fds[nfd].fd = wayland_display_fd;
+ fds[nfd].fd = wayland_fd;
fds[nfd].events = POLLIN;
nfd++;
}
@@ -6777,13 +6780,9 @@ RealWaitForChar(int fd, long msec, int *check_for_gpm UNUSED, int *interrupted)
}
# 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();
+# ifdef FEAT_WAYLAND
+ if (wayland_idx >= 0)
+ wayland_poll_check(fds[wayland_idx].revents);
# endif
# ifdef FEAT_XCLIPBOARD
@@ -6876,14 +6875,13 @@ select_eintr:
}
# endif
-# ifdef FEAT_WAYLAND_CLIPBOARD
-
- if (wayland_may_restore_connection())
+# ifdef FEAT_WAYLAND
+ if ((wayland_fd = wayland_prepare_read()) >= 0)
{
- FD_SET(wayland_display_fd, &rfds);
+ FD_SET(wayland_fd, &rfds);
- if (maxfd < wayland_display_fd)
- maxfd = wayland_display_fd;
+ if (maxfd < wayland_fd)
+ maxfd = wayland_fd;
}
# endif
@@ -6983,13 +6981,9 @@ select_eintr:
socket_server_uninit();
# 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();
+# ifdef FEAT_WAYLAND
+ if (wayland_fd != -1)
+ wayland_select_check(ret > 0 && FD_ISSET(wayland_fd, &rfds));
# endif
# ifdef FEAT_XCLIPBOARD
diff --git a/src/po/vim.pot b/src/po/vim.pot
index d5cb0795d..20571200d 100644
--- a/src/po/vim.pot
+++ b/src/po/vim.pot
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Vim
"
"Report-Msgid-Bugs-To:
vim...@vim.org
"
-"POT-Creation-Date: 2025-09-21 18:48+0000
"
+"POT-Creation-Date: 2025-09-22 19:04+0000
"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE
"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>
"
"Language-Team: LANGUAGE <
L...@li.org>
"
@@ -2220,10 +2220,16 @@ msgstr ""
msgid " SPACE/d/j: screen/page/line down, b/u/k: up, q: quit "
msgstr ""
+msgid "W24: Clipboard register not available. See :h W24"
+msgstr ""
+
msgid "W23: Clipboard register not available, using register 0"
msgstr ""
-msgid "W24: Clipboard register not available. See :h W24"
+msgid "W23: Clipboard register + not available, using register 0"
+msgstr ""
+
+msgid "W23: Clipboard register * not available, using register 0"
msgstr ""
msgid "Question"
diff --git a/src/proto/
clipboard.pro b/src/proto/
clipboard.pro
index 9ccfea90a..5727aa8b7 100644
--- a/src/proto/
clipboard.pro
+++ b/src/proto/
clipboard.pro
@@ -35,6 +35,9 @@ int clip_convert_selection(char_u **str, long_u *len, Clipboard_T *cbd);
int may_get_selection(int regname);
void may_set_selection(void);
void adjust_clip_reg(int *rp);
+int clip_init_wayland(void);
+void clip_uninit_wayland(void);
+int clip_reset_wayland(void);
char *choose_clipmethod(void);
void ex_clipreset(exarg_T *eap);
/* vim: set ft=c : */
diff --git a/src/proto/
message.pro b/src/proto/
message.pro
index 34b381866..5b6c80b4d 100644
--- a/src/proto/
message.pro
+++ b/src/proto/
message.pro
@@ -74,7 +74,7 @@ void give_warning(char_u *message, int hl);
void give_warning_with_source(char_u *message, int hl, int with_source);
void give_warning2(char_u *message, char_u *a1, int hl);
void msg_advance(int col);
-void msg_warn_missing_clipboard(void);
+void msg_warn_missing_clipboard(bool plus, bool star);
int do_dialog(int type, char_u *title, char_u *message, char_u *buttons, int dfltbutton, char_u *textfield, int ex_cmd);
int vim_dialog_yesno(int type, char_u *title, char_u *message, int dflt);
int vim_dialog_yesnocancel(int type, char_u *title, char_u *message, int dflt);
diff --git a/src/proto/
wayland.pro b/src/proto/
wayland.pro
index a1f327dbc..113bf82e4 100644
--- a/src/proto/
wayland.pro
+++ b/src/proto/
wayland.pro
@@ -1,16 +1,27 @@
/* wayland.c */
-int wayland_init_client(const char *display);
-void wayland_uninit_client(void);
-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);
+int vwl_connection_flush(vwl_connection_T *self);
+int vwl_connection_dispatch(vwl_connection_T *self);
+int vwl_connection_roundtrip(vwl_connection_T *self);
+int wayland_init_connection(const char *display);
+void wayland_uninit_connection(void);
+int wayland_prepare_read(void);
+int wayland_update(void);
+void wayland_poll_check(int revents);
+void wayland_select_check(bool is_set);
void ex_wlrestore(exarg_T *eap);
+vwl_seat_T *vwl_connection_get_seat(vwl_connection_T *ct, const char *label);
+struct wl_keyboard *vwl_seat_get_keyboard(vwl_seat_T *seat);
+vwl_data_device_manager_T *vwl_connection_get_data_device_manager(vwl_connection_T *self,wayland_selection_T req_sel, unsigned int *supported);
+vwl_data_device_T *vwl_data_device_manager_get_data_device(vwl_data_device_manager_T *self,vwl_seat_T *seat);
+vwl_data_source_T *vwl_data_device_manager_create_data_source(vwl_data_device_manager_T *self);
+void vwl_data_device_destroy(vwl_data_device_T *self);
+void vwl_data_source_destroy(vwl_data_source_T *self);
+void vwl_data_offer_destroy(vwl_data_offer_T *self);
+void vwl_data_device_manager_discard(vwl_data_device_manager_T *self);
+void vwl_data_device_add_listener(vwl_data_device_T *self,const vwl_data_device_listener_T *listener, void *data);
+void vwl_data_source_add_listener(vwl_data_source_T *self,const vwl_data_source_listener_T *listener, void *data);
+void vwl_data_offer_add_listener(vwl_data_offer_T *self,const vwl_data_offer_listener_T *listener, void *data);
+void vwl_data_device_set_selection(vwl_data_device_T *self, vwl_data_source_T *source, uint32_t serial, wayland_selection_T selection);
+void vwl_data_source_offer(vwl_data_source_T *self, const char *mime_type);
+void vwl_data_offer_receive(vwl_data_offer_T *self, const char *mime_type, int32_t fd);
/* vim: set ft=c : */
diff --git a/src/register.c b/src/register.c
index 818166df5..a866321a3 100644
--- a/src/register.c
+++ b/src/register.c
@@ -204,7 +204,7 @@ valid_yank_reg(
else if (regname == '*' || regname == '+')
{
// Warn about missing clipboard support once
- msg_warn_missing_clipboard();
+ msg_warn_missing_clipboard(true, true);
return FALSE;
}
#endif
@@ -1189,7 +1189,7 @@ op_yank(oparg_T *oap, int deleting, int mess)
(!clip_plus.available && oap->regname == '+'))
{
oap->regname = 0;
- msg_warn_missing_clipboard();
+ msg_warn_missing_clipboard(!clip_plus.available, !clip_star.available);
}
#endif
diff --git a/src/structs.h b/src/structs.h
index e28c38340..981db0e7c 100644
--- a/src/structs.h
+++ b/src/structs.h
@@ -5239,20 +5239,26 @@ struct cellsize {
#ifdef FEAT_WAYLAND
+typedef struct vwl_connection_S vwl_connection_T;
+typedef struct vwl_seat_S vwl_seat_T;
+
+# ifdef FEAT_WAYLAND_CLIPBOARD
+
+typedef struct vwl_data_offer_S vwl_data_offer_T;
+typedef struct vwl_data_source_S vwl_data_source_T;
+typedef struct vwl_data_device_S vwl_data_device_T;
+typedef struct vwl_data_device_manager_S vwl_data_device_manager_T;
+
+typedef struct vwl_data_device_listener_S vwl_data_device_listener_T;
+typedef struct vwl_data_source_listener_S vwl_data_source_listener_T;
+typedef struct vwl_data_offer_listener_S vwl_data_offer_listener_T;
+
// Wayland selections
typedef enum {
- WAYLAND_SELECTION_NONE = 0x0,
- WAYLAND_SELECTION_REGULAR = 0x1,
- WAYLAND_SELECTION_PRIMARY = 0x2,
+ WAYLAND_SELECTION_NONE = 0,
+ WAYLAND_SELECTION_REGULAR = 1 << 0,
+ WAYLAND_SELECTION_PRIMARY = 1 << 1,
} 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
+# endif
+#endif
diff --git a/src/testdir/test_wayland.vim b/src/testdir/test_wayland.vim
index 818574964..155172a0f 100644
--- a/src/testdir/test_wayland.vim
+++ b/src/testdir/test_wayland.vim
@@ -21,12 +21,17 @@ let s:old_wayland_display = $WAYLAND_DISPLAY
" every test function
func s:PreTest()
let $WAYLAND_DISPLAY=s:global_wayland_display
- exe 'wlrestore ' .. $WAYLAND_DISPLAY
+ " Always reconnect so we have a clean state each time and clear both
+ " selections.
+ call system('wl-copy -c')
+ call system('wl-copy -p -c')
+ exe 'wlrestore! ' .. $WAYLAND_DISPLAY
set cpm=wayland
endfunc
func s:SetupFocusStealing()
+ CheckFeature wayland_focus_steal
if !executable('wayland-info')
throw "Skipped: wayland-info program not available"
endif
@@ -35,7 +40,7 @@ func s:SetupFocusStealing()
" seat, so we must use the user's existing Wayland session if they are in one.
let $WAYLAND_DISPLAY = s:old_wayland_display
- exe 'wlrestore ' .. $WAYLAND_DISPLAY
+ exe 'wlrestore! ' .. $WAYLAND_DISPLAY
" Check if we have keyboard capability for seat
if system("wayland-info -i wl_seat | grep capabilities") !~? "keyboard"
@@ -50,16 +55,14 @@ func s:UnsetupFocusStealing()
unlet $VIM_WAYLAND_FORCE_FS
endfunc
-" Need X connection for tests that use client server communication
-func s:CheckXConnection()
- CheckFeature x11
- try
- call remote_send('xxx', '')
- catch /^Vim\%(( \+)\)\=:E240:/ " not possible to start a remote server
- throw 'Skipped: No connection to the X server possible'
- catch
- " ignore other errors
- endtry
+func s:CheckClientserver()
+ CheckFeature clientserver
+
+ if has('socketserver') && !has('x11')
+ if v:servername == ""
+ call remote_startserver('VIMSOCKETSERVER')
+ endif
+ endif
endfunc
func s:EndRemoteVim(name, job)
@@ -76,11 +79,7 @@ endfunc
func Test_wayland_startup()
call s:PreTest()
- call s:CheckXConnection()
-
- if v:servername == ""
- call remote_startserver('VIMSOCKETSERVER')
- endif
+ call s:CheckClientserver()
let l:name = 'WLVIMTEST'
let l:cmd = GetVimCommand() .. ' --servername ' .. l:name
@@ -168,18 +167,12 @@ func Test_wayland_connection_lost()
call EndWaylandCompositor(l:wayland_display)
call assert_equal('', getreg('+'))
- call assert_fails('put +', 'E353:')
- call assert_fails('yank +', 'E1548:')
endfunc
" Basic paste tests
func Test_wayland_paste()
call s:PreTest()
- " Prevent 'Register changed while using it' error, guessing this works because
- " it makes Vim lose the selection?
- wlrestore!
-
" Regular selection
new
@@ -218,8 +211,6 @@ endfunc
func Test_wayland_yank()
call s:PreTest()
- wlrestore!
-
new
call setline(1, 'testing')
@@ -264,7 +255,8 @@ func Test_wayland_mime_types_correct()
\ 'text/plain',
\ 'UTF8_STRING',
\ 'STRING',
- \ 'TEXT'
+ \ 'TEXT',
+ \ 'application/x-vim-instance-' .. getpid()
\ ]
call setreg('+', 'text', 'c')
@@ -359,7 +351,7 @@ endfunc
" Test if autoselect option in 'clipboard' works properly for Wayland
func Test_wayland_autoselect_works()
call s:PreTest()
- call s:CheckXConnection()
+ call s:CheckClientserver()
let l:lines =<< trim END
set cpm=wayland
@@ -375,10 +367,6 @@ func Test_wayland_autoselect_works()
call writefile(l:lines, 'Wltester', 'D')
- if v:servername == ""
- call remote_startserver('VIMSOCKETSERVER')
- endif
-
let l:name = 'WLVIMTEST'
let l:cmd = GetVimCommand() .. ' -S Wltester --servername ' .. l:name
let l:job = job_start(cmd, {'stoponexit': 'kill', 'out_io': 'null'})
@@ -407,24 +395,13 @@ func Test_wayland_autoselect_works()
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
+ call s:EndRemoteVim(l:name, l:job)
endfunc
" Check if the -Y flag works properly
func Test_no_wayland_connect_cmd_flag()
call s:PreTest()
- call s:CheckXConnection()
-
- if v:servername == ""
- call remote_startserver('VIMSOCKETSERVER')
- endif
+ call s:CheckClientserver()
let l:name = 'WLFLAGVIMTEST'
let l:cmd = GetVimCommand() .. ' -Y --servername ' .. l:name
@@ -448,25 +425,13 @@ func Test_no_wayland_connect_cmd_flag()
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
+ call s:EndRemoteVim(l:name, l:job)
endfunc
" Test behaviour when we do something like suspend Vim
func Test_wayland_become_inactive()
call s:PreTest()
- call s:CheckXConnection()
-
- if v:servername == ""
- call remote_startserver('VIMSOCKETSERVER')
- endif
+ call s:CheckClientserver()
let l:name = 'WLLOSEVIMTEST'
let l:cmd = GetVimCommand() .. ' --servername ' .. l:name
@@ -488,15 +453,7 @@ func Test_wayland_become_inactive()
call WaitForAssert({-> assert_equal("Nothing is copied
",
\ 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
+ call s:EndRemoteVim(l:name, l:job)
endfunc
" Test wlseat option
@@ -527,6 +484,7 @@ endfunc
" Test focus stealing
func Test_wayland_focus_steal()
+ CheckFeature wayland_focus_steal
call s:PreTest()
call s:SetupFocusStealing()
@@ -552,17 +510,13 @@ endfunc
" Test when environment is not suitable for Wayland
func Test_wayland_bad_environment()
call s:PreTest()
- call s:CheckXConnection()
+ call s:CheckClientserver()
unlet $WAYLAND_DISPLAY
let l:old = $XDG_RUNTIME_DIR
unlet $XDG_RUNTIME_DIR
- if v:servername == ""
- call remote_startserver('VIMSOCKETSERVER')
- endif
-
let l:name = 'WLVIMTEST'
let l:cmd = GetVimCommand() .. ' --servername ' .. l:name
let l:job = job_start(cmd, {
@@ -576,15 +530,7 @@ func Test_wayland_bad_environment()
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
+ call s:EndRemoteVim(l:name, l:job)
let $XDG_RUNTIME_DIR = l:old
endfunc
@@ -611,7 +557,11 @@ func Test_wayland_lost_selection()
call assert_equal('regular', getreg('+'))
call assert_equal('primary', getreg('*'))
- " Test focus stealing
+endfunc
+
+" Same as above but for the focus stealing method
+func Test_wayland_lost_selection_focus_steal()
+ call s:PreTest()
call s:SetupFocusStealing()
call setreg('+', 'regular')
@@ -624,7 +574,7 @@ func Test_wayland_lost_selection()
call system('wl-copy -p overwrite')
call assert_equal('overwrite', getreg('+'))
- call assert_equal('overwrite', getreg('*'))
+ call assert_equal('overwrite-primary', getreg('*'))
call setreg('+', 'regular')
call setreg('*', 'primary')
@@ -635,14 +585,14 @@ func Test_wayland_lost_selection()
call s:UnsetupFocusStealing()
endfunc
-" Test when there are no supported mime types for the selecftion
+" Test when there are no supported mime types for the selection
func Test_wayland_no_mime_types_supported()
call s:PreTest()
- wlrestore!
+ call system('wl-copy tester')
+ call assert_equal('tester', getreg('+'))
call system('wl-copy -t image/png testing')
-
call assert_equal('', getreg('+'))
call assert_fails('put +', 'E353:')
endfunc
@@ -651,28 +601,17 @@ endfunc
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
+ let l:file = tempname()
+ let l:contents = repeat('c', 1000000) " ~ 1 MB
- put! +
+ call writefile([l:contents], l:file, 'b')
+ call system('cat ' .. l:file .. ' | wl-copy -t TEXT')
- write
+ call assert_equal(l:contents, getreg('+'))
- call system('truncate -s -1 data_file_cmp') " Remove newline at the end
- call system('cmp --silent data_file data_file_cmp')
- call assert_equal(0, v:shell_error)
+ call setreg('+', l:contents, 'c')
- 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!
+ call assert_equal(l:contents, system('wl-paste -n -t TEXT'))
endfunc
" vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/version.c b/src/version.c
index efea7d85f..911b91399 100644
--- a/src/version.c
+++ b/src/version.c
@@ -658,6 +658,11 @@ static char *(features[]) =
"+wayland_clipboard",
#else
"-wayland_clipboard",
+#endif
+#ifdef FEAT_WAYLAND_CLIPBOARD_FS
+ "+wayland_focus_steal",
+#else
+ "-wayland_focus_steal",
#endif
"+wildignore",
"+wildmenu",
@@ -724,6 +729,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
+/**/
+ 1784,
/**/
1783,
/**/
diff --git a/src/vim.h b/src/vim.h
index dd4e782b3..3c4dacb44 100644
--- a/src/vim.h
+++ b/src/vim.h
@@ -2364,6 +2364,8 @@ typedef struct
# ifdef FEAT_GUI_HAIKU
// No clipboard at the moment. TODO?
# endif
+ // If we've already warned about missing/unavailable clipboard
+ bool did_warn;
} Clipboard_T;
#else
typedef int Clipboard_T; // This is required for the prototypes.
diff --git a/src/wayland.c b/src/wayland.c
index 5aa8ec17e..33c10987f 100644
--- a/src/wayland.c
+++ b/src/wayland.c
@@ -8,485 +8,205 @@
*/
/*
- * wayland.c: Stuff related to Wayland
+ * wayland.c: Stuff related to Wayland. Functions that prefixed with "vwl_"
+ * handle/provide abstractions and building blocks to create more
+ * complex things. The "wayland_" functions handle the global
+ * Wayland connection.
+ *
+ * At the end of this file, there are a bunch of macro definitions
+ * that abstract away all the different protocols for the clipboard
+ * we need to support under one single interface. This is then used
+ * by clipboard.c to implement the Wayland clipboard functionality.
+ *
+ * The clipboard functionality monitors the Wayland display at all
+ * times, and saves new selections/offers as events come in. When we
+ * want retrieve the selection, the currently saved data offer is
+ * used from the respective data device.
+ *
+ * The focus stealing code is implemented in clipboard.c, and is
+ * based off of wl-clipboard's implementation. The idea using of
+ * extensive macros to reduce boilerplate code also comes from
+ * wl-clipboard as well. The project page for wl-clipboard can be
+ * found here:
https://github.com/bugaevc/wl-clipboard
*/
#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 capabilities 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;
+#include "wayland.h"
-#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
+vwl_connection_T *wayland_ct;
+bool is_reading = false;
/*
* 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
+vwl_connection_flush(vwl_connection_T *self)
{
- int ret;
-
#ifndef HAVE_SELECT
struct pollfd fds;
- fds.fd = display->fd;
- fds.events = POLLOUT;
+ fds.fd = self->display.fd;
+ fds.events = POLLOUT;
#else
fd_set wfds;
struct timeval tv;
FD_ZERO(&wfds);
- FD_SET(display->fd, &wfds);
-
- tv.tv_sec = p_wtm / 1000;
- tv.tv_usec = (p_wtm % 1000) * 1000;
+ FD_SET(self->display.fd, &wfds);
#endif
- if (display->proxy == NULL)
+ if (self->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)
+ while (true)
{
+ int ret = wl_display_flush(self->display.proxy);
+
+ if (ret == -1 && errno == EAGAIN)
+ {
#ifndef HAVE_SELECT
- if (poll(&fds, 1, p_wtm) <= 0)
+ if (poll(&fds, 1, p_wtm) <= 0)
#else
- if (select(display->fd + 1, NULL, &wfds, NULL, &tv) <= 0)
-#endif
+ tv.tv_sec = p_wtm / 1000;
+ tv.tv_usec = (p_wtm % 1000) * 1000;
+ if (select(self->display.fd + 1, NULL, &wfds, NULL, &tv) <= 0)
return FAIL;
-#ifdef HAVE_SELECT
- tv.tv_sec = 0;
- tv.tv_usec = p_wtm * 1000;
#endif
+ }
+ else if (ret == -1)
+ return FAIL;
+ else
+ break;
}
- // Return FAIL on error or timeout
- if ((errno != 0 && errno != EAGAIN) || ret == -1)
- return FAIL;
return OK;
}
+/*
+ * Like wl_display_roundtrip but polls the display fd with a timeout. Returns
+ * number of events dispatched on success else -1 on failure.
+ */
+ int
+vwl_connection_dispatch(vwl_connection_T *self)
+{
+#ifndef HAVE_SELECT
+ struct pollfd fds;
+
+ fds.fd = self->display.fd;
+ fds.events = POLLIN;
+#else
+ fd_set rfds;
+ struct timeval tv;
+
+ FD_ZERO(&rfds);
+ FD_SET(self->display.fd, &rfds);
+#endif
+
+ if (self->display.proxy == NULL)
+ return -1;
+
+ while (wl_display_prepare_read(self->display.proxy) == -1)
+ // Dispatch any queued events so that we can start reading
+ if (wl_display_dispatch_pending(self->display.proxy) == -1)
+ return -1;
+
+ // Send any requests before we starting blocking to read display fd
+ if (vwl_connection_flush(self) == FAIL)
+ {
+ wl_display_cancel_read(self->display.proxy);
+ return -1;
+ }
+
+ // Poll until there is data to read from the display fd.
+#ifndef HAVE_SELECT
+ if (poll(&fds, 1, p_wtm) <= 0)
+#else
+ tv.tv_sec = p_wtm / 1000;
+ tv.tv_usec = (p_wtm % 1000) * 1000;
+ if (select(self->display.fd + 1, &rfds, NULL, NULL, &tv) <= 0)
+#endif
+ {
+ wl_display_cancel_read(self->display.proxy);
+ return -1;
+ }
+
+ // Read events into the queue
+ if (wl_display_read_events(self->display.proxy) == -1)
+ // No need to cancel
+ return -1;
+
+ // Dispatch those events (call the handlers associated for each event)
+ return wl_display_dispatch_pending(self->display.proxy);
+}
+
/*
* Called when compositor is done processing requests/events.
*/
static void
-vwl_callback_done(void *data, struct wl_callback *callback,
- uint32_t cb_data UNUSED)
+vwl_callback_event_done(void *data, struct wl_callback *callback,
+ uint32_t callback_data UNUSED)
{
- *((int*)data) = TRUE;
+ *((bool*)data) = true;
wl_callback_destroy(callback);
}
+static const struct wl_callback_listener vwl_callback_listener = {
+ .done = vwl_callback_event_done
+};
+
/*
* Like wl_display_roundtrip but polls the display fd with a timeout. Returns
* FAIL on failure and OK on success.
*/
- static int
-vwl_display_roundtrip(vwl_display_T *display)
+ int
+vwl_connection_roundtrip(vwl_connection_T *self)
{
struct wl_callback *callback;
- int ret, done = FALSE;
- struct timeval start, now;
+ int ret;
+ bool done = false;
+#ifdef ELAPSED_FUNC
+ elapsed_T start_tv;
+#endif
- if (display->proxy == NULL)
+ if (self->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);
+ callback = wl_display_sync(self->display.proxy);
if (callback == NULL)
return FAIL;
wl_callback_add_listener(callback, &vwl_callback_listener, &done);
- gettimeofday(&start, NULL);
+#ifdef ELAPSED_FUNC
+ ELAPSED_INIT(start_tv);
+#endif
// Wait till we get the done event (which will set `done` to TRUE), unless
// we timeout
- while (TRUE)
+ while (true)
{
- ret = vwl_display_dispatch(display);
+ ret = vwl_connection_dispatch(self);
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)
+#ifdef ELAPSED_FUNC
+ if (ELAPSED_FUNC(start_tv) >= p_wtm)
{
ret = -1;
break;
}
+#endif
}
if (ret == -1)
@@ -499,94 +219,6 @@ vwl_display_roundtrip(vwl_display_T *display)
return OK;
}
-/*
- * Like wl_display_roundtrip but polls the display fd with a timeout. Returns
- * number of events dispatched on success else -1 on failure.
- */
- static int
-vwl_display_dispatch(vwl_display_T *display)
-{
-#ifndef HAVE_SELECT
- struct pollfd fds;
-
- fds.fd = display->fd;
- fds.events = POLLIN;
-#else
- fd_set rfds;
- struct timeval tv;
-
- FD_ZERO(&rfds);
- FD_SET(display->fd, &rfds);
-
- tv.tv_sec = p_wtm / 1000;
- tv.tv_usec = (p_wtm % 1000) * 1000;
-#endif
-
- if (display->proxy == NULL)
- return -1;
-
- while (wl_display_prepare_read(display->proxy) == -1)
- // Dispatch any queued events so that we can start reading
- if (wl_display_dispatch_pending(display->proxy) == -1)
- return -1;
-
- // Send any requests before we starting blocking to read display fd
- if (vwl_display_flush(display) == FAIL)
- {
- wl_display_cancel_read(display->proxy);
- return -1;
- }
-
- // Poll until there is data to read from the display fd.
-#ifndef HAVE_SELECT
- if (poll(&fds, 1, p_wtm) <= 0)
-#else
- if (select(display->fd + 1, &rfds, NULL, NULL, &tv) <= 0)
-#endif
- {
- wl_display_cancel_read(display->proxy);
- return -1;
- }
-
- // Read events into the queue
- if (wl_display_read_events(display->proxy) == -1)
- return -1;
-
- // Dispatch those events (call the handlers associated for each event)
- return wl_display_dispatch_pending(display->proxy);
-}
-
-/*
- * Same as vwl_display_dispatch but poll/select is never called. This is useful
- * is poll/select was already called before or if you just want to dispatch any
- * events that happen to be waiting to be dispatched on the display fd.
- */
- static int
-vwl_display_dispatch_any(vwl_display_T *display)
-{
- if (display->proxy == NULL)
- return -1;
-
- while (wl_display_prepare_read(display->proxy) == -1)
- // Dispatch any queued events so that we can start reading
- if (wl_display_dispatch_pending(display->proxy) == -1)
- return -1;
-
- // Send any requests before we starting blocking to read display fd
- if (vwl_display_flush(display) == FAIL)
- {
- wl_display_cancel_read(display->proxy);
- return -1;
- }
-
- // Read events into the queue
- if (wl_display_read_events(display->proxy) == -1)
- return -1;
-
- // Dispatch those events (call the handlers associated for each event)
- return wl_display_dispatch_pending(display->proxy);
-}
-
/*
* Redirect libwayland logging to use ch_log + emsg instead.
*/
@@ -616,210 +248,128 @@ vwl_log_handler(const char *fmt, va_list args)
}
/*
- * 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 success 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.
+ * Callback for seat text label/name
*/
static void
-vwl_disconnect_display(void)
+wl_seat_listener_event_name(
+ void *data,
+ struct wl_seat *seat_proxy UNUSED,
+ const char *name)
{
+ vwl_seat_T *seat = data;
- 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;
- }
+ seat->label = (char *)vim_strsave((char_u *)name);
}
/*
- * Tells the compositor we are still responsive.
+ * Callback for seat capabilities
*/
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)
+wl_seat_listener_event_capabilities(
+ void *data,
+ struct wl_seat *seat_proxy UNUSED,
+ uint32_t capabilities)
{
- // 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;
+ vwl_seat_T *seat = data;
-#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;
+ seat->capabilities = capabilities;
}
-#define SET_GOBJECT(object, min_ver) \
- do { \
- chosen_interface = &object##_interface; \
- object_member = (void*)&vwl_gobjects.object; \
- min_version = min_ver; \
- } while (0)
+static const struct wl_seat_listener wl_seat_listener = {
+ .name = wl_seat_listener_event_name,
+ .capabilities = wl_seat_listener_event_capabilities
+};
+
+static void vwl_seat_destroy(vwl_seat_T *self);
/*
* 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,
+wl_registry_listener_event_global(
+ void *data,
+ struct wl_registry *registry,
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;
+ vwl_connection_T *ct = data;
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);
+ struct wl_seat *seat_proxy = wl_registry_bind(registry, name,
+ &wl_seat_interface, version > 5 ? 5 : version);
+ vwl_seat_T *seat;
- else if (STRCMP(interface,
wl_data_device_manager_interface.name) == 0)
- SET_GOBJECT(wl_data_device_manager, 1);
+ if (seat_proxy == NULL)
+ return;
- else if (STRCMP(interface,
wl_shm_interface.name) == 0)
- SET_GOBJECT(wl_shm, 1);
+ seat = ALLOC_CLEAR_ONE(vwl_seat_T);
- else if (STRCMP(interface,
wl_compositor_interface.name) == 0)
- SET_GOBJECT(wl_compositor, 2);
+ if (seat == NULL || ga_grow(&ct->gobjects.seats, 1) == FAIL)
+ {
+ vwl_seat_destroy(seat);
+ return;
+ }
- else if (STRCMP(interface,
xdg_wm_base_interface.name) == 0)
- SET_GOBJECT(xdg_wm_base, 1);
+ seat->proxy = seat_proxy;
+ wl_seat_add_listener(seat_proxy, &wl_seat_listener, seat);
- else if (STRCMP(interface,
-
zwp_primary_selection_device_manager_v1_interface.name) == 0)
- SET_GOBJECT(zwp_primary_selection_device_manager_v1, 1);
-#endif
+ if (vwl_connection_roundtrip(ct) == FAIL || seat->label == NULL)
+ {
+ vwl_seat_destroy(seat);
+ return;
+ }
- if (chosen_interface == NULL || version < min_version)
- return;
+ ((vwl_seat_T **)ct->gobjects.seats.ga_data)[ct->gobjects.seats.ga_len++]
+ = seat;
+ }
+#ifdef FEAT_WAYLAND_CLIPBOARD
+ else if (STRCMP(interface,
zwlr_data_control_manager_v1_interface.name) == 0)
+ ct->gobjects.zwlr_data_control_manager_v1 =
+ wl_registry_bind(registry, name,
+ &zwlr_data_control_manager_v1_interface,
+ version > 2 ? 2 : version);
- proxy = wl_registry_bind(vwl_display.registry.proxy, name, chosen_interface,
- version);
+ else if (STRCMP(interface,
ext_data_control_manager_v1_interface.name) == 0)
+ ct->gobjects.ext_data_control_manager_v1 =
+ wl_registry_bind(registry, name,
+ &ext_data_control_manager_v1_interface, 1);
+
+# ifdef FEAT_WAYLAND_CLIPBOARD_FS
+ else if (p_wst)
+ {
+ if (STRCMP(interface,
wl_data_device_manager_interface.name) == 0)
+ ct->gobjects.wl_data_device_manager =
+ wl_registry_bind(registry, name,
+ &wl_data_device_manager_interface, 1);
+
+ else if (STRCMP(interface,
wl_shm_interface.name) == 0)
+ ct->gobjects.wl_shm =
+ wl_registry_bind(registry, name,
+ &wl_shm_interface, 1);
+
+ else if (STRCMP(interface,
wl_compositor_interface.name) == 0)
+ ct->gobjects.wl_compositor =
+ wl_registry_bind(registry, name,
+ &wl_compositor_interface, 1);
+
+ else if (STRCMP(interface,
xdg_wm_base_interface.name) == 0)
+ ct->gobjects.xdg_wm_base =
+ wl_registry_bind(registry, name,
+ &xdg_wm_base_interface, 1);
+
+ else if (STRCMP(interface,
+
zwp_primary_selection_device_manager_v1_interface.name)
+ == 0)
+ ct->gobjects.zwp_primary_selection_device_manager_v1 =
+ wl_registry_bind(registry, name,
+ &zwp_primary_selection_device_manager_v1_interface, 1);
+ }
+# endif // FEAT_WAYLAND_CLIPBOARD_FS
+#endif // FEAT_WAYLAND_CLIPBOARD
- 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;
}
/*
@@ -829,86 +379,196 @@ vwl_registry_listener_global(
* global will just be ignored on the compositor side.
*/
static void
-vwl_registry_listener_global_remove(
+wl_registry_listener_event_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
- */
+#ifdef FEAT_WAYLAND_CLIPBOARD_FS
static void
-vwl_add_seat(struct wl_seat *seat_proxy)
+xdg_wm_base_listener_event_ping(
+ void *data UNUSED,
+ struct xdg_wm_base *xdg_base,
+ uint32_t serial)
{
- vwl_seat_T *seat;
+ xdg_wm_base_pong(xdg_base, serial);
+}
+#endif
- if (ga_grow(&vwl_seats, 1) == FAIL)
- return;
+static const struct wl_registry_listener wl_registry_listener = {
+ .global = wl_registry_listener_event_global,
+ .global_remove = wl_registry_listener_event_global_remove
+};
- seat = &((vwl_seat_T *)vwl_seats.ga_data)[vwl_seats.ga_len];
+#ifdef FEAT_WAYLAND_CLIPBOARD_FS
+static const struct xdg_wm_base_listener xdg_wm_base_listener = {
+ .ping = xdg_wm_base_listener_event_ping
+};
+#endif
- seat->proxy = seat_proxy;
+static void vwl_connection_destroy(vwl_connection_T *self);
- // Get label and capabilities
- wl_seat_add_listener(seat_proxy, &vwl_seat_listener, seat);
+#ifdef FEAT_WAYLAND_CLIPBOARD
- if (vwl_display_roundtrip(&vwl_display) == FAIL)
- return;
+# define VWL_DESTROY_GOBJECT(ct, object) \
+ if (ct->gobjects.object != NULL) \
+ { \
+ object##_destroy(ct->gobjects.object); \
+ ct->gobjects.object = NULL; \
+ }
- // Check if label has been allocated
- if (seat->label == NULL)
- return;
+# define VWL_GOBJECT_AVAIL(ct, object) (ct->gobjects.object != NULL)
- vwl_seats.ga_len++;
-}
+#endif
-/*
- * Callback for seat text label/name
- */
- static void
-vwl_seat_listener_name(
- void *data,
- struct wl_seat *seat_proxy UNUSED,
- const char *name)
+// Make sure to call wayland_set_display(display);
+ static vwl_connection_T *
+vwl_connection_new(const char *display)
{
- vwl_seat_T *seat = data;
+ vwl_connection_T *ct;
+#ifdef FEAT_WAYLAND_CLIPBOARD_FS
+ const char_u *env;
+ bool force_fs;
+#endif
+ if (wayland_no_connect)
+ return NULL;
- seat->label = (char *)vim_strsave((char_u *)name);
+ // We will get an error if XDG_RUNTIME_DIR is not set.
+ if (mch_getenv("XDG_RUNTIME_DIR") == NULL)
+ return NULL;
+
+ ct = ALLOC_CLEAR_ONE(vwl_connection_T);
+
+ if (ct == NULL)
+ return NULL;
+
+ // Must set log handler before we connect display in order to work.
+ wl_log_set_handler_client(vwl_log_handler);
+
+ ct->display.proxy = wl_display_connect(display);
+
+ if (ct->display.proxy == NULL)
+ {
+ vim_free(ct);
+ return NULL;
+ }
+
+ ct->display.fd = wl_display_get_fd(ct->display.proxy);
+ ct->registry.proxy = wl_display_get_registry(ct->display.proxy);
+
+ if (ct->registry.proxy == NULL)
+ {
+ wl_display_disconnect(ct->display.proxy);
+ vim_free(ct);
+ return NULL;
+ }
+
+ ga_init2(&ct->gobjects.seats, sizeof(vwl_seat_T *), 1);
+
+ wl_registry_add_listener(ct->registry.proxy, &wl_registry_listener, ct);
+
+#ifdef FEAT_WAYLAND_CLIPBOARD_FS
+ env = mch_getenv("VIM_WAYLAND_FORCE_FS");
+ force_fs = (env != NULL && STRCMP(env, "1") == 0);
+
+ if (force_fs)
+ p_wst = TRUE;
+#endif
+
+ if (vwl_connection_roundtrip(ct) == FAIL)
+ {
+ vwl_connection_destroy(ct);
+ return NULL;
+ }
+
+#ifdef FEAT_WAYLAND_CLIPBOARD_FS
+ if (force_fs)
+ {
+ // Force using focus stealing method
+ VWL_DESTROY_GOBJECT(ct, ext_data_control_manager_v1)
+ VWL_DESTROY_GOBJECT(ct, zwlr_data_control_manager_v1)
+ }
+
+ // If data control protocols are available, we don't need the other global
+ // objects.
+ else if (VWL_GOBJECT_AVAIL(ct, ext_data_control_manager_v1)
+ || VWL_GOBJECT_AVAIL(ct, zwlr_data_control_manager_v1))
+ {
+ VWL_DESTROY_GOBJECT(ct, wl_data_device_manager)
+ VWL_DESTROY_GOBJECT(ct, wl_shm)
+ VWL_DESTROY_GOBJECT(ct, wl_compositor)
+ VWL_DESTROY_GOBJECT(ct, xdg_wm_base)
+ VWL_DESTROY_GOBJECT(ct, zwp_primary_selection_device_manager_v1)
+ }
+
+ // Start responding to pings from the compositor if we have xdg_wm_base
+ if (VWL_GOBJECT_AVAIL(ct, xdg_wm_base))
+ xdg_wm_base_add_listener(ct->gobjects.xdg_wm_base,
+ &xdg_wm_base_listener, NULL);
+#endif
+
+ return ct;
}
+#ifdef FEAT_WAYLAND_CLIPBOARD
+
/*
- * Callback for seat capabilities
+ * Destroy/free seat.
*/
static void
-vwl_seat_listener_capabilities(
- void *data,
- struct wl_seat *seat_proxy UNUSED,
- uint32_t capabilities)
+vwl_seat_destroy(vwl_seat_T *self)
{
- vwl_seat_T *seat = data;
-
- seat->capabilities = capabilities;
+ if (self == NULL)
+ return;
+ if (self->proxy != NULL)
+ {
+ if (wl_seat_get_version(self->proxy) >= 5)
+ // Helpful for the compositor
+ wl_seat_release(self->proxy);
+ else
+ wl_seat_destroy(self->proxy);
+ }
+ vim_free(self->label);
+ vim_free(self);
}
/*
- * Destroy/free seat.
+ * Disconnects the display and frees up all resources, including all global
+ * objects.
*/
static void
-vwl_destroy_seat(vwl_seat_T *seat)
+vwl_connection_destroy(vwl_connection_T *self)
{
- if (seat->proxy != NULL)
+#ifdef FEAT_WAYLAND_CLIPBOARD
+ VWL_DESTROY_GOBJECT(self, ext_data_control_manager_v1)
+ VWL_DESTROY_GOBJECT(self, zwlr_data_control_manager_v1)
+# ifdef FEAT_WAYLAND_CLIPBOARD_FS
+ VWL_DESTROY_GOBJECT(self, wl_data_device_manager)
+ VWL_DESTROY_GOBJECT(self, wl_shm)
+ VWL_DESTROY_GOBJECT(self, wl_compositor)
+ VWL_DESTROY_GOBJECT(self, xdg_wm_base)
+ VWL_DESTROY_GOBJECT(self, zwp_primary_selection_device_manager_v1)
+# endif
+#endif
+
+ for (int i = 0; i < self->gobjects.seats.ga_len; i++)
+ vwl_seat_destroy(((vwl_seat_T **)self->gobjects.seats.ga_data)[i]);
+ ga_clear(&self->gobjects.seats);
+ self->gobjects.seats.ga_len = 0;
+
+ if (self->registry.proxy != NULL)
{
- 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;
+ wl_registry_destroy(self->registry.proxy);
+ self->registry.proxy = NULL;
+ }
+ if (self->display.proxy != NULL)
+ {
+ wl_display_disconnect(self->display.proxy);
+ self->display.proxy = NULL;
}
- vim_free(seat->label);
- seat->label = NULL;
+ vim_free(self);
}
/*
@@ -916,50 +576,87 @@ vwl_destroy_seat(vwl_seat_T *seat)
* If NULL or an empty string is passed as the label then the first available
* seat found is used.
*/
- static vwl_seat_T *
-vwl_get_seat(const char *label)
+ vwl_seat_T *
+vwl_connection_get_seat(vwl_connection_T *self, const char *label)
{
- if ((STRCMP(label, "") == 0 || label == NULL) && vwl_seats.ga_len > 0)
- return &((vwl_seat_T *)vwl_seats.ga_data)[0];
+ if ((STRCMP(label, "") == 0 || label == NULL)
+ && self->gobjects.seats.ga_len > 0)
+ return ((vwl_seat_T **)self->gobjects.seats.ga_data)[0];
- for (int i = 0; i < vwl_seats.ga_len; i++)
+ for (int i = 0; i < self->gobjects.seats.ga_len; i++)
{
- vwl_seat_T *seat = &((vwl_seat_T *)vwl_seats.ga_data)[i];
+ vwl_seat_T *seat = ((vwl_seat_T **)self->gobjects.seats.ga_data)[i];
if (STRCMP(seat->label, label) == 0)
return seat;
}
return NULL;
}
+#ifdef FEAT_WAYLAND_CLIPBOARD_FS
/*
* Get keyboard object from seat and return it. NULL is returned on
* failure such as when a keyboard is not available for seat.
*/
- static struct wl_keyboard *
-vwl_seat_get_keyboard(vwl_seat_T *seat)
+ struct wl_keyboard *
+vwl_seat_get_keyboard(vwl_seat_T *self)
{
- if (!(seat->capabilities & WL_SEAT_CAPABILITY_KEYBOARD))
+ if (!(self->capabilities & WL_SEAT_CAPABILITY_KEYBOARD))
return NULL;
- return wl_seat_get_keyboard(seat->proxy);
+ return wl_seat_get_keyboard(self->proxy);
+}
+#endif
+
+#endif
+
+/*
+ * Set wayland_display_name to display. Note that this allocate a copy of the
+ * string, unless NULL is passed. If NULL is passed then v:wayland_display is
+ * set to $WAYLAND_DISPLAY, but wayland_display_name is set to NULL.
+ */
+ static void
+wayland_set_display(const char *display)
+{
+ if (display == NULL)
+ display = (char*)mch_getenv((char_u*)"WAYLAND_DISPLAY");
+ else if (display == wayland_display_name)
+ // Don't want to be freeing vwl_display_strname then trying to copy it
+ // after.
+ goto exit;
+
+ if (display == NULL)
+ // $WAYLAND_DISPLAY is not set
+ display = "";
+
+ // Leave unchanged if display is empty (but not NULL)
+ if (STRCMP(display, "") != 0)
+ {
+ vim_free(wayland_display_name);
+ wayland_display_name = (char*)vim_strsave((char_u*)display);
+ }
+
+exit:
+#ifdef FEAT_EVAL
+ set_vim_var_string(VV_WAYLAND_DISPLAY, (char_u*)display, -1);
+#endif
}
/*
- * 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
+ * Initializes the global Wayland connection. Connects to the Wayland display
+ * with given name and binds to global objects as needed. If display is NULL
+ * then the $WAYLAND_DISPLAY environment variable will be used (handled by
+ * libwayland). Returns FAIL on failure and OK on
* success
*/
int
-wayland_init_client(const char *display)
+wayland_init_connection(const char *display)
{
wayland_set_display(display);
- if (vwl_connect_display(display) == FAIL ||
- vwl_listen_to_registry() == FAIL)
- goto fail;
+ wayland_ct = vwl_connection_new(display);
- wayland_display_fd = vwl_display.fd;
+ if (wayland_ct == NULL)
+ goto fail;
return OK;
fail:
@@ -969,1545 +666,803 @@ fail:
}
/*
- * Disconnect Wayland client and free up all resources used.
+ * Disconnect global Wayland connection and free up all resources used.
*/
void
-wayland_uninit_client(void)
+wayland_uninit_connection(void)
{
+ if (wayland_ct == NULL)
+ return;
#ifdef FEAT_WAYLAND_CLIPBOARD
- wayland_cb_uninit();
+ clip_uninit_wayland();
#endif
- vwl_disconnect_display();
-
+ vwl_connection_destroy(wayland_ct);
+ wayland_ct = NULL;
+ is_reading = false;
wayland_set_display("");
}
+static int wayland_ct_restore_count = 0;
+
/*
- * Return TRUE if Wayland display connection is valid and ready.
+ * Attempts to restore the Wayland display connection.
*/
- static int
-wayland_client_is_connected(int quiet)
+ static void
+wayland_restore_connection(void)
{
- 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;
+ // No point in restoring the connection if we are exiting or dying.
+ if (exiting || v_dying || wayland_ct_restore_count <= 0)
+ wayland_set_display("");
+
+ --wayland_ct_restore_count;
+ wayland_uninit_connection();
+
+ if (wayland_init_connection(wayland_display_name) == OK)
+ {
+#ifdef FEAT_WAYLAND_CLIPBOARD
+ clip_init_wayland();
+#endif
+ }
}
/*
- * Flush requests and process new Wayland events, does not poll the display file
- * descriptor.
+ * Should be called before polling (select or poll) the global Wayland
+ * connection display fd. Returns fd on success and -1 on failure.
*/
int
-wayland_client_update(void)
+wayland_prepare_read(void)
{
- return vwl_display_dispatch_any(&vwl_display) == -1 ? FAIL : OK;
-}
+ if (wayland_ct == NULL)
+ return -1;
-#ifdef FEAT_WAYLAND_CLIPBOARD
+ if (is_reading)
+ {
+ wl_display_cancel_read(wayland_ct->display.proxy);
+ is_reading = false;
+ }
-/*
- * 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;
+ while (wl_display_prepare_read(wayland_ct->display.proxy) == -1)
+ // Event queue not empty, dispatch the events
+ if (wl_display_dispatch_pending(wayland_ct->display.proxy) == -1)
+ return -1;
+
+ if (vwl_connection_flush(wayland_ct) < 0)
+ {
+ wl_display_cancel_read(wayland_ct->display.proxy);
+ return -1;
+ }
+
+ is_reading = true;
+
+ return wayland_ct->display.fd;
}
/*
- * Configure xdg_surface
+ * Catch up on any qeueued events
*/
- static void
-vwl_xdg_surface_listener_configure(
- void *data UNUSED,
- struct xdg_surface *surface,
- uint32_t serial)
+ int
+wayland_update(void)
{
- xdg_surface_ack_configure(surface, serial);
+ if (wayland_ct == NULL)
+ return FAIL;
+ return vwl_connection_roundtrip(wayland_ct);
}
-/*
- * 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)
+#ifndef HAVE_SELECT
+
+ void
+wayland_poll_check(int revents)
{
- vwl_buffer_store_T *store = data;
+ if (wayland_ct == NULL)
+ return;
- store->available = TRUE;
+ is_reading = false;
+ if (revents & POLLIN)
+ if (wl_display_read_events(wayland_ct->display.proxy) != -1)
+ {
+ wl_display_dispatch_pending(wayland_ct->display.proxy);
+ return;
+ }
+ else if (revents & (POLLHUP | POLLERR))
+ wl_display_cancel_read(wayland_ct->display.proxy);
+ else
+ {
+ // Nothing happened
+ wl_display_cancel_read(wayland_ct->display.proxy);
+ return;
+ }
+ wayland_restore_connection();
}
-/*
- * 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);
+#else // ifdef HAVE_SELECT
- close(store->fd);
+ void
+wayland_select_check(bool is_set)
+{
+ if (wayland_ct == NULL)
+ return;
- vim_free(store);
+ is_reading = false;
+ if (is_set)
+ {
+ if (wl_display_read_events(wayland_ct->display.proxy) != -1)
+ wl_display_dispatch_pending(wayland_ct->display.proxy);
+ else
+ {
+ wl_display_cancel_read(wayland_ct->display.proxy);
+ wayland_restore_connection();
+ }
+ }
+ else
+ wl_display_cancel_read(wayland_ct->display.proxy);
}
+#endif // !HAVE_SELECT
+
/*
- * Initialize a buffer and its backing memory pool.
+ * Disconnect then reconnect Wayland connection, and update clipmethod.
*/
- static vwl_buffer_store_T *
-vwl_init_buffer_store(int width, int height)
+ void
+ex_wlrestore(exarg_T *eap)
{
- 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;
+ char *display;
- store->width = width;
- store->height = height;
- store->stride = store->width * 4;
- store->size = store->stride * store->height;
+ if (eap->arg == NULL || STRLEN(eap->arg) == 0)
+ // Use current display name if none given
+ display = wayland_display_name;
+ else
+ display = (char*)eap->arg;
- fd = mch_create_anon_file();
- r = ftruncate(fd, store->size);
+ // Return early if shebang is not passed, we are still connected, and if not
+ // changing to a new Wayland display.
+ if (!eap->forceit && wayland_ct != NULL &&
+ (display == wayland_display_name ||
+ (wayland_display_name != NULL &&
+ STRCMP(wayland_display_name, display) == 0)))
+ return;
- if (r == -1)
+#ifdef FEAT_WAYLAND_CLIPBOARD
+ if (clipmethod == CLIPMETHOD_WAYLAND)
{
- if (fd >= 0)
- close(fd);
- return NULL;
+ // Lose any selections we own
+ if (clip_star.owned)
+ clip_lose_selection(&clip_star);
+ if (clip_plus.owned)
+ clip_lose_selection(&clip_plus);
}
+#endif
- 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);
+ if (display != NULL)
+ display = (char*)vim_strsave((char_u*)display);
- store->fd = fd;
+ // Will lose any selections we own
+ wayland_uninit_connection();
- wl_buffer_add_listener(store->buffer, &vwl_cb_buffer_listener, store);
+ // Reset amount of available tries to reconnect the display to 5
+ wayland_ct_restore_count = 5;
- if (vwl_display_roundtrip(&vwl_display) == -1)
+ if (wayland_init_connection(display) == OK)
{
- vwl_destroy_buffer_store(store);
- return NULL;
+ smsg(_("restoring Wayland display %s"), wayland_display_name);
+
+#ifdef FEAT_WAYLAND_CLIPBOARD
+ clip_plus.did_warn = false;
+ clip_star.did_warn = false;
+ clip_init_wayland();
+#endif
}
+ else
+ msg(_("failed restoring, lost connection to Wayland display"));
- store->available = TRUE;
+ vim_free(display);
- return store;
+ choose_clipmethod();
}
+#ifdef FEAT_WAYLAND_CLIPBOARD
+
/*
- * Destroy a focus stealing store structure.
+ * Get a suitable data device manager from connection. "supported" should be
+ * iniitialized to VWL_DATA_PROTOCOL_NONE beforehand. Returns NULL if there are
+ * no data device manager available with the required selection.
*/
- 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)
+ vwl_data_device_manager_T *
+vwl_connection_get_data_device_manager(
+ vwl_connection_T *self,
+ wayland_selection_T req_sel,
+ int_u *supported)
+{
+ vwl_data_device_manager_T *manager =
+ ALLOC_CLEAR_ONE(vwl_data_device_manager_T);
+
+ // Prioritize ext-data-control-v1 over wlr-data-control-unstable-v1 because
+ // it is newer.
+ if (self->gobjects.ext_data_control_manager_v1 != NULL)
{
- 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);
+ manager->proxy = self->gobjects.ext_data_control_manager_v1;
+ manager->protocol = VWL_DATA_PROTOCOL_EXT;
- 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;
+ *supported |= (WAYLAND_SELECTION_REGULAR | WAYLAND_SELECTION_PRIMARY);
+ }
+ else if (self->gobjects.zwlr_data_control_manager_v1 != NULL)
+ {
+ manager->proxy = self->gobjects.zwlr_data_control_manager_v1;
+ manager->protocol = VWL_DATA_PROTOCOL_WLR;
- 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);
+ *supported |= WAYLAND_SELECTION_REGULAR;
+ // Only version 2 or greater supports the primary selection
+ if (zwlr_data_control_manager_v1_get_version(manager->proxy) >= 2)
+ *supported |= WAYLAND_SELECTION_PRIMARY;
+ }
+#ifdef FEAT_WAYLAND_CLIPBOARD_FS
+ else if (self->gobjects.wl_data_device_manager != NULL
+ && req_sel == WAYLAND_SELECTION_REGULAR)
{
- // 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;
+ manager->proxy = self->gobjects.wl_data_device_manager;
+ manager->protocol = VWL_DATA_PROTOCOL_CORE;
- gettimeofday(&start, NULL);
+ *supported |= WAYLAND_SELECTION_REGULAR;
+ }
- while (vwl_display_dispatch(&vwl_display) != -1)
+ if (req_sel == WAYLAND_SELECTION_PRIMARY
+ && !(*supported & WAYLAND_SELECTION_PRIMARY))
+ if (self->gobjects.zwp_primary_selection_device_manager_v1 != NULL)
{
- if (store->got_focus)
- break;
-
- gettimeofday(&now, NULL);
+ manager->proxy =
+ self->gobjects.zwp_primary_selection_device_manager_v1;
+ manager->protocol = VWL_DATA_PROTOCOL_PRIMARY;
- if ((now.tv_sec * 1000000 + now.tv_usec) -
- (start.tv_sec * 1000000 + start.tv_usec)
- >= p_wtm * 1000)
- goto fail;
+ *supported |= WAYLAND_SELECTION_PRIMARY;
}
- }
-early_exit:
- vwl_destroy_fs_surface(store);
- vwl_display_flush(&vwl_display);
+#endif
- return OK;
-fail:
- vwl_destroy_fs_surface(store);
- vwl_display_flush(&vwl_display);
+ if (!(*supported & req_sel))
+ {
+ vim_free(manager);
+ return NULL;
+ }
- return FAIL;
+ return manager;
}
-/*
- * Called when the keyboard focus is on our surface
- */
- static void
-vwl_fs_keyboard_listener_enter(
- void *data,
- struct wl_keyboard *keyboard UNUSED,
- uint32_t serial,
- struct wl_surface *surface UNUSED,
- struct wl_array *keys UNUSED)
+ vwl_data_device_T *
+vwl_data_device_manager_get_data_device(
+ vwl_data_device_manager_T *self,
+ vwl_seat_T *seat)
{
- vwl_fs_surface_T *store = data;
-
- store->got_focus = TRUE;
-
- if (store->on_focus != NULL)
- store->on_focus(store->user_data, serial);
-}
+ vwl_data_device_T *device = ALLOC_CLEAR_ONE(vwl_data_device_T);
-// Dummy functions to handle keyboard events we don't care about.
+ switch (self->protocol)
+ {
+ case VWL_DATA_PROTOCOL_EXT:
+ device->proxy = ext_data_control_manager_v1_get_data_device(
+ self->proxy, seat->proxy);
+ break;
+ case VWL_DATA_PROTOCOL_WLR:
+ device->proxy = zwlr_data_control_manager_v1_get_data_device(
+ self->proxy, seat->proxy);
+ break;
+#ifdef FEAT_WAYLAND_CLIPBOARD_FS
+ case VWL_DATA_PROTOCOL_CORE:
+ device->proxy = wl_data_device_manager_get_data_device(
+ self->proxy, seat->proxy);
+ break;
+ case VWL_DATA_PROTOCOL_PRIMARY:
+ device->proxy = zwp_primary_selection_device_manager_v1_get_device(
+ self->proxy, seat->proxy);
+ break;
+#endif
+ default:
+ vim_free(device);
+ return NULL;
+ }
+ device->protocol = self->protocol;
- 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);
+ return device;
}
- static void
-vwl_fs_keyboard_listener_leave(
- void *data UNUSED,
- struct wl_keyboard *keyboard UNUSED,
- uint32_t serial UNUSED,
- struct wl_surface *surface UNUSED)
+ vwl_data_source_T *
+vwl_data_device_manager_create_data_source(vwl_data_device_manager_T *self)
{
-}
+ vwl_data_source_T *source = ALLOC_CLEAR_ONE(vwl_data_source_T);
- 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)
-{
-}
+ switch (self->protocol)
+ {
+ case VWL_DATA_PROTOCOL_EXT:
+ source->proxy = ext_data_control_manager_v1_create_data_source(
+ self->proxy);
+ break;
+ case VWL_DATA_PROTOCOL_WLR:
+ source->proxy = zwlr_data_control_manager_v1_create_data_source(
+ self->proxy);
+ break;
+#ifdef FEAT_WAYLAND_CLIPBOARD_FS
+ case VWL_DATA_PROTOCOL_CORE:
+ source->proxy = wl_data_device_manager_create_data_source(
+ self->proxy);
+ break;
+ case VWL_DATA_PROTOCOL_PRIMARY:
+ source->proxy =
+ zwp_primary_selection_device_manager_v1_create_source(
+ self->proxy);
+ break;
+#endif
+ default:
+ vim_free(source);
+ return NULL;
+ }
+ source->protocol = self->protocol;
- 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)
-{
+ return source;
}
- static void
-vwl_fs_keyboard_listener_repeat_info(
- void *data UNUSED,
- struct wl_keyboard *keyboard UNUSED,
- int32_t rate UNUSED,
- int32_t delay UNUSED)
+ static vwl_data_offer_T *
+vwl_data_device_wrap_offer_proxy(vwl_data_device_T *self, void *proxy)
{
-}
+ vwl_data_offer_T *offer = ALLOC_CLEAR_ONE(vwl_data_offer_T);
-#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)
+ if (offer == NULL)
+ return NULL;
- static void
-vwl_data_device_destroy(vwl_data_device_T *device, int alloced)
-{
- VWL_CODE_DATA_OBJECT_DESTROY(device);
-}
+ offer->proxy = proxy;
+ offer->protocol = self->protocol;
+ offer->data = self->data;
+ ga_init2(&offer->mime_types, sizeof(char *), 10);
- static void
-vwl_data_offer_destroy(vwl_data_offer_T *offer, int alloced)
-{
- VWL_CODE_DATA_OBJECT_DESTROY(offer);
-}
+ // Try pre allocating the array, 10 mime types seems to usually be the
+ // maximum from experience.
+ ga_grow(&offer->mime_types, 10);
- static void
-vwl_data_source_destroy(vwl_data_source_T *source, int alloced)
-{
- VWL_CODE_DATA_OBJECT_DESTROY(source);
+ return offer;
}
+#ifdef FEAT_WAYLAND_CLIPBOARD_FS
+# define VWL_CODE_DATA_PROXY_FS_DESTROY(type) \
+ case VWL_DATA_PROTOCOL_CORE: \
+ wl_data_##type##_destroy(self->proxy); \
+ break; \
+ case VWL_DATA_PROTOCOL_PRIMARY: \
+ zwp_primary_selection_##type##_v1_destroy(self->proxy); \
+ break;
+#else
+# define VWL_CODE_DATA_PROXY_FS_DESTROY(type)
+#endif
-// 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;
+#define VWL_FUNC_DATA_PROXY_DESTROY(type) \
+ void \
+ vwl_data_##type##_destroy(vwl_data_##type##_T *self) \
+ { \
+ if (self == NULL) \
+ return; \
+ switch (self->protocol) \
+ { \
+ case VWL_DATA_PROTOCOL_EXT: \
+ ext_data_control_##type##_v1_destroy(self->proxy); \
+ break; \
+ case VWL_DATA_PROTOCOL_WLR: \
+ zwlr_data_control_##type##_v1_destroy(self->proxy); \
+ break; \
+ VWL_CODE_DATA_PROXY_FS_DESTROY(type) \
+ default: \
+ break; \
+ } \
+ vim_free(self); \
+ }
-// These functions handle the more complicated data_offer and selection events.
+VWL_FUNC_DATA_PROXY_DESTROY(device)
+VWL_FUNC_DATA_PROXY_DESTROY(source)
- static void
-vwl_gen_data_device_listener_data_offer(void *data, void *offer_proxy)
+ void
+vwl_data_offer_destroy(vwl_data_offer_T *self)
{
- vwl_data_device_T *device = data;
-
- tmp_vwl_offer = alloc(sizeof(*tmp_vwl_offer));
-
- if (tmp_vwl_offer != NULL)
+ if (self == NULL)
+ return;
+ switch (self->protocol)
{
- tmp_vwl_offer->proxy = offer_proxy;
- tmp_vwl_offer->protocol = device->protocol;
-
- vwl_data_device_listener.data_offer(device, tmp_vwl_offer);
+ case VWL_DATA_PROTOCOL_EXT:
+ ext_data_control_offer_v1_destroy(self->proxy);
+ break;
+ case VWL_DATA_PROTOCOL_WLR:
+ zwlr_data_control_offer_v1_destroy(self->proxy);
+ break;
+#ifdef FEAT_WAYLAND_CLIPBOARD_FS
+ case VWL_DATA_PROTOCOL_CORE:
+ wl_data_offer_destroy(self->proxy);
+ break;
+ case VWL_DATA_PROTOCOL_PRIMARY:
+ zwp_primary_selection_offer_v1_destroy(self->proxy);
+ break;
+#endif
+ default:
+ break;
}
+ ga_clear_strings(&self->mime_types);
+ vim_free(self);
}
- static void
-vwl_gen_data_device_listener_selection(
- void *data,
- void *offer_proxy,
- wayland_selection_T selection,
- vwl_data_protocol_T protocol)
+/*
+ * Doesn't destroy the actual global object proxy, only frees the structure.
+ */
+void
+vwl_data_device_manager_discard(vwl_data_device_manager_T *self)
{
- 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);
+ if (self == NULL)
+ return;
+ vim_free(self);
+}
+
+#define VWL_FUNC_DATA_DEVICE_EVENT_DATA_OFFER(device_type, offer_type) \
+ static void \
+ device_type##_listener_event_data_offer( \
+ void *data, \
+ struct device_type *device UNUSED, \
+ struct offer_type *offer) \
+ { \
+ vwl_data_device_T *self = data; \
+ self->offer = vwl_data_device_wrap_offer_proxy(self, offer); \
+ self->listener->data_offer(self->data, self, self->offer); \
}
- else
- {
- vwl_data_device_listener.selection(data, tmp_vwl_offer, selection);
- tmp_vwl_offer = NULL;
+
+// We want to set the offer to NULL after the selection callback, because the
+// callback may free the offer, and we don't want a dangling pointer.
+#define VWL_FUNC_DATA_DEVICE_EVENT_SELECTION(device_type, offer_type) \
+ static void \
+ device_type##_listener_event_selection( \
+ void *data, \
+ struct device_type *device UNUSED, \
+ struct offer_type *offer UNUSED) \
+ { \
+ vwl_data_device_T *self = data; \
+ self->listener->selection(self->data, self, self->offer, \
+ WAYLAND_SELECTION_REGULAR); \
+ self->offer = NULL; \
+ } \
+
+#define VWL_FUNC_DATA_DEVICE_EVENT_PRIMARY_SELECTION(device_type, offer_type) \
+ static void \
+ device_type##_listener_event_primary_selection( \
+ void *data, \
+ struct device_type *device UNUSED, \
+ struct offer_type *offer UNUSED) \
+ { \
+ vwl_data_device_T *self = data; \
+ self->listener->selection(self->data, self, self->offer, \
+ WAYLAND_SELECTION_PRIMARY); \
+ self->offer = NULL; \
}
-}
-// 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); \
-}
+#define VWL_FUNC_DATA_DEVICE_EVENT_FINISHED(device_type) \
+ static void \
+ device_type##_listener_event_finished( \
+ void *data, \
+ struct device_type *device UNUSED) \
+ { \
+ vwl_data_device_T *self = data; \
+ self->listener->finished(self->data, self); \
+ }
-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(
+VWL_FUNC_DATA_DEVICE_EVENT_DATA_OFFER(
+ ext_data_control_device_v1, ext_data_control_offer_v1)
+VWL_FUNC_DATA_DEVICE_EVENT_DATA_OFFER(
+ zwlr_data_control_device_v1, zwlr_data_control_offer_v1)
+#ifdef FEAT_WAYLAND_CLIPBOARD_FS
+VWL_FUNC_DATA_DEVICE_EVENT_DATA_OFFER(wl_data_device, wl_data_offer)
+VWL_FUNC_DATA_DEVICE_EVENT_DATA_OFFER(
zwp_primary_selection_device_v1, zwp_primary_selection_offer_v1)
+#endif
-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
-};
+VWL_FUNC_DATA_DEVICE_EVENT_SELECTION(
+ ext_data_control_device_v1, ext_data_control_offer_v1
+)
+VWL_FUNC_DATA_DEVICE_EVENT_SELECTION(
+ zwlr_data_control_device_v1, zwlr_data_control_offer_v1
+)
+VWL_FUNC_DATA_DEVICE_EVENT_PRIMARY_SELECTION(
+ ext_data_control_device_v1, ext_data_control_offer_v1
+)
+VWL_FUNC_DATA_DEVICE_EVENT_PRIMARY_SELECTION(
+ zwlr_data_control_device_v1, zwlr_data_control_offer_v1
+)
+#ifdef FEAT_WAYLAND_CLIPBOARD_FS
+VWL_FUNC_DATA_DEVICE_EVENT_SELECTION(
+ wl_data_device, wl_data_offer
+)
+VWL_FUNC_DATA_DEVICE_EVENT_PRIMARY_SELECTION(
+ zwp_primary_selection_device_v1, zwp_primary_selection_offer_v1)
+#endif
-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
-};
+VWL_FUNC_DATA_DEVICE_EVENT_FINISHED(ext_data_control_device_v1)
+VWL_FUNC_DATA_DEVICE_EVENT_FINISHED(zwlr_data_control_device_v1)
-struct wl_data_device_listener wl_data_device_listener = {
- .data_offer = wl_data_device_listener_data_offer,
- .selection = wl_data_device_listener_selection,
+static struct ext_data_control_device_v1_listener
+ ext_data_control_device_v1_listener = {
+ .data_offer = ext_data_control_device_v1_listener_event_data_offer,
+ .selection = ext_data_control_device_v1_listener_event_selection,
+ .primary_selection =
+ ext_data_control_device_v1_listener_event_primary_selection,
+ .finished = ext_data_control_device_v1_listener_event_finished
};
-
-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
+static const struct zwlr_data_control_device_v1_listener
+ zwlr_data_control_device_v1_listener = {
+ .data_offer = zwlr_data_control_device_v1_listener_event_data_offer,
+ .selection = zwlr_data_control_device_v1_listener_event_selection,
+ .primary_selection =
+ zwlr_data_control_device_v1_listener_event_primary_selection,
+ .finished = zwlr_data_control_device_v1_listener_event_finished
};
-
-// 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
+#ifdef FEAT_WAYLAND_CLIPBOARD_FS
+static const struct wl_data_device_listener wl_data_device_listener = {
+ .data_offer = wl_data_device_listener_event_data_offer,
+ .selection = wl_data_device_listener_event_selection,
};
-
-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
+static const struct zwp_primary_selection_device_v1_listener
+ zwp_primary_selection_device_v1_listener = {
+ .data_offer = zwp_primary_selection_device_v1_listener_event_data_offer,
+ .selection =
+ zwp_primary_selection_device_v1_listener_event_primary_selection,
};
+# endif
-struct wl_data_source_listener wl_data_source_listener = {
- .send = wl_data_source_listener_send,
- .cancelled = wl_data_source_listener_cancelled
-};
+# define VWL_FUNC_DATA_SOURCE_EVENT_SEND(source_type) \
+ static void \
+ source_type##_listener_event_send( \
+ void *data, struct source_type *source UNUSED, \
+ const char *mime_type, int fd) \
+ { \
+ vwl_data_source_T *self = data; \
+ self->listener->send(self->data, self, mime_type, fd); \
+ }
-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,
-};
+# define VWL_FUNC_DATA_SOURCE_EVENT_CANCELLED(source_type) \
+ static void \
+ source_type##_listener_event_cancelled( \
+ void *data, struct source_type *source UNUSED) \
+ { \
+ vwl_data_source_T *self = data; \
+ self->listener->cancelled(self->data, self); \
+ }
-// OFFERS
-struct zwlr_data_control_offer_v1_listener
-zwlr_data_control_offer_v1_listener = {
- .offer = zwlr_data_control_offer_v1_listener_offer
-};
+VWL_FUNC_DATA_SOURCE_EVENT_SEND(ext_data_control_source_v1)
+VWL_FUNC_DATA_SOURCE_EVENT_SEND(zwlr_data_control_source_v1)
+#ifdef FEAT_WAYLAND_CLIPBOARD_FS
+VWL_FUNC_DATA_SOURCE_EVENT_SEND(wl_data_source)
+VWL_FUNC_DATA_SOURCE_EVENT_SEND(zwp_primary_selection_source_v1)
+#endif
-struct ext_data_control_offer_v1_listener
-ext_data_control_offer_v1_listener = {
- .offer = ext_data_control_offer_v1_listener_offer
-};
+VWL_FUNC_DATA_SOURCE_EVENT_CANCELLED(ext_data_control_source_v1)
+VWL_FUNC_DATA_SOURCE_EVENT_CANCELLED(zwlr_data_control_source_v1)
+#ifdef FEAT_WAYLAND_CLIPBOARD_FS
+VWL_FUNC_DATA_SOURCE_EVENT_CANCELLED(wl_data_source)
+VWL_FUNC_DATA_SOURCE_EVENT_CANCELLED(zwp_primary_selection_source_v1)
+#endif
-struct wl_data_offer_listener wl_data_offer_listener = {
- .offer = wl_data_offer_listener_offer
+static const struct ext_data_control_source_v1_listener
+ ext_data_control_source_v1_listener = {
+ .send = ext_data_control_source_v1_listener_event_send,
+ .cancelled = ext_data_control_source_v1_listener_event_cancelled
};
-
-struct zwp_primary_selection_offer_v1_listener
-zwp_primary_selection_offer_v1_listener = {
- .offer = zwp_primary_selection_offer_v1_listener_offer
+static const struct zwlr_data_control_source_v1_listener
+ zwlr_data_control_source_v1_listener = {
+ .send = zwlr_data_control_source_v1_listener_event_send,
+ .cancelled = zwlr_data_control_source_v1_listener_event_cancelled
+};
+#ifdef FEAT_WAYLAND_CLIPBOARD_FS
+static const struct wl_data_source_listener wl_data_source_listener = {
+ .send = wl_data_source_listener_event_send,
+ .cancelled = wl_data_source_listener_event_cancelled
};
+static const struct zwp_primary_selection_source_v1_listener
+ zwp_primary_selection_source_v1_listener = {
+ .send = zwp_primary_selection_source_v1_listener_event_send,
+ .cancelled = zwp_primary_selection_source_v1_listener_event_cancelled
+};
+#endif
-// `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) \
+#define VWL_FUNC_DATA_OFFER_EVENT_OFFER(offer_type) \
+ static void \
+ offer_type##_listener_event_offer( \
+ void *data, \
+ struct offer_type *offer UNUSED, \
+ const char *mime_type) \
{ \
- 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)
+ vwl_data_offer_T *self = data; \
+ if (STRCMP(mime_type, wayland_vim_special_mime) == 0) \
+ self->from_vim = true; \
+ else if (!self->from_vim && \
+ self->listener->offer(self->data, self, mime_type)) \
+ { \
+ char *mime = (char *)vim_strsave((char_u *)mime_type); \
+ if (ga_grow(&self->mime_types, 1) == FAIL) \
+ vim_free(mime); \
+ else \
+ if (mime != NULL) \
+ ((char **)self->mime_types.ga_data) \
+ [self->mime_types.ga_len++] = mime; \
+ } \
+ }
- static void
-vwl_data_device_add_listener(vwl_data_device_T *device, void *data)
-{
- VWL_CODE_DATA_OBJECT_ADD_LISTENER(device);
-}
+VWL_FUNC_DATA_OFFER_EVENT_OFFER(ext_data_control_offer_v1)
+VWL_FUNC_DATA_OFFER_EVENT_OFFER(zwlr_data_control_offer_v1)
+#ifdef FEAT_WAYLAND_CLIPBOARD_FS
+VWL_FUNC_DATA_OFFER_EVENT_OFFER(wl_data_offer)
+VWL_FUNC_DATA_OFFER_EVENT_OFFER(zwp_primary_selection_offer_v1)
+#endif
- static void
-vwl_data_source_add_listener(vwl_data_source_T *source, void *data)
-{
- VWL_CODE_DATA_OBJECT_ADD_LISTENER(source);
-}
+static const struct ext_data_control_offer_v1_listener
+ ext_data_control_offer_v1_listener = {
+ .offer = ext_data_control_offer_v1_listener_event_offer
+};
+static const struct zwlr_data_control_offer_v1_listener
+ zwlr_data_control_offer_v1_listener = {
+ .offer = zwlr_data_control_offer_v1_listener_event_offer
+};
+#ifdef FEAT_WAYLAND_CLIPBOARD_FS
+static const struct wl_data_offer_listener
+ wl_data_offer_listener = {
+ .offer = wl_data_offer_listener_event_offer
+};
+static const struct zwp_primary_selection_offer_v1_listener
+ zwp_primary_selection_offer_v1_listener = {
+ .offer = zwp_primary_selection_offer_v1_listener_event_offer
+};
+#endif
- static void
-vwl_data_offer_add_listener(vwl_data_offer_T *offer, void *data)
-{
- VWL_CODE_DATA_OBJECT_ADD_LISTENER(offer);
-}
+#ifdef FEAT_WAYLAND_CLIPBOARD_FS
+# define VWL_CODE_DATA_PROXY_FS_ADD_LISTENER(type) \
+ case VWL_DATA_PROTOCOL_CORE: \
+ wl_data_##type##_add_listener(self->proxy, \
+ &wl_data_##type##_listener, self); \
+ break; \
+ case VWL_DATA_PROTOCOL_PRIMARY: \
+ zwp_primary_selection_##type##_v1_add_listener(self->proxy, \
+ &zwp_primary_selection_##type##_v1_listener, self); \
+ break;
+#else
+# define VWL_CODE_DATA_PROXY_FS_ADD_LISTENER(type)
+#endif
+
+#define VWL_FUNC_DATA_PROXY_ADD_LISTENER(type) \
+ void \
+ vwl_data_##type##_add_listener( \
+ vwl_data_##type##_T *self, \
+ const vwl_data_##type##_listener_T *listener, \
+ void *data) \
+ { \
+ if (self == NULL) \
+ return; \
+ self->data = data; \
+ self->listener = listener; \
+ switch (self->protocol) \
+ { \
+ case VWL_DATA_PROTOCOL_EXT: \
+ ext_data_control_##type##_v1_add_listener(self->proxy, \
+ &ext_data_control_##type##_v1_listener, self); \
+ break; \
+ case VWL_DATA_PROTOCOL_WLR: \
+ zwlr_data_control_##type##_v1_add_listener(self->proxy, \
+ &zwlr_data_control_##type##_v1_listener, self); \
+ break; \
+ VWL_CODE_DATA_PROXY_FS_ADD_LISTENER(type) \
+ default: \
+ break; \
+ } \
+ }
+
+VWL_FUNC_DATA_PROXY_ADD_LISTENER(device)
+VWL_FUNC_DATA_PROXY_ADD_LISTENER(source)
+VWL_FUNC_DATA_PROXY_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.
+ * Set the given selection to source. If a data control protocol is being used,
+ * "serial" is ignored.
*/
- static void
+ void
vwl_data_device_set_selection(
- vwl_data_device_T *device,
- vwl_data_source_T *source,
- uint32_t serial,
- wayland_selection_T selection)
+ vwl_data_device_T *self,
+ vwl_data_source_T *source,
+ uint32_t serial UNUSED,
+ wayland_selection_T selection
+)
{
+ void *proxy = source == NULL ? NULL : source->proxy;
+
if (selection == WAYLAND_SELECTION_REGULAR)
{
- switch (device->protocol)
+ switch (self->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);
+ ext_data_control_device_v1_set_selection(self->proxy, proxy);
+ break;
+ case VWL_DATA_PROTOCOL_WLR:
+ zwlr_data_control_device_v1_set_selection(self->proxy, proxy);
break;
+#ifdef FEAT_WAYLAND_CLIPBOARD_FS
case VWL_DATA_PROTOCOL_CORE:
- wl_data_device_set_selection(
- device->proxy, source->proxy, serial);
+ wl_data_device_set_selection(self->proxy, proxy, serial);
break;
+#endif
default:
break;
}
}
else if (selection == WAYLAND_SELECTION_PRIMARY)
{
- switch (device->protocol)
+ switch (self->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);
+ self->proxy, proxy
+ );
+ break;
+ case VWL_DATA_PROTOCOL_WLR:
+ zwlr_data_control_device_v1_set_primary_selection(
+ self->proxy, proxy
+ );
break;
+#ifdef FEAT_WAYLAND_CLIPBOARD_FS
case VWL_DATA_PROTOCOL_PRIMARY:
zwp_primary_selection_device_v1_set_selection(
- device->proxy, source->proxy, serial);
+ self->proxy, proxy, serial);
break;
+#endif
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)
+ void
+vwl_data_source_offer(vwl_data_source_T *self, const char *mime_type)
{
- switch (offer->protocol)
+ switch (self->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);
+ ext_data_control_source_v1_offer(self->proxy, mime_type);
+ break;
+ case VWL_DATA_PROTOCOL_WLR:
+ zwlr_data_control_source_v1_offer(self->proxy, mime_type);
break;
+#ifdef FEAT_WAYLAND_CLIPBOARD_FS
case VWL_DATA_PROTOCOL_CORE:
- wl_data_offer_receive(offer->proxy, mime_type, fd);
+ wl_data_source_offer(self->proxy, mime_type);
break;
case VWL_DATA_PROTOCOL_PRIMARY:
- zwp_primary_selection_offer_v1_receive(offer->proxy, mime_type, fd);
+ zwp_primary_selection_source_v1_offer(self->proxy, mime_type);
break;
+#endif
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)
+ void
+vwl_data_offer_receive(
+ vwl_data_offer_T *self,
+ const char *mime_type,
+ int32_t fd)
{
- switch (manager->protocol)
+ switch (self->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);
+ ext_data_control_offer_v1_receive(self->proxy, mime_type, fd);
+ break;
+ case VWL_DATA_PROTOCOL_WLR:
+ zwlr_data_control_offer_v1_receive(self->proxy, mime_type, fd);
break;
+#ifdef FEAT_WAYLAND_CLIPBOARD_FS
case VWL_DATA_PROTOCOL_CORE:
- device->proxy = wl_data_device_manager_get_data_device(
- manager->proxy, seat->proxy);
+ wl_data_offer_receive(self->proxy, mime_type, fd);
break;
case VWL_DATA_PROTOCOL_PRIMARY:
- device->proxy = zwp_primary_selection_device_manager_v1_get_device(
- manager->proxy, seat->proxy);
+ zwp_primary_selection_offer_v1_receive(self->proxy, mime_type, fd);
break;
+#endif
default:
- device->protocol = VWL_DATA_PROTOCOL_NONE;
- return;
+ break;
}
- 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(&clip_sel->device, FALSE);
- vwl_data_offer_destroy(clip_sel->offer, TRUE);
- vwl_data_source_destroy(&clip_sel->source, FALSE);
- vwl_clipboard_free_mime_types(clip_sel);
-}
-
-/*
- * Return a pointer to a grow array of mime types that the current offer
- * supports sending. If the returned garray has NULL for ga_data or a ga_len of
- * 0, then the selection is cleared. If focus stealing is required, a surface
- * will be created to steal focus first.
- */
- garray_T *
-wayland_cb_get_mime_types(wayland_selection_T selection)
-{
- vwl_clipboard_selection_T *clip_sel;
-
- if (selection == WAYLAND_SELECTION_REGULAR)
- clip_sel = &vwl_clipboard.regular;
- else if (selection == WAYLAND_SELECTION_PRIMARY)
- clip_sel = &vwl_clipboard.primary;
- else
- return NULL;
-
- if (clip_sel->requires_focus)
- {
- // We don't care about the on_focus callback since once we gain focus
- // the data offer events will come immediately.
- if (vwl_init_fs_surface(vwl_clipboard.seat,
- vwl_clipboard.fs_buffer, NULL, NULL) == FAIL)
- return NULL;
- }
- else if (vwl_display_roundtrip(&vwl_display) == FAIL)
- return NULL;
-
- return &clip_sel->mime_types;
-}
-
-/*
- * Receive data from the given selection, and return the fd to read data from.
- * On failure -1 is returned.
- */
- int
-wayland_cb_receive_data(const char *mime_type, wayland_selection_T selection)
-{
- vwl_clipboard_selection_T *clip_sel;
-
- // Create pipe that source client will write to
- int fds[2];
-
- if (selection == WAYLAND_SELECTION_REGULAR)
- clip_sel = &vwl_clipboard.regular;
- else if (selection == WAYLAND_SELECTION_PRIMARY)
- clip_sel = &vwl_clipboard.primary;
- else
- return -1;
-
- if (!wayland_client_is_connected(FALSE) ||
- !vwl_clipboard_selection_is_ready(clip_sel))
- return -1;
-
- if (clip_sel->offer == NULL || clip_sel->offer->proxy == NULL)
- return -1;
-
- if (pipe(fds) == -1)
- return -1;
-
- vwl_data_offer_receive(clip_sel->offer, mime_type, fds[1]);
-
- close(fds[1]); // Close before we read data so that when the source client
- // closes their end we receive an EOF.
-
- if (vwl_display_flush(&vwl_display) == OK)
- return fds[0];
-
- close(fds[0]);
-
- return -1;
-}
-
-/*
- * Callback for send event. Just call the user callback which will handle it
- * and do the writing stuff.
- */
- static void
-vwl_data_source_listener_send(
- vwl_data_source_T *source,
- const char *mime_type,
- int32_t fd)
-{
- vwl_clipboard_selection_T *clip_sel = source->data;
-
- if (clip_sel->send_cb != NULL)
- clip_sel->send_cb(mime_type, fd, clip_sel->selection);
- close(fd);
-}
-
-/*
- * Callback for cancelled event, just call the user callback.
- */
- static void
-vwl_data_source_listener_cancelled(vwl_data_source_T *source)
-{
- vwl_clipboard_selection_T *clip_sel = source->data;
-
- if (clip_sel->send_cb != NULL)
- clip_sel->cancelled_cb(clip_sel->selection);
- vwl_data_source_destroy(source, FALSE);
-}
-
-/*
- * Set the selection when we gain focus
- */
- static void
-vwl_on_focus_set_selection(void *data, uint32_t serial)
-{
- vwl_clipboard_selection_T *clip_sel = data;
-
- vwl_data_device_set_selection(
- &clip_sel->device,
- &clip_sel->source,
- serial,
- clip_sel->selection);
- vwl_display_roundtrip(&vwl_display);
-}
-
-/*
- * Become the given selection's owner, and advertise to other clients the mime
- * types found in mime_types array. Returns FAIL on failure and OK on success.
- */
- int
-wayland_cb_own_selection(
- wayland_cb_send_data_func_T send_cb,
- wayland_cb_selection_cancelled_func_T cancelled_cb,
- const char **mime_types,
- int len,
- wayland_selection_T selection)
-{
- vwl_clipboard_selection_T *clip_sel;
-
- if (selection == WAYLAND_SELECTION_REGULAR)
- clip_sel = &vwl_clipboard.regular;
- else if (selection == WAYLAND_SELECTION_PRIMARY)
- clip_sel = &vwl_clipboard.primary;
- else
- return FAIL;
-
- if (clip_sel->source.proxy != NULL)
- {
- if (selection == WAYLAND_SELECTION_PRIMARY)
- // We already own the selection, ignore (only do this for primary
- // selection). We don't re set the selection because then we would
- // be setting the selection every time the user moves the visual
- // selection cursor, which is messy and inefficient.
- //
- // Vim doesn't have a mechanism to only set the selection
- // when the user stops selecting (such as the user releasing the
- // mouse button in graphical Wayland applications). So this
- // behaviour in Vim differs from other Wayland applications.
- return OK;
- else if (selection == WAYLAND_SELECTION_REGULAR)
- {
- // Technically we don't need to do this as we already own the
- // selection, however if a user yanks text a second time, the
- // text yanked won't appear in their clipboard manager if they are
- // using one.
- //
- // This can be unexpected behaviour for the user so its probably
- // better to do it this way. Additionally other Wayland applications
- // seem to set the selection every time.
- //
- // There should be no noticeable performance change since its not
- // like this is running in the background constantly in Vim, only
- // runs once when the user yanks text to the system clipboard.
- vwl_data_source_destroy(&clip_sel->source, FALSE);
- vwl_display_flush(&vwl_display);
- }
- else
- // Shouldn't happen
- return FAIL;
- }
-
- if (!wayland_client_is_connected(FALSE) ||
- !vwl_clipboard_selection_is_ready(clip_sel))
- return FAIL;
-
- clip_sel->send_cb = send_cb;
- clip_sel->cancelled_cb = cancelled_cb;
-
- vwl_create_data_source(&clip_sel->manager, &clip_sel->source);
-
- vwl_data_source_add_listener(&clip_sel->source, clip_sel);
-
- // Advertise mime types
- for (int i = 0; i < len; i++)
- vwl_data_source_offer(&clip_sel->source, mime_types[i]);
-
- if (clip_sel->requires_focus)
- {
- // Call set_selection later when we gain focus
- if (vwl_init_fs_surface(vwl_clipboard.seat, vwl_clipboard.fs_buffer,
- vwl_on_focus_set_selection, clip_sel) == FAIL)
- goto fail;
- }
- else
- {
- vwl_data_device_set_selection(&clip_sel->device,
- &clip_sel->source, 0, selection);
- if (vwl_display_roundtrip(&vwl_display) == FAIL)
- goto fail;
- }
-
- return OK;
-fail:
- vwl_data_source_destroy(&clip_sel->source, FALSE);
- return FAIL;
-}
-
-/*
- * Disown the given selection, so that we are not the source client that other
- * clients receive data from.
- */
- void
-wayland_cb_lose_selection(wayland_selection_T selection)
-{
- if (selection == WAYLAND_SELECTION_REGULAR)
- vwl_data_source_destroy(&vwl_clipboard.regular.source, FALSE);
- else if (selection == WAYLAND_SELECTION_PRIMARY)
- vwl_data_source_destroy(&vwl_clipboard.primary.source, FALSE);
- vwl_display_flush(&vwl_display);
-}
-
-/*
- * Return TRUE if the selection is owned by either us or another client.
- */
- int
-wayland_cb_selection_is_owned(wayland_selection_T selection)
-{
- vwl_display_roundtrip(&vwl_display);
-
- if (selection == WAYLAND_SELECTION_REGULAR)
- return vwl_clipboard.regular.source.proxy != NULL
- || vwl_clipboard.regular.offer != NULL;
- else if (selection == WAYLAND_SELECTION_PRIMARY)
- return vwl_clipboard.primary.source.proxy != NULL
- || vwl_clipboard.primary.offer != NULL;
- else
- return FALSE;
-}
-
-/*
- * Return TRUE if the Wayland clipboard/selections are ready to use.
- */
- int
-wayland_cb_is_ready(void)
-{
- vwl_display_roundtrip(&vwl_display);
-
- // Clipboard is ready if we have at least one selection available
- return wayland_client_is_connected(TRUE) &&
- (vwl_clipboard_selection_is_ready(&vwl_clipboard.regular) ||
- vwl_clipboard_selection_is_ready(&vwl_clipboard.primary));
-}
-
-/*
- * Reload Wayland clipboard, useful if changing seat.
- */
- int
-wayland_cb_reload(void)
-{
- // Lose any selections we own
- if (clipmethod == CLIPMETHOD_WAYLAND)
- {
- if (clip_star.owned)
- clip_lose_selection(&clip_star);
- if (clip_plus.owned)
- clip_lose_selection(&clip_plus);
- }
-
- wayland_cb_uninit();
-
- if (wayland_cb_init((char*)p_wse) == FAIL)
- return FAIL;
-
- choose_clipmethod();
- return OK;
}
#endif // FEAT_WAYLAND_CLIPBOARD
-static int wayland_ct_restore_count = 0;
-
-/*
- * Attempts to restore the Wayland display connection. Returns OK if display
- * connection was/is now valid, else FAIL if the display connection is invalid.
- */
- int
-wayland_may_restore_connection(void)
-{
- // No point if we still are already connected properly
- if (wayland_client_is_connected(TRUE))
- return OK;
-
- // No point in restoring the connection if we are exiting or dying.
- if (exiting || v_dying || wayland_ct_restore_count <= 0)
- {
- wayland_set_display("");
- return FAIL;
- }
-
- --wayland_ct_restore_count;
- wayland_uninit_client();
-
- return wayland_init_client(wayland_display_name);
-}
-
-/*
- * Disconnect then reconnect Wayland connection, and update clipmethod.
- */
- void
-ex_wlrestore(exarg_T *eap)
-{
- char *display;
-
- if (eap->arg == NULL || STRLEN(eap->arg) == 0)
- // Use current display name if none given
- display = wayland_display_name;
- else
- display = (char*)eap->arg;
-
- // Return early if shebang is not passed, we are still connected, and if not
- // changing to a new Wayland display.
- if (!eap->forceit && wayland_client_is_connected(TRUE) &&
- (display == wayland_display_name ||
- (wayland_display_name != NULL &&
- STRCMP(wayland_display_name, display) == 0)))
- return;
-
-#ifdef FEAT_WAYLAND_CLIPBOARD
- if (clipmethod == CLIPMETHOD_WAYLAND)
- {
- // Lose any selections we own
- if (clip_star.owned)
- clip_lose_selection(&clip_star);
- if (clip_plus.owned)
- clip_lose_selection(&clip_plus);
- }
-#endif
-
-
- if (display != NULL)
- display = (char*)vim_strsave((char_u*)display);
-
- wayland_uninit_client();
-
- // Reset amount of available tries to reconnect the display to 5
- wayland_ct_restore_count = 5;
-
- if (wayland_init_client(display) == OK)
- {
- smsg(_("restoring Wayland display %s"), wayland_display_name);
-
-#ifdef FEAT_WAYLAND_CLIPBOARD
- wayland_cb_init((char*)p_wse);
-#endif
- }
- else
- msg(_("failed restoring, lost connection to Wayland display"));
-
- vim_free(display);
-
- choose_clipmethod();
-}
-
-/*
- * Set wayland_display_name to display. Note that this allocate a copy of the
- * string, unless NULL is passed. If NULL is passed then v:wayland_display is
- * set to $WAYLAND_DISPLAY, but wayland_display_name is set to NULL.
- */
- static void
-wayland_set_display(const char *display)
-{
- if (display == NULL)
- display = (char*)mch_getenv((char_u*)"WAYLAND_DISPLAY");
- else if (display == wayland_display_name)
- // Don't want to be freeing vwl_display_strname then trying to copy it
- // after.
- goto exit;
-
- if (display == NULL)
- // $WAYLAND_DISPLAY is not set
- display = "";
-
- // Leave unchanged if display is empty (but not NULL)
- if (STRCMP(display, "") != 0)
- {
- vim_free(wayland_display_name);
- wayland_display_name = (char*)vim_strsave((char_u*)display);
- }
-
-exit:
-#ifdef FEAT_EVAL
- set_vim_var_string(VV_WAYLAND_DISPLAY, (char_u*)display, -1);
-#endif
-}
-
#endif // FEAT_WAYLAND
diff --git a/src/wayland.h b/src/wayland.h
new file mode 100644
index 000000000..d260af4a9
--- /dev/null
+++ b/src/wayland.h
@@ -0,0 +1,214 @@
+/* vi:set ts=8 sts=4 sw=4 noet:
+ *
+ * VIM - Vi IMproved by Bram Moolenaar
+ *
+ * Do ":help uganda" in Vim to read copying and usage conditions.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ * See README.txt for an overview of the Vim source code.
+ */
+
+/*
+ * wayland.h: Common definitions for Wayland code
+ */
+
+
+#ifdef FEAT_WAYLAND
+
+#include <wayland-client.h>
+
+#ifdef FEAT_WAYLAND_CLIPBOARD
+# include "auto/wayland/wlr-data-control-unstable-v1.h"
+# include "auto/wayland/ext-data-control-v1.h"
+# ifdef FEAT_WAYLAND_CLIPBOARD_FS
+# include "auto/wayland/xdg-shell.h"
+# include "auto/wayland/primary-selection-unstable-v1.h"
+# endif
+#endif
+
+#ifdef FEAT_WAYLAND_CLIPBOARD
+
+// Wayland protocols for accessing the selection
+typedef enum {
+ VWL_DATA_PROTOCOL_NONE,
+ VWL_DATA_PROTOCOL_EXT,
+ VWL_DATA_PROTOCOL_WLR,
+#ifdef FEAT_WAYLAND_CLIPBOARD_FS
+ VWL_DATA_PROTOCOL_CORE,
+ VWL_DATA_PROTOCOL_PRIMARY
+#endif
+} vwl_data_protocol_T;
+
+#endif // FEAT_WAYLAND_CLIPBOARD
+
+// Struct that represents a seat. (Should be accessed via
+// vwl_get_seat()).
+struct vwl_seat_S {
+ struct wl_seat *proxy;
+ char *label; // Name of seat as text (e.g. seat0,
+ // seat1...).
+ uint32_t capabilities; // Bitmask of the capabilites of the seat
+ // (pointer, keyboard, touch).
+};
+
+// Struct wrapper for a Wayland connection
+struct vwl_connection_S {
+ struct {
+ struct wl_display *proxy;
+ int fd; // File descriptor for display
+ } display;
+
+ struct {
+ struct wl_registry *proxy;
+ } registry;
+
+ // Global objects
+ struct {
+ garray_T seats;
+
+#ifdef FEAT_WAYLAND_CLIPBOARD
+ struct zwlr_data_control_manager_v1 *zwlr_data_control_manager_v1;
+ struct ext_data_control_manager_v1 *ext_data_control_manager_v1;
+# ifdef FEAT_WAYLAND_CLIPBOARD_FS
+ struct wl_data_device_manager *wl_data_device_manager;
+ struct wl_shm *wl_shm;
+ struct wl_compositor *wl_compositor;
+ struct xdg_wm_base *xdg_wm_base;
+ struct zwp_primary_selection_device_manager_v1
+ *zwp_primary_selection_device_manager_v1;
+# endif
+#endif
+ } gobjects;
+};
+
+#ifdef FEAT_WAYLAND_CLIPBOARD
+
+// LISTENER WRAPPERS
+
+struct vwl_data_device_listener_S {
+ void (*data_offer)(void *data,
+ vwl_data_device_T *device,
+ vwl_data_offer_T *offer);
+ void (*selection)(void *data,
+ vwl_data_device_T *device,
+ vwl_data_offer_T *offer,
+ wayland_selection_T selection);
+
+ // This event is only relevant for data control protocols
+ void (*finished)(void *data, vwl_data_device_T *device);
+};
+
+struct vwl_data_source_listener_S {
+ void (*send)(void *data,
+ vwl_data_source_T *source,
+ const char *mime_type,
+ int fd);
+ void (*cancelled)(void *data, vwl_data_source_T *source);
+};
+
+struct vwl_data_offer_listener_S {
+ // Return TRUE to add mime type to internal array in data offer. Note that
+ // this is not called for the special Vim mime type
+ // (wayland_vim_special_mime), but offer->from_vim is set to true.
+ // Additionally when the special mime type is received, any offer events
+ // after are ignored.
+ bool (*offer)(void *data, vwl_data_offer_T *offer, const char *mime_type);
+};
+
+// DATA RELATED OBJECT WRAPPERS
+// These wrap around a proxy and act as a generic container.
+// The `data` member is used to pass other needed stuff around such as a
+// vwl_clipboard_selection_T pointer.
+
+struct vwl_data_offer_S {
+ void *proxy;
+ void *data; // Should be same as parent data
+ // device.
+ garray_T mime_types;
+ bool from_vim; // If offer came from us setting the
+ // selection.
+
+ const vwl_data_offer_listener_T *listener;
+ vwl_data_protocol_T protocol;
+};
+
+struct vwl_data_source_S {
+ void *proxy;
+ void *data;
+ const vwl_data_source_listener_T *listener;
+ vwl_data_protocol_T protocol;
+};
+
+struct vwl_data_device_S {
+ void *proxy;
+ void *data;
+ vwl_data_offer_T *offer;
+ const vwl_data_device_listener_T *listener;
+ vwl_data_protocol_T protocol;
+};
+
+struct vwl_data_device_manager_S {
+ void *proxy;
+ vwl_data_protocol_T protocol;
+};
+
+#ifdef FEAT_WAYLAND_CLIPBOARD_FS
+
+// Dummy functions to handle keyboard events we don't care about.
+
+#define VWL_FUNCS_DUMMY_KEYBOARD_EVENTS() \
+ static void \
+clip_wl_fs_keyboard_listener_keymap( \
+ void *data UNUSED, \
+ struct wl_keyboard *keyboard UNUSED, \
+ uint32_t format UNUSED, \
+ int fd, \
+ uint32_t size UNUSED) \
+{ \
+ close(fd); \
+} \
+ static void \
+clip_wl_fs_keyboard_listener_leave( \
+ void *data UNUSED, \
+ struct wl_keyboard *keyboard UNUSED, \
+ uint32_t serial UNUSED, \
+ struct wl_surface *surface UNUSED) \
+{ \
+} \
+ static void \
+clip_wl_fs_keyboard_listener_key( \
+ void *data UNUSED, \
+ struct wl_keyboard *keyboard UNUSED, \
+ uint32_t serial UNUSED, \
+ uint32_t time UNUSED, \
+ uint32_t key UNUSED, \
+ uint32_t state UNUSED) \
+{ \
+} \
+ static void \
+clip_wl_fs_keyboard_listener_modifiers( \
+ void *data UNUSED, \
+ struct wl_keyboard *keyboard UNUSED, \
+ uint32_t serial UNUSED, \
+ uint32_t mods_depressed UNUSED, \
+ uint32_t mods_latched UNUSED, \
+ uint32_t mods_locked UNUSED, \
+ uint32_t group UNUSED) \
+{ \
+} \
+ static void \
+clip_wl_fs_keyboard_listener_repeat_info( \
+ void *data UNUSED, \
+ struct wl_keyboard *keyboard UNUSED, \
+ int32_t rate UNUSED, \
+ int32_t delay UNUSED) \
+{ \
+}
+
+#endif
+
+#endif // FEAT_WAYLAND_CLIPBOARD
+
+// Global Wayland connection. Is also set to NULL when the connection is lost.
+extern vwl_connection_T *wayland_ct;
+
+#endif // FEAT_WAYLAND