1use {
2 std::{
3 ffi::OsStr,
4 os::windows::ffi::OsStrExt,
5 mem,
6 cell::{
7 Cell,
8 RefCell,
9 },
10 },
11 crate::{
12 log,
13 error,
14 windows::{
15 core::HRESULT,
16 core::PCWSTR,
17 core::PCSTR,
18 core::IntoParam,
19 Win32::{
20 UI::{
21 WindowsAndMessaging::{
22 WNDCLASSEXW,
23 PM_REMOVE,
24 LoadIconW,
25 RegisterClassExW,
26 IsGUIThread,
27 GetMessageW,
28 TranslateMessage,
29 DispatchMessageW,
30 PeekMessageW,
31 SetTimer,
32 KillTimer,
33 ShowCursor,
34 SetCursor,
35 LoadCursorW,
36 IsProcessDPIAware,
37 IDC_ARROW,
38 IDC_CROSS,
39 IDC_HAND,
40 IDC_SIZEALL,
41 IDC_IBEAM,
42 IDC_HELP,
43 IDC_NO,
44 IDC_SIZEWE,
45 IDC_SIZENS,
46 IDC_SIZENESW,
47 IDC_SIZENWSE,
48 WM_QUIT,
49 CS_HREDRAW,
50 CS_VREDRAW,
51 CS_OWNDC,
52 IDI_WINLOGO,
53 },
54 HiDpi::{
55 PROCESS_DPI_AWARENESS,
56 DPI_AWARENESS_CONTEXT,
57 MONITOR_DPI_TYPE,
58 DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2,
59 DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE,
60 PROCESS_PER_MONITOR_DPI_AWARE,
61 MDT_EFFECTIVE_DPI
62 },
63 },
64 Graphics::Gdi::{
65 CreateSolidBrush,
66 HMONITOR,
67 GetDC,
68 MonitorFromWindow,
69 GetDeviceCaps,
70 MONITOR_DEFAULTTONEAREST,
71 LOGPIXELSX
72 },
73 Foundation::{
74 COLORREF,
75 S_OK,
76 HWND,
77 BOOL,
78 FARPROC,
79 DRAGDROP_S_DROP,
80 DRAGDROP_S_CANCEL,
81 },
82 System::{
83 Threading::ExitProcess,
84 LibraryLoader::{
85 GetModuleHandleW,
86 LoadLibraryA,
87 GetProcAddress,
88 },
89 Performance::{
90 QueryPerformanceCounter,
91 QueryPerformanceFrequency,
92 },
93 Ole::{
95 OleInitialize,
96 IDropSource,
98 DROPEFFECT,
99 DROPEFFECT_COPY,
100 DROPEFFECT_MOVE,
101 },
102 },
103 },
104 },
105 event::*,
106 cursor::MouseCursor,
107 os::{
108 cx_native::EventFlow,
109 windows::{
110 dropsource::*,
111 dataobject::*,
112 win32_event::Win32Event,
113 win32_window::Win32Window,
114 },
115 },
116 window::WindowId,
117 },
118};
119pub const FALSE: BOOL = BOOL(0);
120pub const TRUE: BOOL = BOOL(1);
121
122static mut WIN32_APP: Option<RefCell<Win32App >> = None;
123
124pub fn get_win32_app_global() -> std::cell::RefMut<'static, Win32App> {
125 unsafe {
126 WIN32_APP.as_mut().unwrap().borrow_mut()
127 }
128}
129
130pub fn init_win32_app_global(event_callback: Box<dyn FnMut(Win32Event) -> EventFlow>) {
131 unsafe {
132 WIN32_APP = Some(RefCell::new(Win32App::new(event_callback)));
133 }
134}
135
136#[allow(non_snake_case)]
138pub unsafe fn DoDragDrop<P0, P1>(pdataobj: P0, pdropsource: P1, dwokeffects: DROPEFFECT, pdweffect: *mut DROPEFFECT) -> HRESULT
139where
140P0: IntoParam<IDataObject>,
141P1: IntoParam<IDropSource>,
142{
143 ::windows_targets::link!("ole32.dll" "system" fn DoDragDrop(pdataobj: *mut::core::ffi::c_void, pdropsource: *mut::core::ffi::c_void, dwokeffects: DROPEFFECT, pdweffect: *mut DROPEFFECT) -> HRESULT);
144 DoDragDrop(pdataobj.into_param().abi(), pdropsource.into_param().abi(), dwokeffects, pdweffect)
145}
146
147pub struct Win32App {
148 pub time_start: i64,
149 pub time_freq: i64,
150 event_callback: Option<Box<dyn FnMut(Win32Event) -> EventFlow >>,
151 pub window_class_name: Vec<u16>,
152 pub all_windows: Vec<HWND>,
153 pub timers: Vec<Win32Timer>,
154 pub was_signal_poll: bool,
155 pub event_flow: EventFlow,
156 pub dpi_functions: DpiFunctions,
157 pub current_cursor: MouseCursor,
158 pub currently_clicked_window_id: Option<WindowId >,
159 pub start_dragging_items: Option<Vec<DragItem >>,
160 pub is_dragging_internal: Cell<bool>,
161}
162
163#[derive(Clone)]
164pub enum Win32Timer {
165 Free,
166 Timer {win32_id: usize, timer_id: u64, interval: f64, repeats: bool},
167 Resize {win32_id: usize},
168 DragDrop {win32_id: usize},
169 SignalPoll {win32_id: usize},
170}
171
172impl Win32App {
173 pub fn new(event_callback: Box<dyn FnMut(Win32Event) -> EventFlow>) -> Win32App {
174
175 let window_class_name = encode_wide("MakepadWindow\0");
176 let class = WNDCLASSEXW {
177 cbSize: mem::size_of::<WNDCLASSEXW>() as u32,
178 style: CS_HREDRAW
179 | CS_VREDRAW
180 | CS_OWNDC,
181 lpfnWndProc: Some(Win32Window::window_class_proc),
182 hInstance: unsafe {GetModuleHandleW(None).unwrap().into()},
183 hIcon: unsafe {LoadIconW(None, IDI_WINLOGO).unwrap()}, lpszClassName: PCWSTR(window_class_name.as_ptr()),
185 hbrBackground: unsafe {CreateSolidBrush(COLORREF(0x3f3f3f3f))},
186 ..Default::default()
187 };
195
196 unsafe {
197 RegisterClassExW(&class);
198 IsGUIThread(TRUE);
199
200 OleInitialize(None).unwrap();
202 }
203
204 let mut time_start = 0i64;
205 unsafe {QueryPerformanceCounter(&mut time_start).unwrap()};
206
207 let mut time_freq = 0i64;
208 unsafe {QueryPerformanceFrequency(&mut time_freq).unwrap()};
209
210 let win32_app = Win32App {
211 start_dragging_items: None,
212 window_class_name,
213 was_signal_poll: false,
214 time_start,
215 time_freq,
216 event_callback: Some(event_callback),
217 event_flow: EventFlow::Poll,
218 all_windows: Vec::new(),
219 timers: Vec::new(),
220 dpi_functions: DpiFunctions::new(),
221 current_cursor: MouseCursor::Default,
222 currently_clicked_window_id: None,
223 is_dragging_internal: Cell::new(false),
224 };
225 win32_app.dpi_functions.become_dpi_aware();
226
227 win32_app
228 }
229
230 pub fn event_loop() {
231 unsafe {
232 loop {
233 let event_flow = get_win32_app_global().event_flow.clone();
234 match event_flow {
235 EventFlow::Wait => {
236 let mut msg = std::mem::MaybeUninit::uninit();
237 let ret = GetMessageW(msg.as_mut_ptr(), None, 0, 0);
238 let msg = msg.assume_init();
239 if ret == FALSE {
240 debug_assert_eq!(msg.message, WM_QUIT);
242 get_win32_app_global().event_flow = EventFlow::Exit;
243 }
244 else {
245 TranslateMessage(&msg);
246 DispatchMessageW(&msg);
247 if !get_win32_app_global().was_signal_poll() {
248 Win32App::do_callback(Win32Event::Paint);
249 }
250 }
251 }
252 EventFlow::Poll => {
253 let mut msg = std::mem::MaybeUninit::uninit();
254 let ret = PeekMessageW(msg.as_mut_ptr(), None, 0, 0, PM_REMOVE);
255 let msg = msg.assume_init();
256 if ret == FALSE {
257 Win32App::do_callback(Win32Event::Paint)
258 }
259 else {
260 TranslateMessage(&msg);
261 DispatchMessageW(&msg);
262 }
263 }
264 EventFlow::Exit => panic!()
265 }
266 Win32App::poll_start_drag_drop();
267 }
268 }
269 }
270
271 pub fn do_callback(event: Win32Event) {
272 let cb = get_win32_app_global().event_callback.take();
273 if let Some(mut callback) = cb {
274 let event_flow = callback(event);
275 get_win32_app_global().event_flow = event_flow;
276 if let EventFlow::Exit = event_flow {
277 unsafe {ExitProcess(0);}
278 }
279 get_win32_app_global().event_callback = Some(callback);
280 }
281 }
282
283 pub unsafe extern "system" fn timer_proc(_hwnd: HWND, _arg1: u32, in_win32_id: usize, _arg2: u32) {
284 let hit_timer = {
285 let mut win32_app = get_win32_app_global();
286 {
287 let mut hit_timer = None;
288 for slot in 0..win32_app.timers.len() {
289 match win32_app.timers[slot] {
290 Win32Timer::Timer {win32_id, repeats, ..} => if win32_id == in_win32_id {
291 hit_timer = Some(win32_app.timers[slot].clone());
292 if !repeats {
293 KillTimer(None, in_win32_id).unwrap();
294 win32_app.timers[slot] = Win32Timer::Free;
295 }
296 break;
297 },
298 Win32Timer::DragDrop {win32_id, ..} => if win32_id == in_win32_id {
299 hit_timer = Some(win32_app.timers[slot].clone());
300 break;
301 },
302 Win32Timer::Resize {win32_id, ..} => if win32_id == in_win32_id {
303 hit_timer = Some(win32_app.timers[slot].clone());
304 break;
305 },
306 Win32Timer::SignalPoll {win32_id, ..} => if win32_id == in_win32_id {
307 hit_timer = Some(win32_app.timers[slot].clone());
308 break;
309 }
310 _ => ()
311 }
312 };
313 hit_timer
314 }
315 };
316 let time =get_win32_app_global().time_now();
318 if let Some(hit_timer) = hit_timer {
319 match hit_timer {
320 Win32Timer::Timer {timer_id, ..} => {
321 Win32App::do_callback(Win32Event::Timer(TimerEvent {
322 time: Some(time),
323 timer_id: timer_id
324 }));
325 },
326 Win32Timer::Resize {..} => {
327 Win32App::do_callback(Win32Event::Paint);
328 },
329 Win32Timer::DragDrop {..} => {
330 Win32App::do_callback(Win32Event::Paint);
331 },
332 Win32Timer::SignalPoll {..} => {
333 Win32App::do_callback(
334 Win32Event::Signal
335 );
336 get_win32_app_global().was_signal_poll = true;
337 }
338 _ => ()
339 }
340 }
341 }
342
343 pub fn was_signal_poll(&mut self) -> bool {
344 if self.was_signal_poll {
345 self.was_signal_poll = false;
346 true
347 }
348 else {
349 false
350 }
351 }
352
353 pub fn get_free_timer_slot(&mut self) -> usize {
354 for slot in 0..self.timers.len() {
356 if let Win32Timer::Free = self.timers[slot] {
357 return slot
358 }
359 }
360 let slot = self.timers.len();
361 self.timers.push(Win32Timer::Free);
362 slot
363 }
364
365 pub fn start_timer(&mut self, timer_id: u64, interval: f64, repeats: bool) {
366 let slot = self.get_free_timer_slot();
367 let win32_id = unsafe {SetTimer(None, 0, (interval * 1000.0) as u32, Some(Self::timer_proc))};
368 self.timers[slot] = Win32Timer::Timer {
369 timer_id: timer_id,
370 win32_id: win32_id,
371 interval: interval,
372 repeats: repeats
373 };
374 }
375
376 pub fn stop_timer(&mut self, which_timer_id: u64) {
377 for slot in 0..self.timers.len() {
378 if let Win32Timer::Timer {win32_id, timer_id, ..} = self.timers[slot] {
379 if timer_id == which_timer_id {
380 self.timers[slot] = Win32Timer::Free;
381 unsafe {KillTimer(None, win32_id).unwrap();}
382 }
383 }
384 }
385 }
386
387 pub fn start_resize(&mut self) {
388 let slot = self.get_free_timer_slot();
389 let win32_id = unsafe {SetTimer(None, 0, 8 as u32, Some(Self::timer_proc))};
390 self.timers[slot] = Win32Timer::Resize {win32_id: win32_id};
391 }
392
393 pub fn poll_start_drag_drop() {
394 let items = get_win32_app_global().start_dragging_items.take();
395 if let Some(items) = items {
396 {
397 let mut win32_app = get_win32_app_global();
398 let slot = win32_app.get_free_timer_slot();
399 let win32_id = unsafe {SetTimer(None, 0, 8 as u32, Some(Self::timer_proc))};
400 win32_app.timers[slot] = Win32Timer::DragDrop {win32_id: win32_id};
401 }
402
403 if items.len() > 1 {
404 error!("multi-item drag/drop operation not supported");
405 }
406 match &items[0] {
407 DragItem::FilePath {path, internal_id,} => {
408
409 if (path.len() > 0) || internal_id.is_some() {
413
414 let data_object: IDataObject = DragItem::FilePath {path: path.clone(), internal_id: internal_id.clone(),}.into();
416
417 let drop_source: IDropSource = DropSource {}.into();
419
420 get_win32_app_global().is_dragging_internal.replace(true);
421 let mut effect = DROPEFFECT(0);
422 match unsafe {DoDragDrop(&data_object, &drop_source, DROPEFFECT_COPY | DROPEFFECT_MOVE, &mut effect)} {
423 DRAGDROP_S_DROP => {},
424 DRAGDROP_S_CANCEL => {},
425 _ => {log!("DoDragDrop: failed for some reason")},
426 }
427 get_win32_app_global().is_dragging_internal.replace(false);
428 }
429 },
430 _ => {
431 error!("Only DragItem::FilePath supported");
432 }
433 }
434 {
435 let mut win32_app = get_win32_app_global();
436 for slot in 0..win32_app.timers.len() {
437 if let Win32Timer::DragDrop {win32_id} = win32_app.timers[slot] {
438 win32_app.timers[slot] = Win32Timer::Free;
439 unsafe {KillTimer(None, win32_id).unwrap();}
440 }
441 }
442 }
443 }
444 }
445
446 pub fn start_signal_poll(&mut self) {
447 let slot = self.get_free_timer_slot();
448 let win32_id = unsafe {SetTimer(None, 0, 8 as u32, Some(Self::timer_proc))};
449 self.timers[slot] = Win32Timer::SignalPoll {win32_id: win32_id};
450 }
451
452 pub fn stop_resize(&mut self) {
453 for slot in 0..self.timers.len() {
454 if let Win32Timer::Resize {win32_id} = self.timers[slot] {
455 self.timers[slot] = Win32Timer::Free;
456 unsafe {KillTimer(None, win32_id).unwrap();}
457 }
458 }
459 }
460
461 pub fn start_dragging(&mut self, items: Vec<DragItem>) {
462 self.start_dragging_items = Some(items);
463 }
464
465 pub fn time_now(&self) -> f64 {
466 unsafe {
467 let mut time_now = 0i64;
468 QueryPerformanceCounter(&mut time_now).unwrap();
469 (time_now - self.time_start) as f64 / self.time_freq as f64
470 }
471 }
472
473 pub fn set_mouse_cursor(&mut self, cursor: MouseCursor) {
474 if self.current_cursor != cursor {
475 let win32_cursor = match cursor {
476 MouseCursor::Hidden => {
477 PCWSTR::null()
478 },
479 MouseCursor::Default => IDC_ARROW,
480 MouseCursor::Crosshair => IDC_CROSS,
481 MouseCursor::Hand => IDC_HAND,
482 MouseCursor::Arrow => IDC_ARROW,
483 MouseCursor::Move => IDC_SIZEALL,
484 MouseCursor::Text => IDC_IBEAM,
485 MouseCursor::Wait => IDC_ARROW,
486 MouseCursor::Help => IDC_HELP,
487 MouseCursor::NotAllowed => IDC_NO,
488
489 MouseCursor::EResize => IDC_SIZEWE,
490 MouseCursor::NResize => IDC_SIZENS,
491 MouseCursor::NeResize => IDC_SIZENESW,
492 MouseCursor::NwResize => IDC_SIZENWSE,
493 MouseCursor::SResize => IDC_SIZENS,
494 MouseCursor::SeResize => IDC_SIZENWSE,
495 MouseCursor::SwResize => IDC_SIZENESW,
496 MouseCursor::WResize => IDC_SIZEWE,
497
498
499 MouseCursor::NsResize => IDC_SIZENS,
500 MouseCursor::NeswResize => IDC_SIZENESW,
501 MouseCursor::EwResize => IDC_SIZEWE,
502 MouseCursor::NwseResize => IDC_SIZENWSE,
503
504 MouseCursor::ColResize => IDC_SIZEWE,
505 MouseCursor::RowResize => IDC_SIZENS,
506 };
507 self.current_cursor = cursor;
508 unsafe {
509 if win32_cursor == PCWSTR::null() {
510 ShowCursor(FALSE);
511 }
512 else {
513 SetCursor(LoadCursorW(None, win32_cursor).unwrap());
514 ShowCursor(TRUE);
515 }
516 }
517 }
519 }
520}
521
522type SetProcessDPIAware = unsafe extern "system" fn () -> BOOL;
525type SetProcessDpiAwareness = unsafe extern "system" fn (value: PROCESS_DPI_AWARENESS,) -> HRESULT;
526type SetProcessDpiAwarenessContext = unsafe extern "system" fn (value: DPI_AWARENESS_CONTEXT,) -> BOOL;
527type GetDpiForWindow = unsafe extern "system" fn (hwnd: HWND) -> u32;
528type GetDpiForMonitor = unsafe extern "system" fn (hmonitor: HMONITOR, dpi_type: MONITOR_DPI_TYPE, dpi_x: *mut u32, dpi_y: *mut u32) -> HRESULT;
529type EnableNonClientDpiScaling = unsafe extern "system" fn (hwnd: HWND) -> BOOL;
530
531fn get_function_impl(library: &str, function: &str) -> FARPROC {
534 let module = unsafe {LoadLibraryA(PCSTR::from_raw(library.as_ptr()))};
537 if module.is_err() {
538 return None;
539 }
540
541 let function_ptr = unsafe {GetProcAddress(module.unwrap(), PCSTR::from_raw(function.as_ptr()))};
542 if function_ptr.is_none() {
543 return None;
544 }
545
546 function_ptr
547}
548
549macro_rules!get_function {
550 ( $ lib: expr, $ func: ident) => {
551 get_function_impl(concat!( $ lib, '\0'), concat!(stringify!( $ func), '\0'))
552 .map( | f | unsafe {mem::transmute::<_, $ func>(f)})
553 }
554}
555
556pub fn encode_wide(string: impl AsRef<OsStr>) -> Vec<u16> {
557 string.as_ref().encode_wide().chain(std::iter::once(0)).collect()
558}
559
560pub struct DpiFunctions {
571 get_dpi_for_window: Option<GetDpiForWindow>,
572 get_dpi_for_monitor: Option<GetDpiForMonitor>,
573 enable_nonclient_dpi_scaling: Option<EnableNonClientDpiScaling>,
574 set_process_dpi_awareness_context: Option<SetProcessDpiAwarenessContext>,
575 set_process_dpi_awareness: Option<SetProcessDpiAwareness>,
576 set_process_dpi_aware: Option<SetProcessDPIAware>
577}
578
579const BASE_DPI: u32 = 96;
580
581impl DpiFunctions {
582 fn new() -> DpiFunctions {
583 DpiFunctions {
584 get_dpi_for_window: get_function!("user32.dll", GetDpiForWindow),
585 get_dpi_for_monitor: get_function!("shcore.dll", GetDpiForMonitor),
586 enable_nonclient_dpi_scaling: get_function!("user32.dll", EnableNonClientDpiScaling),
587 set_process_dpi_awareness_context: get_function!("user32.dll", SetProcessDpiAwarenessContext),
588 set_process_dpi_awareness: get_function!("shcore.dll", SetProcessDpiAwareness),
589 set_process_dpi_aware: get_function!("user32.dll", SetProcessDPIAware)
590 }
591 }
592
593 fn become_dpi_aware(&self) {
594 unsafe {
595 if let Some(set_process_dpi_awareness_context) = self.set_process_dpi_awareness_context {
596 if set_process_dpi_awareness_context(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2) == FALSE {
598 set_process_dpi_awareness_context(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE);
601 }
602 }
603 else if let Some(set_process_dpi_awareness) = self.set_process_dpi_awareness {
604 set_process_dpi_awareness(PROCESS_PER_MONITOR_DPI_AWARE).unwrap();
606 }
607 else if let Some(set_process_dpi_aware) = self.set_process_dpi_aware {
608 set_process_dpi_aware().unwrap();
610 }
611 }
612 }
613
614 pub fn enable_non_client_dpi_scaling(&self, hwnd: HWND) {
615 unsafe {
616 if let Some(enable_nonclient_dpi_scaling) = self.enable_nonclient_dpi_scaling {
617 enable_nonclient_dpi_scaling(hwnd);
618 }
619 }
620 }
621 pub fn hwnd_dpi_factor(&self, hwnd: HWND) -> f32 {
640 unsafe {
641 let hdc = GetDC(hwnd);
642 if hdc.is_invalid() {
643 panic!("`GetDC` returned null!");
644 }
645 let dpi = if let Some(get_dpi_for_window) = self.get_dpi_for_window {
646 match get_dpi_for_window(hwnd) {
648 0 => BASE_DPI, dpi => dpi as u32,
650 }
651 }
652 else if let Some(get_dpi_for_monitor) = self.get_dpi_for_monitor {
653 let monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
655 if monitor.is_invalid() {
656 BASE_DPI
657 }
658 else {
659 let mut dpi_x = 0;
660 let mut dpi_y = 0;
661 if get_dpi_for_monitor(monitor, MDT_EFFECTIVE_DPI, &mut dpi_x, &mut dpi_y) == S_OK {
662 dpi_x as u32
663 } else {
664 BASE_DPI
665 }
666 }
667 }
668 else {
669 if IsProcessDPIAware() == TRUE {
671 GetDeviceCaps(hdc, LOGPIXELSX) as u32
674 } else {
675 BASE_DPI
679 }
680 };
681 dpi as f32 / BASE_DPI as f32
682 }
683 }
684
685}