diff main.c @ 513:ef5a2aa8382b

Refactor input handling.
author Matti Hamalainen <ccr@tnsp.org>
date Tue, 05 Jun 2012 22:16:42 +0300
parents 93c8ba1ef55f
children 151edcb79ce4
line wrap: on
line diff
--- a/main.c	Tue Jun 05 19:56:57 2012 +0300
+++ b/main.c	Tue Jun 05 22:16:42 2012 +0300
@@ -19,11 +19,9 @@
 #ifdef __WIN32
 #define SET_CONFIG_FILE    "nnchat.txt"
 #define SET_DIR_SEPARATOR  "\\"
-#define SET_DELAY          (0)
 #else
 #define SET_CONFIG_FILE    ".nnchat"
 #define SET_DIR_SEPARATOR  "/"
-#define SET_DELAY          (5)
 #endif
 
 #define SET_PROFILE_PREFIX "http://www.newbienudes.com/profile/%s/"
@@ -63,6 +61,9 @@
         *setBrowser = NULL;
 cfgitem_t *cfg = NULL;
 
+nn_editbuf_t *editHistBuf[SET_MAX_HISTORY+2];
+int      editHistPos = 0,
+         editHistMax = 0;
 
 /* Logging mode flags
  */
@@ -313,6 +314,19 @@
 }
 
 
+void debugMsg(const char *fmt, ...)
+{
+    if (optDebug)
+    {
+        va_list ap;
+
+        va_start(ap, fmt);
+        printMsgV(NULL, LOG_FILE|LOG_WINDOW, fmt, ap);
+        va_end(ap);
+    }
+}
+
+
 void errorFunc(struct _nn_conn_t *conn, const char *fmt, va_list ap)
 {
     (void) conn;
@@ -1292,24 +1306,161 @@
 }
 
 
