changeset 1653:fca9a4c17432

Improve UI latency and CPU usage Remove main_loop(), and use GIOChannels for the FIFO system.
author Myhailo Danylenko <isbear@ukrpost.net>
date Tue, 01 Dec 2009 21:06:06 +0100
parents 8036750d0169
children 031eda601e8a
files mcabber/src/Makefile.am mcabber/src/commands.c mcabber/src/commands.h mcabber/src/fifo.c mcabber/src/fifo.h mcabber/src/hooks.c mcabber/src/main.c mcabber/src/main.h mcabber/src/screen.c mcabber/src/settings.c mcabber/src/xmpp.c mcabber/src/xmpp.h mcabber/src/xmpp_iq.c
diffstat 13 files changed, 197 insertions(+), 153 deletions(-) [+]
line wrap: on
line diff
--- a/mcabber/src/Makefile.am	Sun Nov 22 23:38:31 2009 +0200
+++ b/mcabber/src/Makefile.am	Tue Dec 01 21:06:06 2009 +0100
@@ -1,5 +1,5 @@
 bin_PROGRAMS = mcabber
-mcabber_SOURCES = main.c roster.c roster.h events.c events.h \
+mcabber_SOURCES = main.c main.h roster.c roster.h events.c events.h \
 		  commands.c commands.h compl.c compl.h \
 		  hbuf.c hbuf.h screen.c screen.h logprint.h \
 		  settings.c settings.h hooks.c hooks.h utf8.c utf8.h \
--- a/mcabber/src/commands.c	Sun Nov 22 23:38:31 2009 +0200
+++ b/mcabber/src/commands.c	Tue Dec 01 21:06:06 2009 +0100
@@ -38,6 +38,7 @@
 #include "otr.h"
 #include "utf8.h"
 #include "xmpp.h"
+#include "main.h"
 
 #define IMSTATUS_AWAY           "away"
 #define IMSTATUS_ONLINE         "online"
--- a/mcabber/src/commands.h	Sun Nov 22 23:38:31 2009 +0200
+++ b/mcabber/src/commands.h	Tue Dec 01 21:06:06 2009 +0100
@@ -27,9 +27,6 @@
 void cmd_add(const char *name, const char *help, guint flags1, guint flags2, void (*f)(char*), gpointer userdata);
 #endif
 
-extern char *mcabber_version(void);
-extern void mcabber_set_terminate_ui(void);
-
 void cmd_room_whois(gpointer bud, char *nick_locale, guint interactive);
 void cmd_room_leave(gpointer bud, char *arg);
 void cmd_setstatus(const char *recipient, const char *arg);
--- a/mcabber/src/fifo.c	Sun Nov 22 23:38:31 2009 +0200
+++ b/mcabber/src/fifo.c	Tue Dec 01 21:06:06 2009 +0100
@@ -2,6 +2,7 @@
  * fifo.c       -- Read commands from a named pipe
  *
  * Copyright (C) 2008,2009 Mikael Berthe <mikael@lilotux.net>
+ * Copyrigth (C) 2009      Myhailo Danylenko <isbear@ukrpost.net>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -19,14 +20,12 @@
  * USA
  */
 
-#include <stdio.h>
 #include <stdlib.h>
 #include <glib.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <unistd.h>
-#include <sys/time.h>
 #include <errno.h>
 #include <fcntl.h>
 
@@ -34,156 +33,146 @@
 #include "logprint.h"
 #include "utils.h"
 #include "settings.h"
-
-#include "hbuf.h"   // For HBB_BLOCKSIZE
+#include "main.h"
 
-static FILE *sfd;
-static char *fifo_name;
+static char *fifo_name = NULL;
+static GIOChannel *fifo_channel = NULL;
 
 static const char *FIFO_ENV_NAME = "MCABBER_FIFO";
 
