aboutsummaryrefslogtreecommitdiff
path: root/modules/TUI
diff options
context:
space:
mode:
Diffstat (limited to 'modules/TUI')
-rw-r--r--modules/TUI/module.jai38
-rw-r--r--modules/TUI/unix.jai297
-rw-r--r--modules/TUI/windows.jai118
3 files changed, 259 insertions, 194 deletions
diff --git a/modules/TUI/module.jai b/modules/TUI/module.jai
index 5fd29f6..121aa42 100644
--- a/modules/TUI/module.jai
+++ b/modules/TUI/module.jai
@@ -2,6 +2,9 @@
#scope_file
+// - fix/implement/finish `TODO` on `TUI\module` (use some sort of buffet to reduce io/print calls)
+// - fix/implement/finish `TODO` on `TUI\module` (use `dirty_bit_flag` to only update ehat has been changed)
+
#if OS == {
case .LINUX;
#load "unix.jai";
@@ -525,16 +528,17 @@ read_input_line :: (count_limit: int, is_visible: bool = true) -> string, Key {
return result, key;
}
-start :: () {
+start :: () -> success := true #must {
if active == true return;
- // TODO Should start() call flush_input internally?
-
- setup_key_map(); // TODO This is being called multiple times... please fix me!
-
input_string.data = input_buffer.data;
input_string.count = 0;
input_override = xx Keys.None;
+
+ previous_logger = context.logger;
+ context.logger = module_logger;
+
+ setup_key_map();
write_strings(
Commands.HideCursor,
@@ -542,26 +546,34 @@ start :: () {
Commands.AlternateScreenBuffer,
Commands.EncodingUTF8,
Commands.CursorNormalMode,
- Commands.KeypadNumMode);
-
- previous_logger = context.logger;
- context.logger = module_logger;
+ Commands.KeypadNumMode
+ );
- OS_prepare_terminal();
+ if !OS_prepare_terminal() then return false;
active = true;
+
+ return;
}
-stop :: () {
+stop :: () -> success := true #must {
if active == false return;
+
active = false;
clear_style();
- OS_reset_terminal();
+
+ if !OS_reset_terminal() then return false;
context.logger = previous_logger;
- write_strings(Commands.MainScreenBuffer, Commands.RestoreCursorPosition, Commands.ShowCursor);
+ write_strings(
+ Commands.MainScreenBuffer,
+ Commands.RestoreCursorPosition,
+ Commands.ShowCursor
+ );
+
+ return;
}
flush_input :: () {
diff --git a/modules/TUI/unix.jai b/modules/TUI/unix.jai
index 8eeb6c0..a6cd467 100644
--- a/modules/TUI/unix.jai
+++ b/modules/TUI/unix.jai
@@ -1,22 +1,171 @@
#scope_file
/*
-TODO : then log all error on unix...
TODO : then do a good implementation of the libc functions about attributes...
*/
+USE_LIBC :: true;
+
#import "Atomics";
#import "System";
#import "POSIX";
+ // Set Attributes Actions.
+ // LINUX : https://sourceware.org/git/glibc.git -> ./sysdeps/unix/sysv/linux/bits/termios-tcflow.h
+ // MACOS : https://opensource.apple.com/source/xnu/xnu-792/bsd/sys/termios.h.auto.html
+ // OptionalActions :: enum s32 {
+ TCSANOW :: 0; // Change immediately.
+ TCSADRAIN :: 1; // Change when pending output is written.
+ TCSAFLUSH :: 2; // Flush pending input before changing.
+ // }
+
+ // TODO
+ // QueueSelector :: enum s32 {
+ // queue_selector
+ #if OS == {
+ case .LINUX;
+ // https://sourceware.org/git/glibc.git -> ./sysdeps/unix/sysv/linux/bits/termios.h
+ TCIFLUSH :: 0; // Discard data received but not yet read.
+ TCOFLUSH :: 1; // Discard data written but not yet sent.
+ TCIOFLUSH :: 2; // Discard all pending data.
+
+ case .MACOS;
+ // https://opensource.apple.com/source/xnu/xnu-792/bsd/sys/termios.h.auto.html
+ TCIFLUSH :: 1; // Discard data received but not yet read.
+ TCOFLUSH :: 2; // Discard data written but not yet sent.
+ TCIOFLUSH :: 3; // Discard all pending data.
+ }
+ }
+
+ // LINUX : https://sourceware.org/git/glibc.git -> ./sysdeps/unix/sysv/linux/bits/termios-struct.h
+ // MACOS : https://opensource.apple.com/source/xnu/xnu-792/bsd/sys/termios.h.auto.html
+ #if OS == {
+ case .LINUX;
+ NCCS :: 32;
+
+ case .MACOS;
+ NCCS :: 20;
+ }
+
+ // Information for the termios.h enums is platform dependent and was retrieved from:
+ // https://elixir.bootlin.com/glibc/latest/source/sysdeps/unix/sysv/linux/bits/termios.h
+
+
+
+ // The struct termios.
+ Terminal_IO_Mode :: struct {
+ c_iflag : Input_Modes; // Input mode flags.
+ c_oflag : Output_Modes; // Output mode flags.
+ c_cflag : Control_Modes; // Control modes flags.
+ c_lflag : Local_Modes; // Local modes flags.
+ c_line : u8; // Line discipline.
+ c_cc : [NCCS]Control_Chars; // Control characters.
+ c_ispeed : u32; // Input speed (baud rates).
+ c_ospeed : u32; // Output speed (baud rates).
+ }
+
+ // Input modes.
+ Input_Modes :: enum_flags u32 {
+ IGNBRK :: 0000001; // Ignore break condition.
+ BRKINT :: 0000002; // Signal interrupt on break.
+ IGNPAR :: 0000004; // Ignore characters with parity errors.
+ PARMRK :: 0000010; // Mark parity and framing errors.
+ INPCK :: 0000020; // Enable input parity check.
+ ISTRIP :: 0000040; // Strip 8th bit off characters.
+ INLCR :: 0000100; // Map NL to CR on input.
+ IGNCR :: 0000200; // Ignore CR.
+ ICRNL :: 0000400; // Map CR to NL on input.
+ IUCLC :: 0001000; // Translate upper case input to lower case (not in POSIX).
+ IXON :: 0002000; // Enable start/stop output control.
+ IXANY :: 0004000; // Any character will restart after stop.
+ IXOFF :: 0010000; // Enable start/stop input control.
+ IMAXBEL :: 0020000; // Ring bell when input queue is full (not in POSIX).
+ IUTF8 :: 0040000; // Input is UTF8 (not in POSIX).
+ }
+
+ // Output modes.
+ Output_Modes :: enum_flags u32 {
+ OPOST :: 0000001; // Perform output processing.
+ OLCUC :: 0000002; // Map lowercase characters to uppercase on output (not in POSIX).
+ ONLCR :: 0000004; // Map NL to CR-NL on output.
+ OCRNL :: 0000010; // Map CR to NL.
+ ONOCR :: 0000020; // Discard CR's when on column 0.
+ ONLRET :: 0000040; // Move to column 0 on NL.
+ OFILL :: 0000100; // Send fill characters for delays.
+ OFDEL :: 0000200; // Fill is DEL.
+ VTDLY :: 0040000; // Select vertical-tab delays:
+ VT0 :: 0000000; // Vertical-tab delay type 0.
+ VT1 :: 0040000; // Vertical-tab delay type 1.
+ }
+
+ // Control modes.
+ Control_Modes :: enum u32 {
+ CS5 :: 0000000; // 5 bits per byte.
+ CS6 :: 0000020; // 6 bits per byte.
+ CS7 :: 0000040; // 7 bits per byte.
+ CS8 :: 0000060; // 8 bits per byte.
+ CSIZE :: 0000060; // Number of bits per byte (mask).
+ CSTOPB :: 0000100; // Two stop bits instead of one.
+ CREAD :: 0000200; // Enable receiver.
+ PARENB :: 0000400; // Parity enable.
+ PARODD :: 0001000; // Odd parity instead of even.
+ HUPCL :: 0002000; // Hang up on last close.
+ CLOCAL :: 0004000;
+ }
+
+ // Local modes.
+ Local_Modes :: enum_flags u32 {
+ ISIG :: 0000001; // Enable signals.
+ ICANON :: 0000002; // Do erase and kill processing.
+ ECHO :: 0000010; // Enable echo.
+ ECHOE :: 0000020; // Visual erase for ERASE.
+ ECHOK :: 0000040; // Echo NL after KILL.
+ ECHONL :: 0000100; // Echo NL even if ECHO is off.
+ NOFLSH :: 0000200; // Disable flush after interrupt.
+ TOSTOP :: 0000400; // Send SIGTTOU for background output.
+ IEXTEN :: 0100000; // Enable DISCARD and LNEXT.
+ }
+
+ // Control Characters
+ Control_Chars :: enum u8 {
+ VINTR :: 0;
+ VQUIT :: 1;
+ VERASE :: 2;
+ VKILL :: 3;
+ VEOF :: 4;
+ VTIME :: 5; // Time-out value (tenths of a second) [!ICANON].
+ VMIN :: 6; // Minimum number of bytes read at once [!ICANON].
+ VSWTC :: 7;
+ VSTART :: 8;
+ VSTOP :: 9;
+ VSUSP :: 10;
+ VEOL :: 11;
+ VREPRINT :: 12;
+ VDISCARD :: 13;
+ VWERASE :: 14;
+ VLNEXT :: 15;
+ VEOL2 :: 16;
+ }
+
+
+#if USE_LIBC {
// Required to do unlocking input.
libc :: #system_library "libc";
+
+ // https://codebrowser.dev/glibc/glibc/sysdeps/unix/sysv/linux/tcsetattr.c.html
+ tcsetattr :: (fd: s32, optional_actions: s32, termios_p : *Terminal_IO_Mode) -> s32 #foreign libc;
+
+ // https://codebrowser.dev/glibc/glibc/sysdeps/unix/sysv/linux/tcgetattr.c.html
+ tcgetattr :: (fd: s32, termios_p: *Terminal_IO_Mode) -> s32 #foreign libc;
+
+ // https://codebrowser.dev/glibc/glibc/sysdeps/unix/sysv/linux/tcflush.c.html
+ tcflush :: (fd: s32, queue_selector: s32) -> s32 #foreign libc;
+}
+else {
// https://codebrowser.dev/glibc/glibc/sysdeps/unix/sysv/linux/tcsetattr.c.html
- // tcsetattr :: (fd: s32, optional_actions: s32, termios_p : *Terminal_IO_Mode) -> s32 #foreign libc;
tcsetattr :: (fd: s32, optional_actions: s32, termios_p : *Terminal_IO_Mode) -> s32 {
- // TODO IMPLEMENT ME
-
+
#if OS == .LINUX {
TCSETS :: 0x5402;
TCSETSW :: 0x5403;
@@ -71,7 +220,6 @@ TODO : then do a good implementation of the libc functions about attributes...
}
// https://codebrowser.dev/glibc/glibc/sysdeps/unix/sysv/linux/tcgetattr.c.html
- // tcgetattr :: (fd: s32, termios_p: *Terminal_IO_Mode) -> s32 #foreign libc;
tcgetattr :: (fd: s32, termios_p: *Terminal_IO_Mode) -> s32 {
TCSETS :: 0x5402;
TCSETSW :: 0x5403;
@@ -135,128 +283,25 @@ TODO : then do a good implementation of the libc functions about attributes...
}
// https://codebrowser.dev/glibc/glibc/sysdeps/unix/sysv/linux/tcflush.c.html
- // tcflush :: (fd: s32, queue_selector: s32) -> s32 #foreign libc;
tcflush :: inline (fd: s32, queue_selector: s32) -> s32 {
TCFLSH :: 0x540B;
return ioctl(fd, TCFLSH, queue_selector);
}
-
- // Information for the termios.h enums is platform dependent and was retrieved from:
- // https://elixir.bootlin.com/glibc/latest/source/sysdeps/unix/sysv/linux/bits/termios.h
-
- // Set Attributes Actions.
- SetAttributesActions :: enum u32 {
- TCSANOW :: 0; // Change immediately.
- TCSADRAIN :: 1; // Change when pending output is written.
- TCSAFLUSH :: 2; // Flush pending input before changing.
- }
-
- // Input modes.
- Input_Modes :: enum_flags u32 {
- IGNBRK :: 0000001; // Ignore break condition.
- BRKINT :: 0000002; // Signal interrupt on break.
- IGNPAR :: 0000004; // Ignore characters with parity errors.
- PARMRK :: 0000010; // Mark parity and framing errors.
- INPCK :: 0000020; // Enable input parity check.
- ISTRIP :: 0000040; // Strip 8th bit off characters.
- INLCR :: 0000100; // Map NL to CR on input.
- IGNCR :: 0000200; // Ignore CR.
- ICRNL :: 0000400; // Map CR to NL on input.
- IUCLC :: 0001000; // Translate upper case input to lower case (not in POSIX).
- IXON :: 0002000; // Enable start/stop output control.
- IXANY :: 0004000; // Any character will restart after stop.
- IXOFF :: 0010000; // Enable start/stop input control.
- IMAXBEL :: 0020000; // Ring bell when input queue is full (not in POSIX).
- IUTF8 :: 0040000; // Input is UTF8 (not in POSIX).
- }
-
- // Output modes.
- Output_Modes :: enum_flags u32 {
- OPOST :: 0000001; // Perform output processing.
- OLCUC :: 0000002; // Map lowercase characters to uppercase on output (not in POSIX).
- ONLCR :: 0000004; // Map NL to CR-NL on output.
- OCRNL :: 0000010; // Map CR to NL.
- ONOCR :: 0000020; // Discard CR's when on column 0.
- ONLRET :: 0000040; // Move to column 0 on NL.
- OFILL :: 0000100; // Send fill characters for delays.
- OFDEL :: 0000200; // Fill is DEL.
- VTDLY :: 0040000; // Select vertical-tab delays:
- VT0 :: 0000000; // Vertical-tab delay type 0.
- VT1 :: 0040000; // Vertical-tab delay type 1.
- }
-
- // Control modes.
- Control_Modes :: enum u32 {
- CS5 :: 0000000; // 5 bits per byte.
- CS6 :: 0000020; // 6 bits per byte.
- CS7 :: 0000040; // 7 bits per byte.
- CS8 :: 0000060; // 8 bits per byte.
- CSIZE :: 0000060; // Number of bits per byte (mask).
- CSTOPB :: 0000100; // Two stop bits instead of one.
- CREAD :: 0000200; // Enable receiver.
- PARENB :: 0000400; // Parity enable.
- PARODD :: 0001000; // Odd parity instead of even.
- HUPCL :: 0002000; // Hang up on last close.
- CLOCAL :: 0004000;
- }
-
- // Local modes.
- Local_Modes :: enum_flags u32 {
- ISIG :: 0000001; // Enable signals.
- ICANON :: 0000002; // Do erase and kill processing.
- ECHO :: 0000010; // Enable echo.
- ECHOE :: 0000020; // Visual erase for ERASE.
- ECHOK :: 0000040; // Echo NL after KILL.
- ECHONL :: 0000100; // Echo NL even if ECHO is off.
- NOFLSH :: 0000200; // Disable flush after interrupt.
- TOSTOP :: 0000400; // Send SIGTTOU for background output.
- IEXTEN :: 0100000; // Enable DISCARD and LNEXT.
- }
+}
- // Control Characters
- Control_Chars :: enum u8 {
- VINTR :: 0;
- VQUIT :: 1;
- VERASE :: 2;
- VKILL :: 3;
- VEOF :: 4;
- VTIME :: 5; // Time-out value (tenths of a second) [!ICANON].
- VMIN :: 6; // Minimum number of bytes read at once [!ICANON].
- VSWTC :: 7;
- VSTART :: 8;
- VSTOP :: 9;
- VSUSP :: 10;
- VEOL :: 11;
- VREPRINT :: 12;
- VDISCARD :: 13;
- VWERASE :: 14;
- VLNEXT :: 15;
- VEOL2 :: 16;
- }
+////////////////////////////////////////////////////////////////////////////////
- // The struct termios.
- Terminal_IO_Mode :: struct {
- c_iflag : Input_Modes; // Input mode flags.
- c_oflag : Output_Modes; // Output mode flags.
- c_cflag : Control_Modes; // Control modes flags.
- c_lflag : Local_Modes; // Local modes flags.
- c_line : u8; // Line discipline.
- c_cc : [32]Control_Chars;// Control characters.
- c_ispeed : u32; // Input speed (baud rates).
- c_ospeed : u32; // Output speed (baud rates).
- }
-
initial_tio_mode: Terminal_IO_Mode;
raw_tio_mode: Terminal_IO_Mode;
+ was_resized : bool;
+
////////////////////////////////////////////////////////////////////////////////
-// Resize detection
-was_resized : bool;
resize_handler :: (signal_code : s32) #c_call {
new_context : Context;
push_context new_context {
- if signal_code != SIGWINCH then return;
+ if signal_code != SIGWINCH then return;
atomic_swap(*was_resized, true);
}
}
@@ -279,13 +324,14 @@ restore_resize_handler :: () {
#scope_export
-OS_prepare_terminal :: () -> success := true { // On critical paths, we may use the #must for the success return value.
+OS_prepare_terminal :: () -> success := true {
error: int = ---;
error = tcgetattr(STDIN_FILENO, *initial_tio_mode);
if error {
error_code, error_string := get_error_value_and_string();
log_error("Failed to get initial_tio_mode: code %, %", error_code, error_string);
+ return false;
}
raw_tio_mode = initial_tio_mode;
@@ -297,7 +343,7 @@ OS_prepare_terminal :: () -> success := true { // On critical paths, we may use
raw_tio_mode.c_cc[Control_Chars.VMIN] = 1;
raw_tio_mode.c_cc[Control_Chars.VTIME] = 0;
- error = tcsetattr(STDIN_FILENO, 0, *raw_tio_mode);
+ error = tcsetattr(STDIN_FILENO, TCSANOW, *raw_tio_mode);
if error {
error_code, error_string := get_error_value_and_string();
log_error("Failed to set raw_tio_mode: code %, %", error_code, error_string);
@@ -311,7 +357,7 @@ OS_prepare_terminal :: () -> success := true { // On critical paths, we may use
OS_reset_terminal :: inline () -> success := true {
restore_resize_handler();
- error := tcsetattr(STDIN_FILENO, 0, *initial_tio_mode);
+ error := tcsetattr(STDIN_FILENO, TCSANOW, *initial_tio_mode);
if error {
error_code, error_string := get_error_value_and_string();
log_error("Failed to set initial_tio_mode: code %, %", error_code, error_string);
@@ -321,7 +367,6 @@ OS_reset_terminal :: inline () -> success := true {
}
OS_flush_input :: inline () -> success := true {
- TCIFLUSH :: 0; // TODO Is this always zero in all systems?
error := tcflush(STDIN_FILENO, TCIFLUSH);
if error {
error_code, error_string := get_error_value_and_string();
@@ -331,7 +376,6 @@ OS_flush_input :: inline () -> success := true {
return;
}
-// TODO Nothing is checking for the errors returned by this... shame!
OS_read_input :: (buffer: *u8, bytes_to_read: s64) -> bytes_read: s64, success := true {
bytes_read := read(STDIN_FILENO, buffer, xx bytes_to_read);
if bytes_read < 0 {
@@ -345,12 +389,21 @@ OS_read_input :: (buffer: *u8, bytes_to_read: s64) -> bytes_read: s64, success :
// timeout_milliseconds
// 0: do not wait
// -1: wait indefinitely
-OS_wait_for_input :: (timeout_milliseconds: s32 = -1) -> is_input_available: bool {
+OS_wait_for_input :: (timeout_milliseconds: s32 = -1) -> is_input_available: bool, success := true {
fds := pollfd.[ .{ fd = STDIN_FILENO, events = POLLIN, revents = 0 } ];
nfds := fds.count;
- poll_return := poll(fds.data, xx nfds, xx timeout_milliseconds); // TODO Wait for input using poll syscall. This breaks and throws '-1 | 4 | Interrupted system call' when we resize the window while polling.
- error_code, error_message := get_error_value_and_string(); // FIXME Not used.
- return ifx poll_return > 0 then true else false;
+ result := poll(fds.data, xx nfds, xx timeout_milliseconds); // Returns '-1' with errno '4 | Interrupted system call' on window resize.
+
+ if result == -1 {
+ error_code, error_string := get_error_value_and_string();
+ // Ignore window resize events (error_code 4).
+ if error_code != 4 {
+ log_error("Unexpected error while waiting for input: code %, %", error_code, error_string);
+ return false, false;
+ }
+ }
+
+ return ifx result > 0 then true else false;
}
OS_was_terminal_resized :: () -> bool {
diff --git a/modules/TUI/windows.jai b/modules/TUI/windows.jai
index 5755ef4..608266d 100644
--- a/modules/TUI/windows.jai
+++ b/modules/TUI/windows.jai
@@ -9,47 +9,30 @@
CP_UTF8 :: 65001;
// https://learn.microsoft.com/windows/win32/winprog/windows-data-types
- LPVOID :: *void;
- BOOL :: bool;
- CHAR :: s8;
- WCHAR :: s16;
- SHORT :: s16;
- USHORT :: u16;
- WORD :: u16;
- DWORD :: u32;
- LPDWORD :: *u32;
- UINT :: u32;
-
- PINPUT_RECORD :: *INPUT_RECORD;
-
- // https://learn.microsoft.com/en-us/windows/console/readconsoleinput
- ReadConsoleInputW :: (hConsoleInput: HANDLE, lpBuffer: PINPUT_RECORD, nLength: DWORD, lpNumberOfEventsRead: LPDWORD) -> success: bool #foreign kernel32;
-
- // https://learn.microsoft.com/windows/console/readconsole
- ReadConsoleW :: (hConsoleInput: HANDLE, lpBuffer: LPVOID, nNumberOfchars_to_read: DWORD, lpNumberOfchars_read: LPVOID, pInputControl: LPVOID) -> success: bool #foreign kernel32;
-
- // https://learn.microsoft.com/windows/console/flushconsoleinputbuffer
- FlushConsoleInputBuffer :: (hConsoleInput: HANDLE) -> bool #foreign kernel32;
-
- // https://learn.microsoft.com/windows/console/getnumberofconsoleinputevents
- GetNumberOfConsoleInputEvents :: (hConsoleInput: HANDLE, lpcNumberOfEvents: LPDWORD) -> bool #foreign kernel32;
-
- // https://learn.microsoft.com/en-us/windows/console/peekconsoleinput
- PeekConsoleInputW :: (hConsoleInput: HANDLE, lpBuffer: PINPUT_RECORD, nLength: DWORD, lpNumberOfEventsRead: LPDWORD) -> bool #foreign kernel32;
+ LPVOID :: *void;
+ BOOL :: bool;
+ CHAR :: s8;
+ WCHAR :: s16;
+ SHORT :: s16;
+ WORD :: u16;
+ DWORD :: u32;
+ LPDWORD :: *u32;
+ UINT :: u32;
+ PINPUT_RECORD :: *INPUT_RECORD;
// https://learn.microsoft.com/en-us/windows/console/setconsolemode
// https://learn.microsoft.com/en-us/windows/console/high-level-console-modes
Console_Input_Mode :: enum_flags u32 {
- ENABLE_PROCESSED_INPUT; // If enable, control keys (Ctrl+C, Backspace, ...) are processed by the system.
- ENABLE_LINE_INPUT; // If enable, ReadFile or ReadConsole function return on CR; otherwise they return when one or more characters are available.
- ENABLE_ECHO_INPUT; // Echoes input on screen. Only available if ENABLE_LINE_INPUT is set.
+ ENABLE_PROCESSED_INPUT; // If set, control sequences are processed by the system.
+ ENABLE_LINE_INPUT; // If set, ReadFile and ReadConsole function return on CR; otherwise return when characters are available.
+ ENABLE_ECHO_INPUT; // If set, Echoes input on screen. Only available if ENABLE_LINE_INPUT is set.
ENABLE_WINDOW_INPUT;
- ENABLE_MOUSE_INPUT; // Makes mouse events available to the ReadConsoleInput function.
- ENABLE_INSERT_MODE; // If enabled, text entered will be inserted at the current cursor location and all text following that location will not be overwritten. When disabled, all following text will be overwritten.
+ ENABLE_MOUSE_INPUT;
+ ENABLE_INSERT_MODE;
_UNUSED_0040_;
_UNUSED_0080_;
_UNUSED_0100_;
- ENABLE_VIRTUAL_TERMINAL_INPUT; // If enable, makes user input available to the ReadConsole function.
+ ENABLE_VIRTUAL_TERMINAL_INPUT; // If set, makes user input available to the ReadConsole function.
_UNUSED_0400_;
_UNUSED_0800_;
}
@@ -57,11 +40,11 @@
// https://learn.microsoft.com/en-us/windows/console/setconsolemode
// https://learn.microsoft.com/en-us/windows/console/high-level-console-modes
Console_Output_Mode :: enum_flags u32 {
- ENABLE_PROCESSED_OUTPUT; //
- ENABLE_WRAP_AT_EOL_OUTPUT; //
- ENABLE_VIRTUAL_TERMINAL_PROCESSING; //
- DISABLE_NEWLINE_AUTO_RETURN; //
- ENABLE_LVB_GRID_WORLDWIDE; //
+ ENABLE_PROCESSED_OUTPUT; // If set, ASCII control sequences are processed by the system.
+ ENABLE_WRAP_AT_EOL_OUTPUT; // If set, the cursor moves to the beginning of the next row when it reaches the end of the current row.
+ ENABLE_VIRTUAL_TERMINAL_PROCESSING; // If set, VT100 control sequences are processed by the system.
+ DISABLE_NEWLINE_AUTO_RETURN;
+ ENABLE_LVB_GRID_WORLDWIDE;
_UNUSED_0020_;
_UNUSED_0040_;
_UNUSED_0080_;
@@ -123,6 +106,23 @@
bSetFocus : BOOL;
}
+ // https://learn.microsoft.com/en-us/windows/console/readconsoleinput
+ ReadConsoleInputW :: (hConsoleInput: HANDLE, lpBuffer: PINPUT_RECORD, nLength: DWORD, lpNumberOfEventsRead: LPDWORD) -> success: bool #foreign kernel32;
+
+ // https://learn.microsoft.com/windows/console/readconsole
+ ReadConsoleW :: (hConsoleInput: HANDLE, lpBuffer: LPVOID, nNumberOfchars_to_read: DWORD, lpNumberOfchars_read: LPVOID, pInputControl: LPVOID) -> success: bool #foreign kernel32;
+
+ // https://learn.microsoft.com/windows/console/flushconsoleinputbuffer
+ FlushConsoleInputBuffer :: (hConsoleInput: HANDLE) -> bool #foreign kernel32;
+
+ // https://learn.microsoft.com/windows/console/getnumberofconsoleinputevents
+ GetNumberOfConsoleInputEvents :: (hConsoleInput: HANDLE, lpcNumberOfEvents: LPDWORD) -> bool #foreign kernel32;
+
+ // https://learn.microsoft.com/en-us/windows/console/peekconsoleinput
+ PeekConsoleInputW :: (hConsoleInput: HANDLE, lpBuffer: PINPUT_RECORD, nLength: DWORD, lpNumberOfEventsRead: LPDWORD) -> bool #foreign kernel32;
+
+////////////////////////////////////////////////////////////////////////////////
+
stdin: HANDLE;
initial_stdin_mode: u32;
raw_stdin_mode: Console_Input_Mode;
@@ -135,6 +135,7 @@
widechar_buffer: [512] u16;
+////////////////////////////////////////////////////////////////////////////////
peek_input :: inline () -> INPUT_RECORD, success := true {
record: INPUT_RECORD;
@@ -172,19 +173,19 @@ count_input :: inline () -> u32, success := true {
#scope_export
-OS_prepare_terminal :: () {
+OS_prepare_terminal :: () -> success := true {
// stdin
stdin = GetStdHandle(STD_INPUT_HANDLE);
if stdin == INVALID_HANDLE_VALUE {
error_code, error_string := get_error_value_and_string();
log_error("Invalid input handler: code %, %", error_code, error_string);
- return;
+ return false;
}
if xx GetConsoleMode(stdin, *initial_stdin_mode) == false {
error_code, error_string := get_error_value_and_string();
log_error("Failed to get input mode: code %, %", error_code, error_string);
- return;
+ return false;
}
raw_stdin_mode = (cast(Console_Input_Mode) initial_stdin_mode);
raw_stdin_mode |= (.ENABLE_VIRTUAL_TERMINAL_INPUT);
@@ -193,7 +194,7 @@ OS_prepare_terminal :: () {
if xx SetConsoleMode(stdin, xx raw_stdin_mode) == false {
error_code, error_string := get_error_value_and_string();
log_error("Failed to set input mode: code %, %", error_code, error_string);
- return;
+ return false;
}
// stdout
@@ -201,12 +202,12 @@ OS_prepare_terminal :: () {
if stdout == INVALID_HANDLE_VALUE {
error_code, error_string := get_error_value_and_string();
log_error("Invalid output handler: code %, %", error_code, error_string);
- return;
+ return false;
}
if xx GetConsoleMode(stdout, *initial_stdout_mode) == false {
error_code, error_string := get_error_value_and_string();
log_error("Failed to get output mode: code %, %", error_code, error_string);
- return;
+ return false;
}
raw_stdout_mode = (cast(Console_Output_Mode) initial_stdout_mode);
raw_stdout_mode |= (.ENABLE_VIRTUAL_TERMINAL_PROCESSING | .ENABLE_PROCESSED_OUTPUT | .ENABLE_WRAP_AT_EOL_OUTPUT);
@@ -214,7 +215,7 @@ OS_prepare_terminal :: () {
if xx SetConsoleMode(stdout, xx raw_stdout_mode) == false {
error_code, error_string := get_error_value_and_string();
log_error("Failed to set output mode: code %, %", error_code, error_string);
- return;
+ return false;
}
// Acording to [documentation](https://learn.microsoft.com/en-us/windows/win32/intl/code-pages)
@@ -223,26 +224,27 @@ OS_prepare_terminal :: () {
// As long we use the Unicode functions, we shouldn't need to set the code page to UTF8.
// SetConsoleCP(CP_UTF8);
// SetConsoleOutputCP(CP_UTF8);
+
+ return;
}
-OS_reset_terminal :: () {
+OS_reset_terminal :: () -> success := true {
if xx SetConsoleMode(stdin, initial_stdin_mode) == false {
error_code, error_string := get_error_value_and_string();
log_error("Failed to reset input mode: code %, %", error_code, error_string);
- return;
+ return false;
}
if xx SetConsoleMode(stdout, initial_stdout_mode) == false {
error_code, error_string := get_error_value_and_string();
log_error("Failed to reset output mode: code %, %", error_code, error_string);
- return;
+ return false;
}
+ return;
}
OS_flush_input :: inline () {
- /*
- This API is not recommended and does not have a virtual terminal equivalent.
- Attempting to empty the input queue all at once can destroy state in the queue in an unexpected manner.
- */
+ // This API is not recommended and does not have a virtual terminal equivalent.
+ // Attempting to empty the input queue all at once can destroy state in the queue in an unexpected manner.
if FlushConsoleInputBuffer(stdin) == false {
error_code, error_string := get_error_value_and_string();
log_error("Failed to flush input: code %, %", error_code, error_string);
@@ -318,13 +320,11 @@ OS_read_input :: (buffer: *u8, bytes_to_read: s64) -> bytes_read: s64, success :
// 0: do not wait
// -1: wait indefinitely
OS_wait_for_input :: (timeout_milliseconds: s32 = -1) -> is_input_available: bool, success := true {
- /*
- The Windows API provides all input events (keyboard, mouse, window resize) on a single input buffer.
- To make it match this module's API, we need to do some pre-processing while waiting for input.
- This means that OS_wait_for_input will peek at the input events, signal if a window resize is found,
- and discard unwanted events (like button release events).
- A similar logic is applied in OS_read_input.
- */
+ // The Windows API provides all input events (keyboard, mouse, window resize) on a single input buffer.
+ // To make it match this module's API, we need to do some pre-processing while waiting for input.
+ // This means that OS_wait_for_input will peek at the input events, signal if a window resize is found,
+ // and discard unwanted events (like button release events).
+ // A similar logic is applied in OS_read_input.
expiration := current_time_monotonic() + to_apollo(timeout_milliseconds / 1000.0);