From c9d530504fc28fbc16540c97f292f60c6179e96a Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Thu, 23 Apr 2026 18:08:51 +0300 Subject: [PATCH] readline: fix The read_char () of the readline module does not work in Windows OS. (fixes #24686) --- vlib/readline/readline.v | 2 + vlib/readline/readline_windows.c.v | 72 ++++++++++++++++++- .../tests/projects_that_should_compile_test.v | 21 ++++++ 3 files changed, 94 insertions(+), 1 deletion(-) diff --git a/vlib/readline/readline.v b/vlib/readline/readline.v index a3ee4932e..887c542a6 100644 --- a/vlib/readline/readline.v +++ b/vlib/readline/readline.v @@ -36,4 +36,6 @@ pub mut: last_completion_offset int completion_list []string completion_callback fn (string) []string = unsafe { nil } +mut: + orig_stdin_mode u32 // Windows } diff --git a/vlib/readline/readline_windows.c.v b/vlib/readline/readline_windows.c.v index f02d078af..fc5ae200e 100644 --- a/vlib/readline/readline_windows.c.v +++ b/vlib/readline/readline_windows.c.v @@ -2,7 +2,8 @@ // Use of this source code is governed by an MIT license // that can be found in the LICENSE file. // -// TODO: Windows version needs to be implemented. +// Windows currently supports basic line reading and raw-character input. +// The richer line-editing functionality is still implemented only on nix. // Will serve as more advanced input method // based on the work of https://github.com/AmokHuginnsson/replxx // @@ -10,10 +11,79 @@ module readline import os +#include +#include + // needed for parity with readline_default.c.v struct Termios { } +fn C._getwch() i32 + +fn (mut r Readline) set_raw_mode(keep_processed_input bool) { + if os.is_atty(0) <= 0 || os.getenv('TERM') == 'dumb' { + r.is_tty = false + r.is_raw = false + return + } + stdin_handle := C.GetStdHandle(C.STD_INPUT_HANDLE) + if stdin_handle == C.INVALID_HANDLE_VALUE || !C.GetConsoleMode(stdin_handle, &r.orig_stdin_mode) { + r.is_tty = false + r.is_raw = false + return + } + mut raw_mode := r.orig_stdin_mode & (~u32(C.ENABLE_LINE_INPUT | C.ENABLE_ECHO_INPUT)) + if !keep_processed_input { + raw_mode &= ~u32(C.ENABLE_PROCESSED_INPUT) + } + if !C.SetConsoleMode(stdin_handle, raw_mode) { + r.is_tty = false + r.is_raw = false + return + } + r.is_tty = true + r.is_raw = true +} + +// enable_raw_mode enables the raw mode of the terminal. +// In raw mode all key presses are directly sent to the program and no interpretation is done. +pub fn (mut r Readline) enable_raw_mode() { + r.set_raw_mode(false) +} + +// enable_raw_mode_nosig enables the raw mode of the terminal while keeping console signal processing enabled. +pub fn (mut r Readline) enable_raw_mode_nosig() { + r.set_raw_mode(true) +} + +// disable_raw_mode disables the raw mode of the terminal. +pub fn (mut r Readline) disable_raw_mode() { + if !r.is_raw { + return + } + stdin_handle := C.GetStdHandle(C.STD_INPUT_HANDLE) + if stdin_handle != C.INVALID_HANDLE_VALUE { + C.SetConsoleMode(stdin_handle, r.orig_stdin_mode) + } + r.is_raw = false +} + +// read_char reads a single character. +pub fn (r Readline) read_char() !int { + ch := int(C._getwch()) + if ch < 0 { + return error('failed to read character') + } + if ch !in [0, 0xe0] { + return ch + } + extended := int(C._getwch()) + if extended < 0 { + return error('failed to read character') + } + return int((u32(ch) << 16) | u32(extended)) +} + // Only use standard os.get_line // Need implementation for readline capabilities // diff --git a/vlib/v/tests/projects_that_should_compile_test.v b/vlib/v/tests/projects_that_should_compile_test.v index ab07bcbfd..d5f7137ff 100644 --- a/vlib/v/tests/projects_that_should_compile_test.v +++ b/vlib/v/tests/projects_that_should_compile_test.v @@ -332,4 +332,25 @@ fn test_usecache_build_module_sumtype_uses_canonical_type_id_helper() { generated_c := os.read_file(generated_c_path)! assert generated_c.contains('payload__Foo_to_sumtype_payload__Value(payload__Foo* x, bool is_mut)') assert generated_c.contains('._typ = _v_type_idx_payload__Foo()') +fn test_readline_raw_mode_methods_should_check_for_windows() { + source_path := os.join_path(os.vtmp_dir(), 'readline_raw_mode_issue_24686_${os.getpid()}.v') + source := [ + 'module main', + '', + 'import readline', + '', + 'fn main() {', + '\tmut r := readline.Readline{}', + '\tr.enable_raw_mode()', + '\tr.disable_raw_mode()', + '\tr.enable_raw_mode_nosig()', + '\tprintln(r.read_char()!)', + '\tr.disable_raw_mode()', + '}', + ].join_lines() + write_file(source_path, source) + defer { + os.rm(source_path) or {} + } + _ = vrun_ok('-os windows -check', source_path) } -- 2.39.5