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 }