aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core-commands.c123
-rw-r--r--src/interface-curses/interface.c503
-rw-r--r--src/interface-gtk/interface.c94
-rw-r--r--src/interface.h10
-rw-r--r--src/main.c24
-rw-r--r--src/ring.c10
-rw-r--r--src/sciteco.h6
-rw-r--r--src/search.c12
-rw-r--r--src/spawn.c2
-rw-r--r--src/stdio-commands.c37
-rw-r--r--src/view.c16
11 files changed, 468 insertions, 369 deletions
diff --git a/src/core-commands.c b/src/core-commands.c
index 528fa64..63d8dbc 100644
--- a/src/core-commands.c
+++ b/src/core-commands.c
@@ -2084,7 +2084,7 @@ teco_state_ecommand_flags(teco_machine_main_t *ctx, GError **error)
/*$ EJ properties
* [key]EJ -> value -- Get and set system properties
* value,keyEJ
- * rgb,color,3EJ
+ * [fore,]back,7EJ
* -EJ -> event
* -2EJ -> y, x
*
@@ -2136,46 +2136,36 @@ teco_state_ecommand_flags(teco_machine_main_t *ctx, GError **error)
* first.
* Memory limiting is enabled by default.
* .IP 3:
- * This \fBwrite-only\fP property allows redefining the
- * first 16 entries of the terminal color palette \(em a
- * feature required by some
- * color schemes when using the Curses user interface.
- * When setting this property, you are making a request
- * to define the terminal \fIcolor\fP as the Scintilla-compatible
- * RGB color value given in the \fIrgb\fP parameter.
- * \fIcolor\fP must be a value between 0 and 15
- * corresponding to black, red, green, yellow, blue, magenta,
- * cyan, white, bright black, bright red, etc. in that order.
- * The \fIrgb\fP value has the format 0xBBGGRR, i.e. the red
- * component is the least-significant byte and all other bytes
- * are ignored.
- * Note that on curses, RGB color values sent to Scintilla
- * are actually mapped to these 16 colors by the Scinterm port
- * and may represent colors with no resemblance to the \(lqRGB\(rq
- * value used (depending on the current palette) \(em they should
- * instead be viewed as placeholders for 16 standard terminal
- * color codes.
- * Please refer to the Scinterm manual for details on the allowed
- * \(lqRGB\(rq values and how they map to terminal colors.
- * This command provides a crude way to request exact RGB colors
- * for the first 16 terminal colors.
- * The color definition may be queued or be completely ignored
- * on other user interfaces and no feedback is given
- * if it fails. In fact feedback cannot be given reliably anyway.
- * Note that on 8 color terminals, only the first 8 colors
- * can be redefined (if you are lucky).
- * Note that due to restrictions of most terminal emulators
- * and some curses implementations, this command simply will not
- * restore the original palette entry or request
- * when rubbed out and should generally only be used in
- * \fIbatch-mode\fP \(em typically when loading a color scheme.
- * For the same reasons \(em even though \*(ST tries hard to
- * restore the original palette on exit \(em palette changes may
- * persist after \*(ST terminates on most terminal emulators on Unix.
- * The only emulator which will restore their default palette
- * on exit the author is aware of is \fBxterm\fP(1) and
- * the Linux console driver.
- * You have been warned. Good luck.
+ * .SCITECO_TOPIC palette
+ * This \fBwrite-only\fP property allows disabling use of
+ * the 16 color palette of default colors when using
+ * the Curses user interface.
+ * By default, when working with RGB values in Scintilla
+ * messages (and \*(ST color schemes for that matter)
+ * the 16 \(lqdefault\(rq colors are represented by
+ * special RGB placeholders (e.g. 0x000080 for \fICOLOR_RED\fP)
+ * as documented in the Scinterm manual.
+ * Any other RGB values will be automatically allocated
+ * in the terminal emulator if supported, giving the illusion
+ * of true color support, even though the actual number of
+ * concurrent colors on screen may be limited by the terminal
+ * emulator (see \fImax_colors\fP and \fImax_pairs\fP
+ * .B terminfo (1)
+ * capabilities) and other factors.
+ * \*(ST tries to avoid the first 16 color ids so as not to
+ * interfer with other terminal-based applications.
+ * The default palette however may be overwritten when requesting
+ * new colors in terminals restricted to 16 colors.
+ * In this case the original palette might not be restored properly on
+ * program termination and affect other terminal applications.
+ * The 16 predefined colors do not map to exact RGB values,
+ * though, but point into a palette at the terminal emulator.
+ * \*(ST therefore allows you to disable the 16 RGB placeholders
+ * by calling \(lq0,3EJ\(rq so that all colors are freshly initialized
+ * and will represent their exact RGB values.
+ * It has no effect when using the GTK interface.
+ * You are recommended to add \(lq0,3EJ\(rq to all color
+ * schemes that rely on exact RGB values.
* .IP 4:
* The column after the last horizontal movement.
* This is only used by \fBfnkeys.tes\fP and is similar to the Scintilla-internal
@@ -2194,9 +2184,30 @@ teco_state_ecommand_flags(teco_machine_main_t *ctx, GError **error)
* They are removed automatically when no longer required,
* but may be left around when the \*(ST crashes or terminates
* unexpectedly.
+ * During graceful shutdowns (\fBSIGTERM\fP etc.) recovery
+ * files may be dumped immediately even before the interval expires.
* After changing the interval, the new value may become
* active only after the previous interval expires.
* Recovery files are not dumped in batch mode.
+ * .IP 7:
+ * .SCITECO_TOPIC "default colors"
+ * This \fBwrite-only\fP property allows configuring the default
+ * foreground and background colors, given as two
+ * Scintilla-encoded RGB (0xBBGGRR) numbers.
+ * When asked to draw these colors, the terminal's native
+ * default colors \(em which may be some kind of white on black or
+ * black on white \(em are used instead.
+ * Therefore this command does not change any colors on screen
+ * \(em it just defines placeholder values for the terminal's
+ * default colors.
+ * This may be used e.g. to get a true black background where
+ * the terminal emulator's palette uses a different hue for
+ * Curses color COLOR_BLACK without having to resort to
+ * color redefinitions.
+ * With caution this may even be used to build color schemes that
+ * work both on white on black and black on white terminals.
+ * Use -1 to disable default foreground or background colors.
+ * It has no effect when using the GTK interface.
* .
* .IP -1:
* Type of the last mouse event (\fBread-only\fP).
@@ -2248,10 +2259,11 @@ teco_state_ecommand_properties(teco_machine_main_t *ctx, GError **error)
EJ_USER_INTERFACE = 0,
EJ_BUFFERS,
EJ_MEMORY_LIMIT,
- EJ_INIT_COLOR,
+ EJ_PALETTE,
EJ_CARETX,
EJ_CMDLINE_HEIGHT,
- EJ_RECOVERY_INTERVAL
+ EJ_RECOVERY_INTERVAL,
+ EJ_DEFAULT_COLORS
};
static teco_int_t caret_x = 0;
@@ -2272,19 +2284,13 @@ teco_state_ecommand_properties(teco_machine_main_t *ctx, GError **error)
return;
break;
- case EJ_INIT_COLOR:
- if (value < 0 || value >= 16) {
- g_set_error(error, TECO_ERROR, TECO_ERROR_FAILED,
- "Invalid color code %" TECO_INT_FORMAT " "
- "specified for <EJ>", value);
- return;
- }
- if (!teco_expressions_args()) {
- teco_error_argexpected_set(error, "EJ");
+ case EJ_PALETTE:
+ if (teco_is_success(value)) {
+ g_set_error_literal(error, TECO_ERROR, TECO_ERROR_FAILED,
+ "Default color palette can only be disabled");
return;
}
- teco_interface_init_color((guint)value,
- (guint32)teco_expressions_pop_num(0));
+ teco_interface_disable_palette();
break;
case EJ_CARETX:
@@ -2313,6 +2319,15 @@ teco_state_ecommand_properties(teco_machine_main_t *ctx, GError **error)
/* FIXME: Perhaps signal the interface to reprogram timers */
break;
+ case EJ_DEFAULT_COLORS: {
+ teco_int_t fore;
+
+ if (!teco_expressions_pop_num_calc(&fore, -1, error))
+ return;
+ teco_interface_set_default_colors(fore, value);
+ break;
+ }
+
default:
g_set_error(error, TECO_ERROR, TECO_ERROR_FAILED,
"Cannot set property %" TECO_INT_FORMAT " "
diff --git a/src/interface-curses/interface.c b/src/interface-curses/interface.c
index 45821f9..ff6a0f2 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,28 @@ 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];
-
+ gboolean use_palette;
/**
- * 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.
+ * Scintilla RGB values which should be rendered
+ * as the terminal's default foreground/background colors.
+ * This is -1 if colors haven't been configured.
*/
- struct {
- gshort r, g, b;
- } orig_color_table[16];
+ gint32 default_fg, default_bg;
int stdin_orig, stdout_orig, stderr_orig;
SCREEN *screen;
@@ -227,6 +243,76 @@ static struct {
} teco_interface;
/**
+ * Translate a Scintilla-compatible RGB color value
+ * (0xBBGGRR) to a Curses color triple (0 to 1000
+ * for each component).
+ */
+static inline void
+teco_rgb2curses_triple(guint32 rgb, gshort *r, gshort *g, gshort *b)
+{
+ /* NOTE: We could also use 200/51 */
+ *r = ((rgb & 0x0000FF) >> 0)*1000/0xFF;
+ *g = ((rgb & 0x00FF00) >> 8)*1000/0xFF;
+ *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 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)
+{
+ static guint colors = 0;
+
+ if (!has_colors())
+ 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;
+}
+
+/**
* Returns the curses color pair for the given curses foreground and background colors.
* Initializes a new pair if necessary.
*
@@ -237,12 +323,12 @@ static struct {
* 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
+ * @param fg curses foreground color (0xBBGGRR)
+ * @param bg curses background color (0xBBGGRR)
* @return curses color pair number
*/
static gshort
-teco_color_pair(attr_t *attr, gshort fg, gshort bg)
+teco_color_pair(attr_t *attr, guint32 fg, guint32 bg)
{
static guint next_pair = 1;
@@ -254,78 +340,44 @@ teco_color_pair(attr_t *attr, gshort fg, gshort bg)
* color schemes.
*/
if (!has_colors()) {
- if (bg != COLOR_BLACK)
- *attr |= WA_REVERSE;
+ if (bg != 0x000000)
+ *attr ^= WA_REVERSE;
return 0;
}
+ gshort fg_id, bg_id;
+
+ /*
+ * Prefer to use reverse attribute since it will work with
+ * the terminals default colors.
+ */
+ if (fg == teco_interface.default_bg && bg == teco_interface.default_fg) {
+ fg_id = bg_id = -1;
+ *attr ^= WA_REVERSE;
+ } else {
+ fg_id = fg == teco_interface.default_fg ? -1 : teco_rgb2curses(fg);
+ bg_id = bg == teco_interface.default_bg ? -1 : teco_rgb2curses(bg);
+ }
+
G_STATIC_ASSERT(sizeof(gshort)*2 <= sizeof(guint));
- gpointer key = GUINT_TO_POINTER(((guint)fg << 8*sizeof(bg)) | bg);
+ gpointer key = GUINT_TO_POINTER(((guint)fg_id << 8*sizeof(bg_id)) | bg_id);
gpointer value = g_hash_table_lookup(teco_interface.pair_table, key);
if (G_LIKELY(value != NULL))
return GPOINTER_TO_UINT(value);
- if (G_UNLIKELY(next_pair >= COLOR_PAIRS || next_pair > G_MAXSHORT))
+ if (G_UNLIKELY(next_pair >= MAX_PAIRS))
return 0;
- init_pair(next_pair, fg, bg);
+ init_pair(next_pair, fg_id, bg_id);
g_hash_table_insert(teco_interface.pair_table, key, GUINT_TO_POINTER(next_pair));
return next_pair++;
}
static inline gint
-teco_wattr_set(WINDOW *win, attr_t attr, gshort fg, gshort bg)
+teco_wattr_set(WINDOW *win, attr_t attr, guint32 fg, guint32 bg)
{
gshort pair = teco_color_pair(&attr, fg, bg);
return wattr_set(win, attr, pair, NULL);
}
-/**
- * Translate a Scintilla-compatible RGB color value
- * (0xBBGGRR) to a Curses color triple (0 to 1000
- * for each component).
- */
-static inline void
-teco_rgb2curses_triple(guint32 rgb, gshort *r, gshort *g, gshort *b)
-{
- /* NOTE: We could also use 200/51 */
- *r = ((rgb & 0x0000FF) >> 0)*1000/0xFF;
- *g = ((rgb & 0x00FF00) >> 8)*1000/0xFF;
- *b = ((rgb & 0xFF0000) >> 16)*1000/0xFF;
-}
-
-/**
- * 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.
- */
-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;
- }
-
- return COLOR_WHITE;
-}
-
static gint
teco_xterm_version(void)
{
@@ -414,9 +466,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);
@@ -431,10 +480,9 @@ static void teco_interface_draw_info(void);
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.default_fg = 0xC0C0C0; /* white */
+ teco_interface.default_bg = 0x000000; /* black */;
teco_interface.stdin_orig = teco_interface.stdout_orig = teco_interface.stderr_orig = -1;
@@ -476,133 +524,17 @@ 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)
+void
+teco_interface_disable_palette(void)
{
- /*
- * No way to restore the palette, or it's
- * unnecessary (e.g. XCurses)
- */
+ teco_interface.use_palette = FALSE;
}
-#endif
-
void
-teco_interface_init_color(guint color, guint32 rgb)
+teco_interface_set_default_colors(gint32 fg, gint32 bg)
{
- 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.default_fg = fg;
+ teco_interface.default_bg = bg;
}
#ifdef CURSES_TTY
@@ -744,14 +676,41 @@ 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();
- /*
- * 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);
+ scintilla_set_color_offsets(MAX_COLORS, MAX_PAIRS);
+ if (!teco_interface.use_palette)
+ scintilla_disable_color_palette();
+#ifdef HAVE_USE_DEFAULT_COLORS
+ /* color -1 will be the terminal's default foreground or background color */
+ use_default_colors();
+#endif
+ scintilla_set_default_colors(teco_interface.default_fg,
+ teco_interface.default_bg);
+
+ 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
@@ -833,19 +792,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
@@ -877,7 +823,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
@@ -942,23 +887,23 @@ teco_interface_msg_literal(teco_msg_t type, const gchar *str, gsize len)
teco_interface_stdio_msg(type, str, len);
#endif
- gshort fg, bg;
+ guint32 fg, bg;
- fg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETBACK, STYLE_DEFAULT, 0));
+ fg = teco_interface_ssm(SCI_STYLEGETBACK, STYLE_DEFAULT, 0);
switch (type) {
default:
case TECO_MSG_USER:
- bg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETFORE, STYLE_DEFAULT, 0));
+ bg = teco_interface_ssm(SCI_STYLEGETFORE, STYLE_DEFAULT, 0);
break;
case TECO_MSG_INFO:
- bg = COLOR_GREEN;
+ bg = 0x008000; /* green */
break;
case TECO_MSG_WARNING:
- bg = COLOR_YELLOW;
+ bg = 0x008080; /* yellow */
break;
case TECO_MSG_ERROR:
- bg = COLOR_RED;
+ bg = 0x000080; /* red */
beep();
break;
}
@@ -975,8 +920,8 @@ teco_interface_msg_clear(void)
if (!teco_interface.input_pad) /* batch mode */
return;
- gshort fg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETBACK, STYLE_DEFAULT, 0));
- gshort bg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETFORE, STYLE_DEFAULT, 0));
+ guint32 fg = teco_interface_ssm(SCI_STYLEGETBACK, STYLE_DEFAULT, 0);
+ guint32 bg = teco_interface_ssm(SCI_STYLEGETFORE, STYLE_DEFAULT, 0);
wmove(teco_interface.msg_window, 0, 0);
teco_wattr_set(teco_interface.msg_window, 0, fg, bg);
@@ -1004,15 +949,30 @@ teco_interface_getch(gboolean widechar)
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;
@@ -1153,8 +1113,8 @@ teco_interface_draw_info(void)
* the current buffer's STYLE_DEFAULT.
* The same style is used for MSG_USER messages.
*/
- gshort fg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETBACK, STYLE_DEFAULT, 0));
- gshort bg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETFORE, STYLE_DEFAULT, 0));
+ guint32 fg = teco_interface_ssm(SCI_STYLEGETBACK, STYLE_DEFAULT, 0);
+ guint32 bg = teco_interface_ssm(SCI_STYLEGETFORE, STYLE_DEFAULT, 0);
wmove(teco_interface.info_window, 0, 0);
teco_wattr_set(teco_interface.info_window, 0, fg, bg);
@@ -1726,8 +1686,8 @@ teco_interface_popup_show(gsize prefix_len)
/* batch mode */
return;
- gshort fg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETFORE, STYLE_CALLTIP, 0));
- gshort bg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETBACK, STYLE_CALLTIP, 0));
+ guint32 fg = teco_interface_ssm(SCI_STYLEGETFORE, STYLE_CALLTIP, 0);
+ guint32 bg = teco_interface_ssm(SCI_STYLEGETBACK, STYLE_CALLTIP, 0);
teco_interface.popup_prefix_len = prefix_len;
attr_t attr = 0;
@@ -1783,7 +1743,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 */
@@ -1802,7 +1762,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.
@@ -1817,7 +1777,7 @@ teco_interface_is_interrupted(void)
GINT_TO_POINTER(key));
}
- return teco_interrupted != FALSE;
+ return teco_interrupted == TECO_INTERRUPTED;
}
#endif
@@ -1932,8 +1892,8 @@ teco_interface_process_mevent(MEVENT *event, GError **error)
else if (event->bstate & BUTTON_NUM(5))
teco_curses_info_popup_scroll(&teco_interface.popup, +2);
- gshort fg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETFORE, STYLE_CALLTIP, 0));
- gshort bg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETBACK, STYLE_CALLTIP, 0));
+ guint32 fg = teco_interface_ssm(SCI_STYLEGETFORE, STYLE_CALLTIP, 0);
+ guint32 bg = 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);
@@ -2106,7 +2066,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 */
@@ -2122,6 +2082,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.
*
@@ -2227,21 +2212,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
@@ -2373,6 +2346,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);
diff --git a/src/interface-gtk/interface.c b/src/interface-gtk/interface.c
index 08ccf5d..1f6259f 100644
--- a/src/interface-gtk/interface.c
+++ b/src/interface-gtk/interface.c
@@ -78,7 +78,7 @@ static void teco_interface_popup_clicked_cb(GtkWidget *popup, gchar *str, gulong
gpointer user_data);
static gboolean teco_interface_window_delete_cb(GtkWidget *widget, GdkEventAny *event,
gpointer user_data);
-static gboolean teco_interface_sigterm_handler(gpointer user_data) G_GNUC_UNUSED;
+static gboolean teco_interface_termination_handler(gpointer user_data) G_GNUC_UNUSED;
static gchar teco_interface_get_ansi_key(GdkEventKey *event);
#define TECO_UNNAMED_FILE "(Unnamed)"
@@ -404,7 +404,8 @@ teco_interface_get_options(void)
return group;
}
-void teco_interface_init_color(guint color, guint32 rgb) {}
+void teco_interface_disable_palette(void) {}
+void teco_interface_set_default_colors(gint32 fg, gint32 bg) {}
void
teco_interface_msg_literal(teco_msg_t type, const gchar *str, gsize len)
@@ -451,14 +452,15 @@ teco_interface_getch_commit_cb(GtkIMContext *context, gchar *str, gpointer user_
*/
*cp = g_utf8_get_char_validated(str, -1);
g_assert(*cp >= 0);
- gtk_main_quit();
+
+ /* we might be invoked outside of a nested event loop */
+ if (gtk_main_level() > 1)
+ gtk_main_quit();
}
static gboolean
-teco_interface_getch_input_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data)
+teco_interface_getch_process_event(GdkEvent *event, teco_int_t *cp)
{
- teco_int_t *cp = user_data;
-
g_assert(event->type == GDK_KEY_PRESS);
switch (event->key.keyval) {
@@ -476,19 +478,34 @@ teco_interface_getch_input_cb(GtkWidget *widget, GdkEvent *event, gpointer user_
*cp = TECO_CTL_KEY(g_ascii_toupper(*cp));
switch (*cp) {
case TECO_CTL_KEY('C'):
- teco_interrupted = TRUE;
+ teco_interrupted = TECO_INTERRUPTED;
/* fall through */
case TECO_CTL_KEY('D'):
+ /* emulates EOF on stdin */
*cp = -1;
}
break;
}
+ /*
+ * NOTE: The teco_interface_getch_commit_cb() could be called immediately
+ * without returning to the event loop.
+ */
gtk_im_context_filter_keypress(teco_interface.input_method, &event->key);
- return TRUE;
+ return *cp >= 0;
}
- gtk_main_quit();
+ return TRUE;
+}
+
+static gboolean
+teco_interface_getch_input_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data)
+{
+ teco_int_t *cp = user_data;
+
+ if (teco_interface_getch_process_event(event, cp))
+ gtk_main_quit();
+
return TRUE;
}
@@ -513,6 +530,22 @@ teco_interface_getch(gboolean widechar)
G_CALLBACK(teco_interface_getch_commit_cb), &cp);
/*
+ * The original teco_interface_input_cb() could have already enqueued events
+ * (also between ^T calls).
+ * This must be done after registering teco_interface_getch_commit_cb() already.
+ *
+ * NOTE: Already enqueued mouse events will currently be discarded silently.
+ */
+ for (;;) {
+ g_autoptr(GdkEvent) event = g_queue_pop_head(teco_interface.event_queue);
+ if (!event)
+ break;
+ if (event->type == GDK_KEY_PRESS &&
+ teco_interface_getch_process_event(event, &cp))
+ goto cleanup;
+ }
+
+ /*
* Highlights the first character in the label.
* This mimics what the Curses UI does.
* Is there a better way to signal that we expect input?
@@ -526,6 +559,7 @@ teco_interface_getch(gboolean widechar)
gdk_window_freeze_updates(top_window);
+cleanup:
g_signal_handler_disconnect(teco_interface.input_method, commit_handler);
g_signal_handlers_unblock_by_func(teco_interface.input_method, teco_interface_cmdline_commit_cb, NULL);
g_signal_handler_disconnect(teco_interface.window, key_handler);
@@ -790,7 +824,7 @@ teco_interface_is_interrupted(void)
{
if (!gtk_main_level())
/* batch mode */
- return teco_interrupted != FALSE;
+ return teco_interrupted == TECO_INTERRUPTED;
/*
* By polling only every TECO_POLL_INTERVAL microseconds
@@ -800,11 +834,11 @@ teco_interface_is_interrupted(void)
guint64 now_ts = g_get_monotonic_time();
if (G_LIKELY(last_poll_ts+TECO_POLL_INTERVAL > now_ts))
- return teco_interrupted != FALSE;
+ return teco_interrupted == TECO_INTERRUPTED;
last_poll_ts = now_ts;
gtk_main_iteration_do(FALSE);
- return teco_interrupted != FALSE;
+ return teco_interrupted == TECO_INTERRUPTED;
}
void
@@ -1265,13 +1299,12 @@ teco_interface_event_loop(GError **error)
#endif
/*
- * SIGTERM emulates the "Close" key just like when
- * closing the window if supported by this version of glib.
- * Note that this replaces SciTECO's default SIGTERM handler
- * so it will additionally raise(SIGINT).
+ * SIGTERM and SIGHUP terminate the program, but will
+ * dump recovery files first (if enabled).
*/
#ifdef G_OS_UNIX
- g_unix_signal_add(SIGTERM, teco_interface_sigterm_handler, NULL);
+ g_unix_signal_add(SIGTERM, teco_interface_termination_handler, NULL);
+ g_unix_signal_add(SIGHUP, teco_interface_termination_handler, NULL);
#endif
/* the interval might have been changed in the profile */
@@ -1395,7 +1428,7 @@ teco_interface_input_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data)
* If the execution thread is currently blocking,
* the key is delivered like an ordinary key press.
*/
- teco_interrupted = TRUE;
+ teco_interrupted = TECO_INTERRUPTED;
else
g_queue_push_tail(teco_interface.event_queue,
gdk_event_copy(event));
@@ -1436,7 +1469,7 @@ teco_interface_input_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data)
const teco_view_t *last_view = teco_interface_current_view;
sptr_t last_vpos = teco_interface_ssm(SCI_GETFIRSTVISIBLELINE, 0, 0);
- teco_interrupted = FALSE;
+ teco_interrupted = TECO_NORMAL;
switch (event->type) {
case GDK_KEY_PRESS:
teco_interface_handle_key_press(&event->key, &error);
@@ -1453,7 +1486,7 @@ teco_interface_input_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data)
default:
g_assert_not_reached();
}
- teco_interrupted = FALSE;
+ teco_interrupted = TECO_NORMAL;
teco_interface_update(teco_interface_current_view != last_view);
/* always expand folds, even after mouse clicks */
@@ -1563,16 +1596,23 @@ teco_interface_window_delete_cb(GtkWidget *widget, GdkEventAny *event, gpointer
}
static gboolean
-teco_interface_sigterm_handler(gpointer user_data)
+teco_interface_termination_handler(gpointer user_data)
{
+ teco_interface_msg(TECO_MSG_WARNING, "Received termination signal - dumping recovery files");
+ teco_interrupted = TECO_TERMINATED;
+
/*
- * Similar to window deletion - emulate "close" key press.
+ * This may be an emergency shutdown or another kind of
+ * termination that we cannot ignore, so better dump
+ * recovery files before terminating.
*/
- GtkWidget *widget = teco_interface.window;
+ if (teco_ring_recovery_interval != 0)
+ teco_ring_dump_recovery();
- g_autoptr(GdkEvent) close_event = gdk_event_new(GDK_KEY_PRESS);
- close_event->key.window = gtk_widget_get_parent_window(widget);
- close_event->key.keyval = GDK_KEY_Close;
+ /*
+ * Otherwise we terminate and clean up as if by `-EX`.
+ */
+ gtk_main_quit();
- return teco_interface_input_cb(widget, close_event, NULL);
+ return G_SOURCE_REMOVE;
}
diff --git a/src/interface.h b/src/interface.h
index 54f807b..a7968d1 100644
--- a/src/interface.h
+++ b/src/interface.h
@@ -57,13 +57,17 @@ void teco_interface_init(gint argc, gchar **argv);
GOptionGroup *teco_interface_get_options(void);
/** @pure makes sense only on Curses */
-void teco_interface_init_color(guint color, guint32 rgb);
+void teco_interface_disable_palette(void);
+
+/** @pure makes sense only on Curses */
+void teco_interface_set_default_colors(gint32 fg, gint32 bg);
typedef enum {
- TECO_MSG_USER,
+ TECO_MSG_USER = 0,
TECO_MSG_INFO,
TECO_MSG_WARNING,
- TECO_MSG_ERROR
+ TECO_MSG_ERROR,
+ TECO_MSG_MAX = TECO_MSG_ERROR
} teco_msg_t;
extern teco_msg_t teco_interface_msg_level;
diff --git a/src/main.c b/src/main.c
index 8bc02f1..29ef024 100644
--- a/src/main.c
+++ b/src/main.c
@@ -66,12 +66,15 @@
teco_int_t teco_ed = TECO_ED_AUTOEOL;
/**
- * Whether there was an asyncronous interruption (usually after pressing CTRL+C).
+ * Whether there was an asynchronous interruption (usually after
+ * pressing CTRL+C) or termination (SIGTERM).
* However you should always use teco_interface_is_interrupted(),
* to check for interruptions because of its side effects.
* This variable is safe to set to TRUE from signal handlers and threads.
+ *
+ * This is a teco_interrupted_t.
*/
-volatile sig_atomic_t teco_interrupted = FALSE;
+volatile sig_atomic_t teco_interrupted = TECO_NORMAL;
/*
* FIXME: Move this into file-utils.c?
@@ -346,7 +349,7 @@ teco_initialize_environment(void)
static void
teco_sigint_handler(int signal)
{
- teco_interrupted = TRUE;
+ teco_interrupted = TECO_INTERRUPTED;
}
#ifdef G_OS_WIN32
@@ -385,8 +388,21 @@ main(int argc, char **argv)
getchar();
#endif
+ /*
+ * FIXME: Gracefully handle SIGTERM.
+ * This however would require polling for the flag in Curses
+ * getch() loops.
+ * GTK already catches SIGTERM.
+ */
+#ifdef HAVE_SIGACTION
+ static const struct sigaction sigint = {
+ .sa_handler = teco_sigint_handler,
+ .sa_flags = 0 /* don't auto-restart syscalls */
+ };
+ sigaction(SIGINT, &sigint, NULL);
+#else
signal(SIGINT, teco_sigint_handler);
- signal(SIGTERM, teco_sigint_handler);
+#endif
/*
* Important for Unicode handling in curses and glib.
diff --git a/src/ring.c b/src/ring.c
index 8bb825c..9feb444 100644
--- a/src/ring.c
+++ b/src/ring.c
@@ -143,7 +143,12 @@ teco_buffer_save(teco_buffer_t *ctx, const gchar *filename, GError **error)
static inline void
teco_buffer_free(teco_buffer_t *ctx)
{
- if (ctx->state > TECO_BUFFER_DIRTY_NO_DUMP) {
+ /*
+ * During graceful, but unexpected shutdowns (SIGTERM etc.),
+ * we must preserve the recovery files.
+ */
+ if (ctx->state > TECO_BUFFER_DIRTY_NO_DUMP &&
+ teco_interrupted != TECO_TERMINATED) {
g_autofree gchar *filename_recovery = teco_buffer_get_recovery(ctx);
g_unlink(filename_recovery);
}
@@ -317,7 +322,8 @@ guint teco_ring_recovery_interval = 2*60;
/**
* Create recovery files for all dirty buffers.
*
- * Should be called by the interface every teco_ring_recovery_interval seconds.
+ * Should be called by the interface every teco_ring_recovery_interval seconds
+ * or before graceful terminations (SIGTERM etc.).
* This does not generate or expect undo tokens, so it can be called
* even when idlying.
*/
diff --git a/src/sciteco.h b/src/sciteco.h
index 16dba69..88078c2 100644
--- a/src/sciteco.h
+++ b/src/sciteco.h
@@ -109,6 +109,12 @@ teco_default_codepage(void)
return teco_ed & TECO_ED_DEFAULT_ANSI ? SC_CHARSET_ANSI : SC_CP_UTF8;
}
+typedef enum {
+ TECO_NORMAL = 0,
+ TECO_INTERRUPTED,
+ TECO_TERMINATED
+} teco_interrupted_t;
+
/* in main.c */
extern volatile sig_atomic_t teco_interrupted;
diff --git a/src/search.c b/src/search.c
index 856d079..89bf688 100644
--- a/src/search.c
+++ b/src/search.c
@@ -603,6 +603,11 @@ teco_do_search(GRegex *re, gsize from, gsize to, gint *count, GError **error)
g_propagate_error(error, tmp_error);
return FALSE;
}
+
+ if (G_UNLIKELY(teco_interface_is_interrupted())) {
+ teco_error_interrupted_set(error);
+ return FALSE;
+ }
}
if (!*count)
@@ -659,6 +664,13 @@ teco_do_search(GRegex *re, gsize from, gsize to, gint *count, GError **error)
return FALSE;
}
+ if (G_UNLIKELY(teco_interface_is_interrupted())) {
+ teco_error_interrupted_set(error);
+ for (int i = 0; i < matched_num; i++)
+ g_free(matched[i].ranges);
+ return FALSE;
+ }
+
i = ++matched_total % -(*count);
}
diff --git a/src/spawn.c b/src/spawn.c
index 61718fd..716bafa 100644
--- a/src/spawn.c
+++ b/src/spawn.c
@@ -869,7 +869,7 @@ teco_spawn_idle_cb(gpointer user_data)
{
if (G_LIKELY(!teco_interface_is_interrupted()))
return G_SOURCE_CONTINUE;
- teco_interrupted = FALSE;
+ teco_interrupted = TECO_NORMAL;
/*
* The first CTRL+C will try to gracefully terminate the process.
diff --git a/src/stdio-commands.c b/src/stdio-commands.c
index abb6566..c2a1c16 100644
--- a/src/stdio-commands.c
+++ b/src/stdio-commands.c
@@ -255,17 +255,42 @@ teco_state_print_string_initial(teco_machine_main_t *ctx, GError **error)
static teco_state_t *
teco_state_print_string_done(teco_machine_main_t *ctx, teco_string_t str, GError **error)
{
- teco_interface_msg_literal(TECO_MSG_USER, str.data, str.len);
+ teco_int_t type;
+
+ if (ctx->flags.mode > TECO_MODE_NORMAL)
+ return &teco_state_start;
+
+ if (!teco_expressions_pop_num_calc(&type, TECO_MSG_USER, error))
+ return NULL;
+ if (type < TECO_MSG_USER || type > TECO_MSG_MAX) {
+ g_set_error_literal(error, TECO_ERROR, TECO_ERROR_FAILED,
+ "Invalid message level specified for <^A>");
+ return NULL;
+ }
+
+ teco_interface_msg_literal(type, str.data, str.len);
return &teco_state_start;
}
/*$ "^A" ":^A" print "print string"
- * ^A<string>^A -- Print string as message
- * @^A/string/
- * :^A<string>^A
+ * [type]^A<string>^A -- Print string as message
+ * [type]@^A/string/
+ * [type]:^A<string>^A
*
- * Print <string> as a message, i.e. in the message line
- * in interactive mode and if possible on the terminal (stdout) as well.
+ * Print <string> as a message with severity <type>, i.e. in the
+ * message line in interactive mode and if possible on the terminal
+ * as well.
+ * The following message levels are supported:
+ * .IP 0: 3
+ * User-level message:
+ * They are written without any prefixes directly to stdout.
+ * This is the default if <type> is omitted.
+ * .IP 1:
+ * Info-level message
+ * .IP 2:
+ * Warnings
+ * .IP 3:
+ * Errors
*
* \fB^A\fP differs from all other commands in the way <string>
* is terminated.
diff --git a/src/view.c b/src/view.c
index a522d1c..8094186 100644
--- a/src/view.c
+++ b/src/view.c
@@ -109,12 +109,12 @@ teco_view_setup(teco_view_t *ctx)
teco_view_ssm(ctx, SCI_SETCARETSTYLE,
CARETSTYLE_BLOCK | CARETSTYLE_OVERSTRIKE_BLOCK | CARETSTYLE_BLOCK_AFTER, 0);
teco_view_ssm(ctx, SCI_SETCARETPERIOD, 0, 0);
- teco_view_ssm(ctx, SCI_SETCARETFORE, 0xFFFFFF, 0);
+ teco_view_ssm(ctx, SCI_SETCARETFORE, 0xC0C0C0, 0);
teco_view_ssm(ctx, SCI_SETSELFORE, TRUE, 0x000000);
- teco_view_ssm(ctx, SCI_SETSELBACK, TRUE, 0xFFFFFF);
+ teco_view_ssm(ctx, SCI_SETSELBACK, TRUE, 0xC0C0C0);
- teco_view_ssm(ctx, SCI_STYLESETFORE, STYLE_DEFAULT, 0xFFFFFF);
+ teco_view_ssm(ctx, SCI_STYLESETFORE, STYLE_DEFAULT, 0xC0C0C0);
teco_view_ssm(ctx, SCI_STYLESETBACK, STYLE_DEFAULT, 0x000000);
teco_view_ssm(ctx, SCI_STYLESETFONT, STYLE_DEFAULT, (sptr_t)"Monospace");
teco_view_ssm(ctx, SCI_STYLECLEARALL, 0, 0);
@@ -132,7 +132,7 @@ teco_view_setup(teco_view_t *ctx)
* default if no color-scheme is applied (and --no-profile).
*/
teco_view_ssm(ctx, SCI_STYLESETFORE, STYLE_CALLTIP, 0x000000);
- teco_view_ssm(ctx, SCI_STYLESETBACK, STYLE_CALLTIP, 0xFFFFFF);
+ teco_view_ssm(ctx, SCI_STYLESETBACK, STYLE_CALLTIP, 0xC0C0C0);
/*
* Since we have patched out Scintilla's original SetRepresentations(),
@@ -234,8 +234,8 @@ teco_view_load_from_channel(teco_view_t *ctx, GIOChannel *channel,
*/
guint cp = teco_view_get_codepage(ctx);
if (cp == SC_CP_UTF8)
- teco_interface_ssm(SCI_RELEASELINECHARACTERINDEX,
- SC_LINECHARACTERINDEX_UTF32, 0);
+ teco_view_ssm(ctx, SCI_RELEASELINECHARACTERINDEX,
+ SC_LINECHARACTERINDEX_UTF32, 0);
teco_view_ssm(ctx, SCI_BEGINUNDOACTION, 0, 0);
if (clear) {
@@ -314,8 +314,8 @@ cleanup:
teco_view_ssm(ctx, SCI_ENDUNDOACTION, 0, 0);
if (cp == SC_CP_UTF8)
- teco_interface_ssm(SCI_ALLOCATELINECHARACTERINDEX,
- SC_LINECHARACTERINDEX_UTF32, 0);
+ teco_view_ssm(ctx, SCI_ALLOCATELINECHARACTERINDEX,
+ SC_LINECHARACTERINDEX_UTF32, 0);
return ret;
}