1use core::marker::PhantomData;
2
3use abi_stable::{
4 StableAbi, sabi_trait,
5 std_types::{RBox, ROption, RStr, RString, RVec},
6};
7
8use crate::{
9 payload::{PayloadBox, PayloadParserBox},
10 store::{ErasedRef, ErasedValue, Store, StoreCallbackBox, TypeKey},
11 surfaces::{
12 ComponentFactoryBox, CompositorFactoryBox, ConfigParserBox, DeployableDesktopItemFactoryBox,
13 DesktopItemFactoryBox, ForegroundServiceFactoryBox, PayloadFactoryBox, QuickSettingsFactoryBox,
14 RenderableFactoryBox, ServiceFactoryBox, SpotlightFactoryBox,
15 },
16 types::{
17 AudioState, BluetoothState, CanvasRef, Color, CompositorState, Length, NetworkState, Node,
18 NotchStatus, Notification, PowerState, SystemState, Theme, TrayState,
19 },
20};
21
22#[sabi_trait]
23pub trait Host: Send + Sync + 'static {
24 fn log(&self, level: i32, message: RString);
25 fn plugin_error(&self, code: u32, message: RString);
26
27 fn option_str(&self, key: RStr<'_>) -> ROption<RString>;
28 fn option_f64(&self, key: RStr<'_>) -> ROption<f64>;
29 fn option_i64(&self, key: RStr<'_>) -> ROption<i64>;
30 fn option_bool(&self, key: RStr<'_>) -> ROption<bool>;
31
32 fn config_str(&self, key: RStr<'_>) -> ROption<RString>;
33 fn config_f64(&self, key: RStr<'_>) -> ROption<f64>;
34 fn config_i64(&self, key: RStr<'_>) -> ROption<i64>;
35 fn config_bool(&self, key: RStr<'_>) -> ROption<bool>;
36
37 fn set_interval(&self, millis: u32, repeating: bool, tag: u32) -> i32;
38 fn register_fd(&self, fd: i32, action: u32) -> i32;
39 fn request_redraw(&self, window: u64);
40
41 fn compositor_state(&self) -> ROption<CompositorState>;
42 fn set_compositor_state(&self, state: CompositorState);
43 fn compositor_register_fd(&self, fd: i32, flags: u32) -> i32;
44
45 fn audio_state(&self) -> ROption<AudioState>;
46 fn bluetooth_state(&self) -> ROption<BluetoothState>;
47 fn network_state(&self) -> ROption<NetworkState>;
48 fn system_state(&self) -> ROption<SystemState>;
49 fn tray_state(&self) -> ROption<TrayState>;
50 fn power_state(&self) -> ROption<PowerState>;
51
52 fn style_number(&self, classes: RStr<'_>, property: RStr<'_>) -> ROption<f32>;
53 fn style_bool(&self, classes: RStr<'_>, property: RStr<'_>) -> ROption<bool>;
54 fn style_color(&self, classes: RStr<'_>, property: RStr<'_>) -> ROption<Color>;
55 fn theme(&self) -> Theme;
56 fn register_style(&self, css: RStr<'_>) -> i32;
57
58 fn dispatch(&self, command: RStr<'_>) -> bool;
59
60 fn persistence_get(&self, key: RStr<'_>) -> ROption<RString>;
61 fn persistence_set(&self, key: RStr<'_>, value: RStr<'_>) -> bool;
62 fn persistence_remove(&self, key: RStr<'_>) -> bool;
63
64 fn notify(&self, notification: Notification) -> ROption<u32>;
65
66 fn store_insert(&self, key: TypeKey, value: ErasedValue) -> bool;
67 fn store_get<'a>(&self, ctx: &Context<'a>, key: TypeKey) -> ROption<ErasedRef<'a>>;
68 fn store_remove(&self, key: TypeKey) -> ROption<ErasedValue>;
69 fn store_contains(&self, key: TypeKey) -> bool;
70 fn store_subscribe(&self, key: TypeKey, callback: StoreCallbackBox);
71
72 fn register_component(&self, name: RString, factory: ComponentFactoryBox);
73 fn register_renderable(&self, name: RString, factory: RenderableFactoryBox);
74 fn register_payload(&self, command: RString, factory: PayloadFactoryBox);
75 fn register_desktop_item(&self, name: RString, factory: DesktopItemFactoryBox);
76 fn register_desktop_item_deployer(&self, name: RString, factory: DeployableDesktopItemFactoryBox);
77 fn register_compositor(&self, name: RString, factory: CompositorFactoryBox);
78 fn register_spotlight(&self, name: RString, factory: SpotlightFactoryBox);
79 fn register_quick_settings(&self, name: RString, factory: QuickSettingsFactoryBox);
80 fn register_config_parser(&self, name: RString, parser: ConfigParserBox);
81 fn register_command(&self, name: RString, title: RString, description: RString);
82
83 fn register_payload_parser(&self, commands: RVec<RString>, parser: PayloadParserBox);
84
85 fn dispatch_payload(&self, name: RStr<'_>, payload: PayloadBox) -> bool;
86
87 fn canvas(&self, width: u32, height: u32) -> ROption<CanvasRef>;
88
89 fn store_touch(&self, key: TypeKey) -> bool;
90
91 fn register_service(&self, name: RString, factory: ServiceFactoryBox, autostart: bool);
92 fn register_foreground_service(&self, name: RString, factory: ForegroundServiceFactoryBox);
93 fn service_start(&self, name: RStr<'_>) -> bool;
94 fn service_stop(&self, name: RStr<'_>) -> bool;
95 fn service_running(&self, name: RStr<'_>) -> bool;
96 fn service_send(&self, name: RStr<'_>, message: RStr<'_>) -> bool;
97
98 fn service_notify(&self) -> bool;
99 fn service_sleep(&self, millis: u64) -> bool;
100 fn service_stopping(&self) -> bool;
101 fn service_recv(&self, timeout_millis: u32) -> ROption<RString>;
102
103 fn notch_set_status(&self, name: RString, status: ROption<NotchStatus>);
104}
105
106pub type HostRef<'a> = &'a Host_TO<RBox<()>>;
107
108#[repr(C)]
109#[derive(StableAbi, Clone, Copy)]
110pub struct Context<'a> {
111 host: HostRef<'a>,
112}
113
114impl<'a> Context<'a> {
115 pub fn new(host: HostRef<'a>) -> Self {
116 Self { host }
117 }
118
119 pub fn host(&self) -> HostRef<'a> {
120 self.host
121 }
122
123 pub fn log(&self, level: i32, message: impl Into<RString>) {
124 self.host.log(level, message.into());
125 }
126
127 pub fn error(&self, code: u32, message: impl Into<RString>) {
128 self.host.plugin_error(code, message.into());
129 }
130
131 pub fn set_interval(&self, millis: u32, repeating: bool, tag: u32) -> i32 {
132 self.host.set_interval(millis, repeating, tag)
133 }
134
135 pub fn register_fd(&self, fd: i32, action: u32) -> i32 {
136 self.host.register_fd(fd, action)
137 }
138
139 pub fn request_redraw(&self, window: u64) {
140 self.host.request_redraw(window);
141 }
142
143 pub fn options(&self) -> Options<'a> {
144 Options { ctx: *self }
145 }
146
147 pub fn update_status(&self, name: impl Into<RString>, status: NotchStatus) {
148 self
149 .host
150 .notch_set_status(name.into(), ROption::RSome(status));
151 }
152
153 pub fn remove_status(&self, name: impl Into<RString>) {
154 self.host.notch_set_status(name.into(), ROption::RNone);
155 }
156
157 pub fn config_str(&self, key: &str) -> Option<RString> {
158 match self.host.config_str(RStr::from_str(key)) {
159 ROption::RSome(value) => Some(value),
160 ROption::RNone => None,
161 }
162 }
163
164 pub fn config_f64(&self, key: &str) -> Option<f64> {
165 match self.host.config_f64(RStr::from_str(key)) {
166 ROption::RSome(value) => Some(value),
167 ROption::RNone => None,
168 }
169 }
170
171 pub fn config_i64(&self, key: &str) -> Option<i64> {
172 match self.host.config_i64(RStr::from_str(key)) {
173 ROption::RSome(value) => Some(value),
174 ROption::RNone => None,
175 }
176 }
177
178 pub fn config_bool(&self, key: &str) -> Option<bool> {
179 match self.host.config_bool(RStr::from_str(key)) {
180 ROption::RSome(value) => Some(value),
181 ROption::RNone => None,
182 }
183 }
184
185 pub fn style(&self, classes: &str) -> StyleRef<'a> {
186 StyleRef {
187 ctx: *self,
188 classes: RString::from(classes),
189 }
190 }
191
192 pub fn resolve(&self, classes: &[&str]) -> StyleRef<'a> {
193 self.style(&classes.join(" "))
194 }
195
196 pub fn theme(&self) -> Theme {
197 self.host.theme()
198 }
199
200 pub fn compositor_state(&self) -> Option<CompositorState> {
201 match self.host.compositor_state() {
202 ROption::RSome(state) => Some(state),
203 ROption::RNone => None,
204 }
205 }
206
207 pub fn set_compositor_state(&self, state: CompositorState) {
208 self.host.set_compositor_state(state);
209 }
210
211 pub fn dispatch(&self, command: &str) -> bool {
212 self.host.dispatch(RStr::from_str(command))
213 }
214
215 pub fn dispatch_with_string(&self, name: &str, payload: &str) -> bool {
216 let command = format!("{name} {payload}");
217 self.dispatch(&command)
218 }
219
220 pub fn dispatch_payload(&self, name: &str, payload: PayloadBox) -> bool {
221 self.host.dispatch_payload(RStr::from_str(name), payload)
222 }
223
224 pub fn service_start(&self, name: &str) -> bool {
225 self.host.service_start(RStr::from_str(name))
226 }
227
228 pub fn service_stop(&self, name: &str) -> bool {
229 self.host.service_stop(RStr::from_str(name))
230 }
231
232 pub fn service_running(&self, name: &str) -> bool {
233 self.host.service_running(RStr::from_str(name))
234 }
235
236 pub fn service_send(&self, name: &str, message: &str) -> bool {
237 self
238 .host
239 .service_send(RStr::from_str(name), RStr::from_str(message))
240 }
241
242 pub fn persistence_get_raw(&self, key: &str) -> Option<RString> {
243 match self.host.persistence_get(RStr::from_str(key)) {
244 ROption::RSome(value) => Some(value),
245 ROption::RNone => None,
246 }
247 }
248
249 pub fn persistence_set_raw(&self, key: &str, value: &str) -> bool {
250 self
251 .host
252 .persistence_set(RStr::from_str(key), RStr::from_str(value))
253 }
254
255 pub fn persistence_remove(&self, key: &str) -> bool {
256 self.host.persistence_remove(RStr::from_str(key))
257 }
258
259 pub fn persistence_get<T: serde::de::DeserializeOwned>(&self, key: &str) -> Option<T> {
260 let raw = self.persistence_get_raw(key)?;
261 match ron::from_str::<T>(&raw) {
262 Ok(value) => Some(value),
263 Err(e) => {
264 self.log(1, format!("[persistence] failed to decode {key:?}: {e}"));
265 None
266 }
267 }
268 }
269
270 pub fn persistence_open_or_get<T>(&self, key: &str, default: T) -> T
271 where
272 T: serde::Serialize + serde::de::DeserializeOwned,
273 {
274 match self.persistence_get(key) {
275 Some(value) => value,
276 None => {
277 self.persistence_set(key, &default);
278 default
279 }
280 }
281 }
282
283 pub fn persistence_set<T: serde::Serialize>(&self, key: &str, value: &T) -> bool {
284 match ron::ser::to_string(value) {
285 Ok(raw) => self.persistence_set_raw(key, &raw),
286 Err(_) => false,
287 }
288 }
289
290 pub fn persistence_has(&self, key: &str) -> bool {
291 self.persistence_get_raw(key).is_some()
292 }
293
294 pub fn notify(&self, notification: &Notification) -> Option<u32> {
295 match self.host.notify(notification.clone()) {
296 ROption::RSome(id) => Some(id),
297 ROption::RNone => None,
298 }
299 }
300
301 pub fn audio_state(&self) -> Option<AudioState> {
302 match self.host.audio_state() {
303 ROption::RSome(state) => Some(state),
304 ROption::RNone => None,
305 }
306 }
307
308 pub fn bluetooth_state(&self) -> Option<BluetoothState> {
309 match self.host.bluetooth_state() {
310 ROption::RSome(state) => Some(state),
311 ROption::RNone => None,
312 }
313 }
314
315 pub fn network_state(&self) -> Option<NetworkState> {
316 match self.host.network_state() {
317 ROption::RSome(state) => Some(state),
318 ROption::RNone => None,
319 }
320 }
321
322 pub fn system_state(&self) -> Option<SystemState> {
323 match self.host.system_state() {
324 ROption::RSome(state) => Some(state),
325 ROption::RNone => None,
326 }
327 }
328
329 pub fn tray_state(&self) -> Option<TrayState> {
330 match self.host.tray_state() {
331 ROption::RSome(state) => Some(state),
332 ROption::RNone => None,
333 }
334 }
335
336 pub fn power_state(&self) -> Option<PowerState> {
337 match self.host.power_state() {
338 ROption::RSome(state) => Some(state),
339 ROption::RNone => None,
340 }
341 }
342
343 pub fn store(&self) -> Store<'a> {
344 Store::new(*self)
345 }
346
347 pub fn canvas(&self, width: u32, height: u32) -> Option<CanvasBuffer<'_>> {
348 match self.host.canvas(width, height) {
349 ROption::RSome(reference) if !reference.ptr.is_null() && reference.len > 0 => {
350 Some(CanvasBuffer {
351 ptr: reference.ptr,
352 len: reference.len,
353 id: reference.id,
354 marker: PhantomData,
355 })
356 }
357 _ => None,
358 }
359 }
360
361 pub fn canvas_node(&self, width: u32, height: u32, draw: impl FnOnce(&mut [u8])) -> Node {
362 self.canvas_node_inner(0, width, height, draw)
363 }
364
365 pub fn canvas_cached_node(
366 &self,
367 cache_key: u64,
368 width: u32,
369 height: u32,
370 draw: impl FnOnce(&mut [u8]),
371 ) -> Node {
372 self.canvas_node_inner(cache_key, width, height, draw)
373 }
374
375 fn canvas_node_inner(
376 &self,
377 cache_key: u64,
378 width: u32,
379 height: u32,
380 draw: impl FnOnce(&mut [u8]),
381 ) -> Node {
382 match self.canvas(width, height) {
383 Some(mut buffer) => {
384 draw(&mut buffer);
385 Node::Canvas {
386 cache_key,
387 pixel_width: width,
388 pixel_height: height,
389 width: Length::Shrink,
390 height: Length::Shrink,
391 pixels: RVec::new(),
392 pool_id: buffer.id(),
393 classes: RVec::new(),
394 }
395 }
396 None => {
397 if cache_key == 0 {
398 Node::canvas(width, height, draw)
399 } else {
400 Node::canvas_cached(cache_key, width, height, draw)
401 }
402 }
403 }
404 }
405
406 pub fn config<T: Send + Sync + Clone + 'static>(&self, name: &str) -> Option<T> {
407 self.with_config(name, |value: &T| value.clone())
408 }
409
410 pub fn with_config<T: Send + Sync + 'static, R>(
411 &self,
412 name: &str,
413 f: impl FnOnce(&T) -> R,
414 ) -> Option<R> {
415 crate::sdk::config_values()
416 .lock()
417 .unwrap()
418 .get(name)
419 .and_then(|value| value.downcast_ref::<T>())
420 .map(f)
421 }
422}
423
424#[repr(C)]
425#[derive(StableAbi, Clone, Copy)]
426pub struct ServiceContext<'a> {
427 host: HostRef<'a>,
428}
429
430impl<'a> ServiceContext<'a> {
431 pub fn new(host: HostRef<'a>) -> Self {
432 Self { host }
433 }
434
435 pub fn host(&self) -> HostRef<'a> {
436 self.host
437 }
438
439 pub fn log(&self, level: i32, message: impl Into<RString>) {
440 self.host.log(level, message.into());
441 }
442
443 pub fn error(&self, code: u32, message: impl Into<RString>) {
444 self.host.plugin_error(code, message.into());
445 }
446
447 pub fn sleep(&self, millis: u64) -> bool {
448 self.host.service_sleep(millis)
449 }
450
451 pub fn stopping(&self) -> bool {
452 self.host.service_stopping()
453 }
454
455 pub fn changed(&self) -> bool {
456 self.host.service_notify()
457 }
458
459 pub fn recv(&self, timeout_millis: u32) -> Option<RString> {
460 match self.host.service_recv(timeout_millis) {
461 ROption::RSome(message) => Some(message),
462 ROption::RNone => None,
463 }
464 }
465
466 pub fn send(&self, name: &str, message: &str) -> bool {
467 self
468 .host
469 .service_send(RStr::from_str(name), RStr::from_str(message))
470 }
471
472 pub fn dispatch(&self, command: &str) -> bool {
473 self.host.dispatch(RStr::from_str(command))
474 }
475
476 pub fn dispatch_with_string(&self, name: &str, payload: &str) -> bool {
477 let command = format!("{name} {payload}");
478 self.dispatch(&command)
479 }
480
481 pub fn config_str(&self, key: &str) -> Option<RString> {
482 match self.host.config_str(RStr::from_str(key)) {
483 ROption::RSome(value) => Some(value),
484 ROption::RNone => None,
485 }
486 }
487
488 pub fn config_f64(&self, key: &str) -> Option<f64> {
489 match self.host.config_f64(RStr::from_str(key)) {
490 ROption::RSome(value) => Some(value),
491 ROption::RNone => None,
492 }
493 }
494
495 pub fn config_i64(&self, key: &str) -> Option<i64> {
496 match self.host.config_i64(RStr::from_str(key)) {
497 ROption::RSome(value) => Some(value),
498 ROption::RNone => None,
499 }
500 }
501
502 pub fn config_bool(&self, key: &str) -> Option<bool> {
503 match self.host.config_bool(RStr::from_str(key)) {
504 ROption::RSome(value) => Some(value),
505 ROption::RNone => None,
506 }
507 }
508
509 pub fn config<T: Send + Sync + Clone + 'static>(&self, name: &str) -> Option<T> {
510 self.with_config(name, |value: &T| value.clone())
511 }
512
513 pub fn with_config<T: Send + Sync + 'static, R>(
514 &self,
515 name: &str,
516 f: impl FnOnce(&T) -> R,
517 ) -> Option<R> {
518 crate::sdk::config_values()
519 .lock()
520 .unwrap()
521 .get(name)
522 .and_then(|value| value.downcast_ref::<T>())
523 .map(f)
524 }
525
526 pub fn persistence_get_raw(&self, key: &str) -> Option<RString> {
527 match self.host.persistence_get(RStr::from_str(key)) {
528 ROption::RSome(value) => Some(value),
529 ROption::RNone => None,
530 }
531 }
532
533 pub fn persistence_set_raw(&self, key: &str, value: &str) -> bool {
534 self
535 .host
536 .persistence_set(RStr::from_str(key), RStr::from_str(value))
537 }
538
539 pub fn persistence_remove(&self, key: &str) -> bool {
540 self.host.persistence_remove(RStr::from_str(key))
541 }
542
543 pub fn persistence_get<T: serde::de::DeserializeOwned>(&self, key: &str) -> Option<T> {
544 let raw = self.persistence_get_raw(key)?;
545 match ron::from_str::<T>(&raw) {
546 Ok(value) => Some(value),
547 Err(e) => {
548 self.log(1, format!("[persistence] failed to decode {key:?}: {e}"));
549 None
550 }
551 }
552 }
553
554 pub fn persistence_open_or_get<T>(&self, key: &str, default: T) -> T
555 where
556 T: serde::Serialize + serde::de::DeserializeOwned,
557 {
558 match self.persistence_get(key) {
559 Some(value) => value,
560 None => {
561 self.persistence_set(key, &default);
562 default
563 }
564 }
565 }
566
567 pub fn persistence_set<T: serde::Serialize>(&self, key: &str, value: &T) -> bool {
568 match ron::ser::to_string(value) {
569 Ok(raw) => self.persistence_set_raw(key, &raw),
570 Err(_) => false,
571 }
572 }
573
574 pub fn persistence_has(&self, key: &str) -> bool {
575 self.persistence_get_raw(key).is_some()
576 }
577
578 pub fn notify(&self, notification: &Notification) -> Option<u32> {
579 match self.host.notify(notification.clone()) {
580 ROption::RSome(id) => Some(id),
581 ROption::RNone => None,
582 }
583 }
584}
585
586pub struct CanvasBuffer<'a> {
587 ptr: *mut u8,
588 len: usize,
589 id: u64,
590 marker: PhantomData<&'a mut [u8]>,
591}
592
593impl CanvasBuffer<'_> {
594 pub fn id(&self) -> u64 {
595 self.id
596 }
597}
598
599impl core::ops::Deref for CanvasBuffer<'_> {
600 type Target = [u8];
601
602 fn deref(&self) -> &[u8] {
603 unsafe { core::slice::from_raw_parts(self.ptr, self.len) }
604 }
605}
606
607impl core::ops::DerefMut for CanvasBuffer<'_> {
608 fn deref_mut(&mut self) -> &mut [u8] {
609 unsafe { core::slice::from_raw_parts_mut(self.ptr, self.len) }
610 }
611}
612
613#[derive(Clone, Copy)]
614pub struct Options<'a> {
615 ctx: Context<'a>,
616}
617
618impl Options<'_> {
619 pub fn str(&self, key: &str) -> Option<RString> {
620 match self.ctx.host.option_str(RStr::from_str(key)) {
621 ROption::RSome(value) => Some(value),
622 ROption::RNone => None,
623 }
624 }
625
626 pub fn f64(&self, key: &str) -> Option<f64> {
627 match self.ctx.host.option_f64(RStr::from_str(key)) {
628 ROption::RSome(value) => Some(value),
629 ROption::RNone => None,
630 }
631 }
632
633 pub fn i64(&self, key: &str) -> Option<i64> {
634 match self.ctx.host.option_i64(RStr::from_str(key)) {
635 ROption::RSome(value) => Some(value),
636 ROption::RNone => None,
637 }
638 }
639
640 pub fn bool(&self, key: &str) -> Option<bool> {
641 match self.ctx.host.option_bool(RStr::from_str(key)) {
642 ROption::RSome(value) => Some(value),
643 ROption::RNone => None,
644 }
645 }
646}
647
648#[derive(Clone)]
649pub struct StyleRef<'a> {
650 ctx: Context<'a>,
651 classes: RString,
652}
653
654impl StyleRef<'_> {
655 pub fn classes(&self) -> &str {
656 &self.classes
657 }
658
659 pub fn number(&self, property: &str) -> Option<f32> {
660 match self
661 .ctx
662 .host
663 .style_number(RStr::from_str(&self.classes), RStr::from_str(property))
664 {
665 ROption::RSome(value) => Some(value),
666 ROption::RNone => None,
667 }
668 }
669
670 pub fn length(&self, property: &str) -> Option<f32> {
671 self.number(property)
672 }
673
674 pub fn bool(&self, property: &str) -> Option<bool> {
675 match self
676 .ctx
677 .host
678 .style_bool(RStr::from_str(&self.classes), RStr::from_str(property))
679 {
680 ROption::RSome(value) => Some(value),
681 ROption::RNone => None,
682 }
683 }
684
685 pub fn color(&self, property: &str) -> Option<Color> {
686 match self
687 .ctx
688 .host
689 .style_color(RStr::from_str(&self.classes), RStr::from_str(property))
690 {
691 ROption::RSome(value) => Some(value),
692 ROption::RNone => None,
693 }
694 }
695
696 pub fn color_or(&self, property: &str, default: Color) -> Color {
697 self.color(property).unwrap_or(default)
698 }
699
700 pub fn padding(&self, default: impl Into<[f32; 2]>) -> [f32; 2] {
701 if let Some(x) = self.length("padding-x") {
702 let y = self.length("padding-y").unwrap_or(x);
703 return [y, x];
704 }
705 if let Some(p) = self.length("padding") {
706 return [p, p];
707 }
708 default.into()
709 }
710}