aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorgingerBill <bill@gingerbill.org>2024-03-08 13:20:33 +0000
committergingerBill <bill@gingerbill.org>2024-03-08 13:20:33 +0000
commit032e193d0d9602081ab125bd56cd62c0be0de53b (patch)
tree81d15ce3cbc7d9cefb9044404a7801b3c51b24a5
parent0f83ab466fc733094bfe46bb2915ad54b67957a6 (diff)
parent53ce94503422b904ae52990c7d8bab292413b2bc (diff)
Merge branch 'master' of https://github.com/odin-lang/Odin
-rw-r--r--base/runtime/entry_unix.odin2
-rw-r--r--base/runtime/heap_allocator_unix.odin4
-rw-r--r--base/runtime/os_specific_haiku.odin21
-rwxr-xr-xbuild_odin.sh5
-rw-r--r--core/c/libc/errno.odin18
-rw-r--r--core/c/libc/stdio.odin30
-rw-r--r--core/c/libc/time.odin2
-rw-r--r--core/c/libc/wctype.odin6
-rw-r--r--core/os/os_haiku.odin435
-rw-r--r--core/os/stat_unix.odin2
-rw-r--r--core/os/stream.odin6
-rw-r--r--core/sync/futex_haiku.odin167
-rw-r--r--core/sync/primitives_haiku.odin8
-rw-r--r--core/sys/haiku/errors.odin239
-rw-r--r--core/sys/haiku/find_directory.odin168
-rw-r--r--core/sys/haiku/os.odin502
-rw-r--r--core/sys/haiku/types.odin54
-rw-r--r--core/sys/unix/pthread_haiku.odin71
-rw-r--r--core/sys/unix/pthread_unix.odin24
-rw-r--r--core/sys/unix/time_unix.odin2
-rw-r--r--core/sys/windows/gdi32.odin45
-rw-r--r--core/sys/windows/kernel32.odin19
-rw-r--r--core/sys/windows/shell32.odin51
-rw-r--r--core/sys/windows/user32.odin80
-rw-r--r--core/sys/windows/winmm.odin206
-rw-r--r--core/thread/thread_unix.odin13
-rw-r--r--core/time/time_unix.odin2
-rw-r--r--src/build_settings.cpp71
-rw-r--r--src/check_builtin.cpp1
-rw-r--r--src/checker.cpp1
-rw-r--r--src/gb/gb.h49
-rw-r--r--src/linker.cpp4
-rw-r--r--src/llvm_backend.cpp4
-rw-r--r--src/path.cpp922
-rw-r--r--src/threading.cpp174
-rw-r--r--src/tilde.cpp1
36 files changed, 2912 insertions, 497 deletions
diff --git a/base/runtime/entry_unix.odin b/base/runtime/entry_unix.odin
index 233007936..e49698e6e 100644
--- a/base/runtime/entry_unix.odin
+++ b/base/runtime/entry_unix.odin
@@ -1,5 +1,5 @@
//+private
-//+build linux, darwin, freebsd, openbsd
+//+build linux, darwin, freebsd, openbsd, haiku
//+no-instrumentation
package runtime
diff --git a/base/runtime/heap_allocator_unix.odin b/base/runtime/heap_allocator_unix.odin
index bfbbb5303..2b6698885 100644
--- a/base/runtime/heap_allocator_unix.odin
+++ b/base/runtime/heap_allocator_unix.odin
@@ -1,4 +1,4 @@
-//+build linux, darwin, freebsd, openbsd
+//+build linux, darwin, freebsd, openbsd, haiku
//+private
package runtime
@@ -35,4 +35,4 @@ _heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
_heap_free :: proc(ptr: rawptr) {
_unix_free(ptr)
-} \ No newline at end of file
+}
diff --git a/base/runtime/os_specific_haiku.odin b/base/runtime/os_specific_haiku.odin
new file mode 100644
index 000000000..f8dafac3d
--- /dev/null
+++ b/base/runtime/os_specific_haiku.odin
@@ -0,0 +1,21 @@
+//+build haiku
+//+private
+package runtime
+
+foreign import libc "system:c"
+
+foreign libc {
+ @(link_name="write")
+ _unix_write :: proc(fd: i32, buf: rawptr, size: int) -> int ---
+
+ _errnop :: proc() -> ^i32 ---
+}
+
+_stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
+ ret := _unix_write(2, raw_data(data), len(data))
+ if ret < len(data) {
+ err := _errnop()
+ return int(ret), _OS_Errno(err^ if err != nil else 0)
+ }
+ return int(ret), 0
+}
diff --git a/build_odin.sh b/build_odin.sh
index fab6c5fd1..93319b4ef 100755
--- a/build_odin.sh
+++ b/build_odin.sh
@@ -82,6 +82,11 @@ OpenBSD)
LDFLAGS="$LDFLAGS -liconv"
LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)"
;;
+Haiku)
+ CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags) -I/system/develop/headers/private/shared -I/system/develop/headers/private/kernel"
+ LDFLAGS="$LDFLAGS -liconv"
+ LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)"
+ ;;
*)
error "Platform \"$OS_NAME\" unsupported"
;;
diff --git a/core/c/libc/errno.odin b/core/c/libc/errno.odin
index fe6fbb073..7af763706 100644
--- a/core/c/libc/errno.odin
+++ b/core/c/libc/errno.odin
@@ -80,6 +80,24 @@ when ODIN_OS == .Darwin {
ERANGE :: 34
}
+when ODIN_OS == .Haiku {
+ @(private="file")
+ @(default_calling_convention="c")
+ foreign libc {
+ @(link_name="_errnop")
+ _get_errno :: proc() -> ^int ---
+ }
+
+ @(private="file")
+ B_GENERAL_ERROR_BASE :: min(i32)
+ @(private="file")
+ B_POSIX_ERROR_BASE :: B_GENERAL_ERROR_BASE + 0x7000
+
+ EDOM :: B_POSIX_ERROR_BASE + 16
+ EILSEQ :: B_POSIX_ERROR_BASE + 38
+ ERANGE :: B_POSIX_ERROR_BASE + 17
+}
+
// Odin has no way to make an identifier "errno" behave as a function call to
// read the value, or to produce an lvalue such that you can assign a different
// error value to errno. To work around this, just expose it as a function like
diff --git a/core/c/libc/stdio.odin b/core/c/libc/stdio.odin
index 39969e4a8..b83ddecc8 100644
--- a/core/c/libc/stdio.odin
+++ b/core/c/libc/stdio.odin
@@ -163,6 +163,36 @@ when ODIN_OS == .Darwin {
}
}
+when ODIN_OS == .Haiku {
+ fpos_t :: distinct i64
+
+ _IOFBF :: 0
+ _IOLBF :: 1
+ _IONBF :: 2
+
+ BUFSIZ :: 8192
+
+ EOF :: int(-1)
+
+ FOPEN_MAX :: 128
+
+ FILENAME_MAX :: 256
+
+ L_tmpnam :: 512
+
+ SEEK_SET :: 0
+ SEEK_CUR :: 1
+ SEEK_END :: 2
+
+ TMP_MAX :: 32768
+
+ foreign libc {
+ stderr: ^FILE
+ stdin: ^FILE
+ stdout: ^FILE
+ }
+}
+
@(default_calling_convention="c")
foreign libc {
// 7.21.4 Operations on files
diff --git a/core/c/libc/time.odin b/core/c/libc/time.odin
index 72b899546..4c4280f30 100644
--- a/core/c/libc/time.odin
+++ b/core/c/libc/time.odin
@@ -45,7 +45,7 @@ when ODIN_OS == .Windows {
}
}
-when ODIN_OS == .Linux || ODIN_OS == .FreeBSD || ODIN_OS == .Darwin || ODIN_OS == .OpenBSD {
+when ODIN_OS == .Linux || ODIN_OS == .FreeBSD || ODIN_OS == .Darwin || ODIN_OS == .OpenBSD || ODIN_OS == .Haiku {
@(default_calling_convention="c")
foreign libc {
// 7.27.2 Time manipulation functions
diff --git a/core/c/libc/wctype.odin b/core/c/libc/wctype.odin
index 43aee9dc6..cbce220d4 100644
--- a/core/c/libc/wctype.odin
+++ b/core/c/libc/wctype.odin
@@ -29,7 +29,11 @@ when ODIN_OS == .Windows {
} else when ODIN_OS == .FreeBSD {
wctrans_t :: distinct int
wctype_t :: distinct ulong
-
+
+} else when ODIN_OS == .Haiku {
+ wctrans_t :: distinct i32
+ wctype_t :: distinct i32
+
}
@(default_calling_convention="c")
diff --git a/core/os/os_haiku.odin b/core/os/os_haiku.odin
new file mode 100644
index 000000000..06052fc42
--- /dev/null
+++ b/core/os/os_haiku.odin
@@ -0,0 +1,435 @@
+package os
+
+foreign import libc "system:c"
+
+import "base:runtime"
+import "core:c"
+import "core:strings"
+import "core:sys/haiku"
+
+Handle :: i32
+Pid :: i32
+File_Time :: i64
+Errno :: i32
+
+MAX_PATH :: haiku.PATH_MAX
+
+ENOSYS :: int(haiku.Errno.POSIX_ERROR_BASE) + 9
+
+INVALID_HANDLE :: ~Handle(0)
+
+ERROR_NONE: Errno: 0
+
+stdin: Handle = 0
+stdout: Handle = 1
+stderr: Handle = 2
+
+pid_t :: haiku.pid_t
+off_t :: haiku.off_t
+dev_t :: haiku.dev_t
+ino_t :: haiku.ino_t
+mode_t :: haiku.mode_t
+nlink_t :: haiku.nlink_t
+uid_t :: haiku.uid_t
+gid_t :: haiku.gid_t
+blksize_t :: haiku.blksize_t
+blkcnt_t :: haiku.blkcnt_t
+time_t :: haiku.time_t
+
+
+Unix_File_Time :: struct {
+ seconds: time_t,
+ nanoseconds: c.long,
+}
+
+OS_Stat :: struct {
+ device_id: dev_t, // device ID that this file resides on
+ serial: ino_t, // this file's serial inode ID
+ mode: mode_t, // file mode (rwx for user, group, etc)
+ nlink: nlink_t, // number of hard links to this file
+ uid: uid_t, // user ID of the file's owner
+ gid: gid_t, // group ID of the file's group
+ size: off_t, // file size, in bytes
+ rdev: dev_t, // device type (not used)
+ block_size: blksize_t, // optimal blocksize for I/O
+
+ last_access: Unix_File_Time, // time of last access
+ modified: Unix_File_Time, // time of last data modification
+ status_change: Unix_File_Time, // time of last file status change
+ birthtime: Unix_File_Time, // time of file creation
+
+ type: u32, // attribute/index type
+
+ blocks: blkcnt_t, // blocks allocated for file
+}
+
+/* file access modes for open() */
+O_RDONLY :: 0x0000 /* read only */
+O_WRONLY :: 0x0001 /* write only */
+O_RDWR :: 0x0002 /* read and write */
+O_ACCMODE :: 0x0003 /* mask to get the access modes above */
+O_RWMASK :: O_ACCMODE
+
+/* flags for open() */
+O_EXCL :: 0x0100 /* exclusive creat */
+O_CREATE :: 0x0200 /* create and open file */
+O_TRUNC :: 0x0400 /* open with truncation */
+O_NOCTTY :: 0x1000 /* don't make tty the controlling tty */
+O_NOTRAVERSE :: 0x2000 /* do not traverse leaf link */
+
+// File type
+S_IFMT :: 0o170000 // Type of file mask
+S_IFIFO :: 0o010000 // Named pipe (fifo)
+S_IFCHR :: 0o020000 // Character special
+S_IFDIR :: 0o040000 // Directory
+S_IFBLK :: 0o060000 // Block special
+S_IFREG :: 0o100000 // Regular
+S_IFLNK :: 0o120000 // Symbolic link
+S_IFSOCK :: 0o140000 // Socket
+S_ISVTX :: 0o001000 // Save swapped text even after use
+
+// File mode
+ // Read, write, execute/search by owner
+S_IRWXU :: 0o0700 // RWX mask for owner
+S_IRUSR :: 0o0400 // R for owner
+S_IWUSR :: 0o0200 // W for owner
+S_IXUSR :: 0o0100 // X for owner
+
+ // Read, write, execute/search by group
+S_IRWXG :: 0o0070 // RWX mask for group
+S_IRGRP :: 0o0040 // R for group
+S_IWGRP :: 0o0020 // W for group
+S_IXGRP :: 0o0010 // X for group
+
+ // Read, write, execute/search by others
+S_IRWXO :: 0o0007 // RWX mask for other
+S_IROTH :: 0o0004 // R for other
+S_IWOTH :: 0o0002 // W for other
+S_IXOTH :: 0o0001 // X for other
+
+S_ISUID :: 0o4000 // Set user id on execution
+S_ISGID :: 0o2000 // Set group id on execution
+S_ISTXT :: 0o1000 // Sticky bit
+
+S_ISLNK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFLNK }
+S_ISREG :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFREG }
+S_ISDIR :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFDIR }
+S_ISCHR :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFCHR }
+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 }
+
+
+foreign libc {
+ @(link_name="_errnop") __error :: proc() -> ^c.int ---
+
+ @(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, mode: c.int) -> 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="write") _unix_write :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t ---
+ @(link_name="lseek") _unix_seek :: proc(fd: Handle, offset: off_t, whence: c.int) -> off_t ---
+ @(link_name="stat") _unix_stat :: proc(path: cstring, sb: ^OS_Stat) -> c.int ---
+ @(link_name="fstat") _unix_fstat :: proc(fd: Handle, sb: ^OS_Stat) -> c.int ---
+ @(link_name="lstat") _unix_lstat :: proc(path: cstring, sb: ^OS_Stat) -> c.int ---
+ @(link_name="readlink") _unix_readlink :: proc(path: cstring, buf: ^byte, bufsiz: c.size_t) -> c.ssize_t ---
+ @(link_name="access") _unix_access :: proc(path: cstring, mask: c.int) -> c.int ---
+ @(link_name="getcwd") _unix_getcwd :: proc(buf: cstring, len: c.size_t) -> cstring ---
+ @(link_name="chdir") _unix_chdir :: proc(path: cstring) -> c.int ---
+ @(link_name="rename") _unix_rename :: proc(old, new: cstring) -> c.int ---
+ @(link_name="unlink") _unix_unlink :: proc(path: cstring) -> c.int ---
+ @(link_name="rmdir") _unix_rmdir :: proc(path: cstring) -> c.int ---
+ @(link_name="mkdir") _unix_mkdir :: proc(path: cstring, mode: mode_t) -> c.int ---
+
+ @(link_name="getpagesize") _unix_getpagesize :: proc() -> c.int ---
+ @(link_name="sysconf") _sysconf :: proc(name: c.int) -> c.long ---
+ @(link_name="fdopendir") _unix_fdopendir :: proc(fd: Handle) -> Dir ---
+ @(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="malloc") _unix_malloc :: proc(size: c.size_t) -> rawptr ---
+ @(link_name="calloc") _unix_calloc :: proc(num, size: c.size_t) -> rawptr ---
+ @(link_name="free") _unix_free :: proc(ptr: rawptr) ---
+ @(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="exit") _unix_exit :: proc(status: c.int) -> ! ---
+
+ @(link_name="dlopen") _unix_dlopen :: proc(filename: cstring, flags: c.int) -> rawptr ---
+ @(link_name="dlsym") _unix_dlsym :: proc(handle: rawptr, symbol: cstring) -> rawptr ---
+ @(link_name="dlclose") _unix_dlclose :: proc(handle: rawptr) -> c.int ---
+ @(link_name="dlerror") _unix_dlerror :: proc() -> cstring ---
+}
+
+MAXNAMLEN :: haiku.NAME_MAX
+
+Dirent :: struct {
+ dev: dev_t,
+ pdef: dev_t,
+ ino: ino_t,
+ pino: ino_t,
+ reclen: u16,
+ name: [MAXNAMLEN + 1]byte, // name
+}
+
+Dir :: distinct rawptr // DIR*
+
+is_path_separator :: proc(r: rune) -> bool {
+ return r == '/'
+}
+
+get_last_error :: proc "contextless" () -> int {
+ return int(__error()^)
+}
+
+fork :: proc() -> (Pid, Errno) {
+ pid := _unix_fork()
+ if pid == -1 {
+ return Pid(-1), Errno(get_last_error())
+ }
+ return Pid(pid), ERROR_NONE
+}
+
+open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Errno) {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
+ cstr := strings.clone_to_cstring(path, context.temp_allocator)
+ handle := _unix_open(cstr, c.int(flags), c.int(mode))
+ if handle == -1 {
+ return INVALID_HANDLE, Errno(get_last_error())
+ }
+ return handle, ERROR_NONE
+}
+
+close :: proc(fd: Handle) -> Errno {
+ result := _unix_close(fd)
+ if result == -1 {
+ return Errno(get_last_error())
+ }
+ return ERROR_NONE
+}
+
+// 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.
+@(private)
+MAX_RW :: 1 << 30
+
+read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
+ to_read := min(c.size_t(len(data)), MAX_RW)
+ bytes_read := _unix_read(fd, &data[0], to_read)
+ if bytes_read == -1 {
+ return -1, Errno(get_last_error())
+ }
+ return int(bytes_read), ERROR_NONE
+}
+
+write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
+ if len(data) == 0 {
+ return 0, ERROR_NONE
+ }
+
+ to_write := min(c.size_t(len(data)), MAX_RW)
+ bytes_written := _unix_write(fd, &data[0], to_write)
+ if bytes_written == -1 {
+ return -1, Errno(get_last_error())
+ }
+ return int(bytes_written), ERROR_NONE
+}
+
+seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
+ res := _unix_seek(fd, offset, c.int(whence))
+ if res == -1 {
+ return -1, Errno(get_last_error())
+ }
+ return res, ERROR_NONE
+}
+
+file_size :: proc(fd: Handle) -> (i64, Errno) {
+ s, err := _fstat(fd)
+ if err != ERROR_NONE {
+ return -1, err
+ }
+ return s.size, ERROR_NONE
+}
+
+// "Argv" arguments converted to Odin strings
+args := _alloc_command_line_arguments()
+
+_alloc_command_line_arguments :: proc() -> []string {
+ res := make([]string, len(runtime.args__))
+ for arg, i in runtime.args__ {
+ res[i] = string(arg)
+ }
+ return res
+}
+
+@private
+_stat :: proc(path: string) -> (OS_Stat, Errno) {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
+ cstr := strings.clone_to_cstring(path, context.temp_allocator)
+
+ // deliberately uninitialized
+ s: OS_Stat = ---
+ res := _unix_stat(cstr, &s)
+ if res == -1 {
+ return s, Errno(get_last_error())
+ }
+ return s, ERROR_NONE
+}
+
+@private
+_lstat :: proc(path: string) -> (OS_Stat, Errno) {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
+ cstr := strings.clone_to_cstring(path, context.temp_allocator)
+
+ // deliberately uninitialized
+ s: OS_Stat = ---
+ res := _unix_lstat(cstr, &s)
+ if res == -1 {
+ return s, Errno(get_last_error())
+ }
+ return s, ERROR_NONE
+}
+
+@private
+_fstat :: proc(fd: Handle) -> (OS_Stat, Errno) {
+ // deliberately uninitialized
+ s: OS_Stat = ---
+ res := _unix_fstat(fd, &s)
+ if res == -1 {
+ return s, Errno(get_last_error())
+ }
+ return s, ERROR_NONE
+}
+
+@private
+_fdopendir :: proc(fd: Handle) -> (Dir, Errno) {
+ dirp := _unix_fdopendir(fd)
+ if dirp == cast(Dir)nil {
+ return nil, Errno(get_last_error())
+ }
+ return dirp, ERROR_NONE
+}
+
+@private
+_closedir :: proc(dirp: Dir) -> Errno {
+ rc := _unix_closedir(dirp)
+ if rc != 0 {
+ return Errno(get_last_error())
+ }
+ return ERROR_NONE
+}
+
+@private
+_rewinddir :: proc(dirp: Dir) {
+ _unix_rewinddir(dirp)
+}
+
+@private
+_readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Errno, end_of_stream: bool) {
+ result: ^Dirent
+ rc := _unix_readdir_r(dirp, &entry, &result)
+
+ if rc != 0 {
+ err = Errno(get_last_error())
+ return
+ }
+ err = ERROR_NONE
+
+ if result == nil {
+ end_of_stream = true
+ return
+ }
+
+ return
+}
+
+@private
+_readlink :: proc(path: string) -> (string, Errno) {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator)
+ path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
+
+ bufsz : uint = MAX_PATH
+ buf := make([]byte, MAX_PATH)
+ for {
+ rc := _unix_readlink(path_cstr, &(buf[0]), bufsz)
+ if rc == -1 {
+ delete(buf)
+ return "", Errno(get_last_error())
+ } else if rc == int(bufsz) {
+ bufsz += MAX_PATH
+ delete(buf)
+ buf = make([]byte, bufsz)
+ } else {
+ return strings.string_from_ptr(&buf[0], rc), ERROR_NONE
+ }
+ }
+}
+
+absolute_path_from_handle :: proc(fd: Handle) -> (string, Errno) {
+ return "", Errno(ENOSYS)
+}
+
+absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) {
+ rel := rel
+ if rel == "" {
+ rel = "."
+ }
+
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator)
+ rel_cstr := strings.clone_to_cstring(rel, context.temp_allocator)
+
+ path_ptr := _unix_realpath(rel_cstr, nil)
+ if path_ptr == nil {
+ return "", Errno(get_last_error())
+ }
+ defer _unix_free(path_ptr)
+
+ path_cstr := transmute(cstring)path_ptr
+ path = strings.clone( string(path_cstr) )
+
+ return path, ERROR_NONE
+}
+
+access :: proc(path: string, mask: int) -> (bool, Errno) {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
+ cstr := strings.clone_to_cstring(path, context.temp_allocator)
+ res := _unix_access(cstr, c.int(mask))
+ if res == -1 {
+ return false, Errno(get_last_error())
+ }
+ return true, ERROR_NONE
+}
+
+lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
+ path_str := strings.clone_to_cstring(key, context.temp_allocator)
+ cstr := _unix_getenv(path_str)
+ if cstr == nil {
+ return "", false
+ }
+ return strings.clone(string(cstr), allocator), true
+}
+
+get_env :: proc(key: string, allocator := context.allocator) -> (value: string) {
+ value, _ = lookup_env(key, allocator)
+ return
+}
+
+@(private)
+_processor_core_count :: proc() -> int {
+ info: haiku.system_info
+ haiku.get_system_info(&info)
+ return int(info.cpu_count)
+}
+
+exit :: proc "contextless" (code: int) -> ! {
+ runtime._cleanup_runtime_contextless()
+ _unix_exit(i32(code))
+}
diff --git a/core/os/stat_unix.odin b/core/os/stat_unix.odin
index dae7ab2fb..5e83c0e16 100644
--- a/core/os/stat_unix.odin
+++ b/core/os/stat_unix.odin
@@ -1,4 +1,4 @@
-//+build linux, darwin, freebsd, openbsd
+//+build linux, darwin, freebsd, openbsd, haiku
package os
import "core:time"
diff --git a/core/os/stream.odin b/core/os/stream.odin
index d7ce11d26..25f31218c 100644
--- a/core/os/stream.odin
+++ b/core/os/stream.odin
@@ -32,7 +32,7 @@ _file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte,
}
case .Read_At:
- when !(ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD) {
+ when !(ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Haiku) {
n_int, os_err = read_at(fd, p, offset)
n = i64(n_int)
if n == 0 && os_err == 0 {
@@ -46,7 +46,7 @@ _file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte,
err = .EOF
}
case .Write_At:
- when !(ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD) {
+ when !(ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Haiku) {
n_int, os_err = write_at(fd, p, offset)
n = i64(n_int)
if n == 0 && os_err == 0 {
@@ -60,7 +60,7 @@ _file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte,
case .Destroy:
err = .Empty
case .Query:
- when ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD {
+ when ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Haiku {
return io.query_utility({.Close, .Flush, .Read, .Write, .Seek, .Size, .Query})
} else {
return io.query_utility({.Close, .Flush, .Read, .Read_At, .Write, .Write_At, .Seek, .Size, .Query})
diff --git a/core/sync/futex_haiku.odin b/core/sync/futex_haiku.odin
new file mode 100644
index 000000000..1dd719e7a
--- /dev/null
+++ b/core/sync/futex_haiku.odin
@@ -0,0 +1,167 @@
+//+private
+package sync
+
+import "core:c"
+import "core:runtime"
+import "core:sys/haiku"
+import "core:sys/unix"
+import "core:time"
+
+@(private="file")
+Wait_Node :: struct {
+ thread: unix.pthread_t,
+ futex: ^Futex,
+ prev, next: ^Wait_Node,
+}
+@(private="file")
+atomic_flag :: distinct bool
+@(private="file")
+Wait_Queue :: struct {
+ lock: atomic_flag,
+ list: Wait_Node,
+}
+@(private="file")
+waitq_lock :: proc "contextless" (waitq: ^Wait_Queue) {
+ for cast(bool)atomic_exchange_explicit(&waitq.lock, atomic_flag(true), .Acquire) {
+ cpu_relax() // spin...
+ }
+}
+@(private="file")
+waitq_unlock :: proc "contextless" (waitq: ^Wait_Queue) {
+ atomic_store_explicit(&waitq.lock, atomic_flag(false), .Release)
+}
+
+// FIXME: This approach may scale badly in the future,
+// possible solution - hash map (leads to deadlocks now).
+@(private="file")
+g_waitq: Wait_Queue
+
+@(init, private="file")
+g_waitq_init :: proc() {
+ g_waitq = {
+ list = {
+ prev = &g_waitq.list,
+ next = &g_waitq.list,
+ },
+ }
+}
+
+@(private="file")
+get_waitq :: #force_inline proc "contextless" (f: ^Futex) -> ^Wait_Queue {
+ _ = f
+ return &g_waitq
+}
+
+_futex_wait :: proc "contextless" (f: ^Futex, expect: u32) -> (ok: bool) {
+ waitq := get_waitq(f)
+ waitq_lock(waitq)
+ defer waitq_unlock(waitq)
+
+ head := &waitq.list
+ waiter := Wait_Node{
+ thread = unix.pthread_self(),
+ futex = f,
+ prev = head,
+ next = head.next,
+ }
+
+ 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)
+
+ 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
+ }
+
+ waiter.prev.next = waiter.next
+ waiter.next.prev = waiter.prev
+
+ unix.pthread_sigmask(haiku.SIG_SETMASK, &old_mask, nil)
+
+ // FIXME: Add error handling!
+ return
+}
+
+_futex_wait_with_timeout :: proc "contextless" (f: ^Futex, expect: u32, duration: time.Duration) -> (ok: bool) {
+ if duration <= 0 {
+ return false
+ }
+ waitq := get_waitq(f)
+ waitq_lock(waitq)
+ defer waitq_unlock(waitq)
+
+ head := &waitq.list
+ waiter := Wait_Node{
+ thread = unix.pthread_self(),
+ futex = f,
+ prev = head,
+ next = head.next,
+ }
+
+ 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)
+
+ 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),
+ tv_nsec = i64(duration % 1e9),
+ }
+ haiku.sigtimedwait(&mask, &info, &ts)
+ errno := haiku.errno()
+ ok = errno == .EAGAIN || errno == .OK
+ }
+
+ waiter.prev.next = waiter.next
+ waiter.next.prev = waiter.prev
+
+ unix.pthread_sigmask(haiku.SIG_SETMASK, &old_mask, nil)
+
+ // FIXME: Add error handling!
+ return
+}
+
+_futex_signal :: proc "contextless" (f: ^Futex) {
+ waitq := get_waitq(f)
+ waitq_lock(waitq)
+ defer waitq_unlock(waitq)
+
+ head := &waitq.list
+ for waiter := head.next; waiter != head; waiter = waiter.next {
+ if waiter.futex == f {
+ unix.pthread_kill(waiter.thread, haiku.SIGCONT)
+ break
+ }
+ }
+}
+
+_futex_broadcast :: proc "contextless" (f: ^Futex) {
+ waitq := get_waitq(f)
+ waitq_lock(waitq)
+ defer waitq_unlock(waitq)
+
+ head := &waitq.list
+ for waiter := head.next; waiter != head; waiter = waiter.next {
+ if waiter.futex == f {
+ unix.pthread_kill(waiter.thread, haiku.SIGCONT)
+ }
+ }
+}
diff --git a/core/sync/primitives_haiku.odin b/core/sync/primitives_haiku.odin
new file mode 100644
index 000000000..4b8f6b02d
--- /dev/null
+++ b/core/sync/primitives_haiku.odin
@@ -0,0 +1,8 @@
+//+private
+package sync
+
+import "core:sys/haiku"
+
+_current_thread_id :: proc "contextless" () -> int {
+ return int(haiku.find_thread(nil))
+}
diff --git a/core/sys/haiku/errors.odin b/core/sys/haiku/errors.odin
new file mode 100644
index 000000000..023045001
--- /dev/null
+++ b/core/sys/haiku/errors.odin
@@ -0,0 +1,239 @@
+//+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,
+
+ // General Errors
+ NO_MEMORY = GENERAL_ERROR_BASE + 0,
+ IO_ERROR = GENERAL_ERROR_BASE + 1,
+ PERMISSION_DENIED = GENERAL_ERROR_BASE + 2,
+ BAD_INDEX = GENERAL_ERROR_BASE + 3,
+ BAD_TYPE = GENERAL_ERROR_BASE + 4,
+ BAD_VALUE = GENERAL_ERROR_BASE + 5,
+ MISMATCHED_VALUES = GENERAL_ERROR_BASE + 6,
+ NAME_NOT_FOUND = GENERAL_ERROR_BASE + 7,
+ NAME_IN_USE = GENERAL_ERROR_BASE + 8,
+ TIMED_OUT = GENERAL_ERROR_BASE + 9,
+ INTERRUPTED = GENERAL_ERROR_BASE + 10,
+ WOULD_BLOCK = GENERAL_ERROR_BASE + 11,
+ CANCELED = GENERAL_ERROR_BASE + 12,
+ NO_INIT = GENERAL_ERROR_BASE + 13,
+ NOT_INITIALIZED = GENERAL_ERROR_BASE + 13,
+ BUSY = GENERAL_ERROR_BASE + 14,
+ NOT_ALLOWED = GENERAL_ERROR_BASE + 15,
+ BAD_DATA = GENERAL_ERROR_BASE + 16,
+ DONT_DO_THAT = GENERAL_ERROR_BASE + 17,
+
+ ERROR = -1,
+ OK = 0,
+ NO_ERROR = 0,
+
+ // Kernel Kit Errors
+ BAD_SEM_ID = OS_ERROR_BASE + 0,
+ NO_MORE_SEMS = OS_ERROR_BASE + 1,
+ BAD_THREAD_ID = OS_ERROR_BASE + 0x100,
+ NO_MORE_THREADS = OS_ERROR_BASE + 0x101,
+ BAD_THREAD_STATE = OS_ERROR_BASE + 0x102,
+ BAD_TEAM_ID = OS_ERROR_BASE + 0x103,
+ NO_MORE_TEAMS = OS_ERROR_BASE + 0x104,
+ BAD_PORT_ID = OS_ERROR_BASE + 0x200,
+ NO_MORE_PORTS = OS_ERROR_BASE + 0x201,
+ BAD_IMAGE_ID = OS_ERROR_BASE + 0x300,
+ BAD_ADDRESS = OS_ERROR_BASE + 0x301,
+ NOT_AN_EXECUTABLE = OS_ERROR_BASE + 0x302,
+ MISSING_LIBRARY = OS_ERROR_BASE + 0x303,
+ MISSING_SYMBOL = OS_ERROR_BASE + 0x304,
+ UNKNOWN_EXECUTABLE = OS_ERROR_BASE + 0x305,
+ LEGACY_EXECUTABLE = OS_ERROR_BASE + 0x306,
+
+ DEBUGGER_ALREADY_INSTALLED = OS_ERROR_BASE + 0x400,
+
+ // Application Kit Errors
+ BAD_REPLY = APP_ERROR_BASE + 0,
+ DUPLICATE_REPLY = APP_ERROR_BASE + 1,
+ MESSAGE_TO_SELF = APP_ERROR_BASE + 2,
+ BAD_HANDLER = APP_ERROR_BASE + 3,
+ ALREADY_RUNNING = APP_ERROR_BASE + 4,
+ LAUNCH_FAILED = APP_ERROR_BASE + 5,
+ AMBIGUOUS_APP_LAUNCH = APP_ERROR_BASE + 6,
+ UNKNOWN_MIME_TYPE = APP_ERROR_BASE + 7,
+ BAD_SCRIPT_SYNTAX = APP_ERROR_BASE + 8,
+ LAUNCH_FAILED_NO_RESOLVE_LINK = APP_ERROR_BASE + 9,
+ LAUNCH_FAILED_EXECUTABLE = APP_ERROR_BASE + 10,
+ LAUNCH_FAILED_APP_NOT_FOUND = APP_ERROR_BASE + 11,
+ LAUNCH_FAILED_APP_IN_TRASH = APP_ERROR_BASE + 12,
+ LAUNCH_FAILED_NO_PREFERRED_APP = APP_ERROR_BASE + 13,
+ LAUNCH_FAILED_FILES_APP_NOT_FOUND = APP_ERROR_BASE + 14,
+ BAD_MIME_SNIFFER_RULE = APP_ERROR_BASE + 15,
+ NOT_A_MESSAGE = APP_ERROR_BASE + 16,
+ SHUTDOWN_CANCELLED = APP_ERROR_BASE + 17,
+ SHUTTING_DOWN = APP_ERROR_BASE + 18,
+
+ // Storage Kit/File System Errors
+ FILE_ERROR = STORAGE_ERROR_BASE + 0,
+ // 1 was B_FILE_NOT_FOUND (deprecated)
+ FILE_EXISTS = STORAGE_ERROR_BASE + 2,
+ ENTRY_NOT_FOUND = STORAGE_ERROR_BASE + 3,
+ NAME_TOO_LONG = STORAGE_ERROR_BASE + 4,
+ NOT_A_DIRECTORY = STORAGE_ERROR_BASE + 5,
+ DIRECTORY_NOT_EMPTY = STORAGE_ERROR_BASE + 6,
+ DEVICE_FULL = STORAGE_ERROR_BASE + 7,
+ READ_ONLY_DEVICE = STORAGE_ERROR_BASE + 8,
+ IS_A_DIRECTORY = STORAGE_ERROR_BASE + 9,
+ NO_MORE_FDS = STORAGE_ERROR_BASE + 10,
+ CROSS_DEVICE_LINK = STORAGE_ERROR_BASE + 11,
+ LINK_LIMIT = STORAGE_ERROR_BASE + 12,
+ BUSTED_PIPE = STORAGE_ERROR_BASE + 13,
+ UNSUPPORTED = STORAGE_ERROR_BASE + 14,
+ PARTITION_TOO_SMALL = STORAGE_ERROR_BASE + 15,
+ 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,
+
+ // 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,
+
+ // Media Kit Errors
+ STREAM_NOT_FOUND = MEDIA_ERROR_BASE + 0,
+ SERVER_NOT_FOUND = MEDIA_ERROR_BASE + 1,
+ RESOURCE_NOT_FOUND = MEDIA_ERROR_BASE + 2,
+ RESOURCE_UNAVAILABLE = MEDIA_ERROR_BASE + 3,
+ BAD_SUBSCRIBER = MEDIA_ERROR_BASE + 4,
+ SUBSCRIBER_NOT_ENTERED = MEDIA_ERROR_BASE + 5,
+ BUFFER_NOT_AVAILABLE = MEDIA_ERROR_BASE + 6,
+ LAST_BUFFER_ERROR = MEDIA_ERROR_BASE + 7,
+ MEDIA_SYSTEM_FAILURE = MEDIA_ERROR_BASE + 100,
+ MEDIA_BAD_NODE = MEDIA_ERROR_BASE + 101,
+ MEDIA_NODE_BUSY = MEDIA_ERROR_BASE + 102,
+ MEDIA_BAD_FORMAT = MEDIA_ERROR_BASE + 103,
+ MEDIA_BAD_BUFFER = MEDIA_ERROR_BASE + 104,
+ MEDIA_TOO_MANY_NODES = MEDIA_ERROR_BASE + 105,
+ MEDIA_TOO_MANY_BUFFERS = MEDIA_ERROR_BASE + 106,
+ MEDIA_NODE_ALREADY_EXISTS = MEDIA_ERROR_BASE + 107,
+ MEDIA_BUFFER_ALREADY_EXISTS = MEDIA_ERROR_BASE + 108,
+ MEDIA_CANNOT_SEEK = MEDIA_ERROR_BASE + 109,
+ MEDIA_CANNOT_CHANGE_RUN_MODE = MEDIA_ERROR_BASE + 110,
+ MEDIA_APP_ALREADY_REGISTERED = MEDIA_ERROR_BASE + 111,
+ MEDIA_APP_NOT_REGISTERED = MEDIA_ERROR_BASE + 112,
+ MEDIA_CANNOT_RECLAIM_BUFFERS = MEDIA_ERROR_BASE + 113,
+ MEDIA_BUFFERS_NOT_RECLAIMED = MEDIA_ERROR_BASE + 114,
+ MEDIA_TIME_SOURCE_STOPPED = MEDIA_ERROR_BASE + 115,
+ MEDIA_TIME_SOURCE_BUSY = MEDIA_ERROR_BASE + 116,
+ MEDIA_BAD_SOURCE = MEDIA_ERROR_BASE + 117,
+ MEDIA_BAD_DESTINATION = MEDIA_ERROR_BASE + 118,
+ MEDIA_ALREADY_CONNECTED = MEDIA_ERROR_BASE + 119,
+ MEDIA_NOT_CONNECTED = MEDIA_ERROR_BASE + 120,
+ MEDIA_BAD_CLIP_FORMAT = MEDIA_ERROR_BASE + 121,
+ MEDIA_ADDON_FAILED = MEDIA_ERROR_BASE + 122,
+ MEDIA_ADDON_DISABLED = MEDIA_ERROR_BASE + 123,
+ MEDIA_CHANGE_IN_PROGRESS = MEDIA_ERROR_BASE + 124,
+ MEDIA_STALE_CHANGE_COUNT = MEDIA_ERROR_BASE + 125,
+ MEDIA_ADDON_RESTRICTED = MEDIA_ERROR_BASE + 126,
+ MEDIA_NO_HANDLER = MEDIA_ERROR_BASE + 127,
+ MEDIA_DUPLICATE_FORMAT = MEDIA_ERROR_BASE + 128,
+ MEDIA_REALTIME_DISABLED = MEDIA_ERROR_BASE + 129,
+ MEDIA_REALTIME_UNAVAILABLE = MEDIA_ERROR_BASE + 130,
+
+ // Mail Kit Errors
+ MAIL_NO_DAEMON = MAIL_ERROR_BASE + 0,
+ MAIL_UNKNOWN_USER = MAIL_ERROR_BASE + 1,
+ MAIL_WRONG_PASSWORD = MAIL_ERROR_BASE + 2,
+ MAIL_UNKNOWN_HOST = MAIL_ERROR_BASE + 3,
+ MAIL_ACCESS_ERROR = MAIL_ERROR_BASE + 4,
+ MAIL_UNKNOWN_FIELD = MAIL_ERROR_BASE + 5,
+ MAIL_NO_RECIPIENT = MAIL_ERROR_BASE + 6,
+ MAIL_INVALID_MAIL = MAIL_ERROR_BASE + 7,
+
+ // Printing Errors
+ NO_PRINT_SERVER = PRINT_ERROR_BASE + 0,
+
+ // Device Kit Errors
+ DEV_INVALID_IOCTL = DEVICE_ERROR_BASE + 0,
+ DEV_NO_MEMORY = DEVICE_ERROR_BASE + 1,
+ DEV_BAD_DRIVE_NUM = DEVICE_ERROR_BASE + 2,
+ DEV_NO_MEDIA = DEVICE_ERROR_BASE + 3,
+ DEV_UNREADABLE = DEVICE_ERROR_BASE + 4,
+ DEV_FORMAT_ERROR = DEVICE_ERROR_BASE + 5,
+ DEV_TIMEOUT = DEVICE_ERROR_BASE + 6,
+ DEV_RECALIBRATE_ERROR = DEVICE_ERROR_BASE + 7,
+ DEV_SEEK_ERROR = DEVICE_ERROR_BASE + 8,
+ DEV_ID_ERROR = DEVICE_ERROR_BASE + 9,
+ DEV_READ_ERROR = DEVICE_ERROR_BASE + 10,
+ DEV_WRITE_ERROR = DEVICE_ERROR_BASE + 11,
+ DEV_NOT_READY = DEVICE_ERROR_BASE + 12,
+ DEV_MEDIA_CHANGED = DEVICE_ERROR_BASE + 13,
+ DEV_MEDIA_CHANGE_REQUESTED = DEVICE_ERROR_BASE + 14,
+ DEV_RESOURCE_CONFLICT = DEVICE_ERROR_BASE + 15,
+ DEV_CONFIGURATION_ERROR = DEVICE_ERROR_BASE + 16,
+ DEV_DISABLED_BY_USER = DEVICE_ERROR_BASE + 17,
+ DEV_DOOR_OPEN = DEVICE_ERROR_BASE + 18,
+ DEV_INVALID_PIPE = DEVICE_ERROR_BASE + 19,
+ DEV_CRC_ERROR = DEVICE_ERROR_BASE + 20,
+ DEV_STALLED = DEVICE_ERROR_BASE + 21,
+ DEV_BAD_PID = DEVICE_ERROR_BASE + 22,
+ DEV_UNEXPECTED_PID = DEVICE_ERROR_BASE + 23,
+ DEV_DATA_OVERRUN = DEVICE_ERROR_BASE + 24,
+ DEV_DATA_UNDERRUN = DEVICE_ERROR_BASE + 25,
+ DEV_FIFO_OVERRUN = DEVICE_ERROR_BASE + 26,
+ DEV_FIFO_UNDERRUN = DEVICE_ERROR_BASE + 27,
+ DEV_PENDING = DEVICE_ERROR_BASE + 28,
+ DEV_MULTIPLE_ERRORS = DEVICE_ERROR_BASE + 29,
+ DEV_TOO_LATE = DEVICE_ERROR_BASE + 30,
+
+ // Translation Kit Errors
+ TRANSLATION_BASE_ERROR = TRANSLATION_ERROR_BASE + 0,
+ NO_TRANSLATOR = TRANSLATION_ERROR_BASE + 1,
+ ILLEGAL_DATA = TRANSLATION_ERROR_BASE + 2,
+}
+
+errno :: #force_inline proc "contextless" () -> Errno {
+ return Errno(_errnop()^)
+}
+
+foreign import libroot "system: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 ---
+}
diff --git a/core/sys/haiku/find_directory.odin b/core/sys/haiku/find_directory.odin
new file mode 100644
index 000000000..103e677d7
--- /dev/null
+++ b/core/sys/haiku/find_directory.odin
@@ -0,0 +1,168 @@
+//+build haiku
+package sys_haiku
+
+import "core:c"
+
+directory_which :: enum c.int {
+ // Per volume directories
+ DESKTOP_DIRECTORY = 0,
+ TRASH_DIRECTORY,
+
+ // System directories
+ SYSTEM_DIRECTORY = 1000,
+ SYSTEM_ADDONS_DIRECTORY = 1002,
+ SYSTEM_BOOT_DIRECTORY,
+ SYSTEM_FONTS_DIRECTORY,
+ SYSTEM_LIB_DIRECTORY,
+ SYSTEM_SERVERS_DIRECTORY,
+ SYSTEM_APPS_DIRECTORY,
+ SYSTEM_BIN_DIRECTORY,
+ SYSTEM_DOCUMENTATION_DIRECTORY = 1010,
+ SYSTEM_PREFERENCES_DIRECTORY,
+ SYSTEM_TRANSLATORS_DIRECTORY,
+ SYSTEM_MEDIA_NODES_DIRECTORY,
+ SYSTEM_SOUNDS_DIRECTORY,
+ SYSTEM_DATA_DIRECTORY,
+ SYSTEM_DEVELOP_DIRECTORY,
+ SYSTEM_PACKAGES_DIRECTORY,
+ SYSTEM_HEADERS_DIRECTORY,
+ SYSTEM_ETC_DIRECTORY = 2008,
+ SYSTEM_SETTINGS_DIRECTORY = 2010,
+ SYSTEM_LOG_DIRECTORY = 2012,
+ SYSTEM_SPOOL_DIRECTORY,
+ SYSTEM_TEMP_DIRECTORY,
+ SYSTEM_VAR_DIRECTORY,
+ SYSTEM_CACHE_DIRECTORY = 2020,
+ SYSTEM_NONPACKAGED_DIRECTORY = 2023,
+ SYSTEM_NONPACKAGED_ADDONS_DIRECTORY,
+ SYSTEM_NONPACKAGED_TRANSLATORS_DIRECTORY,
+ SYSTEM_NONPACKAGED_MEDIA_NODES_DIRECTORY,
+ SYSTEM_NONPACKAGED_BIN_DIRECTORY,
+ SYSTEM_NONPACKAGED_DATA_DIRECTORY,
+ SYSTEM_NONPACKAGED_FONTS_DIRECTORY,
+ SYSTEM_NONPACKAGED_SOUNDS_DIRECTORY,
+ SYSTEM_NONPACKAGED_DOCUMENTATION_DIRECTORY,
+ SYSTEM_NONPACKAGED_LIB_DIRECTORY,
+ SYSTEM_NONPACKAGED_HEADERS_DIRECTORY,
+ SYSTEM_NONPACKAGED_DEVELOP_DIRECTORY,
+
+ // User directories. These are interpreted in the context of the user making the find_directory call.
+ USER_DIRECTORY = 3000,
+ USER_CONFIG_DIRECTORY,
+ USER_ADDONS_DIRECTORY,
+ USER_BOOT_DIRECTORY,
+ USER_FONTS_DIRECTORY,
+ USER_LIB_DIRECTORY,
+ USER_SETTINGS_DIRECTORY,
+ USER_DESKBAR_DIRECTORY,
+ USER_PRINTERS_DIRECTORY,
+ USER_TRANSLATORS_DIRECTORY,
+ USER_MEDIA_NODES_DIRECTORY,
+ USER_SOUNDS_DIRECTORY,
+ USER_DATA_DIRECTORY,
+ USER_CACHE_DIRECTORY,
+ USER_PACKAGES_DIRECTORY,
+ USER_HEADERS_DIRECTORY,
+ USER_NONPACKAGED_DIRECTORY,
+ USER_NONPACKAGED_ADDONS_DIRECTORY,
+ USER_NONPACKAGED_TRANSLATORS_DIRECTORY,
+ USER_NONPACKAGED_MEDIA_NODES_DIRECTORY,
+ USER_NONPACKAGED_BIN_DIRECTORY,
+ USER_NONPACKAGED_DATA_DIRECTORY,
+ USER_NONPACKAGED_FONTS_DIRECTORY,
+ USER_NONPACKAGED_SOUNDS_DIRECTORY,
+ USER_NONPACKAGED_DOCUMENTATION_DIRECTORY,
+ USER_NONPACKAGED_LIB_DIRECTORY,
+ USER_NONPACKAGED_HEADERS_DIRECTORY,
+ USER_NONPACKAGED_DEVELOP_DIRECTORY,
+ USER_DEVELOP_DIRECTORY,
+ USER_DOCUMENTATION_DIRECTORY,
+ USER_SERVERS_DIRECTORY,
+ USER_APPS_DIRECTORY,
+ USER_BIN_DIRECTORY,
+ USER_PREFERENCES_DIRECTORY,
+ USER_ETC_DIRECTORY,
+ USER_LOG_DIRECTORY,
+ USER_SPOOL_DIRECTORY,
+ USER_VAR_DIRECTORY,
+
+ // Global directories
+ APPS_DIRECTORY = 4000,
+ PREFERENCES_DIRECTORY,
+ UTILITIES_DIRECTORY,
+ PACKAGE_LINKS_DIRECTORY,
+
+ // Obsolete: Legacy BeOS definition to be phased out
+ BEOS_DIRECTORY = 1000,
+ BEOS_SYSTEM_DIRECTORY,
+ BEOS_ADDONS_DIRECTORY,
+ BEOS_BOOT_DIRECTORY,
+ BEOS_FONTS_DIRECTORY,
+ BEOS_LIB_DIRECTORY,
+ BEOS_SERVERS_DIRECTORY,
+ BEOS_APPS_DIRECTORY,
+ BEOS_BIN_DIRECTORY,
+ BEOS_ETC_DIRECTORY,
+ BEOS_DOCUMENTATION_DIRECTORY,
+ BEOS_PREFERENCES_DIRECTORY,
+ BEOS_TRANSLATORS_DIRECTORY,
+ BEOS_MEDIA_NODES_DIRECTORY,
+ BEOS_SOUNDS_DIRECTORY,
+}
+
+find_path_flags :: enum c.int {
+ CREATE_DIRECTORY = 0x0001,
+ CREATE_PARENT_DIRECTORY = 0x0002,
+ EXISTING_ONLY = 0x0004,
+
+ // find_paths() only!
+ SYSTEM_ONLY = 0x0010,
+ USER_ONLY = 0x0020,
+}
+
+path_base_directory :: enum c.int {
+ INSTALLATION_LOCATION_DIRECTORY,
+ ADD_ONS_DIRECTORY,
+ APPS_DIRECTORY,
+ BIN_DIRECTORY,
+ BOOT_DIRECTORY,
+ CACHE_DIRECTORY,
+ DATA_DIRECTORY,
+ DEVELOP_DIRECTORY,
+ DEVELOP_LIB_DIRECTORY,
+ DOCUMENTATION_DIRECTORY,
+ ETC_DIRECTORY,
+ FONTS_DIRECTORY,
+ HEADERS_DIRECTORY,
+ LIB_DIRECTORY,
+ LOG_DIRECTORY,
+ MEDIA_NODES_DIRECTORY,
+ PACKAGES_DIRECTORY,
+ PREFERENCES_DIRECTORY,
+ SERVERS_DIRECTORY,
+ SETTINGS_DIRECTORY,
+ SOUNDS_DIRECTORY,
+ SPOOL_DIRECTORY,
+ TRANSLATORS_DIRECTORY,
+ VAR_DIRECTORY,
+
+ // find_path() only!
+ IMAGE_PATH = 1000,
+ PACKAGE_PATH,
+}
+
+// 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) }
+
+foreign import libroot "system: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 ---
+}
diff --git a/core/sys/haiku/os.odin b/core/sys/haiku/os.odin
new file mode 100644
index 000000000..1e00145eb
--- /dev/null
+++ b/core/sys/haiku/os.odin
@@ -0,0 +1,502 @@
+//+build haiku
+package sys_haiku
+
+import "core:c"
+import "core:sys/unix"
+
+foreign import libroot "system:c"
+
+PATH_MAX :: 1024
+NAME_MAX :: 256
+MAXPATHLEN :: PATH_MAX
+
+FILE_NAME_LENGTH :: NAME_MAX
+PATH_NAME_LENGTH :: MAXPATHLEN
+OS_NAME_LENGTH :: 32
+
+// Areas
+
+area_info :: struct {
+ area: area_id,
+ name: [OS_NAME_LENGTH]c.char,
+ size: c.size_t,
+ lock: u32,
+ protection: u32,
+ team: team_id,
+ ram_size: u32,
+ copy_count: u32,
+ in_count: u32,
+ out_count: u32,
+ address: rawptr,
+}
+
+area_locking :: enum u32 {
+ 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
+}
+
+// for create_area() and clone_area()
+address_spec :: enum u32 {
+ ANY_ADDRESS = 0,
+ EXACT_ADDRESS = 1,
+ BASE_ADDRESS = 2,
+ CLONE_ADDRESS = 3,
+ ANY_KERNEL_ADDRESS = 4,
+ // ANY_KERNEL_BLOCK_ADDRESS = 5,
+ RANDOMIZED_ANY_ADDRESS = 6,
+ RANDOMIZED_BASE_ADDRESS = 7,
+}
+
+area_protection_flags :: enum u32 {
+ READ_AREA = 1 << 0,
+ WRITE_AREA = 1 << 1,
+ EXECUTE_AREA = 1 << 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,
+}
+
+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 ---
+ 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 ---
+ 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 ---
+}
+
+// Ports
+
+port_info :: struct {
+ port: port_id,
+ team: team_id,
+ name: [OS_NAME_LENGTH]c.char,
+ 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,
+ // read the message, but don't remove it; kernel-only; memory must be locked
+ PEEK_PORT_MESSAGE = 0x100,
+}
+
+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 ---
+ 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 ---
+ 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 ---
+}
+
+// Semaphores
+
+sem_info :: struct {
+ sem: sem_id,
+ team: team_id,
+ name: [OS_NAME_LENGTH]c.char,
+ 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)
+
+ // 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
+}
+
+foreign libroot {
+ create_sem :: proc(count: i32, name: cstring) -> sem_id ---
+ delete_sem :: proc(id: sem_id) -> status_t ---
+ acquire_sem :: proc(id: sem_id) -> status_t ---
+ acquire_sem_etc :: proc(id: sem_id, count: i32, flags: semaphore_flags, timeout: bigtime_t) -> status_t ---
+ release_sem :: proc(id: sem_id) -> status_t ---
+ release_sem_etc :: proc(id: sem_id, count: i32, flags: semaphore_flags) -> status_t ---
+ switch_sem :: proc(semToBeReleased: sem_id) -> status_t ---
+ 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 ---
+}
+
+// Teams
+
+team_info :: struct {
+ team: team_id,
+ thread_count: i32,
+ image_count: i32,
+ area_count: i32,
+ debugger_nub_thread: thread_id,
+ debugger_nub_port: port_id,
+ argc: i32,
+ args: [64]c.char,
+ uid: uid_t,
+ gid: gid_t,
+
+ // Haiku R1 extensions
+ real_uid: uid_t,
+ real_gid: gid_t,
+ group_id: pid_t,
+ session_id: pid_t,
+ parent: team_id,
+ name: [OS_NAME_LENGTH]c.char,
+ start_time: bigtime_t,
+}
+
+CURRENT_TEAM :: 0
+SYSTEM_TEAM :: 1
+
+team_usage_info :: struct {
+ user_time: bigtime_t,
+ kernel_time: bigtime_t,
+}
+
+team_usage_who :: enum i32 {
+ // compatible to sys/resource.h RUSAGE_SELF and RUSAGE_CHILDREN
+ SELF = 0,
+ CHILDREN = -1,
+}
+
+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 ---
+}
+
+// Threads
+
+thread_state :: enum c.int {
+ RUNNING = 1,
+ READY,
+ RECEIVING,
+ ASLEEP,
+ SUSPENDED,
+ WAITING,
+}
+
+thread_info :: struct {
+ thread: thread_id,
+ team: team_id,
+ name: [OS_NAME_LENGTH]c.char,
+ state: thread_state,
+ priority: thread_priority,
+ sem: sem_id,
+ user_time: bigtime_t,
+ kernel_time: bigtime_t,
+ stack_base: rawptr,
+ stack_end: rawptr,
+}
+
+thread_priority :: enum i32 {
+ IDLE_PRIORITY = 0,
+ LOWEST_ACTIVE_PRIORITY = 1,
+ LOW_PRIORITY = 5,
+ NORMAL_PRIORITY = 10,
+ DISPLAY_PRIORITY = 15,
+ URGENT_DISPLAY_PRIORITY = 20,
+ REAL_TIME_DISPLAY_PRIORITY = 100,
+ URGENT_PRIORITY = 110,
+ REAL_TIME_PRIORITY = 120,
+}
+
+FIRST_REAL_TIME_PRIORITY :: thread_priority.REAL_TIME_PRIORITY
+
+// time base for snooze_*(), compatible with the clockid_t constants defined in <time.h>
+SYSTEM_TIMEBASE :: 0
+
+thread_func :: #type proc "c" (rawptr) -> status_t
+
+foreign libroot {
+ spawn_thread :: proc(thread_func, name: cstring, priority: thread_priority, data: rawptr) -> thread_id ---
+ kill_thread :: proc(thread: thread_id) -> status_t ---
+ resume_thread :: proc(thread: thread_id) -> status_t ---
+ suspend_thread :: proc(thread: thread_id) -> status_t ---
+ rename_thread :: proc(thread: thread_id, newName: cstring) -> status_t ---
+ set_thread_priority :: proc(thread: thread_id, newPriority: thread_priority) -> status_t ---
+ exit_thread :: proc(status: status_t) ---
+ wait_for_thread :: proc(thread: thread_id, returnValue: ^status_t) -> status_t ---
+ // FIXME: Find and define those flags.
+ 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 ---
+ 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 ---
+ // bridge to the pthread API
+ get_pthread_thread_id :: proc(thread: pthread_t) -> thread_id ---
+}
+
+// Time
+
+foreign libroot {
+ real_time_clock :: proc() -> c.ulong ---
+ set_real_time_clock :: proc(secsSinceJan1st1970: c.ulong) ---
+ real_time_clock_usecs :: proc() -> bigtime_t ---
+ // time since booting in microseconds
+ system_time :: proc() -> bigtime_t ---
+ // time since booting in nanoseconds
+ system_time_nsecs :: proc() -> nanotime_t ---
+}
+
+// Alarm
+
+alarm_mode :: enum u32 {
+ ONE_SHOT_ABSOLUTE_ALARM = 1,
+ ONE_SHOT_RELATIVE_ALARM,
+ PERIODIC_ALARM, // "when" specifies the period
+}
+
+foreign libroot {
+ set_alarm :: proc(_when: bigtime_t, mode: alarm_mode) -> bigtime_t ---
+}
+
+// Debugger
+
+foreign libroot {
+ debugger :: proc(message: cstring) ---
+ /*
+ calling this function with a non-zero value will cause your thread
+ to receive signals for any exceptional conditions that occur (i.e.
+ you'll get SIGSEGV for data access exceptions, SIGFPE for floating
+ point errors, SIGILL for illegal instructions, etc).
+
+ to re-enable the default debugger pass a zero.
+ */
+ disable_debugger :: proc(state: c.int) -> c.int ---
+}
+
+// System information
+
+cpu_info :: struct {
+ active_time: bigtime_t,
+ enabled: bool,
+ current_frequency: u64,
+}
+
+system_info :: struct {
+ boot_time: bigtime_t, // time of boot (usecs since 1/1/1970)
+
+ cpu_count: u32, // number of cpus
+
+ max_pages: u64, // total # of accessible pages
+ used_pages: u64, // # of accessible pages in use
+ cached_pages: u64,
+ block_cache_pages: u64,
+ ignored_pages: u64, // # of ignored/inaccessible pages
+
+ needed_memory: u64,
+ free_memory: u64,
+
+ max_swap_pages: u64,
+ free_swap_pages: u64,
+
+ page_faults: u32, // # of page faults
+
+ max_sems: u32,
+ used_sems: u32,
+
+ max_ports: u32,
+ used_ports: u32,
+
+ max_threads: u32,
+ used_threads: u32,
+
+ 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_version: i64,
+ abi: u32, // the system API
+}
+
+topology_level_type :: enum c.int {
+ UNKNOWN,
+ ROOT,
+ SMT,
+ CORE,
+ PACKAGE,
+}
+
+cpu_platform :: enum c.int {
+ UNKNOWN,
+ x86,
+ x86_64,
+ PPC,
+ PPC_64,
+ M68K,
+ ARM,
+ ARM_64,
+ ALPHA,
+ MIPS,
+ SH,
+ SPARC,
+ RISC_V,
+}
+
+cpu_vendor :: enum c.int {
+ UNKNOWN,
+ AMD,
+ CYRIX,
+ IDT,
+ INTEL,
+ NATIONAL_SEMICONDUCTOR,
+ RISE,
+ TRANSMETA,
+ VIA,
+ IBM,
+ MOTOROLA,
+ NEC,
+ HYGON,
+ SUN,
+ FUJITSU,
+}
+
+cpu_topology_node_info :: struct {
+ id: u32,
+ type: topology_level_type,
+ level: u32,
+
+ data: struct #raw_union {
+ _root: struct {
+ platform: cpu_platform,
+ },
+ _package: struct {
+ vendor: cpu_vendor,
+ cache_line_size: u32
+ },
+ _core: struct {
+ model: u32,
+ default_frequency: u64,
+ },
+ },
+}
+
+// FIXME: Add cpuid_info when bit fields are ready.
+
+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_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
+}
+
+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) ---
+}
diff --git a/core/sys/haiku/types.odin b/core/sys/haiku/types.odin
new file mode 100644
index 000000000..0440d5a98
--- /dev/null
+++ b/core/sys/haiku/types.odin
@@ -0,0 +1,54 @@
+//+build haiku
+package sys_haiku
+
+import "core:c"
+
+status_t :: i32
+bigtime_t :: i64
+nanotime_t :: i64
+type_code :: u32
+perform_code :: u32
+
+phys_addr_t :: uintptr
+phys_size_t :: phys_addr_t
+generic_addr_t :: uintptr
+generic_size_t :: generic_addr_t
+
+area_id :: i32
+port_id :: i32
+sem_id :: i32
+team_id :: i32
+thread_id :: i32
+
+blkcnt_t :: i64
+blksize_t :: i32
+fsblkcnt_t :: i64
+fsfilcnt_t :: i64
+off_t :: i64
+ino_t :: i64
+cnt_t :: i32
+dev_t :: i32
+pid_t :: i32
+id_t :: i32
+
+uid_t :: u32
+gid_t :: u32
+mode_t :: u32
+umode_t :: u32
+nlink_t :: i32
+
+caddr_t :: ^c.char
+
+addr_t :: phys_addr_t
+key_t :: i32
+
+clockid_t :: i32
+
+time_t :: i64 when ODIN_ARCH == .amd64 || ODIN_ARCH == .arm64 else i32
+
+sig_atomic_t :: c.int
+sigset_t :: u64
+
+image_id :: i32
+
+pthread_t :: rawptr
diff --git a/core/sys/unix/pthread_haiku.odin b/core/sys/unix/pthread_haiku.odin
new file mode 100644
index 000000000..1278f34fe
--- /dev/null
+++ b/core/sys/unix/pthread_haiku.odin
@@ -0,0 +1,71 @@
+package unix
+
+import "core:c"
+
+pthread_t :: distinct rawptr
+pthread_attr_t :: distinct rawptr
+pthread_mutex_t :: distinct rawptr
+pthread_mutexattr_t :: distinct rawptr
+pthread_cond_t :: distinct rawptr
+pthread_condattr_t :: distinct rawptr
+pthread_rwlock_t :: distinct rawptr
+pthread_rwlockattr_t :: distinct rawptr
+pthread_barrier_t :: distinct rawptr
+pthread_barrierattr_t :: distinct rawptr
+pthread_spinlock_t :: distinct rawptr
+
+pthread_key_t :: distinct c.int
+pthread_once_t :: struct {
+ state: c.int,
+ mutex: pthread_mutex_t,
+}
+
+PTHREAD_MUTEX_DEFAULT :: 0
+PTHREAD_MUTEX_NORMAL :: 1
+PTHREAD_MUTEX_ERRORCHECK :: 2
+PTHREAD_MUTEX_RECURSIVE :: 3
+
+PTHREAD_DETACHED :: 0x1
+PTHREAD_SCOPE_SYSTEM :: 0x2
+PTHREAD_INHERIT_SCHED :: 0x4
+PTHREAD_NOFLOAT :: 0x8
+
+PTHREAD_CREATE_DETACHED :: PTHREAD_DETACHED
+PTHREAD_CREATE_JOINABLE :: 0
+PTHREAD_SCOPE_PROCESS :: 0
+PTHREAD_EXPLICIT_SCHED :: 0
+
+SCHED_FIFO :: 1
+SCHED_RR :: 2
+SCHED_SPORADIC :: 3
+SCHED_OTHER :: 4
+
+sched_param :: struct {
+ sched_priority: c.int,
+}
+
+sem_t :: distinct rawptr
+
+PTHREAD_CANCEL_ENABLE :: 0
+PTHREAD_CANCEL_DISABLE :: 1
+PTHREAD_CANCEL_DEFERRED :: 0
+PTHREAD_CANCEL_ASYNCHRONOUS :: 2
+
+foreign import libc "system:c"
+
+@(default_calling_convention="c")
+foreign libc {
+ sem_open :: proc(name: cstring, flags: c.int) -> ^sem_t ---
+
+ sem_init :: proc(sem: ^sem_t, pshared: c.int, initial_value: c.uint) -> c.int ---
+ sem_destroy :: proc(sem: ^sem_t) -> c.int ---
+ sem_post :: proc(sem: ^sem_t) -> c.int ---
+ sem_wait :: proc(sem: ^sem_t) -> c.int ---
+ sem_trywait :: proc(sem: ^sem_t) -> c.int ---
+
+ pthread_yield :: proc() ---
+
+ pthread_setcancelstate :: proc (state: c.int, old_state: ^c.int) -> c.int ---
+ pthread_setcanceltype :: proc (type: c.int, old_type: ^c.int) -> c.int ---
+ pthread_cancel :: proc (thread: pthread_t) -> c.int ---
+}
diff --git a/core/sys/unix/pthread_unix.odin b/core/sys/unix/pthread_unix.odin
index 8bf397647..4fe3c8dfa 100644
--- a/core/sys/unix/pthread_unix.odin
+++ b/core/sys/unix/pthread_unix.odin
@@ -1,4 +1,4 @@
-//+build linux, darwin, freebsd, openbsd
+//+build linux, darwin, freebsd, openbsd, haiku
package unix
foreign import "system:pthread"
@@ -16,6 +16,8 @@ foreign pthread {
// retval is a pointer to a location to put the return value of the thread proc.
pthread_join :: proc(t: pthread_t, retval: ^rawptr) -> c.int ---
+ pthread_kill :: proc(t: pthread_t, sig: c.int) -> c.int ---
+
pthread_self :: proc() -> pthread_t ---
pthread_equal :: proc(a, b: pthread_t) -> b32 ---
@@ -31,15 +33,9 @@ foreign pthread {
pthread_attr_getschedparam :: proc(attrs: ^pthread_attr_t, param: ^sched_param) -> c.int ---
pthread_attr_setschedparam :: proc(attrs: ^pthread_attr_t, param: ^sched_param) -> c.int ---
- pthread_attr_getschedpolicy :: proc(t: ^pthread_attr_t, policy: ^c.int) -> c.int ---
- pthread_attr_setschedpolicy :: proc(t: ^pthread_attr_t, policy: c.int) -> c.int ---
-
// states: PTHREAD_CREATE_DETACHED, PTHREAD_CREATE_JOINABLE
pthread_attr_setdetachstate :: proc(attrs: ^pthread_attr_t, detach_state: c.int) -> c.int ---
-
- // scheds: PTHREAD_INHERIT_SCHED, PTHREAD_EXPLICIT_SCHED
- pthread_attr_setinheritsched :: proc(attrs: ^pthread_attr_t, sched: c.int) -> c.int ---
-
+
// NOTE(tetra, 2019-11-06): WARNING: Different systems have different alignment requirements.
// For maximum usefulness, use the OS's page size.
// ALSO VERY MAJOR WARNING: `stack_ptr` must be the LAST byte of the stack on systems
@@ -52,8 +48,20 @@ foreign pthread {
pthread_attr_setstack :: proc(attrs: ^pthread_attr_t, stack_ptr: rawptr, stack_size: u64) -> c.int ---
pthread_attr_getstack :: proc(attrs: ^pthread_attr_t, stack_ptr: ^rawptr, stack_size: ^u64) -> c.int ---
+ pthread_sigmask :: proc(how: c.int, set: rawptr, oldset: rawptr) -> c.int ---
+
sched_yield :: proc() -> c.int ---
+}
+
+// NOTE: Unimplemented in Haiku.
+when ODIN_OS != .Haiku {
+ foreign pthread {
+ // scheds: PTHREAD_INHERIT_SCHED, PTHREAD_EXPLICIT_SCHED
+ pthread_attr_setinheritsched :: proc(attrs: ^pthread_attr_t, sched: c.int) -> c.int ---
+ pthread_attr_getschedpolicy :: proc(t: ^pthread_attr_t, policy: ^c.int) -> c.int ---
+ pthread_attr_setschedpolicy :: proc(t: ^pthread_attr_t, policy: c.int) -> c.int ---
+ }
}
@(default_calling_convention="c")
diff --git a/core/sys/unix/time_unix.odin b/core/sys/unix/time_unix.odin
index 108067dd4..088dc378b 100644
--- a/core/sys/unix/time_unix.odin
+++ b/core/sys/unix/time_unix.odin
@@ -1,4 +1,4 @@
-//+build linux, darwin, freebsd, openbsd
+//+build linux, darwin, freebsd, openbsd, haiku
package unix
when ODIN_OS == .Darwin {
diff --git a/core/sys/windows/gdi32.odin b/core/sys/windows/gdi32.odin
index 1d2be6dab..6d53845de 100644
--- a/core/sys/windows/gdi32.odin
+++ b/core/sys/windows/gdi32.odin
@@ -1,6 +1,8 @@
// +build windows
package sys_windows
+import "core:math/fixed"
+
foreign import gdi32 "system:Gdi32.lib"
@(default_calling_convention="system")
@@ -11,6 +13,7 @@ foreign gdi32 {
SetBkColor :: proc(hdc: HDC, color: COLORREF) -> COLORREF ---
CreateCompatibleDC :: proc(hdc: HDC) -> HDC ---
+ DeleteDC :: proc(hdc: HDC) -> BOOL ---
CreateDIBPatternBrush :: proc(h: HGLOBAL, iUsage: UINT) -> HBRUSH ---
@@ -93,3 +96,45 @@ foreign gdi32 {
RGB :: #force_inline proc "contextless" (r, g, b: u8) -> COLORREF {
return transmute(COLORREF)[4]u8{r, g, b, 0}
}
+
+FXPT2DOT30 :: distinct fixed.Fixed(i32, 30)
+
+CIEXYZ :: struct {
+ ciexyzX: FXPT2DOT30,
+ ciexyzY: FXPT2DOT30,
+ ciexyzZ: FXPT2DOT30,
+}
+
+CIEXYZTRIPLE :: struct {
+ ciexyzRed: CIEXYZ,
+ ciexyzGreen: CIEXYZ,
+ ciexyzBlue: CIEXYZ,
+}
+
+// https://learn.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapv5header
+BITMAPV5HEADER :: struct {
+ bV5Size: DWORD,
+ bV5Width: LONG,
+ bV5Height: LONG,
+ bV5Planes: WORD,
+ bV5BitCount: WORD,
+ bV5Compression: DWORD,
+ bV5SizeImage: DWORD,
+ bV5XPelsPerMeter: LONG,
+ bV5YPelsPerMeter: LONG,
+ bV5ClrUsed: DWORD,
+ bV5ClrImportant: DWORD,
+ bV5RedMask: DWORD,
+ bV5GreenMask: DWORD,
+ bV5BlueMask: DWORD,
+ bV5AlphaMask: DWORD,
+ bV5CSType: DWORD,
+ bV5Endpoints: CIEXYZTRIPLE,
+ bV5GammaRed: DWORD,
+ bV5GammaGreen: DWORD,
+ bV5GammaBlue: DWORD,
+ bV5Intent: DWORD,
+ bV5ProfileData: DWORD,
+ bV5ProfileSize: DWORD,
+ bV5Reserved: DWORD,
+}
diff --git a/core/sys/windows/kernel32.odin b/core/sys/windows/kernel32.odin
index 33422370c..952d5bb31 100644
--- a/core/sys/windows/kernel32.odin
+++ b/core/sys/windows/kernel32.odin
@@ -1200,3 +1200,22 @@ SYSTEM_LOGICAL_PROCESSOR_INFORMATION :: struct {
Relationship: LOGICAL_PROCESSOR_RELATIONSHIP,
DummyUnion: DUMMYUNIONNAME_u,
}
+
+/* Global Memory Flags */
+GMEM_FIXED :: 0x0000
+GMEM_MOVEABLE :: 0x0002
+GMEM_NOCOMPACT :: 0x0010
+GMEM_NODISCARD :: 0x0020
+GMEM_ZEROINIT :: 0x0040
+GMEM_MODIFY :: 0x0080
+GMEM_DISCARDABLE :: 0x0100
+GMEM_NOT_BANKED :: 0x1000
+GMEM_SHARE :: 0x2000
+GMEM_DDESHARE :: 0x2000
+GMEM_NOTIFY :: 0x4000
+GMEM_LOWER :: GMEM_NOT_BANKED
+GMEM_VALID_FLAGS :: 0x7F72
+GMEM_INVALID_HANDLE :: 0x8000
+
+GHND :: (GMEM_MOVEABLE | GMEM_ZEROINIT)
+GPTR :: (GMEM_FIXED | GMEM_ZEROINIT)
diff --git a/core/sys/windows/shell32.odin b/core/sys/windows/shell32.odin
index 358b8482f..4108d54d8 100644
--- a/core/sys/windows/shell32.odin
+++ b/core/sys/windows/shell32.odin
@@ -25,6 +25,7 @@ foreign shell32 {
SHAppBarMessage :: proc(dwMessage: DWORD, pData: PAPPBARDATA) -> UINT_PTR ---
Shell_NotifyIconW :: proc(dwMessage: DWORD, lpData: ^NOTIFYICONDATAW) -> BOOL ---
+ SHChangeNotify :: proc(wEventId: LONG, uFlags: UINT, dwItem1: LPCVOID, dwItem2: LPCVOID) ---
SHGetKnownFolderIDList :: proc(rfid: REFKNOWNFOLDERID, dwFlags: /* KNOWN_FOLDER_FLAG */ DWORD, hToken: HANDLE, ppidl: rawptr) -> HRESULT ---
SHSetKnownFolderPath :: proc(rfid: REFKNOWNFOLDERID, dwFlags: /* KNOWN_FOLDER_FLAG */ DWORD, hToken: HANDLE, pszPath: PCWSTR ) -> HRESULT ---
@@ -91,3 +92,53 @@ KNOWN_FOLDER_FLAG :: enum u32 {
SIMPLE_IDLIST = 0x00000100,
ALIAS_ONLY = 0x80000000,
}
+
+SHCNRF_InterruptLevel :: 0x0001
+SHCNRF_ShellLevel :: 0x0002
+SHCNRF_RecursiveInterrupt :: 0x1000
+SHCNRF_NewDelivery :: 0x8000
+
+SHCNE_RENAMEITEM :: 0x00000001
+SHCNE_CREATE :: 0x00000002
+SHCNE_DELETE :: 0x00000004
+SHCNE_MKDIR :: 0x00000008
+SHCNE_RMDIR :: 0x00000010
+SHCNE_MEDIAINSERTED :: 0x00000020
+SHCNE_MEDIAREMOVED :: 0x00000040
+SHCNE_DRIVEREMOVED :: 0x00000080
+SHCNE_DRIVEADD :: 0x00000100
+SHCNE_NETSHARE :: 0x00000200
+SHCNE_NETUNSHARE :: 0x00000400
+SHCNE_ATTRIBUTES :: 0x00000800
+SHCNE_UPDATEDIR :: 0x00001000
+SHCNE_UPDATEITEM :: 0x00002000
+SHCNE_SERVERDISCONNECT :: 0x00004000
+SHCNE_UPDATEIMAGE :: 0x00008000
+SHCNE_DRIVEADDGUI :: 0x00010000
+SHCNE_RENAMEFOLDER :: 0x00020000
+SHCNE_FREESPACE :: 0x00040000
+
+SHCNE_EXTENDED_EVENT :: 0x04000000
+
+SHCNE_ASSOCCHANGED :: 0x08000000
+
+SHCNE_DISKEVENTS :: 0x0002381F
+SHCNE_GLOBALEVENTS :: 0x0C0581E0
+SHCNE_ALLEVENTS :: 0x7FFFFFFF
+SHCNE_INTERRUPT :: 0x80000000
+
+SHCNEE_ORDERCHANGED :: 2
+SHCNEE_MSI_CHANGE :: 4
+SHCNEE_MSI_UNINSTALL :: 5
+
+SHCNF_IDLIST :: 0x0000
+SHCNF_PATHA :: 0x0001
+SHCNF_PRINTERA :: 0x0002
+SHCNF_DWORD :: 0x0003
+SHCNF_PATHW :: 0x0005
+SHCNF_PRINTERW :: 0x0006
+SHCNF_TYPE :: 0x00FF
+SHCNF_FLUSH :: 0x1000
+SHCNF_FLUSHNOWAIT :: 0x3000
+
+SHCNF_NOTIFYRECURSIVE :: 0x10000
diff --git a/core/sys/windows/user32.odin b/core/sys/windows/user32.odin
index 8c0fa246d..a589c3ec9 100644
--- a/core/sys/windows/user32.odin
+++ b/core/sys/windows/user32.odin
@@ -47,6 +47,7 @@ foreign user32 {
UpdateWindow :: proc(hWnd: HWND) -> BOOL ---
SetActiveWindow :: proc(hWnd: HWND) -> HWND ---
GetActiveWindow :: proc() -> HWND ---
+ RedrawWindow :: proc(hwnd: HWND, lprcUpdate: LPRECT, hrgnUpdate: HRGN, flags: RedrawWindowFlags) -> BOOL ---
GetMessageW :: proc(lpMsg: ^MSG, hWnd: HWND, wMsgFilterMin: UINT, wMsgFilterMax: UINT) -> BOOL ---
@@ -85,6 +86,14 @@ foreign user32 {
LoadCursorW :: proc(hInstance: HINSTANCE, lpCursorName: LPCWSTR) -> HCURSOR ---
LoadImageW :: proc(hInst: HINSTANCE, name: LPCWSTR, type: UINT, cx: c_int, cy: c_int, fuLoad: UINT) -> HANDLE ---
+ CreateIcon :: proc(hInstance: HINSTANCE, nWidth: c_int, nHeight: c_int, cPlanes: BYTE, cBitsPixel: BYTE, lpbANDbits: PBYTE, lpbXORbits: PBYTE) -> HICON ---
+ CreateIconFromResource :: proc(presbits: PBYTE, dwResSize: DWORD, fIcon: BOOL, dwVer: DWORD) -> HICON ---
+ DestroyIcon :: proc(hIcon: HICON) -> BOOL ---
+ DrawIcon :: proc(hDC: HDC, X: c_int, Y: c_int, hIcon: HICON) -> BOOL ---
+
+ CreateCursor :: proc(hInst: HINSTANCE, xHotSpot: c_int, yHotSpot: c_int, nWidth: c_int, nHeight: c_int, pvANDPlane: PVOID, pvXORPlane: PVOID) -> HCURSOR ---
+ DestroyCursor :: proc(hCursor: HCURSOR) -> BOOL ---
+
GetWindowRect :: proc(hWnd: HWND, lpRect: LPRECT) -> BOOL ---
GetClientRect :: proc(hWnd: HWND, lpRect: LPRECT) -> BOOL ---
ClientToScreen :: proc(hWnd: HWND, lpPoint: LPPOINT) -> BOOL ---
@@ -134,7 +143,7 @@ foreign user32 {
GetKeyState :: proc(nVirtKey: c_int) -> SHORT ---
GetAsyncKeyState :: proc(vKey: c_int) -> SHORT ---
-
+
GetKeyboardState :: proc(lpKeyState: PBYTE) -> BOOL ---
MapVirtualKeyW :: proc(uCode: UINT, uMapType: UINT) -> UINT ---
@@ -166,7 +175,7 @@ foreign user32 {
MonitorFromRect :: proc(lprc: LPRECT, dwFlags: Monitor_From_Flags) -> HMONITOR ---
MonitorFromWindow :: proc(hwnd: HWND, dwFlags: Monitor_From_Flags) -> HMONITOR ---
EnumDisplayMonitors :: proc(hdc: HDC, lprcClip: LPRECT, lpfnEnum: Monitor_Enum_Proc, dwData: LPARAM) -> BOOL ---
-
+
EnumWindows :: proc(lpEnumFunc: Window_Enum_Proc, lParam: LPARAM) -> BOOL ---
SetThreadDpiAwarenessContext :: proc(dpiContext: DPI_AWARENESS_CONTEXT) -> DPI_AWARENESS_CONTEXT ---
@@ -239,6 +248,9 @@ foreign user32 {
GetSystemMenu :: proc(hWnd: HWND, bRevert: BOOL) -> HMENU ---
EnableMenuItem :: proc(hMenu: HMENU, uIDEnableItem: UINT, uEnable: UINT) -> BOOL ---
+
+ DrawTextW :: proc(hdc: HDC, lpchText: LPCWSTR, cchText: INT, lprc: LPRECT, format: DrawTextFormat) -> INT ---
+ DrawTextExW :: proc(hdc: HDC, lpchText: LPCWSTR, cchText: INT, lprc: LPRECT, format: DrawTextFormat, lpdtp: PDRAWTEXTPARAMS) -> INT ---
}
CreateWindowW :: #force_inline proc "system" (
@@ -471,6 +483,19 @@ RI_MOUSE_BUTTON_5_UP :: 0x0200
RI_MOUSE_WHEEL :: 0x0400
RI_MOUSE_HWHEEL :: 0x0800
+HID_USAGE_PAGE_GENERIC :: 0x01
+HID_USAGE_PAGE_GAME :: 0x05
+HID_USAGE_PAGE_LED :: 0x08
+HID_USAGE_PAGE_BUTTON :: 0x09
+
+HID_USAGE_GENERIC_POINTER :: 0x01
+HID_USAGE_GENERIC_MOUSE :: 0x02
+HID_USAGE_GENERIC_JOYSTICK :: 0x04
+HID_USAGE_GENERIC_GAMEPAD :: 0x05
+HID_USAGE_GENERIC_KEYBOARD :: 0x06
+HID_USAGE_GENERIC_KEYPAD :: 0x07
+HID_USAGE_GENERIC_MULTI_AXIS_CONTROLLER :: 0x08
+
WINDOWPLACEMENT :: struct {
length: UINT,
flags: UINT,
@@ -493,3 +518,54 @@ WINDOWINFO :: struct {
wCreatorVersion: WORD,
}
PWINDOWINFO :: ^WINDOWINFO
+
+DRAWTEXTPARAMS :: struct {
+ cbSize : UINT ,
+ iTabLength: int ,
+ iLeftMargin: int ,
+ iRightMargin: int ,
+ uiLengthDrawn: UINT ,
+}
+PDRAWTEXTPARAMS :: ^DRAWTEXTPARAMS
+
+DrawTextFormat :: enum UINT {
+ DT_TOP = 0x00000000,
+ DT_LEFT = 0x00000000,
+ DT_CENTER = 0x00000001,
+ DT_RIGHT = 0x00000002,
+ DT_VCENTER = 0x00000004,
+ DT_BOTTOM = 0x00000008,
+ DT_WORDBREAK = 0x00000010,
+ DT_SINGLELINE = 0x00000020,
+ DT_EXPANDTABS = 0x00000040,
+ DT_TABSTOP = 0x00000080,
+ DT_NOCLIP = 0x00000100,
+ DT_EXTERNALLEADING = 0x00000200,
+ DT_CALCRECT = 0x00000400,
+ DT_NOPREFIX = 0x00000800,
+ DT_INTERNAL = 0x00001000,
+ DT_EDITCONTROL = 0x00002000,
+ DT_PATH_ELLIPSIS = 0x00004000,
+ DT_END_ELLIPSIS = 0x00008000,
+ DT_MODIFYSTRING = 0x00010000,
+ DT_RTLREADING = 0x00020000,
+ DT_WORD_ELLIPSIS = 0x00040000,
+ DT_NOFULLWIDTHCHARBREAK = 0x00080000,
+ DT_HIDEPREFIX = 0x00100000,
+ DT_PREFIXONLY = 0x00200000,
+}
+
+RedrawWindowFlags :: enum UINT {
+ RDW_INVALIDATE = 0x0001,
+ RDW_INTERNALPAINT = 0x0002,
+ RDW_ERASE = 0x0004,
+ RDW_VALIDATE = 0x0008,
+ RDW_NOINTERNALPAINT = 0x0010,
+ RDW_NOERASE = 0x0020,
+ RDW_NOCHILDREN = 0x0040,
+ RDW_ALLCHILDREN = 0x0080,
+ RDW_UPDATENOW = 0x0100,
+ RDW_ERASENOW = 0x0200,
+ RDW_FRAME = 0x0400,
+ RDW_NOFRAME = 0x0800,
+}
diff --git a/core/sys/windows/winmm.odin b/core/sys/windows/winmm.odin
index 0807df8de..8ddef29c0 100644
--- a/core/sys/windows/winmm.odin
+++ b/core/sys/windows/winmm.odin
@@ -11,6 +11,32 @@ foreign winmm {
timeBeginPeriod :: proc(uPeriod: UINT) -> MMRESULT ---
timeEndPeriod :: proc(uPeriod: UINT) -> MMRESULT ---
timeGetTime :: proc() -> DWORD ---
+
+ waveOutGetNumDevs :: proc() -> UINT ---
+ waveOutGetDevCapsW :: proc(uDeviceID: UINT_PTR, pwoc: LPWAVEOUTCAPSW, cbwoc: UINT) -> MMRESULT ---
+ waveOutGetVolume :: proc(hwo: HWAVEOUT, pdwVolume: LPDWORD) -> MMRESULT ---
+ waveOutSetVolume :: proc(hwo: HWAVEOUT, dwVolume: DWORD) -> MMRESULT ---
+ waveOutGetErrorTextW :: proc(mmrError: MMRESULT, pszText: LPWSTR, cchText: UINT) -> MMRESULT ---
+ waveOutOpen :: proc(phwo: LPHWAVEOUT, uDeviceID: UINT, pwfx: LPCWAVEFORMATEX, dwCallback: DWORD_PTR, dwInstance: DWORD_PTR, fdwOpen: DWORD) -> MMRESULT ---
+ waveOutClose :: proc(hwo: HWAVEOUT) -> MMRESULT ---
+ waveOutPrepareHeader :: proc(hwo: HWAVEOUT, pwh: LPWAVEHDR, cbwh: UINT) -> MMRESULT ---
+ waveOutUnprepareHeader :: proc(hwo: HWAVEOUT, pwh: LPWAVEHDR, cbwh: UINT) -> MMRESULT ---
+ waveOutWrite :: proc(hwo: HWAVEOUT, pwh: LPWAVEHDR, cbwh: UINT) -> MMRESULT ---
+ waveOutPause :: proc(hwo: HWAVEOUT) -> MMRESULT ---
+ waveOutRestart :: proc(hwo: HWAVEOUT) -> MMRESULT ---
+ waveOutReset :: proc(hwo: HWAVEOUT) -> MMRESULT ---
+ waveOutBreakLoop :: proc(hwo: HWAVEOUT) -> MMRESULT ---
+ waveOutGetPosition :: proc(hwo: HWAVEOUT, pmmt: LPMMTIME, cbmmt: UINT) -> MMRESULT ---
+ waveOutGetPitch :: proc(hwo: HWAVEOUT, pdwPitch: LPDWORD) -> MMRESULT ---
+ waveOutSetPitch :: proc(hwo: HWAVEOUT, pdwPitch: DWORD) -> MMRESULT ---
+ waveOutGetPlaybackRate :: proc(hwo: HWAVEOUT, pdwRate: LPDWORD) -> MMRESULT ---
+ waveOutSetPlaybackRate :: proc(hwo: HWAVEOUT, pdwRate: DWORD) -> MMRESULT ---
+ waveOutGetID :: proc(hwo: HWAVEOUT, puDeviceID: LPUINT) -> MMRESULT ---
+
+ waveInGetNumDevs :: proc() -> UINT ---
+ waveInGetDevCapsW :: proc(uDeviceID: UINT_PTR, pwic: LPWAVEINCAPSW, cbwic: UINT) -> MMRESULT ---
+
+ PlaySoundW :: proc(pszSound: LPCWSTR, hmod: HMODULE, fdwSound: DWORD) -> BOOL ---
}
LPTIMECAPS :: ^TIMECAPS
@@ -169,4 +195,182 @@ MCIERR_NO_IDENTITY :: MCIERR_BASE + 94
MIXERR_INVALLINE :: (MIXERR_BASE + 0)
MIXERR_INVALCONTROL :: (MIXERR_BASE + 1)
MIXERR_INVALVALUE :: (MIXERR_BASE + 2)
-MIXERR_LASTERROR :: (MIXERR_BASE + 2) \ No newline at end of file
+MIXERR_LASTERROR :: (MIXERR_BASE + 2)
+
+/* waveform output */
+MM_WOM_OPEN :: 0x3BB
+MM_WOM_CLOSE :: 0x3BC
+MM_WOM_DONE :: 0x3BD
+/* waveform input */
+MM_WIM_OPEN :: 0x3BE
+MM_WIM_CLOSE :: 0x3BF
+MM_WIM_DATA :: 0x3C0
+
+WOM_OPEN :: MM_WOM_OPEN
+WOM_CLOSE :: MM_WOM_CLOSE
+WOM_DONE :: MM_WOM_DONE
+WIM_OPEN :: MM_WIM_OPEN
+WIM_CLOSE :: MM_WIM_CLOSE
+WIM_DATA :: MM_WIM_DATA
+
+WAVE_MAPPER : UINT : 0xFFFFFFFF // -1
+
+WAVE_FORMAT_QUERY :: 0x0001
+WAVE_ALLOWSYNC :: 0x0002
+WAVE_MAPPED :: 0x0004
+WAVE_FORMAT_DIRECT :: 0x0008
+WAVE_FORMAT_DIRECT_QUERY :: (WAVE_FORMAT_QUERY | WAVE_FORMAT_DIRECT)
+WAVE_MAPPED_DEFAULT_COMMUNICATION_DEVICE :: 0x0010
+
+WHDR_DONE :: 0x00000001 /* done bit */
+WHDR_PREPARED :: 0x00000002 /* set if this header has been prepared */
+WHDR_BEGINLOOP :: 0x00000004 /* loop start block */
+WHDR_ENDLOOP :: 0x00000008 /* loop end block */
+WHDR_INQUEUE :: 0x00000010 /* reserved for driver */
+
+WAVECAPS_PITCH :: 0x0001 /* supports pitch control */
+WAVECAPS_PLAYBACKRATE :: 0x0002 /* supports playback rate control */
+WAVECAPS_VOLUME :: 0x0004 /* supports volume control */
+WAVECAPS_LRVOLUME :: 0x0008 /* separate left-right volume control */
+WAVECAPS_SYNC :: 0x0010
+WAVECAPS_SAMPLEACCURATE :: 0x0020
+
+WAVE_INVALIDFORMAT :: 0x00000000 /* invalid format */
+WAVE_FORMAT_1M08 :: 0x00000001 /* 11.025 kHz, Mono, 8-bit */
+WAVE_FORMAT_1S08 :: 0x00000002 /* 11.025 kHz, Stereo, 8-bit */
+WAVE_FORMAT_1M16 :: 0x00000004 /* 11.025 kHz, Mono, 16-bit */
+WAVE_FORMAT_1S16 :: 0x00000008 /* 11.025 kHz, Stereo, 16-bit */
+WAVE_FORMAT_2M08 :: 0x00000010 /* 22.05 kHz, Mono, 8-bit */
+WAVE_FORMAT_2S08 :: 0x00000020 /* 22.05 kHz, Stereo, 8-bit */
+WAVE_FORMAT_2M16 :: 0x00000040 /* 22.05 kHz, Mono, 16-bit */
+WAVE_FORMAT_2S16 :: 0x00000080 /* 22.05 kHz, Stereo, 16-bit */
+WAVE_FORMAT_4M08 :: 0x00000100 /* 44.1 kHz, Mono, 8-bit */
+WAVE_FORMAT_4S08 :: 0x00000200 /* 44.1 kHz, Stereo, 8-bit */
+WAVE_FORMAT_4M16 :: 0x00000400 /* 44.1 kHz, Mono, 16-bit */
+WAVE_FORMAT_4S16 :: 0x00000800 /* 44.1 kHz, Stereo, 16-bit */
+WAVE_FORMAT_44M08 :: 0x00000100 /* 44.1 kHz, Mono, 8-bit */
+WAVE_FORMAT_44S08 :: 0x00000200 /* 44.1 kHz, Stereo, 8-bit */
+WAVE_FORMAT_44M16 :: 0x00000400 /* 44.1 kHz, Mono, 16-bit */
+WAVE_FORMAT_44S16 :: 0x00000800 /* 44.1 kHz, Stereo, 16-bit */
+WAVE_FORMAT_48M08 :: 0x00001000 /* 48 kHz, Mono, 8-bit */
+WAVE_FORMAT_48S08 :: 0x00002000 /* 48 kHz, Stereo, 8-bit */
+WAVE_FORMAT_48M16 :: 0x00004000 /* 48 kHz, Mono, 16-bit */
+WAVE_FORMAT_48S16 :: 0x00008000 /* 48 kHz, Stereo, 16-bit */
+WAVE_FORMAT_96M08 :: 0x00010000 /* 96 kHz, Mono, 8-bit */
+WAVE_FORMAT_96S08 :: 0x00020000 /* 96 kHz, Stereo, 8-bit */
+WAVE_FORMAT_96M16 :: 0x00040000 /* 96 kHz, Mono, 16-bit */
+WAVE_FORMAT_96S16 :: 0x00080000 /* 96 kHz, Stereo, 16-bit */
+
+HWAVE :: distinct HANDLE
+HWAVEIN :: distinct HANDLE
+HWAVEOUT :: distinct HANDLE
+
+LPHWAVEIN :: ^HWAVEIN
+LPHWAVEOUT :: ^HWAVEOUT
+
+// https://learn.microsoft.com/en-us/windows/win32/multimedia/multimedia-timer-structures
+MMTIME :: struct {
+ wType: UINT,
+ u: struct #raw_union {
+ ms: DWORD,
+ sample: DWORD,
+ cb: DWORD,
+ ticks: DWORD,
+ smpte: struct {
+ hour: BYTE,
+ min: BYTE,
+ sec: BYTE,
+ frame: BYTE,
+ fps: BYTE,
+ dummy: BYTE,
+ pad: [2]BYTE,
+ },
+ midi: struct {
+ songptrpos: DWORD,
+ },
+ },
+}
+LPMMTIME :: ^MMTIME
+
+MAXPNAMELEN :: 32
+MAXERRORLENGTH :: 256
+MMVERSION :: UINT
+
+/* flags for wFormatTag field of WAVEFORMAT */
+WAVE_FORMAT_PCM :: 1
+
+WAVEFORMATEX :: struct {
+ wFormatTag: WORD,
+ nChannels: WORD,
+ nSamplesPerSec: DWORD,
+ nAvgBytesPerSec: DWORD,
+ nBlockAlign: WORD,
+ wBitsPerSample: WORD,
+ cbSize: WORD,
+}
+LPCWAVEFORMATEX :: ^WAVEFORMATEX
+
+WAVEHDR :: struct {
+ lpData: LPSTR, /* pointer to locked data buffer */
+ dwBufferLength: DWORD, /* length of data buffer */
+ dwBytesRecorded: DWORD, /* used for input only */
+ dwUser: DWORD_PTR, /* for client's use */
+ dwFlags: DWORD, /* assorted flags (see defines) */
+ dwLoops: DWORD, /* loop control counter */
+ lpNext: LPWAVEHDR, /* reserved for driver */
+ reserved: DWORD_PTR, /* reserved for driver */
+}
+LPWAVEHDR :: ^WAVEHDR
+
+WAVEINCAPSW :: struct {
+ wMid: WORD, /* manufacturer ID */
+ wPid: WORD, /* product ID */
+ vDriverVersion: MMVERSION, /* version of the driver */
+ szPname: [MAXPNAMELEN]WCHAR, /* product name (NULL terminated string) */
+ dwFormats: DWORD, /* formats supported */
+ wChannels: WORD, /* number of channels supported */
+ wReserved1: WORD, /* structure packing */
+}
+LPWAVEINCAPSW :: ^WAVEINCAPSW
+
+WAVEOUTCAPSW :: struct {
+ wMid: WORD, /* manufacturer ID */
+ wPid: WORD, /* product ID */
+ vDriverVersion: MMVERSION, /* version of the driver */
+ szPname: [MAXPNAMELEN]WCHAR, /* product name (NULL terminated string) */
+ dwFormats: DWORD, /* formats supported */
+ wChannels: WORD, /* number of sources supported */
+ wReserved1: WORD, /* packing */
+ dwSupport: DWORD, /* functionality supported by driver */
+}
+LPWAVEOUTCAPSW :: ^WAVEOUTCAPSW
+
+// flag values for PlaySound
+SND_SYNC :: 0x0000 /* play synchronously (default) */
+SND_ASYNC :: 0x0001 /* play asynchronously */
+SND_NODEFAULT :: 0x0002 /* silence (!default) if sound not found */
+SND_MEMORY :: 0x0004 /* pszSound points to a memory file */
+SND_LOOP :: 0x0008 /* loop the sound until next sndPlaySound */
+SND_NOSTOP :: 0x0010 /* don't stop any currently playing sound */
+
+SND_NOWAIT :: 0x00002000 /* don't wait if the driver is busy */
+SND_ALIAS :: 0x00010000 /* name is a registry alias */
+SND_ALIAS_ID :: 0x00110000 /* alias is a predefined ID */
+SND_FILENAME :: 0x00020000 /* name is file name */
+SND_RESOURCE :: 0x00040004 /* name is resource name or atom */
+
+SND_PURGE :: 0x0040 /* purge non-static events for task */
+SND_APPLICATION :: 0x0080 /* look for application specific association */
+
+SND_SENTRY :: 0x00080000 /* Generate a SoundSentry event with this sound */
+SND_RING :: 0x00100000 /* Treat this as a "ring" from a communications app - don't duck me */
+SND_SYSTEM :: 0x00200000 /* Treat this as a system sound */
+
+
+CALLBACK_TYPEMASK :: 0x00070000 /* callback type mask */
+CALLBACK_NULL :: 0x00000000 /* no callback */
+CALLBACK_WINDOW :: 0x00010000 /* dwCallback is a HWND */
+CALLBACK_TASK :: 0x00020000 /* dwCallback is a HTASK */
+CALLBACK_FUNCTION :: 0x00030000 /* dwCallback is a FARPROC */
+CALLBACK_THREAD :: CALLBACK_TASK /* thread ID replaces 16 bit task */
+CALLBACK_EVENT :: 0x00050000 /* dwCallback is an EVENT Handle */
diff --git a/core/thread/thread_unix.odin b/core/thread/thread_unix.odin
index 19e421646..c75710873 100644
--- a/core/thread/thread_unix.odin
+++ b/core/thread/thread_unix.odin
@@ -1,4 +1,4 @@
-// +build linux, darwin, freebsd, openbsd
+// +build linux, darwin, freebsd, openbsd, haiku
// +private
package thread
@@ -78,7 +78,9 @@ _create :: proc(procedure: Thread_Proc, priority: Thread_Priority) -> ^Thread {
// NOTE(tetra, 2019-11-01): These only fail if their argument is invalid.
assert(unix.pthread_attr_setdetachstate(&attrs, unix.PTHREAD_CREATE_JOINABLE) == 0)
- assert(unix.pthread_attr_setinheritsched(&attrs, unix.PTHREAD_EXPLICIT_SCHED) == 0)
+ when ODIN_OS != .Haiku {
+ assert(unix.pthread_attr_setinheritsched(&attrs, unix.PTHREAD_EXPLICIT_SCHED) == 0)
+ }
thread := new(Thread)
if thread == nil {
@@ -88,8 +90,11 @@ _create :: proc(procedure: Thread_Proc, priority: Thread_Priority) -> ^Thread {
// Set thread priority.
policy: i32
- res := unix.pthread_attr_getschedpolicy(&attrs, &policy)
- assert(res == 0)
+ res: i32
+ when ODIN_OS != .Haiku {
+ res = unix.pthread_attr_getschedpolicy(&attrs, &policy)
+ assert(res == 0)
+ }
params: unix.sched_param
res = unix.pthread_attr_getschedparam(&attrs, &params)
assert(res == 0)
diff --git a/core/time/time_unix.odin b/core/time/time_unix.odin
index ba0d91527..1c46b5994 100644
--- a/core/time/time_unix.odin
+++ b/core/time/time_unix.odin
@@ -1,5 +1,5 @@
//+private
-//+build linux, darwin, freebsd, openbsd
+//+build linux, darwin, freebsd, openbsd, haiku
package time
import "core:sys/unix"
diff --git a/src/build_settings.cpp b/src/build_settings.cpp
index 0bcb9f298..fdaa971f1 100644
--- a/src/build_settings.cpp
+++ b/src/build_settings.cpp
@@ -18,6 +18,7 @@ enum TargetOsKind : u16 {
TargetOs_essence,
TargetOs_freebsd,
TargetOs_openbsd,
+ TargetOs_haiku,
TargetOs_wasi,
TargetOs_js,
@@ -78,6 +79,7 @@ gb_global String target_os_names[TargetOs_COUNT] = {
str_lit("essence"),
str_lit("freebsd"),
str_lit("openbsd"),
+ str_lit("haiku"),
str_lit("wasi"),
str_lit("js"),
@@ -542,6 +544,13 @@ gb_global TargetMetrics target_openbsd_amd64 = {
str_lit("e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"),
};
+gb_global TargetMetrics target_haiku_amd64 = {
+ TargetOs_haiku,
+ TargetArch_amd64,
+ 8, 8, 8, 16,
+ str_lit("x86_64-unknown-haiku"),
+};
+
gb_global TargetMetrics target_essence_amd64 = {
TargetOs_essence,
TargetArch_amd64,
@@ -641,6 +650,7 @@ gb_global NamedTargetMetrics named_targets[] = {
{ str_lit("freebsd_amd64"), &target_freebsd_amd64 },
{ str_lit("openbsd_amd64"), &target_openbsd_amd64 },
+ { str_lit("haiku_amd64"), &target_haiku_amd64 },
{ str_lit("freestanding_wasm32"), &target_freestanding_wasm32 },
{ str_lit("wasi_wasm32"), &target_wasi_wasm32 },
@@ -872,6 +882,58 @@ gb_internal String internal_odin_root_dir(void) {
return path;
}
+#elif defined(GB_SYSTEM_HAIKU)
+
+#include <FindDirectory.h>
+
+gb_internal String path_to_fullpath(gbAllocator a, String s, bool *ok_);
+
+gb_internal String internal_odin_root_dir(void) {
+ String path = global_module_path;
+ isize len, i;
+ u8 *text;
+
+ if (global_module_path_set) {
+ return global_module_path;
+ }
+
+ auto path_buf = array_make<char>(heap_allocator(), 300);
+ defer (array_free(&path_buf));
+
+ len = 0;
+ for (;;) {
+ u32 sz = path_buf.count;
+ int res = find_path(B_APP_IMAGE_SYMBOL, B_FIND_PATH_IMAGE_PATH, nullptr, &path_buf[0], sz);
+ if(res == B_OK) {
+ len = sz;
+ break;
+ } else {
+ array_resize(&path_buf, sz + 1);
+ }
+ }
+
+ mutex_lock(&string_buffer_mutex);
+ defer (mutex_unlock(&string_buffer_mutex));
+
+ text = gb_alloc_array(permanent_allocator(), u8, len + 1);
+ gb_memmove(text, &path_buf[0], len);
+
+ path = path_to_fullpath(heap_allocator(), make_string(text, len), nullptr);
+
+ for (i = path.len-1; i >= 0; i--) {
+ u8 c = path[i];
+ if (c == '/' || c == '\\') {
+ break;
+ }
+ path.len--;
+ }
+
+ global_module_path = path;
+ global_module_path_set = true;
+
+ return path;
+}
+
#elif defined(GB_SYSTEM_OSX)
#include <mach-o/dyld.h>
@@ -888,6 +950,7 @@ gb_internal String internal_odin_root_dir(void) {
}
auto path_buf = array_make<char>(heap_allocator(), 300);
+ defer (array_free(&path_buf));
len = 0;
for (;;) {
@@ -920,9 +983,6 @@ gb_internal String internal_odin_root_dir(void) {
global_module_path = path;
global_module_path_set = true;
-
- // array_free(&path_buf);
-
return path;
}
#else
@@ -1301,6 +1361,8 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta
metrics = &target_freebsd_amd64;
#elif defined(GB_SYSTEM_OPENBSD)
metrics = &target_openbsd_amd64;
+ #elif defined(GB_SYSTEM_HAIKU)
+ metrics = &target_haiku_amd64;
#elif defined(GB_CPU_ARM)
metrics = &target_linux_arm64;
#else
@@ -1405,6 +1467,9 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta
case TargetOs_openbsd:
bc->link_flags = str_lit("-arch x86-64 ");
break;
+ case TargetOs_haiku:
+ bc->link_flags = str_lit("-arch x86-64 ");
+ break;
}
} else if (bc->metrics.arch == TargetArch_i386) {
switch (bc->metrics.os) {
diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp
index c85fb28d6..e1b1cd693 100644
--- a/src/check_builtin.cpp
+++ b/src/check_builtin.cpp
@@ -4928,6 +4928,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
case TargetOs_essence:
case TargetOs_freebsd:
case TargetOs_openbsd:
+ case TargetOs_haiku:
switch (build_context.metrics.arch) {
case TargetArch_i386:
case TargetArch_amd64:
diff --git a/src/checker.cpp b/src/checker.cpp
index 5827fc695..72c0ae574 100644
--- a/src/checker.cpp
+++ b/src/checker.cpp
@@ -1010,6 +1010,7 @@ gb_internal void init_universal(void) {
{"Linux", TargetOs_linux},
{"Essence", TargetOs_essence},
{"FreeBSD", TargetOs_freebsd},
+ {"Haiku", TargetOs_haiku},
{"OpenBSD", TargetOs_openbsd},
{"WASI", TargetOs_wasi},
{"JS", TargetOs_js},
diff --git a/src/gb/gb.h b/src/gb/gb.h
index 93d250f21..702647121 100644
--- a/src/gb/gb.h
+++ b/src/gb/gb.h
@@ -83,6 +83,10 @@ extern "C" {
#ifndef GB_SYSTEM_OPENBSD
#define GB_SYSTEM_OPENBSD 1
#endif
+ #elif defined(__HAIKU__) || defined(__haiku__)
+ #ifndef GB_SYSTEM_HAIKU
+ #define GB_SYSTEM_HAIKU 1
+ #endif
#else
#error This UNIX operating system is not supported
#endif
@@ -206,7 +210,7 @@ extern "C" {
#endif
#include <stdlib.h> // NOTE(bill): malloc on linux
#include <sys/mman.h>
- #if !defined(GB_SYSTEM_OSX) && !defined(__FreeBSD__) && !defined(__OpenBSD__)
+ #if !defined(GB_SYSTEM_OSX) && !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__HAIKU__)
#include <sys/sendfile.h>
#endif
#include <sys/stat.h>
@@ -247,6 +251,13 @@ extern "C" {
#include <pthread_np.h>
#define lseek64 lseek
#endif
+
+#if defined(GB_SYSTEM_HAIKU)
+ #include <stdio.h>
+ #include <pthread.h>
+ #include <kernel/OS.h>
+ #define lseek64 lseek
+#endif
#if defined(GB_SYSTEM_UNIX)
#include <semaphore.h>
@@ -801,6 +812,13 @@ typedef struct gbAffinity {
isize thread_count;
isize threads_per_core;
} gbAffinity;
+#elif defined(GB_SYSTEM_HAIKU)
+typedef struct gbAffinity {
+ b32 is_accurate;
+ isize core_count;
+ isize thread_count;
+ isize threads_per_core;
+} gbAffinity;
#else
#error TODO(bill): Unknown system
#endif
@@ -2984,6 +3002,8 @@ gb_inline u32 gb_thread_current_id(void) {
__asm__("mov %%fs:0x10,%0" : "=r"(thread_id));
#elif defined(GB_SYSTEM_LINUX)
thread_id = gettid();
+#elif defined(GB_SYSTEM_HAIKU)
+ thread_id = find_thread(NULL);
#else
#error Unsupported architecture for gb_thread_current_id()
#endif
@@ -3184,7 +3204,9 @@ b32 gb_affinity_set(gbAffinity *a, isize core, isize thread_index) {
//info.affinity_tag = cast(integer_t)index;
//result = thread_policy_set(thread, THREAD_AFFINITY_POLICY, cast(thread_policy_t)&info, THREAD_AFFINITY_POLICY_COUNT);
+#if !defined(GB_SYSTEM_HAIKU)
result = pthread_setaffinity_np(thread, sizeof(cpuset_t), &mn);
+#endif
return result == 0;
}
@@ -3240,6 +3262,29 @@ isize gb_affinity_thread_count_for_core(gbAffinity *a, isize core) {
GB_ASSERT(0 <= core && core < a->core_count);
return a->threads_per_core;
}
+#elif defined(GB_SYSTEM_HAIKU)
+#include <unistd.h>
+
+void gb_affinity_init(gbAffinity *a) {
+ a->core_count = sysconf(_SC_NPROCESSORS_ONLN);
+ a->threads_per_core = 1;
+ a->is_accurate = a->core_count > 0;
+ a->core_count = a->is_accurate ? a->core_count : 1;
+ a->thread_count = a->core_count;
+}
+
+void gb_affinity_destroy(gbAffinity *a) {
+ gb_unused(a);
+}
+
+b32 gb_affinity_set(gbAffinity *a, isize core, isize thread_index) {
+ return true;
+}
+
+isize gb_affinity_thread_count_for_core(gbAffinity *a, isize core) {
+ GB_ASSERT(0 <= core && core < a->core_count);
+ return a->threads_per_core;
+}
#else
#error TODO(bill): Unknown system
#endif
@@ -5457,7 +5502,7 @@ gb_inline b32 gb_file_copy(char const *existing_filename, char const *new_filena
}
}
- gb_free(buf);
+ gb_mfree(buf);
close(new_fd);
close(existing_fd);
diff --git a/src/linker.cpp b/src/linker.cpp
index 0144c4aaf..0cdeaf8d9 100644
--- a/src/linker.cpp
+++ b/src/linker.cpp
@@ -474,8 +474,8 @@ gb_internal i32 linker_stage(LinkerData *gen) {
link_settings = gb_string_appendc(link_settings, "-Wl,-fini,'_odin_exit_point' ");
}
- } else if (build_context.metrics.os != TargetOs_openbsd) {
- // OpenBSD defaults to PIE executable. do not pass -no-pie for it.
+ } else if (build_context.metrics.os != TargetOs_openbsd && build_context.metrics.os != TargetOs_haiku) {
+ // OpenBSD and Haiku default to PIE executable. do not pass -no-pie for it.
link_settings = gb_string_appendc(link_settings, "-no-pie ");
}
diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp
index efba19f23..ca4341525 100644
--- a/src/llvm_backend.cpp
+++ b/src/llvm_backend.cpp
@@ -2564,8 +2564,8 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
switch (build_context.reloc_mode) {
case RelocMode_Default:
- if (build_context.metrics.os == TargetOs_openbsd) {
- // Always use PIC for OpenBSD: it defaults to PIE
+ if (build_context.metrics.os == TargetOs_openbsd || build_context.metrics.os == TargetOs_haiku) {
+ // Always use PIC for OpenBSD and Haiku: they default to PIE
reloc_mode = LLVMRelocPIC;
}
break;
diff --git a/src/path.cpp b/src/path.cpp
index de80c9def..742bba7f8 100644
--- a/src/path.cpp
+++ b/src/path.cpp
@@ -1,461 +1,461 @@
-/*
- Path handling utilities.
-*/
-#if !defined(GB_SYSTEM_WINDOWS)
-#include <unistd.h>
-#endif
-
-gb_internal String remove_extension_from_path(String const &s) {
- if (s.len != 0 && s.text[s.len-1] == '.') {
- return s;
- }
- for (isize i = s.len-1; i >= 0; i--) {
- if (s[i] == '.') {
- return substring(s, 0, i);
- }
- }
- return s;
-}
-
-gb_internal String remove_directory_from_path(String const &s) {
- isize len = 0;
- for (isize i = s.len-1; i >= 0; i--) {
- if (s[i] == '/' ||
- s[i] == '\\') {
- break;
- }
- len += 1;
- }
- return substring(s, s.len-len, s.len);
-}
-
-
-// NOTE(Mark Naughton): getcwd as String
-#if !defined(GB_SYSTEM_WINDOWS)
-gb_internal String get_current_directory(void) {
- char cwd[256];
- getcwd(cwd, 256);
-
- return make_string_c(cwd);
-}
-
-#else
-gb_internal String get_current_directory(void) {
- gbAllocator a = heap_allocator();
-
- wchar_t cwd[256];
- GetCurrentDirectoryW(256, cwd);
-
- String16 wstr = make_string16_c(cwd);
-
- return string16_to_string(a, wstr);
-}
-#endif
-
-gb_internal bool path_is_directory(String path);
-
-gb_internal String directory_from_path(String const &s) {
- if (path_is_directory(s)) {
- return s;
- }
-
- isize i = s.len-1;
- for (; i >= 0; i--) {
- if (s[i] == '/' ||
- s[i] == '\\') {
- break;
- }
- }
- if (i >= 0) {
- return substring(s, 0, i);
- }
- return substring(s, 0, 0);
-}
-
-#if defined(GB_SYSTEM_WINDOWS)
- gb_internal bool path_is_directory(String path) {
- gbAllocator a = heap_allocator();
- String16 wstr = string_to_string16(a, path);
- defer (gb_free(a, wstr.text));
-
- i32 attribs = GetFileAttributesW(wstr.text);
- if (attribs < 0) return false;
-
- return (attribs & FILE_ATTRIBUTE_DIRECTORY) != 0;
- }
-
-#else
- gb_internal bool path_is_directory(String path) {
- gbAllocator a = heap_allocator();
- char *copy = cast(char *)copy_string(a, path).text;
- defer (gb_free(a, copy));
-
- struct stat s;
- if (stat(copy, &s) == 0) {
- return (s.st_mode & S_IFDIR) != 0;
- }
- return false;
- }
-#endif
-
-
-gb_internal String path_to_full_path(gbAllocator a, String path) {
- gbAllocator ha = heap_allocator();
- char *path_c = gb_alloc_str_len(ha, cast(char *)path.text, path.len);
- defer (gb_free(ha, path_c));
-
- char *fullpath = gb_path_get_full_name(a, path_c);
- String res = string_trim_whitespace(make_string_c(fullpath));
-#if defined(GB_SYSTEM_WINDOWS)
- for (isize i = 0; i < res.len; i++) {
- if (res.text[i] == '\\') {
- res.text[i] = '/';
- }
- }
-#endif
- return copy_string(a, res);
-}
-
-struct Path {
- String basename;
- String name;
- String ext;
-};
-
-// NOTE(Jeroen): Naively turns a Path into a string.
-gb_internal String path_to_string(gbAllocator a, Path path) {
- if (path.basename.len + path.name.len + path.ext.len == 0) {
- return make_string(nullptr, 0);
- }
-
- isize len = path.basename.len + 1 + path.name.len + 1;
- if (path.ext.len > 0) {
- len += path.ext.len + 1;
- }
-
- u8 *str = gb_alloc_array(a, u8, len);
-
- isize i = 0;
- gb_memmove(str+i, path.basename.text, path.basename.len); i += path.basename.len;
-
- gb_memmove(str+i, "/", 1); i += 1;
-
- gb_memmove(str+i, path.name.text, path.name.len); i += path.name.len;
- if (path.ext.len > 0) {
- gb_memmove(str+i, ".", 1); i += 1;
- gb_memmove(str+i, path.ext.text, path.ext.len); i += path.ext.len;
- }
- str[i] = 0;
-
- String res = make_string(str, i);
- res = string_trim_whitespace(res);
- return res;
-}
-
-// NOTE(Jeroen): Naively turns a Path into a string, then normalizes it using `path_to_full_path`.
-gb_internal String path_to_full_path(gbAllocator a, Path path) {
- String temp = path_to_string(heap_allocator(), path);
- defer (gb_free(heap_allocator(), temp.text));
-
- return path_to_full_path(a, temp);
-}
-
-// NOTE(Jeroen): Takes a path like "odin" or "W:\Odin", turns it into a full path,
-// and then breaks it into its components to make a Path.
-gb_internal Path path_from_string(gbAllocator a, String const &path) {
- Path res = {};
-
- if (path.len == 0) return res;
-
- String fullpath = path_to_full_path(a, path);
- defer (gb_free(heap_allocator(), fullpath.text));
-
- res.basename = directory_from_path(fullpath);
- res.basename = copy_string(a, res.basename);
-
- if (path_is_directory(fullpath)) {
- // It's a directory. We don't need to tinker with the name and extension.
- // It could have a superfluous trailing `/`. Remove it if so.
- if (res.basename.len > 0 && res.basename.text[res.basename.len - 1] == '/') {
- res.basename.len--;
- }
- return res;
- }
-
- // Note(Dragos): Is the copy_string required if it's a substring?
- isize name_start = (res.basename.len > 0) ? res.basename.len + 1 : res.basename.len;
- res.name = substring(fullpath, name_start, fullpath.len);
- res.name = remove_extension_from_path(res.name);
- res.name = copy_string(a, res.name);
-
- res.ext = path_extension(fullpath, false); // false says not to include the dot.
- res.ext = copy_string(a, res.ext);
- return res;
-}
-
-// NOTE(Jeroen): Takes a path String and returns the last path element.
-gb_internal String last_path_element(String const &path) {
- isize count = 0;
- u8 * start = (u8 *)(&path.text[path.len - 1]);
- for (isize length = path.len; length > 0 && path.text[length - 1] != '/'; length--) {
- count++;
- start--;
- }
- if (count > 0) {
- start++; // Advance past the `/` and return the substring.
- String res = make_string(start, count);
- return res;
- }
- // Must be a root path like `/` or `C:/`, return empty String.
- return STR_LIT("");
-}
-
-gb_internal bool path_is_directory(Path path) {
- String path_string = path_to_full_path(heap_allocator(), path);
- defer (gb_free(heap_allocator(), path_string.text));
-
- return path_is_directory(path_string);
-}
-
-struct FileInfo {
- String name;
- String fullpath;
- i64 size;
- bool is_dir;
-};
-
-enum ReadDirectoryError {
- ReadDirectory_None,
-
- ReadDirectory_InvalidPath,
- ReadDirectory_NotExists,
- ReadDirectory_Permission,
- ReadDirectory_NotDir,
- ReadDirectory_Empty,
- ReadDirectory_Unknown,
-
- ReadDirectory_COUNT,
-};
-
-gb_internal i64 get_file_size(String path) {
- char *c_str = alloc_cstring(heap_allocator(), path);
- defer (gb_free(heap_allocator(), c_str));
-
- gbFile f = {};
- gbFileError err = gb_file_open(&f, c_str);
- defer (gb_file_close(&f));
- if (err != gbFileError_None) {
- return -1;
- }
- return gb_file_size(&f);
-}
-
-
-#if defined(GB_SYSTEM_WINDOWS)
-gb_internal ReadDirectoryError read_directory(String path, Array<FileInfo> *fi) {
- GB_ASSERT(fi != nullptr);
-
-
- while (path.len > 0) {
- Rune end = path[path.len-1];
- if (end == '/') {
- path.len -= 1;
- } else if (end == '\\') {
- path.len -= 1;
- } else {
- break;
- }
- }
-
- if (path.len == 0) {
- return ReadDirectory_InvalidPath;
- }
- {
- char *c_str = alloc_cstring(temporary_allocator(), path);
- gbFile f = {};
- gbFileError file_err = gb_file_open(&f, c_str);
- defer (gb_file_close(&f));
-
- switch (file_err) {
- case gbFileError_Invalid: return ReadDirectory_InvalidPath;
- case gbFileError_NotExists: return ReadDirectory_NotExists;
- // case gbFileError_Permission: return ReadDirectory_Permission;
- }
- }
-
- if (!path_is_directory(path)) {
- return ReadDirectory_NotDir;
- }
-
-
- gbAllocator a = heap_allocator();
- char *new_path = gb_alloc_array(a, char, path.len+3);
- defer (gb_free(a, new_path));
-
- gb_memmove(new_path, path.text, path.len);
- gb_memmove(new_path+path.len, "/*", 2);
- new_path[path.len+2] = 0;
-
- String np = make_string(cast(u8 *)new_path, path.len+2);
- String16 wstr = string_to_string16(a, np);
- defer (gb_free(a, wstr.text));
-
- WIN32_FIND_DATAW file_data = {};
- HANDLE find_file = FindFirstFileW(wstr.text, &file_data);
- if (find_file == INVALID_HANDLE_VALUE) {
- return ReadDirectory_Unknown;
- }
- defer (FindClose(find_file));
-
- array_init(fi, a, 0, 100);
-
- do {
- wchar_t *filename_w = file_data.cFileName;
- u64 size = cast(u64)file_data.nFileSizeLow;
- size |= (cast(u64)file_data.nFileSizeHigh) << 32;
- String name = string16_to_string(a, make_string16_c(filename_w));
- if (name == "." || name == "..") {
- gb_free(a, name.text);
- continue;
- }
-
- String filepath = {};
- filepath.len = path.len+1+name.len;
- filepath.text = gb_alloc_array(a, u8, filepath.len+1);
- defer (gb_free(a, filepath.text));
- gb_memmove(filepath.text, path.text, path.len);
- gb_memmove(filepath.text+path.len, "/", 1);
- gb_memmove(filepath.text+path.len+1, name.text, name.len);
-
- FileInfo info = {};
- info.name = name;
- info.fullpath = path_to_full_path(a, filepath);
- info.size = cast(i64)size;
- info.is_dir = (file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
- array_add(fi, info);
- } while (FindNextFileW(find_file, &file_data));
-
- if (fi->count == 0) {
- return ReadDirectory_Empty;
- }
-
- return ReadDirectory_None;
-}
-#elif defined(GB_SYSTEM_LINUX) || defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_FREEBSD) || defined(GB_SYSTEM_OPENBSD)
-
-#include <dirent.h>
-
-gb_internal ReadDirectoryError read_directory(String path, Array<FileInfo> *fi) {
- GB_ASSERT(fi != nullptr);
-
- gbAllocator a = heap_allocator();
-
- char *c_path = alloc_cstring(a, path);
- defer (gb_free(a, c_path));
-
- DIR *dir = opendir(c_path);
- if (!dir) {
- switch (errno) {
- case ENOENT:
- return ReadDirectory_NotExists;
- case EACCES:
- return ReadDirectory_Permission;
- case ENOTDIR:
- return ReadDirectory_NotDir;
- default:
- // ENOMEM: out of memory
- // EMFILE: per-process limit on open fds reached
- // ENFILE: system-wide limit on total open files reached
- return ReadDirectory_Unknown;
- }
- GB_PANIC("unreachable");
- }
-
- array_init(fi, a, 0, 100);
-
- for (;;) {
- struct dirent *entry = readdir(dir);
- if (entry == nullptr) {
- break;
- }
-
- String name = make_string_c(entry->d_name);
- if (name == "." || name == "..") {
- continue;
- }
-
- String filepath = {};
- filepath.len = path.len+1+name.len;
- filepath.text = gb_alloc_array(a, u8, filepath.len+1);
- defer (gb_free(a, filepath.text));
- gb_memmove(filepath.text, path.text, path.len);
- gb_memmove(filepath.text+path.len, "/", 1);
- gb_memmove(filepath.text+path.len+1, name.text, name.len);
- filepath.text[filepath.len] = 0;
-
-
- struct stat dir_stat = {};
-
- if (stat((char *)filepath.text, &dir_stat)) {
- continue;
- }
-
- if (S_ISDIR(dir_stat.st_mode)) {
- continue;
- }
-
- i64 size = dir_stat.st_size;
-
- FileInfo info = {};
- info.name = name;
- info.fullpath = path_to_full_path(a, filepath);
- info.size = size;
- array_add(fi, info);
- }
-
- if (fi->count == 0) {
- return ReadDirectory_Empty;
- }
-
- return ReadDirectory_None;
-}
-
-
-#else
-#error Implement read_directory
-#endif
-
-#if !defined(GB_SYSTEM_WINDOWS)
-gb_internal bool write_directory(String path) {
- char const *pathname = (char *) path.text;
-
- if (access(pathname, W_OK) < 0) {
- return false;
- }
-
- return true;
-}
-#else
-gb_internal bool write_directory(String path) {
- String16 wstr = string_to_string16(heap_allocator(), path);
- LPCWSTR wdirectory_name = wstr.text;
-
- HANDLE directory = CreateFileW(wdirectory_name,
- GENERIC_WRITE,
- 0,
- NULL,
- OPEN_EXISTING,
- FILE_FLAG_BACKUP_SEMANTICS,
- NULL);
-
- if (directory == INVALID_HANDLE_VALUE) {
- DWORD error_code = GetLastError();
- if (error_code == ERROR_ACCESS_DENIED) {
- return false;
- }
- }
-
- CloseHandle(directory);
- return true;
-}
-#endif
+/*
+ Path handling utilities.
+*/
+#if !defined(GB_SYSTEM_WINDOWS)
+#include <unistd.h>
+#endif
+
+gb_internal String remove_extension_from_path(String const &s) {
+ if (s.len != 0 && s.text[s.len-1] == '.') {
+ return s;
+ }
+ for (isize i = s.len-1; i >= 0; i--) {
+ if (s[i] == '.') {
+ return substring(s, 0, i);
+ }
+ }
+ return s;
+}
+
+gb_internal String remove_directory_from_path(String const &s) {
+ isize len = 0;
+ for (isize i = s.len-1; i >= 0; i--) {
+ if (s[i] == '/' ||
+ s[i] == '\\') {
+ break;
+ }
+ len += 1;
+ }
+ return substring(s, s.len-len, s.len);
+}
+
+
+// NOTE(Mark Naughton): getcwd as String
+#if !defined(GB_SYSTEM_WINDOWS)
+gb_internal String get_current_directory(void) {
+ char cwd[256];
+ getcwd(cwd, 256);
+
+ return make_string_c(cwd);
+}
+
+#else
+gb_internal String get_current_directory(void) {
+ gbAllocator a = heap_allocator();
+
+ wchar_t cwd[256];
+ GetCurrentDirectoryW(256, cwd);
+
+ String16 wstr = make_string16_c(cwd);
+
+ return string16_to_string(a, wstr);
+}
+#endif
+
+gb_internal bool path_is_directory(String path);
+
+gb_internal String directory_from_path(String const &s) {
+ if (path_is_directory(s)) {
+ return s;
+ }
+
+ isize i = s.len-1;
+ for (; i >= 0; i--) {
+ if (s[i] == '/' ||
+ s[i] == '\\') {
+ break;
+ }
+ }
+ if (i >= 0) {
+ return substring(s, 0, i);
+ }
+ return substring(s, 0, 0);
+}
+
+#if defined(GB_SYSTEM_WINDOWS)
+ gb_internal bool path_is_directory(String path) {
+ gbAllocator a = heap_allocator();
+ String16 wstr = string_to_string16(a, path);
+ defer (gb_free(a, wstr.text));
+
+ i32 attribs = GetFileAttributesW(wstr.text);
+ if (attribs < 0) return false;
+
+ return (attribs & FILE_ATTRIBUTE_DIRECTORY) != 0;
+ }
+
+#else
+ gb_internal bool path_is_directory(String path) {
+ gbAllocator a = heap_allocator();
+ char *copy = cast(char *)copy_string(a, path).text;
+ defer (gb_free(a, copy));
+
+ struct stat s;
+ if (stat(copy, &s) == 0) {
+ return (s.st_mode & S_IFDIR) != 0;
+ }
+ return false;
+ }
+#endif
+
+
+gb_internal String path_to_full_path(gbAllocator a, String path) {
+ gbAllocator ha = heap_allocator();
+ char *path_c = gb_alloc_str_len(ha, cast(char *)path.text, path.len);
+ defer (gb_free(ha, path_c));
+
+ char *fullpath = gb_path_get_full_name(a, path_c);
+ String res = string_trim_whitespace(make_string_c(fullpath));
+#if defined(GB_SYSTEM_WINDOWS)
+ for (isize i = 0; i < res.len; i++) {
+ if (res.text[i] == '\\') {
+ res.text[i] = '/';
+ }
+ }
+#endif
+ return copy_string(a, res);
+}
+
+struct Path {
+ String basename;
+ String name;
+ String ext;
+};
+
+// NOTE(Jeroen): Naively turns a Path into a string.
+gb_internal String path_to_string(gbAllocator a, Path path) {
+ if (path.basename.len + path.name.len + path.ext.len == 0) {
+ return make_string(nullptr, 0);
+ }
+
+ isize len = path.basename.len + 1 + path.name.len + 1;
+ if (path.ext.len > 0) {
+ len += path.ext.len + 1;
+ }
+
+ u8 *str = gb_alloc_array(a, u8, len);
+
+ isize i = 0;
+ gb_memmove(str+i, path.basename.text, path.basename.len); i += path.basename.len;
+
+ gb_memmove(str+i, "/", 1); i += 1;
+
+ gb_memmove(str+i, path.name.text, path.name.len); i += path.name.len;
+ if (path.ext.len > 0) {
+ gb_memmove(str+i, ".", 1); i += 1;
+ gb_memmove(str+i, path.ext.text, path.ext.len); i += path.ext.len;
+ }
+ str[i] = 0;
+
+ String res = make_string(str, i);
+ res = string_trim_whitespace(res);
+ return res;
+}
+
+// NOTE(Jeroen): Naively turns a Path into a string, then normalizes it using `path_to_full_path`.
+gb_internal String path_to_full_path(gbAllocator a, Path path) {
+ String temp = path_to_string(heap_allocator(), path);
+ defer (gb_free(heap_allocator(), temp.text));
+
+ return path_to_full_path(a, temp);
+}
+
+// NOTE(Jeroen): Takes a path like "odin" or "W:\Odin", turns it into a full path,
+// and then breaks it into its components to make a Path.
+gb_internal Path path_from_string(gbAllocator a, String const &path) {
+ Path res = {};
+
+ if (path.len == 0) return res;
+
+ String fullpath = path_to_full_path(a, path);
+ defer (gb_free(heap_allocator(), fullpath.text));
+
+ res.basename = directory_from_path(fullpath);
+ res.basename = copy_string(a, res.basename);
+
+ if (path_is_directory(fullpath)) {
+ // It's a directory. We don't need to tinker with the name and extension.
+ // It could have a superfluous trailing `/`. Remove it if so.
+ if (res.basename.len > 0 && res.basename.text[res.basename.len - 1] == '/') {
+ res.basename.len--;
+ }
+ return res;
+ }
+
+ // Note(Dragos): Is the copy_string required if it's a substring?
+ isize name_start = (res.basename.len > 0) ? res.basename.len + 1 : res.basename.len;
+ res.name = substring(fullpath, name_start, fullpath.len);
+ res.name = remove_extension_from_path(res.name);
+ res.name = copy_string(a, res.name);
+
+ res.ext = path_extension(fullpath, false); // false says not to include the dot.
+ res.ext = copy_string(a, res.ext);
+ return res;
+}
+
+// NOTE(Jeroen): Takes a path String and returns the last path element.
+gb_internal String last_path_element(String const &path) {
+ isize count = 0;
+ u8 * start = (u8 *)(&path.text[path.len - 1]);
+ for (isize length = path.len; length > 0 && path.text[length - 1] != '/'; length--) {
+ count++;
+ start--;
+ }
+ if (count > 0) {
+ start++; // Advance past the `/` and return the substring.
+ String res = make_string(start, count);
+ return res;
+ }
+ // Must be a root path like `/` or `C:/`, return empty String.
+ return STR_LIT("");
+}
+
+gb_internal bool path_is_directory(Path path) {
+ String path_string = path_to_full_path(heap_allocator(), path);
+ defer (gb_free(heap_allocator(), path_string.text));
+
+ return path_is_directory(path_string);
+}
+
+struct FileInfo {
+ String name;
+ String fullpath;
+ i64 size;
+ bool is_dir;
+};
+
+enum ReadDirectoryError {
+ ReadDirectory_None,
+
+ ReadDirectory_InvalidPath,
+ ReadDirectory_NotExists,
+ ReadDirectory_Permission,
+ ReadDirectory_NotDir,
+ ReadDirectory_Empty,
+ ReadDirectory_Unknown,
+
+ ReadDirectory_COUNT,
+};
+
+gb_internal i64 get_file_size(String path) {
+ char *c_str = alloc_cstring(heap_allocator(), path);
+ defer (gb_free(heap_allocator(), c_str));
+
+ gbFile f = {};
+ gbFileError err = gb_file_open(&f, c_str);
+ defer (gb_file_close(&f));
+ if (err != gbFileError_None) {
+ return -1;
+ }
+ return gb_file_size(&f);
+}
+
+
+#if defined(GB_SYSTEM_WINDOWS)
+gb_internal ReadDirectoryError read_directory(String path, Array<FileInfo> *fi) {
+ GB_ASSERT(fi != nullptr);
+
+
+ while (path.len > 0) {
+ Rune end = path[path.len-1];
+ if (end == '/') {
+ path.len -= 1;
+ } else if (end == '\\') {
+ path.len -= 1;
+ } else {
+ break;
+ }
+ }
+
+ if (path.len == 0) {
+ return ReadDirectory_InvalidPath;
+ }
+ {
+ char *c_str = alloc_cstring(temporary_allocator(), path);
+ gbFile f = {};
+ gbFileError file_err = gb_file_open(&f, c_str);
+ defer (gb_file_close(&f));
+
+ switch (file_err) {
+ case gbFileError_Invalid: return ReadDirectory_InvalidPath;
+ case gbFileError_NotExists: return ReadDirectory_NotExists;
+ // case gbFileError_Permission: return ReadDirectory_Permission;
+ }
+ }
+
+ if (!path_is_directory(path)) {
+ return ReadDirectory_NotDir;
+ }
+
+
+ gbAllocator a = heap_allocator();
+ char *new_path = gb_alloc_array(a, char, path.len+3);
+ defer (gb_free(a, new_path));
+
+ gb_memmove(new_path, path.text, path.len);
+ gb_memmove(new_path+path.len, "/*", 2);
+ new_path[path.len+2] = 0;
+
+ String np = make_string(cast(u8 *)new_path, path.len+2);
+ String16 wstr = string_to_string16(a, np);
+ defer (gb_free(a, wstr.text));
+
+ WIN32_FIND_DATAW file_data = {};
+ HANDLE find_file = FindFirstFileW(wstr.text, &file_data);
+ if (find_file == INVALID_HANDLE_VALUE) {
+ return ReadDirectory_Unknown;
+ }
+ defer (FindClose(find_file));
+
+ array_init(fi, a, 0, 100);
+
+ do {
+ wchar_t *filename_w = file_data.cFileName;
+ u64 size = cast(u64)file_data.nFileSizeLow;
+ size |= (cast(u64)file_data.nFileSizeHigh) << 32;
+ String name = string16_to_string(a, make_string16_c(filename_w));
+ if (name == "." || name == "..") {
+ gb_free(a, name.text);
+ continue;
+ }
+
+ String filepath = {};
+ filepath.len = path.len+1+name.len;
+ filepath.text = gb_alloc_array(a, u8, filepath.len+1);
+ defer (gb_free(a, filepath.text));
+ gb_memmove(filepath.text, path.text, path.len);
+ gb_memmove(filepath.text+path.len, "/", 1);
+ gb_memmove(filepath.text+path.len+1, name.text, name.len);
+
+ FileInfo info = {};
+ info.name = name;
+ info.fullpath = path_to_full_path(a, filepath);
+ info.size = cast(i64)size;
+ info.is_dir = (file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
+ array_add(fi, info);
+ } while (FindNextFileW(find_file, &file_data));
+
+ if (fi->count == 0) {
+ return ReadDirectory_Empty;
+ }
+
+ return ReadDirectory_None;
+}
+#elif defined(GB_SYSTEM_LINUX) || defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_FREEBSD) || defined(GB_SYSTEM_OPENBSD) || defined(GB_SYSTEM_HAIKU)
+
+#include <dirent.h>
+
+gb_internal ReadDirectoryError read_directory(String path, Array<FileInfo> *fi) {
+ GB_ASSERT(fi != nullptr);
+
+ gbAllocator a = heap_allocator();
+
+ char *c_path = alloc_cstring(a, path);
+ defer (gb_free(a, c_path));
+
+ DIR *dir = opendir(c_path);
+ if (!dir) {
+ switch (errno) {
+ case ENOENT:
+ return ReadDirectory_NotExists;
+ case EACCES:
+ return ReadDirectory_Permission;
+ case ENOTDIR:
+ return ReadDirectory_NotDir;
+ default:
+ // ENOMEM: out of memory
+ // EMFILE: per-process limit on open fds reached
+ // ENFILE: system-wide limit on total open files reached
+ return ReadDirectory_Unknown;
+ }
+ GB_PANIC("unreachable");
+ }
+
+ array_init(fi, a, 0, 100);
+
+ for (;;) {
+ struct dirent *entry = readdir(dir);
+ if (entry == nullptr) {
+ break;
+ }
+
+ String name = make_string_c(entry->d_name);
+ if (name == "." || name == "..") {
+ continue;
+ }
+
+ String filepath = {};
+ filepath.len = path.len+1+name.len;
+ filepath.text = gb_alloc_array(a, u8, filepath.len+1);
+ defer (gb_free(a, filepath.text));
+ gb_memmove(filepath.text, path.text, path.len);
+ gb_memmove(filepath.text+path.len, "/", 1);
+ gb_memmove(filepath.text+path.len+1, name.text, name.len);
+ filepath.text[filepath.len] = 0;
+
+
+ struct stat dir_stat = {};
+
+ if (stat((char *)filepath.text, &dir_stat)) {
+ continue;
+ }
+
+ if (S_ISDIR(dir_stat.st_mode)) {
+ continue;
+ }
+
+ i64 size = dir_stat.st_size;
+
+ FileInfo info = {};
+ info.name = name;
+ info.fullpath = path_to_full_path(a, filepath);
+ info.size = size;
+ array_add(fi, info);
+ }
+
+ if (fi->count == 0) {
+ return ReadDirectory_Empty;
+ }
+
+ return ReadDirectory_None;
+}
+
+
+#else
+#error Implement read_directory
+#endif
+
+#if !defined(GB_SYSTEM_WINDOWS)
+gb_internal bool write_directory(String path) {
+ char const *pathname = (char *) path.text;
+
+ if (access(pathname, W_OK) < 0) {
+ return false;
+ }
+
+ return true;
+}
+#else
+gb_internal bool write_directory(String path) {
+ String16 wstr = string_to_string16(heap_allocator(), path);
+ LPCWSTR wdirectory_name = wstr.text;
+
+ HANDLE directory = CreateFileW(wdirectory_name,
+ GENERIC_WRITE,
+ 0,
+ NULL,
+ OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS,
+ NULL);
+
+ if (directory == INVALID_HANDLE_VALUE) {
+ DWORD error_code = GetLastError();
+ if (error_code == ERROR_ACCESS_DENIED) {
+ return false;
+ }
+ }
+
+ CloseHandle(directory);
+ return true;
+}
+#endif
diff --git a/src/threading.cpp b/src/threading.cpp
index 725b58c89..a469435d2 100644
--- a/src/threading.cpp
+++ b/src/threading.cpp
@@ -492,6 +492,8 @@ gb_internal u32 thread_current_id(void) {
__asm__("mov %%fs:0x10,%0" : "=r"(thread_id));
#elif defined(GB_SYSTEM_LINUX)
thread_id = gettid();
+#elif defined(GB_SYSTEM_HAIKU)
+ thread_id = find_thread(NULL);
#else
#error Unsupported architecture for thread_current_id()
#endif
@@ -831,8 +833,178 @@ gb_internal void futex_wait(Futex *f, Footex val) {
WaitOnAddress(f, (void *)&val, sizeof(val), INFINITE);
} while (f->load() == val);
}
+
+#elif defined(GB_SYSTEM_HAIKU)
+
+// Futex implementation taken from https://tavianator.com/2023/futex.html
+
+#include <pthread.h>
+#include <atomic>
+
+struct _Spinlock {
+ std::atomic_flag state;
+
+ void init() {
+ state.clear();
+ }
+
+ void lock() {
+ while (state.test_and_set(std::memory_order_acquire)) {
+ #if defined(GB_CPU_X86)
+ _mm_pause();
+ #else
+ (void)0; // spin...
+ #endif
+ }
+ }
+
+ void unlock() {
+ state.clear(std::memory_order_release);
+ }
+};
+
+struct Futex_Waitq;
+
+struct Futex_Waiter {
+ _Spinlock lock;
+ pthread_t thread;
+ Futex *futex;
+ Futex_Waitq *waitq;
+ Futex_Waiter *prev, *next;
+};
+
+struct Futex_Waitq {
+ _Spinlock lock;
+ Futex_Waiter list;
+
+ void init() {
+ auto head = &list;
+ head->prev = head->next = head;
+ }
+};
+
+// FIXME: This approach may scale badly in the future,
+// possible solution - hash map (leads to deadlocks now).
+
+Futex_Waitq g_waitq = {
+ .lock = ATOMIC_FLAG_INIT,
+ .list = {
+ .prev = &g_waitq.list,
+ .next = &g_waitq.list,
+ },
+};
+
+Futex_Waitq *get_waitq(Futex *f) {
+ // Future hash map method...
+ return &g_waitq;
+}
+
+void futex_signal(Futex *f) {
+ auto waitq = get_waitq(f);
+
+ waitq->lock.lock();
+
+ auto head = &waitq->list;
+ for (auto waiter = head->next; waiter != head; waiter = waiter->next) {
+ if (waiter->futex != f) {
+ continue;
+ }
+ waitq->lock.unlock();
+ pthread_kill(waiter->thread, SIGCONT);
+ return;
+ }
+
+ waitq->lock.unlock();
+}
+
+void futex_broadcast(Futex *f) {
+ auto waitq = get_waitq(f);
+
+ waitq->lock.lock();
+
+ auto head = &waitq->list;
+ for (auto waiter = head->next; waiter != head; waiter = waiter->next) {
+ if (waiter->futex != f) {
+ continue;
+ }
+ if (waiter->next == head) {
+ waitq->lock.unlock();
+ pthread_kill(waiter->thread, SIGCONT);
+ return;
+ } else {
+ pthread_kill(waiter->thread, SIGCONT);
+ }
+ }
+
+ waitq->lock.unlock();
+}
+
+void futex_wait(Futex *f, Footex val) {
+ Futex_Waiter waiter;
+ waiter.thread = pthread_self();
+ waiter.futex = f;
+
+ auto waitq = get_waitq(f);
+ while (waitq->lock.state.test_and_set(std::memory_order_acquire)) {
+ if (f->load(std::memory_order_relaxed) != val) {
+ return;
+ }
+ #if defined(GB_CPU_X86)
+ _mm_pause();
+ #else
+ (void)0; // spin...
+ #endif
+ }
+
+ waiter.waitq = waitq;
+ waiter.lock.init();
+ waiter.lock.lock();
+
+ auto head = &waitq->list;
+ waiter.prev = head->prev;
+ waiter.next = head;
+ waiter.prev->next = &waiter;
+ waiter.next->prev = &waiter;
+
+ waiter.prev->next = &waiter;
+ waiter.next->prev = &waiter;
+
+ sigset_t old_mask, mask;
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGCONT);
+ pthread_sigmask(SIG_BLOCK, &mask, &old_mask);
+
+ if (f->load(std::memory_order_relaxed) == val) {
+ waiter.lock.unlock();
+ waitq->lock.unlock();
+
+ int sig;
+ sigwait(&mask, &sig);
+
+ waitq->lock.lock();
+ waiter.lock.lock();
+
+ while (waitq != waiter.waitq) {
+ auto req = waiter.waitq;
+ waiter.lock.unlock();
+ waitq->lock.unlock();
+ waitq = req;
+ waitq->lock.lock();
+ waiter.lock.lock();
+ }
+ }
+
+ waiter.prev->next = waiter.next;
+ waiter.next->prev = waiter.prev;
+
+ pthread_sigmask(SIG_SETMASK, &old_mask, NULL);
+
+ waiter.lock.unlock();
+ waitq->lock.unlock();
+}
+
#endif
#if defined(GB_SYSTEM_WINDOWS)
#pragma warning(pop)
-#endif \ No newline at end of file
+#endif
diff --git a/src/tilde.cpp b/src/tilde.cpp
index 06428f317..4fc7d1c9b 100644
--- a/src/tilde.cpp
+++ b/src/tilde.cpp
@@ -825,6 +825,7 @@ gb_internal bool cg_generate_code(Checker *c, LinkerData *linker_data) {
case TargetOs_essence:
case TargetOs_freebsd:
case TargetOs_openbsd:
+ case TargetOs_haiku:
debug_format = TB_DEBUGFMT_DWARF;
break;
}