1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
#![deny(missing_docs)]

//! # Fake-DPI
//! A window wrapper that simulates fake Hi-DPI screen by manipulating window events.
//!
//! Used to test application logic on computers without Hi-DPI screen support.

extern crate window;
extern crate input;

use std::time::Duration;
use std::error::Error;

use window::{
    AdvancedWindow,
    BuildFromWindowSettings,
    Position,
    Window,
    WindowSettings,
    Size,
};
use input::{Input, Event};

/// Wraps a window to simulate Hi-DPI screen.
pub struct FakeDpiWindow<W> {
    /// The inner window.
    pub inner: W,
    /// Controls DPI factor.
    ///
    /// This can be changed at run-time to test application logic.
    /// By default, this is set to `2.0`.
    pub dpi: f64,
}

impl<W: BuildFromWindowSettings> BuildFromWindowSettings for FakeDpiWindow<W> {
    fn build_from_window_settings(
        settings: &WindowSettings
    ) ->  Result<Self, Box<dyn Error + 'static>> {
        let dpi = 2.0;
        let mut settings = settings.clone();
        let size = settings.get_size();
        settings.set_size(Size {
            width: size.width * dpi,
            height: size.height * dpi,
        });
        Ok(FakeDpiWindow {
            inner: settings.build()?,
            dpi,
        })
    }
}

impl<W: Window> Window for FakeDpiWindow<W> {
    fn set_should_close(&mut self, val: bool) {self.inner.set_should_close(val)}
    fn should_close(&self) -> bool {self.inner.should_close()}
    fn size(&self) -> Size {
        let size = self.inner.size();
        Size {width: size.width / self.dpi, height: size.height / self.dpi}
    }
    fn swap_buffers(&mut self) {self.inner.swap_buffers()}
    fn wait_event(&mut self) -> Event {
        map_input(self.dpi, self.inner.wait_event())
    }
    fn wait_event_timeout(&mut self, val: Duration) -> Option<Event> {
        self.inner.wait_event_timeout(val).map(|e| map_input(self.dpi, e))
    }
    fn poll_event(&mut self) -> Option<Event> {
        self.inner.poll_event().map(|e| map_input(self.dpi, e))
    }
    fn draw_size(&self) -> Size {self.inner.draw_size()}
}

impl<W: AdvancedWindow> AdvancedWindow for FakeDpiWindow<W> {
    fn get_title(&self) -> String {self.inner.get_title()}
    fn set_title(&mut self, val: String) {self.inner.set_title(val)}
    fn get_exit_on_esc(&self) -> bool {self.inner.get_exit_on_esc()}
    fn set_exit_on_esc(&mut self, val: bool) {self.inner.set_exit_on_esc(val)}
    fn get_automatic_close(&self) -> bool {self.inner.get_automatic_close()}
    fn set_automatic_close(&mut self, val: bool) {self.inner.set_automatic_close(val)}
    fn set_capture_cursor(&mut self, val: bool) {self.inner.set_capture_cursor(val)}
    fn show(&mut self) {self.inner.show()}
    fn hide(&mut self) {self.inner.hide()}
    fn get_position(&self) -> Option<Position> {self.inner.get_position()}
    fn set_position<P: Into<Position>>(&mut self, val: P) {self.inner.set_position(val)}
    fn set_size<S: Into<Size>>(&mut self, val: S) {self.inner.set_size(val)}
}

fn map_input(dpi: f64, e: Event) -> Event {
    use Input::*;
    use input::Motion::*;
    use input::ResizeArgs;

    if let Event::Input(e, ts) = e {
        Event::Input(match e {
            Focus(_) | Cursor(_) | Move(Touch(_)) | Move(ControllerAxis(_)) | Button(_) | Text(_) | FileDrag(_) | Close(_) => e,
            Move(MouseCursor(pos)) => Move(MouseCursor([pos[0] / dpi, pos[1] / dpi])),
            Move(MouseRelative(pos)) => Move(MouseRelative([pos[0] / dpi, pos[1] / dpi])),
            Move(MouseScroll(pos)) => Move(MouseScroll([pos[0] / dpi, pos[1] / dpi])),
            Resize(args) => Resize(ResizeArgs {
                draw_size: args.draw_size,
                window_size: [args.window_size[0] / dpi, args.window_size[1] / dpi],
            })
        }, ts)
    } else {
        e
    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
        assert_eq!(2 + 2, 4);
    }
}