projects/xwrited

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 Mar 16 23:54:20 2015 +0100 (2015-03-16)
parents 6440ba6e3466
children 129f316b99aa
files main.c
line diff
     1.1 --- a/main.c	Mon Mar 16 20:04:12 2015 +0100
     1.2 +++ b/main.c	Mon Mar 16 23:54:20 2015 +0100
     1.3 @@ -1,5 +1,5 @@
     1.4  /*
     1.5 - * Copyright (C) 2014 Guido Berhoerster <guido+xwrited@berhoerster.name>
     1.6 + * Copyright (C) 2015 Guido Berhoerster <guido+xwrited@berhoerster.name>
     1.7   *
     1.8   * Permission is hereby granted, free of charge, to any person obtaining
     1.9   * a copy of this software and associated documentation files (the
    1.10 @@ -40,12 +40,17 @@
    1.11  #include "xwrited-unique.h"
    1.12  #include "xwrited-utmp.h"
    1.13  
    1.14 +#define BUFFER_TIMEOUT (250)
    1.15 +
    1.16  enum {
    1.17  	PIPE_R_FD = 0,
    1.18  	PIPE_W_FD
    1.19  };
    1.20  
    1.21 -static int	signal_pipe_fd[2] = { -1, -1 };
    1.22 +static int		signal_pipe_fd[2] = { -1, -1 };
    1.23 +static guint		notify_timeout_id;
    1.24 +static GMainLoop	*loop;
    1.25 +static GString		*buffer;
    1.26  
    1.27  static void
    1.28  on_signal(int signo)
    1.29 @@ -68,7 +73,6 @@
    1.30  static gboolean
    1.31  signal_read_cb(GIOChannel *source, GIOCondition cond, gpointer user_data)
    1.32  {
    1.33 -	GMainLoop	*loop = (GMainLoop *)user_data;
    1.34  	sigset_t	sigset;
    1.35  	sigset_t	old_sigset;
    1.36  	GIOStatus	status;
    1.37 @@ -114,11 +118,11 @@
    1.38  }
    1.39  
    1.40  static gboolean
    1.41 -send_notification(GString *raw_str, GMainLoop *loop)
    1.42 +send_notification(void)
    1.43  {
    1.44  	gboolean	retval = FALSE;
    1.45  	GString		*utf8_str = NULL;
    1.46 -	gchar		*startp = raw_str->str;
    1.47 +	gchar		*startp = buffer->str;
    1.48  	gchar		*endp;
    1.49  	GRegex		*regex = NULL;
    1.50  	GError		*error = NULL;
    1.51 @@ -127,8 +131,8 @@
    1.52  	gchar		*tmp;
    1.53  	NotifyNotification *notification = NULL;
    1.54  
    1.55 -	utf8_str = g_string_sized_new(raw_str->len);
    1.56 -	while (!g_utf8_validate(startp, raw_str->str + raw_str->len -
    1.57 +	utf8_str = g_string_sized_new(buffer->len);
    1.58 +	while (!g_utf8_validate(startp, buffer->str + buffer->len -
    1.59  	    startp, (const gchar **)&endp)) {
    1.60  		g_string_append_len(utf8_str, startp, endp - startp);
    1.61  		/*
    1.62 @@ -137,9 +141,9 @@
    1.63  		 */
    1.64  		g_string_append(utf8_str, "\357\277\275");
    1.65  
    1.66 -		startp = endp + ((endp < raw_str->str + raw_str->len) ? 1 : 0);
    1.67 +		startp = endp + ((endp < buffer->str + buffer->len) ? 1 : 0);
    1.68  	}
    1.69 -	g_string_append_len(utf8_str, startp, raw_str->str + raw_str->len -
    1.70 +	g_string_append_len(utf8_str, startp, buffer->str + buffer->len -
    1.71  	    startp);
    1.72  
    1.73  	/* remove any CR, BEL and trailing space and tabs */
    1.74 @@ -212,30 +216,41 @@
    1.75  	if (utf8_str != NULL) {
    1.76  		g_string_free(utf8_str, TRUE);
    1.77  	}
    1.78 +	/* prevent a permanently large buffer */
    1.79 +	g_string_free(buffer, FALSE);
    1.80 +	buffer = g_string_sized_new(BUFSIZ);
    1.81  
    1.82  	return (retval);
    1.83  }
    1.84  
    1.85  static gboolean
    1.86 -master_pty_read_cb(GIOChannel *source, GIOCondition cond,
    1.87 -    gpointer user_data)
    1.88 +notify_timeout_cb(gpointer user_data)
    1.89  {
    1.90 -	GMainLoop	*loop = (GMainLoop *)user_data;
    1.91 +	if (!send_notification()) {
    1.92 +		g_warning("failed to send notification");
    1.93 +	}
    1.94 +
    1.95 +	notify_timeout_id = 0;
    1.96 +
    1.97 +	return (FALSE);
    1.98 +}
    1.99 +
   1.100 +static gboolean
   1.101 +master_pty_read_cb(GIOChannel *source, GIOCondition cond, gpointer user_data)
   1.102 +{
   1.103  	gchar		buf[BUFSIZ];
   1.104 -	GString		*raw_str = NULL;
   1.105  	GIOStatus	status;
   1.106  	gsize		buf_len;
   1.107  	GError		*error = NULL;
   1.108  
   1.109  	if ((cond & G_IO_IN) || (cond & G_IO_PRI)) {
   1.110 -		raw_str = g_string_sized_new(BUFSIZ);
   1.111  		/* read message from master pty */
   1.112  		while ((status = g_io_channel_read_chars(source, buf, BUFSIZ,
   1.113  		    &buf_len, &error)) == G_IO_STATUS_NORMAL) {
   1.114  			if (buf_len > 0) {
   1.115  				g_debug("read %" G_GSSIZE_FORMAT " bytes from "
   1.116  				    "master pty", buf_len);
   1.117 -				g_string_append_len(raw_str, buf,
   1.118 +				g_string_append_len(buffer, buf,
   1.119  				    (gssize)buf_len);
   1.120  			}
   1.121  		}
   1.122 @@ -244,23 +259,23 @@
   1.123  			    error->message);
   1.124  			g_error_free(error);
   1.125  			g_main_loop_quit(loop);
   1.126 -			goto out;
   1.127 +			return (FALSE);
   1.128  		}
   1.129  
   1.130 -		if (!send_notification(raw_str, loop)) {
   1.131 -			g_warning("failed to send notification");
   1.132 +		/*
   1.133 +		 * schedule a timeout for sending a notification with the
   1.134 +		 * buffered message
   1.135 +		 */
   1.136 +		if (notify_timeout_id <= 0) {
   1.137 +			notify_timeout_id = g_timeout_add(BUFFER_TIMEOUT,
   1.138 +			    notify_timeout_cb, NULL);
   1.139  		}
   1.140  	}
   1.141  
   1.142  	if ((cond & G_IO_ERR) || (cond & G_IO_HUP)) {
   1.143  		g_critical("connection to master pty broken");
   1.144  		g_main_loop_quit(loop);
   1.145 -			goto out;
   1.146 -	}
   1.147 -
   1.148 -out:
   1.149 -	if (raw_str != NULL) {
   1.150 -		g_string_free(raw_str, TRUE);
   1.151 +		return (FALSE);
   1.152  	}
   1.153  
   1.154  	return (TRUE);
   1.155 @@ -270,7 +285,6 @@
   1.156  main(int argc, char *argv[])
   1.157  {
   1.158  	int		status = EXIT_FAILURE;
   1.159 -	GMainLoop	*loop = NULL;
   1.160  	GError		*error = NULL;
   1.161  	XWritedUnique	*app = NULL;
   1.162  	GOptionContext	*context = NULL;
   1.163 @@ -340,6 +354,8 @@
   1.164  		goto out;
   1.165  	}
   1.166  
   1.167 +	buffer = g_string_sized_new(BUFSIZ);
   1.168 +
   1.169  	/* open master pty */
   1.170  	masterfd = posix_openpt(O_RDWR | O_NOCTTY);
   1.171  	if (masterfd == -1) {
   1.172 @@ -380,7 +396,7 @@
   1.173  		goto out;
   1.174  	}
   1.175  	if (!g_io_add_watch(master_pty_channel, G_IO_IN | G_IO_PRI | G_IO_HUP |
   1.176 -	    G_IO_ERR, master_pty_read_cb, loop)) {
   1.177 +	    G_IO_ERR, master_pty_read_cb, NULL)) {
   1.178  		g_critical("failed to add watch on signal channel");
   1.179  		goto out;
   1.180  	}
   1.181 @@ -417,7 +433,7 @@
   1.182  		goto out;
   1.183  	}
   1.184  	if (g_io_add_watch(signal_channel, G_IO_IN | G_IO_PRI | G_IO_HUP |
   1.185 -	    G_IO_ERR, signal_read_cb, loop) == 0) {
   1.186 +	    G_IO_ERR, signal_read_cb, NULL) == 0) {
   1.187  		g_critical("failed to add watch on the signal channel");
   1.188  		goto out;
   1.189  	}
   1.190 @@ -473,6 +489,10 @@
   1.191  		close(masterfd);
   1.192  	}
   1.193  
   1.194 +	if (buffer != NULL) {
   1.195 +		g_string_free(buffer, FALSE);
   1.196 +	}
   1.197 +
   1.198  	if (app != NULL) {
   1.199  		g_object_unref(app);
   1.200  	}