diff options
| author | gingerBill <bill@gingerbill.org> | 2024-03-08 13:20:33 +0000 |
|---|---|---|
| committer | gingerBill <bill@gingerbill.org> | 2024-03-08 13:20:33 +0000 |
| commit | 032e193d0d9602081ab125bd56cd62c0be0de53b (patch) | |
| tree | 81d15ce3cbc7d9cefb9044404a7801b3c51b24a5 /core | |
| parent | 0f83ab466fc733094bfe46bb2915ad54b67957a6 (diff) | |
| parent | 53ce94503422b904ae52990c7d8bab292413b2bc (diff) | |
Merge branch 'master' of https://github.com/odin-lang/Odin
Diffstat (limited to 'core')
| -rw-r--r-- | core/c/libc/errno.odin | 18 | ||||
| -rw-r--r-- | core/c/libc/stdio.odin | 30 | ||||
| -rw-r--r-- | core/c/libc/time.odin | 2 | ||||
| -rw-r--r-- | core/c/libc/wctype.odin | 6 | ||||
| -rw-r--r-- | core/os/os_haiku.odin | 435 | ||||
| -rw-r--r-- | core/os/stat_unix.odin | 2 | ||||
| -rw-r--r-- | core/os/stream.odin | 6 | ||||
| -rw-r--r-- | core/sync/futex_haiku.odin | 167 | ||||
| -rw-r--r-- | core/sync/primitives_haiku.odin | 8 | ||||
| -rw-r--r-- | core/sys/haiku/errors.odin | 239 | ||||
| -rw-r--r-- | core/sys/haiku/find_directory.odin | 168 | ||||
| -rw-r--r-- | core/sys/haiku/os.odin | 502 | ||||
| -rw-r--r-- | core/sys/haiku/types.odin | 54 | ||||
| -rw-r--r-- | core/sys/unix/pthread_haiku.odin | 71 | ||||
| -rw-r--r-- | core/sys/unix/pthread_unix.odin | 24 | ||||
| -rw-r--r-- | core/sys/unix/time_unix.odin | 2 | ||||
| -rw-r--r-- | core/sys/windows/gdi32.odin | 45 | ||||
| -rw-r--r-- | core/sys/windows/kernel32.odin | 19 | ||||
| -rw-r--r-- | core/sys/windows/shell32.odin | 51 | ||||
| -rw-r--r-- | core/sys/windows/user32.odin | 80 | ||||
| -rw-r--r-- | core/sys/windows/winmm.odin | 206 | ||||
| -rw-r--r-- | core/thread/thread_unix.odin | 13 | ||||
| -rw-r--r-- | core/time/time_unix.odin | 2 |
23 files changed, 2127 insertions, 23 deletions
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, ¶ms) 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" |