On Thu, 17 Oct 2013 10:04:11 +1100
Lex Trotman <ele...@gmail.com> wrote:

> On 17 October 2013 06:08, Thomas Martitz <
> thomas.mart...@student.htw-berlin.de> wrote:
> 
> > Am 16.10.2013 21:04, schrieb Dimitar Zhekov:
> >
> >> What remains, without going too deep, is a modal dialog. Annoying, of
> >> course, but with the benefit that you can easily stop a bad process.
> >
> > I may have missed it but why have it blocking if async spawn now works?
> > The current build process (on Linux) doesn't block either.
> 
> I think Dimitar is talking about for tools use where the results of the
> command are pasted to the buffer, so the existence of the buffer and the
> selection should not change while the command is running.

Exactly.

I'm a bit sick and overworked now, so here's an incomplete patch.
When sending the current selection to a custom command, please don't
close or modify the document - best of all, wait for the command to
complete. Everything else should work, though it's not pretty.

The utils_set_up_io_channel() macro will be removed, and
utils_setup_io_channel() will remain, without cond (::= G_IO_IN |
G_IO_PRI | G_IO_ERR | G_IO_HUP | G_IO_NVAL) and nblock (::= TRUE).

I don't care about the names of the new structure and functions.
Once this thing is complete and working, rename them as you see fit.

-- 
E-gards: Jimmy
diff --git a/src/build.c b/src/build.c
index 8c2a3f9..8e25075 100644
--- a/src/build.c
+++ b/src/build.c
@@ -65,7 +65,7 @@
 
 /* g_spawn_async_with_pipes doesn't work on Windows */
 #ifdef G_OS_WIN32
-#define SYNC_SPAWN
+/*#define SYNC_SPAWN*/
 #endif
 
 /* Number of editor indicators to draw - limited as this can affect performance */
diff --git a/src/tools.c b/src/tools.c
index aa18f6b..97f1e60 100644
--- a/src/tools.c
+++ b/src/tools.c
@@ -83,6 +83,10 @@ struct cc_data
 	GString *buffer;		/* buffer holding stdout content, or NULL */
 	gboolean error;			/* whether and error occurred */
 	gboolean finished;		/* whether the command has finished */
+	gint stdin_fd;			/* command stdin to write to */
+	gchar *selection;		/* selection to write to stdin */
+	gint written;			/* number of bytes written */
+	gint remaining;			/* remaining bytes to write */
 };
 
 
@@ -290,13 +294,20 @@ static gboolean cc_replace_sel_cb(gpointer user_data)
 		return TRUE;
 	}
 
-	if (! data->error && data->buffer != NULL)
+	if (data->remaining)
+	{
+		ui_set_statusbar(TRUE, "%s: %s: %s", G_STRFUNC, "Failed sending data to command",
+						g_strerror(errno));
+	}
+	else if (! data->error && data->buffer != NULL)
 	{	/* Command completed successfully */
 		sci_replace_sel(data->doc->editor->sci, data->buffer->str);
 	}
 
 	if (data->buffer)
 		g_string_free(data->buffer, TRUE);
+	close(data->stdin_fd);
+	g_free(data->selection);
 	g_slice_free1(sizeof *data, data);
 
 	return FALSE;
@@ -344,6 +355,33 @@ static void cc_exit_cb(GPid child_pid, gint status, gpointer user_data)
 }
 
 
+static gboolean cc_outfunc(gpointer user_data)
+{
+	struct cc_data *data = user_data;
+	gint wrote;
+
+	do
+	{
+		wrote = write(data->stdin_fd, data->selection + data->written, data->remaining);
+		#ifdef G_OS_UNIX
+		if (wrote < 0 && errno != EAGAIN)
+		#else
+		if (wrote < 0 && errno != EAGAIN && errno != ENOSPC)
+		#endif
+		{
+			ui_set_statusbar(TRUE, "%s: %s: %s", G_STRFUNC, "Failed sending data to command",
+							g_strerror(errno));
+			data->error = TRUE;
+			return FALSE;
+		}
+		data->written += wrote;
+		data->remaining -= wrote;
+	} while (wrote > 0 && data->remaining > 0);
+
+	return data->remaining > 0;
+}
+
+
 /* Executes command (which should include all necessary command line args) and passes the current
  * selection through the standard input of command. The whole output of command replaces the
  * current selection. */
