shaders, graphics/ui api, kv fixes, terminal entry
authorhgn <hgodden00@gmail.com>
Fri, 17 Oct 2025 14:21:35 +0000 (15:21 +0100)
committerhgn <hgodden00@gmail.com>
Fri, 17 Oct 2025 14:21:35 +0000 (15:21 +0100)
13 files changed:
foundation.kv
include/common_api.h
include/engine_backend.h
include/graphics_api.h
include/input_api.h
include/opengl.h
source/engine/input.c
source/engine/ui.c
source/foundation/allocator_stretchy.c
source/foundation/keyvalues.c
source/graphics/graphics_software.c
source/graphics/ui.c
source/terminal_main.c [new file with mode: 0644]

index 96319dbee874784fd39944d18a692e4a663efaef..42eaefb1eba1a4c83eebee3f85bb15872b2c4c4b 100644 (file)
@@ -44,7 +44,7 @@ add source/foundation/temporary.c
 
 {
    if terminal
-   add source/terminal/main.c
+   add source/terminal_main.c
 }
 
 {
index 0657af8142ec045c452b1162f09509074dadeb31..4a18eae6a5269fa6fdbaccc7fb14c34a3af5abf9 100644 (file)
@@ -1,8 +1,5 @@
 /* Voyager common application interface */
 
-#define API
-#define IMPL
-
 #define VG_PRE_MAIN \
    _exit_init(); \
    _log_init(); \
@@ -26,6 +23,7 @@ typedef               double f64;
 typedef unsigned        char bool;
 
 #define NULL 0
+
 #define BYTES_KB( X ) X*1024
 #define BYTES_MB( X ) X*1024*1024
 #define BYTES_GB( X ) X*1024*1024*1024
@@ -337,7 +335,7 @@ u32 keyvalues_type( struct keyvalues *kvs, u32 kv_offset );
 c8 *keyvalues_key( struct keyvalues *kvs, u32 kv_offset, u32 *out_length );
 c8 *keyvalues_value( struct keyvalues *kvs, u32 kv_offset, u32 *out_length );
 
-u32 keyvalues_get( struct keyvalues *kvs, u32 root_offset, const c8 *key );
+u32 keyvalues_get( struct keyvalues *kvs, u32 root_offset, const c8 *key, bool relative );
 u32 keyvalues_get_next( struct keyvalues *kvs, u32 kv_offset );
 u32 keyvalues_get_child( struct keyvalues *kvs, u32 root_offset, u32 index );
 
@@ -345,6 +343,7 @@ const c8 *keyvalues_read_string( struct keyvalues *kvs, u32 root_offset, const c
 bool keyvalues_read_i32s( struct keyvalues *kvs, u32 root_offset, const c8 *key, i32 *default_values, i32 *out_values, u32 len );
 bool keyvalues_read_u32s( struct keyvalues *kvs, u32 root_offset, const c8 *key, u32 *default_values, u32 *out_values, u32 len );
 bool keyvalues_read_f32s( struct keyvalues *kvs, u32 root_offset, const c8 *key, f32 *default_values, f32 *out_values, u32 len );
+u32 keyvalues_append_frame( struct keyvalues *kvs, u32 parent_offset, const c8 *key );
 u32 keyvalues_append_string( struct keyvalues *kvs, u32 parent_offset, const c8 *key, const c8 *value );
 u32 keyvalues_append_i32s( struct keyvalues *kvs, u32 parent_offset, const c8 *key, i32 *values, u32 len );
 u32 keyvalues_append_u32s( struct keyvalues *kvs, u32 parent_offset, const c8 *key, u32 *values, u32 len );
index 887fb48389f0748a2ec6afb926399826c5e6b85e..7f1621a3f333cb217b72685c4c5ad85a7194c080 100644 (file)
@@ -1,7 +1,7 @@
-IMPL void _engine_console_ui(void);
-IMPL void _engine_console_init(void);
+void _engine_console_ui(void);
+void _engine_console_init(void);
 
-IMPL void _engine_ui_init(void);
-IMPL void _engine_ui_pre_render(void);
-IMPL void _engine_ui_post_render(void);
-IMPL void _engine_ui_input_character( u32 codepoint );
+void _engine_ui_init(void);
+void _engine_ui_pre_render(void);
+void _engine_ui_post_render(void);
+void _engine_ui_input_character( u32 codepoint );
index 57f52650e3eeec19a0b0d4e5f800d56ae8c4680a..e66096e50d8fe918d95c06df94537d8e11d4b8d5 100644 (file)
@@ -27,6 +27,8 @@ union colour
 };
 #pragma pack(pop)
 
+union colour graphics_lerp_colour( union colour c0, union colour c1, i32 d, i32 t );
+
 enum blending_mode
 {
    k_blending_mode_mix_alpha,
@@ -46,6 +48,7 @@ void _graphics_push_blendmode( enum blending_mode mode );
 void _graphics_fill_rect( i16 rect[4], union colour colour );
 void _graphics_line_rect( i16 rect[4], union colour colour );
 void _graphics_line( i16 p0[2], i16 p1[2], union colour colour );
+void _graphics_line2( i16 p0[2], i16 p1[2], union colour c0, union colour c1 );
 void _graphics_line_triangle( i16 p0[2], i16 p1[2], i16 p2[2], union colour colour );
 void _graphics_fill_triangle( i16 in_p0[2], i16 in_p1[2], i16 in_p2[2], union colour colour );
 void _graphics_glyph( i16 co[2], u32 glyph, i16 scale, union colour colour );
@@ -92,6 +95,7 @@ bool _font_decode_bitmap( i16 uv[2] );
 void _ui_set_mouse( i16 x, i16 y );
 void _ui_get_mouse_co( i16 out_co[2] );
 void _ui_input_text( const c8 *text );
+bool _ui_want_mouse( i16 area[4] );
 
 #define UI_PADDING_PX     8
 #define UI_ROW_PADDING_PX 18
index 5af31f637bcf04ff90d0f3d77bb72b4c708262d8..e4f21dd261c555d7fff6ba624a16ff6a321b75e3 100644 (file)
@@ -29,5 +29,6 @@ u8 _input_button( enum input_id id );
 
 void _input_layer_whitelist( u32 whitelist );
 bool _input_layer_filter( u32 mask );
+void _input_string( struct stream *stream, enum input_id id );
 
 #include "generated/input.h"
index b04c4972718348e46165a358d080141f323a07c1..8e7dac3c7e0a761c605db7194f4989f91ac68da8 100644 (file)
@@ -1,7 +1,7 @@
 #include "vg/dep/glad.4.3/glad/glad.h"
 #include "generated/shaders.h"
 
-API GLuint compile_opengl_subshader( GLint type, const c8 *sources[], u32 source_count, bool critical, const c8 *debug_path );
-API bool link_opengl_program( GLuint program, bool critical );
+GLuint compile_opengl_subshader( GLint type, const c8 *sources[], u32 source_count, bool critical, const c8 *debug_path );
+bool link_opengl_program( GLuint program, bool critical );
 
 void _shader_bind( enum shader_id id );
index ba79bec5fd3873bdab6246df862c5ee5ba77007c..6c9c54f109da790fc0c2dd885e925a3dccedd21e 100644 (file)
@@ -413,3 +413,37 @@ void _input_layer_whitelist( u32 whitelist )
 {
    _whitelist = whitelist; 
 }
+
+void _input_string( struct stream *stream, enum input_id id )
+{
+   u32 matched = 0;
+   
+   for( u32 i=0; i<stretchy_count( &_bind_allocator ); i ++ )
+   {
+      struct bind *bind = stretchy_get( &_bind_allocator, i );
+      if( bind->input_index == id )
+      {
+         if( matched )
+            $v_string( stream, {" or "} );
+
+         matched ++;
+
+         for( u32 j=0; j<ARRAY_COUNT( _input_aliases ); j ++ )
+         {
+            struct input_alias *alias_j = &_input_aliases[j];
+            if( (alias_j->device_type == k_input_device_modifier) && (bind->modifiers & alias_j->key) )
+            {
+               $v_string( stream, {alias_j->alias}, {"+"} );
+            }
+         }
+         for( u32 j=0; j<ARRAY_COUNT( _input_aliases ); j ++ )
+         {
+            struct input_alias *alias_j = &_input_aliases[j];
+            if( (alias_j->device_type == bind->device) && (alias_j->key == bind->id) )
+            {
+               $v_string( stream, {alias_j->alias} );
+            }
+         }
+      }
+   }
+}
index 9ed7b11d6a8570c750ab05d8f676662278a3745f..a135bd22a838e3e401098e7567c570192cfcd39f 100644 (file)
@@ -54,7 +54,7 @@ void _engine_ui_pre_render(void)
 
    _graphics_set_target( &_ui_surface );
    _graphics_viewport( 0, 0, _engine.w, _engine.h );
-   _graphics_fill_rect( (i16[]){ 0, 0, _engine.w, _engine.h }, (union colour){{0,0,0,255}} );
+   //_graphics_fill_rect( (i16[]){ 0, 0, _engine.w, _engine.h }, (union colour){{0,0,0,255}} );
 }
 
 void _engine_ui_input_character( u32 codepoint )
@@ -65,6 +65,10 @@ void _engine_ui_input_character( u32 codepoint )
 
 void _engine_ui_post_render(void)
 {
+   glEnable( GL_BLEND );
+   glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
+   glBlendEquation( GL_FUNC_ADD );
+
    glActiveTexture( GL_TEXTURE0 );
    glBindTexture( GL_TEXTURE_2D, _ui_surface_texture );
    glTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, 1920, 1080, GL_BGRA, GL_UNSIGNED_BYTE, _ui_surface.buffer );
@@ -75,4 +79,6 @@ void _engine_ui_post_render(void)
    _shader_blit_uInverseRatio( (f32[2]){ (f64)_engine.w/1920.0, (f64)_engine.h/1080.0 } );
    glBindVertexArray( quad_vao );
    glDrawArrays( GL_TRIANGLES, 0, 6 );
+
+   glDisable( GL_BLEND );
 }
