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 request_app_close(&self, pid: PID) -> Result<(), GuiServerError>
150 where
151 P: MessageAllowed<msg::RequestAppClose>,
152 {
153 self.conn.try_send_scalar(msg::RequestAppClose { pid: pid.get() as usize })?;
154 Ok(())
155 }
156
157 pub fn capture_screen(&self) -> Result<xous::DropDeallocate, GuiServerError>
161 where
162 P: MessageAllowed<msg::CaptureScreen>,
163 {
164 let mem = xous::map_memory(None, None, consts::FB_SIZE, xous::MemoryFlags::W)?;
165 self.conn.lend_mut(msg::CaptureScreen(mem));
166 Ok(xous::DropDeallocate::new(mem))
167 }
168
169 pub fn inject_touch(&self, touch: touch::Touch) -> Result<(), GuiServerError>
171 where
172 P: MessageAllowed<msg::InjectTouch>,
173 {
174 self.conn.try_send_scalar(msg::InjectTouch(touch))?;
175 Ok(())
176 }
177
178 pub fn inject_key(&self, is_pressed: bool, key: Key) -> Result<(), GuiServerError>
180 where
181 P: MessageAllowed<msg::InjectKey>,
182 {
183 self.conn.try_send_scalar(msg::InjectKey { is_pressed, key })?;
184 Ok(())
185 }
186
187 pub fn inject_power_button(&self, is_pressed: bool) -> Result<(), GuiServerError>
189 where
190 P: MessageAllowed<msg::InjectPowerButton>,
191 {
192 self.conn.try_send_scalar(msg::InjectPowerButton(is_pressed))?;
193 Ok(())
194 }
195
196 pub fn update_kiosk_policy(&self, policy: msg::UpdateKioskPolicy) -> Result<(), GuiServerError>
197 where
198 P: MessageAllowed<msg::UpdateKioskPolicy>,
199 {
200 self.conn.try_send_scalar(policy)?;
201 Ok(())
202 }
203}
204
205impl<P: CheckedPermissions> GuiApi<P> {
206 pub fn register(name: &str, height: usize) -> Result<Self, GuiServerError>
208 where
209 P: MessageAllowed<msg::RegisterAppMessage>,
210 {
211 let (api, cid) = Self::register_inner()?;
212 api.inner.conn.send_blocking_archive(msg::RegisterAppMessage(RegisterApp {
213 cid,
214 name: name.into(),
215 height,
216 }))?;
217 Ok(api)
218 }
219
220 pub fn set_control_center_color(&self, color: Option<ControlCenterColor>) -> Result<(), GuiServerError>
225 where
226 P: MessageAllowed<msg::SetControlCenterColor>,
227 {
228 self.inner.conn.try_send_scalar(msg::SetControlCenterColor { color })?;
229 Ok(())
230 }
231
232 pub fn register_control_center(height: usize) -> Result<Self, GuiServerError>
235 where
236 P: MessageAllowed<msg::RegisterControlCenter>,
237 {
238 let (api, cid) = Self::register_inner()?;
239 api.inner.conn.send_blocking_archive(msg::RegisterControlCenter { cid, height })?;
240 Ok(api)
241 }
242
243 pub fn register_keyboard(height: usize) -> Result<Self, GuiServerError>
246 where
247 P: MessageAllowed<msg::RegisterKeyboard>,
248 {
249 let (api, cid) = Self::register_inner()?;
250 api.inner.conn.send_blocking_archive(msg::RegisterKeyboard { cid, height })?;
251 Ok(api)
252 }
253
254 pub fn register_with_role<M>(name: &str, height: usize) -> Result<Self, GuiServerError>
257 where
258 M: msg::RoleClaim,
259 P: MessageAllowed<msg::RegisterAppMessage> + MessageAllowed<M>,
260 {
261 let (api, cid) = Self::register_inner()?;
262 api.inner.conn.send_blocking_scalar(M::default());
263 api.inner.conn.send_blocking_archive(msg::RegisterAppMessage(RegisterApp {
264 cid,
265 name: name.into(),
266 height,
267 }))?;
268 Ok(api)
269 }
270
271 fn register_inner() -> Result<(Self, CID), GuiServerError> {
272 let sid = xous::create_server()?;
273 let cid_self = xous::connect(sid)?;
274 let inner = GuiApiLight::connect();
275 let api = Self { inner, sid, cid_self };
276 let gui_server_pid = api.inner.conn.get_remote_pid();
277
278 let gui_server_cid = xous::connect_for_process(gui_server_pid, api.sid)?;
279 xous::allow_messages_on_connection(gui_server_pid, gui_server_cid, 0..64)?;
280
281 Ok((api, gui_server_cid))
282 }
283
284 pub fn sid(&self) -> SID { self.sid }
285
286 pub fn submit_frame(&self, buffer: MemoryRange) -> Result<(), GuiServerError>
288 where
289 P: MessageAllowed<msg::SubmitFrame>,
290 {
291 Ok(self.conn.try_send_move(msg::SubmitFrame { buffer })?)
292 }
293
294 pub fn show_camera(&self, y_pos: u16) -> Result<(), GuiServerError>
295 where
296 P: MessageAllowed<msg::ShowCamera>,
297 {
298 self.conn.try_send_scalar(msg::ShowCamera { y_pos })?;
299 Ok(())
300 }
301
302 pub fn hide_camera(&self) -> Result<(), GuiServerError>
303 where
304 P: MessageAllowed<msg::HideCamera>,
305 {
306 self.conn.try_send_scalar(msg::HideCamera)?;
307 Ok(())
308 }
309
310 pub fn update_keyboard(&self, msg: msg::UpdateKeyboard) -> Result<(), GuiServerError>
311 where
312 P: MessageAllowed<msg::UpdateKeyboard>,
313 {
314 self.conn.try_send_archive(msg)?;
315 Ok(())
316 }
317
318 pub fn hide_keyboard(&self) -> Result<(), GuiServerError>
319 where
320 P: MessageAllowed<msg::HideKeyboard>,
321 {
322 self.conn.try_send_scalar(msg::HideKeyboard)?;
323 Ok(())
324 }
325
326 pub fn notify_login_success(&self) -> Result<(), GuiServerError>
327 where
328 P: MessageAllowed<msg::LoginSuccess>,
329 {
330 self.conn.try_send_scalar(msg::LoginSuccess)?;
331 Ok(())
332 }
333
334 pub fn wake_event_loop(&self) {
335 let msg = xous::Message::new_scalar(InputMessage::Noop as usize, 0, 0, 0, 0);
336 if let Err(e) = xous::send_message(self.cid_self, msg) {
337 log::error!("Failed to send wake event to self: {e:?}");
338 }
339 }
340
341 pub fn request_redraw(&self) -> Result<(), GuiServerError>
342 where
343 P: MessageAllowed<msg::RequestRedraw>,
344 {
345 self.conn.try_send_scalar(msg::RequestRedraw)?;
346 Ok(())
347 }
348
349 pub fn try_receive_input(&self) -> Option<(InputMessage, xous::MessageEnvelope)> {
350 if let Ok(Some(msg)) = xous::try_receive_message(self.sid) {
351 let opcode = FromPrimitive::from_usize(msg.body.id());
352 return opcode.map(|opcode| (opcode, msg));
353 }
354
355 None
356 }
357
358 pub fn receive_input(&self) -> Result<(InputMessage, xous::MessageEnvelope), GuiServerError> {
359 xous::receive_message(self.sid)
360 .map(|msg| {
361 let opcode = FromPrimitive::from_usize(msg.body.id());
362 (opcode.expect("input opcode"), msg)
363 })
364 .map_err(Into::into)
365 }
366
367 pub fn key_pressed(&self, key: Key) -> Result<(), GuiServerError>
368 where
369 P: MessageAllowed<msg::KeyPressed>,
370 {
371 self.conn.try_send_scalar(msg::KeyPressed(Some(key)))?;
372 Ok(())
373 }
374
375 pub fn key_released(&self, key: Key) -> Result<(), GuiServerError>
376 where
377 P: MessageAllowed<msg::KeyReleased>,
378 {
379 self.conn.try_send_scalar(msg::KeyReleased(Some(key)))?;
380 Ok(())
381 }
382
383 pub fn animate_next_frame(&self, animation_kind: NextFrameAnimationKind) -> Result<(), GuiServerError>
384 where
385 P: MessageAllowed<msg::AnimateNextFrame>,
386 {
387 self.conn.try_send_scalar(msg::AnimateNextFrame { animation_kind })?;
388 Ok(())
389 }
390}
391
392impl<P: CheckedPermissions> std::ops::Deref for GuiApi<P> {
393 type Target = GuiApiLight<P>;
394
395 fn deref(&self) -> &Self::Target { &self.inner }
396}
397
398#[derive(Debug, PartialEq, num_derive::FromPrimitive, num_derive::ToPrimitive, Copy, Clone)]
399pub enum InputMessage {
400 Touch = 0,
401 KeyPress,
402 KeyRelease,
403
404 NavigationFocused,
407
408 NavigationCancelled,
410
411 Noop,
413
414 Visible,
416
417 Hidden,
419
420 FrameBuffer,
423
424 Custom1,
425 Custom2,
426 Custom3,
427 Custom4,
428
429 CloseRequested,
431
432 Scroll,
436}
437
438#[derive(Debug, Copy, Clone)]
439pub enum Key {
440 Char(usize),
441 Backspace,
442 Delete,
443 CursorLeft,
444 CursorRight,
445 Enter,
446 Tab,
447}
448
449impl server::AsScalar<2> for Key {
450 fn as_scalar(&self) -> [u32; 2] {
451 match self {
452 Key::Char(c) => [0, *c as _],
453 Key::Backspace => [1, 0],
454 Key::Delete => [2, 0],
455 Key::CursorLeft => [3, 0],
456 Key::CursorRight => [4, 0],
457 Key::Enter => [5, 0],
458 Key::Tab => [6, 0],
459 }
460 }
461}
462
463impl server::FromScalar<2> for Key {
464 fn from_scalar(value: [u32; 2]) -> Self {
465 match value[0] {
466 1 => Key::Backspace,
467 2 => Key::Delete,
468 3 => Key::CursorLeft,
469 4 => Key::CursorRight,
470 5 => Key::Enter,
471 6 => Key::Tab,
472 _ => Key::Char(value[1] as _),
473 }
474 }
475}
476
477impl<P: CheckedPermissions> Drop for GuiApi<P> {
478 fn drop(&mut self) {
479 if let Err(e) = xous::destroy_server(self.sid) {
480 log::error!("Error destroying gui api event server: {e:?}");
481 }
482 }
483}
484
485#[derive(Debug, Copy, Clone, FromPrimitive, Default)]
486pub enum NextFrameAnimationKind {
487 #[default]
488 SlideInLeft = 0,
489 SlideInRight,
490 SlideOutLeft,
491 SlideOutRight,
492}
493
494#[derive(
495 Debug,
496 Copy,
497 Clone,
498 FromPrimitive,
499 Default,
500 PartialEq,
501 Eq,
502 rkyv::Archive,
503 rkyv::Serialize,
504 rkyv::Deserialize,
505)]
506#[rkyv(derive(Debug))]
507pub enum KeyboardKind {
508 #[default]
509 Alphanumeric = 0,
510 Password,
511 Numbers,
512 Decimal,
513 Email,
514}
515
516impl FromScalar<1> for KeyboardKind {
517 fn from_scalar([value]: [u32; 1]) -> Self { Self::from_u32(value).unwrap_or_default() }
518}
519
520impl AsScalar<1> for KeyboardKind {
521 fn as_scalar(&self) -> [u32; 1] { [*self as u32] }
522}
523
524impl From<&ArchivedKeyboardKind> for KeyboardKind {
525 fn from(archived: &ArchivedKeyboardKind) -> Self {
526 rkyv::deserialize::<_, rkyv::rancor::Error>(archived).unwrap()
527 }
528}