changeset 8047:7c74cafac0a1 v7.4.1318

commit https://github.com/vim/vim/commit/7b3ca76a451b10d238ef946f3231762e0bd988e9 Author: Bram Moolenaar <Bram@vim.org> Date: Sun Feb 14 19:13:43 2016 +0100 patch 7.4.1318 Problem: Channel with pipes doesn't work in GUI. Solution: Register input handlers for pipes.
author Christian Brabandt <cb@256bit.org>
date Sun, 14 Feb 2016 19:15:09 +0100
parents 759a0d82475e
children e50896a2b629
files src/channel.c src/eval.c src/feature.h src/gui_w48.c src/os_unix.c src/os_win32.c src/proto/channel.pro src/structs.h src/version.c
diffstat 9 files changed, 312 insertions(+), 265 deletions(-) [+]
line wrap: on
line diff
--- a/src/channel.c
+++ b/src/channel.c
@@ -213,7 +213,8 @@ static int next_ch_id = 0;
     channel_T *
 add_channel(void)
 {
-    channel_T *channel = (channel_T *)alloc_clear((int)sizeof(channel_T));
+    int		which;
+    channel_T	*channel = (channel_T *)alloc_clear((int)sizeof(channel_T));
 
     if (channel == NULL)
 	return NULL;
@@ -221,21 +222,23 @@ add_channel(void)
     channel->ch_id = next_ch_id++;
     ch_log(channel, "Opening channel\n");
 
-    channel->ch_sock = (sock_T)-1;
 #ifdef CHANNEL_PIPES
-    channel->ch_in = -1;
-    channel->ch_out = -1;
-    channel->ch_err = -1;
+    for (which = CHAN_SOCK; which <= CHAN_IN; ++which)
+#else
+    which = CHAN_SOCK;
 #endif
+    {
+	channel->ch_pfd[which].ch_fd = (sock_T)-1;
 #ifdef FEAT_GUI_X11
-    channel->ch_inputHandler = (XtInputId)NULL;
+	channel->ch_pfd[which].ch_inputHandler = (XtInputId)NULL;
 #endif
 #ifdef FEAT_GUI_GTK
-    channel->ch_inputHandler = 0;
+	channel->ch_pfd[which].ch_inputHandler = 0;
 #endif
 #ifdef FEAT_GUI_W32
-    channel->ch_inputHandler = -1;
+	channel->ch_pfd[which].ch_inputHandler = -1;
 #endif
+    }
 
     channel->ch_timeout = 2000;
 
@@ -290,7 +293,7 @@ channel_read_netbeans(int id)
     if (channel == NULL)
 	ch_errorn(NULL, "Channel %d not found", id);
     else
-	channel_read(channel, FALSE, "messageFromNetbeans");
+	channel_read(channel, -1, "messageFromNetbeans");
 }
 #endif
 
