Init commit

This commit is contained in:
Ethanell 2024-04-09 22:37:23 +02:00
commit 5b1716c7d2
17 changed files with 656 additions and 0 deletions

23
.cargo/config.toml Normal file
View file

@ -0,0 +1,23 @@
[target.thumbv7em-none-eabihf]
rustflags = [
# CPU is Cortex-M4 (STM32WB55)
"-C", "target-cpu=cortex-m4",
# Size optimizations
"-C", "panic=abort",
"-C", "debuginfo=0",
"-C", "opt-level=z",
# LTO helps reduce binary size
"-C", "embed-bitcode=yes",
"-C", "lto=yes",
# Linker flags for relocatable binary
"-C", "link-args=--script=flipperzero-rt.ld --Bstatic --relocatable --discard-all --strip-all --lto-O3 --lto-whole-program-visibility",
# Required to link with `lld`
"-Z", "no-unique-section-names=yes",
]
[build]
target = "thumbv7em-none-eabihf"

9
.gitignore vendored Normal file
View file

@ -0,0 +1,9 @@
# Generated by Cargo
# will have compiled files and executables
/target
# These are backup files generated by rustfmt
**/*.rs.bk
src/icons/**/*.icon

265
Cargo.lock generated Normal file
View file

@ -0,0 +1,265 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "autocfg"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80"
[[package]]
name = "bitflags"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
[[package]]
name = "block-buffer"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
dependencies = [
"generic-array",
]
[[package]]
name = "byteorder"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "crypto-common"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
dependencies = [
"generic-array",
"typenum",
]
[[package]]
name = "digest"
version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer",
"crypto-common",
]
[[package]]
name = "enum-primitive-derive"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba7795da175654fe16979af73f81f26a8ea27638d8d9823d317016888a63dc4c"
dependencies = [
"num-traits",
"quote",
"syn 2.0.58",
]
[[package]]
name = "flipperzero"
version = "0.11.0"
source = "git+https://github.com/flipperzero-rs/flipperzero?branch=main#6f8f5f505b06218b3424e34f44e39aa965f744f3"
dependencies = [
"bitflags",
"digest",
"flipperzero-sys",
"flipperzero-test",
"lock_api",
"rand_core",
"ufmt",
]
[[package]]
name = "flipperzero-alloc"
version = "0.11.0"
source = "git+https://github.com/flipperzero-rs/flipperzero?branch=main#6f8f5f505b06218b3424e34f44e39aa965f744f3"
dependencies = [
"flipperzero-sys",
]
[[package]]
name = "flipperzero-rt"
version = "0.11.0"
source = "git+https://github.com/flipperzero-rs/flipperzero?branch=main#6f8f5f505b06218b3424e34f44e39aa965f744f3"
dependencies = [
"flipperzero-sys",
]
[[package]]
name = "flipperzero-sys"
version = "0.11.0"
source = "git+https://github.com/flipperzero-rs/flipperzero?branch=main#6f8f5f505b06218b3424e34f44e39aa965f744f3"
dependencies = [
"ufmt",
]
[[package]]
name = "flipperzero-test"
version = "0.11.0"
source = "git+https://github.com/flipperzero-rs/flipperzero?branch=main#6f8f5f505b06218b3424e34f44e39aa965f744f3"
dependencies = [
"flipperzero-sys",
"flipperzero-test-macros",
"ufmt",
]
[[package]]
name = "flipperzero-test-macros"
version = "0.11.0"
source = "git+https://github.com/flipperzero-rs/flipperzero?branch=main#6f8f5f505b06218b3424e34f44e39aa965f744f3"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "generic-array"
version = "0.14.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
dependencies = [
"typenum",
"version_check",
]
[[package]]
name = "glob"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
[[package]]
name = "hellow-world-rs"
version = "0.1.0"
dependencies = [
"byteorder",
"enum-primitive-derive",
"flipperzero",
"flipperzero-alloc",
"flipperzero-rt",
"flipperzero-sys",
"glob",
"num-traits",
]
[[package]]
name = "lock_api"
version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45"
dependencies = [
"autocfg",
"scopeguard",
]
[[package]]
name = "num-traits"
version = "0.2.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a"
dependencies = [
"autocfg",
]
[[package]]
name = "proc-macro2"
version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "syn"
version = "2.0.58"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "typenum"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
[[package]]
name = "ufmt"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a64846ec02b57e9108d6469d98d1648782ad6bb150a95a9baac26900bbeab9d"
dependencies = [
"ufmt-macros",
"ufmt-write",
]
[[package]]
name = "ufmt-macros"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d337d3be617449165cb4633c8dece429afd83f84051024079f97ad32a9663716"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "ufmt-write"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e87a2ed6b42ec5e28cc3b94c09982969e9227600b2e3dcbc1db927a84c06bd69"
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"

