changeset 288:e7ef3db3b954

Implement "windows" in the chat. Only main window used for now, and even that is not working perfectly yet, tho.
author Matti Hamalainen <ccr@tnsp.org>
date Sat, 11 Jun 2011 01:10:48 +0300
parents eb74097b73f5
children c53f880837f5
files nnchat.c
diffstat 1 files changed, 227 insertions(+), 112 deletions(-) [+]
line wrap: on
line diff
--- a/nnchat.c	Sat Jun 11 01:09:27 2011 +0300
+++ b/nnchat.c	Sat Jun 11 01:10:48 2011 +0300
@@ -35,8 +35,19 @@
 #define SET_BACKBUF_LEN (512)       /* Backbuffer size (in lines) */
 #define SET_MAX_HISTORY (16)        /* Command history length */
 #define SET_KEEPALIVE   (15*60)     /* Ping/keepalive period in seconds */
+#define SET_MAX_WINDOWS (32)
 
 
+typedef struct {
+    qringbuf_t *data;   /* "Backbuffer" data for this window */
+    int pos;            /* Current position in the window, 0 = real time */
+    char *id;           /* Chatter ID, NULL = main window */
+    BOOL dirty;
+    
+    char *buf;
+    size_t len, bufsize;
+} nn_window_t;
+
 
 /* Options
  */
@@ -54,14 +65,17 @@
 char    optNickSep = ':';
 BOOL    optDaemon = FALSE;
 FILE    *optLogFile = NULL;
-WINDOW  *mainWin = NULL,
-        *statusWin = NULL,
-        *editWin = NULL;
 BOOL    setPrvMode = FALSE;
 BOOL    setIgnoreMode = FALSE;
 BOOL    optDebug = FALSE;
 BOOL    optLogEnable = FALSE;
 
+nn_window_t *chatWindows[SET_MAX_WINDOWS],
+        *currWin = NULL;
+WINDOW  *mainWin = NULL,
+        *statusWin = NULL,
+        *editWin = NULL;
+
 qlist_t *setIgnoreList = NULL,
         *setIdleMessages = NULL;
 nn_userhash_t *nnUsers = NULL;
@@ -174,6 +188,7 @@
     return TRUE;
 }
 
+
 BOOL getTimeStamp(char *str, size_t len, const char *fmt)
 {
     time_t stamp = time(NULL);
@@ -188,23 +203,43 @@
 }
 
 
-char *encodeUsername(char *str)
+nn_window_t *nn_window_new()
 {
-    unsigned char *c = (unsigned char *) str;
-    if (str == NULL) return NULL;
-    for (; *c ; c++) 
-        if (*c == ' ') *c = 255;
-    return str;
+    nn_window_t *res = th_calloc(1, sizeof(nn_window_t));
+    
+    if (res == NULL) return NULL;
+
+    res->data = th_ringbuf_new(SET_BACKBUF_LEN, th_free);
+    if (res->data == NULL) {
+        th_free(res);
+        return NULL;
+    }
+    
+    return res;
 }
 
 
-char *decodeUsername(char *str)
+void nn_window_free(nn_window_t *win)
 {
-    unsigned char *c = (unsigned char *) str;
-    if (str == NULL) return NULL;
-    for (; *c ; c++) 
-        if (*c == 255) *c = ' ';
-    return str;
+    if (win != NULL) {
+        th_ringbuf_free(win->data);
+        th_free(win->id);
+        th_free(win);
+    }
+}
+
+
+nn_window_t *nn_find_window(const char *id)
+{
+    int i;
+
+    for (i = 0; i < SET_MAX_WINDOWS; i++)
+        if (chatWindows[i] != NULL &&
+            chatWindows[i]->id != NULL &&
+            strcasecmp(id, chatWindows[i]->id) == 0)
+            return chatWindows[i];
+
+    return NULL;
 }
 
 
@@ -247,13 +282,14 @@
     wrefresh(statusWin);
 }
 
+
 void printEditBuf(const char *str, nn_editbuf_t *buf)
 {
     char *tmp;
-    if (statusWin == NULL || buf == NULL) return;
+    if (editWin == NULL || buf == NULL) return;
 
     buf->data[buf->len] = 0;
-    tmp = decodeUsername(th_strdup(buf->data));
+    tmp = nn_username_decode(th_strdup(buf->data));
     
     werase(editWin);
     
@@ -307,11 +343,7 @@
                 }
             }
         } else {
-            if ((unsigned char) *s == 255)
-                waddch(win, ((unsigned char) ' ') | col);
-            else
-            if (*s != '\r')
-                waddch(win, ((unsigned char) *s) | col);
+            waddch(win, ((unsigned char) *s) | col);
             s++;
         }
     }
