0
0
mirror of https://github.com/vim/vim.git synced 2025-11-15 23:14:06 -05:00

patch 9.1.1485: missing Wayland clipboard support

Problem:  missing Wayland clipboard support
Solution: make it work (Foxe Chen)

fixes: #5157
closes: #17097

Signed-off-by: Foxe Chen <chen.foxe@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
Foxe Chen
2025-06-27 21:10:35 +02:00
committed by Christian Brabandt
parent 03125277e9
commit b90c2395b2
68 changed files with 7520 additions and 226 deletions

View File

@@ -26,6 +26,12 @@
#include "os_unixx.h" // unix includes for os_unix.c only
#ifdef HAVE_SHM_OPEN
# include <sys/mman.h>
# include <sys/stat.h>
# include <fcntl.h>
#endif
#ifdef USE_XSMP
# include <X11/SM/SMlib.h>
#endif
@@ -135,7 +141,6 @@ static void sig_sysmouse SIGPROTOARG;
# include <X11/StringDefs.h>
static Widget xterm_Shell = (Widget)0;
static void clip_update(void);
static void xterm_update(void);
# endif
Window x11_window = 0;
@@ -1303,33 +1308,43 @@ sigcont_handler SIGDEFARG(sigarg)
}
#endif
#if defined(FEAT_CLIPBOARD) && defined(FEAT_X11)
# ifdef USE_SYSTEM
#if defined(FEAT_CLIPBOARD)
# if defined(USE_SYSTEM) && (defined(FEAT_X11) \
|| defined(FEAT_WAYLAND_CLIPBOARD))
static void *clip_star_save = NULL;
static void *clip_plus_save = NULL;
# endif
# if defined(FEAT_CLIPBOARD) && (defined(FEAT_X11) \
|| defined(FEAT_WAYLAND_CLIPBOARD))
/*
* Called when Vim is going to sleep or execute a shell command.
* We can't respond to requests for the X selections. Lose them, otherwise
* other applications will hang. But first copy the text to cut buffer 0.
* We can't respond to requests for the X or wayland selections.
* Lose them, otherwise other applications will hang. But first
* copy the text to cut buffer 0 (for X11). Wayland users must have
* a clipboard manager to replicate such behaviour.
*/
static void
loose_clipboard(void)
{
if (clip_star.owned || clip_plus.owned)
{
#ifdef FEAT_X11
x11_export_final_selection();
#endif
if (clip_star.owned)
clip_lose_selection(&clip_star);
if (clip_plus.owned)
clip_lose_selection(&clip_plus);
#ifdef FEAT_X11
if (x11_display != NULL)
XFlush(x11_display);
#endif
}
}
#endif
# ifdef USE_SYSTEM
# if defined(USE_SYSTEM) && (defined(FEAT_X11) || defined(FEAT_WAYLAND_CLIPBOARD))
/*
* Save clipboard text to restore later.
*/
@@ -1343,7 +1358,7 @@ save_clipboard(void)
}
/*
* Restore clipboard text if no one own the X selection.
* Restore clipboard text if no one own the X/Wayland selection.
*/
static void
restore_clipboard(void)
@@ -1385,7 +1400,8 @@ mch_suspend(void)
settmode(TMODE_COOK);
out_flush(); // needed to disable mouse on some systems
# if defined(FEAT_CLIPBOARD) && defined(FEAT_X11)
# if defined(FEAT_CLIPBOARD) && (defined(FEAT_X11) \
|| defined(FEAT_WAYLAND_CLIPBOARD))
loose_clipboard();
# endif
# if defined(SIGCONT)
@@ -1810,7 +1826,7 @@ x_IOerror_handler(Display *dpy UNUSED)
* (e.g. through tmux).
*/
static void
may_restore_clipboard(void)
may_restore_x11_clipboard(void)
{
// No point in restoring the connecting if we are exiting or dying.
if (!exiting && !v_dying && xterm_dpy_retry_count > 0)
@@ -1844,13 +1860,14 @@ ex_xrestore(exarg_T *eap)
xterm_display = (char *)vim_strnsave(eap->arg, arglen);
xterm_display_allocated = TRUE;
}
smsg(_("restoring display %s"), xterm_display == NULL
smsg(_("restoring X11 display %s"), xterm_display == NULL
? (char *)mch_getenv((char_u *)"DISPLAY") : xterm_display);
clear_xterm_clip();
x11_window = 0;
xterm_dpy_retry_count = 5; // Try reconnecting five times
may_restore_clipboard();
may_restore_x11_clipboard();
choose_clipmethod();
}
#endif
@@ -4836,8 +4853,11 @@ mch_call_shell_system(
if (options & SHELL_COOKED)
settmode(TMODE_COOK); // set to normal mode
# if defined(FEAT_CLIPBOARD) && defined(FEAT_X11)
# if defined(FEAT_CLIPBOARD) && (defined(FEAT_X11) \
|| defined(FEAT_WAYLAND_CLIPBOARD))
# if defined(FEAT_X11) || defined(FEAT_WAYLAND_CLIPBOARD)
save_clipboard();
#endif
loose_clipboard();
# endif
@@ -4899,7 +4919,8 @@ mch_call_shell_system(
settmode(TMODE_RAW); // set to raw mode
}
resettitle();
# if defined(FEAT_CLIPBOARD) && defined(FEAT_X11)
# if defined(FEAT_CLIPBOARD) && (defined(FEAT_X11) \
|| defined(FEAT_WAYLAND_CLIPBOARD))
restore_clipboard();
# endif
return x;
@@ -5168,7 +5189,7 @@ mch_call_shell_fork(
* different on different machines. This may cause a warning
* message with strict compilers, don't worry about it.
* Call _exit() instead of exit() to avoid closing the connection
* to the X server (esp. with GTK, which uses atexit()).
* to the X/Wayland server (esp. with GTK, which uses atexit()).
*/
execvp(argv[0], argv);
_exit(EXEC_FAILED); // exec failed, return failure code
@@ -5586,6 +5607,11 @@ mch_call_shell_fork(
// Handle any X events, e.g. serving the clipboard.
clip_update();
# endif
#ifdef FEAT_WAYLAND
// Handle wayland events such as sending data as the source
// client.
wayland_client_update();
#endif
}
finished:
p_more = p_more_save;
@@ -5612,7 +5638,7 @@ finished:
close(toshell_fd);
close(fromshell_fd);
}
# if defined(FEAT_XCLIPBOARD) && defined(FEAT_X11)
# if (defined(FEAT_XCLIPBOARD) && defined(FEAT_X11)) || defined(FEAT_WAYLAND)
else
{
long delay_msec = 1;
@@ -5623,8 +5649,8 @@ finished:
out_str_t_TE();
/*
* Similar to the loop above, but only handle X events, no
* I/O.
* Similar to the loop above, but only handle X and Wayland
* events, no I/O.
*/
for (;;)
{
@@ -5651,8 +5677,15 @@ finished:
break;
}
#if defined(FEAT_XCLIPBOARD) && defined(FEAT_X11)
// Handle any X events, e.g. serving the clipboard.
clip_update();
#endif
#ifdef FEAT_WAYLAND
// Handle wayland events such as sending data as the source
// client.
wayland_client_update();
#endif
// Wait for 1 to 10 msec. 1 is faster but gives the child
// less time, gradually wait longer.
@@ -6505,8 +6538,11 @@ RealWaitForChar(int fd, long msec, int *check_for_gpm UNUSED, int *interrupted)
#endif
#ifndef HAVE_SELECT
// each channel may use in, out and err
struct pollfd fds[6 + 3 * MAX_OPEN_CHANNELS];
struct pollfd fds[7 + 3 * MAX_OPEN_CHANNELS];
int nfd;
# ifdef FEAT_WAYLAND_CLIPBOARD
int wayland_idx = -1;
# endif
# ifdef FEAT_XCLIPBOARD
int xterm_idx = -1;
# endif
@@ -6530,6 +6566,15 @@ RealWaitForChar(int fd, long msec, int *check_for_gpm UNUSED, int *interrupted)
fds[0].events = POLLIN;
nfd = 1;
# ifdef FEAT_WAYLAND_CLIPBOARD
if (wayland_may_restore_connection())
{
wayland_idx = nfd;
fds[nfd].fd = vwl_display_fd;
fds[nfd].events = POLLIN;
nfd++;
}
# endif
# ifdef FEAT_XCLIPBOARD
may_restore_clipboard();
if (xterm_Shell != (Widget)0)
@@ -6558,9 +6603,9 @@ RealWaitForChar(int fd, long msec, int *check_for_gpm UNUSED, int *interrupted)
nfd++;
}
# endif
#ifdef FEAT_JOB_CHANNEL
# ifdef FEAT_JOB_CHANNEL
nfd = channel_poll_setup(nfd, &fds, &towait);
#endif
# endif
if (interrupted != NULL)
*interrupted = FALSE;
@@ -6576,6 +6621,15 @@ RealWaitForChar(int fd, long msec, int *check_for_gpm UNUSED, int *interrupted)
finished = FALSE;
# endif
# ifdef FEAT_WAYLAND_CLIPBOARD
// Technically we should first call wl_display_prepare_read() before
// polling the fd, then read and dispatch after we poll. However that is
// only needed for multi threaded environments to prevent deadlocks so
// we are fine.
if (fds[wayland_idx].revents & POLLIN)
wayland_client_update();
# endif
# ifdef FEAT_XCLIPBOARD
if (xterm_Shell != (Widget)0 && (fds[xterm_idx].revents & POLLIN))
{
@@ -6608,11 +6662,11 @@ RealWaitForChar(int fd, long msec, int *check_for_gpm UNUSED, int *interrupted)
finished = FALSE; // Try again
}
# endif
#ifdef FEAT_JOB_CHANNEL
# ifdef FEAT_JOB_CHANNEL
// also call when ret == 0, we may be polling a keep-open channel
if (ret >= 0)
channel_poll_check(ret, &fds);
#endif
# endif
#else // HAVE_SELECT
@@ -6656,8 +6710,19 @@ select_eintr:
# endif
maxfd = fd;
# ifdef FEAT_WAYLAND_CLIPBOARD
if (wayland_may_restore_connection())
{
FD_SET(wayland_display_fd, &rfds);
if (maxfd < wayland_display_fd)
maxfd = wayland_display_fd;
}
# endif
# ifdef FEAT_XCLIPBOARD
may_restore_clipboard();
may_restore_x11_clipboard();
if (xterm_Shell != (Widget)0)
{
FD_SET(ConnectionNumber(xterm_dpy), &rfds);
@@ -6745,6 +6810,15 @@ select_eintr:
finished = FALSE;
# endif
# ifdef FEAT_WAYLAND_CLIPBOARD
// Technically we should first call wl_display_prepare_read() before
// polling the fd, then read and dispatch after we poll. However that is
// only needed for multi threaded environments to prevent deadlocks so
// we are fine.
if (ret > 0 && FD_ISSET(wayland_display_fd, &rfds))
wayland_client_update();
# endif
# ifdef FEAT_XCLIPBOARD
if (ret > 0 && xterm_Shell != (Widget)0
&& FD_ISSET(ConnectionNumber(xterm_dpy), &rfds))
@@ -6789,11 +6863,11 @@ select_eintr:
}
}
# endif
#ifdef FEAT_JOB_CHANNEL
# ifdef FEAT_JOB_CHANNEL
// also call when ret == 0, we may be polling a keep-open channel
if (ret >= 0)
(void)channel_select_check(ret, &rfds, &wfds);
#endif
# endif
#endif // HAVE_SELECT
@@ -8295,7 +8369,7 @@ clip_update(void)
* nothing in the X event queue (& no timers pending), then we return
* immediately.
*/
static void
void
xterm_update(void)
{
XEvent event;
@@ -8846,3 +8920,33 @@ start_timeout(long msec)
}
# endif // PROF_NSEC
#endif // FEAT_RELTIME
/*
* Create an anonymous/temporary file/object and return its file descriptor.
* Returns -1 on error.
*/
int
mch_create_anon_file(void)
{
int fd = -1;
#ifdef HAVE_SHM_OPEN
const char template[] = "/vimXXXXXX";
for (int i = 0; i < 100; i++)
{
mch_get_random((char_u*)template + 4, 6);
errno = 0;
fd = shm_open(template, O_CREAT | O_RDWR | O_EXCL, 0600);
if (fd >= 0 || errno != EEXIST)
break; }
// Remove object name from namespace
shm_unlink(template);
#endif
if (fd == -1)
// Last resort
fd = fileno(tmpfile());
return fd;
}