aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorRobin Haberkorn <robin.haberkorn@googlemail.com>2025-03-16 15:13:39 +0300
committerRobin Haberkorn <robin.haberkorn@googlemail.com>2025-03-16 15:13:39 +0300
commit327d749ce03d25897447ec36ed4c46c0da4a72cb (patch)
tree198eeb70efeb62386a9735ee9a26230a16085f3b /src
parent24b08dac99804bed30824e9becb6f773d5db1874 (diff)
downloadsciteco-327d749ce03d25897447ec36ed4c46c0da4a72cb.tar.gz
further improved monochrome terminal support: fixed reverse text on reverse backgrounds
* Unfortunately we cannot use `wbkgdset(win, A_REVERSE)` if we plan to use reverse text on this background, i.e. if we want to cancel out the background A_REVERSE. * SciTECO therefore no longer uses background attributes, but only foreground attributes. When setting a reverse text, we XOR A_REVERSE into the previous attributes. * This fixes control characters especially in the info line and popups, as well as rendering of the popup scroll bars. * The command-line should now be rendered properly even on a dark-on-bright color theme (which does not yet exist).
Diffstat (limited to 'src')
-rw-r--r--src/interface-curses/curses-info-popup.c17
-rw-r--r--src/interface-curses/curses-utils.c34
-rw-r--r--src/interface-curses/curses-utils.h17
-rw-r--r--src/interface-curses/interface.c32
4 files changed, 72 insertions, 28 deletions
diff --git a/src/interface-curses/curses-info-popup.c b/src/interface-curses/curses-info-popup.c
index dffbcf8..332d434 100644
--- a/src/interface-curses/curses-info-popup.c
+++ b/src/interface-curses/curses-info-popup.c
@@ -98,7 +98,13 @@ teco_curses_info_popup_init_pad(teco_curses_info_popup_t *ctx, attr_t attr)
*/
ctx->pad = newpad(pad_lines, cols - 2);
- wbkgd(ctx->pad, ' ' | attr);
+ /*
+ * NOTE: attr could contain A_REVERSE on monochrome terminals,
+ * so we use foreground attributes instead of background attributes.
+ * This way, we can cancel out the A_REVERSE if necessary.
+ */
+ wattrset(ctx->pad, attr);
+ teco_curses_clrtobot(ctx->pad);
/*
* cur_col is the row currently written.
@@ -113,7 +119,8 @@ teco_curses_info_popup_init_pad(teco_curses_info_popup_t *ctx, attr_t attr)
wmove(ctx->pad, cur_line-1,
(cur_col % pad_cols)*pad_colwidth);
- wattrset(ctx->pad, entry->highlight ? A_BOLD : A_NORMAL);
+ if (entry->highlight)
+ wattron(ctx->pad, A_BOLD);
switch (entry->type) {
case TECO_POPUP_FILE:
@@ -137,6 +144,8 @@ teco_curses_info_popup_init_pad(teco_curses_info_popup_t *ctx, attr_t attr)
break;
}
+ wattroff(ctx->pad, A_BOLD);
+
cur_col++;
}
}
@@ -167,7 +176,7 @@ teco_curses_info_popup_show(teco_curses_info_popup_t *ctx, attr_t attr)
/* window covers message, scintilla and info windows */
ctx->window = newwin(popup_lines, 0, lines - 1 - popup_lines, 0);
- wbkgdset(ctx->window, ' ' | attr);
+ wattrset(ctx->window, attr);
wborder(ctx->window,
ACS_VLINE,
@@ -198,7 +207,7 @@ teco_curses_info_popup_show(teco_curses_info_popup_t *ctx, attr_t attr)
* Instead, simply draw reverse blanks.
*/
wmove(ctx->window, bar_y, cols-1);
- wattron(ctx->window, A_REVERSE);
+ wattrset(ctx->window, attr ^ A_REVERSE);
wvline(ctx->window, ' ', bar_height);
}
diff --git a/src/interface-curses/curses-utils.c b/src/interface-curses/curses-utils.c
index 3fd680b..f94b6dc 100644
--- a/src/interface-curses/curses-utils.c
+++ b/src/interface-curses/curses-utils.c
@@ -48,9 +48,18 @@ guint
teco_curses_format_str(WINDOW *win, const gchar *str, gsize len, gint max_width)
{
gint truncate_len = teco_ed & TECO_ED_ICONS ? 1 : 3;
- int old_x, old_y;
gint chars_added = 0;
+ /*
+ * The entire background might be in reverse, especially
+ * on monochrome terminals.
+ * In those cases, we have to __remove__ the A_REVERSE flag.
+ */
+ attr_t attrs = A_NORMAL;
+ short pair = 0;
+ wattr_get(win, &attrs, &pair, NULL);
+
+ int old_x, old_y;
getyx(win, old_y, old_x);
if (max_width < 0)
@@ -72,37 +81,38 @@ teco_curses_format_str(WINDOW *win, const gchar *str, gsize len, gint max_width)
chars_added++;
if (chars_added > max_width)
goto truncate;
- waddch(win, '$' | A_REVERSE);
+ wattr_set(win, attrs ^ A_REVERSE, pair, NULL);
+ waddch(win, '$');
break;
case '\r':
chars_added += 2;
if (chars_added > max_width)
goto truncate;
- waddch(win, 'C' | A_REVERSE);
- waddch(win, 'R' | A_REVERSE);
+ wattr_set(win, attrs ^ A_REVERSE, pair, NULL);
+ waddstr(win, "CR");
break;
case '\n':
chars_added += 2;
if (chars_added > max_width)
goto truncate;
- waddch(win, 'L' | A_REVERSE);
- waddch(win, 'F' | A_REVERSE);
+ wattr_set(win, attrs ^ A_REVERSE, pair, NULL);
+ waddstr(win, "LF");
break;
case '\t':
chars_added += 3;
if (chars_added > max_width)
goto truncate;
- waddch(win, 'T' | A_REVERSE);
- waddch(win, 'A' | A_REVERSE);
- waddch(win, 'B' | A_REVERSE);
+ wattr_set(win, attrs ^ A_REVERSE, pair, NULL);
+ waddstr(win, "TAB");
break;
default:
if (TECO_IS_CTL(*str)) {
chars_added += 2;
if (chars_added > max_width)
goto truncate;
- waddch(win, '^' | A_REVERSE);
- waddch(win, TECO_CTL_ECHO(*str) | A_REVERSE);
+ wattr_set(win, attrs ^ A_REVERSE, pair, NULL);
+ waddch(win, '^');
+ waddch(win, TECO_CTL_ECHO(*str));
} else {
chars_added++;
if (chars_added > max_width)
@@ -116,6 +126,8 @@ teco_curses_format_str(WINDOW *win, const gchar *str, gsize len, gint max_width)
waddnstr(win, str, clen);
}
}
+ /* restore original state of A_REVERSE */
+ wattr_set(win, attrs, pair, NULL);
str += clen;
len -= clen;
diff --git a/src/interface-curses/curses-utils.h b/src/interface-curses/curses-utils.h
index 9f2e8f3..18cdd3d 100644
--- a/src/interface-curses/curses-utils.h
+++ b/src/interface-curses/curses-utils.h
@@ -34,3 +34,20 @@ teco_curses_add_wc(WINDOW *win, gunichar chr)
gchar buf[6];
waddnstr(win, buf, g_unichar_to_utf8(chr, buf));
}
+
+/**
+ * Clear from the current position until the end of the given
+ * curses window with the current \b foreground attributes.
+ * This is similar to wclrtobot(), but does not use the
+ * background attributes.
+ */
+static inline void
+teco_curses_clrtobot(WINDOW *win)
+{
+ int max_x, max_y;
+ getmaxyx(win, max_y, max_x);
+ if (getcurx(win)+1 < max_x)
+ whline(win, ' ', max_x - getcurx(win));
+ for (int y = getcury(win)+1; y <= max_y; y++)
+ mvwhline(win, y, 0, ' ', max_x);
+}
diff --git a/src/interface-curses/interface.c b/src/interface-curses/interface.c
index 2e87601..75ba036 100644
--- a/src/interface-curses/interface.c
+++ b/src/interface-curses/interface.c
@@ -899,8 +899,12 @@ teco_interface_vmsg(teco_msg_t type, const gchar *fmt, va_list ap)
break;
}
+ /*
+ * NOTE: This is safe since we don't have to cancel out any A_REVERSE,
+ * that could be set in the background attributes.
+ */
wmove(teco_interface.msg_window, 0, 0);
- wbkgdset(teco_interface.msg_window, ' ' | teco_color_attr(fg, bg));
+ wbkgdset(teco_interface.msg_window, teco_color_attr(fg, bg));
vw_printw(teco_interface.msg_window, fmt, ap);
wclrtoeol(teco_interface.msg_window);
}
@@ -914,7 +918,7 @@ teco_interface_msg_clear(void)
short fg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETBACK, STYLE_DEFAULT, 0));
short bg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETFORE, STYLE_DEFAULT, 0));
- wbkgdset(teco_interface.msg_window, ' ' | teco_color_attr(fg, bg));
+ wbkgdset(teco_interface.msg_window, teco_color_attr(fg, bg));
werase(teco_interface.msg_window);
}
@@ -1026,7 +1030,7 @@ teco_interface_draw_info(void)
short bg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETFORE, STYLE_DEFAULT, 0));
wmove(teco_interface.info_window, 0, 0);
- wbkgdset(teco_interface.info_window, ' ' | teco_color_attr(fg, bg));
+ wattrset(teco_interface.info_window, teco_color_attr(fg, bg));
const gchar *info_type_str;
@@ -1061,7 +1065,7 @@ teco_interface_draw_info(void)
g_assert_not_reached();
}
- wclrtoeol(teco_interface.info_window);
+ teco_curses_clrtobot(teco_interface.info_window);
/*
* Make sure the title will consist only of printable characters.
@@ -1125,7 +1129,7 @@ teco_interface_cmdline_update(const teco_cmdline_t *cmdline)
short fg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETFORE, STYLE_DEFAULT, 0));
short bg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETBACK, STYLE_DEFAULT, 0));
- wcolor_set(teco_interface.cmdline_pad, teco_color_pair(fg, bg), NULL);
+ wattrset(teco_interface.cmdline_pad, teco_color_attr(fg, bg));
/* format effective command line */
teco_interface.cmdline_len =
@@ -1141,6 +1145,8 @@ teco_interface_cmdline_update(const teco_cmdline_t *cmdline)
* as command line, since we can then define a style
* for rubbed out parts of the command line which will
* be user-configurable.
+ * The attributes, supported by the terminal can theoretically
+ * be queried with term_attrs().
*/
wattron(teco_interface.cmdline_pad, A_UNDERLINE | A_BOLD);
@@ -1157,18 +1163,18 @@ teco_interface_cmdline_update(const teco_cmdline_t *cmdline)
* Highlight cursor after effective command line
* FIXME: This should use SCI_GETCARETFORE().
*/
+ attr_t attr = A_NORMAL;
+ short pair = 0;
if (teco_interface.cmdline_rubout_len) {
- attr_t attr = 0;
- short pair = 0;
-
wmove(teco_interface.cmdline_pad, 0, teco_interface.cmdline_len);
wattr_get(teco_interface.cmdline_pad, &attr, &pair, NULL);
wchgat(teco_interface.cmdline_pad, 1,
- (attr & A_UNDERLINE) | A_REVERSE, pair, NULL);
+ (attr & (A_UNDERLINE | A_REVERSE)) ^ A_REVERSE, pair, NULL);
} else {
teco_interface.cmdline_len++;
- wattroff(teco_interface.cmdline_pad, A_UNDERLINE | A_BOLD);
- waddch(teco_interface.cmdline_pad, ' ' | A_REVERSE);
+ wattr_get(teco_interface.cmdline_pad, &attr, &pair, NULL);
+ wattr_set(teco_interface.cmdline_pad, (attr & ~(A_UNDERLINE | A_BOLD)) ^ A_REVERSE, pair, NULL);
+ waddch(teco_interface.cmdline_pad, ' ');
}
teco_interface_draw_cmdline();
@@ -1196,9 +1202,9 @@ teco_interface_draw_cmdline(void)
short fg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETFORE, STYLE_DEFAULT, 0));
short bg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETBACK, STYLE_DEFAULT, 0));
- wbkgdset(teco_interface.cmdline_window, ' ' | teco_color_attr(fg, bg));
- werase(teco_interface.cmdline_window);
+ wattrset(teco_interface.cmdline_window, teco_color_attr(fg, bg));
mvwaddch(teco_interface.cmdline_window, 0, 0, '*' | A_BOLD);
+ teco_curses_clrtobot(teco_interface.cmdline_window);
copywin(teco_interface.cmdline_pad, teco_interface.cmdline_window,
0, disp_offset, 0, 1, 0, disp_len, FALSE);
}