about summary refs log tree commit diff stats
path: root/src/ui
diff options
context:
space:
mode:
Diffstat (limited to 'src/ui')
-rw-r--r--src/ui/console.c23
-rw-r--r--src/ui/core.c161
-rw-r--r--src/ui/inputwin.c982
-rw-r--r--src/ui/inputwin.h16
-rw-r--r--src/ui/ui.h9
-rw-r--r--src/ui/window.c203
-rw-r--r--src/ui/window.h7
7 files changed, 606 insertions, 795 deletions
diff --git a/src/ui/console.c b/src/ui/console.c
index 2c37c40c..14ee7314 100644
--- a/src/ui/console.c
+++ b/src/ui/console.c
@@ -1424,27 +1424,10 @@ cons_navigation_help(void)
     cons_show("");
     cons_show("Navigation:");
     cons_show("");
-    cons_show("Alt-1                            : This console window.");
-    cons_show("F1                               : This console window.");
-    cons_show("Alt-2..Alt-0                     : Chat windows.");
-    cons_show("F2..F10                          : Chat windows.");
+    cons_show("Alt-1..Alt-0, F1..F10            : Choose window.");
     cons_show("Alt-LEFT, Alt-RIGHT              : Previous/next chat window");
-    cons_show("UP, DOWN                         : Navigate input history.");
-    cons_show("Ctrl-n, Ctrl-p                   : Navigate input history.");
-    cons_show("LEFT, RIGHT, HOME, END           : Move cursor.");
-    cons_show("Ctrl-b, Ctrl-f, Ctrl-a, Ctrl-e   : Move cursor.");
-    cons_show("Ctrl-LEFT, Ctrl-RIGHT            : Jump word.");
-    cons_show("Ctrl-w                           : Delete previous word.");
-    cons_show("Alt-Backspace                    : Delete previous word.");
-    cons_show("Backspace                        : Delete previous character.");
-    cons_show("DEL                              : Delete next character.");
-    cons_show("Ctrl-d                           : Delete next character.");
-    cons_show("ESC                              : Clear current input.");
-    cons_show("Ctrl-u                           : Delete all previous characters.");
-    cons_show("TAB                              : Autocomplete.");
-    cons_show("PAGE UP, PAGE DOWN               : Page the main window.");
-    cons_show("Shift-UP, Shift-DOWN             : Page occupants/roster panel.");
-    cons_show("Ctrl-UP, Ctrl-DOWN               : Page occupants/roster panel.");
+    cons_show("PAGEUP, PAGEDOWN                 : Page the main window.");
+    cons_show("Alt-PAGEUP, Alt-PAGEDOWN         : Page occupants/roster panel.");
     cons_show("");
 
     cons_alert();
diff --git a/src/ui/core.c b/src/ui/core.c
index 85d5748a..0fde3387 100644
--- a/src/ui/core.c
+++ b/src/ui/core.c
@@ -41,6 +41,9 @@
 #include <stdlib.h>
 #include <string.h>
 #include <assert.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+
 #ifdef HAVE_LIBXSS
 #include <X11/extensions/scrnsaver.h>
 #endif
@@ -76,13 +79,15 @@ static char *win_title;
 
 static int inp_size;
 
+static gboolean perform_resize = FALSE;
+
 #ifdef HAVE_LIBXSS
 static Display *display;
 #endif
 
 static GTimer *ui_idle_time;
 
-static void _win_handle_switch(const wint_t ch);
+//static void _win_handle_switch(const wint_t ch);
 static void _win_show_history(int win_index, const char * const contact);
 static void _ui_draw_term_title(void);
 
