diff options
| author | dam <devnull@localhost> | 2024-02-10 02:53:54 +0000 |
|---|---|---|
| committer | dam <devnull@localhost> | 2024-02-10 02:53:54 +0000 |
| commit | 4aa3ae769bc079dd5cbd3419cc44b1d95986f837 (patch) | |
| tree | 3cdb560516c72811dd4b9f19e4876f3f0f9ea9e3 /TUI/windows.jai | |
| parent | e8ddaf3289a836c10f45b1b8c019922a99393488 (diff) | |
| download | task-time-tracker-4aa3ae769bc079dd5cbd3419cc44b1d95986f837.tar.zst task-time-tracker-4aa3ae769bc079dd5cbd3419cc44b1d95986f837.zip | |
Fixed some windows OS_ procedures. Simplified and fixed get_key().
Diffstat (limited to 'TUI/windows.jai')
| -rw-r--r-- | TUI/windows.jai | 180 |
1 files changed, 166 insertions, 14 deletions
diff --git a/TUI/windows.jai b/TUI/windows.jai index f704262..ff4d6a1 100644 --- a/TUI/windows.jai +++ b/TUI/windows.jai @@ -1,16 +1,23 @@ #scope_file +#import "Basic"; #import "Atomics"; #import "System"; #import "Windows"; // https://learn.microsoft.com/windows/win32/winprog/windows-data-types LPVOID :: *void; - LPDWORD :: *s32; BOOL :: bool; + CHAR :: s8; + WCHAR :: s16; SHORT :: s16; + USHORT :: u16; WORD :: u16; DWORD :: s32; + LPDWORD :: *s32; + + + PINPUT_RECORD :: *INPUT_RECORD; // https://learn.microsoft.com/windows/console/console-virtual-terminal-sequences // https://learn.microsoft.com/windows/console/console-virtual-terminal-sequences#designate-character-set @@ -18,11 +25,16 @@ kernel32 :: #system_library "kernel32"; + // TODO Cleanup unused foreign procedures. + // https://learn.microsoft.com/windows/console/getconsolescreenbufferinfo GetConsoleScreenBufferInfo :: (hConsoleOutput: HANDLE, lpConsoleScreenBufferInfo: *CONSOLE_SCREEN_BUFFER_INFO) -> bool #foreign kernel32; + // https://learn.microsoft.com/en-us/windows/console/readconsoleinput + ReadConsoleInputA :: (hConsoleInput: HANDLE, lpBuffer: PINPUT_RECORD, nLength: DWORD, lpNumberOfEventsRead: LPDWORD) -> success: bool #foreign kernel32; + // https://learn.microsoft.com/windows/console/readconsole - ReadConsoleA :: (hConsoleInput: HANDLE, lpBuffer: LPVOID, nNumberOfCharsToRead: DWORD, lpNumberOfCharsRead: LPVOID, pInputControl := LPVOID) -> bool #foreign kernel32; + ReadConsoleA :: (hConsoleInput: HANDLE, lpBuffer: LPVOID, nNumberOfCharsToRead: DWORD, lpNumberOfCharsRead: LPVOID, pInputControl := LPVOID) -> success: bool #foreign kernel32; // https://learn.microsoft.com/windows/console/getconsolemode GetConsoleMode :: (hConsoleHandle: HANDLE, lpMode: *DWORD) -> BOOL #foreign kernel32; @@ -33,8 +45,17 @@ // https://learn.microsoft.com/windows/win32/api/errhandlingapi/nf-errhandlingapi-getlasterror GetLastError :: () -> s32 #foreign kernel32; - // https://learn.microsoft.com/windows/win32/api/synchapi/nf-synchapi-waitforsingleobjectex - WaitForSingleObjectEx :: (hHandle: HANDLE, dwMilliseconds: DWORD, bAlertable: BOOL) -> s32 #foreign kernel32; + // https://learn.microsoft.com/windows/win32/api/synchapi/nf-synchapi-waitforsingleobject + WaitForSingleObject :: (hHandle: HANDLE, dwMilliseconds: DWORD) -> s32 #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 + PeekConsoleInputA :: (hConsoleInput: HANDLE, lpBuffer: PINPUT_RECORD, nLength: DWORD, lpNumberOfEventsRead: LPDWORD) -> bool #foreign kernel32; // https://learn.microsoft.com/en-us/windows/console/setconsolemode // https://learn.microsoft.com/en-us/windows/console/high-level-console-modes @@ -42,7 +63,7 @@ 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. - _UNUSED_0008_; + ENABLE_WINDOW_INPUT; ENABLE_MOUSE_INPUT; // 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. _UNUSED_0040_; @@ -86,6 +107,49 @@ dwMaximumWindowSize : COORD; } + INPUT_RECORD :: struct { + EventType : INPUT_RECORD_EVENT_TYPE; + union { + KeyEvent : KEY_EVENT_RECORD; + MouseEvent : MOUSE_EVENT_RECORD; + WindowBufferSizeEvent : WINDOW_BUFFER_SIZE_RECORD; + // These events are used internally and should be ignored. + //MenuEvent : MENU_EVENT_RECORD; + //FocusEvent : FOCUS_EVENT_RECORD; + } + //Event : EventUnion; + } + + INPUT_RECORD_EVENT_TYPE :: enum s32 { + KEY_EVENT :: 0x0001; + MOUSE_EVENT :: 0x0002; + WINDOW_BUFFER_SIZE_EVENT :: 0x0004; + MENU_EVENT :: 0x0008; + FOCUS_EVENT :: 0x0010; + } + + KEY_EVENT_RECORD :: struct { + bKeyDown : BOOL; + wRepeatCount : WORD; + wVirtualKeyCode : WORD; + wVirtualScanCode : WORD; + union { + UnicodeChar : WCHAR; + AsciiChar : CHAR; + } + dwControlKeyState : DWORD; + } + + MOUSE_EVENT_RECORD :: struct { + dwMousePosition : COORD; + dwButtonState : DWORD; + dwControlKeyState : DWORD; + dwEventFlags : DWORD; + } + + WINDOW_BUFFER_SIZE_RECORD :: struct { + dwSize : COORD; + } stdin: HANDLE; initial_stdin_mode: u32; @@ -98,6 +162,7 @@ //////////////////////////////////////////////////////////////////////////////// // Resize detection +was_resized : bool; resize_handler :: (signal_code : s32) #c_call { /* TODO @@ -131,6 +196,36 @@ restore_resize_handler :: () { */ } +peek_input :: inline () -> INPUT_RECORD { + record: INPUT_RECORD; + records_read: s32; + if PeekConsoleInputA(stdin, *record, 1, *records_read) == false { + _, error_message := get_error_value_and_string(); + assert(false, error_message); + } + return record; +} + +read_input :: inline () -> INPUT_RECORD { + record: INPUT_RECORD; + records_read: s32; + if ReadConsoleInputA(stdin, *record, 1, *records_read) == false { + _, error_message := get_error_value_and_string(); + assert(false, error_message); + } + return record; +} + +count_input :: inline () -> s32 { + count: s32; + if GetNumberOfConsoleInputEvents(stdin, *count) == false { + _, error_message := get_error_value_and_string(); + assert(false, error_message); + } + return count; +} + + //////////////////////////////////////////////////////////////////////////////// #scope_export @@ -187,44 +282,101 @@ OS_reset_terminal :: () { } OS_flush_input :: inline () { - // TODO - https://learn.microsoft.com/en-us/windows/console/flushconsoleinputbuffer - // BOOL WINAPI FlushConsoleInputBuffer(_In_ HANDLE hConsoleInput); /* NOTE 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. */ + success := FlushConsoleInputBuffer(stdin); + if success == false { + _, error_message := get_error_value_and_string(); + assert(false, error_message); // TODO A bit harsh arent we? + } } OS_read_input :: (buffer: *u8, bytes_to_read: s64) -> bytes_read: s64, error: bool = false, error_message: string = "" { + assert(bytes_to_read <= 0x7fff_ffff, "The Windows API only allows to read up to s32 bytes from the standard input."); + bytes_read: s32; success := ReadConsoleA(stdin, buffer, cast(s32)bytes_to_read, *bytes_read); if success == false { _, error_message := get_error_value_and_string(); return -1, true, error_message; } + // print(">%:%<", bytes_to_read, bytes_read); TODO DEBUG return bytes_read; } -OS_wait_for_input :: (timeout_milliseconds: s32) -> is_input_available: bool { +// timeout_milliseconds +// 0: do not wait +// -1: wait indefinitely +OS_wait_for_input :: (timeout_milliseconds: s32 = -1) -> is_input_available: bool { /* TODO Try to implement using: https://learn.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-waitforsingleobjectex + + This wait procedure on windows should check if next input is valid (keyboard down) before returning, otherwise it should discar that input and go back to wait (if more sleep is allowed). */ - poll_return := WaitForSingleObjectEx(stdin, timeout_milliseconds, true); - error_code, error_message := get_error_value_and_string(); // FIXME Not used. + expiration := current_time_monotonic() + to_apollo(timeout_milliseconds / 1000.0); + // Possible values for poll_return TODO NOT BEING USED WAIT_ABANDONED :: 0x00000080; - WAIT_IO_COMPLETION :: 0x000000C0; + //WAIT_IO_COMPLETION :: 0x000000C0; WAIT_OBJECT_0 :: 0x00000000; WAIT_TIMEOUT :: 0x00000102; WAIT_FAILED :: 0xFFFFFFFF; - - return ifx poll_return == 0 then true else false; + //return ifx poll_return == WAIT_OBJECT_0 then true else false; + + while true { + poll_return := WaitForSingleObject(stdin, timeout_milliseconds); + + // TODO Weird looking code... + if poll_return == { + case WAIT_TIMEOUT; + return false; + case WAIT_ABANDONED; + print("MUTEX STUFF"); // https://learn.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-waitforsingleobject + return false; + case WAIT_FAILED; + _, error_message := get_error_value_and_string(); + assert(false, error_message); + } +// if poll_return != WAIT_OBJECT_0 then return false; + + // Discard invalid input until a valid input is found or no input is left. + count := count_input(); + while count > 0 { + record := peek_input(); + if record.EventType == .KEY_EVENT && record.KeyEvent.bKeyDown == true || record.EventType == .WINDOW_BUFFER_SIZE_EVENT { + return true; + } + read_input(); // TODO Discard input. + count -= 1; + } + + now := current_time_monotonic(); + if now >= expiration then return false; + timeout_milliseconds = xx to_milliseconds(expiration - now); + } + + return false; } OS_was_terminal_resized :: () -> bool { - return atomic_swap(*was_resized, false); // TODO If the windows implementation is similar, we may push this into the main module file. + + defer was_resized = false; // TODO Not using this flag... what happens if ... not sure what... :thinking: + + flag: = false; + + record := peek_input(); + while record.EventType == .WINDOW_BUFFER_SIZE_EVENT { + read_input(); + record = peek_input(); + flag = true; + } + + //return was_resized; + return flag; } |
