Zig bindings for orca (still WIP) #140
|
@ -1,19 +1,6 @@
|
|||
@echo off
|
||||
|
||||
:: compile wasm module
|
||||
rem set wasmFlags=--target=wasm32^
|
||||
rem --no-standard-libraries ^
|
||||
rem -fno-builtin ^
|
||||
rem -Wl,--no-entry ^
|
||||
rem -Wl,--export-dynamic ^
|
||||
rem -Wl,--relocatable ^
|
||||
rem -g ^
|
||||
rem -O2 ^
|
||||
rem -mbulk-memory ^
|
||||
rem -D__ORCA__ ^
|
||||
rem -isystem ..\..\src\libc-shim\include ^
|
||||
rem -I..\..\ext -I ..\..\src
|
||||
|
||||
set ORCA_DIR=..\..
|
||||
set STDLIB_DIR=%ORCA_DIR%\src\libc-shim
|
||||
|
||||
|
|
|
@ -54,6 +54,10 @@ export fn oc_on_mouse_up(button: oc.MouseButton) void {
|
|||
oc.log.info("mouse up! {}", .{button}, @src());
|
||||
}
|
||||
|
||||
export fn oc_on_mouse_wheel(dx: f32, dy: f32) void {
|
||||
oc.log.info("mouse wheel! dx: {d:.2}, dy: {d:.2}", .{ dx, dy }, @src());
|
||||
}
|
||||
|
||||
export fn oc_on_key_down(scan: oc.ScanCode, key: oc.KeyCode) void {
|
||||
oc.log.info("key down: {} {}", .{ scan, key }, @src());
|
||||
}
|
||||
|
@ -132,10 +136,6 @@ export fn oc_on_frame_refresh() void {
|
|||
const center_x = frame_size.x / 2;
|
||||
const text_begin_x = center_x - text_rect.Flat.w / 2;
|
||||
|
||||
oc.log.info("text rect width: {d}", .{text_rect.Flat.w}, @src());
|
||||
oc.log.info("center_x: {d}", .{center_x}, @src());
|
||||
oc.log.info("text_begin_x: {d}", .{text_begin_x}, @src());
|
||||
|
||||
Mat2x3.push(Mat2x3.translate(text_begin_x, 150));
|
||||
defer Mat2x3.pop();
|
||||
|
||||
|
|
386
src/orca.zig
386
src/orca.zig
|
@ -90,6 +90,109 @@ pub const ListElt = extern struct {
|
|||
pub const List = extern struct {
|
||||
first: ?*ListElt,
|
||||
last: ?*ListElt,
|
||||
|
||||
pub fn init() List {
|
||||
return .{
|
||||
.first = null,
|
||||
.last = null,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn begin(self: *List) ?*ListElt {
|
||||
return self.first;
|
||||
}
|
||||
|
||||
pub fn end() ?*ListElt {
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn last(self: *List) ?*ListElt {
|
||||
return self.last;
|
||||
}
|
||||
|
||||
pub fn insert(self: *List, after_elt: *ListElt, elt: *ListElt) void {
|
||||
elt.prev = after_elt;
|
||||
elt.next = after_elt.next;
|
||||
if (after_elt.next != null) {
|
||||
after_elt.next.prev = elt;
|
||||
} else {
|
||||
self.last = elt;
|
||||
}
|
||||
after_elt.next = elt;
|
||||
}
|
||||
|
||||
pub fn insertBefore(self: *List, before_elt: *ListElt, elt: *ListElt) void {
|
||||
elt.next = before_elt;
|
||||
elt.prev = before_elt.prev;
|
||||
if (before_elt.prev != null) {
|
||||
before_elt.prev.next = elt;
|
||||
} else {
|
||||
self.first = elt;
|
||||
}
|
||||
before_elt.prev = elt;
|
||||
}
|
||||
|
||||
pub fn remove(self: *List, elt: *ListElt) void {
|
||||
if (elt.prev != null) {
|
||||
elt.prev.next = elt.next;
|
||||
} else {
|
||||
self.first = elt.next;
|
||||
}
|
||||
if (elt.next != null) {
|
||||
elt.next.prev = elt.prev;
|
||||
} else {
|
||||
self.last = elt.prev;
|
||||
}
|
||||
elt.prev = blk: {
|
||||
const tmp = null;
|
||||
elt.next = tmp;
|
||||
break :blk tmp;
|
||||
};
|
||||
}
|
||||
|
||||
pub fn push(self: *List, elt: *ListElt) void {
|
||||
elt.next = self.first;
|
||||
elt.prev = null;
|
||||
if (self.first != null) {
|
||||
self.first.prev = elt;
|
||||
} else {
|
||||
self.last = elt;
|
||||
}
|
||||
self.first = elt;
|
||||
}
|
||||
|
||||
pub fn pop(self: *List) ListElt {
|
||||
var elt: *ListElt = begin(self);
|
||||
if (elt != end(self)) {
|
||||
remove(self, elt);
|
||||
return elt;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn pushBack(self: *List, elt: *ListElt) void {
|
||||
elt.prev = self.last;
|
||||
elt.next = null;
|
||||
if (self.last != null) {
|
||||
self.last.next = elt;
|
||||
} else {
|
||||
self.first = elt;
|
||||
}
|
||||
self.last = elt;
|
||||
}
|
||||
|
||||
pub fn popBack(self: *List) ListElt {
|
||||
var elt: *ListElt = last(self);
|
||||
if (elt != end(self)) {
|
||||
remove(self, elt);
|
||||
return elt;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn empty(self: *const List) bool {
|
||||
return (self.first == null) or (self.last == null);
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------------------
|
||||
|
@ -198,87 +301,129 @@ pub const ArenaOptions = extern struct {
|
|||
|
||||
fn stringType(comptime CharType: type) type {
|
||||
return extern struct {
|
||||
const Self = @This();
|
||||
pub const StrListElt = extern struct {
|
||||
list_elt: ListElt,
|
||||
string: Str,
|
||||
};
|
||||
|
||||
pub const StrList = extern struct {
|
||||
list: List,
|
||||
elt_count: u64,
|
||||
len: u64,
|
||||
|
||||
pub fn init() StrList {
|
||||
return .{
|
||||
.list = List.init(),
|
||||
.elt_count = 0,
|
||||
.len = 0,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn push(list: *StrList, str: *Str, arena: *Arena) void {
|
||||
var elt: *StrListElt = arena.pushType(ListElt);
|
||||
elt.string = str;
|
||||
list.append(elt.list_elt);
|
||||
list.elt_count += 1;
|
||||
list.len += str.len;
|
||||
}
|
||||
|
||||
pub fn pushf(list: *StrList, arena: *Arena, comptime format: []const u8, args: anytype) Str {
|
||||
var str = Str.pushf(arena, format, args);
|
||||
list.push(str, arena);
|
||||
}
|
||||
|
||||
// TODO
|
||||
// pub fn join(list: *const StrList, arena: *Arena) Str;
|
||||
// const empty = Str{ .ptr = null, .len = 0 };
|
||||
// return list.collate(arena, empty, empty, empty));
|
||||
// }
|
||||
// pub fn collate(list: *StrList, arena: *Arena, prefix: Str, separator: Str, postfix: Str) Str;
|
||||
};
|
||||
|
||||
const Str = @This();
|
||||
|
||||
ptr: ?[*]CharType,
|
||||
len: usize,
|
||||
|
||||
extern fn strncmp(a: ?[*]u8, b: ?[*]u8, len: usize) c_int;
|
||||
|
||||
// extern fn oc_str8_push_buffer(arena: *Arena, len: u64, buffer: [*]u8) oc_str8;
|
||||
// extern fn oc_str8_push_cstring(arena: *Arena, str: [*]const u8) oc_str8;
|
||||
// extern fn oc_str8_push_copy(arena: *Arena, s: oc_str8) oc_str8;
|
||||
// extern fn oc_str8_push_slice(arena: *Arena, s: oc_str8, start: u64, end: u64) oc_str8;
|
||||
// // extern fn oc_str8_pushfv(arena: oc_arena*, char: const* format, args: va_list) oc_str8;
|
||||
// // extern fn oc_str8_pushf(arena: oc_arena*, char: const* format, ...) oc_str8;
|
||||
// extern fn oc_str8_cmp(s1: oc_str8, s2: oc_str8) c_int;
|
||||
|
||||
// extern fn oc_str8_list_push(oc_arena* arena, oc_str8_list* list, oc_str8 str) void;
|
||||
// extern fn oc_str8_list_pushf(oc_arena* arena, oc_str8_list* list, const char* format, ...) void;
|
||||
|
||||
// extern fn oc_str8_list_collate(oc_arena* arena, oc_str8_list list, oc_str8 prefix, oc_str8 separator, oc_str8 postfix) oc_str8;
|
||||
// extern fn oc_str8_list_join(oc_arena* arena, oc_str8_list list) oc_str8;
|
||||
// extern fn oc_str8_split(oc_arena* arena, oc_str8 str, oc_str8_list separators) oc_str8_list;
|
||||
|
||||
// extern fn oc_str16_from_buffer(u64 len, u16* buffer) oc_str16;
|
||||
// extern fn oc_str16_slice(oc_str16 s, u64 start, u64 end) oc_str16;
|
||||
|
||||
// extern fn oc_str16_push_buffer(oc_arena* arena, u64 len, u16* buffer) oc_str16;
|
||||
// extern fn oc_str16_push_copy(oc_arena* arena, oc_str16 s) oc_str16;
|
||||
// extern fn oc_str16_push_slice(oc_arena* arena, oc_str16 s, u64 start, u64 end) oc_str16;
|
||||
// extern fn oc_str16_list_push(oc_arena* arena, oc_str16_list* list, oc_str16 str) void;
|
||||
// extern fn oc_str16_list_join(oc_arena* arena, oc_str16_list list) oc_str16;
|
||||
// extern fn oc_str16_split(oc_arena* arena, oc_str16 str, oc_str16_list separators) oc_str16_list;
|
||||
// extern fn oc_str32_from_buffer(u64 len, u32* buffer) oc_str32;
|
||||
// extern fn oc_str32_slice(oc_str32 s, u64 start, u64 end) oc_str32;
|
||||
|
||||
// extern fn oc_str32_push_buffer(oc_arena* arena, u64 len, u32* buffer) oc_str32;
|
||||
// extern fn oc_str32_push_copy(oc_arena* arena, oc_str32 s) oc_str32;
|
||||
// extern fn oc_str32_push_slice(oc_arena* arena, oc_str32 s, u64 start, u64 end) oc_str32;
|
||||
// extern fn oc_str32_list_push(oc_arena* arena, oc_str32_list* list, oc_str32 str) void;
|
||||
// extern fn oc_str32_list_join(oc_arena* arena, oc_str32_list list) oc_str32;
|
||||
// extern fn oc_str32_split(oc_arena* arena, oc_str32 str, oc_str32_list separators) oc_str32_list;
|
||||
|
||||
pub fn fromSlice(str: []const CharType) Self {
|
||||
pub fn fromSlice(str: []const CharType) Str {
|
||||
return .{
|
||||
.ptr = @constCast(str.ptr),
|
||||
.len = str.len,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn slice(self: *const Self) []CharType {
|
||||
if (self.ptr) |p| {
|
||||
return p[0..self.len];
|
||||
pub fn slice(str: *const Str) []CharType {
|
||||
if (str.ptr) |p| {
|
||||
return p[0..str.len];
|
||||
}
|
||||
|
||||
return &[_]CharType{};
|
||||
}
|
||||
|
||||
pub fn sliceInner(self: *const Self, start: u64, end: u64) []CharType {
|
||||
if (self.ptr) |p| {
|
||||
pub fn sliceInner(str: *const Str, start: u64, end: u64) []CharType {
|
||||
if (str.ptr) |p| {
|
||||
assert(start <= end, "{}.sliceLen: start <= end", .{typename()}, @src());
|
||||
assert(end <= self.len, "{}.sliceLen: end <= self.len", .{typename()}, @src());
|
||||
assert(end <= str.len, "{}.sliceLen: end <= str.len", .{typename()}, @src());
|
||||
return p[start..end];
|
||||
}
|
||||
|
||||
return &[_]CharType{};
|
||||
}
|
||||
|
||||
pub fn fromBuffer(len: u64, buffer: ?[]CharType) Self {
|
||||
pub fn fromBuffer(len: u64, buffer: ?[]CharType) Str {
|
||||
return .{
|
||||
.ptr = buffer,
|
||||
.len = @intCast(len),
|
||||
};
|
||||
}
|
||||
|
||||
// pub fn cmp(a: *const Self, b: *const Self) std.math.Order {
|
||||
// if (CharType != u8) {
|
||||
// @compileError("cmp() is only supported for Str8");
|
||||
// }
|
||||
// const value = strncmp(a.ptr, b.ptr, std.math.min(a.len, b.len));
|
||||
pub fn pushBuffer(arena: *Arena, buffer: []u8) Str {
|
||||
var str: Str = undefined;
|
||||
str.len = buffer.len;
|
||||
str.ptr = arena.pushArray(CharType, buffer.len + 1);
|
||||
@memcpy(str.ptr[0..buffer.len], buffer);
|
||||
str.ptr[buffer.len] = 0;
|
||||
return str;
|
||||
}
|
||||
|
||||
pub fn pushCopy(str: *const Str, arena: *Arena) Str {
|
||||
return pushBuffer(arena, str.ptr[0..str.len]);
|
||||
}
|
||||
|
||||
pub fn pushSlice(str: *const Str, arena: *Arena, start: usize, end: usize) Str {
|
||||
return pushBuffer(arena, str.ptr[start..end]);
|
||||
}
|
||||
|
||||
pub fn pushf(arena: *Arena, format: []const u8, args: anytype) Str {
|
||||
if (CharType != u8) {
|
||||
@compileError("pushf() is only supported for Str8");
|
||||
}
|
||||
|
||||
var str: Str = undefined;
|
||||
str.len = @intCast(std.fmt.count(format, args));
|
||||
str.ptr = arena.pushArray(CharType, str.len + 1);
|
||||
_ = std.fmt.bufPrintZ(str.ptr[0 .. str.len + 1], format, args) catch unreachable;
|
||||
return str;
|
||||
}
|
||||
|
||||
// }
|
||||
// TODO
|
||||
// pub fn split() void;
|
||||
|
||||
pub fn cmp(a: *const Str, b: *const Str) std.math.Order {
|
||||
if (CharType != u8) {
|
||||
@compileError("cmp() is only supported for Str8");
|
||||
}
|
||||
const value = strncmp(a.ptr, b.ptr, std.math.min(a.len, b.len));
|
||||
if (value < 0) {
|
||||
return .lt;
|
||||
} else if (value == 0) {
|
||||
return .eq;
|
||||
} else {
|
||||
return .gt;
|
||||
}
|
||||
}
|
||||
|
||||
fn typename() []const u8 {
|
||||
return switch (CharType) {
|
||||
|
@ -318,136 +463,6 @@ pub const Str8List = stringListType(Str8);
|
|||
pub const Str16List = stringListType(Str8);
|
||||
pub const Str32List = stringListType(Str8);
|
||||
|
||||
// pub const Str8 = extern struct {
|
||||
// ptr: ?[*:0]u8,
|
||||
// len: usize,
|
||||
|
||||
// pub fn fromSlice(slice: [:0]const u8) Str8 {
|
||||
// return .{
|
||||
// ptr: @constCast(slice.ptr),
|
||||
// len: slice.len,
|
||||
// };
|
||||
// }
|
||||
// };
|
||||
|
||||
// typedef struct oc_str8
|
||||
// {
|
||||
// char* ptr;
|
||||
// size_t len;
|
||||
// } oc_str8;
|
||||
|
||||
// #define OC_STR8(s) ((oc_str8){ .ptr = (char*)s, .len = (s) ? strlen(s) : 0 })
|
||||
|
||||
//NOTE: this only works with string literals, but is sometimes necessary to generate compile-time constants
|
||||
// #define OC_STR8_LIT(s) \
|
||||
// { \
|
||||
// (char*)(s), sizeof(s) - 1 \
|
||||
// }
|
||||
|
||||
// #define oc_str8_lp(s) ((s).len), ((s).ptr)
|
||||
// #define oc_str8_ip(s) (int)oc_str8_lp(s)
|
||||
|
||||
// ORCA_API oc_str8 oc_str8_from_buffer(u64 len, char* buffer);
|
||||
// ORCA_API oc_str8 oc_str8_slice(oc_str8 s, u64 start, u64 end);
|
||||
|
||||
// ORCA_API oc_str8 oc_str8_push_buffer(oc_arena* arena, u64 len, char* buffer);
|
||||
// ORCA_API oc_str8 oc_str8_push_cstring(oc_arena* arena, const char* str);
|
||||
// ORCA_API oc_str8 oc_str8_push_copy(oc_arena* arena, oc_str8 s);
|
||||
// ORCA_API oc_str8 oc_str8_push_slice(oc_arena* arena, oc_str8 s, u64 start, u64 end);
|
||||
|
||||
// ORCA_API oc_str8 oc_str8_pushfv(oc_arena* arena, const char* format, va_list args);
|
||||
// ORCA_API oc_str8 oc_str8_pushf(oc_arena* arena, const char* format, ...);
|
||||
|
||||
// ORCA_API int oc_str8_cmp(oc_str8 s1, oc_str8 s2);
|
||||
|
||||
// ORCA_API char* oc_str8_to_cstring(oc_arena* arena, oc_str8 string);
|
||||
|
||||
// typedef struct oc_str8_elt
|
||||
// {
|
||||
// oc_list_elt listElt;
|
||||
// oc_str8 string;
|
||||
// } oc_str8_elt;
|
||||
|
||||
// typedef struct oc_str8_list
|
||||
// {
|
||||
// oc_list list;
|
||||
// u64 eltCount;
|
||||
// u64 len;
|
||||
// } oc_str8_list;
|
||||
|
||||
// ORCA_API void oc_str8_list_push(oc_arena* arena, oc_str8_list* list, oc_str8 str);
|
||||
// ORCA_API void oc_str8_list_pushf(oc_arena* arena, oc_str8_list* list, const char* format, ...);
|
||||
|
||||
// ORCA_API oc_str8 oc_str8_list_collate(oc_arena* arena, oc_str8_list list, oc_str8 prefix, oc_str8 separator, oc_str8 postfix);
|
||||
// ORCA_API oc_str8 oc_str8_list_join(oc_arena* arena, oc_str8_list list);
|
||||
// ORCA_API oc_str8_list oc_str8_split(oc_arena* arena, oc_str8 str, oc_str8_list separators);
|
||||
|
||||
// //----------------------------------------------------------------------------------
|
||||
// // [STRINGS] u16 strings
|
||||
// //----------------------------------------------------------------------------------
|
||||
// typedef struct oc_str16
|
||||
// {
|
||||
// u16* ptr;
|
||||
// size_t len;
|
||||
// } oc_str16;
|
||||
|
||||
// ORCA_API oc_str16 oc_str16_from_buffer(u64 len, u16* buffer);
|
||||
// ORCA_API oc_str16 oc_str16_slice(oc_str16 s, u64 start, u64 end);
|
||||
|
||||
// ORCA_API oc_str16 oc_str16_push_buffer(oc_arena* arena, u64 len, u16* buffer);
|
||||
// ORCA_API oc_str16 oc_str16_push_copy(oc_arena* arena, oc_str16 s);
|
||||
// ORCA_API oc_str16 oc_str16_push_slice(oc_arena* arena, oc_str16 s, u64 start, u64 end);
|
||||
|
||||
// typedef struct oc_str16_elt
|
||||
// {
|
||||
// oc_list_elt listElt;
|
||||
// oc_str16 string;
|
||||
// } oc_str16_elt;
|
||||
|
||||
// typedef struct oc_str16_list
|
||||
// {
|
||||
// oc_list list;
|
||||
// u64 eltCount;
|
||||
// u64 len;
|
||||
// } oc_str16_list;
|
||||
|
||||
// ORCA_API void oc_str16_list_push(oc_arena* arena, oc_str16_list* list, oc_str16 str);
|
||||
// ORCA_API oc_str16 oc_str16_list_join(oc_arena* arena, oc_str16_list list);
|
||||
// ORCA_API oc_str16_list oc_str16_split(oc_arena* arena, oc_str16 str, oc_str16_list separators);
|
||||
|
||||
// //----------------------------------------------------------------------------------
|
||||
// // [STRINGS] u32 strings
|
||||
// //----------------------------------------------------------------------------------
|
||||
// typedef struct oc_str32
|
||||
// {
|
||||
// u32* ptr;
|
||||
// size_t len;
|
||||
// } oc_str32;
|
||||
|
||||
// ORCA_API oc_str32 oc_str32_from_buffer(u64 len, u32* buffer);
|
||||
// ORCA_API oc_str32 oc_str32_slice(oc_str32 s, u64 start, u64 end);
|
||||
|
||||
// ORCA_API oc_str32 oc_str32_push_buffer(oc_arena* arena, u64 len, u32* buffer);
|
||||
// ORCA_API oc_str32 oc_str32_push_copy(oc_arena* arena, oc_str32 s);
|
||||
// ORCA_API oc_str32 oc_str32_push_slice(oc_arena* arena, oc_str32 s, u64 start, u64 end);
|
||||
|
||||
// typedef struct oc_str32_elt
|
||||
// {
|
||||
// oc_list_elt listElt;
|
||||
// oc_str32 string;
|
||||
// } oc_str32_elt;
|
||||
|
||||
// typedef struct oc_str32_list
|
||||
// {
|
||||
// oc_list list;
|
||||
// u64 eltCount;
|
||||
// u64 len;
|
||||
// } oc_str32_list;
|
||||
|
||||
// ORCA_API void oc_str32_list_push(oc_arena* arena, oc_str32_list* list, oc_str32 str);
|
||||
// ORCA_API oc_str32 oc_str32_list_join(oc_arena* arena, oc_str32_list list);
|
||||
// ORCA_API oc_str32_list oc_str32_split(oc_arena* arena, oc_str32 str, oc_str32_list separators);
|
||||
|
||||
//------------------------------------------------------------------------------------------
|
||||
// [UTF8]
|
||||
//------------------------------------------------------------------------------------------
|
||||
|
@ -1402,6 +1417,7 @@ pub const Surface = extern struct {
|
|||
extern fn oc_surface_contents_scaling(surface: Surface) Vec2;
|
||||
extern fn oc_surface_bring_to_front(surface: Surface) void;
|
||||
extern fn oc_surface_send_to_back(surface: Surface) void;
|
||||
// TODO
|
||||
// extern fn oc_surface_render_commands(
|
||||
// surface: Surface,
|
||||
// color: Color,
|
||||
|
@ -1616,6 +1632,18 @@ pub const circleFill = oc_circle_fill;
|
|||
pub const circleStroke = oc_circle_stroke;
|
||||
pub const arc = oc_arc;
|
||||
|
||||
//------------------------------------------------------------------------------------------
|
||||
// [GRAPHICS]: GLES
|
||||
ilidemi marked this conversation as resolved
Outdated
|
||||
//------------------------------------------------------------------------------------------
|
||||
|
||||
// TODO
|
||||
|
||||
//------------------------------------------------------------------------------------------
|
||||
// [UI]: GLES
|
||||
//------------------------------------------------------------------------------------------
|
||||
|
||||
// TODO
|
||||
|
||||
//------------------------------------------------------------------------------------------
|
||||
// [FILE IO] basic API
|
||||
//------------------------------------------------------------------------------------------
|
||||
|
|
Loading…
Reference in New Issue
Should non-core data structs be put into the namespace as well? Arena/Vec2 makes sense at the top level, File/IO/UI stuff seems like it can be contained
You mean like putting File/FileType/IoReq/etc into a struct named
io
, kind of like how the log stuff is namespaced intolog
? If so, I think that makes sense.What looked confusing to me was how functions are namespaced but the structs local to that module are not. Guess something like File is used for fonts and images too so it's a judgement call but something like FileDialog is pretty clear. I'm coming at this from UI angle where pretty much all of the structs are local.
Ohh I think I get it. File and Surface are legit structs here, just with a ton of methods. I thought they're more like function namespaces. Apologies for confusion.
I am still confused,
oc_file_size
andoc_file_open_with_request
are declared in the exact same way but former has File as the self argument and the latter doesn't. Does that translate into a method call and a namespaced function respectively?In Zig, calling funtions on structs is just syntax sugar for passing it as the first argument to the function. So these are equivalent:
And you can have other functions that don't pass the struct type as the first argument, you just have to call them explicitly on the struct type and not an instance, otherwise you'll get an error:
Hopefully that clears things up?
So I guess to answer your question, directly, yes. :P
And the latter one goes under File because it's a sort of a factory method. All makes sense, thank you for the explanation!