-//  fifo_init(fifo_path)
-// Create and open the FIFO file.
-// If fifo_path is NULL, reopen the current pipe.
-// Return 0 (success) or -1 (failure).
+static gboolean attach_fifo(const char *name);
+
+static guint fifo_callback(GIOChannel *channel,
+                           GIOCondition condition,
+                           gpointer data)
+{
+  if (condition & (G_IO_IN|G_IO_PRI)) {
+    GIOStatus  chstat;
+    gchar     *buf;
+    gsize      endpos;
+
+    chstat = g_io_channel_read_line(channel, &buf, NULL, &endpos, NULL);
+    if (chstat == G_IO_STATUS_ERROR || chstat == G_IO_STATUS_EOF) {
+      if (!attach_fifo(fifo_name))
+        scr_LogPrint(LPRINT_LOGNORM,
+                     "Reopening fifo failed! Fifo will not work from now!");
+      return FALSE;
+    }
+    if (buf) {
+      guint logflag;
+      guint fifo_ignore = settings_opt_get_int("fifo_ignore");
+
+      if (endpos)
+        buf[endpos] = '\0';
+
+      if (settings_opt_get_int("fifo_hide_commands"))
+        logflag = LPRINT_LOG;
+      else
+        logflag = LPRINT_LOGNORM;
+      scr_LogPrint(logflag, "%s FIFO command: %s",
+                   (fifo_ignore ? "Ignoring" : "Executing"), buf);
+      if (!fifo_ignore) {
+        if (process_command(buf, TRUE) == 255)
+          mcabber_set_terminate_ui();
+      }
+
+      g_free(buf);
+    }
+  } else if (condition & (G_IO_ERR|G_IO_NVAL|G_IO_HUP)) {
+    if (!attach_fifo(fifo_name))
+      scr_LogPrint(LPRINT_LOGNORM,
+                   "Reopening fifo failed! Fifo will not work from now!");
+    return FALSE;
+  }
+  return TRUE;
+}
+
+static void fifo_destroy_callback(gpointer data)
+{
+  GIOChannel *channel = (GIOChannel *)data;
+  g_io_channel_unref(channel);
+}
+
+static gboolean check_fifo(const char *name)
+{
+  struct stat finfo;
+  if (stat(name, &finfo) == -1) {
+    /* some unknown error */
+    if (errno != ENOENT)
+      return FALSE;
+    /* fifo not yet exists */
+    if (mkfifo(name, S_IRUSR|S_IWUSR) != -1)
+      return check_fifo(name);
+    else
+      return FALSE;
+  }
+
+  /* file exists */
+  if (S_ISFIFO(finfo.st_mode))
+    return TRUE;
+  else
+    return FALSE;
+}
+
+static gboolean attach_fifo(const char *name)
+{
+  GSource *source;
+  int fd = open (name, O_RDONLY|O_NONBLOCK);
+  if (fd == -1)
+    return FALSE;
+
+  fifo_channel = g_io_channel_unix_new(fd);
+
+  g_io_channel_set_flags(fifo_channel, G_IO_FLAG_NONBLOCK, NULL);
+  g_io_channel_set_encoding(fifo_channel, NULL, NULL);
+  g_io_channel_set_close_on_unref(fifo_channel, TRUE);
+
+  source = g_io_create_watch(fifo_channel,
+                             G_IO_IN|G_IO_PRI|G_IO_ERR|G_IO_HUP|G_IO_NVAL);
+  g_source_set_callback(source, (GSourceFunc)fifo_callback,
+                        (gpointer)fifo_channel,
+                        (GDestroyNotify)fifo_destroy_callback);
+  g_source_attach(source, main_context);
+
+  return TRUE;
+}
+
 int fifo_init(const char *fifo_path)
 {
-  struct stat buf;
-  int fd;
-  char *fifo_path_xp;
-
-  if (!sfd && !fifo_path)
-    return -1;  // Nothing to do...
+  if (fifo_path) {
+    fifo_name = expand_filename(fifo_path);
 
-  if (sfd && !fifo_path) {  // We want to reinitialize the pipe
-    fclose(sfd);
-    sfd = NULL;
-    if (fifo_name)
-      goto fifo_init_open;
-  }
-  sfd = NULL;
-
-  fifo_path_xp = expand_filename(fifo_path);
-
-  if (!stat(fifo_path_xp, &buf)) {
-    if (!S_ISFIFO(buf.st_mode)) {
+    if (!check_fifo(fifo_name)) {
       scr_LogPrint(LPRINT_LOGNORM, "WARNING: Cannot create the FIFO. "
-                   "%s already exists and is not a pipe", fifo_path_xp);
-      g_free(fifo_path_xp);
+                   "%s already exists and is not a pipe", fifo_name);
+      g_free(fifo_name);
       return -1;
     }
+  } else if (fifo_name)
+    g_source_remove_by_user_data(fifo_channel);
+  else
+    return -1;
 
-    if (unlink(fifo_path_xp)) {
-      scr_LogPrint(LPRINT_LOGNORM, "WARNING: Unable to unlink FIFO %s [%s]",
-                   fifo_path_xp, g_strerror(errno));
-      g_free(fifo_path_xp);
-      return -1;
-    }
-  }
-
-  if (mkfifo(fifo_path_xp, S_IWUSR | S_IRUSR)) {
-    scr_LogPrint(LPRINT_LOGNORM, "WARNING: Cannot create the FIFO [%s]",
-                 g_strerror(errno));
-    g_free(fifo_path_xp);
+  if (!attach_fifo(fifo_name)) {
+    scr_LogPrint(LPRINT_LOGNORM, "Error: Cannot open fifo");
     return -1;
   }
 
-  fifo_name = fifo_path_xp;
-
-fifo_init_open:
-  fd = open(fifo_name, O_RDONLY | O_NONBLOCK);
-  if (!fd)
-    return -1;
-
   setenv(FIFO_ENV_NAME, fifo_name, 1);
 
-  sfd = fdopen(fd, "r");
-  if (fifo_path)
-    scr_LogPrint(LPRINT_LOGNORM, "FIFO initialized (%s)", fifo_name);
-  return 0;
+  scr_LogPrint(LPRINT_LOGNORM, "FIFO initialized (%s)", fifo_path);
+  return 1;
 }
 
-//  fifo_deinit()
-// Close the current FIFO pipe and delete it.
 void fifo_deinit(void)
 {
   unsetenv(FIFO_ENV_NAME);
-  if (sfd) {
-    fclose(sfd);
-    sfd = NULL;
-  }
-  if (fifo_name) {
-    unlink(fifo_name);
-    g_free(fifo_name);
-    fifo_name = NULL;
-  }
-}
 
-//  fifo_read()
-// Read a line from the FIFO pipe (if available), and execute it.
-void fifo_read(void)
-{
-  struct timeval tv;
-  fd_set fds;
-  char *getbuf;
-  char buf[HBB_BLOCKSIZE+1];
-  int fd;
-
-  if (!sfd) {
-    return;
-  }
-
-  tv.tv_sec = 0;
-  tv.tv_usec = 0;
-
-  fd = fileno(sfd);
-
-  FD_ZERO(&fds);
-  FD_SET(fd, &fds);
-
-  select(fd + 1, &fds, NULL, NULL, &tv);
-
-  if (!FD_ISSET(fd, &fds)) {
-    return;
-  }
-
-  getbuf = fgets(buf, HBB_BLOCKSIZE, sfd);
-  if (getbuf) {
-    guint logflag;
-    char *eol = buf;
-    guint fifo_ignore = settings_opt_get_int("fifo_ignore");
-
-    // Strip trailing newlines
-    for ( ; *eol ; eol++)
-      ;
-    if (eol > buf)
-      eol--;
-    while (eol > buf && *eol == '\n')
-      *eol-- = 0;
-
-    if (settings_opt_get_int("fifo_hide_commands"))
-      logflag = LPRINT_LOG;
-    else
-      logflag = LPRINT_LOGNORM;
-    scr_LogPrint(logflag, "%s FIFO command: %s",
-                 (fifo_ignore ? "Ignoring" : "Executing"), buf);
-    if (!fifo_ignore) {
-      if (process_command(buf, TRUE) == 255)
-        mcabber_set_terminate_ui();
-    }
-  } else {
-    if (feof(sfd))
-      fifo_init(NULL);  // Reopen the FIFO on EOF
-  }
-}
-
-//  fifo_get_fd()
-// Return the FIFO file descriptor (-1 if none).
-int fifo_get_fd(void)
-{
-  if (sfd)
-    return fileno(sfd);
-  return -1;
+  /* destroy open fifo */
+  unlink(fifo_name);
+  g_source_remove_by_user_data(fifo_channel);
+  /* channel itself should be destroyed by destruction callback */
+  g_free(fifo_name);
 }
 
 /* vim: set expandtab cindent cinoptions=>2\:2(0:  For Vim users... */
--- a/mcabber/src/fifo.h	Sun Nov 22 23:38:31 2009 +0200
+++ b/mcabber/src/fifo.h	Tue Dec 01 21:06:06 2009 +0100
@@ -3,8 +3,6 @@
 
 int  fifo_init(const char *fifo_path);
 void fifo_deinit(void);
-void fifo_read(void);
-int  fifo_get_fd(void);
 
 #endif /* __FIFO_H__ */
 
--- a/mcabber/src/hooks.c	Sun Nov 22 23:38:31 2009 +0200
+++ b/mcabber/src/hooks.c	Tue Dec 01 21:06:06 2009 +0100
@@ -34,7 +34,7 @@
 #include "utils.h"
 #include "utf8.h"
 #include "commands.h"
-#include "fifo.h"
+#include "main.h"
 
 #ifdef MODULES_ENABLE
 #include <glib.h>
@@ -92,7 +92,6 @@
     last = now;
   }
   */
