aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPřemysl Janouch <p.janouch@gmail.com>2015-11-15 01:03:29 +0100
committerPřemysl Janouch <p.janouch@gmail.com>2015-11-15 01:07:12 +0100
commitd8299a1231110908986159b6f0ea65484fb17c84 (patch)
tree07fe42a0690f1cda42663cbf0a5d609e8822532c
parent465c2e4082ab80860f8770efaee80c60da93c82f (diff)
downloadxK-d8299a1231110908986159b6f0ea65484fb17c84.tar.gz
xK-d8299a1231110908986159b6f0ea65484fb17c84.tar.xz
xK-d8299a1231110908986159b6f0ea65484fb17c84.zip
degesch: enable and use bracketed paste mode
urxvt, xterm and maybe others support quoting text pasted by the user from clipboard, which prevents leading tabs from changing into highlights. The handling isn't perfect so far, just wrong in a different way, as we mishandle newlines.
-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;
}