fortrangoingonforty/facsimile / 377c775

Browse files

fix terminal escape sequence leak and scroll artifacts

Authored by espadonne
SHA
377c7759ae36d06a737c8f38c1a4a26b7cc56ace
Parents
2057ce4
Tree
8a95c86

3 changed files

StatusFile+-
M src/terminal/raw_mode_module.f90 16 1
M src/terminal/renderer_module.f90 4 0
M src/terminal/terminal_io_module.f90 4 47
src/terminal/raw_mode_module.f90modified
@@ -4,7 +4,7 @@ module raw_mode_module
44
     private
55
 
66
     public :: enable_raw_mode, disable_raw_mode, input_available, read_char_timeout
7
-    public :: read_char_escape, input_available_count
7
+    public :: read_char_escape, input_available_count, get_terminal_size
88
 
99
     ! C function interfaces
1010
     interface
@@ -37,6 +37,11 @@ module raw_mode_module
3737
             import :: c_int
3838
             integer(c_int) :: c_read_char_escape
3939
         end function c_read_char_escape
40
+
41
+        subroutine c_get_terminal_size(rows, cols) bind(C, name="get_terminal_size")
42
+            import :: c_int
43
+            integer(c_int), intent(out) :: rows, cols
44
+        end subroutine c_get_terminal_size
4045
     end interface
4146
 
4247
 contains
@@ -90,4 +95,14 @@ contains
9095
         ch = result
9196
     end function read_char_escape
9297
 
98
+    ! Get terminal size using ioctl (no escape sequences)
99
+    subroutine get_terminal_size(rows, cols)
100
+        integer, intent(out) :: rows, cols
101
+        integer(c_int) :: c_rows, c_cols
102
+
103
+        call c_get_terminal_size(c_rows, c_cols)
104
+        rows = c_rows
105
+        cols = c_cols
106
+    end subroutine get_terminal_size
107
+
93108
 end module raw_mode_module
src/terminal/renderer_module.f90modified
@@ -270,6 +270,8 @@ contains
270270
                         call terminal_write('~' // repeat(' ', content_width - 1))
271271
                     end if
272272
                 end if
273
+                ! Clear to end of line to prevent stale content when scrolling
274
+                call terminal_write(char(27) // '[K')
273275
             end do
274276
 
275277
         ! Render status bar
@@ -1114,6 +1116,8 @@ contains
11141116
                 padding = '~' // repeat(' ', adjusted_width - 1)
11151117
                 call terminal_write(padding)
11161118
             end if
1119
+            ! Clear to end of line to prevent stale content when scrolling
1120
+            call terminal_write(char(27) // '[K')
11171121
         end do
11181122
     end subroutine render_editor_pane
11191123
 
src/terminal/terminal_io_module.f90modified
@@ -6,7 +6,8 @@ module terminal_io_module
66
                                raw_input_available => input_available, &
77
                                raw_read_char_timeout => read_char_timeout, &
88
                                raw_read_char_escape => read_char_escape, &
9
-                               raw_input_available_count => input_available_count
9
+                               raw_input_available_count => input_available_count, &
10
+                               raw_get_terminal_size => get_terminal_size
1011
     implicit none
1112
     private
1213
 
@@ -65,28 +66,8 @@ contains
6566
 
6667
     subroutine terminal_get_size(rows, cols)
6768
         integer, intent(out) :: rows, cols
68
-        character(len=32) :: response
69
-        integer :: ios, r, c
70
-
71
-        ! Request cursor position after moving to bottom-right
72
-        write(output_unit, '(a)', advance='no') CSI // '999;999H'
73
-        write(output_unit, '(a)', advance='no') CSI // '6n'
74
-        flush(output_unit)
75
-
76
-        ! Read response (format: ESC[row;colR)
77
-        read(input_unit, '(a)', iostat=ios) response
78
-
79
-        ! Parse response
80
-        if (ios == 0 .and. response(1:2) == ESC // '[') then
81
-            ! Parse the response manually to handle variable width
82
-            call parse_cursor_response(response(3:), r, c)
83
-            rows = r
84
-            cols = c
85
-        else
86
-            ! Fallback to default
87
-            rows = 24
88
-            cols = 80
89
-        end if
69
+        ! Use ioctl-based method from C (no escape sequences)
70
+        call raw_get_terminal_size(rows, cols)
9071
     end subroutine terminal_get_size
9172
 
9273
     subroutine terminal_enable_raw_mode()
@@ -139,30 +120,6 @@ contains
139120
         flush(output_unit)
140121
     end subroutine terminal_write
141122
 
142
-    subroutine parse_cursor_response(response, row, col)
143
-        character(len=*), intent(in) :: response
144
-        integer, intent(out) :: row, col
145
-        integer :: semicolon_pos, r_pos, ios
146
-
147
-        row = 24
148
-        col = 80
149
-
150
-        ! Find semicolon position
151
-        semicolon_pos = index(response, ';')
152
-        if (semicolon_pos == 0) return
153
-
154
-        ! Find 'R' position
155
-        r_pos = index(response, 'R')
156
-        if (r_pos == 0) return
157
-
158
-        ! Parse row and column
159
-        read(response(1:semicolon_pos-1), '(i10)', iostat=ios) row
160
-        if (ios /= 0) return
161
-
162
-        read(response(semicolon_pos+1:r_pos-1), '(i10)', iostat=ios) col
163
-        if (ios /= 0) return
164
-    end subroutine parse_cursor_response
165
-
166123
     subroutine terminal_enable_mouse()
167124
         ! Enable mouse tracking modes:
168125
         ! 1000 - Enable normal mouse tracking