Rotated UI rendering
This commit is contained in:
parent
fc1aa617af
commit
a9870fe133
1123
firmware2/Cargo.lock
generated
1123
firmware2/Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -49,7 +49,7 @@ itertools = { version = "0.14.0", default-features = false }
|
|||
bytemuck = "1.24.0"
|
||||
slint = { version = "1.14.1", default-features = false, features = ["compat-1-2", "libm", "log", "unsafe-single-threaded", "renderer-software"]}
|
||||
critical-section = "1.2.0"
|
||||
shadow-rs = { version = "1.5.0", default-features = false }
|
||||
backtrace = { version = "0.3.76", default-features = false }
|
||||
|
||||
# Crates for serial UART CLI
|
||||
embedded-cli = { version = "0.2.1", default-features = false, features = ["help", "macros"] }
|
||||
|
|
@ -62,7 +62,7 @@ const-gen = "1.6"
|
|||
embuild = "0.33"
|
||||
cc = "1.2.9"
|
||||
slint-build = "1.14.1"
|
||||
shadow-rs = { version = "1.5.0", features = ["no_std"]}
|
||||
gix = { version = "0.76.0", default-features = false, features = ["max-performance", "status"] }
|
||||
|
||||
[[bin]]
|
||||
name = "acid-firmware"
|
||||
|
|
|
|||
|
|
@ -6,13 +6,24 @@ use std::{env, fs};
|
|||
use const_gen::*;
|
||||
use slint_build::{CompilerConfiguration, EmbedResourcesKind};
|
||||
use xz2::read::XzEncoder;
|
||||
use shadow_rs::ShadowBuilder;
|
||||
// use shadow_rs::{BuildPattern, ShadowBuilder};
|
||||
|
||||
fn main() {
|
||||
ShadowBuilder::builder()
|
||||
.deny_const(Default::default())
|
||||
.build()
|
||||
.unwrap();
|
||||
if let Ok(repo) = gix::discover(env::var_os("CARGO_MANIFEST_DIR").unwrap().into_string().unwrap()) {
|
||||
let commit_hash = repo.head_commit().unwrap().short_id().unwrap();
|
||||
println!("cargo:rustc-env=GIT_COMMIT_HASH={}", commit_hash);
|
||||
println!("cargo:rustc-env=GIT_COMMIT={}",
|
||||
repo.find_tag(repo.head_id().unwrap())
|
||||
.ok()
|
||||
.map(|tag| format!("{} ({})", tag.decode().unwrap().name, commit_hash))
|
||||
.unwrap_or_else(|| commit_hash.to_string())
|
||||
);
|
||||
}
|
||||
// ShadowBuilder::builder()
|
||||
// .build_pattern(BuildPattern::Lazy)
|
||||
// .deny_const(Default::default())
|
||||
// .build()
|
||||
// .unwrap();
|
||||
|
||||
// Generate vial config at the root of project
|
||||
println!("cargo:rerun-if-changed=vial.json");
|
||||
|
|
@ -24,6 +35,8 @@ fn main() {
|
|||
// println!("cargo:rustc-link-arg=-Tdefmt.x");
|
||||
|
||||
let slint_config = CompilerConfiguration::new()
|
||||
// .with_scale_factor(4.0)
|
||||
.with_style("cosmic-dark".to_string())
|
||||
.embed_resources(EmbedResourcesKind::EmbedForSoftwareRenderer);
|
||||
slint_build::compile_with_config("ui/main.slint", slint_config).expect("Slint build failed");
|
||||
slint_build::print_rustc_flags().unwrap()
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
|
||||
use core::fmt::Write;
|
||||
|
||||
use embedded_cli::cli::CliBuilder;
|
||||
use embedded_cli::Command;
|
||||
use esp_hal::{Async, uart::{TxError, UartRx}};
|
||||
|
|
@ -71,7 +73,7 @@ pub async fn run_console(mut uart_rx: UartRx<'_, Async>) {
|
|||
// write!(cli.writer(), "Hello, {}", name.unwrap_or("World"))?;
|
||||
// }
|
||||
Base::Version => {
|
||||
cli.writer().write_str(crate::build::CLAP_LONG_VERSION).unwrap();
|
||||
cli.writer().write_fmt(format_args!("{} - {} - {}", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION"), env!("GIT_COMMIT"))).unwrap();
|
||||
}
|
||||
Base::Reset => {
|
||||
cli.writer().write_str("Performing software reset.").unwrap();
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
use core::cell::RefCell;
|
||||
use core::fmt::Write;
|
||||
|
||||
use alloc::{string::String, vec::Vec};
|
||||
use backtrace::{BytesOrWideString, Symbol, SymbolName};
|
||||
use critical_section::{CriticalSection, Mutex};
|
||||
use esp_hal::uart::UartTx;
|
||||
use esp_hal::Blocking;
|
||||
|
|
@ -83,7 +85,7 @@ fn panic_handler(info: &core::panic::PanicInfo) -> ! {
|
|||
use esp_backtrace::Backtrace;
|
||||
|
||||
println!("{RED}");
|
||||
println!("====================== PANIC ======================");
|
||||
println!("=============== CUSTOM PANIC HANDLER ==============");
|
||||
println!("{info}{RESET}");
|
||||
println!("");
|
||||
println!("Backtrace:");
|
||||
|
|
@ -93,11 +95,39 @@ fn panic_handler(info: &core::panic::PanicInfo) -> ! {
|
|||
|
||||
for frame in backtrace.frames() {
|
||||
println!("0x{:x}", frame.program_counter());
|
||||
print_resolved_symbol(frame.program_counter());
|
||||
}
|
||||
|
||||
loop {}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "usb-log"))]
|
||||
fn print_resolved_symbol(address: usize) {
|
||||
unsafe {
|
||||
backtrace::resolve_unsynchronized(address as _, |sym| {
|
||||
println!("? {:x?}\n{}\n\n", sym.addr().map(|addr| addr as usize), sym.name().unwrap_or(SymbolName::new(b"(unknown)")));
|
||||
// sym.filename_raw().unwrap_or(BytesOrWideString::Bytes(&[]));
|
||||
// let segments: Vec<String> = vec![
|
||||
// sym.addr().map(|a| format!("{:x?}", a as isize)),
|
||||
// sym.name().map(|n| n.to_string()),
|
||||
// sym.filename().map(|p| {
|
||||
// format!(
|
||||
// "{}{}",
|
||||
// p.to_string_lossy(),
|
||||
// sym.lineno()
|
||||
// .map(|n| format!(" (line {})", n))
|
||||
// .unwrap_or_else(|| "".to_string())
|
||||
// )
|
||||
// }),
|
||||
// ]
|
||||
// .into_iter()
|
||||
// .flatten()
|
||||
// .collect();
|
||||
// segments.join("\n")
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub fn setup_alternative_logging(alt_uart: UartTx<'static, Blocking>, level_filter: LevelFilter)
|
||||
{
|
||||
critical_section::with(|cs| {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ use core::time::Duration;
|
|||
use alloc::boxed::Box;
|
||||
use alloc::rc::Rc;
|
||||
use alloc::vec;
|
||||
use shadow_rs::shadow;
|
||||
use bt_hci::controller::ExternalController;
|
||||
use embassy_executor::Spawner;
|
||||
use esp_alloc::{HeapRegion, MemoryCapability};
|
||||
|
|
@ -26,7 +25,7 @@ use esp_hal::lcd_cam::lcd::dpi::Dpi;
|
|||
use esp_hal::mcpwm::{McPwm, PeripheralClockConfig};
|
||||
use esp_hal::psram::{FlashFreq, PsramConfig, PsramSize, SpiRamFreq, SpiTimingConfigCoreClock};
|
||||
use esp_hal::rng::TrngSource;
|
||||
use esp_hal::system::Stack;
|
||||
use esp_hal::system::{CpuControl, Stack};
|
||||
use esp_hal::time::Instant;
|
||||
use esp_hal::timer::timg::TimerGroup;
|
||||
use esp_hal::{Blocking, ram};
|
||||
|
|
@ -44,14 +43,17 @@ use rmk::{join_all};
|
|||
use rmk::keyboard::Keyboard;
|
||||
use rmk::storage::async_flash_wrapper;
|
||||
use rmk::{initialize_keymap_and_storage, run_devices, run_rmk};
|
||||
use slint::platform::software_renderer::Rgb565Pixel;
|
||||
use slint::platform::WindowAdapter;
|
||||
use slint::platform::software_renderer::{MinimalSoftwareWindow, RenderingRotation, RepaintBufferType, Rgb565Pixel, SoftwareRenderer};
|
||||
use slint::{ComponentHandle, PhysicalSize, WindowSize};
|
||||
use static_cell::StaticCell;
|
||||
use ui::AppWindow;
|
||||
use ui::window_adapter::SoftwareWindowAdapter;
|
||||
use {esp_alloc as _, esp_backtrace as _};
|
||||
|
||||
use crate::matrix::IoeMatrix;
|
||||
use crate::peripherals::st7701s::St7701s;
|
||||
use crate::ui::backend::{FramebufferPtr, SlintBackend};
|
||||
use crate::vial::{VIAL_KEYBOARD_DEF, VIAL_KEYBOARD_ID};
|
||||
|
||||
mod keymap;
|
||||
|
|
@ -64,8 +66,6 @@ mod logging;
|
|||
#[cfg(feature = "alt-log")]
|
||||
mod console;
|
||||
|
||||
shadow!(build);
|
||||
|
||||
// This creates a default app-descriptor required by the esp-idf bootloader.
|
||||
// For more information see: <https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/system/app_image_format.html#application-description>
|
||||
esp_bootloader_esp_idf::esp_app_desc!();
|
||||
|
|
@ -267,10 +267,11 @@ async fn main(_spawner: Spawner) {
|
|||
960,
|
||||
));
|
||||
|
||||
let window_size = [framebuffer.width, framebuffer.height];
|
||||
// let window_size = [framebuffer.width, framebuffer.height];
|
||||
let window_size = [framebuffer.height, framebuffer.width];
|
||||
let framebuffer_ptr = FramebufferPtr(framebuffer.as_target_pixels() as _);
|
||||
|
||||
static SECOND_CORE_STACK: StaticCell<Stack<8192>> = StaticCell::new();
|
||||
static SECOND_CORE_STACK: StaticCell<Stack<{8192 * 2}>> = StaticCell::new();
|
||||
let second_core_stack = SECOND_CORE_STACK.init(Stack::new());
|
||||
esp_rtos::start_second_core(
|
||||
peripherals.CPU_CTRL,
|
||||
|
|
@ -381,50 +382,6 @@ impl Framebuffer {
|
|||
}
|
||||
}
|
||||
|
||||
struct SlintBackend {
|
||||
window_size: [u32; 2],
|
||||
window: RefCell<Option<Rc<slint::platform::software_renderer::MinimalSoftwareWindow>>>,
|
||||
framebuffer: FramebufferPtr,
|
||||
// peripherals: RefCell<Option<Peripherals>>,
|
||||
}
|
||||
|
||||
struct FramebufferPtr(*mut [Rgb565Pixel]);
|
||||
|
||||
unsafe impl Send for FramebufferPtr {}
|
||||
|
||||
impl slint::platform::Platform for SlintBackend {
|
||||
fn create_window_adapter(&self) -> Result<Rc<dyn slint::platform::WindowAdapter>, slint::PlatformError> {
|
||||
let window = slint::platform::software_renderer::MinimalSoftwareWindow::new(
|
||||
slint::platform::software_renderer::RepaintBufferType::ReusedBuffer,
|
||||
);
|
||||
window.set_size(WindowSize::Physical(PhysicalSize::new(self.window_size[0], self.window_size[1])));
|
||||
self.window.replace(Some(window.clone()));
|
||||
Ok(window)
|
||||
}
|
||||
|
||||
fn duration_since_start(&self) -> Duration {
|
||||
Duration::from_millis(Instant::now().duration_since_epoch().as_millis())
|
||||
}
|
||||
|
||||
fn run_event_loop(&self) -> Result<(), slint::PlatformError> {
|
||||
loop {
|
||||
slint::platform::update_timers_and_animations();
|
||||
|
||||
if let Some(window) = self.window.borrow().clone() {
|
||||
// window.try_dispatch_event(todo!())?;
|
||||
|
||||
window.draw_if_needed(|renderer| {
|
||||
// TODO: Proper synchronization.
|
||||
let framebuffer = unsafe { &mut *self.framebuffer.0 };
|
||||
// TODO: Try using height to see if it rotates the screen correctly. Might need
|
||||
// to swap dimensions elsewhere.
|
||||
renderer.render(framebuffer, self.window_size[0] as usize);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// impl DrawTarget for Framebuffer {
|
||||
// type Color = Rgb565;
|
||||
// type Error = ();
|
||||
|
|
|
|||
56
firmware2/src/ui/backend.rs
Normal file
56
firmware2/src/ui/backend.rs
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
use core::{cell::RefCell, time::Duration};
|
||||
|
||||
use alloc::rc::Rc;
|
||||
use esp_hal::time::Instant;
|
||||
use log::info;
|
||||
use slint::{PhysicalSize, WindowSize, platform::software_renderer::{RenderingRotation, RepaintBufferType, Rgb565Pixel, SoftwareRenderer}};
|
||||
|
||||
use super::window_adapter::SoftwareWindowAdapter;
|
||||
|
||||
pub struct FramebufferPtr(pub *mut [Rgb565Pixel]);
|
||||
|
||||
unsafe impl Send for FramebufferPtr {}
|
||||
|
||||
pub struct SlintBackend {
|
||||
pub window_size: [u32; 2],
|
||||
pub window: RefCell<Option<Rc<SoftwareWindowAdapter>>>,
|
||||
pub framebuffer: FramebufferPtr,
|
||||
// pub peripherals: RefCell<Option<Peripherals>>,
|
||||
}
|
||||
|
||||
impl slint::platform::Platform for SlintBackend {
|
||||
fn create_window_adapter(&self) -> Result<Rc<dyn slint::platform::WindowAdapter>, slint::PlatformError> {
|
||||
// TODO: Custom window adapter impl needs to be implemented, so we can change `rotation` on
|
||||
// `SoftwareRenderer`.
|
||||
let renderer = SoftwareRenderer::new_with_repaint_buffer_type(RepaintBufferType::ReusedBuffer /* TODO: Implement a swapchain */);
|
||||
renderer.set_rendering_rotation(RenderingRotation::Rotate270);
|
||||
let window = SoftwareWindowAdapter::new(renderer);
|
||||
// window.set_scale_factor(4.0);
|
||||
window.set_size(WindowSize::Physical(PhysicalSize::new(self.window_size[0], self.window_size[1])));
|
||||
self.window.replace(Some(window.clone()));
|
||||
Ok(window)
|
||||
}
|
||||
|
||||
fn duration_since_start(&self) -> Duration {
|
||||
Duration::from_millis(Instant::now().duration_since_epoch().as_millis())
|
||||
}
|
||||
|
||||
fn run_event_loop(&self) -> Result<(), slint::PlatformError> {
|
||||
loop {
|
||||
slint::platform::update_timers_and_animations();
|
||||
|
||||
if let Some(window) = self.window.borrow().clone() {
|
||||
// window.try_dispatch_event(todo!())?;
|
||||
|
||||
window.draw_if_needed(|renderer| {
|
||||
// TODO: Proper synchronization.
|
||||
let framebuffer = unsafe { &mut *self.framebuffer.0 };
|
||||
// TODO: Try using height to see if it rotates the screen correctly. Might need
|
||||
// to swap dimensions elsewhere.
|
||||
renderer.render(framebuffer, self.window_size[1] as usize);
|
||||
info!("UI rendered.");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +1,6 @@
|
|||
// #![cfg_attr(not(feature = "simulator"), no_main)]
|
||||
|
||||
pub mod window_adapter;
|
||||
pub mod backend;
|
||||
|
||||
slint::include_modules!();
|
||||
|
|
|
|||
86
firmware2/src/ui/window_adapter.rs
Normal file
86
firmware2/src/ui/window_adapter.rs
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
use core::{cell::Cell, ops::{Deref, DerefMut}};
|
||||
|
||||
use alloc::rc::{Rc, Weak};
|
||||
use slint::{PhysicalSize, Window, WindowSize, platform::{Renderer, WindowAdapter, WindowEvent, software_renderer::{RepaintBufferType, SoftwareRenderer}}};
|
||||
|
||||
/// This is a minimal adapter for a Window that doesn't have any other feature than rendering
|
||||
/// using the software renderer.
|
||||
pub struct SoftwareWindowAdapter {
|
||||
pub window: Window,
|
||||
pub renderer: SoftwareRenderer,
|
||||
needs_redraw: Cell<bool>,
|
||||
size: Cell<PhysicalSize>,
|
||||
}
|
||||
|
||||
impl SoftwareWindowAdapter {
|
||||
/// Instantiate a new MinimalWindowAdaptor
|
||||
///
|
||||
/// The `repaint_buffer_type` parameter specify what kind of buffer are passed to the [`SoftwareRenderer`]
|
||||
pub fn new(renderer: SoftwareRenderer) -> Rc<Self> {
|
||||
Rc::new_cyclic(|w: &Weak<Self>| Self {
|
||||
window: Window::new(w.clone()),
|
||||
renderer,
|
||||
needs_redraw: Cell::new(true),
|
||||
size: Default::default(),
|
||||
})
|
||||
}
|
||||
|
||||
/// If the window needs to be redrawn, the callback will be called with the
|
||||
/// [renderer](SoftwareRenderer) that should be used to do the drawing.
|
||||
///
|
||||
/// [`SoftwareRenderer::render()`] or [`SoftwareRenderer::render_by_line()`] should be called
|
||||
/// in that callback.
|
||||
///
|
||||
/// Return true if something was redrawn.
|
||||
pub fn draw_if_needed(&self, render_callback: impl FnOnce(&SoftwareRenderer)) -> bool {
|
||||
if self.needs_redraw.replace(false) /*|| self.renderer.rendering_metrics_collector.is_some()*/ {
|
||||
render_callback(&self.renderer);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_scale_factor(&self, scale_factor: f32) {
|
||||
self.window.dispatch_event(WindowEvent::ScaleFactorChanged { scale_factor });
|
||||
}
|
||||
}
|
||||
|
||||
impl WindowAdapter for SoftwareWindowAdapter {
|
||||
fn window(&self) -> &Window {
|
||||
&self.window
|
||||
}
|
||||
|
||||
fn renderer(&self) -> &dyn Renderer {
|
||||
&self.renderer
|
||||
}
|
||||
|
||||
fn size(&self) -> PhysicalSize {
|
||||
self.size.get()
|
||||
}
|
||||
|
||||
fn set_size(&self, size: WindowSize) {
|
||||
let sf = self.window.scale_factor();
|
||||
self.size.set(size.to_physical(sf));
|
||||
self.window
|
||||
.dispatch_event(WindowEvent::Resized { size: size.to_logical(sf) })
|
||||
}
|
||||
|
||||
fn request_redraw(&self) {
|
||||
self.needs_redraw.set(true);
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for SoftwareWindowAdapter {
|
||||
type Target = Window;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.window
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for SoftwareWindowAdapter {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.window
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,16 @@
|
|||
import { Button, VerticalBox } from "std-widgets.slint";
|
||||
import { Button, VerticalBox, LineEdit, GridBox } from "std-widgets.slint";
|
||||
|
||||
export component AppWindow inherits Window {
|
||||
y: 0px;
|
||||
in-out property <int> counter: 42;
|
||||
default-font-family: "IBM Plex Sans";
|
||||
default-font-size: 16pt;
|
||||
callback request-increase-value();
|
||||
GridBox {
|
||||
height: 240px;
|
||||
padding: 0px;
|
||||
padding-top: 8px;
|
||||
width: 960px;
|
||||
VerticalBox {
|
||||
Text {
|
||||
text: "Counter: \{root.counter}";
|
||||
|
|
@ -14,5 +22,19 @@ export component AppWindow inherits Window {
|
|||
root.request-increase-value();
|
||||
}
|
||||
}
|
||||
|
||||
LineEdit {
|
||||
input-type: InputType.password;
|
||||
text: "LineEdit";
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
text: "Button";
|
||||
}
|
||||
|
||||
Button {
|
||||
text: "Button";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue