X-Git-Url: https://harrygodden.com/git/?a=blobdiff_plain;f=vg_console.h;h=26c6a6d20c00414eef330fa18f7500b4caf52dd9;hb=0aea2ef68a5ed32fc940673402a1b4b67f38d4d3;hp=8d35aa286fdce8933e35b39720cd5dfd6534d850;hpb=a7938c00a8c486ad0e2a765c3092017487ba761e;p=vg.git diff --git a/vg_console.h b/vg_console.h index 8d35aa2..26c6a6d 100644 --- a/vg_console.h +++ b/vg_console.h @@ -10,22 +10,51 @@ #include "vg/vg_ui.h" #include "vg/vg_log.h" -typedef struct vg_convar vg_convar; +#define VG_VAR_F32_PERSISTENT( NAME ) \ + vg_var_push( (struct vg_var){ \ + .name = #NAME, \ + .data = &NAME, \ + .data_type = k_var_dtype_f32, \ + .persistent = 1 \ + }); + +#define VG_VAR_F32( NAME ) \ + vg_var_push( (struct vg_var){ \ + .name = #NAME, \ + .data = &NAME, \ + .data_type = k_var_dtype_f32, \ + }); + +#define VG_VAR_I32_PERSISTENT( NAME ) \ + vg_var_push( (struct vg_var){ \ + .name = #NAME, \ + .data = &NAME, \ + .data_type = k_var_dtype_i32, \ + .persistent = 1 \ + }); + +#define VG_VAR_I32( NAME ) \ + vg_var_push( (struct vg_var){ \ + .name = #NAME, \ + .data = &NAME, \ + .data_type = k_var_dtype_i32, \ + }); + +typedef struct vg_var vg_var; typedef struct vg_cmd vg_cmd; struct vg_console { - struct vg_convar + struct vg_var { void *data; - void (*update)(void); const char *name; - enum vg_convar_dtype + enum vg_var_dtype { - k_convar_dtype_i32, - k_convar_dtype_u32, - k_convar_dtype_f32 + k_var_dtype_i32, + k_var_dtype_u32, + k_var_dtype_f32 } data_type; @@ -47,18 +76,33 @@ struct vg_console int persistent; /* Should this var be stored to cfg/auto.conf? */ } - convars[ 32 ]; + vars[ 128 ]; struct vg_cmd { - int (*function)( int argc, char const *argv[] ); + int (*function)( int argc, char const *argv[] ); + void (*poll_suggest)( int argc, char const *argv[] ); const char *name; } functions[ 32 ]; - u32 convar_count, function_count; + struct + { + const char *str; + int len; + + u32 lev_score; + } + suggestions[12]; + u32 suggestion_count; + int suggestion_select, + suggestion_pastepos, + suggestion_maxlen; + + u32 var_count, function_count; - char input[96]; + char input[96], + input_copy[96]; int cursor_user, cursor_pos, string_length; char history[32][96]; @@ -68,7 +112,7 @@ struct vg_console } vg_console; -VG_STATIC void vg_convar_push( struct vg_convar cv ); +VG_STATIC void vg_var_push( struct vg_var cv ); VG_STATIC void vg_function_push( struct vg_cmd cmd ); VG_STATIC void _vg_console_draw( void ); @@ -102,13 +146,13 @@ VG_STATIC int _vg_console_enabled(void) return vg_console.enabled; } -VG_STATIC void vg_convar_push( vg_convar cv ) +VG_STATIC void vg_var_push( vg_var cv ) { - if( vg_console.convar_count > vg_list_size(vg_console.convars) ) - vg_fatal_exit_loop( "Too many convars registered" ); + if( vg_console.var_count > vg_list_size(vg_console.vars) ) + vg_fatal_exit_loop( "Too many vars registered" ); vg_info( "Console variable '%s' registered\n", cv.name ); - vg_console.convars[ vg_console.convar_count ++ ] = cv; + vg_console.vars[ vg_console.var_count ++ ] = cv; } VG_STATIC void vg_function_push( struct vg_cmd cmd ) @@ -136,9 +180,12 @@ VG_STATIC void _vg_console_draw( void ) vg_uictx.cursor[3] = log_lines*fh; ui_fill_x(); + /* + * log + */ ui_new_node(); { - ui_fill_rect( vg_uictx.cursor, 0x77333333 ); + ui_fill_rect( vg_uictx.cursor, 0x77181818 ); vg_uictx.cursor[3] = fh; ui_align_bottom(); @@ -157,12 +204,14 @@ VG_STATIC void _vg_console_draw( void ) } ui_end_down(); + + /* Input area */ vg_uictx.cursor[1] += 2; vg_uictx.cursor[3] = fh; - + ui_new_node(); { - ui_fill_rect( vg_uictx.cursor, 0x77333333 ); + ui_fill_rect( vg_uictx.cursor, 0x77111111 ); ui_text( vg_uictx.cursor, vg_console.input, 1, 0 ); int start = VG_MIN( vg_console.cursor_pos, vg_console.cursor_user ), @@ -175,6 +224,33 @@ VG_STATIC void _vg_console_draw( void ) ui_fill_rect( vg_uictx.cursor, 0x66ffffff ); } ui_end_down(); + + + /* suggestions */ + if( vg_console.suggestion_count ) + { + vg_uictx.cursor[0] += UI_GLYPH_SPACING_X * vg_console.suggestion_pastepos; + vg_uictx.cursor[1] += 2; + vg_uictx.cursor[3] = vg_console.suggestion_count * fh; + vg_uictx.cursor[2] = UI_GLYPH_SPACING_X * vg_console.suggestion_maxlen; + + ui_new_node(); + { + ui_fill_rect( vg_uictx.cursor, 0x77040404 ); + + vg_uictx.cursor[3] = fh; + for( int i=0; iname ); } - for( int i=0; iname ); } @@ -242,21 +318,21 @@ VG_STATIC void _vg_console_write_persistent(void) { FILE *fp = fopen( "cfg/auto.conf", "w" ); - for( int i=0; ipersistent ) { switch( cv->data_type ) { - case k_convar_dtype_i32: + case k_var_dtype_i32: fprintf( fp, "%s %d\n", cv->name, *(i32 *)(cv->data) ); break; - case k_convar_dtype_u32: + case k_var_dtype_u32: fprintf( fp, "%s %u\n", cv->name, *(u32 *)(cv->data) ); break; - case k_convar_dtype_f32: + case k_var_dtype_f32: fprintf( fp, "%s %.5f\n", cv->name, *(float *)(cv->data ) ); break; } @@ -271,119 +347,322 @@ VG_STATIC void _vg_console_free(void) _vg_console_write_persistent(); } -VG_STATIC void vg_execute_console_input( const char *cmd ) +/* + * splits src into tokens and fills out args as pointers to those tokens + * returns number of tokens + * dst must be as long as src + */ +VG_STATIC int vg_console_tokenize( const char *src, char *dst, + const char *args[8] ) { - char temp[512]; - char const *args[9]; - int arg_count = 0; + int arg_count = 0, + in_token = 0; - int in_token = 0; - - /* Split string into tokens */ - for( int i = 0; i < vg_list_size( temp ); i ++ ) + for( int i=0; 1; i ++ ) { - if( cmd[i] ) + if( src[i] ) { - if( cmd[i] == ' ' || cmd[i] == '\t' ) + if( src[i] == ' ' || src[i] == '\t' ) { - temp[i] = '\0'; + if( in_token ) + dst[i] = '\0'; + in_token = 0; - if( arg_count == vg_list_size( args ) ) + if( arg_count == 8 ) break; } else { - temp[i] = cmd[i]; + dst[i] = src[i]; if( !in_token ) { - args[ arg_count ++ ] = temp + i; + args[ arg_count ++ ] = &dst[i]; in_token = 1; } } } else { - temp[i] = '\0'; + dst[i] = '\0'; break; } } - - if( arg_count == 0 ) - return; - - int data_int; - float data_float; - - for( int i=0; iname, args[0] ) ) + struct vg_var *cv = &vg_console.vars[ i ]; + if( !strcmp( cv->name, kw ) ) { - /* Cvar Matched, try get value */ - if( arg_count >= 2 ) - { - switch( cv->data_type ) - { - case k_convar_dtype_u32: - case k_convar_dtype_i32: - - data_int = atoi( args[1] ); - - *((int *)cv->data) = cv->opt_i32.clamp? - VG_MIN( VG_MAX(data_int, cv->opt_i32.min), cv->opt_i32.max ): - data_int; - - break; - case k_convar_dtype_f32: - data_float = atof( args[1] ); - *((float *)cv->data) = cv->opt_f32.clamp? - vg_minf( vg_maxf( data_float, cv->opt_f32.min), - cv->opt_f32.max ): - data_float; - break; - } + return cv; + } + } - if( cv->update ) - cv->update(); - } - else - { - switch( cv->data_type ) - { - case k_convar_dtype_i32: - vg_info( "= %d\n", *((int *)cv->data) ); - break; - case k_convar_dtype_u32: - vg_info( "= %u\n", *((u32 *)cv->data) ); - break; - case k_convar_dtype_f32: - vg_info( "= %.4f\n", *((float *)cv->data) ); - break; - } - } - - return; - } - } - - /* - * Find and excecute command - */ + return NULL; +} + +VG_STATIC vg_cmd *vg_console_match_cmd( const char *kw ) +{ for( int i=0; iname, args[0] ) ) + if( !strcmp( cmd->name, kw ) ) { - cmd->function( arg_count-1, args+1 ); - return; - } + return cmd; + } + } + + return NULL; +} + +VG_STATIC void vg_execute_console_input( const char *cmd ) +{ + char temp[512]; + char const *args[8]; + int arg_count = vg_console_tokenize( cmd, temp, args ); + + if( arg_count == 0 ) + return; + + int data_int; + float data_float; + + vg_var *cv = vg_console_match_var( args[0] ); + vg_cmd *fn = vg_console_match_cmd( args[0] ); + + assert( !(cv && fn) ); + + if( cv ) + { + /* Cvar Matched, try get value */ + if( arg_count >= 2 ) + { + if( (cv->data_type == k_var_dtype_u32) || + (cv->data_type == k_var_dtype_i32) ) + { + data_int = atoi( args[1] ); + + *((int *)cv->data) = cv->opt_i32.clamp? + VG_MIN( VG_MAX(data_int, cv->opt_i32.min), cv->opt_i32.max ): + data_int; + } + else if( cv->data_type == k_var_dtype_f32 ) + { + data_float = atof( args[1] ); + *((float *)cv->data) = cv->opt_f32.clamp? + vg_minf( vg_maxf( data_float, cv->opt_f32.min), + cv->opt_f32.max ): + data_float; + } + } + else + { + if( cv->data_type == k_var_dtype_i32 ) + vg_info( "= %d\n", *((int *)cv->data) ); + else if( cv->data_type == k_var_dtype_u32 ) + vg_info( "= %u\n", *((u32 *)cv->data) ); + else if( cv->data_type == k_var_dtype_f32 ) + vg_info( "= %.4f\n", *((float *)cv->data) ); + } + + return; } + else if( fn ) + { + fn->function( arg_count-1, args+1 ); + return; + } vg_error( "No command/var named '%s'. Use 'list' to view all\n", args[0] ); } +u32 str_lev_distance( const char *s1, const char *s2 ) +{ + u32 m = strlen( s1 ), + n = strlen( s2 ); + + if( m==0 ) return n; + if( n==0 ) return m; + + assert( n+1 <= 256 ); + + u32 costs[ 256 ]; + + for( u32 k=0; k<=n; k++ ) + costs[k] = k; + + u32 i = 0; + for( u32 i=0; i=0; j -- ) + if( score > vg_console.suggestions[j].lev_score ) + best_pos = j; + + /* insert if good score */ + if( best_pos < vg_list_size( vg_console.suggestions ) ) + { + int start = VG_MIN( vg_console.suggestion_count, + vg_list_size( vg_console.suggestions )-1 ); + for( int j=start; j>best_pos; j -- ) + vg_console.suggestions[j] = vg_console.suggestions[j-1]; + + vg_console.suggestions[ best_pos ].str = str; + vg_console.suggestions[ best_pos ].len = strlen( str ); + vg_console.suggestions[ best_pos ].lev_score = score; + + if( vg_console.suggestion_count < + vg_list_size( vg_console.suggestions ) ) + vg_console.suggestion_count ++; + } +} + +VG_STATIC void console_update_suggestions(void) +{ + vg_console.suggestion_count = 0; + vg_console.suggestion_select = -1; + vg_console.suggestion_maxlen = 0; + + /* + * - must be typing something + * - must be at the end + * - prev char must not be a whitespace + * - cursors should match + */ + + if( vg_console.cursor_pos == 0 ) + return; + + if( vg_console.cursor_pos != vg_console.cursor_user ) + return; + + if( vg_console.input[ vg_console.cursor_pos ] != '\0' ) + return; + + if( (vg_console.input[ vg_console.cursor_pos -1 ] == ' ') || + (vg_console.input[ vg_console.cursor_pos -1 ] == '\t') ) + return; + + char temp[128]; + const char *args[8]; + + int token_count = vg_console_tokenize( vg_console.input, temp, args ); + + vg_console.suggestion_pastepos = args[token_count-1]-temp; + + /* Score all our commands and cvars */ + if( token_count == 1 ) + { + for( int i=0; iname, args[0], 1 ); + } + + for( int i=0; iname, args[0], 1 ); + } + } + else + { + vg_cmd *cmd = vg_console_match_cmd( args[0] ); + vg_var *var = vg_console_match_var( args[0] ); + + assert( !( cmd && var ) ); + + if( cmd ) + if( cmd->poll_suggest ) + cmd->poll_suggest( token_count-1, &args[1] ); + } + + /* some post processing */ + for( int i=0; i= vg_console.suggestion_count ) + vg_console.suggestion_select = -1; + + _console_fetch_suggestion(); + } +} + +VG_STATIC void _console_suggest_prev(void) +{ + if( vg_console.suggestion_count ) + { + _console_suggest_store_normal(); + + vg_console.suggestion_select --; + + if( vg_console.suggestion_select < -1 ) + vg_console.suggestion_select = vg_console.suggestion_count-1; + + _console_fetch_suggestion(); + } +} + +/* + * Handles binds + */ VG_STATIC void console_proc_key( SDL_Keysym ev ) { /* Open / close console */ @@ -655,7 +1008,7 @@ VG_STATIC void console_proc_key( SDL_Keysym ev ) } if( !vg_console.enabled ) return; - + struct console_mapping { u16 mod; @@ -663,7 +1016,7 @@ VG_STATIC void console_proc_key( SDL_Keysym ev ) void (*handler)(void); } - mapping[] = + mappings[] = { { 0, SDLK_LEFT, _console_left }, { KMOD_SHIFT, SDLK_LEFT, _console_left_select }, @@ -681,32 +1034,48 @@ VG_STATIC void console_proc_key( SDL_Keysym ev ) { KMOD_CTRL, SDLK_c, console_to_clipboard }, { KMOD_CTRL, SDLK_x, _console_cut }, { KMOD_CTRL, SDLK_v, console_clipboard_paste }, - { 0, SDLK_RETURN, _console_enter } + { 0, SDLK_RETURN, _console_enter }, + { KMOD_CTRL, SDLK_n, _console_suggest_next }, + { KMOD_CTRL, SDLK_p, _console_suggest_prev } }; - for( int i=0; ikey == ev.sym ) + if( mapping->key == ev.sym ) { - if( mk->mod == 0 ) + if( mapping->mod == 0 ) { - if( ev.mod == 0 ) + if( mod == 0 ) { - mk->handler(); + mapping->handler(); return; } } - else if( (ev.mod & mk->mod) == mk->mod ) + else if( (mod & mapping->mod) == mapping->mod ) { - mk->handler(); + mapping->handler(); return; } } } } +/* + * Callback for text entry mode + */ VG_STATIC void console_proc_utf8( const char *text ) { const char *ptr = text; @@ -717,6 +1086,8 @@ VG_STATIC void console_proc_utf8( const char *text ) console_put_char( *ptr ); ptr ++; } + + console_update_suggestions(); } #endif /* VG_CONSOLE_H */