aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPřemysl Janouch <p.janouch@gmail.com>2016-03-07 01:08:52 +0100
committerPřemysl Janouch <p.janouch@gmail.com>2016-03-07 22:52:56 +0100
commit696273558ea6f9a766e340fb9349560a209cdaac (patch)
treedccaada03ff9a2f2d03c5d0d0dbee6a4ca1adf24
parent584d2f0295df01cff4ac24c336af40f4c2a762e2 (diff)
downloadxK-696273558ea6f9a766e340fb9349560a209cdaac.tar.gz
xK-696273558ea6f9a766e340fb9349560a209cdaac.tar.xz
xK-696273558ea6f9a766e340fb9349560a209cdaac.zip
degesch: rewrite input layer
Now with less #ifdefs.
-rw-r--r--degesch.c1098
1 files changed, 652 insertions, 446 deletions
diff --git a/degesch.c b/degesch.c
index 3d0130a..838ec34 100644
--- a/degesch.c
+++ b/degesch.c
@@ -70,160 +70,256 @@ enum
#include <ffi.h>
-#ifdef HAVE_READLINE
-#include <readline/readline.h>
-#include <readline/history.h>
-#endif // HAVE_READLINE
-
-#ifdef HAVE_EDITLINE
-#include <histedit.h>
-#endif // HAVE_EDITLINE
-
#ifdef HAVE_LUA
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
#endif // HAVE_LUA
-/// Some arbitrary limit for the history file
-#define HISTORY_LIMIT 10000
+// --- Terminal information ----------------------------------------------------
-/// Characters that separate words
-#define WORD_BREAKING_CHARS " \f\n\r\t\v"
+static struct
+{
+ bool initialized; ///< Terminal is available
+ bool stdout_is_tty; ///< `stdout' is a terminal
+ bool stderr_is_tty; ///< `stderr' is a terminal
-// --- User interface ----------------------------------------------------------
+ struct termios termios; ///< Terminal attributes
+ char *color_set_fg[256]; ///< Codes to set the foreground colour
+ char *color_set_bg[256]; ///< Codes to set the background colour
-// I'm not sure which one of these backends is worse: whether it's GNU Readline
-// or BSD Editline. They both have their own annoying problems.
+ int lines; ///< Number of lines
+ int columns; ///< Number of columns
+}
+g_terminal;
-struct input_buffer
+static void
+update_screen_size (void)
{
-#ifdef HAVE_READLINE
- HISTORY_STATE *history; ///< Saved history state
- char *saved_line; ///< Saved line content
- int saved_mark; ///< Saved mark
-#elif defined HAVE_EDITLINE
- HistoryW *history; ///< The history object
- wchar_t *saved_line; ///< Saved line content
- int saved_len; ///< Length of the saved line
-#endif // HAVE_EDITLINE
- int saved_point; ///< Saved cursor position
-};
+#ifdef TIOCGWINSZ
+ if (!g_terminal.stdout_is_tty)
+ return;
-static struct input_buffer *
-input_buffer_new (void)
+ struct winsize size;
+ if (!ioctl (STDOUT_FILENO, TIOCGWINSZ, (char *) &size))
+ {
+ char *row = getenv ("LINES");
+ char *col = getenv ("COLUMNS");
+ unsigned long tmp;
+ g_terminal.lines =
+ (row && xstrtoul (&tmp, row, 10)) ? tmp : size.ws_row;
+ g_terminal.columns =
+ (col && xstrtoul (&tmp, col, 10)) ? tmp : size.ws_col;
+ }
+#endif // TIOCGWINSZ
+}
+
+static bool
+init_terminal (void)
{
- struct input_buffer *self = xcalloc (1, sizeof *self);
-#ifdef HAVE_EDITLINE
- self->history = history_winit ();
+ int tty_fd = -1;
+ if ((g_terminal.stderr_is_tty = isatty (STDERR_FILENO)))
+ tty_fd = STDERR_FILENO;
+ if ((g_terminal.stdout_is_tty = isatty (STDOUT_FILENO)))
+ tty_fd = STDOUT_FILENO;
- HistEventW ev;
- history_w (self->history, &ev, H_SETSIZE, HISTORY_LIMIT);
-#endif // HAVE_EDITLINE
- return self;
+ int err;
+ if (tty_fd == -1 || setupterm (NULL, tty_fd, &err) == ERR)
+ return false;
+
+ // Make sure all terminal features used by us are supported
+ if (!set_a_foreground || !set_a_background
+ || !enter_bold_mode || !exit_attribute_mode
+ || tcgetattr (tty_fd, &g_terminal.termios))
+ {
+ del_curterm (cur_term);
+ return false;
+ }
+
+ // Make sure newlines are output correctly
+ g_terminal.termios.c_oflag |= ONLCR;
+ (void) tcsetattr (tty_fd, TCSADRAIN, &g_terminal.termios);
+
+ g_terminal.lines = tigetnum ("lines");
+ g_terminal.columns = tigetnum ("cols");
+ update_screen_size ();
+
+ int max = MIN (256, max_colors);
+ for (int i = 0; i < max; i++)
+ {
+ g_terminal.color_set_fg[i] = xstrdup (tparm (set_a_foreground,
+ i, 0, 0, 0, 0, 0, 0, 0, 0));
+ g_terminal.color_set_bg[i] = xstrdup (tparm (set_a_background,
+ i, 0, 0, 0, 0, 0, 0, 0, 0));
+ }
+ return g_terminal.initialized = true;
}
static void
-input_buffer_destroy (struct input_buffer *self)
+free_terminal (void)
{
-#ifdef HAVE_READLINE
- // Can't really free "history" contents from here
- free (self->history);
-#elif defined HAVE_EDITLINE
- history_wend (self->history);
-#endif // HAVE_EDITLINE
- free (self->saved_line);
- free (self);
+ if (!g_terminal.initialized)
+ return;
+
+ for (int i = 0; i < 256; i++)
+ {
+ free (g_terminal.color_set_fg[i]);
+ free (g_terminal.color_set_bg[i]);
+ }
+ del_curterm (cur_term);
}
-typedef bool (*input_fn) (int count, int key, void *user_data);
+// --- User interface ----------------------------------------------------------
-struct input_fn_data
-{
- ffi_closure closure; ///< Closure
+// I'm not sure which one of these backends is worse: whether it's GNU Readline
+// or BSD Editline. They both have their own annoying problems. We use lots
+// of hacks to get the results we want and need.
+//
+// The abstraction is a necessary evil. It's still not 100%, though.
- LIST_HEADER (struct input_fn_data)
- input_fn callback; ///< Real callback
- void *user_data; ///< Real callback user data
+/// Some arbitrary limit for the history
+#define HISTORY_LIMIT 10000
-#ifdef HAVE_EDITLINE
- wchar_t *name; ///< Function name
- wchar_t *help; ///< Function help
-#endif // HAVE_EDITLINE
-};
+/// Characters that separate words
+#define WORD_BREAKING_CHARS " \f\n\r\t\v"
struct input
{
- bool active; ///< Are we a thing?
+ struct input_vtable *vtable; ///< Virtual methods
+ void *user_data; ///< User data for callbacks
+};
-#if defined HAVE_READLINE
- char *saved_line; ///< Saved line content
- int saved_point; ///< Saved cursor position
- int saved_mark; ///< Saved mark
-#elif defined HAVE_EDITLINE
- EditLine *editline; ///< The EditLine object
-#endif // HAVE_EDITLINE
- struct input_fn_data *fns; ///< Functions
+typedef void *input_buffer_t; ///< Pointer alias for input buffers
- char *prompt; ///< The prompt we use
- int prompt_shown; ///< Whether the prompt is shown now
+/// Named function that can be bound to a sequence of characters
+typedef bool (*input_fn) (int count, int key, void *user_data);
- struct input_buffer *current; ///< Current input buffer
+// A little bit better than tons of forwarder functions in our case
+#define CALL(self, name) ((self)->vtable->name ((self)))
+#define CALL_(self, name, ...) ((self)->vtable->name ((self), __VA_ARGS__))
+
+struct input_vtable
+{
+ /// Start the interface under the given program name
+ void (*start) (void *input, const char *program_name);
+ /// Stop the interface
+ void (*stop) (void *input);
+ /// Prepare or unprepare terminal for our needs
+ void (*prepare) (void *input, bool enabled);
+ /// Destroy the object
+ void (*destroy) (void *input);
+
+ /// Hide the prompt if shown
+ void (*hide) (void *input);
+ /// Show the prompt if hidden
+ void (*show) (void *input);
+ /// Change the prompt string; takes ownership
+ const char *(*get_prompt) (void *input);
+ /// Change the prompt string; takes ownership
+ void (*set_prompt) (void *input, char *prompt);
+ /// Ring the terminal bell
+ void (*ding) (void *input);
+
+ /// Create a new input buffer
+ input_buffer_t (*buffer_new) (void *input);
+ // XXX: there's also destroy_buffer
+ /// Destroy an input buffer
+ void (*buffer_destroy) (void *input, input_buffer_t buffer);
+ /// Switch to a different input buffer
+ void (*buffer_switch) (void *input, input_buffer_t buffer);
+
+ /// Register a function that can be bound to character sequences
+ void (*register_fn) (void *input,
+ const char *name, const char *help, input_fn fn, void *user_data);
+ /// Bind an arbitrary sequence of characters to the given named function
+ void (*bind) (void *input, const char *seq, const char *fn);
+ /// Bind Ctrl+key to the given named function
+ void (*bind_control) (void *input, char key, const char *fn);
+ /// Bind Alt+key to the given named function
+ void (*bind_meta) (void *input, char key, const char *fn);
+
+ /// Get the current line input
+ char *(*get_line) (void *input);
+ /// Clear the current line input
+ void (*clear_line) (void *input);
+ /// Insert text at current position
+ bool (*insert) (void *input, const char *text);
+
+ /// Handle terminal resize
+ void (*on_tty_resized) (void *input);
+ /// Handle terminal input
+ void (*on_tty_readable) (void *input);
};
-static void
-input_init (struct input *self)
-{
- memset (self, 0, sizeof *self);
-}
-
-static void
-input_free (struct input *self)
-{
-#ifdef HAVE_READLINE
- free (self->saved_line);
-#endif // HAVE_READLINE
- LIST_FOR_EACH (struct input_fn_data, iter, self->fns)
- {
-#ifdef HAVE_EDITLINE
- free (iter->name);
- free (iter->help);
-#endif // HAVE_EDITLINE
- ffi_closure_free (iter);
- }
- free (self->prompt);
-}
+#define INPUT_VTABLE(XX) \
+ XX (start) XX (stop) XX (prepare) XX (destroy) \
+ XX (hide) XX (show) XX (get_prompt) XX (set_prompt) XX (ding) \
+ XX (buffer_new) XX (buffer_destroy) XX (buffer_switch) \
+ XX (register_fn) XX (bind) XX (bind_control) XX (bind_meta) \
+ XX (get_line) XX (clear_line) XX (insert) \
+ XX (on_tty_resized) XX (on_tty_readable)
// --- GNU Readline ------------------------------------------------------------
#ifdef HAVE_READLINE
+#include <readline/readline.h>
+#include <readline/history.h>
+
#define INPUT_START_IGNORE RL_PROMPT_START_IGNORE
#define INPUT_END_IGNORE RL_PROMPT_END_IGNORE
-#define input_ding(self) rl_ding ()
+struct input_rl_fn
+{
+ ffi_closure closure; ///< Closure
+
+ LIST_HEADER (struct input_rl_fn)
+ input_fn callback; ///< Real callback
+ void *user_data; ///< Real callback user data
+};
-static void
-input_on_terminal_resized (struct input *self)
+struct input_rl_buffer
{
- (void) self;
+ HISTORY_STATE *history; ///< Saved history state
+ char *saved_line; ///< Saved line content
+ int saved_point; ///< Saved cursor position
+ int saved_mark; ///< Saved mark
+};
- // 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 ();
-}
+struct input_rl
+{
+ struct input super; ///< Parent class
+
+ bool active; ///< Interface has been started
+ char *prompt; ///< The prompt we use
+ int prompt_shown; ///< Whether the prompt is shown now
+
+ char *saved_line; ///< Saved line content
+ int saved_point; ///< Saved cursor position
+ int saved_mark; ///< Saved mark
+
+ struct input_rl_fn *fns; ///< Named functions
+ struct input_rl_buffer *current; ///< Current input buffer
+};
static void
-input_on_readable (struct input *self)
+input_rl_ding (void *input)
{
- (void) self;
- rl_callback_read_char ();
+ (void) input;
+ rl_ding ();
+}
+
+static const char *
+input_rl_get_prompt (void *input)
+{
+ struct input_rl *self = input;
+ return self->prompt;
}
static void
-input_set_prompt (struct input *self, char *prompt)
+input_rl_set_prompt (void *input, char *prompt)
{
+ struct input_rl *self = input;
free (self->prompt);
self->prompt = prompt;
@@ -241,36 +337,54 @@ input_set_prompt (struct input *self, char *prompt)
}
static void
-input_erase_content (struct input *self)
+input_rl_clear_line (void *input)
{
- (void) self;
-
+ (void) input;
rl_replace_line ("", false);
rl_redisplay ();
}
static void
-input_erase (struct input *self)
+input_rl__erase (struct input_rl *self)
{
- (void) self;
-
rl_set_prompt ("");
- input_erase_content (self);
+ input_rl_clear_line (self);
+}
+
+static bool
+input_rl_insert (void *input, const char *s)
+{
+ struct input_rl *self = input;
+ rl_insert_text (s);
+ if (self->prompt_shown > 0)
+ rl_redisplay ();
+
+ // GNU Readline, contrary to Editline, doesn't care about validity
+ return true;
}
+static char *
+input_rl_get_line (void *input)
+{
+ (void) input;
+ return rl_copy_text (0, rl_end);
+}
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
static void
-input_bind (struct input *self, const char *seq, const char *function_name)
+input_rl_bind (void *input, const char *seq, const char *function_name)
{
- (void) self;
+ (void) input;
rl_bind_keyseq (seq, rl_named_function (function_name));
}
static void
-input_bind_meta (struct input *self, char key, const char *function_name)
+input_rl_bind_meta (void *input, char key, const char *function_name)
{
// This one seems to actually work
char keyseq[] = { '\\', 'e', key, 0 };
- input_bind (self, keyseq, function_name);
+ input_rl_bind (input, keyseq, function_name);
#if 0
// While this one only fucks up UTF-8
// Tested with urxvt and xterm, on Debian Jessie/Arch, default settings
@@ -280,38 +394,18 @@ input_bind_meta (struct input *self, char key, const char *function_name)
}
static void
-input_bind_control (struct input *self, char key, const char *function_name)
+input_rl_bind_control (void *input, char key, const char *function_name)
{
char keyseq[] = { '\\', 'C', '-', key, 0 };
- input_bind (self, keyseq, function_name);
+ input_rl_bind (input, keyseq, function_name);
}
-static bool
-input_insert (struct input *self, const char *s)
-{
- rl_insert_text (s);
- if (self->prompt_shown > 0)
- rl_redisplay ();
-
- // GNU Readline, contrary to Editline, doesn't care about validity
- return true;
-}
-
-static char *
-input_get_content (struct input *self)
-{
- (void) self;
- return rl_copy_text (0, rl_end);
-}
-
-// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
static void
-input_closure_forwarder (ffi_cif *cif, void *ret, void **args, void *user_data)
+input_rl__forward (ffi_cif *cif, void *ret, void **args, void *user_data)
{
(void) cif;
- struct input_fn_data *data = user_data;
+ struct input_rl_fn *data = user_data;
if (!data->callback
(*(int *) args[0], UNMETA (*(int *) args[1]), data->user_data))
rl_ding ();
@@ -319,13 +413,14 @@ input_closure_forwarder (ffi_cif *cif, void *ret, void **args, void *user_data)
}
static void
-input_add_fn (struct input *self,
+input_rl_register_fn (void *input,
const char *name, const char *help, input_fn callback, void *user_data)
{
+ struct input_rl *self = input;
(void) help;
void *bound_fn = NULL;
- struct input_fn_data *data = ffi_closure_alloc (sizeof *data, &bound_fn);
+ struct input_rl_fn *data = ffi_closure_alloc (sizeof *data, &bound_fn);
hard_assert (data);
static ffi_cif cif;
@@ -337,7 +432,7 @@ input_add_fn (struct input *self,
data->callback = callback;
data->user_data = user_data;
hard_assert (ffi_prep_closure_loc (&data->closure,
- &cif, input_closure_forwarder, data, bound_fn) == FFI_OK);
+ &cif, input_rl__forward, data, bound_fn) == FFI_OK);
rl_add_defun (name, (rl_command_func_t *) bound_fn, -1);
LIST_PREPEND (self->fns, data);
@@ -350,8 +445,9 @@ static void on_readline_input (char *line);
static char **app_readline_completion (const char *text, int start, int end);
static void
-input_start (struct input *self, const char *program_name)
+input_rl_start (void *input, const char *program_name)
{
+ struct input_rl *self = input;
using_history ();
// This can cause memory leaks, or maybe even a segfault. Funny, eh?
stifle_history (HISTORY_LIMIT);
@@ -373,10 +469,11 @@ input_start (struct input *self, const char *program_name)
}
static void
-input_stop (struct input *self)
+input_rl_stop (void *input)
{
+ struct input_rl *self = input;
if (self->prompt_shown > 0)
- input_erase (self);
+ input_rl__erase (self);
// This is okay as long as we're not called from within readline
rl_callback_handler_remove ();
@@ -384,13 +481,23 @@ input_stop (struct input *self)
self->prompt_shown = false;
}
+static void
+input_rl_prepare (void *input, bool enabled)
+{
+ (void) input;
+ if (enabled)
+ rl_prep_terminal (true);
+ else
+ rl_deprep_terminal ();
+}
+
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// 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)
+input_rl__save_buffer (struct input_rl *self, struct input_rl_buffer *buffer)
{
(void) self;
@@ -405,7 +512,7 @@ input_save_buffer (struct input *self, struct input_buffer *buffer)
}
static void
-input_restore_buffer (struct input *self, struct input_buffer *buffer)
+input_rl__restore_buffer (struct input_rl *self, struct input_rl_buffer *buffer)
{
if (buffer->history)
{
@@ -442,8 +549,10 @@ input_restore_buffer (struct input *self, struct input_buffer *buffer)
}
static void
-input_switch_buffer (struct input *self, struct input_buffer *buffer)
+input_rl_buffer_switch (void *input, input_buffer_t input_buffer)
{
+ struct input_rl *self = input;
+ struct input_rl_buffer *buffer = input_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
@@ -452,7 +561,7 @@ input_switch_buffer (struct input *self, struct input_buffer *buffer)
// Save this buffer's history so that it's independent for each buffer
if (self->current)
- input_save_buffer (self, self->current);
+ input_rl__save_buffer (self, self->current);
else
// Just throw it away; there should always be an active buffer however
#if RL_READLINE_VERSION >= 0x0603
@@ -462,21 +571,30 @@ input_switch_buffer (struct input *self, struct input_buffer *buffer)
clear_history ();
#endif // RL_READLINE_VERSION < 0x0603
- input_restore_buffer (self, buffer);
+ input_rl__restore_buffer (self, buffer);
self->current = buffer;
}
static void
-input_destroy_buffer (struct input *self, struct input_buffer *buffer)
+input_rl__buffer_destroy_wo_history (struct input_rl_buffer *self)
{
- (void) self;
+ free (self->history);
+ free (self->saved_line);
+ free (self);
+}
+
+static void
+input_rl_buffer_destroy (void *input, input_buffer_t input_buffer)
+{
+ (void) input;
+ struct input_rl_buffer *buffer = input_buffer;
// 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 input_switch_buffer() for why we need to do this BS
+ // See input_rl_buffer_switch() for why we need to do this BS
rl_free_undo_list ();
// This is probably the only way we can free the history fully
@@ -495,13 +613,25 @@ input_destroy_buffer (struct input *self, struct input_buffer *buffer)
}
#endif // RL_READLINE_VERSION
- input_buffer_destroy (buffer);
+ input_rl__buffer_destroy_wo_history (buffer);
+}
+
+static input_buffer_t
+input_rl_buffer_new (void *input)
+{
+ (void) input;
+ struct input_rl_buffer *self = xcalloc (1, sizeof *self);
+ return self;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+// Since {save,restore}_buffer() store history, we can't use them here like we
+// do with libedit, because then buffer_destroy() can free memory that's still
+// being used by readline. This situation is bound to happen on quit.
+
static void
-input_save (struct input *self)
+input_rl__save (struct input_rl *self)
{
hard_assert (!self->saved_line);
@@ -511,7 +641,7 @@ input_save (struct input *self)
}
static void
-input_restore (struct input *self)
+input_rl__restore (struct input_rl *self)
{
hard_assert (self->saved_line);
@@ -526,100 +656,146 @@ input_restore (struct input *self)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static void
-input_hide (struct input *self)
+input_rl_hide (void *input)
{
+ struct input_rl *self = input;
if (!self->active || self->prompt_shown-- < 1)
return;
- input_save (self);
- input_erase (self);
+ input_rl__save (self);
+ input_rl__erase (self);
}
static void
-input_show (struct input *self)
+input_rl_show (void *input)
{
+ struct input_rl *self = input;
if (!self->active || ++self->prompt_shown < 1)
return;
- input_restore (self);
+ input_rl__restore (self);
rl_redisplay ();
}
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+static void
+input_rl_on_tty_resized (void *input)
+{
+ (void) input;
+ // 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_rl_on_tty_readable (void *input)
+{
+ (void) input;
+ rl_callback_read_char ();
+}
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+static void
+input_rl_destroy (void *input)
+{
+ struct input_rl *self = input;
+ free (self->saved_line);
+ LIST_FOR_EACH (struct input_rl_fn, iter, self->fns)
+ ffi_closure_free (iter);
+ free (self->prompt);
+ free (self);
+}
+
+#define XX(a) .a = input_rl_ ## a,
+static struct input_vtable input_rl_vtable = { INPUT_VTABLE (XX) };
+#undef XX
+
+static struct input *
+input_rl_new (void)
+{
+ struct input_rl *self = xcalloc (1, sizeof *self);
+ self->super.vtable = &input_rl_vtable;
+ return &self->super;
+}
+
+#define input_new input_rl_new
#endif // HAVE_READLINE
// --- BSD Editline ------------------------------------------------------------
#ifdef HAVE_EDITLINE
+#include <histedit.h>
+
#define INPUT_START_IGNORE '\x01'
#define INPUT_END_IGNORE '\x01'
-static void app_editline_init (struct input *self);
-
-static void
-input_ding (struct input *self)
+struct input_el_fn
{
- (void) self;
+ ffi_closure closure; ///< Closure
- // XXX: this isn't probably very portable;
- // we could use "bell" from terminfo but that creates a dependency
- write (STDOUT_FILENO, "\a", 1);
-}
+ LIST_HEADER (struct input_el_fn)
+ input_fn callback; ///< Real callback
+ void *user_data; ///< Real callback user data
-static void
-input_on_terminal_resized (struct input *self)
-{
- el_resize (self->editline);
-}
+ wchar_t *name; ///< Function name
+ wchar_t *help; ///< Function help
+};
-static void
-input_bind (struct input *self, const char *seq, const char *function_name)
+struct input_el_buffer
{
- el_set (self->editline, EL_BIND, seq, function_name, NULL);
-}
+ HistoryW *history; ///< The history object
+ wchar_t *saved_line; ///< Saved line content
+ int saved_len; ///< Length of the saved line
+ int saved_point; ///< Saved cursor position
+};
-static void
-input_bind_meta (struct input *self, char key, const char *function_name)
+struct input_el
{
- char keyseq[] = { 'M', '-', key, 0 };
- input_bind (self, keyseq, function_name);
-}
+ struct input super; ///< Parent class
+ EditLine *editline; ///< The EditLine object
-static void
-input_bind_control (struct input *self, char key, const char *function_name)
+ bool active; ///< Are we a thing?
+ char *prompt; ///< The prompt we use
+ int prompt_shown; ///< Whether the prompt is shown now
+
+ struct input_el_fn *fns; ///< Named functions
+ struct input_el_buffer *current; ///< Current input buffer
+};
+
+static void app_editline_init (struct input_el *self);
+
+static int
+input_el__get_termios (int character, int fallback)
{
- char keyseq[] = { '^', key, 0 };
- input_bind (self, keyseq, function_name);
+ if (!g_terminal.initialized)
+ return fallback;
+
+ cc_t value = g_terminal.termios.c_cc[character];
+ if (value == _POSIX_VDISABLE)
+ return fallback;
+ return value;
}
static void
-input_redisplay (struct input *self)
+input_el__redisplay (void *input)
{
// See rl_redisplay()
- // The character is VREPRINT (usually C-r)
- // TODO: read it from terminal info
- // XXX: could we potentially break UTF-8 with this?
- char x[] = { ('R' - 'A' + 1), 0 };
+ struct input_el *self = input;
+ char x[] = { input_el__get_termios (VREPRINT, 'R' - 0x40), 0 };
el_push (self->editline, x);
// We have to do this or it gets stuck and nothing is done
(void) el_gets (self->editline, NULL);
}
-static void
-input_set_prompt (struct input *self, char *prompt)
-{
- free (self->prompt);
- self->prompt = prompt;
-
- if (self->prompt_shown > 0)
- input_redisplay (self);
-}
-
static char *
-input_make_prompt (EditLine *editline)
+input_el__make_prompt (EditLine *editline)
{
- struct input *self;
+ struct input_el *self;
el_get (editline, EL_CLIENTDATA, &self);
if (!self->prompt)
return "";
@@ -627,42 +803,74 @@ input_make_prompt (EditLine *editline)
}
static char *
-input_make_empty_prompt (EditLine *editline)
+input_el__make_empty_prompt (EditLine *editline)
{
(void) editline;
return "";
}
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
static void
-input_erase_content (struct input *self)
+input_el_ding (void *input)
+{
+ // XXX: this isn't probably very portable;
+ // we could use "bell" from terminfo but that creates a dependency
+ (void) input;
+ write (STDOUT_FILENO, "\a", 1);
+}
+
+static const char *
+input_el_get_prompt (void *input)
{
+ struct input_el *self = input;
+ return self->prompt;
+}
+
+static void
+input_el_set_prompt (void *input, char *prompt)
+{
+ struct input_el *self = input;
+ free (self->prompt);
+ self->prompt = prompt;
+
+ if (self->prompt_shown > 0)
+ input_el__redisplay (self);
+}
+
+static void
+input_el_clear_line (void *input)
+{
+ struct input_el *self = input;
const LineInfoW *info = el_wline (self->editline);
int len = info->lastchar - info->buffer;
int point = info->cursor - info->buffer;
el_cursor (self->editline, len - point);
el_wdeletestr (self->editline, len);
- input_redisplay (self);
+ input_el__redisplay (self);
}
static void
-input_erase (struct input *self)
+input_el__erase (struct input_el *self)
{
- el_set (self->editline, EL_PROMPT, input_make_empty_prompt);
- input_erase_content (self);
+ el_set (self->editline, EL_PROMPT, input_el__make_empty_prompt);
+ input_el_clear_line (self);
}
static bool
-input_insert (struct input *self, const char *s)
+input_el_insert (void *input, const char *s)
{
+ struct input_el *self = input;
bool success = !*s || !el_insertstr (self->editline, s);
if (self->prompt_shown > 0)
- input_redisplay (self);
+ input_el__redisplay (self);
return success;
}
static char *
-input_get_content (struct input *self)
+input_el_get_line (void *input)
{
+ struct input_el *self = input;
const LineInfo *info = el_line (self->editline);
return xstrndup (info->buffer, info->lastchar - info->buffer);
}
@@ -670,11 +878,32 @@ input_get_content (struct input *self)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static void
-input_closure_forwarder (ffi_cif *cif, void *ret, void **args, void *user_data)
+input_el_bind (void *input, const char *seq, const char *function_name)
+{
+ struct input_el *self = input;
+ el_set (self->editline, EL_BIND, seq, function_name, NULL);
+}
+
+static void
+input_el_bind_meta (void *input, char key, const char *function_name)
+{
+ char keyseq[] = { 'M', '-', key, 0 };
+ input_el_bind (input, keyseq, function_name);
+}
+
+static void
+input_el_bind_control (void *input, char key, const char *function_name)
+{
+ char keyseq[] = { '^', key, 0 };
+ input_el_bind (input, keyseq, function_name);
+}
+
+static void
+input_el__forward (ffi_cif *cif, void *ret, void **args, void *user_data)
{
(void) cif;
- struct input_fn_data *data = user_data;
+ struct input_el_fn *data = user_data;
*(unsigned char *) ret = data->callback
(1, *(int *) args[1], data->user_data) ? CC_NORM : CC_ERROR;
}
@@ -690,11 +919,11 @@ ascii_to_wide (const char *ascii)
}
static void
-input_add_fn (struct input *self,
+input_el_register_fn (void *input,
const char *name, const char *help, input_fn callback, void *user_data)
{
void *bound_fn = NULL;
- struct input_fn_data *data = ffi_closure_alloc (sizeof *data, &bound_fn);
+ struct input_el_fn *data = ffi_closure_alloc (sizeof *data, &bound_fn);
hard_assert (data);
static ffi_cif cif;
@@ -707,8 +936,9 @@ input_add_fn (struct input *self,
data->name = ascii_to_wide (name);
data->help = ascii_to_wide (help);
hard_assert (ffi_prep_closure_loc (&data->closure,
- &cif, input_closure_forwarder, data, bound_fn) == FFI_OK);
+ &cif, input_el__forward, data, bound_fn) == FFI_OK);
+ struct input_el *self = input;
el_wset (self->editline, EL_ADDFN, data->name, data->help, bound_fn);
LIST_PREPEND (self->fns, data);
}
@@ -716,12 +946,13 @@ input_add_fn (struct input *self,
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static void
-input_start (struct input *self, const char *program_name)
+input_el_start (void *input, const char *program_name)
{
+ struct input_el *self = input;
self->editline = el_init (program_name, stdin, stdout, stderr);
el_set (self->editline, EL_CLIENTDATA, self);
el_set (self->editline, EL_PROMPT_ESC,
- input_make_prompt, INPUT_START_IGNORE);
+ input_el__make_prompt, INPUT_START_IGNORE);
el_set (self->editline, EL_SIGNAL, false);
el_set (self->editline, EL_UNBUFFERED, true);
el_set (self->editline, EL_EDITOR, "emacs");
@@ -733,10 +964,11 @@ input_start (struct input *self, const char *program_name)
}
static void
-input_stop (struct input *self)
+input_el_stop (void *input)
{
+ struct input_el *self = input;
if (self->prompt_shown > 0)
- input_erase (self);
+ input_el__erase (self);
el_end (self->editline);
self->editline = NULL;
@@ -744,10 +976,17 @@ input_stop (struct input *self)
self->prompt_shown = false;
}
+static void
+input_el_prepare (void *input, bool enabled)
+{
+ struct input_el *self = input;
+ el_set (self->editline, EL_PREP_TERM, enabled);
+}
+
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static void
-input_save_buffer (struct input *self, struct input_buffer *buffer)
+input_el__save_buffer (struct input_el *self, struct input_el_buffer *buffer)
{
const LineInfoW *info = el_wline (self->editline);
int len = info->lastchar - info->buffer;
@@ -764,7 +1003,14 @@ input_save_buffer (struct input *self, struct input_buffer *buffer)
}
static void
-input_restore_buffer (struct input *self, struct input_buffer *buffer)
+input_el__save (struct input_el *self)
+{
+ if (self->current)
+ input_el__save_buffer (self, self->current);
+}
+
+static void
+input_el__restore_buffer (struct input_el *self, struct input_el_buffer *buffer)
{
if (buffer->saved_line)
{
@@ -777,70 +1023,92 @@ input_restore_buffer (struct input *self, struct input_buffer *buffer)
}
static void
-input_switch_buffer (struct input *self, struct input_buffer *buffer)
+input_el__restore (struct input_el *self)
{
if (self->current)
- input_save_buffer (self, self->current);
-
- input_restore_buffer (self, buffer);
- el_wset (self->editline, EL_HIST, history, buffer->history);
- self->current = buffer;
-}
-
-static void
-input_destroy_buffer (struct input *self, struct input_buffer *buffer)
-{
- (void) self;
- input_buffer_destroy (buffer);
+ input_el__restore_buffer (self, self->current);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static void
-input_save (struct input *self)
+input_el_buffer_switch (void *input, input_buffer_t input_buffer)
{
+ struct input_el *self = input;
+ struct input_el_buffer *buffer = input_buffer;
+
if (self->current)
- input_save_buffer (self, self->current);
+ input_el__save_buffer (self, self->current);
+
+ input_el__restore_buffer (self, buffer);
+ el_wset (self->editline, EL_HIST, history, buffer->history);
+ self->current = buffer;
}
static void
-input_restore (struct input *self)
+input_el_buffer_destroy (void *input, input_buffer_t input_buffer)
{
- if (self->current)
- input_restore_buffer (self, self->current);
+ (void) input;
+ struct input_el_buffer *buffer = input_buffer;
+
+ history_wend (buffer->history);
+ free (buffer->saved_line);
+ free (buffer);
+}
+
+static input_buffer_t
+input_el_buffer_new (void *input)
+{
+ (void) input;
+ struct input_el_buffer *self = xcalloc (1, sizeof *self);
+ self->history = history_winit ();
+
+ HistEventW ev;
+ history_w (self->history, &ev, H_SETSIZE, HISTORY_LIMIT);
+ return self;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static void
-input_hide (struct input *self)
+input_el_hide (void *input)
{
+ struct input_el *self = input;
if (!self->active || self->prompt_shown-- < 1)
return;
- input_save (self);
- input_erase (self);
+ input_el__save (self);
+ input_el__erase (self);
}
static void
-input_show (struct input *self)
+input_el_show (void *input)
{
+ struct input_el *self = input;
if (!self->active || ++self->prompt_shown < 1)
return;
- input_restore (self);
+ input_el__restore (self);
// XXX: the ignore doesn't quite work, see https://gnats.netbsd.org/47539
el_set (self->editline,
- EL_PROMPT_ESC, input_make_prompt, INPUT_START_IGNORE);
- input_redisplay (self);
+ EL_PROMPT_ESC, input_el__make_prompt, INPUT_START_IGNORE);
+ input_el__redisplay (self);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static void
-input_on_readable (struct input *self)
+input_el_on_tty_resized (void *input)
+{
+ struct input_el *self = input;
+ el_resize (self->editline);
+}
+
+static void
+input_el_on_tty_readable (void *input)
{
// We bind the return key to process it how we need to
+ struct input_el *self = input;
// el_gets() with EL_UNBUFFERED doesn't work with UTF-8,
// we must use the wide-character interface
@@ -849,16 +1117,43 @@ input_on_readable (struct input *self)
if (!buf || count-- <= 0)
return;
- // The character is VEOF (usually C-d)
- // TODO: read it from terminal info
- if (count == 0 && buf[0] == ('D' - 'A' + 1))
+ if (count == 0 && buf[0] == ('D' - 0x40) /* hardcoded VEOF in editline */)
{
el_deletestr (self->editline, 1);
- input_redisplay (self);
- input_ding (self);
+ input_el__redisplay (self);
+ input_el_ding (self);
}
}
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+static void
+input_el_destroy (void *input)
+{
+ struct input_el *self = input;
+ LIST_FOR_EACH (struct input_el_fn, iter, self->fns)
+ {
+ free (iter->name);
+ free (iter->help);
+ ffi_closure_free (iter);
+ }
+ free (self->prompt);
+ free (self);
+}
+
+#define XX(a) .a = input_el_ ## a,
+static struct input_vtable input_el_vtable = { INPUT_VTABLE (XX) };
+#undef XX
+
+static struct input *
+input_el_new (void)
+{
+ struct input_el *self = xcalloc (1, sizeof *self);
+ self->super.vtable = &input_el_vtable;
+ return &self->super;
+}
+
+#define input_new input_el_new
#endif // HAVE_EDITLINE
// --- Application data --------------------------------------------------------
@@ -1198,7 +1493,8 @@ struct buffer
enum buffer_type type; ///< Type of the buffer
char *name; ///< The name of the buffer
- struct input_buffer *input_data; ///< User interface data
+ struct input *input; ///< API for "input_data"
+ input_buffer_t input_data; ///< User interface data
// Buffer contents:
@@ -1220,11 +1516,12 @@ struct buffer
};
static struct buffer *
-buffer_new (void)
+buffer_new (struct input *input)
{
struct buffer *self = xcalloc (1, sizeof *self);
self->ref_count = 1;
- self->input_data = input_buffer_new ();
+ self->input = input;
+ self->input_data = CALL (input, buffer_new);
return self;
}
@@ -1233,7 +1530,15 @@ buffer_destroy (struct buffer *self)
{
free (self->name);
if (self->input_data)
- input_buffer_destroy (self->input_data);
+ {
+#ifdef HAVE_READLINE
+ // FIXME: can't really free "history" contents from here, as we cannot
+ // be sure that the user interface pointer is valid and usable
+ input_rl__buffer_destroy_wo_history (self->input_data);
+#else // ! HAVE_READLINE
+ CALL_ (self->input, buffer_destroy, self->input_data);
+#endif // ! HAVE_READLINE
+ }
LIST_FOR_EACH (struct buffer_line, iter, self->lines)
buffer_line_destroy (iter);
if (self->log_file)
@@ -1667,7 +1972,7 @@ struct app_context
iconv_t term_from_utf8; ///< UTF-8 to terminal encoding
iconv_t latin1_to_utf8; ///< ISO Latin 1 to UTF-8
- struct input input; ///< User interface
+ struct input *input; ///< User interface
struct poller_idle input_event; ///< Pending input event
struct str_vector pending_input; ///< Pending input lines
@@ -1751,7 +2056,7 @@ app_context_init (struct app_context *self)
free (encoding);
- input_init (&self->input);
+ self->input = input_new ();
str_vector_init (&self->pending_input);
str_init (&self->input_buffer);
@@ -1776,7 +2081,8 @@ app_context_free (struct app_context *self)
LIST_FOR_EACH (struct buffer, iter, self->buffers)
{
#ifdef HAVE_READLINE
- input_destroy_buffer (&self->input, iter->input_data);
+ // We can use the user interface here; see buffer_destroy()
+ CALL_ (self->input, buffer_destroy, iter->input_data);
iter->input_data = NULL;
#endif // HAVE_READLINE
buffer_unref (iter);
@@ -1790,7 +2096,7 @@ app_context_free (struct app_context *self)
iconv_close (self->term_from_utf8);
iconv_close (self->term_to_utf8);
- input_free (&self->input);
+ CALL (self->input, destroy);
str_vector_free (&self->pending_input);
str_free (&self->input_buffer);
@@ -2198,102 +2504,6 @@ serialize_configuration (struct config_item *root, struct str *output)
#define COLOR_256(name, c256) \
(((COLOR_ ## name) & 0xFFFF) | (((c256) & 0xFFFF) << 16))
-static struct
-{
- bool initialized; ///< Terminal is available
- bool stdout_is_tty; ///< `stdout' is a terminal
- bool stderr_is_tty; ///< `stderr' is a terminal
-
- char *color_set_fg[256]; ///< Codes to set the foreground colour
- char *color_set_bg[256]; ///< Codes to set the background colour
-
- int lines; ///< Number of lines
- int columns; ///< Number of columns
-}
-g_terminal;
-
-static void
-update_screen_size (void)
-{
-#ifdef TIOCGWINSZ
- if (!g_terminal.stdout_is_tty)
- return;
-
- struct winsize size;
- if (!ioctl (STDOUT_FILENO, TIOCGWINSZ, (char *) &size))
- {
- char *row = getenv ("LINES");
- char *col = getenv ("COLUMNS");
- unsigned long tmp;
- g_terminal.lines =
- (row && xstrtoul (&tmp, row, 10)) ? tmp : size.ws_row;
- g_terminal.columns =
- (col && xstrtoul (&tmp, col, 10)) ? tmp : size.ws_col;
- }
-#endif // TIOCGWINSZ
-}
-
-static bool
-init_terminal (void)
-{
- int tty_fd = -1;
- if ((g_terminal.stderr_is_tty = isatty (STDERR_FILENO)))
- tty_fd = STDERR_FILENO;
- if ((g_terminal.stdout_is_tty = isatty (STDOUT_FILENO)))
- tty_fd = STDOUT_FILENO;
-
- int err;
- if (tty_fd == -1 || setupterm (NULL, tty_fd, &err) == ERR)
- return false;
-
- // Make sure all terminal features used by us are supported
- if (!set_a_foreground || !set_a_background
- || !enter_bold_mode || !exit_attribute_mode)
- {
- del_curterm (cur_term);
- return false;
- }
-
- // Make sure newlines are output correctly
- struct termios termios;
- if (!tcgetattr (tty_fd, &termios))
- {
- termios.c_oflag |= ONLCR;
- (void) tcsetattr (tty_fd, TCSADRAIN, &termios);
- }
-
- g_terminal.lines = tigetnum ("lines");
- g_terminal.columns = tigetnum ("cols");
- update_screen_size ();
-
- int max = MIN (256, max_colors);
- for (int i = 0; i < max; i++)
- {
- g_terminal.color_set_fg[i] = xstrdup (tparm (set_a_foreground,
- i, 0, 0, 0, 0, 0, 0, 0, 0));
- g_terminal.color_set_bg[i] = xstrdup (tparm (set_a_background,
- i, 0, 0, 0, 0, 0, 0, 0, 0));
- }
-
- return g_terminal.initialized = true;
-}
-
-static void
-free_terminal (void)
-{
- if (!g_terminal.initialized)
- return;
-
- for (int i = 0; i < 256; i++)
- {
- free (g_terminal.color_set_fg[i]);
- free (g_terminal.color_set_bg[i]);
- }
- del_curterm (cur_term);
-}
-
-// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
typedef int (*terminal_printer_fn) (int);
static int
@@ -2346,13 +2556,13 @@ log_message_attributed (void *user_data, const char *quote, const char *fmt,
FILE *stream = stderr;
struct app_context *ctx = g_ctx;
- input_hide (&ctx->input);
+ CALL (ctx->input, hide);
print_attributed (ctx, stream, (intptr_t) user_data, "%s", quote);
vprint_attributed (ctx, stream, (intptr_t) user_data, fmt, ap);
fputs ("\n", stream);
- input_show (&ctx->input);
+ CALL (ctx->input, show);
}
static ssize_t
@@ -3216,11 +3426,11 @@ buffer_line_display (struct app_context *ctx,
FORMATTER_ADD_ITEM (&f, IGNORE_ATTR, .attribute = 1);
}
- input_hide (&ctx->input);
+ CALL (ctx->input, hide);
buffer_line_flush (line, &f, stdout, false);
// Flush the trailing formatting reset item
fflush (stdout);
- input_show (&ctx->input);
+ CALL (ctx->input, show);
}
static void
@@ -3289,7 +3499,7 @@ log_formatter (struct app_context *ctx,
&& !(flags & BUFFER_LINE_UNIMPORTANT);
bool important = (flags & BUFFER_LINE_HIGHLIGHT) || unseen_pm;
if (ctx->beep_on_highlight && important)
- input_ding (&ctx->input);
+ CALL (ctx->input, ding);
bool can_leak = false;
if ((buffer == ctx->global_buffer)
@@ -3487,7 +3697,7 @@ buffer_remove (struct app_context *ctx, struct buffer *buffer)
hard_assert (buffer != ctx->current_buffer);
hard_assert (buffer != ctx->global_buffer);
- input_destroy_buffer (&ctx->input, buffer->input_data);
+ CALL_ (ctx->input, buffer_destroy, buffer->input_data);
buffer->input_data = NULL;
// And make sure to unlink the buffer from "irc_buffer_map"
@@ -3525,7 +3735,7 @@ static void
buffer_print_backlog (struct app_context *ctx, struct buffer *buffer)
{
// The prompt can take considerable time to redraw
- input_hide (&ctx->input);
+ CALL (ctx->input, hide);
// That is, minus the readline prompt
int display_limit = MAX (10, g_terminal.lines - 1);
@@ -3575,7 +3785,7 @@ buffer_print_backlog (struct app_context *ctx, struct buffer *buffer)
buffer_update_time (ctx, time (NULL));
refresh_prompt (ctx);
- input_show (&ctx->input);
+ CALL (ctx->input, show);
}
static void
@@ -3585,7 +3795,7 @@ buffer_activate (struct app_context *ctx, struct buffer *buffer)
return;
buffer_print_backlog (ctx, buffer);
- input_switch_buffer (&ctx->input, buffer->input_data);
+ CALL_ (ctx->input, buffer_switch, buffer->input_data);
// Now at last we can switch the pointers
ctx->last_buffer = ctx->current_buffer;
@@ -3736,7 +3946,7 @@ buffer_remove_safe (struct app_context *ctx, struct buffer *buffer)
static void
init_global_buffer (struct app_context *ctx)
{
- struct buffer *global = ctx->global_buffer = buffer_new ();
+ struct buffer *global = ctx->global_buffer = buffer_new (ctx->input);
global->type = BUFFER_GLOBAL;
global->name = xstrdup (PROGRAM_NAME);
@@ -3786,7 +3996,7 @@ irc_get_or_make_user_buffer (struct server *s, const char *nickname)
struct user *user = irc_get_or_make_user (s, nickname);
// Open a new buffer for the user
- buffer = buffer_new ();
+ buffer = buffer_new (s->ctx->input);
buffer->type = BUFFER_PM;
buffer->name = xstrdup_printf ("%s.%s", s->name, nickname);
buffer->server = s;
@@ -4202,7 +4412,7 @@ initiate_quit (struct app_context *ctx)
log_global_status (ctx, "Shutting down");
// Hide the user interface
- input_hide (&ctx->input);
+ CALL (ctx->input, hide);
// Initiate a connection close
struct str_map_iter iter;
@@ -5281,10 +5491,11 @@ static void
input_maybe_set_prompt (struct input *self, char *new_prompt)
{
// Redisplay can be an expensive operation
- if (self->prompt && !strcmp (new_prompt, self->prompt))
+ const char *prompt = CALL (self, get_prompt);
+ if (prompt && !strcmp (new_prompt, prompt))
free (new_prompt);
else
- input_set_prompt (self, new_prompt);
+ CALL_ (self, set_prompt, new_prompt);
}
static void
@@ -5302,7 +5513,7 @@ refresh_prompt (struct app_context *ctx)
if (have_attributes)
{
// XXX: to be completely correct, we should use tputs, but we cannot
- input_maybe_set_prompt (&ctx->input, xstrdup_printf ("%c%s%c%s%c%s%c",
+ input_maybe_set_prompt (ctx->input, xstrdup_printf ("%c%s%c%s%c%s%c",
INPUT_START_IGNORE, ctx->attrs[ATTR_PROMPT],
INPUT_END_IGNORE,
localized,
@@ -5311,7 +5522,7 @@ refresh_prompt (struct app_context *ctx)
free (localized);
}
else
- input_maybe_set_prompt (&ctx->input, localized);
+ input_maybe_set_prompt (ctx->input, localized);
}
// --- Helpers -----------------------------------------------------------------
@@ -5829,7 +6040,7 @@ irc_handle_join (struct server *s, const struct irc_message *msg)
// We've joined a new channel
if (!channel && irc_is_this_us (s, msg->prefix))
{
- buffer = buffer_new ();
+ buffer = buffer_new (s->ctx->input);
buffer->type = BUFFER_CHANNEL;
buffer->name = xstrdup_printf ("%s.%s", s->name, channel_name);
buffer->server = s;
@@ -7462,7 +7673,7 @@ server_add (struct app_context *ctx,
s->config = subtree;
// Add a buffer and activate it
- struct buffer *buffer = s->buffer = buffer_new ();
+ struct buffer *buffer = s->buffer = buffer_new (ctx->input);
buffer->type = BUFFER_SERVER;
buffer->name = xstrdup (s->name);
buffer->server = s;
@@ -11241,15 +11452,11 @@ suspend_terminal (struct app_context *ctx)
if (ctx->terminal_suspended++ > 0)
return;
-#ifdef HAVE_READLINE
- rl_deprep_terminal ();
-#elif defined (HAVE_EDITLINE)
- el_set (ctx->input.editline, EL_PREP_TERM, 0);
-#endif
-
toggle_bracketed_paste (false);
- input_hide (&ctx->input);
+ CALL (ctx->input, hide);
poller_fd_reset (&ctx->tty_event);
+
+ CALL_ (ctx->input, prepare, false);
}
static void
@@ -11259,18 +11466,14 @@ resume_terminal (struct app_context *ctx)
return;
update_screen_size ();
-#ifdef HAVE_READLINE
- rl_prep_terminal (true);
-#elif defined (HAVE_EDITLINE)
- el_set (ctx->input.editline, EL_PREP_TERM, 1);
-#endif
+ CALL_ (ctx->input, prepare, true);
toggle_bracketed_paste (true);
// In theory we could just print all unseen messages but this is safer
buffer_print_backlog (ctx, ctx->current_buffer);
// Now it's safe to process any user input
poller_fd_set (&ctx->tty_event, POLLIN);
- input_show (&ctx->input);
+ CALL (ctx->input, show);
}
static pid_t
@@ -11302,15 +11505,13 @@ spawn_helper_child (struct app_context *ctx)
static void
redraw_screen (struct app_context *ctx)
{
- input_hide (&ctx->input);
-
// If by some circumstance we had the wrong idea
- input_on_terminal_resized (&ctx->input);
+ CALL (ctx->input, on_tty_resized);
update_screen_size ();
+ CALL (ctx->input, hide);
buffer_print_backlog (ctx, ctx->current_buffer);
-
- input_show (&ctx->input);
+ CALL (ctx->input, show);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@@ -11325,7 +11526,7 @@ dump_input_to_file (struct app_context *ctx, char *template, struct error **e)
if (fd < 0)
FAIL ("%s", strerror (errno));
- char *input = input_get_content (&ctx->input);
+ char *input = CALL (ctx->input, get_line);
bool success = xwrite (fd, input, strlen (input), e);
free (input);
@@ -11402,9 +11603,9 @@ input_editor_process (struct app_context *ctx)
error_free (e);
}
else
- input_erase_content (&ctx->input);
+ CALL (ctx->input, clear_line);
- if (!input_insert (&ctx->input, input.str))
+ if (!CALL_ (ctx->input, insert, input.str))
log_global_error (ctx, "#s: #s", "Input editing failed",
"could not re-insert the modified text");
@@ -11589,8 +11790,8 @@ on_start_paste_mode (int count, int key, void *user_data)
static void
bind_common_keys (struct app_context *ctx)
{
- struct input *self = &ctx->input;
-#define XX(...) input_add_fn (self, __VA_ARGS__, ctx);
+ struct input *self = ctx->input;
+#define XX(...) CALL_ (self, register_fn, __VA_ARGS__, ctx);
XX ("previous-buffer", "Previous buffer", on_previous_buffer)
XX ("next-buffer", "Next buffer", on_next_buffer)
XX ("goto-buffer", "Go to buffer", on_goto_buffer)
@@ -11603,26 +11804,26 @@ bind_common_keys (struct app_context *ctx)
XX ("start-paste-mode", "Bracketed paste", on_start_paste_mode)
#undef XX
- input_bind_control (self, 'p', "previous-buffer");
- input_bind_control (self, 'n', "next-buffer");
+ CALL_ (self, bind_control, 'p', "previous-buffer");
+ CALL_ (self, bind_control, 'n', "next-buffer");
// Redefine M-0 through M-9 to switch buffers
for (int i = 0; i <= 9; i++)
- input_bind_meta (self, '0' + i, "goto-buffer");
+ CALL_ (self, bind_meta, '0' + i, "goto-buffer");
- input_bind_meta (self, '\t', "switch-buffer");
- input_bind_meta (self, 'm', "insert-attribute");
- input_bind_meta (self, 'h', "display-full-log");
- input_bind_meta (self, 'e', "edit-input");
+ CALL_ (self, bind_meta, '\t', "switch-buffer");
+ CALL_ (self, bind_meta, 'm', "insert-attribute");
+ CALL_ (self, bind_meta, 'h', "display-full-log");
+ CALL_ (self, bind_meta, 'e', "edit-input");
- if (key_f5) input_bind (self, key_f5, "previous-buffer");
- if (key_f6) input_bind (self, key_f6, "next-buffer");
- if (key_ppage) input_bind (self, key_ppage, "display-backlog");
+ if (key_f5) CALL_ (self, bind, key_f5, "previous-buffer");
+ if (key_f6) CALL_ (self, bind, key_f6, "next-buffer");
+ if (key_ppage) CALL_ (self, bind, key_ppage, "display-backlog");
if (clear_screen)
- input_bind_control (self, 'l', "redraw-screen");
+ CALL_ (self, bind_control, 'l', "redraw-screen");
- input_bind (self, "\x1b[200~", "start-paste-mode");
+ CALL_ (self, bind, "\x1b[200~", "start-paste-mode");
}
// --- GNU Readline user actions -----------------------------------------------
@@ -11638,10 +11839,12 @@ on_readline_return (int count, int key)
// Let readline pass the line to our input handler
rl_done = 1;
- // Hide the line, don't redisplay it
struct app_context *ctx = g_ctx;
- input_hide (&ctx->input);
- input_restore (&ctx->input);
+ struct input_rl *self = (struct input_rl *) ctx->input;
+
+ // Hide the line, don't redisplay it
+ CALL (ctx->input, hide);
+ input_rl__restore (self);
return 0;
}
@@ -11649,6 +11852,7 @@ static void
on_readline_input (char *line)
{
struct app_context *ctx = g_ctx;
+ struct input_rl *self = (struct input_rl *) ctx->input;
if (line)
{
@@ -11663,15 +11867,15 @@ on_readline_input (char *line)
else
{
// Prevent readline from showing the prompt twice for w/e reason
- input_hide (&ctx->input);
- input_restore (&ctx->input);
+ CALL (ctx->input, hide);
+ input_rl__restore (self);
- input_ding (&ctx->input);
+ CALL (ctx->input, ding);
}
- if (ctx->input.active)
+ if (self->active)
// Readline automatically redisplays it
- ctx->input.prompt_shown = 1;
+ self->prompt_shown = 1;
}
static char **
@@ -11690,7 +11894,7 @@ static int
app_readline_init (void)
{
struct app_context *ctx = g_ctx;
- struct input *self = &ctx->input;
+ struct input *self = ctx->input;
// XXX: maybe use rl_make_bare_keymap() and start from there;
// our dear user could potentionally rig things up in a way that might
@@ -11700,8 +11904,8 @@ app_readline_init (void)
bind_common_keys (ctx);
// Move native history commands
- input_bind_meta (self, 'p', "previous-history");
- input_bind_meta (self, 'n', "next-history");
+ CALL_ (self, bind_meta, 'p', "previous-history");
+ CALL_ (self, bind_meta, 'n', "next-history");
// We need to hide the prompt and input first
rl_bind_key (RETURN, rl_named_function ("send-line"));
@@ -11709,7 +11913,7 @@ app_readline_init (void)
rl_variable_bind ("completion-ignore-case", "on");
rl_bind_key (TAB, rl_named_function ("menu-complete"));
if (key_btab)
- input_bind (self, key_btab, "menu-complete-backward");
+ CALL_ (self, bind, key_btab, "menu-complete-backward");
return 0;
}
@@ -11775,6 +11979,7 @@ on_editline_return (EditLine *editline, int key)
{
(void) key;
struct app_context *ctx = g_ctx;
+ struct input_el *self = (struct input_el *) ctx->input;
const LineInfoW *info = el_wline (editline);
int len = info->lastchar - info->buffer;
@@ -11788,7 +11993,7 @@ on_editline_return (EditLine *editline, int key)
if (*line)
{
HistEventW ev;
- history_w (ctx->input.current->history, &ev, H_ENTER, line);
+ history_w (self->current->history, &ev, H_ENTER, line);
print_debug ("history: %d %ls", ev.num, ev.str);
}
free (line);
@@ -11805,7 +12010,7 @@ on_editline_return (EditLine *editline, int key)
}
static void
-app_editline_init (struct input *self)
+app_editline_init (struct input_el *self)
{
// el_set() leaks memory in 20150325 and other versions, we need wchar_t
el_wset (self->editline, EL_ADDFN,
@@ -11814,20 +12019,21 @@ app_editline_init (struct input *self)
L"complete", L"Complete word", on_editline_complete);
bind_common_keys (g_ctx);
+ struct input *input = &self->super;
// Move native history commands
- input_bind_meta (self, 'p', "ed-prev-history");
- input_bind_meta (self, 'n', "ed-next-history");
+ CALL_ (input, bind_meta, 'p', "ed-prev-history");
+ CALL_ (input, bind_meta, 'n', "ed-next-history");
// No, editline, it's not supposed to kill the entire line
- input_bind_control (self, 'w', "ed-delete-prev-word");
+ CALL_ (input, bind_control, 'w', "ed-delete-prev-word");
// Just what are you doing?
- input_bind_control (self, 'u', "vi-kill-line-prev");
+ CALL_ (input, bind_control, 'u', "vi-kill-line-prev");
// We need to hide the prompt and input first
- input_bind (self, "\n", "send-line");
+ CALL_ (input, bind, "\n", "send-line");
- input_bind_control (self, 'i', "complete");
+ CALL_ (input, bind_control, 'i', "complete");
// Source the user's defaults file
el_source (self->editline, NULL);
@@ -12157,14 +12363,14 @@ process_mirc_escape (const struct pollfd *fd, struct app_context *ctx)
goto error;
switch (buf->str[0])
{
- case 'b': input_insert (&ctx->input, "\x02"); break;
- case 'c': input_insert (&ctx->input, "\x03"); break;
+ case 'b': CALL_ (ctx->input, insert, "\x02"); break;
+ case 'c': CALL_ (ctx->input, insert, "\x03"); break;
case 'i':
- case ']': input_insert (&ctx->input, "\x1d"); break;
+ case ']': CALL_ (ctx->input, insert, "\x1d"); break;
case 'u':
- case '_': input_insert (&ctx->input, "\x1f"); break;
- case 'v': input_insert (&ctx->input, "\x16"); break;
- case 'o': input_insert (&ctx->input, "\x0f"); break;
+ case '_': CALL_ (ctx->input, insert, "\x1f"); break;
+ case 'v': CALL_ (ctx->input, insert, "\x16"); break;
+ case 'o': CALL_ (ctx->input, insert, "\x0f"); break;
default:
goto error;
@@ -12172,7 +12378,7 @@ process_mirc_escape (const struct pollfd *fd, struct app_context *ctx)
goto done;
error:
- input_ding (&ctx->input);
+ CALL (ctx->input, ding);
done:
str_reset (buf);
ctx->awaiting_mirc_escape = false;
@@ -12204,11 +12410,11 @@ process_bracketed_paste (const struct pollfd *fd, struct app_context *ctx)
(text_len = BRACKETED_PASTE_LIMIT));
buf->str[text_len] = '\0';
- if (input_insert (&ctx->input, buf->str))
+ if (CALL_ (ctx->input, insert, buf->str))
goto done;
error:
- input_ding (&ctx->input);
+ CALL (ctx->input, ding);
log_global_error (ctx, "Paste failed");
done:
str_reset (buf);
@@ -12278,7 +12484,7 @@ on_tty_readable (const struct pollfd *fd, struct app_context *ctx)
else if (ctx->in_bracketed_paste)
process_bracketed_paste (fd, ctx);
else if (!ctx->quitting)
- input_on_readable (&ctx->input);
+ CALL (ctx->input, on_tty_readable);
// User activity detected, stop current auto-away and start anew;
// since they might have just changed the settings, do this last
@@ -12573,7 +12779,7 @@ main (int argc, char *argv[])
// Initialize input so that we can switch to new buffers
refresh_prompt (&ctx);
- input_start (&ctx.input, argv[0]);
+ CALL_ (ctx.input, start, argv[0]);
toggle_bracketed_paste (true);
reset_autoaway (&ctx);
@@ -12585,7 +12791,7 @@ main (int argc, char *argv[])
while (ctx.polling)
poller_run (&ctx.poller);
- input_stop (&ctx.input);
+ CALL (ctx.input, stop);
if (get_config_boolean (ctx.config.root, "behaviour.save_on_quit"))
save_configuration (&ctx);