diff options
Diffstat (limited to 'src')
36 files changed, 2439 insertions, 489 deletions
diff --git a/src/common.h b/src/common.h index ba5557e6..46d0c45a 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 13 +#define IRSSI_ABI_VERSION 14 #define DEFAULT_SERVER_ADD_PORT 6667 #define DEFAULT_SERVER_ADD_TLS_PORT 6697 diff --git a/src/core/network-openssl.c b/src/core/network-openssl.c index c7ce4b43..9fddf073 100644 --- a/src/core/network-openssl.c +++ b/src/core/network-openssl.c @@ -46,6 +46,7 @@ #endif /* OpenSSL 1.1.0 also introduced some useful additions to the api */ +#if (OPENSSL_VERSION_NUMBER >= 0x10002000L) #if (OPENSSL_VERSION_NUMBER < 0x10100000L) || defined (LIBRESSL_VERSION_NUMBER) static int X509_STORE_up_ref(X509_STORE *vfy) { @@ -57,6 +58,7 @@ static int X509_STORE_up_ref(X509_STORE *vfy) return (n > 1) ? 1 : 0; } #endif +#endif /* ssl i/o channel object */ typedef struct @@ -72,7 +74,10 @@ typedef struct } GIOSSLChannel; static int ssl_inited = FALSE; +/* https://github.com/irssi/irssi/issues/820 */ +#if (OPENSSL_VERSION_NUMBER >= 0x10002000L) static X509_STORE *store = NULL; +#endif static void irssi_ssl_free(GIOChannel *handle) { @@ -379,7 +384,9 @@ static GIOFuncs irssi_ssl_channel_funcs = { gboolean irssi_ssl_init(void) { +#if (OPENSSL_VERSION_NUMBER >= 0x10002000L) int success; +#endif #if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && !defined(LIBRESSL_VERSION_NUMBER) if (!OPENSSL_init_ssl(OPENSSL_INIT_SSL_DEFAULT, NULL)) { @@ -391,6 +398,8 @@ gboolean irssi_ssl_init(void) SSL_load_error_strings(); OpenSSL_add_all_algorithms(); #endif + +#if (OPENSSL_VERSION_NUMBER >= 0x10002000L) store = X509_STORE_new(); if (store == NULL) { g_error("Could not initialize OpenSSL: X509_STORE_new() failed"); @@ -404,6 +413,7 @@ gboolean irssi_ssl_init(void) store = NULL; /* Don't return an error; the user might have their own cafile/capath. */ } +#endif ssl_inited = TRUE; @@ -522,13 +532,21 @@ static GIOChannel *irssi_ssl_get_iochannel(GIOChannel *handle, int port, SERVER_ g_free(scafile); g_free(scapath); verify = TRUE; - } else if (store != NULL) { + } +#if (OPENSSL_VERSION_NUMBER >= 0x10002000L) + else if (store != NULL) { /* Make sure to increment the refcount every time the store is * used, that's essential not to get it free'd by OpenSSL when * the SSL_CTX is destroyed. */ X509_STORE_up_ref(store); SSL_CTX_set_cert_store(ctx, store); } +#else + else { + if (!SSL_CTX_set_default_verify_paths(ctx)) + g_warning("Could not load default certificates"); + } +#endif if(!(ssl = SSL_new(ctx))) { diff --git a/src/fe-common/core/chat-completion.c b/src/fe-common/core/chat-completion.c index 97cd0565..7ecdd4a2 100644 --- a/src/fe-common/core/chat-completion.c +++ b/src/fe-common/core/chat-completion.c @@ -639,6 +639,59 @@ static void complete_window_nicks(GList **list, WINDOW_REC *window, } } +/* Checks if a line is only nicks from autocompletion. + This lets us insert colons only at the beginning of a list + of nicks */ +static int only_nicks(const char *linestart) +{ + int i = 1; + char prev; + + // at the beginning of the line + if (*linestart == '\0') { + return TRUE; + } + + /* completion_char being a whole string introduces loads of edge cases + and can't be handled generally. Skip this case; we handled the + "beginning of line" case already */ + if (completion_char[1] != '\0') + return FALSE; + + /* This would make the completion char get inserted everywhere otherwise */ + if (*completion_char == ' ') + return FALSE; + + /* First ensure that the line is of the format "foo: bar: baz" + we check this by ensuring each space is preceded by a colon or + another space */ + while (linestart[i] != '\0') { + if (linestart[i] == ' ') { + prev = linestart[i - 1]; + if (prev != *completion_char && prev != ' ') + return FALSE; + } + i += 1; + } + + /* There's an edge case here, if we're completing something + like `foo: bar ba<tab>`, then the `linestart` line will end + at "bar", and we'll miss the space. Ensure that the end + of the line is a colon followed by an optional series of spaces */ + i -= 1; + while (i >= 0) { + if (linestart[i] == ' ') { + i--; + continue; + } else if (linestart[i] == *completion_char) { + return TRUE; + } else { + break; + } + } + return FALSE; +} + static void sig_complete_word(GList **list, WINDOW_REC *window, const char *word, const char *linestart, int *want_space) @@ -691,7 +744,7 @@ static void sig_complete_word(GList **list, WINDOW_REC *window, } else if (channel != NULL) { /* nick completion .. we could also be completing a nick after /MSG from nicks in channel */ - const char *suffix = *linestart != '\0' ? NULL : completion_char; + const char *suffix = only_nicks(linestart) ? completion_char : NULL; complete_window_nicks(list, window, word, suffix); } else if (window->level & MSGLEVEL_MSGS) { /* msgs window, complete /MSG nicks */ diff --git a/src/fe-common/core/fe-common-core.c b/src/fe-common/core/fe-common-core.c index a3b7364c..209c2d9e 100644 --- a/src/fe-common/core/fe-common-core.c +++ b/src/fe-common/core/fe-common-core.c @@ -470,26 +470,49 @@ void fe_common_core_finish_init(void) gboolean strarray_find_dest(char **array, const TEXT_DEST_REC *dest) { + WI_ITEM_REC *item; + int server_tag_len, channel_type, query_type; + char **tmp; + + channel_type = module_get_uniq_id_str("WINDOW ITEM TYPE", "CHANNEL"); + query_type = module_get_uniq_id_str("WINDOW ITEM TYPE", "QUERY"); + g_return_val_if_fail(array != NULL, FALSE); + g_return_val_if_fail(dest != NULL, FALSE); + g_return_val_if_fail(dest->window != NULL, FALSE); + g_return_val_if_fail(dest->target != NULL, FALSE); - if (strarray_find(array, "*") != -1) - return TRUE; + item = window_item_find_window(dest->window, dest->server, dest->target); + if (item == NULL) { + return FALSE; + } - if (strarray_find(array, dest->target) != -1) - return TRUE; + server_tag_len = dest->server_tag != NULL ? strlen(dest->server_tag) : 0; + for (tmp = array; *tmp != NULL; tmp++) { + char *str = *tmp; + if (*str == '\0') { + continue; + } - if (dest->server_tag != NULL) { - char *tagtarget = g_strdup_printf("%s/%s", dest->server_tag, "*"); - int ret = strarray_find(array, tagtarget); - g_free(tagtarget); - if (ret != -1) - return TRUE; + if (server_tag_len && + g_ascii_strncasecmp(str, dest->server_tag, server_tag_len) == 0 && + str[server_tag_len] == '/') { + str += server_tag_len + 1; + } - tagtarget = g_strdup_printf("%s/%s", dest->server_tag, dest->target); - ret = strarray_find(array, tagtarget); - g_free(tagtarget); - if (ret != -1) + if (g_strcmp0(str, "") == 0 || g_strcmp0(str, "::all") == 0) { return TRUE; + } else if (g_ascii_strcasecmp(str, dest->target) == 0) { + return TRUE; + } else if (item->type == query_type && + g_strcmp0(str, dest->target[0] == '=' ? "::dccqueries" : + "::queries") == 0) { + return TRUE; + } else if (item->type == channel_type && + g_strcmp0(str, "::channels") == 0) { + return TRUE; + } } + return FALSE; } diff --git a/src/fe-common/core/fe-windows.h b/src/fe-common/core/fe-windows.h index 32d6cfcd..aaa773c8 100644 --- a/src/fe-common/core/fe-windows.h +++ b/src/fe-common/core/fe-windows.h @@ -11,6 +11,14 @@ enum { DATA_LEVEL_HILIGHT }; +enum { + MAIN_WINDOW_TYPE_NONE = -1, + MAIN_WINDOW_TYPE_DEFAULT = 0, + MAIN_WINDOW_TYPE_HIDDEN = 1, + MAIN_WINDOW_TYPE_SPLIT = 2, + MAIN_WINDOW_TYPE_RSPLIT = 3 +}; + typedef struct { char *servertag; char *name; diff --git a/src/fe-common/core/formats.c b/src/fe-common/core/formats.c index 37db6f7c..4c819c2d 100644 --- a/src/fe-common/core/formats.c +++ b/src/fe-common/core/formats.c @@ -480,27 +480,30 @@ int format_real_length(const char *str, int len) start = str; tmp = g_string_new(NULL); - while (*str != '\0' && len > 0) { + while (*str != '\0') { + oldstr = str; if (*str == '%' && str[1] != '\0') { str++; if (*str != '%') { adv = format_expand_styles(tmp, &str, NULL); - str += adv; - if (adv) - continue; - } - - /* %% or unknown %code, written as-is */ - if (*str != '%') { - if (--len == 0) - break; + if (adv) { + str += adv; + continue; + } + /* discount for unknown % */ + if (--len < 0) { + str = oldstr; + break; + } + oldstr = str; } } - oldstr = str; len -= string_advance(&str, utf8); - if (len < 0) + if (len < 0) { str = oldstr; + break; + } } g_string_free(tmp, TRUE); diff --git a/src/fe-common/core/themes.c b/src/fe-common/core/themes.c index cb1cce8f..6692985b 100644 --- a/src/fe-common/core/themes.c +++ b/src/fe-common/core/themes.c @@ -130,7 +130,7 @@ static char *theme_replace_expand(THEME_REC *theme, int index, abstract = rec->data; abstract = theme_format_expand_data(theme, (const char **) &abstract, default_fg, default_bg, - last_fg, last_bg, flags); + last_fg, last_bg, (flags | EXPAND_FLAG_IGNORE_REPLACES)); ret = parse_special_string(abstract, NULL, NULL, data, NULL, PARSE_FLAG_ONLY_ARGS); g_free(abstract); diff --git a/src/fe-common/core/window-commands.c b/src/fe-common/core/window-commands.c index 57c81ac2..a81c0180 100644 --- a/src/fe-common/core/window-commands.c +++ b/src/fe-common/core/window-commands.c @@ -169,7 +169,7 @@ static void cmd_window(const char *data, void *server, WI_ITEM_REC *item) command_runsub("window", data, server, item); } -/* SYNTAX: WINDOW NEW [HIDDEN|SPLIT] */ +/* SYNTAX: WINDOW NEW [HIDDEN|SPLIT|RSPLIT] */ static void cmd_window_new(const char *data, void *server, WI_ITEM_REC *item) { WINDOW_REC *window; @@ -177,8 +177,9 @@ static void cmd_window_new(const char *data, void *server, WI_ITEM_REC *item) g_return_if_fail(data != NULL); - type = (g_ascii_strncasecmp(data, "hid", 3) == 0 || g_ascii_strcasecmp(data, "tab") == 0) ? 1 : - (g_ascii_strcasecmp(data, "split") == 0 ? 2 : 0); + type = (g_ascii_strncasecmp(data, "hid", 3) == 0 || g_ascii_strcasecmp(data, "tab") == 0) ? MAIN_WINDOW_TYPE_HIDDEN : + g_ascii_strcasecmp(data, "split") == 0 ? MAIN_WINDOW_TYPE_SPLIT : + g_ascii_strncasecmp(data, "rs", 2) == 0 ? MAIN_WINDOW_TYPE_RSPLIT : MAIN_WINDOW_TYPE_DEFAULT; signal_emit("gui window create override", 1, GINT_TO_POINTER(type)); window = window_create(NULL, FALSE); diff --git a/src/fe-common/core/window-items.c b/src/fe-common/core/window-items.c index bd6ae5e9..8eab7124 100644 --- a/src/fe-common/core/window-items.c +++ b/src/fe-common/core/window-items.c @@ -314,7 +314,7 @@ void window_item_create(WI_ITEM_REC *item, int automatic) /* create new window to use */ if (settings_get_bool("autocreate_split_windows")) { signal_emit("gui window create override", 1, - GINT_TO_POINTER(0)); + GINT_TO_POINTER(MAIN_WINDOW_TYPE_SPLIT)); } window = window_create(item, automatic); } else { diff --git a/src/fe-common/irc/fe-netjoin.c b/src/fe-common/irc/fe-netjoin.c index 8272093f..a7a2e4fe 100644 --- a/src/fe-common/irc/fe-netjoin.c +++ b/src/fe-common/irc/fe-netjoin.c @@ -245,23 +245,18 @@ static void print_netjoins(NETJOIN_SERVER_REC *server, const char *filter_channe message before it. */ static void sig_print_starting(TEXT_DEST_REC *dest) { - NETJOIN_SERVER_REC *rec; + GSList *tmp, *next; if (printing_joins) return; - if (!IS_IRC_SERVER(dest->server)) - return; - - if (!(dest->level & MSGLEVEL_PUBLIC)) - return; - - if (!server_ischannel(dest->server, dest->target)) - return; + for (tmp = joinservers; tmp != NULL; tmp = next) { + NETJOIN_SERVER_REC *server = tmp->data; - rec = netjoin_find_server(IRC_SERVER(dest->server)); - if (rec != NULL && rec->netjoins != NULL) - print_netjoins(rec, dest->target); + next = tmp->next; + if (server->netjoins != NULL) + print_netjoins(server, NULL); + } } static int sig_check_netjoins(void) diff --git a/src/fe-common/irc/fe-netsplit.c b/src/fe-common/irc/fe-netsplit.c index 4c69dd10..edd3fc34 100644 --- a/src/fe-common/irc/fe-netsplit.c +++ b/src/fe-common/irc/fe-netsplit.c @@ -199,7 +199,7 @@ static void temp_split_chan_free(TEMP_SPLIT_CHAN_REC *rec) g_free(rec); } -static void print_splits(IRC_SERVER_REC *server, const char *channel) +static void print_splits(IRC_SERVER_REC *server, const char *filter_channel) { TEMP_SPLIT_REC temp; GSList *servers; @@ -218,7 +218,7 @@ static void print_splits(IRC_SERVER_REC *server, const char *channel) g_hash_table_foreach(server->splits, (GHFunc) get_server_splits, &temp); - print_server_splits(server, &temp, channel); + print_server_splits(server, &temp, filter_channel); g_slist_foreach(temp.channels, (GFunc) temp_split_chan_free, NULL); @@ -247,23 +247,17 @@ static int check_server_splits(IRC_SERVER_REC *server) message before it. */ static void sig_print_starting(TEXT_DEST_REC *dest) { - IRC_SERVER_REC *rec; + GSList *tmp; if (printing_splits) return; - if (!IS_IRC_SERVER(dest->server)) - return; - - if (!(dest->level & MSGLEVEL_PUBLIC)) - return; - - if (!server_ischannel(dest->server, dest->target)) - return; + for (tmp = servers; tmp != NULL; tmp = tmp->next) { + IRC_SERVER_REC *rec = tmp->data; - rec = IRC_SERVER(dest->server); - if (rec->split_servers != NULL) - print_splits(rec, dest->target); + if (IS_IRC_SERVER(rec) && rec->split_servers != NULL) + print_splits(rec, NULL); + } } static int sig_check_splits(void) diff --git a/src/fe-fuzz/Makefile.am b/src/fe-fuzz/Makefile.am index c11b3dbb..40abd5ba 100644 --- a/src/fe-fuzz/Makefile.am +++ b/src/fe-fuzz/Makefile.am @@ -1,3 +1,5 @@ +SUBDIRS = irc fe-common + bin_PROGRAMS = irssi-fuzz # Force link with CXX for libfuzzer support diff --git a/src/fe-fuzz/fe-common/Makefile.am b/src/fe-fuzz/fe-common/Makefile.am new file mode 100644 index 00000000..52770885 --- /dev/null +++ b/src/fe-fuzz/fe-common/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = core diff --git a/src/fe-fuzz/fe-common/core/Makefile.am b/src/fe-fuzz/fe-common/core/Makefile.am new file mode 100644 index 00000000..4fe5937c --- /dev/null +++ b/src/fe-fuzz/fe-common/core/Makefile.am @@ -0,0 +1,46 @@ +bin_PROGRAMS = theme-load-fuzz + +# Force link with CXX for libfuzzer support +CCLD=$(CXX) $(CXXFLAGS) + +AM_CPPFLAGS = \ + -I$(top_srcdir)/src \ + -I$(top_srcdir)/src/core/ \ + -I$(top_srcdir)/src/irc/core/ \ + -I$(top_srcdir)/src/fe-common/core/ \ + $(GLIB_CFLAGS) + +AM_DEPENDENCIES = \ + ../../../core/libcore.a \ + ../../../lib-config/libirssi_config.a \ + ../../../irc/libirc.a \ + ../../../irc/core/libirc_core.a \ + ../../../irc/dcc/libirc_dcc.a \ + ../../../irc/flood/libirc_flood.a \ + ../../../irc/notifylist/libirc_notifylist.a \ + ../../../fe-common/core/libfe_common_core.a \ + ../../../fe-common/irc/libfe_common_irc.a \ + ../../../fe-common/irc/dcc/libfe_irc_dcc.a \ + ../../../fe-common/irc/notifylist/libfe_irc_notifylist.a + +LDADD = \ + ../../../irc/libirc.a \ + ../../../irc/core/libirc_core.a \ + ../../../irc/dcc/libirc_dcc.a \ + ../../../irc/flood/libirc_flood.a \ + ../../../irc/notifylist/libirc_notifylist.a \ + ../../../fe-common/core/libfe_common_core.a \ + ../../../fe-common/irc/libfe_common_irc.a \ + ../../../fe-common/irc/dcc/libfe_irc_dcc.a \ + ../../../fe-common/irc/notifylist/libfe_irc_notifylist.a \ + ../../../core/libcore.a \ + ../../../lib-config/libirssi_config.a \ + @PROG_LIBS@ \ + $(FUZZER_LIBS) + +theme_load_fuzz_SOURCES = \ + theme-load.c \ + $(top_srcdir)/src/fe-text/module-formats.c + +noinst_HEADERS = \ + $(top_srcdir)/src/fe-text/module-formats.h diff --git a/src/fe-fuzz/fe-common/core/theme-load.c b/src/fe-fuzz/fe-common/core/theme-load.c new file mode 100644 index 00000000..14df74c6 --- /dev/null +++ b/src/fe-fuzz/fe-common/core/theme-load.c @@ -0,0 +1,66 @@ +/* + theme-load.c : irssi + + Copyright (C) 2018 Joseph Bisch + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "module.h" +#include "modules-load.h" +#include "levels.h" +#include "../fe-text/module-formats.h" // need to explicitly grab from fe-text +#include "themes.h" +#include "core.h" +#include "fe-common-core.h" +#include "args.h" +#include "printtext.h" +#include "irc.h" +#include "themes.h" + +#include <stddef.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + +int LLVMFuzzerInitialize(int *argc, char ***argv) { + core_register_options(); + fe_common_core_register_options(); + char *irssi_argv[] = {*argv[0], "--home", "/tmp/irssi", NULL}; + int irssi_argc = sizeof(irssi_argv) / sizeof(char *) - 1; + args_execute(irssi_argc, irssi_argv); + core_preinit((*argv)[0]); + core_init(); + fe_common_core_init(); + theme_register(gui_text_formats); + module_register("core", "fe-fuzz"); + return 0; +} + +int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + gchar *copy = g_strndup((const gchar *)data, size); + + FILE *fp = fopen("/tmp/irssi/fuzz.theme", "wb"); + if (fp) { + fwrite(copy, strlen(copy), 1, fp); + fclose(fp); + } + + THEME_REC *theme = theme_load("fuzz"); + theme_destroy(theme); + + g_free(copy); + return 0; +} diff --git a/src/fe-fuzz/irc/Makefile.am b/src/fe-fuzz/irc/Makefile.am new file mode 100644 index 00000000..52770885 --- /dev/null +++ b/src/fe-fuzz/irc/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = core diff --git a/src/fe-fuzz/irc/core/Makefile.am b/src/fe-fuzz/irc/core/Makefile.am new file mode 100644 index 00000000..fa614abb --- /dev/null +++ b/src/fe-fuzz/irc/core/Makefile.am @@ -0,0 +1,46 @@ +bin_PROGRAMS = event-get-params-fuzz + +# Force link with CXX for libfuzzer support +CCLD=$(CXX) $(CXXFLAGS) + +AM_CPPFLAGS = \ + -I$(top_srcdir)/src \ + -I$(top_srcdir)/src/core/ \ + -I$(top_srcdir)/src/irc/core/ \ + -I$(top_srcdir)/src/fe-common/core/ \ + $(GLIB_CFLAGS) + +AM_DEPENDENCIES = \ + ../../../core/libcore.a \ + ../../../lib-config/libirssi_config.a \ + ../../../irc/libirc.a \ + ../../../irc/core/libirc_core.a \ + ../../../irc/dcc/libirc_dcc.a \ + ../../../irc/flood/libirc_flood.a \ + ../../../irc/notifylist/libirc_notifylist.a \ + ../../../fe-common/core/libfe_common_core.a \ + ../../../fe-common/irc/libfe_common_irc.a \ + ../../../fe-common/irc/dcc/libfe_irc_dcc.a \ + ../../../fe-common/irc/notifylist/libfe_irc_notifylist a + +LDADD = \ + ../../../irc/libirc.a \ + ../../../irc/core/libirc_core.a \ + ../../../irc/dcc/libirc_dcc.a \ + ../../../irc/flood/libirc_flood.a \ + ../../../irc/notifylist/libirc_notifylist.a \ + ../../../fe-common/core/libfe_common_core.a \ + ../../../fe-common/irc/libfe_common_irc.a \ + ../../../fe-common/irc/dcc/libfe_irc_dcc.a \ + ../../../fe-common/irc/notifylist/libfe_irc_notifylist.a \ + ../../../core/libcore.a \ + ../../../lib-config/libirssi_config.a \ + @PROG_LIBS@ \ + $(FUZZER_LIBS) + +event_get_params_fuzz_SOURCES = \ + event-get-params.c \ + $(top_srcdir)/src/fe-text/module-formats.c + +noinst_HEADERS = \ + $(top_srcdir)/src/fe-text/module-formats.h diff --git a/src/fe-fuzz/irc/core/event-get-params.c b/src/fe-fuzz/irc/core/event-get-params.c new file mode 100644 index 00000000..c50b6205 --- /dev/null +++ b/src/fe-fuzz/irc/core/event-get-params.c @@ -0,0 +1,82 @@ +/* + event-get-params.c : irssi + + Copyright (C) 2017 Joseph Bisch + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "module.h" +#include "modules-load.h" +#include "levels.h" +#include "../fe-text/module-formats.h" // need to explicitly grab from fe-text +#include "themes.h" +#include "core.h" +#include "fe-common-core.h" +#include "args.h" +#include "printtext.h" +#include "irc.h" + +#include <stddef.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + +int LLVMFuzzerInitialize(int *argc, char ***argv) { + core_register_options(); + fe_common_core_register_options(); + /* no args */ + args_execute(0, NULL); + core_preinit((*argv)[0]); + core_init(); + fe_common_core_init(); + theme_register(gui_text_formats); + module_register("core", "fe-fuzz"); + return 0; +} + +int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + if (size < 1) { + return 0; + } + uint8_t count = *data; + gchar *copy = g_strndup((const gchar *)data+1, size-1); + + char *output0; + char *output1; + char *output2; + char *output3; + char *params; + if (count % 8 == 0) { + params = event_get_params(copy, 1 | PARAM_FLAG_GETREST, &output0); + } else if (count % 8 == 1) { + params = event_get_params(copy, 2 | PARAM_FLAG_GETREST, &output0, &output1); + } else if (count % 8 == 2) { + params = event_get_params(copy, 3 | PARAM_FLAG_GETREST, &output0, &output1, &output2); + } else if (count % 8 == 3) { + params = event_get_params(copy, 4 | PARAM_FLAG_GETREST, &output0, &output1, &output2, &output3); + } else if (count % 8 == 4) { + params = event_get_params(copy, 1, &output0); + } else if (count % 8 == 5) { + params = event_get_params(copy, 2, &output0, &output1); + } else if (count % 8 == 6) { + params = event_get_params(copy, 3, &output0, &output1, &output2); + } else { + params = event_get_params(copy, 4, &output0, &output1, &output2, &output3); + } + g_free(params); + g_free(copy); + return 0; +} diff --git a/src/fe-text/gui-entry.c b/src/fe-text/gui-entry.c index e91fcfb3..52a39969 100644 --- a/src/fe-text/gui-entry.c +++ b/src/fe-text/gui-entry.c @@ -65,6 +65,10 @@ static void entry_text_grow(GUI_ENTRY_REC *entry, int grow_size) entry->text_alloc = nearest_power(entry->text_alloc+grow_size); entry->text = g_realloc(entry->text, sizeof(unichar) * entry->text_alloc); + + if (entry->uses_extents) + entry->extents = g_realloc(entry->extents, + sizeof(char *) * entry->text_alloc); } GUI_ENTRY_REC *gui_entry_create(int xpos, int ypos, int width, int utf8) @@ -74,14 +78,30 @@ GUI_ENTRY_REC *gui_entry_create(int xpos, int ypos, int width, int utf8) rec = g_new0(GUI_ENTRY_REC, 1); rec->xpos = xpos; rec->ypos = ypos; - rec->width = width; - rec->text_alloc = 1024; + rec->width = width; + rec->text_alloc = 1024; rec->text = g_new(unichar, rec->text_alloc); - rec->text[0] = '\0'; - rec->utf8 = utf8; + rec->extents = NULL; + rec->text[0] = '\0'; + rec->utf8 = utf8; return rec; } +static void destroy_extents(GUI_ENTRY_REC *entry) +{ + if (entry->uses_extents) { + int i; + for (i = 0; i < entry->text_alloc; i++) { + if (entry->extents[i] != NULL) { + g_free(entry->extents[i]); + } + } + } + g_free(entry->extents); + entry->extents = NULL; + entry->uses_extents = FALSE; +} + void gui_entry_destroy(GUI_ENTRY_REC *entry) { GSList *tmp; @@ -100,9 +120,10 @@ void gui_entry_destroy(GUI_ENTRY_REC *entry) } g_slist_free(entry->kill_ring); - g_free(entry->text); + destroy_extents(entry); + g_free(entry->text); g_free(entry->prompt); - g_free(entry); + g_free(entry); } /* big5 functions */ @@ -164,15 +185,36 @@ void big5_to_unichars(const char *str, unichar *out) *out = '\0'; } +/* Return screen length of plain string */ +static int scrlen_str(const char *str, int utf8) +{ + int len = 0; + char *stripped; + g_return_val_if_fail(str != NULL, 0); + + stripped = strip_codes(str); + len = string_width(stripped, utf8 ? TREAT_STRING_AS_UTF8 : TREAT_STRING_AS_BYTES); + g_free(stripped); + return len; +} + /* ----------------------------- */ -static int pos2scrpos(GUI_ENTRY_REC *entry, int pos) +static int pos2scrpos(GUI_ENTRY_REC *entry, int pos, int cursor) { int i; int xpos = 0; + if (!cursor && pos <= 0) + return 0; + + if (entry->uses_extents && entry->extents[0] != NULL) { + xpos += scrlen_str(entry->extents[0], entry->utf8); + } + for (i = 0; i < pos; i++) { unichar c = entry->text[i]; + const char *extent = entry->uses_extents ? entry->extents[i+1] : NULL; if (term_type == TERM_TYPE_BIG5) xpos += big5_width(c); @@ -180,16 +222,26 @@ static int pos2scrpos(GUI_ENTRY_REC *entry, int pos) xpos += unichar_isprint(c) ? mk_wcwidth(c) : 1; else xpos++; + + if (extent != NULL) { + xpos += scrlen_str(extent, entry->utf8); + } + } return xpos; } static int scrpos2pos(GUI_ENTRY_REC *entry, int pos) { - int i, width, xpos; + int i, width, xpos = 0; - for (i = 0, xpos = 0; i < entry->text_len; i++) { + if (entry->uses_extents && entry->extents[0] != NULL) { + xpos += scrlen_str(entry->extents[0], entry->utf8); + } + + for (i = 0; i < entry->text_len && xpos < pos; i++) { unichar c = entry->text[i]; + const char *extent = entry->uses_extents ? entry->extents[i+1] : NULL; if (term_type == TERM_TYPE_BIG5) width = big5_width(c); @@ -198,15 +250,13 @@ static int scrpos2pos(GUI_ENTRY_REC *entry, int pos) else width = 1; - if (xpos + width > pos) - break; xpos += width; - } - if (xpos == pos) - return i; - else - return i-1; + if (extent != NULL) { + xpos += scrlen_str(extent, entry->utf8); + } + } + return i; } /* Fixes the cursor position in screen */ @@ -215,19 +265,19 @@ static void gui_entry_fix_cursor(GUI_ENTRY_REC *entry) int old_scrstart; /* assume prompt len == prompt scrlen */ - int start = pos2scrpos(entry, entry->scrstart); - int now = pos2scrpos(entry, entry->pos); + int start = pos2scrpos(entry, entry->scrstart, FALSE); + int now = pos2scrpos(entry, entry->pos, TRUE); old_scrstart = entry->scrstart; - if (now-start < entry->width - 2 - entry->promptlen && now-start > 0) + if (now-start < entry->width - 2 - entry->promptlen && now-start > 0) { entry->scrpos = now-start; - else if (now < entry->width - 1 - entry->promptlen) { + } else if (now < entry->width - 1 - entry->promptlen) { entry->scrstart = 0; entry->scrpos = now; } else { entry->scrstart = scrpos2pos(entry, now-(entry->width - entry->promptlen)*2/3); - start = pos2scrpos(entry, entry->scrstart); + start = pos2scrpos(entry, entry->scrstart, FALSE); entry->scrpos = now - start; } @@ -235,59 +285,140 @@ static void gui_entry_fix_cursor(GUI_ENTRY_REC *entry) entry->redraw_needed_from = 0; } +static char *text_effects_only(const char *p) +{ + GString *str; + + str = g_string_sized_new(strlen(p)); + for (; *p != '\0'; p++) { + if (*p == 4 && p[1] != '\0') { + if (p[1] >= FORMAT_STYLE_SPECIAL) { + g_string_append_len(str, p, 2); + p++; + continue; + } + + /* irssi color */ + if (p[2] != '\0') { +#ifdef TERM_TRUECOLOR + if (p[1] == FORMAT_COLOR_24) { + if (p[3] == '\0') p += 2; + else if (p[4] == '\0') p += 3; + else if (p[5] == '\0') p += 4; + else { + g_string_append_len(str, p, 6); + p += 5; + } + } else { +#endif /* TERM_TRUECOLOR */ + g_string_append_len(str, p, 3); + p += 2; +#ifdef TERM_TRUECOLOR + } +#endif /* TERM_TRUECOLOR */ + continue; + } + } + } + + return g_string_free(str, FALSE); +} + static void gui_entry_draw_from(GUI_ENTRY_REC *entry, int pos) { - int i; - int xpos, end_xpos; + int i, start; + int start_xpos, xpos, new_xpos, end_xpos; + char *tmp; + GString *str; + + start = entry->scrstart + pos; - xpos = entry->xpos + entry->promptlen + - pos2scrpos(entry, pos + entry->scrstart) - - pos2scrpos(entry, entry->scrstart); + start_xpos = xpos = entry->xpos + entry->promptlen + + pos2scrpos(entry, start, FALSE) - + pos2scrpos(entry, entry->scrstart, FALSE); end_xpos = entry->xpos + entry->width; if (xpos > end_xpos) return; + str = g_string_sized_new(entry->text_alloc); + term_set_color(root_window, ATTR_RESET); - term_move(root_window, xpos, entry->ypos); + /* term_move(root_window, xpos, entry->ypos); */ + + if (entry->uses_extents && entry->extents[0] != NULL) { + g_string_append(str, entry->extents[0]); + } + for (i = 0; i < start && i < entry->text_len; i++) { + const char *extent = entry->uses_extents ? entry->extents[i+1] : NULL; + if (extent != NULL) { + g_string_append(str, extent); + } + } + if (i == 0) { + xpos += scrlen_str(str->str, entry->utf8); + } else { + tmp = text_effects_only(str->str); + g_string_assign(str, tmp); + g_free(tmp); + } - for (i = entry->scrstart + pos; i < entry->text_len; i++) { + for (; i < entry->text_len; i++) { unichar c = entry->text[i]; + const char *extent = entry->uses_extents ? entry->extents[i+1] : NULL; + new_xpos = xpos; if (entry->hidden) - xpos++; + new_xpos++; else if (term_type == TERM_TYPE_BIG5) - xpos += big5_width(c); + new_xpos += big5_width(c); else if (entry->utf8) - xpos += unichar_isprint(c) ? mk_wcwidth(c) : 1; + new_xpos += unichar_isprint(c) ? mk_wcwidth(c) : 1; else - xpos++; + new_xpos++; - if (xpos > end_xpos) + if (new_xpos > end_xpos) break; if (entry->hidden) - term_addch(root_window, ' '); + g_string_append_c(str, ' '); else if (unichar_isprint(c)) - term_add_unichar(root_window, c); + g_string_append_unichar(str, c); else { - term_set_color(root_window, ATTR_RESET|ATTR_REVERSE); - term_addch(root_window, (c & 127)+'A'-1); - term_set_color(root_window, ATTR_RESET); + g_string_append_c(str, 4); + g_string_append_c(str, FORMAT_STYLE_REVERSE); + g_string_append_c(str, (c & 127)+'A'-1); + g_string_append_c(str, 4); + g_string_append_c(str, FORMAT_STYLE_REVERSE); + } + xpos = new_xpos; + + if (extent != NULL) { + new_xpos += scrlen_str(extent, entry->utf8); + + if (new_xpos > end_xpos) + break; + + g_string_append(str, extent); + xpos = new_xpos; } } /* clear the rest of the input line */ if (xpos < end_xpos) { - if (end_xpos == term_width) - term_clrtoeol(root_window); - else { + if (end_xpos == term_width) { + g_string_append_c(str, 4); + g_string_append_c(str, FORMAT_STYLE_CLRTOEOL); + } else { while (xpos < end_xpos) { - term_addch(root_window, ' '); + g_string_append_c(str, ' '); xpos++; } } } + + gui_printtext_internal(start_xpos, entry->ypos, str->str); + g_string_free(str, TRUE); } static void gui_entry_draw(GUI_ENTRY_REC *entry) @@ -359,19 +490,6 @@ void gui_entry_set_active(GUI_ENTRY_REC *entry) } } -/* Return screen length of plain string */ -static int scrlen_str(const char *str) -{ - int len = 0; - char *stripped; - g_return_val_if_fail(str != NULL, 0); - - stripped = strip_codes(str); - len = string_width(stripped, -1); - g_free(stripped); - return len; -} - void gui_entry_set_prompt(GUI_ENTRY_REC *entry, const char *str) { int oldlen; @@ -382,7 +500,7 @@ void gui_entry_set_prompt(GUI_ENTRY_REC *entry, const char *str) if (str != NULL) { g_free_not_null(entry->prompt); entry->prompt = g_strdup(str); - entry->promptlen = scrlen_str(str); + entry->promptlen = scrlen_str(str, entry->utf8); } if (entry->prompt != NULL) @@ -416,6 +534,7 @@ void gui_entry_set_text(GUI_ENTRY_REC *entry, const char *str) entry->text_len = 0; entry->pos = 0; entry->text[0] = '\0'; + destroy_extents(entry); gui_entry_insert_text(entry, str); } @@ -488,6 +607,15 @@ void gui_entry_insert_text(GUI_ENTRY_REC *entry, const char *str) g_memmove(entry->text + entry->pos + len, entry->text + entry->pos, (entry->text_len-entry->pos + 1) * sizeof(unichar)); + /* make space for the color */ + if (entry->uses_extents) { + g_memmove(entry->extents + entry->pos + len + 1, entry->extents + entry->pos + 1, + (entry->text_len-entry->pos) * sizeof(char *)); + for (i = 0; i < len; i++) { + entry->extents[entry->pos + i + 1] = NULL; + } + } + if (!entry->utf8) { if (term_type == TERM_TYPE_BIG5) { chr = entry->text[entry->pos + len]; @@ -514,7 +642,7 @@ void gui_entry_insert_text(GUI_ENTRY_REC *entry, const char *str) void gui_entry_insert_char(GUI_ENTRY_REC *entry, unichar chr) { - g_return_if_fail(entry != NULL); + g_return_if_fail(entry != NULL); if (chr == 0 || chr == 13 || chr == 10) return; /* never insert NUL, CR or LF characters */ @@ -522,7 +650,7 @@ void gui_entry_insert_char(GUI_ENTRY_REC *entry, unichar chr) if (entry->utf8 && entry->pos == 0 && mk_wcwidth(chr) == 0) return; - gui_entry_redraw_from(entry, entry->pos); + gui_entry_redraw_from(entry, entry->pos); entry_text_grow(entry, 1); @@ -530,9 +658,15 @@ void gui_entry_insert_char(GUI_ENTRY_REC *entry, unichar chr) g_memmove(entry->text + entry->pos + 1, entry->text + entry->pos, (entry->text_len-entry->pos + 1) * sizeof(unichar)); + if (entry->uses_extents) { + g_memmove(entry->extents + entry->pos + 1 + 1, entry->extents + entry->pos + 1, + (entry->text_len-entry->pos) * sizeof(char *)); + entry->extents[entry->pos + 1] = NULL; + } + entry->text[entry->pos] = chr; entry->text_len++; - entry->pos++; + entry->pos++; gui_entry_fix_cursor(entry); gui_entry_draw(entry); @@ -631,7 +765,7 @@ static GUI_ENTRY_CUTBUFFER_REC *get_cutbuffer_rec(GUI_ENTRY_REC *entry, CUTBUFFE void gui_entry_erase(GUI_ENTRY_REC *entry, int size, CUTBUFFER_UPDATE_OP update_cutbuffer) { - size_t w = 0; + size_t i, w = 0; g_return_if_fail(entry != NULL); @@ -700,6 +834,23 @@ void gui_entry_erase(GUI_ENTRY_REC *entry, int size, CUTBUFFER_UPDATE_OP update_ g_memmove(entry->text + entry->pos - size, entry->text + entry->pos, (entry->text_len-entry->pos+1) * sizeof(unichar)); + if (entry->uses_extents) { + for (i = entry->pos - size; i < entry->pos; i++) { + if (entry->extents[i+1] != NULL) { + g_free(entry->extents[i+1]); + } + } + g_memmove(entry->extents + entry->pos - size + 1, entry->extents + entry->pos + 1, + (entry->text_len - entry->pos) * sizeof(void *)); /* no null terminator here */ + for (i = 0; i < size; i++) { + entry->extents[entry->text_len - i] = NULL; + } + if (entry->text_len == size && entry->extents[0] != NULL) { + g_free(entry->extents[0]); + entry->extents[0] = NULL; + } + } + entry->pos -= size; entry->text_len -= size; @@ -719,11 +870,28 @@ void gui_entry_erase_cell(GUI_ENTRY_REC *entry) mk_wcwidth(entry->text[entry->pos+size]) == 0) size++; g_memmove(entry->text + entry->pos, entry->text + entry->pos + size, - (entry->text_len-entry->pos-size+1) * sizeof(unichar)); + (entry->text_len-entry->pos-size+1) * sizeof(unichar)); + + if (entry->uses_extents) { + int i; + for (i = 0; i < size; i++) { + g_free(entry->extents[entry->pos + i + 1]); + } + g_memmove(entry->extents + entry->pos + 1, entry->extents + entry->pos + size + 1, + (entry->text_len-entry->pos-size) * sizeof(char *)); + for (i = 0; i < size; i++) { + entry->extents[entry->text_len - i] = NULL; + } + if (entry->text_len == size && entry->extents[0] != NULL) { + g_free(entry->extents[0]); + entry->extents[0] = NULL; + } + } entry->text_len -= size; gui_entry_redraw_from(entry, entry->pos); + gui_entry_fix_cursor(entry); gui_entry_draw(entry); } @@ -782,6 +950,7 @@ void gui_entry_erase_next_word(GUI_ENTRY_REC *entry, int to_space, CUTBUFFER_UPD void gui_entry_transpose_chars(GUI_ENTRY_REC *entry) { unichar chr; + char *extent; if (entry->pos == 0 || entry->text_len < 2) return; @@ -794,6 +963,12 @@ void gui_entry_transpose_chars(GUI_ENTRY_REC *entry) entry->text[entry->pos] = entry->text[entry->pos-1]; entry->text[entry->pos-1] = chr; + if (entry->uses_extents) { + extent = entry->extents[entry->pos+1]; + entry->extents[entry->pos+1] = entry->extents[entry->pos]; + entry->extents[entry->pos] = extent; + } + entry->pos++; gui_entry_redraw_from(entry, entry->pos-2); @@ -830,31 +1005,60 @@ void gui_entry_transpose_words(GUI_ENTRY_REC *entry) /* do wordswap if any found */ if (spos1 < epos1 && epos1 < spos2 && spos2 < epos2) { unichar *first, *sep, *second; + char **first_extent, **sep_extent, **second_extent; int i; first = (unichar *) g_malloc( (epos1 - spos1) * sizeof(unichar) ); sep = (unichar *) g_malloc( (spos2 - epos1) * sizeof(unichar) ); second = (unichar *) g_malloc( (epos2 - spos2) * sizeof(unichar) ); - for (i = spos1; i < epos1; i++) + first_extent = (char **) g_malloc( (epos1 - spos1) * sizeof(char *) ); + sep_extent = (char **) g_malloc( (spos2 - epos1) * sizeof(char *) ); + second_extent = (char **) g_malloc( (epos2 - spos2) * sizeof(char *) ); + + for (i = spos1; i < epos1; i++) { first[i-spos1] = entry->text[i]; - for (i = epos1; i < spos2; i++) + if (entry->uses_extents) + first_extent[i-spos1] = entry->extents[i+1]; + } + for (i = epos1; i < spos2; i++) { sep[i-epos1] = entry->text[i]; - for (i = spos2; i < epos2; i++) + if (entry->uses_extents) + sep_extent[i-epos1] = entry->extents[i+1]; + } + for (i = spos2; i < epos2; i++) { second[i-spos2] = entry->text[i]; + if (entry->uses_extents) + second_extent[i-spos2] = entry->extents[i+1]; + } entry->pos = spos1; - for (i = 0; i < epos2-spos2; i++) - entry->text[entry->pos++] = second[i]; - for (i = 0; i < spos2-epos1; i++) - entry->text[entry->pos++] = sep[i]; - for (i = 0; i < epos1-spos1; i++) - entry->text[entry->pos++] = first[i]; + for (i = 0; i < epos2-spos2; i++) { + entry->text[entry->pos] = second[i]; + if (entry->uses_extents) + entry->extents[entry->pos+1] = second_extent[i]; + entry->pos++; + } + for (i = 0; i < spos2-epos1; i++) { + entry->text[entry->pos] = sep[i]; + if (entry->uses_extents) + entry->extents[entry->pos+1] = sep_extent[i]; + entry->pos++; + } + for (i = 0; i < epos1-spos1; i++) { + entry->text[entry->pos] = first[i]; + if (entry->uses_extents) + entry->extents[entry->pos+1] = first_extent[i]; + entry->pos++; + } g_free(first); g_free(sep); g_free(second); + g_free(first_extent); + g_free(sep_extent); + g_free(second_extent); } gui_entry_redraw_from(entry, spos1); @@ -938,11 +1142,17 @@ void gui_entry_set_pos(GUI_ENTRY_REC *entry, int pos) void gui_entry_set_text_and_pos_bytes(GUI_ENTRY_REC *entry, const char *str, int pos_bytes) { - int pos; + int pos, extents_alloc; + char **extents; const char *ptr; g_return_if_fail(entry != NULL); + extents = entry->extents; + extents_alloc = entry->text_alloc; + entry->extents = NULL; + entry->uses_extents = FALSE; + gui_entry_set_text(entry, str); if (entry->utf8) { @@ -953,6 +1163,19 @@ void gui_entry_set_text_and_pos_bytes(GUI_ENTRY_REC *entry, const char *str, int else pos = pos_bytes; + if (extents != NULL) { + entry->uses_extents = TRUE; + entry->extents = extents; + if (extents_alloc < entry->text_alloc) { + int i; + entry->extents = g_realloc(entry->extents, + sizeof(char *) * entry->text_alloc); + for (i = extents_alloc; i < entry->text_alloc; i++) { + entry->extents[i] = NULL; + } + } + } + gui_entry_redraw_from(entry, 0); gui_entry_set_pos(entry, pos); } @@ -1042,3 +1265,234 @@ void gui_entry_redraw(GUI_ENTRY_REC *entry) gui_entry_fix_cursor(entry); gui_entry_draw(entry); } + +static void gui_entry_alloc_extents(GUI_ENTRY_REC *entry) +{ + entry->uses_extents = TRUE; + entry->extents = g_new0(char *, entry->text_alloc); +} + +void gui_entry_set_extent(GUI_ENTRY_REC *entry, int pos, const char *text) +{ + int update = FALSE; + + g_return_if_fail(entry != NULL); + + if (pos < 0 || pos > entry->text_len) + return; + + if (text == NULL) + return; + + if (!entry->uses_extents) { + gui_entry_alloc_extents(entry); + } + + if (g_strcmp0(entry->extents[pos], text) != 0) { + g_free(entry->extents[pos]); + if (*text == '\0') { + entry->extents[pos] = NULL; + } else { + entry->extents[pos] = g_strdup(text); + } + update = TRUE; + } + + if (update) { + gui_entry_redraw_from(entry, pos - 1); + gui_entry_fix_cursor(entry); + gui_entry_draw(entry); + } +} + +void gui_entry_set_extents(GUI_ENTRY_REC *entry, int pos, int len, const char *left, const char *right) +{ + int end, update = FALSE; + + g_return_if_fail(entry != NULL); + + if (pos < 0 || len < 0 || pos > entry->text_len) + return; + + end = pos + len; + + if (end > entry->text_len) + end = entry->text_len; + + if (!entry->uses_extents) { + gui_entry_alloc_extents(entry); + } + + if (g_strcmp0(entry->extents[pos], left) != 0) { + g_free(entry->extents[pos]); + if (*left == '\0') { + entry->extents[pos] = NULL; + } else { + entry->extents[pos] = g_strdup(left); + } + update = TRUE; + } + + if (pos != end && g_strcmp0(entry->extents[end], right) != 0) { + g_free(entry->extents[end]); + if (*right == '\0') { + entry->extents[end] = NULL; + } else { + entry->extents[end] = g_strdup(right); + } + update = TRUE; + } + + if (update) { + gui_entry_redraw_from(entry, pos - 1); + gui_entry_fix_cursor(entry); + gui_entry_draw(entry); + } +} + +void gui_entry_clear_extents(GUI_ENTRY_REC *entry, int pos, int len) +{ + int i, end, update = FALSE; + + g_return_if_fail(entry != NULL); + + if (pos < 0 || len < 0 || pos > entry->text_len) + return; + + end = pos + len; + + if (end > entry->text_len) + end = entry->text_len; + + if (!entry->uses_extents) { + return; + } + + for (i = pos; i <= end; i++) { + if (entry->extents[i] != NULL) { + g_free(entry->extents[i]); + entry->extents[i] = NULL; + update = TRUE; + } + } + + if (update) { + gui_entry_redraw_from(entry, pos); + gui_entry_fix_cursor(entry); + gui_entry_draw(entry); + } +} + +char *gui_entry_get_extent(GUI_ENTRY_REC *entry, int pos) +{ + g_return_val_if_fail(entry != NULL, NULL); + + if (!entry->uses_extents) + return NULL; + + if (pos < 0 || pos >= entry->text_len) + return NULL; + + return entry->extents[pos]; +} + +#define POS_FLAG "%|" +GSList *gui_entry_get_text_and_extents(GUI_ENTRY_REC *entry) +{ + GSList *list = NULL; + GString *str; + int i; + + g_return_val_if_fail(entry != NULL, NULL); + + if (entry->uses_extents && entry->extents[0] != NULL) { + if (entry->pos == 0) { + list = g_slist_prepend(list, g_strconcat(entry->extents[0], POS_FLAG, NULL)); + } else { + list = g_slist_prepend(list, g_strdup(entry->extents[0])); + } + } else { + if (entry->pos == 0) { + list = g_slist_prepend(list, g_strdup(POS_FLAG)); + } else { + list = g_slist_prepend(list, NULL); + } + } + + str = g_string_sized_new(entry->text_alloc); + for (i = 0; i < entry->text_len; i++) { + if (entry->utf8) { + g_string_append_unichar(str, entry->text[i]); + } else if (term_type == TERM_TYPE_BIG5) { + if(entry->text[i] > 0xff) + g_string_append_c(str, (entry->text[i] >> 8) & 0xff); + g_string_append_c(str, entry->text[i] & 0xff); + } else { + g_string_append_c(str, entry->text[i]); + } + if (entry->pos == i+1 || (entry->uses_extents && entry->extents[i+1] != NULL)) { + list = g_slist_prepend(list, g_strdup(str->str)); + g_string_truncate(str, 0); + if (entry->uses_extents && entry->extents[i+1] != NULL) { + if (entry->pos == i+1) { + list = g_slist_prepend(list, g_strconcat(entry->extents[i+1], POS_FLAG, NULL)); + } else { + list = g_slist_prepend(list, g_strdup(entry->extents[i+1])); + } + } else if (entry->pos == i+1) { + list = g_slist_prepend(list, g_strdup(POS_FLAG)); + } + } + } + if (str->len > 0) { + list = g_slist_prepend(list, g_strdup(str->str)); + } + list = g_slist_reverse(list); + g_string_free(str, TRUE); + + return list; +} + +void gui_entry_set_text_and_extents(GUI_ENTRY_REC *entry, GSList *list) +{ + GSList *tmp; + int pos = -1; + int is_extent = 1; + + gui_entry_set_text(entry, ""); + for (tmp = list, is_extent = TRUE; tmp != NULL; tmp = tmp->next, is_extent ^= 1) { + if (is_extent) { + char *extent; + int len; + + if (tmp->data == NULL) + continue; + + extent = g_strdup(tmp->data); + len = strlen(extent); + if (len >= strlen(POS_FLAG) && g_strcmp0(&extent[len-strlen(POS_FLAG)], POS_FLAG) == 0) { + char *tmp; + tmp = extent; + extent = g_strndup(tmp, len - strlen(POS_FLAG)); + g_free(tmp); + pos = entry->pos; + } + + if (strlen(extent) > 0) { + gui_entry_set_extent(entry, entry->pos, extent); + } + g_free(extent); + } else { + gui_entry_insert_text(entry, tmp->data); + } + } + gui_entry_set_pos(entry, pos); +} + +void gui_entry_init(void) +{ +} + +void gui_entry_deinit(void) +{ +} diff --git a/src/fe-text/gui-entry.h b/src/fe-text/gui-entry.h index 000c5f03..dff860d3 100644 --- a/src/fe-text/gui-entry.h +++ b/src/fe-text/gui-entry.h @@ -9,6 +9,7 @@ typedef struct { typedef struct { int text_len, text_alloc; /* as shorts, not chars */ unichar *text; + char **extents; GSList *kill_ring; @@ -26,6 +27,7 @@ typedef struct { unsigned int previous_append_next_kill:1; unsigned int append_next_kill:1; unsigned int yank_preceded:1; + unsigned int uses_extents:1; } GUI_ENTRY_REC; typedef enum { @@ -77,5 +79,14 @@ void gui_entry_move_words(GUI_ENTRY_REC *entry, int count, int to_space); void gui_entry_redraw(GUI_ENTRY_REC *entry); +void gui_entry_set_extent(GUI_ENTRY_REC *entry, int pos, const char *text); +void gui_entry_set_extents(GUI_ENTRY_REC *entry, int pos, int len, const char *left, const char *right); +void gui_entry_clear_extents(GUI_ENTRY_REC *entry, int pos, int len); +char *gui_entry_get_extent(GUI_ENTRY_REC *entry, int pos); +GSList *gui_entry_get_text_and_extents(GUI_ENTRY_REC *entry); +void gui_entry_set_text_and_extents(GUI_ENTRY_REC *entry, GSList *list); + +void gui_entry_init(void); +void gui_entry_deinit(void); #endif diff --git a/src/fe-text/gui-printtext.c b/src/fe-text/gui-printtext.c index a07451fa..c52f9ced 100644 --- a/src/fe-text/gui-printtext.c +++ b/src/fe-text/gui-printtext.c @@ -24,6 +24,7 @@ #include "formats.h" #include "printtext.h" +#include "themes.h" #include "term.h" #include "gui-printtext.h" @@ -138,6 +139,39 @@ void gui_printtext_after(TEXT_DEST_REC *dest, LINE_REC *prev, const char *str) gui_printtext_after_time(dest, prev, str, 0); } +void gui_printtext_window_border(int x, int y) +{ + char *v0, *v1; + int len; + if (current_theme != NULL) { + v1 = theme_format_expand(current_theme, "{window_border} "); + len = format_real_length(v1, 1); + v1[len] = '\0'; + } + else { + v1 = g_strdup(" "); + } + + if (*v1 == '\0') { + g_free(v1); + v1 = g_strdup(" "); + } + + if (clrtoeol_info->color != NULL) { + char *color = g_strdup(clrtoeol_info->color); + len = format_real_length(color, 0); + color[len] = '\0'; + v0 = g_strconcat(color, v1, NULL); + g_free(color); + g_free(v1); + } else { + v0 = v1; + } + + gui_printtext(x, y, v0); + g_free(v0); +} + static void remove_old_lines(TEXT_BUFFER_VIEW_REC *view) { LINE_REC *line; @@ -236,8 +270,13 @@ static void sig_gui_print_text(WINDOW_REC *window, void *fgcolor, term_set_color2(root_window, attr, fg, bg); term_move(root_window, next_xpos, next_ypos); - if (flags & GUI_PRINT_FLAG_CLRTOEOL) - term_clrtoeol(root_window); + if (flags & GUI_PRINT_FLAG_CLRTOEOL) { + if (clrtoeol_info->window != NULL) { + term_window_clrtoeol_abs(clrtoeol_info->window, next_ypos); + } else { + term_clrtoeol(root_window); + } + } next_xpos += term_addstr(root_window, str); return; } diff --git a/src/fe-text/gui-printtext.h b/src/fe-text/gui-printtext.h index d2671497..64595bbe 100644 --- a/src/fe-text/gui-printtext.h +++ b/src/fe-text/gui-printtext.h @@ -20,5 +20,6 @@ void gui_printtext(int xpos, int ypos, const char *str); 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); #endif diff --git a/src/fe-text/gui-readline.c b/src/fe-text/gui-readline.c index b3a78396..88e827a7 100644 --- a/src/fe-text/gui-readline.c +++ b/src/fe-text/gui-readline.c @@ -1174,6 +1174,7 @@ void gui_readline_init(void) key_bind("key", NULL, "^H", "backspace", (SIGNAL_FUNC) key_combo); key_bind("key", NULL, "^?", "backspace", (SIGNAL_FUNC) key_combo); key_bind("key", NULL, "^I", "tab", (SIGNAL_FUNC) key_combo); + key_bind("key", NULL, "meta2-Z", "stab", (SIGNAL_FUNC) key_combo); /* meta */ key_bind("key", NULL, "^[", "meta", (SIGNAL_FUNC) key_combo); @@ -1278,7 +1279,7 @@ void gui_readline_init(void) /* line transmitting */ key_bind("send_line", "Execute the input line", "return", NULL, (SIGNAL_FUNC) key_send_line); - key_bind("word_completion_backward", "", NULL, NULL, (SIGNAL_FUNC) key_word_completion_backward); + key_bind("word_completion_backward", "Choose previous completion suggestion", "stab", NULL, (SIGNAL_FUNC) key_word_completion_backward); key_bind("word_completion", "Complete the current word", "tab", NULL, (SIGNAL_FUNC) key_word_completion); key_bind("erase_completion", "Remove the completion added by word_completion", "meta-k", NULL, (SIGNAL_FUNC) key_erase_completion); key_bind("check_replaces", "Check word replaces", NULL, NULL, (SIGNAL_FUNC) key_check_replaces); diff --git a/src/fe-text/gui-windows.c b/src/fe-text/gui-windows.c index 34c55772..3efb9803 100644 --- a/src/fe-text/gui-windows.c +++ b/src/fe-text/gui-windows.c @@ -75,17 +75,18 @@ static void gui_window_created(WINDOW_REC *window, void *automatic) g_return_if_fail(window != NULL); - new_parent = window_create_override == 0 || - window_create_override == 2 || + new_parent = window_create_override == MAIN_WINDOW_TYPE_DEFAULT || + window_create_override == MAIN_WINDOW_TYPE_SPLIT || + window_create_override == MAIN_WINDOW_TYPE_RSPLIT || active_win == NULL || WINDOW_GUI(active_win) == NULL; - parent = !new_parent ? WINDOW_MAIN(active_win) : mainwindow_create(); + parent = !new_parent ? WINDOW_MAIN(active_win) : mainwindow_create(window_create_override == MAIN_WINDOW_TYPE_RSPLIT); if (parent == NULL) { /* not enough space for new window, but we really can't abort creation of the window anymore, so create hidden window instead. */ parent = WINDOW_MAIN(active_win); } - window_create_override = -1; + window_create_override = MAIN_WINDOW_TYPE_NONE; if (parent->active == NULL) parent->active = window; window->gui_data = gui_window_init(window, parent); @@ -285,13 +286,14 @@ static void read_settings(void) void gui_windows_init(void) { - settings_add_bool("lookandfeel", "autostick_split_windows", TRUE); + settings_add_bool("lookandfeel", "autostick_split_windows", FALSE); + settings_add_bool("lookandfeel", "autounstick_windows", TRUE); settings_add_int("lookandfeel", "indent", 10); settings_add_bool("lookandfeel", "indent_always", FALSE); settings_add_bool("lookandfeel", "break_wide", FALSE); settings_add_bool("lookandfeel", "scroll", TRUE); - window_create_override = -1; + window_create_override = MAIN_WINDOW_TYPE_NONE; read_settings(); signal_add("gui window create override", (SIGNAL_FUNC) sig_window_create_override); diff --git a/src/fe-text/irssi.c b/src/fe-text/irssi.c index ef443670..f30ce4b8 100644 --- a/src/fe-text/irssi.c +++ b/src/fe-text/irssi.c @@ -165,6 +165,7 @@ static void textui_finish_init(void) gui_expandos_init(); gui_printtext_init(); gui_readline_init(); + gui_entry_init(); lastlog_init(); mainwindows_init(); mainwindow_activity_init(); @@ -207,6 +208,8 @@ static void textui_finish_init(void) fe_settings_set_print("nick"); if (user_settings_changed & USER_SETTINGS_HOSTNAME) fe_settings_set_print("hostname"); + + term_environment_check(); } static void textui_deinit(void) @@ -228,6 +231,7 @@ static void textui_deinit(void) lastlog_deinit(); statusbar_deinit(); + gui_entry_deinit(); gui_printtext_deinit(); gui_readline_deinit(); gui_windows_deinit(); diff --git a/src/fe-text/mainwindows-layout.c b/src/fe-text/mainwindows-layout.c index acbcb6b9..17c6647b 100644 --- a/src/fe-text/mainwindows-layout.c +++ b/src/fe-text/mainwindows-layout.c @@ -55,9 +55,9 @@ static void sig_layout_window_save(WINDOW_REC *window, CONFIG_NODE *node) static void sig_layout_window_restore(WINDOW_REC *window, CONFIG_NODE *node) { WINDOW_REC *parent; - GUI_WINDOW_REC *gui; + GUI_WINDOW_REC *gui; - gui = WINDOW_GUI(window); + gui = WINDOW_GUI(window); parent = window_find_refnum(config_node_get_int(node, "parent", -1)); if (parent != NULL) @@ -71,7 +71,7 @@ static void sig_layout_window_restore(WINDOW_REC *window, CONFIG_NODE *node) if (config_node_get_str(node, "scroll", NULL) != NULL) { gui->use_scroll = TRUE; gui->scroll = config_node_get_bool(node, "scroll", TRUE); - textbuffer_view_set_scroll(gui->view, gui->scroll); + textbuffer_view_set_scroll(gui->view, gui->scroll); } } @@ -84,6 +84,8 @@ static void main_window_save(MAIN_WINDOW_REC *window, CONFIG_NODE *node) iconfig_node_set_int(node, "first_line", window->first_line); iconfig_node_set_int(node, "lines", window->height); + iconfig_node_set_int(node, "first_column", window->first_column); + iconfig_node_set_int(node, "columns", window->width); } static void sig_layout_save(void) @@ -98,8 +100,16 @@ static void sig_layout_save(void) static int window_node_cmp(CONFIG_NODE *n1, CONFIG_NODE *n2) { - return config_node_get_int(n1, "first_line", 0) > - config_node_get_int(n2, "first_line", 0) ? -1 : 1; + return (config_node_get_int(n1, "first_line", 0) == + config_node_get_int(n2, "first_line", 0) + && + config_node_get_int(n1, "first_column", 0) > + config_node_get_int(n2, "first_column", 0) + ) || + config_node_get_int(n1, "first_line", 0) > + config_node_get_int(n2, "first_line", 0) + ? -1 + : 1; } /* Returns list of mainwindow nodes sorted by first_line @@ -118,14 +128,45 @@ static GSList *get_sorted_windows_config(CONFIG_NODE *node) return output; } +static GSList *get_windows_config_filter_line(GSList *in) +{ + GSList *tmp, *output; + + output = NULL; + for (tmp = in; tmp != NULL; tmp = tmp->next) { + CONFIG_NODE *node = tmp->data; + if (config_node_get_int(node, "first_column", 0) == 0) + output = g_slist_append(output, node); + } + + return output; +} + +static GSList *get_windows_config_filter_column(GSList *in, int first_line, int last_line) +{ + GSList *tmp, *output; + + output = NULL; + for (tmp = in; tmp != NULL; tmp = tmp->next) { + int l1, l2; + CONFIG_NODE *node = tmp->data; + l1 = config_node_get_int(node, "first_line", -1); + l2 = l1 + config_node_get_int(node, "lines", 0) - 1; + if (l1 >= first_line && l2 <= last_line) + output = g_slist_prepend(output, node); + } + + return output; +} + static void sig_layout_restore(void) { - MAIN_WINDOW_REC *lower_window; - WINDOW_REC *window; + MAIN_WINDOW_REC *lower_window; + WINDOW_REC *window, *first; CONFIG_NODE *node; - GSList *tmp, *sorted_config; - int avail_height, height, *heights; - int i, lower_size, windows_count, diff; + GSList *tmp, *sorted_config, *lines_config; + int avail_height, height, *heights, *widths, max_wins_line; + int i, lower_size, lines_count, columns_count, diff; node = iconfig_node_traverse("mainwindows", FALSE); if (node == NULL) return; @@ -133,51 +174,56 @@ static void sig_layout_restore(void) sorted_config = get_sorted_windows_config(node); if (sorted_config == NULL) return; - windows_count = g_slist_length(sorted_config); + lines_config = get_windows_config_filter_line(sorted_config); + lines_count = g_slist_length(lines_config); - /* calculate the saved terminal height */ + /* calculate the saved terminal height */ avail_height = term_height - screen_reserved_top - screen_reserved_bottom; height = 0; - heights = g_new0(int, windows_count); - for (i = 0, tmp = sorted_config; tmp != NULL; tmp = tmp->next, i++) { + heights = g_new0(int, lines_count); + for (i = 0, tmp = lines_config; tmp != NULL; tmp = tmp->next, i++) { CONFIG_NODE *node = tmp->data; - heights[i] = config_node_get_int(node, "lines", 0); + heights[i] = config_node_get_int(node, "lines", 0); height += heights[i]; } + max_wins_line = (term_width + 1) / (NEW_WINDOW_WIDTH + 1); + if (max_wins_line < 1) + max_wins_line = 1; + if (avail_height <= (WINDOW_MIN_SIZE*2)+1) { /* we can fit only one window to screen - give it all the height we can */ - windows_count = 1; - heights[0] = avail_height; + lines_count = 1; + heights[0] = avail_height; } else if (height != avail_height) { /* Terminal's height is different from the saved one. Resize the windows so they fit to screen. */ while (height > avail_height && - windows_count*(WINDOW_MIN_SIZE+1) > avail_height) { + lines_count*(WINDOW_MIN_SIZE+1) > avail_height) { /* all windows can't fit into screen, remove the lowest ones */ - windows_count--; + lines_count--; } - /* try to keep the windows' size about the same in percents */ - for (i = 0; i < windows_count; i++) { + /* try to keep the windows' size about the same in percents */ + for (i = 0; i < lines_count; i++) { int size = avail_height*heights[i]/height; if (size < WINDOW_MIN_SIZE+1) - size = WINDOW_MIN_SIZE+1; + size = WINDOW_MIN_SIZE+1; heights[i] = size; } /* give/remove the last bits */ - height = 0; - for (i = 0; i < windows_count; i++) - height += heights[i]; + height = 0; + for (i = 0; i < lines_count; i++) + height += heights[i]; diff = height < avail_height ? 1 : -1; for (i = 0; height != avail_height; i++) { - if (i == windows_count) + if (i == lines_count) i = 0; if (heights[i] > WINDOW_MIN_SIZE+1) { @@ -188,25 +234,59 @@ static void sig_layout_restore(void) } /* create all the visible windows with correct size */ - lower_window = NULL; lower_size = 0; - for (i = 0, tmp = sorted_config; i < windows_count; tmp = tmp->next, i++) { + lower_window = NULL; lower_size = 0; first = NULL; + for (i = 0, tmp = lines_config; i < lines_count; tmp = tmp->next, i++) { + GSList *tmp2, *columns_config, *line; + int j, l1, l2; CONFIG_NODE *node = tmp->data; if (node->key == NULL) continue; - /* create a new window + mainwindow */ - signal_emit("gui window create override", 1, - GINT_TO_POINTER(0)); + l1 = config_node_get_int(node, "first_line", -1); + l2 = l1 + config_node_get_int(node, "lines", 0) - 1; + columns_config = get_windows_config_filter_column(sorted_config, l1, l2); + + window = NULL; columns_count = 0; + widths = g_new0(int, max_wins_line); + for (j = 0, tmp2 = columns_config; j < max_wins_line && tmp2 != NULL; tmp2 = tmp2->next, j++) { + int width; + WINDOW_REC *new_win; + CONFIG_NODE *node2 = tmp2->data; + if (node2->key == NULL) continue; + + /* create a new window + mainwindow */ + signal_emit("gui window create override", 1, + GINT_TO_POINTER(window == NULL ? MAIN_WINDOW_TYPE_SPLIT : MAIN_WINDOW_TYPE_RSPLIT)); + + new_win = window_create(NULL, TRUE); - window = window_create(NULL, TRUE); - window_set_refnum(window, atoi(node->key)); + window_set_refnum(new_win, atoi(node2->key)); + width = config_node_get_int(node2, "columns", NEW_WINDOW_WIDTH); + widths[j] = width; + columns_count += width + (window == NULL ? 0 : 1); + + if (window == NULL) + window = new_win; + if (first == NULL) + first = new_win; + + window_set_active(new_win); + active_mainwin = WINDOW_MAIN(new_win); + } + if (window == NULL) + continue; + line = g_slist_reverse(mainwindows_get_line(WINDOW_MAIN(window))); + for (j = g_slist_length(line), tmp2 = line; tmp2 != NULL; tmp2 = tmp2->next, j--) { + int width = MAX(NEW_WINDOW_WIDTH, widths[j-1] * term_width / columns_count); + MAIN_WINDOW_REC *rec = tmp2->data; + mainwindow_set_rsize(rec, width); + } + g_slist_free(line); + g_free(widths); if (lower_size > 0) mainwindow_set_size(lower_window, lower_size, FALSE); - window_set_active(window); - active_mainwin = WINDOW_MAIN(window); - - lower_window = WINDOW_MAIN(window); + lower_window = WINDOW_MAIN(window); lower_size = heights[i]; if (lower_size < WINDOW_MIN_SIZE+1) lower_size = WINDOW_MIN_SIZE+1; @@ -216,6 +296,11 @@ static void sig_layout_restore(void) if (lower_size > 0) mainwindow_set_size(lower_window, lower_size, FALSE); + + if (first != NULL) { + window_set_active(first); + active_mainwin = WINDOW_MAIN(first); + } } static void sig_layout_reset(void) diff --git a/src/fe-text/mainwindows.c b/src/fe-text/mainwindows.c index 5f58c25f..83a3e0cc 100644 --- a/src/fe-text/mainwindows.c +++ b/src/fe-text/mainwindows.c @@ -34,24 +34,27 @@ GSList *mainwindows; MAIN_WINDOW_REC *active_mainwin; +MAIN_WINDOW_BORDER_REC *clrtoeol_info; int screen_reserved_top, screen_reserved_bottom; -static int old_screen_width, old_screen_height; +int screen_reserved_left, screen_reserved_right; +static int screen_width, screen_height; #define mainwindow_create_screen(window) \ - term_window_create(0, \ + term_window_create((window)->first_column + (window)->statusbar_columns_left, \ (window)->first_line + (window)->statusbar_lines_top, \ - (window)->width, \ + (window)->width - (window)->statusbar_columns, \ (window)->height - (window)->statusbar_lines) #define mainwindow_set_screen_size(window) \ - term_window_move((window)->screen_win, 0, \ + term_window_move((window)->screen_win, \ + (window)->first_column + (window)->statusbar_columns_left, \ (window)->first_line + (window)->statusbar_lines_top, \ - (window)->width, \ + (window)->width - (window)->statusbar_columns, \ (window)->height - (window)->statusbar_lines); -static MAIN_WINDOW_REC *find_window_with_room(void) +static MAIN_WINDOW_REC *find_window_with_room() { MAIN_WINDOW_REC *biggest_rec; GSList *tmp; @@ -71,14 +74,34 @@ static MAIN_WINDOW_REC *find_window_with_room(void) return biggest_rec; } +static MAIN_WINDOW_REC *find_window_with_room_right(void) +{ + MAIN_WINDOW_REC *biggest_rec; + GSList *tmp; + int space, biggest; + + biggest = 0; biggest_rec = NULL; + for (tmp = mainwindows; tmp != NULL; tmp = tmp->next) { + MAIN_WINDOW_REC *rec = tmp->data; + + space = MAIN_WINDOW_TEXT_WIDTH(rec); + if (space >= 2 * NEW_WINDOW_WIDTH && space > biggest) { + biggest = space; + biggest_rec = rec; + } + } + + return biggest_rec; +} + #define window_size_equals(window, mainwin) \ - ((window)->width == (mainwin)->width && \ + ((window)->width == MAIN_WINDOW_TEXT_WIDTH(mainwin) && \ (window)->height == MAIN_WINDOW_TEXT_HEIGHT(mainwin)) static void mainwindow_resize_windows(MAIN_WINDOW_REC *window) { GSList *tmp; - int resized; + int resized; mainwindow_set_screen_size(window); @@ -89,24 +112,31 @@ static void mainwindow_resize_windows(MAIN_WINDOW_REC *window) if (rec->gui_data != NULL && WINDOW_GUI(rec)->parent == window && !window_size_equals(rec, window)) { - resized = TRUE; - gui_window_resize(rec, window->width, + resized = TRUE; + gui_window_resize(rec, MAIN_WINDOW_TEXT_WIDTH(window), MAIN_WINDOW_TEXT_HEIGHT(window)); } } - if (resized) + if (resized) signal_emit("mainwindow resized", 1, window); } static void mainwindow_resize(MAIN_WINDOW_REC *window, int xdiff, int ydiff) { + int height, width; if (quitting || (xdiff == 0 && ydiff == 0)) - return; + return; - window->width += xdiff; + height = window->height + ydiff; + width = window->width + xdiff; + window->width = window->last_column-window->first_column+1; window->height = window->last_line-window->first_line+1; - window->size_dirty = TRUE; + if (height != window->height || width != window->width) { + g_warning("Resizing window %p W:%d expected:%d H:%d expected:%d", + window, window->width, width, window->height, height); + } + window->size_dirty = TRUE; } static GSList *get_sticky_windows_sorted(MAIN_WINDOW_REC *mainwin) @@ -178,14 +208,13 @@ void mainwindows_recreate(void) } } -MAIN_WINDOW_REC *mainwindow_create(void) +MAIN_WINDOW_REC *mainwindow_create(int right) { MAIN_WINDOW_REC *rec, *parent; int space; rec = g_new0(MAIN_WINDOW_REC, 1); rec->dirty = TRUE; - rec->width = term_width; if (mainwindows == NULL) { active_mainwin = rec; @@ -193,21 +222,53 @@ MAIN_WINDOW_REC *mainwindow_create(void) rec->first_line = screen_reserved_top; rec->last_line = term_height-1 - screen_reserved_bottom; rec->height = rec->last_line-rec->first_line+1; + rec->first_column = screen_reserved_left; + rec->last_column = screen_width-1 - screen_reserved_right; + rec->width = rec->last_column-rec->first_column+1; } else { parent = WINDOW_MAIN(active_win); - if (MAIN_WINDOW_TEXT_HEIGHT(parent) < - WINDOW_MIN_SIZE+NEW_WINDOW_SIZE) - parent = find_window_with_room(); - if (parent == NULL) - return NULL; /* not enough space */ - - space = parent->height / 2; - rec->first_line = parent->first_line; - rec->last_line = rec->first_line + space; - rec->height = rec->last_line-rec->first_line+1; - parent->first_line += space+1; - mainwindow_resize(parent, 0, -space-1); + if (!right) { + GSList *tmp, *line; + if (MAIN_WINDOW_TEXT_HEIGHT(parent) < + WINDOW_MIN_SIZE+NEW_WINDOW_SIZE) + parent = find_window_with_room(); + if (parent == NULL) + return NULL; /* not enough space */ + + space = parent->height / 2; + rec->first_line = parent->first_line; + rec->last_line = rec->first_line + space; + rec->height = rec->last_line-rec->first_line+1; + rec->first_column = screen_reserved_left; + rec->last_column = screen_width-1 - screen_reserved_right; + rec->width = rec->last_column-rec->first_column+1; + + line = mainwindows_get_line(parent); + for (tmp = line; tmp != NULL; tmp = tmp->next) { + MAIN_WINDOW_REC *rec = tmp->data; + rec->first_line += space+1; + mainwindow_resize(rec, 0, -space-1); + } + g_slist_free(line); + } else { + if (MAIN_WINDOW_TEXT_WIDTH(parent) < + 2* NEW_WINDOW_WIDTH) + parent = find_window_with_room_right(); + if (parent == NULL) + return NULL; /* not enough space */ + + space = parent->width / 2; + rec->first_line = parent->first_line; + rec->last_line = parent->last_line; + rec->height = parent->height; + rec->first_column = parent->last_column - space + 1; + rec->last_column = parent->last_column; + rec->width = rec->last_column-rec->first_column+1; + + parent->last_column -= space+1; + mainwindow_resize(parent, -space-1, 0); + } } rec->screen_win = mainwindow_create_screen(rec); @@ -218,16 +279,22 @@ MAIN_WINDOW_REC *mainwindow_create(void) return rec; } -static MAIN_WINDOW_REC *mainwindows_find_lower(int line) +static MAIN_WINDOW_REC *mainwindows_find_lower(MAIN_WINDOW_REC *window) { + int last_line; MAIN_WINDOW_REC *best; GSList *tmp; + if (window != NULL) + last_line = window->last_line; + else + last_line = -1; + best = NULL; for (tmp = mainwindows; tmp != NULL; tmp = tmp->next) { MAIN_WINDOW_REC *rec = tmp->data; - if (rec->first_line > line && + if (rec->first_line > last_line && (best == NULL || rec->first_line < best->first_line)) best = rec; } @@ -235,16 +302,64 @@ static MAIN_WINDOW_REC *mainwindows_find_lower(int line) return best; } -static MAIN_WINDOW_REC *mainwindows_find_upper(int line) +static MAIN_WINDOW_REC *mainwindows_find_right(MAIN_WINDOW_REC *window, int find_first) { + int first_line, last_line, last_column; MAIN_WINDOW_REC *best; GSList *tmp; + if (window != NULL) { + first_line = window->first_line; + last_line = window->last_line; + last_column = window->last_column; + } else { + first_line = last_line = last_column = -1; + } + + if (find_first) + last_column = -1; + best = NULL; for (tmp = mainwindows; tmp != NULL; tmp = tmp->next) { MAIN_WINDOW_REC *rec = tmp->data; - if (rec->last_line < line && + if (rec->first_line >= first_line && + rec->last_line <= last_line && + rec->first_column > last_column && + (best == NULL || rec->first_column < best->first_column)) + best = rec; + } + + return best; +} + +static MAIN_WINDOW_REC *mainwindows_find_lower_right(MAIN_WINDOW_REC *window) +{ + MAIN_WINDOW_REC *best; + + best = mainwindows_find_right(window, FALSE); + if (best == NULL) + best = mainwindows_find_lower(window); + + return best; +} + +static MAIN_WINDOW_REC *mainwindows_find_upper(MAIN_WINDOW_REC *window) +{ + int first_line; + MAIN_WINDOW_REC *best; + GSList *tmp; + + if (window != NULL) + first_line = window->first_line; + else + first_line = screen_height; + + best = NULL; + for (tmp = mainwindows; tmp != NULL; tmp = tmp->next) { + MAIN_WINDOW_REC *rec = tmp->data; + + if (rec->last_line < first_line && (best == NULL || rec->last_line > best->last_line)) best = rec; } @@ -252,27 +367,142 @@ static MAIN_WINDOW_REC *mainwindows_find_upper(int line) return best; } -static void mainwindows_add_space(int first_line, int last_line) +static MAIN_WINDOW_REC *mainwindows_find_left(MAIN_WINDOW_REC *window, int find_last) +{ + int first_line, last_line, first_column; + MAIN_WINDOW_REC *best; + GSList *tmp; + + if (window != NULL) { + first_line = window->first_line; + last_line = window->last_line; + first_column = window->first_column; + } else { + first_line = last_line = screen_height; + first_column = screen_width; + } + + if (find_last) + first_column = screen_width; + + best = NULL; + for (tmp = mainwindows; tmp != NULL; tmp = tmp->next) { + MAIN_WINDOW_REC *rec = tmp->data; + + if (rec->first_line >= first_line && + rec->last_line <= last_line && + rec->last_column < first_column && + (best == NULL || rec->last_column > best->last_column)) + best = rec; + } + + return best; +} + +static MAIN_WINDOW_REC *mainwindows_find_upper_left(MAIN_WINDOW_REC *window) +{ + MAIN_WINDOW_REC *best; + + best = mainwindows_find_left(window, FALSE); + if (best == NULL) + best = mainwindows_find_upper(window); + + return best; +} + +static MAIN_WINDOW_REC *mainwindows_find_left_upper(MAIN_WINDOW_REC *window) +{ + MAIN_WINDOW_REC *best; + + best = mainwindows_find_left(window, FALSE); + if (best == NULL) + best = mainwindows_find_left(mainwindows_find_upper(window), TRUE); + + return best; +} + +GSList *mainwindows_get_line(MAIN_WINDOW_REC *rec) +{ + MAIN_WINDOW_REC *win; + GSList *list; + + list = NULL; + + for (win = mainwindows_find_left(rec, FALSE); + win != NULL; + win = mainwindows_find_left(win, FALSE)) { + list = g_slist_append(list, win); + } + + if (rec != NULL) + list = g_slist_append(list, rec); + + for (win = mainwindows_find_right(rec, FALSE); + win != NULL; + win = mainwindows_find_right(win, FALSE)) { + list = g_slist_append(list, win); + } + + return list; +} + +/* add back the space which was occupied by destroyed mainwindow first_line .. last_line */ +static void mainwindows_add_space(MAIN_WINDOW_REC *destroy_win) { MAIN_WINDOW_REC *rec; - int size; + int size, rsize; + + if (destroy_win->last_line < destroy_win->first_line) + return; - if (last_line < first_line) + if (destroy_win->last_column < destroy_win->first_column) return; - size = last_line-first_line+1; + rsize = destroy_win->last_column-destroy_win->first_column+1; + rec = mainwindows_find_left(destroy_win, FALSE); + if (rec != NULL) { + rec->last_column = destroy_win->last_column; + mainwindow_resize(rec, rsize+1, 0); + return; + } - rec = mainwindows_find_lower(last_line); + rec = mainwindows_find_right(destroy_win, FALSE); if (rec != NULL) { - rec->first_line = first_line; - mainwindow_resize(rec, 0, size); + rec->first_column = destroy_win->first_column; + mainwindow_resize(rec, rsize+1, 0); return; } - rec = mainwindows_find_upper(first_line); + size = destroy_win->last_line-destroy_win->first_line+1; + + rec = mainwindows_find_lower(destroy_win); if (rec != NULL) { - rec->last_line = last_line; - mainwindow_resize(rec, 0, size); + GSList *tmp, *list; + list = mainwindows_get_line(rec); + + for (tmp = list; tmp != NULL; tmp = tmp->next) { + MAIN_WINDOW_REC *rec = tmp->data; + rec->first_line = destroy_win->first_line; + mainwindow_resize(rec, 0, size); + } + + g_slist_free(list); + return; + } + + rec = mainwindows_find_upper(destroy_win); + if (rec != NULL) { + GSList *tmp, *list; + list = mainwindows_get_line(rec); + + for (tmp = list; tmp != NULL; tmp = tmp->next) { + MAIN_WINDOW_REC *rec = tmp->data; + rec->last_line = destroy_win->last_line; + mainwindow_resize(rec, 0, size); + } + + g_slist_free(list); + return; } } @@ -302,8 +532,7 @@ void mainwindow_destroy(MAIN_WINDOW_REC *window) if (mainwindows != NULL) { gui_windows_remove_parent(window); if (!quitting) { - mainwindows_add_space(window->first_line, - window->last_line); + mainwindows_add_space(window); mainwindows_redraw(); } } @@ -313,6 +542,14 @@ void mainwindow_destroy(MAIN_WINDOW_REC *window) if (active_mainwin == window) active_mainwin = NULL; } +void mainwindow_destroy_half(MAIN_WINDOW_REC *window) +{ + int really_quitting = quitting; + quitting = TRUE; + mainwindow_destroy(window); + quitting = really_quitting; +} + void mainwindows_redraw(void) { GSList *tmp; @@ -327,12 +564,20 @@ void mainwindows_redraw(void) static int mainwindows_compare(MAIN_WINDOW_REC *w1, MAIN_WINDOW_REC *w2) { - return w1->first_line < w2->first_line ? -1 : 1; + return w1->first_line < w2->first_line ? -1 + : w1->first_line > w2->first_line ? 1 + : w1->first_column < w2->first_column ? -1 + : w1->first_column > w2->first_column ? 1 + : 0; } static int mainwindows_compare_reverse(MAIN_WINDOW_REC *w1, MAIN_WINDOW_REC *w2) { - return w1->first_line < w2->first_line ? 1 : -1; + return w1->first_line < w2->first_line ? 1 + : w1->first_line > w2->first_line ? -1 + : w1->first_column < w2->first_column ? 1 + : w1->first_column > w2->first_column ? -1 + : 0; } GSList *mainwindows_get_sorted(int reverse) @@ -348,123 +593,235 @@ GSList *mainwindows_get_sorted(int reverse) return list; } -static void mainwindows_resize_smaller(int xdiff, int ydiff) +static void mainwindows_resize_smaller(int ydiff) { - MAIN_WINDOW_REC *rec; + MAIN_WINDOW_REC *rec; GSList *sorted, *tmp; - int space; + int space; - sorted = mainwindows_get_sorted(TRUE); + sorted = NULL; + for (rec = mainwindows_find_lower(NULL); + rec != NULL; + rec = mainwindows_find_lower(rec)) { + sorted = g_slist_prepend(sorted, rec); + } if (sorted == NULL) return; for (;;) { + int skip_active = FALSE; space = 0; - for (tmp = mainwindows; tmp != NULL; tmp = tmp->next) { + /* for each line of windows, calculate the space that can be reduced still */ + for (tmp = sorted; tmp != NULL; tmp = tmp->next) { + int min; + GSList *line, *ltmp; rec = tmp->data; - space += MAIN_WINDOW_TEXT_HEIGHT(rec)-WINDOW_MIN_SIZE; + line = mainwindows_get_line(rec); + min = screen_height - ydiff; + for (ltmp = line; ltmp != NULL; ltmp = ltmp->next) { + int lmin; + MAIN_WINDOW_REC *win = ltmp->data; + if (win == active_mainwin && tmp == sorted) + skip_active = TRUE; + + lmin = MAIN_WINDOW_TEXT_HEIGHT(win)-WINDOW_MIN_SIZE; + if (lmin < min) + min = lmin; + } + g_slist_free(line); + space += min; } if (space >= -ydiff) break; rec = sorted->data; - if (rec == active_mainwin && sorted->next != NULL) + if (skip_active && sorted->next != NULL) rec = sorted->next->data; sorted = g_slist_remove(sorted, rec); if (sorted != NULL) { /* terminal is too small - destroy the uppest window and try again */ - mainwindow_destroy(rec); + GSList *line, *ltmp; + line = mainwindows_get_line(rec); + for (ltmp = line; ltmp != NULL; ltmp = ltmp->next) { + MAIN_WINDOW_REC *win = ltmp->data; + mainwindow_destroy(win); + } + g_slist_free(line); } else { - /* only one window in screen.. just force the resize */ - rec->last_line += ydiff; - mainwindow_resize(rec, xdiff, ydiff); - return; + /* only one line of window in screen.. just force the resize */ + GSList *line, *ltmp; + line = mainwindows_get_line(rec); + for (ltmp = line; ltmp != NULL; ltmp = ltmp->next) { + MAIN_WINDOW_REC *win = ltmp->data; + win->last_line += ydiff; + mainwindow_resize(win, 0, ydiff); + } + g_slist_free(line); + return; } } /* resize windows that have space */ for (tmp = sorted; tmp != NULL && ydiff < 0; tmp = tmp->next) { - rec = tmp->data; + int min; + GSList *line, *ltmp; - space = MAIN_WINDOW_TEXT_HEIGHT(rec)-WINDOW_MIN_SIZE; - if (space == 0) { - mainwindow_resize(rec, xdiff, 0); - - rec->first_line += ydiff; - rec->last_line += ydiff; - signal_emit("mainwindow moved", 1, rec); - continue; + rec = tmp->data; + line = mainwindows_get_line(rec); + min = screen_height - ydiff; + for (ltmp = line; ltmp != NULL; ltmp = ltmp->next) { + int lmin; + MAIN_WINDOW_REC *win = ltmp->data; + lmin = MAIN_WINDOW_TEXT_HEIGHT(win)-WINDOW_MIN_SIZE; + if (lmin < min) + min = lmin; } + space = min; - if (space > -ydiff) space = -ydiff; - rec->last_line += ydiff; - ydiff += space; - rec->first_line += ydiff; - - mainwindow_resize(rec, xdiff, -space); - } + if (space == 0) { + /* move the line */ + for (ltmp = line; ltmp != NULL; ltmp = ltmp->next) { + MAIN_WINDOW_REC *win = ltmp->data; + mainwindow_resize(win, 0, 0); + win->size_dirty = TRUE; + win->first_line += ydiff; + win->last_line += ydiff; + signal_emit("mainwindow moved", 1, win); + } + } else { + if (space > -ydiff) space = -ydiff; + for (ltmp = line; ltmp != NULL; ltmp = ltmp->next) { + MAIN_WINDOW_REC *win = ltmp->data; + win->last_line += ydiff; + win->first_line += ydiff + space; - if (xdiff != 0) { - while (tmp != NULL) { - mainwindow_resize(tmp->data, xdiff, 0); - tmp = tmp->next; + mainwindow_resize(win, 0, -space); + } + ydiff += space; } + g_slist_free(line); } g_slist_free(sorted); } -static void mainwindows_resize_bigger(int xdiff, int ydiff) +static void mainwindows_rresize_line(int xdiff, MAIN_WINDOW_REC *win) { - GSList *sorted, *tmp; + int windows, i, extra_width, next_column, shrunk; + int *widths; + GSList *line, *tmp; - sorted = mainwindows_get_sorted(FALSE); - for (tmp = sorted; tmp != NULL; tmp = tmp->next) { - MAIN_WINDOW_REC *rec = tmp->data; + line = mainwindows_get_line(win); + windows = g_slist_length(line); + widths = g_new0(int, windows); - if (ydiff == 0 || tmp->next != NULL) { - mainwindow_resize(rec, xdiff, 0); - continue; + extra_width = screen_width - windows + 1; + for (tmp = line, i = 0; tmp != NULL; tmp = tmp->next, i++) { + MAIN_WINDOW_REC *rec = tmp->data; + widths[i] = (MAIN_WINDOW_TEXT_WIDTH(rec) * (screen_width - windows + 1)) / (screen_width - xdiff - windows + 1); + extra_width -= widths[i] + rec->statusbar_columns; + } + shrunk = FALSE; + for (i = windows; extra_width < 0; i = i > 1 ? i - 1 : windows) { + if (widths[i-1] > NEW_WINDOW_WIDTH || (i == 1 && !shrunk)) { + widths[i-1]--; + extra_width++; + shrunk = i == 1; } - - /* lowest window - give all the extra space for it */ - rec->last_line += ydiff; - mainwindow_resize(rec, xdiff, ydiff); } - g_slist_free(sorted); -} -static void mainwindows_resize_horiz(int xdiff) -{ - GSList *tmp; + next_column = 0; - for (tmp = mainwindows; tmp != NULL; tmp = tmp->next) { - MAIN_WINDOW_REC *rec = tmp->data; +#define extra ( (i >= screen_width % windows && i < extra_width + (screen_width % windows)) \ + || i + windows < extra_width + (screen_width % windows) ? 1 : 0 ) - mainwindow_resize(rec, xdiff, 0); + for (tmp = line, i = 0; tmp != NULL; tmp = tmp->next, i++) { + MAIN_WINDOW_REC *rec = tmp->data; + rec->first_column = next_column; + rec->last_column = rec->first_column + widths[i] + rec->statusbar_columns + extra - 1; + next_column = rec->last_column + 2; + mainwindow_resize(rec, widths[i] + rec->statusbar_columns + extra - rec->width, 0); + rec->size_dirty = TRUE; } +#undef extra + + g_free(widths); + g_slist_free(line); } void mainwindows_resize(int width, int height) { int xdiff, ydiff; - xdiff = width-old_screen_width; - ydiff = height-old_screen_height; - old_screen_width = width; - old_screen_height = height; + xdiff = width-screen_width; + ydiff = height-screen_height; + screen_width = width; + screen_height = height; + + if (ydiff > 0) { + /* algorithm: enlarge bottom window */ + MAIN_WINDOW_REC *rec; + GSList *line, *tmp; + line = mainwindows_get_line(mainwindows_find_upper(NULL)); + for (tmp = line; tmp != NULL; tmp = tmp->next) { + rec = tmp->data; + rec->last_line += ydiff; + mainwindow_resize(rec, 0, ydiff); + } + g_slist_free(line); + } - if (ydiff < 0) - mainwindows_resize_smaller(xdiff, ydiff); - else if (ydiff > 0) - mainwindows_resize_bigger(xdiff, ydiff); - else if (xdiff != 0) - mainwindows_resize_horiz(xdiff); + if (xdiff > 0) { + /* algorithm: distribute new space on each line */ + MAIN_WINDOW_REC *win; - signal_emit("terminal resized", 0); + for (win = mainwindows_find_lower(NULL); + win != NULL; + win = mainwindows_find_lower(win)) { + mainwindows_rresize_line(xdiff, win); + } + } + + if (xdiff < 0) { + /* algorithm: shrink each window, + destroy windows on the right if no room */ + MAIN_WINDOW_REC *win; + + for (win = mainwindows_find_lower(NULL); + win != NULL; + win = mainwindows_find_lower(win)) { + int max_windows, i, last_column; + GSList *line, *tmp; + + line = mainwindows_get_line(win); + max_windows = (screen_width + 1) / (NEW_WINDOW_WIDTH + 1); + if (max_windows < 1) + max_windows = 1; + last_column = screen_width - 1; + for (tmp = line, i = 0; tmp != NULL; tmp = tmp->next, i++) { + MAIN_WINDOW_REC *rec = tmp->data; + if (i >= max_windows) + mainwindow_destroy_half(rec); + else + last_column = rec->last_column; + } + win = line->data; + g_slist_free(line); + + mainwindows_rresize_line(screen_width - last_column + 1, win); + } + } + + if (ydiff < 0) { + /* algorithm: shrink windows starting from bottom, + destroy windows starting from top if no room */ + mainwindows_resize_smaller(ydiff); + } + + signal_emit("terminal resized", 0); irssi_redraw(); } @@ -474,31 +831,37 @@ int mainwindows_reserve_lines(int top, int bottom) MAIN_WINDOW_REC *window; int ret; - ret = -1; + ret = -1; if (top != 0) { + GSList *list, *tmp; g_return_val_if_fail(top > 0 || screen_reserved_top > top, -1); ret = screen_reserved_top; screen_reserved_top += top; - window = mainwindows_find_lower(-1); - if (window != NULL) { + list = mainwindows_get_line(mainwindows_find_lower(NULL)); + for (tmp = list; tmp != NULL; tmp = tmp->next) { + window = tmp->data; window->first_line += top; mainwindow_resize(window, 0, -top); } + g_slist_free(list); } if (bottom != 0) { + GSList *list, *tmp; g_return_val_if_fail(bottom > 0 || screen_reserved_bottom > bottom, -1); ret = screen_reserved_bottom; screen_reserved_bottom += bottom; - window = mainwindows_find_upper(term_height); - if (window != NULL) { + list = mainwindows_get_line(mainwindows_find_upper(NULL)); + for (tmp = list; tmp != NULL; tmp = tmp->next) { + window = tmp->data; window->last_line -= bottom; mainwindow_resize(window, 0, -bottom); } + g_slist_free(list); } return ret; @@ -528,47 +891,113 @@ int mainwindow_set_statusbar_lines(MAIN_WINDOW_REC *window, return ret; } -static void mainwindows_resize_two(MAIN_WINDOW_REC *grow_win, - MAIN_WINDOW_REC *shrink_win, int count) +static void mainwindows_resize_two(GSList *grow_list, + GSList *shrink_list, int count) { - irssi_set_dirty(); + GSList *tmp; + MAIN_WINDOW_REC *win; - mainwindow_resize(grow_win, 0, count); - mainwindow_resize(shrink_win, 0, -count); - grow_win->dirty = TRUE; - shrink_win->dirty = TRUE; + irssi_set_dirty(); + + for (tmp = shrink_list; tmp != NULL; tmp = tmp->next) { + win = tmp->data; + mainwindow_resize(win, 0, -count); + win->dirty = TRUE; + } + for (tmp = grow_list; tmp != NULL; tmp = tmp->next) { + win = tmp->data; + mainwindow_resize(win, 0, count); + win->dirty = TRUE; + } } static int try_shrink_lower(MAIN_WINDOW_REC *window, int count) { MAIN_WINDOW_REC *shrink_win; - shrink_win = mainwindows_find_lower(window->last_line); - if (shrink_win != NULL && - MAIN_WINDOW_TEXT_HEIGHT(shrink_win)-count >= WINDOW_MIN_SIZE) { - window->last_line += count; - shrink_win->first_line += count; - mainwindows_resize_two(window, shrink_win, count); - return TRUE; + g_return_val_if_fail(count >= 0, FALSE); + + shrink_win = mainwindows_find_lower(window); + if (shrink_win != NULL) { + int ok; + GSList *shrink_list, *tmp; + MAIN_WINDOW_REC *win; + + ok = TRUE; + shrink_list = mainwindows_get_line(shrink_win); + + for (tmp = shrink_list; tmp != NULL; tmp = tmp->next) { + win = tmp->data; + if (MAIN_WINDOW_TEXT_HEIGHT(win)-count < WINDOW_MIN_SIZE) { + ok = FALSE; + break; + } + } + if (ok) { + GSList *grow_list; + grow_list = mainwindows_get_line(window); + + for (tmp = shrink_list; tmp != NULL; tmp = tmp->next) { + win = tmp->data; + win->first_line += count; + } + for (tmp = grow_list; tmp != NULL; tmp = tmp->next) { + win = tmp->data; + win->last_line += count; + } + + mainwindows_resize_two(grow_list, shrink_list, count); + g_slist_free(grow_list); + } + + g_slist_free(shrink_list); + return ok; } - return FALSE; + return FALSE; } static int try_shrink_upper(MAIN_WINDOW_REC *window, int count) { MAIN_WINDOW_REC *shrink_win; - shrink_win = mainwindows_find_upper(window->first_line); - if (shrink_win != NULL && - MAIN_WINDOW_TEXT_HEIGHT(shrink_win)-count >= WINDOW_MIN_SIZE) { - window->first_line -= count; - shrink_win->last_line -= count; - mainwindows_resize_two(window, shrink_win, count); - return TRUE; + g_return_val_if_fail(count >= 0, FALSE); + + shrink_win = mainwindows_find_upper(window); + if (shrink_win != NULL) { + int ok; + GSList *shrink_list, *tmp; + MAIN_WINDOW_REC *win; + + ok = TRUE; + shrink_list = mainwindows_get_line(shrink_win); + + for (tmp = shrink_list; tmp != NULL; tmp = tmp->next) { + win = tmp->data; + if (MAIN_WINDOW_TEXT_HEIGHT(win)-count < WINDOW_MIN_SIZE) { + ok = FALSE; + break; + } + } + if (ok) { + GSList *grow_list; + grow_list = mainwindows_get_line(window); + for (tmp = grow_list; tmp != NULL; tmp = tmp->next) { + win = tmp->data; + win->first_line -= count; + } + for (tmp = shrink_list; tmp != NULL; tmp = tmp->next) { + win = tmp->data; + win->last_line -= count; + } + mainwindows_resize_two(grow_list, shrink_list, count); + g_slist_free(grow_list); + } + g_slist_free(shrink_list); + return ok; } - return FALSE; + return FALSE; } static int mainwindow_grow(MAIN_WINDOW_REC *window, int count, @@ -588,32 +1017,58 @@ static int try_grow_lower(MAIN_WINDOW_REC *window, int count) { MAIN_WINDOW_REC *grow_win; - grow_win = mainwindows_find_lower(window->last_line); + grow_win = mainwindows_find_lower(window); if (grow_win != NULL) { - window->last_line -= count; - grow_win->first_line -= count; - mainwindows_resize_two(grow_win, window, count); + MAIN_WINDOW_REC *win; + GSList *grow_list, *shrink_list, *tmp; + grow_list = mainwindows_get_line(grow_win); + shrink_list = mainwindows_get_line(window); + for (tmp = grow_list; tmp != NULL; tmp = tmp->next) { + win = tmp->data; + win->first_line -= count; + } + for (tmp = shrink_list; tmp != NULL; tmp = tmp->next) { + win = tmp->data; + win->last_line -= count; + } + mainwindows_resize_two(grow_list, shrink_list, count); + g_slist_free(shrink_list); + g_slist_free(grow_list); } - return grow_win != NULL; + return grow_win != NULL; } static int try_grow_upper(MAIN_WINDOW_REC *window, int count) { MAIN_WINDOW_REC *grow_win; - grow_win = mainwindows_find_upper(window->first_line); + grow_win = mainwindows_find_upper(window); if (grow_win != NULL) { - window->first_line += count; - grow_win->last_line += count; - mainwindows_resize_two(grow_win, window, count); + MAIN_WINDOW_REC *win; + GSList *grow_list, *shrink_list, *tmp; + grow_list = mainwindows_get_line(grow_win); + shrink_list = mainwindows_get_line(window); + for (tmp = grow_list; tmp != NULL; tmp = tmp->next) { + win = tmp->data; + win->last_line += count; + } + for (tmp = shrink_list; tmp != NULL; tmp = tmp->next) { + win = tmp->data; + win->first_line += count; + } + mainwindows_resize_two(grow_list, shrink_list, count); + g_slist_free(shrink_list); + g_slist_free(grow_list); } - return grow_win != NULL; + return grow_win != NULL; } static int mainwindow_shrink(MAIN_WINDOW_REC *window, int count, int resize_lower) { + g_return_val_if_fail(count >= 0, FALSE); + if (MAIN_WINDOW_TEXT_HEIGHT(window)-count < WINDOW_MIN_SIZE) return FALSE; @@ -627,6 +1082,115 @@ static int mainwindow_shrink(MAIN_WINDOW_REC *window, int count, int resize_lowe return TRUE; } +static void mainwindows_rresize_two(MAIN_WINDOW_REC *grow_win, + MAIN_WINDOW_REC *shrink_win, int count) +{ + irssi_set_dirty(); + + mainwindow_resize(grow_win, count, 0); + mainwindow_resize(shrink_win, -count, 0); + grow_win->dirty = TRUE; + shrink_win->dirty = TRUE; +} + +static int try_rshrink_right(MAIN_WINDOW_REC *window, int count) +{ + MAIN_WINDOW_REC *shrink_win; + + g_return_val_if_fail(count >= 0, FALSE); + + shrink_win = mainwindows_find_right(window, FALSE); + if (shrink_win != NULL) { + if (MAIN_WINDOW_TEXT_WIDTH(shrink_win)-count < NEW_WINDOW_WIDTH) { + return FALSE; + } + + shrink_win->first_column += count; + window->last_column += count; + + mainwindows_rresize_two(window, shrink_win, count); + return TRUE; + } + + return FALSE; +} + +static int try_rshrink_left(MAIN_WINDOW_REC *window, int count) +{ + MAIN_WINDOW_REC *shrink_win; + + g_return_val_if_fail(count >= 0, FALSE); + + shrink_win = mainwindows_find_left(window, FALSE); + if (shrink_win != NULL) { + if (MAIN_WINDOW_TEXT_WIDTH(shrink_win)-count < NEW_WINDOW_WIDTH) { + return FALSE; + } + window->first_column -= count; + shrink_win->last_column -= count; + + mainwindows_rresize_two(window, shrink_win, count); + return TRUE; + } + + return FALSE; +} + +static int mainwindow_rgrow(MAIN_WINDOW_REC *window, int count) +{ + if (!try_rshrink_right(window, count)) { + if (!try_rshrink_left(window, count)) + return FALSE; + } + + return TRUE; +} + +static int try_rgrow_right(MAIN_WINDOW_REC *window, int count) +{ + MAIN_WINDOW_REC *grow_win; + + grow_win = mainwindows_find_right(window, FALSE); + if (grow_win != NULL) { + grow_win->first_column -= count; + window->last_column -= count; + mainwindows_rresize_two(grow_win, window, count); + return TRUE; + } + + return FALSE; +} + +static int try_rgrow_left(MAIN_WINDOW_REC *window, int count) +{ + MAIN_WINDOW_REC *grow_win; + + grow_win = mainwindows_find_left(window, FALSE); + if (grow_win != NULL) { + grow_win->last_column += count; + window->first_column += count; + mainwindows_rresize_two(grow_win, window, count); + return TRUE; + } + + return FALSE; +} + +static int mainwindow_rshrink(MAIN_WINDOW_REC *window, int count) +{ + g_return_val_if_fail(count >= 0, FALSE); + + if (MAIN_WINDOW_TEXT_WIDTH(window)-count < NEW_WINDOW_WIDTH) + return FALSE; + + if (!try_rgrow_right(window, count)) { + if (!try_rgrow_left(window, count)) + return FALSE; + } + + return TRUE; +} + /* Change the window height - the height includes the lines needed for statusbars. If resize_lower is TRUE, the lower window is first tried to be resized instead of upper window. */ @@ -639,6 +1203,15 @@ void mainwindow_set_size(MAIN_WINDOW_REC *window, int height, int resize_lower) mainwindow_grow(window, height, resize_lower); } +void mainwindow_set_rsize(MAIN_WINDOW_REC *window, int width) +{ + width -= window->width; + if (width < 0) + mainwindow_rshrink(window, -width); + else + mainwindow_rgrow(window, width); +} + void mainwindows_redraw_dirty(void) { GSList *tmp; @@ -653,6 +1226,23 @@ void mainwindows_redraw_dirty(void) if (rec->dirty) { rec->dirty = FALSE; gui_window_redraw(rec->active); + } else if (WINDOW_GUI(rec->active)->view->dirty) { + gui_window_redraw(rec->active); + } + } +} + +static void mainwindow_grow_int(int count) +{ + if (count == 0) { + return; + } else if (count < 0) { + if (!mainwindow_shrink(WINDOW_MAIN(active_win), -count, FALSE)) { + printformat_window(active_win, MSGLEVEL_CLIENTNOTICE, TXT_WINDOW_TOO_SMALL); + } + } else { + if (!mainwindow_grow(WINDOW_MAIN(active_win), count, FALSE)) { + printformat_window(active_win, MSGLEVEL_CLIENTNOTICE, TXT_WINDOW_TOO_SMALL); } } } @@ -660,16 +1250,11 @@ void mainwindows_redraw_dirty(void) /* SYNTAX: WINDOW GROW [<lines>] */ static void cmd_window_grow(const char *data) { - MAIN_WINDOW_REC *window; int count; count = *data == '\0' ? 1 : atoi(data); - window = WINDOW_MAIN(active_win); - if (!mainwindow_grow(window, count, FALSE)) { - printformat_window(active_win, MSGLEVEL_CLIENTNOTICE, - TXT_WINDOW_TOO_SMALL); - } + mainwindow_grow_int(count); } /* SYNTAX: WINDOW SHRINK [<lines>] */ @@ -678,16 +1263,14 @@ static void cmd_window_shrink(const char *data) int count; count = *data == '\0' ? 1 : atoi(data); - if (!mainwindow_shrink(WINDOW_MAIN(active_win), count, FALSE)) { - printformat_window(active_win, MSGLEVEL_CLIENTNOTICE, - TXT_WINDOW_TOO_SMALL); - } + if (count < -INT_MAX) count = -INT_MAX; + + mainwindow_grow_int(-count); } /* SYNTAX: WINDOW SIZE <lines> */ static void cmd_window_size(const char *data) { - char sizestr[MAX_INT_STRLEN]; int size; if (!is_numeric(data, 0)) return; @@ -695,47 +1278,60 @@ static void cmd_window_size(const char *data) size -= WINDOW_MAIN(active_win)->height - WINDOW_MAIN(active_win)->statusbar_lines; - if (size == 0) return; + if (size < -INT_MAX) size = -INT_MAX; - ltoa(sizestr, size < 0 ? -size : size); - if (size < 0) - cmd_window_shrink(sizestr); - else - cmd_window_grow(sizestr); + mainwindow_grow_int(size); } /* SYNTAX: WINDOW BALANCE */ static void cmd_window_balance(void) { - GSList *sorted, *tmp; + GSList *sorted, *stmp, *line, *ltmp; int avail_size, unit_size, bigger_units; int windows, last_line, old_size; + MAIN_WINDOW_REC *win; windows = g_slist_length(mainwindows); if (windows == 1) return; + sorted = NULL; + windows = 0; + for (win = mainwindows_find_lower(NULL); + win != NULL; + win = mainwindows_find_lower(win)) { + windows++; + sorted = g_slist_append(sorted, win); + } + avail_size = term_height - screen_reserved_top-screen_reserved_bottom; unit_size = avail_size/windows; bigger_units = avail_size%windows; - sorted = mainwindows_get_sorted(FALSE); - last_line = screen_reserved_top; - for (tmp = sorted; tmp != NULL; tmp = tmp->next) { - MAIN_WINDOW_REC *rec = tmp->data; + last_line = screen_reserved_top; + for (stmp = sorted; stmp != NULL; stmp = stmp->next) { + win = stmp->data; + line = mainwindows_get_line(win); - old_size = rec->height; - rec->first_line = last_line; - rec->last_line = rec->first_line + unit_size-1; + for (ltmp = line; ltmp != NULL; ltmp = ltmp->next) { + MAIN_WINDOW_REC *rec = ltmp->data; + old_size = rec->height; + rec->first_line = last_line; + rec->last_line = rec->first_line + unit_size-1; - if (bigger_units > 0) { - rec->last_line++; - bigger_units--; - } + if (bigger_units > 0) { + rec->last_line++; + } - rec->height = rec->last_line-rec->first_line+1; - last_line = rec->last_line+1; + rec->height = rec->last_line-rec->first_line+1; + + mainwindow_resize(rec, 0, rec->height-old_size); + } + if (line != NULL && bigger_units > 0) { + bigger_units--; + } + last_line = win->last_line+1; - mainwindow_resize(rec, 0, rec->height-old_size); + g_slist_free(line); } g_slist_free(sorted); @@ -769,29 +1365,30 @@ static void cmd_window_hide(const char *data) return; if (WINDOW_MAIN(window)->sticky_windows) { - printformat_window(active_win, MSGLEVEL_CLIENTERROR, - TXT_CANT_HIDE_STICKY_WINDOWS); - return; + if (!settings_get_bool("autounstick_windows")) { + printformat_window(active_win, MSGLEVEL_CLIENTERROR, + TXT_CANT_HIDE_STICKY_WINDOWS); + return; + } } mainwindow_destroy(WINDOW_MAIN(window)); if (active_mainwin == NULL) { active_mainwin = WINDOW_MAIN(active_win); - window_set_active(active_mainwin->active); + window_set_active(active_mainwin->active); } } -/* SYNTAX: WINDOW SHOW <number>|<name> */ -static void cmd_window_show(const char *data) +static void _cmd_window_show_opt(const char *data, int right) { - MAIN_WINDOW_REC *parent; + MAIN_WINDOW_REC *parent; WINDOW_REC *window; if (*data == '\0') cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS); if (is_numeric(data, '\0')) { - window = window_find_refnum(atoi(data)); + window = window_find_refnum(atoi(data)); if (window == NULL) { printformat_window(active_win, MSGLEVEL_CLIENTERROR, TXT_REFNUM_NOT_FOUND, data); @@ -804,30 +1401,161 @@ static void cmd_window_show(const char *data) return; if (WINDOW_GUI(window)->sticky) { - printformat_window(active_win, MSGLEVEL_CLIENTERROR, - TXT_CANT_SHOW_STICKY_WINDOWS); - return; + if (!settings_get_bool("autounstick_windows")) { + printformat_window(active_win, MSGLEVEL_CLIENTERROR, + TXT_CANT_SHOW_STICKY_WINDOWS); + return; + } + } + + parent = mainwindow_create(right); + if (parent == NULL) { + printformat_window(active_win, MSGLEVEL_CLIENTERROR, TXT_WINDOW_TOO_SMALL); + return; } - parent = mainwindow_create(); parent->active = window; - gui_window_reparent(window, parent); + gui_window_reparent(window, parent); if (settings_get_bool("autostick_split_windows")) - gui_window_set_sticky(window); + gui_window_set_sticky(window); active_mainwin = NULL; window_set_active(window); } +/* SYNTAX: WINDOW SHOW <number>|<name> */ +static void cmd_window_show(const char *data) +{ + _cmd_window_show_opt(data, FALSE); +} + +/* SYNTAX: WINDOW RSHOW <number>|<name> */ +static void cmd_window_rshow(const char *data) +{ + _cmd_window_show_opt(data, TRUE); +} + +static void window_rgrow_int(int count) +{ + if (count == 0) { + return; + } else if (count < 0) { + if (!mainwindow_rshrink(WINDOW_MAIN(active_win), -count)) { + printformat_window(active_win, MSGLEVEL_CLIENTNOTICE, TXT_WINDOW_TOO_SMALL); + } + } else { + if (!mainwindow_rgrow(WINDOW_MAIN(active_win), count)) { + printformat_window(active_win, MSGLEVEL_CLIENTNOTICE, TXT_WINDOW_TOO_SMALL); + } + } +} + +/* SYNTAX: WINDOW RGROW [<columns>] */ +static void cmd_window_rgrow(const char *data) +{ + int count; + + count = *data == '\0' ? 1 : atoi(data); + + window_rgrow_int(count); +} + +/* SYNTAX: WINDOW RSHRINK [<lines>] */ +static void cmd_window_rshrink(const char *data) +{ + int count; + + count = *data == '\0' ? 1 : atoi(data); + if (count < -INT_MAX) count = -INT_MAX; + + window_rgrow_int(-count); +} + +/* SYNTAX: WINDOW RSIZE <columns> */ +static void cmd_window_rsize(const char *data) +{ + int rsize; + + if (!is_numeric(data, 0)) return; + rsize = atoi(data); + + rsize -= MAIN_WINDOW_TEXT_WIDTH(WINDOW_MAIN(active_win)); + + window_rgrow_int(rsize); +} + +/* SYNTAX: WINDOW RBALANCE */ +static void cmd_window_rbalance(void) +{ + GSList *line, *ltmp; + int avail_width, unit_width, bigger_units; + int windows, last_column, old_width; + MAIN_WINDOW_REC *win; + + line = mainwindows_get_line(WINDOW_MAIN(active_win)); + windows = g_slist_length(line); + if (windows == 1) { + g_slist_free(line); + return; + } + + avail_width = term_width - screen_reserved_left-screen_reserved_right - windows + 1; + unit_width = avail_width/windows; + bigger_units = avail_width%windows; + + last_column = screen_reserved_left; + for (ltmp = line; ltmp != NULL; ltmp = ltmp->next) { + win = ltmp->data; + old_width = win->width; + win->first_column = last_column; + win->last_column = win->first_column + unit_width-1; + + if (bigger_units > 0) { + win->last_column++; + bigger_units--; + } + + mainwindow_resize(win, win->last_column - win->first_column + 1 - old_width, 0); + last_column = win->last_column+2; + } + g_slist_free(line); + + mainwindows_redraw(); +} + /* SYNTAX: WINDOW UP */ static void cmd_window_up(void) { MAIN_WINDOW_REC *rec; - rec = mainwindows_find_upper(active_mainwin->first_line); + rec = mainwindows_find_left_upper(active_mainwin); if (rec == NULL) - rec = mainwindows_find_upper(term_height); + rec = mainwindows_find_left_upper(NULL); + if (rec != NULL) + window_set_active(rec->active); +} + +/* SYNTAX: WINDOW DUP */ +static void cmd_window_dup(void) +{ + MAIN_WINDOW_REC *rec; + + rec = mainwindows_find_upper(active_mainwin); + if (rec == NULL) + rec = mainwindows_find_upper(NULL); + if (rec != NULL) + window_set_active(rec->active); +} + +/* SYNTAX: WINDOW DLEFT */ +static void cmd_window_dleft(void) +{ + MAIN_WINDOW_REC *rec; + + rec = mainwindows_find_left(active_mainwin, FALSE); + if (rec == NULL) + rec = mainwindows_find_left(active_mainwin, TRUE); if (rec != NULL) window_set_active(rec->active); } @@ -837,9 +1565,33 @@ static void cmd_window_down(void) { MAIN_WINDOW_REC *rec; - rec = mainwindows_find_lower(active_mainwin->last_line); + rec = mainwindows_find_lower_right(active_mainwin); + if (rec == NULL) + rec = mainwindows_find_lower_right(NULL); + if (rec != NULL) + window_set_active(rec->active); +} + +/* SYNTAX: WINDOW DDOWN */ +static void cmd_window_ddown(void) +{ + MAIN_WINDOW_REC *rec; + + rec = mainwindows_find_lower(active_mainwin); if (rec == NULL) - rec = mainwindows_find_lower(-1); + rec = mainwindows_find_lower(NULL); + if (rec != NULL) + window_set_active(rec->active); +} + +/* SYNTAX: WINDOW DRIGHT */ +static void cmd_window_dright(void) +{ + MAIN_WINDOW_REC *rec; + + rec = mainwindows_find_right(active_mainwin, FALSE); + if (rec == NULL) + rec = mainwindows_find_right(active_mainwin, TRUE); if (rec != NULL) window_set_active(rec->active); } @@ -997,13 +1749,37 @@ static void cmd_window_move_right(void) window_set_refnum(active_win, refnum); } +/* SYNTAX: WINDOW MOVE DLEFT */ +static void cmd_window_move_dleft(void) +{ + MAIN_WINDOW_REC *rec; + + rec = mainwindows_find_left(active_mainwin, FALSE); + if (rec == NULL) + rec = mainwindows_find_left(active_mainwin, TRUE); + if (rec != NULL) + window_reparent(active_win, rec); +} + +/* SYNTAX: WINDOW MOVE DRIGHT */ +static void cmd_window_move_dright(void) +{ + MAIN_WINDOW_REC *rec; + + rec = mainwindows_find_right(active_mainwin, FALSE); + if (rec == NULL) + rec = mainwindows_find_right(active_mainwin, TRUE); + if (rec != NULL) + window_reparent(active_win, rec); +} + /* SYNTAX: WINDOW MOVE UP */ static void cmd_window_move_up(void) { MAIN_WINDOW_REC *rec; - rec = mainwindows_find_upper(active_mainwin->first_line); - if (rec != NULL) + rec = mainwindows_find_upper_left(active_mainwin); + if (rec != NULL) window_reparent(active_win, rec); } @@ -1012,7 +1788,7 @@ static void cmd_window_move_down(void) { MAIN_WINDOW_REC *rec; - rec = mainwindows_find_lower(active_mainwin->last_line); + rec = mainwindows_find_lower_right(active_mainwin); if (rec != NULL) window_reparent(active_win, rec); } @@ -1058,12 +1834,14 @@ static void sig_window_print_info(WINDOW_REC *win) void mainwindows_init(void) { - old_screen_width = term_width; - old_screen_height = term_height; + screen_width = term_width; + screen_height = term_height; mainwindows = NULL; active_mainwin = NULL; + clrtoeol_info = g_new0(MAIN_WINDOW_BORDER_REC, 1); screen_reserved_top = screen_reserved_bottom = 0; + screen_reserved_left = screen_reserved_right = 0; command_bind("window grow", NULL, (SIGNAL_FUNC) cmd_window_grow); command_bind("window shrink", NULL, (SIGNAL_FUNC) cmd_window_shrink); @@ -1075,18 +1853,30 @@ void mainwindows_init(void) command_bind("window down", NULL, (SIGNAL_FUNC) cmd_window_down); command_bind("window left", NULL, (SIGNAL_FUNC) cmd_window_left); command_bind("window right", NULL, (SIGNAL_FUNC) cmd_window_right); + command_bind("window dup", NULL, (SIGNAL_FUNC) cmd_window_dup); + command_bind("window ddown", NULL, (SIGNAL_FUNC) cmd_window_ddown); + command_bind("window dleft", NULL, (SIGNAL_FUNC) cmd_window_dleft); + command_bind("window dright", NULL, (SIGNAL_FUNC) cmd_window_dright); command_bind("window stick", NULL, (SIGNAL_FUNC) cmd_window_stick); command_bind("window move left", NULL, (SIGNAL_FUNC) cmd_window_move_left); command_bind("window move right", NULL, (SIGNAL_FUNC) cmd_window_move_right); command_bind("window move up", NULL, (SIGNAL_FUNC) cmd_window_move_up); command_bind("window move down", NULL, (SIGNAL_FUNC) cmd_window_move_down); - signal_add("window print info", (SIGNAL_FUNC) sig_window_print_info); + command_bind("window move dleft", NULL, (SIGNAL_FUNC) cmd_window_move_dleft); + command_bind("window move dright", NULL, (SIGNAL_FUNC) cmd_window_move_dright); + command_bind("window rgrow", NULL, (SIGNAL_FUNC) cmd_window_rgrow); + command_bind("window rshrink", NULL, (SIGNAL_FUNC) cmd_window_rshrink); + command_bind("window rsize", NULL, (SIGNAL_FUNC) cmd_window_rsize); + command_bind("window rbalance", NULL, (SIGNAL_FUNC) cmd_window_rbalance); + command_bind("window rshow", NULL, (SIGNAL_FUNC) cmd_window_rshow); + signal_add("window print info", (SIGNAL_FUNC) sig_window_print_info); } void mainwindows_deinit(void) { while (mainwindows != NULL) mainwindow_destroy(mainwindows->data); + g_free(clrtoeol_info); command_unbind("window grow", (SIGNAL_FUNC) cmd_window_grow); command_unbind("window shrink", (SIGNAL_FUNC) cmd_window_shrink); @@ -1098,10 +1888,21 @@ void mainwindows_deinit(void) command_unbind("window down", (SIGNAL_FUNC) cmd_window_down); command_unbind("window left", (SIGNAL_FUNC) cmd_window_left); command_unbind("window right", (SIGNAL_FUNC) cmd_window_right); + command_unbind("window dup", (SIGNAL_FUNC) cmd_window_dup); + command_unbind("window ddown", (SIGNAL_FUNC) cmd_window_ddown); + command_unbind("window dleft", (SIGNAL_FUNC) cmd_window_dleft); + command_unbind("window dright", (SIGNAL_FUNC) cmd_window_dright); command_unbind("window stick", (SIGNAL_FUNC) cmd_window_stick); command_unbind("window move left", (SIGNAL_FUNC) cmd_window_move_left); command_unbind("window move right", (SIGNAL_FUNC) cmd_window_move_right); command_unbind("window move up", (SIGNAL_FUNC) cmd_window_move_up); command_unbind("window move down", (SIGNAL_FUNC) cmd_window_move_down); - signal_remove("window print info", (SIGNAL_FUNC) sig_window_print_info); + command_unbind("window move dleft", (SIGNAL_FUNC) cmd_window_move_dleft); + command_unbind("window move dright", (SIGNAL_FUNC) cmd_window_move_dright); + command_unbind("window rgrow", (SIGNAL_FUNC) cmd_window_rgrow); + command_unbind("window rshrink", (SIGNAL_FUNC) cmd_window_rshrink); + command_unbind("window rsize", (SIGNAL_FUNC) cmd_window_rsize); + command_unbind("window rbalance", (SIGNAL_FUNC) cmd_window_rbalance); + command_unbind("window rshow", (SIGNAL_FUNC) cmd_window_rshow); + signal_remove("window print info", (SIGNAL_FUNC) sig_window_print_info); } diff --git a/src/fe-text/mainwindows.h b/src/fe-text/mainwindows.h index 1bca333d..414275bf 100644 --- a/src/fe-text/mainwindows.h +++ b/src/fe-text/mainwindows.h @@ -5,36 +5,48 @@ #include "term.h" #define WINDOW_MIN_SIZE 2 +#define NEW_WINDOW_WIDTH 10 #define MAIN_WINDOW_TEXT_HEIGHT(window) \ ((window)->height-(window)->statusbar_lines) +#define MAIN_WINDOW_TEXT_WIDTH(window) \ + ((window)->width-(window)->statusbar_columns) + typedef struct { WINDOW_REC *active; TERM_WINDOW *screen_win; - int sticky_windows; /* number of sticky windows */ + int sticky_windows; /* number of sticky windows */ int first_line, last_line; /* first/last line used by this window (0..x) (includes statusbars) */ + int first_column, last_column; /* first/last column used by this window (0..x) (includes statusbars) */ int width, height; /* width/height of the window (includes statusbars) */ GSList *statusbars; - int statusbar_lines_top; - int statusbar_lines_bottom; + int statusbar_lines_top, statusbar_lines_bottom; int statusbar_lines; /* top+bottom */ + int statusbar_columns_left, statusbar_columns_right; + int statusbar_columns; /* left+right */ unsigned int dirty:1; /* This window needs a redraw */ unsigned int size_dirty:1; /* We'll need to resize the window, but haven't got around doing it just yet. */ } MAIN_WINDOW_REC; +typedef struct { + char *color; + TERM_WINDOW *window; +} MAIN_WINDOW_BORDER_REC; + extern GSList *mainwindows; extern MAIN_WINDOW_REC *active_mainwin; +extern MAIN_WINDOW_BORDER_REC *clrtoeol_info; extern int screen_reserved_top, screen_reserved_bottom; void mainwindows_init(void); void mainwindows_deinit(void); -MAIN_WINDOW_REC *mainwindow_create(void); +MAIN_WINDOW_REC *mainwindow_create(int); void mainwindow_destroy(MAIN_WINDOW_REC *window); void mainwindows_redraw(void); @@ -45,6 +57,7 @@ void mainwindows_recreate(void); to be resized instead of upper window. */ void mainwindow_set_size(MAIN_WINDOW_REC *window, int height, int resize_lower); +void mainwindow_set_rsize(MAIN_WINDOW_REC *window, int width); void mainwindows_resize(int width, int height); void mainwindow_change_active(MAIN_WINDOW_REC *mainwin, @@ -56,5 +69,6 @@ int mainwindow_set_statusbar_lines(MAIN_WINDOW_REC *window, void mainwindows_redraw_dirty(void); GSList *mainwindows_get_sorted(int reverse); +GSList *mainwindows_get_line(MAIN_WINDOW_REC *rec); #endif diff --git a/src/fe-text/module-formats.c b/src/fe-text/module-formats.c index b8a26192..b234f46c 100644 --- a/src/fe-text/module-formats.c +++ b/src/fe-text/module-formats.c @@ -41,8 +41,8 @@ FORMAT_REC gui_text_formats[] = { "refnum_not_found", "Window number $0 not found", 1, { 0 } }, { "window_too_small", "Not enough room to resize this window", 0 }, { "cant_hide_last", "You can't hide the last window", 0 }, - { "cant_hide_sticky_windows", "You can't hide sticky windows (use /WINDOW STICK OFF)", 0 }, - { "cant_show_sticky_windows", "You can't show sticky windows (use /WINDOW STICK OFF)", 0 }, + { "cant_hide_sticky_windows", "You can't hide sticky windows (use /SET autounstick_windows ON)", 0 }, + { "cant_show_sticky_windows", "You can't show sticky windows (use /SET autounstick_windows ON)", 0 }, { "window_not_sticky", "Window is not sticky", 0 }, { "window_set_sticky", "Window set sticky", 0 }, { "window_unset_sticky", "Window is not sticky anymore", 0 }, @@ -87,7 +87,7 @@ FORMAT_REC gui_text_formats[] = "|_ _|_ _ _____(_)%:" " | || '_(_-<_-< |%:" "|___|_| /__/__/_|%:" - "Irssi v$J - http://www.irssi.org", 0 }, + "Irssi v$J - https://irssi.org", 0 }, { "welcome_firsttime", "- - - - - - - - - - - - - - - - - - - - - - - - - - - -\n" "Hi there! If this is your first time using Irssi, you%:" diff --git a/src/fe-text/statusbar-items.c b/src/fe-text/statusbar-items.c index c7d6bcfb..5740a40b 100644 --- a/src/fe-text/statusbar-items.c +++ b/src/fe-text/statusbar-items.c @@ -418,7 +418,7 @@ static void item_input(SBAR_ITEM_REC *item, int get_size_only) rec = g_hash_table_lookup(input_entries, item->bar->config->name); if (rec == NULL) { - rec = gui_entry_create(item->xpos, item->bar->real_ypos, + rec = gui_entry_create(ITEM_WINDOW_REAL_XPOS(item), item->bar->real_ypos, item->size, term_type == TERM_TYPE_UTF8); gui_entry_set_active(rec); g_hash_table_insert(input_entries, @@ -426,12 +426,21 @@ static void item_input(SBAR_ITEM_REC *item, int get_size_only) } if (get_size_only) { - item->min_size = 2+term_width/10; - item->max_size = term_width; - return; + int max_width; + WINDOW_REC *window; + + window = item->bar->parent_window != NULL + ? item->bar->parent_window->active + : NULL; + + max_width = window != NULL ? window->width : term_width; + + item->min_size = 2+max_width/10; + item->max_size = max_width; + return; } - gui_entry_move(rec, item->xpos, item->bar->real_ypos, + gui_entry_move(rec, ITEM_WINDOW_REAL_XPOS(item), item->bar->real_ypos, item->size); gui_entry_redraw(rec); /* FIXME: this is only necessary with ^L.. */ } diff --git a/src/fe-text/statusbar.c b/src/fe-text/statusbar.c index f0dff828..40837eea 100644 --- a/src/fe-text/statusbar.c +++ b/src/fe-text/statusbar.c @@ -242,17 +242,24 @@ static void statusbar_resize_items(STATUSBAR_REC *bar, int max_width) static void statusbar_calc_item_positions(STATUSBAR_REC *bar) { - WINDOW_REC *old_active_win; + WINDOW_REC *window; + WINDOW_REC *old_active_win; GSList *tmp, *right_items; int xpos, rxpos; + int max_width; old_active_win = active_win; - if (bar->parent_window != NULL) + if (bar->parent_window != NULL) active_win = bar->parent_window->active; - statusbar_resize_items(bar, term_width); + window = bar->parent_window != NULL + ? bar->parent_window->active + : NULL; + + max_width = window != NULL ? window->width : term_width; + statusbar_resize_items(bar, max_width); - /* left-aligned items */ + /* left-aligned items */ xpos = 0; for (tmp = bar->items; tmp != NULL; tmp = tmp->next) { SBAR_ITEM_REC *rec = tmp->data; @@ -260,11 +267,11 @@ static void statusbar_calc_item_positions(STATUSBAR_REC *bar) if (!rec->config->right_alignment && (rec->size > 0 || rec->current_size > 0)) { if (SBAR_ITEM_REDRAW_NEEDED(bar, rec, xpos)) { - /* redraw the item */ + /* redraw the item */ rec->dirty = TRUE; if (bar->dirty_xpos == -1 || xpos < bar->dirty_xpos) { - irssi_set_dirty(); + irssi_set_dirty(); bar->dirty = TRUE; bar->dirty_xpos = xpos; } @@ -277,12 +284,12 @@ static void statusbar_calc_item_positions(STATUSBAR_REC *bar) /* right-aligned items - first copy them to a new list backwards, easier to draw them in right order */ - right_items = NULL; + right_items = NULL; for (tmp = bar->items; tmp != NULL; tmp = tmp->next) { SBAR_ITEM_REC *rec = tmp->data; if (rec->config->right_alignment) { - if (rec->size > 0) + if (rec->size > 0) right_items = g_slist_prepend(right_items, rec); else if (rec->current_size > 0 && (bar->dirty_xpos == -1 || @@ -291,12 +298,12 @@ static void statusbar_calc_item_positions(STATUSBAR_REC *bar) to begin from the item's old xpos */ irssi_set_dirty(); bar->dirty = TRUE; - bar->dirty_xpos = rec->xpos; + bar->dirty_xpos = rec->xpos; } } } - rxpos = term_width; + rxpos = max_width; for (tmp = right_items; tmp != NULL; tmp = tmp->next) { SBAR_ITEM_REC *rec = tmp->data; @@ -312,7 +319,7 @@ static void statusbar_calc_item_positions(STATUSBAR_REC *bar) rec->xpos = rxpos; } } - g_slist_free(right_items); + g_slist_free(right_items); active_win = old_active_win; } @@ -451,8 +458,13 @@ static void mainwindow_recalc_ypos(MAIN_WINDOW_REC *window, int placement) static void sig_mainwindow_resized(MAIN_WINDOW_REC *window) { - mainwindow_recalc_ypos(window, STATUSBAR_TOP); - mainwindow_recalc_ypos(window, STATUSBAR_BOTTOM); + GSList *tmp; + mainwindow_recalc_ypos(window, STATUSBAR_TOP); + mainwindow_recalc_ypos(window, STATUSBAR_BOTTOM); + for (tmp = window->statusbars; tmp != NULL; tmp = tmp->next) { + STATUSBAR_REC *bar = tmp->data; + statusbar_redraw(bar, TRUE); + } } STATUSBAR_REC *statusbar_create(STATUSBAR_GROUP_REC *group, @@ -728,7 +740,7 @@ void statusbar_item_default_handler(SBAR_ITEM_REC *item, int get_size_only, g_string_append_c(out, ' '); } - gui_printtext(item->xpos, item->bar->real_ypos, out->str); + gui_printtext(ITEM_WINDOW_REAL_XPOS(item), item->bar->real_ypos, out->str); g_string_free(out, TRUE); } g_free(tmpstr); @@ -960,20 +972,42 @@ void statusbar_item_destroy(SBAR_ITEM_REC *item) g_free(item); } +static MAIN_WINDOW_BORDER_REC *set_border_info(STATUSBAR_REC *bar) +{ + MAIN_WINDOW_BORDER_REC *orig_border, *new_border; + orig_border = clrtoeol_info; + new_border = g_new0(MAIN_WINDOW_BORDER_REC, 1); + new_border->window = bar->parent_window != NULL ? bar->parent_window->screen_win : NULL; + new_border->color = bar->color; + clrtoeol_info = new_border; + return orig_border; +} + +static void restore_border_info(MAIN_WINDOW_BORDER_REC *border_info) +{ + MAIN_WINDOW_BORDER_REC *old_border; + old_border = clrtoeol_info; + clrtoeol_info = border_info; + g_free(old_border); +} + static void statusbar_redraw_needed_items(STATUSBAR_REC *bar) { - WINDOW_REC *old_active_win; + WINDOW_REC *old_active_win; GSList *tmp; char *str; old_active_win = active_win; - if (bar->parent_window != NULL) + if (bar->parent_window != NULL) active_win = bar->parent_window->active; if (bar->dirty_xpos >= 0) { + MAIN_WINDOW_BORDER_REC *orig_border; + orig_border = set_border_info(bar); str = g_strconcat(bar->color, "%>", NULL); - gui_printtext(bar->dirty_xpos, bar->real_ypos, str); + gui_printtext(BAR_WINDOW_REAL_DIRTY_XPOS(bar), bar->real_ypos, str); g_free(str); + restore_border_info(orig_border); } for (tmp = bar->items; tmp != NULL; tmp = tmp->next) { @@ -982,13 +1016,13 @@ static void statusbar_redraw_needed_items(STATUSBAR_REC *bar) if (rec->dirty || (bar->dirty_xpos != -1 && rec->xpos >= bar->dirty_xpos)) { - rec->current_size = rec->size; + rec->current_size = rec->size; rec->func(rec, FALSE); rec->dirty = FALSE; } } - active_win = old_active_win; + active_win = old_active_win; } void statusbar_redraw_dirty(void) diff --git a/src/fe-text/statusbar.h b/src/fe-text/statusbar.h index 309294b0..b0048cc4 100644 --- a/src/fe-text/statusbar.h +++ b/src/fe-text/statusbar.h @@ -23,6 +23,12 @@ typedef struct SBAR_ITEM_REC SBAR_ITEM_REC; #define STATUSBAR_VISIBLE_ACTIVE 2 #define STATUSBAR_VISIBLE_INACTIVE 3 +#define ITEM_WINDOW_REAL_XPOS(item) ( ( (item)->bar->parent_window != NULL ? \ + (item)->bar->parent_window->first_column + (item)->bar->parent_window->statusbar_columns_left : 0 ) + (item)->xpos ) + +#define BAR_WINDOW_REAL_DIRTY_XPOS(bar) ( ( (bar)->parent_window != NULL ? \ + (bar)->parent_window->first_column + (bar)->parent_window->statusbar_columns_left : 0 ) + (bar)->dirty_xpos ) + typedef struct { char *name; GSList *config_bars; diff --git a/src/fe-text/term-terminfo.c b/src/fe-text/term-terminfo.c index 6645cfb0..68947f0c 100644 --- a/src/fe-text/term-terminfo.c +++ b/src/fe-text/term-terminfo.c @@ -23,6 +23,7 @@ #include "term.h" #include "terminfo-core.h" #include "fe-windows.h" +#include "gui-printtext.h" #include "utf8.h" #include <signal.h> @@ -284,10 +285,10 @@ void term_window_clear(TERM_WINDOW *window) { int y; - terminfo_set_normal(); - if (window->y == 0 && window->height == term_height) { - term_clear(); - } else { + terminfo_set_normal(); + if (window->y == 0 && window->height == term_height && window->width == term_width) { + term_clear(); + } else { for (y = 0; y < window->height; y++) { term_move(window, 0, y); term_clrtoeol(window); @@ -452,14 +453,14 @@ void term_set_color(TERM_WINDOW *window, int col) void term_move(TERM_WINDOW *window, int x, int y) { if (x >= 0 && y >= 0) { - vcmove = TRUE; - vcx = x+window->x; - vcy = y+window->y; - - if (vcx >= term_width) - vcx = term_width-1; - if (vcy >= term_height) - vcy = term_height-1; + vcmove = TRUE; + vcx = x+window->x; + vcy = y+window->y; + + if (vcx >= term_width) + vcx = term_width-1; + if (vcy >= term_height) + vcy = term_height-1; } } @@ -552,7 +553,7 @@ int term_addstr(TERM_WINDOW *window, const char *str) while (*ptr != '\0') { tmp = g_utf8_get_char_validated(ptr, -1); /* On utf8 error, treat as single byte and try to - continue interpretting rest of string as utf8 */ + continue interpreting rest of string as utf8 */ if (tmp == (gunichar)-1 || tmp == (gunichar)-2) { len++; ptr++; @@ -574,21 +575,52 @@ int term_addstr(TERM_WINDOW *window, const char *str) void term_clrtoeol(TERM_WINDOW *window) { - /* clrtoeol() doesn't necessarily understand colors */ - if (last_fg == -1 && last_bg == -1 && - (last_attrs & (ATTR_UNDERLINE|ATTR_REVERSE|ATTR_ITALIC)) == 0) { - if (!term_lines_empty[vcy]) { - if (vcmove) term_move_real(); - terminfo_clrtoeol(); - if (vcx == 0) term_lines_empty[vcy] = TRUE; - } - } else if (vcx < term_width) { - /* we'll need to fill the line ourself. */ + if (vcx < window->x) { + /* we just wrapped outside of the split, warp the cursor back into the window */ + vcx += window->x; + vcmove = TRUE; + } + if (window->x + window->width < term_width) { + /* we need to fill a vertical split */ if (vcmove) term_move_real(); - terminfo_repeat(' ', term_width-vcx); + terminfo_repeat(' ', window->x + window->width - vcx + 1); terminfo_move(vcx, vcy); - term_lines_empty[vcy] = FALSE; + term_lines_empty[vcy] = FALSE; + } else { + /* clrtoeol() doesn't necessarily understand colors */ + if (last_fg == -1 && last_bg == -1 && + (last_attrs & (ATTR_UNDERLINE|ATTR_REVERSE|ATTR_ITALIC)) == 0) { + if (!term_lines_empty[vcy]) { + if (vcmove) term_move_real(); + terminfo_clrtoeol(); + if (vcx == 0) term_lines_empty[vcy] = TRUE; + } + } else if (vcx < term_width) { + /* we'll need to fill the line ourself. */ + if (vcmove) term_move_real(); + terminfo_repeat(' ', term_width-vcx); + terminfo_move(vcx, vcy); + term_lines_empty[vcy] = FALSE; + } + } +} + +void term_window_clrtoeol(TERM_WINDOW* window, int ypos) +{ + if (ypos >= 0 && window->y + ypos != vcy) { + /* the line is already full */ + return; } + term_clrtoeol(window); + if (window->x + window->width < term_width) { + gui_printtext_window_border(window->x + window->width, window->y + ypos); + term_set_color(window, ATTR_RESET); + } +} + +void term_window_clrtoeol_abs(TERM_WINDOW* window, int ypos) +{ + term_window_clrtoeol(window, ypos - window->y); } void term_move_cursor(int x, int y) @@ -733,3 +765,31 @@ void term_gets(GArray *buffer, int *line_count) } } } + +static const char* term_env_warning = + "You seem to be running Irssi inside %2$s, but the TERM environment variable " + "is set to '%1$s', which can cause display glitches.\n" + "Consider changing TERM to '%2$s' or '%2$s-256color' instead."; + +void term_environment_check(void) +{ + const char *term, *sty, *tmux, *multiplexer; + + term = g_getenv("TERM"); + sty = g_getenv("STY"); + tmux = g_getenv("TMUX"); + + multiplexer = (sty && *sty) ? "screen" : + (tmux && *tmux) ? "tmux" : NULL; + + if (!multiplexer) { + return; + } + + if (term && (g_str_has_prefix(term, "screen") || + g_str_has_prefix(term, "tmux"))) { + return; + } + + g_warning(term_env_warning, term, multiplexer); +} diff --git a/src/fe-text/term.h b/src/fe-text/term.h index 0c7847f6..f25154c2 100644 --- a/src/fe-text/term.h +++ b/src/fe-text/term.h @@ -85,6 +85,8 @@ void term_addch(TERM_WINDOW *window, char chr); void term_add_unichar(TERM_WINDOW *window, unichar chr); int term_addstr(TERM_WINDOW *window, const char *str); void term_clrtoeol(TERM_WINDOW *window); +void term_window_clrtoeol(TERM_WINDOW* window, int ypos); +void term_window_clrtoeol_abs(TERM_WINDOW* window, int ypos_abs); void term_move_cursor(int x, int y); @@ -105,4 +107,6 @@ void term_gets(GArray *buffer, int *line_count); void term_common_init(void); void term_common_deinit(void); +void term_environment_check(void); + #endif diff --git a/src/fe-text/textbuffer-view.c b/src/fe-text/textbuffer-view.c index b54f1c8e..99625ecc 100644 --- a/src/fe-text/textbuffer-view.c +++ b/src/fe-text/textbuffer-view.c @@ -394,9 +394,9 @@ static void view_reset_cache(TEXT_BUFFER_VIEW_REC *view) static int view_line_draw(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line, int subline, int ypos, int max) { - INDENT_FUNC indent_func; + INDENT_FUNC indent_func; LINE_CACHE_REC *cache; - const unsigned char *text, *end, *text_newline; + const unsigned char *text, *end, *text_newline; unsigned char *tmp; unichar chr; int xpos, color, drawcount, first, need_move, need_clrtoeol, char_width; @@ -405,54 +405,54 @@ static int view_line_draw(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line, #endif if (view->dirty) /* don't bother drawing anything - redraw is coming */ - return 0; + return 0; cache = textbuffer_view_get_line_cache(view, line); if (subline >= cache->count) - return 0; + return 0; - color = ATTR_RESET; - need_move = TRUE; need_clrtoeol = FALSE; + color = ATTR_RESET; + need_move = TRUE; need_clrtoeol = FALSE; xpos = drawcount = 0; first = TRUE; text_newline = text = subline == 0 ? line->text : cache->lines[subline-1].start; for (;;) { if (text == text_newline) { - if (need_clrtoeol && xpos < term_width) { + if (need_clrtoeol && xpos < view->width + (view->width == term_width ? 0 : 1)) { term_set_color(view->window, ATTR_RESET); - term_clrtoeol(view->window); + term_window_clrtoeol(view->window, ypos); } if (first) first = FALSE; else { ypos++; - if (--max == 0) + if (--max == 0) break; } if (subline > 0) { - /* continuing previous line - indent it */ + /* continuing previous line - indent it */ indent_func = cache->lines[subline-1].indent_func; if (indent_func == NULL) xpos = cache->lines[subline-1].indent; - color = cache->lines[subline-1].color; + color = cache->lines[subline-1].color; #ifdef TERM_TRUECOLOR - fg24 = cache->lines[subline-1].fg24; - bg24 = cache->lines[subline-1].bg24; + fg24 = cache->lines[subline-1].fg24; + bg24 = cache->lines[subline-1].bg24; #endif } else { indent_func = NULL; } if (xpos == 0 && indent_func == NULL) - need_clrtoeol = TRUE; + need_clrtoeol = TRUE; else { /* line was indented - need to clear the - indented area first */ + indented area first */ term_set_color(view->window, ATTR_RESET); term_move(view->window, 0, ypos); - term_clrtoeol(view->window); + term_window_clrtoeol(view->window, ypos); if (indent_func != NULL) xpos = indent_func(view, line, ypos); @@ -469,9 +469,17 @@ static int view_line_draw(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line, } else { /* get the beginning of the next subline */ text_newline = cache->lines[subline].start; - need_move = !cache->lines[subline].continues; + if (view->width == term_width) { + /* ensure that links / long words are not broken */ + need_move = !cache->lines[subline].continues; + } else { + /* we cannot use the need_move + optimisation unless the split spans + the whole width */ + need_move = TRUE; + } } - drawcount++; + drawcount++; subline++; } @@ -479,10 +487,10 @@ static int view_line_draw(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line, /* command */ text++; if (*text == LINE_CMD_EOL) - break; + break; if (*text == LINE_CMD_CONTINUE) { - /* jump to next block */ + /* jump to next block */ memcpy(&tmp, text+1, sizeof(unsigned char *)); text = tmp; continue; @@ -517,13 +525,13 @@ static int view_line_draw(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line, } xpos += char_width; - if (xpos <= term_width) { + if (xpos <= view->width) { if (unichar_isprint(chr)) { if (view->utf8) - term_add_unichar(view->window, chr); + term_add_unichar(view->window, chr); else - for (; text < end; text++) - term_addch(view->window, *text); + for (; text < end; text++) + term_addch(view->window, *text); } else { /* low-ascii */ term_set_color(view->window, ATTR_RESET|ATTR_REVERSE); @@ -534,12 +542,12 @@ static int view_line_draw(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line, text = end; } - if (need_clrtoeol && xpos < term_width) { + if (need_clrtoeol && xpos < view->width + (view->width == term_width ? 0 : 1)) { term_set_color(view->window, ATTR_RESET); - term_clrtoeol(view->window); + term_window_clrtoeol(view->window, ypos); } - return drawcount; + return drawcount; } /* Recalculate view's bottom line information - try to keep the @@ -751,7 +759,7 @@ static void view_draw(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line, term_set_color(view->window, ATTR_RESET); while (lines > 0) { term_move(view->window, 0, ypos); - term_clrtoeol(view->window); + term_window_clrtoeol(view->window, ypos); ypos++; lines--; } } @@ -793,51 +801,51 @@ static int view_scroll(TEXT_BUFFER_VIEW_REC *view, LINE_REC **lines, int linecount, realcount, scroll_visible; if (*lines == NULL) - return 0; + return 0; /* scroll down */ scroll_visible = lines == &view->startline; realcount = -*subline; scrollcount += *subline; - *subline = 0; + *subline = 0; while (scrollcount > 0) { linecount = view_get_linecount(view, *lines); if ((scroll_visible && *lines == view->bottom_startline) && (scrollcount >= view->bottom_subline)) { *subline = view->bottom_subline; - realcount += view->bottom_subline; - scrollcount = 0; - break; + realcount += view->bottom_subline; + scrollcount = 0; + break; } - realcount += linecount; + realcount += linecount; scrollcount -= linecount; if (scrollcount < 0) { - realcount += scrollcount; + realcount += scrollcount; *subline = linecount+scrollcount; - scrollcount = 0; - break; + scrollcount = 0; + break; } if ((*lines)->next == NULL) break; - *lines = (*lines)->next; + *lines = (*lines)->next; } - /* scroll up */ + /* scroll up */ while (scrollcount < 0 && (*lines)->prev != NULL) { *lines = (*lines)->prev; linecount = view_get_linecount(view, *lines); - realcount -= linecount; + realcount -= linecount; scrollcount += linecount; if (scrollcount > 0) { - realcount += scrollcount; + realcount += scrollcount; *subline = scrollcount; - break; + break; } } @@ -845,19 +853,28 @@ static int view_scroll(TEXT_BUFFER_VIEW_REC *view, LINE_REC **lines, if (realcount <= -view->height || realcount >= view->height) { /* scrolled more than screenful, redraw the whole view */ - textbuffer_view_redraw(view); + textbuffer_view_redraw(view); } else { - term_set_color(view->window, ATTR_RESET); - term_window_scroll(view->window, realcount); + if (view->width == term_width) { + /* we can try to use vt100 scroll regions */ + term_set_color(view->window, ATTR_RESET); + term_window_scroll(view->window, realcount); - if (draw_nonclean) { - if (realcount < 0) - view_draw_top(view, -realcount, TRUE); - else - view_draw_bottom(view, realcount); - } + if (draw_nonclean) { + if (realcount < 0) + view_draw_top(view, -realcount, TRUE); + else + view_draw_bottom(view, realcount); + } - term_refresh(view->window); + term_refresh(view->window); + } else { + /* do not bother with vt400 scroll + rectangles for now, redraw the + whole view */ + view->dirty = TRUE; + irssi_set_dirty(); + } } } diff --git a/src/perl/textui/TextUI.xs b/src/perl/textui/TextUI.xs index 5d2c8a7f..e2f162a0 100644 --- a/src/perl/textui/TextUI.xs +++ b/src/perl/textui/TextUI.xs @@ -40,6 +40,7 @@ static void perl_text_buffer_view_fill_hash(HV *hv, TEXT_BUFFER_VIEW_REC *view) (void) hv_store(hv, "startline", 9, plain_bless(view->startline, "Irssi::TextUI::Line"), 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_subline", 14, newSViv(view->bottom_subline), 0); @@ -123,6 +124,74 @@ gui_input_set(str) CODE: gui_entry_set_text(active_entry, str); +void +gui_input_set_extent(pos, text) + int pos + char *text +PREINIT: + char *tt; +CODE: + tt = text != NULL ? format_string_expand(text, NULL) : NULL; + gui_entry_set_extent(active_entry, pos, tt); + g_free(tt); + +void +gui_input_set_extents(pos, len, left, right) + int pos + int len + char *left + char *right +PREINIT: + char *tl; + char *tr; +CODE: + tl = left != NULL ? format_string_expand(left, NULL) : NULL; + tr = right != NULL ? format_string_expand(right, NULL) : NULL; + gui_entry_set_extents(active_entry, pos, len, tl, tr); + g_free(tl); + g_free(tr); + +void +gui_input_clear_extents(pos, len = 0) + int pos + int len +CODE: + gui_entry_clear_extents(active_entry, pos, len); + +void +gui_input_get_extent(pos) + int pos +PREINIT: + char *ret; +PPCODE: + ret = gui_entry_get_extent(active_entry, pos); + XPUSHs(sv_2mortal(new_pv(ret))); + g_free(ret); + +void +gui_input_get_text_and_extents() +PREINIT: + GSList *ret, *tmp; +PPCODE: + ret = gui_entry_get_text_and_extents(active_entry); + for (tmp = ret; tmp != NULL; tmp = tmp->next) { + XPUSHs(sv_2mortal(new_pv(tmp->data))); + } + g_slist_free_full(ret, g_free); + +void +gui_input_set_text_and_extents(...) +PREINIT: + GSList *list; + int i; +PPCODE: + list = NULL; + for (i = items; i > 0; i--) { + list = g_slist_prepend(list, SvPV_nolen(ST(i-1))); + } + gui_entry_set_text_and_extents(active_entry, list); + g_slist_free(list); + int gui_input_get_pos() CODE: |