@@ -132,82 +132,60 @@ contains |
| 132 | | 132 | |
| 133 | subroutine read_key(key) | 133 | subroutine read_key(key) |
| 134 | character(len=1), intent(out) :: key | 134 | character(len=1), intent(out) :: key |
| 135 | - integer :: status, unit_num, iostat | 135 | + character(len=1) :: next_char |
| 136 | - character(len=256) :: cmd | 136 | + integer :: iostat, tty_unit |
| 137 | - | | |
| 138 | - ! Use bash with timeout to read a character (avoids blocking on ESC) | | |
| 139 | - ! This prevents the ESC key from requiring a second keypress | | |
| 140 | - write(cmd, '(A)') 'bash -c "read -t 0.1 -n 1 -s key < /dev/tty && echo -n $key" > ' // FUSS_TEMP // ' 2>/dev/null' | | |
| 141 | - call execute_command_line(trim(cmd), exitstat=status) | | |
| 142 | | 137 | |
| 143 | - if (status /= 0) then | 138 | + ! Open /dev/tty for reading |
| 144 | - ! Timeout or error - treat as quit | 139 | + open(newunit=tty_unit, file='/dev/tty', status='old', action='read', iostat=iostat) |
| 145 | - key = 'q' | | |
| 146 | - return | | |
| 147 | - end if | | |
| 148 | - | | |
| 149 | - ! Read the character from temp file | | |
| 150 | - open(newunit=unit_num, file=FUSS_TEMP, status='old', action='read', iostat=iostat) | | |
| 151 | if (iostat /= 0) then | 140 | if (iostat /= 0) then |
| 152 | - key = 'q' | 141 | + key = 'q' ! If we can't open tty, quit |
| 153 | return | 142 | return |
| 154 | end if | 143 | end if |
| 155 | - read(unit_num, '(A1)', iostat=iostat) key | | |
| 156 | - close(unit_num, status='delete') | | |
| 157 | | 144 | |
| 158 | - if (iostat /= 0) then | 145 | + ! Read one character |
| 159 | - key = 'q' | 146 | + read(tty_unit, '(A1)', iostat=iostat, advance='no') key |
| 160 | - return | | |
| 161 | - end if | | |
| 162 | | 147 | |
| 163 | ! Check for escape sequence (arrow keys or alt-key combos) | 148 | ! Check for escape sequence (arrow keys or alt-key combos) |
| 164 | if (key == achar(27)) then | 149 | if (key == achar(27)) then |
| 165 | - ! Detected ESC - try to read next char quickly with timeout | 150 | + ! Detected ESC - try to read next char (non-blocking check) |
| 166 | - write(cmd, '(A)') 'bash -c "read -t 0.05 -n 1 -s key < /dev/tty && echo -n $key" > ' // & | 151 | + read(tty_unit, '(A1)', iostat=iostat, advance='no') next_char |
| 167 | - FUSS_TEMP // ' 2>/dev/null' | | |
| 168 | - call execute_command_line(trim(cmd), exitstat=status) | | |
| 169 | | 152 | |
| 170 | - if (status == 0) then | 153 | + if (iostat == 0) then |
| 171 | - ! Got a following character - check what it is | 154 | + ! Got a character after ESC |
| 172 | - open(newunit=unit_num, file=FUSS_TEMP, status='old', action='read', iostat=iostat) | 155 | + if (next_char == '[') then |
| 173 | - if (iostat == 0) then | 156 | + ! Arrow key sequence: ESC[A/B/C/D - read final character |
| 174 | - read(unit_num, '(A1)', iostat=iostat) key | 157 | + read(tty_unit, '(A1)', iostat=iostat, advance='no') next_char |
| 175 | - close(unit_num, status='delete') | 158 | + if (iostat == 0) then |
| 176 | - | 159 | + ! Encode arrow keys as unique control codes to avoid conflict with uppercase letters |
| 177 | - ! Check for arrow keys (ESC [ A/B/C/D) or alt-keys (ESC letter) | 160 | + ! Up=28, Down=29, Right=30, Left=31 |
| 178 | - if (key == '[') then | 161 | + if (next_char == 'A') then |
| 179 | - ! Arrow key sequence - read final character | 162 | + key = achar(28) ! Up arrow |
| 180 | - write(cmd, '(A)') 'bash -c "read -t 0.05 -n 1 -s key < /dev/tty && echo -n $key" > ' // & | 163 | + else if (next_char == 'B') then |
| 181 | - FUSS_TEMP // ' 2>/dev/null' | 164 | + key = achar(29) ! Down arrow |
| 182 | - call execute_command_line(trim(cmd), exitstat=status) | 165 | + else if (next_char == 'C') then |
| 183 | - if (status == 0) then | 166 | + key = achar(30) ! Right arrow |
| 184 | - open(newunit=unit_num, file=FUSS_TEMP, status='old', action='read', iostat=iostat) | 167 | + else if (next_char == 'D') then |
| 185 | - if (iostat == 0) then | 168 | + key = achar(31) ! Left arrow |
| 186 | - read(unit_num, '(A1)', iostat=iostat) key | 169 | + else |
| 187 | - close(unit_num, status='delete') | 170 | + ! Unknown escape sequence, return ESC |
| 188 | - ! Encode arrow keys as unique control codes to avoid conflict with uppercase letters | 171 | + key = achar(27) |
| 189 | - ! Up=28, Down=29, Right=30, Left=31 | | |
| 190 | - if (key == 'A') then | | |
| 191 | - key = achar(28) ! Up arrow | | |
| 192 | - else if (key == 'B') then | | |
| 193 | - key = achar(29) ! Down arrow | | |
| 194 | - else if (key == 'C') then | | |
| 195 | - key = achar(30) ! Right arrow | | |
| 196 | - else if (key == 'D') then | | |
| 197 | - key = achar(31) ! Left arrow | | |
| 198 | - end if | | |
| 199 | - end if | | |
| 200 | end if | 172 | end if |
| 201 | - else if (key >= 'a' .and. key <= 'z') then | | |
| 202 | - ! Alt-letter: encode as control char (1-26 for alt-a through alt-z) | | |
| 203 | - key = achar(1 + ichar(key) - ichar('a')) | | |
| 204 | end if | 173 | end if |
| | 174 | + else if (next_char >= 'a' .and. next_char <= 'z') then |
| | 175 | + ! Alt-letter sequence: ESC followed by lowercase letter |
| | 176 | + ! Encode as ASCII control characters (1-26 for alt-a through alt-z) |
| | 177 | + key = achar(1 + ichar(next_char) - ichar('a')) |
| | 178 | + else |
| | 179 | + ! Unknown sequence after ESC, return ESC |
| | 180 | + key = achar(27) |
| 205 | end if | 181 | end if |
| 206 | else | 182 | else |
| 207 | - ! Just ESC alone (timeout, no following character) | 183 | + ! No character available after ESC - it's just ESC key alone |
| 208 | key = achar(27) | 184 | key = achar(27) |
| 209 | end if | 185 | end if |
| 210 | end if | 186 | end if |
| | 187 | + |
| | 188 | + close(tty_unit) |
| 211 | end subroutine read_key | 189 | end subroutine read_key |
| 212 | | 190 | |
| 213 | subroutine read_line(prompt, line) | 191 | subroutine read_line(prompt, line) |