@@ -319,6 +351,55 @@
 }
 
 
+void nn_window_print(nn_window_t *win, const char *fmt)
+{
+    const char *s = fmt;
+    while (*s) {
+        if (*s == '\n') {
+            th_vputch(&(win->buf), &(win->bufsize), &(win->len), '\n');
+            th_vputch(&(win->buf), &(win->bufsize), &(win->len), 0);
+            th_ringbuf_add(win->data, win->buf);
+            win->buf = NULL;
+            win->dirty = TRUE;
+        }
+        else    
+        if ((unsigned char) *s == 255)
+            th_vputch(&(win->buf), &(win->bufsize), &(win->len), ' ');
+        else
+        if (*s != '\r')
+            th_vputch(&(win->buf), &(win->bufsize), &(win->len), *s);
+        s++;
+    }
+}
+
+
+void updateMainWin(BOOL force)
+{
+    int y, w, h, offs;
+    qringbuf_t *buf;
+
+    if (mainWin == NULL || currWin == NULL) return;
+    if (!force && !currWin->dirty) return;
+
+    buf = currWin->data;
+    getmaxyx(mainWin, h, w);
+    werase(mainWin);
+    
+    offs = buf->size - h - currWin->pos;
+    if (offs < 0)
+        offs = 0;
+
+    for (y = 0; y < h && offs < buf->size; offs++) {
+        if (buf->data[offs] != NULL)
+            printWin(mainWin, (char *) buf->data[offs]);
+        y = getcury(mainWin);
+    }
+    
+    currWin->dirty = FALSE;
+    wrefresh(mainWin);
+}
+
+
 int printFile(FILE *outFile, const char *fmt)
 {
     const char *s = fmt;
@@ -346,13 +427,13 @@
     return 0;
 }
 
-void printMsgV(int flags, const char *fmt, va_list ap)
+void printMsgV(nn_window_t *win, int flags, const char *fmt, va_list ap)
 {
-    char tmpStr[128], buf[8192];
+    char tmpStr[128], *buf;
     
     getTimeStamp(tmpStr, sizeof(tmpStr), "½17½[½11½%H:%M:%S½17½]½0½ ");
     
-    vsnprintf(buf, sizeof(buf), fmt, ap);
+    buf = th_strdup_vprintf(fmt, ap);
     
     if (optLogFile && (flags & LOG_FILE)) {
         if (flags & LOG_STAMP) printFile(optLogFile, tmpStr);
@@ -361,36 +442,39 @@
     }
     
     if (!optDaemon && (flags & LOG_WINDOW)) {
-        if (flags & LOG_STAMP) printWin(mainWin, tmpStr);
-        printWin(mainWin, buf);
-        wrefresh(mainWin);
+        nn_window_t *tmp = win != NULL ? win : chatWindows[0];
+        if (flags & LOG_STAMP) nn_window_print(tmp, tmpStr);
+        nn_window_print(tmp, buf);
+        updateMainWin(FALSE);
     }
+    
+    th_free(buf);
 }
 
-void printMsg(const char *fmt, ...)
+void printMsg(nn_window_t *win, const char *fmt, ...)
 {
     va_list ap;
     
     va_start(ap, fmt);
-    printMsgV(LOG_STAMP | LOG_WINDOW | LOG_FILE, fmt, ap);
+    printMsgV(win, LOG_STAMP | LOG_WINDOW | LOG_FILE, fmt, ap);
     va_end(ap);
 }
 
-void printMsgC(const char *fmt, ...)
+void printMsgC(nn_window_t *win, const char *fmt, ...)
 {
     va_list ap;
     
     va_start(ap, fmt);
-    printMsgV(LOG_WINDOW | LOG_FILE, fmt, ap);
+    printMsgV(win, LOG_WINDOW | LOG_FILE, fmt, ap);
     va_end(ap);
 }
 