@@ -91,7 +96,9 @@ ui_init(void)
 {
     log_info("Initialising UI");
     initscr();
-    raw();
+    nonl();
+    cbreak();
+    noecho();
     keypad(stdscr, TRUE);
     if (prefs_get_boolean(PREF_MOUSE)) {
         mousemask(ALL_MOUSE_EVENTS, NULL);
@@ -116,6 +123,12 @@ ui_init(void)
 }
 
 void
+ui_sigwinch_handler(int sig)
+{
+    perform_resize = TRUE;
+}
+
+void
 ui_update(void)
 {
     ProfWin *current = wins_get_current();
@@ -132,6 +145,13 @@ ui_update(void)
     status_bar_update_virtual();
     inp_put_back();
     doupdate();
+
+    if (perform_resize) {
+        signal(SIGWINCH, SIG_IGN);
+        ui_resize();
+        perform_resize = FALSE;
+        signal(SIGWINCH, ui_sigwinch_handler);
+    }
 }
 
 void
@@ -174,84 +194,66 @@ ui_close(void)
 {
     notifier_uninit();
     wins_destroy();
+    inp_close();
     endwin();
 }
 
-char*
+char *
 ui_readline(void)
 {
-    int key_type;
-    wint_t ch;
-
-    char *line = inp_read(&key_type, &ch);
-    _win_handle_switch(ch);
+    return inp_readline();
+}
 
+void
+ui_page_up(void)
+{
     ProfWin *current = wins_get_current();
-    win_handle_page(current, ch, key_type);
-
-    if (ch == KEY_RESIZE) {
-        ui_resize();
-    }
+    win_page_up(current);
+}
 
-    if (ch != ERR && key_type != ERR) {
-        ui_reset_idle_time();
-        ui_input_nonblocking(TRUE);
-    } else {
-        ui_input_nonblocking(FALSE);
-    }
+void
+ui_page_down(void)
+{
+    ProfWin *current = wins_get_current();
+    win_page_down(current);
+}
 
-    return line;
+void
+ui_subwin_page_up(void)
+{
+    ProfWin *current = wins_get_current();
+    win_sub_page_up(current);
 }
 
 void
-ui_inp_history_append(char *inp)
+ui_subwin_page_down(void)
 {
-    inp_history_append(inp);
+    ProfWin *current = wins_get_current();
+    win_sub_page_down(current);
 }
 
 void
 ui_input_clear(void)
 {
-    inp_win_reset();
+    inp_win_clear();
 }
 
 void
 ui_input_nonblocking(gboolean reset)
 {
-    static gint timeout = 0;
-    static gint no_input_count = 0;
-
-    if (! prefs_get_boolean(PREF_INPBLOCK_DYNAMIC)) {
-        inp_non_block(prefs_get_inpblock());
-        return;
-    }
-
-    if (reset) {
-        timeout = 0;
-        no_input_count = 0;
-    }
-
-    if (timeout < prefs_get_inpblock()) {
-        no_input_count++;
-
-        if (no_input_count % 10 == 0) {
-            timeout += no_input_count;
-
-            if (timeout > prefs_get_inpblock()) {
-                timeout = prefs_get_inpblock();
-            }
-        }
-    }
-
-    inp_non_block(timeout);
+    inp_nonblocking(reset);
 }
 
 void
 ui_resize(void)
 {
-    log_info("Resizing UI");
+    struct winsize w;
+    ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
     erase();
+    resizeterm(w.ws_row, w.ws_col);
     refresh();
+
+    log_info("Resizing UI");
     title_bar_resize();
     wins_resize_all();
     status_bar_resize();
@@ -2247,14 +2249,9 @@ ui_win_unread(int index)
 char *
 ui_ask_password(void)
 {
-  char *passwd = malloc(sizeof(char) * (MAX_PASSWORD_SIZE + 1));
   status_bar_get_password();
   status_bar_update_virtual();
-  inp_block();
-  inp_get_password(passwd);
-  inp_non_block(prefs_get_inpblock());
-
-  return passwd;
+  return inp_get_password();
 }
 
 void
@@ -2939,32 +2936,32 @@ ui_hide_roster(void)
     }
 }
 
