Platform and OS stability stuff
[vg.git] / src / vg / vg_console.h
index 07a90d2416813c16d693d9609aafda80c2e44c3a..fc2775ca49115efeb343b8207f5e70ba2e1ed592 100644 (file)
@@ -1,10 +1,17 @@
-// Copyright (C) 2021 Harry Godden (hgn) - All Rights Reserved
+/* Copyright (C) 2021-2022 Harry Godden (hgn) - All Rights Reserved */
+
+#ifndef VG_CONSOLE_H
+#define VG_CONSOLE_H
+
+#include "vg/vg_ui.h"
+#include "vg/vg_log.h"
 
 struct vg_console
 {
        struct vg_convar
        {
                void *data;
+      void (*update)(void);
                const char *name;
                
                enum vg_convar_dtype
@@ -20,10 +27,18 @@ struct vg_console
                        struct
                        {
                                int min, max, clamp;
-                       } opt_i32;
+                       } 
+         opt_i32;
+
+         struct
+         {
+            float min, max;
+            int clamp;
+         }
+         opt_f32;
                };
 
-               int persistent; // Should be stored to cfg/auto.conf
+               int persistent; /* Should this var be stored to cfg/auto.conf? */
        } 
        *convars;
        
@@ -33,14 +48,14 @@ struct vg_console
                const char *name;
        }
        *functions;
+
+   u32 convar_count, convar_cap,
+       function_count, function_cap;
        
-       char lines[16][512];
-       u32 current, len;
-       
-       char input[512];
+       char input[96];
        int cursor_user, cursor_pos, string_length;
        
-       char history[32][512];
+       char history[32][96];
        int history_last, history_pos, history_count;
        
        int enabled;
@@ -48,10 +63,6 @@ struct vg_console
 }
 vg_console = { .scale = 1 };
 
-// Declerations
-// ------------
-
-// Registration
 static void vg_convar_push( struct vg_convar cv );
 static void vg_function_push( struct vg_cmd cmd );
 
@@ -63,24 +74,26 @@ static void vg_console_write_persistent(void);
 static void vg_console_free(void);
 static void execute_console_input( const char *cmd );
 
-// Console interface
-// -----------------
-
+/*
+ * Console interface
+ */
 static void console_make_selection( int* start, int* end );
-static void console_move_cursor( int* cursor0, int* cursor1, int dir, int snap_together );
+static void console_move_cursor( int* cursor0, int* cursor1, 
+      int dir, int snap_together );
 static int console_makeroom( int datastart, int length );
 static int console_delete_char( int direction );
 static void console_to_clipboard(void);
 static void console_clipboard_paste(void);
 static void console_put_char( char c );
 static void console_history_get( char* buf, int entry_num );
-static void console_proc_key( GLFWwindow* ptrW, int key, int scancode, int action, int mods );
+static void console_proc_key( GLFWwindow* ptrW, int key, 
+      int scancode, int action, int mods );
 static void console_proc_wchar( GLFWwindow* ptrW, u32 uWchar );
 static int vg_console_enabled(void);
 
