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, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
43pub struct RegisterApp {
44 pub cid: CID,
45 pub name: AppName,
46 pub height: usize,
47}
48
49#[derive(Debug, Copy, Clone, FromPrimitive, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize, Default)]
50pub enum ModalStyle {
51 #[default]
54 SlideUpDraggablePopup = 0,
55
56 SlideUpFixedPopup,
59
60 SlideUpFullscreen,
62
63 Instant,
65}
66
67#[derive(Clone, Debug, Default)]
69pub struct GuiApiLight<P: CheckedPermissions> {
70 conn: CheckedConn<P>,
71}
72
73#[derive(Debug)]
75pub struct GuiApi<P: CheckedPermissions> {
76 inner: GuiApiLight<P>,
77 cid_self: CID,
78 sid: SID,
79}
80
81impl<P: CheckedPermissions> GuiApiLight<P> {
82 pub fn connect() -> Self { Self { conn: CheckedConn::default() } }
85
86 pub fn switch_to(&self, next_pid: PID, x: usize, y: usize) -> Result<(), GuiServerError>
89 where
90 P: MessageAllowed<msg::SwitchTo>,
91 {
92 self.conn.try_send_scalar(msg::SwitchTo { next_pid: next_pid.get() as usize, x, y })?;
93 Ok(())
94 }
95
96 pub fn switch_to_launcher(&self) -> Result<bool, GuiServerError>
98 where
99 P: MessageAllowed<msg::SwitchToLauncher>,
100 {
101 Ok(self.conn.try_send_blocking_scalar(msg::SwitchToLauncher)?)
102 }
103
104 pub fn is_locked(&self) -> Result<bool, GuiServerError>
105 where
106 P: MessageAllowed<msg::IsLocked>,
107 {
108 Ok(self.conn.try_send_blocking_scalar(msg::IsLocked)?)
109 }
110
111 pub fn shutdown(&self) -> Result<(), GuiServerError>
112 where
113 P: MessageAllowed<msg::Shutdown>,
114 {
115 Ok(self.conn.try_send_blocking_scalar(msg::Shutdown { reboot: false })?)
116 }
117
118 pub fn reboot(&self) -> Result<(), GuiServerError>
119 where
120 P: MessageAllowed<msg::Shutdown>,
121 {
122 Ok(self.conn.try_send_blocking_scalar(msg::Shutdown { reboot: true })?)
123 }
124
125 pub fn close_app(&self, pid: PID) -> Result<(), GuiServerError>
128 where
129 P: MessageAllowed<msg::CloseApp>,
130 {
131 Ok(self.conn.try_send_blocking_scalar(msg::CloseApp { pid: pid.get() as usize })??)
132 }
133
134 pub fn capture_screen(&self) -> Result<xous::DropDeallocate, GuiServerError>
138 where
139 P: MessageAllowed<msg::CaptureScreen>,
140 {
141 let mem = xous::map_memory(None, None, consts::FB_SIZE, xous::MemoryFlags::W)?;
142 self.conn.lend_mut(msg::CaptureScreen(mem));
143 Ok(xous::DropDeallocate::new(mem))
144 }
145
146 pub fn inject_touch(&self, touch: touch::Touch) -> Result<(), GuiServerError>
148 where
149 P: MessageAllowed<msg::InjectTouch>,
150 {
151 self.conn.try_send_scalar(msg::InjectTouch(touch))?;
152 Ok(())
153 }
154
155 pub fn inject_key(&self, is_pressed: bool, key: Key) -> Result<(), GuiServerError>
157 where
158 P: MessageAllowed<msg::InjectKey>,
159 {
160 self.conn.try_send_scalar(msg::InjectKey { is_pressed, key })?;
161 Ok(())
162 }
163
164 pub fn inject_power_button(&self, is_pressed: bool) -> Result<(), GuiServerError>
166 where
167 P: MessageAllowed<msg::InjectPowerButton>,
168 {
169 self.conn.try_send_scalar(msg::InjectPowerButton(is_pressed))?;
170 Ok(())
171 }
172
173 pub fn update_kiosk_policy(&self, policy: msg::UpdateKioskPolicy) -> Result<(), GuiServerError>
174 where
175 P: MessageAllowed<msg::UpdateKioskPolicy>,
176 {
177 self.conn.try_send_scalar(policy)?;
178 Ok(())
179 }
180}
181
182impl<P: CheckedPermissions> GuiApi<P> {
183 pub fn register(name: &str, height: usize) -> Result<Self, GuiServerError>
185 where
186 P: MessageAllowed<msg::RegisterAppMessage>,
187 {
188 let (api, cid) = Self::register_inner()?;
189 api.inner.conn.send_blocking_archive(msg::RegisterAppMessage(RegisterApp {
190 cid,
191 name: name.into(),
192 height,
193 }))?;
194 Ok(api)
195 }
196
197 pub fn register_control_center(height: usize) -> Result<Self, GuiServerError>
200 where
201 P: MessageAllowed<msg::RegisterControlCenter>,
202 {
203 let (api, cid) = Self::register_inner()?;
204 api.inner.conn.send_blocking_archive(msg::RegisterControlCenter { cid, height })?;
205 Ok(api)
206 }
207
208 pub fn register_keyboard(height: usize) -> Result<Self, GuiServerError>
211 where
212 P: MessageAllowed<msg::RegisterKeyboard>,
213 {
214 let (api, cid) = Self::register_inner()?;
215 api.inner.conn.send_blocking_archive(msg::RegisterKeyboard { cid, height })?;
216 Ok(api)
217 }
218
219 pub fn register_with_role<M>(name: &str, height: usize) -> Result<Self, GuiServerError>
222 where
223 M: msg::RoleClaim,
224 P: MessageAllowed<msg::RegisterAppMessage> + MessageAllowed<M>,
225 {
226 let (api, cid) = Self::register_inner()?;
227 api.inner.conn.send_blocking_scalar(M::default());
228 api.inner.conn.send_blocking_archive(msg::RegisterAppMessage(RegisterApp {
229 cid,
230 name: name.into(),
231 height,
232 }))?;
233 Ok(api)
234 }
235
236 fn register_inner() -> Result<(Self, CID), GuiServerError> {
237 let sid = xous::create_server()?;
238 let cid_self = xous::connect(sid)?;
239 let inner = GuiApiLight::connect();
240 let api = Self { inner, sid, cid_self };
241 let gui_server_pid = api.inner.conn.get_remote_pid();
242
243 let gui_server_cid = xous::connect_for_process(gui_server_pid, api.sid)?;
244 xous::allow_messages_on_connection(gui_server_pid, gui_server_cid, 0..64)?;
245
246 Ok((api, gui_server_cid))
247 }
248
249 pub fn sid(&self) -> SID { self.sid }
250
251 pub fn submit_frame(&self, buffer: MemoryRange) -> Result<(), GuiServerError>
253 where
254 P: MessageAllowed<msg::SubmitFrame>,
255 {
256 Ok(self.conn.try_send_move(msg::SubmitFrame { buffer })?)
257 }
258
259 pub fn show_camera(&self, y_pos: u16) -> Result<(), GuiServerError>
260 where
261 P: MessageAllowed<msg::ShowCamera>,
262 {
263 self.conn.try_send_scalar(msg::ShowCamera { y_pos })?;
264 Ok(())
265 }
266
267 pub fn hide_camera(&self) -> Result<(), GuiServerError>
268 where
269 P: MessageAllowed<msg::HideCamera>,
270 {
271 self.conn.try_send_scalar(msg::HideCamera)?;
272 Ok(())
273 }
274
275 pub fn update_keyboard(&self, msg: msg::UpdateKeyboard) -> Result<(), GuiServerError>
276 where
277 P: MessageAllowed<msg::UpdateKeyboard>,
278 {
279 self.conn.try_send_archive(msg)?;
280 Ok(())
281 }
282
283 pub fn hide_keyboard(&self) -> Result<(), GuiServerError>
284 where
285 P: MessageAllowed<msg::HideKeyboard>,
286 {
287 self.conn.try_send_scalar(msg::HideKeyboard)?;
288 Ok(())
289 }
290
291 pub fn notify_login_success(&self) -> Result<(), GuiServerError>
292 where
293 P: MessageAllowed<msg::LoginSuccess>,
294 {
295 self.conn.try_send_scalar(msg::LoginSuccess)?;
296 Ok(())
297 }
298
299 pub fn wake_event_loop(&self) {
300 let msg = xous::Message::new_scalar(InputMessage::Noop as usize, 0, 0, 0, 0);
301 if let Err(e) = xous::send_message(self.cid_self, msg) {
302 log::error!("Failed to send wake event to self: {e:?}");
303 }
304 }
305
306 pub fn request_redraw(&self) -> Result<(), GuiServerError>
307 where
308 P: MessageAllowed<msg::RequestRedraw>,
309 {
310 self.conn.try_send_scalar(msg::RequestRedraw)?;
311 Ok(())
312 }
313
314 pub fn try_receive_input(&self) -> Option<(InputMessage, xous::MessageEnvelope)> {
315 if let Ok(Some(msg)) = xous::try_receive_message(self.sid) {
316 let opcode = FromPrimitive::from_usize(msg.body.id());
317 return opcode.map(|opcode| (opcode, msg));
318 }
319
320 None
321 }
322
323 pub fn receive_input(&self) -> Result<(InputMessage, xous::MessageEnvelope), GuiServerError> {
324 xous::receive_message(self.sid)
325 .map(|msg| {
326 let opcode = FromPrimitive::from_usize(msg.body.id());
327 (opcode.expect("input opcode"), msg)
328 })
329 .map_err(Into::into)
330 }
331
332 pub fn key_pressed(&self, key: Key) -> Result<(), GuiServerError>
333 where
334 P: MessageAllowed<msg::KeyPressed>,
335 {
336 self.conn.try_send_scalar(msg::KeyPressed(Some(key)))?;
337 Ok(())
338 }
339
340 pub fn key_released(&self, key: Key) -> Result<(), GuiServerError>
341 where
342 P: MessageAllowed<msg::KeyReleased>,
343 {
344 self.conn.try_send_scalar(msg::KeyReleased(Some(key)))?;
345 Ok(())
346 }
347
348 pub fn animate_next_frame(&self, animation_kind: NextFrameAnimationKind) -> Result<(), GuiServerError>
349 where
350 P: MessageAllowed<msg::AnimateNextFrame>,
351 {
352 self.conn.try_send_scalar(msg::AnimateNextFrame { animation_kind })?;
353 Ok(())
354 }
355}
356
357impl<P: CheckedPermissions> std::ops::Deref for GuiApi<P> {
358 type Target = GuiApiLight<P>;
359
360 fn deref(&self) -> &Self::Target { &self.inner }
361}
362
363#[derive(Debug, PartialEq, num_derive::FromPrimitive, num_derive::ToPrimitive, Copy, Clone)]
364pub enum InputMessage {
365 Touch = 0,
366 KeyPress,
367 KeyRelease,
368
369 NavigationFocused,
372
373 NavigationCancelled,
375
376 Noop,
378
379 Visible,
381
382 Hidden,
384
385 FrameBuffer,
388
389 Custom1,
390 Custom2,
391 Custom3,
392 Custom4,
393
394 CloseRequested,
396
397 Scroll,
401}
402
403#[derive(Debug, Copy, Clone)]
404pub enum Key {
405 Char(usize),
406 Backspace,
407 Delete,
408 CursorLeft,
409 CursorRight,
410 Enter,
411 Tab,
412}
413
414impl server::AsScalar<2> for Key {
415 fn as_scalar(&self) -> [u32; 2] {
416 match self {
417 Key::Char(c) => [0, *c as _],
418 Key::Backspace => [1, 0],
419 Key::Delete => [2, 0],
420 Key::CursorLeft => [3, 0],
421 Key::CursorRight => [4, 0],
422 Key::Enter => [5, 0],
423 Key::Tab => [6, 0],
424 }
425 }
426}
427
428impl server::FromScalar<2> for Key {
429 fn from_scalar(value: [u32; 2]) -> Self {
430 match value[0] {
431 1 => Key::Backspace,
432 2 => Key::Delete,
433 3 => Key::CursorLeft,
434 4 => Key::CursorRight,
435 5 => Key::Enter,
436 6 => Key::Tab,
437 _ => Key::Char(value[1] as _),
438 }
439 }
440}
441
442impl<P: CheckedPermissions> Drop for GuiApi<P> {
443 fn drop(&mut self) {
444 if let Err(e) = xous::destroy_server(self.sid) {
445 log::error!("Error destroying gui api event server: {e:?}");
446 }
447 }
448}
449
450#[derive(Debug, Copy, Clone, FromPrimitive, Default)]
451pub enum NextFrameAnimationKind {
452 #[default]
453 SlideInLeft = 0,
454 SlideInRight,
455 SlideOutLeft,
456 SlideOutRight,
457}
458
459#[derive(
460 Debug,
461 Copy,
462 Clone,
463 FromPrimitive,
464 Default,
465 PartialEq,
466 Eq,
467 rkyv::Archive,
468 rkyv::Serialize,
469 rkyv::Deserialize,
470)]
471#[rkyv(derive(Debug))]
472pub enum KeyboardKind {
473 #[default]
474 Alphanumeric = 0,
475 Password,
476 Numbers,
477 Decimal,
478 Email,
479}
480
481impl FromScalar<1> for KeyboardKind {
482 fn from_scalar([value]: [u32; 1]) -> Self { Self::from_u32(value).unwrap_or_default() }
483}
484
485impl AsScalar<1> for KeyboardKind {
486 fn as_scalar(&self) -> [u32; 1] { [*self as u32] }
487}
488
489impl From<&ArchivedKeyboardKind> for KeyboardKind {
490 fn from(archived: &ArchivedKeyboardKind) -> Self {
491 rkyv::deserialize::<_, rkyv::rancor::Error>(archived).unwrap()
492 }
493}