-  fifo_read();
 }
 
 void hk_message_in(const char *bjid, const char *resname,
--- a/mcabber/src/main.c	Sun Nov 22 23:38:31 2009 +0200
+++ b/mcabber/src/main.c	Tue Dec 01 21:06:06 2009 +0100
@@ -30,6 +30,7 @@
 #include <sys/wait.h>
 #include <glib.h>
 #include <config.h>
+#include <poll.h>
 
 #include "caps.h"
 #include "screen.h"
@@ -53,7 +54,9 @@
 #endif
 
 static unsigned int terminate_ui;
-GMainLoop *main_loop = NULL;
+GMainContext *main_context;
+
+static gboolean update_screen = TRUE;
 
 static struct termios *backup_termios;
 
@@ -70,7 +73,6 @@
 
 static void mcabber_terminate(const char *msg)
 {
-  fifo_deinit();
   xmpp_disconnect();
   scr_TerminateCurses();
 
@@ -251,12 +253,32 @@
   terminate_ui = TRUE;
 }
 
-gboolean mcabber_loop()
+typedef struct {
+  GSource source;
+  GPollFD pollfd;
+} mcabber_source_t;
+
+static gboolean mcabber_source_prepare(GSource *source, gint *timeout)
+{
+  *timeout = -1;
+  return FALSE;
+}
+
+static gboolean mcabber_source_check(GSource *source)
+{
+  mcabber_source_t *mc_source = (mcabber_source_t *) source;
+  gushort revents = mc_source->pollfd.revents;
+  if (revents)
+    return TRUE;
+  return FALSE;
+}
+
+static gboolean mcabber_source_dispatch(GSource *source, GSourceFunc callback,
+                                        gpointer udata)
 {
   keycode kcode;
 
   if (terminate_ui) {
-    g_main_loop_quit(main_loop);
     return FALSE;
   }
   scr_DoUpdate();
@@ -264,18 +286,24 @@
 
   while (kcode.value != ERR) {
     process_key(kcode);
-    scr_DoUpdate();
+    update_screen = TRUE;
     scr_Getch(&kcode);
   }
   scr_CheckAutoAway(FALSE);
 
-  if (update_roster)
-    scr_DrawRoster();
-
   hk_mainloop();
   return TRUE;
 }
 
+static GSourceFuncs mcabber_source_funcs = {
+  mcabber_source_prepare,
+  mcabber_source_check,
+  mcabber_source_dispatch,
+  NULL,
+  NULL,
+  NULL
+};
+
 int main(int argc, char **argv)
 {
   char *configFile = NULL;
@@ -398,7 +426,7 @@
   /* Load previous roster state */
   hlog_load_state();
 
-  main_loop = g_main_loop_new(NULL, TRUE);
+  main_context = g_main_context_default();
 
   if (ret < 0) {
     scr_LogPrint(LPRINT_NORMAL, "No configuration file has been found.");
@@ -408,10 +436,29 @@
     xmpp_connect();
   }
 
-  scr_LogPrint(LPRINT_DEBUG, "Entering into main loop...");
+  { // add keypress processing source
+    GSource *mc_source = g_source_new(&mcabber_source_funcs,
+                                      sizeof(mcabber_source_t));
+    GPollFD *mc_pollfd = &(((mcabber_source_t *)mc_source)->pollfd);
+    mc_pollfd->fd = STDIN_FILENO;
+    mc_pollfd->events = POLLIN|POLLERR|POLLPRI;
+    mc_pollfd->revents = 0;
+    g_source_add_poll(mc_source, mc_pollfd);
+    g_source_attach(mc_source, main_context);
+
+    scr_LogPrint(LPRINT_DEBUG, "Entering into main loop...");
 
-  g_timeout_add(100, mcabber_loop, NULL);
-  g_main_loop_run(main_loop);
+    while(!terminate_ui) {
+      g_main_context_iteration(main_context, TRUE);
+      if (update_roster)
+        scr_DrawRoster();
+      if(update_screen)
+        scr_DoUpdate();
+    }
+
+    g_source_destroy(mc_source);
+    g_source_unref(mc_source);
+  }
 
   scr_TerminateCurses();
 #ifdef MODULES_ENABLE
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mcabber/src/main.h	Tue Dec 01 21:06:06 2009 +0100
@@ -0,0 +1,11 @@
+#ifndef __MCABBER_MAIN_H__
+#define __MCABBER_MAIN_H__ 1
+
+extern GMainContext *main_context;
+
+void mcabber_set_terminate_ui(void);
+char *mcabber_version(void);
+
+#endif
+
+/* vim: set expandtab cindent cinoptions=>2\:2(0:  For Vim users... */
--- a/mcabber/src/screen.c	Sun Nov 22 23:38:31 2009 +0200
+++ b/mcabber/src/screen.c	Tue Dec 01 21:06:06 2009 +0100
@@ -59,6 +59,7 @@
 #include "settings.h"
 #include "utils.h"
 #include "xmpp.h"
+#include "main.h"
 
 #define get_color(col)  (COLOR_PAIR(col)|COLOR_ATTRIB[col])
 #define compose_color(col)  (COLOR_PAIR(col->color_pair)|col->color_attrib)
--- a/mcabber/src/settings.c	Sun Nov 22 23:38:31 2009 +0200
+++ b/mcabber/src/settings.c	Tue Dec 01 21:06:06 2009 +0100
@@ -30,6 +30,7 @@
 #include "otr.h"
 #include "utils.h"
 #include "xmpp.h"
+#include "main.h"
 
 // Maximum line length
 // (probably best to use the same value as INPUTLINE_LENGTH)
--- a/mcabber/src/xmpp.c	Sun Nov 22 23:38:31 2009 +0200
+++ b/mcabber/src/xmpp.c	Tue Dec 01 21:06:06 2009 +0100
@@ -39,6 +39,7 @@
 #include "screen.h"
 #include "settings.h"
 #include "utils.h"
+#include "main.h"
 
 #define RECONNECTION_TIMEOUT    60L
 
@@ -1615,8 +1616,7 @@
     return;
   }
 
-  lconnection = lm_connection_new_with_context
-          (NULL, g_main_loop_get_context(main_loop));
+  lconnection = lm_connection_new_with_context(NULL, main_context);
 
   g_log_set_handler("LM", LM_LOG_LEVEL_ALL, lm_debug_handler, NULL);
 
--- a/mcabber/src/xmpp.h	Sun Nov 22 23:38:31 2009 +0200
+++ b/mcabber/src/xmpp.h	Tue Dec 01 21:06:06 2009 +0100
@@ -30,7 +30,6 @@
 
 extern LmConnection* lconnection;
 extern LmSSL* lssl;
-extern GMainLoop *main_loop;
 
 void xmpp_connect(void);
 void xmpp_disconnect(void);
--- a/mcabber/src/xmpp_iq.c	Sun Nov 22 23:38:31 2009 +0200
+++ b/mcabber/src/xmpp_iq.c	Tue Dec 01 21:06:06 2009 +0100
@@ -33,6 +33,7 @@
 #include "logprint.h"
 #include "settings.h"
 #include "caps.h"
+#include "main.h"
 
 extern struct xmpp_error xmpp_errors[];