oc_wasm_opencomputers/
screen.rs

1//! Provides high-level access to the screen APIs.
2
3use crate::common::Dimension;
4use crate::error::Error;
5use alloc::vec::Vec;
6use oc_wasm_futures::invoke::{component_method, Buffer};
7use oc_wasm_helpers::{error::NullAndStringOr, Lockable};
8use oc_wasm_safe::{component::Invoker, Address};
9
10/// The type name for screen components.
11pub const TYPE: &str = "screen";
12
13/// A screen component.
14#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
15pub struct Screen(Address);
16
17impl Screen {
18	/// Creates a wrapper around a screen.
19	///
20	/// The `address` parameter is the address of the screen. It is not checked for correctness at
21	/// this time because network topology could change after this function returns; as such, each
22	/// usage of the value may fail instead.
23	#[must_use = "This function is only useful for its return value"]
24	pub fn new(address: Address) -> Self {
25		Self(address)
26	}
27
28	/// Returns the address of the screen.
29	#[must_use = "This function is only useful for its return value"]
30	pub fn address(&self) -> &Address {
31		&self.0
32	}
33}
34
35impl<'a, B: 'a + Buffer> Lockable<'a, 'a, B> for Screen {
36	type Locked = Locked<'a, B>;
37
38	fn lock(&self, invoker: &'a mut Invoker, buffer: &'a mut B) -> Self::Locked {
39		Locked {
40			address: self.0,
41			invoker,
42			buffer,
43		}
44	}
45}
46
47/// A screen component on which methods can be invoked.
48///
49/// This type combines a screen address, an [`Invoker`] that can be used to make method calls, and
50/// a scratch buffer used to perform CBOR encoding and decoding. A value of this type can be
51/// created by calling [`Screen::lock`], and it can be dropped to return the borrow of the invoker
52/// and buffer to the caller so they can be reused for other purposes.
53///
54/// The `'a` lifetime is the lifetime of the invoker and the buffer. The `B` type is the type of
55/// scratch buffer to use.
56pub struct Locked<'a, B: Buffer> {
57	/// The component address.
58	address: Address,
59
60	/// The invoker.
61	invoker: &'a mut Invoker,
62
63	/// The buffer.
64	buffer: &'a mut B,
65}
66
67impl<'a, B: Buffer> Locked<'a, B> {
68	/// Checks whether the screen is powered on or off.
69	///
70	/// # Errors
71	/// * [`BadComponent`](Error::BadComponent)
72	/// * [`TooManyDescriptors`](Error::TooManyDescriptors)
73	pub async fn is_on(&mut self) -> Result<bool, Error> {
74		let ret: (bool,) =
75			component_method::<(), _, _>(self.invoker, self.buffer, &self.address, "isOn", None)
76				.await?;
77		Ok(ret.0)
78	}
79
80	/// Powers on the screen, returning whether the power was previously off.
81	///
82	/// # Errors
83	/// * [`BadComponent`](Error::BadComponent)
84	/// * [`TooManyDescriptors`](Error::TooManyDescriptors)
85	pub async fn turn_on(&mut self) -> Result<bool, Error> {
86		let ret: (bool, bool) =
87			component_method::<(), _, _>(self.invoker, self.buffer, &self.address, "turnOn", None)
88				.await?;
89		Ok(ret.0)
90	}
91
92	/// Powers off the screen, returning whether the power was previously on.
93	///
94	/// # Errors
95	/// * [`BadComponent`](Error::BadComponent)
96	/// * [`TooManyDescriptors`](Error::TooManyDescriptors)
97	pub async fn turn_off(&mut self) -> Result<bool, Error> {
98		let ret: (bool, bool) =
99			component_method::<(), _, _>(self.invoker, self.buffer, &self.address, "turnOff", None)
100				.await?;
101		Ok(ret.0)
102	}
103
104	/// Returns the screen’s aspect ratio. The aspect ratio is measured in metres.
105	///
106	/// # Errors
107	/// * [`BadComponent`](Error::BadComponent)
108	/// * [`TooManyDescriptors`](Error::TooManyDescriptors)
109	pub async fn get_aspect_ratio(&mut self) -> Result<Dimension, Error> {
110		let ret: (f64, f64) = component_method::<(), _, _>(
111			self.invoker,
112			self.buffer,
113			&self.address,
114			"getAspectRatio",
115			None,
116		)
117		.await?;
118		// For some reason the method call’s return value is a pair of f64s, but in reality the
119		// numbers are always counts of Minecraft blocks so they are small nonnegative integers.
120		#[allow(clippy::cast_possible_truncation)]
121		#[allow(clippy::cast_sign_loss)]
122		Ok(Dimension {
123			width: ret.0 as u32,
124			height: ret.1 as u32,
125		})
126	}
127
128	/// Returns the addresses of the keyboards connected to the screen.
129	///
130	/// # Errors
131	/// * [`BadComponent`](Error::BadComponent)
132	/// * [`TooManyDescriptors`](Error::TooManyDescriptors)
133	pub async fn get_keyboards(&mut self) -> Result<Vec<Address>, Error> {
134		let ret: (Vec<Address>,) = component_method::<(), _, _>(
135			self.invoker,
136			self.buffer,
137			&self.address,
138			"getKeyboards",
139			None,
140		)
141		.await?;
142		Ok(ret.0)
143	}
144
145	/// Sets whether mouse positions are reported at subpixel granularity and returns the old
146	/// setting.
147	///
148	/// # Errors
149	/// * [`BadComponent`](Error::BadComponent)
150	/// * [`TooManyDescriptors`](Error::TooManyDescriptors)
151	/// * [`Unsupported`](Error::Unsupported) is returned if the screen is not advanced enough to
152	///   return subpixel-granularity touch data.
153	pub async fn set_precise(&mut self, precise: bool) -> Result<bool, Error> {
154		let ret: NullAndStringOr<'_, (bool,)> = component_method(
155			self.invoker,
156			self.buffer,
157			&self.address,
158			"setPrecise",
159			Some(&(precise,)),
160		)
161		.await?;
162		match ret {
163			NullAndStringOr::Ok((f,)) => Ok(f),
164			NullAndStringOr::Err("unsupported operation") => Err(Error::Unsupported),
165			NullAndStringOr::Err(_) => {
166				Err(Error::BadComponent(oc_wasm_safe::error::Error::Unknown))
167			}
168		}
169	}
170
171	/// Returns whether mouse positions are reported at subpixel granularity.
172	///
173	/// # Errors
174	/// * [`BadComponent`](Error::BadComponent)
175	/// * [`TooManyDescriptors`](Error::TooManyDescriptors)
176	pub async fn is_precise(&mut self) -> Result<bool, Error> {
177		let ret: (bool,) = component_method::<(), _, _>(
178			self.invoker,
179			self.buffer,
180			&self.address,
181			"isPrecise",
182			None,
183		)
184		.await?;
185		Ok(ret.0)
186	}
187
188	/// Sets whether the touch-screen and open-GUI gestures are inverted from their normal
189	/// configuration and returns the old setting.
190	///
191	/// # Errors
192	/// * [`BadComponent`](Error::BadComponent)
193	/// * [`TooManyDescriptors`](Error::TooManyDescriptors)
194	pub async fn set_touch_mode_inverted(&mut self, inverted: bool) -> Result<bool, Error> {
195		let ret: (bool,) = component_method(
196			self.invoker,
197			self.buffer,
198			&self.address,
199			"setTouchModeInverted",
200			Some(&(inverted,)),
201		)
202		.await?;
203		Ok(ret.0)
204	}
205
206	/// Returns whether the touch-screen and open-GUI gestures are inverted from their normal
207	/// configuration.
208	///
209	/// # Errors
210	/// * [`BadComponent`](Error::BadComponent)
211	/// * [`TooManyDescriptors`](Error::TooManyDescriptors)
212	pub async fn is_touch_mode_inverted(&mut self) -> Result<bool, Error> {
213		let ret: (bool,) = component_method::<(), _, _>(
214			self.invoker,
215			self.buffer,
216			&self.address,
217			"isTouchModeInverted",
218			None,
219		)
220		.await?;
221		Ok(ret.0)
222	}
223}