| 1 | module screen_mod |
| 2 | use cell_mod |
| 3 | implicit none |
| 4 | private |
| 5 | |
| 6 | public :: screen_t |
| 7 | public :: screen_init, screen_destroy, screen_resize |
| 8 | public :: screen_clear, screen_clear_line, screen_clear_region |
| 9 | public :: screen_set_cell, screen_get_cell |
| 10 | public :: screen_mark_dirty, screen_mark_all_dirty |
| 11 | public :: screen_clear_dirty, screen_is_dirty |
| 12 | |
| 13 | type :: screen_t |
| 14 | integer :: rows = 0 |
| 15 | integer :: cols = 0 |
| 16 | type(cell_t), allocatable :: cells(:,:) ! (row, col) |
| 17 | logical, allocatable :: dirty(:) ! Per-line dirty flags |
| 18 | end type screen_t |
| 19 | |
| 20 | contains |
| 21 | |
| 22 | ! Initialize screen buffer |
| 23 | subroutine screen_init(scr, rows, cols) |
| 24 | type(screen_t), intent(inout) :: scr |
| 25 | integer, intent(in) :: rows, cols |
| 26 | integer :: r, c |
| 27 | |
| 28 | scr%rows = rows |
| 29 | scr%cols = cols |
| 30 | |
| 31 | allocate(scr%cells(rows, cols)) |
| 32 | allocate(scr%dirty(rows)) |
| 33 | |
| 34 | ! Initialize all cells to spaces with default colors |
| 35 | do r = 1, rows |
| 36 | do c = 1, cols |
| 37 | scr%cells(r, c) = cell_t(32, default_fg, default_bg, 0) |
| 38 | end do |
| 39 | end do |
| 40 | |
| 41 | ! Mark all lines as dirty initially |
| 42 | scr%dirty = .true. |
| 43 | end subroutine screen_init |
| 44 | |
| 45 | ! Destroy screen buffer |
| 46 | subroutine screen_destroy(scr) |
| 47 | type(screen_t), intent(inout) :: scr |
| 48 | |
| 49 | if (allocated(scr%cells)) deallocate(scr%cells) |
| 50 | if (allocated(scr%dirty)) deallocate(scr%dirty) |
| 51 | scr%rows = 0 |
| 52 | scr%cols = 0 |
| 53 | end subroutine screen_destroy |
| 54 | |
| 55 | ! Resize screen buffer (preserves content where possible) |
| 56 | subroutine screen_resize(scr, new_rows, new_cols) |
| 57 | type(screen_t), intent(inout) :: scr |
| 58 | integer, intent(in) :: new_rows, new_cols |
| 59 | type(cell_t), allocatable :: new_cells(:,:) |
| 60 | logical, allocatable :: new_dirty(:) |
| 61 | integer :: min_rows, min_cols, r, c |
| 62 | |
| 63 | if (new_rows == scr%rows .and. new_cols == scr%cols) return |
| 64 | |
| 65 | allocate(new_cells(new_rows, new_cols)) |
| 66 | allocate(new_dirty(new_rows)) |
| 67 | |
| 68 | ! Initialize new buffer with spaces |
| 69 | do r = 1, new_rows |
| 70 | do c = 1, new_cols |
| 71 | new_cells(r, c) = cell_t(32, default_fg, default_bg, 0) |
| 72 | end do |
| 73 | end do |
| 74 | |
| 75 | ! Copy existing content |
| 76 | min_rows = min(scr%rows, new_rows) |
| 77 | min_cols = min(scr%cols, new_cols) |
| 78 | |
| 79 | do r = 1, min_rows |
| 80 | do c = 1, min_cols |
| 81 | new_cells(r, c) = scr%cells(r, c) |
| 82 | end do |
| 83 | end do |
| 84 | |
| 85 | ! Replace buffers |
| 86 | call move_alloc(new_cells, scr%cells) |
| 87 | call move_alloc(new_dirty, scr%dirty) |
| 88 | |
| 89 | scr%rows = new_rows |
| 90 | scr%cols = new_cols |
| 91 | |
| 92 | ! Mark all lines dirty after resize |
| 93 | scr%dirty = .true. |
| 94 | end subroutine screen_resize |
| 95 | |
| 96 | ! Clear entire screen |
| 97 | subroutine screen_clear(scr) |
| 98 | type(screen_t), intent(inout) :: scr |
| 99 | integer :: r, c |
| 100 | |
| 101 | do r = 1, scr%rows |
| 102 | do c = 1, scr%cols |
| 103 | scr%cells(r, c) = cell_t(32, default_fg, default_bg, 0) |
| 104 | end do |
| 105 | scr%dirty(r) = .true. |
| 106 | end do |
| 107 | end subroutine screen_clear |
| 108 | |
| 109 | ! Clear a single line |
| 110 | subroutine screen_clear_line(scr, row) |
| 111 | type(screen_t), intent(inout) :: scr |
| 112 | integer, intent(in) :: row |
| 113 | integer :: c |
| 114 | |
| 115 | if (row < 1 .or. row > scr%rows) return |
| 116 | |
| 117 | do c = 1, scr%cols |
| 118 | scr%cells(row, c) = cell_t(32, default_fg, default_bg, 0) |
| 119 | end do |
| 120 | scr%dirty(row) = .true. |
| 121 | end subroutine screen_clear_line |
| 122 | |
| 123 | ! Clear a region of the screen |
| 124 | subroutine screen_clear_region(scr, r1, c1, r2, c2) |
| 125 | type(screen_t), intent(inout) :: scr |
| 126 | integer, intent(in) :: r1, c1, r2, c2 |
| 127 | integer :: r, c, row_start, row_end, col_start, col_end |
| 128 | |
| 129 | row_start = max(1, min(r1, r2)) |
| 130 | row_end = min(scr%rows, max(r1, r2)) |
| 131 | col_start = max(1, min(c1, c2)) |
| 132 | col_end = min(scr%cols, max(c1, c2)) |
| 133 | |
| 134 | do r = row_start, row_end |
| 135 | do c = col_start, col_end |
| 136 | scr%cells(r, c) = cell_t(32, default_fg, default_bg, 0) |
| 137 | end do |
| 138 | scr%dirty(r) = .true. |
| 139 | end do |
| 140 | end subroutine screen_clear_region |
| 141 | |
| 142 | ! Set a cell at given position |
| 143 | subroutine screen_set_cell(scr, row, col, cell) |
| 144 | type(screen_t), intent(inout) :: scr |
| 145 | integer, intent(in) :: row, col |
| 146 | type(cell_t), intent(in) :: cell |
| 147 | |
| 148 | if (row < 1 .or. row > scr%rows) return |
| 149 | if (col < 1 .or. col > scr%cols) return |
| 150 | |
| 151 | scr%cells(row, col) = cell |
| 152 | scr%dirty(row) = .true. |
| 153 | end subroutine screen_set_cell |
| 154 | |
| 155 | ! Get a cell at given position |
| 156 | function screen_get_cell(scr, row, col) result(cell) |
| 157 | type(screen_t), intent(in) :: scr |
| 158 | integer, intent(in) :: row, col |
| 159 | type(cell_t) :: cell |
| 160 | |
| 161 | if (row < 1 .or. row > scr%rows .or. col < 1 .or. col > scr%cols) then |
| 162 | cell = cell_t(32, default_fg, default_bg, 0) |
| 163 | return |
| 164 | end if |
| 165 | |
| 166 | cell = scr%cells(row, col) |
| 167 | end function screen_get_cell |
| 168 | |
| 169 | ! Mark a line as dirty |
| 170 | subroutine screen_mark_dirty(scr, row) |
| 171 | type(screen_t), intent(inout) :: scr |
| 172 | integer, intent(in) :: row |
| 173 | |
| 174 | if (row >= 1 .and. row <= scr%rows) then |
| 175 | scr%dirty(row) = .true. |
| 176 | end if |
| 177 | end subroutine screen_mark_dirty |
| 178 | |
| 179 | ! Mark all lines as dirty |
| 180 | subroutine screen_mark_all_dirty(scr) |
| 181 | type(screen_t), intent(inout) :: scr |
| 182 | |
| 183 | scr%dirty = .true. |
| 184 | end subroutine screen_mark_all_dirty |
| 185 | |
| 186 | ! Clear dirty flag for a line |
| 187 | subroutine screen_clear_dirty(scr, row) |
| 188 | type(screen_t), intent(inout) :: scr |
| 189 | integer, intent(in) :: row |
| 190 | |
| 191 | if (row >= 1 .and. row <= scr%rows) then |
| 192 | scr%dirty(row) = .false. |
| 193 | end if |
| 194 | end subroutine screen_clear_dirty |
| 195 | |
| 196 | ! Check if a line is dirty |
| 197 | function screen_is_dirty(scr, row) result(is_dirty) |
| 198 | type(screen_t), intent(in) :: scr |
| 199 | integer, intent(in) :: row |
| 200 | logical :: is_dirty |
| 201 | |
| 202 | if (row < 1 .or. row > scr%rows) then |
| 203 | is_dirty = .false. |
| 204 | return |
| 205 | end if |
| 206 | |
| 207 | is_dirty = scr%dirty(row) |
| 208 | end function screen_is_dirty |
| 209 | |
| 210 | end module screen_mod |
| 211 |