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}