From 87843f47e4c12d57c51157996757186ae5c564d4 Mon Sep 17 00:00:00 2001
From: Přemysl Janouch
Date: Tue, 5 May 2015 03:23:53 +0200
Subject: degesch: try to abstract GNU Readline
---
degesch.c | 777 ++++++++++++++++++++++++++++++++++++--------------------------
1 file changed, 447 insertions(+), 330 deletions(-)
diff --git a/degesch.c b/degesch.c
index 639b60b..75aff82 100644
--- a/degesch.c
+++ b/degesch.c
@@ -67,6 +67,289 @@ enum
#include
#include
+// --- User interface ----------------------------------------------------------
+
+// Currently provided by GNU Readline. A libedit backend is also possible.
+
+struct input_buffer
+{
+ HISTORY_STATE *history; ///< Saved history state
+ char *saved_line; ///< Saved line content
+ int saved_point; ///< Saved cursor position
+ int saved_mark; ///< Saved mark
+};
+
+static struct input_buffer *
+input_buffer_new (void)
+{
+ struct input_buffer *self = xcalloc (1, sizeof *self);
+ return self;
+}
+
+static void
+input_buffer_destroy (struct input_buffer *self)
+{
+ // Can't really free "history" from here
+ free (self->saved_line);
+ free (self);
+}
+
+struct input
+{
+ bool active; ///< Are we a thing?
+
+ char *saved_line; ///< Saved line content
+ int saved_point; ///< Saved cursor position
+ int saved_mark; ///< Saved mark
+
+ char *prompt; ///< The prompt we use
+ int prompt_shown; ///< Whether the prompt is shown now
+
+ struct input_buffer *current; ///< Current input buffer
+};
+
+static void
+input_init (struct input *self)
+{
+ memset (self, 0, sizeof *self);
+}
+
+static void
+input_free (struct input *self)
+{
+ free (self->saved_line);
+ free (self->prompt);
+}
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+#define input_ding(self) rl_ding ()
+
+static void
+input_on_terminal_resized (struct input *self)
+{
+ (void) self;
+
+ // This fucks up big time on terminals with automatic wrapping such as
+ // rxvt-unicode or newer VTE when the current line overflows, however we
+ // can't do much about that
+ rl_resize_terminal ();
+}
+
+static void
+input_on_readable (struct input *self)
+{
+ (void) self;
+ rl_callback_read_char ();
+}
+
+static void
+input_set_prompt (struct input *self, char *prompt)
+{
+ free (self->prompt);
+ self->prompt = prompt;
+
+ // First reset the prompt to work around a bug in readline
+ rl_set_prompt ("");
+ if (self->prompt_shown)
+ rl_redisplay ();
+
+ rl_set_prompt (self->prompt);
+ if (self->prompt_shown)
+ rl_redisplay ();
+}
+
+static void
+input_erase (struct input *self)
+{
+ (void) self;
+
+ rl_set_prompt ("");
+ rl_replace_line ("", 0);
+ rl_point = rl_mark = 0;
+ rl_redisplay ();
+}
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+static int app_readline_init (void);
+static void on_readline_input (char *line);
+
+static void
+input_start (struct input *self)
+{
+ rl_startup_hook = app_readline_init;
+ rl_catch_sigwinch = false;
+ rl_callback_handler_install (self->prompt, on_readline_input);
+ self->prompt_shown = 1;
+ self->active = true;
+}
+
+static void
+input_stop (struct input *self)
+{
+ if (self->prompt_shown > 0)
+ input_erase (self);
+
+ // This is okay as long as we're not called from within readline
+ rl_callback_handler_remove ();
+ self->active = false;
+}
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+static void
+input_save (struct input *self)
+{
+ hard_assert (!self->saved_line);
+
+ self->saved_point = rl_point;
+ self->saved_mark = rl_mark;
+ self->saved_line = rl_copy_text (0, rl_end);
+}
+
+static void
+input_restore (struct input *self)
+{
+ hard_assert (self->saved_line);
+
+ rl_set_prompt (self->prompt);
+ rl_replace_line (self->saved_line, 0);
+ rl_point = self->saved_point;
+ rl_mark = self->saved_mark;
+ free (self->saved_line);
+ self->saved_line = NULL;
+}
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+static void
+input_hide (struct input *self)
+{
+ if (!self->active || self->prompt_shown-- < 1)
+ return;
+
+ input_save (self);
+ input_erase (self);
+}
+
+static void
+input_show (struct input *self)
+{
+ if (!self->active || ++self->prompt_shown < 1)
+ return;
+
+ input_restore (self);
+ rl_redisplay ();
+}
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+// The following part shows you why it's not a good idea to use
+// GNU Readline for this kind of software. Or for anything else, really.
+
+static void
+input_save_buffer (struct input *self, struct input_buffer *buffer)
+{
+ (void) self;
+
+ buffer->history = history_get_history_state ();
+ buffer->saved_line = rl_copy_text (0, rl_end);
+ buffer->saved_point = rl_point;
+ buffer->saved_mark = rl_mark;
+}
+
+static void
+input_restore_buffer (struct input *self, struct input_buffer *buffer)
+{
+ // Restore the target buffer's history
+ if (buffer->history)
+ {
+ // history_get_history_state() just allocates a new HISTORY_STATE
+ // and fills it with its current internal data. We don't need that
+ // shell anymore after reviving it.
+ history_set_history_state (buffer->history);
+ free (buffer->history);
+ buffer->history = NULL;
+ }
+ else
+ {
+ // This should get us a clean history while keeping the flags.
+ // Note that we've either saved the previous history entries, or we've
+ // cleared them altogether, so there should be nothing to leak.
+ HISTORY_STATE *state = history_get_history_state ();
+ state->offset = state->length = state->size = 0;
+ history_set_history_state (state);
+ free (state);
+ }
+
+ // Try to restore the target buffer's readline state
+ if (buffer->saved_line)
+ {
+ rl_replace_line (buffer->saved_line, 0);
+ rl_point = buffer->saved_point;
+ rl_mark = buffer->saved_mark;
+ free (buffer->saved_line);
+ buffer->saved_line = NULL;
+
+ if (self->prompt_shown)
+ rl_redisplay ();
+ }
+}
+
+static void
+input_switch_buffer (struct input *self, struct input_buffer *buffer)
+{
+ // There could possibly be occurences of the current undo list in some
+ // history entry. We either need to free the undo list, or move it
+ // somewhere else to load back later, as the buffer we're switching to
+ // has its own history state.
+ rl_free_undo_list ();
+
+ // Save this buffer's history so that it's independent for each buffer
+ if (self->current)
+ input_save_buffer (self, self->current);
+ else
+ // Just throw it away; there should always be an active buffer however
+#if RL_READLINE_VERSION >= 0x0603
+ rl_clear_history ();
+#else // RL_READLINE_VERSION < 0x0603
+ // At least something... this may leak undo entries
+ clear_history ();
+#endif // RL_READLINE_VERSION < 0x0603
+
+ input_restore_buffer (self, buffer);
+ self->current = buffer;
+}
+
+static void
+input_destroy_buffer (struct input *self, struct input_buffer *buffer)
+{
+ (void) self;
+
+ // rl_clear_history, being the only way I know of to get rid of the complete
+ // history including attached data, is a pretty recent addition. *sigh*
+#if RL_READLINE_VERSION >= 0x0603
+ if (buffer->history)
+ {
+ // See buffer_activate() for why we need to do this BS
+ rl_free_undo_list ();
+
+ // This is probably the only way we can free the history fully
+ HISTORY_STATE *state = history_get_history_state ();
+
+ history_set_history_state (buffer->history);
+ free (buffer->history);
+ rl_clear_history ();
+
+ history_set_history_state (state);
+ free (state);
+ }
+#endif // RL_READLINE_VERSION
+
+ input_buffer_destroy (buffer);
+}
+
// --- Application data --------------------------------------------------------
// All text stored in our data structures is encoded in UTF-8.
@@ -310,12 +593,7 @@ struct buffer
enum buffer_type type; ///< Type of the buffer
char *name; ///< The name of the buffer
- // Readline state:
-
- HISTORY_STATE *history; ///< Saved history state
- char *saved_line; ///< Saved line
- int saved_point; ///< Saved position in line
- int saved_mark; ///< Saved mark
+ struct input_buffer *input_data; ///< User interface data
// Buffer contents:
@@ -336,6 +614,7 @@ static struct buffer *
buffer_new (void)
{
struct buffer *self = xcalloc (1, sizeof *self);
+ self->input_data = input_buffer_new ();
return self;
}
@@ -343,8 +622,8 @@ static void
buffer_destroy (struct buffer *self)
{
free (self->name);
- // Can't really free "history" here
- free (self->saved_line);
+ if (self->input_data)
+ input_buffer_destroy (self->input_data);
LIST_FOR_EACH (struct buffer_line, iter, self->lines)
buffer_line_destroy (iter);
if (self->user)
@@ -501,8 +780,7 @@ struct app_context
int lines; ///< Current terminal height
int columns; ///< Current ternimal width
- char *readline_prompt; ///< The prompt we use for readline
- bool readline_prompt_shown; ///< Whether the prompt is shown now
+ struct input input; ///< User interface
}
*g_ctx;
@@ -539,6 +817,8 @@ app_context_init (struct app_context *self)
strerror (errno));
free (encoding);
+
+ input_init (&self->input);
}
static void
@@ -560,7 +840,7 @@ app_context_free (struct app_context *self)
iconv_close (self->term_from_utf8);
iconv_close (self->term_to_utf8);
- free (self->readline_prompt);
+ input_free (&self->input);
}
static void refresh_prompt (struct app_context *ctx);
@@ -899,47 +1179,6 @@ free_terminal (void)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-struct app_readline_state
-{
- char *saved_line;
- int saved_point;
- int saved_mark;
-};
-
-static void
-app_readline_hide (struct app_readline_state *state)
-{
- state->saved_point = rl_point;
- state->saved_mark = rl_mark;
- state->saved_line = rl_copy_text (0, rl_end);
- rl_set_prompt ("");
- rl_replace_line ("", 0);
- rl_redisplay ();
-}
-
-static void
-app_readline_restore (struct app_readline_state *state, const char *prompt)
-{
- rl_set_prompt (prompt);
- rl_replace_line (state->saved_line, 0);
- rl_point = state->saved_point;
- rl_mark = state->saved_mark;
- rl_redisplay ();
- free (state->saved_line);
-}
-
-static void
-app_readline_erase_to_bol (const char *prompt)
-{
- rl_set_prompt ("");
- rl_replace_line ("", 0);
- rl_point = rl_mark = 0;
- rl_redisplay ();
- rl_set_prompt (prompt);
-}
-
-// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
typedef int (*terminal_printer_fn) (int);
static int
@@ -991,16 +1230,13 @@ log_message_attributed (void *user_data, const char *quote, const char *fmt,
{
FILE *stream = stderr;
- struct app_readline_state state;
- if (g_ctx->readline_prompt_shown)
- app_readline_hide (&state);
+ input_hide (&g_ctx->input);
print_attributed (g_ctx, stream, (intptr_t) user_data, "%s", quote);
vprint_attributed (g_ctx, stream, (intptr_t) user_data, fmt, ap);
fputs ("\n", stream);
- if (g_ctx->readline_prompt_shown)
- app_readline_restore (&state, g_ctx->readline_prompt);
+ input_show (&g_ctx->input);
}
static void
@@ -1550,9 +1786,7 @@ buffer_line_display (struct app_context *ctx,
free (nick);
- struct app_readline_state state;
- if (ctx->readline_prompt_shown)
- app_readline_hide (&state);
+ input_hide (&ctx->input);
// TODO: write the line to a log file; note that the global and server
// buffers musn't collide with filenames
@@ -1561,8 +1795,7 @@ buffer_line_display (struct app_context *ctx,
formatter_flush (&f, stdout);
formatter_free (&f);
- if (ctx->readline_prompt_shown)
- app_readline_restore (&state, ctx->readline_prompt);
+ input_show (&ctx->input);
}
static void
@@ -1638,25 +1871,8 @@ buffer_remove (struct app_context *ctx, struct buffer *buffer)
// TODO: part from the channel if needed
- // rl_clear_history, being the only way I know of to get rid of the complete
- // history including attached data, is a pretty recent addition. *sigh*
-#if RL_READLINE_VERSION >= 0x0603
- if (buffer->history)
- {
- // See buffer_activate() for why we need to do this BS
- rl_free_undo_list ();
-
- // This is probably the only way we can free the history fully
- HISTORY_STATE *state = history_get_history_state ();
-
- history_set_history_state (buffer->history);
- free (buffer->history);
- rl_clear_history ();
-
- history_set_history_state (state);
- free (state);
- }
-#endif // RL_READLINE_VERSION
+ input_destroy_buffer (&ctx->input, buffer->input_data);
+ buffer->input_data = NULL;
// And make sure to unlink the buffer from "irc_buffer_map"
struct server *s = buffer->server;
@@ -1692,74 +1908,16 @@ buffer_activate (struct app_context *ctx, struct buffer *buffer)
// That is, minus the buffer switch line and the readline prompt
int to_display = MAX (10, ctx->lines - 2);
- struct buffer_line *line = buffer->lines_tail;
- while (line && line->prev && --to_display > 0)
- line = line->prev;
-
- // Once we've found where we want to start with the backlog, print it
- for (; line; line = line->next)
- buffer_line_display (ctx, line, false);
- buffer->unseen_messages_count = 0;
-
- // The following part shows you why it's not a good idea to use
- // GNU Readline for this kind of software. Or for anything else, really.
-
- // There could possibly be occurences of the current undo list in some
- // history entry. We either need to free the undo list, or move it
- // somewhere else to load back later, as the buffer we're switching to
- // has its own history state.
- rl_free_undo_list ();
-
- // Save this buffer's history so that it's independent for each buffer
- if (ctx->current_buffer)
- {
- ctx->current_buffer->history = history_get_history_state ();
- ctx->current_buffer->saved_line = rl_copy_text (0, rl_end);
- ctx->current_buffer->saved_point = rl_point;
- ctx->current_buffer->saved_mark = rl_mark;
- }
- else
- // Just throw it away; there should always be an active buffer however
-#if RL_READLINE_VERSION >= 0x0603
- rl_clear_history ();
-#else // RL_READLINE_VERSION < 0x0603
- // At least something... this may leak undo entries
- clear_history ();
-#endif // RL_READLINE_VERSION < 0x0603
-
- // Restore the target buffer's history
- if (buffer->history)
- {
- // history_get_history_state() just allocates a new HISTORY_STATE
- // and fills it with its current internal data. We don't need that
- // shell anymore after reviving it.
- history_set_history_state (buffer->history);
- free (buffer->history);
- buffer->history = NULL;
- }
- else
- {
- // This should get us a clean history while keeping the flags.
- // Note that we've either saved the previous history entries, or we've
- // cleared them altogether, so there should be nothing to leak.
- HISTORY_STATE *state = history_get_history_state ();
- state->offset = state->length = state->size = 0;
- history_set_history_state (state);
- free (state);
- }
-
- // Try to restore the target buffer's readline state
- if (buffer->saved_line)
- {
- rl_replace_line (buffer->saved_line, 0);
- rl_point = buffer->saved_point;
- rl_mark = buffer->saved_mark;
- free (buffer->saved_line);
- buffer->saved_line = 0;
+ struct buffer_line *line = buffer->lines_tail;
+ while (line && line->prev && --to_display > 0)
+ line = line->prev;
- if (ctx->readline_prompt_shown)
- rl_redisplay ();
- }
+ // Once we've found where we want to start with the backlog, print it
+ for (; line; line = line->next)
+ buffer_line_display (ctx, line, false);
+ buffer->unseen_messages_count = 0;
+
+ input_switch_buffer (&ctx->input, buffer->input_data);
// Now at last we can switch the pointers
ctx->last_buffer = ctx->current_buffer;
@@ -2039,15 +2197,8 @@ try_finish_quit (struct app_context *ctx)
static void
initiate_quit (struct app_context *ctx)
{
- // First get rid of readline
- if (ctx->readline_prompt_shown)
- {
- app_readline_erase_to_bol (ctx->readline_prompt);
- ctx->readline_prompt_shown = false;
- }
-
- // This is okay as long as we're not called from within readline
- rl_callback_handler_remove ();
+ // Destroy the user interface
+ input_stop (&ctx->input);
buffer_send_status (ctx, ctx->global_buffer, "Shutting down");
@@ -2106,16 +2257,13 @@ irc_send (struct server *s, const char *format, ...)
if (g_debug_mode)
{
- struct app_readline_state state;
- if (s->ctx->readline_prompt_shown)
- app_readline_hide (&state);
+ input_hide (&s->ctx->input);
char *term = irc_to_term (s->ctx, str.str);
fprintf (stderr, "[IRC] <== \"%s\"\n", term);
free (term);
- if (s->ctx->readline_prompt_shown)
- app_readline_restore (&state, s->ctx->readline_prompt);
+ input_show (&s->ctx->input);
}
str_append (&str, "\r\n");
@@ -2383,145 +2531,19 @@ refresh_prompt (struct app_context *ctx)
make_prompt (ctx, &prompt);
str_append_c (&prompt, ' ');
- // After building the new prompt, replace the old one
- free (ctx->readline_prompt);
-
if (!have_attributes)
- ctx->readline_prompt = xstrdup (prompt.str);
+ input_set_prompt (&ctx->input, xstrdup (prompt.str));
else
{
// XXX: to be completely correct, we should use tputs, but we cannot
- ctx->readline_prompt = xstrdup_printf ("%c%s%c%s%c%s%c",
+ input_set_prompt (&ctx->input, xstrdup_printf ("%c%s%c%s%c%s%c",
RL_PROMPT_START_IGNORE, ctx->attrs[ATTR_PROMPT],
RL_PROMPT_END_IGNORE,
prompt.str,
RL_PROMPT_START_IGNORE, ctx->attrs[ATTR_RESET],
- RL_PROMPT_END_IGNORE);
+ RL_PROMPT_END_IGNORE));
}
str_free (&prompt);
-
- // First reset the prompt to work around a bug in readline
- rl_set_prompt ("");
- if (ctx->readline_prompt_shown)
- rl_redisplay ();
-
- rl_set_prompt (ctx->readline_prompt);
- if (ctx->readline_prompt_shown)
- rl_redisplay ();
-}
-
-static int
-on_readline_goto_buffer (int count, int key)
-{
- (void) count;
-
- int n = UNMETA (key) - '0';
- if (n < 0 || n > 9)
- return 0;
-
- // There's no buffer zero
- if (n == 0)
- n = 10;
-
- struct app_context *ctx = g_ctx;
- if (ctx->last_buffer && buffer_get_index (ctx, ctx->current_buffer) == n)
- // Fast switching between two buffers
- buffer_activate (ctx, ctx->last_buffer);
- else if (!buffer_goto (ctx, n == 0 ? 10 : n))
- rl_ding ();
- return 0;
-}
-
-static int
-on_readline_previous_buffer (int count, int key)
-{
- (void) key;
-
- struct app_context *ctx = g_ctx;
- if (ctx->current_buffer)
- buffer_activate (ctx, buffer_previous (ctx, count));
- return 0;
-}
-
-static int
-on_readline_next_buffer (int count, int key)
-{
- (void) key;
-
- struct app_context *ctx = g_ctx;
- if (ctx->current_buffer)
- buffer_activate (ctx, buffer_next (ctx, count));
- return 0;
-}
-
-static int
-on_readline_return (int count, int key)
-{
- (void) count;
- (void) key;
-
- struct app_context *ctx = g_ctx;
-
- // Let readline pass the line to our input handler
- rl_done = 1;
-
- // Save readline state
- int saved_point = rl_point;
- int saved_mark = rl_mark;
- char *saved_line = rl_copy_text (0, rl_end);
-
- // Erase the entire line from screen
- rl_set_prompt ("");
- rl_replace_line ("", 0);
- rl_redisplay ();
- ctx->readline_prompt_shown = false;
-
- // Restore readline state
- rl_set_prompt (ctx->readline_prompt);
- rl_replace_line (saved_line, 0);
- rl_point = saved_point;
- rl_mark = saved_mark;
- free (saved_line);
- return 0;
-}
-
-static void
-app_readline_bind_meta (char key, rl_command_func_t cb)
-{
- // This one seems to actually work
- char keyseq[] = { '\\', 'e', key, 0 };
- rl_bind_keyseq (keyseq, cb);
-#if 0
- // While this one only fucks up UTF-8
- // Tested with urxvt and xterm, on Debian Jessie/Arch, default settings
- // \M- behaves exactly the same
- rl_bind_key (META (key), cb);
-#endif
-}
-
-static int
-init_readline (void)
-{
- // XXX: maybe use rl_make_bare_keymap() and start from there;
- // our dear user could potentionally rig things up in a way that might
- // result in some funny unspecified behaviour
-
- rl_add_defun ("previous-buffer", on_readline_previous_buffer, -1);
- rl_add_defun ("next-buffer", on_readline_next_buffer, -1);
-
- // Redefine M-0 through M-9 to switch buffers
- for (int i = 0; i <= 9; i++)
- app_readline_bind_meta ('0' + i, on_readline_goto_buffer);
-
- rl_bind_keyseq ("\\C-p", rl_named_function ("previous-buffer"));
- rl_bind_keyseq ("\\C-n", rl_named_function ("next-buffer"));
- app_readline_bind_meta ('p', rl_named_function ("previous-history"));
- app_readline_bind_meta ('n', rl_named_function ("next-history"));
-
- // We need to hide the prompt first
- rl_bind_key (RETURN, on_readline_return);
-
- return 0;
}
// --- Input handling ----------------------------------------------------------
@@ -3195,16 +3217,13 @@ irc_process_message (const struct irc_message *msg,
if (g_debug_mode)
{
- struct app_readline_state state;
- if (s->ctx->readline_prompt_shown)
- app_readline_hide (&state);
+ input_hide (&s->ctx->input);
char *term = irc_to_term (s->ctx, raw);
fprintf (stderr, "[IRC] ==> \"%s\"\n", term);
free (term);
- if (s->ctx->readline_prompt_shown)
- app_readline_restore (&state, s->ctx->readline_prompt);
+ input_show (&s->ctx->input);
}
// XXX: or is the 001 numeric enough? For what?
@@ -4688,6 +4707,132 @@ irc_connect (struct server *s, bool *should_retry, struct error **e)
return true;
}
+// --- User interface actions --------------------------------------------------
+
+static int
+on_readline_goto_buffer (int count, int key)
+{
+ (void) count;
+
+ int n = UNMETA (key) - '0';
+ if (n < 0 || n > 9)
+ return 0;
+
+ // There's no buffer zero
+ if (n == 0)
+ n = 10;
+
+ struct app_context *ctx = g_ctx;
+ if (ctx->last_buffer && buffer_get_index (ctx, ctx->current_buffer) == n)
+ // Fast switching between two buffers
+ buffer_activate (ctx, ctx->last_buffer);
+ else if (!buffer_goto (ctx, n == 0 ? 10 : n))
+ input_ding (self);
+ return 0;
+}
+
+static int
+on_readline_previous_buffer (int count, int key)
+{
+ (void) key;
+
+ struct app_context *ctx = g_ctx;
+ if (ctx->current_buffer)
+ buffer_activate (ctx, buffer_previous (ctx, count));
+ return 0;
+}
+
+static int
+on_readline_next_buffer (int count, int key)
+{
+ (void) key;
+
+ struct app_context *ctx = g_ctx;
+ if (ctx->current_buffer)
+ buffer_activate (ctx, buffer_next (ctx, count));
+ return 0;
+}
+
+static int
+on_readline_return (int count, int key)
+{
+ (void) count;
+ (void) key;
+
+ struct input *self = &g_ctx->input;
+
+ // Let readline pass the line to our input handler
+ rl_done = 1;
+
+ // Hide the line, don't redisplay it
+ input_hide (self);
+ input_restore (self);
+ return 0;
+}
+
+static void
+on_readline_input (char *line)
+{
+ struct input *self = &g_ctx->input;
+
+ if (line)
+ {
+ if (*line)
+ add_history (line);
+
+ process_input (g_ctx, line);
+ free (line);
+ }
+ else
+ {
+ input_hide (self);
+ input_restore (self);
+ input_ding (self);
+ }
+
+ if (self->active)
+ // Readline automatically redisplays it
+ self->prompt_shown = 1;
+}
+
+static void
+app_readline_bind_meta (char key, rl_command_func_t cb)
+{
+ // This one seems to actually work
+ char keyseq[] = { '\\', 'e', key, 0 };
+ rl_bind_keyseq (keyseq, cb);
+#if 0
+ // While this one only fucks up UTF-8
+ // Tested with urxvt and xterm, on Debian Jessie/Arch, default settings
+ // \M- behaves exactly the same
+ rl_bind_key (META (key), cb);
+#endif
+}
+
+static int
+app_readline_init (void)
+{
+ // XXX: maybe use rl_make_bare_keymap() and start from there;
+ // our dear user could potentionally rig things up in a way that might
+ // result in some funny unspecified behaviour
+
+ rl_add_defun ("previous-buffer", on_readline_previous_buffer, -1);
+ rl_add_defun ("next-buffer", on_readline_next_buffer, -1);
+
+ // Redefine M-0 through M-9 to switch buffers
+ for (int i = 0; i <= 9; i++)
+ app_readline_bind_meta ('0' + i, on_readline_goto_buffer);
+
+ rl_bind_keyseq ("\\C-p", rl_named_function ("previous-buffer"));
+ rl_bind_keyseq ("\\C-n", rl_named_function ("next-buffer"));
+ app_readline_bind_meta ('p', rl_named_function ("previous-history"));
+ app_readline_bind_meta ('n', rl_named_function ("next-history"));
+
+ // We need to hide the prompt first
+ rl_bind_key (RETURN, on_readline_return);
+ return 0;
+}
+
// --- I/O event handlers ------------------------------------------------------
static void
@@ -4712,10 +4857,7 @@ on_signal_pipe_readable (const struct pollfd *fd, struct app_context *ctx)
if (g_winch_received)
{
- // This fucks up big time on terminals with automatic wrapping such as
- // rxvt-unicode or newer VTE when the current line overflows, however we
- // can't do much about that
- rl_resize_terminal ();
+ input_on_terminal_resized (&ctx->input);
rl_get_screen_size (&ctx->lines, &ctx->columns);
}
}
@@ -4728,29 +4870,7 @@ on_tty_readable (const struct pollfd *fd, struct app_context *ctx)
if (fd->revents & ~(POLLIN | POLLHUP | POLLERR))
print_debug ("fd %d: unexpected revents: %d", fd->fd, fd->revents);
- rl_callback_read_char ();
-}
-
-static void
-on_readline_input (char *line)
-{
- if (line)
- {
- if (*line)
- add_history (line);
-
- process_input (g_ctx, line);
- free (line);
- }
- else
- {
- app_readline_erase_to_bol (g_ctx->readline_prompt);
- rl_ding ();
- }
-
- // initiate_quit() disables readline; we just wait then
- if (!g_ctx->quitting)
- g_ctx->readline_prompt_shown = true;
+ input_on_readable (&ctx->input);
}
// --- Configuration loading ---------------------------------------------------
@@ -4957,18 +5077,15 @@ main (int argc, char *argv[])
init_colors (&ctx);
init_poller_events (&ctx);
init_buffers (&ctx);
- ctx.current_buffer = ctx.server.buffer;
+ buffer_activate (&ctx, ctx.server.buffer);
+
refresh_prompt (&ctx);
+ input_start (&ctx.input);
+ rl_get_screen_size (&ctx.lines, &ctx.columns);
// Connect to the server ASAP
poller_timer_set (&ctx.server.reconnect_tmr, 0);
- rl_startup_hook = init_readline;
- rl_catch_sigwinch = false;
- rl_callback_handler_install (ctx.readline_prompt, on_readline_input);
- rl_get_screen_size (&ctx.lines, &ctx.columns);
- ctx.readline_prompt_shown = true;
-
ctx.polling = true;
while (ctx.polling)
poller_run (&ctx.poller);
--
cgit v1.2.3-70-g09d2