Application Context Implementation #4

Closed
kayomn wants to merge 93 commits from event-loop-dev into main
2 changed files with 196 additions and 158 deletions
Showing only changes of commit 979c2a73f3 - Show all commits

View File

@ -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();
}
}

View File

@ -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,
};
@ -587,6 +494,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,