1 /**
2  * Display
3  */
4 module d2d.Display;
5 
6 import std.algorithm;
7 import std.conv;
8 import std.datetime.stopwatch;
9 import core.thread;
10 import d2d;
11 import d2d.sdl2;
12 
13 /**
14  * A display that handles collecting events and drawing to the activity and handling window stuff
15  * Will handle the main loop and send events to where they need to be handled
16  */
17 class Display {
18 
19     int frameSleep = 1000 / 60; ///How long to wait between frames in milliseconds; will be ignored in case of VSync
20     bool isRunning; ///Whether the display is running; will stop running if set to false
21     Activity activity; ///The activity that the display is displaying right now
22     EventHandler[] eventHandlers; ///All event handlers of the display; define specific behaviours for events; events pass to handlers from first to last
23     private ulong _frames; ///How many frames have passed
24     private Keyboard _keyboard; ///The keyboard input source
25     private Mouse _mouse; ///The mouse input source
26     private Window _window; ///The actual SDL window
27     private Renderer _renderer; ///The renderer for the window
28 
29     /**
30      * Sets the window's framerate
31      */
32     @property void framerate(int fps) {
33         this.frameSleep = 1000 / fps;
34     }
35 
36     /**
37      * Gets the window's framerate
38      */
39     @property int framerate() {
40         return 1000 / this.frameSleep;
41     }
42 
43     /**
44      * Gets how many frames have passed since the window started
45      */
46     @property ulong frames() {
47         return this._frames;
48     }
49 
50     /**
51      * Gets the keyboard of the display
52      */
53     @property Keyboard keyboard() {
54         return this._keyboard;
55     }
56 
57     /**
58      * Gets the mouse of the display
59      */
60     @property Mouse mouse() {
61         return this._mouse;
62     }
63 
64     /**
65      * Gets the window of the display
66      */
67     @property Window window() {
68         return this._window;
69     }
70 
71     /**
72      * Gets the contained window's renderer
73      */
74     @property Renderer renderer() {
75         return this._renderer;
76     }
77 
78     /**
79      * Constructs a display given a width, height, window flags, renderer flags, a title, and a path for an image icon (or null)
80      * Disregarding width and height, constructor asks for flags first because once set, those cannot be changed
81      */
82     this(int w, int h, SDL_WindowFlags flags = SDL_WINDOW_SHOWN,
83             uint rendererFlags = 0, string title = "", string iconPath = null) {
84         this._window = new Window(w, h, flags, title);
85         this._renderer = new Renderer(this._window, rendererFlags);
86         if (iconPath !is null && iconPath != "") {
87             this.window.icon = loadImage(iconPath);
88         }
89         this._keyboard = new Keyboard();
90         this._mouse = new Mouse();
91     }
92 
93     /**
94      * Actually runs the display and handles event collection and framerate and most other things
95      */
96     void run() {
97         this.isRunning = true;
98         StopWatch timer = StopWatch(AutoStart.yes);
99         while (this.isRunning) {
100             timer.reset();
101             SDL_Event event;
102             immutable activityExists = this.activity !is null;
103             while (SDL_PollEvent(&event) != 0) {
104                 if (event.type == SDL_QUIT) {
105                     this.isRunning = false;
106                 }
107                 this.mouse.handleEvent(event);
108                 this.keyboard.handleEvent(event);
109                 this.eventHandlers.each!(handler => handler.handleEvent(event));
110                 if (activityExists) {
111                     this.activity.components.each!(component => component.handleEvent(event));
112                     this.activity.handleEvent(event);
113                 }
114             }
115             if (activityExists) {
116                 this.activity.draw();
117                 iRectangle oldClipRect = this.renderer.clipRect();
118                 foreach (component; this.activity.components) {
119                     this.renderer.clipRect = component.location;
120                     component.draw();
121                 }
122                 this.renderer.clipRect = oldClipRect;
123                 this.activity.update();
124             }
125             this.renderer.present();
126             this._frames++;
127             if (this.renderer.info.flags & SDL_RENDERER_PRESENTVSYNC) {
128                 continue;
129             }
130             immutable sleepTime = this.frameSleep - timer.peek().total!"msecs";
131             if (sleepTime > 0) {
132                 Thread.sleep(msecs(sleepTime));
133             }
134         }
135     }
136 
137 }