patch 9.1.1725: Wayland code can be improved

Problem:  Wayland code can be improved
Solution: Refactor Wayland Clipboard code (Foxe Chen)

This refactor makes the Wayland codebase less convoluted:
- Move clipboard code in 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: #18139

Signed-off-by: Foxe Chen <chen.foxe@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
Foxe Chen
2025-09-01 20:52:44 +02:00
committed by Christian Brabandt
parent 6a2d0496a1
commit f50504a87b
29 changed files with 2613 additions and 2684 deletions

View File

@ -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 \

View File

@ -1,4 +1,4 @@
*builtin.txt* For Vim version 9.1. Last change: 2025 Aug 24
*builtin.txt* For Vim version 9.1. Last change: 2025 Sep 01
VIM REFERENCE MANUAL by Bram Moolenaar
@ -13161,7 +13161,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)

View File

@ -1,4 +1,4 @@
*gui_x11.txt* For Vim version 9.1. Last change: 2025 Aug 10
*gui_x11.txt* For Vim version 9.1. Last change: 2025 Sep 01
VIM REFERENCE MANUAL by Bram Moolenaar
@ -709,8 +709,8 @@ overwriting selected text.
*W23*
When you are yanking into the "* or "+ register and Vim cannot establish a
connection to the X11 selection (or clipboard), it will use register 0 and
output a warning:
connection to the X11 selection (or X11/Wayland clipboard), it will use
register 0 and output a warning:
Warning: Clipboard register not available, using register 0 ~

View File

@ -1,4 +1,4 @@
*options.txt* For Vim version 9.1. Last change: 2025 Aug 27
*options.txt* For Vim version 9.1. Last change: 2025 Sep 01
VIM REFERENCE MANUAL by Bram Moolenaar
@ -10195,7 +10195,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|.

View File

@ -1542,6 +1542,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*

View File

@ -1,4 +1,4 @@
*various.txt* For Vim version 9.1. Last change: 2025 Aug 18
*various.txt* For Vim version 9.1. Last change: 2025 Sep 01
VIM REFERENCE MANUAL by Bram Moolenaar
@ -528,6 +528,8 @@ 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.

View File

@ -1,4 +1,4 @@
*wayland.txt* For Vim version 9.1. Last change: 2025 Aug 27
*wayland.txt* For Vim version 9.1. Last change: 2025 Sep 01
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

View File

@ -1,7 +1,7 @@
" These commands create the option window.
"
" Maintainer: The Vim Project <https://github.com/vim/vim>
" Last Change: 2025 Aug 23
" Last Change: 2025 Sep 01
" Former Maintainer: Bram Moolenaar <Bram@vim.org>
" If there already is an option window, jump to that one.
@ -822,7 +822,7 @@ if has('wayland')
call <SID>AddOption("wlseat", gettext("Wayland seat to use"))
call <SID>OptionG("wse", &wse)
endif
if has("wayland_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

View File

@ -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) \
@ -3861,9 +3863,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 \
@ -4564,7 +4566,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

45
src/auto/configure vendored
View File

@ -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
@ -9273,11 +9277,37 @@ fi
if test "$with_wayland" = yes; then
cppflags_save=$CPPFLAGS
cflags_save=$CFLAGS
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for wayland" >&5
printf %s "checking for wayland... " >&6; }
if "$PKG_CONFIG" --exists 'wayland-client'; then
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
printf "%s\n" "yes" >&6; }
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking --enable-wayland-focus-steal argument" >&5
printf %s "checking --enable-wayland-focus-steal argument... " >&6; }
# Check whether --enable-wayland-focus-steal was given.
if test ${enable_wayland_focus_steal+y}
then :
enableval=$enable_wayland_focus_steal; enable_wayland_fs=$enableval
else case e in #(
e) enable_wayland_fs="yes" ;;
esac
fi
if test "$enable_wayland_fs" = "yes"
then :
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
printf "%s\n" "yes" >&6; }
printf "%s\n" "#define FEAT_WAYLAND_CLIPBOARD_FS 1" >>confdefs.h
else case e in #(
e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
printf "%s\n" "no" >&6; } ;;
esac
fi
printf "%s\n" "#define HAVE_WAYLAND 1" >>confdefs.h
WAYLAND_CPPFLAGS=`$PKG_CONFIG --cflags-only-I wayland-client`
@ -9288,16 +9318,23 @@ printf "%s\n" "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 :
WAYLAND_SRC+=" \
auto/wayland/xdg-shell.c \
auto/wayland/primary-selection-unstable-v1.c"
WAYLAND_OBJ+=" \
objects/xdg-shell.o \
objects/primary-selection-unstable-v1.o"
fi

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -2466,9 +2466,23 @@ AC_ARG_WITH(wayland,
if test "$with_wayland" = yes; then
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"],
[WAYLAND_SRC+=" \
auto/wayland/xdg-shell.c \
auto/wayland/primary-selection-unstable-v1.c"
WAYLAND_OBJ+=" \
objects/xdg-shell.o \
objects/primary-selection-unstable-v1.o"])
AC_SUBST(WAYLAND_CPPFLAGS)
AC_SUBST(WAYLAND_CFLAGS)
AC_SUBST(WAYLAND_LIBS)

