about summary refs log tree commit diff stats
path: root/src/ui/inputwin.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/ui/inputwin.c')
-rw-r--r--src/ui/inputwin.c982
1 files changed, 375 insertions, 607 deletions
diff --git a/src/ui/inputwin.c b/src/ui/inputwin.c
index bc042b3f..dbf1d7f0 100644
--- a/src/ui/inputwin.c
+++ b/src/ui/inputwin.c
@@ -38,6 +38,11 @@
 #include <stdlib.h>
 #include <string.h>
 #include <wchar.h>
+#include <sys/time.h>
+#include <errno.h>
+
+#include <readline/readline.h>
+#include <readline/history.h>
 
 #ifdef HAVE_NCURSESW_NCURSES_H
 #include <ncursesw/ncurses.h>
@@ -50,7 +55,6 @@
 #include "config/accounts.h"
 #include "config/preferences.h"
 #include "config/theme.h"
-#include "tools/history.h"
 #include "log.h"
 #include "muc.h"
 #include "profanity.h"
@@ -61,37 +65,45 @@
 #include "ui/windows.h"
 #include "xmpp/xmpp.h"
 
-#define _inp_win_update_virtual() pnoutrefresh(inp_win, 0, pad_start, rows-1, 0, rows-1, cols-1)
-
-#define KEY_CTRL_A 0001
-#define KEY_CTRL_B 0002
-#define KEY_CTRL_D 0004
-#define KEY_CTRL_E 0005
-#define KEY_CTRL_F 0006
-#define KEY_CTRL_N 0016
-#define KEY_CTRL_P 0020
-#define KEY_CTRL_U 0025
-#define KEY_CTRL_W 0027
-
-#define MAX_HISTORY 100
-#define INP_WIN_MAX 1000
-
 static WINDOW *inp_win;
-static History history;
-
-static char input[INP_WIN_MAX];
-static int input_len_bytes;
-
 static int pad_start = 0;
-static int rows, cols;
 
-static int _handle_edit(int key_type, const wint_t ch);
-static int _handle_alt_key(int key);
-static void _handle_backspace(void);
-static int _printable(const wint_t ch);
-static void _clear_input(void);
-static void _go_to_end(void);
-static void _delete_previous_word(void);
+static struct timeval p_rl_timeout;
+static gint inp_timeout = 0;
+static gint no_input_count = 0;
+
+static fd_set fds;
+static int r;
+static char *inp_line = NULL;
+static gboolean get_password = FALSE;
+
+static void _inp_win_update_virtual(void);
+static int _inp_printable(const wint_t ch);
+static void _inp_win_handle_scroll(void);
+static int _inp_offset_to_col(char *str, int offset);
+static void _inp_write(char *line, int offset);
+
+static int _inp_rl_getc(FILE *stream);
+static void _inp_rl_linehandler(char *line);
+static int _inp_rl_tab_handler(int count, int key);
+static int _inp_rl_clear_handler(int count, int key);
+static int _inp_rl_win1_handler(int count, int key);
+static int _inp_rl_win2_handler(int count, int key);
+static int _inp_rl_win3_handler(int count, int key);
+static int _inp_rl_win4_handler(int count, int key);
+static int _inp_rl_win5_handler(int count, int key);
+static int _inp_rl_win6_handler(int count, int key);
+static int _inp_rl_win7_handler(int count, int key);
+static int _inp_rl_win8_handler(int count, int key);
+static int _inp_rl_win9_handler(int count, int key);
+static int _inp_rl_win0_handler(int count, int key);
+static int _inp_rl_altleft_handler(int count, int key);
+static int _inp_rl_altright_handler(int count, int key);
+static int _inp_rl_pageup_handler(int count, int key);
+static int _inp_rl_pagedown_handler(int count, int key);
+static int _inp_rl_altpageup_handler(int count, int key);
+static int _inp_rl_altpagedown_handler(int count, int key);
+static int _inp_rl_startup_hook(void);
 
 void
 create_input_window(void)
@@ -101,25 +113,84 @@ create_input_window(void)
 #else
     ESCDELAY = 25;
 #endif
