k_convar_dtype_f32
}
data_type;
+
+ union
+ {
+ struct
+ {
+ int min, max, clamp;
+ } opt_i32;
+ };
}
*convars;
char lines[16][512];
u32 current, len;
+
+ char input[512];
+ int cursor_user, cursor_pos, string_length;
+
+ char history[32][512];
+ int history_last, history_pos, history_count;
+
+ int enabled;
+ int scale;
}
-vg_console;
+vg_console = { .scale = 1 };
+
+static int vg_console_enabled(void) { return vg_console.enabled; }
static void vg_convar_push( struct vg_convar cv )
{
}
static void vg_console_draw( void )
-{
+{
+ if( !vg_console.enabled )
+ return;
+
int ptr = vg_console.current - vg_console.len;
if( ptr <= 0 )
ptr += vg_list_size( vg_console.lines );
ui_global_ctx.cursor[0] = 0;
ui_global_ctx.cursor[1] = 0;
- ui_global_ctx.cursor[3] = vg_console.len*8;
+ ui_global_ctx.cursor[3] = vg_console.len*8*vg_console.scale;
ui_fill_x( &ui_global_ctx );
ui_new_node( &ui_global_ctx );
{
ui_fill_rect( &ui_global_ctx, ui_global_ctx.cursor, 0x77333333 );
- ui_global_ctx.cursor[3] = 8;
+ ui_global_ctx.cursor[3] = 8*vg_console.scale;
ui_align_bottom( &ui_global_ctx );
for( int i = 0; i < vg_console.len; i ++ )
{
- ui_text( &ui_global_ctx, vg_console.lines[ptr], 1, 0 );
- ui_global_ctx.cursor[1] -= 8;
+ ui_text( &ui_global_ctx, vg_console.lines[ptr], vg_console.scale, 0 );
+ ui_global_ctx.cursor[1] -= 8*vg_console.scale;
ptr --;
if( ptr < 0 )
}
}
ui_end_down( &ui_global_ctx );
+
+ ui_global_ctx.cursor[1] += 2;
+ ui_global_ctx.cursor[3] = 8*vg_console.scale;
+
+ ui_new_node( &ui_global_ctx );
+ {
+ ui_fill_rect( &ui_global_ctx, ui_global_ctx.cursor, 0x77333333 );
+
+ ui_text( &ui_global_ctx, vg_console.input, vg_console.scale, 0 );
+
+ int start = VG_MIN( vg_console.cursor_pos, vg_console.cursor_user ),
+ end = VG_MAX( vg_console.cursor_pos, vg_console.cursor_user );
+
+ ui_global_ctx.cursor[0] = start * 6 * vg_console.scale;
+ ui_global_ctx.cursor[2] = (start == end? 1: (end-start)) * 6 * vg_console.scale;
+
+ ui_fill_rect( &ui_global_ctx, ui_global_ctx.cursor, 0x66ffffff );
+ }
+ ui_end_down( &ui_global_ctx );
}
void vg_console_println( const char *str )
static void vg_console_init(void)
{
vg_log_callback = vg_console_println;
+
+ vg_convar_push( (struct vg_convar)
+ { .name = "console_scale", .data = &vg_console.scale, .data_type = k_convar_dtype_i32,
+ .opt_i32 = { .clamp = 1, .min = 1, .max = 7 } } );
}
static void vg_console_free(void)
{
arrfree( vg_console.convars );
}
+
+// Returns advance amount
+static int console_token_read( char *buf, int buf_len, const char *src )
+{
+ int wait_for_next = 0;
+
+ for( int i = 0; i < buf_len; i ++ )
+ {
+ if( src[i] )
+ {
+ if( isspace( src[i] ) )
+ {
+ buf[i] = '\0';
+ wait_for_next = 1;
+ }
+ else
+ {
+ if( wait_for_next )
+ return i;
+
+ buf[i] = src[i];
+ }
+ }
+ else
+ {
+ buf[i] = '\0';
+ return i;
+ }
+ }
+
+ return 0;
+}
+
+static void execute_console_input( const char *cmd )
+{
+ char temp[512];
+ int cmd_pos = console_token_read( temp, 512, cmd );
+
+ int data_int;
+
+ for( int i = 0; i < arrlen( vg_console.convars ); i ++ )
+ {
+ struct vg_convar *cv = &vg_console.convars[ i ];
+ if( !strcmp( cv->name, temp ) )
+ {
+ // Matched, try get value
+ if( console_token_read( temp, 512, cmd+cmd_pos ) )
+ {
+ switch( cv->data_type )
+ {
+ case k_convar_dtype_u32:
+ case k_convar_dtype_i32:
+
+ data_int = atoi( temp );
+ *((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: *((float *)cv->data) = atof( temp ); break;
+ }
+ }
+ 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;
+ }
+ }
+
+ vg_error( "No command/variable named '%s'\n", temp );
+}
+
+// =============================================================================================================================
+// Console interface
+
+static void console_make_selection( int* start, int* end )
+{
+ *start = VG_MIN( vg_console.cursor_pos, vg_console.cursor_user );
+ *end = VG_MAX( vg_console.cursor_pos, vg_console.cursor_user );
+}
+
+static void console_move_cursor( int* cursor0, int* cursor1, int dir, int snap_together )
+{
+ *cursor0 = VG_MAX( 0, vg_console.cursor_user + dir );
+ *cursor0 = VG_MIN( VG_MIN( vg_list_size( vg_console.input ), strlen( vg_console.input )), *cursor0 );
+ if( snap_together )
+ *cursor1 = *cursor0;
+}
+
+static int console_makeroom( int datastart, int length )
+{
+ int move_to = VG_MIN( datastart+length, vg_list_size( vg_console.input ) );
+ int move_amount = strlen( vg_console.input )-datastart;
+ int move_end = VG_MIN( move_to+move_amount, vg_list_size( vg_console.input ) );
+ move_amount = move_end-move_to;
+
+ if( move_amount )
+ memmove( &vg_console.input[ move_to ], &vg_console.input[ datastart ], move_end-move_to );
+
+ vg_console.input[ move_end ] = '\0';
+
+ return VG_MIN( length, vg_list_size( vg_console.input )-datastart );
+}
+
+static int console_delete_char( int direction )
+{
+ int start, end;
+ console_make_selection( &start, &end );
+
+ // There is no selection
+ if( !(end-start) )
+ {
+ if( direction == 1 ) end = VG_MIN( end+1, strlen( vg_console.input ) );
+ else if( direction == -1 ) start = VG_MAX( start-1, 0 );
+ }
+
+ // Still no selction, no need to do anything
+ if( !(end-start) )
+ return start;
+
+ // Copy the end->terminator to start
+ int remaining_length = strlen( vg_console.input )+1-end;
+ memmove( &vg_console.input[ start ], &vg_console.input[ end ], remaining_length );
+ return start;
+}
+
+static void console_to_clipboard(void)
+{
+ int start, end;
+ console_make_selection( &start, &end );
+ char buffer[512];
+
+ if( end-start )
+ {
+ memcpy( buffer, &vg_console.input[ start ], end-start );
+ buffer[ end-start ] = 0x00;
+ glfwSetClipboardString( NULL, buffer );
+ }
+}
+
+static void console_clipboard_paste(void)
+{
+ int datastart = console_delete_char(0);
+ const char* clipboard = glfwGetClipboardString(NULL);
+ int length = strlen(clipboard);
+
+ int cpylength = console_makeroom(datastart, length);
+
+ memcpy( vg_console.input + datastart, clipboard, cpylength);
+
+ console_move_cursor( &vg_console.cursor_user, &vg_console.cursor_pos, cpylength, 1 );
+}
+
+static void console_put_char( char c )
+{
+ if( !vg_console.enabled )
+ return;
+
+ vg_console.cursor_user = console_delete_char(0);
+
+ if( console_makeroom( vg_console.cursor_user, 1 ) )
+ vg_console.input[ vg_console.cursor_user ] = c;
+
+ console_move_cursor( &vg_console.cursor_user, &vg_console.cursor_pos, 1, 1 );
+}
+
+static void console_add_to_history( const char* str )
+{
+ if( strcmp( str, vg_console.history[ vg_console.history_last ]) )
+ {
+ vg_console.history_last = ( vg_console.history_last + 1) % vg_list_size(vg_console.history );
+ vg_console.history_count = VG_MIN( vg_list_size( vg_console.history ), vg_console.history_count + 1 );
+ strcpy( vg_console.history[ vg_console.history_last ], str );
+ }
+}
+
+static void console_history_get( char* buf, int entry_num )
+{
+ if( !vg_console.history_count )
+ return;
+
+ int pick = (vg_console.history_last - VG_MIN( entry_num, vg_console.history_count -1 )) % vg_list_size( vg_console.history );
+ strcpy( buf, vg_console.history[ pick ] );
+}
+
+static void console_proc_key( GLFWwindow* ptrW, int key, int scancode, int action, int mods )
+{
+ if( action )
+ {
+ int cursor_diff = vg_console.cursor_pos - vg_console.cursor_user? 0: 1;
+
+ if( key == GLFW_KEY_GRAVE_ACCENT )
+ {
+ vg_console.enabled = !vg_console.enabled;
+ return;
+ }
+
+ if( !vg_console.enabled )
+ return;
+
+ if( key == GLFW_KEY_LEFT )
+ {
+ if( mods & GLFW_MOD_SHIFT ) // Receed secondary cursor
+ {
+ console_move_cursor( &vg_console.cursor_user, NULL, -1, 0 );
+ }
+ else // Match and receed both cursors
+ {
+ console_move_cursor( &vg_console.cursor_user, &vg_console.cursor_pos, -cursor_diff, 1 );
+ }
+ }
+ else if( key == GLFW_KEY_RIGHT ) // Advance secondary cursor
+ {
+ if( mods & GLFW_MOD_SHIFT )
+ {
+ console_move_cursor( &vg_console.cursor_user, NULL, 1, 0 );
+ }
+ else // Match and advance both cursors
+ {
+ console_move_cursor( &vg_console.cursor_user, &vg_console.cursor_pos, +cursor_diff, 1 );
+ }
+ }
+ else if( key == GLFW_KEY_DOWN )
+ {
+ if( mods & GLFW_MOD_SHIFT ){}
+ else
+ {
+ vg_console.history_pos = VG_MAX( 0, vg_console.history_pos-1 );
+ console_history_get( vg_console.input, vg_console.history_pos );
+ console_move_cursor( &vg_console.cursor_user, &vg_console.cursor_pos, vg_list_size( vg_console.input ), 1 );
+ }
+ }
+ else if( key == GLFW_KEY_UP )
+ {
+ if( mods & GLFW_MOD_SHIFT ){}
+ else
+ {
+ vg_console.history_pos = VG_MAX
+ (
+ 0,
+ VG_MIN
+ (
+ vg_console.history_pos+1,
+ VG_MIN
+ (
+ vg_list_size( vg_console.history ),
+ vg_console.history_count - 1
+ )
+ )
+ );
+
+ console_history_get( vg_console.input, vg_console.history_pos );
+ console_move_cursor( &vg_console.cursor_user, &vg_console.cursor_pos, vg_list_size( vg_console.input ), 1);
+ }
+ }
+ else if( key == GLFW_KEY_BACKSPACE ) // Lookback delete
+ {
+ vg_console.cursor_user = console_delete_char( -1 );
+ vg_console.cursor_pos = vg_console.cursor_user;
+ }
+ else if( key == GLFW_KEY_DELETE ) // Lookforward delete
+ {
+ vg_console.cursor_user = console_delete_char( 1 );
+ vg_console.cursor_pos = vg_console.cursor_user;
+ }
+ else if( key == GLFW_KEY_HOME ) // Home key
+ {
+ if( mods & GLFW_MOD_SHIFT )
+ console_move_cursor( &vg_console.cursor_user, NULL, -10000, 0 );
+ else
+ console_move_cursor( &vg_console.cursor_user, &vg_console.cursor_pos, -10000, 1 );
+ }
+ else if( key == GLFW_KEY_END ) // End key
+ {
+ if( mods & GLFW_MOD_SHIFT )
+ console_move_cursor( &vg_console.cursor_user, NULL, 10000, 0 );
+ else
+ console_move_cursor( &vg_console.cursor_user, &vg_console.cursor_pos, vg_list_size( vg_console.input ), 1 );
+ }
+ else if( key == GLFW_KEY_A )
+ {
+ if( mods & GLFW_MOD_CONTROL ) // Select all
+ {
+ console_move_cursor( &vg_console.cursor_user, NULL, 10000, 0);
+ console_move_cursor( &vg_console.cursor_pos, NULL, -10000, 0);
+ }
+ }
+ else if( key == GLFW_KEY_C ) // Copy
+ {
+ if( mods & GLFW_MOD_CONTROL )
+ {
+ console_to_clipboard();
+ }
+ }
+ else if( key == GLFW_KEY_X ) // Cut
+ {
+ if( mods & GLFW_MOD_CONTROL )
+ {
+ console_to_clipboard();
+ vg_console.cursor_user = console_delete_char(0);
+ vg_console.cursor_pos = vg_console.cursor_user;
+ }
+ }
+ else if( key == GLFW_KEY_V ) // Paste
+ {
+ if( mods & GLFW_MOD_CONTROL )
+ {
+ console_clipboard_paste();
+ }
+ }
+ else if( key == GLFW_KEY_ENTER )
+ {
+ if( !strlen( vg_console.input ) )
+ return;
+
+ vg_info( "%s\n", vg_console.input );
+
+ console_add_to_history( vg_console.input );
+ vg_console.history_pos = -1;
+ execute_console_input( vg_console.input );
+ console_move_cursor( &vg_console.cursor_user, &vg_console.cursor_pos, -10000, 1 );
+ vg_console.input[0] = '\0';
+ }
+ }
+}
+
+// Handle an OS based input of UTF32 character from the keyboard or such
+static void console_proc_wchar( GLFWwindow* ptrW, u32 uWchar )
+{
+ //LOG_INFO("Recieved wchar: %u\n", uWchar);
+ if( uWchar <= 0x7F && (char)uWchar != 0x60)
+ {
+ console_put_char((char)uWchar);
+ }
+}