diff options
Diffstat (limited to 'TUI')
| -rw-r--r-- | TUI/module.jai | 125 | ||||
| -rw-r--r-- | TUI/unix.jai | 8 |
2 files changed, 112 insertions, 21 deletions
diff --git a/TUI/module.jai b/TUI/module.jai index a12b762..5e19e4f 100644 --- a/TUI/module.jai +++ b/TUI/module.jai @@ -97,6 +97,7 @@ input_buffer : [64] u8; input_string : string; input_override : Key; +row := 5; set_next_key :: (key: Key) { input_override = key; @@ -104,6 +105,22 @@ set_next_key :: (key: Key) { get_key :: (timeout_milliseconds: s32 = -1) -> Key { + // BBBB BBBB & 1100 0000 == 10XX XXXX -> is continuation byte + is_utf8_continuation_byte :: inline (byte: u8) -> bool { + return (byte & 0xC0) == 0x80; + } + + // BBBB BBBB & 1110 0000 == 110X XXXX -> 1 initial + 1 continuation byte + // BBBB BBBB & 1111 0000 == 1110 XXXX -> 1 initial + 2 continuation byte + // BBBB BBBB & 1111 1000 == 1111 0XXX -> 1 initial + 3 continuation byte + count_utf8_bytes :: inline (byte: u8) -> int { + if (byte & 0xE0) == 0xC0 return 1+1; + if (byte & 0xF0) == 0xE0 return 1+2; + if (byte & 0xF8) == 0xF0 return 1+3; + return 1; + } + + /* TODO WIP Implement UTF-8 support, i.e., merge continuation bytes if needed: @@ -123,23 +140,73 @@ get_key :: (timeout_milliseconds: s32 = -1) -> Key { } if OS_was_terminal_resized() return xx Keys.Resize; - + + // FIXME Old version... + // if input_string.count > 0 { + // defer advance(*input_string, 1); + // return input_string[0]; + // } + // FIXME New version... if input_string.count > 0 { - defer advance(*input_string, 1); - return input_string[0]; + utf8_bytes := count_utf8_bytes(input_string[0]); + key: Key; + for 1..utf8_bytes { + key = key << 8; + key |= input_string[utf8_bytes-it]; + } + advance(*input_string, utf8_bytes); + return key; } is_input_available := OS_wait_for_input(timeout_milliseconds); if OS_was_terminal_resized() return xx Keys.Resize; + // FIXME Old version... + // if is_input_available { + // bytes_read := OS_read_input(input_buffer.data, input_buffer.count); // TODO Does not check for read errors. + // if bytes_read > 0 { + // input_string.data = input_buffer.data; + // input_string.count = bytes_read; + // defer advance(*input_string, 1); + // return input_string[0]; + // } + // } + // FIXME New version... if is_input_available { bytes_read := OS_read_input(input_buffer.data, input_buffer.count); // TODO Does not check for read errors. if bytes_read > 0 { input_string.data = input_buffer.data; input_string.count = bytes_read; - defer advance(*input_string, 1); - return input_string[0]; + utf8_bytes := count_utf8_bytes(input_string[0]); + + key: Key; + for 1..utf8_bytes { + key = key << 8; + key |= input_string[utf8_bytes-it]; + } + + /// /// /// /// /// /// /// /// /// + // DEBUG + br, bc := get_cursor_position(); + column := 3; + row += 1; + nr, rc := get_terminal_size(); + + if row >= (rc - 5) then row = 5; + + set_cursor_position(row, column); + for 0..input_string.count-1 { + print("%:% | ", + FormatInt.{base= 2, minimum_digits = 8, value = input_string[it]}, + FormatInt.{base= 16, minimum_digits = 2, value = input_string[it]}, + ); // TODO DEBUG + } + set_cursor_position(br, bc); + /// /// /// /// /// /// /// /// /// + + advance(*input_string, utf8_bytes); + return key; } } return xx Keys.None; @@ -191,7 +258,7 @@ stop :: () { } flush_input :: () { - // TODO + OS_flush_input(); } draw_box :: (x: int, y: int, width: int, height: int) { @@ -256,16 +323,23 @@ get_terminal_size :: () -> rows: int, columns: int { flush_input(); write_string(Commands.QueryWindowSizeInChars); - input := get_str(64); - - // Expected message format: [8;<r>;<c>t + + // Expected response format: \e[8;<r>;<c>t // where <r> is the number of rows and <c> of columns. - assert( - input[0] == #char "\e" && - input[1] == #char "[" && - input[2] == #char "8", - //input[input.count-1] == #char "t", + FORMAT :: "\e[8;<r>;<c>t"; + + // Discard head noise. + while input.count >= 3 && (input[0] != FORMAT[0] || input[1] != FORMAT[1] || input[2] != FORMAT[2]) { + advance(*input); + } + + // Discard tail noise. + while input.count >= 2 && input[input.count-1] != FORMAT[FORMAT.count-1] { + input.count -= 1; + } + + assert(input[0] == FORMAT[0] && input[1] == FORMAT[1] && input[2] == FORMAT[2] && input[input.count-1] == FORMAT[FORMAT.count-1], "Query window size in chars returned invalid response."); parts := split(input, ";"); @@ -289,17 +363,26 @@ get_cursor_position :: () -> row: int, column: int { flush_input(); write_string(Commands.QueryCursorPosition); - input := get_str(64); - // Expected message format: \e[<r>;<c>R + // Expected response format: \e[<r>;<c>R // where <r> is the number of rows and <c> of columns. - assert( - input[0] == #char "\e" && - input[1] == #char "[" && - input[input.count-1] == #char "R", - "Query cursor position returned invalid response."); + FORMAT :: "\e[<r>;<c>R"; + + // Discard head noise. + while input.count >= 2 && (input[0] != FORMAT[0] || input[1] != FORMAT[1]) { + advance(*input); + } + + // Discard tail noise. + while input.count >= 2 && input[input.count-1] != FORMAT[FORMAT.count-1] { + input.count -= 1; + } + assert(input[0] == FORMAT[0] && input[1] == FORMAT[1] && input[input.count-1] == FORMAT[FORMAT.count-1], + "Query cursor position returned invalid response."); + + advance(*input, 2); parts := split(input, ";"); row := parse_int(*parts[0]); diff --git a/TUI/unix.jai b/TUI/unix.jai index fb79f86..73a763a 100644 --- a/TUI/unix.jai +++ b/TUI/unix.jai @@ -25,6 +25,9 @@ // 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; + // 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 @@ -169,6 +172,11 @@ restore_resize_handler :: () { #scope_export +OS_flush_input :: inline () { + TCIFLUSH :: 0; // TODO Is this always zero in all systems? + tcflush(STDIN_FILENO, TCIFLUSH); +} + OS_prepare_terminal :: () { tcgetattr(STDIN_FILENO, *initial_tio_mode); // TODO Log on error. raw_tio_mode = initial_tio_mode; |
