aboutsummaryrefslogtreecommitdiff
path: root/modules/TUI/unix.jai
diff options
context:
space:
mode:
authordam <dam@gudinoff>2024-04-30 00:05:26 +0100
committerdam <dam@gudinoff>2024-04-30 00:05:26 +0100
commitd7c2c312fe2ac08cadc3534a0a35357ef50cca20 (patch)
treecfaa0289011dfe5e33e55e78c5fffa4e4ea0663e /modules/TUI/unix.jai
parent1f2afe4db186342bda551e96bcd1d9b029d7c5ba (diff)
downloadtask-time-tracker-d7c2c312fe2ac08cadc3534a0a35357ef50cca20.tar.zst
task-time-tracker-d7c2c312fe2ac08cadc3534a0a35357ef50cca20.zip
WIP : Add logger and cleanup TUI module.
Diffstat (limited to 'modules/TUI/unix.jai')
-rw-r--r--modules/TUI/unix.jai297
1 files changed, 175 insertions, 122 deletions
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 {