Windows: official port -- UCRT static build, UTF-8 throughout, Low IL sandbox, ncurses TUI, installer; v0.6.0
Build
* cross build switches to pkgsCross.ucrt64 (shell-win.nix); curl (Schannel),
winpthreads and ncurses 6.6 are linked statically, so the release is
cst-agent.exe + busybox.exe (busybox-w32 Unicode) with no extra DLLs
* windres embeds a manifest with activeCodePage=UTF-8 and a version resource
* ncurses 6.6 Windows console driver patches (mouse button state, A_DIM,
wide-char declarations): scripts/patches/
* scripts/bootstrap_msys2.sh builds the same release natively in MSYS2
UCRT64; PR #1's MINGW64 notes now point there
* vendor/tre-regex (musl's TRE) provides <regex.h> on MinGW
UTF-8
* paths cross the system call boundary as UTF-16 (*W APIs); console code
pages set to 65001 and restored on exit; files are read and written in
binary mode
* child output that is not valid UTF-8 is decoded from the console, OEM or
ANSI system code page
* read_file hides CR; edit_file, apply_patch and write_file keep CRLF line
endings and a UTF-8 BOM
OS layer and processes
* os_dir_* and cst_fnmatch replace the POSIX-only directory walks in
subagent, plugins, skills, undo and fs_tools; realpath / path_under and
symlink rejection work on Windows
* every child goes through CreateProcessW with its own environment block
inside a Job Object, so timeouts and Esc end the whole process tree
Sandbox (auto mode, "auto · lowil")
* run_shell runs with a restricted Low integrity token; the workspace gets
an inheritable Low label once, the sandbox TEMP is labeled Low, and the
credentials file gets a no-read-up label
* drive roots, the profile and system folders are never labeled
* network commands still ask, since Low IL does not block the network;
Access denied and MSYS2's 0xc0000142 offer a rerun outside the sandbox
* /sandbox lists labeled folders, /sandbox reset-labels restores them
TUI
* raster console fonts are switched to Consolas / NSimSun while running
* the home logo falls back to text when the console font lacks U+2580
* mouse drag copy goes to the Windows clipboard
Distribution
* scripts/install_windows.ps1 installs a Gitea release into
%LOCALAPPDATA%\Programs\cst-agent and adds it to the user PATH;
cst-agent-update.cmd reruns it; scripts/release_windows.sh uploads the
zip with the token taken from the environment
* scripts/run_windows.bat is gone; docs/README-windows.md is rewritten
Tests
* new test_lowil, test_win_utf8 and test_os_dir; tests made portable
(cst_test_util.h)
* scripts/win_test.sh and scripts/winvm/ run the suite on English and
Chinese Windows Server VMs as Administrator, a standard user and a
Chinese user name: 29/29 on all six
This commit is contained in:
@@ -0,0 +1,350 @@
|
||||
--- a/ncurses/curses.priv.h
|
||||
+++ b/ncurses/curses.priv.h
|
||||
@@ -2294,7 +2294,10 @@
|
||||
#if defined(_NC_WINDOWS_NATIVE) && !defined(_NC_MSC) && !USE_NAMED_PIPES
|
||||
/*
|
||||
* MinGW has wide-character functions, but they do not work correctly.
|
||||
+ * Declare the originals first so a later <wchar.h> is not macro-expanded.
|
||||
*/
|
||||
+#include <stdlib.h>
|
||||
+#include <wchar.h>
|
||||
extern int __MINGW_NOTHROW _nc_wctomb(char *, wchar_t);
|
||||
#define wctomb(s,wc) _nc_wctomb(s,wc)
|
||||
#define wcrtomb(s,wc,n) _nc_wctomb(s,wc)
|
||||
--- a/ncurses/tinfo/lib_win32con.c
|
||||
+++ b/ncurses/tinfo/lib_win32con.c
|
||||
@@ -653,6 +653,21 @@
|
||||
return result;
|
||||
}
|
||||
|
||||
+/* Whether handle_mouse() turns this record into an event: wheel, a button
|
||||
+ * change (press or release), or a drag. */
|
||||
+static bool
|
||||
+mouse_wanted(const SCREEN *sp, const MOUSE_EVENT_RECORD *mer)
|
||||
+{
|
||||
+ int held = (int) (mer->dwButtonState & BUTTON_MASK);
|
||||
+
|
||||
+ if (mer->dwEventFlags & MOUSE_WHEELED)
|
||||
+ return TRUE;
|
||||
+ if (held != sp->_drv_mouse_new_buttons)
|
||||
+ return TRUE;
|
||||
+ return (mer->dwEventFlags & MOUSE_MOVED) && held
|
||||
+ && (sp->_mouse_mask & REPORT_MOUSE_POSITION);
|
||||
+}
|
||||
+
|
||||
#define AdjustY() (WINCONSOLE.buffered \
|
||||
? 0 \
|
||||
: (int) WINCONSOLE.SBI.srWindow.Top)
|
||||
@@ -661,36 +676,47 @@
|
||||
handle_mouse(SCREEN *sp, MOUSE_EVENT_RECORD mer)
|
||||
{
|
||||
MEVENT work;
|
||||
- bool result = FALSE;
|
||||
+ int old_buttons;
|
||||
+ int new_buttons;
|
||||
|
||||
assert(sp);
|
||||
|
||||
- sp->_drv_mouse_old_buttons = sp->_drv_mouse_new_buttons;
|
||||
- sp->_drv_mouse_new_buttons = mer.dwButtonState & BUTTON_MASK;
|
||||
+ if (sp->_drv_mouse_tail >= FIFO_SIZE)
|
||||
+ return FALSE;
|
||||
|
||||
- /*
|
||||
- * We're only interested if the button is pressed or released.
|
||||
- * FIXME: implement continuous event-tracking.
|
||||
- */
|
||||
- if (sp->_drv_mouse_new_buttons != sp->_drv_mouse_old_buttons) {
|
||||
- memset(&work, 0, sizeof(work));
|
||||
+ memset(&work, 0, sizeof(work));
|
||||
+ work.x = mer.dwMousePosition.X;
|
||||
+ work.y = mer.dwMousePosition.Y - AdjustY();
|
||||
+
|
||||
+ /* the wheel reports like xterm: button 4 up, button 5 down */
|
||||
+ if (mer.dwEventFlags & MOUSE_WHEELED) {
|
||||
+ work.bstate = ((SHORT) HIWORD(mer.dwButtonState) > 0)
|
||||
+ ? BUTTON4_PRESSED
|
||||
+ : BUTTON5_PRESSED;
|
||||
+ sp->_drv_mouse_fifo[sp->_drv_mouse_tail++] = work;
|
||||
+ return TRUE;
|
||||
+ }
|
||||
|
||||
- if (sp->_drv_mouse_new_buttons) {
|
||||
- work.bstate |= decode_mouse(sp, sp->_drv_mouse_new_buttons);
|
||||
+ old_buttons = sp->_drv_mouse_new_buttons;
|
||||
+ new_buttons = (int) (mer.dwButtonState & BUTTON_MASK);
|
||||
+ sp->_drv_mouse_old_buttons = old_buttons;
|
||||
+ sp->_drv_mouse_new_buttons = new_buttons;
|
||||
+
|
||||
+ if (new_buttons != old_buttons) {
|
||||
+ if (new_buttons & ~old_buttons) {
|
||||
+ work.bstate = decode_mouse(sp, new_buttons & ~old_buttons);
|
||||
} else {
|
||||
/* cf: BUTTON_PRESSED, BUTTON_RELEASED */
|
||||
- work.bstate |= (decode_mouse(sp, sp->_drv_mouse_old_buttons)
|
||||
- >> 1);
|
||||
- result = TRUE;
|
||||
+ work.bstate = decode_mouse(sp, old_buttons & ~new_buttons) >> 1;
|
||||
}
|
||||
-
|
||||
- work.x = mer.dwMousePosition.X;
|
||||
- work.y = mer.dwMousePosition.Y - AdjustY();
|
||||
-
|
||||
- sp->_drv_mouse_fifo[sp->_drv_mouse_tail] = work;
|
||||
- sp->_drv_mouse_tail += 1;
|
||||
+ } else if ((mer.dwEventFlags & MOUSE_MOVED) && new_buttons
|
||||
+ && (sp->_mouse_mask & REPORT_MOUSE_POSITION)) {
|
||||
+ work.bstate = REPORT_MOUSE_POSITION; /* drag */
|
||||
+ } else {
|
||||
+ return FALSE;
|
||||
}
|
||||
- return result;
|
||||
+ sp->_drv_mouse_fifo[sp->_drv_mouse_tail++] = work;
|
||||
+ return TRUE;
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -813,6 +839,13 @@
|
||||
returnCode(found);
|
||||
}
|
||||
|
||||
+#if USE_WIDEC_SUPPORT
|
||||
+/* UTF-8 bytes of the last non-ASCII key event not yet read */
|
||||
+static int utf8_pend[4];
|
||||
+static int utf8_len, utf8_pos;
|
||||
+static unsigned utf8_high;
|
||||
+#endif
|
||||
+
|
||||
NCURSES_EXPORT(int)
|
||||
_nc_console_twait(
|
||||
const SCREEN *sp,
|
||||
@@ -845,6 +878,14 @@
|
||||
TR(TRACE_IEVENT, ("start twait: hdl=%p, %d milliseconds, mode: %d",
|
||||
hdl, milliseconds, mode));
|
||||
|
||||
+#if USE_WIDEC_SUPPORT
|
||||
+ if ((mode & TW_INPUT) && utf8_pos < utf8_len) {
|
||||
+ if (timeleft)
|
||||
+ *timeleft = milliseconds;
|
||||
+ return TW_INPUT;
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
if (milliseconds < 0)
|
||||
milliseconds = NC_INFINITY;
|
||||
|
||||
@@ -940,9 +981,7 @@
|
||||
continue;
|
||||
case MOUSE_EVENT:
|
||||
T(("twait:event MOUSE_EVENT"));
|
||||
- if (decode_mouse(sp,
|
||||
- (inp_rec.Event.MouseEvent.dwButtonState
|
||||
- & BUTTON_MASK)) == 0) {
|
||||
+ if (!mouse_wanted(sp, &inp_rec.Event.MouseEvent)) {
|
||||
CONSUME();
|
||||
} else if (mode & TW_MOUSE) {
|
||||
code = TW_MOUSE;
|
||||
@@ -1024,6 +1063,16 @@
|
||||
|
||||
T((T_CALLED("lib_win32con::_nc_console_read(%p)"), sp));
|
||||
|
||||
+#if USE_WIDEC_SUPPORT
|
||||
+ /* wget_wch() decodes the input as a byte stream in the locale's
|
||||
+ * multibyte encoding (UTF-8), so non-ASCII UTF-16 key events are
|
||||
+ * handed over one UTF-8 byte per call. */
|
||||
+ if (utf8_pos < utf8_len) {
|
||||
+ *buf = utf8_pend[utf8_pos++];
|
||||
+ returnCode(1);
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
while ((b = read_keycode(hdl, &inp_rec, 1, &nRead))) {
|
||||
if (b && nRead > 0) {
|
||||
if (rc < 0)
|
||||
@@ -1034,6 +1083,36 @@
|
||||
continue;
|
||||
*buf = (int) inp_rec.Event.KeyEventChar;
|
||||
vk = inp_rec.Event.KeyEvent.wVirtualKeyCode;
|
||||
+#if USE_WIDEC_SUPPORT
|
||||
+ if (*buf >= 0x80) {
|
||||
+ unsigned cp = (unsigned) *buf;
|
||||
+ if (cp >= 0xD800 && cp < 0xDC00) {
|
||||
+ utf8_high = cp;
|
||||
+ continue;
|
||||
+ }
|
||||
+ if (cp >= 0xDC00 && cp < 0xE000) {
|
||||
+ if (!utf8_high)
|
||||
+ continue;
|
||||
+ cp = 0x10000 + ((utf8_high - 0xD800) << 10) + (cp - 0xDC00);
|
||||
+ }
|
||||
+ utf8_high = 0;
|
||||
+ utf8_len = 0;
|
||||
+ if (cp < 0x800) {
|
||||
+ utf8_pend[utf8_len++] = (int) (0xC0 | (cp >> 6));
|
||||
+ } else if (cp < 0x10000) {
|
||||
+ utf8_pend[utf8_len++] = (int) (0xE0 | (cp >> 12));
|
||||
+ utf8_pend[utf8_len++] = (int) (0x80 | ((cp >> 6) & 0x3F));
|
||||
+ } else {
|
||||
+ utf8_pend[utf8_len++] = (int) (0xF0 | (cp >> 18));
|
||||
+ utf8_pend[utf8_len++] = (int) (0x80 | ((cp >> 12) & 0x3F));
|
||||
+ utf8_pend[utf8_len++] = (int) (0x80 | ((cp >> 6) & 0x3F));
|
||||
+ }
|
||||
+ utf8_pend[utf8_len++] = (int) (0x80 | (cp & 0x3F));
|
||||
+ *buf = utf8_pend[0];
|
||||
+ utf8_pos = 1;
|
||||
+ break;
|
||||
+ }
|
||||
+#endif
|
||||
/*
|
||||
* There are 24 virtual function-keys, and typically
|
||||
* 12 function-keys on a keyboard. Use the shift-modifier
|
||||
--- a/ncurses/widechar/widechars.c
|
||||
+++ b/ncurses/widechar/widechars.c
|
||||
@@ -97,20 +97,10 @@
|
||||
wchar_t temp;
|
||||
|
||||
if (s != NULL && n != 0) {
|
||||
+ /* _nc_mbtowc() returns the bytes used, not a character count */
|
||||
count = _nc_mbtowc(&temp, s, n);
|
||||
- if (count == 1) {
|
||||
- int check = WideCharToMultiByte(CP_UTF8,
|
||||
- 0,
|
||||
- &temp,
|
||||
- 1,
|
||||
- NULL,
|
||||
- 0, /* compute length only */
|
||||
- NULL,
|
||||
- NULL);
|
||||
- TR(TRACE_BITS, ("\tcheck ->%d\n", check));
|
||||
- if (check > 0 && (size_t) check <= n) {
|
||||
- result = check;
|
||||
- }
|
||||
+ if (count > 0 && (size_t) count <= n) {
|
||||
+ result = count;
|
||||
}
|
||||
} else {
|
||||
result = 0;
|
||||
--- a/ncurses/win32con/win32_driver.c
|
||||
+++ b/ncurses/win32con/win32_driver.c
|
||||
@@ -87,8 +87,13 @@
|
||||
if (ch & A_BOLD)
|
||||
res |= FOREGROUND_INTENSITY;
|
||||
|
||||
- if (ch & A_DIM)
|
||||
- res |= BACKGROUND_INTENSITY;
|
||||
+ /* dim lowers the foreground: bright -> normal, light gray -> dark gray */
|
||||
+ if ((ch & A_DIM) && !(ch & A_BOLD)) {
|
||||
+ if (res & FOREGROUND_INTENSITY)
|
||||
+ res &= ~FOREGROUND_INTENSITY;
|
||||
+ else if ((res & 7) == 7)
|
||||
+ res = (res & ~7) | FOREGROUND_INTENSITY;
|
||||
+ }
|
||||
|
||||
return res;
|
||||
}
|
||||
@@ -159,7 +164,6 @@
|
||||
int y, int x, cchar_t *str, int limit)
|
||||
{
|
||||
int actual = 0;
|
||||
- MakeArray(ci, CHAR_INFO, limit);
|
||||
COORD loc, siz;
|
||||
SMALL_RECT rec;
|
||||
int i;
|
||||
@@ -169,39 +173,71 @@
|
||||
AssertTCB();
|
||||
SetSP();
|
||||
|
||||
- for (i = actual = 0; i < limit; i++) {
|
||||
- ch = str[i];
|
||||
- if (isWidecExt(ch))
|
||||
- continue;
|
||||
- ci[actual].CharInfoChar = CharOf(ch);
|
||||
- ci[actual].Attributes = MapAttr(WINCONSOLE.SBI.wAttributes,
|
||||
- AttrOf(ch));
|
||||
- if (AttrOf(ch) & A_ALTCHARSET) {
|
||||
- if (_nc_wacs) {
|
||||
- int which = CharOf(ch);
|
||||
- if (which > 0
|
||||
- && which < ACS_LEN
|
||||
- && CharOf(_nc_wacs[which]) != 0) {
|
||||
- ci[actual].CharInfoChar = CharOf(_nc_wacs[which]);
|
||||
+ /*
|
||||
+ * The console stores a double-width character as two cells flagged
|
||||
+ * leading/trailing byte. Never start or end a write inside one, and
|
||||
+ * emit both halves so the columns after it stay put.
|
||||
+ */
|
||||
+ if (limit > 0 && x > 0 && isWidecExt(str[0])) {
|
||||
+ --str;
|
||||
+ --x;
|
||||
+ ++limit;
|
||||
+ }
|
||||
+ if (limit > 0 && x + limit < screen_columns(sp) && isWidecExt(str[limit]))
|
||||
+ ++limit;
|
||||
+
|
||||
+ {
|
||||
+ MakeArray(ci, CHAR_INFO, limit);
|
||||
+
|
||||
+ for (i = actual = 0; i < limit; i++) {
|
||||
+ ch = str[i];
|
||||
+ if (isWidecExt(ch)) {
|
||||
+ if (actual > 0
|
||||
+ && (ci[actual - 1].Attributes & COMMON_LVB_LEADING_BYTE)) {
|
||||
+ ci[actual] = ci[actual - 1];
|
||||
+ ci[actual].Attributes = (WORD)
|
||||
+ ((ci[actual].Attributes & ~COMMON_LVB_LEADING_BYTE)
|
||||
+ | COMMON_LVB_TRAILING_BYTE);
|
||||
} else {
|
||||
ci[actual].CharInfoChar = ' ';
|
||||
+ ci[actual].Attributes = MapAttr(WINCONSOLE.SBI.wAttributes,
|
||||
+ AttrOf(ch));
|
||||
}
|
||||
+ ++actual;
|
||||
+ continue;
|
||||
}
|
||||
+ ci[actual].CharInfoChar = CharOf(ch);
|
||||
+ ci[actual].Attributes = MapAttr(WINCONSOLE.SBI.wAttributes,
|
||||
+ AttrOf(ch));
|
||||
+ if (AttrOf(ch) & A_ALTCHARSET) {
|
||||
+ if (_nc_wacs) {
|
||||
+ int which = CharOf(ch);
|
||||
+ if (which > 0
|
||||
+ && which < ACS_LEN
|
||||
+ && CharOf(_nc_wacs[which]) != 0) {
|
||||
+ ci[actual].CharInfoChar = CharOf(_nc_wacs[which]);
|
||||
+ } else {
|
||||
+ ci[actual].CharInfoChar = ' ';
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ if (i + 1 < limit && isWidecExt(str[i + 1]))
|
||||
+ ci[actual].Attributes |= COMMON_LVB_LEADING_BYTE;
|
||||
+ ++actual;
|
||||
}
|
||||
- ++actual;
|
||||
- }
|
||||
|
||||
- loc.X = (SHORT) 0;
|
||||
- loc.Y = (SHORT) 0;
|
||||
- siz.X = (SHORT) actual;
|
||||
- siz.Y = 1;
|
||||
-
|
||||
- rec.Left = (SHORT) x;
|
||||
- rec.Top = (SHORT) (y + AdjustY());
|
||||
- rec.Right = (SHORT) (x + limit - 1);
|
||||
- rec.Bottom = rec.Top;
|
||||
+ loc.X = (SHORT) 0;
|
||||
+ loc.Y = (SHORT) 0;
|
||||
+ siz.X = (SHORT) actual;
|
||||
+ siz.Y = 1;
|
||||
+
|
||||
+ rec.Left = (SHORT) x;
|
||||
+ rec.Top = (SHORT) (y + AdjustY());
|
||||
+ rec.Right = (SHORT) (x + actual - 1);
|
||||
+ rec.Bottom = rec.Top;
|
||||
|
||||
- return write_screen(WINCONSOLE.hdl, ci, siz, loc, &rec);
|
||||
+ return write_screen(WINCONSOLE.hdl, ci, siz, loc, &rec);
|
||||
+ }
|
||||
}
|
||||
#define con_write(tcb, y, x, str, n) con_write16(tcb, y, x, str, n)
|
||||
#else
|
||||
Reference in New Issue
Block a user