1 /*
2 Copyright (c) 2014-2022 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     bool[255] joystickButtonPressed = false;
103     bool[255] joystickButtonUp = false;
104     bool[255] joystickButtonDown = false;
105 
106     Array!(bool*) toReset; // Used for resetting UP and DOWN events after end of frame
107 
108     int mouseX = 0;
109     int mouseY = 0;
110     int mouseRelX = 0;
111     int mouseRelY = 0;
112     bool enableKeyRepeat = false;
113 
114     double deltaTime = 0.0;
115     uint deltaTimeMs = 0;
116 
117     uint windowWidth;
118     uint windowHeight;
119     bool windowFocused = true;
120 
121     SDL_GameController* controller = null;
122     SDL_Joystick* joystick = null;
123 
124     InputManager inputManager;
125     
126     void delegate(SDL_Event* event) onProcessEvent;
127 
128     this(SDL_Window* win, uint winWidth, uint winHeight)
129     {
130         window = win;
131 
132         windowWidth = winWidth;
133         windowHeight = winHeight;
134 
135         if(SDL_NumJoysticks() > 0)
136         {
137             SDL_GameControllerAddMappingsFromFile("gamecontrollerdb.txt");
138 
139             if (SDL_IsGameController(0))
140             {
141                 controller = SDL_GameControllerOpen(0);
142 
143                 if (SDL_GameControllerMapping(controller) is null)
144                     writeln("Warning: no mapping found for controller!");
145 
146                 SDL_GameControllerEventState(SDL_ENABLE);
147             }
148             else
149             {
150                 joystick = SDL_JoystickOpen(0);
151             }
152         }
153 
154         toReset = Array!(bool*)();
155 
156         inputManager = New!InputManager(this);
157     }
158 
159     ~this()
160     {
161         toReset.free();
162         Delete(inputManager);
163     }
164 
165     void exit()
166     {
167         running = false;
168     }
169 
170     void addEvent(Event e)
171     {
172         if (numEvents < maxNumEvents)
173         {
174             eventStack[numEvents] = e;
175             numEvents++;
176         }
177         else
178             writeln("Warning: event stack overflow");
179     }
180 
181     void generateFileChangeEvent(string filename)
182     {
183         Event e = Event(EventType.FileChange);
184         e.filename = filename;
185         addUserEvent(e);
186     }
187 
188     void addUserEvent(Event e)
189     {
190         if (numUserEvents < maxNumEvents)
191         {
192             userEventStack[numUserEvents] = e;
193             numUserEvents++;
194         }
195         else
196             writeln("Warning: user event stack overflow");
197     }
198 
199     void generateUserEvent(int code)
200     {
201         Event e = Event(EventType.UserEvent);
202         e.userCode = code;
203         addUserEvent(e);
204     }
205 
206     bool gameControllerAvailable()
207     {
208         return (controller !is null);
209     }
210 
211     bool joystickAvailable()
212     {
213         return (joystick !is null || controller !is null);
214     }
215 
216     float gameControllerAxis(int axis)
217     {
218         return cast(float)(SDL_GameControllerGetAxis(controller, cast(SDL_GameControllerAxis)axis)) / 32768.0f;
219     }
220 
221     float joystickAxis(int axis)
222     {
223         if (joystick)
224         {
225             double a = cast(double)(SDL_JoystickGetAxis(joystick, axis));
226             return a / 32768.0f;
227         }
228         else if (controller)
229         {
230             return cast(float)(SDL_GameControllerGetAxis(controller, cast(SDL_GameControllerAxis)axis)) / 32768.0f;
231         }
232         else return 0.0;
233     }
234 
235     void update()
236     {
237         numEvents = 0;
238 
239         mouseRelX = 0;
240         mouseRelY = 0;
241 
242         for (uint i = 0; i < numUserEvents; i++)
243         {
244             Event e = userEventStack[i];
245             addEvent(e);
246         }
247 
248         numUserEvents = 0;
249 
250         SDL_Event event;
251 
252         while(SDL_PollEvent(&event))
253         {
254             if (onProcessEvent) onProcessEvent(&event);
255             Event e;
256             switch (event.type)
257             {
258                 case SDL_KEYDOWN:
259                     if (event.key.repeat && !enableKeyRepeat)
260                         break;
261 
262                     keyPressed[event.key.keysym.scancode] = true;
263                     keyDown[event.key.keysym.scancode] = true;
264                     keyUp[event.key.keysym.scancode] = false;
265                     toReset.insertBack(&keyDown[event.key.keysym.scancode]);
266 
267                     e = Event(EventType.KeyDown);
268                     e.key = event.key.keysym.scancode;
269                     addEvent(e);
270                     break;
271 
272                 case SDL_KEYUP:
273                     keyPressed[event.key.keysym.scancode] = false;
274                     keyDown[event.key.keysym.scancode] = false;
275                     keyUp[event.key.keysym.scancode] = true;
276                     toReset.insertBack(&keyUp[event.key.keysym.scancode]);
277 
278                     e = Event(EventType.KeyUp);
279                     e.key = event.key.keysym.scancode;
280                     addEvent(e);
281                     break;
282 
283                 case SDL_TEXTINPUT:
284                     e = Event(EventType.TextInput);
285                     char[] input = event.text.text;
286                     if ((input[0] & 0x80) == 0)
287                     {
288                         e.unicode = input[0];
289                     }
290                     else if ((input[0] & 0xE0) == 0xC0)
291                     {
292                         e.unicode = ((input[0] & 0x1F) << 6) | (input[1] & 0x3F);
293                     }
294                     else if ((input[0] & 0xF0) == 0xE0)
295                     {
296                         e.unicode = ((input[0] & 0x0F) << 12) | ((input[1] & 0x3F) << 6) | (input[2] & 0x3F);
297                     }
298                     else if ((input[0] & 0xF8) == 0xF0)
299                     {
300                         e.unicode = (((input[0] & 0x0F) << 18) | ((input[1] & 0x3F) << 12) | ((input[2] & 0x3F) << 6) | (input[3] & 0x3F));
301                     }
302                     addEvent(e);
303                     break;
304 
305                 case SDL_MOUSEMOTION:
306                     mouseX = event.motion.x;
307                     mouseY = event.motion.y;
308                     mouseRelX = event.motion.xrel;
309                     mouseRelY = event.motion.yrel;
310                     break;
311 
312                 case SDL_MOUSEBUTTONDOWN:
313                     mouseButtonPressed[event.button.button] = true;
314                     mouseButtonDown[event.button.button] = true;
315                     toReset.insertBack(&mouseButtonDown[event.button.button]);
316 
317                     e = Event(EventType.MouseButtonDown);
318                     e.button = event.button.button;
319                     addEvent(e);
320                     break;
321 
322                 case SDL_MOUSEBUTTONUP:
323                     mouseButtonPressed[event.button.button] = false;
324                     mouseButtonUp[event.button.button] = true;
325                     toReset.insertBack(&mouseButtonUp[event.button.button]);
326 
327                     e = Event(EventType.MouseButtonUp);
328                     e.button = event.button.button;
329                     addEvent(e);
330                     break;
331 
332                 case SDL_MOUSEWHEEL:
333                     e = Event(EventType.MouseWheel);
334                     e.mouseWheelX = event.wheel.x;
335                     e.mouseWheelY = event.wheel.y;
336                     addEvent(e);
337                     break;
338 
339                 case SDL_JOYBUTTONDOWN:
340                     if(joystick is null) break;
341                     if (event.jbutton.state == SDL_PRESSED)
342                     {
343                         e = Event(EventType.JoystickButtonDown);
344                         joystickButtonPressed[event.jbutton.button] = true;
345                         joystickButtonDown[event.jbutton.button] = true;
346                         toReset.insertBack(&joystickButtonDown[event.jbutton.button]);
347                     }
348                     else if (event.jbutton.state == SDL_RELEASED)
349                     {
350                         e = Event(EventType.JoystickButtonUp);
351                         joystickButtonPressed[event.jbutton.button] = false;
352                         joystickButtonUp[event.jbutton.button] = true;
353                         toReset.insertBack(&joystickButtonUp[event.jbutton.button]);
354                     }
355                     e.joystickButton = event.jbutton.button;
356                     addEvent(e);
357                     break;
358 
359                 case SDL_JOYBUTTONUP:
360                     if(joystick is null) break;
361                     if (event.jbutton.state == SDL_PRESSED)
362                     {
363                         e = Event(EventType.JoystickButtonDown);
364                         joystickButtonPressed[event.jbutton.button] = true;
365                         joystickButtonDown[event.jbutton.button] = true;
366                         toReset.insertBack(&joystickButtonDown[event.jbutton.button]);
367                     }
368                     else if (event.jbutton.state == SDL_RELEASED)
369                     {
370                         e = Event(EventType.JoystickButtonUp);
371                         joystickButtonPressed[event.jbutton.button] = false;
372                         joystickButtonUp[event.jbutton.button] = true;
373                         toReset.insertBack(&joystickButtonUp[event.jbutton.button]);
374                     }
375                     e.joystickButton = event.jbutton.button;
376                     addEvent(e);
377                     break;
378 
379                 case SDL_CONTROLLERBUTTONDOWN:
380                     controllerButtonPressed[event.cbutton.button] = true;
381                     controllerButtonDown[event.cbutton.button] = true;
382                     toReset.insertBack(&controllerButtonDown[event.cbutton.button]);
383 
384                     e = Event(EventType.JoystickButtonDown);
385                     e.joystickButton = event.cbutton.button;
386                     addEvent(e);
387                     break;
388 
389                 case SDL_CONTROLLERBUTTONUP:
390                     controllerButtonPressed[event.cbutton.button] = false;
391                     controllerButtonUp[event.cbutton.button] = true;
392                     toReset.insertBack(&controllerButtonUp[event.cbutton.button]);
393 
394                     e = Event(EventType.JoystickButtonUp);
395                     e.joystickButton = event.cbutton.button;
396                     addEvent(e);
397                     break;
398 
399                 case SDL_CONTROLLERAXISMOTION:
400                     // TODO: add state modification
401                     e = Event(EventType.JoystickAxisMotion);
402                     e.joystickAxis = event.caxis.axis;
403                     e.joystickAxisValue = cast(float)event.caxis.value / 32768.0f;
404                     if (controller)
405                     {
406                         if (e.joystickAxis == 0)
407                             e.joystickAxisValue = SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_LEFTY);
408                         if (e.joystickAxis == 1)
409                             e.joystickAxisValue = SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_LEFTX);
410                         e.joystickAxisValue = e.joystickAxisValue / 32768.0f;
411                     }
412                     addEvent(e);
413                     break;
414 
415                 case SDL_WINDOWEVENT:
416                     if (event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED)
417                     {
418                         windowWidth = event.window.data1;
419                         windowHeight = event.window.data2;
420                         e = Event(EventType.Resize);
421                         e.width = windowWidth;
422                         e.height = windowHeight;
423                         addEvent(e);
424                     }
425                     else if (event.window.event == SDL_WINDOWEVENT_FOCUS_GAINED)
426                     {
427                         e = Event(EventType.FocusGain);
428                         addEvent(e);
429                     }
430                     else if (event.window.event == SDL_WINDOWEVENT_FOCUS_LOST)
431                     {
432                         e = Event(EventType.FocusLoss);
433                         addEvent(e);
434                     }
435                     break;
436 
437                 case SDL_DROPFILE:
438                     e = Event(EventType.DropFile);
439                     e.filename = to!string(event.drop.file);
440                     addEvent(e);
441                     break;
442 
443                 case SDL_QUIT:
444                     exit();
445                     e = Event(EventType.Quit);
446                     addEvent(e);
447                     break;
448 
449                 default:
450                     break;
451             }
452         }
453     }
454 
455     protected int lastTime = 0;
456     void updateTimer()
457     {
458         int currentTime = SDL_GetTicks();
459         auto elapsedTime = currentTime - lastTime;
460         lastTime = currentTime;
461         deltaTimeMs = elapsedTime;
462         deltaTime = cast(double)(elapsedTime) * 0.001;
463     }
464 
465     void setMouse(int x, int y)
466     {
467         SDL_WarpMouseInWindow(window, x, y);
468         mouseX = x;
469         mouseY = y;
470     }
471 
472     void setMouseToCenter()
473     {
474         float x = (cast(float)windowWidth) / 2;
475         float y = (cast(float)windowHeight) / 2;
476         setMouse(cast(int)x, cast(int)y);
477     }
478 
479     int showCursor(bool mode)
480     {
481         return SDL_ShowCursor(mode);
482     }
483 
484     int setRelativeMouseMode(bool mode)
485     {
486         return SDL_SetRelativeMouseMode(cast(SDL_bool)mode);
487     }
488 
489     float aspectRatio()
490     {
491         return cast(float)windowWidth / cast(float)windowHeight;
492     }
493 
494     void resetUpDown()
495     {
496         // reset all UP and DOWN events
497         foreach(key; toReset)
498         {
499             *key = false;
500         }
501         toReset.removeBack(cast(uint)toReset.length);
502     }
503 }
504 
505 abstract class EventListener: Owner
506 {
507     EventManager eventManager;
508     InputManager inputManager;
509     bool enabled = true;
510 
511     this(EventManager emngr, Owner owner)
512     {
513         super(owner);
514         eventManager = emngr;
515         if(emngr !is null)
516             inputManager = emngr.inputManager;
517     }
518 
519     protected void generateUserEvent(int code)
520     {
521         eventManager.generateUserEvent(code);
522     }
523 
524     void processEvents()
525     {
526         if (!enabled)
527             return;
528 
529         for (uint i = 0; i < eventManager.numEvents; i++)
530         {
531             Event* e = &eventManager.eventStack[i];
532             processEvent(e);
533         }
534     }
535 
536     void processEvent(Event* e)
537     {
538         switch(e.type)
539         {
540             case EventType.KeyDown:
541                 onKeyDown(e.key);
542                 break;
543             case EventType.KeyUp:
544                 onKeyUp(e.key);
545                 break;
546             case EventType.TextInput:
547                 onTextInput(e.unicode);
548                 break;
549             case EventType.MouseButtonDown:
550                 onMouseButtonDown(e.button);
551                 break;
552             case EventType.MouseButtonUp:
553                 onMouseButtonUp(e.button);
554                 break;
555             case EventType.MouseWheel:
556                 onMouseWheel(e.mouseWheelX, e.mouseWheelY);
557                 break;
558             case EventType.JoystickButtonDown:
559                 onJoystickButtonDown(e.joystickButton);
560                 break;
561             case EventType.JoystickButtonUp:
562                 onJoystickButtonUp(e.joystickButton);
563                 break;
564             case EventType.JoystickAxisMotion:
565                 onJoystickAxisMotion(e.joystickAxis, e.joystickAxisValue);
566                 break;
567             case EventType.Resize:
568                 onResize(e.width, e.height);
569                 break;
570             case EventType.FocusLoss:
571                 onFocusLoss();
572                 break;
573             case EventType.FocusGain:
574                 onFocusGain();
575                 break;
576             case EventType.Quit:
577                 onQuit();
578                 break;
579             case EventType.FileChange:
580                 onFileChange(e.filename);
581                 break;
582             case EventType.DropFile:
583                 onDropFile(e.filename);
584                 break;
585             case EventType.UserEvent:
586                 onUserEvent(e.userCode);
587                 break;
588             default:
589                 break;
590         }
591     }
592 
593     void onKeyDown(int key) {}
594     void onKeyUp(int key) {}
595     void onTextInput(dchar code) {}
596     void onMouseButtonDown(int button) {}
597     void onMouseButtonUp(int button) {}
598     void onMouseWheel(int x, int y) {}
599     void onJoystickButtonDown(int button) {}
600     void onJoystickButtonUp(int button) {}
601     void onJoystickAxisMotion(int axis, float value) {}
602     void onResize(int width, int height) {}
603     void onFocusLoss() {}
604     void onFocusGain() {}
605     void onQuit() {}
606     void onFileChange(string filename) {}
607     void onDropFile(string filename) {}
608     void onUserEvent(int code) {}
609 }