Skip to content

Commit

Permalink
examples/rust: Add basic Slint example
Browse files Browse the repository at this point in the history
Summary:
- Added a new Rust example demonstrating the integration of Slint UI framework with NuttX
- Includes CMake build configuration, Kconfig options, and Makefile support
- Implements a basic UI application with a timer counter display
- Provides framebuffer rendering support for NuttX using RGB565 format
- Includes touch input handling implementation (currently not integrated with UI)

Impact:
- Demonstrates Rust UI development capabilities on NuttX
- Provides a foundation for building more complex Rust-based UI applications
- Enables future integration of touch input with Slint UI components
- Shows how to use Rust's FFI capabilities to interface with NuttX system calls
- Adds support for software rendering of Slint UI on embedded systems

Signed-off-by: Huang Qi <huangqi3@xiaomi.com>
  • Loading branch information
no1wudi committed Jan 23, 2025
1 parent dca032a commit 036bb81
Show file tree
Hide file tree
Showing 10 changed files with 480 additions and 0 deletions.
1 change: 1 addition & 0 deletions examples/rust/slint/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
target
31 changes: 31 additions & 0 deletions examples/rust/slint/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# ##############################################################################
# apps/examples/rust/slint/CMakeLists.txt
#
# 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.
#
# ##############################################################################

if(CONFIG_EXAMPLES_RUST_SLINT)

# Build the Rust crate using nuttx_add_rust
nuttx_add_rust(CRATE_NAME slint CRATE_PATH ${CMAKE_CURRENT_SOURCE_DIR})

nuttx_add_application(
NAME ${CONFIG_EXAMPLES_RUST_SLINT_PROGNAME} STACKSIZE
${CONFIG_EXAMPLES_RUST_SLINT_STACKSIZE} PRIORITY
${CONFIG_EXAMPLES_RUST_SLINT_PRIORITY})

endif() # CONFIG_EXAMPLES_RUST_SLINT
24 changes: 24 additions & 0 deletions examples/rust/slint/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
[package]
name = "slint"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ["staticlib"]

[profile.dev]
panic = "abort"

[profile.release]
panic = "abort"
lto = true
codegen-units = 1
opt-level = 'z'

[dependencies]
libc = "0.2"
slint = { version = "1.9", default-features = false, features = ["compat-1-2", "renderer-software", "libm", "unsafe-single-threaded"] }
nuttx = { git = "https://github.com/no1wudi/nuttx-rs.git", branch = "master" }

[build-dependencies]
slint-build = { version = "1.9" }
29 changes: 29 additions & 0 deletions examples/rust/slint/Kconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#
# For a description of the syntax of this configuration file,
# see the file kconfig-language.txt in the NuttX tools repository.
#

config EXAMPLES_RUST_SLINT
tristate "Slint Basic Example"
default n
---help---
Enable the basic Slint example.

if EXAMPLES_RUST_SLINT

config EXAMPLES_RUST_SLINT_PROGNAME
string "Program name"
default "slint"
---help---
This is the name of the program that will be used when the
program is installed.

config EXAMPLES_RUST_SLINT_PRIORITY
int "Task priority"
default 100

config EXAMPLES_RUST_SLINT_STACKSIZE
int "Stack size"
default DEFAULT_TASK_STACKSIZE

endif
26 changes: 26 additions & 0 deletions examples/rust/slint/Make.defs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
############################################################################
# apps/examples/rust/slint/Make.defs
#
# 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.
#
############################################################################

include $(APPDIR)/tools/Rust.mk

ifneq ($(CONFIG_EXAMPLES_RUST_SLINT),)
CONFIGURED_APPS += $(APPDIR)/examples/rust/slint
EXTRA_LIBS += $(call RUST_GET_BINDIR,slint,$(APPDIR)/examples/rust)
endif
34 changes: 34 additions & 0 deletions examples/rust/slint/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
############################################################################
# apps/examples/rust/slint/Makefile
#
# 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.
#
############################################################################

include $(APPDIR)/Make.defs

PROGNAME = $(CONFIG_EXAMPLES_RUST_SLINT_PROGNAME)
PRIORITY = $(CONFIG_EXAMPLES_RUST_SLINT_PRIORITY)
STACKSIZE = $(CONFIG_EXAMPLES_RUST_SLINT_STACKSIZE)
MODULE = $(CONFIG_EXAMPLES_RUST_SLINT)

context::
$(call RUST_CARGO_BUILD,slint,$(APPDIR)/examples/rust)

clean::
$(call RUST_CARGO_CLEAN,slint,$(APPDIR)/examples/rust)

include $(APPDIR)/Application.mk
28 changes: 28 additions & 0 deletions examples/rust/slint/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// ##############################################################################
// examples/rust/slint/build.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.
//
// ##############################################################################

fn main() {
slint_build::compile_with_config(
"ui/app.slint",
slint_build::CompilerConfiguration::new()
.embed_resources(slint_build::EmbedResourcesKind::EmbedForSoftwareRenderer),
)
.unwrap();
}
133 changes: 133 additions & 0 deletions examples/rust/slint/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
// ##############################################################################
// 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::video::fb::*;

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!("Plane info: {:?}", planeinfo);
println!("Video info: {:?}", videoinfo);

if videoinfo.fmt != Format::RGB565 as u8 {
println!("Unsupported pixel format, only RGB565 is supported for now");
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(),
};

loop {
slint::platform::update_timers_and_animations();
window.draw_if_needed(|renderer| {
renderer.render_by_line(nxrender.clone());
});
}
}
Loading

0 comments on commit 036bb81

Please sign in to comment.