diff options
| author | dam <dam@gudinoff> | 2024-04-30 00:05:26 +0100 |
|---|---|---|
| committer | dam <dam@gudinoff> | 2024-04-30 00:05:26 +0100 |
| commit | d7c2c312fe2ac08cadc3534a0a35357ef50cca20 (patch) | |
| tree | cfaa0289011dfe5e33e55e78c5fffa4e4ea0663e /modules/TUI | |
| parent | 1f2afe4db186342bda551e96bcd1d9b029d7c5ba (diff) | |
| download | task-time-tracker-d7c2c312fe2ac08cadc3534a0a35357ef50cca20.tar.zst task-time-tracker-d7c2c312fe2ac08cadc3534a0a35357ef50cca20.zip | |
WIP : Add logger and cleanup TUI module.
Diffstat (limited to 'modules/TUI')
| -rw-r--r-- | modules/TUI/module.jai | 38 | ||||
| -rw-r--r-- | modules/TUI/unix.jai | 297 | ||||
| -rw-r--r-- | modules/TUI/windows.jai | 118 |
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); |
