druid_win_shell/
window.rs

1// Copyright 2018 The xi-editor Authors.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Creation and management of windows.
16
17#![allow(non_snake_case)]
18
19use std::any::Any;
20use std::cell::{Cell, RefCell};
21use std::ffi::OsString;
22use std::mem;
23use std::ptr::{null, null_mut};
24use std::rc::{Rc, Weak};
25use std::sync::{Arc, Mutex};
26
27use winapi::Interface;
28use winapi::ctypes::{c_int, c_void};
29use winapi::shared::basetsd::*;
30use winapi::shared::dxgi::*;
31use winapi::shared::dxgi1_2::*;
32use winapi::shared::dxgitype::*;
33use winapi::shared::dxgiformat::*;
34use winapi::shared::minwindef::*;
35use winapi::shared::windef::*;
36use winapi::shared::winerror::*;
37use winapi::um::d2d1::*;
38use winapi::um::unknwnbase::*;
39use winapi::um::wingdi::*;
40use winapi::um::winnt::*;
41use winapi::um::winuser::*;
42
43use direct2d;
44use direct2d::math::SizeU;
45use direct2d::render_target::{GenericRenderTarget, HwndRenderTarget, RenderTarget};
46
47use Error;
48use dialog::{FileDialogOptions, FileDialogType, get_file_dialog_path};
49use dcomp::{D3D11Device, DCompositionDevice, DCompositionTarget, DCompositionVisual};
50use menu::Menu;
51use paint::{self, PaintCtx};
52use util::{OPTIONAL_FUNCTIONS, as_result, FromWide, ToWide};
53
54extern "system" {
55    pub fn DwmFlush();
56}
57
58/// Builder abstraction for creating new windows.
59pub struct WindowBuilder {
60    handler: Option<Box<WinHandler>>,
61    dwStyle: DWORD,
62    title: String,
63    cursor: Cursor,
64    menu: Option<Menu>,
65    present_strategy: PresentStrategy,
66}
67
68#[derive(Clone, Copy, PartialEq, Eq, Debug)]
69/// It's very tricky to get smooth dynamics (especially resizing) and
70/// good performance on Windows. This setting lets clients experiment
71/// with different strategies.
72pub enum PresentStrategy {
73    /// Don't try to use DXGI at all, only create Hwnd render targets.
74    /// Note: on Windows 7 this is the only mode available.
75    Hwnd,
76
77    /// Corresponds to the swap effect DXGI_SWAP_EFFECT_SEQUENTIAL. In
78    /// testing, it causes diagonal banding artifacts with Nvidia
79    /// adapters, and incremental present doesn't work. However, it
80    /// is compatible with GDI (such as menus).
81    Sequential,
82
83    /// Corresponds to the swap effect DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL.
84    /// In testing, it seems to perform well (including allowing smooth
85    /// resizing when the frame can be rendered quickly), but isn't
86    /// compatible with GDI.
87    Flip,
88
89    /// Corresponds to the swap effect DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL
90    /// but with a redirection surface for GDI compatibility. Resize is
91    /// very laggy and artifacty.
92    FlipRedirect,
93}
94
95#[derive(Clone, Default)]
96pub struct WindowHandle(Weak<WindowState>);
97
98/// A handle that can get used to schedule an idle handler. Note that
99/// this handle is thread safe. If the handle is used after the hwnd
100/// has been destroyed, probably not much will go wrong (the XI_RUN_IDLE
101/// message may be sent to a stray window).
102#[derive(Clone)]
103pub struct IdleHandle {
104    pub(crate) hwnd: HWND,
105    queue: Arc<Mutex<Vec<Box<IdleCallback>>>>,
106}
107
108trait IdleCallback: Send {
109    fn call(self: Box<Self>, a: &Any);
110}
111
112impl<F: FnOnce(&Any) + Send> IdleCallback for F {
113    fn call(self: Box<F>, a: &Any) {
114        (*self)(a)
115    }
116}
117
118struct WindowState {
119    hwnd: Cell<HWND>,
120    dpi: Cell<f32>,
121    wndproc: Box<WndProc>,
122    idle_queue: Arc<Mutex<Vec<Box<IdleCallback>>>>,
123}
124
125/// App behavior, supplied by the app.
126///
127/// Many of the "window procedure" messages map to calls to this trait.
128/// The methods are non-mut because the window procedure can be called
129/// recursively; implementers are expected to use `RefCell` or the like,
130/// but should be careful to keep the lifetime of the borrow short.
131pub trait WinHandler {
132    /// Provide the handler with a handle to the window so that it can
133    /// invalidate or make other requests.
134    fn connect(&self, handle: &WindowHandle);
135
136    /// Called when the size of the window is changed. Note that size
137    /// is in physical pixels.
138    #[allow(unused_variables)]
139    fn size(&self, width: u32, height: u32) {}
140
141    /// Request the handler to paint the window contents. Return value
142    /// indicates whether window is animating, i.e. whether another paint
143    /// should be scheduled for the next animation frame.
144    fn paint(&self, ctx: &mut PaintCtx) -> bool;
145
146    /// Called when the resources need to be rebuilt.
147    fn rebuild_resources(&self) {}
148
149    #[allow(unused_variables)]
150    /// Called when a menu item is selected.
151    fn command(&self, id: u32) {}
152
153    /// Called on keyboard input of a single character. This corresponds
154    /// to the WM_CHAR message. Handling of text input will continue to
155    /// evolve, we need to handle input methods and more.
156    ///
157    /// The modifiers are a combination of `M_ALT`, `M_CTRL`, `M_SHIFT`.
158    #[allow(unused_variables)]
159    fn char(&self, ch: u32, mods: u32) {}
160
161    /// Called on a key down event. This corresponds to the WM_KEYDOWN
162    /// message. The key code is as WM_KEYDOWN. We'll want to add stuff
163    /// like the modifier state.
164    ///
165    /// The modifiers are a combination of `M_ALT`, `M_CTRL`, `M_SHIFT`.
166    ///
167    /// Return `true` if the event is handled.
168    #[allow(unused_variables)]
169    fn keydown(&self, vkey_code: i32, mods: u32) -> bool { false }
170
171    /// Called on a mouse wheel event. This corresponds to a
172    /// [WM_MOUSEWHEEL](https://msdn.microsoft.com/en-us/library/windows/desktop/ms645617(v=vs.85).aspx)
173    /// message.
174    ///
175    /// The modifiers are the same as WM_MOUSEWHEEL.
176    #[allow(unused_variables)]
177    fn mouse_wheel(&self, delta: i32, mods: u32) {}
178
179    /// Called on a mouse horizontal wheel event. This corresponds to a
180    /// [WM_MOUSEHWHEEL](https://msdn.microsoft.com/en-us/library/windows/desktop/ms645614(v=vs.85).aspx)
181    /// message.
182    ///
183    /// The modifiers are the same as WM_MOUSEHWHEEL.
184    #[allow(unused_variables)]
185    fn mouse_hwheel(&self, delta: i32, mods: u32) {}
186
187    /// Called when the mouse moves. Note that the x, y coordinates are
188    /// in absolute pixels.
189    ///
190    /// TODO: should we reuse the MouseEvent struct for this method as well?
191    #[allow(unused_variables)]
192    fn mouse_move(&self, x: i32, y: i32, mods: u32) {}
193
194    /// Called on mouse button up or down. Note that the x, y
195    /// coordinates are in absolute pixels.
196    #[allow(unused_variables)]
197    fn mouse(&self, event: &MouseEvent) {}
198
199    /// Called when the window is being destroyed. Note that this happens
200    /// earlier in the sequence than drop (at WM_DESTROY, while the latter is
201    /// WM_NCDESTROY).
202    fn destroy(&self) {}
203
204    /// Get a reference to the handler state. Used mostly by idle handlers.
205    fn as_any(&self) -> &Any;
206}
207
208/// A mouse button press or release event.
209#[derive(Debug)]
210pub struct MouseEvent {
211    /// X coordinate in absolute pixels.
212    pub x: i32,
213    /// Y coordinate in absolute pixels.
214    pub y: i32,
215    /// Modifiers, as in raw WM message
216    pub mods: u32,
217    /// Which button was pressed or released.
218    pub which: MouseButton,
219    /// Type of event.
220    pub ty: MouseType,
221}
222
223/// An indicator of which mouse button was pressed.
224#[derive(PartialEq, Eq, Clone, Copy, Debug)]
225pub enum MouseButton {
226    /// Left mouse button.
227    Left,
228    /// Middle mouse button.
229    Middle,
230    /// Right mouse button.
231    Right,
232    /// First X button.
233    X1,
234    /// Second X button.
235    X2,
236}
237
238/// An indicator of the state change of a mouse button.
239#[derive(PartialEq, Eq, Clone, Copy, Debug)]
240pub enum MouseType {
241    /// Mouse down event.
242    Down,
243    /// Note: DoubleClick is currently disabled, as we don't use the
244    /// Windows processing.
245    DoubleClick,
246    /// Mouse up event.
247    Up,
248}
249
250/// Standard cursor types. This is only a subset, others can be added as needed.
251pub enum Cursor {
252    Arrow,
253    IBeam,
254}
255
256/// Generic handler trait for the winapi window procedure entry point.
257trait WndProc {
258    fn connect(&self, handle: &WindowHandle, state: WndState);
259
260    fn window_proc(&self, hwnd: HWND, msg: UINT, wparam: WPARAM, lparam: LPARAM)
261        -> Option<LRESULT>;
262}
263
264/// Modifier mask for alt key in `keydown` and `char` events.
265pub const M_ALT: u32 = 1;
266
267/// Modifier mask for control key in `keydown` and `char` events.
268pub const M_CTRL: u32 = 2;
269
270/// Modifier mask for shift key in `keydown` and `char` events.
271pub const M_SHIFT: u32 = 4;
272
273// State and logic for the winapi window procedure entry point. Note that this level
274// implements policies such as the use of Direct2D for painting.
275struct MyWndProc {
276    handler: Box<WinHandler>,
277    handle: RefCell<WindowHandle>,
278    d2d_factory: direct2d::Factory,
279    state: RefCell<Option<WndState>>,
280}
281
282struct WndState {
283    render_target: Option<GenericRenderTarget>,
284    dcomp_state: Option<DCompState>,
285    dpi: f32,
286}
287
288/// State for DirectComposition. This is optional because it is only supported
289/// on 8.1 and up.
290struct DCompState {
291    swap_chain: *mut IDXGISwapChain1,
292    dcomp_device: DCompositionDevice,
293    dcomp_target: DCompositionTarget,
294    swapchain_visual: DCompositionVisual,
295    // True if in a drag-resizing gesture (at which point the swapchain is disabled)
296    sizing: bool,
297}
298
299/// Message indicating there are idle tasks to run.
300const XI_RUN_IDLE: UINT = WM_USER;
301
302impl Default for PresentStrategy {
303    fn default() -> PresentStrategy {
304        // We probably want to change this, but we need GDI to work. Too bad about
305        // the artifacty resizing.
306        PresentStrategy::FlipRedirect
307    }
308}
309
310fn get_mod_state(lparam: LPARAM) -> u32 {
311    unsafe {
312        let mut mod_state = 0;
313        if (lparam & (1 << 29)) != 0 { mod_state |= MOD_ALT as u32; }
314        if GetKeyState(VK_CONTROL) < 0 { mod_state |= MOD_CONTROL as u32; }
315        if GetKeyState(VK_SHIFT) < 0 { mod_state |= MOD_SHIFT as u32; }
316        mod_state
317    }
318}
319
320impl MyWndProc {
321    fn rebuild_render_target(&self) {
322        unsafe {
323            let mut state = self.state.borrow_mut();
324            let s = state.as_mut().unwrap();
325            let swap_chain = s.dcomp_state.as_ref().unwrap().swap_chain;
326            let rt = paint::create_render_target_dxgi(&self.d2d_factory, swap_chain, s.dpi)
327                .map(|rt| rt.as_generic());
328            s.render_target = rt.ok();
329        }
330    }
331
332    // Renders but does not present.
333    fn render(&self) {
334        let mut state = self.state.borrow_mut();
335        let s = state.as_mut().unwrap();
336        let rt = s.render_target.as_mut().unwrap();
337        rt.begin_draw();
338        let anim = self.handler.paint(&mut PaintCtx {
339            d2d_factory: &self.d2d_factory,
340            render_target: rt,
341        });
342        // Maybe should deal with lost device here...
343        let res = rt.end_draw();
344        if let Err(e) = res {
345            println!("EndDraw error: {:?}", e);
346        }
347        if anim {
348            let handle = self.handle.borrow().get_idle_handle().unwrap();
349            // Note: maybe add WindowHandle as arg to idle handler so we don't need this.
350            let handle2 = handle.clone();
351            handle.add_idle(move |_| handle2.invalidate());
352        }
353    }
354}
355
356impl WndProc for MyWndProc {
357    fn connect(&self, handle: &WindowHandle, state: WndState) {
358        *self.handle.borrow_mut() = handle.clone();
359        self.handler.connect(handle);
360        *self.state.borrow_mut() = Some(state);
361    }
362
363    fn window_proc(&self, hwnd: HWND, msg: UINT, wparam: WPARAM, lparam: LPARAM)
364        -> Option<LRESULT>
365    {
366        //println!("wndproc msg: {}", msg);
367        match msg {
368            WM_ERASEBKGND => {
369                Some(0)
370            }
371            WM_PAINT => unsafe {
372                if self.state.borrow().as_ref().unwrap().render_target.is_none() {
373                    let rt = paint::create_render_target(&self.d2d_factory, hwnd)
374                        .map(|rt| rt.as_generic());
375                    self.state.borrow_mut().as_mut().unwrap().render_target = rt.ok();
376                }
377                self.render();
378                let mut state = self.state.borrow_mut();
379                let s = state.as_mut().unwrap();
380                if let Some(ref mut ds) = s.dcomp_state {
381                    if !ds.sizing {
382                        (*ds.swap_chain).Present(1, 0);
383                        let _ = ds.dcomp_device.commit();
384                    }
385                }
386                ValidateRect(hwnd, null_mut());
387                Some(0)
388            },
389            WM_ENTERSIZEMOVE => unsafe {
390                if self.state.borrow().as_ref().unwrap().dcomp_state.is_some() {
391                    let rt = paint::create_render_target(&self.d2d_factory, hwnd)
392                        .map(|rt| rt.as_generic());
393                    self.state.borrow_mut().as_mut().unwrap().render_target = rt.ok();
394                    self.handler.rebuild_resources();
395                    self.render();
396
397                    let mut state = self.state.borrow_mut();
398                    let s = state.as_mut().unwrap();
399                    if let Some(ref mut ds) = s.dcomp_state {
400                        let _ = ds.dcomp_target.clear_root();
401                        let _ = ds.dcomp_device.commit();
402                        ds.sizing = true;
403                    }
404                }
405                None
406            }
407            WM_EXITSIZEMOVE => unsafe {
408                if self.state.borrow().as_ref().unwrap().dcomp_state.is_some() {
409                    let mut rect: RECT = mem::uninitialized();
410                    GetClientRect(hwnd, &mut rect);
411                    let width = (rect.right - rect.left) as u32;
412                    let height = (rect.bottom - rect.top) as u32;
413                    let res = (*self.state.borrow_mut().as_mut().unwrap()
414                        .dcomp_state.as_mut().unwrap().swap_chain)
415                        .ResizeBuffers(2, width, height, DXGI_FORMAT_UNKNOWN, 0);
416                    if SUCCEEDED(res) {
417                        self.handler.rebuild_resources();
418                        self.rebuild_render_target();
419                        self.render();
420                        let mut state = self.state.borrow_mut();
421                        let mut s = state.as_mut().unwrap();
422                        (*s.dcomp_state.as_ref().unwrap().swap_chain).Present(0, 0);
423                    } else {
424                        println!("ResizeBuffers failed: 0x{:x}", res);
425                    }
426
427                    // Flush to present flicker artifact (old swapchain composited)
428                    // It might actually be better to create a new swapchain here.
429                    DwmFlush();
430
431                    let mut state = self.state.borrow_mut();
432                    let mut s = state.as_mut().unwrap();
433                    if let Some(ref mut ds) = s.dcomp_state {
434                        let _ = ds.dcomp_target.set_root(&mut ds.swapchain_visual);
435                        let _ = ds.dcomp_device.commit();
436                        ds.sizing = false;
437                    }
438                }
439                None
440            }
441            WM_SIZE => unsafe {
442                let width = LOWORD(lparam as u32) as u32;
443                let height = HIWORD(lparam as u32) as u32;
444                self.handler.size(width, height);
445                let use_hwnd = if let Some(ref dcomp_state) =
446                    self.state.borrow().as_ref().unwrap().dcomp_state
447                {
448                    dcomp_state.sizing
449                } else {
450                    true
451                };
452                if use_hwnd {
453                    let mut state = self.state.borrow_mut();
454                    let mut s = state.as_mut().unwrap();
455                    if let Some(ref mut rt) = s.render_target {
456                        if let Some(hrt) = cast_to_hwnd(rt) {
457                            let width = LOWORD(lparam as u32) as u32;
458                            let height = HIWORD(lparam as u32) as u32;
459                            let size = SizeU(D2D1_SIZE_U { width, height });
460                            let _ = hrt.resize(size);
461                        }
462                    }
463                    InvalidateRect(hwnd, null_mut(), FALSE);
464                } else {
465                    let res;
466                    {
467                        let mut state = self.state.borrow_mut();
468                        let mut s = state.as_mut().unwrap();
469                        s.render_target = None;
470                        res = (*s.dcomp_state.as_mut().unwrap().swap_chain)
471                            .ResizeBuffers(0, width, height, DXGI_FORMAT_UNKNOWN, 0);
472                    }
473                    if SUCCEEDED(res) {
474                        self.rebuild_render_target();
475                        self.render();
476                        let mut state = self.state.borrow_mut();
477                        let mut s = state.as_mut().unwrap();
478                        if let Some(ref mut dcomp_state) = s.dcomp_state {
479                            (*dcomp_state.swap_chain).Present(0, 0);
480                            let _ = dcomp_state.dcomp_device.commit();
481                        }
482                        ValidateRect(hwnd, null_mut());
483                    } else {
484                        println!("ResizeBuffers failed: 0x{:x}", res);
485                    }
486                }
487                Some(0)
488            },
489            WM_COMMAND => {
490                self.handler.command(LOWORD(wparam as u32) as u32);
491                Some(0)
492            }
493            WM_CHAR => {
494                let mods = get_mod_state(lparam);
495                self.handler.char(wparam as u32, mods);
496                Some(0)
497            }
498            WM_KEYDOWN | WM_SYSKEYDOWN => {
499                let mods = get_mod_state(lparam);
500                let handled = self.handler.keydown(wparam as i32, mods);
501                if handled {
502                    Some(0)
503                } else {
504                    None
505                }
506            }
507            WM_MOUSEWHEEL => {
508                let delta = HIWORD(wparam as u32) as i16 as i32;
509                let mods = LOWORD(wparam as u32) as u32;
510                self.handler.mouse_wheel(delta, mods);
511                Some(0)
512            }
513            WM_MOUSEHWHEEL => {
514                let delta = HIWORD(wparam as u32) as i16 as i32;
515                let mods = LOWORD(wparam as u32) as u32;
516                self.handler.mouse_hwheel(delta, mods);
517                Some(0)
518            }
519            WM_MOUSEMOVE => {
520                let x = LOWORD(lparam as u32) as i16 as i32;
521                let y = HIWORD(lparam as u32) as i16 as i32;
522                let mods = LOWORD(wparam as u32) as u32;
523                self.handler.mouse_move(x, y, mods);
524                Some(0)
525            }
526            // TODO: not clear where double-click processing should happen. Currently disabled
527            // because CS_DBLCLKS is not set
528            WM_LBUTTONDBLCLK | WM_LBUTTONDOWN | WM_LBUTTONUP |
529                WM_MBUTTONDBLCLK | WM_MBUTTONDOWN | WM_MBUTTONUP |
530                WM_RBUTTONDBLCLK | WM_RBUTTONDOWN | WM_RBUTTONUP |
531                WM_XBUTTONDBLCLK | WM_XBUTTONDOWN | WM_XBUTTONUP =>
532            {
533                let which = match msg {
534                    WM_LBUTTONDBLCLK | WM_LBUTTONDOWN | WM_LBUTTONUP => MouseButton::Left,
535                    WM_MBUTTONDBLCLK | WM_MBUTTONDOWN | WM_MBUTTONUP => MouseButton::Middle,
536                    WM_RBUTTONDBLCLK | WM_RBUTTONDOWN | WM_RBUTTONUP => MouseButton::Right,
537                    WM_XBUTTONDBLCLK | WM_XBUTTONDOWN | WM_XBUTTONUP =>
538                        match HIWORD(wparam as u32) {
539                            1 => MouseButton::X1,
540                            2 => MouseButton::X2,
541                            _ => {
542                                println!("unexpected X button event");
543                                return None;
544                            }
545                        },
546                    _ => unreachable!()
547                };
548                let ty = match msg {
549                    WM_LBUTTONDOWN | WM_MBUTTONDOWN | WM_RBUTTONDOWN | WM_XBUTTONDOWN =>
550                        MouseType::Down,
551                    WM_LBUTTONDBLCLK | WM_MBUTTONDBLCLK | WM_RBUTTONDBLCLK | WM_XBUTTONDBLCLK =>
552                        MouseType::DoubleClick,
553                    WM_LBUTTONUP | WM_MBUTTONUP | WM_RBUTTONUP | WM_XBUTTONUP =>
554                        MouseType::Up,
555                    _ => unreachable!(),
556                };
557                let x = LOWORD(lparam as u32) as i16 as i32;
558                let y = HIWORD(lparam as u32) as i16 as i32;
559                let mods = LOWORD(wparam as u32) as u32;
560                let event = MouseEvent { x, y, mods, which, ty };
561                self.handler.mouse(&event);
562                Some(0)
563            }
564            WM_DESTROY => {
565                self.handler.destroy();
566                None
567            }
568            XI_RUN_IDLE => {
569                let queue = self.handle.borrow().take_idle_queue();
570                let handler_as_any = self.handler.as_any();
571                for callback in queue {
572                    callback.call(handler_as_any);
573                }
574                Some(0)
575            }
576            _ => None
577        }
578    }
579}
580
581impl WindowBuilder {
582    pub fn new() -> WindowBuilder {
583        WindowBuilder {
584            handler: None,
585            dwStyle: WS_OVERLAPPEDWINDOW,
586            title: String::new(),
587            cursor: Cursor::Arrow,
588            menu: None,
589            present_strategy: Default::default(),
590        }
591    }
592
593    /// This takes ownership, and is typically used with UiMain
594    pub fn set_handler(&mut self, handler: Box<WinHandler>) {
595        self.handler = Some(handler);
596    }
597
598    pub fn set_scroll(&mut self, hscroll: bool, vscroll: bool) {
599        self.dwStyle &= !(WS_HSCROLL | WS_VSCROLL);
600        if hscroll {
601            self.dwStyle |= WS_HSCROLL;
602        }
603        if vscroll {
604            self.dwStyle |= WS_VSCROLL;
605        }
606    }
607
608    pub fn set_title<S: Into<String>>(&mut self, title: S) {
609        self.title = title.into();
610    }
611
612    /// Set the default cursor for the window.
613    pub fn set_cursor(&mut self, cursor: Cursor) {
614        self.cursor = cursor;
615    }
616
617    pub fn set_menu(&mut self, menu: Menu) {
618        self.menu = Some(menu);
619    }
620
621    pub fn set_present_strategy(&mut self, present_strategy: PresentStrategy) {
622        self.present_strategy = present_strategy;
623    }
624
625    pub fn build(self)
626        -> Result<WindowHandle, Error>
627    {
628        unsafe {
629            // Maybe separate registration in build api? Probably only need to
630            // register once even for multiple window creation.
631
632            // TODO: probably want configurable class name.
633            let class_name = "Xi Editor".to_wide();
634            let icon = LoadIconW(0 as HINSTANCE, IDI_APPLICATION);
635            let cursor = LoadCursorW(0 as HINSTANCE, self.cursor.get_lpcwstr());
636            let brush = CreateSolidBrush(0xffffff);
637            let wnd = WNDCLASSW {
638                style: 0,
639                lpfnWndProc: Some(win_proc_dispatch),
640                cbClsExtra: 0,
641                cbWndExtra: 0,
642                hInstance: 0 as HINSTANCE,
643                hIcon: icon,
644                hCursor: cursor,
645                hbrBackground: brush,
646                lpszMenuName: 0 as LPCWSTR,
647                lpszClassName: class_name.as_ptr(),
648            };
649            let class_atom = RegisterClassW(&wnd);
650            if class_atom == 0 {
651                return Err(Error::Null);
652            }
653
654            let wndproc = MyWndProc {
655                handler: self.handler.unwrap(),
656                handle: Default::default(),
657                d2d_factory: direct2d::Factory::new().unwrap(),
658                state: RefCell::new(None),
659            };
660
661            let window = WindowState {
662                hwnd: Cell::new(0 as HWND),
663                dpi: Cell::new(0.0),
664                wndproc: Box::new(wndproc),
665                idle_queue: Default::default(),
666            };
667            let win = Rc::new(window);
668            let handle = WindowHandle(Rc::downgrade(&win));
669
670            // Simple scaling based on System Dpi (96 is equivalent to 100%)
671            let dpi = if let Some(func) = OPTIONAL_FUNCTIONS.GetDpiForSystem {
672                // Only supported on windows 10
673                func() as f32
674            } else {
675                // TODO GetDpiForMonitor is supported on windows 8.1, try falling back to that here
676                // Probably GetDeviceCaps(..., LOGPIXELSX) is the best to do pre-10
677                96.0
678            };
679            win.dpi.set(dpi);
680            let width = (500.0 * (dpi/96.0)) as i32;
681            let height = (400.0 * (dpi/96.0)) as i32;
682
683            let hmenu = match self.menu {
684                Some(menu) => menu.into_hmenu(),
685                None => 0 as HMENU,
686            };
687            let mut dwExStyle = 0;
688            if self.present_strategy == PresentStrategy::Flip {
689                dwExStyle |= WS_EX_NOREDIRECTIONBITMAP;
690            }
691            let hwnd = create_window(dwExStyle, class_name.as_ptr(),
692                self.title.to_wide().as_ptr(), self.dwStyle,
693                CW_USEDEFAULT, CW_USEDEFAULT, width, height, 0 as HWND, hmenu, 0 as HINSTANCE,
694                win.clone());
695            if hwnd.is_null() {
696                return Err(Error::Null);
697            }
698
699            let dcomp_state = create_dcomp_state(self.present_strategy, hwnd)
700                .unwrap_or_else(|e| {
701                    println!("Error creating swapchain, falling back to hwnd: {:?}", e);
702                    None
703                });
704
705            win.hwnd.set(hwnd);
706            let state = WndState {
707                render_target: None,
708                dcomp_state,
709                dpi,
710            };
711            win.wndproc.connect(&handle, state);
712            mem::drop(win);
713            Ok(handle)
714        }
715    }
716}
717
718/// Choose an adapter. Here the heuristic is to choose the adapter with the
719/// largest video memory, which will generally be the discrete adapter. It's
720/// possible that on some systems the integrated adapter might be a better
721/// choice, but that probably depends on usage.
722unsafe fn choose_adapter(factory: *mut IDXGIFactory2) -> *mut IDXGIAdapter {
723    let mut i = 0;
724    let mut best_adapter = null_mut();
725    let mut best_vram = 0;
726    loop {
727        let mut adapter: *mut IDXGIAdapter = null_mut();
728        if !SUCCEEDED((*factory).EnumAdapters(i, &mut adapter)) {
729            break;
730        }
731        let mut desc: DXGI_ADAPTER_DESC = mem::uninitialized();
732        (*adapter).GetDesc(&mut desc);
733        let vram = desc.DedicatedVideoMemory;
734        if i == 0 || vram > best_vram {
735            best_vram = vram;
736            best_adapter = adapter;
737        }
738        println!("{:?}: desc = {:?}, vram = {}",
739            adapter,
740            (&mut desc.Description[0] as LPWSTR).from_wide(),
741            desc.DedicatedVideoMemory);
742        i += 1;
743    }
744    best_adapter
745}
746
747unsafe fn create_dcomp_state(present_strategy: PresentStrategy, hwnd: HWND)
748    -> Result<Option<DCompState>, Error>
749{
750    if present_strategy == PresentStrategy::Hwnd {
751        return Ok(None);
752    }
753    if let Some(create_dxgi_factory2) = OPTIONAL_FUNCTIONS.CreateDXGIFactory2 {
754
755        let mut factory: *mut IDXGIFactory2 = null_mut();
756        as_result(create_dxgi_factory2(0, &IID_IDXGIFactory2,
757            &mut factory as *mut *mut IDXGIFactory2 as *mut *mut c_void))?;
758        println!("dxgi factory pointer = {:?}", factory);
759        let adapter = choose_adapter(factory);
760        println!("adapter = {:?}", adapter);
761
762        let mut d3d11_device = D3D11Device::new_simple()?;
763        let mut d2d1_device = d3d11_device.create_d2d1_device()?;
764        let mut dcomp_device = d2d1_device.create_composition_device()?;
765        let mut dcomp_target = dcomp_device.create_target_for_hwnd(hwnd, true)?;
766
767        let (swap_effect, bufs) = match present_strategy {
768            PresentStrategy::Hwnd => unreachable!(),
769            PresentStrategy::Sequential => (DXGI_SWAP_EFFECT_SEQUENTIAL, 1),
770            PresentStrategy::Flip | PresentStrategy::FlipRedirect =>
771                (DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL, 2),
772        };
773        let desc = DXGI_SWAP_CHAIN_DESC1 {
774            Width: 1024,
775            Height: 768,
776            Format: DXGI_FORMAT_B8G8R8A8_UNORM,
777            Stereo: FALSE,
778            SampleDesc: DXGI_SAMPLE_DESC { Count: 1, Quality: 0 },
779            BufferUsage: DXGI_USAGE_RENDER_TARGET_OUTPUT,
780            BufferCount: bufs,
781            Scaling: DXGI_SCALING_STRETCH,
782            SwapEffect: swap_effect,
783            AlphaMode: DXGI_ALPHA_MODE_IGNORE,
784            Flags: 0,
785        };
786        let mut swap_chain: *mut IDXGISwapChain1 = null_mut();
787        let res = (*factory).CreateSwapChainForComposition(d3d11_device.raw_ptr() as *mut IUnknown,
788            &desc, null_mut(), &mut swap_chain);
789        println!("swap chain res = 0x{:x}, pointer = {:?}", res, swap_chain);
790
791        let mut swapchain_visual = dcomp_device.create_visual()?;
792        swapchain_visual.set_content_raw(swap_chain as *mut IUnknown)?;
793        dcomp_target.set_root(&mut swapchain_visual)?;
794        Ok(Some(DCompState {
795            swap_chain, dcomp_device, dcomp_target, swapchain_visual,
796            sizing: false,
797        }))
798    } else {
799        Ok(None)
800    }
801}
802
803unsafe extern "system" fn win_proc_dispatch(hwnd: HWND, msg: UINT, wparam: WPARAM, lparam: LPARAM)
804    -> LRESULT
805{
806    if msg == WM_CREATE {
807        let create_struct = &*(lparam as *const CREATESTRUCTW);
808        let wndproc_ptr = create_struct.lpCreateParams;
809        SetWindowLongPtrW(hwnd, GWLP_USERDATA, wndproc_ptr as LONG_PTR);
810    }
811    let window_ptr = GetWindowLongPtrW(hwnd, GWLP_USERDATA) as *const WindowState;
812    let result = {
813        if window_ptr.is_null() {
814            None
815        } else {
816            (*window_ptr).wndproc.window_proc(hwnd, msg, wparam, lparam)
817        }
818    };
819    if msg == WM_NCDESTROY {
820        if !window_ptr.is_null() {
821            SetWindowLongPtrW(hwnd, GWLP_USERDATA, 0);
822            mem::drop(Rc::from_raw(window_ptr));
823        }
824    }
825    match result {
826        Some(lresult) => lresult,
827        None => DefWindowProcW(hwnd, msg, wparam, lparam),
828    }
829}
830
831/// Create a window (same parameters as CreateWindowExW) with associated WndProc.
832unsafe fn create_window(
833        dwExStyle: DWORD, lpClassName: LPCWSTR, lpWindowName: LPCWSTR, dwStyle: DWORD, x: c_int,
834        y: c_int, nWidth: c_int, nHeight: c_int, hWndParent: HWND, hMenu: HMENU,
835        hInstance: HINSTANCE, wndproc: Rc<WindowState>) -> HWND
836{
837    CreateWindowExW(dwExStyle, lpClassName, lpWindowName, dwStyle, x, y,
838        nWidth, nHeight, hWndParent, hMenu, hInstance, Rc::into_raw(wndproc) as LPVOID)
839}
840
841impl Cursor {
842    fn get_lpcwstr(&self) -> LPCWSTR {
843        match self {
844            Cursor::Arrow => IDC_ARROW,
845            Cursor::IBeam => IDC_IBEAM,
846        }
847    }
848}
849
850impl WindowHandle {
851    pub fn show(&self) {
852        if let Some(w) = self.0.upgrade() {
853            let hwnd = w.hwnd.get();
854            unsafe {
855                ShowWindow(hwnd, SW_SHOWNORMAL);
856                UpdateWindow(hwnd);
857            }
858        }
859    }
860
861    pub fn close(&self) {
862        if let Some(w) = self.0.upgrade() {
863            let hwnd = w.hwnd.get();
864            unsafe {
865                DestroyWindow(hwnd);
866            }
867        }
868    }
869
870    pub fn invalidate(&self) {
871        if let Some(w) = self.0.upgrade() {
872            let hwnd = w.hwnd.get();
873            unsafe {
874                InvalidateRect(hwnd, null(), FALSE);
875            }
876        }
877    }
878
879    /// Get the raw HWND handle, for uses that are not wrapped in
880    /// druid_win_shell.
881    pub fn get_hwnd(&self) -> Option<HWND> {
882        self.0.upgrade().map(|w| w.hwnd.get())
883    }
884
885    pub fn file_dialog(&self, ty: FileDialogType, options: FileDialogOptions)
886        -> Result<OsString, Error>
887    {
888        let hwnd = self.get_hwnd().ok_or(Error::Null)?;
889        unsafe { get_file_dialog_path(hwnd, ty, options) }
890    }
891
892    /// Get a handle that can be used to schedule an idle task.
893    pub fn get_idle_handle(&self) -> Option<IdleHandle> {
894        self.0.upgrade().map(|w|
895            IdleHandle {
896                hwnd: w.hwnd.get(),
897                queue: w.idle_queue.clone(),
898            }
899        )
900    }
901
902    fn take_idle_queue(&self) -> Vec<Box<IdleCallback>> {
903        if let Some(w) = self.0.upgrade() {
904            mem::replace(&mut w.idle_queue.lock().unwrap(), Vec::new())
905        } else {
906            Vec::new()
907        }
908    }
909
910    /// Get the dpi of the window.
911    pub fn get_dpi(&self) -> f32 {
912        if let Some(w) = self.0.upgrade() {
913            w.dpi.get()
914        } else {
915            96.0
916        }
917    }
918
919    /// Convert a dimension in px units to physical pixels (rounding).
920    pub fn px_to_pixels(&self, x: f32) -> i32 {
921        (x * self.get_dpi() * (1.0 / 96.0)).round() as i32
922    }
923
924    /// Convert a point in px units to physical pixels (rounding).
925    pub fn px_to_pixels_xy(&self, x: f32, y: f32) -> (i32, i32) {
926        let scale = self.get_dpi() * (1.0 / 96.0);
927        ((x * scale).round() as i32, (y * scale).round() as i32)
928    }
929
930    /// Convert a dimension in physical pixels to px units.
931    pub fn pixels_to_px<T: Into<f64>>(&self, x: T) -> f32 {
932        (x.into() as f32) * 96.0 / self.get_dpi()
933    }
934
935    /// Convert a point in physical pixels to px units.
936    pub fn pixels_to_px_xy<T: Into<f64>>(&self, x: T, y: T) -> (f32, f32) {
937        let scale = 96.0 / self.get_dpi();
938        ((x.into() as f32) * scale, (y.into() as f32) * scale)
939    }
940}
941
942// There is a tiny risk of things going wrong when hwnd is sent across threads.
943unsafe impl Send for IdleHandle {}
944
945impl IdleHandle {
946    /// Add an idle handler, which is called (once) when the message loop
947    /// is empty. The idle handler will be run from the window's wndproc,
948    /// which means it won't be scheduled if the window is closed.
949    pub fn add_idle<F>(&self, callback: F)
950        where F: FnOnce(&Any) + Send + 'static
951    {
952        let mut queue = self.queue.lock().unwrap();
953        if queue.is_empty() {
954            unsafe {
955                PostMessageW(self.hwnd, XI_RUN_IDLE, 0, 0);
956            }
957        }
958        queue.push(Box::new(callback));
959    }
960
961    fn invalidate(&self) {
962        unsafe {
963            InvalidateRect(self.hwnd, null(), FALSE);
964        }        
965    }
966}
967
968/// Casts render target to hwnd variant.
969///
970/// TODO: investigate whether there's a better way to do this.
971unsafe fn cast_to_hwnd(rt: &GenericRenderTarget) -> Option<HwndRenderTarget> {
972    let raw_ptr = rt.clone().get_raw();
973    let mut hwnd = null_mut();
974    let err = (*raw_ptr).QueryInterface(&ID2D1HwndRenderTarget::uuidof(), &mut hwnd);
975    if SUCCEEDED(err) {
976        Some(HwndRenderTarget::from_raw(hwnd as *mut ID2D1HwndRenderTarget))
977    } else {
978        None
979    }
980}