aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--INSTALL9
-rw-r--r--README2
-rw-r--r--configure.ac41
m---------contrib/scinterm0
m---------contrib/scintilla0
-rw-r--r--doc/tutorial.ms.in4
-rw-r--r--fallback.teco_inibin2511 -> 2607 bytes
-rw-r--r--freebsd/pkg-plist1
-rw-r--r--lib/Makefile.am2
-rw-r--r--lib/fnkeys.tesbin4391 -> 5102 bytes
-rw-r--r--lib/tank.tes211
-rw-r--r--m4/ax_with_ncurses.m44
-rw-r--r--src/cmdline.c8
-rw-r--r--src/core-commands.c37
-rw-r--r--src/interface-curses/curses-info-popup.c32
-rw-r--r--src/interface-curses/curses-info-popup.h2
-rw-r--r--src/interface-curses/curses-utils.c16
-rw-r--r--src/interface-curses/curses-utils.h9
-rw-r--r--src/interface-curses/interface.c183
-rw-r--r--src/interface-gtk/interface.c76
-rw-r--r--src/interface.h2
-rw-r--r--src/main.c7
-rw-r--r--src/qreg.c6
-rw-r--r--src/ring.c9
-rw-r--r--tests/testsuite.at13
-rwxr-xr-xwww/build.tes25
26 files changed, 521 insertions, 178 deletions
diff --git a/INSTALL b/INSTALL
index a44ee66..3816fde 100644
--- a/INSTALL
+++ b/INSTALL
@@ -21,13 +21,14 @@ SciTECO Build and Runtime Dependencies
* Glib 2 as a cross-platform runtime library (v2.44 or later):
https://developer.gnome.org/glib/
* When choosing the Curses interface, you need one of:
- * NCurses (http://www.gnu.org/software/ncurses/).
- If you plan to use the ncurses MinGW port,
- I recommend ncurses 6.0 or later.
+ * ncurses (http://www.gnu.org/software/ncurses/).
+ Should be built with wide-character support (--enable-widec).
+ If you plan to use the ncurses MinGW port, I recommend ncurses 6.0 or later.
* NetBSD Curses (https://github.com/sabotage-linux/netbsd-curses).
- This is the default on NetBSD.
+ This is the default on NetBSD and should always support wide characters.
* PDCursesMod v4.5.4 or later (https://github.com/Bill-Gray/PDCursesMod.git).
This is the recommended flavor of PDCurses to use.
+ It must be built with WIDE=Y and UTF8=Y.
* PDCurses/EMCurses (https://github.com/rhaberkorn/emcurses).
* PDCurses/XCurses (http://pdcurses.sourceforge.net/).
Note that XCurses v3.4 appears to be broken, you may have to
diff --git a/README b/README
index 4e1fd79..4ed071d 100644
--- a/README
+++ b/README
@@ -23,7 +23,7 @@ The Curses frontend is verified to work with [ncurses](https://www.gnu.org/softw
[PDCurses/XCurses](https://github.com/wmcbrine/PDCurses),
[PDCursesMod](https://github.com/Bill-Gray/PDCursesMod) and
[EMCurses](https://github.com/rhaberkorn/emcurses).
-All X/Open-compatible libraries should be supported.
+All X/Open-compatible libraries with enhanced and wide-character support should be supported.
SVr4 curses without enhanced definitions is **not** supported.
Linux, FreeBSD, NetBSD, [Mac OS X](https://sciteco.fmsbw.de/knowledge/Mac%20OS%20Support),
diff --git a/configure.ac b/configure.ac
index ed5e056..673fb33 100644
--- a/configure.ac
+++ b/configure.ac
@@ -215,7 +215,7 @@ case $host in
*-*-linux* | *-*-*bsd* | *-*-darwin* | *-*-cygwin* | *-*-haiku*)
# NOTE: Keep this on a single line for compatibility
# with ancient versions of Autoconf.
- AC_CHECK_FUNCS([realpath readlink pathconf fchown dup dup2 getpid open read kill mmap popen pclose isatty fork setsid], , [
+ AC_CHECK_FUNCS([realpath readlink pathconf fchown dup dup2 getpid open read kill mmap popen pclose isatty fork execv setsid], , [
AC_MSG_ERROR([Missing libc function])
])
AC_SEARCH_LIBS(dladdr, [dl], , [
@@ -289,7 +289,8 @@ case $INTERFACE in
case $INTERFACE in
ncurses)
# This gives precendence to the widechar version of ncurses,
- # which is necessary for Unicode support even when not using widechar APIs.
+ # which is necessary for Unicode support and when using more than
+ # 256 color pairs.
# However we also accept libncurses.so if it also contains the
# enhanced definitions.
# NOTE: This also defines CURSES_CFLAGS and CURSES_LIBS arguments,
@@ -303,6 +304,9 @@ case $INTERFACE in
CXXFLAGS="$CXXFLAGS $CURSES_CFLAGS"
LIBS="$LIBS $CURSES_LIBS"
+ # Scinterm cares about the correct flags itself.
+ AC_DEFINE(NCURSES_WIDECHAR, 1, [Provide wide-character functions])
+
AC_CHECK_FUNCS([tigetstr])
;;
@@ -312,10 +316,6 @@ case $INTERFACE in
# installed, we want this to be an explicit setting.
AC_DEFINE(NETBSD_CURSES, 1, [Building against netbsd-curses])
- CFLAGS="$CFLAGS $CURSES_CFLAGS"
- CXXFLAGS="$CXXFLAGS $CURSES_CFLAGS"
- LIBS="$LIBS $CURSES_LIBS"
-
if [[ "x$CURSES_LIBS" = "x" ]]; then
# libncurses.pc is only shipped by Void Linux' fork,
# not in NetBSD itself.
@@ -325,9 +325,12 @@ case $INTERFACE in
else
AC_MSG_CHECKING([checking for netbsd-curses (CURSES_LIBS)])
AC_MSG_RESULT([$CURSES_LIBS])
- LIBS="$LIBS $CURSES_LIBS"
fi
+ CFLAGS="$CFLAGS $CURSES_CFLAGS"
+ CXXFLAGS="$CXXFLAGS $CURSES_CFLAGS"
+ LIBS="$LIBS $CURSES_LIBS"
+
AC_CHECK_FUNCS([tigetstr])
;;
@@ -353,9 +356,11 @@ case $INTERFACE in
LIBS="$LIBS $CURSES_LIBS"
# It is crucial to define XCURSES before including curses.h.
+ # This is not important for Scinterm.
AC_DEFINE(XCURSES, 1, [Enable PDCurses/XCurses extensions])
AC_CHECK_FUNC([has_mouse], [
+ # not important to pass to Scinterm
AC_DEFINE(PDC_NCMOUSE, 1, [PDCurses built with ncurses mouse API])
])
@@ -363,9 +368,6 @@ case $INTERFACE in
;;
pdcurses*)
- CFLAGS="$CFLAGS $CURSES_CFLAGS"
- CXXFLAGS="$CXXFLAGS $CURSES_CFLAGS"
-
if [[ "x$CURSES_LIBS" = "x" ]]; then
AC_CHECK_LIB(pdcurses, PDC_get_version, , [
AC_MSG_ERROR([libpdcurses missing!])
@@ -373,21 +375,26 @@ case $INTERFACE in
else
AC_MSG_CHECKING([checking for PDCurses (CURSES_LIBS)])
AC_MSG_RESULT([$CURSES_LIBS])
- LIBS="$LIBS $CURSES_LIBS"
fi
# It is crucial to define PDC_WIDE before including curses.h.
# FIXME: MinGW has a pdcurses.h that already defines all necessary macros,
# but it's not in upstream PDCurses/PDCursesMod.
- AC_CHECK_FUNC([add_wch], [
- AC_DEFINE(PDC_WIDE, 1, [PDCurses built with wide-character support])
- # FIXME: It would be better to check for PDC_FORCE_UTF8.
- # Theoretically, we could check for endwin_u[32|64]_4302,
- # but I'm not sure this will work reliably in the future.
- AC_DEFINE(PDC_FORCE_UTF8, 1, [PDCursesMod forces use of UTF8])
+ # FIXME: It would be better to check for PDC_FORCE_UTF8.
+ # Theoretically, we could check for endwin_u[32|64]_4302,
+ # but I'm not sure this will work reliably in the future.
+ CURSES_CFLAGS="$CURSES_CFLAGS -DPDC_WIDE -DPDC_FORCE_UTF8"
+
+ CFLAGS="$CFLAGS $CURSES_CFLAGS"
+ CXXFLAGS="$CXXFLAGS $CURSES_CFLAGS"
+ LIBS="$LIBS $CURSES_LIBS"
+
+ AC_CHECK_FUNC([add_wch], [], [
+ AC_MSG_ERROR([libpdcurses does not include wide-character support!])
])
AC_CHECK_FUNC([has_mouse], [
+ # not important to pass to Scinterm
AC_DEFINE(PDC_NCMOUSE, 1, [PDCurses built with ncurses mouse API])
])
diff --git a/contrib/scinterm b/contrib/scinterm
-Subproject 26f5d3f5290a2910e44dec16d05da2db33c1272
+Subproject 7f5b0e2bdfb23a2806ed0cf9c6d25c1f37c0392
diff --git a/contrib/scintilla b/contrib/scintilla
-Subproject b64652b857d3a5922c72ccc801ac77aa9cff27c
+Subproject 748f9cbc4f2a9d7a4216feaa37aee8e834123f0
diff --git a/doc/tutorial.ms.in b/doc/tutorial.ms.in
index 92da0af..4016605 100644
--- a/doc/tutorial.ms.in
+++ b/doc/tutorial.ms.in
@@ -36,6 +36,10 @@
. ns
..
.
+.\" Work around problems with early device extension commands.
+.\" See groff(7) and https://savannah.gnu.org/bugs/?67992
+\c
+.
.\" The entire document is monospaced as users are supposed to
.\" navigate through it and perhaps even modify it inplace.
.SCITECO_TT
diff --git a/fallback.teco_ini b/fallback.teco_ini
index 229bc77..e1e263b 100644
--- a/fallback.teco_ini
+++ b/fallback.teco_ini
Binary files differ
diff --git a/freebsd/pkg-plist b/freebsd/pkg-plist
index ab74145..dd3e7cb 100644
--- a/freebsd/pkg-plist
+++ b/freebsd/pkg-plist
@@ -13,6 +13,7 @@ share/man/man7/%%PROGRAM_PREFIX%%sciteco.7.gz
%%DATADIR%%/lib/getopt.tes
%%DATADIR%%/lib/lexer.tes
%%DATADIR%%/lib/repl.tes
+%%DATADIR%%/lib/tank.tes
%%DATADIR%%/lib/tecat.tes
%%LEXILLA%%%%DATADIR%%/lib/lexers/abaqus.tes
%%LEXILLA%%%%DATADIR%%/lib/lexers/ada.tes
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 0da616a..04e83ad 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -1,5 +1,5 @@
dist_scitecolib_DATA = color.tes lexer.tes session.tes opener.tes \
- fnkeys.tes string.tes getopt.tes
+ fnkeys.tes string.tes getopt.tes tank.tes
# Standalone scripts.
# These are not installed via _SCRIPTS as it would add the --program-prefix.
diff --git a/lib/fnkeys.tes b/lib/fnkeys.tes
index 937caf9..f58a26a 100644
--- a/lib/fnkeys.tes
+++ b/lib/fnkeys.tes
Binary files differ
diff --git a/lib/tank.tes b/lib/tank.tes
new file mode 100644
index 0000000..8573e22
--- /dev/null
+++ b/lib/tank.tes
@@ -0,0 +1,211 @@
+!*
+ * TANK MODE
+ *
+ * Drive an ASCII tank around your buffer.
+ * Invoke via EIQ[$SCITECOPATH]/tank.tes
+ *!
+[tank.sprite.0] !* background *!
+
+!* FIXME: Perhaps encode the gun's top in the integer cell *!
+[tank.sprite.1]
+█▓█▓
+╠════○
+▓█▓█
+[tank.sprite.2]
+ █▓█▓
+○════╣
+ ▓█▓█
+[tank.sprite.3]
+ ┌┐
+▓││█
+█││▓
+▓┴┴█
+[tank.sprite.4]
+█┬┬▓
+▓││█
+█││▓
+ └┘
+
+!*
+ * f,tM[tank.rand] - LCG pseudo random number generator for values between f and t
+ * This is based on the version in the BSD libc.
+ *!
+::U[tank.rand]
+@[tank.rand]{
+ U.tU.f
+ (1103515245*Q[tank.rand]+12345) & (2^*31-1)U[tank.rand]
+ (Q[tank.rand]^/(Q.t-Q.f+1)+Q.f)
+}
+
+!* x,yM[tank.goto] -> success|failure *!
+@[tank.goto]{
+ U.yU.x
+ Q.x"< 0 ' Q.y"< 0 '
+ J Q.y:L"F 0 '
+ Q.x<0A"< I  F> ' 0A-10"= I  F> ' C>
+ -1
+}
+
+!* x,y,spriteM[tank.blit] *!
+@[tank.blit]{
+ U.sU.yU.x Q.xU.#x0
+ Q.s"N
+ 10[tank.sprite.0] !* background *!
+ %[tank.blit]
+ '
+ <
+ %.iQ[tank.sprite.\.s]U.c Q.c:;
+ Q[tank.blit]&1"=
+ !* toggle the chain character to simulate movements *!
+ Q.c-█"= ▓U.c | Q.c-▓"= █U.c ' '
+ '
+
+ Q.c-10"=
+ %.y Q.#x0U.x
+ Q.s"N 10:[tank.sprite.0] '
+ |
+ Q.x,Q.yM[tank.goto];
+ Q.s"N !* not background sprite *!
+ Q.c- "=
+ !* space is "transparent" - copy background unmodified *!
+ 0A"< I  | 0A-10"= I  | C ' '
+ -A:[tank.sprite.0]
+ %.x F<
+ '
+ !* "destroy" fields we have touched *!
+  U.d
+ 0A"F 0A-10"N 0A- "N 33,126M[tank.rand]U.d ' ' '
+ Q.d:[tank.sprite.0]
+ '
+ 0A"F 0A-10"N D ' ' Q.cI
+ %.x
+ '
+ >
+}
+
+@[tank.explode]{
+ !!.[explosion]*oO%. 
+ .[explosion]✹✸✶✧· 
+ :Q.[explosion]<
+ 0A"F 0A-10"N D ' '
+ %.i-1Q.[explosion]IR
+ >
+}
+
+0U[tank.score]
+
+@[tank.shoot]{
+ U.#dr U.yU.x
+
+ !* FIXME: Could also be encoded into a map *!
+ Q.#dr-1Oright,left,up,down
+ !right!
+ 6-1%.x %.y
+ 1U.#dx 0U.#dy
+ Oend
+ !left!
+ %.y
+ -U.#dx 0U.#dy
+ Oend
+ !up!
+ 2-1%.x
+ 0U.#dx -U.#dy
+ Oend
+ !down!
+ 2-1%.x 3%.y
+ 0U.#dx 1U.#dy
+ !* fall through *!
+ !end!
+ <
+ (Q.#dx%.x),(Q.#dy%.y)M[tank.goto]"F  '
+ 0A"<  ' Q.#dy"= 0A-10"=  ' '
+ 0A- "N 0A-10"N 1; ' '
+ 0A"F 0A-10"N D ' ' I●
+  -DI 
+ >
+ M[tank.explode]
+
+ %[tank.score] SCORE: \[tank.score]
+}
+
+@[tank.selfdestruct]{
+ U.yU.x Q.x+2U.#cx Q.y+2U.#cy
+ .[explosion]✹✹✸✸✶✶✧✧· 
+ 10<
+ Q.#cx-10U.x 20<
+ Q.x-1"< %.x F> '
+ Q.#cy-10U.y 20<
+ !* d = sqrt((x - cx)^2 + ((y - cy) * aspect)^2) *!
+ (Q.x-Q.#cx)^*2 + ((Q.y-Q.#cy)*2)^*2 U.#d2
+
+ Q.r^*2 - Q.#d2"<
+ Q.#d2 - (Q.r+2)^*2"<
+ !* on shock wave *!
+ Q.x,Q.yM[tank.goto]"F %.y F> '
+ -D Q.rQ.[explosion]I
+ '
+ |
+ !* within shock wave *!
+ Q.x,Q.yM[tank.goto]"F %.y F> '
+ -D I 
+ '
+ %.y>
+ %.x>
+ 1
+ %.r>
+ Q.#cx,Q.#cyM[tank.goto]
+}
+
+ESGETCOLUMNU.x :-1U.y
+Q.xU.#xo Q.yU.#yo
+
+!*
+ * Normalize identations to spaces -- assumed by M[goto]
+ * FIXME: Will not work well with character representations.
+ *!
+ESGETTABDRAWMODE-2"N
+ J <S^I;
+ ESGETCOLUMN-(-1ESGETCOLUMN)U.w
+ -D Q.w<I >
+ >
+'
+
+CONTROL: W A S D. SHOOT: SPACE. QUIT: ESCAPE. PRESS ANY KEY TO START. 
+
+ESGETCARETSTYLEU.[caretstyle]
+0ESSETCARETSTYLE
+
+1U.[sprite]
+<
+ Q.#xo,Q.#yo,0M[tank.blit] !* redraw background *!
+ Q.xU.#xo Q.yU.#yo
+
+ Q.x,Q.y,Q.[sprite]M[tank.blit]
+ 0 U.c
+ :Okey.U.c
+ F>
+ !key.w!
+ Q.y"> -%.y ' 3U.[sprite]
+ F>
+ !key.a!
+ Q.x,Q.y,0M[tank.blit] !* redraw background *!
+ Q.x"> -%.x ' 2U.[sprite]
+ F>
+ !key.s!
+ Q.x,Q.y,0M[tank.blit] !* redraw background *!
+ %.y 4U.[sprite]
+ F>
+ !key.d!
+ Q.x,Q.y,0M[tank.blit] !* redraw background *!
+ %.x 1U.[sprite]
+ F>
+ !key. !
+ !* FIXME: this also scrolls due to  *!
+ Q.x,Q.y,Q.[sprite]M[tank.shoot]
+ F>
+ !key.!
+ Q.x,Q.yM[tank.selfdestruct]
+ 1;
+>
+
+Q.[caretstyle]ESSETCARETSTYLE
diff --git a/m4/ax_with_ncurses.m4 b/m4/ax_with_ncurses.m4
index 4dc2f33..cdcfd04 100644
--- a/m4/ax_with_ncurses.m4
+++ b/m4/ax_with_ncurses.m4
@@ -5,8 +5,8 @@
# DESCRIPTION
#
# This macro checks for an ncurses library with enhanced definitions
-# providing a curses.h either in the default search path or as
-# established by pkg-config.
+# (including wide-char support) providing a curses.h either in the default
+# search path or as established by pkg-config.
#
# It is based on the AX_WITH_CURSES macro but does not attempt
# to find any non-standard header, which would require #ifdefing
diff --git a/src/cmdline.c b/src/cmdline.c
index 9a7b356..b944b5e 100644
--- a/src/cmdline.c
+++ b/src/cmdline.c
@@ -99,6 +99,14 @@ teco_cmdline_init(void)
teco_cmdline_ssm(SCI_SETTABDRAWMODE, SCTD_CONTROLCHAR, 0);
/*
+ * FIXME: This works around a problem where the caret is not
+ * scrolled in multi-line mode after it wraps at the end of the
+ * line. You cannot scroll the command-line with the mouse, so the
+ * user won't see any difference.
+ */
+ teco_cmdline_ssm(SCI_SETENDATLASTLINE, FALSE, 0);
+
+ /*
* FIXME: Something resets the margin text, so we have to set it last.
*/
teco_cmdline_ssm(SCI_MARGINSETTEXT, 0, (sptr_t)"*");
diff --git a/src/core-commands.c b/src/core-commands.c
index cd9a8fa..528fa64 100644
--- a/src/core-commands.c
+++ b/src/core-commands.c
@@ -1739,8 +1739,10 @@ TECO_DEFINE_STATE(teco_state_ascii,
/*$ ^[^[ ^[$ $$ ^C terminate return
* [a1,a2,...]$$ -- Terminate command line or return from macro
+ * -$$
* [a1,a2,...]^[$
* [a1,a2,...]^C
+ * -^C
*
* Returns from the current macro invocation.
* This will pass control to the calling macro immediately
@@ -1754,6 +1756,8 @@ TECO_DEFINE_STATE(teco_state_ascii,
* All braces opened in the current macro invocation will
* be closed and their values discarded.
* Only the direct arguments to \fB$$\fP will be kept.
+ * If the prefix sign is set (\(lq-\fB$$\fP\(rq) the command
+ * will always leave -1 on the stack.
*
* Returning from the top-level macro in batch mode
* will exit the program or start up interactive mode depending
@@ -1806,6 +1810,9 @@ teco_return(teco_machine_main_t *ctx, GError **error)
ctx->parent.current = &teco_state_start;
if (!teco_expressions_eval(FALSE, error))
return NULL;
+ if (teco_num_sign < 0)
+ /* only if you wrote -$$ */
+ teco_expressions_push(1);
teco_error_return_set(error, teco_expressions_args());
return NULL;
}
@@ -1873,6 +1880,18 @@ TECO_DEFINE_STATE_START(teco_state_escape,
.end_of_macro_cb = teco_state_escape_end_of_macro
);
+static gboolean
+teco_state_ctlc_initial(teco_machine_main_t *ctx, GError **error)
+{
+ if (G_UNLIKELY(ctx == &teco_cmdline.machine)) {
+ g_set_error_literal(error, TECO_ERROR, TECO_ERROR_FAILED,
+ "<^C> is not allowed to terminate command-lines");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
/*
* Just like ^[, ^C actually implements a lookahead,
* so a ^C itself does nothing.
@@ -1892,20 +1911,20 @@ teco_state_ctlc_input(teco_machine_main_t *ctx, gunichar chr, GError **error)
}
static gboolean
-teco_state_ctlc_initial(teco_machine_main_t *ctx, GError **error)
+teco_state_ctlc_end_of_macro(teco_machine_main_t *ctx, GError **error)
{
- if (G_UNLIKELY(ctx == &teco_cmdline.machine)) {
- g_set_error_literal(error, TECO_ERROR, TECO_ERROR_FAILED,
- "<^C> is not allowed to terminate command-lines");
- return FALSE;
- }
-
+ if (ctx->flags.mode > TECO_MODE_NORMAL)
+ return TRUE;
+ if (teco_num_sign < 0)
+ /* only if you wrote -^C */
+ teco_expressions_push(1);
return TRUE;
}
TECO_DEFINE_STATE_START(teco_state_ctlc,
.initial_cb = (teco_state_initial_cb_t)teco_state_ctlc_initial,
- .input_cb = (teco_state_input_cb_t)teco_state_ctlc_input
+ .input_cb = (teco_state_input_cb_t)teco_state_ctlc_input,
+ .end_of_macro_cb = (teco_state_end_of_macro_cb_t)teco_state_ctlc_end_of_macro
);
static teco_state_t *
@@ -2169,7 +2188,7 @@ teco_state_ecommand_flags(teco_machine_main_t *ctx, GError **error)
* .IP 6:
* .SCITECO_TOPIC recovery
* Interval in seconds for the creation of recovery files
- * or 0 if those dumps are disabled (the default is 300 seconds).
+ * or 0 if those dumps are disabled (the default is 120 seconds).
* When enabled all dirty buffers are dumped to files with hash
* signs around the original basename (\fB#\fIfilename\fB#\fR).
* They are removed automatically when no longer required,
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 6c8b231..b49540b 100644
--- a/src/interface-curses/interface.c
+++ b/src/interface-curses/interface.c
@@ -137,6 +137,7 @@ 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);
/**
* Get bright variant of one of the 8 standard
@@ -233,63 +234,49 @@ static struct {
* Scinterm no longer initializes all color pairs for all combinations of
* the builtin foreground and background colors.
* Since curses does not guarantee any number of color pairs, we cannot do that either.
- * Instead we allocate color pairs beginnig at the second half of
- * color pair space on demand (similar to what Scinterm does).
- *
- * @note Scinterm now also has scintilla_set_color_offsets(),
- * so we could use the color pairs beginning with 1 as well.
+ * 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 allocated_pairs = 0;
+ 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 << 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);
- /*
- * We are still adding colors with COLOR_PAIR(), which allows
- * at most 256 color pairs (1-255).
- */
- gshort new_pair = MIN(COLOR_PAIRS, 256)/2 + allocated_pairs;
- if (G_UNLIKELY(new_pair >= 256))
+ if (G_UNLIKELY(next_pair >= COLOR_PAIRS || next_pair > G_MAXSHORT))
return 0;
- allocated_pairs++;
- init_pair(new_pair, fg, bg);
- g_hash_table_insert(teco_interface.pair_table, key, GUINT_TO_POINTER(new_pair));
- return new_pair;
+ 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 and contrast.tes
- * color schemes.
- */
- return bg != COLOR_BLACK ? A_REVERSE : 0;
+ gshort pair = teco_color_pair(&attr, fg, bg);
+ return wattr_set(win, attr, pair, NULL);
}
/**
@@ -443,7 +430,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;
@@ -457,7 +444,7 @@ 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);
/*
@@ -760,6 +747,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
@@ -950,7 +943,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));
@@ -972,7 +965,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);
}
@@ -983,11 +976,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);
}
@@ -1012,15 +1005,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 = TRUE;
+ /* 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;
@@ -1161,11 +1169,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;
@@ -1734,11 +1742,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
@@ -1938,9 +1948,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;
}
@@ -2126,6 +2138,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.
*
@@ -2231,22 +2268,10 @@ teco_interface_event_loop_iter(void)
* Control keys and keys with printable representation
*/
default:
- if (key > 0xFF)
- /* unhandled function key */
+ if (key > 0xFF || !teco_interface_check_key(key))
+ /* unhandled function key or bogus key press */
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))
- return;
-#endif
-
/*
* NOTE: There's also wget_wch(), but it requires
* a widechar version of Curses.
diff --git a/src/interface-gtk/interface.c b/src/interface-gtk/interface.c
index ace3a2f..1540245 100644
--- a/src/interface-gtk/interface.c
+++ b/src/interface-gtk/interface.c
@@ -22,6 +22,7 @@
#include <stdlib.h>
#include <string.h>
#include <signal.h>
+#include <unistd.h>
#include <glib.h>
#include <glib/gprintf.h>
@@ -132,10 +133,10 @@ static struct {
} teco_interface;
void
-teco_interface_init(void)
+teco_interface_init(gint argc, gchar **argv)
{
#ifdef G_OS_UNIX
- if (teco_interface.detach) {
+ if (teco_interface.detach && !g_getenv("__SCITECO_DETACHED")) {
/*
* NOTE: There is also daemon() on BSD/Linux,
* but the following should be more portable.
@@ -148,9 +149,29 @@ teco_interface_init(void)
setsid();
- g_freopen("/dev/null", "r", stdin);
- g_freopen("/dev/null", "a+", stdout);
- g_freopen("/dev/null", "a+", stderr);
+ if (isatty(0)) {
+ G_GNUC_UNUSED FILE *stdin_new = g_freopen("/dev/null", "r", stdin);
+ g_assert(stdin_new != NULL);
+ }
+ if (isatty(1)) {
+ G_GNUC_UNUSED FILE *stdout_new = g_freopen("/dev/null", "a+", stdout);
+ g_assert(stdout_new != NULL);
+ }
+ if (isatty(2)) {
+ G_GNUC_UNUSED FILE *stderr_new = g_freopen("/dev/null", "a+", stderr);
+ g_assert(stderr_new != NULL);
+ }
+
+ /*
+ * gtk_get_option_group() already initialized GTK and even though the
+ * display is not yet opened, it's unsafe to continue.
+ * Instead, we re-exec already in the child process.
+ * We cannot easily remove --detach from argv, but still guard against
+ * recursive forks by using an environment variable.
+ */
+ g_setenv("__SCITECO_DETACHED", "1", TRUE);
+ execv(argv[0], argv);
+ g_assert_not_reached();
}
#endif
@@ -430,14 +451,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) {
@@ -458,16 +480,31 @@ teco_interface_getch_input_cb(GtkWidget *widget, GdkEvent *event, gpointer user_
teco_interrupted = TRUE;
/* 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;
}
@@ -492,6 +529,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?
@@ -505,6 +558,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);
diff --git a/src/interface.h b/src/interface.h
index f196a83..54f807b 100644
--- a/src/interface.h
+++ b/src/interface.h
@@ -51,7 +51,7 @@
extern teco_view_t *teco_interface_current_view;
/** @pure */
-void teco_interface_init(void);
+void teco_interface_init(gint argc, gchar **argv);
/** @pure */
GOptionGroup *teco_interface_get_options(void);
diff --git a/src/main.c b/src/main.c
index 4d46817..8bc02f1 100644
--- a/src/main.c
+++ b/src/main.c
@@ -54,7 +54,7 @@
/*
* Define this to pause the program at the beginning
- * of main() (Windows only).
+ * of main().
* This is a useful hack on Windows, where gdbserver
* sometimes refuses to start SciTECO but attaches
* to a running process just fine.
@@ -382,8 +382,7 @@ main(int argc, char **argv)
#endif
#ifdef DEBUG_PAUSE
- /* Windows debugging hack (see above) */
- system("pause");
+ getchar();
#endif
signal(SIGINT, teco_sigint_handler);
@@ -445,7 +444,7 @@ main(int argc, char **argv)
*/
teco_qreg_table_init(&teco_qreg_table_globals, TRUE);
- teco_interface_init();
+ teco_interface_init(argc, argv);
/*
* QRegister view must be initialized only now
diff --git a/src/qreg.c b/src/qreg.c
index 4cf92f0..8ef82c0 100644
--- a/src/qreg.c
+++ b/src/qreg.c
@@ -1694,11 +1694,7 @@ teco_machine_qregspec_auto_complete(teco_machine_qregspec_t *ctx, teco_string_t
/* two-letter Q-Reg */
restrict_len = 2;
- /*
- * FIXME: This is not quite right as it will propose even
- * lower case single or two-letter Q-Register names.
- */
- return teco_rb3str_auto_complete(&ctx->result_table->tree, !restrict_len,
+ return teco_rb3str_auto_complete(&ctx->result_table->tree, TRUE,
ctx->name.data, ctx->name.len, restrict_len, insert) &&
ctx->nesting == 1;
}
diff --git a/src/ring.c b/src/ring.c
index 2445718..8bb825c 100644
--- a/src/ring.c
+++ b/src/ring.c
@@ -312,7 +312,7 @@ teco_ring_save_all_dirty_buffers(GError **error)
* Recovery creation interval in seconds or 0 if disabled.
* It's not currently enforced in batch mode.
*/
-guint teco_ring_recovery_interval = 5*60;
+guint teco_ring_recovery_interval = 2*60;
/**
* Create recovery files for all dirty buffers.
@@ -731,13 +731,18 @@ teco_state_read_file_done(teco_machine_main_t *ctx, teco_string_t str, GError **
return &teco_state_start;
sptr_t pos = teco_interface_ssm(SCI_GETCURRENTPOS, 0, 0);
+ teco_undo_int(teco_ranges[0].from) = teco_interface_bytes2glyphs(pos);
g_autofree gchar *filename = teco_file_expand_path(str.data);
/* FIXME: Add wrapper to interface.h? */
if (!teco_view_load(teco_interface_current_view, filename, FALSE, error))
return NULL;
- if (teco_interface_ssm(SCI_GETCURRENTPOS, 0, 0) != pos) {
+ pos = teco_interface_ssm(SCI_GETCURRENTPOS, 0, 0);
+ teco_undo_int(teco_ranges[0].to) = teco_interface_bytes2glyphs(pos);
+ teco_undo_guint(teco_ranges_count) = 1;
+
+ if (teco_ranges[0].from != teco_ranges[0].to) {
teco_ring_dirtify();
if (teco_current_doc_must_undo())
diff --git a/tests/testsuite.at b/tests/testsuite.at
index 23e2dab..6bc33e4 100644
--- a/tests/testsuite.at
+++ b/tests/testsuite.at
@@ -296,6 +296,13 @@ TE_CHECK([[@I/ABCDEF/J @FS/ABC/1234/ ^S+4"N(0/0)']], 0, ignore, ignore)
TE_CHECK([[@^Ua/XYZ/ Ga ^S+3"N(0/0)']], 0, ignore, ignore)
# NOTE: EN currently inserts another trailing linefeed.
TE_CHECK([[@EN/*/XYZ/ ^S+4"N(0/0)']], 0, ignore, ignore)
+AT_DATA([test.txt], [[0123456789
+]])
+TE_CHECK([[@ER"test.txt" ^S+11"N(0/0)']], 0, ignore, ignore)
+AT_CLEANUP
+
+AT_SETUP([Macro calls])
+TE_CHECK([[@^Ua{-$$} Ma+1"N(0/0)']], 0, ignore, ignore)
AT_CLEANUP
AT_SETUP([Editing local registers in macro calls])
@@ -664,6 +671,12 @@ TE_CHECK([[@^Um{U.a Q.a-100000"<%.aMm'} 0Mm]], 0, ignore, ignore)
AT_XFAIL_IF(true)
AT_CLEANUP
+AT_SETUP([Backtracking in patterns])
+# ^ES should be greedy and posessive
+TE_CHECK([[@I/ /J :@S/^ES^X/"S(0/0)']], 0, ignore, ignore)
+AT_XFAIL_IF(true)
+AT_CLEANUP
+
AT_SETUP([Rub out from empty string argument])
# Should rub out the modifiers as well.
# Will currently fail because it tries to execute `:@{`.
diff --git a/www/build.tes b/www/build.tes
index 848a63c..89ac70a 100755
--- a/www/build.tes
+++ b/www/build.tes
@@ -18,6 +18,7 @@
<html>
<head>
<title>SciTECO - &lt;Website&gt; Q[title]</title>
+ <link rel="canonical" href="https://sciteco.fmsbw.de/Q[file]">
<link rel="icon" type="image/x-icon" href="https://sciteco.fmsbw.de/graphics/sciteco.ico">
<meta name="description" content="Advanced TECO dialect and interactive screen editor based on Scintilla">
<style>
@@ -65,7 +66,7 @@
</html>
}
-EBindex.html HK
+[file] EBindex.html HK
[title]Home M[header]
EClowdown -thtml --html-no-skiphtml --html-no-escapehtml ../NEWS.md
I<hr>
@@ -73,7 +74,7 @@ EBindex.html HK
M[footer]
EW
-EBscreenshots.html HK
+[file]screenshots.html EBN[file] HK
[title]Screenshots M[header]
EClowdown -thtml --html-no-skiphtml --html-no-escapehtml screenshots.md
M[footer]
@@ -87,44 +88,44 @@ EW
* FIXME: Support out-of-tree builds.
* Perhaps pass in the biuld directory.
*!
-EBQ[builddir]/doc/sciteco.1.html
+[file]sciteco.1.html EBN[builddir]/doc/N[file]
S<body>S<h1  L 0,.K
[title]sciteco(1) M[header] G[grohtml-header]
FD<hr>S</body> .,ZK
M[footer]
-EWsciteco.1.html
+EWQ[file]
-EBQ[builddir]/doc/sciteco.7.html
+[file]sciteco.7.html EBN[builddir]/doc/N[file]
S<body>S<h1  L 0,.K
[title]sciteco(7) M[header] G[grohtml-header]
FD<hr>S</body> .,ZK
M[footer]
-EWsciteco.7.html
+EWQ[file]
!*
* These grohtml-generated documents are not in the header bar,
* but still postprocessed for consinstency.
*!
-EBQ[builddir]/doc/grosciteco.tes.1.html
+[file]grosciteco.tes.1.html EBN[builddir]/doc/N[file]
S<body>S<h1  L 0,.K
[title]grosciteco.tes(1) M[header] G[grohtml-header]
FD<hr>S</body> .,ZK
M[footer]
-EWgrosciteco.tes.1.html
+EWQ[file]
-EBQ[builddir]/doc/tedoc.tes.1.html
+[file]tedoc.tes.1.html EBN[builddir]/doc/N[file]
S<body>S<h1  L 0,.K
[title]tedoc.tes(1) M[header] G[grohtml-header]
FD<hr>S</body> .,ZK
M[footer]
-EWtedoc.tes.1.html
+EWQ[file]
-EBQ[builddir]/doc/tutorial.html
+[file]tutorial.html EBN[builddir]/doc/N[file]
S<body>S<h1  L 0,.K
[title]Tutorial M[header] G[grohtml-header]
FD<hr>S</body> .,ZK
M[footer]
-EWtutorial.html
+EWQ[file]
EX