1use crate::common::{Dimension, Point, Rgb, Vector2};
4use crate::error::Error;
5use crate::helpers::Ignore;
6use core::fmt::{Debug, Formatter};
7use minicbor::Encode;
8use oc_wasm_futures::invoke::{component_method, Buffer};
9use oc_wasm_helpers::{error::NullAndStringOr, Lockable};
10use oc_wasm_safe::{
11 component::{Invoker, MethodCallError},
12 extref, Address,
13};
14
15pub const TYPE: &str = "gpu";
17
18#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
20pub struct Gpu(Address);
21
22impl Gpu {
23 #[must_use = "This function is only useful for its return value"]
29 pub fn new(address: Address) -> Self {
30 Self(address)
31 }
32
33 #[must_use = "This function is only useful for its return value"]
35 pub fn address(&self) -> &Address {
36 &self.0
37 }
38}
39
40impl<'a, B: 'a + Buffer> Lockable<'a, 'a, B> for Gpu {
41 type Locked = Locked<'a, B>;
42
43 fn lock(&self, invoker: &'a mut Invoker, buffer: &'a mut B) -> Self::Locked {
44 Locked {
45 address: self.0,
46 invoker,
47 buffer,
48 }
49 }
50}
51
52pub struct Locked<'a, B: Buffer> {
62 address: Address,
64
65 invoker: &'a mut Invoker,
67
68 buffer: &'a mut B,
70}
71
72impl<'a, B: Buffer> Locked<'a, B> {
73 pub async fn bind(&mut self, screen: Address, reset: bool) -> Result<(), Error> {
83 let ret: NullAndStringOr<'_, Ignore> = component_method(
84 self.invoker,
85 self.buffer,
86 &self.address,
87 "bind",
88 Some(&(screen, reset)),
89 )
90 .await?;
91 match ret {
92 NullAndStringOr::Ok(_) => Ok(()),
93 NullAndStringOr::Err(_) => Err(Error::BadScreen),
94 }
95 }
96
97 pub async fn get_screen(&mut self) -> Result<Option<Address>, Error> {
104 let ret: NullAndStringOr<'_, (Option<Address>,)> = component_method::<(), _, _>(
105 self.invoker,
106 self.buffer,
107 &self.address,
108 "getScreen",
109 None,
110 )
111 .await?;
112 Ok(match ret {
113 NullAndStringOr::Ok(ret) => ret.0,
114 NullAndStringOr::Err(_) => None,
115 })
116 }
117
118 pub async fn get_background(&mut self) -> Result<Colour, Error> {
125 self.get_colour("getBackground").await
126 }
127
128 pub async fn set_background(
136 &mut self,
137 colour: Colour,
138 ) -> Result<(Rgb, Option<PaletteIndex>), Error> {
139 self.set_colour(colour, "setBackground").await
140 }
141
142 pub async fn get_foreground(&mut self) -> Result<Colour, Error> {
149 self.get_colour("getForeground").await
150 }
151
152 pub async fn set_foreground(
160 &mut self,
161 colour: Colour,
162 ) -> Result<(Rgb, Option<PaletteIndex>), Error> {
163 self.set_colour(colour, "setForeground").await
164 }
165
166 pub async fn get_palette_colour(&mut self, index: PaletteIndex) -> Result<Rgb, Error> {
174 let ret: Result<NullAndStringOr<'_, (u32,)>, _> = component_method(
175 self.invoker,
176 self.buffer,
177 &self.address,
178 "getPaletteColor",
179 Some(&(index.0,)),
180 )
181 .await;
182 let ret = Self::map_bad_parameters(ret, Error::BadPaletteIndex)?;
183 let ret = Self::map_no_screen(ret)?;
184 Ok(Rgb(ret.0))
185 }
186
187 pub async fn set_palette_colour(
195 &mut self,
196 index: PaletteIndex,
197 colour: Rgb,
198 ) -> Result<Rgb, Error> {
199 let ret: Result<NullAndStringOr<'_, (u32,)>, _> = component_method(
200 self.invoker,
201 self.buffer,
202 &self.address,
203 "setPaletteColor",
204 Some(&(index.0, colour.0)),
205 )
206 .await;
207 let ret = Self::map_bad_parameters(ret, Error::BadPaletteIndex)?;
208 let ret = Self::map_no_screen(ret)?;
209 Ok(Rgb(ret.0))
210 }
211
212 pub async fn max_depth(&mut self) -> Result<u8, Error> {
220 let ret: NullAndStringOr<'_, (u8,)> = component_method::<(), _, _>(
221 self.invoker,
222 self.buffer,
223 &self.address,
224 "maxDepth",
225 None,
226 )
227 .await?;
228 let ret = Self::map_no_screen(ret)?;
229 Ok(ret.0)
230 }
231
232 pub async fn get_depth(&mut self) -> Result<u8, Error> {
239 let ret: NullAndStringOr<'_, (u8,)> = component_method::<(), _, _>(
240 self.invoker,
241 self.buffer,
242 &self.address,
243 "getDepth",
244 None,
245 )
246 .await?;
247 let ret = Self::map_no_screen(ret)?;
248 Ok(ret.0)
249 }
250
251 pub async fn set_depth(&mut self, depth: u8) -> Result<(), Error> {
259 let ret: Result<NullAndStringOr<'_, Ignore>, _> = component_method(
260 self.invoker,
261 self.buffer,
262 &self.address,
263 "setDepth",
264 Some(&(depth,)),
265 )
266 .await;
267 let ret = Self::map_bad_parameters(ret, Error::BadDepth)?;
268 Self::map_no_screen(ret)?;
269 Ok(())
270 }
271
272 pub async fn max_resolution(&mut self) -> Result<Dimension, Error> {
280 self.get_dimension("maxResolution").await
281 }
282
283 pub async fn get_resolution(&mut self) -> Result<Dimension, Error> {
290 self.get_dimension("getResolution").await
291 }
292
293 pub async fn set_resolution(&mut self, resolution: Dimension) -> Result<bool, Error> {
302 self.set_dimension("setResolution", resolution).await
303 }
304
305 pub async fn get_viewport(&mut self) -> Result<Dimension, Error> {
312 self.get_dimension("getViewport").await
313 }
314
315 pub async fn set_viewport(&mut self, resolution: Dimension) -> Result<bool, Error> {
323 self.set_dimension("setViewport", resolution).await
324 }
325
326 pub async fn get(&mut self, point: Point) -> Result<CharacterCellContents, Error> {
339 type Return<'character> = (&'character str, u32, u32, Option<u32>, Option<u32>);
340 let ret: Result<NullAndStringOr<'_, Return<'_>>, _> = component_method(
341 self.invoker,
342 self.buffer,
343 &self.address,
344 "get",
345 Some(&(point.x, point.y)),
346 )
347 .await;
348 let ret = Self::map_bad_parameters(ret, Error::BadCoordinate)?;
349 let ret = Self::map_no_screen(ret)?;
350 if let Some(character) = ret.0.chars().next() {
351 Ok(CharacterCellContents {
352 character,
353 foreground: (Rgb(ret.1), ret.3.map(PaletteIndex)),
354 background: (Rgb(ret.2), ret.4.map(PaletteIndex)),
355 })
356 } else {
357 Err(Error::BadComponent(oc_wasm_safe::error::Error::CborDecode))
360 }
361 }
362
363 pub async fn set(
371 &mut self,
372 position: Point,
373 text: &str,
374 direction: TextDirection,
375 ) -> Result<(), Error> {
376 #[derive(Encode)]
377 #[cbor(array)]
378 struct Params<'a> {
379 #[n(0)]
380 x: u32,
381 #[n(1)]
382 y: u32,
383 #[n(2)]
384 value: extref::String<'a>,
385 #[n(3)]
386 direction: TextDirection,
387 }
388 let text = unsafe { extref::String::new(text) };
390 let ret: NullAndStringOr<'_, Ignore> = component_method(
391 self.invoker,
392 self.buffer,
393 &self.address,
394 "set",
395 Some(&Params {
396 x: position.x,
397 y: position.y,
398 value: text,
399 direction,
400 }),
401 )
402 .await?;
403 match ret {
404 NullAndStringOr::Ok(_) => Ok(()),
405 NullAndStringOr::Err("no screen") => Err(Error::BadScreen),
406 NullAndStringOr::Err("not enough energy") => Err(Error::NotEnoughEnergy),
407 NullAndStringOr::Err(_) => {
408 Err(Error::BadComponent(oc_wasm_safe::error::Error::Unknown))
409 }
410 }
411 }
412
413 pub async fn copy(
421 &mut self,
422 source: Point,
423 dimension: Dimension,
424 translation: Vector2,
425 ) -> Result<(), Error> {
426 #[derive(Encode)]
427 #[cbor(array)]
428 struct Params {
429 #[n(0)]
430 x: u32,
431 #[n(1)]
432 y: u32,
433 #[n(2)]
434 width: u32,
435 #[n(3)]
436 height: u32,
437 #[n(4)]
438 tx: i32,
439 #[n(5)]
440 ty: i32,
441 }
442 let ret: NullAndStringOr<'_, Ignore> = component_method(
443 self.invoker,
444 self.buffer,
445 &self.address,
446 "copy",
447 Some(&Params {
448 x: source.x,
449 y: source.y,
450 width: dimension.width,
451 height: dimension.height,
452 tx: translation.x,
453 ty: translation.y,
454 }),
455 )
456 .await?;
457 match ret {
458 NullAndStringOr::Ok(_) => Ok(()),
459 NullAndStringOr::Err("no screen") => Err(Error::BadScreen),
460 NullAndStringOr::Err("not enough energy") => Err(Error::NotEnoughEnergy),
461 NullAndStringOr::Err(_) => {
462 Err(Error::BadComponent(oc_wasm_safe::error::Error::Unknown))
463 }
464 }
465 }
466
467 pub async fn fill(
475 &mut self,
476 target: Point,
477 dimension: Dimension,
478 character: char,
479 ) -> Result<(), Error> {
480 #[derive(Encode)]
481 #[cbor(array)]
482 struct Params<'a> {
483 #[n(0)]
484 x: u32,
485 #[n(1)]
486 y: u32,
487 #[n(2)]
488 width: u32,
489 #[n(3)]
490 height: u32,
491 #[n(4)]
492 value: &'a str,
493 }
494 let mut character_buffer = [0_u8; 4];
495 let character = character.encode_utf8(&mut character_buffer);
496 let ret: Result<NullAndStringOr<'_, Ignore>, _> = component_method(
497 self.invoker,
498 self.buffer,
499 &self.address,
500 "fill",
501 Some(&Params {
502 x: target.x,
503 y: target.y,
504 width: dimension.width,
505 height: dimension.height,
506 value: character,
507 }),
508 )
509 .await;
510 match ret {
511 Ok(NullAndStringOr::Ok(_)) => Ok(()),
512 Ok(NullAndStringOr::Err("no screen")) => Err(Error::BadScreen),
513 Ok(NullAndStringOr::Err("not enough energy")) => Err(Error::NotEnoughEnergy),
514 Ok(NullAndStringOr::Err(_)) => {
515 Err(Error::BadComponent(oc_wasm_safe::error::Error::Unknown))
516 }
517 Err(
518 e @ (MethodCallError::Other(exception) | MethodCallError::BadParameters(exception)),
519 ) => {
520 const INVALID_FILL_VALUE: &str = "invalid fill value";
526 const ERROR_MESSAGE_BUFFER_SIZE: usize = INVALID_FILL_VALUE.len();
527 let mut message_buffer = [0_u8; ERROR_MESSAGE_BUFFER_SIZE];
528 match exception.message(&mut message_buffer) {
529 Ok(INVALID_FILL_VALUE) => Err(Error::BadFillCharacter),
530 _ => Err(Error::BadComponent(e.into())),
531 }
532 }
533 Err(MethodCallError::TooManyDescriptors) => Err(Error::TooManyDescriptors),
534 Err(e) => {
535 Err(Error::BadComponent(e.into()))
537 }
538 }
539 }
540
541 #[allow(clippy::missing_panics_doc)] async fn get_colour(&mut self, method: &str) -> Result<Colour, Error> {
549 let ret: NullAndStringOr<'_, (u32, bool)> =
550 component_method::<(), _, _>(self.invoker, self.buffer, &self.address, method, None)
551 .await?;
552 let ret = Self::map_no_screen(ret)?;
553 Ok(if ret.1 {
554 Colour::PaletteIndex(PaletteIndex(ret.0))
555 } else {
556 Colour::Rgb(Rgb(ret.0))
557 })
558 }
559
560 #[allow(clippy::missing_panics_doc)] async fn set_colour(
569 &mut self,
570 colour: Colour,
571 method: &str,
572 ) -> Result<(Rgb, Option<PaletteIndex>), Error> {
573 let params: (u32, bool) = match colour {
574 Colour::Rgb(rgb) => (rgb.0, false),
575 Colour::PaletteIndex(pi) => (pi.0, true),
576 };
577 let ret: Result<NullAndStringOr<'_, (u32, Option<u32>)>, _> = component_method(
578 self.invoker,
579 self.buffer,
580 &self.address,
581 method,
582 Some(¶ms),
583 )
584 .await;
585 let ret = Self::map_bad_parameters(ret, Error::BadPaletteIndex)?;
586 let ret = Self::map_no_screen(ret)?;
587 Ok((Rgb(ret.0), ret.1.map(PaletteIndex)))
588 }
589
590 async fn get_dimension(&mut self, method: &str) -> Result<Dimension, Error> {
597 let ret: NullAndStringOr<'_, Dimension> =
598 component_method::<(), _, _>(self.invoker, self.buffer, &self.address, method, None)
599 .await?;
600 let ret = Self::map_no_screen(ret)?;
601 Ok(ret)
602 }
603
604 async fn set_dimension(&mut self, method: &str, parameter: Dimension) -> Result<bool, Error> {
612 let ret: Result<NullAndStringOr<'_, (bool,)>, _> = component_method(
613 self.invoker,
614 self.buffer,
615 &self.address,
616 method,
617 Some(¶meter),
618 )
619 .await;
620 let ret = Self::map_bad_parameters(ret, Error::BadCoordinate)?;
621 let ret = Self::map_no_screen(ret)?;
622 Ok(ret.0)
623 }
624
625 fn map_bad_parameters<T>(
630 x: Result<T, MethodCallError<'_>>,
631 bad_parameters: Error,
632 ) -> Result<T, Error> {
633 x.map_err(|e| match e {
634 MethodCallError::BadParameters(_) => bad_parameters,
635 MethodCallError::TooManyDescriptors => Error::TooManyDescriptors,
636 e => Error::BadComponent(e.into()),
637 })
638 }
639
640 fn map_no_screen<T>(x: NullAndStringOr<'_, T>) -> Result<T, Error> {
644 match x {
645 NullAndStringOr::Ok(x) => Ok(x),
646 NullAndStringOr::Err("no screen") => Err(Error::BadScreen),
647 NullAndStringOr::Err(_) => {
648 Err(Error::BadComponent(oc_wasm_safe::error::Error::Unknown))
649 }
650 }
651 }
652}
653
654impl<B: Buffer> Debug for Locked<'_, B> {
655 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), core::fmt::Error> {
656 Gpu::new(self.address).fmt(f)
657 }
658}
659
660#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
662pub struct PaletteIndex(pub u32);
663
664#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
666pub enum Colour {
667 Rgb(Rgb),
669
670 PaletteIndex(PaletteIndex),
672}
673
674#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
676pub struct CharacterCellContents {
677 pub character: char,
679 pub foreground: (Rgb, Option<PaletteIndex>),
681 pub background: (Rgb, Option<PaletteIndex>),
683}
684
685#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
687pub enum TextDirection {
688 Horizontal,
690 Vertical,
692}
693
694impl<Context> Encode<Context> for TextDirection {
695 fn encode<W: minicbor::encode::Write>(
696 &self,
697 e: &mut minicbor::Encoder<W>,
698 _: &mut Context,
699 ) -> Result<(), minicbor::encode::Error<W::Error>> {
700 e.bool(*self == Self::Vertical)?;
701 Ok(())
702 }
703}