-    getmaxyx(stdscr, rows, cols);
+    if (inp_timeout == 1000) {
+        p_rl_timeout.tv_sec = 1;
+        p_rl_timeout.tv_usec = 0;
+    } else {
+        p_rl_timeout.tv_sec = 0;
+        p_rl_timeout.tv_usec = inp_timeout * 1000;
+    }
+
+    rl_readline_name = "profanity";
+    rl_getc_function = _inp_rl_getc;
+    rl_startup_hook = _inp_rl_startup_hook;
+    rl_callback_handler_install(NULL, _inp_rl_linehandler);
+
     inp_win = newpad(1, INP_WIN_MAX);
     wbkgd(inp_win, theme_attrs(THEME_INPUT_TEXT));;
     keypad(inp_win, TRUE);
     wmove(inp_win, 0, 0);
+
     _inp_win_update_virtual();
-    history = history_new(MAX_HISTORY);
+}
+
+char *
+inp_readline(void)
+{
+    free(inp_line);
+    inp_line = NULL;
+    FD_ZERO(&fds);
+    FD_SET(fileno(rl_instream), &fds);
+    errno = 0;
+    r = select(FD_SETSIZE, &fds, NULL, NULL, &p_rl_timeout);
+    if (r < 0) {
+        char *err_msg = strerror(errno);
+        log_error("Readline failed: %s", err_msg);
+        return NULL;
+    }
+
+    if (FD_ISSET(fileno(rl_instream), &fds)) {
+        rl_callback_read_char();
+
+        if (rl_line_buffer &&
+                rl_line_buffer[0] != '/' &&
+                rl_line_buffer[0] != '\0' &&
+                rl_line_buffer[0] != '\n') {
+            prof_handle_activity();
+        }
+
+        ui_reset_idle_time();
+        _inp_write(rl_line_buffer, rl_point);
+        inp_nonblocking(TRUE);
+    } else {
+        inp_nonblocking(FALSE);
+        prof_handle_idle();
+    }
+
+    if (inp_timeout == 1000) {
+        p_rl_timeout.tv_sec = 1;
+        p_rl_timeout.tv_usec = 0;
+    } else {
+        p_rl_timeout.tv_sec = 0;
+        p_rl_timeout.tv_usec = inp_timeout * 1000;
+    }
+
+    if (inp_line) {
+        return strdup(inp_line);
+    } else {
+        return NULL;
+    }
 }
 
 void
 inp_win_resize(void)
 {
-    int inp_x;
-    getmaxyx(stdscr, rows, cols);
-    inp_x = getcurx(inp_win);
+    int col = getcurx(inp_win);
+    int wcols = getmaxx(stdscr);
 
     // if lost cursor off screen, move contents to show it
-    if (inp_x >= pad_start + cols) {
-        pad_start = inp_x - (cols / 2);
+    if (col >= pad_start + wcols) {
+        pad_start = col - (wcols / 2);
         if (pad_start < 0) {
             pad_start = 0;
         }
@@ -130,127 +201,60 @@ inp_win_resize(void)
 }
 
 void
-inp_non_block(gint timeout)
-{
-    wtimeout(inp_win, timeout);
-}
-
-void
-inp_block(void)
+inp_nonblocking(gboolean reset)
 {
-    wtimeout(inp_win, -1);
-}
-
-char *
-inp_read(int *key_type, wint_t *ch)
-{
-    int display_size = utf8_display_len(input);
-
-    // echo off, and get some more input
-    noecho();
-    *key_type = wget_wch(inp_win, ch);
-
-    gboolean in_command = FALSE;
-    if ((display_size > 0 && input[0] == '/') ||
-            (display_size == 0 && *ch == '/')) {
-        in_command = TRUE;
+    if (! prefs_get_boolean(PREF_INPBLOCK_DYNAMIC)) {
+        inp_timeout = prefs_get_inpblock();
+        return;
     }
 
-    if (*key_type == ERR) {
-        prof_handle_idle();
-    }
-    if ((*key_type != ERR) && (*key_type != KEY_CODE_YES) && !in_command && _printable(*ch)) {
-        prof_handle_activity();
+    if (reset) {
+        inp_timeout = 0;
+        no_input_count = 0;
     }
 
-    // if it wasn't an arrow key etc
-    if (!_handle_edit(*key_type, *ch)) {
-        if (_printable(*ch) && *key_type != KEY_CODE_YES) {
-            if (input_len_bytes >= INP_WIN_MAX) {
-                *ch = ERR;
-                return NULL;
-            }
+    if (inp_timeout < prefs_get_inpblock()) {
+        no_input_count++;
 
-            int inp_x = getcurx(inp_win);
-
-            // handle insert if not at end of input
-            if (inp_x < display_size) {
-                char bytes[MB_CUR_MAX];
-                size_t utf_len = wcrtomb(bytes, *ch, NULL);
-
-                char *next_ch = g_utf8_offset_to_pointer(input, inp_x);
-                char *offset;
-                for (offset = &input[input_len_bytes - 1]; offset >= next_ch; offset--) {
-                    *(offset + utf_len) = *offset;
-                }
-                int i;
-                for (i = 0; i < utf_len; i++) {
-                     *(next_ch + i) = bytes[i];
-                }
-
-                input_len_bytes += utf_len;
-                input[input_len_bytes] = '\0';
-                waddstr(inp_win, next_ch);
-                wmove(inp_win, 0, inp_x + 1);
-
-                if (inp_x - pad_start > cols-3) {
-                    pad_start++;
-                    _inp_win_update_virtual();
-                }
-
-            // otherwise just append
-            } else {
-                char bytes[MB_CUR_MAX+1];
-                size_t utf_len = wcrtomb(bytes, *ch, NULL);
-
-                // wcrtomb can return (size_t) -1
-                if (utf_len < MB_CUR_MAX) {
-                    int i;
-                    for (i = 0 ; i < utf_len; i++) {
-                        input[input_len_bytes++] = bytes[i];
-                    }
-                    input[input_len_bytes] = '\0';
-
-                    bytes[utf_len] = '\0';
-                    waddstr(inp_win, bytes);
-                    display_size++;
-
-                    // if gone over screen size follow input
-                    int rows, cols;
-                    getmaxyx(stdscr, rows, cols);
-                    if (display_size - pad_start > cols-2) {
-                        pad_start++;
-                        _inp_win_update_virtual();
-                    }
-                }
-            }
+        if (no_input_count % 10 == 0) {
+            inp_timeout += no_input_count;
 
-            cmd_reset_autocomplete();
+            if (inp_timeout > prefs_get_inpblock()) {
+                inp_timeout = prefs_get_inpblock();
+            }
         }
     }
-
-    echo();
-
-    if (*ch == '\n') {
-        input[input_len_bytes] = '\0';
-        input_len_bytes = 0;
-        return strdup(input);
-    } else {
-        return NULL;
-    }
 }
 
 void
-inp_get_password(char *passwd)
+inp_close(void)
+{
+    rl_callback_handler_remove();
+}
+
+char*
+inp_get_password(void)
 {
-    _clear_input();
+    werase(inp_win);
+    wmove(inp_win, 0, 0);
+    pad_start = 0;
     _inp_win_update_virtual();
     doupdate();
-    noecho();
-    mvwgetnstr(inp_win, 0, 1, passwd, MAX_PASSWORD_SIZE);
-    wmove(inp_win, 0, 0);
-    echo();
+    char *password = NULL;
+    get_password = TRUE;
+    while (!password) {
+        password = inp_readline();
+        ui_update();
+        werase(inp_win);
+        wmove(inp_win, 0, 0);
+        pad_start = 0;
+        _inp_win_update_virtual();
+        doupdate();
+    }
+    get_password = FALSE;
+
     status_bar_clear();
+    return password;
 }
 
 void
@@ -260,528 +264,292 @@ inp_put_back(void)
 }
 
 void
-inp_replace_input(const char * const new_input)
-{
-    strncpy(input, new_input, INP_WIN_MAX);
-    input_len_bytes = strlen(input);
-    inp_win_reset();
-    input[input_len_bytes] = '\0';
-    waddstr(inp_win, input);
-    _go_to_end();
-}
-
-void
-inp_win_reset(void)
+inp_win_clear(void)
 {
-    _clear_input();
+    werase(inp_win);
+    wmove(inp_win, 0, 0);
     pad_start = 0;
     _inp_win_update_virtual();
 }
 
-void
-inp_history_append(char *inp)
+static void
+_inp_win_update_virtual(void)
 {
-    history_append(history, inp);
+    int wrows, wcols;
+    getmaxyx(stdscr, wrows, wcols);
+    pnoutrefresh(inp_win, 0, pad_start, wrows-1, 0, wrows-1, wcols-2);
 }
 
 static void
-_clear_input(void)
+_inp_write(char *line, int offset)
 {
+    int col = _inp_offset_to_col(line, offset);
     werase(inp_win);
-    wmove(inp_win, 0, 0);
+    waddstr(inp_win, line);
+    wmove(inp_win, 0, col);
+    _inp_win_handle_scroll();
+
+    _inp_win_update_virtual();
 }
 
-/*
- * Deal with command editing, return 1 if ch was an edit
- * key press: up, down, left, right or backspace
- * return 0 if it wasn't
- */
 static int
-_handle_edit(int key_type, const wint_t ch)
-{
-    char *prev = NULL;
-    char *next = NULL;
-    int inp_x = getcurx(inp_win);
-    int next_ch;
-    int display_size = utf8_display_len(input);
-
-    // CTRL-LEFT
-    if ((key_type == KEY_CODE_YES) && (ch == 547 || ch == 545 || ch == 544 || ch == 540 || ch == 539) && (inp_x > 0)) {
-        input[input_len_bytes] = '\0';
-        gchar *curr_ch = g_utf8_offset_to_pointer(input, inp_x);
-        curr_ch = g_utf8_find_prev_char(input, curr_ch);
-        gchar *prev_ch;
-        gunichar curr_uni;
-        gunichar prev_uni;
-
-        while (curr_ch != NULL) {
-            curr_uni = g_utf8_get_char(curr_ch);
-
-            if (g_unichar_isspace(curr_uni)) {
-                curr_ch = g_utf8_find_prev_char(input, curr_ch);
-            } else {
-                prev_ch = g_utf8_find_prev_char(input, curr_ch);
-                if (prev_ch == NULL) {
-                    curr_ch = NULL;
-                    break;
-                } else {
-                    prev_uni = g_utf8_get_char(prev_ch);
-                    if (g_unichar_isspace(prev_uni)) {
-                        break;
-                    } else {
-                        curr_ch = prev_ch;
-                    }
-                }
-            }
-        }
+_inp_printable(const wint_t ch)
+{
+    char bytes[MB_CUR_MAX+1];
+    size_t utf_len = wcrtomb(bytes, ch, NULL);
+    bytes[utf_len] = '\0';
+    gunichar unichar = g_utf8_get_char(bytes);
 
-        if (curr_ch == NULL) {
-            inp_x = 0;
-            wmove(inp_win, 0, inp_x);
-        } else {
-            glong offset = g_utf8_pointer_to_offset(input, curr_ch);
-            inp_x = offset;
-            wmove(inp_win, 0, inp_x);
+    return g_unichar_isprint(unichar) && (ch != KEY_MOUSE);
+}
+
+static int
+_inp_offset_to_col(char *str, int offset)
+{
+    int i = 0;
+    int col = 0;
+
+    while (i < offset && str[i] != '\0') {
+        gunichar uni = g_utf8_get_char(&str[i]);
+        size_t ch_len = mbrlen(&str[i], 4, NULL);
+        i += ch_len;
+        col++;
+        if (g_unichar_iswide(uni)) {
+            col++;
         }
+    }
 
-        // if gone off screen to left, jump left (half a screen worth)
-        if (inp_x <= pad_start) {
-            pad_start = pad_start - (cols / 2);
-            if (pad_start < 0) {
-                pad_start = 0;
-            }
+    return col;
+}
 
-            _inp_win_update_virtual();
-        }
-        return 1;
-
-    // CTRL-RIGHT
-    } else if ((key_type == KEY_CODE_YES) && (ch == 562 || ch == 560 || ch == 555 || ch == 559 || ch == 554) && (inp_x < display_size)) {
-        input[input_len_bytes] = '\0';
-        gchar *curr_ch = g_utf8_offset_to_pointer(input, inp_x);
-        gchar *next_ch = g_utf8_find_next_char(curr_ch, NULL);
-        gunichar curr_uni;
-        gunichar next_uni;
-        gboolean moved = FALSE;
-
-        while (g_utf8_pointer_to_offset(input, next_ch) < display_size) {
-            curr_uni = g_utf8_get_char(curr_ch);
-            next_uni = g_utf8_get_char(next_ch);
-            curr_ch = next_ch;
-            next_ch = g_utf8_find_next_char(next_ch, NULL);
-
-            if (!g_unichar_isspace(curr_uni) && g_unichar_isspace(next_uni) && moved) {
-                break;
-            } else {
-                moved = TRUE;
-            }
-        }
+static void
+_inp_win_handle_scroll(void)
+{
+    int col = getcurx(inp_win);
+    int wcols = getmaxx(stdscr);
 
-        if (next_ch == NULL) {
-            inp_x = display_size;
-            wmove(inp_win, 0, inp_x);
-        } else {
-            glong offset = g_utf8_pointer_to_offset(input, curr_ch);
-            if (offset == display_size - 1) {
-                inp_x = offset + 1;
-            } else {
-                inp_x = offset;
-            }
-            wmove(inp_win, 0, inp_x);
+    if (col == 0) {
+        pad_start = 0;
+    } else if (col >= pad_start + (wcols -2)) {
+        pad_start = col - (wcols / 2);
+        if (pad_start < 0) {
+            pad_start = 0;
         }
-
-        // if gone off screen to right, jump right (half a screen worth)
-        if (inp_x > pad_start + cols) {
-            pad_start = pad_start + (cols / 2);
-            _inp_win_update_virtual();
+    } else if (col <= pad_start) {
+        pad_start = pad_start - (wcols / 2);
+        if (pad_start < 0) {
+            pad_start = 0;
         }
+    }
+}
 
-        return 1;
-
-    // ALT-LEFT
-    } else if ((key_type == KEY_CODE_YES) && (ch == 537 || ch == 542)) {
-        ui_previous_win();
-        return 1;
-
-    // ALT-RIGHT
-    } else if ((key_type == KEY_CODE_YES) && (ch == 552 || ch == 557)) {
-        ui_next_win();
-        return 1;
-
-    // other editing keys
-    } else {
-        switch(ch) {
-
-        case 27: // ESC
-            // check for ALT-key
-            next_ch = wgetch(inp_win);
-            if (next_ch != ERR) {
-                return _handle_alt_key(next_ch);
-            } else {
-                input_len_bytes = 0;
-                inp_win_reset();
-                return 1;
-            }
-
-        case 127:
-            _handle_backspace();
-            return 1;
-        case KEY_BACKSPACE:
-            if (key_type != KEY_CODE_YES) {
-                return 0;
-            }
-            _handle_backspace();
-            return 1;
-
-        case KEY_DC: // DEL
-            if (key_type != KEY_CODE_YES) {
-                return 0;
-            }
-        case KEY_CTRL_D:
-            if (inp_x == display_size-1) {
-                gchar *start = g_utf8_substring(input, 0, inp_x);
-                for (input_len_bytes = 0; input_len_bytes < strlen(start); input_len_bytes++) {
-                    input[input_len_bytes] = start[input_len_bytes];
-                }
-                input[input_len_bytes] = '\0';
-
-                g_free(start);
-
-                _clear_input();
-                waddstr(inp_win, input);
-            } else if (inp_x < display_size-1) {
-                gchar *start = g_utf8_substring(input, 0, inp_x);
-                gchar *end = g_utf8_substring(input, inp_x+1, input_len_bytes);
-                GString *new = g_string_new(start);
-                g_string_append(new, end);
-
-                for (input_len_bytes = 0; input_len_bytes < strlen(new->str); input_len_bytes++) {
-                    input[input_len_bytes] = new->str[input_len_bytes];
-                }
-                input[input_len_bytes] = '\0';
-
-                g_free(start);
-                g_free(end);
-                g_string_free(new, FALSE);
-
-                _clear_input();
-                waddstr(inp_win, input);
-                wmove(inp_win, 0, inp_x);
-            }
-            return 1;
-
-        case KEY_LEFT:
-            if (key_type != KEY_CODE_YES) {
-                return 0;
-            }
-        case KEY_CTRL_B:
-            if (inp_x > 0) {
-                wmove(inp_win, 0, inp_x-1);
-
-                // current position off screen to left
-                if (inp_x - 1 < pad_start) {
-                    pad_start--;
-                    _inp_win_update_virtual();
-                }
-            }
-            return 1;
-
-        case KEY_RIGHT:
-            if (key_type != KEY_CODE_YES) {
-                return 0;
-            }
-        case KEY_CTRL_F:
-            if (inp_x < display_size) {
-                wmove(inp_win, 0, inp_x+1);
-
-                // current position off screen to right
-                if ((inp_x + 1 - pad_start) >= cols) {
-                    pad_start++;
-                    _inp_win_update_virtual();
-                }
-            }
-            return 1;
-
-        case KEY_UP:
-            if (key_type != KEY_CODE_YES) {
-                return 0;
-            }
-        case KEY_CTRL_P:
-            input[input_len_bytes] = '\0';
-            prev = history_previous(history, input);
-            if (prev) {
-                inp_replace_input(prev);
-            }
-            return 1;
-
-        case KEY_DOWN:
-            if (key_type != KEY_CODE_YES) {
-                return 0;
-            }
-        case KEY_CTRL_N:
-            input[input_len_bytes] = '\0';
-            next = history_next(history, input);
-            if (next) {
-                inp_replace_input(next);
-            } else if (input_len_bytes != 0) {
-                input[input_len_bytes] = '\0';
-                history_append(history, input);
-                inp_replace_input("");
-            }
-            return 1;
-
-        case KEY_HOME:
-            if (key_type != KEY_CODE_YES) {
-                return 0;
-            }
-        case KEY_CTRL_A:
-            wmove(inp_win, 0, 0);
-            pad_start = 0;
-            _inp_win_update_virtual();
-            return 1;
+// Readline callbacks
 
-        case KEY_END:
-            if (key_type != KEY_CODE_YES) {
-                return 0;
-            }
-        case KEY_CTRL_E:
-            _go_to_end();
-            return 1;
-
-        case 9: // tab
-            if (input_len_bytes != 0) {
-                input[input_len_bytes] = '\0';
-                if ((strncmp(input, "/", 1) != 0) && (ui_current_win_type() == WIN_MUC)) {
-                    char *result = muc_autocomplete(input);
-                    if (result) {
-                        inp_replace_input(result);
-                        free(result);
-                    }
-                } else if (strncmp(input, "/", 1) == 0) {
-                    char *result = cmd_autocomplete(input);
-                    if (result) {
-                        inp_replace_input(result);
-                        free(result);
-                    }
-                }
-            }
-            return 1;
+static int
+_inp_rl_startup_hook(void)
+{
+    rl_bind_keyseq("\\e1", _inp_rl_win1_handler);
+    rl_bind_keyseq("\\e2", _inp_rl_win2_handler);
+    rl_bind_keyseq("\\e3", _inp_rl_win3_handler);
+    rl_bind_keyseq("\\e4", _inp_rl_win4_handler);
+    rl_bind_keyseq("\\e5", _inp_rl_win5_handler);
+    rl_bind_keyseq("\\e6", _inp_rl_win6_handler);
+    rl_bind_keyseq("\\e7", _inp_rl_win7_handler);
+    rl_bind_keyseq("\\e8", _inp_rl_win8_handler);
+    rl_bind_keyseq("\\e9", _inp_rl_win9_handler);
+    rl_bind_keyseq("\\e0", _inp_rl_win0_handler);
+
+    rl_bind_keyseq("\\eOP", _inp_rl_win1_handler);
+    rl_bind_keyseq("\\eOQ", _inp_rl_win2_handler);
+    rl_bind_keyseq("\\eOR", _inp_rl_win3_handler);
+    rl_bind_keyseq("\\eOS", _inp_rl_win4_handler);
+    rl_bind_keyseq("\\e[15~", _inp_rl_win5_handler);
+    rl_bind_keyseq("\\e[17~", _inp_rl_win6_handler);
+    rl_bind_keyseq("\\e[18~", _inp_rl_win7_handler);
+    rl_bind_keyseq("\\e[19~", _inp_rl_win8_handler);
+    rl_bind_keyseq("\\e[20~", _inp_rl_win9_handler);
+    rl_bind_keyseq("\\e[21~", _inp_rl_win0_handler);
+
+#ifdef PLATFORM_OSX
+    rl_bind_keyseq("\\e[1;9D", _inp_rl_altleft_handler);
+    rl_bind_keyseq("\\e[1;9C", _inp_rl_altright_handler);
+    rl_bind_keyseq("\\e\\e[5~", _inp_rl_altpageup_handler);
+    rl_bind_keyseq("\\e\\e[6~", _inp_rl_altpagedown_handler);
+#else
+    rl_bind_keyseq("\\e[1;3D", _inp_rl_altleft_handler);
+    rl_bind_keyseq("\\e[1;3C", _inp_rl_altright_handler);
+    rl_bind_keyseq("\\e[5;3~", _inp_rl_altpageup_handler);
+    rl_bind_keyseq("\\e[6;3~", _inp_rl_altpagedown_handler);
+#endif
+    rl_bind_keyseq("\\e[5~", _inp_rl_pageup_handler);
+    rl_bind_keyseq("\\e[6~", _inp_rl_pagedown_handler);
 
-        case KEY_CTRL_W:
-            _delete_previous_word();
-            return 1;
-            break;
+    rl_bind_key('\t', _inp_rl_tab_handler);
+    rl_bind_key(CTRL('L'), _inp_rl_clear_handler);
 
-        case KEY_CTRL_U:
-            while (getcurx(inp_win) > 0) {
-                _delete_previous_word();
-            }
-            return 1;
-            break;
+    return 0;
+}
 
-        default:
-            return 0;
+static void
+_inp_rl_linehandler(char *line)
+{
+    if (line && *line) {
+        if (!get_password) {
+            add_history(line);
         }
     }
+    inp_line = line;
 }
 
-static void
-_handle_backspace(void)
-{
-    int inp_x = getcurx(inp_win);
-    int display_size = utf8_display_len(input);
-    roster_reset_search_attempts();
-    if (display_size > 0) {
-
-        // if at end, delete last char
-        if (inp_x >= display_size) {
-            gchar *start = g_utf8_substring(input, 0, inp_x-1);
-            for (input_len_bytes = 0; input_len_bytes < strlen(start); input_len_bytes++) {
-                input[input_len_bytes] = start[input_len_bytes];
-            }
-            input[input_len_bytes] = '\0';
-
-            g_free(start);
+static int
+_inp_rl_getc(FILE *stream)
+{
+    int ch = rl_getc(stream);
+    if (_inp_printable(ch)) {
+        cmd_reset_autocomplete();
+    }
+    return ch;
+}
 
-            _clear_input();
-            waddstr(inp_win, input);
-            wmove(inp_win, 0, inp_x -1);
+static int
+_inp_rl_clear_handler(int count, int key)
+{
+    ui_clear_current();
+    return 0;
+}
 
-        // if in middle, delete and shift chars left
-        } else if (inp_x > 0 && inp_x < display_size) {
-            gchar *start = g_utf8_substring(input, 0, inp_x - 1);
-            gchar *end = g_utf8_substring(input, inp_x, input_len_bytes);
-            GString *new_str = g_string_new(start);
-            g_string_append(new_str, end);
+static int
+_inp_rl_tab_handler(int count, int key)
+{
+    if (rl_point != rl_end || !rl_line_buffer) {
+        return 0;
+    }
 
-            for (input_len_bytes = 0; input_len_bytes < strlen(new_str->str); input_len_bytes++) {
-                input[input_len_bytes] = new_str->str[input_len_bytes];
-            }
-            input[input_len_bytes] = '\0';
+    if ((strncmp(rl_line_buffer, "/", 1) != 0) && (ui_current_win_type() == WIN_MUC)) {
+        char *result = muc_autocomplete(rl_line_buffer);
+        if (result) {
+            rl_replace_line(result, 0);
+            rl_point = rl_end;
+        }
+    } else if (strncmp(rl_line_buffer, "/", 1) == 0) {
+        char *result = cmd_autocomplete(rl_line_buffer);
+        if (result) {
+            rl_replace_line(result, 0);
+            rl_point = rl_end;
+        }
+    }
 
-            g_free(start);
-            g_free(end);
-            g_string_free(new_str, TRUE);
+    return 0;
+}
 
-            _clear_input();
-            waddstr(inp_win, input);
-            wmove(inp_win, 0, inp_x -1);
-        }
+static int
+_inp_rl_win1_handler(int count, int key)
+{
+    ui_switch_win(1);
+    return 0;
+}
 
-        // if gone off screen to left, jump left (half a screen worth)
-        if (inp_x <= pad_start) {
-            pad_start = pad_start - (cols / 2);
-            if (pad_start < 0) {
-                pad_start = 0;
-            }
+static int
+_inp_rl_win2_handler(int count, int key)
+{
+    ui_switch_win(2);
+    return 0;
+}
 
-            _inp_win_update_virtual();
-        }
-    }
+static int
+_inp_rl_win3_handler(int count, int key)
+{
+    ui_switch_win(3);
+    return 0;
+}
 
+static int
+_inp_rl_win4_handler(int count, int key)
+{
+    ui_switch_win(4);
+    return 0;
 }
 
 static int
-_handle_alt_key(int key)
-{
-    switch (key)
-    {
-        case '1':
-            ui_switch_win(1);
-            break;
-        case '2':
-            ui_switch_win(2);
-            break;
-        case '3':
-            ui_switch_win(3);
-            break;
-        case '4':
-            ui_switch_win(4);
-            break;
-        case '5':
-            ui_switch_win(5);
-            break;
-        case '6':
-            ui_switch_win(6);
-            break;
-        case '7':
-            ui_switch_win(7);
-            break;
-        case '8':
-            ui_switch_win(8);
-            break;
-        case '9':
-            ui_switch_win(9);
-            break;
-        case '0':
-            ui_switch_win(0);
-            break;
-        case KEY_LEFT:
-            ui_previous_win();
-            break;
-        case KEY_RIGHT:
-            ui_next_win();
-            break;
-        case 263:
-        case 127:
-            _delete_previous_word();
-            break;
-        default:
-            break;
-    }
-    return 1;
+_inp_rl_win5_handler(int count, int key)
+{
+    ui_switch_win(5);
+    return 0;
 }
 
-static void
-_delete_previous_word(void)
-{
-    int end_del = getcurx(inp_win);
-    int start_del = end_del;
-
-    input[input_len_bytes] = '\0';
-    gchar *curr_ch = g_utf8_offset_to_pointer(input, end_del);
-    curr_ch = g_utf8_find_prev_char(input, curr_ch);
-    gchar *prev_ch;
-    gunichar curr_uni;
-    gunichar prev_uni;
-
-    while (curr_ch != NULL) {
-        curr_uni = g_utf8_get_char(curr_ch);
-
-        if (g_unichar_isspace(curr_uni)) {
-            curr_ch = g_utf8_find_prev_char(input, curr_ch);
-        } else {
-            prev_ch = g_utf8_find_prev_char(input, curr_ch);
-            if (prev_ch == NULL) {
-                curr_ch = NULL;
-                break;
-            } else {
-                prev_uni = g_utf8_get_char(prev_ch);
-                if (g_unichar_isspace(prev_uni)) {
-                    break;
-                } else {
-                    curr_ch = prev_ch;
-                }
-            }
-        }
-    }
+static int
+_inp_rl_win6_handler(int count, int key)
+{
+    ui_switch_win(6);
+    return 0;
+}
 
-    if (curr_ch == NULL) {
-        start_del = 0;
-    } else {
-        start_del = g_utf8_pointer_to_offset(input, curr_ch);
-    }
+static int
+_inp_rl_win7_handler(int count, int key)
+{
+    ui_switch_win(7);
+    return 0;
+}
 
-    gint len = g_utf8_strlen(input, -1);
-    gchar *start_string = g_utf8_substring(input, 0, start_del);
-    gchar *end_string = g_utf8_substring(input, end_del, len);
+static int
+_inp_rl_win8_handler(int count, int key)
+{
+    ui_switch_win(8);
+    return 0;
+}
 
-    int i;
-    for (i = 0; i < strlen(start_string); i++) {
-        input[i] = start_string[i];
-    }
-    for (i = 0; i < strlen(end_string); i++) {
-        input[strlen(start_string)+i] = end_string[i];
-    }
+static int
+_inp_rl_win9_handler(int count, int key)
+{
+    ui_switch_win(9);
+    return 0;
+}
 
-    input_len_bytes = strlen(start_string)+i;
-    input[input_len_bytes] = '\0';
+static int
+_inp_rl_win0_handler(int count, int key)
+{
+    ui_switch_win(0);
+    return 0;
+}
 
-    g_free(start_string);
-    g_free(end_string);
+static int
+_inp_rl_altleft_handler(int count, int key)
+{
+    ui_previous_win();
+    return 0;
+}
 
-    _clear_input();
-    waddstr(inp_win, input);
-    wmove(inp_win, 0, start_del);
+static int
+_inp_rl_altright_handler(int count, int key)
+{
+    ui_next_win();
+    return 0;
+}
 
-    // if gone off screen to left, jump left (half a screen worth)
-    if (start_del <= pad_start) {
-        pad_start = pad_start - (cols / 2);
-        if (pad_start < 0) {
-            pad_start = 0;
-        }
+static int
+_inp_rl_pageup_handler(int count, int key)
+{
+    ui_page_up();
+    return 0;
+}
 
-        _inp_win_update_virtual();
-    }
+static int
+_inp_rl_pagedown_handler(int count, int key)
+{
+    ui_page_down();
+    return 0;
 }
 
-static void
-_go_to_end(void)
+static int
+_inp_rl_altpageup_handler(int count, int key)
 {
-    int display_size = utf8_display_len(input);
-    wmove(inp_win, 0, display_size);
-    if (display_size > cols-2) {
-        pad_start = display_size - cols + 1;
-        _inp_win_update_virtual();
-    }
+    ui_subwin_page_up();
+    return 0;
 }
 
 static int
-_printable(const wint_t ch)
+_inp_rl_altpagedown_handler(int count, int key)
 {
-    char bytes[MB_CUR_MAX+1];
-    size_t utf_len = wcrtomb(bytes, ch, NULL);
-    bytes[utf_len] = '\0';
-    gunichar unichar = g_utf8_get_char(bytes);
-    return g_unichar_isprint(unichar) && (ch != KEY_MOUSE);
+    ui_subwin_page_down();
+    return 0;
 }