This is an automated email from the ASF dual-hosted git repository.

xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx-apps.git


The following commit(s) were added to refs/heads/master by this push:
     new 18838d23b examples/rust: Add basic Slint example
18838d23b is described below

commit 18838d23bff52e9e15627abcb54e7a4085489673
Author: Huang Qi <huang...@xiaomi.com>
AuthorDate: Wed Jan 22 16:06:47 2025 +0800

    examples/rust: Add basic Slint example
    
    Summary:
    - Added a basic Slint example for NuttX, demonstrating how to integrate 
Slint UI framework with Rust on NuttX
    - Includes a simple UI with a counter and touchscreen input handling
    - Provides CMake, Kconfig, and Makefile configurations for building the 
example
    
    Impact:
    - Introduces a new example showcasing Slint UI framework usage on NuttX
    - Enables developers to explore Rust-based UI development on embedded 
systems
    - Demonstrates integration with NuttX framebuffer and touchscreen drivers
    
    Signed-off-by: Huang Qi <huang...@xiaomi.com>
---
 examples/rust/slint/.gitignore     |   1 +
 examples/rust/slint/CMakeLists.txt |  31 +++++++
 examples/rust/slint/Cargo.toml     |  24 ++++++
 examples/rust/slint/Kconfig        |  29 +++++++
 examples/rust/slint/Make.defs      |  26 ++++++
 examples/rust/slint/Makefile       |  34 ++++++++
 examples/rust/slint/build.rs       |  28 ++++++
 examples/rust/slint/src/lib.rs     | 171 +++++++++++++++++++++++++++++++++++++
 examples/rust/slint/ui/app.slint   |  25 ++++++
 9 files changed, 369 insertions(+)

diff --git a/examples/rust/slint/.gitignore b/examples/rust/slint/.gitignore
new file mode 100644
index 000000000..eb5a316cb
--- /dev/null
+++ b/examples/rust/slint/.gitignore
@@ -0,0 +1 @@
+target
diff --git a/examples/rust/slint/CMakeLists.txt 
b/examples/rust/slint/CMakeLists.txt
new file mode 100644
index 000000000..6ed088aea
--- /dev/null
+++ b/examples/rust/slint/CMakeLists.txt
@@ -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
diff --git a/examples/rust/slint/Cargo.toml b/examples/rust/slint/Cargo.toml
new file mode 100644
index 000000000..4600f2220
--- /dev/null
+++ b/examples/rust/slint/Cargo.toml
@@ -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" }
diff --git a/examples/rust/slint/Kconfig b/examples/rust/slint/Kconfig
new file mode 100644
index 000000000..babc19d04
--- /dev/null
+++ b/examples/rust/slint/Kconfig
@@ -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
diff --git a/examples/rust/slint/Make.defs b/examples/rust/slint/Make.defs
new file mode 100644
index 000000000..9259913fc
--- /dev/null
+++ b/examples/rust/slint/Make.defs
@@ -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
diff --git a/examples/rust/slint/Makefile b/examples/rust/slint/Makefile
new file mode 100644
index 000000000..34b444e9d
--- /dev/null
+++ b/examples/rust/slint/Makefile
@@ -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
diff --git a/examples/rust/slint/build.rs b/examples/rust/slint/build.rs
new file mode 100644
index 000000000..6142de92a
--- /dev/null
+++ b/examples/rust/slint/build.rs
@@ -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();
+}
diff --git a/examples/rust/slint/src/lib.rs b/examples/rust/slint/src/lib.rs
new file mode 100644
index 000000000..043274cbc
--- /dev/null
+++ b/examples/rust/slint/src/lib.rs
@@ -0,0 +1,171 @@
+// 
##############################################################################
+// 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 != Format::RGB565 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;
+            }
+        }
+    }
+}
diff --git a/examples/rust/slint/ui/app.slint b/examples/rust/slint/ui/app.slint
new file mode 100644
index 000000000..29c14af06
--- /dev/null
+++ b/examples/rust/slint/ui/app.slint
@@ -0,0 +1,25 @@
+import { Button, VerticalBox, HorizontalBox , AboutSlint } from 
"std-widgets.slint";
+
+export component AppWindow inherits Window {
+    property <int> counter: 0;
+
+    timer := Timer {
+        interval: 1s;
+        running: true;
+        triggered() => {
+            counter += 1;
+        }
+    }
+
+    VerticalBox {
+        Text {
+            horizontal-alignment: center;
+            vertical-alignment: center;
+            text: "Times since start on NuttX: \n\{root.counter}";
+            font-size: 24px;
+        }
+
+        AboutSlint { }
+
+    }
+}

Reply via email to