1 /*
2 Copyright (c) 2014-2020 Timur Gafarov, Mateusz Muszyński
3 
4 Boost Software License - Version 1.0 - August 17th, 2003
5 
6 Permission is hereby granted, free of charge, to any person or organization
7 obtaining a copy of the software and accompanying documentation covered by
8 this license (the "Software") to use, reproduce, display, distribute,
9 execute, and transmit the Software, and to prepare derivative works of the
10 Software, and to permit third-parties to whom the Software is furnished to
11 do so, all subject to the following:
12 
13 The copyright notices in the Software and this entire statement, including
14 the above license grant, this restriction and the following disclaimer,
15 must be included in all copies of the Software, in whole or in part, and
16 all derivative works of the Software, unless such copies or derivative
17 works are solely in the form of machine-executable object code generated by
18 a source language processor.
19 
20 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
23 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
24 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
25 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26 DEALINGS IN THE SOFTWARE.
27 */
28 
29 module dagon.core.event;
30 
31 import std.stdio;
32 import std.ascii;
33 import std.conv;
34 import dlib.core.memory;
35 import dlib.core.ownership;
36 import dlib.container.array;
37 import dagon.core.bindings;
38 import dagon.core.input;
39 
40 enum EventType
41 {
42     KeyDown,
43     KeyUp,
44     TextInput,
45     MouseMotion,
46     MouseButtonDown,
47     MouseButtonUp,
48     MouseWheel,
49     JoystickButtonDown,
50     JoystickButtonUp,
51     JoystickAxisMotion,
52     Resize,
53     FocusLoss,
54     FocusGain,
55     Quit,
56     FileChange,
57     DropFile,
58     UserEvent
59 }
60 
61 struct Event
62 {
63     EventType type;
64     int key;
65     dchar unicode;
66     int button;
67     int joystickButton;
68     int joystickAxis;
69     float joystickAxisValue;
70     int width;
71     int height;
72     int userCode;
73     int mouseWheelX;
74     int mouseWheelY;
75     string filename;
76 }
77 
78 class EventManager
79 {
80     SDL_Window* window;
81 
82     enum maxNumEvents = 50;
83     Event[maxNumEvents] eventStack;
84     Event[maxNumEvents] userEventStack;
85     uint numEvents;
86     uint numUserEvents;
87 
88     bool running = true;
89 
90     bool[512] keyPressed = false;
91     bool[512] keyUp = false;
92     bool[512] keyDown = false;
93 
94     bool[255] mouseButtonPressed = false;
95     bool[255] mouseButtonUp = false;
96     bool[255] mouseButtonDown = false;
97 
98     bool[255] controllerButtonPressed = false;
99     bool[255] controllerButtonUp = false;
100     bool[255] controllerButtonDown = false;
101 
102     Array!(bool*) toReset; // Used for resetting UP and DOWN events after end of frame
103 
104     int mouseX = 0;
105     int mouseY = 0;
106     int mouseRelX = 0;
107     int mouseRelY = 0;
108     bool enableKeyRepeat = false;
109 
110     double deltaTime = 0.0;
111     uint deltaTimeMs = 0;
112 
113     uint windowWidth;
114     uint windowHeight;
115     bool windowFocused = true;
116 
117     SDL_GameController* controller = null;
118     SDL_Joystick* joystick = null;
119 
120     InputManager inputManager;
121 
122     this(SDL_Window* win, uint winWidth, uint winHeight)
123     {
124         window = win;
125 
126         windowWidth = winWidth;
127         windowHeight = winHeight;
128 
129         if(SDL_NumJoysticks() > 0)
130         {
131             SDL_GameControllerAddMappingsFromFile("gamecontrollerdb.txt");
132 
133             if (SDL_IsGameController(0))
134             {
135                 controller = SDL_GameControllerOpen(0);
136 
137                 if (SDL_GameControllerMapping(controller) is null)
138                     writeln("Warning: no mapping found for controller!");
139 
140                 SDL_GameControllerEventState(SDL_ENABLE);
141             }
142             else
143             {
144                 joystick = SDL_JoystickOpen(0);
145             }
146         }
147 
148         toReset = Array!(bool*)();
149 
150         inputManager = New!InputManager(this);
151     }
152 
153     ~this()
154     {
155         toReset.free();
156         Delete(inputManager);
157     }
158 
159     void exit()
160     {
161         running = false;
162     }
163 
164     void addEvent(Event e)
165     {
166         if (numEvents < maxNumEvents)
167         {
168             eventStack[numEvents] = e;
169             numEvents++;
170         }
171         else
172             writeln("Warning: event stack overflow");
173     }
174 
175     void generateFileChangeEvent(string filename)
176     {
177         Event e = Event(EventType.FileChange);
178         e.filename = filename;
179         addUserEvent(e);
180     }
181 
182     void addUserEvent(Event e)
183     {
184         if (numUserEvents < maxNumEvents)
185         {
186             userEventStack[numUserEvents] = e;
187             numUserEvents++;
188         }
189         else
190             writeln("Warning: user event stack overflow");
191     }
192 
193     void generateUserEvent(int code)
194     {
195         Event e = Event(EventType.UserEvent);
196         e.userCode = code;
197         addUserEvent(e);
198     }
199 
200     bool gameControllerAvailable()
201     {
202         return (controller !is null);
203     }
204 
205     bool joystickAvailable()
206     {
207         return (joystick !is null || controller !is null);
208     }
209 
210     float gameControllerAxis(int axis)
211     {
212         return cast(float)(SDL_GameControllerGetAxis(controller, cast(SDL_GameControllerAxis)axis)) / 32768.0f;
213     }
214 
215     float joystickAxis(int axis)
216     {
217         if (joystick)
218         {
219             double a = cast(double)(SDL_JoystickGetAxis(joystick, axis));
220             return a / 32768.0f;
221         }
222         else if (controller)
223         {
224             return cast(float)(SDL_GameControllerGetAxis(controller, cast(SDL_GameControllerAxis)axis)) / 32768.0f;
225         }
226         else return 0.0;
227     }
228 
229     void update()
230     {
231         numEvents = 0;
232 
233         mouseRelX = 0;
234         mouseRelY = 0;
235 
236         for (uint i = 0; i < numUserEvents; i++)
237         {
238             Event e = userEventStack[i];
239             addEvent(e);
240         }
241 
242         numUserEvents = 0;
243 
244         SDL_Event event;
245 
246         while(SDL_PollEvent(&event))
247         {
248             Event e;
249             switch (event.type)
250             {
251                 case SDL_KEYDOWN:
252                     if (event.key.repeat && !enableKeyRepeat)
253                         break;
254 
255                     keyPressed[event.key.keysym.scancode] = true;
256                     keyDown[event.key.keysym.scancode] = true;
257                     keyUp[event.key.keysym.scancode] = false;
258                     toReset.insertBack(&keyDown[event.key.keysym.scancode]);
259 
260                     e = Event(EventType.KeyDown);
261                     e.key = event.key.keysym.scancode;
262                     addEvent(e);
263                     break;
264 
265                 case SDL_KEYUP:
266                     keyPressed[event.key.keysym.scancode] = false;
267                     keyDown[event.key.keysym.scancode] = false;
268                     keyUp[event.key.keysym.scancode] = true;
269                     toReset.insertBack(&keyUp[event.key.keysym.scancode]);
270 
271                     e = Event(EventType.KeyUp);
272                     e.key = event.key.keysym.scancode;
273                     addEvent(e);
274                     break;
275 
276                 case SDL_TEXTINPUT:
277                     e = Event(EventType.TextInput);
278                     char[] input = event.text.text;
279                     if ((input[0] & 0x80) == 0)
280                     {
281                         e.unicode = input[0];
282                     }
283                     else if ((input[0] & 0xE0) == 0xC0)
284                     {
285                         e.unicode = ((input[0] & 0x1F) << 6) | (input[1] & 0x3F);
286                     }
287                     else if ((input[0] & 0xF0) == 0xE0)
288                     {
289                         e.unicode = ((input[0] & 0x0F) << 12) | ((input[1] & 0x3F) << 6) | (input[2] & 0x3F);
290                     }
291                     else if ((input[0] & 0xF8) == 0xF0)
292                     {
293                         e.unicode = (((input[0] & 0x0F) << 18) | ((input[1] & 0x3F) << 12) | ((input[2] & 0x3F) << 6) | (input[3] & 0x3F));
294                     }
295                     addEvent(e);
296                     break;
297 
298                 case SDL_MOUSEMOTION:
299                     mouseX = event.motion.x;
300                     mouseY = event.motion.y;
301                     mouseRelX = event.motion.xrel;
302                     mouseRelY = event.motion.yrel;
303                     break;
304 
305                 case SDL_MOUSEBUTTONDOWN:
306                     mouseButtonPressed[event.button.button] = true;
307                     mouseButtonDown[event.button.button] = true;
308                     toReset.insertBack(&mouseButtonDown[event.button.button]);
309 
310                     e = Event(EventType.MouseButtonDown);
311                     e.button = event.button.button;
312                     addEvent(e);
313                     break;
314 
315                 case SDL_MOUSEBUTTONUP:
316                     mouseButtonPressed[event.button.button] = false;
317                     mouseButtonUp[event.button.button] = true;
318                     toReset.insertBack(&mouseButtonUp[event.button.button]);
319 
320                     e = Event(EventType.MouseButtonUp);
321                     e.button = event.button.button;
322                     addEvent(e);
323                     break;
324 
325                 case SDL_MOUSEWHEEL:
326                     e = Event(EventType.MouseWheel);
327                     e.mouseWheelX = event.wheel.x;
328                     e.mouseWheelY = event.wheel.y;
329                     addEvent(e);
330                     break;
331 
332                 case SDL_JOYBUTTONDOWN:
333                     if(joystick is null) break;
334                     if (event.jbutton.state == SDL_PRESSED)
335                         e = Event(EventType.JoystickButtonDown);
336                     else if (event.jbutton.state == SDL_RELEASED)
337                         e = Event(EventType.JoystickButtonUp);
338                     e.joystickButton = event.jbutton.button;
339                     addEvent(e);
340                     break;
341 
342                 case SDL_JOYBUTTONUP:
343                     // TODO: add state modification
344                     if(joystick is null) break;
345                     if (event.jbutton.state == SDL_PRESSED)
346                         e = Event(EventType.JoystickButtonDown);
347                     else if (event.jbutton.state == SDL_RELEASED)
348                         e = Event(EventType.JoystickButtonUp);
349                     e.joystickButton = event.jbutton.button;
350                     addEvent(e);
351                     break;
352 
353                 case SDL_CONTROLLERBUTTONDOWN:
354                     // TODO: add state modification
355                     controllerButtonPressed[event.cbutton.button] = true;
356                     controllerButtonDown[event.cbutton.button] = true;
357                     toReset.insertBack(&controllerButtonDown[event.cbutton.button]);
358 
359                     e = Event(EventType.JoystickButtonDown);
360                     e.joystickButton = event.cbutton.button;
361                     addEvent(e);
362                     break;
363 
364                 case SDL_CONTROLLERBUTTONUP:
365                     // TODO: add state modification
366                     controllerButtonPressed[event.cbutton.button] = false;
367                     controllerButtonUp[event.cbutton.button] = true;
368                     toReset.insertBack(&controllerButtonUp[event.cbutton.button]);
369 
370                     e = Event(EventType.JoystickButtonUp);
371                     e.joystickButton = event.cbutton.button;
372                     addEvent(e);
373                     break;
374 
375                 case SDL_CONTROLLERAXISMOTION:
376                     // TODO: add state modification
377                     e = Event(EventType.JoystickAxisMotion);
378                     e.joystickAxis = event.caxis.axis;
379                     e.joystickAxisValue = cast(float)event.caxis.value / 32768.0f;
380 
381                     if (controller)
382                     {
383                         if (e.joystickAxis == 0)
384                             e.joystickAxisValue = SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_LEFTY);
385                         if (e.joystickAxis == 1)
386                             e.joystickAxisValue = SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_LEFTX);
387 
388                         e.joystickAxisValue = e.joystickAxisValue / 32768.0f;
389                     }
390 
391                     addEvent(e);
392                     break;
393 
394                 case SDL_WINDOWEVENT:
395                     if (event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED)
396                     {
397                         windowWidth = event.window.data1;
398                         windowHeight = event.window.data2;
399                         e = Event(EventType.Resize);
400                         e.width = windowWidth;
401                         e.height = windowHeight;
402                         addEvent(e);
403                     }
404                     else if (event.window.event == SDL_WINDOWEVENT_FOCUS_GAINED)
405                     {
406                         e = Event(EventType.FocusGain);
407                         addEvent(e);
408                     }
409                     else if (event.window.event == SDL_WINDOWEVENT_FOCUS_LOST)
410                     {
411                         e = Event(EventType.FocusLoss);
412                         addEvent(e);
413                     }
414                     break;
415 
416                 case SDL_DROPFILE:
417                     e = Event(EventType.DropFile);
418                     e.filename = to!string(event.drop.file);
419                     addEvent(e);
420                     break;
421 
422                 case SDL_QUIT:
423                     exit();
424                     e = Event(EventType.Quit);
425                     addEvent(e);
426                     break;
427 
428                 default:
429                     break;
430             }
431         }
432     }
433 
434     protected int lastTime = 0;
435     void updateTimer()
436     {
437         int currentTime = SDL_GetTicks();
438         auto elapsedTime = currentTime - lastTime;
439         lastTime = currentTime;
440         deltaTimeMs = elapsedTime;
441         deltaTime = cast(double)(elapsedTime) * 0.001;
442     }
443 
444     void setMouse(int x, int y)
445     {
446         SDL_WarpMouseInWindow(window, x, y);
447         mouseX = x;
448         mouseY = y;
449     }
450 
451     void setMouseToCenter()
452     {
453         float x = (cast(float)windowWidth) / 2;
454         float y = (cast(float)windowHeight) / 2;
455         setMouse(cast(int)x, cast(int)y);
456     }
457 
458     void showCursor(bool mode)
459     {
460         SDL_ShowCursor(mode);
461     }
462 
463     float aspectRatio()
464     {
465         return cast(float)windowWidth / cast(float)windowHeight;
466     }
467 
468     void resetUpDown()
469     {
470         // reset all UP and DOWN events
471         foreach(key; toReset)
472         {
473             *key = false;
474         }
475         toReset.removeBack(cast(uint)toReset.length);
476     }
477 }
478 
479 abstract class EventListener: Owner
480 {
481     EventManager eventManager;
482     InputManager inputManager;
483     bool enabled = true;
484 
485     this(EventManager emngr, Owner owner)
486     {
487         super(owner);
488         eventManager = emngr;
489         if(emngr !is null)
490             inputManager = emngr.inputManager;
491     }
492 
493     protected void generateUserEvent(int code)
494     {
495         eventManager.generateUserEvent(code);
496     }
497 
498     void processEvents()
499     {
500         if (!enabled)
501             return;
502 
503         for (uint i = 0; i < eventManager.numEvents; i++)
504         {
505             Event* e = &eventManager.eventStack[i];
506             processEvent(e);
507         }
508     }
509 
510     void processEvent(Event* e)
511     {
512         switch(e.type)
513         {
514             case EventType.KeyDown:
515                 onKeyDown(e.key);
516                 break;
517             case EventType.KeyUp:
518                 onKeyUp(e.key);
519                 break;
520             case EventType.TextInput:
521                 onTextInput(e.unicode);
522                 break;
523             case EventType.MouseButtonDown:
524                 onMouseButtonDown(e.button);
525                 break;
526             case EventType.MouseButtonUp:
527                 onMouseButtonUp(e.button);
528                 break;
529             case EventType.MouseWheel:
530                 onMouseWheel(e.mouseWheelX, e.mouseWheelY);
531                 break;
532             case EventType.JoystickButtonDown:
533                 onJoystickButtonDown(e.joystickButton);
534                 break;
535             case EventType.JoystickButtonUp:
536                 onJoystickButtonUp(e.joystickButton);
537                 break;
538             case EventType.JoystickAxisMotion:
539                 onJoystickAxisMotion(e.joystickAxis, e.joystickAxisValue);
540                 break;
541             case EventType.Resize:
542                 onResize(e.width, e.height);
543                 break;
544             case EventType.FocusLoss:
545                 onFocusLoss();
546                 break;
547             case EventType.FocusGain:
548                 onFocusGain();
549                 break;
550             case EventType.Quit:
551                 onQuit();
552                 break;
553             case EventType.FileChange:
554                 onFileChange(e.filename);
555                 break;
556             case EventType.DropFile:
557                 onDropFile(e.filename);
558                 break;
559             case EventType.UserEvent:
560                 onUserEvent(e.userCode);
561                 break;
562             default:
563                 break;
564         }
565     }
566 
567     void onKeyDown(int key) {}
568     void onKeyUp(int key) {}
569     void onTextInput(dchar code) {}
570     void onMouseButtonDown(int button) {}
571     void onMouseButtonUp(int button) {}
572     void onMouseWheel(int x, int y) {}
573     void onJoystickButtonDown(int button) {}
574     void onJoystickButtonUp(int button) {}
575     void onJoystickAxisMotion(int axis, float value) {}
576     void onResize(int width, int height) {}
577     void onFocusLoss() {}
578     void onFocusGain() {}
579     void onQuit() {}
580     void onFileChange(string filename) {}
581     void onDropFile(string filename) {}
582     void onUserEvent(int code) {}
583 }