@@ -318,43 +321,54 @@ messageFromNetbeans(gpointer clientData,
 #endif
 
     static void
+channel_gui_register_one(channel_T *channel, int which)
+{
+# ifdef FEAT_GUI_X11
+    /* Tell notifier we are interested in being called
+     * when there is input on the editor connection socket. */
+    if (channel->ch_pfd[which].ch_inputHandler == (XtInputId)NULL)
+	channel->ch_pfd[which].ch_inputHandler = XtAppAddInput(
+		(XtAppContext)app_context,
+		channel->ch_pfd[which].ch_fd,
+		(XtPointer)(XtInputReadMask + XtInputExceptMask),
+		messageFromNetbeans,
+		(XtPointer)(long)channel->ch_id);
+# else
+#  ifdef FEAT_GUI_GTK
+    /* Tell gdk we are interested in being called when there
+     * is input on the editor connection socket. */
+    if (channel->ch_pfd[which].ch_inputHandler == 0)
+	channel->ch_pfd[which].ch_inputHandler = gdk_input_add(
+		(gint)channel->ch_pfd[which].ch_fd, (GdkInputCondition)
+		((int)GDK_INPUT_READ + (int)GDK_INPUT_EXCEPTION),
+		messageFromNetbeans,
+		(gpointer)(long)channel->ch_id);
+#  else
+#   ifdef FEAT_GUI_W32
+    /* Tell Windows we are interested in receiving message when there
+     * is input on the editor connection socket.  */
+    if (channel->ch_pfd[which].ch_inputHandler == -1)
+	channel->ch_pfd[which].ch_inputHandler = WSAAsyncSelect(
+		channel->ch_pfd[which].ch_fd,
+		s_hwnd, WM_NETBEANS, FD_READ);
+#   endif
+#  endif
+# endif
+}
+
+    void
 channel_gui_register(channel_T *channel)
 {
     if (!CH_HAS_GUI)
 	return;
 
-    /* TODO: pipes */
-# ifdef FEAT_GUI_X11
-    /* tell notifier we are interested in being called
-     * when there is input on the editor connection socket
-     */
-    if (channel->ch_inputHandler == (XtInputId)NULL)
-	channel->ch_inputHandler =
-	    XtAppAddInput((XtAppContext)app_context, channel->ch_sock,
-			 (XtPointer)(XtInputReadMask + XtInputExceptMask),
-			messageFromNetbeans, (XtPointer)(long)channel->ch_id);
-# else
-#  ifdef FEAT_GUI_GTK
-    /*
-     * Tell gdk we are interested in being called when there
-     * is input on the editor connection socket
-     */
-    if (channel->ch_inputHandler == 0)
-	channel->ch_inputHandler =
-	    gdk_input_add((gint)channel->ch_sock, (GdkInputCondition)
-			     ((int)GDK_INPUT_READ + (int)GDK_INPUT_EXCEPTION),
-			 messageFromNetbeans, (gpointer)(long)channel->ch_id);
-#  else
-#   ifdef FEAT_GUI_W32
-    /*
-     * Tell Windows we are interested in receiving message when there
-     * is input on the editor connection socket.
-     */
-    if (channel->ch_inputHandler == -1)
-	channel->ch_inputHandler =
-	    WSAAsyncSelect(channel->ch_sock, s_hwnd, WM_NETBEANS, FD_READ);
-#   endif
-#  endif
+    if (channel->ch_pfd[CHAN_SOCK].ch_fd >= 0)
+	channel_gui_register_one(channel, CHAN_SOCK);
+# ifdef CHANNEL_PIPES
+    if (channel->ch_pfd[CHAN_OUT].ch_fd >= 0)
+	channel_gui_register_one(channel, CHAN_OUT);
+    if (channel->ch_pfd[CHAN_ERR].ch_fd >= 0)
+	channel_gui_register_one(channel, CHAN_ERR);
 # endif
 }
 
@@ -368,37 +382,47 @@ channel_gui_register_all(void)
     channel_T *channel;
 
     for (channel = first_channel; channel != NULL; channel = channel->ch_next)
-	/* TODO: pipes */
-	if (channel->ch_sock >= 0)
-	    channel_gui_register(channel);
+	channel_gui_register(channel);
+}
+
+    static void
+channel_gui_unregister_one(channel_T *channel, int which)
+{
+# ifdef FEAT_GUI_X11
+    if (channel->ch_pfd[which].ch_inputHandler != (XtInputId)NULL)
+    {
+	XtRemoveInput(channel->ch_pfd[which].ch_inputHandler);
+	channel->ch_pfd[which].ch_inputHandler = (XtInputId)NULL;
+    }
+# else
+#  ifdef FEAT_GUI_GTK
+    if (channel->ch_pfd[which].ch_inputHandler != 0)
+    {
+	gdk_input_remove(channel->ch_pfd[which].ch_inputHandler);
+	channel->ch_pfd[which].ch_inputHandler = 0;
+    }
+#  else
+#   ifdef FEAT_GUI_W32
+    if (channel->ch_pfd[which].ch_inputHandler == 0)
+    {
+	WSAAsyncSelect(channel->ch_pfd[which].ch_fd, s_hwnd, 0, 0);
+	channel->ch_pfd[which].ch_inputHandler = -1;
+    }
+#   endif
+#  endif
+# endif
 }
 
     static void
 channel_gui_unregister(channel_T *channel)
 {
-    /* TODO: pipes */
-# ifdef FEAT_GUI_X11
-    if (channel->ch_inputHandler != (XtInputId)NULL)
-    {
-	XtRemoveInput(channel->ch_inputHandler);
-	channel->ch_inputHandler = (XtInputId)NULL;
-    }
-# else
-#  ifdef FEAT_GUI_GTK
-    if (channel->ch_inputHandler != 0)
-    {
-	gdk_input_remove(channel->ch_inputHandler);
-	channel->ch_inputHandler = 0;
-    }
-#  else
-#   ifdef FEAT_GUI_W32
-    if (channel->ch_inputHandler == 0)
-    {
-	WSAAsyncSelect(channel->ch_sock, s_hwnd, 0, 0);
-	channel->ch_inputHandler = -1;
-    }
-#   endif
-#  endif
+    if (channel->ch_pfd[CHAN_SOCK].ch_fd >= 0)
+	channel_gui_unregister_one(channel, CHAN_SOCK);
+# ifdef CHANNEL_PIPES
+    if (channel->ch_pfd[CHAN_OUT].ch_fd >= 0)
+	channel_gui_unregister_one(channel, CHAN_OUT);
+    if (channel->ch_pfd[CHAN_ERR].ch_fd >= 0)
+	channel_gui_unregister_one(channel, CHAN_ERR);
 # endif
 }
 
