aboutsummaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
authorflysand7 <thebumboni@gmail.com>2025-03-02 20:05:55 +1100
committerflysand7 <thebumboni@gmail.com>2025-03-02 20:05:55 +1100
commit698c510ba7bb5794b3eeed7aecb8327386f00da7 (patch)
tree8c376debaf24f3ac8782192310e49e730509cdf9 /core
parent5d290dce069cb257b2e3effdd4e9b1e7dc21e722 (diff)
parentf390598b403eb336276ef9161753bf26d24d0d01 (diff)
Merge branch 'master' into docs-simd
Diffstat (limited to 'core')
-rw-r--r--core/c/c.odin2
-rw-r--r--core/c/libc/errno.odin15
-rw-r--r--core/c/libc/locale.odin2
-rw-r--r--core/c/libc/stdio.odin3
-rw-r--r--core/c/libc/stdlib.odin15
-rw-r--r--core/c/libc/time.odin2
-rw-r--r--core/container/queue/queue.odin3
-rw-r--r--core/crypto/rand_generic.odin1
-rw-r--r--core/crypto/rand_wasi.odin13
-rw-r--r--core/dynlib/lib_unix.odin2
-rw-r--r--core/encoding/base32/base32.odin378
-rw-r--r--core/encoding/base32/base32_test.odin227
-rw-r--r--core/encoding/json/marshal.odin16
-rw-r--r--core/encoding/json/tokenizer.odin3
-rw-r--r--core/encoding/json/unmarshal.odin12
-rw-r--r--core/flags/util.odin2
-rw-r--r--core/fmt/fmt.odin40
-rw-r--r--core/image/general.odin2
-rw-r--r--core/image/png/helpers.odin130
-rw-r--r--core/io/io.odin4
-rw-r--r--core/io/util.odin10
-rw-r--r--core/log/file_console_logger.odin16
-rw-r--r--core/log/log.odin35
-rw-r--r--core/log/multi_logger.odin12
-rw-r--r--core/math/linalg/general.odin96
-rw-r--r--core/math/linalg/glsl/linalg_glsl.odin100
-rw-r--r--core/math/linalg/glsl/linalg_glsl_math.odin2
-rw-r--r--core/math/linalg/hlsl/linalg_hlsl.odin84
-rw-r--r--core/math/linalg/specific.odin12
-rw-r--r--core/math/rand/rand.odin28
-rw-r--r--core/mem/alloc.odin97
-rw-r--r--core/mem/allocators.odin23
-rw-r--r--core/mem/doc.odin4
-rw-r--r--core/mem/mem.odin9
-rw-r--r--core/mem/tlsf/tlsf_internal.odin2
-rw-r--r--core/mem/tracking_allocator.odin50
-rw-r--r--core/mem/virtual/arena.odin28
-rw-r--r--core/net/common.odin4
-rw-r--r--core/net/dns.odin80
-rw-r--r--core/net/dns_windows.odin7
-rw-r--r--core/net/socket.odin57
-rw-r--r--core/net/socket_linux.odin4
-rw-r--r--core/net/socket_windows.odin3
-rw-r--r--core/odin/ast/ast.odin7
-rw-r--r--core/odin/ast/clone.odin3
-rw-r--r--core/odin/parser/file_tags.odin24
-rw-r--r--core/odin/parser/parser.odin43
-rw-r--r--core/os/dir_unix.odin2
-rw-r--r--core/os/os2/dir.odin124
-rw-r--r--core/os/os2/dir_linux.odin109
-rw-r--r--core/os/os2/dir_posix.odin64
-rw-r--r--core/os/os2/dir_walker.odin230
-rw-r--r--core/os/os2/dir_wasi.odin124
-rw-r--r--core/os/os2/dir_windows.odin45
-rw-r--r--core/os/os2/env.odin6
-rw-r--r--core/os/os2/env_linux.odin40
-rw-r--r--core/os/os2/env_posix.odin28
-rw-r--r--core/os/os2/env_wasi.odin159
-rw-r--r--core/os/os2/env_windows.odin31
-rw-r--r--core/os/os2/errors_posix.odin13
-rw-r--r--core/os/os2/errors_wasi.odin47
-rw-r--r--core/os/os2/file.odin7
-rw-r--r--core/os/os2/file_linux.odin159
-rw-r--r--core/os/os2/file_posix.odin72
-rw-r--r--core/os/os2/file_wasi.odin560
-rw-r--r--core/os/os2/file_windows.odin76
-rw-r--r--core/os/os2/heap_linux.odin724
-rw-r--r--core/os/os2/heap_wasi.odin6
-rw-r--r--core/os/os2/path.odin12
-rw-r--r--core/os/os2/path_darwin.odin17
-rw-r--r--core/os/os2/path_freebsd.odin29
-rw-r--r--core/os/os2/path_linux.odin24
-rw-r--r--core/os/os2/path_netbsd.odin24
-rw-r--r--core/os/os2/path_openbsd.odin57
-rw-r--r--core/os/os2/path_posix.odin2
-rw-r--r--core/os/os2/path_wasi.odin117
-rw-r--r--core/os/os2/path_windows.odin20
-rw-r--r--core/os/os2/pipe_linux.odin4
-rw-r--r--core/os/os2/pipe_posix.odin8
-rw-r--r--core/os/os2/pipe_wasi.odin13
-rw-r--r--core/os/os2/process.odin11
-rw-r--r--core/os/os2/process_linux.odin64
-rw-r--r--core/os/os2/process_posix.odin8
-rw-r--r--core/os/os2/process_wasi.odin89
-rw-r--r--core/os/os2/process_windows.odin2
-rw-r--r--core/os/os2/stat_wasi.odin101
-rw-r--r--core/os/os2/stat_windows.odin23
-rw-r--r--core/os/os2/temp_file_wasi.odin9
-rw-r--r--core/os/os_darwin.odin2
-rw-r--r--core/os/os_freebsd.odin8
-rw-r--r--core/os/os_haiku.odin34
-rw-r--r--core/os/os_linux.odin2
-rw-r--r--core/os/stat_unix.odin2
-rw-r--r--core/path/filepath/match.odin1
-rw-r--r--core/path/filepath/path_unix.odin2
-rw-r--r--core/path/filepath/path_wasi.odin36
-rw-r--r--core/path/filepath/walk.odin1
-rw-r--r--core/prof/spall/doc.odin2
-rw-r--r--core/simd/simd.odin78
-rw-r--r--core/slice/slice.odin54
-rw-r--r--core/strings/builder.odin25
-rw-r--r--core/strings/intern.odin16
-rw-r--r--core/strings/strings.odin18
-rw-r--r--core/sync/futex_haiku.odin50
-rw-r--r--core/sync/futex_linux.odin3
-rw-r--r--core/sync/futex_wasm.odin20
-rw-r--r--core/sync/futex_windows.odin4
-rw-r--r--core/sync/primitives_atomic.odin2
-rw-r--r--core/sys/darwin/Foundation/NSBlock.odin4
-rw-r--r--core/sys/darwin/Foundation/NSData.odin17
-rw-r--r--core/sys/darwin/Foundation/NSDate.odin5
-rw-r--r--core/sys/darwin/Foundation/NSMenu.odin16
-rw-r--r--core/sys/darwin/Foundation/NSSavePanel.odin10
-rw-r--r--core/sys/darwin/Foundation/NSString.odin5
-rw-r--r--core/sys/darwin/Foundation/NSToolbar.odin14
-rw-r--r--core/sys/darwin/Foundation/NSURL.odin5
-rw-r--r--core/sys/darwin/Foundation/NSURLRequest.odin24
-rw-r--r--core/sys/darwin/Foundation/NSURLResponse.odin19
-rw-r--r--core/sys/darwin/Foundation/NSWindow.odin32
-rw-r--r--core/sys/haiku/errno.odin (renamed from core/sys/haiku/errors.odin)165
-rw-r--r--core/sys/haiku/find_directory.odin43
-rw-r--r--core/sys/haiku/os.odin290
-rw-r--r--core/sys/haiku/types.odin14
-rw-r--r--core/sys/linux/bits.odin97
-rw-r--r--core/sys/linux/helpers.odin4
-rw-r--r--core/sys/linux/sys.odin44
-rw-r--r--core/sys/linux/types.odin232
-rw-r--r--core/sys/linux/wrappers.odin28
-rw-r--r--core/sys/orca/macros.odin77
-rw-r--r--core/sys/orca/odin.odin16
-rw-r--r--core/sys/orca/orca.odin926
-rw-r--r--core/sys/posix/arpa_inet.odin4
-rw-r--r--core/sys/posix/dirent.odin27
-rw-r--r--core/sys/posix/errno.odin89
-rw-r--r--core/sys/posix/fcntl.odin69
-rw-r--r--core/sys/posix/fnmatch.odin4
-rw-r--r--core/sys/posix/glob.odin6
-rw-r--r--core/sys/posix/grp.odin4
-rw-r--r--core/sys/posix/langinfo.odin6
-rw-r--r--core/sys/posix/libgen.odin2
-rw-r--r--core/sys/posix/locale.odin2
-rw-r--r--core/sys/posix/monetary.odin2
-rw-r--r--core/sys/posix/netdb.odin52
-rw-r--r--core/sys/posix/netinet_in.odin61
-rw-r--r--core/sys/posix/poll.odin49
-rw-r--r--core/sys/posix/pthread.odin52
-rw-r--r--core/sys/posix/pwd.odin14
-rw-r--r--core/sys/posix/sched.odin9
-rw-r--r--core/sys/posix/signal.odin149
-rw-r--r--core/sys/posix/signal_libc.odin2
-rw-r--r--core/sys/posix/stdio_libc.odin2
-rw-r--r--core/sys/posix/stdlib.odin2
-rw-r--r--core/sys/posix/stdlib_libc.odin2
-rw-r--r--core/sys/posix/string.odin2
-rw-r--r--core/sys/posix/string_libc.odin2
-rw-r--r--core/sys/posix/sys_ipc.odin25
-rw-r--r--core/sys/posix/sys_msg.odin20
-rw-r--r--core/sys/posix/sys_resource.odin53
-rw-r--r--core/sys/posix/sys_select.odin16
-rw-r--r--core/sys/posix/sys_sem.odin28
-rw-r--r--core/sys/posix/sys_socket.odin66
-rw-r--r--core/sys/posix/sys_stat.odin32
-rw-r--r--core/sys/posix/sys_time.odin13
-rw-r--r--core/sys/posix/sys_times.odin4
-rw-r--r--core/sys/posix/sys_uio.odin4
-rw-r--r--core/sys/posix/sys_un.odin10
-rw-r--r--core/sys/posix/sys_utsname.odin6
-rw-r--r--core/sys/posix/sys_wait.odin54
-rw-r--r--core/sys/posix/termios.odin184
-rw-r--r--core/sys/posix/time.odin13
-rw-r--r--core/sys/posix/unistd.odin560
-rw-r--r--core/sys/posix/unistd_libc.odin2
-rw-r--r--core/sys/posix/utime.odin4
-rw-r--r--core/sys/wasm/js/odin.js5
-rw-r--r--core/sys/windows/comctl32.odin2088
-rw-r--r--core/sys/windows/dbghelp.odin2
-rw-r--r--core/sys/windows/dnsapi.odin2
-rw-r--r--core/sys/windows/kernel32.odin51
-rw-r--r--core/sys/windows/types.odin537
-rw-r--r--core/sys/windows/user32.odin31
-rw-r--r--core/sys/windows/ux_theme.odin1
-rw-r--r--core/sys/windows/window_messages.odin170
-rw-r--r--core/sys/windows/xinput.odin210
-rw-r--r--core/testing/runner.odin1
-rw-r--r--core/testing/signal_handler_posix.odin2
-rw-r--r--core/text/match/strlib.odin16
-rw-r--r--core/text/regex/regex.odin1
-rw-r--r--core/thread/thread_pool.odin17
-rw-r--r--core/time/time_other.odin1
-rw-r--r--core/time/time_unix.odin2
-rw-r--r--core/time/timezone/tz_windows.odin294
-rw-r--r--core/time/timezone/tzdate.odin4
192 files changed, 9603 insertions, 2958 deletions
diff --git a/core/c/c.odin b/core/c/c.odin
index 3dfc19ffc..73727d8d5 100644
--- a/core/c/c.odin
+++ b/core/c/c.odin
@@ -114,3 +114,5 @@ CHAR_BIT :: 8
va_list :: struct #align(16) {
_: [4096]u8,
}
+
+FILE :: struct {}
diff --git a/core/c/libc/errno.odin b/core/c/libc/errno.odin
index de429a6ec..5d1ca8248 100644
--- a/core/c/libc/errno.odin
+++ b/core/c/libc/errno.odin
@@ -88,14 +88,15 @@ when ODIN_OS == .Haiku {
_get_errno :: proc() -> ^int ---
}
- @(private="file")
- B_GENERAL_ERROR_BASE :: min(i32)
- @(private="file")
- B_POSIX_ERROR_BASE :: B_GENERAL_ERROR_BASE + 0x7000
+ _HAIKU_USE_POSITIVE_POSIX_ERRORS :: #config(HAIKU_USE_POSITIVE_POSIX_ERRORS, false)
+ _POSIX_ERROR_FACTOR :: -1 when _HAIKU_USE_POSITIVE_POSIX_ERRORS else 1
+
+ @(private="file") _GENERAL_ERROR_BASE :: min(int)
+ @(private="file") _POSIX_ERROR_BASE :: _GENERAL_ERROR_BASE + 0x7000
- EDOM :: B_POSIX_ERROR_BASE + 16
- EILSEQ :: B_POSIX_ERROR_BASE + 38
- ERANGE :: B_POSIX_ERROR_BASE + 17
+ EDOM :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 16)
+ EILSEQ :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 38)
+ ERANGE :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 17)
}
when ODIN_OS == .JS {
diff --git a/core/c/libc/locale.odin b/core/c/libc/locale.odin
index 371d755c5..d95f5c164 100644
--- a/core/c/libc/locale.odin
+++ b/core/c/libc/locale.odin
@@ -110,7 +110,7 @@ when ODIN_OS == .Windows {
}
}
-when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Windows {
+when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Haiku || ODIN_OS == .Windows {
LC_ALL :: 0
LC_COLLATE :: 1
diff --git a/core/c/libc/stdio.odin b/core/c/libc/stdio.odin
index a94a53696..56e4e8f66 100644
--- a/core/c/libc/stdio.odin
+++ b/core/c/libc/stdio.odin
@@ -1,5 +1,6 @@
package libc
+import "core:c"
import "core:io"
when ODIN_OS == .Windows {
@@ -15,7 +16,7 @@ when ODIN_OS == .Windows {
// 7.21 Input/output
-FILE :: struct {}
+FILE :: c.FILE
Whence :: enum int {
SET = SEEK_SET,
diff --git a/core/c/libc/stdlib.odin b/core/c/libc/stdlib.odin
index 98280e44b..c0e273872 100644
--- a/core/c/libc/stdlib.odin
+++ b/core/c/libc/stdlib.odin
@@ -42,6 +42,21 @@ when ODIN_OS == .Linux {
}
}
+when ODIN_OS == .Haiku {
+ RAND_MAX :: 0x7fffffff
+
+ // GLIBC and MUSL only
+ @(private="file")
+ @(default_calling_convention="c")
+ foreign libc {
+ __ctype_get_mb_cur_max :: proc() -> ushort ---
+ }
+
+ MB_CUR_MAX :: #force_inline proc() -> size_t {
+ return size_t(__ctype_get_mb_cur_max())
+ }
+}
+
when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD {
RAND_MAX :: 0x7fffffff
diff --git a/core/c/libc/time.odin b/core/c/libc/time.odin
index 6828793ec..33f8dc3af 100644
--- a/core/c/libc/time.odin
+++ b/core/c/libc/time.odin
@@ -95,7 +95,7 @@ when ODIN_OS == .Linux || ODIN_OS == .FreeBSD || ODIN_OS == .Darwin || ODIN_OS =
time_t :: distinct i64
- when ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD {
+ when ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .Haiku {
clock_t :: distinct int32_t
} else {
clock_t :: distinct long
diff --git a/core/container/queue/queue.odin b/core/container/queue/queue.odin
index f83a5f2b7..d1040a7c9 100644
--- a/core/container/queue/queue.odin
+++ b/core/container/queue/queue.odin
@@ -46,8 +46,7 @@ init_with_contents :: proc(q: ^$Q/Queue($T), backing: []T) -> bool {
cap = builtin.len(backing),
allocator = {procedure=runtime.nil_allocator_proc, data=nil},
}
- q.len = len(backing)
- q.offset = len(backing)
+ q.len = builtin.len(backing)
return true
}
diff --git a/core/crypto/rand_generic.odin b/core/crypto/rand_generic.odin
index ef578f5c0..8266f8ffc 100644
--- a/core/crypto/rand_generic.odin
+++ b/core/crypto/rand_generic.odin
@@ -5,6 +5,7 @@
#+build !netbsd
#+build !darwin
#+build !js
+#+build !wasi
package crypto
HAS_RAND_BYTES :: false
diff --git a/core/crypto/rand_wasi.odin b/core/crypto/rand_wasi.odin
new file mode 100644
index 000000000..9653fb985
--- /dev/null
+++ b/core/crypto/rand_wasi.odin
@@ -0,0 +1,13 @@
+package crypto
+
+import "core:fmt"
+import "core:sys/wasm/wasi"
+
+HAS_RAND_BYTES :: true
+
+@(private)
+_rand_bytes :: proc(dst: []byte) {
+ if err := wasi.random_get(dst); err != nil {
+ fmt.panicf("crypto: wasi.random_get failed: %v", err)
+ }
+}
diff --git a/core/dynlib/lib_unix.odin b/core/dynlib/lib_unix.odin
index 50ab1acc8..1a6a737a4 100644
--- a/core/dynlib/lib_unix.odin
+++ b/core/dynlib/lib_unix.odin
@@ -13,6 +13,8 @@ _load_library :: proc(path: string, global_symbols: bool, allocator: runtime.All
flags := posix.RTLD_Flags{.NOW}
if global_symbols {
flags += {.GLOBAL}
+ } else {
+ flags += posix.RTLD_LOCAL
}
cpath := strings.clone_to_cstring(path, allocator)
diff --git a/core/encoding/base32/base32.odin b/core/encoding/base32/base32.odin
index f3320428d..2267a872b 100644
--- a/core/encoding/base32/base32.odin
+++ b/core/encoding/base32/base32.odin
@@ -1,148 +1,230 @@
-package encoding_base32
-
-// @note(zh): Encoding utility for Base32
-// A secondary param can be used to supply a custom alphabet to
-// @link(encode) and a matching decoding table to @link(decode).
-// If none is supplied it just uses the standard Base32 alphabet.
-// Incase your specific version does not use padding, you may
-// truncate it from the encoded output.
-
-ENC_TABLE := [32]byte {
- 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
- 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
- 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
- 'Y', 'Z', '2', '3', '4', '5', '6', '7',
-}
-
-PADDING :: '='
-
-DEC_TABLE := [?]u8 {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 26, 27, 28, 29, 30, 31, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
- 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0,
- 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
- 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-}
-
-encode :: proc(data: []byte, ENC_TBL := ENC_TABLE, allocator := context.allocator) -> string {
- out_length := (len(data) + 4) / 5 * 8
- out := make([]byte, out_length)
- _encode(out, data)
- return string(out)
-}
-
-@private
-_encode :: proc(out, data: []byte, ENC_TBL := ENC_TABLE, allocator := context.allocator) {
- out := out
- data := data
-
- for len(data) > 0 {
- carry: byte
- switch len(data) {
- case:
- out[7] = ENC_TABLE[data[4] & 0x1f]
- carry = data[4] >> 5
- fallthrough
- case 4:
- out[6] = ENC_TABLE[carry | (data[3] << 3) & 0x1f]
- out[5] = ENC_TABLE[(data[3] >> 2) & 0x1f]
- carry = data[3] >> 7
- fallthrough
- case 3:
- out[4] = ENC_TABLE[carry | (data[2] << 1) & 0x1f]
- carry = (data[2] >> 4) & 0x1f
- fallthrough
- case 2:
- out[3] = ENC_TABLE[carry | (data[1] << 4) & 0x1f]
- out[2] = ENC_TABLE[(data[1] >> 1) & 0x1f]
- carry = (data[1] >> 6) & 0x1f
- fallthrough
- case 1:
- out[1] = ENC_TABLE[carry | (data[0] << 2) & 0x1f]
- out[0] = ENC_TABLE[data[0] >> 3]
- }
-
- if len(data) < 5 {
- out[7] = byte(PADDING)
- if len(data) < 4 {
- out[6] = byte(PADDING)
- out[5] = byte(PADDING)
- if len(data) < 3 {
- out[4] = byte(PADDING)
- if len(data) < 2 {
- out[3] = byte(PADDING)
- out[2] = byte(PADDING)
- }
- }
- }
- break
- }
- data = data[5:]
- out = out[8:]
- }
-}
-
-decode :: proc(data: string, DEC_TBL := DEC_TABLE, allocator := context.allocator) -> []byte #no_bounds_check{
- if len(data) == 0 {
- return nil
- }
-
- outi := 0
- data := data
-
- out := make([]byte, len(data) / 8 * 5, allocator)
- end := false
- for len(data) > 0 && !end {
- dbuf : [8]byte
- dlen := 8
-
- for j := 0; j < 8; {
- if len(data) == 0 {
- dlen, end = j, true
- break
- }
- input := data[0]
- data = data[1:]
- if input == byte(PADDING) && j >= 2 && len(data) < 8 {
- assert(!(len(data) + j < 8 - 1), "Corrupted input")
- for k := 0; k < 8-1-j; k +=1 {
- assert(len(data) < k || data[k] == byte(PADDING), "Corrupted input")
- }
- dlen, end = j, true
- assert(dlen != 1 && dlen != 3 && dlen != 6, "Corrupted input")
- break
- }
- dbuf[j] = DEC_TABLE[input]
- assert(dbuf[j] != 0xff, "Corrupted input")
- j += 1
- }
-
- switch dlen {
- case 8:
- out[outi + 4] = dbuf[6] << 5 | dbuf[7]
- fallthrough
- case 7:
- out[outi + 3] = dbuf[4] << 7 | dbuf[5] << 2 | dbuf[6] >> 3
- fallthrough
- case 5:
- out[outi + 2] = dbuf[3] << 4 | dbuf[4] >> 1
- fallthrough
- case 4:
- out[outi + 1] = dbuf[1] << 6 | dbuf[2] << 1 | dbuf[3] >> 4
- fallthrough
- case 2:
- out[outi + 0] = dbuf[0] << 3 | dbuf[1] >> 2
- }
- outi += 5
- }
- return out
-}
+// Base32 encoding/decoding implementation as specified in RFC 4648.
+// [[ More; https://www.rfc-editor.org/rfc/rfc4648.html ]]
+package encoding_base32
+
+// @note(zh): Encoding utility for Base32
+// A secondary param can be used to supply a custom alphabet to
+// @link(encode) and a matching decoding table to @link(decode).
+// If none is supplied it just uses the standard Base32 alphabet.
+// In case your specific version does not use padding, you may
+// truncate it from the encoded output.
+
+// Error represents errors that can occur during base32 decoding operations.
+// As per RFC 4648:
+// - Section 3.3: Invalid character handling
+// - Section 3.2: Padding requirements
+// - Section 6: Base32 encoding specifics (including block size requirements)
+Error :: enum {
+ None,
+ Invalid_Character, // Input contains characters outside the specified alphabet
+ Invalid_Length, // Input length is not valid for base32 (must be a multiple of 8 with proper padding)
+ Malformed_Input, // Input has improper structure (wrong padding position or incomplete groups)
+}
+
+Validate_Proc :: #type proc(c: byte) -> bool
+
+@private
+_validate_default :: proc(c: byte) -> bool {
+ return (c >= 'A' && c <= 'Z') || (c >= '2' && c <= '7')
+}
+
+@(rodata)
+ENC_TABLE := [32]byte {
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
+ 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
+ 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
+ 'Y', 'Z', '2', '3', '4', '5', '6', '7',
+}
+
+PADDING :: '='
+
+@(rodata)
+DEC_TABLE := [256]u8 {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 26, 27, 28, 29, 30, 31, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0,
+ 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+}
+
+encode :: proc(data: []byte, ENC_TBL := ENC_TABLE, allocator := context.allocator) -> string {
+ out_length := (len(data) + 4) / 5 * 8
+ out := make([]byte, out_length, allocator)
+ _encode(out, data, ENC_TBL)
+ return string(out[:])
+}
+
+@private
+_encode :: proc(out, data: []byte, ENC_TBL := ENC_TABLE, allocator := context.allocator) {
+ out := out
+ data := data
+
+ for len(data) > 0 {
+ carry: byte
+ switch len(data) {
+ case:
+ out[7] = ENC_TBL[data[4] & 0x1f]
+ carry = data[4] >> 5
+ fallthrough
+ case 4:
+ out[6] = ENC_TBL[carry | (data[3] << 3) & 0x1f]
+ out[5] = ENC_TBL[(data[3] >> 2) & 0x1f]
+ carry = data[3] >> 7
+ fallthrough
+ case 3:
+ out[4] = ENC_TBL[carry | (data[2] << 1) & 0x1f]
+ carry = (data[2] >> 4) & 0x1f
+ fallthrough
+ case 2:
+ out[3] = ENC_TBL[carry | (data[1] << 4) & 0x1f]
+ out[2] = ENC_TBL[(data[1] >> 1) & 0x1f]
+ carry = (data[1] >> 6) & 0x1f
+ fallthrough
+ case 1:
+ out[1] = ENC_TBL[carry | (data[0] << 2) & 0x1f]
+ out[0] = ENC_TBL[data[0] >> 3]
+ }
+
+ if len(data) < 5 {
+ out[7] = byte(PADDING)
+ if len(data) < 4 {
+ out[6] = byte(PADDING)
+ out[5] = byte(PADDING)
+ if len(data) < 3 {
+ out[4] = byte(PADDING)
+ if len(data) < 2 {
+ out[3] = byte(PADDING)
+ out[2] = byte(PADDING)
+ }
+ }
+ }
+ break
+ }
+ data = data[5:]
+ out = out[8:]
+ }
+}
+
+@(optimization_mode="favor_size")
+decode :: proc(
+ data: string,
+ DEC_TBL := DEC_TABLE,
+ validate: Validate_Proc = _validate_default,
+ allocator := context.allocator) -> (out: []byte, err: Error) {
+ if len(data) == 0 {
+ return nil, .None
+ }
+
+ // Check minimum length requirement first
+ if len(data) < 2 {
+ return nil, .Invalid_Length
+ }
+
+ // Validate characters using provided validation function
+ for i := 0; i < len(data); i += 1 {
+ c := data[i]
+ if c == byte(PADDING) {
+ break
+ }
+ if !validate(c) {
+ return nil, .Invalid_Character
+ }
+ }
+
+ // Validate padding and length
+ data_len := len(data)
+ padding_count := 0
+ for i := data_len - 1; i >= 0; i -= 1 {
+ if data[i] != byte(PADDING) {
+ break
+ }
+ padding_count += 1
+ }
+
+ // Check for proper padding and length combinations
+ if padding_count > 0 {
+ // Verify no padding in the middle
+ for i := 0; i < data_len - padding_count; i += 1 {
+ if data[i] == byte(PADDING) {
+ return nil, .Malformed_Input
+ }
+ }
+
+ content_len := data_len - padding_count
+ mod8 := content_len % 8
+ required_padding: int
+ switch mod8 {
+ case 2: required_padding = 6 // 2 chars need 6 padding chars
+ case 4: required_padding = 4 // 4 chars need 4 padding chars
+ case 5: required_padding = 3 // 5 chars need 3 padding chars
+ case 7: required_padding = 1 // 7 chars need 1 padding char
+ case: required_padding = 0
+ }
+
+ if required_padding > 0 {
+ if padding_count != required_padding {
+ return nil, .Malformed_Input
+ }
+ } else if mod8 != 0 {
+ return nil, .Malformed_Input
+ }
+ } else {
+ // No padding - must be multiple of 8
+ if data_len % 8 != 0 {
+ return nil, .Malformed_Input
+ }
+ }
+
+ // Calculate decoded length: 5 bytes for every 8 input chars
+ input_chars := data_len - padding_count
+ out_len := input_chars * 5 / 8
+ out = make([]byte, out_len, allocator)
+ defer if err != .None {
+ delete(out)
+ }
+
+ // Process input in 8-byte blocks
+ outi := 0
+ for i := 0; i < input_chars; i += 8 {
+ buf: [8]byte
+ block_size := min(8, input_chars - i)
+
+ // Decode block
+ for j := 0; j < block_size; j += 1 {
+ buf[j] = DEC_TBL[data[i + j]]
+ }
+
+ // Convert to output bytes based on block size
+ bytes_to_write := block_size * 5 / 8
+ switch block_size {
+ case 8:
+ out[outi + 4] = (buf[6] << 5) | buf[7]
+ fallthrough
+ case 7:
+ out[outi + 3] = (buf[4] << 7) | (buf[5] << 2) | (buf[6] >> 3)
+ fallthrough
+ case 5:
+ out[outi + 2] = (buf[3] << 4) | (buf[4] >> 1)
+ fallthrough
+ case 4:
+ out[outi + 1] = (buf[1] << 6) | (buf[2] << 1) | (buf[3] >> 4)
+ fallthrough
+ case 2:
+ out[outi] = (buf[0] << 3) | (buf[1] >> 2)
+ }
+ outi += bytes_to_write
+ }
+
+ return
+}
diff --git a/core/encoding/base32/base32_test.odin b/core/encoding/base32/base32_test.odin
new file mode 100644
index 000000000..ea41ae36f
--- /dev/null
+++ b/core/encoding/base32/base32_test.odin
@@ -0,0 +1,227 @@
+package encoding_base32
+
+import "core:testing"
+import "core:bytes"
+
+@(test)
+test_base32_decode_valid :: proc(t: ^testing.T) {
+ // RFC 4648 Section 10 - Test vectors
+ cases := [?]struct {
+ input, expected: string,
+ }{
+ {"", ""},
+ {"MY======", "f"},
+ {"MZXQ====", "fo"},
+ {"MZXW6===", "foo"},
+ {"MZXW6YQ=", "foob"},
+ {"MZXW6YTB", "fooba"},
+ {"MZXW6YTBOI======", "foobar"},
+ }
+
+ for c in cases {
+ output, err := decode(c.input)
+ if output != nil {
+ defer delete(output)
+ }
+ testing.expect_value(t, err, Error.None)
+ expected := transmute([]u8)c.expected
+ if output != nil {
+ testing.expect(t, bytes.equal(output, expected))
+ } else {
+ testing.expect(t, len(c.expected) == 0)
+ }
+ }
+}
+
+@(test)
+test_base32_encode :: proc(t: ^testing.T) {
+ // RFC 4648 Section 10 - Test vectors
+ cases := [?]struct {
+ input, expected: string,
+ }{
+ {"", ""},
+ {"f", "MY======"},
+ {"fo", "MZXQ===="},
+ {"foo", "MZXW6==="},
+ {"foob", "MZXW6YQ="},
+ {"fooba", "MZXW6YTB"},
+ {"foobar", "MZXW6YTBOI======"},
+ }
+
+ for c in cases {
+ output := encode(transmute([]byte)c.input)
+ defer delete(output)
+ testing.expect(t, output == c.expected)
+ }
+}
+
+@(test)
+test_base32_decode_invalid :: proc(t: ^testing.T) {
+ // Section 3.3 - Non-alphabet characters
+ {
+ // Characters outside alphabet
+ input := "MZ1W6YTB" // '1' not in alphabet (A-Z, 2-7)
+ output, err := decode(input)
+ if output != nil {
+ defer delete(output)
+ }
+ testing.expect_value(t, err, Error.Invalid_Character)
+ }
+ {
+ // Lowercase not allowed
+ input := "mzxq===="
+ output, err := decode(input)
+ if output != nil {
+ defer delete(output)
+ }
+ testing.expect_value(t, err, Error.Invalid_Character)
+ }
+
+ // Section 3.2 - Padding requirements
+ {
+ // Padding must only be at end
+ input := "MZ=Q===="
+ output, err := decode(input)
+ if output != nil {
+ defer delete(output)
+ }
+ testing.expect_value(t, err, Error.Malformed_Input)
+ }
+ {
+ // Missing padding
+ input := "MZXQ" // Should be MZXQ====
+ output, err := decode(input)
+ if output != nil {
+ defer delete(output)
+ }
+ testing.expect_value(t, err, Error.Malformed_Input)
+ }
+ {
+ // Incorrect padding length
+ input := "MZXQ=" // Needs 4 padding chars
+ output, err := decode(input)
+ if output != nil {
+ defer delete(output)
+ }
+ testing.expect_value(t, err, Error.Malformed_Input)
+ }
+ {
+ // Too much padding
+ input := "MY=========" // Extra padding chars
+ output, err := decode(input)
+ if output != nil {
+ defer delete(output)
+ }
+ testing.expect_value(t, err, Error.Malformed_Input)
+ }
+
+ // Section 6 - Base32 block size requirements
+ {
+ // Single character (invalid block)
+ input := "M"
+ output, err := decode(input)
+ if output != nil {
+ defer delete(output)
+ }
+ testing.expect_value(t, err, Error.Invalid_Length)
+ }
+}
+
+@(test)
+test_base32_roundtrip :: proc(t: ^testing.T) {
+ cases := [?]string{
+ "",
+ "f",
+ "fo",
+ "foo",
+ "foob",
+ "fooba",
+ "foobar",
+ }
+
+ for input in cases {
+ encoded := encode(transmute([]byte)input)
+ defer delete(encoded)
+ decoded, err := decode(encoded)
+ if decoded != nil {
+ defer delete(decoded)
+ }
+ testing.expect_value(t, err, Error.None)
+ testing.expect(t, bytes.equal(decoded, transmute([]byte)input))
+ }
+}
+
+@(test)
+test_base32_custom_alphabet :: proc(t: ^testing.T) {
+ custom_enc_table := [32]byte{
+ '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
+ 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
+ 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
+ }
+
+ custom_dec_table: [256]u8
+ for i := 0; i < len(custom_enc_table); i += 1 {
+ custom_dec_table[custom_enc_table[i]] = u8(i)
+ }
+
+ /*
+ custom_dec_table := [256]u8{
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x00-0x0f
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x10-0x1f
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x20-0x2f
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, // 0x30-0x3f ('0'-'9')
+ 0, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, // 0x40-0x4f ('A'-'O')
+ 25, 26, 27, 28, 29, 30, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x50-0x5f ('P'-'V')
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x60-0x6f
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x70-0x7f
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x80-0x8f
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x90-0x9f
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xa0-0xaf
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xb0-0xbf
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xc0-0xcf
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xd0-0xdf
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xe0-0xef
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xf0-0xff
+ }
+ */
+
+ custom_validate :: proc(c: byte) -> bool {
+ return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'V') || c == byte(PADDING)
+ }
+
+ cases := [?]struct {
+ input: string,
+ enc_expected: string,
+ }{
+ {"f", "CO======"},
+ {"fo", "CPNG===="},
+ {"foo", "CPNMU==="},
+ }
+
+ for c in cases {
+ // Test encoding
+ encoded := encode(transmute([]byte)c.input, custom_enc_table)
+ defer delete(encoded)
+ testing.expect(t, encoded == c.enc_expected)
+
+ // Test decoding
+ decoded, err := decode(encoded, custom_dec_table, custom_validate)
+ defer if decoded != nil {
+ delete(decoded)
+ }
+
+ testing.expect_value(t, err, Error.None)
+ testing.expect(t, bytes.equal(decoded, transmute([]byte)c.input))
+ }
+
+ // Test invalid character detection
+ {
+ input := "WXY=====" // Contains chars not in our alphabet
+ output, err := decode(input, custom_dec_table, custom_validate)
+ if output != nil {
+ delete(output)
+ }
+ testing.expect_value(t, err, Error.Invalid_Character)
+ }
+}
diff --git a/core/encoding/json/marshal.odin b/core/encoding/json/marshal.odin
index f0f0927a1..020facd14 100644
--- a/core/encoding/json/marshal.odin
+++ b/core/encoding/json/marshal.odin
@@ -209,13 +209,23 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
opt_write_end(w, opt, ']') or_return
case runtime.Type_Info_Enumerated_Array:
- opt_write_start(w, opt, '[') or_return
+ index_type := reflect.type_info_base(info.index)
+ enum_type := index_type.variant.(reflect.Type_Info_Enum)
+
+ opt_write_start(w, opt, '{') or_return
for i in 0..<info.count {
+ value := cast(runtime.Type_Info_Enum_Value)i
+ index, found := slice.linear_search(enum_type.values, value)
+ if !found {
+ continue
+ }
+
opt_write_iteration(w, opt, i == 0) or_return
+ opt_write_key(w, opt, enum_type.names[index]) or_return
data := uintptr(v.data) + uintptr(i*info.elem_size)
marshal_to_writer(w, any{rawptr(data), info.elem.id}, opt) or_return
}
- opt_write_end(w, opt, ']') or_return
+ opt_write_end(w, opt, '}') or_return
case runtime.Type_Info_Dynamic_Array:
opt_write_start(w, opt, '[') or_return
@@ -667,4 +677,4 @@ cast_any_int_to_u128 :: proc(any_int_value: any) -> u128 {
}
return u
-} \ No newline at end of file
+}
diff --git a/core/encoding/json/tokenizer.odin b/core/encoding/json/tokenizer.odin
index 5c20a2cc3..e46d879a7 100644
--- a/core/encoding/json/tokenizer.odin
+++ b/core/encoding/json/tokenizer.odin
@@ -259,6 +259,7 @@ get_token :: proc(t: ^Tokenizer) -> (token: Token, err: Error) {
skip_digits(t)
}
if t.r == 'e' || t.r == 'E' {
+ token.kind = .Float
switch r := next_rune(t); r {
case '+', '-':
next_rune(t)
@@ -485,7 +486,7 @@ is_valid_string_literal :: proc(str: string, spec: Specification) -> bool {
case '"':
// okay
case '\'':
- if spec != .JSON {
+ if spec == .JSON {
return false
}
// okay
diff --git a/core/encoding/json/unmarshal.odin b/core/encoding/json/unmarshal.odin
index 447397de4..57371e360 100644
--- a/core/encoding/json/unmarshal.odin
+++ b/core/encoding/json/unmarshal.odin
@@ -417,15 +417,15 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
if .raw_union in t.flags {
return UNSUPPORTED_TYPE
}
-
+
+ fields := reflect.struct_fields_zipped(ti.id)
+
struct_loop: for p.curr_token.kind != end_token {
key := parse_object_key(p, p.allocator) or_return
defer delete(key, p.allocator)
unmarshal_expect_token(p, .Colon)
- fields := reflect.struct_fields_zipped(ti.id)
-
field_test :: #force_inline proc "contextless" (field_used: [^]byte, offset: uintptr) -> bool {
prev_set := field_used[offset/8] & byte(offset&7) != 0
field_used[offset/8] |= byte(offset&7)
@@ -433,13 +433,13 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
}
field_used_bytes := (reflect.size_of_typeid(ti.id)+7)/8
- field_used := intrinsics.alloca(field_used_bytes, 1)
+ field_used := intrinsics.alloca(field_used_bytes + 1, 1) // + 1 to not overflow on size_of 0 types.
intrinsics.mem_zero(field_used, field_used_bytes)
use_field_idx := -1
for field, field_idx in fields {
- tag_value := string(reflect.struct_tag_get(field.tag, "json"))
+ tag_value := reflect.struct_tag_get(field.tag, "json")
json_name, _ := json_name_from_tag_value(tag_value)
if key == json_name {
use_field_idx = field_idx
@@ -470,7 +470,7 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
}
}
- if field.name == key {
+ if field.name == key || (field.tag != "" && reflect.struct_tag_get(field.tag, "json") == key) {
offset = field.offset
type = field.type
found = true
diff --git a/core/flags/util.odin b/core/flags/util.odin
index f1bd60e75..c182289be 100644
--- a/core/flags/util.odin
+++ b/core/flags/util.odin
@@ -36,7 +36,7 @@ parse_or_exit :: proc(
args = program_args[1:]
}
- error := parse(model, args, style)
+ error := parse(model, args, style, true, true, allocator, loc)
if error != nil {
stderr := os.stream_from_handle(os.stderr)
diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin
index 49e9f2e6d..b7b42ffa4 100644
--- a/core/fmt/fmt.odin
+++ b/core/fmt/fmt.odin
@@ -314,7 +314,29 @@ assertf :: proc(condition: bool, fmt: string, args: ..any, loc := #caller_locati
p = runtime.default_assertion_failure_proc
}
message := tprintf(fmt, ..args)
- p("Runtime assertion", message, loc)
+ p("runtime assertion", message, loc)
+ }
+ internal(loc, fmt, ..args)
+ }
+}
+// Runtime ensure with a formatted message
+//
+// Inputs:
+// - condition: The boolean condition to be asserted
+// - fmt: A format string with placeholders for the provided arguments
+// - args: A variadic list of arguments to be formatted
+// - loc: The location of the caller
+//
+ensuref :: proc(condition: bool, fmt: string, args: ..any, loc := #caller_location) {
+ if !condition {
+ @(cold)
+ internal :: proc(loc: runtime.Source_Code_Location, fmt: string, args: ..any) {
+ p := context.assertion_failure_proc
+ if p == nil {
+ p = runtime.default_assertion_failure_proc
+ }
+ message := tprintf(fmt, ..args)
+ p("unsatisfied ensure", message, loc)
}
internal(loc, fmt, ..args)
}
@@ -332,7 +354,7 @@ panicf :: proc(fmt: string, args: ..any, loc := #caller_location) -> ! {
p = runtime.default_assertion_failure_proc
}
message := tprintf(fmt, ..args)
- p("Panic", message, loc)
+ p("panic", message, loc)
}
// Creates a formatted C string
@@ -591,6 +613,10 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any, flush := true, newline :
i += 1
width_index, _, index_ok := _arg_number(fmt, &i, len(args))
+ if !index_ok {
+ width_index, index_ok = error_check_arg(fi, false, unused_args^)
+ }
+
if index_ok {
unused_args^ -= {width_index}
@@ -616,6 +642,10 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any, flush := true, newline :
i += 1
precision_index, _, index_ok := _arg_number(fmt, &i, len(args))
+ if !index_ok {
+ precision_index, index_ok = error_check_arg(fi, false, unused_args^)
+ }
+
if index_ok {
unused_args^ -= {precision_index}
fi.prec, _, fi.prec_set = int_from_arg(args, precision_index)
@@ -1267,7 +1297,7 @@ fmt_rune :: proc(fi: ^Info, r: rune, verb: rune) {
case 'q', 'w':
fi.n += io.write_quoted_rune(fi.writer, r)
case:
- fmt_int(fi, u64(r), false, 32, verb)
+ fmt_int(fi, u64(u32(r)), false, 32, verb)
}
}
// Formats an integer value according to the specified formatting verb.
@@ -1357,9 +1387,9 @@ _pad :: proc(fi: ^Info, s: string) {
if fi.minus { // right pad
io.write_string(fi.writer, s, &fi.n)
fmt_write_padding(fi, width)
- } else if !fi.space && s != "" && s[0] == '-' {
+ } else if !fi.space && s != "" && (s[0] == '-' || s[0] == '+') {
// left pad accounting for zero pad of negative number
- io.write_byte(fi.writer, '-', &fi.n)
+ io.write_byte(fi.writer, s[0], &fi.n)
fmt_write_padding(fi, width)
io.write_string(fi.writer, s[1:], &fi.n)
} else { // left pad
diff --git a/core/image/general.odin b/core/image/general.odin
index c4a884071..e92b54f18 100644
--- a/core/image/general.odin
+++ b/core/image/general.odin
@@ -146,7 +146,7 @@ which_bytes :: proc(data: []byte) -> Which_File_Type {
case s[6:10] == "JFIF", s[6:10] == "Exif":
return .JPEG
case s[:3] == "\xff\xd8\xff":
- switch s[4] {
+ switch s[3] {
case 0xdb, 0xee, 0xe1, 0xe0:
return .JPEG
}
diff --git a/core/image/png/helpers.odin b/core/image/png/helpers.odin
index f094b54a9..a9495ed4d 100644
--- a/core/image/png/helpers.odin
+++ b/core/image/png/helpers.odin
@@ -396,132 +396,4 @@ exif :: proc(c: image.PNG_Chunk) -> (res: Exif, ok: bool) {
General helper functions
*/
-compute_buffer_size :: image.compute_buffer_size
-
-/*
- PNG save helpers
-*/
-
-when false {
-
- make_chunk :: proc(c: any, t: Chunk_Type) -> (res: Chunk) {
-
- data: []u8
- if v, ok := c.([]u8); ok {
- data = v
- } else {
- data = mem.any_to_bytes(c)
- }
-
- res.header.length = u32be(len(data))
- res.header.type = t
- res.data = data
-
- // CRC the type
- crc := hash.crc32(mem.any_to_bytes(res.header.type))
- // Extend the CRC with the data
- res.crc = u32be(hash.crc32(data, crc))
- return
- }
-
- write_chunk :: proc(fd: os.Handle, chunk: Chunk) {
- c := chunk
- // Write length + type
- os.write_ptr(fd, &c.header, 8)
- // Write data
- os.write_ptr(fd, mem.raw_data(c.data), int(c.header.length))
- // Write CRC32
- os.write_ptr(fd, &c.crc, 4)
- }
-
- write_image_as_png :: proc(filename: string, image: Image) -> (err: Error) {
- profiler.timed_proc()
- using image
- using os
- flags: int = O_WRONLY|O_CREATE|O_TRUNC
-
- if len(image.pixels) == 0 || len(image.pixels) < image.width * image.height * int(image.channels) {
- return .Invalid_Image_Dimensions
- }
-
- mode: int = 0
- when ODIN_OS == .Linux || ODIN_OS == .Darwin {
- // NOTE(justasd): 644 (owner read, write; group read; others read)
- mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH
- }
-
- fd, fderr := open(filename, flags, mode)
- if fderr != nil {
- return .Cannot_Open_File
- }
- defer close(fd)
-
- magic := Signature
-
- write_ptr(fd, &magic, 8)
-
- ihdr := IHDR{
- width = u32be(width),
- height = u32be(height),
- bit_depth = depth,
- compression_method = 0,
- filter_method = 0,
- interlace_method = .None,
- }
-
- switch channels {
- case 1: ihdr.color_type = Color_Type{}
- case 2: ihdr.color_type = Color_Type{.Alpha}
- case 3: ihdr.color_type = Color_Type{.Color}
- case 4: ihdr.color_type = Color_Type{.Color, .Alpha}
- case:// Unhandled
- return .Unknown_Color_Type
- }
- h := make_chunk(ihdr, .IHDR)
- write_chunk(fd, h)
-
- bytes_needed := width * height * int(channels) + height
- filter_bytes := mem.make_dynamic_array_len_cap([dynamic]u8, bytes_needed, bytes_needed, context.allocator)
- defer delete(filter_bytes)
-
- i := 0; j := 0
- // Add a filter byte 0 per pixel row
- for y := 0; y < height; y += 1 {
- filter_bytes[j] = 0; j += 1
- for x := 0; x < width; x += 1 {
- for z := 0; z < channels; z += 1 {
- filter_bytes[j+z] = image.pixels[i+z]
- }
- i += channels; j += channels
- }
- }
- assert(j == bytes_needed)
-
- a: []u8 = filter_bytes[:]
-
- out_buf: ^[dynamic]u8
- defer free(out_buf)
-
- ctx := zlib.ZLIB_Context{
- in_buf = &a,
- out_buf = out_buf,
- }
- err = zlib.write_zlib_stream_from_memory(&ctx)
-
- b: []u8
- if err == nil {
- b = ctx.out_buf[:]
- } else {
- return err
- }
-
- idat := make_chunk(b, .IDAT)
-
- write_chunk(fd, idat)
-
- iend := make_chunk([]u8{}, .IEND)
- write_chunk(fd, iend)
-
- return nil
- }
-}
+compute_buffer_size :: image.compute_buffer_size \ No newline at end of file
diff --git a/core/io/io.odin b/core/io/io.odin
index 6072aec6d..c2b44cbdb 100644
--- a/core/io/io.odin
+++ b/core/io/io.odin
@@ -126,7 +126,7 @@ _i64_err :: #force_inline proc "contextless" (n: int, err: Error) -> (i64, Error
}
-// read reads up to len(p) bytes into s. It returns the number of bytes read and any error if occurred.
+// read reads up to len(p) bytes into p. It returns the number of bytes read and any error if occurred.
//
// When read encounters an .EOF or error after successfully reading n > 0 bytes, it returns the number of
// bytes read along with the error.
@@ -142,7 +142,7 @@ read :: proc(s: Reader, p: []byte, n_read: ^int = nil) -> (n: int, err: Error) {
return
}
-// write writes up to len(p) bytes into s. It returns the number of bytes written and any error if occurred.
+// write writes up to len(p) bytes into p. It returns the number of bytes written and any error if occurred.
write :: proc(s: Writer, p: []byte, n_written: ^int = nil) -> (n: int, err: Error) {
if s.procedure != nil {
n64: i64
diff --git a/core/io/util.odin b/core/io/util.odin
index 296be7bc0..fdbbd5b9f 100644
--- a/core/io/util.odin
+++ b/core/io/util.odin
@@ -132,9 +132,13 @@ write_encoded_rune :: proc(w: Writer, r: rune, write_quote := true, n_written: ^
buf: [2]byte
s := strconv.append_bits(buf[:], u64(r), 16, true, 64, strconv.digits, nil)
switch len(s) {
- case 0: write_string(w, "00", &n) or_return
- case 1: write_byte(w, '0', &n) or_return
- case 2: write_string(w, s, &n) or_return
+ case 0:
+ write_string(w, "00", &n) or_return
+ case 1:
+ write_byte(w, '0', &n) or_return
+ fallthrough
+ case 2:
+ write_string(w, s, &n) or_return
}
} else {
write_rune(w, r, &n) or_return
diff --git a/core/log/file_console_logger.odin b/core/log/file_console_logger.odin
index e45f99523..6d93fb879 100644
--- a/core/log/file_console_logger.odin
+++ b/core/log/file_console_logger.odin
@@ -37,30 +37,30 @@ File_Console_Logger_Data :: struct {
ident: string,
}
-create_file_logger :: proc(h: os.Handle, lowest := Level.Debug, opt := Default_File_Logger_Opts, ident := "") -> Logger {
- data := new(File_Console_Logger_Data)
+create_file_logger :: proc(h: os.Handle, lowest := Level.Debug, opt := Default_File_Logger_Opts, ident := "", allocator := context.allocator) -> Logger {
+ data := new(File_Console_Logger_Data, allocator)
data.file_handle = h
data.ident = ident
return Logger{file_console_logger_proc, data, lowest, opt}
}
-destroy_file_logger :: proc(log: Logger) {
+destroy_file_logger :: proc(log: Logger, allocator := context.allocator) {
data := cast(^File_Console_Logger_Data)log.data
if data.file_handle != os.INVALID_HANDLE {
os.close(data.file_handle)
}
- free(data)
+ free(data, allocator)
}
-create_console_logger :: proc(lowest := Level.Debug, opt := Default_Console_Logger_Opts, ident := "") -> Logger {
- data := new(File_Console_Logger_Data)
+create_console_logger :: proc(lowest := Level.Debug, opt := Default_Console_Logger_Opts, ident := "", allocator := context.allocator) -> Logger {
+ data := new(File_Console_Logger_Data, allocator)
data.file_handle = os.INVALID_HANDLE
data.ident = ident
return Logger{file_console_logger_proc, data, lowest, opt}
}
-destroy_console_logger :: proc(log: Logger) {
- free(log.data)
+destroy_console_logger :: proc(log: Logger, allocator := context.allocator) {
+ free(log.data, allocator)
}
file_console_logger_proc :: proc(logger_data: rawptr, level: Level, text: string, options: Options, location := #caller_location) {
diff --git a/core/log/log.odin b/core/log/log.odin
index cbb2e922b..2b6317060 100644
--- a/core/log/log.odin
+++ b/core/log/log.odin
@@ -115,7 +115,7 @@ panicf :: proc(fmt_str: string, args: ..any, location := #caller_location) -> !
}
@(disabled=ODIN_DISABLE_ASSERT)
-assert :: proc(condition: bool, message := "", loc := #caller_location) {
+assert :: proc(condition: bool, message := #caller_expression(condition), loc := #caller_location) {
if !condition {
@(cold)
internal :: proc(message: string, loc: runtime.Source_Code_Location) {
@@ -145,7 +145,38 @@ assertf :: proc(condition: bool, fmt_str: string, args: ..any, loc := #caller_lo
}
message := fmt.tprintf(fmt_str, ..args)
log(.Fatal, message, location=loc)
- p("Runtime assertion", message, loc)
+ p("runtime assertion", message, loc)
+ }
+ internal(loc, fmt_str, ..args)
+ }
+}
+
+ensure :: proc(condition: bool, message := #caller_expression(condition), loc := #caller_location) {
+ if !condition {
+ @(cold)
+ internal :: proc(message: string, loc: runtime.Source_Code_Location) {
+ p := context.assertion_failure_proc
+ if p == nil {
+ p = runtime.default_assertion_failure_proc
+ }
+ log(.Fatal, message, location=loc)
+ p("unsatisfied ensure", message, loc)
+ }
+ internal(message, loc)
+ }
+}
+
+ensuref :: proc(condition: bool, fmt_str: string, args: ..any, loc := #caller_location) {
+ if !condition {
+ @(cold)
+ internal :: proc(loc: runtime.Source_Code_Location, fmt_str: string, args: ..any) {
+ p := context.assertion_failure_proc
+ if p == nil {
+ p = runtime.default_assertion_failure_proc
+ }
+ message := fmt.tprintf(fmt_str, ..args)
+ log(.Fatal, message, location=loc)
+ p("unsatisfied ensure", message, loc)
}
internal(loc, fmt_str, ..args)
}
diff --git a/core/log/multi_logger.odin b/core/log/multi_logger.odin
index 96d0f3dbd..6122554bb 100644
--- a/core/log/multi_logger.odin
+++ b/core/log/multi_logger.odin
@@ -5,17 +5,17 @@ Multi_Logger_Data :: struct {
loggers: []Logger,
}
-create_multi_logger :: proc(logs: ..Logger) -> Logger {
- data := new(Multi_Logger_Data)
- data.loggers = make([]Logger, len(logs))
+create_multi_logger :: proc(logs: ..Logger, allocator := context.allocator) -> Logger {
+ data := new(Multi_Logger_Data, allocator)
+ data.loggers = make([]Logger, len(logs), allocator)
copy(data.loggers, logs)
return Logger{multi_logger_proc, data, Level.Debug, nil}
}
-destroy_multi_logger :: proc(log: Logger) {
+destroy_multi_logger :: proc(log: Logger, allocator := context.allocator) {
data := (^Multi_Logger_Data)(log.data)
- delete(data.loggers)
- free(data)
+ delete(data.loggers, allocator)
+ free(data, allocator)
}
multi_logger_proc :: proc(logger_data: rawptr, level: Level, text: string,
diff --git a/core/math/linalg/general.odin b/core/math/linalg/general.odin
index f82d75bff..4a0150972 100644
--- a/core/math/linalg/general.odin
+++ b/core/math/linalg/general.odin
@@ -167,6 +167,18 @@ vector_triple_product :: proc "contextless" (a, b, c: $T/[$N]$E) -> T where IS_N
length :: proc{vector_length, quaternion_length}
length2 :: proc{vector_length2, quaternion_length2}
+
+@(require_results)
+clamp_length :: proc "contextless" (v: $T/[$N]$E, a: E) -> T where IS_FLOAT(E) {
+ if a <= 0 {
+ return 0
+ }
+
+ m2 := length2(v)
+ return v if (m2 <= a*a) else (v / sqrt(m2) * a) // returns original when m2 is 0
+}
+
+
@(require_results)
projection :: proc "contextless" (x, normal: $T/[$N]$E) -> T where IS_NUMERIC(E) {
return dot(x, normal) / dot(normal, normal) * normal
@@ -405,6 +417,13 @@ adjugate :: proc{
matrix4x4_adjugate,
}
+cofactor :: proc{
+ matrix1x1_cofactor,
+ matrix2x2_cofactor,
+ matrix3x3_cofactor,
+ matrix4x4_cofactor,
+}
+
inverse_transpose :: proc{
matrix1x1_inverse_transpose,
matrix2x2_inverse_transpose,
@@ -467,9 +486,9 @@ matrix3x3_determinant :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (det: T) #
}
@(require_results)
matrix4x4_determinant :: proc "contextless" (m: $M/matrix[4, 4]$T) -> (det: T) #no_bounds_check {
- a := adjugate(m)
+ c := cofactor(m)
for i in 0..<4 {
- det += m[0, i] * a[0, i]
+ det += m[0, i] * c[0, i]
}
return
}
@@ -486,6 +505,47 @@ matrix1x1_adjugate :: proc "contextless" (x: $M/matrix[1, 1]$T) -> (y: M) #no_bo
@(require_results)
matrix2x2_adjugate :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) #no_bounds_check {
y[0, 0] = +x[1, 1]
+ y[0, 1] = -x[0, 1]
+ y[1, 0] = -x[1, 0]
+ y[1, 1] = +x[0, 0]
+ return
+}
+
+@(require_results)
+matrix3x3_adjugate :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (y: M) #no_bounds_check {
+ y[0, 0] = +(m[1, 1] * m[2, 2] - m[2, 1] * m[1, 2])
+ y[1, 0] = -(m[1, 0] * m[2, 2] - m[2, 0] * m[1, 2])
+ y[2, 0] = +(m[1, 0] * m[2, 1] - m[2, 0] * m[1, 1])
+ y[0, 1] = -(m[0, 1] * m[2, 2] - m[2, 1] * m[0, 2])
+ y[1, 1] = +(m[0, 0] * m[2, 2] - m[2, 0] * m[0, 2])
+ y[2, 1] = -(m[0, 0] * m[2, 1] - m[2, 0] * m[0, 1])
+ y[0, 2] = +(m[0, 1] * m[1, 2] - m[1, 1] * m[0, 2])
+ y[1, 2] = -(m[0, 0] * m[1, 2] - m[1, 0] * m[0, 2])
+ y[2, 2] = +(m[0, 0] * m[1, 1] - m[1, 0] * m[0, 1])
+ return
+}
+
+@(require_results)
+matrix4x4_adjugate :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) #no_bounds_check {
+ for i in 0..<4 {
+ for j in 0..<4 {
+ sign: T = 1 if (i + j) % 2 == 0 else -1
+ y[i, j] = sign * matrix_minor(x, j, i)
+ }
+ }
+ return
+}
+
+
+@(require_results)
+matrix1x1_cofactor :: proc "contextless" (x: $M/matrix[1, 1]$T) -> (y: M) #no_bounds_check {
+ y = x
+ return
+}
+
+@(require_results)
+matrix2x2_cofactor :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) #no_bounds_check {
+ y[0, 0] = +x[1, 1]
y[0, 1] = -x[1, 0]
y[1, 0] = -x[0, 1]
y[1, 1] = +x[0, 0]
@@ -493,7 +553,7 @@ matrix2x2_adjugate :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) #no_bo
}
@(require_results)
-matrix3x3_adjugate :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (y: M) #no_bounds_check {
+matrix3x3_cofactor :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (y: M) #no_bounds_check {
y[0, 0] = +(m[1, 1] * m[2, 2] - m[2, 1] * m[1, 2])
y[0, 1] = -(m[1, 0] * m[2, 2] - m[2, 0] * m[1, 2])
y[0, 2] = +(m[1, 0] * m[2, 1] - m[2, 0] * m[1, 1])
@@ -508,7 +568,7 @@ matrix3x3_adjugate :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (y: M) #no_bo
@(require_results)
-matrix4x4_adjugate :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) #no_bounds_check {
+matrix4x4_cofactor :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) #no_bounds_check {
for i in 0..<4 {
for j in 0..<4 {
sign: T = 1 if (i + j) % 2 == 0 else -1
@@ -544,19 +604,19 @@ matrix2x2_inverse_transpose :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y:
@(require_results)
matrix3x3_inverse_transpose :: proc "contextless" (x: $M/matrix[3, 3]$T) -> (y: M) #no_bounds_check {
- a := adjugate(x)
+ c := cofactor(x)
d := determinant(x)
when intrinsics.type_is_integer(T) {
for i in 0..<3 {
for j in 0..<3 {
- y[i, j] = a[i, j] / d
+ y[i, j] = c[i, j] / d
}
}
} else {
id := 1/d
for i in 0..<3 {
for j in 0..<3 {
- y[i, j] = a[i, j] * id
+ y[i, j] = c[i, j] * id
}
}
}
@@ -565,22 +625,22 @@ matrix3x3_inverse_transpose :: proc "contextless" (x: $M/matrix[3, 3]$T) -> (y:
@(require_results)
matrix4x4_inverse_transpose :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) #no_bounds_check {
- a := adjugate(x)
+ c := cofactor(x)
d: T
for i in 0..<4 {
- d += x[0, i] * a[0, i]
+ d += x[0, i] * c[0, i]
}
when intrinsics.type_is_integer(T) {
for i in 0..<4 {
for j in 0..<4 {
- y[i, j] = a[i, j] / d
+ y[i, j] = c[i, j] / d
}
}
} else {
id := 1/d
for i in 0..<4 {
for j in 0..<4 {
- y[i, j] = a[i, j] * id
+ y[i, j] = c[i, j] * id
}
}
}
@@ -613,19 +673,19 @@ matrix2x2_inverse :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) #no_bou
@(require_results)
matrix3x3_inverse :: proc "contextless" (x: $M/matrix[3, 3]$T) -> (y: M) #no_bounds_check {
- a := adjugate(x)
+ c := cofactor(x)
d := determinant(x)
when intrinsics.type_is_integer(T) {
for i in 0..<3 {
for j in 0..<3 {
- y[i, j] = a[j, i] / d
+ y[i, j] = c[j, i] / d
}
}
} else {
id := 1/d
for i in 0..<3 {
for j in 0..<3 {
- y[i, j] = a[j, i] * id
+ y[i, j] = c[j, i] * id
}
}
}
@@ -634,22 +694,22 @@ matrix3x3_inverse :: proc "contextless" (x: $M/matrix[3, 3]$T) -> (y: M) #no_bou
@(require_results)
matrix4x4_inverse :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) #no_bounds_check {
- a := adjugate(x)
+ c := cofactor(x)
d: T
for i in 0..<4 {
- d += x[0, i] * a[0, i]
+ d += x[0, i] * c[0, i]
}
when intrinsics.type_is_integer(T) {
for i in 0..<4 {
for j in 0..<4 {
- y[i, j] = a[j, i] / d
+ y[i, j] = c[j, i] / d
}
}
} else {
id := 1/d
for i in 0..<4 {
for j in 0..<4 {
- y[i, j] = a[j, i] * id
+ y[i, j] = c[j, i] * id
}
}
}
diff --git a/core/math/linalg/glsl/linalg_glsl.odin b/core/math/linalg/glsl/linalg_glsl.odin
index 5444f89e2..bd2cf416a 100644
--- a/core/math/linalg/glsl/linalg_glsl.odin
+++ b/core/math/linalg/glsl/linalg_glsl.odin
@@ -473,6 +473,22 @@ floor :: proc{
@(require_results) floor_dvec3 :: proc "c" (x: dvec3) -> dvec3 { return {floor(x.x), floor(x.y), floor(x.z)} }
@(require_results) floor_dvec4 :: proc "c" (x: dvec4) -> dvec4 { return {floor(x.x), floor(x.y), floor(x.z), floor(x.w)} }
+trunc :: proc{
+ trunc_f32,
+ trunc_f64,
+ trunc_vec2,
+ trunc_vec3,
+ trunc_vec4,
+ trunc_dvec2,
+ trunc_dvec3,
+ trunc_dvec4,
+}
+@(require_results) trunc_vec2 :: proc "c" (x: vec2) -> vec2 { return {trunc(x.x), trunc(x.y)} }
+@(require_results) trunc_vec3 :: proc "c" (x: vec3) -> vec3 { return {trunc(x.x), trunc(x.y), trunc(x.z)} }
+@(require_results) trunc_vec4 :: proc "c" (x: vec4) -> vec4 { return {trunc(x.x), trunc(x.y), trunc(x.z), trunc(x.w)} }
+@(require_results) trunc_dvec2 :: proc "c" (x: dvec2) -> dvec2 { return {trunc(x.x), trunc(x.y)} }
+@(require_results) trunc_dvec3 :: proc "c" (x: dvec3) -> dvec3 { return {trunc(x.x), trunc(x.y), trunc(x.z)} }
+@(require_results) trunc_dvec4 :: proc "c" (x: dvec4) -> dvec4 { return {trunc(x.x), trunc(x.y), trunc(x.z), trunc(x.w)} }
round :: proc{
@@ -1866,6 +1882,13 @@ adjugate :: proc{
adjugate_matrix4x4,
}
+cofactor :: proc{
+ cofactor_matrix1x1,
+ cofactor_matrix2x2,
+ cofactor_matrix3x3,
+ cofactor_matrix4x4,
+}
+
inverse_transpose :: proc{
inverse_transpose_matrix1x1,
inverse_transpose_matrix2x2,
@@ -1928,9 +1951,9 @@ determinant_matrix3x3 :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (det: T) {
}
@(require_results)
determinant_matrix4x4 :: proc "contextless" (m: $M/matrix[4, 4]$T) -> (det: T) {
- a := adjugate(m)
+ c := cofactor(m)
#no_bounds_check for i in 0..<4 {
- det += m[0, i] * a[0, i]
+ det += m[0, i] * c[0, i]
}
return
}
@@ -1947,6 +1970,47 @@ adjugate_matrix1x1 :: proc "contextless" (x: $M/matrix[1, 1]$T) -> (y: M) {
@(require_results)
adjugate_matrix2x2 :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) {
y[0, 0] = +x[1, 1]
+ y[0, 1] = -x[0, 1]
+ y[1, 0] = -x[1, 0]
+ y[1, 1] = +x[0, 0]
+ return
+}
+
+@(require_results)
+adjugate_matrix3x3 :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (y: M) {
+ y[0, 0] = +(m[1, 1] * m[2, 2] - m[2, 1] * m[1, 2])
+ y[1, 0] = -(m[1, 0] * m[2, 2] - m[2, 0] * m[1, 2])
+ y[2, 0] = +(m[1, 0] * m[2, 1] - m[2, 0] * m[1, 1])
+ y[0, 1] = -(m[0, 1] * m[2, 2] - m[2, 1] * m[0, 2])
+ y[1, 1] = +(m[0, 0] * m[2, 2] - m[2, 0] * m[0, 2])
+ y[2, 1] = -(m[0, 0] * m[2, 1] - m[2, 0] * m[0, 1])
+ y[0, 2] = +(m[0, 1] * m[1, 2] - m[1, 1] * m[0, 2])
+ y[1, 2] = -(m[0, 0] * m[1, 2] - m[1, 0] * m[0, 2])
+ y[2, 2] = +(m[0, 0] * m[1, 1] - m[1, 0] * m[0, 1])
+ return
+}
+
+@(require_results)
+adjugate_matrix4x4 :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) {
+ for i in 0..<4 {
+ for j in 0..<4 {
+ sign: T = 1 if (i + j) % 2 == 0 else -1
+ y[i, j] = sign * matrix_minor(x, j, i)
+ }
+ }
+ return
+}
+
+
+@(require_results)
+cofactor_matrix1x1 :: proc "contextless" (x: $M/matrix[1, 1]$T) -> (y: M) {
+ y = x
+ return
+}
+
+@(require_results)
+cofactor_matrix2x2 :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) {
+ y[0, 0] = +x[1, 1]
y[0, 1] = -x[1, 0]
y[1, 0] = -x[0, 1]
y[1, 1] = +x[0, 0]
@@ -1954,7 +2018,7 @@ adjugate_matrix2x2 :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) {
}
@(require_results)
-adjugate_matrix3x3 :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (y: M) {
+cofactor_matrix3x3 :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (y: M) {
y[0, 0] = +(m[1, 1] * m[2, 2] - m[2, 1] * m[1, 2])
y[0, 1] = -(m[1, 0] * m[2, 2] - m[2, 0] * m[1, 2])
y[0, 2] = +(m[1, 0] * m[2, 1] - m[2, 0] * m[1, 1])
@@ -1969,7 +2033,7 @@ adjugate_matrix3x3 :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (y: M) {
@(require_results)
-adjugate_matrix4x4 :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) {
+cofactor_matrix4x4 :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) {
for i in 0..<4 {
for j in 0..<4 {
sign: T = 1 if (i + j) % 2 == 0 else -1
@@ -2005,19 +2069,19 @@ inverse_transpose_matrix2x2 :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y:
@(require_results)
inverse_transpose_matrix3x3 :: proc "contextless" (x: $M/matrix[3, 3]$T) -> (y: M) #no_bounds_check {
- a := adjugate(x)
+ c := cofactor(x)
d := determinant(x)
when intrinsics.type_is_integer(T) {
for i in 0..<3 {
for j in 0..<3 {
- y[i, j] = a[i, j] / d
+ y[i, j] = c[i, j] / d
}
}
} else {
id := 1/d
for i in 0..<3 {
for j in 0..<3 {
- y[i, j] = a[i, j] * id
+ y[i, j] = c[i, j] * id
}
}
}
@@ -2026,22 +2090,22 @@ inverse_transpose_matrix3x3 :: proc "contextless" (x: $M/matrix[3, 3]$T) -> (y:
@(require_results)
inverse_transpose_matrix4x4 :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) #no_bounds_check {
- a := adjugate(x)
+ c := cofactor(x)
d: T
for i in 0..<4 {
- d += x[0, i] * a[0, i]
+ d += x[0, i] * c[0, i]
}
when intrinsics.type_is_integer(T) {
for i in 0..<4 {
for j in 0..<4 {
- y[i, j] = a[i, j] / d
+ y[i, j] = c[i, j] / d
}
}
} else {
id := 1/d
for i in 0..<4 {
for j in 0..<4 {
- y[i, j] = a[i, j] * id
+ y[i, j] = c[i, j] * id
}
}
}
@@ -2074,19 +2138,19 @@ inverse_matrix2x2 :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) {
@(require_results)
inverse_matrix3x3 :: proc "contextless" (x: $M/matrix[3, 3]$T) -> (y: M) #no_bounds_check {
- a := adjugate(x)
+ c := cofactor(x)
d := determinant(x)
when intrinsics.type_is_integer(T) {
for i in 0..<3 {
for j in 0..<3 {
- y[i, j] = a[j, i] / d
+ y[i, j] = c[j, i] / d
}
}
} else {
id := 1/d
for i in 0..<3 {
for j in 0..<3 {
- y[i, j] = a[j, i] * id
+ y[i, j] = c[j, i] * id
}
}
}
@@ -2095,22 +2159,22 @@ inverse_matrix3x3 :: proc "contextless" (x: $M/matrix[3, 3]$T) -> (y: M) #no_bou
@(require_results)
inverse_matrix4x4 :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) #no_bounds_check {
- a := adjugate(x)
+ c := cofactor(x)
d: T
for i in 0..<4 {
- d += x[0, i] * a[0, i]
+ d += x[0, i] * c[0, i]
}
when intrinsics.type_is_integer(T) {
for i in 0..<4 {
for j in 0..<4 {
- y[i, j] = a[j, i] / d
+ y[i, j] = c[j, i] / d
}
}
} else {
id := 1/d
for i in 0..<4 {
for j in 0..<4 {
- y[i, j] = a[j, i] * id
+ y[i, j] = c[j, i] * id
}
}
}
diff --git a/core/math/linalg/glsl/linalg_glsl_math.odin b/core/math/linalg/glsl/linalg_glsl_math.odin
index 82b1857ab..b4461ca3b 100644
--- a/core/math/linalg/glsl/linalg_glsl_math.odin
+++ b/core/math/linalg/glsl/linalg_glsl_math.odin
@@ -23,6 +23,7 @@ import "core:math"
@(require_results) exp2_f32 :: proc "c" (x: f32) -> f32 { return math.pow(f32(2), x) }
@(require_results) sign_f32 :: proc "c" (x: f32) -> f32 { return math.sign(x) }
@(require_results) floor_f32 :: proc "c" (x: f32) -> f32 { return math.floor(x) }
+@(require_results) trunc_f32 :: proc "c" (x: f32) -> f32 { return math.trunc(x) }
@(require_results) round_f32 :: proc "c" (x: f32) -> f32 { return math.round(x) }
@(require_results) ceil_f32 :: proc "c" (x: f32) -> f32 { return math.ceil(x) }
@(require_results) mod_f32 :: proc "c" (x, y: f32) -> f32 { return math.mod(x, y) }
@@ -55,6 +56,7 @@ fract_f32 :: proc "c" (x: f32) -> f32 {
@(require_results) exp2_f64 :: proc "c" (x: f64) -> f64 { return math.pow(f64(2), x) }
@(require_results) sign_f64 :: proc "c" (x: f64) -> f64 { return math.sign(x) }
@(require_results) floor_f64 :: proc "c" (x: f64) -> f64 { return math.floor(x) }
+@(require_results) trunc_f64 :: proc "c" (x: f64) -> f64 { return math.trunc(x) }
@(require_results) round_f64 :: proc "c" (x: f64) -> f64 { return math.round(x) }
@(require_results) ceil_f64 :: proc "c" (x: f64) -> f64 { return math.ceil(x) }
@(require_results) mod_f64 :: proc "c" (x, y: f64) -> f64 { return math.mod(x, y) }
diff --git a/core/math/linalg/hlsl/linalg_hlsl.odin b/core/math/linalg/hlsl/linalg_hlsl.odin
index a89fdddd3..cca70f9c8 100644
--- a/core/math/linalg/hlsl/linalg_hlsl.odin
+++ b/core/math/linalg/hlsl/linalg_hlsl.odin
@@ -1514,6 +1514,13 @@ adjugate :: proc{
adjugate_matrix4x4,
}
+cofactor :: proc{
+ cofactor_matrix1x1,
+ cofactor_matrix2x2,
+ cofactor_matrix3x3,
+ cofactor_matrix4x4,
+}
+
inverse_transpose :: proc{
inverse_transpose_matrix1x1,
inverse_transpose_matrix2x2,
@@ -1568,9 +1575,9 @@ determinant_matrix3x3 :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (det: T) {
}
@(require_results)
determinant_matrix4x4 :: proc "contextless" (m: $M/matrix[4, 4]$T) -> (det: T) {
- a := adjugate(m)
+ c := cofactor(m)
#no_bounds_check for i in 0..<4 {
- det += m[0, i] * a[0, i]
+ det += m[0, i] * c[0, i]
}
return
}
@@ -1587,6 +1594,47 @@ adjugate_matrix1x1 :: proc "contextless" (x: $M/matrix[1, 1]$T) -> (y: M) {
@(require_results)
adjugate_matrix2x2 :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) {
y[0, 0] = +x[1, 1]
+ y[0, 1] = -x[0, 1]
+ y[1, 0] = -x[1, 0]
+ y[1, 1] = +x[0, 0]
+ return
+}
+
+@(require_results)
+adjugate_matrix3x3 :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (y: M) {
+ y[0, 0] = +(m[1, 1] * m[2, 2] - m[2, 1] * m[1, 2])
+ y[1, 0] = -(m[1, 0] * m[2, 2] - m[2, 0] * m[1, 2])
+ y[2, 0] = +(m[1, 0] * m[2, 1] - m[2, 0] * m[1, 1])
+ y[0, 1] = -(m[0, 1] * m[2, 2] - m[2, 1] * m[0, 2])
+ y[1, 1] = +(m[0, 0] * m[2, 2] - m[2, 0] * m[0, 2])
+ y[2, 1] = -(m[0, 0] * m[2, 1] - m[2, 0] * m[0, 1])
+ y[0, 2] = +(m[0, 1] * m[1, 2] - m[1, 1] * m[0, 2])
+ y[1, 2] = -(m[0, 0] * m[1, 2] - m[1, 0] * m[0, 2])
+ y[2, 2] = +(m[0, 0] * m[1, 1] - m[1, 0] * m[0, 1])
+ return
+}
+
+@(require_results)
+adjugate_matrix4x4 :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) {
+ for i in 0..<4 {
+ for j in 0..<4 {
+ sign: T = 1 if (i + j) % 2 == 0 else -1
+ y[i, j] = sign * matrix_minor(x, j, i)
+ }
+ }
+ return
+}
+
+
+@(require_results)
+cofactor_matrix1x1 :: proc "contextless" (x: $M/matrix[1, 1]$T) -> (y: M) {
+ y = x
+ return
+}
+
+@(require_results)
+cofactor_matrix2x2 :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) {
+ y[0, 0] = +x[1, 1]
y[0, 1] = -x[1, 0]
y[1, 0] = -x[0, 1]
y[1, 1] = +x[0, 0]
@@ -1594,7 +1642,7 @@ adjugate_matrix2x2 :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) {
}
@(require_results)
-adjugate_matrix3x3 :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (y: M) {
+cofactor_matrix3x3 :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (y: M) {
y[0, 0] = +(m[1, 1] * m[2, 2] - m[2, 1] * m[1, 2])
y[0, 1] = -(m[1, 0] * m[2, 2] - m[2, 0] * m[1, 2])
y[0, 2] = +(m[1, 0] * m[2, 1] - m[2, 0] * m[1, 1])
@@ -1609,7 +1657,7 @@ adjugate_matrix3x3 :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (y: M) {
@(require_results)
-adjugate_matrix4x4 :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) {
+cofactor_matrix4x4 :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) {
for i in 0..<4 {
for j in 0..<4 {
sign: T = 1 if (i + j) % 2 == 0 else -1
@@ -1645,19 +1693,19 @@ inverse_transpose_matrix2x2 :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y:
@(require_results)
inverse_transpose_matrix3x3 :: proc "contextless" (x: $M/matrix[3, 3]$T) -> (y: M) #no_bounds_check {
- a := adjugate(x)
+ c := cofactor(x)
d := determinant(x)
when intrinsics.type_is_integer(T) {
for i in 0..<3 {
for j in 0..<3 {
- y[i, j] = a[i, j] / d
+ y[i, j] = c[i, j] / d
}
}
} else {
id := 1/d
for i in 0..<3 {
for j in 0..<3 {
- y[i, j] = a[i, j] * id
+ y[i, j] = c[i, j] * id
}
}
}
@@ -1666,22 +1714,22 @@ inverse_transpose_matrix3x3 :: proc "contextless" (x: $M/matrix[3, 3]$T) -> (y:
@(require_results)
inverse_transpose_matrix4x4 :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) #no_bounds_check {
- a := adjugate(x)
+ c := cofactor(x)
d: T
for i in 0..<4 {
- d += x[0, i] * a[0, i]
+ d += x[0, i] * c[0, i]
}
when intrinsics.type_is_integer(T) {
for i in 0..<4 {
for j in 0..<4 {
- y[i, j] = a[i, j] / d
+ y[i, j] = c[i, j] / d
}
}
} else {
id := 1/d
for i in 0..<4 {
for j in 0..<4 {
- y[i, j] = a[i, j] * id
+ y[i, j] = c[i, j] * id
}
}
}
@@ -1714,19 +1762,19 @@ inverse_matrix2x2 :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) {
@(require_results)
inverse_matrix3x3 :: proc "contextless" (x: $M/matrix[3, 3]$T) -> (y: M) #no_bounds_check {
- a := adjugate(x)
+ c := cofactor(x)
d := determinant(x)
when intrinsics.type_is_integer(T) {
for i in 0..<3 {
for j in 0..<3 {
- y[i, j] = a[j, i] / d
+ y[i, j] = c[j, i] / d
}
}
} else {
id := 1/d
for i in 0..<3 {
for j in 0..<3 {
- y[i, j] = a[j, i] * id
+ y[i, j] = c[j, i] * id
}
}
}
@@ -1735,22 +1783,22 @@ inverse_matrix3x3 :: proc "contextless" (x: $M/matrix[3, 3]$T) -> (y: M) #no_bou
@(require_results)
inverse_matrix4x4 :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) #no_bounds_check {
- a := adjugate(x)
+ c := cofactor(x)
d: T
for i in 0..<4 {
- d += x[0, i] * a[0, i]
+ d += x[0, i] * c[0, i]
}
when intrinsics.type_is_integer(T) {
for i in 0..<4 {
for j in 0..<4 {
- y[i, j] = a[j, i] / d
+ y[i, j] = c[j, i] / d
}
}
} else {
id := 1/d
for i in 0..<4 {
for j in 0..<4 {
- y[i, j] = a[j, i] * id
+ y[i, j] = c[j, i] * id
}
}
}
diff --git a/core/math/linalg/specific.odin b/core/math/linalg/specific.odin
index b841f0610..c23feddce 100644
--- a/core/math/linalg/specific.odin
+++ b/core/math/linalg/specific.odin
@@ -1207,8 +1207,8 @@ matrix2_inverse_f16 :: proc "contextless" (m: Matrix2f16) -> (c: Matrix2f16) #no
d := m[0, 0]*m[1, 1] - m[0, 1]*m[1, 0]
id := 1.0/d
c[0, 0] = +m[1, 1] * id
- c[0, 1] = -m[1, 0] * id
- c[1, 0] = -m[0, 1] * id
+ c[0, 1] = -m[0, 1] * id
+ c[1, 0] = -m[1, 0] * id
c[1, 1] = +m[0, 0] * id
return c
}
@@ -1217,8 +1217,8 @@ matrix2_inverse_f32 :: proc "contextless" (m: Matrix2f32) -> (c: Matrix2f32) #no
d := m[0, 0]*m[1, 1] - m[0, 1]*m[1, 0]
id := 1.0/d
c[0, 0] = +m[1, 1] * id
- c[0, 1] = -m[1, 0] * id
- c[1, 0] = -m[0, 1] * id
+ c[0, 1] = -m[0, 1] * id
+ c[1, 0] = -m[1, 0] * id
c[1, 1] = +m[0, 0] * id
return c
}
@@ -1227,8 +1227,8 @@ matrix2_inverse_f64 :: proc "contextless" (m: Matrix2f64) -> (c: Matrix2f64) #no
d := m[0, 0]*m[1, 1] - m[0, 1]*m[1, 0]
id := 1.0/d
c[0, 0] = +m[1, 1] * id
- c[0, 1] = -m[1, 0] * id
- c[1, 0] = -m[0, 1] * id
+ c[0, 1] = -m[0, 1] * id
+ c[1, 0] = -m[1, 0] * id
c[1, 1] = +m[0, 0] * id
return c
}
diff --git a/core/math/rand/rand.odin b/core/math/rand/rand.odin
index 474277e84..537256d32 100644
--- a/core/math/rand/rand.odin
+++ b/core/math/rand/rand.odin
@@ -16,9 +16,9 @@ Generator_Query_Info :: runtime.Random_Generator_Query_Info
Default_Random_State :: runtime.Default_Random_State
default_random_generator :: runtime.default_random_generator
+@(require_results)
create :: proc(seed: u64) -> (state: Default_Random_State) {
seed := seed
- runtime.default_random_generator(&state)
runtime.default_random_generator_proc(&state, .Reset, ([^]byte)(&seed)[:size_of(seed)])
return
}
@@ -34,30 +34,6 @@ Example:
import "core:fmt"
set_global_seed_example :: proc() {
- rand.set_global_seed(1)
- fmt.println(rand.uint64())
- }
-
-Possible Output:
-
- 10
-*/
-@(deprecated="Prefer `rand.reset`")
-set_global_seed :: proc(seed: u64) {
- runtime.random_generator_reset_u64(context.random_generator, seed)
-}
-
-/*
-Reset the seed used by the context.random_generator.
-
-Inputs:
-- seed: The seed value
-
-Example:
- import "core:math/rand"
- import "core:fmt"
-
- set_global_seed_example :: proc() {
rand.reset(1)
fmt.println(rand.uint64())
}
@@ -491,7 +467,7 @@ Example:
Possible Output:
15.312
- 673.130
+ 273.130
*/
@(require_results) float32_range :: proc(low, high: f32, gen := context.random_generator) -> (val: f32) {
diff --git a/core/mem/alloc.odin b/core/mem/alloc.odin
index fac58daaf..1094e7381 100644
--- a/core/mem/alloc.odin
+++ b/core/mem/alloc.odin
@@ -786,6 +786,27 @@ delete_map :: proc(
}
/*
+Free an SoA slice.
+*/
+delete_soa_slice :: proc(
+ array: $T/#soa[]$E,
+ allocator := context.allocator,
+ loc := #caller_location,
+) -> Allocator_Error {
+ return runtime.delete_soa_slice(array, allocator, loc)
+}
+
+/*
+Free an SoA dynamic array.
+*/
+delete_soa_dynamic_array :: proc(
+ array: $T/#soa[dynamic]$E,
+ loc := #caller_location,
+) -> Allocator_Error {
+ return runtime.delete_soa_dynamic_array(array, loc)
+}
+
+/*
Free.
*/
delete :: proc{
@@ -794,6 +815,8 @@ delete :: proc{
delete_dynamic_array,
delete_slice,
delete_map,
+ delete_soa_slice,
+ delete_soa_dynamic_array,
}
/*
@@ -900,8 +923,7 @@ make_dynamic_array :: proc(
Allocate a dynamic array with initial length.
This procedure creates a dynamic array of type `T`, with `allocator` as its
-backing allocator, and initial capacity of `0`, and initial length specified by
-`len`.
+backing allocator, and initial capacity and length specified by `len`.
*/
@(require_results)
make_dynamic_array_len :: proc(
@@ -910,7 +932,7 @@ make_dynamic_array_len :: proc(
allocator := context.allocator,
loc := #caller_location,
) -> (T, Allocator_Error) {
- return runtime.make_dynamic_array_len_cap(T, len, len, allocator, loc)
+ return runtime.make_dynamic_array_len(T, len, allocator, loc)
}
/*
@@ -965,6 +987,71 @@ make_multi_pointer :: proc(
}
/*
+Allocate an SoA slice.
+
+This procedure allocates an SoA slice of type `T` with length `len`, from an
+allocator specified by `allocator`, and returns the allocated SoA slice.
+*/
+@(require_results)
+make_soa_slice :: proc(
+ $T: typeid/#soa[]$E,
+ #any_int len: int,
+ allocator := context.allocator,
+ loc := #caller_location
+) -> (array: T, err: Allocator_Error) {
+ return runtime.make_soa_slice(T, len, allocator, loc)
+}
+
+/*
+Allocate an SoA dynamic array.
+
+This procedure creates an SoA dynamic array of type `T`, with `allocator` as
+its backing allocator, and initial length and capacity of `0`.
+*/
+@(require_results)
+make_soa_dynamic_array :: proc(
+ $T: typeid/#soa[dynamic]$E,
+ allocator := context.allocator,
+ loc := #caller_location
+) -> (array: T, err: Allocator_Error) {
+ return runtime.make_soa_dynamic_array(T, allocator, loc)
+}
+
+/*
+Allocate an SoA dynamic array with initial length.
+
+This procedure creates an SoA dynamic array of type `T`, with `allocator` as its
+backing allocator, and initial capacity and length specified by `len`.
+*/
+@(require_results)
+make_soa_dynamic_array_len :: proc(
+ $T: typeid/#soa[dynamic]$E,
+ #any_int len: int,
+ allocator := context.allocator,
+ loc := #caller_location
+) -> (array: T, err: Allocator_Error) {
+ return runtime.make_soa_dynamic_array_len(T, len, allocator, loc)
+}
+
+/*
+Allocate an SoA dynamic array with initial length and capacity.
+
+This procedure creates an SoA dynamic array of type `T`, with `allocator` as its
+backing allocator, and initial capacity specified by `cap`, and initial length
+specified by `len`.
+*/
+@(require_results)
+make_soa_dynamic_array_len_cap :: proc(
+ $T: typeid/#soa[dynamic]$E,
+ #any_int len: int,
+ #any_int cap: int,
+ allocator := context.allocator,
+ loc := #caller_location
+) -> (array: T, err: Allocator_Error) {
+ return runtime.make_soa_dynamic_array_len_cap(T, len, cap, allocator, loc)
+}
+
+/*
Allocate.
*/
make :: proc{
@@ -974,6 +1061,10 @@ make :: proc{
make_dynamic_array_len_cap,
make_map,
make_multi_pointer,
+ make_soa_slice,
+ make_soa_dynamic_array,
+ make_soa_dynamic_array_len,
+ make_soa_dynamic_array_len_cap,
}
/*
diff --git a/core/mem/allocators.odin b/core/mem/allocators.odin
index 13d509f1e..028be58e3 100644
--- a/core/mem/allocators.odin
+++ b/core/mem/allocators.odin
@@ -140,14 +140,6 @@ arena_init :: proc(a: ^Arena, data: []byte) {
a.temp_count = 0
}
-@(deprecated="prefer 'mem.arena_init'")
-init_arena :: proc(a: ^Arena, data: []byte) {
- a.data = data
- a.offset = 0
- a.peak_used = 0
- a.temp_count = 0
-}
-
/*
Allocate memory from an arena.
@@ -786,14 +778,6 @@ stack_init :: proc(s: ^Stack, data: []byte) {
s.peak_used = 0
}
-@(deprecated="prefer 'mem.stack_init'")
-init_stack :: proc(s: ^Stack, data: []byte) {
- s.data = data
- s.prev_offset = 0
- s.curr_offset = 0
- s.peak_used = 0
-}
-
/*
Allocate memory from stack.
@@ -1162,13 +1146,6 @@ small_stack_init :: proc(s: ^Small_Stack, data: []byte) {
s.peak_used = 0
}
-@(deprecated="prefer 'small_stack_init'")
-init_small_stack :: proc(s: ^Small_Stack, data: []byte) {
- s.data = data
- s.offset = 0
- s.peak_used = 0
-}
-
/*
Small stack allocator.
diff --git a/core/mem/doc.odin b/core/mem/doc.odin
index 98755d797..580b08c96 100644
--- a/core/mem/doc.odin
+++ b/core/mem/doc.odin
@@ -16,8 +16,8 @@ a multipointer can be indexed, but does not have a definite length. A slice is
a pointer that points to multiple objects equipped with the length, specifying
the amount of objects a slice points to.
-When object's values are read through a pointer, that operation is called a
-*load* operation. When memory is read through a pointer, that operation is
+When an object's values are read through a pointer, that operation is called a
+*load* operation. When memory is written to through a pointer, that operation is
called a *store* operation. Both of these operations can be called a *memory
access operation*.
diff --git a/core/mem/mem.odin b/core/mem/mem.odin
index ccbc77798..b2a7158a1 100644
--- a/core/mem/mem.odin
+++ b/core/mem/mem.odin
@@ -685,11 +685,4 @@ calc_padding_with_header :: proc "contextless" (ptr: uintptr, align: uintptr, he
}
}
return int(padding)
-}
-
-@(require_results, deprecated="prefer 'slice.clone'")
-clone_slice :: proc(slice: $T/[]$E, allocator := context.allocator, loc := #caller_location) -> (new_slice: T) {
- new_slice, _ = make(T, len(slice), allocator, loc)
- runtime.copy(new_slice, slice)
- return new_slice
-}
+} \ No newline at end of file
diff --git a/core/mem/tlsf/tlsf_internal.odin b/core/mem/tlsf/tlsf_internal.odin
index cac151183..66141fcbb 100644
--- a/core/mem/tlsf/tlsf_internal.odin
+++ b/core/mem/tlsf/tlsf_internal.odin
@@ -260,7 +260,7 @@ adjust_request_size :: proc(size, align: uint) -> (adjusted: uint) {
// aligned size must not exceed `BLOCK_SIZE_MAX`, or we'll go out of bounds on `sl_bitmap`.
if aligned := align_up(size, align); aligned < BLOCK_SIZE_MAX {
- adjusted = min(aligned, BLOCK_SIZE_MAX)
+ adjusted = max(aligned, BLOCK_SIZE_MIN)
}
return
}
diff --git a/core/mem/tracking_allocator.odin b/core/mem/tracking_allocator.odin
index cf780de3f..25c547471 100644
--- a/core/mem/tracking_allocator.odin
+++ b/core/mem/tracking_allocator.odin
@@ -35,11 +35,17 @@ Tracking_Allocator_Bad_Free_Entry :: struct {
}
/*
+Callback type for when tracking allocator runs into a bad free.
+*/
+Tracking_Allocator_Bad_Free_Callback :: proc(t: ^Tracking_Allocator, memory: rawptr, location: runtime.Source_Code_Location)
+
+/*
Tracking allocator data.
*/
Tracking_Allocator :: struct {
backing: Allocator,
allocation_map: map[rawptr]Tracking_Allocator_Entry,
+ bad_free_callback: Tracking_Allocator_Bad_Free_Callback,
bad_free_array: [dynamic]Tracking_Allocator_Bad_Free_Entry,
mutex: sync.Mutex,
clear_on_free_all: bool,
@@ -61,6 +67,7 @@ allocate the tracked data.
tracking_allocator_init :: proc(t: ^Tracking_Allocator, backing_allocator: Allocator, internals_allocator := context.allocator) {
t.backing = backing_allocator
t.allocation_map.allocator = internals_allocator
+ t.bad_free_callback = tracking_allocator_bad_free_callback_panic
t.bad_free_array.allocator = internals_allocator
if .Free_All in query_features(t.backing) {
t.clear_on_free_all = true
@@ -110,14 +117,43 @@ tracking_allocator_reset :: proc(t: ^Tracking_Allocator) {
}
/*
+Default behavior for a bad free: Crash with error message that says where the
+bad free happened.
+
+Override Tracking_Allocator.bad_free_callback to have something else happen. For
+example, you can use tracking_allocator_bad_free_callback_add_to_array to return
+the tracking allocator to the old behavior, where the bad_free_array was used.
+*/
+tracking_allocator_bad_free_callback_panic :: proc(t: ^Tracking_Allocator, memory: rawptr, location: runtime.Source_Code_Location) {
+ runtime.print_caller_location(location)
+ runtime.print_string(" Tracking allocator error: Bad free of pointer ")
+ runtime.print_uintptr(uintptr(memory))
+ runtime.print_string("\n")
+ runtime.trap()
+}
+
+/*
+Alternative behavior for a bad free: Store in `bad_free_array`. If you use this,
+then you must make sure to check Tracking_Allocator.bad_free_array at some point.
+*/
+tracking_allocator_bad_free_callback_add_to_array :: proc(t: ^Tracking_Allocator, memory: rawptr, location: runtime.Source_Code_Location) {
+ append(&t.bad_free_array, Tracking_Allocator_Bad_Free_Entry {
+ memory = memory,
+ location = location,
+ })
+}
+
+/*
Tracking allocator.
The tracking allocator is an allocator wrapper that tracks memory allocations.
This allocator stores all the allocations in a map. Whenever a pointer that's
not inside of the map is freed, the `bad_free_array` entry is added.
-An example of how to use the `Tracking_Allocator` to track subsequent allocations
-in your program and report leaks and bad frees:
+Here follows an example of how to use the `Tracking_Allocator` to track
+subsequent allocations in your program and report leaks. By default, the
+tracking allocator will crash on bad frees. You can override that behavior by
+overriding `track.bad_free_callback`.
Example:
@@ -137,9 +173,6 @@ Example:
for _, leak in track.allocation_map {
fmt.printf("%v leaked %m\n", leak.location, leak.size)
}
- for bad_free in track.bad_free_array {
- fmt.printf("%v allocation %p was freed badly\n", bad_free.location, bad_free.memory)
- }
}
*/
@(require_results)
@@ -191,10 +224,9 @@ tracking_allocator_proc :: proc(
}
if mode == .Free && old_memory != nil && old_memory not_in data.allocation_map {
- append(&data.bad_free_array, Tracking_Allocator_Bad_Free_Entry{
- memory = old_memory,
- location = loc,
- })
+ if data.bad_free_callback != nil {
+ data.bad_free_callback(data, old_memory, loc)
+ }
} else {
result = data.backing.procedure(data.backing.data, mode, size, alignment, old_memory, old_size, loc) or_return
}
diff --git a/core/mem/virtual/arena.odin b/core/mem/virtual/arena.odin
index 79407d80d..5191505cf 100644
--- a/core/mem/virtual/arena.odin
+++ b/core/mem/virtual/arena.odin
@@ -204,8 +204,9 @@ arena_free_all :: proc(arena: ^Arena, loc := #caller_location) {
}
// Zero the first block's memory
if arena.curr_block != nil {
- mem.zero(arena.curr_block.base, int(arena.curr_block.used))
+ curr_block_used := int(arena.curr_block.used)
arena.curr_block.used = 0
+ mem.zero(arena.curr_block.base, curr_block_used)
}
arena.total_used = 0
case .Static, .Buffer:
@@ -327,10 +328,24 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
case size == 0:
err = .Mode_Not_Implemented
return
- case (uintptr(old_data) & uintptr(alignment-1) == 0) && size < old_size:
- // shrink data in-place
- data = old_data[:size]
- return
+ case uintptr(old_data) & uintptr(alignment-1) == 0:
+ if size < old_size {
+ // shrink data in-place
+ data = old_data[:size]
+ return
+ }
+
+ if block := arena.curr_block; block != nil {
+ start := uint(uintptr(old_memory)) - uint(uintptr(block.base))
+ old_end := start + old_size
+ new_end := start + size
+ if start < old_end && old_end == block.used && new_end <= block.reserved {
+ // grow data in-place, adjusting next allocation
+ _ = alloc_from_memory_block(block, new_end - old_end, 1, default_commit_size=arena.default_commit_size) or_return
+ data = block.base[start:new_end]
+ return
+ }
+ }
}
new_memory := arena_alloc(arena, size, alignment, location) or_return
@@ -401,9 +416,10 @@ arena_temp_end :: proc(temp: Arena_Temp, loc := #caller_location) {
if block := arena.curr_block; block != nil {
assert(block.used >= temp.used, "out of order use of arena_temp_end", loc)
- amount_to_zero := min(block.used-temp.used, block.reserved-block.used)
+ amount_to_zero := block.used-temp.used
mem.zero_slice(block.base[temp.used:][:amount_to_zero])
block.used = temp.used
+ arena.total_used -= amount_to_zero
}
}
diff --git a/core/net/common.odin b/core/net/common.odin
index 263fc770f..12add8225 100644
--- a/core/net/common.odin
+++ b/core/net/common.odin
@@ -95,6 +95,7 @@ Resolve_Error :: enum u32 {
}
DNS_Error :: enum u32 {
+ None = 0,
Invalid_Hostname_Error = 1,
Invalid_Hosts_Config_Error,
Invalid_Resolv_Config_Error,
@@ -147,6 +148,9 @@ IP6_Loopback :: IP6_Address{0, 0, 0, 0, 0, 0, 0, 1}
IP4_Any := IP4_Address{}
IP6_Any := IP6_Address{}
+IP4_mDNS_Broadcast := Endpoint{address=IP4_Address{224, 0, 0, 251}, port=5353}
+IP6_mDNS_Broadcast := Endpoint{address=IP6_Address{65282, 0, 0, 0, 0, 0, 0, 251}, port = 5353}
+
Endpoint :: struct {
address: Address,
port: int,
diff --git a/core/net/dns.odin b/core/net/dns.odin
index ffb97fc5b..7eb543db3 100644
--- a/core/net/dns.odin
+++ b/core/net/dns.odin
@@ -7,10 +7,11 @@ package net
*/
/*
- Copyright 2022 Tetralux <tetraluxonpc@gmail.com>
- Copyright 2022 Colin Davidson <colrdavidson@gmail.com>
- Copyright 2022 Jeroen van Rijn <nom@duclavier.com>.
- Copyright 2024 Feoramund <rune@swevencraft.org>.
+ Copyright 2022 Tetralux <tetraluxonpc@gmail.com>
+ Copyright 2022 Colin Davidson <colrdavidson@gmail.com>
+ Copyright 2022 Jeroen van Rijn <nom@duclavier.com>.
+ Copyright 2024 Feoramund <rune@swevencraft.org>.
+ Copyright 2025 Christiano Haesbaert <haesbaert@haesbaert.org>.
Made available under Odin's BSD-3 license.
List of contributors:
@@ -18,12 +19,14 @@ package net
Colin Davidson: Linux platform code, OSX platform code, Odin-native DNS resolver
Jeroen van Rijn: Cross platform unification, code style, documentation
Feoramund: FreeBSD platform code
+ Haesbaert: Security fixes
*/
import "core:mem"
import "core:strings"
import "core:time"
import "core:os"
+import "core:math/rand"
/*
Default configuration for DNS resolution.
*/
@@ -50,9 +53,12 @@ init_dns_configuration :: proc() {
dns_configuration.hosts_file, _ = replace_environment_path(dns_configuration.hosts_file)
}
+@(fini, private)
destroy_dns_configuration :: proc() {
delete(dns_configuration.resolv_conf)
+ dns_configuration.resolv_conf = ""
delete(dns_configuration.hosts_file)
+ dns_configuration.hosts_file = ""
}
dns_configuration := DEFAULT_DNS_CONFIGURATION
@@ -132,7 +138,14 @@ resolve_ip4 :: proc(hostname_and_maybe_port: string) -> (ep4: Endpoint, err: Net
return
}
case Host:
- recs, _ := get_dns_records_from_os(t.hostname, .IP4, context.temp_allocator)
+ recs: []DNS_Record
+
+ if ODIN_OS != .Windows && strings.has_suffix(t.hostname, ".local") {
+ recs, _ = get_dns_records_from_nameservers(t.hostname, .IP4, {IP4_mDNS_Broadcast}, nil, context.temp_allocator)
+ } else {
+ recs, _ = get_dns_records_from_os(t.hostname, .IP4, context.temp_allocator)
+ }
+
if len(recs) == 0 {
err = .Unable_To_Resolve
return
@@ -159,7 +172,14 @@ resolve_ip6 :: proc(hostname_and_maybe_port: string) -> (ep6: Endpoint, err: Net
return t, nil
}
case Host:
- recs, _ := get_dns_records_from_os(t.hostname, .IP6, context.temp_allocator)
+ recs: []DNS_Record
+
+ if ODIN_OS != .Windows && strings.has_suffix(t.hostname, ".local") {
+ recs, _ = get_dns_records_from_nameservers(t.hostname, .IP6, {IP6_mDNS_Broadcast}, nil, context.temp_allocator)
+ } else {
+ recs, _ = get_dns_records_from_os(t.hostname, .IP6, context.temp_allocator)
+ }
+
if len(recs) == 0 {
err = .Unable_To_Resolve
return
@@ -210,7 +230,7 @@ get_dns_records_from_nameservers :: proc(hostname: string, type: DNS_Record_Type
}
hdr := DNS_Header{
- id = 0,
+ id = u16be(rand.uint32()),
is_response = false,
opcode = 0,
is_authoritative = false,
@@ -255,23 +275,23 @@ get_dns_records_from_nameservers :: proc(hostname: string, type: DNS_Record_Type
return nil, .Connection_Error
}
- // recv_sz, _, recv_err := recv_udp(conn, dns_response_buf[:])
- // if recv_err == UDP_Recv_Error.Timeout {
- // continue
- // } else if recv_err != nil {
- // continue
- // }
- recv_sz, _ := recv_udp(conn, dns_response_buf[:]) or_continue
+ recv_sz, src := recv_udp(conn, dns_response_buf[:]) or_continue
if recv_sz == 0 {
continue
}
+ if src != name_server {
+ continue
+ }
dns_response = dns_response_buf[:recv_sz]
- rsp, _ok := parse_response(dns_response, type)
+ rsp, xid, _ok := parse_response(dns_response, type)
if !_ok {
return nil, .Server_Error
}
+ if id != xid {
+ continue
+ }
if len(rsp) == 0 {
continue
@@ -525,18 +545,21 @@ decode_hostname :: proc(packet: []u8, start_idx: int, allocator := context.alloc
return
}
- if packet[cur_idx] > 63 && packet[cur_idx] != 0xC0 {
- return
- }
-
- switch packet[cur_idx] {
+ switch {
- // This is a offset to more data in the packet, jump to it
- case 0xC0:
+ // A pointer is when the two higher bits are set.
+ case packet[cur_idx] & 0xC0 == 0xC0:
+ if len(packet[cur_idx:]) < 2 {
+ return
+ }
pkt := packet[cur_idx:cur_idx+2]
val := (^u16be)(raw_data(pkt))^
offset := int(val & 0x3FFF)
- if offset > len(packet) {
+ // RFC 9267 a ptr should only point backwards, enough to avoid infinity.
+ // "The offset at which this octet is located must be smaller than the offset
+ // at which the compression pointer is located". Still keep iteration_max to
+ // avoid tiny jumps.
+ if offset > len(packet) || offset >= cur_idx {
return
}
@@ -547,6 +570,10 @@ decode_hostname :: proc(packet: []u8, start_idx: int, allocator := context.alloc
level += 1
}
+ // Validate label len
+ case packet[cur_idx] > LABEL_MAX:
+ return
+
// This is a label, insert it into the hostname
case:
label_size := int(packet[cur_idx])
@@ -785,7 +812,7 @@ parse_record :: proc(packet: []u8, cur_off: ^int, filter: DNS_Record_Type = nil)
- Data[]
*/
-parse_response :: proc(response: []u8, filter: DNS_Record_Type = nil, allocator := context.allocator) -> (records: []DNS_Record, ok: bool) {
+parse_response :: proc(response: []u8, filter: DNS_Record_Type = nil, allocator := context.allocator) -> (records: []DNS_Record, xid: u16be, ok: bool) {
context.allocator = allocator
HEADER_SIZE_BYTES :: 12
@@ -798,11 +825,13 @@ parse_response :: proc(response: []u8, filter: DNS_Record_Type = nil, allocator
dns_hdr_chunks := mem.slice_data_cast([]u16be, response[:HEADER_SIZE_BYTES])
hdr := unpack_dns_header(dns_hdr_chunks[0], dns_hdr_chunks[1])
if !hdr.is_response {
+ delete(_records)
return
}
question_count := int(dns_hdr_chunks[2])
if question_count != 1 {
+ delete(_records)
return
}
answer_count := int(dns_hdr_chunks[3])
@@ -854,6 +883,7 @@ parse_response :: proc(response: []u8, filter: DNS_Record_Type = nil, allocator
append(&_records, rec)
}
}
+ xid = hdr.id
- return _records[:], true
+ return _records[:], xid, true
}
diff --git a/core/net/dns_windows.odin b/core/net/dns_windows.odin
index 2f3831767..7736851b8 100644
--- a/core/net/dns_windows.odin
+++ b/core/net/dns_windows.odin
@@ -29,9 +29,14 @@ import win "core:sys/windows"
_get_dns_records_os :: proc(hostname: string, type: DNS_Record_Type, allocator := context.allocator) -> (records: []DNS_Record, err: DNS_Error) {
context.allocator = allocator
+ options := win.DNS_QUERY_OPTIONS{}
+ if strings.has_suffix(hostname, ".local") {
+ options = {.MULTICAST_ONLY, .MULTICAST_WAIT} // 0x00020500
+ }
+
host_cstr := strings.clone_to_cstring(hostname, context.temp_allocator)
rec: ^win.DNS_RECORD
- res := win.DnsQuery_UTF8(host_cstr, u16(type), 0, nil, &rec, nil)
+ res := win.DnsQuery_UTF8(host_cstr, u16(type), options, nil, &rec, nil)
switch u32(res) {
case 0:
diff --git a/core/net/socket.odin b/core/net/socket.odin
index 950c7ac11..c5ea11e11 100644
--- a/core/net/socket.odin
+++ b/core/net/socket.odin
@@ -34,23 +34,12 @@ any_socket_to_socket :: proc "contextless" (socket: Any_Socket) -> Socket {
Expects both hostname and port to be present in the `hostname_and_port` parameter, either as:
`a.host.name:9999`, or as `1.2.3.4:9999`, or IP6 equivalent.
- Calls `parse_hostname_or_endpoint` and `resolve`, then `dial_tcp_from_endpoint`.
+ Calls `parse_hostname_or_endpoint` and `dial_tcp_from_host_or_endpoint`.
*/
dial_tcp_from_hostname_and_port_string :: proc(hostname_and_port: string, options := default_tcp_options) -> (socket: TCP_Socket, err: Network_Error) {
target := parse_hostname_or_endpoint(hostname_and_port) or_return
- switch t in target {
- case Endpoint:
- return dial_tcp_from_endpoint(t, options)
- case Host:
- if t.port == 0 {
- return 0, .Port_Required
- }
- ep4, ep6 := resolve(t.hostname) or_return
- ep := ep4 if ep4.address != nil else ep6 // NOTE(tetra): We don't know what family the server uses, so we just default to IP4.
- ep.port = t.port
- return dial_tcp_from_endpoint(ep, options)
- }
- unreachable()
+
+ return dial_tcp_from_host_or_endpoint(target, options)
}
/*
@@ -61,17 +50,39 @@ dial_tcp_from_hostname_and_port_string :: proc(hostname_and_port: string, option
*/
dial_tcp_from_hostname_with_port_override :: proc(hostname: string, port: int, options := default_tcp_options) -> (socket: TCP_Socket, err: Network_Error) {
target := parse_hostname_or_endpoint(hostname) or_return
+ switch &t in target {
+ case Endpoint:
+ t.port = port
+ case Host:
+ t.port = port
+ }
+
+ return dial_tcp_from_host_or_endpoint(target, options)
+}
+
+/*
+ Expects the `host` as Host.
+*/
+dial_tcp_from_host :: proc(host: Host, options := default_tcp_options) -> (socket: TCP_Socket, err: Network_Error) {
+ if host.port == 0 {
+ return 0, .Port_Required
+ }
+ ep4, ep6 := resolve(host.hostname) or_return
+ ep := ep4 if ep4.address != nil else ep6 // NOTE(tetra): We don't know what family the server uses, so we just default to IP4.
+ ep.port = host.port
+ return dial_tcp_from_endpoint(ep, options)
+}
+
+/*
+ Expects the `target` as a Host_OrEndpoint.
+ Unwraps the underlying type and calls `dial_tcp_from_host` or `dial_tcp_from_endpoint`.
+*/
+dial_tcp_from_host_or_endpoint :: proc(target: Host_Or_Endpoint, options := default_tcp_options) -> (socket: TCP_Socket, err: Network_Error) {
switch t in target {
case Endpoint:
- return dial_tcp_from_endpoint({t.address, port}, options)
+ return dial_tcp_from_endpoint(t, options)
case Host:
- if port == 0 {
- return 0, .Port_Required
- }
- ep4, ep6 := resolve(t.hostname) or_return
- ep := ep4 if ep4.address != nil else ep6 // NOTE(tetra): We don't know what family the server uses, so we just default to IP4.
- ep.port = port
- return dial_tcp_from_endpoint(ep, options)
+ return dial_tcp_from_host(t, options)
}
unreachable()
}
@@ -90,6 +101,8 @@ dial_tcp :: proc{
dial_tcp_from_address_and_port,
dial_tcp_from_hostname_and_port_string,
dial_tcp_from_hostname_with_port_override,
+ dial_tcp_from_host,
+ dial_tcp_from_host_or_endpoint,
}
create_socket :: proc(family: Address_Family, protocol: Socket_Protocol) -> (socket: Any_Socket, err: Network_Error) {
diff --git a/core/net/socket_linux.odin b/core/net/socket_linux.odin
index b7816b0b6..cafec747d 100644
--- a/core/net/socket_linux.odin
+++ b/core/net/socket_linux.odin
@@ -35,6 +35,7 @@ Socket_Option :: enum c.int {
Send_Buffer_Size = c.int(linux.Socket_Option.SNDBUF),
Receive_Timeout = c.int(linux.Socket_Option.RCVTIMEO),
Send_Timeout = c.int(linux.Socket_Option.SNDTIMEO),
+ Broadcast = c.int(linux.Socket_Option.BROADCAST),
}
// Wrappers and unwrappers for system-native types
@@ -337,7 +338,8 @@ _set_option :: proc(sock: Any_Socket, option: Socket_Option, value: any, loc :=
.Reuse_Address,
.Keep_Alive,
.Out_Of_Bounds_Data_Inline,
- .TCP_Nodelay:
+ .TCP_Nodelay,
+ .Broadcast:
// TODO: verify whether these are options or not on Linux
// .Broadcast, <-- yes
// .Conditional_Accept,
diff --git a/core/net/socket_windows.odin b/core/net/socket_windows.odin
index 747d5cab3..f19be536a 100644
--- a/core/net/socket_windows.odin
+++ b/core/net/socket_windows.odin
@@ -80,8 +80,9 @@ _dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_optio
sockaddr := _endpoint_to_sockaddr(endpoint)
res := win.connect(win.SOCKET(socket), &sockaddr, size_of(sockaddr))
if res < 0 {
+ err = Dial_Error(win.WSAGetLastError())
close(socket)
- return {}, Dial_Error(win.WSAGetLastError())
+ return {}, err
}
if options.no_delay {
diff --git a/core/odin/ast/ast.odin b/core/odin/ast/ast.odin
index f62feec8c..3b8998b31 100644
--- a/core/odin/ast/ast.odin
+++ b/core/odin/ast/ast.odin
@@ -432,10 +432,13 @@ Range_Stmt :: struct {
reverse: bool,
}
-Inline_Range_Stmt :: struct {
+Inline_Range_Stmt :: Unroll_Range_Stmt
+
+Unroll_Range_Stmt :: struct {
using node: Stmt,
label: ^Expr,
- inline_pos: tokenizer.Pos,
+ unroll_pos: tokenizer.Pos,
+ args: []^Expr,
for_pos: tokenizer.Pos,
val0: ^Expr,
val1: ^Expr,
diff --git a/core/odin/ast/clone.odin b/core/odin/ast/clone.odin
index 67f7ffa95..b7501e6ca 100644
--- a/core/odin/ast/clone.odin
+++ b/core/odin/ast/clone.odin
@@ -242,8 +242,9 @@ clone_node :: proc(node: ^Node) -> ^Node {
r.vals = clone(r.vals)
r.expr = clone(r.expr)
r.body = clone(r.body)
- case ^Inline_Range_Stmt:
+ case ^Unroll_Range_Stmt:
r.label = clone(r.label)
+ r.args = clone(r.args)
r.val0 = clone(r.val0)
r.val1 = clone(r.val1)
r.expr = clone(r.expr)
diff --git a/core/odin/parser/file_tags.odin b/core/odin/parser/file_tags.odin
index 84b172148..c5c6637c3 100644
--- a/core/odin/parser/file_tags.odin
+++ b/core/odin/parser/file_tags.odin
@@ -17,6 +17,9 @@ Build_Kind :: struct {
arch: runtime.Odin_Arch_Types,
}
+// empty build kind acts as a marker for separating multiple lines with build tags
+BUILD_KIND_NEWLINE_MARKER :: Build_Kind{}
+
File_Tags :: struct {
build_project_name: [][]string,
build: []Build_Kind,
@@ -147,6 +150,11 @@ parse_file_tags :: proc(file: ast.File, allocator := context.allocator) -> (tags
append(build_project_names, build_project_name_strings[index_start:])
}
case "build":
+
+ if len(build_kinds) > 0 {
+ append(build_kinds, BUILD_KIND_NEWLINE_MARKER)
+ }
+
kinds_loop: for {
os_positive: runtime.Odin_OS_Types
os_negative: runtime.Odin_OS_Types
@@ -248,10 +256,20 @@ match_build_tags :: proc(file_tags: File_Tags, target: Build_Target) -> bool {
project_name_correct ||= group_correct
}
- os_and_arch_correct := len(file_tags.build) == 0
+ os_and_arch_correct := true
- for kind in file_tags.build {
- os_and_arch_correct ||= target.os in kind.os && target.arch in kind.arch
+ if len(file_tags.build) > 0 {
+ os_and_arch_correct_line := false
+
+ for kind in file_tags.build {
+ if kind == BUILD_KIND_NEWLINE_MARKER {
+ os_and_arch_correct &&= os_and_arch_correct_line
+ os_and_arch_correct_line = false
+ } else {
+ os_and_arch_correct_line ||= target.os in kind.os && target.arch in kind.arch
+ }
+ }
+ os_and_arch_correct &&= os_and_arch_correct_line
}
return !file_tags.ignore && project_name_correct && os_and_arch_correct
diff --git a/core/odin/parser/parser.odin b/core/odin/parser/parser.odin
index 5a7440339..63c7e388f 100644
--- a/core/odin/parser/parser.odin
+++ b/core/odin/parser/parser.odin
@@ -1262,11 +1262,49 @@ parse_foreign_decl :: proc(p: ^Parser) -> ^ast.Decl {
parse_unrolled_for_loop :: proc(p: ^Parser, inline_tok: tokenizer.Token) -> ^ast.Stmt {
- for_tok := expect_token(p, .For)
val0, val1: ^ast.Expr
in_tok: tokenizer.Token
expr: ^ast.Expr
body: ^ast.Stmt
+ args: [dynamic]^ast.Expr
+
+ if allow_token(p, .Open_Paren) {
+ p.expr_level += 1
+ if p.curr_tok.kind == .Close_Paren {
+ error(p, p.curr_tok.pos, "#unroll expected at least 1 argument, got 0")
+ } else {
+ args = make([dynamic]^ast.Expr)
+ for p.curr_tok.kind != .Close_Paren &&
+ p.curr_tok.kind != .EOF {
+ arg := parse_value(p)
+
+ if p.curr_tok.kind == .Eq {
+ eq := expect_token(p, .Eq)
+ if arg != nil {
+ if _, ok := arg.derived.(^ast.Ident); !ok {
+ error(p, arg.pos, "expected an identifier for 'key=value'")
+ }
+ }
+ value := parse_value(p)
+ fv := ast.new(ast.Field_Value, arg.pos, value)
+ fv.field = arg
+ fv.sep = eq.pos
+ fv.value = value
+
+ arg = fv
+ }
+
+ append(&args, arg)
+
+ allow_token(p, .Comma) or_break
+ }
+ }
+
+ p.expr_level -= 1
+ _ = expect_token_after(p, .Close_Paren, "#unroll")
+ }
+
+ for_tok := expect_token(p, .For)
bad_stmt := false
@@ -1309,7 +1347,8 @@ parse_unrolled_for_loop :: proc(p: ^Parser, inline_tok: tokenizer.Token) -> ^ast
}
range_stmt := ast.new(ast.Inline_Range_Stmt, inline_tok.pos, body)
- range_stmt.inline_pos = inline_tok.pos
+ range_stmt.unroll_pos = inline_tok.pos
+ range_stmt.args = args[:]
range_stmt.for_pos = for_tok.pos
range_stmt.val0 = val0
range_stmt.val1 = val1
diff --git a/core/os/dir_unix.odin b/core/os/dir_unix.odin
index 26e865204..f06bf8b37 100644
--- a/core/os/dir_unix.odin
+++ b/core/os/dir_unix.odin
@@ -1,4 +1,4 @@
-#+build darwin, linux, netbsd, freebsd, openbsd
+#+build darwin, linux, netbsd, freebsd, openbsd, haiku
package os
import "core:strings"
diff --git a/core/os/os2/dir.odin b/core/os/os2/dir.odin
index a41ef68f9..4a7762ded 100644
--- a/core/os/os2/dir.odin
+++ b/core/os/os2/dir.odin
@@ -20,7 +20,7 @@ read_directory :: proc(f: ^File, n: int, allocator: runtime.Allocator) -> (files
TEMP_ALLOCATOR_GUARD()
- it := read_directory_iterator_create(f) or_return
+ it := read_directory_iterator_create(f)
defer _read_directory_iterator_destroy(&it)
dfi := make([dynamic]File_Info, 0, size, temp_allocator())
@@ -34,9 +34,14 @@ read_directory :: proc(f: ^File, n: int, allocator: runtime.Allocator) -> (files
if n > 0 && index == n {
break
}
+
+ _ = read_directory_iterator_error(&it) or_break
+
append(&dfi, file_info_clone(fi, allocator) or_return)
}
+ _ = read_directory_iterator_error(&it) or_return
+
return slice.clone(dfi[:], allocator)
}
@@ -61,22 +66,129 @@ read_all_directory_by_path :: proc(path: string, allocator: runtime.Allocator) -
Read_Directory_Iterator :: struct {
- f: ^File,
+ f: ^File,
+ err: struct {
+ err: Error,
+ path: [dynamic]byte,
+ },
+ index: int,
impl: Read_Directory_Iterator_Impl,
}
+/*
+Creates a directory iterator with the given directory.
-@(require_results)
-read_directory_iterator_create :: proc(f: ^File) -> (Read_Directory_Iterator, Error) {
- return _read_directory_iterator_create(f)
+For an example on how to use the iterator, see `read_directory_iterator`.
+*/
+read_directory_iterator_create :: proc(f: ^File) -> (it: Read_Directory_Iterator) {
+ read_directory_iterator_init(&it, f)
+ return
+}
+
+/*
+Initialize a directory iterator with the given directory.
+
+This procedure may be called on an existing iterator to reuse it for another directory.
+
+For an example on how to use the iterator, see `read_directory_iterator`.
+*/
+read_directory_iterator_init :: proc(it: ^Read_Directory_Iterator, f: ^File) {
+ it.err.err = nil
+ it.err.path.allocator = file_allocator()
+ clear(&it.err.path)
+
+ it.f = f
+ it.index = 0
+
+ _read_directory_iterator_init(it, f)
}
+/*
+Destroys a directory iterator.
+*/
read_directory_iterator_destroy :: proc(it: ^Read_Directory_Iterator) {
+ if it == nil {
+ return
+ }
+
+ delete(it.err.path)
+
_read_directory_iterator_destroy(it)
}
-// NOTE(bill): `File_Info` does not need to deleted on each iteration. Any copies must be manually copied with `file_info_clone`
+/*
+Retrieve the last error that happened during iteration.
+*/
+@(require_results)
+read_directory_iterator_error :: proc(it: ^Read_Directory_Iterator) -> (path: string, err: Error) {
+ return string(it.err.path[:]), it.err.err
+}
+
+@(private)
+read_directory_iterator_set_error :: proc(it: ^Read_Directory_Iterator, path: string, err: Error) {
+ if err == nil {
+ return
+ }
+
+ resize(&it.err.path, len(path))
+ copy(it.err.path[:], path)
+
+ it.err.err = err
+}
+
+/*
+Returns the next file info entry for the iterator's directory.
+
+The given `File_Info` is reused in subsequent calls so a copy (`file_info_clone`) has to be made to
+extend its lifetime.
+
+Example:
+ package main
+
+ import "core:fmt"
+ import os "core:os/os2"
+
+ main :: proc() {
+ f, oerr := os.open("core")
+ ensure(oerr == nil)
+ defer os.close(f)
+
+ it := os.read_directory_iterator_create(f)
+ defer os.read_directory_iterator_destroy(&it)
+
+ for info in os.read_directory_iterator(&it) {
+ // Optionally break on the first error:
+ // Supports not doing this, and keeping it going with remaining items.
+ // _ = os.read_directory_iterator_error(&it) or_break
+
+ // Handle error as we go:
+ // Again, no need to do this as it will keep going with remaining items.
+ if path, err := os.read_directory_iterator_error(&it); err != nil {
+ fmt.eprintfln("failed reading %s: %s", path, err)
+ continue
+ }
+
+ // Or, do not handle errors during iteration, and just check the error at the end.
+
+
+ fmt.printfln("%#v", info)
+ }
+
+ // Handle error if one happened during iteration at the end:
+ if path, err := os.read_directory_iterator_error(&it); err != nil {
+ fmt.eprintfln("read directory failed at %s: %s", path, err)
+ }
+ }
+*/
@(require_results)
read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info, index: int, ok: bool) {
+ if it.f == nil {
+ return
+ }
+
+ if it.index == 0 && it.err.err != nil {
+ return
+ }
+
return _read_directory_iterator(it)
}
diff --git a/core/os/os2/dir_linux.odin b/core/os/os2/dir_linux.odin
index f26b4fc79..a868a02c4 100644
--- a/core/os/os2/dir_linux.odin
+++ b/core/os/os2/dir_linux.odin
@@ -1,20 +1,119 @@
#+private
package os2
-Read_Directory_Iterator_Impl :: struct {
+import "core:sys/linux"
+Read_Directory_Iterator_Impl :: struct {
+ prev_fi: File_Info,
+ dirent_backing: []u8,
+ dirent_buflen: int,
+ dirent_off: int,
}
-
@(require_results)
_read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info, index: int, ok: bool) {
+ scan_entries :: proc(it: ^Read_Directory_Iterator, dfd: linux.Fd, entries: []u8, offset: ^int) -> (fd: linux.Fd, file_name: string) {
+ for d in linux.dirent_iterate_buf(entries, offset) {
+ file_name = linux.dirent_name(d)
+ if file_name == "." || file_name == ".." {
+ continue
+ }
+
+ file_name_cstr := cstring(raw_data(file_name))
+ entry_fd, errno := linux.openat(dfd, file_name_cstr, {.NOFOLLOW, .PATH})
+ if errno == .NONE {
+ return entry_fd, file_name
+ } else {
+ read_directory_iterator_set_error(it, file_name, _get_platform_error(errno))
+ }
+ }
+
+ return -1, ""
+ }
+
+ index = it.index
+ it.index += 1
+
+ dfd := linux.Fd(_fd(it.f))
+
+ entries := it.impl.dirent_backing[:it.impl.dirent_buflen]
+ entry_fd, file_name := scan_entries(it, dfd, entries, &it.impl.dirent_off)
+
+ for entry_fd == -1 {
+ if len(it.impl.dirent_backing) == 0 {
+ it.impl.dirent_backing = make([]u8, 512, file_allocator())
+ }
+
+ loop: for {
+ buflen, errno := linux.getdents(linux.Fd(dfd), it.impl.dirent_backing[:])
+ #partial switch errno {
+ case .EINVAL:
+ delete(it.impl.dirent_backing, file_allocator())
+ n := len(it.impl.dirent_backing) * 2
+ it.impl.dirent_backing = make([]u8, n, file_allocator())
+ continue
+ case .NONE:
+ if buflen == 0 {
+ return
+ }
+ it.impl.dirent_off = 0
+ it.impl.dirent_buflen = buflen
+ entries = it.impl.dirent_backing[:buflen]
+ break loop
+ case:
+ read_directory_iterator_set_error(it, name(it.f), _get_platform_error(errno))
+ return
+ }
+ }
+
+ entry_fd, file_name = scan_entries(it, dfd, entries, &it.impl.dirent_off)
+ }
+ defer linux.close(entry_fd)
+
+ // PERF: reuse the fullpath string like on posix and wasi.
+ file_info_delete(it.impl.prev_fi, file_allocator())
+
+ err: Error
+ fi, err = _fstat_internal(entry_fd, file_allocator())
+ it.impl.prev_fi = fi
+
+ if err != nil {
+ path, _ := _get_full_path(entry_fd, temp_allocator())
+ read_directory_iterator_set_error(it, path, err)
+ }
+
+ ok = true
return
}
-@(require_results)
-_read_directory_iterator_create :: proc(f: ^File) -> (Read_Directory_Iterator, Error) {
- return {}, .Unsupported
+_read_directory_iterator_init :: proc(it: ^Read_Directory_Iterator, f: ^File) {
+ // NOTE: Allow calling `init` to target a new directory with the same iterator.
+ it.impl.dirent_buflen = 0
+ it.impl.dirent_off = 0
+
+ if f == nil || f.impl == nil {
+ read_directory_iterator_set_error(it, "", .Invalid_File)
+ return
+ }
+
+ stat: linux.Stat
+ errno := linux.fstat(linux.Fd(fd(f)), &stat)
+ if errno != .NONE {
+ read_directory_iterator_set_error(it, name(f), _get_platform_error(errno))
+ return
+ }
+
+ if (stat.mode & linux.S_IFMT) != linux.S_IFDIR {
+ read_directory_iterator_set_error(it, name(f), .Invalid_Dir)
+ return
+ }
}
_read_directory_iterator_destroy :: proc(it: ^Read_Directory_Iterator) {
+ if it == nil {
+ return
+ }
+
+ delete(it.impl.dirent_backing, file_allocator())
+ file_info_delete(it.impl.prev_fi, file_allocator())
}
diff --git a/core/os/os2/dir_posix.odin b/core/os/os2/dir_posix.odin
index 14fddde50..d9fa16f8d 100644
--- a/core/os/os2/dir_posix.odin
+++ b/core/os/os2/dir_posix.odin
@@ -6,7 +6,6 @@ import "core:sys/posix"
Read_Directory_Iterator_Impl :: struct {
dir: posix.DIR,
- idx: int,
fullpath: [dynamic]byte,
}
@@ -14,14 +13,16 @@ Read_Directory_Iterator_Impl :: struct {
_read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info, index: int, ok: bool) {
fimpl := (^File_Impl)(it.f.impl)
- index = it.impl.idx
- it.impl.idx += 1
+ index = it.index
+ it.index += 1
for {
+ posix.set_errno(nil)
entry := posix.readdir(it.impl.dir)
if entry == nil {
- // NOTE(laytan): would be good to have an `error` field on the `Read_Directory_Iterator`
- // There isn't a way to now know if it failed or if we are at the end.
+ if errno := posix.errno(); errno != nil {
+ read_directory_iterator_set_error(it, name(it.f), _get_platform_error(errno))
+ }
return
}
@@ -31,51 +32,62 @@ _read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info
}
sname := string(cname)
+ n := len(fimpl.name)+1
+ if err := non_zero_resize(&it.impl.fullpath, n+len(sname)); err != nil {
+ read_directory_iterator_set_error(it, sname, err)
+ ok = true
+ return
+ }
+ copy(it.impl.fullpath[n:], sname)
+
stat: posix.stat_t
if posix.fstatat(posix.dirfd(it.impl.dir), cname, &stat, { .SYMLINK_NOFOLLOW }) != .OK {
- // NOTE(laytan): would be good to have an `error` field on the `Read_Directory_Iterator`
- // There isn't a way to now know if it failed or if we are at the end.
+ read_directory_iterator_set_error(it, string(it.impl.fullpath[:]), _get_platform_error())
+ ok = true
return
}
- n := len(fimpl.name)+1
- non_zero_resize(&it.impl.fullpath, n+len(sname))
- n += copy(it.impl.fullpath[n:], sname)
-
fi = internal_stat(stat, string(it.impl.fullpath[:]))
ok = true
return
}
}
-@(require_results)
-_read_directory_iterator_create :: proc(f: ^File) -> (iter: Read_Directory_Iterator, err: Error) {
+_read_directory_iterator_init :: proc(it: ^Read_Directory_Iterator, f: ^File) {
if f == nil || f.impl == nil {
- err = .Invalid_File
+ read_directory_iterator_set_error(it, "", .Invalid_File)
return
}
impl := (^File_Impl)(f.impl)
- iter.f = f
- iter.impl.idx = 0
+ // NOTE: Allow calling `init` to target a new directory with the same iterator.
+ it.impl.fullpath.allocator = file_allocator()
+ clear(&it.impl.fullpath)
+ if err := reserve(&it.impl.fullpath, len(impl.name)+128); err != nil {
+ read_directory_iterator_set_error(it, name(f), err)
+ return
+ }
- iter.impl.fullpath.allocator = file_allocator()
- append(&iter.impl.fullpath, impl.name)
- append(&iter.impl.fullpath, "/")
- defer if err != nil { delete(iter.impl.fullpath) }
+ append(&it.impl.fullpath, impl.name)
+ append(&it.impl.fullpath, "/")
// `fdopendir` consumes the file descriptor so we need to `dup` it.
dupfd := posix.dup(impl.fd)
if dupfd == -1 {
- err = _get_platform_error()
+ read_directory_iterator_set_error(it, name(f), _get_platform_error())
return
}
- defer if err != nil { posix.close(dupfd) }
+ defer if it.err.err != nil { posix.close(dupfd) }
+
+ // NOTE: Allow calling `init` to target a new directory with the same iterator.
+ if it.impl.dir != nil {
+ posix.closedir(it.impl.dir)
+ }
- iter.impl.dir = posix.fdopendir(dupfd)
- if iter.impl.dir == nil {
- err = _get_platform_error()
+ it.impl.dir = posix.fdopendir(dupfd)
+ if it.impl.dir == nil {
+ read_directory_iterator_set_error(it, name(f), _get_platform_error())
return
}
@@ -83,7 +95,7 @@ _read_directory_iterator_create :: proc(f: ^File) -> (iter: Read_Directory_Itera
}
_read_directory_iterator_destroy :: proc(it: ^Read_Directory_Iterator) {
- if it == nil || it.impl.dir == nil {
+ if it.impl.dir == nil {
return
}
diff --git a/core/os/os2/dir_walker.odin b/core/os/os2/dir_walker.odin
new file mode 100644
index 000000000..0af751f31
--- /dev/null
+++ b/core/os/os2/dir_walker.odin
@@ -0,0 +1,230 @@
+package os2
+
+import "core:container/queue"
+
+/*
+A recursive directory walker.
+
+Note that none of the fields should be accessed directly.
+*/
+Walker :: struct {
+ todo: queue.Queue(string),
+ skip_dir: bool,
+ err: struct {
+ path: [dynamic]byte,
+ err: Error,
+ },
+ iter: Read_Directory_Iterator,
+}
+
+walker_init_path :: proc(w: ^Walker, path: string) {
+ cloned_path, err := clone_string(path, file_allocator())
+ if err != nil {
+ walker_set_error(w, path, err)
+ return
+ }
+
+ walker_clear(w)
+
+ if _, err = queue.push(&w.todo, cloned_path); err != nil {
+ walker_set_error(w, cloned_path, err)
+ return
+ }
+}
+
+walker_init_file :: proc(w: ^Walker, f: ^File) {
+ handle, err := clone(f)
+ if err != nil {
+ path, _ := clone_string(name(f), file_allocator())
+ walker_set_error(w, path, err)
+ return
+ }
+
+ walker_clear(w)
+
+ read_directory_iterator_init(&w.iter, handle)
+}
+
+/*
+Initializes a walker, either using a path or a file pointer to a directory the walker will start at.
+
+You are allowed to repeatedly call this to reuse it for later walks.
+
+For an example on how to use the walker, see `walker_walk`.
+*/
+walker_init :: proc {
+ walker_init_path,
+ walker_init_file,
+}
+
+@(require_results)
+walker_create_path :: proc(path: string) -> (w: Walker) {
+ walker_init_path(&w, path)
+ return
+}
+
+@(require_results)
+walker_create_file :: proc(f: ^File) -> (w: Walker) {
+ walker_init_file(&w, f)
+ return
+}
+
+/*
+Creates a walker, either using a path or a file pointer to a directory the walker will start at.
+
+For an example on how to use the walker, see `walker_walk`.
+*/
+walker_create :: proc {
+ walker_create_path,
+ walker_create_file,
+}
+
+/*
+Returns the last error that occurred during the walker's operations.
+
+Can be called while iterating, or only at the end to check if anything failed.
+*/
+@(require_results)
+walker_error :: proc(w: ^Walker) -> (path: string, err: Error) {
+ return string(w.err.path[:]), w.err.err
+}
+
+@(private)
+walker_set_error :: proc(w: ^Walker, path: string, err: Error) {
+ if err == nil {
+ return
+ }
+
+ resize(&w.err.path, len(path))
+ copy(w.err.path[:], path)
+
+ w.err.err = err
+}
+
+@(private)
+walker_clear :: proc(w: ^Walker) {
+ w.iter.f = nil
+ w.skip_dir = false
+
+ w.err.path.allocator = file_allocator()
+ clear(&w.err.path)
+
+ w.todo.data.allocator = file_allocator()
+ for path in queue.pop_front_safe(&w.todo) {
+ delete(path, file_allocator())
+ }
+}
+
+walker_destroy :: proc(w: ^Walker) {
+ walker_clear(w)
+ queue.destroy(&w.todo)
+ delete(w.err.path)
+ read_directory_iterator_destroy(&w.iter)
+}
+
+// Marks the current directory to be skipped (not entered into).
+walker_skip_dir :: proc(w: ^Walker) {
+ w.skip_dir = true
+}
+
+/*
+Returns the next file info in the iterator, files are iterated in breadth-first order.
+
+If an error occurred opening a directory, you may get zero'd info struct and
+`walker_error` will return the error.
+
+Example:
+ package main
+
+ import "core:fmt"
+ import "core:strings"
+ import os "core:os/os2"
+
+ main :: proc() {
+ w := os.walker_create("core")
+ defer os.walker_destroy(&w)
+
+ for info in os.walker_walk(&w) {
+ // Optionally break on the first error:
+ // _ = walker_error(&w) or_break
+
+ // Or, handle error as we go:
+ if path, err := os.walker_error(&w); err != nil {
+ fmt.eprintfln("failed walking %s: %s", path, err)
+ continue
+ }
+
+ // Or, do not handle errors during iteration, and just check the error at the end.
+
+
+
+ // Skip a directory:
+ if strings.has_suffix(info.fullpath, ".git") {
+ os.walker_skip_dir(&w)
+ continue
+ }
+
+ fmt.printfln("%#v", info)
+ }
+
+ // Handle error if one happened during iteration at the end:
+ if path, err := os.walker_error(&w); err != nil {
+ fmt.eprintfln("failed walking %s: %v", path, err)
+ }
+ }
+*/
+@(require_results)
+walker_walk :: proc(w: ^Walker) -> (fi: File_Info, ok: bool) {
+ if w.skip_dir {
+ w.skip_dir = false
+ if skip, sok := queue.pop_back_safe(&w.todo); sok {
+ delete(skip, file_allocator())
+ }
+ }
+
+ if w.iter.f == nil {
+ if queue.len(w.todo) == 0 {
+ return
+ }
+
+ next := queue.pop_front(&w.todo)
+
+ handle, err := open(next)
+ if err != nil {
+ walker_set_error(w, next, err)
+ return {}, true
+ }
+
+ read_directory_iterator_init(&w.iter, handle)
+
+ delete(next, file_allocator())
+ }
+
+ info, _, iter_ok := read_directory_iterator(&w.iter)
+
+ if path, err := read_directory_iterator_error(&w.iter); err != nil {
+ walker_set_error(w, path, err)
+ }
+
+ if !iter_ok {
+ close(w.iter.f)
+ w.iter.f = nil
+ return walker_walk(w)
+ }
+
+ if info.type == .Directory {
+ path, err := clone_string(info.fullpath, file_allocator())
+ if err != nil {
+ walker_set_error(w, "", err)
+ return
+ }
+
+ _, err = queue.push_back(&w.todo, path)
+ if err != nil {
+ walker_set_error(w, path, err)
+ return
+ }
+ }
+
+ return info, iter_ok
+}
diff --git a/core/os/os2/dir_wasi.odin b/core/os/os2/dir_wasi.odin
new file mode 100644
index 000000000..61c005674
--- /dev/null
+++ b/core/os/os2/dir_wasi.odin
@@ -0,0 +1,124 @@
+#+private
+package os2
+
+import "base:runtime"
+import "core:slice"
+import "base:intrinsics"
+import "core:sys/wasm/wasi"
+
+Read_Directory_Iterator_Impl :: struct {
+ fullpath: [dynamic]byte,
+ buf: []byte,
+ off: int,
+}
+
+@(require_results)
+_read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info, index: int, ok: bool) {
+ fimpl := (^File_Impl)(it.f.impl)
+
+ buf := it.impl.buf[it.impl.off:]
+
+ index = it.index
+ it.index += 1
+
+ for {
+ if len(buf) < size_of(wasi.dirent_t) {
+ return
+ }
+
+ entry := intrinsics.unaligned_load((^wasi.dirent_t)(raw_data(buf)))
+ buf = buf[size_of(wasi.dirent_t):]
+
+ assert(len(buf) < int(entry.d_namlen))
+
+ name := string(buf[:entry.d_namlen])
+ buf = buf[entry.d_namlen:]
+ it.impl.off += size_of(wasi.dirent_t) + int(entry.d_namlen)
+
+ if name == "." || name == ".." {
+ continue
+ }
+
+ n := len(fimpl.name)+1
+ if alloc_err := non_zero_resize(&it.impl.fullpath, n+len(name)); alloc_err != nil {
+ read_directory_iterator_set_error(it, name, alloc_err)
+ ok = true
+ return
+ }
+ copy(it.impl.fullpath[n:], name)
+
+ stat, err := wasi.path_filestat_get(__fd(it.f), {}, name)
+ if err != nil {
+ // Can't stat, fill what we have from dirent.
+ stat = {
+ ino = entry.d_ino,
+ filetype = entry.d_type,
+ }
+ read_directory_iterator_set_error(it, string(it.impl.fullpath[:]), _get_platform_error(err))
+ }
+
+ fi = internal_stat(stat, string(it.impl.fullpath[:]))
+ ok = true
+ return
+ }
+}
+
+_read_directory_iterator_init :: proc(it: ^Read_Directory_Iterator, f: ^File) {
+ // NOTE: Allow calling `init` to target a new directory with the same iterator.
+ it.impl.off = 0
+
+ if f == nil || f.impl == nil {
+ read_directory_iterator_set_error(it, "", .Invalid_File)
+ return
+ }
+
+ impl := (^File_Impl)(f.impl)
+
+ buf: [dynamic]byte
+ // NOTE: Allow calling `init` to target a new directory with the same iterator.
+ if it.impl.buf != nil {
+ buf = slice.into_dynamic(it.impl.buf)
+ }
+ buf.allocator = file_allocator()
+
+ defer if it.err.err != nil { delete(buf) }
+
+ for {
+ if err := non_zero_resize(&buf, 512 if len(buf) == 0 else len(buf)*2); err != nil {
+ read_directory_iterator_set_error(it, name(f), err)
+ return
+ }
+
+ n, err := wasi.fd_readdir(__fd(f), buf[:], 0)
+ if err != nil {
+ read_directory_iterator_set_error(it, name(f), _get_platform_error(err))
+ return
+ }
+
+ if n < len(buf) {
+ non_zero_resize(&buf, n)
+ break
+ }
+
+ assert(n == len(buf))
+ }
+ it.impl.buf = buf[:]
+
+ // NOTE: Allow calling `init` to target a new directory with the same iterator.
+ it.impl.fullpath.allocator = file_allocator()
+ clear(&it.impl.fullpath)
+ if err := reserve(&it.impl.fullpath, len(impl.name)+128); err != nil {
+ read_directory_iterator_set_error(it, name(f), err)
+ return
+ }
+
+ append(&it.impl.fullpath, impl.name)
+ append(&it.impl.fullpath, "/")
+
+ return
+}
+
+_read_directory_iterator_destroy :: proc(it: ^Read_Directory_Iterator) {
+ delete(it.impl.buf, file_allocator())
+ delete(it.impl.fullpath)
+}
diff --git a/core/os/os2/dir_windows.odin b/core/os/os2/dir_windows.odin
index f71e7e763..d592e8036 100644
--- a/core/os/os2/dir_windows.odin
+++ b/core/os/os2/dir_windows.odin
@@ -44,16 +44,11 @@ Read_Directory_Iterator_Impl :: struct {
path: string,
prev_fi: File_Info,
no_more_files: bool,
- index: int,
}
@(require_results)
_read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info, index: int, ok: bool) {
- if it.f == nil {
- return
- }
-
TEMP_ALLOCATOR_GUARD()
for !it.impl.no_more_files {
@@ -63,19 +58,21 @@ _read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info
fi, err = find_data_to_file_info(it.impl.path, &it.impl.find_data, file_allocator())
if err != nil {
+ read_directory_iterator_set_error(it, it.impl.path, err)
return
}
+
if fi.name != "" {
it.impl.prev_fi = fi
ok = true
- index = it.impl.index
- it.impl.index += 1
+ index = it.index
+ it.index += 1
}
if !win32.FindNextFileW(it.impl.find_handle, &it.impl.find_data) {
e := _get_platform_error()
- if pe, _ := is_platform_error(e); pe == i32(win32.ERROR_NO_MORE_FILES) {
- it.impl.no_more_files = true
+ if pe, _ := is_platform_error(e); pe != i32(win32.ERROR_NO_MORE_FILES) {
+ read_directory_iterator_set_error(it, it.impl.path, e)
}
it.impl.no_more_files = true
}
@@ -86,16 +83,27 @@ _read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info
return
}
-@(require_results)
-_read_directory_iterator_create :: proc(f: ^File) -> (it: Read_Directory_Iterator, err: Error) {
- if f == nil {
+_read_directory_iterator_init :: proc(it: ^Read_Directory_Iterator, f: ^File) {
+ it.impl.no_more_files = false
+
+ if f == nil || f.impl == nil {
+ read_directory_iterator_set_error(it, "", .Invalid_File)
return
}
+
it.f = f
impl := (^File_Impl)(f.impl)
+ // NOTE: Allow calling `init` to target a new directory with the same iterator - reset idx.
+ if it.impl.find_handle != nil {
+ win32.FindClose(it.impl.find_handle)
+ }
+ if it.impl.path != "" {
+ delete(it.impl.path, file_allocator())
+ }
+
if !is_directory(impl.name) {
- err = .Invalid_Dir
+ read_directory_iterator_set_error(it, impl.name, .Invalid_Dir)
return
}
@@ -118,14 +126,19 @@ _read_directory_iterator_create :: proc(f: ^File) -> (it: Read_Directory_Iterato
it.impl.find_handle = win32.FindFirstFileW(raw_data(wpath_search), &it.impl.find_data)
if it.impl.find_handle == win32.INVALID_HANDLE_VALUE {
- err = _get_platform_error()
+ read_directory_iterator_set_error(it, impl.name, _get_platform_error())
return
}
- defer if err != nil {
+ defer if it.err.err != nil {
win32.FindClose(it.impl.find_handle)
}
- it.impl.path = _cleanpath_from_buf(wpath, file_allocator()) or_return
+ err: Error
+ it.impl.path, err = _cleanpath_from_buf(wpath, file_allocator())
+ if err != nil {
+ read_directory_iterator_set_error(it, impl.name, err)
+ }
+
return
}
diff --git a/core/os/os2/env.odin b/core/os/os2/env.odin
index c8d39b270..13c107f3c 100644
--- a/core/os/os2/env.odin
+++ b/core/os/os2/env.odin
@@ -22,8 +22,8 @@ lookup_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string,
}
// set_env sets the value of the environment variable named by the key
-// Returns true on success, false on failure
-set_env :: proc(key, value: string) -> bool {
+// Returns Error on failure
+set_env :: proc(key, value: string) -> Error {
return _set_env(key, value)
}
@@ -41,7 +41,7 @@ clear_env :: proc() {
// environ returns a copy of strings representing the environment, in the form "key=value"
// NOTE: the slice of strings and the strings with be allocated using the supplied allocator
@(require_results)
-environ :: proc(allocator: runtime.Allocator) -> []string {
+environ :: proc(allocator: runtime.Allocator) -> ([]string, Error) {
return _environ(allocator)
}
diff --git a/core/os/os2/env_linux.odin b/core/os/os2/env_linux.odin
index c248a323c..f24bd8d84 100644
--- a/core/os/os2/env_linux.odin
+++ b/core/os/os2/env_linux.odin
@@ -54,7 +54,7 @@ _lookup_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string
return
}
-_set_env :: proc(key, v_new: string) -> bool {
+_set_env :: proc(key, v_new: string) -> Error {
if _org_env_begin == 0 {
_build_env()
}
@@ -63,7 +63,7 @@ _set_env :: proc(key, v_new: string) -> bool {
kv_size := len(key) + len(v_new) + 2
if v_curr, idx := _lookup(key); idx != NOT_FOUND {
if v_curr == v_new {
- return true
+ return nil
}
sync.mutex_lock(&_env_mutex)
defer sync.mutex_unlock(&_env_mutex)
@@ -76,9 +76,9 @@ _set_env :: proc(key, v_new: string) -> bool {
// wasn't in the environment in the first place.
k_addr, v_addr := _kv_addr_from_val(v_curr, key)
if len(v_new) > len(v_curr) {
- k_addr = ([^]u8)(heap_resize(k_addr, kv_size))
+ k_addr = ([^]u8)(runtime.heap_resize(k_addr, kv_size))
if k_addr == nil {
- return false
+ return .Out_Of_Memory
}
v_addr = &k_addr[len(key) + 1]
}
@@ -86,13 +86,13 @@ _set_env :: proc(key, v_new: string) -> bool {
v_addr[len(v_new)] = 0
append(&_env, string(k_addr[:kv_size]))
- return true
+ return nil
}
}
- k_addr := ([^]u8)(heap_alloc(kv_size))
+ k_addr := ([^]u8)(runtime.heap_alloc(kv_size))
if k_addr == nil {
- return false
+ return .Out_Of_Memory
}
intrinsics.mem_copy_non_overlapping(k_addr, raw_data(key), len(key))
k_addr[len(key)] = '='
@@ -104,7 +104,7 @@ _set_env :: proc(key, v_new: string) -> bool {
sync.mutex_lock(&_env_mutex)
append(&_env, string(k_addr[:kv_size - 1]))
sync.mutex_unlock(&_env_mutex)
- return true
+ return nil
}
_unset_env :: proc(key: string) -> bool {
@@ -129,7 +129,7 @@ _unset_env :: proc(key: string) -> bool {
// if we got this far, the envrionment variable
// existed AND was allocated by us.
k_addr, _ := _kv_addr_from_val(v, key)
- heap_free(k_addr)
+ runtime.heap_free(k_addr)
return true
}
@@ -139,7 +139,7 @@ _clear_env :: proc() {
for kv in _env {
if !_is_in_org_env(kv) {
- heap_free(raw_data(kv))
+ runtime.heap_free(raw_data(kv))
}
}
clear(&_env)
@@ -149,18 +149,26 @@ _clear_env :: proc() {
_org_env_end = ~uintptr(0)
}
-_environ :: proc(allocator: runtime.Allocator) -> []string {
+_environ :: proc(allocator: runtime.Allocator) -> (environ: []string, err: Error) {
if _org_env_begin == 0 {
_build_env()
}
- env := make([]string, len(_env), allocator)
+ env := make([dynamic]string, 0, len(_env), allocator) or_return
+ defer if err != nil {
+ for e in env {
+ delete(e, allocator)
+ }
+ delete(env)
+ }
sync.mutex_lock(&_env_mutex)
defer sync.mutex_unlock(&_env_mutex)
- for entry, i in _env {
- env[i], _ = clone_string(entry, allocator)
+ for entry in _env {
+ s := clone_string(entry, allocator) or_return
+ append(&env, s)
}
- return env
+ environ = env[:]
+ return
}
// The entire environment is stored as 0 terminated strings,
@@ -193,7 +201,7 @@ _build_env :: proc() {
return
}
- _env = make(type_of(_env), heap_allocator())
+ _env = make(type_of(_env), runtime.heap_allocator())
cstring_env := _get_original_env()
_org_env_begin = uintptr(rawptr(cstring_env[0]))
for i := 0; cstring_env[i] != nil; i += 1 {
diff --git a/core/os/os2/env_posix.odin b/core/os/os2/env_posix.odin
index e2080485d..3db8d817a 100644
--- a/core/os/os2/env_posix.odin
+++ b/core/os/os2/env_posix.odin
@@ -26,13 +26,15 @@ _lookup_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string
return
}
-_set_env :: proc(key, value: string) -> (ok: bool) {
+_set_env :: proc(key, value: string) -> (err: Error) {
TEMP_ALLOCATOR_GUARD()
- ckey := strings.clone_to_cstring(key, temp_allocator())
- cval := strings.clone_to_cstring(key, temp_allocator())
+ ckey := strings.clone_to_cstring(key, temp_allocator()) or_return
+ cval := strings.clone_to_cstring(key, temp_allocator()) or_return
- ok = posix.setenv(ckey, cval, true) == .OK
+ if posix.setenv(ckey, cval, true) != nil {
+ err = _get_platform_error_from_errno()
+ }
return
}
@@ -54,23 +56,23 @@ _clear_env :: proc() {
}
}
-_environ :: proc(allocator: runtime.Allocator) -> (environ: []string) {
+_environ :: proc(allocator: runtime.Allocator) -> (environ: []string, err: Error) {
n := 0
for entry := posix.environ[0]; entry != nil; n, entry = n+1, posix.environ[n] {}
- err: runtime.Allocator_Error
- if environ, err = make([]string, n, allocator); err != nil {
- // NOTE(laytan): is the environment empty or did allocation fail, how does the user know?
- return
+ r := make([dynamic]string, 0, n, allocator) or_return
+ defer if err != nil {
+ for e in r {
+ delete(e, allocator)
+ }
+ delete(r)
}
for i, entry := 0, posix.environ[0]; entry != nil; i, entry = i+1, posix.environ[i] {
- if environ[i], err = strings.clone(string(entry), allocator); err != nil {
- // NOTE(laytan): is the entire environment returned or did allocation fail, how does the user know?
- return
- }
+ append(&r, strings.clone(string(entry), allocator) or_return)
}
+ environ = r[:]
return
}
diff --git a/core/os/os2/env_wasi.odin b/core/os/os2/env_wasi.odin
new file mode 100644
index 000000000..305192c92
--- /dev/null
+++ b/core/os/os2/env_wasi.odin
@@ -0,0 +1,159 @@
+#+private
+package os2
+
+import "base:runtime"
+
+import "core:strings"
+import "core:sync"
+import "core:sys/wasm/wasi"
+
+g_env: map[string]string
+g_env_buf: []byte
+g_env_mutex: sync.RW_Mutex
+g_env_error: Error
+g_env_built: bool
+
+build_env :: proc() -> (err: Error) {
+ if g_env_built || g_env_error != nil {
+ return g_env_error
+ }
+
+ sync.guard(&g_env_mutex)
+
+ if g_env_built || g_env_error != nil {
+ return g_env_error
+ }
+
+ defer if err != nil {
+ g_env_error = err
+ }
+
+ num_envs, size_of_envs, _err := wasi.environ_sizes_get()
+ if _err != nil {
+ return _get_platform_error(_err)
+ }
+
+ g_env = make(map[string]string, num_envs, file_allocator()) or_return
+ defer if err != nil { delete(g_env) }
+
+ g_env_buf = make([]byte, size_of_envs, file_allocator()) or_return
+ defer if err != nil { delete(g_env_buf, file_allocator()) }
+
+ TEMP_ALLOCATOR_GUARD()
+
+ envs := make([]cstring, num_envs, temp_allocator()) or_return
+
+ _err = wasi.environ_get(raw_data(envs), raw_data(g_env_buf))
+ if _err != nil {
+ return _get_platform_error(_err)
+ }
+
+ for env in envs {
+ key, _, value := strings.partition(string(env), "=")
+ g_env[key] = value
+ }
+
+ g_env_built = true
+ return
+}
+
+delete_string_if_not_original :: proc(str: string) {
+ start := uintptr(raw_data(g_env_buf))
+ end := start + uintptr(len(g_env_buf))
+ ptr := uintptr(raw_data(str))
+ if ptr < start || ptr > end {
+ delete(str, file_allocator())
+ }
+}
+
+@(require_results)
+_lookup_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string, found: bool) {
+ if err := build_env(); err != nil {
+ return
+ }
+
+ sync.shared_guard(&g_env_mutex)
+
+ value = g_env[key] or_return
+ value, _ = clone_string(value, allocator)
+ return
+}
+
+@(require_results)
+_set_env :: proc(key, value: string) -> (err: Error) {
+ build_env() or_return
+
+ sync.guard(&g_env_mutex)
+
+ defer if err != nil {
+ delete_key(&g_env, key)
+ }
+
+ key_ptr, value_ptr, just_inserted := map_entry(&g_env, key) or_return
+
+ if just_inserted {
+ key_ptr^ = clone_string(key, file_allocator()) or_return
+ defer if err != nil {
+ delete(key_ptr^, file_allocator())
+ }
+ value_ptr^ = clone_string(value, file_allocator()) or_return
+ return
+ }
+
+ delete_string_if_not_original(value_ptr^)
+
+ value_ptr^ = clone_string(value, file_allocator()) or_return
+ return
+}
+
+@(require_results)
+_unset_env :: proc(key: string) -> bool {
+ if err := build_env(); err != nil {
+ return false
+ }
+
+ sync.guard(&g_env_mutex)
+
+ dkey, dval := delete_key(&g_env, key)
+ delete_string_if_not_original(dkey)
+ delete_string_if_not_original(dval)
+ return true
+}
+
+_clear_env :: proc() {
+ sync.guard(&g_env_mutex)
+
+ for k, v in g_env {
+ delete_string_if_not_original(k)
+ delete_string_if_not_original(v)
+ }
+
+ delete(g_env_buf, file_allocator())
+ g_env_buf = {}
+
+ clear(&g_env)
+
+ g_env_built = true
+}
+
+@(require_results)
+_environ :: proc(allocator: runtime.Allocator) -> (environ: []string, err: Error) {
+ build_env() or_return
+
+ sync.shared_guard(&g_env_mutex)
+
+ envs := make([dynamic]string, 0, len(g_env), allocator) or_return
+ defer if err != nil {
+ for env in envs {
+ delete(env, allocator)
+ }
+ delete(envs)
+ }
+
+ for k, v in g_env {
+ append(&envs, concatenate({k, "=", v}, allocator) or_return)
+ }
+
+ environ = envs[:]
+ return
+}
diff --git a/core/os/os2/env_windows.odin b/core/os/os2/env_windows.odin
index a1e8c969d..3ac26a261 100644
--- a/core/os/os2/env_windows.odin
+++ b/core/os/os2/env_windows.odin
@@ -36,12 +36,15 @@ _lookup_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string
return
}
-_set_env :: proc(key, value: string) -> bool {
+_set_env :: proc(key, value: string) -> Error {
TEMP_ALLOCATOR_GUARD()
- k, _ := win32_utf8_to_wstring(key, temp_allocator())
- v, _ := win32_utf8_to_wstring(value, temp_allocator())
+ k := win32_utf8_to_wstring(key, temp_allocator()) or_return
+ v := win32_utf8_to_wstring(value, temp_allocator()) or_return
- return bool(win32.SetEnvironmentVariableW(k, v))
+ if !win32.SetEnvironmentVariableW(k, v) {
+ return _get_platform_error()
+ }
+ return nil
}
_unset_env :: proc(key: string) -> bool {
@@ -52,7 +55,7 @@ _unset_env :: proc(key: string) -> bool {
_clear_env :: proc() {
TEMP_ALLOCATOR_GUARD()
- envs := environ(temp_allocator())
+ envs, _ := environ(temp_allocator())
for env in envs {
for j in 1..<len(env) {
if env[j] == '=' {
@@ -63,10 +66,10 @@ _clear_env :: proc() {
}
}
-_environ :: proc(allocator: runtime.Allocator) -> []string {
+_environ :: proc(allocator: runtime.Allocator) -> (environ: []string, err: Error) {
envs := win32.GetEnvironmentStringsW()
if envs == nil {
- return nil
+ return
}
defer win32.FreeEnvironmentStringsW(envs)
@@ -82,7 +85,13 @@ _environ :: proc(allocator: runtime.Allocator) -> []string {
}
}
- r := make([dynamic]string, 0, n, allocator)
+ r := make([dynamic]string, 0, n, allocator) or_return
+ defer if err != nil {
+ for e in r {
+ delete(e, allocator)
+ }
+ delete(r)
+ }
for from, i, p := 0, 0, envs; true; i += 1 {
c := ([^]u16)(p)[i]
if c == 0 {
@@ -90,12 +99,14 @@ _environ :: proc(allocator: runtime.Allocator) -> []string {
break
}
w := ([^]u16)(p)[from:i]
- append(&r, win32_utf16_to_utf8(w, allocator) or_else "")
+ s := win32_utf16_to_utf8(w, allocator) or_return
+ append(&r, s)
from = i + 1
}
}
- return r[:]
+ environ = r[:]
+ return
}
diff --git a/core/os/os2/errors_posix.odin b/core/os/os2/errors_posix.odin
index 0b5876c0b..8a9ca07df 100644
--- a/core/os/os2/errors_posix.odin
+++ b/core/os/os2/errors_posix.odin
@@ -10,8 +10,12 @@ _error_string :: proc(errno: i32) -> string {
return string(posix.strerror(posix.Errno(errno)))
}
-_get_platform_error :: proc() -> Error {
- #partial switch errno := posix.errno(); errno {
+_get_platform_error_from_errno :: proc() -> Error {
+ return _get_platform_error_existing(posix.errno())
+}
+
+_get_platform_error_existing :: proc(errno: posix.Errno) -> Error {
+ #partial switch errno {
case .EPERM:
return .Permission_Denied
case .EEXIST:
@@ -32,3 +36,8 @@ _get_platform_error :: proc() -> Error {
return Platform_Error(errno)
}
}
+
+_get_platform_error :: proc{
+ _get_platform_error_existing,
+ _get_platform_error_from_errno,
+}
diff --git a/core/os/os2/errors_wasi.odin b/core/os/os2/errors_wasi.odin
new file mode 100644
index 000000000..b88e5b81e
--- /dev/null
+++ b/core/os/os2/errors_wasi.odin
@@ -0,0 +1,47 @@
+#+private
+package os2
+
+import "base:runtime"
+
+import "core:slice"
+import "core:sys/wasm/wasi"
+
+_Platform_Error :: wasi.errno_t
+
+_error_string :: proc(errno: i32) -> string {
+ e := wasi.errno_t(errno)
+ if e == .NONE {
+ return ""
+ }
+
+ err := runtime.Type_Info_Enum_Value(e)
+
+ ti := &runtime.type_info_base(type_info_of(wasi.errno_t)).variant.(runtime.Type_Info_Enum)
+ if idx, ok := slice.binary_search(ti.values, err); ok {
+ return ti.names[idx]
+ }
+ return "<unknown platform error>"
+}
+
+_get_platform_error :: proc(errno: wasi.errno_t) -> Error {
+ #partial switch errno {
+ case .PERM:
+ return .Permission_Denied
+ case .EXIST:
+ return .Exist
+ case .NOENT:
+ return .Not_Exist
+ case .TIMEDOUT:
+ return .Timeout
+ case .PIPE:
+ return .Broken_Pipe
+ case .BADF:
+ return .Invalid_File
+ case .NOMEM:
+ return .Out_Of_Memory
+ case .NOSYS:
+ return .Unsupported
+ case:
+ return Platform_Error(errno)
+ }
+}
diff --git a/core/os/os2/file.odin b/core/os/os2/file.odin
index eedf8570c..28d2bc69b 100644
--- a/core/os/os2/file.odin
+++ b/core/os/os2/file.odin
@@ -115,7 +115,7 @@ open :: proc(name: string, flags := File_Flags{.Read}, perm := 0o777) -> (^File,
@(require_results)
new_file :: proc(handle: uintptr, name: string) -> ^File {
- file, err := _new_file(handle, name)
+ file, err := _new_file(handle, name, file_allocator())
if err != nil {
panic(error_string(err))
}
@@ -123,6 +123,11 @@ new_file :: proc(handle: uintptr, name: string) -> ^File {
}
@(require_results)
+clone :: proc(f: ^File) -> (^File, Error) {
+ return _clone(f)
+}
+
+@(require_results)
fd :: proc(f: ^File) -> uintptr {
return _fd(f)
}
diff --git a/core/os/os2/file_linux.odin b/core/os/os2/file_linux.odin
index e9ce13447..811ee7055 100644
--- a/core/os/os2/file_linux.odin
+++ b/core/os/os2/file_linux.odin
@@ -7,6 +7,13 @@ import "core:time"
import "core:sync"
import "core:sys/linux"
+// Most implementations will EINVAL at some point when doing big writes.
+// In practice a read/write call would probably never read/write these big buffers all at once,
+// which is why the number of bytes is returned and why there are procs that will call this in a
+// loop for you.
+// We set a max of 1GB to keep alignment and to be safe.
+MAX_RW :: 1 << 30
+
File_Impl :: struct {
file: File,
name: string,
@@ -39,37 +46,23 @@ _stderr := File{
@init
_standard_stream_init :: proc() {
- @static stdin_impl := File_Impl {
- name = "/proc/self/fd/0",
- fd = 0,
- }
-
- @static stdout_impl := File_Impl {
- name = "/proc/self/fd/1",
- fd = 1,
- }
-
- @static stderr_impl := File_Impl {
- name = "/proc/self/fd/2",
- fd = 2,
+ new_std :: proc(impl: ^File_Impl, fd: linux.Fd, name: string) -> ^File {
+ impl.file.impl = impl
+ impl.fd = linux.Fd(fd)
+ impl.allocator = runtime.nil_allocator()
+ impl.name = name
+ impl.file.stream = {
+ data = impl,
+ procedure = _file_stream_proc,
+ }
+ impl.file.fstat = _fstat
+ return &impl.file
}
- stdin_impl.allocator = file_allocator()
- stdout_impl.allocator = file_allocator()
- stderr_impl.allocator = file_allocator()
-
- _stdin.impl = &stdin_impl
- _stdout.impl = &stdout_impl
- _stderr.impl = &stderr_impl
-
- // cannot define these initially because cyclic reference
- _stdin.stream.data = &stdin_impl
- _stdout.stream.data = &stdout_impl
- _stderr.stream.data = &stderr_impl
-
- stdin = &_stdin
- stdout = &_stdout
- stderr = &_stderr
+ @(static) files: [3]File_Impl
+ stdin = new_std(&files[0], 0, "/proc/self/fd/0")
+ stdout = new_std(&files[1], 1, "/proc/self/fd/1")
+ stderr = new_std(&files[2], 2, "/proc/self/fd/2")
}
_open :: proc(name: string, flags: File_Flags, perm: int) -> (f: ^File, err: Error) {
@@ -80,6 +73,9 @@ _open :: proc(name: string, flags: File_Flags, perm: int) -> (f: ^File, err: Err
// terminal would be incredibly rare. This has no effect on files while
// allowing us to open serial devices.
sys_flags: linux.Open_Flags = {.NOCTTY, .CLOEXEC}
+ when size_of(rawptr) == 4 {
+ sys_flags += {.LARGEFILE}
+ }
switch flags & (O_RDONLY|O_WRONLY|O_RDWR) {
case O_RDONLY:
case O_WRONLY: sys_flags += {.WRONLY}
@@ -97,18 +93,18 @@ _open :: proc(name: string, flags: File_Flags, perm: int) -> (f: ^File, err: Err
return nil, _get_platform_error(errno)
}
- return _new_file(uintptr(fd), name)
+ return _new_file(uintptr(fd), name, file_allocator())
}
-_new_file :: proc(fd: uintptr, _: string = "") -> (f: ^File, err: Error) {
- impl := new(File_Impl, file_allocator()) or_return
+_new_file :: proc(fd: uintptr, _: string, allocator: runtime.Allocator) -> (f: ^File, err: Error) {
+ impl := new(File_Impl, allocator) or_return
defer if err != nil {
- free(impl, file_allocator())
+ free(impl, allocator)
}
impl.file.impl = impl
impl.fd = linux.Fd(fd)
- impl.allocator = file_allocator()
- impl.name = _get_full_path(impl.fd, file_allocator()) or_return
+ impl.allocator = allocator
+ impl.name = _get_full_path(impl.fd, impl.allocator) or_return
impl.file.stream = {
data = impl,
procedure = _file_stream_proc,
@@ -117,6 +113,23 @@ _new_file :: proc(fd: uintptr, _: string = "") -> (f: ^File, err: Error) {
return &impl.file, nil
}
+_clone :: proc(f: ^File) -> (clone: ^File, err: Error) {
+ if f == nil || f.impl == nil {
+ return
+ }
+
+ fd := (^File_Impl)(f.impl).fd
+
+ clonefd, errno := linux.dup(fd)
+ if errno != nil {
+ err = _get_platform_error(errno)
+ return
+ }
+ defer if err != nil { linux.close(clonefd) }
+
+ return _new_file(uintptr(clonefd), "", file_allocator())
+}
+
@(require_results)
_open_buffered :: proc(name: string, buffer_size: uint, flags := File_Flags{.Read}, perm := 0o777) -> (f: ^File, err: Error) {
@@ -190,10 +203,11 @@ _seek :: proc(f: ^File_Impl, offset: i64, whence: io.Seek_From) -> (ret: i64, er
}
_read :: proc(f: ^File_Impl, p: []byte) -> (i64, Error) {
- if len(p) == 0 {
+ if len(p) <= 0 {
return 0, nil
}
- n, errno := linux.read(f.fd, p[:])
+
+ n, errno := linux.read(f.fd, p[:min(len(p), MAX_RW)])
if errno != .NONE {
return -1, _get_platform_error(errno)
}
@@ -201,13 +215,13 @@ _read :: proc(f: ^File_Impl, p: []byte) -> (i64, Error) {
}
_read_at :: proc(f: ^File_Impl, p: []byte, offset: i64) -> (i64, Error) {
- if len(p) == 0 {
+ if len(p) <= 0 {
return 0, nil
}
if offset < 0 {
return 0, .Invalid_Offset
}
- n, errno := linux.pread(f.fd, p[:], offset)
+ n, errno := linux.pread(f.fd, p[:min(len(p), MAX_RW)], offset)
if errno != .NONE {
return -1, _get_platform_error(errno)
}
@@ -217,29 +231,42 @@ _read_at :: proc(f: ^File_Impl, p: []byte, offset: i64) -> (i64, Error) {
return i64(n), nil
}
-_write :: proc(f: ^File_Impl, p: []byte) -> (i64, Error) {
- if len(p) == 0 {
- return 0, nil
- }
- n, errno := linux.write(f.fd, p[:])
- if errno != .NONE {
- return -1, _get_platform_error(errno)
+_write :: proc(f: ^File_Impl, p: []byte) -> (nt: i64, err: Error) {
+ p := p
+ for len(p) > 0 {
+ n, errno := linux.write(f.fd, p[:min(len(p), MAX_RW)])
+ if errno != .NONE {
+ err = _get_platform_error(errno)
+ return
+ }
+
+ p = p[n:]
+ nt += i64(n)
}
- return i64(n), nil
+
+ return
}
-_write_at :: proc(f: ^File_Impl, p: []byte, offset: i64) -> (i64, Error) {
- if len(p) == 0 {
- return 0, nil
- }
+_write_at :: proc(f: ^File_Impl, p: []byte, offset: i64) -> (nt: i64, err: Error) {
if offset < 0 {
return 0, .Invalid_Offset
}
- n, errno := linux.pwrite(f.fd, p[:], offset)
- if errno != .NONE {
- return -1, _get_platform_error(errno)
+
+ p := p
+ offset := offset
+ for len(p) > 0 {
+ n, errno := linux.pwrite(f.fd, p[:min(len(p), MAX_RW)], offset)
+ if errno != .NONE {
+ err = _get_platform_error(errno)
+ return
+ }
+
+ p = p[n:]
+ nt += i64(n)
+ offset += i64(n)
}
- return i64(n), nil
+
+ return
}
_file_size :: proc(f: ^File_Impl) -> (n: i64, err: Error) {
@@ -272,28 +299,12 @@ _truncate :: proc(f: ^File, size: i64) -> Error {
}
_remove :: proc(name: string) -> Error {
- is_dir_fd :: proc(fd: linux.Fd) -> bool {
- s: linux.Stat
- if linux.fstat(fd, &s) != .NONE {
- return false
- }
- return linux.S_ISDIR(s.mode)
- }
-
TEMP_ALLOCATOR_GUARD()
name_cstr := temp_cstring(name) or_return
- fd, errno := linux.open(name_cstr, {.NOFOLLOW})
- #partial switch (errno) {
- case .ELOOP:
- /* symlink */
- case .NONE:
- defer linux.close(fd)
- if is_dir_fd(fd) {
- return _get_platform_error(linux.rmdir(name_cstr))
- }
- case:
- return _get_platform_error(errno)
+ if fd, errno := linux.open(name_cstr, _OPENDIR_FLAGS + {.NOFOLLOW}); errno == .NONE {
+ linux.close(fd)
+ return _get_platform_error(linux.rmdir(name_cstr))
}
return _get_platform_error(linux.unlink(name_cstr))
diff --git a/core/os/os2/file_posix.odin b/core/os/os2/file_posix.odin
index b7dc43287..43d5866b1 100644
--- a/core/os/os2/file_posix.odin
+++ b/core/os/os2/file_posix.odin
@@ -21,23 +21,29 @@ File_Impl :: struct {
name: string,
cname: cstring,
fd: posix.FD,
+ allocator: runtime.Allocator,
}
@(init)
init_std_files :: proc() {
- // NOTE: is this (paths) also the case on non darwin?
-
- stdin = __new_file(posix.STDIN_FILENO)
- (^File_Impl)(stdin.impl).name = "/dev/stdin"
- (^File_Impl)(stdin.impl).cname = "/dev/stdin"
-
- stdout = __new_file(posix.STDIN_FILENO)
- (^File_Impl)(stdout.impl).name = "/dev/stdout"
- (^File_Impl)(stdout.impl).cname = "/dev/stdout"
+ new_std :: proc(impl: ^File_Impl, fd: posix.FD, name: cstring) -> ^File {
+ impl.file.impl = impl
+ impl.fd = fd
+ impl.allocator = runtime.nil_allocator()
+ impl.cname = name
+ impl.name = string(name)
+ impl.file.stream = {
+ data = impl,
+ procedure = _file_stream_proc,
+ }
+ impl.file.fstat = _fstat
+ return &impl.file
+ }
- stderr = __new_file(posix.STDIN_FILENO)
- (^File_Impl)(stderr.impl).name = "/dev/stderr"
- (^File_Impl)(stderr.impl).cname = "/dev/stderr"
+ @(static) files: [3]File_Impl
+ stdin = new_std(&files[0], posix.STDIN_FILENO, "/dev/stdin")
+ stdout = new_std(&files[1], posix.STDOUT_FILENO, "/dev/stdout")
+ stderr = new_std(&files[2], posix.STDERR_FILENO, "/dev/stderr")
}
_open :: proc(name: string, flags: File_Flags, perm: int) -> (f: ^File, err: Error) {
@@ -72,10 +78,10 @@ _open :: proc(name: string, flags: File_Flags, perm: int) -> (f: ^File, err: Err
return
}
- return _new_file(uintptr(fd), name)
+ return _new_file(uintptr(fd), name, file_allocator())
}
-_new_file :: proc(handle: uintptr, name: string) -> (f: ^File, err: Error) {
+_new_file :: proc(handle: uintptr, name: string, allocator: runtime.Allocator) -> (f: ^File, err: Error) {
if name == "" {
err = .Invalid_Path
return
@@ -84,10 +90,10 @@ _new_file :: proc(handle: uintptr, name: string) -> (f: ^File, err: Error) {
return
}
- crname := _posix_absolute_path(posix.FD(handle), name, file_allocator()) or_return
+ crname := _posix_absolute_path(posix.FD(handle), name, allocator) or_return
rname := string(crname)
- f = __new_file(posix.FD(handle))
+ f = __new_file(posix.FD(handle), allocator)
impl := (^File_Impl)(f.impl)
impl.name = rname
impl.cname = crname
@@ -95,10 +101,11 @@ _new_file :: proc(handle: uintptr, name: string) -> (f: ^File, err: Error) {
return f, nil
}
-__new_file :: proc(handle: posix.FD) -> ^File {
- impl := new(File_Impl, file_allocator())
+__new_file :: proc(handle: posix.FD, allocator: runtime.Allocator) -> ^File {
+ impl := new(File_Impl, allocator)
impl.file.impl = impl
impl.fd = posix.FD(handle)
+ impl.allocator = allocator
impl.file.stream = {
data = impl,
procedure = _file_stream_proc,
@@ -107,6 +114,29 @@ __new_file :: proc(handle: posix.FD) -> ^File {
return &impl.file
}
+_clone :: proc(f: ^File) -> (clone: ^File, err: Error) {
+ if f == nil || f.impl == nil {
+ err = .Invalid_Pointer
+ return
+ }
+
+ impl := (^File_Impl)(f.impl)
+
+ fd := posix.dup(impl.fd)
+ if fd <= 0 {
+ err = _get_platform_error()
+ return
+ }
+ defer if err != nil { posix.close(fd) }
+
+ clone = __new_file(fd, file_allocator())
+ clone_impl := (^File_Impl)(clone.impl)
+ clone_impl.cname = clone_to_cstring(impl.name, file_allocator()) or_return
+ clone_impl.name = string(clone_impl.cname)
+
+ return
+}
+
_close :: proc(f: ^File_Impl) -> (err: Error) {
if f == nil { return nil }
@@ -114,8 +144,10 @@ _close :: proc(f: ^File_Impl) -> (err: Error) {
err = _get_platform_error()
}
- delete(f.cname, file_allocator())
- free(f, file_allocator())
+ allocator := f.allocator
+
+ delete(f.cname, allocator)
+ free(f, allocator)
return
}
diff --git a/core/os/os2/file_wasi.odin b/core/os/os2/file_wasi.odin
new file mode 100644
index 000000000..0245841e3
--- /dev/null
+++ b/core/os/os2/file_wasi.odin
@@ -0,0 +1,560 @@
+#+private
+package os2
+
+import "base:runtime"
+
+import "core:io"
+import "core:sys/wasm/wasi"
+import "core:time"
+
+// NOTE: Don't know if there is a max in wasi.
+MAX_RW :: 1 << 30
+
+File_Impl :: struct {
+ file: File,
+ name: string,
+ fd: wasi.fd_t,
+ allocator: runtime.Allocator,
+}
+
+// WASI works with "preopened" directories, the environment retrieves directories
+// (for example with `wasmtime --dir=. module.wasm`) and those given directories
+// are the only ones accessible by the application.
+//
+// So in order to facilitate the `os` API (absolute paths etc.) we keep a list
+// of the given directories and match them when needed (notably `os.open`).
+Preopen :: struct {
+ fd: wasi.fd_t,
+ prefix: string,
+}
+preopens: []Preopen
+
+@(init)
+init_std_files :: proc() {
+ new_std :: proc(impl: ^File_Impl, fd: wasi.fd_t, name: string) -> ^File {
+ impl.file.impl = impl
+ impl.allocator = runtime.nil_allocator()
+ impl.fd = fd
+ impl.name = string(name)
+ impl.file.stream = {
+ data = impl,
+ procedure = _file_stream_proc,
+ }
+ impl.file.fstat = _fstat
+ return &impl.file
+ }
+
+ @(static) files: [3]File_Impl
+ stdin = new_std(&files[0], 0, "/dev/stdin")
+ stdout = new_std(&files[1], 1, "/dev/stdout")
+ stderr = new_std(&files[2], 2, "/dev/stderr")
+}
+
+@(init)
+init_preopens :: proc() {
+ strip_prefixes :: proc(path: string) -> string {
+ path := path
+ loop: for len(path) > 0 {
+ switch {
+ case path[0] == '/':
+ path = path[1:]
+ case len(path) > 2 && path[0] == '.' && path[1] == '/':
+ path = path[2:]
+ case len(path) == 1 && path[0] == '.':
+ path = path[1:]
+ case:
+ break loop
+ }
+ }
+ return path
+ }
+
+ n: int
+ n_loop: for fd := wasi.fd_t(3); ; fd += 1 {
+ _, err := wasi.fd_prestat_get(fd)
+ #partial switch err {
+ case .BADF: break n_loop
+ case .SUCCESS: n += 1
+ case:
+ print_error(stderr, _get_platform_error(err), "unexpected error from wasi_prestat_get")
+ break n_loop
+ }
+ }
+
+ alloc_err: runtime.Allocator_Error
+ preopens, alloc_err = make([]Preopen, n, file_allocator())
+ if alloc_err != nil {
+ print_error(stderr, alloc_err, "could not allocate memory for wasi preopens")
+ return
+ }
+
+ loop: for &preopen, i in preopens {
+ fd := wasi.fd_t(3 + i)
+
+ desc, err := wasi.fd_prestat_get(fd)
+ assert(err == .SUCCESS)
+
+ switch desc.tag {
+ case .DIR:
+ buf: []byte
+ buf, alloc_err = make([]byte, desc.dir.pr_name_len, file_allocator())
+ if alloc_err != nil {
+ print_error(stderr, alloc_err, "could not allocate memory for wasi preopen dir name")
+ continue loop
+ }
+
+ if err = wasi.fd_prestat_dir_name(fd, buf); err != .SUCCESS {
+ print_error(stderr, _get_platform_error(err), "could not get filesystem preopen dir name")
+ continue loop
+ }
+
+ preopen.fd = fd
+ preopen.prefix = strip_prefixes(string(buf))
+ }
+ }
+}
+
+@(require_results)
+match_preopen :: proc(path: string) -> (wasi.fd_t, string, bool) {
+ @(require_results)
+ prefix_matches :: proc(prefix, path: string) -> bool {
+ // Empty is valid for any relative path.
+ if len(prefix) == 0 && len(path) > 0 && path[0] != '/' {
+ return true
+ }
+
+ if len(path) < len(prefix) {
+ return false
+ }
+
+ if path[:len(prefix)] != prefix {
+ return false
+ }
+
+ // Only match on full components.
+ i := len(prefix)
+ for i > 0 && prefix[i-1] == '/' {
+ i -= 1
+ }
+ return path[i] == '/'
+ }
+
+ path := path
+ if path == "" {
+ return 0, "", false
+ }
+
+ for len(path) > 0 && path[0] == '/' {
+ path = path[1:]
+ }
+
+ match: Preopen
+ #reverse for preopen in preopens {
+ if (match.fd == 0 || len(preopen.prefix) > len(match.prefix)) && prefix_matches(preopen.prefix, path) {
+ match = preopen
+ }
+ }
+
+ if match.fd == 0 {
+ return 0, "", false
+ }
+
+ relative := path[len(match.prefix):]
+ for len(relative) > 0 && relative[0] == '/' {
+ relative = relative[1:]
+ }
+
+ if len(relative) == 0 {
+ relative = "."
+ }
+
+ return match.fd, relative, true
+}
+
+_open :: proc(name: string, flags: File_Flags, perm: int) -> (f: ^File, err: Error) {
+ dir_fd, relative, ok := match_preopen(name)
+ if !ok {
+ return nil, .Invalid_Path
+ }
+
+ oflags: wasi.oflags_t
+ if .Create in flags { oflags += {.CREATE} }
+ if .Excl in flags { oflags += {.EXCL} }
+ if .Trunc in flags { oflags += {.TRUNC} }
+
+ fdflags: wasi.fdflags_t
+ if .Append in flags { fdflags += {.APPEND} }
+ if .Sync in flags { fdflags += {.SYNC} }
+
+ // NOTE: rights are adjusted to what this package's functions might want to call.
+ rights: wasi.rights_t
+ if .Read in flags { rights += {.FD_READ, .FD_FILESTAT_GET, .PATH_FILESTAT_GET} }
+ if .Write in flags { rights += {.FD_WRITE, .FD_SYNC, .FD_FILESTAT_SET_SIZE, .FD_FILESTAT_SET_TIMES, .FD_SEEK} }
+
+ fd, fderr := wasi.path_open(dir_fd, {.SYMLINK_FOLLOW}, relative, oflags, rights, {}, fdflags)
+ if fderr != nil {
+ err = _get_platform_error(fderr)
+ return
+ }
+
+ return _new_file(uintptr(fd), name, file_allocator())
+}
+
+_new_file :: proc(handle: uintptr, name: string, allocator: runtime.Allocator) -> (f: ^File, err: Error) {
+ if name == "" {
+ err = .Invalid_Path
+ return
+ }
+
+ impl := new(File_Impl, allocator) or_return
+ defer if err != nil { free(impl, allocator) }
+
+ impl.allocator = allocator
+ // NOTE: wasi doesn't really do full paths afact.
+ impl.name = clone_string(name, allocator) or_return
+ impl.fd = wasi.fd_t(handle)
+ impl.file.impl = impl
+ impl.file.stream = {
+ data = impl,
+ procedure = _file_stream_proc,
+ }
+ impl.file.fstat = _fstat
+
+ return &impl.file, nil
+}
+
+_clone :: proc(f: ^File) -> (clone: ^File, err: Error) {
+ if f == nil || f.impl == nil {
+ return
+ }
+
+ dir_fd, relative, ok := match_preopen(name(f))
+ if !ok {
+ return nil, .Invalid_Path
+ }
+
+ fd, fderr := wasi.path_open(dir_fd, {.SYMLINK_FOLLOW}, relative, {}, {}, {}, {})
+ if fderr != nil {
+ err = _get_platform_error(fderr)
+ return
+ }
+ defer if err != nil { wasi.fd_close(fd) }
+
+ fderr = wasi.fd_renumber((^File_Impl)(f.impl).fd, fd)
+ if fderr != nil {
+ err = _get_platform_error(fderr)
+ return
+ }
+
+ return _new_file(uintptr(fd), name(f), file_allocator())
+}
+
+_close :: proc(f: ^File_Impl) -> (err: Error) {
+ if errno := wasi.fd_close(f.fd); errno != nil {
+ err = _get_platform_error(errno)
+ }
+
+ delete(f.name, f.allocator)
+ free(f, f.allocator)
+ return
+}
+
+_fd :: proc(f: ^File) -> uintptr {
+ return uintptr(__fd(f))
+}
+
+__fd :: proc(f: ^File) -> wasi.fd_t {
+ if f != nil && f.impl != nil {
+ return (^File_Impl)(f.impl).fd
+ }
+ return -1
+}
+
+_name :: proc(f: ^File) -> string {
+ if f != nil && f.impl != nil {
+ return (^File_Impl)(f.impl).name
+ }
+ return ""
+}
+
+_sync :: proc(f: ^File) -> Error {
+ return _get_platform_error(wasi.fd_sync(__fd(f)))
+}
+
+_truncate :: proc(f: ^File, size: i64) -> Error {
+ return _get_platform_error(wasi.fd_filestat_set_size(__fd(f), wasi.filesize_t(size)))
+}
+
+_remove :: proc(name: string) -> Error {
+ dir_fd, relative, ok := match_preopen(name)
+ if !ok {
+ return .Invalid_Path
+ }
+
+ err := wasi.path_remove_directory(dir_fd, relative)
+ if err == .NOTDIR {
+ err = wasi.path_unlink_file(dir_fd, relative)
+ }
+
+ return _get_platform_error(err)
+}
+
+_rename :: proc(old_path, new_path: string) -> Error {
+ src_dir_fd, src_relative, src_ok := match_preopen(old_path)
+ if !src_ok {
+ return .Invalid_Path
+ }
+
+ new_dir_fd, new_relative, new_ok := match_preopen(new_path)
+ if !new_ok {
+ return .Invalid_Path
+ }
+
+ return _get_platform_error(wasi.path_rename(src_dir_fd, src_relative, new_dir_fd, new_relative))
+}
+
+_link :: proc(old_name, new_name: string) -> Error {
+ src_dir_fd, src_relative, src_ok := match_preopen(old_name)
+ if !src_ok {
+ return .Invalid_Path
+ }
+
+ new_dir_fd, new_relative, new_ok := match_preopen(new_name)
+ if !new_ok {
+ return .Invalid_Path
+ }
+
+ return _get_platform_error(wasi.path_link(src_dir_fd, {.SYMLINK_FOLLOW}, src_relative, new_dir_fd, new_relative))
+}
+
+_symlink :: proc(old_name, new_name: string) -> Error {
+ src_dir_fd, src_relative, src_ok := match_preopen(old_name)
+ if !src_ok {
+ return .Invalid_Path
+ }
+
+ new_dir_fd, new_relative, new_ok := match_preopen(new_name)
+ if !new_ok {
+ return .Invalid_Path
+ }
+
+ if src_dir_fd != new_dir_fd {
+ return .Invalid_Path
+ }
+
+ return _get_platform_error(wasi.path_symlink(src_relative, src_dir_fd, new_relative))
+}
+
+_read_link :: proc(name: string, allocator: runtime.Allocator) -> (s: string, err: Error) {
+ dir_fd, relative, ok := match_preopen(name)
+ if !ok {
+ return "", .Invalid_Path
+ }
+
+ n, _err := wasi.path_readlink(dir_fd, relative, nil)
+ if _err != nil {
+ err = _get_platform_error(_err)
+ return
+ }
+
+ buf := make([]byte, n, allocator) or_return
+
+ _, _err = wasi.path_readlink(dir_fd, relative, buf)
+ s = string(buf)
+ err = _get_platform_error(_err)
+ return
+}
+
+_chdir :: proc(name: string) -> Error {
+ return .Unsupported
+}
+
+_fchdir :: proc(f: ^File) -> Error {
+ return .Unsupported
+}
+
+_fchmod :: proc(f: ^File, mode: int) -> Error {
+ return .Unsupported
+}
+
+_chmod :: proc(name: string, mode: int) -> Error {
+ return .Unsupported
+}
+
+_fchown :: proc(f: ^File, uid, gid: int) -> Error {
+ return .Unsupported
+}
+
+_chown :: proc(name: string, uid, gid: int) -> Error {
+ return .Unsupported
+}
+
+_lchown :: proc(name: string, uid, gid: int) -> Error {
+ return .Unsupported
+}
+
+_chtimes :: proc(name: string, atime, mtime: time.Time) -> Error {
+ dir_fd, relative, ok := match_preopen(name)
+ if !ok {
+ return .Invalid_Path
+ }
+
+ _atime := wasi.timestamp_t(atime._nsec)
+ _mtime := wasi.timestamp_t(mtime._nsec)
+
+ return _get_platform_error(wasi.path_filestat_set_times(dir_fd, {.SYMLINK_FOLLOW}, relative, _atime, _mtime, {.MTIM, .ATIM}))
+}
+
+_fchtimes :: proc(f: ^File, atime, mtime: time.Time) -> Error {
+ _atime := wasi.timestamp_t(atime._nsec)
+ _mtime := wasi.timestamp_t(mtime._nsec)
+
+ return _get_platform_error(wasi.fd_filestat_set_times(__fd(f), _atime, _mtime, {.ATIM, .MTIM}))
+}
+
+_exists :: proc(path: string) -> bool {
+ dir_fd, relative, ok := match_preopen(path)
+ if !ok {
+ return false
+ }
+
+ _, err := wasi.path_filestat_get(dir_fd, {.SYMLINK_FOLLOW}, relative)
+ if err != nil {
+ return false
+ }
+
+ return true
+}
+
+_file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) {
+ f := (^File_Impl)(stream_data)
+ fd := f.fd
+
+ switch mode {
+ case .Read:
+ if len(p) <= 0 {
+ return
+ }
+
+ to_read := min(len(p), MAX_RW)
+ _n, _err := wasi.fd_read(fd, {p[:to_read]})
+ n = i64(_n)
+
+ if _err != nil {
+ err = .Unknown
+ } else if n == 0 {
+ err = .EOF
+ }
+
+ return
+
+ case .Read_At:
+ if len(p) <= 0 {
+ return
+ }
+
+ if offset < 0 {
+ err = .Invalid_Offset
+ return
+ }
+
+ to_read := min(len(p), MAX_RW)
+ _n, _err := wasi.fd_pread(fd, {p[:to_read]}, wasi.filesize_t(offset))
+ n = i64(_n)
+
+ if _err != nil {
+ err = .Unknown
+ } else if n == 0 {
+ err = .EOF
+ }
+
+ return
+
+ case .Write:
+ p := p
+ for len(p) > 0 {
+ to_write := min(len(p), MAX_RW)
+ _n, _err := wasi.fd_write(fd, {p[:to_write]})
+ if _err != nil {
+ err = .Unknown
+ return
+ }
+ p = p[_n:]
+ n += i64(_n)
+ }
+ return
+
+ case .Write_At:
+ p := p
+ offset := offset
+
+ if offset < 0 {
+ err = .Invalid_Offset
+ return
+ }
+
+ for len(p) > 0 {
+ to_write := min(len(p), MAX_RW)
+ _n, _err := wasi.fd_pwrite(fd, {p[:to_write]}, wasi.filesize_t(offset))
+ if _err != nil {
+ err = .Unknown
+ return
+ }
+
+ p = p[_n:]
+ n += i64(_n)
+ offset += i64(_n)
+ }
+ return
+
+ case .Seek:
+ #assert(int(wasi.whence_t.SET) == int(io.Seek_From.Start))
+ #assert(int(wasi.whence_t.CUR) == int(io.Seek_From.Current))
+ #assert(int(wasi.whence_t.END) == int(io.Seek_From.End))
+
+ switch whence {
+ case .Start, .Current, .End:
+ break
+ case:
+ err = .Invalid_Whence
+ return
+ }
+
+ _n, _err := wasi.fd_seek(fd, wasi.filedelta_t(offset), wasi.whence_t(whence))
+ #partial switch _err {
+ case .INVAL:
+ err = .Invalid_Offset
+ case:
+ err = .Unknown
+ case .SUCCESS:
+ n = i64(_n)
+ }
+ return
+
+ case .Size:
+ stat, _err := wasi.fd_filestat_get(fd)
+ if _err != nil {
+ err = .Unknown
+ return
+ }
+
+ n = i64(stat.size)
+ return
+
+ case .Flush:
+ ferr := _sync(&f.file)
+ err = error_to_io_error(ferr)
+ return
+
+ case .Close, .Destroy:
+ ferr := _close(f)
+ err = error_to_io_error(ferr)
+ return
+
+ case .Query:
+ return io.query_utility({.Read, .Read_At, .Write, .Write_At, .Seek, .Size, .Flush, .Close, .Destroy, .Query})
+
+ case:
+ return 0, .Empty
+ }
+}
diff --git a/core/os/os2/file_windows.odin b/core/os/os2/file_windows.odin
index b91a1bc3b..b123330e0 100644
--- a/core/os/os2/file_windows.odin
+++ b/core/os/os2/file_windows.odin
@@ -44,17 +44,38 @@ File_Impl :: struct {
@(init)
init_std_files :: proc() {
- stdin = new_file(uintptr(win32.GetStdHandle(win32.STD_INPUT_HANDLE)), "<stdin>")
- stdout = new_file(uintptr(win32.GetStdHandle(win32.STD_OUTPUT_HANDLE)), "<stdout>")
- stderr = new_file(uintptr(win32.GetStdHandle(win32.STD_ERROR_HANDLE)), "<stderr>")
-}
-@(fini)
-fini_std_files :: proc() {
- _destroy((^File_Impl)(stdin.impl))
- _destroy((^File_Impl)(stdout.impl))
- _destroy((^File_Impl)(stderr.impl))
-}
+ new_std :: proc(impl: ^File_Impl, code: u32, name: string) -> ^File {
+ impl.file.impl = impl
+
+ impl.allocator = runtime.nil_allocator()
+ impl.fd = win32.GetStdHandle(code)
+ impl.name = name
+ impl.wname = nil
+
+ handle := _handle(&impl.file)
+ kind := File_Impl_Kind.File
+ if m: u32; win32.GetConsoleMode(handle, &m) {
+ kind = .Console
+ }
+ if win32.GetFileType(handle) == win32.FILE_TYPE_PIPE {
+ kind = .Pipe
+ }
+ impl.kind = kind
+ impl.file.stream = {
+ data = impl,
+ procedure = _file_stream_proc,
+ }
+ impl.file.fstat = _fstat
+
+ return &impl.file
+ }
+
+ @(static) files: [3]File_Impl
+ stdin = new_std(&files[0], win32.STD_INPUT_HANDLE, "<stdin>")
+ stdout = new_std(&files[1], win32.STD_OUTPUT_HANDLE, "<stdout>")
+ stderr = new_std(&files[2], win32.STD_ERROR_HANDLE, "<stderr>")
+}
_handle :: proc(f: ^File) -> win32.HANDLE {
return win32.HANDLE(_fd(f))
@@ -132,21 +153,21 @@ _open_internal :: proc(name: string, flags: File_Flags, perm: int) -> (handle: u
_open :: proc(name: string, flags: File_Flags, perm: int) -> (f: ^File, err: Error) {
flags := flags if flags != nil else {.Read}
handle := _open_internal(name, flags, perm) or_return
- return _new_file(handle, name)
+ return _new_file(handle, name, file_allocator())
}
-_new_file :: proc(handle: uintptr, name: string) -> (f: ^File, err: Error) {
+_new_file :: proc(handle: uintptr, name: string, allocator: runtime.Allocator) -> (f: ^File, err: Error) {
if handle == INVALID_HANDLE {
return
}
- impl := new(File_Impl, file_allocator()) or_return
+ impl := new(File_Impl, allocator) or_return
defer if err != nil {
- free(impl, file_allocator())
+ free(impl, allocator)
}
impl.file.impl = impl
- impl.allocator = file_allocator()
+ impl.allocator = allocator
impl.fd = rawptr(handle)
impl.name = clone_string(name, impl.allocator) or_return
impl.wname = win32_utf8_to_wstring(name, impl.allocator) or_return
@@ -180,7 +201,7 @@ _open_buffered :: proc(name: string, buffer_size: uint, flags := File_Flags{.Rea
}
_new_file_buffered :: proc(handle: uintptr, name: string, buffer_size: uint) -> (f: ^File, err: Error) {
- f, err = _new_file(handle, name)
+ f, err = _new_file(handle, name, file_allocator())
if f != nil && err == nil {
impl := (^File_Impl)(f.impl)
impl.r_buf = make([]byte, buffer_size, file_allocator())
@@ -189,6 +210,29 @@ _new_file_buffered :: proc(handle: uintptr, name: string, buffer_size: uint) ->
return
}
+_clone :: proc(f: ^File) -> (clone: ^File, err: Error) {
+ if f == nil || f.impl == nil {
+ return
+ }
+
+ clonefd: win32.HANDLE
+ process := win32.GetCurrentProcess()
+ if !win32.DuplicateHandle(
+ process,
+ win32.HANDLE(_fd(f)),
+ process,
+ &clonefd,
+ 0,
+ false,
+ win32.DUPLICATE_SAME_ACCESS,
+ ) {
+ err = _get_platform_error()
+ return
+ }
+ defer if err != nil { win32.CloseHandle(clonefd) }
+
+ return _new_file(uintptr(clonefd), name(f), file_allocator())
+}
_fd :: proc(f: ^File) -> uintptr {
if f == nil || f.impl == nil {
diff --git a/core/os/os2/heap_linux.odin b/core/os/os2/heap_linux.odin
index ede5eb2ac..1d1f12726 100644
--- a/core/os/os2/heap_linux.odin
+++ b/core/os/os2/heap_linux.odin
@@ -1,726 +1,6 @@
#+private
package os2
-import "core:sys/linux"
-import "core:sync"
-import "core:mem"
-
-// NOTEs
-//
-// All allocations below DIRECT_MMAP_THRESHOLD exist inside of memory "Regions." A region
-// consists of a Region_Header and the memory that will be divided into allocations to
-// send to the user. The memory is an array of "Allocation_Headers" which are 8 bytes.
-// Allocation_Headers are used to navigate the memory in the region. The "next" member of
-// the Allocation_Header points to the next header, and the space between the headers
-// can be used to send to the user. This space between is referred to as "blocks" in the
-// code. The indexes in the header refer to these blocks instead of bytes. This allows us
-// to index all the memory in the region with a u16.
-//
-// When an allocation request is made, it will use the first free block that can contain
-// the entire block. If there is an excess number of blocks (as specified by the constant
-// BLOCK_SEGMENT_THRESHOLD), this extra space will be segmented and left in the free_list.
-//
-// To keep the implementation simple, there can never exist 2 free blocks adjacent to each
-// other. Any freeing will result in attempting to merge the blocks before and after the
-// newly free'd blocks.
-//
-// Any request for size above the DIRECT_MMAP_THRESHOLD will result in the allocation
-// getting its own individual mmap. Individual mmaps will still get an Allocation_Header
-// that contains the size with the last bit set to 1 to indicate it is indeed a direct
-// mmap allocation.
-
-// Why not brk?
-// glibc's malloc utilizes a mix of the brk and mmap system calls. This implementation
-// does *not* utilize the brk system call to avoid possible conflicts with foreign C
-// code. Just because we aren't directly using libc, there is nothing stopping the user
-// from doing it.
-
-// What's with all the #no_bounds_check?
-// When memory is returned from mmap, it technically doesn't get written ... well ... anywhere
-// until that region is written to by *you*. So, when a new region is created, we call mmap
-// to get a pointer to some memory, and we claim that memory is a ^Region. Therefor, the
-// region itself is never formally initialized by the compiler as this would result in writing
-// zeros to memory that we can already assume are 0. This would also have the effect of
-// actually commiting this data to memory whether it gets used or not.
-
-
-//
-// Some variables to play with
-//
-
-// Minimum blocks used for any one allocation
-MINIMUM_BLOCK_COUNT :: 2
-
-// Number of extra blocks beyond the requested amount where we would segment.
-// E.g. (blocks) |H0123456| 7 available
-// |H01H0123| Ask for 2, now 4 available
-BLOCK_SEGMENT_THRESHOLD :: 4
-
-// Anything above this threshold will get its own memory map. Since regions
-// are indexed by 16 bit integers, this value should not surpass max(u16) * 6
-DIRECT_MMAP_THRESHOLD_USER :: int(max(u16))
-
-// The point at which we convert direct mmap to region. This should be a decent
-// amount less than DIRECT_MMAP_THRESHOLD to avoid jumping in and out of regions.
-MMAP_TO_REGION_SHRINK_THRESHOLD :: DIRECT_MMAP_THRESHOLD - PAGE_SIZE * 4
-
-// free_list is dynamic and is initialized in the begining of the region memory
-// when the region is initialized. Once resized, it can be moved anywhere.
-FREE_LIST_DEFAULT_CAP :: 32
-
-
-//
-// Other constants that should not be touched
-//
-
-// This universally seems to be 4096 outside of uncommon archs.
-PAGE_SIZE :: 4096
-
-// just rounding up to nearest PAGE_SIZE
-DIRECT_MMAP_THRESHOLD :: (DIRECT_MMAP_THRESHOLD_USER-1) + PAGE_SIZE - (DIRECT_MMAP_THRESHOLD_USER-1) % PAGE_SIZE
-
-// Regions must be big enough to hold DIRECT_MMAP_THRESHOLD - 1 as well
-// as end right on a page boundary as to not waste space.
-SIZE_OF_REGION :: DIRECT_MMAP_THRESHOLD + 4 * int(PAGE_SIZE)
-
-// size of user memory blocks
-BLOCK_SIZE :: size_of(Allocation_Header)
-
-// number of allocation sections (call them blocks) of the region used for allocations
-BLOCKS_PER_REGION :: u16((SIZE_OF_REGION - size_of(Region_Header)) / BLOCK_SIZE)
-
-// minimum amount of space that can used by any individual allocation (includes header)
-MINIMUM_ALLOCATION :: (MINIMUM_BLOCK_COUNT * BLOCK_SIZE) + BLOCK_SIZE
-
-// This is used as a boolean value for Region_Header.local_addr.
-CURRENTLY_ACTIVE :: (^^Region)(~uintptr(0))
-
-FREE_LIST_ENTRIES_PER_BLOCK :: BLOCK_SIZE / size_of(u16)
-
-MMAP_FLAGS : linux.Map_Flags : {.ANONYMOUS, .PRIVATE}
-MMAP_PROT : linux.Mem_Protection : {.READ, .WRITE}
-
-@thread_local _local_region: ^Region
-global_regions: ^Region
-
-
-// There is no way of correctly setting the last bit of free_idx or
-// the last bit of requested, so we can safely use it as a flag to
-// determine if we are interacting with a direct mmap.
-REQUESTED_MASK :: 0x7FFFFFFFFFFFFFFF
-IS_DIRECT_MMAP :: 0x8000000000000000
-
-// Special free_idx value that does not index the free_list.
-NOT_FREE :: 0x7FFF
-Allocation_Header :: struct #raw_union {
- using _: struct {
- // Block indicies
- idx: u16,
- prev: u16,
- next: u16,
- free_idx: u16,
- },
- requested: u64,
-}
-
-Region_Header :: struct #align(16) {
- next_region: ^Region, // points to next region in global_heap (linked list)
- local_addr: ^^Region, // tracks region ownership via address of _local_region
- reset_addr: ^^Region, // tracks old local addr for reset
- free_list: []u16,
- free_list_len: u16,
- free_blocks: u16, // number of free blocks in region (includes headers)
- last_used: u16, // farthest back block that has been used (need zeroing?)
- _reserved: u16,
-}
-
-Region :: struct {
- hdr: Region_Header,
- memory: [BLOCKS_PER_REGION]Allocation_Header,
-}
-
-_heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
- size, alignment: int,
- old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, mem.Allocator_Error) {
- //
- // NOTE(tetra, 2020-01-14): The heap doesn't respect alignment.
- // Instead, we overallocate by `alignment + size_of(rawptr) - 1`, and insert
- // padding. We also store the original pointer returned by heap_alloc right before
- // the pointer we return to the user.
- //
-
- aligned_alloc :: proc(size, alignment: int, old_ptr: rawptr = nil) -> ([]byte, mem.Allocator_Error) {
- a := max(alignment, align_of(rawptr))
- space := size + a - 1
-
- allocated_mem: rawptr
- if old_ptr != nil {
- original_old_ptr := mem.ptr_offset((^rawptr)(old_ptr), -1)^
- allocated_mem = heap_resize(original_old_ptr, space+size_of(rawptr))
- } else {
- allocated_mem = heap_alloc(space+size_of(rawptr))
- }
- aligned_mem := rawptr(mem.ptr_offset((^u8)(allocated_mem), size_of(rawptr)))
-
- ptr := uintptr(aligned_mem)
- aligned_ptr := (ptr - 1 + uintptr(a)) & -uintptr(a)
- diff := int(aligned_ptr - ptr)
- if (size + diff) > space || allocated_mem == nil {
- return nil, .Out_Of_Memory
- }
-
- aligned_mem = rawptr(aligned_ptr)
- mem.ptr_offset((^rawptr)(aligned_mem), -1)^ = allocated_mem
-
- return mem.byte_slice(aligned_mem, size), nil
- }
-
- aligned_free :: proc(p: rawptr) {
- if p != nil {
- heap_free(mem.ptr_offset((^rawptr)(p), -1)^)
- }
- }
-
- aligned_resize :: proc(p: rawptr, old_size: int, new_size: int, new_alignment: int) -> (new_memory: []byte, err: mem.Allocator_Error) {
- if p == nil {
- return nil, nil
- }
-
- return aligned_alloc(new_size, new_alignment, p)
- }
-
- switch mode {
- case .Alloc, .Alloc_Non_Zeroed:
- return aligned_alloc(size, alignment)
-
- case .Free:
- aligned_free(old_memory)
-
- case .Free_All:
- return nil, .Mode_Not_Implemented
-
- case .Resize, .Resize_Non_Zeroed:
- if old_memory == nil {
- return aligned_alloc(size, alignment)
- }
- return aligned_resize(old_memory, old_size, size, alignment)
-
- case .Query_Features:
- set := (^mem.Allocator_Mode_Set)(old_memory)
- if set != nil {
- set^ = {.Alloc, .Free, .Resize, .Query_Features}
- }
- return nil, nil
-
- case .Query_Info:
- return nil, .Mode_Not_Implemented
- }
-
- return nil, nil
-}
-
-heap_alloc :: proc(size: int) -> rawptr {
- if size >= DIRECT_MMAP_THRESHOLD {
- return _direct_mmap_alloc(size)
- }
-
- // atomically check if the local region has been stolen
- if _local_region != nil {
- res := sync.atomic_compare_exchange_strong_explicit(
- &_local_region.hdr.local_addr,
- &_local_region,
- CURRENTLY_ACTIVE,
- .Acquire,
- .Relaxed,
- )
- if res != &_local_region {
- // At this point, the region has been stolen and res contains the unexpected value
- expected := res
- if res != CURRENTLY_ACTIVE {
- expected = res
- res = sync.atomic_compare_exchange_strong_explicit(
- &_local_region.hdr.local_addr,
- expected,
- CURRENTLY_ACTIVE,
- .Acquire,
- .Relaxed,
- )
- }
- if res != expected {
- _local_region = nil
- }
- }
- }
-
- size := size
- size = _round_up_to_nearest(size, BLOCK_SIZE)
- blocks_needed := u16(max(MINIMUM_BLOCK_COUNT, size / BLOCK_SIZE))
-
- // retrieve a region if new thread or stolen
- if _local_region == nil {
- _local_region, _ = _region_retrieve_with_space(blocks_needed)
- if _local_region == nil {
- return nil
- }
- }
- defer sync.atomic_store_explicit(&_local_region.hdr.local_addr, &_local_region, .Release)
-
- // At this point we have a usable region. Let's find the user some memory
- idx: u16
- local_region_idx := _region_get_local_idx()
- back_idx := -1
- infinite: for {
- for i := 0; i < int(_local_region.hdr.free_list_len); i += 1 {
- idx = _local_region.hdr.free_list[i]
- #no_bounds_check if _get_block_count(_local_region.memory[idx]) >= blocks_needed {
- break infinite
- }
- }
- sync.atomic_store_explicit(&_local_region.hdr.local_addr, &_local_region, .Release)
- _local_region, back_idx = _region_retrieve_with_space(blocks_needed, local_region_idx, back_idx)
- }
- user_ptr, used := _region_get_block(_local_region, idx, blocks_needed)
-
- sync.atomic_sub_explicit(&_local_region.hdr.free_blocks, used + 1, .Release)
-
- // If this memory was ever used before, it now needs to be zero'd.
- if idx < _local_region.hdr.last_used {
- mem.zero(user_ptr, int(used) * BLOCK_SIZE)
- } else {
- _local_region.hdr.last_used = idx + used
- }
-
- return user_ptr
-}
-
-heap_resize :: proc(old_memory: rawptr, new_size: int) -> rawptr #no_bounds_check {
- alloc := _get_allocation_header(old_memory)
- if alloc.requested & IS_DIRECT_MMAP > 0 {
- return _direct_mmap_resize(alloc, new_size)
- }
-
- if new_size > DIRECT_MMAP_THRESHOLD {
- return _direct_mmap_from_region(alloc, new_size)
- }
-
- return _region_resize(alloc, new_size)
-}
-
-heap_free :: proc(memory: rawptr) {
- alloc := _get_allocation_header(memory)
- if sync.atomic_load(&alloc.requested) & IS_DIRECT_MMAP == IS_DIRECT_MMAP {
- _direct_mmap_free(alloc)
- return
- }
-
- assert(alloc.free_idx == NOT_FREE)
-
- _region_find_and_assign_local(alloc)
- _region_local_free(alloc)
- sync.atomic_store_explicit(&_local_region.hdr.local_addr, &_local_region, .Release)
-}
-
-//
-// Regions
-//
-_new_region :: proc() -> ^Region #no_bounds_check {
- ptr, errno := linux.mmap(0, uint(SIZE_OF_REGION), MMAP_PROT, MMAP_FLAGS, -1, 0)
- if errno != .NONE {
- return nil
- }
- new_region := (^Region)(ptr)
-
- new_region.hdr.local_addr = CURRENTLY_ACTIVE
- new_region.hdr.reset_addr = &_local_region
-
- free_list_blocks := _round_up_to_nearest(FREE_LIST_DEFAULT_CAP, FREE_LIST_ENTRIES_PER_BLOCK)
- _region_assign_free_list(new_region, &new_region.memory[1], u16(free_list_blocks) * FREE_LIST_ENTRIES_PER_BLOCK)
-
- // + 2 to account for free_list's allocation header
- first_user_block := len(new_region.hdr.free_list) / FREE_LIST_ENTRIES_PER_BLOCK + 2
-
- // first allocation header (this is a free list)
- new_region.memory[0].next = u16(first_user_block)
- new_region.memory[0].free_idx = NOT_FREE
- new_region.memory[first_user_block].idx = u16(first_user_block)
- new_region.memory[first_user_block].next = BLOCKS_PER_REGION - 1
-
- // add the first user block to the free list
- new_region.hdr.free_list[0] = u16(first_user_block)
- new_region.hdr.free_list_len = 1
- new_region.hdr.free_blocks = _get_block_count(new_region.memory[first_user_block]) + 1
-
- for r := sync.atomic_compare_exchange_strong(&global_regions, nil, new_region);
- r != nil;
- r = sync.atomic_compare_exchange_strong(&r.hdr.next_region, nil, new_region) {}
-
- return new_region
-}
-
-_region_resize :: proc(alloc: ^Allocation_Header, new_size: int, alloc_is_free_list: bool = false) -> rawptr #no_bounds_check {
- assert(alloc.free_idx == NOT_FREE)
-
- old_memory := mem.ptr_offset(alloc, 1)
-
- old_block_count := _get_block_count(alloc^)
- new_block_count := u16(
- max(MINIMUM_BLOCK_COUNT, _round_up_to_nearest(new_size, BLOCK_SIZE) / BLOCK_SIZE),
- )
- if new_block_count < old_block_count {
- if new_block_count - old_block_count >= MINIMUM_BLOCK_COUNT {
- _region_find_and_assign_local(alloc)
- _region_segment(_local_region, alloc, new_block_count, alloc.free_idx)
- new_block_count = _get_block_count(alloc^)
- sync.atomic_store_explicit(&_local_region.hdr.local_addr, &_local_region, .Release)
- }
- // need to zero anything within the new block that that lies beyond new_size
- extra_bytes := int(new_block_count * BLOCK_SIZE) - new_size
- extra_bytes_ptr := mem.ptr_offset((^u8)(alloc), new_size + BLOCK_SIZE)
- mem.zero(extra_bytes_ptr, extra_bytes)
- return old_memory
- }
-
- if !alloc_is_free_list {
- _region_find_and_assign_local(alloc)
- }
- defer if !alloc_is_free_list {
- sync.atomic_store_explicit(&_local_region.hdr.local_addr, &_local_region, .Release)
- }
-
- // First, let's see if we can grow in place.
- if alloc.next != BLOCKS_PER_REGION - 1 && _local_region.memory[alloc.next].free_idx != NOT_FREE {
- next_alloc := _local_region.memory[alloc.next]
- total_available := old_block_count + _get_block_count(next_alloc) + 1
- if total_available >= new_block_count {
- alloc.next = next_alloc.next
- _local_region.memory[alloc.next].prev = alloc.idx
- if total_available - new_block_count > BLOCK_SEGMENT_THRESHOLD {
- _region_segment(_local_region, alloc, new_block_count, next_alloc.free_idx)
- } else {
- _region_free_list_remove(_local_region, next_alloc.free_idx)
- }
- mem.zero(&_local_region.memory[next_alloc.idx], int(alloc.next - next_alloc.idx) * BLOCK_SIZE)
- _local_region.hdr.last_used = max(alloc.next, _local_region.hdr.last_used)
- _local_region.hdr.free_blocks -= (_get_block_count(alloc^) - old_block_count)
- if alloc_is_free_list {
- _region_assign_free_list(_local_region, old_memory, _get_block_count(alloc^))
- }
- return old_memory
- }
- }
-
- // If we made it this far, we need to resize, copy, zero and free.
- region_iter := _local_region
- local_region_idx := _region_get_local_idx()
- back_idx := -1
- idx: u16
- infinite: for {
- for i := 0; i < len(region_iter.hdr.free_list); i += 1 {
- idx = region_iter.hdr.free_list[i]
- if _get_block_count(region_iter.memory[idx]) >= new_block_count {
- break infinite
- }
- }
- if region_iter != _local_region {
- sync.atomic_store_explicit(
- &region_iter.hdr.local_addr,
- region_iter.hdr.reset_addr,
- .Release,
- )
- }
- region_iter, back_idx = _region_retrieve_with_space(new_block_count, local_region_idx, back_idx)
- }
- if region_iter != _local_region {
- sync.atomic_store_explicit(
- &region_iter.hdr.local_addr,
- region_iter.hdr.reset_addr,
- .Release,
- )
- }
-
- // copy from old memory
- new_memory, used_blocks := _region_get_block(region_iter, idx, new_block_count)
- mem.copy(new_memory, old_memory, int(old_block_count * BLOCK_SIZE))
-
- // zero any new memory
- addon_section := mem.ptr_offset((^Allocation_Header)(new_memory), old_block_count)
- new_blocks := used_blocks - old_block_count
- mem.zero(addon_section, int(new_blocks) * BLOCK_SIZE)
-
- region_iter.hdr.free_blocks -= (used_blocks + 1)
-
- // Set free_list before freeing.
- if alloc_is_free_list {
- _region_assign_free_list(_local_region, new_memory, used_blocks)
- }
-
- // free old memory
- _region_local_free(alloc)
- return new_memory
-}
-
-_region_local_free :: proc(alloc: ^Allocation_Header) #no_bounds_check {
- alloc := alloc
- add_to_free_list := true
-
- idx := sync.atomic_load(&alloc.idx)
- prev := sync.atomic_load(&alloc.prev)
- next := sync.atomic_load(&alloc.next)
- block_count := next - idx - 1
- free_blocks := sync.atomic_load(&_local_region.hdr.free_blocks) + block_count + 1
- sync.atomic_store_explicit(&_local_region.hdr.free_blocks, free_blocks, .Release)
-
- // try to merge with prev
- if idx > 0 && sync.atomic_load(&_local_region.memory[prev].free_idx) != NOT_FREE {
- sync.atomic_store_explicit(&_local_region.memory[prev].next, next, .Release)
- _local_region.memory[next].prev = prev
- alloc = &_local_region.memory[prev]
- add_to_free_list = false
- }
-
- // try to merge with next
- if next < BLOCKS_PER_REGION - 1 && sync.atomic_load(&_local_region.memory[next].free_idx) != NOT_FREE {
- old_next := next
- sync.atomic_store_explicit(&alloc.next, sync.atomic_load(&_local_region.memory[old_next].next), .Release)
-
- sync.atomic_store_explicit(&_local_region.memory[next].prev, idx, .Release)
-
- if add_to_free_list {
- sync.atomic_store_explicit(&_local_region.hdr.free_list[_local_region.memory[old_next].free_idx], idx, .Release)
- sync.atomic_store_explicit(&alloc.free_idx, _local_region.memory[old_next].free_idx, .Release)
- } else {
- // NOTE: We have aleady merged with prev, and now merged with next.
- // Now, we are actually going to remove from the free_list.
- _region_free_list_remove(_local_region, _local_region.memory[old_next].free_idx)
- }
- add_to_free_list = false
- }
-
- // This is the only place where anything is appended to the free list.
- if add_to_free_list {
- fl := _local_region.hdr.free_list
- fl_len := sync.atomic_load(&_local_region.hdr.free_list_len)
- sync.atomic_store_explicit(&alloc.free_idx, fl_len, .Release)
- fl[alloc.free_idx] = idx
- sync.atomic_store_explicit(&_local_region.hdr.free_list_len, fl_len + 1, .Release)
- if int(fl_len + 1) == len(fl) {
- free_alloc := _get_allocation_header(mem.raw_data(_local_region.hdr.free_list))
- _region_resize(free_alloc, len(fl) * 2 * size_of(fl[0]), true)
- }
- }
-}
-
-_region_assign_free_list :: proc(region: ^Region, memory: rawptr, blocks: u16) {
- raw_free_list := transmute(mem.Raw_Slice)region.hdr.free_list
- raw_free_list.len = int(blocks) * FREE_LIST_ENTRIES_PER_BLOCK
- raw_free_list.data = memory
- region.hdr.free_list = transmute([]u16)(raw_free_list)
-}
-
-_region_retrieve_with_space :: proc(blocks: u16, local_idx: int = -1, back_idx: int = -1) -> (^Region, int) {
- r: ^Region
- idx: int
- for r = sync.atomic_load(&global_regions); r != nil; r = r.hdr.next_region {
- if idx == local_idx || idx < back_idx || sync.atomic_load(&r.hdr.free_blocks) < blocks {
- idx += 1
- continue
- }
- idx += 1
- local_addr: ^^Region = sync.atomic_load(&r.hdr.local_addr)
- if local_addr != CURRENTLY_ACTIVE {
- res := sync.atomic_compare_exchange_strong_explicit(
- &r.hdr.local_addr,
- local_addr,
- CURRENTLY_ACTIVE,
- .Acquire,
- .Relaxed,
- )
- if res == local_addr {
- r.hdr.reset_addr = local_addr
- return r, idx
- }
- }
- }
-
- return _new_region(), idx
-}
-
-_region_retrieve_from_addr :: proc(addr: rawptr) -> ^Region {
- r: ^Region
- for r = global_regions; r != nil; r = r.hdr.next_region {
- if _region_contains_mem(r, addr) {
- return r
- }
- }
- unreachable()
-}
-
-_region_get_block :: proc(region: ^Region, idx, blocks_needed: u16) -> (rawptr, u16) #no_bounds_check {
- alloc := &region.memory[idx]
-
- assert(alloc.free_idx != NOT_FREE)
- assert(alloc.next > 0)
-
- block_count := _get_block_count(alloc^)
- if block_count - blocks_needed > BLOCK_SEGMENT_THRESHOLD {
- _region_segment(region, alloc, blocks_needed, alloc.free_idx)
- } else {
- _region_free_list_remove(region, alloc.free_idx)
- }
-
- alloc.free_idx = NOT_FREE
- return mem.ptr_offset(alloc, 1), _get_block_count(alloc^)
-}
-
-_region_segment :: proc(region: ^Region, alloc: ^Allocation_Header, blocks, new_free_idx: u16) #no_bounds_check {
- old_next := alloc.next
- alloc.next = alloc.idx + blocks + 1
- region.memory[old_next].prev = alloc.next
-
- // Initialize alloc.next allocation header here.
- region.memory[alloc.next].prev = alloc.idx
- region.memory[alloc.next].next = old_next
- region.memory[alloc.next].idx = alloc.next
- region.memory[alloc.next].free_idx = new_free_idx
-
- // Replace our original spot in the free_list with new segment.
- region.hdr.free_list[new_free_idx] = alloc.next
-}
-
-_region_get_local_idx :: proc() -> int {
- idx: int
- for r := sync.atomic_load(&global_regions); r != nil; r = r.hdr.next_region {
- if r == _local_region {
- return idx
- }
- idx += 1
- }
-
- return -1
-}
-
-_region_find_and_assign_local :: proc(alloc: ^Allocation_Header) {
- // Find the region that contains this memory
- if !_region_contains_mem(_local_region, alloc) {
- _local_region = _region_retrieve_from_addr(alloc)
- }
-
- // At this point, _local_region is set correctly. Spin until acquire
- res := CURRENTLY_ACTIVE
-
- for res == CURRENTLY_ACTIVE {
- res = sync.atomic_compare_exchange_strong_explicit(
- &_local_region.hdr.local_addr,
- &_local_region,
- CURRENTLY_ACTIVE,
- .Acquire,
- .Relaxed,
- )
- }
-}
-
-_region_contains_mem :: proc(r: ^Region, memory: rawptr) -> bool #no_bounds_check {
- if r == nil {
- return false
- }
- mem_int := uintptr(memory)
- return mem_int >= uintptr(&r.memory[0]) && mem_int <= uintptr(&r.memory[BLOCKS_PER_REGION - 1])
-}
-
-_region_free_list_remove :: proc(region: ^Region, free_idx: u16) #no_bounds_check {
- // pop, swap and update allocation hdr
- if n := region.hdr.free_list_len - 1; free_idx != n {
- region.hdr.free_list[free_idx] = sync.atomic_load(&region.hdr.free_list[n])
- alloc_idx := region.hdr.free_list[free_idx]
- sync.atomic_store_explicit(&region.memory[alloc_idx].free_idx, free_idx, .Release)
- }
- region.hdr.free_list_len -= 1
-}
-
-//
-// Direct mmap
-//
-_direct_mmap_alloc :: proc(size: int) -> rawptr {
- mmap_size := _round_up_to_nearest(size + BLOCK_SIZE, PAGE_SIZE)
- new_allocation, errno := linux.mmap(0, uint(mmap_size), MMAP_PROT, MMAP_FLAGS, -1, 0)
- if errno != .NONE {
- return nil
- }
-
- alloc := (^Allocation_Header)(uintptr(new_allocation))
- alloc.requested = u64(size) // NOTE: requested = requested size
- alloc.requested += IS_DIRECT_MMAP
- return rawptr(mem.ptr_offset(alloc, 1))
-}
-
-_direct_mmap_resize :: proc(alloc: ^Allocation_Header, new_size: int) -> rawptr {
- old_requested := int(alloc.requested & REQUESTED_MASK)
- old_mmap_size := _round_up_to_nearest(old_requested + BLOCK_SIZE, PAGE_SIZE)
- new_mmap_size := _round_up_to_nearest(new_size + BLOCK_SIZE, PAGE_SIZE)
- if int(new_mmap_size) < MMAP_TO_REGION_SHRINK_THRESHOLD {
- return _direct_mmap_to_region(alloc, new_size)
- } else if old_requested == new_size {
- return mem.ptr_offset(alloc, 1)
- }
-
- new_allocation, errno := linux.mremap(alloc, uint(old_mmap_size), uint(new_mmap_size), {.MAYMOVE})
- if errno != .NONE {
- return nil
- }
-
- new_header := (^Allocation_Header)(uintptr(new_allocation))
- new_header.requested = u64(new_size)
- new_header.requested += IS_DIRECT_MMAP
-
- if new_mmap_size > old_mmap_size {
- // new section may not be pointer aligned, so cast to ^u8
- new_section := mem.ptr_offset((^u8)(new_header), old_requested + BLOCK_SIZE)
- mem.zero(new_section, new_mmap_size - old_mmap_size)
- }
- return mem.ptr_offset(new_header, 1)
-
-}
-
-_direct_mmap_from_region :: proc(alloc: ^Allocation_Header, new_size: int) -> rawptr {
- new_memory := _direct_mmap_alloc(new_size)
- if new_memory != nil {
- old_memory := mem.ptr_offset(alloc, 1)
- mem.copy(new_memory, old_memory, int(_get_block_count(alloc^)) * BLOCK_SIZE)
- }
- _region_find_and_assign_local(alloc)
- _region_local_free(alloc)
- sync.atomic_store_explicit(&_local_region.hdr.local_addr, &_local_region, .Release)
- return new_memory
-}
-
-_direct_mmap_to_region :: proc(alloc: ^Allocation_Header, new_size: int) -> rawptr {
- new_memory := heap_alloc(new_size)
- if new_memory != nil {
- mem.copy(new_memory, mem.ptr_offset(alloc, -1), new_size)
- _direct_mmap_free(alloc)
- }
- return new_memory
-}
-
-_direct_mmap_free :: proc(alloc: ^Allocation_Header) {
- requested := int(alloc.requested & REQUESTED_MASK)
- mmap_size := _round_up_to_nearest(requested + BLOCK_SIZE, PAGE_SIZE)
- linux.munmap(alloc, uint(mmap_size))
-}
-
-//
-// Util
-//
-
-_get_block_count :: #force_inline proc(alloc: Allocation_Header) -> u16 {
- return alloc.next - alloc.idx - 1
-}
-
-_get_allocation_header :: #force_inline proc(raw_mem: rawptr) -> ^Allocation_Header {
- return mem.ptr_offset((^Allocation_Header)(raw_mem), -1)
-}
-
-_round_up_to_nearest :: #force_inline proc(size, round: int) -> int {
- return (size-1) + round - (size-1) % round
-}
+import "base:runtime"
+_heap_allocator_proc :: runtime.heap_allocator_proc
diff --git a/core/os/os2/heap_wasi.odin b/core/os/os2/heap_wasi.odin
new file mode 100644
index 000000000..7da3c4845
--- /dev/null
+++ b/core/os/os2/heap_wasi.odin
@@ -0,0 +1,6 @@
+#+private
+package os2
+
+import "base:runtime"
+
+_heap_allocator_proc :: runtime.wasm_allocator_proc
diff --git a/core/os/os2/path.odin b/core/os/os2/path.odin
index 254950d68..9231307f5 100644
--- a/core/os/os2/path.odin
+++ b/core/os/os2/path.odin
@@ -2,6 +2,8 @@ package os2
import "base:runtime"
+import "core:path/filepath"
+
Path_Separator :: _Path_Separator // OS-Specific
Path_Separator_String :: _Path_Separator_String // OS-Specific
Path_List_Separator :: _Path_List_Separator // OS-Specific
@@ -39,3 +41,13 @@ setwd :: set_working_directory
set_working_directory :: proc(dir: string) -> (err: Error) {
return _set_working_directory(dir)
}
+
+get_executable_path :: proc(allocator: runtime.Allocator) -> (path: string, err: Error) {
+ return _get_executable_path(allocator)
+}
+
+get_executable_directory :: proc(allocator: runtime.Allocator) -> (path: string, err: Error) {
+ path = _get_executable_path(allocator) or_return
+ path, _ = filepath.split(path)
+ return
+}
diff --git a/core/os/os2/path_darwin.odin b/core/os/os2/path_darwin.odin
new file mode 100644
index 000000000..65aaf1e95
--- /dev/null
+++ b/core/os/os2/path_darwin.odin
@@ -0,0 +1,17 @@
+package os2
+
+import "base:runtime"
+
+import "core:sys/darwin"
+import "core:sys/posix"
+
+_get_executable_path :: proc(allocator: runtime.Allocator) -> (path: string, err: Error) {
+ buffer: [darwin.PIDPATHINFO_MAXSIZE]byte = ---
+ ret := darwin.proc_pidpath(posix.getpid(), raw_data(buffer[:]), len(buffer))
+ if ret > 0 {
+ return clone_string(string(buffer[:ret]), allocator)
+ }
+
+ err = _get_platform_error()
+ return
+}
diff --git a/core/os/os2/path_freebsd.odin b/core/os/os2/path_freebsd.odin
new file mode 100644
index 000000000..577108eca
--- /dev/null
+++ b/core/os/os2/path_freebsd.odin
@@ -0,0 +1,29 @@
+package os2
+
+import "base:runtime"
+
+import "core:sys/freebsd"
+import "core:sys/posix"
+
+_get_executable_path :: proc(allocator: runtime.Allocator) -> (path: string, err: Error) {
+ req := []freebsd.MIB_Identifier{.CTL_KERN, .KERN_PROC, .KERN_PROC_PATHNAME, freebsd.MIB_Identifier(-1)}
+
+ size: uint
+ if ret := freebsd.sysctl(req, nil, &size, nil, 0); ret != .NONE {
+ err = _get_platform_error(posix.Errno(ret))
+ return
+ }
+ assert(size > 0)
+
+ buf := make([]byte, size, allocator) or_return
+ defer if err != nil { delete(buf, allocator) }
+
+ assert(uint(len(buf)) == size)
+
+ if ret := freebsd.sysctl(req, raw_data(buf), &size, nil, 0); ret != .NONE {
+ err = _get_platform_error(posix.Errno(ret))
+ return
+ }
+
+ return string(buf[:size]), nil
+}
diff --git a/core/os/os2/path_linux.odin b/core/os/os2/path_linux.odin
index 7be4121ae..e3e7f8a7c 100644
--- a/core/os/os2/path_linux.odin
+++ b/core/os/os2/path_linux.odin
@@ -1,9 +1,10 @@
#+private
package os2
+import "base:runtime"
+
import "core:strings"
import "core:strconv"
-import "base:runtime"
import "core:sys/linux"
_Path_Separator :: '/'
@@ -77,8 +78,6 @@ _mkdir_all :: proc(path: string, perm: int) -> Error {
}
_remove_all :: proc(path: string) -> Error {
- DT_DIR :: 4
-
remove_all_dir :: proc(dfd: linux.Fd) -> Error {
n := 64
buf := make([]u8, n)
@@ -173,6 +172,25 @@ _set_working_directory :: proc(dir: string) -> Error {
return _get_platform_error(linux.chdir(dir_cstr))
}
+_get_executable_path :: proc(allocator: runtime.Allocator) -> (path: string, err: Error) {
+ TEMP_ALLOCATOR_GUARD()
+
+ buf := make([dynamic]byte, 1024, temp_allocator()) or_return
+ for {
+ n, errno := linux.readlink("/proc/self/exe", buf[:])
+ if errno != .NONE {
+ err = _get_platform_error(errno)
+ return
+ }
+
+ if n < len(buf) {
+ return clone_string(string(buf[:n]), allocator)
+ }
+
+ resize(&buf, len(buf)*2) or_return
+ }
+}
+
_get_full_path :: proc(fd: linux.Fd, allocator: runtime.Allocator) -> (fullpath: string, err: Error) {
PROC_FD_PATH :: "/proc/self/fd/"
diff --git a/core/os/os2/path_netbsd.odin b/core/os/os2/path_netbsd.odin
new file mode 100644
index 000000000..f56a91fd6
--- /dev/null
+++ b/core/os/os2/path_netbsd.odin
@@ -0,0 +1,24 @@
+package os2
+
+import "base:runtime"
+
+import "core:sys/posix"
+
+_get_executable_path :: proc(allocator: runtime.Allocator) -> (path: string, err: Error) {
+ TEMP_ALLOCATOR_GUARD()
+
+ buf := make([dynamic]byte, 1024, temp_allocator()) or_return
+ for {
+ n := posix.readlink("/proc/curproc/exe", raw_data(buf), len(buf))
+ if n < 0 {
+ err = _get_platform_error()
+ return
+ }
+
+ if n < len(buf) {
+ return clone_string(string(buf[:n]), allocator)
+ }
+
+ resize(&buf, len(buf)*2) or_return
+ }
+}
diff --git a/core/os/os2/path_openbsd.odin b/core/os/os2/path_openbsd.odin
new file mode 100644
index 000000000..37b5de927
--- /dev/null
+++ b/core/os/os2/path_openbsd.odin
@@ -0,0 +1,57 @@
+package os2
+
+import "base:runtime"
+
+import "core:strings"
+import "core:sys/posix"
+
+_get_executable_path :: proc(allocator: runtime.Allocator) -> (path: string, err: Error) {
+ // OpenBSD does not have an API for this, we do our best below.
+
+ if len(runtime.args__) <= 0 {
+ err = .Invalid_Path
+ return
+ }
+
+ real :: proc(path: cstring, allocator: runtime.Allocator) -> (out: string, err: Error) {
+ real := posix.realpath(path)
+ if real == nil {
+ err = _get_platform_error()
+ return
+ }
+ defer posix.free(real)
+ return clone_string(string(real), allocator)
+ }
+
+ arg := runtime.args__[0]
+ sarg := string(arg)
+
+ if len(sarg) == 0 {
+ err = .Invalid_Path
+ return
+ }
+
+ if sarg[0] == '.' || sarg[0] == '/' {
+ return real(arg, allocator)
+ }
+
+ TEMP_ALLOCATOR_GUARD()
+
+ buf := strings.builder_make(temp_allocator())
+
+ paths := get_env("PATH", temp_allocator())
+ for dir in strings.split_iterator(&paths, ":") {
+ strings.builder_reset(&buf)
+ strings.write_string(&buf, dir)
+ strings.write_string(&buf, "/")
+ strings.write_string(&buf, sarg)
+
+ cpath := strings.to_cstring(&buf) or_return
+ if posix.access(cpath, {.X_OK}) == .OK {
+ return real(cpath, allocator)
+ }
+ }
+
+ err = .Invalid_Path
+ return
+}
diff --git a/core/os/os2/path_posix.odin b/core/os/os2/path_posix.odin
index 5ffdac28e..e6b95c0d4 100644
--- a/core/os/os2/path_posix.odin
+++ b/core/os/os2/path_posix.odin
@@ -81,7 +81,7 @@ _remove_all :: proc(path: string) -> Error {
fullpath, _ := concatenate({path, "/", string(cname), "\x00"}, temp_allocator())
if entry.d_type == .DIR {
- _remove_all(fullpath[:len(fullpath)-1])
+ _remove_all(fullpath[:len(fullpath)-1]) or_return
} else {
if posix.unlink(cstring(raw_data(fullpath))) != .OK {
return _get_platform_error()
diff --git a/core/os/os2/path_wasi.odin b/core/os/os2/path_wasi.odin
new file mode 100644
index 000000000..1c4fafa17
--- /dev/null
+++ b/core/os/os2/path_wasi.odin
@@ -0,0 +1,117 @@
+#+private
+package os2
+
+import "base:runtime"
+
+import "core:path/filepath"
+import "core:sync"
+import "core:sys/wasm/wasi"
+
+_Path_Separator :: '/'
+_Path_Separator_String :: "/"
+_Path_List_Separator :: ':'
+
+_is_path_separator :: proc(c: byte) -> bool {
+ return c == _Path_Separator
+}
+
+_mkdir :: proc(name: string, perm: int) -> Error {
+ dir_fd, relative, ok := match_preopen(name)
+ if !ok {
+ return .Invalid_Path
+ }
+
+ return _get_platform_error(wasi.path_create_directory(dir_fd, relative))
+}
+
+_mkdir_all :: proc(path: string, perm: int) -> Error {
+ if path == "" {
+ return .Invalid_Path
+ }
+
+ TEMP_ALLOCATOR_GUARD()
+
+ if exists(path) {
+ return .Exist
+ }
+
+ clean_path := filepath.clean(path, temp_allocator())
+ return internal_mkdir_all(clean_path)
+
+ internal_mkdir_all :: proc(path: string) -> Error {
+ dir, file := filepath.split(path)
+ if file != path && dir != "/" {
+ if len(dir) > 1 && dir[len(dir) - 1] == '/' {
+ dir = dir[:len(dir) - 1]
+ }
+ internal_mkdir_all(dir) or_return
+ }
+
+ err := _mkdir(path, 0)
+ if err == .Exist { err = nil }
+ return err
+ }
+}
+
+_remove_all :: proc(path: string) -> (err: Error) {
+ // PERF: this works, but wastes a bunch of memory using the read_directory_iterator API
+ // and using open instead of wasi fds directly.
+ {
+ dir := open(path) or_return
+ defer close(dir)
+
+ iter := read_directory_iterator_create(dir)
+ defer read_directory_iterator_destroy(&iter)
+
+ for fi in read_directory_iterator(&iter) {
+ _ = read_directory_iterator_error(&iter) or_break
+
+ if fi.type == .Directory {
+ _remove_all(fi.fullpath) or_return
+ } else {
+ remove(fi.fullpath) or_return
+ }
+ }
+
+ _ = read_directory_iterator_error(&iter) or_return
+ }
+
+ return remove(path)
+}
+
+g_wd: string
+g_wd_mutex: sync.Mutex
+
+_get_working_directory :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
+ sync.guard(&g_wd_mutex)
+
+ return clone_string(g_wd if g_wd != "" else "/", allocator)
+}
+
+_set_working_directory :: proc(dir: string) -> (err: Error) {
+ sync.guard(&g_wd_mutex)
+
+ if dir == g_wd {
+ return
+ }
+
+ if g_wd != "" {
+ delete(g_wd, file_allocator())
+ }
+
+ g_wd = clone_string(dir, file_allocator()) or_return
+ return
+}
+
+_get_executable_path :: proc(allocator: runtime.Allocator) -> (path: string, err: Error) {
+ if len(args) <= 0 {
+ return clone_string("/", allocator)
+ }
+
+ arg := args[0]
+ if len(arg) > 0 && (arg[0] == '.' || arg[0] == '/') {
+ return clone_string(arg, allocator)
+ }
+
+ return concatenate({"/", arg}, allocator)
+}
diff --git a/core/os/os2/path_windows.odin b/core/os/os2/path_windows.odin
index 3e92cb6f3..041a4d1e3 100644
--- a/core/os/os2/path_windows.odin
+++ b/core/os/os2/path_windows.odin
@@ -136,6 +136,26 @@ _set_working_directory :: proc(dir: string) -> (err: Error) {
return
}
+_get_executable_path :: proc(allocator: runtime.Allocator) -> (path: string, err: Error) {
+ TEMP_ALLOCATOR_GUARD()
+
+ buf := make([dynamic]u16, 512, temp_allocator()) or_return
+ for {
+ ret := win32.GetModuleFileNameW(nil, raw_data(buf), win32.DWORD(len(buf)))
+ if ret == 0 {
+ err = _get_platform_error()
+ return
+ }
+
+ if ret == win32.DWORD(len(buf)) && win32.GetLastError() == win32.ERROR_INSUFFICIENT_BUFFER {
+ resize(&buf, len(buf)*2) or_return
+ continue
+ }
+
+ return win32_utf16_to_utf8(buf[:ret], allocator)
+ }
+}
+
can_use_long_paths: bool
@(init)
diff --git a/core/os/os2/pipe_linux.odin b/core/os/os2/pipe_linux.odin
index 852674c69..bb4456e1c 100644
--- a/core/os/os2/pipe_linux.odin
+++ b/core/os/os2/pipe_linux.odin
@@ -10,8 +10,8 @@ _pipe :: proc() -> (r, w: ^File, err: Error) {
return nil, nil,_get_platform_error(errno)
}
- r = _new_file(uintptr(fds[0])) or_return
- w = _new_file(uintptr(fds[1])) or_return
+ r = _new_file(uintptr(fds[0]), "", file_allocator()) or_return
+ w = _new_file(uintptr(fds[1]), "", file_allocator()) or_return
return
}
diff --git a/core/os/os2/pipe_posix.odin b/core/os/os2/pipe_posix.odin
index df9425339..7c07bc068 100644
--- a/core/os/os2/pipe_posix.odin
+++ b/core/os/os2/pipe_posix.odin
@@ -21,7 +21,7 @@ _pipe :: proc() -> (r, w: ^File, err: Error) {
return
}
- r = __new_file(fds[0])
+ r = __new_file(fds[0], file_allocator())
ri := (^File_Impl)(r.impl)
rname := strings.builder_make(file_allocator())
@@ -29,9 +29,9 @@ _pipe :: proc() -> (r, w: ^File, err: Error) {
strings.write_string(&rname, "/dev/fd/")
strings.write_int(&rname, int(fds[0]))
ri.name = strings.to_string(rname)
- ri.cname = strings.to_cstring(&rname)
+ ri.cname = strings.to_cstring(&rname) or_return
- w = __new_file(fds[1])
+ w = __new_file(fds[1], file_allocator())
wi := (^File_Impl)(w.impl)
wname := strings.builder_make(file_allocator())
@@ -39,7 +39,7 @@ _pipe :: proc() -> (r, w: ^File, err: Error) {
strings.write_string(&wname, "/dev/fd/")
strings.write_int(&wname, int(fds[1]))
wi.name = strings.to_string(wname)
- wi.cname = strings.to_cstring(&wname)
+ wi.cname = strings.to_cstring(&wname) or_return
return
}
diff --git a/core/os/os2/pipe_wasi.odin b/core/os/os2/pipe_wasi.odin
new file mode 100644
index 000000000..19c11b51d
--- /dev/null
+++ b/core/os/os2/pipe_wasi.odin
@@ -0,0 +1,13 @@
+#+private
+package os2
+
+_pipe :: proc() -> (r, w: ^File, err: Error) {
+ err = .Unsupported
+ return
+}
+
+@(require_results)
+_pipe_has_data :: proc(r: ^File) -> (ok: bool, err: Error) {
+ err = .Unsupported
+ return
+}
diff --git a/core/os/os2/process.odin b/core/os/os2/process.odin
index 5b5a6e844..c90e3add2 100644
--- a/core/os/os2/process.odin
+++ b/core/os/os2/process.odin
@@ -290,12 +290,21 @@ process_open :: proc(pid: int, flags := Process_Open_Flags {}) -> (Process, Erro
return _process_open(pid, flags)
}
+
+/*
+OS-specific process attributes.
+*/
+Process_Attributes :: struct {
+ sys_attr: _Sys_Process_Attributes,
+}
+
/*
The description of how a process should be created.
*/
Process_Desc :: struct {
// OS-specific attributes.
- sys_attr: _Sys_Process_Attributes,
+ sys_attr: Process_Attributes,
+
// The working directory of the process. If the string has length 0, the
// working directory is assumed to be the current working directory of the
// current process.
diff --git a/core/os/os2/process_linux.odin b/core/os/os2/process_linux.odin
index 7eb4dfa44..632bde6ba 100644
--- a/core/os/os2/process_linux.odin
+++ b/core/os/os2/process_linux.odin
@@ -111,7 +111,7 @@ _process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator
strings.write_string(&path_builder, "/proc/")
strings.write_int(&path_builder, pid)
- proc_fd, errno := linux.open(strings.to_cstring(&path_builder), _OPENDIR_FLAGS)
+ proc_fd, errno := linux.open(strings.to_cstring(&path_builder) or_return, _OPENDIR_FLAGS)
if errno != .NONE {
err = _get_platform_error(errno)
return
@@ -169,7 +169,7 @@ _process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator
strings.write_int(&path_builder, pid)
strings.write_string(&path_builder, "/cmdline")
- cmdline_bytes, cmdline_err := _read_entire_pseudo_file(strings.to_cstring(&path_builder), temp_allocator())
+ cmdline_bytes, cmdline_err := _read_entire_pseudo_file(strings.to_cstring(&path_builder) or_return, temp_allocator())
if cmdline_err != nil || len(cmdline_bytes) == 0 {
err = cmdline_err
break cmdline_if
@@ -190,7 +190,7 @@ _process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator
strings.write_int(&path_builder, pid)
strings.write_string(&path_builder, "/cwd")
- cwd, cwd_err = _read_link_cstr(strings.to_cstring(&path_builder), temp_allocator()) // allowed to fail
+ cwd, cwd_err = _read_link_cstr(strings.to_cstring(&path_builder) or_return, temp_allocator()) // allowed to fail
if cwd_err == nil && .Working_Dir in selection {
info.working_dir = strings.clone(cwd, allocator) or_return
info.fields += {.Working_Dir}
@@ -258,7 +258,7 @@ _process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator
strings.write_int(&path_builder, pid)
strings.write_string(&path_builder, "/stat")
- proc_stat_bytes, stat_err := _read_entire_pseudo_file(strings.to_cstring(&path_builder), temp_allocator())
+ proc_stat_bytes, stat_err := _read_entire_pseudo_file(strings.to_cstring(&path_builder) or_return, temp_allocator())
if stat_err != nil {
err = stat_err
break stat_if
@@ -330,7 +330,7 @@ _process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator
strings.write_int(&path_builder, pid)
strings.write_string(&path_builder, "/environ")
- if env_bytes, env_err := _read_entire_pseudo_file(strings.to_cstring(&path_builder), temp_allocator()); env_err == nil {
+ if env_bytes, env_err := _read_entire_pseudo_file(strings.to_cstring(&path_builder) or_return, temp_allocator()); env_err == nil {
env := string(env_bytes)
env_list := make([dynamic]string, allocator) or_return
@@ -384,14 +384,6 @@ _Sys_Process_Attributes :: struct {}
@(private="package")
_process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
- has_executable_permissions :: proc(fd: linux.Fd) -> bool {
- backing: [48]u8
- b := strings.builder_from_bytes(backing[:])
- strings.write_string(&b, "/proc/self/fd/")
- strings.write_int(&b, int(fd))
- return linux.access(strings.to_cstring(&b), linux.X_OK) == .NONE
- }
-
TEMP_ALLOCATOR_GUARD()
if len(desc.command) == 0 {
@@ -411,7 +403,7 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
}
// search PATH if just a plain name is provided
- exe_fd: linux.Fd
+ exe_path: cstring
executable_name := desc.command[0]
if strings.index_byte(executable_name, '/') < 0 {
path_env := get_env("PATH", temp_allocator())
@@ -426,16 +418,11 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
strings.write_byte(&exe_builder, '/')
strings.write_string(&exe_builder, executable_name)
- exe_path := strings.to_cstring(&exe_builder)
- if exe_fd, errno = linux.openat(dir_fd, exe_path, {.PATH, .CLOEXEC}); errno != .NONE {
- continue
- }
- if !has_executable_permissions(exe_fd) {
- linux.close(exe_fd)
- continue
+ exe_path = strings.to_cstring(&exe_builder) or_return
+ if linux.access(exe_path, linux.X_OK) == .NONE {
+ found = true
+ break
}
- found = true
- break
}
if !found {
// check in cwd to match windows behavior
@@ -443,29 +430,18 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
strings.write_string(&exe_builder, "./")
strings.write_string(&exe_builder, executable_name)
- exe_path := strings.to_cstring(&exe_builder)
- if exe_fd, errno = linux.openat(dir_fd, exe_path, {.PATH, .CLOEXEC}); errno != .NONE {
+ exe_path = strings.to_cstring(&exe_builder) or_return
+ if linux.access(exe_path, linux.X_OK) != .NONE {
return process, .Not_Exist
}
- if !has_executable_permissions(exe_fd) {
- linux.close(exe_fd)
- return process, .Permission_Denied
- }
}
} else {
- exe_path := temp_cstring(executable_name) or_return
- if exe_fd, errno = linux.openat(dir_fd, exe_path, {.PATH, .CLOEXEC}); errno != .NONE {
- return process, _get_platform_error(errno)
- }
- if !has_executable_permissions(exe_fd) {
- linux.close(exe_fd)
- return process, .Permission_Denied
+ exe_path = temp_cstring(executable_name) or_return
+ if linux.access(exe_path, linux.X_OK) != .NONE {
+ return process, .Not_Exist
}
}
- // At this point, we have an executable.
- defer linux.close(exe_fd)
-
// args and environment need to be a list of cstrings
// that are terminated by a nil pointer.
cargs := make([]cstring, len(desc.command) + 1, temp_allocator()) or_return
@@ -492,7 +468,6 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
}
defer linux.close(child_pipe_fds[READ])
-
// TODO: This is the traditional textbook implementation with fork.
// A more efficient implementation with vfork:
//
@@ -572,8 +547,13 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
if _, errno = linux.dup2(stderr_fd, STDERR); errno != .NONE {
write_errno_to_parent_and_abort(child_pipe_fds[WRITE], errno)
}
+ if dir_fd != linux.AT_FDCWD {
+ if errno = linux.fchdir(dir_fd); errno != .NONE {
+ write_errno_to_parent_and_abort(child_pipe_fds[WRITE], errno)
+ }
+ }
- errno = linux.execveat(exe_fd, "", &cargs[0], env, {.AT_EMPTY_PATH})
+ errno = linux.execveat(dir_fd, exe_path, &cargs[0], env)
assert(errno != nil)
write_errno_to_parent_and_abort(child_pipe_fds[WRITE], errno)
}
@@ -614,7 +594,7 @@ _process_state_update_times :: proc(state: ^Process_State) -> (err: Error) {
strings.write_string(&path_builder, "/stat")
stat_buf: []u8
- stat_buf, err = _read_entire_pseudo_file(strings.to_cstring(&path_builder), temp_allocator())
+ stat_buf, err = _read_entire_pseudo_file(strings.to_cstring(&path_builder) or_return, temp_allocator())
if err != nil {
return
}
diff --git a/core/os/os2/process_posix.odin b/core/os/os2/process_posix.odin
index b54374cec..3fa429cbe 100644
--- a/core/os/os2/process_posix.odin
+++ b/core/os/os2/process_posix.odin
@@ -71,7 +71,7 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
strings.write_byte(&exe_builder, '/')
strings.write_string(&exe_builder, exe_name)
- if exe_fd := posix.open(strings.to_cstring(&exe_builder), {.CLOEXEC, .EXEC}); exe_fd == -1 {
+ if exe_fd := posix.open(strings.to_cstring(&exe_builder) or_return, {.CLOEXEC, .EXEC}); exe_fd == -1 {
continue
} else {
posix.close(exe_fd)
@@ -91,7 +91,7 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
// "hello/./world" is fine right?
- if exe_fd := posix.open(strings.to_cstring(&exe_builder), {.CLOEXEC, .EXEC}); exe_fd == -1 {
+ if exe_fd := posix.open(strings.to_cstring(&exe_builder) or_return, {.CLOEXEC, .EXEC}); exe_fd == -1 {
err = .Not_Exist
return
} else {
@@ -102,7 +102,7 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
strings.builder_reset(&exe_builder)
strings.write_string(&exe_builder, exe_name)
- if exe_fd := posix.open(strings.to_cstring(&exe_builder), {.CLOEXEC, .EXEC}); exe_fd == -1 {
+ if exe_fd := posix.open(strings.to_cstring(&exe_builder) or_return, {.CLOEXEC, .EXEC}); exe_fd == -1 {
err = .Not_Exist
return
} else {
@@ -181,7 +181,7 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
if posix.chdir(cwd) != .OK { abort(pipe[WRITE]) }
}
- res := posix.execve(strings.to_cstring(&exe_builder), raw_data(cmd), env)
+ res := posix.execve(strings.to_cstring(&exe_builder) or_return, raw_data(cmd), env)
assert(res == -1)
abort(pipe[WRITE])
diff --git a/core/os/os2/process_wasi.odin b/core/os/os2/process_wasi.odin
new file mode 100644
index 000000000..6ebfe3788
--- /dev/null
+++ b/core/os/os2/process_wasi.odin
@@ -0,0 +1,89 @@
+#+private
+package os2
+
+import "base:runtime"
+
+import "core:time"
+import "core:sys/wasm/wasi"
+
+_exit :: proc "contextless" (code: int) -> ! {
+ wasi.proc_exit(wasi.exitcode_t(code))
+}
+
+_get_uid :: proc() -> int {
+ return 0
+}
+
+_get_euid :: proc() -> int {
+ return 0
+}
+
+_get_gid :: proc() -> int {
+ return 0
+}
+
+_get_egid :: proc() -> int {
+ return 0
+}
+
+_get_pid :: proc() -> int {
+ return 0
+}
+
+_get_ppid :: proc() -> int {
+ return 0
+}
+
+_process_info_by_handle :: proc(process: Process, selection: Process_Info_Fields, allocator: runtime.Allocator) -> (info: Process_Info, err: Error) {
+ err = .Unsupported
+ return
+}
+
+_current_process_info :: proc(selection: Process_Info_Fields, allocator: runtime.Allocator) -> (info: Process_Info, err: Error) {
+ err = .Unsupported
+ return
+}
+
+_Sys_Process_Attributes :: struct {}
+
+_process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
+ err = .Unsupported
+ return
+}
+
+_process_wait :: proc(process: Process, timeout: time.Duration) -> (process_state: Process_State, err: Error) {
+ err = .Unsupported
+ return
+}
+
+_process_close :: proc(process: Process) -> Error {
+ return .Unsupported
+}
+
+_process_kill :: proc(process: Process) -> (err: Error) {
+ return .Unsupported
+}
+
+_process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator: runtime.Allocator) -> (info: Process_Info, err: Error) {
+ err = .Unsupported
+ return
+}
+
+_process_list :: proc(allocator: runtime.Allocator) -> (list: []int, err: Error) {
+ err = .Unsupported
+ return
+}
+
+_process_open :: proc(pid: int, flags: Process_Open_Flags) -> (process: Process, err: Error) {
+ process.pid = pid
+ err = .Unsupported
+ return
+}
+
+_process_handle_still_valid :: proc(p: Process) -> Error {
+ return nil
+}
+
+_process_state_update_times :: proc(p: Process, state: ^Process_State) {
+ return
+}
diff --git a/core/os/os2/process_windows.odin b/core/os/os2/process_windows.odin
index 1984cbfdf..536930ee1 100644
--- a/core/os/os2/process_windows.odin
+++ b/core/os/os2/process_windows.odin
@@ -427,7 +427,7 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
command_line_w := win32_utf8_to_wstring(command_line, temp_allocator()) or_return
environment := desc.env
if desc.env == nil {
- environment = environ(temp_allocator())
+ environment = environ(temp_allocator()) or_return
}
environment_block := _build_environment_block(environment, temp_allocator())
environment_block_w := win32_utf8_to_utf16(environment_block, temp_allocator()) or_return
diff --git a/core/os/os2/stat_wasi.odin b/core/os/os2/stat_wasi.odin
new file mode 100644
index 000000000..2992c6267
--- /dev/null
+++ b/core/os/os2/stat_wasi.odin
@@ -0,0 +1,101 @@
+#+private
+package os2
+
+import "base:runtime"
+
+import "core:path/filepath"
+import "core:sys/wasm/wasi"
+import "core:time"
+
+internal_stat :: proc(stat: wasi.filestat_t, fullpath: string) -> (fi: File_Info) {
+ fi.fullpath = fullpath
+ fi.name = filepath.base(fi.fullpath)
+
+ fi.inode = u128(stat.ino)
+ fi.size = i64(stat.size)
+
+ switch stat.filetype {
+ case .BLOCK_DEVICE: fi.type = .Block_Device
+ case .CHARACTER_DEVICE: fi.type = .Character_Device
+ case .DIRECTORY: fi.type = .Directory
+ case .REGULAR_FILE: fi.type = .Regular
+ case .SOCKET_DGRAM, .SOCKET_STREAM: fi.type = .Socket
+ case .SYMBOLIC_LINK: fi.type = .Symlink
+ case .UNKNOWN: fi.type = .Undetermined
+ case: fi.type = .Undetermined
+ }
+
+ fi.creation_time = time.Time{_nsec=i64(stat.ctim)}
+ fi.modification_time = time.Time{_nsec=i64(stat.mtim)}
+ fi.access_time = time.Time{_nsec=i64(stat.atim)}
+
+ return
+}
+
+_fstat :: proc(f: ^File, allocator: runtime.Allocator) -> (fi: File_Info, err: Error) {
+ if f == nil || f.impl == nil {
+ err = .Invalid_File
+ return
+ }
+
+ impl := (^File_Impl)(f.impl)
+
+ stat, _err := wasi.fd_filestat_get(__fd(f))
+ if _err != nil {
+ err = _get_platform_error(_err)
+ return
+ }
+
+ fullpath := clone_string(impl.name, allocator) or_return
+ return internal_stat(stat, fullpath), nil
+}
+
+_stat :: proc(name: string, allocator: runtime.Allocator) -> (fi: File_Info, err: Error) {
+ if name == "" {
+ err = .Invalid_Path
+ return
+ }
+
+ dir_fd, relative, ok := match_preopen(name)
+ if !ok {
+ err = .Invalid_Path
+ return
+ }
+
+ stat, _err := wasi.path_filestat_get(dir_fd, {.SYMLINK_FOLLOW}, relative)
+ if _err != nil {
+ err = _get_platform_error(_err)
+ return
+ }
+
+ // NOTE: wasi doesn't really do full paths afact.
+ fullpath := clone_string(name, allocator) or_return
+ return internal_stat(stat, fullpath), nil
+}
+
+_lstat :: proc(name: string, allocator: runtime.Allocator) -> (fi: File_Info, err: Error) {
+ if name == "" {
+ err = .Invalid_Path
+ return
+ }
+
+ dir_fd, relative, ok := match_preopen(name)
+ if !ok {
+ err = .Invalid_Path
+ return
+ }
+
+ stat, _err := wasi.path_filestat_get(dir_fd, {}, relative)
+ if _err != nil {
+ err = _get_platform_error(_err)
+ return
+ }
+
+ // NOTE: wasi doesn't really do full paths afact.
+ fullpath := clone_string(name, allocator) or_return
+ return internal_stat(stat, fullpath), nil
+}
+
+_same_file :: proc(fi1, fi2: File_Info) -> bool {
+ return fi1.fullpath == fi2.fullpath
+}
diff --git a/core/os/os2/stat_windows.odin b/core/os/os2/stat_windows.odin
index 0a019e9da..31f5d9e88 100644
--- a/core/os/os2/stat_windows.odin
+++ b/core/os/os2/stat_windows.odin
@@ -72,7 +72,11 @@ internal_stat :: proc(name: string, create_file_attributes: u32, allocator: runt
ok := win32.GetFileAttributesExW(wname, win32.GetFileExInfoStandard, &fa)
if ok && fa.dwFileAttributes & win32.FILE_ATTRIBUTE_REPARSE_POINT == 0 {
// Not a symlink
- return _file_info_from_win32_file_attribute_data(&fa, name, allocator)
+ fi = _file_info_from_win32_file_attribute_data(&fa, name, allocator) or_return
+ if fi.type == .Undetermined {
+ fi.type = _file_type_from_create_file(wname, create_file_attributes)
+ }
+ return
}
err := 0 if ok else win32.GetLastError()
@@ -86,7 +90,11 @@ internal_stat :: proc(name: string, create_file_attributes: u32, allocator: runt
}
win32.FindClose(sh)
- return _file_info_from_win32_find_data(&fd, name, allocator)
+ fi = _file_info_from_win32_find_data(&fd, name, allocator) or_return
+ if fi.type == .Undetermined {
+ fi.type = _file_type_from_create_file(wname, create_file_attributes)
+ }
+ return
}
h := win32.CreateFileW(wname, 0, 0, nil, win32.OPEN_EXISTING, create_file_attributes, nil)
@@ -194,6 +202,15 @@ file_type :: proc(h: win32.HANDLE) -> File_Type {
return .Undetermined
}
+_file_type_from_create_file :: proc(wname: win32.wstring, create_file_attributes: u32) -> File_Type {
+ h := win32.CreateFileW(wname, 0, 0, nil, win32.OPEN_EXISTING, create_file_attributes, nil)
+ if h == win32.INVALID_HANDLE_VALUE {
+ return .Undetermined
+ }
+ defer win32.CloseHandle(h)
+ return file_type(h)
+}
+
_file_type_mode_from_file_attributes :: proc(file_attributes: win32.DWORD, h: win32.HANDLE, ReparseTag: win32.DWORD) -> (type: File_Type, mode: int) {
if file_attributes & win32.FILE_ATTRIBUTE_READONLY != 0 {
mode |= 0o444
@@ -266,7 +283,7 @@ _file_info_from_get_file_information_by_handle :: proc(path: string, h: win32.HA
fi.name = basename(path)
fi.inode = u128(u64(d.nFileIndexHigh)<<32 + u64(d.nFileIndexLow))
fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow)
- type, mode := _file_type_mode_from_file_attributes(d.dwFileAttributes, nil, 0)
+ type, mode := _file_type_mode_from_file_attributes(d.dwFileAttributes, h, 0)
fi.type = type
fi.mode |= mode
fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime))
diff --git a/core/os/os2/temp_file_wasi.odin b/core/os/os2/temp_file_wasi.odin
new file mode 100644
index 000000000..d5628d300
--- /dev/null
+++ b/core/os/os2/temp_file_wasi.odin
@@ -0,0 +1,9 @@
+#+private
+package os2
+
+import "base:runtime"
+
+_temp_dir :: proc(allocator: runtime.Allocator) -> (string, runtime.Allocator_Error) {
+ // NOTE: requires user to add /tmp to their preopen dirs, no standard way exists.
+ return clone_string("/tmp", allocator)
+}
diff --git a/core/os/os_darwin.odin b/core/os/os_darwin.odin
index d4435ec63..bbffc46d7 100644
--- a/core/os/os_darwin.odin
+++ b/core/os/os_darwin.odin
@@ -1287,7 +1287,7 @@ sendto :: proc(sd: Socket, data: []u8, flags: int, addr: ^SOCKADDR, addrlen: soc
}
send :: proc(sd: Socket, data: []byte, flags: int) -> (u32, Error) {
- result := _unix_send(c.int(sd), raw_data(data), len(data), 0)
+ result := _unix_send(c.int(sd), raw_data(data), len(data), i32(flags))
if result < 0 {
return 0, get_last_error()
}
diff --git a/core/os/os_freebsd.odin b/core/os/os_freebsd.odin
index 837e79f4d..87a56b057 100644
--- a/core/os/os_freebsd.odin
+++ b/core/os/os_freebsd.odin
@@ -624,6 +624,14 @@ is_dir_path :: proc(path: string, follow_links: bool = true) -> bool {
is_file :: proc {is_file_path, is_file_handle}
is_dir :: proc {is_dir_path, is_dir_handle}
+@(require_results)
+exists :: proc(path: string) -> bool {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
+ cpath := strings.clone_to_cstring(path, context.temp_allocator)
+ res := _unix_access(cpath, O_RDONLY)
+ return res == 0
+}
+
// NOTE(bill): Uses startup to initialize it
stdin: Handle = 0
diff --git a/core/os/os_haiku.odin b/core/os/os_haiku.odin
index 4ad370724..4a57afb87 100644
--- a/core/os/os_haiku.odin
+++ b/core/os/os_haiku.odin
@@ -1,11 +1,13 @@
package os
-foreign import libc "system:c"
+foreign import lib "system:c"
import "base:runtime"
import "core:c"
+import "core:c/libc"
import "core:strings"
import "core:sys/haiku"
+import "core:sys/posix"
Handle :: i32
Pid :: i32
@@ -14,7 +16,7 @@ _Platform_Error :: haiku.Errno
MAX_PATH :: haiku.PATH_MAX
-ENOSYS :: _Platform_Error(i32(haiku.Errno.POSIX_ERROR_BASE) + 9)
+ENOSYS :: _Platform_Error(haiku.Errno.ENOSYS)
INVALID_HANDLE :: ~Handle(0)
@@ -117,14 +119,13 @@ S_ISBLK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFBLK
S_ISFIFO :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFIFO }
S_ISSOCK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFSOCK }
+__error :: libc.errno
+_unix_open :: posix.open
-foreign libc {
- @(link_name="_errorp") __error :: proc() -> ^c.int ---
-
+foreign lib {
@(link_name="fork") _unix_fork :: proc() -> pid_t ---
@(link_name="getthrid") _unix_getthrid :: proc() -> int ---
- @(link_name="open") _unix_open :: proc(path: cstring, flags: c.int, #c_vararg mode: ..u16) -> Handle ---
@(link_name="close") _unix_close :: proc(fd: Handle) -> c.int ---
@(link_name="read") _unix_read :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t ---
@(link_name="pread") _unix_pread :: proc(fd: Handle, buf: rawptr, size: c.size_t, offset: i64) -> c.ssize_t ---
@@ -150,6 +151,7 @@ foreign libc {
@(link_name="closedir") _unix_closedir :: proc(dirp: Dir) -> c.int ---
@(link_name="rewinddir") _unix_rewinddir :: proc(dirp: Dir) ---
@(link_name="readdir_r") _unix_readdir_r :: proc(dirp: Dir, entry: ^Dirent, result: ^^Dirent) -> c.int ---
+ @(link_name="dup") _unix_dup :: proc(fd: Handle) -> Handle ---
@(link_name="malloc") _unix_malloc :: proc(size: c.size_t) -> rawptr ---
@(link_name="calloc") _unix_calloc :: proc(num, size: c.size_t) -> rawptr ---
@@ -157,7 +159,7 @@ foreign libc {
@(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: c.size_t) -> rawptr ---
@(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring ---
- @(link_name="realpath") _unix_realpath :: proc(path: cstring, resolved_path: rawptr) -> rawptr ---
+ @(link_name="realpath") _unix_realpath :: proc(path: cstring, resolved_path: [^]byte = nil) -> cstring ---
@(link_name="exit") _unix_exit :: proc(status: c.int) -> ! ---
@@ -203,7 +205,7 @@ fork :: proc() -> (Pid, Error) {
open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
- handle := _unix_open(cstr, c.int(flags), u16(mode))
+ handle := cast(Handle)_unix_open(cstr, transmute(posix.O_Flags)i32(flags), transmute(posix.mode_t)i32(mode))
if handle == -1 {
return INVALID_HANDLE, get_last_error()
}
@@ -444,7 +446,7 @@ absolute_path_from_relative :: proc(rel: string, allocator := context.allocator)
if path_ptr == nil {
return "", get_last_error()
}
- defer _unix_free(path_ptr)
+ defer _unix_free(rawptr(path_ptr))
path_cstr := cstring(path_ptr)
return strings.clone(string(path_cstr), allocator)
@@ -488,3 +490,17 @@ exit :: proc "contextless" (code: int) -> ! {
runtime._cleanup_runtime_contextless()
_unix_exit(i32(code))
}
+
+@(require_results)
+current_thread_id :: proc "contextless" () -> int {
+ return int(haiku.find_thread(nil))
+}
+
+@(private, require_results)
+_dup :: proc(fd: Handle) -> (Handle, Error) {
+ dup := _unix_dup(fd)
+ if dup == -1 {
+ return INVALID_HANDLE, get_last_error()
+ }
+ return dup, nil
+}
diff --git a/core/os/os_linux.odin b/core/os/os_linux.odin
index e023ce7cb..2281e6a82 100644
--- a/core/os/os_linux.odin
+++ b/core/os/os_linux.odin
@@ -1155,7 +1155,7 @@ sendto :: proc(sd: Socket, data: []u8, flags: int, addr: ^SOCKADDR, addrlen: soc
}
send :: proc(sd: Socket, data: []byte, flags: int) -> (u32, Error) {
- result := unix.sys_sendto(int(sd), raw_data(data), len(data), 0, nil, 0)
+ result := unix.sys_sendto(int(sd), raw_data(data), len(data), flags, nil, 0)
if result < 0 {
return 0, _get_errno(int(result))
}
diff --git a/core/os/stat_unix.odin b/core/os/stat_unix.odin
index 7f7985e83..648987a07 100644
--- a/core/os/stat_unix.odin
+++ b/core/os/stat_unix.odin
@@ -53,7 +53,7 @@ File_Info :: struct {
@(private, require_results)
_make_time_from_unix_file_time :: proc(uft: Unix_File_Time) -> time.Time {
return time.Time{
- _nsec = uft.nanoseconds + uft.seconds * 1_000_000_000,
+ _nsec = i64(uft.nanoseconds) + i64(uft.seconds) * 1_000_000_000,
}
}
diff --git a/core/path/filepath/match.odin b/core/path/filepath/match.odin
index 003f8046d..1f0ac9287 100644
--- a/core/path/filepath/match.odin
+++ b/core/path/filepath/match.odin
@@ -1,3 +1,4 @@
+#+build !wasi
package filepath
import "core:os"
diff --git a/core/path/filepath/path_unix.odin b/core/path/filepath/path_unix.odin
index 35b98a7ae..8bf412599 100644
--- a/core/path/filepath/path_unix.odin
+++ b/core/path/filepath/path_unix.odin
@@ -1,4 +1,4 @@
-#+build linux, darwin, freebsd, openbsd, netbsd
+#+build linux, darwin, freebsd, openbsd, netbsd, haiku
package filepath
import "base:runtime"
diff --git a/core/path/filepath/path_wasi.odin b/core/path/filepath/path_wasi.odin
new file mode 100644
index 000000000..74cc6ca1e
--- /dev/null
+++ b/core/path/filepath/path_wasi.odin
@@ -0,0 +1,36 @@
+package filepath
+
+import "base:runtime"
+
+import "core:strings"
+
+SEPARATOR :: '/'
+SEPARATOR_STRING :: `/`
+LIST_SEPARATOR :: ':'
+
+is_reserved_name :: proc(path: string) -> bool {
+ return false
+}
+
+is_abs :: proc(path: string) -> bool {
+ return strings.has_prefix(path, "/")
+}
+
+abs :: proc(path: string, allocator := context.allocator) -> (string, bool) {
+ if is_abs(path) {
+ return strings.clone(string(path), allocator), true
+ }
+
+ return path, false
+}
+
+join :: proc(elems: []string, allocator := context.allocator) -> (joined: string, err: runtime.Allocator_Error) #optional_allocator_error {
+ for e, i in elems {
+ if e != "" {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
+ p := strings.join(elems[i:], SEPARATOR_STRING, context.temp_allocator) or_return
+ return clean(p, allocator)
+ }
+ }
+ return "", nil
+}
diff --git a/core/path/filepath/walk.odin b/core/path/filepath/walk.odin
index 51dfa71d2..53b10eed7 100644
--- a/core/path/filepath/walk.odin
+++ b/core/path/filepath/walk.odin
@@ -1,3 +1,4 @@
+#+build !wasi
package filepath
import "core:os"
diff --git a/core/prof/spall/doc.odin b/core/prof/spall/doc.odin
index c81bad05f..b007ad4cb 100644
--- a/core/prof/spall/doc.odin
+++ b/core/prof/spall/doc.odin
@@ -18,6 +18,8 @@ Example:
defer spall.context_destroy(&spall_ctx)
buffer_backing := make([]u8, spall.BUFFER_DEFAULT_SIZE)
+ defer delete(buffer_backing)
+
spall_buffer = spall.buffer_create(buffer_backing, u32(sync.current_thread_id()))
defer spall.buffer_destroy(&spall_ctx, &spall_buffer)
diff --git a/core/simd/simd.odin b/core/simd/simd.odin
index 714e42510..e91b12a4d 100644
--- a/core/simd/simd.odin
+++ b/core/simd/simd.odin
@@ -1982,6 +1982,84 @@ the picture represents the `indices` parameter:
*/
swizzle :: builtin.swizzle
+/*
+Extract the set of most-significant bits of a SIMD vector.
+
+This procedure checks the the most-significant bit (MSB) for each lane of vector
+and returns the numbers of lanes with the most-significant bit set. This procedure
+can be used in conjuction with `lanes_eq` (and other similar procedures) to
+count the number of matched lanes by computing the cardinality of the resulting
+set.
+
+Inputs:
+- `a`: An input vector.
+
+Result:
+- A bitset of integers, corresponding to the indexes of the lanes, whose MSBs
+ are set.
+
+**Operation**:
+
+ bits_per_lane = 8*size_of(a[0])
+ res = bit_set {}
+ for i in 0 ..< len(a) {
+ if a[i] & 1<<(bits_per_lane-1) != 0 {
+ res |= i
+ }
+ }
+ return res
+
+Example:
+
+ // Since lanes 0, 1, 4, 7 contain negative numbers, the most significant
+ // bits for them will be set.
+ v := #simd[8]i32 { -1, -2, +3, +4, -5, +6, +7, -8 }
+ fmt.println(simd.extract_msbs(v))
+
+Output:
+
+ bit_set[0..=7]{0, 1, 4, 7}
+*/
+extract_msbs :: intrinsics.simd_extract_msbs
+
+/*
+Extract the set of least-significant bits of a SIMD vector.
+
+This procedure checks the the least-significant bit (LSB) for each lane of vector
+and returns the numbers of lanes with the least-significant bit set. This procedure
+can be used in conjuction with `lanes_eq` (and other similar procedures) to
+count the number of matched lanes by computing the cardinality of the resulting
+set.
+
+Inputs:
+- `a`: An input vector.
+
+Result:
+- A bitset of integers, corresponding to the indexes of the lanes, whose LSBs
+ are set.
+
+**Operation**:
+
+ res = bit_set {}
+ for i in 0 ..< len(a) {
+ if a[i] & 1 != 0 {
+ res |= i
+ }
+ }
+ return res
+
+Example:
+
+ // Since lanes 0, 2, 4, 6 contain odd integers, the least significant bits
+ // for these lanes are set.
+ v := #simd[8]i32 { -1, -2, +3, +4, -5, +6, +7, -8 }
+ fmt.println(simd.extract_lsbs(v))
+
+Output:
+
+ bit_set[0..=7]{0, 2, 4, 6}
+*/
+extract_lsbs :: intrinsics.simd_extract_lsbs
/*
Reorder the lanes of two SIMD vectors.
diff --git a/core/slice/slice.odin b/core/slice/slice.odin
index c31edf281..c328fd267 100644
--- a/core/slice/slice.odin
+++ b/core/slice/slice.odin
@@ -48,22 +48,41 @@ to_type :: proc(buf: []u8, $T: typeid) -> (T, bool) #optional_ok {
}
/*
- Turn a slice of one type, into a slice of another type.
-
- Only converts the type and length of the slice itself.
- The length is rounded down to the nearest whole number of items.
-
- ```
- large_items := []i64{1, 2, 3, 4}
- small_items := slice.reinterpret([]i32, large_items)
- assert(len(small_items) == 8)
- ```
- ```
- small_items := []byte{1, 0, 0, 0, 0, 0, 0, 0,
- 2, 0, 0, 0}
- large_items := slice.reinterpret([]i64, small_items)
- assert(len(large_items) == 1) // only enough bytes to make 1 x i64; two would need at least 8 bytes.
- ```
+Turn a slice of one type, into a slice of another type.
+
+Only converts the type and length of the slice itself.
+The length is rounded down to the nearest whole number of items.
+
+Example:
+
+ import "core:fmt"
+ import "core:slice"
+
+ i64s_as_i32s :: proc() {
+ large_items := []i64{1, 2, 3, 4}
+ small_items := slice.reinterpret([]i32, large_items)
+ assert(len(small_items) == 8)
+ fmt.println(large_items, "->", small_items)
+ }
+
+ bytes_as_i64s :: proc() {
+ small_items := [12]byte{}
+ small_items[0] = 1
+ small_items[8] = 2
+ large_items := slice.reinterpret([]i64, small_items[:])
+ assert(len(large_items) == 1) // only enough bytes to make 1 x i64; two would need at least 8 bytes.
+ fmt.println(small_items, "->", large_items)
+ }
+
+ reinterpret_example :: proc() {
+ i64s_as_i32s()
+ bytes_as_i64s()
+ }
+
+Output:
+ [1, 2, 3, 4] -> [1, 0, 2, 0, 3, 0, 4, 0]
+ [1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0] -> [1]
+
*/
@(require_results)
reinterpret :: proc "contextless" ($T: typeid/[]$U, s: []$V) -> []U {
@@ -879,8 +898,7 @@ bitset_to_enum_slice_with_buffer :: proc(buf: []$E, bs: $T) -> (slice: []E) wher
// sl := slice.bitset_to_enum_slice(bs)
@(require_results)
bitset_to_enum_slice_with_make :: proc(bs: $T, $E: typeid, allocator := context.allocator) -> (slice: []E) where intrinsics.type_is_enum(E), intrinsics.type_bit_set_elem_type(T) == E {
- ones := intrinsics.count_ones(transmute(E)bs)
- buf := make([]E, int(ones), allocator)
+ buf := make([]E, card(bs), allocator)
return bitset_to_enum_slice(buf, bs)
}
diff --git a/core/strings/builder.odin b/core/strings/builder.odin
index 97a615990..e5a88527a 100644
--- a/core/strings/builder.odin
+++ b/core/strings/builder.odin
@@ -288,18 +288,41 @@ to_string :: proc(b: Builder) -> (res: string) {
/*
Appends a trailing null byte after the end of the current Builder byte buffer and then casts it to a cstring
+NOTE: This procedure will not check if the backing buffer has enough space to include the extra null byte.
+
Inputs:
- b: A pointer to builder
Returns:
- res: A cstring of the Builder's buffer
*/
-to_cstring :: proc(b: ^Builder) -> (res: cstring) {
+unsafe_to_cstring :: proc(b: ^Builder) -> (res: cstring) {
append(&b.buf, 0)
pop(&b.buf)
return cstring(raw_data(b.buf))
}
/*
+Appends a trailing null byte after the end of the current Builder byte buffer and then casts it to a cstring
+
+Inputs:
+- b: A pointer to builder
+
+Returns:
+- res: A cstring of the Builder's buffer upon success
+- err: An optional allocator error if one occured, `nil` otherwise
+*/
+to_cstring :: proc(b: ^Builder) -> (res: cstring, err: mem.Allocator_Error) {
+ n := append(&b.buf, 0) or_return
+ if n != 1 {
+ return nil, .Out_Of_Memory
+ }
+ pop(&b.buf)
+ #no_bounds_check {
+ assert(b.buf[len(b.buf)] == 0)
+ }
+ return cstring(raw_data(b.buf)), nil
+}
+/*
Returns the length of the Builder's buffer, in bytes
Inputs:
diff --git a/core/strings/intern.odin b/core/strings/intern.odin
index 4c270980c..0b8ed173e 100644
--- a/core/strings/intern.odin
+++ b/core/strings/intern.odin
@@ -89,6 +89,7 @@ intern_get_cstring :: proc(m: ^Intern, text: string) -> (str: cstring, err: runt
entry := _intern_get_entry(m, text) or_return
return cstring(&entry.str[0]), nil
}
+
/*
Internal function to lookup whether the text string exists in the map, returns the entry
Sets and allocates the entry if it wasn't set yet
@@ -104,13 +105,15 @@ Returns:
- err: An allocator error if one occured, `nil` otherwise
*/
_intern_get_entry :: proc(m: ^Intern, text: string) -> (new_entry: ^Intern_Entry, err: runtime.Allocator_Error) #no_bounds_check {
- if prev, ok := m.entries[text]; ok {
- return prev, nil
- }
if m.allocator.procedure == nil {
m.allocator = context.allocator
}
+ key_ptr, val_ptr, inserted := map_entry(&m.entries, text) or_return
+ if !inserted {
+ return val_ptr^, nil
+ }
+
entry_size := int(offset_of(Intern_Entry, str)) + len(text) + 1
bytes := runtime.mem_alloc(entry_size, align_of(Intern_Entry), m.allocator) or_return
new_entry = (^Intern_Entry)(raw_data(bytes))
@@ -120,6 +123,9 @@ _intern_get_entry :: proc(m: ^Intern, text: string) -> (new_entry: ^Intern_Entry
new_entry.str[new_entry.len] = 0
key := string(new_entry.str[:new_entry.len])
- m.entries[key] = new_entry
- return new_entry, nil
+
+ key_ptr^ = key
+ val_ptr^ = new_entry
+
+ return
}
diff --git a/core/strings/strings.odin b/core/strings/strings.odin
index af93ff33c..e99a1bfb4 100644
--- a/core/strings/strings.odin
+++ b/core/strings/strings.odin
@@ -1031,14 +1031,17 @@ Returns:
*/
@private
_split_iterator :: proc(s: ^string, sep: string, sep_save: int) -> (res: string, ok: bool) {
+ m: int
if sep == "" {
- res = s[:]
- ok = true
- s^ = s[len(s):]
- return
+ if len(s) == 0 {
+ m = -1
+ } else {
+ _, w := utf8.decode_rune_in_string(s^)
+ m = w
+ }
+ } else {
+ m = index(s^, sep)
}
-
- m := index(s^, sep)
if m < 0 {
// not found
res = s[:]
@@ -1872,7 +1875,8 @@ index_multi :: proc(s: string, substrs: []string) -> (idx: int, width: int) {
lowest_index := len(s)
found := false
for substr in substrs {
- if i := index(s, substr); i >= 0 {
+ haystack := s[:min(len(s), lowest_index + len(substr))]
+ if i := index(haystack, substr); i >= 0 {
if i < lowest_index {
lowest_index = i
width = len(substr)
diff --git a/core/sync/futex_haiku.odin b/core/sync/futex_haiku.odin
index 21d07b801..52321644a 100644
--- a/core/sync/futex_haiku.odin
+++ b/core/sync/futex_haiku.odin
@@ -1,14 +1,13 @@
#+private
package sync
-import "core:c"
import "core:sys/haiku"
-import "core:sys/unix"
+import "core:sys/posix"
import "core:time"
@(private="file")
Wait_Node :: struct {
- thread: unix.pthread_t,
+ thread: posix.pthread_t,
futex: ^Futex,
prev, next: ^Wait_Node,
}
@@ -58,7 +57,7 @@ _futex_wait :: proc "contextless" (f: ^Futex, expect: u32) -> (ok: bool) {
head := &waitq.list
waiter := Wait_Node{
- thread = unix.pthread_self(),
+ thread = posix.pthread_self(),
futex = f,
prev = head,
next = head.next,
@@ -67,25 +66,24 @@ _futex_wait :: proc "contextless" (f: ^Futex, expect: u32) -> (ok: bool) {
waiter.prev.next = &waiter
waiter.next.prev = &waiter
- old_mask, mask: haiku.sigset_t
- haiku.sigemptyset(&mask)
- haiku.sigaddset(&mask, haiku.SIGCONT)
- unix.pthread_sigmask(haiku.SIG_BLOCK, &mask, &old_mask)
+ old_mask, mask: posix.sigset_t
+ posix.sigemptyset(&mask)
+ posix.sigaddset(&mask, .SIGCONT)
+ posix.pthread_sigmask(.BLOCK, &mask, &old_mask)
if u32(atomic_load_explicit(f, .Acquire)) == expect {
waitq_unlock(waitq)
defer waitq_lock(waitq)
- sig: c.int
- haiku.sigwait(&mask, &sig)
- errno := haiku.errno()
- ok = errno == .OK
+ sig: posix.Signal
+ errno := posix.sigwait(&mask, &sig)
+ ok = errno == nil
}
waiter.prev.next = waiter.next
waiter.next.prev = waiter.prev
- _ = unix.pthread_sigmask(haiku.SIG_SETMASK, &old_mask, nil)
+ _ = posix.pthread_sigmask(.SETMASK, &old_mask, nil)
// FIXME: Add error handling!
return
@@ -101,7 +99,7 @@ _futex_wait_with_timeout :: proc "contextless" (f: ^Futex, expect: u32, duration
head := &waitq.list
waiter := Wait_Node{
- thread = unix.pthread_self(),
+ thread = posix.pthread_self(),
futex = f,
prev = head,
next = head.next,
@@ -110,29 +108,29 @@ _futex_wait_with_timeout :: proc "contextless" (f: ^Futex, expect: u32, duration
waiter.prev.next = &waiter
waiter.next.prev = &waiter
- old_mask, mask: haiku.sigset_t
- haiku.sigemptyset(&mask)
- haiku.sigaddset(&mask, haiku.SIGCONT)
- unix.pthread_sigmask(haiku.SIG_BLOCK, &mask, &old_mask)
+ old_mask, mask: posix.sigset_t
+ posix.sigemptyset(&mask)
+ posix.sigaddset(&mask, .SIGCONT)
+ posix.pthread_sigmask(.BLOCK, &mask, &old_mask)
if u32(atomic_load_explicit(f, .Acquire)) == expect {
waitq_unlock(waitq)
defer waitq_lock(waitq)
- info: haiku.siginfo_t
- ts := unix.timespec{
- tv_sec = i64(duration / 1e9),
+ info: posix.siginfo_t
+ ts := posix.timespec{
+ tv_sec = posix.time_t(i64(duration / 1e9)),
tv_nsec = i64(duration % 1e9),
}
haiku.sigtimedwait(&mask, &info, &ts)
- errno := haiku.errno()
- ok = errno == .EAGAIN || errno == .OK
+ errno := posix.errno()
+ ok = errno == .EAGAIN || errno == nil
}
waiter.prev.next = waiter.next
waiter.next.prev = waiter.prev
- unix.pthread_sigmask(haiku.SIG_SETMASK, &old_mask, nil)
+ posix.pthread_sigmask(.SETMASK, &old_mask, nil)
// FIXME: Add error handling!
return
@@ -146,7 +144,7 @@ _futex_signal :: proc "contextless" (f: ^Futex) {
head := &waitq.list
for waiter := head.next; waiter != head; waiter = waiter.next {
if waiter.futex == f {
- unix.pthread_kill(waiter.thread, haiku.SIGCONT)
+ posix.pthread_kill(waiter.thread, .SIGCONT)
break
}
}
@@ -160,7 +158,7 @@ _futex_broadcast :: proc "contextless" (f: ^Futex) {
head := &waitq.list
for waiter := head.next; waiter != head; waiter = waiter.next {
if waiter.futex == f {
- unix.pthread_kill(waiter.thread, haiku.SIGCONT)
+ posix.pthread_kill(waiter.thread, .SIGCONT)
}
}
}
diff --git a/core/sync/futex_linux.odin b/core/sync/futex_linux.odin
index 52143880b..2f4b5af72 100644
--- a/core/sync/futex_linux.odin
+++ b/core/sync/futex_linux.odin
@@ -49,9 +49,6 @@ _futex_signal :: proc "contextless" (futex: ^Futex) {
}
_futex_broadcast :: proc "contextless" (futex: ^Futex) {
- // NOTE(flysand): This code was kinda funny and I don't want to remove it, but here I will
- // record history of what has been in here before
- // FUTEX_WAKE_PRIVATE | FUTEX_WAKE
_, errno := linux.futex(cast(^linux.Futex) futex, linux.FUTEX_WAKE, {.PRIVATE}, max(i32))
#partial switch errno {
case .NONE:
diff --git a/core/sync/futex_wasm.odin b/core/sync/futex_wasm.odin
index 0f9659a02..16e69ca74 100644
--- a/core/sync/futex_wasm.odin
+++ b/core/sync/futex_wasm.odin
@@ -12,8 +12,8 @@ _futex_wait :: proc "contextless" (f: ^Futex, expected: u32) -> bool {
when !intrinsics.has_target_feature("atomics") {
panic_contextless("usage of `core:sync` requires the `-target-feature:\"atomics\"` or a `-microarch` that supports it")
} else {
- s := intrinsics.wasm_memory_atomic_wait32((^u32)(f), expected, -1)
- return s != 0
+ _ = intrinsics.wasm_memory_atomic_wait32((^u32)(f), expected, -1)
+ return true
}
}
@@ -22,7 +22,7 @@ _futex_wait_with_timeout :: proc "contextless" (f: ^Futex, expected: u32, durati
panic_contextless("usage of `core:sync` requires the `-target-feature:\"atomics\"` or a `-microarch` that supports it")
} else {
s := intrinsics.wasm_memory_atomic_wait32((^u32)(f), expected, i64(duration))
- return s != 0
+ return s != 2
}
}
@@ -30,12 +30,7 @@ _futex_signal :: proc "contextless" (f: ^Futex) {
when !intrinsics.has_target_feature("atomics") {
panic_contextless("usage of `core:sync` requires the `-target-feature:\"atomics\"` or a `-microarch` that supports it")
} else {
- loop: for {
- s := intrinsics.wasm_memory_atomic_notify32((^u32)(f), 1)
- if s >= 1 {
- return
- }
- }
+ _ = intrinsics.wasm_memory_atomic_notify32((^u32)(f), 1)
}
}
@@ -43,12 +38,7 @@ _futex_broadcast :: proc "contextless" (f: ^Futex) {
when !intrinsics.has_target_feature("atomics") {
panic_contextless("usage of `core:sync` requires the `-target-feature:\"atomics\"` or a `-microarch` that supports it")
} else {
- loop: for {
- s := intrinsics.wasm_memory_atomic_notify32((^u32)(f), ~u32(0))
- if s >= 0 {
- return
- }
- }
+ _ = intrinsics.wasm_memory_atomic_notify32((^u32)(f), max(u32))
}
}
diff --git a/core/sync/futex_windows.odin b/core/sync/futex_windows.odin
index bb9686a1a..927e6781e 100644
--- a/core/sync/futex_windows.odin
+++ b/core/sync/futex_windows.odin
@@ -26,7 +26,7 @@ foreign Ntdll {
BUT requires taking the return value of it and if it is non-zero
converting that status to a DOS error and then SetLastError
If this is not done, then things don't work as expected when
- and error occurs
+ an error occurs
GODDAMN MICROSOFT!
*/
@@ -46,7 +46,7 @@ _futex_wait :: proc "contextless" (f: ^Futex, expect: u32) -> bool {
_futex_wait_with_timeout :: proc "contextless" (f: ^Futex, expect: u32, duration: time.Duration) -> bool {
expect := expect
- // NOTE(bill): for some bizarre reason, this has be a negative number
+ // NOTE(bill): for some bizarre reason, this has to be a negative number
timeout := -i64(duration / 100)
return CustomWaitOnAddress(f, &expect, size_of(expect), &timeout)
}
diff --git a/core/sync/primitives_atomic.odin b/core/sync/primitives_atomic.odin
index 3c4324eb7..a8a84b2bc 100644
--- a/core/sync/primitives_atomic.odin
+++ b/core/sync/primitives_atomic.odin
@@ -67,7 +67,7 @@ atomic_mutex_unlock :: proc "contextless" (m: ^Atomic_Mutex) {
switch atomic_exchange_explicit(&m.state, .Unlocked, .Release) {
case .Unlocked:
- unreachable()
+ // Kind of okay - unlocking while already unlocked.
case .Locked:
// Okay
case .Waiting:
diff --git a/core/sys/darwin/Foundation/NSBlock.odin b/core/sys/darwin/Foundation/NSBlock.odin
index b9d94bfee..1ef5e8a9b 100644
--- a/core/sys/darwin/Foundation/NSBlock.odin
+++ b/core/sys/darwin/Foundation/NSBlock.odin
@@ -25,6 +25,10 @@ Block_createLocalWithParam :: proc (user_data: rawptr, user_proc: proc "c" (user
b, _ := Block_createInternalWithParam(false, user_data, user_proc, {})
return b
}
+@(objc_type=Block, objc_name="invoke")
+Block_invoke :: proc "c" (self: ^Block, args: ..any) -> ^Object {
+ return msgSend(^Object, self, "invoke:", ..args)
+}
@(private)
Internal_Block_Literal_Base :: struct {
diff --git a/core/sys/darwin/Foundation/NSData.odin b/core/sys/darwin/Foundation/NSData.odin
index 04c1ce25d..8baaf3486 100644
--- a/core/sys/darwin/Foundation/NSData.odin
+++ b/core/sys/darwin/Foundation/NSData.odin
@@ -13,6 +13,23 @@ Data_init :: proc "c" (self: ^Data) -> ^Data {
return msgSend(^Data, self, "init")
}
+@(objc_type=Data, objc_name="initWithBytes")
+Data_initWithBytes :: proc "c" (self: ^Data, bytes: []byte) -> ^Data {
+ return msgSend(^Data, self, "initWithBytes:length:", raw_data(bytes), len(bytes))
+}
+
+@(objc_type=Data, objc_name="initWithBytesNoCopy")
+Data_initWithBytesNoCopy :: proc "c" (self: ^Data, bytes: []byte, freeWhenDone: BOOL) -> ^Data {
+ return msgSend(
+ ^Data,
+ self,
+ "initWithBytesNoCopy:length:freeWhenDone:",
+ raw_data(bytes),
+ len(bytes),
+ freeWhenDone,
+ )
+}
+
@(objc_type=Data, objc_name="mutableBytes")
Data_mutableBytes :: proc "c" (self: ^Data) -> rawptr {
return msgSend(rawptr, self, "mutableBytes")
diff --git a/core/sys/darwin/Foundation/NSDate.odin b/core/sys/darwin/Foundation/NSDate.odin
index 41efb0cf5..4ba539aa4 100644
--- a/core/sys/darwin/Foundation/NSDate.odin
+++ b/core/sys/darwin/Foundation/NSDate.odin
@@ -18,6 +18,11 @@ Date_dateWithTimeIntervalSinceNow :: proc "c" (secs: TimeInterval) -> ^Date {
return msgSend(^Date, Date, "dateWithTimeIntervalSinceNow:", secs)
}
+@(objc_type=Date, objc_name="timeIntervalSince1970")
+Date_timeIntervalSince1970 :: proc "c" (self: ^Date) -> f64 {
+ return msgSend(f64, self, "timeIntervalSince1970")
+}
+
@(objc_type=Date, objc_name="distantFuture", objc_is_class_method=true)
Date_distantFuture :: proc "c" () -> ^Date {
return msgSend(^Date, Date, "distantFuture")
diff --git a/core/sys/darwin/Foundation/NSMenu.odin b/core/sys/darwin/Foundation/NSMenu.odin
index e49162a7f..9a74151b0 100644
--- a/core/sys/darwin/Foundation/NSMenu.odin
+++ b/core/sys/darwin/Foundation/NSMenu.odin
@@ -30,6 +30,7 @@ MenuItem :: struct {using _: Object}
MenuItem_alloc :: proc "c" () -> ^MenuItem {
return msgSend(^MenuItem, MenuItem, "alloc")
}
+
@(objc_type=MenuItem, objc_name="registerActionCallback", objc_is_class_method=true)
MenuItem_registerActionCallback :: proc "c" (name: cstring, callback: MenuItemCallback) -> SEL {
s := string(name)
@@ -50,11 +51,21 @@ MenuItem_registerActionCallback :: proc "c" (name: cstring, callback: MenuItemCa
return sel
}
+@(objc_type=MenuItem, objc_name="separatorItem", objc_is_class_method=true)
+MenuItem_separatorItem :: proc "c" () -> ^MenuItem {
+ return msgSend(^MenuItem, MenuItem, "separatorItem")
+}
+
@(objc_type=MenuItem, objc_name="init")
MenuItem_init :: proc "c" (self: ^MenuItem) -> ^MenuItem {
return msgSend(^MenuItem, self, "init")
}
+@(objc_type=MenuItem, objc_name="initWithTitle")
+MenuItem_initWithTitle :: proc "c" (self: ^MenuItem, title: ^String, action: SEL, keyEquivalent: ^String) -> ^MenuItem {
+ return msgSend(^MenuItem, self, "initWithTitle:action:keyEquivalent:", title, action, keyEquivalent)
+}
+
@(objc_type=MenuItem, objc_name="setKeyEquivalentModifierMask")
MenuItem_setKeyEquivalentModifierMask :: proc "c" (self: ^MenuItem, modifierMask: KeyEquivalentModifierMask) {
msgSend(nil, self, "setKeyEquivalentModifierMask:", modifierMask)
@@ -75,6 +86,11 @@ MenuItem_title :: proc "c" (self: ^MenuItem) -> ^String {
return msgSend(^String, self, "title")
}
+@(objc_type=MenuItem, objc_name="setTitle")
+MenuItem_setTitle :: proc "c" (self: ^MenuItem, title: ^String) -> ^String {
+ return msgSend(^String, self, "title:", title)
+}
+
@(objc_class="NSMenu")
diff --git a/core/sys/darwin/Foundation/NSSavePanel.odin b/core/sys/darwin/Foundation/NSSavePanel.odin
index 8e4d7a07b..d40b3ecd5 100644
--- a/core/sys/darwin/Foundation/NSSavePanel.odin
+++ b/core/sys/darwin/Foundation/NSSavePanel.odin
@@ -7,3 +7,13 @@ SavePanel :: struct{ using _: Panel }
SavePanel_runModal :: proc "c" (self: ^SavePanel) -> ModalResponse {
return msgSend(ModalResponse, self, "runModal")
}
+
+@(objc_type=SavePanel, objc_name="savePanel", objc_is_class_method=true)
+SavePanel_savePanel :: proc "c" () -> ^SavePanel {
+ return msgSend(^SavePanel, SavePanel, "savePanel")
+}
+
+@(objc_type=SavePanel, objc_name="URL")
+SavePanel_URL :: proc "c" (self: ^SavePanel) -> ^URL {
+ return msgSend(^URL, self, "URL")
+}
diff --git a/core/sys/darwin/Foundation/NSString.odin b/core/sys/darwin/Foundation/NSString.odin
index a10b33fc0..eac855c3b 100644
--- a/core/sys/darwin/Foundation/NSString.odin
+++ b/core/sys/darwin/Foundation/NSString.odin
@@ -134,6 +134,11 @@ String_isEqualToString :: proc "c" (self, other: ^String) -> BOOL {
return msgSend(BOOL, self, "isEqualToString:", other)
}
+@(objc_type=String, objc_name="stringByAppendingString")
+String_stringByAppendingString :: proc "c" (self, other: ^String) -> ^String {
+ return msgSend(^String, self, "stringByAppendingString:", other)
+}
+
@(objc_type=String, objc_name="rangeOfString")
String_rangeOfString :: proc "c" (self, other: ^String, options: StringCompareOptions) -> Range {
return msgSend(Range, self, "rangeOfString:options:", other, options)
diff --git a/core/sys/darwin/Foundation/NSToolbar.odin b/core/sys/darwin/Foundation/NSToolbar.odin
new file mode 100644
index 000000000..be6613df4
--- /dev/null
+++ b/core/sys/darwin/Foundation/NSToolbar.odin
@@ -0,0 +1,14 @@
+package objc_Foundation
+@(objc_class = "NSToolbar")
+
+Toolbar :: struct { using _: Object }
+
+@(objc_type = Toolbar, objc_name = "alloc", objc_is_class_method = true)
+Toolbar_alloc :: proc "c" () -> ^Toolbar {
+ return msgSend(^Toolbar, Toolbar, "alloc")
+}
+
+@(objc_type = Toolbar, objc_name = "init")
+Toolbar_init :: proc "c" (self: ^Toolbar) -> ^Toolbar {
+ return msgSend(^Toolbar, self, "init")
+}
diff --git a/core/sys/darwin/Foundation/NSURL.odin b/core/sys/darwin/Foundation/NSURL.odin
index 9e9081219..fb9ebca9e 100644
--- a/core/sys/darwin/Foundation/NSURL.odin
+++ b/core/sys/darwin/Foundation/NSURL.odin
@@ -28,3 +28,8 @@ URL_initFileURLWithPath :: proc "c" (self: ^URL, path: ^String) -> ^URL {
URL_fileSystemRepresentation :: proc "c" (self: ^URL) -> cstring {
return msgSend(cstring, self, "fileSystemRepresentation")
}
+
+@(objc_type=URL, objc_name="relativePath")
+URL_relativePath :: proc "c" (self: ^URL) -> ^String {
+ return msgSend(^String, self, "relativePath")
+}
diff --git a/core/sys/darwin/Foundation/NSURLRequest.odin b/core/sys/darwin/Foundation/NSURLRequest.odin
new file mode 100644
index 000000000..6b2819c67
--- /dev/null
+++ b/core/sys/darwin/Foundation/NSURLRequest.odin
@@ -0,0 +1,24 @@
+package objc_Foundation
+
+@(objc_class = "URLRequest")
+URLRequest :: struct { using _: Object }
+
+@(objc_type = URLRequest, objc_name = "alloc", objc_is_class_method = true)
+URLRequest_alloc :: proc "c" () -> ^URLRequest {
+ return msgSend(^URLRequest, URLRequest, "alloc")
+}
+
+@(objc_type = URLRequest, objc_name = "requestWithURL", objc_is_class_method = true)
+URLRequest_requestWithURL :: proc "c" (url: ^URL) -> ^URLRequest {
+ return msgSend(^URLRequest, URLRequest, "requestWithURL:", url)
+}
+
+@(objc_type = URLRequest, objc_name = "init")
+URLRequest_init :: proc "c" (self: ^URLRequest) -> ^URLRequest {
+ return msgSend(^URLRequest, URLRequest, "init")
+}
+
+@(objc_type = URLRequest, objc_name = "url")
+URLRequest_url :: proc "c" (self: ^URLRequest) -> ^URL {
+ return msgSend(^URL, self, "URL")
+} \ No newline at end of file
diff --git a/core/sys/darwin/Foundation/NSURLResponse.odin b/core/sys/darwin/Foundation/NSURLResponse.odin
new file mode 100644
index 000000000..6295817e8
--- /dev/null
+++ b/core/sys/darwin/Foundation/NSURLResponse.odin
@@ -0,0 +1,19 @@
+package objc_Foundation
+
+@(objc_class = "NSURLResponse")
+URLResponse :: struct { using _: Object }
+
+@(objc_type = URLResponse, objc_name = "alloc", objc_is_class_method = true)
+URLResponse_alloc :: proc "c" () -> ^URLResponse {
+ return msgSend(^URLResponse, URLResponse, "alloc")
+}
+
+@(objc_type = URLResponse, objc_name = "init")
+URLResponse_init :: proc "c" (self: ^URLResponse) -> ^URLResponse {
+ return msgSend(^URLResponse, URLResponse, "init")
+}
+
+@(objc_type = URLResponse, objc_name = "initWithURL")
+URLResponse_initWithURL :: proc "c" (self: ^URLResponse, url: ^URL, mime_type: ^String, length: int, encoding: ^String ) -> ^URLResponse {
+ return msgSend(^URLResponse, self, "initWithURL:MIMEType:expectedContentLength:textEncodingName:", url, mime_type, Integer(length), encoding)
+} \ No newline at end of file
diff --git a/core/sys/darwin/Foundation/NSWindow.odin b/core/sys/darwin/Foundation/NSWindow.odin
index 0fe334207..57ac2b6f6 100644
--- a/core/sys/darwin/Foundation/NSWindow.odin
+++ b/core/sys/darwin/Foundation/NSWindow.odin
@@ -129,6 +129,10 @@ WindowDelegateTemplate :: struct {
windowDidExitVersionBrowser: proc(notification: ^Notification),
}
+Window_Title_Visibility :: enum UInteger {
+ Visible,
+ Hidden,
+}
WindowDelegate :: struct { using _: Object } // This is not the same as NSWindowDelegate
_WindowDelegateInternal :: struct {
@@ -616,6 +620,10 @@ View_setWantsLayer :: proc "c" (self: ^View, wantsLayer: BOOL) {
View_convertPointFromView :: proc "c" (self: ^View, point: Point, view: ^View) -> Point {
return msgSend(Point, self, "convertPoint:fromView:", point, view)
}
+@(objc_type=View, objc_name="addSubview")
+View_addSubview :: proc "c" (self: ^View, view: ^View) {
+ msgSend(nil, self, "addSubview:", view)
+}
@(objc_class="NSWindow")
Window :: struct {using _: Responder}
@@ -748,4 +756,28 @@ Window_hasTitleBar :: proc "c" (self: ^Window) -> BOOL {
@(objc_type=Window, objc_name="orderedIndex")
Window_orderedIndex :: proc "c" (self: ^Window) -> Integer {
return msgSend(Integer, self, "orderedIndex")
+}
+@(objc_type=Window, objc_name="setMinSize")
+Window_setMinSize :: proc "c" (self: ^Window, size: Size) {
+ msgSend(nil, self, "setMinSize:", size)
+}
+@(objc_type=Window, objc_name="setTitleVisibility")
+Window_setTitleVisibility :: proc "c" (self: ^Window, visibility: Window_Title_Visibility) {
+ msgSend(nil, self, "setTitleVisibility:", visibility)
+}
+@(objc_type=Window, objc_name="performZoom")
+Window_performZoom :: proc "c" (self: ^Window) {
+ msgSend(nil, self, "performZoom:", self)
+}
+@(objc_type=Window, objc_name="setFrameAutosaveName")
+NSWindow_setFrameAutosaveName :: proc "c" (self: ^Window, name: ^String) {
+ msgSend(nil, self, "setFrameAutosaveName:", name)
+}
+@(objc_type=Window, objc_name="performWindowDragWithEvent")
+Window_performWindowDragWithEvent :: proc "c" (self: ^Window, event: ^Event) {
+ msgSend(nil, self, "performWindowDragWithEvent:", event)
+}
+@(objc_type=Window, objc_name="setToolbar")
+Window_setToolbar :: proc "c" (self: ^Window, toolbar: ^Toolbar) {
+ msgSend(nil, self, "setToolbar:", toolbar)
} \ No newline at end of file
diff --git a/core/sys/haiku/errors.odin b/core/sys/haiku/errno.odin
index febe647ea..ef5a360bd 100644
--- a/core/sys/haiku/errors.odin
+++ b/core/sys/haiku/errno.odin
@@ -1,26 +1,31 @@
#+build haiku
package sys_haiku
-import "core:c"
-
-Errno :: enum c.int {
- // Error baselines
- GENERAL_ERROR_BASE = min(c.int),
- OS_ERROR_BASE = GENERAL_ERROR_BASE + 0x1000,
- APP_ERROR_BASE = GENERAL_ERROR_BASE + 0x2000,
- INTERFACE_ERROR_BASE = GENERAL_ERROR_BASE + 0x3000,
- MEDIA_ERROR_BASE = GENERAL_ERROR_BASE + 0x4000,
- TRANSLATION_ERROR_BASE = GENERAL_ERROR_BASE + 0x4800,
- MIDI_ERROR_BASE = GENERAL_ERROR_BASE + 0x5000,
- STORAGE_ERROR_BASE = GENERAL_ERROR_BASE + 0x6000,
- POSIX_ERROR_BASE = GENERAL_ERROR_BASE + 0x7000,
- MAIL_ERROR_BASE = GENERAL_ERROR_BASE + 0x8000,
- PRINT_ERROR_BASE = GENERAL_ERROR_BASE + 0x9000,
- DEVICE_ERROR_BASE = GENERAL_ERROR_BASE + 0xa000,
-
- // Developer-defined errors start at (ERRORS_END+1)
- ERRORS_END = GENERAL_ERROR_BASE + 0xffff,
+import "core:sys/posix"
+foreign import libroot "system:c"
+
+USE_POSITIVE_POSIX_ERRORS :: posix._HAIKU_USE_POSITIVE_POSIX_ERRORS
+POSIX_ERROR_FACTOR :: posix._POSIX_ERROR_FACTOR
+
+// Error baselines
+GENERAL_ERROR_BASE :: min(i32)
+OS_ERROR_BASE :: GENERAL_ERROR_BASE + 0x1000
+APP_ERROR_BASE :: GENERAL_ERROR_BASE + 0x2000
+INTERFACE_ERROR_BASE :: GENERAL_ERROR_BASE + 0x3000
+MEDIA_ERROR_BASE :: GENERAL_ERROR_BASE + 0x4000
+TRANSLATION_ERROR_BASE :: GENERAL_ERROR_BASE + 0x4800
+MIDI_ERROR_BASE :: GENERAL_ERROR_BASE + 0x5000
+STORAGE_ERROR_BASE :: GENERAL_ERROR_BASE + 0x6000
+POSIX_ERROR_BASE :: GENERAL_ERROR_BASE + 0x7000
+MAIL_ERROR_BASE :: GENERAL_ERROR_BASE + 0x8000
+PRINT_ERROR_BASE :: GENERAL_ERROR_BASE + 0x9000
+DEVICE_ERROR_BASE :: GENERAL_ERROR_BASE + 0xA000
+
+// Developer-defined errors start at (ERRORS_END+1)
+ERRORS_END :: GENERAL_ERROR_BASE + 0xFFFF
+
+Errno :: enum i32 {
// General Errors
NO_MEMORY = GENERAL_ERROR_BASE + 0,
IO_ERROR = GENERAL_ERROR_BASE + 1,
@@ -107,31 +112,95 @@ Errno :: enum c.int {
PARTIAL_READ = STORAGE_ERROR_BASE + 16,
PARTIAL_WRITE = STORAGE_ERROR_BASE + 17,
- // Some POSIX errors
- E2BIG = POSIX_ERROR_BASE + 1,
- EFBIG = POSIX_ERROR_BASE + 4,
- ENODEV = POSIX_ERROR_BASE + 7,
- ERANGE = POSIX_ERROR_BASE + 17,
- EOVERFLOW = POSIX_ERROR_BASE + 41,
- EOPNOTSUPP = POSIX_ERROR_BASE + 43,
-
- ENOSYS = POSIX_ERROR_BASE + 9,
- EAGAIN = WOULD_BLOCK,
+ EIO = posix.EIO,
+ EACCES = posix.EACCES,
+ EINVAL = posix.EINVAL,
+ ETIMEDOUT = posix.ETIMEDOUT,
+ EINTR = posix.EINTR,
+ EAGAIN = posix.EAGAIN,
+ EWOULDBLOCK = posix.EWOULDBLOCK,
+ EBUSY = posix.EBUSY,
+ EPERM = posix.EPERM,
+ EFAULT = posix.EFAULT,
+ ENOEXEC = posix.ENOEXEC,
+ EBADF = posix.EBADF,
+ EEXIST = posix.EEXIST,
+ ENOENT = posix.ENOENT,
+ ENAMETOOLONG = posix.ENAMETOOLONG,
+ ENOTDIR = posix.ENOTDIR,
+ ENOTEMPTY = posix.ENOTEMPTY,
+ ENOSPC = posix.ENOSPC,
+ EROFS = posix.EROFS,
+ EISDIR = posix.EISDIR,
+ EMFILE = posix.EMFILE,
+ EXDEV = posix.EXDEV,
+ ELOOP = posix.ELOOP,
+ EPIPE = posix.EPIPE,
+ ENOMEM = posix.ENOMEM,
+ E2BIG = posix.E2BIG,
+ ECHILD = posix.ECHILD,
+ EDEADLK = posix.EDEADLK,
+ EFBIG = posix.EFBIG,
+ EMLINK = posix.EMLINK,
+ ENFILE = posix.ENFILE,
+ ENODEV = posix.ENODEV,
+ ENOLCK = posix.ENOLCK,
+ ENOSYS = posix.ENOSYS,
+ ENOTTY = posix.ENOTTY,
+ ENXIO = posix.ENXIO,
+ ESPIPE = posix.ESPIPE,
+ ESRCH = posix.ESRCH,
+ EDOM = posix.EDOM,
+ ERANGE = posix.ERANGE,
+ EPROTOTYPE = posix.EPROTOTYPE,
+ EPROTONOSUPPORT = posix.EPROTONOSUPPORT,
+ EAFNOSUPPORT = posix.EAFNOSUPPORT,
+ EADDRINUSE = posix.EADDRINUSE,
+ EADDRNOTAVAIL = posix.EADDRNOTAVAIL,
+ ENETDOWN = posix.ENETDOWN,
+ ENETUNREACH = posix.ENETUNREACH,
+ ENETRESET = posix.ENETRESET,
+ ECONNABORTED = posix.ECONNABORTED,
+ ECONNRESET = posix.ECONNRESET,
+ EISCONN = posix.EISCONN,
+ ENOTCONN = posix.ENOTCONN,
+ ECONNREFUSED = posix.ECONNREFUSED,
+ EHOSTUNREACH = posix.EHOSTUNREACH,
+ ENOPROTOOPT = posix.ENOPROTOOPT,
+ ENOBUFS = posix.ENOBUFS,
+ EINPROGRESS = posix.EINPROGRESS,
+ EALREADY = posix.EALREADY,
+ EILSEQ = posix.EILSEQ,
+ ENOMSG = posix.ENOMSG,
+ ESTALE = posix.ESTALE,
+ EOVERFLOW = posix.EOVERFLOW,
+ EMSGSIZE = posix.EMSGSIZE,
+ EOPNOTSUPP = posix.EOPNOTSUPP,
+ ENOTSOCK = posix.ENOTSOCK,
+ EBADMSG = posix.EBADMSG,
+ ECANCELED = posix.ECANCELED,
+ EDESTADDRREQ = posix.EDESTADDRREQ,
+ EDQUOT = posix.EDQUOT,
+ EIDRM = posix.EIDRM,
+ EMULTIHOP = posix.EMULTIHOP,
+ ENODATA = posix.ENODATA,
+ ENOLINK = posix.ENOLINK,
+ ENOSR = posix.ENOSR,
+ ENOSTR = posix.ENOSTR,
+ ENOTSUP = posix.ENOTSUP,
+ EPROTO = posix.EPROTO,
+ ETIME = posix.ETIME,
+ ETXTBSY = posix.ETXTBSY,
+ ENOTRECOVERABLE = posix.ENOTRECOVERABLE,
+ EOWNERDEAD = posix.EOWNERDEAD,
// New error codes that can be mapped to POSIX errors
- TOO_MANY_ARGS_NEG = E2BIG,
- FILE_TOO_LARGE_NEG = EFBIG,
- DEVICE_NOT_FOUND_NEG = ENODEV,
- RESULT_NOT_REPRESENTABLE_NEG = ERANGE,
- BUFFER_OVERFLOW_NEG = EOVERFLOW,
- NOT_SUPPORTED_NEG = EOPNOTSUPP,
-
- TOO_MANY_ARGS_POS = -E2BIG,
- FILE_TOO_LARGE_POS = -EFBIG,
- DEVICE_NOT_FOUND_POS = -ENODEV,
- RESULT_NOT_REPRESENTABLE_POS = -ERANGE,
- BUFFER_OVERFLOW_POS = -EOVERFLOW,
- NOT_SUPPORTED_POS = -EOPNOTSUPP,
+ TOO_MANY_ARGS = POSIX_ERROR_FACTOR * E2BIG,
+ FILE_TOO_LARGE = POSIX_ERROR_FACTOR * EFBIG,
+ DEVICE_NOT_FOUND = POSIX_ERROR_FACTOR * ENODEV,
+ RESULT_NOT_REPRESENTABLE = POSIX_ERROR_FACTOR * ERANGE,
+ BUFFER_OVERFLOW = POSIX_ERROR_FACTOR * EOVERFLOW,
+ NOT_SUPPORTED = POSIX_ERROR_FACTOR * EOPNOTSUPP,
// Media Kit Errors
STREAM_NOT_FOUND = MEDIA_ERROR_BASE + 0,
@@ -226,14 +295,8 @@ Errno :: enum c.int {
ILLEGAL_DATA = TRANSLATION_ERROR_BASE + 2,
}
-errno :: #force_inline proc "contextless" () -> Errno {
- return Errno(_errnop()^)
-}
-
-foreign import libroot "system:c"
+@(default_calling_convention="c")
foreign libroot {
- _to_positive_error :: proc(error: c.int) -> c.int ---
- _to_negative_error :: proc(error: c.int) -> c.int ---
-
- _errnop :: proc() -> ^c.int ---
+ _to_positive_error :: proc(error: i32) -> i32 ---
+ _to_negative_error :: proc(error: i32) -> i32 ---
}
diff --git a/core/sys/haiku/find_directory.odin b/core/sys/haiku/find_directory.odin
index 758c4dff4..c700bd53b 100644
--- a/core/sys/haiku/find_directory.odin
+++ b/core/sys/haiku/find_directory.odin
@@ -1,9 +1,11 @@
#+build haiku
package sys_haiku
-import "core:c"
+import "base:intrinsics"
-directory_which :: enum c.int {
+foreign import libroot "system:c"
+
+directory_which :: enum i32 {
// Per volume directories
DESKTOP_DIRECTORY = 0,
TRASH_DIRECTORY,
@@ -110,17 +112,18 @@ directory_which :: enum c.int {
BEOS_SOUNDS_DIRECTORY,
}
-find_path_flags :: enum c.int {
- CREATE_DIRECTORY = 0x0001,
- CREATE_PARENT_DIRECTORY = 0x0002,
- EXISTING_ONLY = 0x0004,
+find_path_flag :: enum u32 {
+ CREATE_DIRECTORY = intrinsics.constant_log2(0x0001),
+ CREATE_PARENT_DIRECTORY = intrinsics.constant_log2(0x0002),
+ EXISTING_ONLY = intrinsics.constant_log2(0x0004),
- // find_paths() only!
- SYSTEM_ONLY = 0x0010,
- USER_ONLY = 0x0020,
+ // find_paths() only
+ SYSTEM_ONLY = intrinsics.constant_log2(0x0010),
+ USER_ONLY = intrinsics.constant_log2(0x0020),
}
+find_path_flags :: distinct bit_set[find_path_flag; u32]
-path_base_directory :: enum c.int {
+path_base_directory :: enum i32 {
INSTALLATION_LOCATION_DIRECTORY,
ADD_ONS_DIRECTORY,
APPS_DIRECTORY,
@@ -146,7 +149,7 @@ path_base_directory :: enum c.int {
TRANSLATORS_DIRECTORY,
VAR_DIRECTORY,
- // find_path() only!
+ // find_path() only
IMAGE_PATH = 1000,
PACKAGE_PATH,
}
@@ -154,15 +157,15 @@ path_base_directory :: enum c.int {
// value that can be used instead of a pointer to a symbol in the program image
APP_IMAGE_SYMBOL :: rawptr(addr_t(0))
// pointer to a symbol in the callers image (same as B_CURRENT_IMAGE_SYMBOL)
-current_image_symbol :: proc() -> rawptr { return rawptr(current_image_symbol) }
+current_image_symbol :: proc "contextless" () -> rawptr { return rawptr(current_image_symbol) }
-foreign import libroot "system:c"
+@(default_calling_convention="c")
foreign libroot {
- find_directory :: proc(which: directory_which, volume: dev_t, createIt: bool, pathString: [^]c.char, length: i32) -> status_t ---
- find_path :: proc(codePointer: rawptr, baseDirectory: path_base_directory, subPath: cstring, pathBuffer: [^]c.char, bufferSize: c.size_t) -> status_t ---
- find_path_etc :: proc(codePointer: rawptr, dependency: cstring, architecture: cstring, baseDirectory: path_base_directory, subPath: cstring, flags: find_path_flags, pathBuffer: [^]c.char, bufferSize: c.size_t) -> status_t ---
- find_path_for_path :: proc(path: cstring, baseDirectory: path_base_directory, subPath: cstring, pathBuffer: [^]c.char, bufferSize: c.size_t) -> status_t ---
- find_path_for_path_etc :: proc(path: cstring, dependency: cstring, architecture: cstring, baseDirectory: path_base_directory, subPath: cstring, flags: find_path_flags, pathBuffer: [^]c.char, bufferSize: c.size_t) -> status_t ---
- find_paths :: proc(baseDirectory: path_base_directory, subPath: cstring, _paths: ^[^][^]c.char, _pathCount: ^c.size_t) -> status_t ---
- find_paths_etc :: proc(architecture: cstring, baseDirectory: path_base_directory, subPath: cstring, flags: find_path_flags, _paths: ^[^][^]c.char, _pathCount: ^c.size_t) -> status_t ---
+ find_directory :: proc(which: directory_which, volume: dev_t, createIt: bool, pathString: [^]byte, length: i32) -> status_t ---
+ find_path :: proc(codePointer: rawptr, baseDirectory: path_base_directory, subPath: cstring, pathBuffer: [^]byte, bufferSize: uint) -> status_t ---
+ find_path_etc :: proc(codePointer: rawptr, dependency: cstring, architecture: cstring, baseDirectory: path_base_directory, subPath: cstring, flags: find_path_flags, pathBuffer: [^]byte, bufferSize: uint) -> status_t ---
+ find_path_for_path :: proc(path: cstring, baseDirectory: path_base_directory, subPath: cstring, pathBuffer: [^]byte, bufferSize: uint) -> status_t ---
+ find_path_for_path_etc :: proc(path: cstring, dependency: cstring, architecture: cstring, baseDirectory: path_base_directory, subPath: cstring, flags: find_path_flags, pathBuffer: [^]byte, bufferSize: uint) -> status_t ---
+ find_paths :: proc(baseDirectory: path_base_directory, subPath: cstring, _paths: ^[^][^]byte, _pathCount: ^uint) -> status_t ---
+ find_paths_etc :: proc(architecture: cstring, baseDirectory: path_base_directory, subPath: cstring, flags: find_path_flags, _paths: ^[^][^]byte, _pathCount: ^uint) -> status_t ---
}
diff --git a/core/sys/haiku/os.odin b/core/sys/haiku/os.odin
index 6ab3ef573..3edee88b5 100644
--- a/core/sys/haiku/os.odin
+++ b/core/sys/haiku/os.odin
@@ -1,8 +1,8 @@
#+build haiku
package sys_haiku
-import "core:c"
-import "core:sys/unix"
+import "base:intrinsics"
+import "core:sys/posix"
foreign import libroot "system:c"
@@ -18,8 +18,8 @@ OS_NAME_LENGTH :: 32
area_info :: struct {
area: area_id,
- name: [OS_NAME_LENGTH]c.char,
- size: c.size_t,
+ name: [OS_NAME_LENGTH]byte,
+ size: uint,
lock: u32,
protection: u32,
team: team_id,
@@ -31,11 +31,11 @@ area_info :: struct {
}
area_locking :: enum u32 {
- NO_LOCK = 0,
- LAZY_LOCK = 1,
- FULL_LOCK = 2,
- CONTIGUOUS = 3,
- LOMEM = 4, // CONTIGUOUS, < 16 MB physical address
+ NO_LOCK = 0,
+ LAZY_LOCK = 1,
+ FULL_LOCK = 2,
+ CONTIGUOUS = 3,
+ LOMEM = 4, // CONTIGUOUS, < 16 MB physical address
_32_BIT_FULL_LOCK = 5, // FULL_LOCK, < 4 GB physical addresses
_32_BIT_CONTIGUOUS = 6, // CONTIGUOUS, < 4 GB physical address
}
@@ -52,27 +52,29 @@ address_spec :: enum u32 {
RANDOMIZED_BASE_ADDRESS = 7,
}
-area_protection_flags :: enum u32 {
- READ_AREA = 1 << 0,
- WRITE_AREA = 1 << 1,
- EXECUTE_AREA = 1 << 2,
+area_protection_flag :: enum u32 {
+ READ_AREA = 0,
+ WRITE_AREA = 1,
+ EXECUTE_AREA = 2,
// "stack" protection is not available on most platforms - it's used
// to only commit memory as needed, and have guard pages at the
// bottom of the stack.
- STACK_AREA = 1 << 3,
- CLONEABLE_AREA = 1 << 8,
+ STACK_AREA = 3,
+ CLONEABLE_AREA = 8,
}
+area_protection_flags :: distinct bit_set[area_protection_flag; u32]
+@(default_calling_convention="c")
foreign libroot {
- create_area :: proc(name: cstring, startAddress: ^rawptr, addressSpec: address_spec, size: c.size_t, lock: area_locking, protection: area_protection_flags) -> area_id ---
+ create_area :: proc(name: cstring, startAddress: ^rawptr, addressSpec: address_spec, size: uint, lock: area_locking, protection: area_protection_flags) -> area_id ---
clone_area :: proc(name: cstring, destAddress: ^rawptr, addressSpec: address_spec, protection: area_protection_flags, source: area_id) -> area_id ---
find_area :: proc(name: cstring) -> area_id ---
area_for :: proc(address: rawptr) -> area_id ---
delete_area :: proc(id: area_id) -> status_t ---
- resize_area :: proc(id: area_id, newSize: c.size_t) -> status_t ---
+ resize_area :: proc(id: area_id, newSize: uint) -> status_t ---
set_area_protection :: proc(id: area_id, newProtection: area_protection_flags) -> status_t ---
- _get_area_info :: proc(id: area_id, areaInfo: ^area_info, size: c.size_t) -> status_t ---
- _get_next_area_info :: proc(team: team_id, cookie: ^c.ssize_t, areaInfo: ^area_info, size: c.size_t) -> status_t ---
+ _get_area_info :: proc(id: area_id, areaInfo: ^area_info, size: uint) -> status_t ---
+ _get_next_area_info :: proc(team: team_id, cookie: ^int, areaInfo: ^area_info, size: uint) -> status_t ---
}
// Ports
@@ -80,33 +82,35 @@ foreign libroot {
port_info :: struct {
port: port_id,
team: team_id,
- name: [OS_NAME_LENGTH]c.char,
+ name: [OS_NAME_LENGTH]byte,
capacity: i32, // queue depth
queue_count: i32, // # msgs waiting to be read
total_count: i32, // total # msgs read so far
}
-port_flags :: enum u32 {
- USE_USER_MEMCPY = 0x80000000,
+port_flag :: enum u32 {
+ USE_USER_MEMCPY = intrinsics.constant_log2(0x80000000),
// read the message, but don't remove it; kernel-only; memory must be locked
- PEEK_PORT_MESSAGE = 0x100,
+ PEEK_PORT_MESSAGE = intrinsics.constant_log2(0x100),
}
+port_flags :: distinct bit_set[port_flag; u32]
+@(default_calling_convention="c")
foreign libroot {
create_port :: proc(capacity: i32, name: cstring) -> port_id ---
find_port :: proc(name: cstring) -> port_id ---
- read_port :: proc(port: port_id, code: ^i32, buffer: rawptr, bufferSize: c.size_t) -> c.ssize_t ---
- read_port_etc :: proc(port: port_id, code: ^i32, buffer: rawptr, bufferSize: c.size_t, flags: port_flags, timeout: bigtime_t) -> c.ssize_t ---
- write_port :: proc(port: port_id, code: i32, buffer: rawptr, bufferSize: c.size_t) -> status_t ---
- write_port_etc :: proc(port: port_id, code: i32, buffer: rawptr, bufferSize: c.size_t, flags: port_flags, timeout: bigtime_t) -> status_t ---
+ read_port :: proc(port: port_id, code: ^i32, buffer: rawptr, bufferSize: uint) -> int ---
+ read_port_etc :: proc(port: port_id, code: ^i32, buffer: rawptr, bufferSize: uint, flags: port_flags, timeout: bigtime_t) -> int ---
+ write_port :: proc(port: port_id, code: i32, buffer: rawptr, bufferSize: uint) -> status_t ---
+ write_port_etc :: proc(port: port_id, code: i32, buffer: rawptr, bufferSize: uint, flags: port_flags, timeout: bigtime_t) -> status_t ---
close_port :: proc(port: port_id) -> status_t ---
delete_port :: proc(port: port_id) -> status_t ---
- port_buffer_size :: proc(port: port_id) -> c.ssize_t ---
- port_buffer_size_etc :: proc(port: port_id, flags: port_flags, timeout: bigtime_t) -> c.ssize_t ---
- port_count :: proc(port: port_id) -> c.ssize_t ---
+ port_buffer_size :: proc(port: port_id) -> int ---
+ port_buffer_size_etc :: proc(port: port_id, flags: port_flags, timeout: bigtime_t) -> int ---
+ port_count :: proc(port: port_id) -> int ---
set_port_owner :: proc(port: port_id, team: team_id) -> status_t ---
- _get_port_info :: proc(port: port_id, portInfo: ^port_info, portInfoSize: c.size_t) -> status_t ---
- _get_next_port_info :: proc(team: team_id, cookie: ^i32, portInfo: ^port_info, portInfoSize: c.size_t) -> status_t ---
+ _get_port_info :: proc(port: port_id, portInfo: ^port_info, portInfoSize: uint) -> status_t ---
+ _get_next_port_info :: proc(team: team_id, cookie: ^i32, portInfo: ^port_info, portInfoSize: uint) -> status_t ---
}
// Semaphores
@@ -114,22 +118,24 @@ foreign libroot {
sem_info :: struct {
sem: sem_id,
team: team_id,
- name: [OS_NAME_LENGTH]c.char,
+ name: [OS_NAME_LENGTH]byte,
count: i32,
latest_holder: thread_id,
}
-semaphore_flags :: enum u32 {
- CAN_INTERRUPT = 0x01, // acquisition of the semaphore can be interrupted (system use only)
- CHECK_PERMISSION = 0x04, // ownership will be checked (system use only)
- KILL_CAN_INTERRUPT = 0x20, // acquisition of the semaphore can be interrupted by SIGKILL[THR], even if not CAN_INTERRUPT (system use only)
+semaphore_flag :: enum u32 {
+ CAN_INTERRUPT = intrinsics.constant_log2(0x01), // acquisition of the semaphore can be interrupted (system use only)
+ CHECK_PERMISSION = intrinsics.constant_log2(0x04), // ownership will be checked (system use only)
+ KILL_CAN_INTERRUPT = intrinsics.constant_log2(0x20), // acquisition of the semaphore can be interrupted by SIGKILL[THR], even if not CAN_INTERRUPT (system use only)
// release_sem_etc() only flags
- DO_NOT_RESCHEDULE = 0x02, // thread is not rescheduled
- RELEASE_ALL = 0x08, // all waiting threads will be woken up, count will be zeroed
- RELEASE_IF_WAITING_ONLY = 0x10, // release count only if there are any threads waiting
+ DO_NOT_RESCHEDULE = intrinsics.constant_log2(0x02), // thread is not rescheduled
+ RELEASE_ALL = intrinsics.constant_log2(0x08), // all waiting threads will be woken up, count will be zeroed
+ RELEASE_IF_WAITING_ONLY = intrinsics.constant_log2(0x10), // release count only if there are any threads waiting
}
+semaphore_flags :: distinct bit_set[semaphore_flag; u32]
+@(default_calling_convention="c")
foreign libroot {
create_sem :: proc(count: i32, name: cstring) -> sem_id ---
delete_sem :: proc(id: sem_id) -> status_t ---
@@ -141,8 +147,8 @@ foreign libroot {
switch_sem_etc :: proc(semToBeReleased: sem_id, id: sem_id, count: i32, flags: semaphore_flags, timeout: bigtime_t) -> status_t ---
get_sem_count :: proc(id: sem_id, threadCount: ^i32) -> status_t ---
set_sem_owner :: proc(id: sem_id, team: team_id) -> status_t ---
- _get_sem_info :: proc(id: sem_id, info: ^sem_info, infoSize: c.size_t) -> status_t ---
- _get_next_sem_info :: proc(team: team_id, cookie: ^i32, info: ^sem_info, infoSize: c.size_t) -> status_t ---
+ _get_sem_info :: proc(id: sem_id, info: ^sem_info, infoSize: uint) -> status_t ---
+ _get_next_sem_info :: proc(team: team_id, cookie: ^i32, info: ^sem_info, infoSize: uint) -> status_t ---
}
// Teams
@@ -155,7 +161,7 @@ team_info :: struct {
debugger_nub_thread: thread_id,
debugger_nub_port: port_id,
argc: i32,
- args: [64]c.char,
+ args: [64]byte,
uid: uid_t,
gid: gid_t,
@@ -165,7 +171,7 @@ team_info :: struct {
group_id: pid_t,
session_id: pid_t,
parent: team_id,
- name: [OS_NAME_LENGTH]c.char,
+ name: [OS_NAME_LENGTH]byte,
start_time: bigtime_t,
}
@@ -183,17 +189,18 @@ team_usage_who :: enum i32 {
CHILDREN = -1,
}
+@(default_calling_convention="c")
foreign libroot {
// see also: send_signal()
kill_team :: proc(team: team_id) -> status_t ---
- _get_team_info :: proc(id: team_id, info: ^team_info, size: c.size_t) -> status_t ---
- _get_next_team_info :: proc(cookie: ^i32, info: ^team_info, size: c.size_t) -> status_t ---
- _get_team_usage_info :: proc(id: team_id, who: team_usage_who, info: ^team_usage_info, size: c.size_t) -> status_t ---
+ _get_team_info :: proc(id: team_id, info: ^team_info, size: uint) -> status_t ---
+ _get_next_team_info :: proc(cookie: ^i32, info: ^team_info, size: uint) -> status_t ---
+ _get_team_usage_info :: proc(id: team_id, who: team_usage_who, info: ^team_usage_info, size: uint) -> status_t ---
}
// Threads
-thread_state :: enum c.int {
+thread_state :: enum i32 {
RUNNING = 1,
READY,
RECEIVING,
@@ -205,7 +212,7 @@ thread_state :: enum c.int {
thread_info :: struct {
thread: thread_id,
team: team_id,
- name: [OS_NAME_LENGTH]c.char,
+ name: [OS_NAME_LENGTH]byte,
state: thread_state,
priority: thread_priority,
sem: sem_id,
@@ -234,6 +241,7 @@ SYSTEM_TIMEBASE :: 0
thread_func :: #type proc "c" (rawptr) -> status_t
+@(default_calling_convention="c")
foreign libroot {
spawn_thread :: proc(thread_func, name: cstring, priority: thread_priority, data: rawptr) -> thread_id ---
kill_thread :: proc(thread: thread_id) -> status_t ---
@@ -247,24 +255,25 @@ foreign libroot {
wait_for_thread_etc :: proc(id: thread_id, flags: u32, timeout: bigtime_t, _returnCode: ^status_t) -> status_t ---
on_exit_thread :: proc(callback: proc "c" (rawptr), data: rawptr) -> status_t ---
find_thread :: proc(name: cstring) -> thread_id ---
- send_data :: proc(thread: thread_id, code: i32, buffer: rawptr, bufferSize: c.size_t) -> status_t ---
- receive_data :: proc(sender: ^thread_id, buffer: rawptr, bufferSize: c.size_t) -> i32 ---
+ send_data :: proc(thread: thread_id, code: i32, buffer: rawptr, bufferSize: uint) -> status_t ---
+ receive_data :: proc(sender: ^thread_id, buffer: rawptr, bufferSize: uint) -> i32 ---
has_data :: proc(thread: thread_id) -> bool ---
snooze :: proc(amount: bigtime_t) -> status_t ---
// FIXME: Find and define those flags.
- snooze_etc :: proc(amount: bigtime_t, timeBase: c.int, flags: u32) -> status_t ---
- snooze_until :: proc(time: bigtime_t, timeBase: c.int) -> status_t ---
- _get_thread_info :: proc(id: thread_id, info: ^thread_info, size: c.size_t) -> status_t ---
- _get_next_thread_info :: proc(team: team_id, cookie: ^i32, info: ^thread_info, size: c.size_t) -> status_t ---
+ snooze_etc :: proc(amount: bigtime_t, timeBase: i32, flags: u32) -> status_t ---
+ snooze_until :: proc(time: bigtime_t, timeBase: i32) -> status_t ---
+ _get_thread_info :: proc(id: thread_id, info: ^thread_info, size: uint) -> status_t ---
+ _get_next_thread_info :: proc(team: team_id, cookie: ^i32, info: ^thread_info, size: uint) -> status_t ---
// bridge to the pthread API
get_pthread_thread_id :: proc(thread: pthread_t) -> thread_id ---
}
// Time
+@(default_calling_convention="c")
foreign libroot {
- real_time_clock :: proc() -> c.ulong ---
- set_real_time_clock :: proc(secsSinceJan1st1970: c.ulong) ---
+ real_time_clock :: proc() -> uint ---
+ set_real_time_clock :: proc(secsSinceJan1st1970: uint) ---
real_time_clock_usecs :: proc() -> bigtime_t ---
// time since booting in microseconds
system_time :: proc() -> bigtime_t ---
@@ -280,12 +289,14 @@ alarm_mode :: enum u32 {
PERIODIC_ALARM, // "when" specifies the period
}
+@(default_calling_convention="c")
foreign libroot {
set_alarm :: proc(_when: bigtime_t, mode: alarm_mode) -> bigtime_t ---
}
// Debugger
+@(default_calling_convention="c")
foreign libroot {
debugger :: proc(message: cstring) ---
/*
@@ -296,7 +307,7 @@ foreign libroot {
to re-enable the default debugger pass a zero.
*/
- disable_debugger :: proc(state: c.int) -> c.int ---
+ disable_debugger :: proc(state: i32) -> i32 ---
}
// System information
@@ -338,15 +349,15 @@ system_info :: struct {
max_teams: u32,
used_teams: u32,
- kernel_name: [FILE_NAME_LENGTH]c.char,
- kernel_build_date: [OS_NAME_LENGTH]c.char,
- kernel_build_time: [OS_NAME_LENGTH]c.char,
+ kernel_name: [FILE_NAME_LENGTH]byte,
+ kernel_build_date: [OS_NAME_LENGTH]byte,
+ kernel_build_time: [OS_NAME_LENGTH]byte,
kernel_version: i64,
abi: u32, // the system API
}
-topology_level_type :: enum c.int {
+topology_level_type :: enum i32 {
UNKNOWN,
ROOT,
SMT,
@@ -354,7 +365,7 @@ topology_level_type :: enum c.int {
PACKAGE,
}
-cpu_platform :: enum c.int {
+cpu_platform :: enum i32 {
UNKNOWN,
x86,
x86_64,
@@ -370,7 +381,7 @@ cpu_platform :: enum c.int {
RISC_V,
}
-cpu_vendor :: enum c.int {
+cpu_vendor :: enum i32 {
UNKNOWN,
AMD,
CYRIX,
@@ -408,95 +419,80 @@ cpu_topology_node_info :: struct {
},
}
-// FIXME: Add cpuid_info when bit fields are ready.
+when ODIN_ARCH == .amd64 || ODIN_ARCH == .i386 {
+ cpuid_info :: struct #raw_union {
+ eax_0: struct {
+ max_eax: u32,
+ vendor_id: [12]byte,
+ },
+
+ eax_1: struct {
+ using _: bit_field u32 {
+ stepping: u32 | 4,
+ model: u32 | 4,
+ family: u32 | 4,
+ type: u32 | 2,
+ reserved_0: u32 | 2,
+ extended_model: u32 | 4,
+ extended_family: u32 | 8,
+ reserved_1: u32 | 4,
+ },
+
+ using _: bit_field u32 {
+ brand_index: u32 | 8,
+ clflush: u32 | 8,
+ logical_cpus: u32 | 8,
+ apic_id: u32 | 8,
+ },
+
+ features: u32,
+ extended_features: u32,
+ },
+
+ eax_2: struct {
+ call_num: u8,
+ cache_descriptors: [15]u8,
+ },
+
+ eax_3: struct {
+ reserved: [2]u32,
+ serial_number_high: u32,
+ serial_number_low: u32,
+ },
+
+ as_chars: [16]byte,
+ regs: struct {
+ eax: u32,
+ ebx: u32,
+ edx: u32,
+ ecx: u32,
+ },
+ }
+}
+
+@(default_calling_convention="c")
foreign libroot {
get_system_info :: proc(info: ^system_info) -> status_t ---
- _get_cpu_info_etc :: proc(firstCPU: u32, cpuCount: u32, info: ^cpu_info, size: c.size_t) -> status_t ---
+ _get_cpu_info_etc :: proc(firstCPU: u32, cpuCount: u32, info: ^cpu_info, size: uint) -> status_t ---
get_cpu_topology_info :: proc(topologyInfos: [^]cpu_topology_node_info, topologyInfoCount: ^u32) -> status_t ---
- is_computer_on :: proc() -> i32 ---
- is_computer_on_fire :: proc() -> f64 ---
-}
-
-// Signal.h
-
-SIG_BLOCK :: 1
-SIG_UNBLOCK :: 2
-SIG_SETMASK :: 3
-
-/*
- * The list of all defined signals:
- *
- * The numbering of signals for Haiku attempts to maintain
- * some consistency with UN*X conventions so that things
- * like "kill -9" do what you expect.
- */
-
-SIGHUP :: 1 // hangup -- tty is gone!
-SIGINT :: 2 // interrupt
-SIGQUIT :: 3 // `quit' special character typed in tty
-SIGILL :: 4 // illegal instruction
-SIGCHLD :: 5 // child process exited
-SIGABRT :: 6 // abort() called, dont' catch
-SIGPIPE :: 7 // write to a pipe w/no readers
-SIGFPE :: 8 // floating point exception
-SIGKILL :: 9 // kill a team (not catchable)
-SIGSTOP :: 10 // suspend a thread (not catchable)
-SIGSEGV :: 11 // segmentation violation (read: invalid pointer)
-SIGCONT :: 12 // continue execution if suspended
-SIGTSTP :: 13 // `stop' special character typed in tty
-SIGALRM :: 14 // an alarm has gone off (see alarm())
-SIGTERM :: 15 // termination requested
-SIGTTIN :: 16 // read of tty from bg process
-SIGTTOU :: 17 // write to tty from bg process
-SIGUSR1 :: 18 // app defined signal 1
-SIGUSR2 :: 19 // app defined signal 2
-SIGWINCH :: 20 // tty window size changed
-SIGKILLTHR :: 21 // be specific: kill just the thread, not team
-SIGTRAP :: 22 // Trace/breakpoint trap
-SIGPOLL :: 23 // Pollable event
-SIGPROF :: 24 // Profiling timer expired
-SIGSYS :: 25 // Bad system call
-SIGURG :: 26 // High bandwidth data is available at socket
-SIGVTALRM :: 27 // Virtual timer expired
-SIGXCPU :: 28 // CPU time limit exceeded
-SIGXFSZ :: 29 // File size limit exceeded
-SIGBUS :: 30 // access to undefined portion of a memory object
-
-sigval :: struct #raw_union {
- sival_int: c.int,
- sival_ptr: rawptr,
-}
-
-siginfo_t :: struct {
- si_signo: c.int, // signal number
- si_code: c.int, // signal code
- si_errno: c.int, // if non zero, an error number associated with this signal
-
- si_pid: pid_t, // sending process ID
- si_uid: uid_t, // real user ID of sending process
- si_addr: rawptr, // address of faulting instruction
- si_status: c.int, // exit value or signal
- si_band: c.long, // band event for SIGPOLL
- si_value: sigval, // signal value
+ when ODIN_ARCH == .amd64 || ODIN_ARCH == .i386 {
+ get_cpuid :: proc(info: ^cpuid_info, eaxRegister: u32, cpuNum: u32) -> status_t ---
+ }
+
+ is_computer_on :: proc() -> i32 ---
+ is_computer_on_fire :: proc() -> f64 ---
}
+// POSIX signals
+
+@(default_calling_convention="c")
foreign libroot {
- // signal set (sigset_t) manipulation
- sigemptyset :: proc(set: ^sigset_t) -> c.int ---
- sigfillset :: proc(set: ^sigset_t) -> c.int ---
- sigaddset :: proc(set: ^sigset_t, _signal: c.int) -> c.int ---
- sigdelset :: proc(set: ^sigset_t, _signal: c.int) -> c.int ---
- sigismember :: proc(set: ^sigset_t, _signal: c.int) -> c.int ---
- // querying and waiting for signals
- sigpending :: proc(set: ^sigset_t) -> c.int ---
- sigsuspend :: proc(mask: ^sigset_t) -> c.int ---
- sigpause :: proc(_signal: c.int) -> c.int ---
- sigwait :: proc(set: ^sigset_t, _signal: ^c.int) -> c.int ---
- sigwaitinfo :: proc(set: ^sigset_t, info: ^siginfo_t) -> c.int ---
- sigtimedwait :: proc(set: ^sigset_t, info: ^siginfo_t, timeout: ^unix.timespec) -> c.int ---
-
- send_signal :: proc(threadID: thread_id, signal: c.uint) -> c.int ---
- set_signal_stack :: proc(base: rawptr, size: c.size_t) ---
+ /*
+ Wait for queued signals.
+
+ [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/sigtimedwait.html ]]
+ */
+ sigtimedwait :: proc(set: ^posix.sigset_t, info: ^posix.siginfo_t, timeout: ^posix.timespec) -> posix.result ---
}
diff --git a/core/sys/haiku/types.odin b/core/sys/haiku/types.odin
index 47755b0b7..f15ffb00d 100644
--- a/core/sys/haiku/types.odin
+++ b/core/sys/haiku/types.odin
@@ -1,9 +1,7 @@
#+build haiku
package sys_haiku
-import "core:c"
-
-status_t :: i32
+status_t :: Errno
bigtime_t :: i64
nanotime_t :: i64
type_code :: u32
@@ -37,16 +35,20 @@ mode_t :: u32
umode_t :: u32
nlink_t :: i32
-caddr_t :: ^c.char
+caddr_t :: [^]byte
addr_t :: phys_addr_t
key_t :: i32
clockid_t :: i32
-time_t :: i64 when ODIN_ARCH == .amd64 || ODIN_ARCH == .arm64 else i32
+time_t :: int
+timespec :: struct {
+ tv_sec: time_t,
+ tv_nsec: int,
+}
-sig_atomic_t :: c.int
+sig_atomic_t :: i32
sigset_t :: u64
image_id :: i32
diff --git a/core/sys/linux/bits.odin b/core/sys/linux/bits.odin
index 9ce2e206e..4493ea767 100644
--- a/core/sys/linux/bits.odin
+++ b/core/sys/linux/bits.odin
@@ -152,43 +152,65 @@ Errno :: enum i32 {
RDONLY flag is not present, because it has the value of 0, i.e. it is the
default, unless WRONLY or RDWR is specified.
*/
-Open_Flags_Bits :: enum {
- WRONLY = 0,
- RDWR = 1,
- CREAT = 6,
- EXCL = 7,
- NOCTTY = 8,
- TRUNC = 9,
- APPEND = 10,
- NONBLOCK = 11,
- DSYNC = 12,
- ASYNC = 13,
- DIRECT = 14,
- LARGEFILE = 15,
- DIRECTORY = 16,
- NOFOLLOW = 17,
- NOATIME = 18,
- CLOEXEC = 19,
- PATH = 21,
-}
-// https://github.com/torvalds/linux/blob/7367539ad4b0f8f9b396baf02110962333719a48/include/uapi/asm-generic/fcntl.h#L19
-#assert(1 << uint(Open_Flags_Bits.WRONLY) == 0o0000000_1)
-#assert(1 << uint(Open_Flags_Bits.RDWR) == 0o0000000_2)
-#assert(1 << uint(Open_Flags_Bits.CREAT) == 0o00000_100)
-#assert(1 << uint(Open_Flags_Bits.EXCL) == 0o00000_200)
-#assert(1 << uint(Open_Flags_Bits.NOCTTY) == 0o00000_400)
-#assert(1 << uint(Open_Flags_Bits.TRUNC) == 0o0000_1000)
-#assert(1 << uint(Open_Flags_Bits.APPEND) == 0o0000_2000)
-#assert(1 << uint(Open_Flags_Bits.NONBLOCK) == 0o0000_4000)
-#assert(1 << uint(Open_Flags_Bits.DSYNC) == 0o000_10000)
-#assert(1 << uint(Open_Flags_Bits.ASYNC) == 0o000_20000)
-#assert(1 << uint(Open_Flags_Bits.DIRECT) == 0o000_40000)
-#assert(1 << uint(Open_Flags_Bits.LARGEFILE) == 0o00_100000)
-#assert(1 << uint(Open_Flags_Bits.DIRECTORY) == 0o00_200000)
-#assert(1 << uint(Open_Flags_Bits.NOFOLLOW) == 0o00_400000)
-#assert(1 << uint(Open_Flags_Bits.NOATIME) == 0o0_1000000)
-#assert(1 << uint(Open_Flags_Bits.CLOEXEC) == 0o0_2000000)
-#assert(1 << uint(Open_Flags_Bits.PATH) == 0o_10000000)
+when ODIN_ARCH != .arm64 && ODIN_ARCH != .arm32 {
+ Open_Flags_Bits :: enum {
+ WRONLY = 0,
+ RDWR = 1,
+ CREAT = 6,
+ EXCL = 7,
+ NOCTTY = 8,
+ TRUNC = 9,
+ APPEND = 10,
+ NONBLOCK = 11,
+ DSYNC = 12,
+ ASYNC = 13,
+ DIRECT = 14,
+ LARGEFILE = 15,
+ DIRECTORY = 16,
+ NOFOLLOW = 17,
+ NOATIME = 18,
+ CLOEXEC = 19,
+ PATH = 21,
+ }
+ // https://github.com/torvalds/linux/blob/7367539ad4b0f8f9b396baf02110962333719a48/include/uapi/asm-generic/fcntl.h#L19
+ #assert(1 << uint(Open_Flags_Bits.WRONLY) == 0o0000000_1)
+ #assert(1 << uint(Open_Flags_Bits.RDWR) == 0o0000000_2)
+ #assert(1 << uint(Open_Flags_Bits.CREAT) == 0o00000_100)
+ #assert(1 << uint(Open_Flags_Bits.EXCL) == 0o00000_200)
+ #assert(1 << uint(Open_Flags_Bits.NOCTTY) == 0o00000_400)
+ #assert(1 << uint(Open_Flags_Bits.TRUNC) == 0o0000_1000)
+ #assert(1 << uint(Open_Flags_Bits.APPEND) == 0o0000_2000)
+ #assert(1 << uint(Open_Flags_Bits.NONBLOCK) == 0o0000_4000)
+ #assert(1 << uint(Open_Flags_Bits.DSYNC) == 0o000_10000)
+ #assert(1 << uint(Open_Flags_Bits.ASYNC) == 0o000_20000)
+ #assert(1 << uint(Open_Flags_Bits.DIRECT) == 0o000_40000)
+ #assert(1 << uint(Open_Flags_Bits.LARGEFILE) == 0o00_100000)
+ #assert(1 << uint(Open_Flags_Bits.DIRECTORY) == 0o00_200000)
+ #assert(1 << uint(Open_Flags_Bits.NOFOLLOW) == 0o00_400000)
+ #assert(1 << uint(Open_Flags_Bits.NOATIME) == 0o0_1000000)
+ #assert(1 << uint(Open_Flags_Bits.CLOEXEC) == 0o0_2000000)
+ #assert(1 << uint(Open_Flags_Bits.PATH) == 0o_10000000)
+} else {
+ Open_Flags_Bits :: enum {
+ WRONLY = 0,
+ RDWR = 1,
+ CREAT = 6,
+ EXCL = 7,
+ NOCTTY = 8,
+ TRUNC = 9,
+ APPEND = 10,
+ NONBLOCK = 11,
+ DSYNC = 12,
+ ASYNC = 13,
+ DIRECTORY = 14,
+ NOFOLLOW = 15,
+ DIRECT = 16,
+ LARGEFILE = 17,
+ NOATIME = 18,
+ CLOEXEC = 19,
+ PATH = 21,
+ }
+}
/*
Bits for FD_Flags bitset
@@ -1307,6 +1329,7 @@ Socket_Option :: enum {
ACCEPTCONN = 30,
PEERSEC = 31,
PASSSEC = 34,
+ IP_ADD_MEMBERSHIP = 35,
MARK = 36,
PROTOCOL = 38,
DOMAIN = 39,
diff --git a/core/sys/linux/helpers.odin b/core/sys/linux/helpers.odin
index aefc1179e..9a7550d57 100644
--- a/core/sys/linux/helpers.odin
+++ b/core/sys/linux/helpers.odin
@@ -138,8 +138,8 @@ errno_unwrap :: proc {errno_unwrap2, errno_unwrap3}
when size_of(int) == 4 {
// xxx64 system calls take some parameters as pairs of ulongs rather than a single pointer
@(private)
- compat64_arg_pair :: #force_inline proc "contextless" (a: i64) -> (hi: uint, lo: uint) {
- no_sign := uint(a)
+ compat64_arg_pair :: #force_inline proc "contextless" (a: i64) -> (lo: uint, hi: uint) {
+ no_sign := u64(a)
hi = uint(no_sign >> 32)
lo = uint(no_sign & 0xffff_ffff)
return
diff --git a/core/sys/linux/sys.odin b/core/sys/linux/sys.odin
index 690902f07..532c1ff5f 100644
--- a/core/sys/linux/sys.odin
+++ b/core/sys/linux/sys.odin
@@ -151,7 +151,8 @@ lseek :: proc "contextless" (fd: Fd, off: i64, whence: Seek_Whence) -> (i64, Err
return errno_unwrap(ret, i64)
} else {
result: i64 = ---
- ret := syscall(SYS__llseek, fd, compat64_arg_pair(off), &result, whence)
+ lo, hi := compat64_arg_pair(off)
+ ret := syscall(SYS__llseek, fd, hi, lo, &result, whence)
return result, Errno(-ret)
}
}
@@ -251,7 +252,11 @@ ioctl :: proc "contextless" (fd: Fd, request: u32, arg: uintptr) -> (uintptr) {
Available since Linux 2.2.
*/
pread :: proc "contextless" (fd: Fd, buf: []u8, offset: i64) -> (int, Errno) {
- ret := syscall(SYS_pread64, fd, raw_data(buf), len(buf), compat64_arg_pair(offset))
+ when ODIN_ARCH == .arm32 {
+ ret := syscall(SYS_pread64, fd, raw_data(buf), len(buf), 0, compat64_arg_pair(offset))
+ } else {
+ ret := syscall(SYS_pread64, fd, raw_data(buf), len(buf), compat64_arg_pair(offset))
+ }
return errno_unwrap(ret, int)
}
@@ -261,7 +266,11 @@ pread :: proc "contextless" (fd: Fd, buf: []u8, offset: i64) -> (int, Errno) {
Available since Linux 2.2.
*/
pwrite :: proc "contextless" (fd: Fd, buf: []u8, offset: i64) -> (int, Errno) {
- ret := syscall(SYS_pwrite64, fd, raw_data(buf), len(buf), compat64_arg_pair(offset))
+ when ODIN_ARCH == .arm32 {
+ ret := syscall(SYS_pwrite64, fd, raw_data(buf), len(buf), 0, compat64_arg_pair(offset))
+ } else {
+ ret := syscall(SYS_pwrite64, fd, raw_data(buf), len(buf), compat64_arg_pair(offset))
+ }
return errno_unwrap(ret, int)
}
@@ -1127,7 +1136,10 @@ fdatasync :: proc "contextless" (fd: Fd) -> (Errno) {
On 32-bit architectures available since Linux 2.4.
*/
truncate :: proc "contextless" (name: cstring, length: i64) -> (Errno) {
- when size_of(int) == 4 {
+ when ODIN_ARCH == .arm32 {
+ ret := syscall(SYS_truncate64, cast(rawptr) name, 0, compat64_arg_pair(length))
+ return Errno(-ret)
+ } else when size_of(int) == 4 {
ret := syscall(SYS_truncate64, cast(rawptr) name, compat64_arg_pair(length))
return Errno(-ret)
} else {
@@ -1141,7 +1153,10 @@ truncate :: proc "contextless" (name: cstring, length: i64) -> (Errno) {
On 32-bit architectures available since 2.4.
*/
ftruncate :: proc "contextless" (fd: Fd, length: i64) -> (Errno) {
- when size_of(int) == 4 {
+ when ODIN_ARCH == .arm32 {
+ ret := syscall(SYS_ftruncate64, fd, 0, compat64_arg_pair(length))
+ return Errno(-ret)
+ } else when size_of(int) == 4 {
ret := syscall(SYS_ftruncate64, fd, compat64_arg_pair(length))
return Errno(-ret)
} else {
@@ -1952,10 +1967,10 @@ sigaltstack :: proc "contextless" (stack: ^Sig_Stack, old_stack: ^Sig_Stack) ->
*/
mknod :: proc "contextless" (name: cstring, mode: Mode, dev: Dev) -> (Errno) {
when ODIN_ARCH == .arm64 || ODIN_ARCH == .riscv64 {
- ret := syscall(SYS_mknodat, AT_FDCWD, cast(rawptr) name, transmute(u32) mode, dev)
+ ret := syscall(SYS_mknodat, AT_FDCWD, cast(rawptr) name, transmute(u32) mode, cast(uint) dev)
return Errno(-ret)
} else {
- ret := syscall(SYS_mknod, cast(rawptr) name, transmute(u32) mode, dev)
+ ret := syscall(SYS_mknod, cast(rawptr) name, transmute(u32) mode, cast(uint) dev)
return Errno(-ret)
}
}
@@ -1995,10 +2010,10 @@ statfs :: proc "contextless" (path: cstring, statfs: ^Stat_FS) -> (Errno) {
*/
fstatfs :: proc "contextless" (fd: Fd, statfs: ^Stat_FS) -> (Errno) {
when size_of(int) == 8 {
- ret := syscall(SYS_statfs, fd, statfs)
+ ret := syscall(SYS_fstatfs, fd, statfs)
return Errno(-ret)
} else {
- ret := syscall(SYS_statfs64, fd, size_of(Stat_FS), statfs)
+ ret := syscall(SYS_fstatfs64, fd, size_of(Stat_FS), statfs)
return Errno(-ret)
}
}
@@ -2586,7 +2601,7 @@ mkdirat :: proc "contextless" (dirfd: Fd, name: cstring, mode: Mode) -> (Errno)
Available since Linux 2.6.16.
*/
mknodat :: proc "contextless" (dirfd: Fd, name: cstring, mode: Mode, dev: Dev) -> (Errno) {
- ret := syscall(SYS_mknodat, dirfd, cast(rawptr) name, transmute(u32) mode, dev)
+ ret := syscall(SYS_mknodat, dirfd, cast(rawptr) name, transmute(u32) mode, cast(uint) dev)
return Errno(-ret)
}
@@ -2684,13 +2699,8 @@ faccessat :: proc "contextless" (dirfd: Fd, name: cstring, mode: Mode = F_OK) ->
Available since Linux 2.6.16.
*/
ppoll :: proc "contextless" (fds: []Poll_Fd, timeout: ^Time_Spec, sigmask: ^Sig_Set) -> (i32, Errno) {
- when size_of(int) == 8 {
- ret := syscall(SYS_ppoll, raw_data(fds), len(fds), timeout, sigmask, size_of(Sig_Set))
- return errno_unwrap(ret, i32)
- } else {
- ret := syscall(SYS_ppoll_time64, raw_data(fds), len(fds), timeout, sigmask, size_of(Sig_Set))
- return errno_unwrap(ret, i32)
- }
+ ret := syscall(SYS_ppoll, raw_data(fds), len(fds), timeout, sigmask, size_of(Sig_Set))
+ return errno_unwrap(ret, i32)
}
// TODO(flysand): unshare
diff --git a/core/sys/linux/types.odin b/core/sys/linux/types.odin
index 42d5cc988..dcc72f72b 100644
--- a/core/sys/linux/types.odin
+++ b/core/sys/linux/types.odin
@@ -3,7 +3,7 @@ package linux
/*
Type for storage device handle.
*/
-Dev :: distinct int
+Dev :: distinct u64
/*
Type for 32-bit User IDs.
@@ -153,6 +153,7 @@ when ODIN_ARCH == .amd64 {
uid: Uid,
gid: Gid,
rdev: Dev,
+ _: [4]u8,
size: i64,
blksize: uint,
blocks: u64,
@@ -516,79 +517,79 @@ Pid_FD_Flags :: bit_set[Pid_FD_Flags_Bits; i32]
Sig_Set :: [_SIGSET_NWORDS]uint
@private SI_MAX_SIZE :: 128
-@private SI_ARCH_PREAMBLE :: 4 * size_of(i32)
+@private SI_ARCH_PREAMBLE :: 4 * size_of(i32) when size_of(rawptr) == 8 else 3 * size_of(i32)
@private SI_PAD_SIZE :: SI_MAX_SIZE - SI_ARCH_PREAMBLE
Sig_Handler_Fn :: #type proc "c" (sig: Signal)
Sig_Restore_Fn :: #type proc "c" () -> !
-Sig_Info :: struct #packed {
- signo: Signal,
- errno: Errno,
- code: i32,
- _pad0: i32,
- using _union: struct #raw_union {
- _pad1: [SI_PAD_SIZE]u8,
- using _kill: struct {
- pid: Pid, /* sender's pid */
- uid: Uid, /* sender's uid */
- },
- using _timer: struct {
- timerid: i32, /* timer id */
- overrun: i32, /* overrun count */
- value: Sig_Val, /* timer value */
- },
- /* POSIX.1b signals */
- using _rt: struct {
- _pid0: Pid, /* sender's pid */
- _uid0: Uid, /* sender's uid */
- },
- /* SIGCHLD */
- using _sigchld: struct {
- _pid1: Pid, /* which child */
- _uid1: Uid, /* sender's uid */
- status: i32, /* exit code */
- utime: uint,
- stime: uint, //clock_t
- },
- /* SIGILL, SIGFPE, SIGSEGV, SIGBUS */
- using _sigfault: struct {
- addr: rawptr, /* faulting insn/memory ref. */
- using _: struct #raw_union {
- trapno: i32, /* Trap number that caused signal */
- addr_lsb: i16, /* LSB of the reported address */
- using _addr_bnd: struct {
- _pad2: u64,
- lower: rawptr, /* lower bound during fault */
- upper: rawptr, /* upper bound during fault */
- },
- using _addr_pkey: struct {
- _pad3: u64,
- pkey: u32, /* protection key on PTE that faulted */
- },
- using _perf: struct {
- perf_data: u64,
- perf_type: u32,
- perf_flags: u32,
+when size_of(rawptr) == 8 {
+ Sig_Info :: struct #packed {
+ signo: Signal,
+ errno: Errno,
+ code: i32,
+ _pad0: i32,
+ using _union: struct #raw_union {
+ _pad1: [SI_PAD_SIZE]u8,
+ using _kill: struct {
+ pid: Pid, /* sender's pid */
+ uid: Uid, /* sender's uid */
+ },
+ using _timer: struct {
+ timerid: i32, /* timer id */
+ overrun: i32, /* overrun count */
+ value: Sig_Val, /* timer value */
+ },
+ /* POSIX.1b signals */
+ using _rt: struct {
+ _pid0: Pid, /* sender's pid */
+ _uid0: Uid, /* sender's uid */
+ },
+ /* SIGCHLD */
+ using _sigchld: struct {
+ _pid1: Pid, /* which child */
+ _uid1: Uid, /* sender's uid */
+ status: i32, /* exit code */
+ utime: uint,
+ stime: uint, //clock_t
+ },
+ /* SIGILL, SIGFPE, SIGSEGV, SIGBUS */
+ using _sigfault: struct {
+ addr: rawptr, /* faulting insn/memory ref. */
+ using _: struct #raw_union {
+ trapno: i32, /* Trap number that caused signal */
+ addr_lsb: i16, /* LSB of the reported address */
+ using _addr_bnd: struct {
+ _pad2: u64,
+ lower: rawptr, /* lower bound during fault */
+ upper: rawptr, /* upper bound during fault */
+ },
+ using _addr_pkey: struct {
+ _pad3: u64,
+ pkey: u32, /* protection key on PTE that faulted */
+ },
+ using _perf: struct {
+ perf_data: u64,
+ perf_type: u32,
+ perf_flags: u32,
+ },
},
},
+ /* SIGPOLL */
+ using _sigpoll: struct {
+ band: int, /* POLL_IN, POLL_OUT, POLL_MSG */
+ fd: Fd,
+ },
+ /* SIGSYS */
+ using _sigsys: struct {
+ call_addr: rawptr, /* calling user insn */
+ syscall: i32, /* triggering system call number */
+ arch: u32, /* AUDIT_ARCH_* of syscall */
+ },
},
- /* SIGPOLL */
- using _sigpoll: struct {
- band: int, /* POLL_IN, POLL_OUT, POLL_MSG */
- fd: Fd,
- },
- /* SIGSYS */
- using _sigsys: struct {
- call_addr: rawptr, /* calling user insn */
- syscall: i32, /* triggering system call number */
- arch: u32, /* AUDIT_ARCH_* of syscall */
- },
- },
-}
+ }
-#assert(size_of(Sig_Info) == 128)
-when ODIN_ARCH == .amd64 || ODIN_ARCH == .arm64 {
+ #assert(size_of(Sig_Info) == 128)
#assert(offset_of(Sig_Info, signo) == 0x00)
#assert(offset_of(Sig_Info, errno) == 0x04)
#assert(offset_of(Sig_Info, code) == 0x08)
@@ -615,7 +616,96 @@ when ODIN_ARCH == .amd64 || ODIN_ARCH == .arm64 {
#assert(offset_of(Sig_Info, syscall) == 0x18)
#assert(offset_of(Sig_Info, arch) == 0x1C)
} else {
- // TODO
+ Sig_Info :: struct {
+ signo: Signal,
+ errno: Errno,
+ code: i32,
+ using _union: struct #raw_union {
+ _pad1: [SI_PAD_SIZE]u8,
+ using _kill: struct {
+ pid: Pid, /* sender's pid */
+ uid: Uid, /* sender's uid */
+ },
+ using _timer: struct {
+ timerid: i32, /* timer id */
+ overrun: i32, /* overrun count */
+ value: Sig_Val, /* timer value */
+ },
+ /* POSIX.1b signals */
+ using _rt: struct {
+ _pid0: Pid, /* sender's pid */
+ _uid0: Uid, /* sender's uid */
+ },
+ /* SIGCHLD */
+ using _sigchld: struct {
+ _pid1: Pid, /* which child */
+ _uid1: Uid, /* sender's uid */
+ status: i32, /* exit code */
+ utime: uint,
+ stime: uint, //clock_t
+ },
+ /* SIGILL, SIGFPE, SIGSEGV, SIGBUS */
+ using _sigfault: struct {
+ addr: rawptr, /* faulting insn/memory ref. */
+ using _: struct #raw_union {
+ trapno: i32, /* Trap number that caused signal */
+ addr_lsb: i16, /* LSB of the reported address */
+ using _addr_bnd: struct {
+ _pad2: u32,
+ lower: rawptr, /* lower bound during fault */
+ upper: rawptr, /* upper bound during fault */
+ },
+ using _addr_pkey: struct {
+ _pad3: u32,
+ pkey: u32, /* protection key on PTE that faulted */
+ },
+ using _perf: struct {
+ perf_data: u32,
+ perf_type: u32,
+ perf_flags: u32,
+ },
+ },
+ },
+ /* SIGPOLL */
+ using _sigpoll: struct {
+ band: int, /* POLL_IN, POLL_OUT, POLL_MSG */
+ fd: Fd,
+ },
+ /* SIGSYS */
+ using _sigsys: struct {
+ call_addr: rawptr, /* calling user insn */
+ syscall: i32, /* triggering system call number */
+ arch: u32, /* AUDIT_ARCH_* of syscall */
+ },
+ },
+ }
+
+ #assert(size_of(Sig_Info) == 128)
+ #assert(offset_of(Sig_Info, signo) == 0x00)
+ #assert(offset_of(Sig_Info, errno) == 0x04)
+ #assert(offset_of(Sig_Info, code) == 0x08)
+ #assert(offset_of(Sig_Info, pid) == 0x0c)
+ #assert(offset_of(Sig_Info, uid) == 0x10)
+ #assert(offset_of(Sig_Info, timerid) == 0x0c)
+ #assert(offset_of(Sig_Info, overrun) == 0x10)
+ #assert(offset_of(Sig_Info, value) == 0x14)
+ #assert(offset_of(Sig_Info, status) == 0x14)
+ #assert(offset_of(Sig_Info, utime) == 0x18)
+ #assert(offset_of(Sig_Info, stime) == 0x1c)
+ #assert(offset_of(Sig_Info, addr) == 0x0c)
+ #assert(offset_of(Sig_Info, addr_lsb) == 0x10)
+ #assert(offset_of(Sig_Info, trapno) == 0x10)
+ #assert(offset_of(Sig_Info, lower) == 0x14)
+ #assert(offset_of(Sig_Info, upper) == 0x18)
+ #assert(offset_of(Sig_Info, pkey) == 0x14)
+ #assert(offset_of(Sig_Info, perf_data) == 0x10)
+ #assert(offset_of(Sig_Info, perf_type) == 0x14)
+ #assert(offset_of(Sig_Info, perf_flags) == 0x18)
+ #assert(offset_of(Sig_Info, band) == 0x0c)
+ #assert(offset_of(Sig_Info, fd) == 0x10)
+ #assert(offset_of(Sig_Info, call_addr) == 0x0c)
+ #assert(offset_of(Sig_Info, syscall) == 0x10)
+ #assert(offset_of(Sig_Info, arch) == 0x14)
}
SIGEV_MAX_SIZE :: 64
@@ -685,12 +775,21 @@ Address_Family :: distinct Protocol_Family
Socket_Msg :: bit_set[Socket_Msg_Bits; i32]
/*
+ Struct representing a generic socket address.
+*/
+Sock_Addr :: struct #packed {
+ sa_family: Address_Family,
+ sa_data: [14]u8,
+}
+
+/*
Struct representing IPv4 socket address.
*/
Sock_Addr_In :: struct #packed {
sin_family: Address_Family,
sin_port: u16be,
sin_addr: [4]u8,
+ sin_zero: [size_of(Sock_Addr) - size_of(Address_Family) - size_of(u16be) - size_of([4]u8)]u8,
}
/*
@@ -720,6 +819,7 @@ Sock_Addr_Any :: struct #raw_union {
family: Address_Family,
port: u16be,
},
+ using generic: Sock_Addr,
using ipv4: Sock_Addr_In,
using ipv6: Sock_Addr_In6,
using uds: Sock_Addr_Un,
diff --git a/core/sys/linux/wrappers.odin b/core/sys/linux/wrappers.odin
index 4f6118c80..ab1992a57 100644
--- a/core/sys/linux/wrappers.odin
+++ b/core/sys/linux/wrappers.odin
@@ -86,22 +86,18 @@ dirent_iterate_buf :: proc "contextless" (buf: []u8, offs: ^int) -> (d: ^Dirent,
/// The lifetime of the string is bound to the lifetime of the provided dirent structure
dirent_name :: proc "contextless" (dirent: ^Dirent) -> string #no_bounds_check {
str := ([^]u8)(&dirent.name)
- // Note(flysand): The string size calculated above applies only to the ideal case
- // we subtract 1 byte from the string size, because a null terminator is guaranteed
- // to be present. But! That said, the dirents are aligned to 8 bytes and the padding
- // between the null terminator and the start of the next struct may be not initialized
- // which means we also have to scan these garbage bytes.
- str_size := int(dirent.reclen) - 1 - cast(int)offset_of(Dirent, name)
- // This skips *only* over the garbage, since if we're not garbage we're at nul terminator,
- // which skips this loop
- for str[str_size] != 0 {
- str_size -= 1
- }
- for str[str_size-1] == 0 {
- str_size -= 1
+ // Dirents are aligned to 8 bytes, so there is guaranteed to be a null
+ // terminator in the last 8 bytes.
+ str_size := int(dirent.reclen) - cast(int)offset_of(Dirent, name)
+
+ trunc := min(str_size, 8)
+ str_size -= trunc
+ for _ in 0..<trunc {
+ str_size += 1
+ if str[str_size] == 0 {
+ break
+ }
}
- // Oh yeah btw i could also just `repne scasb` this thing, but honestly I started doing
- // it the painful way, might as well finish doing it that way
return string(str[:str_size])
}
@@ -117,4 +113,4 @@ perf_cache_config :: #force_inline proc "contextless" (id: Perf_Hardware_Cache_I
op: Perf_Hardware_Cache_Op_Id,
res: Perf_Hardware_Cache_Result_Id) -> u64 {
return u64(id) | (u64(op) << 8) | (u64(res) << 16)
-} \ No newline at end of file
+}
diff --git a/core/sys/orca/macros.odin b/core/sys/orca/macros.odin
index d6a1a0f82..12adfdb91 100644
--- a/core/sys/orca/macros.odin
+++ b/core/sys/orca/macros.odin
@@ -37,12 +37,7 @@ log_info :: proc "contextless" (msg: cstring, loc := #caller_location) {
}
abort :: proc "contextless" (msg: cstring, loc := #caller_location) {
- abort_ext(
- cstring(raw_data(loc.procedure)),
- cstring(raw_data(loc.file_path)),
- loc.line,
- msg,
- )
+ abort_ext(cstring(raw_data(loc.procedure)), cstring(raw_data(loc.file_path)), loc.line, msg)
}
////////////////////////////////////////////////////////////////////////////////
@@ -55,7 +50,12 @@ list_entry :: proc "contextless" (elt: ^list_elt, $T: typeid, $member: string) -
}
// Get the next entry in a list.
-list_next_entry :: proc "contextless" (list: ^list, elt: ^list_elt, $T: typeid, $member: string) -> ^T {
+list_next_entry :: proc "contextless" (
+ list: ^list,
+ elt: ^list_elt,
+ $T: typeid,
+ $member: string,
+) -> ^T {
if elt.next != list.last {
return list_entry(elt.next, T, member)
}
@@ -64,7 +64,12 @@ list_next_entry :: proc "contextless" (list: ^list, elt: ^list_elt, $T: typeid,
}
// Get the previous entry in a list.
-list_prev_entry :: proc "contextless" (list: ^list, elt: ^list_elt, $T: typeid, $member: string) -> ^T {
+list_prev_entry :: proc "contextless" (
+ list: ^list,
+ elt: ^list_elt,
+ $T: typeid,
+ $member: string,
+) -> ^T {
if elt.prev != list.last {
return list_entry(elt.prev, T, member)
}
@@ -94,9 +99,23 @@ list_last_entry :: proc "contextless" (list: ^list, $T: typeid, $member: string)
// _elt: ^list_elt
// for elt in oc.list_for(list, &_elt, int, "elt") {
// }
-list_for :: proc "contextless" (list: ^list, elt: ^^list_elt, $T: typeid, $member: string) -> (^T, bool) {
+list_for :: proc "contextless" (
+ list: ^list,
+ elt: ^^list_elt,
+ $T: typeid,
+ $member: string,
+) -> (
+ ^T,
+ bool,
+) {
if elt == nil {
- assert_fail(#file, #procedure, #line, "elt != nil", "misuse of `list_for`, expected `elt` to not be nil")
+ assert_fail(
+ #file,
+ #procedure,
+ #line,
+ "elt != nil",
+ "misuse of `list_for`, expected `elt` to not be nil",
+ )
}
if elt^ == nil {
@@ -112,7 +131,15 @@ list_for :: proc "contextless" (list: ^list, elt: ^^list_elt, $T: typeid, $membe
list_iter :: list_for
-list_for_reverse :: proc "contextless" (list: ^list, elt: ^^list_elt, $T: typeid, $member: string) -> (^T, bool) {
+list_for_reverse :: proc "contextless" (
+ list: ^list,
+ elt: ^^list_elt,
+ $T: typeid,
+ $member: string,
+) -> (
+ ^T,
+ bool,
+) {
if elt^ == nil {
elt^ = list.last
entry := list_checked_entry(elt^, T, member)
@@ -226,27 +253,17 @@ str32_list_for :: proc "contextless" (list: ^str32_list, elt: ^^list_elt) -> (^s
return list_for(&list.list, elt, str32_elt, "listElt")
}
-@(deferred_none=ui_box_end)
-ui_container :: proc "contextless" (name: string, flags: ui_flags = {}) -> ^ui_box {
- return ui_box_begin_str8(name, flags)
-}
-
-@(deferred_none=ui_end_frame)
-ui_frame :: proc "contextless" (frame_size: [2]f32, style: ui_style, mask: ui_style_mask) {
- ui_begin_frame(frame_size, style, mask)
-}
-
-@(deferred_none=ui_panel_end)
-ui_panel :: proc "contextless" (name: cstring, flags: ui_flags) {
- ui_panel_begin(name, flags)
+@(deferred_none = ui_box_end)
+ui_container :: proc "contextless" (name: string) -> ^ui_box {
+ return ui_box_begin_str8(name)
}
-@(deferred_none=ui_menu_end)
-ui_menu :: proc "contextless" (name: cstring) {
- ui_menu_begin(name)
+@(deferred_none = ui_menu_end)
+ui_menu :: proc "contextless" (key, name: string) {
+ ui_menu_begin_str8(key, name)
}
-@(deferred_none=ui_menu_bar_end)
-ui_menu_bar :: proc "contextless" (name: cstring) {
- ui_menu_bar_begin(name)
+@(deferred_none = ui_menu_bar_end)
+ui_menu_bar :: proc "contextless" (key: string) {
+ ui_menu_bar_begin_str8(key)
}
diff --git a/core/sys/orca/odin.odin b/core/sys/orca/odin.odin
index 5c3e3e4d9..c59b990cf 100644
--- a/core/sys/orca/odin.odin
+++ b/core/sys/orca/odin.odin
@@ -8,15 +8,25 @@ create_odin_logger :: proc(lowest := runtime.Logger_Level.Debug, ident := "") ->
return runtime.Logger{odin_logger_proc, nil, lowest, {}}
}
+log_typed :: proc "contextless" (level: log_level, msg: cstring, loc := #caller_location) {
+ log_ext(
+ level,
+ cstring(raw_data(loc.procedure)),
+ cstring(raw_data(loc.file_path)),
+ loc.line,
+ msg,
+ )
+}
+
odin_logger_proc :: proc(logger_data: rawptr, level: runtime.Logger_Level, text: string, options: runtime.Logger_Options, location := #caller_location) {
cbuf := make([]byte, len(text)+1, context.temp_allocator)
copy(cbuf, text)
ctext := cstring(raw_data(cbuf))
switch level {
- case .Debug, .Info: log_info(ctext, location)
- case .Warning: log_warning(ctext, location)
+ case .Debug, .Info: log_typed(.INFO, ctext, location)
+ case .Warning: log_typed(.WARNING, ctext, location)
case: fallthrough
- case .Error, .Fatal: log_error(ctext, location)
+ case .Error, .Fatal: log_typed(.ERROR, ctext, location)
}
}
diff --git a/core/sys/orca/orca.odin b/core/sys/orca/orca.odin
index d1e7dbf66..abcf42fe2 100644
--- a/core/sys/orca/orca.odin
+++ b/core/sys/orca/orca.odin
@@ -14,27 +14,6 @@ pool :: struct {
blockSize: u64,
}
-@(link_prefix="OC_")
-foreign {
- UI_DARK_THEME: ui_theme
- UI_LIGHT_THEME: ui_theme
-
- UI_DARK_PALETTE: ui_palette
- UI_LIGHT_PALETTE: ui_palette
-}
-
-
-SYS_MAX_ERROR :: 1024
-
-sys_err_def :: struct {
- msg: [SYS_MAX_ERROR]u8 `fmt:"s,0"`,
- code: i32,
-}
-
-@(link_prefix="oc_")
-foreign {
- sys_error: sys_err_def
-}
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 }
@@ -178,43 +157,6 @@ file_write_slice :: proc(file: file, slice: []char) -> u64 {
file_read_slice :: proc(file: file, slice: []char) -> u64 {
return file_read(file, u64(len(slice)), raw_data(slice))
}
-
-style_enum :: enum {
- SIZE_WIDTH = 1,
- SIZE_HEIGHT,
-
- LAYOUT_AXIS,
- LAYOUT_ALIGN_X,
- LAYOUT_ALIGN_Y,
- LAYOUT_SPACING,
- LAYOUT_MARGIN_X,
- LAYOUT_MARGIN_Y,
-
- FLOAT_X,
- FLOAT_Y,
-
- COLOR,
- BG_COLOR,
- BORDER_COLOR,
- BORDER_SIZE,
- ROUNDNESS,
-
- FONT,
- FONT_SIZE,
-
- ANIMATION_TIME,
- ANIMATION_MASK,
-}
-
-ui_style_mask :: bit_set[style_enum; u64]
-
-// Masks like the C version that can be used as common combinations
-SIZE :: ui_style_mask { .SIZE_WIDTH, .SIZE_HEIGHT }
-LAYOUT_MARGINS :: ui_style_mask { .LAYOUT_MARGIN_X, .LAYOUT_MARGIN_Y }
-LAYOUT :: ui_style_mask { .LAYOUT_AXIS, .LAYOUT_ALIGN_X, .LAYOUT_ALIGN_Y, .LAYOUT_SPACING, .LAYOUT_MARGIN_X, .LAYOUT_MARGIN_Y }
-FLOAT :: ui_style_mask { .FLOAT_X, .FLOAT_Y }
-MASK_INHERITED :: ui_style_mask { .COLOR, .FONT, .FONT_SIZE, .ANIMATION_TIME, .ANIMATION_MASK }
-
////////////////////////////////////////////////////////////////////////////////
// Utility data structures and helpers used throughout the Orca API.
////////////////////////////////////////////////////////////////////////////////
@@ -578,8 +520,28 @@ foreign {
// A unicode codepoint.
utf32 :: rune
+// This enum declares the possible return status of UTF8 decoding/encoding operations.
+utf8_status :: enum u32 {
+ // The operation was successful.
+ OK = 0,
+ // The operation unexpectedly encountered the end of the utf8 sequence.
+ OUT_OF_BOUNDS = 1,
+ // A continuation byte was encountered where a leading byte was expected.
+ UNEXPECTED_CONTINUATION_BYTE = 3,
+ // A leading byte was encountered in the middle of the encoding of utf8 codepoint.
+ UNEXPECTED_LEADING_BYTE = 4,
+ // The utf8 sequence contains an invalid byte.
+ INVALID_BYTE = 5,
+ // The operation encountered an invalid utf8 codepoint.
+ INVALID_CODEPOINT = 6,
+ // The utf8 sequence contains an overlong encoding of a utf8 codepoint.
+ OVERLONG_ENCODING = 7,
+}
+
// A type representing the result of decoding of utf8-encoded codepoint.
utf8_dec :: struct {
+ // The status of the decoding operation. If not `OC_UTF8_OK`, it describes the error that was encountered during decoding.
+ status: utf8_status,
// The decoded codepoint.
codepoint: utf32,
// The size of the utf8 sequence encoding that codepoint.
@@ -1033,7 +995,7 @@ file_dialog_kind :: enum u32 {
// File dialog flags.
file_dialog_flag :: enum u32 {
// This dialog allows selecting files.
- FILES = 1,
+ FILES = 0,
// This dialog allows selecting directories.
DIRECTORIES,
// This dialog allows selecting multiple items.
@@ -1180,7 +1142,7 @@ io_req :: struct {
unused: u64,
},
using _: struct #raw_union {
- open: struct {
+ using open: struct {
// The access permissions requested on the file to open.
rights: file_access,
// The options to use when opening the file.
@@ -1390,181 +1352,333 @@ foreign {
// A 2D Vector Graphics API.
////////////////////////////////////////////////////////////////////////////////
+// An opaque handle to a graphics surface.
surface :: distinct u64
+// An opaque handle representing a rendering engine for the canvas API.
canvas_renderer :: distinct u64
+// An opaque handle to a canvas context. Canvas contexts are used to hold contextual state about drawing commands, such as the current color or the current line width, and to record drawing commands. Once commands have been recorded, they can be rendered to a surface using `oc_canvas_render()`.
canvas_context :: distinct u64
+// An opaque font handle.
font :: distinct u64
+// An opaque image handle.
image :: distinct u64
+// This enum describes possible blending modes for color gradient.
gradient_blend_space :: enum u32 {
+ // The gradient colors are interpolated in linear space.
LINEAR = 0,
+ // The gradient colors are interpolated in sRGB space.
SRGB = 1,
}
+// An enum identifying possible color spaces.
color_space :: enum u32 {
+ // A linear RGB color space.
RGB = 0,
+ // An sRGB color space.
SRGB = 1,
}
+// A struct representing a color.
color :: struct { using c: [4]f32, colorSpace: color_space }
+// Stroke joint types.
joint_type :: enum u32 {
+ // Miter joint.
MITER = 0,
+ // Bevel joint.
BEVEL = 1,
+ // Don't join path segments.
NONE = 2,
}
+// Cap types.
cap_type :: enum u32 {
+ // Don't draw caps.
NONE = 0,
+ // Square caps.
SQUARE = 1,
}
+// A struct describing the metrics of a font.
font_metrics :: struct {
+ // The ascent from the baseline to the top of the line (a positive value means the top line is above the baseline).
ascent: f32,
+ // The descent from the baseline to the bottom line (a positive value means the bottom line is below the baseline).
descent: f32,
+ // The gap between two lines of text.
lineGap: f32,
+ // The height of the lowercase character 'x'.
xHeight: f32,
+ // The height of capital letters.
capHeight: f32,
+ // The maximum character width.
width: f32,
}
+// A struct describing the metrics of a single glyph.
glyph_metrics :: struct {
ink: rect,
+ // The default amount from which to advance the cursor after drawing this glyph.
advance: vec2,
}
+// A struct describing the metrics of a run of glyphs.
text_metrics :: struct {
+ // The bounding box of the inked portion of the text.
ink: rect,
+ // The logical bounding box of the text (including ascents, descents, and line gaps).
logical: rect,
+ // The amount from which to advance the cursor after drawing the text.
advance: vec2,
}
+// An opaque struct representing a rectangle atlas. This is used to allocate rectangular regions of an image to make texture atlases.
rect_atlas :: struct {}
+// A struct describing a rectangular sub-region of an image.
image_region :: struct {
+ // The image handle.
image: image,
+ // The rectangular region of the image.
rect: rect,
}
@(default_calling_convention="c", link_prefix="oc_")
foreign {
+ // Returns a `nil` surface handle.
surface_nil :: proc() -> surface ---
+ // Check if a surface handle is `nil`.
surface_is_nil :: proc(surface: surface) -> bool ---
+ // Destroy a graphics surface.
surface_destroy :: proc(surface: surface) ---
+ /*
+ Get a surface's size.
+
+ The size is returned in device-independent "points". To get the size in pixels, multiply the size in points by the scaling factor returned by `oc_surface_contents_scaling()`.
+ */
surface_get_size :: proc(surface: surface) -> vec2 ---
+ // Get the scaling factor of a surface.
surface_contents_scaling :: proc(surface: surface) -> vec2 ---
+ // Bring a surface to the foreground, rendering it on top of other surfaces.
surface_bring_to_front :: proc(surface: surface) ---
+ // Send a surface to the background, rendering it below other surfaces.
surface_send_to_back :: proc(surface: surface) ---
+ // Checks if a surface is hidden.
surface_get_hidden :: proc(surface: surface) -> bool ---
+ // Set the hidden status of a surface.
surface_set_hidden :: proc(surface: surface, hidden: bool) ---
+ // Create a color using RGBA values.
color_rgba :: proc(r: f32, g: f32, b: f32, a: f32) -> color ---
+ // Create a current color using sRGBA values.
color_srgba :: proc(r: f32, g: f32, b: f32, a: f32) -> color ---
+ // Convert a color from one color space to another.
color_convert :: proc(_color: color, colorSpace: color_space) -> color ---
+ // Returns a `nil` canvas renderer handle.
canvas_renderer_nil :: proc() -> canvas_renderer ---
+ // Checks if a canvas renderer handle is `nil`.
canvas_renderer_is_nil :: proc(renderer: canvas_renderer) -> bool ---
+ // Create a canvas renderer.
canvas_renderer_create :: proc() -> canvas_renderer ---
+ // Destroy a canvas renderer.
canvas_renderer_destroy :: proc(renderer: canvas_renderer) ---
+ // Render canvas commands onto a surface.
canvas_render :: proc(renderer: canvas_renderer, _context: canvas_context, surface: surface) ---
+ // Present a canvas surface to the display.
canvas_present :: proc(renderer: canvas_renderer, surface: surface) ---
+ // Create a surface for rendering vector graphics.
canvas_surface_create :: proc(renderer: canvas_renderer) -> surface ---
- canvas_surface_swap_interval :: proc(surface: surface, swap: i32) ---
+ // Returns a `nil` canvas context handle.
canvas_context_nil :: proc() -> canvas_context ---
+ // Checks if a canvas context handle is `nil`.
canvas_context_is_nil :: proc(_context: canvas_context) -> bool ---
+ // Create a canvas context.
canvas_context_create :: proc() -> canvas_context ---
+ // Destroy a canvas context
canvas_context_destroy :: proc(_context: canvas_context) ---
+ // Make a canvas context current in the calling thread. Subsequent canvas commands will refer to this context until another context is made current.
canvas_context_select :: proc(_context: canvas_context) -> canvas_context ---
+ // Set the multisample anti-aliasing sample count for the commands of a context.
canvas_context_set_msaa_sample_count :: proc(_context: canvas_context, sampleCount: u32) ---
+ // Return a `nil` font handle.
font_nil :: proc() -> font ---
+ // Check if a font handle is `nil`.
font_is_nil :: proc(font: font) -> bool ---
+ // Create a font from in-memory TrueType data.
font_create_from_memory :: proc(mem: str8, rangeCount: u32, ranges: ^unicode_range) -> font ---
+ // Create a font from a TrueType font file.
font_create_from_file :: proc(file: file, rangeCount: u32, ranges: ^unicode_range) -> font ---
+ // Create a font from a TrueType font file path.
font_create_from_path :: proc(path: str8, rangeCount: u32, ranges: ^unicode_range) -> font ---
+ // Destroy a font.
font_destroy :: proc(font: font) ---
+ // Get the glyph indices of a run of unicode code points in a given font.
font_get_glyph_indices :: proc(font: font, codePoints: str32, backing: str32) -> str32 ---
+ // Get the glyph indices of a run of unicode code points in a given font and push them on an arena.
font_push_glyph_indices :: proc(arena: ^arena, font: font, codePoints: str32) -> str32 ---
+ // Get the glyp index of a single codepoint in a given font.
font_get_glyph_index :: proc(font: font, codePoint: utf32) -> u32 ---
+ // Get a font's metrics for a given font size.
font_get_metrics :: proc(font: font, emSize: f32) -> font_metrics ---
+ // Get a font's unscaled metrics.
font_get_metrics_unscaled :: proc(font: font) -> font_metrics ---
+ // Get a scale factor to apply to unscaled font metrics to obtain a given 'm' size.
font_get_scale_for_em_pixels :: proc(font: font, emSize: f32) -> f32 ---
+ // Get text metrics for a run of unicode code points.
font_text_metrics_utf32 :: proc(font: font, fontSize: f32, codepoints: str32) -> text_metrics ---
+ // Get the text metrics for a utf8 string.
font_text_metrics :: proc(font: font, fontSize: f32, text: str8) -> text_metrics ---
+ // Returns a `nil` image handle.
image_nil :: proc() -> image ---
+ // Check if an image handle is `nil`.
image_is_nil :: proc(a: image) -> bool ---
+ // Create an uninitialized image.
image_create :: proc(renderer: canvas_renderer, width: u32, height: u32) -> image ---
+ // Create an image from an array of 8 bit per channel rgba values.
image_create_from_rgba8 :: proc(renderer: canvas_renderer, width: u32, height: u32, pixels: [^]u8) -> image ---
+ // Create an image from in-memory png, jpeg or bmp data.
image_create_from_memory :: proc(renderer: canvas_renderer, mem: str8, flip: bool) -> image ---
+ // Create an image from an image file. Supported formats are: png, jpeg or bmp.
image_create_from_file :: proc(renderer: canvas_renderer, file: file, flip: bool) -> image ---
+ // Create an image from an image file path. Supported formats are: png, jpeg or bmp.
image_create_from_path :: proc(renderer: canvas_renderer, path: str8, flip: bool) -> image ---
+ // Destroy an image.
image_destroy :: proc(image: image) ---
+ // Upload pixels to an image.
image_upload_region_rgba8 :: proc(image: image, region: rect, pixels: [^]u8) ---
+ // Get the size of an image.
image_size :: proc(image: image) -> vec2 ---
+ // Create a rectangle atlas.
rect_atlas_create :: proc(arena: ^arena, width: i32, height: i32) -> ^rect_atlas ---
+ // Allocate a rectangular region from an atlas.
rect_atlas_alloc :: proc(atlas: ^rect_atlas, width: i32, height: i32) -> rect ---
+ // Recycle a rectangular region that was previously allocated from an atlas.
rect_atlas_recycle :: proc(atlas: ^rect_atlas, rect: rect) ---
+ // Allocate an image region from an atlas and upload pixels to that region.
image_atlas_alloc_from_rgba8 :: proc(atlas: ^rect_atlas, backingImage: image, width: u32, height: u32, pixels: [^]u8) -> image_region ---
+ // Allocate an image region from an atlas and upload an image to it.
image_atlas_alloc_from_memory :: proc(atlas: ^rect_atlas, backingImage: image, mem: str8, flip: bool) -> image_region ---
+ // Allocate an image region from an atlas and upload an image to it.
image_atlas_alloc_from_file :: proc(atlas: ^rect_atlas, backingImage: image, file: file, flip: bool) -> image_region ---
+ // Allocate an image region from an atlas and upload an image to it.
image_atlas_alloc_from_path :: proc(atlas: ^rect_atlas, backingImage: image, path: str8, flip: bool) -> image_region ---
+ // Recycle an image region allocated from an atlas.
image_atlas_recycle :: proc(atlas: ^rect_atlas, imageRgn: image_region) ---
+ // Push a matrix on the transform stack.
matrix_push :: proc(_matrix: mat2x3) ---
+ // Multiply a matrix with the top of the transform stack, and push the result on the top of the stack.
matrix_multiply_push :: proc(_matrix: mat2x3) ---
+ // Pop a matrix from the transform stack.
matrix_pop :: proc() ---
+ // Get the top matrix of the transform stack.
matrix_top :: proc() -> mat2x3 ---
+ // Push a clip rectangle to the clip stack.
clip_push :: proc(x: f32, y: f32, w: f32, h: f32) ---
+ // Pop from the clip stack.
clip_pop :: proc() ---
+ // Get the clip rectangle from the top of the clip stack.
clip_top :: proc() -> rect ---
+ // Set the current color.
set_color :: proc(_color: color) ---
+ // Set the current color using linear RGBA values.
set_color_rgba :: proc(r: f32, g: f32, b: f32, a: f32) ---
+ // Set the current color using sRGBA values.
set_color_srgba :: proc(r: f32, g: f32, b: f32, a: f32) ---
+ // Set the current color gradient.
set_gradient :: proc(blendSpace: gradient_blend_space, bottomLeft: color, bottomRight: color, topRight: color, topLeft: color) ---
+ // Set the current line width.
set_width :: proc(width: f32) ---
+ // Set the current tolerance for the line width. Bigger values increase performance but allow more inconsistent stroke widths along a path.
set_tolerance :: proc(tolerance: f32) ---
+ // Set the current joint style.
set_joint :: proc(joint: joint_type) ---
+ // Set the maximum joint excursion. If a joint would extend past this threshold, the renderer falls back to a bevel joint.
set_max_joint_excursion :: proc(maxJointExcursion: f32) ---
+ // Set the current cap style.
set_cap :: proc(cap: cap_type) ---
+ // The the current font.
set_font :: proc(font: font) ---
+ // Set the current font size.
set_font_size :: proc(size: f32) ---
+ // Set the current text flip value. `true` flips the y-axis of text rendering commands.
set_text_flip :: proc(flip: bool) ---
+ // Set the current source image.
set_image :: proc(image: image) ---
+ // Set the current source image region.
set_image_source_region :: proc(region: rect) ---
+ // Get the current color
get_color :: proc() -> color ---
+ // Get the current line width.
get_width :: proc() -> f32 ---
+ // Get the current line width tolerance.
get_tolerance :: proc() -> f32 ---
+ // Get the current joint style.
get_joint :: proc() -> joint_type ---
+ // Get the current max joint excursion.
get_max_joint_excursion :: proc() -> f32 ---
+ // Get the current cap style.
get_cap :: proc() -> cap_type ---
+ // Get the current font.
get_font :: proc() -> font ---
+ // Get the current font size.
get_font_size :: proc() -> f32 ---
+ // Get the current text flip value.
get_text_flip :: proc() -> bool ---
+ // Get the current source image.
get_image :: proc() -> image ---
+ // Get the current image source region.
get_image_source_region :: proc() -> rect ---
+ // Get the current cursor position.
get_position :: proc() -> vec2 ---
+ // Move the cursor to a given position.
move_to :: proc(x: f32, y: f32) ---
+ // Add a line to the path from the current position to a new one.
line_to :: proc(x: f32, y: f32) ---
+ // Add a quadratic Bézier curve to the path from the current position to a new one.
quadratic_to :: proc(x1: f32, y1: f32, x2: f32, y2: f32) ---
+ // Add a cubic Bézier curve to the path from the current position to a new one.
cubic_to :: proc(x1: f32, y1: f32, x2: f32, y2: f32, x3: f32, y3: f32) ---
+ // Close the current path with a line.
close_path :: proc() ---
+ // Add the outlines of a glyph run to the path, using glyph indices.
glyph_outlines :: proc(glyphIndices: str32) -> rect ---
+ // Add the outlines of a glyph run to the path, using unicode codepoints.
codepoints_outlines :: proc(string: str32) ---
+ // Add the outlines of a glyph run to the path, using a utf8 string.
text_outlines :: proc(string: str8) ---
+ // Clear the canvas to the current color.
clear :: proc() ---
+ // Fill the current path.
fill :: proc() ---
+ // Stroke the current path.
stroke :: proc() ---
+ // Draw a filled rectangle.
rectangle_fill :: proc(x: f32, y: f32, w: f32, h: f32) ---
+ // Draw a stroked rectangle.
rectangle_stroke :: proc(x: f32, y: f32, w: f32, h: f32) ---
+ // Draw a filled rounded rectangle.
rounded_rectangle_fill :: proc(x: f32, y: f32, w: f32, h: f32, r: f32) ---
+ // Draw a stroked rounded rectangle.
rounded_rectangle_stroke :: proc(x: f32, y: f32, w: f32, h: f32, r: f32) ---
+ // Draw a filled ellipse.
ellipse_fill :: proc(x: f32, y: f32, rx: f32, ry: f32) ---
+ // Draw a stroked ellipse.
ellipse_stroke :: proc(x: f32, y: f32, rx: f32, ry: f32) ---
+ // Draw a filled circle.
circle_fill :: proc(x: f32, y: f32, r: f32) ---
+ // Draw a stroked circle.
circle_stroke :: proc(x: f32, y: f32, r: f32) ---
+ // Add an arc to the path.
arc :: proc(x: f32, y: f32, r: f32, arcAngle: f32, startAngle: f32) ---
+ // Draw a text line.
text_fill :: proc(x: f32, y: f32, text: str8) ---
+ // Draw an image.
image_draw :: proc(image: image, rect: rect) ---
+ // Draw a sub-region of an image.
image_draw_region :: proc(image: image, srcRegion: rect, dstRegion: rect) ---
}
@@ -1574,9 +1688,11 @@ foreign {
@(default_calling_convention="c", link_prefix="oc_")
foreign {
+ // Create a graphics surface for GLES rendering.
gles_surface_create :: proc() -> surface ---
+ // Make the GL context of the surface current.
gles_surface_make_current :: proc(surface: surface) ---
- gles_surface_swap_interval :: proc(surface: surface, interval: i32) ---
+ // Swap the buffers of a GLES surface.
gles_surface_swap_buffers :: proc(surface: surface) ---
}
@@ -1638,10 +1754,37 @@ input_state :: struct {
clipboard: clipboard_state,
}
-ui_key :: struct {
- hash: u64,
+@(default_calling_convention="c", link_prefix="oc_")
+foreign {
+ input_process_event :: proc(arena: ^arena, state: ^input_state, event: ^event) ---
+ input_next_frame :: proc(state: ^input_state) ---
+ key_down :: proc(state: ^input_state, key: key_code) -> bool ---
+ key_press_count :: proc(state: ^input_state, key: key_code) -> u8 ---
+ key_release_count :: proc(state: ^input_state, key: key_code) -> u8 ---
+ key_repeat_count :: proc(state: ^input_state, key: key_code) -> u8 ---
+ key_down_scancode :: proc(state: ^input_state, key: scan_code) -> bool ---
+ key_press_count_scancode :: proc(state: ^input_state, key: scan_code) -> u8 ---
+ key_release_count_scancode :: proc(state: ^input_state, key: scan_code) -> u8 ---
+ key_repeat_count_scancode :: proc(state: ^input_state, key: scan_code) -> u8 ---
+ mouse_down :: proc(state: ^input_state, button: mouse_button) -> bool ---
+ mouse_pressed :: proc(state: ^input_state, button: mouse_button) -> u8 ---
+ mouse_released :: proc(state: ^input_state, button: mouse_button) -> u8 ---
+ mouse_clicked :: proc(state: ^input_state, button: mouse_button) -> bool ---
+ mouse_double_clicked :: proc(state: ^input_state, button: mouse_button) -> bool ---
+ mouse_position :: proc(state: ^input_state) -> vec2 ---
+ mouse_delta :: proc(state: ^input_state) -> vec2 ---
+ mouse_wheel :: proc(state: ^input_state) -> vec2 ---
+ input_text_utf32 :: proc(arena: ^arena, state: ^input_state) -> str32 ---
+ input_text_utf8 :: proc(arena: ^arena, state: ^input_state) -> str8 ---
+ clipboard_pasted :: proc(state: ^input_state) -> bool ---
+ clipboard_pasted_text :: proc(state: ^input_state) -> str8 ---
+ key_mods :: proc(state: ^input_state) -> keymod_flags ---
}
+////////////////////////////////////////////////////////////////////////////////
+// Graphical User Interface Core API.
+////////////////////////////////////////////////////////////////////////////////
+
ui_axis :: enum u32 {
X = 0,
Y = 1,
@@ -1654,19 +1797,10 @@ ui_align :: enum u32 {
CENTER = 2,
}
-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 u32 {
- TEXT = 0,
- PIXELS = 1,
- CHILDREN = 2,
+ CHILDREN = 0,
+ TEXT = 1,
+ PIXELS = 2,
PARENT = 3,
PARENT_MINUS_PIXELS = 4,
}
@@ -1678,10 +1812,96 @@ ui_size :: struct {
minSize: f32,
}
+ui_overflow :: enum u32 {
+ CLIP = 0,
+ ALLOW = 1,
+ SCROLL = 2,
+}
+
+ui_attribute :: enum u32 {
+ WIDTH = 0,
+ HEIGHT = 1,
+ AXIS = 2,
+ MARGIN_X = 3,
+ MARGIN_Y = 4,
+ SPACING = 5,
+ ALIGN_X = 6,
+ ALIGN_Y = 7,
+ FLOATING_X = 8,
+ FLOATING_Y = 9,
+ FLOAT_TARGET_X = 10,
+ FLOAT_TARGET_Y = 11,
+ OVERFLOW_X = 12,
+ OVERFLOW_Y = 13,
+ CONSTRAIN_X = 14,
+ CONSTRAIN_Y = 15,
+ COLOR = 16,
+ BG_COLOR = 17,
+ BORDER_COLOR = 18,
+ FONT = 19,
+ TEXT_SIZE = 20,
+ BORDER_SIZE = 21,
+ ROUNDNESS = 22,
+ DRAW_MASK = 23,
+ ANIMATION_TIME = 24,
+ ANIMATION_MASK = 25,
+ CLICK_THROUGH = 26,
+ ATTRIBUTE_COUNT = 27,
+}
+
+ui_attribute_mask :: enum u32 {
+ NONE = 0,
+ SIZE_WIDTH = 1,
+ SIZE_HEIGHT = 2,
+ LAYOUT_AXIS = 4,
+ LAYOUT_ALIGN_X = 64,
+ LAYOUT_ALIGN_Y = 128,
+ LAYOUT_SPACING = 32,
+ LAYOUT_MARGIN_X = 8,
+ LAYOUT_MARGIN_Y = 16,
+ FLOATING_X = 256,
+ FLOATING_Y = 512,
+ FLOAT_TARGET_X = 1024,
+ FLOAT_TARGET_Y = 2048,
+ OVERFLOW_X = 4096,
+ OVERFLOW_Y = 8192,
+ CONSTRAIN_X = 16384,
+ CONSTRAIN_Y = 32768,
+ COLOR = 65536,
+ BG_COLOR = 131072,
+ BORDER_COLOR = 262144,
+ BORDER_SIZE = 2097152,
+ ROUNDNESS = 4194304,
+ FONT = 524288,
+ FONT_SIZE = 1048576,
+ DRAW_MASK = 8388608,
+ ANIMATION_TIME = 16777216,
+ ANIMATION_MASK = 33554432,
+ CLICK_THROUGH = 67108864,
+}
+
+ui_layout_align :: [2]ui_align
+
+ui_layout :: struct {
+ axis: ui_axis,
+ spacing: f32,
+ margin: [2]f32,
+ align: ui_layout_align,
+ overflow: [2]ui_overflow,
+ constrain: [2]bool,
+}
+
ui_box_size :: [2]ui_size
ui_box_floating :: [2]bool
+ui_draw_mask :: enum u32 {
+ BACKGROUND = 1,
+ BORDER = 2,
+ TEXT = 4,
+ PROC = 8,
+}
+
ui_style :: struct {
size: ui_box_size,
layout: ui_layout,
@@ -1694,241 +1914,37 @@ ui_style :: struct {
fontSize: f32,
borderSize: f32,
roundness: f32,
+ drawMask: u32,
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,
-}
-
-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,
-}
-
-ui_tag :: struct {
- hash: u64,
+ animationMask: ui_attribute_mask,
+ clickThrough: bool,
}
-ui_selector_kind :: enum u32 {
- ANY = 0,
- OWNER = 1,
- TEXT = 2,
- TAG = 3,
- STATUS = 4,
- KEY = 5,
-}
-
-ui_status_flag :: enum u8 {
- HOVER = 1,
- HOT,
- ACTIVE,
- DRAGGING,
-}
-ui_status :: bit_set[ui_status_flag; u8]
+ui_context :: struct {}
-ui_selector_op :: enum u32 {
- DESCENDANT = 0,
- AND = 1,
+ui_sig :: struct {
+ box: ^ui_box,
+ mouse: vec2,
+ delta: vec2,
+ wheel: vec2,
+ lastPressedMouse: vec2,
+ pressed: bool,
+ released: bool,
+ clicked: bool,
+ doubleClicked: bool,
+ tripleClicked: bool,
+ rightPressed: bool,
+ closed: bool,
+ active: bool,
+ hover: bool,
+ focus: bool,
+ pasted: bool,
}
-ui_selector :: struct {
- listElt: list_elt,
- kind: ui_selector_kind,
- op: ui_selector_op,
- using _: struct #raw_union {
- text: str8,
- key: ui_key,
- tag: ui_tag,
- status: ui_status,
- },
-}
+ui_box_draw_proc :: proc "c" (arg0: ^ui_box, arg1: rawptr)
-ui_pattern :: struct {
- l: list,
+ui_key :: struct {
+ hash: u64,
}
ui_box :: struct {
@@ -1936,16 +1952,16 @@ ui_box :: struct {
children: list,
parent: ^ui_box,
overlayElt: list_elt,
+ overlay: bool,
bucketElt: list_elt,
key: ui_key,
frameCounter: u64,
- flags: ui_flags,
- string: str8,
+ keyString: str8,
+ text: str8,
tags: list,
drawProc: ui_box_draw_proc,
drawData: rawptr,
- beforeRules: list,
- afterRules: list,
+ rules: list,
targetStyle: ^ui_style,
style: ui_style,
z: u32,
@@ -1954,7 +1970,8 @@ ui_box :: struct {
spacing: [2]f32,
minSize: [2]f32,
rect: rect,
- sig: ^ui_sig,
+ styleVariables: list,
+ sig: ui_sig,
fresh: bool,
closed: bool,
parentClosed: bool,
@@ -1967,76 +1984,100 @@ ui_box :: struct {
activeTransition: f32,
}
-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: bool,
- released: bool,
- clicked: bool,
- doubleClicked: bool,
- tripleClicked: bool,
- rightPressed: bool,
- dragging: bool,
- hovering: bool,
- pasted: bool,
+@(default_calling_convention="c", link_prefix="oc_")
+foreign {
+ ui_context_create :: proc(defaultFont: font) -> ^ui_context ---
+ ui_context_destroy :: proc(_context: ^ui_context) ---
+ ui_get_context :: proc() -> ^ui_context ---
+ ui_set_context :: proc(_context: ^ui_context) ---
+ ui_process_event :: proc(event: ^event) ---
+ ui_frame_begin :: proc(size: vec2) ---
+ ui_frame_end :: proc() ---
+ ui_draw :: proc() ---
+ ui_input :: proc() -> ^input_state ---
+ ui_frame_arena :: proc() -> ^arena ---
+ ui_frame_time :: proc() -> f64 ---
+ ui_box_begin_str8 :: proc(string: str8) -> ^ui_box ---
+ ui_box_end :: proc() -> ^ui_box ---
+ ui_box_set_draw_proc :: proc(box: ^ui_box, _proc: ui_box_draw_proc, data: rawptr) ---
+ ui_box_set_text :: proc(box: ^ui_box, text: str8) ---
+ ui_box_set_overlay :: proc(box: ^ui_box, overlay: bool) ---
+ ui_box_set_closed :: proc(box: ^ui_box, closed: bool) ---
+ ui_box_user_data_get :: proc(box: ^ui_box) -> cstring ---
+ ui_box_user_data_push :: proc(box: ^ui_box, size: u64) -> cstring ---
+ ui_box_request_focus :: proc(box: ^ui_box) ---
+ ui_box_release_focus :: proc(box: ^ui_box) ---
+ ui_box_get_sig :: proc(box: ^ui_box) -> ui_sig ---
+ ui_set_draw_proc :: proc(_proc: ui_box_draw_proc, data: rawptr) ---
+ ui_set_text :: proc(text: str8) ---
+ ui_set_overlay :: proc(overlay: bool) ---
+ ui_set_closed :: proc(closed: bool) ---
+ ui_user_data_get :: proc() -> cstring ---
+ ui_user_data_push :: proc(size: u64) -> cstring ---
+ ui_request_focus :: proc() ---
+ ui_release_focus :: proc() ---
+ ui_get_sig :: proc() -> ui_sig ---
+ ui_box_tag_str8 :: proc(box: ^ui_box, string: str8) ---
+ ui_tag_str8 :: proc(string: str8) ---
+ ui_tag_next_str8 :: proc(string: str8) ---
+ ui_style_rule_begin :: proc(pattern: str8) ---
+ ui_style_rule_end :: proc() ---
+ ui_style_set_i32 :: proc(attr: ui_attribute, i: i32) ---
+ ui_style_set_f32 :: proc(attr: ui_attribute, f: f32) ---
+ ui_style_set_color :: proc(attr: ui_attribute, _color: color) ---
+ ui_style_set_font :: proc(attr: ui_attribute, font: font) ---
+ ui_style_set_size :: proc(attr: ui_attribute, size: ui_size) ---
+ ui_style_set_var_str8 :: proc(attr: ui_attribute, var: str8) ---
+ ui_style_set_var :: proc(attr: ui_attribute, var: cstring) ---
+ ui_var_default_i32_str8 :: proc(name: str8, i: i32) ---
+ ui_var_default_f32_str8 :: proc(name: str8, f: f32) ---
+ ui_var_default_size_str8 :: proc(name: str8, size: ui_size) ---
+ ui_var_default_color_str8 :: proc(name: str8, _color: color) ---
+ ui_var_default_font_str8 :: proc(name: str8, font: font) ---
+ ui_var_default_str8 :: proc(name: str8, src: str8) ---
+ ui_var_default_i32 :: proc(name: cstring, i: i32) ---
+ ui_var_default_f32 :: proc(name: cstring, f: f32) ---
+ ui_var_default_size :: proc(name: cstring, size: ui_size) ---
+ ui_var_default_color :: proc(name: cstring, _color: color) ---
+ ui_var_default_font :: proc(name: cstring, font: font) ---
+ ui_var_default :: proc(name: cstring, src: cstring) ---
+ ui_var_set_i32_str8 :: proc(name: str8, i: i32) ---
+ ui_var_set_f32_str8 :: proc(name: str8, f: f32) ---
+ ui_var_set_size_str8 :: proc(name: str8, size: ui_size) ---
+ ui_var_set_color_str8 :: proc(name: str8, _color: color) ---
+ ui_var_set_font_str8 :: proc(name: str8, font: font) ---
+ ui_var_set_str8 :: proc(name: str8, src: str8) ---
+ ui_var_set_i32 :: proc(name: cstring, i: i32) ---
+ ui_var_set_f32 :: proc(name: cstring, f: f32) ---
+ ui_var_set_size :: proc(name: cstring, size: ui_size) ---
+ ui_var_set_color :: proc(name: cstring, _color: color) ---
+ ui_var_set_font :: proc(name: cstring, font: font) ---
+ ui_var_set :: proc(name: cstring, src: cstring) ---
+ ui_var_get_i32_str8 :: proc(name: str8) -> i32 ---
+ ui_var_get_f32_str8 :: proc(name: str8) -> f32 ---
+ ui_var_get_size_str8 :: proc(name: str8) -> ui_size ---
+ ui_var_get_color_str8 :: proc(name: str8) -> color ---
+ ui_var_get_font_str8 :: proc(name: str8) -> font ---
+ ui_var_get_i32 :: proc(name: cstring) -> i32 ---
+ ui_var_get_f32 :: proc(name: cstring) -> f32 ---
+ ui_var_get_size :: proc(name: cstring) -> ui_size ---
+ ui_var_get_color :: proc(name: cstring) -> color ---
+ ui_var_get_font :: proc(name: cstring) -> font ---
+ ui_theme_dark :: proc() ---
+ ui_theme_light :: proc() ---
}
-ui_box_draw_proc :: proc "c" (arg0: ^ui_box, arg1: rawptr)
-
-ui_flag :: enum u32 {
- CLICKABLE = 0,
- SCROLL_WHEEL_X,
- SCROLL_WHEEL_Y,
- BLOCK_MOUSE,
- HOT_ANIMATION,
- ACTIVE_ANIMATION,
- OVERFLOW_ALLOW_X,
- OVERFLOW_ALLOW_Y,
- CLIP,
- DRAW_BACKGROUND,
- DRAW_FOREGROUND,
- DRAW_BORDER,
- DRAW_TEXT,
- DRAW_PROC,
- OVERLAY,
-}
-ui_flags :: bit_set[ui_flag; u32]
-
-MAX_INPUT_CHAR_PER_FRAME :: 64
-
-ui_input_text :: struct {
- count: u8 `fmt:"-"`,
- codePoints: [64]utf32 `fmt:"s,count"`,
-}
-
-ui_stack_elt :: struct {
- parent: ^ui_stack_elt,
- using _: struct #raw_union {
- box: ^ui_box,
- size: ui_size,
- clip: rect,
- },
-}
+////////////////////////////////////////////////////////////////////////////////
+// Graphical User Interface Widgets.
+////////////////////////////////////////////////////////////////////////////////
-ui_tag_elt :: struct {
- listElt: list_elt,
- tag: ui_tag,
+ui_text_box_result :: struct {
+ changed: bool,
+ accepted: bool,
+ text: str8,
+ box: ^ui_box,
}
-BOX_MAP_BUCKET_COUNT :: 1024
-
ui_edit_move :: enum u32 {
NONE = 0,
CHAR = 1,
@@ -2044,40 +2085,16 @@ ui_edit_move :: enum u32 {
LINE = 3,
}
-ui_context :: struct {
- init: bool,
- input: input_state,
- frameCounter: u64,
- frameTime: f64,
- lastFrameDuration: f64,
- frameArena: arena,
- boxPool: pool,
- boxMap: [1024]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,
- theme: ^ui_theme,
-}
-
-ui_text_box_result :: struct {
- changed: bool,
- accepted: bool,
+ui_text_box_info :: struct {
text: str8,
+ defaultText: str8,
+ cursor: i32,
+ mark: i32,
+ selectionMode: ui_edit_move,
+ wordSelectionInitialCursor: i32,
+ wordSelectionInitialMark: i32,
+ firstDisplayedChar: i32,
+ cursorBlinkStart: f64,
}
ui_select_popup_info :: struct {
@@ -2097,82 +2114,29 @@ ui_radio_group_info :: struct {
@(default_calling_convention="c", link_prefix="oc_")
foreign {
- input_process_event :: proc(arena: ^arena, state: ^input_state, event: ^event) ---
- input_next_frame :: proc(state: ^input_state) ---
- key_down :: proc(state: ^input_state, key: key_code) -> bool ---
- key_press_count :: proc(state: ^input_state, key: key_code) -> u8 ---
- key_release_count :: proc(state: ^input_state, key: key_code) -> u8 ---
- key_repeat_count :: proc(state: ^input_state, key: key_code) -> u8 ---
- key_down_scancode :: proc(state: ^input_state, key: scan_code) -> bool ---
- key_press_count_scancode :: proc(state: ^input_state, key: scan_code) -> u8 ---
- key_release_count_scancode :: proc(state: ^input_state, key: scan_code) -> u8 ---
- key_repeat_count_scancode :: proc(state: ^input_state, key: scan_code) -> u8 ---
- mouse_down :: proc(state: ^input_state, button: mouse_button) -> bool ---
- mouse_pressed :: proc(state: ^input_state, button: mouse_button) -> u8 ---
- mouse_released :: proc(state: ^input_state, button: mouse_button) -> u8 ---
- mouse_clicked :: proc(state: ^input_state, button: mouse_button) -> bool ---
- mouse_double_clicked :: proc(state: ^input_state, button: mouse_button) -> bool ---
- mouse_position :: proc(state: ^input_state) -> vec2 ---
- mouse_delta :: proc(state: ^input_state) -> vec2 ---
- mouse_wheel :: proc(state: ^input_state) -> vec2 ---
- input_text_utf32 :: proc(arena: ^arena, state: ^input_state) -> str32 ---
- input_text_utf8 :: proc(arena: ^arena, state: ^input_state) -> str8 ---
- clipboard_pasted :: proc(state: ^input_state) -> bool ---
- clipboard_pasted_text :: proc(state: ^input_state) -> str8 ---
- key_mods :: proc(state: ^input_state) -> keymod_flags ---
- ui_init :: proc(_context: ^ui_context) ---
- ui_get_context :: proc() -> ^ui_context ---
- ui_set_context :: proc(_context: ^ui_context) ---
- ui_process_event :: proc(event: ^event) ---
- ui_begin_frame :: proc(size: vec2, #by_ptr defaultStyle: ui_style, mask: ui_style_mask) ---
- ui_end_frame :: proc() ---
- ui_draw :: proc() ---
- ui_set_theme :: proc(theme: ^ui_theme) ---
- ui_key_make_str8 :: proc(string: str8) -> ui_key ---
- ui_key_make_path :: proc(path: str8_list) -> ui_key ---
- ui_box_make_str8 :: proc(string: str8, flags: ui_flags) -> ^ui_box ---
- ui_box_begin_str8 :: proc(string: str8, flags: ui_flags) -> ^ui_box ---
- ui_box_end :: proc() -> ^ui_box ---
- ui_box_push :: proc(box: ^ui_box) ---
- ui_box_pop :: proc() ---
- ui_box_top :: proc() -> ^ui_box ---
- ui_box_lookup_key :: proc(key: ui_key) -> ^ui_box ---
- ui_box_lookup_str8 :: proc(string: str8) -> ^ui_box ---
- ui_box_set_draw_proc :: proc(box: ^ui_box, _proc: ui_box_draw_proc, data: rawptr) ---
- ui_box_closed :: proc(box: ^ui_box) -> bool ---
- ui_box_set_closed :: proc(box: ^ui_box, closed: bool) ---
- ui_box_active :: proc(box: ^ui_box) -> bool ---
- ui_box_activate :: proc(box: ^ui_box) ---
- ui_box_deactivate :: proc(box: ^ui_box) ---
- ui_box_hot :: proc(box: ^ui_box) -> bool ---
- ui_box_set_hot :: proc(box: ^ui_box, hot: bool) ---
- ui_box_sig :: proc(box: ^ui_box) -> ui_sig ---
- ui_tag_make_str8 :: proc(string: str8) -> ui_tag ---
- ui_tag_box_str8 :: proc(box: ^ui_box, string: str8) ---
- ui_tag_next_str8 :: proc(string: str8) ---
- ui_apply_style_with_mask :: proc(dst: ^ui_style, src: ^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_next :: proc(#by_ptr style: ui_style, mask: ui_style_mask) ---
- ui_style_match_before :: proc(pattern: ui_pattern, #by_ptr style: ui_style, mask: ui_style_mask) ---
- ui_style_match_after :: proc(pattern: ui_pattern, #by_ptr style: ui_style, mask: ui_style_mask) ---
- 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: ^bool) -> ui_sig ---
+ ui_label :: proc(key: cstring, label: cstring) -> ui_sig ---
+ ui_label_str8 :: proc(key: str8, label: str8) -> ui_sig ---
+ ui_button :: proc(key: cstring, text: cstring) -> ui_sig ---
+ ui_button_str8 :: proc(key: str8, text: str8) -> ui_sig ---
+ ui_checkbox :: proc(key: cstring, checked: ^bool) -> ui_sig ---
+ ui_checkbox_str8 :: proc(key: str8, checked: ^bool) -> ui_sig ---
ui_slider :: proc(name: cstring, value: ^f32) -> ^ui_box ---
- ui_scrollbar :: proc(name: cstring, thumbRatio: f32, scrollValue: ^f32) -> ^ui_box ---
- ui_tooltip :: proc(label: cstring) ---
- ui_panel_begin :: proc(name: cstring, flags: ui_flags) ---
- ui_panel_end :: proc() ---
- ui_menu_bar_begin :: proc(name: cstring) ---
+ ui_slider_str8 :: proc(name: str8, value: ^f32) -> ^ui_box ---
+ ui_tooltip :: proc(key: cstring, text: cstring) ---
+ ui_tooltip_str8 :: proc(key: str8, text: str8) ---
+ ui_menu_bar_begin :: proc(key: cstring) ---
+ ui_menu_bar_begin_str8 :: proc(key: str8) ---
ui_menu_bar_end :: proc() ---
- ui_menu_begin :: proc(label: cstring) ---
+ ui_menu_begin :: proc(key: cstring, name: cstring) ---
+ ui_menu_begin_str8 :: proc(key: str8, name: str8) ---
ui_menu_end :: proc() ---
- ui_menu_button :: proc(label: cstring) -> ui_sig ---
- 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_menu_button :: proc(key: cstring, text: cstring) -> ui_sig ---
+ ui_menu_button_str8 :: proc(key: str8, text: str8) -> ui_sig ---
+ ui_text_box :: proc(key: cstring, arena: ^arena, info: ^ui_text_box_info) -> ui_text_box_result ---
+ ui_text_box_str8 :: proc(key: str8, arena: ^arena, info: ^ui_text_box_info) -> ui_text_box_result ---
+ ui_select_popup :: proc(key: cstring, info: ^ui_select_popup_info) -> ui_select_popup_info ---
+ ui_select_popup_str8 :: proc(key: str8, info: ^ui_select_popup_info) -> ui_select_popup_info ---
+ ui_radio_group :: proc(key: cstring, info: ^ui_radio_group_info) -> ui_radio_group_info ---
+ ui_radio_group_str8 :: proc(key: str8, info: ^ui_radio_group_info) -> ui_radio_group_info ---
}
diff --git a/core/sys/posix/arpa_inet.odin b/core/sys/posix/arpa_inet.odin
index d3592dd80..ac850ed49 100644
--- a/core/sys/posix/arpa_inet.odin
+++ b/core/sys/posix/arpa_inet.odin
@@ -1,10 +1,12 @@
-#+build darwin, linux, freebsd, openbsd, netbsd
+#+build darwin, linux, freebsd, openbsd, netbsd, haiku
package posix
import "core:c"
when ODIN_OS == .Darwin {
foreign import lib "system:System.framework"
+} else when ODIN_OS == .Haiku {
+ foreign import lib "system:network"
} else {
foreign import lib "system:c"
}
diff --git a/core/sys/posix/dirent.odin b/core/sys/posix/dirent.odin
index bf32be8cf..1394f6b9e 100644
--- a/core/sys/posix/dirent.odin
+++ b/core/sys/posix/dirent.odin
@@ -1,4 +1,4 @@
-#+build darwin, linux, freebsd, openbsd, netbsd
+#+build darwin, linux, freebsd, openbsd, netbsd, haiku
package posix
import "core:c"
@@ -219,12 +219,23 @@ when ODIN_OS == .Darwin {
} else when ODIN_OS == .Linux {
- dirent :: struct {
- d_ino: u64, /* [PSX] file number of entry */
- d_off: i64, /* directory offset of the next entry */
- d_reclen: u16, /* length of this record */
- d_type: D_Type, /* file type */
- d_name: [256]c.char `fmt:"s,0"`, /* [PSX] entry name */
- }
+ dirent :: struct {
+ d_ino: u64, /* [PSX] file number of entry */
+ d_off: i64, /* directory offset of the next entry */
+ d_reclen: u16, /* length of this record */
+ d_type: D_Type, /* file type */
+ d_name: [256]c.char `fmt:"s,0"`, /* [PSX] entry name */
+ }
+
+} else when ODIN_OS == .Haiku {
+
+ dirent :: struct {
+ d_dev: dev_t, /* device */
+ d_pdev: dev_t, /* parent device (only for queries) */
+ d_ino: ino_t, /* inode number */
+ d_pino: ino_t, /* parent inode (only for queries) */
+ d_reclen: c.ushort, /* length of this record, not the name */
+ d_name: [0]c.char `fmt:"s,0"`, /* name of the entry (null byte terminated) */
+ }
}
diff --git a/core/sys/posix/errno.odin b/core/sys/posix/errno.odin
index 9bc77f12e..bb4e9e045 100644
--- a/core/sys/posix/errno.odin
+++ b/core/sys/posix/errno.odin
@@ -1,4 +1,4 @@
-#+build windows, darwin, linux, freebsd, openbsd, netbsd
+#+build windows, darwin, linux, freebsd, openbsd, netbsd, haiku
package posix
import "core:c"
@@ -536,5 +536,92 @@ when ODIN_OS == .Darwin {
ETXTBSY :: 139
EWOULDBLOCK :: 140
EXDEV :: 18
+} else when ODIN_OS == .Haiku {
+ _HAIKU_USE_POSITIVE_POSIX_ERRORS :: libc._HAIKU_USE_POSITIVE_POSIX_ERRORS
+ _POSIX_ERROR_FACTOR :: libc._POSIX_ERROR_FACTOR
+
+ _GENERAL_ERROR_BASE :: min(c.int)
+ _OS_ERROR_BASE :: _GENERAL_ERROR_BASE + 0x1000
+ _STORAGE_ERROR_BASE :: _GENERAL_ERROR_BASE + 0x6000
+ _POSIX_ERROR_BASE :: _GENERAL_ERROR_BASE + 0x7000
+
+ EIO :: _POSIX_ERROR_FACTOR * (_GENERAL_ERROR_BASE + 1) // B_IO_ERROR
+ EACCES :: _POSIX_ERROR_FACTOR * (_GENERAL_ERROR_BASE + 2) // B_PERMISSION_DENIED
+ EINVAL :: _POSIX_ERROR_FACTOR * (_GENERAL_ERROR_BASE + 5) // B_BAD_VALUE
+ ETIMEDOUT :: _POSIX_ERROR_FACTOR * (_GENERAL_ERROR_BASE + 9) // B_TIMED_OUT
+ EINTR :: _POSIX_ERROR_FACTOR * (_GENERAL_ERROR_BASE + 10) // B_INTERRUPTED
+ EAGAIN :: _POSIX_ERROR_FACTOR * (_GENERAL_ERROR_BASE + 11) // B_WOULD_BLOCK /* SysV compatibility */
+ EWOULDBLOCK :: _POSIX_ERROR_FACTOR * (_GENERAL_ERROR_BASE + 11) // B_WOULD_BLOCK /* BSD compatibility */
+ EBUSY :: _POSIX_ERROR_FACTOR * (_GENERAL_ERROR_BASE + 14) // B_BUSY
+ EPERM :: _POSIX_ERROR_FACTOR * (_GENERAL_ERROR_BASE + 15) // B_NOT_ALLOWED
+ EFAULT :: _POSIX_ERROR_FACTOR * (_OS_ERROR_BASE + 0x301) // B_BAD_ADDRESS
+ ENOEXEC :: _POSIX_ERROR_FACTOR * (_OS_ERROR_BASE + 0x302) // B_NOT_AN_EXECUTABLE
+ EBADF :: _POSIX_ERROR_FACTOR * (_STORAGE_ERROR_BASE + 0) // B_FILE_ERROR
+ EEXIST :: _POSIX_ERROR_FACTOR * (_STORAGE_ERROR_BASE + 2) // B_FILE_EXISTS
+ ENOENT :: _POSIX_ERROR_FACTOR * (_STORAGE_ERROR_BASE + 3) // B_ENTRY_NOT_FOUND
+ ENAMETOOLONG :: _POSIX_ERROR_FACTOR * (_STORAGE_ERROR_BASE + 4) // B_NAME_TOO_LONG
+ ENOTDIR :: _POSIX_ERROR_FACTOR * (_STORAGE_ERROR_BASE + 5) // B_NOT_A_DIRECTORY
+ ENOTEMPTY :: _POSIX_ERROR_FACTOR * (_STORAGE_ERROR_BASE + 6) // B_DIRECTORY_NOT_EMPTY
+ ENOSPC :: _POSIX_ERROR_FACTOR * (_STORAGE_ERROR_BASE + 7) // B_DEVICE_FULL
+ EROFS :: _POSIX_ERROR_FACTOR * (_STORAGE_ERROR_BASE + 8) // B_READ_ONLY_DEVICE
+ EISDIR :: _POSIX_ERROR_FACTOR * (_STORAGE_ERROR_BASE + 9) // B_IS_A_DIRECTORY
+ EMFILE :: _POSIX_ERROR_FACTOR * (_STORAGE_ERROR_BASE + 10) // B_NO_MORE_FDS
+ EXDEV :: _POSIX_ERROR_FACTOR * (_STORAGE_ERROR_BASE + 11) // B_CROSS_DEVICE_LINK
+ ELOOP :: _POSIX_ERROR_FACTOR * (_STORAGE_ERROR_BASE + 12) // B_LINK_LIMIT
+ EPIPE :: _POSIX_ERROR_FACTOR * (_STORAGE_ERROR_BASE + 13) // B_BUSTED_PIPE
+ ENOMEM :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 0) when _HAIKU_USE_POSITIVE_POSIX_ERRORS else (_GENERAL_ERROR_BASE + 0) // B_NO_MEMORY
+ E2BIG :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 1)
+ ECHILD :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 2)
+ EDEADLK :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 3)
+ EFBIG :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 4)
+ EMLINK :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 5)
+ ENFILE :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 6)
+ ENODEV :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 7)
+ ENOLCK :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 8)
+ ENOSYS :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 9)
+ ENOTTY :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 10)
+ ENXIO :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 11)
+ ESPIPE :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 12)
+ ESRCH :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 13)
+ EPROTOTYPE :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 18)
+ EPROTONOSUPPORT :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 19)
+ EAFNOSUPPORT :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 21)
+ EADDRINUSE :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 22)
+ EADDRNOTAVAIL :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 23)
+ ENETDOWN :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 24)
+ ENETUNREACH :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 25)
+ ENETRESET :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 26)
+ ECONNABORTED :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 27)
+ ECONNRESET :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 28)
+ EISCONN :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 29)
+ ENOTCONN :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 30)
+ ECONNREFUSED :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 32)
+ EHOSTUNREACH :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 33)
+ ENOPROTOOPT :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 34)
+ ENOBUFS :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 35)
+ EINPROGRESS :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 36)
+ EALREADY :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 37)
+ ENOMSG :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 39)
+ ESTALE :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 40)
+ EOVERFLOW :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 41)
+ EMSGSIZE :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 42)
+ EOPNOTSUPP :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 43)
+ ENOTSOCK :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 44)
+ EBADMSG :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 46)
+ ECANCELED :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 47)
+ EDESTADDRREQ :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 48)
+ EDQUOT :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 49)
+ EIDRM :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 50)
+ EMULTIHOP :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 51)
+ ENODATA :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 52)
+ ENOLINK :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 53)
+ ENOSR :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 54)
+ ENOSTR :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 55)
+ ENOTSUP :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 56)
+ EPROTO :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 57)
+ ETIME :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 58)
+ ETXTBSY :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 59)
+ ENOTRECOVERABLE :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 61)
+ EOWNERDEAD :: _POSIX_ERROR_FACTOR * (_POSIX_ERROR_BASE + 62)
}
diff --git a/core/sys/posix/fcntl.odin b/core/sys/posix/fcntl.odin
index d948af600..bc0b5b5ba 100644
--- a/core/sys/posix/fcntl.odin
+++ b/core/sys/posix/fcntl.odin
@@ -1,4 +1,4 @@
-#+build linux, darwin, openbsd, freebsd, netbsd
+#+build linux, darwin, openbsd, freebsd, netbsd, haiku
package posix
import "core:c"
@@ -343,6 +343,7 @@ when ODIN_OS == .Darwin {
l_type: Lock_Type, /* [PSX] type of lock */
l_whence: c.short, /* [PSX] flag (Whence) of starting offset */
}
+
} else when ODIN_OS == .OpenBSD {
off_t :: distinct c.int64_t
@@ -408,6 +409,72 @@ when ODIN_OS == .Darwin {
l_whence: c.short, /* [PSX] flag (Whence) of starting offset */
}
+} else when ODIN_OS == .Haiku {
+
+ off_t :: distinct c.int64_t
+ pid_t :: distinct c.int32_t
+
+ /* commands that can be passed to fcntl() */
+ F_DUPFD :: 0x0001 /* duplicate fd */
+ F_GETFD :: 0x0002 /* get fd flags */
+ F_SETFD :: 0x0004 /* set fd flags */
+ F_GETFL :: 0x0008 /* get file status flags and access mode */
+ F_SETFL :: 0x0010 /* set file status flags */
+ F_GETLK :: 0x0020 /* get locking information */
+ F_SETLK :: 0x0080 /* set locking information */
+ F_SETLKW :: 0x0100 /* as above, but waits if blocked */
+ F_DUPFD_CLOEXEC :: 0x0200 /* duplicate fd with close on exec set */
+ F_GETOWN :: -1 // NOTE: Not supported.
+ F_SETOWN :: -1 // NOTE: Not supported.
+
+ /* advisory locking types */
+ F_RDLCK :: 0x0040 /* read or shared lock */
+ F_UNLCK :: 0x0200 /* unlock */
+ F_WRLCK :: 0x0400 /* write or exclusive lock */
+
+ /* file descriptor flags for fcntl() */
+ FD_CLOEXEC :: 1
+
+ O_CLOEXEC :: 0x00000040
+ O_CREAT :: 0x0200
+ O_DIRECTORY :: 0x00200000
+ O_EXCL :: 0x0100
+ O_NOCTTY :: 0x1000
+ O_NOFOLLOW :: 0x00080000
+ O_TRUNC :: 0x0400
+
+ _O_TTY_INIT :: 0
+ O_TTY_INIT :: O_Flags{} // NOTE: not defined in the headers
+
+ O_APPEND :: 0x0800
+ O_DSYNC :: 0x040000
+ O_NONBLOCK :: 0x0080
+ O_SYNC :: 0x010000
+ O_RSYNC :: 0x020000
+
+ O_EXEC :: 0x04000000 // NOTE: not defined in the headers
+ O_RDONLY :: 0
+ O_RDWR :: 0x0002
+ O_WRONLY :: 0x0001
+
+ _O_SEARCH :: 0
+ O_SEARCH :: O_Flags{} // NOTE: not defined in the headers
+
+ AT_FDCWD: FD: -100
+
+ AT_EACCESS :: 0x08
+ AT_SYMLINK_NOFOLLOW :: 0x01
+ AT_SYMLINK_FOLLOW :: 0x02
+ AT_REMOVEDIR :: 0x04
+
+ flock :: struct {
+ l_type: Lock_Type, /* [PSX] type of lock */
+ l_whence: c.short, /* [PSX] flag (Whence) of starting offset */
+ l_start: off_t, /* [PSX] relative offset in bytes */
+ l_len: off_t, /* [PSX] size; if 0 then until EOF */
+ l_pid: pid_t, /* [PSX] process ID of the process holding the lock */
+ }
+
} else when ODIN_OS == .Linux {
off_t :: distinct c.int64_t
diff --git a/core/sys/posix/fnmatch.odin b/core/sys/posix/fnmatch.odin
index 2d582705c..04c3d2888 100644
--- a/core/sys/posix/fnmatch.odin
+++ b/core/sys/posix/fnmatch.odin
@@ -1,4 +1,4 @@
-#+build darwin, linux, openbsd, freebsd, netbsd
+#+build darwin, linux, openbsd, freebsd, netbsd, haiku
package posix
import "core:c"
@@ -46,7 +46,7 @@ FNM_Flag_Bits :: enum c.int {
}
FNM_Flags :: bit_set[FNM_Flag_Bits; c.int]
-when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
+when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Haiku {
FNM_NOMATCH :: 1
diff --git a/core/sys/posix/glob.odin b/core/sys/posix/glob.odin
index 7c8009a59..fb90b7546 100644
--- a/core/sys/posix/glob.odin
+++ b/core/sys/posix/glob.odin
@@ -1,4 +1,4 @@
-#+build linux, darwin, netbsd, openbsd, freebsd
+#+build linux, darwin, netbsd, openbsd, freebsd, haiku
package posix
import "core:c"
@@ -109,7 +109,7 @@ when ODIN_OS == .Darwin {
GLOB_NOMATCH :: -3
GLOB_NOSPACE :: -1
-} else when ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD {
+} else when ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .Haiku {
glob_t :: struct {
gl_pathc: c.size_t, /* [PSX] count of paths matched by pattern */
@@ -134,7 +134,7 @@ when ODIN_OS == .Darwin {
GLOB_ERR :: 0x0004
GLOB_MARK :: 0x0008
GLOB_NOCHECK :: 0x0010
- GLOB_NOESCAPE :: 0x2000 when ODIN_OS == .FreeBSD else 0x0100
+ GLOB_NOESCAPE :: 0x2000 when ODIN_OS == .FreeBSD || ODIN_OS == .Haiku else 0x0100
GLOB_NOSORT :: 0x0020
GLOB_ABORTED :: -2
diff --git a/core/sys/posix/grp.odin b/core/sys/posix/grp.odin
index 956ed148b..3694308a0 100644
--- a/core/sys/posix/grp.odin
+++ b/core/sys/posix/grp.odin
@@ -1,4 +1,4 @@
-#+build linux, darwin, netbsd, openbsd, freebsd
+#+build linux, darwin, netbsd, openbsd, freebsd, haiku
package posix
import "core:c"
@@ -115,7 +115,7 @@ foreign lib {
getgrnam_r :: proc(name: cstring, grp: ^group, buffer: [^]byte, bufsize: c.size_t, result: ^^group) -> Errno ---
}
-when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux {
+when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Haiku || ODIN_OS == .Linux {
gid_t :: distinct c.uint32_t
diff --git a/core/sys/posix/langinfo.odin b/core/sys/posix/langinfo.odin
index 3c001aee0..1fddfe280 100644
--- a/core/sys/posix/langinfo.odin
+++ b/core/sys/posix/langinfo.odin
@@ -1,4 +1,4 @@
-#+build linux, darwin, netbsd, openbsd, freebsd
+#+build linux, darwin, netbsd, openbsd, freebsd, haiku
package posix
import "core:c"
@@ -143,7 +143,7 @@ nl_item :: enum nl_item_t {
CRNCYSTR = CRNCYSTR,
}
-when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD {
+when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .Haiku {
// NOTE: declared with `_t` so we can enumerate the real `nl_info`.
nl_item_t :: distinct c.int
@@ -210,7 +210,7 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD {
YESEXPR :: 52
NOEXPR :: 53
- CRNCYSTR :: 56
+ CRNCYSTR :: 54 when ODIN_OS == .Haiku else 56
} else when ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
diff --git a/core/sys/posix/libgen.odin b/core/sys/posix/libgen.odin
index 69176a557..2354bf70d 100644
--- a/core/sys/posix/libgen.odin
+++ b/core/sys/posix/libgen.odin
@@ -1,4 +1,4 @@
-#+build linux, darwin, netbsd, openbsd, freebsd
+#+build linux, darwin, netbsd, openbsd, freebsd, haiku
package posix
when ODIN_OS == .Darwin {
diff --git a/core/sys/posix/locale.odin b/core/sys/posix/locale.odin
index 5b8d7c216..bbe10e803 100644
--- a/core/sys/posix/locale.odin
+++ b/core/sys/posix/locale.odin
@@ -1,4 +1,4 @@
-#+build windows, linux, darwin, netbsd, openbsd, freebsd
+#+build windows, linux, darwin, netbsd, openbsd, freebsd, haiku
package posix
import "core:c/libc"
diff --git a/core/sys/posix/monetary.odin b/core/sys/posix/monetary.odin
index ee342e211..a444bff09 100644
--- a/core/sys/posix/monetary.odin
+++ b/core/sys/posix/monetary.odin
@@ -1,4 +1,4 @@
-#+build linux, darwin, netbsd, openbsd, freebsd
+#+build linux, darwin, netbsd, openbsd, freebsd, haiku
package posix
import "core:c"
diff --git a/core/sys/posix/netdb.odin b/core/sys/posix/netdb.odin
index 79e13a140..ff1cb9d4c 100644
--- a/core/sys/posix/netdb.odin
+++ b/core/sys/posix/netdb.odin
@@ -1,4 +1,4 @@
-#+build linux, darwin, netbsd, openbsd, freebsd
+#+build linux, darwin, netbsd, openbsd, freebsd, haiku
package posix
import "core:c"
@@ -319,7 +319,7 @@ Info_Errno :: enum c.int {
OVERFLOW = EAI_OVERFLOW,
}
-when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux {
+when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux || ODIN_OS == .Haiku {
hostent :: struct {
h_name: cstring, /* [PSX] official name of host */
@@ -352,15 +352,28 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
// The highest reserved port number.
IPPORT_RESERVED :: 1024
- addrinfo :: struct {
- ai_flags: Addrinfo_Flags, /* [PSX] input flags */
- ai_family: AF, /* [PSX] address family of socket */
- ai_socktype: Sock, /* [PSX] socket type */
- ai_protocol: Protocol, /* [PSX] protocol of socket */
- ai_addrlen: socklen_t, /* [PSX] length of socket address */
- ai_canonname: cstring, /* [PSX] canonical name of service location */
- ai_addr: ^sockaddr, /* [PSX] binary address */
- ai_next: ^addrinfo, /* [PSX] pointer to next in list */
+ when ODIN_OS == .Linux || ODIN_OS == .OpenBSD {
+ addrinfo :: struct {
+ ai_flags: Addrinfo_Flags, /* [PSX] input flags */
+ ai_family: AF, /* [PSX] address family of socket */
+ ai_socktype: Sock, /* [PSX] socket type */
+ ai_protocol: Protocol, /* [PSX] protocol of socket */
+ ai_addrlen: socklen_t, /* [PSX] length of socket address */
+ ai_addr: ^sockaddr, /* [PSX] binary address */
+ ai_canonname: cstring, /* [PSX] canonical name of service location */
+ ai_next: ^addrinfo, /* [PSX] pointer to next in list */
+ }
+ } else {
+ addrinfo :: struct {
+ ai_flags: Addrinfo_Flags, /* [PSX] input flags */
+ ai_family: AF, /* [PSX] address family of socket */
+ ai_socktype: Sock, /* [PSX] socket type */
+ ai_protocol: Protocol, /* [PSX] protocol of socket */
+ ai_addrlen: socklen_t, /* [PSX] length of socket address */
+ ai_canonname: cstring, /* [PSX] canonical name of service location */
+ ai_addr: ^sockaddr, /* [PSX] binary address */
+ ai_next: ^addrinfo, /* [PSX] pointer to next in list */
+ }
}
when ODIN_OS == .Darwin {
@@ -431,6 +444,23 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
NI_NUMERICSCOPE :: 0x100
NI_DGRAM :: 16
+ } else when ODIN_OS == .Haiku {
+
+ AI_PASSIVE :: 0x001
+ AI_CANONNAME :: 0x002
+ AI_NUMERICHOST :: 0x004
+ AI_NUMERICSERV :: 0x008
+ AI_V4MAPPED :: 0x800
+ AI_ALL :: 0x100
+ AI_ADDRCONFIG :: 0x400
+
+ NI_NOFQDN :: 0x01
+ NI_NUMERICHOST :: 0x02
+ NI_NAMEREQD :: 0x04
+ NI_NUMERICSERV :: 0x08
+ NI_DGRAM :: 0x10
+ NI_NUMERICSCOPE :: 0x40
+
}
when ODIN_OS == .OpenBSD {
diff --git a/core/sys/posix/netinet_in.odin b/core/sys/posix/netinet_in.odin
index a2cf904ce..ec05915de 100644
--- a/core/sys/posix/netinet_in.odin
+++ b/core/sys/posix/netinet_in.odin
@@ -1,4 +1,4 @@
-#+build linux, darwin, netbsd, openbsd, freebsd
+#+build linux, darwin, netbsd, openbsd, freebsd, haiku
package posix
import "core:c"
@@ -31,20 +31,31 @@ Protocol :: enum c.int {
UDP = IPPROTO_UDP,
}
-when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux {
+when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux || ODIN_OS == .Haiku {
in_addr :: struct {
s_addr: in_addr_t, /* [PSX] big endian address */
}
- in6_addr :: struct {
- using _: struct #raw_union {
- s6_addr: [16]c.uint8_t, /* [PSX] big endian address */
- __u6_addr16: [8]c.uint16_t,
- __u6_addr32: [4]c.uint32_t,
- },
+ when ODIN_OS == .Haiku {
+ in6_addr :: struct #packed {
+ using _: struct #raw_union {
+ s6_addr: [16]c.uint8_t, /* [PSX] big endian address */
+ __u6_addr16: [8]c.uint16_t,
+ __u6_addr32: [4]c.uint32_t,
+ },
+ }
+ } else {
+ in6_addr :: struct {
+ using _: struct #raw_union {
+ s6_addr: [16]c.uint8_t, /* [PSX] big endian address */
+ __u6_addr16: [8]c.uint16_t,
+ __u6_addr32: [4]c.uint32_t,
+ },
+ }
}
+
when ODIN_OS == .Linux {
sockaddr_in :: struct {
@@ -77,12 +88,20 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
} else {
+ when ODIN_OS == .Haiku {
+ @(private)
+ _SIN_ZEROSIZE :: 24
+ } else {
+ @(private)
+ _SIN_ZEROSIZE :: 8
+ }
+
sockaddr_in :: struct {
sin_len: c.uint8_t,
sin_family: sa_family_t, /* [PSX] AF_INET (but a smaller size) */
sin_port: in_port_t, /* [PSX] port number */
sin_addr: in_addr, /* [PSX] IP address */
- sin_zero: [8]c.char,
+ sin_zero: [_SIN_ZEROSIZE]c.char,
}
sockaddr_in6 :: struct {
@@ -99,13 +118,23 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
ipv6mr_interface: c.uint, /* [PSX] interface index */
}
- IPV6_JOIN_GROUP :: 12
- IPV6_LEAVE_GROUP :: 13
- IPV6_MULTICAST_HOPS :: 10
- IPV6_MULTICAST_IF :: 9
- IPV6_MULTICAST_LOOP :: 11
- IPV6_UNICAST_HOPS :: 4
- IPV6_V6ONLY :: 27
+ when ODIN_OS == .Haiku {
+ IPV6_JOIN_GROUP :: 28
+ IPV6_LEAVE_GROUP :: 29
+ IPV6_MULTICAST_HOPS :: 25
+ IPV6_MULTICAST_IF :: 24
+ IPV6_MULTICAST_LOOP :: 26
+ IPV6_UNICAST_HOPS :: 27
+ IPV6_V6ONLY :: 30
+ } else {
+ IPV6_JOIN_GROUP :: 12
+ IPV6_LEAVE_GROUP :: 13
+ IPV6_MULTICAST_HOPS :: 10
+ IPV6_MULTICAST_IF :: 9
+ IPV6_MULTICAST_LOOP :: 11
+ IPV6_UNICAST_HOPS :: 4
+ IPV6_V6ONLY :: 27
+ }
}
diff --git a/core/sys/posix/poll.odin b/core/sys/posix/poll.odin
index 9c3b8b081..44ec767a6 100644
--- a/core/sys/posix/poll.odin
+++ b/core/sys/posix/poll.odin
@@ -1,4 +1,4 @@
-#+build linux, darwin, netbsd, openbsd, freebsd
+#+build linux, darwin, netbsd, openbsd, freebsd, haiku
package posix
import "base:intrinsics"
@@ -25,7 +25,11 @@ foreign lib {
poll :: proc(fds: [^]pollfd, nfds: nfds_t, timeout: c.int) -> c.int ---
}
-nfds_t :: c.uint
+when ODIN_OS == .Haiku {
+ nfds_t :: c.ulong
+} else {
+ nfds_t :: c.uint
+}
Poll_Event_Bits :: enum c.short {
// Data other than high-priority data may be read without blocking.
@@ -53,7 +57,7 @@ Poll_Event_Bits :: enum c.short {
}
Poll_Event :: bit_set[Poll_Event_Bits; c.short]
-when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
+when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Haiku {
pollfd :: struct {
fd: FD, /* [PSX] the following descriptor being polled */
@@ -61,17 +65,36 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
revents: Poll_Event, /* [PSX] the output event flags */
}
- POLLIN :: 0x0001
- POLLRDNORM :: 0x0040
- POLLRDBAND :: 0x0080
- POLLPRI :: 0x0002
- POLLOUT :: 0x0004
- POLLWRNORM :: POLLOUT
- POLLWRBAND :: 0x0100
+ when ODIN_OS == .Haiku {
+
+ POLLIN :: 0x0001 /* any readable data available */
+ POLLOUT :: 0x0002 /* file descriptor is writeable */
+ POLLRDNORM :: POLLIN
+ POLLWRNORM :: POLLOUT
+ POLLRDBAND :: 0x0008 /* priority readable data */
+ POLLWRBAND :: 0x0010 /* priority data can be written */
+ POLLPRI :: 0x0020 /* high priority readable data */
+
+ POLLERR :: 0x0004 /* errors pending */
+ POLLHUP :: 0x0080 /* disconnected */
+ POLLNVAL :: 0x1000 /* invalid file descriptor */
+
+ } else {
+
+ POLLIN :: 0x0001
+ POLLRDNORM :: 0x0040
+ POLLRDBAND :: 0x0080
+ POLLPRI :: 0x0002
+ POLLOUT :: 0x0004
+ POLLWRNORM :: POLLOUT
+ POLLWRBAND :: 0x0100
+
+ POLLERR :: 0x0008
+ POLLHUP :: 0x0010
+ POLLNVAL :: 0x0020
+
+ }
- POLLERR :: 0x0008
- POLLHUP :: 0x0010
- POLLNVAL :: 0x0020
} else when ODIN_OS == .Linux {
diff --git a/core/sys/posix/pthread.odin b/core/sys/posix/pthread.odin
index 490064da6..36a3cd7b3 100644
--- a/core/sys/posix/pthread.odin
+++ b/core/sys/posix/pthread.odin
@@ -1,4 +1,4 @@
-#+build linux, darwin, netbsd, openbsd, freebsd
+#+build linux, darwin, netbsd, openbsd, freebsd, haiku
package posix
import "core:c"
@@ -554,6 +554,56 @@ when ODIN_OS == .Darwin {
sched_priority: c.int, /* [PSX] process or thread execution scheduling priority */
}
+} else when ODIN_OS == .Haiku {
+
+ PTHREAD_CANCEL_ASYNCHRONOUS :: 2
+ PTHREAD_CANCEL_DEFERRED :: 0
+
+ PTHREAD_CANCEL_DISABLE :: 1
+ PTHREAD_CANCEL_ENABLE :: 0
+
+ PTHREAD_CANCELED :: rawptr(uintptr(1))
+
+ PTHREAD_CREATE_DETACHED :: 0x1
+ PTHREAD_CREATE_JOINABLE :: 0
+
+ PTHREAD_EXPLICIT_SCHED :: 0
+ PTHREAD_INHERIT_SCHED :: 0x4
+
+ PTHREAD_PRIO_INHERIT :: 1
+ PTHREAD_PRIO_NONE :: 0
+ PTHREAD_PRIO_PROTECT :: 2
+
+ PTHREAD_PROCESS_SHARED :: 1
+ PTHREAD_PROCESS_PRIVATE :: 0
+
+ PTHREAD_SCOPE_PROCESS :: 0
+ PTHREAD_SCOPE_SYSTEM :: 0x2
+
+ pthread_t :: distinct rawptr
+ pthread_attr_t :: distinct rawptr
+ pthread_key_t :: distinct c.int
+
+ pthread_mutex_t :: struct {
+ flags: u32,
+ lock: i32,
+ unused: i32,
+ owner: i32,
+ owner_count: i32,
+ }
+
+ pthread_cond_t :: struct {
+ flags: u32,
+ unused: i32,
+ mutex: ^pthread_mutex_t,
+ waiter_count: i32,
+ lock: i32,
+ }
+
+ sched_param :: struct {
+ sched_priority: c.int, /* [PSX] process or thread execution scheduling priority */
+ }
+
} else when ODIN_OS == .Linux {
PTHREAD_CANCEL_DEFERRED :: 0
diff --git a/core/sys/posix/pwd.odin b/core/sys/posix/pwd.odin
index 33cbcd7c5..75d15c899 100644
--- a/core/sys/posix/pwd.odin
+++ b/core/sys/posix/pwd.odin
@@ -1,4 +1,4 @@
-#+build linux, darwin, netbsd, openbsd, freebsd
+#+build linux, darwin, netbsd, openbsd, freebsd, haiku
package posix
import "core:c"
@@ -176,4 +176,16 @@ when ODIN_OS == .Darwin || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
pw_shell: cstring, /* Shell program. */
}
+} else when ODIN_OS == .Haiku {
+
+ passwd :: struct {
+ pw_name: cstring, /* [PSX] user name */
+ pw_passwd: cstring, /* encrypted password */
+ pw_uid: uid_t, /* [PSX] user uid */
+ pw_gid: gid_t, /* [PSX] user gid */
+ pw_dir: cstring, /* Home directory. */
+ pw_shell: cstring, /* Shell program. */
+ pw_gecos: cstring, /* Real name. */
+ }
+
}
diff --git a/core/sys/posix/sched.odin b/core/sys/posix/sched.odin
index e91178b09..82b335653 100644
--- a/core/sys/posix/sched.odin
+++ b/core/sys/posix/sched.odin
@@ -1,4 +1,4 @@
-#+build linux, darwin, netbsd, openbsd, freebsd
+#+build linux, darwin, netbsd, openbsd, freebsd, haiku
package posix
import "core:c"
@@ -101,4 +101,11 @@ when ODIN_OS == .Darwin {
SCHED_FIFO :: 1
SCHED_RR :: 2
+} else when ODIN_OS == .Haiku {
+
+ SCHED_FIFO :: 1
+ SCHED_RR :: 2
+ // SCHED_SPORADIC :: 3 NOTE: not a thing on freebsd, netbsd and probably others, leaving it out
+ SCHED_OTHER :: 4
+
}
diff --git a/core/sys/posix/signal.odin b/core/sys/posix/signal.odin
index 4ba4e9943..c7e2cc09b 100644
--- a/core/sys/posix/signal.odin
+++ b/core/sys/posix/signal.odin
@@ -1,4 +1,4 @@
-#+build linux, darwin, netbsd, openbsd, freebsd
+#+build linux, darwin, netbsd, openbsd, freebsd, haiku
package posix
import "base:intrinsics"
@@ -1180,4 +1180,151 @@ when ODIN_OS == .Darwin {
SI_TIMER :: -2
SI_MESGQ :: -3
SI_ASYNCIO :: -4
+
+} else when ODIN_OS == .Haiku {
+
+ // Request that signal be held
+ SIG_HOLD :: rawptr(uintptr(3))
+
+ uid_t :: distinct c.uint32_t
+ sigset_t :: distinct u64
+
+ SIGHUP :: 1 // hangup -- tty is gone!
+ //SIGINT :: 2 // interrupt
+ SIGQUIT :: 3 // `quit' special character typed in tty
+ //SIGILL :: 4 // illegal instruction
+ SIGCHLD :: 5 // child process exited
+ //SIGABRT :: 6 // abort() called, dont' catch
+ SIGPIPE :: 7 // write to a pipe w/no readers
+ //SIGFPE :: 8 // floating point exception
+ SIGKILL :: 9 // kill a team (not catchable)
+ SIGSTOP :: 10 // suspend a thread (not catchable)
+ //SIGSEGV :: 11 // segmentation violation (read: invalid pointer)
+ SIGCONT :: 12 // continue execution if suspended
+ SIGTSTP :: 13 // `stop' special character typed in tty
+ SIGALRM :: 14 // an alarm has gone off (see alarm())
+ //SIGTERM :: 15 // termination requested
+ SIGTTIN :: 16 // read of tty from bg process
+ SIGTTOU :: 17 // write to tty from bg process
+ SIGUSR1 :: 18 // app defined signal 1
+ SIGUSR2 :: 19 // app defined signal 2
+ SIGWINCH :: 20 // tty window size changed
+ SIGKILLTHR :: 21 // be specific: kill just the thread, not team
+ SIGTRAP :: 22 // Trace/breakpoint trap
+ SIGPOLL :: 23 // Pollable event
+ SIGPROF :: 24 // Profiling timer expired
+ SIGSYS :: 25 // Bad system call
+ SIGURG :: 26 // High bandwidth data is available at socket
+ SIGVTALRM :: 27 // Virtual timer expired
+ SIGXCPU :: 28 // CPU time limit exceeded
+ SIGXFSZ :: 29 // File size limit exceeded
+ SIGBUS :: 30 // access to undefined portion of a memory object
+
+ // NOTE: this is actually defined as `sigaction`, but due to the function with the same name
+ // `_t` has been added.
+
+ sigaction_t :: struct {
+ using _: struct #raw_union {
+ sa_handler: proc "c" (Signal), /* [PSX] signal-catching function or one of the SIG_IGN or SIG_DFL */
+ sa_sigaction: proc "c" (Signal, ^siginfo_t, rawptr), /* [PSX] signal-catching function */
+ },
+ sa_mask: sigset_t, /* [PSX] set of signals to be blocked during execution of the signal handling function */
+ sa_flags: SA_Flags, /* [PSX] special flags */
+ sa_userdata: rawptr, /* will be passed to the signal handler, BeOS extension */
+ }
+
+ SIG_BLOCK :: 1
+ SIG_UNBLOCK :: 2
+ SIG_SETMASK :: 3
+
+ SA_NOCLDSTOP :: 0x01
+ SA_NOCLDWAIT :: 0x02
+ SA_RESETHAND :: 0x04
+ SA_NODEFER :: 0x08
+ SA_RESTART :: 0x10
+ SA_ONSTACK :: 0x20
+ SA_SIGINFO :: 0x40
+
+ SS_ONSTACK :: 1
+ SS_DISABLE :: 2
+
+ MINSIGSTKSZ :: 8192
+ SIGSTKSZ :: 16384
+
+ stack_t :: struct {
+ ss_sp: rawptr, /* [PSX] stack base or pointer */
+ ss_size: c.size_t, /* [PSX] stack size */
+ ss_flags: SS_Flags, /* [PSX] flags */
+ }
+
+ siginfo_t :: struct {
+ si_signo: Signal, /* [PSX] signal number */
+ si_code: struct #raw_union { /* [PSX] specific more detailed codes per signal */
+ ill: ILL_Code,
+ fpe: FPE_Code,
+ segv: SEGV_Code,
+ bus: BUS_Code,
+ trap: TRAP_Code,
+ chld: CLD_Code,
+ poll: POLL_Code,
+ any: Any_Code,
+ },
+ si_errno: Errno, /* [PSX] errno value associated with this signal */
+ si_pid: pid_t, /* sending process ID */
+ si_uid: uid_t, /* real user ID of sending process */
+ si_addr: rawptr, /* address of faulting instruction */
+ si_status: c.int, /* exit value or signal */
+ si_band: c.long, /* band event for SIGPOLL */
+ si_value: sigval, /* signal value */
+ }
+
+ /* any signal */
+ SI_USER :: 0 /* signal sent by user */
+ SI_QUEUE :: 1 /* signal sent by sigqueue() */
+ SI_TIMER :: 2 /* signal sent on timer_settime() timeout */
+ SI_ASYNCIO :: 3 /* signal sent on asynchronous I/O completion */
+ SI_MESGQ :: 4 /* signal sent on arrival of message on empty message queue */
+ /* SIGILL */
+ ILL_ILLOPC :: 10 /* illegal opcode */
+ ILL_ILLOPN :: 11 /* illegal operand */
+ ILL_ILLADR :: 12 /* illegal addressing mode */
+ ILL_ILLTRP :: 13 /* illegal trap */
+ ILL_PRVOPC :: 14 /* privileged opcode */
+ ILL_PRVREG :: 15 /* privileged register */
+ ILL_COPROC :: 16 /* coprocessor error */
+ ILL_BADSTK :: 17 /* internal stack error */
+ /* SIGFPE */
+ FPE_INTDIV :: 20 /* integer division by zero */
+ FPE_INTOVF :: 21 /* integer overflow */
+ FPE_FLTDIV :: 22 /* floating-point division by zero */
+ FPE_FLTOVF :: 23 /* floating-point overflow */
+ FPE_FLTUND :: 24 /* floating-point underflow */
+ FPE_FLTRES :: 25 /* floating-point inexact result */
+ FPE_FLTINV :: 26 /* invalid floating-point operation */
+ FPE_FLTSUB :: 27 /* subscript out of range */
+ /* SIGSEGV */
+ SEGV_MAPERR :: 30 /* address not mapped to object */
+ SEGV_ACCERR :: 31 /* invalid permissions for mapped object */
+ /* SIGBUS */
+ BUS_ADRALN :: 40 /* invalid address alignment */
+ BUS_ADRERR :: 41 /* nonexistent physical address */
+ BUS_OBJERR :: 42 /* object-specific hardware error */
+ /* SIGTRAP */
+ TRAP_BRKPT :: 50 /* process breakpoint */
+ TRAP_TRACE :: 51 /* process trace trap. */
+ /* SIGCHLD */
+ CLD_EXITED :: 60 /* child exited */
+ CLD_KILLED :: 61 /* child terminated abnormally without core dump */
+ CLD_DUMPED :: 62 /* child terminated abnormally with core dump */
+ CLD_TRAPPED :: 63 /* traced child trapped */
+ CLD_STOPPED :: 64 /* child stopped */
+ CLD_CONTINUED :: 65 /* stopped child continued */
+ /* SIGPOLL */
+ POLL_IN :: 70 /* input available */
+ POLL_OUT :: 71 /* output available */
+ POLL_MSG :: 72 /* input message available */
+ POLL_ERR :: 73 /* I/O error */
+ POLL_PRI :: 74 /* high priority input available */
+ POLL_HUP :: 75 /* device disconnected */
+
}
diff --git a/core/sys/posix/signal_libc.odin b/core/sys/posix/signal_libc.odin
index aef22da29..7a054ddd7 100644
--- a/core/sys/posix/signal_libc.odin
+++ b/core/sys/posix/signal_libc.odin
@@ -1,4 +1,4 @@
-#+build linux, windows, darwin, netbsd, openbsd, freebsd
+#+build linux, windows, darwin, netbsd, openbsd, freebsd, haiku
package posix
import "base:intrinsics"
diff --git a/core/sys/posix/stdio_libc.odin b/core/sys/posix/stdio_libc.odin
index fbd949b2c..12706970d 100644
--- a/core/sys/posix/stdio_libc.odin
+++ b/core/sys/posix/stdio_libc.odin
@@ -1,4 +1,4 @@
-#+build linux, windows, linux, darwin, netbsd, openbsd, freebsd
+#+build linux, windows, linux, darwin, netbsd, openbsd, freebsd, haiku
package posix
import "core:c"
diff --git a/core/sys/posix/stdlib.odin b/core/sys/posix/stdlib.odin
index 640c70b5a..5f1ae1908 100644
--- a/core/sys/posix/stdlib.odin
+++ b/core/sys/posix/stdlib.odin
@@ -1,4 +1,4 @@
-#+build linux, darwin, netbsd, openbsd, freebsd
+#+build linux, darwin, netbsd, openbsd, freebsd, haiku
package posix
import "base:intrinsics"
diff --git a/core/sys/posix/stdlib_libc.odin b/core/sys/posix/stdlib_libc.odin
index fa4d925b2..6574026f4 100644
--- a/core/sys/posix/stdlib_libc.odin
+++ b/core/sys/posix/stdlib_libc.odin
@@ -1,4 +1,4 @@
-#+build linux, windows, darwin, netbsd, openbsd, freebsd
+#+build linux, windows, darwin, netbsd, openbsd, freebsd, haiku
package posix
import "base:intrinsics"
diff --git a/core/sys/posix/string.odin b/core/sys/posix/string.odin
index 96b6a9007..3f9dbb43e 100644
--- a/core/sys/posix/string.odin
+++ b/core/sys/posix/string.odin
@@ -1,4 +1,4 @@
-#+build linux, darwin, netbsd, openbsd, freebsd
+#+build linux, darwin, netbsd, openbsd, freebsd, haiku
package posix
import "core:c"
diff --git a/core/sys/posix/string_libc.odin b/core/sys/posix/string_libc.odin
index 336352cbc..72164cc4c 100644
--- a/core/sys/posix/string_libc.odin
+++ b/core/sys/posix/string_libc.odin
@@ -1,4 +1,4 @@
-#+build linux, windows, darwin, netbsd, openbsd, freebsd
+#+build linux, windows, darwin, netbsd, openbsd, freebsd, haiku
package posix
when ODIN_OS == .Windows {
diff --git a/core/sys/posix/sys_ipc.odin b/core/sys/posix/sys_ipc.odin
index 0f7ec06c5..bf5938ce1 100644
--- a/core/sys/posix/sys_ipc.odin
+++ b/core/sys/posix/sys_ipc.odin
@@ -1,4 +1,4 @@
-#+build linux, darwin, netbsd, openbsd, freebsd
+#+build linux, darwin, netbsd, openbsd, freebsd, haiku
package posix
import "core:c"
@@ -111,4 +111,27 @@ when ODIN_OS == .Darwin {
IPC_SET :: 1
IPC_STAT :: 2
+} else when ODIN_OS == .Haiku {
+
+ key_t :: distinct c.int32_t
+
+ ipc_perm :: struct {
+ key: key_t,
+ uid: uid_t, /* [PSX] owner's user ID */
+ gid: gid_t, /* [PSX] owner's group ID */
+ cuid: uid_t, /* [PSX] creator's user ID */
+ cgid: gid_t, /* [PSX] creator's group ID */
+ mode: mode_t, /* [PSX] read/write perms */
+ }
+
+ IPC_CREAT :: 0o01000
+ IPC_EXCL :: 0o02000
+ IPC_NOWAIT :: 0o04000
+
+ IPC_PRIVATE :: key_t(0)
+
+ IPC_RMID :: 0
+ IPC_SET :: 1
+ IPC_STAT :: 2
+
}
diff --git a/core/sys/posix/sys_msg.odin b/core/sys/posix/sys_msg.odin
index 0e78777f9..c578b1fc6 100644
--- a/core/sys/posix/sys_msg.odin
+++ b/core/sys/posix/sys_msg.odin
@@ -1,4 +1,4 @@
-#+build linux, darwin, netbsd, openbsd, freebsd
+#+build linux, darwin, netbsd, openbsd, freebsd, haiku
package posix
import "core:c"
@@ -171,4 +171,22 @@ when ODIN_OS == .Darwin {
__unused: [2]c.ulong,
}
+} else when ODIN_OS == .Haiku {
+
+ msgqnum_t :: distinct c.uint32_t
+ msglen_t :: distinct c.uint32_t
+
+ MSG_NOERROR :: 0o10000
+
+ msqid_ds :: struct {
+ msg_perm: ipc_perm, /* [PSX] operation permission structure */
+ msg_qnum: msgqnum_t, /* [PSX] number of messages currently on queue */
+ msg_qbytes: msglen_t, /* [PSX] maximum number of bytes allowed on queue */
+ msg_lspid: pid_t, /* [PSX] process ID of last msgsnd() */
+ msg_lrpid: pid_t, /* [PSX] process ID of last msgrcv() */
+ msg_stime: time_t, /* [PSX] time of last msgsnd() */
+ msg_rtime: time_t, /* [PSX] time of last msgrcv() */
+ msg_ctime: time_t, /* [PSX] time of last change */
+ }
+
}
diff --git a/core/sys/posix/sys_resource.odin b/core/sys/posix/sys_resource.odin
index 9af2a929b..ae478382a 100644
--- a/core/sys/posix/sys_resource.odin
+++ b/core/sys/posix/sys_resource.odin
@@ -1,4 +1,4 @@
-#+build linux, darwin, netbsd, openbsd, freebsd
+#+build linux, darwin, netbsd, openbsd, freebsd, haiku
package posix
import "core:c"
@@ -96,15 +96,26 @@ when ODIN_OS == .NetBSD {
@(private) LGETRUSAGE :: "getrusage"
}
-when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux {
+when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux || ODIN_OS == .Haiku {
PRIO_PROCESS :: 0
PRIO_PGRP :: 1
PRIO_USER :: 2
- rlim_t :: distinct c.uint64_t
+ when ODIN_OS == .Haiku {
+ rlim_t :: distinct c.ulong
+ } else {
+ rlim_t :: distinct c.uint64_t
+ }
- RLIM_INFINITY :: ~rlim_t(0) when ODIN_OS == .Linux else (rlim_t(1) << 63) - 1
+ when ODIN_OS == .Haiku {
+ RLIM_INFINITY :: rlim_t(0xFFFFFFFF)
+ } else when ODIN_OS == .Linux {
+ RLIM_INFINITY :: ~rlim_t(0)
+ } else {
+ RLIM_INFINITY :: (rlim_t(1) << 63) - 1
+ }
+
RLIM_SAVED_MAX :: RLIM_INFINITY
RLIM_SAVED_CUR :: RLIM_INFINITY
@@ -140,19 +151,29 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
ru_nivcsw: c.long, /* involuntary " */
}
- RLIMIT_CORE :: 4
- RLIMIT_CPU :: 0
- RLIMIT_DATA :: 2
- RLIMIT_FSIZE :: 1
- RLIMIT_NOFILE :: 7 when ODIN_OS == .Linux else 8
- RLIMIT_STACK :: 3
-
- when ODIN_OS == .Linux {
- RLIMIT_AS :: 9
- } else when ODIN_OS == .Darwin || ODIN_OS == .OpenBSD {
- RLIMIT_AS :: 5
+ when ODIN_OS == .Haiku {
+ RLIMIT_CORE :: 0
+ RLIMIT_CPU :: 1
+ RLIMIT_DATA :: 2
+ RLIMIT_FSIZE :: 3
+ RLIMIT_NOFILE :: 4
+ RLIMIT_STACK :: 5
+ RLIMIT_AS :: 6
} else {
- RLIMIT_AS :: 10
+ RLIMIT_CORE :: 4
+ RLIMIT_CPU :: 0
+ RLIMIT_DATA :: 2
+ RLIMIT_FSIZE :: 1
+ RLIMIT_NOFILE :: 7 when ODIN_OS == .Linux else 8
+ RLIMIT_STACK :: 3
+
+ when ODIN_OS == .Linux {
+ RLIMIT_AS :: 9
+ } else when ODIN_OS == .Darwin || ODIN_OS == .OpenBSD {
+ RLIMIT_AS :: 5
+ } else {
+ RLIMIT_AS :: 10
+ }
}
}
diff --git a/core/sys/posix/sys_select.odin b/core/sys/posix/sys_select.odin
index 2058ee777..a75e58de6 100644
--- a/core/sys/posix/sys_select.odin
+++ b/core/sys/posix/sys_select.odin
@@ -1,4 +1,4 @@
-#+build linux, darwin, netbsd, openbsd, freebsd
+#+build linux, darwin, netbsd, openbsd, freebsd, haiku
package posix
import "base:intrinsics"
@@ -56,9 +56,9 @@ when ODIN_OS == .NetBSD {
LSELECT :: "select"
}
-when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux {
+when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux || ODIN_OS == .Haiku {
- suseconds_t :: distinct (c.int32_t when ODIN_OS == .Darwin || ODIN_OS == .NetBSD else c.long)
+ suseconds_t :: distinct (c.int32_t when ODIN_OS == .Darwin || ODIN_OS == .NetBSD || ODIN_OS == .Haiku else c.long)
timeval :: struct {
tv_sec: time_t, /* [PSX] seconds */
@@ -75,8 +75,14 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
@(private)
ALIGN :: align_of(c.long) when ODIN_OS == .FreeBSD || ODIN_OS == .Linux else align_of(c.int32_t)
- fd_set :: struct #align(ALIGN) {
- fds_bits: [(FD_SETSIZE / __NFDBITS) when (FD_SETSIZE % __NFDBITS) == 0 else (FD_SETSIZE / __NFDBITS) + 1]c.int32_t,
+ when ODIN_OS == .Haiku {
+ fd_set :: struct #align(ALIGN) {
+ fds_bits: [(FD_SETSIZE + (__NFDBITS - 1)) / __NFDBITS]c.int32_t,
+ }
+ } else {
+ fd_set :: struct #align(ALIGN) {
+ fds_bits: [(FD_SETSIZE / __NFDBITS) when (FD_SETSIZE % __NFDBITS) == 0 else (FD_SETSIZE / __NFDBITS) + 1]c.int32_t,
+ }
}
@(private)
diff --git a/core/sys/posix/sys_sem.odin b/core/sys/posix/sys_sem.odin
index 6b695e766..069315f87 100644
--- a/core/sys/posix/sys_sem.odin
+++ b/core/sys/posix/sys_sem.odin
@@ -1,4 +1,4 @@
-#+build linux, darwin, netbsd, openbsd, freebsd
+#+build linux, darwin, netbsd, openbsd, freebsd, haiku
package posix
import "core:c"
@@ -154,4 +154,30 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
sem_flg: c.short, /* [PSX] operation flags */
}
+} else when ODIN_OS == .Haiku {
+
+ SEM_UNDO :: 10 // undo the operation on exit
+
+ // Commands for `semctl'.
+ GETPID :: 3
+ GETVAL :: 4
+ GETALL :: 5
+ GETNCNT :: 6
+ GETZCNT :: 7
+ SETVAL :: 8
+ SETALL :: 9
+
+ semid_ds :: struct {
+ sem_perm: ipc_perm, // [PSX] operation permission structure
+ sem_nsems: c.ushort, // [PSX] number of semaphores in set
+ sem_otime: time_t, // [PSX] last semop()
+ sem_ctime: time_t, // [PSX] last time changed by semctl()
+ }
+
+ sembuf :: struct {
+ sem_num: c.ushort, /* [PSX] semaphore number */
+ sem_op: c.short, /* [PSX] semaphore operation */
+ sem_flg: c.short, /* [PSX] operation flags */
+ }
+
}
diff --git a/core/sys/posix/sys_socket.odin b/core/sys/posix/sys_socket.odin
index 4dd6074a3..0645893d0 100644
--- a/core/sys/posix/sys_socket.odin
+++ b/core/sys/posix/sys_socket.odin
@@ -1,4 +1,4 @@
-#+build linux, darwin, netbsd, openbsd, freebsd
+#+build linux, darwin, netbsd, openbsd, freebsd, haiku
package posix
import "core:c"
@@ -328,24 +328,32 @@ when ODIN_OS == .NetBSD {
@(private) LSOCKET :: "socket"
}
-when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux {
+when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux || ODIN_OS == .Haiku {
socklen_t :: distinct c.uint
+ when ODIN_OS == .Haiku {
+ @(private)
+ _SA_DATASIZE :: 30
+ } else {
+ @(private)
+ _SA_DATASIZE :: 14
+ }
+
when ODIN_OS == .Linux {
_sa_family_t :: distinct c.ushort
sockaddr :: struct {
- sa_family: sa_family_t, /* [PSX] address family */
- sa_data: [14]c.char, /* [PSX] socket address */
+ sa_family: sa_family_t, /* [PSX] address family */
+ sa_data: [_SA_DATASIZE]c.char, /* [PSX] socket address */
}
} else {
_sa_family_t :: distinct c.uint8_t
sockaddr :: struct {
- sa_len: c.uint8_t, /* total length */
- sa_family: sa_family_t, /* [PSX] address family */
- sa_data: [14]c.char, /* [PSX] socket address */
+ sa_len: c.uint8_t, /* total length */
+ sa_family: sa_family_t, /* [PSX] address family */
+ sa_data: [_SA_DATASIZE]c.char, /* [PSX] socket address */
}
}
@@ -355,6 +363,11 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
_SS_PAD1SIZE :: 6
@(private)
_SS_PAD2SIZE :: 240
+ } else when ODIN_OS == .Haiku {
+ @(private)
+ _SS_PAD1SIZE :: 6
+ @(private)
+ _SS_PAD2SIZE :: 112
} else when ODIN_OS == .Linux {
@(private)
_SS_SIZE :: 128
@@ -486,6 +499,26 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
SO_RCVTIMEO :: 66
SO_SNDTIMEO :: 67
+ } else when ODIN_OS == .Haiku {
+ SOL_SOCKET :: -1
+
+ SO_ACCEPTCONN :: 0x00000001
+ SO_BROADCAST :: 0x00000002
+ SO_DEBUG :: 0x00000004
+ SO_DONTROUTE :: 0x00000008
+ SO_ERROR :: 0x40000007
+ SO_KEEPALIVE :: 0x00000010
+ SO_OOBINLINE :: 0x00000020
+ SO_RCVBUF :: 0x40000004
+ SO_RCVLOWAT :: 0x40000005
+ SO_REUSEADDR :: 0x00000040
+ SO_SNDBUF :: 0x40000001
+ SO_SNDLOWAT :: 0x40000002
+ SO_TYPE :: 0x40000008
+
+ SO_LINGER :: 0x00000200
+ SO_RCVTIMEO :: 0x40000006
+ SO_SNDTIMEO :: 0x40000003
} else {
SOL_SOCKET :: 0xffff
@@ -523,7 +556,11 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
}
// The maximum backlog queue length for listen().
- SOMAXCONN :: 128
+ when ODIN_OS == .Haiku {
+ SOMAXCONN :: 32
+ } else {
+ SOMAXCONN :: 128
+ }
when ODIN_OS == .Linux {
MSG_CTRUNC :: 0x008
@@ -549,11 +586,18 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
MSG_NOSIGNAL :: 0x00020000
} else when ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
MSG_NOSIGNAL :: 0x0400
+ } else when ODIN_OS == .Haiku {
+ MSG_NOSIGNAL :: 0x800
}
}
- AF_INET :: 2
- AF_UNIX :: 1
+ when ODIN_OS == .Haiku {
+ AF_INET :: 1
+ AF_UNIX :: 9
+ } else {
+ AF_INET :: 2
+ AF_UNIX :: 1
+ }
when ODIN_OS == .Darwin {
AF_INET6 :: 30
@@ -563,6 +607,8 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
AF_INET6 :: 24
} else when ODIN_OS == .Linux {
AF_INET6 :: 10
+ } else when ODIN_OS == .Haiku {
+ AF_INET6 :: 5
}
SHUT_RD :: 0
diff --git a/core/sys/posix/sys_stat.odin b/core/sys/posix/sys_stat.odin
index 61b98ef35..265356e54 100644
--- a/core/sys/posix/sys_stat.odin
+++ b/core/sys/posix/sys_stat.odin
@@ -1,4 +1,4 @@
-#+build linux, darwin, netbsd, openbsd, freebsd
+#+build linux, darwin, netbsd, openbsd, freebsd, haiku
package posix
import "core:c"
@@ -428,6 +428,36 @@ when ODIN_OS == .Darwin {
UTIME_NOW :: -2
UTIME_OMIT :: -1
+} else when ODIN_OS == .Haiku {
+
+ dev_t :: distinct c.int32_t
+ nlink_t :: distinct c.int32_t
+ _mode_t :: distinct c.uint32_t
+ blkcnt_t :: distinct c.int64_t
+ blksize_t :: distinct c.int32_t
+ ino_t :: distinct c.int64_t
+
+ stat_t :: struct {
+ st_dev: dev_t, /* [PSX] ID of device containing file */
+ st_ino: ino_t, /* [PSX] file serial number */
+ st_mode: mode_t, /* [PSX] mode of file */
+ st_nlink: nlink_t, /* [PSX] number of hard links */
+ st_uid: uid_t, /* [PSX] user ID of the file */
+ st_gid: gid_t, /* [PSX] group ID of the file */
+ st_size: off_t, /* [PSX] file size, in bytes */
+ st_rdev: dev_t, /* [PSX] device ID */
+ st_blksize: blksize_t, /* [PSX] optimal blocksize for I/O */
+ st_atim: timespec, /* [PSX] time of last access */
+ st_mtim: timespec, /* [PSX] time of last data modification */
+ st_ctim: timespec, /* [PSX] time of last status change */
+ st_crtim: timespec, /* [PSX] time of last status change */
+ st_type: c.uint32_t,
+ st_blocks: blkcnt_t, /* [PSX] blocks allocated for file */
+ }
+
+ UTIME_NOW :: 1000000000
+ UTIME_OMIT :: 1000000001
+
} else when ODIN_OS == .Linux {
dev_t :: distinct u64
diff --git a/core/sys/posix/sys_time.odin b/core/sys/posix/sys_time.odin
index 3036352aa..94eafec85 100644
--- a/core/sys/posix/sys_time.odin
+++ b/core/sys/posix/sys_time.odin
@@ -1,4 +1,4 @@
-#+build linux, darwin, netbsd, openbsd, freebsd
+#+build linux, darwin, netbsd, openbsd, freebsd, haiku
package posix
import "core:c"
@@ -78,4 +78,15 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
ITIMER_VIRTUAL :: 1
ITIMER_PROF :: 2
+} else when ODIN_OS == .Haiku {
+
+ itimerval :: struct {
+ it_interval: timeval, /* [PSX] timer interval */
+ it_value: timeval, /* [PSX] current value */
+ }
+
+ ITIMER_REAL :: 1
+ ITIMER_VIRTUAL :: 2
+ ITIMER_PROF :: 3
+
}
diff --git a/core/sys/posix/sys_times.odin b/core/sys/posix/sys_times.odin
index 113e3f963..73db489a7 100644
--- a/core/sys/posix/sys_times.odin
+++ b/core/sys/posix/sys_times.odin
@@ -1,4 +1,4 @@
-#+build linux, darwin, netbsd, openbsd, freebsd
+#+build linux, darwin, netbsd, openbsd, freebsd, haiku
package posix
when ODIN_OS == .Darwin {
@@ -25,7 +25,7 @@ when ODIN_OS == .NetBSD {
@(private) LTIMES :: "times"
}
-when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux {
+when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux || ODIN_OS == .Haiku {
tms :: struct {
tms_utime: clock_t, /* [PSX] user CPU time */
diff --git a/core/sys/posix/sys_uio.odin b/core/sys/posix/sys_uio.odin
index a0ad2934e..5770f8058 100644
--- a/core/sys/posix/sys_uio.odin
+++ b/core/sys/posix/sys_uio.odin
@@ -1,4 +1,4 @@
-#+build linux, darwin, netbsd, openbsd, freebsd
+#+build linux, darwin, netbsd, openbsd, freebsd, haiku
package posix
import "core:c"
@@ -31,7 +31,7 @@ foreign libc {
writev :: proc(fildes: FD, iov: [^]iovec, iovcnt: c.int) -> c.ssize_t ---
}
-when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux {
+when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux || ODIN_OS == .Haiku {
iovec :: struct {
iov_base: rawptr, /* [PSX] base address of I/O memory region */
diff --git a/core/sys/posix/sys_un.odin b/core/sys/posix/sys_un.odin
index ca5c4ee31..167bf3ce1 100644
--- a/core/sys/posix/sys_un.odin
+++ b/core/sys/posix/sys_un.odin
@@ -1,4 +1,4 @@
-#+build linux, darwin, netbsd, openbsd, freebsd
+#+build linux, darwin, netbsd, openbsd, freebsd, haiku
package posix
import "core:c"
@@ -20,4 +20,12 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
sun_path: [108]c.char, /* [PSX] socket pathname */
}
+} else when ODIN_OS == .Haiku {
+
+ sockaddr_un :: struct {
+ sun_len: c.uint8_t,
+ sun_family: sa_family_t, /* [PSX] address family */
+ sun_path: [126]c.char, /* [PSX] socket pathname */
+ }
+
}
diff --git a/core/sys/posix/sys_utsname.odin b/core/sys/posix/sys_utsname.odin
index 64930160f..5ea8807a7 100644
--- a/core/sys/posix/sys_utsname.odin
+++ b/core/sys/posix/sys_utsname.odin
@@ -1,4 +1,4 @@
-#+build linux, darwin, netbsd, openbsd, freebsd
+#+build linux, darwin, netbsd, openbsd, freebsd, haiku
package posix
import "core:c"
@@ -38,10 +38,10 @@ foreign lib {
uname :: proc(uname: ^utsname) -> c.int ---
}
-when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
+when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Haiku {
@(private)
- _SYS_NAMELEN :: 256
+ _SYS_NAMELEN :: 32 when ODIN_OS == .Haiku else 256
utsname :: struct {
sysname: [_SYS_NAMELEN]c.char `fmt:"s,0"`, /* [PSX] name of OS */
diff --git a/core/sys/posix/sys_wait.odin b/core/sys/posix/sys_wait.odin
index 812bd8c62..d3bcdfddd 100644
--- a/core/sys/posix/sys_wait.odin
+++ b/core/sys/posix/sys_wait.odin
@@ -1,4 +1,4 @@
-#+build linux, darwin, netbsd, openbsd, freebsd
+#+build linux, darwin, netbsd, openbsd, freebsd, haiku
package posix
import "core:c"
@@ -442,4 +442,56 @@ when ODIN_OS == .Darwin {
_WIFCONTINUED :: #force_inline proc "contextless" (x: c.int) -> bool {
return x == 0xffff
}
+
+} else when ODIN_OS == .Haiku {
+
+ id_t :: distinct c.int32_t
+
+ WCONTINUED :: 0x04
+ WNOHANG :: 0x01
+ WUNTRACED :: 0x02
+
+ WEXITED :: 0x08
+ WNOWAIT :: 0x20
+ WSTOPPED :: 0x10
+
+ _P_ALL :: 0
+ _P_PID :: 1
+ _P_PGID :: 2
+
+ @(private)
+ _WIFEXITED :: #force_inline proc "contextless" (x: c.int) -> bool {
+ return (x & ~(c.int)(0xff)) == 0
+ }
+
+ @(private)
+ _WEXITSTATUS :: #force_inline proc "contextless" (x: c.int) -> c.int {
+ return x & 0xff
+ }
+
+ @(private)
+ _WIFSIGNALED :: #force_inline proc "contextless" (x: c.int) -> bool {
+ return ((x >> 8) & 0xff) != 0
+ }
+
+ @(private)
+ _WTERMSIG :: #force_inline proc "contextless" (x: c.int) -> Signal {
+ return Signal((x >> 8) & 0xff)
+ }
+
+ @(private)
+ _WIFSTOPPED :: #force_inline proc "contextless" (x: c.int) -> bool {
+ return ((x >> 16) & 0xff) != 0
+ }
+
+ @(private)
+ _WSTOPSIG :: #force_inline proc "contextless" (x: c.int) -> Signal {
+ return Signal((x >> 16) & 0xff)
+ }
+
+ @(private)
+ _WIFCONTINUED :: #force_inline proc "contextless" (x: c.int) -> bool {
+ return (x & 0x20000) != 0
+ }
+
}
diff --git a/core/sys/posix/termios.odin b/core/sys/posix/termios.odin
index 0c07eceb9..4ca884e87 100644
--- a/core/sys/posix/termios.odin
+++ b/core/sys/posix/termios.odin
@@ -1,4 +1,4 @@
-#+build linux, darwin, netbsd, openbsd, freebsd
+#+build linux, darwin, netbsd, openbsd, freebsd, haiku
package posix
import "core:c"
@@ -138,17 +138,30 @@ CLocal_Flag_Bits :: enum tcflag_t {
}
CLocal_Flags :: bit_set[CLocal_Flag_Bits; tcflag_t]
-CControl_Flag_Bits :: enum tcflag_t {
- // CS5 = log2(CS5), /* 5 bits (pseudo) (default) */
- CS6 = log2(CS6), /* 6 bits */
- CS7 = log2(CS7), /* 7 bits */
- CS8 = log2(CS8), /* 8 bits */
- CSTOPB = log2(CSTOPB), /* send 2 stop bits */
- CREAD = log2(CREAD), /* enable receiver */
- PARENB = log2(PARENB), /* parity enable */
- PARODD = log2(PARODD), /* odd parity, else even */
- HUPCL = log2(HUPCL), /* hang up on last close */
- CLOCAL = log2(CLOCAL), /* ignore modem status lines */
+when ODIN_OS == .Haiku {
+ CControl_Flag_Bits :: enum tcflag_t {
+ // CS7 = log2(CS7), /* 7 bits (default) */
+ CS8 = log2(CS8), /* 8 bits */
+ CSTOPB = log2(CSTOPB), /* send 2 stop bits */
+ CREAD = log2(CREAD), /* enable receiver */
+ PARENB = log2(PARENB), /* parity enable */
+ PARODD = log2(PARODD), /* odd parity, else even */
+ HUPCL = log2(HUPCL), /* hang up on last close */
+ CLOCAL = log2(CLOCAL), /* ignore modem status lines */
+ }
+} else {
+ CControl_Flag_Bits :: enum tcflag_t {
+ // CS5 = log2(CS5), /* 5 bits (pseudo) (default) */
+ CS6 = log2(CS6), /* 6 bits */
+ CS7 = log2(CS7), /* 7 bits */
+ CS8 = log2(CS8), /* 8 bits */
+ CSTOPB = log2(CSTOPB), /* send 2 stop bits */
+ CREAD = log2(CREAD), /* enable receiver */
+ PARENB = log2(PARENB), /* parity enable */
+ PARODD = log2(PARODD), /* odd parity, else even */
+ HUPCL = log2(HUPCL), /* hang up on last close */
+ CLOCAL = log2(CLOCAL), /* ignore modem status lines */
+ }
}
CControl_Flags :: bit_set[CControl_Flag_Bits; tcflag_t]
@@ -597,4 +610,151 @@ when ODIN_OS == .Darwin {
TCOOFF :: 0
TCOON :: 1
+} else when ODIN_OS == .Haiku {
+
+ cc_t :: distinct c.uchar
+ _speed_t :: distinct c.uint32_t
+ tcflag_t :: distinct c.uint16_t
+
+ // Same as speed_t, but 16-bit.
+ CSpeed :: enum tcflag_t {
+ B0 = B0,
+ B50 = B50,
+ B75 = B75,
+ B110 = B110,
+ B134 = B134,
+ B150 = B150,
+ B200 = B200,
+ B300 = B300,
+ B600 = B600,
+ B1200 = B1200,
+ B1800 = B1800,
+ B2400 = B2400,
+ B4800 = B4800,
+ B9600 = B9600,
+ B19200 = B19200,
+ B38400 = B38400,
+ }
+
+ termios :: struct {
+ c_iflag: CInput_Flags, /* [XBD] input flags */
+ c_ispeed: CSpeed, /* input speed */
+ c_oflag: COutput_Flags, /* [XBD] output flags */
+ c_ospeed: CSpeed, /* output speed */
+ c_cflag: CControl_Flags, /* [XBD] control flags */
+ c_ispeed_high: tcflag_t, /* high word of input baudrate */
+ c_lflag: CLocal_Flags, /* [XBD] local flag */
+ c_ospeed_high: tcflag_t, /* high word of output baudrate */
+ c_line: c.char,
+ _padding: c.uchar,
+ _padding2: c.uchar,
+ c_cc: [NCCS]cc_t,
+ }
+
+ NCCS :: 11
+
+ VINTR :: 0
+ VQUIT :: 1
+ VERASE :: 2
+ VKILL :: 3
+ VEOF :: 4
+ VEOL :: 5
+ VMIN :: 4
+ VTIME :: 5
+ VEOL2 :: 6
+ VSWTCH :: 7
+ VSTART :: 8
+ VSTOP :: 9
+ VSUSP :: 10
+
+ IGNBRK :: 0x01 /* ignore break condition */
+ BRKINT :: 0x02 /* break sends interrupt */
+ IGNPAR :: 0x04 /* ignore characters with parity errors */
+ PARMRK :: 0x08 /* mark parity errors */
+ INPCK :: 0x10 /* enable input parity checking */
+ ISTRIP :: 0x20 /* strip high bit from characters */
+ INLCR :: 0x40 /* maps newline to CR on input */
+ IGNCR :: 0x80 /* ignore carriage returns */
+ ICRNL :: 0x100 /* map CR to newline on input */
+ IXON :: 0x400 /* enable input SW flow control */
+ IXANY :: 0x800 /* any character will restart input */
+ IXOFF :: 0x1000 /* enable output SW flow control */
+
+ OPOST :: 0x01 /* enable postprocessing of output */
+ ONLCR :: 0x04 /* map NL to CR-NL on output */
+ OCRNL :: 0x08 /* map CR to NL on output */
+ ONOCR :: 0x10 /* no CR output when at column 0 */
+ ONLRET :: 0x20 /* newline performs CR function */
+ OFILL :: 0x40 /* use fill characters for delays */
+ OFDEL :: 0x80 /* Fills are DEL, otherwise NUL */
+ _NLDLY :: 0x100 /* Newline delays: */
+ NL0 :: 0x000
+ NL1 :: 0x100
+ _CRDLY :: 0x600 /* Carriage return delays: */
+ CR0 :: 0x000
+ CR1 :: 0x200
+ CR2 :: 0x400
+ CR3 :: 0x600
+ _TABDLY :: 0x1800 /* Tab delays: */
+ TAB0 :: 0x0000
+ TAB1 :: 0x0800
+ TAB3 :: 0x1800
+ _BSDLY :: 0x2000 /* Backspace delays: */
+ BS0 :: 0x0000
+ BS1 :: 0x2000
+ _VTDLY :: 0x4000 /* Vertical tab delays: */
+ VT0 :: 0x0000
+ VT1 :: 0x4000
+ _FFDLY :: 0x8000 /* Form feed delays: */
+ FF0 :: 0x0000
+ FF1 :: 0x8000
+
+ B0 :: 0x00 /* hang up */
+ B50 :: 0x01 /* 50 baud */
+ B75 :: 0x02
+ B110 :: 0x03
+ B134 :: 0x04
+ B150 :: 0x05
+ B200 :: 0x06
+ B300 :: 0x07
+ B600 :: 0x08
+ B1200 :: 0x09
+ B1800 :: 0x0A
+ B2400 :: 0x0B
+ B4800 :: 0x0C
+ B9600 :: 0x0D
+ B19200 :: 0x0E
+ B38400 :: 0x0F
+
+ _CSIZE :: 0x20 /* character size */
+ //CS5 :: 0x00 /* only 7 and 8 bits supported */
+ //CS6 :: 0x00 /* Note, it was not very wise to set all of these */
+ //CS7 :: 0x00 /* to zero, but there is not much we can do about it*/
+ CS8 :: 0x20
+ CSTOPB :: 0x40 /* send 2 stop bits, not 1 */
+ CREAD :: 0x80 /* enable receiver */
+ PARENB :: 0x100 /* parity enable */
+ PARODD :: 0x200 /* odd parity, else even */
+ HUPCL :: 0x400 /* hangs up on last close */
+ CLOCAL :: 0x800 /* indicates local line */
+
+ ISIG :: 0x01 /* enable signals */
+ ICANON :: 0x02 /* Canonical input */
+ ECHO :: 0x08 /* Enable echo */
+ ECHOE :: 0x10 /* Echo erase as bs-sp-bs */
+ ECHOK :: 0x20 /* Echo nl after kill */
+ ECHONL :: 0x40 /* Echo nl */
+ NOFLSH :: 0x80 /* Disable flush after int or quit */
+ TOSTOP :: 0x100 /* stop bg processes that write to tty */
+ IEXTEN :: 0x200 /* implementation defined extensions */
+
+ TCIFLUSH :: 1
+ TCOFLUSH :: 2
+ TCIOFLUSH :: 3
+
+ TCIOFF :: 0x04
+ TCION :: 0x08
+ TCOOFF :: 0x01
+ TCOON :: 0x02
+
}
diff --git a/core/sys/posix/time.odin b/core/sys/posix/time.odin
index f9c51c63c..88f0153f4 100644
--- a/core/sys/posix/time.odin
+++ b/core/sys/posix/time.odin
@@ -1,4 +1,4 @@
-#+build linux, darwin, netbsd, openbsd, freebsd
+#+build linux, darwin, netbsd, openbsd, freebsd, haiku
package posix
import "core:c"
@@ -230,6 +230,17 @@ when ODIN_OS == .Darwin {
getdate_err: Errno = .ENOSYS // NOTE: looks like it's not a thing on OpenBSD.
+} else when ODIN_OS == .Haiku {
+
+ clockid_t :: distinct c.int32_t
+
+ CLOCK_MONOTONIC :: 0
+ CLOCK_PROCESS_CPUTIME_ID :: -2
+ CLOCK_REALTIME :: -1
+ CLOCK_THREAD_CPUTIME_ID :: -3
+
+ getdate_err: Errno = .ENOSYS // NOTE: looks like it's not a thing on Haiku.
+
} else when ODIN_OS == .Linux {
clockid_t :: distinct c.int
diff --git a/core/sys/posix/unistd.odin b/core/sys/posix/unistd.odin
index 0526b3235..b8020317c 100644
--- a/core/sys/posix/unistd.odin
+++ b/core/sys/posix/unistd.odin
@@ -1,4 +1,4 @@
-#+build linux, darwin, netbsd, openbsd, freebsd
+#+build linux, darwin, netbsd, openbsd, freebsd, haiku
package posix
import "core:c"
@@ -899,7 +899,7 @@ CS :: enum c.int {
}
PC :: enum c.int {
- _2_SYMLINK = _PC_2_SYMLINK,
+ _2_SYMLINKS = _PC_2_SYMLINKS,
_ALLOC_SIZE_MIN = _PC_ALLOC_SIZE_MIN,
_ASYNC_IO = _PC_ASYNC_IO,
_CHOWN_RESTRICTED = _PC_CHOWN_RESTRICTED,
@@ -1099,7 +1099,7 @@ when ODIN_OS == .Darwin {
_PC_CHOWN_RESTRICTED :: 7
_PC_NO_TRUNC :: 8
_PC_VDISABLE :: 9
- _PC_2_SYMLINK :: 15
+ _PC_2_SYMLINKS :: 15
_PC_ALLOC_SIZE_MIN :: 16
_PC_ASYNC_IO :: 17
_PC_FILESIZEBITS :: 18
@@ -1280,7 +1280,7 @@ when ODIN_OS == .Darwin {
_PC_CHOWN_RESTRICTED :: 7
_PC_NO_TRUNC :: 8
_PC_VDISABLE :: 9
- _PC_2_SYMLINK :: 13 // NOTE: not in headers (freebsd)
+ _PC_2_SYMLINKS :: 13 // NOTE: not in headers (freebsd)
_PC_ALLOC_SIZE_MIN :: 10
_PC_ASYNC_IO :: 53
_PC_FILESIZEBITS :: 12
@@ -1461,7 +1461,7 @@ when ODIN_OS == .Darwin {
_PC_CHOWN_RESTRICTED :: 7
_PC_NO_TRUNC :: 8
_PC_VDISABLE :: 9
- _PC_2_SYMLINK :: 13 // NOTE: not in headers
+ _PC_2_SYMLINKS :: 13 // NOTE: not in headers
_PC_ALLOC_SIZE_MIN :: 10 // NOTE: not in headers
_PC_ASYNC_IO :: 53 // NOTE: not in headers
_PC_FILESIZEBITS :: 11
@@ -1646,7 +1646,7 @@ when ODIN_OS == .Darwin {
_PC_CHOWN_RESTRICTED :: 7
_PC_NO_TRUNC :: 8
_PC_VDISABLE :: 9
- _PC_2_SYMLINK :: 10
+ _PC_2_SYMLINKS :: 10
_PC_ALLOC_SIZE_MIN :: 11
_PC_ASYNC_IO :: 12
_PC_FILESIZEBITS :: 13
@@ -1816,180 +1816,390 @@ when ODIN_OS == .Darwin {
F_TLOCK :: 2
F_ULOCK :: 0
- _CS_PATH :: 1
- _CS_POSIX_V6_WIDTH_RESTRICTED_ENVS :: 2
-
- _CS_POSIX_V6_ILP32_OFF32_CFLAGS :: 1116
- _CS_POSIX_V6_ILP32_OFF32_LDFLAGS :: 1117
- _CS_POSIX_V6_ILP32_OFF32_LIBS :: 1118
- _CS_POSIX_V6_ILP32_OFFBIG_CFLAGS :: 1120
- _CS_POSIX_V6_ILP32_OFFBIG_LDFLAGS :: 1121
- _CS_POSIX_V6_ILP32_OFFBIG_LIBS :: 1122
- _CS_POSIX_V6_LP64_OFF64_CFLAGS :: 1124
- _CS_POSIX_V6_LP64_OFF64_LDFLAGS :: 1125
- _CS_POSIX_V6_LP64_OFF64_LIBS :: 1126
- _CS_POSIX_V6_LPBIG_OFFBIG_CFLAGS :: 1128
- _CS_POSIX_V6_LPBIG_OFFBIG_LDFLAGS :: 1129
- _CS_POSIX_V6_LPBIG_OFFBIG_LIBS :: 1130
-
- _PC_LINK_MAX :: 1
- _PC_MAX_CANON :: 2
- _PC_MAX_INPUT :: 3
- _PC_NAME_MAX :: 4
- _PC_PATH_MAX :: 5
- _PC_PIPE_BUF :: 6
- _PC_CHOWN_RESTRICTED :: 7
- _PC_NO_TRUNC :: 8
- _PC_VDISABLE :: 9
- _PC_SYNC_IO :: 10
- _PC_ASYNC_IO :: 11
- _PC_PRIO_IO :: 12
- _PC_FILESIZEBITS :: 14
- _PC_REC_INCR_XFER_SIZE :: 15
- _PC_REC_MAX_XFER_SIZE :: 16
- _PC_REC_MIN_XFER_SIZE :: 17
- _PC_REC_XFER_ALIGN :: 18
- _PC_ALLOC_SIZE_MIN :: 19
- _PC_SYMLINK_MAX :: 20
- _PC_2_SYMLINK :: 21
-
- _SC_ARG_MAX :: 1
- _SC_CHILD_MAX :: 2
- _SC_CLK_TCK :: 3
- _SC_NGROUPS_MAX :: 4
- _SC_OPEN_MAX :: 5
- _SC_STREAM_MAX :: 6
- _SC_TZNAME_MAX :: 7
- _SC_JOB_CONTROL :: 8
- _SC_SAVED_IDS :: 9
- _SC_REALTIME_SIGNALS :: 10
- _SC_PRIORITY_SCHEDULING :: 11
- _SC_TIMERS :: 12
- _SC_ASYNCHRONOUS_IO :: 13
- _SC_PRIORITIZED_IO :: 14
- _SC_SYNCHRONIZED_IO :: 15
- _SC_FSYNC :: 16
- _SC_MAPPED_FILES :: 17
- _SC_MEMLOCK :: 18
- _SC_MEMLOCK_RANGE :: 19
- _SC_MEMORY_PROTECTION :: 20
- _SC_MESSAGE_PASSING :: 21
- _SC_SEMAPHORES :: 22
- _SC_SHARED_MEMORY_OBJECTS :: 23
- _SC_AIO_LISTIO_MAX :: 24
- _SC_AIO_MAX :: 25
- _SC_AIO_PRIO_DELTA_MAX :: 26
- _SC_DELAYTIMER_MAX :: 27
- _SC_MQ_OPEN_MAX :: 28
- _SC_MQ_PRIO_MAX :: 29
- _SC_VERSION :: 30
- _SC_PAGESIZE :: 31
- _SC_PAGE_SIZE :: _SC_PAGESIZE
- _SC_RTSIG_MAX :: 32
- _SC_SEM_NSEMS_MAX :: 33
- _SC_SEM_VALUE_MAX :: 34
- _SC_SIGQUEUE_MAX :: 35
- _SC_TIMER_MAX :: 36
- _SC_BC_BASE_MAX :: 37
- _SC_BC_DIM_MAX :: 38
- _SC_BC_SCALE_MAX :: 39
- _SC_BC_STRING_MAX :: 40
- _SC_COLL_WEIGHTS_MAX :: 41
- _SC_EXPR_NEST_MAX :: 43
- _SC_LINE_MAX :: 44
- _SC_RE_DUP_MAX :: 45
- _SC_2_VERSION :: 47
- _SC_2_C_BIND :: 48
- _SC_2_C_DEV :: 49
- _SC_2_FORT_DEV :: 50
- _SC_2_FORT_RUN :: 51
- _SC_2_SW_DEV :: 52
- _SC_2_LOCALEDEF :: 53
-
- _SC_IOV_MAX :: 62
- _SC_THREADS :: 69
- _SC_THREAD_SAFE_FUNCTIONS :: 70
- _SC_GETGR_R_SIZE_MAX :: 71
- _SC_GETPW_R_SIZE_MAX :: 72
- _SC_LOGIN_NAME_MAX :: 73
- _SC_TTY_NAME_MAX :: 74
- _SC_THREAD_DESTRUCTOR_ITERATIONS :: 75
- _SC_THREAD_KEYS_MAX :: 76
- _SC_THREAD_STACK_MIN :: 77
- _SC_THREAD_THREADS_MAX :: 78
- _SC_THREAD_ATTR_STACKADDR :: 79
- _SC_THREAD_ATTR_STACKSIZE :: 80
- _SC_THREAD_PRIORITY_SCHEDULING :: 81
- _SC_THREAD_PRIO_INHERIT :: 82
- _SC_THREAD_PRIO_PROTECT :: 83
- _SC_THREAD_PROCESS_SHARED :: 84
- _SC_NPROCESSORS_CONF :: 85
- _SC_NPROCESSORS_ONLN :: 86
- _SC_PHYS_PAGES :: 87
- _SC_AVPHYS_PAGES :: 88
- _SC_ATEXIT_MAX :: 89
- _SC_PASS_MAX :: 90
- _SC_XOPEN_VERSION :: 91
- _SC_XOPEN_UNIX :: 92
- _SC_XOPEN_CRYPT :: 93
- _SC_XOPEN_ENH_I18N :: 94
- _SC_XOPEN_SHM :: 95
- _SC_2_CHAR_TERM :: 96
+ _CS_PATH :: 0
+ _CS_POSIX_V6_WIDTH_RESTRICTED_ENVS :: 1
+ _CS_GNU_LIBC_VERSION :: 2
+ _CS_GNU_LIBPTHREAD_VERSION :: 3
+ _CS_POSIX_V5_WIDTH_RESTRICTED_ENVS :: 4
+ _CS_POSIX_V7_WIDTH_RESTRICTED_ENVS :: 5
+
+ _CS_POSIX_V6_ILP32_OFF32_CFLAGS :: 1116
+ _CS_POSIX_V6_ILP32_OFF32_LDFLAGS :: 1117
+ _CS_POSIX_V6_ILP32_OFF32_LIBS :: 1118
+ _CS_POSIX_V6_ILP32_OFF32_LINTFLAGS :: 1119
+ _CS_POSIX_V6_ILP32_OFFBIG_CFLAGS :: 1120
+ _CS_POSIX_V6_ILP32_OFFBIG_LDFLAGS :: 1121
+ _CS_POSIX_V6_ILP32_OFFBIG_LIBS :: 1122
+ _CS_POSIX_V6_ILP32_OFFBIG_LINTFLAGS :: 1123
+ _CS_POSIX_V6_LP64_OFF64_CFLAGS :: 1124
+ _CS_POSIX_V6_LP64_OFF64_LDFLAGS :: 1125
+ _CS_POSIX_V6_LP64_OFF64_LIBS :: 1126
+ _CS_POSIX_V6_LP64_OFF64_LINTFLAGS :: 1127
+ _CS_POSIX_V6_LPBIG_OFFBIG_CFLAGS :: 1128
+ _CS_POSIX_V6_LPBIG_OFFBIG_LDFLAGS :: 1129
+ _CS_POSIX_V6_LPBIG_OFFBIG_LIBS :: 1130
+ _CS_POSIX_V6_LPBIG_OFFBIG_LINTFLAGS :: 1131
+ _CS_POSIX_V7_ILP32_OFF32_CFLAGS :: 1132
+ _CS_POSIX_V7_ILP32_OFF32_LDFLAGS :: 1133
+ _CS_POSIX_V7_ILP32_OFF32_LIBS :: 1134
+ _CS_POSIX_V7_ILP32_OFF32_LINTFLAGS :: 1135
+ _CS_POSIX_V7_ILP32_OFFBIG_CFLAGS :: 1136
+ _CS_POSIX_V7_ILP32_OFFBIG_LDFLAGS :: 1137
+ _CS_POSIX_V7_ILP32_OFFBIG_LIBS :: 1138
+ _CS_POSIX_V7_ILP32_OFFBIG_LINTFLAGS :: 1139
+ _CS_POSIX_V7_LP64_OFF64_CFLAGS :: 1140
+ _CS_POSIX_V7_LP64_OFF64_LDFLAGS :: 1141
+ _CS_POSIX_V7_LP64_OFF64_LIBS :: 1142
+ _CS_POSIX_V7_LP64_OFF64_LINTFLAGS :: 1143
+ _CS_POSIX_V7_LPBIG_OFFBIG_CFLAGS :: 1144
+ _CS_POSIX_V7_LPBIG_OFFBIG_LDFLAGS :: 1145
+ _CS_POSIX_V7_LPBIG_OFFBIG_LIBS :: 1146
+ _CS_POSIX_V7_LPBIG_OFFBIG_LINTFLAGS :: 1147
+ _CS_V6_ENV :: 1148
+ _CS_V7_ENV :: 1149
+ _CS_POSIX_V7_THREADS_CFLAGS :: 1150
+ _CS_POSIX_V7_THREADS_LDFLAGS :: 1151
+
+ _PC_LINK_MAX :: 0
+ _PC_MAX_CANON :: 1
+ _PC_MAX_INPUT :: 2
+ _PC_NAME_MAX :: 3
+ _PC_PATH_MAX :: 4
+ _PC_PIPE_BUF :: 5
+ _PC_CHOWN_RESTRICTED :: 6
+ _PC_NO_TRUNC :: 7
+ _PC_VDISABLE :: 8
+ _PC_SYNC_IO :: 9
+ _PC_ASYNC_IO :: 10
+ _PC_PRIO_IO :: 11
+ _PC_SOCK_MAXBUF :: 12
+ _PC_FILESIZEBITS :: 13
+ _PC_REC_INCR_XFER_SIZE :: 14
+ _PC_REC_MAX_XFER_SIZE :: 15
+ _PC_REC_MIN_XFER_SIZE :: 16
+ _PC_REC_XFER_ALIGN :: 17
+ _PC_ALLOC_SIZE_MIN :: 18
+ _PC_SYMLINK_MAX :: 19
+ _PC_2_SYMLINKS :: 20
+
+ _SC_ARG_MAX :: 0
+ _SC_CHILD_MAX :: 1
+ _SC_CLK_TCK :: 2
+ _SC_NGROUPS_MAX :: 3
+ _SC_OPEN_MAX :: 4
+ _SC_STREAM_MAX :: 5
+ _SC_TZNAME_MAX :: 6
+ _SC_JOB_CONTROL :: 7
+ _SC_SAVED_IDS :: 8
+ _SC_REALTIME_SIGNALS :: 9
+ _SC_PRIORITY_SCHEDULING :: 10
+ _SC_TIMERS :: 11
+ _SC_ASYNCHRONOUS_IO :: 12
+ _SC_PRIORITIZED_IO :: 13
+ _SC_SYNCHRONIZED_IO :: 14
+ _SC_FSYNC :: 15
+ _SC_MAPPED_FILES :: 16
+ _SC_MEMLOCK :: 17
+ _SC_MEMLOCK_RANGE :: 18
+ _SC_MEMORY_PROTECTION :: 19
+ _SC_MESSAGE_PASSING :: 20
+ _SC_SEMAPHORES :: 21
+ _SC_SHARED_MEMORY_OBJECTS :: 22
+ _SC_AIO_LISTIO_MAX :: 23
+ _SC_AIO_MAX :: 24
+ _SC_AIO_PRIO_DELTA_MAX :: 25
+ _SC_DELAYTIMER_MAX :: 26
+ _SC_MQ_OPEN_MAX :: 27
+ _SC_MQ_PRIO_MAX :: 28
+ _SC_VERSION :: 29
+ _SC_PAGE_SIZE :: 30
+ _SC_PAGESIZE :: _SC_PAGE_SIZE
+ _SC_RTSIG_MAX :: 31
+ _SC_SEM_NSEMS_MAX :: 32
+ _SC_SEM_VALUE_MAX :: 33
+ _SC_SIGQUEUE_MAX :: 34
+ _SC_TIMER_MAX :: 35
+ _SC_BC_BASE_MAX :: 36
+ _SC_BC_DIM_MAX :: 37
+ _SC_BC_SCALE_MAX :: 38
+ _SC_BC_STRING_MAX :: 39
+ _SC_COLL_WEIGHTS_MAX :: 40
+ _SC_EXPR_NEST_MAX :: 42
+ _SC_LINE_MAX :: 43
+ _SC_RE_DUP_MAX :: 44
+ _SC_2_VERSION :: 46
+ _SC_2_C_BIND :: 47
+ _SC_2_C_DEV :: 48
+ _SC_2_FORT_DEV :: 49
+ _SC_2_FORT_RUN :: 50
+ _SC_2_SW_DEV :: 51
+ _SC_2_LOCALEDEF :: 52
+ _SC_UIO_MAXIOV :: 60
+ _SC_IOV_MAX :: _SC_UIO_MAXIOV
+ _SC_THREADS :: 67
+ _SC_THREAD_SAFE_FUNCTIONS :: 68
+ _SC_GETGR_R_SIZE_MAX :: 69
+ _SC_GETPW_R_SIZE_MAX :: 70
+ _SC_LOGIN_NAME_MAX :: 71
+ _SC_TTY_NAME_MAX :: 72
+ _SC_THREAD_DESTRUCTOR_ITERATIONS :: 73
+ _SC_THREAD_KEYS_MAX :: 74
+ _SC_THREAD_STACK_MIN :: 75
+ _SC_THREAD_THREADS_MAX :: 76
+ _SC_THREAD_ATTR_STACKADDR :: 77
+ _SC_THREAD_ATTR_STACKSIZE :: 78
+ _SC_THREAD_PRIORITY_SCHEDULING :: 79
+ _SC_THREAD_PRIO_INHERIT :: 80
+ _SC_THREAD_PRIO_PROTECT :: 81
+ _SC_THREAD_PROCESS_SHARED :: 82
+ _SC_NPROCESSORS_CONF :: 83
+ _SC_NPROCESSORS_ONLN :: 84
+ _SC_PHYS_PAGES :: 85
+ _SC_AVPHYS_PAGES :: 86
+ _SC_ATEXIT_MAX :: 87
+ _SC_PASS_MAX :: 88
+ _SC_XOPEN_VERSION :: 89
+ _SC_XOPEN_XCU_VERSION :: 90
+ _SC_XOPEN_UNIX :: 91
+ _SC_XOPEN_CRYPT :: 92
+ _SC_XOPEN_ENH_I18N :: 93
+ _SC_XOPEN_SHM :: 94
+ _SC_2_CHAR_TERM :: 95
_SC_2_UPE :: 97
-
- _SC_XOPEN_LEGACY :: 129
- _SC_XOPEN_REALTIME :: 130
- _SC_XOPEN_REALTIME_THREADS :: 131
- _SC_ADVISORY_INFO :: 132
- _SC_BARRIERS :: 133
- _SC_CLOCK_SELECTION :: 137
- _SC_CPUTIME :: 138
- _SC_THREAD_CPUTIME :: 139
- _SC_MONOTONIC_CLOCK :: 149
- _SC_READER_WRITER_LOCKS :: 153
- _SC_SPIN_LOCKS :: 154
- _SC_REGEXP :: 155
- _SC_SHELL :: 157
- _SC_SPAWN :: 159
- _SC_SPORADIC_SERVER :: 160
- _SC_THREAD_SPORADIC_SERVER :: 161
- _SC_TIMEOUTS :: 164
- _SC_TYPED_MEMORY_OBJECTS :: 165
- _SC_2_PBS :: 168
- _SC_2_PBS_ACCOUNTING :: 169
- _SC_2_PBS_LOCATE :: 170
- _SC_2_PBS_MESSAGE :: 171
- _SC_2_PBS_TRACK :: 172
- _SC_SYMLOOP_MAX :: 173
- _SC_2_PBS_CHECKPOINT :: 174
- _SC_V6_ILP32_OFF32 :: 175
- _SC_V6_ILP32_OFFBIG :: 176
- _SC_V6_LP64_OFF64 :: 177
- _SC_V6_LPBIG_OFFBIG :: 178
- _SC_HOST_NAME_MAX :: 179
- _SC_TRACE :: 180
- _SC_TRACE_EVENT_FILTER :: 181
- _SC_TRACE_INHERIT :: 182
- _SC_TRACE_LOG :: 183
-
- _SC_IPV6 :: 234
- _SC_RAW_SOCKETS :: 235
- _SC_V7_ILP32_OFF32 :: 236
- _SC_V7_ILP32_OFFBIG :: 237
- _SC_V7_LP64_OFF64 :: 238
- _SC_V7_LPBIG_OFFBIG :: 239
- _SC_SS_REPL_MAX :: 240
- _SC_TRACE_EVENT_NAME_MAX :: 241
- _SC_TRACE_NAME_MAX :: 242
- _SC_TRACE_SYS_MAX :: 243
- _SC_TRACE_USER_EVENT_MAX :: 244
- _SC_XOPEN_STREAMS :: 245
- _SC_THREAD_ROBUST_PRIO_INHERIT :: 246
- _SC_THREAD_ROBUST_PRIO_PROTECT :: 247
+ _SC_XOPEN_XPG2 :: 98
+ _SC_XOPEN_XPG3 :: 99
+ _SC_XOPEN_XPG4 :: 100
+ _SC_NZERO :: 109
+ _SC_XBS5_ILP32_OFF32 :: 125
+ _SC_XBS5_ILP32_OFFBIG :: 126
+ _SC_XBS5_LP64_OFF64 :: 127
+ _SC_XBS5_LPBIG_OFFBIG :: 128
+ _SC_XOPEN_LEGACY :: 129
+ _SC_XOPEN_REALTIME :: 130
+ _SC_XOPEN_REALTIME_THREADS :: 131
+ _SC_ADVISORY_INFO :: 132
+ _SC_BARRIERS :: 133
+ _SC_CLOCK_SELECTION :: 137
+ _SC_CPUTIME :: 138
+ _SC_THREAD_CPUTIME :: 139
+ _SC_MONOTONIC_CLOCK :: 149
+ _SC_READER_WRITER_LOCKS :: 153
+ _SC_SPIN_LOCKS :: 154
+ _SC_REGEXP :: 155
+ _SC_SHELL :: 157
+ _SC_SPAWN :: 159
+ _SC_SPORADIC_SERVER :: 160
+ _SC_THREAD_SPORADIC_SERVER :: 161
+ _SC_TIMEOUTS :: 164
+ _SC_TYPED_MEMORY_OBJECTS :: 165
+ _SC_2_PBS :: 168
+ _SC_2_PBS_ACCOUNTING :: 169
+ _SC_2_PBS_LOCATE :: 170
+ _SC_2_PBS_MESSAGE :: 171
+ _SC_2_PBS_TRACK :: 172
+ _SC_SYMLOOP_MAX :: 173
+ _SC_STREAMS :: 174
+ _SC_2_PBS_CHECKPOINT :: 175
+ _SC_V6_ILP32_OFF32 :: 176
+ _SC_V6_ILP32_OFFBIG :: 177
+ _SC_V6_LP64_OFF64 :: 178
+ _SC_V6_LPBIG_OFFBIG :: 179
+ _SC_HOST_NAME_MAX :: 180
+ _SC_TRACE :: 181
+ _SC_TRACE_EVENT_FILTER :: 182
+ _SC_TRACE_INHERIT :: 183
+ _SC_TRACE_LOG :: 184
+
+ _SC_IPV6 :: 235
+ _SC_RAW_SOCKETS :: 236
+ _SC_V7_ILP32_OFF32 :: 237
+ _SC_V7_ILP32_OFFBIG :: 238
+ _SC_V7_LP64_OFF64 :: 239
+ _SC_V7_LPBIG_OFFBIG :: 240
+ _SC_SS_REPL_MAX :: 241
+ _SC_TRACE_EVENT_NAME_MAX :: 242
+ _SC_TRACE_NAME_MAX :: 243
+ _SC_TRACE_SYS_MAX :: 244
+ _SC_TRACE_USER_EVENT_MAX :: 245
+ _SC_XOPEN_STREAMS :: 246
+ _SC_THREAD_ROBUST_PRIO_INHERIT :: 247
+ _SC_THREAD_ROBUST_PRIO_PROTECT :: 248
+ _SC_MINSIGSTKSZ :: 249
+ _SC_SIGSTKSZ :: 250
// NOTE: Not implemented.
_SC_XOPEN_UUCP :: 0
// NOTE: Not implemented.
_POSIX_VDISABLE :: 0
+} else when ODIN_OS == .Haiku {
+
+ _F_OK :: 0
+ X_OK :: 1
+ W_OK :: 2
+ R_OK :: 4
+
+ F_LOCK :: 1
+ F_TEST :: 3
+ F_TLOCK :: 2
+ F_ULOCK :: 0
+
+ _CS_PATH :: 1
+ _CS_POSIX_V6_WIDTH_RESTRICTED_ENVS :: 0 // Undefined.
+ _CS_POSIX_V6_ILP32_OFF32_CFLAGS :: 0 // Undefined.
+ _CS_POSIX_V6_ILP32_OFF32_LDFLAGS :: 0 // Undefined.
+ _CS_POSIX_V6_ILP32_OFF32_LIBS :: 0 // Undefined.
+ _CS_POSIX_V6_ILP32_OFFBIG_CFLAGS :: 0 // Undefined.
+ _CS_POSIX_V6_ILP32_OFFBIG_LDFLAGS :: 0 // Undefined.
+ _CS_POSIX_V6_ILP32_OFFBIG_LIBS :: 0 // Undefined.
+ _CS_POSIX_V6_LP64_OFF64_CFLAGS :: 0 // Undefined.
+ _CS_POSIX_V6_LP64_OFF64_LDFLAGS :: 0 // Undefined.
+ _CS_POSIX_V6_LP64_OFF64_LIBS :: 0 // Undefined.
+ _CS_POSIX_V6_LPBIG_OFFBIG_CFLAGS :: 0 // Undefined.
+ _CS_POSIX_V6_LPBIG_OFFBIG_LDFLAGS :: 0 // Undefined.
+ _CS_POSIX_V6_LPBIG_OFFBIG_LIBS :: 0 // Undefined.
+
+ _SC_ASYNCHRONOUS_IO :: 0 // Undefined.
+ _SC_RAW_SOCKETS :: 0 // Undefined.
+ _SC_SS_REPL_MAX :: 0 // Undefined.
+ _SC_TRACE_EVENT_NAME_MAX :: 0 // Undefined.
+ _SC_TRACE_NAME_MAX :: 0 // Undefined.
+ _SC_TRACE_SYS_MAX :: 0 // Undefined.
+ _SC_TRACE_USER_EVENT_MAX :: 0 // Undefined.
+
+ _PC_CHOWN_RESTRICTED :: 1
+ _PC_MAX_CANON :: 2
+ _PC_MAX_INPUT :: 3
+ _PC_NAME_MAX :: 4
+ _PC_NO_TRUNC :: 5
+ _PC_PATH_MAX :: 6
+ _PC_PIPE_BUF :: 7
+ _PC_VDISABLE :: 8
+ _PC_LINK_MAX :: 25
+ _PC_SYNC_IO :: 26
+ _PC_ASYNC_IO :: 27
+ _PC_PRIO_IO :: 28
+ _PC_FILESIZEBITS :: 30
+ _PC_REC_INCR_XFER_SIZE :: 31
+ _PC_REC_MAX_XFER_SIZE :: 32
+ _PC_REC_MIN_XFER_SIZE :: 33
+ _PC_REC_XFER_ALIGN :: 34
+ _PC_ALLOC_SIZE_MIN :: 35
+ _PC_SYMLINK_MAX :: 36
+ _PC_2_SYMLINKS :: 37
+
+ _SC_ARG_MAX :: 15
+ _SC_CHILD_MAX :: 16
+ _SC_CLK_TCK :: 17
+ _SC_JOB_CONTROL :: 18
+ _SC_NGROUPS_MAX :: 19
+ _SC_OPEN_MAX :: 20
+ _SC_SAVED_IDS :: 21
+ _SC_STREAM_MAX :: 22
+ _SC_TZNAME_MAX :: 23
+ _SC_VERSION :: 24
+ _SC_GETGR_R_SIZE_MAX :: 25
+ _SC_GETPW_R_SIZE_MAX :: 26
+ _SC_PAGE_SIZE :: 27
+ _SC_PAGESIZE :: _SC_PAGE_SIZE
+ _SC_SEM_NSEMS_MAX :: 28
+ _SC_SEM_VALUE_MAX :: 29
+ _SC_SEMAPHORES :: 30
+ _SC_THREADS :: 31
+ _SC_IOV_MAX :: 32
+ _SC_NPROCESSORS_CONF :: 34
+ _SC_NPROCESSORS_ONLN :: 35
+ _SC_ATEXIT_MAX :: 37
+ _SC_MAPPED_FILES :: 45
+ _SC_THREAD_PROCESS_SHARED :: 46
+ _SC_THREAD_STACK_MIN :: 47
+ _SC_THREAD_ATTR_STACKADDR :: 48
+ _SC_THREAD_ATTR_STACKSIZE :: 49
+ _SC_THREAD_PRIORITY_SCHEDULING :: 50
+ _SC_REALTIME_SIGNALS :: 51
+ _SC_MEMORY_PROTECTION :: 52
+ _SC_SIGQUEUE_MAX :: 53
+ _SC_RTSIG_MAX :: 54
+ _SC_MONOTONIC_CLOCK :: 55
+ _SC_DELAYTIMER_MAX :: 56
+ _SC_TIMER_MAX :: 57
+ _SC_TIMERS :: 58
+ _SC_CPUTIME :: 59
+ _SC_THREAD_CPUTIME :: 60
+ _SC_HOST_NAME_MAX :: 61
+ _SC_REGEXP :: 62
+ _SC_SYMLOOP_MAX :: 63
+ _SC_SHELL :: 64
+ _SC_TTY_NAME_MAX :: 65
+ _SC_ADVISORY_INFO :: 66
+ _SC_BARRIERS :: 67
+ _SC_CLOCK_SELECTION :: 68
+ _SC_FSYNC :: 69
+ _SC_IPV6 :: 70
+ _SC_MEMLOCK :: 71
+ _SC_MEMLOCK_RANGE :: 72
+ _SC_MESSAGE_PASSING :: 73
+ _SC_PRIORITIZED_IO :: 74
+ _SC_PRIORITY_SCHEDULING :: 75
+ _SC_READER_WRITER_LOCKS :: 76
+ _SC_SHARED_MEMORY_OBJECTS :: 77
+ _SC_SPAWN :: 78
+ _SC_SPIN_LOCKS :: 79
+ _SC_SPORADIC_SERVER :: 80
+ _SC_SYNCHRONIZED_IO :: 81
+ _SC_THREAD_PRIO_INHERIT :: 82
+ _SC_THREAD_PRIO_PROTECT :: 83
+ _SC_THREAD_SAFE_FUNCTIONS :: 86
+ _SC_THREAD_SPORADIC_SERVER :: 87
+ _SC_TIMEOUTS :: 88
+ _SC_TRACE :: 89
+ _SC_TRACE_EVENT_FILTER :: 90
+ _SC_TRACE_INHERIT :: 91
+ _SC_TRACE_LOG :: 92
+ _SC_TYPED_MEMORY_OBJECTS :: 93
+ _SC_V6_ILP32_OFF32 :: 94
+ _SC_V6_ILP32_OFFBIG :: 95
+ _SC_V6_LP64_OFF64 :: 96
+ _SC_V6_LPBIG_OFFBIG :: 97
+ _SC_2_C_BIND :: 102
+ _SC_2_C_DEV :: 103
+ _SC_2_CHAR_TERM :: 104
+ _SC_2_FORT_DEV :: 105
+ _SC_2_FORT_RUN :: 106
+ _SC_2_LOCALEDEF :: 107
+ _SC_2_PBS :: 108
+ _SC_2_PBS_ACCOUNTING :: 109
+ _SC_2_PBS_CHECKPOINT :: 110
+ _SC_2_PBS_LOCATE :: 111
+ _SC_2_PBS_MESSAGE :: 112
+ _SC_2_PBS_TRACK :: 113
+ _SC_2_SW_DEV :: 114
+ _SC_2_UPE :: 115
+ _SC_2_VERSION :: 116
+ _SC_XOPEN_CRYPT :: 117
+ _SC_XOPEN_ENH_I18N :: 118
+ _SC_XOPEN_REALTIME :: 119
+ _SC_XOPEN_REALTIME_THREADS :: 120
+ _SC_XOPEN_SHM :: 121
+ _SC_XOPEN_STREAMS :: 122
+ _SC_XOPEN_UNIX :: 123
+ _SC_XOPEN_VERSION :: 125
+ _SC_AIO_LISTIO_MAX :: 126
+ _SC_AIO_MAX :: 127
+ _SC_AIO_PRIO_DELTA_MAX :: 128
+ _SC_BC_BASE_MAX :: 129
+ _SC_BC_DIM_MAX :: 130
+ _SC_BC_SCALE_MAX :: 131
+ _SC_BC_STRING_MAX :: 132
+ _SC_COLL_WEIGHTS_MAX :: 133
+ _SC_EXPR_NEST_MAX :: 134
+ _SC_LINE_MAX :: 135
+ _SC_LOGIN_NAME_MAX :: 136
+ _SC_MQ_OPEN_MAX :: 137
+ _SC_MQ_PRIO_MAX :: 138
+ _SC_THREAD_DESTRUCTOR_ITERATIONS :: 139
+ _SC_THREAD_KEYS_MAX :: 140
+ _SC_THREAD_THREADS_MAX :: 141
+ _SC_RE_DUP_MAX :: 142
+
}
diff --git a/core/sys/posix/unistd_libc.odin b/core/sys/posix/unistd_libc.odin
index bbfe3d59d..74edb6862 100644
--- a/core/sys/posix/unistd_libc.odin
+++ b/core/sys/posix/unistd_libc.odin
@@ -1,4 +1,4 @@
-#+build linux, windows, darwin, netbsd, openbsd, freebsd
+#+build linux, windows, darwin, netbsd, openbsd, freebsd, haiku
package posix
import "core:c"
diff --git a/core/sys/posix/utime.odin b/core/sys/posix/utime.odin
index e884eb1a3..98c8166d6 100644
--- a/core/sys/posix/utime.odin
+++ b/core/sys/posix/utime.odin
@@ -1,4 +1,4 @@
-#+build linux, darwin, netbsd, openbsd, freebsd
+#+build linux, darwin, netbsd, openbsd, freebsd, haiku
package posix
when ODIN_OS == .Darwin {
@@ -25,7 +25,7 @@ when ODIN_OS == .NetBSD {
@(private) LUTIME :: "utime"
}
-when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux {
+when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux || ODIN_OS == .Haiku {
utimbuf :: struct {
actime: time_t, /* [PSX] access time (seconds since epoch) */
diff --git a/core/sys/wasm/js/odin.js b/core/sys/wasm/js/odin.js
index 07a77952c..29227c526 100644
--- a/core/sys/wasm/js/odin.js
+++ b/core/sys/wasm/js/odin.js
@@ -110,7 +110,10 @@ class WasmMemoryInterface {
}
loadCstring(ptr) {
- const start = this.loadPtr(ptr);
+ return this.loadCstringDirect(this.loadPtr(ptr));
+ }
+
+ loadCstringDirect(start) {
if (start == 0) {
return null;
}
diff --git a/core/sys/windows/comctl32.odin b/core/sys/windows/comctl32.odin
index 477800413..d954f952c 100644
--- a/core/sys/windows/comctl32.odin
+++ b/core/sys/windows/comctl32.odin
@@ -5,5 +5,2093 @@ foreign import "system:Comctl32.lib"
@(default_calling_convention="system")
foreign Comctl32 {
+ InitCommonControlsEx :: proc(picce: ^INITCOMMONCONTROLSEX) -> BOOL ---
LoadIconWithScaleDown :: proc(hinst: HINSTANCE, pszName: PCWSTR, cx: c_int, cy: c_int, phico: ^HICON) -> HRESULT ---
+ SetWindowSubclass :: proc(hwnd: HWND, pfnSubclass: SUBCLASSPROC, uIdSubclass: UINT_PTR, dwRefData: DWORD_PTR) ---
+}
+
+ICC_LISTVIEW_CLASSES :: 0x00000001
+ICC_TREEVIEW_CLASSES :: 0x00000002
+ICC_BAR_CLASSES :: 0x00000004
+ICC_TAB_CLASSES :: 0x00000008
+ICC_UPDOWN_CLASS :: 0x00000010
+ICC_PROGRESS_CLASS :: 0x00000020
+ICC_HOTKEY_CLASS :: 0x00000040
+ICC_ANIMATE_CLASS :: 0x00000080
+ICC_WIN95_CLASSES :: 0x000000FF
+ICC_DATE_CLASSES :: 0x00000100
+ICC_USEREX_CLASSES :: 0x00000200
+ICC_COOL_CLASSES :: 0x00000400
+ICC_INTERNET_CLASSES :: 0x00000800
+ICC_PAGESCROLLER_CLASS :: 0x00001000
+ICC_NATIVEFNTCTL_CLASS :: 0x00002000
+ICC_STANDARD_CLASSES :: 0x00004000
+ICC_LINK_CLASS :: 0x00008000
+
+INITCOMMONCONTROLSEX :: struct {
+ dwSize: DWORD,
+ dwICC: DWORD,
+}
+
+COMCTL32_VERSION :: 6
+HINST_COMMCTRL :: cast(HINSTANCE)(~uintptr(0))
+
+// Common Control Class Names
+WC_HEADER :: "SysHeader32"
+WC_LISTVIEW :: "SysListView32"
+WC_TREEVIEW :: "SysTreeView32"
+WC_COMBOBOXEX :: "ComboBoxEx32"
+WC_TABCONTROL :: "SysTabControl32"
+WC_IPADDRESS :: "SysIPAddress32"
+WC_PAGESCROLLER :: "SysPager"
+WC_NATIVEFONTCTL :: "NativeFontCtl"
+WC_BUTTON :: "Button"
+WC_STATIC :: "Static"
+WC_EDIT :: "Edit"
+WC_LISTBOX :: "ListBox"
+WC_COMBOBOX :: "ComboBox"
+WC_SCROLLBAR :: "ScrollBar"
+WC_LINK :: "SysLink"
+
+TOOLBARCLASSNAME :: "ToolbarWindow32"
+REBARCLASSNAME :: "ReBarWindow32"
+STATUSCLASSNAME :: "msctls_statusbar32"
+
+TOOLTIPS_CLASS :: "tooltips_class32"
+TRACKBAR_CLASS :: "msctls_trackbar32"
+UPDOWN_CLASS :: "msctls_updown32"
+PROGRESS_CLASS :: "msctls_progress32"
+HOTKEY_CLASS :: "msctls_hotkey32"
+ANIMATE_CLASS :: "SysAnimate32"
+MONTHCAL_CLASS :: "SysMonthCal32"
+DATETIMEPICK_CLASS :: "SysDateTimePick32"
+
+// Common Control Constants
+MSGF_COMMCTRL_BEGINDRAG :: 0x4200
+MSGF_COMMCTRL_SIZEHEADER :: 0x4201
+MSGF_COMMCTRL_DRAGSELECT :: 0x4202
+MSGF_COMMCTRL_TOOLBARCUST :: 0x4203
+
+// Custom Draw Constants
+CDRF_DODEFAULT :: 0x00
+CDRF_NEWFONT :: 0x02
+CDRF_SKIPDEFAULT :: 0x04
+CDRF_NOTIFYPOSTPAINT :: 0x10
+CDRF_NOTIFYITEMDRAW :: 0x20
+CDRF_NOTIFYSUBITEMDRAW :: 0x20
+CDRF_NOTIFYPOSTERASE :: 0x40
+
+CDDS_PREPAINT :: 0x00001
+CDDS_POSTPAINT :: 0x00002
+CDDS_PREERASE :: 0x00003
+CDDS_POSTERASE :: 0x00004
+CDDS_ITEM :: 0x10000
+CDDS_ITEMPREPAINT :: (CDDS_ITEM | CDDS_PREPAINT)
+CDDS_ITEMPOSTPAINT :: (CDDS_ITEM | CDDS_POSTPAINT)
+CDDS_ITEMPREERASE :: (CDDS_ITEM | CDDS_PREERASE)
+CDDS_ITEMPOSTERASE :: (CDDS_ITEM | CDDS_POSTERASE)
+CDDS_SUBITEM :: 0x20000
+
+CDIS_SELECTED :: 0x001
+CDIS_GRAYED :: 0x002
+CDIS_DISABLED :: 0x004
+CDIS_CHECKED :: 0x008
+CDIS_FOCUS :: 0x010
+CDIS_DEFAULT :: 0x020
+CDIS_HOT :: 0x040
+CDIS_MARKED :: 0x080
+CDIS_INDETERMINATE :: 0x100
+CDIS_SHOWKEYBOARDCUES :: 0x200
+
+// Image Lists
+CLR_NONE :: 0xFFFFFFFF
+CLR_DEFAULT :: 0xFF000000
+
+ILC_MASK :: 0x00000001
+ILC_COLOR :: 0x00000000
+ILC_COLORDDB :: 0x000000FE
+ILC_COLOR4 :: 0x00000004
+ILC_COLOR8 :: 0x00000008
+ILC_COLOR16 :: 0x00000010
+ILC_COLOR24 :: 0x00000018
+ILC_COLOR32 :: 0x00000020
+ILC_PALETTE :: 0x00000800
+ILC_MIRROR :: 0x00002000
+ILC_PERITEMMIRROR :: 0x00008000
+ILC_ORIGINALSIZE :: 0x00010000
+ILC_HIGHQUALITYSCALE :: 0x00020000
+
+ILD_NORMAL :: 0x00000000
+ILD_TRANSPARENT :: 0x00000001
+ILD_MASK :: 0x00000010
+ILD_IMAGE :: 0x00000020
+ILD_ROP :: 0x00000040
+ILD_BLEND25 :: 0x00000002
+ILD_BLEND50 :: 0x00000004
+ILD_OVERLAYMASK :: 0x00000F00
+ILD_PRESERVEALPHA :: 0x00001000
+ILD_SCALE :: 0x00002000
+ILD_DPISCALE :: 0x00004000
+ILD_ASYNC :: 0x00008000
+
+ILD_SELECTED :: ILD_BLEND50
+ILD_FOCUS :: ILD_BLEND25
+ILD_BLEND :: ILD_BLEND50
+CLR_HILIGHT :: CLR_DEFAULT
+
+ILS_NORMAL :: 0x00000000
+ILS_GLOW :: 0x00000001
+ILS_SHADOW :: 0x00000002
+ILS_SATURATE :: 0x00000004
+ILS_ALPHA :: 0x00000008
+
+ILGT_NORMAL :: 0x00000000
+ILGT_ASYNC :: 0x00000001
+
+ILCF_MOVE :: 0x00000000
+ILCF_SWAP :: 0x00000001
+
+ILP_NORMAL :: 0
+ILP_DOWNLEVEL :: 1
+
+IMAGELISTDRAWPARAMS :: struct {
+ cbSize: DWORD,
+ himl: HIMAGELIST,
+ i: i32,
+ hdcDst: HDC,
+ x: i32,
+ y: i32,
+ cx: i32,
+ cy: i32,
+ xBitmap: i32,
+ yBitmap: i32,
+ rgbBk: COLORREF,
+ rgbFg: COLORREF,
+ fStyle: UINT,
+ dwRop: DWORD,
+ fState: DWORD,
+ Frame: DWORD,
+ crEffect: COLORREF,
+}
+LPIMAGELISTDRAWPARAMS :: ^IMAGELISTDRAWPARAMS
+
+IMAGEINFO :: struct {
+ hbmImage: HBITMAP,
+ hbmMask: HBITMAP,
+ Unused1: i32,
+ Unused2: i32,
+ rcImage: RECT,
+}
+LPIMAGEINFO :: ^IMAGEINFO
+
+@(default_calling_convention="system")
+foreign Comctl32 {
+ ImageList_Create :: proc(cx, cy: i32, flags: UINT, cInitial, cGrow: i32) -> HIMAGELIST ---
+ ImageList_Destroy :: proc(himl: HIMAGELIST) -> BOOL ---
+ ImageList_GetImageCount :: proc(himl: HIMAGELIST) -> i32 ---
+ ImageList_SetImageCount :: proc(himl: HIMAGELIST, uNewCount: UINT) -> BOOL ---
+ ImageList_Add :: proc(himl: HIMAGELIST, hbmImage, hbmMask: HBITMAP) -> i32 ---
+ ImageList_ReplaceIcon :: proc(himl: HIMAGELIST, i: i32, hicon: HICON) -> i32 ---
+ ImageList_SetBkColor :: proc(himl: HIMAGELIST, clrBk: COLORREF) -> COLORREF ---
+ ImageList_GetBkColor :: proc(himl: HIMAGELIST) -> COLORREF ---
+ ImageList_SetOverlayImage :: proc(himl: HIMAGELIST, iImage: i32, iOverlay: i32) -> BOOL ---
+ ImageList_Draw :: proc(himl: HIMAGELIST, i: i32, hdcDst: HDC, x, y: i32, fStyle: UINT) -> BOOL ---
+ ImageList_Replace :: proc(himl: HIMAGELIST, i: i32, hbmImage, hbmMask: HBITMAP) -> BOOL ---
+ ImageList_AddMasked :: proc(himl: HIMAGELIST, hbmImage: HBITMAP, crMask: COLORREF) -> i32 ---
+ ImageList_DrawEx :: proc(himl: HIMAGELIST, i: i32, hdcDst: HDC, x, y, dx, dy: i32, rgbBk, rgbFg: COLORREF, fStyle: UINT) -> BOOL ---
+ ImageList_DrawIndirect :: proc(pimldp: ^IMAGELISTDRAWPARAMS) -> BOOL ---
+ ImageList_Remove :: proc(himl: HIMAGELIST, i: i32) -> BOOL ---
+ ImageList_GetIcon :: proc(himl: HIMAGELIST, i: i32, flags: UINT) -> HICON ---
+ ImageList_LoadImageW :: proc(hi: HINSTANCE, lpbmp: LPCWSTR, cx, cgrow: i32, crMask: COLORREF, uType, uFlags: UINT) -> HIMAGELIST ---
+ ImageList_Copy :: proc(himlDst: HIMAGELIST, iDst: i32, himlSrc: HIMAGELIST, iSrc: i32, uFlags: UINT) -> BOOL ---
+ ImageList_BeginDrag :: proc(himlTrack: HIMAGELIST, iTrack, dxHotspot, dyHotspot: i32) -> BOOL ---
+ ImageList_EndDrag :: proc() ---
+ ImageList_DragEnter :: proc(hwndLock: HWND, x, y: i32) -> BOOL ---
+ ImageList_DragLeave :: proc(hwndLock: HWND) -> BOOL ---
+ ImageList_DragMove :: proc(x, y: i32) -> BOOL ---
+ ImageList_SetDragCursorImage :: proc(himlDrag: HIMAGELIST, iDrag, dxHotspot, dyHotspot: i32) -> BOOL ---
+ ImageList_DragShowNolock :: proc(fShow: BOOL) -> BOOL ---
+ ImageList_GetDragImage :: proc(ppt, pptHotspot: ^POINT) -> HIMAGELIST ---
+ ImageList_Read :: proc(pstm: ^IStream) -> HIMAGELIST ---
+ ImageList_Write :: proc(himl: HIMAGELIST, pstm: ^IStream) -> BOOL ---
+ ImageList_ReadEx :: proc(dwFlags: DWORD, pstm: ^IStream, riid: REFIID, ppv: PVOID) -> HRESULT ---
+ ImageList_WriteEx :: proc(himl: HIMAGELIST, dwFlags: DWORD, pstm: ^IStream) -> HRESULT ---
+ ImageList_GetIconSize :: proc(himl: HIMAGELIST, cx, cy: ^i32) -> BOOL ---
+ ImageList_SetIconSize :: proc(himl: HIMAGELIST, cx, cy: i32) -> BOOL ---
+ ImageList_GetImageInfo :: proc(himl: HIMAGELIST, i: i32, pImageInfo: ^IMAGEINFO) -> BOOL ---
+ ImageList_Merge :: proc(himl1: HIMAGELIST, i1: i32, himl2: HIMAGELIST, i2: i32, dx, dy: i32) -> HIMAGELIST ---
+ ImageList_Duplicate :: proc(himl: HIMAGELIST) -> HIMAGELIST ---
+ HIMAGELIST_QueryInterface :: proc(himl: HIMAGELIST, riid: REFIID, ppv: rawptr) -> HRESULT ---
+}
+
+ImageList_AddIcon :: #force_inline proc "system" (himl: HIMAGELIST, hicon: HICON) -> i32 {
+ return ImageList_ReplaceIcon(himl, -1, hicon)
+}
+ImageList_RemoveAll :: #force_inline proc "system" (himl: HIMAGELIST) -> BOOL {
+ return ImageList_Remove(himl, -1)
+}
+ImageList_ExtractIcon :: #force_inline proc "system" (hi: HINSTANCE, himl: HIMAGELIST, i: i32) -> HICON {
+ return ImageList_GetIcon(himl, i, 0)
+}
+ImageList_LoadBitmap :: #force_inline proc "system" (hi: HINSTANCE, lpbmp: LPCWSTR, cx, cGrow: i32, crMask: COLORREF) -> HIMAGELIST {
+ return ImageList_LoadImageW(hi, lpbmp, cx, cGrow, crMask, IMAGE_BITMAP, 0)
+}
+
+// Status Bar Control
+SBT_NOBORDERS :: 0x0100
+SBT_POPOUT :: 0x0200
+SBT_RTLREADING :: 0x0400
+SBT_NOTABPARSING :: 0x0800
+SBT_OWNERDRAW :: 0x1000
+
+SBN_SIMPLEMODECHANGE :: SBN_FIRST - 0
+
+SB_SIMPLEID :: 0xFF
+
+@(default_calling_convention="system")
+foreign Comctl32 {
+ DrawStatusTextW :: proc(hDC: HDC, lprc: ^RECT, pszText: LPCWSTR, uFlags: UINT) ---
+ CreateStatusWindowW :: proc(style: LONG, lpszText: LPCWSTR, hwndParent: HWND, wID: UINT) -> HWND ---
+}
+
+// Menu Help
+MINSYSCOMMAND :: SC_SIZE
+
+@(default_calling_convention="system")
+foreign Comctl32 {
+ MenuHelp :: proc(uMsg: UINT, wParam: WPARAM, lParam: LPARAM, hMainMenu: HMENU, hInst: HINSTANCE, hwndStatus: HWND, lpwIDs: ^UINT) ---
+ ShowHideMenuCtl :: proc(hWnd: HWND, uFlags: UINT_PTR, lpInfo: LPINT) -> BOOL ---
+ GetEffectiveClientRect :: proc(hWnd: HWND, lprc: LPRECT, lpInfo: ^INT) ---
+}
+
+// Drag List
+DL_CURSORSET :: 0
+DL_STOPCURSOR :: 1
+DL_COPYCURSOR :: 2
+DL_MOVECURSOR :: 3
+
+DRAGLISTMSGSTRING :: "commctrl_DragListMsg"
+
+@(default_calling_convention="system")
+foreign Comctl32 {
+ MakeDragList :: proc(hLB: HWND) -> BOOL ---
+ DrawInsert :: proc(handParent: HWND, hLB: HWND, nItem: c_int) ---
+ LBItemFromPt :: proc(hLB: HWND, pt: POINT, bAutoScroll: BOOL) -> c_int ---
+}
+
+// Header Control
+HDTEXTFILTERW :: struct {
+ pszText: LPWSTR,
+ cchTextMax: INT,
+}
+HD_TEXTFILTERW :: HDTEXTFILTERW
+LPHDTEXTFILTERW :: ^HDTEXTFILTERW
+LPHD_TEXTFILTERW :: LPHDTEXTFILTERW
+
+HDITEMW :: struct {
+ mask: UINT,
+ cxy: c_int,
+ pszText: LPWSTR,
+ hbm: HBITMAP,
+ cchTextMax: c_int,
+ fmt: c_int,
+ lParam: LPARAM,
+ iImage: c_int,
+ iOrder: c_int,
+ type: UINT,
+ pvFilter: rawptr,
+}
+HD_ITEMW :: HDITEMW
+LPHDITEMW :: ^HDITEMW
+LPHD_ITEMW :: LPHDITEMW
+
+HDLAYOUT :: struct {
+ prc: ^RECT,
+ pwpos: ^WINDOWPOS,
+}
+HD_LAYOUT :: HDLAYOUT
+LPHDLAYOUT :: ^HDLAYOUT
+LPHD_LAYOUT :: LPHDLAYOUT
+
+HDHITTESTINFO :: struct {
+ pt: POINT,
+ flags: UINT,
+ iItem: c_int,
+}
+HD_HITTESTINFO :: HDHITTESTINFO
+LPHDHITTESTINFO :: ^HDHITTESTINFO
+LPHD_HITTESTINFO :: LPHDHITTESTINFO
+
+NMHEADERW :: struct {
+ hdr: NMHDR,
+ iItem: c_int,
+ iButton: c_int,
+ pitem: ^HDITEMW,
+}
+LPNMHEADERW :: ^NMHEADERW
+HD_NOTIFYW :: NMHEADERW
+LPHD_NOTIFYW :: LPNMHEADERW
+
+NMHDDISPINFOW :: struct {
+ hdr: NMHDR,
+ iItem: c_int,
+ mask: UINT,
+ pszText: LPWSTR,
+ cchTextMax: c_int,
+ iImage: c_int,
+ lParam: LPARAM,
+}
+LPNMHDDISPINFOW :: ^NMHDDISPINFOW
+
+NMHDFILTERBTNCLICK :: struct {
+ hdr: NMHDR,
+ iItem: c_int,
+ rc: RECT,
+}
+LPNMHDFILTERBTNCLICK :: ^NMHDFILTERBTNCLICK
+
+Header_GetItemCount :: #force_inline proc "system" (hwndHD: HWND) -> c_int {
+ return cast(c_int)SendMessageW(hwndHD, HDM_GETITEMCOUNT, 0, 0)
+}
+Header_InsertItem :: #force_inline proc "system" (hwndHD: HWND, i: c_int, phdi: ^HD_ITEMW) -> c_int {
+ return cast(c_int)SendMessageW(hwndHD, HDM_INSERTITEMW, cast(WPARAM)i, cast(LPARAM)uintptr(phdi))
+}
+Header_DeleteItem :: #force_inline proc "system" (hwndHD: HWND, i: c_int) -> BOOL {
+ return cast(BOOL)SendMessageW(hwndHD, HDM_DELETEITEM, cast(WPARAM)i, 0)
+}
+Header_GetItem :: #force_inline proc "system" (hwndHD: HWND, i: c_int, phdi: ^HD_ITEMW) -> BOOL {
+ return cast(BOOL)SendMessageW(hwndHD, HDM_GETITEMW, cast(WPARAM)i, cast(LPARAM)uintptr(phdi))
+}
+Header_SetItem :: #force_inline proc "system" (hwndHD: HWND, i: c_int, phdi: ^HD_ITEMW) -> BOOL {
+ return cast(BOOL)SendMessageW(hwndHD, HDM_SETITEMW, cast(WPARAM)i, cast(LPARAM)uintptr(phdi))
+}
+Header_Layout :: #force_inline proc "system" (hwndHD: HWND, playout: ^HD_LAYOUT) -> BOOL {
+ return cast(BOOL)SendMessageW(hwndHD, HDM_LAYOUT, 0, cast(LPARAM)uintptr(playout))
+}
+
+Header_GetItemRect :: #force_inline proc "system" (hwnd: HWND, iItem: c_int, lprc: ^RECT) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd,HDM_GETITEMRECT,cast(WPARAM)iItem,cast(LPARAM)uintptr(lprc))
+}
+Header_SetImageList :: #force_inline proc "system" (hwnd: HWND, himl: HIMAGELIST) -> HIMAGELIST {
+ return cast(HIMAGELIST)uintptr(SendMessageW(hwnd,HDM_SETIMAGELIST,0,cast(LPARAM)uintptr(himl)))
+}
+Header_GetImageList :: #force_inline proc "system" (hwnd: HWND) -> HIMAGELIST {
+ return cast(HIMAGELIST)uintptr(SendMessageW(hwnd,HDM_GETIMAGELIST,0,0))
+}
+Header_OrderToIndex :: #force_inline proc "system" (hwnd: HWND, i: c_int) -> c_int {
+ return cast(c_int)SendMessageW(hwnd,HDM_ORDERTOINDEX,cast(WPARAM)i,0)
+}
+Header_CreateDragImage :: #force_inline proc "system" (hwnd: HWND, i: c_int) -> HIMAGELIST {
+ return cast(HIMAGELIST)uintptr(SendMessageW(hwnd,HDM_CREATEDRAGIMAGE,cast(WPARAM)i,0))
+}
+Header_GetOrderArray :: #force_inline proc "system" (hwnd: HWND, iCount: c_int, lpi: ^c_int) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd,HDM_GETORDERARRAY,cast(WPARAM)iCount,cast(LPARAM)uintptr(lpi))
+}
+Header_SetOrderArray :: #force_inline proc "system" (hwnd: HWND, iCount: c_int, lpi: ^c_int) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd,HDM_SETORDERARRAY,cast(WPARAM)iCount,cast(LPARAM)uintptr(lpi))
+}
+Header_SetHotDivider :: #force_inline proc "system" (hwnd: HWND, fPos: BOOL, dw: DWORD) -> c_int {
+ return cast(c_int)SendMessageW(hwnd,HDM_SETHOTDIVIDER,cast(WPARAM)fPos,cast(LPARAM)dw)
+}
+Header_SetBitmapMargin :: #force_inline proc "system" (hwnd: HWND, iWidth: c_int) -> c_int {
+ return cast(c_int)SendMessageW(hwnd,HDM_SETBITMAPMARGIN,cast(WPARAM)iWidth,0)
+}
+Header_GetBitmapMargin :: #force_inline proc "system" (hwnd: HWND) -> c_int {
+ return cast(c_int)SendMessageW(hwnd,HDM_GETBITMAPMARGIN,0,0)
+}
+Header_SetUnicodeFormat :: #force_inline proc "system" (hwnd: HWND, fUnicode: BOOL) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd,HDM_SETUNICODEFORMAT,cast(WPARAM)fUnicode,0)
+}
+Header_GetUnicodeFormat :: #force_inline proc "system" (hwnd: HWND) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd,HDM_GETUNICODEFORMAT,0,0)
+}
+Header_SetFilterChangeTimeout :: #force_inline proc "system" (hwnd: HWND, i: c_int) -> c_int {
+ return cast(c_int)SendMessageW(hwnd,HDM_SETFILTERCHANGETIMEOUT,0,cast(LPARAM)i)
+}
+Header_EditFilter :: #force_inline proc "system" (hwnd: HWND, i: c_int, fDiscardChanges: BOOL) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd,HDM_EDITFILTER,cast(WPARAM)i,MAKELPARAM(fDiscardChanges,0))
+}
+Header_ClearFilter :: #force_inline proc "system" (hwnd: HWND, i: c_int) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd,HDM_CLEARFILTER,cast(WPARAM)i,0)
+}
+Header_ClearAllFilters :: #force_inline proc "system" (hwnd: HWND) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd,HDM_CLEARFILTER,~WPARAM(0),0)
+}
+
+// Toolbar Control
+COLORSCHEME :: struct {
+ dwSize: DWORD,
+ clrBtnHighlight: COLORREF,
+ clrBtnShadow: COLORREF,
+}
+LPCOLORSCHEME :: ^COLORSCHEME
+
+COLORMAP :: struct {
+ from: COLORREF,
+ to: COLORREF,
+}
+LPCOLORMAP :: ^COLORMAP
+
+TBBUTTON :: struct {
+ iBitmap: c_int,
+ idCommand: c_int,
+ fsState: BYTE,
+ fsStyle: BYTE,
+ bReserved: [size_of(uintptr) - 2]BYTE,
+ dwData: DWORD_PTR,
+ iString: INT_PTR,
+}
+PTBBUTTON :: ^TBBUTTON
+LPTBBUTTON :: ^TBBUTTON
+LPCTBBUTTON :: ^TBBUTTON
+
+TBADDBITMAP :: struct {
+ hInst: HINSTANCE,
+ nID: UINT_PTR,
+}
+LPTBADDBITMAP :: ^TBADDBITMAP
+
+TBSAVEPARAMSW :: struct {
+ hkr: HKEY,
+ pszSubKey: LPCWSTR,
+ pszValueName: LPCWSTR,
+}
+
+TBINSERTMARK :: struct {
+ iButton: c_int,
+ dwFlags: DWORD,
+}
+LPTBINSERTMARK :: ^TBINSERTMARK
+
+TBREPLACEBITMAP :: struct {
+ hInstOld: HINSTANCE,
+ nIDOld: UINT_PTR,
+ hInstNew: HINSTANCE,
+ nIDNew: UINT_PTR,
+ nButtons: c_int,
+}
+LPTBREPLACEBITMAP :: ^TBREPLACEBITMAP
+
+TBBUTTONINFOW :: struct {
+ cbSize: UINT,
+ dwMask: DWORD,
+ idCommand: c_int,
+ iImage: c_int,
+ fsState: BYTE,
+ fsStyle: BYTE,
+ cx: WORD,
+ lParam: DWORD_PTR,
+ pszText: LPWSTR,
+ cchText: c_int,
+}
+LPTBBUTTONINFOW :: ^TBBUTTONINFOW
+
+TBMETRICS :: struct {
+ cbSize: UINT,
+ dwMask: DWORD,
+ cxPad: c_int,
+ cyPad: c_int,
+ cxBarPad: c_int,
+ cyBarPad: c_int,
+ cxButtonSpacing: c_int,
+ cyButtonSpacing: c_int,
+}
+LPTBMETRICS :: ^TBMETRICS
+
+NMTTCUSTOMDRAW :: struct {
+ nmcd: NMCUSTOMDRAW,
+ uDrawFlags: UINT,
+}
+LPNMTTCUSTOMDRAW :: ^NMTTCUSTOMDRAW
+
+@(default_calling_convention="system")
+foreign Comctl32 {
+ CreateToolbarEx :: proc(hwnd: HWND, ws: DWORD, wID: UINT, nBitmaps: c_int, hBMInst: HINSTANCE, wBMID: UINT_PTR, lpButtons: LPCTBBUTTON, iNumButtons: c_int, dxButton,dyButton: c_int, dxBitmap,dyBitmap: c_int, uStructSize: UINT) -> HWND ---
+ CreateMappedBitmap :: proc(hInstance: HINSTANCE, idBitmap: INT_PTR, wFlags: UINT, lpColorMap: LPCOLORMAP, iNumMaps: c_int) -> HBITMAP ---
+}
+
+// Button Control
+BUTTON_IMAGELIST_ALIGN_LEFT :: 0
+BUTTON_IMAGELIST_ALIGN_RIGHT :: 1
+BUTTON_IMAGELIST_ALIGN_TOP :: 2
+BUTTON_IMAGELIST_ALIGN_BOTTOM :: 3
+BUTTON_IMAGELIST_ALIGN_CENTER :: 4
+
+BCSIF_GLYPH :: 0x0001
+BCSIF_IMAGE :: 0x0002
+BCSIF_STYLE :: 0x0004
+BCSIF_SIZE :: 0x0008
+
+BCSS_NOSPLIT :: 0x0001
+BCSS_STRETCH :: 0x0002
+BCSS_ALIGNLEFT :: 0x0004
+BCSS_IMAGE :: 0x0008
+
+BUTTON_IMAGELIST :: struct {
+ himl: HIMAGELIST,
+ margin: RECT,
+ uAlign: UINT,
+}
+PBUTTON_IMAGELIST :: ^BUTTON_IMAGELIST
+
+BUTTON_SPLITINFO :: struct {
+ mask: UINT,
+ himlGlyph: HIMAGELIST,
+ uSplitStyle: UINT,
+ size: SIZE,
+}
+PBUTTON_SPLITINFO :: ^BUTTON_SPLITINFO
+
+NMBCHOTITEM :: struct {
+ hdr: NMHDR,
+ dwFlags: DWORD,
+}
+LPNMBCHOTITEM :: ^NMBCHOTITEM
+
+NMBCDROPDOWN :: struct {
+ hdr: NMHDR,
+ rcButton: RECT,
+}
+LPNMBCDROPDOWN :: ^NMBCDROPDOWN
+
+// BCM_SETIMAGELIST value
+BCCL_NOGLYPH :: cast(HIMAGELIST)(~uintptr(0))
+
+Button_GetIdealSize :: #force_inline proc "system" (hwnd: HWND, psize: ^SIZE) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, BCM_GETIDEALSIZE, 0, cast(LPARAM)uintptr(psize))
+}
+Button_SetImageList :: #force_inline proc "system" (hwnd: HWND, pbuttonImagelist: PBUTTON_IMAGELIST) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, BCM_SETIMAGELIST, 0, cast(LPARAM)uintptr(pbuttonImagelist))
+}
+Button_GetImageList :: #force_inline proc "system" (hwnd: HWND, pbuttonImagelist: PBUTTON_IMAGELIST) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, BCM_GETIMAGELIST, 0, cast(LPARAM)uintptr(pbuttonImagelist))
+}
+Button_SetTextMargin :: #force_inline proc "system" (hwnd: HWND, pmargin: ^RECT) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, BCM_SETTEXTMARGIN, 0, cast(LPARAM)uintptr(pmargin))
+}
+Button_GetTextMargin :: #force_inline proc "system" (hwnd: HWND, pmargin: ^RECT) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, BCM_GETTEXTMARGIN, 0, cast(LPARAM)uintptr(pmargin))
+}
+Button_SetNote :: #force_inline proc "system" (hwnd: HWND, psz: LPCWSTR) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, BCM_SETNOTE, 0, cast(LPARAM)uintptr(psz))
+}
+Button_GetNote :: #force_inline proc "system" (hwnd: HWND, psz: LPCWSTR, pcc: ^c_int) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, BCM_GETNOTE, uintptr(pcc), cast(LPARAM)uintptr(psz))
+}
+Button_GetNoteLength :: #force_inline proc "system" (hwnd: HWND) -> LRESULT {
+ return SendMessageW(hwnd, BCM_GETNOTELENGTH, 0, 0)
+}
+Button_SetElevationRequiredState :: #force_inline proc "system" (hwnd: HWND, fRequired: BOOL) -> LRESULT {
+ return SendMessageW(hwnd, BCM_SETSHIELD, 0, cast(LPARAM)fRequired)
+}
+Button_SetDropDownState :: #force_inline proc "system" (hwnd: HWND, fDropDown: BOOL) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, BCM_SETDROPDOWNSTATE, cast(WPARAM)fDropDown, 0)
+}
+Button_SetSplitInfo :: #force_inline proc "system" (hwnd: HWND, psi: ^BUTTON_SPLITINFO) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, BCM_SETSPLITINFO, 0, cast(LPARAM)uintptr(psi))
+}
+Button_GetSplitInfo :: #force_inline proc "system" (hwnd: HWND, psi: ^BUTTON_SPLITINFO) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, BCM_GETSPLITINFO, 0, cast(LPARAM)uintptr(psi))
+}
+
+// Edit Control
+EDITBALLOONTIP :: struct {
+ cbStruct: DWORD,
+ pszTitle: LPCWSTR,
+ pszText: LPCWSTR,
+ ttiIcon: INT,
+}
+PEDITBALLOONTIP :: ^EDITBALLOONTIP
+
+Edit_SetCueBannerText :: #force_inline proc "system" (hwnd: HWND, lpcwText: LPCWSTR) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, EM_SETCUEBANNER, 0, cast(LPARAM)uintptr(lpcwText))
+}
+Edit_SetCueBannerTextFocused :: #force_inline proc "system" (hwnd: HWND, lpcwText: LPCWSTR, fDrawFocused: BOOL) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, EM_SETCUEBANNER, cast(WPARAM)fDrawFocused, cast(LPARAM)uintptr(lpcwText))
+}
+Edit_GetCueBannerText :: #force_inline proc "system" (hwnd: HWND, lpwText: LPWSTR, cchText: LONG) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, EM_GETCUEBANNER, uintptr(lpwText), cast(LPARAM)cchText)
+}
+Edit_ShowBalloonTip :: #force_inline proc "system" (hwnd: HWND, peditballoontip: PEDITBALLOONTIP) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, EM_SHOWBALLOONTIP, 0, cast(LPARAM)uintptr(peditballoontip))
+}
+Edit_HideBalloonTip :: #force_inline proc "system" (hwnd: HWND) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, EM_HIDEBALLOONTIP, 0, 0)
+}
+
+Edit_SetHilite :: #force_inline proc "system" (hwndCtl: HWND, ichStart: c_int, ichEnd: c_int) {
+ SendMessageW(hwndCtl, EM_SETHILITE, cast(WPARAM)ichStart, cast(LPARAM)ichEnd)
+}
+Edit_GetHilite :: #force_inline proc "system" (hwndCtl: HWND) -> DWORD {
+ return cast(DWORD)SendMessageW(hwndCtl, EM_GETHILITE, 0, 0)
+}
+
+Edit_NoSetFocus :: #force_inline proc "system" (hwndCtl: HWND) {
+ SendMessageW(hwndCtl, EM_NOSETFOCUS, 0, 0)
+}
+Edit_TakeFocus :: #force_inline proc "system" (hwndCtl: HWND) {
+ SendMessageW(hwndCtl, EM_TAKEFOCUS, 0, 0)
+}
+
+// Up Down Control
+@(default_calling_convention="system")
+foreign Comctl32 {
+ CreateUpDownControl :: proc(dwStyle: DWORD, x,y: c_int, cx,cy: c_int, hParent: HWND, nID: c_int, hInst: HINSTANCE, hBuddy: HWND, nUpper,nLower,nPos: c_int) -> HWND ---
+}
+
+// Progress Bar Control
+PBRANGE :: struct {
+ iLow: c_int,
+ iHigh: c_int,
+}
+PPBRANGE :: ^PBRANGE
+
+// Hot Key Control
+HOTKEYF_SHIFT :: 0x1
+HOTKEYF_CONTROL :: 0x2
+HOTKEYF_ALT :: 0x4
+HOTKEYF_EXT :: 0x8
+
+HKCOMB_NONE :: 0x01
+HKCOMB_S :: 0x02
+HKCOMB_C :: 0x04
+HKCOMB_A :: 0x08
+HKCOMB_SC :: 0x10
+HKCOMB_SA :: 0x20
+HKCOMB_CA :: 0x40
+HKCOMB_SCA :: 0x80
+
+// List View Control
+LVSIL_NORMAL :: 0
+LVSIL_SMALL :: 1
+LVSIL_STATE :: 2
+
+LVIF_TEXT :: 0x001
+LVIF_IMAGE :: 0x002
+LVIF_PARAM :: 0x004
+LVIF_STATE :: 0x008
+LVIF_INDENT :: 0x010
+LVIF_GROUPID :: 0x100
+LVIF_COLUMNS :: 0x200
+LVIF_NORECOMPUTE :: 0x800
+
+LVIS_FOCUSED :: 0x01
+LVIS_SELECTED :: 0x02
+LVIS_CUT :: 0x04
+LVIS_DROPHILITED :: 0x08
+LVIS_GLOW :: 0x10
+LVIS_ACTIVATING :: 0x20
+
+LVIS_OVERLAYMASK :: 0x0F00
+LVIS_STATEIMAGEMASK :: 0xF000
+
+LVNI_ALL :: 0x000
+LVNI_FOCUSED :: 0x001
+LVNI_SELECTED :: 0x002
+LVNI_CUT :: 0x004
+LVNI_DROPHILITED :: 0x008
+LVNI_ABOVE :: 0x100
+LVNI_BELOW :: 0x200
+LVNI_TOLEFT :: 0x400
+LVNI_TORIGHT :: 0x800
+
+LVFI_PARAM :: 0x01
+LVFI_STRING :: 0x02
+LVFI_PARTIAL :: 0x08
+LVFI_WRAP :: 0x20
+LVFI_NEARESTXY :: 0x40
+
+I_INDENTCALLBACK :: -1
+
+I_GROUPIDCALLBACK :: -1
+I_GROUPIDNONE :: -2
+
+LPSTR_TEXTCALLBACKW :: cast(LPWSTR)~uintptr(0)
+
+I_IMAGECALLBACK :: -1
+I_IMAGENONE :: -2
+
+I_COLUMNSCALLBACK :: ~UINT(0)
+
+LVIR_BOUNDS :: 0
+LVIR_ICON :: 1
+LVIR_LABEL :: 2
+LVIR_SELECTBOUNDS :: 3
+
+LVHT_NOWHERE :: 0x1
+LVHT_ONITEMICON :: 0x2
+LVHT_ONITEMLABEL :: 0x4
+LVHT_ONITEMSTATEICON :: 0x8
+LVHT_ONITEM :: LVHT_ONITEMICON | LVHT_ONITEMLABEL | LVHT_ONITEMSTATEICON
+
+LVHT_ABOVE :: 0x08
+LVHT_BELOW :: 0x10
+LVHT_TORIGHT :: 0x20
+LVHT_TOLEFT :: 0x40
+
+LVA_DEFAULT :: 0x0
+LVA_ALIGNLEFT :: 0x1
+LVA_ALIGNTOP :: 0x2
+LVA_SNAPTOGRID :: 0x5
+
+LVCF_FMT :: 0x001
+LVCF_WIDTH :: 0x002
+LVCF_TEXT :: 0x004
+LVCF_SUBITEM :: 0x008
+LVCF_IMAGE :: 0x010
+LVCF_ORDER :: 0x020
+LVCF_MINWIDTH :: 0x040
+LVCF_DEFAULTWIDTH :: 0x080
+LVCF_IDEALWIDTH :: 0x100
+
+LVCFMT_LEFT :: 0x0000000
+LVCFMT_RIGHT :: 0x0000001
+LVCFMT_CENTER :: 0x0000002
+LVCFMT_FIXED_WIDTH :: 0x0000100
+LVCFMT_IMAGE :: 0x0000800
+LVCFMT_BITMAP_ON_RIGHT :: 0x0001000
+LVCFMT_COL_HAS_IMAGES :: 0x0008000
+LVCFMT_NO_DPI_SCALE :: 0x0040000
+LVCFMT_FIXED_RATIO :: 0x0080000
+LVCFMT_LINE_BREAK :: 0x0100000
+LVCFMT_FILL :: 0x0200000
+LVCFMT_WRAP :: 0x0400000
+LVCFMT_NO_TITLE :: 0x0800000
+LVCFMT_SPLITBUTTON :: 0x1000000
+
+LVCFMT_JUSTIFYMASK :: 0x3
+LVCFMT_TILE_PLACEMENTMASK :: (LVCFMT_LINE_BREAK|LVCFMT_FILL)
+
+LVSCW_AUTOSIZE :: -1
+LVSCW_AUTOSIZE_USEHEADER :: -2
+
+LVSICF_NOINVALIDATEALL :: 0x1
+LVSICF_NOSCROLL :: 0x2
+
+LVS_EX_GRIDLINES :: 0x00000001
+LVS_EX_SUBITEMIMAGES :: 0x00000002
+LVS_EX_CHECKBOXES :: 0x00000004
+LVS_EX_TRACKSELECT :: 0x00000008
+LVS_EX_HEADERDRAGDROP :: 0x00000010
+LVS_EX_FULLROWSELECT :: 0x00000020
+LVS_EX_ONECLICKACTIVATE :: 0x00000040
+LVS_EX_TWOCLICKACTIVATE :: 0x00000080
+LVS_EX_FLATSB :: 0x00000100
+LVS_EX_REGIONAL :: 0x00000200
+LVS_EX_INFOTIP :: 0x00000400
+LVS_EX_UNDERLINEHOT :: 0x00000800
+LVS_EX_UNDERLINECOLD :: 0x00001000
+LVS_EX_MULTIWORKAREAS :: 0x00002000
+LVS_EX_LABELTIP :: 0x00004000
+LVS_EX_BORDERSELECT :: 0x00008000
+LVS_EX_DOUBLEBUFFER :: 0x00010000
+LVS_EX_HIDELABELS :: 0x00020000
+LVS_EX_SINGLEROW :: 0x00040000
+LVS_EX_SNAPTOGRID :: 0x00080000
+LVS_EX_SIMPLESELECT :: 0x00100000
+LVS_EX_JUSTIFYCOLUMNS :: 0x00200000
+LVS_EX_TRANSPARENTBKGND :: 0x00400000
+LVS_EX_TRANSPARENTSHADOWTEXT :: 0x00800000
+LVS_EX_AUTOAUTOARRANGE :: 0x01000000
+LVS_EX_HEADERINALLVIEWS :: 0x02000000
+LVS_EX_AUTOCHECKSELECT :: 0x08000000
+LVS_EX_AUTOSIZECOLUMNS :: 0x10000000
+LVS_EX_COLUMNSNAPPOINTS :: 0x40000000
+LVS_EX_COLUMNOVERFLOW :: 0x80000000
+
+LV_MAX_WORKAREAS :: 16
+
+LVBKIF_SOURCE_NONE :: 0x0
+LVBKIF_SOURCE_HBITMAP :: 0x1
+LVBKIF_SOURCE_URL :: 0x2
+LVBKIF_SOURCE_MASK :: 0x3
+
+LVBKIF_STYLE_NORMAL :: 0x00
+LVBKIF_STYLE_TILE :: 0x10
+LVBKIF_STYLE_MASK :: 0x10
+
+LVBKIF_FLAG_TILEOFFSET :: 0x100
+
+LVBKIF_TYPE_WATERMARK :: 0x10000000
+
+LV_VIEW_ICON :: 0x0
+LV_VIEW_DETAILS :: 0x1
+LV_VIEW_SMALLICON :: 0x2
+LV_VIEW_LIST :: 0x3
+LV_VIEW_TILE :: 0x4
+LV_VIEW_MAX :: 0x4
+
+LVGF_NONE :: 0x00
+LVGF_HEADER :: 0x01
+LVGF_FOOTER :: 0x02
+LVGF_STATE :: 0x04
+LVGF_ALIGN :: 0x08
+LVGF_GROUPID :: 0x10
+
+LVGS_NORMAL :: 0x0
+LVGS_COLLAPSED :: 0x1
+LVGS_HIDDEN :: 0x2
+
+LVGA_HEADER_LEFT :: 0x1
+LVGA_HEADER_CENTER :: 0x2
+LVGA_HEADER_RIGHT :: 0x4
+LVGA_FOOTER_LEFT :: 0x8
+LVGA_FOOTER_CENTER :: 0x10
+LVGA_FOOTER_RIGHT :: 0x20
+
+LVGMF_NONE :: 0x0
+LVGMF_BORDERSIZE :: 0x1
+LVGMF_BORDERCOLOR :: 0x2
+LVGMF_TEXTCOLOR :: 0x4
+
+LVTVIF_AUTOSIZE :: 0x0
+LVTVIF_FIXEDWIDTH :: 0x1
+LVTVIF_FIXEDHEIGHT :: 0x2
+LVTVIF_FIXEDSIZE :: 0x3
+
+LVTVIM_TILESIZE :: 0x1
+LVTVIM_COLUMNS :: 0x2
+LVTVIM_LABELMARGIN :: 0x4
+
+LVIM_AFTER :: 0x1
+
+LVKF_ALT :: 0x1
+LVKF_CONTROL :: 0x2
+LVKF_SHIFT :: 0x4
+
+LVCDI_ITEM :: 0x0
+LVCDI_GROUP :: 0x1
+
+LVCDRF_NOSELECT :: 0x10000
+LVCDRF_NOGROUPFRAME :: 0x20000
+
+LVN_ITEMCHANGING :: (LVN_FIRST-0)
+LVN_ITEMCHANGED :: (LVN_FIRST-1)
+LVN_INSERTITEM :: (LVN_FIRST-2)
+LVN_DELETEITEM :: (LVN_FIRST-3)
+LVN_DELETEALLITEMS :: (LVN_FIRST-4)
+LVN_BEGINLABELEDITA :: (LVN_FIRST-5)
+LVN_BEGINLABELEDITW :: (LVN_FIRST-75)
+LVN_ENDLABELEDITA :: (LVN_FIRST-6)
+LVN_ENDLABELEDITW :: (LVN_FIRST-76)
+LVN_COLUMNCLICK :: (LVN_FIRST-8)
+LVN_BEGINDRAG :: (LVN_FIRST-9)
+LVN_BEGINRDRAG :: (LVN_FIRST-11)
+LVN_ODCACHEHINT :: (LVN_FIRST-13)
+LVN_ODFINDITEMA :: (LVN_FIRST-52)
+LVN_ODFINDITEMW :: (LVN_FIRST-79)
+LVN_ITEMACTIVATE :: (LVN_FIRST-14)
+LVN_ODSTATECHANGED :: (LVN_FIRST-15)
+LVN_HOTTRACK :: (LVN_FIRST-21)
+LVN_GETDISPINFOA :: (LVN_FIRST-50)
+LVN_GETDISPINFOW :: (LVN_FIRST-77)
+LVN_SETDISPINFOA :: (LVN_FIRST-51)
+LVN_SETDISPINFOW :: (LVN_FIRST-78)
+LVN_KEYDOWN :: (LVN_FIRST-55)
+LVN_MARQUEEBEGIN :: (LVN_FIRST-56)
+LVN_GETINFOTIPA :: (LVN_FIRST-57)
+LVN_GETINFOTIPW :: (LVN_FIRST-58)
+LVN_BEGINSCROLL :: (LVN_FIRST-80)
+LVN_ENDSCROLL :: (LVN_FIRST-81)
+
+LVIF_DI_SETITEM :: 0x1000
+
+LVGIT_UNFOLDED :: 0x1
+
+LVITEMW :: struct {
+ mask: UINT,
+ iItem: c_int,
+ iSubItem: c_int,
+ state: UINT,
+ stateMask: UINT,
+ pszText: LPWSTR,
+ cchTextMax: c_int,
+ iImage: c_int,
+ lParam: LPARAM,
+ iIndent: c_int,
+ iGroupId: c_int,
+ cColumns: UINT,
+ puColumns: PUINT,
+}
+LV_ITEMW :: LVITEMW
+LPLVITEMW :: ^LVITEMW
+LPLV_ITEMW :: LPLVITEMW
+
+LVFINDINFOW :: struct {
+ flags: UINT,
+ psz: LPCWSTR,
+ lParam: LPARAM,
+ pt: POINT,
+ vkDirection: UINT,
+}
+LPFINDINFOW :: ^LVFINDINFOW
+LV_FINDINFOW :: LVFINDINFOW
+
+LVHITTESTINFO :: struct {
+ pt: POINT,
+ flags: UINT,
+ iItem: c_int,
+ iSubItem: c_int,
+}
+LV_HITTESTINFO :: LVHITTESTINFO
+LPLVHITTESTINFO :: ^LVHITTESTINFO
+LPLV_HITTESTINFO :: LPLVHITTESTINFO
+
+LVCOLUMNW :: struct {
+ mask: UINT,
+ fmt: c_int,
+ cx: c_int,
+ pszText: LPWSTR,
+ cchTextMax: c_int,
+ iSubItem: c_int,
+ iImage: c_int,
+ iOrder: c_int,
+ cxMin: c_int,
+ cxDefault: c_int,
+ cxIdeal: c_int,
+}
+LV_COLUMNW :: LVCOLUMNW
+LPLVCOLUMNW :: ^LVCOLUMNW
+LPLV_COLUMNW :: LPLVCOLUMNW
+
+LVBKIMAGEW :: struct {
+ ulFlags: ULONG,
+ hbm: HBITMAP,
+ pszImage: LPWSTR,
+ cchImageMax: UINT,
+ xOffsetPercent: c_int,
+ yOffsetPercent: c_int,
+}
+LV_BKIMAGEW :: LVBKIMAGEW
+LPLVBKIMAGEW :: ^LVBKIMAGEW
+LPLV_BKIMAGEW :: LPLVBKIMAGEW
+
+LVGROUP :: struct {
+ cbSize: UINT,
+ mask: UINT,
+ pszHeader: LPWSTR,
+ cchHeader: c_int,
+ pszFooter: LPWSTR,
+ cchFooter: c_int,
+ iGroupId: c_int,
+ stateMask: UINT,
+ state: UINT,
+ uAlign: UINT,
+}
+PLVGROUP :: ^LVGROUP
+
+LVGROUPMETRICS :: struct {
+ cbSize: UINT,
+ mask: UINT,
+ Left: UINT,
+ Top: UINT,
+ Right: UINT,
+ Bottom: UINT,
+ crLeft: COLORREF,
+ crTop: COLORREF,
+ crRight: COLORREF,
+ crBottom: COLORREF,
+ crHeader: COLORREF,
+ crFooter: COLORREF,
+}
+PLVGROUPMETRICS :: ^LVGROUPMETRICS
+
+LVINSERTGROUPSORTED :: struct {
+ pfnGroupCompare: PFNLVGROUPCOMPARE,
+ pvData: rawptr,
+ lvGroup: LVGROUP,
+}
+PLVINSERTGROUPSORTED :: ^LVINSERTGROUPSORTED
+
+LVTILEVIEWINFO :: struct {
+ cbSize: UINT,
+ dwMask: DWORD,
+ dwFlags: DWORD,
+ sizeTile: SIZE,
+ cLines: c_int,
+ rcLabelMargin: RECT,
+}
+PLVTILEVIEWINFO :: ^LVTILEVIEWINFO
+
+LVTILEINFO :: struct {
+ cbSize: UINT,
+ iItem: c_int,
+ cColumns: UINT,
+ puColumns: PUINT,
+}
+PLVTILEINFO :: ^LVTILEINFO
+
+LVINSERTMARK :: struct {
+ cbSize: UINT,
+ dwFlags: DWORD,
+ iItem: c_int,
+ dwReserved: DWORD,
+}
+LPLVINSERTMARK :: ^LVINSERTMARK
+
+LVSETINFOTIP :: struct {
+ cbSize: UINT,
+ dwFlags: DWORD,
+ pszText: LPWSTR,
+ iItem: c_int,
+ iSubItem: c_int,
+}
+PLVSETINFOTIP :: ^LVSETINFOTIP
+
+NMLISTVIEW :: struct {
+ hdr: NMHDR,
+ iItem: c_int,
+ iSubItem: c_int,
+ uNewState: UINT,
+ uOldState: UINT,
+ uChanged: UINT,
+ ptAction: POINT,
+ lParam: LPARAM,
+}
+NM_LISTVIEW :: NMLISTVIEW
+LPNMLISTVIEW :: ^NMLISTVIEW
+LPNM_LISTVIEW :: LPNMLISTVIEW
+
+NMITEMACTIVATE :: struct {
+ hdr: NMHDR,
+ iItem: c_int,
+ iSubItem: c_int,
+ uNewState: UINT,
+ uOldState: UINT,
+ uChanged: UINT,
+ ptAction: POINT,
+ lParam: LPARAM,
+ uKeyFlags: UINT,
+}
+NM_ITEMACTIVATE :: NMITEMACTIVATE
+LPNMITEMACTIVATE :: ^NMITEMACTIVATE
+LPNM_ITEMACTIVATE :: LPNMITEMACTIVATE
+
+NMLVCUSTOMDRAW :: struct {
+ nmcd: NMCUSTOMDRAW,
+ clrText: COLORREF,
+ clrTextBk: COLORREF,
+ iSubItem: c_int,
+ dwItemType: DWORD,
+ clrFace: COLORREF,
+ iIconEffect: c_int,
+ iIconPhase: c_int,
+ iPartId: c_int,
+ iStateId: c_int,
+ rcText: RECT,
+ uAlign: UINT,
+}
+NMLV_CUSTOMDRAW :: NMLVCUSTOMDRAW
+LPNMLVCUSTOMDRAW :: ^NMLVCUSTOMDRAW
+LPNMLV_CUSTOMDRAW :: LPNMLVCUSTOMDRAW
+
+NMLVCACHEHINT :: struct {
+ hdr: NMHDR,
+ iFrom: c_int,
+ iTo: c_int,
+}
+LPNMLVCACHEHINT :: ^NMLVCACHEHINT
+NM_CACHEHINT :: NMLVCACHEHINT
+PNM_CACHEHINT :: LPNMLVCACHEHINT
+LPNM_CACHEHINT :: LPNMLVCACHEHINT
+
+NMLVFINDITEMW :: struct {
+ hdr: NMHDR,
+ iStart: c_int,
+ lvfi: LVFINDINFOW,
+}
+LPNMLVFINDITEMW :: ^NMLVFINDITEMW
+NM_FINDITEMW :: NMLVFINDITEMW
+PNM_FINDITEMW :: LPNMLVFINDITEMW
+LPNM_FINDITEMW :: LPNMLVFINDITEMW
+
+NMLVODSTATECHANGE :: struct {
+ hdr: NMHDR,
+ iFrom: c_int,
+ iTo: c_int,
+ uNewState: UINT,
+ uOldState: UINT,
+}
+LPNMLVODSTATECHANGE :: ^NMLVODSTATECHANGE
+NM_ODSTATECHANGE :: NMLVODSTATECHANGE
+PNM_ODSTATECHANGE :: NMLVODSTATECHANGE
+LPNM_ODSTATECHANGE :: LPNMLVODSTATECHANGE
+
+LVDISPINFOW :: struct {
+ hdr: NMHDR,
+ item: LVITEMW,
+}
+LV_DISPINFO :: LVDISPINFOW
+LPNMLVDISPINFOW :: ^LVDISPINFOW
+
+NMLVKEYDOWN :: struct #packed {
+ hdr: NMHDR,
+ wVKey: WORD,
+ flags: UINT,
+}
+LV_KEYDOWN :: NMLVKEYDOWN
+LPNMLVKEYDOWN :: ^NMLVKEYDOWN
+
+NMLVGETINFOTIPW :: struct {
+ hdr: NMHDR,
+ dwFlags: DWORD,
+ pszText: LPWSTR,
+ cchTextMax: c_int,
+ iItem: c_int,
+ iSubItem: c_int,
+ lParam: LPARAM,
+}
+LPNMLVGETINFOTIPW :: ^NMLVGETINFOTIPW
+
+NMLVSCROLL :: struct {
+ hdr: NMHDR,
+ dx: c_int,
+ dy: c_int,
+}
+LPNMLVSCROLL :: ^NMLVSCROLL
+
+PFNLVCOMPARE :: #type proc "system" (lpItem1,lpItem2: LPARAM, lpUser: LPARAM) -> c_int
+PFNLVGROUPCOMPARE :: #type proc "system" (item1,item2: c_int, user: rawptr) -> c_int
+
+INDEXTOSTATEIMAGEMASK :: #force_inline proc "system" (i: UINT) -> UINT {
+ return i << 12
+}
+
+ListView_GetItem :: #force_inline proc "system" (hwnd: HWND, pitem: ^LV_ITEMW) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, LVM_GETITEMW, 0, cast(LPARAM)uintptr(pitem))
+}
+ListView_SetItem :: #force_inline proc "system" (hwnd: HWND, pitem: ^LV_ITEMW) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, LVM_SETITEMW, 0, cast(LPARAM)uintptr(pitem))
+}
+ListView_InsertItem :: #force_inline proc "system" (hwnd: HWND, pitem: ^LV_ITEMW) -> c_int {
+ return cast(c_int)SendMessageW(hwnd, LVM_INSERTITEMW, 0, cast(LPARAM)uintptr(pitem))
+}
+ListView_DeleteItem :: #force_inline proc "system" (hwnd: HWND, i: c_int) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, LVM_DELETEITEM, cast(WPARAM)i, 0)
+}
+ListView_DeleteAllItems :: #force_inline proc "system" (hwnd: HWND) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, LVM_DELETEALLITEMS, 0, 0)
+}
+ListView_GetCallbackMask :: #force_inline proc "system" (hwnd: HWND) -> UINT {
+ return cast(UINT)SendMessageW(hwnd, LVM_GETCALLBACKMASK, 0, 0)
+}
+ListView_SetCallbackMask :: #force_inline proc "system" (hwnd: HWND, mask: UINT) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, LVM_SETCALLBACKMASK, cast(WPARAM)mask, 0)
+}
+ListView_GetNextItem :: #force_inline proc "system" (hwnd: HWND, i: c_int, flags: UINT) -> c_int {
+ return cast(c_int)SendMessageW(hwnd, LVM_GETNEXTITEM, cast(WPARAM)i, MAKELPARAM(flags,0))
+}
+ListView_FindItem :: #force_inline proc "system" (hwnd: HWND, iStart: c_int, plvfi: ^LV_FINDINFOW) -> c_int {
+ return cast(c_int)SendMessageW(hwnd, LVM_FINDITEMW, cast(WPARAM)iStart, cast(LPARAM)uintptr(plvfi))
+}
+ListView_GetItemRect :: #force_inline proc "system" (hwnd: HWND, i: c_int, prc: ^RECT, code: c_int) -> BOOL {
+ if prc != nil {
+ prc.left = code
+ }
+ return cast(BOOL)SendMessageW(hwnd, LVM_GETITEMRECT, cast(WPARAM)i, cast(LPARAM)uintptr(prc))
+}
+ListView_SetItemPosition :: #force_inline proc "system" (hwnd: HWND, i: c_int, x,y: c_int) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, LVM_SETITEMPOSITION, cast(WPARAM)i, MAKELPARAM(x,y))
+}
+ListView_GetItemPosition :: #force_inline proc "system" (hwnd: HWND, i: c_int, ppt: ^POINT) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, LVM_GETITEMPOSITION, cast(WPARAM)i, cast(LPARAM)uintptr(ppt))
+}
+ListView_GetStringWidth :: #force_inline proc "system" (hwndLV: HWND, psz: LPCWSTR) -> c_int {
+ return cast(c_int)SendMessageW(hwndLV, LVM_GETSTRINGWIDTHW, 0, cast(LPARAM)uintptr(psz))
+}
+ListView_HitTest :: #force_inline proc "system" (hwndLV: HWND, pinfo: ^LV_HITTESTINFO) -> c_int {
+ return cast(c_int)SendMessageW(hwndLV, LVM_HITTEST, 0, cast(LPARAM)uintptr(pinfo))
+}
+ListView_EnsureVisible :: #force_inline proc "system" (hwndLV: HWND, i: c_int, fPartialOK: BOOL) -> BOOL {
+ return cast(BOOL)SendMessageW(hwndLV, LVM_ENSUREVISIBLE, cast(WPARAM)i, MAKELPARAM(fPartialOK,0))
+}
+ListView_Scroll :: #force_inline proc "system" (hwndLV: HWND, dx,dy: c_int) -> BOOL {
+ return cast(BOOL)SendMessageW(hwndLV, LVM_SCROLL, cast(WPARAM)dx, cast(LPARAM)dy)
+}
+ListView_RedrawItems :: #force_inline proc "system" (hwndLV: HWND, iFirst,iLast: c_int) -> BOOL {
+ return cast(BOOL)SendMessageW(hwndLV, LVM_REDRAWITEMS, cast(WPARAM)iFirst, cast(LPARAM)iLast)
+}
+ListView_Arrange :: #force_inline proc "system" (hwndLV: HWND, code: UINT) -> BOOL {
+ return cast(BOOL)SendMessageW(hwndLV, LVM_ARRANGE, cast(WPARAM)code, 0)
+}
+ListView_EditLabel :: #force_inline proc "system" (hwndLV: HWND, i: c_int) -> HWND {
+ return cast(HWND)uintptr(SendMessageW(hwndLV, LVM_EDITLABELW, cast(WPARAM)i, 0))
+}
+ListView_GetEditControl :: #force_inline proc "system" (hwndLV: HWND) -> HWND {
+ return cast(HWND)uintptr(SendMessageW(hwndLV, LVM_GETEDITCONTROL, 0, 0))
+}
+ListView_GetColumn :: #force_inline proc "system" (hwnd: HWND, iCol: c_int, pcol: ^LV_COLUMNW) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, LVM_GETCOLUMNW, cast(WPARAM)iCol, cast(LPARAM)uintptr(pcol))
+}
+ListView_SetColumn :: #force_inline proc "system" (hwnd: HWND, iCol: c_int, pcol: ^LV_COLUMNW) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, LVM_SETCOLUMNW, cast(WPARAM)iCol, cast(LPARAM)uintptr(pcol))
+}
+ListView_InsertColumn :: #force_inline proc "system" (hwnd: HWND, iCol: c_int, pcol: ^LV_COLUMNW) -> c_int {
+ return cast(c_int)SendMessageW(hwnd, LVM_INSERTCOLUMNW, cast(WPARAM)iCol, cast(LPARAM)uintptr(pcol))
+}
+ListView_DeleteColumn :: #force_inline proc "system" (hwnd: HWND, iCol: c_int) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, LVM_DELETECOLUMN, cast(WPARAM)iCol, 0)
+}
+ListView_GetColumnWidth :: #force_inline proc "system" (hwnd: HWND, iCol: c_int) -> c_int {
+ return cast(c_int)SendMessageW(hwnd, LVM_GETCOLUMNWIDTH, cast(WPARAM)iCol, 0)
+}
+ListView_SetColumnWidth :: #force_inline proc "system" (hwnd: HWND, iCol: c_int, cx: c_int) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, LVM_SETCOLUMNWIDTH, cast(WPARAM)iCol, MAKELPARAM(cx,0))
+}
+ListView_GetHeader :: #force_inline proc "system" (hwnd: HWND) -> HWND {
+ return cast(HWND)uintptr(SendMessageW(hwnd, LVM_GETHEADER, 0, 0))
+}
+ListView_CreateDragImage :: #force_inline proc "system" (hwnd: HWND, i: c_int, lpptUpLeft: LPPOINT) -> HIMAGELIST {
+ return cast(HIMAGELIST)uintptr(SendMessageW(hwnd, LVM_CREATEDRAGIMAGE, cast(WPARAM)i, cast(LPARAM)uintptr(lpptUpLeft)))
+}
+ListView_GetViewRect :: #force_inline proc "system" (hwnd: HWND, prc: ^RECT) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, LVM_GETVIEWRECT, 0, cast(LPARAM)uintptr(prc))
+}
+ListView_GetTextColor :: #force_inline proc "system" (hwnd: HWND) -> COLORREF {
+ return cast(COLORREF)SendMessageW(hwnd, LVM_GETTEXTCOLOR, 0, 0)
+}
+ListView_SetTextColor :: #force_inline proc "system" (hwnd: HWND, clrText: COLORREF) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, LVM_SETTEXTCOLOR, 0, cast(LPARAM)clrText)
+}
+ListView_GetTextBkColor :: #force_inline proc "system" (hwnd: HWND) -> COLORREF {
+ return cast(COLORREF)SendMessageW(hwnd, LVM_GETTEXTBKCOLOR, 0, 0)
+}
+ListView_SetTextBkColor :: #force_inline proc "system" (hwnd: HWND, clrTextBk: COLORREF) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, LVM_SETTEXTBKCOLOR, 0, cast(LPARAM)clrTextBk)
+}
+ListView_GetTopIndex :: #force_inline proc "system" (hwndLV: HWND) -> c_int {
+ return cast(c_int)SendMessageW(hwndLV, LVM_GETTOPINDEX, 0, 0)
+}
+ListView_GetCountPerPage :: #force_inline proc "system" (hwndLV: HWND) -> c_int {
+ return cast(c_int)SendMessageW(hwndLV, LVM_GETCOUNTPERPAGE, 0, 0)
+}
+ListView_GetOrigin :: #force_inline proc "system" (hwndLV: HWND, ppt: ^POINT) -> BOOL {
+ return cast(BOOL)SendMessageW(hwndLV, LVM_GETORIGIN, 0, cast(LPARAM)uintptr(ppt))
+}
+ListView_Update :: #force_inline proc "system" (hwndLV: HWND, i: c_int) -> BOOL {
+ return cast(BOOL)SendMessageW(hwndLV, LVM_UPDATE, cast(WPARAM)i, 0)
+}
+ListView_SetItemState :: #force_inline proc "system" (hwndLV: HWND, i: c_int, data: UINT, mask: UINT) {
+ item := LV_ITEMW {
+ stateMask = mask,
+ state = data,
+ }
+ SendMessageW(hwndLV, LVM_SETITEMSTATE, cast(WPARAM)i, cast(LPARAM)uintptr(&item))
+}
+ListView_SetCheckState :: #force_inline proc "system" (hwndLV: HWND, i: c_int, fCheck: BOOL) {
+ ListView_SetItemState(hwndLV, i, INDEXTOSTATEIMAGEMASK(2 if fCheck else 1), LVIS_STATEIMAGEMASK)
+}
+ListView_GetItemState :: #force_inline proc "system" (hwndLV: HWND, i: c_int, mask: UINT) -> UINT {
+ return cast(UINT)SendMessageW(hwndLV, LVM_GETITEMSTATE, cast(WPARAM)i, cast(LPARAM)mask)
+}
+ListView_GetCheckState :: #force_inline proc "system" (hwndLV: HWND, i: c_int) -> UINT {
+ return ((cast(UINT)SendMessageW(hwndLV, LVM_GETITEMSTATE, cast(WPARAM)i, cast(LPARAM)LVIS_STATEIMAGEMASK)) >> 12) - 1
+}
+ListView_GetItemText :: #force_inline proc "system" (hwndLV: HWND, i: c_int, iSubItem: c_int, pszText: LPWSTR, cchTextMax: c_int) {
+ item := LV_ITEMW {
+ iSubItem = iSubItem,
+ cchTextMax = cchTextMax,
+ pszText = pszText,
+ }
+ SendMessageW(hwndLV, LVM_GETITEMTEXTW, cast(WPARAM)i, cast(LPARAM)uintptr(&item))
+}
+ListView_SetItemText :: #force_inline proc "system" (hwndLV: HWND, i: c_int, iSubItem: c_int, pszText: LPWSTR) {
+ item := LV_ITEMW {
+ iSubItem = iSubItem,
+ pszText = pszText,
+ }
+ SendMessageW(hwndLV, LVM_SETITEMTEXTW, cast(WPARAM)i, cast(LPARAM)uintptr(&item))
+}
+ListView_SetItemCount :: #force_inline proc "system" (hwndLV: HWND, cItems: c_int) {
+ SendMessageW(hwndLV, LVM_SETITEMCOUNT, cast(WPARAM)cItems, 0)
+}
+ListView_SetItemCountEx :: #force_inline proc "system" (hwndLV: HWND, cItems: c_int, dwFlags: DWORD) {
+ SendMessageW(hwndLV, LVM_SETITEMCOUNT, cast(WPARAM)cItems, cast(LPARAM)dwFlags)
+}
+ListView_SortItems :: #force_inline proc "system" (hwndLV: HWND, pfnCompare: PFNLVCOMPARE, lpUser: LPARAM) -> BOOL {
+ return cast(BOOL)SendMessageW(hwndLV, LVM_SORTITEMS, cast(WPARAM)lpUser, cast(LPARAM)transmute(uintptr)(pfnCompare))
+}
+ListView_SetItemPosition32 :: #force_inline proc "system" (hwndLV: HWND, i: c_int, x0,y0: c_int) {
+ ptNewPos := POINT {
+ x = x0,
+ y = y0,
+ }
+ SendMessageW(hwndLV, LVM_SETITEMPOSITION32, cast(WPARAM)i, cast(LPARAM)uintptr(&ptNewPos))
+}
+ListView_GetSelectedCount :: #force_inline proc "system" (hwndLV: HWND) -> UINT {
+ return cast(UINT)SendMessageW(hwndLV, LVM_GETSELECTEDCOUNT, 0, 0)
+}
+ListView_GetItemSpacing :: #force_inline proc "system" (hwndLV: HWND, fSmall: BOOL) -> DWORD {
+ return cast(DWORD)SendMessageW(hwndLV, LVM_GETITEMSPACING, cast(WPARAM)fSmall, 0)
+}
+ListView_GetISearchString :: #force_inline proc "system" (hwndLV: HWND, lpsz: LPWSTR) -> BOOL {
+ return cast(BOOL)SendMessageW(hwndLV, LVM_GETISEARCHSTRINGW, 0, cast(LPARAM)uintptr(lpsz))
+}
+ListView_SetIconSpacing :: #force_inline proc "system" (hwndLV: HWND, cx,cy: c_int) -> DWORD {
+ return cast(DWORD)SendMessageW(hwndLV, LVM_SETICONSPACING, 0, cast(LPARAM)MAKELONG(cx,cy))
+}
+ListView_SetExtendedListViewStyle :: #force_inline proc "system" (hwndLV: HWND, dw: DWORD) -> DWORD {
+ return cast(DWORD)SendMessageW(hwndLV, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, cast(LPARAM)dw)
+}
+ListView_SetExtendedListViewStyleEx :: #force_inline proc "system" (hwndLV: HWND, dwMask: DWORD, dw: DWORD) -> DWORD {
+ return cast(DWORD)SendMessageW(hwndLV, LVM_SETEXTENDEDLISTVIEWSTYLE, cast(WPARAM)dwMask, cast(LPARAM)dw)
+}
+ListView_GetSubItemRect :: #force_inline proc "system" (hwnd: HWND, iItem: c_int, iSubItem: c_int, code: c_int, prc: LPRECT) -> BOOL {
+ if prc != nil {
+ prc.top = iSubItem
+ prc.left = code
+ }
+ return cast(BOOL)SendMessageW(hwnd, LVM_GETSUBITEMRECT, cast(WPARAM)iItem, cast(LPARAM)uintptr(prc))
+}
+ListView_SubItemHitTest :: #force_inline proc "system" (hwnd: HWND, plvhti: LPLVHITTESTINFO) -> c_int {
+ return cast(c_int)SendMessageW(hwnd, LVM_SUBITEMHITTEST, 0, cast(LPARAM)uintptr(plvhti))
+}
+ListView_SetColumnOrderArray :: #force_inline proc "system" (hwnd: HWND, iCount: c_int, pi: LPINT) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, LVM_SETCOLUMNORDERARRAY, cast(WPARAM)iCount, cast(LPARAM)uintptr(pi))
+}
+ListView_GetColumnOrderArray :: #force_inline proc "system" (hwnd: HWND, iCount: c_int, pi: LPINT) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, LVM_GETCOLUMNORDERARRAY, cast(WPARAM)iCount, cast(LPARAM)uintptr(pi))
+}
+ListView_SetHotItem :: #force_inline proc "system" (hwnd: HWND, i: c_int) -> c_int {
+ return cast(c_int)SendMessageW(hwnd, LVM_SETHOTITEM, cast(WPARAM)i, 0)
+}
+ListView_GetHotItem :: #force_inline proc "system" (hwnd: HWND) -> c_int {
+ return cast(c_int)SendMessageW(hwnd, LVM_GETHOTITEM, 0, 0)
+}
+ListView_SetHotCursor :: #force_inline proc "system" (hwnd: HWND, hcur: HCURSOR) -> HCURSOR {
+ return cast(HCURSOR)uintptr(SendMessageW(hwnd, LVM_SETHOTCURSOR, 0, cast(LPARAM)uintptr(hcur)))
+}
+ListView_GetHotCursor :: #force_inline proc "system" (hwnd: HWND) -> HCURSOR {
+ return cast(HCURSOR)uintptr(SendMessageW(hwnd, LVM_GETHOTCURSOR, 0, 0))
+}
+ListView_ApproximateViewRect :: #force_inline proc "system" (hwnd: HWND, iWidth,iHeight: c_int, iCount: c_int) -> DWORD {
+ return cast(DWORD)SendMessageW(hwnd, LVM_APPROXIMATEVIEWRECT, cast(WPARAM)iCount, MAKELPARAM(iWidth,iHeight))
+}
+ListView_SetWorkAreas :: #force_inline proc "system" (hwnd: HWND, nWorkAreas: UINT, prc: ^RECT) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, LVM_SETWORKAREAS, cast(WPARAM)nWorkAreas, cast(LPARAM)uintptr(prc))
+}
+ListView_GetWorkAreas :: #force_inline proc "system" (hwnd: HWND, nWorkAreas: UINT, prc: ^RECT) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, LVM_GETWORKAREAS, cast(WPARAM)nWorkAreas, cast(LPARAM)uintptr(prc))
+}
+ListView_GetNumberOfWorkAreas :: #force_inline proc "system" (hwnd: HWND, pnWorkAreas: ^UINT) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, LVM_GETNUMBEROFWORKAREAS, 0, cast(LPARAM)uintptr(pnWorkAreas))
+}
+ListView_GetSelectionMark :: #force_inline proc "system" (hwnd: HWND) -> c_int {
+ return cast(c_int)SendMessageW(hwnd, LVM_GETSELECTIONMARK, 0, 0)
+}
+ListView_SetSelectionMark :: #force_inline proc "system" (hwnd: HWND, i: c_int) -> c_int {
+ return cast(c_int)SendMessageW(hwnd, LVM_SETSELECTIONMARK, 0, cast(LPARAM)i)
+}
+ListView_SetHoverTime :: #force_inline proc "system" (hwndLV: HWND, dwHoverTimeMs: DWORD) -> DWORD {
+ return cast(DWORD)SendMessageW(hwndLV, LVM_SETHOVERTIME, 0, cast(LPARAM)dwHoverTimeMs)
+}
+ListView_GetHoverTime :: #force_inline proc "system" (hwndLV: HWND) -> DWORD {
+ return cast(DWORD)SendMessageW(hwndLV, LVM_GETHOVERTIME, 0, 0)
+}
+ListView_SetToolTips :: #force_inline proc "system" (hwndLV: HWND, hwndNewHwnd: HWND) -> HWND {
+ return cast(HWND)uintptr(SendMessageW(hwndLV, LVM_SETTOOLTIPS, cast(WPARAM)hwndNewHwnd, 0))
+}
+ListView_GetToolTips :: #force_inline proc "system" (hwndLV: HWND) -> HWND {
+ return cast(HWND)uintptr(SendMessageW(hwndLV, LVM_GETTOOLTIPS, 0, 0))
+}
+ListView_SortItemsEx :: #force_inline proc "system" (hwndLV: HWND, pfnCompare: PFNLVCOMPARE, lpUser: LPARAM) -> BOOL {
+ return cast(BOOL)SendMessageW(hwndLV, LVM_SORTITEMSEX, cast(WPARAM)lpUser, cast(LPARAM)transmute(uintptr)(pfnCompare))
+}
+ListView_SetSelectedColumn :: #force_inline proc "system" (hwnd: HWND, iCol: c_int) {
+ SendMessageW(hwnd, LVM_SETSELECTEDCOLUMN, cast(WPARAM)iCol, 0)
+}
+ListView_SetView :: #force_inline proc "system" (hwnd: HWND, iView: DWORD) -> c_int {
+ return cast(c_int)SendMessageW(hwnd, LVM_SETVIEW, cast(WPARAM)iView, 0)
+}
+ListView_GetView :: #force_inline proc "system" (hwnd: HWND) -> DWORD {
+ return cast(DWORD)SendMessageW(hwnd, LVM_GETVIEW, 0, 0)
+}
+ListView_InsertGroup :: #force_inline proc "system" (hwnd: HWND, index: c_int, pgrp: PLVGROUP) -> c_int {
+ return cast(c_int)SendMessageW(hwnd, LVM_INSERTGROUP, cast(WPARAM)index, cast(LPARAM)uintptr(pgrp))
+}
+ListView_SetGroupInfo :: #force_inline proc "system" (hwnd: HWND, iGroupId: c_int, pgrp: PLVGROUP) -> c_int {
+ return cast(c_int)SendMessageW(hwnd, LVM_SETGROUPINFO, cast(WPARAM)iGroupId, cast(LPARAM)uintptr(pgrp))
+}
+ListView_GetGroupInfo :: #force_inline proc "system" (hwnd: HWND, iGroupId: c_int, pgrp: PLVGROUP) -> c_int {
+ return cast(c_int)SendMessageW(hwnd, LVM_GETGROUPINFO, cast(WPARAM)iGroupId, cast(LPARAM)uintptr(pgrp))
+}
+ListView_RemoveGroup :: #force_inline proc "system" (hwnd: HWND, iGroupId: c_int) -> c_int {
+ return cast(c_int)SendMessageW(hwnd, LVM_REMOVEGROUP, cast(WPARAM)iGroupId, 0)
+}
+ListView_MoveGroup :: #force_inline proc "system" (hwnd: HWND, iGroupId: c_int, toIndex: c_int) {
+ SendMessageW(hwnd, LVM_MOVEGROUP, cast(WPARAM)iGroupId, cast(LPARAM)toIndex)
+}
+ListView_MoveItemToGroup :: #force_inline proc "system" (hwnd: HWND, idItemFrom: c_int, idGroupTo: c_int) {
+ SendMessageW(hwnd, LVM_MOVEITEMTOGROUP, cast(WPARAM)idItemFrom, cast(LPARAM)idGroupTo)
+}
+ListView_SetGroupMetrics :: #force_inline proc "system" (hwnd: HWND, pGroupMetrics: PLVGROUPMETRICS) {
+ SendMessageW(hwnd, LVM_SETGROUPMETRICS, 0, cast(LPARAM)uintptr(pGroupMetrics))
+}
+ListView_GetGroupMetrics :: #force_inline proc "system" (hwnd: HWND, pGroupMetrics: PLVGROUPMETRICS) {
+ SendMessageW(hwnd, LVM_GETGROUPMETRICS, 0, cast(LPARAM)uintptr(pGroupMetrics))
+}
+ListView_EnableGroupView :: #force_inline proc "system" (hwnd: HWND, fEnable: BOOL) -> c_int {
+ return cast(c_int)SendMessageW(hwnd, LVM_ENABLEGROUPVIEW, cast(WPARAM)fEnable, 0)
+}
+ListView_SortGroups :: #force_inline proc "system" (hwnd: HWND, pfnGroupCompare: PFNLVGROUPCOMPARE, pUser: rawptr) -> c_int {
+ return cast(c_int)SendMessageW(hwnd, LVM_SORTGROUPS, transmute(uintptr)(pfnGroupCompare), cast(LPARAM)uintptr(pUser))
+}
+ListView_InsertGroupSorted :: #force_inline proc "system" (hwnd: HWND, structInsert: PLVINSERTGROUPSORTED) {
+ SendMessageW(hwnd, LVM_INSERTGROUPSORTED, uintptr(structInsert), 0)
+}
+ListView_RemoveAllGroups :: #force_inline proc "system" (hwnd: HWND) {
+ SendMessageW(hwnd, LVM_REMOVEALLGROUPS, 0, 0)
+}
+ListView_HasGroup :: #force_inline proc "system" (hwnd: HWND, dwGroupId: c_int) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, LVM_HASGROUP, cast(WPARAM)dwGroupId, 0)
+}
+ListView_SetTileViewInfo :: #force_inline proc "system" (hwnd: HWND, ptvi: PLVTILEVIEWINFO) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, LVM_SETTILEVIEWINFO, 0, cast(LPARAM)uintptr(ptvi))
+}
+ListView_GetTileViewInfo :: #force_inline proc "system" (hwnd: HWND, ptvi: PLVTILEVIEWINFO) {
+ SendMessageW(hwnd, LVM_GETTILEVIEWINFO, 0, cast(LPARAM)uintptr(ptvi))
+}
+ListView_SetTileInfo :: #force_inline proc "system" (hwnd: HWND, pti: PLVTILEINFO) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, LVM_SETTILEINFO, 0, cast(LPARAM)uintptr(pti))
+}
+ListView_GetTileInfo :: #force_inline proc "system" (hwnd: HWND, pti: PLVTILEINFO) {
+ SendMessageW(hwnd, LVM_GETTILEINFO, 0, cast(LPARAM)uintptr(pti))
+}
+ListView_SetInsertMark :: #force_inline proc "system" (hwnd: HWND, lvim: LPLVINSERTMARK) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, LVM_SETINSERTMARK, 0, cast(LPARAM)uintptr(lvim))
+}
+ListView_GetInsertMark :: #force_inline proc "system" (hwnd: HWND, lvim: LPLVINSERTMARK) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, LVM_GETINSERTMARK, 0, cast(LPARAM)uintptr(lvim))
+}
+ListView_InsertMarkHitTest :: #force_inline proc "system" (hwnd: HWND, point: LPPOINT, lvim: LPLVINSERTMARK) -> c_int {
+ return cast(c_int)SendMessageW(hwnd, LVM_INSERTMARKHITTEST, uintptr(point), cast(LPARAM)uintptr(lvim))
+}
+ListView_GetInsertMarkRect :: #force_inline proc "system" (hwnd: HWND, rc: LPRECT) -> c_int {
+ return cast(c_int)SendMessageW(hwnd, LVM_GETINSERTMARKRECT, 0, cast(LPARAM)uintptr(rc))
+}
+ListView_SetInsertMarkColor :: #force_inline proc "system" (hwnd: HWND, color: COLORREF) -> COLORREF {
+ return cast(COLORREF)SendMessageW(hwnd, LVM_SETINSERTMARKCOLOR, 0, cast(LPARAM)color)
+}
+ListView_GetInsertMarkColor :: #force_inline proc "system" (hwnd: HWND) -> COLORREF {
+ return cast(COLORREF)SendMessageW(hwnd, LVM_GETINSERTMARKCOLOR, 0, 0)
+}
+ListView_SetInfoTip :: #force_inline proc "system" (hwndLV: HWND, plvInfoTip: PLVSETINFOTIP) -> BOOL {
+ return cast(BOOL)SendMessageW(hwndLV, LVM_SETINFOTIP, 0, cast(LPARAM)uintptr(plvInfoTip))
+}
+ListView_GetSelectedColumn :: #force_inline proc "system" (hwnd: HWND) -> UINT {
+ return cast(UINT)SendMessageW(hwnd, LVM_GETSELECTEDCOLUMN, 0, 0)
+}
+ListView_IsGroupViewEnabled :: #force_inline proc "system" (hwnd: HWND) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, LVM_ISGROUPVIEWENABLED, 0, 0)
+}
+ListView_GetOutlineColor :: #force_inline proc "system" (hwnd: HWND) -> COLORREF {
+ return cast(COLORREF)SendMessageW(hwnd, LVM_GETOUTLINECOLOR, 0, 0)
+}
+ListView_SetOutlineColor :: #force_inline proc "system" (hwnd: HWND, color: COLORREF) -> COLORREF {
+ return cast(COLORREF)SendMessageW(hwnd, LVM_SETOUTLINECOLOR, 0, cast(LPARAM)color)
+}
+ListView_CancelEditLabel :: #force_inline proc "system" (hwnd: HWND) {
+ SendMessageW(hwnd, LVM_CANCELEDITLABEL, 0, 0)
+}
+ListView_MapIndexToID :: #force_inline proc "system" (hwnd: HWND, index: UINT) -> UINT {
+ return cast(UINT)SendMessageW(hwnd, LVM_MAPINDEXTOID, cast(WPARAM)index, 0)
+}
+ListView_MapIDToIndex :: #force_inline proc "system" (hwnd: HWND, id: UINT) -> UINT {
+ return cast(UINT)SendMessageW(hwnd, LVM_MAPIDTOINDEX, cast(WPARAM)id, 0)
+}
+ListView_IsItemVisible :: #force_inline proc "system" (hwnd: HWND, index: UINT) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, LVM_ISITEMVISIBLE, cast(WPARAM)index, 0)
+}
+
+// Tree View Control
+HTREEITEM :: distinct rawptr
+
+TVIF_TEXT :: 0x01
+TVIF_IMAGE :: 0x02
+TVIF_PARAM :: 0x04
+TVIF_STATE :: 0x08
+TVIF_HANDLE :: 0x10
+TVIF_SELECTEDIMAGE :: 0x20
+TVIF_CHILDREN :: 0x40
+TVIF_INTEGRAL :: 0x80
+
+TVIS_SELECTED :: 0x02
+TVIS_CUT :: 0x04
+TVIS_DROPHILITED :: 0x08
+TVIS_BOLD :: 0x10
+TVIS_EXPANDED :: 0x20
+TVIS_EXPANDEDONCE :: 0x40
+TVIS_EXPANDPARTIAL :: 0x80
+
+TVIS_OVERLAYMASK :: 0x0F00
+TVIS_STATEIMAGEMASK :: 0xF000
+TVIS_USERMASK :: 0xF000
+
+I_CHILDRENCALLBACK :: (-1)
+
+TVI_ROOT :: cast(HTREEITEM)~uintptr(0x10000 - 1)
+TVI_FIRST :: cast(HTREEITEM)~uintptr(0x0FFFF - 1)
+TVI_LAST :: cast(HTREEITEM)~uintptr(0x0FFFE - 1)
+TVI_SORT :: cast(HTREEITEM)~uintptr(0x0FFFD - 1)
+
+TVN_SELCHANGINGA :: (TVN_FIRST-1)
+TVN_SELCHANGINGW :: (TVN_FIRST-50)
+TVN_SELCHANGEDA :: (TVN_FIRST-2)
+TVN_SELCHANGEDW :: (TVN_FIRST-51)
+TVN_GETDISPINFOA :: (TVN_FIRST-3)
+TVN_GETDISPINFOW :: (TVN_FIRST-52)
+TVN_SETDISPINFOA :: (TVN_FIRST-4)
+TVN_SETDISPINFOW :: (TVN_FIRST-53)
+TVN_ITEMEXPANDINGA :: (TVN_FIRST-5)
+TVN_ITEMEXPANDINGW :: (TVN_FIRST-54)
+TVN_ITEMEXPANDEDA :: (TVN_FIRST-6)
+TVN_ITEMEXPANDEDW :: (TVN_FIRST-55)
+TVN_BEGINDRAGA :: (TVN_FIRST-7)
+TVN_BEGINDRAGW :: (TVN_FIRST-56)
+TVN_BEGINRDRAGA :: (TVN_FIRST-8)
+TVN_BEGINRDRAGW :: (TVN_FIRST-57)
+TVN_DELETEITEMA :: (TVN_FIRST-9)
+TVN_DELETEITEMW :: (TVN_FIRST-58)
+TVN_BEGINLABELEDITA :: (TVN_FIRST-10)
+TVN_BEGINLABELEDITW :: (TVN_FIRST-59)
+TVN_ENDLABELEDITA :: (TVN_FIRST-11)
+TVN_ENDLABELEDITW :: (TVN_FIRST-60)
+TVN_KEYDOWN :: (TVN_FIRST-12)
+TVN_GETINFOTIPA :: (TVN_FIRST-13)
+TVN_GETINFOTIPW :: (TVN_FIRST-14)
+TVN_SINGLEEXPAND :: (TVN_FIRST-15)
+
+TVC_UNKNOWN :: 0x0
+TVC_BYMOUSE :: 0x1
+TVC_BYKEYBOARD :: 0x2
+
+TVIF_DI_SETITEM :: 0x1000
+
+TVNRET_DEFAULT :: 0
+TVNRET_SKIPOLD :: 1
+TVNRET_SKIPNEW :: 2
+
+TVCDRF_NOIMAGES :: 0x10000
+
+TVITEMW :: struct {
+ mask: UINT,
+ hItem: HTREEITEM,
+ state: UINT,
+ stateMask: UINT,
+ pszText: LPWSTR,
+ cchTextMax: c_int,
+ iImage: c_int,
+ iSelectedImage: c_int,
+ cChildren: c_int,
+ lParam: LPARAM,
+}
+TV_ITEMW :: TVITEMW
+LPTVITEMW :: ^TVITEMW
+LPTV_ITEMW :: LPTVITEMW
+
+TVITEMEXW :: struct {
+ mask: UINT,
+ hItem: HTREEITEM,
+ state: UINT,
+ stateMask: UINT,
+ pszText: LPWSTR,
+ cchTextMax: c_int,
+ iImage: c_int,
+ iSelectedImage: c_int,
+ cChildren: c_int,
+ lParam: LPARAM,
+ iIntegral: c_int,
+}
+TV_ITEMEXW :: TVITEMEXW
+LPTVITEMEXW :: ^TVITEMEXW
+LPTV_ITEMEXW :: LPTVITEMEXW
+
+TVINSERTSTRUCTW :: struct {
+ hParent: HTREEITEM,
+ hInsertAfter: HTREEITEM,
+ _: struct #raw_union {
+ itemex: TVITEMEXW,
+ item: TV_ITEMW,
+ },
+}
+TV_INSERTSTRUCTW :: TVINSERTSTRUCTW
+LPTVINSERTSTRUCTW :: ^TVINSERTSTRUCTW
+LPTV_INSERTSTRUCTW :: LPTVINSERTSTRUCTW
+
+TVHITTESTINFO :: struct {
+ pt: POINT,
+ flags: UINT,
+ hItem: HTREEITEM,
+}
+TV_HITTESTINFO :: TVHITTESTINFO
+LPTVHITTESTINFO :: ^TVHITTESTINFO
+LPTV_HITTESTINFO :: LPTVHITTESTINFO
+
+TVSORTCB :: struct {
+ hParent: HTREEITEM,
+ lpfnCompare: PFNTVCOMPARE,
+ lParam: LPARAM,
+}
+TV_SORTCB :: TVSORTCB
+LPTVSORTCB :: ^TVSORTCB
+LPTV_SORTCB :: LPTVSORTCB
+
+NMTREEVIEWW :: struct {
+ hdr: NMHDR,
+ action: UINT,
+ itemOld: TVITEMW,
+ itemNew: TVITEMW,
+ ptDrag: POINT,
+}
+NM_TREEVIEWW :: NMTREEVIEWW
+LPNMTREEVIEWW :: ^NMTREEVIEWW
+LPNM_TREEVIEWW :: LPNMTREEVIEWW
+
+NMTVDISPINFOW :: struct {
+ hdr: NMHDR,
+ item: TVITEMW,
+}
+TV_DISPINFOW :: NMTVDISPINFOW
+LPNMTVDISPINFOW :: ^NMTVDISPINFOW
+
+NMTVDISPINFOEXW :: struct {
+ hdr: NMHDR,
+ item: TVITEMEXW,
+}
+TV_DISPINFOEXW :: NMTVDISPINFOEXW
+LPNMTVDISPINFOEXW :: ^NMTVDISPINFOEXW
+
+NMTVKEYDOWN :: struct #packed {
+ hdr: NMHDR,
+ wVKey: WORD,
+ flags: UINT,
+}
+TV_KEYDOWN :: NMTVKEYDOWN
+LPNMTVKEYDOWN :: ^NMTVKEYDOWN
+
+NMTVCUSTOMDRAW :: struct {
+ nmcd: NMCUSTOMDRAW,
+ clrText: COLORREF,
+ clrTextBk: COLORREF,
+ iLevel: c_int,
+}
+LPNMTVCUSTOMDRAW :: ^NMTVCUSTOMDRAW
+
+NMTVGETINFOTIPW :: struct {
+ hdr: NMHDR,
+ pszText: LPWSTR,
+ cchTextMax: c_int,
+ hItem: HTREEITEM,
+ lParam: LPARAM,
+}
+TV_GETINFOTIPW :: NMTVGETINFOTIPW
+LPNMTVGETINFOTIPW :: ^NMTVGETINFOTIPW
+
+PFNTVCOMPARE :: #type proc "system" (lParam1,lParam2: LPARAM, lParamSort: LPARAM) -> c_int
+
+TreeView_InsertItem :: #force_inline proc "system" (hwnd: HWND, lpis: LPTV_INSERTSTRUCTW) -> HTREEITEM {
+ return cast(HTREEITEM)uintptr(SendMessageW(hwnd, TVM_INSERTITEMW, 0, cast(LPARAM)uintptr(lpis)))
+}
+TreeView_DeleteItem :: #force_inline proc "system" (hwnd: HWND, hitem: HTREEITEM) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, TVM_DELETEITEM, 0, cast(LPARAM)uintptr(hitem))
+}
+TreeView_DeleteAllItems :: #force_inline proc "system" (hwnd: HWND) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, TVM_DELETEITEM, 0, cast(LPARAM)transmute(uintptr)(TVI_ROOT))
+}
+TreeView_Expand :: #force_inline proc "system" (hwnd: HWND, hitem: HTREEITEM, code: UINT) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, TVM_EXPAND, cast(WPARAM)code, cast(LPARAM)uintptr(hitem))
+}
+TreeView_GetItemRect :: #force_inline proc "system" (hwnd: HWND, hitem: HTREEITEM, prc: ^RECT, code: UINT) -> BOOL {
+ alias: struct #raw_union {
+ rc: ^RECT,
+ hitem: ^HTREEITEM,
+ }
+
+ alias.rc = prc
+ alias.hitem^ = hitem
+
+ return cast(BOOL)SendMessageW(hwnd, TVM_GETITEMRECT, cast(WPARAM)code, cast(LPARAM)uintptr(prc))
+}
+TreeView_GetCount :: #force_inline proc "system" (hwnd: HWND) -> UINT {
+ return cast(UINT)SendMessageW(hwnd, TVM_GETCOUNT, 0, 0)
+}
+TreeView_GetIndent :: #force_inline proc "system" (hwnd: HWND) -> UINT {
+ return cast(UINT)SendMessageW(hwnd, TVM_GETINDENT, 0, 0)
+}
+TreeView_SetIndent :: #force_inline proc "system" (hwnd: HWND, indent: UINT) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, TVM_SETINDENT, cast(WPARAM)indent, 0)
+}
+TreeView_GetImageList :: #force_inline proc "system" (hwnd: HWND, iImage: INT) -> HIMAGELIST {
+ return cast(HIMAGELIST)uintptr(SendMessageW(hwnd, TVM_GETIMAGELIST, cast(WPARAM)iImage, 0))
+}
+TreeView_SetImageList :: #force_inline proc "system" (hwnd: HWND, himl: HIMAGELIST, iImage: INT) -> HIMAGELIST {
+ return cast(HIMAGELIST)uintptr(SendMessageW(hwnd, TVM_SETIMAGELIST, cast(WPARAM)iImage, cast(LPARAM)uintptr(himl)))
+}
+TreeView_GetNextItem :: #force_inline proc "system" (hwnd: HWND, hitem: HTREEITEM, code: UINT) -> HTREEITEM {
+ return cast(HTREEITEM)uintptr(SendMessageW(hwnd, TVM_GETNEXTITEM, cast(WPARAM)code, cast(LPARAM)uintptr(hitem)))
+}
+TreeView_GetChild :: #force_inline proc "system" (hwnd: HWND, hitem: HTREEITEM) -> HTREEITEM {
+ return TreeView_GetNextItem(hwnd, hitem, TVGN_CHILD)
+}
+TreeView_GetNextSibling :: #force_inline proc "system" (hwnd: HWND, hitem: HTREEITEM) -> HTREEITEM {
+ return TreeView_GetNextItem(hwnd, hitem, TVGN_NEXT)
+}
+TreeView_GetPrevSibling :: #force_inline proc "system" (hwnd: HWND, hitem: HTREEITEM) -> HTREEITEM {
+ return TreeView_GetNextItem(hwnd, hitem, TVGN_PREVIOUS)
+}
+TreeView_GetParent :: #force_inline proc "system" (hwnd: HWND, hitem: HTREEITEM) -> HTREEITEM {
+ return TreeView_GetNextItem(hwnd, hitem, TVGN_PARENT)
+}
+TreeView_GetFirstVisible :: #force_inline proc "system" (hwnd: HWND) -> HTREEITEM {
+ return TreeView_GetNextItem(hwnd, nil, TVGN_FIRSTVISIBLE)
+}
+TreeView_GetNextVisible :: #force_inline proc "system" (hwnd: HWND, hitem: HTREEITEM) -> HTREEITEM {
+ return TreeView_GetNextItem(hwnd, hitem, TVGN_NEXTVISIBLE)
+}
+TreeView_GetPrevVisible :: #force_inline proc "system" (hwnd: HWND, hitem: HTREEITEM) -> HTREEITEM {
+ return TreeView_GetNextItem(hwnd, hitem, TVGN_PREVIOUSVISIBLE)
+}
+TreeView_GetSelection :: #force_inline proc "system" (hwnd: HWND) -> HTREEITEM {
+ return TreeView_GetNextItem(hwnd, nil, TVGN_CARET)
+}
+TreeView_GetDropHilight :: #force_inline proc "system" (hwnd: HWND) -> HTREEITEM {
+ return TreeView_GetNextItem(hwnd, nil, TVGN_DROPHILITE)
+}
+TreeView_GetRoot :: #force_inline proc "system" (hwnd: HWND) -> HTREEITEM {
+ return TreeView_GetNextItem(hwnd, nil, TVGN_ROOT)
+}
+TreeView_GetLastVisible :: #force_inline proc "system" (hwnd: HWND) -> HTREEITEM {
+ return TreeView_GetNextItem(hwnd, nil, TVGN_LASTVISIBLE)
+}
+TreeView_Select :: #force_inline proc "system" (hwnd: HWND, hitem: HTREEITEM, code: UINT) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, TVM_SELECTITEM, cast(WPARAM)code, cast(LPARAM)uintptr(hitem))
+}
+TreeView_SelectItem :: #force_inline proc "system" (hwnd: HWND, hitem: HTREEITEM) -> BOOL {
+ return TreeView_Select(hwnd, hitem, TVGN_CARET)
+}
+TreeView_SelectDropTarget :: #force_inline proc "system" (hwnd: HWND, hitem: HTREEITEM) -> BOOL {
+ return TreeView_Select(hwnd, hitem, TVGN_DROPHILITE)
+}
+TreeView_SelectSetFirstVisible :: #force_inline proc "system" (hwnd: HWND, hitem: HTREEITEM) -> BOOL {
+ return TreeView_Select(hwnd, hitem, TVGN_FIRSTVISIBLE)
+}
+TreeView_GetItem :: #force_inline proc "system" (hwnd: HWND, pitem: ^TV_ITEMW) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, TVM_GETITEMW, 0, cast(LPARAM)uintptr(pitem))
+}
+TreeView_SetItem :: #force_inline proc "system" (hwnd: HWND, pitem: ^TV_ITEMW) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, TVM_SETITEMW, 0, cast(LPARAM)uintptr(pitem))
+}
+TreeView_EditLabel :: #force_inline proc "system" (hwnd: HWND, hitem: HTREEITEM) -> HWND {
+ return cast(HWND)uintptr(SendMessageW(hwnd, TVM_EDITLABELW, 0, cast(LPARAM)uintptr(hitem)))
+}
+TreeView_GetEditControl :: #force_inline proc "system" (hwnd: HWND) -> HWND {
+ return cast(HWND)uintptr(SendMessageW(hwnd, TVM_GETEDITCONTROL, 0, 0))
+}
+TreeView_GetVisibleCount :: #force_inline proc "system" (hwnd: HWND) -> UINT {
+ return cast(UINT)SendMessageW(hwnd, TVM_GETVISIBLECOUNT, 0, 0)
+}
+TreeView_HitTest :: #force_inline proc "system" (hwnd: HWND, lpht: LPTV_HITTESTINFO) -> HTREEITEM {
+ return cast(HTREEITEM)uintptr(SendMessageW(hwnd, TVM_HITTEST, 0, cast(LPARAM)uintptr(lpht)))
+}
+TreeView_CreateDragImage :: #force_inline proc "system" (hwnd: HWND, hitem: HTREEITEM) -> HIMAGELIST {
+ return cast(HIMAGELIST)uintptr(SendMessageW(hwnd, TVM_CREATEDRAGIMAGE, 0, cast(LPARAM)uintptr(hitem)))
+}
+TreeView_SortChildren :: #force_inline proc "system" (hwnd: HWND, hitem: HTREEITEM, recurse: BOOL) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, TVM_SORTCHILDREN, cast(WPARAM)recurse, cast(LPARAM)uintptr(hitem))
+}
+TreeView_EnsureVisible :: #force_inline proc "system" (hwnd: HWND, hitem: HTREEITEM) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, TVM_ENSUREVISIBLE, 0, cast(LPARAM)uintptr(hitem))
+}
+TreeView_SortChildrenCB :: #force_inline proc "system" (hwnd: HWND, psort: LPTVSORTCB, recurse: BOOL) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, TVM_SORTCHILDRENCB, cast(WPARAM)recurse, cast(LPARAM)uintptr(psort))
+}
+TreeView_EndEditLabelNow :: #force_inline proc "system" (hwnd: HWND, fCancel: BOOL) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, TVM_ENDEDITLABELNOW, cast(WPARAM)fCancel, 0)
+}
+TreeView_SetToolTips :: #force_inline proc "system" (hwnd: HWND, hwndTT: HWND) -> HWND {
+ return cast(HWND)uintptr(SendMessageW(hwnd, TVM_SETTOOLTIPS, uintptr(hwndTT), 0))
+}
+TreeView_GetToolTips :: #force_inline proc "system" (hwnd: HWND) -> HWND {
+ return cast(HWND)uintptr(SendMessageW(hwnd, TVM_GETTOOLTIPS, 0, 0))
+}
+TreeView_GetISearchString :: #force_inline proc "system" (hwnd: HWND, lpsz: LPWSTR) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, TVM_GETISEARCHSTRINGW, 0, cast(LPARAM)uintptr(lpsz))
+}
+TreeView_SetInsertMark :: #force_inline proc "system" (hwnd: HWND, hItem: HTREEITEM, fAfter: BOOL) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, TVM_SETINSERTMARK, cast(WPARAM)fAfter, cast(LPARAM)uintptr(hItem))
+}
+TreeView_SetUnicodeFormat :: #force_inline proc "system" (hwnd: HWND, fUnicode: BOOL) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, TVM_SETUNICODEFORMAT, cast(WPARAM)fUnicode, 0)
+}
+TreeView_GetUnicodeFormat :: #force_inline proc "system" (hwnd: HWND) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, TVM_GETUNICODEFORMAT, 0, 0)
+}
+TreeView_SetItemHeight :: #force_inline proc "system" (hwnd: HWND, iHeight: c_int) -> c_int {
+ return cast(c_int)SendMessageW(hwnd, TVM_SETITEMHEIGHT, cast(WPARAM)iHeight, 0)
+}
+TreeView_GetItemHeight :: #force_inline proc "system" (hwnd: HWND) -> c_int {
+ return cast(c_int)SendMessageW(hwnd, TVM_GETITEMHEIGHT, 0, 0)
+}
+TreeView_SetBkColor :: #force_inline proc "system" (hwnd: HWND, clr: COLORREF) -> COLORREF {
+ return cast(COLORREF)SendMessageW(hwnd, TVM_SETBKCOLOR, 0, cast(LPARAM)clr)
+}
+TreeView_SetTextColor :: #force_inline proc "system" (hwnd: HWND, clr: COLORREF) -> COLORREF {
+ return cast(COLORREF)SendMessageW(hwnd, TVM_SETTEXTCOLOR, 0, cast(LPARAM)clr)
+}
+TreeView_GetBkColor :: #force_inline proc "system" (hwnd: HWND) -> COLORREF {
+ return cast(COLORREF)SendMessageW(hwnd, TVM_GETBKCOLOR, 0, 0)
+}
+TreeView_GetTextColor :: #force_inline proc "system" (hwnd: HWND) -> COLORREF {
+ return cast(COLORREF)SendMessageW(hwnd, TVM_GETTEXTCOLOR, 0, 0)
+}
+TreeView_SetScrollTime :: #force_inline proc "system" (hwnd: HWND, uTime: UINT) -> UINT {
+ return cast(UINT)SendMessageW(hwnd, TVM_SETSCROLLTIME, cast(WPARAM)uTime, 0)
+}
+TreeView_GetScrollTime :: #force_inline proc "system" (hwnd: HWND) -> UINT {
+ return cast(UINT)SendMessageW(hwnd, TVM_GETSCROLLTIME, 0, 0)
+}
+TreeView_SetInsertMarkColor :: #force_inline proc "system" (hwnd: HWND, clr: COLORREF) -> COLORREF {
+ return cast(COLORREF)SendMessageW(hwnd, TVM_SETINSERTMARKCOLOR, 0, cast(LPARAM)clr)
+}
+TreeView_GetInsertMarkColor :: #force_inline proc "system" (hwnd: HWND) -> COLORREF {
+ return cast(COLORREF)SendMessageW(hwnd, TVM_GETINSERTMARKCOLOR, 0, 0)
+}
+TreeView_SetItemState :: #force_inline proc "system" (hwndTV: HWND, hti: HTREEITEM, data: UINT, mask: UINT) {
+ item := TVITEMW {
+ mask = TVIF_STATE,
+ hItem = hti,
+ stateMask = mask,
+ state = data,
+ }
+ SendMessageW(hwndTV, TVM_SETITEMW, 0, cast(LPARAM)uintptr(&item))
+}
+TreeView_SetCheckState :: #force_inline proc "system" (hwndTV: HWND, hti: HTREEITEM, fCheck: BOOL) {
+ TreeView_SetItemState(hwndTV, hti, INDEXTOSTATEIMAGEMASK(2 if fCheck else 1), TVIS_STATEIMAGEMASK)
+}
+TreeView_GetItemState :: #force_inline proc "system" (hwndTV: HWND, hti: HTREEITEM, mask: UINT) -> UINT {
+ return cast(UINT)SendMessageW(hwndTV, TVM_GETITEMSTATE, uintptr(hti), cast(LPARAM)mask)
+}
+TreeView_GetCheckState :: #force_inline proc "system" (hwndTV: HWND, hti: HTREEITEM) -> UINT {
+ return ((cast(UINT)SendMessageW(hwndTV, TVM_GETITEMSTATE, uintptr(hti), cast(LPARAM)TVIS_STATEIMAGEMASK)) >> 12) - 1
+}
+TreeView_SetLineColor :: #force_inline proc "system" (hwnd: HWND, clr: COLORREF) -> COLORREF {
+ return cast(COLORREF)SendMessageW(hwnd, TVM_SETLINECOLOR, 0, cast(LPARAM)clr)
+}
+TreeView_GetLineColor :: #force_inline proc "system" (hwnd: HWND) -> COLORREF {
+ return cast(COLORREF)SendMessageW(hwnd, TVM_GETLINECOLOR, 0, 0)
+}
+TreeView_MapAccIDToHTREEITEM :: #force_inline proc "system" (hwnd: HWND, id: UINT) -> HTREEITEM {
+ return cast(HTREEITEM)uintptr(SendMessageW(hwnd, TVM_MAPACCIDTOHTREEITEM, cast(WPARAM)id, 0))
+}
+TreeView_MapHTREEITEMToAccID :: #force_inline proc "system" (hwnd: HWND, htreeitem: HTREEITEM) -> UINT {
+ return cast(UINT)SendMessageW(hwnd, TVM_MAPHTREEITEMTOACCID, uintptr(htreeitem), 0)
+}
+
+// Combo Box Ex Control
+CBEIF_TEXT :: 0x01
+CBEIF_IMAGE :: 0x02
+CBEIF_SELECTEDIMAGE :: 0x04
+CBEIF_OVERLAY :: 0x08
+CBEIF_INDENT :: 0x10
+CBEIF_LPARAM :: 0x20
+
+CBEIF_DI_SETITEM :: 0x10000000
+
+CBES_EX_NOEDITIMAGE :: 0x01
+CBES_EX_NOEDITIMAGEINDENT :: 0x02
+CBES_EX_PATHWORDBREAKPROC :: 0x04
+CBES_EX_NOSIZELIMIT :: 0x08
+CBES_EX_CASESENSITIVE :: 0x10
+
+CBEN_GETDISPINFOA :: (CBEN_FIRST - 0)
+CBEN_INSERTITEM :: (CBEN_FIRST - 1)
+CBEN_DELETEITEM :: (CBEN_FIRST - 2)
+CBEN_BEGINEDIT :: (CBEN_FIRST - 4)
+CBEN_ENDEDITA :: (CBEN_FIRST - 5)
+CBEN_ENDEDITW :: (CBEN_FIRST - 6)
+CBEN_GETDISPINFOW :: (CBEN_FIRST - 7)
+CBEN_DRAGBEGINA :: (CBEN_FIRST - 8)
+CBEN_DRAGBEGINW :: (CBEN_FIRST - 9)
+
+CBENF_KILLFOCUS :: 1
+CBENF_RETURN :: 2
+CBENF_ESCAPE :: 3
+CBENF_DROPDOWN :: 4
+
+CBEMAXSTRLEN :: 260
+
+COMBOBOXEXITEMW :: struct {
+ mask: UINT,
+ iItem: INT_PTR,
+ pszText: LPWSTR,
+ cchTextMax: c_int,
+ iImage: c_int,
+ iSelectedImage: c_int,
+ iOverlay: c_int,
+ iIndent: c_int,
+ lParam: LPARAM,
+}
+PCOMBOBOXEXITEMW :: ^COMBOBOXEXITEMW
+PCCOMBOBOXEXITEMW :: ^COMBOBOXEXITEMW
+
+NMCOMBOBOXEXW :: struct {
+ hdr: NMHDR,
+ ceItem: COMBOBOXEXITEMW,
+}
+PNMCOMBOBOXEXW :: ^NMCOMBOBOXEXW
+
+NMCBEDRAGBEGINW :: struct {
+ hdr: NMHDR,
+ iItemId: c_int,
+ szText: [CBEMAXSTRLEN]WCHAR,
+}
+PNMCBEDRAGBEGINW :: ^NMCBEDRAGBEGINW
+LPNMCBEDRAGBEGINW :: PNMCBEDRAGBEGINW
+
+NMCBEENDEDITW :: struct {
+ hdr: NMHDR,
+ fChanged: BOOL,
+ iNewSelection: c_int,
+ szText: [CBEMAXSTRLEN]WCHAR,
+ iWhy: c_int,
+}
+PNMCBEENDEDITW :: ^NMCBEENDEDITW
+LPNMCBEENDEDITW :: PNMCBEENDEDITW
+
+// Tab Control
+TCS_EX_FLATSEPARATORS :: 0x1
+TCS_EX_REGISTERDROP :: 0x2
+
+TCN_KEYDOWN :: TCN_FIRST - 0
+TCN_SELCHANGE :: TCN_FIRST - 1
+TCN_SELCHANGING :: TCN_FIRST - 2
+TCN_GETOBJECT :: TCN_FIRST - 3
+TCN_FOCUSCHANGE :: TCN_FIRST - 4
+
+TCITEMHEADERW :: struct {
+ mask: UINT,
+ lpReserved1: UINT,
+ lpReserved2: UINT,
+ pszText: LPWSTR,
+ cchTextMax: c_int,
+ iImage: c_int,
+}
+TC_ITEMHEADERW :: TCITEMHEADERW
+LPTCITEMHEADERW :: ^TCITEMHEADERW
+LPTC_ITEMHEADERW :: LPTCITEMHEADERW
+
+TCITEMW :: struct {
+ mask: UINT,
+ dwState: DWORD,
+ dwStateMask: DWORD,
+ pszText: LPWSTR,
+ cchTextMax: c_int,
+ iImage: c_int,
+ lParam: LPARAM,
+}
+TC_ITEMW :: TCITEMW
+LPTCITEMW :: ^TCITEMW
+LPTC_ITEMW :: LPTCITEMW
+
+TCHITTESTINFO :: struct {
+ pt: POINT,
+ flags: UINT,
+}
+TC_HITTESTINFO :: TCHITTESTINFO
+LPTCHITTESTINFO :: ^TCHITTESTINFO
+LPTC_HITTESTINFO :: LPTCHITTESTINFO
+
+NMTCKEYDOWN :: struct #packed {
+ hdr: NMHDR,
+ wVKey: WORD,
+ flags: UINT,
+}
+TC_KEYDOWN :: NMTCKEYDOWN
+
+TabCtrl_GetImageList :: #force_inline proc "system" (hwnd: HWND) -> HIMAGELIST {
+ return cast(HIMAGELIST)uintptr(SendMessageW(hwnd, TCM_GETIMAGELIST, 0, 0))
+}
+TabCtrl_SetImageList :: #force_inline proc "system" (hwnd: HWND, himl: HIMAGELIST) -> HIMAGELIST {
+ return cast(HIMAGELIST)uintptr(SendMessageW(hwnd, TCM_SETIMAGELIST, 0, cast(LPARAM)uintptr(himl)))
+}
+TabCtrl_GetItemCount :: #force_inline proc "system" (hwnd: HWND) -> c_int {
+ return cast(c_int)SendMessageW(hwnd, TCM_GETITEMCOUNT, 0, 0)
+}
+TabCtrl_GetItem :: #force_inline proc "system" (hwnd: HWND, iItem: c_int, pitem: ^TC_ITEMW) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, TCM_GETITEMW, cast(WPARAM)iItem, cast(LPARAM)uintptr(pitem))
+}
+TabCtrl_SetItem :: #force_inline proc "system" (hwnd: HWND, iItem: c_int, pitem: ^TC_ITEMW) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, TCM_SETITEMW, cast(WPARAM)iItem, cast(LPARAM)uintptr(pitem))
+}
+TabCtrl_InsertItem :: #force_inline proc "system" (hwnd: HWND, iItem: c_int, pitem: ^TC_ITEMW) -> c_int {
+ return cast(c_int)SendMessageW(hwnd, TCM_INSERTITEMW, cast(WPARAM)iItem, cast(LPARAM)uintptr(pitem))
+}
+TabCtrl_DeleteItem :: #force_inline proc "system" (hwnd: HWND, i: c_int) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, TCM_DELETEITEM, cast(WPARAM)i, 0)
+}
+TabCtrl_DeleteAllItems :: #force_inline proc "system" (hwnd: HWND) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, TCM_DELETEALLITEMS, 0, 0)
+}
+TabCtrl_GetItemRect :: #force_inline proc "system" (hwnd: HWND, i: c_int, prc: ^RECT) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, TCM_GETITEMRECT, cast(WPARAM)i, cast(LPARAM)uintptr(prc))
+}
+TabCtrl_GetCurSel :: #force_inline proc "system" (hwnd: HWND) -> c_int {
+ return cast(c_int)SendMessageW(hwnd, TCM_GETCURSEL, 0, 0)
+}
+TabCtrl_SetCurSel :: #force_inline proc "system" (hwnd: HWND, i: c_int) -> c_int {
+ return cast(c_int)SendMessageW(hwnd, TCM_SETCURSEL, cast(WPARAM)i, 0)
+}
+TabCtrl_HitTest :: #force_inline proc "system" (hwndTC: HWND, pinfo: ^TC_HITTESTINFO) -> c_int {
+ return cast(c_int)SendMessageW(hwndTC, TCM_HITTEST, 0, cast(LPARAM)uintptr(pinfo))
+}
+TabCtrl_SetItemExtra :: #force_inline proc "system" (hwndTC: HWND, cb: c_int) -> BOOL {
+ return cast(BOOL)SendMessageW(hwndTC, TCM_SETITEMEXTRA, cast(WPARAM)cb, 0)
+}
+TabCtrl_AdjustRect :: #force_inline proc "system" (hwnd: HWND, bLarger: BOOL, prc: ^RECT) -> c_int {
+ return cast(c_int)SendMessageW(hwnd, TCM_ADJUSTRECT, cast(WPARAM)bLarger, cast(LPARAM)uintptr(prc))
+}
+TabCtrl_SetItemSize :: #force_inline proc "system" (hwnd: HWND, x,y: c_int) -> DWORD {
+ return cast(DWORD)SendMessageW(hwnd, TCM_SETITEMSIZE, 0, MAKELPARAM(x,y))
+}
+TabCtrl_RemoveImage :: #force_inline proc "system" (hwnd: HWND, i: c_int) {
+ SendMessageW(hwnd, TCM_REMOVEIMAGE, cast(WPARAM)i, 0)
+}
+TabCtrl_SetPadding :: #force_inline proc "system" (hwnd: HWND, cx,cy: c_int) {
+ SendMessageW(hwnd, TCM_SETPADDING, 0, MAKELPARAM(cx,cy))
+}
+TabCtrl_GetRowCount :: #force_inline proc "system" (hwnd: HWND) -> c_int {
+ return cast(c_int)SendMessageW(hwnd, TCM_GETROWCOUNT, 0, 0)
+}
+TabCtrl_GetToolTips :: #force_inline proc "system" (hwnd: HWND) -> HWND {
+ return cast(HWND)uintptr(SendMessageW(hwnd, TCM_GETTOOLTIPS, 0, 0))
+}
+TabCtrl_SetToolTips :: #force_inline proc "system" (hwnd: HWND, hwndTT: HWND) {
+ SendMessageW(hwnd, TCM_SETTOOLTIPS, uintptr(hwndTT), 0)
+}
+TabCtrl_GetCurFocus :: #force_inline proc "system" (hwnd: HWND) -> c_int {
+ return cast(c_int)SendMessageW(hwnd, TCM_GETCURFOCUS, 0, 0)
+}
+TabCtrl_SetCurFocus :: #force_inline proc "system" (hwnd: HWND, i: c_int) {
+ SendMessageW(hwnd, TCM_SETCURFOCUS, cast(WPARAM)i, 0)
+}
+TabCtrl_SetMinTabWidth :: #force_inline proc "system" (hwnd: HWND, x: c_int) -> c_int {
+ return cast(c_int)SendMessageW(hwnd, TCM_SETMINTABWIDTH, 0, cast(LPARAM)x)
+}
+TabCtrl_DeselectAll :: #force_inline proc "system" (hwnd: HWND, fExcludeFocus: BOOL) {
+ SendMessageW(hwnd, TCM_DESELECTALL, cast(WPARAM)fExcludeFocus, 0)
+}
+TabCtrl_HighlightItem :: #force_inline proc "system" (hwnd: HWND, i: c_int, fHighlight: BOOL) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, TCM_HIGHLIGHTITEM, cast(WPARAM)i, cast(LPARAM)MAKELONG(fHighlight,0))
+}
+TabCtrl_SetExtendedStyle :: #force_inline proc "system" (hwnd: HWND, dw: DWORD) -> DWORD {
+ return cast(DWORD)SendMessageW(hwnd, TCM_SETEXTENDEDSTYLE, 0, cast(LPARAM)dw)
+}
+TabCtrl_GetExtendedStyle :: #force_inline proc "system" (hwnd: HWND) -> DWORD {
+ return cast(DWORD)SendMessageW(hwnd, TCM_GETEXTENDEDSTYLE, 0, 0)
+}
+TabCtrl_SetUnicodeFormat :: #force_inline proc "system" (hwnd: HWND, fUnicode: BOOL) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, TCM_SETUNICODEFORMAT, cast(WPARAM)fUnicode, 0)
+}
+TabCtrl_GetUnicodeFormat :: #force_inline proc "system" (hwnd: HWND) -> BOOL {
+ return cast(BOOL)SendMessageW(hwnd, TCM_GETUNICODEFORMAT, 0, 0)
}
diff --git a/core/sys/windows/dbghelp.odin b/core/sys/windows/dbghelp.odin
index 336992b4a..e32b4c874 100644
--- a/core/sys/windows/dbghelp.odin
+++ b/core/sys/windows/dbghelp.odin
@@ -15,7 +15,7 @@ MINIDUMP_DIRECTORY :: struct {
Location: MINIDUMP_LOCATION_DESCRIPTOR,
}
-MINIDUMP_EXCEPTION_INFORMATION :: struct {
+MINIDUMP_EXCEPTION_INFORMATION :: struct #max_field_align(4) {
ThreadId: DWORD,
ExceptionPointers: ^EXCEPTION_POINTERS,
ClientPointers: BOOL,
diff --git a/core/sys/windows/dnsapi.odin b/core/sys/windows/dnsapi.odin
index 4fd9f7a19..728813696 100644
--- a/core/sys/windows/dnsapi.odin
+++ b/core/sys/windows/dnsapi.odin
@@ -5,6 +5,6 @@ foreign import "system:Dnsapi.lib"
@(default_calling_convention="system")
foreign Dnsapi {
- DnsQuery_UTF8 :: proc(name: cstring, type: u16, options: DWORD, extra: PVOID, results: ^^DNS_RECORD, reserved: PVOID) -> DNS_STATUS ---
+ DnsQuery_UTF8 :: proc(name: cstring, type: u16, options: DNS_QUERY_OPTIONS, extra: PVOID, results: ^^DNS_RECORD, reserved: PVOID) -> DNS_STATUS ---
DnsRecordListFree :: proc(list: ^DNS_RECORD, options: DWORD) ---
}
diff --git a/core/sys/windows/kernel32.odin b/core/sys/windows/kernel32.odin
index 8be50bceb..266dcdbf4 100644
--- a/core/sys/windows/kernel32.odin
+++ b/core/sys/windows/kernel32.odin
@@ -20,6 +20,15 @@ COMMON_LVB_GRID_RVERTICAL :: WORD(0x1000)
COMMON_LVB_REVERSE_VIDEO :: WORD(0x4000)
COMMON_LVB_UNDERSCORE :: WORD(0x8000)
COMMON_LVB_SBCSDBCS :: WORD(0x0300)
+EV_BREAK :: DWORD(0x0040)
+EV_CTS :: DWORD(0x0008)
+EV_DSR :: DWORD(0x0010)
+EV_ERR :: DWORD(0x0080)
+EV_RING :: DWORD(0x0100)
+EV_RLSD :: DWORD(0x0020)
+EV_RXCHAR :: DWORD(0x0001)
+EV_RXFLAG :: DWORD(0x0002)
+EV_TXEMPTY :: DWORD(0x0004)
@(default_calling_convention="system")
foreign kernel32 {
@@ -109,9 +118,12 @@ foreign kernel32 {
ClearCommError :: proc(hFile: HANDLE, lpErrors: ^Com_Error, lpStat: ^COMSTAT) -> BOOL ---
GetCommState :: proc(handle: HANDLE, dcb: ^DCB) -> BOOL ---
SetCommState :: proc(handle: HANDLE, dcb: ^DCB) -> BOOL ---
- GetCommPorts :: proc(lpPortNumbers: PULONG, uPortNumbersCount: ULONG, puPortNumbersFound: PULONG) -> ULONG ---
+ SetCommMask :: proc(handle: HANDLE, dwEvtMap: DWORD) -> BOOL ---
+ GetCommMask :: proc(handle: HANDLE, lpEvtMask: LPDWORD) -> BOOL ---
+ WaitCommEvent :: proc(handle: HANDLE, lpEvtMask: LPDWORD, lpOverlapped: LPOVERLAPPED) -> BOOL ---
GetCommandLineW :: proc() -> LPCWSTR ---
GetTempPathW :: proc(nBufferLength: DWORD, lpBuffer: LPCWSTR) -> DWORD ---
+ GetTempFileNameW :: proc(lpPathName: LPCWSTR, lpPrefixString: LPCWSTR, uUnique: c_int, lpTempFileName: LPWSTR) -> c_uint ---
GetCurrentProcess :: proc() -> HANDLE ---
GetCurrentProcessId :: proc() -> DWORD ---
GetCurrentThread :: proc() -> HANDLE ---
@@ -239,6 +251,10 @@ foreign kernel32 {
hThread: HANDLE,
lpContext: LPCONTEXT,
) -> BOOL ---
+ SetThreadContext :: proc(
+ hThread: HANDLE,
+ lpContext: LPCONTEXT,
+ ) -> BOOL ---
CreateProcessW :: proc(
lpApplicationName: LPCWSTR,
lpCommandLine: LPWSTR,
@@ -1068,6 +1084,11 @@ foreign one_core {
PageProtection: ULONG,
PreferredNode: ULONG,
) -> PVOID ---
+ GetCommPorts :: proc(
+ lpPortNumbers: PULONG,
+ uPortNumbersCount: ULONG,
+ puPortNumbersFound: PULONG,
+ ) -> ULONG ---
}
@@ -1220,3 +1241,31 @@ GHND :: (GMEM_MOVEABLE | GMEM_ZEROINIT)
GPTR :: (GMEM_FIXED | GMEM_ZEROINIT)
LPTOP_LEVEL_EXCEPTION_FILTER :: PVECTORED_EXCEPTION_HANDLER
+
+ACTCTXW :: struct {
+ Size: ULONG,
+ Flags: DWORD,
+ Source: LPCWSTR,
+ ProcessorArchitecture: USHORT,
+ LangId: LANGID,
+ AssemblyDirectory: LPCWSTR,
+ ResourceName: LPCWSTR,
+ ApplicationName: LPCWSTR,
+ Module: HMODULE,
+}
+PACTCTXW :: ^ACTCTXW
+PCACTCTXW :: ^ACTCTXW
+
+ACTCTX_FLAG_PROCESSOR_ARCHITECTURE_VALID :: 0x001
+ACTCTX_FLAG_LANGID_VALID :: 0x002
+ACTCTX_FLAG_ASSEMBLY_DIRECTORY_VALID :: 0x004
+ACTCTX_FLAG_RESOURCE_NAME_VALID :: 0x008
+ACTCTX_FLAG_SET_PROCESS_DEFAULT :: 0x010
+ACTCTX_FLAG_APPLICATION_NAME_VALID :: 0x020
+ACTCTX_FLAG_HMODULE_VALID :: 0x080
+
+@(default_calling_convention="system")
+foreign kernel32 {
+ CreateActCtxW :: proc(pActCtx: ^ACTCTXW) -> HANDLE ---
+ ActivateActCtx :: proc(hActCtx: HANDLE, lpCookie: ^ULONG_PTR) -> BOOL ---
+}
diff --git a/core/sys/windows/types.odin b/core/sys/windows/types.odin
index 5dcf09eab..f698b63d4 100644
--- a/core/sys/windows/types.odin
+++ b/core/sys/windows/types.odin
@@ -66,6 +66,7 @@ PULONG_PTR :: ^ULONG_PTR
LPULONG_PTR :: ^ULONG_PTR
DWORD_PTR :: ULONG_PTR
LONG_PTR :: int
+INT_PTR :: int
UINT_PTR :: uintptr
ULONG :: c_ulong
ULONGLONG :: c_ulonglong
@@ -143,6 +144,7 @@ LPWSAPROTOCOL_INFO :: ^WSAPROTOCOL_INFO
LPSTR :: ^CHAR
LPWSTR :: ^WCHAR
OLECHAR :: WCHAR
+BSTR :: ^OLECHAR
LPOLESTR :: ^OLECHAR
LPCOLESTR :: LPCSTR
LPFILETIME :: ^FILETIME
@@ -541,6 +543,44 @@ COLOR_3DHIGHLIGHT :: COLOR_BTNHIGHLIGHT
COLOR_3DHILIGHT :: COLOR_BTNHIGHLIGHT
COLOR_BTNHILIGHT :: COLOR_BTNHIGHLIGHT
+// Common Control Notification Code Ranges
+NM_FIRST :: 0
+NM_LAST :: ~DWORD(99 - 1)
+LVN_FIRST :: ~DWORD(100 - 1)
+LVN_LAST :: ~DWORD(199 - 1)
+HDN_FIRST :: ~DWORD(300 - 1)
+HDN_LAST :: ~DWORD(399 - 1)
+TVN_FIRST :: ~DWORD(400 - 1)
+TVN_LAST :: ~DWORD(499 - 1)
+TTN_FIRST :: ~DWORD(520 - 1)
+TTN_LAST :: ~DWORD(549 - 1)
+TCN_FIRST :: ~DWORD(550 - 1)
+TCN_LAST :: ~DWORD(580 - 1)
+CDN_FIRST :: ~DWORD(601 - 1)
+CDN_LAST :: ~DWORD(699 - 1)
+TBN_FIRST :: ~DWORD(700 - 1)
+TBN_LAST :: ~DWORD(720 - 1)
+UDN_FIRST :: ~DWORD(721 - 1)
+UDN_LAST :: ~DWORD(740 - 1)
+MCN_FIRST :: ~DWORD(750 - 1)
+MCN_LAST :: ~DWORD(759 - 1)
+DTN_FIRST :: ~DWORD(760 - 1)
+DTN_LAST :: ~DWORD(799 - 1)
+CBEN_FIRST :: ~DWORD(800 - 1)
+CBEN_LAST :: ~DWORD(830 - 1)
+RBN_FIRST :: ~DWORD(831 - 1)
+RBN_LAST :: ~DWORD(859 - 1)
+IPN_FIRST :: ~DWORD(860 - 1)
+IPN_LAST :: ~DWORD(879 - 1)
+SBN_FIRST :: ~DWORD(880 - 1)
+SBN_LAST :: ~DWORD(899 - 1)
+PGN_FIRST :: ~DWORD(900 - 1)
+PGN_LAST :: ~DWORD(950 - 1)
+WMN_FIRST :: ~DWORD(1000 - 1)
+WMN_LAST :: ~DWORD(1200 - 1)
+BCN_FIRST :: ~DWORD(1250 - 1)
+BCN_LAST :: ~DWORD(1350 - 1)
+
// Combo Box Notification Codes
CBN_ERRSPACE :: -1
CBN_SELCHANGE :: 1
@@ -623,6 +663,10 @@ BST_INDETERMINATE :: 0x0002
BST_PUSHED :: 0x0004
BST_FOCUS :: 0x0008
+// Button Control Notification Codes
+BCN_HOTITEMCHANGE :: (BCN_FIRST + 0x0001)
+BCN_DROPDOWN :: (BCN_FIRST + 0x0002)
+
// Static Control Constants
SS_LEFT :: 0x00000000
SS_CENTER :: 0x00000001
@@ -685,6 +729,416 @@ EN_VSCROLL :: 0x0602
EN_ALIGN_LTR_EC :: 0x0700
EN_ALIGN_RTL_EC :: 0x0701
+// Toolbar Styles
+TBS_AUTOTICKS :: 0x001
+TBS_VERT :: 0x002
+TBS_HORZ :: 0x000
+TBS_TOP :: 0x004
+TBS_BOTTOM :: 0x000
+TBS_LEFT :: 0x004
+TBS_RIGHT :: 0x000
+TBS_BOTH :: 0x008
+TBS_NOTICKS :: 0x010
+TBS_ENABLESELRANGE :: 0x020
+TBS_FIXEDLENGTH :: 0x040
+TBS_NOTHUMB :: 0x080
+TBS_TOOLTIPS :: 0x100
+TBS_REVERSED :: 0x200
+TBS_DOWNISLEFT :: 0x400
+
+// Toolbar Button Styles
+TBSTYLE_BUTTON :: 0x0000
+TBSTYLE_SEP :: 0x0001
+TBSTYLE_CHECK :: 0x0002
+TBSTYLE_GROUP :: 0x0004
+TBSTYLE_CHECKGROUP :: (TBSTYLE_GROUP | TBSTYLE_CHECK)
+TBSTYLE_DROPDOWN :: 0x0008
+TBSTYLE_AUTOSIZE :: 0x0010
+TBSTYLE_NOPREFIX :: 0x0020
+TBSTYLE_TOOLTIPS :: 0x0100
+TBSTYLE_WRAPABLE :: 0x0200
+TBSTYLE_ALTDRAG :: 0x0400
+TBSTYLE_FLAT :: 0x0800
+TBSTYLE_LIST :: 0x1000
+TBSTYLE_CUSTOMERASE :: 0x2000
+TBSTYLE_REGISTERDROP :: 0x4000
+TBSTYLE_TRANSPARENT :: 0x8000
+
+// Toolbar Button Styles (Aliases)
+BTNS_BUTTON :: TBSTYLE_BUTTON
+BTNS_SEP :: TBSTYLE_SEP
+BTNS_CHECK :: TBSTYLE_CHECK
+BTNS_GROUP :: TBSTYLE_GROUP
+BTNS_CHECKGROUP :: TBSTYLE_CHECKGROUP
+BTNS_DROPDOWN :: TBSTYLE_DROPDOWN
+BTNS_AUTOSIZE :: TBSTYLE_AUTOSIZE
+BTNS_NOPREFIX :: TBSTYLE_NOPREFIX
+BTNS_SHOWTEXT :: 0x40
+BTNS_WHOLEDROPDOWN :: 0x80
+
+// Toolbar Extended Styles
+TBSTYLE_EX_DRAWDDARROWS :: 0x01
+TBSTYLE_EX_MIXEDBUTTONS :: 0x08
+TBSTYLE_EX_HIDECLIPPEDBUTTONS :: 0x10
+TBSTYLE_EX_DOUBLEBUFFER :: 0x80
+
+// Toolbar Item State Codes
+TBSTATE_CHECKED :: 0x01
+TBSTATE_PRESSED :: 0x02
+TBSTATE_ENABLED :: 0x04
+TBSTATE_HIDDEN :: 0x08
+TBSTATE_INDETERMINATE :: 0x10
+TBSTATE_WRAP :: 0x20
+TBSTATE_ELLIPSES :: 0x40
+TBSTATE_MARKED :: 0x80
+
+// Toolbar Constants
+TBCDRF_NOEDGES :: 0x010000
+TBCDRF_HILITEHOTTRACK :: 0x020000
+TBCDRF_NOOFFSET :: 0x040000
+TBCDRF_NOMARK :: 0x080000
+TBCDRF_NOETCHEDEFFECT :: 0x100000
+TBCDRF_BLENDICON :: 0x200000
+TBCDRF_NOBACKGROUND :: 0x400000
+
+TBBF_LARGE :: 0x1
+
+TBIF_IMAGE :: 0x00000001
+TBIF_TEXT :: 0x00000002
+TBIF_STATE :: 0x00000004
+TBIF_STYLE :: 0x00000008
+TBIF_LPARAM :: 0x00000010
+TBIF_COMMAND :: 0x00000020
+TBIF_SIZE :: 0x00000040
+TBIF_BYINDEX :: 0x80000000
+
+TBMF_PAD :: 0x1
+TBMF_BARPAD :: 0x2
+TBMF_BUTTONSPACING :: 0x4
+
+IDB_STD_SMALL_COLOR :: 0
+IDB_STD_LARGE_COLOR :: 1
+IDB_VIEW_SMALL_COLOR :: 4
+IDB_VIEW_LARGE_COLOR :: 5
+IDB_HIST_SMALL_COLOR :: 8
+IDB_HIST_LARGE_COLOR :: 9
+
+STD_CUT :: 0
+STD_COPY :: 1
+STD_PASTE :: 2
+STD_UNDO :: 3
+STD_REDOW :: 4
+STD_DELETE :: 5
+STD_FILENEW :: 6
+STD_FILEOPEN :: 7
+STD_FILESAVE :: 8
+STD_PRINTPRE :: 9
+STD_PROPERTIES :: 10
+STD_HELP :: 11
+STD_FIND :: 12
+STD_REPLACE :: 13
+STD_PRINT :: 14
+
+VIEW_LARGEICONS :: 0
+VIEW_SMALLICONS :: 1
+VIEW_LIST :: 2
+VIEW_DETAILS :: 3
+VIEW_SORTNAME :: 4
+VIEW_SORTSIZE :: 5
+VIEW_SORTDATE :: 6
+VIEW_SORTTYPE :: 7
+VIEW_PARENTFOLDER :: 8
+VIEW_NETCONNECT :: 9
+VIEW_NETDISCONNECT :: 10
+VIEW_NEWFOLDER :: 11
+VIEW_VIEWMENU :: 12
+
+HIST_BACK :: 0
+HIST_FORWARD :: 1
+HIST_FAVORITES :: 2
+HIST_ADDTOFAVORITES :: 3
+HIST_VIEWTREE :: 4
+
+// Header Control Styles
+HDS_HORZ :: 0x000
+HDS_BUTTONS :: 0x002
+HDS_HOTTRACK :: 0x004
+HDS_HIDDEN :: 0x008
+HDS_DRAGDROP :: 0x040
+HDS_FULLDRAG :: 0x080
+HDS_FILTERBAR :: 0x100
+HDS_FLAT :: 0x200
+
+// Header Control Notifications
+HDN_ITEMCHANGINGA :: (HDN_FIRST-0)
+HDN_ITEMCHANGEDA :: (HDN_FIRST-1)
+HDN_ITEMCLICKA :: (HDN_FIRST-2)
+HDN_ITEMDBLCLICKA :: (HDN_FIRST-3)
+HDN_DIVIDERDBLCLICKA :: (HDN_FIRST-5)
+HDN_BEGINTRACKA :: (HDN_FIRST-6)
+HDN_ENDTRACKA :: (HDN_FIRST-7)
+HDN_TRACKA :: (HDN_FIRST-8)
+HDN_GETDISPINFOA :: (HDN_FIRST-9)
+HDN_BEGINDRAG :: (HDN_FIRST-10)
+HDN_ENDDRAG :: (HDN_FIRST-11)
+HDN_FILTERCHANGE :: (HDN_FIRST-12)
+HDN_FILTERBTNCLICK :: (HDN_FIRST-13)
+HDN_ITEMCHANGINGW :: (HDN_FIRST-20)
+HDN_ITEMCHANGEDW :: (HDN_FIRST-21)
+HDN_ITEMCLICKW :: (HDN_FIRST-22)
+HDN_ITEMDBLCLICKW :: (HDN_FIRST-23)
+HDN_DIVIDERDBLCLICKW :: (HDN_FIRST-25)
+HDN_BEGINTRACKW :: (HDN_FIRST-26)
+HDN_ENDTRACKW :: (HDN_FIRST-27)
+HDN_TRACKW :: (HDN_FIRST-28)
+HDN_GETDISPINFOW :: (HDN_FIRST-29)
+
+// Header Control Constants
+HDFT_ISSTRING :: 0x0000
+HDFT_ISNUMBER :: 0x0001
+HDFT_HASNOVALUE :: 0x8000
+
+HDI_WIDTH :: 0x001
+HDI_HEIGHT :: HDI_WIDTH
+HDI_TEXT :: 0x002
+HDI_FORMAT :: 0x004
+HDI_LPARAM :: 0x008
+HDI_BITMAP :: 0x010
+HDI_IMAGE :: 0x020
+HDI_DI_SETITEM :: 0x040
+HDI_ORDER :: 0x080
+HDI_FILTER :: 0x100
+
+HDF_LEFT :: 0x0000000
+HDF_RIGHT :: 0x0000001
+HDF_CENTER :: 0x0000002
+HDF_JUSTIFYMASK :: 0x0000003
+HDF_RTLREADING :: 0x0000004
+HDF_CHECKBOX :: 0x0000040
+HDF_CHECKED :: 0x0000080
+HDF_FIXEDWIDTH :: 0x0000100
+HDF_SORTDOWN :: 0x0000200
+HDF_SORTUP :: 0x0000400
+HDF_IMAGE :: 0x0000800
+HDF_BITMAP_ON_RIGHT :: 0x0001000
+HDF_BITMAP :: 0x0002000
+HDF_STRING :: 0x0004000
+HDF_OWNERDRAW :: 0x0008000
+HDF_SPLITBUTTON :: 0x1000000
+
+HHT_NOWHERE :: 0x001
+HHT_ONHEADER :: 0x002
+HHT_ONDIVIDER :: 0x004
+HHT_ONDIVOPEN :: 0x008
+HHT_ONFILTER :: 0x010
+HHT_ONFILTERBUTTON :: 0x020
+HHT_ABOVE :: 0x100
+HHT_BELOW :: 0x200
+HHT_TORIGHT :: 0x400
+HHT_TOLEFT :: 0x800
+
+// Rebar Control Styles
+RBS_TOOLTIPS :: 0x0100
+RBS_VARHEIGHT :: 0x0200
+RBS_BANDBORDERS :: 0x0400
+RBS_FIXEDORDER :: 0x0800
+RBS_REGISTERDROP :: 0x1000
+RBS_AUTOSIZE :: 0x2000
+RBS_VERTICALGRIPPER :: 0x4000
+RBS_DBLCLKTOGGLE :: 0x8000
+
+// Tooltip Control Styles
+TTS_ALWAYSTIP :: 0x01
+TTS_NOPREFIX :: 0x02
+TTS_NOANIMATE :: 0x10
+TTS_NOFADE :: 0x20
+TTS_BALLOON :: 0x40
+TTS_CLOSE :: 0x80
+
+// Statusbar Control Styles
+SBARS_SIZEGRIP :: 0x100
+SBARS_TOOLTIPS :: 0x800
+
+// Statusbar Control Constants
+SBT_TOOLTIPS :: 0x800
+
+// Up-Down Control Styles
+UDS_WRAP :: 0x001
+UDS_SETBUDDYINT :: 0x002
+UDS_ALIGNRIGHT :: 0x004
+UDS_ALIGNLEFT :: 0x008
+UDS_AUTOBUDDY :: 0x010
+UDS_ARROWKEYS :: 0x020
+UDS_HORZ :: 0x040
+UDS_NOTHOUSANDS :: 0x080
+UDS_HOTTRACK :: 0x100
+
+// Common Control Styles
+CCS_TOP :: 0x01
+CCS_NOMOVEY :: 0x02
+CCS_BOTTOM :: 0x03
+CCS_NORESIZE :: 0x04
+CCS_NOPARENTALIGN :: 0x08
+CCS_ADJUSTABLE :: 0x20
+CCS_NODIVIDER :: 0x40
+CCS_VERT :: 0x80
+CCS_LEFT :: (CCS_VERT | CCS_TOP)
+CCS_RIGHT :: (CCS_VERT | CCS_BOTTOM)
+CCS_NOMOVEX :: (CCS_VERT | CCS_NOMOVEY)
+
+// List-View Control Styles
+LVS_ICON :: 0x0000
+LVS_REPORT :: 0x0001
+LVS_SMALLICON :: 0x0002
+LVS_LIST :: 0x0003
+LVS_TYPEMASK :: 0x0003
+LVS_SINGLESEL :: 0x0004
+LVS_SHOWSELALWAYS :: 0x0008
+LVS_SORTASCENDING :: 0x0010
+LVS_SORTDESCENDING :: 0x0020
+LVS_SHAREIMAGELISTS :: 0x0040
+LVS_NOLABELWRAP :: 0x0080
+LVS_AUTOARRANGE :: 0x0100
+LVS_EDITLABELS :: 0x0200
+LVS_OWNERDATA :: 0x1000
+LVS_NOSCROLL :: 0x2000
+LVS_TYPESTYLEMASK :: 0xFC00
+LVS_ALIGNTOP :: 0x0000
+LVS_ALIGNLEFT :: 0x0800
+LVS_ALIGNMASK :: 0x0C00
+LVS_OWNERDRAWFIXED :: 0x0400
+LVS_NOCOLUMNHEADER :: 0x4000
+LVS_NOSORTHEADER :: 0x8000
+
+// Tree-View Control Styles
+TVS_HASBUTTONS :: 0x0001
+TVS_HASLINES :: 0x0002
+TVS_LINESATROOT :: 0x0004
+TVS_EDITLABELS :: 0x0008
+TVS_DISABLEDRAGDROP :: 0x0010
+TVS_SHOWSELALWAYS :: 0x0020
+TVS_RTLREADING :: 0x0040
+TVS_NOTOOLTIPS :: 0x0080
+TVS_CHECKBOXES :: 0x0100
+TVS_TRACKSELECT :: 0x0200
+TVS_SINGLEEXPAND :: 0x0400
+TVS_INFOTIP :: 0x0800
+TVS_FULLROWSELECT :: 0x1000
+TVS_NOSCROLL :: 0x2000
+TVS_NONEVENHEIGHT :: 0x4000
+TVS_NOHSCROLL :: 0x8000
+
+// Tree-View Control Constants
+TVE_COLLAPSE :: 0x0001
+TVE_EXPAND :: 0x0002
+TVE_TOGGLE :: 0x0003
+TVE_EXPANDPARTIAL :: 0x4000
+TVE_COLLAPSERESET :: 0x8000
+
+TVSIL_NORMAL :: 0
+TVSIL_STATE :: 2
+
+TVGN_ROOT :: 0x0
+TVGN_NEXT :: 0x1
+TVGN_PREVIOUS :: 0x2
+TVGN_PARENT :: 0x3
+TVGN_CHILD :: 0x4
+TVGN_FIRSTVISIBLE :: 0x5
+TVGN_NEXTVISIBLE :: 0x6
+TVGN_PREVIOUSVISIBLE :: 0x7
+TVGN_DROPHILITE :: 0x8
+TVGN_CARET :: 0x9
+TVGN_LASTVISIBLE :: 0xA
+
+TVSI_NOSINGLEEXPAND :: 0x8000
+
+TVHT_NOWHERE :: 0x001
+TVHT_ONITEMICON :: 0x002
+TVHT_ONITEMLABEL :: 0x004
+TVHT_ONITEM :: (TVHT_ONITEMICON | TVHT_ONITEMLABEL | TVHT_ONITEMSTATEICON)
+TVHT_ONITEMINDENT :: 0x008
+TVHT_ONITEMBUTTON :: 0x010
+TVHT_ONITEMRIGHT :: 0x020
+TVHT_ONITEMSTATEICON :: 0x040
+TVHT_ABOVE :: 0x100
+TVHT_BELOW :: 0x200
+TVHT_TORIGHT :: 0x400
+TVHT_TOLEFT :: 0x800
+
+// Tab Control Styles
+TCS_SCROLLOPPOSITE :: 0x0001
+TCS_BOTTOM :: 0x0002
+TCS_RIGHT :: 0x0002
+TCS_MULTISELECT :: 0x0004
+TCS_FLATBUTTONS :: 0x0008
+TCS_FORCEICONLEFT :: 0x0010
+TCS_FORCELABELLEFT :: 0x0020
+TCS_HOTTRACK :: 0x0040
+TCS_VERTICAL :: 0x0080
+TCS_TABS :: 0x0000
+TCS_BUTTONS :: 0x0100
+TCS_SINGLELINE :: 0x0000
+TCS_MULTILINE :: 0x0200
+TCS_RIGHTJUSTIFY :: 0x0000
+TCS_FIXEDWIDTH :: 0x0400
+TCS_RAGGEDRIGHT :: 0x0800
+TCS_FOCUSONBUTTONDOWN :: 0x1000
+TCS_OWNERDRAWFIXED :: 0x2000
+TCS_TOOLTIPS :: 0x4000
+TCS_FOCUSNEVER :: 0x8000
+
+// Tab Control Constants
+TCIF_TEXT :: 0x01
+TCIF_IMAGE :: 0x02
+TCIF_RTLREADING :: 0x04
+TCIF_PARAM :: 0x08
+TCIF_STATE :: 0x10
+
+TCIS_BUTTONPRESSED :: 0x1
+TCIS_HIGHLIGHTED :: 0x2
+
+TCHT_NOWHERE :: 0x1
+TCHT_ONITEMICON :: 0x2
+TCHT_ONITEMLABEL :: 0x4
+TCHT_ONITEM :: (TCHT_ONITEMICON | TCHT_ONITEMLABEL)
+
+// Animation Control Styles
+ACS_CENTER :: 0x1
+ACS_TRANSPARENT :: 0x2
+ACS_AUTOPLAY :: 0x4
+ACS_TIMER :: 0x8
+
+// Month-Calendar Control Styles
+MCS_DAYSTATE :: 0x01
+MCS_MULTISELECT :: 0x02
+MCS_WEEKNUMBERS :: 0x04
+MCS_NOTODAYCIRCLE :: 0x08
+MCS_NOTODAY :: 0x10
+
+// Date-and-Time Picker Control Styles
+DTS_UPDOWN :: 0x01
+DTS_SHOWNONE :: 0x02
+DTS_SHORTDATEFORMAT :: 0x00
+DTS_LONGDATEFORMAT :: 0x04
+DTS_SHORTDATECENTURYFORMAT :: 0x0C
+DTS_TIMEFORMAT :: 0x09
+DTS_APPCANPARSE :: 0x10
+DTS_RIGHTALIGN :: 0x20
+
+// Pager Control Styles
+PGS_VERT :: 0x0
+PGS_HORZ :: 0x1
+PGS_AUTOSCROLL :: 0x2
+PGS_DRAGNDROP :: 0x4
+
+// Native Font Control Styles
+NFS_EDIT :: 0x01
+NFS_STATIC :: 0x02
+NFS_LISTCOMBO :: 0x04
+NFS_BUTTON :: 0x08
+NFS_ALL :: 0x10
+NFS_USEFONTASSOC :: 0x20
+
// Font Weights
FW_DONTCARE :: 0
FW_THIN :: 100
@@ -795,6 +1249,8 @@ TIMERPROC :: #type proc "system" (HWND, UINT, UINT_PTR, DWORD)
WNDPROC :: #type proc "system" (HWND, UINT, WPARAM, LPARAM) -> LRESULT
+SUBCLASSPROC :: #type proc "system" (HWND, UINT, WPARAM, LPARAM, UINT_PTR, DWORD_PTR) -> LRESULT
+
HOOKPROC :: #type proc "system" (code: c_int, wParam: WPARAM, lParam: LPARAM) -> LRESULT
WINEVENTPROC :: #type proc "system" (
@@ -1203,6 +1659,16 @@ NMHDR :: struct {
code: UINT, // NM_ code
}
+NMCUSTOMDRAW :: struct {
+ hdr: NMHDR,
+ dwDrawStage: DWORD,
+ hdc: HDC,
+ rc: RECT,
+ dwItemSpec: DWORD_PTR,
+ uItemState: UINT,
+ lItemlParam: LPARAM,
+}
+
NCCALCSIZE_PARAMS :: struct {
rgrc: [3]RECT,
lppos: PWINDOWPOS,
@@ -2201,7 +2667,24 @@ DUPLICATE_SAME_ACCESS: DWORD : 0x00000002
CONDITION_VARIABLE_INIT :: CONDITION_VARIABLE{}
SRWLOCK_INIT :: SRWLOCK{}
-STARTF_USESTDHANDLES: DWORD : 0x00000100
+// Flags in STARTUPINFOW.dwFlags.
+STARTF_USESHOWWINDOW: DWORD : 0x00000001
+STARTF_USESIZE: DWORD : 0x00000002
+STARTF_USEPOSITION: DWORD : 0x00000004
+STARTF_USECOUNTCHARS: DWORD : 0x00000008
+STARTF_USEFILLATTRIBUTE: DWORD : 0x00000010
+STARTF_RUNFULLSCREEN: DWORD : 0x00000020 // ignored for non-x86 platforms
+STARTF_FORCEONFEEDBACK: DWORD : 0x00000040
+STARTF_FORCEOFFFEEDBACK: DWORD : 0x00000080
+STARTF_USESTDHANDLES: DWORD : 0x00000100
+// WINVER >= 0x400
+STARTF_USEHOTKEY: DWORD : 0x00000200
+STARTF_TITLEISLINKNAME: DWORD : 0x00000800
+STARTF_TITLEISAPPID: DWORD : 0x00001000
+STARTF_PREVENTPINNING: DWORD : 0x00002000
+// WINVER >= 0x600
+STARTF_UNTRUSTEDSOURCE: DWORD : 0x00008000
+
VOLUME_NAME_DOS: DWORD : 0x0
@@ -2694,11 +3177,23 @@ EXCEPTION_MAXIMUM_PARAMETERS :: 15
EXCEPTION_DATATYPE_MISALIGNMENT :: 0x80000002
EXCEPTION_BREAKPOINT :: 0x80000003
+EXCEPTION_SINGLE_STEP :: 0x80000004
EXCEPTION_ACCESS_VIOLATION :: 0xC0000005
+EXCEPTION_IN_PAGE_ERROR :: 0xC0000006
EXCEPTION_ILLEGAL_INSTRUCTION :: 0xC000001D
+EXCEPTION_NONCONTINUABLE_EXCEPTION :: 0xC0000025
+EXCEPTION_INVALID_DISPOSITION :: 0xC0000026
EXCEPTION_ARRAY_BOUNDS_EXCEEDED :: 0xC000008C
+EXCEPTION_FLT_DENORMAL_OPERAND :: 0xC000008D
+EXCEPTION_FLT_DIVIDE_BY_ZERO :: 0xC000008E
+EXCEPTION_FLT_INEXACT_RESULT :: 0xC000008F
+EXCEPTION_FLT_INVALID_OPERATION :: 0xC0000090
+EXCEPTION_FLT_OVERFLOW :: 0xC0000091
+EXCEPTION_FLT_STACK_CHECK :: 0xC0000092
+EXCEPTION_FLT_UNDERFLOW :: 0xC0000093
EXCEPTION_INT_DIVIDE_BY_ZERO :: 0xC0000094
EXCEPTION_INT_OVERFLOW :: 0xC0000095
+EXCEPTION_PRIV_INSTRUCTION :: 0xC0000096
EXCEPTION_STACK_OVERFLOW :: 0xC00000FD
STATUS_PRIVILEGED_INSTRUCTION :: 0xC0000096
@@ -3415,8 +3910,6 @@ TIME_ZONE_INFORMATION :: struct {
DaylightBias: LONG,
}
-
-@(private="file")
IMAGE_DOS_HEADER :: struct {
e_magic: WORD,
e_cblp: WORD,
@@ -3534,6 +4027,19 @@ IMAGE_EXPORT_DIRECTORY :: struct {
AddressOfNameOrdinals: DWORD, // RVA from base of image
}
+IMAGE_DEBUG_DIRECTORY :: struct {
+ Characteristics: DWORD,
+ TimeDateStamp: DWORD,
+ MajorVersion: WORD,
+ MinorVersion: WORD,
+ Type: DWORD,
+ SizeOfData: DWORD,
+ AddressOfRawData: DWORD,
+ PointerToRawData: DWORD,
+}
+
+IMAGE_DEBUG_TYPE_CODEVIEW :: 2
+
SICHINTF :: DWORD
SHCONTF :: DWORD
SFGAOF :: ULONG
@@ -4550,6 +5056,31 @@ DNS_SRV_DATAA :: struct {
_: WORD, // padding
}
+// See https://learn.microsoft.com/en-us/windows/win32/dns/dns-constants
+DNS_QUERY_OPTION :: enum DWORD {
+ ACCEPT_TRUNCATED_RESPONSE = 0,
+ DNS_QUERY_USE_TCP_ONLY = 1,
+ NO_RECURSION = 2,
+ BYPASS_CACHE = 3,
+ NO_WIRE_QUERY = 4,
+ NO_LOCAL_NAME = 5,
+ NO_HOSTS_FILE = 6,
+ NO_NETBT = 7,
+ WIRE_ONLY = 8,
+ RETURN_MESSAGE = 9,
+ MULTICAST_ONLY = 10,
+ NO_MULTICAST = 11,
+ TREAT_AS_FQDN = 12,
+ ADDRCONFIG = 13,
+ DUAL_ADDR = 14,
+ MULTICAST_WAIT = 17,
+ MULTICAST_VERIFY = 18,
+ DONT_RESET_TTL_VALUES = 20,
+ DISABLE_IDN_ENCODING = 21,
+ APPEND_MULTILABEL = 23,
+}
+DNS_QUERY_OPTIONS :: bit_set[DNS_QUERY_OPTION; DWORD]
+
SOCKADDR :: struct {
sa_family: ADDRESS_FAMILY,
sa_data: [14]CHAR,
diff --git a/core/sys/windows/user32.odin b/core/sys/windows/user32.odin
index 4ae33cd32..94cd57811 100644
--- a/core/sys/windows/user32.odin
+++ b/core/sys/windows/user32.odin
@@ -2,6 +2,7 @@
package sys_windows
import "base:intrinsics"
+import "core:c"
foreign import user32 "system:User32.lib"
@(default_calling_convention="system")
@@ -32,6 +33,8 @@ foreign user32 {
RegisterClassExW :: proc(^WNDCLASSEXW) -> ATOM ---
UnregisterClassW :: proc(lpClassName: LPCWSTR, hInstance: HINSTANCE) -> BOOL ---
+ RegisterHotKey :: proc(hnwd: HWND, id: c.int, fsModifiers: UINT, vk: UINT) -> BOOL ---
+
CreateWindowExW :: proc(
dwExStyle: DWORD,
lpClassName: LPCWSTR,
@@ -51,6 +54,7 @@ foreign user32 {
IsWindowVisible :: proc(hwnd: HWND) -> BOOL ---
IsWindowEnabled :: proc(hwnd: HWND) -> BOOL ---
IsIconic :: proc(hwnd: HWND) -> BOOL ---
+ IsZoomed :: proc(hwnd: HWND) -> BOOL ---
BringWindowToTop :: proc(hWnd: HWND) -> BOOL ---
GetTopWindow :: proc(hWnd: HWND) -> HWND ---
SetForegroundWindow :: proc(hWnd: HWND) -> BOOL ---
@@ -59,6 +63,8 @@ foreign user32 {
UpdateWindow :: proc(hWnd: HWND) -> BOOL ---
SetActiveWindow :: proc(hWnd: HWND) -> HWND ---
GetActiveWindow :: proc() -> HWND ---
+ SetFocus :: proc(hWnd: HWND) -> HWND ---
+ GetFocus :: proc() -> HWND ---
RedrawWindow :: proc(hwnd: HWND, lprcUpdate: LPRECT, hrgnUpdate: HRGN, flags: RedrawWindowFlags) -> BOOL ---
SetParent :: proc(hWndChild: HWND, hWndNewParent: HWND) -> HWND ---
SetPropW :: proc(hWnd: HWND, lpString: LPCWSTR, hData: HANDLE) -> BOOL ---
@@ -207,6 +213,7 @@ foreign user32 {
EnumDisplayMonitors :: proc(hdc: HDC, lprcClip: LPRECT, lpfnEnum: Monitor_Enum_Proc, dwData: LPARAM) -> BOOL ---
EnumWindows :: proc(lpEnumFunc: Window_Enum_Proc, lParam: LPARAM) -> BOOL ---
+ EnumChildWindows :: proc(hWndParent: HWND, lpEnumFunc: Window_Enum_Proc, lParam: LPARAM) -> BOOL ---
IsProcessDPIAware :: proc() -> BOOL ---
SetProcessDPIAware :: proc() -> BOOL ---
@@ -548,11 +555,11 @@ RI_KEY_TERMSRV_SHADOW :: 0x10
MOUSE_MOVE_RELATIVE :: 0x00
MOUSE_MOVE_ABSOLUTE :: 0x01
MOUSE_VIRTUAL_DESKTOP :: 0x02
-MOUSE_ATTRIUBTTES_CHANGED :: 0x04
+MOUSE_ATTRIBUTES_CHANGED :: 0x04
MOUSE_MOVE_NOCOALESCE :: 0x08
RI_MOUSE_BUTTON_1_DOWN :: 0x0001
-RI_MOUSE_LEFT_BUTTON_DOWNS :: RI_MOUSE_BUTTON_1_DOWN
+RI_MOUSE_LEFT_BUTTON_DOWN :: RI_MOUSE_BUTTON_1_DOWN
RI_MOUSE_BUTTON_1_UP :: 0x0002
RI_MOUSE_LEFT_BUTTON_UP :: RI_MOUSE_BUTTON_1_UP
RI_MOUSE_BUTTON_2_DOWN :: 0x0004
@@ -842,3 +849,23 @@ FKF_CONFIRMHOTKEY :: 0x8
FKF_HOTKEYSOUND :: 0x10
FKF_INDICATOR :: 0x20
FKF_CLICKON :: 0x40
+
+NONCLIENTMETRICSW :: struct {
+ cbSize: UINT,
+ iBorderWidth: i32,
+ iScrollWidth: i32,
+ iScrollHeight: i32,
+ iCaptionWidth: i32,
+ iCaptionHeight: i32,
+ lfCaptionFont: LOGFONTW,
+ iSmCaptionWidth: i32,
+ iSmCaptionHeight: i32,
+ lfSmCaptionFont: LOGFONTW,
+ iMenuWidth: i32,
+ iMenuHeight: i32,
+ lfMenuFont: LOGFONTW,
+ lfStatusFont: LOGFONTW,
+ lfMessageFont: LOGFONTW,
+ iPaddedBorderWidth: i32,
+}
+LPNONCLIENTMETRICSW :: ^NONCLIENTMETRICSW
diff --git a/core/sys/windows/ux_theme.odin b/core/sys/windows/ux_theme.odin
index 392cf1e18..679d9998f 100644
--- a/core/sys/windows/ux_theme.odin
+++ b/core/sys/windows/ux_theme.odin
@@ -9,4 +9,5 @@ PMARGINS :: ^MARGINS
@(default_calling_convention="system")
foreign uxtheme {
IsThemeActive :: proc() -> BOOL ---
+ SetWindowTheme :: proc(hWnd: HWND, pszSubAppName, pszSubIdList: LPCWSTR) -> HRESULT ---
}
diff --git a/core/sys/windows/window_messages.odin b/core/sys/windows/window_messages.odin
index d69771bdf..0901ee696 100644
--- a/core/sys/windows/window_messages.odin
+++ b/core/sys/windows/window_messages.odin
@@ -687,10 +687,14 @@ EM_GETAUTOURLDETECT :: 0x045c
TB_GETSTRINGA :: 0x045c
EM_SETPALETTE :: 0x045d
EM_GETTEXTEX :: 0x045e
+TB_SETHOTITEM2 :: 0x045e
EM_GETTEXTLENGTHEX :: 0x045f
EM_SHOWSCROLLBAR :: 0x0460
+TB_SETLISTGAP :: 0x0460
EM_SETTEXTEX :: 0x0461
+TB_GETIMAGELISTCOUNT :: 0x0462
TAPI_REPLY :: 0x0463
+TB_GETIDEALSIZE :: 0x0463
ACM_OPENA :: 0x0464
BFFM_SETSTATUSTEXTA :: 0x0464
CDM_FIRST :: 0x0464
@@ -704,6 +708,7 @@ CDM_GETFILEPATH :: 0x0465
EM_GETPUNCTUATION :: 0x0465
IPM_SETADDRESS :: 0x0465
PSM_SETCURSEL :: 0x0465
+TB_GETMETRICS :: 0x0465
UDM_SETRANGE :: 0x0465
WM_CHOOSEFONT_SETLOGFONT :: 0x0465
ACM_STOP :: 0x0466
@@ -712,6 +717,7 @@ CDM_GETFOLDERPATH :: 0x0466
EM_SETWORDWRAPMODE :: 0x0466
IPM_GETADDRESS :: 0x0466
PSM_REMOVEPAGE :: 0x0466
+TB_SETMETRICS :: 0x0466
UDM_GETRANGE :: 0x0466
WM_CAP_SET_CALLBACK_ERRORW :: 0x0466
WM_CHOOSEFONT_SETFLAGS :: 0x0466
@@ -721,6 +727,7 @@ CDM_GETFOLDERIDLIST :: 0x0467
EM_GETWORDWRAPMODE :: 0x0467
IPM_SETRANGE :: 0x0467
PSM_ADDPAGE :: 0x0467
+TB_GETITEMDROPDOWNRECT :: 0x0467
UDM_SETPOS :: 0x0467
WM_CAP_SET_CALLBACK_STATUSW :: 0x0467
BFFM_SETSTATUSTEXTW :: 0x0468
@@ -728,11 +735,13 @@ CDM_SETCONTROLTEXT :: 0x0468
EM_SETIMECOLOR :: 0x0468
IPM_SETFOCUS :: 0x0468
PSM_CHANGED :: 0x0468
+TB_SETPRESSEDIMAGELIST :: 0x0468
UDM_GETPOS :: 0x0468
CDM_HIDECONTROL :: 0x0469
EM_GETIMECOLOR :: 0x0469
IPM_ISBLANK :: 0x0469
PSM_RESTARTWINDOWS :: 0x0469
+TB_GETPRESSEDIMAGELIST :: 0x0469
UDM_SETBUDDY :: 0x0469
CDM_SETDEFEXT :: 0x046a
EM_SETIMEOPTIONS :: 0x046a
@@ -915,6 +924,10 @@ FM_GETDRIVEINFOW :: 0x0611
FM_GETFILESELW :: 0x0614
FM_GETFILESELLFNW :: 0x0615
WLX_WM_SAS :: 0x0659
+LM_HITTEST :: 0x0700
+LM_GETIDEALHEIGHT :: 0x0701
+LM_SETITEM :: 0x0702
+LM_GETITEM :: 0x0703
SM_GETSELCOUNT :: 0x07e8
UM_GETSELCOUNT :: 0x07e8
WM_CPL_LAUNCH :: 0x07e8
@@ -1011,6 +1024,7 @@ LVM_GETITEMW :: 0x104b
LVM_SETITEMW :: 0x104c
LVM_INSERTITEMW :: 0x104d
LVM_GETTOOLTIPS :: 0x104e
+LVM_SORTITEMSEX :: 0x1051
LVM_FINDITEMW :: 0x1053
LVM_GETSTRINGWIDTHW :: 0x1057
LVM_GETCOLUMNW :: 0x105f
@@ -1065,7 +1079,143 @@ LVM_GETFOOTERITEM :: 0x10d0
LVM_GETITEMINDEXRECT :: 0x10d1
LVM_SETITEMINDEXSTATE :: 0x10d2
LVM_GETNEXTITEMINDEX :: 0x10d3
+TV_FIRST :: 0x1100
+TVM_INSERTITEMA :: (TV_FIRST+0)
+TVM_DELETEITEM :: (TV_FIRST+1)
+TVM_EXPAND :: (TV_FIRST+2)
+TVM_GETITEMRECT :: (TV_FIRST+4)
+TVM_GETCOUNT :: (TV_FIRST+5)
+TVM_GETINDENT :: (TV_FIRST+6)
+TVM_SETINDENT :: (TV_FIRST+7)
+TVM_GETIMAGELIST :: (TV_FIRST+8)
+TVM_SETIMAGELIST :: (TV_FIRST+9)
+TVM_GETNEXTITEM :: (TV_FIRST+10)
+TVM_SELECTITEM :: (TV_FIRST+11)
+TVM_GETITEMA :: (TV_FIRST+12)
+TVM_SETITEMA :: (TV_FIRST+13)
+TVM_EDITLABELA :: (TV_FIRST+14)
+TVM_GETEDITCONTROL :: (TV_FIRST+15)
+TVM_GETVISIBLECOUNT :: (TV_FIRST+16)
+TVM_HITTEST :: (TV_FIRST+17)
+TVM_CREATEDRAGIMAGE :: (TV_FIRST+18)
+TVM_SORTCHILDREN :: (TV_FIRST+19)
+TVM_ENSUREVISIBLE :: (TV_FIRST+20)
+TVM_SORTCHILDRENCB :: (TV_FIRST+21)
+TVM_ENDEDITLABELNOW :: (TV_FIRST+22)
+TVM_GETISEARCHSTRINGA :: (TV_FIRST+23)
+TVM_SETTOOLTIPS :: (TV_FIRST+24)
+TVM_GETTOOLTIPS :: (TV_FIRST+25)
+TVM_SETINSERTMARK :: (TV_FIRST+26)
+TVM_SETUNICODEFORMAT :: CCM_SETUNICODEFORMAT
+TVM_GETUNICODEFORMAT :: CCM_GETUNICODEFORMAT
+TVM_SETITEMHEIGHT :: (TV_FIRST+27)
+TVM_GETITEMHEIGHT :: (TV_FIRST+28)
+TVM_SETBKCOLOR :: (TV_FIRST+29)
+TVM_SETTEXTCOLOR :: (TV_FIRST+30)
+TVM_GETBKCOLOR :: (TV_FIRST+31)
+TVM_GETTEXTCOLOR :: (TV_FIRST+32)
+TVM_SETSCROLLTIME :: (TV_FIRST+33)
+TVM_GETSCROLLTIME :: (TV_FIRST+34)
+TVM_SETINSERTMARKCOLOR :: (TV_FIRST+37)
+TVM_GETINSERTMARKCOLOR :: (TV_FIRST+38)
+TVM_GETITEMSTATE :: (TV_FIRST+39)
+TVM_SETLINECOLOR :: (TV_FIRST+40)
+TVM_GETLINECOLOR :: (TV_FIRST+41)
+TVM_MAPACCIDTOHTREEITEM :: (TV_FIRST+42)
+TVM_MAPHTREEITEMTOACCID :: (TV_FIRST+43)
+TVM_INSERTITEMW :: (TV_FIRST+50)
+TVM_GETITEMW :: (TV_FIRST+62)
+TVM_SETITEMW :: (TV_FIRST+63)
+TVM_GETISEARCHSTRINGW :: (TV_FIRST+64)
+TVM_EDITLABELW :: (TV_FIRST+65)
+HDM_FIRST :: 0x1200
+HDM_GETITEMCOUNT :: (HDM_FIRST+0)
+HDM_INSERTITEMA :: (HDM_FIRST+1)
+HDM_DELETEITEM :: (HDM_FIRST+2)
+HDM_GETITEMA :: (HDM_FIRST+3)
+HDM_SETITEMA :: (HDM_FIRST+4)
+HDM_LAYOUT :: (HDM_FIRST+5)
+HDM_HITTEST :: (HDM_FIRST+6)
+HDM_GETITEMRECT :: (HDM_FIRST+7)
+HDM_SETIMAGELIST :: (HDM_FIRST+8)
+HDM_GETIMAGELIST :: (HDM_FIRST+9)
+HDM_INSERTITEMW :: (HDM_FIRST+10)
+HDM_GETITEMW :: (HDM_FIRST+11)
+HDM_SETITEMW :: (HDM_FIRST+12)
+HDM_ORDERTOINDEX :: (HDM_FIRST+15)
+HDM_CREATEDRAGIMAGE :: (HDM_FIRST+16)
+HDM_GETORDERARRAY :: (HDM_FIRST+17)
+HDM_SETORDERARRAY :: (HDM_FIRST+18)
+HDM_SETHOTDIVIDER :: (HDM_FIRST+19)
+HDM_SETBITMAPMARGIN :: (HDM_FIRST+20)
+HDM_GETBITMAPMARGIN :: (HDM_FIRST+21)
+HDM_SETFILTERCHANGETIMEOUT :: (HDM_FIRST+22)
+HDM_SETUNICODEFORMAT :: CCM_SETUNICODEFORMAT
+HDM_GETUNICODEFORMAT :: CCM_GETUNICODEFORMAT
+HDM_EDITFILTER :: (HDM_FIRST+23)
+HDM_CLEARFILTER :: (HDM_FIRST+24)
+TCM_FIRST :: 0x1300
+TCM_GETIMAGELIST :: (TCM_FIRST+2)
+TCM_SETIMAGELIST :: (TCM_FIRST+3)
+TCM_GETITEMCOUNT :: (TCM_FIRST+4)
+TCM_GETITEMA :: (TCM_FIRST+5)
+TCM_SETITEMA :: (TCM_FIRST+6)
+TCM_INSERTITEMA :: (TCM_FIRST+7)
+TCM_DELETEITEM :: (TCM_FIRST+8)
+TCM_DELETEALLITEMS :: (TCM_FIRST+9)
+TCM_GETITEMRECT :: (TCM_FIRST+10)
+TCM_GETCURSEL :: (TCM_FIRST+11)
+TCM_SETCURSEL :: (TCM_FIRST+12)
+TCM_HITTEST :: (TCM_FIRST+13)
+TCM_SETITEMEXTRA :: (TCM_FIRST+14)
+TCM_ADJUSTRECT :: (TCM_FIRST+40)
+TCM_SETITEMSIZE :: (TCM_FIRST+41)
+TCM_REMOVEIMAGE :: (TCM_FIRST+42)
+TCM_SETPADDING :: (TCM_FIRST+43)
+TCM_GETROWCOUNT :: (TCM_FIRST+44)
+TCM_GETTOOLTIPS :: (TCM_FIRST+45)
+TCM_SETTOOLTIPS :: (TCM_FIRST+46)
+TCM_GETCURFOCUS :: (TCM_FIRST+47)
+TCM_SETCURFOCUS :: (TCM_FIRST+48)
+TCM_SETMINTABWIDTH :: (TCM_FIRST+49)
+TCM_DESELECTALL :: (TCM_FIRST+50)
+TCM_HIGHLIGHTITEM :: (TCM_FIRST+51)
+TCM_SETEXTENDEDSTYLE :: (TCM_FIRST+52)
+TCM_GETEXTENDEDSTYLE :: (TCM_FIRST+53)
+TCM_SETUNICODEFORMAT :: CCM_SETUNICODEFORMAT
+TCM_GETUNICODEFORMAT :: CCM_GETUNICODEFORMAT
+TCM_GETITEMW :: (TCM_FIRST+60)
+TCM_SETITEMW :: (TCM_FIRST+61)
+TCM_INSERTITEMW :: (TCM_FIRST+62)
+PGM_FIRST :: 0x1400
+PGM_SETCHILD :: (PGM_FIRST+1)
+PGM_RECALCSIZE :: (PGM_FIRST+2)
+PGM_FORWARDMOUSE :: (PGM_FIRST+3)
+PGM_SETBKCOLOR :: (PGM_FIRST+4)
+PGM_GETBKCOLOR :: (PGM_FIRST+5)
+PGM_SETBORDER :: (PGM_FIRST+6)
+PGM_GETBORDER :: (PGM_FIRST+7)
+PGM_SETPOS :: (PGM_FIRST+8)
+PGM_GETPOS :: (PGM_FIRST+9)
+PGM_SETBUTTONSIZE :: (PGM_FIRST+10)
+PGM_GETBUTTONSIZE :: (PGM_FIRST+11)
+PGM_GETBUTTONSTATE :: (PGM_FIRST+12)
+PGM_GETDROPTARGET :: CCM_GETDROPTARGET
+ECM_FIRST :: 0x1500
+EM_SETCUEBANNER :: ECM_FIRST + 0x0001
+EM_GETCUEBANNER :: ECM_FIRST + 0x0002
+EM_SHOWBALLOONTIP :: ECM_FIRST + 0x0003
+EM_HIDEBALLOONTIP :: ECM_FIRST + 0x0004
+EM_SETHILITE :: ECM_FIRST + 0x0005
+EM_GETHILITE :: ECM_FIRST + 0x0006
+EM_NOSETFOCUS :: ECM_FIRST + 0x0007
+EM_TAKEFOCUS :: ECM_FIRST + 0x0008
BCM_FIRST :: 0x1600
+BCM_GETIDEALSIZE :: BCM_FIRST + 0x0001
+BCM_SETIMAGELIST :: BCM_FIRST + 0x0002
+BCM_GETIMAGELIST :: BCM_FIRST + 0x0003
+BCM_SETTEXTMARGIN :: BCM_FIRST + 0x0004
+BCM_GETTEXTMARGIN :: BCM_FIRST + 0x0005
BCM_SETDROPDOWNSTATE :: BCM_FIRST + 0x0006
BCM_SETSPLITINFO :: BCM_FIRST + 0x0007
BCM_GETSPLITINFO :: BCM_FIRST + 0x0008
@@ -1073,9 +1223,29 @@ BCM_SETNOTE :: BCM_FIRST + 0x0009
BCM_GETNOTE :: BCM_FIRST + 0x000A
BCM_GETNOTELENGTH :: BCM_FIRST + 0x000B
BCM_SETSHIELD :: BCM_FIRST + 0x000C
+CBM_FIRST :: 0x1700
+CB_SETMINVISIBLE :: CBM_FIRST + 0x0001
+CB_GETMINVISIBLE :: CBM_FIRST + 0x0002
+CCM_FIRST :: 0x2000
+CCM_LAST :: (CCM_FIRST+0x200)
+CCM_SETBKCOLOR :: (CCM_FIRST+1)
+CCM_SETCOLORSCHEME :: (CCM_FIRST+2)
+CCM_GETCOLORSCHEME :: (CCM_FIRST+3)
+CCM_GETDROPTARGET :: (CCM_FIRST+4)
+CCM_SETUNICODEFORMAT :: (CCM_FIRST+5)
+CCM_GETUNICODEFORMAT :: (CCM_FIRST+6)
+CCM_SETVERSION :: (CCM_FIRST+7)
+CCM_GETVERSION :: (CCM_FIRST+8)
+CCM_SETNOTIFYWINDOW :: (CCM_FIRST+9)
+CCM_SETWINDOWTHEME :: (CCM_FIRST+11)
+CCM_DPISCALE :: (CCM_FIRST+12)
OCM__BASE :: 0x2000
LVM_SETUNICODEFORMAT :: 0x2005
+SB_SETUNICODEFORMAT :: 0x2005
LVM_GETUNICODEFORMAT :: 0x2006
+SB_GETUNICODEFORMAT :: 0x2006
+CBEM_SETWINDOWTHEME :: 0x200b
+TB_SETWINDOWTHEME :: 0x200b
OCM_CTLCOLOR :: 0x2019
OCM_DRAWITEM :: 0x202b
OCM_MEASUREITEM :: 0x202c
diff --git a/core/sys/windows/xinput.odin b/core/sys/windows/xinput.odin
new file mode 100644
index 000000000..0089f88cb
--- /dev/null
+++ b/core/sys/windows/xinput.odin
@@ -0,0 +1,210 @@
+#+build windows
+package sys_windows
+
+foreign import "system:xinput.lib"
+
+// Device types available in XINPUT_CAPABILITIES
+// Correspond to XINPUT_DEVTYPE_...
+XINPUT_DEVTYPE :: enum BYTE {
+ GAMEPAD = 0x01,
+}
+
+// Device subtypes available in XINPUT_CAPABILITIES
+// Correspond to XINPUT_DEVSUBTYPE_...
+XINPUT_DEVSUBTYPE :: enum BYTE {
+ UNKNOWN = 0x00,
+ GAMEPAD = 0x01,
+ WHEEL = 0x02,
+ ARCADE_STICK = 0x03,
+ FLIGHT_STICK = 0x04,
+ DANCE_PAD = 0x05,
+ GUITAR = 0x06,
+ GUITAR_ALTERNATE = 0x07,
+ DRUM_KIT = 0x08,
+ GUITAR_BASS = 0x0B,
+ ARCADE_PAD = 0x13,
+}
+
+// Flags for XINPUT_CAPABILITIES
+// Correspond to log2(XINPUT_CAPS_...)
+XINPUT_CAP :: enum WORD {
+ FFB_SUPPORTED = 0,
+ WIRELESS = 1,
+ VOICE_SUPPORTED = 2,
+ PMD_SUPPORTED = 3,
+ NO_NAVIGATION = 4,
+}
+XINPUT_CAPS :: distinct bit_set[XINPUT_CAP;WORD]
+
+// Constants for gamepad buttons
+// Correspond to log2(XINPUT_GAMEPAD_...)
+XINPUT_GAMEPAD_BUTTON_BIT :: enum WORD {
+ DPAD_UP = 0,
+ DPAD_DOWN = 1,
+ DPAD_LEFT = 2,
+ DPAD_RIGHT = 3,
+ START = 4,
+ BACK = 5,
+ LEFT_THUMB = 6,
+ RIGHT_THUMB = 7,
+ LEFT_SHOULDER = 8,
+ RIGHT_SHOULDER = 9,
+ A = 12,
+ B = 13,
+ X = 14,
+ Y = 15,
+}
+XINPUT_GAMEPAD_BUTTON :: distinct bit_set[XINPUT_GAMEPAD_BUTTON_BIT;WORD]
+
+// Gamepad thresholds
+XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE: SHORT : 7849
+XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE: SHORT : 8689
+XINPUT_GAMEPAD_TRIGGER_THRESHOLD: SHORT : 30
+
+// Flags to pass to XInputGetCapabilities
+// Corresponds to log2(XINPUT_FLAG_...)
+XINPUT_FLAG_BIT :: enum WORD {
+ GAMEPAD = 0,
+}
+XINPUT_FLAG :: distinct bit_set[XINPUT_FLAG_BIT;DWORD]
+
+// Devices that support batteries
+// Corresponds to BATTERY_DEVTYPE_...
+BATTERY_DEVTYPE :: enum BYTE {
+ GAMEPAD = 0x00,
+ HEADSET = 0x01,
+}
+
+// Flags for battery status level
+// Correspond to BATTERY_TYPE_...
+BATTERY_TYPE :: enum BYTE {
+ DISCONNECTED = 0x00, // This device is not connected
+ WIRED = 0x01, // Wired device, no battery
+ ALKALINE = 0x02, // Alkaline battery source
+ NIMH = 0x03, // Nickel Metal Hydride battery source
+ UNKNOWN = 0xFF, // Cannot determine the battery type
+}
+
+// These are only valid for wireless, connected devices, with known battery types
+// The amount of use time remaining depends on the type of device.
+// Correspond to BATTERY_LEVEL_...
+BATTERY_LEVEL :: enum BYTE {
+ EMPTY = 0x00,
+ LOW = 0x01,
+ MEDIUM = 0x02,
+ FULL = 0x03,
+}
+
+// User index definitions
+
+// Index of the gamer associated with the device
+XUSER :: enum DWORD {
+ One = 0,
+ Two = 1,
+ Three = 2,
+ Four = 3,
+ Any = 0x000000FF, // Can be only used with XInputGetKeystroke
+}
+
+XUSER_MAX_COUNT :: 4
+
+// Codes returned for the gamepad keystroke
+// Corresponds to VK_PAD_...
+VK_PAD :: enum WORD {
+ A = 0x5800,
+ B = 0x5801,
+ X = 0x5802,
+ Y = 0x5803,
+ RSHOULDER = 0x5804,
+ LSHOULDER = 0x5805,
+ LTRIGGER = 0x5806,
+ RTRIGGER = 0x5807,
+ DPAD_UP = 0x5810,
+ DPAD_DOWN = 0x5811,
+ DPAD_LEFT = 0x5812,
+ DPAD_RIGHT = 0x5813,
+ START = 0x5814,
+ BACK = 0x5815,
+ LTHUMB_PRESS = 0x5816,
+ RTHUMB_PRESS = 0x5817,
+ LTHUMB_UP = 0x5820,
+ LTHUMB_DOWN = 0x5821,
+ LTHUMB_RIGHT = 0x5822,
+ LTHUMB_LEFT = 0x5823,
+ LTHUMB_UPLEFT = 0x5824,
+ LTHUMB_UPRIGHT = 0x5825,
+ LTHUMB_DOWNRIGHT = 0x5826,
+ LTHUMB_DOWNLEFT = 0x5827,
+ RTHUMB_UP = 0x5830,
+ RTHUMB_DOWN = 0x5831,
+ RTHUMB_RIGHT = 0x5832,
+ RTHUMB_LEFT = 0x5833,
+ RTHUMB_UPLEFT = 0x5834,
+ RTHUMB_UPRIGHT = 0x5835,
+ RTHUMB_DOWNRIGHT = 0x5836,
+ RTHUMB_DOWNLEFT = 0x5837,
+}
+
+// Flags used in XINPUT_KEYSTROKE
+// Correspond to log2(XINPUT_KEYSTROKE_...)
+XINPUT_KEYSTROKE_BIT :: enum WORD {
+ KEYDOWN = 0,
+ KEYUP = 1,
+ REPEAT = 2,
+}
+XINPUT_KEYSTROKES :: distinct bit_set[XINPUT_KEYSTROKE_BIT;WORD]
+
+// Structures used by XInput APIs
+XINPUT_GAMEPAD :: struct {
+ wButtons: XINPUT_GAMEPAD_BUTTON,
+ bLeftTrigger: BYTE,
+ bRightTrigger: BYTE,
+ sThumbLX: SHORT,
+ sThumbLY: SHORT,
+ sThumbRX: SHORT,
+ sThumbRY: SHORT,
+}
+
+XINPUT_STATE :: struct {
+ dwPacketNumber: DWORD,
+ Gamepad: XINPUT_GAMEPAD,
+}
+
+XINPUT_VIBRATION :: struct {
+ wLeftMotorSpeed: WORD,
+ wRightMotorSpeed: WORD,
+}
+
+XINPUT_CAPABILITIES :: struct {
+ Type: XINPUT_DEVTYPE,
+ SubType: XINPUT_DEVSUBTYPE,
+ Flags: XINPUT_CAPS,
+ Gamepad: XINPUT_GAMEPAD,
+ Vibration: XINPUT_VIBRATION,
+}
+
+XINPUT_BATTERY_INFORMATION :: struct {
+ BatteryType: BATTERY_TYPE,
+ BatteryLevel: BATTERY_LEVEL,
+}
+
+XINPUT_KEYSTROKE :: struct {
+ VirtualKey: VK_PAD,
+ Unicode: WCHAR,
+ Flags: XINPUT_KEYSTROKES,
+ UserIndex: XUSER,
+ HidCode: BYTE,
+}
+
+// XInput APIs
+@(default_calling_convention = "system")
+foreign xinput {
+ XInputGetState :: proc(user: XUSER, pState: ^XINPUT_STATE) -> System_Error ---
+ XInputSetState :: proc(user: XUSER, pVibration: ^XINPUT_VIBRATION) -> System_Error ---
+ XInputGetCapabilities :: proc(user: XUSER, dwFlags: XINPUT_FLAG, pCapabilities: ^XINPUT_CAPABILITIES) -> System_Error ---
+ XInputEnable :: proc(enable: BOOL) ---
+ XInputGetAudioDeviceIds :: proc(user: XUSER, pRenderDeviceId: LPWSTR, pRenderCount: ^UINT, pCaptureDeviceId: LPWSTR, pCaptureCount: ^UINT) -> System_Error ---
+ XInputGetBatteryInformation :: proc(user: XUSER, devType: BATTERY_DEVTYPE, pBatteryInformation: ^XINPUT_BATTERY_INFORMATION) -> System_Error ---
+ XInputGetKeystroke :: proc(user: XUSER, dwReserved: DWORD, pKeystroke: ^XINPUT_KEYSTROKE) -> System_Error ---
+ XInputGetDSoundAudioDeviceGuids :: proc(user: XUSER, pDSoundRenderGuid: ^GUID, pDSoundCaptureGuid: ^GUID) -> System_Error ---
+}
diff --git a/core/testing/runner.odin b/core/testing/runner.odin
index 6b9d610ed..83a5ac4e7 100644
--- a/core/testing/runner.odin
+++ b/core/testing/runner.odin
@@ -391,6 +391,7 @@ runner :: proc(internal_tests: []Internal_Test) -> bool {
fmt.assertf(alloc_error == nil, "Error allocating memory for task allocator #%i: %v", i, alloc_error)
when TRACKING_MEMORY {
mem.tracking_allocator_init(&task_memory_trackers[i], mem.rollback_stack_allocator(&task_allocators[i]))
+ task_memory_trackers[i].bad_free_callback = mem.tracking_allocator_bad_free_callback_add_to_array
}
}
diff --git a/core/testing/signal_handler_posix.odin b/core/testing/signal_handler_posix.odin
index 1bfcc875b..0efba27dc 100644
--- a/core/testing/signal_handler_posix.odin
+++ b/core/testing/signal_handler_posix.odin
@@ -1,4 +1,4 @@
-#+build linux, darwin, netbsd, openbsd, freebsd
+#+build linux, darwin, netbsd, openbsd, freebsd, haiku
#+private
package testing
diff --git a/core/text/match/strlib.odin b/core/text/match/strlib.odin
index bfa696dcd..819f464c5 100644
--- a/core/text/match/strlib.odin
+++ b/core/text/match/strlib.odin
@@ -682,11 +682,14 @@ find_aux :: proc(
// iterative matching which returns the 0th/1st match
// rest has to be used from captures
+// assumes captures is zeroed on first iteration
+// resets captures to zero on last iteration
gmatch :: proc(
haystack: ^string,
pattern: string,
captures: ^[MAX_CAPTURES]Match,
) -> (res: string, ok: bool) {
+ haystack^ = haystack[captures[0].byte_end:]
if len(haystack) > 0 {
length, err := find_aux(haystack^, pattern, 0, false, captures)
@@ -695,10 +698,11 @@ gmatch :: proc(
first := length > 1 ? 1 : 0
cap := captures[first]
res = haystack[cap.byte_start:cap.byte_end]
- haystack^ = haystack[cap.byte_end:]
}
}
-
+ if !ok {
+ captures^ = {}
+ }
return
}
@@ -794,11 +798,14 @@ gsub_with :: proc(
gsub :: proc { gsub_builder, gsub_allocator }
// iterative find with zeroth capture only
+// assumes captures is zeroed on first iteration
+// resets captures to zero on last iteration
gfind :: proc(
haystack: ^string,
pattern: string,
captures: ^[MAX_CAPTURES]Match,
) -> (res: string, ok: bool) {
+ haystack^ = haystack[captures[0].byte_end:]
if len(haystack) > 0 {
length, err := find_aux(haystack^, pattern, 0, true, captures)
@@ -806,10 +813,11 @@ gfind :: proc(
ok = true
cap := captures[0]
res = haystack[cap.byte_start:cap.byte_end]
- haystack^ = haystack[cap.byte_end:]
}
}
-
+ if !ok {
+ captures^ = {}
+ }
return
}
diff --git a/core/text/regex/regex.odin b/core/text/regex/regex.odin
index 3dc26b5c6..8f8efe252 100644
--- a/core/text/regex/regex.odin
+++ b/core/text/regex/regex.odin
@@ -381,6 +381,7 @@ match_with_preallocated_capture :: proc(
capture.pos[n] = {a, b}
n += 1
}
+ num_groups = n
}
return
diff --git a/core/thread/thread_pool.odin b/core/thread/thread_pool.odin
index d9166b450..59bf90620 100644
--- a/core/thread/thread_pool.odin
+++ b/core/thread/thread_pool.odin
@@ -9,6 +9,7 @@ package thread
import "base:intrinsics"
import "core:sync"
import "core:mem"
+import "core:container/queue"
Task_Proc :: #type proc(task: Task)
@@ -40,7 +41,7 @@ Pool :: struct {
threads: []^Thread,
- tasks: [dynamic]Task,
+ tasks: queue.Queue(Task),
tasks_done: [dynamic]Task,
}
@@ -69,13 +70,13 @@ pool_thread_runner :: proc(t: ^Thread) {
}
// Once initialized, the pool's memory address is not allowed to change until
-// it is destroyed.
+// it is destroyed.
//
// The thread pool requires an allocator which it either owns, or which is thread safe.
pool_init :: proc(pool: ^Pool, allocator: mem.Allocator, thread_count: int) {
context.allocator = allocator
pool.allocator = allocator
- pool.tasks = make([dynamic]Task)
+ queue.init(&pool.tasks)
pool.tasks_done = make([dynamic]Task)
pool.threads = make([]^Thread, max(thread_count, 1))
@@ -92,7 +93,7 @@ pool_init :: proc(pool: ^Pool, allocator: mem.Allocator, thread_count: int) {
}
pool_destroy :: proc(pool: ^Pool) {
- delete(pool.tasks)
+ queue.destroy(&pool.tasks)
delete(pool.tasks_done)
for &t in pool.threads {
@@ -140,11 +141,11 @@ pool_join :: proc(pool: ^Pool) {
// the thread pool. You can even add tasks from inside other tasks.
//
// Each task also needs an allocator which it either owns, or which is thread
-// safe.
+// safe.
pool_add_task :: proc(pool: ^Pool, allocator: mem.Allocator, procedure: Task_Proc, data: rawptr, user_index: int = 0) {
sync.guard(&pool.mutex)
- append(&pool.tasks, Task{
+ queue.push_back(&pool.tasks, Task{
procedure = procedure,
data = data,
user_index = user_index,
@@ -288,10 +289,10 @@ pool_is_empty :: #force_inline proc(pool: ^Pool) -> bool {
pool_pop_waiting :: proc(pool: ^Pool) -> (task: Task, got_task: bool) {
sync.guard(&pool.mutex)
- if len(pool.tasks) != 0 {
+ if queue.len(pool.tasks) != 0 {
intrinsics.atomic_sub(&pool.num_waiting, 1)
intrinsics.atomic_add(&pool.num_in_processing, 1)
- task = pop_front(&pool.tasks)
+ task = queue.pop_front(&pool.tasks)
got_task = true
}
diff --git a/core/time/time_other.odin b/core/time/time_other.odin
index d89bcbd42..3f0f06e9b 100644
--- a/core/time/time_other.odin
+++ b/core/time/time_other.odin
@@ -9,6 +9,7 @@
#+build !wasi
#+build !windows
#+build !orca
+#+build !haiku
package time
_IS_SUPPORTED :: false
diff --git a/core/time/time_unix.odin b/core/time/time_unix.odin
index 61c4e91d3..c384d6d07 100644
--- a/core/time/time_unix.odin
+++ b/core/time/time_unix.odin
@@ -1,5 +1,5 @@
#+private
-#+build darwin, freebsd, openbsd, netbsd
+#+build darwin, freebsd, openbsd, netbsd, haiku
package time
import "core:sys/posix"
diff --git a/core/time/timezone/tz_windows.odin b/core/time/timezone/tz_windows.odin
index 238c4c933..8dc5f533c 100644
--- a/core/time/timezone/tz_windows.odin
+++ b/core/time/timezone/tz_windows.odin
@@ -11,146 +11,147 @@ TZ_Abbrev :: struct {
dst: string,
}
-tz_abbrevs := map[string]TZ_Abbrev {
- "Egypt Standard Time" = {"EET", "EEST"}, // Africa/Cairo
- "Morocco Standard Time" = {"+00", "+01"}, // Africa/Casablanca
- "South Africa Standard Time" = {"SAST", "SAST"}, // Africa/Johannesburg
- "South Sudan Standard Time" = {"CAT", "CAT"}, // Africa/Juba
- "Sudan Standard Time" = {"CAT", "CAT"}, // Africa/Khartoum
- "W. Central Africa Standard Time" = {"WAT", "WAT"}, // Africa/Lagos
- "E. Africa Standard Time" = {"EAT", "EAT"}, // Africa/Nairobi
- "Sao Tome Standard Time" = {"GMT", "GMT"}, // Africa/Sao_Tome
- "Libya Standard Time" = {"EET", "EET"}, // Africa/Tripoli
- "Namibia Standard Time" = {"CAT", "CAT"}, // Africa/Windhoek
- "Aleutian Standard Time" = {"HST", "HDT"}, // America/Adak
- "Alaskan Standard Time" = {"AKST", "AKDT"}, // America/Anchorage
- "Tocantins Standard Time" = {"-03", "-03"}, // America/Araguaina
- "Paraguay Standard Time" = {"-04", "-03"}, // America/Asuncion
- "Bahia Standard Time" = {"-03", "-03"}, // America/Bahia
- "SA Pacific Standard Time" = {"-05", "-05"}, // America/Bogota
- "Argentina Standard Time" = {"-03", "-03"}, // America/Buenos_Aires
- "Eastern Standard Time (Mexico)" = {"EST", "EST"}, // America/Cancun
- "Venezuela Standard Time" = {"-04", "-04"}, // America/Caracas
- "SA Eastern Standard Time" = {"-03", "-03"}, // America/Cayenne
- "Central Standard Time" = {"CST", "CDT"}, // America/Chicago
- "Central Brazilian Standard Time" = {"-04", "-04"}, // America/Cuiaba
- "Mountain Standard Time" = {"MST", "MDT"}, // America/Denver
- "Greenland Standard Time" = {"-03", "-02"}, // America/Godthab
- "Turks And Caicos Standard Time" = {"EST", "EDT"}, // America/Grand_Turk
- "Central America Standard Time" = {"CST", "CST"}, // America/Guatemala
- "Atlantic Standard Time" = {"AST", "ADT"}, // America/Halifax
- "Cuba Standard Time" = {"CST", "CDT"}, // America/Havana
- "US Eastern Standard Time" = {"EST", "EDT"}, // America/Indianapolis
- "SA Western Standard Time" = {"-04", "-04"}, // America/La_Paz
- "Pacific Standard Time" = {"PST", "PDT"}, // America/Los_Angeles
- "Mountain Standard Time (Mexico)" = {"MST", "MST"}, // America/Mazatlan
- "Central Standard Time (Mexico)" = {"CST", "CST"}, // America/Mexico_City
- "Saint Pierre Standard Time" = {"-03", "-02"}, // America/Miquelon
- "Montevideo Standard Time" = {"-03", "-03"}, // America/Montevideo
- "Eastern Standard Time" = {"EST", "EDT"}, // America/New_York
- "US Mountain Standard Time" = {"MST", "MST"}, // America/Phoenix
- "Haiti Standard Time" = {"EST", "EDT"}, // America/Port-au-Prince
- "Magallanes Standard Time" = {"-03", "-03"}, // America/Punta_Arenas
- "Canada Central Standard Time" = {"CST", "CST"}, // America/Regina
- "Pacific SA Standard Time" = {"-04", "-03"}, // America/Santiago
- "E. South America Standard Time" = {"-03", "-03"}, // America/Sao_Paulo
- "Newfoundland Standard Time" = {"NST", "NDT"}, // America/St_Johns
- "Pacific Standard Time (Mexico)" = {"PST", "PDT"}, // America/Tijuana
- "Yukon Standard Time" = {"MST", "MST"}, // America/Whitehorse
- "Central Asia Standard Time" = {"+06", "+06"}, // Asia/Almaty
- "Jordan Standard Time" = {"+03", "+03"}, // Asia/Amman
- "Arabic Standard Time" = {"+03", "+03"}, // Asia/Baghdad
- "Azerbaijan Standard Time" = {"+04", "+04"}, // Asia/Baku
- "SE Asia Standard Time" = {"+07", "+07"}, // Asia/Bangkok
- "Altai Standard Time" = {"+07", "+07"}, // Asia/Barnaul
- "Middle East Standard Time" = {"EET", "EEST"}, // Asia/Beirut
- "India Standard Time" = {"IST", "IST"}, // Asia/Calcutta
- "Transbaikal Standard Time" = {"+09", "+09"}, // Asia/Chita
- "Sri Lanka Standard Time" = {"+0530", "+0530"}, // Asia/Colombo
- "Syria Standard Time" = {"+03", "+03"}, // Asia/Damascus
- "Bangladesh Standard Time" = {"+06", "+06"}, // Asia/Dhaka
- "Arabian Standard Time" = {"+04", "+04"}, // Asia/Dubai
- "West Bank Standard Time" = {"EET", "EEST"}, // Asia/Hebron
- "W. Mongolia Standard Time" = {"+07", "+07"}, // Asia/Hovd
- "North Asia East Standard Time" = {"+08", "+08"}, // Asia/Irkutsk
- "Israel Standard Time" = {"IST", "IDT"}, // Asia/Jerusalem
- "Afghanistan Standard Time" = {"+0430", "+0430"}, // Asia/Kabul
- "Russia Time Zone 11" = {"+12", "+12"}, // Asia/Kamchatka
- "Pakistan Standard Time" = {"PKT", "PKT"}, // Asia/Karachi
- "Nepal Standard Time" = {"+0545", "+0545"}, // Asia/Katmandu
- "North Asia Standard Time" = {"+07", "+07"}, // Asia/Krasnoyarsk
- "Magadan Standard Time" = {"+11", "+11"}, // Asia/Magadan
- "N. Central Asia Standard Time" = {"+07", "+07"}, // Asia/Novosibirsk
- "Omsk Standard Time" = {"+06", "+06"}, // Asia/Omsk
- "North Korea Standard Time" = {"KST", "KST"}, // Asia/Pyongyang
- "Qyzylorda Standard Time" = {"+05", "+05"}, // Asia/Qyzylorda
- "Myanmar Standard Time" = {"+0630", "+0630"}, // Asia/Rangoon
- "Arab Standard Time" = {"+03", "+03"}, // Asia/Riyadh
- "Sakhalin Standard Time" = {"+11", "+11"}, // Asia/Sakhalin
- "Korea Standard Time" = {"KST", "KST"}, // Asia/Seoul
- "China Standard Time" = {"CST", "CST"}, // Asia/Shanghai
- "Singapore Standard Time" = {"+08", "+08"}, // Asia/Singapore
- "Russia Time Zone 10" = {"+11", "+11"}, // Asia/Srednekolymsk
- "Taipei Standard Time" = {"CST", "CST"}, // Asia/Taipei
- "West Asia Standard Time" = {"+05", "+05"}, // Asia/Tashkent
- "Georgian Standard Time" = {"+04", "+04"}, // Asia/Tbilisi
- "Iran Standard Time" = {"+0330", "+0330"}, // Asia/Tehran
- "Tokyo Standard Time" = {"JST", "JST"}, // Asia/Tokyo
- "Tomsk Standard Time" = {"+07", "+07"}, // Asia/Tomsk
- "Ulaanbaatar Standard Time" = {"+08", "+08"}, // Asia/Ulaanbaatar
- "Vladivostok Standard Time" = {"+10", "+10"}, // Asia/Vladivostok
- "Yakutsk Standard Time" = {"+09", "+09"}, // Asia/Yakutsk
- "Ekaterinburg Standard Time" = {"+05", "+05"}, // Asia/Yekaterinburg
- "Caucasus Standard Time" = {"+04", "+04"}, // Asia/Yerevan
- "Azores Standard Time" = {"-01", "+00"}, // Atlantic/Azores
- "Cape Verde Standard Time" = {"-01", "-01"}, // Atlantic/Cape_Verde
- "Greenwich Standard Time" = {"GMT", "GMT"}, // Atlantic/Reykjavik
- "Cen. Australia Standard Time" = {"ACST", "ACDT"}, // Australia/Adelaide
- "E. Australia Standard Time" = {"AEST", "AEST"}, // Australia/Brisbane
- "AUS Central Standard Time" = {"ACST", "ACST"}, // Australia/Darwin
- "Aus Central W. Standard Time" = {"+0845", "+0845"}, // Australia/Eucla
- "Tasmania Standard Time" = {"AEST", "AEDT"}, // Australia/Hobart
- "Lord Howe Standard Time" = {"+1030", "+11"}, // Australia/Lord_Howe
- "W. Australia Standard Time" = {"AWST", "AWST"}, // Australia/Perth
- "AUS Eastern Standard Time" = {"AEST", "AEDT"}, // Australia/Sydney
- "UTC-11" = {"-11", "-11"}, // Etc/GMT+11
- "Dateline Standard Time" = {"-12", "-12"}, // Etc/GMT+12
- "UTC-02" = {"-02", "-02"}, // Etc/GMT+2
- "UTC-08" = {"-08", "-08"}, // Etc/GMT+8
- "UTC-09" = {"-09", "-09"}, // Etc/GMT+9
- "UTC+12" = {"+12", "+12"}, // Etc/GMT-12
- "UTC+13" = {"+13", "+13"}, // Etc/GMT-13
- "UTC" = {"UTC", "UTC"}, // Etc/UTC
- "Astrakhan Standard Time" = {"+04", "+04"}, // Europe/Astrakhan
- "W. Europe Standard Time" = {"CET", "CEST"}, // Europe/Berlin
- "GTB Standard Time" = {"EET", "EEST"}, // Europe/Bucharest
- "Central Europe Standard Time" = {"CET", "CEST"}, // Europe/Budapest
- "E. Europe Standard Time" = {"EET", "EEST"}, // Europe/Chisinau
- "Turkey Standard Time" = {"+03", "+03"}, // Europe/Istanbul
- "Kaliningrad Standard Time" = {"EET", "EET"}, // Europe/Kaliningrad
- "FLE Standard Time" = {"EET", "EEST"}, // Europe/Kiev
- "GMT Standard Time" = {"GMT", "BST"}, // Europe/London
- "Belarus Standard Time" = {"+03", "+03"}, // Europe/Minsk
- "Russian Standard Time" = {"MSK", "MSK"}, // Europe/Moscow
- "Romance Standard Time" = {"CET", "CEST"}, // Europe/Paris
- "Russia Time Zone 3" = {"+04", "+04"}, // Europe/Samara
- "Saratov Standard Time" = {"+04", "+04"}, // Europe/Saratov
- "Volgograd Standard Time" = {"MSK", "MSK"}, // Europe/Volgograd
- "Central European Standard Time" = {"CET", "CEST"}, // Europe/Warsaw
- "Mauritius Standard Time" = {"+04", "+04"}, // Indian/Mauritius
- "Samoa Standard Time" = {"+13", "+13"}, // Pacific/Apia
- "New Zealand Standard Time" = {"NZST", "NZDT"}, // Pacific/Auckland
- "Bougainville Standard Time" = {"+11", "+11"}, // Pacific/Bougainville
- "Chatham Islands Standard Time" = {"+1245", "+1345"}, // Pacific/Chatham
- "Easter Island Standard Time" = {"-06", "-05"}, // Pacific/Easter
- "Fiji Standard Time" = {"+12", "+12"}, // Pacific/Fiji
- "Central Pacific Standard Time" = {"+11", "+11"}, // Pacific/Guadalcanal
- "Hawaiian Standard Time" = {"HST", "HST"}, // Pacific/Honolulu
- "Line Islands Standard Time" = {"+14", "+14"}, // Pacific/Kiritimati
- "Marquesas Standard Time" = {"-0930", "-0930"}, // Pacific/Marquesas
- "Norfolk Standard Time" = {"+11", "+12"}, // Pacific/Norfolk
- "West Pacific Standard Time" = {"+10", "+10"}, // Pacific/Port_Moresby
- "Tonga Standard Time" = {"+13", "+13"}, // Pacific/Tongatapu
+@(rodata)
+tz_abbrevs := [?]struct{key: string, value: TZ_Abbrev}{
+ {"Egypt Standard Time", {"EET", "EEST"}}, // Africa/Cairo
+ {"Morocco Standard Time", {"+00", "+01"}}, // Africa/Casablanca
+ {"South Africa Standard Time", {"SAST", "SAST"}}, // Africa/Johannesburg
+ {"South Sudan Standard Time", {"CAT", "CAT"}}, // Africa/Juba
+ {"Sudan Standard Time", {"CAT", "CAT"}}, // Africa/Khartoum
+ {"W. Central Africa Standard Time", {"WAT", "WAT"}}, // Africa/Lagos
+ {"E. Africa Standard Time", {"EAT", "EAT"}}, // Africa/Nairobi
+ {"Sao Tome Standard Time", {"GMT", "GMT"}}, // Africa/Sao_Tome
+ {"Libya Standard Time", {"EET", "EET"}}, // Africa/Tripoli
+ {"Namibia Standard Time", {"CAT", "CAT"}}, // Africa/Windhoek
+ {"Aleutian Standard Time", {"HST", "HDT"}}, // America/Adak
+ {"Alaskan Standard Time", {"AKST", "AKDT"}}, // America/Anchorage
+ {"Tocantins Standard Time", {"-03", "-03"}}, // America/Araguaina
+ {"Paraguay Standard Time", {"-04", "-03"}}, // America/Asuncion
+ {"Bahia Standard Time", {"-03", "-03"}}, // America/Bahia
+ {"SA Pacific Standard Time", {"-05", "-05"}}, // America/Bogota
+ {"Argentina Standard Time", {"-03", "-03"}}, // America/Buenos_Aires
+ {"Eastern Standard Time (Mexico)", {"EST", "EST"}}, // America/Cancun
+ {"Venezuela Standard Time", {"-04", "-04"}}, // America/Caracas
+ {"SA Eastern Standard Time", {"-03", "-03"}}, // America/Cayenne
+ {"Central Standard Time", {"CST", "CDT"}}, // America/Chicago
+ {"Central Brazilian Standard Time", {"-04", "-04"}}, // America/Cuiaba
+ {"Mountain Standard Time", {"MST", "MDT"}}, // America/Denver
+ {"Greenland Standard Time", {"-03", "-02"}}, // America/Godthab
+ {"Turks And Caicos Standard Time", {"EST", "EDT"}}, // America/Grand_Turk
+ {"Central America Standard Time", {"CST", "CST"}}, // America/Guatemala
+ {"Atlantic Standard Time", {"AST", "ADT"}}, // America/Halifax
+ {"Cuba Standard Time", {"CST", "CDT"}}, // America/Havana
+ {"US Eastern Standard Time", {"EST", "EDT"}}, // America/Indianapolis
+ {"SA Western Standard Time", {"-04", "-04"}}, // America/La_Paz
+ {"Pacific Standard Time", {"PST", "PDT"}}, // America/Los_Angeles
+ {"Mountain Standard Time (Mexico)", {"MST", "MST"}}, // America/Mazatlan
+ {"Central Standard Time (Mexico)", {"CST", "CST"}}, // America/Mexico_City
+ {"Saint Pierre Standard Time", {"-03", "-02"}}, // America/Miquelon
+ {"Montevideo Standard Time", {"-03", "-03"}}, // America/Montevideo
+ {"Eastern Standard Time", {"EST", "EDT"}}, // America/New_York
+ {"US Mountain Standard Time", {"MST", "MST"}}, // America/Phoenix
+ {"Haiti Standard Time", {"EST", "EDT"}}, // America/Port-au-Prince
+ {"Magallanes Standard Time", {"-03", "-03"}}, // America/Punta_Arenas
+ {"Canada Central Standard Time", {"CST", "CST"}}, // America/Regina
+ {"Pacific SA Standard Time", {"-04", "-03"}}, // America/Santiago
+ {"E. South America Standard Time", {"-03", "-03"}}, // America/Sao_Paulo
+ {"Newfoundland Standard Time", {"NST", "NDT"}}, // America/St_Johns
+ {"Pacific Standard Time (Mexico)", {"PST", "PDT"}}, // America/Tijuana
+ {"Yukon Standard Time", {"MST", "MST"}}, // America/Whitehorse
+ {"Central Asia Standard Time", {"+06", "+06"}}, // Asia/Almaty
+ {"Jordan Standard Time", {"+03", "+03"}}, // Asia/Amman
+ {"Arabic Standard Time", {"+03", "+03"}}, // Asia/Baghdad
+ {"Azerbaijan Standard Time", {"+04", "+04"}}, // Asia/Baku
+ {"SE Asia Standard Time", {"+07", "+07"}}, // Asia/Bangkok
+ {"Altai Standard Time", {"+07", "+07"}}, // Asia/Barnaul
+ {"Middle East Standard Time", {"EET", "EEST"}}, // Asia/Beirut
+ {"India Standard Time", {"IST", "IST"}}, // Asia/Calcutta
+ {"Transbaikal Standard Time", {"+09", "+09"}}, // Asia/Chita
+ {"Sri Lanka Standard Time", {"+0530", "+0530"}}, // Asia/Colombo
+ {"Syria Standard Time", {"+03", "+03"}}, // Asia/Damascus
+ {"Bangladesh Standard Time", {"+06", "+06"}}, // Asia/Dhaka
+ {"Arabian Standard Time", {"+04", "+04"}}, // Asia/Dubai
+ {"West Bank Standard Time", {"EET", "EEST"}}, // Asia/Hebron
+ {"W. Mongolia Standard Time", {"+07", "+07"}}, // Asia/Hovd
+ {"North Asia East Standard Time", {"+08", "+08"}}, // Asia/Irkutsk
+ {"Israel Standard Time", {"IST", "IDT"}}, // Asia/Jerusalem
+ {"Afghanistan Standard Time", {"+0430", "+0430"}}, // Asia/Kabul
+ {"Russia Time Zone 11", {"+12", "+12"}}, // Asia/Kamchatka
+ {"Pakistan Standard Time", {"PKT", "PKT"}}, // Asia/Karachi
+ {"Nepal Standard Time", {"+0545", "+0545"}}, // Asia/Katmandu
+ {"North Asia Standard Time", {"+07", "+07"}}, // Asia/Krasnoyarsk
+ {"Magadan Standard Time", {"+11", "+11"}}, // Asia/Magadan
+ {"N. Central Asia Standard Time", {"+07", "+07"}}, // Asia/Novosibirsk
+ {"Omsk Standard Time", {"+06", "+06"}}, // Asia/Omsk
+ {"North Korea Standard Time", {"KST", "KST"}}, // Asia/Pyongyang
+ {"Qyzylorda Standard Time", {"+05", "+05"}}, // Asia/Qyzylorda
+ {"Myanmar Standard Time", {"+0630", "+0630"}}, // Asia/Rangoon
+ {"Arab Standard Time", {"+03", "+03"}}, // Asia/Riyadh
+ {"Sakhalin Standard Time", {"+11", "+11"}}, // Asia/Sakhalin
+ {"Korea Standard Time", {"KST", "KST"}}, // Asia/Seoul
+ {"China Standard Time", {"CST", "CST"}}, // Asia/Shanghai
+ {"Singapore Standard Time", {"+08", "+08"}}, // Asia/Singapore
+ {"Russia Time Zone 10", {"+11", "+11"}}, // Asia/Srednekolymsk
+ {"Taipei Standard Time", {"CST", "CST"}}, // Asia/Taipei
+ {"West Asia Standard Time", {"+05", "+05"}}, // Asia/Tashkent
+ {"Georgian Standard Time", {"+04", "+04"}}, // Asia/Tbilisi
+ {"Iran Standard Time", {"+0330", "+0330"}}, // Asia/Tehran
+ {"Tokyo Standard Time", {"JST", "JST"}}, // Asia/Tokyo
+ {"Tomsk Standard Time", {"+07", "+07"}}, // Asia/Tomsk
+ {"Ulaanbaatar Standard Time", {"+08", "+08"}}, // Asia/Ulaanbaatar
+ {"Vladivostok Standard Time", {"+10", "+10"}}, // Asia/Vladivostok
+ {"Yakutsk Standard Time", {"+09", "+09"}}, // Asia/Yakutsk
+ {"Ekaterinburg Standard Time", {"+05", "+05"}}, // Asia/Yekaterinburg
+ {"Caucasus Standard Time", {"+04", "+04"}}, // Asia/Yerevan
+ {"Azores Standard Time", {"-01", "+00"}}, // Atlantic/Azores
+ {"Cape Verde Standard Time", {"-01", "-01"}}, // Atlantic/Cape_Verde
+ {"Greenwich Standard Time", {"GMT", "GMT"}}, // Atlantic/Reykjavik
+ {"Cen. Australia Standard Time", {"ACST", "ACDT"}}, // Australia/Adelaide
+ {"E. Australia Standard Time", {"AEST", "AEST"}}, // Australia/Brisbane
+ {"AUS Central Standard Time", {"ACST", "ACST"}}, // Australia/Darwin
+ {"Aus Central W. Standard Time", {"+0845", "+0845"}}, // Australia/Eucla
+ {"Tasmania Standard Time", {"AEST", "AEDT"}}, // Australia/Hobart
+ {"Lord Howe Standard Time", {"+1030", "+11"}}, // Australia/Lord_Howe
+ {"W. Australia Standard Time", {"AWST", "AWST"}}, // Australia/Perth
+ {"AUS Eastern Standard Time", {"AEST", "AEDT"}}, // Australia/Sydney
+ {"UTC-11", {"-11", "-11"}}, // Etc/GMT+11
+ {"Dateline Standard Time", {"-12", "-12"}}, // Etc/GMT+12
+ {"UTC-02", {"-02", "-02"}}, // Etc/GMT+2
+ {"UTC-08", {"-08", "-08"}}, // Etc/GMT+8
+ {"UTC-09", {"-09", "-09"}}, // Etc/GMT+9
+ {"UTC+12", {"+12", "+12"}}, // Etc/GMT-12
+ {"UTC+13", {"+13", "+13"}}, // Etc/GMT-13
+ {"UTC", {"UTC", "UTC"}}, // Etc/UTC
+ {"Astrakhan Standard Time", {"+04", "+04"}}, // Europe/Astrakhan
+ {"W. Europe Standard Time", {"CET", "CEST"}}, // Europe/Berlin
+ {"GTB Standard Time", {"EET", "EEST"}}, // Europe/Bucharest
+ {"Central Europe Standard Time", {"CET", "CEST"}}, // Europe/Budapest
+ {"E. Europe Standard Time", {"EET", "EEST"}}, // Europe/Chisinau
+ {"Turkey Standard Time", {"+03", "+03"}}, // Europe/Istanbul
+ {"Kaliningrad Standard Time", {"EET", "EET"}}, // Europe/Kaliningrad
+ {"FLE Standard Time", {"EET", "EEST"}}, // Europe/Kiev
+ {"GMT Standard Time", {"GMT", "BST"}}, // Europe/London
+ {"Belarus Standard Time", {"+03", "+03"}}, // Europe/Minsk
+ {"Russian Standard Time", {"MSK", "MSK"}}, // Europe/Moscow
+ {"Romance Standard Time", {"CET", "CEST"}}, // Europe/Paris
+ {"Russia Time Zone 3", {"+04", "+04"}}, // Europe/Samara
+ {"Saratov Standard Time", {"+04", "+04"}}, // Europe/Saratov
+ {"Volgograd Standard Time", {"MSK", "MSK"}}, // Europe/Volgograd
+ {"Central European Standard Time", {"CET", "CEST"}}, // Europe/Warsaw
+ {"Mauritius Standard Time", {"+04", "+04"}}, // Indian/Mauritius
+ {"Samoa Standard Time", {"+13", "+13"}}, // Pacific/Apia
+ {"New Zealand Standard Time", {"NZST", "NZDT"}}, // Pacific/Auckland
+ {"Bougainville Standard Time", {"+11", "+11"}}, // Pacific/Bougainville
+ {"Chatham Islands Standard Time", {"+1245", "+1345"}}, // Pacific/Chatham
+ {"Easter Island Standard Time", {"-06", "-05"}}, // Pacific/Easter
+ {"Fiji Standard Time", {"+12", "+12"}}, // Pacific/Fiji
+ {"Central Pacific Standard Time", {"+11", "+11"}}, // Pacific/Guadalcanal
+ {"Hawaiian Standard Time", {"HST", "HST"}}, // Pacific/Honolulu
+ {"Line Islands Standard Time", {"+14", "+14"}}, // Pacific/Kiritimati
+ {"Marquesas Standard Time", {"-0930", "-0930"}}, // Pacific/Marquesas
+ {"Norfolk Standard Time", {"+11", "+12"}}, // Pacific/Norfolk
+ {"West Pacific Standard Time", {"+10", "+10"}}, // Pacific/Port_Moresby
+ {"Tonga Standard Time", {"+13", "+13"}}, // Pacific/Tongatapu
}
iana_to_windows_tz :: proc(iana_name: string, allocator := context.allocator) -> (name: string, success: bool) {
@@ -269,7 +270,18 @@ _region_load :: proc(reg_str: string, allocator := context.allocator) -> (out_re
defer delete(wintz_name, allocator)
defer delete(iana_name, allocator)
- abbrevs := tz_abbrevs[wintz_name] or_return
+ abbrevs: TZ_Abbrev
+ abbrevs_ok: bool
+ for pair in tz_abbrevs {
+ if pair.key == wintz_name {
+ abbrevs = pair.value
+ abbrevs_ok = true
+ break
+ }
+ }
+ if !abbrevs_ok {
+ return
+ }
if abbrevs.std == "UTC" && abbrevs.dst == abbrevs.std {
return nil, true
}
diff --git a/core/time/timezone/tzdate.odin b/core/time/timezone/tzdate.odin
index 96df44299..e62400889 100644
--- a/core/time/timezone/tzdate.odin
+++ b/core/time/timezone/tzdate.odin
@@ -168,7 +168,7 @@ process_rrule :: proc(rrule: datetime.TZ_RRule, tm: time.Time) -> (out: datetime
},
}
record_sort_proc :: proc(i, j: datetime.TZ_Record) -> bool {
- return i.time > j.time
+ return i.time < j.time
}
slice.sort_by(records, record_sort_proc)
@@ -179,7 +179,7 @@ process_rrule :: proc(rrule: datetime.TZ_RRule, tm: time.Time) -> (out: datetime
}
}
- return records[len(records)-1], true
+ return records[0], true
}
datetime_to_utc :: proc(dt: datetime.DateTime) -> (out: datetime.DateTime, success: bool) #optional_ok {