@@ -372,8 +410,6 @@ void tools_execute_custom_command(GeanyDocument *doc, const gchar *command)
 	if (g_spawn_async_with_pipes(NULL, argv, NULL, G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
 						NULL, NULL, &pid, &stdin_fd, &stdout_fd, &stderr_fd, &error))
 	{
-		gchar *sel;
-		gint remaining, wrote;
 		struct cc_data *data = g_slice_alloc(sizeof *data);
 
 		data->error = FALSE;
@@ -381,34 +417,25 @@ void tools_execute_custom_command(GeanyDocument *doc, const gchar *command)
 		data->buffer = NULL;
 		data->doc = doc;
 		data->command = command;
+		data->stdin_fd = stdin_fd;
+		data->selection = sci_get_selection_contents(doc->editor->sci);
+		data->written = 0;
+		data->remaining = strlen(data->selection);
 
 		g_child_watch_add(pid, cc_exit_cb, data);
 
 		/* use GIOChannel to monitor stdout */
 		utils_set_up_io_channel(stdout_fd, G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
-				FALSE, cc_iofunc, data);
+				TRUE, cc_iofunc, data);
 		/* copy program's stderr to Geany's stdout to help error tracking */
 		utils_set_up_io_channel(stderr_fd, G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
-				FALSE, cc_iofunc_err, data);
-
-		/* get selection */
-		sel = sci_get_selection_contents(doc->editor->sci);
-
+				TRUE, cc_iofunc_err, data);
 		/* write data to the command */
-		remaining = strlen(sel);
-		do
+		if (data->remaining > 0 && cc_outfunc(data))
 		{
-			wrote = write(stdin_fd, sel, remaining);
-			if (G_UNLIKELY(wrote < 0))
-			{
-				g_warning("%s: %s: %s\n", G_STRFUNC, "Failed sending data to command",
-										g_strerror(errno));
-				break;
-			}
-			remaining -= wrote;
-		} while (remaining > 0);
-		close(stdin_fd);
-		g_free(sel);
+			utils_set_pipe_nonblock(stdin_fd);
+			g_timeout_add(50, cc_outfunc, data);
+		}
 	}
 	else
 	{
diff --git a/src/utils.c b/src/utils.c
index a2c3526..35a188b 100644
--- a/src/utils.c
+++ b/src/utils.c
@@ -40,6 +40,12 @@
 # include <sys/types.h>
 #endif
 
+#ifdef G_OS_UNIX
+# include <fcntl.h>
+#else
+# include <windows.h>
+#endif
+
 #include <glib/gstdio.h>
 
 #include <gio/gio.h>
@@ -1028,21 +1034,83 @@ gchar *utils_get_current_time_string(void)
 }
 
 