@@ -440,6 +464,7 @@ channel_open(char *hostname, int port_in
     {
 	ch_error(NULL, "in socket() in channel_open().\n");
 	PERROR("E898: socket() in channel_open()");
+	channel_free(channel);
 	return NULL;
     }
 
@@ -453,6 +478,7 @@ channel_open(char *hostname, int port_in
 	ch_error(NULL, "in gethostbyname() in channel_open()\n");
 	PERROR("E901: gethostbyname() in channel_open()");
 	sock_close(sd);
+	channel_free(channel);
 	return NULL;
     }
     memcpy((char *)&server.sin_addr, host->h_addr, host->h_length);
@@ -472,6 +498,7 @@ channel_open(char *hostname, int port_in
 	    ch_errorn(NULL, "channel_open: Connect failed with errno %d\n",
 								       errno);
 	    sock_close(sd);
+	    channel_free(channel);
 	    return NULL;
 	}
     }
@@ -492,6 +519,7 @@ channel_open(char *hostname, int port_in
 								       errno);
 	    PERROR(_("E902: Cannot connect to port"));
 	    sock_close(sd);
+	    channel_free(channel);
 	    return NULL;
 	}
     }
@@ -513,12 +541,14 @@ channel_open(char *hostname, int port_in
 								       errno);
 	    PERROR(_("E902: Cannot connect to port"));
 	    sock_close(sd);
+	    channel_free(channel);
 	    return NULL;
 	}
 	if (!FD_ISSET(sd, &wfds))
 	{
 	    /* don't give an error, we just timed out. */
 	    sock_close(sd);
+	    channel_free(channel);
 	    return NULL;
 	}
     }
@@ -542,6 +572,7 @@ channel_open(char *hostname, int port_in
 	    SOCK_ERRNO;
 	    ch_log(NULL, "socket() retry in channel_open()\n");
 	    PERROR("E900: socket() retry in channel_open()");
+	    channel_free(channel);
 	    return NULL;
 	}
 	if (connect(sd, (struct sockaddr *)&server, sizeof(server)))
@@ -575,12 +606,13 @@ channel_open(char *hostname, int port_in
 		ch_error(NULL, "Cannot connect to port after retry\n");
 		PERROR(_("E899: Cannot connect to port after retry2"));
 		sock_close(sd);
+		channel_free(channel);
 		return NULL;
 	    }
 	}
     }
 
-    channel->ch_sock = sd;
+    channel->CH_SOCK = sd;
     channel->ch_close_cb = close_cb;
 
 #ifdef FEAT_GUI
