diff options
Diffstat (limited to 'src/interface-curses')
| -rw-r--r-- | src/interface-curses/curses-icons.c | 6 | ||||
| -rw-r--r-- | src/interface-curses/curses-icons.h | 2 | ||||
| -rw-r--r-- | src/interface-curses/curses-info-popup.c | 54 | ||||
| -rw-r--r-- | src/interface-curses/curses-info-popup.h | 4 | ||||
| -rw-r--r-- | src/interface-curses/curses-utils.c | 18 | ||||
| -rw-r--r-- | src/interface-curses/curses-utils.h | 14 | ||||
| -rw-r--r-- | src/interface-curses/interface.c | 244 |
7 files changed, 211 insertions, 131 deletions
diff --git a/src/interface-curses/curses-icons.c b/src/interface-curses/curses-icons.c index 7c021d6..0e14655 100644 --- a/src/interface-curses/curses-icons.c +++ b/src/interface-curses/curses-icons.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2025 Robin Haberkorn + * Copyright (C) 2012-2026 Robin Haberkorn * * 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 @@ -362,6 +362,10 @@ teco_curses_icon_cmp(const void *a, const void *b) gunichar teco_curses_icons_lookup_file(const gchar *filename) { + if (!filename || !*filename) + /* "(Unnamed)" file */ + return 0xf1036; /* */ + g_autofree gchar *basename = g_path_get_basename(filename); const teco_curses_icon_t *icon; diff --git a/src/interface-curses/curses-icons.h b/src/interface-curses/curses-icons.h index fce9d75..a12fe88 100644 --- a/src/interface-curses/curses-icons.h +++ b/src/interface-curses/curses-icons.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2025 Robin Haberkorn + * Copyright (C) 2012-2026 Robin Haberkorn * * 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 diff --git a/src/interface-curses/curses-info-popup.c b/src/interface-curses/curses-info-popup.c index c51a99b..83d4665 100644 --- a/src/interface-curses/curses-info-popup.c +++ b/src/interface-curses/curses-info-popup.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2025 Robin Haberkorn + * Copyright (C) 2012-2026 Robin Haberkorn * * 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 @@ -19,6 +19,8 @@ #include "config.h" #endif +#include <string.h> + #include <glib.h> #include <curses.h> @@ -38,6 +40,7 @@ typedef struct { teco_stailq_entry_t entry; teco_popup_entry_type_t type; + /** entry name or empty string for the "(Unnamed)" buffer */ teco_string_t name; gboolean highlight; } teco_popup_entry_t; @@ -70,7 +73,7 @@ teco_curses_info_popup_add(teco_curses_info_popup_t *ctx, teco_popup_entry_type_ } static void -teco_curses_info_popup_init_pad(teco_curses_info_popup_t *ctx, attr_t attr) +teco_curses_info_popup_init_pad(teco_curses_info_popup_t *ctx, attr_t attr, gshort pair) { int pad_lines; /**! pad height */ gint pad_cols; /**! entry columns */ @@ -99,11 +102,11 @@ teco_curses_info_popup_init_pad(teco_curses_info_popup_t *ctx, attr_t attr) ctx->pad = newpad(pad_lines, COLS - 2); /* - * NOTE: attr could contain A_REVERSE on monochrome terminals, + * NOTE: attr could contain WA_REVERSE on monochrome terminals, * so we use foreground attributes instead of background attributes. - * This way, we can cancel out the A_REVERSE if necessary. + * This way, we can cancel out the WA_REVERSE if necessary. */ - wattrset(ctx->pad, attr); + wattr_set(ctx->pad, attr, pair, NULL); teco_curses_clrtobot(ctx->pad); /* @@ -122,25 +125,32 @@ teco_curses_info_popup_init_pad(teco_curses_info_popup_t *ctx, attr_t attr) if (entry->highlight) wattron(ctx->pad, A_BOLD); + teco_string_t name = entry->name; + if (!name.len) { + name.data = TECO_UNNAMED_FILE; + name.len = strlen(name.data); + } + switch (entry->type) { case TECO_POPUP_FILE: - g_assert(!teco_string_contains(&entry->name, '\0')); + g_assert(!teco_string_contains(name, '\0')); if (teco_ed & TECO_ED_ICONS) { + /* "(Unnamed)" buffer is looked up as "" */ teco_curses_add_wc(ctx->pad, teco_curses_icons_lookup_file(entry->name.data)); waddch(ctx->pad, ' '); } - teco_curses_format_filename(ctx->pad, entry->name.data, -1); + teco_curses_format_filename(ctx->pad, name.data, -1); break; case TECO_POPUP_DIRECTORY: - g_assert(!teco_string_contains(&entry->name, '\0')); + g_assert(!teco_string_contains(name, '\0')); if (teco_ed & TECO_ED_ICONS) { teco_curses_add_wc(ctx->pad, teco_curses_icons_lookup_dir(entry->name.data)); waddch(ctx->pad, ' '); } - teco_curses_format_filename(ctx->pad, entry->name.data, -1); + teco_curses_format_filename(ctx->pad, name.data, -1); break; default: - teco_curses_format_str(ctx->pad, entry->name.data, entry->name.len, -1); + teco_curses_format_str(ctx->pad, name.data, name.len, -1); break; } @@ -151,7 +161,7 @@ teco_curses_info_popup_init_pad(teco_curses_info_popup_t *ctx, attr_t attr) } void -teco_curses_info_popup_show(teco_curses_info_popup_t *ctx, attr_t attr) +teco_curses_info_popup_show(teco_curses_info_popup_t *ctx, attr_t attr, gshort pair) { if (!ctx->length) /* nothing to display */ @@ -161,7 +171,7 @@ teco_curses_info_popup_show(teco_curses_info_popup_t *ctx, attr_t attr) delwin(ctx->window); if (!ctx->pad) - teco_curses_info_popup_init_pad(ctx, attr); + teco_curses_info_popup_init_pad(ctx, attr, pair); gint pad_lines = getmaxy(ctx->pad); /* @@ -173,15 +183,17 @@ 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 - teco_cmdline.height - popup_lines, 0); - wattrset(ctx->window, attr); + wattr_set(ctx->window, attr, pair, NULL); - wborder(ctx->window, - ACS_VLINE, - ACS_VLINE, /* may be overwritten with scrollbar */ - ACS_HLINE, - ' ', /* no bottom line */ - ACS_ULCORNER, ACS_URCORNER, - ACS_VLINE, ACS_VLINE); + /* + * NOTE: wborder() is broken for large pair numbers, at least on ncurses. + */ + waddch(ctx->window, ACS_ULCORNER); + whline(ctx->window, ACS_HLINE, COLS - 2); + mvwaddch(ctx->window, 0, COLS - 1, ACS_URCORNER); + mvwvline(ctx->window, 1, 0, ACS_VLINE, getmaxy(ctx->window)-1); + /* may be overwritten with scrollbar */ + mvwvline(ctx->window, 1, COLS - 1, ACS_VLINE, getmaxy(ctx->window)-1); copywin(ctx->pad, ctx->window, ctx->pad_first_line, 0, @@ -204,7 +216,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); - wattrset(ctx->window, attr ^ A_REVERSE); + wattr_set(ctx->window, attr ^ WA_REVERSE, pair, NULL); wvline(ctx->window, ' ', bar_height); } diff --git a/src/interface-curses/curses-info-popup.h b/src/interface-curses/curses-info-popup.h index d845b29..898ba70 100644 --- a/src/interface-curses/curses-info-popup.h +++ b/src/interface-curses/curses-info-popup.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2025 Robin Haberkorn + * Copyright (C) 2012-2026 Robin Haberkorn * * 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 @@ -49,7 +49,7 @@ teco_curses_info_popup_init(teco_curses_info_popup_t *ctx) void teco_curses_info_popup_add(teco_curses_info_popup_t *ctx, teco_popup_entry_type_t type, const gchar *name, gsize name_len, gboolean highlight); -void teco_curses_info_popup_show(teco_curses_info_popup_t *ctx, attr_t attr); +void teco_curses_info_popup_show(teco_curses_info_popup_t *ctx, attr_t attr, gshort pair); const teco_string_t *teco_curses_info_popup_getentry(teco_curses_info_popup_t *ctx, gint y, gint x); void teco_curses_info_popup_scroll_page(teco_curses_info_popup_t *ctx); void teco_curses_info_popup_scroll(teco_curses_info_popup_t *ctx, gint delta); diff --git a/src/interface-curses/curses-utils.c b/src/interface-curses/curses-utils.c index f94b6dc..3b25d56 100644 --- a/src/interface-curses/curses-utils.c +++ b/src/interface-curses/curses-utils.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2025 Robin Haberkorn + * Copyright (C) 2012-2026 Robin Haberkorn * * 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 @@ -53,9 +53,9 @@ teco_curses_format_str(WINDOW *win, const gchar *str, gsize len, gint max_width) /* * The entire background might be in reverse, especially * on monochrome terminals. - * In those cases, we have to __remove__ the A_REVERSE flag. + * In those cases, we have to __remove__ the WA_REVERSE flag. */ - attr_t attrs = A_NORMAL; + attr_t attrs = WA_NORMAL; short pair = 0; wattr_get(win, &attrs, &pair, NULL); @@ -81,28 +81,28 @@ teco_curses_format_str(WINDOW *win, const gchar *str, gsize len, gint max_width) chars_added++; if (chars_added > max_width) goto truncate; - wattr_set(win, attrs ^ A_REVERSE, pair, NULL); + wattr_set(win, attrs ^ WA_REVERSE, pair, NULL); waddch(win, '$'); break; case '\r': chars_added += 2; if (chars_added > max_width) goto truncate; - wattr_set(win, attrs ^ A_REVERSE, pair, NULL); + wattr_set(win, attrs ^ WA_REVERSE, pair, NULL); waddstr(win, "CR"); break; case '\n': chars_added += 2; if (chars_added > max_width) goto truncate; - wattr_set(win, attrs ^ A_REVERSE, pair, NULL); + wattr_set(win, attrs ^ WA_REVERSE, pair, NULL); waddstr(win, "LF"); break; case '\t': chars_added += 3; if (chars_added > max_width) goto truncate; - wattr_set(win, attrs ^ A_REVERSE, pair, NULL); + wattr_set(win, attrs ^ WA_REVERSE, pair, NULL); waddstr(win, "TAB"); break; default: @@ -110,7 +110,7 @@ teco_curses_format_str(WINDOW *win, const gchar *str, gsize len, gint max_width) chars_added += 2; if (chars_added > max_width) goto truncate; - wattr_set(win, attrs ^ A_REVERSE, pair, NULL); + wattr_set(win, attrs ^ WA_REVERSE, pair, NULL); waddch(win, '^'); waddch(win, TECO_CTL_ECHO(*str)); } else { @@ -126,7 +126,7 @@ teco_curses_format_str(WINDOW *win, const gchar *str, gsize len, gint max_width) waddnstr(win, str, clen); } } - /* restore original state of A_REVERSE */ + /* restore original state of WA_REVERSE */ wattr_set(win, attrs, pair, NULL); str += clen; diff --git a/src/interface-curses/curses-utils.h b/src/interface-curses/curses-utils.h index 18cdd3d..c6d9d8d 100644 --- a/src/interface-curses/curses-utils.h +++ b/src/interface-curses/curses-utils.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2025 Robin Haberkorn + * Copyright (C) 2012-2026 Robin Haberkorn * * 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 @@ -20,19 +20,19 @@ #include <curses.h> +/** what is displayed for unnamed buffers in the info line and popups */ +#define TECO_UNNAMED_FILE "(Unnamed)" + guint teco_curses_format_str(WINDOW *win, const gchar *str, gsize len, gint max_width); guint teco_curses_format_filename(WINDOW *win, const gchar *filename, gint max_width); -/** - * Add Unicode character to window. - * This is just like wadd_wch(), but does not require wide-char APIs. - */ +/** Add Unicode character to window. */ static inline void teco_curses_add_wc(WINDOW *win, gunichar chr) { - gchar buf[6]; - waddnstr(win, buf, g_unichar_to_utf8(chr, buf)); + wchar_t wc = chr; + waddnwstr(win, &wc, 1); } /** diff --git a/src/interface-curses/interface.c b/src/interface-curses/interface.c index 073ff86..45821f9 100644 --- a/src/interface-curses/interface.c +++ b/src/interface-curses/interface.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2025 Robin Haberkorn + * Copyright (C) 2012-2026 Robin Haberkorn * * 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 @@ -138,8 +138,6 @@ static gint teco_xterm_version(void) G_GNUC_UNUSED; static gint teco_interface_blocking_getch(void); -#define UNNAMED_FILE "(Unnamed)" - /** * Get bright variant of one of the 8 standard * curses colors. @@ -202,6 +200,7 @@ static struct { TECO_INFO_TYPE_BUFFER = 0, TECO_INFO_TYPE_QREG } info_type; + /* current document's name or empty string for "(Unnamed)" buffer */ teco_string_t info_current; gboolean info_dirty; @@ -233,53 +232,50 @@ static struct { * * Scinterm no longer initializes all color pairs for all combinations of * the builtin foreground and background colors. - * Since curses guarantees only 256 color pairs, we cannot do that either. - * Instead we allocate color pairs beginnig at 128 on demand - * (similar to what Scinterm does). + * Since curses does not guarantee any number of color pairs, we cannot do that either. + * 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 last_pair = 127; + static guint 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 |= WA_REVERSE; + return 0; + } G_STATIC_ASSERT(sizeof(gshort)*2 <= sizeof(guint)); - gpointer key = GUINT_TO_POINTER(((guint)fg << 16) | bg); + 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); - init_pair(++last_pair, fg, bg); - g_hash_table_insert(teco_interface.pair_table, key, GUINT_TO_POINTER(last_pair)); - return last_pair; + if (G_UNLIKELY(next_pair >= COLOR_PAIRS || next_pair > G_MAXSHORT)) + return 0; + 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 - * color scheme. - */ - return bg != COLOR_BLACK ? A_REVERSE : 0; + gshort pair = teco_color_pair(&attr, fg, bg); + return wattr_set(win, attr, pair, NULL); } /** @@ -393,6 +389,13 @@ teco_view_noutrefresh(teco_view_t *ctx) scintilla_noutrefresh(ctx); } +static inline void +teco_view_update_cursor(teco_view_t *ctx) +{ + if (teco_view_ssm(ctx, SCI_GETCARETSTYLE, 0, 0) & CARETSTYLE_CURSES) + scintilla_update_cursor(ctx); +} + static inline WINDOW * teco_view_get_window(teco_view_t *ctx) { @@ -426,7 +429,7 @@ static void teco_interface_set_window_title(const gchar *title); static void teco_interface_draw_info(void); void -teco_interface_init(void) +teco_interface_init(gint argc, gchar **argv) { for (guint i = 0; i < G_N_ELEMENTS(teco_interface.color_table); i++) teco_interface.color_table[i] = -1; @@ -437,20 +440,16 @@ teco_interface_init(void) teco_curses_info_popup_init(&teco_interface.popup); - /* - * Make sure we have a string for the info line - * even if teco_interface_info_update() is never called. - */ - teco_string_init(&teco_interface.info_current, PACKAGE_NAME, strlen(PACKAGE_NAME)); - teco_cmdline_init(); /* * The default INDIC_STRIKE wouldn't be visible. - * Instead we use INDIC_STRAIGHTBOX, which will be rendered as underlined if - * the alpha is 0. + * Instead we use INDIC_SQUIGGLE, which is rendered as WA_UNDERLINE. */ - teco_cmdline_ssm(SCI_INDICSETSTYLE, INDICATOR_RUBBEDOUT, INDIC_STRAIGHTBOX); - teco_cmdline_ssm(SCI_INDICSETALPHA, INDICATOR_RUBBEDOUT, 0); + teco_cmdline_ssm(SCI_INDICSETSTYLE, INDICATOR_RUBBEDOUT, INDIC_SQUIGGLE); + /* + * Enable hardware cursor by default. + */ + teco_cmdline_ssm(SCI_SETCARETSTYLE, CARETSTYLE_CURSES, 0); /* * On all platforms except Curses/XTerm, it's @@ -747,6 +746,12 @@ 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. + * Some Curses implementations (NetBSD, PDCurses) may support less, + * but set COLOR_PAIRS accordingly. + */ + scintilla_set_color_offsets(0, MIN(COLOR_PAIRS, G_MAXSHORT)/2); /* * On UNIX terminals, the escape key is usually @@ -790,8 +795,6 @@ teco_interface_init_interactive(GError **error) cbreak(); noecho(); - /* Scintilla draws its own cursor */ - curs_set(0); /* * This has also been observed to reduce flickering * in teco_interface_refresh(). @@ -818,7 +821,7 @@ teco_interface_init_interactive(GError **error) * must always be TRUE so we receive KEY_RESIZE. */ keypad(teco_interface.input_pad, TRUE); - nodelay(teco_interface.input_pad, TRUE); + wtimeout(teco_interface.input_pad, 0); teco_interface.input_queue = g_queue_new(); @@ -939,7 +942,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)); @@ -961,7 +964,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); } @@ -972,11 +975,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); } @@ -990,10 +993,11 @@ teco_interface_getch(gboolean widechar) /* * Signal that we accept input by drawing a real cursor in the message bar. + * FIXME: curs_set(2) would be better, but isn't always visible. */ wmove(teco_interface.msg_window, 0, 0); - curs_set(1); wrefresh(teco_interface.msg_window); + curs_set(1); gchar buf[4]; gint i = 0; @@ -1020,7 +1024,6 @@ teco_interface_getch(gboolean widechar) i = 0; } while (cp < 0); - curs_set(0); return cp; } @@ -1150,36 +1153,40 @@ 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; waddstr(teco_interface.info_window, PACKAGE_NAME " "); + teco_string_t info_current = teco_interface.info_current; + if (!info_current.len) { + info_current.data = TECO_UNNAMED_FILE; + info_current.len = strlen(info_current.data); + } + switch (teco_interface.info_type) { case TECO_INFO_TYPE_QREG: info_type_str = PACKAGE_NAME " - <QRegister> "; teco_curses_add_wc(teco_interface.info_window, teco_ed & TECO_ED_ICONS ? TECO_CURSES_ICONS_QREG : '-'); waddstr(teco_interface.info_window, " <QRegister> "); - /* same formatting as in command lines */ teco_curses_format_str(teco_interface.info_window, - teco_interface.info_current.data, - teco_interface.info_current.len, -1); + info_current.data, info_current.len, -1); break; case TECO_INFO_TYPE_BUFFER: info_type_str = PACKAGE_NAME " - <Buffer> "; - g_assert(!teco_string_contains(&teco_interface.info_current, '\0')); + g_assert(!teco_string_contains(info_current, '\0')); + /* "(Unnamed)" buffer has to be looked up as "" */ teco_curses_add_wc(teco_interface.info_window, teco_ed & TECO_ED_ICONS ? teco_curses_icons_lookup_file(teco_interface.info_current.data) : '-'); waddstr(teco_interface.info_window, " <Buffer> "); - teco_curses_format_filename(teco_interface.info_window, - teco_interface.info_current.data, + teco_curses_format_filename(teco_interface.info_window, info_current.data, getmaxx(teco_interface.info_window) - getcurx(teco_interface.info_window) - 1); waddch(teco_interface.info_window, teco_interface.info_dirty ? '*' : ' '); @@ -1195,8 +1202,7 @@ teco_interface_draw_info(void) * Make sure the title will consist only of printable characters. */ g_autofree gchar *info_current_printable; - info_current_printable = teco_string_echo(teco_interface.info_current.data, - teco_interface.info_current.len); + info_current_printable = teco_string_echo(info_current.data, info_current.len); g_autofree gchar *title = g_strconcat(info_type_str, info_current_printable, teco_interface.info_dirty ? "*" : "", NULL); teco_interface_set_window_title(title); @@ -1216,10 +1222,9 @@ teco_interface_info_update_qreg(const teco_qreg_t *reg) void teco_interface_info_update_buffer(const teco_buffer_t *buffer) { - const gchar *filename = buffer->filename ? : UNNAMED_FILE; - teco_string_clear(&teco_interface.info_current); - teco_string_init(&teco_interface.info_current, filename, strlen(filename)); + teco_string_init(&teco_interface.info_current, buffer->filename, + buffer->filename ? strlen(buffer->filename) : 0); teco_interface.info_dirty = buffer->state > TECO_BUFFER_CLEAN; teco_interface.info_type = TECO_INFO_TYPE_BUFFER; /* NOTE: drawn in teco_interface_event_loop_iter() */ @@ -1233,7 +1238,20 @@ teco_interface_info_update_buffer(const teco_buffer_t *buffer) * default clipboard ("~") as we do not know whether * it corresponds to the X11 PRIMARY, SECONDARY or * CLIPBOARD selections. + * + * On XCurses we must not (and don't have to) probe + * the clipboard as it would be before Xinitscr(). */ +#ifdef XCURSES + +static void +teco_interface_init_clipboard(void) +{ + teco_qreg_table_replace(&teco_qreg_table_globals, teco_qreg_clipboard_new("")); +} + +#else /* XCURSES */ + static void teco_interface_init_clipboard(void) { @@ -1258,6 +1276,8 @@ teco_interface_init_clipboard(void) teco_qreg_table_replace(&teco_qreg_table_globals, teco_qreg_clipboard_new("")); } +#endif /* !XCURSES */ + gboolean teco_interface_set_clipboard(const gchar *name, const gchar *str, gsize str_len, GError **error) { @@ -1429,7 +1449,7 @@ teco_interface_osc52_get_clipboard(const gchar *name, gchar **str, gsize *len, G * We restore all changed Curses settings before returning * to be on the safe side. */ - halfdelay(1); /* 100ms timeout */ + wtimeout(teco_interface.input_pad, 100); /* don't interpret escape sequences */ keypad(teco_interface.input_pad, FALSE); @@ -1496,7 +1516,7 @@ teco_interface_osc52_get_clipboard(const gchar *name, gchar **str, gsize *len, G cleanup: keypad(teco_interface.input_pad, TRUE); - nodelay(teco_interface.input_pad, TRUE); + wtimeout(teco_interface.input_pad, 0); return ret; } @@ -1547,7 +1567,7 @@ teco_interface_set_clipboard(const gchar *name, const gchar *str, gsize str_len, g_auto(teco_string_t) command; if (!reg->vtable->get_string(reg, &command.data, &command.len, NULL, error)) return FALSE; - if (teco_string_contains(&command, '\0')) { + if (teco_string_contains(command, '\0')) { teco_error_qregcontainsnull_set(error, reg_name, strlen(reg_name), FALSE); return FALSE; } @@ -1608,7 +1628,7 @@ teco_interface_get_clipboard(const gchar *name, gchar **str, gsize *len, GError g_auto(teco_string_t) command; if (!reg->vtable->get_string(reg, &command.data, &command.len, NULL, error)) return FALSE; - if (teco_string_contains(&command, '\0')) { + if (teco_string_contains(command, '\0')) { teco_error_qregcontainsnull_set(error, reg_name, strlen(reg_name), FALSE); return FALSE; } @@ -1706,11 +1726,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 @@ -1828,7 +1850,31 @@ teco_interface_refresh(gboolean force) wnoutrefresh(teco_interface.msg_window); teco_view_noutrefresh(teco_cmdline.view); teco_curses_info_popup_noutrefresh(&teco_interface.popup); + /* + * If hardware cursors (CARETSTYLE_CURSES) are enabled on the + * command-line view, make sure that the cursor is left + * in the correct position. + * + * FIXME: This shouldn't be necessary if we refreshed the command-line + * view last. Also, if we wanted to support the hardware cursor + * in the main view as well, we'd have to handle a possibly + * overlappig info popup. + */ + teco_view_update_cursor(teco_cmdline.view); doupdate(); + + /* + * Scinterm enables/disables the hardware cursor, + * but only if CARETSTYLE_CURSES is used. + * Disabling the cursor repeatedly ensures you can change + * the caret style interactively. + * It also makes sure the cursor is reset after + * teco_interface_getch(). + * Also, window resizes sometimes enable the hardware cursor on + * PDCurses/wincon (FIXME). + */ + if (!(teco_view_ssm(teco_cmdline.view, SCI_GETCARETSTYLE, 0, 0) & CARETSTYLE_CURSES)) + curs_set(0); } #if NCURSES_MOUSE_VERSION >= 2 @@ -1865,10 +1911,13 @@ teco_interface_process_mevent(MEVENT *event, GError **error) event->y, event->x); if (insert && machine->current->insert_completion_cb) { - /* successfully clicked popup item */ + /* + * Successfully clicked popup item. + * `insert` is the empty string for the "(Unnamed)" buffer. + */ const teco_string_t insert_suffix = {insert->data + teco_interface.popup_prefix_len, insert->len - teco_interface.popup_prefix_len}; - if (!machine->current->insert_completion_cb(machine, &insert_suffix, error)) + if (!machine->current->insert_completion_cb(machine, insert_suffix, error)) return FALSE; teco_interface_popup_clear(); @@ -1883,9 +1932,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; } @@ -1968,6 +2019,20 @@ teco_interface_process_mevent(MEVENT *event, GError **error) return teco_cmdline_keymacro("MOUSE", -1, error); } +#ifdef __PDCURSES__ + +static gboolean +teco_interface_getmouse(GError **error) +{ + MEVENT event; + + /* in contrast to ncurses, there is no separate mouse event queue */ + return getmouse(&event) != OK || + teco_interface_process_mevent(&event, error); +} + +#else /* __PDCURSES__ */ + static gboolean teco_interface_getmouse(GError **error) { @@ -1980,6 +2045,8 @@ teco_interface_getmouse(GError **error) return TRUE; } +#endif /* !__PDCURSES__ */ + #endif /* NCURSES_MOUSE_VERSION >= 2 */ static gint @@ -2001,7 +2068,8 @@ teco_interface_blocking_getch(void) gboolean new_mousekey = (teco_ed & TECO_ED_MOUSEKEY) != 0; if (new_mousekey != old_mousekey) { old_mousekey = new_mousekey; - mmask_t mmask = BUTTON_EVENT(PRESSED) | BUTTON_EVENT(RELEASED); + mmask_t mmask = BUTTON_EVENT(PRESSED) | BUTTON_EVENT(RELEASED) | + BUTTON_SHIFT | BUTTON_CTRL | BUTTON_ALT; #ifdef __PDCURSES__ /* * On PDCurses it's crucial NOT to mask for BUTTONX_CLICKED. @@ -2016,7 +2084,6 @@ teco_interface_blocking_getch(void) /* no special <CTRL/C> handling */ raw(); - nodelay(teco_interface.input_pad, FALSE); /* * Make sure we return when it's time to create recovery dumps. @@ -2027,6 +2094,8 @@ teco_interface_blocking_getch(void) gdouble elapsed = g_timer_elapsed(teco_interface.recovery_timer, NULL); wtimeout(teco_interface.input_pad, MAX((gdouble)teco_ring_recovery_interval - elapsed, 0)*1000); + } else { + wtimeout(teco_interface.input_pad, -1); } /* @@ -2038,7 +2107,7 @@ teco_interface_blocking_getch(void) teco_memory_start_limiting(); /* allow asynchronous interruptions on <CTRL/C> */ teco_interrupted = FALSE; - nodelay(teco_interface.input_pad, TRUE); + wtimeout(teco_interface.input_pad, 0); #if defined(CURSES_TTY) || defined(PDCURSES_WINCON) || defined(NCURSES_WIN32) noraw(); /* FIXME: necessary because of NCURSES_WIN32 bug */ cbreak(); @@ -2083,11 +2152,6 @@ teco_interface_event_loop_iter(void) return; #ifdef KEY_RESIZE case KEY_RESIZE: - /* - * At least on PDCurses/Wincon, the hardware cursor is sometimes - * reactivated. - */ - curs_set(0); teco_interface_resize_all_windows(); break; #endif |
