workflow_nw/
application.rs1use crate::media::MediaStreamTrackKind;
8use crate::result::Result;
9use nw_sys::{prelude::*, utils};
10use std::rc::Rc;
11use wasm_bindgen::JsCast;
12use wasm_bindgen::prelude::*;
13use web_sys::{MediaStream, MediaStreamTrack, MouseEvent};
14use workflow_core::channel::*;
15use workflow_wasm::prelude::*;
16
17static mut APP: Option<Arc<Application>> = None;
18
19pub fn app() -> Option<Arc<Application>> {
21 let app_ptr = &raw const APP;
22 unsafe { (*app_ptr).clone() }
23}
24
25#[derive(Clone)]
31pub struct Application {
32 pub media_stream: Rc<Mutex<Option<MediaStream>>>,
34
35 pub callbacks: CallbackMap,
37}
38
39unsafe impl Send for Application {}
40unsafe impl Sync for Application {}
41
42impl Application {
43 pub fn new() -> Result<Arc<Self>> {
46 if let Some(app) = app() {
47 return Ok(app);
48 }
49 let app = Arc::new(Self {
50 callbacks: CallbackMap::new(),
51 media_stream: Rc::new(Mutex::new(None)),
52 });
53
54 unsafe {
55 APP = Some(app.clone());
56 };
57
58 Ok(app)
59 }
60
61 pub fn set_media_stream(&self, media_stream: Option<MediaStream>) -> Result<()> {
63 *self.media_stream.lock()? = media_stream;
64 Ok(())
65 }
66
67 pub fn get_media_stream(&self) -> Result<Option<MediaStream>> {
69 let media_stream = self.media_stream.lock()?.clone();
70 Ok(media_stream)
71 }
72
73 pub fn stop_media_stream(
77 &self,
78 track_kind: Option<MediaStreamTrackKind>,
79 mut stream: Option<MediaStream>,
80 ) -> Result<()> {
81 if stream.is_none() {
82 stream = self.get_media_stream()?;
83 }
84 if let Some(media_stream) = stream {
85 let tracks = media_stream.get_tracks();
86 let kind = track_kind.unwrap_or(MediaStreamTrackKind::All);
87 let mut all = false;
88 let mut video = false;
89 let mut audio = false;
90 match kind {
91 MediaStreamTrackKind::All => {
92 all = true;
93 }
94 MediaStreamTrackKind::Video => {
95 video = true;
96 }
97 MediaStreamTrackKind::Audio => {
98 audio = true;
99 }
100 }
101
102 for index in 0..tracks.length() {
103 if let Ok(track) = tracks.get(index).dyn_into::<MediaStreamTrack>() {
104 let k = track.kind();
105 if all || (k.eq("video") && video) || (k.eq("audio") && audio) {
106 track.stop();
107 }
108 }
109 }
110 }
111 Ok(())
112 }
113
114 pub fn create_window_with_callback<F>(
117 &self,
118 url: &str,
119 option: &nw_sys::window::Options,
120 callback: F,
121 ) -> Result<()>
122 where
123 F: FnMut(nw_sys::Window) -> std::result::Result<(), JsValue> + 'static,
124 {
125 let callback = Callback::new(callback);
126
127 nw_sys::window::open_with_options_and_callback(url, option, callback.as_ref());
128
129 self.callbacks.retain(callback)?;
130 Ok(())
131 }
132
133 pub async fn create_window_async(
152 url: &str,
153 option: &nw_sys::window::Options,
154 ) -> Result<nw_sys::window::Window> {
155 let (sender, receiver) = oneshot();
156
157 let callback = Callback::new(move |window: nw_sys::Window| {
158 sender.try_send(window).unwrap();
159 });
160
161 nw_sys::window::open_with_options_and_callback(url, option, callback.as_ref());
162 Ok(receiver.recv().await?)
163 }
164
165 pub fn create_window(url: &str, option: &nw_sys::window::Options) -> Result<()> {
169 nw_sys::window::open_with_options(url, option);
170
171 Ok(())
172 }
173
174 pub fn create_context_menu(&self, menus: Vec<nw_sys::MenuItem>) -> Result<()> {
176 let popup_menu = nw_sys::Menu::new();
177 for menu_item in menus {
178 popup_menu.append(&menu_item);
179 }
180
181 self.on_context_menu(move |ev: MouseEvent| -> std::result::Result<(), JsValue> {
182 ev.prevent_default();
183 popup_menu.popup(ev.x(), ev.y());
184 Ok(())
185 })?;
186
187 Ok(())
188 }
189
190 pub fn on_context_menu<F>(&self, callback: F) -> Result<()>
192 where
193 F: Sized + FnMut(MouseEvent) -> std::result::Result<(), JsValue> + 'static,
194 {
195 let win = nw_sys::window::get();
196 let dom_win = win.window();
197 let body = utils::body(Some(dom_win));
198
199 let callback = callback!(callback);
200 body.add_event_listener_with_callback("contextmenu", callback.as_ref())?;
201 self.callbacks.retain(callback)?;
202
203 Ok(())
204 }
205
206 pub fn choose_desktop_media<F>(
214 &self,
215 sources: nw_sys::screen::MediaSources,
216 mut callback: F,
217 ) -> Result<()>
218 where
219 F: 'static + FnMut(Option<String>) -> Result<()>,
220 {
221 let callback_ = Callback::new(move |value: JsValue| -> std::result::Result<(), JsValue> {
222 let mut stream_id = None;
223 if value.is_string()
224 && let Some(id) = value.as_string()
225 && !id.is_empty()
226 {
227 stream_id = Some(id);
228 }
229
230 callback(stream_id)?;
231
232 Ok(())
233 });
234
235 nw_sys::screen::choose_desktop_media(sources, callback_.as_ref())?;
236
237 self.callbacks.retain(callback_)?;
238
239 Ok(())
240 }
241}