nw_sys/screen.rs
1//!
2//! Access to system display information, including system resolution, display layout and
3//! display layout change notification events.
4//!
5//! # Synopsis
6//! ```
7//! // init must be called once during startup, before any function to nw.Screen can be called
8//! nw_sys::screen::init_once();
9//!
10//! let display_bounds_changed_callback = Callback::new(move |screen:JsValue|{
11//! let screen: nw_sys::screen::ScreenInfo = screen.try_into()?;
12//! log_info!("displayBoundsChanged: {:#?}", screen);
13//! Ok(())
14//! });
15//!
16//! let display_added_callback = Callback::new(move |screen:JsValue|{
17//! let screen: nw_sys::screen::ScreenInfo = screen.try_into()?;
18//! log_info!("displayAdded: {:#?}", screen);
19//! Ok(())
20//! });
21//!
22//! let display_removed_callback = Callback::new(move |screen:JsValue|{
23//! let screen: nw_sys::screen::ScreenInfo = screen.try_into()?;
24//! log_info!("displayRemoved: {:#?}", screen);
25//! Ok(())
26//! });
27//!
28//! // listen to screen events
29//! nw_sys::screen::on("displayBoundsChanged", display_bounds_changed_callback.as_ref());
30//! nw_sys::screen::on("displayAdded", display_added_callback.as_ref());
31//! nw_sys::screen::on("displayRemoved", display_removed_callback.as_ref());
32//!
33//! // save callbacks somewhere
34//! app.push_callback(display_bounds_changed_callback)?;
35//! app.push_callback(display_added_callback)?;
36//! app.push_callback(display_removed_callback)?;
37//!
38//! ```
39//!
40//! Screen
41//! # Synopsis
42//! ```
43//! use workflow_wasm::prelude::*;
44//!
45//! // init must be called once during startup, before any function to nw.Screen can be called
46//! nw_sys::screen::init_once();
47//!
48//! let display_bounds_changed_callback = callback!(move |screen:JsValue|{
49//! let screen: nw_sys::screen::ScreenInfo = screen.try_into()?;
50//! log_info!("displayBoundsChanged: {:#?}", screen);
51//! Ok(())
52//! });
53//!
54//! let display_added_callback = callback!(move |screen:JsValue|{
55//! let screen: nw_sys::screen::ScreenInfo = screen.try_into()?;
56//! log_info!("displayAdded: {:#?}", screen);
57//! Ok(())
58//! });
59//!
60//! let display_removed_callback = callback!(move |screen:JsValue|{
61//! let screen: nw_sys::screen::ScreenInfo = screen.try_into()?;
62//! log_info!("displayRemoved: {:#?}", screen);
63//! Ok(())
64//! });
65//!
66//! // listen to screen events
67//! nw_sys::screen::on("displayBoundsChanged", display_bounds_changed_callback.into_js());
68//! nw_sys::screen::on("displayAdded", display_added_callback.into_js());
69//! nw_sys::screen::on("displayRemoved", display_removed_callback.into_js());
70//!
71//! // save callbacks somewhere
72//! app.push_callback(display_bounds_changed_callback)?;
73//! app.push_callback(display_added_callback)?;
74//! app.push_callback(display_removed_callback)?;
75//!
76//! ```
77
78use js_sys::{Array, Function};
79use wasm_bindgen::prelude::*;
80//use workflow_log::log_info;
81//use crate::options::OptionsExt;
82use crate::result::Result;
83use crate::utils;
84
85#[wasm_bindgen]
86extern "C" {
87
88 #[wasm_bindgen(js_namespace=nw, js_name = Screen)]
89 #[derive(Debug, Clone)]
90 type ScreenLocal;
91
92 #[wasm_bindgen(js_namespace=["nw", "Screen"], js_name = Init)]
93 /// Init the Screen singleton object, you only need to call this once
94 ///
95 /// ⧉ [NWJS Documentation](https://docs.nwjs.io/en/latest/References/Screen/#screeninit)
96 ///
97 pub fn init();
98
99 #[wasm_bindgen(getter, static_method_of=ScreenLocal, js_namespace=["nw"], js_class=Screen, js_name = screens)]
100 fn screens_impl() -> Array;
101
102 #[wasm_bindgen(js_namespace=["nw", "Screen"], js_name = chooseDesktopMedia)]
103 fn choose_desktop_media_impl(sources: Array, callback: &Function);
104
105 #[wasm_bindgen(js_namespace=["nw", "Screen"], js_name = on)]
106 ///
107 ///
108 /// Interface for accessing display & monitor layout information. For usage example please refer to [nw_sys::screen](self)
109 ///
110 /// ### Events:
111 /// - displayBoundsChanged (screen)
112 /// - displayAdded (screen)
113 /// - displayRemoved (screen)
114 ///
115 ///
116 /// ⧉ [NWJS Documentation](https://docs.nwjs.io/en/latest/References/Screen/#event-displayboundschangedscreen)
117 ///
118 pub fn on(event_name: &str, callback: &Function);
119}
120
121pub mod desktop_capture_monitor {
122 use js_sys::Function;
123 use wasm_bindgen::prelude::*;
124
125 #[wasm_bindgen]
126 extern "C" {
127
128 #[wasm_bindgen(js_namespace=["nw", "Screen"], js_name = DesktopCaptureMonitor)]
129 #[derive(Debug, Clone)]
130 type DCM;
131
132 #[wasm_bindgen(getter, static_method_of=DCM, js_namespace=["nw", "Screen"], js_class=DesktopCaptureMonitor, js_name = started)]
133 /// Return Boolean of whether the DesktopCaptureMonitor is started.
134 ///
135 ///
136 /// ⧉ [NWJS Documentation](https://docs.nwjs.io/en/latest/References/Screen/#screendesktopcapturemonitorstarted)
137 ///
138 pub fn started() -> bool;
139
140 #[wasm_bindgen(js_namespace=["nw", "Screen", "DesktopCaptureMonitor"], js_name = start)]
141 /// The DesktopCaptureMonitor will start monitoring the system
142 /// and trigger the the events. The screen may flicker
143 /// if while DesktopCaptureMonitor is running.
144 ///
145 /// Example:
146 /// ```rust
147 /// nw::screen::desktop_capture_monitor::start(true, true);
148 /// ```
149 ///
150 /// ⧉ [NWJS Documentation](https://docs.nwjs.io/en/latest/References/Screen/#screendesktopcapturemonitorstartshould_include_screens-should_include_windows)
151 ///
152 pub fn start(should_include_screens: bool, should_include_windows: bool);
153
154 #[wasm_bindgen(js_namespace=["nw", "Screen", "DesktopCaptureMonitor"], js_name = stop)]
155 /// The `DesktopCaptureMonitor` will stop monitoring the system.
156 /// `DesktopCaptureMonitor` should be stopped after a stream is selected.
157 ///
158 ///
159 /// ⧉ [NWJS Documentation](https://docs.nwjs.io/en/latest/References/Screen/#screendesktopcapturemonitorstop)
160 ///
161 pub fn stop();
162
163 #[wasm_bindgen(js_namespace=["nw", "Screen", "DesktopCaptureMonitor"], js_name = registerStream)]
164 /// Register and return a valid stream id which will be used into
165 /// chromeMediaSourceId in get_user_media constraints.
166 ///
167 /// See Synopsis for the usage.
168 ///
169 ///
170 /// ⧉ [NWJS Documentation](https://docs.nwjs.io/en/latest/References/Screen/#screendesktopcapturemonitorregisterstreamid)
171 ///
172 pub fn register_stream(id: &str) -> String;
173
174 #[wasm_bindgen(static_method_of=DCM, js_namespace=["nw", "Screen"], js_class=DesktopCaptureMonitor, js_name = on)]
175 /// Add event listener
176 ///
177 /// ### Events:
178 /// - added (id, name, order, type, primary)
179 /// - removed (order)
180 /// - orderchanged (id, new_order, old_order)
181 /// - namechanged (id, name)
182 /// - thumbnailchanged (id, thumbnail)
183 ///
184 ///
185 /// ⧉ [NWJS Documentation](https://docs.nwjs.io/en/latest/References/Screen/#event-added-id-name-order-type-primary)
186 ///
187 pub fn on(event_name: &str, callback: &Function);
188 }
189
190 /// Return Boolean of whether the DesktopCaptureMonitor is started.
191 ///
192 ///
193 /// ⧉ [NWJS Documentation](https://docs.nwjs.io/en/latest/References/Screen/#screendesktopcapturemonitorstarted)
194 ///
195 pub fn started() -> bool {
196 DCM::started()
197 }
198
199 /// Add event listener
200 ///
201 /// ### Events:
202 /// - added (id, name, order, type, primary)
203 /// - removed (order)
204 /// - orderchanged (id, new_order, old_order)
205 /// - namechanged (id, name)
206 /// - thumbnailchanged (id, thumbnail)
207 ///
208 ///
209 /// ⧉ [NWJS Documentation](https://docs.nwjs.io/en/latest/References/Screen/#event-added-id-name-order-type-primary)
210 ///
211 pub fn on(event_name: &str, callback: &Function) {
212 DCM::on(event_name, callback)
213 }
214}
215
216static mut INIT: bool = false;
217
218/// Return true is screen is initialized
219pub fn is_initialized() -> bool {
220 unsafe { INIT }
221}
222
223/// Call the screen::init() if screen is not initialized yet
224pub fn init_once() {
225 if !is_initialized() {
226 unsafe { INIT = true };
227 init();
228 }
229}
230
231/// Media source type
232pub enum MediaSources {
233 Screen,
234 Window,
235 ScreenAndWindow,
236}
237
238/// Choose desktop media
239///
240/// Screen sharing by selection; Currently only working in Windows and OSX
241/// and some linux distribution.
242///
243/// ⧉ [NWJS Documentation](https://docs.nwjs.io/en/latest/References/Screen/#screenchoosedesktopmedia-sources-callback)
244///
245pub fn choose_desktop_media(sources: MediaSources, callback: &Function) -> Result<()> {
246 let array = Array::new();
247 match sources {
248 MediaSources::Screen => {
249 array.push(&JsValue::from("screen"));
250 }
251 MediaSources::Window => {
252 array.push(&JsValue::from("window"));
253 }
254 MediaSources::ScreenAndWindow => {
255 array.push(&JsValue::from("screen"));
256 array.push(&JsValue::from("window"));
257 }
258 };
259
260 choose_desktop_media_impl(array, callback);
261 Ok(())
262}
263
264/// Get the array of screen (number of screen connected to the computer)
265///
266/// ⧉ [NWJS Documentation](https://docs.nwjs.io/en/latest/References/Screen/#screenscreens)
267///
268pub fn screens() -> Result<Vec<ScreenInfo>> {
269 let mut result: Vec<ScreenInfo> = Vec::new();
270 let array = ScreenLocal::screens_impl();
271 for index in 0..array.length() {
272 let screen = array.get(index);
273 //log_info!("screen: {:#?}", screen);
274 result.push(screen.try_into()?);
275 }
276 Ok(result)
277}
278
279/// physical screen resolution, can be negative,
280/// not necessarily start from 0,
281/// depending on screen arrangement
282#[derive(Debug)]
283pub struct Bounds {
284 pub x: f64,
285 pub y: f64,
286 pub width: f64,
287 pub height: f64,
288}
289
290/// useable area within the screen bound
291#[derive(Debug)]
292pub struct WorkArea {
293 pub x: f64,
294 pub y: f64,
295 pub width: f64,
296 pub height: f64,
297}
298
299/// Screen Info
300#[derive(Debug)]
301pub struct ScreenInfo {
302 pub id: u64,
303 pub scale_factor: f64,
304 pub is_built_in: bool,
305 pub rotation: u64,
306 pub touch_support: u64,
307 pub bounds: Bounds,
308 pub work_area: WorkArea,
309}
310
311fn read_box(jsv: &JsValue, prop: &str) -> Result<(f64, f64, f64, f64)> {
312 let jsv = utils::try_get_js_value(jsv, prop)?;
313 let x = utils::try_get_f64_from_prop(&jsv, "x")?;
314 let y = utils::try_get_f64_from_prop(&jsv, "y")?;
315 let width = utils::try_get_f64_from_prop(&jsv, "width")?;
316 let height = utils::try_get_f64_from_prop(&jsv, "height")?;
317
318 Ok((x, y, width, height))
319}
320
321impl TryFrom<JsValue> for ScreenInfo {
322 type Error = crate::error::Error;
323 fn try_from(jsv: JsValue) -> std::result::Result<Self, Self::Error> {
324 let id = utils::try_get_u64_from_prop(&jsv, "id")?;
325 let scale_factor = utils::try_get_f64_from_prop(&jsv, "scaleFactor")?;
326 let is_built_in = utils::try_get_bool_from_prop(&jsv, "isBuiltIn")?;
327 let rotation = utils::try_get_u64_from_prop(&jsv, "rotation")?;
328 let touch_support = utils::try_get_u64_from_prop(&jsv, "touchSupport")?;
329
330 let (x, y, width, height) = read_box(&jsv, "bounds")?;
331 let bounds = Bounds {
332 x,
333 y,
334 width,
335 height,
336 };
337
338 let (x, y, width, height) = read_box(&jsv, "work_area")?;
339 let work_area = WorkArea {
340 x,
341 y,
342 width,
343 height,
344 };
345
346 let info = Self {
347 id,
348 scale_factor,
349 is_built_in,
350 rotation,
351 touch_support,
352 bounds,
353 work_area,
354 };
355 Ok(info)
356 }
357}