+BOOL processUserInput(int c, nn_editbuf_t *editBuf, nn_editstate_t *editState)
+{
+    // Chat window switching via Meta/Esc-[1..9]
+    if (c >= 0x5001 && c <= 0x5009)
+    {
+        nn_window_t *win = nnwin_get(c - 0x5000);
+        if (win != NULL)
+        {
+            currWin = win;
+            editState->update = TRUE;
+        }
+    }
+    else
+    switch (c)
+    {
+    case KEY_ENTER:
+        // Call the user input handler
+        if (editBuf->len > 0)
+        {
+            int result;
+
+            if (editHistMax > 0)
+            {
+                nn_editbuf_free(editHistBuf[SET_MAX_HISTORY+1]);
+                editHistBuf[SET_MAX_HISTORY+1] = NULL;
+                memmove(&editHistBuf[2], &editHistBuf[1],
+                    editHistMax * sizeof(editHistBuf[0]));
+            }
+
+            editHistPos = 0;
+            editHistBuf[1] = nn_editbuf_copy(editBuf);
+            if (editHistMax < SET_MAX_HISTORY)
+                editHistMax++;
+
+            result = nn_handle_input(editState->conn, editBuf->data, editBuf->len);
+
+            nn_editbuf_clear(editBuf);
+
+            if (result < 0)
+            {
+                errorMsg("Fatal error handling user input: %s\n", editBuf->data);
+                editState->isError = TRUE;
+            }
+            else
+            {
+                // Update time value of last sent message for unidle timeouts
+                editState->prevKeepAlive = time(NULL);
+            }
+        }
+        break;
+
+    case KEY_NPAGE:
+    case KEY_PPAGE:
+        // Page Up / Page Down
+        if (currWin != NULL)
+        {
+            int oldPos = currWin->pos, page = (scrHeight - 4) / 3;
+
+            currWin->pos += (c == KEY_NPAGE) ? - page : page;
+
+            if (currWin->pos >= currWin->data->n - page)
+                currWin->pos = currWin->data->n - page;
+            if (currWin->pos < 0)
+                currWin->pos = 0;
+
+            if (oldPos != currWin->pos)
+                editState->update = TRUE;
+        }
+        break;
+
+    case KEY_UP: // Backwards in input history
+        if (editHistPos == 0)
+        {
+            nn_editbuf_free(editHistBuf[0]);
+            editHistBuf[0] = nn_editbuf_copy(editBuf);
+        }
+        if (editHistPos < editHistMax)
+        {
+            editHistPos++;
+            nn_editbuf_free(editBuf);
+            editBuf = nn_editbuf_copy(editHistBuf[editHistPos]);
+        }
+        break;
+
+    case KEY_DOWN: // Forwards in input history
+        if (editHistPos > 0)
+        {
+            editHistPos--;
+            nn_editbuf_free(editBuf);
+            editBuf = nn_editbuf_copy(editHistBuf[editHistPos]);
+        }
+        break;
+
+    case KEY_F(5): // F5 = Ignore mode
+        setIgnoreMode = setIgnoreMode;
+        printMsgQ(currWin, "Ignore mode = %s\n", setIgnoreMode ? "ON" : "OFF");
+        break;
+
+    case 0x03: // ^C = quit
+    case KEY_F(9): // F9 = Quit
+        printMsg(currWin, "Quitting per user request (%d/0x%x).\n", c, c);
+        editState->exitProg = TRUE;
+        break;
+
+    case 0x09: // Tab = complete username or command
+        nn_tabcomplete_buffer(editBuf);
+        break;
+    
+    default:
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+
+BOOL processUserPrompt(int c, nn_editbuf_t *editBuf, nn_editstate_t *editState)
+{
+    (void) editBuf;
+    
+    switch (c)
+    {
+    case KEY_ENTER:
+        editState->done = TRUE;
+        break;
+    
+    default:
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+
+void updateUserPrompt(nn_editbuf_t *editBuf, nn_editstate_t *editState)
+{
+    nnwin_update(editState->update, editState->mask, editBuf, optUserName, optUserColor);
+}
+
+
 int main(int argc, char *argv[])
 {
+    char *tmpStr;
+    int index, updateCount = 0;
+    BOOL argsOK, colorSet = FALSE;
     nn_conn_t *conn = NULL;
-    int updateCount = 0;
-    BOOL argsOK, isError = FALSE,
-        exitProg = FALSE,
-        colorSet = FALSE,
-        insertMode = TRUE;
-    time_t prevKeepAlive;
-    char *tmpStr;
     nn_editbuf_t *editBuf = nn_editbuf_new(NN_TMPBUF_SIZE);
-    nn_editbuf_t *histBuf[SET_MAX_HISTORY+2];
-    int histPos = 0, histMax = 0, index;
-
+    nn_editstate_t editState;
     cfgitem_t *tmpcfg;
     char *setHomeDir = NULL;
 
-    memset(histBuf, 0, sizeof(histBuf));
+    memset(editHistBuf, 0, sizeof(editHistBuf));
+    memset(&editState, 0, sizeof(editState));
+    editState.insertMode = TRUE;
+    editState.debugMsg = debugMsg;
 
     // Initialize
     th_init("NNChat", "Newbie Nudes chat client", NN_VERSION,
@@ -1417,6 +1568,7 @@
 
     if (optUserNameCmd != NULL)
     {
+        THMSG(1, "Username set on commandline.\n");
         optUserName = optUserNameCmd;
         optPassword = optPasswordCmd;
     }
@@ -1453,14 +1605,26 @@
 
     if (cursesInit)
     {
-        nnwin_update(TRUE, NULL, optUserName, optUserColor);
-    }
+        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);
+
+        nnwin_update(TRUE, FALSE, NULL, optUserName, optUserColor);
 
-    // Check if we have username and password
-    if (cursesInit && (optUserName == NULL || optPassword == NULL))
-    {
-        optUserName = nnwin_prompt_requester("You can avoid this prompt by issuing '/save' after logging in.\nNN username: ", FALSE);
-        optPassword = nnwin_prompt_requester("NN password: ", TRUE);
+        // Check if we have username and password
+        if (optUserName == NULL || optPassword == NULL)
+        {
+            printMsg(NULL, "Please enter your NN login credentials.\n");
+            printMsg(NULL, "You can avoid doing this every time by issuing '/save' after logging in.\n");
+
+            printMsg(NULL, "Enter your NN username ...\n");
+            optUserName = nnwin_prompt_requester(FALSE, &editState, processUserPrompt, updateUserPrompt);
+
+            editState.mask = TRUE;
+            printMsg(NULL, "Enter your NN password ...\n");
+            optPassword = nnwin_prompt_requester(TRUE, &editState, processUserPrompt, updateUserPrompt);
+            editState.mask = FALSE;
+        }
     }
 
     if (optUserName == NULL || optPassword == NULL)
@@ -1476,6 +1640,8 @@
         errorMsg("Could not create connection structure.\n");
         goto err_exit;
     }
+    
+    editState.conn = conn;
 
     // Are we using a proxy?
     if (optProxyType != NN_PROXY_NONE && optProxyServer != NULL)
@@ -1541,25 +1707,12 @@
     nn_usercmd_init();
     
     // Initialize random numbers
-    prevKeepAlive = time(NULL);
-    srandom((int) prevKeepAlive);
-
-    if (cursesInit)
-    {
-        // Initialize rest of interactive UI code
-        nn_editbuf_clear(editBuf);
-
-        // First update of screen
-        nnwin_update(TRUE, editBuf, optUserName, optUserColor);
-
-        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);
-    }
+    editState.prevKeepAlive = time(NULL);
+    srandom((int) editState.prevKeepAlive);
 
     // Enter mainloop
     nn_conn_reset(conn);
-    while (!isError && !exitProg)
+    while (!editState.isError && !editState.exitProg)
     {
         int retries = 3, cres;
 
@@ -1569,7 +1722,7 @@
         {
             while (conn->ptr < conn->in_ptr &&
                    *(conn->in_ptr - 1) == 0 &&
-                   retries > 0 && !isError)
+                   retries > 0 && !editState.isError)
             {
 //                nn_conn_dump_buffer(stderr, conn);
                 int result = nn_parse_protocol(conn);
@@ -1589,326 +1742,29 @@
                     nn_conn_buf_skip(conn, strlen(conn->ptr) + 1);
                 }
                 else
-                    isError = TRUE;
+                    editState.isError = TRUE;
             }
         }
         else
         if (cres < 0 || !nn_conn_check(conn))
-            isError = TRUE;
+            editState.isError = TRUE;
 
         // Handle user input
         if (cursesInit)
         {
-            int c, cnt = 0;
-            BOOL update = FALSE;
-
-            // Handle several buffered keypresses at once
-            do
-            {
-                c = wgetch(stdscr);
-                
-                /* Handle various problematic cases where terminal 
-                 * keycodes do not get properly translated by curses
-                 */
-                if (c == 0x1b)
-                {
-                    // ^[O
-                    c = wgetch(stdscr);
-                    if (c == 'O')
-                    {
-                        c = wgetch(stdscr);
-                        switch (c)
-                        {
-                        case 'd':
-                            c = 0x204;
-                            break;
-                        case 'c':
-                            c = 0x206;
-                            break;
-                        default:
-                            if (optDebug)
-                                printMsg(currWin, "Unhandled ESC-O key sequence 0x%02x\n", c);
-                            break;
-                        }
-                    }
-                    // ^[[
-                    else if (c == '[')
-                    {
-                        c = wgetch(stdscr);
-                        switch (c)
-                        {
-                        case 0x31:
-                            c = wgetch(stdscr);
-                            if (c >= 0x31 && c <= 0x39)
-                                c = KEY_F(c - 0x30);
-                            else
-                                c = ERR;
-                            break;
-
-                        case 0x32:
-                            c = KEY_IC;
-                            break;
-                        case 0x33:
-                            c = KEY_DC;
-                            break;
-
-                        case 0x35:
-                            c = KEY_PPAGE;
-                            break;
-                        case 0x36:
-                            c = KEY_NPAGE;
-                            break;
-
-                        case 0x37:
-                            c = KEY_HOME;
-                            break;
-                        case 0x38:
-                            c = KEY_END;
-                            break;
-
-                        default:
-                            if (optDebug)
-                                printMsg(currWin, "Unhandled ESC-[*~ key sequence 0x%02x\n", c);
-                            c = ERR;
-                            break;
-                        }
-                        // Get the trailing ~
-                        if (c != ERR)
-                            wgetch(stdscr);
-                    }
-                    if (c >= 0x31 && c <= 0x39)
-                    {
-                        // Chat window switching via Meta/Esc-[1..9]
-                        nn_window_t *win = nnwin_get(c - 0x30);
-                        if (win != NULL)
-                        {
-                            currWin = win;
-                            update = TRUE;
-                        }
-                        c = ERR;
-                    }
-                    else
-                    {
-                        if (optDebug)
-                            printMsg(currWin, "Unhandled ESC key sequence 0x%02x\n", c);
-                    }
-                }
-#if defined(__WIN32) && defined(PDCURSES)
-                else if (c >= 0x198 && c <= 0x1a0)
-                {
-                    // Chat window switching via Meta/Esc-[1..9]
-                    nn_window_t *win = nnwin_get(c - 0x198);
-                    if (win != NULL)
-                    {
-                        currWin = win;
-                        update = TRUE;
-                    }
-                    c = ERR;
-                }
-#endif
-
-                switch (c)
-                {
-#ifdef KEY_RESIZE
-                case KEY_RESIZE:
-                    resize_term(0, 0);
-                    erase();
-                    timeout(SET_DELAY);
-                    nnwin_reset();
-                    update = TRUE;
-                    break;
-#endif
-
-                case KEY_ENTER:
-                case '\n':
-                case '\r':
-                    // Call the user input handler
-                    if (editBuf->len > 0)
-                    {
-                        int result;
-
-                        if (histMax > 0)
-                        {
-                            nn_editbuf_free(histBuf[SET_MAX_HISTORY+1]);
-                            histBuf[SET_MAX_HISTORY+1] = NULL;
-                            memmove(&histBuf[2], &histBuf[1], histMax * sizeof(histBuf[0]));
-                        }
-
-                        histPos = 0;
-                        histBuf[1] = nn_editbuf_copy(editBuf);
-                        if (histMax < SET_MAX_HISTORY) histMax++;
-
-                        result = nn_handle_input(conn, editBuf->data, editBuf->len);
-
-                        nn_editbuf_clear(editBuf);
-
-                        if (result < 0)
-                        {
-                            errorMsg("Fatal error handling user input: %s\n", editBuf->data);
-                            isError = TRUE;
-                        }
-                        else
-                        {
-                            // Update time value of last sent message for unidle timeouts
-                            prevKeepAlive = time(NULL);
-                        }
-                    }
-                    break;
-
-                case KEY_UP: // Backwards in input history
-                    if (histPos == 0)
-                    {
-                        nn_editbuf_free(histBuf[0]);
-                        histBuf[0] = nn_editbuf_copy(editBuf);
-                    }
-                    if (histPos < histMax)
-                    {
-                        histPos++;
-                        nn_editbuf_free(editBuf);
-                        editBuf = nn_editbuf_copy(histBuf[histPos]);
-                    }
-                    break;
-
-                case KEY_DOWN: // Forwards in input history
-                    if (histPos > 0)
-                    {
-                        histPos--;
-                        nn_editbuf_free(editBuf);
-                        editBuf = nn_editbuf_copy(histBuf[histPos]);
-                    }
-                    break;
-
-                case 0x204: // ctrl+left arrow = Skip words left
-                case 0x20b:
-                    while (editBuf->pos > 0 && isspace((int) editBuf->data[editBuf->pos - 1]))
-                        editBuf->pos--;
-                    while (editBuf->pos > 0 && !isspace((int) editBuf->data[editBuf->pos - 1]))
-                        editBuf->pos--;
-                    editBuf->dirty = TRUE;
-                    break;
-
-                case 0x206: // ctrl+right arrow = Skip words right
-                case 0x210:
-                    while (editBuf->pos < editBuf->len && isspace((int) editBuf->data[editBuf->pos]))
-                        editBuf->pos++;
-                    while (editBuf->pos < editBuf->len && !isspace((int) editBuf->data[editBuf->pos]))
-                        editBuf->pos++;
-                    editBuf->dirty = TRUE;
-                    break;
-
-                case KEY_HOME:
-                    nn_editbuf_setpos(editBuf, 0);
-                    break;
-                case KEY_END:
-                    nn_editbuf_setpos(editBuf, editBuf->len);
-                    break;
-                case KEY_LEFT:
-                    nn_editbuf_setpos(editBuf, editBuf->pos - 1);
-                    break;
-                case KEY_RIGHT:
-                    nn_editbuf_setpos(editBuf, editBuf->pos + 1);
-                    break;
-
-                case KEY_BACKSPACE:
-                case 0x08:
-                case 0x7f:
-                    nn_editbuf_delete(editBuf, editBuf->pos - 1);
-                    nn_editbuf_setpos(editBuf, editBuf->pos - 1);
-                    break;
-
-                case KEY_DC: // Delete character
-                    nn_editbuf_delete(editBuf, editBuf->pos);
-                    break;
-
-
-                case KEY_IC: // Ins = Toggle insert / overwrite mode
-                    insertMode = !insertMode;
-                    break;
-
-                case KEY_F(2): // F2 = Clear editbuffer
-                    nn_editbuf_clear(editBuf);
-                    break;
-
-                case KEY_F(5): // F5 = Ignore mode
-                    setIgnoreMode = !setIgnoreMode;
-                    printMsgQ(currWin, "Ignore mode = %s\n", setIgnoreMode ? "ON" : "OFF");
-                    break;
-
-#if 0
-                case KEY_F(8): // F8 = Debug
-                    optDebug = !optDebug;
-                    update = TRUE;
-                    break;
-#endif
-
-                case 0x03: // ^C = quit
-                case KEY_F(9): // F9 = Quit
-                    printMsg(currWin, "Quitting per user request (%d/0x%x).\n", c, c);
-                    exitProg = TRUE;
-                    break;
-
-                case 0x09: // Tab = complete username or command
-                    nn_tabcomplete_buffer(editBuf);
-                    break;
-
-                case 0x0c: // Ctrl + L
-                    update = TRUE;
-                    break;
-
-                case KEY_NPAGE:
-                case KEY_PPAGE:
-                    // Page Up / Page Down
-                    if (currWin != NULL)
-                    {
-                        int oldPos = currWin->pos, page = scrHeight - 4;
-
-                        currWin->pos += (c == KEY_NPAGE) ? - page : page;
-
-                        if (currWin->pos >= currWin->data->n - page)
-                            currWin->pos = currWin->data->n - page;
-                        if (currWin->pos < 0)
-                            currWin->pos = 0;
-
-                        if (oldPos != currWin->pos)
-                            update = TRUE;
-                    }
-                    break;
-
-                case ERR:
-                    // Ignore
-                    break;
-
-                default:
-                    if (isprint(c) || c == 0xe4 || c == 0xf6 || c == 0xc4 || c == 0xd6)
-                    {
-                        if (insertMode)
-                            nn_editbuf_insert(editBuf, editBuf->pos, c);
-                        else
-                            nn_editbuf_write(editBuf, editBuf->pos, c);
-                        nn_editbuf_setpos(editBuf, editBuf->pos + 1);
-                    }
-                    else
-                    {
-                        if (optDebug)
-                            printMsg(currWin, "Unhandled key: 0x%02x\n", c);
-                    }
-                    break;
-                }
-            }
-            while (c != ERR && !exitProg && ++cnt < 10);
-
-            nnwin_update(update, editBuf, optUserName, optUserColor);
-        } // cursesInit
+            nnwin_input_process(editBuf, &editState, processUserInput);
+            nnwin_update(editState.update, editState.mask, editBuf, optUserName, optUserColor);
+        }
 
         if (++updateCount > 10)
         {
             time_t tmpTime = time(NULL);
-            if (tmpTime - prevKeepAlive > SET_KEEPALIVE)
+            if (tmpTime - editState.prevKeepAlive > SET_KEEPALIVE)
             {
                 size_t n = ((size_t) random()) % th_llist_length(setIdleMessages);
                 qlist_t *node = th_llist_get_nth(setIdleMessages, n);
                 nn_conn_send_msg(conn, optUserNameEnc, node->data);
-                prevKeepAlive = tmpTime;
+                editState.prevKeepAlive = tmpTime;
             }
 
             if (!colorSet)
@@ -1916,8 +1772,12 @@
                 colorSet = TRUE;
                 nn_conn_send_msg_v(conn, optUserNameEnc, "%%2FSetFontColor%%20%%2Dcolor%%20%06X", optUserColor);
             }
+            
+            if (cursesInit)
+            {
+                nnwin_update(FALSE, editState.mask, editBuf, optUserName, optUserColor);
+            }
 
-            nnwin_update(FALSE, editBuf, optUserName, optUserColor);
             updateCount = 0;
         }
 
@@ -1932,7 +1792,7 @@
     nn_editbuf_free(editBuf);
 
     for (index = 0; index <= SET_MAX_HISTORY; index++)
-        nn_editbuf_free(histBuf[index]);
+        nn_editbuf_free(editHistBuf[index]);
 
 #ifdef __WIN32
     if (errorMessages)