Application Context Implementation #4
17
src/main.zig
17
src/main.zig
|
@ -21,30 +21,29 @@ test {
|
|||
_ = sys;
|
||||
}
|
||||
|
||||
fn run(event_loop: *sys.EventLoop, graphics: *sys.GraphicsContext) anyerror!void {
|
||||
fn run(ev: *sys.EventLoop, fs: *const sys.FileSystem, gr: *sys.GraphicsContext) anyerror!void {
|
||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||
|
||||
defer _ = gpa.deinit();
|
||||
|
||||
{
|
||||
const file_access = try event_loop.open(.readonly,
|
||||
try sys.FileSystem.data.joinedPath(&.{"data", "ona.lua"}));
|
||||
const file_access = try ev.open(.readonly, try fs.data.joinedPath(&.{"ona.lua"}));
|
||||
|
||||
defer event_loop.close(file_access);
|
||||
defer ev.close(file_access);
|
||||
|
||||
const file_size = try file_access.size(event_loop);
|
||||
const file_size = try file_access.size(ev);
|
||||
const allocator = gpa.allocator();
|
||||
const buffer = try allocator.alloc(u8, file_size);
|
||||
|
||||
defer allocator.free(buffer);
|
||||
|
||||
if ((try event_loop.readFile(file_access, buffer)) != file_size)
|
||||
if ((try ev.readFile(file_access, buffer)) != file_size)
|
||||
return error.ScriptLoadFailure;
|
||||
|
||||
event_loop.log(.debug, buffer);
|
||||
ev.log(.debug, buffer);
|
||||
}
|
||||
|
||||
while (graphics.poll()) |_| {
|
||||
graphics.present();
|
||||
while (gr.poll()) |_| {
|
||||
gr.present();
|
||||
}
|
||||
}
|
||||
|
|
281
src/sys.zig
281
src/sys.zig
|
@ -61,7 +61,6 @@ pub const EventLoop = opaque {
|
|||
/// Internal state of the event loop hidden from the API consumer.
|
||||
///
|
||||
const Implementation = struct {
|
||||
user_prefix: []u8,
|
||||
file_system_semaphore: *ext.SDL_sem,
|
||||
file_system_mutex: *ext.SDL_mutex,
|
||||
file_system_thread: ?*ext.SDL_Thread,
|
||||
|
@ -71,6 +70,8 @@ pub const EventLoop = opaque {
|
|||
///
|
||||
///
|
||||
const InitError = error {
|
||||
DataFileNotFound,
|
||||
DataFileInvalid,
|
||||
OutOfSemaphores,
|
||||
OutOfMutexes,
|
||||
OutOfMemory,
|
||||
|
@ -147,22 +148,19 @@ pub const EventLoop = opaque {
|
|||
///
|
||||
///
|
||||
fn init() InitError!Implementation {
|
||||
const data_file_access = @ptrCast(*FileAccess,
|
||||
ext.SDL_RWFromFile("./data.tar", "r+") orelse return error.DataFileNotFound);
|
||||
|
||||
return Implementation{
|
||||
.user_prefix = create_pref_path: {
|
||||
const path = ext.SDL_GetPrefPath("ona", "ona") orelse {
|
||||
ext.SDL_LogCritical(ext.SDL_LOG_CATEGORY_APPLICATION,
|
||||
"Failed to load user path");
|
||||
|
||||
return error.OutOfMemory;
|
||||
};
|
||||
|
||||
break: create_pref_path path[0 .. std.mem.len(path)];
|
||||
.data_archive = tar.Archive.init(data_file_access) catch |err| switch (err) {
|
||||
error.Invalid, error.Inaccessible => return error.DataFileInvalid,
|
||||
},
|
||||
|
||||
.file_system_semaphore = ext.SDL_CreateSemaphore(0)
|
||||
orelse return error.OutOfSemaphores,
|
||||
|
||||
.file_system_mutex = ext.SDL_CreateMutex() orelse return error.OutOfMutexes,
|
||||
.data_file = data_file_access,
|
||||
.file_system_thread = null,
|
||||
};
|
||||
}
|
||||
|
@ -181,51 +179,12 @@ pub const EventLoop = opaque {
|
|||
while (implementation.file_system_messages) |messages| {
|
||||
switch (messages.request) {
|
||||
.exit => return 0,
|
||||
.log => |*log_request| .log(log_request.kind, log_request.message),
|
||||
|
||||
.log => |*log_request| ext.SDL_LogMessage(ext.SDL_LOG_CATEGORY_APPLICATION,
|
||||
@enumToInt(log_request.kind), log_request.message.ptr),
|
||||
.open => |*open_request| open_request.result =
|
||||
.open(open_request.mode, open_request.file_system_path),
|
||||
|
||||
.open => |*open_request| {
|
||||
switch (open_request.file_system_path.file_system) {
|
||||
.data => {
|
||||
// TODO: Implement
|
||||
open_request.result = error.NotFound;
|
||||
},
|
||||
|
||||
.user => {
|
||||
var path_buffer = std.mem.zeroes([4096]u8);
|
||||
var path = stack.Fixed(u8){.buffer = path_buffer[0 .. ]};
|
||||
|
||||
path.pushAll(implementation.user_prefix) catch {
|
||||
open_request.result = error.NotFound;
|
||||
|
||||
continue;
|
||||
};
|
||||
|
||||
if (!open_request.file_system_path.write(path.writer())) {
|
||||
open_request.result = error.NotFound;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ext.SDL_RWFromFile(&path_buffer, switch (open_request.mode) {
|
||||
.readonly => "rb",
|
||||
.overwrite => "wb",
|
||||
.append => "ab",
|
||||
})) |rw_ops| {
|
||||
open_request.result = @ptrCast(*FileAccess, rw_ops);
|
||||
} else {
|
||||
open_request.result = error.NotFound;
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
.close => |*close_request| {
|
||||
// TODO: Use this result somehow.
|
||||
_ = ext.SDL_RWclose(@ptrCast(*ext.SDL_RWops, @alignCast(
|
||||
@alignOf(ext.SDL_RWops), close_request.file_access)));
|
||||
},
|
||||
.close => |*close_request| .close(close_request.file_access),
|
||||
|
||||
.read_file => |read_request| {
|
||||
// TODO: Implement.
|
||||
|
@ -270,63 +229,6 @@ pub const EventLoop = opaque {
|
|||
}
|
||||
};
|
||||
|
||||
///
|
||||
/// [LogKind.info] represents a log message which is purely informative and does not indicate
|
||||
/// any kind of issue.
|
||||
///
|
||||
/// [LogKind.debug] represents a log message which is purely for debugging purposes and will
|
||||
/// only occurs in debug builds.
|
||||
///
|
||||
/// [LogKind.warning] represents a log message which is a warning about a issue that does not
|
||||
/// break anything important but is not ideal.
|
||||
///
|
||||
pub const LogKind = enum(u32) {
|
||||
info = ext.SDL_LOG_PRIORITY_INFO,
|
||||
debug = ext.SDL_LOG_PRIORITY_DEBUG,
|
||||
warning = ext.SDL_LOG_PRIORITY_WARN,
|
||||
};
|
||||
|
||||
///
|
||||
/// [OpenError.NotFound] is a catch-all for when a file could not be located to be opened. This
|
||||
/// may be as simple as it doesn't exist or the because the underlying file-system will not /
|
||||
/// cannot give access to it at this time.
|
||||
///
|
||||
pub const OpenError = error {
|
||||
NotFound,
|
||||
};
|
||||
|
||||
///
|
||||
/// [OpenMode.readonly] indicates that an existing file is opened in a read-only state,
|
||||
/// disallowing write access.
|
||||
///
|
||||
/// [OpenMode.overwrite] indicates that an empty file has been created or an existing file has
|
||||
/// been completely overwritten into.
|
||||
///
|
||||
/// [OpenMode.append] indicates that an existing file that has been opened for reading from and
|
||||
/// writing to on the end of existing data.
|
||||
///
|
||||
pub const OpenMode = enum {
|
||||
readonly,
|
||||
overwrite,
|
||||
append,
|
||||
};
|
||||
|
||||
///
|
||||
/// [SeekOrigin.head] indicates that a seek operation will seek from the offset origin of the
|
||||
/// file beginning, or "head".
|
||||
///
|
||||
/// [SeekOrigin.tail] indicates that a seek operation will seek from the offset origin of the
|
||||
/// file end, or "tail".
|
||||
///
|
||||
/// [SeekOrigin.cursor] indicates that a seek operation will seek from the current position of
|
||||
/// the file cursor.
|
||||
///
|
||||
pub const SeekOrigin = enum {
|
||||
head,
|
||||
tail,
|
||||
cursor,
|
||||
};
|
||||
|
||||
///
|
||||
/// Closes access to the file referenced by `file_access` via `event_loop`.
|
||||
///
|
||||
|
@ -488,15 +390,15 @@ pub const FileError = error {
|
|||
/// Platform-agnostic mechanism for working with an abstraction of the underlying file-system(s)
|
||||
/// available to the application in a sandboxed environment.
|
||||
///
|
||||
pub const FileSystem = enum {
|
||||
data,
|
||||
user,
|
||||
pub const FileSystem = struct {
|
||||
data: Root,
|
||||
user: Root,
|
||||
|
||||
///
|
||||
/// Platform-agnostic mechanism for referencing files and directories on a [FileSystem].
|
||||
///
|
||||
pub const Path = struct {
|
||||
file_system: FileSystem,
|
||||
root: *const Root,
|
||||
length: u16,
|
||||
buffer: [max]u8,
|
||||
|
||||
|
@ -521,7 +423,7 @@ pub const FileSystem = enum {
|
|||
/// byte. Because of this, it is not safe to asume that a path may hold [max] individual
|
||||
/// characters.
|
||||
///
|
||||
pub const max = 1000;
|
||||
pub const max = 512;
|
||||
|
||||
///
|
||||
///
|
||||
|
@ -540,12 +442,17 @@ pub const FileSystem = enum {
|
|||
};
|
||||
|
||||
///
|
||||
/// Creates and returns a [Path] value in the file system to the location specified by the
|
||||
/// joining of the `sequences` path values.
|
||||
///
|
||||
pub fn joinedPath(file_system: FileSystem, sequences: []const []const u8) PathError!Path {
|
||||
///
|
||||
pub const Root = struct {
|
||||
prefix: []const u8,
|
||||
|
||||
///
|
||||
///
|
||||
///
|
||||
pub fn joinedPath(root: Root, sequences: []const []const u8) PathError!Path {
|
||||
var path = Path{
|
||||
.file_system = file_system,
|
||||
.root = root,
|
||||
.buffer = std.mem.zeroes([Path.max]u8),
|
||||
.length = 0,
|
||||
};
|
||||
|
@ -586,6 +493,7 @@ pub const FileSystem = enum {
|
|||
|
||||
return path;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
///
|
||||
|
@ -629,13 +537,125 @@ pub const GraphicsContext = opaque {
|
|||
///
|
||||
///
|
||||
pub fn GraphicsRunner(comptime Errors: type) type {
|
||||
return fn (*EventLoop, *GraphicsContext) callconv(.Async) Errors!void;
|
||||
return fn (*EventLoop, *FileSystem, *GraphicsContext) callconv(.Async) Errors!void;
|
||||
}
|
||||
|
||||
///
|
||||
/// [LogKind.info] represents a log message which is purely informative and does not indicate
|
||||
/// any kind of issue.
|
||||
///
|
||||
/// [LogKind.debug] represents a log message which is purely for debugging purposes and will
|
||||
/// only occurs in debug builds.
|
||||
///
|
||||
/// [LogKind.warning] represents a log message which is a warning about a issue that does not
|
||||
/// break anything important but is not ideal.
|
||||
///
|
||||
pub const LogKind = enum(u32) {
|
||||
info = ext.SDL_LOG_PRIORITY_INFO,
|
||||
debug = ext.SDL_LOG_PRIORITY_DEBUG,
|
||||
warning = ext.SDL_LOG_PRIORITY_WARN,
|
||||
};
|
||||
|
||||
///
|
||||
/// [OpenError.NotFound] is a catch-all for when a file could not be located to be opened. This
|
||||
/// may be as simple as it doesn't exist or the because the underlying file-system will not /
|
||||
/// cannot give access to it at this time.
|
||||
///
|
||||
pub const OpenError = error {
|
||||
NotFound,
|
||||
};
|
||||
|
||||
///
|
||||
/// [OpenMode.readonly] indicates that an existing file is opened in a read-only state,
|
||||
/// disallowing write access.
|
||||
///
|
||||
/// [OpenMode.overwrite] indicates that an empty file has been created or an existing file has
|
||||
/// been completely overwritten into.
|
||||
///
|
||||
/// [OpenMode.append] indicates that an existing file that has been opened for reading from and
|
||||
/// writing to on the end of existing data.
|
||||
///
|
||||
pub const OpenMode = enum {
|
||||
readonly,
|
||||
overwrite,
|
||||
append,
|
||||
};
|
||||
|
||||
///
|
||||
///
|
||||
///
|
||||
pub const RunError = error {
|
||||
InitFailure,
|
||||
AlreadyRunning,
|
||||
};
|
||||
|
||||
///
|
||||
/// [SeekOrigin.head] indicates that a seek operation will seek from the offset origin of the
|
||||
/// file beginning, or "head".
|
||||
///
|
||||
/// [SeekOrigin.tail] indicates that a seek operation will seek from the offset origin of the
|
||||
/// file end, or "tail".
|
||||
///
|
||||
/// [SeekOrigin.cursor] indicates that a seek operation will seek from the current position of
|
||||
/// the file cursor.
|
||||
///
|
||||
pub const SeekOrigin = enum {
|
||||
head,
|
||||
tail,
|
||||
cursor,
|
||||
};
|
||||
|
||||
///
|
||||
///
|
||||
///
|
||||
pub fn close(file_access: *FileAccess) void {
|
||||
if (!ext.SDL_RWclose(@ptrCast(*ext.SDL_RWops,
|
||||
@alignCast(@alignOf(ext.SDL_RWops), file_access)))) {
|
||||
|
||||
ext.SDL_LogCritical(ext.SDL_LOG_CATEGORY_APPLICATION,
|
||||
"Failed to close file - may have been already closed");
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
///
|
||||
///
|
||||
pub fn runGraphics(comptime Errors: anytype, comptime run: GraphicsRunner(Errors)) Errors!void {
|
||||
pub fn log(kind: LogKind, message: []const u8) void {
|
||||
ext.SDL_LogMessage(ext.SDL_LOG_CATEGORY_APPLICATION,
|
||||
@enumToInt(kind), "%.*s", message.len, message.ptr);
|
||||
}
|
||||
|
||||
///
|
||||
///
|
||||
///
|
||||
pub fn open(mode: OpenMode, file_system_path: FileSystem.Path) OpenError!*FileAccess {
|
||||
switch (file_system_path.file_system) {
|
||||
.data => {
|
||||
// TODO: Implement
|
||||
return error.NotFound;
|
||||
},
|
||||
|
||||
.user => {
|
||||
var path_buffer = std.mem.zeroes([4096]u8);
|
||||
var path = stack.Fixed(u8){.buffer = path_buffer[0 .. ]};
|
||||
|
||||
path.pushAll("/home/kayomn/.local/share") catch return error.NotFound;
|
||||
|
||||
if (file_system_path.write(path.writer())) return error.NotFound;
|
||||
|
||||
return @ptrCast(*FileAccess, ext.SDL_RWFromFile(&path_buffer, switch (mode) {
|
||||
.readonly => "rb",
|
||||
.overwrite => "wb",
|
||||
.append => "ab",
|
||||
})) orelse error.NotFound;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
///
|
||||
///
|
||||
pub fn runGraphics(comptime Errors: anytype, comptime run: GraphicsRunner(Errors)) (RunError || Errors)!void {
|
||||
if (ext.SDL_Init(ext.SDL_INIT_EVERYTHING) != 0) {
|
||||
ext.SDL_LogCritical(ext.SDL_LOG_CATEGORY_APPLICATION, "Failed to initialize runtime");
|
||||
|
||||
|
@ -669,6 +689,25 @@ pub fn runGraphics(comptime Errors: anytype, comptime run: GraphicsRunner(Errors
|
|||
|
||||
defer ext.SDL_DestroyRenderer(renderer);
|
||||
|
||||
var file_system = FileSystem{
|
||||
.user = .{.prefix = create_pref_path: {
|
||||
const path = ext.SDL_GetPrefPath("ona", "ona") orelse {
|
||||
ext.SDL_LogCritical(ext.SDL_LOG_CATEGORY_APPLICATION,
|
||||
"Failed to load user path");
|
||||
|
||||
return error.InitFailure;
|
||||
};
|
||||
|
||||
break: create_pref_path path[0 .. std.mem.len(path)];
|
||||
}},
|
||||
|
||||
.data = .{.prefix = "./"},
|
||||
};
|
||||
|
||||
defer {
|
||||
ext.SDL_free(file_system.user.prefix);
|
||||
}
|
||||
|
||||
var event_loop = EventLoop.Implementation.init() catch |err| {
|
||||
switch (err) {
|
||||
error.OutOfMemory => ext.SDL_LogCritical(ext.SDL_LOG_CATEGORY_APPLICATION,
|
||||
|
|
Loading…
Reference in New Issue