diff --git a/build.sh b/build.sh index a13d04e..3e75b3a 100755 --- a/build.sh +++ b/build.sh @@ -24,7 +24,7 @@ if [ $OS = "Darwin" ] ; then CXX=clang++ DYLIB_SUFFIX='dylib' SYS_LIBS='' - FLAGS="-mmacos-version-min=10.15.4 -DMG_IMPLEMENTS_BACKEND_METAL -maes" + FLAGS="-mmacos-version-min=10.15.4 -DMG_IMPLEMENTS_BACKEND_METAL -DMG_IMPLEMENTS_BACKEND_GLES -maes" CFLAGS="-std=c11" elif [ $OS = "Linux" ] ; then @@ -40,8 +40,9 @@ fi #-------------------------------------------------------------- BINDIR="./bin" SRCDIR="./src" +EXTDIR="./ext" RESDIR="./resources" -INCLUDES="-I$SRCDIR -I$SRCDIR/util -I$SRCDIR/platform" +INCLUDES="-I$SRCDIR -I$SRCDIR/util -I$SRCDIR/platform -I$EXTDIR/angle_headers" #-------------------------------------------------------------- # Build @@ -60,7 +61,7 @@ if [ $target = 'lib' ] ; then # compile milepost. We use one compilation unit for all C code, and one compilation # unit for all ObjectiveC code $CC $DEBUG_FLAGS -c -o $BINDIR/milepost_c.o $CFLAGS $FLAGS $INCLUDES $SRCDIR/milepost.c - $CC $DEBUG_FLAGS -c -o $BINDIR/milepost_objc.o $FLAGS $INCLUDES $SRCDIR/milepost.mm + $CC $DEBUG_FLAGS -c -o $BINDIR/milepost_objc.o $FLAGS $INCLUDES $SRCDIR/milepost.m # build the static library libtool -static -o $BINDIR/libmilepost.a $BINDIR/milepost_c.o $BINDIR/milepost_objc.o diff --git a/src/graphics.c b/src/graphics.c index edb8273..248ce7c 100644 --- a/src/graphics.c +++ b/src/graphics.c @@ -219,26 +219,43 @@ typedef struct mg_font_info // internal handle system //--------------------------------------------------------------- -const u32 MG_MAX_SURFACES = 256, +const u32 MG_MAX_RESOURCE_SLOTS = 256, MG_MAX_CONTEXTS = 256, MG_FONT_MAX_COUNT = 256; -typedef struct mg_surface_slot +typedef struct mg_resource_slot { list_elt freeListElt; u32 generation; - mg_surface_info* surface; + union + { + mg_surface_info* surface; + mg_surface_server_info* server; + mg_surface_client_info* client; + }; -} mg_surface_slot; +} mg_resource_slot; + +typedef struct mg_resource_pool +{ + mg_resource_slot slots[MG_MAX_RESOURCE_SLOTS]; + list_info freeList; + u32 nextIndex; + +} mg_resource_pool; typedef struct mg_info { bool init; - list_info surfaceFreeList; + + mg_resource_pool surfaces; + mg_resource_pool servers; + mg_resource_pool clients; + list_info contextFreeList; list_info fontFreeList; u32 fontsNextIndex; - mg_surface_slot surfaceSlots[MG_MAX_SURFACES]; + mg_canvas_data contexts[MG_MAX_CONTEXTS]; mg_font_info fonts[MG_FONT_MAX_COUNT]; @@ -246,16 +263,14 @@ typedef struct mg_info static mg_info __mgInfo = {0}; + void mg_init() { if(!__mgInfo.init) { - ListInit(&__mgInfo.surfaceFreeList); - for(int i=0; ifreeList, mg_resource_slot, freeListElt); + if(!slot && pool->nextIndex < MG_MAX_RESOURCE_SLOTS) + { + slot = &pool->slots[pool->nextIndex]; + slot->generation = 1; + pool->nextIndex++; + } + return(slot); } -void mg_surface_slot_recycle(mg_surface_slot* slot) +void mg_resource_slot_recycle(mg_resource_pool* pool, mg_resource_slot* slot) { + DEBUG_ASSERT(slot >= pool->slots && slot < pool->slots + MG_MAX_RESOURCE_SLOTS); #ifdef DEBUG if(slot->generation == UINT32_MAX) { @@ -282,18 +303,18 @@ void mg_surface_slot_recycle(mg_surface_slot* slot) } #endif slot->generation++; - ListPush(&__mgInfo.surfaceFreeList, &slot->freeListElt); + ListPush(&pool->freeList, &slot->freeListElt); } -mg_surface_slot* mg_surface_slot_from_handle(mg_surface surface) +mg_resource_slot* mg_resource_slot_from_handle(mg_resource_pool* pool, u64 h) { - u32 index = surface.h>>32; - u32 generation = surface.h & 0xffffffff; - if(index >= MG_MAX_SURFACES) + u32 index = h>>32; + u32 generation = h & 0xffffffff; + if(index >= MG_MAX_RESOURCE_SLOTS) { return(0); } - mg_surface_slot* slot = &__mgInfo.surfaceSlots[index]; + mg_resource_slot* slot = &pool->slots[index]; if(slot->generation != generation) { return(0); @@ -304,9 +325,42 @@ mg_surface_slot* mg_surface_slot_from_handle(mg_surface surface) } } +u64 mg_resource_handle_from_slot(mg_resource_pool* pool, mg_resource_slot* slot) +{ + DEBUG_ASSERT( (slot - pool->slots) >= 0 + && (slot - pool->slots) < MG_MAX_RESOURCE_SLOTS); + + u64 h = ((u64)(slot - pool->slots))<<32 + |((u64)(slot->generation)); + return(h); +} + +//------------------------------------------------------------------------ +// surface handles +//------------------------------------------------------------------------ + +mg_surface mg_surface_nil() +{ + return((mg_surface){.h = 0}); +} + +mg_surface mg_surface_alloc_handle(mg_surface_info* surface) +{ + mg_resource_slot* slot = mg_resource_slot_alloc(&__mgInfo.surfaces); + if(!slot) + { + LOG_ERROR("no more surface slots\n"); + return(mg_surface_nil()); + } + slot->surface = surface; + u64 h = mg_resource_handle_from_slot(&__mgInfo.surfaces, slot); + mg_surface handle = {h}; + return(handle); +} + mg_surface_info* mg_surface_ptr_from_handle(mg_surface surface) { - mg_surface_slot* slot = mg_surface_slot_from_handle(surface); + mg_resource_slot* slot = mg_resource_slot_from_handle(&__mgInfo.surfaces, surface.h); if(slot) { return(slot->surface); @@ -317,29 +371,80 @@ mg_surface_info* mg_surface_ptr_from_handle(mg_surface surface) } } -mg_surface mg_surface_handle_from_slot(mg_surface_slot* slot) -{ - DEBUG_ASSERT( (slot - __mgInfo.surfaceSlots) >= 0 - && (slot - __mgInfo.surfaceSlots) < MG_MAX_SURFACES); +//------------------------------------------------------------------------ +// surface server handles +//------------------------------------------------------------------------ - u64 h = ((u64)(slot - __mgInfo.surfaceSlots))<<32 - |((u64)(slot->generation)); - return((mg_surface){.h = h}); +mg_surface_server mg_surface_server_nil() +{ + return((mg_surface_server){.h = 0}); } -mg_surface mg_surface_alloc_handle(mg_surface_info* surface) +mg_surface_server mg_surface_server_alloc_handle(mg_surface_server_info* server) { - mg_surface_slot* slot = mg_surface_slot_alloc(); - slot->surface = surface; - mg_surface handle = mg_surface_handle_from_slot(slot); + mg_resource_slot* slot = mg_resource_slot_alloc(&__mgInfo.servers); + if(!slot) + { + LOG_ERROR("no more server slots\n"); + return(mg_surface_server_nil()); + } + slot->server = server; + u64 h = mg_resource_handle_from_slot(&__mgInfo.servers, slot); + mg_surface_server handle = {h}; return(handle); } -mg_surface mg_surface_nil() +mg_surface_server_info* mg_surface_server_ptr_from_handle(mg_surface_server server) { - return((mg_surface){.h = 0}); + mg_resource_slot* slot = mg_resource_slot_from_handle(&__mgInfo.servers, server.h); + if(slot) + { + return(slot->server); + } + else + { + return(0); + } } +//------------------------------------------------------------------------ +// surface client handles +//------------------------------------------------------------------------ +mg_surface_client mg_surface_client_nil() +{ + return((mg_surface_client){.h = 0}); +} + +mg_surface_client mg_surface_client_alloc_handle(mg_surface_client_info* client) +{ + mg_resource_slot* slot = mg_resource_slot_alloc(&__mgInfo.clients); + if(!slot) + { + LOG_ERROR("no more client slots\n"); + return(mg_surface_client_nil()); + } + slot->client = client; + u64 h = mg_resource_handle_from_slot(&__mgInfo.clients, slot); + mg_surface_client handle = {h}; + return(handle); +} + +mg_surface_client_info* mg_surface_client_ptr_from_handle(mg_surface_client client) +{ + mg_resource_slot* slot = mg_resource_slot_from_handle(&__mgInfo.clients, client.h); + if(slot) + { + return(slot->client); + } + else + { + return(0); + } +} + +//------------------------------------------------------------------------ +// canvas +//------------------------------------------------------------------------ mg_canvas_data* mg_canvas_alloc() { return(ListPopEntry(&__mgInfo.contextFreeList, mg_canvas_data, freeListElt)); @@ -416,11 +521,16 @@ mg_image_data* mg_image_ptr_from_handle(mg_canvas_data* context, mg_image handle //--------------------------------------------------------------- #ifdef MG_IMPLEMENTS_BACKEND_METAL -//NOTE: function is defined in metal_backend.mm -mg_surface mg_metal_surface_create_for_window(mp_window window); -mg_surface mg_metal_surface_create_for_view(mp_view view); + //NOTE: function is defined in metal_backend.mm + mg_surface mg_metal_surface_create_for_window(mp_window window); + mg_surface mg_metal_surface_create_for_view(mp_view view); #endif //MG_IMPLEMENTS_BACKEND_METAL +#ifdef MG_IMPLEMENTS_BACKEND_GLES + mg_surface mg_gles_surface_create_offscreen(); + mg_surface_server mg_gles_surface_create_server(mg_surface_info* surface); +#endif //MG_IMPLEMENTS_BACKEND_GLES + void mg_init(); mg_surface mg_surface_create_for_window(mp_window window, mg_backend_id backend) @@ -469,16 +579,51 @@ mg_surface mg_surface_create_for_view(mp_view view, mg_backend_id backend) return(surface); } +mg_surface mg_surface_create_offscreen(mg_backend_id backend) +{ + DEBUG_ASSERT(__mgInfo.init); + + mg_surface surface = mg_surface_nil(); + + switch(backend) + { + #ifdef MG_IMPLEMENTS_BACKEND_GLES + case MG_BACKEND_GLES: + surface = mg_gles_surface_create_offscreen(); + break; + #endif + + //... + + default: + break; + } + + return(surface); +} + +void* mg_surface_get_os_resource(mg_surface surface) +{ + DEBUG_ASSERT(__mgInfo.init); + + void* res = 0; + mg_resource_slot* slot = mg_resource_slot_from_handle(&__mgInfo.surfaces, surface.h); + if(slot) + { + res = slot->surface->getOSResource(slot->surface); + } + return(res); +} void mg_surface_destroy(mg_surface handle) { DEBUG_ASSERT(__mgInfo.init); - mg_surface_slot* slot = mg_surface_slot_from_handle(handle); + mg_resource_slot* slot = mg_resource_slot_from_handle(&__mgInfo.surfaces, handle.h); if(slot) { slot->surface->destroy(slot->surface); - mg_surface_slot_recycle(slot); + mg_resource_slot_recycle(&__mgInfo.surfaces, slot); } } @@ -515,6 +660,18 @@ void mg_surface_present(mg_surface surface) } } +void mg_surface_set_hidden(mg_surface surface, bool hidden) +{ + DEBUG_ASSERT(__mgInfo.init); + + mg_surface_info* surfaceInfo = mg_surface_ptr_from_handle(surface); + if(surfaceInfo) + { + surfaceInfo->setHidden(surfaceInfo, hidden); + } +} + + vec2 mg_surface_size(mg_surface surface) { DEBUG_ASSERT(__mgInfo.init); @@ -527,6 +684,97 @@ vec2 mg_surface_size(mg_surface surface) return(res); } +//--------------------------------------------------------------- +// graphics surface server +//--------------------------------------------------------------- + +mg_surface_server mg_surface_server_create(mg_surface surface) +{ + mg_surface_server server = mg_surface_server_nil(); + + mg_surface_info* surfaceInfo = mg_surface_ptr_from_handle(surface); + if(surfaceInfo) + { + switch(surfaceInfo->backend) + { + case MG_BACKEND_GLES: + server = mg_gles_surface_create_server(surfaceInfo); + break; + + default: + break; + } + } + return(server); +} + +void mg_surface_server_destroy(mg_surface_server handle) +{ + mg_resource_slot* slot = mg_resource_slot_from_handle(&__mgInfo.servers, handle.h); + if(slot) + { + slot->server->destroy(slot->server); + mg_resource_slot_recycle(&__mgInfo.servers, slot); + } +} + +mg_surface_server_id mg_surface_server_get_id(mg_surface_server server) +{ + mg_surface_server_info* serverInfo = mg_surface_server_ptr_from_handle(server); + if(serverInfo) + { + return(serverInfo->getID(serverInfo)); + } + else + { + return(0); + } +} + +//--------------------------------------------------------------- +// graphics surface client +//--------------------------------------------------------------- + +//TODO: move elsewhere, guard with OS ifdef +mg_surface_client mg_osx_surface_client_create(mg_surface_server_id id); + +mg_surface_client mg_surface_client_create(mg_surface_server_id id) +{ + mg_surface_client client = mg_surface_client_nil(); + + client = mg_osx_surface_client_create(id); + return(client); +} + +void mg_surface_client_destroy(mg_surface_client handle) +{ + mg_resource_slot* slot = mg_resource_slot_from_handle(&__mgInfo.clients, handle.h); + if(slot) + { + slot->client->destroy(slot->client); + mg_resource_slot_recycle(&__mgInfo.clients, slot); + } +} + +void mg_surface_client_attach_to_view(mg_surface_client client, mp_view view) +{ + mg_surface_client_info* clientInfo = mg_surface_client_ptr_from_handle(client); + + if(clientInfo) + { + clientInfo->attachment = view; + clientInfo->attach(clientInfo); + } +} + +void mg_surface_client_detach(mg_surface_client client) +{ + mg_surface_client_info* clientInfo = mg_surface_client_ptr_from_handle(client); + if(clientInfo) + { + clientInfo->detach(clientInfo); + } +} //--------------------------------------------------------------- // graphics stream handles @@ -3606,15 +3854,14 @@ void mg_clear(mg_canvas handle) mg_push_command(context, (mg_primitive){.cmd = MG_CMD_CLEAR, .attributes = context->attributes}); } -void mg_get_current_position(mg_canvas handle, f32* x, f32* y) +vec2 mg_get_position(mg_canvas handle) { mg_canvas_data* context = mg_canvas_ptr_from_handle(handle); if(!context) { - return; + return((vec2){0, 0}); } - *x = context->subPathLastPoint.x; - *y = context->subPathLastPoint.y; + return(context->subPathLastPoint); } void mg_path_push_elements(mg_canvas_data* context, u32 count, mg_path_elt* elements) @@ -4004,6 +4251,11 @@ void mg_arc(mg_canvas handle, f32 x, f32 y, f32 r, f32 arcAngle, f32 startAngle) mg_image mg_image_nil() { return((mg_image){0}); } +bool mg_image_equal(mg_image a, mg_image b) +{ + return(a.h == b.h); +} + mg_image mg_image_handle_from_ptr(mg_canvas_data* canvas, mg_image_data* imageData) { DEBUG_ASSERT( (imageData - canvas->images) >= 0 @@ -4053,9 +4305,9 @@ mg_image mg_image_create_from_rgba8(mg_canvas handle, u32 width, u32 height, u8* && context->atlasPos.y + height < MG_ATLAS_SIZE) { imageData->rect = (mp_rect){context->atlasPos.x, - context->atlasPos.y, - width, - height}; + context->atlasPos.y, + width, + height}; context->atlasPos.x += width; context->atlasLineHeight = maximum(context->atlasLineHeight, height); @@ -4114,6 +4366,21 @@ void mg_image_destroy(mg_canvas handle, mg_image image) //TODO invalidate image handle, maybe free atlas area } +vec2 mg_image_size(mg_canvas handle, mg_image image) +{ + vec2 size = {0}; + mg_canvas_data* context = mg_canvas_ptr_from_handle(handle); + if(context) + { + mg_image_data* imageData = mg_image_ptr_from_handle(context, image); + if(imageData) + { + size = (vec2){imageData->rect.w, imageData->rect.h}; + } + } + return(size); +} + void mg_image_draw(mg_canvas handle, mg_image image, mp_rect rect) { mg_canvas_data* context = mg_canvas_ptr_from_handle(handle); @@ -4122,7 +4389,7 @@ void mg_image_draw(mg_canvas handle, mg_image image, mp_rect rect) return; } mg_primitive primitive = {.cmd = MG_CMD_IMAGE_DRAW, - .rect = (mp_rect){rect.x, rect.y, rect.h, rect.w}, + .rect = (mp_rect){rect.x, rect.y, rect.w, rect.h}, .attributes = context->attributes}; primitive.attributes.image = image; @@ -4137,7 +4404,7 @@ void mg_rounded_image_draw(mg_canvas handle, mg_image image, mp_rect rect, f32 r return; } mg_primitive primitive = {.cmd = MG_CMD_ROUNDED_IMAGE_DRAW, - .roundedRect = {rect.x, rect.y, rect.h, rect.w, roundness}, + .roundedRect = {rect.x, rect.y, rect.w, rect.h, roundness}, .attributes = context->attributes}; primitive.attributes.image = image; diff --git a/src/graphics.h b/src/graphics.h index 6c83a65..7a11f92 100644 --- a/src/graphics.h +++ b/src/graphics.h @@ -23,6 +23,7 @@ typedef struct mg_surface { u64 h; } mg_surface; typedef enum { MG_BACKEND_DUMMY, MG_BACKEND_METAL, + MG_BACKEND_GLES, //... } mg_backend_id; @@ -31,14 +32,35 @@ void mg_init(); mg_surface mg_surface_nil(); mg_surface mg_surface_create_for_window(mp_window window, mg_backend_id backend); mg_surface mg_surface_create_for_view(mp_view view, mg_backend_id backend); +mg_surface mg_surface_create_offscreen(mg_backend_id backend); + void mg_surface_destroy(mg_surface surface); +void* mg_surface_get_os_resource(mg_surface surface); void mg_surface_prepare(mg_surface surface); void mg_surface_present(mg_surface surface); void mg_surface_resize(mg_surface surface, int width, int height); +void mg_surface_set_hidden(mg_surface surface, bool hidden); vec2 mg_surface_size(mg_surface surface); +//------------------------------------------------------------------------------------------ +//NOTE(martin): graphics surface sharing +//------------------------------------------------------------------------------------------ +typedef void* mg_surface_server_id; + +typedef struct mg_surface_server { u64 h; } mg_surface_server; +typedef struct mg_surface_client { u64 h; } mg_surface_client; + +mg_surface_server mg_surface_server_create(mg_surface surface); +void mg_surface_server_destroy(mg_surface_server server); +mg_surface_server_id mg_surface_server_get_id(mg_surface_server server); + +mg_surface_client mg_surface_client_create(mg_surface_server_id id); +void mg_surface_client_destroy(mg_surface_client client); +void mg_surface_client_attach_to_view(mg_surface_client client, mp_view view); +void mg_surface_client_detach(mg_surface_client client); + //------------------------------------------------------------------------------------------ //NOTE(martin): canvas drawing structs //------------------------------------------------------------------------------------------ @@ -181,7 +203,7 @@ bool mg_get_text_flip(mg_canvas context); //------------------------------------------------------------------------------------------ //NOTE(martin): path construction //------------------------------------------------------------------------------------------ -void mg_get_current_position(mg_canvas context, f32* x, f32* y); +vec2 mg_get_position(mg_canvas context); void mg_move_to(mg_canvas context, f32 x, f32 y); void mg_line_to(mg_canvas context, f32 x, f32 y); void mg_quadratic_to(mg_canvas context, f32 x1, f32 y1, f32 x2, f32 y2); @@ -217,12 +239,16 @@ void mg_arc(mg_canvas handle, f32 x, f32 y, f32 r, f32 arcAngle, f32 startAngle) //------------------------------------------------------------------------------------------ typedef struct mg_image { u64 h; } mg_image; +mg_image mg_image_nil(); +bool mg_image_equal(mg_image a, mg_image b); + mg_image mg_image_create_from_rgba8(mg_canvas canvas, u32 width, u32 height, u8* pixels); mg_image mg_image_create_from_data(mg_canvas canvas, str8 data, bool flip); mg_image mg_image_create_from_file(mg_canvas canvas, str8 path, bool flip); void mg_image_drestroy(mg_canvas canvas, mg_image image); +vec2 mg_image_size(mg_canvas canvas, mg_image image); void mg_image_draw(mg_canvas canvas, mg_image image, mp_rect rect); void mg_rounded_image_draw(mg_canvas handle, mg_image image, mp_rect rect, f32 roundness); diff --git a/src/graphics_internal.h b/src/graphics_internal.h index 2f5a215..312f360 100644 --- a/src/graphics_internal.h +++ b/src/graphics_internal.h @@ -15,13 +15,18 @@ extern "C" { #endif +//--------------------------------------------------------------------------------------------- +// Surfaces +//--------------------------------------------------------------------------------------------- typedef struct mg_surface_info mg_surface_info; typedef void (*mg_surface_prepare_proc)(mg_surface_info* surface); typedef void (*mg_surface_present_proc)(mg_surface_info* surface); typedef void (*mg_surface_resize_proc)(mg_surface_info* surface, u32 width, u32 height); +typedef void (*mg_surface_set_hidden_proc)(mg_surface_info* surface, bool hidden); typedef vec2 (*mg_surface_get_size_proc)(mg_surface_info* surface); typedef void (*mg_surface_destroy_proc)(mg_surface_info* surface); +typedef void* (*mg_surface_get_os_resource_proc)(mg_surface_info* surface); typedef struct mg_surface_info { @@ -30,12 +35,54 @@ typedef struct mg_surface_info mg_surface_prepare_proc prepare; mg_surface_present_proc present; mg_surface_resize_proc resize; + mg_surface_set_hidden_proc setHidden; mg_surface_get_size_proc getSize; + mg_surface_get_os_resource_proc getOSResource; } mg_surface_info; mg_surface mg_surface_alloc_handle(mg_surface_info* surface); +//--------------------------------------------------------------------------------------------- +// Surface servers +//--------------------------------------------------------------------------------------------- +typedef struct mg_surface_server_info mg_surface_server_info; + +typedef void (*mg_surface_server_destroy_proc)(mg_surface_server_info* server); +typedef mg_surface_server_id (*mg_surface_server_get_id_proc)(mg_surface_server_info* server); + +typedef struct mg_surface_server_info +{ + mg_surface_server_destroy_proc destroy; + mg_surface_server_get_id_proc getID; + +} mg_surface_server_info; + +mg_surface_server mg_surface_server_alloc_handle(mg_surface_server_info* server); + +//--------------------------------------------------------------------------------------------- +// Surface clients +//--------------------------------------------------------------------------------------------- +typedef struct mg_surface_client_info mg_surface_client_info; + +typedef void (*mg_surface_client_destroy_proc)(mg_surface_client_info* client); +typedef void (*mg_surface_client_attach_proc)(mg_surface_client_info* client); +typedef void (*mg_surface_client_detach_proc)(mg_surface_client_info* client); + +typedef struct mg_surface_client_info +{ + mg_surface_client_destroy_proc destroy; + mg_surface_client_attach_proc attach; + mg_surface_client_detach_proc detach; + + mp_view attachment; + +} mg_surface_client_info; + +mg_surface_client mg_surface_client_alloc_handle(mg_surface_client_info* client); +//--------------------------------------------------------------------------------------------- +// vertex layout +//--------------------------------------------------------------------------------------------- typedef struct mgc_vertex_layout { u32 maxVertexCount; diff --git a/src/metal_painter.mm b/src/metal_painter.mm deleted file mode 100644 index 34af6a5..0000000 --- a/src/metal_painter.mm +++ /dev/null @@ -1,419 +0,0 @@ -/************************************************************//** -* -* @file: graphics.mm -* @author: Martin Fouilleul -* @date: 12/07/2020 -* @revision: -* -*****************************************************************/ -#import -#import -#include - -#include"graphics_internal.h" -#include"macro_helpers.h" -#include"osx_app.h" - -#include"metal_shader.h" - -#define LOG_SUBSYSTEM "Graphics" - -static const int MG_METAL_PAINTER_DEFAULT_BUFFER_LENGTH = 4<<20; - -typedef struct mg_metal_painter -{ - mg_canvas_painter interface; - - mg_metal_surface* surface; - - // permanent metal resources - id tilingPipeline; - id sortingPipeline; - id boxingPipeline; - id computePipeline; - id renderPipeline; - - mp_rect viewPort; - - // textures and buffers - id outTexture; - id atlasTexture; - id vertexBuffer; - id indexBuffer; - id tileCounters; - id tilesArray; - id triangleArray; - id boxArray; - -} mg_metal_painter; - -void mg_metal_painter_draw_buffers(mg_canvas_painter* interface, u32 vertexCount, u32 indexCount, mg_color clearColor) -{ - mg_metal_painter* painter = (mg_metal_painter*)interface; - mg_metal_surface* surface = painter->surface; - - @autoreleasepool - { - if(surface->commandBuffer == nil || surface->commandBuffer == nil) - { - mg_metal_surface_acquire_drawable_and_command_buffer(surface); - } - - ASSERT(indexCount * sizeof(i32) < [painter->indexBuffer length]); - - f32 scale = surface->metalLayer.contentsScale; - vector_uint2 viewportSize = {painter->viewPort.w * scale, painter->viewPort.h * scale}; - - //----------------------------------------------------------- - //NOTE(martin): encode the clear counter - //----------------------------------------------------------- - id blitEncoder = [surface->commandBuffer blitCommandEncoder]; - [blitEncoder fillBuffer: painter->tileCounters range: NSMakeRange(0, RENDERER_MAX_TILES*sizeof(uint)) value: 0]; - [blitEncoder endEncoding]; - - //----------------------------------------------------------- - //NOTE(martin): encode the boxing pass - //----------------------------------------------------------- - id boxEncoder = [surface->commandBuffer computeCommandEncoder]; - [boxEncoder setComputePipelineState: painter->boxingPipeline]; - [boxEncoder setBuffer: painter->vertexBuffer offset:0 atIndex: 0]; - [boxEncoder setBuffer: painter->indexBuffer offset:0 atIndex: 1]; - [boxEncoder setBuffer: painter->triangleArray offset:0 atIndex: 2]; - [boxEncoder setBuffer: painter->boxArray offset:0 atIndex: 3]; - [boxEncoder setBytes: &scale length: sizeof(float) atIndex: 4]; - - MTLSize boxGroupSize = MTLSizeMake(painter->boxingPipeline.maxTotalThreadsPerThreadgroup, 1, 1); - MTLSize boxGridSize = MTLSizeMake(indexCount/3, 1, 1); - - [boxEncoder dispatchThreads: boxGridSize threadsPerThreadgroup: boxGroupSize]; - [boxEncoder endEncoding]; - - //----------------------------------------------------------- - //NOTE(martin): encode the tiling pass - //----------------------------------------------------------- - - id tileEncoder = [surface->commandBuffer computeCommandEncoder]; - [tileEncoder setComputePipelineState: painter->tilingPipeline]; - [tileEncoder setBuffer: painter->boxArray offset:0 atIndex: 0]; - [tileEncoder setBuffer: painter->tileCounters offset:0 atIndex: 1]; - [tileEncoder setBuffer: painter->tilesArray offset:0 atIndex: 2]; - [tileEncoder setBytes: &viewportSize length: sizeof(vector_uint2) atIndex: 3]; - - [tileEncoder dispatchThreads: boxGridSize threadsPerThreadgroup: boxGroupSize]; - [tileEncoder endEncoding]; - - //----------------------------------------------------------- - //NOTE(martin): encode the sorting pass - //----------------------------------------------------------- - - id sortEncoder = [surface->commandBuffer computeCommandEncoder]; - [sortEncoder setComputePipelineState: painter->sortingPipeline]; - [sortEncoder setBuffer: painter->tileCounters offset:0 atIndex: 0]; - [sortEncoder setBuffer: painter->triangleArray offset:0 atIndex: 1]; - [sortEncoder setBuffer: painter->tilesArray offset:0 atIndex: 2]; - [sortEncoder setBytes: &viewportSize length: sizeof(vector_uint2) atIndex: 3]; - - u32 nTilesX = (viewportSize.x + RENDERER_TILE_SIZE - 1)/RENDERER_TILE_SIZE; - u32 nTilesY = (viewportSize.y + RENDERER_TILE_SIZE - 1)/RENDERER_TILE_SIZE; - - MTLSize sortGroupSize = MTLSizeMake(painter->boxingPipeline.maxTotalThreadsPerThreadgroup, 1, 1); - MTLSize sortGridSize = MTLSizeMake(nTilesX*nTilesY, 1, 1); - - [sortEncoder dispatchThreads: sortGridSize threadsPerThreadgroup: sortGroupSize]; - [sortEncoder endEncoding]; - - //----------------------------------------------------------- - //NOTE(martin): create compute encoder and encode commands - //----------------------------------------------------------- - vector_float4 clearColorVec4 = {clearColor.r, clearColor.g, clearColor.b, clearColor.a}; - - id encoder = [surface->commandBuffer computeCommandEncoder]; - [encoder setComputePipelineState:painter->computePipeline]; - [encoder setTexture: painter->outTexture atIndex: 0]; - [encoder setTexture: painter->atlasTexture atIndex: 1]; - [encoder setBuffer: painter->vertexBuffer offset:0 atIndex: 0]; - [encoder setBuffer: painter->tileCounters offset:0 atIndex: 1]; - [encoder setBuffer: painter->tilesArray offset:0 atIndex: 2]; - [encoder setBuffer: painter->triangleArray offset:0 atIndex: 3]; - [encoder setBuffer: painter->boxArray offset:0 atIndex: 4]; - [encoder setBytes: &clearColorVec4 length: sizeof(vector_float4) atIndex: 5]; - - //TODO: check that we don't exceed maxTotalThreadsPerThreadgroup - DEBUG_ASSERT(RENDERER_TILE_SIZE*RENDERER_TILE_SIZE <= painter->computePipeline.maxTotalThreadsPerThreadgroup); - MTLSize threadGridSize = MTLSizeMake(viewportSize.x, viewportSize.y, 1); - MTLSize threadGroupSize = MTLSizeMake(RENDERER_TILE_SIZE, RENDERER_TILE_SIZE, 1); - - [encoder dispatchThreads: threadGridSize threadsPerThreadgroup:threadGroupSize]; - [encoder endEncoding]; - - //----------------------------------------------------------- - //NOTE(martin): acquire drawable, create render encoder to blit texture to framebuffer - //----------------------------------------------------------- - - MTLViewport viewport = {painter->viewPort.x * scale, - painter->viewPort.y * scale, - painter->viewPort.w * scale, - painter->viewPort.h * scale, - 0, - 1}; - - MTLRenderPassDescriptor* renderPassDescriptor = [MTLRenderPassDescriptor renderPassDescriptor]; - renderPassDescriptor.colorAttachments[0].texture = surface->drawable.texture; - renderPassDescriptor.colorAttachments[0].storeAction = MTLStoreActionStore; - - id renderEncoder = [surface->commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor]; - [renderEncoder setViewport: viewport]; - [renderEncoder setRenderPipelineState: painter->renderPipeline]; - [renderEncoder setFragmentTexture: painter->outTexture atIndex: 0]; - [renderEncoder drawPrimitives: MTLPrimitiveTypeTriangle - vertexStart: 0 - vertexCount: 3 ]; - [renderEncoder endEncoding]; - } -} - -void mg_metal_painter_viewport(mg_canvas_painter* interface, mp_rect viewPort) -{ - mg_metal_painter* painter = (mg_metal_painter*)interface; - mg_metal_surface* surface = painter->surface; - - painter->viewPort = viewPort; - - @autoreleasepool - { - f32 scale = surface->metalLayer.contentsScale; - CGSize drawableSize = (CGSize){.width = viewPort.w * scale, .height = viewPort.h * scale}; - - [painter->outTexture release]; - - MTLTextureDescriptor* texDesc = [[MTLTextureDescriptor alloc] init]; - texDesc.textureType = MTLTextureType2D; - texDesc.storageMode = MTLStorageModePrivate; - texDesc.usage = MTLTextureUsageShaderRead | MTLTextureUsageShaderWrite; - texDesc.pixelFormat = MTLPixelFormatBGRA8Unorm;// MTLPixelFormatBGRA8Unorm_sRGB; - texDesc.width = drawableSize.width; - texDesc.height = drawableSize.height; - - painter->outTexture = [surface->device newTextureWithDescriptor:texDesc]; - } -} - -void mg_metal_painter_update_vertex_layout(mg_metal_painter* painter) -{ - mg_metal_surface* surface = painter->surface; - - char* vertexBase = (char*)[painter->vertexBuffer contents]; - - painter->interface.vertexLayout = (mgc_vertex_layout){ - .maxVertexCount = MG_METAL_PAINTER_DEFAULT_BUFFER_LENGTH, - .maxIndexCount = MG_METAL_PAINTER_DEFAULT_BUFFER_LENGTH, - .posBuffer = vertexBase + offsetof(mg_vertex, pos), - .posStride = sizeof(mg_vertex), - .cubicBuffer = vertexBase + offsetof(mg_vertex, cubic), - .cubicStride = sizeof(mg_vertex), - .uvBuffer = vertexBase + offsetof(mg_vertex, uv), - .uvStride = sizeof(mg_vertex), - .colorBuffer = vertexBase + offsetof(mg_vertex, color), - .colorStride = sizeof(mg_vertex), - .zIndexBuffer = vertexBase + offsetof(mg_vertex, zIndex), - .zIndexStride = sizeof(mg_vertex), - .clipsBuffer = vertexBase + offsetof(mg_vertex, clip), - .clipsStride = sizeof(mg_vertex), - .indexBuffer = [painter->indexBuffer contents], - .indexStride = sizeof(int)}; -} - - -void mg_metal_painter_destroy(mg_canvas_painter* interface) -{ - mg_metal_painter* painter = (mg_metal_painter*)interface; - - @autoreleasepool - { - [painter->outTexture release]; - [painter->atlasTexture release]; - [painter->vertexBuffer release]; - [painter->indexBuffer release]; - [painter->tilesArray release]; - [painter->triangleArray release]; - [painter->boxArray release]; - [painter->computePipeline release]; - } -} - -void mg_metal_painter_atlas_upload(mg_canvas_painter* interface, mp_rect rect, u8* bytes) -{@autoreleasepool{ - mg_metal_painter* painter = (mg_metal_painter*)interface; - - MTLRegion region = MTLRegionMake2D(rect.x, rect.y, rect.w, rect.h); - [painter->atlasTexture replaceRegion:region - mipmapLevel:0 - withBytes:(void*)bytes - bytesPerRow: 4 * rect.w]; -}} - -extern "C" mg_canvas_painter* mg_metal_painter_create_for_surface(mg_metal_surface* surface, mp_rect viewPort) -{ - mg_metal_painter* painter = malloc_type(mg_metal_painter); - painter->surface = surface; - - //NOTE(martin): setup interface functions - painter->interface.drawBuffers = mg_metal_painter_draw_buffers; - painter->interface.setViewPort = mg_metal_painter_viewport; - painter->interface.destroy = mg_metal_painter_destroy; - painter->interface.atlasUpload = mg_metal_painter_atlas_upload; - - painter->viewPort = viewPort; - - @autoreleasepool - { - f32 scale = surface->metalLayer.contentsScale; - CGSize drawableSize = (CGSize){.width = viewPort.w * scale, .height = viewPort.h * scale}; - - //----------------------------------------------------------- - //NOTE(martin): create our output texture - //----------------------------------------------------------- - MTLTextureDescriptor* texDesc = [[MTLTextureDescriptor alloc] init]; - texDesc.textureType = MTLTextureType2D; - texDesc.storageMode = MTLStorageModePrivate; - texDesc.usage = MTLTextureUsageShaderRead | MTLTextureUsageShaderWrite; - texDesc.pixelFormat = MTLPixelFormatBGRA8Unorm;// MTLPixelFormatBGRA8Unorm_sRGB; - texDesc.width = drawableSize.width; - texDesc.height = drawableSize.height; - - painter->outTexture = [surface->device newTextureWithDescriptor:texDesc]; - //TODO(martin): retain ? - - //----------------------------------------------------------- - //NOTE(martin): create our atlas texture - //----------------------------------------------------------- - texDesc.textureType = MTLTextureType2D; - texDesc.storageMode = MTLStorageModeManaged; - texDesc.usage = MTLTextureUsageShaderRead; - texDesc.pixelFormat = MTLPixelFormatRGBA8Unorm; //MTLPixelFormatBGRA8Unorm; - texDesc.width = MG_ATLAS_SIZE; - texDesc.height = MG_ATLAS_SIZE; - - painter->atlasTexture = [surface->device newTextureWithDescriptor:texDesc]; - - //----------------------------------------------------------- - //NOTE(martin): create buffers for vertex and index - //----------------------------------------------------------- - - MTLResourceOptions bufferOptions = MTLResourceCPUCacheModeWriteCombined - | MTLResourceStorageModeShared; - - painter->indexBuffer = [surface->device newBufferWithLength: MG_METAL_PAINTER_DEFAULT_BUFFER_LENGTH*sizeof(int) - options: bufferOptions]; - - painter->vertexBuffer = [surface->device newBufferWithLength: MG_METAL_PAINTER_DEFAULT_BUFFER_LENGTH*sizeof(mg_vertex) - options: bufferOptions]; - - painter->tilesArray = [surface->device newBufferWithLength: RENDERER_TILE_BUFFER_SIZE*sizeof(int)*RENDERER_MAX_TILES - options: MTLResourceStorageModePrivate]; - - painter->triangleArray = [surface->device newBufferWithLength: MG_METAL_PAINTER_DEFAULT_BUFFER_LENGTH*sizeof(mg_triangle_data) - options: MTLResourceStorageModePrivate]; - - painter->boxArray = [surface->device newBufferWithLength: MG_METAL_PAINTER_DEFAULT_BUFFER_LENGTH*sizeof(vector_float4) - options: MTLResourceStorageModePrivate]; - - //TODO(martin): retain ? - //----------------------------------------------------------- - //NOTE(martin): create and initialize tile counters - //----------------------------------------------------------- - painter->tileCounters = [surface->device newBufferWithLength: RENDERER_MAX_TILES*sizeof(uint) - options: MTLResourceStorageModePrivate]; - id commandBuffer = [surface->commandQueue commandBuffer]; - id blitEncoder = [commandBuffer blitCommandEncoder]; - [blitEncoder fillBuffer: painter->tileCounters range: NSMakeRange(0, RENDERER_MAX_TILES*sizeof(uint)) value: 0]; - [blitEncoder endEncoding]; - [commandBuffer commit]; - - //----------------------------------------------------------- - //NOTE(martin): load the library - //----------------------------------------------------------- - - //TODO(martin): filepath magic to find metallib path when not in the working directory - char* shaderPath = 0; - mp_app_get_resource_path("../resources/metal_shader.metallib", &shaderPath); - NSString* metalFileName = [[NSString alloc] initWithCString: shaderPath encoding: NSUTF8StringEncoding]; - free(shaderPath); - NSError* err = 0; - id library = [surface->device newLibraryWithFile: metalFileName error:&err]; - if(err != nil) - { - const char* errStr = [[err localizedDescription] UTF8String]; - LOG_ERROR("error : %s\n", errStr); - return(0); - } - id tilingFunction = [library newFunctionWithName:@"TileKernel"]; - id sortingFunction = [library newFunctionWithName:@"SortKernel"]; - id boxingFunction = [library newFunctionWithName:@"BoundingBoxKernel"]; - id computeFunction = [library newFunctionWithName:@"RenderKernel"]; - id vertexFunction = [library newFunctionWithName:@"VertexShader"]; - id fragmentFunction = [library newFunctionWithName:@"FragmentShader"]; - - //----------------------------------------------------------- - //NOTE(martin): setup our data layout and pipeline state - //----------------------------------------------------------- - NSError* error = NULL; - painter->computePipeline = [surface->device newComputePipelineStateWithFunction: computeFunction - error:&error]; - ASSERT(painter->computePipeline); - - MTLComputePipelineDescriptor* tilingPipelineDesc = [[MTLComputePipelineDescriptor alloc] init]; - tilingPipelineDesc.computeFunction = tilingFunction; -// tilingPipelineDesc.threadGroupSizeIsMultipleOfThreadExecutionWidth = true; - - painter->tilingPipeline = [surface->device newComputePipelineStateWithDescriptor: tilingPipelineDesc - options: MTLPipelineOptionNone - reflection: nil - error: &error]; - - MTLComputePipelineDescriptor* sortingPipelineDesc = [[MTLComputePipelineDescriptor alloc] init]; - sortingPipelineDesc.computeFunction = sortingFunction; -// sortingPipelineDesc.threadGroupSizeIsMultipleOfThreadExecutionWidth = true; - - painter->sortingPipeline = [surface->device newComputePipelineStateWithDescriptor: sortingPipelineDesc - options: MTLPipelineOptionNone - reflection: nil - error: &error]; - - MTLComputePipelineDescriptor* boxingPipelineDesc = [[MTLComputePipelineDescriptor alloc] init]; - boxingPipelineDesc.computeFunction = boxingFunction; -// boxingPipelineDesc.threadGroupSizeIsMultipleOfThreadExecutionWidth = true; - - painter->boxingPipeline = [surface->device newComputePipelineStateWithDescriptor: boxingPipelineDesc - options: MTLPipelineOptionNone - reflection: nil - error: &error]; - //----------------------------------------------------------- - //NOTE(martin): setup our render pipeline state - //----------------------------------------------------------- - // create and initialize the pipeline state descriptor - MTLRenderPipelineDescriptor *pipelineStateDescriptor = [[MTLRenderPipelineDescriptor alloc] init]; - pipelineStateDescriptor.label = @"My simple pipeline"; - pipelineStateDescriptor.vertexFunction = vertexFunction; - pipelineStateDescriptor.fragmentFunction = fragmentFunction; - pipelineStateDescriptor.colorAttachments[0].pixelFormat = surface->metalLayer.pixelFormat; - - // create render pipeline - painter->renderPipeline = [surface->device newRenderPipelineStateWithDescriptor: pipelineStateDescriptor error:&err]; - if(err != nil) - { - const char* errStr = [[err localizedDescription] UTF8String]; - const char* descStr = [[err localizedFailureReason] UTF8String]; - const char* recovStr = [[err localizedRecoverySuggestion] UTF8String]; - LOG_ERROR("(%li) %s. %s. %s\n", [err code], errStr, descStr, recovStr); - return(0); - } - } - - mg_metal_painter_update_vertex_layout(painter); - - return((mg_canvas_painter*)painter); -} - - -#undef LOG_SUBSYSTEM diff --git a/src/metal_surface.mm b/src/metal_surface.mm deleted file mode 100644 index d5ab513..0000000 --- a/src/metal_surface.mm +++ /dev/null @@ -1,231 +0,0 @@ -/************************************************************//** -* -* @file: graphics.mm -* @author: Martin Fouilleul -* @date: 12/07/2020 -* @revision: -* -*****************************************************************/ -#import -#import -#include - -#include"graphics_internal.h" -#include"macro_helpers.h" -#include"osx_app.h" - -#define LOG_SUBSYSTEM "Graphics" - -static const u32 MP_METAL_MAX_DRAWABLES_IN_FLIGHT = 3; - -typedef struct mg_metal_surface -{ - mg_surface_info interface; - - // permanent metal resources - NSView* view; - id device; - CAMetalLayer* metalLayer; - id commandQueue; - - // transient metal resources - id drawable; - id commandBuffer; - - dispatch_semaphore_t drawableSemaphore; - -} mg_metal_surface; - -void mg_metal_surface_acquire_drawable_and_command_buffer(mg_metal_surface* surface) -{@autoreleasepool{ - /*WARN(martin): this is super important - When the app is put in the background, it seems that if there are buffers in flight, the drawables to - can be leaked. This causes the gpu to allocate more and more drawables, until the app crashes. - (note: the drawable objects themselves are released once the app comes back to the forefront, but the - memory allocated in the GPU is never freed...) - - In background the gpu seems to create drawable if none is available instead of actually - blocking on nextDrawable. These drawable never get freed. - This is not a problem if our shader is fast enough, since a previous drawable becomes - available before we finish the frame. But we want to protect against it anyway - - The normal blocking mechanism of nextDrawable seems useless, so we implement our own scheme by - counting the number of drawables available with a semaphore that gets decremented here and - incremented in the presentedHandler of the drawable. - Thus we ensure that we don't consume more drawables than we are able to draw. - - //TODO: we _also_ should stop trying to render if we detect that the app is in the background - or occluded, but we can't count only on this because there is a potential race between the - notification of background mode and the rendering. - - //TODO: We should set a reasonable timeout and skip the frame and log an error in case we are stalled - for too long. - */ - dispatch_semaphore_wait(surface->drawableSemaphore, DISPATCH_TIME_FOREVER); - - surface->drawable = [surface->metalLayer nextDrawable]; - ASSERT(surface->drawable != nil); - - //TODO: make this a weak reference if we use ARC - dispatch_semaphore_t semaphore = surface->drawableSemaphore; - [surface->drawable addPresentedHandler:^(id drawable){ - dispatch_semaphore_signal(semaphore); - }]; - - //NOTE(martin): create a command buffer - surface->commandBuffer = [surface->commandQueue commandBuffer]; - - [surface->commandBuffer retain]; - [surface->drawable retain]; -}} - -void mg_metal_surface_prepare(mg_surface_info* interface) -{ - mg_metal_surface* surface = (mg_metal_surface*)interface; - mg_metal_surface_acquire_drawable_and_command_buffer(surface); -} - -void mg_metal_surface_present(mg_surface_info* interface) -{ - mg_metal_surface* surface = (mg_metal_surface*)interface; - @autoreleasepool - { - //NOTE(martin): present drawable and commit command buffer - [surface->commandBuffer presentDrawable: surface->drawable]; - [surface->commandBuffer commit]; - [surface->commandBuffer waitUntilCompleted]; - - //NOTE(martin): acquire next frame resources - [surface->commandBuffer release]; - surface->commandBuffer = nil; - [surface->drawable release]; - surface->drawable = nil; - } -} - -void mg_metal_surface_destroy(mg_surface_info* interface) -{ - mg_metal_surface* surface = (mg_metal_surface*)interface; - - @autoreleasepool - { - [surface->commandQueue release]; - [surface->metalLayer release]; - [surface->device release]; - } -} - -void mg_metal_surface_resize(mg_surface_info* interface, u32 width, u32 height) -{ - mg_metal_surface* surface = (mg_metal_surface*)interface; - - @autoreleasepool - { - //TODO(martin): actually detect scaling - CGRect frame = {{0, 0}, {width, height}}; - [surface->view setFrame: frame]; - f32 scale = surface->metalLayer.contentsScale; - CGSize drawableSize = (CGSize){.width = width * scale, .height = height * scale}; - surface->metalLayer.drawableSize = drawableSize; - } -} - -vec2 mg_metal_surface_get_size(mg_surface_info* interface) -{ - mg_metal_surface* surface = (mg_metal_surface*)interface; - - @autoreleasepool - { - //TODO(martin): actually detect scaling - CGRect frame = surface->view.frame; - return((vec2){frame.size.width, frame.size.height}); - } -} - -static const f32 MG_METAL_SURFACE_CONTENTS_SCALING = 2; - -extern "C" mg_surface mg_metal_surface_create_for_view(mp_view view) -{ - mp_view_data* viewData = mp_view_ptr_from_handle(view); - if(!viewData) - { - return(mg_surface_nil()); - } - else - { - mg_metal_surface* surface = (mg_metal_surface*)malloc(sizeof(mg_metal_surface)); - - //NOTE(martin): setup interface functions - surface->interface.backend = MG_BACKEND_METAL; - surface->interface.destroy = mg_metal_surface_destroy; - surface->interface.prepare = mg_metal_surface_prepare; - surface->interface.present = mg_metal_surface_present; - surface->interface.resize = mg_metal_surface_resize; - surface->interface.getSize = mg_metal_surface_get_size; - - @autoreleasepool - { - surface->drawableSemaphore = dispatch_semaphore_create(MP_METAL_MAX_DRAWABLES_IN_FLIGHT); - - //----------------------------------------------------------- - //NOTE(martin): create a metal device and a metal layer and - //----------------------------------------------------------- - surface->device = MTLCreateSystemDefaultDevice(); - [surface->device retain]; - surface->metalLayer = [CAMetalLayer layer]; - [surface->metalLayer retain]; - surface->metalLayer.device = surface->device; - - surface->view = viewData->nsView; - - //----------------------------------------------------------- - //NOTE(martin): set the size and scaling - //----------------------------------------------------------- - CGSize size = surface->view.bounds.size; - size.width *= MG_METAL_SURFACE_CONTENTS_SCALING; - size.height *= MG_METAL_SURFACE_CONTENTS_SCALING; - surface->metalLayer.drawableSize = size; - surface->metalLayer.contentsScale = MG_METAL_SURFACE_CONTENTS_SCALING; - - surface->metalLayer.pixelFormat = MTLPixelFormatBGRA8Unorm; - - surface->view.wantsLayer = YES; - surface->view.layer = surface->metalLayer; - - //NOTE(martin): handling resizing - surface->view.layerContentsRedrawPolicy = NSViewLayerContentsRedrawDuringViewResize; - surface->metalLayer.autoresizingMask = kCALayerHeightSizable | kCALayerWidthSizable; - surface->metalLayer.needsDisplayOnBoundsChange = YES; - - //----------------------------------------------------------- - //NOTE(martin): create a command queue - //----------------------------------------------------------- - surface->commandQueue = [surface->device newCommandQueue]; - [surface->commandQueue retain]; - - //NOTE(martin): command buffer and drawable are set on demand and at the end of each present() call - surface->drawable = nil; - surface->commandBuffer = nil; - } - - mg_surface handle = mg_surface_alloc_handle((mg_surface_info*)surface); - viewData->surface = handle; - - return(handle); - } -} - -extern "C" mg_surface mg_metal_surface_create_for_window(mp_window window) -{ - mp_window_data* windowData = mp_window_ptr_from_handle(window); - if(!windowData) - { - return(mg_surface_nil()); - } - else - { - return(mg_metal_surface_create_for_view(windowData->mainView)); - } -} - -#undef LOG_SUBSYSTEM diff --git a/src/milepost.h b/src/milepost.h index e4603be..e7c7e4d 100644 --- a/src/milepost.h +++ b/src/milepost.h @@ -27,5 +27,6 @@ #include"mp_app.h" #include"graphics.h" #include"ui.h" +#include"hash.h" #endif //__MILEPOST_H_ diff --git a/src/milepost.mm b/src/milepost.mm deleted file mode 100644 index 98124c6..0000000 --- a/src/milepost.mm +++ /dev/null @@ -1,12 +0,0 @@ -/************************************************************//** -* -* @file: milepost.mm -* @author: Martin Fouilleul -* @date: 13/02/2021 -* @revision: -* -*****************************************************************/ - -#include"osx_app.mm" -#include"metal_surface.mm" -#include"metal_painter.mm" diff --git a/src/mp_app.h b/src/mp_app.h index ad48cbd..c00225d 100644 --- a/src/mp_app.h +++ b/src/mp_app.h @@ -338,6 +338,7 @@ typedef void(*mp_event_callback)(mp_event event, void* data); void mp_set_event_callback(mp_event_callback callback, void* data); void mp_set_target_fps(u32 fps); void mp_run_loop(); +void mp_end_input_frame(); void mp_pump_events(f64 timeout); bool mp_next_event(mp_event* event); @@ -345,8 +346,6 @@ bool mp_next_event(mp_event* event); typedef void(*mp_live_resize_callback)(mp_event event, void* data); void mp_set_live_resize_callback(mp_live_resize_callback callback, void* data); - - //-------------------------------------------------------------------- // Input state polling //-------------------------------------------------------------------- @@ -373,7 +372,7 @@ str8 mp_input_text_utf8(mem_arena* arena); //-------------------------------------------------------------------- // app resources //-------------------------------------------------------------------- -int mp_app_get_resource_path(const char* name, char** result); +str8 mp_app_get_resource_path(mem_arena* arena, const char* name); str8 mp_app_get_executable_path(mem_arena* arena); //-------------------------------------------------------------------- diff --git a/src/osx_app.h b/src/osx_app.h index cd98b0b..fb0c31f 100644 --- a/src/osx_app.h +++ b/src/osx_app.h @@ -14,7 +14,7 @@ #include"mp_app.h" #include"graphics.h" -struct mp_window_data +typedef struct mp_window_data { list_elt freeListElt; u32 generation; @@ -31,9 +31,9 @@ struct mp_window_data bool hidden; mp_view mainView; -}; +} mp_window_data; -struct mp_view_data +typedef struct mp_view_data { list_elt freeListElt; u32 generation; @@ -41,7 +41,7 @@ struct mp_view_data mp_window window; NSView* nsView; mg_surface surface; -}; +} mp_view_data; @interface MPNativeWindow : NSWindow { diff --git a/src/osx_app.mm b/src/osx_app.mm deleted file mode 100644 index 1517d3d..0000000 --- a/src/osx_app.mm +++ /dev/null @@ -1,2487 +0,0 @@ -//***************************************************************** -// -// $file: osx_app.cpp $ -// $author: Martin Fouilleul $ -// $date: 16/05/2020 $ -// $revision: $ -// $note: (C) 2020 by Martin Fouilleul - all rights reserved $ -// -//***************************************************************** - -#include // malloc/free - -#include"osx_app.h" -#include"lists.h" -#include"ringbuffer.h" -#include"memory.h" -#include"macro_helpers.h" -#include"platform_clock.h" - -#define LOG_SUBSYSTEM "Application" - -//-------------------------------------------------------------------- -// mp window struct and utility functions -//-------------------------------------------------------------------- - -static mp_rect mp_osx_to_user_screen_rect(mp_rect rect) -{ - @autoreleasepool - { - NSRect screenRect = [[NSScreen mainScreen] frame]; - rect.y = screenRect.size.height - rect.y - rect.h; - } - return(rect); -} - -static mp_rect mp_user_to_osx_screen_rect(mp_rect rect) -{ - @autoreleasepool - { - NSRect screenRect = [[NSScreen mainScreen] frame]; - rect.y = screenRect.size.height - rect.y - rect.h; - } - return(rect); -} - -static void mp_window_update_rect_cache(mp_window_data* window) -{ - @autoreleasepool - { - NSRect frame = [window->nsWindow frame]; - window->frameRect = mp_osx_to_user_screen_rect((mp_rect){frame.origin.x, frame.origin.y, frame.size.width, frame.size.height}); - - const NSRect contentRect = [[window->nsWindow contentView] frame]; - - window->contentRect = (mp_rect){ contentRect.origin.x, - contentRect.origin.y, - contentRect.size.width, - contentRect.size.height }; - - window->contentRect.y = window->frameRect.h - window->contentRect.y - window->contentRect.h; - } -} - -static u32 mp_osx_get_window_style_mask(mp_window_style style) -{ - u32 mask = 0; - if(style & MP_WINDOW_STYLE_NO_TITLE) - { - mask = NSWindowStyleMaskBorderless; - } - else - { - mask = NSWindowStyleMaskTitled; - } - - if(!(style & MP_WINDOW_STYLE_FIXED_SIZE)) - { - mask |= NSWindowStyleMaskResizable; - } - if(!(style & MP_WINDOW_STYLE_NO_CLOSE)) - { - mask |= NSWindowStyleMaskClosable; - } - if(!(style & MP_WINDOW_STYLE_NO_MINIFY)) - { - mask |= NSWindowStyleMaskMiniaturizable; - } - return(mask); -} - -//--------------------------------------------------------------- -// App struct and utility functions -//--------------------------------------------------------------- - -const u32 MP_APP_MAX_WINDOWS = 128; -const u32 MP_APP_MAX_VIEWS = 128; - -typedef struct mp_frame_stats -{ - f64 start; - f64 workTime; - f64 remainingTime; - f64 targetFramePeriod; -} mp_frame_stats; - -typedef struct mp_key_utf8 -{ - u8 labelLen; - char label[8]; -} mp_key_utf8; - -typedef struct mp_key_state -{ - u64 lastUpdate; - u32 transitionCounter; - bool down; - bool clicked; - bool doubleClicked; - -} mp_key_state; - -typedef struct mp_keyboard_state -{ - mp_key_state keys[MP_KEY_COUNT]; - mp_key_mods mods; -} mp_keyboard_state; - -typedef struct mp_mouse_state -{ - u64 lastUpdate; - vec2 pos; - vec2 delta; - vec2 wheel; - - union - { - mp_key_state buttons[MP_MOUSE_BUTTON_COUNT]; - struct - { - mp_key_state left; - mp_key_state right; - mp_key_state middle; - mp_key_state ext1; - mp_key_state ext2; - }; - }; -} mp_mouse_state; - -const u32 MP_INPUT_TEXT_BACKING_SIZE = 64; - -typedef struct mp_text_state -{ - u64 lastUpdate; - utf32 backing[MP_INPUT_TEXT_BACKING_SIZE]; - str32 codePoints; -} mp_text_state; - -typedef struct mp_input_state -{ - u64 frameCounter; - mp_keyboard_state keyboard; - mp_mouse_state mouse; - mp_text_state text; -} mp_input_state; - -struct mp_app_data -{ - bool init; - bool shouldQuit; - - mp_input_state inputState; - ringbuffer eventQueue; - - mp_live_resize_callback liveResizeCallback; - void* liveResizeData; - - //TODO: we should probably use a CVDisplayLink, but it complexifies things since - // it's called from another thread - NSTimer* frameTimer; - mp_event_callback eventCallback; - void* eventData; - - mp_frame_stats frameStats; - - NSCursor* cursor; - mp_window_data windowPool[MP_APP_MAX_WINDOWS]; - list_info windowFreeList; - - mp_view_data viewPool[MP_APP_MAX_VIEWS]; - list_info viewFreeList; - - TISInputSourceRef kbLayoutInputSource; - void* kbLayoutUnicodeData; - id kbLayoutListener; - mp_key_utf8 mpKeyToLabel[256]; - - int mpKeysToNative[MP_KEY_MAX]; - int nativeToMPKeys[256]; - -}; - -static mp_app_data __mpAppData = {}; - -//////////////////////////////////////////////////////////////////////////////////////////////////////// - -void mp_init_window_handles() -{ - ListInit(&__mpAppData.windowFreeList); - for(int i=0; i>32; - u32 generation = handle.h & 0xffffffff; - if(index >= MP_APP_MAX_WINDOWS) - { - return(0); - } - mp_window_data* window = &__mpAppData.windowPool[index]; - if(window->generation != generation) - { - return(0); - } - else - { - return(window); - } -} - -mp_window mp_window_handle_from_ptr(mp_window_data* window) -{ - DEBUG_ASSERT( (window - __mpAppData.windowPool) >= 0 - && (window - __mpAppData.windowPool) < MP_APP_MAX_WINDOWS); - - u64 h = ((u64)(window - __mpAppData.windowPool))<<32 - | ((u64)window->generation); - - return((mp_window){h}); -} - -void mp_window_recycle_ptr(mp_window_data* window) -{ - window->generation++; - ListPush(&__mpAppData.windowFreeList, &window->freeListElt); -} - - -void mp_init_view_handles() -{ - ListInit(&__mpAppData.viewFreeList); - for(int i=0; i>32; - u32 generation = handle.h & 0xffffffff; - if(index >= MP_APP_MAX_VIEWS) - { - return(0); - } - mp_view_data* view = &__mpAppData.viewPool[index]; - if(view->generation != generation) - { - return(0); - } - else - { - return(view); - } -} - -mp_view mp_view_handle_from_ptr(mp_view_data* view) -{ - DEBUG_ASSERT( (view - __mpAppData.viewPool) >= 0 - && (view - __mpAppData.viewPool) < MP_APP_MAX_VIEWS); - - u64 h = ((u64)(view - __mpAppData.viewPool))<<32 - | ((u64)view->generation); - - return((mp_view){h}); -} - -void mp_view_recycle_ptr(mp_view_data* view) -{ - view->generation++; - ListPush(&__mpAppData.viewFreeList, &view->freeListElt); -} - -/* -void mp_app_set_process_event_callback(mp_app_process_event_callback callback, void* userData) -{ - DEBUG_ASSERT(callback); - __mpAppData.userData = userData; - __mpAppData.processEvent = callback; -} -*/ -///////////////////////////////////////////////////////////////////////////////////////////////////////// - - -static void mp_init_osx_keys() -{ - memset(__mpAppData.nativeToMPKeys, MP_KEY_UNKNOWN, 256*sizeof(int)); - - __mpAppData.nativeToMPKeys[0x1D] = MP_KEY_0; - __mpAppData.nativeToMPKeys[0x12] = MP_KEY_1; - __mpAppData.nativeToMPKeys[0x13] = MP_KEY_2; - __mpAppData.nativeToMPKeys[0x14] = MP_KEY_3; - __mpAppData.nativeToMPKeys[0x15] = MP_KEY_4; - __mpAppData.nativeToMPKeys[0x17] = MP_KEY_5; - __mpAppData.nativeToMPKeys[0x16] = MP_KEY_6; - __mpAppData.nativeToMPKeys[0x1A] = MP_KEY_7; - __mpAppData.nativeToMPKeys[0x1C] = MP_KEY_8; - __mpAppData.nativeToMPKeys[0x19] = MP_KEY_9; - __mpAppData.nativeToMPKeys[0x00] = MP_KEY_A; - __mpAppData.nativeToMPKeys[0x0B] = MP_KEY_B; - __mpAppData.nativeToMPKeys[0x08] = MP_KEY_C; - __mpAppData.nativeToMPKeys[0x02] = MP_KEY_D; - __mpAppData.nativeToMPKeys[0x0E] = MP_KEY_E; - __mpAppData.nativeToMPKeys[0x03] = MP_KEY_F; - __mpAppData.nativeToMPKeys[0x05] = MP_KEY_G; - __mpAppData.nativeToMPKeys[0x04] = MP_KEY_H; - __mpAppData.nativeToMPKeys[0x22] = MP_KEY_I; - __mpAppData.nativeToMPKeys[0x26] = MP_KEY_J; - __mpAppData.nativeToMPKeys[0x28] = MP_KEY_K; - __mpAppData.nativeToMPKeys[0x25] = MP_KEY_L; - __mpAppData.nativeToMPKeys[0x2E] = MP_KEY_M; - __mpAppData.nativeToMPKeys[0x2D] = MP_KEY_N; - __mpAppData.nativeToMPKeys[0x1F] = MP_KEY_O; - __mpAppData.nativeToMPKeys[0x23] = MP_KEY_P; - __mpAppData.nativeToMPKeys[0x0C] = MP_KEY_Q; - __mpAppData.nativeToMPKeys[0x0F] = MP_KEY_R; - __mpAppData.nativeToMPKeys[0x01] = MP_KEY_S; - __mpAppData.nativeToMPKeys[0x11] = MP_KEY_T; - __mpAppData.nativeToMPKeys[0x20] = MP_KEY_U; - __mpAppData.nativeToMPKeys[0x09] = MP_KEY_V; - __mpAppData.nativeToMPKeys[0x0D] = MP_KEY_W; - __mpAppData.nativeToMPKeys[0x07] = MP_KEY_X; - __mpAppData.nativeToMPKeys[0x10] = MP_KEY_Y; - __mpAppData.nativeToMPKeys[0x06] = MP_KEY_Z; - - __mpAppData.nativeToMPKeys[0x27] = MP_KEY_APOSTROPHE; - __mpAppData.nativeToMPKeys[0x2A] = MP_KEY_BACKSLASH; - __mpAppData.nativeToMPKeys[0x2B] = MP_KEY_COMMA; - __mpAppData.nativeToMPKeys[0x18] = MP_KEY_EQUAL; - __mpAppData.nativeToMPKeys[0x32] = MP_KEY_GRAVE_ACCENT; - __mpAppData.nativeToMPKeys[0x21] = MP_KEY_LEFT_BRACKET; - __mpAppData.nativeToMPKeys[0x1B] = MP_KEY_MINUS; - __mpAppData.nativeToMPKeys[0x2F] = MP_KEY_PERIOD; - __mpAppData.nativeToMPKeys[0x1E] = MP_KEY_RIGHT_BRACKET; - __mpAppData.nativeToMPKeys[0x29] = MP_KEY_SEMICOLON; - __mpAppData.nativeToMPKeys[0x2C] = MP_KEY_SLASH; - __mpAppData.nativeToMPKeys[0x0A] = MP_KEY_WORLD_1; - - __mpAppData.nativeToMPKeys[0x33] = MP_KEY_BACKSPACE; - __mpAppData.nativeToMPKeys[0x39] = MP_KEY_CAPS_LOCK; - __mpAppData.nativeToMPKeys[0x75] = MP_KEY_DELETE; - __mpAppData.nativeToMPKeys[0x7D] = MP_KEY_DOWN; - __mpAppData.nativeToMPKeys[0x77] = MP_KEY_END; - __mpAppData.nativeToMPKeys[0x24] = MP_KEY_ENTER; - __mpAppData.nativeToMPKeys[0x35] = MP_KEY_ESCAPE; - __mpAppData.nativeToMPKeys[0x7A] = MP_KEY_F1; - __mpAppData.nativeToMPKeys[0x78] = MP_KEY_F2; - __mpAppData.nativeToMPKeys[0x63] = MP_KEY_F3; - __mpAppData.nativeToMPKeys[0x76] = MP_KEY_F4; - __mpAppData.nativeToMPKeys[0x60] = MP_KEY_F5; - __mpAppData.nativeToMPKeys[0x61] = MP_KEY_F6; - __mpAppData.nativeToMPKeys[0x62] = MP_KEY_F7; - __mpAppData.nativeToMPKeys[0x64] = MP_KEY_F8; - __mpAppData.nativeToMPKeys[0x65] = MP_KEY_F9; - __mpAppData.nativeToMPKeys[0x6D] = MP_KEY_F10; - __mpAppData.nativeToMPKeys[0x67] = MP_KEY_F11; - __mpAppData.nativeToMPKeys[0x6F] = MP_KEY_F12; - __mpAppData.nativeToMPKeys[0x69] = MP_KEY_F13; - __mpAppData.nativeToMPKeys[0x6B] = MP_KEY_F14; - __mpAppData.nativeToMPKeys[0x71] = MP_KEY_F15; - __mpAppData.nativeToMPKeys[0x6A] = MP_KEY_F16; - __mpAppData.nativeToMPKeys[0x40] = MP_KEY_F17; - __mpAppData.nativeToMPKeys[0x4F] = MP_KEY_F18; - __mpAppData.nativeToMPKeys[0x50] = MP_KEY_F19; - __mpAppData.nativeToMPKeys[0x5A] = MP_KEY_F20; - __mpAppData.nativeToMPKeys[0x73] = MP_KEY_HOME; - __mpAppData.nativeToMPKeys[0x72] = MP_KEY_INSERT; - __mpAppData.nativeToMPKeys[0x7B] = MP_KEY_LEFT; - __mpAppData.nativeToMPKeys[0x3A] = MP_KEY_LEFT_ALT; - __mpAppData.nativeToMPKeys[0x3B] = MP_KEY_LEFT_CONTROL; - __mpAppData.nativeToMPKeys[0x38] = MP_KEY_LEFT_SHIFT; - __mpAppData.nativeToMPKeys[0x37] = MP_KEY_LEFT_SUPER; - __mpAppData.nativeToMPKeys[0x6E] = MP_KEY_MENU; - __mpAppData.nativeToMPKeys[0x47] = MP_KEY_NUM_LOCK; - __mpAppData.nativeToMPKeys[0x79] = MP_KEY_PAGE_DOWN; - __mpAppData.nativeToMPKeys[0x74] = MP_KEY_PAGE_UP; - __mpAppData.nativeToMPKeys[0x7C] = MP_KEY_RIGHT; - __mpAppData.nativeToMPKeys[0x3D] = MP_KEY_RIGHT_ALT; - __mpAppData.nativeToMPKeys[0x3E] = MP_KEY_RIGHT_CONTROL; - __mpAppData.nativeToMPKeys[0x3C] = MP_KEY_RIGHT_SHIFT; - __mpAppData.nativeToMPKeys[0x36] = MP_KEY_RIGHT_SUPER; - __mpAppData.nativeToMPKeys[0x31] = MP_KEY_SPACE; - __mpAppData.nativeToMPKeys[0x30] = MP_KEY_TAB; - __mpAppData.nativeToMPKeys[0x7E] = MP_KEY_UP; - - __mpAppData.nativeToMPKeys[0x52] = MP_KEY_KP_0; - __mpAppData.nativeToMPKeys[0x53] = MP_KEY_KP_1; - __mpAppData.nativeToMPKeys[0x54] = MP_KEY_KP_2; - __mpAppData.nativeToMPKeys[0x55] = MP_KEY_KP_3; - __mpAppData.nativeToMPKeys[0x56] = MP_KEY_KP_4; - __mpAppData.nativeToMPKeys[0x57] = MP_KEY_KP_5; - __mpAppData.nativeToMPKeys[0x58] = MP_KEY_KP_6; - __mpAppData.nativeToMPKeys[0x59] = MP_KEY_KP_7; - __mpAppData.nativeToMPKeys[0x5B] = MP_KEY_KP_8; - __mpAppData.nativeToMPKeys[0x5C] = MP_KEY_KP_9; - __mpAppData.nativeToMPKeys[0x45] = MP_KEY_KP_ADD; - __mpAppData.nativeToMPKeys[0x41] = MP_KEY_KP_DECIMAL; - __mpAppData.nativeToMPKeys[0x4B] = MP_KEY_KP_DIVIDE; - __mpAppData.nativeToMPKeys[0x4C] = MP_KEY_KP_ENTER; - __mpAppData.nativeToMPKeys[0x51] = MP_KEY_KP_EQUAL; - __mpAppData.nativeToMPKeys[0x43] = MP_KEY_KP_MULTIPLY; - __mpAppData.nativeToMPKeys[0x4E] = MP_KEY_KP_SUBTRACT; - - memset(__mpAppData.mpKeysToNative, 0, sizeof(int)*MP_KEY_COUNT); - for(int nativeKey=0; nativeKey<256; nativeKey++) - { - mp_key_code mpKey = __mpAppData.nativeToMPKeys[nativeKey]; - if(mpKey) - { - __mpAppData.mpKeysToNative[mpKey] = nativeKey; - } - } -} - -static int mp_convert_osx_key(unsigned short nsCode) -{ - if(nsCode >= 265) - { - return(MP_KEY_UNKNOWN); - } - else - { - return(__mpAppData.nativeToMPKeys[nsCode]); - } -} - -static mp_key_mods mp_convert_osx_mods(NSUInteger nsFlags) -{ - mp_key_mods mods = MP_KEYMOD_NONE; - if(nsFlags & NSEventModifierFlagShift) - { - mods |= MP_KEYMOD_SHIFT; - } - if(nsFlags & NSEventModifierFlagControl) - { - mods |= MP_KEYMOD_CTRL; - } - if(nsFlags & NSEventModifierFlagOption) - { - mods |= MP_KEYMOD_ALT; - } - if(nsFlags & NSEventModifierFlagCommand) - { - mods |= MP_KEYMOD_CMD; - } - return(mods); -} - -static void mp_update_keyboard_layout() -{ - if(__mpAppData.kbLayoutInputSource) - { - CFRelease(__mpAppData.kbLayoutInputSource); - __mpAppData.kbLayoutInputSource = 0; - __mpAppData.kbLayoutUnicodeData = nil; - } - - __mpAppData.kbLayoutInputSource = TISCopyCurrentKeyboardLayoutInputSource(); - if(!__mpAppData.kbLayoutInputSource) - { - LOG_ERROR("Failed to load keyboard layout input source"); - } - - __mpAppData.kbLayoutUnicodeData = TISGetInputSourceProperty(__mpAppData.kbLayoutInputSource, - kTISPropertyUnicodeKeyLayoutData); - if(!__mpAppData.kbLayoutUnicodeData) - { - LOG_ERROR("Failed to load keyboard layout unicode data"); - } - - memset(__mpAppData.mpKeyToLabel, 0, sizeof(mp_key_utf8)*MP_KEY_COUNT); - - for(int key=0; keylabelLen, keyInfo->label); - return(label); -} - -mp_key_code mp_label_to_key(str8 label) -{ - mp_key_code res = MP_KEY_UNKNOWN; - for(int key=0; keylastUpdate != frameCounter) - { - key->transitionCounter = 0; - key->clicked = false; - key->doubleClicked = false; - key->lastUpdate = frameCounter; - } - - key->transitionCounter++; - key->down = down; -} - -static void mp_update_mouse_move(f32 x, f32 y, f32 deltaX, f32 deltaY) -{ - u64 frameCounter = __mpAppData.inputState.frameCounter; - mp_mouse_state* mouse = &__mpAppData.inputState.mouse; - if(mouse->lastUpdate != frameCounter) - { - mouse->delta = (vec2){0, 0}; - mouse->wheel = (vec2){0, 0}; - mouse->lastUpdate = frameCounter; - } - mouse->pos = (vec2){x, y}; - mouse->delta.x += deltaX; - mouse->delta.y += deltaY; -} - -static void mp_update_mouse_wheel(f32 deltaX, f32 deltaY) -{ - u64 frameCounter = __mpAppData.inputState.frameCounter; - mp_mouse_state* mouse = &__mpAppData.inputState.mouse; - if(mouse->lastUpdate != frameCounter) - { - mouse->delta = (vec2){0, 0}; - mouse->wheel = (vec2){0, 0}; - mouse->lastUpdate = frameCounter; - } - mouse->wheel.x += deltaX; - mouse->wheel.y += deltaY; -} - -static void mp_update_text(utf32 codepoint) -{ - - printf("update text\n"); - u64 frameCounter = __mpAppData.inputState.frameCounter; - mp_text_state* text = &__mpAppData.inputState.text; - - if(text->lastUpdate != frameCounter) - { - text->codePoints.len = 0; - text->lastUpdate = frameCounter; - } - - text->codePoints.ptr = text->backing; - if(text->codePoints.len < MP_INPUT_TEXT_BACKING_SIZE) - { - text->codePoints.ptr[text->codePoints.len] = codepoint; - text->codePoints.len++; - } - else - { - LOG_WARNING("too many input codepoints per frame, dropping input"); - } -} - -static void mp_queue_event(mp_event* event) -{ - if(ringbuffer_write_available(&__mpAppData.eventQueue) < sizeof(mp_event)) - { - LOG_ERROR("event queue full\n"); - } - else - { - u32 written = ringbuffer_write(&__mpAppData.eventQueue, sizeof(mp_event), (u8*)event); - DEBUG_ASSERT(written == sizeof(mp_event)); - } -} - -static void mp_dispatch_event(mp_event* event) -{ - if(__mpAppData.eventCallback) - { - __mpAppData.eventCallback(*event, __mpAppData.eventData); - } -} - -//--------------------------------------------------------------- -// Application and app delegate -//--------------------------------------------------------------- - -@interface MPApplication : NSApplication -@end - -@implementation MPApplication --(void)noOpThread:(id)object -{} -@end - -@interface MPAppDelegate : NSObject -@end - -@implementation MPAppDelegate - -- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender -{ - //NOTE: We set shouldQuit to true and send a Quit event - // We then return a value to cancel the direct termination because we still - // want to execte the code after mp_event_loop(). If the user didn't set shouldQuit to - // false, mp_event_loop() will exit, and the user can execute any cleanup needed and - // exit the program. - - __mpAppData.shouldQuit = true; - mp_event event = {}; - event.type = MP_EVENT_QUIT; - mp_dispatch_event(&event); - - return(NSTerminateCancel); -} - -- (void)applicationWillFinishLaunching:(NSNotification *)notification -{@autoreleasepool{ - - //NOTE(martin): add a menu for quit, and a corresponding key equivalent. - // this allows to quit the application when there is no window - // left to catch our Cmd-Q key equivalent - NSMenu* bar = [[NSMenu alloc] init]; - [NSApp setMainMenu:bar]; - - NSMenuItem* appMenuItem = - [bar addItemWithTitle:@"" action:NULL keyEquivalent:@""]; - NSMenu* appMenu = [[NSMenu alloc] init]; - [appMenuItem setSubmenu:appMenu]; - - [appMenu addItemWithTitle: @"Quit" - action: @selector(terminate:) - keyEquivalent: @"q"]; -}} - -- (void)timerElapsed:(NSTimer*)timer -{ - mp_event event = {}; - event.type = MP_EVENT_FRAME; - mp_dispatch_event(&event); -} - -- (void)applicationDidFinishLaunching:(NSNotification *)notification -{@autoreleasepool{ - //WARN(martin): the order of these calls seem to matter a lot for properly showing the menu bar - // with other orderings, the menu doesn't display before the application is put out of - // focus and on focus again... This is flaky undocumented behaviour, so although it is - // fixed by the current ordering, we expect the problem to show up again in future - // versions of macOS. - - //NOTE(martin): send a dummy event to wake-up the run loop and exit from the run loop. - NSEvent* event = [NSEvent otherEventWithType:NSEventTypeApplicationDefined - location:NSMakePoint(0, 0) - modifierFlags:0 - timestamp:0 - windowNumber:0 - context:nil - subtype:0 - data1:0 - data2:0]; - [NSApp postEvent:event atStart:YES]; - [NSApp stop:nil]; -}} - -@end // @implementation MPAppDelegate - -//--------------------------------------------------------------- -// Custom NSWindow -//--------------------------------------------------------------- - -@implementation MPNativeWindow -- (id)initWithMPWindow:(mp_window_data*) window contentRect:(NSRect) rect styleMask:(uint32) style -{ - mpWindow = window; - return([self initWithContentRect:rect styleMask:style backing:NSBackingStoreBuffered defer:NO]); -} -- (BOOL)canBecomeKeyWindow -{ - return(!(mpWindow->style & MP_WINDOW_STYLE_NO_FOCUS)); -} -@end //@implementation MPNativeWindow - -//--------------------------------------------------------------- -// Custom NSWindow delegate -//--------------------------------------------------------------- - -@interface MPNativeWindowDelegate : NSObject -{ - mp_window_data* mpWindow; -} -- (id)initWithMPWindow:(mp_window_data*) window; -@end - -@implementation MPNativeWindowDelegate - -- (id)initWithMPWindow:(mp_window_data*) window -{ - self = [super init]; - if(self != nil) - { - mpWindow = window; - } - return(self); -} - -- (void)windowDidBecomeKey:(NSNotification*)notification -{ - mp_event event = {}; - event.window = mp_window_handle_from_ptr(mpWindow); - event.type = MP_EVENT_WINDOW_FOCUS; - - mpWindow->hidden = false; - - mp_dispatch_event(&event); -} - -- (void)windowDidResignKey:(NSNotification*)notification -{ - mp_event event = {}; - event.window = mp_window_handle_from_ptr(mpWindow); - event.type = MP_EVENT_WINDOW_UNFOCUS; - - mp_dispatch_event(&event); -} - -- (void)windowDidMove:(NSNotification *)notification -{ - const NSRect contentRect = [[mpWindow->nsWindow contentView] frame]; - - mp_window_update_rect_cache(mpWindow); - - mp_event event = {}; - event.window = mp_window_handle_from_ptr(mpWindow); - event.type = MP_EVENT_WINDOW_MOVE; - event.frame.rect.x = contentRect.origin.x; - event.frame.rect.y = contentRect.origin.y; - event.frame.rect.w = contentRect.size.width; - event.frame.rect.h = contentRect.size.height; - - mp_dispatch_event(&event); -} - -- (void)windowDidResize:(NSNotification *)notification -{ - const NSRect contentRect = [[mpWindow->nsWindow contentView] frame]; - - mp_window_update_rect_cache(mpWindow); - - mp_event event = {}; - event.window = mp_window_handle_from_ptr(mpWindow); - event.type = MP_EVENT_WINDOW_RESIZE; - event.frame.rect.x = contentRect.origin.x; - event.frame.rect.y = contentRect.origin.y; - event.frame.rect.w = contentRect.size.width; - event.frame.rect.h = contentRect.size.height; - - mp_rect viewFrame = {0, 0, contentRect.size.width, contentRect.size.height}; - mp_view_set_frame(mpWindow->mainView, viewFrame); - - - if(__mpAppData.liveResizeCallback) - { - __mpAppData.liveResizeCallback(event, __mpAppData.liveResizeData); - } - - //TODO: also ensure we don't overflow the queue during live resize... - mp_dispatch_event(&event); -} - --(void)windowWillStartLiveResize:(NSNotification *)notification -{ - //TODO -} - --(void)windowDidEndLiveResize:(NSNotification *)notification -{ - //TODO -} - -- (void)windowWillClose:(NSNotification *)notification -{ - mpWindow->nsWindow = nil; - [mpWindow->nsView release]; - mpWindow->nsView = nil; - [mpWindow->nsWindowDelegate release]; - mpWindow->nsWindowDelegate = nil; - - mp_window_recycle_ptr(mpWindow); -} - -- (BOOL)windowShouldClose:(id)sender -{ - mpWindow->shouldClose = true; - - mp_event event = {}; - event.window = mp_window_handle_from_ptr(mpWindow); - event.type = MP_EVENT_WINDOW_CLOSE; - - mp_dispatch_event(&event); - - return(mpWindow->shouldClose); -} - -@end //@implementation MPNativeWindowDelegate - -//--------------------------------------------------------------- -// Custom NSView -//--------------------------------------------------------------- - -@interface MPNativeView : NSView -{ - mp_window_data* window; - NSTrackingArea* trackingArea; - NSMutableAttributedString* markedText; -} -- (id)initWithMPWindow:(mp_window_data*) mpWindow; -@end - -@implementation MPNativeView - -- (id)initWithMPWindow:(mp_window_data*) mpWindow -{ - self = [super init]; - if(self != nil) - { - window = mpWindow; - mpWindow->nsView = self; - - NSTrackingAreaOptions trackingOptions = NSTrackingMouseEnteredAndExited - | NSTrackingMouseMoved - | NSTrackingCursorUpdate - | NSTrackingActiveInActiveApp //TODO maybe change that to allow multi-window mouse events... - | NSTrackingEnabledDuringMouseDrag - | NSTrackingInVisibleRect - | NSTrackingAssumeInside ; - - trackingArea = [[NSTrackingArea alloc] initWithRect:[self bounds] options:trackingOptions owner:self userInfo:nil]; - [self addTrackingArea:trackingArea]; - markedText = [[NSMutableAttributedString alloc] init]; - } - return(self); -} - -- (void)dealloc -{ - [trackingArea release]; - [markedText release]; - [super dealloc]; -} - --(void)drawRect:(NSRect)dirtyRect -{ - if(window->style & MP_WINDOW_STYLE_NO_TITLE) - { - [NSGraphicsContext saveGraphicsState]; - NSBezierPath *path = [NSBezierPath bezierPathWithRoundedRect:[self frame] xRadius:5 yRadius:5]; - [path addClip]; - [[NSColor whiteColor] set]; - NSRectFill([self frame]); - } - - if(window->style & MP_WINDOW_STYLE_NO_TITLE) - { - [NSGraphicsContext restoreGraphicsState]; - [window->nsWindow invalidateShadow]; - } -} - -- (BOOL)acceptsFirstReponder -{ - return(YES); -} - -- (void)cursorUpdate:(NSEvent*)event -{ - if(__mpAppData.cursor) - { - [__mpAppData.cursor set]; - } - else - { - [[NSCursor arrowCursor] set]; - } -} - -- (void)mouseDown:(NSEvent *)nsEvent -{ - mp_event event = {}; - event.window = mp_window_handle_from_ptr(window); - event.type = MP_EVENT_MOUSE_BUTTON; - event.key.action = MP_KEY_PRESS; - event.key.code = MP_MOUSE_LEFT; - event.key.mods = mp_convert_osx_mods([nsEvent modifierFlags]); - event.key.clickCount = [nsEvent clickCount]; - - mp_update_key_state(&__mpAppData.inputState.mouse.buttons[event.key.code], true); - if(event.key.clickCount >= 1) - { - __mpAppData.inputState.mouse.buttons[event.key.code].clicked = true; - } - if(event.key.clickCount >= 2) - { - __mpAppData.inputState.mouse.buttons[event.key.code].doubleClicked = true; - } - - mp_dispatch_event(&event); - - [window->nsWindow makeFirstResponder:self]; -} - -- (void)mouseUp:(NSEvent*)nsEvent -{ - mp_event event = {}; - event.window = mp_window_handle_from_ptr(window); - event.type = MP_EVENT_MOUSE_BUTTON; - event.key.action = MP_KEY_RELEASE; - event.key.code = MP_MOUSE_LEFT; - event.key.mods = mp_convert_osx_mods([nsEvent modifierFlags]); - event.key.clickCount = [nsEvent clickCount]; - - mp_update_key_state(&__mpAppData.inputState.mouse.buttons[event.key.code], false); - - mp_dispatch_event(&event); -} - -- (void)rightMouseDown:(NSEvent*)nsEvent -{ - mp_event event = {}; - event.window = mp_window_handle_from_ptr(window); - event.type = MP_EVENT_MOUSE_BUTTON; - event.key.action = MP_KEY_PRESS; - event.key.code = MP_MOUSE_RIGHT; - event.key.mods = mp_convert_osx_mods([nsEvent modifierFlags]); - - mp_update_key_state(&__mpAppData.inputState.mouse.buttons[event.key.code], true); - - mp_dispatch_event(&event); -} - -- (void)rightMouseUp:(NSEvent*)nsEvent -{ - mp_event event = {}; - event.window = mp_window_handle_from_ptr(window); - event.type = MP_EVENT_MOUSE_BUTTON; - event.key.action = MP_KEY_RELEASE; - event.key.code = MP_MOUSE_RIGHT; - event.key.mods = mp_convert_osx_mods([nsEvent modifierFlags]); - - mp_update_key_state(&__mpAppData.inputState.mouse.buttons[event.key.code], false); - - mp_dispatch_event(&event); -} - -- (void)otherMouseDown:(NSEvent*)nsEvent -{ - mp_event event = {}; - event.window = mp_window_handle_from_ptr(window); - event.type = MP_EVENT_MOUSE_BUTTON; - event.key.action = MP_KEY_PRESS; - event.key.code = [nsEvent buttonNumber]; - event.key.mods = mp_convert_osx_mods([nsEvent modifierFlags]); - - mp_update_key_state(&__mpAppData.inputState.mouse.buttons[event.key.code], true); - - mp_dispatch_event(&event); -} - -- (void)otherMouseUp:(NSEvent*)nsEvent -{ - mp_event event = {}; - event.window = mp_window_handle_from_ptr(window); - event.type = MP_EVENT_MOUSE_BUTTON; - event.key.action = MP_KEY_RELEASE; - event.key.code = [nsEvent buttonNumber]; - event.key.mods = mp_convert_osx_mods([nsEvent modifierFlags]); - - mp_update_key_state(&__mpAppData.inputState.mouse.buttons[event.key.code], false); - - mp_dispatch_event(&event); -} - -- (void)mouseDragged:(NSEvent*)nsEvent -{ - [self mouseMoved:nsEvent]; -} - -- (void)mouseMoved:(NSEvent*)nsEvent -{ - NSPoint p = [self convertPoint:[nsEvent locationInWindow] fromView:nil]; - - NSRect frame = [[window->nsWindow contentView] frame]; - mp_event event = {}; - event.type = MP_EVENT_MOUSE_MOVE; - event.window = mp_window_handle_from_ptr(window); - event.move.x = p.x; - event.move.y = p.y; - event.move.deltaX = [nsEvent deltaX]; - event.move.deltaY = -[nsEvent deltaY]; - event.move.mods = mp_convert_osx_mods([nsEvent modifierFlags]); - - mp_update_mouse_move(p.x, p.y, event.move.deltaX, event.move.deltaY); - - mp_dispatch_event(&event); -} - -- (void)scrollWheel:(NSEvent*)nsEvent -{ - mp_event event = {}; - event.window = mp_window_handle_from_ptr(window); - event.type = MP_EVENT_MOUSE_WHEEL; - - double factor = [nsEvent hasPreciseScrollingDeltas] ? 0.1 : 1.0; - event.move.x = 0; - event.move.y = 0; - event.move.deltaX = [nsEvent scrollingDeltaX]*factor; - event.move.deltaY = [nsEvent scrollingDeltaY]*factor; - event.move.mods = mp_convert_osx_mods([nsEvent modifierFlags]); - - mp_update_mouse_wheel(event.move.deltaX, event.move.deltaY); - - mp_dispatch_event(&event); -} - -- (void)mouseExited:(NSEvent *)nsEvent -{ - mp_event event = {}; - event.window = mp_window_handle_from_ptr(window); - event.type = MP_EVENT_MOUSE_LEAVE; - mp_dispatch_event(&event); -} - -- (void)mouseEntered:(NSEvent *)nsEvent -{ - mp_event event = {}; - event.window = mp_window_handle_from_ptr(window); - event.type = MP_EVENT_MOUSE_ENTER; - mp_dispatch_event(&event); -} - - - -- (void)keyDown:(NSEvent*)nsEvent -{ - mp_event event = {}; - event.window = mp_window_handle_from_ptr(window); - event.type = MP_EVENT_KEYBOARD_KEY; - event.key.action = MP_KEY_PRESS; - event.key.code = mp_convert_osx_key([nsEvent keyCode]); - event.key.mods = mp_convert_osx_mods([nsEvent modifierFlags]); - - str8 label = mp_key_to_label(event.key.code); - event.key.labelLen = label.len; - memcpy(event.key.label, label.ptr, label.len); - - mp_update_key_state(&__mpAppData.inputState.keyboard.keys[event.key.code], true); - - mp_dispatch_event(&event); - - [self interpretKeyEvents:@[nsEvent]]; -} - -- (void)keyUp:(NSEvent*)nsEvent -{ - mp_event event = {}; - event.window = mp_window_handle_from_ptr(window); - event.type = MP_EVENT_KEYBOARD_KEY; - event.key.action = MP_KEY_RELEASE; - event.key.code = mp_convert_osx_key([nsEvent keyCode]); - event.key.mods = mp_convert_osx_mods([nsEvent modifierFlags]); - - mp_update_key_state(&__mpAppData.inputState.keyboard.keys[event.key.code], false); - - mp_dispatch_event(&event); -} - -- (void) flagsChanged:(NSEvent*)nsEvent -{ - mp_event event = {}; - event.window = mp_window_handle_from_ptr(window); - event.type = MP_EVENT_KEYBOARD_MODS; - event.key.mods = mp_convert_osx_mods([nsEvent modifierFlags]); - - mp_update_key_mods(event.key.mods); - - mp_dispatch_event(&event); -} - -- (BOOL)performKeyEquivalent:(NSEvent*)nsEvent -{ - if([nsEvent modifierFlags] & NSEventModifierFlagCommand) - { - if([nsEvent charactersIgnoringModifiers] == [NSString stringWithUTF8String:"w"]) - { - [window->nsWindow performClose:self]; - return(YES); - } - else if([nsEvent charactersIgnoringModifiers] == [NSString stringWithUTF8String:"q"]) - { - __mpAppData.shouldQuit = true; - - mp_event event = {}; - event.type = MP_EVENT_QUIT; - - mp_dispatch_event(&event); - - //[NSApp terminate:self]; - return(YES); - } - } - - return([super performKeyEquivalent:nsEvent]); -} - -- (BOOL)hasMarkedText -{ - return([markedText length] > 0); -} - -static const NSRange kEmptyRange = { NSNotFound, 0 }; - -- (NSRange)markedRange -{ - if([markedText length] > 0) - { - return(NSMakeRange(0, [markedText length] - 1)); - } - else - { - return(kEmptyRange); - } -} - -- (NSRange)selectedRange -{ - return(kEmptyRange); -} - -- (void)setMarkedText:(id)string - selectedRange:(NSRange)selectedRange - replacementRange:(NSRange)replacementRange -{ - [markedText release]; - - if([string isKindOfClass:[NSAttributedString class]]) - { - markedText = [[NSMutableAttributedString alloc] initWithAttributedString:string]; - } - else - { - markedText = [[NSMutableAttributedString alloc] initWithString:string]; - } -} - -- (void)unmarkText -{ - [[markedText mutableString] setString:@""]; -} - -- (NSArray*)validAttributesForMarkedText -{ - return([NSArray array]); -} - -- (NSAttributedString*)attributedSubstringForProposedRange:(NSRange)range - actualRange:(NSRangePointer)actualRange -{ - return(nil); -} - -- (NSUInteger)characterIndexForPoint:(NSPoint)point -{ - return(0); -} - -- (NSRect)firstRectForCharacterRange:(NSRange)range - actualRange:(NSRangePointer)actualRange -{ - NSRect frame = [window->nsView frame]; - return(NSMakeRect(frame.origin.x, frame.origin.y, 0.0, 0.0)); -} - -- (void)insertText:(id)string replacementRange:(NSRange)replacementRange -{ - NSString* characters; - NSEvent* nsEvent = [NSApp currentEvent]; - mp_key_mods mods = mp_convert_osx_mods([nsEvent modifierFlags]); - - if([string isKindOfClass:[NSAttributedString class]]) - { - characters = [string string]; - } - else - { - characters = (NSString*) string; - } - - NSRange range = NSMakeRange(0, [characters length]); - while (range.length) - { - utf32 codepoint = 0; - - if ([characters getBytes:&codepoint - maxLength:sizeof(codepoint) - usedLength:NULL - encoding:NSUTF32StringEncoding - options:0 - range:range - remainingRange:&range]) - { - if(codepoint >= 0xf700 && codepoint <= 0xf7ff) - { - continue; - } - - mp_event event = {}; - event.window = mp_window_handle_from_ptr(window); - event.type = MP_EVENT_KEYBOARD_CHAR; - event.character.codepoint = codepoint; - - str8 seq = utf8_encode(event.character.sequence, event.character.codepoint); - event.character.seqLen = seq.len; - - mp_update_text(codepoint); - - mp_dispatch_event(&event); - } - } - [self unmarkText]; -} - -- (void)doCommandBySelector:(SEL)selector -{ -} - - -@end //@implementation MPNativeView - -/* -void mp_sleep_nanoseconds(u64 nanoseconds) -{ - timespec rqtp; - rqtp.tv_sec = nanoseconds / 1000000000; - rqtp.tv_nsec = nanoseconds - rqtp.tv_sec * 1000000000; - nanosleep(&rqtp, 0); -} - -static mach_timebase_info_data_t __machTimeBase__ = {1,1}; - -u64 mp_get_elapsed_nanoseconds() -{ - //NOTE(martin): according to the documentation, mach_absolute_time() does not - // increment when the system is asleep - u64 now = mach_absolute_time(); - now *= __machTimeBase__.numer; - now /= __machTimeBase__.denom; - return(now); -} - -f64 mp_get_elapsed_seconds() -{ - return(1.e-9*(f64)mp_get_elapsed_nanoseconds()); -} -*/ -/* -CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, - const CVTimeStamp *inNow, - const CVTimeStamp *inOutputTime, - CVOptionFlags flagsIn, - CVOptionFlags *flagsOut, - void *displayLinkContext) -{ - if(__mpAppData.displayRefreshCallback) - { - __mpAppData.displayRefreshCallback(__mpAppData.displayRefreshData); - } - - return(0); -} -*/ - -//*************************************************************** -// public API -//*************************************************************** - -//--------------------------------------------------------------- -// App public API -//--------------------------------------------------------------- - -void mp_init() -{@autoreleasepool { - if(!__mpAppData.init) - { - memset(&__mpAppData, 0, sizeof(__mpAppData)); - - mp_clock_init(); - - LOG_MESSAGE("init keys\n"); - mp_init_osx_keys(); - mp_update_keyboard_layout(); - mp_install_keyboard_layout_listener(); - - LOG_MESSAGE("init handles\n"); - mp_init_window_handles(); - mp_init_view_handles(); - - LOG_MESSAGE("init event queue\n"); - ringbuffer_init(&__mpAppData.eventQueue, 16); - - [MPApplication sharedApplication]; - MPAppDelegate* delegate = [[MPAppDelegate alloc] init]; - [NSApp setDelegate: delegate]; - - [NSThread detachNewThreadSelector:@selector(noOpThread:) - toTarget:NSApp - withObject:nil]; - - __mpAppData.init = true; - - LOG_MESSAGE("run application\n"); - [NSApp run]; - - /* - CGDirectDisplayID displayID = CGMainDisplayID(); - CVDisplayLinkCreateWithCGDisplay(displayID, &__mpAppData.displayLink); - CVDisplayLinkSetOutputCallback(__mpAppData.displayLink, DisplayLinkCallback, 0); - */ - - [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; - [NSApp activateIgnoringOtherApps:YES]; - - } -}} - -void mp_terminate() -{ - //TODO: proper app data cleanup (eg delegate, etc) - if(__mpAppData.init) - { - __mpAppData = (mp_app_data){0}; - } -} - -bool mp_should_quit() -{ - return(__mpAppData.shouldQuit); -} - -void mp_do_quit() -{ - __mpAppData.shouldQuit = true; -} - -void mp_cancel_quit() -{ - __mpAppData.shouldQuit = false; -} - -void mp_request_quit() -{ - __mpAppData.shouldQuit = true; - mp_event event = {}; - event.type = MP_EVENT_QUIT; - mp_dispatch_event(&event); -} - -void mp_set_cursor(mp_mouse_cursor cursor) -{ - switch(cursor) - { - case MP_MOUSE_CURSOR_ARROW: - { - __mpAppData.cursor = [NSCursor arrowCursor]; - } break; - case MP_MOUSE_CURSOR_RESIZE_0: - { - __mpAppData.cursor = [[NSCursor class] performSelector:@selector(_windowResizeEastWestCursor)]; - } break; - case MP_MOUSE_CURSOR_RESIZE_90: - { - __mpAppData.cursor = [[NSCursor class] performSelector:@selector(_windowResizeNorthSouthCursor)]; - } break; - case MP_MOUSE_CURSOR_RESIZE_45: - { - __mpAppData.cursor = [[NSCursor class] performSelector:@selector(_windowResizeNorthEastSouthWestCursor)]; - } break; - case MP_MOUSE_CURSOR_RESIZE_135: - { - __mpAppData.cursor = [[NSCursor class] performSelector:@selector(_windowResizeNorthWestSouthEastCursor)]; - } break; - case MP_MOUSE_CURSOR_TEXT: - { - __mpAppData.cursor = [NSCursor IBeamCursor]; - } break; - } - [__mpAppData.cursor set]; -} - -void mp_clipboard_clear() -{@autoreleasepool{ - NSPasteboard* pb = [NSPasteboard generalPasteboard]; - [pb clearContents]; -}} - -void mp_clipboard_set_string(str8 string) -{@autoreleasepool{ - - NSString* nsString = [[NSString alloc] initWithBytes:string.ptr length:string.len encoding:NSUTF8StringEncoding]; - NSPasteboard* pb = [NSPasteboard generalPasteboard]; - [pb writeObjects:[[NSArray alloc] initWithObjects:nsString, nil]]; -}} - -str8 mp_clipboard_copy_string(str8 backing) -{@autoreleasepool{ - //WARN(martin): maxSize includes space for a null terminator - - NSPasteboard* pb = [NSPasteboard generalPasteboard]; - NSString* nsString = [pb stringForType:NSPasteboardTypeString]; - const char* cString = [nsString UTF8String]; - u32 len = minimum(backing.len-1, strlen(cString)); //length without null terminator - strncpy(backing.ptr, cString, backing.len-1); - backing.ptr[len] = '\0'; - - str8 result = str8_slice(backing, 0, len); - return(result); -}} - -str8 mp_clipboard_get_string(mem_arena* arena) -{@autoreleasepool{ - //WARN(martin): maxSize includes space for a null terminator - - NSPasteboard* pb = [NSPasteboard generalPasteboard]; - NSString* nsString = [pb stringForType:NSPasteboardTypeString]; - const char* cString = [nsString UTF8String]; - str8 result = str8_push_cstring(arena, cString); - return(result); -}} - -bool mp_clipboard_has_tag(const char* tag) -{@autoreleasepool{ - - NSString* tagString = [[NSString alloc] initWithUTF8String: tag]; - NSArray* tagArray = [NSArray arrayWithObjects: tagString, nil]; - - NSPasteboard* pb = [NSPasteboard generalPasteboard]; - NSString* available = [pb availableTypeFromArray: tagArray]; - - return(available != nil); -}} - -void mp_clipboard_set_data_for_tag(const char* tag, str8 string) -{@autoreleasepool{ - - NSString* tagString = [[NSString alloc] initWithUTF8String: tag]; - NSArray* tagArray = [NSArray arrayWithObjects: tagString, nil]; - NSData* nsData = [NSData dataWithBytes:string.ptr length:string.len]; - - NSPasteboard* pb = [NSPasteboard generalPasteboard]; - [pb addTypes: tagArray owner:nil]; - [pb setData: nsData forType: tagString]; -}} - -str8 mp_clipboard_get_data_for_tag(mem_arena* arena, const char* tag) -{@autoreleasepool{ - - NSString* tagString = [[NSString alloc] initWithUTF8String: tag]; - - NSPasteboard* pb = [NSPasteboard generalPasteboard]; - NSData* nsData = [pb dataForType: tagString]; - str8 result = str8_push_buffer(arena, [nsData length], (char*)[nsData bytes]); - return(result); -}} - - -//--------------------------------------------------------------- -// Window public API -//--------------------------------------------------------------- - -bool mp_window_handle_is_null(mp_window window) -{ - return(window.h == 0); -} - -mp_window mp_window_null_handle() -{ - return((mp_window){.h = 0}); -} - -/* -//TODO(martin): review include scheme -extern "C" { - mp_graphics_surface mp_metal_surface_create_for_window_ptr(mp_window_data* window); - mp_graphics_surface mp_graphics_surface_null_handle(); - mp_graphics_surface mp_graphics_surface_handle_from_ptr(mp_graphics_surface_data* surface); -} -*/ - -mp_window mp_window_create(mp_rect contentRect, const char* title, mp_window_style style) -{@autoreleasepool{ - mp_window_data* window = mp_window_alloc(); - if(!window) - { - LOG_ERROR("Could not allocate window data\n"); - return((mp_window){0}); - } - - window->style = style; - window->shouldClose = false; - window->hidden = true; - - u32 styleMask = mp_osx_get_window_style_mask(style); - - NSRect screenRect = [[NSScreen mainScreen] frame]; - NSRect rect = NSMakeRect(contentRect.x, - screenRect.size.height - contentRect.y - contentRect.h, - contentRect.w, - contentRect.h); - - window->nsWindow = [[MPNativeWindow alloc] initWithMPWindow: window contentRect:rect styleMask:styleMask]; - window->nsWindowDelegate = [[MPNativeWindowDelegate alloc] initWithMPWindow:window]; - - [window->nsWindow setDelegate:(id)window->nsWindowDelegate]; - [window->nsWindow setTitle:[NSString stringWithUTF8String:title]]; - - if(style & MP_WINDOW_STYLE_NO_TITLE) - { - [window->nsWindow setOpaque:NO]; - [window->nsWindow setBackgroundColor:[NSColor clearColor]]; - [window->nsWindow setHasShadow:YES]; - } - if(style & MP_WINDOW_STYLE_FLOAT) - { - [window->nsWindow setLevel:NSFloatingWindowLevel]; - [window->nsWindow setHidesOnDeactivate:YES]; - } - if(style & MP_WINDOW_STYLE_NO_BUTTONS) - { - [[window->nsWindow standardWindowButton:NSWindowCloseButton] setHidden:YES]; - [[window->nsWindow standardWindowButton:NSWindowMiniaturizeButton] setHidden:YES]; - [[window->nsWindow standardWindowButton:NSWindowZoomButton] setHidden:YES]; - } - - MPNativeView* view = [[MPNativeView alloc] initWithMPWindow:window]; - - [window->nsWindow setContentView:view]; - [window->nsWindow makeFirstResponder:view]; - [window->nsWindow setAcceptsMouseMovedEvents:YES]; - - mp_window_update_rect_cache(window); - - mp_window windowHandle = mp_window_handle_from_ptr(window); - - mp_rect mainViewFrame = {0, 0, contentRect.w, contentRect.h}; - window->mainView = mp_view_create(windowHandle, mainViewFrame); - - return(windowHandle); -}//autoreleasepool -} - -void mp_window_destroy(mp_window window) -{@autoreleasepool{ - mp_window_data* windowData = mp_window_ptr_from_handle(window); - if(windowData) - { - [windowData->nsWindow orderOut:nil]; - - [windowData->nsWindow setDelegate:nil]; - [windowData->nsWindowDelegate release]; - windowData->nsWindowDelegate = nil; - - [windowData->nsView release]; - windowData->nsView = nil; - - [windowData->nsWindow close]; //also release the window - - mp_window_recycle_ptr(windowData); - } -} // autoreleasepool -} - -bool mp_window_should_close(mp_window window) -{ - mp_window_data* windowData = mp_window_ptr_from_handle(window); - if(windowData) - { - return(windowData->shouldClose); - } - else - { - return(false); - } -} - -void mp_window_cancel_close(mp_window window) -{ - mp_window_data* windowData = mp_window_ptr_from_handle(window); - if(windowData) - { - windowData->shouldClose = false; - } -} - -void mp_window_request_close(mp_window window) -{ - mp_window_data* windowData = mp_window_ptr_from_handle(window); - if(windowData) - { - [windowData->nsWindow close]; - //NOTE(martin): this will call our window delegate willClose method - } -} - -void* mp_window_native_pointer(mp_window window) -{ - mp_window_data* windowData = mp_window_ptr_from_handle(window); - if(windowData) - { - return((__bridge void*)windowData->nsWindow); - } - else - { - return(0); - } -} - -void mp_window_center(mp_window window) -{@autoreleasepool{ - mp_window_data* windowData = mp_window_ptr_from_handle(window); - if(windowData) - { - [windowData->nsWindow center]; - } -}} - -bool mp_window_is_hidden(mp_window window) -{ - mp_window_data* windowData = mp_window_ptr_from_handle(window); - if(windowData) - { - return(windowData->hidden); - } - else - { - return(false); - } -} - -bool mp_window_is_focused(mp_window window) -{@autoreleasepool{ - mp_window_data* windowData = mp_window_ptr_from_handle(window); - if(windowData) - { - return([windowData->nsWindow isKeyWindow]); - } - else - { - return(false); - } -}} - -void mp_window_hide(mp_window window) -{@autoreleasepool{ - mp_window_data* windowData = mp_window_ptr_from_handle(window); - if(windowData) - { - windowData->hidden = true; - [windowData->nsWindow orderOut:nil]; - } -}} - -void mp_window_focus(mp_window window) -{@autoreleasepool{ - mp_window_data* windowData = mp_window_ptr_from_handle(window); - if(windowData) - { - [windowData->nsWindow makeKeyWindow]; - } -}} - -void mp_window_send_to_back(mp_window window) -{@autoreleasepool{ - mp_window_data* windowData = mp_window_ptr_from_handle(window); - if(windowData) - { - [windowData->nsWindow orderBack:nil]; - } -}} - -void mp_window_bring_to_front(mp_window window) -{@autoreleasepool{ - mp_window_data* windowData = mp_window_ptr_from_handle(window); - if(windowData) - { - windowData->hidden = false; - [windowData->nsWindow orderFront:nil]; - } -}} - -void mp_window_bring_to_front_and_focus(mp_window window) -{ - mp_window_bring_to_front(window); - mp_window_focus(window); -} - - -mp_rect mp_window_content_rect_for_frame_rect(mp_rect frameRect, mp_window_style style) -{@autoreleasepool{ - u32 mask = mp_osx_get_window_style_mask(style); - mp_rect nativeFrame = mp_user_to_osx_screen_rect(frameRect); - NSRect frame = NSMakeRect(nativeFrame.x, nativeFrame.y, nativeFrame.w, nativeFrame.h); - NSRect content = [NSWindow contentRectForFrameRect:frame styleMask:mask]; - mp_rect result = {content.origin.x, content.origin.y, content.size.width, content.size.height}; - result = mp_osx_to_user_screen_rect(result); - return(result); -}} - -mp_rect mp_window_frame_rect_for_content_rect(mp_rect contentRect, mp_window_style style) -{@autoreleasepool{ - uint32 mask = mp_osx_get_window_style_mask(style); - mp_rect nativeContent = mp_user_to_osx_screen_rect(contentRect); - NSRect content = NSMakeRect(nativeContent.x, nativeContent.y, nativeContent.w, nativeContent.h); - NSRect frame = [NSWindow frameRectForContentRect:content styleMask:mask]; - mp_rect result = {frame.origin.x, frame.origin.y, frame.size.width, frame.size.height}; - result = mp_osx_to_user_screen_rect(result); - return(result); -}} - -mp_rect mp_window_get_content_rect(mp_window window) -{ - mp_window_data* windowData = mp_window_ptr_from_handle(window); - if(windowData) - { - return(windowData->contentRect); - } - else - { - return((mp_rect){}); - } -} - -mp_rect mp_window_get_absolute_content_rect(mp_window window) -{ - mp_window_data* windowData = mp_window_ptr_from_handle(window); - if(windowData) - { - mp_rect rect = windowData->contentRect; - rect.x += windowData->frameRect.x; - rect.y += windowData->frameRect.y; - return(rect); - } - else - { - return((mp_rect){}); - } -} - -mp_rect mp_window_get_frame_rect(mp_window window) -{ - mp_window_data* windowData = mp_window_ptr_from_handle(window); - if(windowData) - { - return(windowData->frameRect); - } - else - { - return((mp_rect){}); - } -} - -void mp_window_set_content_rect(mp_window window, mp_rect contentRect) -{@autoreleasepool{ - mp_window_data* windowData = mp_window_ptr_from_handle(window); - if(windowData) - { - u32 mask = mp_osx_get_window_style_mask(windowData->style); - - mp_rect nativeRect = mp_user_to_osx_screen_rect(contentRect); - NSRect content = NSMakeRect(nativeRect.x, nativeRect.y, nativeRect.w, nativeRect.h); - NSRect frame = [NSWindow frameRectForContentRect:content styleMask:mask]; - - [windowData->nsWindow setFrame:frame display:YES]; - - mp_window_update_rect_cache(windowData); - } -}} -void mp_window_set_frame_rect(mp_window window, mp_rect frameRect) -{@autoreleasepool{ - mp_window_data* windowData = mp_window_ptr_from_handle(window); - if(windowData) - { - mp_rect nativeRect = mp_user_to_osx_screen_rect(frameRect); - NSRect frame = NSMakeRect(nativeRect.x, nativeRect.y, nativeRect.w, nativeRect.h); - [windowData->nsWindow setFrame:frame display:YES]; - - mp_window_update_rect_cache(windowData); - NSRect contentRect = [[windowData->nsWindow contentView] frame]; - } -}} - -void mp_window_set_frame_size(mp_window window, int width, int height) -{ - mp_rect frame = mp_window_get_frame_rect(window); - frame.w = width; - frame.h = height; - mp_window_set_frame_rect(window, frame); -} - -void mp_window_set_content_size(mp_window window, int width, int height) -{ - mp_window_data* windowData = mp_window_ptr_from_handle(window); - if(windowData) - { - mp_rect frame = windowData->frameRect; - mp_rect content = mp_window_content_rect_for_frame_rect(frame, windowData->style); - content.w = width; - content.h = height; - frame = mp_window_frame_rect_for_content_rect(content, windowData->style); - mp_window_set_frame_rect(window, frame); - } -} - -//-------------------------------------------------------------------- -// view management -//-------------------------------------------------------------------- - -mp_view mp_view_nil() -{ - return((mp_view){0}); -} - -bool mp_view_is_nil(mp_view view) -{ - return(!view.h); -} - -mp_view mp_view_create(mp_window windowHandle, mp_rect frame) -{@autoreleasepool{ - mp_window_data* window = mp_window_ptr_from_handle(windowHandle); - if(!window) - { - LOG_ERROR("Can't create view for nil window\n"); - return(mp_view_nil()); - } - - mp_view_data* view = mp_view_alloc(); - if(!view) - { - LOG_ERROR("Could not allocate view data\n"); - return(mp_view_nil()); - } - - view->window = windowHandle; - - NSRect nsFrame = {{frame.x, frame.y}, {frame.w, frame.h}}; - view->nsView = [[NSView alloc] initWithFrame: nsFrame]; - - [[window->nsWindow contentView] addSubview: view->nsView]; - - return(mp_view_handle_from_ptr(view)); -}} - -void mp_view_destroy(mp_view viewHandle) -{@autoreleasepool{ - mp_view_data* view = mp_view_ptr_from_handle(viewHandle); - if(!view) - { - return; - } - - mp_window_data* window = mp_window_ptr_from_handle(view->window); - if(!window) - { - return; - } - - [view->nsView removeFromSuperview]; - - mp_view_recycle_ptr(view); -}} - -void mp_view_set_frame(mp_view viewHandle, mp_rect frame) -{ - mp_view_data* view = mp_view_ptr_from_handle(viewHandle); - if(!view) - { - return; - } - - NSRect nsFrame = {{frame.x, frame.y}, {frame.w, frame.h}}; - view->nsView = [[NSView alloc] initWithFrame: nsFrame]; - - mg_surface_resize(view->surface, frame.w, frame.h); -} - -//-------------------------------------------------------------------- -// Main loop throttle -//-------------------------------------------------------------------- - -void mp_set_target_fps(u32 fps) -{ - __mpAppData.frameStats.targetFramePeriod = 1./(f64)fps; - __mpAppData.frameStats.workTime = 0; - __mpAppData.frameStats.remainingTime = 0; - - if(__mpAppData.frameTimer) - { - [__mpAppData.frameTimer invalidate]; - } - - __mpAppData.frameTimer = [NSTimer timerWithTimeInterval: __mpAppData.frameStats.targetFramePeriod - target: [NSApp delegate] - selector:@selector(timerElapsed:) - userInfo:nil - repeats:YES]; - - [[NSRunLoop currentRunLoop] addTimer:__mpAppData.frameTimer forMode:NSRunLoopCommonModes]; -} -/* -void mp_begin_frame() -{ - __mpAppData.frameStats.start = mp_get_elapsed_seconds(); - - LOG_DEBUG("workTime = %.6f (%.6f fps), remaining = %.6f\n", - __mpAppData.frameStats.workTime, - 1/__mpAppData.frameStats.workTime, - __mpAppData.frameStats.remainingTime); - -} - -void mp_end_frame() -{ - __mpAppData.frameStats.workTime = mp_get_elapsed_seconds() - __mpAppData.frameStats.start; - __mpAppData.frameStats.remainingTime = __mpAppData.frameStats.targetFramePeriod - __mpAppData.frameStats.workTime; - - while(__mpAppData.frameStats.remainingTime > 100e-9) - { - if(__mpAppData.frameStats.remainingTime > 10e-6) - { - mp_sleep_nanoseconds(__mpAppData.frameStats.remainingTime*0.8*1e9); - } - __mpAppData.frameStats.workTime = mp_get_elapsed_seconds() - __mpAppData.frameStats.start; - __mpAppData.frameStats.remainingTime = __mpAppData.frameStats.targetFramePeriod - __mpAppData.frameStats.workTime; - } -} -*/ - -//-------------------------------------------------------------------- -// Events handling -//-------------------------------------------------------------------- - -void mp_set_live_resize_callback(mp_live_resize_callback callback, void* data) -{ - __mpAppData.liveResizeCallback = callback; - __mpAppData.liveResizeData = data; -} - - -void mp_set_event_callback(mp_event_callback callback, void* data) -{ - __mpAppData.eventCallback = callback; - __mpAppData.eventData = data; -} - -void mp_pump_events(f64 timeout) -{ - __mpAppData.inputState.frameCounter++; - - @autoreleasepool - { - bool accumulate = false; - NSDate* date = 0; - if(timeout > 0) - { - date = [NSDate dateWithTimeIntervalSinceNow: (double) timeout]; - } - else if(timeout == 0) - { - date = [NSDate distantPast]; - accumulate = true; - } - else - { - date = [NSDate distantFuture]; - } - - for(;;) - { - NSEvent* event = [NSApp nextEventMatchingMask: NSEventMaskAny - untilDate:date - inMode: NSDefaultRunLoopMode - dequeue: YES]; - - if(event != nil) - { - [NSApp sendEvent:event]; - - if(!accumulate) - { - break; - } - } - else - { - break; - } - } - } -} - -bool mp_next_event(mp_event* event) -{ - //NOTE pop and return event from queue - if(ringbuffer_read_available(&__mpAppData.eventQueue) >= sizeof(mp_event)) - { - u64 read = ringbuffer_read(&__mpAppData.eventQueue, sizeof(mp_event), (u8*)event); - DEBUG_ASSERT(read == sizeof(mp_event)); - return(true); - } - else - { - return(false); - } -} - -void mp_run_loop() -{@autoreleasepool { - - //CVDisplayLinkStart(__mpAppData.displayLink); - - while(!__mpAppData.shouldQuit) - { - NSEvent* event = [NSApp nextEventMatchingMask: NSEventMaskAny - untilDate:[NSDate distantFuture] - inMode: NSDefaultRunLoopMode - dequeue: YES]; - - if(event != nil) - { - [NSApp sendEvent:event]; - } - } - - //CVDisplayLinkStop(__mpAppData.displayLink); -}} - -//-------------------------------------------------------------------- -// Input state polling -//-------------------------------------------------------------------- - -mp_key_state mp_input_get_key_state(mp_key_code key) -{ - if(key <= MP_KEY_COUNT) - { - return(__mpAppData.inputState.keyboard.keys[key]); - } - else - { - return((mp_key_state){0}); - } -} -mp_key_state mp_input_get_mouse_button_state(mp_mouse_button button) -{ - if(button <= MP_MOUSE_BUTTON_COUNT) - { - return(__mpAppData.inputState.mouse.buttons[button]); - } - else - { - return((mp_key_state){0}); - } -} - -bool mp_input_check_key_transition(mp_key_state* key, bool pressed) -{ - bool res = ( (key->lastUpdate == __mpAppData.inputState.frameCounter) - && key->transitionCounter - &&(key->down == pressed || key->transitionCounter > 1)); - return(res); -} - -bool mp_input_key_down(mp_key_code key) -{ - mp_key_state state = mp_input_get_key_state(key); - return(state.down); -} -bool mp_input_key_pressed(mp_key_code key) -{ - mp_key_state state = mp_input_get_key_state(key); - bool res = mp_input_check_key_transition(&state, true); - return(res); -} - -bool mp_input_key_released(mp_key_code key) -{ - mp_key_state state = mp_input_get_key_state(key); - bool res = mp_input_check_key_transition(&state, false); - return(res); -} - -bool mp_input_mouse_down(mp_mouse_button button) -{ - mp_key_state state = mp_input_get_mouse_button_state(button); - return(state.down); -} - -bool mp_input_mouse_pressed(mp_mouse_button button) -{ - mp_key_state state = mp_input_get_mouse_button_state(button); - bool res = mp_input_check_key_transition(&state, true); - return(res); -} - -bool mp_input_mouse_released(mp_mouse_button button) -{ - mp_key_state state = mp_input_get_mouse_button_state(button); - bool res = mp_input_check_key_transition(&state, false); - return(res); -} - -bool mp_input_mouse_clicked(mp_mouse_button button) -{ - mp_key_state state = mp_input_get_mouse_button_state(button); - return(state.clicked); -} - -bool mp_input_mouse_double_clicked(mp_mouse_button button) -{ - mp_key_state state = mp_input_get_mouse_button_state(button); - return(state.doubleClicked); -} - -mp_key_mods mp_input_key_mods() -{ - return(__mpAppData.inputState.keyboard.mods); -} - -vec2 mp_input_mouse_position() -{ - return(__mpAppData.inputState.mouse.pos); -} - -vec2 mp_input_mouse_delta() -{ - if(__mpAppData.inputState.mouse.lastUpdate == __mpAppData.inputState.frameCounter) - { - return(__mpAppData.inputState.mouse.delta); - } - else - { - return((vec2){0, 0}); - } -} - -vec2 mp_input_mouse_wheel() -{ - if(__mpAppData.inputState.mouse.lastUpdate == __mpAppData.inputState.frameCounter) - { - return(__mpAppData.inputState.mouse.wheel); - } - else - { - return((vec2){0, 0}); - } -} - -str32 mp_input_text_utf32(mem_arena* arena) -{ - str32 res = {0}; - if(__mpAppData.inputState.text.lastUpdate == __mpAppData.inputState.frameCounter) - { - res = str32_push_copy(arena, __mpAppData.inputState.text.codePoints); - } - return(res); -} - -str8 mp_input_text_utf8(mem_arena* arena) -{ - str8 res = {0}; - if(__mpAppData.inputState.text.lastUpdate == __mpAppData.inputState.frameCounter) - { - res = utf8_push_from_codepoints(arena, __mpAppData.inputState.text.codePoints); - } - return(res); -} - -//-------------------------------------------------------------------- -// app resources -//-------------------------------------------------------------------- - -#import -#include - -int mp_app_get_resource_path(const char* name, char** result) -{ - @autoreleasepool - { - NSBundle* mainBundle = [NSBundle mainBundle]; - if(!mainBundle) - { - //NOTE(martin): we assume we are running from the command line in debug mode - char* currentPath = getcwd(0, 0); - char* buffer = (char*)malloc(strlen(currentPath) + strlen(name) + 2); - strcpy(buffer, currentPath); - strcat(buffer, "/"); - strcat(buffer, name); - *result = realpath(buffer, 0); - free(currentPath); - free(buffer); - return(1); - } - else - { - NSString* nsName = [[NSString alloc] initWithUTF8String:name]; - NSString* nsPath = [mainBundle executablePath]; - - const char* utf8Path = [nsPath UTF8String]; - const char* dir = dirname((char*)utf8Path); - char* buffer = (char*)malloc(strlen(dir) + strlen(name) + 2); - strcpy(buffer, dir); - strcat(buffer, "/"); - strcat(buffer, name); - *result = realpath(buffer, 0); - free(buffer); - - return(-1); - } - } -} - -#include -str8 mp_app_get_executable_path(mem_arena* arena) -{@autoreleasepool{ - str8 result = {}; - u32 size = 0; - _NSGetExecutablePath(0, &size); - result.len = size; - result.ptr = mem_arena_alloc_array(arena, char, result.len); - _NSGetExecutablePath(result.ptr, &size); - return(result); -}} - -//-------------------------------------------------------------------- -// system dialogs windows -//-------------------------------------------------------------------- - -str8 mp_open_dialog(mem_arena* arena, - const char* title, - const char* defaultPath, - int filterCount, - const char** filters, - bool directory) -{ - @autoreleasepool - { - NSWindow *keyWindow = [NSApp keyWindow]; - - NSOpenPanel* dialog = [NSOpenPanel openPanel] ; - [dialog setLevel:CGShieldingWindowLevel()]; - - if(filterCount) - { - NSMutableArray * fileTypesArray = [NSMutableArray array]; - for(int i=0; i < filterCount; i++) - { - NSString * filt = [NSString stringWithUTF8String:filters[i]]; - [fileTypesArray addObject:filt]; - } - [dialog setAllowedFileTypes:fileTypesArray]; - } - // Enable options in the dialog. - if(directory) - { - [dialog setCanChooseDirectories:YES]; - } - else - { - [dialog setCanChooseFiles:YES]; - } - - - [dialog setAllowsMultipleSelection:FALSE]; - NSString* nsPath = [[NSString stringWithUTF8String:defaultPath?defaultPath:"~"] stringByExpandingTildeInPath]; - [dialog setDirectoryURL:[NSURL fileURLWithPath:nsPath]]; - - // Display the dialog box. If the OK pressed, - // process the files. - - if( [dialog runModal] == NSModalResponseOK ) - { - // Gets list of all files selected - NSArray *files = [dialog URLs]; - //TODO: Loop through the files and process them. - - const char* result = [[[files objectAtIndex:0] path] UTF8String]; - - str8 path = str8_push_cstring(arena, result); - [keyWindow makeKeyWindow]; - - return(path); - } - else - { - [keyWindow makeKeyWindow]; - return((str8){0, 0}); - } - } -} - - -str8 mp_save_dialog(mem_arena* arena, - const char* title, - const char* defaultPath, - int filterCount, - const char** filters) -{ - @autoreleasepool - { - NSWindow *keyWindow = [NSApp keyWindow]; - - NSSavePanel* dialog = [NSSavePanel savePanel] ; - [dialog setLevel:CGShieldingWindowLevel()]; - - if(filterCount) - { - NSMutableArray * fileTypesArray = [NSMutableArray array]; - for(int i=0; i < filterCount; i++) - { - NSString * filt = [NSString stringWithUTF8String:filters[i]]; - [fileTypesArray addObject:filt]; - } - - // Enable options in the dialog. - [dialog setAllowedFileTypes:fileTypesArray]; - } - NSString* nsPath = [[NSString stringWithUTF8String:defaultPath?defaultPath:"~"] stringByExpandingTildeInPath]; - [dialog setDirectoryURL:[NSURL fileURLWithPath:nsPath]]; - - // Display the dialog box. If the OK pressed, - // process the files. - - if( [dialog runModal] == NSModalResponseOK ) - { - // Gets list of all files selected - NSURL *files = [dialog URL]; - // Loop through the files and process them. - - const char* result = [[files path] UTF8String]; - - str8 path = str8_push_cstring(arena, result); - [keyWindow makeKeyWindow]; - return(path); - } - else - { - [keyWindow makeKeyWindow]; - return((str8){0, 0}); - } - } -} - -int mp_alert_popup(const char* title, - const char* message, - uint32 count, - const char** options) -{ - @autoreleasepool - { - NSWindow *keyWindow = [NSApp keyWindow]; - - NSAlert* alert = [[NSAlert alloc] init]; - NSString* string; - for(int i=count-1;i>=0;i--) - { - string = [[NSString alloc] initWithUTF8String:options[i]]; - [alert addButtonWithTitle:string]; - [string release]; - } - string = [[NSString alloc] initWithUTF8String:message]; - [alert setMessageText:string]; - [string release]; - - [alert setAlertStyle:NSAlertStyleWarning]; - int result = count - ([alert runModal]-NSAlertFirstButtonReturn) - 1; - printf("result = %i, NSAlertFirstButtonReturn = %li\n", result, (long)NSAlertFirstButtonReturn); - [keyWindow makeKeyWindow]; - return(result); - } -} - - -#undef LOG_SUBSYSTEM diff --git a/src/ui.c b/src/ui.c index 8a5d903..6a36733 100644 --- a/src/ui.c +++ b/src/ui.c @@ -101,7 +101,7 @@ typedef struct ui_context __thread ui_context __uiThreadContext = {0}; __thread ui_context* __uiCurrentContext = 0; -ui_context* ui_get_context() +ui_context* ui_get_context(void) { return(__uiCurrentContext); } @@ -146,7 +146,7 @@ mp_rect ui_intersect_rects(mp_rect lhs, mp_rect rhs) return(r); } -mp_rect ui_clip_top() +mp_rect ui_clip_top(void) { mp_rect r = {-FLT_MAX/2, -FLT_MAX/2, FLT_MAX, FLT_MAX}; ui_context* ui = ui_get_context(); @@ -166,13 +166,13 @@ void ui_clip_push(mp_rect clip) elt->clip = ui_intersect_rects(current, clip); } -void ui_clip_pop() +void ui_clip_pop(void) { ui_context* ui = ui_get_context(); ui_stack_pop(&ui->clipStack); } -ui_box* ui_box_top() +ui_box* ui_box_top(void) { ui_context* ui = ui_get_context(); ui_stack_elt* elt = ui->boxStack; @@ -191,7 +191,7 @@ void ui_box_push(ui_box* box) } } -void ui_box_pop() +void ui_box_pop(void) { ui_context* ui = ui_get_context(); ui_box* box = ui_box_top(); @@ -243,7 +243,7 @@ void _cat2_(ui_push_, name)(type arg) \ { \ _cat3_(ui_push_, name, _ext)(UI_STYLE_TAG_ANY, UI_STYLE_SEL_ANY, arg); \ } \ -void _cat2_(ui_pop_, name)() \ +void _cat2_(ui_pop_, name)(void) \ { \ ui_context* ui = ui_get_context(); \ ui_stack_pop(&ui->stack); \ @@ -395,7 +395,7 @@ void ui_box_cache(ui_context* ui, ui_box* box) ListAppend(&(ui->boxMap[index]), &box->bucketElt); } -ui_box* ui_box_lookup(ui_context* ui, ui_key key) +ui_box* ui_box_lookup_with_key(ui_context* ui, ui_key key) { u64 index = key.hash & (UI_BOX_MAP_BUCKET_COUNT-1); @@ -409,6 +409,18 @@ ui_box* ui_box_lookup(ui_context* ui, ui_key key) return(0); } +ui_box* ui_box_lookup_str8(str8 string) +{ + ui_context* ui = ui_get_context(); + ui_key key = ui_key_from_string(string); + return(ui_box_lookup_with_key(ui, key)); +} + +ui_box* ui_box_lookup(const char* string) +{ + return(ui_box_lookup_str8(str8_from_cstring((char*)string))); +} + //----------------------------------------------------------------------------- // ui boxes //----------------------------------------------------------------------------- @@ -432,7 +444,7 @@ bool ui_box_hovering(ui_box* box, vec2 p) return(result); } -vec2 ui_mouse_position() +vec2 ui_mouse_position(void) { ui_context* ui = ui_get_context(); @@ -441,7 +453,7 @@ vec2 ui_mouse_position() return(mousePos); } -vec2 ui_mouse_delta() +vec2 ui_mouse_delta(void) { ui_context* ui = ui_get_context(); vec2 delta = mp_input_mouse_delta(); @@ -449,7 +461,7 @@ vec2 ui_mouse_delta() return(delta); } -vec2 ui_mouse_wheel() +vec2 ui_mouse_wheel(void) { ui_context* ui = ui_get_context(); vec2 delta = mp_input_mouse_wheel(); @@ -457,61 +469,12 @@ vec2 ui_mouse_wheel() return(delta); } -void ui_box_compute_signals(ui_context* ui, ui_box* box) -{ - ui_sig* sig = mem_arena_alloc_type(&ui->frameArena, ui_sig); - memset(sig, 0, sizeof(ui_sig)); - - if(!box->closed && !box->parentClosed) - { - vec2 mousePos = ui_mouse_position(); - - sig->hovering = ui_box_hovering(box, mousePos); - - if(box->flags & UI_FLAG_CLICKABLE) - { - if(sig->hovering) - { - sig->pressed = mp_input_mouse_pressed(MP_MOUSE_LEFT); - if(sig->pressed) - { - box->dragging = true; - } - - sig->clicked = mp_input_mouse_clicked(MP_MOUSE_LEFT); - sig->doubleClicked = mp_input_mouse_clicked(MP_MOUSE_LEFT); - } - - sig->released = mp_input_mouse_released(MP_MOUSE_LEFT); - if(sig->released) - { - if(box->dragging && sig->hovering) - { - sig->triggered = true; - } - } - - if(!mp_input_mouse_down(MP_MOUSE_LEFT)) - { - box->dragging = false; - } - - sig->dragging = box->dragging; - } - - sig->mouse = (vec2){mousePos.x - box->rect.x, mousePos.y - box->rect.y}; - sig->delta = ui_mouse_delta(); - sig->wheel = ui_mouse_wheel(); - } - box->sig = sig; -} - ui_box* ui_box_make_str8(str8 string, ui_flags flags) { ui_context* ui = ui_get_context(); ui_key key = ui_key_from_string(string); - ui_box* box = ui_box_lookup(ui, key); + ui_box* box = ui_box_lookup_with_key(ui, key); if(!box) { @@ -545,9 +508,6 @@ ui_box* ui_box_make_str8(str8 string, ui_flags flags) box->floating[UI_AXIS_Y] = false; box->layout = box->parent ? box->parent->layout : (ui_layout){0}; - //NOTE: compute input signals - ui_box_compute_signals(ui, box); - //NOTE: compute style ui_style_selector selector = UI_STYLE_SEL_NORMAL; if(box->hot) @@ -583,7 +543,7 @@ ui_box* ui_box_begin(const char* cstring, ui_flags flags) return(ui_box_begin_str8(string, flags)); } -ui_box* ui_box_end() +ui_box* ui_box_end(void) { ui_context* ui = ui_get_context(); ui_box* box = ui_box_top(); @@ -657,7 +617,53 @@ void ui_box_set_tag(ui_box* box, ui_style_tag tag) ui_sig ui_box_sig(ui_box* box) { - return(*box->sig); + //NOTE: compute input signals + ui_sig sig = {0}; + + sig.box = box; + + if(!box->closed && !box->parentClosed) + { + vec2 mousePos = ui_mouse_position(); + + sig.hovering = ui_box_hovering(box, mousePos); + + if(box->flags & UI_FLAG_CLICKABLE) + { + if(sig.hovering) + { + sig.pressed = mp_input_mouse_pressed(MP_MOUSE_LEFT); + if(sig.pressed) + { + box->dragging = true; + } + + sig.clicked = mp_input_mouse_clicked(MP_MOUSE_LEFT); + sig.doubleClicked = mp_input_mouse_double_clicked(MP_MOUSE_LEFT); + } + + sig.released = mp_input_mouse_released(MP_MOUSE_LEFT); + if(sig.released) + { + if(box->dragging && sig.hovering) + { + sig.triggered = true; + } + } + + if(!mp_input_mouse_down(MP_MOUSE_LEFT)) + { + box->dragging = false; + } + + sig.dragging = box->dragging; + } + + sig.mouse = (vec2){mousePos.x - box->rect.x, mousePos.y - box->rect.y}; + sig.delta = ui_mouse_delta(); + sig.wheel = ui_mouse_wheel(); + } + return(sig); } bool ui_box_hidden(ui_box* box) @@ -701,6 +707,13 @@ void ui_animate_color(ui_context* ui, mg_color* color, mg_color target, f32 anim } } +void ui_animate_ui_size(ui_context* ui, ui_size* size, ui_size target, f32 animationTime) +{ + size->kind = target.kind; + ui_animate_f32(ui, &size->value, target.value, animationTime); + ui_animate_f32(ui, &size->strictness, target.strictness, animationTime); +} + void ui_box_compute_styling(ui_context* ui, ui_box* box) { ui_style* targetStyle = box->targetStyle; @@ -708,11 +721,8 @@ void ui_box_compute_styling(ui_context* ui, ui_box* box) f32 animationTime = targetStyle->animationTime; - box->computedStyle.size[UI_AXIS_X] = targetStyle->size[UI_AXIS_X]; - box->computedStyle.size[UI_AXIS_Y] = targetStyle->size[UI_AXIS_Y]; - - //TODO: interpolate based on transition values - u32 flags = box->computedStyle.animationFlags; + //NOTE: interpolate based on transition values + u32 flags = box->targetStyle->animationFlags; if(box->fresh) { @@ -720,6 +730,24 @@ void ui_box_compute_styling(ui_context* ui, ui_box* box) } else { + if(flags & UI_STYLE_ANIMATE_SIZE_X) + { + ui_animate_ui_size(ui, &box->computedStyle.size[UI_AXIS_X], targetStyle->size[UI_AXIS_X], animationTime); + } + else + { + box->computedStyle.size[UI_AXIS_X] = targetStyle->size[UI_AXIS_X]; + } + + if(flags & UI_STYLE_ANIMATE_SIZE_Y) + { + ui_animate_ui_size(ui, &box->computedStyle.size[UI_AXIS_Y], targetStyle->size[UI_AXIS_Y], animationTime); + } + else + { + box->computedStyle.size[UI_AXIS_Y] = targetStyle->size[UI_AXIS_Y]; + } + if(flags & UI_STYLE_ANIMATE_BG_COLOR) { ui_animate_color(ui, &box->computedStyle.bgColor, targetStyle->bgColor, animationTime); @@ -920,6 +948,17 @@ void ui_layout_compute_rect(ui_context* ui, ui_box* box, vec2 pos) box->rect.y - box->scroll.y}; vec2 currentPos = origin; + vec2 margin = {0, 0}; + for(int i=0; icomputedStyle.size[i].kind == UI_SIZE_CHILDREN) + { + margin.c[i] = box->computedStyle.size[i].value; + } + } + currentPos.x += margin.x; + currentPos.y += margin.y; + vec2 contentsSize = {maximum(box->rect.w, box->childrenSum[UI_AXIS_X]), maximum(box->rect.h, box->childrenSum[UI_AXIS_Y])}; @@ -927,7 +966,7 @@ void ui_layout_compute_rect(ui_context* ui, ui_box* box, vec2 pos) { if(align[i] == UI_ALIGN_END) { - currentPos.c[i] += contentsSize.c[i] - box->childrenSum[i]; + currentPos.c[i] += contentsSize.c[i] - box->childrenSum[i] - margin.c[i]; } } if(align[layoutAxis] == UI_ALIGN_CENTER) @@ -1061,6 +1100,11 @@ void ui_draw_box(mg_canvas canvas, ui_box* box) ui_rectangle_fill(canvas, box->rect, style->roundness); } + if((box->flags & UI_FLAG_DRAW_RENDER_PROC) && box->renderProc) + { + box->renderProc(canvas, box, box->renderData); + } + for_each_in_list(&box->children, child, ui_box, listElt) { ui_draw_box(canvas, child); @@ -1088,11 +1132,6 @@ void ui_draw_box(mg_canvas canvas, ui_box* box) mg_fill(canvas); } - if((box->flags & UI_FLAG_DRAW_RENDER_PROC) && box->renderProc) - { - box->renderProc(canvas, box, box->renderData); - } - if(box->flags & UI_FLAG_CLIP) { mg_clip_pop(canvas); @@ -1196,7 +1235,7 @@ void ui_begin_frame(u32 width, u32 height, ui_style defaultStyle) ui_box_push(contents); } -void ui_end_frame() +void ui_end_frame(void) { ui_context* ui = ui_get_context(); @@ -1226,7 +1265,7 @@ void ui_end_frame() //----------------------------------------------------------------------------- // Init / cleanup //----------------------------------------------------------------------------- -void ui_init() +void ui_init(void) { ui_context* ui = &__uiThreadContext; if(!ui->init) @@ -1240,7 +1279,7 @@ void ui_init() } } -void ui_cleanup() +void ui_cleanup(void) { ui_context* ui = ui_get_context(); mem_arena_release(&ui->frameArena); @@ -1253,11 +1292,11 @@ void ui_cleanup() // Basic helpers //----------------------------------------------------------------------------- -ui_sig ui_label(const char* label) +ui_sig ui_label_str8(str8 label) { ui_flags flags = UI_FLAG_CLIP | UI_FLAG_DRAW_TEXT; - ui_box* box = ui_box_make(label, flags); + ui_box* box = ui_box_make_str8(label, flags); ui_box_set_size(box, UI_AXIS_X, UI_SIZE_TEXT, 0, 0); ui_box_set_size(box, UI_AXIS_Y, UI_SIZE_TEXT, 0, 0); @@ -1265,7 +1304,12 @@ ui_sig ui_label(const char* label) return(sig); } -ui_sig ui_button(const char* label) +ui_sig ui_label(const char* label) +{ + return(ui_label_str8(str8_from_cstring((char*)label))); +} + +ui_sig ui_button_str8(str8 label) { ui_flags flags = UI_FLAG_CLICKABLE | UI_FLAG_CLIP @@ -1275,7 +1319,7 @@ ui_sig ui_button(const char* label) | UI_FLAG_HOT_ANIMATION | UI_FLAG_ACTIVE_ANIMATION; - ui_box* box = ui_box_make(label, flags); + ui_box* box = ui_box_make_str8(label, flags); ui_box_set_tag(box, UI_STYLE_TAG_BUTTON); ui_sig sig = ui_box_sig(box); @@ -1299,6 +1343,11 @@ ui_sig ui_button(const char* label) return(sig); } +ui_sig ui_button(const char* label) +{ + return(ui_button_str8(str8_from_cstring((char*)label))); +} + ui_box* ui_scrollbar(const char* label, f32 thumbRatio, f32* scrollValue) { ui_box* frame = ui_box_begin(label, 0); @@ -1317,6 +1366,7 @@ ui_box* ui_scrollbar(const char* label, f32 thumbRatio, f32* scrollValue) ui_push_fg_color_ext(UI_STYLE_TAG_ANY, UI_STYLE_SEL_HOT|UI_STYLE_SEL_ACTIVE, (mg_color){0, 0, 0, 0.7}); ui_push_roundness_ext(UI_STYLE_TAG_ANY, UI_STYLE_SEL_HOT|UI_STYLE_SEL_ACTIVE, roundness); ui_push_animation_time_ext(UI_STYLE_TAG_ANY, UI_STYLE_SEL_ANY, 1.); + ui_push_animation_flags_ext(UI_STYLE_TAG_ANY, UI_STYLE_SEL_ANY, UI_STYLE_ANIMATE_BG_COLOR|UI_STYLE_ANIMATE_FG_COLOR); ui_flags trackFlags = UI_FLAG_CLIP | UI_FLAG_DRAW_BACKGROUND @@ -1358,6 +1408,7 @@ ui_box* ui_scrollbar(const char* label, f32 thumbRatio, f32* scrollValue) ui_pop_fg_color(); ui_pop_roundness(); ui_pop_animation_time(); + ui_pop_animation_flags(); //NOTE: interaction ui_sig thumbSig = ui_box_sig(thumb); @@ -1507,7 +1558,7 @@ ui_sig ui_tooltip_begin(const char* name) return(ui_box_sig(tooltip)); } -void ui_tooltip_end() +void ui_tooltip_end(void) { ui_box_pop(); // tooltip ui_box_pop(); // ui->overlay @@ -1530,7 +1581,7 @@ void ui_menu_bar_begin(const char* name) } } -void ui_menu_bar_end() +void ui_menu_bar_end(void) { ui_pop_size(UI_AXIS_X); ui_pop_size(UI_AXIS_Y); @@ -1583,7 +1634,7 @@ void ui_menu_begin(const char* label) ui_box_push(menu); } -void ui_menu_end() +void ui_menu_end(void) { ui_box_pop(); // menu ui_box_pop(); // overlay; diff --git a/src/ui.h b/src/ui.h index 5f8ad7d..96f047f 100644 --- a/src/ui.h +++ b/src/ui.h @@ -84,15 +84,16 @@ typedef enum { UI_STYLE_SEL_NORMAL = 1<<0, typedef u32 ui_style_tag; #define UI_STYLE_TAG_ANY (ui_style_tag)0 -typedef enum { UI_STYLE_ANIMATE_SIZE = 1<<1, - UI_STYLE_ANIMATE_BG_COLOR = 1<<2, - UI_STYLE_ANIMATE_FG_COLOR = 1<<3, - UI_STYLE_ANIMATE_BORDER_COLOR = 1<<4, - UI_STYLE_ANIMATE_FONT_COLOR = 1<<5, - UI_STYLE_ANIMATE_FONT_SIZE = 1<<6, - UI_STYLE_ANIMATE_BORDER_SIZE = 1<<7, - UI_STYLE_ANIMATE_ROUNDNESS = 1<<8, - UI_STYLE_ANIMATE_POS = 1<<9, +typedef enum { UI_STYLE_ANIMATE_SIZE_X = 1<<1, + UI_STYLE_ANIMATE_SIZE_Y = 1<<2, + UI_STYLE_ANIMATE_BG_COLOR = 1<<3, + UI_STYLE_ANIMATE_FG_COLOR = 1<<4, + UI_STYLE_ANIMATE_BORDER_COLOR = 1<<5, + UI_STYLE_ANIMATE_FONT_COLOR = 1<<6, + UI_STYLE_ANIMATE_FONT_SIZE = 1<<7, + UI_STYLE_ANIMATE_BORDER_SIZE = 1<<8, + UI_STYLE_ANIMATE_ROUNDNESS = 1<<9, + UI_STYLE_ANIMATE_POS = 1<<10, } ui_style_animation_flags; typedef struct ui_style @@ -183,31 +184,41 @@ struct ui_box typedef struct ui_context ui_context; -void ui_init(); -ui_context* ui_get_context(); +void ui_init(void); +ui_context* ui_get_context(void); void ui_set_context(ui_context* context); void ui_begin_frame(u32 width, u32 height, ui_style defaultStyle); -void ui_end_frame(); +void ui_end_frame(void); void ui_draw(mg_canvas canvas); +ui_box* ui_box_lookup(const char* string); +ui_box* ui_box_lookup_str8(str8 string); ui_box* ui_box_make(const char* string, ui_flags flags); ui_box* ui_box_begin(const char* string, ui_flags flags); ui_box* ui_box_make_str8(str8 string, ui_flags flags); ui_box* ui_box_begin_str8(str8 string, ui_flags flags); -ui_box* ui_box_end(); +ui_box* ui_box_end(void); #define ui_container(name, flags) defer_loop(ui_box_begin(name, flags), ui_box_end()) void ui_box_push(ui_box* box); -void ui_box_pop(); -ui_box* ui_box_top(); +void ui_box_pop(void); +ui_box* ui_box_top(void); + +bool ui_box_closed(ui_box* box); +void ui_box_set_closed(ui_box* box, bool closed); + +bool ui_box_active(ui_box* box); +void ui_box_activate(ui_box* box); +void ui_box_deactivate(ui_box* box); + +bool ui_box_hot(ui_box* box); +void ui_box_set_hot(ui_box* box, bool hot); void ui_box_set_render_proc(ui_box* box, ui_box_render_proc proc, void* data); - void ui_box_set_layout(ui_box* box, ui_axis axis, ui_align alignX, ui_align alignY); void ui_box_set_size(ui_box* box, ui_axis axis, ui_size_kind kind, f32 value, f32 strictness); void ui_box_set_floating(ui_box* box, ui_axis axis, f32 pos); - void ui_box_set_style_selector(ui_box* box, ui_style_selector selector); ui_sig ui_box_sig(ui_box* box); @@ -238,16 +249,16 @@ void ui_push_roundness_ext(ui_style_tag tag, ui_style_selector selector, f32 rou void ui_push_animation_time_ext(ui_style_tag tag, ui_style_selector selector, f32 time); void ui_push_animation_flags_ext(ui_style_tag tag, ui_style_selector selector, u32 flags); -void ui_pop_bg_color(); -void ui_pop_fg_color(); -void ui_pop_font(); -void ui_pop_font_size(); -void ui_pop_font_color(); -void ui_pop_border_size(); -void ui_pop_border_color(); -void ui_pop_roundness(); -void ui_pop_animation_time(); -void ui_pop_animation_flags(); +void ui_pop_bg_color(void); +void ui_pop_fg_color(void); +void ui_pop_font(void); +void ui_pop_font_size(void); +void ui_pop_font_color(void); +void ui_pop_border_size(void); +void ui_pop_border_color(void); +void ui_pop_roundness(void); +void ui_pop_animation_time(void); +void ui_pop_animation_flags(void); // Basic helpers enum { @@ -261,23 +272,26 @@ enum { }; ui_sig ui_label(const char* label); +ui_sig ui_label_str8(str8 label); + ui_sig ui_button(const char* label); +ui_sig ui_button_str8(str8 label); ui_box* ui_scrollbar(const char* label, f32 thumbRatio, f32* scrollValue); ui_box* ui_panel_begin(const char* name); -void ui_panel_end(); +void ui_panel_end(void); #define ui_panel(name) defer_loop(ui_panel_begin(name), ui_panel_end()) ui_sig ui_tooltip_begin(const char* name); -void ui_tooltip_end(); +void ui_tooltip_end(void); #define ui_tooltip(name) defer_loop(ui_tooltip_begin(name), ui_tooltip_end()) void ui_menu_bar_begin(const char* label); -void ui_menu_bar_end(); +void ui_menu_bar_end(void); #define ui_menu_bar(name) defer_loop(ui_menu_bar_begin(name), ui_menu_bar_end()) void ui_menu_begin(const char* label); -void ui_menu_end(); +void ui_menu_end(void); #define ui_menu(name) defer_loop(ui_menu_begin(name), ui_menu_end()) typedef struct ui_text_box_result diff --git a/src/util/strings.c b/src/util/strings.c index 8b1845d..d8ef6c6 100644 --- a/src/util/strings.c +++ b/src/util/strings.c @@ -24,7 +24,14 @@ str8 str8_from_buffer(u64 len, char* buffer) str8 str8_from_cstring(char* str) { - return(str8_from_buffer(strlen(str), (char*)str)); + if(!str) + { + return((str8){0}); + } + else + { + return(str8_from_buffer(strlen(str), (char*)str)); + } } str8 str8_slice(str8 s, u64 start, u64 end) @@ -44,7 +51,14 @@ str8 str8_push_buffer(mem_arena* arena, u64 len, char* buffer) str8 str8_push_cstring(mem_arena* arena, const char* str) { - return(str8_push_buffer(arena, strlen(str), (char*)str)); + if(!str) + { + return((str8){0}); + } + else + { + return(str8_push_buffer(arena, strlen(str), (char*)str)); + } } str8 str8_push_copy(mem_arena* arena, str8 s)