1 /* Copyright (C) 2021-2022 Harry Godden (hgn) - All Rights Reserved */
41 int persistent
; /* Should this var be stored to cfg/auto.conf? */
47 int (*function
)( int argc
, char const *argv
[] );
52 u32 convar_count
, convar_cap
,
53 function_count
, function_cap
;
56 int cursor_user
, cursor_pos
, string_length
;
59 int history_last
, history_pos
, history_count
;
64 vg_console
= { .scale
= 1 };
66 static void vg_convar_push( struct vg_convar cv
);
67 static void vg_function_push( struct vg_cmd cmd
);
69 static void vg_console_draw( void );
70 void vg_console_println( const char *str
);
71 static int vg_console_list( int argc
, char const *argv
[] );
72 static void vg_console_init(void);
73 static void vg_console_write_persistent(void);
74 static void vg_console_free(void);
75 static void execute_console_input( const char *cmd
);
80 static void console_make_selection( int* start
, int* end
);
81 static void console_move_cursor( int* cursor0
, int* cursor1
,
82 int dir
, int snap_together
);
83 static int console_makeroom( int datastart
, int length
);
84 static int console_delete_char( int direction
);
85 static void console_to_clipboard(void);
86 static void console_clipboard_paste(void);
87 static void console_put_char( char c
);
88 static void console_history_get( char* buf
, int entry_num
);
89 static void console_proc_key( GLFWwindow
* ptrW
, int key
,
90 int scancode
, int action
, int mods
);
91 static void console_proc_wchar( GLFWwindow
* ptrW
, u32 uWchar
);
92 static int vg_console_enabled(void);
97 static int vg_console_enabled(void)
99 return vg_console
.enabled
;
102 static void vg_convar_push( struct vg_convar cv
)
104 vg_info( "Console variable '%s' registered\n", cv
.name
);
105 vg_console
.convars
= buffer_reserve( vg_console
.convars
,
106 vg_console
.convar_count
,
107 &vg_console
.convar_cap
, 1,
108 sizeof( struct vg_convar
) );
110 vg_console
.convars
[ vg_console
.convar_count
++ ] = cv
;
113 static void vg_function_push( struct vg_cmd cmd
)
115 vg_info( "Console command '%s' registered\n", cmd
.name
);
116 vg_console
.functions
= buffer_reserve( vg_console
.functions
,
117 vg_console
.function_count
,
118 &vg_console
.function_cap
, 1,
119 sizeof( struct vg_cmd
) );
121 vg_console
.functions
[ vg_console
.function_count
++ ] = cmd
;
124 static void vg_console_draw( void )
126 if( !vg_console
.enabled
)
129 vg_mutex_lock( &log_print_mutex
);
131 int ptr
= vg_log
.buffer_line_current
;
133 int console_lines
= VG_MIN( 16, vg_log
.buffer_line_count
);
135 ui_global_ctx
.cursor
[0] = 0;
136 ui_global_ctx
.cursor
[1] = 0;
137 ui_global_ctx
.cursor
[3] = 16*fh
*vg_console
.scale
;
138 ui_fill_x( &ui_global_ctx
);
140 ui_new_node( &ui_global_ctx
);
142 ui_fill_rect( &ui_global_ctx
, ui_global_ctx
.cursor
, 0x77333333 );
144 ui_global_ctx
.cursor
[3] = fh
*vg_console
.scale
;
145 ui_align_bottom( &ui_global_ctx
);
147 for( int i
=0; i
<console_lines
; i
++ )
152 ptr
= vg_list_size( vg_log
.buffer
)-1;
154 ui_text( &ui_global_ctx
, ui_global_ctx
.cursor
,
155 vg_log
.buffer
[ptr
], vg_console
.scale
, 0 );
156 ui_global_ctx
.cursor
[1] -= fh
*vg_console
.scale
;
160 ui_end_down( &ui_global_ctx
);
162 ui_global_ctx
.cursor
[1] += 2;
163 ui_global_ctx
.cursor
[3] = fh
*vg_console
.scale
;
165 ui_new_node( &ui_global_ctx
);
167 ui_fill_rect( &ui_global_ctx
, ui_global_ctx
.cursor
, 0x77333333 );
169 ui_text( &ui_global_ctx
, ui_global_ctx
.cursor
,
170 vg_console
.input
, vg_console
.scale
, 0 );
172 int start
= VG_MIN( vg_console
.cursor_pos
, vg_console
.cursor_user
),
173 end
= VG_MAX( vg_console
.cursor_pos
, vg_console
.cursor_user
);
175 ui_global_ctx
.cursor
[0] = (start
* UI_GLYPH_SPACING_X
* vg_console
.scale
);
176 ui_global_ctx
.cursor
[2] = (start
== end
? 0.5f
: (float)(end
-start
))
177 * (float)UI_GLYPH_SPACING_X
* (float)vg_console
.scale
;
179 ui_fill_rect( &ui_global_ctx
, ui_global_ctx
.cursor
, 0x66ffffff );
181 ui_end_down( &ui_global_ctx
);
182 vg_mutex_unlock( &log_print_mutex
);
185 static int vg_console_list( int argc
, char const *argv
[] )
187 for( int i
=0; i
<vg_console
.function_count
; i
++ )
189 struct vg_cmd
*cmd
= &vg_console
.functions
[ i
];
190 vg_info( "* %s\n", cmd
->name
);
193 vg_info( "* snowsound\n" );
195 for( int i
=0; i
<vg_console
.convar_count
; i
++ )
197 struct vg_convar
*cv
= &vg_console
.convars
[ i
];
198 vg_info( "%s\n", cv
->name
);
204 static int vg_console_chartest( int argc
, char const *argv
[] )
206 vg_info(" Copyright . . . -----, ,----- ,---. .---. " );
207 vg_info(" 2021-2022 |\\ /| | / | | | | /| " );
208 vg_info(" | \\ / | +-- / +----- +---' | / | " );
209 vg_info(" | \\ / | | / | | \\ | / | " );
210 vg_info(" | \\/ | | / | | \\ | / | " );
211 vg_info(" ' ' '--' [] '----- '----- ' ' '---' "
214 vg_info( "\"THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG\"\n" );
215 vg_info( "'the quick brown fox jumps over the lazy dog'\n" );
216 vg_info( ":;!@#$%^& 0123456789 +-*/=~ ()[]{}<>\n" );
220 static int _test_break( int argc
, const char *argv
[] )
222 vg_fatal_exit_loop( "Test crash from main, after loading (console)" );
226 static void vg_console_init(void)
228 vg_convar_push( (struct vg_convar
)
230 .name
= "console_scale", .data
= &vg_console
.scale
,
231 .data_type
= k_convar_dtype_i32
,
232 .opt_i32
= { .clamp
= 1, .min
= 1, .max
= 7 },
236 vg_function_push( (struct vg_cmd
)
239 .function
= vg_console_list
242 vg_function_push( (struct vg_cmd
)
245 .function
= vg_console_chartest
248 vg_function_push( (struct vg_cmd
)
251 .function
= _test_break
255 static void vg_console_load_autos(void)
257 /* Read and exec persistent commands */
258 FILE *fp
= fopen( "cfg/auto.conf", "r" );
263 while( fgets( line
, sizeof( line
), fp
) )
265 line
[ strcspn( line
, "\r\n#" ) ] = 0x00;
267 if( line
[0] != 0x00 )
269 execute_console_input( line
);
277 static void vg_console_write_persistent(void)
279 FILE *fp
= fopen( "cfg/auto.conf", "w" );
281 for( int i
=0; i
<vg_console
.convar_count
; i
++ )
283 struct vg_convar
*cv
= &vg_console
.convars
[i
];
287 switch( cv
->data_type
)
289 case k_convar_dtype_i32
:
290 fprintf( fp
, "%s %d\n", cv
->name
, *(i32
*)(cv
->data
) );
292 case k_convar_dtype_u32
:
293 fprintf( fp
, "%s %u\n", cv
->name
, *(u32
*)(cv
->data
) );
295 case k_convar_dtype_f32
:
296 fprintf( fp
, "%s %.5f\n", cv
->name
, *(float *)(cv
->data
) );
305 static void vg_console_free(void)
307 vg_console_write_persistent();
309 vg_free( vg_console
.convars
);
310 vg_free( vg_console
.functions
);
313 static void execute_console_input( const char *cmd
)
321 /* Split string into tokens */
322 for( int i
= 0; i
< vg_list_size( temp
); i
++ )
326 if( cmd
[i
] == ' ' || cmd
[i
] == '\t' )
331 if( arg_count
== vg_list_size( args
) )
340 args
[ arg_count
++ ] = temp
+ i
;
358 for( int i
=0; i
<vg_console
.convar_count
; i
++ )
360 struct vg_convar
*cv
= &vg_console
.convars
[ i
];
361 if( !strcmp( cv
->name
, args
[0] ) )
363 /* Cvar Matched, try get value */
366 switch( cv
->data_type
)
368 case k_convar_dtype_u32
:
369 case k_convar_dtype_i32
:
371 data_int
= atoi( args
[1] );
373 *((int *)cv
->data
) = cv
->opt_i32
.clamp
?
374 VG_MIN( VG_MAX(data_int
, cv
->opt_i32
.min
), cv
->opt_i32
.max
):
378 case k_convar_dtype_f32
:
379 data_float
= atof( args
[1] );
380 *((float *)cv
->data
) = cv
->opt_f32
.clamp
?
381 vg_minf( vg_maxf( data_float
, cv
->opt_f32
.min
), cv
->opt_f32
.max
):
391 switch( cv
->data_type
)
393 case k_convar_dtype_i32
:
394 vg_info( "= %d\n", *((int *)cv
->data
) );
396 case k_convar_dtype_u32
:
397 vg_info( "= %u\n", *((u32
*)cv
->data
) );
399 case k_convar_dtype_f32
:
400 vg_info( "= %.4f\n", *((float *)cv
->data
) );
410 * Find and excecute command
412 for( int i
=0; i
<vg_console
.function_count
; i
++ )
414 struct vg_cmd
*cmd
= &vg_console
.functions
[ i
];
415 if( !strcmp( cmd
->name
, args
[0] ) )
417 cmd
->function( arg_count
-1, args
+1 );
422 vg_error( "No command/var named '%s'. Use 'list' to view all\n", args
[0] );
428 static void console_make_selection( int* start
, int* end
)
430 *start
= VG_MIN( vg_console
.cursor_pos
, vg_console
.cursor_user
);
431 *end
= VG_MAX( vg_console
.cursor_pos
, vg_console
.cursor_user
);
434 static void console_move_cursor( int* cursor0
, int* cursor1
,
435 int dir
, int snap_together
)
437 *cursor0
= VG_MAX( 0, vg_console
.cursor_user
+ dir
);
440 VG_MIN( vg_list_size( vg_console
.input
), strlen( vg_console
.input
)),
447 static int console_makeroom( int datastart
, int length
)
449 int move_to
= VG_MIN( datastart
+length
, vg_list_size( vg_console
.input
) );
450 int move_amount
= strlen( vg_console
.input
)-datastart
;
452 VG_MIN( move_to
+move_amount
, vg_list_size( vg_console
.input
) );
453 move_amount
= move_end
-move_to
;
456 memmove( &vg_console
.input
[ move_to
],
457 &vg_console
.input
[ datastart
],
460 vg_console
.input
[ move_end
] = '\0';
462 return VG_MIN( length
, vg_list_size( vg_console
.input
)-datastart
);
465 static int console_delete_char( int direction
)
468 console_make_selection( &start
, &end
);
470 /* There is no selection */
473 if( direction
== 1 ) end
= VG_MIN( end
+1, strlen( vg_console
.input
) );
474 else if( direction
== -1 ) start
= VG_MAX( start
-1, 0 );
477 /* Still no selction, no need to do anything */
481 /* Copy the end->terminator to start */
482 int remaining_length
= strlen( vg_console
.input
)+1-end
;
483 memmove( &vg_console
.input
[ start
],
484 &vg_console
.input
[ end
],
489 static void console_to_clipboard(void)
492 console_make_selection( &start
, &end
);
497 memcpy( buffer
, &vg_console
.input
[ start
], end
-start
);
498 buffer
[ end
-start
] = 0x00;
499 glfwSetClipboardString( NULL
, buffer
);
503 static void console_clipboard_paste(void)
505 int datastart
= console_delete_char(0);
506 const char* clipboard
= glfwGetClipboardString(NULL
);
507 int length
= strlen(clipboard
);
509 int cpylength
= console_makeroom(datastart
, length
);
511 memcpy( vg_console
.input
+ datastart
, clipboard
, cpylength
);
512 console_move_cursor( &vg_console
.cursor_user
,
513 &vg_console
.cursor_pos
, cpylength
, 1 );
516 static void console_put_char( char c
)
518 if( !vg_console
.enabled
)
521 vg_console
.cursor_user
= console_delete_char(0);
523 if( console_makeroom( vg_console
.cursor_user
, 1 ) )
524 vg_console
.input
[ vg_console
.cursor_user
] = c
;
526 console_move_cursor( &vg_console
.cursor_user
, &vg_console
.cursor_pos
, 1, 1 );
529 static void console_history_get( char* buf
, int entry_num
)
531 if( !vg_console
.history_count
)
534 int offset
= VG_MIN( entry_num
, vg_console
.history_count
-1 ),
535 pick
= (vg_console
.history_last
- offset
) %
536 vg_list_size( vg_console
.history
);
537 strcpy( buf
, vg_console
.history
[ pick
] );
540 /* Receed secondary cursor */
541 static void _console_left_select(void)
543 console_move_cursor( &vg_console
.cursor_user
, NULL
, -1, 0 );
546 /* Match and receed both cursors */
547 static void _console_left(void)
549 int cursor_diff
= vg_console
.cursor_pos
- vg_console
.cursor_user
? 0: 1;
551 console_move_cursor( &vg_console
.cursor_user
,
552 &vg_console
.cursor_pos
, -cursor_diff
, 1 );
555 static void _console_right_select(void)
557 console_move_cursor( &vg_console
.cursor_user
, NULL
, 1, 0 );
560 static void _console_right(void)
562 int cursor_diff
= vg_console
.cursor_pos
- vg_console
.cursor_user
? 0: 1;
564 console_move_cursor( &vg_console
.cursor_user
,
565 &vg_console
.cursor_pos
, +cursor_diff
, 1 );
568 static void _console_down(void)
570 vg_console
.history_pos
= VG_MAX( 0, vg_console
.history_pos
-1 );
571 console_history_get( vg_console
.input
, vg_console
.history_pos
);
573 console_move_cursor( &vg_console
.cursor_user
,
574 &vg_console
.cursor_pos
,
575 vg_list_size( vg_console
.input
), 1 );
578 static void _console_up(void)
580 vg_console
.history_pos
= VG_MAX
585 vg_console
.history_pos
+1,
588 vg_list_size( vg_console
.history
),
589 vg_console
.history_count
- 1
594 console_history_get( vg_console
.input
, vg_console
.history_pos
);
595 console_move_cursor( &vg_console
.cursor_user
,
596 &vg_console
.cursor_pos
,
597 vg_list_size( vg_console
.input
), 1);
600 static void _console_backspace(void)
602 vg_console
.cursor_user
= console_delete_char( -1 );
603 vg_console
.cursor_pos
= vg_console
.cursor_user
;
606 static void _console_delete(void)
608 vg_console
.cursor_user
= console_delete_char( 1 );
609 vg_console
.cursor_pos
= vg_console
.cursor_user
;
612 static void _console_home_select(void)
614 console_move_cursor( &vg_console
.cursor_user
, NULL
, -10000, 0 );
617 static void _console_home(void)
619 console_move_cursor( &vg_console
.cursor_user
,
620 &vg_console
.cursor_pos
, -10000, 1 );
623 static void _console_end_select(void)
625 console_move_cursor( &vg_console
.cursor_user
, NULL
, 10000, 0 );
628 static void _console_end(void)
630 console_move_cursor( &vg_console
.cursor_user
,
631 &vg_console
.cursor_pos
,
632 vg_list_size( vg_console
.input
), 1 );
635 static void _console_select_all(void)
637 console_move_cursor( &vg_console
.cursor_user
, NULL
, 10000, 0);
638 console_move_cursor( &vg_console
.cursor_pos
, NULL
, -10000, 0);
641 static void _console_cut(void)
643 console_to_clipboard();
644 vg_console
.cursor_user
= console_delete_char(0);
645 vg_console
.cursor_pos
= vg_console
.cursor_user
;
648 static void _console_enter(void)
650 if( !strlen( vg_console
.input
) )
653 vg_info( "%s\n", vg_console
.input
);
655 if( strcmp( vg_console
.input
,
656 vg_console
.history
[ vg_console
.history_last
]) )
658 vg_console
.history_last
= ( vg_console
.history_last
+ 1) %
659 vg_list_size(vg_console
.history
);
660 vg_console
.history_count
=
661 VG_MIN( vg_list_size( vg_console
.history
),
662 vg_console
.history_count
+ 1 );
663 strcpy( vg_console
.history
[ vg_console
.history_last
],
667 vg_console
.history_pos
= -1;
668 execute_console_input( vg_console
.input
);
669 console_move_cursor( &vg_console
.cursor_user
,
670 &vg_console
.cursor_pos
, -10000, 1 );
671 vg_console
.input
[0] = '\0';
674 static void console_proc_key( GLFWwindow
* ptrW
, int key
, int scancode
,
675 int action
, int mods
)
680 /* Open / close console */
681 if( key
== GLFW_KEY_GRAVE_ACCENT
)
682 vg_console
.enabled
= !vg_console
.enabled
;
684 if( !vg_console
.enabled
) return;
686 struct console_mapping
689 void (*handler
)(void);
693 { 0, GLFW_KEY_LEFT
, _console_left
},
694 { GLFW_MOD_SHIFT
, GLFW_KEY_LEFT
, _console_left_select
},
695 { 0, GLFW_KEY_RIGHT
, _console_right
},
696 { GLFW_MOD_SHIFT
, GLFW_KEY_RIGHT
, _console_right_select
},
697 { 0, GLFW_KEY_DOWN
, _console_down
},
698 { 0, GLFW_KEY_UP
, _console_up
},
699 { 0, GLFW_KEY_BACKSPACE
, _console_backspace
},
700 { 0, GLFW_KEY_DELETE
, _console_delete
},
701 { 0, GLFW_KEY_HOME
, _console_home
},
702 { GLFW_MOD_SHIFT
, GLFW_KEY_HOME
, _console_home_select
},
703 { 0, GLFW_KEY_END
, _console_end
},
704 { GLFW_MOD_SHIFT
, GLFW_KEY_END
, _console_end_select
},
705 { GLFW_MOD_CONTROL
, GLFW_KEY_A
, _console_select_all
},
706 { GLFW_MOD_CONTROL
, GLFW_KEY_C
, console_to_clipboard
},
707 { GLFW_MOD_CONTROL
, GLFW_KEY_X
, _console_cut
},
708 { GLFW_MOD_CONTROL
, GLFW_KEY_V
, console_clipboard_paste
},
709 { 0, GLFW_KEY_ENTER
, _console_enter
}
712 for( int i
=0; i
<vg_list_size( mapping
); i
++ )
714 struct console_mapping
*mk
= &mapping
[i
];
726 else if( (mods
& mk
->mod
) == mk
->mod
)
735 /* Handle an OS based input of UTF32 character from the keyboard or such */
736 static void console_proc_wchar( GLFWwindow
* ptrW
, u32 uWchar
)
738 if( uWchar
<= 0x7F && (char)uWchar
!= 0x60)
740 console_put_char((char)uWchar
);
744 #endif /* VG_CONSOLE_H */