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.window;
8 @safe:
9 
10 import bindbc.sdl;
11 import dsdl2.sdl;
12 import dsdl2.display;
13 import dsdl2.pixels;
14 import dsdl2.rect;
15 import dsdl2.renderer;
16 import dsdl2.surface;
17 
18 import core.memory : GC;
19 import std.conv : to;
20 import std.format : format;
21 import std.string : toStringz;
22 import std.typecons : Nullable, nullable;
23 
24 /++
25  + D enum that wraps `SDL_WINDOWPOS_*` to specify certain state of position in `dsdl2.Window` construction
26  +/
27 enum WindowPos : uint {
28     centered = SDL_WINDOWPOS_CENTERED, /// Wraps `SDL_WINDOWPOS_CENTERED` which sets the window to be in the center
29     undefined = SDL_WINDOWPOS_UNDEFINED /// Wraps `SDL_WINDOWPOS_UNDEFINED` which leaves the window's position undefined
30 }
31 
32 private uint toSDLWindowFlags(bool fullscreen, bool fullscreenDesktop, bool openGL, bool shown, bool hidden,
33     bool borderless, bool resizable, bool minimized, bool maximized, bool inputGrabbed, bool inputFocus,
34     bool mouseFocus, bool foreign, bool allowHighDPI, bool mouseCapture, bool alwaysOnTop, bool skipTaskbar,
35     bool utility, bool tooltip, bool popupMenu, bool vulkan, bool metal, bool mouseGrabbed, bool keyboardGrabbed)
36 in {
37     static if (sdlSupport < SDLSupport.v2_0_1) {
38         assert(allowHighDPI == false);
39     }
40     else {
41         if (allowHighDPI) {
42             assert(getVersion() >= Version(2, 0, 1));
43         }
44     }
45 
46     static if (sdlSupport < SDLSupport.v2_0_4) {
47         assert(mouseCapture == false);
48     }
49     else {
50         if (mouseCapture) {
51             assert(getVersion() >= Version(2, 0, 4));
52         }
53     }
54 
55     static if (sdlSupport < SDLSupport.v2_0_5) {
56         assert(alwaysOnTop == false);
57         assert(skipTaskbar == false);
58         assert(utility == false);
59         assert(tooltip == false);
60         assert(popupMenu == false);
61     }
62     else {
63         if (alwaysOnTop || skipTaskbar || utility || tooltip || popupMenu) {
64             assert(getVersion() >= Version(2, 0, 5));
65         }
66     }
67 
68     static if (sdlSupport < SDLSupport.v2_0_6) {
69         assert(vulkan == false);
70         assert(metal == false);
71     }
72     else {
73         if (vulkan || metal) {
74             assert(getVersion() >= Version(2, 0, 6));
75         }
76     }
77 
78     static if (sdlSupport < SDLSupport.v2_0_16) {
79         assert(mouseGrabbed == false);
80         assert(keyboardGrabbed == false);
81     }
82     else {
83         if (mouseGrabbed || keyboardGrabbed) {
84             assert(getVersion() >= Version(2, 0, 16));
85         }
86     }
87 }
88 do {
89     uint flags = 0;
90 
91     flags |= fullscreen ? SDL_WINDOW_FULLSCREEN : 0;
92     flags |= fullscreenDesktop ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0;
93     flags |= openGL ? SDL_WINDOW_OPENGL : 0;
94     flags |= shown ? SDL_WINDOW_SHOWN : 0;
95     flags |= hidden ? SDL_WINDOW_HIDDEN : 0;
96     flags |= borderless ? SDL_WINDOW_BORDERLESS : 0;
97     flags |= resizable ? SDL_WINDOW_RESIZABLE : 0;
98     flags |= minimized ? SDL_WINDOW_MINIMIZED : 0;
99     flags |= maximized ? SDL_WINDOW_MAXIMIZED : 0;
100     flags |= inputGrabbed ? SDL_WINDOW_INPUT_GRABBED : 0;
101     flags |= inputFocus ? SDL_WINDOW_INPUT_FOCUS : 0;
102     flags |= mouseFocus ? SDL_WINDOW_MOUSE_FOCUS : 0;
103     flags |= foreign ? SDL_WINDOW_FOREIGN : 0;
104 
105     static if (sdlSupport >= SDLSupport.v2_0_1) {
106         flags |= allowHighDPI ? SDL_WINDOW_ALLOW_HIGHDPI : 0;
107     }
108     static if (sdlSupport >= SDLSupport.v2_0_4) {
109         flags |= mouseCapture ? SDL_WINDOW_MOUSE_CAPTURE : 0;
110     }
111     static if (sdlSupport >= SDLSupport.v2_0_5) {
112         flags |= alwaysOnTop ? SDL_WINDOW_ALWAYS_ON_TOP : 0;
113         flags |= skipTaskbar ? SDL_WINDOW_SKIP_TASKBAR : 0;
114         flags |= utility ? SDL_WINDOW_UTILITY : 0;
115         flags |= tooltip ? SDL_WINDOW_TOOLTIP : 0;
116         flags |= popupMenu ? SDL_WINDOW_POPUP_MENU : 0;
117     }
118     static if (sdlSupport >= SDLSupport.v2_0_6) {
119         flags |= vulkan ? SDL_WINDOW_VULKAN : 0;
120         flags |= metal ? SDL_WINDOW_METAL : 0;
121     }
122     static if (sdlSupport >= SDLSupport.v2_0_16) {
123         flags |= mouseGrabbed ? SDL_WINDOW_MOUSE_GRABBED : 0;
124         flags |= keyboardGrabbed ? SDL_WINDOW_KEYBOARD_GRABBED : 0;
125     }
126 
127     return flags;
128 }
129 
130 static if (sdlSupport >= SDLSupport.v2_0_16) {
131     /++
132      + D enum that wraps `SDL_FlashOperation` (from SDL 2.0.16) defining window flashing operations
133      +/
134     enum FlashOperation {
135         /++
136          + Wraps `SDL_FLASH_*` enumeration constants
137          +/
138         cancel = SDL_FLASH_CANCEL,
139         briefly = SDL_FLASH_BRIEFLY, /// ditto
140         untilFocused = SDL_FLASH_UNTIL_FOCUSED /// ditto
141     }
142 }
143 
144 /++
145  + D class that wraps `SDL_Window` managing a window instance specific to the OS
146  +
147  + `dsdl2.Window` provides access to creating windows and managing them for rendering. Internally, SDL uses
148  + OS functions to summon the window.
149  +
150  + Example:
151  + ---
152  + auto window = new dsdl2.Window("My Window", [dsdl2.WindowPos.centered, dsdl2.WindowPos.centered], [800, 600]);
153  + window.surface.fill(dsdl2.Color(255, 0, 0));
154  + window.update();
155  + ---
156  +/
157 final class Window {
158     private PixelFormat pixelFormatProxy = null;
159     private Surface surfaceProxy = null;
160     private bool isOwner = true;
161     private void* userRef = null;
162 
163     @system SDL_Window* sdlWindow = null; /// Internal `SDL_Window` pointer
164 
165     /++
166      + Constructs a `dsdl2.Window` from a vanilla `SDL_Window*` from bindbc-sdl
167      +
168      + Params:
169      +   sdlWindow = the `SDL_Window` pointer to manage
170      +   isOwner = whether the instance owns the given `SDL_Window*` and should destroy it on its own
171      +   userRef = optional pointer to maintain reference link, avoiding GC cleanup
172      +/
173     this(SDL_Window* sdlWindow, bool isOwner = true, void* userRef = null) @system
174     in {
175         assert(sdlWindow !is null);
176     }
177     do {
178         this.sdlWindow = sdlWindow;
179         this.isOwner = isOwner;
180         this.userRef = userRef;
181     }
182 
183     /++
184      + Creates an SDL-handled window from a native pointer handle of the OS, which wraps `SDL_CreateWindowFrom`
185      +
186      + Params:
187      +   nativeHandle = pointer to the native OS window
188      + Throws: `dsdl2.SDLException` if window creation failed
189      +/
190     this(void* nativeHandle) @system
191     in {
192         assert(nativeHandle !is null);
193     }
194     do {
195         this.sdlWindow = SDL_CreateWindowFrom(nativeHandle);
196         if (this.sdlWindow is null) {
197             throw new SDLException;
198         }
199     }
200 
201     /++
202      + Creates a window on the desktop placed at a coordinate in the screen, which wraps `SDL_CreateWindow`
203      +
204      + Params:
205      +   title = title given to the shown window
206      +   position = top-left position of the window in the desktop environment (pair of two `uint`s or flags from
207      +              `dsdl2.WindowPos`)
208      +   size = size of the window in pixels
209      +   fullscreen = adds `SDL_WINDOW_FULLSCREEN` flag
210      +   fullscreenDesktop = adds `SDL_WINDOW_FULLSCREEN_DESKTOP` flag
211      +   openGL = adds `SDL_WINDOW_OPENGL` flag
212      +   shown = adds `SDL_WINDOW_SHOWN` flag
213      +   hidden = adds `SDL_WINDOW_HIDDEN` flag
214      +   borderless = adds `SDL_WINDOW_BORDERLESS` flag
215      +   resizable = adds `SDL_WINDOW_RESIZABLE` flag
216      +   minimized = adds `SDL_WINDOW_MINIMIZED` flag
217      +   maximized = adds `SDL_WINDOW_MAXIMIZED` flag
218      +   inputGrabbed = adds `SDL_WINDOW_INPUT_GRABBED` flag
219      +   inputFocus = adds `SDL_WINDOW_INPUT_FOCUS` flag
220      +   mouseFocus = adds `SDL_WINDOW_MOUSE_FOCUS` flag
221      +   foreign = adds `SDL_WINDOW_FOREIGN` flag
222      +   allowHighDPI = adds `SDL_WINDOW_ALLOW_HIGHDPI` flag (from SDL 2.0.1)
223      +   mouseCapture = adds `SDL_WINDOW_MOUSE_CAPTURE` flag (from SDL 2.0.2)
224      +   alwaysOnTop = adds `SDL_WINDOW_ALWAYS_ON_TOP` flag (from SDL 2.0.5)
225      +   skipTaskbar = adds `SDL_WINDOW_SKIP_TASKBAR` flag (from SDL 2.0.5)
226      +   utility = adds `SDL_WINDOW_UTILITY` flag (from SDL 2.0.5)
227      +   tooltip = adds `SDL_WINDOW_TOOLTIP` flag (from SDL 2.0.5)
228      +   popupMenu = adds `SDL_WINDOW_POPUP_MENU` flag (from SDL 2.0.5)
229      +   vulkan = adds `SDL_WINDOW_VULKAN` flag (from SDL 2.0.6)
230      +   metal = adds `SDL_WINDOW_METAL` flag (from SDL 2.0.6)
231      +   mouseGrabbed = adds `SDL_WINDOW_MOUSE_GRABBED` flag (from SDL 2.0.16)
232      +   keyboardGrabbed = adds `SDL_WINDOW_KEYBOARD_GRABBED` flag (from SDL 2.0.16)
233      + Throws: `dsdl2.SDLException` if window creation failed
234      +/
235     this(string title, uint[2] position, uint[2] size, bool fullscreen = false, bool fullscreenDesktop = false,
236         bool openGL = false, bool shown = false, bool hidden = false, bool borderless = false, bool resizable = false,
237         bool minimized = false, bool maximized = false, bool inputGrabbed = false, bool inputFocus = false,
238         bool mouseFocus = false, bool foreign = false, bool allowHighDPI = false, bool mouseCapture = false,
239         bool alwaysOnTop = false, bool skipTaskbar = false, bool utility = false, bool tooltip = false,
240         bool popupMenu = false, bool vulkan = false, bool metal = false, bool mouseGrabbed = false,
241         bool keyboardGrabbed = false) @trusted
242     in {
243         assert(title !is null);
244     }
245     do {
246         uint flags = toSDLWindowFlags(fullscreen, fullscreenDesktop, openGL, shown, hidden, borderless, resizable,
247             minimized, maximized, inputGrabbed, inputFocus, mouseFocus, foreign, allowHighDPI, mouseCapture,
248             alwaysOnTop, skipTaskbar, utility, tooltip, popupMenu, vulkan, metal, mouseGrabbed, keyboardGrabbed);
249 
250         this.sdlWindow = SDL_CreateWindow(title.toStringz(), position[0].to!int, position[1].to!int,
251             size[0].to!int, size[1].to!int, flags);
252         if (this.sdlWindow is null) {
253             throw new SDLException;
254         }
255     }
256 
257     ~this() @trusted {
258         if (this.isOwner) {
259             SDL_DestroyWindow(this.sdlWindow);
260         }
261     }
262 
263     @trusted invariant { // @suppress(dscanner.trust_too_much)
264         // Instance might be in an invalid state due to holding a non-owned externally-freed object when
265         // destructed in an unpredictable order.
266         if (!this.isOwner && GC.inFinalizer) {
267             return;
268         }
269 
270         assert(this.sdlWindow !is null);
271     }
272 
273     /++
274      + Equality operator overload
275      +/
276     bool opEquals(const Window rhs) const @trusted {
277         return this.sdlWindow is rhs.sdlWindow;
278     }
279 
280     /++
281      + Gets the hash of the `dsdl2.Window`
282      +
283      + Returns: unique hash for the instance being the pointer of the internal `SDL_Window` pointer
284      +/
285     override hash_t toHash() const @trusted {
286         return cast(hash_t) this.sdlWindow;
287     }
288 
289     /++
290      + Formats the `dsdl2.Window` into its construction representation: `"dsdl2.Window(<sdlWindow>)"`
291      +
292      + Returns: the formatted `string`
293      +/
294     override string toString() const @trusted {
295         return "dsdl2.Window(0x%x)".format(this.sdlWindow);
296     }
297 
298     /++
299      + Wraps `SDL_GetWindowID` which gets the internal window ID of the `dsdl2.Window`
300      +
301      + Returns: `uint` of the internal window ID
302      +/
303     uint id() const @property @trusted {
304         return SDL_GetWindowID(cast(SDL_Window*) this.sdlWindow);
305     }
306 
307     /++
308      + Wraps `SDL_GetWindowDisplayIndex` which gets the display where the center of the window is located
309      +
310      + Returns: `dsdl2.Display` of the display the window is located
311      + Throws: `dsdl2.SDLException` if failed to get the display
312      +/
313     const(Display) display() const @property @trusted {
314         int index = SDL_GetWindowDisplayIndex(cast(SDL_Window*) this.sdlWindow);
315         if (index < 0) {
316             throw new SDLException;
317         }
318 
319         return getDisplays()[index];
320     }
321 
322     /++
323      + Wraps `SDL_GetWindowDisplayMode` which gets the window's display mode attributes
324      +
325      + Returns: `dsdl2.DisplayMode` storing the attributes
326      + Throws: `dsdl2.SDLException` if failed to get the display mode
327      +/
328     DisplayMode displayMode() const @property @trusted {
329         SDL_DisplayMode sdlMode = void;
330         if (SDL_GetWindowDisplayMode(cast(SDL_Window*) this.sdlWindow, &sdlMode) != 0) {
331             throw new SDLException;
332         }
333 
334         return DisplayMode(sdlMode);
335     }
336 
337     /++
338      + Wraps `SDL_SetWindowDisplayMode` which sets new display mode attributes to the window
339      +
340      + Params:
341      +   newDisplayMode = `dsdl2.DisplayMode` containing the desired attributes
342      + Throws: `dsdl2.SDLException` if failed to set the display mode
343      +/
344     void displayMode(DisplayMode newDisplayMode) @property @trusted {
345         SDL_DisplayMode sdlMode = newDisplayMode.sdlDisplayMode;
346         if (SDL_SetWindowDisplayMode(this.sdlWindow, &sdlMode) != 0) {
347             throw new SDLException;
348         }
349     }
350 
351     /++
352      + Gets the `dsdl2.PixelFormat` used for pixel data of the window
353      +
354      + Returns: read-only `dsdl2.PixelFormat` instance
355      +/
356     const(PixelFormat) pixelFormat() const @property @trusted {
357         // If the internal pixel format pointer happens to change, rewire the proxy.
358         if (this.pixelFormatProxy is null ||
359             this.pixelFormatProxy.sdlPixelFormatEnum !is SDL_GetWindowPixelFormat(cast(SDL_Window*) this.sdlWindow)) {
360             (cast(Window) this).pixelFormatProxy =
361                 new PixelFormat(SDL_GetWindowPixelFormat(cast(SDL_Window*) this.sdlWindow));
362         }
363 
364         return this.pixelFormatProxy;
365     }
366 
367     /++
368      + Wraps `SDL_GetRenderer` which gets the renderer of the window
369      +
370      + Returns: `dsdl2.Renderer` proxy
371      +/
372     inout(Renderer) renderer() inout @property @trusted {
373         if (SDL_Renderer* sdlRenderer = SDL_GetRenderer(cast(SDL_Window*) this.sdlWindow)) {
374             return cast(inout(Renderer)) new Renderer(sdlRenderer, false, cast(void*) this);
375         }
376         else {
377             throw new SDLException;
378         }
379     }
380 
381     /++
382      + Wraps `SDL_GetWindowTitle` which gets the shown title of the window
383      +
384      + Returns: title `string` of the window
385      +/
386     string title() const @property @trusted {
387         return SDL_GetWindowTitle(cast(SDL_Window*) this.sdlWindow).to!string;
388     }
389 
390     /++
391      + Wraps `SDL_SetWindowTitle` which sets a new title to the window
392      +
393      + Params:
394      +   newTitle = `string` of the new title
395      +/
396     void title(string newTitle) @property @trusted {
397         SDL_SetWindowTitle(this.sdlWindow, newTitle.toStringz());
398     }
399 
400     /++
401      + Wraps `SDL_SetWindowIcon` which sets a new icon to the window
402      +
403      + Params:
404      +   newIcon = `dsdl2.Surface` of the new icon
405      +/
406     void icon(Surface newIcon) @property @trusted
407     in {
408         assert(newIcon !is null);
409     }
410     do {
411         SDL_SetWindowIcon(this.sdlWindow, newIcon.sdlSurface);
412     }
413 
414     /++
415      + Wraps `SDL_GetWindowPosition` which gets the top-left X coordinate position of the window in
416      + the desktop environment
417      +
418      + Returns: top-left X coordinate position of the window in the desktop environment
419      +/
420     int x() const @property @trusted {
421         int x = void;
422         SDL_GetWindowPosition(cast(SDL_Window*) this.sdlWindow, &x, null);
423         return x;
424     }
425 
426     /++
427      + Wraps `SDL_SetWindowPosition` which sets the X position of the window in the desktop environment
428      +
429      + Params:
430      +   newX = top-left X coordinate of the new window position in the desktop environment
431      +/
432     void x(int newX) @property @trusted {
433         SDL_SetWindowPosition(this.sdlWindow, newX, this.y);
434     }
435 
436     /++
437      + Wraps `SDL_GetWindowPosition` which gets the top-left Y coordinate position of the window in
438      + the desktop environment
439      +
440      + Returns: top-left Y coordinate position of the window in the desktop environment
441      +/
442     int y() const @property @trusted {
443         int y = void;
444         SDL_GetWindowPosition(cast(SDL_Window*) this.sdlWindow, null, &y);
445         return y;
446     }
447 
448     /++
449      + Wraps `SDL_SetWindowPosition` which sets the Y position of the window in the desktop environment
450      +
451      + Params:
452      +   newY = top-left Y coordinate of the new window position in the desktop environment
453      +/
454     void y(int newY) @property @trusted {
455         SDL_SetWindowPosition(this.sdlWindow, this.x, newY);
456     }
457 
458     /++
459      + Wraps `SDL_GetWindowPosition` which gets the top-left coordinate position of the window in
460      + the desktop environment
461      +
462      + Returns: top-left coordinate position of the window in the desktop environment
463      +/
464     int[2] position() const @property @trusted {
465         int[2] xy = void;
466         SDL_GetWindowPosition(cast(SDL_Window*) this.sdlWindow, &xy[0], &xy[1]);
467         return xy;
468     }
469 
470     /++
471      + Wraps `SDL_SetWindowPosition` which sets the position of the window in the desktop environment
472      +
473      + Params:
474      +   newPosition = top-left coordinate of the new window position in the desktop environment
475      +/
476     void position(int[2] newPosition) @property @trusted {
477         SDL_SetWindowPosition(this.sdlWindow, newPosition[0], newPosition[1]);
478     }
479 
480     /++
481      + Wraps `SDL_GetWindowSize` which gets the width of the window in pixels
482      +
483      + Returns: width of the window in pixels
484      +/
485     uint width() const @property @trusted {
486         uint w = void;
487         SDL_GetWindowSize(cast(SDL_Window*) this.sdlWindow, cast(int*)&w, null);
488         return w;
489     }
490 
491     /++
492      + Wraps `SDL_SetWindowSize` which resizes the width of the window in pixels
493      +
494      + Params:
495      +   newWidth = new resized width of the window in pixels
496      +/
497     void width(uint newWidth) @property @trusted {
498         SDL_SetWindowSize(this.sdlWindow, newWidth.to!int, this.height);
499     }
500 
501     /++
502      + Wraps `SDL_GetWindowSize` which gets the height of the window in pixels
503      +
504      + Returns: height of the window in pixels
505      +/
506     uint height() const @property @trusted {
507         uint h = void;
508         SDL_GetWindowSize(cast(SDL_Window*) this.sdlWindow, null, cast(int*)&h);
509         return h;
510     }
511 
512     /++
513      + Wraps `SDL_SetWindowSize` which resizes the height of the window in pixels
514      +
515      + Params:
516      +   newHeight = new resized height of the window in pixels
517      +/
518     void height(uint newHeight) @property @trusted {
519         SDL_SetWindowSize(this.sdlWindow, this.width, newHeight.to!int);
520     }
521 
522     /++
523      + Wraps `SDL_GetWindowSize` which gets the size of the window in pixels
524      +
525      + Returns: size of the window in pixels
526      +/
527     uint[2] size() const @property @trusted {
528         uint[2] wh = void;
529         SDL_GetWindowSize(cast(SDL_Window*) this.sdlWindow, cast(int*)&wh[0], cast(int*)&wh[1]);
530         return wh;
531     }
532 
533     /++
534      + Wraps `SDL_SetWindowSize` which resizes the size of the window in pixels
535      +
536      + Params:
537      +   newSize = new resized size of the window in pixels (width and height)
538      +/
539     void size(uint[2] newSize) @property @trusted {
540         SDL_SetWindowSize(this.sdlWindow, newSize[0].to!int, newSize[1].to!int);
541     }
542 
543     /++
544      + Wraps `SDL_GetWindowMinimumSize` which gets the minimum size in pixels that the window can be
545      + resized to
546      +
547      + Returns: minimum set size of the window in pixels
548      +/
549     uint[2] minimumSize() const @property @trusted {
550         uint[2] wh;
551         SDL_GetWindowMinimumSize(cast(SDL_Window*) this.sdlWindow, cast(int*)&wh[0], cast(int*)&wh[1]);
552         return wh;
553     }
554 
555     /++
556      + Wraps `SDL_SetWindowMinimumSize` which sets the minimum size in pixels that the window can be
557      + resized to
558      +
559      + Params:
560      +   newMinimumSize = new minimum set size of the window in pixels (width and height)
561      +/
562     void minimumSize(uint[2] newMinimumSize) @property @trusted {
563         SDL_SetWindowMinimumSize(this.sdlWindow, newMinimumSize[0].to!int,
564             newMinimumSize[1].to!int);
565     }
566 
567     /++
568      + Wraps `SDL_GetWindowMaximumSize` which gets the maximum size in pixels that the window can be
569      + resized to
570      +
571      + Returns: maximum set size of the window in pixels
572      +/
573     uint[2] maximumSize() const @property @trusted {
574         uint[2] wh;
575         SDL_GetWindowMaximumSize(cast(SDL_Window*) this.sdlWindow, cast(int*)&wh[0], cast(int*)&wh[1]);
576         return wh;
577     }
578 
579     /++
580      + Wraps `SDL_SetWindowMaximumSize` which sets the maximum size in pixels that the window can be
581      + resized to
582      +
583      + Params:
584      +   newMaximumSize = new maximum set size of the window in pixels (width and height)
585      +/
586     void maximumSize(uint[2] newMaximumSize) @property @trusted {
587         SDL_SetWindowMaximumSize(this.sdlWindow, newMaximumSize[0].to!int,
588             newMaximumSize[1].to!int);
589     }
590 
591     /++
592      + Wraps `SDL_ShowWindow` which sets the window to be visible in the desktop environment
593      +/
594     void show() @trusted {
595         SDL_ShowWindow(this.sdlWindow);
596     }
597 
598     /++
599      + Wraps `SDL_HideWindow` which sets the window to be invisible in the desktop environment
600      +/
601     void hide() @trusted {
602         SDL_HideWindow(this.sdlWindow);
603     }
604 
605     /++
606      + Wraps `SDL_RaiseWindow` which raises the window above other windows, and sets input focus to the window
607      +/
608     void raise() @trusted {
609         SDL_RaiseWindow(this.sdlWindow);
610     }
611 
612     /++
613      + Wraps `SDL_MaximizeWindow` which maximizes the window in the desktop environment
614      +/
615     void maximize() @trusted {
616         SDL_MaximizeWindow(this.sdlWindow);
617     }
618 
619     /++
620      + Wraps `SDL_MinimizeWindow` which minimizes the window in the desktop environment
621      +/
622     void minimize() @trusted {
623         SDL_MinimizeWindow(this.sdlWindow);
624     }
625 
626     /++
627      + Wraps `SDL_RestoreWindow` which restores the size and position of the window as it was originally
628      +/
629     void restore() @trusted {
630         SDL_RestoreWindow(this.sdlWindow);
631     }
632 
633     /++
634      + Wraps `SDL_GetWindowFlags` to check whether the window is in real fullscreen
635      +
636      + Returns: `true` if the the window is in real fullscreen, otherwise `false`
637      +/
638     bool fullscreen() const @property @trusted {
639         return (SDL_GetWindowFlags(cast(SDL_Window*) this.sdlWindow) & SDL_WINDOW_FULLSCREEN) == SDL_WINDOW_FULLSCREEN;
640     }
641 
642     /++
643      + Wraps `SDL_SetWindowFullscreen` which sets the fullscreen mode of the window
644      +
645      + Params:
646      +   newFullscreen = `true` to make the window fullscreen, otherwise `false`
647      + Throws: `dsdl2.SDLException` if failed to set the window's fullscreen mode
648      +/
649     void fullscreen(bool newFullscreen) @property @trusted {
650         if (SDL_SetWindowFullscreen(this.sdlWindow, newFullscreen ? SDL_WINDOW_FULLSCREEN : 0) != 0) {
651             throw new SDLException;
652         }
653     }
654 
655     /++
656      + Wraps `SDL_GetWindowFlags` to check whether the window is in desktop fullscreen
657      +
658      + Returns: `true` if the the window is in desktop fullscreen, otherwise `false`
659      +/
660     bool fullscreenDesktop() const @property @trusted {
661         return (SDL_GetWindowFlags(cast(SDL_Window*) this.sdlWindow) & SDL_WINDOW_FULLSCREEN_DESKTOP) ==
662             SDL_WINDOW_FULLSCREEN_DESKTOP;
663     }
664 
665     /++
666      + Wraps `SDL_GetWindowFlags` to check whether the window utilizes OpenGL
667      +
668      + Returns: `true` if the the window utilizes OpenGL, otherwise `false`
669      +/
670     bool openGL() const @property @trusted {
671         return (SDL_GetWindowFlags(cast(SDL_Window*) this.sdlWindow) & SDL_WINDOW_OPENGL) == SDL_WINDOW_OPENGL;
672     }
673 
674     static if (sdlSupport >= SDLSupport.v2_0_6) {
675         /++
676          + Wraps `SDL_GetWindowFlags` to check whether the window utilizes Vulkan (from SDL 2.0.6)
677          +
678          + Returns: `true` if the the window utilizes Vulkan, otherwise `false`
679          +/
680         bool vulkan() const @property @trusted
681         in {
682             assert(getVersion() >= Version(2, 0, 6));
683         }
684         do {
685             return (SDL_GetWindowFlags(cast(SDL_Window*) this.sdlWindow) & SDL_WINDOW_VULKAN) == SDL_WINDOW_VULKAN;
686         }
687 
688         /++
689          + Wraps `SDL_GetWindowFlags` to check whether the window utilizes Metal (from SDL 2.0.6)
690          +
691          + Returns: `true` if the the window utilizes Metal, otherwise `false`
692          +/
693         bool metal() const @property @trusted
694         in {
695             assert(getVersion() >= Version(2, 0, 6));
696         }
697         do {
698             return (SDL_GetWindowFlags(cast(SDL_Window*) this.sdlWindow) & SDL_WINDOW_METAL) == SDL_WINDOW_METAL;
699         }
700     }
701 
702     /++
703      + Wraps `SDL_GetWindowFlags` to check whether the window is foreign
704      +
705      + Returns: `true` if the the window is foreign, otherwise `false`
706      +/
707     bool foreign() const @property @trusted {
708         return (SDL_GetWindowFlags(cast(SDL_Window*) this.sdlWindow) & SDL_WINDOW_FOREIGN) == SDL_WINDOW_FOREIGN;
709     }
710 
711     /++
712      + Wraps `SDL_GetWindowFlags` to check whether the window is shown
713      +
714      + Returns: `true` if the the window is shown, otherwise `false`
715      +/
716     bool shown() const @property @trusted {
717         return (SDL_GetWindowFlags(cast(SDL_Window*) this.sdlWindow) & SDL_WINDOW_SHOWN) == SDL_WINDOW_SHOWN;
718     }
719 
720     /++
721      + Wraps `SDL_GetWindowFlags` to check whether the window is hidden
722      +
723      + Returns: `true` if the the window is hidden, otherwise `false`
724      +/
725     bool hidden() const @property @trusted {
726         return (SDL_GetWindowFlags(cast(SDL_Window*) this.sdlWindow) & SDL_WINDOW_HIDDEN) == SDL_WINDOW_HIDDEN;
727     }
728 
729     /++
730      + Wraps `SDL_GetWindowFlags` to check whether the borders of the window are non-existent
731      +
732      + Returns: `true` if borders of the window are non-existent, otherwise `false`
733      +/
734     bool borderless() const @property @trusted {
735         return (SDL_GetWindowFlags(cast(SDL_Window*) this.sdlWindow) & SDL_WINDOW_BORDERLESS) == SDL_WINDOW_BORDERLESS;
736     }
737 
738     /++
739      + Wraps `SDL_GetWindowFlags` to check whether the borders of the window are visible
740      +
741      + Returns: `true` if borders are visible, otherwise `false`
742      +/
743     bool bordered() const @property @trusted {
744         return (SDL_GetWindowFlags(cast(SDL_Window*) this.sdlWindow) & SDL_WINDOW_BORDERLESS) != SDL_WINDOW_BORDERLESS;
745     }
746 
747     /++
748      + Wraps `SDL_SetWindowBordered` which sets whether the borders' visibility
749      +
750      + Params:
751      +   newBordered = `true` to make the borders visible, otherwise `false`
752      +/
753     void bordered(bool newBordered) @property @trusted {
754         SDL_SetWindowBordered(this.sdlWindow, newBordered);
755     }
756 
757     /++
758      + Wraps `SDL_GetWindowFlags` to check whether the window's size is resizable by the user
759      +
760      + Returns: `true` if the window is resizable, otherwise `false`
761      +/
762     bool resizable() const @property @trusted {
763         return (SDL_GetWindowFlags(cast(SDL_Window*) this.sdlWindow) & SDL_WINDOW_RESIZABLE) == SDL_WINDOW_RESIZABLE;
764     }
765 
766     static if (sdlSupport >= SDLSupport.v2_0_5) {
767         /++
768          + Wraps `SDL_SetWindowResizable` (from SDL 2.0.5) which sets the window's resizability
769          +
770          + Params:
771          +   newResizable = `true` to make the window resizable, otherwise `false`
772          +/
773         void resizable(bool newResizable) @property @trusted
774         in {
775             assert(getVersion() >= Version(2, 0, 5));
776         }
777         do {
778             SDL_SetWindowResizable(this.sdlWindow, newResizable);
779         }
780     }
781 
782     /++
783      + Wraps `SDL_GetWindowFlags` to check whether the window is minimized
784      +
785      + Returns: `true` if window is minimized, otherwise `false`
786      +/
787     bool minimized() const @property @trusted {
788         return (SDL_GetWindowFlags(cast(SDL_Window*) this.sdlWindow) & SDL_WINDOW_MINIMIZED) == SDL_WINDOW_MINIMIZED;
789     }
790 
791     /++
792      + Wraps `SDL_GetWindowFlags` to check whether the window is maximized
793      +
794      + Returns: `true` if window is maximized, otherwise `false`
795      +/
796     bool maximized() const @property @trusted {
797         return (SDL_GetWindowFlags(cast(SDL_Window*) this.sdlWindow) & SDL_WINDOW_MAXIMIZED) == SDL_WINDOW_MAXIMIZED;
798     }
799 
800     static if (sdlSupport >= SDLSupport.v2_0_1) {
801         /++
802          + Wraps `SDL_GetWindowFlags` to check whether the window allows high DPI (from SDL 2.0.1)
803          +
804          + Returns: `true` if window allows high DPI, otherwise `false`
805          +/
806         bool allowsHighDPI() const @property @trusted
807         in {
808             assert(getVersion() >= Version(2, 0, 1));
809         }
810         do {
811             return (SDL_GetWindowFlags(cast(SDL_Window*) this.sdlWindow) & SDL_WINDOW_ALLOW_HIGHDPI) ==
812                 SDL_WINDOW_ALLOW_HIGHDPI;
813         }
814     }
815 
816     static if (sdlSupport >= SDLSupport.v2_0_5) {
817         /++
818          + Wraps `SDL_GetWindowFlags` to check whether the window is always on top (from SDL 2.0.5)
819          +
820          + Returns: `true` if window is always on top, otherwise `false`
821          +/
822         bool alwaysOnTop() const @property @trusted
823         in {
824             assert(getVersion() >= Version(2, 0, 5));
825         }
826         do {
827             return (SDL_GetWindowFlags(cast(SDL_Window*) this.sdlWindow) & SDL_WINDOW_ALWAYS_ON_TOP) ==
828                 SDL_WINDOW_ALWAYS_ON_TOP;
829         }
830 
831         /++
832          + Wraps `SDL_GetWindowFlags` to check whether the window is not on the taskbar (from SDL 2.0.5)
833          +
834          + Returns: `true` if window is not on the taskbar, otherwise `false`
835          +/
836         bool skipsTaskbar() const @property @trusted
837         in {
838             assert(getVersion() >= Version(2, 0, 5));
839         }
840         do {
841             return (SDL_GetWindowFlags(cast(SDL_Window*) this.sdlWindow) & SDL_WINDOW_SKIP_TASKBAR) ==
842                 SDL_WINDOW_SKIP_TASKBAR;
843         }
844 
845         /++
846          + Wraps `SDL_GetWindowFlags` to check whether the window is treated as a utility window (from SDL 2.0.5)
847          +
848          + Returns: `true` if window is treated as a utility window, otherwise `false`
849          +/
850         bool utility() const @property @trusted
851         in {
852             assert(getVersion() >= Version(2, 0, 5));
853         }
854         do {
855             return (SDL_GetWindowFlags(cast(SDL_Window*) this.sdlWindow) & SDL_WINDOW_UTILITY) ==
856                 SDL_WINDOW_UTILITY;
857         }
858 
859         /++
860          + Wraps `SDL_GetWindowFlags` to check whether the window is treated as a tooltip window (from SDL 2.0.5)
861          +
862          + Returns: `true` if window is treated as a tooltip window, otherwise `false`
863          +/
864         bool tooltip() const @property @trusted
865         in {
866             assert(getVersion() >= Version(2, 0, 5));
867         }
868         do {
869             return (SDL_GetWindowFlags(cast(SDL_Window*) this.sdlWindow) & SDL_WINDOW_TOOLTIP) ==
870                 SDL_WINDOW_TOOLTIP;
871         }
872 
873         /++
874          + Wraps `SDL_GetWindowFlags` to check whether the window is treated as a popup menu (from SDL 2.0.5)
875          +
876          + Returns: `true` if window is treated as a popup menu, otherwise `false`
877          +/
878         bool popupMenu() const @property @trusted
879         in {
880             assert(getVersion() >= Version(2, 0, 5));
881         }
882         do {
883             return (SDL_GetWindowFlags(cast(SDL_Window*) this.sdlWindow) & SDL_WINDOW_POPUP_MENU) ==
884                 SDL_WINDOW_POPUP_MENU;
885         }
886     }
887 
888     /++
889      + Wraps `SDL_GetWindowGrab` which gets the window's input grab mode
890      +
891      + Returns: `true` if the window is in input grab mode, otherwise `false`
892      +/
893     bool inputGrab() const @property @trusted {
894         return SDL_GetWindowGrab(cast(SDL_Window*) this.sdlWindow) == SDL_TRUE;
895     }
896 
897     /++
898      + Wraps `SDL_SetWindowGrab` which sets the window's input grab mode
899      +
900      + Params:
901      +   newGrab = `true` to set the window on input grab mode, otherwise `false`
902      +/
903     void inputGrab(bool newGrab) @property @trusted {
904         SDL_SetWindowGrab(this.sdlWindow, newGrab);
905     }
906 
907     /++
908      + Wraps `SDL_GetWindowBrightness` which gets the window's brightness value
909      +
910      + Returns: `float` value from `0.0` to `1.0` indicating the window's brightness
911      +/
912     float brightness() const @property @trusted {
913         return SDL_GetWindowBrightness(cast(SDL_Window*) this.sdlWindow);
914     }
915 
916     /++
917      + Wraps `SDL_SetWindowBrightness` which sets the window's brightness value
918      +
919      + Params:
920      +   newBrightness = `float` value specifying the window's brightness from `0.0` (darkest) to `1.0` (brightest)
921      + Throws: `dsdl2.SDLException` if failed to set the window's brightness value
922      +/
923     void brightness(float newBrightness) @property @trusted {
924         if (SDL_SetWindowBrightness(this.sdlWindow, newBrightness) != 0) {
925             throw new SDLException;
926         }
927     }
928 
929     /++
930      + Wraps `SDL_IsScreenKeyboardShown` which checks whether the screen keyboard is shown on the window
931      +
932      + Returns: `true` if the screen keyboard is shown, otherwise `false`
933      +/
934     bool hasShownScreenKeyboard() const @property @trusted {
935         return SDL_IsScreenKeyboardShown(cast(SDL_Window*) this.sdlWindow) == SDL_TRUE;
936     }
937 
938     /++
939      + Wraps `SDL_GetKeyboardFocus` which verifies whether keyboard input is focused to the window
940      +
941      + Returns: `true` if keyboard input is focused, otherwise `false`
942      +/
943     bool keyboardFocused() const @property @trusted {
944         return SDL_GetKeyboardFocus() == this.sdlWindow;
945     }
946 
947     /++
948      + Wraps `SDL_GetMouseFocus` which verifies whether mouse input is focused to the window
949      +
950      + Returns: `true` if mouse input is focused, otherwise `false`
951      +/
952     bool mouseFocused() const @property @trusted {
953         return SDL_GetMouseFocus() == this.sdlWindow;
954     }
955 
956     /++
957      + Wraps `SDL_GetMouseState` which gets the mouse position in the window
958      +
959      + Returns: `[x, y]` of the mouse position relative to the window, otherwise `[-1, -1]` if mouse input is not
960      +          focused to the window
961      +/
962     int[2] mousePosition() const @property @trusted {
963         if (SDL_GetMouseFocus() != this.sdlWindow) {
964             return [-1, -1];
965         }
966 
967         int[2] pos = void;
968         SDL_GetMouseState(&pos[0], &pos[1]);
969         return pos;
970     }
971 
972     /++
973      + Wraps `SDL_WarpMouseInWindow` which sets the mouse position in the window
974      +
975      + Params:
976      +   newMousePosition = coordinate of the mouse position to set
977      +/
978     void mousePosition(int[2] newMousePosition) @property @trusted {
979         SDL_WarpMouseInWindow(this.sdlWindow, newMousePosition[0], newMousePosition[1]);
980     }
981 
982     /++
983      + Wraps `SDL_GetWindowSurface` which gets the window's surface for software rendering
984      +
985      + Returns: `dsdl2.Surface` proxy to the window's surface
986      + Throws: `dsdl2.SDLException` if failed to get the window's surface
987      +/
988     inout(Surface) surface() inout @property @trusted {
989         SDL_Surface* surfacePtr = SDL_GetWindowSurface(cast(SDL_Window*) this.sdlWindow);
990         if (surfacePtr is null) {
991             throw new SDLException;
992         }
993 
994         if (this.surfaceProxy is null) {
995             (cast(Window) this).surfaceProxy = new Surface(surfacePtr, false, cast(void*) this);
996         }
997 
998         // If the surface pointer happens to change, rewire the proxy.
999         if (this.surfaceProxy.sdlSurface !is surfacePtr) {
1000             (cast(Window) this).surfaceProxy.sdlSurface = surfacePtr;
1001         }
1002 
1003         return this.surfaceProxy;
1004     }
1005 
1006     static if (sdlSupport >= SDLSupport.v2_28) {
1007         /++
1008          + Wraps `SDL_DestroyWindowSurface` (from SDL 2.28) which destructs the underlying associated surface
1009          + of the window
1010          +
1011          + Throws: `dsdl2.SDLException` if failed to destruct the surface
1012          +/
1013         void surface(typeof(null) _) @property @trusted
1014         in {
1015             assert(getVersion() >= Version(2, 28));
1016         }
1017         do {
1018             if (SDL_DestroyWindowSurface(this.sdlWindow) != 0) {
1019                 throw new SDLException;
1020             }
1021         }
1022 
1023         /++
1024          + Wraps `SDL_HasWindowSurface` (from SDL 2.28) which checks whether there is a surface associated with
1025          + the window
1026          +
1027          + Returns: `true` if the window has an associated surface, otherwise `false`
1028          +/
1029         bool hasSurface() const @property @trusted
1030         in {
1031             assert(getVersion() >= Version(2, 28));
1032         }
1033         do {
1034             return SDL_HasWindowSurface(cast(SDL_Window*) this.sdlWindow) == SDL_TRUE;
1035         }
1036     }
1037 
1038     /++
1039      + Wraps `SDL_UpdateWindowSurface` which makes the changes to the window's surface current
1040      +
1041      + Throws: `dsdl2.SDLException` if failed to update the window's changes
1042      +/
1043     void update() @trusted {
1044         if (SDL_UpdateWindowSurface(this.sdlWindow) != 0) {
1045             throw new SDLException;
1046         }
1047     }
1048 
1049     /++
1050      + Wraps `SDL_UpdateWindowSurfaceRects` which makes the changes of certain parts of the window surface
1051      + as defined by a list of `dsdl2.Rect`s current
1052      +
1053      + Params:
1054      +   rects = array of `dsdl2.Rect`s defining parts of the window surface to update
1055      + Throws: `dsdl2.SDLException` if failed to update the window's changes
1056      +/
1057     void update(Rect[] rects) @trusted {
1058         if (SDL_UpdateWindowSurfaceRects(this.sdlWindow, cast(SDL_Rect*) rects.ptr,
1059                 rects.length.to!int) != 0) {
1060             throw new SDLException;
1061         }
1062     }
1063 
1064     /++
1065      + Wraps `SDL_GL_SwapWindow` which updates the window with any OpenGL changes
1066      +/
1067     void swapGL() @trusted {
1068         SDL_GL_SwapWindow(this.sdlWindow);
1069     }
1070 
1071     static if (sdlSupport >= SDLSupport.v2_0_1) {
1072         /++
1073          + Wraps `SDL_GL_GetDrawableSize` (from SDL 2.0.1) which gets the drawable height of the window in OpenGL
1074          + in pixels
1075          +
1076          + Returns: height of the window in OpenGL in pixels
1077          +/
1078         uint drawableGLWidth() const @property @trusted
1079         in {
1080             assert(getVersion() >= Version(2, 0, 1));
1081         }
1082         do {
1083             uint w = void;
1084             SDL_GL_GetDrawableSize(cast(SDL_Window*) this.sdlWindow, cast(int*)&w, null);
1085             return w;
1086         }
1087 
1088         /++
1089          + Wraps `SDL_GL_GetDrawableSize` (from SDL 2.0.1) which gets the drawable width of the window in OpenGL
1090          + in pixels
1091          +
1092          + Returns: width of the window in OpenGL in pixels
1093          +/
1094         uint drawableGLHeight() const @property @trusted
1095         in {
1096             assert(getVersion() >= Version(2, 0, 1));
1097         }
1098         do {
1099             uint h = void;
1100             SDL_GL_GetDrawableSize(cast(SDL_Window*) this.sdlWindow, null, cast(int*)&h);
1101             return h;
1102         }
1103 
1104         /++
1105          + Wraps `SDL_GL_GetDrawableSize` (from SDL 2.0.1) which gets the drawable size of the window in OpenGL
1106          + in pixels
1107          +
1108          + Returns: size of the window in OpenGL in pixels
1109          +/
1110         uint[2] drawableGLSize() const @property @trusted
1111         in {
1112             assert(getVersion() >= Version(2, 0, 1));
1113         }
1114         do {
1115             uint[2] wh = void;
1116             SDL_GL_GetDrawableSize(cast(SDL_Window*) this.sdlWindow, cast(int*)&wh[0], cast(int*)&wh[1]);
1117             return wh;
1118         }
1119     }
1120 
1121     static if (sdlSupport >= SDLSupport.v2_0_5) {
1122         /++
1123          + Wraps `SDL_SetWindowInputFocus` (from SDL 2.0.5) which focuses the window to be in reach to the user
1124          +
1125          + Throws: `dsdl2.SDLException` if failed to focus the window
1126          +/
1127         void focus() @trusted
1128         in {
1129             assert(getVersion() >= Version(2, 0, 5));
1130         }
1131         do {
1132             if (SDL_SetWindowInputFocus(this.sdlWindow) != 0) {
1133                 throw new SDLException;
1134             }
1135         }
1136 
1137         /++
1138          + Wraps `SDL_SetWindowModalFor` (from SDL 2.0.5) which sets the window to be a modal of another parent
1139          + window, making the window always be above its parent window
1140          +
1141          + Params:
1142          +   parent = the parent window which owns the window as a modal
1143          + Throws: `dsdl2.SDLException` if failed to set the window as modal
1144          +/
1145         void modalFor(Window parent) @trusted
1146         in {
1147             assert(getVersion() >= Version(2, 0, 5));
1148             assert(parent !is null);
1149         }
1150         do {
1151             if (SDL_SetWindowModalFor(this.sdlWindow, parent.sdlWindow) != 0) {
1152                 throw new SDLException;
1153             }
1154         }
1155 
1156         /++
1157          + Wraps `SDL_GetWindowOpacity` (from SDL 2.0.5) which gets the opacity of the window
1158          +
1159          + Returns: `float` indicating the opacity of the window from `0.0` (transparent) to `1.0` (opaque)
1160          + Throws: `dsdl2.SDLException` if failed to get the window's opacity
1161          +/
1162         float opacity() const @property @trusted
1163         in {
1164             assert(getVersion() >= Version(2, 0, 5));
1165         }
1166         do {
1167             float alpha = void;
1168             if (SDL_GetWindowOpacity(cast(SDL_Window*) this.sdlWindow, &alpha) != 0) {
1169                 throw new SDLException;
1170             }
1171 
1172             return alpha;
1173         }
1174 
1175         /++
1176          + Wraps `SDL_SetWindowOpacity` (from SDL 2.0.5) which sets the opacity of the window
1177          +
1178          + Params:
1179          +   newOpacity = `float` indicating the opacity of the window from `0.0` (transparent) to `1.0` (opaque)
1180          + Throws: `dsdl2.SDLException` if failed to set the window's opacity
1181          +/
1182         void opacity(float newOpacity) @property @trusted
1183         in {
1184             assert(getVersion() >= Version(2, 0, 5));
1185         }
1186         do {
1187             if (SDL_SetWindowOpacity(this.sdlWindow, newOpacity) != 0) {
1188                 throw new SDLException;
1189             }
1190         }
1191     }
1192 
1193     static if (sdlSupport >= SDLSupport.v2_0_16) {
1194         /++
1195          + Wraps `SDL_FlashWindow` (from SDL 2.0.16) which flashes the window in the desktop environment
1196          +
1197          + Params:
1198          +   operation = flashing operation to do
1199          + Throws: `dsdl2.SDLException` if failed to flash the window
1200          +/
1201         void flash(FlashOperation operation) @trusted
1202         in {
1203             assert(getVersion() >= Version(2, 0, 16));
1204         }
1205         do {
1206             if (SDL_FlashWindow(this.sdlWindow, operation) != 0) {
1207                 throw new SDLException;
1208             }
1209         }
1210 
1211         /++
1212          + Wraps `SDL_SetWindowAlwaysOnTop` (from SDL 2.0.16) which sets the status of the window always
1213          + being on top above other windows
1214          +
1215          + Params:
1216          +   newOnTop = `true` to always make the window to be on top, otherwise `false`
1217          +/
1218         void onTop(bool newOnTop) @property @trusted
1219         in {
1220             assert(getVersion() >= Version(2, 0, 16));
1221         }
1222         do {
1223             SDL_SetWindowAlwaysOnTop(this.sdlWindow, newOnTop);
1224         }
1225 
1226         /++
1227          + Wraps `SDL_GetWindowKeyboardGrab` (from SDL 2.0.16) which gets the status of the window grabbing
1228          + onto keyboard input
1229          +
1230          + Returns: `true` if the window is grabbing onto keyboard input, otherwise `false`
1231          +/
1232         bool keyboardGrab() const @property @trusted
1233         in {
1234             assert(getVersion() >= Version(2, 0, 16));
1235         }
1236         do {
1237             return SDL_GetWindowKeyboardGrab(cast(SDL_Window*) this.sdlWindow) == SDL_TRUE;
1238         }
1239 
1240         /++
1241          + Wraps `SDL_SetWindowKeyboardGrab` (from SDL 2.0.16) which sets the status of the window grabbing
1242          + onto keyboard input
1243          +
1244          + Params:
1245          +   newKeyboardGrab = `true` to enable keyboard grab, otherwise `false`
1246          +/
1247         void keyboardGrab(bool newKeyboardGrab) @property @trusted
1248         in {
1249             assert(getVersion() >= Version(2, 0, 16));
1250         }
1251         do {
1252             SDL_SetWindowKeyboardGrab(this.sdlWindow, newKeyboardGrab);
1253         }
1254 
1255         /++
1256          + Wraps `SDL_GetWindowMouseGrab` (from SDL 2.0.16) which gets the status of the window grabbing
1257          + onto mouse input
1258          +
1259          + Returns: `true` if the window is grabbing onto mouse input, otherwise `false`
1260          +/
1261         bool mouseGrab() const @property @trusted
1262         in {
1263             assert(getVersion() >= Version(2, 0, 16));
1264         }
1265         do {
1266             return SDL_GetWindowMouseGrab(cast(SDL_Window*) this.sdlWindow) == SDL_TRUE;
1267         }
1268 
1269         /++
1270          + Wraps `SDL_SetWindowMouseGrab` (from SDL 2.0.16) which sets the status of the window grabbing
1271          + onto mouse input
1272          +
1273          + Params:
1274          +   newMouseGrab = `true` to enable mouse grab, otherwise `false`
1275          +/
1276         void mouseGrab(bool newMouseGrab) @property @trusted
1277         in {
1278             assert(getVersion() >= Version(2, 0, 16));
1279         }
1280         do {
1281             SDL_SetWindowMouseGrab(this.sdlWindow, newMouseGrab);
1282         }
1283     }
1284 
1285     static if (sdlSupport >= SDLSupport.v2_0_18) {
1286         /++
1287          + Wraps `SDL_GetWindowICCProfile` (from SDL 2.0.18) which gets the raw ICC profile data for the
1288          + screen the window is currently on
1289          +
1290          + Returns: untyped array buffer of the raw ICC profile data
1291          + Throws `dsdl2.SDLException` if failed to obtain the ICC profile data
1292          +/
1293         void[] iccProfile() const @property @trusted
1294         in {
1295             assert(getVersion() >= Version(2, 0, 18));
1296         }
1297         do {
1298             size_t size = void;
1299             void* data = SDL_GetWindowICCProfile(cast(SDL_Window*) this.sdlWindow, &size);
1300             scope (exit)
1301                 SDL_free(data);
1302 
1303             if (data is null) {
1304                 throw new SDLException;
1305             }
1306 
1307             // Copies the data under allocation with the GC, as `data` is a manually-handled resource
1308             // allocated by SDL
1309             return data[0 .. size].dup;
1310         }
1311 
1312         /++
1313          + Wraps `SDL_GetWindowMouseRect` (from SDL 2.0.18) which gets the window's mouse confinement rectangle
1314          +
1315          + Returns: `dsdl2.Rect` of the mouse's confinement rectangle in the window
1316          +/
1317         Nullable!Rect mouseRect() const @property @trusted
1318         in {
1319             assert(getVersion() >= Version(2, 0, 18));
1320         }
1321         do {
1322             const(SDL_Rect)* rect = SDL_GetWindowMouseRect(cast(SDL_Window*) this.sdlWindow);
1323             if (rect is null) {
1324                 return Nullable!Rect.init;
1325             }
1326             else {
1327                 return Rect(*rect).nullable;
1328             }
1329         }
1330 
1331         /++
1332          + Wraps `SDL_SetWindowMouseRect` (from SDL 2.0.18) which sets the window's mouse confinement rectangle
1333          +
1334          + Params:
1335          +   newMouseRect = `dsdl2.Rect` specifying the rectangle in window coordinate space to confine the mouse
1336          +                  pointer in
1337          + Throws: `dsdl2.SDLException` if failed to set the confinement
1338          +/
1339         void mouseRect(Rect newMouseRect) @property @trusted
1340         in {
1341             assert(getVersion() >= Version(2, 0, 18));
1342         }
1343         do {
1344             if (SDL_SetWindowMouseRect(this.sdlWindow, &newMouseRect.sdlRect) != 0) {
1345                 throw new SDLException;
1346             }
1347         }
1348 
1349         /++
1350          + Acts as `SDL_SetWindowMouseRect(window, NULL)` (from SDL 2.0.18) which resets the window's mouse
1351          + confinement rectangle
1352          +
1353          + Throws: `dsdl2.SDLException` if failed to reset the confinement
1354          +/
1355         void mouseRect(typeof(null) _) @property @trusted
1356         in {
1357             assert(getVersion() >= Version(2, 0, 18));
1358         }
1359         do {
1360             if (SDL_SetWindowMouseRect(this.sdlWindow, null) != 0) {
1361                 throw new SDLException;
1362             }
1363         }
1364 
1365         /++
1366          + Wraps `SDL_SetWindowMouseRect` (from SDL 2.0.18) which sets or resets the window's mouse
1367          + confinement rectangle
1368          +
1369          + Params:
1370          +   newMouseRect = `dsdl2.Rect` specifying the rectangle in window coordinate space to confine the mouse
1371          +                  pointer in; `null` to reset the confinement
1372          + Throws: `dsdl2.SDLException` if failed to set or reset the confinement
1373          +/
1374         void mouseRect(Nullable!Rect newMouseRect) @property @trusted
1375         in {
1376             assert(getVersion() >= Version(2, 0, 18));
1377         }
1378         do {
1379             if (newMouseRect.isNull) {
1380                 this.mouseRect = null;
1381             }
1382             else {
1383                 this.mouseRect = newMouseRect.get;
1384             }
1385         }
1386     }
1387 
1388     static if (sdlSupport >= SDLSupport.v2_26) {
1389         /++
1390          + Wraps `SDL_GetWindowSizeInPixels` (from SDL 2.26) which gets the actual size of the window in the
1391          + screen in pixels
1392          +
1393          + Returns: actual size of the window in the screen in pixels
1394          +/
1395         uint[2] sizeInPixels() const @property @trusted
1396         in {
1397             assert(getVersion() >= Version(2, 26));
1398         }
1399         do {
1400             uint[2] size = void;
1401             SDL_GetWindowSizeInPixels(cast(SDL_Window*) this.sdlWindow, cast(int*)&size[0],
1402                 cast(int*)&size[1]);
1403             return size;
1404         }
1405     }
1406 }