index 86a099a652ba32ebeb90a220e2940979f7ef1f47..a7568db9f24ab673f240719762e566bc3c5e4060 100644 (file)
@@ -44,6 +44,7 @@ void stretchy_free( struct stretchy_allocator *stretchy )
       c += 1 << (SMALL_SEGMENTS + i);
       stretchy->segments[i] = NULL;
    }
+   stretchy->count = 0;
 }
 
 void stretchy_shrink( struct stretchy_allocator *stretchy )
index 039982c7d4176d6ce5fa0a8c109813e47ed5ce47..e1cab133c59363348187689080ab13ad1982a4fd 100644 (file)
@@ -99,6 +99,11 @@ u32 keyvalues_append_string( struct keyvalues *kvs, u32 parent_offset, const c8
    return kv_offset;
 }
 
+u32 keyvalues_append_frame( struct keyvalues *kvs, u32 parent_offset, const c8 *key )
+{
+   return keyvalues_append_string( kvs, parent_offset, key, NULL );
+}
+
 u32 keyvalues_type( struct keyvalues *kvs, u32 kv_offset )
 {
    struct keyvalue *kv = stack_pointer( kvs->stack, kv_offset );
@@ -163,13 +168,19 @@ u32 keyvalues_get_child( struct keyvalues *kvs, u32 root_offset, u32 index )
    else return 0;
 }
 
