# HG changeset patch # User Guido Berhoerster # Date 1426546460 -3600 # Node ID eb97cafe34e530032ca0997aeabab0d454b37abe # Parent 6440ba6e3466a9aea492a265402e90db847043da 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. diff -r 6440ba6e3466 -r eb97cafe34e5 main.c --- 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 + * Copyright (C) 2015 Guido Berhoerster * * 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); }