-static void
-_win_handle_switch(const wint_t ch)
-{
-    if (ch == KEY_F(1)) {
-        ui_switch_win(1);
-    } else if (ch == KEY_F(2)) {
-        ui_switch_win(2);
-    } else if (ch == KEY_F(3)) {
-        ui_switch_win(3);
-    } else if (ch == KEY_F(4)) {
-        ui_switch_win(4);
-    } else if (ch == KEY_F(5)) {
-        ui_switch_win(5);
-    } else if (ch == KEY_F(6)) {
-        ui_switch_win(6);
-    } else if (ch == KEY_F(7)) {
-        ui_switch_win(7);
-    } else if (ch == KEY_F(8)) {
-        ui_switch_win(8);
-    } else if (ch == KEY_F(9)) {
-        ui_switch_win(9);
-    } else if (ch == KEY_F(10)) {
-        ui_switch_win(0);
-    }
-}
-
+//static void
+//_win_handle_switch(const wint_t ch)
+//{
+//    if (ch == KEY_F(1)) {
+//        ui_switch_win(1);
+//    } else if (ch == KEY_F(2)) {
+//        ui_switch_win(2);
+//    } else if (ch == KEY_F(3)) {
+//        ui_switch_win(3);
+//    } else if (ch == KEY_F(4)) {
+//        ui_switch_win(4);
+//    } else if (ch == KEY_F(5)) {
+//        ui_switch_win(5);
+//    } else if (ch == KEY_F(6)) {
+//        ui_switch_win(6);
+//    } else if (ch == KEY_F(7)) {
+//        ui_switch_win(7);
+//    } else if (ch == KEY_F(8)) {
+//        ui_switch_win(8);
+//    } else if (ch == KEY_F(9)) {
+//        ui_switch_win(9);
+//    } else if (ch == KEY_F(10)) {
+//        ui_switch_win(0);
+//    }
+//}
+//
 static void
 _win_show_history(int win_index, const char * const contact)
 {
diff --git a/src/ui/inputwin.c b/src/ui/inputwin.c
index b2d5e420..328b92d6 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 = g_string_new(start);
-            g_string_append(new, 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); input_len_bytes++) {
-                input[input_len_bytes] = new->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, FALSE);
+    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;
 }
diff --git a/src/ui/inputwin.h b/src/ui/inputwin.h
index 39fde720..91cbb0b2 100644
--- a/src/ui/inputwin.h
+++ b/src/ui/inputwin.h
@@ -35,15 +35,17 @@
 #ifndef UI_INPUTWIN_H
 #define UI_INPUTWIN_H
 
+#include <glib.h>
+
+#define INP_WIN_MAX 1000
+
 void create_input_window(void);
-char* inp_read(int *key_type, wint_t *ch);
-void inp_win_reset(void);
+char* inp_readline(void);
+void inp_nonblocking(gboolean reset);
+void inp_close(void);
+void inp_win_clear(void);
 void inp_win_resize(void);
 void inp_put_back(void);
-void inp_non_block(gint);
-void inp_block(void);
-void inp_get_password(char *passwd);
-void inp_replace_input(const char * const new_input);
-void inp_history_append(char *inp);
+char* inp_get_password(void);
 
 #endif
diff --git a/src/ui/ui.h b/src/ui/ui.h
index 99e73b4a..1062eac1 100644
--- a/src/ui/ui.h
+++ b/src/ui/ui.h
@@ -62,6 +62,7 @@ GSList* ui_get_chat_recipients(void);
 gboolean ui_switch_win(const int i);
 void ui_next_win(void);
 void ui_previous_win(void);
+void ui_sigwinch_handler(int sig);
 
 void ui_gone_secure(const char * const barejid, gboolean trusted);
 void ui_gone_insecure(const char * const barejid);
@@ -218,6 +219,11 @@ void ui_tidy_wins(void);
 void ui_prune_wins(void);
 gboolean ui_swap_wins(int source_win, int target_win);
 
+void ui_page_up(void);
+void ui_page_down(void);
+void ui_subwin_page_up(void);
+void ui_subwin_page_down(void);
+
 void ui_auto_away(void);
 void ui_end_auto_away(void);
 void ui_titlebar_presence(contact_presence_t presence);
@@ -227,9 +233,10 @@ void ui_update_presence(const resource_presence_t resource_presence,
 void ui_about(void);
 void ui_statusbar_new(const int win);
 
-char * ui_readline(void);
+char* ui_readline(void);
 void ui_input_clear(void);
 void ui_input_nonblocking(gboolean);
+void ui_write(char *line, int offset);
 
 void ui_invalid_command_usage(const char * const usage, void (*setting_func)(void));
 
diff --git a/src/ui/window.c b/src/ui/window.c
index aba2f40a..585e0955 100644
--- a/src/ui/window.c
+++ b/src/ui/window.c
@@ -37,6 +37,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <assert.h>
+#include <wchar.h>
 
 #include <glib.h>
 #ifdef HAVE_NCURSESW_NCURSES_H
@@ -359,7 +360,101 @@ win_free(ProfWin* window)
 }
 
 void
