patch 9.1.1651: Cannot use clientserver over socket

Commit: 
https://github.com/vim/vim/commit/96b2154b73f1c0e607c237bd366bdb7f50320b83
Author: Foxe Chen <[email protected]>
Date:   Mon Aug 18 21:40:40 2025 +0200

    patch 9.1.1651: Cannot use clientserver over socket
    
    Problem:  Cannot use clientserver over Unix domain socket
    Solution: Implement socketserver functionality (Foxe Chen).
    
    fixes: #3509
    closes: #17839
    
    Signed-off-by: Foxe Chen <[email protected]>
    Signed-off-by: Christian Brabandt <[email protected]>

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index af9e575fb..c6f32b080 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -85,6 +85,12 @@ jobs:
           - features: normal
             compiler: gcc
             extra: [vimtags]
+          - features: huge
+            compiler: gcc
+            extra: [no_x11]
+          - features: huge
+            compiler: gcc
+            extra: [socketserver]
 
     steps:
       - name: Checkout repository from github
@@ -220,7 +226,7 @@ jobs:
           tiny)
             echo "TEST=testtiny"
             if ${{ contains(matrix.extra, 'nogui') }}; then
-              echo "CONFOPT=--disable-gui"
+              CONFOPT="--disable-gui"
             fi
             ;;
           normal)
@@ -232,10 +238,16 @@ jobs:
               PYTHON3_CONFOPT="--with-python3-stable-abi=3.8"
             fi
             # The ubuntu-24.04 CI runner does not provide a python2 package.
-            echo "CONFOPT=--enable-perlinterp=${INTERFACE} 
--enable-pythoninterp=no --enable-python3interp=${INTERFACE} 
--enable-rubyinterp=${INTERFACE} --enable-luainterp=${INTERFACE} 
--enable-tclinterp=${INTERFACE} ${PYTHON3_CONFOPT}"
+            CONFOPT="--enable-perlinterp=${INTERFACE} --enable-pythoninterp=no 
--enable-python3interp=${INTERFACE} --enable-rubyinterp=${INTERFACE} 
--enable-luainterp=${INTERFACE} --enable-tclinterp=${INTERFACE} 
${PYTHON3_CONFOPT}"
             ;;
           esac
 
+          if ${{ contains(matrix.extra, 'no_x11') }}; then
+            CONFOPT="${CONFOPT} --without-x --disable-gui"
+          fi
+          if ${{ contains(matrix.extra, 'socketserver') }}; then
+            CONFOPT="${CONFOPT} --enable-socketserver"
+          fi
           if ${{ matrix.coverage == true }}; then
             CFLAGS="${CFLAGS} --coverage -DUSE_GCOV_FLUSH"
             echo "LDFLAGS=--coverage"
@@ -259,6 +271,7 @@ jobs:
             echo "TEST=-C runtime/doc vimtags VIMEXE=../../${SRCDIR}/vim"
           fi
           echo "CFLAGS=${CFLAGS}"
+          echo "CONFOPT=${CONFOPT}"
           # Disables GTK attempt to integrate with the accessibility service 
that does run in CI.
           echo "NO_AT_BRIDGE=1"
           ) >> $GITHUB_ENV
diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt
index d82b4e69b..44818dd59 100644
--- a/runtime/doc/builtin.txt
+++ b/runtime/doc/builtin.txt
@@ -13046,6 +13046,7 @@ scrollbind              Compiled with 'scrollbind' 
support. (always true)
 showcmd                        Compiled with 'showcmd' support.
 signs                  Compiled with |:sign| support.
 smartindent            Compiled with 'smartindent' support. (always true)
+socketserver           Compiled with socket server functionality. (Unix only)
 sodium                 Compiled with libsodium for better crypt support
 sound                  Compiled with sound support, e.g. `sound_playevent()`
 spell                  Compiled with spell checking support |spell|.
diff --git a/runtime/doc/remote.txt b/runtime/doc/remote.txt
index 5a6898c25..cf17e3549 100644
--- a/runtime/doc/remote.txt
+++ b/runtime/doc/remote.txt
@@ -1,4 +1,4 @@
-*remote.txt*    For Vim version 9.1.  Last change: 2022 Feb 17
+*remote.txt*    For Vim version 9.1.  Last change: 2025 Aug 18
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -61,7 +61,10 @@ The following command line arguments are available:
    --servername {name}         Become the server {name}.  When used together
                                with one of the --remote commands: connect to
                                server {name} instead of the default (see
-                               below).  The name used will be uppercase.
+                               below).  The name used will be uppercase.  If
+                               using the socketserver, you can specify a
+                               path, see |socketserver-name| for more
+                               details.
                                                                *--remote-send*
    --remote-send {keys}                Send {keys} to server and exit.  The 
{keys}
                                are not mapped.  Special key names are
@@ -72,6 +75,12 @@ The following command line arguments are available:
                                on stdout.
                                                                *--serverlist*
    --serverlist                        Output a list of server names.
+                                                               *--clientserver*
+   --clientserver {method}     Use the specified method {method} as the
+                               backend for clientserver functionality. Can
+                               either be "socket" or "x11".
+                               {only available when Vim is compiled with both
+                               |+X11| and |+socketserver| features}
 
 
 Examples ~
@@ -105,7 +114,8 @@ specified name is not available, a postfix is applied until 
a free name is
 encountered, i.e. "gvim1" for the second invocation of gvim on a particular
 X-server.  The resulting name is available in the servername builtin variable
 |v:servername|.  The case of the server name is ignored, thus "gvim" and
-"GVIM" are considered equal.
+"GVIM" are considered equal.  Note if a socket server is being used, there are
+some differences, see |socketserver-differences|.
 
 When Vim is invoked with --remote, --remote-wait or --remote-send it will try
 to locate the server name determined by the invocation name and --servername
@@ -119,7 +129,8 @@ itself.  This way it is not necessary to know whether gvim 
is already started
 when sending command to it.
 
 The --serverlist argument will cause Vim to print a list of registered command
-servers on the standard output (stdout) and exit.
+servers on the standard output (stdout) and exit.  If a socket server is being
+used, there are caveats, see |socketserver-differences|.
                                                        *{server}*
 The {server} argument is used by several functions.  When this is an empty
 string then on Unix the default server name is used, which is "GVIM".  On
@@ -206,4 +217,64 @@ When using gvim, the --remote-wait only works properly 
this way: >
 
        start /w gvim --remote-wait file.txt
 <
+==============================================================================
+3. Socket server specific items                            
*socketserver-clientserver*
+                                   *E1563* *E1564* *E1565* *E1566* *E1567*
+
+The communication between client and server is done using Unix domain sockets.
+These sockets are either placed in these directories in the following order of
+availability:
+    1. "$XDG_RUTIME_DIR/vim" if $XDG_RUNTIME_DIR is set in the environment.
+    2. "$TMPDIR/vim-[uid]", where "[uid]" is the uid of the user. This
+       directory will have the access permissions set to 700 so only the user
+       can read or write from/to it. If $TMPDIR is not set, "/tmp" is used.
+
+                                                   *socketserver-name*
+When specifying the server id/name, it can be taken as a generic name or an
+absolute or relative path.  If the server id starts with either a "/"
+(absolute) or "./" | "../" (relative), then it is taken as path to the socket.
+Otherwise the server id will be the filename of the socket which will be
+placed in the above common directories.  Note that a server id/name can only
+contain slashes "/" if it is taken as a path, so names such as "abc/dir" will
+be invalid.
+
+Socket server functionality is available in both GTK GUI and terminal versions 
of
+Vim.  Unless Vim is compiled with |+autoservername| feature, the socket server
+will have to started explicitly, just like X11, even in the GUI.
+
+If Vim crashes or does not exit cleanly, the socket server will not remove the
+socket file and it will be left around.  This is generally not a problem,
+because if a socket name is taken, Vim checks if the socket in its place is
+dead (not attached to any process), and can replace it instead of finding a
+new name.
+
+To send commands to a Vim socket server from another application, read the
+source file src/os_unix.c, there is detailed description of the protocol used.
+
+                                                   *socketserver-differences*
+Most of the functionality is the same as X11, however unlike X11, where the
+client does not need to be a server in order to communicate with another
+server, the socket server requires the server to be running even as a client.
+The exception is |serverlist()| or the |--serverlist| argument, which does not
+require the server to be running.
+
+Additionally, the server id or client id will not be a number like X11 or
+MS-Windows (shown in hex representation), instead it is the absolute path to
+the socket.  This can be seen via the |v:servername| variable.
+
+The |--serverlist| argument will act just like X11, however it only checks the
+given common directories above.  If a custom path is used for a socket, it
+will not be detected, such as a path either not in $XDG_RUNTIME_DIR or
+<$TMPDIR or /tmp>/vim of the |--serverlist| Vim process.
+
+If you have both |+socketserver| and |+X11| compiled, you will need to add
+|--clientserver| set to "socket" in combination with |--serverlist| to list
+the available servers.  You cannot list both types of backends in one command.
+
+                                                   *socketserver-x11*
+If Vim is compiled with both |+X11| and |+socketserver|, then deciding which
+backend to use is done at startup time, via the |--clientserver| argument.  By
+default if it is not specified, then X11 will be used.  A Vim instance using a
+socket server cannot communicate with one using X11.
+
  vim:tw=78:sw=4:ts=8:noet:ft=help:norl:
