| 1 | module scrollback_mod |
| 2 | use cell_mod |
| 3 | implicit none |
| 4 | private |
| 5 | |
| 6 | public :: scrollback_t |
| 7 | public :: scrollback_init, scrollback_destroy |
| 8 | public :: scrollback_push_line, scrollback_get_line |
| 9 | public :: scrollback_clear, scrollback_count |
| 10 | |
| 11 | integer, parameter :: DEFAULT_CAPACITY = 10000 |
| 12 | |
| 13 | type :: scrollback_t |
| 14 | type(cell_t), allocatable :: lines(:,:) ! (line_index, column) |
| 15 | integer :: capacity = 0 ! Maximum lines to store |
| 16 | integer :: cols = 0 ! Columns per line |
| 17 | integer :: count = 0 ! Current number of lines stored |
| 18 | integer :: head = 0 ! Next write position (0-based circular) |
| 19 | logical :: initialized = .false. |
| 20 | end type scrollback_t |
| 21 | |
| 22 | contains |
| 23 | |
| 24 | ! Initialize scrollback buffer |
| 25 | subroutine scrollback_init(sb, cols, capacity) |
| 26 | type(scrollback_t), intent(inout) :: sb |
| 27 | integer, intent(in) :: cols |
| 28 | integer, intent(in), optional :: capacity |
| 29 | |
| 30 | if (present(capacity)) then |
| 31 | sb%capacity = capacity |
| 32 | else |
| 33 | sb%capacity = DEFAULT_CAPACITY |
| 34 | end if |
| 35 | |
| 36 | sb%cols = cols |
| 37 | sb%count = 0 |
| 38 | sb%head = 0 |
| 39 | |
| 40 | allocate(sb%lines(sb%capacity, cols)) |
| 41 | |
| 42 | ! Initialize all cells to spaces |
| 43 | sb%lines%codepoint = 32 |
| 44 | sb%lines%fg = default_fg |
| 45 | sb%lines%bg = default_bg |
| 46 | sb%lines%attrs = 0 |
| 47 | |
| 48 | sb%initialized = .true. |
| 49 | end subroutine scrollback_init |
| 50 | |
| 51 | ! Destroy scrollback buffer |
| 52 | subroutine scrollback_destroy(sb) |
| 53 | type(scrollback_t), intent(inout) :: sb |
| 54 | |
| 55 | if (allocated(sb%lines)) deallocate(sb%lines) |
| 56 | sb%capacity = 0 |
| 57 | sb%cols = 0 |
| 58 | sb%count = 0 |
| 59 | sb%head = 0 |
| 60 | sb%initialized = .false. |
| 61 | end subroutine scrollback_destroy |
| 62 | |
| 63 | ! Push a line into scrollback (circular buffer) |
| 64 | subroutine scrollback_push_line(sb, line, cols) |
| 65 | type(scrollback_t), intent(inout) :: sb |
| 66 | type(cell_t), intent(in) :: line(:) |
| 67 | integer, intent(in) :: cols |
| 68 | integer :: col, copy_cols |
| 69 | |
| 70 | if (.not. sb%initialized) return |
| 71 | if (sb%capacity <= 0) return |
| 72 | |
| 73 | ! Calculate how many columns to copy |
| 74 | copy_cols = min(cols, sb%cols, size(line)) |
| 75 | |
| 76 | ! Write to current head position (1-based index) |
| 77 | do col = 1, copy_cols |
| 78 | sb%lines(sb%head + 1, col) = line(col) |
| 79 | end do |
| 80 | |
| 81 | ! Clear remaining columns if line is shorter |
| 82 | do col = copy_cols + 1, sb%cols |
| 83 | sb%lines(sb%head + 1, col) = cell_t(32, default_fg, default_bg, 0) |
| 84 | end do |
| 85 | |
| 86 | ! Advance head (circular) |
| 87 | sb%head = mod(sb%head + 1, sb%capacity) |
| 88 | |
| 89 | ! Update count (max out at capacity) |
| 90 | if (sb%count < sb%capacity) then |
| 91 | sb%count = sb%count + 1 |
| 92 | end if |
| 93 | end subroutine scrollback_push_line |
| 94 | |
| 95 | ! Get a line from scrollback |
| 96 | ! offset: 0 = most recent line, 1 = second most recent, etc. |
| 97 | subroutine scrollback_get_line(sb, offset, line, cols) |
| 98 | type(scrollback_t), intent(in) :: sb |
| 99 | integer, intent(in) :: offset |
| 100 | type(cell_t), intent(out) :: line(:) |
| 101 | integer, intent(in) :: cols |
| 102 | integer :: idx, col, copy_cols |
| 103 | |
| 104 | ! Initialize output to spaces |
| 105 | do col = 1, min(cols, size(line)) |
| 106 | line(col) = cell_t(32, default_fg, default_bg, 0) |
| 107 | end do |
| 108 | |
| 109 | if (.not. sb%initialized) return |
| 110 | if (offset < 0 .or. offset >= sb%count) return |
| 111 | |
| 112 | ! Calculate actual index in circular buffer |
| 113 | ! head points to next write position, so most recent is at head-1 |
| 114 | idx = mod(sb%head - 1 - offset + sb%capacity, sb%capacity) + 1 |
| 115 | |
| 116 | ! Copy line data |
| 117 | copy_cols = min(cols, sb%cols, size(line)) |
| 118 | do col = 1, copy_cols |
| 119 | line(col) = sb%lines(idx, col) |
| 120 | end do |
| 121 | end subroutine scrollback_get_line |
| 122 | |
| 123 | ! Clear scrollback buffer |
| 124 | subroutine scrollback_clear(sb) |
| 125 | type(scrollback_t), intent(inout) :: sb |
| 126 | |
| 127 | sb%count = 0 |
| 128 | sb%head = 0 |
| 129 | end subroutine scrollback_clear |
| 130 | |
| 131 | ! Get number of lines in scrollback |
| 132 | function scrollback_count(sb) result(n) |
| 133 | type(scrollback_t), intent(in) :: sb |
| 134 | integer :: n |
| 135 | |
| 136 | n = sb%count |
| 137 | end function scrollback_count |
| 138 | |
| 139 | end module scrollback_mod |
| 140 |