1 /++ 2 + Authors: Avaxar <avaxar@nekkl.org> 3 + Copyright: Copyright © 2023, Avaxar 4 + License: $(LINK2 https://mit-license.org, MIT License) 5 +/ 6 7 module dsdl2.display; 8 @safe: 9 10 import bindbc.sdl; 11 import dsdl2.sdl; 12 import dsdl2.pixels; 13 import dsdl2.rect; 14 15 import std.array : uninitializedArray; 16 import std.conv : to; 17 import std.format : format; 18 import std.string : toStringz; 19 import std.typecons : Tuple; 20 21 /++ 22 + D struct that wraps `SDL_DisplayMode` containing display mode information 23 +/ 24 struct DisplayMode { 25 PixelFormat pixelFormat; /// Pixel format used 26 uint[2] size; /// Size in pixels 27 uint refreshRate; /// Refresh rate per second 28 void* driverData; /// Internal driver data 29 30 this() @disable; 31 32 /++ 33 + Contructs a `dsdl2.DisplayMode` from a vanilla `SDL_DisplayMode` from bindbc-sdl 34 + 35 + Params: 36 + sdlDisplayMode = the `SDL_DisplayMode` struct 37 +/ 38 this(SDL_DisplayMode sdlDisplayMode) { 39 this.pixelFormat = new PixelFormat(sdlDisplayMode.format); 40 this.size = [sdlDisplayMode.w.to!uint, sdlDisplayMode.h.to!uint]; 41 this.refreshRate = sdlDisplayMode.refresh_rate.to!uint; 42 this.driverData = sdlDisplayMode.driverdata; 43 } 44 45 /++ 46 + Constructs a `dsdl2.DisplayMode` by feeding it its attributes 47 + 48 + Params: 49 + pixelFormat = pixel format 50 + size = size in pixels 51 + refreshRate = refresh rate per second 52 + driverData = internal driver data 53 +/ 54 this(const PixelFormat pixelFormat, uint[2] size, uint refreshRate, void* driverData = null) 55 in { 56 assert(pixelFormat !is null); 57 assert(!pixelFormat.indexed); 58 } 59 do { 60 this.pixelFormat = new PixelFormat(pixelFormat.sdlPixelFormatEnum); 61 this.size = size; 62 this.refreshRate = refreshRate; 63 this.driverData = driverData; 64 } 65 66 invariant { 67 assert(pixelFormat !is null); 68 } 69 70 /++ 71 + Formats the `dsdl2.DisplayMode` into its construction representation: 72 + `"dsdl2.DisplayMode(<pixelFormat>, <size>, <refreshRate>, <driverData>)"` 73 + 74 + Returns: the formatted `string` 75 +/ 76 string toString() const { 77 return "dsdl2.DisplayMode(%s, %s, %d, %p)".format(this.pixelFormat, this.size, 78 this.refreshRate, this.driverData); 79 } 80 81 /++ 82 + Gets the internal `SDL_DisplayMode` representation 83 + 84 + Returns: `SDL_DisplayMode` with all of the attributes 85 +/ 86 inout(SDL_DisplayMode) sdlDisplayMode() inout @property { 87 return inout SDL_DisplayMode(this.pixelFormat.sdlPixelFormatEnum, this.width.to!int, this.height.to!int, 88 this.refreshRate.to!int, this.driverData); 89 } 90 91 /++ 92 + Proxy to the width of the `dsdl2.DisplayMode` 93 + 94 + Returns: width of the `dsdl2.DisplayMode` 95 +/ 96 ref inout(uint) width() return inout @property { 97 return this.size[0]; 98 } 99 100 /++ 101 + Proxy to the height of the `dsdl2.DisplayMode` 102 + 103 + Returns: height of the `dsdl2.DisplayMode` 104 +/ 105 ref inout(uint) height() return inout @property { 106 return this.size[1]; 107 } 108 } 109 110 static if (sdlSupport >= SDLSupport.v2_0_9) { 111 /++ 112 + D enum that wraps `SDL_DisplayOrientation` (from SDL 2.0.9) defining orientation of displays 113 +/ 114 enum DisplayOrientation { 115 /++ 116 + Wraps `SDL_ORIENTATION_*` enumeration constants 117 +/ 118 unknown = SDL_ORIENTATION_UNKNOWN, 119 landscape = SDL_ORIENTATION_LANDSCAPE, /// ditto 120 flippedLandscape = SDL_ORIENTATION_LANDSCAPE_FLIPPED, /// ditto 121 portrait = SDL_ORIENTATION_PORTRAIT, /// ditto 122 flippedPortrait = SDL_ORIENTATION_PORTRAIT_FLIPPED /// ditto 123 } 124 } 125 126 /++ 127 + D class that acts as a proxy for a display from a display index 128 +/ 129 final class Display { 130 const uint sdlDisplayIndex; /// Display index from SDL 131 132 this() @disable; 133 134 private this(uint sdlDisplayIndex) { 135 this.sdlDisplayIndex = sdlDisplayIndex; 136 } 137 138 /++ 139 + Equality operator overload 140 +/ 141 bool opEquals(const Display rhs) const { 142 return this.sdlDisplayIndex == rhs.sdlDisplayIndex; 143 } 144 145 /++ 146 + Gets the hash of the `dsdl2.Display` 147 + 148 + Returns: unique hash for the instance being the display index 149 +/ 150 override hash_t toHash() const { 151 return cast(hash_t) this.sdlDisplayIndex; 152 } 153 154 /++ 155 + Formats the `dsdl2.Display` showing its internal information: `"dsdl2.PixelFormat(<sdlDisplayIndex>)"` 156 + 157 + Returns: the formatted `string` 158 +/ 159 override string toString() const { 160 return "dsdl2.Display(%d)".format(this.sdlDisplayIndex); 161 } 162 163 /++ 164 + Wraps `SDL_GetDisplayName` which gets the display's name 165 + 166 + Returns: the display's name 167 + Throws: `dsdl2.SDLException` if failed to get the display name 168 +/ 169 string name() const @property @trusted { 170 if (const(char)* name = SDL_GetDisplayName(this.sdlDisplayIndex)) { 171 return name.to!string; 172 } 173 else { 174 throw new SDLException; 175 } 176 } 177 178 /++ 179 + Wraps `SDL_GetDisplayBounds` which gets the display's bounding rectangle 180 + 181 + Returns: `dsdl2.Rect` of the display's bounding rectangle 182 + Throws: `dsdl2.SDLException` if failed to get the display bounds 183 +/ 184 Rect bounds() const @property @trusted { 185 Rect rect = void; 186 if (SDL_GetDisplayBounds(this.sdlDisplayIndex, &rect.sdlRect) != 0) { 187 throw new SDLException; 188 } 189 190 return rect; 191 } 192 193 /++ 194 + Gets the width of the display 195 + Wraps in pixels 196 + 197 + Returns: width of the display in pixels 198 +/ 199 uint width() const @property @trusted { 200 return this.bounds.width; 201 } 202 203 /++ 204 + Gets the height of the display in pixels 205 + 206 + Returns: height of the display in pixels 207 +/ 208 uint height() const @property @trusted { 209 return this.bounds.height; 210 } 211 212 /++ 213 + Wraps `SDL_GetNumDisplayModes` and `SDL_GetDisplayMode` which get return a list of the available 214 + display modes of the display 215 + 216 + Returns: array of the `dsdl2.DisplayMode`s 217 + Throws: `dsdl2.SDLException` if failed to get the display modes 218 +/ 219 DisplayMode[] displayModes() const @property @trusted { 220 int numModes = SDL_GetNumDisplayModes(this.sdlDisplayIndex); 221 if (numModes <= 0) { 222 throw new SDLException; 223 } 224 225 SDL_DisplayMode[] sdlModes = new SDL_DisplayMode[](numModes); 226 foreach (i; 0 .. numModes) { 227 if (SDL_GetDisplayMode(this.sdlDisplayIndex, i, &sdlModes[i]) != 0) { 228 throw new SDLException; 229 } 230 } 231 232 DisplayMode[] modes = uninitializedArray!(DisplayMode[])(numModes); 233 foreach (i, SDL_DisplayMode sdlMode; sdlModes) { 234 modes[i] = DisplayMode(sdlMode); 235 } 236 237 return modes; 238 } 239 240 /++ 241 + Wraps `SDL_GetDesktopDisplayMode` which gets the desktop display mode of the display 242 + 243 + Returns: the desktop `dsdl2.DisplayMode` 244 + Throws: `dsdl2.SDLException` if failed to get the desktop display mode 245 +/ 246 DisplayMode desktopDisplayMode() const @property @trusted { 247 SDL_DisplayMode sdlMode = void; 248 if (SDL_GetDesktopDisplayMode(this.sdlDisplayIndex, &sdlMode) != 0) { 249 throw new SDLException; 250 } 251 252 return DisplayMode(sdlMode); 253 } 254 255 /++ 256 + Wraps `SDL_GetCurrentDisplayMode` which gets the current display mode for the display 257 + 258 + Returns: the current `dsdl2.DisplayMode` 259 + Throws: `dsdl2.SDLException` if failed to get the current display mode 260 +/ 261 DisplayMode currentDisplayMode() const @property @trusted { 262 SDL_DisplayMode sdlMode = void; 263 if (SDL_GetCurrentDisplayMode(this.sdlDisplayIndex, &sdlMode) != 0) { 264 throw new SDLException; 265 } 266 267 return DisplayMode(sdlMode); 268 } 269 270 /++ 271 + Wraps `SDL_GetClosestDisplayMode` which gets the closest display mode of the display to the desired mode 272 + 273 + Params: 274 + desiredMode = the desired `dsdl2.DisplayMode` 275 + Returns: the closest available `dsdl2.DisplayMode` of the display 276 + Throws: `dsdl2.SDLException` if failed to get the closest display mode 277 +/ 278 DisplayMode getClosestDisplayMode(DisplayMode desiredMode) const @trusted { 279 SDL_DisplayMode sdlDesiredMode = desiredMode.sdlDisplayMode; 280 SDL_DisplayMode sdlClosestMode = void; 281 282 if (SDL_GetClosestDisplayMode(this.sdlDisplayIndex.to!int, &sdlDesiredMode, &sdlClosestMode) is null) { 283 throw new SDLException; 284 } 285 286 return DisplayMode(sdlClosestMode); 287 } 288 289 static if (sdlSupport >= SDLSupport.v2_0_4) { 290 private alias DisplayDPI = Tuple!(float, "ddpi", float, "hdpi", float, "vdpi"); 291 292 /++ 293 + Wraps `SDL_GetDisplayDPI` (from SDL 2.0.4) which gets the display's DPI information 294 + 295 + Returns: named tuple of `ddpi`, `hdpi`, and `vdpi` 296 + Throws: `dsdl2.SDLException` if failed to get the display's DPI information 297 +/ 298 DisplayDPI displayDPI() const @property @trusted 299 in { 300 assert(getVersion() >= Version(2, 0, 4)); 301 } 302 do { 303 DisplayDPI dpi = void; 304 if (SDL_GetDisplayDPI(this.sdlDisplayIndex, &dpi.ddpi, &dpi.hdpi, &dpi.vdpi) != 0) { 305 throw new SDLException; 306 } 307 308 return dpi; 309 } 310 } 311 312 static if (sdlSupport >= SDLSupport.v2_0_9) { 313 /++ 314 + Wraps `SDL_GetDisplayOrientation` (from SDL 2.0.9) which gets the display's orientation 315 + 316 + Returns: `dsdl2.DisplayOrientation` of the display 317 +/ 318 DisplayOrientation orientation() const @property @trusted 319 in { 320 assert(getVersion() >= Version(2, 0, 9)); 321 } 322 do { 323 return cast(DisplayOrientation) SDL_GetDisplayOrientation(this.sdlDisplayIndex); 324 } 325 } 326 } 327 328 /++ 329 + Gets `dsdl2.Display` proxy instances of the available displays in the system 330 + 331 + Returns: array of proxies to the available `dsdl2.Display`s 332 + Throws: `dsdl2.SDLException` if failed to get the available displays 333 +/ 334 const(Display[]) getDisplays() @trusted { 335 int numDisplays = SDL_GetNumVideoDisplays(); 336 if (numDisplays <= 0) { 337 throw new SDLException; 338 } 339 340 static Display[] displays; 341 if (displays !is null) { 342 size_t originalLength = displays.length; 343 displays.length = numDisplays; 344 345 if (numDisplays > originalLength) { 346 foreach (i; originalLength .. numDisplays) { 347 displays[i] = new Display(i.to!uint); 348 } 349 } 350 } 351 else { 352 displays = new Display[](numDisplays); 353 foreach (i; 0 .. numDisplays) { 354 displays[i] = new Display(i); 355 } 356 } 357 358 return displays; 359 }