-u32 keyvalues_get( struct keyvalues *kvs, u32 root_offset, const c8 *key )
+u32 keyvalues_get( struct keyvalues *kvs, u32 root_offset, const c8 *key, bool relative )
 {
    if( root_offset == 0 )
       root_offset = kvs->root_offset;
 
    u32 hash = buffer_djb2( key, 0 );
-   u32 child_offset = keyvalues_get_child( kvs, root_offset, 0 );
+   u32 child_offset;
+
+   if( relative )
+      child_offset = keyvalues_get_next( kvs, root_offset );
+   else
+      child_offset = keyvalues_get_child( kvs, root_offset, 0 );
+
    while( child_offset )
    {
       struct keyvalue *kv = stack_pointer( kvs->stack, child_offset );
@@ -197,7 +208,7 @@ bool keyvalues_read_i32s( struct keyvalues *kvs, u32 root_offset, const c8 *key,
    bool good = 1;
 
    u32 value_length;
-   char *value = keyvalues_value( kvs, keyvalues_get( kvs, root_offset, key ), &value_length );
+   char *value = keyvalues_value( kvs, keyvalues_get( kvs, root_offset, key, 0 ), &value_length );
    struct stream s;
    stream_open_buffer_read( &s, value, value_length, 0 );
 
@@ -221,7 +232,7 @@ bool keyvalues_read_u32s( struct keyvalues *kvs, u32 root_offset, const c8 *key,
    bool good = 1;
 
    u32 value_length;
-   char *value = keyvalues_value( kvs, keyvalues_get( kvs, root_offset, key ), &value_length );
+   char *value = keyvalues_value( kvs, keyvalues_get( kvs, root_offset, key, 0 ), &value_length );
    struct stream s;
    stream_open_buffer_read( &s, value, value_length, 0 );
 
@@ -245,7 +256,7 @@ bool keyvalues_read_f32s( struct keyvalues *kvs, u32 root_offset, const c8 *key,
    bool good = 1;
 
    u32 value_length;
-   char *value = keyvalues_value( kvs, keyvalues_get( kvs, root_offset, key ), &value_length );
+   char *value = keyvalues_value( kvs, keyvalues_get( kvs, root_offset, key, 0 ), &value_length );
    struct stream s;
    stream_open_buffer_read( &s, value, value_length, 0 );
 
@@ -266,35 +277,33 @@ bool keyvalues_read_f32s( struct keyvalues *kvs, u32 root_offset, const c8 *key,
 
 const c8 *keyvalues_read_string( struct keyvalues *kvs, u32 root_offset, const c8 *key, const c8 *default_value )
 {
-   const c8 *value = keyvalues_value( kvs, keyvalues_get( kvs, root_offset, key ), NULL );
+   const c8 *value = keyvalues_value( kvs, keyvalues_get( kvs, root_offset, key, 0 ), NULL );
    return value? value: default_value;
 }
 
-#define KV_APPEND_TEMPLATE( FUNCTION, ... )                    \
-   u32 temp_frame = _start_temporary_frame();                  \
-   struct stream value_str;                                    \
-   stream_open_stack( &value_str, _temporary_stack_allocator(), k_stream_null_terminate ); \
-   for( u32 i=0; i<len; i++ )                                  \
-   {                                                           \
-      FUNCTION ( &value_str, values[i], __VA_ARGS__ );         \
-      if( i+1!=len )                                           \
-         string_append_c8( &value_str, ' ' );                  \
-   }                                                           \
+#define KV_APPEND_TEMPLATE( FUNCTION, ... )                               \
+   struct stream value_str;                                               \
+   stream_open_stack( &value_str, kvs->stack, k_stream_null_terminate );  \
+   for( u32 i=0; i<len; i++ )                                             \
+   {                                                                      \
+      FUNCTION ( &value_str, values[i], __VA_ARGS__ );                    \
+      if( i+1!=len )                                                      \
+         string_append_c8( &value_str, ' ' );                             \
+   }                                                                      \
    u32 r = keyvalues_append_string( kvs, parent_offset, key, string_get(&value_str) );  \
-   _end_temporary_frame( temp_frame ); \
    return r;
 
-u32 keyvalues_append_vu32( struct keyvalues *kvs, u32 parent_offset, const c8 *key, u32 *values, u32 len )
+u32 keyvalues_append_u32s( struct keyvalues *kvs, u32 parent_offset, const c8 *key, u32 *values, u32 len )
 {
    KV_APPEND_TEMPLATE( string_append_u64, 10 )
 }
 
-u32 keyvalues_append_vi32( struct keyvalues *kvs, u32 parent_offset, const c8 *key, i32 *values, u32 len )
+u32 keyvalues_append_i32s( struct keyvalues *kvs, u32 parent_offset, const c8 *key, i32 *values, u32 len )
 {
    KV_APPEND_TEMPLATE( string_append_i64, 10 );
 }
 
-u32 keyvalues_append_vf32( struct keyvalues *kvs, u32 parent_offset, const c8 *key, f32 *values, u32 len )
+u32 keyvalues_append_f32s( struct keyvalues *kvs, u32 parent_offset, const c8 *key, f32 *values, u32 len )
 {
    KV_APPEND_TEMPLATE( string_append_f64, 10, 5 )
 }
@@ -470,9 +479,57 @@ void keyvalues_parse_stream( struct keyvalues *kvs, u32 root_offset, struct stre
    }
 } 
 
+static void kv_write_string( struct stream *out_stream, const c8 *string )
+{
+   c8 delim=0;
+   bool passed = 0;
+
+   for( u32 i=0; i<4096; i ++ )
+   {
+      c8 c = string[i];
+      if( c == '\0' )
+      {
+         if( i == 0 )
+            delim = '"';
+
+         passed = 1;
+         break;
+      }
+
+      c8 new_delim = 0;
+      if( c == '"' )
+         new_delim = '\'';
+      else if( c == '\'' || ((delim == 0) && (c == ' ' || c == '\t' || c == '\n')) )
+         new_delim = '"';
+
+      if( new_delim )
+      {
+         if( delim && (new_delim != delim) )
+         {
+            break;
+         }
+         else delim = new_delim;
+      }
+   }
+
+   if( passed )
+   {
+      if( delim ) string_append_c8( out_stream, delim );
+      string_append( out_stream, string, 0 );
+      if( delim ) string_append_c8( out_stream, delim );
+   }
+   else
+   {
+      string_append( out_stream, "ERROR_KV_UNWRITABLE_STRING", 0 );
+   }
+}
 
 void keyvalues_write_stream( struct keyvalues *kvs, struct stream *out_stream, u32 node, u32 depth )
 {
+   if( node == 0 )
+      node = kvs->root_offset;
+
+   ASSERT_CRITICAL( keyvalues_type( kvs, node ) == k_keyvalue_type_frame );
    u32 kv = keyvalues_get_child( kvs, node, 0 );
    while( kv )
    {
@@ -480,10 +537,30 @@ void keyvalues_write_stream( struct keyvalues *kvs, struct stream *out_stream, u
       {
          for( u32 i=0; i<depth; i ++ )
             $v_string( out_stream, {"   "} );
-         $v_string( out_stream, {keyvalues_key( kvs, kv, NULL )}, {" "}, {keyvalues_value( kvs, kv, NULL )}, {"\n"} );
+
+         kv_write_string( out_stream, keyvalues_key( kvs, kv, NULL ) );
+         $v_string( out_stream, {" "} );
+         kv_write_string( out_stream, keyvalues_value( kvs, kv, NULL ) );
+         $v_string( out_stream, {"\n"} );
       }
       else
+      {
+         const c8 *key = keyvalues_key( kvs, kv, NULL);
+         if( key )
+         {
+            for( u32 i=0; i<depth; i ++ ) $v_string( out_stream, {"   "} );
+            kv_write_string( out_stream, keyvalues_key( kvs, kv, NULL ) );
+            $v_string( out_stream, {"\n"} );
+         }
+
+         for( u32 i=0; i<depth; i ++ ) $v_string( out_stream, {"   "} );
+         $v_string( out_stream, {"{\n"} );
+
          keyvalues_write_stream( kvs, out_stream, kv, depth+1 );
+
+         for( u32 i=0; i<depth; i ++ ) $v_string( out_stream, {"   "} );
+         $v_string( out_stream, {"}\n"} );
+      }
       kv = keyvalues_get_next( kvs, kv );
    }
 }
@@ -501,41 +578,6 @@ static void vg_kv_write_indent( vg_kv_write *w )
       vg_stream_write( w->stream, (c8[]){ ' ' }, 1 );
 }
 
-static void vg_kv_write_string( vg_kv_write *w, const c8 *string, u32 length )
-{
-   if( length == 0 )
-      length = 0xffffffff;
-
-   u32 i=0;
-   char delim=0;
-   for( ;i<length; i ++ )
-   {
-      char c = string[i];
-      if( c == '\0' )
-         break;
-
-      char new_delim = 0;
-      if( c == '"' )
-         new_delim = '\'';
-      else if( c == '\'' || c == ' ' || c == '\t' || c == '\n' )
-         new_delim = '"';
-
-      if( new_delim )
-      {
-         if( delim && (new_delim != delim) )
-         {
-            vg_kv_write_string( w, "VG_KV_UNWRITABLE_STRING", 0 );
-            return;
-         }
-         else delim = new_delim;
-      }
-   }
-
-   if( delim ) vg_stream_write( w->stream, (c8[]){ delim }, 1 );
-   vg_stream_write( w->stream, string, i );
-   if( delim ) vg_stream_write( w->stream, (c8[]){ delim }, 1 );
-}
-
 void vg_kv_write_block( vg_kv_write *w, const c8 *name, u32 name_length )
 {
    vg_kv_write_indent( w );
index 1f125d83bf17c18d27a49a0c7793dc97e0abb5f2..660c2218ccecd5f28c962b854fc3d3e780cc44f0 100644 (file)
@@ -169,6 +169,31 @@ void _graphics_line( i16 p0[2], i16 p1[2], union colour colour )
       _graphics_pixel( wl, colour );
 }
 
+union colour graphics_lerp_colour( union colour c0, union colour c1, i32 d, i32 t )
+{
+   if( t == 0 ) return c0;
+   if( t == d ) return c1;
+   if( d == 0 ) return c0;
+
+   return (union colour){{ (i32)c0.r + (((i32)c1.r - (i32)c0.r)*d*t) / (d*d),
+                           (i32)c0.g + (((i32)c1.g - (i32)c0.g)*d*t) / (d*d),
+                           (i32)c0.b + (((i32)c1.b - (i32)c0.b)*d*t) / (d*d),
+                           (i32)c0.a + (((i32)c1.a - (i32)c0.a)*d*t) / (d*d) }};
+}
+
+void _graphics_line2( i16 p0[2], i16 p1[2], union colour c0, union colour c1 )
+{
+   struct bresenham b;
+   bresenham_init( &b, p0, p1 );
+   i16 wl[2];
+   while( bresenham_iter( &b, wl ) )
+   {
+      i32 d = b.delta[0],
+          t = b.x;
+      _graphics_pixel( wl, graphics_lerp_colour( c0, c1, d, t ) );
+   }
+}
+
 void _graphics_line_triangle( i16 p0[2], i16 p1[2], i16 p2[2], union colour colour )
 {
    _graphics_line( p0, p1, colour );
index e33a72c5c2edb636c3111fda3c0e0c9c912faa2d..11eb47f190f8a397f8dd78456bd13b0a805ef87b 100644 (file)
@@ -12,6 +12,7 @@ struct
    //i16 clipping_area[4];
 
    i16 mouse_co[2], mouse_co_clicked[2];
+   bool mouse_went_in_click_hole;
 
    /* internal state */
    enum ui_control_type
@@ -329,11 +330,11 @@ static void _ui_textbox_input_string( const c8 *input )
    }
 
    for( i32 i=0; i<input_length; i ++ )
-      if( i < _ui.controls.textbox.text_buffer_length )
+      if( i0+i < _ui.controls.textbox.text_buffer_length )
          buffer[i0+i] = input[i];
    buffer[ i32_min( _ui.controls.textbox.text_buffer_length-1, i0+input_length+remainder_length ) ] = 0;
-   _ui.controls.textbox.cursor_pos = i0+1;
-   _ui.controls.textbox.cursor_user = i0+1;
+   _ui.controls.textbox.cursor_pos = i32_min( i0+input_length, _ui.controls.textbox.text_buffer_length-1 );
+   _ui.controls.textbox.cursor_user = _ui.controls.textbox.cursor_pos;
 }
 
 static void _ui_textbox_set_cursor_user( i32 position, bool super )
@@ -391,6 +392,8 @@ enum textbox_action _ui_textbox( i16 rect[4], c8 *text_buffer, u32 text_buffer_l
       _ui.controls.textbox.action = k_textbox_no_action;
    }
 
+   enum textbox_action return_action = k_textbox_no_action;
+
    union colour text_colour = (union colour){{255,255,255,255}};
    if( _ui.active_control_type == k_ui_control_textbox )
    {
@@ -452,16 +455,23 @@ enum textbox_action _ui_textbox( i16 rect[4], c8 *text_buffer, u32 text_buffer_l
                _ui.controls.textbox.cursor_user = _ui_textbox_visual_to_buffer_index( rect, co );
             }
             else
+            {
                _ui.active_control_type = k_ui_control_none;
+               _ui.active_control_touched = 1;
+            }
+         }
+
+         return_action = _ui.controls.textbox.action;
+         _ui.controls.textbox.action = k_textbox_no_action;
+         if( (return_action == k_textbox_enter) && !(flags & UI_MULTILINE ) )
+         {
+            _ui.active_control_type = k_ui_control_none;
+            _ui.active_control_touched = 1;
          }
       }
    }
    _ui_text( rect, text_buffer, k_ui_align_x_left, text_colour );
 
-   enum textbox_action return_action = _ui.controls.textbox.action;
-   _ui.controls.textbox.action = k_textbox_no_action;
-   if( (return_action == k_textbox_enter) && !(flags & UI_MULTILINE ) )
-      _ui.active_control_type = k_ui_control_none;
    return return_action;
 }
 
@@ -617,6 +627,8 @@ bool _ui_update(void)
    }
 
    _ui.active_control_touched = 0;
+   _ui.mouse_went_in_click_hole = 0;
+
    return _ui.redraw;
 }
 
@@ -662,3 +674,19 @@ void _ui_draw(void)
    _graphics_fill_rect( (i16[]){ _ui.mouse_co[0],_ui.mouse_co[1], 3,3 }, (union colour){{255,255,255,255}} );
 }
 
+bool _ui_want_mouse( i16 area[4] )
+{
+   if( _ui.active_control_touched )
+      return 1;
+
+   if( ui_inside_rect( area, _ui.mouse_co ) )
+   {
+      if( _ui.active_control_type != k_ui_control_none )
+         return 1;
+
+      if( _input_button_down( k_input_action_ui_click, 0 ) )
+         return 1;
+   }
+
+   return 0;
+}
diff --git a/source/terminal_main.c b/source/terminal_main.c
new file mode 100644 (file)
index 0000000..1ef17a0
--- /dev/null
@@ -0,0 +1,7 @@
+#include "generated/hooks.h"
+
+i32 main( i32 argc, const c8 *argv[] )
+{
+   VG_PRE_MAIN;
+   EVENT_CALL( START );
+}