Skip to content

Commit b68a7d3

Browse files
authored
120 virtual keyboard (#121)
* refactor imports in window.rs for improved organization * add event bubbling flag to window object * add keyboard event handling and show keyboard option in layout initialization * add touch screen detection functionality * initialize global allocator before main and add touch screen detection for keyboard option * add safety documentation for keyboard and screen event handlers
1 parent 1c6a3a5 commit b68a7d3

5 files changed

Lines changed: 164 additions & 22 deletions

File tree

drivers/wasm/src/devices/graphics/mod.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use file_system::{Device, create_device};
99
use graphics::Point;
1010
pub use keyboard::*;
1111
pub use mouse::*;
12+
use wasm_bindgen::JsValue;
1213
use web_sys::{HtmlCanvasElement, Window};
1314

1415
pub struct GraphicsDevices {
@@ -35,6 +36,20 @@ pub async fn new() -> Result<GraphicsDevices, String> {
3536
})
3637
}
3738

39+
pub fn has_touch_screen() -> Result<bool, &'static str> {
40+
let window = web_sys::window().ok_or("Failed to get window")?;
41+
42+
// 1. Check if 'ontouchstart' exists in window
43+
let has_ontouchstart =
44+
js_sys::Reflect::has(&window, &JsValue::from_str("ontouchstart")).unwrap_or(false);
45+
46+
// 2. Check maxTouchPoints
47+
let navigator = window.navigator();
48+
let max_touch_points = navigator.max_touch_points();
49+
50+
Ok(has_ontouchstart || max_touch_points > 0)
51+
}
52+
3853
fn get_window_size(window: &Window) -> Option<(u32, u32)> {
3954
let width = window.inner_width().ok()?.as_f64()? as u32;
4055
let height = window.inner_height().ok()?.as_f64()? as u32;

examples/wasm/src/main.rs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
#![no_std]
22

3-
#[cfg(target_arch = "wasm32")]
4-
drivers_wasm::memory::instantiate_global_allocator!();
5-
63
#[cfg(target_arch = "wasm32")]
74
#[xila::task::run(task_path = xila::task, executor = drivers_wasm::executor::instantiate_static_executor!())]
85
async fn main() {
6+
drivers_wasm::memory::instantiate_global_allocator!();
7+
98
extern crate alloc;
109

10+
use alloc::string::ToString;
1111
use alloc::vec;
1212
use drivers_wasm::devices::graphics::GraphicsDevices;
1313
use xila::bootsplash::Bootsplash;
@@ -212,8 +212,15 @@ async fn main() {
212212

213213
bootsplash.stop(graphics_manager).await.unwrap();
214214

215+
let arguments = if drivers_wasm::devices::graphics::has_touch_screen().unwrap() {
216+
log::information!("Touch screen detected.");
217+
vec!["--show-keyboard".to_string()]
218+
} else {
219+
vec![]
220+
};
221+
215222
// - - Execute the shell
216-
let _ = executable::execute("/binaries/graphical_shell", vec![], standard, None)
223+
let _ = executable::execute("/binaries/graphical_shell", arguments, standard, None)
217224
.await
218225
.unwrap()
219226
.join()

executables/shell/graphical/src/layout.rs

Lines changed: 123 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1+
use crate::error::{Error, Result};
12
use alloc::{format, string::String};
2-
use xila::graphics::{self, lvgl, theme};
3+
use core::ptr::null_mut;
4+
use xila::graphics::{self, EventKind, lvgl, theme};
35
use xila::shared::unix_to_human_time;
4-
use xila::time;
5-
6-
use crate::error::{Error, Result};
6+
use xila::{log, time};
77

88
pub struct Layout {
99
window: *mut lvgl::lv_obj_t,
@@ -21,6 +21,85 @@ impl Drop for Layout {
2121
}
2222
}
2323

24+
/// Keyboard event handler.
25+
///
26+
/// # Safety
27+
///
28+
/// This function is unsafe because it dereferences raw pointers.
29+
pub unsafe extern "C" fn keyboard_event_handler(event: *mut lvgl::lv_event_t) {
30+
unsafe {
31+
let code = lvgl::lv_event_get_code(event);
32+
let code = EventKind::from_lvgl_code(code);
33+
let keyboard = lvgl::lv_event_get_user_data(event) as *mut lvgl::lv_obj_t;
34+
35+
if keyboard.is_null() {
36+
return;
37+
}
38+
39+
match code {
40+
EventKind::Ready => {
41+
let focused_input = lvgl::lv_keyboard_get_textarea(keyboard);
42+
43+
if focused_input.is_null() {
44+
return;
45+
}
46+
47+
lvgl::lv_obj_send_event(
48+
focused_input,
49+
EventKind::Ready.into_lvgl_code(),
50+
null_mut(),
51+
);
52+
53+
lvgl::lv_keyboard_set_textarea(keyboard, null_mut());
54+
lvgl::lv_obj_add_flag(keyboard, lvgl::lv_obj_flag_t_LV_OBJ_FLAG_HIDDEN);
55+
}
56+
EventKind::Cancel => {
57+
lvgl::lv_keyboard_set_textarea(keyboard, null_mut());
58+
lvgl::lv_obj_add_flag(keyboard, lvgl::lv_obj_flag_t_LV_OBJ_FLAG_HIDDEN);
59+
}
60+
61+
_ => {}
62+
}
63+
}
64+
}
65+
66+
/// Screen event handler.
67+
///
68+
/// # Safety
69+
///
70+
/// This function is unsafe because it dereferences raw pointers.
71+
pub unsafe extern "C" fn screen_event_handler(event: *mut lvgl::lv_event_t) {
72+
unsafe {
73+
let code = lvgl::lv_event_get_code(event);
74+
let code = EventKind::from_lvgl_code(code);
75+
let target = lvgl::lv_event_get_target_obj(event);
76+
let keyboard = lvgl::lv_event_get_user_data(event) as *mut lvgl::lv_obj_t;
77+
78+
if target.is_null() || keyboard.is_null() {
79+
return;
80+
}
81+
82+
log::information!("event screen : {code:?}");
83+
84+
match code {
85+
EventKind::Focused => {
86+
if lvgl::lv_obj_has_class(target, &lvgl::lv_textarea_class) {
87+
lvgl::lv_keyboard_set_textarea(keyboard, target);
88+
lvgl::lv_obj_remove_flag(keyboard, lvgl::lv_obj_flag_t_LV_OBJ_FLAG_HIDDEN);
89+
lvgl::lv_obj_move_foreground(keyboard);
90+
}
91+
}
92+
EventKind::Defocused => {
93+
if lvgl::lv_obj_has_class(target, &lvgl::lv_textarea_class) {
94+
lvgl::lv_keyboard_set_textarea(keyboard, null_mut());
95+
lvgl::lv_obj_add_flag(keyboard, lvgl::lv_obj_flag_t_LV_OBJ_FLAG_HIDDEN);
96+
}
97+
}
98+
_ => {}
99+
}
100+
}
101+
}
102+
24103
impl Layout {
25104
pub async fn r#loop(&mut self) {
26105
self.update_clock().await;
@@ -47,7 +126,7 @@ impl Layout {
47126
self.window
48127
}
49128

50-
pub async fn new() -> Result<Self> {
129+
pub async fn new(show_keyboard: bool) -> Result<Self> {
51130
let _lock = graphics::get_instance().lock().await; // Lock the graphics
52131

53132
// - Create a window
@@ -147,7 +226,7 @@ impl Layout {
147226
if body.is_null() {
148227
return Err(Error::FailedToCreateObject);
149228
}
150-
229+
lvgl::lv_obj_add_flag(body, lvgl::lv_obj_flag_t_LV_OBJ_FLAG_EVENT_BUBBLE);
151230
lvgl::lv_obj_set_width(body, lvgl::lv_pct(100));
152231
lvgl::lv_obj_set_flex_grow(body, 1);
153232
lvgl::lv_obj_set_style_pad_all(body, 0, lvgl::LV_STATE_DEFAULT);
@@ -156,6 +235,44 @@ impl Layout {
156235
body
157236
};
158237

238+
// - Create a keyboard
239+
unsafe {
240+
let keyboard = lvgl::lv_keyboard_create(window);
241+
242+
if keyboard.is_null() {
243+
return Err(Error::FailedToCreateObject);
244+
}
245+
246+
lvgl::lv_obj_add_flag(keyboard, lvgl::lv_obj_flag_t_LV_OBJ_FLAG_HIDDEN);
247+
248+
if show_keyboard {
249+
lvgl::lv_obj_add_event_cb(
250+
window,
251+
Some(screen_event_handler),
252+
EventKind::Focused.into_lvgl_code(),
253+
keyboard as *mut _,
254+
);
255+
lvgl::lv_obj_add_event_cb(
256+
window,
257+
Some(screen_event_handler),
258+
EventKind::Defocused.into_lvgl_code(),
259+
keyboard as *mut _,
260+
);
261+
lvgl::lv_obj_add_event_cb(
262+
keyboard,
263+
Some(keyboard_event_handler),
264+
EventKind::Ready.into_lvgl_code(),
265+
keyboard as *mut _,
266+
);
267+
lvgl::lv_obj_add_event_cb(
268+
window,
269+
Some(keyboard_event_handler),
270+
EventKind::Cancel.into_lvgl_code(),
271+
keyboard as *mut _,
272+
);
273+
}
274+
};
275+
159276
drop(_lock); // Unlock the graphics
160277

161278
graphics::get_instance().set_window_parent(body).await?;

executables/shell/graphical/src/lib.rs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use core::time::Duration;
1818
use home::Home;
1919
use layout::Layout;
2020
use login::Login;
21-
use xila::executable::{self, Standard};
21+
use xila::executable::{self, ArgumentsParser, Standard};
2222
use xila::file_system::Permissions;
2323
use xila::task;
2424
use xila::users;
@@ -27,7 +27,13 @@ use crate::shortcut::SHORTCUT_PATH;
2727
use crate::{desk::Desk, error::Error};
2828

2929
pub async fn main(standard: Standard, arguments: Vec<String>) -> Result<(), NonZeroUsize> {
30-
Shell::new(standard).await.main(arguments).await
30+
let mut parsed_arguments = ArgumentsParser::new(&arguments);
31+
32+
let show_keyboard = parsed_arguments
33+
.find_map(|argument| Some(argument.options.get_option("show-keyboard").is_some()))
34+
.unwrap_or(false);
35+
36+
Shell::new(standard, show_keyboard).await.main().await
3137
}
3238

3339
pub struct Shell {
@@ -48,8 +54,8 @@ executable::implement_executable_device!(
4854
);
4955

5056
impl Shell {
51-
pub async fn new(standard: Standard) -> Self {
52-
let layout = Layout::new().await.unwrap();
57+
pub async fn new(standard: Standard, show_keyboard: bool) -> Self {
58+
let layout = Layout::new(show_keyboard).await.unwrap();
5359

5460
let login = Box::new(Login::new().await.unwrap());
5561

@@ -63,7 +69,7 @@ impl Shell {
6369
}
6470
}
6571

66-
pub async fn main(&mut self, _: Vec<String>) -> Result<(), NonZeroUsize> {
72+
pub async fn main(&mut self) -> Result<(), NonZeroUsize> {
6773
let virtual_file_system = xila::virtual_file_system::get_instance();
6874
virtual_file_system
6975
.set_permissions(SHORTCUT_PATH, Permissions::ALL_FULL)

modules/graphics/src/window.rs

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,8 @@
1+
use super::lvgl;
2+
use crate::{Color, Error, EventKind, Result, event::Event};
13
use alloc::boxed::Box;
2-
3-
use core::{mem::forget, str};
4-
54
use alloc::collections::VecDeque;
6-
7-
use crate::{Color, Error, EventKind, Result, event::Event};
8-
9-
use super::lvgl;
5+
use core::{mem::forget, str};
106

117
struct UserData {
128
pub queue: VecDeque<Event>,
@@ -94,6 +90,7 @@ impl Window {
9490
lvgl::lv_event_code_t_LV_EVENT_ALL,
9591
&mut user_data.queue as *mut _ as *mut core::ffi::c_void,
9692
);
93+
lvgl::lv_obj_add_flag(window, lvgl::lv_obj_flag_t_LV_OBJ_FLAG_EVENT_BUBBLE);
9794
lvgl::lv_obj_set_user_data(window, Box::into_raw(user_data) as *mut core::ffi::c_void);
9895
// Set the size of the window to 100% of the parent object.
9996
lvgl::lv_obj_set_size(window, lvgl::lv_pct(100), lvgl::lv_pct(100));

0 commit comments

Comments
 (0)