29
Cargo.toml Normal file
View file

@ -0,0 +1,29 @@
cargo-features = ["different-binary-name"]
[package]
name = "hellow-world-rs"
version = "0.1.0"
edition = "2021"
rust-version = "1.78.0"
autobins = false
autoexamples = false
autotests = false
autobenches = false
[[bin]]
name = "hellow-world-rs"
filename = "hellow-world-rs.fap"
bench = false
test = false
[dependencies]
flipperzero = { git = "https://github.com/flipperzero-rs/flipperzero", branch = "main" }
flipperzero-sys = { git = "https://github.com/flipperzero-rs/flipperzero", branch = "main" }
flipperzero-rt = { git = "https://github.com/flipperzero-rs/flipperzero", branch = "main" }
flipperzero-alloc = { git = "https://github.com/flipperzero-rs/flipperzero", branch = "main" }
enum-primitive-derive = "0.3.0"
num-traits = { version = "0.2.18", default-features = false }
[build-dependencies]
byteorder = "1.5.0"
glob = "0.3.1"

21
LICENSE Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 Lewis Westbury
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

5
README.md Normal file
View file

@ -0,0 +1,5 @@
# `flipper-zero-tutorial-app-rs` 🦀
Rust implementation of [flipper-zero-tutorial-app](https://github.com/instantiator/flipper-zero-tutorial-app) by [Lewis Westbury](https://github.com/instantiator).
Using the [Flipper Zero Rust SDK](https://github.com/flipperzero-rs/flipperzero), based on the [app template](https://github.com/flipperzero-rs/flipperzero-template).

24
build.rs Normal file
View file

@ -0,0 +1,24 @@
use std::{fs::File, io::Write, path::PathBuf, process::Command};
use byteorder::WriteBytesExt;
use glob::glob;
fn create_icon(image: PathBuf) {
let mut icon_path = image.clone();
icon_path.set_extension("icon");
let icon = Command::new("convert").arg(image).arg("mono:-").output().expect("Fail to convert image").stdout;
let mut icon_file = File::create(icon_path).expect("Fail to create icon file");
icon_file.write_u8(0).expect("Fail to write icon compress header");
icon_file.write_all(&icon).expect("Fail to write icon data");
}
fn main() {
println!("cargo::rerun-if-changed=src/icons");
for entry in glob("src/icons/**/*.png").unwrap() {
match entry {
Ok(path) => create_icon(path),
Err(e) => eprintln!("{e}"),
}
}
}

3
rust-toolchain.toml Normal file
View file

@ -0,0 +1,3 @@
[toolchain]
channel = "nightly"
targets = [ "thumbv7em-none-eabihf" ]

38
src/app.rs Normal file
View file

@ -0,0 +1,38 @@
use core::{ffi::c_void, pin::Pin};
use alloc::boxed::Box;
use flipperzero_sys as sys;
use crate::{scenes::SCENE_EVENT_HANDLERS, views::view_dispatcher_init};
pub struct App {
pub view_dispatcher: *mut sys::ViewDispatcher,
pub scene_manager: Option<*mut sys::SceneManager>,
pub gui: sys::furi::UnsafeRecord<sys::Gui>,
pub menu: *mut sys::Menu,
pub popup: *mut sys::Popup
}
impl App {
pub fn new() -> Pin<Box<Self>> {
let mut app = Box::pin(App {
view_dispatcher: unsafe { sys::view_dispatcher_alloc() },
scene_manager: None,
gui: unsafe { sys::furi::UnsafeRecord::open(c"gui".as_ptr()) as sys::furi::UnsafeRecord<sys::Gui> },
menu: unsafe { sys::menu_alloc() },
popup: unsafe { sys::popup_alloc() }
});
app.scene_manager = unsafe { Some(sys::scene_manager_alloc(&SCENE_EVENT_HANDLERS, &*app as *const App as *mut c_void)) };
unsafe { view_dispatcher_init(&mut app); }
app
}
}
impl Drop for App {
fn drop(&mut self) {
unsafe {
sys::view_dispatcher_free(self.view_dispatcher);
sys::scene_manager_free(self.scene_manager.unwrap());
}
}
}

BIN
src/icons/cvc_36x36.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 445 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 B

58
src/main.rs Normal file
View file

@ -0,0 +1,58 @@
//! Template project for Flipper Zero.
//! This app prints "Hello, Rust!" to the console then exits.
#![no_main]
#![no_std]
#![feature(variant_count)]
#![feature(const_refs_to_static)]
extern crate alloc;
extern crate flipperzero_alloc;
extern crate flipperzero_rt;
#[macro_use]
extern crate enum_primitive_derive;
extern crate num_traits;
use core::ffi::CStr;
use flipperzero_rt::{entry, manifest};
use flipperzero_sys as sys;
use scenes::Scenes;
use app::App;
use views::Views;
// Define the FAP Manifest for this application
manifest!(
name = "Flipper Hewo World in Rust",
app_version = 1,
has_icon = true,
// See https://github.com/flipperzero-rs/flipperzero/blob/v0.7.2/docs/icons.md for icon format
icon = "icons/rustacean-10x10.icon",
);
// Define the entry function
entry!(main);
mod main_menu;
mod popup_one;
mod popup_two;
mod scenes;
mod views;
mod app;
// Entry point
fn main(_args: Option<&CStr>) -> i32 {
let app = App::new();
unsafe {
sys::view_dispatcher_attach_to_gui(app.view_dispatcher, app.gui.as_ptr(), sys::ViewDispatcherType_ViewDispatcherTypeFullscreen);
sys::scene_manager_next_scene(app.scene_manager.unwrap(), Scenes::MainMenu as u32);
sys::view_dispatcher_run(app.view_dispatcher);
}
// Avoid app to be drop befoer the end of the process
drop(app);
0
}

53
src/main_menu.rs Normal file
View file

@ -0,0 +1,53 @@
use core::{ffi::c_void, ptr::null};
use flipperzero_sys as sys;
use num_traits::FromPrimitive;
use sys::SceneManagerEvent;
use crate::{app::App, scenes::Scenes, views::{app_exit, Views}};
#[derive(Primitive)]
enum Menus {
SelectionOne = 0,
SelectionTwo = 1
}
#[derive(Primitive)]
enum Events {
MenuShowPopupOne = 0,
MenuShowPopupTwo = 1
}
pub unsafe extern "C" fn menu_callback(context: *mut c_void, index: u32) {
let app = context as *mut App;
match Menus::from_u32(index) {
Some(Menus::SelectionOne) => sys::scene_manager_handle_custom_event((*app).scene_manager.unwrap(), Events::MenuShowPopupOne as u32),
Some(Menus::SelectionTwo) => sys::scene_manager_handle_custom_event((*app).scene_manager.unwrap(), Events::MenuShowPopupTwo as u32),
_ => return,
};
}
pub unsafe extern "C" fn scene_on_enter(context: *mut c_void) {
let app = context as *mut App;
sys::menu_reset((*app).menu);
sys::menu_add_item((*app).menu, c"First popup".as_ptr(), null(), Menus::SelectionOne as u32, Some(menu_callback), context);
sys::menu_add_item((*app).menu, c"Second popup".as_ptr(), null(), Menus::SelectionTwo as u32, Some(menu_callback), context);
sys::view_set_previous_callback(sys::menu_get_view((*app).menu), Some(app_exit));
sys::view_dispatcher_switch_to_view((*app).view_dispatcher, Views::Menu as u32);
}
pub unsafe extern "C" fn scene_on_event(context: *mut c_void, event: SceneManagerEvent) -> bool {
let app = context as *mut App;
match Events::from_u32(event.event) {
Some(Events::MenuShowPopupOne) => {sys::scene_manager_next_scene((*app).scene_manager.unwrap(), Scenes::PopupOne as u32); true},
Some(Events::MenuShowPopupTwo) => {sys::scene_manager_next_scene((*app).scene_manager.unwrap(), Scenes::PopupTwo as u32); true},
_ => false,
}
}
pub unsafe extern "C" fn scene_on_exit(context: *mut c_void) {
let app = context as *mut App;
sys::menu_reset((*app).menu);
}

31
src/popup_one.rs Normal file
View file

@ -0,0 +1,31 @@
use core::ffi::c_void;
use flipperzero_sys as sys;
use crate::{App, Views};
pub const TARGET_ICON: sys::Icon = sys::Icon {
width: 36,
height: 36,
frame_count: 1,
frame_rate: 0,
frames: [include_bytes!("icons/cvc_36x36.icon").as_ptr()].as_ptr(),
};
pub unsafe extern "C" fn scene_on_enter(context: *mut c_void) {
let app = context as *mut App;
sys::popup_reset((*app).popup);
sys::popup_set_context((*app).popup, context);
sys::popup_set_header((*app).popup, c"Popup One".as_ptr(), 64, 10, sys::Align_AlignCenter, sys::Align_AlignTop);
sys::popup_set_icon((*app).popup, 10, 10, &TARGET_ICON);
sys::popup_set_text((*app).popup, c"One! One popup. Ah ah ah...".as_ptr(), 64, 20, sys::Align_AlignLeft, sys::Align_AlignTop);
sys::view_dispatcher_switch_to_view((*app).view_dispatcher, Views::Popup as u32);
}
pub unsafe extern "C" fn scene_on_event(_context: *mut c_void, _event: sys::SceneManagerEvent) -> bool {
false // don't handle any events
}
pub unsafe extern "C" fn scene_on_exit(context: *mut c_void) {
let app = context as *mut App;
sys::popup_reset((*app).popup);
}

23
src/popup_two.rs Normal file
View file

@ -0,0 +1,23 @@
use core::ffi::c_void;
use flipperzero_sys as sys;
use crate::{popup_one::TARGET_ICON, App, Views};
pub unsafe extern "C" fn scene_on_enter(context: *mut c_void) {
let app = context as *mut App;
sys::popup_reset((*app).popup);
sys::popup_set_context((*app).popup, context);
sys::popup_set_header((*app).popup, c"Popup Two".as_ptr(), 64, 10, sys::Align_AlignCenter, sys::Align_AlignTop);
sys::popup_set_icon((*app).popup, 10, 10, &TARGET_ICON);
sys::popup_set_text((*app).popup, c"Two! Two popups. (press back)".as_ptr(), 64, 20, sys::Align_AlignLeft, sys::Align_AlignTop);
sys::view_dispatcher_switch_to_view((*app).view_dispatcher, Views::Popup as u32);
}
pub unsafe extern "C" fn scene_on_event(_context: *mut c_void, _event: sys::SceneManagerEvent) -> bool {
false // don't handle any events
}
pub unsafe extern "C" fn scene_on_exit(context: *mut c_void) {
let app = context as *mut App;
sys::popup_reset((*app).popup);
}

47
src/scenes.rs Normal file
View file

@ -0,0 +1,47 @@
use core::{ffi::c_void, mem::variant_count};
use flipperzero_sys as sys;
use crate::{app::App, main_menu, popup_one, popup_two};
#[derive(Primitive)]
pub enum Scenes {
MainMenu = 0,
PopupOne = 1,
PopupTwo = 2
}
pub const SCENE_ON_ENTER_HANDLERS : [sys::AppSceneOnEnterCallback; variant_count::<Scenes>()] = [
Some(main_menu::scene_on_enter),
Some(popup_one::scene_on_enter),
Some(popup_two::scene_on_enter)
];
pub const SCENE_ON_EVENT_HANDLERS : [sys::AppSceneOnEventCallback; variant_count::<Scenes>()] = [
Some(main_menu::scene_on_event),
Some(popup_one::scene_on_event),
Some(popup_two::scene_on_event)
];
pub const SCENE_ON_EXIT_HANDLERS : [sys::AppSceneOnExitCallback; variant_count::<Scenes>()] = [
Some(main_menu::scene_on_exit),
Some(popup_one::scene_on_exit),
Some(popup_two::scene_on_exit)
];
pub const SCENE_EVENT_HANDLERS : sys::SceneManagerHandlers = sys::SceneManagerHandlers {
on_enter_handlers: SCENE_ON_ENTER_HANDLERS.as_ptr(),
on_event_handlers: SCENE_ON_EVENT_HANDLERS.as_ptr(),
on_exit_handlers: SCENE_ON_EXIT_HANDLERS.as_ptr(),
scene_num: variant_count::<Scenes>() as u32
};
pub unsafe extern "C" fn custom_event_callback(context: *mut c_void, event: u32) -> bool {
let app = context as *mut App;
sys::scene_manager_handle_custom_event((*app).scene_manager.unwrap(), event)
}
pub unsafe extern "C" fn navigation_event_callback(context: *mut c_void) -> bool {
let app = context as *mut App;
sys::scene_manager_handle_back_event((*app).scene_manager.unwrap())
}

27
src/views.rs Normal file
View file

@ -0,0 +1,27 @@
use core::ffi::c_void;
use flipperzero_sys as sys;
use crate::{app::App, scenes};
#[derive(Primitive)]
pub enum Views {
Menu = 0,
Popup = 1
}
pub unsafe fn view_dispatcher_init(app: &mut App) {
sys::view_dispatcher_enable_queue(app.view_dispatcher);
sys::view_dispatcher_set_event_callback_context(app.view_dispatcher, &*app as *const App as *mut c_void);
sys::view_dispatcher_set_custom_event_callback(app.view_dispatcher, Some(scenes::custom_event_callback));
sys::view_dispatcher_set_navigation_event_callback(app.view_dispatcher, Some(scenes::navigation_event_callback));
sys::view_dispatcher_add_view(app.view_dispatcher, Views::Menu as u32, sys::menu_get_view(app.menu));
sys::view_dispatcher_add_view(app.view_dispatcher, Views::Popup as u32, sys::popup_get_view(app.popup));
}
pub unsafe extern "C" fn app_exit(_context: *mut core::ffi::c_void) -> u32 {
0xFFFFFFFF // Exit the view_dispatcher_run
}