diff options
| author | Colin Davidson <colrdavidson@gmail.com> | 2024-09-24 02:32:06 -0700 |
|---|---|---|
| committer | Colin Davidson <colrdavidson@gmail.com> | 2024-09-24 02:32:06 -0700 |
| commit | f3ab14b8ccb45d0fef8a96937635bdf0943ce7d6 (patch) | |
| tree | 1309d7c797117463996a84522ef3d1c9713a286c /core/sys | |
| parent | 99938c7d4fb26d43a07dd4b8f4f00ab87e67e73f (diff) | |
| parent | f7d74ff3a8596efef67d151ffb758ed085e94be0 (diff) | |
Merge branch 'master' into macharena
Diffstat (limited to 'core/sys')
99 files changed, 3795 insertions, 374 deletions
diff --git a/core/sys/darwin/CoreFoundation/CFString.odin b/core/sys/darwin/CoreFoundation/CFString.odin index 6ad3c5bfc..24485a494 100644 --- a/core/sys/darwin/CoreFoundation/CFString.odin +++ b/core/sys/darwin/CoreFoundation/CFString.odin @@ -1,7 +1,5 @@ package CoreFoundation -import "base:runtime" - foreign import CoreFoundation "system:CoreFoundation.framework" String :: distinct TypeRef // same as CFStringRef @@ -9,157 +7,157 @@ String :: distinct TypeRef // same as CFStringRef StringEncoding :: distinct u32 StringBuiltInEncodings :: enum StringEncoding { - MacRoman = 0, + MacRoman = 0, WindowsLatin1 = 0x0500, - ISOLatin1 = 0x0201, + ISOLatin1 = 0x0201, NextStepLatin = 0x0B01, - ASCII = 0x0600, - Unicode = 0x0100, - UTF8 = 0x08000100, + ASCII = 0x0600, + Unicode = 0x0100, + UTF8 = 0x08000100, NonLossyASCII = 0x0BFF, - UTF16 = 0x0100, + UTF16 = 0x0100, UTF16BE = 0x10000100, UTF16LE = 0x14000100, - UTF32 = 0x0c000100, - UTF32BE = 0x18000100, - UTF32LE = 0x1c000100, + UTF32 = 0x0c000100, + UTF32BE = 0x18000100, + UTF32LE = 0x1c000100, } StringEncodings :: enum Index { - MacJapanese = 1, - MacChineseTrad = 2, - MacKorean = 3, - MacArabic = 4, - MacHebrew = 5, - MacGreek = 6, - MacCyrillic = 7, - MacDevanagari = 9, - MacGurmukhi = 10, - MacGujarati = 11, - MacOriya = 12, - MacBengali = 13, - MacTamil = 14, - MacTelugu = 15, - MacKannada = 16, - MacMalayalam = 17, - MacSinhalese = 18, - MacBurmese = 19, - MacKhmer = 20, - MacThai = 21, - MacLaotian = 22, - MacGeorgian = 23, - MacArmenian = 24, - MacChineseSimp = 25, - MacTibetan = 26, - MacMongolian = 27, - MacEthiopic = 28, - MacCentralEurRoman = 29, - MacVietnamese = 30, - MacExtArabic = 31, - MacSymbol = 33, - MacDingbats = 34, - MacTurkish = 35, - MacCroatian = 36, - MacIcelandic = 37, - MacRomanian = 38, - MacCeltic = 39, - MacGaelic = 40, - MacFarsi = 0x8C, - MacUkrainian = 0x98, - MacInuit = 0xEC, - MacVT100 = 0xFC, - MacHFS = 0xFF, - ISOLatin2 = 0x0202, - ISOLatin3 = 0x0203, - ISOLatin4 = 0x0204, - ISOLatinCyrillic = 0x0205, - ISOLatinArabic = 0x0206, - ISOLatinGreek = 0x0207, - ISOLatinHebrew = 0x0208, - ISOLatin5 = 0x0209, - ISOLatin6 = 0x020A, - ISOLatinThai = 0x020B, - ISOLatin7 = 0x020D, - ISOLatin8 = 0x020E, - ISOLatin9 = 0x020F, - ISOLatin10 = 0x0210, - DOSLatinUS = 0x0400, - DOSGreek = 0x0405, - DOSBalticRim = 0x0406, - DOSLatin1 = 0x0410, - DOSGreek1 = 0x0411, - DOSLatin2 = 0x0412, - DOSCyrillic = 0x0413, - DOSTurkish = 0x0414, - DOSPortuguese = 0x0415, - DOSIcelandic = 0x0416, - DOSHebrew = 0x0417, - DOSCanadianFrench = 0x0418, - DOSArabic = 0x0419, - DOSNordic = 0x041A, - DOSRussian = 0x041B, - DOSGreek2 = 0x041C, - DOSThai = 0x041D, - DOSJapanese = 0x0420, - DOSChineseSimplif = 0x0421, - DOSKorean = 0x0422, - DOSChineseTrad = 0x0423, - WindowsLatin2 = 0x0501, - WindowsCyrillic = 0x0502, - WindowsGreek = 0x0503, - WindowsLatin5 = 0x0504, - WindowsHebrew = 0x0505, - WindowsArabic = 0x0506, - WindowsBalticRim = 0x0507, - WindowsVietnamese = 0x0508, - WindowsKoreanJohab = 0x0510, - ANSEL = 0x0601, - JIS_X0201_76 = 0x0620, - JIS_X0208_83 = 0x0621, - JIS_X0208_90 = 0x0622, - JIS_X0212_90 = 0x0623, - JIS_C6226_78 = 0x0624, - ShiftJIS_X0213 = 0x0628, - ShiftJIS_X0213_MenKuTen = 0x0629, - GB_2312_80 = 0x0630, - GBK_95 = 0x0631, - GB_18030_2000 = 0x0632, - KSC_5601_87 = 0x0640, - KSC_5601_92_Johab = 0x0641, - CNS_11643_92_P1 = 0x0651, - CNS_11643_92_P2 = 0x0652, - CNS_11643_92_P3 = 0x0653, - ISO_2022_JP = 0x0820, - ISO_2022_JP_2 = 0x0821, - ISO_2022_JP_1 = 0x0822, - ISO_2022_JP_3 = 0x0823, - ISO_2022_CN = 0x0830, - ISO_2022_CN_EXT = 0x0831, - ISO_2022_KR = 0x0840, - EUC_JP = 0x0920, - EUC_CN = 0x0930, - EUC_TW = 0x0931, - EUC_KR = 0x0940, - ShiftJIS = 0x0A01, - KOI8_R = 0x0A02, - Big5 = 0x0A03, - MacRomanLatin1 = 0x0A04, - HZ_GB_2312 = 0x0A05, - Big5_HKSCS_1999 = 0x0A06, - VISCII = 0x0A07, - KOI8_U = 0x0A08, - Big5_E = 0x0A09, - NextStepJapanese = 0x0B02, - EBCDIC_US = 0x0C01, - EBCDIC_CP037 = 0x0C02, - UTF7 = 0x04000100, - UTF7_IMAP = 0x0A10, - ShiftJIS_X0213_00 = 0x0628, // Deprecated. Use `ShiftJIS_X0213` instead. + MacJapanese = 1, + MacChineseTrad = 2, + MacKorean = 3, + MacArabic = 4, + MacHebrew = 5, + MacGreek = 6, + MacCyrillic = 7, + MacDevanagari = 9, + MacGurmukhi = 10, + MacGujarati = 11, + MacOriya = 12, + MacBengali = 13, + MacTamil = 14, + MacTelugu = 15, + MacKannada = 16, + MacMalayalam = 17, + MacSinhalese = 18, + MacBurmese = 19, + MacKhmer = 20, + MacThai = 21, + MacLaotian = 22, + MacGeorgian = 23, + MacArmenian = 24, + MacChineseSimp = 25, + MacTibetan = 26, + MacMongolian = 27, + MacEthiopic = 28, + MacCentralEurRoman = 29, + MacVietnamese = 30, + MacExtArabic = 31, + MacSymbol = 33, + MacDingbats = 34, + MacTurkish = 35, + MacCroatian = 36, + MacIcelandic = 37, + MacRomanian = 38, + MacCeltic = 39, + MacGaelic = 40, + MacFarsi = 0x8C, + MacUkrainian = 0x98, + MacInuit = 0xEC, + MacVT100 = 0xFC, + MacHFS = 0xFF, + ISOLatin2 = 0x0202, + ISOLatin3 = 0x0203, + ISOLatin4 = 0x0204, + ISOLatinCyrillic = 0x0205, + ISOLatinArabic = 0x0206, + ISOLatinGreek = 0x0207, + ISOLatinHebrew = 0x0208, + ISOLatin5 = 0x0209, + ISOLatin6 = 0x020A, + ISOLatinThai = 0x020B, + ISOLatin7 = 0x020D, + ISOLatin8 = 0x020E, + ISOLatin9 = 0x020F, + ISOLatin10 = 0x0210, + DOSLatinUS = 0x0400, + DOSGreek = 0x0405, + DOSBalticRim = 0x0406, + DOSLatin1 = 0x0410, + DOSGreek1 = 0x0411, + DOSLatin2 = 0x0412, + DOSCyrillic = 0x0413, + DOSTurkish = 0x0414, + DOSPortuguese = 0x0415, + DOSIcelandic = 0x0416, + DOSHebrew = 0x0417, + DOSCanadianFrench = 0x0418, + DOSArabic = 0x0419, + DOSNordic = 0x041A, + DOSRussian = 0x041B, + DOSGreek2 = 0x041C, + DOSThai = 0x041D, + DOSJapanese = 0x0420, + DOSChineseSimplif = 0x0421, + DOSKorean = 0x0422, + DOSChineseTrad = 0x0423, + WindowsLatin2 = 0x0501, + WindowsCyrillic = 0x0502, + WindowsGreek = 0x0503, + WindowsLatin5 = 0x0504, + WindowsHebrew = 0x0505, + WindowsArabic = 0x0506, + WindowsBalticRim = 0x0507, + WindowsVietnamese = 0x0508, + WindowsKoreanJohab = 0x0510, + ANSEL = 0x0601, + JIS_X0201_76 = 0x0620, + JIS_X0208_83 = 0x0621, + JIS_X0208_90 = 0x0622, + JIS_X0212_90 = 0x0623, + JIS_C6226_78 = 0x0624, + ShiftJIS_X0213 = 0x0628, + ShiftJIS_X0213_MenKuTen = 0x0629, + GB_2312_80 = 0x0630, + GBK_95 = 0x0631, + GB_18030_2000 = 0x0632, + KSC_5601_87 = 0x0640, + KSC_5601_92_Johab = 0x0641, + CNS_11643_92_P1 = 0x0651, + CNS_11643_92_P2 = 0x0652, + CNS_11643_92_P3 = 0x0653, + ISO_2022_JP = 0x0820, + ISO_2022_JP_2 = 0x0821, + ISO_2022_JP_1 = 0x0822, + ISO_2022_JP_3 = 0x0823, + ISO_2022_CN = 0x0830, + ISO_2022_CN_EXT = 0x0831, + ISO_2022_KR = 0x0840, + EUC_JP = 0x0920, + EUC_CN = 0x0930, + EUC_TW = 0x0931, + EUC_KR = 0x0940, + ShiftJIS = 0x0A01, + KOI8_R = 0x0A02, + Big5 = 0x0A03, + MacRomanLatin1 = 0x0A04, + HZ_GB_2312 = 0x0A05, + Big5_HKSCS_1999 = 0x0A06, + VISCII = 0x0A07, + KOI8_U = 0x0A08, + Big5_E = 0x0A09, + NextStepJapanese = 0x0B02, + EBCDIC_US = 0x0C01, + EBCDIC_CP037 = 0x0C02, + UTF7 = 0x04000100, + UTF7_IMAP = 0x0A10, + ShiftJIS_X0213_00 = 0x0628, // Deprecated. Use `ShiftJIS_X0213` instead. } -@(link_prefix = "CF", default_calling_convention = "c") +@(link_prefix="CF", default_calling_convention="c") foreign CoreFoundation { // Copies the character contents of a string to a local C string buffer after converting the characters to a given encoding. StringGetCString :: proc(theString: String, buffer: [^]byte, bufferSize: Index, encoding: StringEncoding) -> b8 --- @@ -181,23 +179,16 @@ foreign CoreFoundation { STR :: StringMakeConstantString -StringCopyToOdinString :: proc( - theString: String, - allocator := context.allocator, -) -> ( - str: string, - ok: bool, -) #optional_ok { +StringCopyToOdinString :: proc(theString: String, allocator := context.allocator) -> (str: string, ok: bool) #optional_ok { length := StringGetLength(theString) max := StringGetMaximumSizeForEncoding(length, StringEncoding(StringBuiltInEncodings.UTF8)) buf, err := make([]byte, max, allocator) - if err != nil { return } - - raw_str := runtime.Raw_String { - data = raw_data(buf), + if err != nil { + return } - StringGetBytes(theString, {0, length}, StringEncoding(StringBuiltInEncodings.UTF8), 0, false, raw_data(buf), max, (^Index)(&raw_str.len)) - return transmute(string)raw_str, true + n: Index + StringGetBytes(theString, {0, length}, StringEncoding(StringBuiltInEncodings.UTF8), 0, false, raw_data(buf), Index(len(buf)), &n) + return string(buf[:n]), true } diff --git a/core/sys/darwin/Foundation/NSApplication.odin b/core/sys/darwin/Foundation/NSApplication.odin index 482221cdf..7191f6d07 100644 --- a/core/sys/darwin/Foundation/NSApplication.odin +++ b/core/sys/darwin/Foundation/NSApplication.odin @@ -79,7 +79,10 @@ Application_setActivationPolicy :: proc "c" (self: ^Application, activationPolic return msgSend(BOOL, self, "setActivationPolicy:", activationPolicy) } -@(deprecated="Use NSApplication method activate instead.") +// NOTE: this is technically deprecated but still actively used (Sokol, glfw, SDL, etc.) +// and has no clear alternative although `activate` is what Apple tells you to use, +// that does not work the same way. +// @(deprecated="Use NSApplication method activate instead.") @(objc_type=Application, objc_name="activateIgnoringOtherApps") Application_activateIgnoringOtherApps :: proc "c" (self: ^Application, ignoreOtherApps: BOOL) { msgSend(nil, self, "activateIgnoringOtherApps:", ignoreOtherApps) diff --git a/core/sys/darwin/Foundation/NSEvent.odin b/core/sys/darwin/Foundation/NSEvent.odin index f20afd3ab..548c5c172 100644 --- a/core/sys/darwin/Foundation/NSEvent.odin +++ b/core/sys/darwin/Foundation/NSEvent.odin @@ -5,8 +5,8 @@ Event :: struct {using _: Object} -EventMask :: distinct bit_set[EventType; UInteger] -EventMaskAny :: ~EventMask{} +EventMask :: distinct bit_set[EventType; UInteger] +EventMaskAny :: transmute(EventMask)(max(UInteger)) when size_of(UInteger) == 4 { // We don't support a 32-bit darwin system but this is mostly to shut up the type checker for the time being diff --git a/core/sys/darwin/Foundation/NSString.odin b/core/sys/darwin/Foundation/NSString.odin index b4918b3fb..a10b33fc0 100644 --- a/core/sys/darwin/Foundation/NSString.odin +++ b/core/sys/darwin/Foundation/NSString.odin @@ -58,7 +58,10 @@ MakeConstantString :: proc "c" (#const c: cstring) -> ^String { @(link_prefix="NS", default_calling_convention="c") foreign Foundation { - StringFromClass :: proc(cls: Class) -> ^String --- + StringFromClass :: proc(cls: Class) -> ^String --- + ClassFromString :: proc(str: ^String) -> Class --- + StringFromSelector :: proc(selector: SEL) -> ^String --- + SelectorFromString :: proc(str: ^String) -> SEL --- } @(objc_type=String, objc_name="alloc", objc_is_class_method=true) diff --git a/core/sys/darwin/Foundation/objc.odin b/core/sys/darwin/Foundation/objc.odin index 51cfee444..82d6199ce 100644 --- a/core/sys/darwin/Foundation/objc.odin +++ b/core/sys/darwin/Foundation/objc.odin @@ -9,24 +9,85 @@ import "core:c" IMP :: proc "c" (object: id, sel: SEL, #c_vararg args: ..any) -> id +@(default_calling_convention="c") foreign Foundation { - objc_getMetaClass :: proc "c" (name: cstring) -> id --- - objc_lookUpClass :: proc "c" (name: cstring) -> Class --- - objc_allocateClassPair :: proc "c" (superclass : Class, name : cstring, extraBytes : c.size_t) -> Class --- - objc_registerClassPair :: proc "c" (cls : Class) --- - - sel_registerName :: proc "c" (name: cstring) -> SEL --- - - class_addMethod :: proc "c" (cls: Class, name: SEL, imp: IMP, types: cstring) -> BOOL --- - class_getInstanceMethod :: proc "c" (cls: Class, name: SEL) -> Method --- - class_createInstance :: proc "c" (cls: Class, extraBytes: c.size_t) -> id --- - - method_setImplementation :: proc "c" (method: Method, imp: IMP) --- - - object_getClass :: proc "c" (obj: id) -> Class --- - object_setClass :: proc "c" (obj: id, cls: Class) -> Class --- - object_getClassName :: proc "c" (obj: id) -> cstring --- - object_getIndexedIvars :: proc "c" (obj: id) -> rawptr --- + objc_getMetaClass :: proc(name: cstring) -> id --- + objc_lookUpClass :: proc(name: cstring) -> Class --- + objc_allocateClassPair :: proc(superclass: Class, name: cstring, extraBytes: c.size_t) -> Class --- + objc_registerClassPair :: proc(cls: Class) --- + objc_disposeClassPair :: proc(cls: Class) --- + objc_duplicateClass :: proc(original: Class, name: cstring, extraBytes: c.size_t) -> Class --- + objc_getProtocol :: proc(name: cstring) -> ^Protocol --- + objc_copyProtocolList :: proc(outCount: ^uint) -> [^]^Protocol --- + objc_constructInstance :: proc(cls: Class, bytes: rawptr) -> id --- + objc_destructInstance :: proc(obj: id) -> rawptr --- + objc_getClassList :: proc(buffer: [^]Class, bufferCount: int) -> int --- + objc_copyClassList :: proc(outCount: ^uint) -> [^]Class --- + objc_getRequiredClass :: proc(name: cstring) -> Class --- + objc_setAssociatedObject :: proc(object: id, key: rawptr, value: id, policy: objc_AssociationPolicy) --- + objc_getAssociatedObject :: proc(object: id, key: rawptr) -> id --- + objc_removeAssociatedObjects :: proc(object: id) --- + + sel_registerName :: proc(name: cstring) -> SEL --- + sel_getName :: proc(sel: SEL) -> cstring --- + sel_isEqual :: proc(lhs, rhs: SEL) -> BOOL --- + + class_addMethod :: proc(cls: Class, name: SEL, imp: IMP, types: cstring) -> BOOL --- + class_getInstanceMethod :: proc(cls: Class, name: SEL) -> Method --- + class_getClassMethod :: proc(cls: Class, name: SEL) -> Method --- + class_copyMethodList :: proc(cls: Class, outCount: ^uint) -> [^]Method --- + class_createInstance :: proc(cls: Class, extraBytes: c.size_t) -> id --- + class_replaceMethod :: proc(cls: Class, name: SEL, imp: IMP, types: cstring) -> IMP --- + class_getMethodImplementation :: proc(cls: Class, name: SEL) -> IMP --- + class_getSuperclass :: proc(cls: Class) -> Class --- + class_getName :: proc(cls: Class) -> cstring --- + class_isMetaClass :: proc(cls: Class) -> BOOL --- + class_addProtocol :: proc(cls: Class, protocol: ^Protocol) -> BOOL --- + class_getVersion :: proc(cls: Class) -> c.int --- + class_setVersion :: proc(cls: Class, version: c.int) --- + class_getProperty :: proc(cls: Class, name: cstring) -> objc_property_t --- + class_addProperty :: proc(cls: Class, name: cstring, attributes: [^]objc_property_attribute_t, attributeCount: uint) -> BOOL --- + class_replaceProperty :: proc(cls: Class, name: cstring, attributes: [^]objc_property_attribute_t, attributeCount: uint) --- + class_copyPropertyList :: proc(cls: Class, outCount: ^uint) -> [^]objc_property_t --- + class_conformsToProtocol :: proc(cls: Class, protocol: ^Protocol) -> BOOL --- + class_copyProtocolList :: proc(cls: Class, outCount: ^uint) -> [^]^Protocol --- + class_respondsToSelector :: proc(cls: Class, sel: SEL) -> BOOL --- + class_getClassVariable :: proc(cls: Class, name: cstring) -> Ivar --- + class_getInstanceVariable :: proc(cls: Class, name: cstring) -> Ivar --- + class_addIvar :: proc(cls: Class, name: cstring, size: c.size_t, alignment: u8, types: cstring) -> BOOL --- + class_copyIvarList :: proc(cls: Class, outCount: ^uint) -> [^]Ivar --- + class_getInstanceSize :: proc(cls: Class) -> c.size_t --- + + property_getName :: proc(property: objc_property_t) -> cstring --- + property_getAttributes :: proc(property: objc_property_t) -> cstring --- + property_copyAttributeList :: proc(property: objc_property_t, outCount: ^uint) -> [^]objc_property_attribute_t --- + property_copyAttributeValue :: proc(property: objc_property_t, attributeName: cstring) -> cstring --- + + protocol_conformsToProtocol :: proc(proto: ^Protocol, other: ^Protocol) -> BOOL --- + protocol_isEqual :: proc(proto: ^Protocol, other: ^Protocol) -> BOOL --- + protocol_getName :: proc(proto: ^Protocol) -> cstring --- + + method_getImplementation :: proc(m: Method) -> IMP --- + method_setImplementation :: proc(m: Method, imp: IMP) --- + method_copyArgumentType :: proc(m: Method, index: uint) -> cstring --- + method_getReturnType :: proc(m: Method, dst: cstring, dst_len: c.size_t) --- + method_getNumberOfArguments :: proc(m: Method) -> uint --- + method_getArgumentType :: proc(m: Method, index: uint, dst: cstring, dst_len: c.size_t) --- + + object_getClass :: proc(obj: id) -> Class --- + object_setClass :: proc(obj: id, cls: Class) -> Class --- + object_copy :: proc(obj: id, size: c.size_t) -> id --- + object_dispose :: proc(obj: id) -> id --- + object_getClassName :: proc(obj: id) -> cstring --- + object_getIndexedIvars :: proc(obj: id) -> rawptr --- + object_getInstanceVariable :: proc(obj: id, name: cstring, outValue: rawptr) -> Ivar --- + object_setInstanceVariable :: proc(obj: id, name: cstring, value: rawptr) -> Ivar --- + object_getIvar :: proc(obj: id, ivar: Ivar) -> id --- + object_setIvar :: proc(obj: id, ivar: Ivar, value: id) --- + + ivar_getName :: proc(v: Ivar) -> cstring --- + ivar_getTypeEncoding :: proc(v: Ivar) -> cstring --- + ivar_getOffset :: proc(v: Ivar) -> c.ptrdiff_t --- } @@ -47,7 +108,17 @@ objc_method :: struct { } objc_method_list :: struct {} +objc_property :: struct{} +objc_property_t :: ^objc_property + +objc_property_attribute_t :: struct { + name: cstring, + value: cstring, +} + objc_ivar :: struct {} +Ivar :: ^objc_ivar + objc_ivar_list :: struct {} objc_cache :: struct { @@ -85,3 +156,11 @@ objc_class_internals :: struct { protocols: rawptr, } + +objc_AssociationPolicy :: enum c.uintptr_t { + Assign = 0, + Retain_Nonatomic = 1, + Copy_Nonatomic = 3, + Retain = 01401, + Copy = 01403, +} diff --git a/core/sys/darwin/darwin.odin b/core/sys/darwin/darwin.odin index ddd25a76c..d109f5544 100644 --- a/core/sys/darwin/darwin.odin +++ b/core/sys/darwin/darwin.odin @@ -1,4 +1,4 @@ -//+build darwin +#+build darwin package darwin import "core:c" diff --git a/core/sys/darwin/sync.odin b/core/sys/darwin/sync.odin index 121d3edef..58fc7c9e4 100644 --- a/core/sys/darwin/sync.odin +++ b/core/sys/darwin/sync.odin @@ -5,6 +5,7 @@ foreign import system "system:System.framework" // #define OS_WAIT_ON_ADDR_AVAILABILITY \ // __API_AVAILABLE(macos(14.4), ios(17.4), tvos(17.4), watchos(10.4)) when ODIN_OS == .Darwin { + when ODIN_PLATFORM_SUBTARGET == .iOS && ODIN_MINIMUM_OS_VERSION >= 17_04_00 { WAIT_ON_ADDRESS_AVAILABLE :: true } else when ODIN_MINIMUM_OS_VERSION >= 14_04_00 { @@ -12,8 +13,18 @@ when ODIN_OS == .Darwin { } else { WAIT_ON_ADDRESS_AVAILABLE :: false } + + when ODIN_PLATFORM_SUBTARGET == .iOS && ODIN_MINIMUM_OS_VERSION >= 14_00_00 { + ULOCK_WAIT_2_AVAILABLE :: true + } else when ODIN_MINIMUM_OS_VERSION >= 11_00_00 { + ULOCK_WAIT_2_AVAILABLE :: true + } else { + ULOCK_WAIT_2_AVAILABLE :: false + } + } else { WAIT_ON_ADDRESS_AVAILABLE :: false + ULOCK_WAIT_2_AVAILABLE :: false } os_sync_wait_on_address_flag :: enum u32 { diff --git a/core/sys/darwin/xnu_system_call_helpers.odin b/core/sys/darwin/xnu_system_call_helpers.odin index a641a4be9..ae8373f99 100644 --- a/core/sys/darwin/xnu_system_call_helpers.odin +++ b/core/sys/darwin/xnu_system_call_helpers.odin @@ -15,9 +15,9 @@ sys_write_string :: proc (fd: c.int, message: string) -> bool { Offset_From :: enum c.int { SEEK_SET = 0, // the offset is set to offset bytes. SEEK_CUR = 1, // the offset is set to its current location plus offset bytes. - SEEK_END = 2, // the offset is set to the size of the file plus offset bytes. - SEEK_HOLE = 3, // the offset is set to the start of the next hole greater than or equal to the supplied offset. - SEEK_DATA = 4, // the offset is set to the start of the next non-hole file region greater than or equal to the supplied offset. + SEEK_END = 2, // the offset is set to the size of the file plus offset bytes. + SEEK_HOLE = 3, // the offset is set to the start of the next hole greater than or equal to the supplied offset. + SEEK_DATA = 4, // the offset is set to the start of the next non-hole file region greater than or equal to the supplied offset. } Open_Flags_Enum :: enum u8 { diff --git a/core/sys/darwin/xnu_system_call_wrappers.odin b/core/sys/darwin/xnu_system_call_wrappers.odin index 30faf86be..1188091a9 100644 --- a/core/sys/darwin/xnu_system_call_wrappers.odin +++ b/core/sys/darwin/xnu_system_call_wrappers.odin @@ -192,43 +192,43 @@ _STRUCT_TIMEVAL :: struct { /* pwd.h */ _Password_Entry :: struct { - pw_name: cstring, /* username */ - pw_passwd: cstring, /* user password */ - pw_uid: i32, /* user ID */ - pw_gid: i32, /* group ID */ + pw_name: cstring, /* username */ + pw_passwd: cstring, /* user password */ + pw_uid: i32, /* user ID */ + pw_gid: i32, /* group ID */ pw_change: u64, /* password change time */ pw_class: cstring, /* user access class */ - pw_gecos: cstring, /* full user name */ - pw_dir: cstring, /* home directory */ - pw_shell: cstring, /* shell program */ + pw_gecos: cstring, /* full user name */ + pw_dir: cstring, /* home directory */ + pw_shell: cstring, /* shell program */ pw_expire: u64, /* account expiration */ pw_fields: i32, /* filled fields */ } /* processinfo.h */ _Proc_Bsdinfo :: struct { - pbi_flags: u32, /* if is 64bit; emulated etc */ - pbi_status: u32, - pbi_xstatus: u32, - pbi_pid: u32, - pbi_ppid: u32, - pbi_uid: u32, - pbi_gid: u32, - pbi_ruid: u32, - pbi_rgid: u32, - pbi_svuid: u32, - pbi_svgid: u32, - res: u32, - pbi_comm: [DARWIN_MAXCOMLEN]u8, - pbi_name: [2 * DARWIN_MAXCOMLEN]u8, /* empty if no name is registered */ - pbi_nfiles: u32, - pbi_pgid: u32, - pbi_pjobc: u32, - e_tdev: u32, /* controlling tty dev */ - e_tpgid: u32, /* tty process group id */ - pbi_nice: i32, - pbi_start_tvsec: u64, - pbi_start_tvusec: u64, + pbi_flags: u32, /* if is 64bit; emulated etc */ + pbi_status: u32, + pbi_xstatus: u32, + pbi_pid: u32, + pbi_ppid: u32, + pbi_uid: u32, + pbi_gid: u32, + pbi_ruid: u32, + pbi_rgid: u32, + pbi_svuid: u32, + pbi_svgid: u32, + res: u32, + pbi_comm: [DARWIN_MAXCOMLEN]u8, + pbi_name: [2 * DARWIN_MAXCOMLEN]u8, /* empty if no name is registered */ + pbi_nfiles: u32, + pbi_pgid: u32, + pbi_pjobc: u32, + e_tdev: u32, /* controlling tty dev */ + e_tpgid: u32, /* tty process group id */ + pbi_nice: i32, + pbi_start_tvsec: u64, + pbi_start_tvusec: u64, } /*--==========================================================================--*/ diff --git a/core/sys/freebsd/syscalls.odin b/core/sys/freebsd/syscalls.odin index 4a79bd56c..83b51138a 100644 --- a/core/sys/freebsd/syscalls.odin +++ b/core/sys/freebsd/syscalls.odin @@ -14,12 +14,16 @@ import "core:c" // FreeBSD 15 syscall numbers // See: https://alfonsosiciliano.gitlab.io/posts/2023-08-28-freebsd-15-system-calls.html +SYS_read : uintptr : 3 +SYS_write : uintptr : 4 SYS_open : uintptr : 5 SYS_close : uintptr : 6 SYS_getpid : uintptr : 20 SYS_recvfrom : uintptr : 29 SYS_accept : uintptr : 30 +SYS_getsockname: uintptr : 32 SYS_fcntl : uintptr : 92 +SYS_fsync : uintptr : 95 SYS_socket : uintptr : 97 SYS_connect : uintptr : 98 SYS_bind : uintptr : 104 @@ -29,12 +33,46 @@ SYS_shutdown : uintptr : 134 SYS_setsockopt : uintptr : 105 SYS_sysctl : uintptr : 202 SYS__umtx_op : uintptr : 454 +SYS_pread : uintptr : 475 +SYS_pwrite : uintptr : 476 SYS_accept4 : uintptr : 541 // // Odin syscall wrappers // +// Read input. +// +// The read() function appeared in Version 1 AT&T UNIX. +read :: proc "contextless" (fd: Fd, buf: []u8) -> (int, Errno) { + result, ok := intrinsics.syscall_bsd(SYS_read, + cast(uintptr)fd, + cast(uintptr)raw_data(buf), + cast(uintptr)len(buf)) + + if !ok { + return 0, cast(Errno)result + } + + return cast(int)result, nil +} + +// Write output. +// +// The write() function appeared in Version 1 AT&T UNIX. +write :: proc "contextless" (fd: Fd, buf: []u8) -> (int, Errno) { + result, ok := intrinsics.syscall_bsd(SYS_pwrite, + cast(uintptr)fd, + cast(uintptr)raw_data(buf), + cast(uintptr)len(buf)) + + if !ok { + return 0, cast(Errno)result + } + + return cast(int)result, nil +} + // Open or create a file for reading, writing or executing. // // The open() function appeared in Version 1 AT&T UNIX. @@ -164,6 +202,36 @@ accept_nil :: proc "contextless" (s: Fd) -> (Fd, Errno) { accept :: proc { accept_T, accept_nil } +// Get socket name. +// +// The getsockname() system call appeared in 4.2BSD. +getsockname :: proc "contextless" (s: Fd, sockaddr: ^$T) -> Errno { + // sockaddr must contain a valid pointer, or this will segfault because + // we're telling the syscall that there's memory available to write to. + addrlen: socklen_t = size_of(T) + + result, ok := intrinsics.syscall_bsd(SYS_getsockname, + cast(uintptr)s, + cast(uintptr)sockaddr, + cast(uintptr)&addrlen) + + if !ok { + return cast(Errno)result + } + + return nil +} + +// Synchronize changes to a file. +// +// The fsync() system call appeared in 4.2BSD. +fsync :: proc "contextless" (fd: Fd) -> Errno { + result, _ := intrinsics.syscall_bsd(SYS_fsync, + cast(uintptr)fd) + + return cast(Errno)result +} + // File control. // // The fcntl() system call appeared in 4.2BSD. @@ -469,6 +537,46 @@ _umtx_op :: proc "contextless" (obj: rawptr, op: Userland_Mutex_Operation, val: return cast(Errno)result } +// Read input without modifying the file pointer. +// +// The pread() function appeared in AT&T System V Release 4 UNIX. +pread :: proc "contextless" (fd: Fd, buf: []u8, offset: off_t) -> (int, Errno) { + result, ok := intrinsics.syscall_bsd(SYS_pread, + cast(uintptr)fd, + cast(uintptr)raw_data(buf), + cast(uintptr)len(buf), + cast(uintptr)offset) + + if !ok { + return 0, cast(Errno)result + } + + return cast(int)result, nil +} + +// Write output without modifying the file pointer. +// +// The pwrite() function appeared in AT&T System V Release 4 UNIX. +// +// BUGS +// +// The pwrite() system call appends the file without changing the file +// offset if O_APPEND is set, contrary to IEEE Std 1003.1-2008 (“POSIX.1”) +// where pwrite() writes into offset regardless of whether O_APPEND is set. +pwrite :: proc "contextless" (fd: Fd, buf: []u8, offset: off_t) -> (int, Errno) { + result, ok := intrinsics.syscall_bsd(SYS_pwrite, + cast(uintptr)fd, + cast(uintptr)raw_data(buf), + cast(uintptr)len(buf), + cast(uintptr)offset) + + if !ok { + return 0, cast(Errno)result + } + + return cast(int)result, nil +} + // Accept a connection on a socket. // // The accept4() system call appeared in FreeBSD 10.0. diff --git a/core/sys/freebsd/types.odin b/core/sys/freebsd/types.odin index a13961a47..37e8abf68 100644 --- a/core/sys/freebsd/types.odin +++ b/core/sys/freebsd/types.odin @@ -695,12 +695,12 @@ Record_Lock_Flag :: enum c.int { // struct flock File_Lock :: struct { - start: off_t, /* starting offset */ - len: off_t, /* len = 0 means until end of file */ - pid: pid_t, /* lock owner */ - type: Record_Lock_Flag, /* lock type: read/write, etc. */ - whence: c.short, /* type of l_start */ - sysid: c.int, /* remote system id or zero for local */ + start: off_t, /* starting offset */ + len: off_t, /* len = 0 means until end of file */ + pid: pid_t, /* lock owner */ + type: Record_Lock_Flag, /* lock type: read/write, etc. */ + whence: c.short, /* type of l_start */ + sysid: c.int, /* remote system id or zero for local */ } /* diff --git a/core/sys/haiku/errors.odin b/core/sys/haiku/errors.odin index 023045001..febe647ea 100644 --- a/core/sys/haiku/errors.odin +++ b/core/sys/haiku/errors.odin @@ -1,4 +1,4 @@ -//+build haiku +#+build haiku package sys_haiku import "core:c" diff --git a/core/sys/haiku/find_directory.odin b/core/sys/haiku/find_directory.odin index 103e677d7..758c4dff4 100644 --- a/core/sys/haiku/find_directory.odin +++ b/core/sys/haiku/find_directory.odin @@ -1,4 +1,4 @@ -//+build haiku +#+build haiku package sys_haiku import "core:c" diff --git a/core/sys/haiku/os.odin b/core/sys/haiku/os.odin index 883072c2d..6ab3ef573 100644 --- a/core/sys/haiku/os.odin +++ b/core/sys/haiku/os.odin @@ -1,4 +1,4 @@ -//+build haiku +#+build haiku package sys_haiku import "core:c" diff --git a/core/sys/haiku/types.odin b/core/sys/haiku/types.odin index 0440d5a98..47755b0b7 100644 --- a/core/sys/haiku/types.odin +++ b/core/sys/haiku/types.odin @@ -1,4 +1,4 @@ -//+build haiku +#+build haiku package sys_haiku import "core:c" diff --git a/core/sys/info/cpu_arm.odin b/core/sys/info/cpu_arm.odin index aa4bb368a..960e55a56 100644 --- a/core/sys/info/cpu_arm.odin +++ b/core/sys/info/cpu_arm.odin @@ -1,4 +1,4 @@ -//+build arm32, arm64 +#+build arm32, arm64 package sysinfo import "core:sys/unix" diff --git a/core/sys/info/cpu_intel.odin b/core/sys/info/cpu_intel.odin index 73d4c15e7..d6fa98507 100644 --- a/core/sys/info/cpu_intel.odin +++ b/core/sys/info/cpu_intel.odin @@ -1,4 +1,4 @@ -//+build i386, amd64 +#+build i386, amd64 package sysinfo import "base:intrinsics" diff --git a/core/sys/info/cpu_linux_arm.odin b/core/sys/info/cpu_linux_arm.odin index dcc252971..6408decb7 100644 --- a/core/sys/info/cpu_linux_arm.odin +++ b/core/sys/info/cpu_linux_arm.odin @@ -1,5 +1,5 @@ -//+build arm32, arm64 -//+build linux +#+build arm32, arm64 +#+build linux package sysinfo import "core:sys/linux" diff --git a/core/sys/info/cpu_linux_riscv64.odin b/core/sys/info/cpu_linux_riscv64.odin index 0f109e7ba..84f6134d4 100644 --- a/core/sys/info/cpu_linux_riscv64.odin +++ b/core/sys/info/cpu_linux_riscv64.odin @@ -1,5 +1,5 @@ -//+build riscv64 -//+build linux +#+build riscv64 +#+build linux package sysinfo import "base:intrinsics" @@ -8,35 +8,102 @@ import "core:sys/linux" @(init, private) init_cpu_features :: proc() { - fd, err := linux.open("/proc/self/auxv", {}) - if err != .NONE { return } - defer linux.close(fd) - - // This is probably enough right? - buf: [4096]byte - n, rerr := linux.read(fd, buf[:]) - if rerr != .NONE || n == 0 { return } - - ulong :: u64 - AT_HWCAP :: 16 - - // TODO: using these we could get more information than just the basics. - // AT_HWCAP2 :: 26 - // AT_HWCAP3 :: 29 - // AT_HWCAP4 :: 30 - - auxv := buf[:n] - for len(auxv) >= size_of(ulong)*2 { - key := intrinsics.unaligned_load((^ulong)(&auxv[0])) - val := intrinsics.unaligned_load((^ulong)(&auxv[size_of(ulong)])) - auxv = auxv[2*size_of(ulong):] - - if key != AT_HWCAP { - continue + _features: CPU_Features + defer cpu_features = _features + + HWCAP_Bits :: enum u64 { + I = 'I' - 'A', + M = 'M' - 'A', + A = 'A' - 'A', + F = 'F' - 'A', + D = 'D' - 'A', + C = 'C' - 'A', + V = 'V' - 'A', + } + HWCAP :: bit_set[HWCAP_Bits; u64] + + // Read HWCAP for base extensions, we can get this info through hwprobe too but that is Linux 6.4+ only. + { + fd, err := linux.open("/proc/self/auxv", {}) + if err != .NONE { return } + defer linux.close(fd) + + // This is probably enough right? + buf: [4096]byte + n, rerr := linux.read(fd, buf[:]) + if rerr != .NONE || n == 0 { return } + + ulong :: u64 + AT_HWCAP :: 16 + + auxv := buf[:n] + for len(auxv) >= size_of(ulong)*2 { + key := intrinsics.unaligned_load((^ulong)(&auxv[0])) + val := intrinsics.unaligned_load((^ulong)(&auxv[size_of(ulong)])) + auxv = auxv[2*size_of(ulong):] + + if key != AT_HWCAP { + continue + } + + cap := transmute(HWCAP)(val) + if .I in cap { + _features += { .I } + } + if .M in cap { + _features += { .M } + } + if .A in cap { + _features += { .A } + } + if .F in cap { + _features += { .F } + } + if .D in cap { + _features += { .D } + } + if .C in cap { + _features += { .C } + } + if .V in cap { + _features += { .V } + } + break } + } - cpu_features = transmute(CPU_Features)(val) - break + // hwprobe for other features. + { + pairs := []linux.RISCV_HWProbe{ + { key = .IMA_EXT_0 }, + { key = .CPUPERF_0 }, + { key = .MISALIGNED_SCALAR_PERF }, + } + err := linux.riscv_hwprobe(raw_data(pairs), len(pairs), 0, nil, {}) + if err != nil { + assert(err == .ENOSYS, "unexpected error from riscv_hwprobe()") + return + } + + assert(pairs[0].key == .IMA_EXT_0) + exts := pairs[0].value.ima_ext_0 + exts -= { .FD, .C, .V } + _features += transmute(CPU_Features)exts + + if pairs[2].key == .MISALIGNED_SCALAR_PERF { + if pairs[2].value.misaligned_scalar_perf == .FAST { + _features += { .Misaligned_Supported, .Misaligned_Fast } + } else if pairs[2].value.misaligned_scalar_perf != .UNSUPPORTED { + _features += { .Misaligned_Supported } + } + } else { + assert(pairs[1].key == .CPUPERF_0) + if .FAST in pairs[1].value.cpu_perf_0 { + _features += { .Misaligned_Supported, .Misaligned_Fast } + } else if .UNSUPPORTED not_in pairs[1].value.cpu_perf_0 { + _features += { .Misaligned_Supported } + } + } } } diff --git a/core/sys/info/cpu_riscv64.odin b/core/sys/info/cpu_riscv64.odin index 754110911..c3319c48c 100644 --- a/core/sys/info/cpu_riscv64.odin +++ b/core/sys/info/cpu_riscv64.odin @@ -1,13 +1,97 @@ package sysinfo CPU_Feature :: enum u64 { - I = 'I' - 'A', // Base features, don't think this is ever not here. - M = 'M' - 'A', // Integer multiplication and division, currently required by Odin. - A = 'A' - 'A', // Atomics. - F = 'F' - 'A', // Single precision floating point, currently required by Odin. - D = 'D' - 'A', // Double precision floating point, currently required by Odin. - C = 'C' - 'A', // Compressed instructions. - V = 'V' - 'A', // Vector operations. + // Bit-Manipulation ISA Extensions v1. + Zba = 3, + Zbb, + Zbs, + + // CMOs (ratified). + Zicboz, + + // Bit-Manipulation ISA Extensions v1. + Zbc, + + // Scalar Crypto ISA extensions v1. + Zbkb, + Zbkc, + Zbkx, + Zknd, + Zkne, + Zknh, + Zksed, + Zksh, + Zkt, + + // Cryptography Extensions Volume II v1. + Zvbb, + Zvbc, + Zvkb, + Zvkg, + Zvkned, + Zvknha, + Zvknhb, + Zvksed, + Zvksh, + Zvkt, + + // ISA Manual v1. + Zfh, + Zfhmin, + Zihintntl, + + // ISA manual (ratified). + Zvfh, + Zvfhmin, + Zfa, + Ztso, + + // Atomic Compare-and-Swap Instructions Manual (ratified). + Zacas, + Zicond, + + // ISA manual (ratified). + Zihintpause, + + // Vector Extensions Manual v1. + Zve32x, + Zve32f, + Zve64x, + Zve64f, + Zve64d, + + // ISA manual (ratified). + Zimop, + + // Code Size Reduction (ratified). + Zca, + Zcb, + Zcd, + Zcf, + + // ISA manual (ratified). + Zcmop, + Zawrs, + + // Base features, don't think this is ever not here. + I, + // Integer multiplication and division, currently required by Odin. + M, + // Atomics. + A, + // Single precision floating point, currently required by Odin. + F, + // Double precision floating point, currently required by Odin. + D, + // Compressed instructions. + C, + // Vector operations. + V, + + // Indicates Misaligned Scalar Loads will not trap the program. + Misaligned_Supported, + // Indicates Hardware Support for Misaligned Scalar Loads. + Misaligned_Fast, } CPU_Features :: distinct bit_set[CPU_Feature; u64] diff --git a/core/sys/info/doc.odin b/core/sys/info/doc.odin index 802cd9c60..b5cd62d81 100644 --- a/core/sys/info/doc.odin +++ b/core/sys/info/doc.odin @@ -2,6 +2,12 @@ Copyright 2022 Jeroen van Rijn <nom@duclavier.com>. Made available under Odin's BSD-3 license. +List of contributors: + Jeroen van Rijn: Initial implementation. + Laytan: ARM and RISC-V CPU feature detection. +*/ + +/* Package `core:sys/info` gathers system information on: Windows, Linux, macOS, FreeBSD & OpenBSD. @@ -11,9 +17,10 @@ and CPU information. On Windows, GPUs will also be enumerated using the registry. CPU feature flags can be tested against `cpu_features`, where applicable, e.g. -`if .aes in si.aes { ... }` +`if .aes in info.cpu_features.? { ... }` Example: + package main import "core:fmt" import si "core:sys/info" diff --git a/core/sys/info/platform_bsd.odin b/core/sys/info/platform_bsd.odin index e2273d253..6bb32cd3d 100644 --- a/core/sys/info/platform_bsd.odin +++ b/core/sys/info/platform_bsd.odin @@ -1,4 +1,4 @@ -//+build openbsd, netbsd +#+build openbsd, netbsd package sysinfo import sys "core:sys/unix" diff --git a/core/sys/info/platform_darwin.odin b/core/sys/info/platform_darwin.odin index 3fd857bfe..493f038f0 100644 --- a/core/sys/info/platform_darwin.odin +++ b/core/sys/info/platform_darwin.odin @@ -530,6 +530,10 @@ macos_release_map: map[string]Darwin_To_Release = { "23F79" = {{23, 5, 0}, "macOS", {"Sonoma", {14, 5, 0}}}, "23G80" = {{23, 6, 0}, "macOS", {"Sonoma", {14, 6, 0}}}, "23G93" = {{23, 6, 0}, "macOS", {"Sonoma", {14, 6, 1}}}, + "23H124" = {{23, 6, 0}, "macOS", {"Sonoma", {14, 7, 0}}}, + + // MacOS Sequoia + "24A335" = {{24, 0, 0}, "macOS", {"Sequoia", {15, 0, 0}}}, } @(private) diff --git a/core/sys/kqueue/kqueue.odin b/core/sys/kqueue/kqueue.odin index 27d1ecaae..56be1cf7a 100644 --- a/core/sys/kqueue/kqueue.odin +++ b/core/sys/kqueue/kqueue.odin @@ -1,4 +1,4 @@ -//+build darwin, netbsd, openbsd, freebsd +#+build darwin, netbsd, openbsd, freebsd package kqueue when ODIN_OS == .Darwin { diff --git a/core/sys/linux/bits.odin b/core/sys/linux/bits.odin index f78891bc8..8a4a6dd7a 100644 --- a/core/sys/linux/bits.odin +++ b/core/sys/linux/bits.odin @@ -1839,3 +1839,87 @@ Execveat_Flags_Bits :: enum { AT_SYMLINK_NOFOLLOW = 8, AT_EMPTY_PATH = 12, } + +RISCV_HWProbe_Key :: enum i64 { + UNSUPPORTED = -1, + MVENDORID = 0, + MARCHID = 1, + MIMPID = 2, + BASE_BEHAVIOR = 3, + IMA_EXT_0 = 4, + // Deprecated, try `.MISALIGNED_SCALAR_PERF` first, if that is `.UNSUPPORTED`, use this. + CPUPERF_0 = 5, + ZICBOZ_BLOCK_SIZE = 6, + HIGHEST_VIRT_ADDRESS = 7, + TIME_CSR_FREQ = 8, + MISALIGNED_SCALAR_PERF = 9, +} + +RISCV_HWProbe_Flags_Bits :: enum { + WHICH_CPUS, +} + +RISCV_HWProbe_Base_Behavior_Bits :: enum { + IMA, +} + +RISCV_HWProbe_IMA_Ext_0_Bits :: enum { + FD, + C, + V, + EXT_ZBA, + EXT_ZBB, + EXT_ZBS, + EXT_ZICBOZ, + EXT_ZBC, + EXT_ZBKB, + EXT_ZBKC, + EXT_ZBKX, + EXT_ZKND, + EXT_ZKNE, + EXT_ZKNH, + EXT_ZKSED, + EXT_ZKSH, + EXT_ZKT, + EXT_ZVBB, + EXT_ZVBC, + EXT_ZVKB, + EXT_ZVKG, + EXT_ZVKNED, + EXT_ZVKNHA, + EXT_ZVKNHB, + EXT_ZVKSED, + EXT_ZVKSH, + EXT_ZVKT, + EXT_ZFH, + EXT_ZFHMIN, + EXT_ZIHINTNTL, + EXT_ZVFH, + EXT_ZVFHMIN, + EXT_ZFA, + EXT_ZTSO, + EXT_ZACAS, + EXT_ZICOND, + EXT_ZIHINTPAUSE, + EXT_ZVE32X, + EXT_ZVE32F, + EXT_ZVE64X, + EXT_ZVE64F, + EXT_ZVE64D, + EXT_ZIMOP, + EXT_ZCA, + EXT_ZCB, + EXT_ZCD, + EXT_ZCF, + EXT_ZCMOP, + EXT_ZAWRS, +} + +RISCV_HWProbe_Misaligned_Scalar_Perf :: enum { + UNKNOWN, + EMULATED, + SLOW, + FAST, + UNSUPPORTED, +} + diff --git a/core/sys/linux/helpers.odin b/core/sys/linux/helpers.odin index f1abbbf61..aefc1179e 100644 --- a/core/sys/linux/helpers.odin +++ b/core/sys/linux/helpers.odin @@ -1,5 +1,5 @@ -//+build linux -//+no-instrumentation +#+build linux +#+no-instrumentation package linux import "base:intrinsics" diff --git a/core/sys/linux/sys.odin b/core/sys/linux/sys.odin index 6e4194be7..c5894d78b 100644 --- a/core/sys/linux/sys.odin +++ b/core/sys/linux/sys.odin @@ -1,4 +1,4 @@ -//+no-instrumentation +#+no-instrumentation package linux import "base:intrinsics" @@ -1443,8 +1443,9 @@ ptrace_traceme :: proc "contextless" (rq: PTrace_Traceme_Type) -> (Errno) { } ptrace_peek :: proc "contextless" (rq: PTrace_Peek_Type, pid: Pid, addr: uintptr) -> (uint, Errno) { - ret := syscall(SYS_ptrace, rq, pid, addr) - return errno_unwrap(ret, uint) + res: uint = --- + ret := syscall(SYS_ptrace, rq, pid, addr, &res) + return res, Errno(-ret) } ptrace_poke :: proc "contextless" (rq: PTrace_Poke_Type, pid: Pid, addr: uintptr, data: uint) -> (Errno) { @@ -1493,12 +1494,12 @@ ptrace_setregset :: proc "contextless" (rq: PTrace_Setregset_Type, pid: Pid, not } ptrace_getsiginfo :: proc "contextless" (rq: PTrace_Getsiginfo_Type, pid: Pid, si: ^Sig_Info) -> (Errno) { - ret := syscall(SYS_ptrace, rq, pid, si) + ret := syscall(SYS_ptrace, rq, pid, si, rawptr(nil)) return Errno(-ret) } ptrace_peeksiginfo :: proc "contextless" (rq: PTrace_Peeksiginfo_Type, pid: Pid, si: ^Sig_Info) -> (Errno) { - ret := syscall(SYS_ptrace, rq, pid, si) + ret := syscall(SYS_ptrace, rq, pid, si, rawptr(nil)) return Errno(-ret) } @@ -1518,52 +1519,52 @@ ptrace_setoptions :: proc "contextless" (rq: PTrace_Setoptions_Type, pid: Pid, o } ptrace_geteventmsg :: proc "contextless" (rq: PTrace_Geteventmsg_Type, pid: Pid, msg: ^uint) -> (Errno) { - ret := syscall(SYS_ptrace, rq, pid, msg) + ret := syscall(SYS_ptrace, rq, pid, msg, rawptr(nil)) return Errno(-ret) } ptrace_cont :: proc "contextless" (rq: PTrace_Cont_Type, pid: Pid, sig: Signal) -> (Errno) { - ret := syscall(SYS_ptrace, rq, pid, sig) + ret := syscall(SYS_ptrace, rq, pid, rawptr(nil), sig) return Errno(-ret) } ptrace_singlestep :: proc "contextless" (rq: PTrace_Singlestep_Type, pid: Pid, sig: Signal) -> (Errno) { - ret := syscall(SYS_ptrace, rq, pid, sig) + ret := syscall(SYS_ptrace, rq, pid, rawptr(nil), sig) return Errno(-ret) } ptrace_syscall :: proc "contextless" (rq: PTrace_Syscall_Type, pid: Pid, sig: Signal) -> (Errno) { - ret := syscall(SYS_ptrace, rq, pid, sig) + ret := syscall(SYS_ptrace, rq, pid, rawptr(nil), sig) return Errno(-ret) } ptrace_sysemu :: proc "contextless" (rq: PTrace_Sysemu_Type, pid: Pid, sig: Signal) -> (Errno) { - ret := syscall(SYS_ptrace, rq, pid, sig) + ret := syscall(SYS_ptrace, rq, pid, rawptr(nil), sig) return Errno(-ret) } ptrace_sysemu_singlestep :: proc "contextless" (rq: PTrace_Sysemu_Singlestep_Type, pid: Pid, sig: Signal) -> (Errno) { - ret := syscall(SYS_ptrace, rq, pid, sig) + ret := syscall(SYS_ptrace, rq, pid, rawptr(nil), sig) return Errno(-ret) } ptrace_listen :: proc "contextless" (rq: PTrace_Listen_Type, pid: Pid) -> (Errno) { - ret := syscall(SYS_ptrace, rq, pid) + ret := syscall(SYS_ptrace, rq, pid, 0, rawptr(nil)) return Errno(-ret) } ptrace_interrupt :: proc "contextless" (rq: PTrace_Interrupt_Type, pid: Pid) -> (Errno) { - ret := syscall(SYS_ptrace, rq, pid) + ret := syscall(SYS_ptrace, rq, pid, 0, rawptr(nil)) return Errno(-ret) } ptrace_attach :: proc "contextless" (rq: PTrace_Attach_Type, pid: Pid) -> (Errno) { - ret := syscall(SYS_ptrace, rq, pid) + ret := syscall(SYS_ptrace, rq, pid, 0, rawptr(nil)) return Errno(-ret) } ptrace_seize :: proc "contextless" (rq: PTrace_Seize_Type, pid: Pid, opt: PTrace_Options) -> (Errno) { - ret := syscall(SYS_ptrace, rq, pid, 0, transmute(u32) opt) + ret := syscall(SYS_ptrace, rq, pid, 0, transmute(u32) opt, rawptr(nil)) return Errno(-ret) } @@ -2993,3 +2994,18 @@ epoll_pwait2 :: proc(epfd: Fd, events: [^]EPoll_Event, count: i32, timeout: ^Tim // TODO(flysand): fchmodat2 // TODO(flysand): map_shadow_stack + +when ODIN_ARCH == .riscv64 { + /* + Probe for RISC-V Hardware Support. + Available since Linux 6.4. + + TODO: cpu_set_t + + See: https://docs.kernel.org/arch/riscv/hwprobe.html + */ + riscv_hwprobe :: proc "contextless" (pairs: [^]RISCV_HWProbe, pair_count: uint, cpu_count: uint, cpus: rawptr /* cpu_set_t */, flags: RISCV_HWProbe_Flags) -> Errno { + ret := syscall(SYS_riscv_hwprobe, pairs, pair_count, cpu_count, cpus, transmute(u32)flags) + return Errno(-ret) + } +} diff --git a/core/sys/linux/syscall_amd64.odin b/core/sys/linux/syscall_amd64.odin index ee4e16280..31c8ed61c 100644 --- a/core/sys/linux/syscall_amd64.odin +++ b/core/sys/linux/syscall_amd64.odin @@ -1,4 +1,4 @@ -//+build amd64 +#+build amd64 package linux // AMD64 uses the new way to define syscalls, i.e. one that diff --git a/core/sys/linux/syscall_arm32.odin b/core/sys/linux/syscall_arm32.odin index 74640a1a3..731ce36a5 100644 --- a/core/sys/linux/syscall_arm32.odin +++ b/core/sys/linux/syscall_arm32.odin @@ -1,4 +1,4 @@ -//+build arm32 +#+build arm32 package linux // This file was taken and transformed from diff --git a/core/sys/linux/syscall_arm64.odin b/core/sys/linux/syscall_arm64.odin index 61b5a31b7..da8eb45da 100644 --- a/core/sys/linux/syscall_arm64.odin +++ b/core/sys/linux/syscall_arm64.odin @@ -1,4 +1,4 @@ -//+build arm64 +#+build arm64 package linux // Syscalls for arm64 are defined using the new way, i.e. differently from diff --git a/core/sys/linux/syscall_i386.odin b/core/sys/linux/syscall_i386.odin index 4609fc99c..affdff02c 100644 --- a/core/sys/linux/syscall_i386.odin +++ b/core/sys/linux/syscall_i386.odin @@ -1,4 +1,4 @@ -//+build i386 +#+build i386 package linux // The numbers are taken from diff --git a/core/sys/linux/syscall_riscv64.odin b/core/sys/linux/syscall_riscv64.odin index ce374312e..17845c5ed 100644 --- a/core/sys/linux/syscall_riscv64.odin +++ b/core/sys/linux/syscall_riscv64.odin @@ -1,4 +1,4 @@ -//+build riscv64 +#+build riscv64 package linux // https://github.com/riscv-collab/riscv-gnu-toolchain/blob/master/linux-headers/include/asm-generic/unistd.h @@ -248,6 +248,7 @@ SYS_rt_tgsigqueueinfo :: uintptr(240) SYS_perf_event_open :: uintptr(241) SYS_accept4 :: uintptr(242) SYS_recvmmsg :: uintptr(243) +SYS_riscv_hwprobe :: uintptr(258) SYS_wait4 :: uintptr(260) SYS_prlimit64 :: uintptr(261) SYS_fanotify_init :: uintptr(262) diff --git a/core/sys/linux/types.odin b/core/sys/linux/types.odin index 3f873f96c..0e5b8218b 100644 --- a/core/sys/linux/types.odin +++ b/core/sys/linux/types.odin @@ -1335,3 +1335,20 @@ EPoll_Event :: struct #packed { Flags for execveat(2) syscall. */ Execveat_Flags :: bit_set[Execveat_Flags_Bits; i32] + +RISCV_HWProbe_Flags :: bit_set[RISCV_HWProbe_Flags_Bits; u32] +RISCV_HWProbe_CPU_Perf_0 :: bit_set[RISCV_HWProbe_Misaligned_Scalar_Perf; u64] +RISCV_HWProbe_Base_Behavior :: bit_set[RISCV_HWProbe_Base_Behavior_Bits; u64] +RISCV_HWProbe_IMA_Ext_0 :: bit_set[RISCV_HWProbe_IMA_Ext_0_Bits; u64] + +RISCV_HWProbe :: struct { + // set to `.UNSUPPORTED` by the kernel if that is the case. + key: RISCV_HWProbe_Key, + value: struct #raw_union { + base_behavior: RISCV_HWProbe_Base_Behavior, + ima_ext_0: RISCV_HWProbe_IMA_Ext_0, + cpu_perf_0: RISCV_HWProbe_CPU_Perf_0, + misaligned_scalar_perf: RISCV_HWProbe_Misaligned_Scalar_Perf, + raw: u64, + }, +} diff --git a/core/sys/linux/wrappers.odin b/core/sys/linux/wrappers.odin index 7a30c3bde..4f6118c80 100644 --- a/core/sys/linux/wrappers.odin +++ b/core/sys/linux/wrappers.odin @@ -1,4 +1,4 @@ -//+build linux +#+build linux package linux /// Low 8 bits of the exit code diff --git a/core/sys/llvm/bit_manipulation.odin b/core/sys/llvm/bit_manipulation.odin index 39464773d..2e237dd32 100644 --- a/core/sys/llvm/bit_manipulation.odin +++ b/core/sys/llvm/bit_manipulation.odin @@ -1,4 +1,5 @@ // Bit Manipulation Intrinsics + package sys_llvm /* diff --git a/core/sys/llvm/code_generator.odin b/core/sys/llvm/code_generator.odin index 7d41ed67b..6422976c5 100644 --- a/core/sys/llvm/code_generator.odin +++ b/core/sys/llvm/code_generator.odin @@ -1,4 +1,5 @@ // Code Generator Intrinsics + package sys_llvm @(default_calling_convention="none") diff --git a/core/sys/llvm/standard_c_library.odin b/core/sys/llvm/standard_c_library.odin index 108d1268e..1818e8462 100644 --- a/core/sys/llvm/standard_c_library.odin +++ b/core/sys/llvm/standard_c_library.odin @@ -1,4 +1,5 @@ // Standard C Library Intrinsics + package sys_llvm @(default_calling_convention="none") diff --git a/core/sys/posix/time.odin b/core/sys/posix/time.odin index 9b91c9558..5c6ebcf2f 100644 --- a/core/sys/posix/time.odin +++ b/core/sys/posix/time.odin @@ -13,7 +13,7 @@ when ODIN_OS == .Darwin { foreign lib { /* - Convert the broken down time in the structure to a string form: Sun Sep 16 01:03:52 1973\n\0 + Convert the broken down time in the structure to a string form: Sun Sep 16 01:03:52 1973. [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/asctime_r.html ]] */ diff --git a/core/sys/posix/unistd.odin b/core/sys/posix/unistd.odin index 6ed9e5d11..15dbb576f 100644 --- a/core/sys/posix/unistd.odin +++ b/core/sys/posix/unistd.odin @@ -112,7 +112,7 @@ foreign lib { Return configuration-defined string values. Its use and purpose are similar to sysconf(), but it is used where string values rather than numeric values are returned. - Returns: 0 (setting errno) if `name` is invalid, need `buf` `len` if buf is `nil`, amount of bytes added to buf otherwise + Returns: 0 (setting errno) if `name` is invalid, need `buf` of `len` bytes if `buf` is `nil`, amount of bytes added to buf otherwise [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/confstr.html ]] */ diff --git a/core/sys/unix/pthread_darwin.odin b/core/sys/unix/pthread_darwin.odin index 378fa9309..eb2cc4c9f 100644 --- a/core/sys/unix/pthread_darwin.odin +++ b/core/sys/unix/pthread_darwin.odin @@ -1,4 +1,4 @@ -//+build darwin +#+build darwin package unix import "core:c" diff --git a/core/sys/unix/pthread_freebsd.odin b/core/sys/unix/pthread_freebsd.odin index 5f4dac289..38fe7db55 100644 --- a/core/sys/unix/pthread_freebsd.odin +++ b/core/sys/unix/pthread_freebsd.odin @@ -1,4 +1,4 @@ -//+build freebsd +#+build freebsd package unix import "core:c" diff --git a/core/sys/unix/pthread_linux.odin b/core/sys/unix/pthread_linux.odin index f4ded7464..d67add24b 100644 --- a/core/sys/unix/pthread_linux.odin +++ b/core/sys/unix/pthread_linux.odin @@ -1,4 +1,4 @@ -//+build linux +#+build linux package unix import "core:c" diff --git a/core/sys/unix/pthread_openbsd.odin b/core/sys/unix/pthread_openbsd.odin index 855e7d99c..2c6d9e598 100644 --- a/core/sys/unix/pthread_openbsd.odin +++ b/core/sys/unix/pthread_openbsd.odin @@ -1,4 +1,4 @@ -//+build openbsd +#+build openbsd package unix import "core:c" diff --git a/core/sys/unix/pthread_unix.odin b/core/sys/unix/pthread_unix.odin index 68a0859b4..43c4866ed 100644 --- a/core/sys/unix/pthread_unix.odin +++ b/core/sys/unix/pthread_unix.odin @@ -1,4 +1,4 @@ -//+build linux, darwin, freebsd, openbsd, netbsd, haiku +#+build linux, darwin, freebsd, openbsd, netbsd, haiku package unix foreign import "system:pthread" @@ -27,6 +27,8 @@ foreign pthread { pthread_equal :: proc(a, b: pthread_t) -> b32 --- + pthread_detach :: proc(t: pthread_t) -> c.int --- + sched_get_priority_min :: proc(policy: c.int) -> c.int --- sched_get_priority_max :: proc(policy: c.int) -> c.int --- diff --git a/core/sys/unix/sysctl_darwin.odin b/core/sys/unix/sysctl_darwin.odin index 92222bdfe..32dd720b0 100644 --- a/core/sys/unix/sysctl_darwin.odin +++ b/core/sys/unix/sysctl_darwin.odin @@ -1,4 +1,4 @@ -//+build darwin +#+build darwin package unix import "base:intrinsics" diff --git a/core/sys/unix/sysctl_freebsd.odin b/core/sys/unix/sysctl_freebsd.odin index 8ca40ef1b..f5fee6c6c 100644 --- a/core/sys/unix/sysctl_freebsd.odin +++ b/core/sys/unix/sysctl_freebsd.odin @@ -1,4 +1,4 @@ -//+build freebsd +#+build freebsd package unix import "base:intrinsics" diff --git a/core/sys/unix/sysctl_openbsd.odin b/core/sys/unix/sysctl_openbsd.odin index b93e8f9bd..49c9b6336 100644 --- a/core/sys/unix/sysctl_openbsd.odin +++ b/core/sys/unix/sysctl_openbsd.odin @@ -1,4 +1,4 @@ -//+build openbsd +#+build openbsd package unix import "core:c" diff --git a/core/sys/valgrind/callgrind.odin b/core/sys/valgrind/callgrind.odin index b1ba8c6e9..5cd58753a 100644 --- a/core/sys/valgrind/callgrind.odin +++ b/core/sys/valgrind/callgrind.odin @@ -1,4 +1,4 @@ -//+build amd64 +#+build amd64 package sys_valgrind import "base:intrinsics" diff --git a/core/sys/valgrind/helgrind.odin b/core/sys/valgrind/helgrind.odin index 2f0114522..3f5e7a531 100644 --- a/core/sys/valgrind/helgrind.odin +++ b/core/sys/valgrind/helgrind.odin @@ -1,4 +1,4 @@ -//+build amd64 +#+build amd64 package sys_valgrind import "base:intrinsics" diff --git a/core/sys/valgrind/memcheck.odin b/core/sys/valgrind/memcheck.odin index dfbe4c3be..bc77444be 100644 --- a/core/sys/valgrind/memcheck.odin +++ b/core/sys/valgrind/memcheck.odin @@ -1,4 +1,4 @@ -//+build amd64 +#+build amd64 package sys_valgrind import "base:intrinsics" diff --git a/core/sys/valgrind/valgrind.odin b/core/sys/valgrind/valgrind.odin index d0c46af53..b5c71664f 100644 --- a/core/sys/valgrind/valgrind.odin +++ b/core/sys/valgrind/valgrind.odin @@ -1,4 +1,4 @@ -//+build amd64 +#+build amd64 package sys_valgrind import "base:intrinsics" diff --git a/core/sys/wasm/README.md b/core/sys/wasm/README.md new file mode 100644 index 000000000..1aaeaa429 --- /dev/null +++ b/core/sys/wasm/README.md @@ -0,0 +1,15 @@ +# WASM on the Web + +This directory is for use when targeting the `js_wasm32` target and the packages that rely on it. + +The `js_wasm32` target assumes that the WASM output will be ran within a web browser rather than a standalone VM. In the VM cases, either `wasi_wasm32` or `freestanding_wasm32` should be used accordingly. + +## Example for `js_wasm32` + +```html +<!-- Copy `core:sys/wasm/js/odin.js` into your web server --> +<script type="text/javascript" src="odin.js"></script> +<script type="text/javascript"> + odin.runWasm(pathToWasm, consolePreElement); +</script> +``` diff --git a/core/sys/wasm/js/dom.odin b/core/sys/wasm/js/dom.odin new file mode 100644 index 000000000..ffc58a9a3 --- /dev/null +++ b/core/sys/wasm/js/dom.odin @@ -0,0 +1,93 @@ +#+build js wasm32, js wasm64p32 +package wasm_js_interface + +foreign import dom_lib "odin_dom" + +@(default_calling_convention="contextless") +foreign dom_lib { + get_element_value_f64 :: proc(id: string) -> f64 --- + set_element_value_f64 :: proc(id: string, value: f64) --- + + get_element_key_f64 :: proc(id: string, key: string) -> f64 --- + set_element_key_f64 :: proc(id: string, key: string, value: f64) --- + + set_element_value_string :: proc(id: string, value: string) --- + get_element_value_string_length :: proc(id: string) -> int --- + + set_element_key_string :: proc(id: string, key: string, value: string) --- + get_element_key_string_length :: proc(id: string, key: string, ) -> int --- + + device_pixel_ratio :: proc() -> f64 --- + + window_set_scroll :: proc(x, y: f64) --- +} + +get_element_value_string :: proc "contextless" (id: string, buf: []byte) -> string { + @(default_calling_convention="contextless") + foreign dom_lib { + @(link_name="get_element_value_string") + _get_element_value_string :: proc(id: string, buf: []byte) -> int --- + } + n := _get_element_value_string(id, buf) + return string(buf[:n]) +} + +get_element_key_string :: proc "contextless" (id: string, key: string, buf: []byte) -> string { + @(default_calling_convention="contextless") + foreign dom_lib { + @(link_name="get_element_key_string") + _get_element_key_string :: proc(id: string, key: string, buf: []byte) -> int --- + } + n := _get_element_key_string(id, key, buf) + return string(buf[:n]) + +} + + + +get_element_min_max :: proc "contextless" (id: string) -> (min, max: f64) { + @(default_calling_convention="contextless") + foreign dom_lib { + @(link_name="get_element_min_max") + _get_element_min_max :: proc(min_max: ^[2]f64, id: string) --- + } + min_max: [2]f64 + _get_element_min_max(&min_max, id) + return min_max[0], min_max[1] +} + + +Rect :: struct { + x, y, width, height: f64, +} + +get_bounding_client_rect :: proc "contextless" (id: string) -> (rect: Rect) { + @(default_calling_convention="contextless") + foreign dom_lib { + @(link_name="get_bounding_client_rect") + _get_bounding_client_rect :: proc(rect: ^Rect, id: string) --- + } + _get_bounding_client_rect(&rect, id) + return +} + +window_get_rect :: proc "contextless" () -> (rect: Rect) { + @(default_calling_convention="contextless") + foreign dom_lib { + @(link_name="window_get_rect") + _window_get_rect :: proc(rect: ^Rect) --- + } + _window_get_rect(&rect) + return +} + +window_get_scroll :: proc "contextless" () -> (x, y: f64) { + @(default_calling_convention="contextless") + foreign dom_lib { + @(link_name="window_get_scroll") + _window_get_scroll :: proc(scroll: ^[2]f64) --- + } + scroll: [2]f64 + _window_get_scroll(&scroll) + return scroll.x, scroll.y +}
\ No newline at end of file diff --git a/core/sys/wasm/js/dom_all_targets.odin b/core/sys/wasm/js/dom_all_targets.odin new file mode 100644 index 000000000..171deed2f --- /dev/null +++ b/core/sys/wasm/js/dom_all_targets.odin @@ -0,0 +1,36 @@ +#+build !js +package wasm_js_interface + +import "base:runtime" + + +get_element_value_string :: proc "contextless" (id: string, buf: []byte) -> string { + context = runtime.default_context() + panic("vendor:wasm/js not supported on non JS targets") +} + + +get_element_min_max :: proc "contextless" (id: string) -> (min, max: f64) { + context = runtime.default_context() + panic("vendor:wasm/js not supported on non JS targets") +} + + +Rect :: struct { + x, y, width, height: f64, +} + +get_bounding_client_rect :: proc "contextless" (id: string) -> (rect: Rect) { + context = runtime.default_context() + panic("vendor:wasm/js not supported on non JS targets") +} + +window_get_rect :: proc "contextless" () -> (rect: Rect) { + context = runtime.default_context() + panic("vendor:wasm/js not supported on non JS targets") +} + +window_get_scroll :: proc "contextless" () -> (x, y: f64) { + context = runtime.default_context() + panic("vendor:wasm/js not supported on non JS targets") +} diff --git a/core/sys/wasm/js/events.odin b/core/sys/wasm/js/events.odin new file mode 100644 index 000000000..905b3eba9 --- /dev/null +++ b/core/sys/wasm/js/events.odin @@ -0,0 +1,418 @@ +#+build js wasm32, js wasm64p32 +package wasm_js_interface + +foreign import dom_lib "odin_dom" + +Event_Kind :: enum u32 { + Invalid, + + Load, + Unload, + Error, + Resize, + Visibility_Change, + Fullscreen_Change, + Fullscreen_Error, + + Click, + Double_Click, + Mouse_Move, + Mouse_Over, + Mouse_Out, + Mouse_Up, + Mouse_Down, + + Key_Up, + Key_Down, + Key_Press, + + Scroll, + Wheel, + + Focus, + Focus_In, + Focus_Out, + Submit, + Blur, + Change, + Hash_Change, + Select, + + Animation_Start, + Animation_End, + Animation_Iteration, + Animation_Cancel, + + Copy, + Cut, + Paste, + + // Drag, + // Drag_Start, + // Drag_End, + // Drag_Enter, + // Drag_Leave, + // Drag_Over, + // Drop, + + Pointer_Cancel, + Pointer_Down, + Pointer_Enter, + Pointer_Leave, + Pointer_Move, + Pointer_Over, + Pointer_Up, + Got_Pointer_Capture, + Lost_Pointer_Capture, + Pointer_Lock_Change, + Pointer_Lock_Error, + + Selection_Change, + Selection_Start, + + Touch_Cancel, + Touch_End, + Touch_Move, + Touch_Start, + + Transition_Start, + Transition_End, + Transition_Run, + Transition_Cancel, + + Context_Menu, + + Gamepad_Connected, + Gamepad_Disconnected, + + Custom, + +} +event_kind_string := [Event_Kind]string{ + .Invalid = "", + + .Load = "load", + .Unload = "unload", + .Error = "error", + .Resize = "resize", + .Visibility_Change = "visibilitychange", + .Fullscreen_Change = "fullscreenchange", + .Fullscreen_Error = "fullscreenerror", + + .Click = "click", + .Double_Click = "dblclick", + .Mouse_Move = "mousemove", + .Mouse_Over = "mouseover", + .Mouse_Out = "mouseout", + .Mouse_Up = "mouseup", + .Mouse_Down = "mousedown", + + .Key_Up = "keyup", + .Key_Down = "keydown", + .Key_Press = "keypress", + + .Scroll = "scroll", + .Wheel = "wheel", + + .Focus = "focus", + .Focus_In = "focusin", + .Focus_Out = "focusout", + .Submit = "submit", + .Blur = "blur", + .Change = "change", + .Hash_Change = "hashchange", + .Select = "select", + + .Animation_Start = "animationstart", + .Animation_End = "animationend", + .Animation_Iteration = "animationiteration", + .Animation_Cancel = "animationcancel", + + .Copy = "copy", + .Cut = "cut", + .Paste = "paste", + + // .Drag, = "drag", + // .Drag_Start, = "dragstart", + // .Drag_End, = "dragend", + // .Drag_Enter, = "dragenter", + // .Drag_Leave, = "dragleave", + // .Drag_Over, = "dragover", + // .Drop, = "drop", + + .Pointer_Cancel = "pointercancel", + .Pointer_Down = "pointerdown", + .Pointer_Enter = "pointerenter", + .Pointer_Leave = "pointerleave", + .Pointer_Move = "pointermove", + .Pointer_Over = "pointerover", + .Pointer_Up = "pointerup", + .Got_Pointer_Capture = "gotpointercapture", + .Lost_Pointer_Capture = "lostpointercapture", + .Pointer_Lock_Change = "pointerlockchange", + .Pointer_Lock_Error = "pointerlockerror", + + .Selection_Change = "selectionchange", + .Selection_Start = "selectionstart", + + .Transition_Start = "transitionstart", + .Transition_End = "transitionend", + .Transition_Run = "transitionrun", + .Transition_Cancel = "transitioncancel", + + .Touch_Cancel = "touchcancel", + .Touch_End = "touchend", + .Touch_Move = "touchmove", + .Touch_Start = "touchstart", + + .Context_Menu = "contextmenu", + + .Gamepad_Connected = "gamepadconnected", + .Gamepad_Disconnected = "gamepaddisconnected", + + .Custom = "?custom?", +} + +Delta_Mode :: enum u32 { + Pixel = 0, + Line = 1, + Page = 2, +} + +Key_Location :: enum u8 { + Standard = 0, + Left = 1, + Right = 2, + Numpad = 3, +} + +KEYBOARD_MAX_KEY_SIZE :: 16 +KEYBOARD_MAX_CODE_SIZE :: 16 + +GAMEPAD_MAX_ID_SIZE :: 64 +GAMEPAD_MAX_MAPPING_SIZE :: 64 + +GAMEPAD_MAX_BUTTONS :: 64 +GAMEPAD_MAX_AXES :: 16 + +Event_Target_Kind :: enum u32 { + Element = 0, + Document = 1, + Window = 2, +} + +Event_Phase :: enum u8 { + None = 0, + Capturing_Phase = 1, + At_Target = 2, + Bubbling_Phase = 3, +} + +Event_Option :: enum u8 { + Bubbles = 0, + Cancelable = 1, + Composed = 2, +} +Event_Options :: distinct bit_set[Event_Option; u8] + +Gamepad_Button :: struct { + value: f64, + pressed: bool, + touched: bool, +} + +Gamepad_State :: struct { + id: string, + mapping: string, + index: int, + connected: bool, + timestamp: f64, + + button_count: int, + axis_count: int, + buttons: [GAMEPAD_MAX_BUTTONS]Gamepad_Button `fmt:"v,button_count"`, + axes: [GAMEPAD_MAX_AXES]f64 `fmt:"v,axes_count"`, + + _id_len: int `fmt:"-"`, + _mapping_len: int `fmt:"-"`, + _id_buf: [GAMEPAD_MAX_ID_SIZE]byte `fmt:"-"`, + _mapping_buf: [GAMEPAD_MAX_MAPPING_SIZE]byte `fmt:"-"`, +} + +Event :: struct { + kind: Event_Kind, + target_kind: Event_Target_Kind, + current_target_kind: Event_Target_Kind, + id: string, + timestamp: f64, + + phase: Event_Phase, + options: Event_Options, + is_composing: bool, + is_trusted: bool, + + using data: struct #raw_union #align(8) { + scroll: struct { + delta: [2]f64, + }, + visibility_change: struct { + is_visible: bool, + }, + wheel: struct { + delta: [3]f64, + delta_mode: Delta_Mode, + }, + + key: struct { + key: string, + code: string, + location: Key_Location, + + ctrl: bool, + shift: bool, + alt: bool, + meta: bool, + + repeat: bool, + + _key_len: int `fmt:"-"`, + _code_len: int `fmt:"-"`, + _key_buf: [KEYBOARD_MAX_KEY_SIZE]byte `fmt:"-"`, + _code_buf: [KEYBOARD_MAX_KEY_SIZE]byte `fmt:"-"`, + }, + + mouse: struct { + screen: [2]i64, + client: [2]i64, + offset: [2]i64, + page: [2]i64, + movement: [2]i64, + + ctrl: bool, + shift: bool, + alt: bool, + meta: bool, + + button: i16, + buttons: bit_set[0..<16; u16], + }, + + gamepad: Gamepad_State, + }, + + + user_data: rawptr, + callback: proc(e: Event), +} + +@(default_calling_convention="contextless") +foreign dom_lib { + event_stop_propagation :: proc() --- + event_stop_immediate_propagation :: proc() --- + event_prevent_default :: proc() --- + dispatch_custom_event :: proc(id: string, name: string, options := Event_Options{}) -> bool --- +} + +add_event_listener :: proc(id: string, kind: Event_Kind, user_data: rawptr, callback: proc(e: Event), use_capture := false) -> bool { + @(default_calling_convention="contextless") + foreign dom_lib { + @(link_name="add_event_listener") + _add_event_listener :: proc(id: string, name: string, name_code: Event_Kind, user_data: rawptr, callback: proc "odin" (Event), use_capture: bool) -> bool --- + } + // TODO: Pointer_Lock_Change etc related stuff for all different browsers + return _add_event_listener(id, event_kind_string[kind], kind, user_data, callback, use_capture) +} + +remove_event_listener :: proc(id: string, kind: Event_Kind, user_data: rawptr, callback: proc(e: Event)) -> bool { + @(default_calling_convention="contextless") + foreign dom_lib { + @(link_name="remove_event_listener") + _remove_event_listener :: proc(id: string, name: string, user_data: rawptr, callback: proc "odin" (Event)) -> bool --- + } + return _remove_event_listener(id, event_kind_string[kind], user_data, callback) +} + +add_window_event_listener :: proc(kind: Event_Kind, user_data: rawptr, callback: proc(e: Event), use_capture := false) -> bool { + @(default_calling_convention="contextless") + foreign dom_lib { + @(link_name="add_window_event_listener") + _add_window_event_listener :: proc(name: string, name_code: Event_Kind, user_data: rawptr, callback: proc "odin" (Event), use_capture: bool) -> bool --- + } + return _add_window_event_listener(event_kind_string[kind], kind, user_data, callback, use_capture) +} + +remove_window_event_listener :: proc(kind: Event_Kind, user_data: rawptr, callback: proc(e: Event)) -> bool { + @(default_calling_convention="contextless") + foreign dom_lib { + @(link_name="remove_window_event_listener") + _remove_window_event_listener :: proc(name: string, user_data: rawptr, callback: proc "odin" (Event)) -> bool --- + } + return _remove_window_event_listener(event_kind_string[kind], user_data, callback) +} + +remove_event_listener_from_event :: proc(e: Event) -> bool { + if e.id == "" { + return remove_window_event_listener(e.kind, e.user_data, e.callback) + } + return remove_event_listener(e.id, e.kind, e.user_data, e.callback) +} + +add_custom_event_listener :: proc(id: string, name: string, user_data: rawptr, callback: proc(e: Event), use_capture := false) -> bool { + @(default_calling_convention="contextless") + foreign dom_lib { + @(link_name="add_event_listener") + _add_event_listener :: proc(id: string, name: string, name_code: Event_Kind, user_data: rawptr, callback: proc "odin" (Event), use_capture: bool) -> bool --- + } + return _add_event_listener(id, name, .Custom, user_data, callback, use_capture) +} +remove_custom_event_listener :: proc(id: string, name: string, user_data: rawptr, callback: proc(e: Event)) -> bool { + @(default_calling_convention="contextless") + foreign dom_lib { + @(link_name="remove_event_listener") + _remove_event_listener :: proc(id: string, name: string, user_data: rawptr, callback: proc "odin" (Event)) -> bool --- + } + return _remove_event_listener(id, name, user_data, callback) +} + +get_gamepad_state :: proc "contextless" (index: int, s: ^Gamepad_State) -> bool { + @(default_calling_convention="contextless") + foreign dom_lib { + @(link_name="get_gamepad_state") + _get_gamepad_state :: proc(index: int, s: ^Gamepad_State) -> bool --- + } + + if s == nil { + return false + } + return _get_gamepad_state(index, s) +} + + +@(export, link_name="odin_dom_do_event_callback") +do_event_callback :: proc(user_data: rawptr, callback: proc(e: Event)) { + @(default_calling_convention="contextless") + foreign dom_lib { + init_event_raw :: proc(e: ^Event) --- + } + + if callback != nil { + event := Event{ + user_data = user_data, + callback = callback, + } + + + init_event_raw(&event) + + #partial switch event.kind { + case .Key_Up, .Key_Down, .Key_Press: + event.key.key = string(event.key._key_buf[:event.key._key_len]) + event.key.code = string(event.key._code_buf[:event.key._code_len]) + case .Gamepad_Connected, .Gamepad_Disconnected: + event.gamepad.id = string(event.gamepad._id_buf[:event.gamepad._id_len]) + event.gamepad.mapping = string(event.gamepad._mapping_buf[:event.gamepad._mapping_len]) + } + + callback(event) + } +}
\ No newline at end of file diff --git a/core/sys/wasm/js/events_all_targets.odin b/core/sys/wasm/js/events_all_targets.odin new file mode 100644 index 000000000..b7e01ca10 --- /dev/null +++ b/core/sys/wasm/js/events_all_targets.odin @@ -0,0 +1,287 @@ +#+build !js +package wasm_js_interface + + +Event_Kind :: enum u32 { + Invalid, + + Load, + Unload, + Error, + Resize, + Visibility_Change, + Fullscreen_Change, + Fullscreen_Error, + + Click, + Double_Click, + Mouse_Move, + Mouse_Over, + Mouse_Out, + Mouse_Up, + Mouse_Down, + + Key_Up, + Key_Down, + Key_Press, + + Scroll, + Wheel, + + Focus, + Submit, + Blur, + Change, + Select, + + Animation_Start, + Animation_End, + Animation_Iteration, + Animation_Cancel, + + Copy, + Cut, + Paste, + + // Drag, + // Drag_Start, + // Drag_End, + // Drag_Enter, + // Drag_Leave, + // Drag_Over, + // Drop, + + Pointer_Cancel, + Pointer_Down, + Pointer_Enter, + Pointer_Leave, + Pointer_Move, + Pointer_Over, + Pointer_Up, + Got_Pointer_Capture, + Lost_Pointer_Capture, + Pointer_Lock_Change, + Pointer_Lock_Error, + + Selection_Change, + Selection_Start, + + Touch_Cancel, + Touch_End, + Touch_Move, + Touch_Start, + + Transition_Start, + Transition_End, + Transition_Run, + Transition_Cancel, + + Context_Menu, + + Custom, + +} +event_kind_string := [Event_Kind]string{ + .Invalid = "", + + .Load = "load", + .Unload = "unload", + .Error = "error", + .Resize = "resize", + .Visibility_Change = "visibilitychange", + .Fullscreen_Change = "fullscreenchange", + .Fullscreen_Error = "fullscreenerror", + + .Click = "click", + .Double_Click = "dblclick", + .Mouse_Move = "mousemove", + .Mouse_Over = "mouseover", + .Mouse_Out = "mouseout", + .Mouse_Up = "mouseup", + .Mouse_Down = "mousedown", + + .Key_Up = "keyup", + .Key_Down = "keydown", + .Key_Press = "keypress", + + .Scroll = "scroll", + .Wheel = "wheel", + + .Focus = "focus", + .Submit = "submit", + .Blur = "blur", + .Change = "change", + .Select = "select", + + .Animation_Start = "animationstart", + .Animation_End = "animationend", + .Animation_Iteration = "animationiteration", + .Animation_Cancel = "animationcancel", + + .Copy = "copy", + .Cut = "cut", + .Paste = "paste", + + // .Drag, = "drag", + // .Drag_Start, = "dragstart", + // .Drag_End, = "dragend", + // .Drag_Enter, = "dragenter", + // .Drag_Leave, = "dragleave", + // .Drag_Over, = "dragover", + // .Drop, = "drop", + + .Pointer_Cancel = "pointercancel", + .Pointer_Down = "pointerdown", + .Pointer_Enter = "pointerenter", + .Pointer_Leave = "pointerleave", + .Pointer_Move = "pointermove", + .Pointer_Over = "pointerover", + .Pointer_Up = "pointerup", + .Got_Pointer_Capture = "gotpointercapture", + .Lost_Pointer_Capture = "lostpointercapture", + .Pointer_Lock_Change = "pointerlockchange", + .Pointer_Lock_Error = "pointerlockerror", + + .Selection_Change = "selectionchange", + .Selection_Start = "selectionstart", + + .Transition_Start = "transitionstart", + .Transition_End = "transitionend", + .Transition_Run = "transitionrun", + .Transition_Cancel = "transitioncancel", + + .Touch_Cancel = "touchcancel", + .Touch_End = "touchend", + .Touch_Move = "touchmove", + .Touch_Start = "touchstart", + + .Context_Menu = "contextmenu", + + .Custom = "?custom?", +} + +Delta_Mode :: enum u32 { + Pixel = 0, + Line = 1, + Page = 2, +} + +Key_Location :: enum u8 { + Standard = 0, + Left = 1, + Right = 2, + Numpad = 3, +} + +KEYBOARD_MAX_KEY_SIZE :: 16 +KEYBOARD_MAX_CODE_SIZE :: 16 + +Event_Target_Kind :: enum u32 { + Element = 0, + Document = 1, + Window = 2, +} + +Event_Phase :: enum u8 { + None = 0, + Capturing_Phase = 1, + At_Target = 2, + Bubbling_Phase = 3, +} + +Event_Option :: enum u8 { + Bubbles = 0, + Cancelable = 1, + Composed = 2, +} +Event_Options :: distinct bit_set[Event_Option; u8] + +Event :: struct { + kind: Event_Kind, + target_kind: Event_Target_Kind, + current_target_kind: Event_Target_Kind, + id: string, + timestamp: f64, + + phase: Event_Phase, + options: Event_Options, + is_composing: bool, + is_trusted: bool, + + using data: struct #raw_union #align(8) { + scroll: struct { + delta: [2]f64, + }, + visibility_change: struct { + is_visible: bool, + }, + wheel: struct { + delta: [3]f64, + delta_mode: Delta_Mode, + }, + + key: struct { + key: string, + code: string, + location: Key_Location, + + ctrl: bool, + shift: bool, + alt: bool, + meta: bool, + + repeat: bool, + + _key_buf: [KEYBOARD_MAX_KEY_SIZE]byte, + _code_buf: [KEYBOARD_MAX_KEY_SIZE]byte, + }, + + mouse: struct { + screen: [2]i64, + client: [2]i64, + offset: [2]i64, + page: [2]i64, + movement: [2]i64, + + ctrl: bool, + shift: bool, + alt: bool, + meta: bool, + + button: i16, + buttons: bit_set[0..<16; u16], + }, + }, + + + user_data: rawptr, + callback: proc(e: Event), +} + + +add_event_listener :: proc(id: string, kind: Event_Kind, user_data: rawptr, callback: proc(e: Event), use_capture := false) -> bool { + panic("vendor:wasm/js not supported on non JS targets") +} + +remove_event_listener :: proc(id: string, kind: Event_Kind, user_data: rawptr, callback: proc(e: Event)) -> bool { + panic("vendor:wasm/js not supported on non JS targets") +} + +add_window_event_listener :: proc(kind: Event_Kind, user_data: rawptr, callback: proc(e: Event), use_capture := false) -> bool { + panic("vendor:wasm/js not supported on non JS targets") +} + +remove_window_event_listener :: proc(kind: Event_Kind, user_data: rawptr, callback: proc(e: Event)) -> bool { + panic("vendor:wasm/js not supported on non JS targets") +} + +remove_event_listener_from_event :: proc(e: Event) -> bool { + panic("vendor:wasm/js not supported on non JS targets") +} + +add_custom_event_listener :: proc(id: string, name: string, user_data: rawptr, callback: proc(e: Event), use_capture := false) -> bool { + panic("vendor:wasm/js not supported on non JS targets") +} +remove_custom_event_listener :: proc(id: string, name: string, user_data: rawptr, callback: proc(e: Event)) -> bool { + panic("vendor:wasm/js not supported on non JS targets") +}
\ No newline at end of file diff --git a/core/sys/wasm/js/general.odin b/core/sys/wasm/js/general.odin new file mode 100644 index 000000000..4ed2ae298 --- /dev/null +++ b/core/sys/wasm/js/general.odin @@ -0,0 +1,12 @@ +#+build js wasm32, js wasm64p32 +package wasm_js_interface + +foreign import "odin_env" + +@(default_calling_convention="contextless") +foreign odin_env { + trap :: proc() -> ! --- + abort :: proc() -> ! --- + alert :: proc(msg: string) --- + evaluate :: proc(str: string) --- +}
\ No newline at end of file diff --git a/core/sys/wasm/js/memory_all_targets.odin b/core/sys/wasm/js/memory_all_targets.odin new file mode 100644 index 000000000..e80d13c0b --- /dev/null +++ b/core/sys/wasm/js/memory_all_targets.odin @@ -0,0 +1,14 @@ +#+build !js +package wasm_js_interface + +import "core:mem" + +PAGE_SIZE :: 64 * 1024 +page_alloc :: proc(page_count: int) -> (data: []byte, err: mem.Allocator_Error) { + panic("vendor:wasm/js not supported on non-js targets") +} + +page_allocator :: proc() -> mem.Allocator { + panic("vendor:wasm/js not supported on non-js targets") +} + diff --git a/core/sys/wasm/js/memory_js.odin b/core/sys/wasm/js/memory_js.odin new file mode 100644 index 000000000..8232cd0c9 --- /dev/null +++ b/core/sys/wasm/js/memory_js.odin @@ -0,0 +1,44 @@ +#+build js wasm32, js wasm64p32 +package wasm_js_interface + +import "core:mem" +import "base:intrinsics" + +PAGE_SIZE :: 64 * 1024 +page_alloc :: proc(page_count: int) -> (data: []byte, err: mem.Allocator_Error) { + prev_page_count := intrinsics.wasm_memory_grow(0, uintptr(page_count)) + if prev_page_count < 0 { + return nil, .Out_Of_Memory + } + + ptr := ([^]u8)(uintptr(prev_page_count) * PAGE_SIZE) + return ptr[:page_count * PAGE_SIZE], nil +} + +page_allocator :: proc() -> mem.Allocator { + procedure :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode, + size, alignment: int, + old_memory: rawptr, old_size: int, + location := #caller_location) -> ([]byte, mem.Allocator_Error) { + switch mode { + case .Alloc, .Alloc_Non_Zeroed: + assert(size % PAGE_SIZE == 0) + return page_alloc(size/PAGE_SIZE) + case .Resize, .Free, .Free_All, .Query_Info, .Resize_Non_Zeroed: + return nil, .Mode_Not_Implemented + case .Query_Features: + set := (^mem.Allocator_Mode_Set)(old_memory) + if set != nil { + set^ = {.Alloc, .Query_Features} + } + } + + return nil, nil + } + + return { + procedure = procedure, + data = nil, + } +} + diff --git a/core/sys/wasm/js/odin.js b/core/sys/wasm/js/odin.js new file mode 100644 index 000000000..bf002da74 --- /dev/null +++ b/core/sys/wasm/js/odin.js @@ -0,0 +1,1983 @@ +"use strict"; + +(function() { + +function getElement(name) { + if (name) { + return document.getElementById(name); + } + return undefined; +} + +function stripNewline(str) { + return str.replace(/\n/, ' ') +} + +class WasmMemoryInterface { + constructor() { + this.memory = null; + this.exports = null; + this.listenerMap = {}; + + // Size (in bytes) of the integer type, should be 4 on `js_wasm32` and 8 on `js_wasm64p32` + this.intSize = 4; + } + + setIntSize(size) { + this.intSize = size; + } + + setMemory(memory) { + this.memory = memory; + } + + setExports(exports) { + this.exports = exports; + } + + get mem() { + return new DataView(this.memory.buffer); + } + + + loadF32Array(addr, len) { + let array = new Float32Array(this.memory.buffer, addr, len); + return array; + } + loadF64Array(addr, len) { + let array = new Float64Array(this.memory.buffer, addr, len); + return array; + } + loadU32Array(addr, len) { + let array = new Uint32Array(this.memory.buffer, addr, len); + return array; + } + loadI32Array(addr, len) { + let array = new Int32Array(this.memory.buffer, addr, len); + return array; + } + + + loadU8(addr) { return this.mem.getUint8 (addr); } + loadI8(addr) { return this.mem.getInt8 (addr); } + loadU16(addr) { return this.mem.getUint16 (addr, true); } + loadI16(addr) { return this.mem.getInt16 (addr, true); } + loadU32(addr) { return this.mem.getUint32 (addr, true); } + loadI32(addr) { return this.mem.getInt32 (addr, true); } + loadU64(addr) { + const lo = this.mem.getUint32(addr + 0, true); + const hi = this.mem.getUint32(addr + 4, true); + return lo + hi*4294967296; + }; + loadI64(addr) { + const lo = this.mem.getUint32(addr + 0, true); + const hi = this.mem.getInt32 (addr + 4, true); + return lo + hi*4294967296; + }; + loadF32(addr) { return this.mem.getFloat32(addr, true); } + loadF64(addr) { return this.mem.getFloat64(addr, true); } + loadInt(addr) { + if (this.intSize == 8) { + return this.loadI64(addr); + } else if (this.intSize == 4) { + return this.loadI32(addr); + } else { + throw new Error('Unhandled `intSize`, expected `4` or `8`'); + } + }; + loadUint(addr) { + if (this.intSize == 8) { + return this.loadU64(addr); + } else if (this.intSize == 4) { + return this.loadU32(addr); + } else { + throw new Error('Unhandled `intSize`, expected `4` or `8`'); + } + }; + loadPtr(addr) { return this.loadU32(addr); } + + loadB32(addr) { + return this.loadU32(addr) != 0; + } + + loadBytes(ptr, len) { + return new Uint8Array(this.memory.buffer, ptr, Number(len)); + } + + loadString(ptr, len) { + const bytes = this.loadBytes(ptr, Number(len)); + return new TextDecoder().decode(bytes); + } + + loadCstring(ptr) { + const start = this.loadPtr(ptr); + if (start == 0) { + return null; + } + let len = 0; + for (; this.mem.getUint8(start+len) != 0; len += 1) {} + return this.loadString(start, len); + } + + storeU8(addr, value) { this.mem.setUint8 (addr, value); } + storeI8(addr, value) { this.mem.setInt8 (addr, value); } + storeU16(addr, value) { this.mem.setUint16 (addr, value, true); } + storeI16(addr, value) { this.mem.setInt16 (addr, value, true); } + storeU32(addr, value) { this.mem.setUint32 (addr, value, true); } + storeI32(addr, value) { this.mem.setInt32 (addr, value, true); } + storeU64(addr, value) { + this.mem.setUint32(addr + 0, Number(value), true); + + let div = 4294967296; + if (typeof value == 'bigint') { + div = BigInt(div); + } + + this.mem.setUint32(addr + 4, Math.floor(Number(value / div)), true); + } + storeI64(addr, value) { + this.mem.setUint32(addr + 0, Number(value), true); + + let div = 4294967296; + if (typeof value == 'bigint') { + div = BigInt(div); + } + + this.mem.setInt32(addr + 4, Math.floor(Number(value / div)), true); + } + storeF32(addr, value) { this.mem.setFloat32(addr, value, true); } + storeF64(addr, value) { this.mem.setFloat64(addr, value, true); } + storeInt(addr, value) { + if (this.intSize == 8) { + this.storeI64(addr, value); + } else if (this.intSize == 4) { + this.storeI32(addr, value); + } else { + throw new Error('Unhandled `intSize`, expected `4` or `8`'); + } + } + storeUint(addr, value) { + if (this.intSize == 8) { + this.storeU64(addr, value); + } else if (this.intSize == 4) { + this.storeU32(addr, value); + } else { + throw new Error('Unhandled `intSize`, expected `4` or `8`'); + } + } + + // Returned length might not be the same as `value.length` if non-ascii strings are given. + storeString(addr, value) { + const src = new TextEncoder().encode(value); + const dst = new Uint8Array(this.memory.buffer, addr, src.length); + dst.set(src); + return src.length; + } +}; + +class WebGLInterface { + constructor(wasmMemoryInterface) { + this.wasmMemoryInterface = wasmMemoryInterface; + this.ctxElement = null; + this.ctx = null; + this.ctxVersion = 1.0; + this.counter = 1; + this.lastError = 0; + this.buffers = []; + this.mappedBuffers = {}; + this.programs = []; + this.framebuffers = []; + this.renderbuffers = []; + this.textures = []; + this.uniforms = []; + this.shaders = []; + this.vaos = []; + this.contexts = []; + this.currentContext = null; + this.offscreenCanvases = {}; + this.timerQueriesEXT = []; + this.queries = []; + this.samplers = []; + this.transformFeedbacks = []; + this.syncs = []; + this.programInfos = {}; + } + + get mem() { + return this.wasmMemoryInterface + } + + setCurrentContext(element, contextSettings) { + if (!element) { + return false; + } + if (this.ctxElement == element) { + return true; + } + + contextSettings = contextSettings ?? {}; + this.ctx = element.getContext("webgl2", contextSettings) || element.getContext("webgl", contextSettings); + if (!this.ctx) { + return false; + } + this.ctxElement = element; + if (this.ctx.getParameter(0x1F02).indexOf("WebGL 2.0") !== -1) { + this.ctxVersion = 2.0; + } else { + this.ctxVersion = 1.0; + } + return true; + } + + assertWebGL2() { + if (this.ctxVersion < 2) { + throw new Error("WebGL2 procedure called in a canvas without a WebGL2 context"); + } + } + getNewId(table) { + for (var ret = this.counter++, i = table.length; i < ret; i++) { + table[i] = null; + } + return ret; + } + recordError(errorCode) { + this.lastError || (this.lastError = errorCode); + } + populateUniformTable(program) { + let p = this.programs[program]; + this.programInfos[program] = { + uniforms: {}, + maxUniformLength: 0, + maxAttributeLength: -1, + maxUniformBlockNameLength: -1, + }; + for (let ptable = this.programInfos[program], utable = ptable.uniforms, numUniforms = this.ctx.getProgramParameter(p, this.ctx.ACTIVE_UNIFORMS), i = 0; i < numUniforms; ++i) { + let u = this.ctx.getActiveUniform(p, i); + let name = u.name; + if (ptable.maxUniformLength = Math.max(ptable.maxUniformLength, name.length + 1), name.indexOf("]", name.length - 1) !== -1) { + name = name.slice(0, name.lastIndexOf("[")); + } + let loc = this.ctx.getUniformLocation(p, name); + if (loc !== null) { + let id = this.getNewId(this.uniforms); + utable[name] = [u.size, id], this.uniforms[id] = loc; + for (let j = 1; j < u.size; ++j) { + let n = name + "[" + j + "]"; + let loc = this.ctx.getUniformLocation(p, n); + let id = this.getNewId(this.uniforms); + this.uniforms[id] = loc; + } + } + } + } + getSource(shader, strings_ptr, strings_length) { + const stringSize = this.mem.intSize*2; + let source = ""; + for (let i = 0; i < strings_length; i++) { + let ptr = this.mem.loadPtr(strings_ptr + i*stringSize); + let len = this.mem.loadPtr(strings_ptr + i*stringSize + 4); + let str = this.mem.loadString(ptr, len); + source += str; + } + return source; + } + + getWebGL1Interface() { + return { + SetCurrentContextById: (name_ptr, name_len) => { + let name = this.mem.loadString(name_ptr, name_len); + let element = getElement(name); + return this.setCurrentContext(element, {alpha: true, antialias: true, depth: true, premultipliedAlpha: true}); + }, + CreateCurrentContextById: (name_ptr, name_len, attributes) => { + let name = this.mem.loadString(name_ptr, name_len); + let element = getElement(name); + + let contextSettings = { + alpha: !(attributes & (1<<0)), + antialias: !(attributes & (1<<1)), + depth: !(attributes & (1<<2)), + failIfMajorPerformanceCaveat: !!(attributes & (1<<3)), + premultipliedAlpha: !(attributes & (1<<4)), + preserveDrawingBuffer: !!(attributes & (1<<5)), + stencil: !!(attributes & (1<<6)), + desynchronized: !!(attributes & (1<<7)), + }; + + return this.setCurrentContext(element, contextSettings); + }, + GetCurrentContextAttributes: () => { + if (!this.ctx) { + return 0; + } + let attrs = this.ctx.getContextAttributes(); + let res = 0; + if (!attrs.alpha) res |= 1<<0; + if (!attrs.antialias) res |= 1<<1; + if (!attrs.depth) res |= 1<<2; + if (attrs.failIfMajorPerformanceCaveat) res |= 1<<3; + if (!attrs.premultipliedAlpha) res |= 1<<4; + if (attrs.preserveDrawingBuffer) res |= 1<<5; + if (attrs.stencil) res |= 1<<6; + if (attrs.desynchronized) res |= 1<<7; + return res; + }, + + DrawingBufferWidth: () => this.ctx.drawingBufferWidth, + DrawingBufferHeight: () => this.ctx.drawingBufferHeight, + + IsExtensionSupported: (name_ptr, name_len) => { + let name = this.mem.loadString(name_ptr, name_len); + let extensions = this.ctx.getSupportedExtensions(); + return extensions.indexOf(name) !== -1 + }, + + + GetError: () => { + let err = this.lastError; + this.recordError(0); + if (err) { + return err; + } + return this.ctx.getError(); + }, + + GetWebGLVersion: (major_ptr, minor_ptr) => { + let version = this.ctx.getParameter(0x1F02); + if (version.indexOf("WebGL 2.0") !== -1) { + this.mem.storeI32(major_ptr, 2); + this.mem.storeI32(minor_ptr, 0); + return; + } + + this.mem.storeI32(major_ptr, 1); + this.mem.storeI32(minor_ptr, 0); + }, + GetESVersion: (major_ptr, minor_ptr) => { + let version = this.ctx.getParameter(0x1F02); + if (version.indexOf("OpenGL ES 3.0") !== -1) { + this.mem.storeI32(major_ptr, 3); + this.mem.storeI32(minor_ptr, 0); + return; + } + + this.mem.storeI32(major_ptr, 2); + this.mem.storeI32(minor_ptr, 0); + }, + + + ActiveTexture: (x) => { + this.ctx.activeTexture(x); + }, + AttachShader: (program, shader) => { + this.ctx.attachShader(this.programs[program], this.shaders[shader]); + }, + BindAttribLocation: (program, index, name_ptr, name_len) => { + let name = this.mem.loadString(name_ptr, name_len); + this.ctx.bindAttribLocation(this.programs[program], index, name) + }, + BindBuffer: (target, buffer) => { + let bufferObj = buffer ? this.buffers[buffer] : null; + if (target == 35051) { + this.ctx.currentPixelPackBufferBinding = buffer; + } else { + if (target == 35052) { + this.ctx.currentPixelUnpackBufferBinding = buffer; + } + this.ctx.bindBuffer(target, bufferObj) + } + }, + BindFramebuffer: (target, framebuffer) => { + this.ctx.bindFramebuffer(target, framebuffer ? this.framebuffers[framebuffer] : null) + }, + BindTexture: (target, texture) => { + this.ctx.bindTexture(target, texture ? this.textures[texture] : null) + }, + BlendColor: (red, green, blue, alpha) => { + this.ctx.blendColor(red, green, blue, alpha); + }, + BlendEquation: (mode) => { + this.ctx.blendEquation(mode); + }, + BlendFunc: (sfactor, dfactor) => { + this.ctx.blendFunc(sfactor, dfactor); + }, + BlendFuncSeparate: (srcRGB, dstRGB, srcAlpha, dstAlpha) => { + this.ctx.blendFuncSeparate(srcRGB, dstRGB, srcAlpha, dstAlpha); + }, + + + BufferData: (target, size, data, usage) => { + if (data) { + this.ctx.bufferData(target, this.mem.loadBytes(data, size), usage); + } else { + this.ctx.bufferData(target, size, usage); + } + }, + BufferSubData: (target, offset, size, data) => { + if (data) { + this.ctx.bufferSubData(target, offset, this.mem.loadBytes(data, size)); + } else { + this.ctx.bufferSubData(target, offset, null); + } + }, + + + Clear: (x) => { + this.ctx.clear(x); + }, + ClearColor: (r, g, b, a) => { + this.ctx.clearColor(r, g, b, a); + }, + ClearDepth: (x) => { + this.ctx.clearDepth(x); + }, + ClearStencil: (x) => { + this.ctx.clearStencil(x); + }, + ColorMask: (r, g, b, a) => { + this.ctx.colorMask(!!r, !!g, !!b, !!a); + }, + CompileShader: (shader) => { + this.ctx.compileShader(this.shaders[shader]); + }, + + + CompressedTexImage2D: (target, level, internalformat, width, height, border, imageSize, data) => { + if (data) { + this.ctx.compressedTexImage2D(target, level, internalformat, width, height, border, this.mem.loadBytes(data, imageSize)); + } else { + this.ctx.compressedTexImage2D(target, level, internalformat, width, height, border, null); + } + }, + CompressedTexSubImage2D: (target, level, xoffset, yoffset, width, height, format, imageSize, data) => { + if (data) { + this.ctx.compressedTexSubImage2D(target, level, xoffset, yoffset, width, height, format, this.mem.loadBytes(data, imageSize)); + } else { + this.ctx.compressedTexSubImage2D(target, level, xoffset, yoffset, width, height, format, null); + } + }, + + CopyTexImage2D: (target, level, internalformat, x, y, width, height, border) => { + this.ctx.copyTexImage2D(target, level, internalformat, x, y, width, height, border); + }, + CopyTexSubImage2D: (target, level, xoffset, yoffset, x, y, width, height) => { + this.ctx.copyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height); + }, + + + CreateBuffer: () => { + let buffer = this.ctx.createBuffer(); + if (!buffer) { + this.recordError(1282); + return 0; + } + let id = this.getNewId(this.buffers); + buffer.name = id + this.buffers[id] = buffer; + return id; + }, + CreateFramebuffer: () => { + let buffer = this.ctx.createFramebuffer(); + let id = this.getNewId(this.framebuffers); + buffer.name = id + this.framebuffers[id] = buffer; + return id; + }, + CreateProgram: () => { + let program = this.ctx.createProgram(); + let id = this.getNewId(this.programs); + program.name = id; + this.programs[id] = program; + return id; + }, + CreateRenderbuffer: () => { + let buffer = this.ctx.createRenderbuffer(); + let id = this.getNewId(this.renderbuffers); + buffer.name = id; + this.renderbuffers[id] = buffer; + return id; + }, + CreateShader: (shaderType) => { + let shader = this.ctx.createShader(shaderType); + let id = this.getNewId(this.shaders); + shader.name = id; + this.shaders[id] = shader; + return id; + }, + CreateTexture: () => { + let texture = this.ctx.createTexture(); + if (!texture) { + this.recordError(1282) + return 0; + } + let id = this.getNewId(this.textures); + texture.name = id; + this.textures[id] = texture; + return id; + }, + + + CullFace: (mode) => { + this.ctx.cullFace(mode); + }, + + + DeleteBuffer: (id) => { + let obj = this.buffers[id]; + if (obj && id != 0) { + this.ctx.deleteBuffer(obj); + this.buffers[id] = null; + } + }, + DeleteFramebuffer: (id) => { + let obj = this.framebuffers[id]; + if (obj && id != 0) { + this.ctx.deleteFramebuffer(obj); + this.framebuffers[id] = null; + } + }, + DeleteProgram: (id) => { + let obj = this.programs[id]; + if (obj && id != 0) { + this.ctx.deleteProgram(obj); + this.programs[id] = null; + } + }, + DeleteRenderbuffer: (id) => { + let obj = this.renderbuffers[id]; + if (obj && id != 0) { + this.ctx.deleteRenderbuffer(obj); + this.renderbuffers[id] = null; + } + }, + DeleteShader: (id) => { + let obj = this.shaders[id]; + if (obj && id != 0) { + this.ctx.deleteShader(obj); + this.shaders[id] = null; + } + }, + DeleteTexture: (id) => { + let obj = this.textures[id]; + if (obj && id != 0) { + this.ctx.deleteTexture(obj); + this.textures[id] = null; + } + }, + + + DepthFunc: (func) => { + this.ctx.depthFunc(func); + }, + DepthMask: (flag) => { + this.ctx.depthMask(!!flag); + }, + DepthRange: (zNear, zFar) => { + this.ctx.depthRange(zNear, zFar); + }, + DetachShader: (program, shader) => { + this.ctx.detachShader(this.programs[program], this.shaders[shader]); + }, + Disable: (cap) => { + this.ctx.disable(cap); + }, + DisableVertexAttribArray: (index) => { + this.ctx.disableVertexAttribArray(index); + }, + DrawArrays: (mode, first, count) => { + this.ctx.drawArrays(mode, first, count); + }, + DrawElements: (mode, count, type, indices) => { + this.ctx.drawElements(mode, count, type, indices); + }, + + + Enable: (cap) => { + this.ctx.enable(cap); + }, + EnableVertexAttribArray: (index) => { + this.ctx.enableVertexAttribArray(index); + }, + Finish: () => { + this.ctx.finish(); + }, + Flush: () => { + this.ctx.flush(); + }, + FramebufferRenderbuffer: (target, attachment, renderbuffertarget, renderbuffer) => { + this.ctx.framebufferRenderbuffer(target, attachment, renderbuffertarget, this.renderbuffers[renderbuffer]); + }, + FramebufferTexture2D: (target, attachment, textarget, texture, level) => { + this.ctx.framebufferTexture2D(target, attachment, textarget, this.textures[texture], level); + }, + FrontFace: (mode) => { + this.ctx.frontFace(mode); + }, + + + GenerateMipmap: (target) => { + this.ctx.generateMipmap(target); + }, + + + GetAttribLocation: (program, name_ptr, name_len) => { + let name = this.mem.loadString(name_ptr, name_len); + return this.ctx.getAttribLocation(this.programs[program], name); + }, + + + GetParameter: (pname) => { + return this.ctx.getParameter(pname); + }, + GetProgramParameter: (program, pname) => { + return this.ctx.getProgramParameter(this.programs[program], pname) + }, + GetProgramInfoLog: (program, buf_ptr, buf_len, length_ptr) => { + let log = this.ctx.getProgramInfoLog(this.programs[program]); + if (log === null) { + log = "(unknown error)"; + } + if (buf_len > 0 && buf_ptr) { + let n = Math.min(buf_len, log.length); + log = log.substring(0, n); + this.mem.loadBytes(buf_ptr, buf_len).set(new TextEncoder().encode(log)) + + this.mem.storeInt(length_ptr, n); + } + }, + GetShaderInfoLog: (shader, buf_ptr, buf_len, length_ptr) => { + let log = this.ctx.getShaderInfoLog(this.shaders[shader]); + if (log === null) { + log = "(unknown error)"; + } + if (buf_len > 0 && buf_ptr) { + let n = Math.min(buf_len, log.length); + log = log.substring(0, n); + this.mem.loadBytes(buf_ptr, buf_len).set(new TextEncoder().encode(log)) + + this.mem.storeInt(length_ptr, n); + } + }, + GetShaderiv: (shader, pname, p) => { + if (p) { + if (pname == 35716) { + let log = this.ctx.getShaderInfoLog(this.shaders[shader]); + if (log === null) { + log = "(unknown error)"; + } + this.mem.storeInt(p, log.length+1); + } else if (pname == 35720) { + let source = this.ctx.getShaderSource(this.shaders[shader]); + let sourceLength = (source === null || source.length == 0) ? 0 : source.length+1; + this.mem.storeInt(p, sourceLength); + } else { + let param = this.ctx.getShaderParameter(this.shaders[shader], pname); + this.mem.storeI32(p, param); + } + } else { + this.recordError(1281); + } + }, + + + GetUniformLocation: (program, name_ptr, name_len) => { + let name = this.mem.loadString(name_ptr, name_len); + let arrayOffset = 0; + if (name.indexOf("]", name.length - 1) !== -1) { + let ls = name.lastIndexOf("["), + arrayIndex = name.slice(ls + 1, -1); + if (arrayIndex.length > 0 && (arrayOffset = parseInt(arrayIndex)) < 0) { + return -1; + } + name = name.slice(0, ls) + } + var ptable = this.programInfos[program]; + if (!ptable) { + return -1; + } + var uniformInfo = ptable.uniforms[name]; + return (uniformInfo && arrayOffset < uniformInfo[0]) ? uniformInfo[1] + arrayOffset : -1 + }, + + + GetVertexAttribOffset: (index, pname) => { + return this.ctx.getVertexAttribOffset(index, pname); + }, + + + Hint: (target, mode) => { + this.ctx.hint(target, mode); + }, + + + IsBuffer: (buffer) => this.ctx.isBuffer(this.buffers[buffer]), + IsEnabled: (cap) => this.ctx.isEnabled(cap), + IsFramebuffer: (framebuffer) => this.ctx.isFramebuffer(this.framebuffers[framebuffer]), + IsProgram: (program) => this.ctx.isProgram(this.programs[program]), + IsRenderbuffer: (renderbuffer) => this.ctx.isRenderbuffer(this.renderbuffers[renderbuffer]), + IsShader: (shader) => this.ctx.isShader(this.shaders[shader]), + IsTexture: (texture) => this.ctx.isTexture(this.textures[texture]), + + LineWidth: (width) => { + this.ctx.lineWidth(width); + }, + LinkProgram: (program) => { + this.ctx.linkProgram(this.programs[program]); + this.programInfos[program] = null; + this.populateUniformTable(program); + }, + PixelStorei: (pname, param) => { + this.ctx.pixelStorei(pname, param); + }, + PolygonOffset: (factor, units) => { + this.ctx.polygonOffset(factor, units); + }, + + + ReadnPixels: (x, y, width, height, format, type, bufSize, data) => { + this.ctx.readPixels(x, y, width, height, format, type, this.mem.loadBytes(data, bufSize)); + }, + RenderbufferStorage: (target, internalformat, width, height) => { + this.ctx.renderbufferStorage(target, internalformat, width, height); + }, + SampleCoverage: (value, invert) => { + this.ctx.sampleCoverage(value, !!invert); + }, + Scissor: (x, y, width, height) => { + this.ctx.scissor(x, y, width, height); + }, + ShaderSource: (shader, strings_ptr, strings_length) => { + let source = this.getSource(shader, strings_ptr, strings_length); + this.ctx.shaderSource(this.shaders[shader], source); + }, + + StencilFunc: (func, ref, mask) => { + this.ctx.stencilFunc(func, ref, mask); + }, + StencilFuncSeparate: (face, func, ref, mask) => { + this.ctx.stencilFuncSeparate(face, func, ref, mask); + }, + StencilMask: (mask) => { + this.ctx.stencilMask(mask); + }, + StencilMaskSeparate: (face, mask) => { + this.ctx.stencilMaskSeparate(face, mask); + }, + StencilOp: (fail, zfail, zpass) => { + this.ctx.stencilOp(fail, zfail, zpass); + }, + StencilOpSeparate: (face, fail, zfail, zpass) => { + this.ctx.stencilOpSeparate(face, fail, zfail, zpass); + }, + + + TexImage2D: (target, level, internalformat, width, height, border, format, type, size, data) => { + if (data) { + this.ctx.texImage2D(target, level, internalformat, width, height, border, format, type, this.mem.loadBytes(data, size)); + } else { + this.ctx.texImage2D(target, level, internalformat, width, height, border, format, type, null); + } + }, + TexParameterf: (target, pname, param) => { + this.ctx.texParameterf(target, pname, param); + }, + TexParameteri: (target, pname, param) => { + this.ctx.texParameteri(target, pname, param); + }, + TexSubImage2D: (target, level, xoffset, yoffset, width, height, format, type, size, data) => { + this.ctx.texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, this.mem.loadBytes(data, size)); + }, + + + Uniform1f: (location, v0) => { this.ctx.uniform1f(this.uniforms[location], v0); }, + Uniform2f: (location, v0, v1) => { this.ctx.uniform2f(this.uniforms[location], v0, v1); }, + Uniform3f: (location, v0, v1, v2) => { this.ctx.uniform3f(this.uniforms[location], v0, v1, v2); }, + Uniform4f: (location, v0, v1, v2, v3) => { this.ctx.uniform4f(this.uniforms[location], v0, v1, v2, v3); }, + + Uniform1i: (location, v0) => { this.ctx.uniform1i(this.uniforms[location], v0); }, + Uniform2i: (location, v0, v1) => { this.ctx.uniform2i(this.uniforms[location], v0, v1); }, + Uniform3i: (location, v0, v1, v2) => { this.ctx.uniform3i(this.uniforms[location], v0, v1, v2); }, + Uniform4i: (location, v0, v1, v2, v3) => { this.ctx.uniform4i(this.uniforms[location], v0, v1, v2, v3); }, + + UniformMatrix2fv: (location, addr) => { + let array = this.mem.loadF32Array(addr, 2*2); + this.ctx.uniformMatrix2fv(this.uniforms[location], false, array); + }, + UniformMatrix3fv: (location, addr) => { + let array = this.mem.loadF32Array(addr, 3*3); + this.ctx.uniformMatrix3fv(this.uniforms[location], false, array); + }, + UniformMatrix4fv: (location, addr) => { + let array = this.mem.loadF32Array(addr, 4*4); + this.ctx.uniformMatrix4fv(this.uniforms[location], false, array); + }, + + UseProgram: (program) => { + if (program) this.ctx.useProgram(this.programs[program]); + }, + ValidateProgram: (program) => { + if (program) this.ctx.validateProgram(this.programs[program]); + }, + + + VertexAttrib1f: (index, x) => { + this.ctx.vertexAttrib1f(index, x); + }, + VertexAttrib2f: (index, x, y) => { + this.ctx.vertexAttrib2f(index, x, y); + }, + VertexAttrib3f: (index, x, y, z) => { + this.ctx.vertexAttrib3f(index, x, y, z); + }, + VertexAttrib4f: (index, x, y, z, w) => { + this.ctx.vertexAttrib4f(index, x, y, z, w); + }, + VertexAttribPointer: (index, size, type, normalized, stride, ptr) => { + this.ctx.vertexAttribPointer(index, size, type, !!normalized, stride, ptr); + }, + + Viewport: (x, y, w, h) => { + this.ctx.viewport(x, y, w, h); + }, + }; + } + + getWebGL2Interface() { + return { + /* Buffer objects */ + CopyBufferSubData: (readTarget, writeTarget, readOffset, writeOffset, size) => { + this.assertWebGL2(); + this.ctx.copyBufferSubData(readTarget, writeTarget, readOffset, writeOffset, size); + }, + GetBufferSubData: (target, srcByteOffset, dst_buffer_ptr, dst_buffer_len, dstOffset, length) => { + this.assertWebGL2(); + this.ctx.getBufferSubData(target, srcByteOffset, this.mem.loadBytes(dst_buffer_ptr, dst_buffer_len), dstOffset, length); + }, + + /* Framebuffer objects */ + BlitFramebuffer: (srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter) => { + this.assertWebGL2(); + this.ctx.blitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter); + }, + FramebufferTextureLayer: (target, attachment, texture, level, layer) => { + this.assertWebGL2(); + this.ctx.framebufferTextureLayer(target, attachment, this.textures[texture], level, layer); + }, + InvalidateFramebuffer: (target, attachments_ptr, attachments_len) => { + this.assertWebGL2(); + let attachments = this.mem.loadU32Array(attachments_ptr, attachments_len); + this.ctx.invalidateFramebuffer(target, attachments); + }, + InvalidateSubFramebuffer: (target, attachments_ptr, attachments_len, x, y, width, height) => { + this.assertWebGL2(); + let attachments = this.mem.loadU32Array(attachments_ptr, attachments_len); + this.ctx.invalidateSubFramebuffer(target, attachments, x, y, width, height); + }, + ReadBuffer: (src) => { + this.assertWebGL2(); + this.ctx.readBuffer(src); + }, + + /* Renderbuffer objects */ + RenderbufferStorageMultisample: (target, samples, internalformat, width, height) => { + this.assertWebGL2(); + this.ctx.renderbufferStorageMultisample(target, samples, internalformat, width, height); + }, + + /* Texture objects */ + + TexStorage3D: (target, levels, internalformat, width, height, depth) => { + this.assertWebGL2(); + this.ctx.texStorage3D(target, levels, internalformat, width, height, depth); + }, + TexImage3D: (target, level, internalformat, width, height, depth, border, format, type, size, data) => { + this.assertWebGL2(); + if (data) { + this.ctx.texImage3D(target, level, internalformat, width, height, depth, border, format, type, this.mem.loadBytes(data, size)); + } else { + this.ctx.texImage3D(target, level, internalformat, width, height, depth, border, format, type, null); + } + }, + TexSubImage3D: (target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, size, data) => { + this.assertWebGL2(); + this.ctx.texSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, this.mem.loadBytes(data, size)); + }, + CompressedTexImage3D: (target, level, internalformat, width, height, depth, border, imageSize, data) => { + this.assertWebGL2(); + if (data) { + this.ctx.compressedTexImage3D(target, level, internalformat, width, height, depth, border, this.mem.loadBytes(data, imageSize)); + } else { + this.ctx.compressedTexImage3D(target, level, internalformat, width, height, depth, border, null); + } + }, + CompressedTexSubImage3D: (target, level, xoffset, yoffset, zoffset, width, height, depth, format, imageSize, data) => { + this.assertWebGL2(); + if (data) { + this.ctx.compressedTexSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, this.mem.loadBytes(data, imageSize)); + } else { + this.ctx.compressedTexSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, null); + } + }, + + CopyTexSubImage3D: (target, level, xoffset, yoffset, zoffset, x, y, width, height) => { + this.assertWebGL2(); + this.ctx.copyTexSubImage3D(target, level, xoffset, yoffset, zoffset, x, y, width, height); + }, + + /* Programs and shaders */ + GetFragDataLocation: (program, name_ptr, name_len) => { + this.assertWebGL2(); + return this.ctx.getFragDataLocation(this.programs[program], this.mem.loadString(name_ptr, name_len)); + }, + + /* Uniforms */ + Uniform1ui: (location, v0) => { + this.assertWebGL2(); + this.ctx.uniform1ui(this.uniforms[location], v0); + }, + Uniform2ui: (location, v0, v1) => { + this.assertWebGL2(); + this.ctx.uniform2ui(this.uniforms[location], v0, v1); + }, + Uniform3ui: (location, v0, v1, v2) => { + this.assertWebGL2(); + this.ctx.uniform3ui(this.uniforms[location], v0, v1, v2); + }, + Uniform4ui: (location, v0, v1, v2, v3) => { + this.assertWebGL2(); + this.ctx.uniform4ui(this.uniforms[location], v0, v1, v2, v3); + }, + + UniformMatrix3x2fv: (location, addr) => { + this.assertWebGL2(); + let array = this.mem.loadF32Array(addr, 3*2); + this.ctx.uniformMatrix3x2fv(this.uniforms[location], false, array); + }, + UniformMatrix4x2fv: (location, addr) => { + this.assertWebGL2(); + let array = this.mem.loadF32Array(addr, 4*2); + this.ctx.uniformMatrix4x2fv(this.uniforms[location], false, array); + }, + UniformMatrix2x3fv: (location, addr) => { + this.assertWebGL2(); + let array = this.mem.loadF32Array(addr, 2*3); + this.ctx.uniformMatrix2x3fv(this.uniforms[location], false, array); + }, + UniformMatrix4x3fv: (location, addr) => { + this.assertWebGL2(); + let array = this.mem.loadF32Array(addr, 4*3); + this.ctx.uniformMatrix4x3fv(this.uniforms[location], false, array); + }, + UniformMatrix2x4fv: (location, addr) => { + this.assertWebGL2(); + let array = this.mem.loadF32Array(addr, 2*4); + this.ctx.uniformMatrix2x4fv(this.uniforms[location], false, array); + }, + UniformMatrix3x4fv: (location, addr) => { + this.assertWebGL2(); + let array = this.mem.loadF32Array(addr, 3*4); + this.ctx.uniformMatrix3x4fv(this.uniforms[location], false, array); + }, + + /* Vertex attribs */ + VertexAttribI4i: (index, x, y, z, w) => { + this.assertWebGL2(); + this.ctx.vertexAttribI4i(index, x, y, z, w); + }, + VertexAttribI4ui: (index, x, y, z, w) => { + this.assertWebGL2(); + this.ctx.vertexAttribI4ui(index, x, y, z, w); + }, + VertexAttribIPointer: (index, size, type, stride, offset) => { + this.assertWebGL2(); + this.ctx.vertexAttribIPointer(index, size, type, stride, offset); + }, + + /* Writing to the drawing buffer */ + VertexAttribDivisor: (index, divisor) => { + this.assertWebGL2(); + this.ctx.vertexAttribDivisor(index, divisor); + }, + DrawArraysInstanced: (mode, first, count, instanceCount) => { + this.assertWebGL2(); + this.ctx.drawArraysInstanced(mode, first, count, instanceCount); + }, + DrawElementsInstanced: (mode, count, type, offset, instanceCount) => { + this.assertWebGL2(); + this.ctx.drawElementsInstanced(mode, count, type, offset, instanceCount); + }, + DrawRangeElements: (mode, start, end, count, type, offset) => { + this.assertWebGL2(); + this.ctx.drawRangeElements(mode, start, end, count, type, offset); + }, + + /* Multiple Render Targets */ + DrawBuffers: (buffers_ptr, buffers_len) => { + this.assertWebGL2(); + let array = this.mem.loadU32Array(buffers_ptr, buffers_len); + this.ctx.drawBuffers(array); + }, + ClearBufferfv: (buffer, drawbuffer, values_ptr, values_len) => { + this.assertWebGL2(); + let array = this.mem.loadF32Array(values_ptr, values_len); + this.ctx.clearBufferfv(buffer, drawbuffer, array); + }, + ClearBufferiv: (buffer, drawbuffer, values_ptr, values_len) => { + this.assertWebGL2(); + let array = this.mem.loadI32Array(values_ptr, values_len); + this.ctx.clearBufferiv(buffer, drawbuffer, array); + }, + ClearBufferuiv: (buffer, drawbuffer, values_ptr, values_len) => { + this.assertWebGL2(); + let array = this.mem.loadU32Array(values_ptr, values_len); + this.ctx.clearBufferuiv(buffer, drawbuffer, array); + }, + ClearBufferfi: (buffer, drawbuffer, depth, stencil) => { + this.assertWebGL2(); + this.ctx.clearBufferfi(buffer, drawbuffer, depth, stencil); + }, + + /* Query Objects */ + CreateQuery: () => { + this.assertWebGL2(); + let query = this.ctx.createQuery(); + let id = this.getNewId(this.queries); + query.name = id; + this.queries[id] = query; + return id; + }, + DeleteQuery: (id) => { + this.assertWebGL2(); + let obj = this.queries[id]; + if (obj && id != 0) { + this.ctx.deleteQuery(obj); + this.queries[id] = null; + } + }, + IsQuery: (query) => { + this.assertWebGL2(); + return this.ctx.isQuery(this.queries[query]); + }, + BeginQuery: (target, query) => { + this.assertWebGL2(); + this.ctx.beginQuery(target, this.queries[query]) + }, + EndQuery: (target) => { + this.assertWebGL2(); + this.ctx.endQuery(target); + }, + GetQuery: (target, pname) => { + this.assertWebGL2(); + let query = this.ctx.getQuery(target, pname); + if (!query) { + return 0; + } + if (this.queries.indexOf(query) !== -1) { + return query.name; + } + let id = this.getNewId(this.queries); + query.name = id; + this.queries[id] = query; + return id; + }, + + /* Sampler Objects */ + CreateSampler: () => { + this.assertWebGL2(); + let sampler = this.ctx.createSampler(); + let id = this.getNewId(this.samplers); + sampler.name = id; + this.samplers[id] = sampler; + return id; + }, + DeleteSampler: (id) => { + this.assertWebGL2(); + let obj = this.samplers[id]; + if (obj && id != 0) { + this.ctx.deleteSampler(obj); + this.samplers[id] = null; + } + }, + IsSampler: (sampler) => { + this.assertWebGL2(); + return this.ctx.isSampler(this.samplers[sampler]); + }, + BindSampler: (unit, sampler) => { + this.assertWebGL2(); + this.ctx.bindSampler(unit, this.samplers[sampler]); + }, + SamplerParameteri: (sampler, pname, param) => { + this.assertWebGL2(); + this.ctx.samplerParameteri(this.samplers[sampler], pname, param); + }, + SamplerParameterf: (sampler, pname, param) => { + this.assertWebGL2(); + this.ctx.samplerParameterf(this.samplers[sampler], pname, param); + }, + + /* Sync objects */ + FenceSync: (condition, flags) => { + this.assertWebGL2(); + let sync = this.ctx.fenceSync(condition, flags); + let id = this.getNewId(this.syncs); + sync.name = id; + this.syncs[id] = sync; + return id; + }, + IsSync: (sync) => { + this.assertWebGL2(); + return this.ctx.isSync(this.syncs[sync]); + }, + DeleteSync: (id) => { + this.assertWebGL2(); + let obj = this.syncs[id]; + if (obj && id != 0) { + this.ctx.deleteSampler(obj); + this.syncs[id] = null; + } + }, + ClientWaitSync: (sync, flags, timeout) => { + this.assertWebGL2(); + return this.ctx.clientWaitSync(this.syncs[sync], flags, timeout); + }, + WaitSync: (sync, flags, timeout) => { + this.assertWebGL2(); + this.ctx.waitSync(this.syncs[sync], flags, timeout) ; + }, + + + /* Transform Feedback */ + CreateTransformFeedback: () => { + this.assertWebGL2(); + let transformFeedback = this.ctx.createTransformFeedback(); + let id = this.getNewId(this.transformFeedbacks); + transformFeedback.name = id; + this.transformFeedbacks[id] = transformFeedback; + return id; + }, + DeleteTransformFeedback: (id) => { + this.assertWebGL2(); + let obj = this.transformFeedbacks[id]; + if (obj && id != 0) { + this.ctx.deleteTransformFeedback(obj); + this.transformFeedbacks[id] = null; + } + }, + IsTransformFeedback: (tf) => { + this.assertWebGL2(); + return this.ctx.isTransformFeedback(this.transformFeedbacks[tf]); + }, + BindTransformFeedback: (target, tf) => { + this.assertWebGL2(); + this.ctx.bindTransformFeedback(target, this.transformFeedbacks[tf]); + }, + BeginTransformFeedback: (primitiveMode) => { + this.assertWebGL2(); + this.ctx.beginTransformFeedback(primitiveMode); + }, + EndTransformFeedback: () => { + this.assertWebGL2(); + this.ctx.endTransformFeedback(); + }, + TransformFeedbackVaryings: (program, varyings_ptr, varyings_len, bufferMode) => { + this.assertWebGL2(); + const stringSize = this.mem.intSize*2; + let varyings = []; + for (let i = 0; i < varyings_len; i++) { + let ptr = this.mem.loadPtr(varyings_ptr + i*stringSize + 0*4); + let len = this.mem.loadPtr(varyings_ptr + i*stringSize + 1*4); + varyings.push(this.mem.loadString(ptr, len)); + } + this.ctx.transformFeedbackVaryings(this.programs[program], varyings, bufferMode); + }, + PauseTransformFeedback: () => { + this.assertWebGL2(); + this.ctx.pauseTransformFeedback(); + }, + ResumeTransformFeedback: () => { + this.assertWebGL2(); + this.ctx.resumeTransformFeedback(); + }, + + + /* Uniform Buffer Objects and Transform Feedback Buffers */ + BindBufferBase: (target, index, buffer) => { + this.assertWebGL2(); + this.ctx.bindBufferBase(target, index, this.buffers[buffer]); + }, + BindBufferRange: (target, index, buffer, offset, size) => { + this.assertWebGL2(); + this.ctx.bindBufferRange(target, index, this.buffers[buffer], offset, size); + }, + GetUniformBlockIndex: (program, uniformBlockName_ptr, uniformBlockName_len) => { + this.assertWebGL2(); + return this.ctx.getUniformBlockIndex(this.programs[program], this.mem.loadString(uniformBlockName_ptr, uniformBlockName_len)); + }, + // any getActiveUniformBlockParameter(WebGLProgram program, GLuint uniformBlockIndex, GLenum pname); + GetActiveUniformBlockName: (program, uniformBlockIndex, buf_ptr, buf_len, length_ptr) => { + this.assertWebGL2(); + let name = this.ctx.getActiveUniformBlockName(this.programs[program], uniformBlockIndex); + + let n = Math.min(buf_len, name.length); + name = name.substring(0, n); + this.mem.loadBytes(buf_ptr, buf_len).set(new TextEncoder().encode(name)) + this.mem.storeInt(length_ptr, n); + }, + UniformBlockBinding: (program, uniformBlockIndex, uniformBlockBinding) => { + this.assertWebGL2(); + this.ctx.uniformBlockBinding(this.programs[program], uniformBlockIndex, uniformBlockBinding); + }, + + /* Vertex Array Objects */ + CreateVertexArray: () => { + this.assertWebGL2(); + let vao = this.ctx.createVertexArray(); + let id = this.getNewId(this.vaos); + vao.name = id; + this.vaos[id] = vao; + return id; + }, + DeleteVertexArray: (id) => { + this.assertWebGL2(); + let obj = this.vaos[id]; + if (obj && id != 0) { + this.ctx.deleteVertexArray(obj); + this.vaos[id] = null; + } + }, + IsVertexArray: (vertexArray) => { + this.assertWebGL2(); + return this.ctx.isVertexArray(this.vaos[vertexArray]); + }, + BindVertexArray: (vertexArray) => { + this.assertWebGL2(); + this.ctx.bindVertexArray(this.vaos[vertexArray]); + }, + }; + } +}; + + +function odinSetupDefaultImports(wasmMemoryInterface, consoleElement, memory, eventQueue, event_temp) { + const MAX_INFO_CONSOLE_LINES = 512; + let infoConsoleLines = new Array(); + let currentLine = {}; + currentLine[false] = ""; + currentLine[true] = ""; + let prevIsError = false; + + const writeToConsole = (line, isError) => { + if (!line) { + return; + } + + const println = (text, forceIsError) => { + let style = [ + "color: #eee", + "background-color: #d20", + "padding: 2px 4px", + "border-radius: 2px", + ].join(";"); + let doIsError = isError; + if (forceIsError !== undefined) { + doIsError = forceIsError; + } + + if (doIsError) { + console.log("%c"+text, style); + } else { + console.log(text); + } + + }; + + // Print to console + if (line == "\n") { + println(currentLine[isError]); + currentLine[isError] = ""; + } else if (!line.includes("\n")) { + currentLine[isError] = currentLine[isError].concat(line); + } else { + let lines = line.split("\n"); + let printLast = lines.length > 1 && line.endsWith("\n"); + println(currentLine[isError].concat(lines[0])); + currentLine[isError] = ""; + for (let i = 1; i < lines.length-1; i++) { + println(lines[i]); + } + let last = lines[lines.length-1]; + if (printLast) { + println(last); + } else { + currentLine[isError] = last; + } + } + + if (prevIsError != isError) { + if (prevIsError) { + println(currentLine[prevIsError], prevIsError); + currentLine[prevIsError] = ""; + } + } + prevIsError = isError; + + + // HTML based console + if (!consoleElement) { + return; + } + const wrap = (x) => { + if (isError) { + return '<span style="color:#f21">'+x+'</span>'; + } + return x; + }; + + if (line == "\n") { + infoConsoleLines.push(line); + } else if (!line.includes("\n")) { + let prevLine = ""; + if (infoConsoleLines.length > 0) { + prevLine = infoConsoleLines.pop(); + } + infoConsoleLines.push(prevLine.concat(wrap(line))); + } else { + let lines = line.split("\n"); + let lastHasNewline = lines.length > 1 && line.endsWith("\n"); + + let prevLine = ""; + if (infoConsoleLines.length > 0) { + prevLine = infoConsoleLines.pop(); + } + infoConsoleLines.push(prevLine.concat(wrap(lines[0]).concat("\n"))); + + for (let i = 1; i < lines.length-1; i++) { + infoConsoleLines.push(wrap(lines[i]).concat("\n")); + } + let last = lines[lines.length-1]; + if (lastHasNewline) { + infoConsoleLines.push(last.concat("\n")); + } else { + infoConsoleLines.push(last); + } + } + + if (infoConsoleLines.length > MAX_INFO_CONSOLE_LINES) { + infoConsoleLines.shift(MAX_INFO_CONSOLE_LINES); + } + + let data = ""; + for (let i = 0; i < infoConsoleLines.length; i++) { + data = data.concat(infoConsoleLines[i]); + } + + let info = consoleElement; + info.innerHTML = data; + info.scrollTop = info.scrollHeight; + }; + + let webglContext = new WebGLInterface(wasmMemoryInterface); + + const env = {}; + + if (memory) { + env.memory = memory; + } + + return { + env, + "odin_env": { + write: (fd, ptr, len) => { + const str = wasmMemoryInterface.loadString(ptr, len); + if (fd == 1) { + writeToConsole(str, false); + return; + } else if (fd == 2) { + writeToConsole(str, true); + return; + } else { + throw new Error("Invalid fd to 'write'" + stripNewline(str)); + } + }, + trap: () => { throw new Error() }, + alert: (ptr, len) => { alert(wasmMemoryInterface.loadString(ptr, len)) }, + abort: () => { Module.abort() }, + evaluate: (str_ptr, str_len) => { eval.call(null, wasmMemoryInterface.loadString(str_ptr, str_len)); }, + + // return a bigint to be converted to i64 + time_now: () => BigInt(Date.now()), + tick_now: () => performance.now(), + time_sleep: (duration_ms) => { + if (duration_ms > 0) { + // TODO(bill): Does this even make any sense? + } + }, + + sqrt: Math.sqrt, + sin: Math.sin, + cos: Math.cos, + pow: Math.pow, + fmuladd: (x, y, z) => x*y + z, + ln: Math.log, + exp: Math.exp, + ldexp: (x, exp) => x * Math.pow(2, exp), + + rand_bytes: (ptr, len) => { + const view = new Uint8Array(wasmMemoryInterface.memory.buffer, ptr, len) + crypto.getRandomValues(view) + }, + }, + "odin_dom": { + init_event_raw: (ep) => { + const W = wasmMemoryInterface.intSize; + let offset = ep; + let off = (amount, alignment) => { + if (alignment === undefined) { + alignment = Math.min(amount, W); + } + if (offset % alignment != 0) { + offset += alignment - (offset%alignment); + } + let x = offset; + offset += amount; + return x; + }; + + let align = (alignment) => { + const modulo = offset & (alignment-1); + if (modulo != 0) { + offset += alignment - modulo + } + }; + + let wmi = wasmMemoryInterface; + + if (!event_temp.data) { + return; + } + + let e = event_temp.data.event; + + wmi.storeU32(off(4), event_temp.data.name_code); + if (e.target == document) { + wmi.storeU32(off(4), 1); + } else if (e.target == window) { + wmi.storeU32(off(4), 2); + } else { + wmi.storeU32(off(4), 0); + } + if (e.currentTarget == document) { + wmi.storeU32(off(4), 1); + } else if (e.currentTarget == window) { + wmi.storeU32(off(4), 2); + } else { + wmi.storeU32(off(4), 0); + } + + align(W); + + wmi.storeI32(off(W), event_temp.data.id_ptr); + wmi.storeUint(off(W), event_temp.data.id_len); + + align(8); + wmi.storeF64(off(8), e.timeStamp*1e-3); + + wmi.storeU8(off(1), e.eventPhase); + let options = 0; + if (!!e.bubbles) { options |= 1<<0; } + if (!!e.cancelable) { options |= 1<<1; } + if (!!e.composed) { options |= 1<<2; } + wmi.storeU8(off(1), options); + wmi.storeU8(off(1), !!e.isComposing); + wmi.storeU8(off(1), !!e.isTrusted); + + align(8); + if (e instanceof WheelEvent) { + wmi.storeF64(off(8), e.deltaX); + wmi.storeF64(off(8), e.deltaY); + wmi.storeF64(off(8), e.deltaZ); + wmi.storeU32(off(4), e.deltaMode); + } else if (e instanceof MouseEvent) { + wmi.storeI64(off(8), e.screenX); + wmi.storeI64(off(8), e.screenY); + wmi.storeI64(off(8), e.clientX); + wmi.storeI64(off(8), e.clientY); + wmi.storeI64(off(8), e.offsetX); + wmi.storeI64(off(8), e.offsetY); + wmi.storeI64(off(8), e.pageX); + wmi.storeI64(off(8), e.pageY); + wmi.storeI64(off(8), e.movementX); + wmi.storeI64(off(8), e.movementY); + + wmi.storeU8(off(1), !!e.ctrlKey); + wmi.storeU8(off(1), !!e.shiftKey); + wmi.storeU8(off(1), !!e.altKey); + wmi.storeU8(off(1), !!e.metaKey); + + wmi.storeI16(off(2), e.button); + wmi.storeU16(off(2), e.buttons); + } else if (e instanceof KeyboardEvent) { + // Note: those strings are constructed + // on the native side from buffers that + // are filled later, so skip them + const keyPtr = off(W*2, W); + const codePtr = off(W*2, W); + + wmi.storeU8(off(1), e.location); + + wmi.storeU8(off(1), !!e.ctrlKey); + wmi.storeU8(off(1), !!e.shiftKey); + wmi.storeU8(off(1), !!e.altKey); + wmi.storeU8(off(1), !!e.metaKey); + + wmi.storeU8(off(1), !!e.repeat); + + wmi.storeInt(off(W, W), e.key.length) + wmi.storeInt(off(W, W), e.code.length) + wmi.storeString(off(16, 1), e.key); + wmi.storeString(off(16, 1), e.code); + } else if (e.type === 'scroll') { + wmi.storeF64(off(8, 8), window.scrollX); + wmi.storeF64(off(8, 8), window.scrollY); + } else if (e.type === 'visibilitychange') { + wmi.storeU8(off(1), !document.hidden); + } else if (e instanceof GamepadEvent) { + const idPtr = off(W*2, W); + const mappingPtr = off(W*2, W); + + wmi.storeI32(off(W, W), e.gamepad.index); + wmi.storeU8(off(1), !!e.gamepad.connected); + wmi.storeF64(off(8, 8), e.gamepad.timestamp); + + wmi.storeInt(off(W, W), e.gamepad.buttons.length); + wmi.storeInt(off(W, W), e.gamepad.axes.length); + + for (let i = 0; i < 64; i++) { + if (i < e.gamepad.buttons.length) { + let b = e.gamepad.buttons[i]; + wmi.storeF64(off(8, 8), b.value); + wmi.storeU8(off(1), !!b.pressed); + wmi.storeU8(off(1), !!b.touched); + } else { + off(16, 8); + } + } + for (let i = 0; i < 16; i++) { + if (i < e.gamepad.axes.length) { + let a = e.gamepad.axes[i]; + wmi.storeF64(off(8, 8), a); + } else { + off(8, 8); + } + } + + wmi.storeInt(off(W, W), e.gamepad.id.length) + wmi.storeInt(off(W, W), e.gamepad.mapping.length) + wmi.storeString(off(64, 1), e.gamepad.id); + wmi.storeString(off(64, 1), e.gamepad.mapping); + } + }, + + add_event_listener: (id_ptr, id_len, name_ptr, name_len, name_code, data, callback, use_capture) => { + let id = wasmMemoryInterface.loadString(id_ptr, id_len); + let name = wasmMemoryInterface.loadString(name_ptr, name_len); + let element = getElement(id); + if (element == undefined) { + return false; + } + + let listener = (e) => { + let event_data = {}; + event_data.id_ptr = id_ptr; + event_data.id_len = id_len; + event_data.event = e; + event_data.name_code = name_code; + + eventQueue.push({event_data: event_data, data: data, callback: callback}); + }; + wasmMemoryInterface.listenerMap[{data: data, callback: callback}] = listener; + element.addEventListener(name, listener, !!use_capture); + return true; + }, + + add_window_event_listener: (name_ptr, name_len, name_code, data, callback, use_capture) => { + let name = wasmMemoryInterface.loadString(name_ptr, name_len); + let element = window; + let listener = (e) => { + let event_data = {}; + event_data.id_ptr = 0; + event_data.id_len = 0; + event_data.event = e; + event_data.name_code = name_code; + + eventQueue.push({event_data: event_data, data: data, callback: callback}); + }; + wasmMemoryInterface.listenerMap[{data: data, callback: callback}] = listener; + element.addEventListener(name, listener, !!use_capture); + return true; + }, + + remove_event_listener: (id_ptr, id_len, name_ptr, name_len, data, callback) => { + let id = wasmMemoryInterface.loadString(id_ptr, id_len); + let name = wasmMemoryInterface.loadString(name_ptr, name_len); + let element = getElement(id); + if (element == undefined) { + return false; + } + + let listener = wasmMemoryInterface.listenerMap[{data: data, callback: callback}]; + if (listener == undefined) { + return false; + } + element.removeEventListener(name, listener); + return true; + }, + remove_window_event_listener: (name_ptr, name_len, data, callback) => { + let name = wasmMemoryInterface.loadString(name_ptr, name_len); + let element = window; + let key = {data: data, callback: callback}; + let listener = wasmMemoryInterface.listenerMap[key]; + if (!listener) { + return false; + } + wasmMemoryInterface.listenerMap[key] = undefined; + + element.removeEventListener(name, listener); + return true; + }, + + event_stop_propagation: () => { + if (event_temp.data && event_temp.data.event) { + event_temp.data.event.stopPropagation(); + } + }, + event_stop_immediate_propagation: () => { + if (event_temp.data && event_temp.data.event) { + event_temp.data.event.stopImmediatePropagation(); + } + }, + event_prevent_default: () => { + if (event_temp.data && event_temp.data.event) { + event_temp.data.event.preventDefault(); + } + }, + + dispatch_custom_event: (id_ptr, id_len, name_ptr, name_len, options_bits) => { + let id = wasmMemoryInterface.loadString(id_ptr, id_len); + let name = wasmMemoryInterface.loadString(name_ptr, name_len); + let options = { + bubbles: (options_bits & (1<<0)) !== 0, + cancelable: (options_bits & (1<<1)) !== 0, + composed: (options_bits & (1<<2)) !== 0, + }; + + let element = getElement(id); + if (element) { + element.dispatchEvent(new Event(name, options)); + return true; + } + return false; + }, + + get_gamepad_state: (gamepad_id, ep) => { + let index = gamepad_id; + let gps = navigator.getGamepads(); + if (0 <= index && index < gps.length) { + let gamepad = gps[index]; + if (!gamepad) { + return false; + } + + const W = wasmMemoryInterface.intSize; + let offset = ep; + let off = (amount, alignment) => { + if (alignment === undefined) { + alignment = Math.min(amount, W); + } + if (offset % alignment != 0) { + offset += alignment - (offset%alignment); + } + let x = offset; + offset += amount; + return x; + }; + + let align = (alignment) => { + const modulo = offset & (alignment-1); + if (modulo != 0) { + offset += alignment - modulo + } + }; + + let wmi = wasmMemoryInterface; + + const idPtr = off(W*2, W); + const mappingPtr = off(W*2, W); + + wmi.storeI32(off(W), gamepad.index); + wmi.storeU8(off(1), !!gamepad.connected); + wmi.storeF64(off(8), gamepad.timestamp); + + wmi.storeInt(off(W), gamepad.buttons.length); + wmi.storeInt(off(W), gamepad.axes.length); + + for (let i = 0; i < 64; i++) { + if (i < gamepad.buttons.length) { + let b = gamepad.buttons[i]; + wmi.storeF64(off(8, 8), b.value); + wmi.storeU8(off(1), !!b.pressed); + wmi.storeU8(off(1), !!b.touched); + } else { + off(16, 8); + } + } + for (let i = 0; i < 16; i++) { + if (i < gamepad.axes.length) { + wmi.storeF64(off(8, 8), gamepad.axes[i]); + } else { + off(8, 8); + } + } + + wmi.storeInt(off(W, W), gamepad.id.length) + wmi.storeInt(off(W, W), gamepad.mapping.length) + wmi.storeString(off(64, 1), gamepad.id); + wmi.storeString(off(64, 1), gamepad.mapping); + + return true; + } + return false; + }, + + get_element_value_f64: (id_ptr, id_len) => { + let id = wasmMemoryInterface.loadString(id_ptr, id_len); + let element = getElement(id); + return element ? element.value : 0; + }, + get_element_value_string: (id_ptr, id_len, buf_ptr, buf_len) => { + let id = wasmMemoryInterface.loadString(id_ptr, id_len); + let element = getElement(id); + if (element) { + let str = element.value; + if (buf_len > 0 && buf_ptr) { + let n = Math.min(buf_len, str.length); + str = str.substring(0, n); + this.mem.loadBytes(buf_ptr, buf_len).set(new TextEncoder().encode(str)) + return n; + } + } + return 0; + }, + get_element_value_string_length: (id_ptr, id_len) => { + let id = wasmMemoryInterface.loadString(id_ptr, id_len); + let element = getElement(id); + if (element) { + return element.value.length; + } + return 0; + }, + get_element_min_max: (ptr_array2_f64, id_ptr, id_len) => { + let id = wasmMemoryInterface.loadString(id_ptr, id_len); + let element = getElement(id); + if (element) { + let values = wasmMemoryInterface.loadF64Array(ptr_array2_f64, 2); + values[0] = element.min; + values[1] = element.max; + } + }, + set_element_value_f64: (id_ptr, id_len, value) => { + let id = wasmMemoryInterface.loadString(id_ptr, id_len); + let element = getElement(id); + if (element) { + element.value = value; + } + }, + set_element_value_string: (id_ptr, id_len, value_ptr, value_len) => { + let id = wasmMemoryInterface.loadString(id_ptr, id_len); + let value = wasmMemoryInterface.loadString(value_ptr, value_len); + let element = getElement(id); + if (element) { + element.value = value; + } + }, + + get_element_key_f64: (id_ptr, id_len, key_ptr, key_len) => { + let id = wasmMemoryInterface.loadString(id_ptr, id_len); + let key = wasmMemoryInterface.loadString(key_ptr, key_len); + let element = getElement(id); + return element ? element[key] : 0; + }, + get_element_key_string: (id_ptr, id_len, key_ptr, key_len, buf_ptr, buf_len) => { + let id = wasmMemoryInterface.loadString(id_ptr, id_len); + let key = wasmMemoryInterface.loadString(key_ptr, key_len); + let element = getElement(id); + if (element) { + let str = element[key]; + if (buf_len > 0 && buf_ptr) { + let n = Math.min(buf_len, str.length); + str = str.substring(0, n); + this.mem.loadBytes(buf_ptr, buf_len).set(new TextEncoder().encode(str)) + return n; + } + } + return 0; + }, + get_element_key_string_length: (id_ptr, id_len, key_ptr, key_len) => { + let id = wasmMemoryInterface.loadString(id_ptr, id_len); + let key = wasmMemoryInterface.loadString(key_ptr, key_len); + let element = getElement(id); + if (element && element[key]) { + return element[key].length; + } + return 0; + }, + + set_element_key_f64: (id_ptr, id_len, key_ptr, key_len, value) => { + let id = wasmMemoryInterface.loadString(id_ptr, id_len); + let key = wasmMemoryInterface.loadString(key_ptr, key_len); + let element = getElement(id); + if (element) { + element[key] = value; + } + }, + set_element_key_string: (id_ptr, id_len, key_ptr, key_len, value_ptr, value_len) => { + let id = wasmMemoryInterface.loadString(id_ptr, id_len); + let key = wasmMemoryInterface.loadString(key_ptr, key_len); + let value = wasmMemoryInterface.loadString(value_ptr, value_len); + let element = getElement(id); + if (element) { + element[key] = value; + } + }, + + + get_bounding_client_rect: (rect_ptr, id_ptr, id_len) => { + let id = wasmMemoryInterface.loadString(id_ptr, id_len); + let element = getElement(id); + if (element) { + let values = wasmMemoryInterface.loadF64Array(rect_ptr, 4); + let rect = element.getBoundingClientRect(); + values[0] = rect.left; + values[1] = rect.top; + values[2] = rect.right - rect.left; + values[3] = rect.bottom - rect.top; + } + }, + window_get_rect: (rect_ptr) => { + let values = wasmMemoryInterface.loadF64Array(rect_ptr, 4); + values[0] = window.screenX; + values[1] = window.screenY; + values[2] = window.screen.width; + values[3] = window.screen.height; + }, + + window_get_scroll: (pos_ptr) => { + let values = wasmMemoryInterface.loadF64Array(pos_ptr, 2); + values[0] = window.scrollX; + values[1] = window.scrollY; + }, + window_set_scroll: (x, y) => { + window.scroll(x, y); + }, + + device_pixel_ratio: () => { + return window.devicePixelRatio; + }, + + }, + + "webgl": webglContext.getWebGL1Interface(), + "webgl2": webglContext.getWebGL2Interface(), + }; +}; + +/** + * @param {string} wasmPath - Path to the WASM module to run + * @param {?HTMLPreElement} consoleElement - Optional console/pre element to append output to, in addition to the console + * @param {any} extraForeignImports - Imports, in addition to the default runtime to provide the module + * @param {?WasmMemoryInterface} wasmMemoryInterface - Optional memory to use instead of the defaults + * @param {?int} intSize - Size (in bytes) of the integer type, should be 4 on `js_wasm32` and 8 on `js_wasm64p32` + */ +async function runWasm(wasmPath, consoleElement, extraForeignImports, wasmMemoryInterface, intSize = 4) { + if (!wasmMemoryInterface) { + wasmMemoryInterface = new WasmMemoryInterface(); + } + wasmMemoryInterface.setIntSize(intSize); + + let eventQueue = new Array(); + let event_temp = {}; + + let imports = odinSetupDefaultImports(wasmMemoryInterface, consoleElement, wasmMemoryInterface.memory, eventQueue, event_temp); + let exports = {}; + + if (extraForeignImports !== undefined) { + imports = { + ...imports, + ...extraForeignImports, + }; + } + + const response = await fetch(wasmPath); + const file = await response.arrayBuffer(); + const wasm = await WebAssembly.instantiate(file, imports); + exports = wasm.instance.exports; + wasmMemoryInterface.setExports(exports); + + if (exports.memory) { + if (wasmMemoryInterface.memory) { + console.warn("WASM module exports memory, but `runWasm` was given an interface with existing memory too"); + } + wasmMemoryInterface.setMemory(exports.memory); + } + + exports._start(); + + // Define a `@export step :: proc(current_type: f64) -> (keep_going: bool) {` + // in your app and it will get called every frame. + // return `false` to stop the execution of the module. + if (exports.step) { + const odin_ctx = exports.default_context_ptr(); + + let prevTimeStamp = undefined; + function step(currTimeStamp) { + if (prevTimeStamp == undefined) { + prevTimeStamp = currTimeStamp; + } + + const dt = (currTimeStamp - prevTimeStamp)*0.001; + prevTimeStamp = currTimeStamp; + + while (eventQueue.length > 0) { + let e = eventQueue.shift() + event_temp.data = e.event_data; + exports.odin_dom_do_event_callback(e.data, e.callback, odin_ctx); + } + event_temp.data = null; + + if (!exports.step(currTimeStamp*0.001, odin_ctx)) { + exports._end(); + return; + } + + window.requestAnimationFrame(step); + } + + window.requestAnimationFrame(step); + } else { + exports._end(); + } + + return; +}; + +window.odin = { + // Interface Types + WasmMemoryInterface: WasmMemoryInterface, + WebGLInterface: WebGLInterface, + + // Functions + setupDefaultImports: odinSetupDefaultImports, + runWasm: runWasm, +}; +})(); diff --git a/core/sys/wasm/wasi/wasi_api.odin b/core/sys/wasm/wasi/wasi_api.odin index 38d95e754..8d50f1690 100644 --- a/core/sys/wasm/wasi/wasi_api.odin +++ b/core/sys/wasm/wasi/wasi_api.odin @@ -1,4 +1,4 @@ -//+build wasm32 +#+build wasm32 package sys_wasi foreign import wasi "wasi_snapshot_preview1" diff --git a/core/sys/windows/advapi32.odin b/core/sys/windows/advapi32.odin index 1e34f1fd6..f834511d4 100644 --- a/core/sys/windows/advapi32.odin +++ b/core/sys/windows/advapi32.odin @@ -1,4 +1,4 @@ -// +build windows +#+build windows package sys_windows foreign import advapi32 "system:Advapi32.lib" diff --git a/core/sys/windows/bcrypt.odin b/core/sys/windows/bcrypt.odin index d891aa92b..f15f1e305 100644 --- a/core/sys/windows/bcrypt.odin +++ b/core/sys/windows/bcrypt.odin @@ -1,4 +1,4 @@ -// +build windows +#+build windows package sys_windows foreign import bcrypt "system:Bcrypt.lib" diff --git a/core/sys/windows/bluetooth.odin b/core/sys/windows/bluetooth.odin index 7bfb7ea96..86c66b9a1 100644 --- a/core/sys/windows/bluetooth.odin +++ b/core/sys/windows/bluetooth.odin @@ -1,4 +1,4 @@ -// +build windows +#+build windows package sys_windows foreign import "system:bthprops.lib" diff --git a/core/sys/windows/codepage.odin b/core/sys/windows/codepage.odin index 90040f1ee..527289f03 100644 --- a/core/sys/windows/codepage.odin +++ b/core/sys/windows/codepage.odin @@ -1,4 +1,4 @@ -// +build windows +#+build windows package sys_windows // https://learn.microsoft.com/en-us/windows/win32/intl/code-page-identifiers diff --git a/core/sys/windows/comctl32.odin b/core/sys/windows/comctl32.odin index 9c4404a9d..477800413 100644 --- a/core/sys/windows/comctl32.odin +++ b/core/sys/windows/comctl32.odin @@ -1,4 +1,4 @@ -// +build windows +#+build windows package sys_windows foreign import "system:Comctl32.lib" diff --git a/core/sys/windows/comdlg32.odin b/core/sys/windows/comdlg32.odin index 30d9b169c..a9800b47a 100644 --- a/core/sys/windows/comdlg32.odin +++ b/core/sys/windows/comdlg32.odin @@ -1,4 +1,4 @@ -// +build windows +#+build windows package sys_windows foreign import "system:Comdlg32.lib" diff --git a/core/sys/windows/dbghelp.odin b/core/sys/windows/dbghelp.odin index cb5458248..336992b4a 100644 --- a/core/sys/windows/dbghelp.odin +++ b/core/sys/windows/dbghelp.odin @@ -1,4 +1,4 @@ -// +build windows +#+build windows package sys_windows foreign import "system:Dbghelp.lib" diff --git a/core/sys/windows/dnsapi.odin b/core/sys/windows/dnsapi.odin index dd2d1acee..4fd9f7a19 100644 --- a/core/sys/windows/dnsapi.odin +++ b/core/sys/windows/dnsapi.odin @@ -1,4 +1,4 @@ -// +build windows +#+build windows package sys_windows foreign import "system:Dnsapi.lib" diff --git a/core/sys/windows/dwmapi.odin b/core/sys/windows/dwmapi.odin index 91911baae..11a46f53a 100644 --- a/core/sys/windows/dwmapi.odin +++ b/core/sys/windows/dwmapi.odin @@ -1,10 +1,10 @@ -// +build windows +#+build windows package sys_windows foreign import dwmapi "system:Dwmapi.lib" DWMWINDOWATTRIBUTE :: enum { - DWMWA_NCRENDERING_ENABLED, + DWMWA_NCRENDERING_ENABLED = 1, DWMWA_NCRENDERING_POLICY, DWMWA_TRANSITIONS_FORCEDISABLED, DWMWA_ALLOW_NCPAINT, @@ -28,7 +28,7 @@ DWMWINDOWATTRIBUTE :: enum { DWMWA_TEXT_COLOR, DWMWA_VISIBLE_FRAME_BORDER_THICKNESS, DWMWA_SYSTEMBACKDROP_TYPE, - DWMWA_LAST, + DWMWA_LAST, } DWMNCRENDERINGPOLICY :: enum { diff --git a/core/sys/windows/gdi32.odin b/core/sys/windows/gdi32.odin index 5cbafddba..1d7a93d85 100644 --- a/core/sys/windows/gdi32.odin +++ b/core/sys/windows/gdi32.odin @@ -1,4 +1,4 @@ -// +build windows +#+build windows package sys_windows import "core:math/fixed" diff --git a/core/sys/windows/hidpi.odin b/core/sys/windows/hidpi.odin index bea03694e..5e9787527 100644 --- a/core/sys/windows/hidpi.odin +++ b/core/sys/windows/hidpi.odin @@ -1,4 +1,4 @@ -// +build windows +#+build windows package sys_windows import "core:c" diff --git a/core/sys/windows/hidusage.odin b/core/sys/windows/hidusage.odin index a32aa7b9f..eb2a85f2e 100644 --- a/core/sys/windows/hidusage.odin +++ b/core/sys/windows/hidusage.odin @@ -1,4 +1,4 @@ -// +build windows +#+build windows package sys_windows USAGE :: distinct USHORT diff --git a/core/sys/windows/ip_helper.odin b/core/sys/windows/ip_helper.odin index 4c2534c10..7a6e545ac 100644 --- a/core/sys/windows/ip_helper.odin +++ b/core/sys/windows/ip_helper.odin @@ -1,4 +1,4 @@ -// +build windows +#+build windows package sys_windows foreign import "system:iphlpapi.lib" diff --git a/core/sys/windows/kernel32.odin b/core/sys/windows/kernel32.odin index 54dc11389..2771581e6 100644 --- a/core/sys/windows/kernel32.odin +++ b/core/sys/windows/kernel32.odin @@ -1,4 +1,4 @@ -// +build windows +#+build windows package sys_windows foreign import kernel32 "system:Kernel32.lib" @@ -400,6 +400,9 @@ foreign kernel32 { GlobalAlloc :: proc(flags: UINT, bytes: SIZE_T) -> LPVOID --- GlobalReAlloc :: proc(mem: LPVOID, bytes: SIZE_T, flags: UINT) -> LPVOID --- GlobalFree :: proc(mem: LPVOID) -> LPVOID --- + + GlobalLock :: proc(hMem: HGLOBAL) -> LPVOID --- + GlobalUnlock :: proc(hMem: HGLOBAL) -> BOOL --- ReadDirectoryChangesW :: proc( hDirectory: HANDLE, @@ -1175,17 +1178,17 @@ SYSTEM_POWER_STATUS :: struct { } AC_Line_Status :: enum BYTE { - Offline = 0, - Online = 1, - Unknown = 255, + Offline = 0, + Online = 1, + Unknown = 255, } Battery_Flag :: enum BYTE { - High = 0, - Low = 1, - Critical = 2, - Charging = 3, - No_Battery = 7, + High = 0, + Low = 1, + Critical = 2, + Charging = 3, + No_Battery = 7, } Battery_Flags :: bit_set[Battery_Flag; BYTE] diff --git a/core/sys/windows/key_codes.odin b/core/sys/windows/key_codes.odin index 284b0e437..0991ca4b3 100644 --- a/core/sys/windows/key_codes.odin +++ b/core/sys/windows/key_codes.odin @@ -1,4 +1,4 @@ -// +build windows +#+build windows package sys_windows // https://docs.microsoft.com/en-us/windows/win32/inputdev/about-keyboard-input diff --git a/core/sys/windows/known_folders.odin b/core/sys/windows/known_folders.odin index 439d65faf..cbaf5eeeb 100644 --- a/core/sys/windows/known_folders.odin +++ b/core/sys/windows/known_folders.odin @@ -1,4 +1,4 @@ -// +build windows +#+build windows package sys_windows FOLDERID_NetworkFolder :: GUID {0xD20BEEC4, 0x5CA8, 0x4905, {0xAE, 0x3B, 0xBF, 0x25, 0x1E, 0xA0, 0x9B, 0x53}} diff --git a/core/sys/windows/netapi32.odin b/core/sys/windows/netapi32.odin index d9f75c623..9442193ca 100644 --- a/core/sys/windows/netapi32.odin +++ b/core/sys/windows/netapi32.odin @@ -1,4 +1,4 @@ -// +build windows +#+build windows package sys_windows foreign import netapi32 "system:Netapi32.lib" diff --git a/core/sys/windows/ntdll.odin b/core/sys/windows/ntdll.odin index 23444ff34..747130749 100644 --- a/core/sys/windows/ntdll.odin +++ b/core/sys/windows/ntdll.odin @@ -1,4 +1,4 @@ -// +build windows +#+build windows package sys_windows foreign import ntdll_lib "system:ntdll.lib" diff --git a/core/sys/windows/shcore.odin b/core/sys/windows/shcore.odin index 54f67989e..08a76ebe6 100644 --- a/core/sys/windows/shcore.odin +++ b/core/sys/windows/shcore.odin @@ -1,4 +1,4 @@ -// +build windows +#+build windows package sys_windows diff --git a/core/sys/windows/shell32.odin b/core/sys/windows/shell32.odin index 7340ae4d4..54cee718c 100644 --- a/core/sys/windows/shell32.odin +++ b/core/sys/windows/shell32.odin @@ -1,4 +1,4 @@ -// +build windows +#+build windows package sys_windows foreign import shell32 "system:Shell32.lib" diff --git a/core/sys/windows/shlwapi.odin b/core/sys/windows/shlwapi.odin index bf9d2d1e8..095fff304 100644 --- a/core/sys/windows/shlwapi.odin +++ b/core/sys/windows/shlwapi.odin @@ -1,4 +1,4 @@ -// +build windows +#+build windows package sys_windows foreign import shlwapi "system:shlwapi.lib" diff --git a/core/sys/windows/synchronization.odin b/core/sys/windows/synchronization.odin index 79efaab34..bcaeb3f5f 100644 --- a/core/sys/windows/synchronization.odin +++ b/core/sys/windows/synchronization.odin @@ -1,4 +1,4 @@ -// +build windows +#+build windows package sys_windows foreign import Synchronization "system:Synchronization.lib" diff --git a/core/sys/windows/system_params.odin b/core/sys/windows/system_params.odin index e94d777bf..e463feb7e 100644 --- a/core/sys/windows/system_params.odin +++ b/core/sys/windows/system_params.odin @@ -1,4 +1,4 @@ -// +build windows +#+build windows package sys_windows // Parameter for SystemParametersInfo. diff --git a/core/sys/windows/tlhelp.odin b/core/sys/windows/tlhelp.odin index 45d5a3ff9..006c9c330 100644 --- a/core/sys/windows/tlhelp.odin +++ b/core/sys/windows/tlhelp.odin @@ -1,4 +1,4 @@ -//+build windows +#+build windows package sys_windows foreign import kernel32 "system:Kernel32.lib" diff --git a/core/sys/windows/types.odin b/core/sys/windows/types.odin index d9a6bd1fd..934d103e5 100644 --- a/core/sys/windows/types.odin +++ b/core/sys/windows/types.odin @@ -3546,11 +3546,11 @@ SIGDN :: enum c_int { } SIATTRIBFLAGS :: enum c_int { - AND = 0x1, - OR = 0x2, - APPCOMPAT = 0x3, - MASK = 0x3, - ALLITEMS = 0x4000, + AND = 0x1, + OR = 0x2, + APPCOMPAT = 0x3, + MASK = 0x3, + ALLITEMS = 0x4000, } FDAP :: enum c_int { @@ -4503,35 +4503,35 @@ DNS_INFO_NO_RECORDS :: 9501 DNS_QUERY_NO_RECURSION :: 0x00000004 DNS_RECORD :: struct { // aka DNS_RECORDA - pNext: ^DNS_RECORD, - pName: cstring, - wType: WORD, - wDataLength: USHORT, - Flags: DWORD, - dwTtl: DWORD, - _: DWORD, - Data: struct #raw_union { - CNAME: DNS_PTR_DATAA, - A: u32be, // Ipv4 Address - AAAA: u128be, // Ipv6 Address - TXT: DNS_TXT_DATAA, - NS: DNS_PTR_DATAA, - MX: DNS_MX_DATAA, - SRV: DNS_SRV_DATAA, - }, + pNext: ^DNS_RECORD, + pName: cstring, + wType: WORD, + wDataLength: USHORT, + Flags: DWORD, + dwTtl: DWORD, + _: DWORD, + Data: struct #raw_union { + CNAME: DNS_PTR_DATAA, + A: u32be, // Ipv4 Address + AAAA: u128be, // Ipv6 Address + TXT: DNS_TXT_DATAA, + NS: DNS_PTR_DATAA, + MX: DNS_MX_DATAA, + SRV: DNS_SRV_DATAA, + }, } DNS_TXT_DATAA :: struct { - dwStringCount: DWORD, - pStringArray: cstring, + dwStringCount: DWORD, + pStringArray: cstring, } DNS_PTR_DATAA :: cstring DNS_MX_DATAA :: struct { - pNameExchange: cstring, // the hostname - wPreference: WORD, // lower values preferred - _: WORD, // padding. + pNameExchange: cstring, // the hostname + wPreference: WORD, // lower values preferred + _: WORD, // padding. } DNS_SRV_DATAA :: struct { pNameTarget: cstring, diff --git a/core/sys/windows/user32.odin b/core/sys/windows/user32.odin index f1232c680..514592e7b 100644 --- a/core/sys/windows/user32.odin +++ b/core/sys/windows/user32.odin @@ -1,4 +1,4 @@ -// +build windows +#+build windows package sys_windows import "base:intrinsics" @@ -66,7 +66,7 @@ foreign user32 { RemovePropW :: proc(hWnd: HWND, lpString: LPCWSTR) -> HANDLE --- EnumPropsW :: proc(hWnd: HWND, lpEnumFunc: PROPENUMPROCW) -> INT --- EnumPropsExW :: proc(hWnd: HWND, lpEnumFunc: PROPENUMPROCW, lParam: LPARAM) -> INT --- - GetMessageW :: proc(lpMsg: ^MSG, hWnd: HWND, wMsgFilterMin: UINT, wMsgFilterMax: UINT) -> BOOL --- + GetMessageW :: proc(lpMsg: ^MSG, hWnd: HWND, wMsgFilterMin: UINT, wMsgFilterMax: UINT) -> INT --- TranslateMessage :: proc(lpMsg: ^MSG) -> BOOL --- DispatchMessageW :: proc(lpMsg: ^MSG) -> LRESULT --- @@ -142,7 +142,7 @@ foreign user32 { AppendMenuW :: proc(hMenu: HMENU, uFlags: UINT, uIDNewItem: UINT_PTR, lpNewItem: LPCWSTR) -> BOOL --- GetMenu :: proc(hWnd: HWND) -> HMENU --- SetMenu :: proc(hWnd: HWND, hMenu: HMENU) -> BOOL --- - TrackPopupMenu :: proc(hMenu: HMENU, uFlags: UINT, x, y: INT, nReserved: INT, hWnd: HWND, prcRect: ^RECT) -> BOOL --- + TrackPopupMenu :: proc(hMenu: HMENU, uFlags: UINT, x, y: INT, nReserved: INT, hWnd: HWND, prcRect: ^RECT) -> INT --- RegisterWindowMessageW :: proc(lpString: LPCWSTR) -> UINT --- CreateAcceleratorTableW :: proc(paccel: LPACCEL, cAccel: INT) -> HACCEL --- @@ -305,6 +305,13 @@ foreign user32 { GetProcessWindowStation :: proc() -> HWINSTA --- GetUserObjectInformationW :: proc(hObj: HANDLE, nIndex: GetUserObjectInformationFlags, pvInfo: PVOID, nLength: DWORD, lpnLengthNeeded: LPDWORD) -> BOOL --- + + OpenClipboard :: proc(hWndNewOwner: HWND) -> BOOL --- + CloseClipboard :: proc() -> BOOL --- + GetClipboardData :: proc(uFormat: UINT) -> HANDLE --- + SetClipboardData :: proc(uFormat: UINT, hMem: HANDLE) -> HANDLE --- + IsClipboardFormatAvailable :: proc(format: UINT) -> BOOL --- + EmptyClipboard :: proc() -> BOOL --- } CreateWindowW :: #force_inline proc "system" ( @@ -746,3 +753,31 @@ WinEventFlag :: enum DWORD { SKIPOWNPROCESS = 1, INCONTEXT = 2, } + +// Standard Clipboard Formats +CF_TEXT :: 1 +CF_BITMAP :: 2 +CF_METAFILEPICT :: 3 +CF_SYLK :: 4 +CF_DIF :: 5 +CF_TIFF :: 6 +CF_OEMTEXT :: 7 +CF_DIB :: 8 +CF_PALETTE :: 9 +CF_PENDATA :: 10 +CF_RIFF :: 11 +CF_WAVE :: 12 +CF_UNICODETEXT :: 13 +CF_ENHMETAFILE :: 14 +CF_HDROP :: 15 +CF_LOCALE :: 16 +CF_DIBV5 :: 17 +CF_DSPBITMAP :: 0x0082 +CF_DSPENHMETAFILE :: 0x008E +CF_DSPMETAFILEPICT :: 0x0083 +CF_DSPTEXT :: 0x0081 +CF_GDIOBJFIRST :: 0x0300 +CF_GDIOBJLAST :: 0x03FF +CF_OWNERDISPLAY :: 0x0080 +CF_PRIVATEFIRST :: 0x0200 +CF_PRIVATELAST :: 0x02FF diff --git a/core/sys/windows/userenv.odin b/core/sys/windows/userenv.odin index a31e363e1..2a2209d2c 100644 --- a/core/sys/windows/userenv.odin +++ b/core/sys/windows/userenv.odin @@ -1,4 +1,4 @@ -// +build windows +#+build windows package sys_windows foreign import userenv "system:Userenv.lib" diff --git a/core/sys/windows/util.odin b/core/sys/windows/util.odin index 929df1765..b3eb800bc 100644 --- a/core/sys/windows/util.odin +++ b/core/sys/windows/util.odin @@ -1,4 +1,4 @@ -// +build windows +#+build windows package sys_windows import "base:runtime" diff --git a/core/sys/windows/ux_theme.odin b/core/sys/windows/ux_theme.odin index 7af399361..527abd62f 100644 --- a/core/sys/windows/ux_theme.odin +++ b/core/sys/windows/ux_theme.odin @@ -1,4 +1,4 @@ -// +build windows +#+build windows package sys_windows foreign import uxtheme "system:UxTheme.lib" diff --git a/core/sys/windows/wgl.odin b/core/sys/windows/wgl.odin index d0d96d90b..8fea55c3d 100644 --- a/core/sys/windows/wgl.odin +++ b/core/sys/windows/wgl.odin @@ -1,4 +1,4 @@ -// +build windows +#+build windows package sys_windows import "core:c" diff --git a/core/sys/windows/wglext.odin b/core/sys/windows/wglext.odin index 0c4b51d65..4c76b39ec 100644 --- a/core/sys/windows/wglext.odin +++ b/core/sys/windows/wglext.odin @@ -1,4 +1,4 @@ -// +build windows
+#+build windows
package sys_windows
// WGL_ARB_buffer_region
diff --git a/core/sys/windows/window_messages.odin b/core/sys/windows/window_messages.odin index 888c5ccf9..d69771bdf 100644 --- a/core/sys/windows/window_messages.odin +++ b/core/sys/windows/window_messages.odin @@ -1,4 +1,4 @@ -// +build windows +#+build windows package sys_windows WM_NULL :: 0x0000 diff --git a/core/sys/windows/winerror.odin b/core/sys/windows/winerror.odin index 8882dad71..b3b470619 100644 --- a/core/sys/windows/winerror.odin +++ b/core/sys/windows/winerror.odin @@ -1,4 +1,4 @@ -// +build windows +#+build windows package sys_windows // https://learn.microsoft.com/en-us/windows/win32/api/winerror/ @@ -213,6 +213,7 @@ ERROR_BROKEN_PIPE : DWORD : 109 ERROR_CALL_NOT_IMPLEMENTED : DWORD : 120 ERROR_INSUFFICIENT_BUFFER : DWORD : 122 ERROR_INVALID_NAME : DWORD : 123 +ERROR_NEGATIVE_SEEK : DWORD : 131 ERROR_BAD_ARGUMENTS : DWORD : 160 ERROR_LOCK_FAILED : DWORD : 167 ERROR_ALREADY_EXISTS : DWORD : 183 diff --git a/core/sys/windows/winmm.odin b/core/sys/windows/winmm.odin index a1786c27a..3c7ec80e7 100644 --- a/core/sys/windows/winmm.odin +++ b/core/sys/windows/winmm.odin @@ -1,4 +1,4 @@ -// +build windows +#+build windows package sys_windows foreign import winmm "system:Winmm.lib" diff --git a/core/sys/windows/winnls.odin b/core/sys/windows/winnls.odin index 292d2fad2..ffb2638d5 100644 --- a/core/sys/windows/winnls.odin +++ b/core/sys/windows/winnls.odin @@ -1,4 +1,4 @@ -// +build windows +#+build windows package sys_windows LCTYPE :: distinct DWORD diff --git a/core/sys/windows/winver.odin b/core/sys/windows/winver.odin index 091d53d3a..47751dab7 100644 --- a/core/sys/windows/winver.odin +++ b/core/sys/windows/winver.odin @@ -1,4 +1,4 @@ -// +build windows +#+build windows package sys_windows foreign import version "system:version.lib" diff --git a/core/sys/windows/wow64_apiset.odin b/core/sys/windows/wow64_apiset.odin index 28558e9ca..3d29b786e 100644 --- a/core/sys/windows/wow64_apiset.odin +++ b/core/sys/windows/wow64_apiset.odin @@ -1,4 +1,4 @@ -//+build windows +#+build windows package sys_windows foreign import kernel32 "system:Kernel32.lib" diff --git a/core/sys/windows/ws2_32.odin b/core/sys/windows/ws2_32.odin index e9bf8abc9..5b2952495 100644 --- a/core/sys/windows/ws2_32.odin +++ b/core/sys/windows/ws2_32.odin @@ -1,4 +1,4 @@ -// +build windows +#+build windows package sys_windows // Define flags to be used with the WSAAsyncSelect() call. |