-// =========================================================================================================
-// Implementation
-
+/*
+ * Implementation
+ */
 static int vg_console_enabled(void) 
 { 
        return vg_console.enabled; 
@@ -88,12 +101,24 @@ static int vg_console_enabled(void)
 
 static void vg_convar_push( struct vg_convar cv )
 {
-       arrpush( vg_console.convars, cv ); 
+   vg_info( "Console variable '%s' registered\n", cv.name );
+   vg_console.convars = buffer_reserve( vg_console.convars, 
+                                        vg_console.convar_count,
+                                        &vg_console.convar_cap, 1,
+                                        sizeof( struct vg_convar ) );
+
+   vg_console.convars[ vg_console.convar_count ++ ] = cv;
 }
 
 static void vg_function_push( struct vg_cmd cmd )
 {
-       arrpush( vg_console.functions, cmd );
+   vg_info( "Console command '%s' registered\n", cmd.name );
+   vg_console.functions = buffer_reserve( vg_console.functions,
+                                          vg_console.function_count,
+                                          &vg_console.function_cap, 1,
+                                          sizeof( struct vg_cmd ) );
+
+   vg_console.functions[ vg_console.function_count ++ ] = cmd;
 }
 
 static void vg_console_draw( void )
@@ -101,13 +126,15 @@ static void vg_console_draw( void )
        if( !vg_console.enabled )
                return;
 
-       int ptr = vg_console.current-1;
+   vg_mutex_lock( &log_print_mutex );
 
+       int ptr = vg_log.buffer_line_current;
    int const fh = 14;
+   int console_lines = VG_MIN( 16, vg_log.buffer_line_count );
        
        ui_global_ctx.cursor[0] = 0;
        ui_global_ctx.cursor[1] = 0;
-       ui_global_ctx.cursor[3] = vg_list_size( vg_console.lines )*fh*vg_console.scale;
+       ui_global_ctx.cursor[3] = 16*fh*vg_console.scale;
        ui_fill_x( &ui_global_ctx );
        
        ui_new_node( &ui_global_ctx );
@@ -116,17 +143,19 @@ static void vg_console_draw( void )
        
                ui_global_ctx.cursor[3] = fh*vg_console.scale;
                ui_align_bottom( &ui_global_ctx ); 
-               
-               for( int i = 0; i < vg_console.len; i ++ )
+
+               for( int i=0; i<console_lines; i ++ )
                {
+                       ptr --;
+
                        if( ptr < 0 )
-                               ptr = vg_list_size( vg_console.lines )-1;
+                               ptr = vg_list_size( vg_log.buffer )-1;
          
-                       ui_text( &ui_global_ctx, ui_global_ctx.cursor, vg_console.lines[ptr], vg_console.scale, 0 );
+                       ui_text( &ui_global_ctx, ui_global_ctx.cursor, 
+               vg_log.buffer[ptr], vg_console.scale, 0 );
                        ui_global_ctx.cursor[1] -= fh*vg_console.scale;
-               
-                       ptr --;
                }
+
        }
        ui_end_down( &ui_global_ctx );
        
@@ -137,33 +166,25 @@ static void vg_console_draw( void )
        {
                ui_fill_rect( &ui_global_ctx, ui_global_ctx.cursor, 0x77333333 );
                
-               ui_text( &ui_global_ctx, ui_global_ctx.cursor, vg_console.input, vg_console.scale, 0 );
+               ui_text( &ui_global_ctx, ui_global_ctx.cursor, 
+            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 * UI_GLYPH_SPACING_X * vg_console.scale);
-               ui_global_ctx.cursor[2] = (start == end? 0.5f: (float)(end-start)) * (float)UI_GLYPH_SPACING_X * (float)vg_console.scale;
+               ui_global_ctx.cursor[2] = (start == end? 0.5f: (float)(end-start)) 
+         * (float)UI_GLYPH_SPACING_X * (float)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 )
-{
-       if( vg_console.len < vg_list_size( vg_console.lines ) )
-               vg_console.len ++;
-       
-       strcpy( vg_console.lines[ vg_console.current ++ ], str );
-       
-       if( vg_console.current >= vg_list_size( vg_console.lines ) )
-               vg_console.current = 0;
+   vg_mutex_unlock( &log_print_mutex );
 }
 
 static int vg_console_list( int argc, char const *argv[] )
 {
-       for( int i = 0; i < arrlen( vg_console.functions ); i ++ )
+       for( int i=0; i<vg_console.function_count; i ++ )
        {
                struct vg_cmd *cmd = &vg_console.functions[ i ];
                vg_info( "* %s\n", cmd->name );
@@ -171,7 +192,7 @@ static int vg_console_list( int argc, char const *argv[] )
        
        vg_info( "* snowsound\n" );
        
-       for( int i = 0; i < arrlen( vg_console.convars ); i ++ )
+       for( int i=0; i<vg_console.convar_count; i ++ )
        {
                struct vg_convar *cv = &vg_console.convars[ i ];
                vg_info( "%s\n", cv->name );
@@ -182,31 +203,58 @@ static int vg_console_list( int argc, char const *argv[] )
 
 static int vg_console_chartest( int argc, char const *argv[] )
 {
+vg_info(" Copyright  .        . .       -----, ,----- ,---.   .---.  " );
+vg_info(" 2021-2022  |\\      /| |           /  |      |    | |    /| " );
+vg_info("            | \\    / | +--        /   +----- +---'  |   / | " );
+vg_info("            |  \\  /  | |         /    |      |   \\  |  /  | " );
+vg_info("            |   \\/   | |        /     |      |    \\ | /   | " );
+vg_info("            '        ' '--' [] '----- '----- '     ' '---'  " 
+        "SOFTWARE" );
+
    vg_info( "\"THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG\"\n" );
    vg_info( "'the quick brown fox jumps over the lazy dog'\n" );
    vg_info( ":;!@#$%^& 0123456789 +-*/=~ ()[]{}<>\n" );
    return 0;
 }
 
+static int _test_break( int argc, const char *argv[] )
+{
+   vg_fatal_exit_loop( "Test crash from main, after loading (console)" );
+   return 0;
+}
+
 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 } } );
