Skip to main content

slowshell_plugin_abi/
host.rs

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}