diff --git a/runtime/doc/tags b/runtime/doc/tags
index 63c1506f1..fa3382f28 100644
--- a/runtime/doc/tags
+++ b/runtime/doc/tags
@@ -1497,6 +1497,7 @@ $quote    eval.txt        /*$quote*
 +scrollbind    various.txt     /*+scrollbind*
 +signs various.txt     /*+signs*
 +smartindent   various.txt     /*+smartindent*
++socketserver  various.txt     /*+socketserver*
 +sodium        various.txt     /*+sodium*
 +sound various.txt     /*+sound*
 +spell various.txt     /*+spell*
@@ -1557,6 +1558,7 @@ $quote    eval.txt        /*$quote*
 --     starting.txt    /*--*
 ---    starting.txt    /*---*
 --clean        starting.txt    /*--clean*
+--clientserver remote.txt      /*--clientserver*
 --cmd  starting.txt    /*--cmd*
 --echo-wid     starting.txt    /*--echo-wid*
 --gui-dialog-file      starting.txt    /*--gui-dialog-file*
@@ -4697,6 +4699,11 @@ E156     sign.txt        /*E156*
 E1560  vim9.txt        /*E1560*
 E1561  vim9.txt        /*E1561*
 E1562  options.txt     /*E1562*
+E1563  remote.txt      /*E1563*
+E1564  remote.txt      /*E1564*
+E1565  remote.txt      /*E1565*
+E1566  remote.txt      /*E1566*
+E1567  remote.txt      /*E1567*
 E157   sign.txt        /*E157*
 E158   sign.txt        /*E158*
 E159   sign.txt        /*E159*
@@ -10225,6 +10232,10 @@ slow-fast-terminal     term.txt        
/*slow-fast-terminal*
 slow-start     starting.txt    /*slow-start*
 slow-terminal  term.txt        /*slow-terminal*
 socket-interface       channel.txt     /*socket-interface*
+socketserver-clientserver      remote.txt      /*socketserver-clientserver*
+socketserver-differences       remote.txt      /*socketserver-differences*
+socketserver-name      remote.txt      /*socketserver-name*
+socketserver-x11       remote.txt      /*socketserver-x11*
 sort() builtin.txt     /*sort()*
 sorting        change.txt      /*sorting*
 sound-functions        usr_41.txt      /*sound-functions*
diff --git a/runtime/doc/various.txt b/runtime/doc/various.txt
index 7ace61f1e..9d631ad92 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 Aug 06
+*various.txt*   For Vim version 9.1.  Last change: 2025 Aug 18
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -487,6 +487,8 @@ m  *+ruby/dyn*              Ruby interface |ruby-dynamic| 
|/dyn|
 T  *+scrollbind*       'scrollbind'
 N  *+signs*            |:sign|
 T  *+smartindent*      'smartindent'
+N  *+socketserver*     Unix only: socket server backend for clientserver
+                       functionality
 H  *+sodium*           compiled with libsodium for better encryption support
 H  *+sound*            |sound_playevent()|, |sound_playfile()| functions, etc.
 N  *+spell*            spell checking support, see |spell|
diff --git a/runtime/doc/version9.txt b/runtime/doc/version9.txt
index 6dcce6bec..d69afab2e 100644
--- a/runtime/doc/version9.txt
+++ b/runtime/doc/version9.txt
@@ -41745,6 +41745,10 @@ Others: ~
   Unicode 16.
 - Two additional digraphs have been added: LEFT ANGLE BRACKET "<[" and RIGHT
   ANGLE BRACKET "]>".
+- Support for Unix domain sockets have been added for the clientserver
+  feature, see |socketserver-clientserver|.
+
+Platform specific ~
 - MS-Winodws: Paths like "\Windows" and "/Windows" are now considered to be
   absolute paths (to the current drive) and no longer relative.
 
@@ -41849,6 +41853,7 @@ Options: ~
 
 Vim Arguments: ~
 |-Y|                   Do not connect to the Wayland compositor.
+|--clientserver|       Specify backend  for clientserver functionality.
 
 
 ==============================================================================
diff --git a/runtime/doc/vim.1 b/runtime/doc/vim.1
index 1371cb1fe..df39fb2b2 100644
--- a/runtime/doc/vim.1
+++ b/runtime/doc/vim.1
@@ -499,7 +499,14 @@ List the names of all Vim servers that can be found.
 .TP
 \-\-servername {name}
 Use {name} as the server name.  Used for the current Vim, unless used with a
-\-\-remote argument, then it's the name of the server to connect to.
+\-\-remote argument, then it's the name of the server to connect to.  If the
+socketserver backend is being used, if the name starts with "/", "./", or 
"../",
+it is taken as either an absolute, relative or relative path to the socket.
+.TP
+\-\-clientserver {backend}
+Use {backend} as the backend for clientserver functionality, either "socket" or
+"x11" respectively.  Only available when compiled with both socketserver and 
X11
+features present
 .TP
 \-\-socketid {id}
 GTK GUI only: Use the GtkPlug mechanism to run gVim in another window.
diff --git a/runtime/doc/vim.man b/runtime/doc/vim.man
index 6d9cfe626..c1484bebb 100644
--- a/runtime/doc/vim.man
+++ b/runtime/doc/vim.man
@@ -378,7 +378,16 @@ OPTIONS
        --servername {name}
                    Use  {name}  as the server name.  Used for the current Vim,
                    unless used with a --remote argument, then it's the name of
-                   the server to connect to.
+                   the server to connect to.  If the socketserver  backend  is
+                   being used, if the name starts with "/", "./", or "../", it
+                   is  taken  as either an absolute, relative or relative path
+                   to the socket.
+
+       --clientserver {backend}
+                   Use {backend} as the backend for  clientserver  functional‐
+                   ity, either "socket" or "x11" respectively.  Only available
+                   when  compiled  with  both  socketserver  and  X11 features
+                   present
 
        --socketid {id}
                    GTK GUI only: Use the GtkPlug mechanism to run gVim in  an‐
diff --git a/runtime/plugin/rrhelper.vim b/runtime/plugin/rrhelper.vim
index b09cbc10b..5e23eef1b 100644
--- a/runtime/plugin/rrhelper.vim
+++ b/runtime/plugin/rrhelper.vim
@@ -16,9 +16,10 @@ function SetupRemoteReplies()
   let max = argc()
 
   let id = expand("<client>")
-  if id == 0
+  if (type(id) == v:t_number && id == 0) || (type(id) == v:t_string && id == 
'')
     return
   endif
+
   while cnt < max
     " Handle same file from more clients and file being more than once
     " on the command line by encoding this stuff in the group name
diff --git a/runtime/syntax/vim.vim b/runtime/syntax/vim.vim
index cf7f1a43a..eab86d27e 100644
--- a/runtime/syntax/vim.vim
+++ b/runtime/syntax/vim.vim
@@ -2,7 +2,7 @@
 " Language:       Vim script
 " Maintainer:     Hirohito Higashi <h.east.727 ATMARK gmail.com>
 "         Doug Kearns <[email protected]>
-" Last Change:    2025 Aug 16
+" Last Change:    2025 Aug 18
 " Former Maintainer: Charles E. Campbell
 
 " DO NOT CHANGE DIRECTLY.
diff --git a/src/auto/configure b/src/auto/configure
index 06c3ca3a0..44ce30704 100755
--- a/src/auto/configure
+++ b/src/auto/configure
@@ -854,6 +854,7 @@ enable_netbeans
 enable_channel
 enable_terminal
 enable_autoservername
+enable_socketserver
 enable_multibyte
 enable_rightleft
 enable_arabic
@@ -1534,6 +1535,7 @@ Optional Features:
   --disable-channel       Disable process communication support.
   --enable-terminal       Enable terminal emulation support.
   --enable-autoservername Automatically define servername at vim startup.
+  --enable-socketserver Use sockets for clientserver communication.
   --enable-multibyte      Include multibyte editing support.
   --disable-rightleft     Do not include Right-to-Left language support.
   --disable-arabic        Do not include Arabic language support.
@@ -9084,6 +9086,40 @@ if test "$enable_autoservername" = "yes"; then
 
 fi
 
+{ printf "%s
" "$as_me:${as_lineno-$LINENO}: checking --enable-socketserver argument" >&5
+printf %s "checking --enable-socketserver argument... " >&6; }
+# Check whether --enable-socketserver was given.
+if test ${enable_socketserver+y}
+then :
+  enableval=$enable_socketserver; enable_socketserver=$enableval
+else case e in #(
+  e) if test "x$features" = xtiny
+then :
+  enable_socketserver=no_auto
+                        { printf "%s
" "$as_me:${as_lineno-$LINENO}: result: cannot use socketserver with tiny 
features" >&5
+printf "%s
" "cannot use socketserver with tiny features" >&6; }
+else case e in #(
+  e) enable_socketserver=auto ;;
+esac
+fi ;;
+esac
+fi
+
+if test "$enable_socketserver" = "yes"; then
+  printf "%s
" "#define WANT_SOCKETSERVER 1" >>confdefs.h
+
+  { printf "%s
" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s
" "yes" >&6; }
+elif test "$enable_socketserver" = "auto"; then
+  printf "%s
" "#define MAYBE_SOCKETSERVER 1" >>confdefs.h
+
+  { printf "%s
" "$as_me:${as_lineno-$LINENO}: result: auto" >&5
+printf "%s
" "auto" >&6; }
+elif test "$enable_socketserver" = "no"; then
+  { printf "%s
" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s
" "no" >&6; }
+fi
+
 { printf "%s
" "$as_me:${as_lineno-$LINENO}: checking --enable-multibyte argument" >&5
 printf %s "checking --enable-multibyte argument... " >&6; }
 # Check whether --enable-multibyte was given.
diff --git a/src/clientserver.c b/src/clientserver.c
index b19cd447f..bc3fe2d3d 100644
--- a/src/clientserver.c
+++ b/src/clientserver.c
@@ -15,6 +15,11 @@
 
 #if defined(FEAT_CLIENTSERVER) || defined(PROTO)
 
+#ifdef FEAT_SOCKETSERVER
+# include <sys/socket.h>
+# include "sys/un.h"
+#endif
+
 static void cmdsrv_main(int *argc, char **argv, char_u *serverName_arg, char_u 
**serverStr);
 static char_u *serverMakeName(char_u *arg, char *cmd);
 
@@ -191,6 +196,8 @@ static char_u *build_drop_cmd(int filec, char **filev, int 
tabs, int sendReply);
     void
 exec_on_server(mparm_T *parmp)
 {
+    int made_name = FALSE;
+
     if (parmp->serverName_arg != NULL && *parmp->serverName_arg == NUL)
        return;
 
@@ -199,6 +206,23 @@ exec_on_server(mparm_T *parmp)
     serverInitMessaging();
 # endif
 
+#ifdef FEAT_SOCKETSERVER
+    // If servername is specified and we are using sockets, always init the
+    // sockt server. We may need to receive replies back to us. If --serverlist
+    // is passed, the socket server will be uninitialized before listing
+    // sockets then initialized after. This is so we don't add our own socket
+    // in the list. This does not happen in serverlist().
+    if ((parmp->serverArg || parmp->serverName_arg != NULL) &&
+           clientserver_method == CLIENTSERVER_METHOD_SOCKET)
+    {
+       parmp->servername = serverMakeName(parmp->serverName_arg,
+               parmp->argv[0]);
+       if (socket_server_init(parmp->servername) == OK)
+           TIME_MSG("initialize socket server");
+       made_name = TRUE;
+    }
+#endif
+
     /*
      * When a command server argument was found, execute it.  This may
      * exit Vim when it was successful.  Otherwise it's executed further
@@ -214,8 +238,9 @@ exec_on_server(mparm_T *parmp)
     // If we're still running, get the name to register ourselves.
     // On Win32 can register right now, for X11 need to setup the
     // clipboard first, it's further down.
-    parmp->servername = serverMakeName(parmp->serverName_arg,
-           parmp->argv[0]);
+    if (!made_name && parmp->servername == NULL)
+       parmp->servername = serverMakeName(parmp->serverName_arg,
+               parmp->argv[0]);
 # ifdef MSWIN
     if (parmp->servername != NULL)
     {
@@ -224,14 +249,13 @@ exec_on_server(mparm_T *parmp)
     }
 # endif
 }
-
 /*
  * Prepare for running as a Vim server.
  */
     void
 prepare_server(mparm_T *parmp)
 {
-# if defined(FEAT_X11)
+# if defined(FEAT_X11) || defined(FEAT_SOCKETSERVER)
     /*
      * Register for remote command execution with :serversend and --remote
      * unless there was a -X or a --servername '' on the command line.
@@ -239,7 +263,13 @@ prepare_server(mparm_T *parmp)
      * or when compiling with autoservername.
      * When running as root --servername is also required.
      */
-    if (X_DISPLAY != NULL && parmp->servername != NULL && (
+
+    if (
+#  ifdef FEAT_X11
+           X_DISPLAY != NULL &&
+#  endif
+
+           parmp->servername != NULL && (
 #  if defined(FEAT_AUTOSERVERNAME) || defined(FEAT_GUI)
                (
 #   if defined(FEAT_AUTOSERVERNAME)
@@ -254,12 +284,26 @@ prepare_server(mparm_T *parmp)
 #  endif
                parmp->serverName_arg != NULL))
     {
-       (void)serverRegisterName(X_DISPLAY, parmp->servername);
+#  ifdef FEAT_SOCKETSERVER
+       if (clientserver_method == CLIENTSERVER_METHOD_SOCKET)
+       {
+           if (socket_server_init(parmp->servername) == OK)
+               TIME_MSG("initialize socket server");
+       }
+#  endif
+#  ifdef FEAT_X11
+       if (clientserver_method == CLIENTSERVER_METHOD_X11)
+       {
+           (void)serverRegisterName(X_DISPLAY, parmp->servername);
+           TIME_MSG("register x11 server name");
+       }
+#  endif
        vim_free(parmp->servername);
-       TIME_MSG("register server name");
     }
+#ifdef FEAT_X11
     else
        serverDelayedStartName = parmp->servername;
+#endif
 # endif
 
     /*
@@ -299,9 +343,12 @@ cmdsrv_main(
 #define ARGTYPE_SEND           3
     int                silent = FALSE;
     int                tabs = FALSE;
-# ifndef FEAT_X11
+#ifdef FEAT_SOCKETSERVER
+    char_u     *receiver;
+#endif
+# ifdef MSWIN
     HWND       srv;
-# else
+# elif defined(FEAT_X11)
     Window     srv;
 
     setup_term_clip();
@@ -384,16 +431,27 @@ cmdsrv_main(
                }
                Argc = i;
            }
-# ifdef FEAT_X11
-           if (xterm_dpy == NULL)
+
+#ifdef FEAT_SOCKETSERVER
+           if (clientserver_method == CLIENTSERVER_METHOD_SOCKET)
+               ret = socket_server_send(
+                       sname, *serverStr, NULL, &receiver,
+                       0, -1, silent);
+#endif
+#ifdef FEAT_X11
+           if (clientserver_method == CLIENTSERVER_METHOD_X11)
            {
-               mch_errmsg(_("No display"));
-               ret = -1;
+               if (xterm_dpy == NULL)
+               {
+                   mch_errmsg(_("No display"));
+                   ret = -1;
+               }
+               else
+                   ret = serverSendToVim(xterm_dpy, sname, *serverStr,
+                           NULL, &srv, 0, 0, 0, silent);
            }
-           else
-               ret = serverSendToVim(xterm_dpy, sname, *serverStr,
-                                                 NULL, &srv, 0, 0, 0, silent);
-# else
+#endif
+#ifdef MSWIN
            // Win32 always works?
            ret = serverSendToVim(sname, *serverStr, NULL, &srv, 0, 0, silent);
 # endif
@@ -452,14 +510,24 @@ cmdsrv_main(
                vim_memset(done, 0, numFiles);
                while (memchr(done, 0, numFiles) != NULL)
                {
-                   char_u  *p;
+                   char_u  *p = NULL;
                    int     j;
 # ifdef MSWIN
                    p = serverGetReply(srv, NULL, TRUE, TRUE, 0);
                    if (p == NULL)
                        break;
 # else
-                   if (serverReadReply(xterm_dpy, srv, &p, TRUE, -1) < 0)
+#  ifdef FEAT_SOCKETSERVER
+                   if (clientserver_method == CLIENTSERVER_METHOD_SOCKET
+                           && socket_server_read_reply(receiver, &p, -1) == 
FAIL)
+                           break;
+#  endif
+#  ifdef FEAT_X11
+                   if (clientserver_method == CLIENTSERVER_METHOD_X11
+                           && serverReadReply(xterm_dpy, srv, &p, TRUE, -1) < 
0)
+                           break;
+#   endif
+                   if (p == NULL)
                        break;
 # endif
                    j = atoi((char *)p);
@@ -490,12 +558,34 @@ cmdsrv_main(
            if (serverSendToVim(sname, (char_u *)argv[i + 1],
                                                  &res, NULL, 1, 0, FALSE) < 0)
 # else
-           if (xterm_dpy == NULL)
-               mch_errmsg(_("No display: Send expression failed.
"));
-           else if (serverSendToVim(xterm_dpy, sname, (char_u *)argv[i + 1],
-                                              &res, NULL, 1, 0, 1, FALSE) < 0)
+#  ifdef FEAT_SOCKETSERVER
+           if (clientserver_method == CLIENTSERVER_METHOD_SOCKET)
+           {
+               if (!socket_server_valid())
+                   mch_errmsg(_("Socket server not online:"
+                               "Send expression failed"));
+               else if (socket_server_send(sname, (char_u *)argv[i + 1],
+                           &res, NULL, 1, 0, FALSE) < 0)
+                   goto expr_fail;
+           }
+#  endif
+#  ifdef FEAT_X11
+           if (clientserver_method == CLIENTSERVER_METHOD_X11)
+           {
+               if (xterm_dpy == NULL)
+                   mch_errmsg(_("No display: Send expression failed.
"));
+               else if (serverSendToVim(xterm_dpy, sname,
+                           (char_u *)argv[i + 1], &res,
+                           NULL, 1, 0, 1, FALSE) < 0)
+                   goto expr_fail;
+           }
+#  endif
+           if (FALSE)
 # endif
            {
+# if !defined(MSWIN)
+expr_fail:
+# endif
                if (res != NULL && *res != NUL)
                {
                    // Output error from remote
@@ -511,8 +601,25 @@ cmdsrv_main(
            // Win32 always works?
            res = serverGetVimNames();
 # else
-           if (xterm_dpy != NULL)
+#  ifdef FEAT_SOCKETSERVER
+           if (clientserver_method == CLIENTSERVER_METHOD_SOCKET)
+           {
+               int was_init = socket_server_valid();
+
+               // Don't want to add ourselves to the list. So shutdown the
+               // server before listing then startup back again.
+               socket_server_uninit();
+               res = socket_server_list_sockets();
+
+               if (was_init)
+                   socket_server_init(NULL);
+           }
+#  endif
+#  ifdef FEAT_X11
+           if (clientserver_method == CLIENTSERVER_METHOD_X11 &&
+                   xterm_dpy != NULL)
                res = serverGetVimNames(xterm_dpy);
+#  endif
 # endif
            if (did_emsg)
                mch_errmsg("
");
@@ -541,6 +648,9 @@ cmdsrv_main(
 
     if (didone)
     {
+#ifdef FEAT_SOCKETSERVER
+       socket_server_uninit();
+#endif
        display_errors();       // display any collected messages
        exit(exiterr);  // Mission accomplished - get out
     }
@@ -694,7 +804,24 @@ serverMakeName(char_u *arg, char *cmd)
     char_u *p;
 
     if (arg != NULL && *arg != NUL)
+    {
+#ifdef FEAT_SOCKETSERVER
+       // If we are using a socket server, we want to preserve the original
+       // name if it is a path, else uppercase it if its just a generic name.
+       if (clientserver_method == CLIENTSERVER_METHOD_SOCKET)
+       {
+           if (arg[0] == '/' || STRNCMP(arg, "./", 2) == 0 ||
+                   STRNCMP(arg, "../", 3) == 0)
+               p = vim_strsave(arg);
+           else
+               p = vim_strsave_up(arg);
+       }
+       else
+           p = vim_strsave_up(arg);
+#else
        p = vim_strsave_up(arg);
+#endif
+    }
     else
     {
        p = vim_strsave_up(gettail((char_u *)cmd));
@@ -747,7 +874,12 @@ remote_common(typval_T *argvars, typval_T *rettv, int expr)
 # ifdef MSWIN
     HWND       w;
 # else
+#ifdef FEAT_X11
     Window     w;
+#endif
+#ifdef FEAT_SOCKETSERVER
+    char_u     *client = NULL;
+#endif
 # endif
 
     if (check_restricted() || check_secure())
@@ -768,14 +900,33 @@ remote_common(typval_T *argvars, typval_T *rettv, int 
expr)
 # ifdef MSWIN
     if (serverSendToVim(server_name, keys, &r, &w, expr, timeout, TRUE) < 0)
 # else
-    if (serverSendToVim(X_DISPLAY, server_name, keys, &r, &w, expr, timeout,
-                                                                 0, TRUE) < 0)
+#ifdef FEAT_SOCKETSERVER
+    if (clientserver_method == CLIENTSERVER_METHOD_SOCKET)
+       if (socket_server_send(server_name, keys, &r, &client, expr,
+                   timeout * 1000, TRUE) < 0)
+           goto stuff;
+#endif
+#ifdef FEAT_X11
+    if (clientserver_method == CLIENTSERVER_METHOD_X11)
+       if (serverSendToVim(X_DISPLAY, server_name, keys, &r, &w, expr, timeout,
+                   0, TRUE) < 0)
+           goto stuff;
+#endif
 # endif
+#if !defined(MSWIN)
+    if (FALSE)
     {
+stuff:
+#else
+    {
+#endif
        if (r != NULL)
        {
            emsg((char *)r);    // sending worked but evaluation failed
            vim_free(r);
+#ifdef FEAT_SOCKETSERVER
+           vim_free(client);
+#endif
        }
        else
            semsg(_(e_unable_to_send_to_str), server_name);
@@ -787,19 +938,39 @@ remote_common(typval_T *argvars, typval_T *rettv, int 
expr)
     if (argvars[2].v_type != VAR_UNKNOWN)
     {
        dictitem_T      v;
+#if defined(FEAT_SOCKETSERVER)
+       struct sockaddr_un addr;
+       char_u          str[sizeof(addr.sun_path)];
+#else
        char_u          str[30];
+#endif
        char_u          *idvar;
 
        idvar = tv_get_string_chk(&argvars[2]);
        if (idvar != NULL && *idvar != NUL)
        {
+#ifdef MSWIN
            sprintf((char *)str, PRINTF_HEX_LONG_U, (long_u)w);
+#else
+#ifdef FEAT_X11
+           if (clientserver_method == CLIENTSERVER_METHOD_X11)
+               sprintf((char *)str, PRINTF_HEX_LONG_U, (long_u)w);
+#endif
+#ifdef FEAT_SOCKETSERVER
+           if (clientserver_method == CLIENTSERVER_METHOD_SOCKET)
+               vim_snprintf((char *)str, sizeof(addr.sun_path),
+                       "%s", client);
+#endif
+#endif
            v.di_tv.v_type = VAR_STRING;
            v.di_tv.vval.v_string = vim_strsave(str);
            set_var(idvar, &v.di_tv, FALSE);
            vim_free(v.di_tv.vval.v_string);
        }
     }
+#ifdef FEAT_SOCKETSERVER
+    vim_free(client);
+#endif
 }
 #endif
 
@@ -890,11 +1061,20 @@ f_remote_peek(typval_T *argvars UNUSED, typval_T *rettv)
        rettv->vval.v_number = (s != NULL);
     }
 # else
-    if (check_connection() == FAIL)
-       return;
+#  ifdef FEAT_SOCKETSERVER
+    if (clientserver_method == CLIENTSERVER_METHOD_SOCKET)
+       rettv->vval.v_number = socket_server_peek_reply(serverid, &s);
+#  endif
+#  ifdef FEAT_X11
+    if (clientserver_method == CLIENTSERVER_METHOD_X11)
+    {
+       if (check_connection() == FAIL)
+           return;
 
-    rettv->vval.v_number = serverPeekReply(X_DISPLAY,
-                                               serverStrToWin(serverid), &s);
+       rettv->vval.v_number = serverPeekReply(X_DISPLAY,
+               serverStrToWin(serverid), &s);
+    }
+#  endif
 # endif
 
     if (argvars[1].v_type != VAR_UNKNOWN && rettv->vval.v_number > 0)
@@ -943,12 +1123,21 @@ f_remote_read(typval_T *argvars UNUSED, typval_T *rettv)
        if (n != 0)
            r = serverGetReply((HWND)n, FALSE, TRUE, TRUE, timeout);
        if (r == NULL)
+           emsg(_(e_unable_to_read_server_reply));
 # else
-       if (check_connection() == FAIL
+#  ifdef FEAT_SOCKETSERVER
+       if (clientserver_method == CLIENTSERVER_METHOD_SOCKET &&
+               socket_server_read_reply(serverid, &r, timeout * 1000) == FAIL)
+           emsg(_(e_unable_to_read_server_reply));
+#  endif
+#  ifdef FEAT_X11
+       if (clientserver_method == CLIENTSERVER_METHOD_X11 &&
+               (check_connection() == FAIL
                || serverReadReply(X_DISPLAY, serverStrToWin(serverid),
-                                                      &r, FALSE, timeout) < 0)
-# endif
+                                                      &r, FALSE, timeout) < 0))
            emsg(_(e_unable_to_read_server_reply));
+#  endif
+# endif
     }
 #endif
     rettv->v_type = VAR_STRING;
@@ -992,11 +1181,18 @@ f_remote_startserver(typval_T *argvars UNUSED, typval_T 
*rettv UNUSED)
     }
 
     char_u *server = tv_get_string_chk(&argvars[0]);
+# ifdef MSWIN
+    serverSetName(server);
+# else
+# ifdef FEAT_SOCKETSERVER
+    if (clientserver_method == CLIENTSERVER_METHOD_SOCKET)
+       socket_server_init(server);
+# endif
 # ifdef FEAT_X11
-    if (check_connection() == OK)
+    if (clientserver_method == CLIENTSERVER_METHOD_X11 &&
+           check_connection() == OK)
        serverRegisterName(X_DISPLAY, server);
-# else
-    serverSetName(server);
+# endif
 # endif
 
 #else
@@ -1026,13 +1222,30 @@ f_server2client(typval_T *argvars UNUSED, typval_T 
*rettv)
     if (server == NULL || reply == NULL)
        return;
 
-# ifdef FEAT_X11
-    if (check_connection() == FAIL)
+#ifdef FEAT_SOCKETSERVER
+    if (clientserver_method == CLIENTSERVER_METHOD_SOCKET &&
+           socket_server_send_reply(server, reply) == FAIL)
+       goto fail;
+#endif
+
+#ifdef FEAT_X11
+    if (clientserver_method == CLIENTSERVER_METHOD_X11 &&
+           check_connection() == FAIL)
        return;
-# endif
 
+    if (clientserver_method == CLIENTSERVER_METHOD_X11 &&
+           serverSendReply(server, reply) < 0)
+#endif
+#ifdef MSWIN
     if (serverSendReply(server, reply) < 0)
+#endif
+#if defined(FEAT_SOCKETSERVER) && !defined(FEAT_X11) && !defined(MSWIN)
+    if (FALSE)
+#endif
     {
+#ifdef FEAT_SOCKETSERVER
+fail:
+#endif
        emsg(_(e_unable_to_send_to_client));
        return;
     }
@@ -1051,9 +1264,18 @@ f_serverlist(typval_T *argvars UNUSED, typval_T *rettv)
 # ifdef MSWIN
     r = serverGetVimNames();
 # else
+#  ifdef FEAT_SOCKETSERVER
+    if (clientserver_method == CLIENTSERVER_METHOD_SOCKET)
+       r = socket_server_list_sockets();
+#  endif
+#  ifdef FEAT_X11
+    if (clientserver_method == CLIENTSERVER_METHOD_X11)
+    {
     make_connection();
     if (X_DISPLAY != NULL)
        r = serverGetVimNames(X_DISPLAY);
+    }
+#  endif
 # endif
 #endif
     rettv->v_type = VAR_STRING;
diff --git a/src/config.h.in b/src/config.h.in
index fce8d5f44..e8775d98c 100644
--- a/src/config.h.in
+++ b/src/config.h.in
@@ -406,6 +406,12 @@
 /* Define if you want to always define a server name at vim startup. */
 #undef FEAT_AUTOSERVERNAME
 
+/* Define if you want to use sockets for clientserver communication. */
+#undef WANT_SOCKETSERVER
+
+/* Define if you want to use sockets for clientserver communication if it 
makes sense. */
+#undef MAYBE_SOCKETSERVER
+
 /* Define if you want to include fontset support. */
 #undef FEAT_XFONTSET
 
diff --git a/src/configure.ac b/src/configure.ac
index 4d91a074f..cdb818519 100644
--- a/src/configure.ac
+++ b/src/configure.ac
@@ -2340,6 +2340,24 @@ if test "$enable_autoservername" = "yes"; then
   AC_DEFINE(FEAT_AUTOSERVERNAME)
 fi
 
+AC_MSG_CHECKING(--enable-socketserver argument)
+AC_ARG_ENABLE(socketserver,
+       [  --enable-socketserver Use sockets for clientserver communication.],
+       [enable_socketserver=$enableval],
+       AS_IF([test "x$features" = xtiny], 
+                       [enable_socketserver=no_auto
+                        AC_MSG_RESULT([cannot use socketserver with tiny 
features])],
+                       [enable_socketserver=auto]))
+if test "$enable_socketserver" = "yes"; then
+  AC_DEFINE(WANT_SOCKETSERVER)
+  AC_MSG_RESULT([yes])
+elif test "$enable_socketserver" = "auto"; then
+  AC_DEFINE(MAYBE_SOCKETSERVER)
+  AC_MSG_RESULT([auto])
+elif test "$enable_socketserver" = "no"; then
+  AC_MSG_RESULT([no])
+fi
+
 AC_MSG_CHECKING(--enable-multibyte argument)
 AC_ARG_ENABLE(multibyte,
        [  --enable-multibyte      Include multibyte editing support.], ,
diff --git a/src/errors.h b/src/errors.h
index 050fb4219..5d6867464 100644
--- a/src/errors.h
+++ b/src/errors.h
@@ -3782,3 +3782,15 @@ EXTERN char e_duplicate_type_var_name_str[]
 EXTERN char e_diff_anchors_with_hidden_windows[]
        INIT(= N_("E1562: Diff anchors cannot be used with hidden diff 
windows"));
 #endif
+#ifdef FEAT_SOCKETSERVER
+EXTERN char e_socket_path_too_big[]
+       INIT(= N_("E1563: Socket path is too big"));
+EXTERN char e_socket_name_no_slashes[]
+       INIT(= N_("E1564: Socket name cannot have slashes in it without being a 
path"));
+EXTERN char e_socket_server_not_online[]
+       INIT(= N_("E1565: Socket server is not online, call 
remote_startserver() first"));
+EXTERN char e_socket_server_failed_connecting[]
+       INIT(= N_("E1566: Failed connecting to socket %s: %s"));
+EXTERN char e_socket_server_unavailable[]
+       INIT(= N_("E1567: Cannot start socket server, socket path is 
unavailable"));
+#endif
diff --git a/src/evalfunc.c b/src/evalfunc.c
index 7211048d1..419780f38 100644
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -6765,6 +6765,13 @@ f_has(typval_T *argvars, typval_T *rettv)
                1
 #else
                0
+#endif
+               },
+       {"socketserver",
+#ifdef FEAT_SOCKETSERVER
+               1
+#else
+               0
 #endif
                },
        {"balloon_eval",
diff --git a/src/ex_docmd.c b/src/ex_docmd.c
index 87b390aa8..3e48aed79 100644
--- a/src/ex_docmd.c
+++ b/src/ex_docmd.c
@@ -10039,9 +10039,29 @@ eval_vars(
 
 #ifdef FEAT_CLIENTSERVER
        case SPEC_CLIENT:       // Source of last submitted input
+#ifdef MSWIN
                sprintf((char *)strbuf, PRINTF_HEX_LONG_U,
                                                        (long_u)clientWindow);
                result = strbuf;
+#else
+# ifdef FEAT_SOCKETSERVER
+               if (clientserver_method == CLIENTSERVER_METHOD_SOCKET)
+               {
+                   if (client_socket == NULL)
+                       result = (char_u *)"";
+                   else
+                       result = client_socket;
+               }
+# endif
+# ifdef FEAT_X11
+               if (clientserver_method == CLIENTSERVER_METHOD_X11)
+               {
+                   sprintf((char *)strbuf, PRINTF_HEX_LONG_U,
+                                                       (long_u)clientWindow);
+                   result = strbuf;
+               }
+# endif
+#endif
                break;
 #endif
 
diff --git a/src/feature.h b/src/feature.h
index e5df7fab7..826adbe2b 100644
--- a/src/feature.h
+++ b/src/feature.h
@@ -945,11 +945,20 @@
 # define FIND_REPLACE_DIALOG 1
 #endif
 
+/*
+ * +socketserver        Use UNIX domain sockets for clientserver communication
+ */
+#if defined(UNIX) && (defined(WANT_SOCKETSERVER) || \
+       (defined(MAYBE_SOCKETSERVER) && !defined(HAVE_X11)))
+#define FEAT_SOCKETSERVER
+#endif
+
 /*
  * +clientserver       Remote control via the remote_send() function
  *                     and the --remote argument
  */
-#if (defined(MSWIN) || defined(FEAT_XCLIPBOARD)) && defined(FEAT_EVAL)
+#if (defined(MSWIN) || defined(FEAT_XCLIPBOARD) || defined(FEAT_SOCKETSERVER)) 
\
+    && defined(FEAT_EVAL)
 # define FEAT_CLIENTSERVER
 #endif
 
diff --git a/src/globals.h b/src/globals.h
index 9e3a68120..8d031028e 100644
--- a/src/globals.h
+++ b/src/globals.h
@@ -1868,7 +1868,7 @@ EXTERN Window     commWindow INIT(= None);
 EXTERN Window  clientWindow INIT(= None);
 EXTERN Atom    commProperty INIT(= None);
 EXTERN char_u  *serverDelayedStartName INIT(= NULL);
-# else
+# elif defined(MSWIN)
 #  ifdef PROTO
 typedef int HWND;
 #  endif
@@ -2090,3 +2090,35 @@ EXTERN char *wayland_display_name INIT(= NULL);
 EXTERN int wayland_display_fd;
 
 #endif
+
+#if defined(FEAT_CLIENTSERVER) && !defined(MSWIN)
+
+// Backend for clientserver functionality
+typedef enum {
+    CLIENTSERVER_METHOD_NONE,
+    CLIENTSERVER_METHOD_X11,
+    CLIENTSERVER_METHOD_SOCKET
+} clientserver_method_T;
+
+// Default to X11 if compiled with support for it, else use socket server.
+# if defined(FEAT_X11) && defined(FEAT_SOCKETSERVER)
+EXTERN clientserver_method_T clientserver_method
+# else
+// Since we aren't going to be changing clientserver_method, make it constant 
to
+// allow compiler optimizations.
+EXTERN const clientserver_method_T clientserver_method
+# endif
+# ifdef FEAT_X11
+INIT(= CLIENTSERVER_METHOD_X11);
+# elif defined(FEAT_SOCKETSERVER)
+INIT(= CLIENTSERVER_METHOD_SOCKET);
+# else
+INIT(= CLIENTSERVER_METHOD_NONE);
+# endif
+
+#endif
+
+#ifdef FEAT_SOCKETSERVER
+// Path to socket of last client that communicated with us
+EXTERN char_u *client_socket INIT(= NULL);
+#endif
diff --git a/src/gui.c b/src/gui.c
index e125f8733..13ce0523b 100644
--- a/src/gui.c
+++ b/src/gui.c
@@ -150,6 +150,12 @@ gui_start(char_u *arg UNUSED)
        // Reset clipmethod to CLIPMETHOD_NONE
        choose_clipmethod();
 
+#ifdef FEAT_SOCKETSERVER
+    // Install socket server listening socket if we are running it
+    if (socket_server_valid())
+       gui_gtk_init_socket_server();
+#endif
+
     vim_free(old_term);
 
     // If the GUI started successfully, trigger the GUIEnter event, otherwise
diff --git a/src/gui_gtk_x11.c b/src/gui_gtk_x11.c
index f52f38554..2e3af0421 100644
--- a/src/gui_gtk_x11.c
+++ b/src/gui_gtk_x11.c
@@ -99,6 +99,13 @@ extern void bonobo_dock_item_set_behavior(BonoboDockItem 
*dock_item, BonoboDockI
 # include <X11/Sunkeysym.h>
 #endif
 
+#ifdef FEAT_SOCKETSERVER
+# include <glib-unix.h>
+
+// Used to track the source for the listening socket
+static uint socket_server_source_id = 0;
+#endif
+
 /*
  * Easy-to-use macro for multihead support.
  */
@@ -2688,6 +2695,53 @@ global_event_filter(GdkXEvent *xev,
 }
 #endif // !USE_GNOME_SESSION
 
+#ifdef FEAT_SOCKETSERVER
+
+/*
+ * Callback for new events from the socket server listening socket
+ */
+    static int
+socket_server_poll_in(int fd UNUSED, GIOCondition cond, void *user_data UNUSED)
+{
+    if (cond & G_IO_IN)
+       socket_server_accept_client();
+    else if (cond & (G_IO_ERR | G_IO_HUP))
+    {
+       socket_server_uninit();
+       return FALSE;
+    }
+
+    return TRUE;
+}
+
+/*
+ * Initialize socket server for use in the GUI (does not actually initialize 
the
+ * socket server, only attaches a source).
+ */
+    void
+gui_gtk_init_socket_server(void)
+{
+    if (socket_server_source_id > 0)
+       return;
+    // Register source for file descriptor to global default context
+    socket_server_source_id = g_unix_fd_add(socket_server_get_fd(),
+           G_IO_IN | G_IO_ERR | G_IO_HUP, socket_server_poll_in, NULL);
+}
+
+/*
+ * Remove the source for the socket server listening socket.
+ */
+    void
+gui_gtk_uninit_socket_server(void)
+{
+    if (socket_server_source_id > 0)
+    {
+       g_source_remove(socket_server_source_id);
+       socket_server_source_id = 0;
+    }
+}
+
+#endif
 
 /*
  * Setup the window icon & xcmdsrv comm after the main window has been 
realized.
@@ -2754,7 +2808,7 @@ mainwin_realize(GtkWidget *widget UNUSED, gpointer data 
UNUSED)
        setup_save_yourself();
 
 #ifdef FEAT_CLIENTSERVER
-    if (gui_mch_get_display())
+    if (clientserver_method == CLIENTSERVER_METHOD_X11 && 
gui_mch_get_display())
     {
        if (serverName == NULL && serverDelayedStartName != NULL)
        {
diff --git a/src/if_xcmdsrv.c b/src/if_xcmdsrv.c
index 716665a7b..ddb0579a2 100644
--- a/src/if_xcmdsrv.c
+++ b/src/if_xcmdsrv.c
@@ -14,7 +14,7 @@
 #include "vim.h"
 #include "version.h"
 
-#if defined(FEAT_CLIENTSERVER) || defined(PROTO)
+#if (defined(FEAT_CLIENTSERVER) && defined(FEAT_X11)) || defined(PROTO)
 
 # ifdef FEAT_X11
 #  include <X11/Intrinsic.h>
diff --git a/src/main.c b/src/main.c
index 34576c3e2..c77454a26 100644
--- a/src/main.c
+++ b/src/main.c
@@ -1855,7 +1855,8 @@ getout(int exitval)
  * Get the name of the display, before gui_prepare() removes it from
  * argv[].  Used for the xterm-clipboard display.
  *
- * Also find the --server... arguments and --socketid and --windowid
+ * Also find the --server, --clientserver... arguments and --socketid and
+ * --windowid
  */
     static void
 early_arg_scan(mparm_T *parmp UNUSED)
@@ -1900,6 +1901,22 @@ early_arg_scan(mparm_T *parmp UNUSED)
                gui.dofork = FALSE;
 #  endif
        }
+#  if defined(FEAT_X11) && defined(FEAT_SOCKETSERVER)
+       else if (STRNICMP(argv[i], "--clientserver", 14) == 0)
+       {
+           char_u *arg;
+           if (i == argc - 1)
+               mainerr_arg_missing((char_u *)argv[i]);
+           arg = (char_u *)argv[++i];
+
+           if (STRICMP(arg, "socket") == 0)
+               clientserver_method = CLIENTSERVER_METHOD_SOCKET;
+           else if (STRICMP(arg, "x11") == 0)
+               clientserver_method = CLIENTSERVER_METHOD_X11;
+           else
+               mainerr(ME_UNKNOWN_OPTION, arg);
+       }
+#  endif
 # endif
 
 # if defined(FEAT_GUI_GTK) || defined(FEAT_GUI_MSWIN)
@@ -2220,7 +2237,11 @@ command_line_scan(mparm_T *parmp)
                else if (STRNICMP(argv[0] + argv_idx, "serverlist", 10) == 0)
                    ; // already processed -- no arg
                else if (STRNICMP(argv[0] + argv_idx, "servername", 10) == 0
-                      || STRNICMP(argv[0] + argv_idx, "serversend", 10) == 0)
+                      || STRNICMP(argv[0] + argv_idx, "serversend", 10) == 0
+# if defined(FEAT_X11) && defined(FEAT_SOCKETSERVER)
+                      || STRNICMP(argv[0] + argv_idx, "clientserver", 12) == 0
+# endif
+                      )
                {
                    // already processed -- snatch the following arg
                    if (argc > 1)
@@ -3712,6 +3733,9 @@ usage(void)
     main_msg(_("-Y                     Do not connect to Wayland compositor"));
 #endif
 #ifdef FEAT_CLIENTSERVER
+# if defined(FEAT_X11) && defined(FEAT_SOCKETSERVER)
+    main_msg(_("--clientserver <socket|x11> Backend for clientserver 
communication"));
+# endif
     main_msg(_("--remote <files>       Edit <files> in a Vim server if 
possible"));
     main_msg(_("--remote-silent <files>  Same, don't complain if there is no 
server"));
     main_msg(_("--remote-wait <files>  As --remote but wait for files to have 
been edited"));
diff --git a/src/os_unix.c b/src/os_unix.c
index 3e4c6b339..cc1db3d19 100644
--- a/src/os_unix.c
+++ b/src/os_unix.c
@@ -147,6 +147,123 @@ Window        x11_window = 0;
 Display            *x11_display = NULL;
 #endif
 
+#ifdef FEAT_SOCKETSERVER
+# include <sys/socket.h>
+# include <sys/un.h>
+
+# define SOCKET_SERVER_MAX_BACKLOG 5
+# define SOCKET_SERVER_MAX_CMD_SIZE 16384
+# define SOCKET_SERVER_MAX_MSG 6
+
+static int socket_server_fd = -1;
+static char_u *socket_server_path = NULL;
+
+typedef enum {
+    SS_MSG_TYPE_ENCODING    = 'e',  // Encoding of message.
+    SS_MSG_TYPE_STRING     = 'c',  // Script to execute or reply string.
+    SS_MSG_TYPE_SERIAL     = 's',  // Serial of pending command
+    SS_MSG_TYPE_CODE       = 'r',  // Result code for an expression sent
+    SS_MSG_TYPE_SENDER     = 'd'   // Location of socket for the client that
+                                   // sent the command.
+} ss_msg_type_T;
+
+typedef enum {
+    SS_CMD_TYPE_EXPR       = 'E',  // An expression
+    SS_CMD_TYPE_KEYSTROKES  = 'K',  // Series of keystrokes
+    SS_CMD_TYPE_REPLY      = 'R',  // Reply from an expression
+    SS_CMD_TYPE_NOTIFY     = 'N',  // A notification
+    SS_CMD_TYPE_ALIVE      = 'A',  // Check if server is still responsive
+} ss_cmd_type_T;
+
+// Represents a message in a command. A command can contain multiple messages.
+// Each message starts with a single byte representing the type, then a uint32
+// representing the length of the contents, and then the actual contents.
+// Everything is in native byte order.
+//
+// While contents may contain NULL characters, such as when it is a number, it
+// is always NULL terminated. Note that the NULL terminator does not count in
+// the length.
+typedef struct {
+    char_u     msg_type;           // Type of message
+    uint32_t   msg_len;            // Total length of contents
+    char_u     *msg_contents;      // Actual contents of message
+} ss_msg_T;
+
+// Represents a command sent over a socket. Each socket starts with a byte
+// representing the type, then a uint32 representing the number of messages,
+// then a uint32 representing the total size of the messages in bytes, and then
+// the actual messages. Everything is in native byte order.
+typedef struct {
+    char_u     cmd_type;                           // Type of command
+    uint32_t   cmd_num;                            // Number of messages
+    uint32_t   cmd_len;                            // Combined size of all
+                                                   // messages
+    ss_msg_T   cmd_msgs[SOCKET_SERVER_MAX_MSG];    // Array of messages
+} ss_cmd_T;
+
+#define SS_CMD_INFO_SIZE (sizeof(char_u) + (sizeof(uint32_t) * 2))
+#define SS_MSG_INFO_SIZE (sizeof(char_u) + sizeof(uint32_t))
+
+// Represents a pending reply from a command sent to a Vim server. When a
+// command is sent out, we generate unique serial number with it. When we
+// receive any reply, we check which pending command has a matching serial
+// number, and is therefore the reply for that pending command.
+//
+// The reason we just don't use the existing fd created by the connect() call,
+// and communicate using that, is that it can't handle recursive calls, ex:
+// call remote_expr('B', 'remote_expr("A", "<expr>")')
+//
+// This idea is taken from the existing X server functionality
+typedef struct ss_pending_cmd_S {
+    uint32_t   serial;             // Serial number expected in result
+    char_u     code;               // Result code, can be 0 or -1.
+    char_u     *result;            // Result of command
+
+    struct ss_pending_cmd_S *next;  // Next in list
+} ss_pending_cmd_T;
+
+ss_pending_cmd_T *ss_pending_cmds;
+
+// Serial is always greater than zero
+static uint32_t ss_serial = 0;
+
+// Represents a reply from a server2client call. Each client that calls a
+// server2client call to us has its own ss_reply_T. Each time a client sends
+// data using server2client, Vim creates a ss_reply_T if it doesn't exist and
+// adds the string to the array. When remote_read is called, the server id is
+// used to find the specific ss_reply_T, and a single string is popped from the
+// array.
+//
+// This idea is taken from the existing X server functionality
+typedef struct {
+    char_u *sender;
+    garray_T strings;
+} ss_reply_T;
+
+static garray_T ss_replies;
+
+static char_u *socket_server_get_path_from_name(char_u *name);
+static int socket_server_connect(char_u *name, char_u **path, int silent);
+static void socket_server_init_pending_cmd(ss_pending_cmd_T *pending);
+static void socket_server_pop_pending_cmd(ss_pending_cmd_T *pending);
+static void socket_server_init_cmd(ss_cmd_T *cmd, ss_cmd_type_T type);
+static int socket_server_append_msg(ss_cmd_T *cmd, char_u type,
+       char_u *contents, int len);
+static void socket_server_free_cmd(ss_cmd_T *cmd);
+static char_u *socket_server_encode_cmd(ss_cmd_T *cmd, size_t *sz);
+static int socket_server_decode_cmd(ss_cmd_T *cmd, int socket_fd, int timeout);
+static int socket_server_write(int sock_fd, char_u *data, size_t sz,
+       int timeout);
+static ss_reply_T *socket_server_get_reply(char_u *sender, int *index);
+static ss_reply_T *socket_server_add_reply(char_u *sender);
+static void socket_server_remove_reply(char_u *sender);
+static void socket_server_exec_cmd(ss_cmd_T *cmd, int fd);
+static int socket_server_dispatch(int timeout);
+static int socket_server_check_alive(char_u *name);
+static int socket_server_name_is_valid(char_u *name);
+
+#endif // FEAT_SOCKETSERVER
+
 static int ignore_sigtstp = FALSE;
 
 static int get_x11_title(int);
@@ -3653,6 +3770,10 @@ mch_exit(int r)
     x11_export_final_selection();
 #endif
 
+#ifdef FEAT_SOCKETSERVER
+    socket_server_uninit();
+#endif
+
 #ifdef FEAT_GUI
     if (!gui.in_use)
 #endif
@@ -6540,6 +6661,9 @@ RealWaitForChar(int fd, long msec, int *check_for_gpm 
UNUSED, int *interrupted)
                        // each channel may use in, out and err
        struct pollfd   fds[7 + 3 * MAX_OPEN_CHANNELS];
        int             nfd;
+# ifdef FEAT_SOCKETSERVER
+       int             socket_server_idx = -1;
+# endif
 # ifdef FEAT_WAYLAND_CLIPBOARD
        int             wayland_idx = -1;
 # endif
@@ -6566,6 +6690,16 @@ RealWaitForChar(int fd, long msec, int *check_for_gpm 
UNUSED, int *interrupted)
        fds[0].events = POLLIN;
        nfd = 1;
 
+# ifdef FEAT_SOCKETSERVER
+       if (socket_server_fd != -1)
+       {
+           socket_server_idx = nfd;
+           fds[nfd].fd = socket_server_fd;
+           fds[nfd].events = POLLIN;
+           nfd++;
+       }
+# endif
+
 # ifdef FEAT_WAYLAND_CLIPBOARD
        if (wayland_may_restore_connection())
        {
@@ -6621,6 +6755,17 @@ RealWaitForChar(int fd, long msec, int *check_for_gpm 
UNUSED, int *interrupted)
            finished = FALSE;
 # endif
 
+# ifdef FEAT_SOCKETSERVER
+       if (socket_server_fd != -1)
+       {
+           if (fds[socket_server_idx].revents & POLLIN)
+               socket_server_accept_client();
+           else if (fds[socket_server_idx].revents & (POLLHUP | POLLERR))
+               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
@@ -6710,6 +6855,16 @@ select_eintr:
 # endif
        maxfd = fd;
 
+# ifdef FEAT_SOCKETSERVER
+       if (socket_server_fd != -1)
+       {
+           FD_SET(socket_server_fd, &rfds);
+
+           if (maxfd < socket_server_fd)
+               maxfd = socket_server_fd;
+       }
+# endif
+
 # ifdef FEAT_WAYLAND_CLIPBOARD
 
        if (wayland_may_restore_connection())
@@ -6810,6 +6965,16 @@ select_eintr:
            finished = FALSE;
 # endif
 
+# ifdef FEAT_SOCKETSERVER
+       if (socket_server_fd != -1 && ret > 0)
+       {
+           if (FD_ISSET(socket_server_fd, &rfds))
+               socket_server_accept_client();
+           else if (FD_ISSET(socket_server_fd, &efds))
+               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
@@ -6875,9 +7040,16 @@ select_eintr:
        if (finished || msec == 0)
            break;
 
-# ifdef FEAT_CLIENTSERVER
-       if (server_waiting())
+# if defined(FEAT_CLIENTSERVER)
+#  ifdef FEAT_X11
+       if (clientserver_method == CLIENTSERVER_METHOD_X11 && server_waiting())
            break;
+#  endif
+#  ifdef FEAT_SOCKETSERVER
+       if (clientserver_method == CLIENTSERVER_METHOD_SOCKET &&
+               socket_server_waiting_accept())
+           break;
+#  endif
 # endif
 
        // We're going to loop around again, find out for how long
@@ -8961,3 +9133,1544 @@ mch_create_anon_file(void)
     }
     return fd;
 }
+
+#ifdef FEAT_SOCKETSERVER
+
+/*
+ * Initialize socket server called "name" (the socket filename). If "name" is a
+ * path (starts with a '/', './', or '../'), it is assumed to be the path to
+ * the desired socket. If the socket path is already taken, append an
+ * incrementing number to the path until we find a socket filename that can be
+ * used. If NULL is passed as the name, the previous socket path is used (only
+ * if not NULL). Returns OK on success and FAIL on failure.
+ */
+    int
+socket_server_init(char_u *name)
+{
+    struct sockaddr_un addr;
+    char_u             *path;
+    int                        num_printed;
+    int                        fd;
+    int                        i = 1;
+
+    if (socket_server_valid() || (name == NULL && socket_server_path == NULL))
+       return FAIL;
+    if (name == NULL)
+       name = socket_server_path;
+
+    path = alloc(sizeof(addr.sun_path));
+
+    if (path == NULL)
+       return FAIL;
+
+    fd = socket(AF_UNIX, SOCK_STREAM, 0);
+
+    if (fd == -1)
+    {
+       vim_free(path);
+       return FAIL;
+    }
+
+    addr.sun_family = AF_UNIX;
+
+    // If name is not a path, find a common directory to place the
+    // socket.
+    if (name[0] == '/' || STRNCMP(name, "./", 2) == 0 ||
+           STRNCMP(name, "../", 3) == 0)
+       num_printed =
+           vim_snprintf((char *)path, sizeof(addr.sun_path), "%s", name);
+    else
+    {
+       const char_u    *dir;
+       char_u          *buf;
+
+       // Check if there are slashes in the name
+       if (vim_strchr(name, '/') != NULL)
+       {
+           emsg(_(e_socket_name_no_slashes));
+           goto fail;
+       }
+
+       dir = mch_getenv("XDG_RUNTIME_DIR");
+
+       if (dir == NULL)
+       {
+           // Use $TMPDIR or /tmp if $XDG_RUNTIME_DIR is not set.
+           const char_u    *tmpdir = mch_getenv("TMPDIR");
+           size_t          sz;
+
+           if (tmpdir != NULL)
+               dir = tmpdir;
+           else
+               dir = (char_u *)"/tmp";
+
+           sz = STRLEN(dir) + 25;
+           buf = alloc(sz);
+
+           if (buf == NULL)
+               goto fail;
+
+           vim_snprintf((char *)buf, sz, "%s/vim-%lu", dir,
+                   (unsigned long int)getuid());
+       }
+       else
+       {
+           buf = alloc(STRLEN(dir) + STRLEN("vim") + 2);
+
+           if (buf == NULL)
+               goto fail;
+
+           sprintf((char *)buf, "%s/vim", dir);
+       }
+
+       // Always set directory permissions to 0700 for security
+       if (vim_mkdir(buf, 0700) == -1 && errno != EEXIST)
+       {
+           semsg(_("Failed creating socket directory: %s"), strerror(errno));
+           vim_free(buf);
+           goto fail;
+       }
+
+       num_printed = vim_snprintf((char *)path, sizeof(addr.sun_path),
+               "%s/%s", buf, name);
+
+       vim_free(buf);
+    }
+
+    // Check if path was too big
+    if ((size_t)num_printed >= sizeof(addr.sun_path))
+    {
+       emsg(_(e_socket_path_too_big));
+       goto fail;
+    }
+
+    vim_snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", path);
+
+    // Bind to a suitable path/address
+    while (i < 1000)
+    {
+       if (bind(fd, (struct sockaddr *)&addr, sizeof(addr))
+               == -1)
+       {
+           int fd2;
+
+           if (errno != EADDRINUSE)
+           {
+               emsg(_(e_socket_server_unavailable));
+               goto fail;
+           }
+
+           // If the socket is dead, remove it and try again
+           fd2 = socket_server_connect((char_u *)addr.sun_path, NULL, TRUE);
+
+           if (fd2 == -1)
+           {
+               mch_remove(addr.sun_path);
+               continue;
+           }
+           else
+               close(fd2);
+       }
+       else
+           break;
+
+       num_printed = vim_snprintf(addr.sun_path, sizeof(addr.sun_path),
+               "%s%d", path, i);
+
+       if ((size_t)num_printed >= sizeof(addr.sun_path))
+       {
+           // Address too big
+           emsg(_(e_socket_path_too_big));
+           goto fail;
+       }
+
+       i++;
+    }
+
+    if (i >= 1000)
+    {
+       emsg(_(e_socket_server_unavailable));
+       goto fail;
+    }
+
+    // Start listening for connections
+    if (listen(fd, SOCKET_SERVER_MAX_BACKLOG) == -1)
+       goto fail;
+
+    // Set global path and vvar to the absolute path
+    if ((socket_server_path = alloc(MAXPATHL)) == NULL)
+       goto fail;
+
+    socket_server_path[0] = NUL;
+
+    if (mch_FullName((char_u *)addr.sun_path, socket_server_path,
+               MAXPATHL, FALSE) == FAIL)
+    {
+       vim_free(socket_server_path);
+       goto fail;
+    }
+
+    serverName = vim_strsave(socket_server_path);
+#ifdef FEAT_EVAL
+    set_vim_var_string(VV_SEND_SERVER, serverName, -1);
+#endif
+
+    socket_server_fd = fd;
+
+#ifdef FEAT_GUI_GTK
+    if (gui.in_use)
+       // Initialize source for GUI if we are using it
+       gui_gtk_init_socket_server();
+#endif
+
+    vim_free(path);
+    return OK;
+fail:
+    vim_free(path);
+    socket_server_uninit();
+    return FAIL;
+}
+
+    void
+socket_server_uninit(void)
+{
+    if (socket_server_fd != -1)
+    {
+       close(socket_server_fd);
+       socket_server_fd = -1;
+    }
+
+    if (socket_server_path != NULL)
+    {
+       mch_remove(socket_server_path);
+       vim_free(socket_server_path);
+       socket_server_path = NULL;
+    }
+#ifdef FEAT_GUI_GTK
+    if (gui.in_use)
+       gui_gtk_uninit_socket_server();
+#endif
+}
+
+/*
+ * List available sockets that can be connected to, only in common directories
+ * that Vim knows about. Vim instances with custom socket paths will not be
+ * detected. Returns a newline separated string on success and NULL on failure.
+ */
+    char_u *
+socket_server_list_sockets(void)
+{
+    garray_T           str;
+    char_u             *buf;
+    char_u             *path;
+    DIR                        *dirp;
+    struct dirent      *dp;
+    struct sockaddr_un addr;
+    char_u             *known_dirs[] = {
+       mch_getenv("XDG_RUNTIME_DIR"),
+       mch_getenv("TMPDIR"),
+       (char_u *)"/tmp"
+    };
+
+    if ((buf = alloc(sizeof(addr.sun_path))) == NULL)
+       return NULL;
+    if ((path = alloc(sizeof(addr.sun_path))) == NULL)
+    {
+       vim_free(buf);
+       return NULL;
+    }
+
+    ga_init2(&str, 1, 100);
+
+    for (size_t i = 0 ; i < ARRAY_LENGTH(known_dirs); i++)
+    {
+       char_u *dir = known_dirs[i];
+
+       if (dir == NULL)
+           continue;
+
+       if (STRCMP(dir, "/tmp") == 0 ||
+               (known_dirs[1] != NULL && STRCMP(dir, known_dirs[1]) == 0))
+           vim_snprintf((char *)path, sizeof(addr.sun_path), "%s/vim-%lu",
+                   dir, (unsigned long int)getuid());
+       else
+           vim_snprintf((char *)path, sizeof(addr.sun_path), "%s/vim", dir);
+
+       dir = path;
+
+       dirp = opendir((char *)dir);
+
+       if (dirp == NULL)
+           continue;
+
+       // Loop through directory
+       while ((dp = readdir(dirp)) != NULL)
+       {
+           if (STRCMP(dp->d_name, ".") == 0 || STRCMP(dp->d_name, "..") == 0)
+               continue;
+
+           vim_snprintf((char *)buf, sizeof(addr.sun_path), "%s/%s",
+                   dir, dp->d_name);
+
+           // Try sending an ALIVE command. This is more assuring than a
+           // simple connect, and *also seems to make tests less flaky*.
+           if (!socket_server_check_alive(buf))
+               continue;
+
+           ga_concat(&str, (char_u *)dp->d_name);
+           ga_append(&str, '
');
+       }
+
+       closedir(dirp);
+
+       break;
+    }
+
+    vim_free(path);
+    vim_free(buf);
+
+    ga_append(&str, NUL);
+
+    return str.ga_data;
+}
+
+/*
+ * Called when the server has received a new command. If so, parse it and do 
the
+ * stuff it says, and possibly send back a reply.
+ */
+    void
+socket_server_accept_client(void)
+{
+    int        fd = accept(socket_server_fd, NULL, NULL);
+    ss_cmd_T cmd;
+
+    if (fd == -1)
+       return;
+
+    if (socket_server_decode_cmd(&cmd, fd, 1000) == FAIL)
+       goto exit;
+
+#ifdef FEAT_EVAL
+    ch_log(NULL, "accepted new client on socket %s", socket_server_path);
+#endif
+
+    socket_server_exec_cmd(&cmd, fd);
+    socket_server_free_cmd(&cmd);
+
+exit:
+    close(fd);
+}
+
+/*
+ * Check if socket server is able to be used
+ */
+    int
+socket_server_valid(void)
+{
+    return socket_server_fd != -1 && socket_server_path != NULL;
+}
+
+/*
+ * If "name" is a pathless name such as "VIM", search known directories for the
+ * socket named "name", and return the alloc'ed path to it. If "name" starts
+ * with a '/', './' or '../', then a copy of "name" is returned. Returns NULL
+ * on failure or if no socket was found.
+ */
+    static char_u *
+socket_server_get_path_from_name(char_u *name)
+{
+    char_u         *buf;
+    stat_T         s;
+    const char_u    *known_dirs[] = {
+       mch_getenv("XDG_RUNTIME_DIR"),
+       mch_getenv("TMPDIR"),
+       (char_u *)"/tmp"
+    };
+
+    if (name == NULL)
+       return NULL;
+
+    // Ignore if name is a path
+    if (name[0] == '/' || STRNCMP(name, "./", 2) == 0 ||
+           STRNCMP(name, "../", 3) == 0)
+       return vim_strsave(name);
+
+    buf = alloc(MAXPATHL);
+
+    if (buf == NULL)
+       return NULL;
+
+    for (size_t i = 0; i < ARRAY_LENGTH(known_dirs); i++)
+    {
+       const char_u *dir = known_dirs[i];
+
+       if (dir == NULL)
+           continue;
+       else if (STRCMP(dir, "/tmp") == 0 ||
+               (known_dirs[1] != NULL && STRCMP(dir, known_dirs[1]) == 0))
+           vim_snprintf((char *)buf, MAXPATHL, "%s/vim-%lu/%s", dir,
+                   (unsigned long int)getuid(), name);
+       else
+           vim_snprintf((char *)buf, MAXPATHL, "%s/vim/%s", dir, name);
+
+       if (mch_stat((char *)buf,&s) == 0 && S_ISSOCK(s.st_mode))
+       {
+           if (STRCMP(buf, socket_server_path) == 0)
+               // Can't connect to itself
+               break;
+           return buf;
+       }
+    }
+
+    vim_free(buf);
+    return NULL;
+}
+
+/*
+ * Send command to socket named "name". Returns 0 for OK, -1 on error.
+ */
+    int
+socket_server_send(
+       char_u *name,       // Socket path or a general name
+       char_u *str,        // What to send
+       char_u **result,    // Set to result of expr
+       char_u **receiver,  // Full path of "name"
+       int is_expr,        // Is it an expresison or keystrokes?
+       int timeout,        // In milliseconds
+       int silent)         // Don't complain if socket doesn't exist
+{
+    ss_cmd_T       cmd;
+    int                    socket_fd;
+    size_t         sz;
+    char_u         *final;
+    char_u         *path;
+    struct timeval  start, now;
+
+
+    if (!socket_server_valid())
+    {
+       emsg(_(e_socket_server_not_online));
+       return -1;
+    }
+
+    socket_fd = socket_server_connect(name, &path, silent);
+
+    if (socket_fd == -1)
+       return -1;
+
+#ifdef FEAT_EVAL
+    ch_log(NULL, "socket_server_send(%s, %s)", path, str);
+#endif
+
+    // Execute locally if target is ourselves
+    if (serverName != NULL && STRICMP(path, serverName) == 0)
+    {
+       vim_free(path);
+       close(socket_fd);
+       return sendToLocalVim(str, is_expr, result);
+    }
+
+    socket_server_init_cmd(&cmd,
+           is_expr ? SS_CMD_TYPE_EXPR : SS_CMD_TYPE_KEYSTROKES);
+
+    socket_server_append_msg(&cmd, SS_MSG_TYPE_ENCODING, p_enc, STRLEN(p_enc));
+
+    // Add +1 in case of empty string
+    socket_server_append_msg(&cmd, SS_MSG_TYPE_STRING, str, STRLEN(str) + 1);
+
+    // Tell server who we are so it can save our socket path internally for
+    // later use with server2client
+    socket_server_append_msg(&cmd, SS_MSG_TYPE_SENDER, socket_server_path,
+           STRLEN(socket_server_path));
+
+    if (is_expr)
+    {
+       ss_serial++;
+       socket_server_append_msg(&cmd, SS_MSG_TYPE_SERIAL,
+               (char_u *)&ss_serial, sizeof(ss_serial));
+    }
+
+    final = socket_server_encode_cmd(&cmd, &sz);
+
+    if (final == NULL ||
+           socket_server_write(socket_fd, final, sz, 1000) == FAIL)
+    {
+       if (final != NULL)
+           emsg(_(e_failed_to_send_command_to_destination_program));
+
+       vim_free(path);
+       socket_server_free_cmd(&cmd);
+       close(socket_fd);
+       vim_free(final);
+       return -1;
+    }
+    socket_server_free_cmd(&cmd);
+    vim_free(final);
+
+
+    close(socket_fd);
+    if (!is_expr)
+    {
+       if (receiver != NULL)
+           *receiver = path;
+       else
+           vim_free(path);
+
+       // Exit, we aren't waiting for a reponse
+       return 0;
+    }
+
+    ss_pending_cmd_T pending;
+
+    socket_server_init_pending_cmd(&pending);
+
+    gettimeofday(&start, NULL);
+
+    // Wait for server to send back result
+    while (socket_server_dispatch(500) >= 0)
+    {
+       if (pending.result != NULL)
+           break;
+
+       gettimeofday(&now, NULL);
+
+       if ((now.tv_sec * 1000000 + now.tv_usec) -
+               (start.tv_sec * 1000000 + start.tv_usec) >=
+               (timeout > 0 ? timeout * 1000 : 1000 * 1000))
+           break;
+    }
+
+    if (pending.result == NULL)
+    {
+       socket_server_pop_pending_cmd(&pending);
+       vim_free(path);
+       return -1;
+    }
+
+    if (result != NULL)
+       *result = pending.result;
+    else
+       vim_free(pending.result);
+
+    if (receiver != NULL)
+       *receiver = path;
+    else
+       vim_free(path);
+
+    socket_server_pop_pending_cmd(&pending);
+
+    return pending.code == 0 ? 0 : -1;
+}
+
+/*
+ * Wait for replies from "client" and place result in "str". Returns OK on
+ * success and FAIL on failure. Timeout is in milliseconds
+ */
+    int
+socket_server_read_reply(char_u *client, char_u **str, int timeout)
+{
+    ss_reply_T *reply = NULL;
+    struct timeval start, now;
+
+    if (!socket_server_name_is_valid(client))
+       return -1;
+
+    if (!socket_server_valid())
+       return -1;
+
+    if (timeout > 0)
+       gettimeofday(&start, NULL);
+
+    // Try seeing if there already is a reply in the queue
+    goto get_reply;
+
+    while (socket_server_dispatch(500) >= 0)
+    {
+       int fd;
+
+       if (timeout > 0)
+           gettimeofday(&now, NULL);
+
+       if (timeout > 0)
+           if ((now.tv_sec * 1000000 + now.tv_usec) -
+                   (start.tv_sec * 1000000 + start.tv_usec) >= timeout * 1000)
+               break;
+
+get_reply:
+       reply = socket_server_get_reply(client, NULL);
+
+       if (reply != NULL)
+           break;
+
+       // Check if sender is down by connecting to it as a test. A simple
+       // connect will do.
+       fd = socket_server_connect(client, NULL, TRUE);
+
+       if (fd == -1)
+           return FAIL;
+       else
+           close(fd);
+    }
+
+    if (reply == NULL || reply->strings.ga_data == NULL ||
+           reply->strings.ga_len <= 0)
+    {
+       return FAIL;
+    }
+
+    // Consume the string
+    *str = ((char_u **)reply->strings.ga_data)[0];
+
+    for (int i = 1; i < reply->strings.ga_len; i++)
+    {
+       ((char_u **)reply->strings.ga_data)[i - 1] =
+           ((char_u **)reply->strings.ga_data)[i];
+    }
+    reply->strings.ga_len--;
+
+    if (reply->strings.ga_len < 1)
+       // Last string removed, remove the reply
+       socket_server_remove_reply(client);
+
+
+    return OK;
+}
+
+/*
+ * Check for any replies for "sender". Returns 1 if there is and places the
+ * reply in "str" without consuming it. Returns 0 if otherwise and -1 on
+ * error.
+ */
+    int
+socket_server_peek_reply(char_u *sender, char_u **str)
+{
+    ss_reply_T *reply;
+
+    if (!socket_server_name_is_valid(sender))
+       return -1;
+
+    if (!socket_server_valid())
+       return 0;
+
+    reply = socket_server_get_reply(sender, NULL);
+
+    if (reply != NULL && reply->strings.ga_len > 0)
+    {
+       if (str != NULL)
+           *str = ((char_u **)reply->strings.ga_data)[0];
+       return 1;
+    }
+
+    return 0;
+}
+
+/*
+ * Send a string to "client" as a reply (notification). Returns OK on success
+ * and FAIL on failure.
+ */
+    int
+socket_server_send_reply(char_u *client, char_u *str)
+{
+    int socket_fd;
+    ss_cmd_T   cmd;
+    size_t     sz;
+    char_u     *final;
+
+    if (!socket_server_name_is_valid(client))
+       return FAIL;
+
+    if (!socket_server_valid())
+    {
+       emsg(_(e_socket_server_not_online));
+       return FAIL;
+    }
+
+    socket_fd = socket_server_connect(client, NULL, TRUE);
+
+    if (socket_fd == -1)
+       return FAIL;
+
+    socket_server_init_cmd(&cmd, SS_CMD_TYPE_NOTIFY);
+
+    socket_server_append_msg(&cmd, SS_MSG_TYPE_ENCODING, p_enc, STRLEN(p_enc));
+    socket_server_append_msg(&cmd, SS_MSG_TYPE_STRING, str, STRLEN(str));
+    socket_server_append_msg(&cmd, SS_MSG_TYPE_SENDER,
+           socket_server_path, STRLEN(socket_server_path));
+
+    final = socket_server_encode_cmd(&cmd, &sz);
+
+    if (final == NULL ||
+           socket_server_write(socket_fd, final, sz, 1000) == FAIL)
+    {
+       socket_server_free_cmd(&cmd);
+       close(socket_fd);
+       return FAIL;
+    }
+
+    socket_server_free_cmd(&cmd);
+    vim_free(final);
+    close(socket_fd);
+
+    return OK;
+}
+
+/*
+ * Connect to a socket using "name". "path" is set to the full path of "name"
+ * used to create the socket, only if its not NULL. Returns fd on success and 
-1
+ * on failure.
+ */
+    static int
+socket_server_connect(char_u *name, char_u **path, int silent)
+{
+    int                        socket_fd;
+    int                        res;
+    struct sockaddr_un addr;
+
+    char_u *socket_path = socket_server_get_path_from_name(name);
+
+    if (socket_path == NULL)
+    {
+       if (!silent)
+           semsg(_(e_no_registered_server_named_str), name);
+       return -1;
+    }
+    if (STRLEN(socket_path) >= sizeof(addr.sun_path))
+    {
+       // Path too big
+       vim_free(socket_path);
+       return -1;
+    }
+
+    socket_fd = socket(AF_UNIX, SOCK_STREAM, 0);
+
+    if (socket_fd == -1)
+       goto fail;
+
+    addr.sun_family = AF_UNIX;
+    vim_snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", socket_path);
+
+    res = connect(socket_fd, (struct sockaddr *)&addr, sizeof(addr));
+
+    if (res == -1)
+    {
+       if (!silent)
+           semsg(_(e_socket_server_failed_connecting), socket_path,
+                   strerror(errno));
+       goto fail;
+    }
+
+    if (path != NULL)
+       *path = socket_path;
+    else
+       vim_free(socket_path);
+
+    return socket_fd;
+fail:
+    close(socket_fd);
+    vim_free(socket_path);
+    return -1;
+
+}
+
+/*
+ * Add a new pending command to the list of pending commands. Returns OK on
+ * success and FAIL on failure
+ */
+    static void
+socket_server_init_pending_cmd(ss_pending_cmd_T *pending)
+{
+    pending->code = 0;
+    pending->result = NULL;
+    pending->serial = ss_serial;
+    pending->next = ss_pending_cmds;
+    ss_pending_cmds = pending;
+}
+
+/*
+ * Remove pending command from the list, does not free the result string.
+ */
+    static void
+socket_server_pop_pending_cmd(ss_pending_cmd_T *pending)
+{
+    if (ss_pending_cmds == pending)
+    {
+       ss_pending_cmds = pending->next;
+       return;
+    }
+
+    for (ss_pending_cmd_T *cmd = ss_pending_cmds; cmd != NULL; cmd = cmd->next)
+    {
+       if (cmd->next == pending)
+       {
+           cmd->next = pending->next;
+           return;
+       }
+    }
+}
+
+/*
+ * Initialize command structure to empty state
+ */
+    static void
+socket_server_init_cmd(ss_cmd_T *cmd, ss_cmd_type_T type)
+{
+    cmd->cmd_len = 0;
+    cmd->cmd_num = 0;
+    cmd->cmd_type = type;
+}
+
+/*
+ * Append a message to a command. Note that "len" is the length of contents.
+ * Returns OK on sucess and FAIL on failure
+ */
+    static int
+socket_server_append_msg(ss_cmd_T *cmd, char_u type, char_u *contents, int len)
+{
+    ss_msg_T *msg = cmd->cmd_msgs + cmd->cmd_num;
+
+    if (cmd->cmd_num >= SOCKET_SERVER_MAX_MSG)
+       return FAIL;
+
+    // Check if command will be too big.
+    if (SS_CMD_INFO_SIZE + cmd->cmd_len + SS_MSG_INFO_SIZE + len
+           > SOCKET_SERVER_MAX_CMD_SIZE)
+       return FAIL;
+
+    msg->msg_contents = alloc(len);
+
+    if (msg->msg_contents == NULL)
+       return FAIL;
+
+    msg->msg_type = type;
+    msg->msg_len = len;
+    memcpy(msg->msg_contents, contents, len);
+
+    cmd->cmd_len += SS_MSG_INFO_SIZE + len;
+    cmd->cmd_num++;
+
+    return OK;
+}
+
+/*
+ * Free all resources associated with a command object.
+ */
+    static void
+socket_server_free_cmd(ss_cmd_T *cmd)
+{
+    for (uint32_t i = 0; i < cmd->cmd_num; i++)
+    {
+       ss_msg_T *msg = cmd->cmd_msgs + i;
+
+       vim_free(msg->msg_contents);
+    }
+}
+
+/*
+ * Encode command struct and return the final message to send. Returns NULL on
+ * failure.
+ */
+    static char_u *
+socket_server_encode_cmd(ss_cmd_T *cmd, size_t *sz)
+{
+    size_t size;
+    char_u *buf;
+    char_u *start;
+
+    size = SS_CMD_INFO_SIZE + cmd->cmd_len;
+    buf = alloc(size);
+
+    if (buf == NULL)
+       return NULL;
+
+    start = buf;
+    memcpy(start, &cmd->cmd_type, sizeof(cmd->cmd_type));
+    start += sizeof(cmd->cmd_type);
+    memcpy(start, &cmd->cmd_num, sizeof(cmd->cmd_num));
+    start += sizeof(cmd->cmd_num);
+    memcpy(start, &cmd->cmd_len, sizeof(cmd->cmd_len));
+    start += sizeof(cmd->cmd_len);
+
+    // Append messages to buffer
+    for (uint32_t i = 0; i < cmd->cmd_num; i++)
+    {
+       ss_msg_T *msg = cmd->cmd_msgs + i;
+
+       memcpy(start, &msg->msg_type, sizeof(msg->msg_type));
+       start += sizeof(msg->msg_type);
+       memcpy(start, &msg->msg_len, sizeof(msg->msg_len));
+       start += sizeof(msg->msg_len);
+
+       memcpy(start, msg->msg_contents, msg->msg_len);
+       start += msg->msg_len;
+    }
+
+    *sz = size;
+
+    return buf;
+}
+
+/*
+ * Read from "socket_fd" an entire command and return the result in "cmd". The
+ * socket fd should be at the start of the command. Returns OK on success and
+ * FAIL on failure.
+ */
+    static int
+socket_server_decode_cmd(ss_cmd_T *cmd, int socket_fd, int timeout)
+{
+    int                got_cmd_info    = FALSE; // Consists of type, num, and 
len
+    size_t     total_r         = 0;
+    char_u     *buf;
+    char_u     *cur;
+    struct timeval start, now;
+
+    // We also poll the socket server listening file descriptor to handle
+    // recursive remote calls between Vim instances, such as when one Vim
+    // instance calls remote_expr for an expression that calls remote_expr to
+    // itself again.
+#ifndef HAVE_SELECT
+    struct pollfd pfd;
+
+    pfd.fd = socket_fd;
+    pfd.events = POLLIN;
+#else
+    fd_set         rfds;
+    struct timeval  tv;
+
+    FD_ZERO(&rfds);
+    FD_SET(socket_fd, &rfds);
+#endif
+
+    buf = alloc(SS_CMD_INFO_SIZE);
+
+    if (buf == NULL)
+       return FAIL;
+
+    // We may exit in the middle of the loop and free the messages, we don't
+    // want to free an uninitialized pointer.
+    memset(cmd, 0, sizeof(*cmd));
+
+    gettimeofday(&start, NULL);
+
+    while (TRUE)
+    {
+       int ret;
+       ssize_t r = 0;
+
+#ifndef HAVE_SELECT
+       ret = poll(&pfd, 1, timeout);
+#else
+       tv.tv_sec = 0;
+       tv.tv_usec = 500 * 1000;
+       ret = select(socket_fd + 1, &rfds, NULL, NULL, &tv);
+#endif
+       if (ret < 0)
+           goto fail;
+       if (ret == 0)
+           goto continue_loop;
+
+       // Get cmd info first so we know the total size of all messages, and
+       // can read it all in one go.
+       if (!got_cmd_info)
+       {
+           r = read(socket_fd, buf + total_r, SS_CMD_INFO_SIZE - total_r);
+
+           if ((size_t)r >= SS_CMD_INFO_SIZE - total_r)
+           {
+               char_u *tmp;
+
+               got_cmd_info = TRUE;
+
+               memcpy(&cmd->cmd_type, buf, sizeof(cmd->cmd_type));
+               memcpy(&cmd->cmd_num, buf + sizeof(cmd->cmd_type),
+                       sizeof(cmd->cmd_num));
+               memcpy(&cmd->cmd_len,
+                       buf + sizeof(cmd->cmd_type) + sizeof(cmd->cmd_num),
+                       sizeof(cmd->cmd_len));
+
+               if (cmd->cmd_num > SOCKET_SERVER_MAX_MSG)
+                   // Too many messages to handle or invalid number
+                   goto fail;
+
+               if (cmd->cmd_num == 0)
+                   // No messages to read
+                   goto exit;
+
+               // Now that we now the total size of messages, we can realloc
+               // the buffer to contain all data
+               tmp = vim_realloc(buf, SS_CMD_INFO_SIZE + cmd->cmd_len);
+
+               if (tmp == NULL)
+                   goto fail;
+
+               buf = tmp;
+               cur = buf + SS_CMD_INFO_SIZE;
+
+               continue;
+           }
+       }
+       else
+       {
+           // Read message data
+           r = read(socket_fd, cur + total_r, cmd->cmd_len - total_r);
+
+           if ((size_t)r >= cmd->cmd_len - total_r)
+               break;
+       }
+
+       if (r == -1 || r == 0)
+           goto fail;
+
+       total_r += r;
+
+continue_loop:
+       gettimeofday(&now, NULL);
+
+       if ((now.tv_sec * 1000000 + now.tv_usec) -
+               (start.tv_sec * 1000000 + start.tv_usec) >= timeout * 1000)
+           goto fail;
+    }
+
+    // Parse message data
+    for (uint32_t i = 0; i <  cmd->cmd_num; i++)
+    {
+       ss_msg_T *msg = cmd->cmd_msgs + i;
+
+       memcpy(&msg->msg_type, cur, sizeof(msg->msg_type));
+       cur += sizeof(msg->msg_type);
+       memcpy(&msg->msg_len, cur, sizeof(msg->msg_len));
+       cur += sizeof(msg->msg_len);
+
+       msg->msg_contents = alloc(msg->msg_len + 1);
+
+       if (msg->msg_contents == NULL)
+           goto fail;
+
+       memcpy(msg->msg_contents, cur, msg->msg_len);
+       msg->msg_contents[msg->msg_len] = 0; // NULL terminate it
+
+       // Move pointer to start of next message
+       cur += msg->msg_len;
+    }
+
+exit:
+    vim_free(buf);
+    return OK;
+fail:
+    socket_server_free_cmd(cmd);
+    vim_free(buf);
+    return FAIL;
+}
+
+/*
+ * Low level function that writes to a socket with a timeout in milliseconds.
+ * Returns OK on success and FAIL on failure.
+ */
+    static int
+socket_server_write(int socket_fd, char_u *data, size_t sz, int timeout)
+{
+    char_u *cur = data;
+    size_t total_w = 0;
+    struct timeval start, now;
+#ifndef HAVE_SELECT
+    struct pollfd pfd;
+
+    pfd.fd = socket_fd;
+    pfd.events = POLLOUT;
+#else
+    fd_set         wfds;
+    struct timeval  tv;
+
+    FD_ZERO(&wfds);
+    FD_SET(socket_fd, &wfds);
+#endif
+
+    gettimeofday(&start, NULL);
+
+    while (total_w < sz)
+    {
+       int ret;
+       ssize_t written;
+
+       errno = 0;
+#ifndef HAVE_SELECT
+       ret = poll(&pfd, 1, timeout);
+#else
+       tv.tv_sec = 0;
+       tv.tv_usec = 500 * 1000;
+       ret = select(socket_fd + 1, NULL, &wfds, NULL, &tv);
+#endif
+       if (ret < 0)
+           return FAIL;
+       else if (ret == 0)
+           goto continue_loop;
+
+       written = write(socket_fd, cur, sz - total_w);
+
+       if (written == -1)
+           return FAIL;
+
+       total_w += written;
+
+
+continue_loop:
+       gettimeofday(&now, NULL);
+
+       if ((now.tv_sec * 1000000 + now.tv_usec) -
+               (start.tv_sec * 1000000 + start.tv_usec) >= timeout * 1000)
+           return FAIL;
+    }
+
+    return OK;
+}
+
+    static ss_reply_T *
+socket_server_get_reply(char_u *sender, int *index)
+{
+    for (int i = 0; i < ss_replies.ga_len; i++)
+    {
+       ss_reply_T *reply = ((ss_reply_T *)ss_replies.ga_data) + i;
+
+       if (STRCMP(reply->sender, sender) == 0)
+       {
+           if (index != NULL)
+               *index = i;
+           return reply;
+       }
+    }
+    return NULL;
+}
+
+/*
+ * Add reply to list of replies. Returns a pointer to the ss_reply_T that was
+ * initialized or was found.
+ */
+    static ss_reply_T *
+socket_server_add_reply(char_u *sender)
+{
+    ss_reply_T *reply;
+
+    if (ss_replies.ga_growsize == 0)
+       ga_init2(&ss_replies, sizeof(ss_reply_T), 1);
+
+    reply = socket_server_get_reply(sender, NULL);
+
+    if (reply == NULL && ga_grow(&ss_replies, 1) == OK)
+    {
+       reply = ((ss_reply_T *)ss_replies.ga_data) + ss_replies.ga_len++;
+
+       reply->sender = vim_strsave(sender);
+
+       if (reply->sender == NULL)
+           return NULL;
+
+       ga_init2(&reply->strings, sizeof(char_u *), 5);
+    }
+
+    return reply;
+}
+
+    static void
+socket_server_remove_reply(char_u *sender)
+{
+    int index;
+    ss_reply_T *reply = socket_server_get_reply(sender, &index);
+
+    if (reply != NULL)
+    {
+       ss_reply_T *arr = ss_replies.ga_data;
+
+       // Free strings
+       vim_free(reply->sender);
+       ga_clear_strings(&reply->strings);
+
+       // Move all elements after the removed reply forward by one
+       for (int i = index + 1; i < ss_replies.ga_len; i++)
+           arr[i - 1] = arr[i];
+       ss_replies.ga_len--;
+    }
+}
+
+/*
+ * Execute the actions given by command. "fd" is the socket of the client that
+ * sent the command.
+ */
+    static void
+socket_server_exec_cmd(ss_cmd_T *cmd, int fd)
+{
+    char_u         *str = NULL;
+    char_u         *enc = NULL;
+    char_u         *sender = NULL;
+    uint32_t       serial = 0;
+    char_u         rcode = 0;
+    char_u         *to_free;
+    char_u         *to_free2;
+
+    for (uint32_t i = 0; i < cmd->cmd_num; i++)
+    {
+       ss_msg_T *msg = cmd->cmd_msgs + i;
+
+       if (msg->msg_type == SS_MSG_TYPE_STRING)
+           str = msg->msg_contents;
+       if (msg->msg_type == SS_MSG_TYPE_ENCODING)
+           enc = msg->msg_contents;
+       if (msg->msg_type == SS_MSG_TYPE_SERIAL)
+           memcpy(&serial, msg->msg_contents, sizeof(serial));
+       if (msg->msg_type == SS_MSG_TYPE_CODE)
+           memcpy(&rcode, msg->msg_contents, sizeof(rcode));
+       else if (msg->msg_type == SS_MSG_TYPE_SENDER)
+       {
+           sender = msg->msg_contents;
+
+           // Save in global
+           vim_free(client_socket);
+           client_socket = vim_strsave(sender);
+       }
+    }
+
+#ifdef FEAT_EVAL
+    ch_log(NULL, "socket_server_exec_cmd(): encoding: %s, result: %s",
+           enc == NULL ? (char_u *)"(null)" : enc,
+           str == NULL ? (char_u *)"(null)" : str);
+#endif
+
+    if (cmd->cmd_type == SS_CMD_TYPE_EXPR ||
+           cmd->cmd_type == SS_CMD_TYPE_KEYSTROKES)
+    {
+       // Either an expression or keystrokes.
+       if (socket_server_valid() && enc != NULL)
+       {
+           str = serverConvert(enc, str, &to_free);
+
+           if (cmd->cmd_type == SS_CMD_TYPE_KEYSTROKES)
+               server_to_input_buf(str);
+           else if (sender != NULL)
+           {
+               // Evaluate expression and send reply containing result
+               char_u      *result;
+               size_t      sz;
+               char_u      *buf;
+               char_u      code;
+
+               result = eval_client_expr_to_string(str);
+
+               code = result == NULL ? -1 : 0;
+
+               // Send reply
+               ss_cmd_T rcmd;
+
+               socket_server_init_cmd(&rcmd, SS_CMD_TYPE_REPLY);
+
+               // Don't care about errors, server will just ignore command if
+               // its missing something.
+               if (result != NULL)
+                   socket_server_append_msg(&rcmd, SS_MSG_TYPE_STRING, result,
+                           STRLEN(result) + 1); // We add +1 in case "result"
+                                                // is an empty string.
+               else
+                   // An error occured, return an error msg instead
+                   socket_server_append_msg(&rcmd, SS_MSG_TYPE_STRING,
+                           (char_u *)_(e_invalid_expression_received),
+                           STRLEN(e_invalid_expression_received));
+
+               socket_server_append_msg(&rcmd, SS_MSG_TYPE_CODE,
+                       &code, sizeof(code));
+
+               socket_server_append_msg(&rcmd, SS_MSG_TYPE_ENCODING, p_enc,
+                       STRLEN(p_enc));
+
+               socket_server_append_msg(&rcmd, SS_MSG_TYPE_SERIAL,
+                       (char_u *)&serial, sizeof(serial));
+
+               buf = socket_server_encode_cmd(&rcmd, &sz);
+
+               if (buf != NULL)
+               {
+                   int fd2 = socket_server_connect(sender, NULL, TRUE);
+
+                   if (fd2 >= 0)
+                       socket_server_write(fd2, buf, sz, 1000);
+                   vim_free(buf);
+                   close(fd2);
+               }
+
+               socket_server_free_cmd(&rcmd);
+               vim_free(result);
+           }
+           vim_free(to_free);
+       }
+       return;
+    }
+    else if (cmd->cmd_type == SS_CMD_TYPE_REPLY)
+    {
+       // A reply from a previous command we set up, update the corresponding
+       // pending command.
+       if (serial > 0 && str != NULL)
+       {
+           for (ss_pending_cmd_T *pending = ss_pending_cmds; pending != NULL;
+                   pending = pending->next)
+           {
+               if (serial == pending->serial && pending->result == NULL)
+               {
+                   str = serverConvert(enc, str, &to_free);
+
+                   pending->code = rcode;
+
+                   if (to_free == NULL)
+                       pending->result = vim_strsave(str);
+                   else
+                       pending->result = str;
+                   break;
+               }
+           }
+       }
+       return;
+    }
+    else if (cmd->cmd_type == SS_CMD_TYPE_NOTIFY)
+    {
+       // Notification, execute autocommands and save the reply for later use
+       if (sender != NULL && str != NULL && enc != NULL)
+       {
+           ss_reply_T *reply;
+
+           str = serverConvert(enc, str, &to_free);
+           sender = serverConvert(enc, sender, &to_free2);
+
+           reply = socket_server_add_reply(sender);
+
+           if (reply != NULL)
+               ga_copy_string(&reply->strings, str);
+
+           apply_autocmds(EVENT_REMOTEREPLY, sender, str, TRUE, curbuf);
+
+           vim_free(to_free);
+           vim_free(to_free2);
+       }
+       return;
+    }
+    else if (cmd->cmd_type == SS_CMD_TYPE_ALIVE)
+    {
+       // Client wants to check if we are still responsive, send back a single
+       // byte as a YES.
+       char_u buf[1] = {1};
+#ifndef HAVE_SELECT
+       struct pollfd pfd;
+
+       pfd.fd = fd;
+       pfd.events = POLLIN;
+#else
+       fd_set          rfds;
+       struct timeval  tv;
+
+       FD_ZERO(&rfds);
+       FD_SET(fd, &rfds);
+#endif
+
+       if (write(fd, buf, 1) == -1)
+           return;
+
+       // Poll until client closes their end
+
+#ifndef HAVE_SELECT
+       poll(&pfd, 1, 1000);
+#else
+       tv.tv_sec = 1;
+       tv.tv_usec = 0;
+       select(fd + 1, &rfds, NULL, NULL, &tv);
+#endif
+       return;
+    }
+
+    // Command type is invalid, do nothing
+    return;
+}
+
+/*
+ * Poll the socket server fd until a new connection is accepted. Returns 0 on
+ * success, 1 if it timed out or if poll returned empty, and -1 on error.
+ */
+    static int
+socket_server_dispatch(int timeout)
+{
+    int ret;
+#ifndef HAVE_SELECT
+    struct pollfd pfd;
+
+    pfd.fd = socket_server_fd;
+    pfd.events = POLLIN;
+#else
+    fd_set         rfds;
+    fd_set         efds;
+    struct timeval  tv;
+
+    FD_ZERO(&rfds);
+    FD_ZERO(&efds);
+    FD_SET(socket_server_fd, &rfds);
+    FD_SET(socket_server_fd, &efds);
+#endif
+
+#ifndef HAVE_SELECT
+    ret = poll(&pfd, 1, timeout);
+#else
+    tv.tv_sec = timeout / 1000;
+    tv.tv_usec = (timeout % 1000) * 1000;
+    ret = select(socket_server_fd + 1, &rfds, NULL, &efds, &tv);
+#endif
+
+    if (ret < 0)
+       return -1;
+    else if (ret == 0)
+       return 1;
+
+#ifndef HAVE_SELECT
+    if (pfd.revents & POLLIN)
+#else
+    if (FD_ISSET(socket_server_fd, &rfds))
+#endif
+       {
+           socket_server_accept_client();
+           return 0;
+       }
+#ifndef HAVE_SELECT
+    else if (pfd.revents & (POLLHUP | POLLERR))
+#else
+    else if (FD_ISSET(socket_server_fd, &efds))
+#endif
+       // Connection was closed
+       return -1;
+    else
+       return 1;
+
+    return -1;
+}
+
+/*
+ * Check if socket "name" is reponsive by sending an ALIVE command. This does
+ * not require the socket server to be active.
+ */
+    static int
+socket_server_check_alive(char_u *name)
+{
+    int            socket_fd;
+    int            ret;
+    size_t  sz;
+    char_u  *final;
+    char_u  buf[1] = {0};
+#ifndef HAVE_SELECT
+    struct pollfd pfd;
+#else
+    fd_set         rfds;
+    struct timeval  tv;
+#endif
+
+    socket_fd = socket_server_connect(name, NULL, TRUE);
+
+    if (socket_fd == -1)
+       return FALSE;
+
+#ifndef HAVE_SELECT
+    pfd.fd = socket_fd;
+    pfd.events = POLLIN;
+#else
+    FD_ZERO(&rfds);
+    FD_SET(socket_fd, &rfds);
+#endif
+
+    ss_cmd_T cmd;
+
+    socket_server_init_cmd(&cmd, SS_CMD_TYPE_ALIVE);
+
+    final = socket_server_encode_cmd(&cmd, &sz);
+
+    if (final == NULL ||
+           socket_server_write(socket_fd, final, sz, 1000) == FAIL)
+    {
+       vim_free(final);
+       close(socket_fd);
+       return FALSE;
+    }
+    vim_free(final);
+
+    // Poll for response
+#ifndef HAVE_SELECT
+    ret = poll(&pfd, 1, 1000);
+#else
+    tv.tv_sec = 1;
+    tv.tv_usec = 0;
+    ret = select(socket_fd + 1, &rfds, NULL, NULL, &tv);
+#endif
+
+    if (ret > 0)
+       if (read(socket_fd, buf, 1) == -1)
+       {
+           close(socket_fd);
+           return FALSE;
+       }
+
+    close(socket_fd);
+    return buf[0] == 1;
+}
+
+/*
+ * Get file descriptor of listening socket
+ */
+    int
+socket_server_get_fd(void)
+{
+    return socket_server_fd;
+}
+
+
+/*
+ * Check if socket name is a valid name
+ */
+    static int
+socket_server_name_is_valid(char_u *name)
+{
+    if (STRLEN(name) == 0 || (name[0] != '/' && vim_strchr(name, '/') != NULL))
+    {
+       semsg(_(e_invalid_server_id_used_str), name);
+       return FALSE;
+    }
+    return TRUE;
+}
+
+/*
+ * Returns TRUE if there are clients queued in the listening socket waiting to
+ * be accepted
+ */
+    int
+socket_server_waiting_accept(void)
+{
+    int ret;
+#ifndef HAVE_SELECT
+    struct pollfd pfd;
+
+    pfd.fd = socket_server_fd;
+    pfd.events = POLLIN;
+
+    ret = poll(&pfd, 1, 0);
+
+    if (ret > 0 && pfd.revents & POLLIN)
+       return TRUE;
+#else
+    fd_set         rfds;
+    struct timeval  tv;
+
+    if (socket_server_fd == -1)
+       return FALSE;
+
+    FD_ZERO(&rfds);
+    FD_SET(socket_server_fd, &rfds);
+
+    tv.tv_sec = 0;
+    tv.tv_usec = 0;
+    ret = select(socket_server_fd + 1, &rfds, NULL, NULL, &tv);
+
+    if (ret > 0 && FD_ISSET(socket_server_fd, &rfds))
+       return TRUE;
+#endif
+
+    return FALSE;
+}
+
+#endif // FEAT_SOCKETSERVER
diff --git a/src/po/vim.pot b/src/po/vim.pot
index b77bd8fc4..0982f9245 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: [email protected]
"
-"POT-Creation-Date: 2025-08-16 17:57+0200
"
+"POT-Creation-Date: 2025-08-18 21:30+0200
"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE
"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>
"
 "Language-Team: LANGUAGE <[email protected]>
"
@@ -254,6 +254,9 @@ msgstr ""
 msgid "%d of %d edited"
 msgstr ""
 
+msgid "Socket server not online:Send expression failed"
+msgstr ""
+
 msgid "No display: Send expression failed.
"
 msgstr ""
 
@@ -1711,6 +1714,9 @@ msgstr ""
 msgid "-Y                      Do not connect to Wayland compositor"
 msgstr ""
 
+msgid "--clientserver <socket|x11> Backend for clientserver communication"
+msgstr ""
+
 msgid "--remote <files>        Edit <files> in a Vim server if possible"
 msgstr ""
 
@@ -2507,6 +2513,10 @@ msgstr ""
 msgid "XSMP SmcOpenConnection failed: %s"
 msgstr ""
 
+#, c-format
+msgid "Failed creating socket directory: %s"
+msgstr ""
+
 msgid "At line"
 msgstr ""
 
@@ -8806,6 +8816,22 @@ msgstr ""
 msgid "E1562: Diff anchors cannot be used with hidden diff windows"
 msgstr ""
 
+msgid "E1563: Socket path is too big"
+msgstr ""
+
+msgid "E1564: Socket name cannot have slashes in it without being a path"
+msgstr ""
+
+msgid "E1565: Socket server is not online, call remote_startserver() first"
+msgstr ""
+
+#, c-format
+msgid "E1566: Failed connecting to socket %s: %s"
+msgstr ""
+
+msgid "E1567: Cannot start socket server, socket path is unavailable"
+msgstr ""
+
 #. type of cmdline window or 0
 #. result of cmdline window or 0
 #. buffer of cmdline window or NULL
diff --git a/src/proto/gui_gtk_x11.pro b/src/proto/gui_gtk_x11.pro
index d019cf0a3..5e7452d4d 100644
--- a/src/proto/gui_gtk_x11.pro
+++ b/src/proto/gui_gtk_x11.pro
@@ -8,6 +8,8 @@ void gui_mch_stop_blink(int may_call_gui_update_cursor);
 void gui_mch_start_blink(void);
 int gui_mch_early_init_check(int give_message);
 int gui_mch_init_check(void);
+void gui_gtk_init_socket_server(void);
+void gui_gtk_uninit_socket_server(void);
 void gui_mch_set_dark_theme(int dark);
 void gui_mch_show_tabline(int showit);
 int gui_mch_showing_tabline(void);
diff --git a/src/proto/option.pro b/src/proto/option.pro
index f1f8a33a4..8e544184a 100644
--- a/src/proto/option.pro
+++ b/src/proto/option.pro
@@ -68,6 +68,7 @@ char *did_set_shellslash(optset_T *args);
 char *did_set_shiftwidth_tabstop(optset_T *args);
 char *did_set_showtabline(optset_T *args);
 char *did_set_smoothscroll(optset_T *args);
+char *did_set_socktimeoutlen(optset_T *args);
 char *did_set_spell(optset_T *args);
 char *did_set_swapfile(optset_T *args);
 char *did_set_termguicolors(optset_T *args);
diff --git a/src/proto/optionstr.pro b/src/proto/optionstr.pro
index 734959b60..4da92f77c 100644
--- a/src/proto/optionstr.pro
+++ b/src/proto/optionstr.pro
@@ -34,6 +34,8 @@ int expand_set_casemap(optexpand_T *args, int *numMatches, 
char_u ***matches);
 int expand_set_clipboard(optexpand_T *args, int *numMatches, char_u 
***matches);
 char *did_set_clipmethod(optset_T *args);
 int expand_set_clipmethod(optexpand_T *args, int *numMatches, char_u 
***matches);
+char *did_set_clientserver(optset_T *args UNUSED);
+int expand_set_clientserver(optexpand_T *args, int *numMatches, char_u 
***matches);
 char *did_set_chars_option(optset_T *args);
 int expand_set_chars_option(optexpand_T *args, int *numMatches, char_u 
***matches);
 char *did_set_cinoptions(optset_T *args);
diff --git a/src/proto/os_unix.pro b/src/proto/os_unix.pro
index ae0cdbba4..329e1dbbd 100644
--- a/src/proto/os_unix.pro
+++ b/src/proto/os_unix.pro
@@ -94,4 +94,15 @@ void stop_timeout(void);
 volatile sig_atomic_t *start_timeout(long msec);
 void delete_timer(void);
 int mch_create_anon_file(void);
+int socket_server_init(char_u *sock_path);
+void socket_server_uninit(void);
+char_u *socket_server_list_sockets(void);
+void socket_server_accept_client(void);
+int socket_server_valid(void);
+int socket_server_send(char_u *sock_path, char_u *cmd, char_u **result, char_u 
**receiver, int is_expr, int timeout, int silent);
+int socket_server_read_reply(char_u *sender, char_u **str, int timeout);
+int socket_server_peek_reply(char_u *sender, char_u **str);
+int socket_server_send_reply(char_u *client, char_u *str);
+int socket_server_get_fd(void);
+int socket_server_waiting_accept(void);
 /* vim: set ft=c : */
diff --git a/src/testdir/test_clientserver.vim 
b/src/testdir/test_clientserver.vim
index 6fe0f6969..4bda0caef 100644
--- a/src/testdir/test_clientserver.vim
+++ b/src/testdir/test_clientserver.vim
@@ -10,6 +10,12 @@ CheckFeature clientserver
 
 source util/shared.vim
 
+" Unlike X11, we need the socket server running if we want to send commands to
+" a server via sockets.
+if v:servername == ""
+  call remote_startserver('VIMSOCKETSERVERTEST')
+endif
+
 func Check_X11_Connection()
   if has('x11')
     CheckX11
@@ -184,10 +190,18 @@ func Test_client_server()
   call assert_fails('call remote_startserver("")', 'E1175:')
   call assert_fails('call remote_startserver([])', 'E1174:')
   call assert_fails("let x = remote_peek([])", 'E730:')
-  call assert_fails("let x = remote_read('vim10')",
-        \ has('unix') ? ['E573:.*vim10'] : 'E277:')
-  call assert_fails("call server2client('abc', 'xyz')",
-        \ has('unix') ? ['E573:.*abc'] : 'E258:')
+
+  " When using socket server, server id is not a number, but the path to the
+  " socket.
+  if has('socketserver') && !has('X11')
+    call assert_fails("let x = remote_read('vim/10')", ['E573:.*vim/10'])
+    call assert_fails("call server2client('a/b/c', 'xyz')", ['E573:.*a/b/c'])
+  else
+    call assert_fails("let x = remote_read('vim10')",
+          \ has('unix') ? ['E573:.*vim10'] : 'E277:')
+    call assert_fails("call server2client('abc', 'xyz')",
+          \ has('unix') ? ['E573:.*abc'] : 'E258:')
+  endif
 endfunc
 
 func Test_client_server_stopinsert()
@@ -231,6 +245,121 @@ func Test_client_server_stopinsert()
   endtry
 endfunc
 
+" Test if socket server and X11 backends can be chosen and work properly.
+func Test_client_server_x11_and_socket_server()
+  CheckNotMSWindows
+  CheckFeature socketserver
+  CheckFeature x11
+
+  let g:test_is_flaky = 1
+  let cmd = GetVimCommand()
+
+  if cmd == ''
+    throw 'GetVimCommand() failed'
+  endif
+  call Check_X11_Connection()
+
+  let types = ['socket', 'x11']
+
+  for type in types
+    let name = 'VIMTEST_' .. toupper(type)
+    let actual_cmd = cmd .. ' --clientserver ' .. type
+    let actual_cmd .= ' --servername ' .. name
+    let job = job_start(actual_cmd, {'stoponexit': 'kill', 'out_io': 'null'})
+
+    call WaitForAssert({-> assert_equal("run", job_status(job))})
+    call WaitForAssert({-> assert_match(name, system(cmd .. ' --clientserver ' 
.. type .. ' --serverlist'))})
+
+    call assert_match(name, system(actual_cmd .. ' --remote-expr 
"v:servername"'))
+
+    call system(actual_cmd .. " --remote-expr 'execute(\"qa!\")'")
+    try
+      call WaitForAssert({-> assert_equal("dead", job_status(job))})
+    finally
+      if job_status(job) != 'dead'
+        call assert_report('Server did not exit')
+        call job_stop(job, 'kill')
+      endif
+    endtry
+  endfor
+endfunc
+
+" Test if socket server works in the GUI
+func Test_client_socket_server_server_gui()
+  CheckNotMSWindows
+  CheckFeature socketserver
+  CheckFeature gui_gtk
+
+  let g:test_is_flaky = 1
+  let cmd = GetVimCommand()
+
+  if cmd == ''
+    throw 'GetVimCommand() failed'
+  endif
+  call Check_X11_Connection()
+
+  let name = 'VIMTESTSOCKET'
+  let cmd .= ' --clientserver socket'
+  let cmd .= ' --servername ' .. name
+
+  let job = job_start(cmd, {'stoponexit': 'kill', 'out_io': 'null'})
+
+  call WaitForAssert({-> assert_equal("run", job_status(job))})
+  call WaitForAssert({-> assert_match(name, system(cmd .. ' --serverlist'))})
+
+  call system(cmd .. " --remote-expr 'execute(\"gui\")'")
+
+  call assert_match('1', system(cmd .. " --remote-expr 
'has(\"gui_running\")'"))
+  call assert_match(name, system(cmd .. ' --remote-expr "v:servername"'))
+
+  call system(cmd .. " --remote-expr 'execute(\"qa!\")'")
+  try
+    call WaitForAssert({-> assert_equal("dead", job_status(job))})
+  finally
+    if job_status(job) != 'dead'
+      call assert_report('Server did not exit')
+      call job_stop(job, 'kill')
+    endif
+  endtry
+endfunc
+
+" Test if custom paths work for socketserver
+func Test_client_socket_server_custom_path()
+  CheckNotMSWindows
+  CheckFeature socketserver
+  CheckNotFeature x11
+
+  let g:test_is_flaky = 1
+  let cmd = GetVimCommand()
+
+  if cmd == ''
+    throw 'GetVimCommand() failed'
+  endif
+
+  let name = 'VIMTESTSOCKET2'
+
+  let paths = ['./' .. name, '../testdir/' .. name, getcwd(-1) .. '/' .. name]
+
+  for path in paths
+    let actual = cmd .. ' --servername ' .. path
+
+    let job = job_start(actual, {'stoponexit': 'kill', 'out_io': 'null'})
+
+    call WaitForAssert({-> assert_equal("run", job_status(job))})
+    call WaitForAssert({-> assert_equal(path, glob(path))})
+
+    call system(actual .. " --remote-expr 'execute(\"qa!\")'")
+    try
+      call WaitForAssert({-> assert_equal("dead", job_status(job))})
+    finally
+      if job_status(job) != 'dead'
+        call assert_report('Server did not exit')
+        call job_stop(job, 'kill')
+      endif
+    endtry
+  endfor
+endfunc
+
 " Uncomment this line to get a debugging log
 " call ch_logfile('channellog', 'w')
 
diff --git a/src/testdir/test_vim9_builtin.vim 
b/src/testdir/test_vim9_builtin.vim
index 40cc15f1a..eec62536f 100644
--- a/src/testdir/test_vim9_builtin.vim
+++ b/src/testdir/test_vim9_builtin.vim
@@ -3,6 +3,11 @@
 source util/screendump.vim
 import './util/vim9.vim' as v9
 
+" Socket backend for remote functions require the socket server to be running
+if v:servername == ""
+  call remote_startserver('VIMSOCKETSERVERTEST')
+endif
+
 " Test for passing too many or too few arguments to builtin functions
 func Test_internalfunc_arg_error()
   let l =<< trim END
diff --git a/src/testdir/test_wayland.vim b/src/testdir/test_wayland.vim
index b60934c37..3308b2a93 100644
--- a/src/testdir/test_wayland.vim
+++ b/src/testdir/test_wayland.vim
@@ -79,6 +79,10 @@ func Test_wayland_startup()
   call s:PreTest()
   call s:CheckXConnection()
 
+  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, {'stoponexit': 'kill', 'out_io': 'null'})
@@ -372,6 +376,10 @@ 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'})
@@ -415,6 +423,10 @@ func Test_no_wayland_connect_cmd_flag()
   call s:PreTest()
   call s:CheckXConnection()
 
+  if v:servername == ""
+    call remote_startserver('VIMSOCKETSERVER')
+  endif
+
   let l:name = 'WLFLAGVIMTEST'
   let l:cmd = GetVimCommand() .. ' -Y --servername ' .. l:name
   let l:job = job_start(cmd, {'stoponexit': 'kill', 'out_io': 'null'})
@@ -453,6 +465,10 @@ func Test_wayland_become_inactive()
   call s:PreTest()
   call s:CheckXConnection()
 
+  if v:servername == ""
+    call remote_startserver('VIMSOCKETSERVER')
+  endif
+
   let l:name = 'WLLOSEVIMTEST'
   let l:cmd = GetVimCommand() .. ' --servername ' .. l:name
   let l:job = job_start(cmd, {
@@ -544,6 +560,10 @@ func Test_wayland_bad_environment()
   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, {
diff --git a/src/ui.c b/src/ui.c
index ea9125280..45a42434e 100644
--- a/src/ui.c
+++ b/src/ui.c
@@ -407,7 +407,19 @@ inchar_loop(
 
        if ((resize_func != NULL && resize_func(TRUE))
 #if defined(FEAT_CLIENTSERVER) && defined(UNIX)
-               || server_waiting()
+               || (
+# ifdef FEAT_X11
+                   (clientserver_method == CLIENTSERVER_METHOD_X11 &&
+                   server_waiting())
+# endif
+# if defined(FEAT_X11) && defined(FEAT_SOCKETSERVER)
+                   ||
+# endif
+# ifdef FEAT_SOCKETSERVER
+                   (clientserver_method == CLIENTSERVER_METHOD_SOCKET &&
+                    socket_server_waiting_accept())
+# endif
+               )
 #endif
 #ifdef MESSAGE_QUEUE
                || interrupted
diff --git a/src/version.c b/src/version.c
index a50755f53..f04f283d8 100644
--- a/src/version.c
+++ b/src/version.c
@@ -516,6 +516,11 @@ static char *(features[]) =
        "-signs",
 #endif
        "+smartindent",
+#ifdef FEAT_SOCKETSERVER
+       "+socketserver",
+#else
+       "-socketserver",
+#endif
 #ifdef FEAT_SODIUM
 # ifdef DYNAMIC_SODIUM
        "+sodium/dyn",
@@ -719,6 +724,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1651,
 /**/
     1650,
 /**/

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion visit 
https://groups.google.com/d/msgid/vim_dev/E1uo5n1-005Qq7-Gs%40256bit.org.

Raspunde prin e-mail lui