- Added oc_file_open_with_request() to request new file capability for a given path. This presents user with a message box allowing them to accept or deny the request.
- Added oc_file_open_with_dialog() to ask user to grant new file capabilities through an open/save dialog.
This commit is contained in:
parent
0db1589dc6
commit
0205d90941
|
@ -215,3 +215,12 @@ void oc_window_set_frame_size(oc_window window, oc_vec2 size)
|
||||||
frame.h = size.y;
|
frame.h = size.y;
|
||||||
oc_window_set_frame_rect(window, frame);
|
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()));
|
||||||
|
}
|
||||||
|
|
|
@ -389,6 +389,60 @@ ORCA_API oc_str8 oc_save_dialog(oc_arena* arena,
|
||||||
oc_str8 defaultPath,
|
oc_str8 defaultPath,
|
||||||
oc_str8_list filters);
|
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,
|
ORCA_API int oc_alert_popup(oc_str8 title,
|
||||||
oc_str8 message,
|
oc_str8 message,
|
||||||
oc_str8_list options);
|
oc_str8_list options);
|
||||||
|
@ -404,9 +458,7 @@ ORCA_API int oc_directory_create(oc_str8 path);
|
||||||
#else
|
#else
|
||||||
|
|
||||||
void ORCA_IMPORT(oc_request_quit)(void);
|
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_title)(oc_str8 title);
|
||||||
|
|
||||||
void ORCA_IMPORT(oc_runtime_window_set_size)(oc_vec2 size);
|
void ORCA_IMPORT(oc_runtime_window_set_size)(oc_vec2 size);
|
||||||
|
|
||||||
#endif // !defined(OC_PLATFORM_ORCA) || !(OC_PLATFORM_ORCA)
|
#endif // !defined(OC_PLATFORM_ORCA) || !(OC_PLATFORM_ORCA)
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#include "app.h"
|
#include "app.h"
|
||||||
|
|
||||||
#include "platform/platform.h"
|
#include "platform/platform.h"
|
||||||
|
#include "platform/platform_io_internal.h"
|
||||||
#include "util/ringbuffer.h"
|
#include "util/ringbuffer.h"
|
||||||
|
|
||||||
#if OC_PLATFORM_WINDOWS
|
#if OC_PLATFORM_WINDOWS
|
||||||
|
|
|
@ -2,3 +2,8 @@
|
||||||
|
|
||||||
//This is used to pass raw events from the runtime
|
//This is used to pass raw events from the runtime
|
||||||
ORCA_EXPORT oc_event oc_rawEvent;
|
ORCA_EXPORT oc_event oc_rawEvent;
|
||||||
|
|
||||||
|
ORCA_EXPORT void* oc_arena_push_stub(oc_arena* arena, u64 size)
|
||||||
|
{
|
||||||
|
return (oc_arena_push(arena, size));
|
||||||
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
#include "platform_clock.h"
|
#include "platform_clock.h"
|
||||||
#include "platform_debug.h"
|
#include "platform_debug.h"
|
||||||
#include "ringbuffer.h"
|
#include "ringbuffer.h"
|
||||||
|
#include "platform/platform_path.h"
|
||||||
#include "app.c"
|
#include "app.c"
|
||||||
|
|
||||||
//--------------------------------------------------------------------
|
//--------------------------------------------------------------------
|
||||||
|
@ -2133,18 +2133,27 @@ oc_str8 oc_open_dialog(oc_arena* arena,
|
||||||
oc_str8_list filters,
|
oc_str8_list filters,
|
||||||
bool directory)
|
bool directory)
|
||||||
{
|
{
|
||||||
|
__block oc_str8 path = { 0 };
|
||||||
|
|
||||||
|
dispatch_block_t block = ^{
|
||||||
@autoreleasepool
|
@autoreleasepool
|
||||||
{
|
{
|
||||||
NSWindow* keyWindow = [NSApp keyWindow];
|
NSWindow* keyWindow = [NSApp keyWindow];
|
||||||
|
|
||||||
NSOpenPanel* dialog = [NSOpenPanel openPanel];
|
NSOpenPanel* dialog = [NSOpenPanel openPanel];
|
||||||
[dialog setLevel:CGShieldingWindowLevel()];
|
|
||||||
|
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)
|
if(filters.eltCount)
|
||||||
{
|
{
|
||||||
NSMutableArray* fileTypesArray = [NSMutableArray array];
|
NSMutableArray* fileTypesArray = [NSMutableArray array];
|
||||||
|
|
||||||
oc_list_for(&filters.list, elt, oc_str8_elt, listElt)
|
oc_list_for((oc_list*)&filters.list, elt, oc_str8_elt, listElt)
|
||||||
{
|
{
|
||||||
oc_str8 string = elt->string;
|
oc_str8 string = elt->string;
|
||||||
NSString* filter = [[NSString alloc] initWithBytes:string.ptr length:string.len encoding:NSUTF8StringEncoding];
|
NSString* filter = [[NSString alloc] initWithBytes:string.ptr length:string.len encoding:NSUTF8StringEncoding];
|
||||||
|
@ -2189,17 +2198,22 @@ oc_str8 oc_open_dialog(oc_arena* arena,
|
||||||
|
|
||||||
const char* result = [[[files objectAtIndex:0] path] UTF8String];
|
const char* result = [[[files objectAtIndex:0] path] UTF8String];
|
||||||
|
|
||||||
oc_str8 path = oc_str8_push_cstring(arena, result);
|
path = oc_str8_push_cstring(arena, result);
|
||||||
|
}
|
||||||
[keyWindow makeKeyWindow];
|
[keyWindow makeKeyWindow];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (path);
|
if([NSThread isMainThread])
|
||||||
|
{
|
||||||
|
block();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
[keyWindow makeKeyWindow];
|
dispatch_sync(dispatch_get_main_queue(), block);
|
||||||
return ((oc_str8){ 0, 0 });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return (path);
|
||||||
}
|
}
|
||||||
|
|
||||||
oc_str8 oc_save_dialog(oc_arena* arena,
|
oc_str8 oc_save_dialog(oc_arena* arena,
|
||||||
|
@ -2207,6 +2221,9 @@ oc_str8 oc_save_dialog(oc_arena* arena,
|
||||||
oc_str8 defaultPath,
|
oc_str8 defaultPath,
|
||||||
oc_str8_list filters)
|
oc_str8_list filters)
|
||||||
{
|
{
|
||||||
|
__block oc_str8 path = { 0 };
|
||||||
|
|
||||||
|
dispatch_block_t block = ^{
|
||||||
@autoreleasepool
|
@autoreleasepool
|
||||||
{
|
{
|
||||||
NSWindow* keyWindow = [NSApp keyWindow];
|
NSWindow* keyWindow = [NSApp keyWindow];
|
||||||
|
@ -2218,7 +2235,7 @@ oc_str8 oc_save_dialog(oc_arena* arena,
|
||||||
{
|
{
|
||||||
NSMutableArray* fileTypesArray = [NSMutableArray array];
|
NSMutableArray* fileTypesArray = [NSMutableArray array];
|
||||||
|
|
||||||
oc_list_for(&filters.list, elt, oc_str8_elt, listElt)
|
oc_list_for((oc_list*)&filters.list, elt, oc_str8_elt, listElt)
|
||||||
{
|
{
|
||||||
oc_str8 string = elt->string;
|
oc_str8 string = elt->string;
|
||||||
NSString* filter = [[NSString alloc] initWithBytes:string.ptr length:string.len encoding:NSUTF8StringEncoding];
|
NSString* filter = [[NSString alloc] initWithBytes:string.ptr length:string.len encoding:NSUTF8StringEncoding];
|
||||||
|
@ -2252,16 +2269,173 @@ oc_str8 oc_save_dialog(oc_arena* arena,
|
||||||
|
|
||||||
const char* result = [[files path] UTF8String];
|
const char* result = [[files path] UTF8String];
|
||||||
|
|
||||||
oc_str8 path = oc_str8_push_cstring(arena, result);
|
path = oc_str8_push_cstring(arena, result);
|
||||||
|
}
|
||||||
[keyWindow makeKeyWindow];
|
[keyWindow makeKeyWindow];
|
||||||
return (path);
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if([NSThread isMainThread])
|
||||||
|
{
|
||||||
|
block();
|
||||||
}
|
}
|
||||||
else
|
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];
|
[keyWindow makeKeyWindow];
|
||||||
return ((oc_str8){ 0, 0 });
|
|
||||||
|
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,
|
int oc_alert_popup(oc_str8 title,
|
||||||
|
|
|
@ -1565,6 +1565,207 @@ oc_str8 oc_save_dialog(oc_arena* arena,
|
||||||
return (res);
|
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 <commctrl.h>
|
#include <commctrl.h>
|
||||||
|
|
||||||
int oc_alert_popup(oc_str8 title,
|
int oc_alert_popup(oc_str8 title,
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#include "platform/platform.h"
|
#include "platform/platform.h"
|
||||||
#include "platform/platform_clock.h"
|
#include "platform/platform_clock.h"
|
||||||
#include "platform/platform_io.h"
|
#include "platform/platform_io.h"
|
||||||
|
#include "platform/platform_io_dialog.h"
|
||||||
#include "platform/platform_path.h"
|
#include "platform/platform_path.h"
|
||||||
|
|
||||||
#if !defined(OC_PLATFORM_ORCA) || !(OC_PLATFORM_ORCA)
|
#if !defined(OC_PLATFORM_ORCA) || !(OC_PLATFORM_ORCA)
|
||||||
|
|
|
@ -222,4 +222,10 @@ ORCA_API u64 oc_file_size(oc_file file);
|
||||||
|
|
||||||
//TODO: Complete as needed...
|
//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_
|
#endif //__PLATFORM_IO_H_
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
*****************************************************************/
|
*****************************************************************/
|
||||||
|
|
||||||
#include "platform_io.h"
|
#include "platform_io.h"
|
||||||
|
#include "platform_io_dialog.h"
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// File stream read/write API
|
// File stream read/write API
|
||||||
|
|
|
@ -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_
|
|
@ -5,12 +5,17 @@
|
||||||
* @date: 11/06/2023
|
* @date: 11/06/2023
|
||||||
*
|
*
|
||||||
*****************************************************************/
|
*****************************************************************/
|
||||||
|
#include "app/app.h"
|
||||||
#include "platform_io_internal.h"
|
#include "platform/platform_io_internal.h"
|
||||||
#include "platform_path.h"
|
#include "platform/platform_path.h"
|
||||||
|
|
||||||
oc_file_table oc_globalFileTable = { 0 };
|
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* oc_file_slot_alloc(oc_file_table* table)
|
||||||
{
|
{
|
||||||
oc_file_slot* slot = oc_list_pop_entry(&table->freeList, oc_file_slot, freeListElt);
|
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)
|
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);
|
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));
|
||||||
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
|
|
||||||
#include "platform.h"
|
#include "platform.h"
|
||||||
#include "platform_io.h"
|
#include "platform_io.h"
|
||||||
|
#include "platform_io_dialog.h"
|
||||||
|
|
||||||
#if OC_PLATFORM_MACOS || PLATFORM_LINUX
|
#if OC_PLATFORM_MACOS || PLATFORM_LINUX
|
||||||
typedef int oc_file_desc;
|
typedef int oc_file_desc;
|
||||||
|
@ -46,12 +47,22 @@ typedef struct oc_file_table
|
||||||
oc_list freeList;
|
oc_list freeList;
|
||||||
} oc_file_table;
|
} oc_file_table;
|
||||||
|
|
||||||
|
ORCA_API oc_file_table* oc_file_table_get_global();
|
||||||
|
|
||||||
oc_file_slot* oc_file_slot_alloc(oc_file_table* table);
|
oc_file_slot* oc_file_slot_alloc(oc_file_table* table);
|
||||||
void oc_file_slot_recycle(oc_file_table* table, oc_file_slot* slot);
|
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 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);
|
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
|
// raw io primitives
|
||||||
|
|
|
@ -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)
|
oc_str8 oc_path_join(oc_arena* arena, oc_str8_list elements)
|
||||||
{
|
{
|
||||||
//TODO: check if elements have ending/begining '/' ?
|
//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);
|
return (res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -518,7 +518,7 @@ oc_io_cmp oc_io_get_error(oc_file_slot* slot, oc_io_req* req)
|
||||||
return (cmp);
|
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 };
|
oc_io_cmp cmp = { 0 };
|
||||||
|
|
||||||
|
|
|
@ -73,7 +73,7 @@ oc_io_error oc_io_raw_last_error()
|
||||||
return (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 };
|
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_arena_scope scratch = oc_scratch_begin_next(arena);
|
||||||
|
|
||||||
oc_str16 dirPathW = win32_path_from_handle_null_terminated(scratch.arena, dirFd);
|
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)
|
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_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)
|
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);
|
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 };
|
oc_io_cmp cmp = { 0 };
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
bool oc_path_is_absolute(oc_str8 path)
|
bool oc_path_is_absolute(oc_str8 path)
|
||||||
{
|
{
|
||||||
oc_arena_scope scratch = oc_scratch_begin();
|
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);
|
bool result = !PathIsRelativeW(pathW.ptr);
|
||||||
|
|
||||||
oc_scratch_end(scratch);
|
oc_scratch_end(scratch);
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
|
|
||||||
#include "win32_string_helpers.h"
|
#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 };
|
oc_str16 res = { 0 };
|
||||||
res.len = 1 + MultiByteToWideChar(CP_UTF8, 0, s.ptr, s.len, NULL, 0);
|
res.len = 1 + MultiByteToWideChar(CP_UTF8, 0, s.ptr, s.len, NULL, 0);
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
|
|
||||||
#include "util/strings.h"
|
#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);
|
oc_str8 oc_win32_wide_to_utf8(oc_arena* arena, oc_str16 s);
|
||||||
|
|
||||||
#endif // __WIN32_STRING_HELPERS_H_
|
#endif // __WIN32_STRING_HELPERS_H_
|
||||||
|
|
154
src/runtime.c
154
src/runtime.c
|
@ -19,7 +19,7 @@
|
||||||
|
|
||||||
oc_font orca_font_create(const char* resourcePath)
|
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));
|
oc_str8 fontPath = oc_path_executable_relative(oc_scratch(), OC_STR8(resourcePath));
|
||||||
|
|
||||||
FILE* fontFile = fopen(fontPath.ptr, "r");
|
FILE* fontFile = fopen(fontPath.ptr, "r");
|
||||||
|
@ -49,11 +49,6 @@ oc_font orca_font_create(const char* resourcePath)
|
||||||
return (font);
|
return (font);
|
||||||
}
|
}
|
||||||
|
|
||||||
oc_font oc_font_create_default()
|
|
||||||
{
|
|
||||||
return (orca_font_create("../resources/OpenSansLatinSubset.ttf"));
|
|
||||||
}
|
|
||||||
|
|
||||||
oc_runtime __orcaApp = { 0 };
|
oc_runtime __orcaApp = { 0 };
|
||||||
|
|
||||||
oc_runtime* oc_runtime_get()
|
oc_runtime* oc_runtime_get()
|
||||||
|
@ -63,26 +58,69 @@ oc_runtime* oc_runtime_get()
|
||||||
|
|
||||||
oc_runtime_env* oc_runtime_env_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
|
// We can't use the runtime's memory pointer directly because wasm3 embeds a
|
||||||
// header at the beginning of the block we give it.
|
// header at the beginning of the block we give it.
|
||||||
u64 bufferIndex = (u64)wasmPtr & 0xffffffff;
|
u64 bufferIndex = (u64)wasmPtr & 0xffffffff;
|
||||||
u32 memSize = 0;
|
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)
|
if(bufferIndex + length < memSize)
|
||||||
{
|
{
|
||||||
char* nativePtr = memory + bufferIndex;
|
char* nativePtr = memory + bufferIndex;
|
||||||
return nativePtr;
|
return nativePtr;
|
||||||
}
|
}
|
||||||
|
//TODO directly abort here?
|
||||||
|
|
||||||
return NULL;
|
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)
|
void oc_runtime_window_set_title(oc_str8 title)
|
||||||
{
|
{
|
||||||
title.ptr = oc_runtime_ptr_to_native(oc_runtime_get(), title.ptr, title.len);
|
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;
|
oc_runtime* app = &__orcaApp;
|
||||||
|
|
||||||
char* memBase = app->runtime.wasmMemory.ptr;
|
char* memBase = app->env.wasmMemory.ptr;
|
||||||
u32 memSize = app->runtime.wasmMemory.committed;
|
u32 memSize = app->env.wasmMemory.committed;
|
||||||
if(((char*)primitives > memBase)
|
if(((char*)primitives > memBase)
|
||||||
&& ((char*)primitives + primitiveCount * sizeof(oc_primitive) - memBase <= memSize)
|
&& ((char*)primitives + primitiveCount * sizeof(oc_primitive) - memBase <= memSize)
|
||||||
&& ((char*)elements > memBase)
|
&& ((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/io_api_bind_gen.c"
|
||||||
#include "wasmbind/surface_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)
|
i32 orca_runloop(void* user)
|
||||||
{
|
{
|
||||||
oc_runtime* app = &__orcaApp;
|
oc_runtime* app = &__orcaApp;
|
||||||
|
|
||||||
oc_runtime_env_init(&app->runtime);
|
oc_runtime_env_init(&app->env);
|
||||||
|
|
||||||
//NOTE: loads wasm module
|
//NOTE: loads wasm module
|
||||||
const char* bundleNameCString = "module";
|
const char* bundleNameCString = "module";
|
||||||
|
@ -386,42 +408,42 @@ i32 orca_runloop(void* user)
|
||||||
u64 wasmSize = ftell(file);
|
u64 wasmSize = ftell(file);
|
||||||
rewind(file);
|
rewind(file);
|
||||||
|
|
||||||
app->runtime.wasmBytecode.len = wasmSize;
|
app->env.wasmBytecode.len = wasmSize;
|
||||||
app->runtime.wasmBytecode.ptr = oc_malloc_array(char, wasmSize);
|
app->env.wasmBytecode.ptr = oc_malloc_array(char, wasmSize);
|
||||||
fread(app->runtime.wasmBytecode.ptr, 1, app->runtime.wasmBytecode.len, file);
|
fread(app->env.wasmBytecode.ptr, 1, app->env.wasmBytecode.len, file);
|
||||||
fclose(file);
|
fclose(file);
|
||||||
|
|
||||||
u32 stackSize = 65536;
|
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.
|
//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)
|
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)
|
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());
|
oc_arena_clear(oc_scratch());
|
||||||
|
|
||||||
//NOTE: bind orca APIs
|
//NOTE: bind orca APIs
|
||||||
{
|
{
|
||||||
int err = 0;
|
int err = 0;
|
||||||
err |= bindgen_link_core_api(app->runtime.m3Module);
|
err |= bindgen_link_core_api(app->env.m3Module);
|
||||||
err |= bindgen_link_surface_api(app->runtime.m3Module);
|
err |= bindgen_link_surface_api(app->env.m3Module);
|
||||||
err |= bindgen_link_clock_api(app->runtime.m3Module);
|
err |= bindgen_link_clock_api(app->env.m3Module);
|
||||||
err |= bindgen_link_io_api(app->runtime.m3Module);
|
err |= bindgen_link_io_api(app->env.m3Module);
|
||||||
err |= bindgen_link_gles_api(app->runtime.m3Module);
|
err |= bindgen_link_gles_api(app->env.m3Module);
|
||||||
err |= manual_link_gles_api(app->runtime.m3Module);
|
err |= manual_link_gles_api(app->env.m3Module);
|
||||||
|
|
||||||
if(err)
|
if(err)
|
||||||
{
|
{
|
||||||
|
@ -429,10 +451,10 @@ i32 orca_runloop(void* user)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//NOTE: compile
|
//NOTE: compile
|
||||||
res = m3_CompileModule(app->runtime.m3Module);
|
res = m3_CompileModule(app->env.m3Module);
|
||||||
if(res)
|
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.
|
//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];
|
const oc_export_desc* desc = &OC_EXPORT_DESC[i];
|
||||||
IM3Function handler = 0;
|
IM3Function handler = 0;
|
||||||
m3_FindFunction(&handler, app->runtime.m3Runtime, desc->name.ptr);
|
m3_FindFunction(&handler, app->env.m3Runtime, desc->name.ptr);
|
||||||
|
|
||||||
if(handler)
|
if(handler)
|
||||||
{
|
{
|
||||||
|
@ -481,7 +503,7 @@ i32 orca_runloop(void* user)
|
||||||
|
|
||||||
if(checked)
|
if(checked)
|
||||||
{
|
{
|
||||||
app->runtime.exports[i] = handler;
|
app->env.exports[i] = handler;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -491,8 +513,8 @@ i32 orca_runloop(void* user)
|
||||||
}
|
}
|
||||||
|
|
||||||
//NOTE: get location of the raw event slot
|
//NOTE: get location of the raw event slot
|
||||||
IM3Global rawEventGlobal = m3_FindGlobal(app->runtime.m3Module, "oc_rawEvent");
|
IM3Global rawEventGlobal = m3_FindGlobal(app->env.m3Module, "oc_rawEvent");
|
||||||
app->runtime.rawEventOffset = (u32)rawEventGlobal->intValue;
|
app->env.rawEventOffset = (u32)rawEventGlobal->intValue;
|
||||||
|
|
||||||
//NOTE: preopen the app local root dir
|
//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,
|
.open.rights = OC_FILE_ACCESS_READ | OC_FILE_ACCESS_WRITE,
|
||||||
.size = localRootPath.len,
|
.size = localRootPath.len,
|
||||||
.buffer = localRootPath.ptr };
|
.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;
|
app->rootDir = cmp.handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
IM3Function* exports = app->runtime.exports;
|
IM3Function* exports = app->env.exports;
|
||||||
|
|
||||||
//NOTE: call init handler
|
//NOTE: call init handler
|
||||||
if(exports[OC_EXPORT_ON_INIT])
|
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);
|
M3Result res = m3_Call(exports[OC_EXPORT_ON_INIT], 0, 0);
|
||||||
if(res)
|
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);
|
M3Result res = m3_Call(exports[OC_EXPORT_FRAME_RESIZE], 2, args);
|
||||||
if(res)
|
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])
|
if(exports[OC_EXPORT_RAW_EVENT])
|
||||||
{
|
{
|
||||||
#ifndef M3_BIG_ENDIAN
|
#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));
|
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);
|
M3Result res = m3_Call(exports[OC_EXPORT_RAW_EVENT], 1, args);
|
||||||
if(res)
|
if(res)
|
||||||
{
|
{
|
||||||
ORCA_WASM3_ABORT(app->runtime.m3Runtime, res, "Runtime error");
|
ORCA_WASM3_ABORT(app->env.m3Runtime, res, "Runtime error");
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
oc_log_error("oc_on_raw_event() is not supported on big endian platforms");
|
oc_log_error("oc_on_raw_event() is not supported on big endian platforms");
|
||||||
|
@ -581,7 +603,7 @@ i32 orca_runloop(void* user)
|
||||||
M3Result res = m3_Call(exports[OC_EXPORT_FRAME_RESIZE], 2, args);
|
M3Result res = m3_Call(exports[OC_EXPORT_FRAME_RESIZE], 2, args);
|
||||||
if(res)
|
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);
|
M3Result res = m3_Call(exports[OC_EXPORT_MOUSE_DOWN], 1, args);
|
||||||
if(res)
|
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);
|
M3Result res = m3_Call(exports[OC_EXPORT_MOUSE_UP], 1, args);
|
||||||
if(res)
|
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);
|
M3Result res = m3_Call(exports[OC_EXPORT_MOUSE_MOVE], 4, args);
|
||||||
if(res)
|
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);
|
M3Result res = m3_Call(exports[OC_EXPORT_KEY_DOWN], 1, args);
|
||||||
if(res)
|
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);
|
M3Result res = m3_Call(exports[OC_EXPORT_KEY_UP], 1, args);
|
||||||
if(res)
|
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);
|
M3Result res = m3_Call(exports[OC_EXPORT_FRAME_REFRESH], 0, 0);
|
||||||
if(res)
|
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);
|
M3Result res = m3_Call(exports[OC_EXPORT_TERMINATE], 0, 0);
|
||||||
if(res)
|
if(res)
|
||||||
{
|
{
|
||||||
ORCA_WASM3_ABORT(app->runtime.m3Runtime, res, "Runtime error");
|
ORCA_WASM3_ABORT(app->env.m3Runtime, res, "Runtime error");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,8 @@
|
||||||
X(OC_EXPORT_FRAME_REFRESH, "oc_on_frame_refresh", "", "") \
|
X(OC_EXPORT_FRAME_REFRESH, "oc_on_frame_refresh", "", "") \
|
||||||
X(OC_EXPORT_FRAME_RESIZE, "oc_on_resize", "", "ii") \
|
X(OC_EXPORT_FRAME_RESIZE, "oc_on_resize", "", "ii") \
|
||||||
X(OC_EXPORT_RAW_EVENT, "oc_on_raw_event", "", "i") \
|
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
|
typedef enum
|
||||||
{
|
{
|
||||||
|
@ -117,7 +118,7 @@ typedef struct oc_runtime
|
||||||
oc_file_table fileTable;
|
oc_file_table fileTable;
|
||||||
oc_file rootDir;
|
oc_file rootDir;
|
||||||
|
|
||||||
oc_runtime_env runtime;
|
oc_runtime_env env;
|
||||||
|
|
||||||
oc_debug_overlay debugOverlay;
|
oc_debug_overlay debugOverlay;
|
||||||
|
|
||||||
|
@ -127,5 +128,6 @@ oc_runtime* oc_runtime_get();
|
||||||
oc_runtime_env* oc_runtime_env_get();
|
oc_runtime_env* oc_runtime_env_get();
|
||||||
|
|
||||||
void* oc_runtime_ptr_to_native(oc_runtime* runtime, void* wasmPtr, u32 length);
|
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_
|
#endif //__RUNTIME_H_
|
||||||
|
|
157
src/runtime_io.c
157
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;
|
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
|
else
|
||||||
{
|
{
|
||||||
|
@ -39,3 +39,158 @@ oc_io_cmp oc_runtime_io_wait_single_req(oc_io_req* wasmReq)
|
||||||
|
|
||||||
return (cmp);
|
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);
|
||||||
|
}
|
||||||
|
|
|
@ -14,8 +14,7 @@
|
||||||
#include "util/debug.h"
|
#include "util/debug.h"
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C"
|
extern "C" {
|
||||||
{
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
//-------------------------------------------------------------------------
|
//-------------------------------------------------------------------------
|
||||||
|
@ -66,8 +65,8 @@ extern "C"
|
||||||
|
|
||||||
struct oc_list_elt
|
struct oc_list_elt
|
||||||
{
|
{
|
||||||
oc_list_elt* next;
|
|
||||||
oc_list_elt* prev;
|
oc_list_elt* prev;
|
||||||
|
oc_list_elt* next;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct oc_list
|
typedef struct oc_list
|
||||||
|
|
|
@ -241,7 +241,7 @@ ORCA_API oc_arena* oc_scratch_next(oc_arena* used)
|
||||||
return (res);
|
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* scratch = oc_scratch();
|
||||||
oc_arena_scope scope = oc_arena_scope_begin(scratch);
|
oc_arena_scope scope = oc_arena_scope_begin(scratch);
|
||||||
|
|
|
@ -14,8 +14,7 @@
|
||||||
#include "util/typedefs.h"
|
#include "util/typedefs.h"
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C"
|
extern "C" {
|
||||||
{
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
//--------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------
|
||||||
|
@ -100,7 +99,7 @@ extern "C"
|
||||||
//--------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------
|
||||||
ORCA_API oc_arena* oc_scratch();
|
ORCA_API oc_arena* oc_scratch();
|
||||||
ORCA_API oc_arena* oc_scratch_next(oc_arena* used);
|
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(void);
|
||||||
ORCA_API oc_arena_scope oc_scratch_begin_next(oc_arena* used);
|
ORCA_API oc_arena_scope oc_scratch_begin_next(oc_arena* used);
|
||||||
|
|
||||||
#define oc_scratch_end(scope) oc_arena_scope_end(scope)
|
#define oc_scratch_end(scope) oc_arena_scope_end(scope)
|
||||||
|
|
|
@ -17,8 +17,7 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C"
|
extern "C" {
|
||||||
{
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*NOTE:
|
/*NOTE:
|
||||||
|
|
|
@ -5,5 +5,33 @@
|
||||||
"ret": {"name": "oc_io_cmp", "tag": "S"},
|
"ret": {"name": "oc_io_cmp", "tag": "S"},
|
||||||
"args": [ {"name": "req",
|
"args": [ {"name": "req",
|
||||||
"type": {"name": "oc_io_req*", "tag": "p"}}]
|
"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"}}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
bin
|
|
@ -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
|
|
@ -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
|
|
@ -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);
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
The quick brown fox jumps over the lazy dog
|
|
@ -0,0 +1 @@
|
||||||
|
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
|
@ -0,0 +1 @@
|
||||||
|
bin
|
|
@ -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
|
|
@ -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
|
|
@ -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);
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
The quick brown fox jumps over the lazy dog
|
|
@ -0,0 +1 @@
|
||||||
|
bin
|
|
@ -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
|
|
@ -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
|
|
@ -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);
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
The quick brown fox jumps over the lazy dog
|
Loading…
Reference in New Issue