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 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 Com::IDataObject,
94 Ole::{
95 OleInitialize,
96 DoDragDrop,
97 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::DragItemWindows,
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
122
123thread_local! {
124 pub static WIN32_APP: RefCell<Option<Win32App>> = RefCell::new(None);
125}
126
127pub fn with_win32_app<R>(f: impl FnOnce(&mut Win32App) -> R) -> R {
128 WIN32_APP.with_borrow_mut(|app| {
129 f(app.as_mut().unwrap())
130 })
131}
132
133pub fn init_win32_app_global(event_callback: Box<dyn FnMut(Win32Event) -> EventFlow>) {
134 WIN32_APP.with(|app| {
135 *app.borrow_mut() = Some(Win32App::new(event_callback));
136 });
137}
138pub struct Win32App {
151 event_callback: Option<Box<dyn FnMut(Win32Event) -> EventFlow >>,
152 pub window_class_name: Vec<u16>,
153 pub all_windows: Vec<HWND>,
154 pub time: Win32Time,
155 pub timers: Vec<Win32Timer>,
156 pub was_signal_poll: bool,
157 pub event_flow: EventFlow,
158 pub dpi_functions: DpiFunctions,
159 pub current_cursor: Option<MouseCursor>,
160 pub currently_clicked_window_id: Option<WindowId >,
161 pub start_dragging_items: Option<Vec<DragItem >>,
162 pub is_dragging_internal: Cell<bool>,
163}
164
165#[derive(Clone)]
166pub enum Win32Timer {
167 Free,
168 Timer {win32_id: usize, timer_id: u64, interval: f64, repeats: bool},
169 Resize {win32_id: usize},
170 DragDrop {win32_id: usize},
171 SignalPoll {win32_id: usize},
172}
173
174pub struct Win32Time{
175 pub time_start: i64,
176 pub time_freq: i64,
177}
178
179impl Win32Time{
180 pub fn new()->Self{
181 let mut time_start = 0i64;
182 unsafe {QueryPerformanceCounter(&mut time_start).unwrap()};
183
184 let mut time_freq = 0i64;
185 unsafe {QueryPerformanceFrequency(&mut time_freq).unwrap()};
186 Self{
187 time_start,
188 time_freq,
189 }
190 }
191
192 pub fn time_now(&self)->f64{
193 unsafe {
194 let mut time_now = 0i64;
195 QueryPerformanceCounter(&mut time_now).unwrap();
196 (time_now - self.time_start) as f64 / self.time_freq as f64
197 }
198 }
199}
200
201impl Win32App {
202 pub fn new(event_callback: Box<dyn FnMut(Win32Event) -> EventFlow>) -> Win32App {
203
204 let window_class_name = encode_wide("MakepadWindow\0");
205 let class = WNDCLASSEXW {
206 cbSize: mem::size_of::<WNDCLASSEXW>() as u32,
207 style: CS_HREDRAW
208 | CS_VREDRAW
209 | CS_OWNDC,
210 lpfnWndProc: Some(Win32Window::window_class_proc),
211 hInstance: unsafe {GetModuleHandleW(None).unwrap().into()},
212 hIcon: unsafe {LoadIconW(None, IDI_WINLOGO).unwrap()}, lpszClassName: PCWSTR(window_class_name.as_ptr()),
214 hbrBackground: unsafe {CreateSolidBrush(COLORREF(0x3f3f3f3f))},
215 ..Default::default()
216 };
224
225 unsafe {
226 RegisterClassExW(&class);
227 let _ = IsGUIThread(TRUE);
228
229 OleInitialize(None).unwrap();
231 }
232
233
234 let win32_app = Win32App {
235 start_dragging_items: None,
236 window_class_name,
237 was_signal_poll: false,
238 time: Win32Time::new(),
239 event_callback: Some(event_callback),
240 event_flow: EventFlow::Poll,
241 all_windows: Vec::new(),
242 timers: Vec::new(),
243 dpi_functions: DpiFunctions::new(),
244 current_cursor: None,
245 currently_clicked_window_id: None,
246 is_dragging_internal: Cell::new(false),
247 };
248 win32_app.dpi_functions.become_dpi_aware();
249
250 win32_app
251 }
252
253 pub fn event_loop() {
254 unsafe {
255 loop {
256 let event_flow = with_win32_app(|app| app.event_flow.clone());
257 match event_flow {
258 EventFlow::Wait => {
259 let mut msg = std::mem::MaybeUninit::uninit();
260 let ret = GetMessageW(msg.as_mut_ptr(), None, 0, 0);
261 let msg = msg.assume_init();
262 if ret == FALSE {
263 debug_assert_eq!(msg.message, WM_QUIT);
265 with_win32_app(|app| app.event_flow = EventFlow::Exit);
266 }
267 else {
268 let _ = TranslateMessage(&msg);
269 DispatchMessageW(&msg);
270 if !with_win32_app(|app| app.was_signal_poll()) {
271 Win32App::do_callback(Win32Event::Paint);
272 }
273 }
274 }
275 EventFlow::Poll => {
276 let mut msg = std::mem::MaybeUninit::uninit();
277 let ret = PeekMessageW(msg.as_mut_ptr(), None, 0, 0, PM_REMOVE);
278 let msg = msg.assume_init();
279 if ret == FALSE {
280 Win32App::do_callback(Win32Event::Paint)
281 }
282 else {
283 let _ = TranslateMessage(&msg);
284 DispatchMessageW(&msg);
285 }
286 }
287 EventFlow::Exit => panic!()
288 }
289 Win32App::poll_start_drag_drop();
290 }
291 }
292 }
293
294 pub fn do_callback(event: Win32Event) {
295 let cb = with_win32_app(|app| app.event_callback.take());
296 if let Some(mut callback) = cb {
297 let event_flow = callback(event);
298 with_win32_app(|app| app.event_flow = event_flow);
299 if let EventFlow::Exit = event_flow {
300 unsafe {ExitProcess(0);}
301 }
302 with_win32_app(|app| app.event_callback = Some(callback));
303 }
304 }
305
306 pub unsafe extern "system" fn timer_proc(_hwnd: HWND, _arg1: u32, in_win32_id: usize, _arg2: u32) {
307 let hit_timer = {
308 with_win32_app(|app|{
309 let mut hit_timer = None;
310 for slot in 0..app.timers.len() {
311 match app.timers[slot] {
312 Win32Timer::Timer {win32_id, repeats, ..} => if win32_id == in_win32_id {
313 hit_timer = Some(app.timers[slot].clone());
314 if !repeats {
315 KillTimer(None, in_win32_id).unwrap();
316 app.timers[slot] = Win32Timer::Free;
317 }
318 break;
319 },
320 Win32Timer::DragDrop {win32_id, ..} => if win32_id == in_win32_id {
321 hit_timer = Some(app.timers[slot].clone());
322 break;
323 },
324 Win32Timer::Resize {win32_id, ..} => if win32_id == in_win32_id {
325 hit_timer = Some(app.timers[slot].clone());
326 break;
327 },
328 Win32Timer::SignalPoll {win32_id, ..} => if win32_id == in_win32_id {
329 hit_timer = Some(app.timers[slot].clone());
330 break;
331 }
332 _ => ()
333 }
334 };
335 hit_timer
336 })
337 };
338 let time = with_win32_app(|app| app.time_now());
340 if let Some(hit_timer) = hit_timer {
341 match hit_timer {
342 Win32Timer::Timer {timer_id, ..} => {
343 Win32App::do_callback(Win32Event::Timer(TimerEvent {
344 time: Some(time),
345 timer_id: timer_id
346 }));
347 },
348 Win32Timer::Resize {..} => {
349 Win32App::do_callback(Win32Event::Paint);
350 },
351 Win32Timer::DragDrop {..} => {
352 Win32App::do_callback(Win32Event::Paint);
353 },
354 Win32Timer::SignalPoll {..} => {
355 Win32App::do_callback(
356 Win32Event::Signal
357 );
358 with_win32_app(|app| app.was_signal_poll = true);
359 }
360 _ => ()
361 }
362 }
363 }
364
365 pub fn was_signal_poll(&mut self) -> bool {
366 if self.was_signal_poll {
367 self.was_signal_poll = false;
368 true
369 }
370 else {
371 false
372 }
373 }
374
375 pub fn get_free_timer_slot(&mut self) -> usize {
376 for slot in 0..self.timers.len() {
378 if let Win32Timer::Free = self.timers[slot] {
379 return slot
380 }
381 }
382 let slot = self.timers.len();
383 self.timers.push(Win32Timer::Free);
384 slot
385 }
386
387 pub fn start_timer(&mut self, timer_id: u64, interval: f64, repeats: bool) {
388 let slot = self.get_free_timer_slot();
389 let win32_id = unsafe {SetTimer(None, 0, (interval * 1000.0) as u32, Some(Self::timer_proc))};
390 if timer_id == 0{
391 self.timers[slot] = Win32Timer::SignalPoll {
392 win32_id: win32_id,
393 };
394 }
395 else{
396 self.timers[slot] = Win32Timer::Timer {
397 timer_id: timer_id,
398 win32_id: win32_id,
399 interval: interval,
400 repeats: repeats
401 };
402 }
403 }
404
405 pub fn stop_timer(&mut self, which_timer_id: u64) {
406 for slot in 0..self.timers.len() {
407 if let Win32Timer::Timer {win32_id, timer_id, ..} = self.timers[slot] {
408 if timer_id == which_timer_id {
409 self.timers[slot] = Win32Timer::Free;
410 unsafe {KillTimer(None, win32_id).unwrap();}
411 }
412 }
413 }
414 }
415
416 pub fn start_resize(&mut self) {
417 let slot = self.get_free_timer_slot();
418 let win32_id = unsafe {SetTimer(None, 0, 8 as u32, Some(Self::timer_proc))};
419 self.timers[slot] = Win32Timer::Resize {win32_id: win32_id};
420 }
421
422 pub fn poll_start_drag_drop() {
423 let items = with_win32_app(|app| app.start_dragging_items.take());
424 if let Some(items) = items {
425 with_win32_app(|app| {
426 let slot = app.get_free_timer_slot();
427 let win32_id = unsafe {SetTimer(None, 0, 8 as u32, Some(Self::timer_proc))};
428 app.timers[slot] = Win32Timer::DragDrop {win32_id: win32_id};
429 });
430
431 if items.len() > 1 {
432 error!("multi-item drag/drop operation not supported");
433 }
434 match &items[0] {
435 DragItem::FilePath {path, internal_id,} => {
436
437 if (path.len() > 0) || internal_id.is_some() {
441
442 let data_object: IDataObject = DragItemWindows(DragItem::FilePath {path: path.clone(), internal_id: internal_id.clone(),}).into();
444
445 let drop_source: IDropSource = DropSource {}.into();
447
448 with_win32_app(|app| app.is_dragging_internal.replace(true));
449 let mut effect = DROPEFFECT(0);
450 match unsafe {DoDragDrop(&data_object, &drop_source, DROPEFFECT_COPY | DROPEFFECT_MOVE, &mut effect)} {
451 DRAGDROP_S_DROP => {},
452 DRAGDROP_S_CANCEL => {},
453 _ => {log!("DoDragDrop: failed for some reason")},
454 }
455 with_win32_app(|app| app.is_dragging_internal.replace(false));
456 }
457 },
458 _ => {
459 error!("Only DragItem::FilePath supported");
460 }
461 }
462 with_win32_app(|app|{
463 for slot in 0..app.timers.len() {
464 if let Win32Timer::DragDrop {win32_id} = app.timers[slot] {
465 app.timers[slot] = Win32Timer::Free;
466 unsafe {KillTimer(None, win32_id).unwrap();}
467 }
468 }
469 })
470 }
471 }
472
473 pub fn start_signal_poll(&mut self) {
474 let slot = self.get_free_timer_slot();
475 let win32_id = unsafe {SetTimer(None, 0, 8 as u32, Some(Self::timer_proc))};
476 self.timers[slot] = Win32Timer::SignalPoll {win32_id: win32_id};
477 }
478
479 pub fn stop_resize(&mut self) {
480 for slot in 0..self.timers.len() {
481 if let Win32Timer::Resize {win32_id} = self.timers[slot] {
482 self.timers[slot] = Win32Timer::Free;
483 unsafe {KillTimer(None, win32_id).unwrap();}
484 }
485 }
486 }
487
488 pub fn start_dragging(&mut self, items: Vec<DragItem>) {
489 self.start_dragging_items = Some(items);
490 }
491
492 pub fn time_now(&self) -> f64 {
493 self.time.time_now()
494 }
495
496 pub fn set_mouse_cursor(&mut self, cursor: MouseCursor) {
497 if self.current_cursor.is_none() || self.current_cursor.unwrap() != cursor {
498 let win32_cursor = match cursor {
499 MouseCursor::Hidden => {
500 PCWSTR::null()
501 },
502 MouseCursor::Default => IDC_ARROW,
503 MouseCursor::Crosshair => IDC_CROSS,
504 MouseCursor::Hand => IDC_HAND,
505 MouseCursor::Grab | MouseCursor::Grabbing => IDC_HAND,
507 MouseCursor::Arrow => IDC_ARROW,
508 MouseCursor::Move => IDC_SIZEALL,
509 MouseCursor::Text => IDC_IBEAM,
510 MouseCursor::Wait => IDC_ARROW,
511 MouseCursor::Help => IDC_HELP,
512 MouseCursor::NotAllowed => IDC_NO,
513
514 MouseCursor::EResize => IDC_SIZEWE,
515 MouseCursor::NResize => IDC_SIZENS,
516 MouseCursor::NeResize => IDC_SIZENESW,
517 MouseCursor::NwResize => IDC_SIZENWSE,
518 MouseCursor::SResize => IDC_SIZENS,
519 MouseCursor::SeResize => IDC_SIZENWSE,
520 MouseCursor::SwResize => IDC_SIZENESW,
521 MouseCursor::WResize => IDC_SIZEWE,
522
523
524 MouseCursor::NsResize => IDC_SIZENS,
525 MouseCursor::NeswResize => IDC_SIZENESW,
526 MouseCursor::EwResize => IDC_SIZEWE,
527 MouseCursor::NwseResize => IDC_SIZENWSE,
528
529 MouseCursor::ColResize => IDC_SIZEWE,
530 MouseCursor::RowResize => IDC_SIZENS,
531 };
532 self.current_cursor = Some(cursor);
533 unsafe {
534 if win32_cursor == PCWSTR::null() {
535 ShowCursor(FALSE);
536 }
537 else {
538 SetCursor(LoadCursorW(None, win32_cursor).unwrap());
539 ShowCursor(TRUE);
540 }
541 }
542 }
544 }
545}
546
547
548
549type SetProcessDPIAware = unsafe extern "system" fn () -> BOOL;
552type SetProcessDpiAwareness = unsafe extern "system" fn (value: PROCESS_DPI_AWARENESS,) -> HRESULT;
553type SetProcessDpiAwarenessContext = unsafe extern "system" fn (value: DPI_AWARENESS_CONTEXT,) -> BOOL;
554type GetDpiForWindow = unsafe extern "system" fn (hwnd: HWND) -> u32;
555type GetDpiForMonitor = unsafe extern "system" fn (hmonitor: HMONITOR, dpi_type: MONITOR_DPI_TYPE, dpi_x: *mut u32, dpi_y: *mut u32) -> HRESULT;
556type EnableNonClientDpiScaling = unsafe extern "system" fn (hwnd: HWND) -> BOOL;
557
558fn get_function_impl(library: &str, function: &str) -> FARPROC {
561 let module = unsafe {LoadLibraryA(PCSTR::from_raw(library.as_ptr()))};
564 if module.is_err() {
565 return None;
566 }
567
568 let function_ptr = unsafe {GetProcAddress(module.unwrap(), PCSTR::from_raw(function.as_ptr()))};
569 if function_ptr.is_none() {
570 return None;
571 }
572
573 function_ptr
574}
575
576macro_rules!get_function {
577 ( $ lib: expr, $ func: ident) => {
578 get_function_impl(concat!( $ lib, '\0'), concat!(stringify!( $ func), '\0'))
579 .map( | f | unsafe {mem::transmute::<_, $ func>(f)})
580 }
581}
582
583pub fn encode_wide(string: impl AsRef<OsStr>) -> Vec<u16> {
584 string.as_ref().encode_wide().chain(std::iter::once(0)).collect()
585}
586
587pub struct DpiFunctions {
598 get_dpi_for_window: Option<GetDpiForWindow>,
599 get_dpi_for_monitor: Option<GetDpiForMonitor>,
600 enable_nonclient_dpi_scaling: Option<EnableNonClientDpiScaling>,
601 set_process_dpi_awareness_context: Option<SetProcessDpiAwarenessContext>,
602 set_process_dpi_awareness: Option<SetProcessDpiAwareness>,
603 set_process_dpi_aware: Option<SetProcessDPIAware>
604}
605
606const BASE_DPI: u32 = 96;
607
608impl DpiFunctions {
609 fn new() -> DpiFunctions {
610 DpiFunctions {
611 get_dpi_for_window: get_function!("user32.dll", GetDpiForWindow),
612 get_dpi_for_monitor: get_function!("shcore.dll", GetDpiForMonitor),
613 enable_nonclient_dpi_scaling: get_function!("user32.dll", EnableNonClientDpiScaling),
614 set_process_dpi_awareness_context: get_function!("user32.dll", SetProcessDpiAwarenessContext),
615 set_process_dpi_awareness: get_function!("shcore.dll", SetProcessDpiAwareness),
616 set_process_dpi_aware: get_function!("user32.dll", SetProcessDPIAware)
617 }
618 }
619
620 fn become_dpi_aware(&self) {
621 unsafe {
622 if let Some(set_process_dpi_awareness_context) = self.set_process_dpi_awareness_context {
623 if set_process_dpi_awareness_context(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2) == FALSE {
625 let _ = set_process_dpi_awareness_context(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE);
628 }
629 }
630 else if let Some(set_process_dpi_awareness) = self.set_process_dpi_awareness {
631 set_process_dpi_awareness(PROCESS_PER_MONITOR_DPI_AWARE).unwrap();
633 }
634 else if let Some(set_process_dpi_aware) = self.set_process_dpi_aware {
635 set_process_dpi_aware().unwrap();
637 }
638 }
639 }
640
641 pub fn enable_non_client_dpi_scaling(&self, hwnd: HWND) {
642 unsafe {
643 if let Some(enable_nonclient_dpi_scaling) = self.enable_nonclient_dpi_scaling {
644 let _ = enable_nonclient_dpi_scaling(hwnd);
645 }
646 }
647 }
648 pub fn hwnd_dpi_factor(&self, hwnd: HWND) -> f32 {
667 unsafe {
668 let hdc = GetDC(hwnd);
669 if hdc.is_invalid() {
670 panic!("`GetDC` returned null!");
671 }
672 let dpi = if let Some(get_dpi_for_window) = self.get_dpi_for_window {
673 match get_dpi_for_window(hwnd) {
675 0 => BASE_DPI, dpi => dpi as u32,
677 }
678 }
679 else if let Some(get_dpi_for_monitor) = self.get_dpi_for_monitor {
680 let monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
682 if monitor.is_invalid() {
683 BASE_DPI
684 }
685 else {
686 let mut dpi_x = 0;
687 let mut dpi_y = 0;
688 if get_dpi_for_monitor(monitor, MDT_EFFECTIVE_DPI, &mut dpi_x, &mut dpi_y) == S_OK {
689 dpi_x as u32
690 } else {
691 BASE_DPI
692 }
693 }
694 }
695 else {
696 if IsProcessDPIAware() == TRUE {
698 GetDeviceCaps(hdc, LOGPIXELSX) as u32
701 } else {
702 BASE_DPI
706 }
707 };
708 dpi as f32 / BASE_DPI as f32
709 }
710 }
711
712}