aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorskytrias <skytrias@protonmail.com>2024-03-18 22:09:17 +0100
committerskytrias <skytrias@protonmail.com>2024-03-18 22:09:17 +0100
commitbca4c37f02879ba0b7e4f488659600da9d086d4d (patch)
tree5f92de606f3aaa09042ed6547504ca275e8f1655
parent6cb74b63ec6316c1ca030f4d828f02bb28a3b91f (diff)
push orca target and old bindings
-rw-r--r--base/runtime/heap_allocator_other.odin2
-rw-r--r--base/runtime/os_specific_orca.odin14
-rw-r--r--base/runtime/os_specific_wasi.odin1
-rw-r--r--base/runtime/procs.odin11
-rw-r--r--core/os/os_orca.odin109
-rw-r--r--core/sys/orca/algebra.odin58
-rw-r--r--core/sys/orca/app.odin366
-rw-r--r--core/sys/orca/graphics.odin246
-rw-r--r--core/sys/orca/input_state.odin59
-rw-r--r--core/sys/orca/io.odin159
-rw-r--r--core/sys/orca/lists.odin117
-rw-r--r--core/sys/orca/orca.odin32
-rw-r--r--core/sys/orca/ui.odin685
-rw-r--r--core/sys/orca/utf8.odin131
-rw-r--r--core/sys/orca/util.odin266
-rw-r--r--core/sys/wasm/wasi/wasi_api.odin6
-rw-r--r--core/time/time_orca.odin26
-rw-r--r--src/build_settings.cpp18
-rw-r--r--src/checker.cpp1
-rw-r--r--src/linker.cpp13
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;
}