-void printMsgQ(BOOL logOnly, const char *fmt, ...)
+void printMsgQ(nn_window_t *win, BOOL logOnly, const char *fmt, ...)
 {
     va_list ap;
     
     va_start(ap, fmt);
-    printMsgV(logOnly ? (LOG_STAMP | LOG_FILE) : (LOG_STAMP | LOG_WINDOW | LOG_FILE), fmt, ap);
+    printMsgV(win, logOnly ? (LOG_STAMP | LOG_FILE) : (LOG_STAMP | LOG_WINDOW | LOG_FILE), fmt, ap);
     va_end(ap);
 }
 
@@ -403,7 +487,7 @@
     va_list ap2;
 
     va_copy(ap2, ap);
-    printMsgV(LOG_STAMP | LOG_WINDOW | LOG_FILE, fmt, ap);
+    printMsgV(chatWindows[0], LOG_STAMP | LOG_WINDOW | LOG_FILE, fmt, ap);
 
     tmp = th_strdup_vprintf(fmt, ap2);
     
@@ -434,7 +518,7 @@
 void messageFunc(struct _nn_conn_t *conn, const char *fmt, va_list ap)
 {
     (void) conn;
-    printMsgV(LOG_STAMP | LOG_WINDOW | LOG_FILE, fmt, ap);
+    printMsgV(chatWindows[0], LOG_STAMP | LOG_WINDOW | LOG_FILE, fmt, ap);
 }
 
 
@@ -500,13 +584,13 @@
                 setTarget = th_strdup(h + 9);
                 for (q = setTarget; *q && *q != ':'; q++);
                 *q = 0;
-                printMsg("PRV target autoset to '%s'\n", setTarget);
+                printMsg(NULL, "PRV target autoset to '%s'\n", setTarget);
             }
-            printMsgQ(isIgnored, "½11½%s½0½\n", h);
+            printMsgQ(NULL, isIgnored, "½11½%s½0½\n", h);
         } else {
             /* It's an action (/me) */
             h = nn_decode_str2(t);
-            printMsgQ(isIgnored, "½9½* %s½0½\n", h);
+            printMsgQ(NULL, isIgnored, "½9½* %s½0½\n", h);
         }
         th_free(h);
         th_free(t);
@@ -514,7 +598,7 @@
         /* It's a normal message */
         t = nn_strip_tags(s);
         h = nn_decode_str2(t);
-        printMsgQ(isIgnored, "½5½<½%d½%s½5½>½0½ %s\n", isMine ? 14 : 15, userName, h);
+        printMsgQ(NULL, isIgnored, "½5½<½%d½%s½5½>½0½ %s\n", isMine ? 14 : 15, userName, h);
         th_free(h);
         th_free(t);
     }
@@ -533,10 +617,10 @@
     getTimeStamp(tmpStr, sizeof(tmpStr), "%c");
     
     if (!strncmp(str, "FAILURE", 7)) {
-        printMsg("½1½Login failure½0½ - ½3½%s½0½\n", tmpStr);
+        printMsg(NULL, "½1½Login failure½0½ - ½3½%s½0½\n", tmpStr);
         return -2;
     } else if (!strncmp(str, "SUCCESS", 7)) {
-        printMsg("½2½Login success½0½ - ½3½%s½0½\n", tmpStr);
+        printMsg(NULL, "½2½Login success½0½ - ½3½%s½0½\n", tmpStr);
         nn_conn_send_msg(conn, optUserNameEnc, "%%2FRequestUserList");
         return 0;
     } else
@@ -556,9 +640,9 @@
     p = nn_dbldecode_str(str);
     if (!p) return -1;
 
-    nn_userhash_insert(nnUsers, encodeUsername(p));
+    nn_userhash_insert(nnUsers, nn_username_encode(p));
 
-    printMsg("! ½3½%s½0½ ½2½ADDED.½0½\n", p);
+    printMsg(NULL, "! ½3½%s½0½ ½2½ADDED.½0½\n", p);
     th_free(p);
     return 0;
 }
@@ -576,9 +660,9 @@
     p = nn_dbldecode_str(str);
     if (!p) return -1;
 
-    nn_userhash_delete(nnUsers, encodeUsername(p));
+    nn_userhash_delete(nnUsers, nn_username_encode(p));
 
-    printMsg("! ½3½%s½0½ ½1½DELETED.½0½\n", p);
+    printMsg(NULL, "! ½3½%s½0½ ½1½DELETED.½0½\n", p);
     th_free(p);
     return 0;
 }
