fix terminal escape sequence leak and scroll artifacts
- SHA
377c7759ae36d06a737c8f38c1a4a26b7cc56ace- Parents
-
2057ce4 - Tree
8a95c86
377c775
377c7759ae36d06a737c8f38c1a4a26b7cc56ace2057ce4
8a95c86| Status | File | + | - |
|---|---|---|---|
| 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 | ||
| 4 | 4 | private |
| 5 | 5 | |
| 6 | 6 | 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 | |
| 8 | 8 | |
| 9 | 9 | ! C function interfaces |
| 10 | 10 | interface |
@@ -37,6 +37,11 @@ module raw_mode_module | ||
| 37 | 37 | import :: c_int |
| 38 | 38 | integer(c_int) :: c_read_char_escape |
| 39 | 39 | 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 | |
| 40 | 45 | end interface |
| 41 | 46 | |
| 42 | 47 | contains |
@@ -90,4 +95,14 @@ contains | ||
| 90 | 95 | ch = result |
| 91 | 96 | end function read_char_escape |
| 92 | 97 | |
| 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 | + | |
| 93 | 108 | end module raw_mode_module |
src/terminal/renderer_module.f90modified@@ -270,6 +270,8 @@ contains | ||
| 270 | 270 | call terminal_write('~' // repeat(' ', content_width - 1)) |
| 271 | 271 | end if |
| 272 | 272 | end if |
| 273 | + ! Clear to end of line to prevent stale content when scrolling | |
| 274 | + call terminal_write(char(27) // '[K') | |
| 273 | 275 | end do |
| 274 | 276 | |
| 275 | 277 | ! Render status bar |
@@ -1114,6 +1116,8 @@ contains | ||
| 1114 | 1116 | padding = '~' // repeat(' ', adjusted_width - 1) |
| 1115 | 1117 | call terminal_write(padding) |
| 1116 | 1118 | end if |
| 1119 | + ! Clear to end of line to prevent stale content when scrolling | |
| 1120 | + call terminal_write(char(27) // '[K') | |
| 1117 | 1121 | end do |
| 1118 | 1122 | end subroutine render_editor_pane |
| 1119 | 1123 | |
src/terminal/terminal_io_module.f90modified@@ -6,7 +6,8 @@ module terminal_io_module | ||
| 6 | 6 | raw_input_available => input_available, & |
| 7 | 7 | raw_read_char_timeout => read_char_timeout, & |
| 8 | 8 | 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 | |
| 10 | 11 | implicit none |
| 11 | 12 | private |
| 12 | 13 | |
@@ -65,28 +66,8 @@ contains | ||
| 65 | 66 | |
| 66 | 67 | subroutine terminal_get_size(rows, cols) |
| 67 | 68 | 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) | |
| 90 | 71 | end subroutine terminal_get_size |
| 91 | 72 | |
| 92 | 73 | subroutine terminal_enable_raw_mode() |
@@ -139,30 +120,6 @@ contains | ||
| 139 | 120 | flush(output_unit) |
| 140 | 121 | end subroutine terminal_write |
| 141 | 122 | |
| 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 | - | |
| 166 | 123 | subroutine terminal_enable_mouse() |
| 167 | 124 | ! Enable mouse tracking modes: |
| 168 | 125 | ! 1000 - Enable normal mouse tracking |