platform.rs

  1// todo!("windows"): remove
  2#![allow(unused_variables)]
  3
  4use std::{
  5    cell::RefCell,
  6    collections::HashSet,
  7    path::{Path, PathBuf},
  8    rc::Rc,
  9    sync::Arc,
 10    time::Duration,
 11};
 12
 13use anyhow::{anyhow, Result};
 14use async_task::Runnable;
 15use futures::channel::oneshot::Receiver;
 16use parking_lot::Mutex;
 17use time::UtcOffset;
 18use util::SemanticVersion;
 19use windows::Win32::{
 20    Foundation::{CloseHandle, HANDLE, HWND},
 21    System::Threading::{CreateEventW, INFINITE},
 22    UI::WindowsAndMessaging::{
 23        DispatchMessageW, MsgWaitForMultipleObjects, PeekMessageW, PostQuitMessage,
 24        TranslateMessage, MSG, PM_REMOVE, QS_ALLINPUT, WM_QUIT,
 25    },
 26};
 27
 28use crate::{
 29    Action, AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, ForegroundExecutor,
 30    Keymap, Menu, PathPromptOptions, Platform, PlatformDisplay, PlatformInput, PlatformTextSystem,
 31    PlatformWindow, Task, WindowAppearance, WindowOptions, WindowsDispatcher, WindowsDisplay,
 32    WindowsTextSystem, WindowsWindow,
 33};
 34
 35pub(crate) struct WindowsPlatform {
 36    inner: Rc<WindowsPlatformInner>,
 37}
 38
 39pub(crate) struct WindowsPlatformInner {
 40    background_executor: BackgroundExecutor,
 41    pub(crate) foreground_executor: ForegroundExecutor,
 42    main_receiver: flume::Receiver<Runnable>,
 43    text_system: Arc<WindowsTextSystem>,
 44    callbacks: Mutex<Callbacks>,
 45    pub(crate) window_handles: RefCell<HashSet<AnyWindowHandle>>,
 46    pub(crate) event: HANDLE,
 47}
 48
 49impl Drop for WindowsPlatformInner {
 50    fn drop(&mut self) {
 51        unsafe { CloseHandle(self.event) }.ok();
 52    }
 53}
 54
 55#[derive(Default)]
 56struct Callbacks {
 57    open_urls: Option<Box<dyn FnMut(Vec<String>)>>,
 58    become_active: Option<Box<dyn FnMut()>>,
 59    resign_active: Option<Box<dyn FnMut()>>,
 60    quit: Option<Box<dyn FnMut()>>,
 61    reopen: Option<Box<dyn FnMut()>>,
 62    event: Option<Box<dyn FnMut(PlatformInput) -> bool>>,
 63    app_menu_action: Option<Box<dyn FnMut(&dyn Action)>>,
 64    will_open_app_menu: Option<Box<dyn FnMut()>>,
 65    validate_app_menu_command: Option<Box<dyn FnMut(&dyn Action) -> bool>>,
 66}
 67
 68impl WindowsPlatform {
 69    pub(crate) fn new() -> Self {
 70        let (main_sender, main_receiver) = flume::unbounded::<Runnable>();
 71        let event = unsafe { CreateEventW(None, false, false, None) }.unwrap();
 72        let dispatcher = Arc::new(WindowsDispatcher::new(main_sender, event));
 73        let background_executor = BackgroundExecutor::new(dispatcher.clone());
 74        let foreground_executor = ForegroundExecutor::new(dispatcher);
 75        let text_system = Arc::new(WindowsTextSystem::new());
 76        let callbacks = Mutex::new(Callbacks::default());
 77        let window_handles = RefCell::new(HashSet::new());
 78        let inner = Rc::new(WindowsPlatformInner {
 79            background_executor,
 80            foreground_executor,
 81            main_receiver,
 82            text_system,
 83            callbacks,
 84            window_handles,
 85            event,
 86        });
 87        Self { inner }
 88    }
 89}
 90
 91impl Platform for WindowsPlatform {
 92    fn background_executor(&self) -> BackgroundExecutor {
 93        self.inner.background_executor.clone()
 94    }
 95
 96    fn foreground_executor(&self) -> ForegroundExecutor {
 97        self.inner.foreground_executor.clone()
 98    }
 99
100    fn text_system(&self) -> Arc<dyn PlatformTextSystem> {
101        self.inner.text_system.clone()
102    }
103
104    fn run(&self, on_finish_launching: Box<dyn 'static + FnOnce()>) {
105        on_finish_launching();
106        'a: loop {
107            unsafe {
108                MsgWaitForMultipleObjects(Some(&[self.inner.event]), false, INFINITE, QS_ALLINPUT)
109            };
110            let mut msg = MSG::default();
111            while unsafe { PeekMessageW(&mut msg, HWND::default(), 0, 0, PM_REMOVE) }.as_bool() {
112                if msg.message == WM_QUIT {
113                    break 'a;
114                }
115                unsafe { TranslateMessage(&msg) };
116                unsafe { DispatchMessageW(&msg) };
117            }
118            while let Ok(runnable) = self.inner.main_receiver.try_recv() {
119                runnable.run();
120            }
121        }
122        let mut callbacks = self.inner.callbacks.lock();
123        if let Some(callback) = callbacks.quit.as_mut() {
124            callback()
125        }
126    }
127
128    fn quit(&self) {
129        self.foreground_executor()
130            .spawn(async { unsafe { PostQuitMessage(0) } })
131            .detach();
132    }
133
134    // todo!("windows")
135    fn restart(&self) {
136        unimplemented!()
137    }
138
139    // todo!("windows")
140    fn activate(&self, ignoring_other_apps: bool) {}
141
142    // todo!("windows")
143    fn hide(&self) {
144        unimplemented!()
145    }
146
147    // todo!("windows")
148    fn hide_other_apps(&self) {
149        unimplemented!()
150    }
151
152    // todo!("windows")
153    fn unhide_other_apps(&self) {
154        unimplemented!()
155    }
156
157    // todo!("windows")
158    fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>> {
159        vec![Rc::new(WindowsDisplay::new())]
160    }
161
162    // todo!("windows")
163    fn display(&self, id: crate::DisplayId) -> Option<Rc<dyn PlatformDisplay>> {
164        Some(Rc::new(WindowsDisplay::new()))
165    }
166
167    // todo!("windows")
168    fn active_window(&self) -> Option<AnyWindowHandle> {
169        unimplemented!()
170    }
171
172    fn open_window(
173        &self,
174        handle: AnyWindowHandle,
175        options: WindowOptions,
176    ) -> Box<dyn PlatformWindow> {
177        Box::new(WindowsWindow::new(self.inner.clone(), handle, options))
178    }
179
180    // todo!("windows")
181    fn window_appearance(&self) -> WindowAppearance {
182        WindowAppearance::Dark
183    }
184
185    // todo!("windows")
186    fn open_url(&self, url: &str) {
187        // todo!("windows")
188    }
189
190    // todo!("windows")
191    fn on_open_urls(&self, callback: Box<dyn FnMut(Vec<String>)>) {
192        self.inner.callbacks.lock().open_urls = Some(callback);
193    }
194
195    // todo!("windows")
196    fn prompt_for_paths(&self, options: PathPromptOptions) -> Receiver<Option<Vec<PathBuf>>> {
197        unimplemented!()
198    }
199
200    // todo!("windows")
201    fn prompt_for_new_path(&self, directory: &Path) -> Receiver<Option<PathBuf>> {
202        unimplemented!()
203    }
204
205    // todo!("windows")
206    fn reveal_path(&self, path: &Path) {
207        unimplemented!()
208    }
209
210    fn on_become_active(&self, callback: Box<dyn FnMut()>) {
211        self.inner.callbacks.lock().become_active = Some(callback);
212    }
213
214    fn on_resign_active(&self, callback: Box<dyn FnMut()>) {
215        self.inner.callbacks.lock().resign_active = Some(callback);
216    }
217
218    fn on_quit(&self, callback: Box<dyn FnMut()>) {
219        self.inner.callbacks.lock().quit = Some(callback);
220    }
221
222    fn on_reopen(&self, callback: Box<dyn FnMut()>) {
223        self.inner.callbacks.lock().reopen = Some(callback);
224    }
225
226    fn on_event(&self, callback: Box<dyn FnMut(PlatformInput) -> bool>) {
227        self.inner.callbacks.lock().event = Some(callback);
228    }
229
230    // todo!("windows")
231    fn set_menus(&self, menus: Vec<Menu>, keymap: &Keymap) {}
232
233    fn on_app_menu_action(&self, callback: Box<dyn FnMut(&dyn Action)>) {
234        self.inner.callbacks.lock().app_menu_action = Some(callback);
235    }
236
237    fn on_will_open_app_menu(&self, callback: Box<dyn FnMut()>) {
238        self.inner.callbacks.lock().will_open_app_menu = Some(callback);
239    }
240
241    fn on_validate_app_menu_command(&self, callback: Box<dyn FnMut(&dyn Action) -> bool>) {
242        self.inner.callbacks.lock().validate_app_menu_command = Some(callback);
243    }
244
245    fn os_name(&self) -> &'static str {
246        "Windows"
247    }
248
249    fn os_version(&self) -> Result<SemanticVersion> {
250        Ok(SemanticVersion {
251            major: 1,
252            minor: 0,
253            patch: 0,
254        })
255    }
256
257    fn app_version(&self) -> Result<SemanticVersion> {
258        Ok(SemanticVersion {
259            major: 1,
260            minor: 0,
261            patch: 0,
262        })
263    }
264
265    // todo!("windows")
266    fn app_path(&self) -> Result<PathBuf> {
267        Err(anyhow!("not yet implemented"))
268    }
269
270    // todo!("windows")
271    fn local_timezone(&self) -> UtcOffset {
272        UtcOffset::from_hms(9, 0, 0).unwrap()
273    }
274
275    // todo!("windows")
276    fn double_click_interval(&self) -> Duration {
277        Duration::from_millis(100)
278    }
279
280    // todo!("windows")
281    fn path_for_auxiliary_executable(&self, name: &str) -> Result<PathBuf> {
282        Err(anyhow!("not yet implemented"))
283    }
284
285    // todo!("windows")
286    fn set_cursor_style(&self, style: CursorStyle) {}
287
288    // todo!("windows")
289    fn should_auto_hide_scrollbars(&self) -> bool {
290        false
291    }
292
293    // todo!("windows")
294    fn write_to_clipboard(&self, item: ClipboardItem) {
295        unimplemented!()
296    }
297
298    // todo!("windows")
299    fn read_from_clipboard(&self) -> Option<ClipboardItem> {
300        unimplemented!()
301    }
302
303    // todo!("windows")
304    fn write_credentials(&self, url: &str, username: &str, password: &[u8]) -> Task<Result<()>> {
305        Task::Ready(Some(Err(anyhow!("not implemented yet."))))
306    }
307
308    // todo!("windows")
309    fn read_credentials(&self, url: &str) -> Task<Result<Option<(String, Vec<u8>)>>> {
310        Task::Ready(Some(Err(anyhow!("not implemented yet."))))
311    }
312
313    // todo!("windows")
314    fn delete_credentials(&self, url: &str) -> Task<Result<()>> {
315        Task::Ready(Some(Err(anyhow!("not implemented yet."))))
316    }
317
318    fn register_url_scheme(&self, _: &str) -> Task<anyhow::Result<()>> {
319        Task::ready(Err(anyhow!("register_url_scheme unimplemented")))
320    }
321}