@@ -637,7 +721,7 @@
     }
 
     if (optDebug) {
-        printMsg("Unknown protocmd: \"%s\"\n", buf);
+        printMsg(NULL, "Unknown protocmd: \"%s\"\n", buf);
         return 0;
     } else
         return 1;
@@ -666,7 +750,7 @@
         buf[bufLen--] = 0;
 
     /* Decode completed usernames */
-    decodeUsername(buf);
+    nn_username_decode(buf);
     
     /* Check for special user commands */
     if (*buf == 0) {
@@ -676,11 +760,11 @@
         /* Change color */
         int tmpInt;
         if ((tmpInt = th_get_hex_triplet(trimLeft(buf + 7))) < 0) {
-            printMsg("Invalid color value '%s'\n", buf+7);
+            printMsg(currWin, "Invalid color value '%s'\n", buf+7);
             return 1;
         }
         optUserColor = tmpInt;
-        printMsg("Setting color to #%06x\n", optUserColor);
+        printMsg(currWin, "Setting color to #%06x\n", optUserColor);
         nn_conn_send_msg(conn, optUserNameEnc, "%%2FSetFontColor%%20%%2Dcolor%%20%06X", optUserColor);
         return 0;
     }
@@ -690,26 +774,44 @@
             /* Add or remove someone to/from ignore */
             qlist_t *user = th_llist_find_func(setIgnoreList, name, compareUsername);
             if (user != NULL) {
-                printMsg("Removed user '%s' from ignore.\n", name);
+                printMsg(currWin, "Removed user '%s' from ignore.\n", name);
                 th_llist_delete_node(&setIgnoreList, user);
             } else {
-                printMsg("Now ignoring '%s'.\n", name);
+                printMsg(currWin, "Now ignoring '%s'.\n", name);
                 th_llist_append(&setIgnoreList, th_strdup(name));
             }
         } else {
             /* Just list whomever is in ignore now */
             qlist_t *user = setIgnoreList;
             ssize_t nuser = th_llist_length(setIgnoreList);
-            printMsg("Users ignored (%d): ", nuser);
+            char *result = th_strdup_printf("Users ignored (%d): ", nuser);
             while (user != NULL) {
                 if (user->data != NULL) {
-                    printMsgC("'%s'", (char *) user->data);
+                    th_pstr_printf(&result, "%s'%s'", result, (char *) user->data);
                     if (--nuser > 0)
-                        printMsgC(", ");
+                        th_pstr_printf(&result, "%s, ", result);
                 }
                 user = user->next;
             }
-            printMsgC("\n");
+            printMsg(currWin, "%s\n", result);
+            th_free(result);
+        }
+        return 0;
+    }
+    else if (!strncasecmp(buf, "/query", 6)) {
+        char *name = trimLeft(buf + 6);
+        if (strlen(name) > 0) {
+//            qlist_t *user = th_llist_find_func(setIgnoreList, name, compareUsername);
+            printMsg(currWin, "Opening query for '%s'.\n", name);
+            
+        } else {
+        }
+        return 0;
+    }
+    else if (!strncasecmp(buf, "/win", 4)) {
+        char *name = trimLeft(buf + 4);
+        if (strlen(name) > 0) {
+        } else {
         }
         return 0;
     }
@@ -717,11 +819,11 @@
         /* Save configuration */
         FILE *cfgfile = fopen(setConfigFile, "w");
         if (cfgfile == NULL) {
-            printMsg("Could not create configuration to file '%s': %s\n", setConfigFile,
-                strerror(errno));
+            printMsg(currWin, "Could not create configuration to file '%s': %s\n",
+                setConfigFile, strerror(errno));
             return 0;
         }
