aboutsummaryrefslogtreecommitdiff
path: root/TUI/module.jai
diff options
context:
space:
mode:
Diffstat (limited to 'TUI/module.jai')
-rw-r--r--TUI/module.jai129
1 files changed, 94 insertions, 35 deletions
diff --git a/TUI/module.jai b/TUI/module.jai
index 21f1dd7..de56ee8 100644
--- a/TUI/module.jai
+++ b/TUI/module.jai
@@ -559,33 +559,55 @@ get_key :: (timeout_milliseconds: s32 = -1) -> Key {
return xx Keys.None;
}
-// TODO Maybe rename!? Or replace with read_string... or read_input... or something else?!
-get_string :: (count_limit: int = -1) -> string {
+// TODO Review me!
+read_input :: (count_limit: int = -1, terminators: .. u8) -> string {
assert_is_initialized();
-
+
+ assert(count_limit >= 0 || terminators.count > 0, "Infinite loop detected, aborting."); // TODO Maybe just return!?
+ // if count_limit < 0 && terminators.count == 0 {
+ // TODO Write error to log.
+ // return "";
+ // }
+
if count_limit < 0 {
builder: String_Builder;
init_string_builder(*builder);
- while(1) {
+ while read_loop := true {
buffer := get_current_buffer(*builder);
buffer_data := get_buffer_data(buffer);
- buffer.count = OS_read_input(buffer_data, buffer.allocated); // TODO Does not check for read errors.
- if buffer.count < buffer.allocated break;
- expand(*builder);
+ buffer.count += OS_read_input(buffer_data + buffer.count, buffer.allocated - buffer.count);
+ for 0..buffer.count-1 {
+ for t: terminators {
+ if buffer_data[it] == t then break read_loop;
+ }
+ }
+ if buffer.count == buffer.allocated then expand(*builder);
+ OS_wait_for_input();
}
return builder_to_string(*builder);
}
else {
buffer := alloc_string(count_limit);
- buffer.count = OS_read_input(buffer.data, count_limit);
+ buffer.count = 0;
+
+ while read_loop := true {
+ buffer.count += OS_read_input(buffer.data + buffer.count, count_limit - buffer.count);
+ if buffer.count == count_limit then break;
+ for 0..buffer.count-1 {
+ for t: terminators {
+ if buffer[it] == t then break read_loop;
+ }
+ }
+ OS_wait_for_input();
+ }
+
return buffer;
}
}
// TODO UNTESTED
-// TODO Is count_limit the number of bytes of UTF8 symbols?
-user_line_input :: (count_limit: int, is_visible: bool = true) -> string, Key {
+read_input_line :: (count_limit: int, is_visible: bool = true) -> string, Key {
assert(count_limit >= 0, "Invalid value on count_limit parameter.");
str := alloc_string(count_limit);
@@ -689,6 +711,8 @@ stop :: () {
flush_input :: () {
OS_flush_input();
+ input_string.data = input_buffer.data;
+ input_string.count = 0;
}
// TODO move style related procedures here!
@@ -753,33 +777,46 @@ get_terminal_size :: () -> rows: int, columns: int {
auto_release_temp();
- rows, columns: int = ---;
-
flush_input();
write_string(Commands.QueryWindowSizeInChars);
- input := get_string(64,, temporary_allocator);
-
- // Expected response format: \e[8;<r>;<c>t
- // where <r> is the number of rows and <c> of columns.
- 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);
- }
+ rows, columns: int = ---;
+ if OS_wait_for_input(0) {
- // Discard tail noise.
- while input.count >= 3 && input[input.count-1] != FORMAT[FORMAT.count-1] {
- input.count -= 1;
- }
-
- assert(input.count >= 3 &&
- 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.");
+ // Expected response format: \e[8;<r>;<c>t
+ // where <r> is the number of rows and <c> of columns.
+ FORMAT :: "\e[8;<r>;<c>t";
+ input := read_input(64, #char "t",, temporary_allocator);
- parts := split(input, ";",, temporary_allocator);
- rows = parse_int(*parts[1]);
- columns = parse_int(*parts[2]);
+ // 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 >= 3 && input[input.count-1] != FORMAT[FORMAT.count-1] {
+ input.count -= 1;
+ }
+
+ assert(input.count >= 3 &&
+ 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, ";",, temporary_allocator);
+ rows = parse_int(*parts[1]);
+ columns = parse_int(*parts[2]);
+ }
+ // Some systems don't allow to query the terminal size directly.
+ // In such cases, measure it indirectly by the maximum possible cursor position.
+ else {
+ #import "Math"; // TODO Maybe use S16_MAX values directly.
+ flush_input();
+ cursor_row, cursor_column := get_cursor_position();
+ defer set_cursor_position(cursor_row, cursor_column);
+
+ set_cursor_position(S16_MAX, S16_MAX);
+ rows, columns = get_cursor_position();
+ }
return rows, columns;
}
@@ -798,12 +835,12 @@ get_cursor_position :: () -> row: int, column: int {
flush_input();
write_string(Commands.QueryCursorPosition);
- input := get_string(64,, temporary_allocator);
// Expected response format: \e[<r>;<c>R
// where <r> is the number of rows and <c> of columns.
FORMAT :: "\e[<r>;<c>R";
-
+ input := read_input(64, #char "R");
+
// Discard head noise.
while input.count >= 2 && (input[0] != FORMAT[0] || input[1] != FORMAT[1]) {
advance(*input);
@@ -818,7 +855,6 @@ get_cursor_position :: () -> row: int, column: int {
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, ";",, temporary_allocator);
row := parse_int(*parts[0]);
@@ -832,6 +868,29 @@ set_terminal_title :: (title: string) {
}
+test :: () {
+
+ // A) Testing stuff
+#if true {
+ start();
+ flush_input();
+ set_cursor_position(S16_MAX, S16_MAX);
+ r_a, c_a := get_cursor_position();
+ stop();
+ print("\n\rA) size: %, %\n", r_a, c_a);
+}
+
+ // B) Built way
+#if true {
+ start();
+ r_b, c_b := get_terminal_size();
+ stop();
+ print("\n\rB) size: %, %\n", r_b, c_b);
+}
+
+}
+
+
#if OS == .WINDOWS {
// Prototyping zone... keep clear!
}