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 | private | 4 | private |
| 5 | 5 | ||
| 6 | public :: enable_raw_mode, disable_raw_mode, input_available, read_char_timeout | 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 | ! C function interfaces | 9 | ! C function interfaces |
| 10 | interface | 10 | interface |
@@ -37,6 +37,11 @@ module raw_mode_module | |||
| 37 | import :: c_int | 37 | import :: c_int |
| 38 | integer(c_int) :: c_read_char_escape | 38 | integer(c_int) :: c_read_char_escape |
| 39 | end function c_read_char_escape | 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 | end interface | 45 | end interface |
| 41 | 46 | ||
| 42 | contains | 47 | contains |
@@ -90,4 +95,14 @@ contains | |||
| 90 | ch = result | 95 | ch = result |
| 91 | end function read_char_escape | 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 | end module raw_mode_module | 108 | end module raw_mode_module |
src/terminal/renderer_module.f90modified@@ -270,6 +270,8 @@ contains | |||
| 270 | call terminal_write('~' // repeat(' ', content_width - 1)) | 270 | call terminal_write('~' // repeat(' ', content_width - 1)) |
| 271 | end if | 271 | end if |
| 272 | end if | 272 | end if |
| 273 | + ! Clear to end of line to prevent stale content when scrolling | ||
| 274 | + call terminal_write(char(27) // '[K') | ||
| 273 | end do | 275 | end do |
| 274 | 276 | ||
| 275 | ! Render status bar | 277 | ! Render status bar |
@@ -1114,6 +1116,8 @@ contains | |||
| 1114 | padding = '~' // repeat(' ', adjusted_width - 1) | 1116 | padding = '~' // repeat(' ', adjusted_width - 1) |
| 1115 | call terminal_write(padding) | 1117 | call terminal_write(padding) |
| 1116 | end if | 1118 | end if |
| 1119 | + ! Clear to end of line to prevent stale content when scrolling | ||
| 1120 | + call terminal_write(char(27) // '[K') | ||
| 1117 | end do | 1121 | end do |
| 1118 | end subroutine render_editor_pane | 1122 | end subroutine render_editor_pane |
| 1119 | 1123 | ||
src/terminal/terminal_io_module.f90modified@@ -6,7 +6,8 @@ module terminal_io_module | |||
| 6 | raw_input_available => input_available, & | 6 | raw_input_available => input_available, & |
| 7 | raw_read_char_timeout => read_char_timeout, & | 7 | raw_read_char_timeout => read_char_timeout, & |
| 8 | raw_read_char_escape => read_char_escape, & | 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 | implicit none | 11 | implicit none |
| 11 | private | 12 | private |
| 12 | 13 | ||
@@ -65,28 +66,8 @@ contains | |||
| 65 | 66 | ||
| 66 | subroutine terminal_get_size(rows, cols) | 67 | subroutine terminal_get_size(rows, cols) |
| 67 | integer, intent(out) :: rows, cols | 68 | integer, intent(out) :: rows, cols |
| 68 | - character(len=32) :: response | 69 | + ! Use ioctl-based method from C (no escape sequences) |
| 69 | - integer :: ios, r, c | 70 | + call raw_get_terminal_size(rows, cols) |
| 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 | ||
| 90 | end subroutine terminal_get_size | 71 | end subroutine terminal_get_size |
| 91 | 72 | ||
| 92 | subroutine terminal_enable_raw_mode() | 73 | subroutine terminal_enable_raw_mode() |
@@ -139,30 +120,6 @@ contains | |||
| 139 | flush(output_unit) | 120 | flush(output_unit) |
| 140 | end subroutine terminal_write | 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 | subroutine terminal_enable_mouse() | 123 | subroutine terminal_enable_mouse() |
| 167 | ! Enable mouse tracking modes: | 124 | ! Enable mouse tracking modes: |
| 168 | ! 1000 - Enable normal mouse tracking | 125 | ! 1000 - Enable normal mouse tracking |