+       { 
+      .name = "console_scale", .data = &vg_console.scale, 
+      .data_type = k_convar_dtype_i32, 
+               .opt_i32 = { .clamp = 1, .min = 1, .max = 7 },
+      .update = NULL
+   });
        
-       vg_function_push( (struct vg_cmd){
+       vg_function_push( (struct vg_cmd)
+   {
                .name = "list",
                .function = vg_console_list
        });
 
-   vg_function_push( (struct vg_cmd){
+   vg_function_push( (struct vg_cmd)
+   {
       .name = "chartest",
       .function = vg_console_chartest
    });
 
-       // Read and exec persistent commands
+   vg_function_push( (struct vg_cmd)
+   {
+      .name = "crash",
+      .function = _test_break
+   });
+}
+
+static void vg_console_load_autos(void)
+{
+       /* Read and exec persistent commands */
        FILE *fp = fopen( "cfg/auto.conf", "r" );
        if( fp )
        {
@@ -230,7 +278,7 @@ static void vg_console_write_persistent(void)
 {
        FILE *fp = fopen( "cfg/auto.conf", "w" );
        
-       for( int i = 0; i < arrlen( vg_console.convars ); i ++ )
+       for( int i=0; i<vg_console.convar_count; i ++ )
        {
                struct vg_convar *cv = &vg_console.convars[i];
 
@@ -258,8 +306,8 @@ static void vg_console_free(void)
 {
        vg_console_write_persistent();
 
-       arrfree( vg_console.convars );
-       arrfree( vg_console.functions );
+   vg_free( vg_console.convars );
+   vg_free( vg_console.functions );
 }
 
 static void execute_console_input( const char *cmd )
@@ -270,12 +318,12 @@ static void execute_console_input( const char *cmd )
        
        int in_token = 0;
        
-       // Split string into tokens
+       /* Split string into tokens */
        for( int i = 0; i < vg_list_size( temp ); i ++ )
        {
                if( cmd[i] )
                {
-                       if( isspace( cmd[i] ) )
+                       if( cmd[i] == ' ' || cmd[i] == '\t' )
                        {
                                temp[i] = '\0';
                                in_token = 0;
@@ -305,13 +353,14 @@ static void execute_console_input( const char *cmd )
                return;
        
        int data_int;
+   float data_float;
        
-       for( int i = 0; i < arrlen( vg_console.convars ); i ++ )
+       for( int i=0; i<vg_console.convar_count; i ++ )
        {
                struct vg_convar *cv = &vg_console.convars[ i ];
                if( !strcmp( cv->name, args[0] ) )
                {
-                       // Cvar Matched, try get value
+                       /* Cvar Matched, try get value */
                        if( arg_count >= 2 )
                        {
                                switch( cv->data_type )
@@ -320,19 +369,36 @@ static void execute_console_input( const char *cmd )
                                        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;
+                  
+                                               *((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;
+                                       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;
                                }
+
+            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;
+                                       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;
                                }
                        }
                
@@ -340,34 +406,40 @@ static void execute_console_input( const char *cmd )
                }
        }
        
-       // Try commands
-       for( int i = 0; i < arrlen( vg_console.functions ); i ++ )
+   /*
+    * Find and excecute command
+    */
+       for( int i=0; i<vg_console.function_count; i ++ )
        {
                struct vg_cmd *cmd = &vg_console.functions[ i ];
                if( !strcmp( cmd->name, args[0] ) )
                {
-                       // Call function
                        cmd->function( arg_count-1, args+1 );
                        return;
                }
        }
        
-       vg_error( "No command/variable named '%s'. Use 'list' to view all\n", args[0] );
+       vg_error( "No command/var named '%s'. Use 'list' to view all\n", args[0] );
 }
 
-// =============================================================================================================================
-// Console interface
-
+/*
+ * 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 )
+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 );
+       *cursor0 = 
+      VG_MIN( 
+         VG_MIN( vg_list_size( vg_console.input ), strlen( vg_console.input )), 
+      *cursor0 );
+
        if( snap_together ) 
                *cursor1 = *cursor0;
 }
@@ -376,11 +448,14 @@ 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 ) );
+       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 );
+               memmove( &vg_console.input[ move_to ], 
+               &vg_console.input[ datastart ], 
+               move_end-move_to );
        
        vg_console.input[ move_end ] = '\0';
        
@@ -392,20 +467,22 @@ static int console_delete_char( int direction )
        int start, end;
        console_make_selection( &start, &end );
        
-       // There is no selection
+       /* 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
+       /* Still no selction, no need to do anything */
        if( !(end-start) ) 
                return start;
        
-       // Copy the end->terminator to 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 );
+       memmove( &vg_console.input[ start ], 
+            &vg_console.input[ end ], 
+            remaining_length );
        return start;
 }
 
@@ -432,8 +509,8 @@ static void console_clipboard_paste(void)
        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 );
+       console_move_cursor( &vg_console.cursor_user, 
+                        &vg_console.cursor_pos, cpylength, 1 );
 }
 
 static void console_put_char( char c )
@@ -454,163 +531,214 @@ 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 );
+   int offset = VG_MIN( entry_num, vg_console.history_count -1 ),
+       pick = (vg_console.history_last - offset) % 
+               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 )
+/* Receed secondary cursor */
+static void _console_left_select(void)
 {
-       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;
+   console_move_cursor( &vg_console.cursor_user, NULL, -1, 0 );
+}
+
+/* Match and receed both cursors */
+static void _console_left(void)
+{
+   int cursor_diff = vg_console.cursor_pos - vg_console.cursor_user? 0: 1;
+
+   console_move_cursor( &vg_console.cursor_user, 
+                        &vg_console.cursor_pos, -cursor_diff, 1 );
+}
+
+static void _console_right_select(void)
+{
+   console_move_cursor( &vg_console.cursor_user, NULL, 1, 0 );
+}
+
+static void _console_right(void)
+{
+   int cursor_diff = vg_console.cursor_pos - vg_console.cursor_user? 0: 1;
+
+   console_move_cursor( &vg_console.cursor_user, 
+                        &vg_console.cursor_pos, +cursor_diff, 1 );
+}
+
+static void _console_down(void)
+{
+   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 );
+}
+
+static void _console_up(void)
+{
+   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);
+}
+
+static void _console_backspace(void)
+{
+   vg_console.cursor_user = console_delete_char( -1 );
+   vg_console.cursor_pos = vg_console.cursor_user;
+}
+
+static void _console_delete(void)
+{
+   vg_console.cursor_user = console_delete_char( 1 );
+   vg_console.cursor_pos = vg_console.cursor_user;
+}
+
+static void _console_home_select(void)
+{
+   console_move_cursor( &vg_console.cursor_user, NULL, -10000, 0 );
+}
+
+static void _console_home(void)
+{
+   console_move_cursor( &vg_console.cursor_user, 
+                        &vg_console.cursor_pos, -10000, 1 );
+}
+
+static void _console_end_select(void)
+{
+   console_move_cursor( &vg_console.cursor_user, NULL, 10000, 0 );
+}
+
+static void _console_end(void)
+{
+   console_move_cursor( &vg_console.cursor_user, 
+                        &vg_console.cursor_pos, 
+                        vg_list_size( vg_console.input ), 1 );
+}
+
+static void _console_select_all(void)
+{
+   console_move_cursor( &vg_console.cursor_user, NULL, 10000, 0);
+   console_move_cursor( &vg_console.cursor_pos, NULL, -10000, 0);
+}
+
+static void _console_cut(void)
+{
+   console_to_clipboard();
+   vg_console.cursor_user = console_delete_char(0);
+   vg_console.cursor_pos = vg_console.cursor_user;
+}
+
+static void _console_enter(void)
+{
+   if( !strlen( vg_console.input ) ) 
+      return;
+
+   vg_info( "%s\n", vg_console.input );
+
+   if( strcmp( vg_console.input, 
+            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 ], 
+              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';
+}
+
+static void console_proc_key( GLFWwindow* ptrW, int key, int scancode, 
+                              int action, int mods )
+{
+       if( !action )
+      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 );
-                       
-                       if( strcmp( vg_console.input, 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 ], 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';
-               }
-       }
+   /* Open / close console */
+   if( key == GLFW_KEY_GRAVE_ACCENT )
+      vg_console.enabled = !vg_console.enabled;
+   
+   if( !vg_console.enabled ) return;
+   
+   struct console_mapping
+   {
+      u32 mod, key;
+      void (*handler)(void);
+   }
+   mapping[] =
+   {
+      { 0,                 GLFW_KEY_LEFT,       _console_left              },
+      { GLFW_MOD_SHIFT,    GLFW_KEY_LEFT,       _console_left_select       },
+      { 0,                 GLFW_KEY_RIGHT,      _console_right             },
+      { GLFW_MOD_SHIFT,    GLFW_KEY_RIGHT,      _console_right_select      },
+      { 0,                 GLFW_KEY_DOWN,       _console_down              },
+      { 0,                 GLFW_KEY_UP,         _console_up                },
+      { 0,                 GLFW_KEY_BACKSPACE,  _console_backspace         },
+      { 0,                 GLFW_KEY_DELETE,     _console_delete            },
+      { 0,                 GLFW_KEY_HOME,       _console_home              },
+      { GLFW_MOD_SHIFT,    GLFW_KEY_HOME,       _console_home_select       },
+      { 0,                 GLFW_KEY_END,        _console_end               },
+      { GLFW_MOD_SHIFT,    GLFW_KEY_END,        _console_end_select        },
+      { GLFW_MOD_CONTROL,  GLFW_KEY_A,          _console_select_all        },
+      { GLFW_MOD_CONTROL,  GLFW_KEY_C,          console_to_clipboard       },
+      { GLFW_MOD_CONTROL,  GLFW_KEY_X,          _console_cut               },
+      { GLFW_MOD_CONTROL,  GLFW_KEY_V,          console_clipboard_paste    },
+      { 0,                 GLFW_KEY_ENTER,      _console_enter             }
+   };
+
+   for( int i=0; i<vg_list_size( mapping ); i++ )
+   {
+      struct console_mapping *mk = &mapping[i];
+
+      if( mk->key == key )
+      {
+         if( mk->mod == 0 )
+         {
+            if( mods == 0 )
+            {
+               mk->handler();
+               return;
+            }
+         }
+         else if( (mods & mk->mod) == mk->mod )
+         {
+            mk->handler();
+            return;
+         }
+      }
+   }
 }
 
-// Handle an OS based input of UTF32 character from the keyboard or such
+/* 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);
        }
 }
+
+#endif /* VG_CONSOLE_H */