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.event;
8 @safe:
9 
10 import bindbc.sdl;
11 import dsdl2.sdl;
12 import dsdl2.display;
13 import dsdl2.keyboard;
14 import dsdl2.mouse;
15 
16 import std.conv : to;
17 import std.format : format;
18 
19 /++
20  + Wraps `SDL_PumpEvents` which retrieves events from input devices
21  +/
22 void pumpEvents() @trusted {
23     SDL_PumpEvents();
24 }
25 
26 /++
27  + Wraps `SDL_PollEvent` which returns the latest event in queue
28  +
29  + Returns: `dsdl2.Event` if there's an event, otherwise `null`
30  + Example:
31  + ---
32  + // Polls every upcoming event in queue
33  + dsdl2.pumpEvents();
34  + while (auto event = dsdl2.pollEvent()){
35  +     if (cast(dsdl2.QuitEvent)event) {
36  +         dsdl2.quit();
37  +     }
38  + }
39  + ---
40  +/
41 Event pollEvent() @trusted {
42     SDL_Event event = void;
43     if (SDL_PollEvent(&event) == 1) {
44         return Event.fromSDL(event);
45     }
46     else {
47         return null;
48     }
49 }
50 
51 /++
52  + D abstract class that wraps `SDL_Event` containing details of an event polled from `dsdl2.pollEvent()`
53  +/
54 abstract class Event {
55     SDL_Event sdlEvent; /// Internal `SDL_Event` struct
56 
57     override string toString() const;
58 
59     /++
60      + Gets the `SDL_EventType` of the underlying `SDL_Event`
61      +
62      + Returns: `SDL_EventType` enumeration from bindbc-sdl
63      +/
64     SDL_EventType sdlEventType() const nothrow @property {
65         return this.sdlEvent.type;
66     }
67 
68     /++
69      + Proxy to the timestamp of the `dsdl2.Event`
70      +
71      + Returns: timestamp of the `dsdl2.Event`
72      +/
73     ref inout(uint) timestamp() return inout @property {
74         return this.sdlEvent.common.timestamp;
75     }
76 
77     /++
78      + Turns a vanilla `SDL_Event` from bindbc-sdl to `dsdl2.Event`
79      +
80      + Params:
81      +   sdlEvent = vanilla `SDL_Event` from bindbc-sdl
82      + Returns: `dsdl2.Event` of the same attributes
83      +/
84     static Event fromSDL(SDL_Event sdlEvent) @trusted {
85         Event event;
86         switch (sdlEvent.type) {
87         default:
88             event = new UnknownEvent(sdlEvent);
89             break;
90 
91         case SDL_QUIT:
92             event = new QuitEvent;
93             break;
94 
95         case SDL_APP_TERMINATING:
96             event = new AppTerminatingEvent;
97             break;
98 
99         case SDL_APP_LOWMEMORY:
100             event = new AppLowMemoryEvent;
101             break;
102 
103         case SDL_APP_WILLENTERBACKGROUND:
104             event = new AppWillEnterBackgroundEvent;
105             break;
106 
107         case SDL_APP_DIDENTERBACKGROUND:
108             event = new AppDidEnterBackgroundEvent;
109             break;
110 
111         case SDL_APP_WILLENTERFOREGROUND:
112             event = new AppWillEnterForegroundEvent;
113             break;
114 
115         case SDL_APP_DIDENTERFOREGROUND:
116             event = new AppDidEnterForegroundEvent;
117             break;
118 
119             static if (sdlSupport >= SDLSupport.v2_0_14) {
120         case SDL_LOCALECHANGED:
121                 event = new LocaleChangeEvent;
122                 break;
123             }
124 
125             static if (sdlSupport >= SDLSupport.v2_0_9) {
126         case SDL_DISPLAYEVENT:
127                 event = DisplayEvent.fromSDL(sdlEvent);
128                 break;
129             }
130 
131         case SDL_WINDOWEVENT:
132             event = WindowEvent.fromSDL(sdlEvent);
133             break;
134 
135         case SDL_SYSWMEVENT:
136             event = new SysWMEvent(sdlEvent.syswm.msg);
137             break;
138 
139         case SDL_KEYDOWN:
140         case SDL_KEYUP:
141             event = KeyboardEvent.fromSDL(sdlEvent);
142             break;
143 
144         case SDL_TEXTEDITING:
145             event = new TextEditingEvent(sdlEvent.edit.windowID, sdlEvent.edit.text.ptr.to!string,
146                 sdlEvent.edit.start.to!uint, sdlEvent.edit.length.to!uint);
147             break;
148 
149         case SDL_TEXTINPUT:
150             event = new TextInputEvent(sdlEvent.text.windowID, sdlEvent.text.text.ptr.to!string);
151             break;
152 
153             static if (sdlSupport >= SDLSupport.v2_0_4) {
154         case SDL_KEYMAPCHANGED:
155                 event = new KeymapChangedEvent;
156                 break;
157             }
158 
159         case SDL_MOUSEMOTION:
160             event = new MouseMotionEvent(sdlEvent.motion.windowID, sdlEvent.motion.which,
161                 MouseState(sdlEvent.motion.state),
162                 [sdlEvent.motion.x, sdlEvent.motion.y],
163                 [sdlEvent.motion.xrel, sdlEvent.motion.yrel]);
164             break;
165 
166         case SDL_MOUSEBUTTONDOWN:
167         case SDL_MOUSEBUTTONUP:
168             event = MouseButtonEvent.fromSDL(sdlEvent);
169             break;
170 
171         case SDL_MOUSEWHEEL:
172             static if (sdlSupport >= SDLSupport.v2_0_18) {
173                 event = new MouseWheelEvent(sdlEvent.wheel.windowID, sdlEvent.wheel.which,
174                     [sdlEvent.wheel.x, sdlEvent.wheel.y], cast(MouseWheel) sdlEvent.wheel.direction,
175                     [sdlEvent.wheel.preciseX, sdlEvent.wheel.preciseY]);
176                 break;
177             }
178             else static if (sdlSupport >= SDLSupport.v2_0_4) {
179                 event = new MouseWheelEvent(sdlEvent.wheel.windowID, sdlEvent.wheel.which,
180                     [sdlEvent.wheel.x, sdlEvent.wheel.y], cast(MouseWheel) sdlEvent.wheel.direction);
181                 break;
182             }
183             else {
184                 event = new MouseWheelEvent(sdlEvent.wheel.windowID, sdlEvent.wheel.which,
185                     [sdlEvent.wheel.x, sdlEvent.wheel.y]);
186                 break;
187             }
188 
189         case SDL_FINGERMOTION:
190         case SDL_FINGERDOWN:
191         case SDL_FINGERUP:
192             event = FingerEvent.fromSDL(sdlEvent);
193             break;
194 
195         case SDL_MULTIGESTURE:
196             event = new MultiGestureEvent(sdlEvent.mgesture.touchId, sdlEvent.mgesture.dTheta,
197                 sdlEvent.mgesture.dDist, sdlEvent.mgesture.x, sdlEvent.mgesture.y, sdlEvent.mgesture.numFingers);
198             break;
199 
200         case SDL_DOLLARGESTURE:
201         case SDL_DOLLARRECORD:
202             event = DollarEvent.fromSDL(sdlEvent);
203             break;
204 
205         case SDL_DROPFILE:
206             static if (sdlSupport >= SDLSupport.v2_0_5) {
207         case SDL_DROPTEXT:
208         case SDL_DROPBEGIN:
209         case SDL_DROPCOMPLETE:
210             }
211             event = DropFileEvent.fromSDL(sdlEvent);
212             break;
213         }
214 
215         event.timestamp = sdlEvent.common.timestamp;
216         return event;
217     }
218 }
219 
220 /++
221  + D class that wraps SDL events that aren't recognized by dsdl2
222  +/
223 final class UnknownEvent : Event {
224     this(SDL_Event sdlEvent) {
225         this.sdlEvent = sdlEvent;
226     }
227 
228     override string toString() const {
229         return "dsdl2.UnknownEvent(%s)".format(this.sdlEvent);
230     }
231 }
232 
233 /++
234  + D class that wraps `SDL_QUIT` `SDL_Event`s
235  +/
236 final class QuitEvent : Event {
237     this() {
238         this.sdlEvent.type = SDL_QUIT;
239     }
240 
241     invariant {
242         assert(this.sdlEvent.type == SDL_QUIT);
243     }
244 
245     override string toString() const {
246         return "dsdl2.QuitEvent()";
247     }
248 }
249 
250 /++
251  + D class that wraps `SDL_APP_TERMINATING` `SDL_Event`s
252  +/
253 final class AppTerminatingEvent : Event {
254     this() {
255         this.sdlEvent.type = SDL_APP_TERMINATING;
256     }
257 
258     invariant {
259         assert(this.sdlEvent.type == SDL_APP_TERMINATING);
260     }
261 
262     override string toString() const {
263         return "dsdl2.AppTerminatingEvent()";
264     }
265 }
266 
267 /++
268  + D class that wraps `SDL_APP_LOWMEMORY` `SDL_Event`s
269  +/
270 final class AppLowMemoryEvent : Event {
271     this() {
272         this.sdlEvent.type = SDL_APP_LOWMEMORY;
273     }
274 
275     invariant {
276         assert(this.sdlEvent.type == SDL_APP_LOWMEMORY);
277     }
278 
279     override string toString() const {
280         return "dsdl2.AppLowMemoryEvent()";
281     }
282 }
283 
284 /++
285  + D class that wraps `SDL_APP_WILLENTERBACKGROUND` `SDL_Event`s
286  +/
287 final class AppWillEnterBackgroundEvent : Event {
288     this() {
289         this.sdlEvent.type = SDL_APP_WILLENTERBACKGROUND;
290     }
291 
292     invariant {
293         assert(this.sdlEvent.type == SDL_APP_WILLENTERBACKGROUND);
294     }
295 
296     override string toString() const {
297         return "dsdl2.AppWillEnterBackgroundEvent()";
298     }
299 }
300 
301 /++
302  + D class that wraps `SDL_APP_DIDENTERBACKGROUND` `SDL_Event`s
303  +/
304 final class AppDidEnterBackgroundEvent : Event {
305     this() {
306         this.sdlEvent.type = SDL_APP_DIDENTERBACKGROUND;
307     }
308 
309     invariant {
310         assert(this.sdlEvent.type == SDL_APP_DIDENTERBACKGROUND);
311     }
312 
313     override string toString() const {
314         return "dsdl2.AppDidEnterBackgroundEvent()";
315     }
316 }
317 
318 /++
319  + D class that wraps `SDL_APP_WILLENTERFOREGROUND` `SDL_Event`s
320  +/
321 final class AppWillEnterForegroundEvent : Event {
322     this() {
323         this.sdlEvent.type = SDL_APP_WILLENTERFOREGROUND;
324     }
325 
326     invariant {
327         assert(this.sdlEvent.type == SDL_APP_WILLENTERFOREGROUND);
328     }
329 
330     override string toString() const {
331         return "dsdl2.AppWillEnterForegroundEvent()";
332     }
333 }
334 
335 /++
336  + D class that wraps `SDL_APP_DIDENTERFOREGROUND` `SDL_Event`s
337  +/
338 final class AppDidEnterForegroundEvent : Event {
339     this() {
340         this.sdlEvent.type = SDL_APP_DIDENTERFOREGROUND;
341     }
342 
343     invariant {
344         assert(this.sdlEvent.type == SDL_APP_DIDENTERFOREGROUND);
345     }
346 
347     override string toString() const {
348         return "dsdl2.AppDidEnterForegroundEvent()";
349     }
350 }
351 
352 static if (sdlSupport >= SDLSupport.v2_0_14) {
353     /++
354      + D class that wraps `SDL_LOCALECHANGED` `SDL_Event`s (from SDL 2.0.14)
355      +/
356     class LocaleChangeEvent : Event {
357         this() {
358             this.sdlEvent.type = SDL_LOCALECHANGED;
359         }
360 
361         invariant {
362             assert(this.sdlEvent.type == SDL_LOCALECHANGED);
363         }
364 
365         override string toString() const {
366             return "dsdl2.LocaleChangeEvent()";
367         }
368     }
369 }
370 
371 static if (sdlSupport >= SDLSupport.v2_0_9) {
372     /++
373      + D abstract class that wraps `SDL_DISPLAYEVENT` `SDL_Event`s (from SDL 2.0.9)
374      +/
375     abstract class DisplayEvent : Event {
376         invariant {
377             assert(this.sdlEvent.type == SDL_DISPLAYEVENT);
378         }
379 
380         SDL_DisplayEventID sdlDisplayEventID() const nothrow @property {
381             return this.sdlEvent.display.event;
382         }
383 
384         ref inout(uint) display() return inout @property {
385             return this.sdlEvent.display.display;
386         }
387 
388         static Event fromSDL(SDL_Event sdlEvent)
389         in {
390             assert(sdlEvent.type == SDL_DISPLAYEVENT);
391         }
392         do {
393             Event event;
394             switch (sdlEvent.display.event) {
395             default:
396                 event = new UnknownEvent(sdlEvent);
397                 break;
398 
399             case SDL_DISPLAYEVENT_ORIENTATION:
400                 event = new DisplayOrientationEvent(sdlEvent.display.display,
401                     cast(DisplayOrientation) sdlEvent.display.data1);
402                 break;
403 
404                 static if (sdlSupport >= SDLSupport.v2_0_14) {
405             case SDL_DISPLAYEVENT_CONNECTED:
406                     event = new DisplayConnectedEvent(sdlEvent.display.display);
407                     break;
408 
409             case SDL_DISPLAYEVENT_DISCONNECTED:
410                     event = new DisplayDisconnectedEvent(sdlEvent.display.display);
411                     break;
412                 }
413 
414                 static if (sdlSupport >= SDLSupport.v2_28) {
415             case SDL_DISPLAYEVENT_MOVED:
416                     event = new DisplayMovedEvent(sdlEvent.display.display);
417                     break;
418                 }
419             }
420 
421             event.timestamp = sdlEvent.display.timestamp;
422             return event;
423         }
424     }
425 
426     /++
427      + D class that wraps `SDL_DISPLAYEVENT_ORIENTATION` `SDL_DISPLAYEVENT` `SDL_Event`s (from SDL 2.0.9)
428      +/
429     class DisplayOrientationEvent : DisplayEvent {
430         this(uint display, DisplayOrientation orientation) {
431             this.sdlEvent.type = SDL_DISPLAYEVENT;
432             this.sdlEvent.display.event = SDL_DISPLAYEVENT_ORIENTATION;
433             this.sdlEvent.display.display = display;
434             this.sdlEvent.display.data1 = orientation;
435         }
436 
437         invariant {
438             assert(this.sdlEvent.display.event == SDL_DISPLAYEVENT_ORIENTATION);
439         }
440 
441         override string toString() const {
442             return "dsdl2.DisplayOrientationEvent(%d, %d)".format(this.display, this.orientation);
443         }
444 
445         ref inout(DisplayOrientation) orientation() return inout @property @trusted {
446             return *cast(inout(DisplayOrientation*))&this.sdlEvent.display.data1;
447         }
448     }
449 }
450 
451 static if (sdlSupport >= SDLSupport.v2_0_14) {
452     /++
453      + D class that wraps `SDL_DISPLAYEVENT_CONNECTED` `SDL_DISPLAYEVENT` `SDL_Event`s (from SDL 2.0.14)
454      +/
455     class DisplayConnectedEvent : DisplayEvent {
456         this(uint display) {
457             this.sdlEvent.type = SDL_DISPLAYEVENT;
458             this.sdlEvent.display.event = SDL_DISPLAYEVENT_CONNECTED;
459             this.sdlEvent.display.display = display;
460         }
461 
462         invariant {
463             assert(this.sdlEvent.display.event == SDL_DISPLAYEVENT_CONNECTED);
464         }
465 
466         override string toString() const {
467             return "dsdl2.DisplayConnectedEvent(%d)".format(this.display);
468         }
469     }
470 
471     /++
472      + D class that wraps `SDL_DISPLAYEVENT_DISCONNECTED` `SDL_DISPLAYEVENT` `SDL_Event`s (from SDL 2.0.14)
473      +/
474     class DisplayDisconnectedEvent : DisplayEvent {
475         this(uint display) {
476             this.sdlEvent.type = SDL_DISPLAYEVENT;
477             this.sdlEvent.display.event = SDL_DISPLAYEVENT_DISCONNECTED;
478             this.sdlEvent.display.display = display;
479         }
480 
481         invariant {
482             assert(this.sdlEvent.display.event == SDL_DISPLAYEVENT_DISCONNECTED);
483         }
484 
485         override string toString() const {
486             return "dsdl2.DisplayDisconnectedEvent(%d)".format(this.display);
487         }
488     }
489 }
490 
491 static if (sdlSupport >= SDLSupport.v2_28) {
492     /++
493      + D class that wraps `SDL_DISPLAYEVENT_MOVED` `SDL_DISPLAYEVENT` `SDL_Event`s (from SDL 2.0.28)
494      +/
495     class DisplayMovedEvent : DisplayEvent {
496         this(uint display) {
497             this.sdlEvent.type = SDL_DISPLAYEVENT;
498             this.sdlEvent.display.event = SDL_DISPLAYEVENT_MOVED;
499             this.sdlEvent.display.display = display;
500         }
501 
502         invariant {
503             assert(this.sdlEvent.display.event == SDL_DISPLAYEVENT_MOVED);
504         }
505 
506         override string toString() const {
507             return "dsdl2.DisplayMovedEvent(%d)".format(this.display);
508         }
509     }
510 }
511 
512 /++
513  + D abstract class that wraps `SDL_WINDOWEVENT` `SDL_Event`s
514  +/
515 abstract class WindowEvent : Event {
516     invariant {
517         assert(this.sdlEvent.type == SDL_WINDOWEVENT);
518     }
519 
520     SDL_WindowEventID sdlWindowEventID() const nothrow @property {
521         return this.sdlEvent.window.event;
522     }
523 
524     ref inout(uint) windowID() return inout @property {
525         return this.sdlEvent.window.windowID;
526     }
527 
528     static Event fromSDL(SDL_Event sdlEvent)
529     in {
530         assert(sdlEvent.type == SDL_WINDOWEVENT);
531     }
532     do {
533         Event event;
534         switch (sdlEvent.window.event) {
535         default:
536             event = new UnknownEvent(sdlEvent);
537             break;
538 
539         case SDL_WINDOWEVENT_SHOWN:
540             event = new WindowShownEvent(sdlEvent.window.windowID);
541             break;
542 
543         case SDL_WINDOWEVENT_HIDDEN:
544             event = new WindowHiddenEvent(sdlEvent.window.windowID);
545             break;
546 
547         case SDL_WINDOWEVENT_EXPOSED:
548             event = new WindowExposedEvent(sdlEvent.window.windowID);
549             break;
550 
551         case SDL_WINDOWEVENT_MOVED:
552             event = new WindowMovedEvent(sdlEvent.window.windowID,
553                 [sdlEvent.window.data1, sdlEvent.window.data2]);
554             break;
555 
556         case SDL_WINDOWEVENT_RESIZED:
557             event = new WindowResizedEvent(sdlEvent.window.windowID,
558                 [sdlEvent.window.data1, sdlEvent.window.data2]);
559             break;
560 
561         case SDL_WINDOWEVENT_SIZE_CHANGED:
562             event = new WindowSizeChangedEvent(sdlEvent.window.windowID);
563             break;
564 
565         case SDL_WINDOWEVENT_MINIMIZED:
566             event = new WindowMinimizedEvent(sdlEvent.window.windowID);
567             break;
568 
569         case SDL_WINDOWEVENT_MAXIMIZED:
570             event = new WindowMaximizedEvent(sdlEvent.window.windowID);
571             break;
572 
573         case SDL_WINDOWEVENT_RESTORED:
574             event = new WindowRestoredEvent(sdlEvent.window.windowID);
575             break;
576 
577         case SDL_WINDOWEVENT_ENTER:
578             event = new WindowEnterEvent(sdlEvent.window.windowID);
579             break;
580 
581         case SDL_WINDOWEVENT_LEAVE:
582             event = new WindowLeaveEvent(sdlEvent.window.windowID);
583             break;
584 
585         case SDL_WINDOWEVENT_FOCUS_GAINED:
586             event = new WindowFocusGainedEvent(sdlEvent.window.windowID);
587             break;
588 
589         case SDL_WINDOWEVENT_FOCUS_LOST:
590             event = new WindowFocusLostEvent(sdlEvent.window.windowID);
591             break;
592 
593         case SDL_WINDOWEVENT_CLOSE:
594             event = new WindowCloseEvent(sdlEvent.window.windowID);
595             break;
596 
597             static if (sdlSupport >= SDLSupport.v2_0_5) {
598         case SDL_WINDOWEVENT_TAKE_FOCUS:
599                 event = new WindowTakeFocusEvent(sdlEvent.window.windowID);
600                 break;
601 
602         case SDL_WINDOWEVENT_HIT_TEST:
603                 event = new WindowHitTestEvent(sdlEvent.window.windowID);
604                 break;
605             }
606 
607             static if (sdlSupport >= SDLSupport.v2_0_18) {
608         case SDL_WINDOWEVENT_ICCPROF_CHANGED:
609                 event = new WindowICCProfileChangedEvent(sdlEvent.window.windowID);
610                 break;
611 
612         case SDL_WINDOWEVENT_DISPLAY_CHANGED:
613                 event = new WindowDisplayChangedEvent(sdlEvent.window.windowID,
614                     sdlEvent.window.data1);
615                 break;
616             }
617         }
618 
619         event.timestamp = sdlEvent.window.timestamp;
620         return event;
621     }
622 }
623 
624 /++
625  + D class that wraps `SDL_WINDOWEVENT_SHOWN` `SDL_WINDOWEVENT` `SDL_Event`s
626  +/
627 final class WindowShownEvent : WindowEvent {
628     this(uint windowID) {
629         this.sdlEvent.type = SDL_WINDOWEVENT;
630         this.sdlEvent.window.event = SDL_WINDOWEVENT_SHOWN;
631         this.sdlEvent.window.windowID = windowID;
632     }
633 
634     invariant {
635         assert(this.sdlEvent.window.event == SDL_WINDOWEVENT_SHOWN);
636     }
637 
638     override string toString() const {
639         return "dsdl2.WindowShownEvent(%d)".format(this.windowID);
640     }
641 }
642 
643 /++
644  + D class that wraps `SDL_WINDOWEVENT_HIDDEN` `SDL_WINDOWEVENT` `SDL_Event`s
645  +/
646 final class WindowHiddenEvent : WindowEvent {
647     this(uint windowID) {
648         this.sdlEvent.type = SDL_WINDOWEVENT;
649         this.sdlEvent.window.event = SDL_WINDOWEVENT_HIDDEN;
650         this.sdlEvent.window.windowID = windowID;
651     }
652 
653     invariant {
654         assert(this.sdlEvent.window.event == SDL_WINDOWEVENT_HIDDEN);
655     }
656 
657     override string toString() const {
658         return "dsdl2.WindowHiddenEvent(%d)".format(this.windowID);
659     }
660 }
661 
662 /++
663  + D class that wraps `SDL_WINDOWEVENT_EXPOSED` `SDL_WINDOWEVENT` `SDL_Event`s
664  +/
665 final class WindowExposedEvent : WindowEvent {
666     this(uint windowID) {
667         this.sdlEvent.type = SDL_WINDOWEVENT;
668         this.sdlEvent.window.event = SDL_WINDOWEVENT_EXPOSED;
669         this.sdlEvent.window.windowID = windowID;
670     }
671 
672     invariant {
673         assert(this.sdlEvent.window.event == SDL_WINDOWEVENT_EXPOSED);
674     }
675 
676     override string toString() const {
677         return "dsdl2.WindowExposedEvent(%d)".format(this.windowID);
678     }
679 }
680 
681 /++
682  + D class that wraps `SDL_WINDOWEVENT_MOVED` `SDL_WINDOWEVENT` `SDL_Event`s
683  +/
684 final class WindowMovedEvent : WindowEvent {
685     this(uint windowID, int[2] xy) {
686         this.sdlEvent.type = SDL_WINDOWEVENT;
687         this.sdlEvent.window.event = SDL_WINDOWEVENT_MOVED;
688         this.sdlEvent.window.windowID = windowID;
689         this.sdlEvent.window.data1 = xy[0];
690         this.sdlEvent.window.data2 = xy[1];
691     }
692 
693     invariant {
694         assert(this.sdlEvent.window.event == SDL_WINDOWEVENT_MOVED);
695     }
696 
697     override string toString() const {
698         return "dsdl2.WindowMovedEvent(%d, %s)".format(this.windowID, this.xy);
699     }
700 
701     ref inout(int) x() return inout @property {
702         return this.sdlEvent.window.data1;
703     }
704 
705     ref inout(int) y() return inout @property {
706         return this.sdlEvent.window.data2;
707     }
708 
709     ref inout(int[2]) xy() return inout @property @trusted {
710         return *cast(inout(int[2]*))&this.sdlEvent.window.data1;
711     }
712 }
713 
714 /++
715  + D class that wraps `SDL_WINDOWEVENT_RESIZED` `SDL_WINDOWEVENT` `SDL_Event`s
716  +/
717 final class WindowResizedEvent : WindowEvent {
718     this(uint windowID, uint[2] size) {
719         this.sdlEvent.type = SDL_WINDOWEVENT;
720         this.sdlEvent.window.event = SDL_WINDOWEVENT_RESIZED;
721         this.sdlEvent.window.windowID = windowID;
722         this.sdlEvent.window.data1 = size[0].to!int;
723         this.sdlEvent.window.data2 = size[1].to!int;
724     }
725 
726     invariant {
727         assert(this.sdlEvent.window.event == SDL_WINDOWEVENT_RESIZED);
728     }
729 
730     override string toString() const {
731         return "dsdl2.WindowResizedEvent(%d, %s)".format(this.windowID, this.size);
732     }
733 
734     ref inout(uint) width() return inout @property @trusted {
735         return *cast(inout(uint*))&this.sdlEvent.window.data1;
736     }
737 
738     ref inout(uint) height() return inout @property @trusted {
739         return *cast(inout(uint*))&this.sdlEvent.window.data2;
740     }
741 
742     ref inout(uint[2]) size() return inout @property @trusted {
743         return *cast(inout(uint[2]*))&this.sdlEvent.window.data1;
744     }
745 }
746 
747 /++
748  + D class that wraps `SDL_WINDOWEVENT_SIZE_CHANGED` `SDL_WINDOWEVENT` `SDL_Event`s
749  +/
750 final class WindowSizeChangedEvent : WindowEvent {
751     this(uint windowID) {
752         this.sdlEvent.type = SDL_WINDOWEVENT;
753         this.sdlEvent.window.event = SDL_WINDOWEVENT_SIZE_CHANGED;
754         this.sdlEvent.window.windowID = windowID;
755     }
756 
757     invariant {
758         assert(this.sdlEvent.window.event == SDL_WINDOWEVENT_SIZE_CHANGED);
759     }
760 
761     override string toString() const {
762         return "dsdl2.WindowSizeChangedEvent(%d)".format(this.windowID);
763     }
764 }
765 
766 /++
767  + D class that wraps `SDL_WINDOWEVENT_MINIMIZED` `SDL_WINDOWEVENT` `SDL_Event`s
768  +/
769 final class WindowMinimizedEvent : WindowEvent {
770     this(uint windowID) {
771         this.sdlEvent.type = SDL_WINDOWEVENT;
772         this.sdlEvent.window.event = SDL_WINDOWEVENT_MINIMIZED;
773         this.sdlEvent.window.windowID = windowID;
774     }
775 
776     invariant {
777         assert(this.sdlEvent.window.event == SDL_WINDOWEVENT_MINIMIZED);
778     }
779 
780     override string toString() const {
781         return "dsdl2.WindowMinimizedEvent(%d)".format(this.windowID);
782     }
783 }
784 
785 /++
786  + D class that wraps `SDL_WINDOWEVENT_MAXIMIZED` `SDL_WINDOWEVENT` `SDL_Event`s
787  +/
788 final class WindowMaximizedEvent : WindowEvent {
789     this(uint windowID) {
790         this.sdlEvent.type = SDL_WINDOWEVENT;
791         this.sdlEvent.window.event = SDL_WINDOWEVENT_MAXIMIZED;
792         this.sdlEvent.window.windowID = windowID;
793     }
794 
795     invariant {
796         assert(this.sdlEvent.window.event == SDL_WINDOWEVENT_MAXIMIZED);
797     }
798 
799     override string toString() const {
800         return "dsdl2.WindowMaximizedEvent(%d)".format(this.windowID);
801     }
802 }
803 
804 /++
805  + D class that wraps `SDL_WINDOWEVENT_RESTORED` `SDL_WINDOWEVENT` `SDL_Event`s
806  +/
807 final class WindowRestoredEvent : WindowEvent {
808     this(uint windowID) {
809         this.sdlEvent.type = SDL_WINDOWEVENT;
810         this.sdlEvent.window.event = SDL_WINDOWEVENT_RESTORED;
811         this.sdlEvent.window.windowID = windowID;
812     }
813 
814     invariant {
815         assert(this.sdlEvent.window.event == SDL_WINDOWEVENT_RESTORED);
816     }
817 
818     override string toString() const {
819         return "dsdl2.WindowRestoredEvent(%d)".format(this.windowID);
820     }
821 }
822 
823 /++
824  + D class that wraps `SDL_WINDOWEVENT_ENTER` `SDL_WINDOWEVENT` `SDL_Event`s
825  +/
826 final class WindowEnterEvent : WindowEvent {
827     this(uint windowID) {
828         this.sdlEvent.type = SDL_WINDOWEVENT;
829         this.sdlEvent.window.event = SDL_WINDOWEVENT_ENTER;
830         this.sdlEvent.window.windowID = windowID;
831     }
832 
833     invariant {
834         assert(this.sdlEvent.window.event == SDL_WINDOWEVENT_ENTER);
835     }
836 
837     override string toString() const {
838         return "dsdl2.WindowEnterEvent(%d)".format(this.windowID);
839     }
840 }
841 
842 /++
843  + D class that wraps `SDL_WINDOWEVENT_LEAVE` `SDL_WINDOWEVENT` `SDL_Event`s
844  +/
845 final class WindowLeaveEvent : WindowEvent {
846     this(uint windowID) {
847         this.sdlEvent.type = SDL_WINDOWEVENT;
848         this.sdlEvent.window.event = SDL_WINDOWEVENT_LEAVE;
849         this.sdlEvent.window.windowID = windowID;
850     }
851 
852     invariant {
853         assert(this.sdlEvent.window.event == SDL_WINDOWEVENT_LEAVE);
854     }
855 
856     override string toString() const {
857         return "dsdl2.WindowLeaveEvent(%d)".format(this.windowID);
858     }
859 }
860 
861 /++
862  + D class that wraps `SDL_WINDOWEVENT_FOCUS_GAINED` `SDL_WINDOWEVENT` `SDL_Event`s
863  +/
864 final class WindowFocusGainedEvent : WindowEvent {
865     this(uint windowID) {
866         this.sdlEvent.type = SDL_WINDOWEVENT;
867         this.sdlEvent.window.event = SDL_WINDOWEVENT_FOCUS_GAINED;
868         this.sdlEvent.window.windowID = windowID;
869     }
870 
871     invariant {
872         assert(this.sdlEvent.window.event == SDL_WINDOWEVENT_FOCUS_GAINED);
873     }
874 
875     override string toString() const {
876         return "dsdl2.WindowFocusGainedEvent(%d)".format(this.windowID);
877     }
878 }
879 
880 /++
881  + D class that wraps `SDL_WINDOWEVENT_FOCUS_LOST` `SDL_WINDOWEVENT` `SDL_Event`s
882  +/
883 final class WindowFocusLostEvent : WindowEvent {
884     this(uint windowID) {
885         this.sdlEvent.type = SDL_WINDOWEVENT;
886         this.sdlEvent.window.event = SDL_WINDOWEVENT_FOCUS_LOST;
887         this.sdlEvent.window.windowID = windowID;
888     }
889 
890     invariant {
891         assert(this.sdlEvent.window.event == SDL_WINDOWEVENT_FOCUS_LOST);
892     }
893 
894     override string toString() const {
895         return "dsdl2.WindowFocusLostEvent(%d)".format(this.windowID);
896     }
897 }
898 
899 /++
900  + D class that wraps `SDL_WINDOWEVENT_CLOSE` `SDL_WINDOWEVENT` `SDL_Event`s
901  +/
902 final class WindowCloseEvent : WindowEvent {
903     this(uint windowID) {
904         this.sdlEvent.type = SDL_WINDOWEVENT;
905         this.sdlEvent.window.event = SDL_WINDOWEVENT_CLOSE;
906         this.sdlEvent.window.windowID = windowID;
907     }
908 
909     invariant {
910         assert(this.sdlEvent.window.event == SDL_WINDOWEVENT_CLOSE);
911     }
912 
913     override string toString() const {
914         return "dsdl2.WindowCloseEvent(%d)".format(this.windowID);
915     }
916 }
917 
918 static if (sdlSupport >= SDLSupport.v2_0_5) {
919     /++
920      + D class that wraps `SDL_WINDOWEVENT_TAKE_FOCUS` `SDL_WINDOWEVENT` `SDL_Event`s (from SDL 2.0.5)
921      +/
922     class WindowTakeFocusEvent : WindowEvent {
923         this(uint windowID) {
924             this.sdlEvent.type = SDL_WINDOWEVENT;
925             this.sdlEvent.window.event = SDL_WINDOWEVENT_TAKE_FOCUS;
926             this.sdlEvent.window.windowID = windowID;
927         }
928 
929         invariant {
930             assert(this.sdlEvent.window.event == SDL_WINDOWEVENT_TAKE_FOCUS);
931         }
932 
933         override string toString() const {
934             return "dsdl2.WindowTakeFocusEvent(%d)".format(this.windowID);
935         }
936     }
937 
938     /++
939      + D class that wraps `SDL_WINDOWEVENT_HIT_TEST` `SDL_WINDOWEVENT` `SDL_Event`s (from SDL 2.0.5)
940      +/
941     class WindowHitTestEvent : WindowEvent {
942         this(uint windowID) {
943             this.sdlEvent.type = SDL_WINDOWEVENT;
944             this.sdlEvent.window.event = SDL_WINDOWEVENT_HIT_TEST;
945             this.sdlEvent.window.windowID = windowID;
946         }
947 
948         invariant {
949             assert(this.sdlEvent.window.event == SDL_WINDOWEVENT_HIT_TEST);
950         }
951 
952         override string toString() const {
953             return "dsdl2.WindowHitTestEvent(%d)".format(this.windowID);
954         }
955     }
956 }
957 
958 static if (sdlSupport >= SDLSupport.v2_0_18) {
959     /++
960      + D class that wraps `SDL_WINDOWEVENT_ICCPROF_CHANGED` `SDL_WINDOWEVENT` `SDL_Event`s (from SDL 2.0.18)
961      +/
962     class WindowICCProfileChangedEvent : WindowEvent {
963         this(uint windowID) {
964             this.sdlEvent.type = SDL_WINDOWEVENT;
965             this.sdlEvent.window.event = SDL_WINDOWEVENT_ICCPROF_CHANGED;
966             this.sdlEvent.window.windowID = windowID;
967         }
968 
969         invariant {
970             assert(this.sdlEvent.window.event == SDL_WINDOWEVENT_ICCPROF_CHANGED);
971         }
972 
973         override string toString() const {
974             return "dsdl2.WindowICCProfileChangedEvent(%d)".format(this.windowID);
975         }
976     }
977 
978     /++
979      + D class that wraps `SDL_WINDOWEVENT_DISPLAY_CHANGED` `SDL_WINDOWEVENT` `SDL_Event`s (from SDL 2.0.18)
980      +/
981     class WindowDisplayChangedEvent : WindowEvent {
982         this(uint windowID, uint display) {
983             this.sdlEvent.type = SDL_WINDOWEVENT;
984             this.sdlEvent.window.event = SDL_WINDOWEVENT_DISPLAY_CHANGED;
985             this.sdlEvent.window.windowID = windowID;
986             this.sdlEvent.window.data1 = display.to!int;
987         }
988 
989         invariant {
990             assert(this.sdlEvent.window.event == SDL_WINDOWEVENT_DISPLAY_CHANGED);
991         }
992 
993         override string toString() const {
994             return "dsdl2.WindowDisplayChangedEvent(%d)".format(this.windowID, this.display);
995         }
996 
997         ref inout(int) display() return inout @property {
998             return this.sdlEvent.window.data1;
999         }
1000     }
1001 }
1002 
1003 /++
1004  + D class that wraps `SDL_SYSWMEVENT` `SDL_Event`s
1005  +/
1006 final class SysWMEvent : Event {
1007     this(SDL_SysWMmsg* msg) @system {
1008         this.sdlEvent.type = SDL_SYSWMEVENT;
1009         this.sdlEvent.syswm.msg = msg;
1010     }
1011 
1012     @trusted invariant { // @suppress(dscanner.trust_too_much)
1013         assert(this.sdlEvent.syswm.msg !is null);
1014     }
1015 
1016     override string toString() const @trusted {
1017         return "dsdl2.SysWMEvent(0x%x)".format(this.msg);
1018     }
1019 
1020     ref inout(SDL_SysWMmsg*) msg() return inout @property @system {
1021         return this.sdlEvent.syswm.msg;
1022     }
1023 }
1024 
1025 /++
1026  + D abstract class that wraps keyboard `SDL_Event`s
1027  +/
1028 abstract class KeyboardEvent : Event {
1029     invariant {
1030         assert(this.sdlEvent.type == SDL_KEYDOWN || this.sdlEvent.type == SDL_KEYUP);
1031     }
1032 
1033     ref inout(uint) windowID() return inout @property {
1034         return this.sdlEvent.key.windowID;
1035     }
1036 
1037     ref inout(ubyte) repeat() return inout @property {
1038         return this.sdlEvent.key.repeat;
1039     }
1040 
1041     ref inout(Scancode) scancode() return inout @property @trusted {
1042         return *cast(inout(Scancode*))&this.sdlEvent.key.keysym.scancode;
1043     }
1044 
1045     ref inout(Keycode) sym() return inout @property @trusted {
1046         return *cast(inout(Keycode*))&this.sdlEvent.key.keysym.sym;
1047     }
1048 
1049     Keymod mod() const @property {
1050         return Keymod(this.sdlEvent.key.keysym.mod);
1051     }
1052 
1053     void mod(Keymod newMod) @property {
1054         this.sdlEvent.key.keysym.mod = newMod.sdlKeymod;
1055     }
1056 
1057     static Event fromSDL(SDL_Event sdlEvent)
1058     in {
1059         assert(sdlEvent.type == SDL_KEYDOWN || sdlEvent.type == SDL_KEYUP);
1060     }
1061     do {
1062         Event event;
1063         switch (sdlEvent.type) {
1064         default:
1065             assert(false);
1066 
1067         case SDL_KEYDOWN:
1068             event = new KeyDownKeyboardEvent(sdlEvent.key.windowID, sdlEvent.key.repeat,
1069                 cast(Scancode) sdlEvent.key.keysym.scancode, cast(Keycode) sdlEvent.key.keysym.sym,
1070                 Keymod(sdlEvent.key.keysym.mod));
1071             break;
1072 
1073         case SDL_KEYUP:
1074             event = new KeyUpKeyboardEvent(sdlEvent.key.windowID, sdlEvent.key.repeat,
1075                 cast(Scancode) sdlEvent.key.keysym.scancode, cast(Keycode) sdlEvent.key.keysym.sym,
1076                 Keymod(sdlEvent.key.keysym.mod));
1077             break;
1078         }
1079 
1080         event.timestamp = sdlEvent.key.timestamp;
1081         return event;
1082     }
1083 }
1084 
1085 /++
1086  + D class that wraps `SDL_KEYDOWN` `SDL_Event`s
1087  +/
1088 final class KeyDownKeyboardEvent : KeyboardEvent {
1089     this(uint windowID, ubyte repeat, Scancode scancode, Keycode sym, Keymod mod) {
1090         this.sdlEvent.type = SDL_KEYDOWN;
1091         this.sdlEvent.key.windowID = windowID;
1092         this.sdlEvent.key.repeat = repeat;
1093         this.sdlEvent.key.keysym.scancode = cast(SDL_Scancode) scancode;
1094         this.sdlEvent.key.keysym.sym = cast(SDL_Keycode) sym;
1095         this.sdlEvent.key.keysym.mod = mod.sdlKeymod;
1096     }
1097 
1098     invariant {
1099         assert(this.sdlEvent.type == SDL_KEYDOWN);
1100     }
1101 
1102     override string toString() const {
1103         return "dsdl2.KeyDownKeyboardEvent(%d, %d, %s, %s, %s)".format(this.windowID, this.repeat, this.scancode,
1104             this.sym, this.mod);
1105     }
1106 }
1107 
1108 /++
1109  + D class that wraps `SDL_KEYUP` `SDL_Event`s
1110  +/
1111 final class KeyUpKeyboardEvent : KeyboardEvent {
1112     this(uint windowID, ubyte repeat, Scancode scancode, Keycode sym, Keymod mod) {
1113         this.sdlEvent.type = SDL_KEYUP;
1114         this.sdlEvent.key.windowID = windowID;
1115         this.sdlEvent.key.repeat = repeat;
1116         this.sdlEvent.key.keysym.scancode = cast(SDL_Scancode) scancode;
1117         this.sdlEvent.key.keysym.sym = cast(SDL_Keycode) sym;
1118         this.sdlEvent.key.keysym.mod = mod.sdlKeymod;
1119     }
1120 
1121     invariant {
1122         assert(this.sdlEvent.type == SDL_KEYUP);
1123     }
1124 
1125     override string toString() const {
1126         return "dsdl2.KeyUpKeyboardEvent(%d, %d, %s, %s, %s)".format(this.windowID, this.repeat, this.scancode,
1127             this.sym, this.mod);
1128     }
1129 }
1130 
1131 /++
1132  + D class that wraps `SDL_TEXTEDITING` `SDL_Event`s
1133  +/
1134 final class TextEditingEvent : Event {
1135     this(uint windowID, string text, uint start, uint length)
1136     in {
1137         assert(text.length < 32);
1138     }
1139     do {
1140         this.sdlEvent.type = SDL_TEXTEDITING;
1141         this.sdlEvent.edit.windowID = windowID;
1142         this.sdlEvent.edit.text[0 .. text.length] = text[];
1143         this.sdlEvent.edit.start = start.to!int;
1144         this.sdlEvent.edit.length = length.to!int;
1145     }
1146 
1147     invariant {
1148         assert(this.sdlEvent.type == SDL_TEXTEDITING);
1149     }
1150 
1151     override string toString() const @trusted {
1152         return "dsdl2.TextEditingEvent(%d, %s, %d, %d)".format(this.windowID,
1153             [this.text].to!string[1 .. $ - 1], this.start, this.length);
1154     }
1155 
1156     ref inout(uint) windowID() return inout @property {
1157         return this.sdlEvent.edit.windowID;
1158     }
1159 
1160     string text() const @property @trusted {
1161         return this.sdlEvent.edit.text.ptr.to!string;
1162     }
1163 
1164     void text(string newText) @property
1165     in {
1166         assert(newText.length < 32);
1167     }
1168     do {
1169         this.sdlEvent.edit.text[0 .. newText.length] = newText[];
1170     }
1171 
1172     ref inout(uint) start() return inout @property {
1173         return *cast(inout(uint*))&this.sdlEvent.edit.start;
1174     }
1175 
1176     ref inout(uint) length() return inout @property {
1177         return *cast(inout(uint*))&this.sdlEvent.edit.length;
1178     }
1179 }
1180 
1181 /++
1182  + D class that wraps `SDL_TEXTINPUT` `SDL_Event`s
1183  +/
1184 final class TextInputEvent : Event {
1185     this(uint windowID, string text)
1186     in {
1187         assert(text.length < 32);
1188     }
1189     do {
1190         this.sdlEvent.type = SDL_TEXTINPUT;
1191         this.sdlEvent.edit.windowID = windowID;
1192         this.sdlEvent.edit.text[0 .. text.length] = text[];
1193     }
1194 
1195     invariant {
1196         assert(this.sdlEvent.type == SDL_TEXTINPUT);
1197     }
1198 
1199     override string toString() const @trusted {
1200         return "dsdl2.TextInputEvent(%d, %s)".format(this.windowID,
1201             [this.text].to!string[1 .. $ - 1]);
1202     }
1203 
1204     ref inout(uint) windowID() return inout @property {
1205         return this.sdlEvent.edit.windowID;
1206     }
1207 
1208     string text() const @property @trusted {
1209         return this.sdlEvent.edit.text.ptr.to!string;
1210     }
1211 
1212     void text(string newText) @property
1213     in {
1214         assert(newText.length < 32);
1215     }
1216     do {
1217         this.sdlEvent.edit.text[0 .. newText.length] = newText[];
1218     }
1219 }
1220 
1221 static if (sdlSupport >= SDLSupport.v2_0_4) {
1222     /++
1223      + D class that wraps `SDL_KEYMAPCHANGED` `SDL_Event`s (from SDL 2.0.4)
1224      +/
1225     final class KeymapChangedEvent : Event {
1226         this() {
1227             this.sdlEvent.type = SDL_KEYMAPCHANGED;
1228         }
1229 
1230         invariant {
1231             assert(this.sdlEvent.type == SDL_KEYMAPCHANGED);
1232         }
1233 
1234         override string toString() const {
1235             return "dsdl2.KeymapChangedEvent()";
1236         }
1237     }
1238 }
1239 
1240 /++
1241  + D class that wraps `SDL_MOUSEMOTION` `SDL_Event`s
1242  +/
1243 final class MouseMotionEvent : Event {
1244     this(uint windowID, uint which, MouseState state, int[2] xy, int[2] xyRel) {
1245         this.sdlEvent.type = SDL_MOUSEMOTION;
1246         this.sdlEvent.motion.windowID = windowID;
1247         this.sdlEvent.motion.which = which;
1248         this.sdlEvent.motion.state = state.sdlMouseState;
1249         this.sdlEvent.motion.x = xy[0];
1250         this.sdlEvent.motion.y = xy[1];
1251         this.sdlEvent.motion.xrel = xyRel[0];
1252         this.sdlEvent.motion.yrel = xyRel[1];
1253     }
1254 
1255     invariant {
1256         assert(this.sdlEvent.type == SDL_MOUSEMOTION);
1257     }
1258 
1259     override string toString() const {
1260         return "dsdl2.MouseMotionEvent(%d, %d, %s, %s, %s)".format(this.windowID, this.which,
1261             this.state, this.xy, this.xyRel);
1262     }
1263 
1264     ref inout(uint) windowID() return inout @property {
1265         return this.sdlEvent.motion.windowID;
1266     }
1267 
1268     ref inout(uint) which() return inout @property {
1269         return this.sdlEvent.motion.which;
1270     }
1271 
1272     MouseState state() const @property {
1273         return MouseState(this.sdlEvent.motion.state);
1274     }
1275 
1276     void state(MouseState newState) @property {
1277         this.sdlEvent.motion.state = newState.sdlMouseState;
1278     }
1279 
1280     ref inout(int) x() return inout @property {
1281         return this.sdlEvent.motion.x;
1282     }
1283 
1284     ref inout(int) y() return inout @property {
1285         return this.sdlEvent.motion.y;
1286     }
1287 
1288     ref inout(int[2]) xy() return inout @property @trusted {
1289         return *cast(inout(int[2]*))&this.sdlEvent.motion.x;
1290     }
1291 
1292     ref inout(int) xRel() return inout @property {
1293         return this.sdlEvent.motion.xrel;
1294     }
1295 
1296     ref inout(int) yRel() return inout @property {
1297         return this.sdlEvent.motion.yrel;
1298     }
1299 
1300     ref inout(int[2]) xyRel() return inout @property @trusted {
1301         return *cast(inout(int[2]*))&this.sdlEvent.motion.xrel;
1302     }
1303 }
1304 
1305 /++
1306  + D abstract class that wraps mouse button `SDL_Event`s
1307  +/
1308 abstract class MouseButtonEvent : Event {
1309     invariant {
1310         assert(this.sdlEvent.type == SDL_MOUSEBUTTONDOWN || this.sdlEvent.type == SDL_MOUSEBUTTONUP);
1311     }
1312 
1313     ref inout(uint) windowID() return inout @property {
1314         return this.sdlEvent.button.windowID;
1315     }
1316 
1317     ref inout(uint) which() return inout @property {
1318         return this.sdlEvent.button.which;
1319     }
1320 
1321     ref inout(MouseButton) button() inout @property @trusted {
1322         return *cast(inout(MouseButton*))&this.sdlEvent.button.button;
1323     }
1324 
1325     ref inout(ubyte) clicks() return inout @property {
1326         static if (sdlSupport >= SDLSupport.v2_0_2) {
1327             return this.sdlEvent.button.clicks;
1328         }
1329         else {
1330             return this.sdlEvent.button.padding1;
1331         }
1332     }
1333 
1334     ref inout(int) x() return inout @property {
1335         return this.sdlEvent.button.x;
1336     }
1337 
1338     ref inout(int) y() return inout @property {
1339         return this.sdlEvent.button.y;
1340     }
1341 
1342     ref inout(int[2]) xy() return inout @property @trusted {
1343         return *cast(inout(int[2]*))&this.sdlEvent.button.x;
1344     }
1345 
1346     static Event fromSDL(SDL_Event sdlEvent)
1347     in {
1348         assert(sdlEvent.type == SDL_MOUSEBUTTONDOWN || sdlEvent.type == SDL_MOUSEBUTTONUP);
1349     }
1350     do {
1351         Event event;
1352         switch (sdlEvent.type) {
1353         default:
1354             assert(false);
1355 
1356         case SDL_MOUSEBUTTONDOWN:
1357             static if (sdlSupport >= SDLSupport.v2_0_2) {
1358                 event = new MouseButtonDownEvent(sdlEvent.button.windowID, sdlEvent.button.which,
1359                     cast(MouseButton) sdlEvent.button.button, sdlEvent.button.clicks,
1360                     [sdlEvent.button.x, sdlEvent.button.y]);
1361             }
1362             else {
1363                 event = new MouseButtonDownEvent(sdlEvent.button.windowID, sdlEvent.button.which,
1364                     cast(MouseButton) sdlEvent.button.button, 1,
1365                     [sdlEvent.button.x, sdlEvent.button.y]);
1366             }
1367             break;
1368 
1369         case SDL_MOUSEBUTTONUP:
1370             static if (sdlSupport >= SDLSupport.v2_0_2) {
1371                 event = new MouseButtonUpEvent(sdlEvent.button.windowID, sdlEvent.button.which,
1372                     cast(MouseButton) sdlEvent.button.button, sdlEvent.button.clicks,
1373                     [sdlEvent.button.x, sdlEvent.button.y]);
1374             }
1375             else {
1376                 event = new MouseButtonUpEvent(sdlEvent.button.windowID, sdlEvent.button.which,
1377                     cast(MouseButton) sdlEvent.button.button, 1,
1378                     [sdlEvent.button.x, sdlEvent.button.y]);
1379             }
1380             break;
1381         }
1382 
1383         event.timestamp = sdlEvent.button.timestamp;
1384         return event;
1385     }
1386 }
1387 
1388 /++
1389  + D class that wraps `SDL_MOUSEBUTTONDOWN` `SDL_Event`s
1390  +/
1391 final class MouseButtonDownEvent : MouseButtonEvent {
1392     this(uint windowID, uint which, MouseButton button, ubyte clicks, int[2] xy) {
1393         this.sdlEvent.type = SDL_MOUSEBUTTONDOWN;
1394         this.sdlEvent.button.windowID = windowID;
1395         this.sdlEvent.button.which = which;
1396         this.sdlEvent.button.button = button;
1397         this.sdlEvent.button.state = SDL_PRESSED;
1398         static if (sdlSupport >= SDLSupport.v2_0_2) {
1399             this.sdlEvent.button.clicks = clicks;
1400         }
1401         else {
1402             this.sdlEvent.button.padding1 = clicks;
1403         }
1404         this.sdlEvent.button.x = xy[0];
1405         this.sdlEvent.button.y = xy[1];
1406     }
1407 
1408     invariant {
1409         assert(this.sdlEvent.type == SDL_MOUSEBUTTONDOWN);
1410         assert(this.sdlEvent.button.state == SDL_PRESSED);
1411     }
1412 
1413     override string toString() const {
1414         return "dsdl2.MouseButtonDownEvent(%d, %d, %s, %d, %s)".format(this.windowID,
1415             this.which, this.button, this.clicks, this.xy);
1416     }
1417 }
1418 
1419 /++
1420  + D class that wraps `SDL_MOUSEBUTTONUP` `SDL_Event`s
1421  +/
1422 final class MouseButtonUpEvent : MouseButtonEvent {
1423     this(uint windowID, uint which, MouseButton button, ubyte clicks, int[2] xy) {
1424         this.sdlEvent.type = SDL_MOUSEBUTTONDOWN;
1425         this.sdlEvent.button.windowID = windowID;
1426         this.sdlEvent.button.which = which;
1427         this.sdlEvent.button.button = button;
1428         this.sdlEvent.button.state = SDL_RELEASED;
1429         static if (sdlSupport >= SDLSupport.v2_0_2) {
1430             this.sdlEvent.button.clicks = clicks;
1431         }
1432         else {
1433             this.sdlEvent.button.padding1 = clicks;
1434         }
1435         this.sdlEvent.button.x = xy[0];
1436         this.sdlEvent.button.y = xy[1];
1437     }
1438 
1439     invariant {
1440         assert(this.sdlEvent.type == SDL_MOUSEBUTTONDOWN);
1441         assert(this.sdlEvent.button.state == SDL_RELEASED);
1442     }
1443 
1444     override string toString() const {
1445         return "dsdl2.MouseButtonUpEvent(%d, %d, %s, %d, %s)".format(this.windowID,
1446             this.which, this.button, this.clicks, this.xy);
1447     }
1448 }
1449 
1450 /++
1451  + D class that wraps `SDL_MOUSEWHEEL` `SDL_Event`s
1452  +/
1453 final class MouseWheelEvent : Event {
1454     static if (sdlSupport >= SDLSupport.v2_0_18) {
1455         this(uint windowID, uint which, int[2] xy, MouseWheel direction = MouseWheel.normal,
1456             float[2] preciseXY = [0.0, 0.0]) {
1457             this.sdlEvent.type = SDL_MOUSEWHEEL;
1458             this.sdlEvent.wheel.windowID = windowID;
1459             this.sdlEvent.wheel.which = which;
1460             this.sdlEvent.wheel.x = xy[0];
1461             this.sdlEvent.wheel.y = xy[1];
1462             this.sdlEvent.wheel.direction = cast(uint) direction;
1463             this.sdlEvent.wheel.preciseX = preciseXY[0];
1464             this.sdlEvent.wheel.preciseY = preciseXY[1];
1465         }
1466     }
1467     else static if (sdlSupport >= SDLSupport.v2_0_4) {
1468         this(uint windowID, uint which, int[2] xy, MouseWheel direction = MouseWheel.normal) {
1469             this.sdlEvent.type = SDL_MOUSEWHEEL;
1470             this.sdlEvent.wheel.windowID = windowID;
1471             this.sdlEvent.wheel.which = which;
1472             this.sdlEvent.wheel.x = xy[0];
1473             this.sdlEvent.wheel.y = xy[1];
1474             this.sdlEvent.wheel.direction = cast(uint) direction;
1475         }
1476     }
1477     else {
1478         this(uint windowID, uint which, int[2] xy) {
1479             this.sdlEvent.type = SDL_MOUSEWHEEL;
1480             this.sdlEvent.wheel.windowID = windowID;
1481             this.sdlEvent.wheel.which = which;
1482             this.sdlEvent.wheel.x = xy[0];
1483             this.sdlEvent.wheel.y = xy[1];
1484         }
1485     }
1486 
1487     invariant {
1488         assert(this.sdlEvent.type == SDL_MOUSEWHEEL);
1489     }
1490 
1491     ref inout(uint) windowID() return inout @property {
1492         return this.sdlEvent.wheel.windowID;
1493     }
1494 
1495     override string toString() const {
1496         static if (sdlSupport >= SDLSupport.v2_0_18) {
1497             return "dsdl2.MouseWheelEvent(%d, %d, %s, %s, %s)".format(this.windowID, this.which,
1498                 this.xy, this.direction, this.preciseXY);
1499         }
1500         else static if (sdlSupport >= SDLSupport.v2_0_4) {
1501             return "dsdl2.MouseWheelEvent(%d, %d, %s, %s)".format(this.windowID, this.which, this.xy,
1502                 this.direction);
1503         }
1504         else {
1505             return "dsdl2.MouseWheelEvent(%d, %d, %s)".format(this.windowID, this.which, this.xy);
1506         }
1507     }
1508 
1509     ref inout(uint) which() return inout @property {
1510         return this.sdlEvent.wheel.which;
1511     }
1512 
1513     ref inout(int) x() return inout @property {
1514         return this.sdlEvent.wheel.x;
1515     }
1516 
1517     ref inout(int) y() return inout @property {
1518         return this.sdlEvent.wheel.y;
1519     }
1520 
1521     ref inout(int[2]) xy() return inout @property @trusted {
1522         return *cast(inout(int[2]*))&this.sdlEvent.wheel.x;
1523     }
1524 
1525     static if (sdlSupport >= SDLSupport.v2_0_4) {
1526         ref inout(MouseWheel) direction() return inout @property @trusted {
1527             return *cast(inout(MouseWheel*))&this.sdlEvent.wheel.direction;
1528         }
1529     }
1530 
1531     static if (sdlSupport >= SDLSupport.v2_0_18) {
1532         ref inout(float) preciseX() return inout @property {
1533             return this.sdlEvent.wheel.preciseX;
1534         }
1535 
1536         ref inout(float) preciseY() return inout @property {
1537             return this.sdlEvent.wheel.preciseY;
1538         }
1539 
1540         ref inout(float[2]) preciseXY() return inout @property @trusted {
1541             return *cast(inout(float[2]*))&this.sdlEvent.wheel.preciseX;
1542         }
1543     }
1544 }
1545 
1546 /++
1547  + D abstract class that wraps touch finger `SDL_Event`s
1548  +/
1549 abstract class FingerEvent : Event {
1550     invariant {
1551         assert(this.sdlEvent.type == SDL_FINGERMOTION || this.sdlEvent.type == SDL_FINGERDOWN
1552                 || this.sdlEvent.type == SDL_FINGERUP);
1553     }
1554 
1555     ref inout(ulong) touchID() return inout @property {
1556         return *cast(inout ulong*)&this.sdlEvent.tfinger.touchId;
1557     }
1558 
1559     ref inout(ulong) fingerID() return inout @property {
1560         return *cast(inout ulong*)&this.sdlEvent.tfinger.fingerId;
1561     }
1562 
1563     ref inout(float) x() return inout @property {
1564         return this.sdlEvent.tfinger.x;
1565     }
1566 
1567     ref inout(float) y() return inout @property {
1568         return this.sdlEvent.tfinger.y;
1569     }
1570 
1571     ref inout(float) dx() return inout @property {
1572         return this.sdlEvent.tfinger.dx;
1573     }
1574 
1575     ref inout(float) dy() return inout @property {
1576         return this.sdlEvent.tfinger.dy;
1577     }
1578 
1579     ref inout(float) pressure() return inout @property {
1580         return this.sdlEvent.tfinger.pressure;
1581     }
1582 
1583     static if (sdlSupport >= SDLSupport.v2_0_12) {
1584         ref inout(uint) windowID() return inout @property {
1585             return this.sdlEvent.tfinger.windowID;
1586         }
1587     }
1588 
1589     static Event fromSDL(SDL_Event sdlEvent)
1590     in {
1591         assert(sdlEvent.tfinger.type == SDL_FINGERMOTION || sdlEvent.tfinger.type == SDL_FINGERDOWN
1592                 || sdlEvent.tfinger.type == SDL_FINGERUP);
1593     }
1594     do {
1595         Event event;
1596         switch (sdlEvent.type) {
1597         default:
1598             assert(false);
1599 
1600         case SDL_FINGERMOTION:
1601             static if (sdlSupport >= SDLSupport.v2_0_12) {
1602                 event = new FingerMotionEvent(sdlEvent.tfinger.touchId, sdlEvent.tfinger.fingerId, sdlEvent.tfinger.x,
1603                     sdlEvent.tfinger.y, sdlEvent.tfinger.dx, sdlEvent.tfinger.dy, sdlEvent.tfinger.pressure,
1604                     sdlEvent.tfinger.windowID);
1605             }
1606             else {
1607                 event = new FingerMotionEvent(sdlEvent.tfinger.touchId, sdlEvent.tfinger.fingerId, sdlEvent.tfinger.x,
1608                     sdlEvent.tfinger.y, sdlEvent.tfinger.dx, sdlEvent.tfinger.dy, sdlEvent.tfinger.pressure);
1609             }
1610             break;
1611 
1612         case SDL_FINGERDOWN:
1613             static if (sdlSupport >= SDLSupport.v2_0_12) {
1614                 event = new FingerDownEvent(sdlEvent.tfinger.touchId, sdlEvent.tfinger.fingerId, sdlEvent.tfinger.x,
1615                     sdlEvent.tfinger.y, sdlEvent.tfinger.dx, sdlEvent.tfinger.dy, sdlEvent.tfinger.pressure,
1616                     sdlEvent.tfinger.windowID);
1617             }
1618             else {
1619                 event = new FingerDownEvent(sdlEvent.tfinger.touchId, sdlEvent.tfinger.fingerId, sdlEvent.tfinger.x,
1620                     sdlEvent.tfinger.y, sdlEvent.tfinger.dx, sdlEvent.tfinger.dy, sdlEvent.tfinger.pressure);
1621             }
1622             break;
1623 
1624         case SDL_FINGERUP:
1625             static if (sdlSupport >= SDLSupport.v2_0_12) {
1626                 event = new FingerUpEvent(sdlEvent.tfinger.touchId, sdlEvent.tfinger.fingerId, sdlEvent.tfinger.x,
1627                     sdlEvent.tfinger.y, sdlEvent.tfinger.dx, sdlEvent.tfinger.dy, sdlEvent.tfinger.pressure,
1628                     sdlEvent.tfinger.windowID);
1629             }
1630             else {
1631                 event = new FingerUpEvent(sdlEvent.tfinger.touchId, sdlEvent.tfinger.fingerId, sdlEvent.tfinger.x,
1632                     sdlEvent.tfinger.y, sdlEvent.tfinger.dx, sdlEvent.tfinger.dy, sdlEvent.tfinger.pressure);
1633             }
1634             break;
1635         }
1636 
1637         event.timestamp = sdlEvent.tfinger.timestamp;
1638         return event;
1639     }
1640 }
1641 
1642 /++
1643  + D class that wraps `SDL_FINGERMOTION` `SDL_Event`s
1644  +/
1645 class FingerMotionEvent : FingerEvent {
1646     this(ulong touchID, ulong fingerID, float x, float y, float dx, float dy, float pressure) {
1647         this.sdlEvent.type = SDL_FINGERMOTION;
1648         this.sdlEvent.tfinger.touchId = touchID.to!long;
1649         this.sdlEvent.tfinger.fingerId = fingerID.to!long;
1650         this.sdlEvent.tfinger.x = x;
1651         this.sdlEvent.tfinger.y = y;
1652         this.sdlEvent.tfinger.dx = dx;
1653         this.sdlEvent.tfinger.dy = dy;
1654         this.sdlEvent.tfinger.pressure = pressure;
1655     }
1656 
1657     static if (sdlSupport >= SDLSupport.v2_0_12) {
1658         this(ulong touchID, ulong fingerID, float x, float y, float dx, float dy, float pressure,
1659             uint windowID) {
1660             this.sdlEvent.type = SDL_FINGERMOTION;
1661             this.sdlEvent.tfinger.touchId = touchID.to!long;
1662             this.sdlEvent.tfinger.fingerId = fingerID.to!long;
1663             this.sdlEvent.tfinger.x = x;
1664             this.sdlEvent.tfinger.y = y;
1665             this.sdlEvent.tfinger.dx = dx;
1666             this.sdlEvent.tfinger.dy = dy;
1667             this.sdlEvent.tfinger.pressure = pressure;
1668             this.sdlEvent.tfinger.windowID = windowID;
1669         }
1670     }
1671 
1672     invariant {
1673         assert(this.sdlEvent.type == SDL_FINGERMOTION);
1674     }
1675 
1676     override string toString() const {
1677         static if (sdlSupport >= SDLSupport.v2_0_12) {
1678             return "dsdl2.FingerMotionEvent(%d, %d, %f, %f, %f, %f, %f, %d)".format(this.touchID, this.fingerID,
1679                 this.x, this.y, this.dx, this.dy, this.pressure, this.windowID);
1680         }
1681         else {
1682             return "dsdl2.FingerMotionEvent(%d, %d, %f, %f, %f, %f, %f)".format(this.touchID, this.fingerID,
1683                 this.x, this.y, this.dx, this.dy, this.pressure);
1684         }
1685     }
1686 }
1687 
1688 /++
1689  + D class that wraps `SDL_FINGERDOWN` `SDL_Event`s
1690  +/
1691 class FingerDownEvent : FingerEvent {
1692     this(ulong touchID, ulong fingerID, float x, float y, float dx, float dy, float pressure) {
1693         this.sdlEvent.type = SDL_FINGERDOWN;
1694         this.sdlEvent.tfinger.touchId = touchID.to!long;
1695         this.sdlEvent.tfinger.fingerId = fingerID.to!long;
1696         this.sdlEvent.tfinger.x = x;
1697         this.sdlEvent.tfinger.y = y;
1698         this.sdlEvent.tfinger.dx = dx;
1699         this.sdlEvent.tfinger.dy = dy;
1700         this.sdlEvent.tfinger.pressure = pressure;
1701     }
1702 
1703     static if (sdlSupport >= SDLSupport.v2_0_12) {
1704         this(ulong touchID, ulong fingerID, float x, float y, float dx, float dy, float pressure,
1705             uint windowID) {
1706             this.sdlEvent.type = SDL_FINGERDOWN;
1707             this.sdlEvent.tfinger.touchId = touchID.to!long;
1708             this.sdlEvent.tfinger.fingerId = fingerID.to!long;
1709             this.sdlEvent.tfinger.x = x;
1710             this.sdlEvent.tfinger.y = y;
1711             this.sdlEvent.tfinger.dx = dx;
1712             this.sdlEvent.tfinger.dy = dy;
1713             this.sdlEvent.tfinger.pressure = pressure;
1714             this.sdlEvent.tfinger.windowID = windowID;
1715         }
1716     }
1717 
1718     invariant {
1719         assert(this.sdlEvent.type == SDL_FINGERDOWN);
1720     }
1721 
1722     override string toString() const {
1723         static if (sdlSupport >= SDLSupport.v2_0_12) {
1724             return "dsdl2.FingerDownEvent(%d, %d, %f, %f, %f, %f, %f, %d)".format(this.touchID, this.fingerID,
1725                 this.x, this.y, this.dx, this.dy, this.pressure, this.windowID);
1726         }
1727         else {
1728             return "dsdl2.FingerDownEvent(%d, %d, %f, %f, %f, %f, %f)".format(this.touchID, this.fingerID,
1729                 this.x, this.y, this.dx, this.dy, this.pressure);
1730         }
1731     }
1732 }
1733 
1734 /++
1735  + D class that wraps `SDL_FINGERUP` `SDL_Event`s
1736  +/
1737 class FingerUpEvent : FingerEvent {
1738     this(ulong touchID, ulong fingerID, float x, float y, float dx, float dy, float pressure) {
1739         this.sdlEvent.type = SDL_FINGERUP;
1740         this.sdlEvent.tfinger.touchId = touchID.to!long;
1741         this.sdlEvent.tfinger.fingerId = fingerID.to!long;
1742         this.sdlEvent.tfinger.x = x;
1743         this.sdlEvent.tfinger.y = y;
1744         this.sdlEvent.tfinger.dx = dx;
1745         this.sdlEvent.tfinger.dy = dy;
1746         this.sdlEvent.tfinger.pressure = pressure;
1747     }
1748 
1749     static if (sdlSupport >= SDLSupport.v2_0_12) {
1750         this(ulong touchID, ulong fingerID, float x, float y, float dx, float dy, float pressure,
1751             uint windowID) {
1752             this.sdlEvent.type = SDL_FINGERUP;
1753             this.sdlEvent.tfinger.touchId = touchID.to!long;
1754             this.sdlEvent.tfinger.fingerId = fingerID.to!long;
1755             this.sdlEvent.tfinger.x = x;
1756             this.sdlEvent.tfinger.y = y;
1757             this.sdlEvent.tfinger.dx = dx;
1758             this.sdlEvent.tfinger.dy = dy;
1759             this.sdlEvent.tfinger.pressure = pressure;
1760             this.sdlEvent.tfinger.windowID = windowID;
1761         }
1762     }
1763 
1764     invariant {
1765         assert(this.sdlEvent.type == SDL_FINGERUP);
1766     }
1767 
1768     override string toString() const {
1769         static if (sdlSupport >= SDLSupport.v2_0_12) {
1770             return "dsdl2.FingerUpEvent(%d, %d, %f, %f, %f, %f, %f, %d)".format(this.touchID, this.fingerID,
1771                 this.x, this.y, this.dx, this.dy, this.pressure, this.windowID);
1772         }
1773         else {
1774             return "dsdl2.FingerUpEvent(%d, %d, %f, %f, %f, %f, %f)".format(this.touchID, this.fingerID,
1775                 this.x, this.y, this.dx, this.dy, this.pressure);
1776         }
1777     }
1778 }
1779 
1780 /++
1781  + D class that wraps `SDL_MULTIGESTURE` `SDL_Event`s
1782  +/
1783 class MultiGestureEvent : Event {
1784     this(ulong touchID, float dTheta, float dDist, float x, float y, ushort numFingers) {
1785         this.sdlEvent.type = SDL_MULTIGESTURE;
1786         this.sdlEvent.mgesture.touchId = touchID.to!long;
1787         this.sdlEvent.mgesture.dTheta = dTheta;
1788         this.sdlEvent.mgesture.dDist = dDist;
1789         this.sdlEvent.mgesture.x = x;
1790         this.sdlEvent.mgesture.y = y;
1791         this.sdlEvent.mgesture.numFingers = numFingers;
1792     }
1793 
1794     invariant {
1795         assert(this.sdlEvent.type == SDL_MULTIGESTURE);
1796     }
1797 
1798     override string toString() const {
1799         return "dsdl2.MultiGestureEvent(%d, %f, %f, %f, %f, %d)".format(this.touchID,
1800             this.dTheta, this.dDist, this.x, this.y, this.numFingers);
1801     }
1802 
1803     ref inout(ulong) touchID() return inout @property {
1804         return *cast(inout ulong*)&this.sdlEvent.mgesture.touchId;
1805     }
1806 
1807     ref inout(float) dTheta() return inout @property {
1808         return this.sdlEvent.mgesture.dTheta;
1809     }
1810 
1811     ref inout(float) dDist() return inout @property {
1812         return this.sdlEvent.mgesture.dDist;
1813     }
1814 
1815     ref inout(float) x() return inout @property {
1816         return this.sdlEvent.mgesture.x;
1817     }
1818 
1819     ref inout(float) y() return inout @property {
1820         return this.sdlEvent.mgesture.y;
1821     }
1822 
1823     ref inout(ushort) numFingers() return inout @property {
1824         return this.sdlEvent.mgesture.numFingers;
1825     }
1826 }
1827 
1828 /++
1829  + D abstract class that wraps dollar gesture `SDL_Event`s
1830  +/
1831 abstract class DollarEvent : Event {
1832     invariant {
1833         assert(this.sdlEvent.type == SDL_DOLLARGESTURE || this.sdlEvent.type == SDL_DOLLARRECORD);
1834     }
1835 
1836     ref inout(ulong) touchID() return inout @property {
1837         return *cast(inout ulong*)&this.sdlEvent.dgesture.touchId;
1838     }
1839 
1840     ref inout(ulong) gestureID() return inout @property {
1841         return *cast(inout ulong*)&this.sdlEvent.dgesture.gestureId;
1842     }
1843 
1844     static Event fromSDL(SDL_Event sdlEvent)
1845     in {
1846         assert(sdlEvent.type == SDL_DOLLARGESTURE || sdlEvent.type == SDL_DOLLARRECORD);
1847     }
1848     do {
1849         Event event;
1850         switch (sdlEvent.type) {
1851         default:
1852             assert(false);
1853 
1854         case SDL_DOLLARGESTURE:
1855             event = new DollarGestureEvent(sdlEvent.dgesture.touchId, sdlEvent.dgesture.gestureId,
1856                 sdlEvent.dgesture.numFingers, sdlEvent.dgesture.error, sdlEvent.dgesture.x, sdlEvent.dgesture.y);
1857             break;
1858 
1859         case SDL_DOLLARRECORD:
1860             event = new DollarRecordEvent(sdlEvent.dgesture.touchId, sdlEvent.dgesture.gestureId);
1861             break;
1862         }
1863 
1864         event.timestamp = sdlEvent.tfinger.timestamp;
1865         return event;
1866     }
1867 }
1868 
1869 /++
1870  + D class that wraps `SDL_DOLLARGESTURE` `SDL_Event`s
1871  +/
1872 class DollarGestureEvent : DollarEvent {
1873     this(ulong touchID, ulong gestureID, uint numFingers, float error, float x, float y) {
1874         this.sdlEvent.type = SDL_DOLLARGESTURE;
1875         this.sdlEvent.dgesture.touchId = touchID.to!long;
1876         this.sdlEvent.dgesture.gestureId = gestureID.to!long;
1877         this.sdlEvent.dgesture.numFingers = numFingers;
1878         this.sdlEvent.dgesture.error = error;
1879         this.sdlEvent.dgesture.x = x;
1880         this.sdlEvent.dgesture.y = y;
1881     }
1882 
1883     invariant {
1884         assert(this.sdlEvent.type == SDL_DOLLARGESTURE);
1885     }
1886 
1887     override string toString() const {
1888         return "dsdl2.DollarGestureEvent(%d, %d, %d, %f, %f, %f)".format(this.touchID, this.gestureID, this.numFingers,
1889             this.error, this.x, this.y);
1890     }
1891 
1892     ref inout(uint) numFingers() return inout @property {
1893         return this.sdlEvent.dgesture.numFingers;
1894     }
1895 
1896     ref inout(float) error() return inout @property {
1897         return this.sdlEvent.dgesture.error;
1898     }
1899 
1900     ref inout(float) x() return inout @property {
1901         return this.sdlEvent.dgesture.x;
1902     }
1903 
1904     ref inout(float) y() return inout @property {
1905         return this.sdlEvent.dgesture.y;
1906     }
1907 }
1908 
1909 /++
1910  + D class that wraps `SDL_DOLLARRECORD` `SDL_Event`s
1911  +/
1912 class DollarRecordEvent : DollarEvent {
1913     this(ulong touchID, ulong gestureID) {
1914         this.sdlEvent.type = SDL_DOLLARRECORD;
1915         this.sdlEvent.dgesture.touchId = touchID.to!long;
1916         this.sdlEvent.dgesture.gestureId = gestureID.to!long;
1917     }
1918 
1919     invariant {
1920         assert(this.sdlEvent.type == SDL_DOLLARRECORD);
1921     }
1922 
1923     override string toString() const {
1924         return "dsdl2.DollarRecordEvent(%d, %d)".format(this.touchID, this.gestureID);
1925     }
1926 }
1927 
1928 /++
1929  + D abstract class that wraps drop `SDL_Event`s
1930  +/
1931 abstract class DropEvent : Event {
1932     ~this() @trusted {
1933         if (this.sdlEvent.drop.file !is null) {
1934             SDL_free(this.sdlEvent.drop.file);
1935         }
1936     }
1937 
1938     invariant {
1939         static if (sdlSupport >= SDLSupport.v2_0_5) {
1940             assert(this.sdlEvent.type == SDL_DROPFILE || this.sdlEvent.type == SDL_DROPTEXT
1941                     || this.sdlEvent.type == SDL_DROPBEGIN || this.sdlEvent.type == SDL_DROPCOMPLETE);
1942         }
1943         else {
1944             assert(this.sdlEvent.type == SDL_DROPFILE);
1945         }
1946     }
1947 
1948     static if (sdlSupport >= SDLSupport.v2_0_5) {
1949         ref inout(uint) windowID() return inout @property @trusted {
1950             return this.sdlEvent.drop.windowID;
1951         }
1952     }
1953 
1954     static Event fromSDL(SDL_Event sdlEvent) @trusted
1955     in {
1956         static if (sdlSupport >= SDLSupport.v2_0_5) {
1957             assert(sdlEvent.type == SDL_DROPFILE || sdlEvent.type == SDL_DROPTEXT
1958                     || sdlEvent.type == SDL_DROPBEGIN || sdlEvent.type == SDL_DROPCOMPLETE);
1959         }
1960         else {
1961             assert(sdlEvent.type == SDL_DROPFILE);
1962         }
1963     }
1964     do {
1965         Event event;
1966         switch (sdlEvent.type) {
1967         default:
1968             assert(false);
1969 
1970         case SDL_DROPFILE:
1971             static if (sdlSupport >= SDLSupport.v2_0_5) {
1972                 event = new DropFileEvent(sdlEvent.drop.file.to!string, sdlEvent.drop.windowID);
1973             }
1974             else {
1975                 event = new DropFileEvent(sdlEvent.drop.file.to!string);
1976             }
1977             break;
1978 
1979             static if (sdlSupport >= SDLSupport.v2_0_5) {
1980         case SDL_DROPTEXT:
1981                 event = new DropTextEvent(sdlEvent.drop.file.to!string, sdlEvent.drop.windowID);
1982                 break;
1983 
1984         case SDL_DROPBEGIN:
1985                 event = new DropBeginEvent(sdlEvent.drop.windowID);
1986                 break;
1987 
1988         case SDL_DROPCOMPLETE:
1989                 event = new DropCompleteEvent(sdlEvent.drop.windowID);
1990                 break;
1991             }
1992         }
1993 
1994         event.timestamp = sdlEvent.drop.timestamp;
1995         return event;
1996     }
1997 }
1998 
1999 /++
2000  + D class that wraps `SDL_DROPFILE` `SDL_Event`s
2001  +/
2002 class DropFileEvent : DropEvent {
2003     this(string file) @trusted {
2004         this.sdlEvent.type = SDL_DROPFILE;
2005         this.sdlEvent.drop.file = cast(char*) SDL_malloc(file.length + 1);
2006         this.sdlEvent.drop.file[0 .. file.length] = file[0 .. file.length];
2007         this.sdlEvent.drop.file[file.length] = '\0';
2008     }
2009 
2010     static if (sdlSupport >= SDLSupport.v2_0_5) {
2011         this(string file, uint windowID) @trusted {
2012             this.sdlEvent.type = SDL_DROPFILE;
2013             this.sdlEvent.drop.file = cast(char*) SDL_malloc(file.length + 1);
2014             this.sdlEvent.drop.file[0 .. file.length] = file[0 .. file.length];
2015             this.sdlEvent.drop.file[file.length] = '\0';
2016             this.sdlEvent.drop.windowID = windowID;
2017         }
2018     }
2019 
2020     @trusted invariant { // @suppress(dscanner.trust_too_much)
2021         assert(this.sdlEvent.type == SDL_DROPFILE);
2022         assert(this.sdlEvent.drop.file !is null);
2023     }
2024 
2025     override string toString() const {
2026         static if (sdlSupport >= SDLSupport.v2_0_5) {
2027             return "dsdl2.DropFileEvent(%s, %d)".format(this.file, this.windowID);
2028         }
2029         else {
2030             return "dsdl2.DropFileEvent(%s)".format(this.file);
2031         }
2032     }
2033 
2034     string file() const @property @trusted {
2035         return this.sdlEvent.drop.file.to!string;
2036     }
2037 
2038     void file(string newFile) @property @trusted {
2039         SDL_realloc(this.sdlEvent.drop.file, newFile.length + 1);
2040         this.sdlEvent.drop.file[0 .. newFile.length] = newFile[0 .. newFile.length];
2041         this.sdlEvent.drop.file[newFile.length] = '\0';
2042     }
2043 }
2044 
2045 static if (sdlSupport >= SDLSupport.v2_0_5) {
2046     /++
2047      + D class that wraps `SDL_DROPTEXT` `SDL_Event`s (from SDL 2.0.5)
2048      +/
2049     class DropTextEvent : DropEvent {
2050         this(string file, uint windowID) @trusted {
2051             this.sdlEvent.type = SDL_DROPTEXT;
2052             this.sdlEvent.drop.file = cast(char*) SDL_malloc(file.length + 1);
2053             this.sdlEvent.drop.file[0 .. file.length] = file[0 .. file.length];
2054             this.sdlEvent.drop.file[file.length] = '\0';
2055             this.sdlEvent.drop.windowID = windowID;
2056         }
2057 
2058         @trusted invariant { // @suppress(dscanner.trust_too_much)
2059             assert(this.sdlEvent.type == SDL_DROPTEXT);
2060             assert(this.sdlEvent.drop.file !is null);
2061         }
2062 
2063         override string toString() const {
2064             return "dsdl2.DropTextEvent(%s, %d)".format(this.file, this.windowID);
2065         }
2066 
2067         string file() const @property @trusted {
2068             return this.sdlEvent.drop.file.to!string;
2069         }
2070 
2071         void file(string newFile) @property @trusted {
2072             SDL_realloc(this.sdlEvent.drop.file, newFile.length + 1);
2073             this.sdlEvent.drop.file[0 .. newFile.length] = newFile[0 .. newFile.length];
2074             this.sdlEvent.drop.file[newFile.length] = '\0';
2075         }
2076     }
2077 
2078     /++
2079      + D class that wraps `SDL_DROPBEGIN` `SDL_Event`s (from SDL 2.0.5)
2080      +/
2081     class DropBeginEvent : DropEvent {
2082         this(uint windowID) @trusted {
2083             this.sdlEvent.type = SDL_DROPBEGIN;
2084             this.sdlEvent.drop.windowID = windowID;
2085         }
2086 
2087         invariant {
2088             assert(this.sdlEvent.type == SDL_DROPBEGIN);
2089         }
2090 
2091         override string toString() const {
2092             return "dsdl2.DropBeginEvent(%d)".format(this.windowID);
2093         }
2094     }
2095 
2096     /++
2097      + D class that wraps `SDL_DROPCOMPLETE` `SDL_Event`s (from SDL 2.0.5)
2098      +/
2099     class DropCompleteEvent : DropEvent {
2100         this(uint windowID) @trusted {
2101             this.sdlEvent.type = SDL_DROPCOMPLETE;
2102             this.sdlEvent.drop.windowID = windowID;
2103         }
2104 
2105         invariant {
2106             assert(this.sdlEvent.type == SDL_DROPCOMPLETE);
2107         }
2108 
2109         override string toString() const {
2110             return "dsdl2.DropCompleteEvent(%d)".format(this.windowID);
2111         }
2112     }
2113 }