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:
@@ -41,11 +41,12 @@
|
||||
|
||||
static int g_nocolor; /* NO_COLOR */
|
||||
static int g_ascii; /* non-UTF-8 locale or CST_ASCII=1 */
|
||||
static int g_logo_text; /* console font lacks the logo's block glyphs */
|
||||
static int g_utf8 = 1; /* locale can show non-ASCII text */
|
||||
#define HAS_COLOR() (!g_nocolor && has_colors())
|
||||
|
||||
/* Curses: ncursesw on Unix, PDCurses (wide) on Windows. */
|
||||
#if defined(CST_USE_PDCURSES) || defined(_WIN32)
|
||||
/* Curses: ncursesw (Unix and Windows), or PDCurses (wide) on Windows. */
|
||||
#if defined(CST_USE_PDCURSES)
|
||||
#ifndef PDC_WIDE
|
||||
#define PDC_WIDE 1
|
||||
#endif
|
||||
@@ -62,10 +63,23 @@ static int g_utf8 = 1; /* locale can show non-ASCII text */
|
||||
#ifndef KEY_RESIZE
|
||||
#define KEY_RESIZE 0632
|
||||
#endif
|
||||
#define wcwidth cst_wcwidth /* MinGW often lacks wcwidth */
|
||||
#else
|
||||
#include <ncurses.h>
|
||||
#endif
|
||||
#ifdef _WIN32
|
||||
#define wcwidth cst_wcwidth /* MinGW often lacks wcwidth */
|
||||
#endif
|
||||
|
||||
/* The Windows console (conhost/ConPTY) loses wide characters on insert or
|
||||
* delete-character, so curses must repaint cells instead. */
|
||||
static WINDOW *tui_newwin(int rows, int cols, int y, int x) {
|
||||
WINDOW *w = newwin(rows, cols, y, x);
|
||||
#ifdef _WIN32
|
||||
if (w)
|
||||
idcok(w, FALSE);
|
||||
#endif
|
||||
return w;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
static void cst_usleep(unsigned usec) {
|
||||
@@ -1876,10 +1890,10 @@ static void git_branch(const char *dir, char *out, size_t sz) {
|
||||
FILE *f = NULL;
|
||||
for (int depth = 0; depth < 64 && d[0]; depth++) {
|
||||
snprintf(p, sizeof(p), "%s/.git/HEAD", d);
|
||||
if ((f = fopen(p, "r")) != NULL)
|
||||
if ((f = fopen(p, "rb")) != NULL)
|
||||
break;
|
||||
snprintf(p, sizeof(p), "%s/.git", d);
|
||||
FILE *g = fopen(p, "r");
|
||||
FILE *g = fopen(p, "rb");
|
||||
if (g) {
|
||||
int ok = fgets(line, sizeof(line), g) && strncmp(line, "gitdir: ", 8) == 0;
|
||||
fclose(g);
|
||||
@@ -1889,7 +1903,7 @@ static void git_branch(const char *dir, char *out, size_t sz) {
|
||||
? snprintf(p, sizeof(p), "%s/HEAD", line + 8)
|
||||
: snprintf(p, sizeof(p), "%s/%s/HEAD", d, line + 8);
|
||||
if (n > 0 && (size_t)n < sizeof(p))
|
||||
f = fopen(p, "r");
|
||||
f = fopen(p, "rb");
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -2322,7 +2336,7 @@ static void draw_home(tui_t *t) {
|
||||
getmaxyx(t->logw, h, w);
|
||||
int box_top = t->in_y - t->log_y;
|
||||
int box_bot = box_top + t->in_h;
|
||||
if (g_ascii || !g_utf8) {
|
||||
if (g_ascii || !g_utf8 || g_logo_text) {
|
||||
int x = (w - 8) / 2, y = box_top - 2;
|
||||
if (y >= 0) {
|
||||
draw_ansi_at(t->logw, y, x, 3, "cst", CP_LOGO_DIM, 1, 0);
|
||||
@@ -2805,12 +2819,107 @@ static int input_visual_rows(const tui_t *t) {
|
||||
return rows;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
static DWORD g_con_in_mode;
|
||||
static int g_con_in_saved;
|
||||
|
||||
/* QuickEdit makes the console select text itself and swallow the clicks;
|
||||
* it is off while the TUI owns the mouse and restored on exit. */
|
||||
static void win_console_mouse(int on) {
|
||||
HANDLE h = GetStdHandle(STD_INPUT_HANDLE);
|
||||
DWORD m;
|
||||
if (!GetConsoleMode(h, &m))
|
||||
return;
|
||||
if (!g_con_in_saved) {
|
||||
g_con_in_mode = m;
|
||||
g_con_in_saved = 1;
|
||||
}
|
||||
if (on)
|
||||
m = (m & ~(DWORD)ENABLE_QUICK_EDIT_MODE) | ENABLE_MOUSE_INPUT |
|
||||
ENABLE_EXTENDED_FLAGS;
|
||||
else
|
||||
m = g_con_in_mode | ENABLE_EXTENDED_FLAGS;
|
||||
SetConsoleMode(h, m);
|
||||
}
|
||||
|
||||
static CONSOLE_FONT_INFOEX g_con_font;
|
||||
static int g_con_font_saved;
|
||||
|
||||
/* conhost has no glyph fallback: NSimSun, the default on Chinese systems,
|
||||
* draws U+2580 as an empty box. */
|
||||
static int win_font_has(const WCHAR *face, WCHAR ch) {
|
||||
HDC dc = CreateCompatibleDC(NULL);
|
||||
if (!dc)
|
||||
return 1;
|
||||
HFONT font = CreateFontW(16, 0, 0, 0, FW_NORMAL, 0, 0, 0, DEFAULT_CHARSET,
|
||||
0, 0, 0, FF_MODERN, face);
|
||||
WORD idx = 0;
|
||||
int ok = 1;
|
||||
if (font) {
|
||||
HGDIOBJ old = SelectObject(dc, font);
|
||||
if (GetGlyphIndicesW(dc, &ch, 1, &idx, GGI_MARK_NONEXISTING_GLYPHS) == 1)
|
||||
ok = idx != 0xffff;
|
||||
SelectObject(dc, old);
|
||||
DeleteObject(font);
|
||||
}
|
||||
DeleteDC(dc);
|
||||
return ok;
|
||||
}
|
||||
|
||||
/* Raster fonts cannot draw UTF-8 box drawing or CJK; switch the console to
|
||||
* a TrueType font of the same height while the TUI runs. */
|
||||
static void win_console_font(int on) {
|
||||
HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
if (!on) {
|
||||
if (g_con_font_saved)
|
||||
SetCurrentConsoleFontEx(h, FALSE, &g_con_font);
|
||||
g_con_font_saved = 0;
|
||||
return;
|
||||
}
|
||||
CONSOLE_FONT_INFOEX f = {.cbSize = sizeof f};
|
||||
if (!GetCurrentConsoleFontEx(h, FALSE, &f))
|
||||
return;
|
||||
if (!(f.FontFamily & TMPF_TRUETYPE)) {
|
||||
g_con_font = f;
|
||||
CONSOLE_FONT_INFOEX tt = {.cbSize = sizeof tt};
|
||||
tt.dwFontSize.Y = f.dwFontSize.Y > 0 ? f.dwFontSize.Y : 16;
|
||||
tt.FontFamily = FF_MODERN | TMPF_TRUETYPE;
|
||||
tt.FontWeight = FW_NORMAL;
|
||||
wcscpy(tt.FaceName, GetACP() == 936 ? L"NSimSun" : L"Consolas");
|
||||
if (!SetCurrentConsoleFontEx(h, FALSE, &tt))
|
||||
return;
|
||||
g_con_font_saved = 1;
|
||||
f = tt;
|
||||
}
|
||||
if (!getenv("WT_SESSION") && !win_font_has(f.FaceName, 0x2580))
|
||||
g_logo_text = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
#define MOUSE_RUN_MAX 16
|
||||
/* getmouse() hands out a run of queued events newest first (the Windows
|
||||
* console driver queues press, motion and release together); returns them
|
||||
* oldest first. */
|
||||
static int get_mouse_run(MEVENT *out, int max) {
|
||||
int n = 0;
|
||||
while (n < max && getmouse(&out[n]) == OK)
|
||||
n++;
|
||||
for (int i = 0; i < n / 2; i++) {
|
||||
MEVENT tmp = out[i];
|
||||
out[i] = out[n - 1 - i];
|
||||
out[n - 1 - i] = tmp;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
static void apply_mouse(const tui_t *t) {
|
||||
mousemask(t->mouse_on ? (mmask_t)(ALL_MOUSE_EVENTS | REPORT_MOUSE_POSITION)
|
||||
: 0,
|
||||
NULL);
|
||||
mouseinterval(0);
|
||||
#ifdef NCURSES_VERSION
|
||||
#ifdef _WIN32
|
||||
win_console_mouse(t->mouse_on);
|
||||
#elif defined(NCURSES_VERSION)
|
||||
/* ncurses only asks for presses and releases (1000). 1002 also reports
|
||||
* motion while a button is held, for drag selection; terminals treat it as
|
||||
* replacing 1000, so it goes after mousemask and is turned off explicitly. */
|
||||
@@ -2869,8 +2978,8 @@ static void layout(tui_t *t) {
|
||||
int top = (t->rows - 1 - block) / 2;
|
||||
t->in_y = clampi(top + LOGO_H + 1, 0, t->rows - 1 - t->in_h);
|
||||
t->log_x = t->log_y = 0;
|
||||
t->logw = newwin(t->rows - 1, t->cols, 0, 0);
|
||||
t->status = newwin(1, t->cols - 2, t->rows - 1, 1);
|
||||
t->logw = tui_newwin(t->rows - 1, t->cols, 0, 0);
|
||||
t->status = tui_newwin(1, t->cols - 2, t->rows - 1, 1);
|
||||
} else {
|
||||
int mw = t->cols - t->side_w;
|
||||
t->in_y = t->rows - 1 - t->in_h;
|
||||
@@ -2885,14 +2994,14 @@ static void layout(tui_t *t) {
|
||||
t->todo_rows = 0;
|
||||
log_h = t->in_y - t->log_y > 0 ? t->in_y - t->log_y : 1;
|
||||
}
|
||||
t->logw = newwin(log_h, mw - 2, t->log_y, 1);
|
||||
t->logw = tui_newwin(log_h, mw - 2, t->log_y, 1);
|
||||
if (t->todo_rows)
|
||||
t->todow = newwin(t->todo_rows, mw - 2, t->log_y + log_h, 1);
|
||||
t->status = newwin(1, mw - 2, t->rows - 1, 1);
|
||||
t->todow = tui_newwin(t->todo_rows, mw - 2, t->log_y + log_h, 1);
|
||||
t->status = tui_newwin(1, mw - 2, t->rows - 1, 1);
|
||||
if (t->side_w)
|
||||
t->side = newwin(t->rows, t->side_w, 0, mw);
|
||||
t->side = tui_newwin(t->rows, t->side_w, 0, mw);
|
||||
}
|
||||
t->input = newwin(t->in_h, t->in_w, t->in_y, t->in_x);
|
||||
t->input = tui_newwin(t->in_h, t->in_w, t->in_y, t->in_x);
|
||||
if (HAS_COLOR()) {
|
||||
wbkgdset(t->logw, COLOR_PAIR(CP_PANEL));
|
||||
wbkgdset(t->status, COLOR_PAIR(CP_PANEL));
|
||||
@@ -3495,7 +3604,7 @@ static int picker_run(tui_t *t, const char *title, char items[][128], int n,
|
||||
redraw_all(t); /* uncover what a larger dialog hid */
|
||||
last_dh = dh;
|
||||
last_dw = dw;
|
||||
dlg = newwin(dh, dw, dy, dx);
|
||||
dlg = tui_newwin(dh, dw, dy, dx);
|
||||
if (!dlg)
|
||||
break;
|
||||
keypad(dlg, TRUE);
|
||||
@@ -3549,9 +3658,14 @@ static int picker_run(tui_t *t, const char *title, char items[][128], int n,
|
||||
layout(t);
|
||||
redraw_all(t);
|
||||
} else if (wch == KEY_MOUSE) {
|
||||
MEVENT ev;
|
||||
if (getmouse(&ev) != OK)
|
||||
MEVENT evs[MOUSE_RUN_MAX], ev;
|
||||
int ne = get_mouse_run(evs, MOUSE_RUN_MAX), k0 = ne - 1;
|
||||
if (ne == 0)
|
||||
continue;
|
||||
for (int i = 0; i < ne; i++)
|
||||
if (evs[i].bstate & (BUTTON1_PRESSED | BUTTON1_CLICKED))
|
||||
k0 = i; /* a click anywhere in the run wins over its release */
|
||||
ev = evs[k0];
|
||||
if (ev.bstate & BUTTON4_PRESSED) {
|
||||
if (sel > 0)
|
||||
sel--;
|
||||
@@ -4301,6 +4415,34 @@ static void cmd_tools(tui_t *t, char *arg, int *quit) {
|
||||
}
|
||||
}
|
||||
|
||||
static void cmd_sandbox(tui_t *t, char *arg, int *quit) {
|
||||
(void)quit;
|
||||
if (arg && strcmp(arg, "reset-labels") == 0) {
|
||||
char *rep = NULL;
|
||||
int n = cst_sandbox_reset_labels(&rep);
|
||||
if (rep && rep[0])
|
||||
push_lines(t, " ", rep);
|
||||
push_system_line(t, n < 0 ? "(some labels could not be restored; "
|
||||
"they stay listed)"
|
||||
: n == 0 ? "(no labeled directories)"
|
||||
: "(labels restored)");
|
||||
free(rep);
|
||||
return;
|
||||
}
|
||||
int sk = cst_sandbox_probe();
|
||||
push_system_line(t, "Shell sandbox: %s",
|
||||
sk == CST_SANDBOX_NONE ? "none (rule-based approval)"
|
||||
: cst_sandbox_name(sk));
|
||||
char *l = cst_sandbox_labels();
|
||||
if (l) {
|
||||
push_system_line(t, "Labeled Low integrity (writable by sandboxed "
|
||||
"commands):");
|
||||
push_lines(t, " ", l);
|
||||
push_system_line(t, "/sandbox reset-labels restores their old labels");
|
||||
free(l);
|
||||
}
|
||||
}
|
||||
|
||||
static void cmd_permissions(tui_t *t, char *arg, int *quit) {
|
||||
(void)quit;
|
||||
cst_perms_t *p = cst_agent_perms(t->agent);
|
||||
@@ -4537,7 +4679,7 @@ static void cmd_export(tui_t *t, char *arg, int *quit) {
|
||||
*sl = 0;
|
||||
os_mkdir_p(dir, 0755);
|
||||
}
|
||||
FILE *fp = fopen(path, "w");
|
||||
FILE *fp = fopen(path, "wb");
|
||||
if (!fp) {
|
||||
push_system_line(t, "Error: cannot write %s", path);
|
||||
return;
|
||||
@@ -4569,9 +4711,19 @@ static void cmd_export(tui_t *t, char *arg, int *quit) {
|
||||
push_system_line(t, "(exported to %s)", path);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
#define COPY_VIA "clipboard"
|
||||
#else
|
||||
#define COPY_VIA "OSC 52"
|
||||
#endif
|
||||
|
||||
/* Puts text on the terminal's clipboard; returns the bytes sent, 0 on
|
||||
* failure. */
|
||||
static size_t osc52_copy(const char *text, size_t n) {
|
||||
#ifdef _WIN32
|
||||
if (os_clipboard_set(text, n) == 0)
|
||||
return n; /* conhost has no OSC 52 */
|
||||
#endif
|
||||
static const char b64[] =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
if (n > 75000)
|
||||
@@ -4614,8 +4766,8 @@ static void cmd_copy(tui_t *t, char *arg, int *quit) {
|
||||
}
|
||||
size_t n = osc52_copy(text, strlen(text));
|
||||
if (n)
|
||||
push_system_line(t, "(copied %zu bytes of the last reply via OSC 52 — the "
|
||||
"terminal must allow clipboard access)", n);
|
||||
push_system_line(t, "(copied %zu bytes of the last reply via " COPY_VIA
|
||||
" — the terminal must allow clipboard access)", n);
|
||||
}
|
||||
|
||||
static void cmd_init(tui_t *t, char *arg, int *quit) {
|
||||
@@ -4945,6 +5097,8 @@ static const slash_cmd_t SLASH[] = {
|
||||
cmd_tools},
|
||||
{"permissions", "perms", "[reset]", "show saved always-allow rules", 1,
|
||||
cmd_permissions},
|
||||
{"sandbox", "", "[reset-labels]", "shell sandbox and labeled folders", 1,
|
||||
cmd_sandbox},
|
||||
{"approval", "", "[ask|auto|yolo]", "how tools get approved (Shift+Tab)",
|
||||
1, cmd_approval},
|
||||
{"yolo", "", "", "toggle approving everything", 1, cmd_yolo},
|
||||
@@ -5530,7 +5684,7 @@ static void sel_copy(tui_t *t) {
|
||||
for (size_t i = 0; i < b.n; i++)
|
||||
chars += ((unsigned char)b.s[i] & 0xC0) != 0x80;
|
||||
char msg[96];
|
||||
snprintf(msg, sizeof(msg), "Copied %zu characters (OSC 52)", chars);
|
||||
snprintf(msg, sizeof(msg), "Copied %zu characters (" COPY_VIA ")", chars);
|
||||
set_status(t, msg);
|
||||
draw_status(t);
|
||||
}
|
||||
@@ -6187,6 +6341,21 @@ static void insert_newline(tui_t *t) {
|
||||
}
|
||||
|
||||
/* Paste mode: keep newlines/tabs literally; redraw once at the end. */
|
||||
#ifdef _WIN32
|
||||
static int win_key_pending(void) {
|
||||
INPUT_RECORD rec[32];
|
||||
DWORD n = 0;
|
||||
HANDLE h = GetStdHandle(STD_INPUT_HANDLE);
|
||||
if (!PeekConsoleInputW(h, rec, 32, &n))
|
||||
return 0;
|
||||
for (DWORD i = 0; i < n; i++)
|
||||
if (rec[i].EventType == KEY_EVENT && rec[i].Event.KeyEvent.bKeyDown &&
|
||||
rec[i].Event.KeyEvent.uChar.UnicodeChar)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void paste_char(tui_t *t, wint_t wch) {
|
||||
if (wch == 13) {
|
||||
input_put_byte(t, '\n');
|
||||
@@ -6207,20 +6376,31 @@ static void paste_char(tui_t *t, wint_t wch) {
|
||||
}
|
||||
|
||||
int cst_tui_run(cst_agent_t *agent) {
|
||||
#ifdef _WIN32
|
||||
setlocale(LC_ALL, ".UTF-8");
|
||||
#else
|
||||
if (!setlocale(LC_ALL, ""))
|
||||
setlocale(LC_ALL, "C.UTF-8");
|
||||
#endif
|
||||
#ifndef _WIN32
|
||||
if (!getenv("ESCDELAY"))
|
||||
setenv("ESCDELAY", "25", 0);
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
win_console_font(1);
|
||||
#endif
|
||||
initscr();
|
||||
#ifdef _WIN32
|
||||
idcok(stdscr, FALSE);
|
||||
#endif
|
||||
raw(); /* Ctrl+C/Ctrl+Z reach us as keys instead of signals */
|
||||
noecho();
|
||||
keypad(stdscr, TRUE);
|
||||
curs_set(1);
|
||||
theme_init();
|
||||
#ifdef NCURSES_VERSION
|
||||
#if defined(NCURSES_VERSION) && !defined(_WIN32)
|
||||
/* the Windows console driver reads key events, not VT sequences */
|
||||
define_key("\033[200~", KEY_PASTE_BEGIN);
|
||||
define_key("\033[201~", KEY_PASTE_END);
|
||||
printf("\033[?2004h");
|
||||
@@ -6375,6 +6555,18 @@ int cst_tui_run(cst_agent_t *agent) {
|
||||
if (t.sel_state == SEL_SHOWN &&
|
||||
!(rc == KEY_CODE_YES && (wch == KEY_MOUSE || wch == KEY_RESIZE)))
|
||||
sel_clear(&t);
|
||||
#ifdef _WIN32
|
||||
/* The console has no bracketed paste: an Enter with more keys already
|
||||
* queued is a pasted line break, not a send. */
|
||||
if (rc == OK && !t.in_paste && (wch == 10 || wch == 13) &&
|
||||
win_key_pending()) {
|
||||
paste_char(&t, wch);
|
||||
input_changed(&t);
|
||||
after_edit(&t);
|
||||
draw_input(&t);
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!t.in_paste &&
|
||||
!(rc == KEY_CODE_YES &&
|
||||
@@ -6453,12 +6645,13 @@ int cst_tui_run(cst_agent_t *agent) {
|
||||
continue;
|
||||
}
|
||||
if (wch == KEY_MOUSE) {
|
||||
MEVENT ev;
|
||||
if (getmouse(&ev) == OK) {
|
||||
if (on_mouse(&t, &ev, busy) == -2)
|
||||
MEVENT evs[MOUSE_RUN_MAX];
|
||||
int ne = get_mouse_run(evs, MOUSE_RUN_MAX);
|
||||
for (int i = 0; i < ne; i++)
|
||||
if (on_mouse(&t, &evs[i], busy) == -2)
|
||||
goto out;
|
||||
if (ne)
|
||||
draw_input(&t); /* keep the terminal cursor in the box */
|
||||
}
|
||||
continue;
|
||||
}
|
||||
int wdir;
|
||||
@@ -6667,7 +6860,11 @@ out:
|
||||
usleep(100000); /* let a /models probe finish before curl cleanup */
|
||||
|
||||
tui_free(&t);
|
||||
#ifdef NCURSES_VERSION
|
||||
#ifdef _WIN32
|
||||
if (g_con_in_saved)
|
||||
win_console_mouse(0);
|
||||
win_console_font(0);
|
||||
#elif defined(NCURSES_VERSION)
|
||||
kitty_restore();
|
||||
printf("\033[?1002l\033[?2004l");
|
||||
fflush(stdout);
|
||||
|
||||
Reference in New Issue
Block a user