-GIOChannel *utils_set_up_io_channel(
-				gint fd, GIOCondition cond, gboolean nblock, GIOFunc func, gpointer data)
+void utils_set_pipe_nonblock(gint fd)
+{
+#ifdef G_OS_UNIX
+	int state = fcntl(fd, F_GETFL);
+
+	if (state != -1)
+		fcntl(fd, F_SETFL, state | O_NONBLOCK);
+#else
+	HANDLE h = (HANDLE) _get_osfhandle(fd);
+	DWORD state;
+
+	if (h != INVALID_HANDLE_VALUE && GetNamedPipeHandleState(h, &state, NULL, NULL, NULL, NULL, 0))
+	{
+		state |= PIPE_NOWAIT;
+		SetNamedPipeHandleState(h, &state, NULL, NULL);
+	}
+#endif
+}
+
+
+#ifdef G_OS_WIN32
+typedef struct _poll_source
+{
+	GSource source;
+	int fd;
+	GIOChannel *ioc;
+	GIOFunc func;
+	gpointer data;
+	gboolean valid;
+} poll_source;
+
+static gboolean poll_source_prepare(G_GNUC_UNUSED GSource *source, gint *timeout)
+{
+	*timeout = -1;
+	return FALSE;
+}
+
+static gboolean poll_source_check(poll_source *source)
+{
+	DWORD available;
+
+	source->valid = PeekNamedPipe((HANDLE) _get_osfhandle(source->fd), NULL, 0, NULL, &available,
+		NULL);
+	return !source->valid || available;
+}
+
+static gboolean poll_source_dispatch(poll_source *source, GSourceFunc callback, gpointer gdata)
+{
+	source->func(source->ioc, G_IO_IN | (source->valid ? 0 : G_IO_ERR), source->data);
+	return source->valid;
+}
+
+static void poll_source_finalize(poll_source *source)
+{
+	g_io_channel_unref(source->ioc);
+}
+
+static GSourceFuncs poll_source_funcs = { poll_source_prepare,
+	(gboolean (*)(GSource *)) poll_source_check,
+	(gboolean (*)(GSource *, GSourceFunc, gpointer)) poll_source_dispatch,
+	(void (*)(GSource *)) poll_source_finalize, NULL, NULL };
+#endif
+
+
+GIOChannel *utils_setup_io_channel(gint fd, GIOFunc func, gpointer data)
 {
 	GIOChannel *ioc;
 	/*const gchar *encoding;*/
 
 	#ifdef G_OS_WIN32
 	ioc = g_io_channel_win32_new_fd(fd);
+	utils_set_pipe_nonblock(fd);
 	#else
 	ioc = g_io_channel_unix_new(fd);
+	g_io_channel_set_flags(ioc, G_IO_FLAG_NONBLOCK, NULL);
 	#endif
 
-	if (nblock)
-		g_io_channel_set_flags(ioc, G_IO_FLAG_NONBLOCK, NULL);
-
 	g_io_channel_set_encoding(ioc, NULL, NULL);
 /*
 	if (! g_get_charset(&encoding))
@@ -1060,8 +1128,20 @@ GIOChannel *utils_set_up_io_channel(
 	/* "auto-close" ;-) */
 	g_io_channel_set_close_on_unref(ioc, TRUE);
 
-	g_io_add_watch(ioc, cond, func, data);
+#ifdef G_OS_WIN32
+	{
+		poll_source *source = (poll_source *) g_source_new(&poll_source_funcs, sizeof(poll_source));
+
+		source->fd = fd;
+		source->ioc = ioc;
+		source->func = func;
+		source->data = data;
+		g_source_attach((GSource *) source, NULL);
+	}
+#else
+	g_io_add_watch(ioc, G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP | G_IO_NVAL, func, data);
 	g_io_channel_unref(ioc);
+#endif
 
 	return ioc;
 }
diff --git a/src/utils.h b/src/utils.h
index 4219e65..f28675b 100644
--- a/src/utils.h
+++ b/src/utils.h
@@ -224,8 +224,11 @@ gint utils_strtod(const gchar *source, gchar **end, gboolean with_route);
 
 gchar *utils_get_current_time_string(void);
 
-GIOChannel *utils_set_up_io_channel(gint fd, GIOCondition cond, gboolean nblock,
-									GIOFunc func, gpointer data);
+void utils_set_pipe_nonblock(gint fd);
+
+GIOChannel *utils_setup_io_channel(gint fd, GIOFunc func, gpointer data);
+#define utils_set_up_io_channel(fd, cond, nblock, func, data) \
+	utils_setup_io_channel(fd, func, data)
 
 gchar **utils_read_file_in_array(const gchar *filename);
 
_______________________________________________
Devel mailing list
Devel@lists.geany.org
https://lists.geany.org/cgi-bin/mailman/listinfo/devel

Reply via email to