-win_handle_page(ProfWin *window, const wint_t ch, const int result)
+win_page_up(ProfWin *window)
+{
+    int rows = getmaxy(stdscr);
+    int y = getcury(window->layout->win);
+    int page_space = rows - 4;
+    int *page_start = &(window->layout->y_pos);
+
+    *page_start -= page_space;
+
+    // went past beginning, show first page
+    if (*page_start < 0)
+        *page_start = 0;
+
+    window->layout->paged = 1;
+    win_update_virtual(window);
+
+    // switch off page if last line and space line visible
+    if ((y) - *page_start == page_space) {
+        window->layout->paged = 0;
+    }
+}
+
+void
+win_page_down(ProfWin *window)
+{
+    int rows = getmaxy(stdscr);
+    int y = getcury(window->layout->win);
+    int page_space = rows - 4;
+    int *page_start = &(window->layout->y_pos);
+
+    *page_start += page_space;
+
+    // only got half a screen, show full screen
+    if ((y - (*page_start)) < page_space)
+        *page_start = y - page_space;
+
+    // went past end, show full screen
+    else if (*page_start >= y)
+        *page_start = y - page_space - 1;
+
+    window->layout->paged = 1;
+    win_update_virtual(window);
+
+    // switch off page if last line and space line visible
+    if ((y) - *page_start == page_space) {
+        window->layout->paged = 0;
+    }
+}
+
+void
+win_sub_page_down(ProfWin *window)
+{
+
+    if (window->layout->type == LAYOUT_SPLIT) {
+        int rows = getmaxy(stdscr);
+        int page_space = rows - 4;
+        ProfLayoutSplit *split_layout = (ProfLayoutSplit*)window->layout;
+        int sub_y = getcury(split_layout->subwin);
+        int *sub_y_pos = &(split_layout->sub_y_pos);
+
+        *sub_y_pos += page_space;
+
+        // only got half a screen, show full screen
+        if ((sub_y- (*sub_y_pos)) < page_space)
+            *sub_y_pos = sub_y - page_space;
+
+        // went past end, show full screen
+        else if (*sub_y_pos >= sub_y)
+            *sub_y_pos = sub_y - page_space - 1;
+
+        win_update_virtual(window);
+    }
+}
+
+void
+win_sub_page_up(ProfWin *window)
+{
+    if (window->layout->type == LAYOUT_SPLIT) {
+        int rows = getmaxy(stdscr);
+        int page_space = rows - 4;
+        ProfLayoutSplit *split_layout = (ProfLayoutSplit*)window->layout;
+        int *sub_y_pos = &(split_layout->sub_y_pos);
+
+        *sub_y_pos -= page_space;
+
+        // went past beginning, show first page
+        if (*sub_y_pos < 0)
+            *sub_y_pos = 0;
+
+        win_update_virtual(window);
+    }
+}
+
+void
+win_mouse(ProfWin *window, const wint_t ch, const int result)
 {
     int rows = getmaxy(stdscr);
     int y = getcury(window->layout->win);
@@ -403,69 +498,6 @@ win_handle_page(ProfWin *window, const wint_t ch, const int result)
             }
         }
     }
-
-    // page up
-    if (ch == KEY_PPAGE) {
-        *page_start -= page_space;
-
-        // went past beginning, show first page
-        if (*page_start < 0)
-            *page_start = 0;
-
-        window->layout->paged = 1;
-        win_update_virtual(window);
-
-    // page down
-    } else if (ch == KEY_NPAGE) {
-        *page_start += page_space;
-
-        // only got half a screen, show full screen
-        if ((y - (*page_start)) < page_space)
-            *page_start = y - page_space;
-
-        // went past end, show full screen
-        else if (*page_start >= y)
-            *page_start = y - page_space - 1;
-
-        window->layout->paged = 1;
-        win_update_virtual(window);
-    }
-
-    // switch off page if last line and space line visible
-    if ((y) - *page_start == page_space) {
-        window->layout->paged = 0;
-    }
-
-    if (window->layout->type == LAYOUT_SPLIT) {
-        ProfLayoutSplit *split_layout = (ProfLayoutSplit*)window->layout;
-        int sub_y = getcury(split_layout->subwin);
-        int *sub_y_pos = &(split_layout->sub_y_pos);
-
-        // alt up arrow
-        if ((result == KEY_CODE_YES) && ((ch == 565) || (ch == 337))) {
-            *sub_y_pos -= page_space;
-
-            // went past beginning, show first page
-            if (*sub_y_pos < 0)
-                *sub_y_pos = 0;
-
-            win_update_virtual(window);
-
-        // alt down arrow
-        } else if ((result == KEY_CODE_YES) && ((ch == 524) || (ch == 336))) {
-            *sub_y_pos += page_space;
-
-            // only got half a screen, show full screen
-            if ((sub_y- (*sub_y_pos)) < page_space)
-                *sub_y_pos = sub_y - page_space;
-
-            // went past end, show full screen
-            else if (*sub_y_pos >= sub_y)
-                *sub_y_pos = sub_y - page_space - 1;
-
-            win_update_virtual(window);
-        }
-    }
 }
 
 void
