diff options
author | Robin Haberkorn <robin.haberkorn@googlemail.com> | 2025-03-16 15:13:39 +0300 |
---|---|---|
committer | Robin Haberkorn <robin.haberkorn@googlemail.com> | 2025-03-16 15:13:39 +0300 |
commit | 327d749ce03d25897447ec36ed4c46c0da4a72cb (patch) | |
tree | 198eeb70efeb62386a9735ee9a26230a16085f3b /src/interface-curses | |
parent | 24b08dac99804bed30824e9becb6f773d5db1874 (diff) | |
download | sciteco-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/interface-curses')
-rw-r--r-- | src/interface-curses/curses-info-popup.c | 17 | ||||
-rw-r--r-- | src/interface-curses/curses-utils.c | 34 | ||||
-rw-r--r-- | src/interface-curses/curses-utils.h | 17 | ||||
-rw-r--r-- | src/interface-curses/interface.c | 32 |
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); } |