aboutsummaryrefslogtreecommitdiff
path: root/degesch.c
diff options
context:
space:
mode:
Diffstat (limited to 'degesch.c')
-rw-r--r--degesch.c92
1 files changed, 85 insertions, 7 deletions
diff --git a/degesch.c b/degesch.c
index fb95019..4c27283 100644
--- a/degesch.c
+++ b/degesch.c
@@ -1367,9 +1367,13 @@ struct app_context
size_t nick_palette_len; ///< Number of entries in nick_palette
bool awaiting_mirc_escape; ///< Awaiting a mIRC attribute escape
+ // TODO: try to get rid of this in favor of "paste_buffer" -> "input_buffer"
char char_buf[MB_LEN_MAX + 1]; ///< Buffered multibyte char
size_t char_buf_len; ///< How much of an MB char is buffered
+ bool in_bracketed_paste; ///< User is pasting some content
+ struct str paste_buffer; ///< Buffered pasted content
+
bool running_backlog_helper; ///< Running a backlog helper
}
*g_ctx;
@@ -1433,6 +1437,7 @@ app_context_init (struct app_context *self)
free (encoding);
input_init (&self->input);
+ str_init (&self->paste_buffer);
self->nick_palette =
filter_color_cube_for_acceptable_nick_colors (&self->nick_palette_len);
@@ -1466,6 +1471,7 @@ app_context_free (struct app_context *self)
iconv_close (self->term_to_utf8);
input_free (&self->input);
+ str_free (&self->paste_buffer);
}
static void refresh_prompt (struct app_context *ctx);
@@ -8602,6 +8608,7 @@ process_input (struct app_context *ctx, char *user_input)
if (!(input = iconv_xstrdup (ctx->term_to_utf8, user_input, -1, NULL)))
print_error ("character conversion failed for `%s'", "user input");
else
+ // TODO: split at newlines?
(void) process_input_utf8 (ctx, ctx->current_buffer, input, 0);
free (input);
}
@@ -8974,6 +8981,13 @@ make_completions (struct app_context *ctx, char *line, int start, int end)
// --- Common code for user actions --------------------------------------------
static void
+toggle_bracketed_paste (bool enable)
+{
+ fprintf (stdout, "\x1b[?2004%c", "lh"[enable]);
+ fflush (stdout);
+}
+
+static void
suspend_terminal (struct app_context *ctx)
{
#ifdef HAVE_READLINE
@@ -8982,6 +8996,7 @@ suspend_terminal (struct app_context *ctx)
el_set (ctx->input.editline, EL_PREP_TERM, 0);
#endif
+ toggle_bracketed_paste (false);
input_hide (&ctx->input);
poller_fd_reset (&ctx->tty_event);
// TODO: also disable the date change timer
@@ -8996,6 +9011,7 @@ resume_terminal (struct app_context *ctx)
el_set (ctx->input.editline, EL_PREP_TERM, 1);
#endif
+ 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
@@ -9154,6 +9170,8 @@ bind_common_keys (struct app_context *ctx)
if (clear_screen)
input_bind_control (self, 'l', "redraw-screen");
+
+ input_bind (self, "\x1b[200~", "start-paste-mode");
}
// --- GNU Readline user actions -----------------------------------------------
@@ -9237,6 +9255,17 @@ on_readline_insert_attribute (int count, int key)
}
static int
+on_readline_start_paste_mode (int count, int key)
+{
+ (void) count;
+ (void) key;
+
+ struct app_context *ctx = g_ctx;
+ ctx->in_bracketed_paste = true;
+ return 0;
+}
+
+static int
on_readline_return (int count, int key)
{
(void) count;
@@ -9309,6 +9338,7 @@ app_readline_init (void)
rl_add_defun ("display-full-log", on_readline_display_full_log, -1);
rl_add_defun ("redraw-screen", on_readline_redraw_screen, -1);
rl_add_defun ("insert-attribute", on_readline_insert_attribute, -1);
+ rl_add_defun ("start-paste-mode", on_readline_start_paste_mode, -1);
rl_add_defun ("send-line", on_readline_return, -1);
bind_common_keys (ctx);
@@ -9408,6 +9438,16 @@ on_editline_insert_attribute (EditLine *editline, int key)
}
static unsigned char
+on_editline_start_paste_mode (EditLine *editline, int key)
+{
+ (void) editline;
+ (void) key;
+
+ g_ctx->in_bracketed_paste = true;
+ return CC_NORM;
+}
+
+static unsigned char
on_editline_complete (EditLine *editline, int key)
{
(void) key;
@@ -9506,6 +9546,7 @@ app_editline_init (struct input *self)
{ "display-full-log", "Show full log", on_editline_display_full_log },
{ "redraw-screen", "Redraw screen", on_editline_redraw_screen },
{ "insert-attribute", "mIRC formatting", on_editline_insert_attribute },
+ { "start-paste-mode", "Bracketed paste", on_editline_start_paste_mode },
{ "send-line", "Send line", on_editline_return },
{ "complete", "Complete word", on_editline_complete },
};
@@ -9886,6 +9927,43 @@ done:
ctx->awaiting_mirc_escape = false;
}
+#define BRACKETED_PASTE_LIMIT 102400 ///< How much text can be pasted
+
+static void
+process_bracketed_paste (const struct pollfd *fd, struct app_context *ctx)
+{
+ struct str *buf = &ctx->paste_buffer;
+ str_ensure_space (buf, 1);
+ if (read (fd->fd, buf->str + buf->len, 1) != 1)
+ goto error;
+ buf->str[++buf->len] = '\0';
+
+ static const char stop_mark[] = "\x1b[201~";
+ static const size_t stop_mark_len = sizeof stop_mark - 1;
+ if (buf->len < stop_mark_len)
+ return;
+
+ size_t text_len = buf->len - stop_mark_len;
+ if (memcmp (buf->str + text_len, stop_mark, stop_mark_len))
+ return;
+
+ // Avoid endless flooding of the buffer
+ if (text_len > BRACKETED_PASTE_LIMIT)
+ log_global_error (ctx, "Paste trimmed to %zu bytes",
+ (text_len = BRACKETED_PASTE_LIMIT));
+
+ buf->str[text_len] = '\0';
+ if (input_insert (&ctx->input, buf->str))
+ goto done;
+
+error:
+ input_ding (&ctx->input);
+ log_global_error (ctx, "Paste failed");
+done:
+ str_reset (buf);
+ ctx->in_bracketed_paste = false;
+}
+
static void
on_tty_readable (const struct pollfd *fd, struct app_context *ctx)
{
@@ -9895,14 +9973,12 @@ on_tty_readable (const struct pollfd *fd, struct app_context *ctx)
print_debug ("fd %d: unexpected revents: %d", fd->fd, fd->revents);
if (ctx->awaiting_mirc_escape)
- {
process_mirc_escape (fd, ctx);
- return;
- }
-
- // XXX: this may loop for a bit: stop the event or eat the input?
- // (This prevents a segfault when the input has been stopped.)
- if (ctx->input.active)
+ else if (ctx->in_bracketed_paste)
+ process_bracketed_paste (fd, ctx);
+ else if (ctx->input.active)
+ // XXX: this may loop for a bit: stop the event or eat the input?
+ // (This prevents a segfault when the input has been stopped.)
input_on_readable (&ctx->input);
}
@@ -10053,6 +10129,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]);
+ toggle_bracketed_paste (true);
// Finally, we juice the configuration for some servers to create
load_servers (&ctx);
@@ -10065,6 +10142,7 @@ main (int argc, char *argv[])
save_configuration (&ctx);
app_context_free (&ctx);
+ toggle_bracketed_paste (false);
free_terminal ();
return EXIT_SUCCESS;
}