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>
diff --git a/src/os_unix.c b/src/os_unix.c
index f5dea37..bf07b38 100644
--- a/src/os_unix.c
+++ b/src/os_unix.c
@@ -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 @@
 #  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 @@
 }
 #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 @@
 }
 
 /*
- * 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 @@
     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 @@
  * (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 @@
 	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 @@
     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 @@
 	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 @@
 	     * 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 @@
 		    // 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 @@
 		    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 @@
 		    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 @@
 			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 @@
 #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 @@
 	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 @@
 	    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 @@
 	    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 @@
 		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 @@
 # 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 @@
 	    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 @@
 	    }
 	}
 # 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 @@
  * 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 @@
 }
 # 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;
+}