@@ -594,9 +626,9 @@ channel_open(char *hostname, int port_in
     void
 channel_set_pipes(channel_T *channel, int in, int out, int err)
 {
-    channel->ch_in = in;
-    channel->ch_out = out;
-    channel->ch_err = err;
+    channel->CH_IN = in;
+    channel->CH_OUT = out;
+    channel->CH_ERR = err;
 }
 #endif
 
@@ -1115,9 +1147,9 @@ may_invoke_callback(channel_T *channel)
     int
 channel_can_write_to(channel_T *channel)
 {
-    return channel != NULL && (channel->ch_sock >= 0
+    return channel != NULL && (channel->CH_SOCK >= 0
 #ifdef CHANNEL_PIPES
-			  || channel->ch_in >= 0
+			  || channel->CH_IN >= 0
 #endif
 			  );
 }
@@ -1129,11 +1161,11 @@ channel_can_write_to(channel_T *channel)
     int
 channel_is_open(channel_T *channel)
 {
-    return channel != NULL && (channel->ch_sock >= 0
+    return channel != NULL && (channel->CH_SOCK >= 0
 #ifdef CHANNEL_PIPES
-			  || channel->ch_in >= 0
-			  || channel->ch_out >= 0
-			  || channel->ch_err >= 0
+			  || channel->CH_IN >= 0
+			  || channel->CH_OUT >= 0
+			  || channel->CH_ERR >= 0
 #endif
 			  );
 }
@@ -1160,10 +1192,10 @@ channel_close(channel_T *channel)
 {
     ch_log(channel, "Closing channel");
 
-    if (channel->ch_sock >= 0)
+    if (channel->CH_SOCK >= 0)
     {
-	sock_close(channel->ch_sock);
-	channel->ch_sock = -1;
+	sock_close(channel->CH_SOCK);
+	channel->CH_SOCK = -1;
 	channel->ch_close_cb = NULL;
 #ifdef FEAT_GUI
 	channel_gui_unregister(channel);
@@ -1172,20 +1204,20 @@ channel_close(channel_T *channel)
 	channel->ch_callback = NULL;
     }
 #if defined(CHANNEL_PIPES)
-    if (channel->ch_in >= 0)
+    if (channel->CH_IN >= 0)
     {
-	close(channel->ch_in);
-	channel->ch_in = -1;
+	close(channel->CH_IN);
+	channel->CH_IN = -1;
     }
-    if (channel->ch_out >= 0)
+    if (channel->CH_OUT >= 0)
     {
-	close(channel->ch_out);
-	channel->ch_out = -1;
+	close(channel->CH_OUT);
+	channel->CH_OUT = -1;
     }
-    if (channel->ch_err >= 0)
+    if (channel->CH_ERR >= 0)
     {
-	close(channel->ch_err);
-	channel->ch_err = -1;
+	close(channel->CH_ERR);
+	channel->CH_ERR = -1;
     }
 #endif
     channel_clear(channel);
@@ -1351,17 +1383,16 @@ channel_get_id(void)
 
 /*
  * Get the file descriptor to read from, either the socket or stdout.
+ * TODO: never gets stderr.
  */
     static int
-get_read_fd(channel_T *channel, int use_stderr)
+get_read_fd(channel_T *channel)
 {
-    if (channel->ch_sock >= 0)
-	return channel->ch_sock;
+    if (channel->CH_SOCK >= 0)
+	return channel->CH_SOCK;
 #if defined(CHANNEL_PIPES)
-    if (!use_stderr && channel->ch_out >= 0)
-	return channel->ch_out;
-    if (use_stderr && channel->ch_err >= 0)
-	return channel->ch_err;
+    if (channel->CH_OUT >= 0)
+	return channel->CH_OUT;
 #endif
     ch_error(channel, "channel_read() called while socket is closed\n");
     return -1;
@@ -1369,10 +1400,11 @@ get_read_fd(channel_T *channel, int use_
 
 /*
  * Read from channel "channel" for as long as there is something to read.
+ * "which" is CHAN_SOCK, CHAN_OUT or CHAN_ERR.  When -1 guess.
  * The data is put in the read queue.
  */
     void
-channel_read(channel_T *channel, int use_stderr, char *func)
+channel_read(channel_T *channel, int which, char *func)
 {
     static char_u	*buf = NULL;
     int			len = 0;
@@ -1380,10 +1412,13 @@ channel_read(channel_T *channel, int use
     int			fd;
     int			use_socket = FALSE;
 
-    fd = get_read_fd(channel, use_stderr);
+    if (which < 0)
+	fd = get_read_fd(channel);
+    else
+	fd = channel->ch_pfd[which].ch_fd;
     if (fd < 0)
 	return;
-    use_socket = channel->ch_sock >= 0;
+    use_socket = fd == channel->CH_SOCK;
 
     /* Allocate a buffer to read into. */
     if (buf == NULL)
@@ -1450,7 +1485,7 @@ channel_read(channel_T *channel, int use
 	else
 	{
 	    close(fd);
-	    channel->ch_out = -1;
+	    channel->CH_OUT = -1;
 	}
 #endif
 
@@ -1480,13 +1515,14 @@ channel_read_block(channel_T *channel)
     ch_log(channel, "Reading raw\n");
     if (channel_peek(channel) == NULL)
     {
-	int fd = get_read_fd(channel, FALSE);
+	int fd = get_read_fd(channel);
 
+	/* TODO: read both out and err if they are different */
 	ch_log(channel, "No readahead\n");
 	/* Wait for up to the channel timeout. */
 	if (fd < 0 || channel_wait(channel, fd, channel->ch_timeout) == FAIL)
 	    return NULL;
-	channel_read(channel, FALSE, "channel_read_block");
+	channel_read(channel, -1, "channel_read_block");
     }
 
     /* TODO: only get the first message */
@@ -1526,11 +1562,11 @@ channel_read_json_block(channel_T *chann
 		continue;
 
 	    /* Wait for up to the channel timeout. */
-	    fd = get_read_fd(channel, FALSE);
+	    fd = get_read_fd(channel);
 	    if (fd < 0 || channel_wait(channel, fd, channel->ch_timeout)
 								      == FAIL)
 		break;
-	    channel_read(channel, FALSE, "channel_read_json_block");
+	    channel_read(channel, -1, "channel_read_json_block");
 	}
     }
     channel->ch_block_id = 0;
@@ -1539,24 +1575,28 @@ channel_read_json_block(channel_T *chann
 
 # if defined(WIN32) || defined(PROTO)
 /*
- * Lookup the channel from the socket.
+ * Lookup the channel from the socket.  Set "which" to the fd index.
  * Returns NULL when the socket isn't found.
  */
     channel_T *
-channel_fd2channel(sock_T fd)
+channel_fd2channel(sock_T fd, int *whichp)
 {
-    channel_T *channel;
+    channel_T	*channel;
+    int		i;
 
     if (fd >= 0)
 	for (channel = first_channel; channel != NULL;
 						   channel = channel->ch_next)
-	    if (channel->ch_sock == fd
-#  if defined(CHANNEL_PIPES)
-		    || channel->ch_out == fd
-		    || channel->ch_err == fd
+#  ifdef CHANNEL_PIPES
+	    for (i = CHAN_SOCK; i < CHAN_IN; ++i)
+#  else
+	    i = CHAN_SOCK;
 #  endif
-		    )
-		return channel;
+		if (channel->ch_pfd[i].ch_fd == fd)
+		{
+		    *whichp = i;
+		    return channel
+		}
     return NULL;
 }
 # endif
@@ -1574,14 +1614,14 @@ channel_send(channel_T *channel, char_u 
     int		fd = -1;
     int		use_socket = FALSE;
 
-    if (channel->ch_sock >= 0)
+    if (channel->CH_SOCK >= 0)
     {
-	fd = channel->ch_sock;
+	fd = channel->CH_SOCK;
 	use_socket = TRUE;
     }
 #if defined(CHANNEL_PIPES)
-    else if (channel->ch_in >= 0)
-	fd = channel->ch_in;
+    else if (channel->CH_IN >= 0)
+	fd = channel->CH_IN;
 #endif
     if (fd < 0)
     {
@@ -1631,44 +1671,26 @@ channel_send(channel_T *channel, char_u 
     int
 channel_poll_setup(int nfd_in, void *fds_in)
 {
-    int nfd = nfd_in;
-    channel_T *channel;
-    struct pollfd *fds = fds_in;
+    int		nfd = nfd_in;
+    channel_T	*channel;
+    struct	pollfd *fds = fds_in;
+    int		which;
 
     for (channel = first_channel; channel != NULL; channel = channel->ch_next)
-    {
-	if (channel->ch_sock >= 0)
-	{
-	    channel->ch_sock_idx = nfd;
-	    fds[nfd].fd = channel->ch_sock;
-	    fds[nfd].events = POLLIN;
-	    nfd++;
-	}
-	else
-	    channel->ch_sock_idx = -1;
-
 #  ifdef CHANNEL_PIPES
-	if (channel->ch_out >= 0)
-	{
-	    channel->ch_out_idx = nfd;
-	    fds[nfd].fd = channel->ch_out;
-	    fds[nfd].events = POLLIN;
-	    nfd++;
-	}
-	else
-	    channel->ch_out_idx = -1;
-
-	if (channel->ch_err >= 0)
-	{
-	    channel->ch_err_idx = nfd;
-	    fds[nfd].fd = channel->ch_err;
-	    fds[nfd].events = POLLIN;
-	    nfd++;
-	}
-	else
-	    channel->ch_err_idx = -1;
+	for (which = CHAN_SOCK; which < CHAN_IN; ++which)
+#  else
+	which = CHAN_SOCK;
 #  endif
-    }
+	    if (channel->ch_pfd[which].ch_fd >= 0)
+	    {
+		channel->ch_pfd[which].ch_poll_idx = nfd;
+		fds[nfd].fd = channel->ch_pfd[which].ch_fd;
+		fds[nfd].events = POLLIN;
+		nfd++;
+	    }
+	    else
+		channel->ch_pfd[which].ch_poll_idx = -1;
 
     return nfd;
 }
@@ -1679,33 +1701,26 @@ channel_poll_setup(int nfd_in, void *fds
     int
 channel_poll_check(int ret_in, void *fds_in)
 {
-    int ret = ret_in;
-    channel_T *channel;
-    struct pollfd *fds = fds_in;
+    int		ret = ret_in;
+    channel_T	*channel;
+    struct	pollfd *fds = fds_in;
+    int		which;
 
     for (channel = first_channel; channel != NULL; channel = channel->ch_next)
-    {
-	if (ret > 0 && channel->ch_sock_idx != -1
-			     && fds[channel->ch_sock_idx].revents & POLLIN)
-	{
-	    channel_read(channel, FALSE, "channel_poll_check");
-	    --ret;
-	}
 #  ifdef CHANNEL_PIPES
-	if (ret > 0 && channel->ch_out_idx != -1
-			       && fds[channel->ch_out_idx].revents & POLLIN)
+	for (which = CHAN_SOCK; which < CHAN_IN; ++which)
+#  else
+	which = CHAN_SOCK;
+#  endif
 	{
-	    channel_read(channel, FALSE, "channel_poll_check");
-	    --ret;
+	    int idx = channel->ch_pfd[which].ch_poll_idx;
+
+	    if (ret > 0 && idx != -1 && fds[idx].revents & POLLIN)
+	    {
+		channel_read(channel, which, "channel_poll_check");
+		--ret;
+	    }
 	}
-	if (ret > 0 && channel->ch_err_idx != -1
-			       && fds[channel->ch_err_idx].revents & POLLIN)
-	{
-	    channel_read(channel, TRUE, "channel_poll_check");
-	    --ret;
-	}
-#  endif
-    }
 
     return ret;
 }
@@ -1718,33 +1733,27 @@ channel_poll_check(int ret_in, void *fds
     int
 channel_select_setup(int maxfd_in, void *rfds_in)
 {
-    int	    maxfd = maxfd_in;
-    channel_T *channel;
-    fd_set  *rfds = rfds_in;
+    int		maxfd = maxfd_in;
+    channel_T	*channel;
+    fd_set	*rfds = rfds_in;
+    int		which;
 
     for (channel = first_channel; channel != NULL; channel = channel->ch_next)
-    {
-	if (channel->ch_sock >= 0)
-	{
-	    FD_SET(channel->ch_sock, rfds);
-	    if (maxfd < channel->ch_sock)
-		maxfd = channel->ch_sock;
-	}
 #  ifdef CHANNEL_PIPES
-	if (channel->ch_out >= 0)
+	for (which = CHAN_SOCK; which < CHAN_IN; ++which)
+#  else
+	which = CHAN_SOCK;
+#  endif
 	{
-	    FD_SET(channel->ch_out, rfds);
-	    if (maxfd < channel->ch_out)
-		maxfd = channel->ch_out;
+	    sock_T fd = channel->ch_pfd[which].ch_fd;
+
+	    if (fd >= 0)
+	    {
+		FD_SET(fd, rfds);
+		if (maxfd < fd)
+		    maxfd = fd;
+	    }
 	}
-	if (channel->ch_err >= 0)
-	{
-	    FD_SET(channel->ch_err, rfds);
-	    if (maxfd < channel->ch_err)
-		maxfd = channel->ch_err;
-	}
-#  endif
-    }
 
     return maxfd;
 }
@@ -1755,33 +1764,26 @@ channel_select_setup(int maxfd_in, void 
     int
 channel_select_check(int ret_in, void *rfds_in)
 {
-    int	    ret = ret_in;
-    channel_T *channel;
-    fd_set  *rfds = rfds_in;
+    int		ret = ret_in;
+    channel_T	*channel;
+    fd_set	*rfds = rfds_in;
+    int		which;
 
     for (channel = first_channel; channel != NULL; channel = channel->ch_next)
-    {
-	if (ret > 0 && channel->ch_sock >= 0
-				       && FD_ISSET(channel->ch_sock, rfds))
-	{
-	    channel_read(channel, FALSE, "channel_select_check");
-	    --ret;
-	}
 #  ifdef CHANNEL_PIPES
-	if (ret > 0 && channel->ch_out >= 0
-				       && FD_ISSET(channel->ch_out, rfds))
+	for (which = CHAN_SOCK; which < CHAN_IN; ++which)
+#  else
+	which = CHAN_SOCK;
+#  endif
 	{
-	    channel_read(channel, FALSE, "channel_select_check");
-	    --ret;
+	    sock_T fd = channel->ch_pfd[which].ch_fd;
+
+	    if (ret > 0 && fd >= 0 && FD_ISSET(fd, rfds))
+	    {
+		channel_read(channel, which, "channel_select_check");
+		--ret;
+	    }
 	}
-	if (ret > 0 && channel->ch_err >= 0
-				       && FD_ISSET(channel->ch_err, rfds))
-	{
-	    channel_read(channel, TRUE, "channel_select_check");
-	    --ret;
-	}
-#  endif
-    }
 
     return ret;
 }
--- a/src/eval.c
+++ b/src/eval.c
@@ -9970,12 +9970,12 @@ f_ch_open(typval_T *argvars, typval_T *r
     channel = channel_open((char *)address, port, waittime, NULL);
     if (channel != NULL)
     {
+	rettv->vval.v_channel = channel;
 	channel_set_json_mode(channel, ch_mode);
 	channel_set_timeout(channel, timeout);
 	if (callback != NULL && *callback != NUL)
 	    channel_set_callback(channel, callback);
     }
-    rettv->vval.v_channel = channel;
 }
 
 /*
--- a/src/feature.h
+++ b/src/feature.h
@@ -1262,7 +1262,7 @@
 #endif
 
 /*
- * The +job feature requires +eval and Unix or MS-Widndows.
+ * The +job feature requires +eval and Unix or MS-Windows.
  */
 #if (defined(UNIX) || defined(WIN32)) && defined(FEAT_EVAL)
 # define FEAT_JOB
--- a/src/gui_w48.c
+++ b/src/gui_w48.c
@@ -1780,14 +1780,15 @@ process_message(void)
 #ifdef FEAT_CHANNEL
     if (msg.message == WM_NETBEANS)
     {
-	channel_T *channel = channel_fd2channel((sock_T)msg.wParam);
+	int	    what;
+	channel_T   *channel = channel_fd2channel((sock_T)msg.wParam, &what);
 
 	if (channel != NULL)
 	{
 	    /* Disable error messages, they can mess up the display and throw
 	     * an exception. */
 	    ++emsg_off;
-	    channel_read(channel, FALSE, "process_message");
+	    channel_read(channel, what, "process_message");
 	    --emsg_off;
 	}
 	return;
--- a/src/os_unix.c
+++ b/src/os_unix.c
@@ -5116,10 +5116,15 @@ mch_start_job(char **argv, job_T *job)
     close(fd_err[1]);
     channel_set_pipes(channel, fd_in[1], fd_out[0], fd_err[0]);
     channel_set_job(channel, job);
+#ifdef FEAT_GUI
+    channel_gui_register(channel);
+#endif
 
     return;
 
 failed:
+    if (channel != NULL)
+	channel_free(channel);
     if (fd_in[0] >= 0)
     {
 	close(fd_in[0]);
--- a/src/os_win32.c
+++ b/src/os_win32.c
@@ -5039,12 +5039,19 @@ mch_start_job(char *cmd, job_T *job)
     STARTUPINFO		si;
     PROCESS_INFORMATION	pi;
     HANDLE		jo;
+#ifdef FEAT_CHANNEL
+    channel_T	*channel;
+
+    channel = add_channel();
+    if (channel == NULL)
+	return;
+#endif
 
     jo = CreateJobObject(NULL, NULL);
     if (jo == NULL)
     {
 	job->jv_status = JOB_FAILED;
-	return;
+	goto failed;
     }
 
     ZeroMemory(&pi, sizeof(pi));
@@ -5062,22 +5069,40 @@ mch_start_job(char *cmd, job_T *job)
     {
 	CloseHandle(jo);
 	job->jv_status = JOB_FAILED;
-    }
-    else
-    {
-	if (!AssignProcessToJobObject(jo, pi.hProcess))
-	{
-	    /* if failing, switch the way to terminate
-	     * process with TerminateProcess. */
-	    CloseHandle(jo);
-	    jo = NULL;
-	}
-	ResumeThread(pi.hThread);
-	CloseHandle(job->jv_proc_info.hThread);
-	job->jv_proc_info = pi;
-	job->jv_job_object = jo;
-	job->jv_status = JOB_STARTED;
-    }
+	goto failed;
+    }
+
+    if (!AssignProcessToJobObject(jo, pi.hProcess))
+    {
+	/* if failing, switch the way to terminate
+	 * process with TerminateProcess. */
+	CloseHandle(jo);
+	jo = NULL;
+    }
+    ResumeThread(pi.hThread);
+    CloseHandle(job->jv_proc_info.hThread);
+    job->jv_proc_info = pi;
+    job->jv_job_object = jo;
+    job->jv_status = JOB_STARTED;
+
+#ifdef FEAT_CHANNEL
+# if 0
+    /* TODO: connect stdin/stdout/stderr */
+    job->jv_channel = channel;
+    channel_set_pipes(channel, fd_in[1], fd_out[0], fd_err[0]);
+    channel_set_job(channel, job);
+
+#  ifdef FEAT_GUI
+     channel_gui_register(channel);
+#  endif
+# endif
+#endif
+    return;
+
+failed:
+#ifdef FEAT_CHANNEL
+    channel_free(channel);
+#endif
 }
 
     char *
--- a/src/proto/channel.pro
+++ b/src/proto/channel.pro
@@ -2,6 +2,7 @@
 void ch_logfile(FILE *file);
 channel_T *add_channel(void);
 void channel_free(channel_T *channel);
+void channel_gui_register(channel_T *channel);
 void channel_gui_register_all(void);
 channel_T *channel_open(char *hostname, int port_in, int waittime, void (*close_cb)(void));
 void channel_set_pipes(channel_T *channel, int in, int out, int err);
@@ -21,10 +22,10 @@ char_u *channel_peek(channel_T *channel)
 void channel_clear(channel_T *channel);
 void channel_free_all(void);
 int channel_get_id(void);
-void channel_read(channel_T *channel, int use_stderr, char *func);
+void channel_read(channel_T *channel, int what, char *func);
 char_u *channel_read_block(channel_T *channel);
 int channel_read_json_block(channel_T *channel, int id, typval_T **rettv);
-channel_T *channel_fd2channel(sock_T fd);
+channel_T *channel_fd2channel(sock_T fd, int *what);
 int channel_send(channel_T *channel, char_u *buf, char *fun);
 int channel_poll_setup(int nfd_in, void *fds_in);
 int channel_poll_check(int ret_in, void *fds_in);
--- a/src/structs.h
+++ b/src/structs.h
@@ -1301,27 +1301,48 @@ typedef enum
     MODE_JS
 } ch_mode_T;
 
+/* Ordering matters: IN is last, only SOCK/OUT/ERR are polled */
+
+#define CHAN_SOCK   0
+#define CH_SOCK	    ch_pfd[CHAN_SOCK].ch_fd
+
+#ifdef UNIX
+# define CHANNEL_PIPES
+
+# define CHAN_OUT   1
+# define CHAN_ERR   2
+# define CHAN_IN    3
+# define CH_OUT	    ch_pfd[CHAN_OUT].ch_fd
+# define CH_ERR	    ch_pfd[CHAN_ERR].ch_fd
+# define CH_IN	    ch_pfd[CHAN_IN].ch_fd
+#endif
+
+/* The per-fd info for a channel. */
+typedef struct {
+    sock_T	ch_fd;	    /* socket/stdin/stdout/stderr, -1 if not used */
+
+# if defined(UNIX) && !defined(HAVE_SELECT)
+    int		ch_poll_idx;	/* used by channel_poll_setup() */
+# endif
+
+#ifdef FEAT_GUI_X11
+    XtInputId	ch_inputHandler; /* Cookie for input */
+#endif
+#ifdef FEAT_GUI_GTK
+    gint	ch_inputHandler; /* Cookie for input */
+#endif
+#ifdef WIN32
+    int		ch_inputHandler; /* ret.value of WSAAsyncSelect() */
+#endif
+} chan_fd_T;
+
 struct channel_S {
     channel_T	*ch_next;
     channel_T	*ch_prev;
 
     int		ch_id;		/* ID of the channel */
 
-    sock_T	ch_sock;	/* the socket, -1 for a closed channel */
-
-#ifdef UNIX
-# define CHANNEL_PIPES
-    int		ch_in;		/* stdin of the job, -1 if not used */
-    int		ch_out;		/* stdout of the job, -1 if not used */
-    int		ch_err;		/* stderr of the job, -1 if not used */
-
-# if defined(UNIX) && !defined(HAVE_SELECT)
-    int		ch_sock_idx;	/* used by channel_poll_setup() */
-    int		ch_in_idx;	/* used by channel_poll_setup() */
-    int		ch_out_idx;	/* used by channel_poll_setup() */
-    int		ch_err_idx;	/* used by channel_poll_setup() */
-# endif
-#endif
+    chan_fd_T	ch_pfd[4];	/* info for socket, in, out and err */
 
     readq_T	ch_head;	/* dummy node, header for circular queue */
 
@@ -1330,16 +1351,6 @@ struct channel_S {
 				 * the other side has exited, only mention the
 				 * first error until the connection works
 				 * again. */
-#ifdef FEAT_GUI_X11
-    XtInputId	ch_inputHandler; /* Cookie for input */
-#endif
-#ifdef FEAT_GUI_GTK
-    gint	ch_inputHandler; /* Cookie for input */
-#endif
-#ifdef WIN32
-    int		ch_inputHandler; /* simply ret.value of WSAAsyncSelect() */
-#endif
-
     void	(*ch_close_cb)(void); /* callback for when channel is closed */
 
     int		ch_block_id;	/* ID that channel_read_json_block() is
--- a/src/version.c
+++ b/src/version.c
@@ -748,6 +748,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1318,
+/**/
     1317,
 /**/
     1316,