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