summaryrefslogtreecommitdiff
path: root/src/util
diff options
context:
space:
mode:
authoraura <nw@moneybot.cc>2026-08-11 19:10:16 +0200
committeraura <nw@moneybot.cc>2026-08-11 19:10:16 +0200
commit17c07b5f884453130d8be3a6d91d730d80d4f8ae (patch)
tree6ec11e5e5f7f0be9ed899196c2897aa76a907de2 /src/util
parent1aa17de9188790f049c0edc4281597cb4e535627 (diff)
parent0a9d7ce6a0764a87766ebb23cc1ece4c26c8c678 (diff)
Merge remote-tracking branch 'origin/day'
Diffstat (limited to 'src/util')
-rw-r--r--src/util/aabb.h12
-rw-r--r--src/util/allocator.h14
-rw-r--r--src/util/anim.h4
-rw-r--r--src/util/config/config.cpp2
-rw-r--r--src/util/console.cpp12
-rw-r--r--src/util/console.h883
-rw-r--r--src/util/input.cpp20
-rw-r--r--src/util/input.h7
-rw-r--r--src/util/profiler.cpp2
-rw-r--r--src/util/screen.h4
-rw-r--r--src/util/thread.h10
-rw-r--r--src/util/vector.h16
12 files changed, 954 insertions, 32 deletions
diff --git a/src/util/aabb.h b/src/util/aabb.h
index 9dc4b20..6e895fc 100644
--- a/src/util/aabb.h
+++ b/src/util/aabb.h
@@ -15,21 +15,21 @@ inline F32 aabb_support_radius( const AABB& aabb, const VEC3& dir ) {
}
inline U8 aabb_intersects( const AABB& aabb, const AABB& other ) {
- if (aabb.min.x > other.max.x || aabb.max.x < other.min.x)
+ if(aabb.min.x > other.max.x || aabb.max.x < other.min.x)
return 0;
- if (aabb.min.y > other.max.y || aabb.max.y < other.min.y)
+ if(aabb.min.y > other.max.y || aabb.max.y < other.min.y)
return 0;
- if (aabb.min.z > other.max.z || aabb.max.z < other.min.z)
+ if(aabb.min.z > other.max.z || aabb.max.z < other.min.z)
return 0;
return 1;
}
inline U8 aabb_contains_point( const AABB& aabb, const VEC3& point ) {
- if (point.x < aabb.min.x || point.x > aabb.max.x)
+ if(point.x < aabb.min.x || point.x > aabb.max.x)
return 0;
- if (point.y < aabb.min.y || point.y > aabb.max.y)
+ if(point.y < aabb.min.y || point.y > aabb.max.y)
return 0;
- if (point.z < aabb.min.z || point.z > aabb.max.z)
+ if(point.z < aabb.min.z || point.z > aabb.max.z)
return 0;
return 1;
}
diff --git a/src/util/allocator.h b/src/util/allocator.h
index 63fc94a..5fc5aa6 100644
--- a/src/util/allocator.h
+++ b/src/util/allocator.h
@@ -312,14 +312,16 @@ struct LIST {
if( idx >= size )
return;
- if constexpr( !__is_trivially_destructible(T) ) {
- data[idx].~T();
- memset( &data[idx], 0, sizeof(T) );
- }
-
- for( U32 i = idx; i < size - 1; i++ )
+ for( U32 i = idx; i + 1 < size; ++i )
data[i] = data[i + 1];
+ U32 last = size - 1;
+
+ if constexpr( !__is_trivially_destructible(T) )
+ data[last].~T();
+
+ memset( &data[last], 0, sizeof(T) );
+
if( --size < capacity / 4 )
shrink();
}
diff --git a/src/util/anim.h b/src/util/anim.h
index dbc2c11..4c314d9 100644
--- a/src/util/anim.h
+++ b/src/util/anim.h
@@ -9,8 +9,8 @@ inline T lerp( T a, T b, F32 t ) {
template < typename t >
inline t clamp( t a, t min, t max ) {
- if ( a < min ) return min;
- if ( a > max ) return max;
+ if( a < min ) return min;
+ if( a > max ) return max;
return a;
}
diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp
index 00a2512..63e042a 100644
--- a/src/util/config/config.cpp
+++ b/src/util/config/config.cpp
@@ -16,7 +16,7 @@ inline U8 is_whitespace( char c ) {
}
inline void trim_whitespace( char* buf ) {
- if (!buf || !*buf) return;
+ if(!buf || !*buf) return;
U32 start = 0;
while( buf[start] && is_whitespace( buf[start] ) ) start++;
diff --git a/src/util/console.cpp b/src/util/console.cpp
new file mode 100644
index 0000000..eee9030
--- /dev/null
+++ b/src/util/console.cpp
@@ -0,0 +1,12 @@
+#include "console.h"
+#include "../game.h"
+
+extern GAME_DATA* game;
+
+I32 console_clear_cb( CVAR *self, LIST<STR> cmdline ) {
+ if( !game )
+ return 0;
+ return console_clear_output( &game->console );
+}
+
+CVAR* g_clear = var_new( "clear", "clear console output window", &console_clear_cb );
diff --git a/src/util/console.h b/src/util/console.h
new file mode 100644
index 0000000..4a62561
--- /dev/null
+++ b/src/util/console.h
@@ -0,0 +1,883 @@
+#pragma once
+
+#include "../game/vars.h"
+#include "string.h"
+
+#include <math.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+enum CONSOLE_ROUTE : U8 {
+ CONSOLE_ROUTE_NONE = 0,
+ CONSOLE_ROUTE_VIEW = 1 << 0,
+ CONSOLE_ROUTE_STDOUT = 1 << 1,
+ CONSOLE_ROUTE_BOTH = CONSOLE_ROUTE_VIEW | CONSOLE_ROUTE_STDOUT
+};
+
+inline constexpr U64 CONSOLE_BYTES_MAX = 10 * 1024 * 1024;
+
+struct CONSOLE_DATA_RING {
+ LIST<STR> slots{64};
+
+ U32 first{};
+ U32 count{};
+
+ U64 max_bytes{CONSOLE_BYTES_MAX};
+ U64 revision{};
+ U64 total_pushed{};
+ U64 bytes{};
+
+ static U64 line_bytes( const STR& line ) {
+ return (U64)line.size + 1;
+ }
+
+ STR& operator[]( U32 logical_idx ) {
+ U32 physical_idx = ( first + logical_idx ) % slots.capacity;
+ return slots.data[physical_idx];
+ }
+
+ const STR& operator[]( U32 logical_idx ) const {
+ U32 physical_idx = ( first + logical_idx ) % slots.capacity;
+ return slots.data[physical_idx];
+ }
+
+ void grow() {
+ U32 new_capacity = slots.capacity
+ ? slots.capacity * 2
+ : 64;
+
+ LIST<STR> grown{new_capacity};
+
+ for( U32 idx = 0; idx < count; ++idx )
+ grown.push( (*this)[idx] );
+
+ slots = grown;
+ first = 0;
+ }
+
+ void pop_front() {
+ if( !count )
+ return;
+
+ STR& oldest = slots.data[first];
+ bytes -= line_bytes( oldest );
+ oldest = STR();
+
+ first = ( first + 1 ) % slots.capacity;
+
+ --count;
+ if( !count )
+ first = 0;
+ }
+
+ U8 make_room_for( U64 required_bytes ) {
+ if( required_bytes > max_bytes )
+ return 0;
+
+ U8 removed = 0;
+
+ while( count && bytes > max_bytes - required_bytes ) {
+ pop_front();
+ removed = 1;
+ }
+
+ if( removed )
+ ++revision;
+
+ return bytes <= max_bytes - required_bytes;
+ }
+
+ void push( const STR& src ) {
+ if( !max_bytes )
+ return;
+
+ U64 src_bytes = line_bytes( src );
+
+ if( src_bytes > max_bytes ) {
+ U32 keep = max_bytes > 1
+ ? (U32)( max_bytes - 1 )
+ : 0;
+
+ keep = keep < src.size
+ ? keep
+ : src.size;
+
+ STR clipped( keep, src.data + src.size - keep );
+ push( clipped );
+ return;
+ }
+
+ if( !make_room_for( src_bytes ) )
+ return;
+
+ if( count == slots.capacity )
+ grow();
+
+ U32 tail = ( first + count ) % slots.capacity;
+
+ if( tail < slots.size )
+ slots[tail] = src;
+ else
+ slots.push( src );
+
+ ++count;
+ ++total_pushed;
+ bytes += src_bytes;
+ }
+
+ void clear() {
+ slots.clear();
+ slots.reserve( 64 );
+ bytes = 0;
+ count = 0;
+ first = 0;
+ }
+};
+
+struct CONSOLE_STDOUT_CAPTURE {
+ I32 saved_stdout{-1};
+ I32 read_fd{-1};
+ U8 active{};
+};
+
+struct CONSOLE_DATA {
+ CONSOLE_STDOUT_CAPTURE stdout_capture{};
+ CONSOLE_DATA_RING lines{};
+ STR partial_stdout_line{};
+ U8 pending_carriage{};
+ LIST<STR> history{};
+ U64 revision{};
+};
+
+struct CONSOLE_COMMAND {
+ U8 unterminated;
+ LIST<STR> args;
+};
+
+inline U8 console_append_partial_bytes(
+ CONSOLE_DATA* state,
+ const char* text,
+ U32 length
+) {
+ if( !state || !text )
+ return 0;
+
+ if( !length )
+ return 1;
+
+ STR& partial = state->partial_stdout_line;
+ U64 required_bytes = (U64)partial.size + (U64)length + 1;
+
+ if( !state->lines.make_room_for( required_bytes ) )
+ return 0;
+
+ for( U32 idx = 0; idx < length; ++idx )
+ partial.push( text[idx] );
+
+ return 1;
+}
+
+inline void console_clear_partial_line( CONSOLE_DATA* state ) {
+ if( !state )
+ return;
+
+ STR& partial = state->partial_stdout_line;
+ partial.size = 0;
+
+ if( partial.data )
+ partial.data[0] = 0;
+}
+
+inline I32 console_clear_output( CONSOLE_DATA* state ) {
+ if( !state )
+ return 0;
+ state->lines.clear();
+ console_clear_partial_line( state );
+ state->pending_carriage = 0;
+ ++state->revision;
+ return 1;
+}
+extern I32 console_clear_cb( CVAR* self, LIST<STR> cmdline );
+
+inline void console_finish_partial_line( CONSOLE_DATA* state ) {
+ if( !state )
+ return;
+
+ state->lines.push( state->partial_stdout_line );
+ console_clear_partial_line( state );
+}
+
+inline STR console_command_token(
+ const char* text,
+ U32 start,
+ U32 end
+) {
+ STR token;
+ token.reserve( end - start + 1 );
+
+ for( U32 idx = start; idx < end; ++idx ) {
+ if( text[idx] == '"' )
+ continue;
+ token.push( text[idx] );
+ }
+ return token;
+}
+
+inline void console_parse_command(
+ const char* text,
+ CONSOLE_COMMAND* command
+) {
+ if( !command )
+ return;
+
+ command->args.clear();
+ command->unterminated = 0;
+
+ if( !text )
+ return;
+
+ U32 token_start = 0;
+ U8 in_string = 0;
+ U32 cursor = 0;
+
+ while( text[cursor] ) {
+ char c = text[cursor];
+
+ if( c == '\"' )
+ in_string ^= 1;
+
+ if( c == ' ' && !in_string ) {
+ if( cursor > token_start ) {
+ command->args.push(
+ console_command_token(
+ text, token_start, cursor
+ )
+ );
+ }
+
+ do {
+ ++cursor;
+ } while( text[cursor] == ' ' );
+
+ token_start = cursor;
+ continue;
+ }
+
+ ++cursor;
+ }
+
+ if( cursor > token_start ) {
+ command->args.push(
+ console_command_token(
+ text, token_start, cursor
+ )
+ );
+ }
+
+ command->unterminated = in_string;
+}
+
+inline void console_append_text(
+ CONSOLE_DATA* state,
+ const char* text,
+ U32 len
+) {
+ if( !state || !text || !len )
+ return;
+
+ ++state->revision;
+
+ for( U32 idx = 0; idx < len; ++idx ) {
+ char c = text[idx];
+
+ if( state->pending_carriage ) {
+ state->pending_carriage = 0;
+
+ if( c == '\n' ) {
+ console_finish_partial_line( state );
+ continue;
+ }
+ console_clear_partial_line( state );
+ }
+
+ if( c == '\r' ) {
+ state->pending_carriage = 1;
+ continue;
+ }
+
+ if( c == '\n' ) {
+ console_finish_partial_line( state );
+ continue;
+ }
+
+ if( c == '\t' ) {
+ console_append_partial_bytes(
+ state, " ", 2
+ );
+ continue;
+ }
+
+ console_append_partial_bytes( state, &c, 1 );
+ }
+}
+
+inline void console_write_fd( I32 fd, const char* text, U32 len ) {
+ if( fd < 0 || !text )
+ return;
+ U32 written = 0;
+ while( written < len ) {
+ ssize_t result = write( fd, text + written, len - written );
+ if( result > 0 ) {
+ written += (U32)result;
+ continue;
+ }
+ if( result < 0 && errno == EINTR )
+ continue;
+ break;
+ }
+}
+
+inline void console_write_original_stdout(
+ CONSOLE_DATA* state,
+ const char* text,
+ U32 len
+) {
+ I32 fd = state && state->stdout_capture.active
+ ? state->stdout_capture.saved_stdout
+ : STDOUT_FILENO;
+ console_write_fd( fd, text, len );
+}
+
+inline void console_write(
+ CONSOLE_DATA* state,
+ U8 routes,
+ const char* text,
+ U32 len
+) {
+ if( !text || !len )
+ return;
+ if( routes & CONSOLE_ROUTE_VIEW )
+ console_append_text( state, text, len );
+ if( routes & CONSOLE_ROUTE_STDOUT )
+ console_write_original_stdout( state, text, len );
+}
+
+inline void console_print(
+ CONSOLE_DATA* state,
+ U8 routes,
+ const char* fmt,
+ ...
+) {
+ if( !state || !fmt )
+ return;
+
+ va_list args;
+ va_list count_args;
+ va_start( args, fmt );
+ va_copy( count_args, args );
+ I32 len = vsnprintf( 0, 0, fmt, count_args );
+ va_end( count_args );
+
+ if( len < 0 ) {
+ va_end( args );
+ return;
+ }
+
+ STR text;
+ text.reserve( (U32)len + 1 );
+ vsnprintf(
+ text.data, (U32)len + 1,
+ fmt, args
+ );
+ va_end( args );
+
+ text.size = (U32)len;
+ text.data[text.size] = 0;
+ console_write(
+ state,
+ routes,
+ text.data,
+ text.size
+ );
+}
+
+inline U8 console_parse_i32(
+ const STR& text,
+ I32* output
+) {
+ if( !output || !text.data || !text.size )
+ return 0;
+
+ char* end = 0;
+ errno = 0;
+ long parsed = strtol( text.data, &end, 10 );
+
+ if( errno == ERANGE
+ || end == text.data
+ || *end
+ || parsed < INT_MIN
+ || parsed > INT_MAX
+ ) return 0;
+
+ *output = (I32)parsed;
+ return 1;
+}
+
+inline U8 console_parse_f32(
+ const STR& text,
+ F32* output
+) {
+ if( !output || !text.data || !text.size )
+ return 0;
+
+ char* end = 0;
+ errno = 0;
+ F32 parsed = strtof( text.data, &end );
+
+ if( errno == ERANGE
+ || end == text.data
+ || *end
+ || !isfinite( parsed )
+ ) return 0;
+
+ *output = parsed;
+ return 1;
+}
+
+inline U8 console_parse_color_component(
+ const STR& text,
+ F32* output
+) {
+ I32 integer = 0;
+ if( console_parse_i32( text, &integer ) ) {
+ if( integer < 0 || integer > 255 )
+ return 0;
+ *output = (F32)integer / 255.f;
+ return 1;
+ }
+
+ F32 decimal = 0.f;
+ if( !console_parse_f32( text, &decimal )
+ || decimal < 0.f
+ || decimal > 1.f
+ ) return 0;
+
+ *output = decimal;
+ return 1;
+}
+
+inline U8 console_execute_cvar(
+ CONSOLE_DATA* state,
+ CVAR* cvar,
+ LIST<STR>& args
+) {
+ if( cvar->type != CVAR_TYPE_FN && args.size == 0 )
+ return 0;
+
+ switch( cvar->type ) {
+ case CVAR_TYPE_FN: {
+ if( var_call( cvar, args ) )
+ return 1;
+
+ console_print(
+ state, CONSOLE_ROUTE_BOTH,
+ "%s", "[error] failed to run cvar\n"
+ );
+ return 0;
+ }
+ case CVAR_TYPE_INT: {
+ I32 value = 0;
+
+ if( args.size != 1
+ || !console_parse_i32( args[0], &value )
+ ) break;
+
+ var_set( cvar, value );
+ return 1;
+ }
+ case CVAR_TYPE_FLOAT: {
+ F32 value = 0.f;
+
+ if( args.size != 1
+ || !console_parse_f32( args[0], &value )
+ ) break;
+
+ var_set( cvar, value );
+ return 1;
+ } break;
+ case CVAR_TYPE_COLOR: {
+ if( args.size == 1 ) {
+ F32 value = 0.f;
+
+ if( !console_parse_color_component( args[0], &value ) )
+ break;
+
+ var_set( cvar, value );
+ return 1;
+ }
+
+ if( args.size != 3 && args.size != 4 )
+ break;
+
+ F32 components[4] = {
+ cvar->clr_v.r,
+ cvar->clr_v.g,
+ cvar->clr_v.b,
+ cvar->clr_v.a
+ };
+
+ for( U32 idx = 0; idx < args.size; ++idx ) {
+ if(
+ !console_parse_color_component(
+ args[idx],
+ &components[idx]
+ )
+ ) break;
+
+ if( idx + 1 != args.size )
+ continue;
+
+ var_set( cvar,
+ CLR(
+ components[0],
+ components[1],
+ components[2],
+ components[3]
+ )
+ );
+ return 1;
+ }
+ } break;
+ case CVAR_TYPE_STRING: {
+ if( args.size != 1
+ || args[0].size >= sizeof( cvar->str_v )
+ ) break;
+
+ var_set( cvar, args[0] );
+ return 1;
+ }
+ default: {
+ console_print(
+ state, CONSOLE_ROUTE_BOTH,
+ "%s", "[error] unhandled cvar type\n"
+ );
+ return 0;
+ }
+ }
+ console_print(
+ state, CONSOLE_ROUTE_BOTH,
+ "[error] failed to set %s\n",
+ cvar->name.data
+ );
+ return 0;
+}
+
+inline U8 console_print_i32_help(
+ CONSOLE_DATA* state,
+ CVAR* cvar
+) {
+ if( !state || !cvar || cvar->type != CVAR_TYPE_INT )
+ return 0;
+
+ if( cvar->flags & CVAR_FLAG_MINMAX ) {
+ console_print(
+ state, CONSOLE_ROUTE_BOTH,
+ "%s - %s. default(%d) current(%d) min(%d) max(%d)\n",
+ cvar->name.data,
+ cvar->desc.data,
+ cvar->default_v.i_v,
+ cvar->i_v,
+ cvar->min_v.i_v,
+ cvar->max_v.i_v
+ );
+ } else {
+ console_print(
+ state, CONSOLE_ROUTE_BOTH,
+ "%s - %s. default(%d) current(%d)\n",
+ cvar->name.data,
+ cvar->desc.data,
+ cvar->default_v.i_v,
+ cvar->i_v
+ );
+ }
+ return 1;
+}
+inline U8 console_print_f32_help(
+ CONSOLE_DATA* state,
+ CVAR* cvar
+) {
+ if( !state || !cvar || cvar->type != CVAR_TYPE_FLOAT )
+ return 0;
+
+ if( cvar->flags & CVAR_FLAG_MINMAX ) {
+ console_print(
+ state, CONSOLE_ROUTE_BOTH,
+ "%s - %s. default(%f) current(%f) min(%f) max(%f)\n",
+ cvar->name.data,
+ cvar->desc.data,
+ cvar->default_v.fl_v,
+ cvar->fl_v,
+ cvar->min_v.fl_v,
+ cvar->max_v.fl_v
+ );
+ } else {
+ console_print(
+ state, CONSOLE_ROUTE_BOTH,
+ "%s - %s. default(%f) current(%f)\n",
+ cvar->name.data,
+ cvar->desc.data,
+ cvar->default_v.fl_v,
+ cvar->fl_v
+ );
+ }
+ return 1;
+}
+
+inline U8 console_print_cvar_help(
+ CONSOLE_DATA* state,
+ CVAR* cvar
+) {
+ switch( cvar->type ) {
+ case CVAR_TYPE_FN: return 1;
+ case CVAR_TYPE_INT:
+ return console_print_i32_help( state, cvar );
+ case CVAR_TYPE_FLOAT:
+ return console_print_f32_help( state, cvar );
+ case CVAR_TYPE_COLOR: {
+ console_print(
+ state, CONSOLE_ROUTE_BOTH,
+ "%s - %s. default(%f %f %f %f) current(%f %f %f %f)\n",
+ cvar->name.data,
+ cvar->desc.data,
+ cvar->default_v.clr_v.r,
+ cvar->default_v.clr_v.g,
+ cvar->default_v.clr_v.b,
+ cvar->default_v.clr_v.a,
+ cvar->clr_v.r,
+ cvar->clr_v.g,
+ cvar->clr_v.b,
+ cvar->clr_v.a
+ );
+ } break;
+ case CVAR_TYPE_STRING: {
+ console_print(
+ state, CONSOLE_ROUTE_BOTH,
+ "%s - %s. default(%s) current(%s)\n",
+ cvar->name.data,
+ cvar->desc.data,
+ cvar->default_v.str_v,
+ cvar->str_v
+ );
+ } break;
+ default: {
+ console_print(
+ state, CONSOLE_ROUTE_BOTH,
+ "%s", "[error] unhandled cvar type\n"
+ );
+ return 0;
+ }
+ }
+ return 1;
+}
+
+inline U8 console_execute_command(
+ CONSOLE_DATA* state,
+ CONSOLE_COMMAND* command
+) {
+ if( !command || !command->args.size )
+ return 1;
+
+ if( command->unterminated ) {
+ console_print(
+ state, CONSOLE_ROUTE_BOTH,
+ "%s", "[error] unterminated string\n"
+ );
+ return 0;
+ }
+
+ STR command_name = command->args.pop_front();
+ CVAR* cvar = var_find( command_name.data );
+
+ if( !cvar ) {
+ console_print(
+ state, CONSOLE_ROUTE_BOTH,
+ "[error] unknown command: %s\n",
+ command_name.data
+ );
+ return 0;
+ }
+
+ if( !command->args.size && cvar->type != CVAR_TYPE_FN )
+ return console_print_cvar_help( state, cvar );
+ return console_execute_cvar(
+ state,
+ cvar,
+ command->args
+ );
+}
+
+inline U8 console_execute_segment(
+ CONSOLE_DATA* state,
+ const char* text,
+ U32 length
+) {
+ if( !length )
+ return 1;
+
+ STR segment( length, text );
+ CONSOLE_COMMAND command{};
+
+ console_parse_command(
+ segment.data,
+ &command
+ );
+
+ return console_execute_command( state, &command );
+}
+
+inline void console_execute( CONSOLE_DATA* state, const char* cmd ) {
+ if( !state || !cmd )
+ return;
+
+ U32 command_start = 0;
+ U8 in_string = 0;
+
+ for( U32 cursor = 0;; ++cursor ) {
+ char c = cmd[cursor];
+
+ if( c == '"' )
+ in_string ^= 1;
+
+ U8 at_end = !c;
+ U8 at_separator = c == ';' && !in_string;
+
+ if( !at_end && !at_separator )
+ continue;
+
+ if(
+ !console_execute_segment(
+ state,
+ cmd + command_start,
+ cursor - command_start
+ )
+ ) return;
+
+ if( at_end )
+ return;
+
+ command_start = cursor + 1;
+ }
+}
+
+inline U8 console_submit(
+ CONSOLE_DATA* state,
+ const char* cmd
+) {
+ if( !state || !cmd || !cmd[0] )
+ return 0;
+
+ STR submitted( "%s", cmd );
+ state->history.push( submitted );
+
+ console_print(
+ state,
+ CONSOLE_ROUTE_BOTH,
+ "] %s\n",
+ submitted.data
+ );
+
+ console_execute( state, submitted.data );
+ return 1;
+}
+
+inline U8 console_capture_stdout_begin( CONSOLE_DATA* state ) {
+ if( !state )
+ return 0;
+
+ CONSOLE_STDOUT_CAPTURE& capture = state->stdout_capture;
+
+ if( capture.active )
+ return 1;
+
+ I32 saved_stdout = dup( STDOUT_FILENO );
+ if( saved_stdout < 0 )
+ return 0;
+
+ I32 pipe_fds[2];
+ if( pipe( pipe_fds ) ) {
+ close( saved_stdout );
+ return 0;
+ }
+
+ I32 flags = fcntl( pipe_fds[0], F_GETFL, 0 );
+ if( flags < 0
+ || fcntl( pipe_fds[0], F_SETFL, flags | O_NONBLOCK ) < 0 ) {
+ close( pipe_fds[0] );
+ close( pipe_fds[1] );
+ close( saved_stdout );
+ return 0;
+ }
+
+ fflush( stdout );
+ setvbuf( stdout, 0, _IONBF, 0 );
+ if( dup2( pipe_fds[1], STDOUT_FILENO ) < 0 ) {
+ close( pipe_fds[0] );
+ close( pipe_fds[1] );
+ close( saved_stdout );
+ return 0;
+ }
+ close( pipe_fds[1] );
+ capture.saved_stdout = saved_stdout;
+ capture.read_fd = pipe_fds[0];
+ capture.active = 1;
+ return 1;
+}
+
+inline void console_pump_stdout( CONSOLE_DATA* state ) {
+ if( !state )
+ return;
+
+ CONSOLE_STDOUT_CAPTURE& capture = state->stdout_capture;
+ if( !capture.active || capture.read_fd < 0 )
+ return;
+ char buffer[4096];
+ for( ;; ) {
+ ssize_t count = read( capture.read_fd, buffer, sizeof( buffer ) );
+ if( count > 0 ) {
+ console_write_fd(
+ capture.saved_stdout,
+ buffer, (U32)count
+ );
+ console_append_text( state, buffer, (U32)count );
+ continue;
+ }
+ if( count < 0 && errno == EINTR )
+ continue;
+ if( count < 0
+ && ( errno == EAGAIN || errno == EWOULDBLOCK )
+ ) break;
+ break;
+ }
+}
+
+inline void console_capture_stdout_end( CONSOLE_DATA* state ) {
+ if( !state )
+ return;
+
+ CONSOLE_STDOUT_CAPTURE& capture = state->stdout_capture;
+ if( !capture.active )
+ return;
+
+ fflush( stdout );
+ dup2( capture.saved_stdout, STDOUT_FILENO );
+ console_pump_stdout( state );
+ close( capture.read_fd );
+ close( capture.saved_stdout );
+ capture.read_fd = capture.saved_stdout = -1;
+ capture.active = 0;
+}
diff --git a/src/util/input.cpp b/src/util/input.cpp
index fa308e2..46bc5f9 100644
--- a/src/util/input.cpp
+++ b/src/util/input.cpp
@@ -1,6 +1,8 @@
#include "input.h"
#include "SDL_events.h"
+#include <string.h>
+
INPUT_DATA input;
void input_on_event( SDL_Event* e ) {
@@ -30,6 +32,22 @@ void input_on_event( SDL_Event* e ) {
input.mouse.pos_delta.x += (F32)e->motion.xrel * input.mpitch;
input.mouse.pos_delta.y += (F32)e->motion.yrel * input.myaw;
} break;
+ case SDL_TEXTINPUT: {
+ U32 incoming = strlen( e->text.text );
+ U32 available = (U32)sizeof( input.text ) - 1 - input.textlen;
+ U32 count = incoming < available
+ ? incoming
+ : available;
+ if( !count )
+ break;
+ memcpy(
+ input.text + input.textlen,
+ e->text.text,
+ count
+ );
+ input.textlen += count;
+ input.text[input.textlen] = 0;
+ } break;
case SDL_KEYDOWN: {
input.keys[e->key.keysym.sym & 0xff] = 1;
} break;
@@ -77,6 +95,8 @@ void input_on_mouse( I32 type, I32 x, I32 y ) {
void input_frame_end() {
input.mouse.wheel = 0;
+ input.textlen = 0;
+ input.text[0] = 0;
}
U8 input_is_key_down( U32 key ) {
diff --git a/src/util/input.h b/src/util/input.h
index af5e258..f982844 100644
--- a/src/util/input.h
+++ b/src/util/input.h
@@ -34,9 +34,11 @@ struct INPUT_KEYBINDS {
using ON_INPUT_FN = FN<void( SDL_Event* )>;
+constexpr U32 INPUT_TEXT_MAX = 1024;
+
struct INPUT_DATA {
MOUSE_DATA mouse;
- U8 keys[0xff];
+ U8 keys[0x100];
U8 mouselock;
F32 msens = .3f;
F32 myaw = 1.f;
@@ -44,7 +46,8 @@ struct INPUT_DATA {
INPUT_KEYBINDS binds;
LIST<ON_INPUT_FN> on_input;
-
+ char text[INPUT_TEXT_MAX];
+ U32 textlen;
};
extern INPUT_DATA input;
diff --git a/src/util/profiler.cpp b/src/util/profiler.cpp
index 86c1810..5f9cd25 100644
--- a/src/util/profiler.cpp
+++ b/src/util/profiler.cpp
@@ -4,7 +4,7 @@
#include "../game/vars.h"
#include "string.h"
-CVAR* prof_overlay = var_new( "prof_overlay", 0 );
+CVAR* prof_overlay = var_new( "prof_overlay", "show profiler overlay", 0, 0, 1 );
PROFILER_GLOBAL gprof;
void __profiler_intern_draw_tree( PROFILER_LIST_ENTRY* e, GL_FONT* font, I32* x, I32* y ) {
diff --git a/src/util/screen.h b/src/util/screen.h
index 34effb3..6d30231 100644
--- a/src/util/screen.h
+++ b/src/util/screen.h
@@ -25,7 +25,7 @@ inline VEC2 s_c( VEC2 offset = { 0.f, 0.f } ) {
}
inline VEC2 s_tc( VEC2 offset = { 0.f, 0.f } ) {
- return { offset.x + (F32)canvas[0] / 2, 0 };
+ return { offset.x + (F32)canvas[0] / 2, 0.0f };
}
inline VEC2 s_bc( VEC2 offset = { 0.f, 0.f } ) {
@@ -33,7 +33,7 @@ inline VEC2 s_bc( VEC2 offset = { 0.f, 0.f } ) {
}
inline VEC2 s_cl( VEC2 offset = { 0.f, 0.f } ) {
- return { 0, offset.y + (F32)canvas[1] / 2 };
+ return { 0.0f, offset.y + (F32)canvas[1] / 2 };
}
inline VEC2 s_cr( VEC2 offset = { 0.f, 0.f } ) {
diff --git a/src/util/thread.h b/src/util/thread.h
index 72ef5fb..b46ade0 100644
--- a/src/util/thread.h
+++ b/src/util/thread.h
@@ -544,13 +544,13 @@ get the count, it might have changed by another thread calling consume or produc
union thread_mutex_t
{
void* align;
- char data[ 64 ];
+ char data[64];
};
union thread_signal_t
{
void* align;
- char data[ 116 ];
+ char data[116];
};
union thread_atomic_int_t
@@ -567,7 +567,7 @@ union thread_atomic_ptr_t
union thread_timer_t
{
void* data;
- char d[ 8 ];
+ char d[8];
};
struct thread_queue_t
@@ -1418,7 +1418,7 @@ int thread_queue_produce( thread_queue_t* queue, void* value, int timeout_ms )
return 0;
}
int tail = thread_atomic_int_inc( &queue->tail );
- queue->values[ tail % queue->size ] = value;
+ queue->values[tail % queue->size] = value;
if( thread_atomic_int_inc( &queue->count ) == 0 )
thread_signal_raise( &queue->data_ready );
return 1;
@@ -1439,7 +1439,7 @@ void* thread_queue_consume( thread_queue_t* queue, int timeout_ms )
return NULL;
}
int head = thread_atomic_int_inc( &queue->head );
- void* retval = queue->values[ head % queue->size ];
+ void* retval = queue->values[head % queue->size];
if( thread_atomic_int_dec( &queue->count ) == queue->size )
thread_signal_raise( &queue->space_open );
return retval;
diff --git a/src/util/vector.h b/src/util/vector.h
index 294b627..c88ccee 100644
--- a/src/util/vector.h
+++ b/src/util/vector.h
@@ -12,8 +12,9 @@ struct VEC2 {
F32 y;
VEC2() { x = y = 0.0f; }
- VEC2( F32 X, F32 Y ) { x = X; y = Y; }
VEC2( const VEC2& v ) = default;
+ template<typename T1, typename T2>
+ VEC2( T1 X, T2 Y ) { x = (F32)X; y = (F32)Y; }
bool operator==( const VEC2& v ) const { return ( x == v.x && y == v.y ); }
F32& operator[]( I32 i ) { return ( (F32*)this )[i]; }
@@ -46,9 +47,9 @@ struct VEC3 {
F32 z;
VEC3() { x = y = z = 0.0f; }
- VEC3( F32 X, F32 Y, F32 Z ) { x = X; y = Y; z = Z; }
VEC3( const VEC3& v ) = default;
-
+ template<typename T1, typename T2, typename T3>
+ VEC3( T1 X, T2 Y, T3 Z ) { x = (F32)X; y = (F32)Y; z = (F32)Z; }
VEC3( const VEC2& v ) { x = v.x; y = v.y; z = 0.f; }
bool operator==( const VEC3& v ) const { return ( x == v.x && y == v.y && z == v.z ); }
@@ -81,8 +82,9 @@ struct VEC4 {
F32 x, y, z, w;
VEC4() { x = y = z = w = 0; }
- VEC4( F32 X, F32 Y, F32 Z, F32 W ) { x = X; y = Y; z = Z; w = W; }
VEC4( const VEC4& v ) = default;
+ template <typename T1, typename T2, typename T3, typename T4>
+ VEC4( T1 X, T2 Y, T3 Z, T4 W ) { x = (F32)X; y = (F32)Y; z = (F32)Z; w = (F32)W; }
VEC4( const VEC3& v ) { x = v.x; y = v.y; z = v.z; w = 1.f; }
VEC4( const VEC2& v ) { x = v.x; y = v.y; z = 0.f; w = 1.f; }
@@ -227,19 +229,19 @@ inline void angle_vectors( const VEC3& angles, VEC3* forward, VEC3* right, VEC3*
sr = sinf( angles.z * PIRAD );
cr = cosf( angles.z * PIRAD );
- if ( forward ) {
+ if( forward ) {
forward->x = cp * cy;
forward->y = cp * sy;
forward->z = -sp;
}
- if ( right ) {
+ if( right ) {
right->x = -1 * sr * sp * cy + -1 * cr * -sy;
right->y = -1 * sr * sp * sy + -1 * cr * cy;
right->z = -1 * sr * cp;
}
- if ( up ) {
+ if( up ) {
up->x = cr * sp * cy + -sr * -sy;
up->y = cr * sp * sy + -sr * cy;
up->z = cr * cp;