Merge branch 'odin-lang:master' into master

This commit is contained in:
Antonino Simone Di Stefano
2024-09-22 23:32:22 +02:00
committed by GitHub
23 changed files with 372 additions and 25 deletions
+8
View File
@@ -98,6 +98,14 @@ when ODIN_OS == .Haiku {
ERANGE :: B_POSIX_ERROR_BASE + 17
}
when ODIN_OS == .JS {
_ :: libc
_get_errno :: proc "c" () -> ^int {
@(static) errno: int
return &errno
}
}
// Odin has no way to make an identifier "errno" behave as a function call to
// read the value, or to produce an lvalue such that you can assign a different
// error value to errno. To work around this, just expose it as a function like
+24
View File
@@ -89,6 +89,30 @@ when ODIN_OS == .Linux {
}
}
when ODIN_OS == .JS {
fpos_t :: struct #raw_union { _: [16]char, _: longlong, _: double, }
_IOFBF :: 0
_IOLBF :: 1
_IONBF :: 2
BUFSIZ :: 1024
EOF :: int(-1)
FOPEN_MAX :: 1000
FILENAME_MAX :: 4096
L_tmpnam :: 20
SEEK_SET :: 0
SEEK_CUR :: 1
SEEK_END :: 2
TMP_MAX :: 308915776
}
when ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD {
fpos_t :: distinct i64
+10
View File
@@ -10,6 +10,9 @@ when ODIN_OS == .Windows {
foreign import libc "system:c"
}
@(require)
import "base:runtime"
when ODIN_OS == .Windows {
RAND_MAX :: 0x7fff
@@ -145,6 +148,10 @@ aligned_alloc :: #force_inline proc "c" (alignment, size: size_t) -> rawptr {
_aligned_malloc :: proc(size, alignment: size_t) -> rawptr ---
}
return _aligned_malloc(size=size, alignment=alignment)
} else when ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32 {
context = runtime.default_context()
data, _ := runtime.mem_alloc_bytes(auto_cast size, auto_cast alignment)
return raw_data(data)
} else {
foreign libc {
aligned_alloc :: proc(alignment, size: size_t) -> rawptr ---
@@ -160,6 +167,9 @@ aligned_free :: #force_inline proc "c" (ptr: rawptr) {
_aligned_free :: proc(ptr: rawptr) ---
}
_aligned_free(ptr)
} else when ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32 {
context = runtime.default_context()
runtime.mem_free(ptr)
} else {
free(ptr)
}
+1
View File
@@ -12,6 +12,7 @@ when ODIN_OS == .Windows {
foreign import libc "system:c"
}
@(default_calling_convention="c")
foreign libc {
// 7.24.2 Copying functions
memcpy :: proc(s1, s2: rawptr, n: size_t) -> rawptr ---
+1 -1
View File
@@ -45,7 +45,7 @@ when ODIN_OS == .Windows {
}
}
when ODIN_OS == .Linux || ODIN_OS == .FreeBSD || ODIN_OS == .Darwin || ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD || ODIN_OS == .Haiku {
when ODIN_OS == .Linux || ODIN_OS == .FreeBSD || ODIN_OS == .Darwin || ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD || ODIN_OS == .Haiku || ODIN_OS == .JS {
@(default_calling_convention="c")
foreign libc {
// 7.27.2 Time manipulation functions
+1 -1
View File
@@ -14,7 +14,7 @@ when ODIN_OS == .Windows {
wctrans_t :: distinct wchar_t
wctype_t :: distinct ushort
} else when ODIN_OS == .Linux {
} else when ODIN_OS == .Linux || ODIN_OS == .JS {
wctrans_t :: distinct intptr_t
wctype_t :: distinct ulong
-2
View File
@@ -1,8 +1,6 @@
#+build js
package os
import "base:runtime"
foreign import "odin_env"
@(require_results)
+15
View File
@@ -0,0 +1,15 @@
# WASM on the Web
This directory is for use when targeting the `js_wasm32` target and the packages that rely on it.
The `js_wasm32` target assumes that the WASM output will be ran within a web browser rather than a standalone VM. In the VM cases, either `wasi_wasm32` or `freestanding_wasm32` should be used accordingly.
## Example for `js_wasm32`
```html
<!-- Copy `core:sys/wasm/js/odin.js` into your web server -->
<script type="text/javascript" src="odin.js"></script>
<script type="text/javascript">
odin.runWasm(pathToWasm, consolePreElement);
</script>
```
+93
View File
@@ -0,0 +1,93 @@
#+build js wasm32, js wasm64p32
package wasm_js_interface
foreign import dom_lib "odin_dom"
@(default_calling_convention="contextless")
foreign dom_lib {
get_element_value_f64 :: proc(id: string) -> f64 ---
set_element_value_f64 :: proc(id: string, value: f64) ---
get_element_key_f64 :: proc(id: string, key: string) -> f64 ---
set_element_key_f64 :: proc(id: string, key: string, value: f64) ---
set_element_value_string :: proc(id: string, value: string) ---
get_element_value_string_length :: proc(id: string) -> int ---
set_element_key_string :: proc(id: string, key: string, value: string) ---
get_element_key_string_length :: proc(id: string, key: string, ) -> int ---
device_pixel_ratio :: proc() -> f64 ---
window_set_scroll :: proc(x, y: f64) ---
}
get_element_value_string :: proc "contextless" (id: string, buf: []byte) -> string {
@(default_calling_convention="contextless")
foreign dom_lib {
@(link_name="get_element_value_string")
_get_element_value_string :: proc(id: string, buf: []byte) -> int ---
}
n := _get_element_value_string(id, buf)
return string(buf[:n])
}
get_element_key_string :: proc "contextless" (id: string, key: string, buf: []byte) -> string {
@(default_calling_convention="contextless")
foreign dom_lib {
@(link_name="get_element_key_string")
_get_element_key_string :: proc(id: string, key: string, buf: []byte) -> int ---
}
n := _get_element_key_string(id, key, buf)
return string(buf[:n])
}
get_element_min_max :: proc "contextless" (id: string) -> (min, max: f64) {
@(default_calling_convention="contextless")
foreign dom_lib {
@(link_name="get_element_min_max")
_get_element_min_max :: proc(min_max: ^[2]f64, id: string) ---
}
min_max: [2]f64
_get_element_min_max(&min_max, id)
return min_max[0], min_max[1]
}
Rect :: struct {
x, y, width, height: f64,
}
get_bounding_client_rect :: proc "contextless" (id: string) -> (rect: Rect) {
@(default_calling_convention="contextless")
foreign dom_lib {
@(link_name="get_bounding_client_rect")
_get_bounding_client_rect :: proc(rect: ^Rect, id: string) ---
}
_get_bounding_client_rect(&rect, id)
return
}
window_get_rect :: proc "contextless" () -> (rect: Rect) {
@(default_calling_convention="contextless")
foreign dom_lib {
@(link_name="window_get_rect")
_window_get_rect :: proc(rect: ^Rect) ---
}
_window_get_rect(&rect)
return
}
window_get_scroll :: proc "contextless" () -> (x, y: f64) {
@(default_calling_convention="contextless")
foreign dom_lib {
@(link_name="window_get_scroll")
_window_get_scroll :: proc(scroll: ^[2]f64) ---
}
scroll: [2]f64
_window_get_scroll(&scroll)
return scroll.x, scroll.y
}
+36
View File
@@ -0,0 +1,36 @@
#+build !js
package wasm_js_interface
import "base:runtime"
get_element_value_string :: proc "contextless" (id: string, buf: []byte) -> string {
context = runtime.default_context()
panic("vendor:wasm/js not supported on non JS targets")
}
get_element_min_max :: proc "contextless" (id: string) -> (min, max: f64) {
context = runtime.default_context()
panic("vendor:wasm/js not supported on non JS targets")
}
Rect :: struct {
x, y, width, height: f64,
}
get_bounding_client_rect :: proc "contextless" (id: string) -> (rect: Rect) {
context = runtime.default_context()
panic("vendor:wasm/js not supported on non JS targets")
}
window_get_rect :: proc "contextless" () -> (rect: Rect) {
context = runtime.default_context()
panic("vendor:wasm/js not supported on non JS targets")
}
window_get_scroll :: proc "contextless" () -> (x, y: f64) {
context = runtime.default_context()
panic("vendor:wasm/js not supported on non JS targets")
}
+418
View File
@@ -0,0 +1,418 @@
#+build js wasm32, js wasm64p32
package wasm_js_interface
foreign import dom_lib "odin_dom"
Event_Kind :: enum u32 {
Invalid,
Load,
Unload,
Error,
Resize,
Visibility_Change,
Fullscreen_Change,
Fullscreen_Error,
Click,
Double_Click,
Mouse_Move,
Mouse_Over,
Mouse_Out,
Mouse_Up,
Mouse_Down,
Key_Up,
Key_Down,
Key_Press,
Scroll,
Wheel,
Focus,
Focus_In,
Focus_Out,
Submit,
Blur,
Change,
Hash_Change,
Select,
Animation_Start,
Animation_End,
Animation_Iteration,
Animation_Cancel,
Copy,
Cut,
Paste,
// Drag,
// Drag_Start,
// Drag_End,
// Drag_Enter,
// Drag_Leave,
// Drag_Over,
// Drop,
Pointer_Cancel,
Pointer_Down,
Pointer_Enter,
Pointer_Leave,
Pointer_Move,
Pointer_Over,
Pointer_Up,
Got_Pointer_Capture,
Lost_Pointer_Capture,
Pointer_Lock_Change,
Pointer_Lock_Error,
Selection_Change,
Selection_Start,
Touch_Cancel,
Touch_End,
Touch_Move,
Touch_Start,
Transition_Start,
Transition_End,
Transition_Run,
Transition_Cancel,
Context_Menu,
Gamepad_Connected,
Gamepad_Disconnected,
Custom,
}
event_kind_string := [Event_Kind]string{
.Invalid = "",
.Load = "load",
.Unload = "unload",
.Error = "error",
.Resize = "resize",
.Visibility_Change = "visibilitychange",
.Fullscreen_Change = "fullscreenchange",
.Fullscreen_Error = "fullscreenerror",
.Click = "click",
.Double_Click = "dblclick",
.Mouse_Move = "mousemove",
.Mouse_Over = "mouseover",
.Mouse_Out = "mouseout",
.Mouse_Up = "mouseup",
.Mouse_Down = "mousedown",
.Key_Up = "keyup",
.Key_Down = "keydown",
.Key_Press = "keypress",
.Scroll = "scroll",
.Wheel = "wheel",
.Focus = "focus",
.Focus_In = "focusin",
.Focus_Out = "focusout",
.Submit = "submit",
.Blur = "blur",
.Change = "change",
.Hash_Change = "hashchange",
.Select = "select",
.Animation_Start = "animationstart",
.Animation_End = "animationend",
.Animation_Iteration = "animationiteration",
.Animation_Cancel = "animationcancel",
.Copy = "copy",
.Cut = "cut",
.Paste = "paste",
// .Drag, = "drag",
// .Drag_Start, = "dragstart",
// .Drag_End, = "dragend",
// .Drag_Enter, = "dragenter",
// .Drag_Leave, = "dragleave",
// .Drag_Over, = "dragover",
// .Drop, = "drop",
.Pointer_Cancel = "pointercancel",
.Pointer_Down = "pointerdown",
.Pointer_Enter = "pointerenter",
.Pointer_Leave = "pointerleave",
.Pointer_Move = "pointermove",
.Pointer_Over = "pointerover",
.Pointer_Up = "pointerup",
.Got_Pointer_Capture = "gotpointercapture",
.Lost_Pointer_Capture = "lostpointercapture",
.Pointer_Lock_Change = "pointerlockchange",
.Pointer_Lock_Error = "pointerlockerror",
.Selection_Change = "selectionchange",
.Selection_Start = "selectionstart",
.Transition_Start = "transitionstart",
.Transition_End = "transitionend",
.Transition_Run = "transitionrun",
.Transition_Cancel = "transitioncancel",
.Touch_Cancel = "touchcancel",
.Touch_End = "touchend",
.Touch_Move = "touchmove",
.Touch_Start = "touchstart",
.Context_Menu = "contextmenu",
.Gamepad_Connected = "gamepadconnected",
.Gamepad_Disconnected = "gamepaddisconnected",
.Custom = "?custom?",
}
Delta_Mode :: enum u32 {
Pixel = 0,
Line = 1,
Page = 2,
}
Key_Location :: enum u8 {
Standard = 0,
Left = 1,
Right = 2,
Numpad = 3,
}
KEYBOARD_MAX_KEY_SIZE :: 16
KEYBOARD_MAX_CODE_SIZE :: 16
GAMEPAD_MAX_ID_SIZE :: 64
GAMEPAD_MAX_MAPPING_SIZE :: 64
GAMEPAD_MAX_BUTTONS :: 64
GAMEPAD_MAX_AXES :: 16
Event_Target_Kind :: enum u32 {
Element = 0,
Document = 1,
Window = 2,
}
Event_Phase :: enum u8 {
None = 0,
Capturing_Phase = 1,
At_Target = 2,
Bubbling_Phase = 3,
}
Event_Option :: enum u8 {
Bubbles = 0,
Cancelable = 1,
Composed = 2,
}
Event_Options :: distinct bit_set[Event_Option; u8]
Gamepad_Button :: struct {
value: f64,
pressed: bool,
touched: bool,
}
Gamepad_State :: struct {
id: string,
mapping: string,
index: int,
connected: bool,
timestamp: f64,
button_count: int,
axis_count: int,
buttons: [GAMEPAD_MAX_BUTTONS]Gamepad_Button `fmt:"v,button_count"`,
axes: [GAMEPAD_MAX_AXES]f64 `fmt:"v,axes_count"`,
_id_len: int `fmt:"-"`,
_mapping_len: int `fmt:"-"`,
_id_buf: [GAMEPAD_MAX_ID_SIZE]byte `fmt:"-"`,
_mapping_buf: [GAMEPAD_MAX_MAPPING_SIZE]byte `fmt:"-"`,
}
Event :: struct {
kind: Event_Kind,
target_kind: Event_Target_Kind,
current_target_kind: Event_Target_Kind,
id: string,
timestamp: f64,
phase: Event_Phase,
options: Event_Options,
is_composing: bool,
is_trusted: bool,
using data: struct #raw_union #align(8) {
scroll: struct {
delta: [2]f64,
},
visibility_change: struct {
is_visible: bool,
},
wheel: struct {
delta: [3]f64,
delta_mode: Delta_Mode,
},
key: struct {
key: string,
code: string,
location: Key_Location,
ctrl: bool,
shift: bool,
alt: bool,
meta: bool,
repeat: bool,
_key_len: int `fmt:"-"`,
_code_len: int `fmt:"-"`,
_key_buf: [KEYBOARD_MAX_KEY_SIZE]byte `fmt:"-"`,
_code_buf: [KEYBOARD_MAX_KEY_SIZE]byte `fmt:"-"`,
},
mouse: struct {
screen: [2]i64,
client: [2]i64,
offset: [2]i64,
page: [2]i64,
movement: [2]i64,
ctrl: bool,
shift: bool,
alt: bool,
meta: bool,
button: i16,
buttons: bit_set[0..<16; u16],
},
gamepad: Gamepad_State,
},
user_data: rawptr,
callback: proc(e: Event),
}
@(default_calling_convention="contextless")
foreign dom_lib {
event_stop_propagation :: proc() ---
event_stop_immediate_propagation :: proc() ---
event_prevent_default :: proc() ---
dispatch_custom_event :: proc(id: string, name: string, options := Event_Options{}) -> bool ---
}
add_event_listener :: proc(id: string, kind: Event_Kind, user_data: rawptr, callback: proc(e: Event), use_capture := false) -> bool {
@(default_calling_convention="contextless")
foreign dom_lib {
@(link_name="add_event_listener")
_add_event_listener :: proc(id: string, name: string, name_code: Event_Kind, user_data: rawptr, callback: proc "odin" (Event), use_capture: bool) -> bool ---
}
// TODO: Pointer_Lock_Change etc related stuff for all different browsers
return _add_event_listener(id, event_kind_string[kind], kind, user_data, callback, use_capture)
}
remove_event_listener :: proc(id: string, kind: Event_Kind, user_data: rawptr, callback: proc(e: Event)) -> bool {
@(default_calling_convention="contextless")
foreign dom_lib {
@(link_name="remove_event_listener")
_remove_event_listener :: proc(id: string, name: string, user_data: rawptr, callback: proc "odin" (Event)) -> bool ---
}
return _remove_event_listener(id, event_kind_string[kind], user_data, callback)
}
add_window_event_listener :: proc(kind: Event_Kind, user_data: rawptr, callback: proc(e: Event), use_capture := false) -> bool {
@(default_calling_convention="contextless")
foreign dom_lib {
@(link_name="add_window_event_listener")
_add_window_event_listener :: proc(name: string, name_code: Event_Kind, user_data: rawptr, callback: proc "odin" (Event), use_capture: bool) -> bool ---
}
return _add_window_event_listener(event_kind_string[kind], kind, user_data, callback, use_capture)
}
remove_window_event_listener :: proc(kind: Event_Kind, user_data: rawptr, callback: proc(e: Event)) -> bool {
@(default_calling_convention="contextless")
foreign dom_lib {
@(link_name="remove_window_event_listener")
_remove_window_event_listener :: proc(name: string, user_data: rawptr, callback: proc "odin" (Event)) -> bool ---
}
return _remove_window_event_listener(event_kind_string[kind], user_data, callback)
}
remove_event_listener_from_event :: proc(e: Event) -> bool {
if e.id == "" {
return remove_window_event_listener(e.kind, e.user_data, e.callback)
}
return remove_event_listener(e.id, e.kind, e.user_data, e.callback)
}
add_custom_event_listener :: proc(id: string, name: string, user_data: rawptr, callback: proc(e: Event), use_capture := false) -> bool {
@(default_calling_convention="contextless")
foreign dom_lib {
@(link_name="add_event_listener")
_add_event_listener :: proc(id: string, name: string, name_code: Event_Kind, user_data: rawptr, callback: proc "odin" (Event), use_capture: bool) -> bool ---
}
return _add_event_listener(id, name, .Custom, user_data, callback, use_capture)
}
remove_custom_event_listener :: proc(id: string, name: string, user_data: rawptr, callback: proc(e: Event)) -> bool {
@(default_calling_convention="contextless")
foreign dom_lib {
@(link_name="remove_event_listener")
_remove_event_listener :: proc(id: string, name: string, user_data: rawptr, callback: proc "odin" (Event)) -> bool ---
}
return _remove_event_listener(id, name, user_data, callback)
}
get_gamepad_state :: proc "contextless" (index: int, s: ^Gamepad_State) -> bool {
@(default_calling_convention="contextless")
foreign dom_lib {
@(link_name="get_gamepad_state")
_get_gamepad_state :: proc(index: int, s: ^Gamepad_State) -> bool ---
}
if s == nil {
return false
}
return _get_gamepad_state(index, s)
}
@(export, link_name="odin_dom_do_event_callback")
do_event_callback :: proc(user_data: rawptr, callback: proc(e: Event)) {
@(default_calling_convention="contextless")
foreign dom_lib {
init_event_raw :: proc(e: ^Event) ---
}
if callback != nil {
event := Event{
user_data = user_data,
callback = callback,
}
init_event_raw(&event)
#partial switch event.kind {
case .Key_Up, .Key_Down, .Key_Press:
event.key.key = string(event.key._key_buf[:event.key._key_len])
event.key.code = string(event.key._code_buf[:event.key._code_len])
case .Gamepad_Connected, .Gamepad_Disconnected:
event.gamepad.id = string(event.gamepad._id_buf[:event.gamepad._id_len])
event.gamepad.mapping = string(event.gamepad._mapping_buf[:event.gamepad._mapping_len])
}
callback(event)
}
}
+287
View File
@@ -0,0 +1,287 @@
#+build !js
package wasm_js_interface
Event_Kind :: enum u32 {
Invalid,
Load,
Unload,
Error,
Resize,
Visibility_Change,
Fullscreen_Change,
Fullscreen_Error,
Click,
Double_Click,
Mouse_Move,
Mouse_Over,
Mouse_Out,
Mouse_Up,
Mouse_Down,
Key_Up,
Key_Down,
Key_Press,
Scroll,
Wheel,
Focus,
Submit,
Blur,
Change,
Select,
Animation_Start,
Animation_End,
Animation_Iteration,
Animation_Cancel,
Copy,
Cut,
Paste,
// Drag,
// Drag_Start,
// Drag_End,
// Drag_Enter,
// Drag_Leave,
// Drag_Over,
// Drop,
Pointer_Cancel,
Pointer_Down,
Pointer_Enter,
Pointer_Leave,
Pointer_Move,
Pointer_Over,
Pointer_Up,
Got_Pointer_Capture,
Lost_Pointer_Capture,
Pointer_Lock_Change,
Pointer_Lock_Error,
Selection_Change,
Selection_Start,
Touch_Cancel,
Touch_End,
Touch_Move,
Touch_Start,
Transition_Start,
Transition_End,
Transition_Run,
Transition_Cancel,
Context_Menu,
Custom,
}
event_kind_string := [Event_Kind]string{
.Invalid = "",
.Load = "load",
.Unload = "unload",
.Error = "error",
.Resize = "resize",
.Visibility_Change = "visibilitychange",
.Fullscreen_Change = "fullscreenchange",
.Fullscreen_Error = "fullscreenerror",
.Click = "click",
.Double_Click = "dblclick",
.Mouse_Move = "mousemove",
.Mouse_Over = "mouseover",
.Mouse_Out = "mouseout",
.Mouse_Up = "mouseup",
.Mouse_Down = "mousedown",
.Key_Up = "keyup",
.Key_Down = "keydown",
.Key_Press = "keypress",
.Scroll = "scroll",
.Wheel = "wheel",
.Focus = "focus",
.Submit = "submit",
.Blur = "blur",
.Change = "change",
.Select = "select",
.Animation_Start = "animationstart",
.Animation_End = "animationend",
.Animation_Iteration = "animationiteration",
.Animation_Cancel = "animationcancel",
.Copy = "copy",
.Cut = "cut",
.Paste = "paste",
// .Drag, = "drag",
// .Drag_Start, = "dragstart",
// .Drag_End, = "dragend",
// .Drag_Enter, = "dragenter",
// .Drag_Leave, = "dragleave",
// .Drag_Over, = "dragover",
// .Drop, = "drop",
.Pointer_Cancel = "pointercancel",
.Pointer_Down = "pointerdown",
.Pointer_Enter = "pointerenter",
.Pointer_Leave = "pointerleave",
.Pointer_Move = "pointermove",
.Pointer_Over = "pointerover",
.Pointer_Up = "pointerup",
.Got_Pointer_Capture = "gotpointercapture",
.Lost_Pointer_Capture = "lostpointercapture",
.Pointer_Lock_Change = "pointerlockchange",
.Pointer_Lock_Error = "pointerlockerror",
.Selection_Change = "selectionchange",
.Selection_Start = "selectionstart",
.Transition_Start = "transitionstart",
.Transition_End = "transitionend",
.Transition_Run = "transitionrun",
.Transition_Cancel = "transitioncancel",
.Touch_Cancel = "touchcancel",
.Touch_End = "touchend",
.Touch_Move = "touchmove",
.Touch_Start = "touchstart",
.Context_Menu = "contextmenu",
.Custom = "?custom?",
}
Delta_Mode :: enum u32 {
Pixel = 0,
Line = 1,
Page = 2,
}
Key_Location :: enum u8 {
Standard = 0,
Left = 1,
Right = 2,
Numpad = 3,
}
KEYBOARD_MAX_KEY_SIZE :: 16
KEYBOARD_MAX_CODE_SIZE :: 16
Event_Target_Kind :: enum u32 {
Element = 0,
Document = 1,
Window = 2,
}
Event_Phase :: enum u8 {
None = 0,
Capturing_Phase = 1,
At_Target = 2,
Bubbling_Phase = 3,
}
Event_Option :: enum u8 {
Bubbles = 0,
Cancelable = 1,
Composed = 2,
}
Event_Options :: distinct bit_set[Event_Option; u8]
Event :: struct {
kind: Event_Kind,
target_kind: Event_Target_Kind,
current_target_kind: Event_Target_Kind,
id: string,
timestamp: f64,
phase: Event_Phase,
options: Event_Options,
is_composing: bool,
is_trusted: bool,
using data: struct #raw_union #align(8) {
scroll: struct {
delta: [2]f64,
},
visibility_change: struct {
is_visible: bool,
},
wheel: struct {
delta: [3]f64,
delta_mode: Delta_Mode,
},
key: struct {
key: string,
code: string,
location: Key_Location,
ctrl: bool,
shift: bool,
alt: bool,
meta: bool,
repeat: bool,
_key_buf: [KEYBOARD_MAX_KEY_SIZE]byte,
_code_buf: [KEYBOARD_MAX_KEY_SIZE]byte,
},
mouse: struct {
screen: [2]i64,
client: [2]i64,
offset: [2]i64,
page: [2]i64,
movement: [2]i64,
ctrl: bool,
shift: bool,
alt: bool,
meta: bool,
button: i16,
buttons: bit_set[0..<16; u16],
},
},
user_data: rawptr,
callback: proc(e: Event),
}
add_event_listener :: proc(id: string, kind: Event_Kind, user_data: rawptr, callback: proc(e: Event), use_capture := false) -> bool {
panic("vendor:wasm/js not supported on non JS targets")
}
remove_event_listener :: proc(id: string, kind: Event_Kind, user_data: rawptr, callback: proc(e: Event)) -> bool {
panic("vendor:wasm/js not supported on non JS targets")
}
add_window_event_listener :: proc(kind: Event_Kind, user_data: rawptr, callback: proc(e: Event), use_capture := false) -> bool {
panic("vendor:wasm/js not supported on non JS targets")
}
remove_window_event_listener :: proc(kind: Event_Kind, user_data: rawptr, callback: proc(e: Event)) -> bool {
panic("vendor:wasm/js not supported on non JS targets")
}
remove_event_listener_from_event :: proc(e: Event) -> bool {
panic("vendor:wasm/js not supported on non JS targets")
}
add_custom_event_listener :: proc(id: string, name: string, user_data: rawptr, callback: proc(e: Event), use_capture := false) -> bool {
panic("vendor:wasm/js not supported on non JS targets")
}
remove_custom_event_listener :: proc(id: string, name: string, user_data: rawptr, callback: proc(e: Event)) -> bool {
panic("vendor:wasm/js not supported on non JS targets")
}
+12
View File
@@ -0,0 +1,12 @@
#+build js wasm32, js wasm64p32
package wasm_js_interface
foreign import "odin_env"
@(default_calling_convention="contextless")
foreign odin_env {
trap :: proc() -> ! ---
abort :: proc() -> ! ---
alert :: proc(msg: string) ---
evaluate :: proc(str: string) ---
}
+14
View File
@@ -0,0 +1,14 @@
#+build !js
package wasm_js_interface
import "core:mem"
PAGE_SIZE :: 64 * 1024
page_alloc :: proc(page_count: int) -> (data: []byte, err: mem.Allocator_Error) {
panic("vendor:wasm/js not supported on non-js targets")
}
page_allocator :: proc() -> mem.Allocator {
panic("vendor:wasm/js not supported on non-js targets")
}
+44
View File
@@ -0,0 +1,44 @@
#+build js wasm32, js wasm64p32
package wasm_js_interface
import "core:mem"
import "base:intrinsics"
PAGE_SIZE :: 64 * 1024
page_alloc :: proc(page_count: int) -> (data: []byte, err: mem.Allocator_Error) {
prev_page_count := intrinsics.wasm_memory_grow(0, uintptr(page_count))
if prev_page_count < 0 {
return nil, .Out_Of_Memory
}
ptr := ([^]u8)(uintptr(prev_page_count) * PAGE_SIZE)
return ptr[:page_count * PAGE_SIZE], nil
}
page_allocator :: proc() -> mem.Allocator {
procedure :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
size, alignment: int,
old_memory: rawptr, old_size: int,
location := #caller_location) -> ([]byte, mem.Allocator_Error) {
switch mode {
case .Alloc, .Alloc_Non_Zeroed:
assert(size % PAGE_SIZE == 0)
return page_alloc(size/PAGE_SIZE)
case .Resize, .Free, .Free_All, .Query_Info, .Resize_Non_Zeroed:
return nil, .Mode_Not_Implemented
case .Query_Features:
set := (^mem.Allocator_Mode_Set)(old_memory)
if set != nil {
set^ = {.Alloc, .Query_Features}
}
}
return nil, nil
}
return {
procedure = procedure,
data = nil,
}
}
File diff suppressed because it is too large Load Diff
+107 -1
View File
@@ -187,4 +187,110 @@ scan_digits :: proc(s: string, sep: string, count: int) -> (res: int, ok: bool)
found_sep |= rune(s[count]) == v
}
return res, found_sep
}
}
/*
Serialize the timestamp as a RFC 3339 string.
The boolean `ok` is false if the `time` is not a valid datetime, or if allocating the result string fails.
**Inputs**:
- `utc_offset`: offset in minutes wrt UTC (ie. the timezone)
- `include_nanos`: whether to include nanoseconds in the result.
*/
time_to_rfc3339 :: proc(time: Time, utc_offset : int = 0, include_nanos := true, allocator := context.allocator) -> (res: string, ok: bool) {
utc_offset := utc_offset
// convert to datetime
datetime := time_to_datetime(time) or_return
if datetime.year < 0 || datetime.year >= 10_000 { return "", false }
temp_string := [36]u8{}
offset : uint = 0
print_as_fixed_int :: proc(dst: []u8, offset: ^uint, width: i8, i: i64) {
i := i
width := width
for digit_idx in 0..<width {
last_digit := i % 10
dst[offset^ + uint(width) - uint(digit_idx)-1] = '0' + u8(last_digit)
i = i / 10
}
offset^ += uint(width)
}
print_as_fixed_int(temp_string[:], &offset, 4, datetime.year)
temp_string[offset] = '-'
offset += 1
print_as_fixed_int(temp_string[:], &offset, 2, i64(datetime.month))
temp_string[offset] = '-'
offset += 1
print_as_fixed_int(temp_string[:], &offset, 2, i64(datetime.day))
temp_string[offset] = 'T'
offset += 1
print_as_fixed_int(temp_string[:], &offset, 2, i64(datetime.hour))
temp_string[offset] = ':'
offset += 1
print_as_fixed_int(temp_string[:], &offset, 2, i64(datetime.minute))
temp_string[offset] = ':'
offset += 1
print_as_fixed_int(temp_string[:], &offset, 2, i64(datetime.second))
// turn 123_450_000 to 12345, 5
strip_trailing_zeroes_nanos :: proc(n: i64) -> (res: i64, n_digits: i8) {
res = n
n_digits = 9
for res % 10 == 0 {
res = res / 10
n_digits -= 1
}
return
}
// pre-epoch times: turn, say, -400ms to +600ms for display
nanos := time._nsec % 1_000_000_000
if nanos < 0 {
nanos += 1_000_000_000
}
if nanos != 0 && include_nanos {
temp_string[offset] = '.'
offset += 1
// remove trailing zeroes
nanos_nonzero, n_digits := strip_trailing_zeroes_nanos(nanos)
assert(nanos_nonzero != 0)
// write digits, right-to-left
for digit_idx : i8 = n_digits-1; digit_idx >= 0; digit_idx -= 1 {
digit := u8(nanos_nonzero % 10)
temp_string[offset + uint(digit_idx)] = '0' + u8(digit)
nanos_nonzero /= 10
}
offset += uint(n_digits)
}
if utc_offset == 0 {
temp_string[offset] = 'Z'
offset += 1
} else {
temp_string[offset] = utc_offset > 0 ? '+' : '-'
offset += 1
utc_offset = abs(utc_offset)
print_as_fixed_int(temp_string[:], &offset, 2, i64(utc_offset / 60))
temp_string[offset] = ':'
offset += 1
print_as_fixed_int(temp_string[:], &offset, 2, i64(utc_offset % 60))
}
res_as_slice, res_alloc := make_slice([]u8, len=offset, allocator = allocator)
if res_alloc != nil {
return "", false
}
copy(res_as_slice, temp_string[:offset])
return string(res_as_slice), true
}