-        printMsg("Configuration saved in file '%s', res=%d\n",
+        printMsg(currWin, "Configuration saved in file '%s', res=%d\n",
             setConfigFile,
             th_cfg_write(cfgfile, setConfigFile, cfg));
 
@@ -732,7 +834,7 @@
         /* Open given username's profile via firefox in a new tab */
         char *name = trimLeft(buf + 3);
 
-        printMsg("Opening profile for: '%s'\n", name);
+        printMsg(currWin, "Opening profile for: '%s'\n", name);
 
         tmpStr = nn_encode_str1(name);
 #ifdef __WIN32
@@ -742,7 +844,7 @@
         th_free(tmpStr);
         status = ShellExecute(NULL, "open", tmpBuf, NULL, NULL, SW_SHOWNA);
         if (status <= 32)
-            printMsg("Could not launch default web browser: %d\n", status);
+            printMsg(currWin, "Could not launch default web browser: %d\n", status);
         }
 #else
         {
@@ -754,12 +856,12 @@
 
         if (pipe(fds) == -1) {
             int ret = errno;
-            printMsg("Could not open process communication pipe! (%d, %s)\n", ret, strerror(ret));
+            printMsg(currWin, "Could not open process communication pipe! (%d, %s)\n", ret, strerror(ret));
             return 0;
         }
 
         if ((pid = fork()) < 0) {
-            printMsg("Could not create sub-process!\n");
+            printMsg(currWin, "Could not create sub-process!\n");
         } else if (pid == 0) {
             dup2(fds[1], STDOUT_FILENO);
             dup2(fds[0], STDERR_FILENO);
@@ -778,10 +880,10 @@
         th_free(setTarget);
         if (strlen(name) > 0) {
             setTarget = th_strdup(trimLeft(buf + 3));
-            printMsg("Set prv target to '%s'\n", setTarget);
+            printMsg(NULL, "Set prv target to '%s'\n", setTarget);
         } else {
             setTarget = NULL;
-            printMsg("Cleared prv target.\n");
+            printMsg(NULL, "Cleared prv target.\n");
         }
         return 0;
     }
@@ -796,14 +898,14 @@
             snprintf(tmpBuf, sizeof(tmpBuf), "/prv -to %s -msg %s", setTarget, buf);
             buf = tmpBuf;
         } else {
-            printMsg("No target set, exiting prv mode.\n");
+            printMsg(NULL, "No target set, exiting prv mode.\n");
             setPrvMode = FALSE;
             return 1;
         }
     }
     
     /* Send double-encoded */
-    tmpStr = nn_dblencode_str(decodeUsername(buf));
+    tmpStr = nn_dblencode_str(nn_username_decode(buf));
     if (tmpStr == 0) return -2;
     result = nn_conn_send_msg(conn, optUserNameEnc, "%s", tmpStr);
     th_free(tmpStr);
@@ -831,7 +933,7 @@
     editWin = subwin(stdscr, 3, w, h - 3, 0);
         
     if (mainWin == NULL || statusWin == NULL || editWin == NULL) {
-        THERR("Could not create ncurses windows!\n");
+        THERR("Could not create curses chatWindows!\n");
         return FALSE;
     }
     scrollok(mainWin, 1);
@@ -878,7 +980,7 @@
         mode = 3;
     } else {
         if (optDebug)
-            printMsg("no mode\n");
+            printMsg(currWin, "no mode\n");
         return FALSE;
     }
 
@@ -886,7 +988,7 @@
         endPos--;
         if (startPos > 0) {
             if (optDebug)
-                printMsg("str[endPos] == optNickSep && startPos > 0 (%d)\n", startPos);
+                printMsg(currWin, "str[endPos] == optNickSep && startPos > 0 (%d)\n", startPos);
             return FALSE;
         }
         hasSeparator = TRUE;
