Fortran · 5320 bytes Raw Blame History
1 module terminal_io_module
2 use iso_c_binding
3 use iso_fortran_env, only: output_unit, input_unit
4 use raw_mode_module, only: raw_enable_raw_mode => enable_raw_mode, &
5 raw_disable_raw_mode => disable_raw_mode, &
6 raw_input_available => input_available, &
7 raw_read_char_timeout => read_char_timeout, &
8 raw_read_char_escape => read_char_escape, &
9 raw_input_available_count => input_available_count, &
10 raw_get_terminal_size => get_terminal_size
11 implicit none
12 private
13
14 public :: terminal_init, terminal_cleanup, terminal_clear_screen
15 public :: terminal_move_cursor, terminal_hide_cursor, terminal_show_cursor
16 public :: terminal_get_size, terminal_enable_raw_mode, terminal_disable_raw_mode
17 public :: terminal_write, terminal_enable_mouse, terminal_disable_mouse
18 public :: terminal_input_available, terminal_read_char
19 public :: terminal_read_char_escape, terminal_input_available_count
20
21 ! ANSI escape codes
22 character(len=*), parameter :: ESC = char(27)
23 character(len=*), parameter :: CSI = ESC // '['
24
25 contains
26
27 subroutine terminal_init()
28 call terminal_enable_raw_mode()
29 call terminal_enable_mouse()
30 call terminal_clear_screen()
31 call terminal_hide_cursor()
32 end subroutine terminal_init
33
34 subroutine terminal_cleanup()
35 call terminal_show_cursor()
36 call terminal_disable_mouse()
37 call terminal_clear_screen()
38 call terminal_move_cursor(1, 1)
39 call terminal_disable_raw_mode()
40 end subroutine terminal_cleanup
41
42 subroutine terminal_clear_screen()
43 write(output_unit, '(a)', advance='no') CSI // '2J'
44 write(output_unit, '(a)', advance='no') CSI // 'H'
45 flush(output_unit)
46 end subroutine terminal_clear_screen
47
48 subroutine terminal_move_cursor(row, col)
49 integer, intent(in) :: row, col
50 character(len=32) :: seq
51
52 write(seq, '(a,i0,a,i0,a)') CSI, row, ';', col, 'H'
53 write(output_unit, '(a)', advance='no') trim(seq)
54 flush(output_unit)
55 end subroutine terminal_move_cursor
56
57 subroutine terminal_hide_cursor()
58 write(output_unit, '(a)', advance='no') CSI // '?25l'
59 flush(output_unit)
60 end subroutine terminal_hide_cursor
61
62 subroutine terminal_show_cursor()
63 write(output_unit, '(a)', advance='no') CSI // '?25h'
64 flush(output_unit)
65 end subroutine terminal_show_cursor
66
67 subroutine terminal_get_size(rows, cols)
68 integer, intent(out) :: rows, cols
69 ! Use ioctl-based method from C (no escape sequences)
70 call raw_get_terminal_size(rows, cols)
71 end subroutine terminal_get_size
72
73 subroutine terminal_enable_raw_mode()
74 logical :: success
75
76 success = raw_enable_raw_mode()
77 if (.not. success) then
78 ! Fallback - just disable echo
79 write(output_unit, '(a)', advance='no') ESC // '[12l'
80 flush(output_unit)
81 end if
82 end subroutine terminal_enable_raw_mode
83
84 subroutine terminal_disable_raw_mode()
85 logical :: success
86
87 success = raw_disable_raw_mode()
88 if (.not. success) then
89 ! Fallback - re-enable echo
90 write(output_unit, '(a)', advance='no') ESC // '[12h'
91 flush(output_unit)
92 end if
93 end subroutine terminal_disable_raw_mode
94
95 function terminal_input_available() result(available)
96 logical :: available
97 available = raw_input_available()
98 end function terminal_input_available
99
100 function terminal_read_char() result(ch)
101 integer :: ch
102 ch = raw_read_char_timeout()
103 end function terminal_read_char
104
105 ! Fast read for escape sequences (5ms timeout)
106 function terminal_read_char_escape() result(ch)
107 integer :: ch
108 ch = raw_read_char_escape()
109 end function terminal_read_char_escape
110
111 ! Get count of available input bytes
112 function terminal_input_available_count() result(count)
113 integer :: count
114 count = raw_input_available_count()
115 end function terminal_input_available_count
116
117 subroutine terminal_write(text)
118 character(len=*), intent(in) :: text
119 write(output_unit, '(a)', advance='no') text
120 flush(output_unit)
121 end subroutine terminal_write
122
123 subroutine terminal_enable_mouse()
124 ! Enable mouse tracking modes:
125 ! 1000 - Enable normal mouse tracking
126 ! 1002 - Enable button-motion tracking (for drag)
127 ! 1006 - Enable SGR extended mode (for large terminals)
128 write(output_unit, '(a)', advance='no') CSI // '?1000h'
129 write(output_unit, '(a)', advance='no') CSI // '?1002h'
130 write(output_unit, '(a)', advance='no') CSI // '?1006h'
131 flush(output_unit)
132 end subroutine terminal_enable_mouse
133
134 subroutine terminal_disable_mouse()
135 ! Disable mouse tracking modes
136 write(output_unit, '(a)', advance='no') CSI // '?1006l'
137 write(output_unit, '(a)', advance='no') CSI // '?1002l'
138 write(output_unit, '(a)', advance='no') CSI // '?1000l'
139 flush(output_unit)
140 end subroutine terminal_disable_mouse
141
142 end module terminal_io_module