diff --git a/docs/signals.txt b/docs/signals.txt index da72ef57..81a1e3fa 100644 --- a/docs/signals.txt +++ b/docs/signals.txt @@ -351,6 +351,7 @@ Text FE gui-readline.c: "gui key pressed", int key + "paste event", char *paste, char *arg gui-printtext.c: "beep" diff --git a/src/common.h b/src/common.h index ac09a553..7f8604f3 100644 --- a/src/common.h +++ b/src/common.h @@ -6,7 +6,7 @@ #define IRSSI_GLOBAL_CONFIG "irssi.conf" /* config file name in /etc/ */ #define IRSSI_HOME_CONFIG "config" /* config file name in ~/.irssi/ */ -#define IRSSI_ABI_VERSION 43 +#define IRSSI_ABI_VERSION 44 #define DEFAULT_SERVER_ADD_PORT 6667 #define DEFAULT_SERVER_ADD_TLS_PORT 6697 diff --git a/src/fe-text/gui-readline.c b/src/fe-text/gui-readline.c index f848ba1b..8e1b66ff 100644 --- a/src/fe-text/gui-readline.c +++ b/src/fe-text/gui-readline.c @@ -69,6 +69,7 @@ static GArray *paste_buffer_rest; static char *paste_old_prompt; static int paste_prompt, paste_line_count; static int paste_join_multiline; +static int paste_ignore_first_nl; static int paste_timeout_id; static int paste_use_bracketed_mode; static int paste_bracketed_mode; @@ -81,6 +82,25 @@ static int previous_yank_preceded; static const unichar bp_start[] = { 0x1b, '[', '2', '0', '0', '~' }; static const unichar bp_end[] = { 0x1b, '[', '2', '0', '1', '~' }; +#define BRACKETED_PASTE_TIMEOUT (5 * 1000) // ms + +#if GLIB_CHECK_VERSION(2, 62, 0) +/* nothing */ +#else +/* compatibility code for old GLib */ +GArray *g_array_copy(GArray *array) +{ + GArray *out; + guint elt_size; + + elt_size = g_array_get_element_size(array); + out = g_array_sized_new(FALSE, FALSE, elt_size, array->len); + memcpy(out->data, array->data, array->len * elt_size); + + return out; +} +#endif + static void sig_input(void); void input_listen_init(int handle) @@ -330,7 +350,7 @@ static void paste_send(void) g_string_free(str, TRUE); } -static void paste_flush(int send) +static void paste_flush(void (*send)(void)) { if (paste_prompt) { gui_entry_set_text(active_entry, paste_entry); @@ -338,8 +358,8 @@ static void paste_flush(int send) g_free_and_null(paste_entry); } - if (send) - paste_send(); + if (send != NULL) + send(); g_array_set_size(paste_buffer, 0); /* re-add anything that may have been after the bracketed paste end */ @@ -358,6 +378,128 @@ static void paste_flush(int send) gui_entry_redraw(active_entry); } +static void paste_print_line(const char *str) +{ + printformat_window(active_win, MSGLEVEL_CLIENTCRAP, TXT_PASTE_CONTENT, str); +} + +static void paste_print(void) +{ + GArray *garr; + unichar *arr; + GString *str; + char out[10]; + unsigned int i; + gboolean free_garr; + + if (paste_join_multiline) { + garr = g_array_copy(paste_buffer); + paste_buffer_join_lines(garr); + free_garr = TRUE; + } else { + garr = paste_buffer; + free_garr = FALSE; + } + + arr = &g_array_index(garr, unichar, 0); + + str = g_string_new(NULL); + for (i = 0; i < garr->len; i++) { + if (isnewline(arr[i])) { + paste_print_line(str->str); + g_string_truncate(str, 0); + } else if (active_entry->utf8) { + out[g_unichar_to_utf8(arr[i], out)] = '\0'; + g_string_append(str, out); + } else if (term_type == TERM_TYPE_BIG5) { + if (arr[i] > 0xff) + g_string_append_c(str, (arr[i] >> 8) & 0xff); + g_string_append_c(str, arr[i] & 0xff); + } else { + g_string_append_c(str, arr[i]); + } + } + + if (str->len) + paste_print_line(str->str); + + g_string_free(str, TRUE); + if (free_garr) + g_array_free(garr, TRUE); +} + +static void paste_event(const char *arg) +{ + GArray *garr; + unichar *arr; + GString *str; + char out[10]; + unsigned int i; + gboolean free_garr; + + if (paste_join_multiline) { + garr = g_array_copy(paste_buffer); + paste_buffer_join_lines(garr); + free_garr = TRUE; + } else { + garr = paste_buffer; + free_garr = FALSE; + } + + arr = &g_array_index(garr, unichar, 0); + str = g_string_new(NULL); + for (i = 0; i < garr->len; i++) { + if (isnewline(arr[i])) { + g_string_append_c(str, '\n'); + } else if (active_entry->utf8) { + out[g_unichar_to_utf8(arr[i], out)] = '\0'; + g_string_append(str, out); + } else if (term_type == TERM_TYPE_BIG5) { + if (arr[i] > 0xff) + g_string_append_c(str, (arr[i] >> 8) & 0xff); + g_string_append_c(str, arr[i] & 0xff); + } else { + g_string_append_c(str, arr[i]); + } + } + + if (signal_emit("paste event", 2, str->str, arg)) { + paste_flush(NULL); + } + + g_string_free(str, TRUE); + if (free_garr) + g_array_free(garr, TRUE); +} + +static void paste_insert_edit(void) +{ + unichar *arr; + unsigned int i; + + if (paste_join_multiline) + paste_buffer_join_lines(paste_buffer); + + arr = &g_array_index(paste_buffer, unichar, 0); + for (i = 0; i < paste_buffer->len; i++) { + if (isnewline(arr[i])) { + gui_entry_insert_char(active_entry, '\\'); + gui_entry_insert_char(active_entry, 'n'); + } else if (arr[i] == 9) { + gui_entry_insert_char(active_entry, '\\'); + gui_entry_insert_char(active_entry, 't'); + } else if (arr[i] == 27) { + gui_entry_insert_char(active_entry, '\\'); + gui_entry_insert_char(active_entry, 'e'); + } else if (arr[i] == '\\') { + gui_entry_insert_char(active_entry, '\\'); + gui_entry_insert_char(active_entry, '\\'); + } else { + gui_entry_insert_char(active_entry, arr[i]); + } + } +} + static void insert_paste_prompt(void) { char *str; @@ -729,6 +871,17 @@ static gboolean paste_timeout(gpointer data) int split_lines; paste_was_bracketed_mode = paste_bracketed_mode; + if (paste_ignore_first_nl && paste_line_count == 1) { + unichar last_char; + + last_char = g_array_index(paste_buffer, unichar, paste_buffer->len - 1); + + if (isnewline(last_char)) { + g_array_set_size(paste_buffer, paste_buffer->len - 1); + paste_line_count--; + } + } + /* number of lines after splitting extra-long messages */ split_lines = paste_buffer->len / LINE_SPLIT_LIMIT; @@ -747,7 +900,7 @@ static gboolean paste_timeout(gpointer data) active_win->active != NULL) insert_paste_prompt(); else - paste_flush(TRUE); + paste_flush(paste_send); paste_timeout_id = -1; return FALSE; } @@ -777,12 +930,14 @@ static void paste_bracketed_end(int i, gboolean rest) } /* decide what to do with the buffer */ + if (paste_timeout_id != -1) + g_source_remove(paste_timeout_id); paste_timeout(NULL); paste_bracketed_mode = FALSE; } -static void paste_bracketed_middle() +static void paste_bracketed_middle(void) { int i; int marklen = G_N_ELEMENTS(bp_end); @@ -830,8 +985,10 @@ static void sig_input(void) term_gets(buffer, &line_count); key = g_array_index(buffer, unichar, 0); /* Either Ctrl-k or Ctrl-c is pressed */ - if (key == 11 || key == 3) - paste_flush(key == 11); + if (key < 32 && key != 13 /* CR */ && key != 10 /* LF */ && key != 27 /* Esc */) { + key_pressed(keyboard, "paste"); + signal_emit("gui key pressed", 1, GINT_TO_POINTER(key)); + } g_array_free(buffer, TRUE); } else { term_gets(paste_buffer, &paste_line_count); @@ -869,7 +1026,47 @@ static void sig_input(void) static void key_paste_start(void) { - paste_bracketed_mode = TRUE; + if (paste_use_bracketed_mode) { + paste_bracketed_mode = TRUE; + if (paste_timeout_id != -1) + g_source_remove(paste_timeout_id); + paste_timeout_id = g_timeout_add(BRACKETED_PASTE_TIMEOUT, paste_timeout, NULL); + } +} + +static void key_paste_cancel(void) +{ + if (paste_prompt) { + paste_flush(NULL); + } +} + +static void key_paste_print(void) +{ + if (paste_prompt) { + paste_print(); + } +} + +static void key_paste_send(void) +{ + if (paste_prompt) { + paste_flush(paste_send); + } +} + +static void key_paste_edit(void) +{ + if (paste_prompt) { + paste_flush(paste_insert_edit); + } +} + +static void key_paste_event(const char *arg) +{ + if (paste_prompt) { + paste_event(arg); + } } time_t get_idle_time(void) @@ -1131,11 +1328,14 @@ static void setup_changed(void) paste_verify_line_count = settings_get_int("paste_verify_line_count"); paste_join_multiline = settings_get_bool("paste_join_multiline"); + paste_ignore_first_nl = settings_get_bool("paste_ignore_first_nl"); paste_use_bracketed_mode = settings_get_bool("paste_use_bracketed_mode"); term_set_appkey_mode(settings_get_bool("term_appkey_mode")); /* Enable the bracketed paste mode on demand */ term_set_bracketed_paste_mode(paste_use_bracketed_mode); + if (!paste_use_bracketed_mode) + paste_bracketed_mode = FALSE; } void gui_readline_init(void) @@ -1164,7 +1364,8 @@ void gui_readline_init(void) keycodes. this must be larger to allow them to work. */ settings_add_int("misc", "paste_verify_line_count", 5); settings_add_bool("misc", "paste_join_multiline", TRUE); - setup_changed(); + settings_add_bool("misc", "paste_ignore_first_nl", FALSE); + setup_changed(); keyboard = keyboard_create(NULL); key_configure_freeze(); @@ -1235,7 +1436,13 @@ void gui_readline_init(void) key_bind("key", NULL, "meta-O-M", "return", (SIGNAL_FUNC) key_combo); - key_bind("paste_start", "Bracketed paste start", "meta2-200~", "paste_start", (SIGNAL_FUNC) key_paste_start); + /* clang-format off */ + key_bind("paste_start", "Bracketed paste start", "^[[200~", "paste_start", (SIGNAL_FUNC) key_paste_start); + key_bind("paste_cancel", "Cancel paste", "paste-^C", NULL, (SIGNAL_FUNC) key_paste_cancel); + key_bind("paste_print", "Print paste to screen", "paste-^P", NULL, (SIGNAL_FUNC) key_paste_print); + key_bind("paste_send", "Send paste to target", "paste-^K", NULL, (SIGNAL_FUNC) key_paste_send); + key_bind("paste_edit", "Insert paste to input line", "paste-^E", NULL, (SIGNAL_FUNC) key_paste_edit); + key_bind("paste_event", "Send paste to event", "paste-^U", NULL, (SIGNAL_FUNC) key_paste_event); /* cursor movement */ key_bind("backward_character", "Move the cursor a character backward", "left", NULL, (SIGNAL_FUNC) key_backward_character); @@ -1307,8 +1514,9 @@ void gui_readline_init(void) /* inserting special input characters to line.. */ key_bind("escape_char", "Insert the next character exactly as-is to input line", NULL, NULL, (SIGNAL_FUNC) key_escape); key_bind("insert_text", "Append text to line", NULL, NULL, (SIGNAL_FUNC) key_insert_text); + /* clang-format on */ - /* autoreplaces */ + /* autoreplaces */ key_bind("multi", NULL, "return", "check_replaces;send_line", NULL); key_bind("multi", NULL, "space", "check_replaces;insert_text ", NULL); @@ -1338,6 +1546,11 @@ void gui_readline_deinit(void) key_configure_freeze(); key_unbind("paste_start", (SIGNAL_FUNC) key_paste_start); + key_unbind("paste_cancel", (SIGNAL_FUNC) key_paste_cancel); + key_unbind("paste_print", (SIGNAL_FUNC) key_paste_print); + key_unbind("paste_send", (SIGNAL_FUNC) key_paste_send); + key_unbind("paste_edit", (SIGNAL_FUNC) key_paste_edit); + key_unbind("paste_event", (SIGNAL_FUNC) key_paste_event); key_unbind("backward_character", (SIGNAL_FUNC) key_backward_character); key_unbind("forward_character", (SIGNAL_FUNC) key_forward_character); diff --git a/src/fe-text/module-formats.c b/src/fe-text/module-formats.c index e9d4fab9..bb7eacee 100644 --- a/src/fe-text/module-formats.c +++ b/src/fe-text/module-formats.c @@ -21,8 +21,8 @@ #include "module.h" #include -FORMAT_REC gui_text_formats[] = -{ +FORMAT_REC gui_text_formats[] = { + /* clang-format off */ { MODULE_NAME, "Text user interface", 0 }, /* ---- */ @@ -77,8 +77,9 @@ FORMAT_REC gui_text_formats[] = /* ---- */ { NULL, "Pasting", 0 }, - { "paste_warning", "Pasting $0 lines to $1. Press Ctrl-K if you wish to do this or Ctrl-C to cancel.", 2, { 1, 0 } }, + { "paste_warning", "Pasting $0 lines to $1. Press Ctrl-K if you wish to do this or Ctrl-C to cancel. Ctrl-P to print the paste content, Ctrl-E to insert the paste in the input line, Ctrl-U to pass the paste to a signal handler.", 2, { 1, 0 } }, { "paste_prompt", "Hit Ctrl-K to paste, Ctrl-C to abort?", 0 }, + { "paste_content", "%_>%_ $0", 1, { 0 } }, /* ---- */ { NULL, "Welcome", 0 }, @@ -98,4 +99,5 @@ FORMAT_REC gui_text_formats[] = { "welcome_init_settings", "The following settings were initialized", 0 }, { NULL, NULL, 0 } + /* clang-format on */ }; diff --git a/src/fe-text/module-formats.h b/src/fe-text/module-formats.h index 59c877b8..5392705f 100644 --- a/src/fe-text/module-formats.h +++ b/src/fe-text/module-formats.h @@ -5,8 +5,8 @@ enum { TXT_FILL_1, - TXT_LASTLOG_TOO_LONG, - TXT_LASTLOG_COUNT, + TXT_LASTLOG_TOO_LONG, + TXT_LASTLOG_COUNT, TXT_LASTLOG_START, TXT_LASTLOG_END, TXT_LASTLOG_SEPARATOR, @@ -14,18 +14,18 @@ enum { TXT_FILL_2, - TXT_REFNUM_NOT_FOUND, - TXT_WINDOW_TOO_SMALL, - TXT_CANT_HIDE_LAST, + TXT_REFNUM_NOT_FOUND, + TXT_WINDOW_TOO_SMALL, + TXT_CANT_HIDE_LAST, TXT_CANT_HIDE_STICKY_WINDOWS, - TXT_CANT_SHOW_STICKY_WINDOWS, - TXT_WINDOW_NOT_STICKY, - TXT_WINDOW_SET_STICKY, + TXT_CANT_SHOW_STICKY_WINDOWS, + TXT_WINDOW_NOT_STICKY, + TXT_WINDOW_SET_STICKY, TXT_WINDOW_UNSET_STICKY, TXT_WINDOW_INFO_STICKY, - TXT_WINDOW_INFO_SCROLL, - TXT_WINDOW_SCROLL, - TXT_WINDOW_SCROLL_UNKNOWN, + TXT_WINDOW_INFO_SCROLL, + TXT_WINDOW_SCROLL, + TXT_WINDOW_SCROLL_UNKNOWN, TXT_WINDOW_HIDELEVEL, TXT_FILL_3, @@ -53,6 +53,7 @@ enum { TXT_PASTE_WARNING, TXT_PASTE_PROMPT, + TXT_PASTE_CONTENT, TXT_FILL_5, /* Welcome */ diff --git a/src/perl/common/Settings.xs b/src/perl/common/Settings.xs index 2da23400..0f172411 100644 --- a/src/perl/common/Settings.xs +++ b/src/perl/common/Settings.xs @@ -104,6 +104,10 @@ int settings_get_size(key) char *key +int +settings_get_choice(key) + char *key + void settings_set_str(key, value) char *key @@ -134,6 +138,11 @@ settings_set_size(key, value) char *key char *value +int +settings_set_choice(key, value) + char *key + char *value + void settings_add_str(section, key, def) char *section @@ -188,6 +197,16 @@ CODE: perl_settings_add(key); settings_add_size_module(MODULE_NAME"/scripts", section, key, def); +void +settings_add_choice(section, key, def, choices) + char *section + char *key + int def + char *choices +CODE: + perl_settings_add(key); + settings_add_choice_module(MODULE_NAME "/scripts", section, key, def, choices); + void settings_remove(key) char *key