# HG changeset patch # User Myhailo Danylenko # Date 1259697966 -3600 # Node ID fca9a4c1743214a401723dd5e3aa8e6afa1c0afc # Parent 8036750d016989f604c962d4155675c2efca4e68 Improve UI latency and CPU usage Remove main_loop(), and use GIOChannels for the FIFO system. diff -r 8036750d0169 -r fca9a4c17432 mcabber/src/Makefile.am --- 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 \ diff -r 8036750d0169 -r fca9a4c17432 mcabber/src/commands.c --- 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" diff -r 8036750d0169 -r fca9a4c17432 mcabber/src/commands.h --- 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); diff -r 8036750d0169 -r fca9a4c17432 mcabber/src/fifo.c --- 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 + * Copyrigth (C) 2009 Myhailo Danylenko * * 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 #include #include #include #include #include #include -#include #include #include @@ -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... */ diff -r 8036750d0169 -r fca9a4c17432 mcabber/src/fifo.h --- 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__ */ diff -r 8036750d0169 -r fca9a4c17432 mcabber/src/hooks.c --- 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 @@ -92,7 +92,6 @@ last = now; } */ - fifo_read(); } void hk_message_in(const char *bjid, const char *resname, diff -r 8036750d0169 -r fca9a4c17432 mcabber/src/main.c --- 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 #include #include +#include #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 diff -r 8036750d0169 -r fca9a4c17432 mcabber/src/main.h --- /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... */ diff -r 8036750d0169 -r fca9a4c17432 mcabber/src/screen.c --- 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) diff -r 8036750d0169 -r fca9a4c17432 mcabber/src/settings.c --- 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) diff -r 8036750d0169 -r fca9a4c17432 mcabber/src/xmpp.c --- 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); diff -r 8036750d0169 -r fca9a4c17432 mcabber/src/xmpp.h --- 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); diff -r 8036750d0169 -r fca9a4c17432 mcabber/src/xmpp_iq.c --- 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[];