raiguard/tcom
git clone https://git.raiguard.me/raiguard/tcom
7992681
trunk
1package tcom 2 3import rt "base:runtime" 4import hm "core:container/handle_map" 5import "core:log" 6import "core:testing" 7 8Cell :: struct { 9r : rune , 10attribs : Cell_Attribs , 11// TODO: Colors 12} 13 14Cell_Attrib :: enum { 15Bold , 16Dim , 17Italic , 18Underline , 19} 20 21Cell_Attribs :: bit_set [ Cell_Attrib ] 22 23Pane :: struct { 24cells : [] Cell , // owned 25dims : TTY_Pos , 26cursor : Pane_Cursor , 27handle : Pane_Handle , 28allocator : rt . Allocator , 29child : Sys_Child_Info , 30} 31 32Pane_Cursor :: struct { 33using pos : [ 2 ] u16 , 34attribs : Cell_Attribs , 35} 36 37pane_init :: proc ( cmd : [] string , dims : TTY_Pos , allocator : rt . Allocator ) -> ( h : Pane_Handle , err : rt . Allocator_Error ) { 38return hm . add ( & g_panes , Pane { 39cells = make_slice ([] Cell , dims . x * dims . y , allocator ) or_return , 40dims = dims , 41allocator = allocator , 42child = sys_child_start ( cmd , dims , allocator ) or_return , 43}) 44} 45 46pane_init_headless :: proc ( dims : TTY_Pos , allocator : rt . Allocator ) -> ( h : Pane_Handle , err : rt . Allocator_Error ) { 47return hm . add ( & g_panes , Pane { 48cells = make_slice ([] Cell , dims . x * dims . y , allocator ) or_return , 49dims = dims , 50allocator = allocator , 51}) 52} 53 54pane_fini :: proc ( pane_handle : Pane_Handle ) { 55pane , ok := hm . get ( & g_panes , pane_handle ) 56if ! ok { 57log . errorf ( "pane_fini called when pane does not exist." ) 58return 59} 60delete ( pane . cells , pane . allocator ) 61hm . remove ( & g_panes , pane_handle ) 62} 63 64pane_set_dims :: proc ( h : Pane_Handle , dims : TTY_Pos ) -> ( ok : bool ) { 65pane := hm . get ( & g_panes , h ) or_return 66sys_term_set_dims ( dims , pane . child ) 67return true 68} 69 70pane_handle_event :: proc ( h : Pane_Handle , event : TTY_Event ) -> ( ok : bool ) { 71p := hm . get ( & g_panes , h ) or_return 72 73switch e in event { 74case TTY_Ctrl : 75return _pane_handle_ctrl ( p , e ) 76case TTY_Resized : 77log . error ( "TTY_Resize is unimplemented." ) 78// TODO: 79case TTY_Text : 80return _pane_handle_text ( p , e ) 81} 82 83return true 84} 85 86pane_draw :: proc ( h : Pane_Handle ) -> ( ok : bool ) { 87pane := hm . get ( & g_panes , h ) or_return 88log . debug ( "DRAW:" , pane ) 89// TODO: 90return true 91} 92 93// --------------------------------------------------------------------------------------------------------------------- 94// INTERNALS 95 96_pane_advance_cursor :: proc ( p : ^ Pane ) { 97p . cursor . x = ( p . cursor . x + 1 ) % p . dims . x 98if p . cursor . x == 0 { 99p . cursor . y += 1 100} 101} 102 103_pane_get_cursor_cell :: proc ( p : ^ Pane ) -> ^ Cell { 104i := ( p . cursor . y * p . dims . x ) + p . cursor . x 105// log.debug("cursor offset calc:", p.cursor.pos, i) 106return & p . cells [ i ] 107} 108 109_pane_handle_ctrl :: proc ( p : ^ Pane , ctrl : TTY_Ctrl ) -> ( ok : bool ) { 110switch ctrl { 111// C0 control codes 112case . Bell : 113case . Backspace : 114case . Tab : 115case . Line_Feed : 116case . Form_Feed : 117case . Carriage_Return : 118// CSI commands 119case . Reset_All : 120p . cursor . attribs = {} 121case . Bold : 122p . cursor . attribs += {. Bold } 123case . Dim : 124p . cursor . attribs += {. Dim } 125case . Italic : 126p . cursor . attribs += {. Italic } 127case . Underline : 128p . cursor . attribs += {. Underline } 129} 130return true 131} 132 133_pane_handle_text :: proc ( p : ^ Pane , r : rune ) -> ( ok : bool ) { 134cell := _pane_get_cursor_cell ( p ) 135cell . r = r 136cell . attribs = p . cursor . attribs 137_pane_advance_cursor ( p ) 138 139return true 140} 141 142// --------------------------------------------------------------------------------------------------------------------- 143// TESTS 144 145@ test 146pane_test_cursor_attribs :: proc ( t : ^ testing . T ) { 147panes_init ( context . allocator ) 148defer panes_fini () 149 150h , err := pane_init_headless ({ 600 , 400 }, context . allocator ) 151testing . expect ( t , err == nil ) 152defer pane_fini ( h ) 153 154// The pane pointer will be stable throughout this test. 155pane , ok := hm . get ( & g_panes , h ) 156testing . expect ( t , ok ) 157 158pane_handle_event ( h , . Bold ) 159testing . expect ( t , . Bold in pane . cursor . attribs ) 160 161pane_handle_event ( h , . Dim ) 162testing . expect ( t , . Dim in pane . cursor . attribs ) 163 164pane_handle_event ( h , . Italic ) 165testing . expect ( t , . Italic in pane . cursor . attribs ) 166 167pane_handle_event ( h , . Underline ) 168testing . expect ( t , . Underline in pane . cursor . attribs ) 169 170pane_handle_event ( h , . Reset_All ) 171testing . expect ( t , pane . cursor . attribs == {}) 172}