1 /++
2 A module for listening to incoming events from the window manager for their 
3 subsequent processing.
4 
5 Such a layer does not admit events directly to the data, but whether it can show
6 what is happening at the moment, which can serve as a cross-plotter tracking 
7 of events.
8 
9 Using the IEventHandler.nextEvent function, you can scroll through the queue of 
10 events that can be processed and at each queue, the programmer needs to track 
11 the events he needs by the functions of the same interface:
12 ---
13 while (event.nextEvent()) {
14     if (event.keyDown == Key.Space) foo();
15 }
16 ---
17 As you can see, we loop through each event and read what happened.
18 
19 Macros:
20     LREF = <a href="#$1">$1</a>
21     HREF = <a href="$1">$2</a>
22 
23 Authors: $(HREF https://github.com/TodNaz,TodNaz)
24 Copyright: Copyright (c) 2020 - 2021, TodNaz.
25 License: $(HREF https://github.com/TodNaz/Tida/blob/master/LICENSE,MIT)
26 +/
27 module tida.event;
28 
29 enum DeprecatedMethodSize = 
30 "This function is useless. Use the parameters `IWindow.width/IWindow.height`.";
31 
32 /// Mouse keys.
33 enum MouseButton
34 {
35     unknown = 0, /// Unknown mouse button
36     left = 1, /// Left mouse button
37     right = 3, /// Right mouse button
38     middle = 2 /// Middle mouse button
39 }
40 
41 /++
42 Interaction interface and receiving information from the joystick.
43 +/
44 interface IJoystick
45 {
46     import tida.vector;
47     
48     enum maximumAxes = vec!float(32767, 32767);
49     
50     /++
51     A method that returns the value of all axes of the controller. 
52     The maximum number of axes can be checked using the `.length` property:
53     ---
54     writeln("Axless count: ", joystick.axless.length);
55     ---
56     +/
57     @property int[] axless() @safe;
58     
59     /++
60     A method that returns the maximum number of buttons on the controller.
61     +/
62     @property uint maxButtons() @safe;
63     
64     /++
65     A method that returns the name of the controller.
66     +/
67     @property string name() @safe;
68     
69     /++
70     Method showing which button was pressed/released in the current event.
71     +/
72     @property int button() @safe;
73     
74     /++
75     Shows the state when any button was pressed.
76     +/
77     @property bool isButtonDown() @safe;
78     
79     /++
80     Shows the state when any button was released..
81     +/
82     @property bool isButtonUp() @safe;
83     
84     /++
85     Shows the state when any one of the axes has changed its state.
86     +/
87     @property bool isAxisMove() @safe;
88 
89     /++
90     Shows the value of the first axis on the controller. 
91     The value ranges from -1.0 to 1.0.
92     +/
93     final @property Vector!float xy() @safe
94     {
95         return vec!float(axless[0], axless[1]) / maximumAxes;
96     }
97     
98     /++
99     Shows the value of the second axis on the controller. 
100     The value ranges from -1.0 to 1.0.
101     +/
102     final @property Vector!float zr() @safe
103     {
104         return vec!float(axless[2], axless[3]) / maximumAxes;
105     }
106     
107     /++
108     Shows the value of the third axis on the controller. 
109     The value ranges from -1.0 to 1.0.
110     +/
111     final @property Vector!float uv() @safe
112     {
113         return vec!float(axless[4], axless[5]) / maximumAxes;
114     }
115     
116     /++
117     Shows the currently pressed key.
118     +/
119     final @property int buttonDown() @safe
120     {
121         return isButtonDown ? button : -1;
122     }
123     
124     /++
125     Shows the currently released key.
126     +/
127     final @property int buttonUp() @safe
128     {
129         return isButtonUp ? button : -1;
130     }
131 }
132 
133 version(Windows)
134 class Joystick : IJoystick
135 {
136     import core.sys.windows.windows;
137     import tida.vector;
138 
139 private:
140     EventHandler event;   
141 
142 public:
143     int id; // identificator
144     int numAxes; // Max axes
145     int numButtons; // Max buttons
146     int[] axisMin; // Minimum axes values
147     int[] axisMax; // Maximum axes values
148     int[] axisOffset; // Axes offset values
149     float[] axisScale; // Axes scale values
150     string namely; 
151     
152     int[] axesState; // Axes state
153     int[int] buttons;
154     
155     int[] _axis;
156 
157     static immutable(int[]) jid =
158     [
159         JOYSTICKID1,
160         JOYSTICKID2
161     ];
162     
163     static immutable(int[]) jbuttondown =
164     [
165         MM_JOY1BUTTONDOWN,
166         MM_JOY2BUTTONDOWN 
167     ];
168     
169     static immutable(int[]) jbuttonup = 
170     [
171         MM_JOY1BUTTONUP,
172         MM_JOY2BUTTONUP 
173     ];
174     
175     static immutable(int[]) jmove = 
176     [
177         MM_JOY1MOVE,
178         MM_JOY2MOVE
179     ];
180     
181     static immutable defAxisMin = -32768;
182     static immutable defAxisMax = 32767;
183     static immutable defAxisThreshold = (defAxisMax - defAxisMin) / 256;
184 
185 @safe:
186     this(int id, EventHandler event)
187     {
188         this.event = event;
189         this.id = id;
190     }
191     
192 override:
193     @property int[] axless()
194     {
195         return axesState;
196     }
197     
198     @property uint maxButtons()
199     {
200         return numButtons;
201     }
202     
203     @property string name()
204     {
205         return namely;
206     }
207 
208     @property bool isButtonDown()
209     {
210         if (event.currJEvent !is null)
211             return event.currJEvent.type == EventHandler.JoystickEventType.buttonPressed;
212         else
213             return false;
214     }
215     
216     @property bool isButtonUp()
217     {
218         if (event.currJEvent !is null)
219             return event.currJEvent.type == EventHandler.JoystickEventType.buttonReleased;
220         else
221             return false;
222     }
223     
224     @property bool isAxisMove()
225     {
226         if (event.currJEvent !is null)
227             return event.currJEvent.type == EventHandler.JoystickEventType.axisMove;
228         else
229             return false;
230     }
231     
232     @property int button()
233     {
234         if (event.currJEvent !is null)
235             return event.currJEvent.value;
236         else
237             return -1;
238     }
239 }
240 
241 version(Posix)
242 class Joystick : IJoystick
243 {
244     import std.stdio : File;
245     import core.sys.posix.sys.ioctl;
246     import tida.vector;
247 
248 private:
249     int descriptor;
250 
251 public:
252     int id; // identificator
253     string namely;
254     
255     int numAxes; // Max axes
256     int numButtons; // Max buttons
257     
258     int[] _axis;
259 
260     // linux/joystick.h
261     enum JSIOCGAXES = _IOR!ubyte('j', 0x11);
262     enum JSIOCGBUTTONS = _IOR!ubyte('j', 0x12);
263     enum JSIOCGNAME(T) = _IOC!T(_IOC_READ, 'j', 0x13); 
264 
265     enum JS_EVENT_BUTTON = 0x01;    /* button pressed/released */
266     enum JS_EVENT_AXIS = 0x02;    /* joystick moved */
267     enum JS_EVENT_INIT = 0x80;    /* initial state of device */
268 
269     struct js_event
270     {
271         uint time; /* event timestamp in milliseconds */
272         short value;    /* value */
273         ubyte type;  /* event type */
274         ubyte number;    /* axis/button number */
275 
276         ubyte trueType() @safe nothrow pure
277         {
278             return type & ~JS_EVENT_INIT;
279         }
280     }
281 
282     js_event* currJEvent;
283 
284 @safe:
285     package(tida) @property int fd()
286     {
287         return descriptor;
288     }
289 
290     this(int descriptor, int id) @trusted
291     {
292         import std.conv : to;
293     
294         this.descriptor = descriptor;
295         this.id = id;
296 
297         char[80] __name;
298  
299         ioctl(descriptor, JSIOCGAXES, &numAxes);
300         ioctl(descriptor, JSIOCGBUTTONS, &numButtons);
301         ioctl(descriptor, JSIOCGNAME!(char[80]), &__name);
302         
303         namely = __name.to!string;
304         
305         _axis = new int[](numAxes);
306     }
307 
308     ~this() @trusted
309     {
310         import core.sys.posix.unistd;
311 
312         close(descriptor);
313         descriptor = -2;
314     }
315 
316 override:
317     @property int[] axless()
318     {
319         return _axis;
320     }
321 
322     @property uint maxButtons()
323     {
324         return numButtons;
325     }
326     
327     @property string name()
328     {
329         return namely;
330     }
331 
332     @property int button()
333     {
334         if (currJEvent !is null)
335             return currJEvent.number;
336         else
337             return -1;
338     }
339     
340     @property bool isButtonDown()
341     {
342         if (currJEvent !is null)
343             return currJEvent.trueType == JS_EVENT_BUTTON && currJEvent.value == 1;
344         else
345             return false;
346     }
347     
348     @property bool isButtonUp()
349     {
350         if (currJEvent !is null)
351             return currJEvent.trueType == JS_EVENT_BUTTON && currJEvent.value == 0;
352         else
353             return false;
354     }
355     
356     @property bool isAxisMove()
357     {
358         if (currJEvent !is null)
359             return currJEvent.trueType == JS_EVENT_AXIS;
360         else
361             return false;
362     }
363 }
364 
365 /++
366 Interface for cross-platform listening for events from the window manager.
367 +/
368 interface IEventHandler
369 {
370 @safe:
371     /++
372     Moves to the next event. If there are no more events, it returns false, 
373     otherwise, it throws true and the programmer can safely check which event 
374     s in the current queue.
375     +/
376     bool nextEvent();
377 
378     /++
379     Checking if any key is pressed in the current event.
380     +/
381     bool isKeyDown();
382 
383     /++
384     Checking if any key is released in the current event.
385     +/
386     bool isKeyUp();
387 
388     /++
389     Will return the key that was pressed. Returns zero if no key is pressed 
390     in the current event.
391     +/
392     @property int key();
393 
394     /++
395     Returns the key at the moment the key was pressed, 
396     otherwise it returns zero.
397     +/
398     @property final int keyDown()
399     {
400         return isKeyDown ? key : 0;
401     }
402 
403     /++
404     Returns the key at the moment the key was released, 
405     otherwise it returns zero.
406     +/
407     @property final int keyUp()
408     {
409         return isKeyUp ? key : 0;
410     }
411 
412     /++
413     Check if the mouse button is pressed in the current event.
414     +/
415     bool isMouseDown();
416 
417     /++
418     Check if the mouse button is released in the current event.
419     +/
420     bool isMouseUp();
421 
422     /++
423     Returns the currently pressed or released mouse button.
424     +/
425     @property MouseButton mouseButton();
426 
427     /++
428     Returns the mouse button at the moment the key was pressed; 
429     otherwise, it returns zero.
430     +/
431     @property final MouseButton mouseDownButton()
432     {
433         return isMouseDown ? mouseButton : MouseButton.unknown;
434     }
435 
436     /++
437     Returns the mouse button at the moment the key was released; 
438     otherwise, it returns zero.
439     +/
440     @property final MouseButton mouseUpButton()
441     {
442         return isMouseUp ? mouseButton : MouseButton.unknown;
443     }
444 
445     /++
446     Returns the position of the mouse in the window.
447     +/
448     @property int[2] mousePosition();
449 
450     /++
451     Returns in which direction the user is turning the mouse wheel. 
452     1 - down, -1 - up, 0 - does not twist. 
453 
454     This iteration is convenient for multiplying with some real movement coefficient.
455     +/
456     @property int mouseWheel();
457 
458     /++
459     Indicates whether the window has been resized in this event.
460     +/
461     bool isResize();
462 
463     /++
464     Returns the new size of the window 
465     
466     deprecated: 
467     although this will already be available directly in the 
468     window structure itself.
469     +/
470     deprecated(DeprecatedMethodSize) 
471     uint[2] newSizeWindow();
472 
473     /++
474     Indicates whether the user is attempting to exit the program.
475     +/
476     bool isQuit();
477 
478     /++
479     Indicates whether the user has entered text information.
480     +/
481     bool isInputText();
482 
483     /++
484     User entered data.
485     +/
486     @property string inputChar();
487 
488     @property wstring inputWChar();
489     
490     /++
491     Returns an array of the interface for controlling joysticks. 
492     If its length is zero, no joysticks were found.
493 
494     It is checked once, if one controller has been disabled, then it is recommended 
495     to zero the array as shown below and call again to rescan the joysticks.
496     ---
497     event.joysticks.length = 0;
498     ---
499     +/
500     @property Joystick[] joysticks();
501 
502 @trusted:
503     final int opApply(scope int delegate(ref int) dg)
504     {
505         int count = 0;
506 
507         while (this.nextEvent()) 
508         {
509             dg(++count);
510         }
511 
512         return 0;
513     }
514 }
515 
516 version(Posix)
517 class EventHandler : IEventHandler
518 {
519     import x11.X, x11.Xlib, x11.Xutil;
520     import tida.window, tida.runtime;
521 
522 private:
523     struct JoystickEvent
524     {
525         int id;
526         Joystick.js_event data;
527     }
528 
529     tida.window.Window[] windows;
530     Atom destroyWindowEvent;
531     _XIC* ic;
532     Joystick[] _joysticks;
533     JoystickEvent[] jevents;
534 
535 public:
536     XEvent event;
537 
538 @trusted:
539     this(tida.window.Window window)
540     {   
541         this.windows ~= window;
542 
543         this.destroyWindowEvent = XInternAtom(runtime.display, "WM_DELETE_WINDOW", 0);
544 
545         ic = XCreateIC( XOpenIM(runtime.display, null, null, null), 
546                         XNInputStyle, XIMPreeditNothing | XIMStatusNothing, 
547                         XNClientWindow, this.windows[0].handle, null);
548         XSetICFocus(ic);
549         XSetLocaleModifiers("@im=none");
550     }
551     
552     void appendWindow(tida.window.Window window)
553     {
554         this.windows ~= window;
555     }
556     
557     @property tida.window.IWindow windowEvent()
558     {
559         foreach (window; windows)
560         {
561             if (window.handle == this.event.xany.window)
562                 return window;
563         }
564         
565         return null;
566     }
567 
568     int joyHandle()
569     {
570         import core.sys.posix.unistd;
571 
572         int count = 0;
573         foreach (ref e; _joysticks)
574         {
575             Joystick.js_event eEvent;
576             immutable bytes = read(e.fd(), &eEvent, Joystick.js_event.sizeof);
577 
578             if (eEvent.type != 0 &&
579                 ((eEvent.type & Joystick.JS_EVENT_INIT) != Joystick.JS_EVENT_INIT))
580             {
581                 jevents ~= JoystickEvent(e.id, eEvent);
582                 count++;
583             }
584         }
585 
586         return count;
587     }
588 
589     void validateJoysticks()
590     {
591         import std.algorithm : remove;
592         import core.sys.posix.fcntl;
593 
594         foreach (size_t i, ref e; _joysticks)
595         {
596             if (fcntl(e.fd(), F_GETFD) == -1)
597             {
598                 _joysticks = _joysticks.remove(i);
599             }
600         } 
601     }
602 
603 override:
604     bool nextEvent()
605     {
606         joyHandle();
607         immutable pen = XPending(runtime.display);
608 
609         if (pen != 0) 
610         {
611             XNextEvent(runtime.display, &this.event);
612 
613             return pen != 0;
614         } else
615         {
616             if (jevents.length != 0)
617             {
618                 auto currEvent = &jevents[0];
619                 jevents = jevents[1 .. $];
620 
621                 foreach (ref e; _joysticks)
622                 {
623                     if (e.id == currEvent.id)
624                     {
625                         e.currJEvent = &currEvent.data;
626                         if (currEvent.data.trueType == Joystick.JS_EVENT_AXIS)
627                         {
628                             e._axis[currEvent.data.number] = e.currJEvent.value;
629                         }
630                     } else
631                     {
632                         e.currJEvent = null;
633                     }
634                 }
635 
636                 return true;
637             } else
638             {
639                 foreach (ref e; _joysticks)
640                 {
641                     e.currJEvent = null;
642                 }
643 
644                 return false;
645             }
646         }
647     }
648 
649     bool isKeyDown()
650     {
651         return  this.event.type == KeyPress;
652     }
653 
654     bool isKeyUp()
655     {
656         return  this.event.type == KeyRelease;
657     }
658 
659     @property int key()
660     {
661         return this.event.xkey.keycode;
662     }
663 
664     bool isMouseDown()
665     {
666         return  this.event.type == ButtonPress;
667     }
668 
669     bool isMouseUp()
670     {
671         return  this.event.type == ButtonRelease;
672     }
673 
674     @property MouseButton mouseButton()
675     {
676         return cast(MouseButton) this.event.xbutton.button;
677     }
678 
679     @property int[2] mousePosition() @trusted
680     {
681         return [this.event.xmotion.x, this.event.xmotion.y];
682     }
683 
684     @property int mouseWheel()
685     {
686         return this.isMouseDown ? 
687             (this.mouseButton == 4 ? -1 : (this.mouseButton == 5 ? 1 : 0)) : 0;
688     }
689 
690     bool isResize()
691     {
692         return 	this.event.type == ConfigureNotify &&
693         		this.event.xconfigure.type == 22 &&
694         		!this.event.xconfigure.send_event;
695     }
696 
697     uint[2] newSizeWindow()
698     {
699         XWindowAttributes attr;
700         XGetWindowAttributes(   runtime.display, 
701                                 (cast(tida.window.Window) this.windows[0]).handle, &attr);
702 
703         return [attr.width, attr.height];
704     }
705 
706     bool isQuit()
707     {
708         return  this.event.xclient.data.l[0] == this.destroyWindowEvent;
709     }
710 
711     bool isInputText() 
712     {
713         return this.isKeyDown;
714     } 
715 
716     @property string inputChar()
717     {
718         int count;
719         string buf = new string(20);
720         KeySym ks;
721         Status status = 0;
722 
723         count = Xutf8LookupString(  this.ic, cast(XKeyPressedEvent*) &this.event.xkey, 
724                                     cast(char*) buf.ptr, 20, &ks, &status);
725 
726         return buf[0 .. count];
727     }
728 
729     @property wstring inputWChar()
730     {
731         import std.utf : toUTF16;
732 
733         int count;
734         string buf = new string(20);
735         KeySym ks;
736         Status status = 0;
737 
738         count = Xutf8LookupString(  this.ic, cast(XKeyPressedEvent*) &this.event.xkey,
739                                     cast(char*) buf.ptr, 20, &ks, &status);
740 
741         return buf[0 .. count].toUTF16;
742     }
743 
744     @property Joystick[] joysticks()
745     {
746         import core.sys.posix.fcntl;
747         import std.conv : to;
748 
749         if (_joysticks.length != 0)
750         {
751             validateJoysticks();
752             return _joysticks;
753         }
754 
755         foreach (i; 0 .. 2)
756         {
757             int fd = open(("/dev/input/js" ~ i.to!string).ptr, 0);
758             if (fd == -1)
759                 continue;
760 
761             immutable flags = fcntl(fd, F_GETFL, 0);
762             fcntl(fd, F_SETFL, flags | O_NONBLOCK);
763 
764             _joysticks ~= new Joystick(fd, i);
765         }
766 
767         return _joysticks;
768     }
769 }
770 
771 version(Windows)
772 class EventHandler : IEventHandler
773 {
774     import tida.window, tida.runtime;
775     import core.sys.windows.windows;
776 
777 private:
778     tida.window.Window window;
779     Joystick[] _joysticks;
780 
781 public:
782     MSG msg;
783     
784     enum JoystickEventType
785     {
786         axisMove,
787         buttonPressed,
788         buttonReleased
789     }
790     
791     enum JoystickAxis
792     {
793         X = JOY_RETURNX,
794         Y = JOY_RETURNY,
795         Z = JOY_RETURNZ,
796         R = JOY_RETURNR,
797         U = JOY_RETURNU,
798         V = JOY_RETURNV
799     }
800     
801     struct JoystickEvent
802     {
803         int id;
804         JoystickEventType type;
805         int value;
806         
807         JoystickAxis axis;
808     }
809     
810     JoystickEvent[] jEvents;
811     JoystickEvent* currJEvent = null;
812 
813 @safe:
814     this(tida.window.Window window)
815     {
816         this.window = window;
817     }
818 
819 @trusted:
820     void joyPeek()
821     {
822         immutable flagsAxis = [
823             JOY_RETURNX,
824             JOY_RETURNY,
825             JOY_RETURNZ,
826             JOY_RETURNR,
827             JOY_RETURNU,
828             JOY_RETURNV
829         ];
830         
831         foreach (ref e; _joysticks)
832         {
833             JOYINFOEX joyInfo;
834             joyInfo.dwSize = joyInfo.sizeof;
835             joyInfo.dwFlags = JOY_RETURNALL;
836             
837             joyGetPosEx(Joystick.jid[e.id], &joyInfo);
838             
839             immutable axisPos = [
840                 joyInfo.dwXpos,
841                 joyInfo.dwYpos,
842                 joyInfo.dwZpos,
843                 joyInfo.dwRpos,
844                 joyInfo.dwUpos,
845                 joyInfo.dwVpos
846             ];
847             
848             foreach (i; 0 .. e.numAxes)
849             {
850                 if (!(joyInfo.dwFlags & flagsAxis[i]))
851                     continue;
852                     
853                 immutable int value = cast(int) (cast(float) (axisPos[i]) + e.axisOffset[i] * e.axisScale[i]);
854                 immutable change = (value - e._axis[i]);
855                 
856                 if (change > -Joystick.defAxisThreshold &&
857                     change < Joystick.defAxisThreshold)
858                     continue;
859                 
860                 e._axis[i] = value;
861                 e.axesState[i] = !(value < 300 && value > -300) ? value : 0;
862                 
863                 JoystickEvent jevent;
864                 jevent.id = e.id;
865                 jevent.type = JoystickEventType.axisMove;
866                 jevent.axis = cast(JoystickAxis) flagsAxis[i];
867                 jevent.value = !(value < 300 && value > -300) ? value : 0;
868                 jEvents ~= jevent;
869             }
870             
871             if (joyInfo.dwFlags & JOY_RETURNBUTTONS)
872             {
873                 foreach (i; 0 .. e.numButtons)
874                 {
875                     int pressed = joyInfo.dwButtons & (1 << i);
876                 
877                     if (pressed == 0)
878                     {
879                         if (1 << i in e.buttons)
880                         if (e.buttons[1 << i] != 0)
881                         {
882                             JoystickEvent jevent;
883                             jevent.id = e.id;
884                             jevent.type = JoystickEventType.buttonReleased;
885                             jevent.value = 1 << i;
886                             jEvents ~= jevent;
887                             e.buttons[1 << i] = 0;
888                         }
889                         
890                         continue;
891                     }
892                     
893                     if (1 << i in e.buttons)
894                     {
895                         if(e.buttons[1 << i] != 0)
896                             continue;
897                     }
898                      
899                     e.buttons[1 << i] = 1;   
900                     JoystickEvent jevent;
901                     jevent.id = e.id;
902                     jevent.type = JoystickEventType.buttonPressed;
903                     jevent.value = 1 << i;
904                     jEvents ~= jevent;
905                 }
906             }
907         }
908     }
909 
910     bool nextEvent()
911     {
912         TranslateMessage(&this.msg); 
913         DispatchMessage(&this.msg);
914         
915         joyPeek();
916         if (PeekMessage(&this.msg, this.window.handle, 0, 0, PM_REMOVE) == 0)
917         {
918             if (jEvents.length == 0)
919             {
920                 currJEvent = null;
921                 return false;
922             }
923             else
924             {
925                 currJEvent = &jEvents[0];
926                 jEvents = jEvents[1 .. $];
927                 
928                 return true;
929             }
930         } else
931         {
932             return true;
933         }
934     }
935 
936     bool isKeyDown()
937     {
938         return this.msg.message == WM_KEYDOWN;
939     }
940 
941     bool isKeyUp()
942     {
943         return this.msg.message == WM_KEYUP;
944     }
945 
946     @property int key()
947     {
948         return cast(int) this.msg.wParam;
949     }
950 
951     bool isMouseDown()
952     {
953         return this.msg.message == WM_LBUTTONDOWN ||
954                this.msg.message == WM_RBUTTONDOWN ||
955                this.msg.message == WM_MBUTTONDOWN;
956     }
957 
958     bool isMouseUp()
959     {
960         return this.msg.message == WM_LBUTTONUP ||
961                this.msg.message == WM_RBUTTONUP ||
962                this.msg.message == WM_MBUTTONUP;
963     }
964 
965     @property MouseButton mouseButton()
966     {
967         if (this.msg.message == WM_LBUTTONUP || this.msg.message == WM_LBUTTONDOWN)
968             return MouseButton.left;
969 
970         if (this.msg.message == WM_RBUTTONUP || this.msg.message == WM_RBUTTONDOWN)
971             return MouseButton.right;
972 
973         if (this.msg.message == WM_MBUTTONUP || this.msg.message == WM_MBUTTONDOWN)
974             return MouseButton.middle;
975 
976         return MouseButton.unknown;
977     }
978 
979     @property int[2] mousePosition()
980     {
981         POINT p;
982         GetCursorPos(&p);
983         ScreenToClient((cast(Window) this.window).handle, &p);
984 
985         return [p.x, p.y];
986     }
987 
988     @property int mouseWheel()
989     {
990         if (this.msg.message != WM_MOUSEWHEEL) return 0;
991 
992         return (cast(int) this.msg.wParam) > 0 ? -1 : 1;
993     }
994 
995     bool isResize()
996     {
997         bool isResize = window.isResize;
998         window.isResize = false;
999 
1000         return isResize;
1001     }
1002 
1003     uint[2] newSizeWindow()
1004     {
1005         RECT rect;
1006         GetWindowRect((cast(Window) this.window).handle, &rect);
1007 
1008         return [rect.right, rect.bottom];
1009     }
1010 
1011     bool isQuit()
1012     {
1013         return window.isClose;
1014     }
1015 
1016     bool isInputText()
1017     {
1018         return this.msg.message == WM_CHAR;
1019     }
1020 
1021     string inputChar()
1022     {
1023         import std.utf : toUTF8;
1024 
1025         wstring text = [];
1026         text = [cast(wchar) msg.wParam];
1027 
1028         string utftext = text.toUTF8;
1029 
1030         return [utftext[0]];
1031     }
1032 
1033     wstring inputWChar()
1034     {
1035         return [cast(wchar) msg.wParam];
1036     }
1037     
1038     @property Joystick[] joysticks() @trusted
1039     {
1040         import std.conv : to;
1041     
1042         if (_joysticks.length != 0)
1043             return _joysticks;
1044             
1045         immutable numDevs = joyGetNumDevs();
1046         if (numDevs == 0)
1047             return [];
1048         
1049         foreach (i; 0 .. 2)
1050         {
1051             JOYINFOEX jInfo;
1052             JOYCAPS jCaps;
1053             
1054             if (joyGetPosEx(Joystick.jid[i], &jInfo) == JOYERR_UNPLUGGED)
1055             {
1056                 continue;   
1057             }
1058             
1059             if (joySetCapture(window.handle, Joystick.jid[i], 0, true))
1060             {
1061                 continue;
1062             }
1063             
1064             joyGetDevCaps(Joystick.jid[i], &jCaps, jCaps.sizeof);
1065             
1066             auto jj =  new Joystick(i, this);
1067             jj.numAxes = jCaps.wNumAxes;
1068             jj.numButtons = jCaps.wNumButtons;
1069             jj.axesState = new int[](jj.numAxes);
1070             jj.namely = jCaps.szPname.to!string;
1071             
1072             immutable wAxisMin = [
1073                 jCaps.wXmin,
1074                 jCaps.wYmin,
1075                 jCaps.wZmin,
1076                 jCaps.wRmin,
1077                 jCaps.wUmin,
1078                 jCaps.wVmin
1079             ];
1080             
1081             immutable wAxisMax = [
1082                 jCaps.wXmax,
1083                 jCaps.wYmax,
1084                 jCaps.wZmax,
1085                 jCaps.wRmax,
1086                 jCaps.wUmax,
1087                 jCaps.wVmax
1088             ];
1089                         
1090             foreach (j; 0 .. jj.numAxes)
1091             {
1092                 jj._axis ~= 0;
1093                 jj.axisMin ~= wAxisMin[i];
1094                 jj.axisMax ~= wAxisMax[i];
1095                 jj.axisOffset ~= Joystick.defAxisMin - wAxisMin[i];
1096                 jj.axisScale ~= (cast(float) Joystick.defAxisMax - cast(float) (Joystick.defAxisMin)) / (cast(float) wAxisMax[i] - cast(float) wAxisMin[i]);
1097             }
1098             
1099             _joysticks ~= jj;
1100         }
1101         
1102         return _joysticks;
1103     }
1104 }
1105 
1106 version(Posix)///
1107 static enum Key
1108 {
1109     Escape = 9,
1110     F1 = 67,
1111     F2 = 68,
1112     F3 = 69,
1113     F4 = 70,
1114     F5 = 71,
1115     F6 = 72,
1116     F7 = 73,
1117     F8 = 74,
1118     F9 = 75,
1119     F10 = 76,
1120     F11 = 95,
1121     F12 = 96,
1122     PrintScrn = 111,
1123     ScrollLock = 78,
1124     Pause = 110,
1125     Backtick = 49,
1126     K1 = 10,
1127     K2 = 11,
1128     K3 = 12,
1129     K4 = 13,
1130     K5 = 14,
1131     K6 = 15,
1132     K7 = 16,
1133     K8 = 17,
1134     K9 = 18,
1135     K0 = 19,
1136     Minus = 20,
1137     Equal = 21,
1138     Backspace = 22,
1139     Insert = 106,
1140     Home = 97,
1141     PageUp = 99,
1142     NumLock = 77,
1143     KPSlash = 112,
1144     KPStar = 63,
1145     KPMinus = 82,
1146     Tab = 23,
1147     Q = 24,
1148     W = 25,
1149     E = 26,
1150     R = 27,
1151     T = 28,
1152     Y = 29,
1153     U = 30,
1154     I = 31,
1155     O = 32,
1156     P = 33,
1157 
1158     SqBrackLeft = 34,
1159     SqBrackRight = 35,
1160     SquareBracketLeft = 34,
1161     SquareBracketRight = 35,
1162 
1163     Return = 36,
1164     Delete = 107,
1165     End = 103,
1166     PageDown = 105,
1167 
1168     KP7 = 79,
1169     KP8 = 80,
1170     KP9 = 81,
1171 
1172     CapsLock = 66,
1173     A = 38,
1174     S = 39,
1175     D = 40,
1176     F = 41,
1177     G = 42,
1178     H = 43,
1179     J = 44,
1180     K = 45,
1181     L = 46,
1182     Semicolons = 47,
1183     Apostrophe = 48,
1184 
1185     KP4 = 83,
1186     KP5 = 84,
1187     KP6 = 85,
1188 
1189     ShiftLeft = 50,
1190     International = 94,
1191 
1192     Z = 52,
1193     X = 53,
1194     C = 54,
1195     V = 55,
1196     B = 56,
1197     N = 57,
1198     M = 58,
1199     Comma = 59,
1200     Point = 60,
1201     Slash = 61,
1202 
1203     ShiftRight = 62,
1204 
1205     BackSlash = 51,
1206     Up = 111,
1207 
1208     KP1 = 87,
1209     KP2 = 88,
1210     KP3 = 89,
1211 
1212     KPEnter = 108,
1213     CtrlLeft = 37,
1214     SuperLeft = 115,
1215     AltLeft = 64,
1216     Space = 65,
1217     AltRight = 113,
1218     LogoRight = 116,
1219     Menu = 117,
1220     CtrlRight = 109,
1221     Left = 113,
1222     Down = 116,
1223     Right = 114,
1224     KP0 = 90,
1225     KPPoint = 91
1226 }
1227 
1228 version(Windows)///
1229 static enum Key
1230 {
1231     Escape = 0x1B,
1232     F1 = 0x70,
1233     F2 = 0x71,
1234     F3 = 0x72,
1235     F4 = 0x73,
1236     F5 = 0x74,
1237     F6 = 0x75,
1238     F7 = 0x76,
1239     F8 = 0x77,
1240     F9 = 0x78,
1241     F10 = 0x79,
1242     F11 = 0x7A,
1243     F12 = 0x7B,
1244     PrintScrn = 0x2A,
1245     ScrollLock = 0x91,
1246     Pause = 0x13,
1247     Backtick = 0xC0,
1248     K1 = 0x31,
1249     K2 = 0x32,
1250     K3 = 0x33,
1251     K4 = 0x34,
1252     K5 = 0x35,
1253     K6 = 0x36,
1254     K7 = 0x37,
1255     K8 = 0x38,
1256     K9 = 0x39,
1257     K0 = 0x30,
1258     Minus = 0xBD,
1259     Equal = 0xBB,
1260     Backspace = 0x08,
1261     Insert = 0x2D,
1262     Home = 0x24,
1263     PageUp = 0x21,
1264     NumLock = 0x90,
1265     KPSlash = 0x6F,
1266     KPStar = 0xBB,
1267     KPMinus = 0xBD,
1268     Tab = 0x09,
1269     Q = 0x51,
1270     W = 0x57,
1271     E = 0x45,
1272     R = 0x52,
1273     T = 0x54,
1274     Y = 0x59,
1275     U = 0x55,
1276     I = 0x49,
1277     O = 0x4F,
1278     P = 0x50,
1279 
1280     SqBrackLeft = 0xDB,
1281     SqBrackRight = 0xDD,
1282     SquareBracketLeft = 0x30,
1283     SquareBracketRight = 0xBD,
1284 
1285     Return = 0x0D,
1286     Delete = 0x2E,
1287     End = 0x23,
1288     PageDown = 0x22,
1289 
1290     KP7 = 0x67,
1291     KP8 = 0x68,
1292     KP9 = 0x69,
1293 
1294     CapsLock = 0x14,
1295     A = 0x41,
1296     S = 0x53,
1297     D = 0x44,
1298     F = 0x46,
1299     G = 0x47,
1300     H = 0x48,
1301     J = 0x4A,
1302     K = 0x4B,
1303     L = 0x4C,
1304     Semicolons = 0xBA,
1305     Apostrophe = 0xBF,
1306 
1307     KP4 = 0x64,
1308     KP5 = 0x65,
1309     KP6 = 0x66,
1310 
1311     ShiftLeft = 0xA0,
1312     International = 0xA4,
1313 
1314     Z = 0x5A,
1315     X = 0x58,
1316     C = 0x43,
1317     V = 0x56,
1318     B = 0x42,
1319     N = 0x4E,
1320     M = 0x4D,
1321     Comma = 0xBC,
1322     Point = 0xBE,
1323     Slash = 0xBF,
1324 
1325     ShiftRight = 0xA1,
1326 
1327     BackSlash = 0xE2,
1328     Up = 0x26,
1329 
1330     KP1 = 0x61,
1331     KP2 = 0x62,
1332     KP3 = 0x63,
1333 
1334     KPEnter = 0x6A,
1335     CtrlLeft = 0xA2,
1336     SuperLeft = 0xA4,
1337     AltLeft = 0xA4,
1338     Space = 0x20,
1339     AltRight = 0xA5,
1340     SuperRight = 0xA5,
1341     Menu = 0,
1342     CtrlRight = 0xA3,
1343     Left = 0x25,
1344     Down = 0x28,
1345     Right = 0x27,
1346     KP0 = 0x60,
1347     KPPoint = 0x6F
1348 }