aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--degesch.c221
1 files changed, 175 insertions, 46 deletions
diff --git a/degesch.c b/degesch.c
index 7b9d141..1b2e817 100644
--- a/degesch.c
+++ b/degesch.c
@@ -187,6 +187,11 @@ struct buffer
enum buffer_type type; ///< Type of the buffer
char *name; ///< The name of the buffer
+ HISTORY_STATE *history; ///< Saved history state
+ char *saved_line; ///< Saved line
+ int saved_point; ///< Saved position in line
+ int saved_mark; ///< Saved mark
+
// Buffer contents:
struct buffer_line *lines; ///< All lines in this buffer
@@ -215,6 +220,8 @@ static void
buffer_destroy (struct buffer *self)
{
free (self->name);
+ // Can't really free "history" here
+ free (self->saved_line);
free (self->topic);
str_map_free (&self->nicks);
free (self);
@@ -361,6 +368,8 @@ app_context_free (struct app_context *self)
free (self->readline_prompt);
}
+static void refresh_prompt (struct app_context *ctx);
+
// --- Attributed output -------------------------------------------------------
static struct
@@ -417,12 +426,14 @@ 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_point = rl_mark;
state->saved_line = rl_copy_text (0, rl_end);
rl_set_prompt ("");
rl_replace_line ("", 0);
@@ -435,6 +446,7 @@ 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);
}
@@ -677,6 +689,12 @@ buffer_line_display (struct app_context *ctx, struct buffer_line *line)
char *object = iconv_xstrdup (ctx->term_from_utf8, line->object, -1, NULL);
char *reason = iconv_xstrdup (ctx->term_from_utf8, line->reason, -1, NULL);
+ // TODO: colorize the output, note that we shouldn't put everything through
+ // tputs but only the attribute strings. That might prove a bit
+ // challenging. Maybe we could create a helper object to pust text
+ // and formatting into. We could have a varargs function to make it a bit
+ // more friendly, e.g. push(&x, ATTR_JOIN, "--> ", ATTR_RESET, who, NULL)
+
switch (line->type)
{
case BUFFER_LINE_PRIVMSG:
@@ -724,12 +742,15 @@ buffer_line_display (struct app_context *ctx, struct buffer_line *line)
free (object);
free (reason);
- // TODO: hide readline if needed
+ struct app_readline_state state;
+ if (ctx->readline_prompt_shown)
+ app_readline_hide (&state);
printf ("%s\n", text.str);
str_free (&text);
- // TODO: unhide readline if hidden
+ if (ctx->readline_prompt_shown)
+ app_readline_restore (&state, ctx->readline_prompt);
}
static void
@@ -773,7 +794,7 @@ buffer_add (struct app_context *ctx, struct buffer *buffer)
str_map_set (&ctx->buffers_by_name, buffer->name, buffer);
LIST_APPEND_WITH_TAIL (ctx->buffers, ctx->buffers_tail, buffer);
- // TODO: refresh the prompt?
+ // TODO: refresh the prompt? Or caller?
}
static void
@@ -781,6 +802,21 @@ buffer_remove (struct app_context *ctx, struct buffer *buffer)
{
hard_assert (buffer != ctx->current_buffer);
+ 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);
+ rl_clear_history ();
+
+ history_set_history_state (state);
+ free (state);
+ }
+
str_map_set (&ctx->buffers_by_name, buffer->name, NULL);
LIST_UNLINK_WITH_TAIL (ctx->buffers, ctx->buffers_tail, buffer);
buffer_destroy (buffer);
@@ -792,22 +828,85 @@ buffer_remove (struct app_context *ctx, struct buffer *buffer)
if (buffer == ctx->server_buffer)
ctx->server_buffer = NULL;
- // TODO: refresh the prompt?
+ // TODO: refresh the prompt? Or caller?
}
static void
buffer_activate (struct app_context *ctx, struct buffer *buffer)
{
- ctx->current_buffer = buffer;
+ if (ctx->current_buffer == buffer)
+ return;
+
print_status ("%s", buffer->name);
// That is, minus the buffer switch line and the readline prompt
- int to_display = ctx->lines - 2;
- // TODO: find the to_display-th line from the back
- // TODO: print all the lines in order
+ 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);
+
+ // 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.
- // TODO: switch readline history stack
- // TODO: refresh the prompt?
+ // 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
+ rl_clear_history ();
+
+ // Now at last we can switch the pointers
+ ctx->current_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);
+
+ if (ctx->readline_prompt_shown)
+ rl_redisplay ();
+ }
+
+ // TODO: refresh the prompt? Or caller?
}
static void
@@ -1099,6 +1198,8 @@ irc_establish_connection (struct app_context *ctx,
return true;
}
+// --- More readline funky stuff -----------------------------------------------
+
static void
refresh_prompt (struct app_context *ctx)
{
@@ -1140,8 +1241,67 @@ refresh_prompt (struct app_context *ctx)
}
str_free (&prompt);
- // FIXME: when the program hasn't displayed the prompt yet, is this okay?
- rl_redisplay ();
+ // We need to be somehow able to initialize it
+ 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;
+
+ if (!(key & 0x80))
+ return 0;
+
+ int n = (key & 0x7F) - '0';
+ if (n < 0 || n > 9)
+ return 0;
+
+ // TODO: switch to the n-th buffer
+ return 0;
+}
+
+static int
+on_readline_previous_buffer (int count, int key)
+{
+ (void) key;
+
+ // TODO: switch "count" times to the previous buffer
+ return 0;
+}
+
+static int
+on_readline_next_buffer (int count, int key)
+{
+ (void) key;
+
+ // TODO: switch "count" times to the next buffer
+ return 0;
+}
+
+static int
+init_readline (void)
+{
+ // TODO: 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++)
+ rl_bind_key (0x80 /* this is the Meta modifier for Readline */
+ | ('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"));
+ rl_bind_keyseq ("M-p", rl_named_function ("previous-history"));
+ rl_bind_keyseq ("M-n", rl_named_function ("next-history"));
+
+ return 0;
}
// --- Input handling ----------------------------------------------------------
@@ -1268,24 +1428,6 @@ fail:
free (input);
}
-static int
-on_readline_previous_buffer (int count, int key)
-{
- (void) key;
-
- // TODO: switch to the previous buffer
- return 0;
-}
-
-static int
-on_readline_next_buffer (int count, int key)
-{
- (void) key;
-
- // TODO: switch to the next buffer
- return 0;
-}
-
// --- Supporting code (continued) ---------------------------------------------
enum irc_read_result
@@ -1898,6 +2040,7 @@ main (int argc, char *argv[])
atexit (ERR_free_strings);
using_history ();
+ // This can cause memory leaks, or maybe even a segfault. Funny, eh?
stifle_history (HISTORY_LIMIT);
setup_signal_handlers ();
@@ -1905,6 +2048,7 @@ main (int argc, char *argv[])
init_colors (&ctx);
init_poller_events (&ctx);
init_buffers (&ctx);
+ ctx.current_buffer = ctx.global_buffer;
refresh_prompt (&ctx);
// TODO: connect asynchronously (first step towards multiple servers)
@@ -1917,22 +2061,7 @@ main (int argc, char *argv[])
exit (EXIT_FAILURE);
}
- // TODO: maybe use rl_make_bare_keymap() and start from there
-
- // XXX: Since readline() installs a set of default key bindings the first
- // time it is called, there is always the danger that a custom binding
- // installed before the first call to readline() will be overridden.
- // An alternate mechanism is to install custom key bindings in an
- // initialization function assigned to the rl_startup_hook variable.
- rl_add_defun ("previous-buffer", on_readline_previous_buffer, -1);
- rl_add_defun ("next-buffer", on_readline_next_buffer, -1);
-
- // TODO: redefine M-0 through M-9 to switch buffers
- rl_bind_keyseq ("C-p", rl_named_function ("previous-buffer"));
- rl_bind_keyseq ("C-n", rl_named_function ("next-buffer"));
- rl_bind_keyseq ("M-p", rl_named_function ("previous-history"));
- rl_bind_keyseq ("M-n", rl_named_function ("next-history"));
-
+ 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);