diff options
| author | skytrias <skytrias@protonmail.com> | 2024-03-18 22:09:17 +0100 |
|---|---|---|
| committer | skytrias <skytrias@protonmail.com> | 2024-03-18 22:09:17 +0100 |
| commit | bca4c37f02879ba0b7e4f488659600da9d086d4d (patch) | |
| tree | 5f92de606f3aaa09042ed6547504ca275e8f1655 | |
| parent | 6cb74b63ec6316c1ca030f4d828f02bb28a3b91f (diff) | |
push orca target and old bindings
| -rw-r--r-- | base/runtime/heap_allocator_other.odin | 2 | ||||
| -rw-r--r-- | base/runtime/os_specific_orca.odin | 14 | ||||
| -rw-r--r-- | base/runtime/os_specific_wasi.odin | 1 | ||||
| -rw-r--r-- | base/runtime/procs.odin | 11 | ||||
| -rw-r--r-- | core/os/os_orca.odin | 109 | ||||
| -rw-r--r-- | core/sys/orca/algebra.odin | 58 | ||||
| -rw-r--r-- | core/sys/orca/app.odin | 366 | ||||
| -rw-r--r-- | core/sys/orca/graphics.odin | 246 | ||||
| -rw-r--r-- | core/sys/orca/input_state.odin | 59 | ||||
| -rw-r--r-- | core/sys/orca/io.odin | 159 | ||||
| -rw-r--r-- | core/sys/orca/lists.odin | 117 | ||||
| -rw-r--r-- | core/sys/orca/orca.odin | 32 | ||||
| -rw-r--r-- | core/sys/orca/ui.odin | 685 | ||||
| -rw-r--r-- | core/sys/orca/utf8.odin | 131 | ||||
| -rw-r--r-- | core/sys/orca/util.odin | 266 | ||||
| -rw-r--r-- | core/sys/wasm/wasi/wasi_api.odin | 6 | ||||
| -rw-r--r-- | core/time/time_orca.odin | 26 | ||||
| -rw-r--r-- | src/build_settings.cpp | 18 | ||||
| -rw-r--r-- | src/checker.cpp | 1 | ||||
| -rw-r--r-- | src/linker.cpp | 13 |
20 files changed, 2312 insertions, 8 deletions
diff --git a/base/runtime/heap_allocator_other.odin b/base/runtime/heap_allocator_other.odin index 45049c7e9..fda8278cb 100644 --- a/base/runtime/heap_allocator_other.odin +++ b/base/runtime/heap_allocator_other.odin @@ -1,4 +1,4 @@ -//+build js, wasi, freestanding, essence +//+build js, wasi, freestanding, essence, orca //+private package runtime diff --git a/base/runtime/os_specific_orca.odin b/base/runtime/os_specific_orca.odin new file mode 100644 index 000000000..95de7c03e --- /dev/null +++ b/base/runtime/os_specific_orca.odin @@ -0,0 +1,14 @@ +//+build orca +//+private +package runtime + +// TODO +// foreign import +// fd_write :: proc "contextless" () +import "core:sys/wasm/wasi" + +_stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) { + data := (wasi.ciovec_t)(data) + n, err := wasi.fd_write(1, {data}) + return int(n), _OS_Errno(err) +} diff --git a/base/runtime/os_specific_wasi.odin b/base/runtime/os_specific_wasi.odin index 94fa5fa89..4a7fd8fae 100644 --- a/base/runtime/os_specific_wasi.odin +++ b/base/runtime/os_specific_wasi.odin @@ -2,6 +2,7 @@ //+private package runtime +// TODO reimplement fd_write import "core:sys/wasm/wasi" _stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) { diff --git a/base/runtime/procs.odin b/base/runtime/procs.odin index 454574c35..d9422b6a2 100644 --- a/base/runtime/procs.odin +++ b/base/runtime/procs.odin @@ -25,6 +25,17 @@ when ODIN_NO_CRT && ODIN_OS == .Windows { RtlMoveMemory(dst, src, len) return dst } +} else when ODIN_OS == .Orca { + memset :: proc "c" (ptr: rawptr, val: i32, len: int) -> rawptr { + if ptr != nil && len != 0 { + b := byte(val) + p := ([^]byte)(ptr) + for i := 0; i < len; i += 1 { + p[i] = b + } + } + return ptr + } } else when ODIN_NO_CRT || (ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32) { @(link_name="memset", linkage="strong", require) memset :: proc "c" (ptr: rawptr, val: i32, len: int) -> rawptr { diff --git a/core/os/os_orca.odin b/core/os/os_orca.odin new file mode 100644 index 000000000..9bfd87322 --- /dev/null +++ b/core/os/os_orca.odin @@ -0,0 +1,109 @@ +package os + +import "core:sys/wasm/wasi" +import "base:runtime" + +Handle :: distinct i32 +Errno :: distinct i32 + +ERROR_NONE :: Errno(wasi.errno_t.SUCCESS) + +O_RDONLY :: 0x00000 +O_WRONLY :: 0x00001 +O_RDWR :: 0x00002 +O_CREATE :: 0x00040 +O_EXCL :: 0x00080 +O_NOCTTY :: 0x00100 +O_TRUNC :: 0x00200 +O_NONBLOCK :: 0x00800 +O_APPEND :: 0x00400 +O_SYNC :: 0x01000 +O_ASYNC :: 0x02000 +O_CLOEXEC :: 0x80000 + +stdin: Handle = 0 +stdout: Handle = 1 +stderr: Handle = 2 +current_dir: Handle = 3 + +write :: proc(fd: Handle, data: []byte) -> (int, Errno) { + iovs := wasi.ciovec_t(data) + n, err := wasi.fd_write(wasi.fd_t(fd), {iovs}) + return int(n), Errno(err) +} +read :: proc(fd: Handle, data: []byte) -> (int, Errno) { + iovs := wasi.iovec_t(data) + n, err := wasi.fd_read(wasi.fd_t(fd), {iovs}) + return int(n), Errno(err) +} +write_at :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) { + iovs := wasi.ciovec_t(data) + n, err := wasi.fd_pwrite(wasi.fd_t(fd), {iovs}, wasi.filesize_t(offset)) + return int(n), Errno(err) +} +read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) { + iovs := wasi.iovec_t(data) + n, err := wasi.fd_pread(wasi.fd_t(fd), {iovs}, wasi.filesize_t(offset)) + return int(n), Errno(err) +} +open :: proc(path: string, mode: int = O_RDONLY, perm: int = 0) -> (Handle, Errno) { + oflags: wasi.oflags_t + if mode & O_CREATE == O_CREATE { + oflags += {.CREATE} + } + if mode & O_EXCL == O_EXCL { + oflags += {.EXCL} + } + if mode & O_TRUNC == O_TRUNC { + oflags += {.TRUNC} + } + + rights: wasi.rights_t = {.FD_SEEK, .FD_FILESTAT_GET} + switch mode & (O_RDONLY|O_WRONLY|O_RDWR) { + case O_RDONLY: rights += {.FD_READ} + case O_WRONLY: rights += {.FD_WRITE} + case O_RDWR: rights += {.FD_READ, .FD_WRITE} + } + + fdflags: wasi.fdflags_t + if mode & O_APPEND == O_APPEND { + fdflags += {.APPEND} + } + if mode & O_NONBLOCK == O_NONBLOCK { + fdflags += {.NONBLOCK} + } + if mode & O_SYNC == O_SYNC { + fdflags += {.SYNC} + } + fd, err := wasi.path_open(wasi.fd_t(current_dir),{.SYMLINK_FOLLOW},path,oflags,rights,{},fdflags) + return Handle(fd), Errno(err) +} +close :: proc(fd: Handle) -> Errno { + err := wasi.fd_close(wasi.fd_t(fd)) + return Errno(err) +} +seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) { + n, err := wasi.fd_seek(wasi.fd_t(fd), wasi.filedelta_t(offset), wasi.whence_t(whence)) + return i64(n), Errno(err) +} +current_thread_id :: proc "contextless" () -> int { + return 0 +} +@(private) +_processor_core_count :: proc() -> int { + return 1 +} + +file_size :: proc(fd: Handle) -> (i64, Errno) { + stat, err := wasi.fd_filestat_get(wasi.fd_t(fd)) + if err != nil { + return 0, Errno(err) + } + return i64(stat.size), 0 +} + + +exit :: proc "contextless" (code: int) -> ! { + runtime._cleanup_runtime_contextless() + wasi.proc_exit(wasi.exitcode_t(code)) +} diff --git a/core/sys/orca/algebra.odin b/core/sys/orca/algebra.odin new file mode 100644 index 000000000..29aa65e9e --- /dev/null +++ b/core/sys/orca/algebra.odin @@ -0,0 +1,58 @@ +package orca + +import "core:math" + +// TODO use orcas or native? + +@(default_calling_convention="c", link_prefix="oc_") +foreign { + mat2x3_mul_m :: proc(lhs, rhs: mat2x3) -> mat2x3 --- + mat2x3_inv :: proc(x: mat2x3) -> mat2x3 --- + mat2x3_mul :: proc(m: mat2x3, p: vec2) -> vec2 --- + mat2x3_rotate :: proc(radians: f32) -> mat2x3 --- + mat2x3_translate :: proc(x, y: f32) -> mat2x3 --- +} + +// mat2x3_mul_m :: proc "contextless" (lhs, rhs: mat2x3) -> (res: mat2x3) { +// res[0] = lhs[0] * rhs[0] + lhs[1] * rhs[3] +// res[1] = lhs[0] * rhs[1] + lhs[1] * rhs[4] +// res[2] = lhs[0] * rhs[2] + lhs[1] * rhs[5] + lhs[2] +// res[3] = lhs[3] * rhs[0] + lhs[4] * rhs[3] +// res[4] = lhs[3] * rhs[1] + lhs[4] * rhs[4] +// res[5] = lhs[3] * rhs[2] + lhs[4] * rhs[5] + lhs[5] +// return +// } + +// mat2x3_inv :: proc "contextless" (x: mat2x3) -> (res: mat2x3) { +// res[0] = x[4] / (x[0] * x[4] - x[1] * x[3]) +// res[1] = x[1] / (x[1] * x[3] - x[0] * x[4]) +// res[3] = x[3] / (x[1] * x[3] - x[0] * x[4]) +// res[4] = x[0] / (x[0] * x[4] - x[1] * x[3]) +// res[2] = -(x[2] * res[0] + x[5] * res[1]) +// res[5] = -(x[2] * res[3] + x[5] * res[4]) +// return +// } + +// mat2x3_mul :: proc "contextless" (m: mat2x3, p: vec2) -> vec2 { +// return { +// p.x * m[0] + p.y * m[1] + m[2], +// p.x * m[3] + p.y * m[4] + m[5], +// } +// } + +// mat2x3_rotate :: proc "contextless" (radians: f32) -> mat2x3 { +// sinRot := math.sin(radians) +// cosRot := math.cos(radians) +// rot := mat2x3 { +// cosRot, -sinRot, 0, +// sinRot, cosRot, 0, +// } +// return rot +// } + +// mat2x3_translate :: proc "contextless" (x, y: f32) -> mat2x3 { +// return { +// 1, 0, x, +// 0, 1, y, +// } +// } diff --git a/core/sys/orca/app.odin b/core/sys/orca/app.odin new file mode 100644 index 000000000..174df5d90 --- /dev/null +++ b/core/sys/orca/app.odin @@ -0,0 +1,366 @@ +package orca + +import "core:c" + +window :: u64 + +mouse_cursor :: enum c.int { + ARROW, + RESIZE_0, + RESIZE_90, + RESIZE_45, + RESIZE_135, + TEXT, +} + +window_style :: enum u32 { + NO_TITLE = 0x01 << 0, + FIXED_SIZE = 0x01 << 1, + NO_CLOSE = 0x01 << 2, + NO_MINIFY = 0x01 << 3, + NO_FOCUS = 0x01 << 4, + FLOAT = 0x01 << 5, + POPUPMENU = 0x01 << 6, + NO_BUTTONS = 0x01 << 7 +} + +event_type :: enum c.int { + NONE, + KEYBOARD_MODS, //TODO: remove, keep only key? + KEYBOARD_KEY, + KEYBOARD_CHAR, + MOUSE_BUTTON, + MOUSE_MOVE, + MOUSE_WHEEL, + MOUSE_ENTER, + MOUSE_LEAVE, + CLIPBOARD_PASTE, + WINDOW_RESIZE, + WINDOW_MOVE, + WINDOW_FOCUS, + WINDOW_UNFOCUS, + WINDOW_HIDE, // rename to minimize? + WINDOW_SHOW, // rename to restore? + WINDOW_CLOSE, + PATHDROP, + FRAME, + QUIT +} + +key_action :: enum c.int { + NO_ACTION, + PRESS, + RELEASE, + REPEAT +} + +key_code :: enum c.int { + KEY_UNKNOWN = '\x00', + KEY_SPACE = ' ', + KEY_APOSTROPHE = '\'', + KEY_COMMA = ',', + KEY_MINUS = '-', + KEY_PERIOD = '.', + KEY_SLASH = '/', + KEY_0 = '0', + KEY_1 = '1', + KEY_2 = '2', + KEY_3 = '3', + KEY_4 = '4', + KEY_5 = '5', + KEY_6 = '6', + KEY_7 = '7', + KEY_8 = '8', + KEY_9 = '9', + KEY_SEMICOLON = ';', + KEY_EQUAL = '=', + KEY_LEFT_BRACKET = '[', + KEY_BACKSLASH = '\\', + KEY_RIGHT_BRACKET = ']', + KEY_GRAVE_ACCENT = '`', + KEY_A = 'a', + KEY_B = 'b', + KEY_C = 'c', + KEY_D = 'd', + KEY_E = 'e', + KEY_F = 'f', + KEY_G = 'g', + KEY_H = 'h', + KEY_I = 'i', + KEY_J = 'j', + KEY_K = 'k', + KEY_L = 'l', + KEY_M = 'm', + KEY_N = 'n', + KEY_O = 'o', + KEY_P = 'p', + KEY_Q = 'q', + KEY_R = 'r', + KEY_S = 's', + KEY_T = 't', + KEY_U = 'u', + KEY_V = 'v', + KEY_W = 'w', + KEY_X = 'x', + KEY_Y = 'y', + KEY_Z = 'z', + KEY_WORLD_1 = 161, + KEY_WORLD_2 = 162, + KEY_ESCAPE = 256, + KEY_ENTER = 257, + KEY_TAB = 258, + KEY_BACKSPACE = 259, + KEY_INSERT = 260, + KEY_DELETE = 261, + KEY_RIGHT = 262, + KEY_LEFT = 263, + KEY_DOWN = 264, + KEY_UP = 265, + KEY_PAGE_UP = 266, + KEY_PAGE_DOWN = 267, + KEY_HOME = 268, + KEY_END = 269, + KEY_CAPS_LOCK = 280, + KEY_SCROLL_LOCK = 281, + KEY_NUM_LOCK = 282, + KEY_PRINT_SCREEN = 283, + KEY_PAUSE = 284, + KEY_F1 = 290, + KEY_F2 = 291, + KEY_F3 = 292, + KEY_F4 = 293, + KEY_F5 = 294, + KEY_F6 = 295, + KEY_F7 = 296, + KEY_F8 = 297, + KEY_F9 = 298, + KEY_F10 = 299, + KEY_F11 = 300, + KEY_F12 = 301, + KEY_F13 = 302, + KEY_F14 = 303, + KEY_F15 = 304, + KEY_F16 = 305, + KEY_F17 = 306, + KEY_F18 = 307, + KEY_F19 = 308, + KEY_F20 = 309, + KEY_F21 = 310, + KEY_F22 = 311, + KEY_F23 = 312, + KEY_F24 = 313, + KEY_F25 = 314, + KEY_KP_0 = 320, + KEY_KP_1 = 321, + KEY_KP_2 = 322, + KEY_KP_3 = 323, + KEY_KP_4 = 324, + KEY_KP_5 = 325, + KEY_KP_6 = 326, + KEY_KP_7 = 327, + KEY_KP_8 = 328, + KEY_KP_9 = 329, + KEY_KP_DECIMAL = 330, + KEY_KP_DIVIDE = 331, + KEY_KP_MULTIPLY = 332, + KEY_KP_SUBTRACT = 333, + KEY_KP_ADD = 334, + KEY_KP_ENTER = 335, + KEY_KP_EQUAL = 336, + KEY_LEFT_SHIFT = 340, + KEY_LEFT_CONTROL = 341, + KEY_LEFT_ALT = 342, + KEY_LEFT_SUPER = 343, + KEY_RIGHT_SHIFT = 344, + KEY_RIGHT_CONTROL = 345, + KEY_RIGHT_ALT = 346, + KEY_RIGHT_SUPER = 347, + KEY_MENU = 348, +} + +scan_code :: enum c.int { + SCANCODE_UNKNOWN = 0, + SCANCODE_SPACE = 32, + SCANCODE_APOSTROPHE = 39, + SCANCODE_COMMA = 44, + SCANCODE_MINUS = 45, + SCANCODE_PERIOD = 46, + SCANCODE_SLASH = 47, + SCANCODE_0 = 48, + SCANCODE_1 = 49, + SCANCODE_2 = 50, + SCANCODE_3 = 51, + SCANCODE_4 = 52, + SCANCODE_5 = 53, + SCANCODE_6 = 54, + SCANCODE_7 = 55, + SCANCODE_8 = 56, + SCANCODE_9 = 57, + SCANCODE_SEMICOLON = 59, + SCANCODE_EQUAL = 61, + SCANCODE_LEFT_BRACKET = 91, + SCANCODE_BACKSLASH = 92, + SCANCODE_RIGHT_BRACKET = 93, + SCANCODE_GRAVE_ACCENT = 96, + SCANCODE_A = 97, + SCANCODE_B = 98, + SCANCODE_C = 99, + SCANCODE_D = 100, + SCANCODE_E = 101, + SCANCODE_F = 102, + SCANCODE_G = 103, + SCANCODE_H = 104, + SCANCODE_I = 105, + SCANCODE_J = 106, + SCANCODE_K = 107, + SCANCODE_L = 108, + SCANCODE_M = 109, + SCANCODE_N = 110, + SCANCODE_O = 111, + SCANCODE_P = 112, + SCANCODE_Q = 113, + SCANCODE_R = 114, + SCANCODE_S = 115, + SCANCODE_T = 116, + SCANCODE_U = 117, + SCANCODE_V = 118, + SCANCODE_W = 119, + SCANCODE_X = 120, + SCANCODE_Y = 121, + SCANCODE_Z = 122, + SCANCODE_WORLD_1 = 161, + SCANCODE_WORLD_2 = 162, + SCANCODE_ESCAPE = 256, + SCANCODE_ENTER = 257, + SCANCODE_TAB = 258, + SCANCODE_BACKSPACE = 259, + SCANCODE_INSERT = 260, + SCANCODE_DELETE = 261, + SCANCODE_RIGHT = 262, + SCANCODE_LEFT = 263, + SCANCODE_DOWN = 264, + SCANCODE_UP = 265, + SCANCODE_PAGE_UP = 266, + SCANCODE_PAGE_DOWN = 267, + SCANCODE_HOME = 268, + SCANCODE_END = 269, + SCANCODE_CAPS_LOCK = 280, + SCANCODE_SCROLL_LOCK = 281, + SCANCODE_NUM_LOCK = 282, + SCANCODE_PRINT_SCREEN = 283, + SCANCODE_PAUSE = 284, + SCANCODE_F1 = 290, + SCANCODE_F2 = 291, + SCANCODE_F3 = 292, + SCANCODE_F4 = 293, + SCANCODE_F5 = 294, + SCANCODE_F6 = 295, + SCANCODE_F7 = 296, + SCANCODE_F8 = 297, + SCANCODE_F9 = 298, + SCANCODE_F10 = 299, + SCANCODE_F11 = 300, + SCANCODE_F12 = 301, + SCANCODE_F13 = 302, + SCANCODE_F14 = 303, + SCANCODE_F15 = 304, + SCANCODE_F16 = 305, + SCANCODE_F17 = 306, + SCANCODE_F18 = 307, + SCANCODE_F19 = 308, + SCANCODE_F20 = 309, + SCANCODE_F21 = 310, + SCANCODE_F22 = 311, + SCANCODE_F23 = 312, + SCANCODE_F24 = 313, + SCANCODE_F25 = 314, + SCANCODE_KP_0 = 320, + SCANCODE_KP_1 = 321, + SCANCODE_KP_2 = 322, + SCANCODE_KP_3 = 323, + SCANCODE_KP_4 = 324, + SCANCODE_KP_5 = 325, + SCANCODE_KP_6 = 326, + SCANCODE_KP_7 = 327, + SCANCODE_KP_8 = 328, + SCANCODE_KP_9 = 329, + SCANCODE_KP_DECIMAL = 330, + SCANCODE_KP_DIVIDE = 331, + SCANCODE_KP_MULTIPLY = 332, + SCANCODE_KP_SUBTRACT = 333, + SCANCODE_KP_ADD = 334, + SCANCODE_KP_ENTER = 335, + SCANCODE_KP_EQUAL = 336, + SCANCODE_LEFT_SHIFT = 340, + SCANCODE_LEFT_CONTROL = 341, + SCANCODE_LEFT_ALT = 342, + SCANCODE_LEFT_SUPER = 343, + SCANCODE_RIGHT_SHIFT = 344, + SCANCODE_RIGHT_CONTROL = 345, + SCANCODE_RIGHT_ALT = 346, + SCANCODE_RIGHT_SUPER = 347, + SCANCODE_MENU = 348, +} + +keymod_flags :: enum c.int { + NONE = 0x00, + ALT = 0x01, + SHIFT = 0x02, + CTRL = 0x04, + CMD = 0x08, + MAIN_MODIFIER = 0x10 /* CMD on Mac, CTRL on Win32 */ +} + +mouse_button :: enum c.int { + LEFT = 0x00, + RIGHT = 0x01, + MIDDLE = 0x02, + EXT1 = 0x03, + EXT2 = 0x04, +} + +// keyboard and mouse buttons input +key_event :: struct { + action: key_action, + scanCode: scan_code, + keyCode: key_code, + button: mouse_button, + mods: keymod_flags, + clickCount: u8, +} + +// character input +char_event :: struct { + codepoint: utf32, + sequence: [8]c.char, + seqLen: u8, +} + +// mouse move/scroll +mouse_event :: struct { + x: f32, + y: f32, + deltaX: f32, + deltaY: f32, + mods: keymod_flags, +} + +// window resize / move +move_event :: struct { + frame: rect, + content: rect, +} + +event :: struct { + //TODO clipboard and path drop + window: window, + type: event_type, + + _: struct #raw_union { + key: key_event, + character: char_event, + mouse: mouse_event, + move: move_event, + paths: str8_list, + } +} diff --git a/core/sys/orca/graphics.odin b/core/sys/orca/graphics.odin new file mode 100644 index 000000000..d1aa59ef8 --- /dev/null +++ b/core/sys/orca/graphics.odin @@ -0,0 +1,246 @@ +package orca + +import "core:c" + +// types +color :: [4]f32 +utf32 :: u32 + +// handles +surface :: u64 +font :: u64 +image :: u64 +canvas :: u64 + +joint_type :: enum c.int { + MITER, + BEVEL, + NONE, +} + +cap_type :: enum c.int { + NONE, + SQUARE, +} + +font_metrics :: struct { + ascent: f32, // the extent above the baseline (by convention a positive value extends above the baseline) + descent: f32, // the extent below the baseline (by convention, positive value extends below the baseline) + lineGap: f32, // spacing between one row's descent and the next row's ascent + xHeight: f32, // height of the lower case letter 'x' + capHeight: f32, // height of the upper case letter 'M' + width: f32, // maximum width of the font +} + +glyph_metrics :: struct { + ink: rect, + advance: vec2, +} + +text_metrics :: struct { + ink: rect, + logical: rect, + advance: vec2, +} + +rect_atlas :: struct { + arena: ^arena, + size: vec2i, + pos: vec2i, + lineHeight: u32, +} + +image_region :: struct { + image: image, + rect: rect, +} + +//------------------------------------------------------------------------------------------ +// graphics surface +//------------------------------------------------------------------------------------------ +@(default_calling_convention="c", link_prefix="oc_") +foreign { + surface_nil :: proc() -> surface --- + surface_is_nil :: proc() -> c.bool --- + surface_canvas :: proc() -> surface --- + surface_gles :: proc() -> surface --- + surface_destroy :: proc(surface: surface) --- + + surface_select :: proc(surface: surface) --- + surface_deselect :: proc() --- + surface_present :: proc(surface: surface) --- + + surface_get_size :: proc(surface: surface) -> vec2 --- + surface_contents_scaling :: proc(surface: surface) -> vec2 --- + surface_bring_to_front :: proc(surface: surface) --- + surface_send_to_back :: proc(surface: surface) --- +} + +//------------------------------------------------------------------------------------------ +// 2D canvas command buffer +//------------------------------------------------------------------------------------------ +@(default_calling_convention="c", link_prefix="oc_") +foreign { + canvas_nil :: proc() -> canvas --- + canvas_is_nil :: proc(canvas: canvas) -> c.bool --- + canvas_create :: proc() -> canvas --- + canvas_destroy :: proc(canvas: canvas) --- + canvas_set_current :: proc(_canvas: canvas) -> canvas --- + canvas_select :: proc(_canvas: canvas) -> canvas --- + render :: proc(canvas: canvas) --- +} + +//------------------------------------------------------------------------------------------ +// transform and clipping +//------------------------------------------------------------------------------------------ +@(default_calling_convention="c", link_prefix="oc_") +foreign { + matrix_push :: proc(mat: mat2x3) --- + matrix_multiply_push :: proc(mat: mat2x3) --- + matrix_pop :: proc() --- + matrix_top :: proc() -> mat2x3 --- + + clip_push :: proc(x, y, w, h: f32) --- + clip_pop :: proc() --- + clip_top :: proc() -> rect --- +} + +//------------------------------------------------------------------------------------------ +// graphics attributes setting/getting +//------------------------------------------------------------------------------------------ +@(default_calling_convention="c", link_prefix="oc_") +foreign { + set_color :: proc(color: color) --- + set_color_rgba :: proc(r, g, b, a: f32) --- + set_width :: proc(width: f32) --- + set_tolerance :: proc(tolerance: f32) --- + set_joint :: proc(joint: joint_type) --- + set_max_joint_excursion :: proc(maxJointExcursion: f32) --- + set_cap :: proc(cap: cap_type) --- + set_font :: proc(font: font) --- + set_font_size :: proc(size: f32) --- + set_text_flip :: proc(flip: c.bool) --- + set_image :: proc(image: image) --- + set_image_source_region :: proc(region: rect) --- + + get_color :: proc() -> color --- + get_width :: proc() -> f32 --- + get_tolerance :: proc() -> f32 --- + get_joint :: proc() -> joint_type --- + get_max_joint_excursion :: proc() -> f32 --- + get_cap :: proc() -> cap_type --- + get_font :: proc() -> font --- + get_font_size :: proc() -> f32 --- + get_text_flip :: proc() -> bool --- + get_image :: proc() -> image --- +} + +//------------------------------------------------------------------------------------------ +// path construction +//------------------------------------------------------------------------------------------ +@(default_calling_convention="c", link_prefix="oc_") +foreign { + get_position :: proc() -> vec2 --- + move_to :: proc(x, y: f32) --- + line_to :: proc(x, y: f32) --- + quadratic_to :: proc(x1, y1, x2, y2: f32) --- + cubic_to :: proc(x1, y1, x2, y2, x3, y3: f32) --- + close_path :: proc() --- + + glyph_outlines :: proc(glyphIndices: str32) -> rect --- + codepoints_outlines :: proc(str: str32) --- + text_outlines :: proc(str: str8) --- +} + +//------------------------------------------------------------------------------------------ +// clear/fill/stroke +//------------------------------------------------------------------------------------------ +@(default_calling_convention="c", link_prefix="oc_") +foreign { + clear :: proc() --- + fill :: proc() --- + stroke :: proc() --- +} + +//------------------------------------------------------------------------------------------ +// shapes helpers +//------------------------------------------------------------------------------------------ +@(default_calling_convention="c", link_prefix="oc_") +foreign { + rectangle_fill :: proc(x, y, w, h: f32) --- + rectangle_stroke :: proc(x, y, w, h: f32) --- + rounded_rectangle_fill :: proc(x, y, w, h, r: f32) --- + rounded_rectangle_stroke :: proc(x, y, w, h, r: f32) --- + ellipse_fill :: proc(x, y, rx, ry: f32) --- + ellipse_stroke :: proc(x, y, rx, ry: f32) --- + circle_fill :: proc(x, y, r: f32) --- + circle_stroke :: proc(x, y, r: f32) --- + arc :: proc(x, y, r, arcAngle, startAngle: f32) --- + image_draw :: proc(image: image, rect: rect) --- + image_draw_region :: proc(image: image, srcRegion, dstRegion: rect) --- + + text_fill :: proc(x, y: f32, text: str8) --- +} + +//------------------------------------------------------------------------------------------ +// fonts +//------------------------------------------------------------------------------------------ +@(default_calling_convention="c", link_prefix="oc_") +foreign { + font_nil :: proc() -> font --- + font_is_nil :: proc(font: font) -> c.bool --- + + font_create_from_memory :: proc(mem: str8, rangeCount: u32, ranges: [^]unicode_range) -> font --- + font_create_from_file :: proc(file: file, rangeCount: u32, ranges: [^]unicode_range) -> font --- + font_create_from_path :: proc(path: str8, rangeCount: u32, ranges: [^]unicode_range) -> font --- + + font_destroy :: proc(font: font) --- + + font_get_glyph_indices :: proc(font: font, codePoints: str32, backing: str32) -> str32 --- + font_push_glyph_indices :: proc(arena: ^arena, font: font, codePoints: str32) -> str32 --- + font_get_glyph_index :: proc(font: font, codePoint: utf32) -> u32 --- + + font_get_metrics :: proc(font: font, emSize: f32) -> font_metrics --- + font_get_metrics_unscaled :: proc(font: font) -> font_metrics --- + font_get_scale_for_em_pixels :: proc(font: font, emSize: f32) -> f32 --- + + font_text_metrics_utf32 :: proc(font: font, fontSize: f32, codepoints: str32) -> text_metrics --- + font_text_metrics :: proc(font: font, fontSize: f32, text: str8) -> text_metrics --- +} + +//------------------------------------------------------------------------------------------ +// images +//------------------------------------------------------------------------------------------ +@(default_calling_convention="c", link_prefix="oc_") +foreign { + image_nil :: proc() -> image --- + image_is_nil :: proc(a: image) -> c.bool --- + + image_create :: proc(surface: surface, width, height: u32) -> image --- + image_create_from_rgba8 :: proc(surface: surface, width, height: u32, pixels: [^]u8) -> image --- + image_create_from_memory :: proc(surface: surface, mem: str8, flip: c.bool) -> image --- + image_create_from_file :: proc(surface: surface, file: file, flip: c.bool) -> image --- + image_create_from_path :: proc(surface: surface, path: str8, flip: c.bool) -> image --- + + image_destroy :: proc(image: image) --- + + image_upload_region_rgba8 :: proc(image: image, region: rect, pixels: [^]u8) --- + image_size :: proc(image: image) -> vec2 --- +} + +//------------------------------------------------------------------------------------------ +// image atlas +//------------------------------------------------------------------------------------------ +@(default_calling_convention="c", link_prefix="oc_") +foreign { + rect_atlas_create :: proc(arena: ^arena, width, height: i32) -> ^rect_atlas --- + rect_atlas_alloc :: proc(atlas: ^rect_atlas, width, height: i32) -> rect --- + rect_atlas_recycle :: proc(atlas: ^rect_atlas, rect: rect) --- + + image_atlas_allfrom_rgba8 :: proc(atlas: ^rect_atlas, backingImage: image, width, height: u32, pixels: [^]u8) -> image_region --- + image_atlas_allfrom_memory :: proc(atlas: ^rect_atlas, backingImage: image, mem: str8, flip: c.bool) -> image_region --- + image_atlas_allfrom_file :: proc(atlas: ^rect_atlas, backingImage: image, file: file, flip: c.bool) -> image_region --- + image_atlas_allfrom_path :: proc(atlas: ^rect_atlas, backingImage: image, path: str8, flip: c.bool) -> image_region --- + + image_atlas_recycle :: proc(atlas: ^rect_atlas, imageRgn: image_region) --- +} diff --git a/core/sys/orca/input_state.odin b/core/sys/orca/input_state.odin new file mode 100644 index 000000000..d8779f453 --- /dev/null +++ b/core/sys/orca/input_state.odin @@ -0,0 +1,59 @@ +package orca + +import "core:c" + +key_state :: struct { + lastUpdate: u64, + transitionCount: u32, + repeatCount: u32, + down: c.bool, + sysClicked: c.bool, + sysDoubleClicked: c.bool, + sysTripleClicked: c.bool, +} + +keyboard_state :: struct { + keys: [len(key_code)]key_state, + mods: keymod_flags, +} + +mouse_state :: struct { + lastUpdate: u64, + posValid: c.bool, + pos: vec2, + delta: vec2, + wheel: vec2, + + _: struct #raw_union { + buttons: [len(mouse_button)]key_state, + + _: struct { + left: key_state, + right: key_state, + middle: key_state, + ext1: key_state, + ext2: key_state, + } + } +} + +INPUT_TEXT_BACKING_SIZE :: 64 + +text_state :: struct { + lastUpdate: u64, + backing: [INPUT_TEXT_BACKING_SIZE]utf32, + codePoints: str32, +} + +clipboard_state :: struct { + lastUpdate: u64, + pastedText: str8, +} + +input_state :: struct { + frameCounter: u64, + keyboard: keyboard_state, + mouse: mouse_state, + text: text_state, + clipboard: clipboard_state, +} diff --git a/core/sys/orca/io.odin b/core/sys/orca/io.odin new file mode 100644 index 000000000..501a45623 --- /dev/null +++ b/core/sys/orca/io.odin @@ -0,0 +1,159 @@ +package orca + +import "core:c" + +file :: distinct u64 // handle + +file_access :: enum u16 { + NONE = 0, + READ = 1 << 1, + WRITE = 1 << 2, +} + +file_open_flags :: enum u16 { + NONE = 0, + APPEND = 1 << 1, + TRUNCATE = 1 << 2, + CREATE = 1 << 3, + + SYMLINK = 1 << 4, + NO_FOLLOW = 1 << 5, + RESTRICT = 1 << 6, +} + +file_whence :: enum c.int { + SEEK_SET, + SEEK_END, + SEEK_CURRENT, +} + +io_error :: enum i32 { + OK = 0, + ERR_UNKNOWN, + ERR_OP, // unsupported operation + ERR_HANDLE, // invalid handle + ERR_PREV, // previously had a fatal error (last error stored on handle) + ERR_ARG, // invalid argument or argument combination + ERR_PERM, // access denied + ERR_SPACE, // no space left + ERR_NO_ENTRY, // file or directory does not exist + ERR_EXISTS, // file already exists + ERR_NOT_DIR, // path element is not a directory + ERR_DIR, // attempted to write directory + ERR_MAX_FILES, // max open files reached + ERR_MAX_LINKS, // too many symbolic links in path + ERR_PATH_LENGTH, // path too long + ERR_FILE_SIZE, // file too big + ERR_OVERFLOW, // offset too big + ERR_NOT_READY, // no data ready to be read/written + ERR_MEM, // failed to allocate memory + ERR_INTERRUPT, // operation interrupted by a signal + ERR_PHYSICAL, // physical IO error + ERR_NO_DEVICE, // device not found + ERR_WALKOUT, // attempted to walk out of root directory +} + +//---------------------------------------------------------------- +// File System wrapper API +//---------------------------------------------------------------- +file_type :: enum c.int { + UNKNOWN, + REGULAR, + DIRECTORY, + SYMLINK, + BLOCK, + CHARACTER, + FIFO, + SOCKET, +} + +file_perm :: enum u16 { + OTHER_EXEC = 1 << 0, + OTHER_WRITE = 1 << 1, + OTHER_READ = 1 << 2, + + GROUP_EXEC = 1 << 3, + GROUP_WRITE = 1 << 4, + GROUP_READ = 1 << 5, + + OWNER_EXEC = 1 << 6, + OWNER_WRITE = 1 << 7, + OWNER_READ = 1 << 8, + + STICKY_BIT = 1 << 9, + SET_GID = 1 << 10, + SET_UID = 1 << 11, +} + +datestamp :: struct { + seconds: i64, // seconds relative to NTP epoch. + fraction: u64, // fraction of seconds elapsed since the time specified by seconds. +} + +file_status :: struct { + uid: u64, + type: file_type, + perm: file_perm, + size: u64, + + creationDate: datestamp, + accessDate: datestamp, + modificationDate: datestamp, +} + +// TODO file dialogs + +// 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; + +// file_open_with_dialog_result :: u64 // TODO +// file_dialog_desc :: u64 // TODO + +//---------------------------------------------------------------- +// Low-level File IO API +//---------------------------------------------------------------- +// @(default_calling_convention="c", link_prefix="oc_") +// foreign { +// oc_io_cmp oc_io_wait_single_req(oc_io_req* req); +// } + +//---------------------------------------------------------------- +// High-level File IO API +//---------------------------------------------------------------- +@(default_calling_convention="c", link_prefix="oc_") +foreign { + file_nil :: proc() -> file --- + file_is_nil :: proc(handle: file) -> c.bool --- + + file_open :: proc(path: str8, rights: file_access, flags: file_open_flags) -> file --- + file_open_at :: proc(dir: file, path: str8, rights: file_access, flags: file_open_flags) -> file --- + file_close :: proc(file: file) --- + file_last_error :: proc(handle: file) -> io_error --- + + file_pos :: proc(file: file) -> i64 --- + file_seek :: proc(file: file, offset: i64, whence: file_whence) -> i64 --- + file_write :: proc(file: file, size: u64, buffer: [^]byte) -> u64 --- + file_read :: proc(file: file, size: u64, buffer: [^]byte) -> u64 --- + + file_get_status :: proc(file: file) -> file_status --- + file_size :: proc(file: file) -> u64 --- +} + +//---------------------------------------------------------------- +// Asking users for file capabilities +//---------------------------------------------------------------- +// @(default_calling_convention="c", link_prefix="oc_") +// foreign { +// file_open_with_request :: proc(path: str8, rights: file_access, flags: file_open_flags) -> file --- +// file_open_with_dialog :: proc(arena: ^arena, rights: file_access, flags: file_open_flags, desc: ^file_dialog_desc) -> file_open_with_dialog_result --- +// }
\ No newline at end of file diff --git a/core/sys/orca/lists.odin b/core/sys/orca/lists.odin new file mode 100644 index 000000000..fda0f5718 --- /dev/null +++ b/core/sys/orca/lists.odin @@ -0,0 +1,117 @@ +package orca + +// TODO could check if container/intrusive/list/intrusive_list.odin can be used + +//---------------------------------------------------------------- +// Lists +//---------------------------------------------------------------- +list_elt :: struct { + prev: ^list_elt, + next: ^list_elt, +} + +list :: struct { + first: ^list_elt, + last: ^list_elt, +} + +list_init :: proc "c" (list: ^list) { + list.first = nil + list.last = nil +} + +list_insert :: proc "c" (list: ^list, afterElt, elt: ^list_elt) { + elt.prev = afterElt + elt.next = afterElt.next + if afterElt.next != nil { + afterElt.next.prev = elt + } else { + list.last = elt + } + afterElt.next = elt + + // OC_DEBUG_ASSERT(elt.next != elt, "list_insert(): can't insert an element into itself") +} + +list_insert_before :: proc "c" (list: ^list, beforeElt, elt: ^list_elt) { + elt.next = beforeElt + elt.prev = beforeElt.prev + + if beforeElt.prev != nil { + beforeElt.prev.next = elt + } else { + list.first = elt + } + beforeElt.prev = elt + + // OC_DEBUG_ASSERT(elt.next != elt, "list_insert_before(): can't insert an element into itself") +} + +list_remove :: proc "c" (list: ^list, elt: ^list_elt) { + if elt.prev != nil { + elt.prev.next = elt.next + } else { + // OC_DEBUG_ASSERT(list.first == elt) + list.first = elt.next + } + + if elt.next != nil { + elt.next.prev = elt.prev + } else { + // OC_DEBUG_ASSERT(list.last == elt) + list.last = elt.prev + } + + elt.prev = nil + elt.next = nil +} + +list_push :: proc "c" (list: ^list, elt: ^list_elt) { + elt.next = list.first + elt.prev = nil + if list.first != nil { + list.first.prev = elt + } else { + list.last = elt + } + list.first = elt +} + +list_pop :: proc "c" (list: ^list) -> ^list_elt { + elt := list.first + if elt != list.last { + list_remove(list, elt) + return elt + } else { + return nil + } +} + +list_push_back :: proc "c" (list: ^list, elt: ^list_elt) { + elt.prev = list.last + elt.next = nil + + if list.last != nil { + list.last.next = elt + } else { + list.first = elt + } + + list.last = elt +} + +list_append :: list_push_back + +list_pop_back :: proc "c" (list: ^list) -> ^list_elt { + elt := list.last + if elt != nil { + list_remove(list, elt) + return elt + } else { + return nil + } +} + +list_empty :: proc "c" (list: ^list) -> bool { + return list.first == nil || list.last == nil +} diff --git a/core/sys/orca/orca.odin b/core/sys/orca/orca.odin new file mode 100644 index 000000000..dfbf1a798 --- /dev/null +++ b/core/sys/orca/orca.odin @@ -0,0 +1,32 @@ +package orca + +import "core:c" + +vec2 :: [2]f32 +vec3 :: [3]f32 +vec4 :: [4]f32 +vec2i :: [2]i32 +// mat2x3 :: [6]f32 +mat2x3 :: matrix[2, 3]f32 +rect :: [4]f32 + +//------------------------------------------------------------------------------------------ +// window +//------------------------------------------------------------------------------------------ +@(default_calling_convention="c", link_prefix="oc_") +foreign { + window_set_title :: proc(title: str8) --- + window_set_size :: proc(size: vec2) --- + request_quit :: proc() --- +} + +clock_kind :: enum c.int { + MONOTONIC, + UPTIME, + DATE, +} + +@(default_calling_convention="c", link_prefix="oc_") +foreign { + clock_time :: proc(clock: clock_kind) -> f64 --- +} diff --git a/core/sys/orca/ui.odin b/core/sys/orca/ui.odin new file mode 100644 index 000000000..6b2d93750 --- /dev/null +++ b/core/sys/orca/ui.odin @@ -0,0 +1,685 @@ +package orca + +import "core:c" + +ui_key :: struct { + hash: u64 +} + +ui_axis :: enum c.int { + X, + Y, +} + +ui_align :: enum c.int { + START, + END, + CENTER, +} + +ui_layout_align :: [2]ui_align + +ui_layout :: struct { + axis: ui_axis, + spacing: f32, + margin: [2]f32, + align: ui_layout_align, +} + +ui_size_kind :: enum c.int { + TEXT, + PIXELS, + CHILDREN, + PARENT, + PARENT_MINUS_PIXELS, +} + +ui_size :: struct { + kind: ui_size_kind, + value: f32, + relax: f32, + minSize: f32, +} + +ui_box_size :: [2]ui_size +ui_box_floating :: [2]c.bool + +//NOTE: flags for axis-dependent properties (e.g. UI_STYLE_FLOAT_X/Y) need to be consecutive bits +// in order to play well with axis agnostic functions +ui_style_mask :: enum u64 { + NONE = 0, + SIZE_WIDTH = 1 << 1, + SIZE_HEIGHT = 1 << 2, + LAYOUT_AXIS = 1 << 3, + LAYOUT_ALIGN_X = 1 << 4, + LAYOUT_ALIGN_Y = 1 << 5, + LAYOUT_SPACING = 1 << 6, + LAYOUT_MARGIN_X = 1 << 7, + LAYOUT_MARGIN_Y = 1 << 8, + FLOAT_X = 1 << 9, + FLOAT_Y = 1 << 10, + COLOR = 1 << 11, + BG_COLOR = 1 << 12, + BORDER_COLOR = 1 << 13, + BORDER_SIZE = 1 << 14, + ROUNDNESS = 1 << 15, + FONT = 1 << 16, + FONT_SIZE = 1 << 17, + ANIMATION_TIME = 1 << 18, + ANIMATION_MASK = 1 << 19, + + //masks + SIZE = SIZE_WIDTH | SIZE_HEIGHT, + + LAYOUT_MARGINS = LAYOUT_MARGIN_X | LAYOUT_MARGIN_Y, + + LAYOUT = LAYOUT_AXIS | LAYOUT_ALIGN_X | LAYOUT_ALIGN_Y | LAYOUT_SPACING | LAYOUT_MARGIN_X | LAYOUT_MARGIN_Y, + + FLOAT = FLOAT_X | FLOAT_Y, + + MASK_INHERITED = COLOR | FONT | FONT_SIZE | ANIMATION_TIME | ANIMATION_MASK, +} + +ui_style :: struct { + size: ui_box_size, + layout: ui_layout, + floating: ui_box_floating, + floatTarget: vec2, + _color: color, + bgColor: color, + borderColor: color, + font: font, + fontSize: f32, + borderSize: f32, + roundness: f32, + animationTime: f32, + animationMask: ui_style_mask, +} + +ui_palette :: struct { + red0: color, + red1: color, + red2: color, + red3: color, + red4: color, + red5: color, + red6: color, + red7: color, + red8: color, + red9: color, + orange0: color, + orange1: color, + orange2: color, + orange3: color, + orange4: color, + orange5: color, + orange6: color, + orange7: color, + orange8: color, + orange9: color, + amber0: color, + amber1: color, + amber2: color, + amber3: color, + amber4: color, + amber5: color, + amber6: color, + amber7: color, + amber8: color, + amber9: color, + yellow0: color, + yellow1: color, + yellow2: color, + yellow3: color, + yellow4: color, + yellow5: color, + yellow6: color, + yellow7: color, + yellow8: color, + yellow9: color, + lime0: color, + lime1: color, + lime2: color, + lime3: color, + lime4: color, + lime5: color, + lime6: color, + lime7: color, + lime8: color, + lime9: color, + lightGreen0: color, + lightGreen1: color, + lightGreen2: color, + lightGreen3: color, + lightGreen4: color, + lightGreen5: color, + lightGreen6: color, + lightGreen7: color, + lightGreen8: color, + lightGreen9: color, + green0: color, + green1: color, + green2: color, + green3: color, + green4: color, + green5: color, + green6: color, + green7: color, + green8: color, + green9: color, + teal0: color, + teal1: color, + teal2: color, + teal3: color, + teal4: color, + teal5: color, + teal6: color, + teal7: color, + teal8: color, + teal9: color, + cyan0: color, + cyan1: color, + cyan2: color, + cyan3: color, + cyan4: color, + cyan5: color, + cyan6: color, + cyan7: color, + cyan8: color, + cyan9: color, + lightBlue0: color, + lightBlue1: color, + lightBlue2: color, + lightBlue3: color, + lightBlue4: color, + lightBlue5: color, + lightBlue6: color, + lightBlue7: color, + lightBlue8: color, + lightBlue9: color, + blue0: color, + blue1: color, + blue2: color, + blue3: color, + blue4: color, + blue5: color, + blue6: color, + blue7: color, + blue8: color, + blue9: color, + indigo0: color, + indigo1: color, + indigo2: color, + indigo3: color, + indigo4: color, + indigo5: color, + indigo6: color, + indigo7: color, + indigo8: color, + indigo9: color, + violet0: color, + violet1: color, + violet2: color, + violet3: color, + violet4: color, + violet5: color, + violet6: color, + violet7: color, + violet8: color, + violet9: color, + purple0: color, + purple1: color, + purple2: color, + purple3: color, + purple4: color, + purple5: color, + purple6: color, + purple7: color, + purple8: color, + purple9: color, + pink0: color, + pink1: color, + pink2: color, + pink3: color, + pink4: color, + pink5: color, + pink6: color, + pink7: color, + pink8: color, + pink9: color, + grey0: color, + grey1: color, + grey2: color, + grey3: color, + grey4: color, + grey5: color, + grey6: color, + grey7: color, + grey8: color, + grey9: color, + black: color, + white: color, +} + +// TODO exteern +// ui_palette: UI_DARK_PALETTE +// ui_palette: UI_LIGHT_PALETTE + +ui_theme :: struct { + white: color, + primary: color, + primaryHover: color, + primaryActive: color, + border: color, + fill0: color, + fill1: color, + fill2: color, + bg0: color, + bg1: color, + bg2: color, + bg3: color, + bg4: color, + text0: color, + text1: color, + text2: color, + text3: color, + sliderThumbBorder: color, + elevatedBorder: color, + + roundnessSmall: f32, + roundnessMedium: f32, + roundnessLarge: f32, + + palette: ^ui_palette, +} + +@export UI_DARK_THEME: ui_theme +@export UI_LIGHT_THEME: ui_theme + +ui_tag :: struct { + hash: u64, +} + +ui_selector_kind :: enum c.int { + ANY, + OWNER, + TEXT, + TAG, + STATUS, + KEY, +} + +ui_status :: enum u8 { + NONE = 0, + HOVER = 1 << 1, + HOT = 1 << 2, + ACTIVE = 1 << 3, + DRAGGING = 1 << 4, +} + +ui_selector_op :: enum c.int { + DESCENDANT = 0, + AND = 1, +} + +ui_selector :: struct { + listElt: list_elt, + kind: ui_selector_kind, + op: ui_selector_op, + + type: struct #raw_union { + text: str8, + key: ui_key, + tag: ui_tag, + status: ui_status, + } +} + +ui_pattern :: struct { + l: list, +} + +ui_style_rule :: struct { + boxElt: list_elt, + buildElt: list_elt, + tmpElt: list_elt, + + owner: ^ui_box, + pattern: ui_pattern, + mask: ui_style_mask, + style: ^ui_style, +} + +ui_sig :: struct { + box: ^ui_box, + + mouse: vec2, + delta: vec2, + wheel: vec2, + + pressed: c.bool, + released: c.bool, + clicked: c.bool, + doubleClicked: c.bool, + tripleClicked: c.bool, + rightPressed: c.bool, + + dragging: c.bool, + hovering: c.bool, + + pasted: c.bool, + +} + +ui_box_draw_proc :: proc "c" (box: ^ui_box, data: rawptr) + +ui_flags :: enum c.int { + NONE = 0, + CLICKABLE = (1 << 0), + SCROLL_WHEEL_X = (1 << 1), + SCROLL_WHEEL_Y = (1 << 2), + BLOCK_MOUSE = (1 << 3), + HOT_ANIMATION = (1 << 4), + ACTIVE_ANIMATION = (1 << 5), + //WARN: these two following flags need to be kept as consecutive bits to + // play well with axis-agnostic functions + OVERFLOW_ALLOW_X = (1 << 6), + OVERFLOW_ALLOW_Y = (1 << 7), + CLIP = (1 << 8), + DRAW_BACKGROUND = (1 << 9), + DRAW_FOREGROUND = (1 << 10), + DRAW_BORDER = (1 << 11), + DRAW_TEXT = (1 << 12), + DRAW_PROC = (1 << 13), + + OVERLAY = (1 << 16), +} + +ui_box :: struct { + // hierarchy + listElt: list_elt, + children: list, + parent: ^ui_box, + + overlayElt: list_elt, + + // keying and caching + bucketElt: list_elt, + key: ui_key, + frameCounter: u64, + + // builder-provided info + flags: ui_flags, + string: str8, + tags: list, + + drawProc: ui_box_draw_proc, + drawData: rawptr, + + // styling + beforeRules: list, + afterRules: list, + + //ui_style_tag tag + targetStyle: ^ui_style, + style: ui_style, + z: u32, + + floatPos: vec2, + childrenSum: [2]f32, + spacing: [2]f32, + minSize: [2]f32, + rect: rect, + + // signals + sig: ^ui_sig, + + // stateful behaviour + fresh: c.bool, + closed: c.bool, + parentClosed: c.bool, + dragging: c.bool, + hot: c.bool, + active: c.bool, + scroll: vec2, + pressedMouse: vec2, + + // animation data + hotTransition: f32, + activeTransition: f32, +} + +UI_MAX_INPUT_CHAR_PER_FRAME :: 64 + +ui_input_text :: struct { + count: u8, + codePoints: [UI_MAX_INPUT_CHAR_PER_FRAME]utf32, +} + +ui_stack_elt :: struct { + parent: ^ui_stack_elt, + + _: struct #raw_union { + box: ^ui_box, + size: ui_size, + clip: rect, + } +} + +ui_tag_elt :: struct { + listElt: list_elt, + tag: ui_tag, +} + +UI_BOX_MAP_BUCKET_COUNT :: 1024 + +ui_edit_move :: enum c.int { + NONE, + CHAR, + WORD, + LINE, +} + +ui_context :: struct { + init: c.bool, + + input: input_state, + + frameCounter: u64, + frameTime: f64, + lastFrameDuration: f64, + + frameArena: arena, + boxPool: pool, + boxMap: [UI_BOX_MAP_BUCKET_COUNT]list, + + root: ^ui_box, + overlay: ^ui_box, + overlayList: list, + boxStack: ^ui_stack_elt, + clipStack: ^ui_stack_elt, + + nextBoxBeforeRules: list, + nextBoxAfterRules: list, + nextBoxTags: list, + + z: u32, + hovered: ^ui_box, + + focus: ^ui_box, + editCursor: i32, + editMark: i32, + editFirstDisplayedChar: i32, + editCursorBlinkStart: f64, + editSelectionMode: ui_edit_move, + editWordSelectionInitialCursor: i32, + editWordSelectionInitialMark: i32, + + clipboardRegistered: c.bool, + + theme: ^ui_theme, +} + +ui_text_box_result :: struct { + changed: c.bool, + accepted: c.bool, + text: str8, +} + +ui_select_popup_info :: struct { + changed: bool, + selectedIndex: int, // -1 if nothing is selected + optionCount: int, + options: [^]str8, + placeholder: str8, +} + +ui_radio_group_info :: struct { + changed: bool, + selectedIndex: int, // -1 if nothing is selected + optionCount: int, + options: [^]str8, +} + +//---------------------------------------------------------------- +// Context and frame lifecycle +//---------------------------------------------------------------- + +@(default_calling_convention="c", link_prefix="oc_") +foreign { + ui_init :: proc(ctx: ^ui_context) --- + ui_get_context :: proc() -> ^ui_context --- + ui_set_context :: proc(ctx: ^ui_context) --- + + ui_process_event :: proc(event: ^event) --- + ui_begin_frame :: proc(size: vec2, defaultStyle: ^ui_style, mask: ui_style_mask) --- + ui_end_frame :: proc() --- + ui_draw :: proc() --- +} + +@(deferred_none=ui_end_frame) +ui_frame :: proc "c" (size: vec2, defaultStyle: ^ui_style, mask: ui_style_mask) { + ui_begin_frame(size, defaultStyle, mask) +} +ui_frame_scoped :: ui_frame + +//---------------------------------------------------------------- +// Common widget helpers +//---------------------------------------------------------------- +@(default_calling_convention="c", link_prefix="oc_") +foreign { + ui_label :: proc(label: cstring) -> ui_sig --- + ui_label_str8 :: proc(label: str8) -> ui_sig --- + ui_button :: proc(label: cstring) -> ui_sig --- + ui_checkbox :: proc(name: cstring, checked: ^c.bool) -> ui_sig --- + ui_slider :: proc(label: cstring, value: ^f32) -> ^ui_box --- + ui_scrollbar :: proc(label: cstring, thumbRatio: f32, scrollValue: ^f32) -> ^ui_box --- + ui_text_box :: proc(name: cstring, arena: ^arena, text: str8) -> ui_text_box_result --- + ui_select_popup :: proc(name: cstring, info: ^ui_select_popup_info) -> ui_select_popup_info --- + ui_radio_group :: proc(name: cstring, info: ^ui_radio_group_info) -> ui_radio_group_info --- + + ui_panel_begin :: proc(name: cstring, flags: ui_flags) --- + ui_panel_end :: proc() --- + + ui_menu_bar_begin :: proc(label: cstring) --- + ui_menu_bar_end :: proc() --- + + ui_menu_begin :: proc(label: cstring) --- + ui_menu_end :: proc() --- + + ui_menu_button :: proc(name: cstring) -> ui_sig --- + + ui_tooltip_begin :: proc(name: cstring) -> ui_sig --- + ui_tooltip_end :: proc() --- +} + +@(deferred_none=ui_panel_end) +ui_panel :: proc "c" (name: cstring, flags: ui_flags) { + ui_panel_begin(name, flags) +} +ui_panel_scoped :: ui_panel + +@(deferred_none=ui_menu_bar_end) +ui_menu_bar :: proc "c" (label: cstring) { + ui_menu_bar_begin(label) +} +ui_menu_bar_scoped :: ui_menu_bar + +@(deferred_none=ui_menu_end) +ui_menu :: proc "c" (label: cstring) { + ui_menu_begin(label) +} +ui_menu_scoped :: ui_menu + +@(deferred_none=ui_tooltip_end) +ui_tooltip :: proc "c" (label: cstring) -> ui_sig { + return ui_tooltip_begin(label) +} +ui_tooltip_scoped :: ui_menu + +//------------------------------------------------------------------------------------- +// Styling +//------------------------------------------------------------------------------------- +@(default_calling_convention="c", link_prefix="oc_") +foreign { + ui_style_next :: proc(style: ^ui_style, mask: ui_style_mask) --- + + ui_pattern_push :: proc(arena: ^arena, pattern: ^ui_pattern, selector: ui_selector) --- + ui_pattern_all :: proc() -> ui_pattern --- + ui_pattern_owner :: proc() -> ui_pattern --- + + ui_style_match_before :: proc(pattern: ui_pattern, style: ^ui_style, mask: ui_style_mask) --- + ui_style_match_after :: proc(pattern: ui_pattern, style: ^ui_style, mask: ui_style_mask) --- +} + +//------------------------------------------------------------------------------------- +// BOX +//------------------------------------------------------------------------------------- + +@(default_calling_convention="c", link_prefix="oc_") +foreign { + ui_box_make_str8 :: proc(str: str8, flags: ui_flags) -> ^ui_box --- + ui_box_begin_str8 :: proc(str: str8, flags: ui_flags) -> ^ui_box --- + ui_box_end :: proc() -> ^ui_box --- +} + +@(deferred_none=ui_box_end) +ui_container :: proc "c" (str: string, flags: ui_flags) -> ^ui_box { + return ui_box_begin_str8(str, flags) +} + +@(deferred_none=ui_box_end) +ui_container_str8 :: proc "c" (str: str8, flags: ui_flags) -> ^ui_box { + return ui_box_begin_str8(str, flags) +} + +ui_box_make :: proc "c" (str: string, flags: ui_flags) -> ^ui_box { + return ui_box_make_str8(str, flags) +} + +ui_box_begin :: proc "c" (str: string, flags: ui_flags) -> ^ui_box { + return ui_box_begin_str8(str, flags) +} + +//------------------------------------------------------------------------------------- +// BOX +//------------------------------------------------------------------------------------- + +@(default_calling_convention="c", link_prefix="oc_") +foreign { + ui_tag_make_str8 :: proc(str: str8) -> ui_tag --- + ui_tag_box_str8 :: proc(box: ^ui_box, str: str8) --- + ui_tag_next_str8 :: proc(str: str8) --- +} + +ui_tag_make :: proc "c" (s: string) -> ui_tag { + return ui_tag_make_str8(s) +} + +ui_tag_box :: proc "c" (b: ^ui_box, s: string) { + ui_tag_box_str8(b, s) +} + +ui_tag_next :: proc "c" (s: string) { + ui_tag_next_str8(s) +} diff --git a/core/sys/orca/utf8.odin b/core/sys/orca/utf8.odin new file mode 100644 index 000000000..5b86f0c19 --- /dev/null +++ b/core/sys/orca/utf8.odin @@ -0,0 +1,131 @@ +package orca + +unicode_range :: struct { + firstCodePoint: utf32, + count: u32, +} + +UNICODE_BASIC_LATIN:: unicode_range { 0x0000, 127 } +UNICODE_C1_CONTROLS_AND_LATIN_1_SUPPLEMENT:: unicode_range { 0x0080, 127 } +UNICODE_LATIN_EXTENDED_A:: unicode_range { 0x0100, 127 } +UNICODE_LATIN_EXTENDED_B:: unicode_range { 0x0180, 207 } +UNICODE_IPA_EXTENSIONS:: unicode_range { 0x0250, 95 } +UNICODE_SPACING_MODIFIER_LETTERS:: unicode_range { 0x02b0, 79 } +UNICODE_COMBINING_DIACRITICAL_MARKS:: unicode_range { 0x0300, 111 } +UNICODE_GREEK_COPTIC:: unicode_range { 0x0370, 143 } +UNICODE_CYRILLIC:: unicode_range { 0x0400, 255 } +UNICODE_CYRILLIC_SUPPLEMENT:: unicode_range { 0x0500, 47 } +UNICODE_ARMENIAN:: unicode_range { 0x0530, 95 } +UNICODE_HEBREW:: unicode_range { 0x0590, 111 } +UNICODE_ARABIC:: unicode_range { 0x0600, 255 } +UNICODE_SYRIAC:: unicode_range { 0x0700, 79 } +UNICODE_THAANA:: unicode_range { 0x0780, 63 } +UNICODE_DEVANAGARI:: unicode_range { 0x0900, 127 } +UNICODE_BENGALI_ASSAMESE:: unicode_range { 0x0980, 127 } +UNICODE_GURMUKHI:: unicode_range { 0x0a00, 127 } +UNICODE_GUJARATI:: unicode_range { 0x0a80, 127 } +UNICODE_ORIYA:: unicode_range { 0x0b00, 127 } +UNICODE_TAMIL:: unicode_range { 0x0b80, 127 } +UNICODE_TELUGU:: unicode_range { 0x0c00, 127 } +UNICODE_KANNADA:: unicode_range { 0x0c80, 127 } +UNICODE_MALAYALAM:: unicode_range { 0x0d00, 255 } +UNICODE_SINHALA:: unicode_range { 0x0d80, 127 } +UNICODE_THAI:: unicode_range { 0x0e00, 127 } +UNICODE_LAO:: unicode_range { 0x0e80, 127 } +UNICODE_TIBETAN:: unicode_range { 0x0f00, 255 } +UNICODE_MYANMAR:: unicode_range { 0x1000, 159 } +UNICODE_GEORGIAN:: unicode_range { 0x10a0, 95 } +UNICODE_HANGUL_JAMO:: unicode_range { 0x1100, 255 } +UNICODE_ETHIOPIC:: unicode_range { 0x1200, 383 } +UNICODE_CHEROKEE:: unicode_range { 0x13a0, 95 } +UNICODE_UNIFIED_CANADIAN_ABORIGINAL_SYLLABICS:: unicode_range { 0x1400, 639 } +UNICODE_OGHAM:: unicode_range { 0x1680, 31 } +UNICODE_RUNIC:: unicode_range { 0x16a0, 95 } +UNICODE_TAGALOG:: unicode_range { 0x1700, 31 } +UNICODE_HANUNOO:: unicode_range { 0x1720, 31 } +UNICODE_BUHID:: unicode_range { 0x1740, 31 } +UNICODE_TAGBANWA:: unicode_range { 0x1760, 31 } +UNICODE_KHMER:: unicode_range { 0x1780, 127 } +UNICODE_MONGOLIAN:: unicode_range { 0x1800, 175 } +UNICODE_LIMBU:: unicode_range { 0x1900, 79 } +UNICODE_TAI_LE:: unicode_range { 0x1950, 47 } +UNICODE_KHMER_SYMBOLS:: unicode_range { 0x19e0, 31 } +UNICODE_PHONETIC_EXTENSIONS:: unicode_range { 0x1d00, 127 } +UNICODE_LATIN_EXTENDED_ADDITIONAL:: unicode_range { 0x1e00, 255 } +UNICODE_GREEK_EXTENDED:: unicode_range { 0x1f00, 255 } +UNICODE_GENERAL_PUNCTUATION:: unicode_range { 0x2000, 111 } +UNICODE_SUPERSCRIPTS_AND_SUBSCRIPTS:: unicode_range { 0x2070, 47 } +UNICODE_CURRENCY_SYMBOLS:: unicode_range { 0x20a0, 47 } +UNICODE_COMBINING_DIACRITICAL_MARKS_FOR_SYMBOLS:: unicode_range { 0x20d0, 47 } +UNICODE_LETTERLIKE_SYMBOLS:: unicode_range { 0x2100, 79 } +UNICODE_NUMBER_FORMS:: unicode_range { 0x2150, 63 } +UNICODE_ARROWS:: unicode_range { 0x2190, 111 } +UNICODE_MATHEMATICAL_OPERATORS:: unicode_range { 0x2200, 255 } +UNICODE_MISCELLANEOUS_TECHNICAL:: unicode_range { 0x2300, 255 } +UNICODE_CONTROL_PICTURES:: unicode_range { 0x2400, 63 } +UNICODE_OPTICAL_CHARACTER_RECOGNITION:: unicode_range { 0x2440, 31 } +UNICODE_ENCLOSED_ALPHANUMERICS:: unicode_range { 0x2460, 159 } +UNICODE_BOX_DRAWING:: unicode_range { 0x2500, 127 } +UNICODE_BLOCK_ELEMENTS:: unicode_range { 0x2580, 31 } +UNICODE_GEOMETRIC_SHAPES:: unicode_range { 0x25a0, 95 } +UNICODE_MISCELLANEOUS_SYMBOLS:: unicode_range { 0x2600, 255 } +UNICODE_DINGBATS:: unicode_range { 0x2700, 191 } +UNICODE_MISCELLANEOUS_MATHEMATICAL_SYMBOLS_A:: unicode_range { 0x27c0, 47 } +UNICODE_SUPPLEMENTAL_ARROWS_A:: unicode_range { 0x27f0, 15 } +UNICODE_BRAILLE_PATTERNS:: unicode_range { 0x2800, 255 } +UNICODE_SUPPLEMENTAL_ARROWS_B:: unicode_range { 0x2900, 127 } +UNICODE_MISCELLANEOUS_MATHEMATICAL_SYMBOLS_B:: unicode_range { 0x2980, 127 } +UNICODE_SUPPLEMENTAL_MATHEMATICAL_OPERATORS:: unicode_range { 0x2a00, 255 } +UNICODE_MISCELLANEOUS_SYMBOLS_AND_ARROWS:: unicode_range { 0x2b00, 255 } +UNICODE_CJK_RADICALS_SUPPLEMENT:: unicode_range { 0x2e80, 127 } +UNICODE_KANGXI_RADICALS:: unicode_range { 0x2f00, 223 } +UNICODE_IDEOGRAPHIC_DESCRIPTION_CHARACTERS:: unicode_range { 0x2ff0, 15 } +UNICODE_CJK_SYMBOLS_AND_PUNCTUATION:: unicode_range { 0x3000, 63 } +UNICODE_HIRAGANA:: unicode_range { 0x3040, 95 } +UNICODE_KATAKANA:: unicode_range { 0x30a0, 95 } +UNICODE_BOPOMOFO:: unicode_range { 0x3100, 47 } +UNICODE_HANGUL_COMPATIBILITY_JAMO:: unicode_range { 0x3130, 95 } +UNICODE_KANBUN_KUNTEN:: unicode_range { 0x3190, 15 } +UNICODE_BOPOMOFO_EXTENDED:: unicode_range { 0x31a0, 31 } +UNICODE_KATAKANA_PHONETIC_EXTENSIONS:: unicode_range { 0x31f0, 15 } +UNICODE_ENCLOSED_CJK_LETTERS_AND_MONTHS:: unicode_range { 0x3200, 255 } +UNICODE_CJK_COMPATIBILITY:: unicode_range { 0x3300, 255 } +UNICODE_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A:: unicode_range { 0x3400, 6591 } +UNICODE_YIJING_HEXAGRAM_SYMBOLS:: unicode_range { 0x4dc0, 63 } +UNICODE_CJK_UNIFIED_IDEOGRAPHS:: unicode_range { 0x4e00, 20911 } +UNICODE_YI_SYLLABLES:: unicode_range { 0xa000, 1167 } +UNICODE_YI_RADICALS:: unicode_range { 0xa490, 63 } +UNICODE_HANGUL_SYLLABLES:: unicode_range { 0xac00, 11183 } +UNICODE_HIGH_SURROGATE_AREA:: unicode_range { 0xd800, 1023 } +UNICODE_LOW_SURROGATE_AREA:: unicode_range { 0xdc00, 1023 } +UNICODE_PRIVATE_USE_AREA:: unicode_range { 0xe000, 6399 } +UNICODE_CJK_COMPATIBILITY_IDEOGRAPHS:: unicode_range { 0xf900, 511 } +UNICODE_ALPHABETIC_PRESENTATION_FORMS:: unicode_range { 0xfb00, 79 } +UNICODE_ARABIC_PRESENTATION_FORMS_A:: unicode_range { 0xfb50, 687 } +UNICODE_VARIATION_SELECTORS:: unicode_range { 0xfe00, 15 } +UNICODE_COMBINING_HALF_MARKS:: unicode_range { 0xfe20, 15 } +UNICODE_CJK_COMPATIBILITY_FORMS:: unicode_range { 0xfe30, 31 } +UNICODE_SMALL_FORM_VARIANTS:: unicode_range { 0xfe50, 31 } +UNICODE_ARABIC_PRESENTATION_FORMS_B:: unicode_range { 0xfe70, 143 } +UNICODE_HALFWIDTH_AND_FULLWIDTH_FORMS:: unicode_range { 0xff00, 239 } +UNICODE_SPECIALS:: unicode_range { 0xfff0, 15 } +UNICODE_LINEAR_B_SYLLABARY:: unicode_range { 0x10000, 127 } +UNICODE_LINEAR_B_IDEOGRAMS:: unicode_range { 0x10080, 127 } +UNICODE_AEGEAN_NUMBERS:: unicode_range { 0x10100, 63 } +UNICODE_OLD_ITALIC:: unicode_range { 0x10300, 47 } +UNICODE_GOTHIC:: unicode_range { 0x10330, 31 } +UNICODE_UGARITIC:: unicode_range { 0x10380, 31 } +UNICODE_DESERET:: unicode_range { 0x10400, 79 } +UNICODE_SHAVIAN:: unicode_range { 0x10450, 47 } +UNICODE_OSMANYA:: unicode_range { 0x10480, 47 } +UNICODE_CYPRIOT_SYLLABARY:: unicode_range { 0x10800, 63 } +UNICODE_BYZANTINE_MUSICAL_SYMBOLS:: unicode_range { 0x1d000, 255 } +UNICODE_MUSICAL_SYMBOLS:: unicode_range { 0x1d100, 255 } +UNICODE_TAI_XUAN_JING_SYMBOLS:: unicode_range { 0x1d300, 95 } +UNICODE_MATHEMATICAL_ALPHANUMERIC_SYMBOLS:: unicode_range { 0x1d400, 1023 } +UNICODE_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B:: unicode_range { 0x20000, 42719 } +UNICODE_CJK_COMPATIBILITY_IDEOGRAPHS_SUPPLEMENT:: unicode_range { 0x2f800, 543 } +UNICODE_TAGS:: unicode_range { 0xe0000, 127 } +UNICODE_VARIATION_SELECTORS_SUPPLEMENT:: unicode_range { 0xe0100, 239 } +UNICODE_SUPPLEMENTARY_PRIVATE_USE_AREA_A:: unicode_range { 0xf0000, 65533 } +UNICODE_SUPPLEMENTARY_PRIVATE_USE_AREA_B :: unicode_range { 0x100000, 65533 } diff --git a/core/sys/orca/util.odin b/core/sys/orca/util.odin new file mode 100644 index 000000000..4d01234bf --- /dev/null +++ b/core/sys/orca/util.odin @@ -0,0 +1,266 @@ +package orca + +import "core:c" +import "core:fmt" +import "core:runtime" +import "core:intrinsics" + +//---------------------------------------------------------------- +// Arenas +//---------------------------------------------------------------- + +mem_reserve_proc :: proc "c" (ctx: ^base_allocator, size: u64) +mem_modify_proc :: proc "c" (ctx: ^base_allocator, ptr: rawptr, size: u64) + +base_allocator :: struct { + reserve: mem_reserve_proc, + commit: mem_modify_proc, + decommit: mem_modify_proc, + release: mem_modify_proc, +} + +arena_chunk :: struct { + listElt: list_elt, + ptr: ^c.char, + offset: u64, + committed: u64, + cap: u64, +} + +arena :: struct { + base: ^base_allocator, + chunks: list, + currentChunk: ^arena_chunk, +} + +arena_scope :: struct { + arena: ^arena, + chunk: ^arena_chunk, + offset: u64, +} + +arena_options :: struct { + base: ^base_allocator, + reserve: u64, +} + +@(default_calling_convention="c", link_prefix="oc_") +foreign { + arena_init :: proc(arena: ^arena) --- + arena_init_with_options :: proc(arena: ^arena, options: ^arena_options) --- + arena_cleanup :: proc(arena: ^arena) --- + + arena_push :: proc(arena: ^arena, size: u64) -> rawptr --- + arena_clear :: proc(arena: ^arena) --- + + arena_scope_begin :: proc(arena: ^arena) -> arena_scope --- + arena_scope_end :: proc(scope: arena_scope) --- + + scratch_begin :: proc() -> arena_scope --- + scratch_begin_next :: proc(used: ^arena) -> arena_scope --- +} + +arena_push_type :: proc "c" (arena: ^arena, $T: typeid) -> ^T { + return cast(^T) arena_push(arena, size_of(T)) +} + +arena_push_array :: proc "c" (arena: ^arena, $T: typeid, count: int) -> []T { + return ([^]T)(arena_push(arena, size_of(T)))[:count] +} + +scratch_end :: arena_scope_end + +//---------------------------------------------------------------- +// Pool +//---------------------------------------------------------------- + +pool :: struct { + arena: arena, + freeList: list, + blockSize: u64, +} + +pool_options :: struct { + base: ^base_allocator, + reserve: u64, +} + +@(default_calling_convention="c", link_prefix="oc_") +foreign { + pool_init :: proc(pool: ^pool, blockSize: u64) --- + pool_init_with_options :: proc(pool: ^pool, blockSize: u64, options: ^pool_options) --- + pool_cleanup :: proc(pool: ^pool) --- + + pool_alloc :: proc(pool: ^pool) -> rawptr --- + pool_recycle :: proc(pool: ^pool, ptr: rawptr) --- + pool_clear :: proc(pool: ^pool) --- +} + +pool_alloc_type :: proc "c" (arena: ^arena, $T: typeid) -> ^T { + return cast(^T) pool_alloc(arena) +} + +// TODO support list macros? +// #define list_entry :: proc(ptr, type, member) +// #define list_next_entry :: proc(list, elt, type, member) +// #define list_prev_entry :: proc(list, elt, type, member) +// #define list_first_entry :: proc(list, type, member) +// #define list_last_entry :: proc(list, type, member) +// #define list_pop_entry :: proc(list, type, member) + +// @(default_calling_convention="c", link_prefix="oc_") +// foreign { +// list_init :: proc(list: ^list) --- +// list_empty :: proc(list: ^list) -> c.bool --- + +// list_begin :: proc(list: ^list) -> ^list_elt --- +// list_end :: proc(list: ^list) -> ^list_elt --- +// list_last :: proc(list: ^list) -> ^list_elt --- + +// list_insert :: proc(list: ^list, afterElt: ^list_elt, elt: ^list_elt) --- +// list_insert_before :: proc(list: ^list, beforeElt: ^list_elt, elt: ^list_elt) --- +// list_remove :: proc(list: ^list, elt: ^list_elt) --- +// list_push :: proc(list: ^list, elt: ^list_elt) --- +// list_pop :: proc(list: ^list) -> ^list_elt --- +// list_push_back :: proc(list: ^list, elt: ^list_elt) --- +// list_pop_back :: proc(list: ^list) -> ^list_elt --- +// } + +//------------------------------------------------------------------------------------------ +// for iterators +//------------------------------------------------------------------------------------------ + +List_Iterator :: struct($T: typeid) { + iterate: ^list, + curr: ^list_elt, + index: int, + offset: uintptr, +} + +// NOTE(Skytrias): intrusive list iterator +list_iter_init :: proc "c" (iterate: ^list, $T: typeid, $field_name: string) -> (res: List_Iterator(T)) + where intrinsics.type_has_field(T, field_name), + intrinsics.type_field_type(T, field_name) == list_elt { + res.iterate = iterate + res.curr = list_begin(iterate) + res.offset = offset_of_by_string(T, field_name) + return +} + +list_iterate :: proc "c" (iter: ^List_Iterator($T)) -> (ptr: ^T, ok: bool) { + node := iter.curr + if node == nil { + return nil, false + } + iter.index += 1 + iter.curr = node.next + return (^T)(uintptr(node) - iter.offset), true +} + +//---------------------------------------------------------------- +// Strings / string lists / path strings +//---------------------------------------------------------------- + +// TODO use odin cstring when ^c.char is used? + +str8 :: string +str32 :: []rune + +str8_list :: struct { + list: list, + eltCount: u64, + len: u64, +} + +str8_elt :: struct { + str: str8, + listElt: list_elt, +} + +@(default_calling_convention="c", link_prefix="oc_") +foreign { + str8_push_buffer :: proc(arena: ^arena, len: u64, buffer: ^c.char) -> str8 --- + str8_push_cstring :: proc(arena: ^arena, str: ^c.char) -> str8 --- + str8_push_copy :: proc(arena: ^arena, s: str8) -> str8 --- + str8_push_slice :: proc(arena: ^arena, s: str8, start: u64, end: u64) -> str8 --- + + // TODO get rid of these or wrap them + str8_pushfv :: proc(arena: ^arena, format: cstring, args: c.va_list) -> str8 --- + str8_pushf :: proc(arena: ^arena, format: cstring, #c_vararg args: ..any) -> str8 --- + + str8_to_cstring :: proc(arena: ^arena, string: str8) -> ^c.char --- + + str8_list_push :: proc(arena: ^arena, list: ^str8_list, str: str8) --- + str8_list_pushf :: proc(arena: ^arena, list: ^str8_list, format: cstring, #c_vararg args: ..any) --- + + str8_list_collate :: proc(arena: ^arena, list: str8_list, prefix: str8, separator: str8, postfix: str8) -> str8 --- + str8_list_join :: proc(arena: ^arena, list: str8_list) -> str8 --- + str8_split :: proc(arena: ^arena, str: str8, separators: str8_list) -> str8_list --- + + path_slice_directory :: proc(path: str8) -> str8 --- + path_slice_filename :: proc(path: str8) -> str8 --- + path_split :: proc(arena: ^arena, path: str8) -> str8_list --- + path_join :: proc(arena: ^arena, elements: str8_list) -> str8 --- + path_append :: proc(arena: ^arena, parent: str8, relPath: str8) -> str8 --- + path_is_absolute :: proc(path: str8) -> bool --- +} + +//---------------------------------------------------------------- +// Logging +//---------------------------------------------------------------- + +// TODO proper odin formatted strings + +log_level :: enum c.int { + ERROR, + WARNING, + INFO, +} + +@(default_calling_convention="c", link_prefix="oc_") +foreign { + log_ext :: proc( + level: log_level, + function: cstring, + file: cstring, + line: c.int, + fmt: cstring, + #c_vararg args: ..any, + ) --- +} + +log_proc: [1028]u8 +log_file: [1028]u8 + +log_temp :: proc "c" (loc: runtime.Source_Code_Location) -> (function, file: cstring) { + copy(log_proc[:], loc.procedure) + log_proc[len(loc.procedure)] = 0 + function = cstring(&log_proc[0]) + + copy(log_file[:], loc.file_path) + log_file[len(loc.file_path)] = 0 + file = cstring(&log_file[0]) + + return +} + +log_info :: proc "c" (format: cstring, args: ..any, loc := #caller_location) { + function, file := log_temp(loc) + // final := fmt.ctprintf(format, ..args) + // log_ext(.INFO, function, file, loc.line, final, {}) + log_ext(.INFO, function, file, loc.line, format, {}) +} + +log_warning :: proc "c" (format: cstring, args: ..any, loc := #caller_location) { + function, file := log_temp(loc) + // final := fmt.ctprintf(format, ..args) + // log_ext(.WARNING, function, file, loc.line, final, {}) + log_ext(.WARNING, function, file, loc.line, format, {}) +} + +log_error :: proc "c" (format: cstring, args: ..any, loc := #caller_location) { + function, file := log_temp(loc) + // final := fmt.ctprintf(format, ..args) + // log_ext(.ERROR, function, file, loc.line, final, {}) + log_ext(.ERROR, function, file, loc.line, format, {}) +} diff --git a/core/sys/wasm/wasi/wasi_api.odin b/core/sys/wasm/wasi/wasi_api.odin index e9ceb4667..25733a2b3 100644 --- a/core/sys/wasm/wasi/wasi_api.odin +++ b/core/sys/wasm/wasi/wasi_api.odin @@ -1,7 +1,11 @@ //+build wasm32 package sys_wasi -foreign import wasi "wasi_snapshot_preview1" +when ODIN_OS == .Orca { + foreign import wasi "wasi" +} else { + foreign import wasi "wasi_snapshot_preview1" +} DIRCOOKIE_START :: u64(0) size_t :: uint diff --git a/core/time/time_orca.odin b/core/time/time_orca.odin new file mode 100644 index 000000000..49f8a58ce --- /dev/null +++ b/core/time/time_orca.odin @@ -0,0 +1,26 @@ +//+private +//+build orca +package time + +import wasi "core:sys/wasm/wasi" + +_IS_SUPPORTED :: false + +_now :: proc "contextless" () -> Time { + return {} +} + +_sleep :: proc "contextless" (d: Duration) { +} + +_tick_now :: proc "contextless" () -> Tick { + // mul_div_u64 :: proc "contextless" (val, num, den: i64) -> i64 { + // q := val / den + // r := val % den + // return q * num + r * num / den + // } + return {} +} + +_yield :: proc "contextless" () { +} diff --git a/src/build_settings.cpp b/src/build_settings.cpp index fdaa971f1..fe61903ae 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -22,6 +22,7 @@ enum TargetOsKind : u16 { TargetOs_wasi, TargetOs_js, + TargetOs_orca, TargetOs_freestanding, @@ -83,6 +84,7 @@ gb_global String target_os_names[TargetOs_COUNT] = { str_lit("wasi"), str_lit("js"), + str_lit("orca"), str_lit("freestanding"), }; @@ -583,6 +585,14 @@ gb_global TargetMetrics target_wasi_wasm32 = { str_lit("e-m:e-p:32:32-i64:64-n32:64-S128"), }; +gb_global TargetMetrics target_orca_wasm32 = { + TargetOs_orca, + TargetArch_wasm32, + 4, 4, 8, 16, + str_lit("wasm32-wasi-js"), + str_lit("e-m:e-p:32:32-i64:64-n32:64-S128"), +}; + gb_global TargetMetrics target_freestanding_wasm64p32 = { TargetOs_freestanding, @@ -655,6 +665,7 @@ gb_global NamedTargetMetrics named_targets[] = { { str_lit("freestanding_wasm32"), &target_freestanding_wasm32 }, { str_lit("wasi_wasm32"), &target_wasi_wasm32 }, { str_lit("js_wasm32"), &target_js_wasm32 }, + { str_lit("orca_wasm32"), &target_orca_wasm32 }, { str_lit("freestanding_wasm64p32"), &target_freestanding_wasm64p32 }, { str_lit("js_wasm64p32"), &target_js_wasm64p32 }, @@ -1513,9 +1524,12 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta // if (bc->metrics.arch == TargetArch_wasm64) { // link_flags = gb_string_appendc(link_flags, "-mwasm64 "); // } - if (bc->no_entry_point) { + if (bc->no_entry_point || bc->metrics.os == TargetOs_orca) { link_flags = gb_string_appendc(link_flags, "--no-entry "); - } + + // in case orca target was + bc->no_entry_point = true; + } bc->link_flags = make_string_c(link_flags); diff --git a/src/checker.cpp b/src/checker.cpp index 72c0ae574..bf61dc4a6 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -1015,6 +1015,7 @@ gb_internal void init_universal(void) { {"WASI", TargetOs_wasi}, {"JS", TargetOs_js}, {"Freestanding", TargetOs_freestanding}, + {"Orca", TargetOs_orca}, }; auto fields = add_global_enum_type(str_lit("Odin_OS_Type"), values, gb_count_of(values)); diff --git a/src/linker.cpp b/src/linker.cpp index 0cdeaf8d9..ee5e55465 100644 --- a/src/linker.cpp +++ b/src/linker.cpp @@ -69,15 +69,20 @@ gb_internal i32 linker_stage(LinkerData *gen) { if (is_arch_wasm()) { timings_start_section(timings, str_lit("wasm-ld")); + String extra_orca_flags = {}; + if (build_context.metrics.os == TargetOs_orca) { + extra_orca_flags = str_lit(" -L . -lorca --export-dynamic"); + } + #if defined(GB_SYSTEM_WINDOWS) result = system_exec_command_line_app("wasm-ld", - "\"%.*s\\bin\\wasm-ld\" \"%.*s.o\" -o \"%.*s\" %.*s %.*s", + "\"%.*s\\bin\\wasm-ld\" \"%.*s.o\" -o \"%.*s\" %.*s %.*s %.*s", LIT(build_context.ODIN_ROOT), - LIT(output_filename), LIT(output_filename), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags)); + LIT(output_filename), LIT(output_filename), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags), LIT(extra_orca_flags)); #else result = system_exec_command_line_app("wasm-ld", - "wasm-ld \"%.*s.o\" -o \"%.*s\" %.*s %.*s", - LIT(output_filename), LIT(output_filename), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags)); + "wasm-ld \"%.*s.o\" -o \"%.*s\" %.*s %.*s %.*s", + LIT(output_filename), LIT(output_filename), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags), LIT(extra_orca_flags)); #endif return result; } |