blob: 7f81ce29cd326cbc3ca3088de4ae80dfbae92ad0 [file] [log] [blame]
// ##############################################################################
// 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;
}
}
}
}