From f95fc811302eac53d020ef438cd914c564657be5 Mon Sep 17 00:00:00 2001 From: ailin-nemui Date: Tue, 13 Aug 2019 14:59:30 +0200 Subject: [PATCH 1/6] make lines reformattable - completely removed the old textbuffer representation ( https://github.com/shabble/irssi-docs/wiki/Notes-256-Colour#textbuffer-encoding ) - textbuffer-formats is an extra module, so if we unhook the signals it should go back to the "old way" of storing pre-rendered tex - design uses cache, original formats and list of arguments --- src/core/levels.h | 7 +- src/fe-common/core/fe-core-commands.c | 2 + src/fe-common/core/formats.c | 97 ++++++- src/fe-common/core/formats.h | 8 +- src/fe-common/core/printtext.c | 94 ++++--- src/fe-text/Makefile.am | 4 +- src/fe-text/gui-printtext.c | 14 +- src/fe-text/gui-windows.c | 7 +- src/fe-text/irssi.c | 5 + src/fe-text/lastlog.c | 2 +- src/fe-text/meson.build | 2 + src/fe-text/textbuffer-commands.c | 45 ++- src/fe-text/textbuffer-formats.c | 258 +++++++++++++++++ src/fe-text/textbuffer-formats.h | 22 ++ src/fe-text/textbuffer-view.c | 384 +++++++++++++++----------- src/fe-text/textbuffer-view.h | 1 + src/fe-text/textbuffer.c | 382 +++---------------------- src/fe-text/textbuffer.h | 39 +-- src/perl/textui/TextBuffer.xs | 5 +- tests/fe-text/Makefile.am | 1 + tests/fe-text/meson.build | 1 + 21 files changed, 791 insertions(+), 589 deletions(-) create mode 100644 src/fe-text/textbuffer-formats.c create mode 100644 src/fe-text/textbuffer-formats.h diff --git a/src/core/levels.h b/src/core/levels.h index 385ecd0c..04893b34 100644 --- a/src/core/levels.h +++ b/src/core/levels.h @@ -6,6 +6,7 @@ difficult message leveling system (which might be done if really needed..). */ +/* clang-format off */ /* Message levels */ enum { MSGLEVEL_CRAP = 0x0000001, @@ -38,8 +39,12 @@ enum { MSGLEVEL_NEVER = 0x4000000, /* never ignore / never log */ MSGLEVEL_LASTLOG = 0x8000000, /* used for /lastlog */ - MSGLEVEL_HIDDEN = 0x10000000 /* Hidden from view */ + MSGLEVEL_HIDDEN = 0x10000000, /* Hidden from view */ + MSGLEVEL_RESERVED1 = 0x20000000, + MSGLEVEL_RESERVED2 = 0x40000000, + MSGLEVEL_FORMAT = 0x80000000 /* Format data */ }; +/* clang-format on */ int level_get(const char *level); int level2bits(const char *level, int *errorp); diff --git a/src/fe-common/core/fe-core-commands.c b/src/fe-common/core/fe-core-commands.c index 9a2c547f..2c79334a 100644 --- a/src/fe-common/core/fe-core-commands.c +++ b/src/fe-common/core/fe-core-commands.c @@ -233,6 +233,7 @@ static void event_command(const char *data) (data[1] == *cmdchar && data[2] == '^')) && !command_hide_output++) { signal_add_first("print starting", (SIGNAL_FUNC) sig_stop); + signal_add_first("print noformat", (SIGNAL_FUNC) sig_stop); signal_add_first("print format", (SIGNAL_FUNC) sig_stop); signal_add_first("print text", (SIGNAL_FUNC) sig_stop); } @@ -242,6 +243,7 @@ static void event_command_last(const char *data) { if (command_hide_output && !--command_hide_output) { signal_remove("print starting", (SIGNAL_FUNC) sig_stop); + signal_remove("print noformat", (SIGNAL_FUNC) sig_stop); signal_remove("print format", (SIGNAL_FUNC) sig_stop); signal_remove("print text", (SIGNAL_FUNC) sig_stop); } diff --git a/src/fe-common/core/formats.c b/src/fe-common/core/formats.c index 414c6582..a6fde7ea 100644 --- a/src/fe-common/core/formats.c +++ b/src/fe-common/core/formats.c @@ -850,14 +850,13 @@ char *format_get_line_start(THEME_REC *theme, TEXT_DEST_REC *dest, time_t t) return linestart; } -void format_newline(WINDOW_REC *window) +void format_newline(TEXT_DEST_REC *dest) { - g_return_if_fail(window != NULL); + g_return_if_fail(dest != NULL); + g_return_if_fail(dest->window != NULL); - signal_emit_id(signal_gui_print_text, 6, window, - GINT_TO_POINTER(-1), GINT_TO_POINTER(-1), - GINT_TO_POINTER(GUI_PRINT_FLAG_NEWLINE), - "", NULL); + signal_emit_id(signal_gui_print_text, 6, dest->window, GINT_TO_POINTER(-1), + GINT_TO_POINTER(-1), GINT_TO_POINTER(GUI_PRINT_FLAG_NEWLINE), "", dest); } #ifndef TERM_TRUECOLOR @@ -1269,7 +1268,7 @@ void format_send_to_gui(TEXT_DEST_REC *dest, const char *text) } if (type == '\n') { - format_newline(dest->window); + format_newline(dest); fgcolor = theme->default_color; bgcolor = -1; flags &= GUI_PRINT_FLAG_INDENT|GUI_PRINT_FLAG_MONOSPACE; @@ -1415,6 +1414,90 @@ void format_send_to_gui(TEXT_DEST_REC *dest, const char *text) g_free(dup); } +void format_gui_flags(GString *out, int *last_fg, int *last_bg, int *last_flags, int fg, int bg, + int flags) +{ + if (fg != *last_fg || + (flags & GUI_PRINT_FLAG_COLOR_24_FG) != (*last_flags & GUI_PRINT_FLAG_COLOR_24_FG)) { + *last_fg = fg; + +#ifdef TERM_TRUECOLOR + if (flags & GUI_PRINT_FLAG_COLOR_24_FG) { + *last_flags |= GUI_PRINT_FLAG_COLOR_24_FG; + format_24bit_color(out, 0, fg); + } else { + *last_flags &= ~GUI_PRINT_FLAG_COLOR_24_FG; +#endif + if (fg < 0) { + g_string_append_c(out, 4); + g_string_append_c(out, (char) -1); + g_string_append_c(out, FORMAT_COLOR_NOCHANGE); + } else { + format_ext_color(out, 0, fg); + } +#ifdef TERM_TRUECOLOR + } +#endif + } + if (bg != *last_bg || + (flags & GUI_PRINT_FLAG_COLOR_24_BG) != (*last_flags & GUI_PRINT_FLAG_COLOR_24_BG)) { + *last_bg = bg; +#ifdef TERM_TRUECOLOR + if (flags & GUI_PRINT_FLAG_COLOR_24_BG) { + *last_flags |= GUI_PRINT_FLAG_COLOR_24_BG; + format_24bit_color(out, 1, bg); + } else { + *last_flags &= ~GUI_PRINT_FLAG_COLOR_24_BG; +#endif + if (bg < 0) { + g_string_append_c(out, 4); + g_string_append_c(out, FORMAT_COLOR_NOCHANGE); + g_string_append_c(out, (char) -1); + } else { + format_ext_color(out, 1, bg); + } +#ifdef TERM_TRUECOLOR + } +#endif + } + + if ((flags & GUI_PRINT_FLAG_UNDERLINE) != (*last_flags & GUI_PRINT_FLAG_UNDERLINE)) { + *last_flags ^= GUI_PRINT_FLAG_UNDERLINE; + g_string_append_c(out, 4); + g_string_append_c(out, FORMAT_STYLE_UNDERLINE); + } + if ((flags & GUI_PRINT_FLAG_REVERSE) != (*last_flags & GUI_PRINT_FLAG_REVERSE)) { + *last_flags ^= GUI_PRINT_FLAG_REVERSE; + g_string_append_c(out, 4); + g_string_append_c(out, FORMAT_STYLE_REVERSE); + } + if ((flags & GUI_PRINT_FLAG_BLINK) != (*last_flags & GUI_PRINT_FLAG_BLINK)) { + *last_flags ^= GUI_PRINT_FLAG_BLINK; + g_string_append_c(out, 4); + g_string_append_c(out, FORMAT_STYLE_BLINK); + } + if ((flags & GUI_PRINT_FLAG_BOLD) != (*last_flags & GUI_PRINT_FLAG_BOLD)) { + *last_flags ^= GUI_PRINT_FLAG_BOLD; + g_string_append_c(out, 4); + g_string_append_c(out, FORMAT_STYLE_BOLD); + } + if ((flags & GUI_PRINT_FLAG_ITALIC) != (*last_flags & GUI_PRINT_FLAG_ITALIC)) { + *last_flags ^= GUI_PRINT_FLAG_ITALIC; + g_string_append_c(out, 4); + g_string_append_c(out, FORMAT_STYLE_ITALIC); + } + if ((flags & GUI_PRINT_FLAG_MONOSPACE) != (*last_flags & GUI_PRINT_FLAG_MONOSPACE)) { + *last_flags ^= GUI_PRINT_FLAG_MONOSPACE; + g_string_append_c(out, 4); + g_string_append_c(out, FORMAT_STYLE_MONOSPACE); + } + if (flags & GUI_PRINT_FLAG_INDENT) { + *last_flags ^= GUI_PRINT_FLAG_INDENT; + g_string_append_c(out, 4); + g_string_append_c(out, FORMAT_STYLE_INDENT); + } +} + static void read_settings(void) { timestamp_level = settings_get_bool("timestamps") ? MSGLEVEL_ALL : 0; diff --git a/src/fe-common/core/formats.h b/src/fe-common/core/formats.h index 2e6133be..e30ac290 100644 --- a/src/fe-common/core/formats.h +++ b/src/fe-common/core/formats.h @@ -35,6 +35,7 @@ struct _FORMAT_REC { int paramtypes[MAX_FORMAT_PARAMS]; }; +/* clang-format off */ #define PRINT_FLAG_SET_LINE_START 0x0001 #define PRINT_FLAG_SET_LINE_START_IRSSI 0x0002 #define PRINT_FLAG_UNSET_LINE_START 0x0040 @@ -45,6 +46,9 @@ struct _FORMAT_REC { #define PRINT_FLAG_SET_SERVERTAG 0x0010 #define PRINT_FLAG_UNSET_SERVERTAG 0x0020 +#define PRINT_FLAG_FORMAT 0x0080 +/* clang-format on */ + typedef struct _HILIGHT_REC HILIGHT_REC; typedef struct _TEXT_DEST_REC { @@ -113,7 +117,7 @@ void format_create_dest_tag(TEXT_DEST_REC *dest, void *server, const char *server_tag, const char *target, int level, WINDOW_REC *window); -void format_newline(WINDOW_REC *window); +void format_newline(TEXT_DEST_REC *dest); /* Return how many characters in `str' must be skipped before `len' characters of text is skipped. */ @@ -151,6 +155,8 @@ void format_send_to_gui(TEXT_DEST_REC *dest, const char *text); int format_expand_styles(GString *out, const char **format, int *flags); void format_ext_color(GString *out, int bg, int color); void format_24bit_color(GString *out, int bg, unsigned int color); +void format_gui_flags(GString *out, int *last_fg, int *last_bg, int *last_flags, int fg, int bg, + int flags); void formats_init(void); void formats_deinit(void); diff --git a/src/fe-common/core/printtext.c b/src/fe-common/core/printtext.c index 692a3bc2..06c22a4c 100644 --- a/src/fe-common/core/printtext.c +++ b/src/fe-common/core/printtext.c @@ -42,7 +42,7 @@ static int signal_print_noformat; static int sending_print_starting; -static void print_line(TEXT_DEST_REC *dest, const char *text); +static void print_line(TEXT_DEST_REC *dest, const char *text, int formatted); void printformat_module_dest_args(const char *module, TEXT_DEST_REC *dest, int formatnum, va_list va) @@ -63,7 +63,6 @@ void printformat_module_dest_charargs(const char *module, TEXT_DEST_REC *dest, int formatnum, char **arglist) { THEME_REC *theme; - char *str; theme = window_get_theme(dest->window); @@ -75,11 +74,6 @@ void printformat_module_dest_charargs(const char *module, TEXT_DEST_REC *dest, signal_emit_id(signal_print_format, 5, theme, module, dest, GINT_TO_POINTER(formatnum), arglist); - - str = format_get_text_theme_charargs(theme, module, dest, - formatnum, arglist); - if (str != NULL && *str != '\0') print_line(dest, str); - g_free(str); } void printformat_module_dest(const char *module, TEXT_DEST_REC *dest, @@ -164,29 +158,6 @@ void printformat_module_gui(const char *module, int formatnum, ...) va_end(va); } -static void print_line(TEXT_DEST_REC *dest, const char *text) -{ - THEME_REC *theme; - char *str, *tmp, *stripped; - - g_return_if_fail(dest != NULL); - g_return_if_fail(text != NULL); - - theme = window_get_theme(dest->window); - tmp = format_get_level_tag(theme, dest); - str = !theme->info_eol ? format_add_linestart(text, tmp) : - format_add_lineend(text, tmp); - g_free_not_null(tmp); - - /* send both the formatted + stripped (for logging etc.) */ - stripped = strip_codes(str); - signal_emit_id(signal_print_text, 3, dest, str, stripped); - g_free_and_null(dest->hilight_color); - - g_free(str); - g_free(stripped); -} - /* append string to `out', expand newlines. */ static void printtext_append_str(TEXT_DEST_REC *dest, GString *out, const char *str) @@ -195,7 +166,7 @@ static void printtext_append_str(TEXT_DEST_REC *dest, GString *out, if (*str != '\n') g_string_append_c(out, *str); else { - print_line(dest, out->str); + print_line(dest, out->str, 0); g_string_truncate(out, 0); } str++; @@ -314,9 +285,7 @@ static void printtext_dest_args(TEXT_DEST_REC *dest, const char *text, va_list v } str = printtext_get_args(dest, text, va); - signal_emit_id(signal_print_noformat, 2, - dest, str); - print_line(dest, str); + print_line(dest, str, 0); g_free(str); } @@ -361,8 +330,8 @@ void printtext_string(void *server, const char *target, int level, const char *t } str = printtext_expand_formats(text, &dest.flags); - print_line(&dest, str); - g_free(str); + print_line(&dest, str, 0); + g_free(str); } /* Like printtext_window(), but don't handle %s etc. */ @@ -383,8 +352,8 @@ void printtext_string_window(WINDOW_REC *window, int level, const char *text) } str = printtext_expand_formats(text, &dest.flags); - print_line(&dest, str); - g_free(str); + print_line(&dest, str, 0); + g_free(str); } void printtext_window(WINDOW_REC *window, int level, const char *text, ...) @@ -476,6 +445,51 @@ static void sig_print_text(TEXT_DEST_REC *dest, const char *text) signal_emit_id(signal_gui_print_text_finished, 1, dest->window); } +static void sig_print_format(THEME_REC *theme, const char *module, TEXT_DEST_REC *dest, + void *formatnump, char **arglist) +{ + int formatnum; + char *str; + + formatnum = GPOINTER_TO_INT(formatnump); + str = format_get_text_theme_charargs(theme, module, dest, formatnum, arglist); + if (str != NULL && *str != '\0') + print_line(dest, str, 1); + + g_free(str); +} + +static void sig_print_noformat(TEXT_DEST_REC *dest, const char *text) +{ + THEME_REC *theme; + char *str, *tmp, *stripped; + + theme = window_get_theme(dest->window); + tmp = format_get_level_tag(theme, dest); + str = !theme->info_eol ? format_add_linestart(text, tmp) : format_add_lineend(text, tmp); + g_free_not_null(tmp); + + /* send both the formatted + stripped (for logging etc.) */ + stripped = strip_codes(str); + signal_emit_id(signal_print_text, 3, dest, str, stripped); + + g_free_and_null(dest->hilight_color); + + g_free(str); + g_free(stripped); +} + +static void print_line(TEXT_DEST_REC *dest, const char *text, int formatted) +{ + g_return_if_fail(dest != NULL); + g_return_if_fail(text != NULL); + + if (!formatted) + signal_emit_id(signal_print_noformat, 2, dest, text); + else + sig_print_noformat(dest, text); +} + void printtext_multiline(void *server, const char *target, int level, const char *format, const char *text) { @@ -522,6 +536,8 @@ void printtext_init(void) read_settings(); signal_add("print text", (SIGNAL_FUNC) sig_print_text); + signal_add("print format", (SIGNAL_FUNC) sig_print_format); + signal_add("print noformat", (SIGNAL_FUNC) sig_print_noformat); signal_add("gui dialog", (SIGNAL_FUNC) sig_gui_dialog); signal_add("setup changed", (SIGNAL_FUNC) read_settings); } @@ -529,6 +545,8 @@ void printtext_init(void) void printtext_deinit(void) { signal_remove("print text", (SIGNAL_FUNC) sig_print_text); + signal_remove("print format", (SIGNAL_FUNC) sig_print_format); + signal_remove("print noformat", (SIGNAL_FUNC) sig_print_noformat); signal_remove("gui dialog", (SIGNAL_FUNC) sig_gui_dialog); signal_remove("setup changed", (SIGNAL_FUNC) read_settings); } diff --git a/src/fe-text/Makefile.am b/src/fe-text/Makefile.am index 94ce1a3c..8835f4b9 100644 --- a/src/fe-text/Makefile.am +++ b/src/fe-text/Makefile.am @@ -45,6 +45,7 @@ irssi_SOURCES = \ textbuffer.c \ textbuffer-commands.c \ textbuffer-view.c \ + textbuffer-formats.c \ irssi.c \ module-formats.c @@ -57,7 +58,8 @@ pkginc_fe_text_HEADERS = \ statusbar-item.h \ term.h \ textbuffer.h \ - textbuffer-view.h + textbuffer-view.h \ + textbuffer-formats.h noinst_HEADERS = \ gui-entry.h \ diff --git a/src/fe-text/gui-printtext.c b/src/fe-text/gui-printtext.c index 24e91624..fe720264 100644 --- a/src/fe-text/gui-printtext.c +++ b/src/fe-text/gui-printtext.c @@ -19,8 +19,9 @@ */ #include "module.h" -#include +#include #include +#include #include #include @@ -329,7 +330,7 @@ static void sig_gui_print_text(WINDOW_REC *window, void *fgcolor, GUI_WINDOW_REC *gui; TEXT_BUFFER_VIEW_REC *view; LINE_REC *insert_after; - LINE_INFO_REC lineinfo; + LINE_INFO_REC lineinfo = { 0 }; int fg, bg, flags, attr; flags = GPOINTER_TO_INT(pflags); @@ -342,10 +343,16 @@ static void sig_gui_print_text(WINDOW_REC *window, void *fgcolor, return; } + if (dest != NULL && dest->flags & PRINT_FLAG_FORMAT) { + return; + } + lineinfo.level = dest == NULL ? 0 : dest->level; gui = WINDOW_GUI(window); lineinfo.time = (gui->use_insert_after && gui->insert_after_time) ? gui->insert_after_time : time(NULL); + lineinfo.format = + dest != NULL && dest->flags & PRINT_FLAG_FORMAT ? LINE_INFO_FORMAT_SET : NULL; view = gui->view; insert_after = gui->use_insert_after ? @@ -378,7 +385,8 @@ static void sig_gui_printtext_finished(WINDOW_REC *window) insert_after = WINDOW_GUI(window)->use_insert_after ? WINDOW_GUI(window)->insert_after : view->buffer->cur_line; - view_add_eol(view, &insert_after); + if (insert_after != NULL) + view_add_eol(view, &insert_after); remove_old_lines(view); } diff --git a/src/fe-text/gui-windows.c b/src/fe-text/gui-windows.c index 9ca2dfdb..46be2396 100644 --- a/src/fe-text/gui-windows.c +++ b/src/fe-text/gui-windows.c @@ -43,10 +43,9 @@ static GUI_WINDOW_REC *gui_window_init(WINDOW_REC *window, gui = g_new0(GUI_WINDOW_REC, 1); gui->parent = parent; - gui->view = textbuffer_view_create(textbuffer_create(), - window->width, window->height, - settings_get_bool("scroll"), - term_type == TERM_TYPE_UTF8); + gui->view = + textbuffer_view_create(textbuffer_create(window), window->width, window->height, + settings_get_bool("scroll"), term_type == TERM_TYPE_UTF8); textbuffer_view_set_default_indent(gui->view, settings_get_int("indent"), !settings_get_bool("indent_always"), diff --git a/src/fe-text/irssi.c b/src/fe-text/irssi.c index c2013155..1b891522 100644 --- a/src/fe-text/irssi.c +++ b/src/fe-text/irssi.c @@ -71,6 +71,9 @@ void gui_expandos_deinit(void); void textbuffer_commands_init(void); void textbuffer_commands_deinit(void); +void textbuffer_formats_init(void); +void textbuffer_formats_deinit(void); + void lastlog_init(void); void lastlog_deinit(void); @@ -168,6 +171,7 @@ static void textui_finish_init(void) textbuffer_init(); textbuffer_view_init(); textbuffer_commands_init(); + textbuffer_formats_init(); gui_expandos_init(); gui_printtext_init(); gui_readline_init(); @@ -256,6 +260,7 @@ static void textui_deinit(void) mainwindow_activity_deinit(); mainwindows_deinit(); gui_expandos_deinit(); + textbuffer_formats_deinit(); textbuffer_commands_deinit(); textbuffer_view_deinit(); textbuffer_deinit(); diff --git a/src/fe-text/lastlog.c b/src/fe-text/lastlog.c index d5aa298a..54321832 100644 --- a/src/fe-text/lastlog.c +++ b/src/fe-text/lastlog.c @@ -199,7 +199,7 @@ static void show_lastlog(const char *searchtext, GHashTable *optlist, } /* get the line text */ - textbuffer_line2text(rec, fhandle == NULL, line); + textbuffer_line2text(WINDOW_GUI(window)->view->buffer, rec, fhandle == NULL, line); if (!settings_get_bool("timestamps")) { struct tm *tm = localtime(&rec->info.time); char timestamp[10]; diff --git a/src/fe-text/meson.build b/src/fe-text/meson.build index 9070011e..4da98809 100644 --- a/src/fe-text/meson.build +++ b/src/fe-text/meson.build @@ -23,6 +23,7 @@ executable('irssi', 'statusbar.c', 'term.c', 'textbuffer-commands.c', + 'textbuffer-formats.c', 'textbuffer-view.c', 'textbuffer.c', ) @@ -53,6 +54,7 @@ install_headers( 'statusbar-item.h', 'statusbar.h', 'term.h', + 'textbuffer-formats.h', 'textbuffer-view.h', 'textbuffer.h', ), diff --git a/src/fe-text/textbuffer-commands.c b/src/fe-text/textbuffer-commands.c index 11c0f123..f30eab0e 100644 --- a/src/fe-text/textbuffer-commands.c +++ b/src/fe-text/textbuffer-commands.c @@ -19,14 +19,15 @@ */ #include "module.h" -#include -#include #include -#include #include -#include -#include +#include #include +#include +#include +#include +#include +#include #include #include @@ -340,14 +341,29 @@ static void cmd_scrollback_status(void) total_lines = 0; total_mem = 0; for (tmp = windows; tmp != NULL; tmp = tmp->next) { WINDOW_REC *window = tmp->data; + int i; + LINE_REC *tmp; TEXT_BUFFER_VIEW_REC *view; view = WINDOW_GUI(window)->view; window_mem = sizeof(TEXT_BUFFER_REC); - window_mem += g_slist_length(view->buffer->text_chunks) * - sizeof(TEXT_CHUNK_REC); window_mem += view->buffer->lines_count * sizeof(LINE_REC); + for (tmp = view->buffer->cur_line; tmp != NULL; tmp = tmp->prev) { + if (tmp->info.text != NULL) { + window_mem += sizeof(char) * (strlen(tmp->info.text) + 1); + } + if (tmp->info.format != NULL) { + window_mem += sizeof(TEXT_BUFFER_FORMAT_REC); + for (i = 0; i < tmp->info.format->nargs; i++) { + if (tmp->info.format->args[i] != NULL) { + window_mem += + sizeof(char) * + (strlen(tmp->info.format->args[i]) + 1); + } + } + } + } total_lines += view->buffer->lines_count; total_mem += window_mem; printtext(NULL, NULL, MSGLEVEL_CLIENTCRAP, @@ -368,6 +384,19 @@ static void cmd_scrollback_status(void) } } +/* SYNTAX: SCROLLBACK REDRAW */ +static void cmd_scrollback_redraw(void) +{ + GUI_WINDOW_REC *gui; + + gui = WINDOW_GUI(active_win); + + term_refresh_freeze(); + textbuffer_view_reset_cache(gui->view); + gui_window_redraw(active_win); + term_refresh_thaw(); +} + static void sig_away_changed(SERVER_REC *server) { GSList *tmp; @@ -419,6 +448,7 @@ void textbuffer_commands_init(void) command_bind("scrollback home", NULL, (SIGNAL_FUNC) cmd_scrollback_home); command_bind("scrollback end", NULL, (SIGNAL_FUNC) cmd_scrollback_end); command_bind("scrollback status", NULL, (SIGNAL_FUNC) cmd_scrollback_status); + command_bind("scrollback redraw", NULL, (SIGNAL_FUNC) cmd_scrollback_redraw); command_set_options("clear", "all"); command_set_options("scrollback clear", "all"); @@ -442,6 +472,7 @@ void textbuffer_commands_deinit(void) command_unbind("scrollback home", (SIGNAL_FUNC) cmd_scrollback_home); command_unbind("scrollback end", (SIGNAL_FUNC) cmd_scrollback_end); command_unbind("scrollback status", (SIGNAL_FUNC) cmd_scrollback_status); + command_unbind("scrollback redraw", (SIGNAL_FUNC) cmd_scrollback_redraw); signal_remove("setup changed", (SIGNAL_FUNC) read_settings); signal_remove("away mode changed", (SIGNAL_FUNC) sig_away_changed); diff --git a/src/fe-text/textbuffer-formats.c b/src/fe-text/textbuffer-formats.c new file mode 100644 index 00000000..599a85c6 --- /dev/null +++ b/src/fe-text/textbuffer-formats.c @@ -0,0 +1,258 @@ +#include "module.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +TEXT_BUFFER_REC *color_buf; + +void textbuffer_format_rec_free(TEXT_BUFFER_FORMAT_REC *rec) +{ + int n; + + if (rec == NULL) + return; + if (rec == LINE_INFO_FORMAT_SET) + return; + + i_refstr_release(rec->module); + i_refstr_release(rec->format); + i_refstr_release(rec->server_tag); + i_refstr_release(rec->target); + i_refstr_release(rec->nick); + if (rec->nargs >= 1) { + i_refstr_release(rec->args[0]); + } + for (n = 1; n < rec->nargs; n++) { + g_free(rec->args[n]); + } + rec->nargs = 0; + g_free(rec->args); + g_slice_free(TEXT_BUFFER_FORMAT_REC, rec); +} + +static TEXT_BUFFER_FORMAT_REC *format_rec_new(const char *module, const char *format_tag, + const char *server_tag, const char *target, + const char *nick, int nargs, const char **args) +{ + int n; + TEXT_BUFFER_FORMAT_REC *ret = g_slice_new0(TEXT_BUFFER_FORMAT_REC); + ret->module = i_refstr_intern(module); + ret->format = i_refstr_intern(format_tag); + ret->server_tag = i_refstr_intern(server_tag); + ret->target = i_refstr_intern(target); + ret->nick = i_refstr_intern(nick); + ret->nargs = nargs; + ret->args = g_new0(char *, nargs); + if (nargs >= 1) { + ret->args[0] = i_refstr_intern(args[0]); + } + for (n = 1; n < nargs; n++) { + ret->args[n] = g_strdup(args[n]); + } + return ret; +} + +static LINE_INFO_REC *store_lineinfo_tmp(TEXT_DEST_REC *dest) +{ + GUI_WINDOW_REC *gui; + TEXT_BUFFER_VIEW_REC *view; + TEXT_BUFFER_REC *buffer; + LINE_INFO_REC *lineinfo; + + gui = WINDOW_GUI(dest->window); + view = gui->view; + buffer = view->buffer; + + lineinfo = g_new0(LINE_INFO_REC, 1); + lineinfo->level = dest->level; + lineinfo->time = + (gui->use_insert_after && gui->insert_after_time) ? gui->insert_after_time : time(NULL); + + buffer->cur_info = g_slist_prepend(buffer->cur_info, lineinfo); + return lineinfo; +} + +static void free_lineinfo_tmp(WINDOW_REC *window) +{ + GUI_WINDOW_REC *gui; + TEXT_BUFFER_REC *buffer; + LINE_INFO_REC *info; + + gui = WINDOW_GUI(window); + buffer = gui->view->buffer; + + if (buffer->cur_info == NULL) + return; + + info = buffer->cur_info->data; + buffer->cur_info = g_slist_delete_link(buffer->cur_info, buffer->cur_info); + textbuffer_format_rec_free(info->format); + g_free(info); +} + +static void sig_print_format(THEME_REC *theme, const char *module, TEXT_DEST_REC *dest, + void *formatnump, const char **args) +{ + int formatnum; + FORMAT_REC *formats; + LINE_INFO_REC *info; + + info = store_lineinfo_tmp(dest); + + formatnum = GPOINTER_TO_INT(formatnump); + formats = g_hash_table_lookup(default_formats, module); + + info->format = format_rec_new(module, formats[formatnum].tag, dest->server_tag, + dest->target, dest->nick, formats[formatnum].params, args); + + info->format->flags = dest->flags; + dest->flags |= PRINT_FLAG_FORMAT; + + signal_continue(5, theme, module, dest, formatnump, args); + + free_lineinfo_tmp(dest->window); +} + +static void sig_print_noformat(TEXT_DEST_REC *dest, const char *text) +{ + LINE_INFO_REC *info; + + info = store_lineinfo_tmp(dest); + + info->format = format_rec_new(NULL, NULL, dest->server_tag, dest->target, dest->nick, 2, + (const char *[]){ NULL, text }); + + info->format->flags = dest->flags; + dest->flags |= PRINT_FLAG_FORMAT; + + signal_continue(2, dest, text); + + free_lineinfo_tmp(dest->window); +} + +static void sig_gui_print_text_finished(WINDOW_REC *window) +{ + GUI_WINDOW_REC *gui; + LINE_REC *insert_after; + LINE_INFO_REC *info; + TEXT_BUFFER_REC *buffer; + + gui = WINDOW_GUI(window); + buffer = gui->view->buffer; + insert_after = gui->use_insert_after ? gui->insert_after : buffer->cur_line; + + if (buffer->cur_info == NULL) + return; + + info = buffer->cur_info->data; + + if (info->format == NULL) + return; + + info->level |= MSGLEVEL_FORMAT; + + /* the line will be inserted into the view with textbuffer_view_insert_line by + gui-printtext.c:view_add_eol */ + insert_after = textbuffer_insert(buffer, insert_after, (const unsigned char[]){}, 0, info); + + /* the TEXT_BUFFER_FORMAT_REC pointer is now owned by the textbuffer */ + info->format = LINE_INFO_FORMAT_SET; + + if (gui->use_insert_after) + gui->insert_after = insert_after; +} + +char *textbuffer_line_get_text(TEXT_BUFFER_REC *buffer, LINE_REC *line) +{ + GUI_WINDOW_REC *gui; + LINE_REC *curr; + + g_return_val_if_fail(buffer != NULL, NULL); + g_return_val_if_fail(buffer->window != NULL, NULL); + + gui = WINDOW_GUI(buffer->window); + if (line == NULL || gui == NULL) + return NULL; + + if (line->info.level & MSGLEVEL_FORMAT) { + TEXT_DEST_REC dest; + THEME_REC *theme; + int formatnum; + TEXT_BUFFER_FORMAT_REC *format_rec; + char *text, *tmp, *str; + + curr = line; + line = NULL; + format_rec = curr->info.format; + + format_create_dest( + &dest, + format_rec->server_tag != NULL ? server_find_tag(format_rec->server_tag) : NULL, + format_rec->target, curr->info.level & ~MSGLEVEL_FORMAT, buffer->window); + + theme = window_get_theme(dest.window); + + if (format_rec->format != NULL) { + char *arglist[MAX_FORMAT_PARAMS] = { 0 }; + formatnum = format_find_tag(format_rec->module, format_rec->format); + memcpy(arglist, format_rec->args, format_rec->nargs * sizeof(char *)); + text = format_get_text_theme_charargs(theme, format_rec->module, &dest, + formatnum, arglist); + } else { + text = g_strdup(format_rec->args[1]); + } + + if (*text != '\0') { + current_time = curr->info.time; + + tmp = format_get_level_tag(theme, &dest); + str = !theme->info_eol ? format_add_linestart(text, tmp) : + format_add_lineend(text, tmp); + g_free_not_null(tmp); + g_free_not_null(text); + text = str; + tmp = format_get_line_start(theme, &dest, curr->info.time); + str = !theme->info_eol ? format_add_linestart(text, tmp) : + format_add_lineend(text, tmp); + g_free_not_null(tmp); + g_free_not_null(text); + text = str; + /* str = g_strconcat(text, "\n", NULL); */ + /* g_free(text); */ + + dest.flags |= PRINT_FLAG_FORMAT; + + current_time = (time_t) -1; + return str; + } else if (format_rec->format != NULL) { + g_free(text); + return NULL; + } else { + return text; + } + } else { + return g_strdup(line->info.text); + } +} + +void textbuffer_formats_init(void) +{ + signal_add("print format", (SIGNAL_FUNC) sig_print_format); + signal_add("print noformat", (SIGNAL_FUNC) sig_print_noformat); + signal_add_first("gui print text finished", (SIGNAL_FUNC) sig_gui_print_text_finished); +} + +void textbuffer_formats_deinit(void) +{ + signal_remove("print format", (SIGNAL_FUNC) sig_print_format); + signal_remove("print noformat", (SIGNAL_FUNC) sig_print_noformat); + signal_remove("gui print text finished", (SIGNAL_FUNC) sig_gui_print_text_finished); +} diff --git a/src/fe-text/textbuffer-formats.h b/src/fe-text/textbuffer-formats.h new file mode 100644 index 00000000..5a156fda --- /dev/null +++ b/src/fe-text/textbuffer-formats.h @@ -0,0 +1,22 @@ +#ifndef IRSSI_FE_TEXT_TEXTBUFFER_FORMATS_H +#define IRSSI_FE_TEXT_TEXTBUFFER_FORMATS_H + +#include + +typedef struct _TEXT_BUFFER_FORMAT_REC { + char *module; + char *format; + char *server_tag; + char *target; + char *nick; + char **args; + int nargs; + int flags; +} TEXT_BUFFER_FORMAT_REC; + +void textbuffer_format_rec_free(TEXT_BUFFER_FORMAT_REC *rec); +char *textbuffer_line_get_text(TEXT_BUFFER_REC *buffer, LINE_REC *line); +void textbuffer_formats_init(void); +void textbuffer_formats_deinit(void); + +#endif diff --git a/src/fe-text/textbuffer-view.c b/src/fe-text/textbuffer-view.c index 45035fa8..0bbb3655 100644 --- a/src/fe-text/textbuffer-view.c +++ b/src/fe-text/textbuffer-view.c @@ -21,9 +21,12 @@ #define G_LOG_DOMAIN "TextBufferView" #include "module.h" -#include +#include #include #include +#include +#include +#include typedef struct { char *name; @@ -93,6 +96,7 @@ textbuffer_cache_get(GSList *views, int width) static int line_cache_destroy(void *key, LINE_CACHE_REC *cache) { + g_free(cache->line_text); g_free(cache); return TRUE; } @@ -114,52 +118,6 @@ static void textbuffer_cache_unref(TEXT_BUFFER_CACHE_REC *cache) #define FGATTR (ATTR_NOCOLORS | ATTR_RESETFG | FG_MASK | ATTR_FGCOLOR24) #define BGATTR (ATTR_NOCOLORS | ATTR_RESETBG | BG_MASK | ATTR_BGCOLOR24) -static void update_cmd_color(unsigned char cmd, int *color) -{ - if ((cmd & 0x80) == 0) { - if (cmd & LINE_COLOR_BG) { - /* set background color */ - *color &= FGATTR; - if ((cmd & LINE_COLOR_DEFAULT) == 0) - *color |= (cmd & 0x0f) << BG_SHIFT; - else { - *color = (*color & FGATTR) | ATTR_RESETBG; - } - } else { - /* set foreground color */ - *color &= BGATTR; - if ((cmd & LINE_COLOR_DEFAULT) == 0) - *color |= cmd & 0x0f; - else { - *color = (*color & BGATTR) | ATTR_RESETFG; - } - } - } else switch (cmd) { - case LINE_CMD_UNDERLINE: - *color ^= ATTR_UNDERLINE; - break; - case LINE_CMD_REVERSE: - *color ^= ATTR_REVERSE; - break; - case LINE_CMD_BLINK: - *color ^= ATTR_BLINK; - break; - case LINE_CMD_BOLD: - *color ^= ATTR_BOLD; - break; - case LINE_CMD_ITALIC: - *color ^= ATTR_ITALIC; - break; - case LINE_CMD_MONOSPACE: - /* ignored */ - break; - case LINE_CMD_COLOR0: - *color &= BGATTR; - *color &= ~ATTR_FGCOLOR24; - break; - } -} - #ifdef TERM_TRUECOLOR static void unformat_24bit_line_color(const unsigned char **ptr, int off, int *flags, unsigned int *fg, unsigned int *bg) { @@ -167,6 +125,8 @@ static void unformat_24bit_line_color(const unsigned char **ptr, int off, int *f unsigned char rgbx[4]; unsigned int i; for (i = 0; i < 4; ++i) { + if ((*ptr)[i + off] == '\0') + return; rgbx[i] = (*ptr)[i + off]; } rgbx[3] -= 0x20; @@ -202,6 +162,94 @@ static inline unichar read_unichar(const unsigned char *data, const unsigned cha return chr; } +static inline void unformat(const unsigned char **ptr, int *color, unsigned int *fg24, + unsigned int *bg24) +{ + switch (**ptr) { + case FORMAT_STYLE_BLINK: + *color ^= ATTR_BLINK; + break; + case FORMAT_STYLE_UNDERLINE: + *color ^= ATTR_UNDERLINE; + break; + case FORMAT_STYLE_BOLD: + *color ^= ATTR_BOLD; + break; + case FORMAT_STYLE_REVERSE: + *color ^= ATTR_REVERSE; + break; + case FORMAT_STYLE_ITALIC: + *color ^= ATTR_ITALIC; + break; + case FORMAT_STYLE_MONOSPACE: + /* *color ^= ATTR_MONOSPACE; */ + break; + case FORMAT_STYLE_DEFAULTS: + *color = ATTR_RESET; + break; + case FORMAT_STYLE_CLRTOEOL: + break; + case FORMAT_COLOR_EXT1: + *color &= ~ATTR_FGCOLOR24; + *color = (*color & BGATTR) | (0x10 + *++*ptr - FORMAT_COLOR_NOCHANGE); + break; + case FORMAT_COLOR_EXT1_BG: + *color &= ~ATTR_BGCOLOR24; + *color = (*color & FGATTR) | (0x10 + *++*ptr - FORMAT_COLOR_NOCHANGE); + break; + case FORMAT_COLOR_EXT2: + *color &= ~ATTR_FGCOLOR24; + *color = (*color & BGATTR) | (0x60 + *++*ptr - FORMAT_COLOR_NOCHANGE); + break; + case FORMAT_COLOR_EXT2_BG: + *color &= ~ATTR_BGCOLOR24; + *color = (*color & FGATTR) | (0x60 + *++*ptr - FORMAT_COLOR_NOCHANGE); + break; + case FORMAT_COLOR_EXT3: + *color &= ~ATTR_FGCOLOR24; + *color = (*color & BGATTR) | (0xb0 + *++*ptr - FORMAT_COLOR_NOCHANGE); + break; + case FORMAT_COLOR_EXT3_BG: + *color &= ~ATTR_BGCOLOR24; + *color = (*color & FGATTR) | (0xb0 + *++*ptr - FORMAT_COLOR_NOCHANGE); + break; +#ifdef TERM_TRUECOLOR + case FORMAT_COLOR_24: + unformat_24bit_line_color(ptr, 1, color, fg24, bg24); + break; +#endif + default: + if (**ptr != FORMAT_COLOR_NOCHANGE) { + if (**ptr == (unsigned char) 0xff) { + *color = (*color & BGATTR) | ATTR_RESETFG; + } else { + *color = (*color & BGATTR) | (((unsigned char) **ptr - '0') & 0xf); + } + } + if ((*ptr)[1] == '\0') + break; + + (*ptr)++; + if (**ptr != FORMAT_COLOR_NOCHANGE) { + if (**ptr == (unsigned char) 0xff) { + *color = (*color & FGATTR) | ATTR_RESETBG; + } else { + *color = (*color & FGATTR) | + ((((unsigned char) **ptr - '0') & 0xf) << BG_SHIFT); + } + } + } + if (**ptr == '\0') + return; + + (*ptr)++; +} + +#define NEXT_CHAR_OR_BREAK(p) \ + (p)++; \ + if (*(p) == '\0') \ + break + static LINE_CACHE_REC * view_update_line_cache(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line) { @@ -209,14 +257,12 @@ view_update_line_cache(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line) LINE_CACHE_REC *rec; LINE_CACHE_SUB_REC *sub; GSList *lines; - unsigned char cmd; + char *line_text; const unsigned char *ptr, *next_ptr, *last_space_ptr; int xpos, pos, indent_pos, last_space, last_color, color, linecount; unsigned int last_bg24, last_fg24, bg24, fg24; int char_width; - g_return_val_if_fail(line->text != NULL, NULL); - color = ATTR_RESETFG | ATTR_RESETBG; xpos = 0; indent_pos = view->default_indent; last_space = last_color = 0; last_space_ptr = NULL; sub = NULL; @@ -225,114 +271,132 @@ view_update_line_cache(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line) indent_func = view->default_indent_func; linecount = 1; lines = NULL; - for (ptr = line->text;;) { - if (*ptr == '\0') { - /* command */ - ptr++; - cmd = *ptr; - ptr++; - if (cmd == LINE_CMD_EOL) + line_text = textbuffer_line_get_text(view->buffer, line); + if (line_text != NULL) { + for (ptr = (unsigned char *) line_text;;) { + if (*ptr == '\0') break; - if (cmd == LINE_CMD_CONTINUE) { - unsigned char *tmp; + if (*ptr == '\n') { + /* newline */ + xpos = 0; + last_space = 0; + + sub = g_new0(LINE_CACHE_SUB_REC, 1); + + sub->start = ++ptr; + sub->color = color; +#ifdef TERM_TRUECOLOR + sub->fg24 = fg24; + sub->bg24 = bg24; +#endif + + lines = g_slist_append(lines, sub); + linecount++; - memcpy(&tmp, ptr, sizeof(char *)); - ptr = tmp; continue; } - if (cmd == LINE_CMD_INDENT) { - /* set indentation position here - don't do - it if we're too close to right border */ - if (xpos < view->width-5) indent_pos = xpos; - } else if (cmd == LINE_COLOR_EXT) { - color &= ~ATTR_FGCOLOR24; - color = (color & BGATTR) | *ptr++; - } else if (cmd == LINE_COLOR_EXT_BG) { - color &= ~ATTR_BGCOLOR24; - color = (color & FGATTR) | (*ptr++ << BG_SHIFT); - } -#ifdef TERM_TRUECOLOR - else if (cmd == LINE_COLOR_24) - unformat_24bit_line_color(&ptr, 0, &color, &fg24, &bg24); -#endif - else - update_cmd_color(cmd, &color); - continue; - } + if (*ptr == 4) { + /* format */ + NEXT_CHAR_OR_BREAK(ptr); - if (!view->utf8) { - /* MH */ - if (term_type != TERM_TYPE_BIG5 || - ptr[1] == '\0' || !is_big5(ptr[0], ptr[1])) - char_width = 1; - else - char_width = 2; - next_ptr = ptr+char_width; - } else { - read_unichar(ptr, &next_ptr, &char_width); - } - - if (xpos + char_width > view->width && sub != NULL && - (last_space <= indent_pos || last_space <= 10) && - view->longword_noindent) { - /* long word, remove the indentation from this line */ - xpos -= sub->indent; - sub->indent = 0; - sub->indent_func = NULL; - } - - if (xpos + char_width > view->width) { - xpos = indent_func == NULL ? indent_pos : - indent_func(view, line, -1); - - sub = g_new0(LINE_CACHE_SUB_REC, 1); - if (last_space > indent_pos && last_space > 10) { - /* go back to last space */ - color = last_color; fg24 = last_fg24; bg24 = last_bg24; - ptr = last_space_ptr; - while (*ptr == ' ') ptr++; - } else if (view->longword_noindent) { - /* long word, no indentation in next line */ - xpos = 0; - sub->continues = TRUE; + if (*ptr == FORMAT_STYLE_INDENT) { + /* set indentation position here - don't do + it if we're too close to right border */ + if (xpos < view->width - 5) + indent_pos = xpos; + ptr++; + } else { + unformat(&ptr, &color, &fg24, &bg24); + } + continue; } - sub->start = ptr; - sub->indent = xpos; - sub->indent_func = indent_func; - sub->color = color; + if (!view->utf8) { + /* MH */ + if (term_type != TERM_TYPE_BIG5 || ptr[1] == '\0' || + !is_big5(ptr[0], ptr[1])) + char_width = 1; + else + char_width = 2; + next_ptr = ptr + char_width; + } else { + read_unichar(ptr, &next_ptr, &char_width); + } + + if (xpos + char_width > view->width && sub != NULL && + (last_space <= indent_pos || last_space <= 10) && + view->longword_noindent) { + /* long word, remove the indentation from this line */ + xpos -= sub->indent; + sub->indent = 0; + sub->indent_func = NULL; + } + + if (xpos + char_width > view->width) { + xpos = + indent_func == NULL ? indent_pos : indent_func(view, line, -1); + + sub = g_new0(LINE_CACHE_SUB_REC, 1); + if (last_space > indent_pos && last_space > 10) { + /* go back to last space */ + color = last_color; + fg24 = last_fg24; + bg24 = last_bg24; + ptr = last_space_ptr; + while (*ptr == ' ') + ptr++; + } else if (view->longword_noindent) { + /* long word, no indentation in next line */ + xpos = 0; + sub->continues = TRUE; + } + + sub->start = ptr; + sub->indent = xpos; + sub->indent_func = indent_func; + sub->color = color; #ifdef TERM_TRUECOLOR - sub->fg24 = fg24; sub->bg24 = bg24; + sub->fg24 = fg24; + sub->bg24 = bg24; #endif - lines = g_slist_append(lines, sub); - linecount++; + lines = g_slist_append(lines, sub); + linecount++; - last_space = 0; - continue; + last_space = 0; + continue; + } + + if (view->break_wide && char_width > 1) { + last_space = xpos; + last_space_ptr = next_ptr; + last_color = color; + last_fg24 = fg24; + last_bg24 = bg24; + } else if (*ptr == ' ') { + last_space = xpos; + last_space_ptr = ptr; + last_color = color; + last_fg24 = fg24; + last_bg24 = bg24; + } + + xpos += char_width; + ptr = next_ptr; } - - if (view->break_wide && char_width > 1) { - last_space = xpos; - last_space_ptr = next_ptr; - last_color = color; last_fg24 = fg24; last_bg24 = bg24; - } else if (*ptr == ' ') { - last_space = xpos; - last_space_ptr = ptr; - last_color = color; last_fg24 = fg24; last_bg24 = bg24; - } - - xpos += char_width; - ptr = next_ptr; } rec = g_malloc(sizeof(LINE_CACHE_REC)-sizeof(LINE_CACHE_SUB_REC) + sizeof(LINE_CACHE_SUB_REC) * (linecount-1)); rec->last_access = time(NULL); + if (line_text == NULL) { + linecount = 0; + } rec->count = linecount; + rec->line_text = line_text; if (rec->count > 1) { for (pos = 0; lines != NULL; pos++) { @@ -398,12 +462,9 @@ static int view_line_draw(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line, INDENT_FUNC indent_func; LINE_CACHE_REC *cache; const unsigned char *text, *end, *text_newline; - unsigned char *tmp; unichar chr; int xpos, color, drawcount, first, need_move, need_clrtoeol, char_width; -#ifdef TERM_TRUECOLOR unsigned int fg24, bg24; -#endif if (view->dirty) /* don't bother drawing anything - redraw is coming */ return 0; @@ -416,7 +477,8 @@ static int view_line_draw(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line, need_move = TRUE; need_clrtoeol = FALSE; xpos = drawcount = 0; first = TRUE; text_newline = text = - subline == 0 ? line->text : cache->lines[subline-1].start; + subline == 0 ? (unsigned char *) cache->line_text : cache->lines[subline - 1].start; + for (;;) { if (text == text_newline) { if (need_clrtoeol && xpos < view->width + (view->width == term_width ? 0 : 1)) { @@ -484,31 +546,29 @@ static int view_line_draw(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line, subline++; } - if (*text == '\0') { - /* command */ - text++; - if (*text == LINE_CMD_EOL) - break; + if (*text == '\n') { + /* newline */ + NEXT_CHAR_OR_BREAK(text); + continue; + } - if (*text == LINE_CMD_CONTINUE) { - /* jump to next block */ - memcpy(&tmp, text+1, sizeof(unsigned char *)); - text = tmp; - continue; + if (*text == 0) { + break; + } + + if (*text == 4) { + /* format */ + NEXT_CHAR_OR_BREAK(text); + + if (*text == FORMAT_STYLE_INDENT) { + /* ??? */ + NEXT_CHAR_OR_BREAK(text); } else { - if (*text == LINE_COLOR_EXT) - color = (color & BGATTR & ~ATTR_FGCOLOR24) | *++text; - else if (*text == LINE_COLOR_EXT_BG) - color = (color & FGATTR & ~ATTR_BGCOLOR24) | (*++text << BG_SHIFT); -#ifdef TERM_TRUECOLOR - else if (*text == LINE_COLOR_24) - unformat_24bit_line_color(&text, 1, &color, &fg24, &bg24); -#endif - else - update_cmd_color(*text, &color); + unformat(&text, &color, &fg24, &bg24); term_set_color2(view->window, color, fg24, bg24); + if (*text == 0) + break; } - text++; continue; } diff --git a/src/fe-text/textbuffer-view.h b/src/fe-text/textbuffer-view.h index 607016ac..4e5ed82f 100644 --- a/src/fe-text/textbuffer-view.h +++ b/src/fe-text/textbuffer-view.h @@ -27,6 +27,7 @@ typedef struct { typedef struct { time_t last_access; + char *line_text; int count; /* number of real lines */ /* variable sized array, actually. starts from the second line, diff --git a/src/fe-text/textbuffer.c b/src/fe-text/textbuffer.c index 470c0b00..b8fd2090 100644 --- a/src/fe-text/textbuffer.c +++ b/src/fe-text/textbuffer.c @@ -26,176 +26,59 @@ #include #include +#include #include #define TEXT_CHUNK_USABLE_SIZE (LINE_TEXT_CHUNK_SIZE-2-(int)sizeof(char*)) -TEXT_BUFFER_REC *textbuffer_create(void) +TEXT_BUFFER_REC *textbuffer_create(WINDOW_REC *window) { TEXT_BUFFER_REC *buffer; buffer = g_slice_new0(TEXT_BUFFER_REC); + buffer->window = window; buffer->last_eol = TRUE; buffer->last_fg = -1; buffer->last_bg = -1; - return buffer; + buffer->cur_text = g_string_sized_new(TEXT_CHUNK_USABLE_SIZE); + return buffer; } void textbuffer_destroy(TEXT_BUFFER_REC *buffer) { + GSList *tmp; + g_return_if_fail(buffer != NULL); textbuffer_remove_all_lines(buffer); - g_slice_free(TEXT_BUFFER_REC, buffer); -} - -static TEXT_CHUNK_REC *text_chunk_find(TEXT_BUFFER_REC *buffer, - const unsigned char *data) -{ - GSList *tmp; - - for (tmp = buffer->text_chunks; tmp != NULL; tmp = tmp->next) { - TEXT_CHUNK_REC *rec = tmp->data; - - if (data >= rec->buffer && - data < rec->buffer+sizeof(rec->buffer)) - return rec; + g_string_free(buffer->cur_text, TRUE); + for (tmp = buffer->cur_info; tmp != NULL; tmp = tmp->next) { + LINE_INFO_REC *info = buffer->cur_info->data; + textbuffer_format_rec_free(info->format); + g_free(info->text); + g_free(info); } + g_slist_free(buffer->cur_info); - return NULL; -} - -#define mark_temp_eol(chunk) G_STMT_START { \ - (chunk)->buffer[(chunk)->pos] = 0; \ - (chunk)->buffer[(chunk)->pos+1] = LINE_CMD_EOL; \ - } G_STMT_END - -static TEXT_CHUNK_REC *text_chunk_create(TEXT_BUFFER_REC *buffer) -{ - TEXT_CHUNK_REC *rec; - unsigned char *buf, *ptr, **pptr; - - rec = g_slice_new(TEXT_CHUNK_REC); - rec->pos = 0; - rec->refcount = 0; - - if (buffer->cur_line != NULL && buffer->cur_line->text != NULL) { - /* create a link to new block from the old block */ - buf = buffer->cur_text->buffer + buffer->cur_text->pos; - *buf++ = 0; *buf++ = (char) LINE_CMD_CONTINUE; - - /* we want to store pointer to beginning of the new text - block to char* buffer. this probably isn't ANSI-C - compatible, and trying this without the pptr variable - breaks at least NetBSD/Alpha, so don't go "optimize" - it :) */ - ptr = rec->buffer; pptr = &ptr; - memcpy(buf, pptr, sizeof(unsigned char *)); - } else { - /* just to be safe */ - mark_temp_eol(rec); - } - - buffer->cur_text = rec; - buffer->text_chunks = g_slist_append(buffer->text_chunks, rec); - return rec; -} - -static void text_chunk_destroy(TEXT_BUFFER_REC *buffer, TEXT_CHUNK_REC *chunk) -{ - buffer->text_chunks = g_slist_remove(buffer->text_chunks, chunk); - g_slice_free(TEXT_CHUNK_REC, chunk); -} - -static void text_chunk_line_free(TEXT_BUFFER_REC *buffer, LINE_REC *line) -{ - TEXT_CHUNK_REC *chunk; - const unsigned char *text; - unsigned char cmd, *tmp = NULL; - - for (text = line->text;; text++) { - if (*text != '\0') - continue; - - text++; - cmd = *text; - if (cmd == LINE_CMD_CONTINUE || cmd == LINE_CMD_EOL) { - if (cmd == LINE_CMD_CONTINUE) - memcpy(&tmp, text+1, sizeof(char *)); - - /* free the previous block */ - chunk = text_chunk_find(buffer, text); - if (--chunk->refcount == 0) { - if (buffer->cur_text == chunk) - chunk->pos = 0; - else - text_chunk_destroy(buffer, chunk); - } - - if (cmd == LINE_CMD_EOL) - break; - - text = tmp-1; - } - } + buffer->window = NULL; + g_slice_free(TEXT_BUFFER_REC, buffer); } static void text_chunk_append(TEXT_BUFFER_REC *buffer, const unsigned char *data, int len) { - TEXT_CHUNK_REC *chunk; - int left; - int i; - if (len == 0) return; - chunk = buffer->cur_text; - while (chunk->pos + len >= TEXT_CHUNK_USABLE_SIZE) { - left = TEXT_CHUNK_USABLE_SIZE - chunk->pos; - - /* don't split utf-8 character. (assume we can split non-utf8 anywhere.) */ - if (left < len && !is_utf8_leading(data[left])) { - int i; - for (i = 1; i < 4 && left >= i; i++) - if (is_utf8_leading(data[left - i])) { - left -= i; - break; - } - } - - for (i = 5; i > 0; --i) { - if (left >= i && data[left-i] == 0) { - left -= i; /* don't split the commands */ - break; - } - } - - memcpy(chunk->buffer + chunk->pos, data, left); - chunk->pos += left; - - chunk = text_chunk_create(buffer); - chunk->refcount++; - len -= left; data += left; - } - - memcpy(chunk->buffer + chunk->pos, data, len); - chunk->pos += len; - - mark_temp_eol(chunk); + /* g_string_append_len(buffer->cur_text, (const char *)data, len); */ + g_string_append(buffer->cur_text, (const char *) data); } static LINE_REC *textbuffer_line_create(TEXT_BUFFER_REC *buffer) { LINE_REC *rec; - if (buffer->cur_text == NULL) - text_chunk_create(buffer); - rec = g_slice_new0(LINE_REC); - rec->text = buffer->cur_text->buffer + buffer->cur_text->pos; - - buffer->cur_text->refcount++; return rec; } @@ -241,103 +124,18 @@ int textbuffer_line_exists_after(LINE_REC *line, LINE_REC *search) return FALSE; } -#ifdef TERM_TRUECOLOR -static void format_24bit_line_color(unsigned char *out, int *pos, int bg, unsigned int color) -{ - unsigned char rgb[] = { color >> 16, color >> 8, color }; - unsigned char x = bg ? 0x1 : 0; - unsigned int i; - out[(*pos)++] = LINE_COLOR_24; - for (i = 0; i < 3; ++i) { - if (rgb[i] > 0x20) - out[(*pos)++] = rgb[i]; - else { - out[(*pos)++] = 0x20 + rgb[i]; - x |= 0x10 << i; - } - } - out[(*pos)++] = 0x20 + x; -} -#endif - void textbuffer_line_add_colors(TEXT_BUFFER_REC *buffer, LINE_REC **line, int fg, int bg, int flags) { - unsigned char data[22]; - int pos; + GString *out = g_string_new(NULL); + format_gui_flags(out, &buffer->last_fg, &buffer->last_bg, &buffer->last_flags, fg, bg, + flags); - pos = 0; - if (fg != buffer->last_fg - || (flags & GUI_PRINT_FLAG_COLOR_24_FG) != (buffer->last_flags & GUI_PRINT_FLAG_COLOR_24_FG)) { - buffer->last_fg = fg; - data[pos++] = 0; -#ifdef TERM_TRUECOLOR - if (flags & GUI_PRINT_FLAG_COLOR_24_FG) - format_24bit_line_color(data, &pos, 0, fg); - else -#endif - if (fg < 0) - data[pos++] = LINE_COLOR_DEFAULT; - else if (fg < 16) - data[pos++] = fg == 0 ? LINE_CMD_COLOR0 : fg; - else if (fg < 256) { - data[pos++] = LINE_COLOR_EXT; - data[pos++] = fg; - } + if (*(out->str) != '\0') { + *line = + textbuffer_insert(buffer, *line, (unsigned char *) out->str, out->len, NULL); } - if (bg != buffer->last_bg - || (flags & GUI_PRINT_FLAG_COLOR_24_BG) != (buffer->last_flags & GUI_PRINT_FLAG_COLOR_24_BG)) { - buffer->last_bg = bg; - data[pos++] = 0; -#ifdef TERM_TRUECOLOR - if (flags & GUI_PRINT_FLAG_COLOR_24_BG) - format_24bit_line_color(data, &pos, 1, bg); - else -#endif - if (bg < 0) - data[pos++] = LINE_COLOR_BG | LINE_COLOR_DEFAULT; - else if (bg < 16) - data[pos++] = LINE_COLOR_BG | bg; - else if (bg < 256) { - data[pos++] = LINE_COLOR_EXT_BG; - data[pos++] = bg; - } - } - - if ((flags & GUI_PRINT_FLAG_UNDERLINE) != (buffer->last_flags & GUI_PRINT_FLAG_UNDERLINE)) { - data[pos++] = 0; - data[pos++] = LINE_CMD_UNDERLINE; - } - if ((flags & GUI_PRINT_FLAG_REVERSE) != (buffer->last_flags & GUI_PRINT_FLAG_REVERSE)) { - data[pos++] = 0; - data[pos++] = LINE_CMD_REVERSE; - } - if ((flags & GUI_PRINT_FLAG_BLINK) != (buffer->last_flags & GUI_PRINT_FLAG_BLINK)) { - data[pos++] = 0; - data[pos++] = LINE_CMD_BLINK; - } - if ((flags & GUI_PRINT_FLAG_BOLD) != (buffer->last_flags & GUI_PRINT_FLAG_BOLD)) { - data[pos++] = 0; - data[pos++] = LINE_CMD_BOLD; - } - if ((flags & GUI_PRINT_FLAG_ITALIC) != (buffer->last_flags & GUI_PRINT_FLAG_ITALIC)) { - data[pos++] = 0; - data[pos++] = LINE_CMD_ITALIC; - } - if ((flags & GUI_PRINT_FLAG_MONOSPACE) != (buffer->last_flags & GUI_PRINT_FLAG_MONOSPACE)) { - data[pos++] = 0; - data[pos++] = LINE_CMD_MONOSPACE; - } - if (flags & GUI_PRINT_FLAG_INDENT) { - data[pos++] = 0; - data[pos++] = LINE_CMD_INDENT; - } - - if (pos > 0) { - *line = textbuffer_insert(buffer, *line, data, pos, NULL); - } - - buffer->last_flags = flags; + g_string_free(out, TRUE); } LINE_REC *textbuffer_append(TEXT_BUFFER_REC *buffer, @@ -368,6 +166,11 @@ LINE_REC *textbuffer_insert(TEXT_BUFFER_REC *buffer, LINE_REC *insert_after, data[len-2] == 0 && data[len-1] == LINE_CMD_EOL; if (buffer->last_eol) { + if (!line->info.format) { + line->info.text = g_strdup(buffer->cur_text->str); + g_string_truncate(buffer->cur_text, 0); + } + buffer->last_fg = -1; buffer->last_bg = -1; buffer->last_flags = 0; @@ -395,145 +198,50 @@ void textbuffer_remove(TEXT_BUFFER_REC *buffer, LINE_REC *line) line->prev = line->next = NULL; buffer->lines_count--; - text_chunk_line_free(buffer, line); + g_free(line->info.text); + textbuffer_format_rec_free(line->info.format); g_slice_free(LINE_REC, line); } /* Removes all lines from buffer */ void textbuffer_remove_all_lines(TEXT_BUFFER_REC *buffer) { - GSList *tmp; LINE_REC *line; g_return_if_fail(buffer != NULL); - for (tmp = buffer->text_chunks; tmp != NULL; tmp = tmp->next) - g_slice_free(TEXT_CHUNK_REC, tmp->data); - g_slist_free(buffer->text_chunks); - buffer->text_chunks = NULL; - while (buffer->first_line != NULL) { line = buffer->first_line->next; + g_free(buffer->first_line->info.text); + textbuffer_format_rec_free(buffer->first_line->info.format); g_slice_free(LINE_REC, buffer->first_line); buffer->first_line = line; } buffer->lines_count = 0; buffer->cur_line = NULL; - buffer->cur_text = NULL; + g_string_truncate(buffer->cur_text, 0); buffer->last_eol = TRUE; } -static void set_color(GString *str, int cmd) +void textbuffer_line2text(TEXT_BUFFER_REC *buffer, LINE_REC *line, int coloring, GString *str) { - int color = -1; - - if (!(cmd & LINE_COLOR_DEFAULT)) - color = (cmd & 0x0f)+'0'; - - if ((cmd & LINE_COLOR_BG) == 0) { - /* change foreground color */ - g_string_append_printf(str, "\004%c%c", - color, FORMAT_COLOR_NOCHANGE); - } else { - /* change background color */ - g_string_append_printf(str, "\004%c%c", - FORMAT_COLOR_NOCHANGE, color); - } -} - -void textbuffer_line2text(LINE_REC *line, int coloring, GString *str) -{ - unsigned char cmd, *ptr, *tmp; + char *ptr, *tmp; g_return_if_fail(line != NULL); g_return_if_fail(str != NULL); g_string_truncate(str, 0); - for (ptr = line->text;;) { - if (*ptr != 0) { - g_string_append_c(str, (char) *ptr); - ptr++; - continue; - } - - ptr++; - cmd = *ptr; - ptr++; - - if (cmd == LINE_CMD_EOL) { - /* end of line */ - break; - } - - if (cmd == LINE_CMD_CONTINUE) { - /* line continues in another address.. */ - memcpy(&tmp, ptr, sizeof(unsigned char *)); - ptr = tmp; - continue; - } - - if (!coloring) { - /* no colors, skip coloring commands */ - if (cmd == LINE_COLOR_EXT || cmd == LINE_COLOR_EXT_BG) - ptr++; -#ifdef TERM_TRUECOLOR - else if (cmd == LINE_COLOR_24) - ptr+=4; -#endif - - continue; - } - - if ((cmd & LINE_CMD_EOL) == 0) { - /* set color */ - set_color(str, cmd); - } else switch (cmd) { - case LINE_CMD_UNDERLINE: - g_string_append_c(str, 31); - break; - case LINE_CMD_REVERSE: - g_string_append_c(str, 22); - break; - case LINE_CMD_BLINK: - g_string_append_printf(str, "\004%c", - FORMAT_STYLE_BLINK); - break; - case LINE_CMD_BOLD: - g_string_append_printf(str, "\004%c", - FORMAT_STYLE_BOLD); - break; - case LINE_CMD_ITALIC: - g_string_append_printf(str, "\004%c", - FORMAT_STYLE_ITALIC); - break; - case LINE_CMD_MONOSPACE: - g_string_append_printf(str, "\004%c", - FORMAT_STYLE_MONOSPACE); - break; - case LINE_CMD_COLOR0: - g_string_append_printf(str, "\004%c%c", - '0', FORMAT_COLOR_NOCHANGE); - break; - case LINE_CMD_INDENT: - g_string_append_printf(str, "\004%c", - FORMAT_STYLE_INDENT); - break; - case LINE_COLOR_EXT: - format_ext_color(str, 0, *ptr++); - break; - case LINE_COLOR_EXT_BG: - format_ext_color(str, 1, *ptr++); - break; -#ifdef TERM_TRUECOLOR - case LINE_COLOR_24: - g_string_append_printf(str, "\004%c", FORMAT_COLOR_24); - break; -#endif - } + ptr = textbuffer_line_get_text(buffer, line); + if (coloring == 0) { + tmp = ptr; + ptr = strip_codes(tmp); + g_free(tmp); } + g_string_append(str, ptr); + g_free(ptr); } GList *textbuffer_find_text(TEXT_BUFFER_REC *buffer, LINE_REC *startline, @@ -575,7 +283,7 @@ GList *textbuffer_find_text(TEXT_BUFFER_REC *buffer, LINE_REC *startline, (line->info.level & nolevel) == 0; if (*text != '\0') { - textbuffer_line2text(line, FALSE, str); + textbuffer_line2text(buffer, line, FALSE, str); if (line_matched) { line_matched = regexp ? diff --git a/src/fe-text/textbuffer.h b/src/fe-text/textbuffer.h index cbe4fbf0..5aaa817c 100644 --- a/src/fe-text/textbuffer.h +++ b/src/fe-text/textbuffer.h @@ -5,33 +5,23 @@ wastes a lot of memory. */ #define LINE_TEXT_CHUNK_SIZE (16384 - 16) -#define LINE_COLOR_BG 0x20 -#define LINE_COLOR_DEFAULT 0x10 +#define LINE_INFO_FORMAT_SET (void *) 0x1 enum { LINE_CMD_EOL=0x80, /* line ends here */ - LINE_CMD_CONTINUE, /* line continues in next block */ - LINE_CMD_COLOR0, /* change to black, would be same as \0\0 but it breaks things.. */ - LINE_CMD_UNDERLINE, /* enable/disable underlining */ - LINE_CMD_REVERSE, /* enable/disable reversed text */ - LINE_CMD_INDENT, /* if line is split, indent it at this position */ - LINE_CMD_BLINK, /* enable/disable blink */ - LINE_CMD_BOLD, /* enable/disable bold */ - LINE_CMD_ITALIC, /* enable/disable italic */ - LINE_CMD_MONOSPACE, /* enable/disable monospace (gui only) */ - LINE_COLOR_EXT, /* extended color */ - LINE_COLOR_EXT_BG, /* extended bg */ -#ifdef TERM_TRUECOLOR - LINE_COLOR_24, /* 24bit color */ -#endif }; +struct _TEXT_BUFFER_FORMAT_REC; + typedef struct { int level; time_t time; + char *text; + struct _TEXT_BUFFER_FORMAT_REC *format; } LINE_INFO_REC; typedef struct _LINE_REC { + struct _LINE_REC *prev, *next; /* Text in the line. \0 means that the next char will be a color or command. @@ -45,9 +35,6 @@ typedef struct _LINE_REC { DO NOT ADD BLACK WITH \0\0 - this will break things. Use LINE_CMD_COLOR0 instead. */ - struct _LINE_REC *prev, *next; - - unsigned char *text; LINE_INFO_REC info; } LINE_REC; @@ -58,12 +45,14 @@ typedef struct { } TEXT_CHUNK_REC; typedef struct { - GSList *text_chunks; - LINE_REC *first_line; - int lines_count; + WINDOW_REC *window; + + LINE_REC *first_line; + int lines_count; LINE_REC *cur_line; - TEXT_CHUNK_REC *cur_text; + GString *cur_text; + GSList *cur_info; int last_fg; int last_bg; @@ -72,7 +61,7 @@ typedef struct { } TEXT_BUFFER_REC; /* Create new buffer */ -TEXT_BUFFER_REC *textbuffer_create(void); +TEXT_BUFFER_REC *textbuffer_create(WINDOW_REC *window); /* Destroy the buffer */ void textbuffer_destroy(TEXT_BUFFER_REC *buffer); @@ -96,7 +85,7 @@ void textbuffer_remove(TEXT_BUFFER_REC *buffer, LINE_REC *line); /* Removes all lines from buffer, ignoring reference counters */ void textbuffer_remove_all_lines(TEXT_BUFFER_REC *buffer); -void textbuffer_line2text(LINE_REC *line, int coloring, GString *str); +void textbuffer_line2text(TEXT_BUFFER_REC *buffer, LINE_REC *line, int coloring, GString *str); GList *textbuffer_find_text(TEXT_BUFFER_REC *buffer, LINE_REC *startline, int level, int nolevel, const char *text, int before, int after, diff --git a/src/perl/textui/TextBuffer.xs b/src/perl/textui/TextBuffer.xs index 14ef5950..40602272 100644 --- a/src/perl/textui/TextBuffer.xs +++ b/src/perl/textui/TextBuffer.xs @@ -25,7 +25,8 @@ OUTPUT: RETVAL void -textbuffer_line_get_text(line, coloring) +textbuffer_line_get_text(buffer, line, coloring) + Irssi::TextUI::TextBuffer buffer Irssi::TextUI::Line line int coloring PREINIT: @@ -33,7 +34,7 @@ PREINIT: SV *result; PPCODE: str = g_string_new(NULL); - textbuffer_line2text(line, coloring, str); + textbuffer_line2text(buffer, line, coloring, str); result = new_pv(str->str); XPUSHs(sv_2mortal(result)); g_string_free(str, TRUE); diff --git a/tests/fe-text/Makefile.am b/tests/fe-text/Makefile.am index b1762c7d..2a3450e9 100644 --- a/tests/fe-text/Makefile.am +++ b/tests/fe-text/Makefile.am @@ -26,6 +26,7 @@ test_paste_join_multiline_SOURCES = \ ../../src/fe-text/term-terminfo.c \ ../../src/fe-text/terminfo-core.c \ ../../src/fe-text/term.c \ + ../../src/fe-text/textbuffer-formats.c \ ../../src/fe-text/textbuffer-view.c \ ../../src/fe-text/textbuffer.c \ ../../src/fe-text/gui-windows.c \ diff --git a/tests/fe-text/meson.build b/tests/fe-text/meson.build index ea6ec620..397bd39f 100644 --- a/tests/fe-text/meson.build +++ b/tests/fe-text/meson.build @@ -7,6 +7,7 @@ test_test_paste_join_multiline = executable('test-paste-join-multiline', '../../src/fe-text/term-terminfo.c', '../../src/fe-text/term.c', '../../src/fe-text/terminfo-core.c', + '../../src/fe-text/textbuffer-formats.c', '../../src/fe-text/textbuffer-view.c', '../../src/fe-text/textbuffer.c', 'mock-irssi.c', From 6b93d6e3382b08b6e703b2c6dc71ff85b4c1fc9b Mon Sep 17 00:00:00 2001 From: ailin-nemui Date: Tue, 13 Aug 2019 15:15:15 +0200 Subject: [PATCH 2/6] implement expando cache - the expando values need to be stored now that the lines are reformattable, otherwise the old values are lost (and they depend on context only available at the time the line is initially printed) - the cache is collected from the special-vars evaluation code - the cache is controlled by the textbuffer-formats code, and stored in the text_buffer_format_rec --- src/core/core.c | 9 ++- src/core/special-vars.c | 119 ++++++++++++++++++++++++++----- src/core/special-vars.h | 9 +++ src/fe-text/textbuffer-formats.c | 39 +++++++++- src/fe-text/textbuffer-formats.h | 1 + 5 files changed, 155 insertions(+), 22 deletions(-) diff --git a/src/core/core.c b/src/core/core.c index 85f3a5d5..34649c81 100644 --- a/src/core/core.c +++ b/src/core/core.c @@ -34,16 +34,17 @@ #endif #include -#include #include #include #include -#include +#include #include #include -#include #include #include +#include +#include +#include #include #include @@ -262,6 +263,7 @@ void core_init(void) chat_commands_init(); i_refstr_init(); + special_vars_init(); wcwidth_wrapper_init(); settings_add_str("misc", "ignore_signals", ""); @@ -288,6 +290,7 @@ void core_deinit(void) signal_remove("irssi init finished", (SIGNAL_FUNC) sig_irssi_init_finished); wcwidth_wrapper_deinit(); + special_vars_deinit(); i_refstr_deinit(); chat_commands_deinit(); diff --git a/src/core/special-vars.c b/src/core/special-vars.c index ef56e2e1..802fcb36 100644 --- a/src/core/special-vars.c +++ b/src/core/special-vars.c @@ -19,12 +19,13 @@ */ #include "module.h" +#include +#include +#include +#include +#include #include #include -#include -#include -#include -#include #include #define isvarchar(c) \ @@ -40,6 +41,8 @@ #endif static SPECIAL_HISTORY_FUNC history_func = NULL; +static GSList *special_collector; +static GSList *special_cache; static char *get_argument(char **cmd, char **arglist) { @@ -121,8 +124,40 @@ static char *get_long_variable_value(const char *key, SERVER_REC *server, return NULL; } -static char *get_long_variable(char **cmd, SERVER_REC *server, - void *item, int *free_ret, int getname) +static gboolean cache_find(GSList **cache, const char *var, char **ret) +{ + GSList *tmp; + GSList *prev = NULL; + + if (cache == NULL) + return FALSE; + + for (tmp = *cache; tmp;) { + if (g_strcmp0(var, tmp->data) == 0) { + *ret = tmp->next->data; + if (prev != NULL) + prev->next->next = tmp->next->next; + else + *cache = tmp->next->next; + + g_slist_free_1(tmp->next); + g_slist_free_1(tmp); + return TRUE; + } + prev = tmp; + tmp = tmp->next->next; + } + return FALSE; +} + +static gboolean cache_find_char(GSList **cache, char var, char **ret) +{ + char varn[] = { var, '\0' }; + return cache_find(cache, varn, ret); +} + +static char *get_long_variable(char **cmd, SERVER_REC *server, void *item, int *free_ret, + int getname, GSList **collector, GSList **cache) { char *start, *var, *ret; @@ -135,16 +170,23 @@ static char *get_long_variable(char **cmd, SERVER_REC *server, *free_ret = TRUE; return var; } + if (cache_find(cache, var, &ret)) { + g_free(var); + return ret; + } ret = get_long_variable_value(var, server, item, free_ret); + if (collector != NULL) { + *collector = g_slist_prepend(*collector, g_strdup(ret)); + *collector = g_slist_prepend(*collector, i_refstr_intern(var)); + } g_free(var); return ret; } /* return the value of the variable found from `cmd'. if 'getname' is TRUE, return the name of the variable instead it's value */ -static char *get_variable(char **cmd, SERVER_REC *server, void *item, - char **arglist, int *free_ret, int *arg_used, - int getname) +static char *get_variable(char **cmd, SERVER_REC *server, void *item, char **arglist, int *free_ret, + int *arg_used, int getname, GSList **collector, GSList **cache) { EXPANDO_FUNC func; @@ -158,7 +200,7 @@ static char *get_variable(char **cmd, SERVER_REC *server, void *item, if (i_isalpha(**cmd) && isvarchar((*cmd)[1])) { /* long variable name.. */ - return get_long_variable(cmd, server, item, free_ret, getname); + return get_long_variable(cmd, server, item, free_ret, getname, collector, cache); } /* single character variable. */ @@ -167,15 +209,27 @@ static char *get_variable(char **cmd, SERVER_REC *server, void *item, return g_strdup_printf("%c", **cmd); } *free_ret = FALSE; + { + char *ret; + if (cache_find_char(cache, **cmd, &ret)) { + return ret; + } + } func = expando_find_char(**cmd); if (func == NULL) return NULL; else { char str[2]; + char *ret; str[0] = **cmd; str[1] = '\0'; current_expando = str; - return func(server, item, free_ret); + ret = func(server, item, free_ret); + if (**cmd != 'Z' && collector != NULL) { + *collector = g_slist_prepend(*collector, g_strdup(ret)); + *collector = g_slist_prepend(*collector, i_refstr_intern(str)); + } + return ret; } } @@ -199,9 +253,9 @@ static char *get_history(char **cmd, void *item, int *free_ret) return ret; } -static char *get_special_value(char **cmd, SERVER_REC *server, void *item, - char **arglist, int *free_ret, int *arg_used, - int flags) +static char *get_special_value(char **cmd, SERVER_REC *server, void *item, char **arglist, + int *free_ret, int *arg_used, int flags, GSList **collector, + GSList **cache) { char command, *value, *p; int len; @@ -236,8 +290,8 @@ static char *get_special_value(char **cmd, SERVER_REC *server, void *item, } } - value = get_variable(cmd, server, item, arglist, free_ret, - arg_used, flags & PARSE_FLAG_GETNAME); + value = get_variable(cmd, server, item, arglist, free_ret, arg_used, + flags & PARSE_FLAG_GETNAME, collector, cache); if (flags & PARSE_FLAG_GETNAME) return value; @@ -440,8 +494,9 @@ char *parse_special(char **cmd, SERVER_REC *server, void *item, brackets = TRUE; } - value = get_special_value(cmd, server, item, arglist, - free_ret, arg_used, flags); + value = get_special_value(cmd, server, item, arglist, free_ret, arg_used, flags, + special_collector != NULL ? special_collector->data : NULL, + &special_cache); if (**cmd == '\0') g_error("parse_special() : buffer overflow!"); @@ -635,6 +690,22 @@ void special_history_func_set(SPECIAL_HISTORY_FUNC func) history_func = func; } +void special_push_collector(GSList **list) +{ + special_collector = g_slist_prepend(special_collector, list); +} + +void special_pop_collector(void) +{ + special_collector = g_slist_delete_link(special_collector, special_collector); +} + +void special_fill_cache(GSList *list) +{ + g_slist_free(special_cache); + special_cache = g_slist_copy(list); +} + static void update_signals_hash(GHashTable **hash, int *signals) { void *signal_id; @@ -758,3 +829,15 @@ int *special_vars_get_signals(const char *text) { return special_vars_signals_task(text, 0, NULL, TASK_GET_SIGNALS); } + +void special_vars_init(void) +{ + special_cache = NULL; + special_collector = NULL; +} + +void special_vars_deinit(void) +{ + g_slist_free(special_cache); + g_slist_free(special_collector); +} diff --git a/src/core/special-vars.h b/src/core/special-vars.h index f4bd926d..87aba7d6 100644 --- a/src/core/special-vars.h +++ b/src/core/special-vars.h @@ -32,6 +32,11 @@ char *parse_special_string(const char *cmd, SERVER_REC *server, void *item, void eval_special_string(const char *cmd, const char *data, SERVER_REC *server, void *item); +void special_push_collector(GSList **list); +void special_pop_collector(void); + +void special_fill_cache(GSList *list); + void special_history_func_set(SPECIAL_HISTORY_FUNC func); void special_vars_add_signals(const char *text, @@ -41,4 +46,8 @@ void special_vars_remove_signals(const char *text, /* Returns [, EXPANDO_ARG_xxx, , ..., -1] */ int *special_vars_get_signals(const char *text); +void special_vars_init(void); + +void special_vars_deinit(void); + #endif diff --git a/src/fe-text/textbuffer-formats.c b/src/fe-text/textbuffer-formats.c index 599a85c6..e0a5f193 100644 --- a/src/fe-text/textbuffer-formats.c +++ b/src/fe-text/textbuffer-formats.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -13,6 +14,18 @@ TEXT_BUFFER_REC *color_buf; +static void collector_free(GSList **collector) +{ + while (*collector) { + GSList *next = (*collector)->next->next; + i_refstr_release((*collector)->data); + g_free((*collector)->next->data); + g_slist_free_1((*collector)->next); + g_slist_free_1((*collector)); + *collector = next; + } +} + void textbuffer_format_rec_free(TEXT_BUFFER_FORMAT_REC *rec) { int n; @@ -35,6 +48,7 @@ void textbuffer_format_rec_free(TEXT_BUFFER_FORMAT_REC *rec) } rec->nargs = 0; g_free(rec->args); + collector_free(&rec->expando_cache); g_slice_free(TEXT_BUFFER_FORMAT_REC, rec); } @@ -112,12 +126,14 @@ static void sig_print_format(THEME_REC *theme, const char *module, TEXT_DEST_REC info->format = format_rec_new(module, formats[formatnum].tag, dest->server_tag, dest->target, dest->nick, formats[formatnum].params, args); + special_push_collector(&info->format->expando_cache); info->format->flags = dest->flags; dest->flags |= PRINT_FLAG_FORMAT; signal_continue(5, theme, module, dest, formatnump, args); + special_pop_collector(); free_lineinfo_tmp(dest->window); } @@ -125,19 +141,36 @@ static void sig_print_noformat(TEXT_DEST_REC *dest, const char *text) { LINE_INFO_REC *info; + special_push_collector(NULL); info = store_lineinfo_tmp(dest); info->format = format_rec_new(NULL, NULL, dest->server_tag, dest->target, dest->nick, 2, (const char *[]){ NULL, text }); + special_push_collector(&info->format->expando_cache); info->format->flags = dest->flags; dest->flags |= PRINT_FLAG_FORMAT; signal_continue(2, dest, text); + special_pop_collector(); free_lineinfo_tmp(dest->window); } +static GSList *reverse_collector(GSList *a1) +{ + GSList *b1, *c1; + c1 = NULL; + while (a1) { + b1 = a1->next->next; + a1->next->next = c1; + + c1 = a1; + a1 = b1; + } + return c1; +} + static void sig_gui_print_text_finished(WINDOW_REC *window) { GUI_WINDOW_REC *gui; @@ -157,6 +190,8 @@ static void sig_gui_print_text_finished(WINDOW_REC *window) if (info->format == NULL) return; + info->format->expando_cache = reverse_collector(info->format->expando_cache); + info->level |= MSGLEVEL_FORMAT; /* the line will be inserted into the view with textbuffer_view_insert_line by @@ -182,7 +217,7 @@ char *textbuffer_line_get_text(TEXT_BUFFER_REC *buffer, LINE_REC *line) if (line == NULL || gui == NULL) return NULL; - if (line->info.level & MSGLEVEL_FORMAT) { + if (line->info.level & MSGLEVEL_FORMAT && line->info.format != NULL) { TEXT_DEST_REC dest; THEME_REC *theme; int formatnum; @@ -200,6 +235,7 @@ char *textbuffer_line_get_text(TEXT_BUFFER_REC *buffer, LINE_REC *line) theme = window_get_theme(dest.window); + special_fill_cache(format_rec->expando_cache); if (format_rec->format != NULL) { char *arglist[MAX_FORMAT_PARAMS] = { 0 }; formatnum = format_find_tag(format_rec->module, format_rec->format); @@ -238,6 +274,7 @@ char *textbuffer_line_get_text(TEXT_BUFFER_REC *buffer, LINE_REC *line) } else { return text; } + special_fill_cache(NULL); } else { return g_strdup(line->info.text); } diff --git a/src/fe-text/textbuffer-formats.h b/src/fe-text/textbuffer-formats.h index 5a156fda..45e4ce1b 100644 --- a/src/fe-text/textbuffer-formats.h +++ b/src/fe-text/textbuffer-formats.h @@ -11,6 +11,7 @@ typedef struct _TEXT_BUFFER_FORMAT_REC { char *nick; char **args; int nargs; + GSList *expando_cache; int flags; } TEXT_BUFFER_FORMAT_REC; From 57fb173130cec6e82bccfbe6ead09731b591c5a6 Mon Sep 17 00:00:00 2001 From: ailin-nemui Date: Thu, 8 Aug 2019 21:54:30 +0200 Subject: [PATCH 3/6] add perl code for textbuffer-formats - compatibility shim for new line - make format accessible from perl - fix perl line IDs being mixed up due to wrapper --- src/perl/Makefile.am | 1 + src/perl/textui/TextBuffer.xs | 39 ++++++++++-- src/perl/textui/TextBufferView.xs | 19 ++++++ src/perl/textui/TextUI.xs | 29 ++++++--- src/perl/textui/module.h | 2 +- src/perl/textui/typemap | 7 ++- src/perl/textui/wrapper_buffer_line.h | 90 +++++++++++++++++++++++++++ 7 files changed, 170 insertions(+), 17 deletions(-) create mode 100644 src/perl/textui/wrapper_buffer_line.h diff --git a/src/perl/Makefile.am b/src/perl/Makefile.am index 0e56d7a3..45ac1eea 100644 --- a/src/perl/Makefile.am +++ b/src/perl/Makefile.am @@ -108,6 +108,7 @@ textui_sources = \ textui/TextBufferView.xs \ textui/Statusbar.xs \ textui/Makefile.PL.in \ + textui/wrapper_buffer_line.h \ textui/typemap \ textui/module.h diff --git a/src/perl/textui/TextBuffer.xs b/src/perl/textui/TextBuffer.xs index 40602272..ae3973c8 100644 --- a/src/perl/textui/TextBuffer.xs +++ b/src/perl/textui/TextBuffer.xs @@ -1,5 +1,7 @@ #define PERL_NO_GET_CONTEXT #include "module.h" +#include "wrapper_buffer_line.h" +#include MODULE = Irssi::TextUI::TextBuffer PACKAGE = Irssi PROTOTYPES: ENABLE @@ -12,7 +14,7 @@ Irssi::TextUI::Line textbuffer_line_prev(line) Irssi::TextUI::Line line CODE: - RETVAL = line->prev; + RETVAL = perl_wrap_buffer_line(line->buffer, line->line->prev); OUTPUT: RETVAL @@ -20,13 +22,12 @@ Irssi::TextUI::Line textbuffer_line_next(line) Irssi::TextUI::Line line CODE: - RETVAL = line->next; + RETVAL = perl_wrap_buffer_line(line->buffer, line->line->next); OUTPUT: RETVAL void -textbuffer_line_get_text(buffer, line, coloring) - Irssi::TextUI::TextBuffer buffer +textbuffer_line_get_text(line, coloring) Irssi::TextUI::Line line int coloring PREINIT: @@ -34,8 +35,36 @@ PREINIT: SV *result; PPCODE: str = g_string_new(NULL); - textbuffer_line2text(buffer, line, coloring, str); + textbuffer_line2text(line->buffer, line->line, coloring, str); result = new_pv(str->str); XPUSHs(sv_2mortal(result)); g_string_free(str, TRUE); +void +textbuffer_line_get_format(line) + Irssi::TextUI::Line line +PREINIT: + HV *hv; + AV *av; + LINE_REC *l; + TEXT_BUFFER_FORMAT_REC *f; + int i; +PPCODE: + hv = newHV(); + l = line->line; + if (l->info.format != NULL) { + f = l->info.format; + (void) hv_store(hv, "module", 6, new_pv(f->module), 0); + (void) hv_store(hv, "format", 6, new_pv(f->format), 0); + (void) hv_store(hv, "server_tag", 10, new_pv(f->server_tag), 0); + (void) hv_store(hv, "target", 6, new_pv(f->target), 0); + (void) hv_store(hv, "nick", 4, new_pv(f->nick), 0); + av = newAV(); + for (i = 0; i < f->nargs; i++) { + av_push(av, new_pv(f->args[i])); + } + (void) hv_store(hv, "args", 4, newRV_noinc((SV *) av), 0); + } else { + (void) hv_store(hv, "text", 4, new_pv(l->info.text), 0); + } + XPUSHs(sv_2mortal(newRV_noinc((SV *) hv))); diff --git a/src/perl/textui/TextBufferView.xs b/src/perl/textui/TextBufferView.xs index c0dc762d..43aba4e6 100644 --- a/src/perl/textui/TextBufferView.xs +++ b/src/perl/textui/TextBufferView.xs @@ -1,5 +1,6 @@ #define PERL_NO_GET_CONTEXT #include "module.h" +#include "wrapper_buffer_line.h" MODULE = Irssi::TextUI::TextBufferView PACKAGE = Irssi::TextUI::TextBuffer PREFIX = textbuffer_ PROTOTYPES: ENABLE @@ -33,6 +34,10 @@ textbuffer_view_clear(view) Irssi::TextUI::Line textbuffer_view_get_lines(view) Irssi::TextUI::TextBufferView view +CODE: + RETVAL = perl_wrap_buffer_line(view->buffer, textbuffer_view_get_lines(view)); +OUTPUT: + RETVAL void textbuffer_view_scroll(view, lines) @@ -43,16 +48,24 @@ void textbuffer_view_scroll_line(view, line) Irssi::TextUI::TextBufferView view Irssi::TextUI::Line line +CODE: + textbuffer_view_scroll_line(view, line->line); Irssi::TextUI::LineCache textbuffer_view_get_line_cache(view, line) Irssi::TextUI::TextBufferView view Irssi::TextUI::Line line +CODE: + RETVAL = textbuffer_view_get_line_cache(view, line->line); +OUTPUT: + RETVAL void textbuffer_view_remove_line(view, line) Irssi::TextUI::TextBufferView view Irssi::TextUI::Line line +CODE: + textbuffer_view_remove_line(view, line->line); void textbuffer_view_remove_all_lines(view) @@ -68,6 +81,8 @@ textbuffer_view_set_bookmark(view, name, line) Irssi::TextUI::TextBufferView view char *name Irssi::TextUI::Line line +CODE: + textbuffer_view_set_bookmark(view, name, line->line); void textbuffer_view_set_bookmark_bottom(view, name) @@ -78,6 +93,10 @@ Irssi::TextUI::Line textbuffer_view_get_bookmark(view, name) Irssi::TextUI::TextBufferView view char *name +CODE: + RETVAL = perl_wrap_buffer_line(view->buffer, textbuffer_view_get_bookmark(view, name)); +OUTPUT: + RETVAL void textbuffer_view_redraw(view) diff --git a/src/perl/textui/TextUI.xs b/src/perl/textui/TextUI.xs index af978162..e87efb65 100644 --- a/src/perl/textui/TextUI.xs +++ b/src/perl/textui/TextUI.xs @@ -1,11 +1,17 @@ #define PERL_NO_GET_CONTEXT #include "module.h" +#include "wrapper_buffer_line.h" void perl_statusbar_init(void); void perl_statusbar_deinit(void); static int initialized = FALSE; +static SV *buffer_line_bless(TEXT_BUFFER_REC *buffer, LINE_REC *line) +{ + return perl_buffer_line_bless(perl_wrap_buffer_line(buffer, line)); +} + static void perl_main_window_fill_hash(HV *hv, MAIN_WINDOW_REC *window) { (void) hv_store(hv, "active", 6, plain_bless(window->active, "Irssi::UI::Window"), 0); @@ -20,9 +26,9 @@ static void perl_main_window_fill_hash(HV *hv, MAIN_WINDOW_REC *window) static void perl_text_buffer_fill_hash(HV *hv, TEXT_BUFFER_REC *buffer) { - (void) hv_store(hv, "first_line", 10, plain_bless(buffer->first_line, "Irssi::TextUI::Line"), 0); + (void) hv_store(hv, "first_line", 10, buffer_line_bless(buffer, buffer->first_line), 0); (void) hv_store(hv, "lines_count", 11, newSViv(buffer->lines_count), 0); - (void) hv_store(hv, "cur_line", 8, plain_bless(buffer->cur_line, "Irssi::TextUI::Line"), 0); + (void) hv_store(hv, "cur_line", 8, buffer_line_bless(buffer, buffer->cur_line), 0); (void) hv_store(hv, "last_eol", 8, newSViv(buffer->last_eol), 0); } @@ -38,20 +44,22 @@ static void perl_text_buffer_view_fill_hash(HV *hv, TEXT_BUFFER_VIEW_REC *view) (void) hv_store(hv, "ypos", 4, newSViv(view->ypos), 0); - (void) hv_store(hv, "startline", 9, plain_bless(view->startline, "Irssi::TextUI::Line"), 0); + (void) hv_store(hv, "startline", 9, buffer_line_bless(view->buffer, view->startline), 0); (void) hv_store(hv, "subline", 7, newSViv(view->subline), 0); (void) hv_store(hv, "hidden_level", 12, newSViv(view->hidden_level), 0); - (void) hv_store(hv, "bottom_startline", 16, plain_bless(view->bottom_startline, "Irssi::TextUI::Line"), 0); + (void) hv_store(hv, "bottom_startline", 16, + buffer_line_bless(view->buffer, view->bottom_startline), 0); (void) hv_store(hv, "bottom_subline", 14, newSViv(view->bottom_subline), 0); (void) hv_store(hv, "empty_linecount", 15, newSViv(view->empty_linecount), 0); (void) hv_store(hv, "bottom", 6, newSViv(view->bottom), 0); } -static void perl_line_fill_hash(HV *hv, LINE_REC *line) +static void perl_line_fill_hash(HV *hv, struct Buffer_Line_Wrapper *line) { - (void) hv_store(hv, "info", 4, plain_bless(&line->info, "Irssi::TextUI::LineInfo"), 0); + (void) hv_store(hv, "info", 4, plain_bless(&Line(line)->info, "Irssi::TextUI::LineInfo"), + 0); } static void perl_line_cache_fill_hash(HV *hv, LINE_CACHE_REC *cache) @@ -242,7 +250,7 @@ CODE: format_create_dest(&dest, NULL, NULL, level, window); text = format_string_expand(str, NULL); text2 = g_strconcat(text, "\n", NULL); - gui_printtext_after_time(&dest, prev, text2, time); + gui_printtext_after_time(&dest, Line(prev), text2, time); g_free(text); g_free(text2); @@ -257,13 +265,14 @@ PREINIT: TEXT_DEST_REC dest; CODE: format_create_dest(&dest, NULL, NULL, level, window); - gui_printtext_after_time(&dest, prev, str, time); + gui_printtext_after_time(&dest, Line(prev), str, time); Irssi::TextUI::Line last_line_insert(window) Irssi::UI::Window window CODE: - RETVAL = WINDOW_GUI(window)->insert_after; + RETVAL = perl_wrap_buffer_line(WINDOW_GUI(window)->view->buffer, + WINDOW_GUI(window)->insert_after); OUTPUT: RETVAL @@ -281,7 +290,7 @@ PREINIT: TEXT_DEST_REC dest; CODE: format_create_dest(&dest, server, target, level, NULL); - gui_printtext_after_time(&dest, prev, str, time); + gui_printtext_after_time(&dest, Line(prev), str, time); BOOT: irssi_boot(TextUI__Statusbar); diff --git a/src/perl/textui/module.h b/src/perl/textui/module.h index 394c2839..aba5bd77 100644 --- a/src/perl/textui/module.h +++ b/src/perl/textui/module.h @@ -11,7 +11,7 @@ typedef MAIN_WINDOW_REC *Irssi__TextUI__MainWindow; typedef TEXT_BUFFER_REC *Irssi__TextUI__TextBuffer; typedef TEXT_BUFFER_VIEW_REC *Irssi__TextUI__TextBufferView; -typedef LINE_REC *Irssi__TextUI__Line; +typedef struct Buffer_Line_Wrapper *Irssi__TextUI__Line; typedef LINE_CACHE_REC *Irssi__TextUI__LineCache; typedef LINE_INFO_REC *Irssi__TextUI__LineInfo; typedef SBAR_ITEM_REC *Irssi__TextUI__StatusbarItem; diff --git a/src/perl/textui/typemap b/src/perl/textui/typemap index 364cdf32..7710c2d2 100644 --- a/src/perl/textui/typemap +++ b/src/perl/textui/typemap @@ -2,7 +2,7 @@ TYPEMAP Irssi::TextUI::MainWindow T_PlainObj Irssi::TextUI::TextBuffer T_PlainObj Irssi::TextUI::TextBufferView T_PlainObj -Irssi::TextUI::Line T_PlainObj +Irssi::TextUI::Line T_BufferLineWrapper Irssi::TextUI::LineCache T_PlainObj Irssi::TextUI::LineInfo T_PlainObj Irssi::TextUI::StatusbarItem T_PlainObj @@ -12,8 +12,13 @@ INPUT T_PlainObj $var = irssi_ref_object($arg) +T_BufferLineWrapper + $var = irssi_ref_buffer_line_wrap($arg) + OUTPUT T_PlainObj $arg = plain_bless($var, \"$type\"); +T_BufferLineWrapper + $arg = perl_buffer_line_bless($var); diff --git a/src/perl/textui/wrapper_buffer_line.h b/src/perl/textui/wrapper_buffer_line.h new file mode 100644 index 00000000..34310655 --- /dev/null +++ b/src/perl/textui/wrapper_buffer_line.h @@ -0,0 +1,90 @@ +#ifndef IRSSI_PERL_TEXTUI_WRAPPER_BUFFER_LINE_H +#define IRSSI_PERL_TEXTUI_WRAPPER_BUFFER_LINE_H + +/* This Buffer_Line_Wrapper is a compatibility shim so that the Perl + * API does not change in Irssi ABI 24 even though the C API was + * changed. That way scripts can continue to work unchanged. */ + +struct Buffer_Line_Wrapper { + LINE_REC *line; + TEXT_BUFFER_REC *buffer; +}; + +#define Line(wrapper) ((wrapper) == NULL ? NULL : (wrapper)->line) + +static int magic_free_buffer_line(pTHX_ SV *sv, MAGIC *mg) +{ + struct Buffer_Line_Wrapper *wrap = (struct Buffer_Line_Wrapper *) mg->mg_ptr; + g_free(wrap); + mg->mg_ptr = NULL; + sv_setiv(sv, 0); + return 0; +} + +static MGVTBL vtbl_free_buffer_line = { NULL, NULL, NULL, NULL, magic_free_buffer_line }; + +static struct Buffer_Line_Wrapper *perl_wrap_buffer_line(TEXT_BUFFER_REC *buffer, LINE_REC *line) +{ + struct Buffer_Line_Wrapper *wrap; + + if (line == NULL) + return NULL; + + wrap = g_new0(struct Buffer_Line_Wrapper, 1); + wrap->buffer = buffer; + wrap->line = line; + + return wrap; +} + +/* This function is more or less a copy of plain_bless, but with a + special divertion to put the wrapper in _wrapper and the original + line pointer in _irssi, in order to stay compatible with signals + and scripts */ +static SV *perl_buffer_line_bless(struct Buffer_Line_Wrapper *object) +{ + SV *ret, **tmp; + HV *hv; + const char *stash = "Irssi::TextUI::Line"; + + if (object == NULL) + return &PL_sv_undef; + + ret = irssi_bless_plain(stash, object); + hv = hvref(ret); + + tmp = hv_fetch(hv, "_irssi", 6, 0); + + sv_magic(*tmp, NULL, '~', NULL, 0); + + SvMAGIC(*tmp)->mg_private = 0x1551; /* HF */ + SvMAGIC(*tmp)->mg_virtual = &vtbl_free_buffer_line; + SvMAGIC(*tmp)->mg_ptr = (char *) object; + + (void) hv_store(hv, "_wrapper", 8, *tmp, 0); + /* We have to put the Line Pointer in _irssi, not the + compatibility wrapper */ + *tmp = newSViv((IV) object->line); + return ret; +} + +/* This function is a copy of irssi_ref_object, but looking up the + wrapper object in _wrapper instead */ +static void *irssi_ref_buffer_line_wrap(SV *o) +{ + SV **sv; + HV *hv; + void *p; + + hv = hvref(o); + if (hv == NULL) + return NULL; + + sv = hv_fetch(hv, "_wrapper", 8, 0); + if (sv == NULL) + croak("variable is damaged"); + p = GINT_TO_POINTER(SvIV(*sv)); + return p; +} + +#endif From 8fd6dccaf19b08542d855beb468bbf2ba4af56aa Mon Sep 17 00:00:00 2001 From: ailin-nemui Date: Tue, 17 Mar 2020 00:26:25 +0100 Subject: [PATCH 4/6] enable mirc colour processing --- src/fe-common/core/formats.c | 38 ++++++++++++++++------ src/fe-common/core/formats.h | 11 ++++++- src/fe-text/gui-printtext.c | 4 +-- src/fe-text/gui-printtext.h | 1 + src/fe-text/textbuffer-formats.c | 54 +++++++++++++++++++++++++++----- 5 files changed, 87 insertions(+), 21 deletions(-) diff --git a/src/fe-common/core/formats.c b/src/fe-common/core/formats.c index a6fde7ea..a4d3a023 100644 --- a/src/fe-common/core/formats.c +++ b/src/fe-common/core/formats.c @@ -1222,8 +1222,14 @@ char *strip_codes(const char *input) return str; } -/* send a fully parsed text string for GUI to print */ -void format_send_to_gui(TEXT_DEST_REC *dest, const char *text) +/* parse text string into GUI_PRINT_FLAG_* separated pieces and emit them to handler + handler is a SIGNAL_FUNC with the following arguments: + + WINDOW_REC *window, void *fgcolor_int, void *bgcolor_int, + void *flags_int, const char *textpiece, TEXT_DEST_REC *dest + + */ +void format_send_as_gui_flags(TEXT_DEST_REC *dest, const char *text, SIGNAL_FUNC handler) { THEME_REC *theme; char *dup, *str, *ptr, type; @@ -1238,8 +1244,8 @@ void format_send_to_gui(TEXT_DEST_REC *dest, const char *text) if (*str == '\0') { /* empty line, write line info only */ - signal_emit_id(signal_gui_print_text, 6, dest->window, GINT_TO_POINTER(fgcolor), - GINT_TO_POINTER(bgcolor), GINT_TO_POINTER(flags), str, dest); + handler(dest->window, GINT_TO_POINTER(fgcolor), GINT_TO_POINTER(bgcolor), + GINT_TO_POINTER(flags), str, dest); } while (*str != '\0') { @@ -1259,16 +1265,14 @@ void format_send_to_gui(TEXT_DEST_REC *dest, const char *text) if (*str != '\0' || (flags & GUI_PRINT_FLAG_CLRTOEOL)) { /* send the text to gui handler */ - signal_emit_id(signal_gui_print_text, 6, dest->window, - GINT_TO_POINTER(fgcolor), - GINT_TO_POINTER(bgcolor), - GINT_TO_POINTER(flags), str, - dest); + handler(dest->window, GINT_TO_POINTER(fgcolor), GINT_TO_POINTER(bgcolor), + GINT_TO_POINTER(flags), str, dest); flags &= ~(GUI_PRINT_FLAG_INDENT|GUI_PRINT_FLAG_CLRTOEOL); } if (type == '\n') { - format_newline(dest); + handler(dest->window, GINT_TO_POINTER(-1), GINT_TO_POINTER(-1), + GINT_TO_POINTER(GUI_PRINT_FLAG_NEWLINE), "", dest); fgcolor = theme->default_color; bgcolor = -1; flags &= GUI_PRINT_FLAG_INDENT|GUI_PRINT_FLAG_MONOSPACE; @@ -1414,6 +1418,20 @@ void format_send_to_gui(TEXT_DEST_REC *dest, const char *text) g_free(dup); } +inline static void gui_print_text_emitter(WINDOW_REC *window, void *fgcolor_int, void *bgcolor_int, + void *flags_int, const char *textpiece, + TEXT_DEST_REC *dest) +{ + signal_emit_id(signal_gui_print_text, 6, window, fgcolor_int, bgcolor_int, flags_int, + textpiece, dest); +} + +/* send a fully parsed text string for GUI to print */ +void format_send_to_gui(TEXT_DEST_REC *dest, const char *text) +{ + format_send_as_gui_flags(dest, text, (SIGNAL_FUNC) gui_print_text_emitter); +} + void format_gui_flags(GString *out, int *last_fg, int *last_bg, int *last_flags, int fg, int bg, int flags) { diff --git a/src/fe-common/core/formats.h b/src/fe-common/core/formats.h index e30ac290..226348de 100644 --- a/src/fe-common/core/formats.h +++ b/src/fe-common/core/formats.h @@ -1,8 +1,9 @@ #ifndef IRSSI_FE_COMMON_CORE_FORMATS_H #define IRSSI_FE_COMMON_CORE_FORMATS_H -#include +#include #include +#include #define GUI_PRINT_FLAG_BOLD 0x0001 #define GUI_PRINT_FLAG_REVERSE 0x0002 @@ -130,6 +131,14 @@ char *strip_codes(const char *input); /* send a fully parsed text string for GUI to print */ void format_send_to_gui(TEXT_DEST_REC *dest, const char *text); +/* parse text string into GUI_PRINT_FLAG_* separated pieces and emit them to handler + handler is a SIGNAL_FUNC with the following arguments: + + WINDOW_REC *window, void *fgcolor_int, void *bgcolor_int, + void *flags_int, const char *textpiece, TEXT_DEST_REC *dest + + */ +void format_send_as_gui_flags(TEXT_DEST_REC *dest, const char *text, SIGNAL_FUNC handler); #define FORMAT_COLOR_NOCHANGE ('0'-1) /* don't change this, at least hilighting depends this value */ #define FORMAT_COLOR_EXT1 ('0'-2) diff --git a/src/fe-text/gui-printtext.c b/src/fe-text/gui-printtext.c index fe720264..872c73c4 100644 --- a/src/fe-text/gui-printtext.c +++ b/src/fe-text/gui-printtext.c @@ -238,7 +238,7 @@ static void remove_old_lines(TEXT_BUFFER_VIEW_REC *view) } } -static void get_colors(int *flags, int *fg, int *bg, int *attr) +void gui_printtext_get_colors(int *flags, int *fg, int *bg, int *attr) { *attr = 0; if (*flags & GUI_PRINT_FLAG_MIRC_COLOR) { @@ -336,7 +336,7 @@ static void sig_gui_print_text(WINDOW_REC *window, void *fgcolor, flags = GPOINTER_TO_INT(pflags); fg = GPOINTER_TO_INT(fgcolor); bg = GPOINTER_TO_INT(bgcolor); - get_colors(&flags, &fg, &bg, &attr); + gui_printtext_get_colors(&flags, &fg, &bg, &attr); if (window == NULL) { print_text_no_window(flags, fg, bg, attr, str); diff --git a/src/fe-text/gui-printtext.h b/src/fe-text/gui-printtext.h index ba5d6f8f..0acf4d31 100644 --- a/src/fe-text/gui-printtext.h +++ b/src/fe-text/gui-printtext.h @@ -21,5 +21,6 @@ void gui_printtext_internal(int xpos, int ypos, const char *str); void gui_printtext_after(TEXT_DEST_REC *dest, LINE_REC *prev, const char *str); void gui_printtext_after_time(TEXT_DEST_REC *dest, LINE_REC *prev, const char *str, time_t time); void gui_printtext_window_border(int xpos, int ypos); +void gui_printtext_get_colors(int *flags, int *fg, int *bg, int *attr); #endif diff --git a/src/fe-text/textbuffer-formats.c b/src/fe-text/textbuffer-formats.c index e0a5f193..46876e75 100644 --- a/src/fe-text/textbuffer-formats.c +++ b/src/fe-text/textbuffer-formats.c @@ -205,10 +205,47 @@ static void sig_gui_print_text_finished(WINDOW_REC *window) gui->insert_after = insert_after; } +static void parse_colors_collector(const WINDOW_REC *window, const void *fgcolor_int, + const void *bgcolor_int, const void *flags_int, + const char *textpiece, const TEXT_DEST_REC *dest) +{ + int fg, bg, flags, attr; + + flags = GPOINTER_TO_INT(flags_int); + fg = GPOINTER_TO_INT(fgcolor_int); + bg = GPOINTER_TO_INT(bgcolor_int); + gui_printtext_get_colors(&flags, &fg, &bg, &attr); + + if (flags & GUI_PRINT_FLAG_NEWLINE) { + g_string_append_c(color_buf->cur_text, '\n'); + } + format_gui_flags(color_buf->cur_text, &color_buf->last_fg, &color_buf->last_bg, + &color_buf->last_flags, fg, bg, flags); + + g_string_append(color_buf->cur_text, textpiece); +} + +static char *parse_colors(TEXT_DEST_REC *dest, const char *text) +{ + char *tmp; + + if (text == NULL) + return NULL; + + color_buf = textbuffer_create(NULL); + format_send_as_gui_flags(dest, text, (SIGNAL_FUNC) parse_colors_collector); + tmp = g_strdup(color_buf->cur_text->str); + textbuffer_destroy(color_buf); + color_buf = NULL; + + return tmp; +} + char *textbuffer_line_get_text(TEXT_BUFFER_REC *buffer, LINE_REC *line) { + TEXT_DEST_REC dest; GUI_WINDOW_REC *gui; - LINE_REC *curr; + char *tmp, *text = NULL; g_return_val_if_fail(buffer != NULL, NULL); g_return_val_if_fail(buffer->window != NULL, NULL); @@ -218,11 +255,11 @@ char *textbuffer_line_get_text(TEXT_BUFFER_REC *buffer, LINE_REC *line) return NULL; if (line->info.level & MSGLEVEL_FORMAT && line->info.format != NULL) { - TEXT_DEST_REC dest; + LINE_REC *curr; THEME_REC *theme; int formatnum; TEXT_BUFFER_FORMAT_REC *format_rec; - char *text, *tmp, *str; + char *str; curr = line; line = NULL; @@ -267,17 +304,18 @@ char *textbuffer_line_get_text(TEXT_BUFFER_REC *buffer, LINE_REC *line) dest.flags |= PRINT_FLAG_FORMAT; current_time = (time_t) -1; - return str; } else if (format_rec->format != NULL) { g_free(text); - return NULL; - } else { - return text; + text = NULL; } special_fill_cache(NULL); } else { - return g_strdup(line->info.text); + format_create_dest(&dest, NULL, NULL, line->info.level, buffer->window); + text = g_strdup(line->info.text); } + tmp = parse_colors(&dest, text); + g_free(text); + return tmp; } void textbuffer_formats_init(void) From 449d86caf0ff9f5458443d3bb60e5bc7658c038a Mon Sep 17 00:00:00 2001 From: ailin-nemui Date: Sun, 29 Mar 2020 19:10:30 +0200 Subject: [PATCH 5/6] add setting to disable reformat --- src/fe-text/textbuffer-formats.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/fe-text/textbuffer-formats.c b/src/fe-text/textbuffer-formats.c index 46876e75..05158b6f 100644 --- a/src/fe-text/textbuffer-formats.c +++ b/src/fe-text/textbuffer-formats.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -13,6 +14,7 @@ #include TEXT_BUFFER_REC *color_buf; +int scrollback_format; static void collector_free(GSList **collector) { @@ -119,6 +121,9 @@ static void sig_print_format(THEME_REC *theme, const char *module, TEXT_DEST_REC FORMAT_REC *formats; LINE_INFO_REC *info; + if (!scrollback_format) + return; + info = store_lineinfo_tmp(dest); formatnum = GPOINTER_TO_INT(formatnump); @@ -141,6 +146,9 @@ static void sig_print_noformat(TEXT_DEST_REC *dest, const char *text) { LINE_INFO_REC *info; + if (!scrollback_format) + return; + special_push_collector(NULL); info = store_lineinfo_tmp(dest); @@ -318,15 +326,25 @@ char *textbuffer_line_get_text(TEXT_BUFFER_REC *buffer, LINE_REC *line) return tmp; } +static void read_settings(void) +{ + scrollback_format = settings_get_bool("scrollback_format"); +} + void textbuffer_formats_init(void) { + settings_add_bool("lookandfeel", "scrollback_format", TRUE); + + read_settings(); signal_add("print format", (SIGNAL_FUNC) sig_print_format); signal_add("print noformat", (SIGNAL_FUNC) sig_print_noformat); signal_add_first("gui print text finished", (SIGNAL_FUNC) sig_gui_print_text_finished); + signal_add("setup changed", (SIGNAL_FUNC) read_settings); } void textbuffer_formats_deinit(void) { + signal_remove("setup changed", (SIGNAL_FUNC) read_settings); signal_remove("print format", (SIGNAL_FUNC) sig_print_format); signal_remove("print noformat", (SIGNAL_FUNC) sig_print_noformat); signal_remove("gui print text finished", (SIGNAL_FUNC) sig_gui_print_text_finished); From 675696aa84bd9a2aeb2edfa2ec4edd88cffe04c8 Mon Sep 17 00:00:00 2001 From: ailin-nemui Date: Mon, 20 Apr 2020 23:55:46 +0200 Subject: [PATCH 6/6] up abi --- src/common.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common.h b/src/common.h index e0da1080..5fcf5d19 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 26 +#define IRSSI_ABI_VERSION 27 #define DEFAULT_SERVER_ADD_PORT 6667 #define DEFAULT_SERVER_ADD_TLS_PORT 6697