nuttx-apps/examples/rust/slint/src/lib.rs
Huang Qi a8c62307ec examples/rust/slint: Update nuttx crate branch from master to main and fix pixel format check.
Changed the NUTTX dependency branch to 'main' for consistency with repository updates.
Updated pixel format check to use FB_FMT_RGB16_565 for better clarity and accuracy.

Signed-off-by: Huang Qi <huangqi3@xiaomi.com>
2025-03-24 20:34:29 +08:00

171 lines
5.5 KiB
Rust

// ##############################################################################
// examples/rust/slint/src/lib.rs
//
// Licensed to the Apache Software Foundation (ASF) under one or more contributor
// license agreements. See the NOTICE file distributed with this work for
// additional information regarding copyright ownership. The ASF licenses this
// file to you under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
//
// ##############################################################################
use std::rc::Rc;
use std::{cell::RefCell, ffi::CStr};
use slint::platform::software_renderer::{LineBufferProvider, Rgb565Pixel};
use nuttx::input::touchscreen::*;
use nuttx::video::fb::*;
use slint::platform::WindowEvent;
slint::include_modules!();
struct NuttXPlatform {
window: Rc<slint::platform::software_renderer::MinimalSoftwareWindow>,
}
impl slint::platform::Platform for NuttXPlatform {
fn create_window_adapter(
&self,
) -> Result<Rc<dyn slint::platform::WindowAdapter>, slint::PlatformError> {
Ok(self.window.clone())
}
fn duration_since_start(&self) -> std::time::Duration {
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
}
}
fn create_slint_app() -> AppWindow {
AppWindow::new().expect("Failed to load UI")
}
#[derive(Debug, Clone)]
struct NuttXRenderer {
frame_buffer: *mut u8,
line_buffer: Rc<RefCell<Vec<Rgb565Pixel>>>,
stride: usize,
lines: usize,
}
impl LineBufferProvider for NuttXRenderer {
type TargetPixel = Rgb565Pixel;
fn process_line(
&mut self,
line: usize,
range: std::ops::Range<usize>,
render_fn: impl FnOnce(&mut [Self::TargetPixel]),
) {
let fb_size = self.stride * self.lines;
let frame = unsafe {
std::slice::from_raw_parts_mut(self.frame_buffer as *mut Rgb565Pixel, fb_size)
};
let mut buffer = self.line_buffer.borrow_mut();
render_fn(&mut buffer[range.clone()]);
let offset = line * self.stride;
frame[offset..offset + range.end - range.start].copy_from_slice(&buffer[range]);
}
}
#[no_mangle]
pub extern "C" fn slint_main() {
// Open the framebuffer device and get its information
let fbdev = match FrameBuffer::new(CStr::from_bytes_with_nul(b"/dev/fb0\0").unwrap()) {
Ok(fb) => fb,
Err(_) => {
println!("Failed to open framebuffer device");
return;
}
};
let planeinfo = fbdev.get_plane_info().unwrap();
let videoinfo = fbdev.get_video_info().unwrap();
println!("{:?}", planeinfo);
println!("{:?}", videoinfo);
if videoinfo.fmt != FB_FMT_RGB16_565 as u8 {
println!("Unsupported pixel format, only RGB565 is supported for now");
return;
}
// Open the touchscreen device
let mut tsdev = match TouchScreen::open(CStr::from_bytes_with_nul(b"/dev/input0\0").unwrap()) {
Ok(ts) => ts,
Err(_) => {
println!("Failed to open touchscreen device");
return;
}
};
// Setup the Slint backend
let window = slint::platform::software_renderer::MinimalSoftwareWindow::new(Default::default());
window.set_size(slint::PhysicalSize::new(
videoinfo.xres.into(),
videoinfo.yres.into(),
));
// Set the platform
slint::platform::set_platform(Box::new(NuttXPlatform {
window: window.clone(),
}))
.unwrap();
// Configure the UI
let _ui = create_slint_app();
// Create the renderer for NuttX
let nxrender = NuttXRenderer {
frame_buffer: planeinfo.fbmem as *mut u8,
line_buffer: Rc::new(RefCell::new(vec![
Rgb565Pixel::default();
videoinfo.xres as usize
])),
stride: videoinfo.xres.into(),
lines: videoinfo.yres.into(),
};
let button = slint::platform::PointerEventButton::Left;
let mut last_touch_down = false;
let mut last_position = Default::default();
loop {
slint::platform::update_timers_and_animations();
window.draw_if_needed(|renderer| {
renderer.render_by_line(nxrender.clone());
});
if let Ok(sample) = tsdev.read_sample() {
if sample.npoints > 0 {
let point = sample.point[0];
if point.is_pos_valid() {
let position = slint::PhysicalPosition::new(point.x.into(), point.y.into())
.to_logical(window.scale_factor());
last_position = position;
if point.is_touch_down() || point.is_touch_move() {
last_touch_down = true;
window.dispatch_event(WindowEvent::PointerPressed { position, button });
}
}
}
} else {
if last_touch_down {
window.dispatch_event(WindowEvent::PointerReleased {
position: last_position,
button,
});
last_touch_down = false;
}
}
}
}