fortrangoingonforty/fortty / ef0d098

Browse files

Refactor render callback to eliminate executable stack warning

Move do_render from internal procedure to module procedure in new
render_state_mod. This avoids gfortran trampolines that required
executable stack for callbacks.
Authored by mfwolffe <wolffemf@dukes.jmu.edu>
SHA
ef0d098817ca4c61d4c81ea83fad8978da0eb8a3
Parents
19adcf3
Tree
1cb03dc

3 changed files

StatusFile+-
M CMakeLists.txt 1 0
M src/fortty.f90 8 194
A src/render_state.f90 269 0
CMakeLists.txtmodified
@@ -87,6 +87,7 @@ set(FORTRAN_SOURCES
8787
     src/panes/layout.f90
8888
     src/tabs/tab_manager.f90
8989
     src/tabs/tab_bar.f90
90
+    src/render_state.f90
9091
     src/fortty.f90
9192
 )
9293
 
src/fortty.f90modified
@@ -1,6 +1,5 @@
11
 program fortty
22
   use window_mod
3
-  use selection_mod, only: selection_contains
43
   use gl_bindings
54
   use renderer_mod
65
   use font_mod, only: font_find_monospace, font_find_for_codepoint
@@ -8,14 +7,14 @@ program fortty
87
   use terminal_mod
98
   use parser_mod
109
   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
1211
   use config_mod
13
-  use cursor_mod, only: CURSOR_BLOCK, CURSOR_UNDERLINE, CURSOR_BAR
1412
   use glfw_bindings, only: glfwGetTime
1513
   use tab_manager_mod
1614
   use tab_bar_mod
1715
   use pane_mod
1816
   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
1918
   implicit none
2019
 
2120
   type(window_t) :: win
@@ -24,7 +23,6 @@ program fortty
2423
   type(pane_t), pointer :: active_pane
2524
   type(terminal_t), pointer :: term
2625
   type(pty_t), pointer :: active_pty
27
-  type(cell_t) :: cell
2826
   type(config_t) :: cfg
2927
   integer :: win_width, win_height
3028
   integer :: prev_width, prev_height
@@ -33,15 +31,12 @@ program fortty
3331
   character(len=256) :: font_path, fallback_path
3432
   character(len=4096) :: pty_buffer
3533
   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
3735
   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(:)
4036
   integer :: cell_width, cell_height, ascender  ! From font metrics
4137
   real(8) :: current_time, last_time, blink_timer
4238
   logical :: cursor_blink_visible, any_pty_alive
4339
   logical :: was_focused, is_focused
44
-  type(selection_t) :: sel
4540
   integer :: font_delta, new_font_size, base_font_size
4641
   character(len=256) :: font_path_saved, fallback_path_saved
4742
 
@@ -229,6 +224,10 @@ program fortty
229224
   call window_set_pty(active_pty)
230225
   call window_set_terminal(term)
231226
 
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
+
232231
   ! Register render callback for live resize support on macOS
233232
   call window_set_render_callback(do_render)
234233
 
@@ -251,6 +250,7 @@ program fortty
251250
       cursor_blink_visible = .not. cursor_blink_visible
252251
       blink_timer = 0.0d0
253252
     end if
253
+    call render_state_update_blink(cursor_blink_visible)
254254
 
255255
     ! Poll events first - this ensures resize callbacks fire BEFORE we render
256256
     ! so viewport and projection updates happen in the same frame
@@ -432,198 +432,12 @@ program fortty
432432
   end do
433433
 
434434
   ! Cleanup
435
-  if (allocated(sb_line)) deallocate(sb_line)
436435
   call tab_manager_destroy(tab_mgr)
437436
   call renderer_destroy(ren)
438437
   call window_destroy(win)
439438
 
440439
 contains
441440
 
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
-
627441
   ! Handle tab action signals from keyboard
628442
   subroutine handle_tab_action(action)
629443
     use window_mod, only: TAB_ACTION_NEW, TAB_ACTION_CLOSE, TAB_ACTION_NEXT, TAB_ACTION_PREV
src/render_state.f90added
@@ -0,0 +1,269 @@
1
+! Render state module - holds state for the render callback
2
+! This allows do_render to be a module procedure instead of an internal
3
+! procedure, avoiding the need for trampolines and executable stack.
4
+module render_state_mod
5
+  use window_mod
6
+  use gl_bindings
7
+  use renderer_mod
8
+  use terminal_mod
9
+  use screen_mod
10
+  use cell_mod
11
+  use config_mod
12
+  use cursor_mod, only: CURSOR_BLOCK, CURSOR_UNDERLINE, CURSOR_BAR
13
+  use tab_manager_mod
14
+  use tab_bar_mod
15
+  use pane_mod
16
+  use selection_mod, only: selection_t, selection_contains
17
+  implicit none
18
+  private
19
+
20
+  public :: render_state_init
21
+  public :: render_state_update_blink
22
+  public :: do_render
23
+
24
+  ! Pointers to main program state
25
+  type(window_t), pointer, save :: rs_win => null()
26
+  type(renderer_t), pointer, save :: rs_ren => null()
27
+  type(tab_manager_t), pointer, save :: rs_tab_mgr => null()
28
+  type(config_t), pointer, save :: rs_cfg => null()
29
+
30
+  ! Scalar state (copied, not pointed)
31
+  integer, save :: rs_prev_width = 0
32
+  integer, save :: rs_prev_height = 0
33
+  integer, save :: rs_win_width = 0
34
+  integer, save :: rs_win_height = 0
35
+  integer, save :: rs_cell_width = 10
36
+  integer, save :: rs_cell_height = 18
37
+  integer, save :: rs_ascender = 14
38
+  logical, save :: rs_cursor_blink_visible = .true.
39
+
40
+  ! Scrollback line buffer
41
+  type(cell_t), allocatable, save :: rs_sb_line(:)
42
+
43
+contains
44
+
45
+  ! Initialize render state with pointers to main program data
46
+  subroutine render_state_init(win, ren, tab_mgr, cfg, &
47
+                               cell_width, cell_height, ascender, &
48
+                               win_width, win_height)
49
+    type(window_t), target, intent(in) :: win
50
+    type(renderer_t), target, intent(in) :: ren
51
+    type(tab_manager_t), target, intent(in) :: tab_mgr
52
+    type(config_t), target, intent(in) :: cfg
53
+    integer, intent(in) :: cell_width, cell_height, ascender
54
+    integer, intent(in) :: win_width, win_height
55
+
56
+    rs_win => win
57
+    rs_ren => ren
58
+    rs_tab_mgr => tab_mgr
59
+    rs_cfg => cfg
60
+    rs_cell_width = cell_width
61
+    rs_cell_height = cell_height
62
+    rs_ascender = ascender
63
+    rs_win_width = win_width
64
+    rs_win_height = win_height
65
+    rs_prev_width = win_width
66
+    rs_prev_height = win_height
67
+  end subroutine render_state_init
68
+
69
+  ! Update render state with current values
70
+  subroutine render_state_update_blink(cursor_blink_visible)
71
+    logical, intent(in) :: cursor_blink_visible
72
+    rs_cursor_blink_visible = cursor_blink_visible
73
+  end subroutine render_state_update_blink
74
+
75
+  ! Main render subroutine - now a module procedure, not internal
76
+  subroutine do_render()
77
+    integer :: render_width, render_height
78
+    integer :: pane_idx, tab_idx, tab_bar_height
79
+    integer :: scissor_x, scissor_y, scissor_w, scissor_h
80
+    real :: dim_factor, pane_x_offset, pane_y_offset
81
+    real :: bg_r, bg_g, bg_b
82
+    type(pane_t), pointer :: cur_pane
83
+    type(terminal_t), pointer :: pane_term
84
+    type(screen_t), pointer :: pane_scr
85
+    type(selection_t) :: sel
86
+    type(cell_t) :: cell
87
+    integer :: row, col, scroll_offset, sb_offset, screen_row
88
+    real :: x, y, r, g, b
89
+
90
+    ! Guard against uninitialized state
91
+    if (.not. associated(rs_win)) return
92
+    if (.not. associated(rs_ren)) return
93
+    if (.not. associated(rs_tab_mgr)) return
94
+    if (.not. associated(rs_cfg)) return
95
+
96
+    ! Get current window size and update projection if needed
97
+    call window_get_size(rs_win, render_width, render_height)
98
+    if (render_width /= rs_prev_width .or. render_height /= rs_prev_height) then
99
+      rs_prev_width = render_width
100
+      rs_prev_height = render_height
101
+      rs_win_width = render_width
102
+      rs_win_height = render_height
103
+      call renderer_set_projection(rs_ren, rs_win_width, rs_win_height)
104
+    end if
105
+
106
+    ! Clear screen with background color and opacity from config
107
+    bg_r = real(rs_cfg%bg_color%r) / 255.0
108
+    bg_g = real(rs_cfg%bg_color%g) / 255.0
109
+    bg_b = real(rs_cfg%bg_color%b) / 255.0
110
+    call glClearColor(bg_r, bg_g, bg_b, rs_cfg%window_opacity)
111
+    call glClear(GL_COLOR_BUFFER_BIT)
112
+
113
+    ! Render terminal buffer
114
+    call renderer_begin(rs_ren)
115
+
116
+    ! Render tab bar at top
117
+    call tab_bar_render(rs_ren, rs_tab_mgr, rs_win_width, rs_tab_mgr%bar_height, &
118
+                        rs_cell_width, rs_ascender)
119
+
120
+    ! Calculate effective tab bar height (hidden when only 1 tab)
121
+    if (rs_tab_mgr%count > 1) then
122
+      tab_bar_height = rs_tab_mgr%bar_height
123
+    else
124
+      tab_bar_height = 0
125
+    end if
126
+
127
+    ! Guard against no active tab
128
+    if (rs_tab_mgr%active_index < 1 .or. rs_tab_mgr%active_index > rs_tab_mgr%count) then
129
+      call renderer_flush(rs_ren)
130
+      call window_swap_buffers(rs_win)
131
+      return
132
+    end if
133
+
134
+    tab_idx = rs_tab_mgr%active_index
135
+
136
+    ! Get current selection for highlighting (only applies to active pane)
137
+    sel = window_get_selection()
138
+
139
+    ! Render each pane in the active tab
140
+    do pane_idx = 1, rs_tab_mgr%tabs(tab_idx)%pane_count
141
+      cur_pane => rs_tab_mgr%tabs(tab_idx)%panes(pane_idx)
142
+      pane_term => cur_pane%term
143
+      pane_scr => terminal_active_screen(pane_term)
144
+
145
+      ! Determine dimming factor (inactive panes are dimmed)
146
+      if (cur_pane%active) then
147
+        dim_factor = 1.0
148
+      else
149
+        dim_factor = 0.6
150
+      end if
151
+
152
+      ! Calculate pane offset for rendering
153
+      pane_x_offset = real(cur_pane%x)
154
+      pane_y_offset = real(cur_pane%y)
155
+
156
+      ! Enable scissor test for this pane's viewport
157
+      ! OpenGL uses bottom-left origin, so convert from top-left
158
+      scissor_x = cur_pane%x
159
+      scissor_y = rs_win_height - cur_pane%y - cur_pane%height
160
+      scissor_w = cur_pane%width
161
+      scissor_h = cur_pane%height
162
+      call glEnable(GL_SCISSOR_TEST)
163
+      call glScissor(scissor_x, scissor_y, scissor_w, scissor_h)
164
+
165
+      scroll_offset = terminal_get_scroll_offset(pane_term)
166
+
167
+      ! Allocate/resize scrollback line buffer if needed
168
+      if (.not. allocated(rs_sb_line)) then
169
+        allocate(rs_sb_line(cur_pane%cols))
170
+      else if (size(rs_sb_line) /= cur_pane%cols) then
171
+        deallocate(rs_sb_line)
172
+        allocate(rs_sb_line(cur_pane%cols))
173
+      end if
174
+
175
+      do row = 1, pane_scr%rows
176
+        ! Cell top-left y coordinate relative to pane
177
+        y = real(row - 1) * rs_cell_height + pane_y_offset
178
+
179
+        ! Determine if this row shows scrollback or screen content
180
+        sb_offset = scroll_offset - row + 1
181
+
182
+        if (sb_offset > 0 .and. sb_offset <= terminal_get_scrollback_count(pane_term)) then
183
+          ! This row shows scrollback content
184
+          call terminal_get_scrollback_line(pane_term, sb_offset - 1, rs_sb_line, cur_pane%cols)
185
+          do col = 1, min(cur_pane%cols, pane_scr%cols)
186
+            cell = rs_sb_line(col)
187
+
188
+            ! Skip continuation cells (2nd half of wide chars)
189
+            if (cell%is_continuation) cycle
190
+
191
+            x = real(col - 1) * rs_cell_width + pane_x_offset
192
+
193
+            ! Draw selection background if selected (only for active pane)
194
+            if (cur_pane%active .and. selection_contains(sel, row, col)) then
195
+              call renderer_draw_rect(rs_ren, x, y, real(rs_cell_width), real(rs_cell_height), &
196
+                                      0.3, 0.3, 0.6, 1.0)
197
+            end if
198
+
199
+            ! Only render cells with actual text content
200
+            if (cell%codepoint /= 32 .and. cell%codepoint /= 0) then
201
+              r = real(cell%fg%r) / 255.0 * dim_factor
202
+              g = real(cell%fg%g) / 255.0 * dim_factor
203
+              b = real(cell%fg%b) / 255.0 * dim_factor
204
+              call renderer_draw_char(rs_ren, x, y + real(rs_ascender), cell%codepoint, r, g, b, 1.0)
205
+            end if
206
+          end do
207
+        else
208
+          ! This row shows screen content
209
+          screen_row = row - scroll_offset
210
+          if (screen_row >= 1 .and. screen_row <= pane_scr%rows) then
211
+            do col = 1, pane_scr%cols
212
+              cell = screen_get_cell(pane_scr, screen_row, col)
213
+
214
+              ! Skip continuation cells (2nd half of wide chars)
215
+              if (cell%is_continuation) cycle
216
+
217
+              x = real(col - 1) * rs_cell_width + pane_x_offset
218
+
219
+              ! Draw selection background if selected (only for active pane)
220
+              if (cur_pane%active .and. selection_contains(sel, row, col)) then
221
+                call renderer_draw_rect(rs_ren, x, y, real(rs_cell_width), real(rs_cell_height), &
222
+                                        0.3, 0.3, 0.6, 1.0)
223
+              end if
224
+
225
+              ! Only render cells with actual text content
226
+              if (cell%codepoint /= 32 .and. cell%codepoint /= 0) then
227
+                r = real(cell%fg%r) / 255.0 * dim_factor
228
+                g = real(cell%fg%g) / 255.0 * dim_factor
229
+                b = real(cell%fg%b) / 255.0 * dim_factor
230
+                call renderer_draw_char(rs_ren, x, y + real(rs_ascender), cell%codepoint, r, g, b, 1.0)
231
+              end if
232
+            end do
233
+          end if
234
+        end if
235
+      end do
236
+
237
+      ! Draw cursor if visible (only for active pane and when not scrolled back)
238
+      if (cur_pane%active .and. pane_term%cursor%visible .and. scroll_offset == 0) then
239
+        ! Check blink state - only hide cursor if blink is enabled and in off phase
240
+        if (.not. pane_term%cursor%blink .or. rs_cursor_blink_visible) then
241
+          x = real(pane_term%cursor%col - 1) * rs_cell_width + pane_x_offset
242
+          y = real(pane_term%cursor%row - 1) * rs_cell_height + pane_y_offset
243
+
244
+          select case (pane_term%cursor%style)
245
+            case (CURSOR_BLOCK)
246
+              call renderer_draw_rect(rs_ren, x, y, real(rs_cell_width), real(rs_cell_height), &
247
+                                      0.7, 0.7, 0.7, 0.8)
248
+            case (CURSOR_UNDERLINE)
249
+              call renderer_draw_rect(rs_ren, x, y + real(rs_ascender), &
250
+                                      real(rs_cell_width), 2.0, 0.7, 0.7, 0.7, 1.0)
251
+            case (CURSOR_BAR)
252
+              call renderer_draw_rect(rs_ren, x, y, 2.0, real(rs_cell_height), 0.7, 0.7, 0.7, 1.0)
253
+            case default
254
+              call renderer_draw_rect(rs_ren, x, y, real(rs_cell_width), real(rs_cell_height), &
255
+                                      0.7, 0.7, 0.7, 0.8)
256
+          end select
257
+        end if
258
+      end if
259
+
260
+      call glDisable(GL_SCISSOR_TEST)
261
+    end do
262
+
263
+    call renderer_flush(rs_ren)
264
+
265
+    ! Swap buffers
266
+    call window_swap_buffers(rs_win)
267
+  end subroutine do_render
268
+
269
+end module render_state_mod