summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common.h2
-rw-r--r--src/core/channels-setup.c9
-rw-r--r--src/core/levels.c21
-rw-r--r--src/core/levels.h4
-rw-r--r--src/core/misc.c20
-rw-r--r--src/core/misc.h3
-rw-r--r--src/core/network-openssl.c20
-rw-r--r--src/core/servers-setup.c11
-rw-r--r--src/core/special-vars.c19
-rw-r--r--src/fe-common/core/chat-completion.c57
-rw-r--r--src/fe-common/core/completion.c18
-rw-r--r--src/fe-common/core/fe-common-core.c51
-rw-r--r--src/fe-common/core/fe-core-commands.c2
-rw-r--r--src/fe-common/core/fe-exec.c2
-rw-r--r--src/fe-common/core/fe-windows.h8
-rw-r--r--src/fe-common/core/formats.c27
-rw-r--r--src/fe-common/core/keyboard.c54
-rw-r--r--src/fe-common/core/themes.c68
-rw-r--r--src/fe-common/core/window-commands.c7
-rw-r--r--src/fe-common/core/window-items.c2
-rw-r--r--src/fe-common/irc/fe-irc-commands.c12
-rw-r--r--src/fe-common/irc/fe-netjoin.c18
-rw-r--r--src/fe-common/irc/fe-netsplit.c21
-rw-r--r--src/fe-fuzz/Makefile.am2
-rw-r--r--src/fe-fuzz/fe-common/Makefile.am1
-rw-r--r--src/fe-fuzz/fe-common/core/Makefile.am46
-rw-r--r--src/fe-fuzz/fe-common/core/theme-load.c66
-rw-r--r--src/fe-fuzz/irc/Makefile.am1
-rw-r--r--src/fe-fuzz/irc/core/Makefile.am46
-rw-r--r--src/fe-fuzz/irc/core/event-get-params.c82
-rw-r--r--src/fe-text/gui-entry.c596
-rw-r--r--src/fe-text/gui-entry.h11
-rw-r--r--src/fe-text/gui-printtext.c43
-rw-r--r--src/fe-text/gui-printtext.h1
-rw-r--r--src/fe-text/gui-readline.c3
-rw-r--r--src/fe-text/gui-windows.c16
-rw-r--r--src/fe-text/irssi.c4
-rw-r--r--src/fe-text/mainwindows-layout.c169
-rw-r--r--src/fe-text/mainwindows.c1209
-rw-r--r--src/fe-text/mainwindows.h22
-rw-r--r--src/fe-text/module-formats.c7
-rw-r--r--src/fe-text/module-formats.h1
-rw-r--r--src/fe-text/statusbar-items.c19
-rw-r--r--src/fe-text/statusbar.c72
-rw-r--r--src/fe-text/statusbar.h6
-rw-r--r--src/fe-text/term-terminfo.c110
-rw-r--r--src/fe-text/term.h4
-rw-r--r--src/fe-text/textbuffer-commands.c21
-rw-r--r--src/fe-text/textbuffer-view.c188
-rw-r--r--src/fe-text/textbuffer-view.h36
-rw-r--r--src/fe-text/textbuffer.c1
-rw-r--r--src/fe-text/textbuffer.h2
-rw-r--r--src/irc/core/channel-events.c8
-rw-r--r--src/irc/core/irc-cap.c176
-rw-r--r--src/irc/core/irc-nicklist.c19
-rw-r--r--src/irc/core/irc-servers.c6
-rw-r--r--src/irc/core/irc-servers.h3
-rw-r--r--src/irc/core/sasl.c39
-rw-r--r--src/irc/dcc/dcc-get.c10
-rw-r--r--src/perl/irc/Irc.xs17
-rw-r--r--src/perl/textui/TextUI.xs69
61 files changed, 2954 insertions, 634 deletions
diff --git a/src/common.h b/src/common.h
index a7eaa7cf..746aad4e 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 12
+#define IRSSI_ABI_VERSION 15
#define DEFAULT_SERVER_ADD_PORT 6667
#define DEFAULT_SERVER_ADD_TLS_PORT 6697
diff --git a/src/core/channels-setup.c b/src/core/channels-setup.c
index 4966d77d..8002646d 100644
--- a/src/core/channels-setup.c
+++ b/src/core/channels-setup.c
@@ -37,9 +37,14 @@ static int compare_channel_setup (CONFIG_NODE *node, CHANNEL_SETUP_REC *channel)
name = config_node_get_str(node, "name", NULL);
chatnet = config_node_get_str(node, "chatnet", NULL);
- if (g_strcmp0(name, channel->name) != 0 ||
- g_strcmp0(chatnet, channel->chatnet) != 0)
+ if (name == NULL || chatnet == NULL) {
+ return 0;
+ }
+
+ if (g_ascii_strcasecmp(name, channel->name) != 0 ||
+ g_ascii_strcasecmp(chatnet, channel->chatnet) != 0) {
return 1;
+ }
return 0;
}
diff --git a/src/core/levels.c b/src/core/levels.c
index e623c4de..eb7efcf7 100644
--- a/src/core/levels.c
+++ b/src/core/levels.c
@@ -21,6 +21,7 @@
#include "module.h"
#include "levels.h"
+/* the order of these levels must match the bits in levels.h */
static const char *levels[] = {
"CRAP",
"MSGS",
@@ -44,9 +45,6 @@ static const char *levels[] = {
"CLIENTCRAP",
"CLIENTERRORS",
"HILIGHTS",
-
- "NOHILIGHT",
- "NO_ACT",
NULL
};
@@ -63,6 +61,9 @@ int level_get(const char *level)
if (g_ascii_strcasecmp(level, "NO_ACT") == 0)
return MSGLEVEL_NO_ACT;
+ if (g_ascii_strcasecmp(level, "HIDDEN") == 0)
+ return MSGLEVEL_HIDDEN;
+
len = strlen(level);
if (len == 0) return 0;
@@ -138,17 +139,13 @@ char *bits2level(int bits)
str = g_string_new(NULL);
- if (bits & MSGLEVEL_NEVER) {
+ if (bits & MSGLEVEL_NEVER)
g_string_append(str, "NEVER ");
- bits &= ~MSGLEVEL_NEVER;
- }
- if (bits & MSGLEVEL_NO_ACT) {
+ if (bits & MSGLEVEL_NO_ACT)
g_string_append(str, "NO_ACT ");
- bits &= ~MSGLEVEL_NO_ACT;
- }
- if (bits == MSGLEVEL_ALL) {
+ if ((bits & MSGLEVEL_ALL) == MSGLEVEL_ALL) {
g_string_append(str, "ALL ");
} else {
for (n = 0; levels[n] != NULL; n++) {
@@ -156,6 +153,10 @@ char *bits2level(int bits)
g_string_append_printf(str, "%s ", levels[n]);
}
}
+
+ if (bits & MSGLEVEL_HIDDEN)
+ g_string_append(str, "HIDDEN ");
+
if (str->len > 0)
g_string_truncate(str, str->len-1);
diff --git a/src/core/levels.h b/src/core/levels.h
index 9f7e588f..b0ebafba 100644
--- a/src/core/levels.h
+++ b/src/core/levels.h
@@ -36,7 +36,9 @@ enum {
MSGLEVEL_NOHILIGHT = 0x1000000, /* Don't highlight this message */
MSGLEVEL_NO_ACT = 0x2000000, /* Don't trigger channel activity */
MSGLEVEL_NEVER = 0x4000000, /* never ignore / never log */
- MSGLEVEL_LASTLOG = 0x8000000 /* never ignore / never log */
+ MSGLEVEL_LASTLOG = 0x8000000, /* used for /lastlog */
+
+ MSGLEVEL_HIDDEN = 0x10000000 /* Hidden from view */
};
int level_get(const char *level);
diff --git a/src/core/misc.c b/src/core/misc.c
index e589b8c5..27741220 100644
--- a/src/core/misc.c
+++ b/src/core/misc.c
@@ -218,6 +218,19 @@ GSList *gslist_remove_string (GSList *list, const char *str)
return list;
}
+GSList *gslist_delete_string (GSList *list, const char *str, GDestroyNotify free_func)
+{
+ GSList *l;
+
+ l = g_slist_find_custom(list, str, (GCompareFunc) g_strcmp0);
+ if (l != NULL) {
+ free_func(l->data);
+ return g_slist_delete_link(list, l);
+ }
+
+ return list;
+}
+
/* `list' contains pointer to structure with a char* to string. */
char *gslistptr_to_string(GSList *list, int offset, const char *delimiter)
{
@@ -703,8 +716,11 @@ int expand_escape(const char **data)
*data += 2;
return strtol(digit, NULL, 16);
case 'c':
- /* control character (\cA = ^A) */
- (*data)++;
+ /* check for end of string */
+ if ((*data)[1] == '\0')
+ return 0;
+ /* control character (\cA = ^A) */
+ (*data)++;
return i_toupper(**data) - 64;
case '0': case '1': case '2': case '3':
case '4': case '5': case '6': case '7':
diff --git a/src/core/misc.h b/src/core/misc.h
index 375744db..a46a1432 100644
--- a/src/core/misc.h
+++ b/src/core/misc.h
@@ -21,7 +21,8 @@ GSList *gslist_find_string(GSList *list, const char *key);
GSList *gslist_find_icase_string(GSList *list, const char *key);
GList *glist_find_string(GList *list, const char *key);
GList *glist_find_icase_string(GList *list, const char *key);
-GSList *gslist_remove_string (GSList *list, const char *str);
+GSList *gslist_remove_string (GSList *list, const char *str) G_GNUC_DEPRECATED;
+GSList *gslist_delete_string (GSList *list, const char *str, GDestroyNotify free_func);
void gslist_free_full (GSList *list, GDestroyNotify free_func);
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/core/servers-setup.c b/src/core/servers-setup.c
index 9492c58c..2a92a367 100644
--- a/src/core/servers-setup.c
+++ b/src/core/servers-setup.c
@@ -474,10 +474,15 @@ static int compare_server_setup (CONFIG_NODE *node, SERVER_SETUP_REC *server)
chatnet = config_node_get_str(node, "chatnet", NULL);
port = config_node_get_int(node, "port", 0);
- if (g_strcmp0(address, server->address) != 0 ||
- g_strcmp0(chatnet, server->chatnet) != 0 ||
- port != server->port)
+ if (address == NULL || chatnet == NULL) {
+ return 0;
+ }
+
+ if (g_ascii_strcasecmp(address, server->address) != 0 ||
+ g_ascii_strcasecmp(chatnet, server->chatnet) != 0 ||
+ port != server->port) {
return 1;
+ }
return 0;
}
diff --git a/src/core/special-vars.c b/src/core/special-vars.c
index aaf8da8f..33d9cd55 100644
--- a/src/core/special-vars.c
+++ b/src/core/special-vars.c
@@ -33,6 +33,8 @@
#define isarg(c) \
(i_isdigit(c) || (c) == '*' || (c) == '~' || (c) == '-')
+#define ALIGN_MAX 222488
+
static SPECIAL_HISTORY_FUNC history_func = NULL;
static char *get_argument(char **cmd, char **arglist)
@@ -300,6 +302,10 @@ static int get_alignment_args(char **data, int *align, int *flags, char *pad)
if (!parse_uint(str, &endptr, 10, &align_)) {
return FALSE;
}
+ /* alignment larger than supported */
+ if (align_ > ALIGN_MAX) {
+ return FALSE;
+ }
str = endptr;
*align = align_;
@@ -337,11 +343,14 @@ char *get_alignment(const char *text, int align, int flags, char pad)
/* add pad characters */
if (flags & ALIGN_PAD) {
- while (string_width(str->str, policy) < align) {
+ int pad_len = align - string_width(str->str, policy);
+ if (pad_len > 0) {
+ char *pad_full = g_strnfill(pad_len, pad);
if (flags & ALIGN_RIGHT)
- g_string_prepend_c(str, pad);
+ g_string_prepend(str, pad_full);
else
- g_string_append_c(str, pad);
+ g_string_append(str, pad_full);
+ g_free(pad_full);
}
}
@@ -384,6 +393,7 @@ char *parse_special(char **cmd, SERVER_REC *server, void *item,
}
nest_free = FALSE; nest_value = NULL;
+#if 0 /* this code is disabled due to security issues until it is fixed */
if (**cmd == '(' && (*cmd)[1] != '\0') {
/* subvariable */
int toplevel = nested_orig_cmd == NULL;
@@ -412,6 +422,9 @@ char *parse_special(char **cmd, SERVER_REC *server, void *item,
if (toplevel) nested_orig_cmd = NULL;
}
+#else
+ if (nested_orig_cmd) nested_orig_cmd = NULL;
+#endif
if (**cmd != '{')
brackets = FALSE;
diff --git a/src/fe-common/core/chat-completion.c b/src/fe-common/core/chat-completion.c
index 97cd0565..d610008f 100644
--- a/src/fe-common/core/chat-completion.c
+++ b/src/fe-common/core/chat-completion.c
@@ -173,6 +173,7 @@ static void sig_message_public(SERVER_REC *server, const char *msg,
{
CHANNEL_REC *channel;
int own;
+ g_return_if_fail(nick != NULL);
channel = channel_find(server, target);
if (channel != NULL) {
@@ -185,6 +186,7 @@ static void sig_message_join(SERVER_REC *server, const char *channel,
const char *nick, const char *address)
{
CHANNEL_REC *chanrec;
+ g_return_if_fail(nick != NULL);
chanrec = channel_find(server, channel);
if (chanrec != NULL)
@@ -639,6 +641,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 +746,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/completion.c b/src/fe-common/core/completion.c
index e78fe7d5..fd452e5c 100644
--- a/src/fe-common/core/completion.c
+++ b/src/fe-common/core/completion.c
@@ -187,12 +187,18 @@ char *word_complete(WINDOW_REC *window, const char *line, int *pos, int erase, i
char *old;
old = linestart;
- linestart = *linestart == '\0' ?
- g_strdup(word) :
- g_strdup_printf("%s%c%s",
- /* do not accidentally duplicate the word separator */
- line == wordstart - 1 ? "" : linestart,
- old_wordstart[-1], word);
+ /* we want to move word into linestart */
+ if (*linestart == '\0') {
+ linestart = g_strdup(word);
+ } else {
+ GString *str = g_string_new(linestart);
+ if (old_wordstart[-1] != str->str[str->len - 1]) {
+ /* do not accidentally duplicate the word separator */
+ g_string_append_c(str, old_wordstart[-1]);
+ }
+ g_string_append(str, word);
+ linestart = g_string_free(str, FALSE);
+ }
g_free(old);
g_free(word);
diff --git a/src/fe-common/core/fe-common-core.c b/src/fe-common/core/fe-common-core.c
index d0d36490..ef5d2113 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-core-commands.c b/src/fe-common/core/fe-core-commands.c
index fb98cc25..222543d5 100644
--- a/src/fe-common/core/fe-core-commands.c
+++ b/src/fe-common/core/fe-core-commands.c
@@ -114,7 +114,7 @@ static void cmd_version(char *data)
}
}
-/* SYNTAX: CAT <file> */
+/* SYNTAX: CAT <file> [<seek position>] */
static void cmd_cat(const char *data)
{
char *fname, *fposstr;
diff --git a/src/fe-common/core/fe-exec.c b/src/fe-common/core/fe-exec.c
index 36990866..c1739d39 100644
--- a/src/fe-common/core/fe-exec.c
+++ b/src/fe-common/core/fe-exec.c
@@ -613,7 +613,7 @@ static void sig_exec_input(PROCESS_REC *rec, const char *text)
str = g_strconcat(rec->target_nick ? "-nick " :
rec->target_channel ? "-channel " : "",
- rec->target, " ", text, NULL);
+ rec->target, " ", *text == '\0' ? " " : text, NULL);
signal_emit(rec->notice ? "command notice" : "command msg",
3, str, server, item);
g_free(str);
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/keyboard.c b/src/fe-common/core/keyboard.c
index 6f7907eb..c3df5ed7 100644
--- a/src/fe-common/core/keyboard.c
+++ b/src/fe-common/core/keyboard.c
@@ -156,6 +156,7 @@ static void keyconfig_save(const char *id, const char *key, const char *data)
static void keyconfig_clear(const char *key)
{
CONFIG_NODE *node;
+ KEY_REC *rec;
g_return_if_fail(key != NULL);
@@ -165,6 +166,11 @@ static void keyconfig_clear(const char *key)
iconfig_node_remove(iconfig_node_traverse("(keyboard", FALSE),
node);
}
+ if ((rec = g_hash_table_lookup(default_keys, key)) != NULL) {
+ node = iconfig_node_traverse("(keyboard", TRUE);
+ node = iconfig_node_section(node, NULL, NODE_TYPE_BLOCK);
+ iconfig_node_set_str(node, "key", key);
+ }
}
KEYINFO_REC *key_info_find(const char *id)
@@ -569,13 +575,38 @@ void key_configure_remove(const char *key)
g_return_if_fail(key != NULL);
+ keyconfig_clear(key);
+
rec = g_hash_table_lookup(keys, key);
if (rec == NULL) return;
- keyconfig_clear(key);
key_configure_destroy(rec);
}
+/* Reset key to default */
+void key_configure_reset(const char *key)
+{
+ KEY_REC *rec;
+ CONFIG_NODE *node;
+
+ g_return_if_fail(key != NULL);
+
+ node = key_config_find(key);
+ if (node != NULL) {
+ iconfig_node_remove(iconfig_node_traverse("(keyboard", FALSE), node);
+ }
+
+ if ((rec = g_hash_table_lookup(default_keys, key)) != NULL) {
+ key_configure_create(rec->info->id, rec->key, rec->data);
+ } else {
+ rec = g_hash_table_lookup(keys, key);
+ if (rec == NULL)
+ return;
+
+ key_configure_destroy(rec);
+ }
+}
+
static int key_emit_signal(KEYBOARD_REC *keyboard, KEY_REC *key)
{
int consumed;
@@ -739,7 +770,9 @@ static void cmd_show_keys(const char *searchkey, int full)
for (key = rec->keys; key != NULL; key = key->next) {
KEY_REC *rec = key->data;
- if ((len == 0 || g_ascii_strncasecmp(rec->key, searchkey, len) == 0) &&
+ if ((len == 0 ||
+ (full ? strncmp(rec->key, searchkey, len) == 0 :
+ g_ascii_strncasecmp(rec->key, searchkey, len) == 0)) &&
(!full || rec->key[len] == '\0')) {
printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_BIND_LIST,
rec->key, rec->info->id, rec->data == NULL ? "" : rec->data);
@@ -750,7 +783,7 @@ static void cmd_show_keys(const char *searchkey, int full)
printformat(NULL, NULL, MSGLEVEL_CLIENTCRAP, TXT_BIND_FOOTER);
}
-/* SYNTAX: BIND [-list] [-delete] [<key> [<command> [<data>]]] */
+/* SYNTAX: BIND [-list] [-delete | -reset] [<key> [<command> [<data>]]] */
static void cmd_bind(const char *data)
{
GHashTable *optlist;
@@ -780,6 +813,12 @@ static void cmd_bind(const char *data)
key_configure_remove(key);
cmd_params_free(free_arg);
return;
+ } else if (*key != '\0' && g_hash_table_lookup(optlist, "reset")) {
+ /* reset key */
+ key_configure_reset(key);
+ cmd_show_keys(key, TRUE);
+ cmd_params_free(free_arg);
+ return;
}
if (*id == '\0') {
@@ -878,8 +917,13 @@ static void key_config_read(CONFIG_NODE *node)
id = config_node_get_str(node, "id", NULL);
data = config_node_get_str(node, "data", NULL);
- if (key != NULL && id != NULL)
+ if (key != NULL && id != NULL) {
key_configure_create(id, key, data);
+ } else if (key != NULL && id == NULL && data == NULL) {
+ KEY_REC *rec = g_hash_table_lookup(keys, key);
+ if (rec != NULL)
+ key_configure_destroy(rec);
+ }
}
static void read_keyboard_config(void)
@@ -938,7 +982,7 @@ void keyboard_init(void)
signal_add("complete command bind", (SIGNAL_FUNC) sig_complete_bind);
command_bind("bind", NULL, (SIGNAL_FUNC) cmd_bind);
- command_set_options("bind", "delete list");
+ command_set_options("bind", "delete reset list");
}
void keyboard_deinit(void)
diff --git a/src/fe-common/core/themes.c b/src/fe-common/core/themes.c
index cb1cce8f..e9818be9 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);
@@ -382,7 +382,8 @@ char *theme_format_expand_get(THEME_REC *theme, const char **format)
} else {
theme_format_append_next(theme, str, format,
reset, reset,
- &dummy, &dummy, 0);
+ &dummy, &dummy,
+ EXPAND_FLAG_IGNORE_REPLACES);
continue;
}
@@ -400,16 +401,19 @@ char *theme_format_expand_get(THEME_REC *theme, const char **format)
return ret;
}
+static char *theme_format_expand_data_rec(THEME_REC *theme, const char **format,
+ theme_rm_col default_fg, theme_rm_col default_bg,
+ theme_rm_col *save_last_fg, theme_rm_col *save_last_bg,
+ int flags, GTree *block_list);
+
/* expand a single {abstract ...data... } */
-static char *theme_format_expand_abstract(THEME_REC *theme,
- const char **formatp,
- theme_rm_col *last_fg,
- theme_rm_col *last_bg,
- int flags)
+static char *theme_format_expand_abstract(THEME_REC *theme, const char **formatp,
+ theme_rm_col *last_fg, theme_rm_col *last_bg, int flags,
+ GTree *block_list)
{
GString *str;
const char *p, *format;
- char *abstract, *data, *ret;
+ char *abstract, *data, *ret, *blocking;
theme_rm_col default_fg, default_bg;
int len;
@@ -439,12 +443,22 @@ static char *theme_format_expand_abstract(THEME_REC *theme,
}
*formatp = format+len;
+ if (block_list == NULL) {
+ block_list = g_tree_new_full((GCompareDataFunc) g_strcmp0, NULL, g_free, NULL);
+ } else {
+ g_tree_ref(block_list);
+ }
+
/* get the abstract data */
data = g_hash_table_lookup(theme->abstracts, abstract);
- g_free(abstract);
- if (data == NULL) {
+ if (data == NULL || g_tree_lookup(block_list, abstract) != NULL) {
/* unknown abstract, just display the data */
data = "$0-";
+ g_free(abstract);
+ blocking = NULL;
+ } else {
+ blocking = abstract;
+ g_tree_insert(block_list, blocking, blocking);
}
abstract = g_strdup(data);
@@ -473,7 +487,7 @@ static char *theme_format_expand_abstract(THEME_REC *theme,
str = g_string_new(NULL);
p = ret;
while (*p != '\0') {
- if (*p == '\\') {
+ if (*p == '\\' && p[1] != '\0') {
int chr;
p++;
chr = expand_escape(&p);
@@ -488,18 +502,20 @@ static char *theme_format_expand_abstract(THEME_REC *theme,
/* abstract may itself contain abstracts or replaces */
p = abstract;
- ret = theme_format_expand_data(theme, &p, default_fg, default_bg,
- last_fg, last_bg,
- flags | EXPAND_FLAG_LASTCOLOR_ARG);
+ ret = theme_format_expand_data_rec(theme, &p, default_fg, default_bg, last_fg, last_bg,
+ flags | EXPAND_FLAG_LASTCOLOR_ARG, block_list);
g_free(abstract);
+ if (blocking != NULL) {
+ g_tree_remove(block_list, blocking);
+ }
+ g_tree_unref(block_list);
return ret;
}
-/* expand the data part in {abstract data} */
-char *theme_format_expand_data(THEME_REC *theme, const char **format,
- theme_rm_col default_fg, theme_rm_col default_bg,
- theme_rm_col *save_last_fg, theme_rm_col *save_last_bg,
- int flags)
+static char *theme_format_expand_data_rec(THEME_REC *theme, const char **format,
+ theme_rm_col default_fg, theme_rm_col default_bg,
+ theme_rm_col *save_last_fg, theme_rm_col *save_last_bg,
+ int flags, GTree *block_list)
{
GString *str;
char *ret, *abstract;
@@ -545,9 +561,8 @@ char *theme_format_expand_data(THEME_REC *theme, const char **format,
break; /* error */
/* get a single {...} */
- abstract = theme_format_expand_abstract(theme, format,
- &last_fg, &last_bg,
- recurse_flags);
+ abstract = theme_format_expand_abstract(theme, format, &last_fg, &last_bg,
+ recurse_flags, block_list);
if (abstract != NULL) {
g_string_append(str, abstract);
g_free(abstract);
@@ -565,6 +580,15 @@ char *theme_format_expand_data(THEME_REC *theme, const char **format,
return ret;
}
+/* expand the data part in {abstract data} */
+char *theme_format_expand_data(THEME_REC *theme, const char **format, theme_rm_col default_fg,
+ theme_rm_col default_bg, theme_rm_col *save_last_fg,
+ theme_rm_col *save_last_bg, int flags)
+{
+ return theme_format_expand_data_rec(theme, format, default_fg, default_bg, save_last_bg,
+ save_last_bg, flags, NULL);
+}
+
#define IS_OLD_FORMAT(code, last_fg, last_bg) \
(((code) == 'n' && (last_fg) == 'n' && (last_bg) == 'n') || \
((code) != 'n' && ((code) == (last_fg) || (code) == (last_bg))))
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-irc-commands.c b/src/fe-common/irc/fe-irc-commands.c
index 11a911d2..7a6b17a6 100644
--- a/src/fe-common/irc/fe-irc-commands.c
+++ b/src/fe-common/irc/fe-irc-commands.c
@@ -345,16 +345,18 @@ static void cmd_ts(const char *data)
}
typedef struct {
- IRC_SERVER_REC *server;
+ char *server_tag;
char *nick;
} OPER_PASS_REC;
static void cmd_oper_got_pass(const char *password, OPER_PASS_REC *rec)
{
- if (*password != '\0')
- irc_send_cmdv(rec->server, "OPER %s %s", rec->nick, password);
+ SERVER_REC *server_rec = server_find_tag(rec->server_tag);
+ if (*password != '\0' && IS_IRC_SERVER(server_rec))
+ irc_send_cmdv((IRC_SERVER_REC *) server_rec, "OPER %s %s", rec->nick, password);
g_free(rec->nick);
- g_free(rec);
+ g_free(rec->server_tag);
+ g_free(rec);
}
static void cmd_oper(const char *data, IRC_SERVER_REC *server)
@@ -374,7 +376,7 @@ static void cmd_oper(const char *data, IRC_SERVER_REC *server)
OPER_PASS_REC *rec;
rec = g_new(OPER_PASS_REC, 1);
- rec->server = server;
+ rec->server_tag = g_strdup(server->tag);
rec->nick = g_strdup(*nick != '\0' ? nick : server->nick);
format = format_get_text(MODULE_NAME, NULL, server, NULL,
diff --git a/src/fe-common/irc/fe-netjoin.c b/src/fe-common/irc/fe-netjoin.c
index 8272093f..9ea633b4 100644
--- a/src/fe-common/irc/fe-netjoin.c
+++ b/src/fe-common/irc/fe-netjoin.c
@@ -253,15 +253,17 @@ static void sig_print_starting(TEXT_DEST_REC *dest)
if (!IS_IRC_SERVER(dest->server))
return;
- if (!(dest->level & MSGLEVEL_PUBLIC))
- return;
-
- if (!server_ischannel(dest->server, dest->target))
- return;
-
rec = netjoin_find_server(IRC_SERVER(dest->server));
- if (rec != NULL && rec->netjoins != NULL)
- print_netjoins(rec, dest->target);
+ if (rec != NULL && rec->netjoins != NULL) {
+ /* if netjoins exists, the server rec should be
+ still valid. otherwise, calling server->ischannel
+ may not be safe. */
+ if (dest->target != NULL &&
+ !server_ischannel((SERVER_REC *) rec->server, dest->target))
+ return;
+
+ print_netjoins(rec, 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..258d0d57 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);
@@ -255,15 +255,16 @@ static void sig_print_starting(TEXT_DEST_REC *dest)
if (!IS_IRC_SERVER(dest->server))
return;
- if (!(dest->level & MSGLEVEL_PUBLIC))
- return;
-
- if (!server_ischannel(dest->server, dest->target))
- return;
-
rec = IRC_SERVER(dest->server);
- if (rec->split_servers != NULL)
- print_splits(rec, dest->target);
+ if (rec->split_servers != NULL) {
+ /* if split_servers exists, the server rec should be
+ still valid. otherwise, calling server->ischannel
+ may not be safe. */
+ if (dest->target != NULL && !server_ischannel((SERVER_REC *) rec, dest->target))
+ return;
+
+ 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 c63c495c..f761e390 100644
--- a/src/fe-text/gui-windows.c
+++ b/src/fe-text/gui-windows.c
@@ -23,6 +23,7 @@
#include "misc.h"
#include "settings.h"
#include "special-vars.h"
+#include "levels.h"
#include "term.h"
#include "gui-entry.h"
@@ -50,6 +51,7 @@ static GUI_WINDOW_REC *gui_window_init(WINDOW_REC *window,
!settings_get_bool("indent_always"),
get_default_indent_func());
textbuffer_view_set_break_wide(gui->view, settings_get_bool("break_wide"));
+ textbuffer_view_set_hidden_level(gui->view, MSGLEVEL_HIDDEN);
if (parent->active == window)
textbuffer_view_set_window(gui->view, parent->screen_win);
return gui;
@@ -73,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);
@@ -281,13 +284,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 fae02539..17c6647b 100644
--- a/src/fe-text/mainwindows-layout.c
+++ b/src/fe-text/mainwindows-layout.c
@@ -23,6 +23,7 @@
#include "misc.h"
#include "lib-config/iconfig.h"
#include "settings.h"
+#include "levels.h"
#include "mainwindows.h"
#include "gui-windows.h"
@@ -41,6 +42,12 @@ static void sig_layout_window_save(WINDOW_REC *window, CONFIG_NODE *node)
iconfig_node_set_int(node, "parent", active->refnum);
}
+ if (gui->view->hidden_level != MSGLEVEL_HIDDEN) {
+ char *level = bits2level(gui->view->hidden_level);
+ iconfig_node_set_str(node, "hidelevel", level);
+ g_free(level);
+ }
+
if (gui->use_scroll)
iconfig_node_set_bool(node, "scroll", gui->scroll);
}
@@ -48,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)
@@ -58,10 +65,13 @@ static void sig_layout_window_restore(WINDOW_REC *window, CONFIG_NODE *node)
if (config_node_get_bool(node, "sticky", FALSE))
gui_window_set_sticky(window);
+
+ textbuffer_view_set_hidden_level(gui->view, level2bits(config_node_get_str(node, "hidelevel", "HIDDEN"), NULL));
+
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);
}
}
@@ -74,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)
@@ -88,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
@@ -108,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;
@@ -123,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) {
@@ -178,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));
- window = window_create(NULL, TRUE);
- window_set_refnum(window, atoi(node->key));
+ new_win = window_create(NULL, TRUE);
+
+ 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;
@@ -206,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 c4606197..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 },
@@ -50,6 +50,7 @@ FORMAT_REC gui_text_formats[] =
{ "window_info_scroll", "%#Scroll : $0", 1, { 0 } },
{ "window_scroll", "Window scroll mode is now $0", 1, { 0 } },
{ "window_scroll_unknown", "Unknown scroll mode $0, must be ON, OFF or DEFAULT", 1, { 0 } },
+ { "window_hidelevel", "Window hidden level is now $0", 1, { 0 } },
/* ---- */
{ NULL, "Statusbars", 0 },
@@ -86,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/module-formats.h b/src/fe-text/module-formats.h
index 05e9438d..b753238b 100644
--- a/src/fe-text/module-formats.h
+++ b/src/fe-text/module-formats.h
@@ -26,6 +26,7 @@ enum {
TXT_WINDOW_INFO_SCROLL,
TXT_WINDOW_SCROLL,
TXT_WINDOW_SCROLL_UNKNOWN,
+ TXT_WINDOW_HIDELEVEL,
TXT_FILL_3,
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-commands.c b/src/fe-text/textbuffer-commands.c
index 648862e7..97d897f3 100644
--- a/src/fe-text/textbuffer-commands.c
+++ b/src/fe-text/textbuffer-commands.c
@@ -89,6 +89,25 @@ static void cmd_window_scroll(const char *data)
gui->scroll : settings_get_bool("scroll"));
}
+/* SYNTAX: WINDOW HIDELEVEL [<level>] */
+static void cmd_window_hidelevel(const char *data)
+{
+ GUI_WINDOW_REC *gui;
+ char *level;
+
+ g_return_if_fail(data != NULL);
+
+ gui = WINDOW_GUI(active_win);
+ textbuffer_view_set_hidden_level(gui->view,
+ combine_level(gui->view->hidden_level, data));
+ textbuffer_view_redraw(gui->view);
+ level = gui->view->hidden_level == 0 ? g_strdup("NONE") :
+ bits2level(gui->view->hidden_level);
+ printformat_window(active_win, MSGLEVEL_CLIENTNOTICE,
+ TXT_WINDOW_HIDELEVEL, level);
+ g_free(level);
+}
+
static void cmd_scrollback(const char *data, SERVER_REC *server,
WI_ITEM_REC *item)
{
@@ -358,6 +377,7 @@ void textbuffer_commands_init(void)
{
command_bind("clear", NULL, (SIGNAL_FUNC) cmd_clear);
command_bind("window scroll", NULL, (SIGNAL_FUNC) cmd_window_scroll);
+ command_bind("window hidelevel", NULL, (SIGNAL_FUNC) cmd_window_hidelevel);
command_bind("scrollback", NULL, (SIGNAL_FUNC) cmd_scrollback);
command_bind("scrollback clear", NULL, (SIGNAL_FUNC) cmd_scrollback_clear);
command_bind("scrollback levelclear", NULL, (SIGNAL_FUNC) cmd_scrollback_levelclear);
@@ -377,6 +397,7 @@ void textbuffer_commands_deinit(void)
{
command_unbind("clear", (SIGNAL_FUNC) cmd_clear);
command_unbind("window scroll", (SIGNAL_FUNC) cmd_window_scroll);
+ command_unbind("window hidelevel", (SIGNAL_FUNC) cmd_window_hidelevel);
command_unbind("scrollback", (SIGNAL_FUNC) cmd_scrollback);
command_unbind("scrollback clear", (SIGNAL_FUNC) cmd_scrollback_clear);
command_unbind("scrollback levelclear", (SIGNAL_FUNC) cmd_scrollback_levelclear);
diff --git a/src/fe-text/textbuffer-view.c b/src/fe-text/textbuffer-view.c
index cb066f5e..99625ecc 100644
--- a/src/fe-text/textbuffer-view.c
+++ b/src/fe-text/textbuffer-view.c
@@ -41,9 +41,15 @@ static GSList *views;
#define view_is_bottom(view) \
((view)->ypos >= -1 && (view)->ypos < (view)->height)
-#define view_get_linecount(view, line) \
+#define view_get_linecount_hidden(view, line) \
textbuffer_view_get_line_cache(view, line)->count
+#define view_line_is_hidden(view, line) \
+ (((line)->info.level & (view)->hidden_level) != 0)
+
+#define view_get_linecount(view, line) \
+ (view_line_is_hidden(view, line) ? 0 : view_get_linecount_hidden(view, line))
+
static GSList *textbuffer_get_views(TEXT_BUFFER_REC *buffer)
{
GSList *tmp, *list;
@@ -388,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;
@@ -399,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);
@@ -463,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++;
}
@@ -473,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;
@@ -511,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);
@@ -528,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
@@ -552,6 +566,9 @@ static void textbuffer_view_init_bottom(TEXT_BUFFER_VIEW_REC *view)
total = 0;
line = textbuffer_line_last(view->buffer);
for (; line != NULL; line = line->prev) {
+ if (view_line_is_hidden(view, line))
+ continue;
+
linecount = view_get_linecount(view, line);
if (line == view->bottom_startline) {
/* keep the old one, make sure that subline is ok */
@@ -614,6 +631,8 @@ TEXT_BUFFER_VIEW_REC *textbuffer_view_create(TEXT_BUFFER_REC *buffer,
view->subline = view->bottom_subline;
view->bottom = TRUE;
+ view->hidden_level = 0;
+
textbuffer_view_init_ypos(view);
view->bookmarks = g_hash_table_new((GHashFunc) g_str_hash,
@@ -726,8 +745,10 @@ static void view_draw(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line,
return;
while (line != NULL && lines > 0) {
- linecount = view_line_draw(view, line, subline, ypos, lines);
- ypos += linecount; lines -= linecount;
+ if (!view_line_is_hidden(view, line)) {
+ linecount = view_line_draw(view, line, subline, ypos, lines);
+ ypos += linecount; lines -= linecount;
+ }
subline = 0;
line = line->next;
@@ -738,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--;
}
}
@@ -768,58 +789,63 @@ static void view_draw_bottom(TEXT_BUFFER_VIEW_REC *view, int lines)
view_draw(view, line, subline, maxline, lines, TRUE);
}
-/* Returns number of lines actually scrolled */
+/* lines: this pointer is scrolled by scrollcount screen lines
+ subline: this pointer contains the subline position
+ scrollcount: the number of lines to scroll down (negative: up)
+ draw_nonclean: whether to redraw the screen now
+
+ Returns number of lines actually scrolled */
static int view_scroll(TEXT_BUFFER_VIEW_REC *view, LINE_REC **lines,
int *subline, int scrollcount, int draw_nonclean)
{
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;
}
}
@@ -827,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();
+ }
}
}
@@ -1027,7 +1062,7 @@ static void view_insert_line(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line)
view->bottom = view_is_bottom(view);
}
- if (view->window != NULL) {
+ if (view->window != NULL && !view_line_is_hidden(view, line)) {
ypos = view->ypos+1 - view_get_linecount(view, line);
if (ypos >= 0)
subline = 0;
@@ -1042,7 +1077,7 @@ static void view_insert_line(TEXT_BUFFER_VIEW_REC *view, LINE_REC *line)
}
}
- if (view->window != NULL)
+ if (view->window != NULL && !view_line_is_hidden(view, line))
term_refresh(view->window);
}
@@ -1122,6 +1157,12 @@ static int view_get_lines_height(TEXT_BUFFER_VIEW_REC *view,
return height < view->height ? height : view->height;
}
+/* line: line to remove
+ linecount: linecount of that line, to be offset when the line was in/below view
+
+ scroll the window maintaining the startline while removing line
+ if startline is removed, make the previous line the new startline
+*/
static void view_remove_line_update_startline(TEXT_BUFFER_VIEW_REC *view,
LINE_REC *line, int linecount)
{
@@ -1318,6 +1359,37 @@ LINE_REC *textbuffer_view_get_bookmark(TEXT_BUFFER_VIEW_REC *view,
return g_hash_table_lookup(view->bookmarks, name);
}
+void textbuffer_view_set_hidden_level(TEXT_BUFFER_VIEW_REC *view, int level)
+{
+ g_return_if_fail(view != NULL);
+
+ if (view->hidden_level != level) {
+ if (view->empty_linecount > 0 && view->startline != NULL) {
+ int old_height, new_height;
+ LINE_REC *hidden_start;
+
+ hidden_start = view->startline;
+ while (hidden_start->prev != NULL && view_line_is_hidden(view, hidden_start->prev)) {
+ hidden_start = hidden_start->prev;
+ }
+
+ old_height = view_get_lines_height(view, hidden_start, view->subline, NULL);
+ view->hidden_level = level;
+ new_height = view_get_lines_height(view, hidden_start, view->subline, NULL);
+
+ view->empty_linecount -= new_height - old_height;
+
+ if (view->empty_linecount < 0)
+ view->empty_linecount = 0;
+ else if (view->empty_linecount > view->height)
+ view->empty_linecount = view->height;
+ } else {
+ view->hidden_level = level;
+ }
+ textbuffer_view_resize(view, view->width, view->height);
+ }
+}
+
/* Specify window where the changes in view should be drawn,
NULL disables it. */
void textbuffer_view_set_window(TEXT_BUFFER_VIEW_REC *view,
diff --git a/src/fe-text/textbuffer-view.h b/src/fe-text/textbuffer-view.h
index 5e7a9d0a..a670df2b 100644
--- a/src/fe-text/textbuffer-view.h
+++ b/src/fe-text/textbuffer-view.h
@@ -49,41 +49,51 @@ typedef struct {
struct _TEXT_BUFFER_VIEW_REC {
TEXT_BUFFER_REC *buffer;
- GSList *siblings; /* other views that use the same buffer */
+ /* other views that use the same buffer */
+ GSList *siblings;
TERM_WINDOW *window;
int width, height;
int default_indent;
INDENT_FUNC default_indent_func;
- unsigned int longword_noindent:1;
- unsigned int scroll:1; /* scroll down automatically when at bottom */
- unsigned int utf8:1; /* use UTF8 in this view */
- unsigned int break_wide:1; /* Break wide chars in this view */
TEXT_BUFFER_CACHE_REC *cache;
- int ypos; /* cursor position - visible area is 0..height-1 */
+ /* cursor position - visible area is 0..height-1 */
+ int ypos;
- LINE_REC *startline; /* line at the top of the screen */
- int subline; /* number of "real lines" to skip from `startline' */
+ /* line at the top of the screen */
+ LINE_REC *startline;
+ /* number of "real lines" to skip from `startline' */
+ int subline;
/* marks the bottom of the text buffer */
LINE_REC *bottom_startline;
int bottom_subline;
+ /* Bookmarks to the lines in the buffer - removed automatically
+ when the line gets removed from buffer */
+ GHashTable *bookmarks;
+
+ /* these levels should be hidden */
+ int hidden_level;
/* how many empty lines are in screen. a screenful when started
or used /CLEAR */
int empty_linecount;
+
+ unsigned int longword_noindent:1;
+ /* scroll down automatically when at bottom */
+ unsigned int scroll:1;
+ /* use UTF8 in this view */
+ unsigned int utf8:1;
+ /* Break wide chars in this view */
+ unsigned int break_wide:1;
/* window is at the bottom of the text buffer */
unsigned int bottom:1;
/* if !bottom - new text has been printed since we were at bottom */
unsigned int more_text:1;
/* Window needs a redraw */
unsigned int dirty:1;
-
- /* Bookmarks to the lines in the buffer - removed automatically
- when the line gets removed from buffer */
- GHashTable *bookmarks;
};
/* Create new view. */
@@ -143,6 +153,8 @@ void textbuffer_view_set_bookmark_bottom(TEXT_BUFFER_VIEW_REC *view,
/* Return the line for bookmark */
LINE_REC *textbuffer_view_get_bookmark(TEXT_BUFFER_VIEW_REC *view,
const char *name);
+/* Set hidden level for view */
+void textbuffer_view_set_hidden_level(TEXT_BUFFER_VIEW_REC *view, int level);
/* Specify window where the changes in view should be drawn,
NULL disables it. */
diff --git a/src/fe-text/textbuffer.c b/src/fe-text/textbuffer.c
index a920fab2..01cdd118 100644
--- a/src/fe-text/textbuffer.c
+++ b/src/fe-text/textbuffer.c
@@ -230,6 +230,7 @@ LINE_REC *textbuffer_line_last(TEXT_BUFFER_REC *buffer)
return buffer->cur_line;
}
+/* returns TRUE if `search' comes on or after `line' in the buffer */
int textbuffer_line_exists_after(LINE_REC *line, LINE_REC *search)
{
while (line != NULL) {
diff --git a/src/fe-text/textbuffer.h b/src/fe-text/textbuffer.h
index 303789a3..2aa22f1a 100644
--- a/src/fe-text/textbuffer.h
+++ b/src/fe-text/textbuffer.h
@@ -65,10 +65,10 @@ typedef struct {
LINE_REC *cur_line;
TEXT_CHUNK_REC *cur_text;
- unsigned int last_eol:1;
int last_fg;
int last_bg;
int last_flags;
+ unsigned int last_eol:1;
} TEXT_BUFFER_REC;
/* Create new buffer */
diff --git a/src/irc/core/channel-events.c b/src/irc/core/channel-events.c
index b0bddab2..46bbd5fa 100644
--- a/src/irc/core/channel-events.c
+++ b/src/irc/core/channel-events.c
@@ -138,7 +138,13 @@ static void channel_change_topic(IRC_SERVER_REC *server, const char *channel,
g_free_not_null(chanrec->topic_by);
chanrec->topic_by = g_strdup(setby);
- chanrec->topic_time = settime;
+ if (chanrec->topic_by == NULL) {
+ /* ensure invariant topic_time > 0 <=> topic_by != NULL.
+ this could be triggered by a topic command without sender */
+ chanrec->topic_time = 0;
+ } else {
+ chanrec->topic_time = settime;
+ }
signal_emit("channel topic changed", 1, chanrec);
}
diff --git a/src/irc/core/irc-cap.c b/src/irc/core/irc-cap.c
index 5464e493..1a60d99b 100644
--- a/src/irc/core/irc-cap.c
+++ b/src/irc/core/irc-cap.c
@@ -36,7 +36,7 @@ int cap_toggle (IRC_SERVER_REC *server, char *cap, int enable)
return TRUE;
}
else if (!enable && gslist_find_string(server->cap_queue, cap)) {
- server->cap_queue = gslist_remove_string(server->cap_queue, cap);
+ server->cap_queue = gslist_delete_string(server->cap_queue, cap, g_free);
return TRUE;
}
@@ -45,7 +45,7 @@ int cap_toggle (IRC_SERVER_REC *server, char *cap, int enable)
if (enable && !gslist_find_string(server->cap_active, cap)) {
/* Make sure the required cap is supported by the server */
- if (!gslist_find_string(server->cap_supported, cap))
+ if (!g_hash_table_lookup_extended(server->cap_supported, cap, NULL, NULL))
return FALSE;
irc_send_cmdv(server, "CAP REQ %s", cap);
@@ -79,61 +79,131 @@ static void cap_emit_signal (IRC_SERVER_REC *server, char *cmd, char *args)
g_free(signal_name);
}
+static gboolean parse_cap_name(char *name, char **key, char **val)
+{
+ const char *eq;
+
+ g_return_val_if_fail(name != NULL, FALSE);
+ g_return_val_if_fail(name[0] != '\0', FALSE);
+
+ eq = strchr(name, '=');
+ /* KEY only value */
+ if (eq == NULL) {
+ *key = g_strdup(name);
+ *val = NULL;
+ /* Some values are in a KEY=VALUE form, parse them */
+ } else {
+ *key = g_strndup(name, (gsize)(eq - name));
+ *val = g_strdup(eq + 1);
+ }
+
+ return TRUE;
+}
+
static void event_cap (IRC_SERVER_REC *server, char *args, char *nick, char *address)
{
GSList *tmp;
GString *cmd;
- char *params, *evt, *list, **caps;
- int i, caps_length, disable, avail_caps;
+ char *params, *evt, *list, *star, **caps;
+ int i, caps_length, disable, avail_caps, multiline;
- params = event_get_params(args, 3, NULL, &evt, &list);
+ params = event_get_params(args, 4, NULL, &evt, &star, &list);
if (params == NULL)
return;
+ /* Multiline responses have an additional parameter and we have to do
+ * this stupid dance to parse them */
+ if (!g_ascii_strcasecmp(evt, "LS") && !strcmp(star, "*")) {
+ multiline = TRUE;
+ }
+ /* This branch covers the '*' parameter isn't present, adjust the
+ * parameter pointer to compensate for this */
+ else if (list[0] == '\0') {
+ multiline = FALSE;
+ list = star;
+ }
+ /* Malformed request, terminate the negotiation */
+ else {
+ cap_finish_negotiation(server);
+ g_warn_if_reached();
+ return;
+ }
+
+ /* The table is created only when needed */
+ if (server->cap_supported == NULL) {
+ server->cap_supported = g_hash_table_new_full(g_str_hash,
+ g_str_equal,
+ g_free, g_free);
+ }
+
/* Strip the trailing whitespaces before splitting the string, some servers send responses with
* superfluous whitespaces that g_strsplit the interprets as tokens */
caps = g_strsplit(g_strchomp(list), " ", -1);
caps_length = g_strv_length(caps);
- if (!g_strcmp0(evt, "LS")) {
+ if (!g_ascii_strcasecmp(evt, "LS")) {
+ if (!server->cap_in_multiline) {
+ /* Throw away everything and start from scratch */
+ g_hash_table_remove_all(server->cap_supported);
+ }
+
+ server->cap_in_multiline = multiline;
+
/* Create a list of the supported caps */
- for (i = 0; i < caps_length; i++)
- server->cap_supported = g_slist_prepend(server->cap_supported, g_strdup(caps[i]));
+ for (i = 0; i < caps_length; i++) {
+ char *key, *val;
- /* Request the required caps, if any */
- if (server->cap_queue == NULL) {
- cap_finish_negotiation(server);
+ if (!parse_cap_name(caps[i], &key, &val)) {
+ g_warning("Invalid CAP %s key/value pair", evt);
+ continue;
+ }
+
+ if (g_hash_table_lookup_extended(server->cap_supported, key, NULL, NULL)) {
+ /* The specification doesn't say anything about
+ * duplicated values, let's just warn the user */
+ g_warning("The server sent the %s capability twice", key);
+ }
+ g_hash_table_insert(server->cap_supported, key, val);
}
- else {
- cmd = g_string_new("CAP REQ :");
- avail_caps = 0;
+ /* A multiline response is always terminated by a normal one,
+ * wait until we receive that one to require any CAP */
+ if (multiline == FALSE) {
+ /* No CAP has been requested */
+ if (server->cap_queue == NULL) {
+ cap_finish_negotiation(server);
+ }
+ else {
+ cmd = g_string_new("CAP REQ :");
+
+ avail_caps = 0;
- /* Check whether the cap is supported by the server */
- for (tmp = server->cap_queue; tmp != NULL; tmp = tmp->next) {
- if (gslist_find_string(server->cap_supported, tmp->data)) {
- if (avail_caps > 0)
- g_string_append_c(cmd, ' ');
- g_string_append(cmd, tmp->data);
+ /* Check whether the cap is supported by the server */
+ for (tmp = server->cap_queue; tmp != NULL; tmp = tmp->next) {
+ if (g_hash_table_lookup_extended(server->cap_supported, tmp->data, NULL, NULL)) {
+ if (avail_caps > 0)
+ g_string_append_c(cmd, ' ');
+ g_string_append(cmd, tmp->data);
- avail_caps++;
+ avail_caps++;
+ }
}
- }
- /* Clear the queue here */
- gslist_free_full(server->cap_queue, (GDestroyNotify) g_free);
- server->cap_queue = NULL;
+ /* Clear the queue here */
+ gslist_free_full(server->cap_queue, (GDestroyNotify) g_free);
+ server->cap_queue = NULL;
- /* If the server doesn't support any cap we requested close the negotiation here */
- if (avail_caps > 0)
- irc_send_cmd_now(server, cmd->str);
- else
- cap_finish_negotiation(server);
+ /* If the server doesn't support any cap we requested close the negotiation here */
+ if (avail_caps > 0)
+ irc_send_cmd_now(server, cmd->str);
+ else
+ cap_finish_negotiation(server);
- g_string_free(cmd, TRUE);
+ g_string_free(cmd, TRUE);
+ }
}
}
- else if (!g_strcmp0(evt, "ACK")) {
+ else if (!g_ascii_strcasecmp(evt, "ACK")) {
int got_sasl = FALSE;
/* Emit a signal for every ack'd cap */
@@ -141,11 +211,11 @@ static void event_cap (IRC_SERVER_REC *server, char *args, char *nick, char *add
disable = (*caps[i] == '-');
if (disable)
- server->cap_active = gslist_remove_string(server->cap_active, caps[i] + 1);
+ server->cap_active = gslist_delete_string(server->cap_active, caps[i] + 1, g_free);
else
server->cap_active = g_slist_prepend(server->cap_active, g_strdup(caps[i]));
- if (!g_strcmp0(caps[i], "sasl"))
+ if (!strcmp(caps[i], "sasl"))
got_sasl = TRUE;
cap_emit_signal(server, "ack", caps[i]);
@@ -157,7 +227,7 @@ static void event_cap (IRC_SERVER_REC *server, char *args, char *nick, char *add
if (got_sasl == FALSE)
cap_finish_negotiation(server);
}
- else if (!g_strcmp0(evt, "NAK")) {
+ else if (!g_ascii_strcasecmp(evt, "NAK")) {
g_warning("The server answered with a NAK to our CAP request, this should not happen");
/* A NAK'd request means that a required cap can't be enabled or disabled, don't update the
@@ -165,6 +235,42 @@ static void event_cap (IRC_SERVER_REC *server, char *args, char *nick, char *add
for (i = 0; i < caps_length; i++)
cap_emit_signal(server, "nak", caps[i]);
}
+ else if (!g_ascii_strcasecmp(evt, "NEW")) {
+ for (i = 0; i < caps_length; i++) {
+ char *key, *val;
+
+ if (!parse_cap_name(caps[i], &key, &val)) {
+ g_warning("Invalid CAP %s key/value pair", evt);
+ continue;
+ }
+
+ g_hash_table_insert(server->cap_supported, key, val);
+ cap_emit_signal(server, "new", key);
+ }
+ }
+ else if (!g_ascii_strcasecmp(evt, "DEL")) {
+ for (i = 0; i < caps_length; i++) {
+ char *key, *val;
+
+ if (!parse_cap_name(caps[i], &key, &val)) {
+ g_warning("Invalid CAP %s key/value pair", evt);
+ continue;
+ }
+
+ g_hash_table_remove(server->cap_supported, key);
+ cap_emit_signal(server, "delete", key);
+ /* The server removed this CAP, remove it from the list
+ * of the active ones if we had requested it */
+ server->cap_active = gslist_delete_string(server->cap_active, key, g_free);
+ /* We don't transfer the ownership of those two
+ * variables this time, just free them when we're done. */
+ g_free(key);
+ g_free(val);
+ }
+ }
+ else {
+ g_warning("Unhandled CAP subcommand %s", evt);
+ }
g_strfreev(caps);
g_free(params);
diff --git a/src/irc/core/irc-nicklist.c b/src/irc/core/irc-nicklist.c
index 1cb1f3e9..3e16db80 100644
--- a/src/irc/core/irc-nicklist.c
+++ b/src/irc/core/irc-nicklist.c
@@ -323,8 +323,9 @@ static void event_nick_invalid(IRC_SERVER_REC *server, const char *data)
static void event_nick_in_use(IRC_SERVER_REC *server, const char *data)
{
- char *str, *cmd;
+ char *str, *cmd, *params, *nick;
int n;
+ gboolean try_alternate_nick;
g_return_if_fail(data != NULL);
@@ -332,11 +333,21 @@ static void event_nick_in_use(IRC_SERVER_REC *server, const char *data)
/* Already connected, no need to handle this anymore. */
return;
}
+
+ try_alternate_nick = g_ascii_strcasecmp(server->nick, server->connrec->nick) == 0 &&
+ server->connrec->alternate_nick != NULL &&
+ g_ascii_strcasecmp(server->connrec->alternate_nick, server->nick) != 0;
+
+ params = event_get_params(data, 2, NULL, &nick);
+ if (g_ascii_strcasecmp(server->nick, nick) != 0) {
+ /* the server uses a nick different from the one we send */
+ g_free(server->nick);
+ server->nick = g_strdup(nick);
+ }
+ g_free(params);
/* nick already in use - need to change it .. */
- if (g_ascii_strcasecmp(server->nick, server->connrec->nick) == 0 &&
- server->connrec->alternate_nick != NULL &&
- g_ascii_strcasecmp(server->connrec->alternate_nick, server->nick) != 0) {
+ if (try_alternate_nick) {
/* first try, so try the alternative nick.. */
g_free(server->nick);
server->nick = g_strdup(server->connrec->alternate_nick);
diff --git a/src/irc/core/irc-servers.c b/src/irc/core/irc-servers.c
index 5076a688..02d971dc 100644
--- a/src/irc/core/irc-servers.c
+++ b/src/irc/core/irc-servers.c
@@ -443,8 +443,10 @@ static void sig_destroyed(IRC_SERVER_REC *server)
gslist_free_full(server->cap_active, (GDestroyNotify) g_free);
server->cap_active = NULL;
- gslist_free_full(server->cap_supported, (GDestroyNotify) g_free);
- server->cap_supported = NULL;
+ if (server->cap_supported) {
+ g_hash_table_destroy(server->cap_supported);
+ server->cap_supported = NULL;
+ }
gslist_free_full(server->cap_queue, (GDestroyNotify) g_free);
server->cap_queue = NULL;
diff --git a/src/irc/core/irc-servers.h b/src/irc/core/irc-servers.h
index 09f3f81d..1374e846 100644
--- a/src/irc/core/irc-servers.h
+++ b/src/irc/core/irc-servers.h
@@ -68,6 +68,7 @@ struct _IRC_SERVER_REC {
unsigned int motd_got:1; /* We've received MOTD */
unsigned int isupport_sent:1; /* Server has sent us an isupport reply */
unsigned int cap_complete:1; /* We've done the initial CAP negotiation */
+ unsigned int cap_in_multiline:1; /* We're waiting for the multiline response to end */
unsigned int sasl_success:1; /* Did we authenticate successfully ? */
int max_kicks_in_cmd; /* max. number of people to kick with one /KICK command */
@@ -75,7 +76,7 @@ struct _IRC_SERVER_REC {
int max_whois_in_cmd; /* max. number of nicks in one /WHOIS command */
int max_msgs_in_cmd; /* max. number of targets in one /MSG */
- GSList *cap_supported; /* A list of caps supported by the server */
+ GHashTable *cap_supported; /* A list of caps supported by the server */
GSList *cap_active; /* A list of caps active for this session */
GSList *cap_queue; /* A list of caps to request on connection */
diff --git a/src/irc/core/sasl.c b/src/irc/core/sasl.c
index 2b589579..c5aa2caa 100644
--- a/src/irc/core/sasl.c
+++ b/src/irc/core/sasl.c
@@ -55,10 +55,21 @@ static gboolean sasl_timeout(IRC_SERVER_REC *server)
return FALSE;
}
+static void sasl_timeout_stop(IRC_SERVER_REC *server)
+{
+ /* Stop any pending timeout, if any */
+ if (server->sasl_timeout != 0) {
+ g_source_remove(server->sasl_timeout);
+ server->sasl_timeout = 0;
+ }
+}
+
static void sasl_start(IRC_SERVER_REC *server, const char *data, const char *from)
{
IRC_SERVER_CONNECT_REC *conn;
+ sasl_timeout_stop(server);
+
conn = server->connrec;
switch (conn->sasl_mechanism) {
@@ -77,11 +88,6 @@ static void sasl_fail(IRC_SERVER_REC *server, const char *data, const char *from
{
char *params, *error;
- /* Stop any pending timeout, if any */
- if (server->sasl_timeout != 0) {
- g_source_remove(server->sasl_timeout);
- server->sasl_timeout = 0;
- }
params = event_get_params(data, 2, NULL, &error);
@@ -97,10 +103,7 @@ static void sasl_fail(IRC_SERVER_REC *server, const char *data, const char *from
static void sasl_already(IRC_SERVER_REC *server, const char *data, const char *from)
{
- if (server->sasl_timeout != 0) {
- g_source_remove(server->sasl_timeout);
- server->sasl_timeout = 0;
- }
+ sasl_timeout_stop(server);
server->sasl_success = TRUE;
@@ -112,10 +115,7 @@ static void sasl_already(IRC_SERVER_REC *server, const char *data, const char *f
static void sasl_success(IRC_SERVER_REC *server, const char *data, const char *from)
{
- if (server->sasl_timeout != 0) {
- g_source_remove(server->sasl_timeout);
- server->sasl_timeout = 0;
- }
+ sasl_timeout_stop(server);
server->sasl_success = TRUE;
@@ -265,7 +265,7 @@ static void sasl_step_fail(IRC_SERVER_REC *server)
irc_send_cmd_now(server, "AUTHENTICATE *");
cap_finish_negotiation(server);
- server->sasl_timeout = 0;
+ sasl_timeout_stop(server);
signal_emit("server sasl failure", 2, server, "The server sent an invalid payload");
}
@@ -274,11 +274,7 @@ static void sasl_step(IRC_SERVER_REC *server, const char *data, const char *from
{
GString *req = NULL;
- /* Stop the timer */
- if (server->sasl_timeout != 0) {
- g_source_remove(server->sasl_timeout);
- server->sasl_timeout = 0;
- }
+ sasl_timeout_stop(server);
if (!sasl_reassemble_incoming(server, data, &req)) {
sasl_step_fail(server);
@@ -302,10 +298,7 @@ static void sasl_disconnected(IRC_SERVER_REC *server)
return;
}
- if (server->sasl_timeout != 0) {
- g_source_remove(server->sasl_timeout);
- server->sasl_timeout = 0;
- }
+ sasl_timeout_stop(server);
}
void sasl_init(void)
diff --git a/src/irc/dcc/dcc-get.c b/src/irc/dcc/dcc-get.c
index cecbb076..ee8b9a49 100644
--- a/src/irc/dcc/dcc-get.c
+++ b/src/irc/dcc/dcc-get.c
@@ -237,8 +237,12 @@ void sig_dccget_connected(GET_DCC_REC *dcc)
if (temphandle == -1)
ret = -1;
- else
- ret = fchmod(temphandle, dcc_file_create_mode);
+ else {
+ if (fchmod(temphandle, dcc_file_create_mode) != 0)
+ g_warning("fchmod(3) failed: %s", strerror(errno));
+ /* proceed even if chmod fails */
+ ret = 0;
+ }
close(temphandle);
@@ -249,7 +253,7 @@ void sig_dccget_connected(GET_DCC_REC *dcc)
/* Linux */
(errno == EPERM ||
/* FUSE */
- errno == ENOSYS ||
+ errno == ENOSYS || errno == EACCES ||
/* BSD */
errno == EOPNOTSUPP)) {
/* hard links aren't supported - some people
diff --git a/src/perl/irc/Irc.xs b/src/perl/irc/Irc.xs
index 41690010..3bf81f9a 100644
--- a/src/perl/irc/Irc.xs
+++ b/src/perl/irc/Irc.xs
@@ -12,7 +12,10 @@ static void perl_irc_connect_fill_hash(HV *hv, IRC_SERVER_CONNECT_REC *conn)
static void perl_irc_server_fill_hash(HV *hv, IRC_SERVER_REC *server)
{
AV *av;
+ HV *hv_;
GSList *tmp;
+ GHashTableIter iter;
+ gpointer key_, val_;
perl_irc_connect_fill_hash(hv, server->connrec);
perl_server_fill_hash(hv, (SERVER_REC *) server);
@@ -34,10 +37,16 @@ static void perl_irc_server_fill_hash(HV *hv, IRC_SERVER_REC *server)
(void) hv_store(hv, "cap_complete", 12, newSViv(server->cap_complete), 0);
(void) hv_store(hv, "sasl_success", 12, newSViv(server->sasl_success), 0);
- av = newAV();
- for (tmp = server->cap_supported; tmp != NULL; tmp = tmp->next)
- av_push(av, new_pv(tmp->data));
- (void) hv_store(hv, "cap_supported", 13, newRV_noinc((SV*)av), 0);
+ if (server->cap_supported != NULL) {
+ hv_ = newHV();
+ g_hash_table_iter_init(&iter, server->cap_supported);
+ while (g_hash_table_iter_next(&iter, &key_, &val_)) {
+ char *key = (char *)key_;
+ char *val = (char *)val_;
+ hv_store(hv_, key, strlen(key), new_pv(val), 0);
+ }
+ (void) hv_store(hv, "cap_supported", 13, newRV_noinc((SV*)hv_), 0);
+ }
av = newAV();
for (tmp = server->cap_active; tmp != NULL; tmp = tmp->next)
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: