diff --git a/src/app/app.c b/src/app/app.c index 57c7776..45e70ad 100644 --- a/src/app/app.c +++ b/src/app/app.c @@ -215,3 +215,12 @@ void oc_window_set_frame_size(oc_window window, oc_vec2 size) frame.h = size.y; oc_window_set_frame_rect(window, frame); } + +//--------------------------------------------------------------- +// dialogs +//--------------------------------------------------------------- + +oc_file_dialog_result oc_file_dialog(oc_arena* arena, oc_file_dialog_desc* desc) +{ + return (oc_file_dialog_for_table(arena, desc, oc_file_table_get_global())); +} diff --git a/src/app/app.h b/src/app/app.h index 657a162..3f1005c 100644 --- a/src/app/app.h +++ b/src/app/app.h @@ -389,6 +389,60 @@ ORCA_API oc_str8 oc_save_dialog(oc_arena* arena, oc_str8 defaultPath, oc_str8_list filters); +#endif // !defined(OC_PLATFORM_ORCA) || !(OC_PLATFORM_ORCA) + +#include "platform/platform_io.h" + +typedef enum +{ + OC_FILE_DIALOG_SAVE, + OC_FILE_DIALOG_OPEN, +} oc_file_dialog_kind; + +typedef u32 oc_file_dialog_flags; + +enum _oc_file_dialog_flags +{ + OC_FILE_DIALOG_FILES = 1, + OC_FILE_DIALOG_DIRECTORIES = 1 << 1, + OC_FILE_DIALOG_MULTIPLE = 1 << 2, + OC_FILE_DIALOG_CREATE_DIRECTORIES = 1 << 3, +}; + +typedef struct oc_file_dialog_desc +{ + oc_file_dialog_kind kind; + oc_file_dialog_flags flags; + oc_str8 title; + oc_str8 okLabel; + oc_file startAt; + oc_str8 startPath; + oc_str8_list filters; + + //... later customization options with checkboxes / radiobuttons +} oc_file_dialog_desc; + +typedef enum +{ + OC_FILE_DIALOG_CANCEL = 0, + OC_FILE_DIALOG_OK, +} oc_file_dialog_button; + +typedef struct oc_file_dialog_result +{ + oc_file_dialog_button button; + oc_str8 path; + oc_str8_list selection; + +} oc_file_dialog_result; + +#if !defined(OC_PLATFORM_ORCA) || !(OC_PLATFORM_ORCA) + +ORCA_API oc_file_dialog_result oc_file_dialog(oc_arena* arena, oc_file_dialog_desc* desc); + +typedef struct oc_file_table oc_file_table; +ORCA_API oc_file_dialog_result oc_file_dialog_for_table(oc_arena* arena, oc_file_dialog_desc* desc, oc_file_table* table); + ORCA_API int oc_alert_popup(oc_str8 title, oc_str8 message, oc_str8_list options); @@ -404,9 +458,7 @@ ORCA_API int oc_directory_create(oc_str8 path); #else void ORCA_IMPORT(oc_request_quit)(void); - void ORCA_IMPORT(oc_runtime_window_set_title)(oc_str8 title); - void ORCA_IMPORT(oc_runtime_window_set_size)(oc_vec2 size); #endif // !defined(OC_PLATFORM_ORCA) || !(OC_PLATFORM_ORCA) diff --git a/src/app/app_internal.h b/src/app/app_internal.h index 54b68d4..0899d1c 100644 --- a/src/app/app_internal.h +++ b/src/app/app_internal.h @@ -12,6 +12,7 @@ #include "app.h" #include "platform/platform.h" +#include "platform/platform_io_internal.h" #include "util/ringbuffer.h" #if OC_PLATFORM_WINDOWS diff --git a/src/app/orca_app.c b/src/app/orca_app.c index fd349f0..01180ba 100644 --- a/src/app/orca_app.c +++ b/src/app/orca_app.c @@ -2,3 +2,8 @@ //This is used to pass raw events from the runtime ORCA_EXPORT oc_event oc_rawEvent; + +ORCA_EXPORT void* oc_arena_push_stub(oc_arena* arena, u64 size) +{ + return (oc_arena_push(arena, size)); +} diff --git a/src/app/osx_app.m b/src/app/osx_app.m index e89bd70..2a6b27c 100644 --- a/src/app/osx_app.m +++ b/src/app/osx_app.m @@ -19,7 +19,7 @@ #include "platform_clock.h" #include "platform_debug.h" #include "ringbuffer.h" - +#include "platform/platform_path.h" #include "app.c" //-------------------------------------------------------------------- @@ -2133,73 +2133,87 @@ oc_str8 oc_open_dialog(oc_arena* arena, oc_str8_list filters, bool directory) { - @autoreleasepool + __block oc_str8 path = { 0 }; + + dispatch_block_t block = ^{ + @autoreleasepool + { + NSWindow* keyWindow = [NSApp keyWindow]; + + NSOpenPanel* dialog = [NSOpenPanel openPanel]; + + NSString* nsTitle = [[NSString alloc] initWithBytes:title.ptr length:title.len encoding:NSUTF8StringEncoding]; + //NOTE: title is not displayed since OS X 10.11, now use setMessage instead. + // see https://stackoverflow.com/questions/36879212/title-bar-missing-in-nsopenpanel + [dialog setMessage:nsTitle]; + + [dialog setLevel:NSModalPanelWindowLevel]; + + if(filters.eltCount) + { + NSMutableArray* fileTypesArray = [NSMutableArray array]; + + oc_list_for((oc_list*)&filters.list, elt, oc_str8_elt, listElt) + { + oc_str8 string = elt->string; + NSString* filter = [[NSString alloc] initWithBytes:string.ptr length:string.len encoding:NSUTF8StringEncoding]; + [fileTypesArray addObject:filter]; + } + [dialog setAllowedFileTypes:fileTypesArray]; + } + // Enable options in the dialog. + if(directory) + { + [dialog setCanChooseDirectories:YES]; + } + else + { + [dialog setCanChooseFiles:YES]; + } + + [dialog setAllowsMultipleSelection:FALSE]; + + NSString* nsPath = 0; + ; + if(defaultPath.len) + { + nsPath = [[NSString alloc] initWithBytes:defaultPath.ptr length:defaultPath.len encoding:NSUTF8StringEncoding]; + } + else + { + nsPath = [NSString stringWithUTF8String:"~"]; + } + nsPath = [nsPath 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]; + + path = oc_str8_push_cstring(arena, result); + } + [keyWindow makeKeyWindow]; + } + }; + + if([NSThread isMainThread]) { - NSWindow* keyWindow = [NSApp keyWindow]; - - NSOpenPanel* dialog = [NSOpenPanel openPanel]; - [dialog setLevel:CGShieldingWindowLevel()]; - - if(filters.eltCount) - { - NSMutableArray* fileTypesArray = [NSMutableArray array]; - - oc_list_for(&filters.list, elt, oc_str8_elt, listElt) - { - oc_str8 string = elt->string; - NSString* filter = [[NSString alloc] initWithBytes:string.ptr length:string.len encoding:NSUTF8StringEncoding]; - [fileTypesArray addObject:filter]; - } - [dialog setAllowedFileTypes:fileTypesArray]; - } - // Enable options in the dialog. - if(directory) - { - [dialog setCanChooseDirectories:YES]; - } - else - { - [dialog setCanChooseFiles:YES]; - } - - [dialog setAllowsMultipleSelection:FALSE]; - - NSString* nsPath = 0; - ; - if(defaultPath.len) - { - nsPath = [[NSString alloc] initWithBytes:defaultPath.ptr length:defaultPath.len encoding:NSUTF8StringEncoding]; - } - else - { - nsPath = [NSString stringWithUTF8String:"~"]; - } - nsPath = [nsPath 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]; - - oc_str8 path = oc_str8_push_cstring(arena, result); - [keyWindow makeKeyWindow]; - - return (path); - } - else - { - [keyWindow makeKeyWindow]; - return ((oc_str8){ 0, 0 }); - } + block(); } + else + { + dispatch_sync(dispatch_get_main_queue(), block); + } + + return (path); } oc_str8 oc_save_dialog(oc_arena* arena, @@ -2207,61 +2221,221 @@ oc_str8 oc_save_dialog(oc_arena* arena, oc_str8 defaultPath, oc_str8_list filters) { - @autoreleasepool + __block oc_str8 path = { 0 }; + + dispatch_block_t block = ^{ + @autoreleasepool + { + NSWindow* keyWindow = [NSApp keyWindow]; + + NSSavePanel* dialog = [NSSavePanel savePanel]; + [dialog setLevel:CGShieldingWindowLevel()]; + + if(filters.eltCount) + { + NSMutableArray* fileTypesArray = [NSMutableArray array]; + + oc_list_for((oc_list*)&filters.list, elt, oc_str8_elt, listElt) + { + oc_str8 string = elt->string; + NSString* filter = [[NSString alloc] initWithBytes:string.ptr length:string.len encoding:NSUTF8StringEncoding]; + [fileTypesArray addObject:filter]; + } + [dialog setAllowedFileTypes:fileTypesArray]; + } + + NSString* nsPath = 0; + ; + if(defaultPath.len) + { + nsPath = [[NSString alloc] initWithBytes:defaultPath.ptr length:defaultPath.len encoding:NSUTF8StringEncoding]; + } + else + { + nsPath = [NSString stringWithUTF8String:"~"]; + } + nsPath = [nsPath 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]; + + path = oc_str8_push_cstring(arena, result); + } + [keyWindow makeKeyWindow]; + } + }; + + if([NSThread isMainThread]) { - NSWindow* keyWindow = [NSApp keyWindow]; - - NSSavePanel* dialog = [NSSavePanel savePanel]; - [dialog setLevel:CGShieldingWindowLevel()]; - - if(filters.eltCount) - { - NSMutableArray* fileTypesArray = [NSMutableArray array]; - - oc_list_for(&filters.list, elt, oc_str8_elt, listElt) - { - oc_str8 string = elt->string; - NSString* filter = [[NSString alloc] initWithBytes:string.ptr length:string.len encoding:NSUTF8StringEncoding]; - [fileTypesArray addObject:filter]; - } - [dialog setAllowedFileTypes:fileTypesArray]; - } - - NSString* nsPath = 0; - ; - if(defaultPath.len) - { - nsPath = [[NSString alloc] initWithBytes:defaultPath.ptr length:defaultPath.len encoding:NSUTF8StringEncoding]; - } - else - { - nsPath = [NSString stringWithUTF8String:"~"]; - } - nsPath = [nsPath 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]; - - oc_str8 path = oc_str8_push_cstring(arena, result); - [keyWindow makeKeyWindow]; - return (path); - } - else - { - [keyWindow makeKeyWindow]; - return ((oc_str8){ 0, 0 }); - } + block(); } + else + { + dispatch_sync(dispatch_get_main_queue(), block); + } + + return (path); +} + +ORCA_API oc_file_dialog_result oc_file_dialog_for_table(oc_arena* arena, oc_file_dialog_desc* desc, oc_file_table* table) +{ + __block oc_file_dialog_result result = { 0 }; + + dispatch_block_t block = ^{ + @autoreleasepool + { + oc_arena_scope scratch = oc_scratch_begin_next(arena); + + NSWindow* keyWindow = [NSApp keyWindow]; + + NSSavePanel* dialog = 0; + if(desc->kind == OC_FILE_DIALOG_OPEN) + { + NSOpenPanel* openPanel = [NSOpenPanel openPanel]; + dialog = (NSSavePanel*)openPanel; + + openPanel.canChooseFiles = (desc->flags & OC_FILE_DIALOG_FILES) ? YES : NO; + openPanel.canChooseDirectories = (desc->flags & OC_FILE_DIALOG_DIRECTORIES) ? YES : NO; + openPanel.allowsMultipleSelection = (desc->flags & OC_FILE_DIALOG_MULTIPLE) ? YES : NO; + } + else + { + dialog = [NSSavePanel savePanel]; + + dialog.canCreateDirectories = (desc->flags & OC_FILE_DIALOG_CREATE_DIRECTORIES) ? YES : NO; + } + + //NOTE: set title. "title" property is not displayed since OS X 10.11, now use setMessage instead. + // see https://stackoverflow.com/questions/36879212/title-bar-missing-in-nsopenpanel + NSString* nsTitle = [[NSString alloc] initWithBytes:desc->title.ptr + length:desc->title.len + encoding:NSUTF8StringEncoding]; + [dialog setMessage:nsTitle]; + + //NOTE: set ok button + if(desc->okLabel.len) + { + NSString* label = [[NSString alloc] initWithBytes:desc->okLabel.ptr + length:desc->okLabel.len + encoding:NSUTF8StringEncoding]; + + [dialog setPrompt:label]; + } + + //NOTE: set starting path + oc_str8 startPath = { 0 }; + { + oc_str8_list list = { 0 }; + if(!oc_file_is_nil(desc->startAt)) + { + oc_file_slot* slot = oc_file_slot_from_handle(table, desc->startAt); + if(slot) + { + char path[PATH_MAX]; + if(fcntl(slot->fd, F_GETPATH, path) != -1) + { + oc_str8 string = oc_str8_push_cstring(scratch.arena, path); + oc_str8_list_push(scratch.arena, &list, string); + } + } + } + if(desc->startPath.len) + { + oc_str8_list_push(scratch.arena, &list, desc->startPath); + } + startPath = oc_path_join(scratch.arena, list); + } + + NSString* nsPath = 0; + if(startPath.len) + { + nsPath = [[NSString alloc] initWithBytes:startPath.ptr + length:startPath.len + encoding:NSUTF8StringEncoding]; + } + else + { + nsPath = [NSString stringWithUTF8String:"~"]; + } + nsPath = [nsPath stringByExpandingTildeInPath]; + [dialog setDirectoryURL:[NSURL fileURLWithPath:nsPath]]; + + //NOTE: set filters + if(desc->filters.eltCount) + { + NSMutableArray* fileTypesArray = [NSMutableArray array]; + + oc_list_for((oc_list*)&desc->filters.list, elt, oc_str8_elt, listElt) + { + oc_str8 string = elt->string; + NSString* filter = [[NSString alloc] initWithBytes:string.ptr length:string.len encoding:NSUTF8StringEncoding]; + [fileTypesArray addObject:filter]; + } + [dialog setAllowedFileTypes:fileTypesArray]; + } + + // Display the dialog box. If the OK pressed, + // process the files. + + [dialog validateVisibleColumns]; + [dialog setLevel:NSModalPanelWindowLevel]; + + if([dialog runModal] == NSModalResponseOK) + { + if(desc->kind == OC_FILE_DIALOG_OPEN && (desc->flags & OC_FILE_DIALOG_MULTIPLE)) + { + // Gets list of all files selected + NSArray* files = [((NSOpenPanel*)dialog) URLs]; + + const char* path = [[[files objectAtIndex:0] path] UTF8String]; + result.path = oc_str8_push_cstring(arena, path); + + for(int i = 0; i < [files count]; i++) + { + const char* path = [[[files objectAtIndex:i] path] UTF8String]; + oc_str8 string = oc_str8_push_cstring(arena, path); + oc_str8_list_push(arena, &result.selection, string); + } + } + else + { + const char* path = [[[dialog URL] path] UTF8String]; + result.path = oc_str8_push_cstring(arena, path); + + oc_str8_list_push(arena, &result.selection, result.path); + } + result.button = OC_FILE_DIALOG_OK; + } + else + { + result.button = OC_FILE_DIALOG_CANCEL; + } + [keyWindow makeKeyWindow]; + + oc_scratch_end(scratch); + } + }; + + if([NSThread isMainThread]) + { + block(); + } + else + { + dispatch_sync(dispatch_get_main_queue(), block); + } + + return (result); } int oc_alert_popup(oc_str8 title, diff --git a/src/app/win32_app.c b/src/app/win32_app.c index e131178..ad672f4 100644 --- a/src/app/win32_app.c +++ b/src/app/win32_app.c @@ -1189,16 +1189,16 @@ void oc_win32_surface_set_hidden(oc_surface_data* surface, bool hidden) void oc_win32_surface_bring_to_front(oc_surface_data* surface) { - oc_list_remove(&surface->layer.parent->win32.layers, &surface->layer.listElt); - oc_list_push(&surface->layer.parent->win32.layers, &surface->layer.listElt); - oc_win32_update_child_layers_zorder(surface->layer.parent); + oc_list_remove(&surface->layer.parent->win32.layers, &surface->layer.listElt); + oc_list_push(&surface->layer.parent->win32.layers, &surface->layer.listElt); + oc_win32_update_child_layers_zorder(surface->layer.parent); } void oc_win32_surface_send_to_back(oc_surface_data* surface) { - oc_list_remove(&surface->layer.parent->win32.layers, &surface->layer.listElt); - oc_list_push_back(&surface->layer.parent->win32.layers, &surface->layer.listElt); - oc_win32_update_child_layers_zorder(surface->layer.parent); + oc_list_remove(&surface->layer.parent->win32.layers, &surface->layer.listElt); + oc_list_push_back(&surface->layer.parent->win32.layers, &surface->layer.listElt); + oc_win32_update_child_layers_zorder(surface->layer.parent); } void* oc_win32_surface_native_layer(oc_surface_data* surface) @@ -1565,6 +1565,207 @@ oc_str8 oc_save_dialog(oc_arena* arena, return (res); } +#include "platform/platform_io_internal.h" + +oc_str16 win32_path_from_handle_null_terminated(oc_arena* arena, HANDLE handle); // defined in win32_io.c + +oc_file_dialog_result oc_file_dialog_for_table(oc_arena* arena, oc_file_dialog_desc* desc, oc_file_table* table) +{ + oc_arena_scope scratch = oc_scratch_begin_next(arena); + oc_file_dialog_result result = { 0 }; + + HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); + if(SUCCEEDED(hr)) + { + IFileDialog* dialog = 0; + + if(desc->kind == OC_FILE_DIALOG_OPEN) + { + hr = CoCreateInstance(&CLSID_FileOpenDialog, NULL, CLSCTX_ALL, &IID_IFileOpenDialog, (void**)&dialog); + if(SUCCEEDED(hr)) + { + FILEOPENDIALOGOPTIONS opt; + dialog->lpVtbl->GetOptions(dialog, &opt); + + //NOTE: OC_FILE_DIALOG_FILES is always implied, since IFileDialog offers no way to pick _only_ folders + if(desc->flags & OC_FILE_DIALOG_DIRECTORIES) + { + opt |= FOS_PICKFOLDERS; + } + else + { + opt &= ~FOS_PICKFOLDERS; + } + + if(desc->flags & OC_FILE_DIALOG_MULTIPLE) + { + opt |= FOS_ALLOWMULTISELECT; + } + else + { + opt &= ~FOS_ALLOWMULTISELECT; + } + + dialog->lpVtbl->SetOptions(dialog, opt); + } + } + else + { + hr = CoCreateInstance(&CLSID_FileSaveDialog, NULL, CLSCTX_ALL, &IID_IFileSaveDialog, (void**)&dialog); + + //NOTE: OC_FILE_DIALOG_CREATE_DIRECTORIES is implied, IFileSaveDialog offers no way of disabling it... + } + + if(SUCCEEDED(hr)) + { + //NOTE set title + if(desc->title.len) + { + oc_str16 titleWide = oc_win32_utf8_to_wide(scratch.arena, desc->title); + dialog->lpVtbl->SetTitle(dialog, (LPCWSTR)titleWide.ptr); + } + + //NOTE set ok button + if(desc->okLabel.len) + { + oc_str16 okLabelWide = oc_win32_utf8_to_wide(scratch.arena, desc->okLabel); + dialog->lpVtbl->SetOkButtonLabel(dialog, (LPCWSTR)okLabelWide.ptr); + } + + //NOTE: set starting path + oc_str8 startPath = { 0 }; + { + oc_str8_list list = { 0 }; + if(!oc_file_is_nil(desc->startAt)) + { + oc_file_slot* slot = oc_file_slot_from_handle(table, desc->startAt); + if(slot) + { + oc_str16 pathWide = win32_path_from_handle_null_terminated(scratch.arena, slot->fd); + oc_str8 path = oc_win32_wide_to_utf8(scratch.arena, pathWide); + //NOTE: remove potential \\?\ prefix which doesn't work with SHCreateItemFromParsingName() + if(!oc_str8_cmp(oc_str8_slice(path, 0, 4), OC_STR8("\\\\?\\"))) + { + path = oc_str8_slice(path, 4, path.len); + } + oc_str8_list_push(scratch.arena, &list, path); + } + } + if(desc->startPath.len) + { + oc_str8_list_push(scratch.arena, &list, desc->startPath); + } + startPath = oc_path_join(scratch.arena, list); + } + + if(startPath.len) + { + oc_str16 pathWide = oc_win32_utf8_to_wide(scratch.arena, startPath); + + IShellItem* item = 0; + hr = SHCreateItemFromParsingName((LPCWSTR)pathWide.ptr, NULL, &IID_IShellItem, (void**)&item); + if(SUCCEEDED(hr)) + { + hr = dialog->lpVtbl->SetFolder(dialog, item); + item->lpVtbl->Release(item); + } + } + + //NOTE: set filters + if(desc->filters.eltCount) + { + COMDLG_FILTERSPEC* filterSpecs = oc_arena_push_array(scratch.arena, COMDLG_FILTERSPEC, desc->filters.eltCount); + + int i = 0; + oc_list_for(&desc->filters.list, elt, oc_str8_elt, listElt) + { + oc_str8_list list = { 0 }; + oc_str8_list_push(scratch.arena, &list, OC_STR8("*.")); + oc_str8_list_push(scratch.arena, &list, elt->string); + oc_str8 filter = oc_str8_list_join(scratch.arena, list); + + int filterWideSize = 1 + MultiByteToWideChar(CP_UTF8, 0, filter.ptr, filter.len, NULL, 0); + filterSpecs[i].pszSpec = oc_arena_push_array(scratch.arena, wchar_t, filterWideSize); + MultiByteToWideChar(CP_UTF8, 0, filter.ptr, filter.len, (LPWSTR)filterSpecs[i].pszSpec, filterWideSize); + ((LPWSTR)(filterSpecs[i].pszSpec))[filterWideSize - 1] = 0; + + filterSpecs[i].pszName = filterSpecs[i].pszSpec; + i++; + } + + hr = dialog->lpVtbl->SetFileTypes(dialog, i, filterSpecs); + } + + hr = dialog->lpVtbl->Show(dialog, NULL); + + if(SUCCEEDED(hr)) + { + if(desc->kind == OC_FILE_DIALOG_OPEN && (desc->flags & OC_FILE_DIALOG_MULTIPLE)) + { + IShellItemArray* array = 0; + hr = ((IFileOpenDialog*)dialog)->lpVtbl->GetResults((IFileOpenDialog*)dialog, &array); + if(SUCCEEDED(hr)) + { + int count = 0; + array->lpVtbl->GetCount(array, &count); + for(int i = 0; i < count; i++) + { + IShellItem* item = 0; + hr = array->lpVtbl->GetItemAt(array, i, &item); + if(SUCCEEDED(hr)) + { + PWSTR pathWCStr = 0; + hr = item->lpVtbl->GetDisplayName(item, SIGDN_FILESYSPATH, &pathWCStr); + if(SUCCEEDED(hr)) + { + oc_str16 pathWide = oc_str16_from_buffer(lstrlenW(pathWCStr), pathWCStr); + oc_str8 path = oc_win32_wide_to_utf8(arena, pathWide); + oc_str8_list_push(arena, &result.selection, path); + + if(i == 0) + { + result.path = path; + } + + CoTaskMemFree(pathWCStr); + } + item->lpVtbl->Release(item); + } + } + result.button = OC_FILE_DIALOG_OK; + } + } + else + { + IShellItem* item; + hr = dialog->lpVtbl->GetResult(dialog, &item); + if(SUCCEEDED(hr)) + { + PWSTR pathWCStr; + hr = item->lpVtbl->GetDisplayName(item, SIGDN_FILESYSPATH, &pathWCStr); + + if(SUCCEEDED(hr)) + { + oc_str16 pathWide = oc_str16_from_buffer(lstrlenW(pathWCStr), pathWCStr); + result.path = oc_win32_wide_to_utf8(arena, pathWide); + oc_str8_list_push(arena, &result.selection, result.path); + + CoTaskMemFree(pathWCStr); + } + item->lpVtbl->Release(item); + result.button = OC_FILE_DIALOG_OK; + } + } + } + } + dialog->lpVtbl->Release(dialog); + } + CoUninitialize(); + + oc_scratch_end(scratch); + return (result); +} + #include int oc_alert_popup(oc_str8 title, diff --git a/src/orca.h b/src/orca.h index 02d4e9c..a983fa5 100644 --- a/src/orca.h +++ b/src/orca.h @@ -22,6 +22,7 @@ #include "platform/platform.h" #include "platform/platform_clock.h" #include "platform/platform_io.h" +#include "platform/platform_io_dialog.h" #include "platform/platform_path.h" #if !defined(OC_PLATFORM_ORCA) || !(OC_PLATFORM_ORCA) diff --git a/src/platform/platform_io.h b/src/platform/platform_io.h index b5403a5..8c85cdb 100644 --- a/src/platform/platform_io.h +++ b/src/platform/platform_io.h @@ -222,4 +222,10 @@ ORCA_API u64 oc_file_size(oc_file file); //TODO: Complete as needed... +//---------------------------------------------------------------- +// Acquiring new file capabilities through user interaction +//---------------------------------------------------------------- + +ORCA_API oc_file oc_file_open_with_request(oc_str8 path, oc_file_access rights, oc_file_open_flags flags); + #endif //__PLATFORM_IO_H_ diff --git a/src/platform/platform_io_common.c b/src/platform/platform_io_common.c index 4d1141b..eb7314d 100644 --- a/src/platform/platform_io_common.c +++ b/src/platform/platform_io_common.c @@ -7,6 +7,7 @@ *****************************************************************/ #include "platform_io.h" +#include "platform_io_dialog.h" //------------------------------------------------------------------------------ // File stream read/write API diff --git a/src/platform/platform_io_dialog.h b/src/platform/platform_io_dialog.h new file mode 100644 index 0000000..1fb1ecc --- /dev/null +++ b/src/platform/platform_io_dialog.h @@ -0,0 +1,29 @@ +/************************************************************/ /** +* +* @file: platform_io_dialog.h +* @author: Martin Fouilleul +* @date: 01/09/2023 +* +*****************************************************************/ +#ifndef __PLATFORM_IO_DIALOG_H_ +#define __PLATFORM_IO_DIALOG_H_ + +#include "platform_io.h" +#include "app/app.h" + +typedef struct oc_file_open_with_dialog_elt +{ + oc_list_elt listElt; + oc_file file; +} oc_file_open_with_dialog_elt; + +typedef struct oc_file_open_with_dialog_result +{ + oc_file_dialog_button button; + oc_file file; + oc_list selection; +} oc_file_open_with_dialog_result; + +ORCA_API oc_file_open_with_dialog_result oc_file_open_with_dialog(oc_arena* arena, oc_file_access rights, oc_file_open_flags flags, oc_file_dialog_desc* desc); + +#endif //__PLATFORM_IO_DIALOG_H_ diff --git a/src/platform/platform_io_internal.c b/src/platform/platform_io_internal.c index 9f922c0..4a99d6b 100644 --- a/src/platform/platform_io_internal.c +++ b/src/platform/platform_io_internal.c @@ -5,12 +5,17 @@ * @date: 11/06/2023 * *****************************************************************/ - -#include "platform_io_internal.h" -#include "platform_path.h" +#include "app/app.h" +#include "platform/platform_io_internal.h" +#include "platform/platform_path.h" oc_file_table oc_globalFileTable = { 0 }; +oc_file_table* oc_file_table_get_global() +{ + return (&oc_globalFileTable); +} + oc_file_slot* oc_file_slot_alloc(oc_file_table* table) { oc_file_slot* slot = oc_list_pop_entry(&table->freeList, oc_file_slot, freeListElt); @@ -62,7 +67,7 @@ oc_file_slot* oc_file_slot_from_handle(oc_file_table* table, oc_file handle) oc_io_cmp oc_io_wait_single_req(oc_io_req* req) { - return (oc_io_wait_single_req_with_table(req, &oc_globalFileTable)); + return (oc_io_wait_single_req_for_table(req, &oc_globalFileTable)); } //----------------------------------------------------------------------- @@ -341,3 +346,100 @@ oc_io_cmp oc_io_open_at(oc_file_slot* atSlot, oc_io_req* req, oc_file_table* tab } return (cmp); } + +oc_file oc_file_open_with_request_for_table(oc_str8 path, oc_file_access rights, oc_file_open_flags flags, oc_file_table* table) +{ + oc_arena_scope scratch = oc_scratch_begin(); + oc_str8 msg = oc_str8_pushf(scratch.arena, "Application wants to access file '%.*s'.", (int)path.len, path.ptr); + + oc_str8_list options = { 0 }; + oc_str8_list_push(scratch.arena, &options, OC_STR8("Deny")); + oc_str8_list_push(scratch.arena, &options, OC_STR8("Accept")); + + int res = oc_alert_popup(OC_STR8("File Access"), msg, options); + + oc_file file = oc_file_nil(); + if(res == 1) + { + oc_io_cmp cmp = { 0 }; + + oc_io_req req = { + .op = OC_IO_OPEN_AT, + .size = path.len, + .buffer = path.ptr, + .open.rights = rights, + .open.flags = flags + }; + cmp = oc_io_open_at(0, &req, table); + if(cmp.error == OC_IO_OK) + { + file = cmp.handle; + } + } + + oc_scratch_end(scratch); + + return (file); +} + +oc_file oc_file_open_with_request(oc_str8 path, oc_file_access rights, oc_file_open_flags flags) +{ + return (oc_file_open_with_request_for_table(path, rights, flags, &oc_globalFileTable)); +} + +oc_file_open_with_dialog_result oc_file_open_with_dialog_for_table(oc_arena* arena, + oc_file_access rights, + oc_file_open_flags flags, + oc_file_dialog_desc* desc, + oc_file_table* table) +{ + oc_arena_scope scratch = oc_scratch_begin_next(arena); + + oc_file_dialog_result dialogResult = oc_file_dialog_for_table(scratch.arena, desc, table); + + oc_file_open_with_dialog_result result = { + .button = dialogResult.button + }; + + if(dialogResult.button == OC_FILE_DIALOG_OK) + { + int i = 0; + oc_list_for(&dialogResult.selection.list, elt, oc_str8_elt, listElt) + { + oc_file file = oc_file_nil(); + if(elt->string.len) + { + oc_io_req req = { + .op = OC_IO_OPEN_AT, + .size = elt->string.len, + .buffer = elt->string.ptr, + .open.rights = rights, + .open.flags = flags + }; + + oc_io_cmp cmp = oc_io_wait_single_req_for_table(&req, table); + file = cmp.handle; + } + + oc_file_open_with_dialog_elt* resElt = oc_arena_push_type(arena, oc_file_open_with_dialog_elt); + memset(resElt, 0, sizeof(*resElt)); + resElt->file = file; + oc_list_push_back(&result.selection, &resElt->listElt); + + if(i == 0) + { + result.file = file; + i++; + } + } + } + + oc_scratch_end(scratch); + + return (result); +} + +oc_file_open_with_dialog_result oc_file_open_with_dialog(oc_arena* arena, oc_file_access rights, oc_file_open_flags flags, oc_file_dialog_desc* desc) +{ + return (oc_file_open_with_dialog_for_table(arena, rights, flags, desc, &oc_globalFileTable)); +} diff --git a/src/platform/platform_io_internal.h b/src/platform/platform_io_internal.h index de20b52..085204e 100644 --- a/src/platform/platform_io_internal.h +++ b/src/platform/platform_io_internal.h @@ -10,6 +10,7 @@ #include "platform.h" #include "platform_io.h" +#include "platform_io_dialog.h" #if OC_PLATFORM_MACOS || PLATFORM_LINUX typedef int oc_file_desc; @@ -46,12 +47,22 @@ typedef struct oc_file_table oc_list freeList; } oc_file_table; +ORCA_API oc_file_table* oc_file_table_get_global(); + oc_file_slot* oc_file_slot_alloc(oc_file_table* table); void oc_file_slot_recycle(oc_file_table* table, oc_file_slot* slot); oc_file oc_file_from_slot(oc_file_table* table, oc_file_slot* slot); oc_file_slot* oc_file_slot_from_handle(oc_file_table* table, oc_file handle); -ORCA_API oc_io_cmp oc_io_wait_single_req_with_table(oc_io_req* req, oc_file_table* table); +ORCA_API oc_io_cmp oc_io_wait_single_req_for_table(oc_io_req* req, oc_file_table* table); + +ORCA_API oc_file oc_file_open_with_request_for_table(oc_str8 path, oc_file_access rights, oc_file_open_flags flags, oc_file_table* table); + +ORCA_API oc_file_open_with_dialog_result oc_file_open_with_dialog_for_table(oc_arena* arena, + oc_file_access rights, + oc_file_open_flags flags, + oc_file_dialog_desc* desc, + oc_file_table* table); //----------------------------------------------------------------------- // raw io primitives diff --git a/src/platform/platform_path.c b/src/platform/platform_path.c index ca5643e..600ec04 100644 --- a/src/platform/platform_path.c +++ b/src/platform/platform_path.c @@ -54,7 +54,7 @@ oc_str8_list oc_path_split(oc_arena* arena, oc_str8 path) oc_str8 oc_path_join(oc_arena* arena, oc_str8_list elements) { //TODO: check if elements have ending/begining '/' ? - oc_str8 res = oc_str8_list_collate(arena, elements, OC_STR8("/"), OC_STR8("/"), (oc_str8){ 0 }); + oc_str8 res = oc_str8_list_collate(arena, elements, OC_STR8(""), OC_STR8("/"), (oc_str8){ 0 }); return (res); } diff --git a/src/platform/posix_io.c b/src/platform/posix_io.c index 0360bc1..e765464 100644 --- a/src/platform/posix_io.c +++ b/src/platform/posix_io.c @@ -518,7 +518,7 @@ oc_io_cmp oc_io_get_error(oc_file_slot* slot, oc_io_req* req) return (cmp); } -oc_io_cmp oc_io_wait_single_req_with_table(oc_io_req* req, oc_file_table* table) +oc_io_cmp oc_io_wait_single_req_for_table(oc_io_req* req, oc_file_table* table) { oc_io_cmp cmp = { 0 }; diff --git a/src/platform/win32_io.c b/src/platform/win32_io.c index 0c49050..3e705d2 100644 --- a/src/platform/win32_io.c +++ b/src/platform/win32_io.c @@ -73,7 +73,7 @@ oc_io_error oc_io_raw_last_error() return (error); } -static oc_str16 win32_path_from_handle_null_terminated(oc_arena* arena, HANDLE handle) +oc_str16 win32_path_from_handle_null_terminated(oc_arena* arena, HANDLE handle) { oc_str16 res = { 0 }; @@ -108,7 +108,7 @@ static oc_str16 win32_get_path_at_null_terminated(oc_arena* arena, oc_file_desc oc_arena_scope scratch = oc_scratch_begin_next(arena); oc_str16 dirPathW = win32_path_from_handle_null_terminated(scratch.arena, dirFd); - oc_str16 pathW = oc_win32_utf8_to_wide_null_terminated(scratch.arena, path); + oc_str16 pathW = oc_win32_utf8_to_wide(scratch.arena, path); if(dirPathW.len && pathW.len) { @@ -194,7 +194,7 @@ oc_file_desc oc_io_raw_open_at(oc_file_desc dirFd, oc_str8 path, oc_file_access } oc_arena_scope scratch = oc_scratch_begin(); - oc_str16 pathW = oc_win32_utf8_to_wide_null_terminated(scratch.arena, path); + oc_str16 pathW = oc_win32_utf8_to_wide(scratch.arena, path); if(dirFd == NULL || dirFd == INVALID_HANDLE_VALUE) { @@ -505,7 +505,7 @@ static oc_io_cmp oc_io_get_error(oc_file_slot* slot, oc_io_req* req) return (cmp); } -oc_io_cmp oc_io_wait_single_req_with_table(oc_io_req* req, oc_file_table* table) +oc_io_cmp oc_io_wait_single_req_for_table(oc_io_req* req, oc_file_table* table) { oc_io_cmp cmp = { 0 }; diff --git a/src/platform/win32_path.c b/src/platform/win32_path.c index de04e19..a9b8edd 100644 --- a/src/platform/win32_path.c +++ b/src/platform/win32_path.c @@ -13,7 +13,7 @@ bool oc_path_is_absolute(oc_str8 path) { oc_arena_scope scratch = oc_scratch_begin(); - oc_str16 pathW = oc_win32_utf8_to_wide_null_terminated(scratch.arena, path); + oc_str16 pathW = oc_win32_utf8_to_wide(scratch.arena, path); bool result = !PathIsRelativeW(pathW.ptr); oc_scratch_end(scratch); diff --git a/src/platform/win32_string_helpers.c b/src/platform/win32_string_helpers.c index 35a5a8a..cbd509d 100644 --- a/src/platform/win32_string_helpers.c +++ b/src/platform/win32_string_helpers.c @@ -11,7 +11,7 @@ #include "win32_string_helpers.h" -oc_str16 oc_win32_utf8_to_wide_null_terminated(oc_arena* arena, oc_str8 s) +oc_str16 oc_win32_utf8_to_wide(oc_arena* arena, oc_str8 s) { oc_str16 res = { 0 }; res.len = 1 + MultiByteToWideChar(CP_UTF8, 0, s.ptr, s.len, NULL, 0); diff --git a/src/platform/win32_string_helpers.h b/src/platform/win32_string_helpers.h index 7e00490..4aae181 100644 --- a/src/platform/win32_string_helpers.h +++ b/src/platform/win32_string_helpers.h @@ -10,7 +10,7 @@ #include "util/strings.h" -oc_str16 oc_win32_utf8_to_wide_null_terminated(oc_arena* arena, oc_str8 s); +oc_str16 oc_win32_utf8_to_wide(oc_arena* arena, oc_str8 s); oc_str8 oc_win32_wide_to_utf8(oc_arena* arena, oc_str16 s); #endif // __WIN32_STRING_HELPERS_H_ diff --git a/src/runtime.c b/src/runtime.c index 9935976..896c708 100644 --- a/src/runtime.c +++ b/src/runtime.c @@ -19,7 +19,7 @@ oc_font orca_font_create(const char* resourcePath) { - //NOTE(martin): create default font + //NOTE(martin): create default fonts oc_str8 fontPath = oc_path_executable_relative(oc_scratch(), OC_STR8(resourcePath)); FILE* fontFile = fopen(fontPath.ptr, "r"); @@ -49,11 +49,6 @@ oc_font orca_font_create(const char* resourcePath) return (font); } -oc_font oc_font_create_default() -{ - return (orca_font_create("../resources/OpenSansLatinSubset.ttf")); -} - oc_runtime __orcaApp = { 0 }; oc_runtime* oc_runtime_get() @@ -63,26 +58,69 @@ oc_runtime* oc_runtime_get() oc_runtime_env* oc_runtime_env_get() { - return (&__orcaApp.runtime); + return (&__orcaApp.env); } -void* oc_runtime_ptr_to_native(oc_runtime* orca, void* wasmPtr, u32 length) +void orca_wasm3_abort(IM3Runtime runtime, M3Result res, const char* file, const char* function, int line, const char* msg) +{ + M3ErrorInfo errInfo = { 0 }; + m3_GetErrorInfo(runtime, &errInfo); + if(errInfo.message && res == errInfo.result) + { + oc_abort_ext(file, function, line, "%s: %s (%s)", msg, res, errInfo.message); + } + else + { + oc_abort_ext(file, function, line, "%s: %s", msg, res); + } +} + +#define ORCA_WASM3_ABORT(runtime, err, msg) orca_wasm3_abort(runtime, err, __FILE__, __FUNCTION__, __LINE__, msg) + +void* oc_runtime_env_ptr_to_native(oc_runtime_env* env, void* wasmPtr, u32 length) { // We can't use the runtime's memory pointer directly because wasm3 embeds a // header at the beginning of the block we give it. u64 bufferIndex = (u64)wasmPtr & 0xffffffff; u32 memSize = 0; - char* memory = (char*)m3_GetMemory(orca->runtime.m3Runtime, &memSize, 0); + char* memory = (char*)m3_GetMemory(env->m3Runtime, &memSize, 0); if(bufferIndex + length < memSize) { char* nativePtr = memory + bufferIndex; return nativePtr; } + //TODO directly abort here? return NULL; } +void* oc_runtime_ptr_to_native(oc_runtime* orca, void* wasmPtr, u32 length) +{ + return (oc_runtime_env_ptr_to_native(&orca->env, wasmPtr, length)); +} + +void* oc_wasm_arena_push(oc_runtime_env* env, i32 arenaIndex, u64 size) +{ + void* retValues[1] = { 0 }; + const void* retPointers[1] = { &retValues[0] }; + const void* args[2] = { &arenaIndex, &size }; + + M3Result res = m3_Call(env->exports[OC_EXPORT_ARENA_PUSH], 2, args); + if(res) + { + ORCA_WASM3_ABORT(env->m3Runtime, res, "Runtime error"); + } + + res = m3_GetResults(env->exports[OC_EXPORT_ARENA_PUSH], 1, retPointers); + if(res) + { + ORCA_WASM3_ABORT(env->m3Runtime, res, "Runtime error"); + } + void* ptr = oc_runtime_env_ptr_to_native(env, retValues[0], size); + return (ptr); +} + void oc_runtime_window_set_title(oc_str8 title) { title.ptr = oc_runtime_ptr_to_native(oc_runtime_get(), title.ptr, title.len); @@ -231,8 +269,8 @@ void orca_surface_render_commands(oc_surface surface, { oc_runtime* app = &__orcaApp; - char* memBase = app->runtime.wasmMemory.ptr; - u32 memSize = app->runtime.wasmMemory.committed; + char* memBase = app->env.wasmMemory.ptr; + u32 memSize = app->env.wasmMemory.committed; if(((char*)primitives > memBase) && ((char*)primitives + primitiveCount * sizeof(oc_primitive) - memBase <= memSize) && ((char*)elements > memBase) @@ -350,27 +388,11 @@ void oc_runtime_env_init(oc_runtime_env* runtime) #include "wasmbind/io_api_bind_gen.c" #include "wasmbind/surface_api_bind_gen.c" -void orca_wasm3_abort(IM3Runtime runtime, M3Result res, const char* file, const char* function, int line, const char* msg) -{ - M3ErrorInfo errInfo = { 0 }; - m3_GetErrorInfo(runtime, &errInfo); - if(errInfo.message && res == errInfo.result) - { - oc_abort_ext(file, function, line, "%s: %s (%s)", msg, res, errInfo.message); - } - else - { - oc_abort_ext(file, function, line, "%s: %s", msg, res); - } -} - -#define ORCA_WASM3_ABORT(runtime, err, msg) orca_wasm3_abort(runtime, err, __FILE__, __FUNCTION__, __LINE__, msg) - i32 orca_runloop(void* user) { oc_runtime* app = &__orcaApp; - oc_runtime_env_init(&app->runtime); + oc_runtime_env_init(&app->env); //NOTE: loads wasm module const char* bundleNameCString = "module"; @@ -386,42 +408,42 @@ i32 orca_runloop(void* user) u64 wasmSize = ftell(file); rewind(file); - app->runtime.wasmBytecode.len = wasmSize; - app->runtime.wasmBytecode.ptr = oc_malloc_array(char, wasmSize); - fread(app->runtime.wasmBytecode.ptr, 1, app->runtime.wasmBytecode.len, file); + app->env.wasmBytecode.len = wasmSize; + app->env.wasmBytecode.ptr = oc_malloc_array(char, wasmSize); + fread(app->env.wasmBytecode.ptr, 1, app->env.wasmBytecode.len, file); fclose(file); u32 stackSize = 65536; - app->runtime.m3Env = m3_NewEnvironment(); + app->env.m3Env = m3_NewEnvironment(); - app->runtime.m3Runtime = m3_NewRuntime(app->runtime.m3Env, stackSize, NULL); + app->env.m3Runtime = m3_NewRuntime(app->env.m3Env, stackSize, NULL); //NOTE: host memory will be freed when runtime is freed. - m3_RuntimeSetMemoryCallbacks(app->runtime.m3Runtime, wasm_memory_resize_callback, wasm_memory_free_callback, &app->runtime.wasmMemory); + m3_RuntimeSetMemoryCallbacks(app->env.m3Runtime, wasm_memory_resize_callback, wasm_memory_free_callback, &app->env.wasmMemory); - M3Result res = m3_ParseModule(app->runtime.m3Env, &app->runtime.m3Module, (u8*)app->runtime.wasmBytecode.ptr, app->runtime.wasmBytecode.len); + M3Result res = m3_ParseModule(app->env.m3Env, &app->env.m3Module, (u8*)app->env.wasmBytecode.ptr, app->env.wasmBytecode.len); if(res) { - ORCA_WASM3_ABORT(app->runtime.m3Runtime, res, "The application couldn't parse its web assembly module"); + ORCA_WASM3_ABORT(app->env.m3Runtime, res, "The application couldn't parse its web assembly module"); } - res = m3_LoadModule(app->runtime.m3Runtime, app->runtime.m3Module); + res = m3_LoadModule(app->env.m3Runtime, app->env.m3Module); if(res) { - ORCA_WASM3_ABORT(app->runtime.m3Runtime, res, "The application couldn't load its web assembly module into the runtime"); + ORCA_WASM3_ABORT(app->env.m3Runtime, res, "The application couldn't load its web assembly module into the runtime"); } - m3_SetModuleName(app->runtime.m3Module, bundleNameCString); + m3_SetModuleName(app->env.m3Module, bundleNameCString); oc_arena_clear(oc_scratch()); //NOTE: bind orca APIs { int err = 0; - err |= bindgen_link_core_api(app->runtime.m3Module); - err |= bindgen_link_surface_api(app->runtime.m3Module); - err |= bindgen_link_clock_api(app->runtime.m3Module); - err |= bindgen_link_io_api(app->runtime.m3Module); - err |= bindgen_link_gles_api(app->runtime.m3Module); - err |= manual_link_gles_api(app->runtime.m3Module); + err |= bindgen_link_core_api(app->env.m3Module); + err |= bindgen_link_surface_api(app->env.m3Module); + err |= bindgen_link_clock_api(app->env.m3Module); + err |= bindgen_link_io_api(app->env.m3Module); + err |= bindgen_link_gles_api(app->env.m3Module); + err |= manual_link_gles_api(app->env.m3Module); if(err) { @@ -429,10 +451,10 @@ i32 orca_runloop(void* user) } } //NOTE: compile - res = m3_CompileModule(app->runtime.m3Module); + res = m3_CompileModule(app->env.m3Module); if(res) { - ORCA_WASM3_ABORT(app->runtime.m3Runtime, res, "The application couldn't compile its web assembly module"); + ORCA_WASM3_ABORT(app->env.m3Runtime, res, "The application couldn't compile its web assembly module"); } //NOTE: Find and type check event handlers. @@ -440,7 +462,7 @@ i32 orca_runloop(void* user) { const oc_export_desc* desc = &OC_EXPORT_DESC[i]; IM3Function handler = 0; - m3_FindFunction(&handler, app->runtime.m3Runtime, desc->name.ptr); + m3_FindFunction(&handler, app->env.m3Runtime, desc->name.ptr); if(handler) { @@ -481,7 +503,7 @@ i32 orca_runloop(void* user) if(checked) { - app->runtime.exports[i] = handler; + app->env.exports[i] = handler; } else { @@ -491,8 +513,8 @@ i32 orca_runloop(void* user) } //NOTE: get location of the raw event slot - IM3Global rawEventGlobal = m3_FindGlobal(app->runtime.m3Module, "oc_rawEvent"); - app->runtime.rawEventOffset = (u32)rawEventGlobal->intValue; + IM3Global rawEventGlobal = m3_FindGlobal(app->env.m3Module, "oc_rawEvent"); + app->env.rawEventOffset = (u32)rawEventGlobal->intValue; //NOTE: preopen the app local root dir { @@ -502,11 +524,11 @@ i32 orca_runloop(void* user) .open.rights = OC_FILE_ACCESS_READ | OC_FILE_ACCESS_WRITE, .size = localRootPath.len, .buffer = localRootPath.ptr }; - oc_io_cmp cmp = oc_io_wait_single_req_with_table(&req, &app->fileTable); + oc_io_cmp cmp = oc_io_wait_single_req_for_table(&req, &app->fileTable); app->rootDir = cmp.handle; } - IM3Function* exports = app->runtime.exports; + IM3Function* exports = app->env.exports; //NOTE: call init handler if(exports[OC_EXPORT_ON_INIT]) @@ -514,7 +536,7 @@ i32 orca_runloop(void* user) M3Result res = m3_Call(exports[OC_EXPORT_ON_INIT], 0, 0); if(res) { - ORCA_WASM3_ABORT(app->runtime.m3Runtime, res, "Runtime error"); + ORCA_WASM3_ABORT(app->env.m3Runtime, res, "Runtime error"); } } @@ -527,7 +549,7 @@ i32 orca_runloop(void* user) M3Result res = m3_Call(exports[OC_EXPORT_FRAME_RESIZE], 2, args); if(res) { - ORCA_WASM3_ABORT(app->runtime.m3Runtime, res, "Runtime error"); + ORCA_WASM3_ABORT(app->env.m3Runtime, res, "Runtime error"); } } @@ -547,14 +569,14 @@ i32 orca_runloop(void* user) if(exports[OC_EXPORT_RAW_EVENT]) { #ifndef M3_BIG_ENDIAN - oc_event* eventPtr = (oc_event*)wasm_memory_offset_to_ptr(&app->runtime.wasmMemory, app->runtime.rawEventOffset); + oc_event* eventPtr = (oc_event*)wasm_memory_offset_to_ptr(&app->env.wasmMemory, app->env.rawEventOffset); memcpy(eventPtr, event, sizeof(*event)); - const void* args[1] = { &app->runtime.rawEventOffset }; + const void* args[1] = { &app->env.rawEventOffset }; M3Result res = m3_Call(exports[OC_EXPORT_RAW_EVENT], 1, args); if(res) { - ORCA_WASM3_ABORT(app->runtime.m3Runtime, res, "Runtime error"); + ORCA_WASM3_ABORT(app->env.m3Runtime, res, "Runtime error"); } #else oc_log_error("oc_on_raw_event() is not supported on big endian platforms"); @@ -565,7 +587,7 @@ i32 orca_runloop(void* user) { case OC_EVENT_WINDOW_CLOSE: { - oc_request_quit(); + oc_request_quit(); } break; @@ -581,7 +603,7 @@ i32 orca_runloop(void* user) M3Result res = m3_Call(exports[OC_EXPORT_FRAME_RESIZE], 2, args); if(res) { - ORCA_WASM3_ABORT(app->runtime.m3Runtime, res, "Runtime error"); + ORCA_WASM3_ABORT(app->env.m3Runtime, res, "Runtime error"); } } } @@ -598,7 +620,7 @@ i32 orca_runloop(void* user) M3Result res = m3_Call(exports[OC_EXPORT_MOUSE_DOWN], 1, args); if(res) { - ORCA_WASM3_ABORT(app->runtime.m3Runtime, res, "Runtime error"); + ORCA_WASM3_ABORT(app->env.m3Runtime, res, "Runtime error"); } } } @@ -611,7 +633,7 @@ i32 orca_runloop(void* user) M3Result res = m3_Call(exports[OC_EXPORT_MOUSE_UP], 1, args); if(res) { - ORCA_WASM3_ABORT(app->runtime.m3Runtime, res, "Runtime error"); + ORCA_WASM3_ABORT(app->env.m3Runtime, res, "Runtime error"); } } } @@ -626,7 +648,7 @@ i32 orca_runloop(void* user) M3Result res = m3_Call(exports[OC_EXPORT_MOUSE_MOVE], 4, args); if(res) { - ORCA_WASM3_ABORT(app->runtime.m3Runtime, res, "Runtime error"); + ORCA_WASM3_ABORT(app->env.m3Runtime, res, "Runtime error"); } } } @@ -649,7 +671,7 @@ i32 orca_runloop(void* user) M3Result res = m3_Call(exports[OC_EXPORT_KEY_DOWN], 1, args); if(res) { - ORCA_WASM3_ABORT(app->runtime.m3Runtime, res, "Runtime error"); + ORCA_WASM3_ABORT(app->env.m3Runtime, res, "Runtime error"); } } } @@ -661,7 +683,7 @@ i32 orca_runloop(void* user) M3Result res = m3_Call(exports[OC_EXPORT_KEY_UP], 1, args); if(res) { - ORCA_WASM3_ABORT(app->runtime.m3Runtime, res, "Runtime error"); + ORCA_WASM3_ABORT(app->env.m3Runtime, res, "Runtime error"); } } } @@ -680,7 +702,7 @@ i32 orca_runloop(void* user) M3Result res = m3_Call(exports[OC_EXPORT_FRAME_REFRESH], 0, 0); if(res) { - ORCA_WASM3_ABORT(app->runtime.m3Runtime, res, "Runtime error"); + ORCA_WASM3_ABORT(app->env.m3Runtime, res, "Runtime error"); } } @@ -837,7 +859,7 @@ i32 orca_runloop(void* user) M3Result res = m3_Call(exports[OC_EXPORT_TERMINATE], 0, 0); if(res) { - ORCA_WASM3_ABORT(app->runtime.m3Runtime, res, "Runtime error"); + ORCA_WASM3_ABORT(app->env.m3Runtime, res, "Runtime error"); } } diff --git a/src/runtime.h b/src/runtime.h index 271df68..ae958e9 100644 --- a/src/runtime.h +++ b/src/runtime.h @@ -27,7 +27,8 @@ X(OC_EXPORT_FRAME_REFRESH, "oc_on_frame_refresh", "", "") \ X(OC_EXPORT_FRAME_RESIZE, "oc_on_resize", "", "ii") \ X(OC_EXPORT_RAW_EVENT, "oc_on_raw_event", "", "i") \ - X(OC_EXPORT_TERMINATE, "oc_on_terminate", "", "") + X(OC_EXPORT_TERMINATE, "oc_on_terminate", "", "") \ + X(OC_EXPORT_ARENA_PUSH, "oc_arena_push_stub", "i", "iI") typedef enum { @@ -111,13 +112,13 @@ typedef struct oc_debug_overlay typedef struct oc_runtime { - bool quit; + bool quit; oc_window window; oc_file_table fileTable; oc_file rootDir; - oc_runtime_env runtime; + oc_runtime_env env; oc_debug_overlay debugOverlay; @@ -127,5 +128,6 @@ oc_runtime* oc_runtime_get(); oc_runtime_env* oc_runtime_env_get(); void* oc_runtime_ptr_to_native(oc_runtime* runtime, void* wasmPtr, u32 length); +void* oc_wasm_arena_push(oc_runtime_env* env, i32 arenaIndex, u64 size); #endif //__RUNTIME_H_ diff --git a/src/runtime_io.c b/src/runtime_io.c index aa80007..5605e41 100644 --- a/src/runtime_io.c +++ b/src/runtime_io.c @@ -30,7 +30,7 @@ oc_io_cmp oc_runtime_io_wait_single_req(oc_io_req* wasmReq) req.open.flags |= OC_FILE_OPEN_RESTRICT; } } - cmp = oc_io_wait_single_req_with_table(&req, &orca->fileTable); + cmp = oc_io_wait_single_req_for_table(&req, &orca->fileTable); } else { @@ -39,3 +39,158 @@ oc_io_cmp oc_runtime_io_wait_single_req(oc_io_req* wasmReq) return (cmp); } + +oc_file oc_file_open_with_request_bridge(oc_str8 path, oc_file_access rights, oc_file_open_flags flags) +{ + oc_file file = oc_file_nil(); + oc_runtime* orca = oc_runtime_get(); + + path.ptr = oc_runtime_ptr_to_native(orca, path.ptr, path.len); + if(path.ptr) + { + file = oc_file_open_with_request_for_table(path, rights, flags, &orca->fileTable); + } + return (file); +} + +typedef struct oc_wasm_list +{ + u32 first; + u32 last; +} oc_wasm_list; + +typedef struct oc_wasm_list_elt +{ + u32 prev; + u32 next; +} oc_wasm_list_elt; + +typedef struct oc_wasm_str8 +{ + u64 len; + u32 ptr; +} oc_wasm_str8; + +typedef struct oc_wasm_str8_elt +{ + oc_wasm_list_elt listElt; + oc_wasm_str8 string; + +} oc_wasm_str8_elt; + +typedef struct oc_wasm_str8_list +{ + oc_wasm_list list; + u64 eltCount; + u64 len; +} oc_wasm_str8_list; + +typedef struct oc_wasm_file_dialog_desc +{ + oc_file_dialog_kind kind; + oc_file_dialog_flags flags; + oc_wasm_str8 title; + oc_wasm_str8 okLabel; + oc_file startAt; + oc_wasm_str8 startPath; + oc_wasm_str8_list filters; + +} oc_wasm_file_dialog_desc; + +typedef struct oc_wasm_file_open_with_dialog_elt +{ + oc_wasm_list_elt listElt; + oc_file file; +} oc_wasm_file_open_with_dialog_elt; + +typedef struct oc_wasm_file_open_with_dialog_result +{ + oc_file_dialog_button button; + oc_file file; + oc_wasm_list selection; + +} oc_wasm_file_open_with_dialog_result; + +oc_wasm_file_open_with_dialog_result oc_file_open_with_dialog_bridge(i32 wasmArenaIndex, + oc_file_access rights, + oc_file_open_flags flags, + oc_wasm_file_dialog_desc* desc) +{ + oc_runtime* orca = oc_runtime_get(); + oc_arena_scope scratch = oc_scratch_begin(); + + oc_file_dialog_desc nativeDesc = { + .kind = desc->kind, + .flags = desc->flags + }; + + nativeDesc.title.ptr = oc_runtime_ptr_to_native(orca, (char*)(uintptr_t)desc->title.ptr, desc->title.len); + nativeDesc.title.len = desc->title.len; + + nativeDesc.okLabel.ptr = oc_runtime_ptr_to_native(orca, (char*)(uintptr_t)desc->okLabel.ptr, desc->okLabel.len); + nativeDesc.okLabel.len = desc->okLabel.len; + + if(oc_file_is_nil(desc->startAt) && desc->startPath.len) + { + nativeDesc.startAt = orca->rootDir; + } + else + { + nativeDesc.startAt = desc->startAt; + } + nativeDesc.startPath.ptr = oc_runtime_ptr_to_native(orca, (char*)(uintptr_t)desc->startPath.ptr, desc->startPath.len); + nativeDesc.startPath.len = desc->startPath.len; + + u32 eltIndex = desc->filters.list.first; + while(eltIndex) + { + oc_wasm_str8_elt* elt = oc_runtime_ptr_to_native(orca, (char*)(uintptr_t)eltIndex, sizeof(oc_wasm_str8_elt)); + + oc_str8 filter = { 0 }; + filter.ptr = oc_runtime_ptr_to_native(orca, (char*)(uintptr_t)elt->string.ptr, elt->string.len); + filter.len = elt->string.len; + + oc_str8_list_push(scratch.arena, &nativeDesc.filters, filter); + + oc_log_info("filter: %.*s\n", (int)filter.len, filter.ptr); + + eltIndex = elt->listElt.next; + } + + oc_file_open_with_dialog_result nativeResult = oc_file_open_with_dialog_for_table(scratch.arena, rights, flags, &nativeDesc, &orca->fileTable); + + oc_wasm_file_open_with_dialog_result result = { + .button = nativeResult.button, + .file = nativeResult.file + }; + + u32 memSize; + char* wasmMemory = (char*)m3_GetMemory(orca->env.m3Runtime, &memSize, 0); + oc_wasm_file_open_with_dialog_elt* lastElt = 0; + + oc_list_for(&nativeResult.selection, elt, oc_file_open_with_dialog_elt, listElt) + { + oc_wasm_file_open_with_dialog_elt* wasmElt = oc_wasm_arena_push(&orca->env, wasmArenaIndex, sizeof(oc_wasm_file_open_with_dialog_elt)); + wasmElt->file = elt->file; + + if(result.selection.last == 0) + { + result.selection.first = ((char*)wasmElt - wasmMemory); + result.selection.last = result.selection.first; + wasmElt->listElt.prev = 0; + wasmElt->listElt.next = 0; + } + else + { + wasmElt->listElt.prev = result.selection.last; + wasmElt->listElt.next = 0; + lastElt->listElt.next = ((char*)wasmElt - wasmMemory); + + result.selection.last = ((char*)wasmElt - wasmMemory); + } + lastElt = wasmElt; + } + + oc_scratch_end(scratch); + return (result); +} diff --git a/src/util/lists.h b/src/util/lists.h index c09757b..81dc398 100644 --- a/src/util/lists.h +++ b/src/util/lists.h @@ -14,13 +14,12 @@ #include "util/debug.h" #ifdef __cplusplus -extern "C" -{ +extern "C" { #endif - //------------------------------------------------------------------------- - // Intrusive linked lists - //------------------------------------------------------------------------- +//------------------------------------------------------------------------- +// Intrusive linked lists +//------------------------------------------------------------------------- #define oc_list_entry(ptr, type, member) \ oc_container_of(ptr, type, member) @@ -62,162 +61,162 @@ extern "C" #define oc_list_pop_entry(list, type, member) (oc_list_empty(list) ? 0 : oc_list_entry(oc_list_pop(list), type, member)) - typedef struct oc_list_elt oc_list_elt; +typedef struct oc_list_elt oc_list_elt; - struct oc_list_elt - { - oc_list_elt* next; - oc_list_elt* prev; - }; +struct oc_list_elt +{ + oc_list_elt* prev; + oc_list_elt* next; +}; - typedef struct oc_list - { - oc_list_elt* first; - oc_list_elt* last; - } oc_list; +typedef struct oc_list +{ + oc_list_elt* first; + oc_list_elt* last; +} oc_list; - static inline void oc_list_init(oc_list* list) +static inline void oc_list_init(oc_list* list) +{ + list->first = list->last = 0; +} + +static inline oc_list_elt* oc_list_begin(oc_list* list) +{ + return (list->first); +} + +static inline oc_list_elt* oc_list_end(oc_list* list) +{ + return (0); +} + +static inline oc_list_elt* oc_list_last(oc_list* list) +{ + return (list->last); +} + +static inline void oc_list_insert(oc_list* list, oc_list_elt* afterElt, oc_list_elt* elt) +{ + elt->prev = afterElt; + elt->next = afterElt->next; + if(afterElt->next) { - list->first = list->last = 0; + afterElt->next->prev = elt; } - - static inline oc_list_elt* oc_list_begin(oc_list* list) + else { - return (list->first); + list->last = elt; } + afterElt->next = elt; - static inline oc_list_elt* oc_list_end(oc_list* list) + OC_DEBUG_ASSERT(elt->next != elt, "oc_list_insert(): can't insert an element into itself"); +} + +static inline void oc_list_insert_before(oc_list* list, oc_list_elt* beforeElt, oc_list_elt* elt) +{ + elt->next = beforeElt; + elt->prev = beforeElt->prev; + + if(beforeElt->prev) + { + beforeElt->prev->next = elt; + } + else + { + list->first = elt; + } + beforeElt->prev = elt; + + OC_DEBUG_ASSERT(elt->next != elt, "oc_list_insert_before(): can't insert an element into itself"); +} + +static inline void oc_list_remove(oc_list* list, oc_list_elt* elt) +{ + if(elt->prev) + { + elt->prev->next = elt->next; + } + else + { + OC_DEBUG_ASSERT(list->first == elt); + list->first = elt->next; + } + if(elt->next) + { + elt->next->prev = elt->prev; + } + else + { + OC_DEBUG_ASSERT(list->last == elt); + list->last = elt->prev; + } + elt->prev = elt->next = 0; +} + +static inline void oc_list_push(oc_list* list, oc_list_elt* elt) +{ + elt->next = list->first; + elt->prev = 0; + if(list->first) + { + list->first->prev = elt; + } + else + { + list->last = elt; + } + list->first = elt; +} + +static inline oc_list_elt* oc_list_pop(oc_list* list) +{ + oc_list_elt* elt = oc_list_begin(list); + if(elt != oc_list_end(list)) + { + oc_list_remove(list, elt); + return (elt); + } + else { return (0); } +} - static inline oc_list_elt* oc_list_last(oc_list* list) +static inline void oc_list_push_back(oc_list* list, oc_list_elt* elt) +{ + elt->prev = list->last; + elt->next = 0; + if(list->last) { - return (list->last); + list->last->next = elt; } - - static inline void oc_list_insert(oc_list* list, oc_list_elt* afterElt, oc_list_elt* elt) + else { - elt->prev = afterElt; - elt->next = afterElt->next; - if(afterElt->next) - { - afterElt->next->prev = elt; - } - else - { - list->last = elt; - } - afterElt->next = elt; - - OC_DEBUG_ASSERT(elt->next != elt, "oc_list_insert(): can't insert an element into itself"); - } - - static inline void oc_list_insert_before(oc_list* list, oc_list_elt* beforeElt, oc_list_elt* elt) - { - elt->next = beforeElt; - elt->prev = beforeElt->prev; - - if(beforeElt->prev) - { - beforeElt->prev->next = elt; - } - else - { - list->first = elt; - } - beforeElt->prev = elt; - - OC_DEBUG_ASSERT(elt->next != elt, "oc_list_insert_before(): can't insert an element into itself"); - } - - static inline void oc_list_remove(oc_list* list, oc_list_elt* elt) - { - if(elt->prev) - { - elt->prev->next = elt->next; - } - else - { - OC_DEBUG_ASSERT(list->first == elt); - list->first = elt->next; - } - if(elt->next) - { - elt->next->prev = elt->prev; - } - else - { - OC_DEBUG_ASSERT(list->last == elt); - list->last = elt->prev; - } - elt->prev = elt->next = 0; - } - - static inline void oc_list_push(oc_list* list, oc_list_elt* elt) - { - elt->next = list->first; - elt->prev = 0; - if(list->first) - { - list->first->prev = elt; - } - else - { - list->last = elt; - } list->first = elt; } - - static inline oc_list_elt* oc_list_pop(oc_list* list) - { - oc_list_elt* elt = oc_list_begin(list); - if(elt != oc_list_end(list)) - { - oc_list_remove(list, elt); - return (elt); - } - else - { - return (0); - } - } - - static inline void oc_list_push_back(oc_list* list, oc_list_elt* elt) - { - elt->prev = list->last; - elt->next = 0; - if(list->last) - { - list->last->next = elt; - } - else - { - list->first = elt; - } - list->last = elt; - } + list->last = elt; +} #define oc_list_append(a, b) oc_list_push_back(a, b) - static inline oc_list_elt* oc_list_pop_back(oc_list* list) +static inline oc_list_elt* oc_list_pop_back(oc_list* list) +{ + oc_list_elt* elt = oc_list_last(list); + if(elt != oc_list_end(list)) { - oc_list_elt* elt = oc_list_last(list); - if(elt != oc_list_end(list)) - { - oc_list_remove(list, elt); - return (elt); - } - else - { - return (0); - } + oc_list_remove(list, elt); + return (elt); } + else + { + return (0); + } +} - static inline bool oc_list_empty(oc_list* list) - { - return (list->first == 0 || list->last == 0); - } +static inline bool oc_list_empty(oc_list* list) +{ + return (list->first == 0 || list->last == 0); +} #ifdef __cplusplus } // extern "C" diff --git a/src/util/memory.c b/src/util/memory.c index 445d70b..da9fa3d 100644 --- a/src/util/memory.c +++ b/src/util/memory.c @@ -241,7 +241,7 @@ ORCA_API oc_arena* oc_scratch_next(oc_arena* used) return (res); } -ORCA_API oc_arena_scope oc_scratch_begin() +ORCA_API oc_arena_scope oc_scratch_begin(void) { oc_arena* scratch = oc_scratch(); oc_arena_scope scope = oc_arena_scope_begin(scratch); diff --git a/src/util/memory.h b/src/util/memory.h index a042203..fd2c81a 100644 --- a/src/util/memory.h +++ b/src/util/memory.h @@ -14,94 +14,93 @@ #include "util/typedefs.h" #ifdef __cplusplus -extern "C" -{ +extern "C" { #endif - //-------------------------------------------------------------------------------- - //NOTE(martin): memory arena - //-------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------- +//NOTE(martin): memory arena +//-------------------------------------------------------------------------------- - typedef struct oc_arena_chunk - { - oc_list_elt listElt; - char* ptr; - u64 offset; - u64 committed; - u64 cap; - } oc_arena_chunk; +typedef struct oc_arena_chunk +{ + oc_list_elt listElt; + char* ptr; + u64 offset; + u64 committed; + u64 cap; +} oc_arena_chunk; - typedef struct oc_arena - { - oc_base_allocator* base; - oc_list chunks; - oc_arena_chunk* currentChunk; +typedef struct oc_arena +{ + oc_base_allocator* base; + oc_list chunks; + oc_arena_chunk* currentChunk; - } oc_arena; +} oc_arena; - typedef struct oc_arena_scope - { - oc_arena* arena; - oc_arena_chunk* chunk; - u64 offset; - } oc_arena_scope; +typedef struct oc_arena_scope +{ + oc_arena* arena; + oc_arena_chunk* chunk; + u64 offset; +} oc_arena_scope; - typedef struct oc_arena_options - { - oc_base_allocator* base; - u64 reserve; - } oc_arena_options; +typedef struct oc_arena_options +{ + oc_base_allocator* base; + u64 reserve; +} oc_arena_options; - ORCA_API void oc_arena_init(oc_arena* arena); - ORCA_API void oc_arena_init_with_options(oc_arena* arena, oc_arena_options* options); - ORCA_API void oc_arena_cleanup(oc_arena* arena); +ORCA_API void oc_arena_init(oc_arena* arena); +ORCA_API void oc_arena_init_with_options(oc_arena* arena, oc_arena_options* options); +ORCA_API void oc_arena_cleanup(oc_arena* arena); - ORCA_API void* oc_arena_push(oc_arena* arena, u64 size); - ORCA_API void oc_arena_clear(oc_arena* arena); +ORCA_API void* oc_arena_push(oc_arena* arena, u64 size); +ORCA_API void oc_arena_clear(oc_arena* arena); - ORCA_API oc_arena_scope oc_arena_scope_begin(oc_arena* arena); - ORCA_API void oc_arena_scope_end(oc_arena_scope scope); +ORCA_API oc_arena_scope oc_arena_scope_begin(oc_arena* arena); +ORCA_API void oc_arena_scope_end(oc_arena_scope scope); #define oc_arena_push_type(arena, type) ((type*)oc_arena_push(arena, sizeof(type))) #define oc_arena_push_array(arena, type, count) ((type*)oc_arena_push(arena, sizeof(type) * (count))) - //-------------------------------------------------------------------------------- - //NOTE(martin): memory pool - //-------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------- +//NOTE(martin): memory pool +//-------------------------------------------------------------------------------- - //TODO: we could probably remove pool. Most of the time we construct pool on top of - // arenas "manually" with different free lists per object types... +//TODO: we could probably remove pool. Most of the time we construct pool on top of +// arenas "manually" with different free lists per object types... - typedef struct oc_pool - { - oc_arena arena; - oc_list freeList; - u64 blockSize; - } oc_pool; +typedef struct oc_pool +{ + oc_arena arena; + oc_list freeList; + u64 blockSize; +} oc_pool; - typedef struct oc_pool_options - { - oc_base_allocator* base; - u64 reserve; - } oc_pool_options; +typedef struct oc_pool_options +{ + oc_base_allocator* base; + u64 reserve; +} oc_pool_options; - ORCA_API void oc_pool_init(oc_pool* pool, u64 blockSize); - ORCA_API void oc_pool_init_with_options(oc_pool* pool, u64 blockSize, oc_pool_options* options); - ORCA_API void oc_pool_cleanup(oc_pool* pool); +ORCA_API void oc_pool_init(oc_pool* pool, u64 blockSize); +ORCA_API void oc_pool_init_with_options(oc_pool* pool, u64 blockSize, oc_pool_options* options); +ORCA_API void oc_pool_cleanup(oc_pool* pool); - ORCA_API void* oc_pool_alloc(oc_pool* pool); - ORCA_API void oc_pool_recycle(oc_pool* pool, void* ptr); - ORCA_API void oc_pool_clear(oc_pool* pool); +ORCA_API void* oc_pool_alloc(oc_pool* pool); +ORCA_API void oc_pool_recycle(oc_pool* pool, void* ptr); +ORCA_API void oc_pool_clear(oc_pool* pool); #define oc_pool_alloc_type(arena, type) ((type*)oc_pool_alloc(arena)) - //-------------------------------------------------------------------------------- - //NOTE(martin): per-thread implicit scratch arena - //-------------------------------------------------------------------------------- - ORCA_API oc_arena* oc_scratch(); - ORCA_API oc_arena* oc_scratch_next(oc_arena* used); - ORCA_API oc_arena_scope oc_scratch_begin(); - ORCA_API oc_arena_scope oc_scratch_begin_next(oc_arena* used); +//-------------------------------------------------------------------------------- +//NOTE(martin): per-thread implicit scratch arena +//-------------------------------------------------------------------------------- +ORCA_API oc_arena* oc_scratch(); +ORCA_API oc_arena* oc_scratch_next(oc_arena* used); +ORCA_API oc_arena_scope oc_scratch_begin(void); +ORCA_API oc_arena_scope oc_scratch_begin_next(oc_arena* used); #define oc_scratch_end(scope) oc_arena_scope_end(scope) diff --git a/src/util/strings.h b/src/util/strings.h index 4f574be..8ebc37e 100644 --- a/src/util/strings.h +++ b/src/util/strings.h @@ -17,11 +17,10 @@ #include #ifdef __cplusplus -extern "C" -{ +extern "C" { #endif - /*NOTE: +/*NOTE: By convention, functions that take an arena and return a string slice allocated on this arena, always allocate one more element and null-terminate the string. This is done so we can pass those strings directly to C APIs that requires C strings without @@ -31,14 +30,14 @@ extern "C" into the original string. Only the _list nodes_ are allocated on the arena. */ - //---------------------------------------------------------------------------------- - // u8 strings - //---------------------------------------------------------------------------------- - typedef struct oc_str8 - { - u64 len; - char* ptr; - } oc_str8; +//---------------------------------------------------------------------------------- +// u8 strings +//---------------------------------------------------------------------------------- +typedef struct oc_str8 +{ + u64 len; + char* ptr; +} oc_str8; #define OC_STR8(s) ((oc_str8){ .len = (s) ? strlen(s) : 0, .ptr = (char*)s }) @@ -51,109 +50,109 @@ extern "C" #define oc_str8_lp(s) ((s).len), ((s).ptr) #define oc_str8_ip(s) (int)oc_str8_lp(s) - ORCA_API oc_str8 oc_str8_from_buffer(u64 len, char* buffer); - ORCA_API oc_str8 oc_str8_slice(oc_str8 s, u64 start, u64 end); +ORCA_API oc_str8 oc_str8_from_buffer(u64 len, char* buffer); +ORCA_API oc_str8 oc_str8_slice(oc_str8 s, u64 start, u64 end); - ORCA_API oc_str8 oc_str8_push_buffer(oc_arena* arena, u64 len, char* buffer); - ORCA_API oc_str8 oc_str8_push_cstring(oc_arena* arena, const char* str); - ORCA_API oc_str8 oc_str8_push_copy(oc_arena* arena, oc_str8 s); - ORCA_API oc_str8 oc_str8_push_slice(oc_arena* arena, oc_str8 s, u64 start, u64 end); +ORCA_API oc_str8 oc_str8_push_buffer(oc_arena* arena, u64 len, char* buffer); +ORCA_API oc_str8 oc_str8_push_cstring(oc_arena* arena, const char* str); +ORCA_API oc_str8 oc_str8_push_copy(oc_arena* arena, oc_str8 s); +ORCA_API oc_str8 oc_str8_push_slice(oc_arena* arena, oc_str8 s, u64 start, u64 end); - ORCA_API oc_str8 oc_str8_pushfv(oc_arena* arena, const char* format, va_list args); - ORCA_API oc_str8 oc_str8_pushf(oc_arena* arena, const char* format, ...); +ORCA_API oc_str8 oc_str8_pushfv(oc_arena* arena, const char* format, va_list args); +ORCA_API oc_str8 oc_str8_pushf(oc_arena* arena, const char* format, ...); - ORCA_API int oc_str8_cmp(oc_str8 s1, oc_str8 s2); +ORCA_API int oc_str8_cmp(oc_str8 s1, oc_str8 s2); - ORCA_API char* oc_str8_to_cstring(oc_arena* arena, oc_str8 string); +ORCA_API char* oc_str8_to_cstring(oc_arena* arena, oc_str8 string); - //---------------------------------------------------------------------------------- - // string lists - //---------------------------------------------------------------------------------- - typedef struct oc_str8_elt - { - oc_list_elt listElt; - oc_str8 string; - } oc_str8_elt; +//---------------------------------------------------------------------------------- +// string lists +//---------------------------------------------------------------------------------- +typedef struct oc_str8_elt +{ + oc_list_elt listElt; + oc_str8 string; +} oc_str8_elt; - typedef struct oc_str8_list - { - oc_list list; - u64 eltCount; - u64 len; - } oc_str8_list; +typedef struct oc_str8_list +{ + oc_list list; + u64 eltCount; + u64 len; +} oc_str8_list; - ORCA_API void oc_str8_list_push(oc_arena* arena, oc_str8_list* list, oc_str8 str); - ORCA_API void oc_str8_list_pushf(oc_arena* arena, oc_str8_list* list, const char* format, ...); +ORCA_API void oc_str8_list_push(oc_arena* arena, oc_str8_list* list, oc_str8 str); +ORCA_API void oc_str8_list_pushf(oc_arena* arena, oc_str8_list* list, const char* format, ...); - ORCA_API oc_str8 oc_str8_list_collate(oc_arena* arena, oc_str8_list list, oc_str8 prefix, oc_str8 separator, oc_str8 postfix); - ORCA_API oc_str8 oc_str8_list_join(oc_arena* arena, oc_str8_list list); - ORCA_API oc_str8_list oc_str8_split(oc_arena* arena, oc_str8 str, oc_str8_list separators); +ORCA_API oc_str8 oc_str8_list_collate(oc_arena* arena, oc_str8_list list, oc_str8 prefix, oc_str8 separator, oc_str8 postfix); +ORCA_API oc_str8 oc_str8_list_join(oc_arena* arena, oc_str8_list list); +ORCA_API oc_str8_list oc_str8_split(oc_arena* arena, oc_str8 str, oc_str8_list separators); - //---------------------------------------------------------------------------------- - // u16 strings - //---------------------------------------------------------------------------------- - typedef struct oc_str16 - { - u64 len; - u16* ptr; - } oc_str16; +//---------------------------------------------------------------------------------- +// u16 strings +//---------------------------------------------------------------------------------- +typedef struct oc_str16 +{ + u64 len; + u16* ptr; +} oc_str16; - ORCA_API oc_str16 oc_str16_from_buffer(u64 len, u16* buffer); - ORCA_API oc_str16 oc_str16_slice(oc_str16 s, u64 start, u64 end); +ORCA_API oc_str16 oc_str16_from_buffer(u64 len, u16* buffer); +ORCA_API oc_str16 oc_str16_slice(oc_str16 s, u64 start, u64 end); - ORCA_API oc_str16 oc_str16_push_buffer(oc_arena* arena, u64 len, u16* buffer); - ORCA_API oc_str16 oc_str16_push_copy(oc_arena* arena, oc_str16 s); - ORCA_API oc_str16 oc_str16_push_slice(oc_arena* arena, oc_str16 s, u64 start, u64 end); +ORCA_API oc_str16 oc_str16_push_buffer(oc_arena* arena, u64 len, u16* buffer); +ORCA_API oc_str16 oc_str16_push_copy(oc_arena* arena, oc_str16 s); +ORCA_API oc_str16 oc_str16_push_slice(oc_arena* arena, oc_str16 s, u64 start, u64 end); - typedef struct oc_str16_elt - { - oc_list_elt listElt; - oc_str16 string; - } oc_str16_elt; +typedef struct oc_str16_elt +{ + oc_list_elt listElt; + oc_str16 string; +} oc_str16_elt; - typedef struct oc_str16_list - { - oc_list list; - u64 eltCount; - u64 len; - } oc_str16_list; +typedef struct oc_str16_list +{ + oc_list list; + u64 eltCount; + u64 len; +} oc_str16_list; - ORCA_API void oc_str16_list_push(oc_arena* arena, oc_str16_list* list, oc_str16 str); - ORCA_API oc_str16 oc_str16_list_join(oc_arena* arena, oc_str16_list list); - ORCA_API oc_str16_list oc_str16_split(oc_arena* arena, oc_str16 str, oc_str16_list separators); +ORCA_API void oc_str16_list_push(oc_arena* arena, oc_str16_list* list, oc_str16 str); +ORCA_API oc_str16 oc_str16_list_join(oc_arena* arena, oc_str16_list list); +ORCA_API oc_str16_list oc_str16_split(oc_arena* arena, oc_str16 str, oc_str16_list separators); - //---------------------------------------------------------------------------------- - // u32 strings - //---------------------------------------------------------------------------------- - typedef struct oc_str32 - { - u64 len; - u32* ptr; - } oc_str32; +//---------------------------------------------------------------------------------- +// u32 strings +//---------------------------------------------------------------------------------- +typedef struct oc_str32 +{ + u64 len; + u32* ptr; +} oc_str32; - ORCA_API oc_str32 oc_str32_from_buffer(u64 len, u32* buffer); - ORCA_API oc_str32 oc_str32_slice(oc_str32 s, u64 start, u64 end); +ORCA_API oc_str32 oc_str32_from_buffer(u64 len, u32* buffer); +ORCA_API oc_str32 oc_str32_slice(oc_str32 s, u64 start, u64 end); - ORCA_API oc_str32 oc_str32_push_buffer(oc_arena* arena, u64 len, u32* buffer); - ORCA_API oc_str32 oc_str32_push_copy(oc_arena* arena, oc_str32 s); - ORCA_API oc_str32 oc_str32_push_slice(oc_arena* arena, oc_str32 s, u64 start, u64 end); +ORCA_API oc_str32 oc_str32_push_buffer(oc_arena* arena, u64 len, u32* buffer); +ORCA_API oc_str32 oc_str32_push_copy(oc_arena* arena, oc_str32 s); +ORCA_API oc_str32 oc_str32_push_slice(oc_arena* arena, oc_str32 s, u64 start, u64 end); - typedef struct oc_str32_elt - { - oc_list_elt listElt; - oc_str32 string; - } oc_str32_elt; +typedef struct oc_str32_elt +{ + oc_list_elt listElt; + oc_str32 string; +} oc_str32_elt; - typedef struct oc_str32_list - { - oc_list list; - u64 eltCount; - u64 len; - } oc_str32_list; +typedef struct oc_str32_list +{ + oc_list list; + u64 eltCount; + u64 len; +} oc_str32_list; - ORCA_API void oc_str32_list_push(oc_arena* arena, oc_str32_list* list, oc_str32 str); - ORCA_API oc_str32 oc_str32_list_join(oc_arena* arena, oc_str32_list list); - ORCA_API oc_str32_list oc_str32_split(oc_arena* arena, oc_str32 str, oc_str32_list separators); +ORCA_API void oc_str32_list_push(oc_arena* arena, oc_str32_list* list, oc_str32 str); +ORCA_API oc_str32 oc_str32_list_join(oc_arena* arena, oc_str32_list list); +ORCA_API oc_str32_list oc_str32_split(oc_arena* arena, oc_str32 str, oc_str32_list separators); #ifdef __cplusplus } // extern "C" diff --git a/src/wasmbind/io_api.json b/src/wasmbind/io_api.json index ecb5c14..b164b37 100644 --- a/src/wasmbind/io_api.json +++ b/src/wasmbind/io_api.json @@ -5,5 +5,33 @@ "ret": {"name": "oc_io_cmp", "tag": "S"}, "args": [ {"name": "req", "type": {"name": "oc_io_req*", "tag": "p"}}] +}, +{ + "name": "oc_file_open_with_request", + "cname": "oc_file_open_with_request_bridge", + "ret": {"name": "oc_file", "tag": "S"}, + "args": [ + {"name": "path", + "type": {"name": "oc_str8", "tag": "S"}}, + {"name": "rights", + "type": {"name": "oc_file_access", "tag": "i"}}, + {"name": "flags", + "type": {"name": "oc_file_open_flags", "tag": "i"}} + ] +}, +{ + "name": "oc_file_open_with_dialog", + "cname": "oc_file_open_with_dialog_bridge", + "ret": {"name": "oc_file_open_with_dialog_result", "cname": "oc_wasm_file_open_with_dialog_result", "tag": "S"}, + "args": [ + {"name": "arena", + "type": {"name": "oc_arena*", "cname": "i32", "tag": "i"}}, + {"name": "rights", + "type": {"name": "oc_file_access", "tag": "i"}}, + {"name": "flags", + "type": {"name": "oc_file_open_flags", "tag": "i"}}, + {"name": "desc", + "type": {"name": "oc_file_dialog_desc*", "cname": "oc_wasm_file_dialog_desc*", "tag": "p"}} + ] } ] diff --git a/tests/file_dialog/.gitignore b/tests/file_dialog/.gitignore new file mode 100644 index 0000000..c5e82d7 --- /dev/null +++ b/tests/file_dialog/.gitignore @@ -0,0 +1 @@ +bin \ No newline at end of file diff --git a/tests/file_dialog/build.bat b/tests/file_dialog/build.bat new file mode 100644 index 0000000..4ea5871 --- /dev/null +++ b/tests/file_dialog/build.bat @@ -0,0 +1,7 @@ + +set INCLUDES=/I ..\..\src /I ..\..\ext + +mkdir bin + +cl /we4013 /Zi /Zc:preprocessor /std:c11 /experimental:c11atomics %INCLUDES% main.c /link /LIBPATH:../../build/bin orca.dll.lib /out:bin/test_file_dialog.exe +copy ..\..\build\bin\orca.dll bin diff --git a/tests/file_dialog/build.sh b/tests/file_dialog/build.sh new file mode 100755 index 0000000..8123429 --- /dev/null +++ b/tests/file_dialog/build.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +LIBDIR=../../build/bin +SRCDIR=../../src + +INCLUDES="-I$SRCDIR" +LIBS="-L$LIBDIR -lorca" +FLAGS="-mmacos-version-min=10.15.4 -DOC_DEBUG -DLOG_COMPILE_DEBUG" + +if [ ! \( -e bin \) ] ; then + mkdir ./bin +fi + +clang -g $FLAGS $LIBS $INCLUDES -o ./bin/test_file_dialog main.c + +cp $LIBDIR/liborca.dylib ./bin/ + +install_name_tool -add_rpath "@executable_path" ./bin/test_file_dialog diff --git a/tests/file_dialog/main.c b/tests/file_dialog/main.c new file mode 100644 index 0000000..e5d3378 --- /dev/null +++ b/tests/file_dialog/main.c @@ -0,0 +1,45 @@ +/************************************************************/ /** +* +* @file: main.c +* @author: Martin Fouilleul +* @date: 26/05/2023 +* +*****************************************************************/ + +#include "orca.h" + +int main(int argc, char** argv) +{ + oc_init(); + + oc_str8 path = oc_path_executable_relative(oc_scratch(), OC_STR8("../")); + oc_file dir = oc_file_open(path, OC_FILE_ACCESS_READ, 0); + + oc_file_dialog_desc desc = { + .kind = OC_FILE_DIALOG_OPEN, + .flags = OC_FILE_DIALOG_FILES | OC_FILE_DIALOG_MULTIPLE, + .title = OC_STR8("Select Files"), + .okLabel = OC_STR8("Select"), + .startAt = dir, + .startPath = OC_STR8(".."), + }; + + oc_str8_list_push(oc_scratch(), &desc.filters, OC_STR8("txt")); + + oc_file_dialog_result res = oc_file_dialog(oc_scratch(), &desc); + + if(res.button == OC_FILE_DIALOG_CANCEL) + { + oc_log_error("Cancel\n"); + } + else + { + oc_log_info("Selected files:\n"); + oc_list_for(&res.selection.list, elt, oc_str8_elt, listElt) + { + oc_log_info("\t%.*s\n", (int)elt->string.len, elt->string.ptr); + } + } + + return (0); +} diff --git a/tests/file_dialog/test.txt b/tests/file_dialog/test.txt new file mode 100644 index 0000000..ff3bb63 --- /dev/null +++ b/tests/file_dialog/test.txt @@ -0,0 +1 @@ +The quick brown fox jumps over the lazy dog \ No newline at end of file diff --git a/tests/file_dialog/test2.txt b/tests/file_dialog/test2.txt new file mode 100644 index 0000000..4f006a8 --- /dev/null +++ b/tests/file_dialog/test2.txt @@ -0,0 +1 @@ +Lorem ipsum dolor sit amet, consectetur adipiscing elit. diff --git a/tests/file_open_dialog/.gitignore b/tests/file_open_dialog/.gitignore new file mode 100644 index 0000000..c5e82d7 --- /dev/null +++ b/tests/file_open_dialog/.gitignore @@ -0,0 +1 @@ +bin \ No newline at end of file diff --git a/tests/file_open_dialog/build.bat b/tests/file_open_dialog/build.bat new file mode 100644 index 0000000..72b70b5 --- /dev/null +++ b/tests/file_open_dialog/build.bat @@ -0,0 +1,5 @@ + +set INCLUDES=/I ..\..\src /I ..\..\src\util /I ..\..\src\platform /I ../../ext + +cl /we4013 /Zi /Zc:preprocessor /std:c11 %INCLUDES% main.c /link /LIBPATH:../../bin milepost.dll.lib /out:../../bin/test_open_dialog.exe +copy ..\..\build\bin\orca.dll bin diff --git a/tests/file_open_dialog/build.sh b/tests/file_open_dialog/build.sh new file mode 100755 index 0000000..8f5e240 --- /dev/null +++ b/tests/file_open_dialog/build.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +LIBDIR=../../build/bin +SRCDIR=../../src + +INCLUDES="-I$SRCDIR" +LIBS="-L$LIBDIR -lorca" +FLAGS="-mmacos-version-min=10.15.4 -DOC_DEBUG -DLOG_COMPILE_DEBUG" + +if [ ! \( -e bin \) ] ; then + mkdir ./bin +fi + +clang -g $FLAGS $LIBS $INCLUDES -o ./bin/test_open_dialog main.c + +cp $LIBDIR/liborca.dylib ./bin/ + +install_name_tool -add_rpath "@executable_path" ./bin/test_open_dialog diff --git a/tests/file_open_dialog/main.c b/tests/file_open_dialog/main.c new file mode 100644 index 0000000..7d2b32c --- /dev/null +++ b/tests/file_open_dialog/main.c @@ -0,0 +1,37 @@ +/************************************************************/ /** +* +* @file: main.c +* @author: Martin Fouilleul +* @date: 26/05/2023 +* +*****************************************************************/ + +#include "orca.h" + +int main(int argc, char** argv) +{ + oc_init(); + + oc_file_dialog_desc desc = { + .kind = OC_FILE_DIALOG_OPEN, + .flags = OC_FILE_DIALOG_FILES, + .title = OC_STR8("Select Files"), + .okLabel = OC_STR8("Select") + }; + + oc_str8_list_push(oc_scratch(), &desc.filters, OC_STR8("txt")); + + oc_file file = oc_file_open_with_dialog(OC_FILE_ACCESS_READ, 0, &desc); + if(oc_file_is_nil(file)) + { + oc_log_error("Couldn't open file\n"); + } + else + { + char buffer[1024]; + u64 len = oc_file_read(file, 1024, buffer); + oc_log_info("file contents: %.*s\n", (int)len, buffer); + } + + return (0); +} diff --git a/tests/file_open_dialog/test.txt b/tests/file_open_dialog/test.txt new file mode 100644 index 0000000..ff3bb63 --- /dev/null +++ b/tests/file_open_dialog/test.txt @@ -0,0 +1 @@ +The quick brown fox jumps over the lazy dog \ No newline at end of file diff --git a/tests/file_open_request/.gitignore b/tests/file_open_request/.gitignore new file mode 100644 index 0000000..c5e82d7 --- /dev/null +++ b/tests/file_open_request/.gitignore @@ -0,0 +1 @@ +bin \ No newline at end of file diff --git a/tests/file_open_request/build.bat b/tests/file_open_request/build.bat new file mode 100644 index 0000000..e771f65 --- /dev/null +++ b/tests/file_open_request/build.bat @@ -0,0 +1,5 @@ + +set INCLUDES=/I ..\..\src /I ..\..\src\util /I ..\..\src\platform /I ../../ext + +cl /we4013 /Zi /Zc:preprocessor /std:c11 %INCLUDES% main.c /link /LIBPATH:../../bin milepost.dll.lib /out:../../bin/test_open_request.exe +copy ..\..\build\bin\orca.dll bin diff --git a/tests/file_open_request/build.sh b/tests/file_open_request/build.sh new file mode 100755 index 0000000..b63795d --- /dev/null +++ b/tests/file_open_request/build.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +LIBDIR=../../build/bin +SRCDIR=../../src + +INCLUDES="-I$SRCDIR" +LIBS="-L$LIBDIR -lorca" +FLAGS="-mmacos-version-min=10.15.4 -DOC_DEBUG -DLOG_COMPILE_DEBUG" + +if [ ! \( -e bin \) ] ; then + mkdir ./bin +fi + +clang -g $FLAGS $LIBS $INCLUDES -o ./bin/test_open_request main.c + +cp $LIBDIR/liborca.dylib ./bin/ + +install_name_tool -add_rpath "@executable_path" ./bin/test_open_request diff --git a/tests/file_open_request/main.c b/tests/file_open_request/main.c new file mode 100644 index 0000000..02bcd25 --- /dev/null +++ b/tests/file_open_request/main.c @@ -0,0 +1,28 @@ +/************************************************************/ /** +* +* @file: main.c +* @author: Martin Fouilleul +* @date: 26/05/2023 +* +*****************************************************************/ + +#include "orca.h" + +int main(int argc, char** argv) +{ + oc_init(); + + oc_file file = oc_file_open_with_request(OC_STR8("./test.txt"), OC_FILE_ACCESS_READ, 0); + if(oc_file_is_nil(file)) + { + oc_log_error("Couldn't open file\n"); + } + else + { + char buffer[1024]; + u64 len = oc_file_read(file, 1024, buffer); + oc_log_info("file contents: %.*s\n", (int)len, buffer); + } + + return (0); +} diff --git a/tests/file_open_request/test.txt b/tests/file_open_request/test.txt new file mode 100644 index 0000000..ff3bb63 --- /dev/null +++ b/tests/file_open_request/test.txt @@ -0,0 +1 @@ +The quick brown fox jumps over the lazy dog \ No newline at end of file