diff options
Diffstat (limited to 'src/interface-curses')
| -rw-r--r-- | src/interface-curses/curses-info-popup.c | 32 | ||||
| -rw-r--r-- | src/interface-curses/curses-info-popup.h | 2 | ||||
| -rw-r--r-- | src/interface-curses/curses-utils.c | 16 | ||||
| -rw-r--r-- | src/interface-curses/curses-utils.h | 9 | ||||
| -rw-r--r-- | src/interface-curses/interface.c | 541 |
5 files changed, 293 insertions, 307 deletions
diff --git a/src/interface-curses/curses-info-popup.c b/src/interface-curses/curses-info-popup.c index edb6e15..83d4665 100644 --- a/src/interface-curses/curses-info-popup.c +++ b/src/interface-curses/curses-info-popup.c @@ -73,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 */ @@ -102,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); /* @@ -161,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 */ @@ -171,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); /* @@ -183,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, @@ -214,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 fd923e9..898ba70 100644 --- a/src/interface-curses/curses-info-popup.h +++ b/src/interface-curses/curses-info-popup.h @@ -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 875c332..3b25d56 100644 --- a/src/interface-curses/curses-utils.c +++ b/src/interface-curses/curses-utils.c @@ -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 97fc1cc..c6d9d8d 100644 --- a/src/interface-curses/curses-utils.h +++ b/src/interface-curses/curses-utils.h @@ -27,15 +27,12 @@ guint teco_curses_format_str(WINDOW *win, const gchar *str, gsize len, gint max_ 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 0764db3..21e728f 100644 --- a/src/interface-curses/interface.c +++ b/src/interface-curses/interface.c @@ -125,7 +125,8 @@ teco_console_ctrl_handler(DWORD type) { switch (type) { case CTRL_C_EVENT: - teco_interrupted = TRUE; + case CTRL_BREAK_EVENT: + teco_interrupted = TECO_INTERRUPTED; return TRUE; } @@ -137,6 +138,17 @@ teco_console_ctrl_handler(DWORD type) static gint teco_xterm_version(void) G_GNUC_UNUSED; static gint teco_interface_blocking_getch(void); +static inline gboolean teco_interface_check_key(gint key); + +#define MAX_COLORS (MIN(COLORS, G_MAXSHORT)/2) +/** + * Maximum effective number of color pairs. + * 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. + * Half of the range is reserved to Scinterm. + */ +#define MAX_PAIRS (MIN(COLOR_PAIRS, G_MAXSHORT)/2) /** * Get bright variant of one of the 8 standard @@ -154,6 +166,12 @@ static gint teco_interface_blocking_getch(void); * The 8 bright colors (if terminal supports at * least 16 colors), else they are identical to * the non-bright colors (default curses colors). + * + * As a consequence a light black (grey) foreground + * on black background might be invisible on 8 color + * terminals. + * If you add in A_BOLD, there is a good chance that + * the text will still become grey. */ #define COLOR_LBLACK COLOR_LIGHT(COLOR_BLACK) #define COLOR_LRED COLOR_LIGHT(COLOR_RED) @@ -166,30 +184,22 @@ static gint teco_interface_blocking_getch(void); static struct { /** + * Mapping of RGB codes (encoded into a pointer) + * to a curses color number. + */ + GHashTable *color_table; + /** * Mapping of foreground and background curses color tuples * (encoded into a pointer) to a color pair number. */ GHashTable *pair_table; - /** - * Mapping of the first 16 curses color codes (that may or may not - * correspond with the standard terminal color codes) to - * Scintilla-compatible RGB values (red is LSB) to initialize after - * Curses startup. - * Negative values mean no color redefinition (keep the original - * palette entry). + * Whether to map the 16 standard palettized colors + * in color_table. + * Should be disabled when using arbitrary RGB values, + * so every color is initialized to exact RGB values. */ - gint32 color_table[16]; - - /** - * Mapping of the first 16 curses color codes to their - * original values for restoring them on shutdown. - * Unfortunately, this may not be supported on all - * curses ports, so this array may be unused. - */ - struct { - gshort r, g, b; - } orig_color_table[16]; + gboolean use_palette; int stdin_orig, stdout_orig, stderr_orig; SCREEN *screen; @@ -232,56 +242,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). - * - * @note Scinterm now also has scintilla_set_color_offsets(), - * so we could use the lower 127 color pairs as well. + * 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 >= MAX_PAIRS)) + 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); } /** @@ -298,38 +302,60 @@ teco_rgb2curses_triple(guint32 rgb, gshort *r, gshort *g, gshort *b) *b = ((rgb & 0xFF0000) >> 16)*1000/0xFF; } +static inline void +teco_interface_insert_color(guint32 rgb, gshort color) +{ + /* + * Color ids are stored one-offset, so that COLOR_BLACK + * can be discerned from a missing entry. + */ + g_hash_table_insert(teco_interface.color_table, + GUINT_TO_POINTER(rgb), GUINT_TO_POINTER(color)+1); +} + /** * Convert a Scintilla-compatible RGB color value * (0xBBGGRR) to a Curses color code (e.g. COLOR_BLACK). - * This does not work with arbitrary RGB values but - * only the 16 RGB color values defined by Scinterm - * corresponding to the 16 terminal colors. - * It is equivalent to Scinterm's internal `term_color` - * function. + * + * This will automatically initialize new curses colors + * when necessary. + * Unless the palette is disabled with + * teco_interface_disable_palette(), this will return + * the 16 "standard" colors for a predefined list of + * RGB values (just like defined by Scinterm). */ static gshort teco_rgb2curses(guint32 rgb) { - switch (rgb) { - case 0x000000: return COLOR_BLACK; - case 0x000080: return COLOR_RED; - case 0x008000: return COLOR_GREEN; - case 0x008080: return COLOR_YELLOW; - case 0x800000: return COLOR_BLUE; - case 0x800080: return COLOR_MAGENTA; - case 0x808000: return COLOR_CYAN; - case 0xC0C0C0: return COLOR_WHITE; - case 0x404040: return COLOR_LBLACK; - case 0x0000FF: return COLOR_LRED; - case 0x00FF00: return COLOR_LGREEN; - case 0x00FFFF: return COLOR_LYELLOW; - case 0xFF0000: return COLOR_LBLUE; - case 0xFF00FF: return COLOR_LMAGENTA; - case 0xFFFF00: return COLOR_LCYAN; - case 0xFFFFFF: return COLOR_LWHITE; - } + static guint colors = 0; + + if (!has_colors()) + return COLOR_WHITE; - return COLOR_WHITE; + gpointer value = g_hash_table_lookup(teco_interface.color_table, + GUINT_TO_POINTER(rgb)); + if (G_LIKELY(value != NULL)) + return GPOINTER_TO_UINT(value)-1; + + /* + * Even when initializing new colors, we will __try__ not to + * touch the 16 palettized colors. + * First of all, we might still need them and secondly, + * this ensures we don't have to reset them on exit + * (which is not always possible anyway). + * There can still be 16 color terminals with + * can_change_color() == TRUE, though. + */ + guint next_color = colors + (COLORS > 16 ? 16 : 0); + + if (G_UNLIKELY(next_color >= MAX_COLORS) || !can_change_color()) + return COLOR_WHITE; + gshort r, g, b; + teco_rgb2curses_triple(rgb, &r, &g, &b); + init_color(next_color, r, g, b); + teco_interface_insert_color(rgb, next_color); + colors++; + return next_color; } static gint @@ -395,6 +421,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) { @@ -413,9 +446,6 @@ teco_view_free(teco_view_t *ctx) scintilla_delete(ctx); } -static void teco_interface_init_color_safe(guint color, guint32 rgb); -static void teco_interface_restore_colors(void); - static void teco_interface_init_screen(void); static gboolean teco_interface_init_interactive(GError **error); static void teco_interface_restore_batch(void); @@ -428,12 +458,9 @@ 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; - for (guint i = 0; i < G_N_ELEMENTS(teco_interface.orig_color_table); i++) - teco_interface.orig_color_table[i].r = -1; + teco_interface.use_palette = TRUE; teco_interface.stdin_orig = teco_interface.stdout_orig = teco_interface.stderr_orig = -1; @@ -442,9 +469,13 @@ teco_interface_init(void) teco_cmdline_init(); /* * The default INDIC_STRIKE wouldn't be visible. - * Instead we use INDIC_SQUIGGLE, which is rendered as A_UNDERLINE. + * Instead we use INDIC_SQUIGGLE, which is rendered as WA_UNDERLINE. */ 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 @@ -471,133 +502,11 @@ teco_interface_get_options(void) return NULL; } -static void -teco_interface_init_color_safe(guint color, guint32 rgb) -{ -#if defined(__PDCURSES__) && !defined(PDCURSES_GUI) - if (teco_interface.orig_color_table[color].r < 0) { - color_content((short)color, - &teco_interface.orig_color_table[color].r, - &teco_interface.orig_color_table[color].g, - &teco_interface.orig_color_table[color].b); - } -#endif - - gshort r, g, b; - teco_rgb2curses_triple(rgb, &r, &g, &b); - init_color((short)color, r, g, b); -} - -#if defined(__PDCURSES__) && !defined(PDCURSES_GUI) - -/* - * On PDCurses/WinCon, color_content() will actually return - * the real console color palette - or at least the default - * palette when the console started. - */ -static void -teco_interface_restore_colors(void) -{ - if (!can_change_color()) - return; - - for (guint i = 0; i < G_N_ELEMENTS(teco_interface.orig_color_table); i++) { - if (teco_interface.orig_color_table[i].r < 0) - continue; - - init_color((short)i, - teco_interface.orig_color_table[i].r, - teco_interface.orig_color_table[i].g, - teco_interface.orig_color_table[i].b); - } -} - -#elif defined(CURSES_TTY) - -/* - * FIXME: On UNIX/ncurses teco_interface_init_color_safe() __may__ - * change the terminal's palette permanently and there does not - * appear to be any portable way of restoring the original one. - * Curses has color_content(), but there is actually no terminal - * that allows querying the current palette and so color_content() - * will return bogus "default" values and only for the first 8 colors. - * It would do more damage to restore the palette returned by - * color_content() than it helps. - * xterm has the escape sequence "\e]104\a" which restores - * the palette from Xdefaults but not all terminal emulators - * claiming to be "xterm" via $TERM support this escape sequence. - * lxterminal for instance will print gibberish instead. - * So we try to look whether $XTERM_VERSION is set. - * There are hardly any other terminal emulators that support palette - * resets. - * The only emulator I'm aware of which can be identified reliably - * by $TERM supporting a palette reset is the Linux console - * (see console_codes(4)). The escape sequence "\e]R" is already - * part of its terminfo description (orig_colors capability) - * which is apparently sent by endwin(), so the palette is - * already properly restored on endwin(). - * Welcome in Curses hell. - */ -static void -teco_interface_restore_colors(void) -{ - if (teco_xterm_version() < 0) - return; - - /* - * Looks like a real XTerm - */ - fputs("\e]104\a", teco_interface.screen_tty); - fflush(teco_interface.screen_tty); -} - -#else /* (!__PDCURSES__ || PDCURSES_GUI) && !CURSES_TTY */ - -static void -teco_interface_restore_colors(void) -{ - /* - * No way to restore the palette, or it's - * unnecessary (e.g. XCurses) - */ -} - -#endif - void -teco_interface_init_color(guint color, guint32 rgb) +teco_interface_disable_palette(void) { - if (color >= G_N_ELEMENTS(teco_interface.color_table)) - return; - -#if defined(__PDCURSES__) && !defined(PDC_RGB) - /* - * PDCurses will usually number color codes differently - * (least significant bit is the blue component) while - * SciTECO macros will assume a standard terminal color - * code numbering with red as the LSB. - * Therefore we have to swap the bit order of the least - * significant 3 bits here. - */ - color = (color & ~0x5) | - ((color & 0x1) << 2) | ((color & 0x4) >> 2); -#endif - - if (teco_interface.input_pad) { - /* interactive mode */ - if (!can_change_color()) - return; - - teco_interface_init_color_safe(color, rgb); - } else { - /* - * batch mode: store colors, - * they can only be initialized after start_color() - * which is called by Scinterm when interactive - * mode is initialized - */ - teco_interface.color_table[color] = (gint32)rgb; - } + teco_interface.use_palette = FALSE; + scintilla_disable_color_palette(); } #ifdef CURSES_TTY @@ -739,8 +648,33 @@ teco_interface_init_interactive(GError **error) teco_interface_init_screen(); + teco_interface.color_table = g_hash_table_new(g_direct_hash, g_direct_equal); teco_interface.pair_table = g_hash_table_new(g_direct_hash, g_direct_equal); start_color(); + scintilla_set_color_offsets(MAX_COLORS, MAX_PAIRS); + + if (teco_interface.use_palette && has_colors()) { + teco_interface_insert_color(0x000000, COLOR_BLACK); + teco_interface_insert_color(0x000080, COLOR_RED); + teco_interface_insert_color(0x008000, COLOR_GREEN); + teco_interface_insert_color(0x008080, COLOR_YELLOW); + teco_interface_insert_color(0x800000, COLOR_BLUE); + teco_interface_insert_color(0x800080, COLOR_MAGENTA); + teco_interface_insert_color(0x808000, COLOR_CYAN); + teco_interface_insert_color(0xC0C0C0, COLOR_WHITE); + /* + * On 8-color terminals, we still support the + * higher (light) colors, but map them to the lower codes as well. + */ + teco_interface_insert_color(0x404040, COLOR_LBLACK); + teco_interface_insert_color(0x0000FF, COLOR_LRED); + teco_interface_insert_color(0x00FF00, COLOR_LGREEN); + teco_interface_insert_color(0x00FFFF, COLOR_LYELLOW); + teco_interface_insert_color(0xFF0000, COLOR_LBLUE); + teco_interface_insert_color(0xFF00FF, COLOR_LMAGENTA); + teco_interface_insert_color(0xFFFF00, COLOR_LCYAN); + teco_interface_insert_color(0xFFFFFF, COLOR_LWHITE); + } /* * On UNIX terminals, the escape key is usually @@ -784,8 +718,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(). @@ -824,19 +756,6 @@ teco_interface_init_interactive(GError **error) teco_interface_show_view(teco_interface_current_view); /* - * Only now it's safe to redefine the 16 default colors. - */ - if (can_change_color()) { - for (guint i = 0; i < G_N_ELEMENTS(teco_interface.color_table); i++) { - /* - * init_color() may still fail if COLORS < 16 - */ - if (teco_interface.color_table[i] >= 0) - teco_interface_init_color_safe(i, (guint32)teco_interface.color_table[i]); - } - } - - /* * Only now (in interactive mode), it's safe to initialize * the clipboard Q-Registers on ncurses with a compatible terminal * emulator since clipboard operations will no longer interfer @@ -868,7 +787,6 @@ teco_interface_restore_batch(void) * (i.e. return to batch mode) */ endwin(); - teco_interface_restore_colors(); /* * Restore stdin, stdout and stderr, so output goes to @@ -933,7 +851,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)); @@ -955,7 +873,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); } @@ -966,11 +884,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); } @@ -984,25 +902,41 @@ 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; gint32 cp; do { - cp = teco_interface_blocking_getch(); - if (cp == TECO_CTL_KEY('C')) - teco_interrupted = TRUE; - if (cp == TECO_CTL_KEY('C') || cp == TECO_CTL_KEY('D')) { - cp = -1; - break; - } - if (cp < 0 || cp > 0xFF) - continue; + do { + cp = teco_interface_blocking_getch(); + switch (cp) { +#ifdef KEY_RESIZE + case KEY_RESIZE: + teco_interface_resize_all_windows(); + teco_interface_refresh(FALSE); + /* cursor was moved by the resize */ + wmove(teco_interface.msg_window, 0, 0); + wrefresh(teco_interface.msg_window); + break; +#endif + case KEY_BACKSPACE: + return TECO_CTL_KEY('H'); + case KEY_ENTER: + return '\n'; + case TECO_CTL_KEY('C'): + teco_interrupted = TECO_INTERRUPTED; + /* fall through */ + case TECO_CTL_KEY('D'): + /* emulates EOF on stdin */ + return -1; + } + } while (cp < 0 || cp > 0xFF || !teco_interface_check_key(cp)); if (!widechar || !cp) break; @@ -1014,7 +948,6 @@ teco_interface_getch(gboolean widechar) i = 0; } while (cp < 0); - curs_set(0); return cp; } @@ -1144,11 +1077,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; @@ -1229,7 +1162,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) { @@ -1254,6 +1200,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) { @@ -1702,11 +1650,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 @@ -1757,7 +1707,7 @@ teco_interface_popup_clear(void) gboolean teco_interface_is_interrupted(void) { - return teco_interrupted != FALSE; + return teco_interrupted == TECO_INTERRUPTED; } #else /* !CURSES_TTY && !PDCURSES_WINCON && !NCURSES_WIN32 */ @@ -1776,7 +1726,7 @@ teco_interface_is_interrupted(void) { if (!teco_interface.input_pad) /* batch mode */ - return teco_interrupted != FALSE; + return teco_interrupted == TECO_INTERRUPTED; /* * NOTE: wgetch() is configured to be nonblocking. @@ -1791,7 +1741,7 @@ teco_interface_is_interrupted(void) GINT_TO_POINTER(key)); } - return teco_interrupted != FALSE; + return teco_interrupted == TECO_INTERRUPTED; } #endif @@ -1824,7 +1774,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 @@ -1882,9 +1856,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; } @@ -2016,7 +1992,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. @@ -2053,7 +2030,7 @@ teco_interface_blocking_getch(void) gint key = wgetch(teco_interface.input_pad); teco_memory_start_limiting(); /* allow asynchronous interruptions on <CTRL/C> */ - teco_interrupted = FALSE; + teco_interrupted = TECO_NORMAL; wtimeout(teco_interface.input_pad, 0); #if defined(CURSES_TTY) || defined(PDCURSES_WINCON) || defined(NCURSES_WIN32) noraw(); /* FIXME: necessary because of NCURSES_WIN32 bug */ @@ -2069,6 +2046,31 @@ teco_interface_blocking_getch(void) return key; } +#ifdef __PDCURSES__ + +/* + * Especially PDCurses/WinGUI likes to report two keypresses, + * e.g. for CTRL+Shift+6 (CTRL+^). + * Make sure we don't filter out AltGr, which may be reported as CTRL+ALT. + */ +static inline gboolean +teco_interface_check_key(gint key) +{ + return (PDC_get_key_modifiers() & + (PDC_KEY_MODIFIER_CONTROL | PDC_KEY_MODIFIER_ALT)) != PDC_KEY_MODIFIER_CONTROL || + TECO_IS_CTL(key); +} + +#else /* __PDCURSES__ */ + +static inline gboolean +teco_interface_check_key(gint key) +{ + return TRUE; +} + +#endif + /** * One iteration of the event loop. * @@ -2099,11 +2101,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 @@ -2179,21 +2176,9 @@ teco_interface_event_loop_iter(void) * Control keys and keys with printable representation */ default: - if (key > 0xFF) - /* unhandled function key */ - return; - -#ifdef __PDCURSES__ - /* - * Especially PDCurses/WinGUI likes to report two keypresses, - * e.g. for CTRL+Shift+6 (CTRL+^). - * Make sure we don't filter out AltGr, which may be reported as CTRL+ALT. - */ - if ((PDC_get_key_modifiers() & - (PDC_KEY_MODIFIER_CONTROL | PDC_KEY_MODIFIER_ALT)) == PDC_KEY_MODIFIER_CONTROL && - !TECO_IS_CTL(key)) + if (key > 0xFF || !teco_interface_check_key(key)) + /* unhandled function key or bogus key press */ return; -#endif /* * NOTE: There's also wget_wch(), but it requires @@ -2325,6 +2310,8 @@ teco_interface_cleanup(void) if (teco_interface.stdout_orig >= 0) close(teco_interface.stdout_orig); + if (teco_interface.color_table) + g_hash_table_destroy(teco_interface.color_table); if (teco_interface.pair_table) g_hash_table_destroy(teco_interface.pair_table); |
