changeset 13:eb97cafe34e5

Try to prevent messages from being chopped up into multiple notifications Read data into a buffer and only send a notification when 250ms have passed since the first read instead of sending a notification immediately after each read. This is an attempt to prevent messages from being chopped up into multiple notifications. A (rather negligible) downside of this approach is that multiple messages rapidly following each other are coalesced into a single notification.
author Guido Berhoerster <guido+xwrited@berhoerster.name>
date Mon, 16 Mar 2015 23:54:20 +0100
parents 6440ba6e3466
children 129f316b99aa
files main.c
diffstat 1 files changed, 47 insertions(+), 27 deletions(-) [+]
line wrap: on
line diff
--- a/main.c	Mon Mar 16 20:04:12 2015 +0100
+++ b/main.c	Mon Mar 16 23:54:20 2015 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 Guido Berhoerster <guido+xwrited@berhoerster.name>
+ * Copyright (C) 2015 Guido Berhoerster <guido+xwrited@berhoerster.name>
  *
  * Permission is hereby granted, free of charge, to any person obtaining
  * a copy of this software and associated documentation files (the
@@ -40,12 +40,17 @@
 #include "xwrited-unique.h"
 #include "xwrited-utmp.h"
 
+#define BUFFER_TIMEOUT (250)
+
 enum {
 	PIPE_R_FD = 0,
 	PIPE_W_FD
 };
 
-static int	signal_pipe_fd[2] = { -1, -1 };
+static int		signal_pipe_fd[2] = { -1, -1 };
+static guint		notify_timeout_id;
+static GMainLoop	*loop;
+static GString		*buffer;
 
 static void
 on_signal(int signo)
@@ -68,7 +73,6 @@
 static gboolean
 signal_read_cb(GIOChannel *source, GIOCondition cond, gpointer user_data)
 {
-	GMainLoop	*loop = (GMainLoop *)user_data;
 	sigset_t	sigset;
 	sigset_t	old_sigset;
 	GIOStatus	status;
@@ -114,11 +118,11 @@
 }
 
 static gboolean
-send_notification(GString *raw_str, GMainLoop *loop)
+send_notification(void)
 {
 	gboolean	retval = FALSE;
 	GString		*utf8_str = NULL;
-	gchar		*startp = raw_str->str;
+	gchar		*startp = buffer->str;
 	gchar		*endp;
 	GRegex		*regex = NULL;
 	GError		*error = NULL;
@@ -127,8 +131,8 @@
 	gchar		*tmp;
 	NotifyNotification *notification = NULL;
 
-	utf8_str = g_string_sized_new(raw_str->len);
-	while (!g_utf8_validate(startp, raw_str->str + raw_str->len -
+	utf8_str = g_string_sized_new(buffer->len);
+	while (!g_utf8_validate(startp, buffer->str + buffer->len -
 	    startp, (const gchar **)&endp)) {
 		g_string_append_len(utf8_str, startp, endp - startp);
 		/*
@@ -137,9 +141,9 @@
 		 */
 		g_string_append(utf8_str, "\357\277\275");
 
-		startp = endp + ((endp < raw_str->str + raw_str->len) ? 1 : 0);
+		startp = endp + ((endp < buffer->str + buffer->len) ? 1 : 0);
 	}
-	g_string_append_len(utf8_str, startp, raw_str->str + raw_str->len -
+	g_string_append_len(utf8_str, startp, buffer->str + buffer->len -
 	    startp);
 
 	/* remove any CR, BEL and trailing space and tabs */
@@ -212,30 +216,41 @@
 	if (utf8_str != NULL) {
 		g_string_free(utf8_str, TRUE);
 	}
+	/* prevent a permanently large buffer */
+	g_string_free(buffer, FALSE);
+	buffer = g_string_sized_new(BUFSIZ);
 
 	return (retval);
 }
 
 static gboolean
-master_pty_read_cb(GIOChannel *source, GIOCondition cond,
-    gpointer user_data)
+notify_timeout_cb(gpointer user_data)
 {
-	GMainLoop	*loop = (GMainLoop *)user_data;
+	if (!send_notification()) {
+		g_warning("failed to send notification");
+	}
+
+	notify_timeout_id = 0;
+
+	return (FALSE);
+}
+
+static gboolean
+master_pty_read_cb(GIOChannel *source, GIOCondition cond, gpointer user_data)
+{
 	gchar		buf[BUFSIZ];
-	GString		*raw_str = NULL;
 	GIOStatus	status;
 	gsize		buf_len;
 	GError		*error = NULL;
 
 	if ((cond & G_IO_IN) || (cond & G_IO_PRI)) {
-		raw_str = g_string_sized_new(BUFSIZ);
 		/* read message from master pty */
 		while ((status = g_io_channel_read_chars(source, buf, BUFSIZ,
 		    &buf_len, &error)) == G_IO_STATUS_NORMAL) {
 			if (buf_len > 0) {
 				g_debug("read %" G_GSSIZE_FORMAT " bytes from "
 				    "master pty", buf_len);
-				g_string_append_len(raw_str, buf,
+				g_string_append_len(buffer, buf,
 				    (gssize)buf_len);
 			}
 		}
@@ -244,23 +259,23 @@
 			    error->message);
 			g_error_free(error);
 			g_main_loop_quit(loop);
-			goto out;
+			return (FALSE);
 		}
 
-		if (!send_notification(raw_str, loop)) {
-			g_warning("failed to send notification");
+		/*
+		 * schedule a timeout for sending a notification with the
+		 * buffered message
+		 */
+		if (notify_timeout_id <= 0) {
+			notify_timeout_id = g_timeout_add(BUFFER_TIMEOUT,
+			    notify_timeout_cb, NULL);
 		}
 	}
 
 	if ((cond & G_IO_ERR) || (cond & G_IO_HUP)) {
 		g_critical("connection to master pty broken");
 		g_main_loop_quit(loop);
-			goto out;
-	}
-
-out:
-	if (raw_str != NULL) {
-		g_string_free(raw_str, TRUE);
+		return (FALSE);
 	}
 
 	return (TRUE);
@@ -270,7 +285,6 @@
 main(int argc, char *argv[])
 {
 	int		status = EXIT_FAILURE;
-	GMainLoop	*loop = NULL;
 	GError		*error = NULL;
 	XWritedUnique	*app = NULL;
 	GOptionContext	*context = NULL;
@@ -340,6 +354,8 @@
 		goto out;
 	}
 
+	buffer = g_string_sized_new(BUFSIZ);
+
 	/* open master pty */
 	masterfd = posix_openpt(O_RDWR | O_NOCTTY);
 	if (masterfd == -1) {
@@ -380,7 +396,7 @@
 		goto out;
 	}
 	if (!g_io_add_watch(master_pty_channel, G_IO_IN | G_IO_PRI | G_IO_HUP |
-	    G_IO_ERR, master_pty_read_cb, loop)) {
+	    G_IO_ERR, master_pty_read_cb, NULL)) {
 		g_critical("failed to add watch on signal channel");
 		goto out;
 	}
@@ -417,7 +433,7 @@
 		goto out;
 	}
 	if (g_io_add_watch(signal_channel, G_IO_IN | G_IO_PRI | G_IO_HUP |
-	    G_IO_ERR, signal_read_cb, loop) == 0) {
+	    G_IO_ERR, signal_read_cb, NULL) == 0) {
 		g_critical("failed to add watch on the signal channel");
 		goto out;
 	}
@@ -473,6 +489,10 @@
 		close(masterfd);
 	}
 
+	if (buffer != NULL) {
+		g_string_free(buffer, FALSE);
+	}
+
 	if (app != NULL) {
 		g_object_unref(app);
 	}