@@ -913,7 +1015,7 @@
     }
 
     if (optDebug) {
-        printMsg("sPos=%d, ePos=%d <-> bPos=%d, bufLen=%d : pat='%s' (again=%s, hassep=%s, hasspc=%s, newpat=%s, mode=%d)\n",
+        printMsg(currWin, "sPos=%d, ePos=%d <-> bPos=%d, bufLen=%d : pat='%s' (again=%s, hassep=%s, hasspc=%s, newpat=%s, mode=%d)\n",
                   startPos, endPos, buf->pos, buf->len, pattern,
                   again ? "yes" : "no",
                   hasSeparator ? "yes" : "no",
@@ -928,7 +1030,7 @@
             int i;
             char *c = user->name;
             if (optDebug)
-                printMsg("match='%s' / prev='%s'\n", user->name, previous);
+                printMsg(currWin, "match='%s' / prev='%s'\n", user->name, previous);
 
             for (i = startPos; i <= endPos; i++)
                 nn_editbuf_delete(buf, startPos);
@@ -1067,11 +1169,9 @@
     nn_editbuf_t *histBuf[SET_MAX_HISTORY+2];
     int histPos = 0, histMax = 0;
 
-    nn_ringbuf_t *backBuf;
-
     cfgitem_t *tmpcfg;
     char *homeDir = NULL;
-    
+
     
     /* Initialize */
     th_init("NNChat", "Newbie Nudes chat client", NN_VERSION,
@@ -1208,7 +1308,10 @@
         
         if (!initializeWindows())
             goto err_exit;
-        
+
+        memset(chatWindows, 0, sizeof(chatWindows));
+        chatWindows[0] = nn_window_new();
+        currWin = chatWindows[0];        
         updateStatus(insertMode);
     }
 
@@ -1225,13 +1328,13 @@
     }
 
     /* Okay ... */
-    printMsg("Trying to resolve host '%s' ...\n", optServer);
+    printMsg(currWin, "Trying to resolve host '%s' ...\n", optServer);
     tmpHost = gethostbyname(optServer);
     if (tmpHost == NULL) {
         errorMsg("Could not resolve hostname: %s.\n", strerror(h_errno));
         goto err_exit;
     }
-    printMsg("True hostname: %s\n", tmpHost->h_name);
+    printMsg(currWin, "True hostname: %s\n", tmpHost->h_name);
 
     /* To emulate the official client, we first make a request for
      * policy file, even though we don't use it for anything...
@@ -1249,9 +1352,9 @@
     } else {
         int cres = nn_conn_pull(conn);
         if (cres == 0) {
-            printMsg("Probe got: %s\n", conn->buf);
+            printMsg(currWin, "Probe got: %s\n", conn->buf);
         } else {
-            printMsg("Could not get policy probe.\n");
+            printMsg(currWin, "Could not get policy probe.\n");
         }
     }
     nn_conn_close(conn);
@@ -1279,13 +1382,18 @@
     if (cursesInit) {
         /* Initialize rest of interactive UI code */
         memset(histBuf, 0, sizeof(histBuf));
-        backBuf = nn_ringbuf_new(SET_BACKBUF_LEN);
+        memset(histBuf, 0, sizeof(histBuf));
         nn_editbuf_clear(editBuf);
 
         /* First update of screen */
         printEditBuf("", editBuf);
         updateStatus(insertMode);
+
+        printMsg(NULL, "%s v%s - %s\n", th_prog_name, th_prog_version, th_prog_fullname);
+        printMsg(NULL, "%s\n", th_prog_author);
+        printMsg(NULL, "%s\n", th_prog_license);
     }
+    
 
     /* Enter mainloop */
     while (!isError && !exitProg) {
@@ -1297,7 +1405,7 @@
 
                 if (result > 0) {
                     /* Couldn't handle the message for some reason */
-                    printMsg("Could not handle: %s\n", conn->ptr);
+                    printMsg(currWin, "Could not handle: %s\n", conn->ptr);
                 } else if (result < 0) {
                     /* Fatal error, quit */
                     errorMsg("Fatal error with message: %s\n", conn->ptr);
@@ -1314,7 +1422,7 @@
         /* Handle user input */
         if (cursesInit) {
             int c, cnt = 0;
-            BOOL update = FALSE;
+            BOOL update = FALSE, updateMain = TRUE;
             
             /* Handle several buffered keypresses at once */
             do {
@@ -1327,7 +1435,7 @@
                         case 'd': c = 0x204; break;
                         case 'c': c = 0x206; break;
                         default:
-                            printMsg("Unhandled ESC-O key sequence 0x%02x\n", c);
+                            printMsg(currWin, "Unhandled ESC-O key sequence 0x%02x\n", c);
                             break;
                     }
                 } else
@@ -1352,7 +1460,7 @@
                         case 0x38: c = KEY_END; break;
                         
                         default:
-                            printMsg("Unhandled ESC-[*~ key sequence 0x%02x\n", c);
+                            printMsg(currWin, "Unhandled ESC-[*~ key sequence 0x%02x\n", c);
                             c = ERR;
                             break;
                     }
@@ -1360,7 +1468,7 @@
                     if (c != ERR)
                         wgetch(stdscr);
                 } else {
-                    printMsg("Unhandled ESC key sequence 0x%02x\n", c);
+                    printMsg(currWin, "Unhandled ESC key sequence 0x%02x\n", c);
                     continue;
                 }
             }
@@ -1376,10 +1484,12 @@
 #endif
                                 
                 if (!initializeWindows()) {
-                    errorMsg("Error resizing curses windows\n");
+                    errorMsg("Error resizing curses chatWindows\n");
                     isError = TRUE;
                 }
+
                 update = TRUE;
+                updateMain = TRUE;
                 break;
 #endif
             
