@@ -1,6 +1,5 @@ |
| 1 | 1 | program fortty |
| 2 | 2 | use window_mod |
| 3 | | - use selection_mod, only: selection_contains |
| 4 | 3 | use gl_bindings |
| 5 | 4 | use renderer_mod |
| 6 | 5 | use font_mod, only: font_find_monospace, font_find_for_codepoint |
@@ -8,14 +7,14 @@ program fortty |
| 8 | 7 | use terminal_mod |
| 9 | 8 | use parser_mod |
| 10 | 9 | use screen_mod |
| 11 | | - use cell_mod, only: cell_t, set_palette_color, set_default_colors |
| 10 | + use cell_mod, only: set_palette_color, set_default_colors |
| 12 | 11 | use config_mod |
| 13 | | - use cursor_mod, only: CURSOR_BLOCK, CURSOR_UNDERLINE, CURSOR_BAR |
| 14 | 12 | use glfw_bindings, only: glfwGetTime |
| 15 | 13 | use tab_manager_mod |
| 16 | 14 | use tab_bar_mod |
| 17 | 15 | use pane_mod |
| 18 | 16 | use layout_mod, only: DIR_LEFT, DIR_RIGHT, DIR_UP, DIR_DOWN |
| 17 | + use render_state_mod, only: render_state_init, render_state_update_blink, do_render |
| 19 | 18 | implicit none |
| 20 | 19 | |
| 21 | 20 | type(window_t) :: win |
@@ -24,7 +23,6 @@ program fortty |
| 24 | 23 | type(pane_t), pointer :: active_pane |
| 25 | 24 | type(terminal_t), pointer :: term |
| 26 | 25 | type(pty_t), pointer :: active_pty |
| 27 | | - type(cell_t) :: cell |
| 28 | 26 | type(config_t) :: cfg |
| 29 | 27 | integer :: win_width, win_height |
| 30 | 28 | integer :: prev_width, prev_height |
@@ -33,15 +31,12 @@ program fortty |
| 33 | 31 | character(len=256) :: font_path, fallback_path |
| 34 | 32 | character(len=4096) :: pty_buffer |
| 35 | 33 | character(len=256) :: response_buf |
| 36 | | - integer :: nbytes, i, j, k, row, col, scroll_offset, sb_offset, screen_row |
| 34 | + integer :: nbytes, i, j, k |
| 37 | 35 | integer :: response_len, tab_action, pane_action, tab_bar_height |
| 38 | | - real :: x, y, r, g, b, bg_r, bg_g, bg_b |
| 39 | | - type(cell_t), allocatable :: sb_line(:) |
| 40 | 36 | integer :: cell_width, cell_height, ascender ! From font metrics |
| 41 | 37 | real(8) :: current_time, last_time, blink_timer |
| 42 | 38 | logical :: cursor_blink_visible, any_pty_alive |
| 43 | 39 | logical :: was_focused, is_focused |
| 44 | | - type(selection_t) :: sel |
| 45 | 40 | integer :: font_delta, new_font_size, base_font_size |
| 46 | 41 | character(len=256) :: font_path_saved, fallback_path_saved |
| 47 | 42 | |
@@ -229,6 +224,10 @@ program fortty |
| 229 | 224 | call window_set_pty(active_pty) |
| 230 | 225 | call window_set_terminal(term) |
| 231 | 226 | |
| 227 | + ! Initialize render state module with pointers to program state |
| 228 | + call render_state_init(win, ren, tab_mgr, cfg, cell_width, cell_height, ascender, & |
| 229 | + win_width, win_height) |
| 230 | + |
| 232 | 231 | ! Register render callback for live resize support on macOS |
| 233 | 232 | call window_set_render_callback(do_render) |
| 234 | 233 | |
@@ -251,6 +250,7 @@ program fortty |
| 251 | 250 | cursor_blink_visible = .not. cursor_blink_visible |
| 252 | 251 | blink_timer = 0.0d0 |
| 253 | 252 | end if |
| 253 | + call render_state_update_blink(cursor_blink_visible) |
| 254 | 254 | |
| 255 | 255 | ! Poll events first - this ensures resize callbacks fire BEFORE we render |
| 256 | 256 | ! so viewport and projection updates happen in the same frame |
@@ -432,198 +432,12 @@ program fortty |
| 432 | 432 | end do |
| 433 | 433 | |
| 434 | 434 | ! Cleanup |
| 435 | | - if (allocated(sb_line)) deallocate(sb_line) |
| 436 | 435 | call tab_manager_destroy(tab_mgr) |
| 437 | 436 | call renderer_destroy(ren) |
| 438 | 437 | call window_destroy(win) |
| 439 | 438 | |
| 440 | 439 | contains |
| 441 | 440 | |
| 442 | | - ! Internal render subroutine - can be called from main loop or resize callback |
| 443 | | - ! Has access to all program variables through host association |
| 444 | | - subroutine do_render() |
| 445 | | - integer :: render_width, render_height |
| 446 | | - integer :: pane_idx, tab_idx |
| 447 | | - integer :: scissor_x, scissor_y, scissor_w, scissor_h |
| 448 | | - real :: dim_factor, pane_x_offset, pane_y_offset |
| 449 | | - type(pane_t), pointer :: cur_pane |
| 450 | | - type(terminal_t), pointer :: pane_term |
| 451 | | - type(screen_t), pointer :: pane_scr |
| 452 | | - |
| 453 | | - ! Get current window size and update projection if needed |
| 454 | | - ! This is critical for live resize - we need to update projection |
| 455 | | - ! to match the new viewport that was set by the framebuffer callback |
| 456 | | - call window_get_size(win, render_width, render_height) |
| 457 | | - if (render_width /= prev_width .or. render_height /= prev_height) then |
| 458 | | - prev_width = render_width |
| 459 | | - prev_height = render_height |
| 460 | | - win_width = render_width |
| 461 | | - win_height = render_height |
| 462 | | - call renderer_set_projection(ren, win_width, win_height) |
| 463 | | - end if |
| 464 | | - |
| 465 | | - ! Clear screen with background color and opacity from config |
| 466 | | - bg_r = real(cfg%bg_color%r) / 255.0 |
| 467 | | - bg_g = real(cfg%bg_color%g) / 255.0 |
| 468 | | - bg_b = real(cfg%bg_color%b) / 255.0 |
| 469 | | - call glClearColor(bg_r, bg_g, bg_b, cfg%window_opacity) |
| 470 | | - call glClear(GL_COLOR_BUFFER_BIT) |
| 471 | | - |
| 472 | | - ! Render terminal buffer |
| 473 | | - call renderer_begin(ren) |
| 474 | | - |
| 475 | | - ! Render tab bar at top |
| 476 | | - call tab_bar_render(ren, tab_mgr, win_width, tab_mgr%bar_height, cell_width, ascender) |
| 477 | | - |
| 478 | | - ! Calculate effective tab bar height (hidden when only 1 tab) |
| 479 | | - if (tab_mgr%count > 1) then |
| 480 | | - tab_bar_height = tab_mgr%bar_height |
| 481 | | - else |
| 482 | | - tab_bar_height = 0 |
| 483 | | - end if |
| 484 | | - |
| 485 | | - ! Guard against no active tab |
| 486 | | - if (tab_mgr%active_index < 1 .or. tab_mgr%active_index > tab_mgr%count) then |
| 487 | | - call renderer_flush(ren) |
| 488 | | - call window_swap_buffers(win) |
| 489 | | - return |
| 490 | | - end if |
| 491 | | - |
| 492 | | - tab_idx = tab_mgr%active_index |
| 493 | | - |
| 494 | | - ! Get current selection for highlighting (only applies to active pane) |
| 495 | | - sel = window_get_selection() |
| 496 | | - |
| 497 | | - ! Render each pane in the active tab |
| 498 | | - do pane_idx = 1, tab_mgr%tabs(tab_idx)%pane_count |
| 499 | | - cur_pane => tab_mgr%tabs(tab_idx)%panes(pane_idx) |
| 500 | | - pane_term => cur_pane%term |
| 501 | | - pane_scr => terminal_active_screen(pane_term) |
| 502 | | - |
| 503 | | - ! Determine dimming factor (inactive panes are dimmed) |
| 504 | | - if (cur_pane%active) then |
| 505 | | - dim_factor = 1.0 |
| 506 | | - else |
| 507 | | - dim_factor = 0.6 |
| 508 | | - end if |
| 509 | | - |
| 510 | | - ! Calculate pane offset for rendering |
| 511 | | - pane_x_offset = real(cur_pane%x) |
| 512 | | - pane_y_offset = real(cur_pane%y) |
| 513 | | - |
| 514 | | - ! Enable scissor test for this pane's viewport |
| 515 | | - ! OpenGL uses bottom-left origin, so convert from top-left |
| 516 | | - scissor_x = cur_pane%x |
| 517 | | - scissor_y = win_height - cur_pane%y - cur_pane%height |
| 518 | | - scissor_w = cur_pane%width |
| 519 | | - scissor_h = cur_pane%height |
| 520 | | - call glEnable(GL_SCISSOR_TEST) |
| 521 | | - call glScissor(scissor_x, scissor_y, scissor_w, scissor_h) |
| 522 | | - |
| 523 | | - scroll_offset = terminal_get_scroll_offset(pane_term) |
| 524 | | - |
| 525 | | - ! Allocate/resize scrollback line buffer if needed |
| 526 | | - if (.not. allocated(sb_line)) then |
| 527 | | - allocate(sb_line(cur_pane%cols)) |
| 528 | | - else if (size(sb_line) /= cur_pane%cols) then |
| 529 | | - deallocate(sb_line) |
| 530 | | - allocate(sb_line(cur_pane%cols)) |
| 531 | | - end if |
| 532 | | - |
| 533 | | - do row = 1, pane_scr%rows |
| 534 | | - ! Cell top-left y coordinate relative to pane |
| 535 | | - y = real(row - 1) * cell_height + pane_y_offset |
| 536 | | - |
| 537 | | - ! Determine if this row shows scrollback or screen content |
| 538 | | - sb_offset = scroll_offset - row + 1 |
| 539 | | - |
| 540 | | - if (sb_offset > 0 .and. sb_offset <= terminal_get_scrollback_count(pane_term)) then |
| 541 | | - ! This row shows scrollback content |
| 542 | | - call terminal_get_scrollback_line(pane_term, sb_offset - 1, sb_line, cur_pane%cols) |
| 543 | | - do col = 1, min(cur_pane%cols, pane_scr%cols) |
| 544 | | - cell = sb_line(col) |
| 545 | | - |
| 546 | | - ! Skip continuation cells (2nd half of wide chars) |
| 547 | | - if (cell%is_continuation) cycle |
| 548 | | - |
| 549 | | - x = real(col - 1) * cell_width + pane_x_offset |
| 550 | | - |
| 551 | | - ! Draw selection background if selected (only for active pane) |
| 552 | | - if (cur_pane%active .and. selection_contains(sel, row, col)) then |
| 553 | | - call renderer_draw_rect(ren, x, y, real(cell_width), real(cell_height), & |
| 554 | | - 0.3, 0.3, 0.6, 1.0) |
| 555 | | - end if |
| 556 | | - |
| 557 | | - ! Only render cells with actual text content |
| 558 | | - if (cell%codepoint /= 32 .and. cell%codepoint /= 0) then |
| 559 | | - r = real(cell%fg%r) / 255.0 * dim_factor |
| 560 | | - g = real(cell%fg%g) / 255.0 * dim_factor |
| 561 | | - b = real(cell%fg%b) / 255.0 * dim_factor |
| 562 | | - call renderer_draw_char(ren, x, y + real(ascender), cell%codepoint, r, g, b, 1.0) |
| 563 | | - end if |
| 564 | | - end do |
| 565 | | - else |
| 566 | | - ! This row shows screen content |
| 567 | | - screen_row = row - scroll_offset |
| 568 | | - if (screen_row >= 1 .and. screen_row <= pane_scr%rows) then |
| 569 | | - do col = 1, pane_scr%cols |
| 570 | | - cell = screen_get_cell(pane_scr, screen_row, col) |
| 571 | | - |
| 572 | | - ! Skip continuation cells (2nd half of wide chars) |
| 573 | | - if (cell%is_continuation) cycle |
| 574 | | - |
| 575 | | - x = real(col - 1) * cell_width + pane_x_offset |
| 576 | | - |
| 577 | | - ! Draw selection background if selected (only for active pane) |
| 578 | | - if (cur_pane%active .and. selection_contains(sel, row, col)) then |
| 579 | | - call renderer_draw_rect(ren, x, y, real(cell_width), real(cell_height), & |
| 580 | | - 0.3, 0.3, 0.6, 1.0) |
| 581 | | - end if |
| 582 | | - |
| 583 | | - ! Only render cells with actual text content |
| 584 | | - if (cell%codepoint /= 32 .and. cell%codepoint /= 0) then |
| 585 | | - r = real(cell%fg%r) / 255.0 * dim_factor |
| 586 | | - g = real(cell%fg%g) / 255.0 * dim_factor |
| 587 | | - b = real(cell%fg%b) / 255.0 * dim_factor |
| 588 | | - call renderer_draw_char(ren, x, y + real(ascender), cell%codepoint, r, g, b, 1.0) |
| 589 | | - end if |
| 590 | | - end do |
| 591 | | - end if |
| 592 | | - end if |
| 593 | | - end do |
| 594 | | - |
| 595 | | - ! Draw cursor if visible (only for active pane and when not scrolled back) |
| 596 | | - if (cur_pane%active .and. pane_term%cursor%visible .and. scroll_offset == 0) then |
| 597 | | - ! Check blink state - only hide cursor if blink is enabled and in off phase |
| 598 | | - if (.not. pane_term%cursor%blink .or. cursor_blink_visible) then |
| 599 | | - x = real(pane_term%cursor%col - 1) * cell_width + pane_x_offset |
| 600 | | - y = real(pane_term%cursor%row - 1) * cell_height + pane_y_offset |
| 601 | | - |
| 602 | | - select case (pane_term%cursor%style) |
| 603 | | - case (CURSOR_BLOCK) |
| 604 | | - call renderer_draw_rect(ren, x, y, real(cell_width), real(cell_height), & |
| 605 | | - 0.7, 0.7, 0.7, 0.8) |
| 606 | | - case (CURSOR_UNDERLINE) |
| 607 | | - call renderer_draw_rect(ren, x, y + real(ascender), & |
| 608 | | - real(cell_width), 2.0, 0.7, 0.7, 0.7, 1.0) |
| 609 | | - case (CURSOR_BAR) |
| 610 | | - call renderer_draw_rect(ren, x, y, 2.0, real(cell_height), 0.7, 0.7, 0.7, 1.0) |
| 611 | | - case default |
| 612 | | - call renderer_draw_rect(ren, x, y, real(cell_width), real(cell_height), & |
| 613 | | - 0.7, 0.7, 0.7, 0.8) |
| 614 | | - end select |
| 615 | | - end if |
| 616 | | - end if |
| 617 | | - |
| 618 | | - call glDisable(GL_SCISSOR_TEST) |
| 619 | | - end do |
| 620 | | - |
| 621 | | - call renderer_flush(ren) |
| 622 | | - |
| 623 | | - ! Swap buffers |
| 624 | | - call window_swap_buffers(win) |
| 625 | | - end subroutine do_render |
| 626 | | - |
| 627 | 441 | ! Handle tab action signals from keyboard |
| 628 | 442 | subroutine handle_tab_action(action) |
| 629 | 443 | use window_mod, only: TAB_ACTION_NEW, TAB_ACTION_CLOSE, TAB_ACTION_NEXT, TAB_ACTION_PREV |