View File

@ -7615,6 +7615,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},

View File

@ -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.
*/

View File

@ -2080,14 +2080,17 @@ EXTERN clipmethod_T clipmethod INIT(= CLIPMETHOD_NONE);
#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

View File

@ -682,16 +682,19 @@ 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
#ifdef FEAT_CLIPBOARD

View File

@ -4770,7 +4770,7 @@ did_set_winwidth(optset_T *args UNUSED)
char *
did_set_wlsteal(optset_T *args UNUSED)
{
wayland_cb_reload();
clip_reset_wayland();
return NULL;
}

View File

@ -1144,7 +1144,7 @@ EXTERN long p_wmw; // 'winminwidth'
EXTERN long p_wiw; // 'winwidth'
#ifdef FEAT_WAYLAND
EXTERN char_u *p_wse; // 'wlseat'
# ifdef FEAT_WAYLAND_CLIPBOARD
# ifdef FEAT_WAYLAND_CLIPBOARD_FS
EXTERN int p_wst; // 'wlsteal'
# endif
EXTERN long p_wtm; // 'wltimeoutlen'

View File

@ -3009,7 +3009,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

View File

@ -3689,7 +3689,7 @@ did_set_wlseat(optset_T *args UNUSED)
#ifdef FEAT_WAYLAND_CLIPBOARD
// If there isn't any seat named 'wlseat', then let the Wayland clipboard be
// unavailable. Ignore errors returned.
wayland_cb_reload();
clip_reset_wayland();
#endif
return NULL;

View File

@ -5731,7 +5731,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:
@ -5805,7 +5805,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
@ -6657,6 +6657,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];
@ -6700,11 +6703,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++;
}
@ -6768,13 +6771,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
@ -6867,14 +6866,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
@ -6974,13 +6972,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

View File

@ -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 : */

View File

@ -1,17 +1,27 @@
/* wayland.c */
int wayland_init_client(const char *display);
void wayland_uninit_client(void);
int wayland_client_is_connected(int quiet);
int wayland_client_update(void);
int wayland_cb_init(const char *seat);
void wayland_cb_uninit(void);
garray_T * wayland_cb_get_mime_types(wayland_selection_T selection);
int wayland_cb_receive_data(const char *mime_type, wayland_selection_T selection);
int wayland_cb_own_selection( wayland_cb_send_data_func_T send_cb, wayland_cb_selection_cancelled_func_T cancelled_cb, const char **mime_types, int len, wayland_selection_T selection);
void wayland_cb_lose_selection(wayland_selection_T selection);
int wayland_cb_selection_is_owned(wayland_selection_T selection);
int wayland_cb_is_ready(void);
int wayland_cb_reload(void);
int wayland_may_restore_connection(void);
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 : */

View File

@ -5237,20 +5237,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

View File

@ -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,17 +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
if v:exception =~ 'E240:'
thrclientserverow 'Skipped: no connection to the X server'
func s:CheckClientserver()
CheckFeature clientserver
if has('socketserver') && !has('x11')
if v:servername == ""
call remote_startserver('VIMSOCKETSERVER')
endif
endif
" ignore other errors
endtry
endfunc
func s:EndRemoteVim(name, job)
@ -77,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
@ -169,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
@ -219,8 +211,6 @@ endfunc
func Test_wayland_yank()
call s:PreTest()
wlrestore!
new
call setline(1, 'testing')
@ -265,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')
@ -360,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
@ -376,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'})
@ -408,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
@ -449,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
@ -489,15 +453,7 @@ func Test_wayland_become_inactive()
call WaitForAssert({-> assert_equal("Nothing is copied\n",
\ system('wl-paste -n'))})
eval remote_send(l:name, "\<Esc>:qa!\<CR>")
try
call WaitForAssert({-> assert_equal("dead", job_status(l:job))})
finally
if job_status(l:job) != 'dead'
call assert_report('Vim instance did not exit')
call job_stop(l:job, 'kill')
endif
endtry
call s:EndRemoteVim(l:name, l:job)
endfunc
" Test wlseat option
@ -528,6 +484,7 @@ endfunc
" Test focus stealing
func Test_wayland_focus_steal()
CheckFeature wayland_focus_steal
call s:PreTest()
call s:SetupFocusStealing()
@ -553,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, {
@ -577,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
@ -612,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')
@ -636,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
@ -652,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')
let l:file = tempname()
let l:contents = repeat('c', 1000000) " ~ 1 MB
edit data_file_cmp
call writefile([l:contents], l:file, 'b')
call system('cat ' .. l:file .. ' | wl-copy -t TEXT')
put! +
call assert_equal(l:contents, getreg('+'))
write
call setreg('+', l:contents, 'c')
call system('truncate -s -1 data_file_cmp') " Remove newline at the end
call system('cmp --silent data_file data_file_cmp')
call assert_equal(0, v:shell_error)
call 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

View File

@ -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 */
/**/
1725,
/**/
1724,
/**/

File diff suppressed because it is too large Load Diff

214
src/wayland.h Normal file
View File

@ -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