diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5176deb --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.zig-cache/ +zig-out/ +*.fap \ No newline at end of file diff --git a/build.zig b/build.zig new file mode 100644 index 0000000..3ae6248 --- /dev/null +++ b/build.zig @@ -0,0 +1,37 @@ +const std = @import("std"); +const Target = std.Target; +const Build = std.Build; + +pub fn build(b: *Build) void { + // Module + const flipperzero = b.addModule("flipperzero", .{ + .root_source_file = b.path("lib/flipperzero/flipperzero.zig"), + }); + + // Configuration + const optimize = b.standardOptimizeOption(.{}); + const target_query = .{ .cpu_arch = .thumb, .cpu_model = .{ .explicit = &Target.arm.cpu.cortex_m4 }, .os_tag = .freestanding, .abi = .none }; + + // Relocatable elf + const elf = b.addObject(.{ + .name = "zlipper.fap", + .root_source_file = b.path("src/start.zig"), + .target = b.resolveTargetQuery(target_query), + .optimize = optimize, + .strip = true, + .pic = true, + }); + + elf.root_module.addImport("flipperzero", flipperzero); + elf.setLinkerScript(b.path("linker.ld")); + elf.setVerboseLink(true); + + elf.build_id = .none; + elf.bundle_compiler_rt = false; + elf.link_gc_sections = true; + elf.linkage = .static; + + b.default_step.dependOn(&elf.step); + const install_artifact = b.addInstallArtifact(elf, .{ .dest_dir = .{ .override = .{ .bin = {} } } }); + b.getInstallStep().dependOn(&install_artifact.step); +} diff --git a/lib/flipperzero/atomic.zig b/lib/flipperzero/atomic.zig new file mode 100644 index 0000000..f1d2624 --- /dev/null +++ b/lib/flipperzero/atomic.zig @@ -0,0 +1,24 @@ +const flipperzero = @import("flipperzero.zig"); +const c = flipperzero.c; + +pub fn FuriMessageQueue(comptime T: type) type { + return extern struct { + const Self = @This(); + + pub fn create(msg_count: u32) *Self { + return @ptrCast(c.furi_message_queue_alloc(msg_count, @sizeOf(T))); + } + + pub fn destroy(self: *Self) void { + c.furi_message_queue_free(self); + } + + pub fn get(self: *Self, msg_ptr: *T, timeout: u32) void { + _ = c.furi_message_queue_get(self, msg_ptr, timeout); + } + + pub fn put(self: *Self, msg_ptr: *const T, timeout: u32) void { + _ = c.furi_message_queue_put(self, msg_ptr, timeout); + } + }; +} diff --git a/lib/flipperzero/c.zig b/lib/flipperzero/c.zig new file mode 100644 index 0000000..64a01a7 --- /dev/null +++ b/lib/flipperzero/c.zig @@ -0,0 +1,154 @@ +// C +pub extern fn aligned_malloc(len: usize, alignment: usize) ?*anyopaque; +pub extern fn aligned_free(ptr: *anyopaque) void; +pub extern fn malloc(size: usize) ?*anyopaque; +pub extern fn memset(dest: *anyopaque, c: i32, count: usize) *anyopaque; +pub extern fn free(memblock: *anyopaque) void; +pub extern fn snprintf(buffer: [*:0]u8, count: usize, format: [*:0]const u8, ...) i32; + +// Canvas +pub extern fn canvas_clear(canvas: *anyopaque) void; +pub extern fn canvas_draw_str(canvas: *anyopaque, x: u8, y: u8, str: [*:0]const u8) void; +pub extern fn canvas_set_font(canvas: *anyopaque, font: Font) void; + +// Furi +pub const FuriMutexType = enum(u8) { + FuriMutexTypeNormal, + FuriMutexTypeRecursive, +}; +pub const FuriWait = enum(u32) { + FuriWaitForever = 0xFFFFFFFF, +}; +pub const FuriStatus = enum(i32) { FuriStatusOk = 0, FuriStatusError = -1, FuriStatusErrorTimeout = -2, FuriStatusErrorResource = -3, FuriStatusErrorParameter = -4, FuriStatusErrorNoMemory = -5, FuriStatusErrorISR = -6, FuriStatusReserved = 0x7FFFFFFF }; + +pub extern fn furi_delay_us(microseconds: u32) void; +pub extern fn furi_message_queue_alloc(msg_count: u32, msg_size: u32) *anyopaque; +pub extern fn furi_message_queue_free(instance: *anyopaque) void; +pub extern fn furi_message_queue_get(instance: *anyopaque, msg_ptr: *anyopaque, timeout: u32) FuriStatus; +pub extern fn furi_message_queue_put(instance: *anyopaque, msg_ptr: *const anyopaque, timeout: u32) FuriStatus; +pub extern fn furi_mutex_alloc(type: FuriMutexType) *anyopaque; +pub extern fn furi_mutex_free(instance: *anyopaque) void; +pub extern fn furi_record_close(name: [*:0]const u8) void; +pub extern fn furi_record_open(name: [*:0]const u8) *anyopaque; + +// GUI +pub const RECORD_GUI = "gui"; +pub const Font = enum(u8) { + FontPrimary, + FontSecondary, + FontKeyboard, + FontBigNumbers, + FontTotalNumber, +}; +pub const GuiLayer = enum(u8) { + GuiLayerDesktop, + GuiLayerWindow, + GuiLayerStatusBarTop, // Custom on RogueMaster + GuiLayerStatusBarLeft, + GuiLayerStatusBarLeftSlim, // Custom on RogueMaster + GuiLayerStatusBarRight, + GuiLayerStatusBarRightSlim, // Custom on RogueMaster + GuiLayerFullscreen, + GuiLayerMAX, +}; + +pub extern fn gui_add_view_port(gui: *anyopaque, view_port: *anyopaque, layer: GuiLayer) void; +pub extern fn gui_remove_view_port(gui: *anyopaque, view_port: *anyopaque) void; + +// Input +pub const InputEvent = extern struct { + sequence: u32 align(4), + key: InputKey align(1), + type: InputType align(1), +}; +pub const InputKey = enum(u8) { + InputKeyUp, + InputKeyDown, + InputKeyRight, + InputKeyLeft, + InputKeyOk, + InputKeyBack, + InputKeyMAX, +}; +pub const InputType = enum(u8) { + InputTypePress, + InputTypeRelease, + InputTypeShort, + InputTypeLong, + InputTypeRepeat, + InputTypeMAX, +}; + +pub extern fn input_get_key_name(key: InputKey) [*:0]const u8; +pub extern fn input_get_type_name(type: InputType) [*:0]const u8; + +// SceneManager +pub extern fn scene_manager_alloc() *anyopaque; +pub extern fn scene_manager_free(scene_manager: *anyopaque) void; +pub extern fn scene_manager_get_scene_state(scene_manager: *anyopaque, scene_id: u32) u32; +pub extern fn scene_manager_handle_back_event(scene_manager: *anyopaque) bool; +pub extern fn scene_manager_handle_custom_event(scene_manager: *anyopaque, custom_event: u32) bool; +pub extern fn scene_manager_handle_tick_event(scene_manager: *anyopaque) void; +pub extern fn scene_manager_has_previous_scene(scene_manager: *const anyopaque, scene_id: u32) bool; +pub extern fn scene_manager_next_scene(scene_manager: *anyopaque, next_scene_id: u32) bool; +pub extern fn scene_manager_previous_scene(scene_manager: *anyopaque) bool; +pub extern fn scene_manager_search_and_switch_to_another_scene(scene_manager: *anyopaque, scene_id: u32) bool; +pub extern fn scene_manager_search_and_switch_to_previous_scene(scene_manager: *anyopaque, scene_id: u32) bool; +pub extern fn scene_manager_search_and_switch_to_previous_scene_one_of(scene_manager: *anyopaque, scene_ids: *const u32, scene_ids_size: usize) bool; +pub extern fn scene_manager_set_scene_state(scene_manager: *anyopaque, scene_id: u32, state: u32) void; +pub extern fn scene_manager_stop(scene_manager: *anyopaque) void; + +// Storage +pub const RECORD_STORAGE = "storage"; + +// View +pub const ViewModelType = enum(u8) { + ViewModelTypeNone, + ViewModelTypeLockFree, + ViewModelTypeLocking, +}; + +pub extern fn view_alloc() *anyopaque; +pub extern fn view_allocate_model(view: *anyopaque, type: ViewModelType, size: usize) void; +pub extern fn view_commit_model(view: *anyopaque, update: bool) void; +pub extern fn view_free(view: *anyopaque) void; +pub extern fn view_free_model(view: *anyopaque) void; +pub extern fn view_get_model(view: *anyopaque) *anyopaque; + +// Viewdispatcher +pub const ViewDispatcherType = enum(u8) { + ViewDispatcherTypeDesktop, + ViewDispatcherTypeWindow, + ViewDispatcherTypeFullscreen, +}; + +pub extern fn view_dispatcher_add_view(view_dispatcher: *anyopaque, view_id: u32, view: *anyopaque) void; +pub extern fn view_dispatcher_alloc() *anyopaque; +pub extern fn view_dispatcher_attach_to_gui(view_dispatcher: *anyopaque, gui: *anyopaque, type: ViewDispatcherType) void; +pub extern fn view_dispatcher_enable_queue(view_dispatcher: *anyopaque) void; +pub extern fn view_dispatcher_free(view_dispatcher: *anyopaque) void; +pub extern fn view_dispatcher_get_event_loop(view_dispatcher: *anyopaque) *anyopaque; +pub extern fn view_dispatcher_remove_view(view_dispatcher: *anyopaque, view_id: u32) void; +pub extern fn view_dispatcher_run(view_dispatcher: *anyopaque) void; +pub extern fn view_dispatcher_send_custom_event(view_dispatcher: *anyopaque, event: u32) void; +pub extern fn view_dispatcher_send_to_back(view_dispatcher: *anyopaque) void; +pub extern fn view_dispatcher_send_to_front(view_dispatcher: *anyopaque) void; +pub const ViewDispatcherCustomEventCallback = *const fn (?*anyopaque, event: u32) callconv(.C) void; +pub extern fn view_dispatcher_set_custom_event_callback(view_dispatcher: *anyopaque, callback: ViewDispatcherCustomEventCallback) void; +pub extern fn view_dispatcher_set_event_callback_context(view_dispatcher: *anyopaque, context: ?*anyopaque) void; +pub const ViewDispatcherNavigationEventCallback = *const fn (?*anyopaque) callconv(.C) void; +pub extern fn view_dispatcher_set_navigation_event_callback(view_dispatcher: *anyopaque, callback: ViewDispatcherNavigationEventCallback) void; +pub const ViewDispatcherTickEventCallback = *const fn (*anyopaque) callconv(.C) void; +pub extern fn view_dispatcher_set_tick_event_callback(view_dispatcher: *anyopaque, callback: ViewDispatcherTickEventCallback, tick_period: u32) void; +pub extern fn view_dispatcher_stop(view_dispatcher: *anyopaque) void; +pub extern fn view_dispatcher_switch_to_view(view_dispatcher: *anyopaque, view_id: u32) void; + +// Viewport +pub extern fn view_port_alloc() *anyopaque; +pub const ViewPortDrawCallback = *const fn (*anyopaque, ?*anyopaque) callconv(.C) void; +pub extern fn view_port_draw_callback_set(view_port: *anyopaque, draw_callback: ViewPortDrawCallback, context: ?*anyopaque) void; +pub extern fn view_port_enabled_set(view_port: *anyopaque, enabled: bool) void; +pub extern fn view_port_free(view_port: *anyopaque) void; +pub const ViewPortInputCallback = *const fn (*InputEvent, ?*anyopaque) callconv(.C) void; +pub extern fn view_port_input_callback_set(view_port: *anyopaque, input_callback: ViewPortInputCallback, context: ?*anyopaque) void; +pub extern fn view_port_update(view_port: *anyopaque) void; diff --git a/lib/flipperzero/flipperzero.zig b/lib/flipperzero/flipperzero.zig new file mode 100644 index 0000000..c1c0374 --- /dev/null +++ b/lib/flipperzero/flipperzero.zig @@ -0,0 +1,7 @@ +pub const atomic = @import("atomic.zig"); +pub const c = @import("c.zig"); +pub const gui = @import("gui.zig"); +pub const heap = @import("heap.zig"); +pub const manifest = @import("manifest.zig"); +pub const storage = @import("storage.zig"); +pub const thread = @import("thread.zig"); diff --git a/lib/flipperzero/gui.zig b/lib/flipperzero/gui.zig new file mode 100644 index 0000000..1db2157 --- /dev/null +++ b/lib/flipperzero/gui.zig @@ -0,0 +1,227 @@ +const flipperzero = @import("flipperzero.zig"); +const c = flipperzero.c; + +pub const Canvas = extern struct { + const Self = @This(); + + pub fn clear(self: *Self) void { + c.canvas_clear(@ptrCast(self)); + } + + pub fn drawStr(self: *Self, x: u8, y: u8, str: [*:0]const u8) void { + c.canvas_draw_str(@ptrCast(self), x, y, str); + } + + pub fn setFont(self: *Self, font: c.Font) void { + c.canvas_set_font(@ptrCast(self), font); + } +}; + +pub const Gui = extern struct { + const Self = @This(); + + pub fn open() *Self { + return @ptrCast(c.furi_record_open(c.RECORD_GUI)); + } + + pub fn close(self: *Self) void { + _ = self; + c.furi_record_close(c.RECORD_GUI); + } + + pub fn addViewPort(self: *Self, view_port: *ViewPort, layer: c.GuiLayer) void { + c.gui_add_view_port(self, view_port, layer); + } + + pub fn removeViewPort(self: *Self, view_port: *ViewPort) void { + c.gui_remove_view_port(self, view_port); + } +}; + +pub const SceneManager = extern struct { + const Self = @This(); + + pub fn create() *Self { + return @ptrCast(c.scene_manager_alloc()); + } + + pub fn destroy(self: *Self) void { + c.scene_manager_free(self); + } + + pub fn getSceneState(self: *Self, scene_id: u32) u32 { + return c.scene_manager_get_scene_state(self, scene_id); + } + + pub fn handleBackEvent(self: *Self) bool { + return c.scene_manager_handle_back_event(self); + } + + pub fn handleCustomEvent(self: *Self, custom_event: u32) bool { + return c.scene_manager_handle_custom_event(self, custom_event); + } + + pub fn handleTickEvent(self: *Self) void { + c.scene_manager_handle_tick_event(self); + } + + pub fn hasPreviousScene(self: *Self, scene_id: u32) bool { + return c.scene_manager_has_previous_scene(self, scene_id); + } + + pub fn nextScene(self: *Self, next_scene_id: u32) void { + c.scene_manager_next_scene(self, next_scene_id); + } + + pub fn previousScene(self: *Self) bool { + return c.scene_manager_previous_scene(self); + } + + pub fn searchAndSwitchToAnotherScene(self: *Self, scene_id: u32) bool { + return c.scene_manager_search_and_switch_to_another_scene(self, scene_id); + } + + pub fn searchAndSwitchToPreviousScene(self: *Self, scene_id: u32) bool { + return c.scene_manager_search_and_switch_to_previous_scene(self, scene_id); + } + + pub fn searchAndSwitchToPreviousSceneOneOf(self: *Self, scene_ids: *const u32, scene_ids_size: usize) bool { + return c.scene_manager_search_and_switch_to_previous_scene_one_of(self, scene_ids, scene_ids_size); + } + + pub fn setSceneState(self: *Self, scene_id: u32, state: u32) void { + c.scene_manager_set_scene_state(self, scene_id, state); + } + + pub fn stop(self: *Self) void { + c.scene_manager_stop(self); + } +}; + +pub const View = extern struct { + const Self = @This(); + + pub fn create() *Self { + return @ptrCast(c.view_alloc()); + } + + pub fn allocateModel(self: *Self, type_: c.ViewModelType, size: usize) void { + c.view_allocate_model(self, type_, size); + } + + pub fn commitModel(self: *Self, update: bool) void { + c.view_commit_model(self, update); + } + + pub fn destroy(self: *Self) void { + c.view_free(self); + } + + pub fn freeModel(self: *Self) void { + c.view_free_model(self); + } + + pub fn getModel(self: *Self) *anyopaque { + return c.view_get_model(self); + } +}; + +pub const ViewDispatcher = extern struct { + const Self = @This(); + + pub fn create() *Self { + return @ptrCast(c.view_dispatcher_alloc()); + } + + pub fn addView(self: *Self, view_id: u32, view: *View) void { + c.view_dispatcher_add_view(self, view_id, view); + } + + pub fn attachToGui(self: *Self, gui: *Gui, type_: c.ViewDispatcherType) void { + c.view_dispatcher_attach_to_gui(self, gui, type_); + } + + pub fn enableQueue(self: *Self) void { + c.view_dispatcher_enable_queue(self); + } + + pub fn destroy(self: *Self) void { + c.view_dispatcher_free(self); + } + + pub fn getEventLoop(self: *Self) *anyopaque { + return c.view_dispatcher_get_event_loop(self); + } + + pub fn removeView(self: *Self, view_id: u32) void { + c.view_dispatcher_remove_view(self, view_id); + } + + pub fn run(self: *Self) void { + c.view_dispatcher_run(self); + } + + pub fn sendCustomEvent(self: *Self, event: u32) void { + c.view_dispatcher_send_custom_event(self, event); + } + + pub fn sendToBack(self: *Self) void { + c.view_dispatcher_send_to_back(self); + } + + pub fn sendToFront(self: *Self) void { + c.view_dispatcher_send_to_front(self); + } + + pub fn setCustomEventCallback(self: *Self, callback: c.ViewDispatcherCustomEventCallback) void { + c.view_dispatcher_set_custom_event_callback(self, callback); + } + + pub fn setEventCallbackContext(self: *Self, context: ?*anyopaque) void { + c.view_dispatcher_set_event_callback_context(self, context orelse null); + } + + pub fn setNavigationEventCallback(self: *Self, callback: c.ViewDispatcherNavigationEventCallback) void { + c.view_dispatcher_set_navigation_event_callback(self, callback); + } + + pub fn setTickEventCallback(self: *Self, callback: c.ViewDispatcherTickEventCallback, tick_period: u32) void { + c.view_dispatcher_set_tick_event_callback(self, callback, tick_period); + } + + pub fn stop(self: *Self) void { + c.view_dispatcher_stop(self); + } + + pub fn switchToView(self: *Self, view_id: u32) void { + c.view_dispatcher_switch_to_view(self, view_id); + } +}; + +pub const ViewPort = extern struct { + const Self = @This(); + + pub fn create() *Self { + return @ptrCast(c.view_port_alloc()); + } + + pub fn destroy(self: *Self) void { + c.view_port_free(self); + } + + pub fn update(self: *Self) void { + c.view_port_update(self); + } + + pub fn setDrawCallback(self: *Self, callback: c.ViewPortDrawCallback, context: ?*anyopaque) void { + c.view_port_draw_callback_set(self, callback, context orelse null); + } + + pub fn setInputCallback(self: *Self, callback: c.ViewPortInputCallback, context: ?*anyopaque) void { + c.view_port_input_callback_set(self, callback, context orelse null); + } + + pub fn setEnabled(self: *Self, enabled: bool) void { + c.view_port_enabled_set(self, enabled); + } +}; diff --git a/lib/flipperzero/heap.zig b/lib/flipperzero/heap.zig new file mode 100644 index 0000000..0a1b1d9 --- /dev/null +++ b/lib/flipperzero/heap.zig @@ -0,0 +1,48 @@ +const std = @import("std"); +const Allocator = std.mem.Allocator; +const flipperzero = @import("flipperzero.zig"); +const c = flipperzero.c; + +pub const FuriAllocator = struct { + fn alloc( + _: *anyopaque, + len: usize, + log2_align: u8, + return_address: usize, + ) ?[*]u8 { + _ = return_address; + const alignment = @as(usize, 1) << @intCast(log2_align); + return @ptrCast(c.aligned_malloc(len, alignment)); + } + + fn resize( + _: *anyopaque, + buf: []u8, + log2_buf_align: u8, + new_len: usize, + return_address: usize, + ) bool { + _ = return_address; + _ = log2_buf_align; + return new_len <= buf.len; + } + + fn free( + _: *anyopaque, + buf: []u8, + log2_buf_align: u8, + return_address: usize, + ) void { + _ = return_address; + _ = log2_buf_align; + c.aligned_free(buf.ptr); + } +}; + +const furi_allocator_vtable = Allocator.VTable{ + .alloc = FuriAllocator.alloc, + .resize = FuriAllocator.resize, + .free = FuriAllocator.free, +}; + +pub const furi_allocator = Allocator{ .ptr = undefined, .vtable = &furi_allocator_vtable }; diff --git a/lib/flipperzero/manifest.zig b/lib/flipperzero/manifest.zig new file mode 100644 index 0000000..3418e29 --- /dev/null +++ b/lib/flipperzero/manifest.zig @@ -0,0 +1,39 @@ +const std = @import("std"); +const mem = std.mem; + +const MANIFEST_MAGIC: u32 = 0x52474448; +const API_VERSION: u32 = 0x00480001; // API_VERSION 72.1 + +const FlipperManifestBase = extern struct { + manifest_magic: u32 align(1), + manifest_version: u32 align(1), + api_version: u32 align(1), + hardware_target_id: u16 align(1), +}; + +const FlipperApplicationManifestV1 = extern struct { + base: FlipperManifestBase, + stack_size: u16 align(1), + app_version: u32 align(1), + name: [32]u8 align(1), + has_icon: u8 align(1), + icon: [32]u8 align(1), +}; + +const FlipperApplicationManifest = FlipperApplicationManifestV1; + +pub fn createApplicationManifest(stack_size: u16, app_version: u32, comptime name: [*:0]const u8, has_icon: u8, icon: ?[32]u8) FlipperApplicationManifest { + return FlipperApplicationManifest{ + .base = FlipperManifestBase{ + .manifest_magic = MANIFEST_MAGIC, + .manifest_version = 1, + .api_version = API_VERSION, + .hardware_target_id = 7, + }, + .stack_size = stack_size, + .app_version = app_version, + .name = (mem.span(name) ++ mem.zeroes([32 - mem.len(name)]u8)).*, + .has_icon = has_icon, + .icon = icon orelse mem.zeroes([32]u8), + }; +} diff --git a/lib/flipperzero/storage.zig b/lib/flipperzero/storage.zig new file mode 100644 index 0000000..5fa4b9f --- /dev/null +++ b/lib/flipperzero/storage.zig @@ -0,0 +1,15 @@ +const flipperzero = @import("flipperzero.zig"); +const c = flipperzero.c; + +pub const Storage = extern struct { + const Self = @This(); + + pub fn open() *Self { + return @ptrCast(c.furi_record_open(c.RECORD_STORAGE)); + } + + pub fn close(self: *Self) void { + _ = self; + c.furi_record_close(c.RECORD_STORAGE); + } +}; \ No newline at end of file diff --git a/lib/flipperzero/thread.zig b/lib/flipperzero/thread.zig new file mode 100644 index 0000000..6cc728b --- /dev/null +++ b/lib/flipperzero/thread.zig @@ -0,0 +1,35 @@ +const flipperzero = @import("flipperzero.zig"); +const c = flipperzero.c; + +pub const FuriMutex = struct { + const Self = @This(); + + pub fn create(type_: c.FuriMutexType) *Self { + return @ptrCast(c.furi_mutex_alloc(type_)); + } + + pub fn destroy(self: *Self) void { + c.furi_mutex_free(self); + } +}; + +// pub const FuriThread = struct { +// data: *anyopaque, + +// const Self = @This(); + +// pub fn create() Self { +// return Self{ +// .data = c.furi_thread_alloc() +// }; +// } + +// pub fn join(self: Self) void { +// c.furi_thread_join(self.data); +// } + +// pub fn destroy(self: Self) void { +// self.join(); +// c.furi_thread_free(self.data); +// } +// }; diff --git a/linker.ld b/linker.ld new file mode 100644 index 0000000..6203b4e --- /dev/null +++ b/linker.ld @@ -0,0 +1,51 @@ +ENTRY(_start); + +SECTIONS +{ + .text 0x00000000 : ALIGN(4) + { + KEEP(*(.init)); + *(.text .text.*); + KEEP(*(.fini)); + } + + .rodata : + { + *(.rodata .rodata.*); + } + + .bss : + { + *(.bss .bss.*); + } + + .data : + { + *(.data .data.*); + } + + .ARM.attributes : + { + *(.ARM.attributes .ARM.attributes.*); + } + + .fapmeta (INFO) : + { + KEEP(*(.fapmeta)); + } + + /DISCARD/ : + { + *(.text.strlen); + + /* LLVM bitcode should never be in the binary */ + *(.llvmbc .llvmcmd); + + /* Unwinding is not supported */ + *(.ARM.exidx .ARM.exidx.*); + + *(.comment .comment.*); + *(.note .note.*); + *(.debug*); + } +} \ No newline at end of file diff --git a/src/app.zig b/src/app.zig new file mode 100644 index 0000000..f627296 --- /dev/null +++ b/src/app.zig @@ -0,0 +1,263 @@ +// Imports +const std = @import("std"); +const fz = @import("flipperzero"); + +// const Zlipper = struct { +// allocator: std.mem.Allocator, +// mutex: *anyopaque, +// gui: *anyopaque, +// view_port: *anyopaque, +// input_queue: *anyopaque, +// input_event: ?*c.InputEvent, +// running: bool, +// acc: u32, + +// const Self = @This(); + +// pub fn init(allocator: std.mem.Allocator) *Self { +// const self = allocator.create(Self) catch unreachable; +// self.* = Self{ +// .allocator = allocator, +// .mutex = c.furi_mutex_alloc(c.FuriMutexType.FuriMutexTypeNormal), +// .gui = c.furi_record_open(c.RECORD_GUI), +// .view_port = c.view_port_alloc(), +// .input_queue = c.furi_message_queue_alloc(8, @sizeOf(c.InputEvent)), +// .input_event = null, +// .running = true, +// .acc = 0, +// }; + +// c.view_port_draw_callback_set(self.view_port, Self.draw_callback, @ptrCast(self)); +// c.view_port_input_callback_set(self.view_port, Self.input_callback, @ptrCast(self)); +// c.gui_add_view_port(self.gui, self.view_port, c.GuiLayer.GuiLayerFullscreen); + +// return self; +// } + +// pub fn deinit(self: *Self) void { +// c.view_port_enabled_set(self.view_port, false); +// c.gui_remove_view_port(self.gui, self.view_port); +// c.view_port_free(self.view_port); +// c.furi_message_queue_free(self.input_queue); +// c.furi_mutex_free(self.mutex); +// c.furi_record_close(c.RECORD_GUI); +// self.allocator.destroy(self); +// } + +// pub fn update(self: *Self) void { +// var event: c.InputEvent = undefined; +// _ = c.furi_message_queue_get(self.input_queue, @ptrCast(&event), @intFromEnum(c.FuriWait.FuriWaitForever)); + +// self.input_event = &event; + +// if (event.type == c.InputType.InputTypeShort and event.key == c.InputKey.InputKeyBack) { +// self.running = !self.running; +// } + +// c.view_port_update(self.view_port); +// self.acc += 1; +// } + +// fn draw_callback(canvas: *anyopaque, context: ?*anyopaque) callconv(.C) void { +// const self: *Self = @alignCast(@ptrCast(context.?)); + +// var c_buf = std.mem.zeroes([32:0]u8); +// _ = c.snprintf(&c_buf, 32, "snprintf: %d", self.acc); + +// var zig_buf = std.mem.zeroes([32:0]u8); +// _ = std.fmt.bufPrint(&zig_buf, "bufPrint: {}", .{self.acc}) catch return; + +// const alloc_buf = std.fmt.allocPrintZ(self.allocator, "allocPrint: {}", .{self.acc}) catch return; +// defer self.allocator.free(alloc_buf); + +// c.canvas_clear(canvas); +// c.canvas_set_font(canvas, c.Font.FontPrimary); +// c.canvas_draw_str(canvas, 0, 10, &c_buf); +// c.canvas_draw_str(canvas, 0, 20, &zig_buf); +// c.canvas_draw_str(canvas, 0, 30, alloc_buf); + +// if (self.input_event) |input_event| { +// const type_buf = std.fmt.allocPrintZ(self.allocator, "InputType: {s}", .{c.input_get_type_name(input_event.type)}) catch return; +// defer self.allocator.free(type_buf); +// c.canvas_draw_str(canvas, 0, 40, type_buf); + +// const key_buf = std.fmt.allocPrintZ(self.allocator, "InputKey: {s}", .{c.input_get_key_name(input_event.key)}) catch return; +// defer self.allocator.free(key_buf); +// c.canvas_draw_str(canvas, 0, 50, key_buf); +// } +// } + +// fn input_callback(input_event: *c.InputEvent, context: ?*anyopaque) callconv(.C) void { +// const self: *Self = @alignCast(@ptrCast(context.?)); + +// _ = c.furi_message_queue_put(self.input_queue, input_event, @intFromEnum(c.FuriWait.FuriWaitForever)); +// } +// }; + +const Zlipper = struct { + allocator: std.mem.Allocator, + mutex: *fz.thread.FuriMutex, + gui: *fz.gui.Gui, + view_port: *fz.gui.ViewPort, + input_queue: *fz.atomic.FuriMessageQueue(fz.c.InputEvent), + input_event: ?*const fz.c.InputEvent, + running: bool, + acc: u32, + + const Self = @This(); + + pub fn init(allocator: std.mem.Allocator) *Self { + const self = allocator.create(Self) catch unreachable; + self.* = Self{ + .allocator = allocator, + .mutex = fz.thread.FuriMutex.create(fz.c.FuriMutexType.FuriMutexTypeNormal), + .gui = fz.gui.Gui.open(), + .view_port = fz.gui.ViewPort.create(), + .input_queue = fz.atomic.FuriMessageQueue(fz.c.InputEvent).create(8), + .input_event = null, + .running = true, + .acc = 0, + }; + + self.view_port.setDrawCallback(Self.draw_callback, @ptrCast(self)); + self.view_port.setInputCallback(Self.input_callback, @ptrCast(self)); + self.gui.addViewPort(self.view_port, fz.c.GuiLayer.GuiLayerFullscreen); + + return self; + } + + pub fn deinit(self: *Self) void { + self.view_port.setEnabled(false); + self.gui.removeViewPort(self.view_port); + self.view_port.destroy(); + self.input_queue.destroy(); + self.mutex.destroy(); + self.gui.close(); + self.allocator.destroy(self); + } + + pub fn update(self: *Self) void { + var event: fz.c.InputEvent = undefined; + self.input_queue.get(&event, @intFromEnum(fz.c.FuriWait.FuriWaitForever)); + + self.input_event = &event; + + if (event.type == fz.c.InputType.InputTypeShort and event.key == fz.c.InputKey.InputKeyBack) { + self.running = !self.running; + } + + self.view_port.update(); + self.acc += 1; + } + + fn draw_callback(canvas_: *anyopaque, context: ?*anyopaque) callconv(.C) void { + const self: *Self = @alignCast(@ptrCast(context.?)); + const canvas: *fz.gui.Canvas = @ptrCast(canvas_); + + var c_buf = std.mem.zeroes([32:0]u8); + _ = fz.c.snprintf(&c_buf, 32, "snprintf: %d", self.acc); + + var zig_buf = std.mem.zeroes([32:0]u8); + _ = std.fmt.bufPrint(&zig_buf, "bufPrint: {}", .{self.acc}) catch return; + + const alloc_buf = std.fmt.allocPrintZ(self.allocator, "allocPrint: {}", .{self.acc}) catch return; + defer self.allocator.free(alloc_buf); + + canvas.clear(); + canvas.setFont(fz.c.Font.FontPrimary); + canvas.drawStr(0, 10, &c_buf); + canvas.drawStr(0, 20, &zig_buf); + canvas.drawStr(0, 30, alloc_buf); + + if (self.input_event) |input_event| { + const type_buf = std.fmt.allocPrintZ(self.allocator, "InputType: {s}", .{fz.c.input_get_type_name(input_event.type)}) catch return; + defer self.allocator.free(type_buf); + canvas.drawStr(0, 40, type_buf); + + const key_buf = std.fmt.allocPrintZ(self.allocator, "InputKey: {s}", .{fz.c.input_get_key_name(input_event.key)}) catch return; + defer self.allocator.free(key_buf); + canvas.drawStr(0, 50, key_buf); + } + } + + fn input_callback(input_event: *fz.c.InputEvent, context: ?*anyopaque) callconv(.C) void { + const self: *Self = @alignCast(@ptrCast(context.?)); + self.input_queue.put(input_event, @intFromEnum(fz.c.FuriWait.FuriWaitForever)); + } +}; + +// const ZlipperView = enum(u8) { +// MainMenu, +// FileBrowser, +// }; + +// const Zlipper = struct { +// allocator: std.mem.Allocator, +// gui: *fz.gui.Gui, +// storage: *fz.storage.Storage, +// scene_manager: *fz.gui.SceneManager, +// view_dispatcher: *fz.gui.ViewDispatcher, +// // file_browser_output: *fz.string. +// running: bool, + +// const Self = @This(); + +// pub fn init(allocator: std.mem.Allocator) *Self { +// const self = allocator.create(Self) catch unreachable; +// self.* = Self { +// .allocator = allocator, +// .gui = fz.gui.Gui.open(), +// .storage = fz.storage.Storage.open(), +// .scene_manager = fz.gui.SceneManager.create(), +// .view_dispatcher = fz.gui.ViewDispatcher.create(), +// .running = true, +// }; + +// self.view_dispatcher.enableQueue(); +// self.view_dispatcher.setEventCallbackContext(@ptrCast(self)); +// self.view_dispatcher.setCustomEventCallback(Self.customEventCallback); +// self.view_dispatcher.setNavigationEventCallback(Self.navigationEventCallback); +// self.view_dispatcher.attachToGui(self.gui, fz.c.ViewDispatcherType.ViewDispatcherTypeFullscreen); + + +// return self; +// } + +// pub fn customEventCallback(context: ?*anyopaque, event: u32) callconv(.C) void { +// const self: *Self = @alignCast(@ptrCast(context)); +// _ = self; +// _ = event; +// } + +// pub fn navigationEventCallback(context: ?*anyopaque) callconv(.C) void { +// const self: *Self = @alignCast(@ptrCast(context)); +// _ = self; +// } + +// pub fn update(self: *Self) void { +// self.running = false; +// } + +// pub fn deinit(self: *Self) void { +// self.view_dispatcher.removeView(ZlipperView.MainMenu); +// self.view_dispatcher.destroy(); +// self.scene_manager.destroy(); +// self.storage.close(); +// self.gui.close(); +// self.allocator.destroy(self); +// } +// }; + +// Application +pub fn main(args: *u8) i32 { + _ = args; + + var zlipper = Zlipper.init(fz.heap.furi_allocator); + defer zlipper.deinit(); + + while (zlipper.running) { + zlipper.update(); + } + + return 0; +} diff --git a/src/start.zig b/src/start.zig new file mode 100644 index 0000000..8b67e60 --- /dev/null +++ b/src/start.zig @@ -0,0 +1,12 @@ +// Imports +const flipperzero = @import("flipperzero"); +const app = @import("app.zig"); +const manifest = flipperzero.manifest; + +// Manifest +export const FAP_MANIFEST linksection(".fapmeta") = manifest.createApplicationManifest(2048, 1, "Zlipper", 0, null); + +// Entry function +export fn _start(args: *u8) i32 { + return app.main(args); +}