@@ -959,7 +991,6 @@ _win_indent(WINDOW *win, int size)
 static void
 _win_print_wrapped(WINDOW *win, const char * const message)
 {
-    int linei = 0;
     int wordi = 0;
     char *word = malloc(strlen(message) + 1);
 
@@ -972,18 +1003,26 @@ _win_print_wrapped(WINDOW *win, const char * const message)
     }
     free(time_pref);
 
-    while (message[linei] != '\0') {
-        if (message[linei] == ' ') {
+    gchar *curr_ch = g_utf8_offset_to_pointer(message, 0);
+
+    while (*curr_ch != '\0') {
+        if (*curr_ch == ' ') {
             waddch(win, ' ');
-            linei++;
-        } else if (message[linei] == '\n') {
+            curr_ch = g_utf8_next_char(curr_ch);
+        } else if (*curr_ch == '\n') {
             waddch(win, '\n');
             _win_indent(win, indent);
-            linei++;
+            curr_ch = g_utf8_next_char(curr_ch);
         } else {
+            // get word
             wordi = 0;
-            while (message[linei] != ' ' && message[linei] != '\n' && message[linei] != '\0') {
-                word[wordi++] = message[linei++];
+            while (*curr_ch != ' ' && *curr_ch != '\n' && *curr_ch != '\0') {
+                size_t ch_len = mbrlen(curr_ch, 4, NULL);
+                int offset = 0;
+                while (offset < ch_len) {
+                    word[wordi++] = curr_ch[offset++];
+                }
+                curr_ch = g_utf8_next_char(curr_ch);
             }
             word[wordi] = '\0';
 
@@ -991,17 +1030,27 @@ _win_print_wrapped(WINDOW *win, const char * const message)
             int maxx = getmaxx(win);
 
             // word larger than line
-            if (strlen(word) > (maxx - indent)) {
-                int i;
-                for (i = 0; i < wordi; i++) {
+            if (utf8_display_len(word) > (maxx - indent)) {
+                gchar *word_ch = g_utf8_offset_to_pointer(word, 0);
+                while(*word_ch != '\0') {
                     curx = getcurx(win);
                     if (curx < indent) {
                         _win_indent(win, indent);
                     }
-                    waddch(win, word[i]);
+
+                    gchar copy[wordi++];
+                    g_utf8_strncpy(copy, word_ch, 1);
+
+                    if (curx + utf8_display_len(copy) > maxx) {
+                        waddch(win, '\n');
+                        _win_indent(win, indent);
+                    }
+                    waddstr(win, copy);
+
+                    word_ch = g_utf8_next_char(word_ch);
                 }
             } else {
-                if (curx + strlen(word) > maxx) {
+                if (curx + utf8_display_len(word) > maxx) {
                     waddch(win, '\n');
                     _win_indent(win, indent);
                 }
diff --git a/src/ui/window.h b/src/ui/window.h
index fd10a1d7..66e4a5f2 100644
--- a/src/ui/window.h
+++ b/src/ui/window.h
@@ -178,9 +178,14 @@ void win_show_subwin(ProfWin *window);
 int win_roster_cols(void);
 int win_occpuants_cols(void);
 void win_printline_nowrap(WINDOW *win, char *msg);
-void win_handle_page(ProfWin *current, const wint_t ch, const int result);
+void win_mouse(ProfWin *current, const wint_t ch, const int result);
 
 int win_unread(ProfWin *window);
 gboolean win_has_active_subwin(ProfWin *window);
 
+void win_page_up(ProfWin *window);
+void win_page_down(ProfWin *window);
+void win_sub_page_down(ProfWin *window);
+void win_sub_page_up(ProfWin *window);
+
 #endif