1use {
5 num_derive::FromPrimitive,
6 num_traits::FromPrimitive,
7 server::{AsScalar, CheckedConn, CheckedPermissions, FromScalar, MessageAllowed},
8 xous::{MemoryRange, CID, PID, SID},
9};
10
11pub mod consts;
12pub mod error;
13pub mod msg;
14pub mod navigation;
15#[cfg(not(keyos))]
16pub mod simulator;
17pub mod touch;
18
19pub use error::GuiServerError;
20
21#[macro_export]
22macro_rules! use_api {
23 ($gui:path, $server:path) => {
24 mod gui_permissions {
25 use gui_server_api::msg::*;
26 pub use $gui as gui_server_api;
27 use $server as server;
28 #[derive(Clone, Default, server::Permissions)]
29 #[server_name = "os/gui-server"]
30 pub struct GuiPermissions;
31 }
32 type GuiApi = gui_permissions::gui_server_api::GuiApi<gui_permissions::GuiPermissions>;
33 type GuiApiLight = gui_permissions::gui_server_api::GuiApiLight<gui_permissions::GuiPermissions>;
34 };
35 () => {
36 gui_server_api::use_api!(gui_server_api, server);
37 };
38}
39
40pub type AppName = String;
41
42#[derive(Debug, Copy, Clone, PartialEq, Eq)]
45pub struct ControlCenterColor {
46 pub red: u8,
47 pub green: u8,
48 pub blue: u8,
49}
50
51impl ControlCenterColor {
52 pub const fn new(red: u8, green: u8, blue: u8) -> Self { Self { red, green, blue } }
53}
54
55#[derive(Debug, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
56pub struct RegisterApp {
57 pub cid: CID,
58 pub name: AppName,
59 pub height: usize,
60}
61
62#[derive(Debug, Copy, Clone, FromPrimitive, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize, Default)]
63pub enum ModalStyle {
64 #[default]
67 SlideUpDraggablePopup = 0,
68
69 SlideUpFixedPopup,
72
73 SlideUpFullscreen,
75
76 Instant,
78}
79
80#[derive(Clone, Debug, Default)]
82pub struct GuiApiLight<P: CheckedPermissions> {
83 conn: CheckedConn<P>,
84}
85
86#[derive(Debug)]
88pub struct GuiApi<P: CheckedPermissions> {
89 inner: GuiApiLight<P>,
90 cid_self: CID,
91 sid: SID,
92}
93
94impl<P: CheckedPermissions> GuiApiLight<P> {
95 pub fn connect() -> Self { Self { conn: CheckedConn::default() } }
98
99 pub fn switch_to(&self, next_pid: PID, x: usize, y: usize) -> Result<(), GuiServerError>
102 where
103 P: MessageAllowed<msg::SwitchTo>,
104 {
105 self.conn.try_send_scalar(msg::SwitchTo { next_pid: next_pid.get() as usize, x, y })?;
106 Ok(())
107 }
108
109 pub fn switch_to_launcher(&self) -> Result<bool, GuiServerError>
111 where
112 P: MessageAllowed<msg::SwitchToLauncher>,
113 {
114 Ok(self.conn.try_send_blocking_scalar(msg::SwitchToLauncher)?)
115 }
116
117 pub fn is_locked(&self) -> Result<bool, GuiServerError>
118 where
119 P: MessageAllowed<msg::IsLocked>,
120 {
121 Ok(self.conn.try_send_blocking_scalar(msg::IsLocked)?)
122 }
123
124 pub fn shutdown(&self) -> Result<(), GuiServerError>
125 where
126 P: MessageAllowed<msg::Shutdown>,
127 {
128 Ok(self.conn.try_send_blocking_scalar(msg::Shutdown { reboot: false })?)
129 }
130
131 pub fn reboot(&self) -> Result<(), GuiServerError>
132 where
133 P: MessageAllowed<msg::Shutdown>,
134 {
135 Ok(self.conn.try_send_blocking_scalar(msg::Shutdown { reboot: true })?)
136 }
137
138 pub fn close_app(&self, pid: PID) -> Result<(), GuiServerError>
141 where
142 P: MessageAllowed<msg::CloseApp>,
143 {
144 Ok(self.conn.try_send_blocking_scalar(msg::CloseApp { pid: pid.get() as usize })??)
145 }
146
147 pub fn capture_screen(&self) -> Result<xous::DropDeallocate, GuiServerError>
151 where
152 P: MessageAllowed<msg::CaptureScreen>,
153 {
154 let mem = xous::map_memory(None, None, consts::FB_SIZE, xous::MemoryFlags::W)?;
155 self.conn.lend_mut(msg::CaptureScreen(mem));
156 Ok(xous::DropDeallocate::new(mem))
157 }
158
159 pub fn inject_touch(&self, touch: touch::Touch) -> Result<(), GuiServerError>
161 where
162 P: MessageAllowed<msg::InjectTouch>,
163 {
164 self.conn.try_send_scalar(msg::InjectTouch(touch))?;
165 Ok(())
166 }
167
168 pub fn inject_key(&self, is_pressed: bool, key: Key) -> Result<(), GuiServerError>
170 where
171 P: MessageAllowed<msg::InjectKey>,
172 {
173 self.conn.try_send_scalar(msg::InjectKey { is_pressed, key })?;
174 Ok(())
175 }
176
177 pub fn inject_power_button(&self, is_pressed: bool) -> Result<(), GuiServerError>
179 where
180 P: MessageAllowed<msg::InjectPowerButton>,
181 {
182 self.conn.try_send_scalar(msg::InjectPowerButton(is_pressed))?;
183 Ok(())
184 }
185
186 pub fn update_kiosk_policy(&self, policy: msg::UpdateKioskPolicy) -> Result<(), GuiServerError>
187 where
188 P: MessageAllowed<msg::UpdateKioskPolicy>,
189 {
190 self.conn.try_send_scalar(policy)?;
191 Ok(())
192 }
193}
194
195impl<P: CheckedPermissions> GuiApi<P> {
196 pub fn register(name: &str, height: usize) -> Result<Self, GuiServerError>
198 where
199 P: MessageAllowed<msg::RegisterAppMessage>,
200 {
201 let (api, cid) = Self::register_inner()?;
202 api.inner.conn.send_blocking_archive(msg::RegisterAppMessage(RegisterApp {
203 cid,
204 name: name.into(),
205 height,
206 }))?;
207 Ok(api)
208 }
209
210 pub fn set_control_center_color(&self, color: Option<ControlCenterColor>) -> Result<(), GuiServerError>
215 where
216 P: MessageAllowed<msg::SetControlCenterColor>,
217 {
218 self.inner.conn.try_send_scalar(msg::SetControlCenterColor { color })?;
219 Ok(())
220 }
221
222 pub fn register_control_center(height: usize) -> Result<Self, GuiServerError>
225 where
226 P: MessageAllowed<msg::RegisterControlCenter>,
227 {
228 let (api, cid) = Self::register_inner()?;
229 api.inner.conn.send_blocking_archive(msg::RegisterControlCenter { cid, height })?;
230 Ok(api)
231 }
232
233 pub fn register_keyboard(height: usize) -> Result<Self, GuiServerError>
236 where
237 P: MessageAllowed<msg::RegisterKeyboard>,
238 {
239 let (api, cid) = Self::register_inner()?;
240 api.inner.conn.send_blocking_archive(msg::RegisterKeyboard { cid, height })?;
241 Ok(api)
242 }
243
244 pub fn register_with_role<M>(name: &str, height: usize) -> Result<Self, GuiServerError>
247 where
248 M: msg::RoleClaim,
249 P: MessageAllowed<msg::RegisterAppMessage> + MessageAllowed<M>,
250 {
251 let (api, cid) = Self::register_inner()?;
252 api.inner.conn.send_blocking_scalar(M::default());
253 api.inner.conn.send_blocking_archive(msg::RegisterAppMessage(RegisterApp {
254 cid,
255 name: name.into(),
256 height,
257 }))?;
258 Ok(api)
259 }
260
261 fn register_inner() -> Result<(Self, CID), GuiServerError> {
262 let sid = xous::create_server()?;
263 let cid_self = xous::connect(sid)?;
264 let inner = GuiApiLight::connect();
265 let api = Self { inner, sid, cid_self };
266 let gui_server_pid = api.inner.conn.get_remote_pid();
267
268 let gui_server_cid = xous::connect_for_process(gui_server_pid, api.sid)?;
269 xous::allow_messages_on_connection(gui_server_pid, gui_server_cid, 0..64)?;
270
271 Ok((api, gui_server_cid))
272 }
273
274 pub fn sid(&self) -> SID { self.sid }
275
276 pub fn submit_frame(&self, buffer: MemoryRange) -> Result<(), GuiServerError>
278 where
279 P: MessageAllowed<msg::SubmitFrame>,
280 {
281 Ok(self.conn.try_send_move(msg::SubmitFrame { buffer })?)
282 }
283
284 pub fn show_camera(&self, y_pos: u16) -> Result<(), GuiServerError>
285 where
286 P: MessageAllowed<msg::ShowCamera>,
287 {
288 self.conn.try_send_scalar(msg::ShowCamera { y_pos })?;
289 Ok(())
290 }
291
292 pub fn hide_camera(&self) -> Result<(), GuiServerError>
293 where
294 P: MessageAllowed<msg::HideCamera>,
295 {
296 self.conn.try_send_scalar(msg::HideCamera)?;
297 Ok(())
298 }
299
300 pub fn update_keyboard(&self, msg: msg::UpdateKeyboard) -> Result<(), GuiServerError>
301 where
302 P: MessageAllowed<msg::UpdateKeyboard>,
303 {
304 self.conn.try_send_archive(msg)?;
305 Ok(())
306 }
307
308 pub fn hide_keyboard(&self) -> Result<(), GuiServerError>
309 where
310 P: MessageAllowed<msg::HideKeyboard>,
311 {
312 self.conn.try_send_scalar(msg::HideKeyboard)?;
313 Ok(())
314 }
315
316 pub fn notify_login_success(&self) -> Result<(), GuiServerError>
317 where
318 P: MessageAllowed<msg::LoginSuccess>,
319 {
320 self.conn.try_send_scalar(msg::LoginSuccess)?;
321 Ok(())
322 }
323
324 pub fn wake_event_loop(&self) {
325 let msg = xous::Message::new_scalar(InputMessage::Noop as usize, 0, 0, 0, 0);
326 if let Err(e) = xous::send_message(self.cid_self, msg) {
327 log::error!("Failed to send wake event to self: {e:?}");
328 }
329 }
330
331 pub fn request_redraw(&self) -> Result<(), GuiServerError>
332 where
333 P: MessageAllowed<msg::RequestRedraw>,
334 {
335 self.conn.try_send_scalar(msg::RequestRedraw)?;
336 Ok(())
337 }
338
339 pub fn try_receive_input(&self) -> Option<(InputMessage, xous::MessageEnvelope)> {
340 if let Ok(Some(msg)) = xous::try_receive_message(self.sid) {
341 let opcode = FromPrimitive::from_usize(msg.body.id());
342 return opcode.map(|opcode| (opcode, msg));
343 }
344
345 None
346 }
347
348 pub fn receive_input(&self) -> Result<(InputMessage, xous::MessageEnvelope), GuiServerError> {
349 xous::receive_message(self.sid)
350 .map(|msg| {
351 let opcode = FromPrimitive::from_usize(msg.body.id());
352 (opcode.expect("input opcode"), msg)
353 })
354 .map_err(Into::into)
355 }
356
357 pub fn key_pressed(&self, key: Key) -> Result<(), GuiServerError>
358 where
359 P: MessageAllowed<msg::KeyPressed>,
360 {
361 self.conn.try_send_scalar(msg::KeyPressed(Some(key)))?;
362 Ok(())
363 }
364
365 pub fn key_released(&self, key: Key) -> Result<(), GuiServerError>
366 where
367 P: MessageAllowed<msg::KeyReleased>,
368 {
369 self.conn.try_send_scalar(msg::KeyReleased(Some(key)))?;
370 Ok(())
371 }
372
373 pub fn animate_next_frame(&self, animation_kind: NextFrameAnimationKind) -> Result<(), GuiServerError>
374 where
375 P: MessageAllowed<msg::AnimateNextFrame>,
376 {
377 self.conn.try_send_scalar(msg::AnimateNextFrame { animation_kind })?;
378 Ok(())
379 }
380}
381
382impl<P: CheckedPermissions> std::ops::Deref for GuiApi<P> {
383 type Target = GuiApiLight<P>;
384
385 fn deref(&self) -> &Self::Target { &self.inner }
386}
387
388#[derive(Debug, PartialEq, num_derive::FromPrimitive, num_derive::ToPrimitive, Copy, Clone)]
389pub enum InputMessage {
390 Touch = 0,
391 KeyPress,
392 KeyRelease,
393
394 NavigationFocused,
397
398 NavigationCancelled,
400
401 Noop,
403
404 Visible,
406
407 Hidden,
409
410 FrameBuffer,
413
414 Custom1,
415 Custom2,
416 Custom3,
417 Custom4,
418
419 CloseRequested,
421
422 Scroll,
426}
427
428#[derive(Debug, Copy, Clone)]
429pub enum Key {
430 Char(usize),
431 Backspace,
432 Delete,
433 CursorLeft,
434 CursorRight,
435 Enter,
436 Tab,
437}
438
439impl server::AsScalar<2> for Key {
440 fn as_scalar(&self) -> [u32; 2] {
441 match self {
442 Key::Char(c) => [0, *c as _],
443 Key::Backspace => [1, 0],
444 Key::Delete => [2, 0],
445 Key::CursorLeft => [3, 0],
446 Key::CursorRight => [4, 0],
447 Key::Enter => [5, 0],
448 Key::Tab => [6, 0],
449 }
450 }
451}
452
453impl server::FromScalar<2> for Key {
454 fn from_scalar(value: [u32; 2]) -> Self {
455 match value[0] {
456 1 => Key::Backspace,
457 2 => Key::Delete,
458 3 => Key::CursorLeft,
459 4 => Key::CursorRight,
460 5 => Key::Enter,
461 6 => Key::Tab,
462 _ => Key::Char(value[1] as _),
463 }
464 }
465}
466
467impl<P: CheckedPermissions> Drop for GuiApi<P> {
468 fn drop(&mut self) {
469 if let Err(e) = xous::destroy_server(self.sid) {
470 log::error!("Error destroying gui api event server: {e:?}");
471 }
472 }
473}
474
475#[derive(Debug, Copy, Clone, FromPrimitive, Default)]
476pub enum NextFrameAnimationKind {
477 #[default]
478 SlideInLeft = 0,
479 SlideInRight,
480 SlideOutLeft,
481 SlideOutRight,
482}
483
484#[derive(
485 Debug,
486 Copy,
487 Clone,
488 FromPrimitive,
489 Default,
490 PartialEq,
491 Eq,
492 rkyv::Archive,
493 rkyv::Serialize,
494 rkyv::Deserialize,
495)]
496#[rkyv(derive(Debug))]
497pub enum KeyboardKind {
498 #[default]
499 Alphanumeric = 0,
500 Password,
501 Numbers,
502 Decimal,
503 Email,
504}
505
506impl FromScalar<1> for KeyboardKind {
507 fn from_scalar([value]: [u32; 1]) -> Self { Self::from_u32(value).unwrap_or_default() }
508}
509
510impl AsScalar<1> for KeyboardKind {
511 fn as_scalar(&self) -> [u32; 1] { [*self as u32] }
512}
513
514impl From<&ArchivedKeyboardKind> for KeyboardKind {
515 fn from(archived: &ArchivedKeyboardKind) -> Self {
516 rkyv::deserialize::<_, rkyv::rancor::Error>(archived).unwrap()
517 }
518}