aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/interface-curses/interface.c
diff options
context:
space:
mode:
authorRobin Haberkorn <rhaberkorn@fmsbw.de>2026-01-26 19:50:03 +0100
committerRobin Haberkorn <rhaberkorn@fmsbw.de>2026-01-26 19:50:03 +0100
commit7fa9feae452ea05a083b17d3f0cdf69bf5d37822 (patch)
tree080b0d24fad4bceee74cae6a41a56db434aa930d /src/interface-curses/interface.c
parent59d3c24f198ca4628fb69c8da163f103bafebc43 (diff)
Curses: support up to 32767 color pairsHEADmaster-fmsbw-cimaster
* Instead of ORing COLOR_PAIR() into attributes, always pass separate `pair` arguments. Since they are `short` this allows for up to 32767 color pairs. Previously, we could only count on 256 color pairs (or 128 for SciTECO and Scinterm each). * Analoguous changes have been made to Scinterm. See also https://github.com/orbitalquark/scinterm/pull/37 Since it only correctly checks for overflows at the end of the color pair space, we allocate the second half of that space to Scinterm. It is now very unlikely to overflow the color pair space, though. * This wasn't critical of course since even the 128 pairs would be unlikely to exhaust as long as we support only 16 ANSI colors. Scinterm however supports arbitrary RGB colors and we might want to do so soon as well.
Diffstat (limited to 'src/interface-curses/interface.c')
-rw-r--r--src/interface-curses/interface.c100
1 files changed, 46 insertions, 54 deletions
diff --git a/src/interface-curses/interface.c b/src/interface-curses/interface.c
index 365a096..4f00bcf 100644
--- a/src/interface-curses/interface.c
+++ b/src/interface-curses/interface.c
@@ -233,63 +233,49 @@ static struct {
* Scinterm no longer initializes all color pairs for all combinations of
* the builtin foreground and background colors.
* Since curses does not guarantee any number of color pairs, we cannot do that either.
- * Instead we allocate color pairs beginnig at the second half of
- * color pair space on demand (similar to what Scinterm does).
- *
- * @note Scinterm now also has scintilla_set_color_offsets(),
- * so we could use the color pairs beginning with 1 as well.
+ * Instead we allocate color pairs in the first half of
+ * color pair space on demand, while the second half is reserved to Scinterm.
*
+ * @param attr attributes to modify (for supporting monochrome terminals)
* @param fg curses foreground color
* @param bg curses background color
* @return curses color pair number
*/
static gshort
-teco_color_pair(gshort fg, gshort bg)
+teco_color_pair(attr_t *attr, gshort fg, gshort bg)
{
- static gshort allocated_pairs = 0;
+ static gshort next_pair = 1;
+
+ /*
+ * Basic support for monochrome terminals:
+ * Every background, that is not black is assumed to be a
+ * dark-on-bright area, rendered in reverse.
+ * This will at least work with the terminal.tes and contrast.tes
+ * color schemes.
+ */
+ if (!has_colors()) {
+ if (bg != COLOR_BLACK)
+ *attr |= A_REVERSE;
+ return 0;
+ }
G_STATIC_ASSERT(sizeof(gshort)*2 <= sizeof(guint));
gpointer key = GUINT_TO_POINTER(((guint)fg << 8*sizeof(bg)) | bg);
gpointer value = g_hash_table_lookup(teco_interface.pair_table, key);
if (G_LIKELY(value != NULL))
return GPOINTER_TO_UINT(value);
- /*
- * We are still adding colors with COLOR_PAIR(), which allows
- * at most 256 color pairs (1-255).
- */
- gshort new_pair = MIN(COLOR_PAIRS, 256)/2 + allocated_pairs;
- if (G_UNLIKELY(new_pair >= 256))
+ if (G_UNLIKELY(next_pair >= MIN(COLOR_PAIRS, G_MAXSHORT)))
return 0;
- allocated_pairs++;
- init_pair(new_pair, fg, bg);
- g_hash_table_insert(teco_interface.pair_table, key, GUINT_TO_POINTER(new_pair));
- return new_pair;
+ init_pair(next_pair, fg, bg);
+ g_hash_table_insert(teco_interface.pair_table, key, GUINT_TO_POINTER(next_pair));
+ return next_pair++;
}
-/**
- * Curses attribute for the color combination
- * according to the color pairs initialized by
- * Scinterm.
- * This is equivalent to Scinterm's internal term_color_attr().
- *
- * @param fg foreground color
- * @param bg background color
- * @return curses attribute
- */
-static inline attr_t
-teco_color_attr(gshort fg, gshort bg)
+static inline gint
+teco_wattr_set(WINDOW *win, attr_t attr, gshort fg, gshort bg)
{
- if (has_colors())
- return COLOR_PAIR(teco_color_pair(fg, bg));
-
- /*
- * Basic support for monochrome terminals:
- * Every background, that is not black is assumed to be a
- * dark-on-bright area, rendered in reverse.
- * This will at least work with the terminal.tes and contrast.tes
- * color schemes.
- */
- return bg != COLOR_BLACK ? A_REVERSE : 0;
+ gshort pair = teco_color_pair(&attr, fg, bg);
+ return wattr_set(win, attr, pair, NULL);
}
/**
@@ -760,6 +746,8 @@ teco_interface_init_interactive(GError **error)
teco_interface.pair_table = g_hash_table_new(g_direct_hash, g_direct_equal);
start_color();
+ /* Scinterm uses shorts for color pairs, so G_MAXSHORT is the maximum */
+ scintilla_set_color_offsets(0, MIN(COLOR_PAIRS, G_MAXSHORT)/2);
/*
* On UNIX terminals, the escape key is usually
@@ -950,7 +938,7 @@ teco_interface_msg_literal(teco_msg_t type, const gchar *str, gsize len)
teco_interface_stdio_msg(type, str, len);
#endif
- short fg, bg;
+ gshort fg, bg;
fg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETBACK, STYLE_DEFAULT, 0));
@@ -972,7 +960,7 @@ teco_interface_msg_literal(teco_msg_t type, const gchar *str, gsize len)
}
wmove(teco_interface.msg_window, 0, 0);
- wattrset(teco_interface.msg_window, teco_color_attr(fg, bg));
+ teco_wattr_set(teco_interface.msg_window, 0, fg, bg);
teco_curses_format_str(teco_interface.msg_window, str, len, -1);
teco_curses_clrtobot(teco_interface.msg_window);
}
@@ -983,11 +971,11 @@ teco_interface_msg_clear(void)
if (!teco_interface.input_pad) /* batch mode */
return;
- short fg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETBACK, STYLE_DEFAULT, 0));
- short bg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETFORE, STYLE_DEFAULT, 0));
+ gshort fg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETBACK, STYLE_DEFAULT, 0));
+ gshort bg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETFORE, STYLE_DEFAULT, 0));
wmove(teco_interface.msg_window, 0, 0);
- wattrset(teco_interface.msg_window, teco_color_attr(fg, bg));
+ teco_wattr_set(teco_interface.msg_window, 0, fg, bg);
teco_curses_clrtobot(teco_interface.msg_window);
}
@@ -1161,11 +1149,11 @@ teco_interface_draw_info(void)
* the current buffer's STYLE_DEFAULT.
* The same style is used for MSG_USER messages.
*/
- short fg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETBACK, STYLE_DEFAULT, 0));
- short bg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETFORE, STYLE_DEFAULT, 0));
+ gshort fg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETBACK, STYLE_DEFAULT, 0));
+ gshort bg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETFORE, STYLE_DEFAULT, 0));
wmove(teco_interface.info_window, 0, 0);
- wattrset(teco_interface.info_window, teco_color_attr(fg, bg));
+ teco_wattr_set(teco_interface.info_window, 0, fg, bg);
const gchar *info_type_str;
@@ -1734,11 +1722,13 @@ teco_interface_popup_show(gsize prefix_len)
/* batch mode */
return;
- short fg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETFORE, STYLE_CALLTIP, 0));
- short bg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETBACK, STYLE_CALLTIP, 0));
+ gshort fg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETFORE, STYLE_CALLTIP, 0));
+ gshort bg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETBACK, STYLE_CALLTIP, 0));
teco_interface.popup_prefix_len = prefix_len;
- teco_curses_info_popup_show(&teco_interface.popup, teco_color_attr(fg, bg));
+ attr_t attr = 0;
+ gshort pair = teco_color_pair(&attr, fg, bg);
+ teco_curses_info_popup_show(&teco_interface.popup, attr, pair);
}
void
@@ -1938,9 +1928,11 @@ teco_interface_process_mevent(MEVENT *event, GError **error)
else if (event->bstate & BUTTON_NUM(5))
teco_curses_info_popup_scroll(&teco_interface.popup, +2);
- short fg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETFORE, STYLE_CALLTIP, 0));
- short bg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETBACK, STYLE_CALLTIP, 0));
- teco_curses_info_popup_show(&teco_interface.popup, teco_color_attr(fg, bg));
+ gshort fg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETFORE, STYLE_CALLTIP, 0));
+ gshort bg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETBACK, STYLE_CALLTIP, 0));
+ attr_t attr = 0;
+ gshort pair = teco_color_pair(&attr, fg, bg);
+ teco_curses_info_popup_show(&teco_interface.popup, attr, pair);
return TRUE;
}