@@ -1488,12 +1598,12 @@
 
             case KEY_F(5): /* F5 = Ignore mode */
                 setIgnoreMode = !setIgnoreMode;
-                printMsg("Ignore mode = %s\n", setIgnoreMode ? "ON" : "OFF");
+                printMsg(currWin, "Ignore mode = %s\n", setIgnoreMode ? "ON" : "OFF");
                 break;
             
             case KEY_F(7): /* F7 = Clear PRV target */
                 if (setTarget) {
-                    printMsg("Cleared PRV target.\n");
+                    printMsg(NULL, "Cleared PRV target.\n");
                     setPrvMode = FALSE;
                     th_free(setTarget);
                     setTarget = NULL;
@@ -1513,7 +1623,7 @@
             
             case 0x03: /* ^C = quit */
             case KEY_F(9): /* F9 = Quit */
-                printMsg("Quitting per user request.\n");
+                printMsg(currWin, "Quitting per user request.\n");
                 exitProg = TRUE;
                 break;
             
@@ -1524,26 +1634,26 @@
             
             case 0x0c: /* Ctrl + L */
                 updateWindows();
+                update = TRUE;
+                updateMain = TRUE;
                 break;
 
             case KEY_NPAGE:
             case KEY_PPAGE:
-#if 0
+                if (currWin != NULL)
                 {
-                int nlines, ncol, old;
-                getmaxyx(mainWin, nlines, ncol);
-                nlines /= 2;
-                old = backBufPos;
+                int numLines, numCols, oldPos = currWin->pos;
+                getmaxyx(mainWin, numLines, numCols);
+                numLines = (numLines / 2) + 1;
 
-                if (c == KEY_NPAGE)
-                    backBufPos = (backBufPos > nlines) ? backBufPos - nlines : 0;
+                if (c == KEY_PPAGE)
+                    currWin->pos = (currWin->pos > numLines) ? currWin->pos - numLines : 0;
                 else
-                    backBufPos = (backBufPos < );
+                    currWin->pos = (currWin->pos < currWin->data->n - numLines) ? currWin->pos + numLines : currWin->data->n - numLines;
 
-                if (old != backBufPos)
-                    updateMain();
+                if (oldPos != currWin->pos)
+                    updateMain = TRUE;
                 }
-#endif
                 break;
 
             case ERR:
@@ -1559,7 +1669,7 @@
                     nn_editbuf_setpos(editBuf, editBuf->pos + 1);
                     update = TRUE; 
                 } else {
-                    printMsg("Unhandled key: 0x%02x\n", c);
+                    printMsg(currWin, "Unhandled key: 0x%02x\n", c);
                 }
                 break;
             }
@@ -1571,6 +1681,8 @@
                 updateStatus(insertMode);
                 firstUpdate = FALSE; /* a nasty hack ... */
             }
+            
+            updateMainWin(updateMain);
         } /* cursesInit */
         
         if (++updateCount > 10) {
@@ -1584,9 +1696,6 @@
             
             if (!colorSet) {
                 colorSet = TRUE;
-                printMsg("%s v%s - %s\n", th_prog_name, th_prog_version, th_prog_fullname);
-                printMsg("%s\n", th_prog_author);
-                printMsg("%s\n", th_prog_license);
                 nn_conn_send_msg(conn, optUserNameEnc, "%%2FSetFontColor%%20%%2Dcolor%%20%06X", optUserColor);
             }
             
@@ -1599,19 +1708,25 @@
     /* Shutdown */
 err_exit:
     nn_userhash_free(nnUsers);
-    nn_ringbuf_free(backBuf);
     nn_editbuf_free(editBuf);
-    for (histPos = 0; histPos <= SET_MAX_HISTORY; histPos++)
-        nn_editbuf_free(histBuf[histPos]);
-    
-#ifdef __WIN32
-    if (errorMessages || isError) {
+
+    {
+    int i;
+    for (i = 0; i <= SET_MAX_HISTORY; i++)
+        nn_editbuf_free(histBuf[i]);
+
+    for (i = 0; i < SET_MAX_WINDOWS; i++)
+        nn_window_free(chatWindows[i]);
+    }
+
+//#ifdef __WIN32
+    if (errorMessages) {
         char *tmp;
         wclear(editWin);
         tmp = promptRequester(editWin, "Press enter to quit.\n", FALSE);
         th_free(tmp);
     }
-#endif
+//#endif
 
     if (cursesInit) {
         if (curVis != ERR)