platform.rs

  1// todo!("windows"): remove
  2#![allow(unused_variables)]
  3
  4use std::{
  5    cell::RefCell,
  6    collections::HashSet,
  7    ffi::{c_uint, c_void},
  8    path::{Path, PathBuf},
  9    rc::Rc,
 10    sync::Arc,
 11    time::Duration,
 12};
 13
 14use anyhow::{anyhow, Result};
 15use async_task::Runnable;
 16use futures::channel::oneshot::Receiver;
 17use parking_lot::Mutex;
 18use time::UtcOffset;
 19use util::{ResultExt, SemanticVersion};
 20use windows::Win32::{
 21    Foundation::{CloseHandle, GetLastError, HANDLE, HWND, WAIT_EVENT},
 22    System::Threading::{CreateEventW, INFINITE},
 23    UI::WindowsAndMessaging::{
 24        DispatchMessageW, GetMessageW, MsgWaitForMultipleObjects, PostQuitMessage,
 25        SystemParametersInfoW, TranslateMessage, MSG, QS_ALLINPUT, SPI_GETWHEELSCROLLCHARS,
 26        SPI_GETWHEELSCROLLLINES, SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS, WM_QUIT, WM_SETTINGCHANGE,
 27    },
 28};
 29
 30use crate::{
 31    try_get_window_inner, Action, AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle,
 32    ForegroundExecutor, Keymap, Menu, PathPromptOptions, Platform, PlatformDisplay, PlatformInput,
 33    PlatformTextSystem, PlatformWindow, Task, WindowAppearance, WindowOptions, WindowsDispatcher,
 34    WindowsDisplay, WindowsTextSystem, WindowsWindow,
 35};
 36
 37pub(crate) struct WindowsPlatform {
 38    inner: Rc<WindowsPlatformInner>,
 39}
 40
 41/// Windows settings pulled from SystemParametersInfo
 42/// https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-systemparametersinfow
 43#[derive(Default, Debug)]
 44pub(crate) struct WindowsPlatformSystemSettings {
 45    /// SEE: SPI_GETWHEELSCROLLCHARS
 46    pub(crate) wheel_scroll_chars: u32,
 47
 48    /// SEE: SPI_GETWHEELSCROLLLINES
 49    pub(crate) wheel_scroll_lines: u32,
 50}
 51
 52pub(crate) struct WindowsPlatformInner {
 53    background_executor: BackgroundExecutor,
 54    pub(crate) foreground_executor: ForegroundExecutor,
 55    main_receiver: flume::Receiver<Runnable>,
 56    text_system: Arc<WindowsTextSystem>,
 57    callbacks: Mutex<Callbacks>,
 58    pub(crate) window_handles: RefCell<HashSet<AnyWindowHandle>>,
 59    pub(crate) event: HANDLE,
 60    pub(crate) settings: RefCell<WindowsPlatformSystemSettings>,
 61}
 62
 63impl Drop for WindowsPlatformInner {
 64    fn drop(&mut self) {
 65        unsafe { CloseHandle(self.event) }.ok();
 66    }
 67}
 68
 69#[derive(Default)]
 70struct Callbacks {
 71    open_urls: Option<Box<dyn FnMut(Vec<String>)>>,
 72    become_active: Option<Box<dyn FnMut()>>,
 73    resign_active: Option<Box<dyn FnMut()>>,
 74    quit: Option<Box<dyn FnMut()>>,
 75    reopen: Option<Box<dyn FnMut()>>,
 76    event: Option<Box<dyn FnMut(PlatformInput) -> bool>>,
 77    app_menu_action: Option<Box<dyn FnMut(&dyn Action)>>,
 78    will_open_app_menu: Option<Box<dyn FnMut()>>,
 79    validate_app_menu_command: Option<Box<dyn FnMut(&dyn Action) -> bool>>,
 80}
 81
 82enum WindowsMessageWaitResult {
 83    ForegroundExecution,
 84    WindowsMessage(MSG),
 85    Error,
 86}
 87
 88impl WindowsPlatformSystemSettings {
 89    fn new() -> Self {
 90        let mut settings = Self::default();
 91        settings.update_all();
 92        settings
 93    }
 94
 95    pub(crate) fn update_all(&mut self) {
 96        self.update_wheel_scroll_lines();
 97        self.update_wheel_scroll_chars();
 98    }
 99
100    pub(crate) fn update_wheel_scroll_lines(&mut self) {
101        let mut value = c_uint::default();
102        let result = unsafe {
103            SystemParametersInfoW(
104                SPI_GETWHEELSCROLLLINES,
105                0,
106                Some((&mut value) as *mut c_uint as *mut c_void),
107                SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS::default(),
108            )
109        };
110
111        if result.log_err() != None {
112            self.wheel_scroll_lines = value;
113        }
114    }
115
116    pub(crate) fn update_wheel_scroll_chars(&mut self) {
117        let mut value = c_uint::default();
118        let result = unsafe {
119            SystemParametersInfoW(
120                SPI_GETWHEELSCROLLCHARS,
121                0,
122                Some((&mut value) as *mut c_uint as *mut c_void),
123                SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS::default(),
124            )
125        };
126
127        if result.log_err() != None {
128            self.wheel_scroll_chars = value;
129        }
130    }
131}
132
133impl WindowsPlatform {
134    pub(crate) fn new() -> Self {
135        let (main_sender, main_receiver) = flume::unbounded::<Runnable>();
136        let event = unsafe { CreateEventW(None, false, false, None) }.unwrap();
137        let dispatcher = Arc::new(WindowsDispatcher::new(main_sender, event));
138        let background_executor = BackgroundExecutor::new(dispatcher.clone());
139        let foreground_executor = ForegroundExecutor::new(dispatcher);
140        let text_system = Arc::new(WindowsTextSystem::new());
141        let callbacks = Mutex::new(Callbacks::default());
142        let window_handles = RefCell::new(HashSet::new());
143        let settings = RefCell::new(WindowsPlatformSystemSettings::new());
144        let inner = Rc::new(WindowsPlatformInner {
145            background_executor,
146            foreground_executor,
147            main_receiver,
148            text_system,
149            callbacks,
150            window_handles,
151            event,
152            settings,
153        });
154        Self { inner }
155    }
156
157    /// runs message handlers that should be processed before dispatching to prevent translating unnecessary messages
158    /// returns true if message is handled and should not dispatch
159    fn run_immediate_msg_handlers(&self, msg: &MSG) -> bool {
160        if msg.message == WM_SETTINGCHANGE {
161            self.inner.settings.borrow_mut().update_all();
162            return true;
163        }
164
165        if let Some(inner) = try_get_window_inner(msg.hwnd) {
166            inner.handle_immediate_msg(msg.message, msg.wParam, msg.lParam)
167        } else {
168            false
169        }
170    }
171
172    fn wait_message(&self) -> WindowsMessageWaitResult {
173        let wait_result = unsafe {
174            MsgWaitForMultipleObjects(Some(&[self.inner.event]), false, INFINITE, QS_ALLINPUT)
175        };
176
177        match wait_result {
178            WAIT_EVENT(0) => WindowsMessageWaitResult::ForegroundExecution,
179            WAIT_EVENT(1) => {
180                let mut msg = MSG::default();
181                unsafe { GetMessageW(&mut msg, HWND::default(), 0, 0) };
182                WindowsMessageWaitResult::WindowsMessage(msg)
183            }
184            _ => {
185                log::error!("unhandled windows wait message: {}", wait_result.0);
186                WindowsMessageWaitResult::Error
187            }
188        }
189    }
190}
191
192impl Platform for WindowsPlatform {
193    fn background_executor(&self) -> BackgroundExecutor {
194        self.inner.background_executor.clone()
195    }
196
197    fn foreground_executor(&self) -> ForegroundExecutor {
198        self.inner.foreground_executor.clone()
199    }
200
201    fn text_system(&self) -> Arc<dyn PlatformTextSystem> {
202        self.inner.text_system.clone()
203    }
204
205    fn run(&self, on_finish_launching: Box<dyn 'static + FnOnce()>) {
206        on_finish_launching();
207        loop {
208            match self.wait_message() {
209                WindowsMessageWaitResult::ForegroundExecution => {
210                    for runnable in self.inner.main_receiver.drain() {
211                        runnable.run();
212                    }
213                }
214                WindowsMessageWaitResult::WindowsMessage(msg) => {
215                    if msg.message == WM_QUIT {
216                        break;
217                    }
218
219                    if !self.run_immediate_msg_handlers(&msg) {
220                        unsafe { TranslateMessage(&msg) };
221                        unsafe { DispatchMessageW(&msg) };
222                    }
223                }
224                WindowsMessageWaitResult::Error => {}
225            }
226        }
227
228        let mut callbacks = self.inner.callbacks.lock();
229        if let Some(callback) = callbacks.quit.as_mut() {
230            callback()
231        }
232    }
233
234    fn quit(&self) {
235        self.foreground_executor()
236            .spawn(async { unsafe { PostQuitMessage(0) } })
237            .detach();
238    }
239
240    // todo!("windows")
241    fn restart(&self) {
242        unimplemented!()
243    }
244
245    // todo!("windows")
246    fn activate(&self, ignoring_other_apps: bool) {}
247
248    // todo!("windows")
249    fn hide(&self) {
250        unimplemented!()
251    }
252
253    // todo!("windows")
254    fn hide_other_apps(&self) {
255        unimplemented!()
256    }
257
258    // todo!("windows")
259    fn unhide_other_apps(&self) {
260        unimplemented!()
261    }
262
263    // todo!("windows")
264    fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>> {
265        vec![Rc::new(WindowsDisplay::new())]
266    }
267
268    // todo!("windows")
269    fn display(&self, id: crate::DisplayId) -> Option<Rc<dyn PlatformDisplay>> {
270        Some(Rc::new(WindowsDisplay::new()))
271    }
272
273    // todo!("windows")
274    fn active_window(&self) -> Option<AnyWindowHandle> {
275        unimplemented!()
276    }
277
278    fn open_window(
279        &self,
280        handle: AnyWindowHandle,
281        options: WindowOptions,
282    ) -> Box<dyn PlatformWindow> {
283        Box::new(WindowsWindow::new(self.inner.clone(), handle, options))
284    }
285
286    // todo!("windows")
287    fn window_appearance(&self) -> WindowAppearance {
288        WindowAppearance::Dark
289    }
290
291    // todo!("windows")
292    fn open_url(&self, url: &str) {
293        // todo!("windows")
294    }
295
296    // todo!("windows")
297    fn on_open_urls(&self, callback: Box<dyn FnMut(Vec<String>)>) {
298        self.inner.callbacks.lock().open_urls = Some(callback);
299    }
300
301    // todo!("windows")
302    fn prompt_for_paths(&self, options: PathPromptOptions) -> Receiver<Option<Vec<PathBuf>>> {
303        unimplemented!()
304    }
305
306    // todo!("windows")
307    fn prompt_for_new_path(&self, directory: &Path) -> Receiver<Option<PathBuf>> {
308        unimplemented!()
309    }
310
311    // todo!("windows")
312    fn reveal_path(&self, path: &Path) {
313        unimplemented!()
314    }
315
316    fn on_become_active(&self, callback: Box<dyn FnMut()>) {
317        self.inner.callbacks.lock().become_active = Some(callback);
318    }
319
320    fn on_resign_active(&self, callback: Box<dyn FnMut()>) {
321        self.inner.callbacks.lock().resign_active = Some(callback);
322    }
323
324    fn on_quit(&self, callback: Box<dyn FnMut()>) {
325        self.inner.callbacks.lock().quit = Some(callback);
326    }
327
328    fn on_reopen(&self, callback: Box<dyn FnMut()>) {
329        self.inner.callbacks.lock().reopen = Some(callback);
330    }
331
332    fn on_event(&self, callback: Box<dyn FnMut(PlatformInput) -> bool>) {
333        self.inner.callbacks.lock().event = Some(callback);
334    }
335
336    // todo!("windows")
337    fn set_menus(&self, menus: Vec<Menu>, keymap: &Keymap) {}
338
339    fn on_app_menu_action(&self, callback: Box<dyn FnMut(&dyn Action)>) {
340        self.inner.callbacks.lock().app_menu_action = Some(callback);
341    }
342
343    fn on_will_open_app_menu(&self, callback: Box<dyn FnMut()>) {
344        self.inner.callbacks.lock().will_open_app_menu = Some(callback);
345    }
346
347    fn on_validate_app_menu_command(&self, callback: Box<dyn FnMut(&dyn Action) -> bool>) {
348        self.inner.callbacks.lock().validate_app_menu_command = Some(callback);
349    }
350
351    fn os_name(&self) -> &'static str {
352        "Windows"
353    }
354
355    fn os_version(&self) -> Result<SemanticVersion> {
356        Ok(SemanticVersion {
357            major: 1,
358            minor: 0,
359            patch: 0,
360        })
361    }
362
363    fn app_version(&self) -> Result<SemanticVersion> {
364        Ok(SemanticVersion {
365            major: 1,
366            minor: 0,
367            patch: 0,
368        })
369    }
370
371    // todo!("windows")
372    fn app_path(&self) -> Result<PathBuf> {
373        Err(anyhow!("not yet implemented"))
374    }
375
376    // todo!("windows")
377    fn local_timezone(&self) -> UtcOffset {
378        UtcOffset::from_hms(9, 0, 0).unwrap()
379    }
380
381    // todo!("windows")
382    fn double_click_interval(&self) -> Duration {
383        Duration::from_millis(100)
384    }
385
386    // todo!("windows")
387    fn path_for_auxiliary_executable(&self, name: &str) -> Result<PathBuf> {
388        Err(anyhow!("not yet implemented"))
389    }
390
391    // todo!("windows")
392    fn set_cursor_style(&self, style: CursorStyle) {}
393
394    // todo!("windows")
395    fn should_auto_hide_scrollbars(&self) -> bool {
396        false
397    }
398
399    // todo!("windows")
400    fn write_to_clipboard(&self, item: ClipboardItem) {
401        unimplemented!()
402    }
403
404    // todo!("windows")
405    fn read_from_clipboard(&self) -> Option<ClipboardItem> {
406        unimplemented!()
407    }
408
409    // todo!("windows")
410    fn write_credentials(&self, url: &str, username: &str, password: &[u8]) -> Task<Result<()>> {
411        Task::Ready(Some(Err(anyhow!("not implemented yet."))))
412    }
413
414    // todo!("windows")
415    fn read_credentials(&self, url: &str) -> Task<Result<Option<(String, Vec<u8>)>>> {
416        Task::Ready(Some(Err(anyhow!("not implemented yet."))))
417    }
418
419    // todo!("windows")
420    fn delete_credentials(&self, url: &str) -> Task<Result<()>> {
421        Task::Ready(Some(Err(anyhow!("not implemented yet."))))
422    }
423
424    fn register_url_scheme(&self, _: &str) -> Task<anyhow::Result<()>> {
425        Task::ready(Err(anyhow!("register_url_scheme unimplemented")))
426    }
427}