From 39f7d2905f0801a92083ac36df88cbaf5f6b80f2 Mon Sep 17 00:00:00 2001 From: hgn Date: Fri, 17 May 2024 13:18:41 +0100 Subject: [PATCH] wonky start to vg3 --- vg_audio_dsp.h | 2 +- vg_build.h | 126 +- vg_console.c | 49 +- vg_engine.c | 185 ++- vg_engine.h | 36 +- vg_font.h | 2 + vg_loader.c | 19 +- vg_log.c | 2 +- vg_log.h | 2 +- vg_msg.c | 4 +- vg_opengl.h | 6 + vg_platform/android/AndroidManifest.xml | 22 + vg_platform/android/android_native_app_glue.c | 457 ++++++ vg_platform/android/android_native_app_glue.h | 350 +++++ vg_platform/android/debug.keystore | Bin 0 -> 2150 bytes vg_platform/android/resources/mipmap/icon.png | Bin 0 -> 4222 bytes .../android/resources/values/strings.xml | 7 + vg_profiler.c | 2 +- vg_profiler.h | 2 +- vg_shader.c | 9 +- vg_shader.h | 28 +- vg_imgui.c => vg_ui/imgui.c | 1266 ++++++----------- vg_imgui.h => vg_ui/imgui.h | 238 ++-- vg_ui/imgui_impl_opengl.c | 479 +++++++ 24 files changed, 2264 insertions(+), 1029 deletions(-) create mode 100644 vg_opengl.h create mode 100644 vg_platform/android/AndroidManifest.xml create mode 100644 vg_platform/android/android_native_app_glue.c create mode 100644 vg_platform/android/android_native_app_glue.h create mode 100644 vg_platform/android/debug.keystore create mode 100644 vg_platform/android/resources/mipmap/icon.png create mode 100644 vg_platform/android/resources/values/strings.xml rename vg_imgui.c => vg_ui/imgui.c (57%) rename vg_imgui.h => vg_ui/imgui.h (77%) create mode 100644 vg_ui/imgui_impl_opengl.c diff --git a/vg_audio_dsp.h b/vg_audio_dsp.h index d74bc02..e3239eb 100644 --- a/vg_audio_dsp.h +++ b/vg_audio_dsp.h @@ -3,7 +3,7 @@ //#define VG_ECHO_LPF_BUTTERWORTH #include "vg_platform.h" -#include "dep/glad/glad.h" +#include "vg_opengl.h" #include "vg_m.h" struct vg_dsp diff --git a/vg_build.h b/vg_build.h index b40ecd3..f284821 100644 --- a/vg_build.h +++ b/vg_build.h @@ -25,7 +25,8 @@ struct vg_env { k_platform_anyplatform, k_platform_windows, - k_platform_linux + k_platform_linux, + k_platform_android } platform; @@ -34,6 +35,8 @@ struct vg_env k_architecture_anyarch, k_architecture_i386, k_architecture_x86_64, + k_architecture_aarch64, + k_architecture_armv7a } arch; @@ -41,7 +44,8 @@ struct vg_env { k_compiler_blob, k_compiler_clang, - k_compiler_zigcc + k_compiler_zigcc, + k_compiler_clang_android } compiler; @@ -73,6 +77,16 @@ struct vg_env vg_release_env = { .debug_asan = 0 }; +struct vg_env vg_test_android_env = { + .arch = k_architecture_armv7a, + .compiler = k_compiler_clang_android, + .libc = k_libc_version_native, + .debug_asan = 0, + .fresh = 0, + .platform = k_platform_android, + .optimization = 0 +}; + struct vg_project { struct vg_env *env; @@ -104,28 +118,33 @@ static const char *platform_names[] = { [k_platform_anyplatform] = "anyplatform", [k_platform_windows] = "windows", - [k_platform_linux] = "linux" + [k_platform_linux] = "linux", + [k_platform_android] = "android" }; static const char *architecture_names[] = { [k_architecture_anyarch] = "anyarch", [k_architecture_i386] = "i386", - [k_architecture_x86_64] = "x86_64" + [k_architecture_x86_64] = "x86_64", + [k_architecture_aarch64] = "aarch64", + [k_architecture_armv7a] = "armv7a" }; static const char *compiler_names[] = { [k_compiler_blob] = "blob", [k_compiler_clang] = "clang", - [k_compiler_zigcc] = "zig-cc" + [k_compiler_zigcc] = "zig-cc", + [k_compiler_clang_android] = "clang" }; static const char *compiler_paths[] = { [k_compiler_blob] = NULL, [k_compiler_clang] = "clang", - [k_compiler_zigcc] = "zig cc" + [k_compiler_zigcc] = "zig cc", + [k_compiler_clang_android] = "/opt/android-ndk/toolchains/llvm/prebuilt/linux-x86_64/bin/clang \\\n-target armv7a-linux-androideabi22 \\\n" }; static const char *libc_names[] = @@ -212,6 +231,12 @@ void vg_tarball_project( struct vg_project *proj ) proj->uid.buffer, time(NULL), proj->uid.buffer ); } +bool vg_platform_posix( enum platform p ) +{ + if( (p == k_platform_linux) || (p == k_platform_android) ) return 1; + else return 0; +} + /* * The project configurator and compiler. * -------------------------------------------------------------------------- */ @@ -243,7 +268,7 @@ void vg_project_new_target( struct vg_project *proj, const char *name, } - if( proj->env->platform == k_platform_linux ) + if( vg_platform_posix( proj->env->platform ) ) { if( type == k_obj_type_shared ) vg_strcat( &proj->target, ".so" ); @@ -258,8 +283,10 @@ void vg_project_new_target( struct vg_project *proj, const char *name, { vg_include_dir( proj, "-I." ); vg_include_dir( proj, "-I./vg" ); +#if 0 vg_library_dir( proj, "-L." ); vg_library_dir( proj, "-L/usr/lib" ); +#endif } vg_info( " New target: %s\n", name ); @@ -276,16 +303,28 @@ void vg_project_init( struct vg_project *proj, vg_strnull( &proj->compiled_objects, NULL, -1 ); /* check for problems in configuration */ - if( env->libc != k_libc_version_native ){ - if( env->compiler != k_compiler_zigcc ){ + if( env->libc != k_libc_version_native ) + { + if( env->compiler != k_compiler_zigcc ) + { vg_fatal_error( "Cannot specify libc version using the '%s' compiler.\n", compiler_names[ env->compiler ] ); } } - if( env->compiler == k_compiler_clang ){ - if( env->platform != k_platform_linux ){ + if( env->compiler == k_compiler_clang ) + { + if( env->platform != k_platform_linux ) + { + vg_fatal_error( "Cannot compile for '%s' using the '%s' compiler;" ); + } + } + + if( env->compiler == k_compiler_clang_android ) + { + if( env->platform != k_platform_android ) + { vg_fatal_error( "Cannot compile for '%s' using the '%s' compiler;" ); } } @@ -367,11 +406,20 @@ void vg_compile_project( struct vg_project *proj ) vg_strcat( &cmd, " " ); if( proj->type == k_obj_type_obj ) - vg_strcat( &cmd, "-c " ); + vg_strcat( &cmd, "-c -fPIC " ); if( proj->type == k_obj_type_shared ) + { vg_strcat( &cmd, "-shared -fPIC " ); + if( proj->env->platform == k_platform_android ) + { + vg_strcat( &cmd, "-Wl,-soname," ); + vg_strcat( &cmd, proj->target.buffer ); + vg_strcat( &cmd, " " ); + } + } + vg_strcat( &cmd, proj->sources.buffer ); vg_strcat( &cmd, "\\\n" ); @@ -393,13 +441,15 @@ void vg_compile_project( struct vg_project *proj ) } /* target platform specification (zig-cc only) */ - if( proj->env->compiler == k_compiler_zigcc ){ + if( proj->env->compiler == k_compiler_zigcc ) + { vg_strcat( &cmd, " -target " ); vg_strcat( &cmd, architecture_names[proj->env->arch] ); vg_strcat( &cmd, "-" ); vg_strcat( &cmd, platform_names[proj->env->platform] ); - if( proj->env->platform == k_platform_linux ){ + if( proj->env->platform == k_platform_linux ) + { vg_strcat( &cmd, "-gnu" ); vg_strcat( &cmd, libc_names[proj->env->libc] ); } @@ -459,6 +509,7 @@ void vg_add_engine( struct vg_project *proj, struct vg_engine_config *config ) vg_strcat( &config_string, "-DVG_TIMESTEP_FIXED=\"(1.0/" ); vg_strcati32( &config_string, config->fixed_update_hz ); vg_strcat( &config_string, ".0)\" \\\n" ); + if( config->legacy_support_vg_msg1 ) vg_strcat( &config_string, "-DVG_MSG_V1_SUPPORT \\\n" ); if( config->log_source_info ) @@ -468,6 +519,31 @@ void vg_add_engine( struct vg_project *proj, struct vg_engine_config *config ) if( config->custom_shaders ) vg_strcat( &config_string, "-DVG_CUSTOM_SHADERS \\\n" ); + if( proj->env->arch == k_architecture_i386 || + proj->env->arch == k_architecture_armv7a ) + vg_strcat( &config_string, "-DVG_32 \\\n" ); + else + vg_strcat( &config_string, "-DVG_64 \\\n" ); + + if( proj->env->platform == k_platform_android ) + { + vg_strcat( &config_string, "-DVG_ANDROID \\\n" ); + vg_strcat( &config_string, + "-DANDROID -DAPPNAME=\\\"carrot\\\" -DANDROID_FULLSCREEN \\\n" + "-DANDROIDVERSION=22 -mfloat-abi=softfp -m32 \\\n" ); + +#if 0 + vg_strcat( &config_string, + "-L/opt/android-ndk/toolchains/llvm/prebuilt/linux-x86_64/" + "sysroot/usr/lib/arm-linux-androideabi/22 \\\n" + "-I/opt/android-ndk/toolchains/llvm/prebuilt/linux-x86_64/" + "sysroot/usr/include \\\n" + "-I/opt/android-ndk/toolchains/llvm/prebuilt/linux-x86_64/" + "sysroot/usr/include/android \\\n" + ); +#endif + } + vg_strcat( &config_string, "\\\n" ); /* compile heavy dependencies seperately */ @@ -480,14 +556,18 @@ void vg_add_engine( struct vg_project *proj, struct vg_engine_config *config ) /* external dependencies */ vg_project_new_target( &dep_proj, "vg_deps", k_obj_type_obj ); + vg_add_source( &dep_proj, config_string.buffer ); vg_add_source( &dep_proj, "vg/vg_depencies.c" ); vg_compile_project( &dep_proj ); /* glad */ - vg_project_new_target( &dep_proj, "vg_glad", k_obj_type_obj ); - vg_add_source( &dep_proj, "vg/dep/glad/glad.c" ); - vg_include_dir( &dep_proj, "-I./vg/dep " ); - vg_compile_project( &dep_proj ); + if( proj->env->platform != k_platform_android ) + { + vg_project_new_target( &dep_proj, "vg_glad", k_obj_type_obj ); + vg_add_source( &dep_proj, "vg/dep/glad/glad.c" ); + vg_include_dir( &dep_proj, "-I./vg/dep " ); + vg_compile_project( &dep_proj ); + } /* core engine */ vg_project_new_target( &dep_proj, "vg_engine_core", k_obj_type_obj ); @@ -499,6 +579,11 @@ void vg_add_engine( struct vg_project *proj, struct vg_engine_config *config ) /* steamworks */ if( config->steam_api ) { + if( env.platform == k_platform_android ) + { + vg_fatal_error( "Cannot use steam_api on android" ); + } + vg_project_new_target( &dep_proj, "vg_steam", k_obj_type_obj ); vg_add_source( &dep_proj, "vg/vg_steam.c" ); vg_compile_project( &dep_proj ); @@ -533,12 +618,15 @@ void vg_add_engine( struct vg_project *proj, struct vg_engine_config *config ) vg_add_blob( proj, "vg/dep/sdl/SDL2.dll ", "" ); vg_library_dir( proj, "-L./vg/dep/sdl " ); } + else if( proj->env->platform == k_platform_android ) + { + vg_link( proj, "-lGLESv3 -lEGL -lOpenSLES -pthread -landroid -llog " ); + } vg_add_source( proj, config_string.buffer ); vg_add_source( proj, dep_proj.compiled_objects.buffer ); vg_add_source( proj, "\\\n" ); vg_include_dir( proj, "-I./vg/dep " ); - vg_link( proj, "-lm " ); } void vg_add_controller_database( struct vg_project *proj ) diff --git a/vg_console.c b/vg_console.c index 561bb63..57a8134 100644 --- a/vg_console.c +++ b/vg_console.c @@ -1,6 +1,6 @@ #include "vg_engine.h" #include "vg_console.h" -#include "vg_imgui.h" +#include "vg_ui/imgui.h" #include "vg_log.h" #include "vg_string.h" #include @@ -326,8 +326,10 @@ void console_suggest_score_text( const char *str, const char *input, static void console_update_suggestions(void) { - if( vg_ui.focused_control_type != k_ui_control_textbox || - vg_ui.textbuf != vg_console.input ) + ui_context *ctx = ui_current_context(); + + if( ctx->focused_control_type != k_ui_control_textbox || + ctx->textbuf != vg_console.input ) return; vg_console.suggestion_count = 0; @@ -341,12 +343,12 @@ static void console_update_suggestions(void) * - cursors should match */ - if( vg_ui.textbox.cursor_pos == 0 ) return; - if( vg_ui.textbox.cursor_pos != vg_ui.textbox.cursor_user ) return; - if( vg_console.input[ vg_ui.textbox.cursor_pos ] != '\0' ) return; + if( ctx->textbox.cursor_pos == 0 ) return; + if( ctx->textbox.cursor_pos != ctx->textbox.cursor_user ) return; + if( vg_console.input[ ctx->textbox.cursor_pos ] != '\0' ) return; - if( (vg_console.input[ vg_ui.textbox.cursor_pos -1 ] == ' ') || - (vg_console.input[ vg_ui.textbox.cursor_pos -1 ] == '\t') ) + if( (vg_console.input[ ctx->textbox.cursor_pos -1 ] == ' ') || + (vg_console.input[ ctx->textbox.cursor_pos -1 ] == '\t') ) return; char temp[128]; @@ -396,20 +398,22 @@ static void console_update_suggestions(void) */ static void _console_fetch_suggestion(void) { + ui_context *ctx = ui_current_context(); + char *target = &vg_console.input[ vg_console.suggestion_pastepos ]; if( vg_console.suggestion_select == -1 ){ strcpy( target, vg_console.input_copy ); - _ui_textbox_move_cursor( &vg_ui.textbox.cursor_user, - &vg_ui.textbox.cursor_pos, 10000, 1 ); + _ui_textbox_move_cursor( &ctx->textbox.cursor_user, + &ctx->textbox.cursor_pos, 10000, 1 ); } else{ strncpy( target, vg_console.suggestions[ vg_console.suggestion_select ].str, vg_list_size( vg_console.input )-1 ); - _ui_textbox_move_cursor( &vg_ui.textbox.cursor_user, - &vg_ui.textbox.cursor_pos, 10000, 1 ); + _ui_textbox_move_cursor( &ctx->textbox.cursor_user, + &ctx->textbox.cursor_pos, 10000, 1 ); _ui_textbox_put_char( ' ' ); } } @@ -470,6 +474,7 @@ static void console_history_get( char* buf, int entry_num ) static void _vg_console_on_up( char *buf, u32 len ) { + ui_context *ctx = ui_current_context(); if( buf == vg_console.input ){ vg_console.history_pos = VG_MAX @@ -487,26 +492,28 @@ static void _vg_console_on_up( char *buf, u32 len ) ); console_history_get( vg_console.input, vg_console.history_pos ); - _ui_textbox_move_cursor( &vg_ui.textbox.cursor_user, - &vg_ui.textbox.cursor_pos, + _ui_textbox_move_cursor( &ctx->textbox.cursor_user, + &ctx->textbox.cursor_pos, vg_list_size(vg_console.input)-1, 1); } } static void _vg_console_on_down( char *buf, u32 len ) { + ui_context *ctx = ui_current_context(); if( buf == vg_console.input ){ vg_console.history_pos = VG_MAX( 0, vg_console.history_pos-1 ); console_history_get( vg_console.input, vg_console.history_pos ); - _ui_textbox_move_cursor( &vg_ui.textbox.cursor_user, - &vg_ui.textbox.cursor_pos, + _ui_textbox_move_cursor( &ctx->textbox.cursor_user, + &ctx->textbox.cursor_pos, vg_list_size(vg_console.input)-1, 1 ); } } static void _vg_console_on_enter( char *buf, u32 len ) { + ui_context *ctx = ui_current_context(); if( buf == vg_console.input ){ if( !strlen( vg_console.input ) ) return; @@ -527,8 +534,8 @@ static void _vg_console_on_enter( char *buf, u32 len ) vg_console.history_pos = -1; vg_execute_console_input( vg_console.input, 0 ); - _ui_textbox_move_cursor( &vg_ui.textbox.cursor_user, - &vg_ui.textbox.cursor_pos, -10000, 1 ); + _ui_textbox_move_cursor( &ctx->textbox.cursor_user, + &ctx->textbox.cursor_pos, -10000, 1 ); vg_console.input[0] = '\0'; console_update_suggestions(); @@ -593,7 +600,7 @@ void vg_console_draw(void) SDL_AtomicLock( &vg_log.print_sl ); int ptr = vg_log.log_line_current; - int const fh = vg_ui.font->sy, log_lines = 32; + int const fh = ui_current_context()->font->sy, log_lines = 32; int console_lines = VG_MIN( log_lines, vg_log.log_line_count ); ui_rect rect_log = { 0, 0, vg.window_x, log_lines*fh }, @@ -638,9 +645,9 @@ void vg_console_draw(void) ui_rect rect_suggest; rect_copy( rect_input, rect_suggest ); - rect_suggest[0] += 6 + vg_ui.font->sx*vg_console.suggestion_pastepos; + rect_suggest[0] += 6 + ui_current_context()->font->sx*vg_console.suggestion_pastepos; rect_suggest[1] += rect_input[3]; - rect_suggest[2] = vg_ui.font->sx * vg_console.suggestion_maxlen; + rect_suggest[2] = ui_current_context()->font->sx * vg_console.suggestion_maxlen; rect_suggest[3] = vg_console.suggestion_count * fh; ui_fill( rect_suggest, bg_colour ); diff --git a/vg_engine.c b/vg_engine.c index fe3a7e9..48fe045 100644 --- a/vg_engine.c +++ b/vg_engine.c @@ -3,30 +3,59 @@ struct vg_engine vg = { .time_rate = 1.0, - .time_fixed_delta = VG_TIMESTEP_FIXED + .time_fixed_delta = VG_TIMESTEP_FIXED, +#ifdef VG_ANDROID + .sl_status = PTHREAD_MUTEX_INITIALIZER +#endif }; #include +#include "vg/vg_ui/imgui.c" +#include "vg/vg_ui/imgui_impl_opengl.c" +#include "vg/vg_default_font.gc" + +#ifdef VG_ANDROID + +int _wrap_sem_getvalue( sem_t *sem ) +{ + int v; + sem_getvalue( sem, &v ); + return v; +} + +#define TEMP_STATUS_LOCK pthread_mutex_lock +#define TEMP_STATUS_UNLOCK pthread_mutex_unlock +#define TEMP_SEM_POST(X) sem_post( &X ) +#define TEMP_SEM_WAIT(X) sem_wait( &X ) +#define TEMP_SEM_GET(X) _wrap_sem_getvalue( &X ) +#else +#define TEMP_STATUS_LOCK SDL_AtomicLock +#define TEMP_STATUS_UNLOCK SDL_AtomicUnlock +#define TEMP_SEM_POST(X) SDL_SemPost( X ) +#define TEMP_SEM_WAIT(X) SDL_SemWait( X ) +#define TEMP_SEM_GET(X) SDL_SemValue( X ) +#endif enum engine_status _vg_engine_status(void) { - SDL_AtomicLock( &vg.sl_status ); + TEMP_STATUS_LOCK( &vg.sl_status ); enum engine_status status = vg.engine_status; - SDL_AtomicUnlock( &vg.sl_status ); + TEMP_STATUS_UNLOCK( &vg.sl_status ); return status; } enum vg_thread_purpose vg_thread_purpose(void) { - SDL_AtomicLock( &vg.sl_status ); - - if( vg.thread_id_main == SDL_GetThreadID(NULL) ){ - SDL_AtomicUnlock( &vg.sl_status ); + TEMP_STATUS_LOCK( &vg.sl_status ); + if( vg.thread_id_main == SDL_GetThreadID(NULL) ) + { + TEMP_STATUS_UNLOCK( &vg.sl_status ); return k_thread_purpose_main; } - else{ - SDL_AtomicUnlock( &vg.sl_status ); + else + { + TEMP_STATUS_UNLOCK( &vg.sl_status ); return k_thread_purpose_loader; } } @@ -42,7 +71,11 @@ static void vg_assert_thread( enum vg_thread_purpose required ) static void _vg_opengl_sync_init(void) { +#ifdef VG_ANDROID + sem_init( &vg.sem_loader, 0, 1 ); +#else vg.sem_loader = SDL_CreateSemaphore(1); +#endif } #include "vg_console.h" @@ -53,11 +86,11 @@ static void _vg_opengl_sync_init(void) #include "vg_shader.h" #include "vg_tex.h" #include "vg_input.h" -#include "vg_imgui.h" #include "vg_lines.h" #include "vg_rigidbody_view.h" #include "vg_loader.h" #include "vg_opt.h" +#include "vg/vg_ui/imgui.h" /* Diagnostic */ static struct vg_profile vg_prof_update = {.name="update()"}, @@ -92,17 +125,32 @@ void vg_bake_shaders(void) void async_internal_complete( void *payload, u32 size ) { vg_success( "Internal async setup complete\n" ); - SDL_AtomicLock( &vg.sl_status ); - if( vg.engine_status == k_engine_status_crashed ){ - SDL_AtomicUnlock( &vg.sl_status ); +#ifdef VG_ANDROID + pthread_mutex_lock( &vg.sl_status ); +#else + TEMP_STATUS_LOCK( &vg.sl_status ); +#endif + + if( vg.engine_status == k_engine_status_crashed ) + { +#ifdef VG_ANDROID + pthread_mutex_unlock( &vg.sl_status ); +#else + TEMP_STATUS_UNLOCK( &vg.sl_status ); +#endif return; } - else{ + else + { vg.engine_status = k_engine_status_running; } - SDL_AtomicUnlock( &vg.sl_status ); +#ifdef VG_ANDROID + pthread_mutex_unlock( &vg.sl_status ); +#else + TEMP_STATUS_UNLOCK( &vg.sl_status ); +#endif } #ifdef VG_CUSTOM_SHADERS @@ -114,7 +162,9 @@ static void _vg_load_full( void *data ) vg_preload(); vg_tex2d_replace_with_error_async( &vg.tex_missing ); vg_async_stall(); - vg_ui.tex_bg = vg.tex_missing; +#ifndef VG_ANDROID + g_ui_ctx->tex_bg = vg.tex_missing; +#endif /* internal */ vg_loader_step( vg_input_init, vg_input_free ); @@ -141,14 +191,20 @@ static void _vg_process_events(void) v2_zero( vg.mouse_wheel ); v2_zero( vg.mouse_delta ); +#ifdef VG_ANDROID +#else /* SDL event loop */ SDL_Event event; - while( SDL_PollEvent( &event ) ){ - if( event.type == SDL_KEYDOWN ){ + while( SDL_PollEvent( &event ) ) + { + if( event.type == SDL_KEYDOWN ) + { if( vg_console.enabled && - (vg_ui.focused_control_type != k_ui_control_modal) ){ + (g_ui_ctx->focused_control_type != k_ui_control_modal) ) + { if( event.key.keysym.sym == SDLK_ESCAPE || - event.key.keysym.scancode == SDL_SCANCODE_GRAVE ){ + event.key.keysym.scancode == SDL_SCANCODE_GRAVE ) + { vg_console.enabled = 0; ui_defocus_all(); } @@ -160,20 +216,24 @@ static void _vg_process_events(void) event.key.keysym.sym == SDLK_p ){ console_suggest_prev(); } - else{ + else + { ui_proc_key( event.key.keysym ); } } else{ - if( event.key.keysym.scancode == SDL_SCANCODE_GRAVE ){ + if( event.key.keysym.scancode == SDL_SCANCODE_GRAVE ) + { vg_console.enabled = 1; } - else { + else + { ui_proc_key( event.key.keysym ); } } } - else if( event.type == SDL_MOUSEWHEEL ){ + else if( event.type == SDL_MOUSEWHEEL ) + { vg.mouse_wheel[0] += event.wheel.preciseX; vg.mouse_wheel[1] += event.wheel.preciseY; } @@ -217,8 +277,9 @@ static void _vg_process_events(void) } } - SDL_GetMouseState( &vg.mouse_pos[0], &vg.mouse_pos[1] ); + vg.mouse_state = SDL_GetMouseState( &vg.mouse_pos[0], &vg.mouse_pos[1] ); vg_process_inputs(); +#endif } static void _vg_gameloop_update(void) @@ -267,12 +328,17 @@ static void _vg_gameloop_render(void) /* ui */ vg.engine_stage = k_engine_stage_ui; { - ui_prerender(); - if( vg_console.enabled ){ - vg_ui.ignore_input_frames = 10; + ui_prerender( (ui_px[2]){ vg.window_x, vg.window_y }, + (ui_px[2]){ vg.mouse_pos[0], vg.mouse_pos[1] }, + vg.mouse_state ); + ui_set_screen( vg.window_x, vg.window_y ); + + if( vg_console.enabled ) + { + ui_ignore_input_frames( 10 ); vg_gui(); - vg_ui.ignore_input_frames = 0; - vg_ui.wants_mouse = 1; + ui_ignore_input_frames( 0 ); + ui_capture_mouse( 1 ); vg_console_draw(); } else vg_gui(); @@ -315,7 +381,8 @@ static void _vg_gameloop_render(void) ui_text( (ui_rect){258,4,900,900},perf,1,0,k_ui_align_left); } - ui_postrender(); + ui_postrender( vg.time_frame_delta ); + ui_post_update(); } } @@ -418,7 +485,8 @@ static void _vg_gameloop(void){ vg.time_hp_last = vg.time_hp; int post_start = 0; - while(1){ + while(1) + { vg.time_hp = SDL_GetPerformanceCounter(); u64 udt = vg.time_hp - vg.time_hp_last; vg.time_hp_last = vg.time_hp; @@ -437,7 +505,11 @@ static void _vg_gameloop(void){ if( status == k_engine_status_running ) vg_profile_begin( &vg_prof_swap ); +#ifdef VG_ANDROID + // TODO: eglSwapBuffers( +#else SDL_GL_SwapWindow( vg.window ); +#endif if( status == k_engine_status_running ) vg_profile_end( &vg_prof_swap ); @@ -505,6 +577,11 @@ static void _vg_process_launch_opts_internal( int argc, char *argv[] ) static void _vg_init_window( const char *window_name ) { +#ifdef VG_ANDROID + + + +#else vg_info( "SDL_INIT\n" ); if( SDL_Init( SDL_INIT_VIDEO ) != 0 ){ @@ -638,6 +715,7 @@ static void _vg_init_window( const char *window_name ) vg_info( "Display refresh rate: %d\n", dispmode.refresh_rate ); if( !vg.fps_limit) vg.fps_limit = vg.display_refresh_rate; +#endif } static void _vg_terminate(void) @@ -645,16 +723,22 @@ static void _vg_terminate(void) /* Shutdown */ vg_console_write_persistent(); - SDL_AtomicLock( &vg.sl_status ); + TEMP_STATUS_LOCK( &vg.sl_status ); vg.engine_status = k_engine_status_none; - SDL_AtomicUnlock( &vg.sl_status ); + TEMP_STATUS_UNLOCK( &vg.sl_status ); vg_loader_free(); vg_success( "If you see this it means everything went.. \"well\".....\n" ); +#ifdef VG_ANDROID + + + +#else SDL_GL_DeleteContext( vg.gl_context ); SDL_Quit(); +#endif exit(0); } @@ -685,7 +769,11 @@ void vg_enter( int argc, char *argv[], const char *window_name ) vg_async_init(); SDL_SetRelativeMouseMode(1); +#ifdef VG_ANDROID + vg.thread_id_main = pthread_self(); +#else vg.thread_id_main = SDL_GetThreadID(NULL); +#endif /* Opengl-required systems */ vg_ui_init(); @@ -708,9 +796,9 @@ void vg_fatal_error( const char *fmt, ... ) vg_print_backtrace(); - SDL_AtomicLock( &vg.sl_status ); + TEMP_STATUS_LOCK( &vg.sl_status ); vg.engine_status = k_engine_status_crashed; - SDL_AtomicUnlock( &vg.sl_status ); + TEMP_STATUS_UNLOCK( &vg.sl_status ); if( vg_thread_purpose() == k_thread_purpose_loader ) { @@ -885,8 +973,8 @@ void vg_settings_ui_header( ui_rect inout_panel, const char *name ) bool vg_settings_apply_button( ui_rect inout_panel, bool validated ) { ui_rect last_row; - ui_px height = (vg_ui.font->sy + 18) * k_ui_scale; - ui_split( inout_panel, k_ui_axis_h, -height, k_ui_padding, + ui_px height = ui_standard_widget_height( 1 ); + ui_split( inout_panel, k_ui_axis_h, -height, 8, inout_panel, last_row ); const char *string = "Apply"; @@ -908,16 +996,23 @@ bool vg_settings_apply_button( ui_rect inout_panel, bool validated ) return 0; } -static void vg_settings_video_apply(void){ - if( vg_settings_enum_diff( &vg_settings.screenmode ) ){ +static void vg_settings_video_apply(void) +{ +#ifdef VG_ANDROID +#else + if( vg_settings_enum_diff( &vg_settings.screenmode ) ) + { vg.screen_mode = vg_settings.screenmode.new_value; - if( (vg.screen_mode == 0) || (vg.screen_mode == 1) ){ + if( (vg.screen_mode == 0) || (vg.screen_mode == 1) ) + { SDL_DisplayMode video_mode; - if( SDL_GetDesktopDisplayMode( 0, &video_mode ) ){ + if( SDL_GetDesktopDisplayMode( 0, &video_mode ) ) + { vg_error("SDL_GetDesktopDisplayMode failed: %s\n", SDL_GetError()); } - else { + else + { //vg.display_refresh_rate = video_mode.refresh_rate; vg.window_x = video_mode.w; vg.window_y = video_mode.h; @@ -937,6 +1032,7 @@ static void vg_settings_video_apply(void){ SDL_SetWindowMaximumSize( vg.window, 4096, 4096 ); } } +#endif vg.fps_limit = vg_settings.fps_limit.new_value; vg.quality_profile = vg_settings.quality.new_value; @@ -1114,7 +1210,7 @@ static void vg_settings_gui(void) ui_rect screen = { 0, 0, vg.window_x, vg.window_y }; ui_rect window = { 0, 0, 1000, 700 }; ui_rect_center( screen, window ); - vg_ui.wants_mouse = 1; + ui_capture_mouse( 1 ); ui_fill( window, ui_colour( k_ui_bg+1 ) ); ui_outline( window, 1, ui_colour( k_ui_bg+7 ), 0 ); @@ -1178,7 +1274,6 @@ int AmdPowerXpressRequestHighPerformance = 1; #include "vg_camera.c" #include "vg_lines.c" #include "vg_console.c" -#include "vg_imgui.c" #include "vg_input.c" #include "vg_io.c" #include "vg_loader.c" diff --git a/vg_engine.h b/vg_engine.h index 81bbda5..4f56768 100644 --- a/vg_engine.h +++ b/vg_engine.h @@ -69,15 +69,24 @@ #endif #define VG_ENGINE -#define SDL_MAIN_HANDLED +#include "vg_opengl.h" -#include "dep/glad/glad.h" -#include "dep/sdl/include/SDL.h" +#ifdef VG_ANDROID + +/* TODO: android gl includes */ +#include +#include + +#else + #define SDL_MAIN_HANDLED + #include "dep/sdl/include/SDL.h" +#endif #include "vg_platform.h" #include "vg_mem.h" #include "vg_m.h" -#include "vg_imgui.h" +#include "vg_font.h" +#include "vg_ui/imgui.h" #include @@ -116,19 +125,28 @@ enum vg_thread_purpose k_thread_purpose_loader }; -struct vg_engine { + +struct vg_engine +{ /* Engine sync */ +#ifdef VG_ANDROID + pthread_mutex_t sl_status; + pthread_t thread_id_main, thread_id_loader; + sem_t sem_loader; +#else SDL_Window *window; SDL_GLContext gl_context; - SDL_sem *sem_loader; /* allows only one loader at a time */ - jmp_buf env_loader_exit; SDL_threadID thread_id_main, thread_id_loader; - void *thread_data; SDL_SpinLock sl_status; +#endif + + jmp_buf env_loader_exit; + void *thread_data; + enum engine_status{ k_engine_status_none, k_engine_status_load_internal, @@ -160,7 +178,7 @@ struct vg_engine { } vsync_feature; - i32 mouse_pos[2]; + i32 mouse_pos[2], mouse_state; v2f mouse_delta, mouse_wheel; diff --git a/vg_font.h b/vg_font.h index 5fcb29b..9a17561 100644 --- a/vg_font.h +++ b/vg_font.h @@ -1,3 +1,5 @@ +#pragma once + typedef struct vg_font_char vg_font_char; typedef struct vg_font_face vg_font_face; typedef struct vg_font_sheet vg_font_sheet; diff --git a/vg_loader.c b/vg_loader.c index 55ab081..b497f45 100644 --- a/vg_loader.c +++ b/vg_loader.c @@ -126,16 +126,26 @@ void vg_loader_render(void) vg.loader_ring = 1.0f; } -static int _vg_loader_thread( void *pfn ){ +static int _vg_loader_thread( void *pfn ) +{ +#ifdef VG_ANDROID + vg.thread_id_loader = pthread_self(); +#else + vg.thread_id_loader = SDL_GetThreadID(NULL); +#endif + if( setjmp( vg.env_loader_exit ) ) + { + vg.thread_id_loader = 0; return 0; + } /* Run client loader */ //vg_info( "Starting client loader thread @%p\n", pfn ); void (*call_func)(void *data) = pfn; call_func( vg.thread_data ); - SDL_SemPost( vg.sem_loader ); + TEMP_SEM_POST( vg.sem_loader ); vg.thread_id_loader = 0; return 0; @@ -143,7 +153,8 @@ static int _vg_loader_thread( void *pfn ){ int vg_loader_availible(void) { - if( SDL_SemValue( vg.sem_loader ) ){ + if( TEMP_SEM_GET( vg.sem_loader ) ) + { if( !(vg_async.start) ) return 1; } @@ -153,7 +164,7 @@ int vg_loader_availible(void) void vg_loader_start( void(*pfn)(void *data), void *data ) { - SDL_SemWait( vg.sem_loader ); + TEMP_SEM_WAIT( vg.sem_loader ); vg.thread_data = data; SDL_CreateThread( _vg_loader_thread, "vg: loader", pfn ); diff --git a/vg_log.c b/vg_log.c index 84807da..8c2edc2 100644 --- a/vg_log.c +++ b/vg_log.c @@ -114,7 +114,7 @@ void vg_logx( FILE *file, void vg_print_backtrace(void) { -#ifndef _WIN32 +#if !defined(_WIN32) && !defined(VG_ANDROID) void *array[20]; char **strings; int size, i; diff --git a/vg_log.h b/vg_log.h index cf2b737..bb7411b 100644 --- a/vg_log.h +++ b/vg_log.h @@ -33,7 +33,7 @@ #define PRINTF_v3f( V3 ) "%.4f %.4f %.4f\n", V3[0], V3[1], V3[2] #define PRINTF_v4f( V4 ) "%.4f %.4f %.4f %.4f\n", V4[0], V4[1], V4[2], V4[3] -#ifdef _WIN32 +#if VG_32 #define PRINTF_U64 "%llu" #else #define PRINTF_U64 "%lu" diff --git a/vg_msg.c b/vg_msg.c index 65d8ebd..16ce1d8 100644 --- a/vg_msg.c +++ b/vg_msg.c @@ -472,7 +472,7 @@ void vg_msg_print( vg_msg *msg, u32 len ) if( base == k_vg_msg_unsigned ){ printf( -#ifdef _WIN32 +#ifdef VG_32 "%llu" #else "%lu" @@ -481,7 +481,7 @@ void vg_msg_print( vg_msg *msg, u32 len ) } else if( base == k_vg_msg_signed ){ printf( -#ifdef _WIN32 +#ifdef VG_32 "%lld" #else "%ld" diff --git a/vg_opengl.h b/vg_opengl.h new file mode 100644 index 0000000..f112ce9 --- /dev/null +++ b/vg_opengl.h @@ -0,0 +1,6 @@ +#ifdef VG_ANDROID +#include +#include +#else +#include "dep/glad/glad.h" +#endif diff --git a/vg_platform/android/AndroidManifest.xml b/vg_platform/android/AndroidManifest.xml new file mode 100644 index 0000000..8fdb601 --- /dev/null +++ b/vg_platform/android/AndroidManifest.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + diff --git a/vg_platform/android/android_native_app_glue.c b/vg_platform/android/android_native_app_glue.c new file mode 100644 index 0000000..1e63c5e --- /dev/null +++ b/vg_platform/android/android_native_app_glue.c @@ -0,0 +1,457 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "android_native_app_glue.h" + +#include + +#include +#include +#include +#include + +#include + +#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "threaded_app", __VA_ARGS__)) +#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "threaded_app", __VA_ARGS__)) + +/* For debug builds, always enable the debug traces in this library */ +#ifndef NDEBUG +# define LOGV(...) ((void)__android_log_print(ANDROID_LOG_VERBOSE, "threaded_app", __VA_ARGS__)) +#else +# define LOGV(...) ((void)0) +#endif + +static void free_saved_state(struct android_app* android_app) { + pthread_mutex_lock(&android_app->mutex); + if (android_app->savedState != NULL) { + free(android_app->savedState); + android_app->savedState = NULL; + android_app->savedStateSize = 0; + } + pthread_mutex_unlock(&android_app->mutex); +} + +int8_t android_app_read_cmd(struct android_app* android_app) { + int8_t cmd; + if (read(android_app->msgread, &cmd, sizeof(cmd)) != sizeof(cmd)) { + LOGE("No data on command pipe!"); + return -1; + } + if (cmd == APP_CMD_SAVE_STATE) free_saved_state(android_app); + return cmd; +} + +static void print_cur_config(struct android_app* android_app) { + char lang[2], country[2]; + AConfiguration_getLanguage(android_app->config, lang); + AConfiguration_getCountry(android_app->config, country); + + LOGV("Config: mcc=%d mnc=%d lang=%c%c cnt=%c%c orien=%d touch=%d dens=%d " + "keys=%d nav=%d keysHid=%d navHid=%d sdk=%d size=%d long=%d " + "modetype=%d modenight=%d", + AConfiguration_getMcc(android_app->config), + AConfiguration_getMnc(android_app->config), + lang[0], lang[1], country[0], country[1], + AConfiguration_getOrientation(android_app->config), + AConfiguration_getTouchscreen(android_app->config), + AConfiguration_getDensity(android_app->config), + AConfiguration_getKeyboard(android_app->config), + AConfiguration_getNavigation(android_app->config), + AConfiguration_getKeysHidden(android_app->config), + AConfiguration_getNavHidden(android_app->config), + AConfiguration_getSdkVersion(android_app->config), + AConfiguration_getScreenSize(android_app->config), + AConfiguration_getScreenLong(android_app->config), + AConfiguration_getUiModeType(android_app->config), + AConfiguration_getUiModeNight(android_app->config)); +} + +void android_app_pre_exec_cmd(struct android_app* android_app, int8_t cmd) { + switch (cmd) { + case APP_CMD_INPUT_CHANGED: + LOGV("APP_CMD_INPUT_CHANGED"); + pthread_mutex_lock(&android_app->mutex); + if (android_app->inputQueue != NULL) { + AInputQueue_detachLooper(android_app->inputQueue); + } + android_app->inputQueue = android_app->pendingInputQueue; + if (android_app->inputQueue != NULL) { + LOGV("Attaching input queue to looper"); + AInputQueue_attachLooper(android_app->inputQueue, + android_app->looper, LOOPER_ID_INPUT, NULL, + &android_app->inputPollSource); + } + pthread_cond_broadcast(&android_app->cond); + pthread_mutex_unlock(&android_app->mutex); + break; + + case APP_CMD_INIT_WINDOW: + LOGV("APP_CMD_INIT_WINDOW"); + pthread_mutex_lock(&android_app->mutex); + android_app->window = android_app->pendingWindow; + pthread_cond_broadcast(&android_app->cond); + pthread_mutex_unlock(&android_app->mutex); + break; + + case APP_CMD_TERM_WINDOW: + LOGV("APP_CMD_TERM_WINDOW"); + pthread_cond_broadcast(&android_app->cond); + break; + + case APP_CMD_RESUME: + case APP_CMD_START: + case APP_CMD_PAUSE: + case APP_CMD_STOP: + LOGV("activityState=%d", cmd); + pthread_mutex_lock(&android_app->mutex); + android_app->activityState = cmd; + pthread_cond_broadcast(&android_app->cond); + pthread_mutex_unlock(&android_app->mutex); + break; + + case APP_CMD_CONFIG_CHANGED: + LOGV("APP_CMD_CONFIG_CHANGED"); + AConfiguration_fromAssetManager(android_app->config, + android_app->activity->assetManager); + print_cur_config(android_app); + break; + + case APP_CMD_DESTROY: + LOGV("APP_CMD_DESTROY"); + android_app->destroyRequested = 1; + break; + } +} + +void android_app_post_exec_cmd(struct android_app* android_app, int8_t cmd) { + switch (cmd) { + case APP_CMD_TERM_WINDOW: + LOGV("APP_CMD_TERM_WINDOW"); + pthread_mutex_lock(&android_app->mutex); + android_app->window = NULL; + pthread_cond_broadcast(&android_app->cond); + pthread_mutex_unlock(&android_app->mutex); + break; + + case APP_CMD_SAVE_STATE: + LOGV("APP_CMD_SAVE_STATE"); + pthread_mutex_lock(&android_app->mutex); + android_app->stateSaved = 1; + pthread_cond_broadcast(&android_app->cond); + pthread_mutex_unlock(&android_app->mutex); + break; + + case APP_CMD_RESUME: + free_saved_state(android_app); + break; + } +} + +void app_dummy() { +} + +static void android_app_destroy(struct android_app* android_app) { + LOGV("android_app_destroy!"); + free_saved_state(android_app); + pthread_mutex_lock(&android_app->mutex); + if (android_app->inputQueue != NULL) { + AInputQueue_detachLooper(android_app->inputQueue); + } + AConfiguration_delete(android_app->config); + android_app->destroyed = 1; + pthread_cond_broadcast(&android_app->cond); + pthread_mutex_unlock(&android_app->mutex); + // Can't touch android_app object after this. +} + +static void process_input(struct android_app* app, struct android_poll_source* source) { + AInputEvent* event = NULL; + while (AInputQueue_getEvent(app->inputQueue, &event) >= 0) { + LOGV("New input event: type=%d", AInputEvent_getType(event)); + if (AInputQueue_preDispatchEvent(app->inputQueue, event)) { + continue; + } + int32_t handled = 0; + if (app->onInputEvent != NULL) handled = app->onInputEvent(app, event); + AInputQueue_finishEvent(app->inputQueue, event, handled); + } +} + +static void process_cmd(struct android_app* app, struct android_poll_source* source) { + int8_t cmd = android_app_read_cmd(app); + android_app_pre_exec_cmd(app, cmd); + if (app->onAppCmd != NULL) app->onAppCmd(app, cmd); + android_app_post_exec_cmd(app, cmd); +} + +static void* android_app_entry(void* param) { + struct android_app* android_app = (struct android_app*)param; + + android_app->config = AConfiguration_new(); + AConfiguration_fromAssetManager(android_app->config, android_app->activity->assetManager); + + print_cur_config(android_app); + + android_app->cmdPollSource.id = LOOPER_ID_MAIN; + android_app->cmdPollSource.app = android_app; + android_app->cmdPollSource.process = process_cmd; + android_app->inputPollSource.id = LOOPER_ID_INPUT; + android_app->inputPollSource.app = android_app; + android_app->inputPollSource.process = process_input; + + ALooper* looper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS); + ALooper_addFd(looper, android_app->msgread, LOOPER_ID_MAIN, ALOOPER_EVENT_INPUT, NULL, + &android_app->cmdPollSource); + android_app->looper = looper; + + pthread_mutex_lock(&android_app->mutex); + android_app->running = 1; + pthread_cond_broadcast(&android_app->cond); + pthread_mutex_unlock(&android_app->mutex); + + android_main(android_app); + + android_app_destroy(android_app); + return NULL; +} + +// -------------------------------------------------------------------- +// Native activity interaction (called from main thread) +// -------------------------------------------------------------------- + +static struct android_app* android_app_create(ANativeActivity* activity, + void* savedState, size_t savedStateSize) { + struct android_app* android_app = calloc(1, sizeof(struct android_app)); + android_app->activity = activity; + + pthread_mutex_init(&android_app->mutex, NULL); + pthread_cond_init(&android_app->cond, NULL); + + if (savedState != NULL) { + android_app->savedState = malloc(savedStateSize); + android_app->savedStateSize = savedStateSize; + memcpy(android_app->savedState, savedState, savedStateSize); + } + + int msgpipe[2]; + if (pipe(msgpipe)) { + LOGE("could not create pipe: %s", strerror(errno)); + return NULL; + } + android_app->msgread = msgpipe[0]; + android_app->msgwrite = msgpipe[1]; + + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_create(&android_app->thread, &attr, android_app_entry, android_app); + + // Wait for thread to start. + pthread_mutex_lock(&android_app->mutex); + while (!android_app->running) { + pthread_cond_wait(&android_app->cond, &android_app->mutex); + } + pthread_mutex_unlock(&android_app->mutex); + + return android_app; +} + +static void android_app_write_cmd(struct android_app* android_app, int8_t cmd) { + if (write(android_app->msgwrite, &cmd, sizeof(cmd)) != sizeof(cmd)) { + LOGE("Failure writing android_app cmd: %s", strerror(errno)); + } +} + +static void android_app_set_input(struct android_app* android_app, AInputQueue* inputQueue) { + pthread_mutex_lock(&android_app->mutex); + android_app->pendingInputQueue = inputQueue; + android_app_write_cmd(android_app, APP_CMD_INPUT_CHANGED); + while (android_app->inputQueue != android_app->pendingInputQueue) { + pthread_cond_wait(&android_app->cond, &android_app->mutex); + } + pthread_mutex_unlock(&android_app->mutex); +} + +static void android_app_set_window(struct android_app* android_app, ANativeWindow* window) { + pthread_mutex_lock(&android_app->mutex); + if (android_app->pendingWindow != NULL) { + android_app_write_cmd(android_app, APP_CMD_TERM_WINDOW); + } + android_app->pendingWindow = window; + if (window != NULL) { + android_app_write_cmd(android_app, APP_CMD_INIT_WINDOW); + } + while (android_app->window != android_app->pendingWindow) { + pthread_cond_wait(&android_app->cond, &android_app->mutex); + } + pthread_mutex_unlock(&android_app->mutex); +} + +static void android_app_set_activity_state(struct android_app* android_app, int8_t cmd) { + pthread_mutex_lock(&android_app->mutex); + android_app_write_cmd(android_app, cmd); + while (android_app->activityState != cmd) { + pthread_cond_wait(&android_app->cond, &android_app->mutex); + } + pthread_mutex_unlock(&android_app->mutex); +} + +static void android_app_free(struct android_app* android_app) { + pthread_mutex_lock(&android_app->mutex); + android_app_write_cmd(android_app, APP_CMD_DESTROY); + while (!android_app->destroyed) { + pthread_cond_wait(&android_app->cond, &android_app->mutex); + } + pthread_mutex_unlock(&android_app->mutex); + + close(android_app->msgread); + close(android_app->msgwrite); + pthread_cond_destroy(&android_app->cond); + pthread_mutex_destroy(&android_app->mutex); + free(android_app); +} + +static struct android_app* ToApp(ANativeActivity* activity) { + return (struct android_app*) activity->instance; +} + +static void onDestroy(ANativeActivity* activity) { + LOGV("Destroy: %p", activity); + android_app_free(ToApp(activity)); +} + +static void onStart(ANativeActivity* activity) { + LOGV("Start: %p", activity); + android_app_set_activity_state(ToApp(activity), APP_CMD_START); +} + +static void onResume(ANativeActivity* activity) { + LOGV("Resume: %p", activity); + android_app_set_activity_state(ToApp(activity), APP_CMD_RESUME); +} + +static void* onSaveInstanceState(ANativeActivity* activity, size_t* outLen) { + LOGV("SaveInstanceState: %p", activity); + + struct android_app* android_app = ToApp(activity); + void* savedState = NULL; + pthread_mutex_lock(&android_app->mutex); + android_app->stateSaved = 0; + android_app_write_cmd(android_app, APP_CMD_SAVE_STATE); + while (!android_app->stateSaved) { + pthread_cond_wait(&android_app->cond, &android_app->mutex); + } + + if (android_app->savedState != NULL) { + savedState = android_app->savedState; + *outLen = android_app->savedStateSize; + android_app->savedState = NULL; + android_app->savedStateSize = 0; + } + + pthread_mutex_unlock(&android_app->mutex); + + return savedState; +} + +static void onPause(ANativeActivity* activity) { + LOGV("Pause: %p", activity); + android_app_set_activity_state(ToApp(activity), APP_CMD_PAUSE); +} + +static void onStop(ANativeActivity* activity) { + LOGV("Stop: %p", activity); + android_app_set_activity_state(ToApp(activity), APP_CMD_STOP); +} + +static void onConfigurationChanged(ANativeActivity* activity) { + LOGV("ConfigurationChanged: %p", activity); + android_app_write_cmd(ToApp(activity), APP_CMD_CONFIG_CHANGED); +} + +static void onContentRectChanged(ANativeActivity* activity, const ARect* r) { + LOGV("ContentRectChanged: l=%d,t=%d,r=%d,b=%d", r->left, r->top, r->right, r->bottom); + struct android_app* android_app = ToApp(activity); + pthread_mutex_lock(&android_app->mutex); + android_app->contentRect = *r; + pthread_mutex_unlock(&android_app->mutex); + android_app_write_cmd(ToApp(activity), APP_CMD_CONTENT_RECT_CHANGED); +} + +static void onLowMemory(ANativeActivity* activity) { + LOGV("LowMemory: %p", activity); + android_app_write_cmd(ToApp(activity), APP_CMD_LOW_MEMORY); +} + +static void onWindowFocusChanged(ANativeActivity* activity, int focused) { + LOGV("WindowFocusChanged: %p -- %d", activity, focused); + android_app_write_cmd(ToApp(activity), focused ? APP_CMD_GAINED_FOCUS : APP_CMD_LOST_FOCUS); +} + +static void onNativeWindowCreated(ANativeActivity* activity, ANativeWindow* window) { + LOGV("NativeWindowCreated: %p -- %p", activity, window); + android_app_set_window(ToApp(activity), window); +} + +static void onNativeWindowDestroyed(ANativeActivity* activity, ANativeWindow* window) { + LOGV("NativeWindowDestroyed: %p -- %p", activity, window); + android_app_set_window(ToApp(activity), NULL); +} + +static void onNativeWindowRedrawNeeded(ANativeActivity* activity, ANativeWindow* window) { + LOGV("NativeWindowRedrawNeeded: %p -- %p", activity, window); + android_app_write_cmd(ToApp(activity), APP_CMD_WINDOW_REDRAW_NEEDED); +} + +static void onNativeWindowResized(ANativeActivity* activity, ANativeWindow* window) { + LOGV("NativeWindowResized: %p -- %p", activity, window); + android_app_write_cmd(ToApp(activity), APP_CMD_WINDOW_RESIZED); +} + +static void onInputQueueCreated(ANativeActivity* activity, AInputQueue* queue) { + LOGV("InputQueueCreated: %p -- %p", activity, queue); + android_app_set_input(ToApp(activity), queue); +} + +static void onInputQueueDestroyed(ANativeActivity* activity, AInputQueue* queue) { + LOGV("InputQueueDestroyed: %p -- %p", activity, queue); + android_app_set_input(ToApp(activity), NULL); +} + +JNIEXPORT +void ANativeActivity_onCreate(ANativeActivity* activity, void* savedState, size_t savedStateSize) { + LOGV("Creating: %p", activity); + + activity->callbacks->onConfigurationChanged = onConfigurationChanged; + activity->callbacks->onContentRectChanged = onContentRectChanged; + activity->callbacks->onDestroy = onDestroy; + activity->callbacks->onInputQueueCreated = onInputQueueCreated; + activity->callbacks->onInputQueueDestroyed = onInputQueueDestroyed; + activity->callbacks->onLowMemory = onLowMemory; + activity->callbacks->onNativeWindowCreated = onNativeWindowCreated; + activity->callbacks->onNativeWindowDestroyed = onNativeWindowDestroyed; + activity->callbacks->onNativeWindowRedrawNeeded = onNativeWindowRedrawNeeded; + activity->callbacks->onNativeWindowResized = onNativeWindowResized; + activity->callbacks->onPause = onPause; + activity->callbacks->onResume = onResume; + activity->callbacks->onSaveInstanceState = onSaveInstanceState; + activity->callbacks->onStart = onStart; + activity->callbacks->onStop = onStop; + activity->callbacks->onWindowFocusChanged = onWindowFocusChanged; + + activity->instance = android_app_create(activity, savedState, savedStateSize); +} diff --git a/vg_platform/android/android_native_app_glue.h b/vg_platform/android/android_native_app_glue.h new file mode 100644 index 0000000..35a786e --- /dev/null +++ b/vg_platform/android/android_native_app_glue.h @@ -0,0 +1,350 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * The native activity interface provided by + * is based on a set of application-provided callbacks that will be called + * by the Activity's main thread when certain events occur. + * + * This means that each one of this callbacks _should_ _not_ block, or they + * risk having the system force-close the application. This programming + * model is direct, lightweight, but constraining. + * + * The 'android_native_app_glue' static library is used to provide a different + * execution model where the application can implement its own main event + * loop in a different thread instead. Here's how it works: + * + * 1/ The application must provide a function named "android_main()" that + * will be called when the activity is created, in a new thread that is + * distinct from the activity's main thread. + * + * 2/ android_main() receives a pointer to a valid "android_app" structure + * that contains references to other important objects, e.g. the + * ANativeActivity object instance the application is running in. + * + * 3/ the "android_app" object holds an ALooper instance that already + * listens to two important things: + * + * - activity lifecycle events (e.g. "pause", "resume"). See APP_CMD_XXX + * declarations below. + * + * - input events coming from the AInputQueue attached to the activity. + * + * Each of these correspond to an ALooper identifier returned by + * ALooper_pollOnce with values of LOOPER_ID_MAIN and LOOPER_ID_INPUT, + * respectively. + * + * Your application can use the same ALooper to listen to additional + * file-descriptors. They can either be callback based, or with return + * identifiers starting with LOOPER_ID_USER. + * + * 4/ Whenever you receive a LOOPER_ID_MAIN or LOOPER_ID_INPUT event, + * the returned data will point to an android_poll_source structure. You + * can call the process() function on it, and fill in android_app->onAppCmd + * and android_app->onInputEvent to be called for your own processing + * of the event. + * + * Alternatively, you can call the low-level functions to read and process + * the data directly... look at the process_cmd() and process_input() + * implementations in the glue to see how to do this. + * + * See the sample named "native-activity" that comes with the NDK with a + * full usage example. Also look at the JavaDoc of NativeActivity. + */ + +struct android_app; + +/** + * Data associated with an ALooper fd that will be returned as the "outData" + * when that source has data ready. + */ +struct android_poll_source { + // The identifier of this source. May be LOOPER_ID_MAIN or + // LOOPER_ID_INPUT. + int32_t id; + + // The android_app this ident is associated with. + struct android_app* app; + + // Function to call to perform the standard processing of data from + // this source. + void (*process)(struct android_app* app, struct android_poll_source* source); +}; + +/** + * This is the interface for the standard glue code of a threaded + * application. In this model, the application's code is running + * in its own thread separate from the main thread of the process. + * It is not required that this thread be associated with the Java + * VM, although it will need to be in order to make JNI calls any + * Java objects. + */ +struct android_app { + // The application can place a pointer to its own state object + // here if it likes. + void* userData; + + // Fill this in with the function to process main app commands (APP_CMD_*) + void (*onAppCmd)(struct android_app* app, int32_t cmd); + + // Fill this in with the function to process input events. At this point + // the event has already been pre-dispatched, and it will be finished upon + // return. Return 1 if you have handled the event, 0 for any default + // dispatching. + int32_t (*onInputEvent)(struct android_app* app, AInputEvent* event); + + // The ANativeActivity object instance that this app is running in. + ANativeActivity* activity; + + // The current configuration the app is running in. + AConfiguration* config; + + // This is the last instance's saved state, as provided at creation time. + // It is NULL if there was no state. You can use this as you need; the + // memory will remain around until you call android_app_exec_cmd() for + // APP_CMD_RESUME, at which point it will be freed and savedState set to NULL. + // These variables should only be changed when processing a APP_CMD_SAVE_STATE, + // at which point they will be initialized to NULL and you can malloc your + // state and place the information here. In that case the memory will be + // freed for you later. + void* savedState; + size_t savedStateSize; + + // The ALooper associated with the app's thread. + ALooper* looper; + + // When non-NULL, this is the input queue from which the app will + // receive user input events. + AInputQueue* inputQueue; + + // When non-NULL, this is the window surface that the app can draw in. + ANativeWindow* window; + + // Current content rectangle of the window; this is the area where the + // window's content should be placed to be seen by the user. + ARect contentRect; + + // Current state of the app's activity. May be either APP_CMD_START, + // APP_CMD_RESUME, APP_CMD_PAUSE, or APP_CMD_STOP; see below. + int activityState; + + // This is non-zero when the application's NativeActivity is being + // destroyed and waiting for the app thread to complete. + int destroyRequested; + + // ------------------------------------------------- + // Below are "private" implementation of the glue code. + + pthread_mutex_t mutex; + pthread_cond_t cond; + + int msgread; + int msgwrite; + + pthread_t thread; + + struct android_poll_source cmdPollSource; + struct android_poll_source inputPollSource; + + int running; + int stateSaved; + int destroyed; + int redrawNeeded; + AInputQueue* pendingInputQueue; + ANativeWindow* pendingWindow; + ARect pendingContentRect; +}; + +enum { + /** + * Looper data ID of commands coming from the app's main thread, which + * is returned as an identifier from ALooper_pollOnce(). The data for this + * identifier is a pointer to an android_poll_source structure. + * These can be retrieved and processed with android_app_read_cmd() + * and android_app_exec_cmd(). + */ + LOOPER_ID_MAIN = 1, + + /** + * Looper data ID of events coming from the AInputQueue of the + * application's window, which is returned as an identifier from + * ALooper_pollOnce(). The data for this identifier is a pointer to an + * android_poll_source structure. These can be read via the inputQueue + * object of android_app. + */ + LOOPER_ID_INPUT = 2, + + /** + * Start of user-defined ALooper identifiers. + */ + LOOPER_ID_USER = 3, +}; + +enum { + /** + * Command from main thread: the AInputQueue has changed. Upon processing + * this command, android_app->inputQueue will be updated to the new queue + * (or NULL). + */ + APP_CMD_INPUT_CHANGED, + + /** + * Command from main thread: a new ANativeWindow is ready for use. Upon + * receiving this command, android_app->window will contain the new window + * surface. + */ + APP_CMD_INIT_WINDOW, + + /** + * Command from main thread: the existing ANativeWindow needs to be + * terminated. Upon receiving this command, android_app->window still + * contains the existing window; after calling android_app_exec_cmd + * it will be set to NULL. + */ + APP_CMD_TERM_WINDOW, + + /** + * Command from main thread: the current ANativeWindow has been resized. + * Please redraw with its new size. + */ + APP_CMD_WINDOW_RESIZED, + + /** + * Command from main thread: the system needs that the current ANativeWindow + * be redrawn. You should redraw the window before handing this to + * android_app_exec_cmd() in order to avoid transient drawing glitches. + */ + APP_CMD_WINDOW_REDRAW_NEEDED, + + /** + * Command from main thread: the content area of the window has changed, + * such as from the soft input window being shown or hidden. You can + * find the new content rect in android_app::contentRect. + */ + APP_CMD_CONTENT_RECT_CHANGED, + + /** + * Command from main thread: the app's activity window has gained + * input focus. + */ + APP_CMD_GAINED_FOCUS, + + /** + * Command from main thread: the app's activity window has lost + * input focus. + */ + APP_CMD_LOST_FOCUS, + + /** + * Command from main thread: the current device configuration has changed. + */ + APP_CMD_CONFIG_CHANGED, + + /** + * Command from main thread: the system is running low on memory. + * Try to reduce your memory use. + */ + APP_CMD_LOW_MEMORY, + + /** + * Command from main thread: the app's activity has been started. + */ + APP_CMD_START, + + /** + * Command from main thread: the app's activity has been resumed. + */ + APP_CMD_RESUME, + + /** + * Command from main thread: the app should generate a new saved state + * for itself, to restore from later if needed. If you have saved state, + * allocate it with malloc and place it in android_app.savedState with + * the size in android_app.savedStateSize. The will be freed for you + * later. + */ + APP_CMD_SAVE_STATE, + + /** + * Command from main thread: the app's activity has been paused. + */ + APP_CMD_PAUSE, + + /** + * Command from main thread: the app's activity has been stopped. + */ + APP_CMD_STOP, + + /** + * Command from main thread: the app's activity is being destroyed, + * and waiting for the app thread to clean up and exit before proceeding. + */ + APP_CMD_DESTROY, +}; + +/** + * Call when ALooper_pollAll() returns LOOPER_ID_MAIN, reading the next + * app command message. + */ +int8_t android_app_read_cmd(struct android_app* android_app); + +/** + * Call with the command returned by android_app_read_cmd() to do the + * initial pre-processing of the given command. You can perform your own + * actions for the command after calling this function. + */ +void android_app_pre_exec_cmd(struct android_app* android_app, int8_t cmd); + +/** + * Call with the command returned by android_app_read_cmd() to do the + * final post-processing of the given command. You must have done your own + * actions for the command before calling this function. + */ +void android_app_post_exec_cmd(struct android_app* android_app, int8_t cmd); + +/** + * No-op function that used to be used to prevent the linker from stripping app + * glue code. No longer necessary, since __attribute__((visibility("default"))) + * does this for us. + */ +__attribute__(( + deprecated("Calls to app_dummy are no longer necessary. See " + "https://github.com/android-ndk/ndk/issues/381."))) void +app_dummy(); + +/** + * This is the function that application code must implement, representing + * the main entry to the app. + */ +extern void android_main(struct android_app* app); + +#ifdef __cplusplus +} +#endif diff --git a/vg_platform/android/debug.keystore b/vg_platform/android/debug.keystore new file mode 100644 index 0000000000000000000000000000000000000000..6affde7d2d20bfb8ceeb8ce777d67c3e3fa85527 GIT binary patch literal 2150 zcmbW1Svb^-8^>oCVKA0#F-Vq)-)NA1i9*7$WD6CtG$Fg3WC=rMDNE+qWjU5a$~JXm zaEK^Iq_K<9%veVD?eCobxj5J7;(PPCc%S$EzR&YHU>-0*AQ0pz;J<^s?&lHc@8jX& zc_YN@mS@BfPZjB0)dGRwP#_j|2yi1f6cJz;SOLNd2E#$HSQy<_`eEO?=}m><+eRyV z(_{&Oi}(i33=JGrisS4RX^|J4foAEEMIjEeA)aZZbHW|Twk3UQn0vXeDe@?ELmbn#Rq)46B~xHe8ACj(^q=R$?s(yxdlDKbWCF@Gx82TSBl+0 zR+neG%*7!CK35_x$0ylohsDQTkmIT&msKJPTIKXqxw1bQ@RzYsUO`OO)&dWsf3XJ4 zR8)&p6LOl5)i%9y?bhz>p(*u?-_tM6dGUUp={awDY9#2UFn)l*wz4~8pXm4G-D&HD z%%ZlPofVX%zOW)c%!+)qN6|JfdZ=1Czh)o*NR4}Xz17-Ue1cbM_FZ}GB_gKXuy}U9mG0i8pR);O#Vi2vwQX8f39G>OG z#LQEfbAgl}uJB8P5R2nqA)Zzeks^z7PauJO^xIFsL(K+7@xJXvSy7pe%^NC)b`dY#JMiy35lPGFKb@9G&T1=*4jK7i+~Ne>k2Ih9fy6mw3KkSy~eP zUH{Rz{LNL7ctr|4X`bG9xk5bzhq#sKD8!eu+UFW-^8U_uZl9=wO zW^>x`M>&P{vAem&Ub%$5h$|Tn&6+rWOkS51zN;e?t6>(+YkXX9Tc*ErdE36cxS6t7 zPc6i@Mculv`Ah>{qMoBK(r2%~^WNx-bCi5}z`(4$rvAQSQ2gRK zak{r+>J2HT0d2}f4eH>`Jp$yE)-)~pFs^NfXthP>XTti%UtM949*?^R{yI3uR-n1C zek1jxU^pAyz}vw%#c(EH$;^I|%EsG4Gd%N>o0goluceOQmYSQVU(y!8vtmGi0=?zE zNwnnbluBz%Sv#u!V*2%xW(L_wHO>H|i}4iIx$|5|Jg%a}$BkJbX48r92_aq+W6TKI zXOpD~0;Sk!dj%F}bAp~cdt_WcXA@0cYGRwvJXDmSjwIs$A0PKZxpCv;6r@ zZ5QvaoEZ8f_dRlQ$_8|}D>fN&att%zFSPq5ih68$waoh|y!*`a?<|Q8C#mE)e`K45 z{zv+U!EWy~b+`12sBUv3oX4M4rbNqi-NBRNk8h>i&D0k;HBN<_8bi5E9IrI|?MS$T zk}9A90zpwgEEEaEf;S7H5HJJ+L;sK`16&Apg#?p?O)fC_I2;71V+8?z1k?@&MR6JY z->s5Hf5{cgbL7$^&Vge4hhvWe?0*pi3SwsiND6VO-~gP88Wx8=r;2qtfm8uhv493v zofxz9T6{*4eQ@_PORR`wBHFt2DdGpITzTHb{`@8No!a#1 zdEeq5KATov9)KvOX8QccgZsPzzOEq-_B+A2Cs{%6_%?2$O9w(;U0Qy&_bGa^_U-pp z{*+DOl(p9C>MTyA=TOq%>u=5-+HhlafpxgK-i+%Lb7d|?fQ|1+Z+(jwzlgoAdKJM+;vfh$ZEW`Ph@^V z9e9ymOl|$U#G_vyl?VnUUl<{Wng`-^+_rE_xf-)pWR*1;xjahtJ5p0++G?>9M^*JB|zY+nSFG-yIPq94~7a$HuL`E^SGy)e@uG_)HT zdNZ?|&K_6)aWbgRaGyK)RdJ&S$B4xBV;5>5dor_?@0#{(3fl{lc|sOAgL#WKFtsbo zp{j3bW4)GC!fHxQwML7K#llpSw|4zLUCX&&VGw24R(oOS)N7ME|2Ov2L*)wfc`c5d zvXzxu!=oULY2SZpiA!$$V|88jv;Z k+T(vA2rHl+2m*wek4w98R4<+9QkWFuIGgl}IhU~i0R^+z8UO$Q literal 0 HcmV?d00001 diff --git a/vg_platform/android/resources/mipmap/icon.png b/vg_platform/android/resources/mipmap/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..7441eaabbc46b21fb463b4efc47a892c6f8c942a GIT binary patch literal 4222 zcmeHKYitx%6rOFNEyXG*nDR&%7f=e$v(w$#p$oR%LRZ+*&;kXCWp?InJ9b~r%(hz; zP0GUpq7aE_LwFtQ}`)fw2oYJfJP>IwCTdb~hz& z2bKj&K|zVTpa2i6A)n$$7#3YUZ-uky$O}W8D^4vLR_3~j|Ga#CWXIAa{F%`M*5`jb za8`ugd#7(BHzJFCr@s2|;dh(2%wM{*>G7s|yv=g)>V*wgtRw%TvUWAxy|8E4jqA?q zt(N=)C%7^FlDQUJ>Eb;feATq1Wn0>u0S7W&F5mmib8fdjwBom$x9*b1b_#3nX?M0d zmRiQG;Ez6b>e*I&&AD9}C+~g!%=DGhE@~J1yhj$@|1#2i$$ql;s@`h`v8OiIUF^5L zyh{3L+01WFH*8vbWXs0VgUfG~|FFM#=9n2XZ?Cz(@bKz`J5EFzrJ_0gCoRYG^9GJw zDGq;eYw?nW1Fn5Bwr%3ojvU0uLGg**A0aBDhUPML%VdKEC-0{({C{-gJ_o7NA=(OJWR_! z$d3X5)nHYo%aX>8OUy7Ns8j-Ba~6o*1*t2(4zaq#W{jBI=^O}5Pw;j@x9{Bq1IptO zTp=l9gy(iSt;YI-9Fi1SFinom_DQ@=BxJA15}YH4Cmbv<6C7>J@$xnprsYml?x3cN zK?xaDKu#*aV@Ej-gd8%F!+UK6XLCpdFR~2b^>RF&Z9|Bo#ZDAcR0Xn9^mmTRppt<~ zve{S~(GJ4r;2|2`#t;sg;RqVBBF8eE&z5a7sboo*5K;po9H$ZxD-jhAR+NT-IKP$Iy&G z$HF)0JRw=})%_RRxIB0+;Ni%N6b;s|GhMM8Rf=AXy~SSrig}fA+`JTmD8;s*i8Uy8 z-oOXriC0{3KBk6hhSpj+Uc z?E2s2O7HlbLP7X1s1`n!>Nn2Y03TW@-boW(*elo@*s8(Jt4Q$l2^Y=OFs$!WMzdgz zt24ows=GafsaJXp>C<0mtyqu?t|4w$URl~%sqM1|KaNjMKJw;-l$1peD%#G|7?yWD VrKP@Mg7M9cxeJS3P5E=`{|3hrnUMeh literal 0 HcmV?d00001 diff --git a/vg_platform/android/resources/values/strings.xml b/vg_platform/android/resources/values/strings.xml new file mode 100644 index 0000000..a702659 --- /dev/null +++ b/vg_platform/android/resources/values/strings.xml @@ -0,0 +1,7 @@ + + + carrot + carrot + org.mtzero.carrot + + diff --git a/vg_profiler.c b/vg_profiler.c index d203c92..3684ae8 100644 --- a/vg_profiler.c +++ b/vg_profiler.c @@ -1,7 +1,7 @@ #include "vg_platform.h" #include "vg_profiler.h" #include "vg_engine.h" -#include "vg_imgui.h" +#include "vg_ui/imgui.h" int vg_profiler = 0; diff --git a/vg_profiler.h b/vg_profiler.h index 74190cb..5de304e 100644 --- a/vg_profiler.h +++ b/vg_profiler.h @@ -1,6 +1,6 @@ #pragma once #include "vg_platform.h" -#include "vg_imgui.h" +#include "vg_ui/imgui.h" #define VG_PROFILE_SAMPLE_COUNT 128 extern int vg_profiler; diff --git a/vg_shader.c b/vg_shader.c index c12495c..b17c64a 100644 --- a/vg_shader.c +++ b/vg_shader.c @@ -14,7 +14,12 @@ const char *vg_shader_gl_ver = "#version 330 core\n"; -struct vg_shaders vg_shaders; +struct vg_shaders +{ + vg_shader *shaders[48]; + u32 count; +} +static vg_shaders; static GLuint vg_shader_subshader( const char *src, GLint gliShaderType ) { @@ -141,7 +146,7 @@ int vg_shader_compile( struct vg_shader *shader ) return 1; } -static void vg_free_shader( struct vg_shader *shader ) +void vg_free_shader( struct vg_shader *shader ) { if( shader->compiled ) { diff --git a/vg_shader.h b/vg_shader.h index 5e2ad35..1ea106c 100644 --- a/vg_shader.h +++ b/vg_shader.h @@ -1,28 +1,22 @@ #pragma once typedef struct vg_shader vg_shader; - -struct vg_shaders +struct vg_shader { - struct vg_shader - { - GLuint id; - const char *name; + GLuint id; + const char *name; - struct vg_subshader - { - const char *orig_file, - *static_src; - } - vs, fs; - int compiled; + struct vg_subshader + { + const char *orig_file, + *static_src; } - * shaders[48]; - u32 count; -} -extern vg_shaders; + vs, fs; + int compiled; +}; void vg_shaders_compile(void); int vg_shaders_live_recompile(int argc, const char *argv[]); void vg_shader_register( struct vg_shader *shader ); int vg_shader_compile( struct vg_shader *shader ); +void vg_free_shader( struct vg_shader *shader ); diff --git a/vg_imgui.c b/vg_ui/imgui.c similarity index 57% rename from vg_imgui.c rename to vg_ui/imgui.c index bb4f46f..7cf9df8 100644 --- a/vg_imgui.c +++ b/vg_ui/imgui.c @@ -1,451 +1,57 @@ -/* Copyright (C) 2021-2024 Harry Godden (hgn) - All Rights Reserved */ - -/* - * Principles: - * - * 1. layout is defined by subdividing - * 2. a parent node should never be resized by the content after creation - * 3. when the ui is in an interactive state, no controls should ever move - * 4. controls directly reference a memory location and use that as their - * unique id - * 5. a maximum of ONE control per memory location can be drawn at any given - * point. - */ - #pragma once -#include "vg_imgui.h" -#include "vg_engine.h" -#include "vg_tex.h" -#include "vg_shader.h" +#include "vg_ui/imgui.h" +#include "vg_platform.h" +#include "vg_m.h" +#include "vg_font.h" +#include "vg_log.h" #include -ui_px k_ui_widget_height = 28, - k_ui_scale = 1, - k_ui_padding = 8; - -#include "vg/vg_default_font.gc" - -struct vg_imgui vg_ui = { - .scheme = { - [ k_ui_bg+0 ] = UI_RGB( 0x1d2021 ), - [ k_ui_bg+1 ] = UI_RGB( 0x282828 ), - [ k_ui_bg+2 ] = UI_RGB( 0x3c3836 ), - [ k_ui_bg+3 ] = UI_RGB( 0x504945 ), - [ k_ui_bg+4 ] = UI_RGB( 0x665c54 ), - [ k_ui_bg+5 ] = UI_RGB( 0x7c6f64 ), - [ k_ui_bg+6 ] = UI_RGB( 0x928374 ), - [ k_ui_bg+7 ] = UI_RGB( 0xa89984 ), - - [ k_ui_fg+0 ] = UI_RGB( 0xebdbb2 ), - [ k_ui_fg+1 ] = UI_RGB( 0xfbf1c7 ), - [ k_ui_fg+2 ] = UI_RGB( 0xd5c4a1 ), - [ k_ui_fg+3 ] = UI_RGB( 0xbdae93 ), - [ k_ui_fg+4 ] = UI_RGB( 0xa89984 ), - [ k_ui_fg+5 ] = UI_RGB( 0x000000 ), - [ k_ui_fg+6 ] = UI_RGB( 0x000000 ), - [ k_ui_fg+7 ] = UI_RGB( 0x000000 ), - - [ k_ui_red ] = UI_RGB( 0xcc241d ), - [ k_ui_orange ] = UI_RGB( 0xd65d0e ), - [ k_ui_yellow ] = UI_RGB( 0xd79921 ), - [ k_ui_green ] = UI_RGB( 0x98971a ), - [ k_ui_aqua ] = UI_RGB( 0x689d6a ), - [ k_ui_blue ] = UI_RGB( 0x458588 ), - [ k_ui_purple ] = UI_RGB( 0xb16286 ), - [ k_ui_gray ] = UI_RGB( 0x928374 ), - [ k_ui_red + k_ui_brighter ] = UI_RGB( 0xfb4934 ), - [ k_ui_orange + k_ui_brighter ] = UI_RGB( 0xfe8019 ), - [ k_ui_yellow + k_ui_brighter ] = UI_RGB( 0xfabd2f ), - [ k_ui_green + k_ui_brighter ] = UI_RGB( 0xb8bb26 ), - [ k_ui_aqua + k_ui_brighter ] = UI_RGB( 0x8ec07c ), - [ k_ui_blue + k_ui_brighter ] = UI_RGB( 0x83a598 ), - [ k_ui_purple + k_ui_brighter ] = UI_RGB( 0xd3869b ), - [ k_ui_gray + k_ui_brighter ] = UI_RGB( 0xa89984 ), - }, - .font = &vgf_default_small, - .colour = {1.0f,1.0f,1.0f,1.0f}, - .bg_inverse_ratio = {1,1} -}; - -static struct vg_shader _shader_ui ={ - .name = "[vg] ui - transparent", - .vs = { - .orig_file = NULL, - .static_src = - "layout (location=0) in vec2 a_co;" - "layout (location=1) in vec2 a_uv;" - "layout (location=2) in vec4 a_colour;" - "uniform mat3 uPv;" - "uniform vec2 uBGInverseRatio;" - "uniform vec2 uInverseFontSheet;" - "" - "out vec4 aTexCoords;" - "out vec4 aColour;" - "" - "void main(){" - "vec4 proj_pos = vec4( uPv * vec3( a_co, 1.0 ), 1.0 );" - "gl_Position = proj_pos;" - "aTexCoords = vec4( a_uv * uInverseFontSheet, " - " (proj_pos.xy*0.5+0.5) * uBGInverseRatio );" - "aColour = a_colour;" - "}", - }, - .fs = { - .orig_file = NULL, - .static_src = - "uniform sampler2D uTexGlyphs;" - "uniform sampler2D uTexBG;" - "uniform vec4 uColour;" - "uniform float uSpread;" - "out vec4 FragColor;" - "" - "in vec4 aTexCoords;" - "in vec4 aColour;" - "" - "vec2 rand_hash22( vec2 p ){" - "vec3 p3 = fract(vec3(p.xyx) * 213.8976123);" - "p3 += dot(p3, p3.yzx+19.19);" - "return fract(vec2((p3.x + p3.y)*p3.z, (p3.x+p3.z)*p3.y));" - "}" - "" - "void main(){" - "vec4 diffuse = aColour;" - - "vec4 avg = vec4(0.0);" - - "if( aColour.a == 0.0 ){" - "avg = aColour;" - "avg.a = texture( uTexGlyphs, aTexCoords.xy ).r;" - "}" - "else{" - "if( uSpread > 0.0001 ){" - "for( int i=0; i<4; i ++ ){" - "vec2 spread = rand_hash22(aTexCoords.zw+vec2(float(i)));" - "avg += texture( uTexBG, aTexCoords.zw + (spread-0.5)*uSpread );" - "}" - "avg *= 0.25;" - "avg.a = 1.0;" - "avg.rgb = mix( avg.rgb, aColour.rgb, aColour.a );" - "}" - "else{" - "avg = aColour;" - "}" - "}" - - "FragColor = avg * uColour;" - "}" - } -}; - -static struct vg_shader _shader_ui_image = { - .name = "[vg] ui_image", - .vs = - { - .orig_file = NULL, - .static_src = - "layout (location=0) in vec2 a_co;" - "layout (location=1) in vec2 a_uv;" - "layout (location=2) in vec4 a_colour;" - "uniform mat3 uPv;" - - "out vec2 aTexCoords;" - "out vec4 aColour;" - "out vec2 aWsp;" - - "void main()" - "{" - "gl_Position = vec4( uPv * vec3( a_co, 1.0 ), 1.0 );" - "aTexCoords = a_uv * 0.00390625;" - "aColour = a_colour;" - - "aWsp = a_co;" - "}", - }, - .fs = - { - .orig_file = NULL, - .static_src = - "uniform sampler2D uTexImage;" - "uniform vec4 uColour;" - "out vec4 FragColor;" - - "in vec2 aTexCoords;" - "in vec4 aColour;" - "in vec2 aWsp;" - - "void main()" - "{" - "vec4 colour = texture( uTexImage, aTexCoords );" - "FragColor = colour * uColour;" - "}" - } -}; - -static struct vg_shader _shader_ui_hsv = { - .name = "[vg] ui_hsv", - .vs = { - .orig_file = NULL, - .static_src = - "layout (location=0) in vec2 a_co;" - "layout (location=1) in vec2 a_uv;" - "layout (location=2) in vec4 a_colour;" - "uniform mat3 uPv;" - - "out vec2 aTexCoords;" - "out vec4 aColour;" - "out vec2 aWsp;" - - "void main()" - "{" - "gl_Position = vec4( uPv * vec3( a_co, 1.0 ), 1.0 );" - "aTexCoords = a_uv * 0.00390625;" - "aColour = a_colour;" - - "aWsp = a_co;" - "}", - }, - .fs = - { - .orig_file = NULL, - .static_src = - "uniform float uHue;" - "out vec4 FragColor;" - - "in vec2 aTexCoords;" - "in vec4 aColour;" - "in vec2 aWsp;" - - "void main()" - "{" - "vec3 c = vec3( uHue, aTexCoords );" - "vec4 K = vec4(1.0,2.0/3.0,1.0/3.0,3.0);" - "vec3 p = abs(fract(c.xxx+K.xyz)*6.0 - K.www);" - "vec3 colour = c.z*mix(K.xxx,clamp(p-K.xxx,0.0,1.0),c.y);" - "FragColor = vec4( colour, 1.0 );" - "}" - } -}; +ui_context *g_ui_ctx; -void vg_ui_init(void) +//__attribute__((deprecated("message", "replacement"))) +void ui_bind_context( ui_context *context ) { - if( !vg_shader_compile( &_shader_ui ) || - !vg_shader_compile( &_shader_ui_image ) || - !vg_shader_compile( &_shader_ui_hsv ) ){ - vg_fatal_error( "Failed to compile ui shader" ); - } - - /* - * Vertex buffer - * ---------------------------------------- - */ - - vg_ui.max_indices = 20000; - vg_ui.max_verts = 30000; - - /* Generate the buffer we are gonna be drawing to */ - glGenVertexArrays( 1, &vg_ui.vao ); - glGenBuffers( 1, &vg_ui.vbo ); - glGenBuffers( 1, &vg_ui.ebo ); - - glBindVertexArray( vg_ui.vao ); - glBindBuffer( GL_ARRAY_BUFFER, vg_ui.vbo ); - - glBufferData( GL_ARRAY_BUFFER, - vg_ui.max_verts * sizeof( struct ui_vert ), - NULL, GL_DYNAMIC_DRAW ); - glBindVertexArray( vg_ui.vao ); - - glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, vg_ui.ebo ); - glBufferData( GL_ELEMENT_ARRAY_BUFFER, - vg_ui.max_indices * sizeof( u16 ), NULL, GL_DYNAMIC_DRAW ); - - VG_CHECK_GL_ERR(); - - /* Set pointers */ - u32 const stride = sizeof( struct ui_vert ); - - /* XY */ - glVertexAttribPointer( 0, 2, GL_SHORT, GL_FALSE, stride, - (void *)offsetof( struct ui_vert, co ) ); - glEnableVertexAttribArray( 0 ); - - /* UV */ - glVertexAttribPointer( 1, 2, GL_UNSIGNED_SHORT, GL_FALSE, stride, - (void *)offsetof( struct ui_vert, uv ) ); - glEnableVertexAttribArray( 1 ); - - /* COLOUR */ - glVertexAttribPointer( 2, 4, GL_UNSIGNED_BYTE, GL_TRUE, stride, - (void *)offsetof( struct ui_vert, colour ) ); - glEnableVertexAttribArray( 2 ); - - VG_CHECK_GL_ERR(); - - /* Alloc RAM default context */ - u32 vert_size = vg_ui.max_verts*sizeof(struct ui_vert), - inds_size = vg_align8( vg_ui.max_indices*sizeof(u16) ); - - vg_ui.vertex_buffer = vg_linear_alloc( vg_mem.rtmemory, vert_size ); - vg_ui.indice_buffer = vg_linear_alloc( vg_mem.rtmemory, inds_size ); - - /* font - * ----------------------------------------------------- - */ - - /* Load default font */ - - vg_font_sheet *sheet = &vg_default_font_sheet; - u32 pixels = 0, - total = sheet->w*sheet->h, - data = 0; - - vg_linear_clear( vg_mem.scratch ); - u8 *image = vg_linear_alloc( vg_mem.scratch, total ); - - while( pixels < total ) - { - for( int b = 31; b >= 0; b-- ) - { - image[ pixels ++ ] = (sheet->bitmap[data] & (0x1u << b))? 0xffu: 0x00u; - - if( pixels >= total ) - { - total = 0; - break; - } - } - data++; - } - - vg_ui.inverse_font_sheet[0] = 1.0/(f64)sheet->w; - vg_ui.inverse_font_sheet[1] = 1.0/(f64)sheet->h; - - glGenTextures( 1, &vg_ui.tex_glyphs ); - glBindTexture( GL_TEXTURE_2D, vg_ui.tex_glyphs ); - glTexImage2D( GL_TEXTURE_2D, 0, GL_R8, sheet->w, sheet->h, 0, - GL_RED, GL_UNSIGNED_BYTE, image ); - - VG_CHECK_GL_ERR(); - glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); - glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); - glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); - glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); - - /* - * Cursors - * --------------------------------------------------------------- - */ - - vg_ui.cursor_map[ k_ui_cursor_default ] = - SDL_CreateSystemCursor( SDL_SYSTEM_CURSOR_ARROW ); - vg_ui.cursor_map[ k_ui_cursor_hand ] = - SDL_CreateSystemCursor( SDL_SYSTEM_CURSOR_HAND ); - vg_ui.cursor_map[ k_ui_cursor_ibeam ] = - SDL_CreateSystemCursor( SDL_SYSTEM_CURSOR_IBEAM ); -} - -void rect_copy( ui_rect a, ui_rect b ){ - for( int i=0; i<4; i++ ) - b[i] = a[i]; + g_ui_ctx = context; } -void ui_flush( enum ui_shader shader, f32 w, f32 h ){ - u32 vertex_offset = vg_ui.vert_start*sizeof(ui_vert), - vertex_count = vg_ui.cur_vert-vg_ui.vert_start, - vertex_size = vertex_count*sizeof(ui_vert), - - indice_offset = vg_ui.indice_start*sizeof(u16), - indice_count = vg_ui.cur_indice-vg_ui.indice_start, - indice_size = indice_count * sizeof(u16); +//__attribute__((deprecated("message", "replacement"))) +ui_context *ui_current_context(void) +{ + return g_ui_ctx; +} - if( !vertex_size || !indice_size ) - return; - - glBindVertexArray( vg_ui.vao ); - glBindBuffer( GL_ARRAY_BUFFER, vg_ui.vbo ); - glBufferSubData( GL_ARRAY_BUFFER, vertex_offset, vertex_size, - vg_ui.vertex_buffer+vg_ui.vert_start ); - - glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, vg_ui.ebo ); - glBufferSubData( GL_ELEMENT_ARRAY_BUFFER, indice_offset, indice_size, - vg_ui.indice_buffer+vg_ui.indice_start ); - - glDisable( GL_DEPTH_TEST ); - glEnable( GL_BLEND ); - glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); - glBlendEquation( GL_FUNC_ADD ); - glDisable( GL_CULL_FACE ); - - m3x3f view = M3X3_IDENTITY; - m3x3_translate( view, (v3f){ -1.0f, 1.0f, 0.0f } ); - m3x3_scale( view, (v3f){ 1.0f/(w*0.5f), - -1.0f/(h*0.5f), 1.0f } ); - - if( shader == k_ui_shader_colour ){ - glUseProgram( _shader_ui.id ); - - glUniformMatrix3fv( glGetUniformLocation( _shader_ui.id, "uPv" ), 1, - GL_FALSE, (float *)view ); - glUniform4fv( glGetUniformLocation( _shader_ui.id, "uColour" ), 1, - vg_ui.colour ); - - glActiveTexture( GL_TEXTURE0 ); - glBindTexture( GL_TEXTURE_2D, vg_ui.tex_glyphs ); - glUniform1i( glGetUniformLocation( _shader_ui.id, "uTexGlyphs" ), 0 ); - - glActiveTexture( GL_TEXTURE1 ); - glBindTexture( GL_TEXTURE_2D, vg_ui.tex_bg ); - glUniform1i( glGetUniformLocation( _shader_ui.id, "uTexBG" ), 1 ); - glUniform1f( glGetUniformLocation( _shader_ui.id, "uSpread" ), - vg_ui.frosting ); - glUniform2fv( glGetUniformLocation( _shader_ui.id, "uBGInverseRatio" ), - 1, vg_ui.bg_inverse_ratio ); - glUniform2fv( glGetUniformLocation( _shader_ui.id, "uInverseFontSheet" ), - 1, vg_ui.inverse_font_sheet ); - } - else if( shader == k_ui_shader_image ){ - glUseProgram( _shader_ui_image.id ); - glUniformMatrix3fv( glGetUniformLocation( _shader_ui_image.id, "uPv" ), 1, - GL_FALSE, (float *)view ); - glUniform1i( glGetUniformLocation(_shader_ui_image.id,"uTexImage"), 0 ); - glUniform4fv( glGetUniformLocation( _shader_ui_image.id, "uColour" ), 1, - vg_ui.colour ); - } - else if( shader == k_ui_shader_hsv ){ - glUseProgram( _shader_ui_hsv.id ); - glUniformMatrix3fv( glGetUniformLocation( _shader_ui_hsv.id, "uPv" ), 1, - GL_FALSE, (float *)view ); - glUniform1f( glGetUniformLocation(_shader_ui_hsv.id,"uHue"), vg_ui.hue ); - } - else - vg_fatal_error( "Invalid UI shader (%d)\n", shader ); - - glDrawElements( GL_TRIANGLES, indice_count, GL_UNSIGNED_SHORT, - (void *)(vg_ui.indice_start*sizeof(u16)) ); +void ui_init( ui_vert *verts_buf, u32 verts_max, + u16 *indices_buf, u32 indices_max ) +{ + g_ui_ctx->vertex_buffer = verts_buf; + g_ui_ctx->max_verts = verts_max; + g_ui_ctx->cur_vert = 0; + g_ui_ctx->vert_start = 0; - glDisable( GL_BLEND ); + g_ui_ctx->indice_buffer = indices_buf; + g_ui_ctx->max_indices = indices_max; + g_ui_ctx->cur_indice = 0; + g_ui_ctx->indice_start = 0; - vg_ui.indice_start = vg_ui.cur_indice; - vg_ui.vert_start = vg_ui.cur_vert; + if( !verts_buf || !indices_buf ) + exit(0); } -struct ui_vert *ui_fill_rect( ui_rect rect, u32 colour, ui_px uv[4] ) +ui_vert *ui_fill_rect( ui_rect rect, u32 colour, ui_px uv[4] ) { /* this if far from ideal but stops us from crashing */ - if( (vg_ui.cur_vert + 4 > vg_ui.max_verts) || - (vg_ui.cur_indice + 6 > vg_ui.max_indices)) + if( (g_ui_ctx->cur_vert + 4 > g_ui_ctx->max_verts) || + (g_ui_ctx->cur_indice + 6 > g_ui_ctx->max_indices)) { - return &vg_ui.vertex_buffer[0]; + return &g_ui_ctx->vertex_buffer[0]; } - struct ui_vert *vertices = &vg_ui.vertex_buffer[ vg_ui.cur_vert ]; - u16 *indices = &vg_ui.indice_buffer[ vg_ui.cur_indice ]; + ui_vert *vertices = &g_ui_ctx->vertex_buffer[ g_ui_ctx->cur_vert ]; + u16 *indices = &g_ui_ctx->indice_buffer[ g_ui_ctx->cur_indice ]; for( int i=0; i<4; i++ ) - { vertices[i].colour = colour; - } vertices[0].co[0] = rect[0]; vertices[0].co[1] = rect[1]; @@ -463,23 +69,20 @@ struct ui_vert *ui_fill_rect( ui_rect rect, u32 colour, ui_px uv[4] ) vertices[3].co[1] = rect[1]+rect[3]; vertices[3].uv[0] = uv[0]; vertices[3].uv[1] = uv[3]; - u16 ind_start = vg_ui.cur_vert; - u16 start = vg_ui.cur_vert; + u16 start = g_ui_ctx->cur_vert; u32 mesh[] = { 0,2,1, 0,3,2 }; for( u32 i=0; icur_indice += 6; + g_ui_ctx->cur_vert += 4; return vertices; } -struct ui_vert *ui_fill( ui_rect rect, u32 colour ) +ui_vert *ui_fill( ui_rect rect, u32 colour ) { return ui_fill_rect( rect, colour, (ui_px[4]){ 4,4,4,4 } ); } @@ -487,14 +90,15 @@ struct ui_vert *ui_fill( ui_rect rect, u32 colour ) void ui_outline( ui_rect rect, ui_px thickness, u32 colour, u32 mask ) { /* this if far from ideal but stops us from crashing */ - if( (vg_ui.cur_vert + 8 > vg_ui.max_verts) || - (vg_ui.cur_indice + 24 > vg_ui.max_indices)) + if( (g_ui_ctx->cur_vert + 8 > g_ui_ctx->max_verts) || + (g_ui_ctx->cur_indice + 24 > g_ui_ctx->max_indices)) return; - struct ui_vert *vertices = &vg_ui.vertex_buffer[ vg_ui.cur_vert ]; - u16 *indices = &vg_ui.indice_buffer[ vg_ui.cur_indice ]; + ui_vert *vertices = &g_ui_ctx->vertex_buffer[ g_ui_ctx->cur_vert ]; + u16 *indices = &g_ui_ctx->indice_buffer[ g_ui_ctx->cur_indice ]; - for( int i=0; i<8; i++ ){ + for( int i=0; i<8; i++ ) + { vertices[i].uv[0] = 4; vertices[i].uv[1] = 4; vertices[i].colour = colour; @@ -517,26 +121,35 @@ void ui_outline( ui_rect rect, ui_px thickness, u32 colour, u32 mask ) vertices[7].co[0] = vertices[3].co[0]-thickness; vertices[7].co[1] = vertices[3].co[1]+thickness; - u16 start = vg_ui.cur_vert; + u16 start = g_ui_ctx->cur_vert; u32 mesh[] = { 0,5,4, 0,1,5, 1,6,5, 1,2,6, 2,7,6, 2,3,7, 3,4,7, 3,0,4 }; if( !mask ) mask = UI_TOP|UI_LEFT|UI_BOTTOM|UI_RIGHT; u32 c = 0; - for( u32 i=0; icur_indice += c; + g_ui_ctx->cur_vert += 8; +} + +void rect_copy( ui_rect a, ui_rect b ) +{ + for( int i=0; i<4; i++ ) + b[i] = a[i]; } void ui_split( ui_rect rect, enum ui_axis other, ui_px width, ui_px gap, - ui_rect l, ui_rect r ){ + ui_rect l, ui_rect r ) +{ enum ui_axis dir = other ^ 0x1; if( width < 0 ) width = rect[ 2+dir ] + width; @@ -554,28 +167,6 @@ void ui_split( ui_rect rect, enum ui_axis other, ui_px width, ui_px gap, r[ 2+other ] = temp[ 2+other ]; } -void ui_rect_center( ui_rect parent, ui_rect rect ) -{ - rect[0] = parent[0] + (parent[2]-rect[2])/2; - rect[1] = parent[1] + (parent[3]-rect[3])/2; -} - -void ui_fit_item( ui_rect rect, ui_px size[2], ui_rect d ) -{ - i32 rp = (i32)rect[2] * (i32)size[1], - rc = (i32)size[0] * (i32)rect[3]; - - enum ui_axis dir, other; - if( rc > rp ) dir = k_ui_axis_h; - else dir = k_ui_axis_v; - other = dir ^ 0x1; - - d[2+dir] = rect[2+dir]; - d[2+other] = (rect[2+dir] * size[other]) / size[dir]; - - ui_rect_center( rect, d ); -} - void ui_split_ratio( ui_rect rect, enum ui_axis dir, float ratio, ui_px gap, ui_rect l, ui_rect r ) { @@ -585,60 +176,24 @@ void ui_split_ratio( ui_rect rect, enum ui_axis dir, float ratio, void ui_rect_pad( ui_rect rect, ui_px pad[2] ) { + ui_px tmp[2]; + if( !pad ) + { + tmp[0] = g_ui_ctx->padding; + tmp[1] = g_ui_ctx->padding; + pad = tmp; + } + rect[0] += pad[0]; rect[1] += pad[1]; rect[2] -= pad[0]*2; rect[3] -= pad[1]*2; } -ui_px ui_text_line_width( const char *str ) -{ - int length = 0; - const char *_c = str; - u8 c; - - while( (c = *(_c ++)) ){ - if( c >= 32 ) length ++; - else if( c == '\n' ) break; - } - - return length * vg_ui.font->sx; -} - -ui_px ui_text_string_height( const char *str ) -{ - int height = 1; - const char *_c = str; - u8 c; - - while( (c = *(_c ++)) ) - { - if( c == '\n' ) height ++; - } - - return height * vg_ui.font->sy; -} - -ui_px ui_text_aligned_x( const char *str, ui_rect rect, ui_px scale, - enum ui_align align ) -{ - enum ui_align lwr = k_ui_align_lwr & align; - if( lwr == k_ui_align_left ){ - return rect[0]; - } - else{ - ui_px width = ui_text_line_width( str ) * scale; - - if( lwr == k_ui_align_right ) - return rect[0] + rect[2]-width; - else - return rect[0] + (rect[2]-width)/2; - } -} - static ui_px ui_min( ui_px a, ui_px b ){ return ab?a:b; } -static ui_px ui_clamp( ui_px a, ui_px min, ui_px max ){ +static ui_px ui_clamp( ui_px a, ui_px min, ui_px max ) +{ return ui_min( max, ui_max( a, min ) ); } @@ -679,9 +234,9 @@ int ui_inside_rect( ui_rect rect, ui_px co[2] ) int ui_click_down( u32 mask ) { - if( vg_ui.ignore_input_frames ) return 0; - if( (vg_ui.mouse_state[0] & mask) && - !(vg_ui.mouse_state[1] & mask) ) + if( g_ui_ctx->ignore_input_frames ) return 0; + if( (g_ui_ctx->mouse_state[0] & mask) && + !(g_ui_ctx->mouse_state[1] & mask) ) return 1; else return 0; @@ -689,68 +244,157 @@ int ui_click_down( u32 mask ) int ui_clicking( u32 mask ) { - if( vg_ui.ignore_input_frames ) return 0; - return vg_ui.mouse_state[0] & mask; + if( g_ui_ctx->ignore_input_frames ) return 0; + return g_ui_ctx->mouse_state[0] & mask; } int ui_click_up( u32 mask ) { - if( vg_ui.ignore_input_frames ) return 0; - if( (vg_ui.mouse_state[1] & mask) && - !(vg_ui.mouse_state[0] & mask) ) + if( g_ui_ctx->ignore_input_frames ) return 0; + if( (g_ui_ctx->mouse_state[1] & mask) && + !(g_ui_ctx->mouse_state[0] & mask) ) return 1; else return 0; } -void ui_set_mouse_pos( ui_px x, ui_px y ) +void ui_prerender( ui_px dims[2], ui_px mouse[2], i32 mouse_state ) { - SDL_WarpMouseInWindow( vg.window, x, y ); - vg_ui.mouse[0] = x; - vg_ui.mouse[1] = y; - vg_ui.mouse_pos_overriden = 1; -} + g_ui_ctx->mouse_state[1] = g_ui_ctx->mouse_state[0]; + g_ui_ctx->mouse_state[0] = mouse_state; + g_ui_ctx->mouse_delta[0] = mouse[0]-g_ui_ctx->mouse[0]; + g_ui_ctx->mouse_delta[1] = mouse[1]-g_ui_ctx->mouse[1]; + g_ui_ctx->area[0] = dims[0]; + g_ui_ctx->area[1] = dims[1]; -void ui_prerender(void) -{ - int x, y; - vg_ui.mouse_state[1] = vg_ui.mouse_state[0]; - vg_ui.mouse_state[0] = SDL_GetMouseState( &x, &y ); - vg_ui.mouse_delta[0] = x-vg_ui.mouse[0]; - vg_ui.mouse_delta[1] = y-vg_ui.mouse[1]; - - if( !vg_ui.mouse_pos_overriden ) + if( !g_ui_ctx->mouse_pos_overriden ) { - vg_ui.mouse[0] = x; - vg_ui.mouse[1] = y; + g_ui_ctx->mouse[0] = mouse[0]; + g_ui_ctx->mouse[1] = mouse[1]; } - vg_ui.cur_vert = 0; - vg_ui.cur_indice = 0; - vg_ui.vert_start = 0; - vg_ui.indice_start = 0; - vg_ui.focused_control_hit = 0; - vg_ui.cursor = k_ui_cursor_default; - vg_ui.wants_mouse = 0; - vg_ui.mouse_pos_overriden = 0; + g_ui_ctx->cur_vert = 0; + g_ui_ctx->cur_indice = 0; + g_ui_ctx->vert_start = 0; + g_ui_ctx->indice_start = 0; + g_ui_ctx->focused_control_hit = 0; + g_ui_ctx->cursor = k_ui_cursor_default; + g_ui_ctx->wants_mouse = 0; + g_ui_ctx->mouse_pos_overriden = 0; - if( vg_ui.ignore_input_frames ) + if( g_ui_ctx->ignore_input_frames ) { - vg_ui.ignore_input_frames --; + g_ui_ctx->ignore_input_frames --; return; } if( ui_click_down(UI_MOUSE_LEFT)||ui_click_down(UI_MOUSE_MIDDLE)|| ui_click_down(UI_MOUSE_RIGHT) ) { - vg_ui.mouse_click[0] = vg_ui.mouse[0]; - vg_ui.mouse_click[1] = vg_ui.mouse[1]; + g_ui_ctx->mouse_click[0] = g_ui_ctx->mouse[0]; + g_ui_ctx->mouse_click[1] = g_ui_ctx->mouse[1]; + } +} + +void ui_ignore_input_frames( u32 frames ) +{ + g_ui_ctx->ignore_input_frames = frames; +} + +void ui_capture_mouse( bool on ) +{ + g_ui_ctx->wants_mouse = on; +} + +void ui_impl_render_batch( ui_batch *batch ); + +void ui_flush( enum ui_shader shader ) +{ + ui_batch batch; + batch.shader = shader; + batch.vert_offset = g_ui_ctx->vert_start * sizeof(ui_vert); + batch.indice_offset = g_ui_ctx->indice_start * sizeof(u16); + batch.vert_buf = g_ui_ctx->vertex_buffer + g_ui_ctx->vert_start; + batch.vert_count = g_ui_ctx->cur_vert - g_ui_ctx->vert_start; + batch.indice_buf = g_ui_ctx->indice_buffer + g_ui_ctx->indice_start; + batch.indice_count = g_ui_ctx->cur_indice - g_ui_ctx->indice_start; + + ui_impl_render_batch( &batch ); + + g_ui_ctx->indice_start = g_ui_ctx->cur_indice; + g_ui_ctx->vert_start = g_ui_ctx->cur_vert; +} + +void ui_rect_center( ui_rect parent, ui_rect rect ) +{ + rect[0] = parent[0] + (parent[2]-rect[2])/2; + rect[1] = parent[1] + (parent[3]-rect[3])/2; +} + +void ui_fit_item( ui_rect rect, ui_px size[2], ui_rect d ) +{ + i32 rp = (i32)rect[2] * (i32)size[1], + rc = (i32)size[0] * (i32)rect[3]; + + enum ui_axis dir, other; + if( rc > rp ) dir = k_ui_axis_h; + else dir = k_ui_axis_v; + other = dir ^ 0x1; + + d[2+dir] = rect[2+dir]; + d[2+other] = (rect[2+dir] * size[other]) / size[dir]; + + ui_rect_center( rect, d ); +} + +ui_px ui_text_line_width( const char *str ) +{ + int length = 0; + const char *_c = str; + u8 c; + + while( (c = *(_c ++)) ){ + if( c >= 32 ) length ++; + else if( c == '\n' ) break; + } + + return length * g_ui_ctx->font->sx; +} + +ui_px ui_text_string_height( const char *str ) +{ + int height = 1; + const char *_c = str; + u8 c; + + while( (c = *(_c ++)) ) + { + if( c == '\n' ) height ++; + } + + return height * g_ui_ctx->font->sy; +} + +ui_px ui_text_aligned_x( const char *str, ui_rect rect, ui_px scale, + enum ui_align align ) +{ + enum ui_align lwr = k_ui_align_lwr & align; + if( lwr == k_ui_align_left ){ + return rect[0]; + } + else{ + ui_px width = ui_text_line_width( str ) * scale; + + if( lwr == k_ui_align_right ) + return rect[0] + rect[2]-width; + else + return rect[0] + (rect[2]-width)/2; } } u32 ui_colour( enum ui_scheme_colour id ) { - return vg_ui.scheme[ id ]; + return g_ui_ctx->scheme[ id ]; } /* get an appropriately contrasting colour given the base */ @@ -814,8 +458,8 @@ u32 ui_ntext( ui_rect rect, const char *str, u32 len, ui_px scale, text_cursor[0] = ui_text_aligned_x( str, rect, scale, align ); text_cursor[1] = rect[1]; - text_cursor[2] = vg_ui.font->cw*scale; - text_cursor[3] = vg_ui.font->ch*scale; + text_cursor[2] = g_ui_ctx->font->cw*scale; + text_cursor[3] = g_ui_ctx->font->ch*scale; u32 printed_chars = 0; @@ -834,19 +478,19 @@ u32 ui_ntext( ui_rect rect, const char *str, u32 len, ui_px scale, if( printed_chars >= len ) { printed_chars = 0; - text_cursor[1] += vg_ui.font->sy*scale; + text_cursor[1] += g_ui_ctx->font->sy*scale; text_cursor[0] = ui_text_aligned_x( _c, rect, scale, align ); - text_cursor[0] -= vg_ui.font->sx*scale; + text_cursor[0] -= g_ui_ctx->font->sx*scale; ui_rect glyph; - ui_text_glyph( vg_ui.font, '\xb1' /*FIXME*/, glyph ); + ui_text_glyph( g_ui_ctx->font, '\xb1' /*FIXME*/, glyph ); ui_fill_rect( text_cursor, 0x00ffffff, glyph ); - text_cursor[0] += vg_ui.font->sx*scale; + text_cursor[0] += g_ui_ctx->font->sx*scale; } if( c == '\n' ) { - text_cursor[1] += vg_ui.font->sy*scale; + text_cursor[1] += g_ui_ctx->font->sy*scale; text_cursor[0] = ui_text_aligned_x( _c, rect, scale, align ); printed_chars = 0; continue; @@ -854,7 +498,7 @@ u32 ui_ntext( ui_rect rect, const char *str, u32 len, ui_px scale, else if( c >= 33 ) { ui_rect glyph; - ui_text_glyph( vg_ui.font, c, glyph ); + ui_text_glyph( g_ui_ctx->font, c, glyph ); ui_rect cursor_clipped; if( ui_clip( rect, text_cursor, cursor_clipped ) ) @@ -917,12 +561,12 @@ u32 ui_ntext( ui_rect rect, const char *str, u32 len, ui_px scale, } else if( c == '\t' ) { - text_cursor[0] += vg_ui.font->sx*scale*4; + text_cursor[0] += g_ui_ctx->font->sx*scale*4; printed_chars += 4; continue; } - text_cursor[0] += vg_ui.font->sx*scale; + text_cursor[0] += g_ui_ctx->font->sx*scale; printed_chars ++; } @@ -937,7 +581,12 @@ u32 ui_text( ui_rect rect, const char *str, ui_px scale, void ui_font_face( vg_font_face *ff ) { - vg_ui.font = ff; + g_ui_ctx->font = ff; +} + +const vg_font_face *ui_current_font(void) +{ + return g_ui_ctx->font; } /* @@ -950,22 +599,27 @@ void ui_panel( ui_rect in_rect, ui_rect out_panel ) ui_fill( in_rect, ui_colour( k_ui_bg+1 ) ); ui_outline( in_rect, 1, ui_colour( k_ui_bg+7 ), 0 ); rect_copy( in_rect, out_panel ); - ui_rect_pad( out_panel, (ui_px[2]){ k_ui_padding, k_ui_padding } ); + ui_rect_pad( out_panel, NULL ); } void ui_label( ui_rect rect, const char *text, ui_px size, ui_px gap, ui_rect r ) { ui_rect l; - ui_px width = (ui_text_line_width(text)+vg_ui.font->sx) * size; + ui_px width = (ui_text_line_width(text)+g_ui_ctx->font->sx) * size; ui_split( rect, k_ui_axis_v, width, gap, l, r ); ui_text( l, text, 1, k_ui_align_middle_left, 0 ); } +ui_px ui_standard_widget_height( ui_px count ) +{ + return (count * g_ui_ctx->font->sy + 18) * g_ui_ctx->scale; +} + void ui_standard_widget( ui_rect inout_panel, ui_rect out_rect, ui_px count ) { - ui_px height = (count * vg_ui.font->sy + 18) * k_ui_scale; - ui_split( inout_panel, k_ui_axis_h, height, k_ui_padding, + ui_px height = ui_standard_widget_height( count ); + ui_split( inout_panel, k_ui_axis_h, height, g_ui_ctx->padding, out_rect, inout_panel ); } @@ -978,34 +632,38 @@ void ui_info( ui_rect inout_panel, const char *text ) void ui_image( ui_rect rect, GLuint image ) { - ui_flush( k_ui_shader_colour, vg.window_x, vg.window_y ); + ui_flush( k_ui_shader_colour ); glActiveTexture( GL_TEXTURE0 ); glBindTexture( GL_TEXTURE_2D, image ); ui_fill_rect( rect, 0xffffffff, (ui_px[4]){ 0,256,256,0 } ); - ui_flush( k_ui_shader_image, vg.window_x, vg.window_y ); + ui_flush( k_ui_shader_image ); } void ui_defocus_all(void) { - if( vg_ui.focused_control_type == k_ui_control_textbox ){ + if( g_ui_ctx->focused_control_type == k_ui_control_textbox ) + { +#if VG_ANDROID +#else SDL_StopTextInput(); - if( vg_ui.textbox.callbacks.escape ) - vg_ui.textbox.callbacks.escape(); +#endif + if( g_ui_ctx->textbox.callbacks.escape ) + g_ui_ctx->textbox.callbacks.escape(); } - vg_ui.focused_control_id = NULL; - vg_ui.focused_control_hit = 0; - vg_ui.focused_control_type = k_ui_control_none; + g_ui_ctx->focused_control_id = NULL; + g_ui_ctx->focused_control_hit = 0; + g_ui_ctx->focused_control_type = k_ui_control_none; } enum ui_button_state ui_button_base( ui_rect rect ) { int clickup= ui_click_up(UI_MOUSE_LEFT), click = ui_clicking(UI_MOUSE_LEFT) | clickup, - target = ui_inside_rect( rect, vg_ui.mouse_click ) && click, - hover = ui_inside_rect( rect, vg_ui.mouse ); + target = ui_inside_rect( rect, g_ui_ctx->mouse_click ) && click, + hover = ui_inside_rect( rect, g_ui_ctx->mouse ); - if( vg_ui.focused_control_type != k_ui_control_none ) + if( g_ui_ctx->focused_control_type != k_ui_control_none ) { clickup = 0; click = 0; @@ -1014,7 +672,7 @@ enum ui_button_state ui_button_base( ui_rect rect ) } if( hover ) - vg_ui.cursor = k_ui_cursor_hand; + g_ui_ctx->cursor = k_ui_cursor_hand; if( click ) { @@ -1024,7 +682,7 @@ enum ui_button_state ui_button_base( ui_rect rect ) { if( clickup ) { - vg_ui.ignore_input_frames = 2; + g_ui_ctx->ignore_input_frames = 2; ui_defocus_all(); return k_ui_button_click; } @@ -1049,19 +707,19 @@ enum ui_button_state ui_colourbutton( ui_rect rect, { enum ui_button_state state = ui_button_base( rect ); - u32 col_base = vg_ui.scheme[ colour ], - col_highlight = vg_ui.scheme[ hi_colour? hi_colour: k_ui_fg ], - col_hover = vg_ui.scheme[ hover_colour? hover_colour: + u32 col_base = g_ui_ctx->scheme[ colour ], + col_highlight = g_ui_ctx->scheme[ hi_colour? hi_colour: k_ui_fg ], + col_hover = g_ui_ctx->scheme[ hover_colour? hover_colour: colour + k_ui_brighter ]; if( state == k_ui_button_click ) { ui_fill( rect, col_highlight ); - rect_copy( rect, vg_ui.click_fader ); - rect_copy( rect, vg_ui.click_fader_end ); - vg_ui.click_fader_end[3] = 0; - ui_rect_center( rect, vg_ui.click_fader_end ); - vg_ui.click_fade_opacity = 1.0f; + rect_copy( rect, g_ui_ctx->click_fader ); + rect_copy( rect, g_ui_ctx->click_fader_end ); + g_ui_ctx->click_fader_end[3] = 0; + ui_rect_center( rect, g_ui_ctx->click_fader_end ); + g_ui_ctx->click_fade_opacity = 1.0f; } else if( state == k_ui_button_holding_inside ) { @@ -1109,19 +767,20 @@ enum ui_button_state ui_button( ui_rect inout_panel, const char *string ) } static void ui_enum_post(void); -void ui_postrender(void) +void ui_postrender( f32 delta_time ) { - if( vg_ui.click_fade_opacity > 0.0f ){ - float scale = vg_ui.click_fade_opacity; + if( g_ui_ctx->click_fade_opacity > 0.0f ) + { + float scale = g_ui_ctx->click_fade_opacity; scale = vg_maxf( 1.0f/255.0f, scale*scale ); - vg_ui.click_fade_opacity -= vg.time_frame_delta * 3.8f; + g_ui_ctx->click_fade_opacity -= delta_time * 3.8f; u32 colour = (0x00ffffff & ui_colour(k_ui_fg))|0x7f000000; v4f begin, end, dest; for( int i=0; i<4; i++ ){ - begin[i] = vg_ui.click_fader[i]; - end[i] = vg_ui.click_fader_end[i]+1; + begin[i] = g_ui_ctx->click_fader[i]; + end[i] = g_ui_ctx->click_fader_end[i]+1; } v4_lerp( end, begin, scale, dest ); @@ -1134,16 +793,16 @@ void ui_postrender(void) ui_fill( rect, colour ); } - if( vg_ui.focused_control_type == k_ui_control_enum ){ + if( g_ui_ctx->focused_control_type == k_ui_control_enum ){ ui_enum_post(); } - else if( vg_ui.focused_control_type == k_ui_control_modal ){ - ui_rect screen = {0,0,vg.window_x,vg.window_y}; + else if( g_ui_ctx->focused_control_type == k_ui_control_modal ){ + ui_rect screen = { 0,0, g_ui_ctx->area[0], g_ui_ctx->area[1] }; ui_fill( screen, 0xa0000000 ); ui_rect box = {0,0,400,200}; u32 colour = ui_colour(k_ui_fg), - type = vg_ui.modal.options & UI_MODAL_TYPE_BITS; + type = g_ui_ctx->modal.options & UI_MODAL_TYPE_BITS; if ( type == 1 ) colour = ui_colour(k_ui_green); else if( type == 2 ) colour = ui_colour(k_ui_red); else if( type == 3 ) colour = ui_colour(k_ui_yellow); @@ -1159,8 +818,8 @@ void ui_postrender(void) ui_rect row0, row1, btn; ui_split_ratio( message, k_ui_axis_h, 0.5f, 0, row0, row1 ); - row0[0] += vg_ui.font->sx; - ui_ntext( row0, vg_ui.modal.message, (box[2]/vg_ui.font->sx)-2, 1, + row0[0] += g_ui_ctx->font->sx; + ui_ntext( row0, g_ui_ctx->modal.message, (box[2]/g_ui_ctx->font->sx)-2, 1, k_ui_align_left, colour ); rect_copy( row1, btn ); @@ -1168,30 +827,19 @@ void ui_postrender(void) btn[3] = 28; ui_rect_center( row1, btn ); - vg_ui.focused_control_type = k_ui_control_none; /* HACK */ + g_ui_ctx->focused_control_type = k_ui_control_none; /* HACK */ if( ui_button_text( btn, "OK", 1 ) != 1 ) - vg_ui.focused_control_hit = 1; - vg_ui.focused_control_type = k_ui_control_modal; /* HACK */ - vg_ui.wants_mouse = 1; + g_ui_ctx->focused_control_hit = 1; + g_ui_ctx->focused_control_type = k_ui_control_modal; /* HACK */ + g_ui_ctx->wants_mouse = 1; } - ui_flush( k_ui_shader_colour, vg.window_x, vg.window_y ); + ui_flush( k_ui_shader_colour ); - if( !vg_ui.focused_control_hit ){ + if( !g_ui_ctx->focused_control_hit ) + { ui_defocus_all(); } - - if( vg_ui.wants_mouse ){ - SDL_SetWindowGrab( vg.window, SDL_FALSE ); - SDL_SetRelativeMouseMode( SDL_FALSE ); - } - else{ - SDL_SetWindowGrab( vg.window, SDL_TRUE ); - SDL_SetRelativeMouseMode( SDL_TRUE ); - } - - SDL_SetCursor( vg_ui.cursor_map[ vg_ui.cursor ] ); - SDL_ShowCursor(1); } /* @@ -1213,7 +861,7 @@ int ui_checkbox( ui_rect inout_panel, const char *str_label, i32 *data ) ui_standard_widget( inout_panel, rect, 1 ); ui_split( rect, k_ui_axis_v, -rect[3], 0, label, box ); - ui_text( label, str_label, k_ui_scale, k_ui_align_middle_left, 0 ); + ui_text( label, str_label, g_ui_ctx->scale, k_ui_align_middle_left, 0 ); enum ui_button_state state = ui_checkbox_base( box, data ); @@ -1263,7 +911,7 @@ void ui_enum( ui_rect inout_panel, const char *str_label, { ui_rect rect, label, box; ui_standard_widget( inout_panel, rect, 1 ); - ui_label( rect, str_label, k_ui_scale, 0, box ); + ui_label( rect, str_label, g_ui_ctx->scale, 0, box ); const char *display = "OUT OF RANGE"; int valid = 0; @@ -1275,12 +923,12 @@ void ui_enum( ui_rect inout_panel, const char *str_label, } } - if( ui_button_text( box, display, k_ui_scale ) == 1 ){ - vg_ui.focused_control_type = k_ui_control_enum; - vg_ui.ptr_enum = value; - vg_ui._enum.option_count = len; - vg_ui._enum.options = options; - rect_copy( box, vg_ui._enum.rect ); + if( ui_button_text( box, display, g_ui_ctx->scale ) == 1 ){ + g_ui_ctx->focused_control_type = k_ui_control_enum; + g_ui_ctx->ptr_enum = value; + g_ui_ctx->_enum.option_count = len; + g_ui_ctx->_enum.options = options; + rect_copy( box, g_ui_ctx->_enum.rect ); } if( !valid ) @@ -1289,41 +937,41 @@ void ui_enum( ui_rect inout_panel, const char *str_label, static void ui_enum_post(void){ ui_rect drawer; - rect_copy( vg_ui._enum.rect, drawer ); - drawer[3] *= vg_ui._enum.option_count; + rect_copy( g_ui_ctx->_enum.rect, drawer ); + drawer[3] *= g_ui_ctx->_enum.option_count; int close = 0; int clickany= ui_click_up(UI_MOUSE_LEFT|UI_MOUSE_RIGHT|UI_MOUSE_MIDDLE), - hover = ui_inside_rect( drawer, vg_ui.mouse ); + hover = ui_inside_rect( drawer, g_ui_ctx->mouse ); if( clickany && !hover ){ return; } /* HACK */ - vg_ui.focused_control_type = k_ui_control_none; - i32 *value = vg_ui.ptr_enum; + g_ui_ctx->focused_control_type = k_ui_control_none; + i32 *value = g_ui_ctx->ptr_enum; - for( u32 i=0; i_enum.option_count; i++ ){ ui_rect button; - ui_split( drawer, k_ui_axis_h, vg_ui._enum.rect[3], 0, button,drawer ); + ui_split( drawer, k_ui_axis_h, g_ui_ctx->_enum.rect[3], 0, button,drawer ); enum ui_scheme_colour colour = k_ui_bg+3; - if( vg_ui._enum.options[i].value == *value ) + if( g_ui_ctx->_enum.options[i].value == *value ) colour = k_ui_orange; - if( ui_colourbutton_text( button, vg_ui._enum.options[i].alias, - k_ui_scale, colour ) == 1 ){ - *value = vg_ui._enum.options[i].value; + if( ui_colourbutton_text( button, g_ui_ctx->_enum.options[i].alias, + g_ui_ctx->scale, colour ) == 1 ){ + *value = g_ui_ctx->_enum.options[i].value; close = 1; } } /* HACK */ - vg_ui.focused_control_type = k_ui_control_enum; + g_ui_ctx->focused_control_type = k_ui_control_enum; if( !close ) - vg_ui.focused_control_hit = 1; + g_ui_ctx->focused_control_hit = 1; } /* @@ -1343,7 +991,7 @@ enum ui_button_state ui_slider_base( f32 t; if( state & mask_using ) { - t = vg_clampf( (f32)(vg_ui.mouse[0] - box[0]) / (f32)( box[2] ), 0,1 ); + t = vg_clampf( (f32)(g_ui_ctx->mouse[0] - box[0]) / (f32)( box[2] ), 0,1 ); *value = vg_lerpf( min, max, t ); } else @@ -1390,7 +1038,7 @@ bool ui_slider( ui_rect inout_panel, const char *str_label, { ui_rect rect, label, box; ui_standard_widget( inout_panel, rect, 1 ); - ui_label( rect, str_label, k_ui_scale, 0, box ); + ui_label( rect, str_label, g_ui_ctx->scale, 0, box ); return ui_slider_standard( box, min, max, value, NULL ); } @@ -1441,8 +1089,8 @@ void ui_colourpicker( ui_rect inout_panel, const char *str_label, v4f value ) { for( u32 i=0; i<2; i ++ ){ hsv[1+i] = vg_clampf( - (f32)(vg_ui.mouse[i] - square[i]) / (f32)(square[2+i]), - 0.0f, 1.0f ); + (f32)(g_ui_ctx->mouse[i] - square[i]) / (f32)(square[2+i]), + 0.0f, 1.0f ); } hsv[2] = 1.0f-hsv[2]; @@ -1464,10 +1112,10 @@ void ui_colourpicker( ui_rect inout_panel, const char *str_label, v4f value ) ui_fill( preview, v4f_u32_colour( colour ) ); /* Draw hsv shader thingy */ - ui_flush( k_ui_shader_colour, vg.window_x, vg.window_y ); + ui_flush( k_ui_shader_colour ); ui_fill_rect( square, 0xffffffff, (ui_px[4]){ 0,256,256,0 } ); - vg_ui.hue = hsv[0]; - ui_flush( k_ui_shader_hsv, vg.window_x, vg.window_y ); + g_ui_ctx->hue = hsv[0]; + ui_flush( k_ui_shader_hsv ); ui_rect marker = { square[0] + hsv[1] * (f32)square[2] - 4, square[1] + (1.0f-hsv[2]) * (f32)square[3] - 4, @@ -1491,17 +1139,17 @@ void ui_colourpicker( ui_rect inout_panel, const char *str_label, v4f value ) static void _ui_textbox_make_selection( int *start, int *end ) { - *start = VG_MIN( vg_ui.textbox.cursor_pos, vg_ui.textbox.cursor_user ); - *end = VG_MAX( vg_ui.textbox.cursor_pos, vg_ui.textbox.cursor_user ); + *start = VG_MIN( g_ui_ctx->textbox.cursor_pos, g_ui_ctx->textbox.cursor_user ); + *end = VG_MAX( g_ui_ctx->textbox.cursor_pos, g_ui_ctx->textbox.cursor_user ); } void _ui_textbox_move_cursor( int *cursor0, int *cursor1, int dir, int snap_together ) { - *cursor0 = VG_MAX( 0, vg_ui.textbox.cursor_user + dir ); + *cursor0 = VG_MAX( 0, g_ui_ctx->textbox.cursor_user + dir ); *cursor0 = VG_MIN( - VG_MIN( vg_ui.textbox.len-1, strlen( vg_ui.textbuf )), + VG_MIN( g_ui_ctx->textbox.len-1, strlen( g_ui_ctx->textbuf )), *cursor0 ); if( snap_together ) @@ -1510,19 +1158,19 @@ void _ui_textbox_move_cursor( int *cursor0, int *cursor1, static int _ui_textbox_makeroom( int datastart, int length ) { - int move_to = VG_MIN( datastart+length, vg_ui.textbox.len-1 ); - int move_amount = strlen( vg_ui.textbuf )-datastart; - int move_end = VG_MIN( move_to+move_amount, vg_ui.textbox.len-1 ); + int move_to = VG_MIN( datastart+length, g_ui_ctx->textbox.len-1 ); + int move_amount = strlen( g_ui_ctx->textbuf )-datastart; + int move_end = VG_MIN( move_to+move_amount, g_ui_ctx->textbox.len-1 ); move_amount = move_end-move_to; if( move_amount ) - memmove( &vg_ui.textbuf[ move_to ], - &vg_ui.textbuf[ datastart ], + memmove( &g_ui_ctx->textbuf[ move_to ], + &g_ui_ctx->textbuf[ datastart ], move_end-move_to ); - vg_ui.textbuf[ move_end ] = '\0'; + g_ui_ctx->textbuf[ move_end ] = '\0'; - return VG_MIN( length, vg_ui.textbox.len-datastart-1 ); + return VG_MIN( length, g_ui_ctx->textbox.len-datastart-1 ); } int _ui_textbox_delete_char( int direction ) @@ -1532,7 +1180,7 @@ int _ui_textbox_delete_char( int direction ) /* There is no selection */ if( !(end-start) ){ - if( direction == 1 ) end = VG_MIN( end+1, strlen( vg_ui.textbuf ) ); + if( direction == 1 ) end = VG_MIN( end+1, strlen( g_ui_ctx->textbuf ) ); else if( direction == -1 ) start = VG_MAX( start-1, 0 ); } @@ -1541,9 +1189,9 @@ int _ui_textbox_delete_char( int direction ) return start; /* Copy the end->terminator to start */ - int remaining_length = strlen( vg_ui.textbuf )+1-end; - memmove( &vg_ui.textbuf[ start ], - &vg_ui.textbuf[ end ], + int remaining_length = strlen( g_ui_ctx->textbuf )+1-end; + memmove( &g_ui_ctx->textbuf[ start ], + &g_ui_ctx->textbuf[ end ], remaining_length ); return start; } @@ -1554,28 +1202,38 @@ static void _ui_textbox_to_clipboard(void) _ui_textbox_make_selection( &start, &end ); char buffer[512]; - if( end-start ){ - memcpy( buffer, &vg_ui.textbuf[ start ], end-start ); + if( end-start ) + { + memcpy( buffer, &g_ui_ctx->textbuf[ start ], end-start ); buffer[ end-start ] = 0x00; + +#ifdef VG_ANDROID +#else SDL_SetClipboardText( buffer ); +#endif } } static void _ui_textbox_change_callback(void) { - if( vg_ui.textbox.callbacks.change ){ - vg_ui.textbox.callbacks.change( vg_ui.textbuf, vg_ui.textbox.len ); + if( g_ui_ctx->textbox.callbacks.change ) + { + g_ui_ctx->textbox.callbacks.change( g_ui_ctx->textbuf, + g_ui_ctx->textbox.len ); /* we gave permission to modify the buffer in this callback so.. */ - int len = strlen( vg_ui.textbuf ); - vg_ui.textbox.cursor_user = VG_MIN( vg_ui.textbox.cursor_user, len ); - vg_ui.textbox.cursor_pos = VG_MIN( vg_ui.textbox.cursor_pos, len ); + int len = strlen( g_ui_ctx->textbuf ); + g_ui_ctx->textbox.cursor_user = VG_MIN( g_ui_ctx->textbox.cursor_user, len ); + g_ui_ctx->textbox.cursor_pos = VG_MIN( g_ui_ctx->textbox.cursor_pos, len ); } } void ui_start_modal( const char *message, u32 options ); static void _ui_textbox_clipboard_paste(void) { +#if VG_ANDROID + return; +#else if( !SDL_HasClipboardText() ) return; @@ -1587,54 +1245,55 @@ static void _ui_textbox_clipboard_paste(void) int datastart = _ui_textbox_delete_char( 0 ); int length = strlen( text ); - if( (vg_ui.textbox.len - strlen(vg_ui.textbuf)) < length ){ + if( (g_ui_ctx->textbox.len - strlen(g_ui_ctx->textbuf)) < length ){ ui_start_modal( "Clipboard content exceeds buffer size.", UI_MODAL_BAD ); return; } int cpylength = _ui_textbox_makeroom( datastart, length ); - memcpy( vg_ui.textbuf + datastart, text, cpylength); - _ui_textbox_move_cursor( &vg_ui.textbox.cursor_user, - &vg_ui.textbox.cursor_pos, cpylength, 1 ); + memcpy( g_ui_ctx->textbuf + datastart, text, cpylength); + _ui_textbox_move_cursor( &g_ui_ctx->textbox.cursor_user, + &g_ui_ctx->textbox.cursor_pos, cpylength, 1 ); SDL_free( text ); _ui_textbox_change_callback(); +#endif } void _ui_textbox_put_char( char c ) { - vg_ui.textbox.cursor_user = _ui_textbox_delete_char(0); - if( (vg_ui.textbox.len - strlen(vg_ui.textbuf)) <= 1 ) return; + g_ui_ctx->textbox.cursor_user = _ui_textbox_delete_char(0); + if( (g_ui_ctx->textbox.len - strlen(g_ui_ctx->textbuf)) <= 1 ) return; - if( _ui_textbox_makeroom( vg_ui.textbox.cursor_user, 1 ) ) - vg_ui.textbuf[ vg_ui.textbox.cursor_user ] = c; + if( _ui_textbox_makeroom( g_ui_ctx->textbox.cursor_user, 1 ) ) + g_ui_ctx->textbuf[ g_ui_ctx->textbox.cursor_user ] = c; - _ui_textbox_move_cursor( &vg_ui.textbox.cursor_user, - &vg_ui.textbox.cursor_pos, 1, 1 ); + _ui_textbox_move_cursor( &g_ui_ctx->textbox.cursor_user, + &g_ui_ctx->textbox.cursor_pos, 1, 1 ); } /* Receed secondary cursor */ void _ui_textbox_left_select(void) { - _ui_textbox_move_cursor( &vg_ui.textbox.cursor_user, NULL, -1, 0 ); + _ui_textbox_move_cursor( &g_ui_ctx->textbox.cursor_user, NULL, -1, 0 ); } /* Match and receed both cursors */ void _ui_textbox_left(void) { - int cursor_diff = vg_ui.textbox.cursor_pos - vg_ui.textbox.cursor_user? 0: 1; + int cursor_diff = g_ui_ctx->textbox.cursor_pos - g_ui_ctx->textbox.cursor_user? 0: 1; - _ui_textbox_move_cursor( &vg_ui.textbox.cursor_user, - &vg_ui.textbox.cursor_pos, -cursor_diff, 1 ); + _ui_textbox_move_cursor( &g_ui_ctx->textbox.cursor_user, + &g_ui_ctx->textbox.cursor_pos, -cursor_diff, 1 ); } void _ui_textbox_up(void) { - if( vg_ui.textbox.flags & UI_TEXTBOX_MULTILINE ){ - int line_begin = vg_ui.textbox.cursor_user; + if( g_ui_ctx->textbox.flags & UI_TEXTBOX_MULTILINE ){ + int line_begin = g_ui_ctx->textbox.cursor_user; while( line_begin ){ - if( vg_ui.textbuf[ line_begin-1 ] == '\n' ){ + if( g_ui_ctx->textbuf[ line_begin-1 ] == '\n' ){ break; } @@ -1645,56 +1304,56 @@ void _ui_textbox_up(void) int line_above_begin = line_begin-1; while( line_above_begin ){ - if( vg_ui.textbuf[ line_above_begin-1 ] == '\n' ){ + if( g_ui_ctx->textbuf[ line_above_begin-1 ] == '\n' ){ break; } line_above_begin --; } - int offset = vg_ui.textbox.cursor_user - line_begin, + int offset = g_ui_ctx->textbox.cursor_user - line_begin, line_length_above = line_begin - line_above_begin -1; offset = VG_MIN( line_length_above, offset ); - vg_ui.textbox.cursor_user = line_above_begin+offset; - vg_ui.textbox.cursor_pos = line_above_begin+offset; + g_ui_ctx->textbox.cursor_user = line_above_begin+offset; + g_ui_ctx->textbox.cursor_pos = line_above_begin+offset; } else{ - vg_ui.textbox.cursor_user = line_begin; - vg_ui.textbox.cursor_pos = line_begin; + g_ui_ctx->textbox.cursor_user = line_begin; + g_ui_ctx->textbox.cursor_pos = line_begin; } } else{ - if( vg_ui.textbox.callbacks.up ){ - vg_ui.textbox.callbacks.up( vg_ui.textbuf, vg_ui.textbox.len ); + if( g_ui_ctx->textbox.callbacks.up ){ + g_ui_ctx->textbox.callbacks.up( g_ui_ctx->textbuf, g_ui_ctx->textbox.len ); } } } void _ui_textbox_down(void) { - if( vg_ui.textbox.flags & UI_TEXTBOX_MULTILINE ){ - int line_begin = vg_ui.textbox.cursor_user; + if( g_ui_ctx->textbox.flags & UI_TEXTBOX_MULTILINE ){ + int line_begin = g_ui_ctx->textbox.cursor_user; while( line_begin ){ - if( vg_ui.textbuf[ line_begin-1 ] == '\n' ){ + if( g_ui_ctx->textbuf[ line_begin-1 ] == '\n' ){ break; } line_begin --; } - int line_below_begin = vg_ui.textbox.cursor_user; + int line_below_begin = g_ui_ctx->textbox.cursor_user; while(1){ - if( vg_ui.textbuf[ line_below_begin ] == '\0' ){ - vg_ui.textbox.cursor_user = line_below_begin; - vg_ui.textbox.cursor_pos = line_below_begin; + if( g_ui_ctx->textbuf[ line_below_begin ] == '\0' ){ + g_ui_ctx->textbox.cursor_user = line_below_begin; + g_ui_ctx->textbox.cursor_pos = line_below_begin; return; } - if( vg_ui.textbuf[ line_below_begin ] == '\n' ){ + if( g_ui_ctx->textbuf[ line_below_begin ] == '\n' ){ line_below_begin ++; break; } @@ -1704,67 +1363,67 @@ void _ui_textbox_down(void) int line_below_end = line_below_begin; while(1){ - if( vg_ui.textbuf[ line_below_end ] == '\0' || - vg_ui.textbuf[ line_below_end ] == '\n' ){ + if( g_ui_ctx->textbuf[ line_below_end ] == '\0' || + g_ui_ctx->textbuf[ line_below_end ] == '\n' ){ line_below_end ++; break; } line_below_end ++; } - int offset = vg_ui.textbox.cursor_user - line_begin, + int offset = g_ui_ctx->textbox.cursor_user - line_begin, line_length_below = line_below_end - line_below_begin -1; offset = VG_MIN( line_length_below, offset ); - vg_ui.textbox.cursor_user = line_below_begin+offset; - vg_ui.textbox.cursor_pos = line_below_begin+offset; + g_ui_ctx->textbox.cursor_user = line_below_begin+offset; + g_ui_ctx->textbox.cursor_pos = line_below_begin+offset; } else{ - if( vg_ui.textbox.callbacks.down ){ - vg_ui.textbox.callbacks.down( vg_ui.textbuf, vg_ui.textbox.len ); + if( g_ui_ctx->textbox.callbacks.down ){ + g_ui_ctx->textbox.callbacks.down( g_ui_ctx->textbuf, g_ui_ctx->textbox.len ); } } } void _ui_textbox_right_select(void) { - _ui_textbox_move_cursor( &vg_ui.textbox.cursor_user, NULL, 1, 0 ); + _ui_textbox_move_cursor( &g_ui_ctx->textbox.cursor_user, NULL, 1, 0 ); } void _ui_textbox_right(void) { - int cursor_diff = vg_ui.textbox.cursor_pos - vg_ui.textbox.cursor_user? 0: 1; + int cursor_diff = g_ui_ctx->textbox.cursor_pos - g_ui_ctx->textbox.cursor_user? 0: 1; - _ui_textbox_move_cursor( &vg_ui.textbox.cursor_user, - &vg_ui.textbox.cursor_pos, +cursor_diff, 1 ); + _ui_textbox_move_cursor( &g_ui_ctx->textbox.cursor_user, + &g_ui_ctx->textbox.cursor_pos, +cursor_diff, 1 ); } void _ui_textbox_backspace(void) { - if( vg_ui.focused_control_type == k_ui_control_textbox ){ - vg_ui.textbox.cursor_user = _ui_textbox_delete_char( -1 ); - vg_ui.textbox.cursor_pos = vg_ui.textbox.cursor_user; + if( g_ui_ctx->focused_control_type == k_ui_control_textbox ){ + g_ui_ctx->textbox.cursor_user = _ui_textbox_delete_char( -1 ); + g_ui_ctx->textbox.cursor_pos = g_ui_ctx->textbox.cursor_user; _ui_textbox_change_callback(); } } void _ui_textbox_delete(void) { - if( vg_ui.focused_control_type == k_ui_control_textbox ){ - vg_ui.textbox.cursor_user = _ui_textbox_delete_char( 1 ); - vg_ui.textbox.cursor_pos = vg_ui.textbox.cursor_user; + if( g_ui_ctx->focused_control_type == k_ui_control_textbox ){ + g_ui_ctx->textbox.cursor_user = _ui_textbox_delete_char( 1 ); + g_ui_ctx->textbox.cursor_pos = g_ui_ctx->textbox.cursor_user; _ui_textbox_change_callback(); } } void _ui_textbox_home_select(void) { - i32 start = vg_ui.textbox.cursor_user; + i32 start = g_ui_ctx->textbox.cursor_user; - if( vg_ui.textbox.flags & UI_TEXTBOX_MULTILINE ){ + if( g_ui_ctx->textbox.flags & UI_TEXTBOX_MULTILINE ){ while( start ){ - if( vg_ui.textbuf[start-1] == '\n' ) + if( g_ui_ctx->textbuf[start-1] == '\n' ) break; else start --; @@ -1773,69 +1432,69 @@ void _ui_textbox_home_select(void) else start = 0; - vg_ui.textbox.cursor_user = start; + g_ui_ctx->textbox.cursor_user = start; } void _ui_textbox_home(void) { _ui_textbox_home_select(); - vg_ui.textbox.cursor_pos = vg_ui.textbox.cursor_user; + g_ui_ctx->textbox.cursor_pos = g_ui_ctx->textbox.cursor_user; } void _ui_textbox_end_select(void) { - i32 end = vg_ui.textbox.cursor_user; + i32 end = g_ui_ctx->textbox.cursor_user; - if( vg_ui.textbox.flags & UI_TEXTBOX_MULTILINE ){ - while( vg_ui.textbuf[end] ){ - if( vg_ui.textbuf[end] == '\n' ) + if( g_ui_ctx->textbox.flags & UI_TEXTBOX_MULTILINE ){ + while( g_ui_ctx->textbuf[end] ){ + if( g_ui_ctx->textbuf[end] == '\n' ) break; else end ++; } } else - end = VG_MIN( vg_ui.textbox.len-1, strlen(vg_ui.textbuf) ); + end = VG_MIN( g_ui_ctx->textbox.len-1, strlen(g_ui_ctx->textbuf) ); - vg_ui.textbox.cursor_user = end; + g_ui_ctx->textbox.cursor_user = end; } void _ui_textbox_end(void) { _ui_textbox_end_select(); - vg_ui.textbox.cursor_pos = vg_ui.textbox.cursor_user; + g_ui_ctx->textbox.cursor_pos = g_ui_ctx->textbox.cursor_user; } void _ui_textbox_select_all(void) { - _ui_textbox_move_cursor( &vg_ui.textbox.cursor_user, NULL, 10000, 0); - _ui_textbox_move_cursor( &vg_ui.textbox.cursor_pos, NULL, -10000, 0); + _ui_textbox_move_cursor( &g_ui_ctx->textbox.cursor_user, NULL, 10000, 0); + _ui_textbox_move_cursor( &g_ui_ctx->textbox.cursor_pos, NULL, -10000, 0); } void _ui_textbox_cut(void) { _ui_textbox_to_clipboard(); - vg_ui.textbox.cursor_user = _ui_textbox_delete_char(0); - vg_ui.textbox.cursor_pos = vg_ui.textbox.cursor_user; + g_ui_ctx->textbox.cursor_user = _ui_textbox_delete_char(0); + g_ui_ctx->textbox.cursor_pos = g_ui_ctx->textbox.cursor_user; _ui_textbox_change_callback(); } void _ui_textbox_enter(void) { - if( vg_ui.focused_control_type == k_ui_control_textbox ){ - vg_ui.ignore_input_frames = 2; + if( g_ui_ctx->focused_control_type == k_ui_control_textbox ){ + g_ui_ctx->ignore_input_frames = 2; - if( vg_ui.textbox.callbacks.enter ) - vg_ui.textbox.callbacks.enter( vg_ui.textbuf, vg_ui.textbox.len ); + if( g_ui_ctx->textbox.callbacks.enter ) + g_ui_ctx->textbox.callbacks.enter( g_ui_ctx->textbuf, g_ui_ctx->textbox.len ); - if( vg_ui.focused_control_type != k_ui_control_textbox ) return; + if( g_ui_ctx->focused_control_type != k_ui_control_textbox ) return; - if( vg_ui.textbox.flags & UI_TEXTBOX_MULTILINE ){ + if( g_ui_ctx->textbox.flags & UI_TEXTBOX_MULTILINE ){ _ui_textbox_put_char( '\n' ); _ui_textbox_change_callback(); } else{ - if( !(vg_ui.textbox.flags & UI_TEXTBOX_AUTOFOCUS ) ) + if( !(g_ui_ctx->textbox.flags & UI_TEXTBOX_AUTOFOCUS ) ) ui_defocus_all(); } } @@ -1851,7 +1510,7 @@ static void _ui_textbox_calc_index_from_grid( int co[3], int wrap_length ){ int i[3] = {0,0,0}; char c; - while( (c = vg_ui.textbuf[i[2]]) ){ + while( (c = g_ui_ctx->textbuf[i[2]]) ){ if( i[1]==co[1] && i[0]>=co[0] ) break; if( i[0] >= wrap_length ){ @@ -1890,7 +1549,7 @@ static void _ui_textbox_index_calc_coords( int co[3], int wrap_length ){ char c; int i=0; - while( (c = vg_ui.textbuf[i ++]) ){ + while( (c = g_ui_ctx->textbuf[i ++]) ){ if( i > co[2] ) break; if( co[0] >= wrap_length ){ co[1] ++; @@ -1914,7 +1573,7 @@ static void _ui_textbox_index_calc_coords( int co[3], int wrap_length ){ static int _ui_textbox_run_remaining( int index[3], int wrap_length ){ int i=0, printed_chars=0; char c; - while( (c = vg_ui.textbuf[index[2] + (i ++)]) ){ + while( (c = g_ui_ctx->textbuf[index[2] + (i ++)]) ){ if( index[0]+i >= wrap_length ) break; if( c >= 32 && c <= 126 ) printed_chars ++; else if( c == '\n' ) break; @@ -1938,12 +1597,12 @@ int ui_textbox( ui_rect inout_panel, const char *label, int clickup= ui_click_up(UI_MOUSE_LEFT), clickdown = ui_click_down(UI_MOUSE_LEFT), click = ui_clicking(UI_MOUSE_LEFT) | clickup, - target = ui_inside_rect( rect, vg_ui.mouse_click ) && click, - hover = ui_inside_rect( rect, vg_ui.mouse ); + target = ui_inside_rect( rect, g_ui_ctx->mouse_click ) && click, + hover = ui_inside_rect( rect, g_ui_ctx->mouse ); /* allow instant transitions from textbox->textbox */ - if( (vg_ui.focused_control_type != k_ui_control_none) && - (vg_ui.focused_control_type != k_ui_control_textbox) ){ + if( (g_ui_ctx->focused_control_type != k_ui_control_none) && + (g_ui_ctx->focused_control_type != k_ui_control_textbox) ){ clickup = 0; clickdown = 0; click = 0; @@ -1962,7 +1621,7 @@ int ui_textbox( ui_rect inout_panel, const char *label, rect_copy( rect, text_rect ); if( flags & UI_TEXTBOX_MULTILINE ) text_rect[3] = rect[3]-16; - else text_rect[3] = vg_ui.font->sy; + else text_rect[3] = g_ui_ctx->font->sy; text_rect[2] -= 16; ui_rect_center( rect, text_rect ); @@ -1970,14 +1629,14 @@ int ui_textbox( ui_rect inout_panel, const char *label, ui_px wrap_length = 1024; if( flags & UI_TEXTBOX_WRAP ) - wrap_length = text_rect[2] / vg_ui.font->sx; + wrap_length = text_rect[2] / g_ui_ctx->font->sx; if( hover ) { - vg_ui.cursor = k_ui_cursor_ibeam; + g_ui_ctx->cursor = k_ui_cursor_ibeam; } - if( vg_ui.focused_control_id == buf ) + if( g_ui_ctx->focused_control_id == buf ) { ui_fill( rect, col_base ); ui_ntext( text_rect, buf, wrap_length, 1, k_ui_align_left, 0 ); @@ -1988,16 +1647,16 @@ int ui_textbox( ui_rect inout_panel, const char *label, } else { - vg_ui.focused_control_hit = 1; + g_ui_ctx->focused_control_hit = 1; if( click && target ){ int p0[3] ={ - (vg_ui.mouse_click[0] - text_rect[0]) / vg_ui.font->sx, - (vg_ui.mouse_click[1] - text_rect[1]) / vg_ui.font->sy, + (g_ui_ctx->mouse_click[0] - text_rect[0]) / g_ui_ctx->font->sx, + (g_ui_ctx->mouse_click[1] - text_rect[1]) / g_ui_ctx->font->sy, -1 }, p1[3] = { - (vg_ui.mouse[0] - text_rect[0]) / vg_ui.font->sx, - (vg_ui.mouse[1] - text_rect[1]) / vg_ui.font->sy, + (g_ui_ctx->mouse[0] - text_rect[0]) / g_ui_ctx->font->sx, + (g_ui_ctx->mouse[1] - text_rect[1]) / g_ui_ctx->font->sy, -1 }; @@ -2006,23 +1665,23 @@ int ui_textbox( ui_rect inout_panel, const char *label, _ui_textbox_calc_index_from_grid( p0, wrap_length ); _ui_textbox_calc_index_from_grid( p1, wrap_length ); - vg_ui.textbox.cursor_pos = p0[2]; - vg_ui.textbox.cursor_user = p1[2]; + g_ui_ctx->textbox.cursor_pos = p0[2]; + g_ui_ctx->textbox.cursor_user = p1[2]; } else { int max = strlen( buf ); - vg_ui.textbox.cursor_pos = VG_MAX( 0, VG_MIN( max, p0[0] )), - vg_ui.textbox.cursor_user = VG_MAX( 0, VG_MIN( max, p1[0] )); + g_ui_ctx->textbox.cursor_pos = VG_MAX( 0, VG_MIN( max, p0[0] )), + g_ui_ctx->textbox.cursor_user = VG_MAX( 0, VG_MIN( max, p1[0] )); } } - ui_outline( rect, -2, vg_ui.scheme[ k_ui_orange ], 0 ); + ui_outline( rect, -2, g_ui_ctx->scheme[ k_ui_orange ], 0 ); ui_rect cursor; - int c0 = vg_ui.textbox.cursor_pos, - c1 = vg_ui.textbox.cursor_user, + int c0 = g_ui_ctx->textbox.cursor_pos, + c1 = g_ui_ctx->textbox.cursor_user, start = VG_MIN( c0, c1 ), end = VG_MAX( c0, c1 ), chars = end-start; @@ -2036,12 +1695,12 @@ int ui_textbox( ui_rect inout_panel, const char *label, if( start==end ) { - cursor[0] = text_rect[0] + pos[0]*vg_ui.font->sx-1; + cursor[0] = text_rect[0] + pos[0]*g_ui_ctx->font->sx-1; cursor[1] = text_rect[1] + pos[1]*14; cursor[2] = 2; cursor[3] = 13; ui_fill( cursor, col_cursor ); - rect_copy( cursor, vg_ui.click_fader_end ); + rect_copy( cursor, g_ui_ctx->click_fader_end ); } else { @@ -2050,9 +1709,9 @@ int ui_textbox( ui_rect inout_panel, const char *label, int run = _ui_textbox_run_remaining( pos, wrap_length ); run = VG_MIN( run, remaining ); - cursor[0] = text_rect[0] + pos[0]*vg_ui.font->sx-1; + cursor[0] = text_rect[0] + pos[0]*g_ui_ctx->font->sx-1; cursor[1] = text_rect[1] + pos[1]*14; - cursor[2] = (float)(run)*(float)vg_ui.font->sx; + cursor[2] = (float)(run)*(float)g_ui_ctx->font->sx; cursor[3] = 13; ui_fill( cursor, col_cursor ); @@ -2062,12 +1721,12 @@ int ui_textbox( ui_rect inout_panel, const char *label, pos[1] ++; pos[2] += run; } - rect_copy( cursor, vg_ui.click_fader_end ); + rect_copy( cursor, g_ui_ctx->click_fader_end ); } } else { - cursor[0] = text_rect[0] + start*vg_ui.font->sx-1; + cursor[0] = text_rect[0] + start*g_ui_ctx->font->sx-1; cursor[1] = text_rect[1]; cursor[3] = 13; @@ -2077,16 +1736,16 @@ int ui_textbox( ui_rect inout_panel, const char *label, } else { - cursor[2] = (float)(chars)*(float)vg_ui.font->sx; + cursor[2] = (float)(chars)*(float)g_ui_ctx->font->sx; } - if( (vg_ui.click_fade_opacity<=0.0f) && + if( (g_ui_ctx->click_fade_opacity<=0.0f) && ui_clip( rect, cursor, cursor ) ) { ui_fill( cursor, col_cursor ); } - rect_copy( cursor, vg_ui.click_fader_end ); + rect_copy( cursor, g_ui_ctx->click_fader_end ); } } @@ -2100,32 +1759,35 @@ int ui_textbox( ui_rect inout_panel, const char *label, ui_defocus_all(); ui_fill( rect, col_highlight ); - vg_ui.ignore_input_frames = 2; - rect_copy( rect, vg_ui.click_fader ); - rect_copy( rect, vg_ui.click_fader_end ); + g_ui_ctx->ignore_input_frames = 2; + rect_copy( rect, g_ui_ctx->click_fader ); + rect_copy( rect, g_ui_ctx->click_fader_end ); - vg_ui.click_fade_opacity = 1.0f; - vg_ui.textbuf = buf; - vg_ui.focused_control_hit = 1; - vg_ui.focused_control_type = k_ui_control_textbox; - vg_ui.textbox.len = len; - vg_ui.textbox.flags = flags; - vg_ui.textbox.cursor_pos = 0; - vg_ui.textbox.cursor_user = 0; + g_ui_ctx->click_fade_opacity = 1.0f; + g_ui_ctx->textbuf = buf; + g_ui_ctx->focused_control_hit = 1; + g_ui_ctx->focused_control_type = k_ui_control_textbox; + g_ui_ctx->textbox.len = len; + g_ui_ctx->textbox.flags = flags; + g_ui_ctx->textbox.cursor_pos = 0; + g_ui_ctx->textbox.cursor_user = 0; if( callbacks ) { - vg_ui.textbox.callbacks = *callbacks; + g_ui_ctx->textbox.callbacks = *callbacks; } else { - vg_ui.textbox.callbacks.change = NULL; - vg_ui.textbox.callbacks.down = NULL; - vg_ui.textbox.callbacks.up = NULL; - vg_ui.textbox.callbacks.enter = NULL; + g_ui_ctx->textbox.callbacks.change = NULL; + g_ui_ctx->textbox.callbacks.down = NULL; + g_ui_ctx->textbox.callbacks.up = NULL; + g_ui_ctx->textbox.callbacks.enter = NULL; } +#ifdef VG_ANDROID +#else SDL_StartTextInput(); +#endif } } @@ -2163,7 +1825,8 @@ void ui_tabs( ui_rect inout_panel, ui_rect out_content_panel, ui_outline( inout_panel, 1, ui_colour( k_ui_bg+5 ), 0 ); rect_copy( inout_panel, out_content_panel ); - ui_rect_pad( out_content_panel, (ui_px[2]){ k_ui_padding, k_ui_padding } ); + ui_rect_pad( out_content_panel, + (ui_px[2]){ g_ui_ctx->padding, g_ui_ctx->padding } ); /* place buttons */ for( i32 i=0; ifocused_control_type = k_ui_control_modal; + g_ui_ctx->modal.message = message; + g_ui_ctx->modal.callbacks.close = NULL; + g_ui_ctx->modal.options = options; u32 type = options & UI_MODAL_TYPE_BITS; if( type == UI_MODAL_OK ) vg_info( message ); @@ -2215,9 +1878,11 @@ void ui_start_modal( const char *message, u32 options ) /* * Handles binds */ +#ifdef VG_ANDROID +#else void ui_proc_key( SDL_Keysym ev ) { - if( vg_ui.focused_control_type != k_ui_control_textbox ){ + if( g_ui_ctx->focused_control_type != k_ui_control_textbox ){ return; } @@ -2279,16 +1944,19 @@ void ui_proc_key( SDL_Keysym ev ) } } } +#endif /* * Callback for text entry mode */ void ui_proc_utf8( const char *text ) { - if( vg_ui.focused_control_type == k_ui_control_textbox ){ + if( g_ui_ctx->focused_control_type == k_ui_control_textbox ) + { const char *ptr = text; - while( *ptr ){ + while( *ptr ) + { if( *ptr != '`' ) _ui_textbox_put_char( *ptr ); ptr ++; } @@ -2304,9 +1972,9 @@ void ui_proc_utf8( const char *text ) void ui_dev_colourview(void) { - ui_rect window = {vg.window_x-256,0,256,vg.window_y}, swatch; + ui_rect window = {g_ui_ctx->area[0]-256,0,256,g_ui_ctx->area[1]}, swatch; - const char *names[vg_list_size(vg_ui.scheme)] = { + const char *names[vg_list_size(g_ui_ctx->scheme)] = { [k_ui_bg] = "k_ui_bg", "k_ui_bg+1", "k_ui_bg+2", "k_ui_bg+3", "k_ui_bg+4", "k_ui_bg+5", "k_ui_bg+6", "k_ui_bg+7", @@ -2321,7 +1989,7 @@ void ui_dev_colourview(void) ui_rect col[2]; ui_split_ratio( window, k_ui_axis_v, 0.5f, 0, col[0], col[1] ); - for( int i=0; ischeme); i++ ){ int which = (i/8)%2; ui_split( col[which], k_ui_axis_h, 24, 0, swatch, col[which] ); diff --git a/vg_imgui.h b/vg_ui/imgui.h similarity index 77% rename from vg_imgui.h rename to vg_ui/imgui.h index 8ec0330..4da6162 100644 --- a/vg_imgui.h +++ b/vg_ui/imgui.h @@ -1,31 +1,17 @@ -/* Copyright (C) 2021-2024 Harry Godden (hgn) - All Rights Reserved */ - -/* - * Principles: - * - * 1. layout is defined by subdividing - * 2. a parent node should never be resized by the content after creation - * 3. when the ui is in an interactive state, no controls should ever move - * 4. controls directly reference a memory location and use that as their - * unique id - * 5. a maximum of ONE control per memory location can be drawn at any given - * point. - */ - #pragma once -#include "vg_engine.h" -#include "vg_tex.h" -#include "vg_shader.h" +#include "vg_platform.h" +#include "vg_m.h" #include "vg_font.h" - -extern vg_font_sheet vg_default_font_sheet; -extern vg_font_face vgf_default_small, vgf_default_large, vgf_default_title; +#include "vg_log.h" +#include typedef i16 ui_px; typedef ui_px ui_rect[4]; typedef ui_px ui_point[2]; typedef struct ui_vert ui_vert; +typedef struct ui_context ui_context; +typedef struct ui_batch ui_batch; enum ui_axis { k_ui_axis_h = 0x0u, @@ -51,16 +37,16 @@ enum ui_align k_ui_align_bottom_center = 0x0200| 0x02, }; -#pragma pack(push,1) -struct ui_vert +enum ui_shader { - ui_px co[2]; - u16 uv[2]; - u32 colour; + k_ui_shader_colour, + k_ui_shader_image, + k_ui_shader_hsv, }; -#pragma pack(pop) -enum ui_scheme_colour{ +typedef u32 ui_scheme[8*4]; +enum ui_scheme_colour +{ k_ui_bg = 0, k_ui_fg = 8, k_ui_hue = 16, @@ -75,18 +61,6 @@ enum ui_scheme_colour{ k_ui_brighter = 8 }; -enum ui_shader { - k_ui_shader_colour, - k_ui_shader_image, - k_ui_shader_hsv, -}; - -extern ui_px k_ui_widget_height, - k_ui_scale, - k_ui_padding; - -typedef u32 ui_scheme[8*4]; - #define UI_RGB( STDHEX ) 0xff000000 |\ ((STDHEX&0x000000ff)<<16) |\ ((STDHEX&0x0000ff00) ) |\ @@ -102,16 +76,40 @@ typedef u32 ui_scheme[8*4]; #define UI_MODAL_WARN 0x3 #define UI_MODAL_TYPE_BITS 0x3 +#if 0 #define UI_MOUSE_LEFT (SDL_BUTTON(SDL_BUTTON_LEFT)) #define UI_MOUSE_RIGHT (SDL_BUTTON(SDL_BUTTON_RIGHT)) #define UI_MOUSE_MIDDLE (SDL_BUTTON(SDL_BUTTON_MIDDLE)) +#else +#define UI_MOUSE_LEFT 1 +#define UI_MOUSE_RIGHT 2 +#define UI_MOUSE_MIDDLE 4 +#endif #define UI_TOP 0x1 #define UI_LEFT 0x2 #define UI_BOTTOM 0x4 #define UI_RIGHT 0x8 -struct vg_imgui +#pragma pack(push,1) +struct ui_vert +{ + ui_px co[2]; + u16 uv[2]; + u32 colour; +}; +#pragma pack(pop) + +enum ui_button_state +{ + k_ui_button_none = 0x0, + k_ui_button_click = 0x1, + k_ui_button_holding_inside = 0x2, + k_ui_button_holding_outside = 0x4, + k_ui_button_hover = 0x8 +}; + +struct ui_context { struct ui_vert *vertex_buffer; u16 *indice_buffer; @@ -119,11 +117,14 @@ struct vg_imgui cur_vert, cur_indice, vert_start, indice_start; - union { - void *focused_control_id; /* uses the memory location of various locking - controls as an id */ - char *textbuf; - i32 *ptr_enum; + ui_px area[2]; + + union + { + void *focused_control_id; /* uses the memory location of various locking + controls as an id */ + char *textbuf; + i32 *ptr_enum; }; u32 focused_control_hit; @@ -135,13 +136,16 @@ struct vg_imgui } focused_control_type; - union{ /* controls that can be focused */ - struct ui_textbuf{ + union /* controls data that can be focused */ + { + struct ui_textbuf + { int cursor_user, cursor_pos; u32 len; u32 flags; - struct ui_textbox_callbacks{ + struct ui_textbox_callbacks + { void (*enter)( char *, u32 ), (*up)( char *, u32 ), (*down)( char *, u32 ), @@ -152,8 +156,10 @@ struct vg_imgui } textbox; - struct ui_enum{ - struct ui_enum_opt{ + struct ui_enum + { + struct ui_enum_opt + { i32 value; const char *alias; } @@ -164,35 +170,35 @@ struct vg_imgui _enum; }; - struct ui_modal{ + struct ui_modal + { const char *message; u32 options; - struct ui_modal_callbacks{ + struct ui_modal_callbacks + { void (*close)(u32); } callbacks; } modal; - - GLuint tex_glyphs, vao, vbo, ebo, tex_bg; - v2f bg_inverse_ratio; ui_px mouse[2], mouse_delta[2], mouse_click[2]; u32 mouse_state[2]; u32 ignore_input_frames; + bool mouse_pos_overriden; int wants_mouse; ui_rect click_fader, click_fader_end; float click_fade_opacity; - f32 frosting; ui_scheme scheme; const vg_font_face *font; v2f inverse_font_sheet; - enum ui_cursor{ + enum ui_cursor + { k_ui_cursor_default, k_ui_cursor_ibeam, k_ui_cursor_hand, @@ -200,70 +206,75 @@ struct vg_imgui } cursor; - SDL_Cursor *cursor_map[ k_ui_cursor_max ]; + ui_px widget_height, scale, padding; + f32 hue; + + /* at some point this should be implementation specific? */ v4f colour; + f32 frosting; + v2f bg_inverse_ratio; + GLuint tex_bg; +}; - f32 hue; /* lol this sucks */ -} -extern vg_ui; +struct ui_batch +{ + enum ui_shader shader; -enum ui_button_state { - k_ui_button_none = 0x0, - k_ui_button_click = 0x1, - k_ui_button_holding_inside = 0x2, - k_ui_button_holding_outside = 0x4, - k_ui_button_hover = 0x8 -}; + ui_vert *vert_buf; + u32 vert_count, vert_offset; -/* TODO: docu.. */ + u16 *indice_buf; + u32 indice_count, indice_offset; +}; -void vg_ui_init(void); -void rect_copy( ui_rect a, ui_rect b ); -void ui_flush( enum ui_shader shader, f32 w, f32 h ); -struct ui_vert *ui_fill_rect( ui_rect rect, u32 colour, ui_px uv[4] ); -struct ui_vert *ui_fill( ui_rect rect, u32 colour ); +void ui_bind_context( ui_context *context ); +void ui_init( ui_vert *verts_buf, u32 verts_max, + u16 *indices_buf, u32 indices_max ); +ui_vert *ui_fill_rect( ui_rect rect, u32 colour, ui_px uv[4] ); +ui_vert *ui_fill( ui_rect rect, u32 colour ); void ui_outline( ui_rect rect, ui_px thickness, u32 colour, u32 mask ); +void rect_copy( ui_rect a, ui_rect b ); void ui_split( ui_rect rect, enum ui_axis other, ui_px width, ui_px gap, ui_rect l, ui_rect r ); -void ui_rect_center( ui_rect parent, ui_rect rect ); -void ui_fit_item( ui_rect rect, ui_px size[2], ui_rect d ); void ui_split_ratio( ui_rect rect, enum ui_axis dir, float ratio, ui_px gap, ui_rect l, ui_rect r ); void ui_rect_pad( ui_rect rect, ui_px pad[2] ); -ui_px ui_text_line_width( const char *str ); -ui_px ui_text_string_height( const char *str ); -ui_px ui_text_aligned_x( const char *str, ui_rect rect, ui_px scale, - enum ui_align align ); int ui_clip( ui_rect parent, ui_rect child, ui_rect clipped ); int ui_inside_rect( ui_rect rect, ui_px co[2] ); int ui_click_down( u32 mask ); int ui_clicking( u32 mask ); int ui_click_up( u32 mask ); -void ui_set_mouse_pos( ui_px x, ui_px y ); -void ui_prerender(void); +void ui_prerender( ui_px dims[2], ui_px mouse[2], i32 mouse_state ); +void ui_ignore_input_frames( u32 frames ); +void ui_capture_mouse( bool on ); +void ui_flush( enum ui_shader shader ); +void ui_rect_center( ui_rect parent, ui_rect rect ); +void ui_fit_item( ui_rect rect, ui_px size[2], ui_rect d ); +ui_px ui_text_line_width( const char *str ); +ui_px ui_text_string_height( const char *str ); +ui_px ui_text_aligned_x( const char *str, ui_rect rect, ui_px scale, + enum ui_align align ); u32 ui_colour( enum ui_scheme_colour id ); - -/* get an appropriately contrasting colour given the base */ u32 ui_colourcont( enum ui_scheme_colour id ); - void ui_hex_to_norm( u32 hex, v4f norm ); u32 v4f_u32_colour( v4f colour ); - u32 ui_opacity( u32 colour, f32 opacity ); -void ui_font_face( vg_font_face *ff ); u32 ui_ntext( ui_rect rect, const char *str, u32 len, ui_px scale, enum ui_align align, u32 colour ); u32 ui_text( ui_rect rect, const char *str, ui_px scale, enum ui_align align, u32 colour ); +void ui_font_face( vg_font_face *ff ); void ui_panel( ui_rect in_rect, ui_rect out_panel ); void ui_label( ui_rect rect, const char *text, ui_px size, ui_px gap, ui_rect r ); +ui_px ui_standard_widget_height( ui_px count ); void ui_standard_widget( ui_rect inout_panel, ui_rect out_rect, ui_px count ); void ui_info( ui_rect inout_panel, const char *text ); void ui_image( ui_rect rect, GLuint image ); void ui_defocus_all(void); - enum ui_button_state ui_button_base( ui_rect rect ); + +/* TODO: split this out into a formatless button and one that auto fills */ enum ui_button_state ui_colourbutton( ui_rect rect, enum ui_scheme_colour colour, enum ui_scheme_colour hover_colour, @@ -274,36 +285,28 @@ enum ui_button_state ui_colourbutton_text( enum ui_button_state ui_button_text( ui_rect rect, const char *string, ui_px scale ); enum ui_button_state ui_button( ui_rect inout_panel, const char *string ); - -void ui_postrender(void); - +void ui_postrender( f32 delta_time ); enum ui_button_state ui_checkbox_base( ui_rect box, i32 *data ); int ui_checkbox( ui_rect inout_panel, const char *str_label, i32 *data ); void ui_enum( ui_rect inout_panel, const char *str_label, struct ui_enum_opt *options, u32 len, i32 *value ); -enum ui_button_state ui_slider_base( ui_rect box, f32 min, f32 max, f32 *value, - f32 *out_t ); + +enum ui_button_state ui_slider_base( + ui_rect box, f32 min, f32 max, f32 *value, f32 *out_t ); +void ui_slider_text( ui_rect box, const char *format, f32 value ); +bool ui_slider_standard( ui_rect box, f32 min, f32 max, f32 *value, + const char *format ); bool ui_slider( ui_rect inout_panel, const char *str_label, f32 min, f32 max, f32 *value ); -void ui_slider_text( ui_rect box, const char *format, f32 value ); void ui_colourpicker( ui_rect inout_panel, const char *str_label, v4f value ); -int ui_textbox( ui_rect inout_panel, const char *label, - char *buf, u32 len, u32 lines, u32 flags, - struct ui_textbox_callbacks *callbacks ); -void ui_tabs( ui_rect inout_panel, ui_rect out_content_panel, - const char **titles, u32 count, i32 *page ); -void ui_start_modal( const char *message, u32 options ); -void ui_proc_key( SDL_Keysym ev ); -void ui_proc_utf8( const char *text ); -void ui_dev_colourview(void); - void _ui_textbox_move_cursor( int *cursor0, int *cursor1, int dir, int snap_together ); int _ui_textbox_delete_char( int direction ); +void ui_start_modal( const char *message, u32 options ); void _ui_textbox_put_char( char c ); -void _ui_textbox_up(void); -void _ui_textbox_left(void); void _ui_textbox_left_select(void); +void _ui_textbox_left(void); +void _ui_textbox_up(void); void _ui_textbox_down(void); void _ui_textbox_right_select(void); void _ui_textbox_right(void); @@ -316,3 +319,26 @@ void _ui_textbox_end(void); void _ui_textbox_select_all(void); void _ui_textbox_cut(void); void _ui_textbox_enter(void); +int ui_textbox( ui_rect inout_panel, const char *label, + char *buf, u32 len, u32 lines, u32 flags, + struct ui_textbox_callbacks *callbacks ); +void ui_tabs( ui_rect inout_panel, ui_rect out_content_panel, + const char **titles, u32 count, i32 *page ); +void ui_start_modal( const char *message, u32 options ); +#ifdef VG_ANDROID +#else +void ui_proc_key( SDL_Keysym ev ); +#endif +void ui_proc_utf8( const char *text ); +void ui_dev_colourview(void); +void ui_post_update(void); +void ui_set_mouse_pos( ui_px x, ui_px y ); +void vg_ui_init(void); + +const vg_font_face *ui_current_font(void); +ui_context *ui_current_context(void); + +extern vg_font_sheet vg_default_font_sheet; +extern vg_font_face vgf_default_small, vgf_default_large, vgf_default_title; +void ui_set_screen( i32 width, i32 height ); +extern ui_context *g_ui_ctx; diff --git a/vg_ui/imgui_impl_opengl.c b/vg_ui/imgui_impl_opengl.c new file mode 100644 index 0000000..5e43e14 --- /dev/null +++ b/vg_ui/imgui_impl_opengl.c @@ -0,0 +1,479 @@ +#include "vg_opengl.h" +#include "vg_shader.h" + +struct +{ + GLuint vao, vbo, ebo; + m3x3f pv; + ui_context ctx; + GLuint tex_glyphs; + +#ifdef VG_ANDROID +#else + SDL_Cursor *cursor_map[ k_ui_cursor_max ]; +#endif +} +static opengl_ui = +{ + .ctx = + { + .scheme = + { + [ k_ui_bg+0 ] = UI_RGB( 0x1d2021 ), + [ k_ui_bg+1 ] = UI_RGB( 0x282828 ), + [ k_ui_bg+2 ] = UI_RGB( 0x3c3836 ), + [ k_ui_bg+3 ] = UI_RGB( 0x504945 ), + [ k_ui_bg+4 ] = UI_RGB( 0x665c54 ), + [ k_ui_bg+5 ] = UI_RGB( 0x7c6f64 ), + [ k_ui_bg+6 ] = UI_RGB( 0x928374 ), + [ k_ui_bg+7 ] = UI_RGB( 0xa89984 ), + + [ k_ui_fg+0 ] = UI_RGB( 0xebdbb2 ), + [ k_ui_fg+1 ] = UI_RGB( 0xfbf1c7 ), + [ k_ui_fg+2 ] = UI_RGB( 0xd5c4a1 ), + [ k_ui_fg+3 ] = UI_RGB( 0xbdae93 ), + [ k_ui_fg+4 ] = UI_RGB( 0xa89984 ), + [ k_ui_fg+5 ] = UI_RGB( 0x000000 ), + [ k_ui_fg+6 ] = UI_RGB( 0x000000 ), + [ k_ui_fg+7 ] = UI_RGB( 0x000000 ), + + [ k_ui_red ] = UI_RGB( 0xcc241d ), + [ k_ui_orange ] = UI_RGB( 0xd65d0e ), + [ k_ui_yellow ] = UI_RGB( 0xd79921 ), + [ k_ui_green ] = UI_RGB( 0x98971a ), + [ k_ui_aqua ] = UI_RGB( 0x689d6a ), + [ k_ui_blue ] = UI_RGB( 0x458588 ), + [ k_ui_purple ] = UI_RGB( 0xb16286 ), + [ k_ui_gray ] = UI_RGB( 0x928374 ), + [ k_ui_red + k_ui_brighter ] = UI_RGB( 0xfb4934 ), + [ k_ui_orange + k_ui_brighter ] = UI_RGB( 0xfe8019 ), + [ k_ui_yellow + k_ui_brighter ] = UI_RGB( 0xfabd2f ), + [ k_ui_green + k_ui_brighter ] = UI_RGB( 0xb8bb26 ), + [ k_ui_aqua + k_ui_brighter ] = UI_RGB( 0x8ec07c ), + [ k_ui_blue + k_ui_brighter ] = UI_RGB( 0x83a598 ), + [ k_ui_purple + k_ui_brighter ] = UI_RGB( 0xd3869b ), + [ k_ui_gray + k_ui_brighter ] = UI_RGB( 0xa89984 ), + }, + .font = &vgf_default_small, + .colour = {1.0f,1.0f,1.0f,1.0f}, + .bg_inverse_ratio = {1,1}, + .scale = 1, + .padding = 8, + .widget_height = 28 + } +}; + +#if 0 +static vg_shader _shader_ui = +{ + .vs.static_src = + "layout (location=0) in vec2 a_co;" + "layout (location=1) in vec2 a_uv;" + "layout (location=2) in vec4 a_colour;" + "uniform mat3 uPv;" + "uniform vec2 uInverseFontSheet;" + "" + "out vec2 aTexCoords;" + "out vec4 aColour;" + "" + "void main(){" + "gl_Position = vec4( uPv * vec3( a_co, 1.0 ), 1.0 );" + "aTexCoords = a_uv * uInverseFontSheet;" + "aColour = a_colour;" + "}", + .fs.static_src = + "uniform sampler2D uTexGlyphs;" + "uniform lowp vec4 uColour;" + "out lowp vec4 FragColor;" + "" + "in highp vec2 aTexCoords;" + "in lowp vec4 aColour;" + "" + "void main(){" + "lowp vec4 avg = vec4(0.0);" + + "if( aColour.a == 0.0 ){" + "avg = aColour;" + "avg.a = texture( uTexGlyphs, aTexCoords ).r;" + "}" + "else{" + "avg = aColour;" + "}" + + "FragColor = avg * uColour;" + "}" +}; +#else + +static struct vg_shader _shader_ui ={ + .name = "[vg] ui - transparent", + .vs = { + .orig_file = NULL, + .static_src = + "layout (location=0) in vec2 a_co;" + "layout (location=1) in vec2 a_uv;" + "layout (location=2) in vec4 a_colour;" + "uniform mat3 uPv;" + "uniform vec2 uBGInverseRatio;" + "uniform vec2 uInverseFontSheet;" + "" + "out vec4 aTexCoords;" + "out vec4 aColour;" + "" + "void main(){" + "vec4 proj_pos = vec4( uPv * vec3( a_co, 1.0 ), 1.0 );" + "gl_Position = proj_pos;" + "aTexCoords = vec4( a_uv * uInverseFontSheet, " + " (proj_pos.xy*0.5+0.5) * uBGInverseRatio );" + "aColour = a_colour;" + "}", + }, + .fs = { + .orig_file = NULL, + .static_src = + "uniform sampler2D uTexGlyphs;" + "uniform sampler2D uTexBG;" + "uniform vec4 uColour;" + "uniform float uSpread;" + "out vec4 FragColor;" + "" + "in vec4 aTexCoords;" + "in vec4 aColour;" + "" + "vec2 rand_hash22( vec2 p ){" + "vec3 p3 = fract(vec3(p.xyx) * 213.8976123);" + "p3 += dot(p3, p3.yzx+19.19);" + "return fract(vec2((p3.x + p3.y)*p3.z, (p3.x+p3.z)*p3.y));" + "}" + "" + "void main(){" + "vec4 diffuse = aColour;" + + "vec4 avg = vec4(0.0);" + + "if( aColour.a == 0.0 ){" + "avg = aColour;" + "avg.a = texture( uTexGlyphs, aTexCoords.xy ).r;" + "}" + "else{" + "if( uSpread > 0.0001 ){" + "for( int i=0; i<4; i ++ ){" + "vec2 spread = rand_hash22(aTexCoords.zw+vec2(float(i)));" + "avg += texture( uTexBG, aTexCoords.zw + (spread-0.5)*uSpread );" + "}" + "avg *= 0.25;" + "avg.a = 1.0;" + "avg.rgb = mix( avg.rgb, aColour.rgb, aColour.a );" + "}" + "else{" + "avg = aColour;" + "}" + "}" + + "FragColor = avg * uColour;" + "}" + } +}; + +static struct vg_shader _shader_ui_image = { + .name = "[vg] ui_image", + .vs = + { + .orig_file = NULL, + .static_src = + "layout (location=0) in vec2 a_co;" + "layout (location=1) in vec2 a_uv;" + "layout (location=2) in vec4 a_colour;" + "uniform mat3 uPv;" + + "out vec2 aTexCoords;" + "out vec4 aColour;" + "out vec2 aWsp;" + + "void main()" + "{" + "gl_Position = vec4( uPv * vec3( a_co, 1.0 ), 1.0 );" + "aTexCoords = a_uv * 0.00390625;" + "aColour = a_colour;" + + "aWsp = a_co;" + "}", + }, + .fs = + { + .orig_file = NULL, + .static_src = + "uniform sampler2D uTexImage;" + "uniform vec4 uColour;" + "out vec4 FragColor;" + + "in vec2 aTexCoords;" + "in vec4 aColour;" + "in vec2 aWsp;" + + "void main()" + "{" + "vec4 colour = texture( uTexImage, aTexCoords );" + "FragColor = colour * uColour;" + "}" + } +}; + +static struct vg_shader _shader_ui_hsv = { + .name = "[vg] ui_hsv", + .vs = { + .orig_file = NULL, + .static_src = + "layout (location=0) in vec2 a_co;" + "layout (location=1) in vec2 a_uv;" + "layout (location=2) in vec4 a_colour;" + "uniform mat3 uPv;" + + "out vec2 aTexCoords;" + "out vec4 aColour;" + "out vec2 aWsp;" + + "void main()" + "{" + "gl_Position = vec4( uPv * vec3( a_co, 1.0 ), 1.0 );" + "aTexCoords = a_uv * 0.00390625;" + "aColour = a_colour;" + + "aWsp = a_co;" + "}", + }, + .fs = + { + .orig_file = NULL, + .static_src = + "uniform float uHue;" + "out vec4 FragColor;" + + "in vec2 aTexCoords;" + "in vec4 aColour;" + "in vec2 aWsp;" + + "void main()" + "{" + "vec3 c = vec3( uHue, aTexCoords );" + "vec4 K = vec4(1.0,2.0/3.0,1.0/3.0,3.0);" + "vec3 p = abs(fract(c.xxx+K.xyz)*6.0 - K.www);" + "vec3 colour = c.z*mix(K.xxx,clamp(p-K.xxx,0.0,1.0),c.y);" + "FragColor = vec4( colour, 1.0 );" + "}" + } +}; +#endif + +void ui_set_screen( i32 width, i32 height ) +{ + m3x3_identity( opengl_ui.pv ); + m3x3_translate( opengl_ui.pv, (v3f){ -1.0f, 1.0f, 0.0f } ); + m3x3_scale( opengl_ui.pv, (v3f){ 1.0f/((f32)width*0.5f), + -1.0f/((f32)height*0.5f), 1.0f } ); +} + +void vg_ui_init(void) +{ + if( !vg_shader_compile( &_shader_ui ) || + !vg_shader_compile( &_shader_ui_hsv ) || + !vg_shader_compile( &_shader_ui_image ) ) + { + vg_fatal_error( "" ); + } + + u32 verts = 30000, indices = 20000; + ui_bind_context( &opengl_ui.ctx ); + ui_init( malloc( sizeof(ui_vert)*verts ), verts, + malloc( sizeof(u16)*indices ), indices ); + + /* Generate the buffer we are gonna be drawing to */ + glGenVertexArrays( 1, &opengl_ui.vao ); + glGenBuffers( 1, &opengl_ui.vbo ); + glGenBuffers( 1, &opengl_ui.ebo ); + + glBindVertexArray( opengl_ui.vao ); + glBindBuffer( GL_ARRAY_BUFFER, opengl_ui.vbo ); + glBufferData( GL_ARRAY_BUFFER, verts*sizeof(ui_vert), NULL, GL_DYNAMIC_DRAW ); + + glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, opengl_ui.ebo ); + glBufferData( GL_ELEMENT_ARRAY_BUFFER, + indices*sizeof(u16), NULL, GL_DYNAMIC_DRAW ); + + /* Set pointers */ + u32 const stride = sizeof(ui_vert); + + /* XY */ + glVertexAttribPointer( 0, 2, GL_SHORT, GL_FALSE, stride, + (void *)offsetof( ui_vert, co ) ); + glEnableVertexAttribArray( 0 ); + + /* UV */ + glVertexAttribPointer( 1, 2, GL_UNSIGNED_SHORT, GL_FALSE, stride, + (void *)offsetof( ui_vert, uv ) ); + glEnableVertexAttribArray( 1 ); + + /* COLOUR */ + glVertexAttribPointer( 2, 4, GL_UNSIGNED_BYTE, GL_TRUE, stride, + (void *)offsetof( ui_vert, colour ) ); + glEnableVertexAttribArray( 2 ); + +#ifdef VG_ANDROID +#else + opengl_ui.cursor_map[ k_ui_cursor_default ] = + SDL_CreateSystemCursor( SDL_SYSTEM_CURSOR_ARROW ); + opengl_ui.cursor_map[ k_ui_cursor_hand ] = + SDL_CreateSystemCursor( SDL_SYSTEM_CURSOR_HAND ); + opengl_ui.cursor_map[ k_ui_cursor_ibeam ] = + SDL_CreateSystemCursor( SDL_SYSTEM_CURSOR_IBEAM ); +#endif + + /* font + * ----------------------------------------------------- + */ + + /* Load default font */ + + vg_font_sheet *sheet = &vg_default_font_sheet; + u32 pixels = 0, + total = sheet->w*sheet->h, + data = 0; + + vg_linear_clear( vg_mem.scratch ); + u8 *image = vg_linear_alloc( vg_mem.scratch, total ); + + while( pixels < total ) + { + for( int b = 31; b >= 0; b-- ) + { + image[ pixels ++ ] = (sheet->bitmap[data] & (0x1u << b))? 0xffu: 0x00u; + + if( pixels >= total ) + { + total = 0; + break; + } + } + data++; + } + + opengl_ui.ctx.inverse_font_sheet[0] = 1.0/(f64)sheet->w; + opengl_ui.ctx.inverse_font_sheet[1] = 1.0/(f64)sheet->h; + + glGenTextures( 1, &opengl_ui.tex_glyphs ); + glBindTexture( GL_TEXTURE_2D, opengl_ui.tex_glyphs ); + glTexImage2D( GL_TEXTURE_2D, 0, GL_R8, sheet->w, sheet->h, 0, + GL_RED, GL_UNSIGNED_BYTE, image ); + + VG_CHECK_GL_ERR(); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); +} + +void ui_free(void) +{ + vg_free_shader( &_shader_ui ); + ui_bind_context( NULL ); + free( opengl_ui.ctx.indice_buffer ); + free( opengl_ui.ctx.vertex_buffer ); +} + +void ui_impl_render_batch( ui_batch *batch ) +{ + glBindVertexArray( opengl_ui.vao ); + glBindBuffer( GL_ARRAY_BUFFER, opengl_ui.vbo ); + glBufferSubData( GL_ARRAY_BUFFER, + batch->vert_offset, + batch->vert_count*sizeof(ui_vert), + batch->vert_buf ); + + glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, opengl_ui.ebo ); + glBufferSubData( GL_ELEMENT_ARRAY_BUFFER, + batch->indice_offset, + batch->indice_count*sizeof(u16), + batch->indice_buf ); + + glDisable( GL_DEPTH_TEST ); + glDisable( GL_CULL_FACE ); + glEnable( GL_BLEND ); + glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); + glBlendEquation( GL_FUNC_ADD ); + + if( batch->shader == k_ui_shader_colour ) + { + glUseProgram( _shader_ui.id ); + glUniformMatrix3fv( glGetUniformLocation( _shader_ui.id, "uPv" ), 1, + GL_FALSE, (float *)opengl_ui.pv ); + glUniform4fv( glGetUniformLocation( _shader_ui.id, "uColour" ), 1, + g_ui_ctx->colour ); + + glActiveTexture( GL_TEXTURE0 ); + glBindTexture( GL_TEXTURE_2D, opengl_ui.tex_glyphs ); + glUniform1i( glGetUniformLocation( _shader_ui.id, "uTexGlyphs" ), 0 ); + + glActiveTexture( GL_TEXTURE1 ); + glBindTexture( GL_TEXTURE_2D, g_ui_ctx->tex_bg ); + glUniform1i( glGetUniformLocation( _shader_ui.id, "uTexBG" ), 1 ); + glUniform1f( glGetUniformLocation( _shader_ui.id, "uSpread" ), + g_ui_ctx->frosting ); + glUniform2fv( glGetUniformLocation( _shader_ui.id, "uBGInverseRatio" ), + 1, g_ui_ctx->bg_inverse_ratio ); + glUniform2fv( glGetUniformLocation( _shader_ui.id, "uInverseFontSheet" ), + 1, g_ui_ctx->inverse_font_sheet ); + } + else if( batch->shader == k_ui_shader_image ) + { + glUseProgram( _shader_ui_image.id ); + glUniformMatrix3fv( glGetUniformLocation( _shader_ui_image.id, "uPv" ), 1, + GL_FALSE, (float *)opengl_ui.pv ); + glUniform1i( glGetUniformLocation(_shader_ui_image.id,"uTexImage"), 0 ); + glUniform4fv( glGetUniformLocation( _shader_ui_image.id, "uColour" ), 1, + g_ui_ctx->colour ); + } + else if( batch->shader == k_ui_shader_hsv ) + { + glUseProgram( _shader_ui_hsv.id ); + glUniformMatrix3fv( glGetUniformLocation( _shader_ui_hsv.id, "uPv" ), 1, + GL_FALSE, (float *)opengl_ui.pv ); + glUniform1f( glGetUniformLocation(_shader_ui_hsv.id,"uHue"), + g_ui_ctx->hue ); + } + else + vg_fatal_error( "Invalid UI shader (%d)\n", batch->shader ); + + glDrawElements( GL_TRIANGLES, batch->indice_count, GL_UNSIGNED_SHORT, + (void *)((size_t)batch->indice_offset) ); +} + +void ui_post_update(void) +{ +#ifdef VG_ANDROID +#else + if( g_ui_ctx->wants_mouse ) + { + SDL_SetWindowGrab( vg.window, SDL_FALSE ); + SDL_SetRelativeMouseMode( SDL_FALSE ); + } + else + { + SDL_SetWindowGrab( vg.window, SDL_TRUE ); + SDL_SetRelativeMouseMode( SDL_TRUE ); + } + + SDL_SetCursor( opengl_ui.cursor_map[ g_ui_ctx->cursor ] ); + SDL_ShowCursor(1); +#endif +} + +void ui_set_mouse_pos( ui_px x, ui_px y ) +{ +#ifdef VG_ANDROID +#else + SDL_WarpMouseInWindow( vg.window, x, y ); + g_ui_ctx->mouse[0] = x; + g_ui_ctx->mouse[1] = y; + g_ui_ctx->mouse_pos_overriden = 1; +#endif +} -- 2.25.1