Improve UI latency and CPU usage
Remove main_loop(), and use GIOChannels for the FIFO system.
--- 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[];