mirror of
https://github.com/rizinorg/rizin.git
synced 2026-10-12 02:21:57 +00:00
cons: fix Windows console VT mode save and restore (#6169)
* cons: fix windows console VT restore * restore state in rzcons and own to instance for state
This commit is contained in:
+51
-18
@@ -21,6 +21,12 @@ static RzConsContext rz_cons_context_default = { { { { 0 } } } };
|
||||
static RzCons rz_cons_instance = { 0 };
|
||||
#define I rz_cons_instance
|
||||
|
||||
#if __WINDOWS__
|
||||
// restore only the console flags rizin owns instead of replaying the whole host mode word
|
||||
#define RZ_CONS_OUTPUT_MODE_MASK (ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING)
|
||||
#define RZ_CONS_INPUT_MODE_MASK (ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT | ENABLE_MOUSE_INPUT | ENABLE_QUICK_EDIT_MODE | ENABLE_EXTENDED_FLAGS | ENABLE_VIRTUAL_TERMINAL_INPUT)
|
||||
#endif
|
||||
|
||||
// this structure goes into cons_stack when rz_cons_push/pop
|
||||
typedef struct {
|
||||
char *buf;
|
||||
@@ -535,7 +541,9 @@ RZ_API bool rz_cons_enable_mouse(const bool enable) {
|
||||
HANDLE h;
|
||||
bool enabled = I.mouse;
|
||||
h = GetStdHandle(STD_INPUT_HANDLE);
|
||||
GetConsoleMode(h, &mode);
|
||||
if (!GetConsoleMode(h, &mode)) {
|
||||
return enabled;
|
||||
}
|
||||
mode |= ENABLE_EXTENDED_FLAGS;
|
||||
mode = enable
|
||||
? (mode | ENABLE_MOUSE_INPUT) & ~ENABLE_QUICK_EDIT_MODE
|
||||
@@ -564,34 +572,46 @@ static void set_console_codepage_to_utf8(void) {
|
||||
}
|
||||
|
||||
static void save_console_state(void) {
|
||||
if (rz_cons_isatty()) {
|
||||
// Snapshot the exact std handles and their current console modes before any probing mutates them.
|
||||
I.saved_input_handle = GetStdHandle(STD_INPUT_HANDLE);
|
||||
I.saved_output_handle = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
I.saved_input_console = GetConsoleMode((HANDLE)I.saved_input_handle, &I.old_input_mode);
|
||||
I.saved_output_console = GetConsoleMode((HANDLE)I.saved_output_handle, &I.old_output_mode);
|
||||
if (I.saved_output_console) {
|
||||
if (!(I.old_ocp = GetConsoleOutputCP())) {
|
||||
rz_sys_perror("GetConsoleOutputCP");
|
||||
}
|
||||
}
|
||||
if (I.saved_input_console) {
|
||||
if (!(I.old_cp = GetConsoleCP())) {
|
||||
rz_sys_perror("GetConsoleCP");
|
||||
}
|
||||
if (!GetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), &I.old_output_mode)) {
|
||||
rz_sys_perror("GetConsoleMode");
|
||||
}
|
||||
if (!GetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), &I.old_input_mode)) {
|
||||
rz_sys_perror("GetConsoleCP");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline DWORD restore_console_mode_bits(DWORD mode, DWORD saved_mode, DWORD mask) {
|
||||
return (mode & ~mask) | (saved_mode & mask);
|
||||
}
|
||||
|
||||
static void restore_console_state(void) {
|
||||
if (rz_cons_isatty()) {
|
||||
if (!SetConsoleCP(I.old_cp)) {
|
||||
rz_sys_perror("SetConsoleCP");
|
||||
}
|
||||
DWORD mode;
|
||||
if (I.saved_output_console && GetConsoleMode((HANDLE)I.saved_output_handle, &mode)) {
|
||||
if (!SetConsoleOutputCP(I.old_ocp)) {
|
||||
rz_sys_perror("SetConsoleOutputCP");
|
||||
}
|
||||
if (!SetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), I.old_output_mode)) {
|
||||
// Keep unrelated host-owned bits intact and restore only the output flags we changed.
|
||||
mode = restore_console_mode_bits(mode, I.old_output_mode, RZ_CONS_OUTPUT_MODE_MASK);
|
||||
if (!SetConsoleMode((HANDLE)I.saved_output_handle, mode)) {
|
||||
rz_sys_perror("SetConsoleMode");
|
||||
}
|
||||
if (!SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), I.old_input_mode)) {
|
||||
}
|
||||
if (I.saved_input_console && GetConsoleMode((HANDLE)I.saved_input_handle, &mode)) {
|
||||
if (!SetConsoleCP(I.old_cp)) {
|
||||
rz_sys_perror("SetConsoleCP");
|
||||
}
|
||||
// Input teardown mirrors output teardown for the subset of flags Rizin manages.
|
||||
mode = restore_console_mode_bits(mode, I.old_input_mode, RZ_CONS_INPUT_MODE_MASK);
|
||||
if (!SetConsoleMode((HANDLE)I.saved_input_handle, mode)) {
|
||||
rz_sys_perror("SetConsoleMode");
|
||||
}
|
||||
}
|
||||
@@ -605,6 +625,10 @@ RZ_API RzCons *rz_cons_new(void) {
|
||||
if (I.refcnt != 1) {
|
||||
return &I;
|
||||
}
|
||||
#if __WINDOWS__
|
||||
// Save the console state before rz_line_new() runs VT detection on Windows.
|
||||
save_console_state();
|
||||
#endif
|
||||
I.rgbstr = rz_cons_rgb_str_off;
|
||||
I.line = rz_line_new();
|
||||
I.enable_highlight = true;
|
||||
@@ -635,8 +659,9 @@ RZ_API RzCons *rz_cons_new(void) {
|
||||
I.num = NULL;
|
||||
I.null = 0;
|
||||
#if __WINDOWS__
|
||||
save_console_state();
|
||||
I.vtmode = rz_cons_detect_vt_mode();
|
||||
// Keep line editing on the same VT mode that the main console instance selected.
|
||||
I.line->vtmode = I.vtmode;
|
||||
set_console_codepage_to_utf8();
|
||||
#else
|
||||
I.vtmode = RZ_VIRT_TERM_MODE_COMPLETE;
|
||||
@@ -653,8 +678,11 @@ RZ_API RzCons *rz_cons_new(void) {
|
||||
I.term_raw.c_cc[VMIN] = 1; // Solaris stuff hehe
|
||||
rz_sys_signal(SIGWINCH, resize);
|
||||
#elif __WINDOWS__
|
||||
I.term_buf = I.old_input_mode | ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT;
|
||||
I.term_raw = ~(ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT);
|
||||
if (I.saved_input_console) {
|
||||
// Raw/buffered mode masks must be derived from the saved console input mode.
|
||||
I.term_buf = I.old_input_mode | ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT;
|
||||
I.term_raw = ~(ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT);
|
||||
}
|
||||
if (!SetConsoleCtrlHandler((PHANDLER_ROUTINE)__w32_control, TRUE)) {
|
||||
eprintf("rz_cons: Cannot set control console handler\n");
|
||||
}
|
||||
@@ -1698,7 +1726,12 @@ RZ_API void rz_cons_set_raw(bool is_raw) {
|
||||
#elif __WINDOWS__
|
||||
DWORD mode;
|
||||
HANDLE h = GetStdHandle(STD_INPUT_HANDLE);
|
||||
GetConsoleMode(h, &mode);
|
||||
if (!GetConsoleMode(h, &mode)) {
|
||||
// non console stdin is valid in batch mode.. just track state and leave the handle untouched
|
||||
fflush(stdout);
|
||||
I.oldraw = is_raw;
|
||||
return;
|
||||
}
|
||||
if (is_raw) {
|
||||
if (I.term_pty) {
|
||||
rz_sys_xsystem("stty raw -echo");
|
||||
|
||||
+9
-4
@@ -397,13 +397,15 @@ static int __cons_readchar_w32(ut32 usec) {
|
||||
void *bed;
|
||||
I->mouse_event = MOUSE_NONE;
|
||||
h = GetStdHandle(STD_INPUT_HANDLE);
|
||||
GetConsoleMode(h, &mode);
|
||||
const bool has_input_console = GetConsoleMode(h, &mode);
|
||||
DWORD newmode = ENABLE_WINDOW_INPUT;
|
||||
if (I->vtmode == RZ_VIRT_TERM_MODE_COMPLETE) {
|
||||
newmode |= ENABLE_VIRTUAL_TERMINAL_INPUT;
|
||||
}
|
||||
newmode |= mode;
|
||||
SetConsoleMode(h, newmode);
|
||||
if (has_input_console) {
|
||||
// Only toggle temporary read modes when stdin is a real console handle.
|
||||
SetConsoleMode(h, newmode | mode);
|
||||
}
|
||||
do {
|
||||
bed = rz_cons_sleep_begin();
|
||||
if (usec) {
|
||||
@@ -545,7 +547,10 @@ static int __cons_readchar_w32(ut32 usec) {
|
||||
resizeWin();
|
||||
}
|
||||
} while (ch == 0 && !do_break);
|
||||
SetConsoleMode(h, mode);
|
||||
if (has_input_console) {
|
||||
// Restore the temporary read mode only when we successfully captured one.
|
||||
SetConsoleMode(h, mode);
|
||||
}
|
||||
return ch;
|
||||
}
|
||||
#endif
|
||||
|
||||
+19
-13
@@ -2029,27 +2029,33 @@ static bool scr_vtmode(void *user, void *data) {
|
||||
|
||||
DWORD mode;
|
||||
HANDLE input = GetStdHandle(STD_INPUT_HANDLE);
|
||||
GetConsoleMode(input, &mode);
|
||||
if (node->i_value == RZ_VIRT_TERM_MODE_COMPLETE) {
|
||||
SetConsoleMode(input, mode & ENABLE_VIRTUAL_TERMINAL_INPUT);
|
||||
cons->term_raw |= ENABLE_VIRTUAL_TERMINAL_INPUT;
|
||||
} else {
|
||||
SetConsoleMode(input, mode & ~ENABLE_VIRTUAL_TERMINAL_INPUT);
|
||||
cons->term_raw &= ~ENABLE_VIRTUAL_TERMINAL_INPUT;
|
||||
if (GetConsoleMode(input, &mode)) {
|
||||
if (node->i_value == RZ_VIRT_TERM_MODE_COMPLETE) {
|
||||
// Enabling VT input must preserve the rest of the console input flags.
|
||||
SetConsoleMode(input, mode | ENABLE_VIRTUAL_TERMINAL_INPUT);
|
||||
cons->term_raw |= ENABLE_VIRTUAL_TERMINAL_INPUT;
|
||||
} else {
|
||||
SetConsoleMode(input, mode & ~ENABLE_VIRTUAL_TERMINAL_INPUT);
|
||||
cons->term_raw &= ~ENABLE_VIRTUAL_TERMINAL_INPUT;
|
||||
}
|
||||
}
|
||||
HANDLE streams[] = { GetStdHandle(STD_OUTPUT_HANDLE), GetStdHandle(STD_ERROR_HANDLE) };
|
||||
int i;
|
||||
if (node->i_value > RZ_VIRT_TERM_MODE_DISABLE) {
|
||||
for (i = 0; i < RZ_ARRAY_SIZE(streams); i++) {
|
||||
GetConsoleMode(streams[i], &mode);
|
||||
SetConsoleMode(streams[i],
|
||||
mode | ENABLE_PROCESSED_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
|
||||
if (GetConsoleMode(streams[i], &mode)) {
|
||||
// Output VT mode is an additive toggle on the current console output state.
|
||||
SetConsoleMode(streams[i],
|
||||
mode | ENABLE_PROCESSED_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i < RZ_ARRAY_SIZE(streams); i++) {
|
||||
GetConsoleMode(streams[i], &mode);
|
||||
SetConsoleMode(streams[i],
|
||||
mode & ~ENABLE_VIRTUAL_TERMINAL_PROCESSING & ~ENABLE_WRAP_AT_EOL_OUTPUT);
|
||||
if (GetConsoleMode(streams[i], &mode)) {
|
||||
// Disabling VT output should only clear the bits enabled by this callback.
|
||||
SetConsoleMode(streams[i],
|
||||
mode & ~(ENABLE_VIRTUAL_TERMINAL_PROCESSING | ENABLE_WRAP_AT_EOL_OUTPUT));
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -558,6 +558,10 @@ typedef struct rz_cons_t {
|
||||
struct termios term_raw, term_buf;
|
||||
#elif __WINDOWS__
|
||||
unsigned long term_raw, term_buf, term_pty;
|
||||
void *saved_input_handle;
|
||||
void *saved_output_handle;
|
||||
bool saved_input_console;
|
||||
bool saved_output_console;
|
||||
unsigned long old_input_mode, old_output_mode;
|
||||
ut32 old_cp;
|
||||
ut32 old_ocp;
|
||||
|
||||
Reference in New Issue
Block a user