1 /*
2 Copyright (c) 2014-2018 Timur Gafarov
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 derelict.sdl2.sdl;
35 import dlib.core.memory;
36 import dagon.core.ownership;
37 import dagon.resource.asset;
38 
39 enum EventType
40 {
41     KeyDown,
42     KeyUp,
43     TextInput,
44     MouseMotion,
45     MouseButtonDown,
46     MouseButtonUp,
47     MouseWheel,
48     JoystickButtonDown,
49     JoystickButtonUp,
50     JoystickAxisMotion,
51     Resize,
52     FocusLoss,
53     FocusGain,
54     Quit,
55     AssetReload,
56     UserEvent
57 }
58 
59 struct Event
60 {
61     EventType type;
62     int key;
63     dchar unicode;
64     int button;
65     int joystickButton;
66     int joystickAxis;
67     float joystickAxisValue;
68     int width;
69     int height;
70     int userCode;
71     int mouseWheelX;
72     int mouseWheelY;
73     Asset asset;
74 }
75 
76 class EventManager
77 {
78     SDL_Window* window;
79 
80     enum maxNumEvents = 50;
81     Event[maxNumEvents] eventStack;
82     Event[maxNumEvents] userEventStack;
83     uint numEvents;
84     uint numUserEvents;
85 
86     bool running = true;
87 
88     bool[512] keyPressed = false;
89     bool[255] mouseButtonPressed = false;
90     int mouseX = 0;
91     int mouseY = 0;
92     int mouseRelX = 0;
93     int mouseRelY = 0;
94     bool enableKeyRepeat = false;
95 
96     double deltaTime = 0.0;
97     double averageDelta = 0.0;
98     uint deltaTimeMs = 0;
99     int fps = 0;
100 
101     //uint videoWidth;
102     //uint videoHeight;
103 
104     uint windowWidth;
105     uint windowHeight;
106     bool windowFocused = true;
107     
108     SDL_GameController* controller = null;
109     SDL_Joystick* joystick = null;
110 
111     this(SDL_Window* win, uint winWidth, uint winHeight)
112     {
113         window = win;
114 
115         windowWidth = winWidth;
116         windowHeight = winHeight;
117 
118         //auto videoInfo = SDL_GetVideoInfo();
119         //videoWidth = videoInfo.current_w;
120         //videoHeight = videoInfo.current_h;
121         
122         SDL_InitSubSystem(SDL_INIT_JOYSTICK);
123 
124         if (SDL_IsGameController(0))
125         {
126             controller = SDL_GameControllerOpen(0);
127             
128             SDL_GameControllerAddMappingsFromFile("gamecontrollerdb.txt");
129             
130             if (SDL_GameControllerMapping(controller) is null)
131                 writeln("Warning: no mapping found for controller!");
132             
133             SDL_GameControllerEventState(SDL_ENABLE);
134         }
135         else
136         {
137             joystick = SDL_JoystickOpen(0);
138         }
139     }
140 
141     void addEvent(Event e)
142     {
143         if (numEvents < maxNumEvents)
144         {
145             eventStack[numEvents] = e;
146             numEvents++;
147         }
148         else
149             writeln("Warning: event stack overflow");
150     }
151     
152     void generateAssetReloadEvent(Asset asset)
153     {
154         Event e = Event(EventType.AssetReload);
155         e.asset = asset;
156         addUserEvent(e);
157     }
158 
159     void addUserEvent(Event e)
160     {
161         if (numUserEvents < maxNumEvents)
162         {
163             userEventStack[numUserEvents] = e;
164             numUserEvents++;
165         }
166         else
167             writeln("Warning: user event stack overflow");
168     }
169 
170     void generateUserEvent(int code)
171     {
172         Event e = Event(EventType.UserEvent);
173         e.userCode = code;
174         addUserEvent(e);
175     }
176     
177     bool gameControllerAvailable()
178     {
179         return (controller !is null);
180     }
181     
182     bool joystickAvailable()
183     {
184         return (joystick !is null || controller !is null);
185     }
186     
187     float gameControllerAxis(int axis)
188     {
189         return cast(float)(SDL_GameControllerGetAxis(controller, cast(SDL_GameControllerAxis)axis)) / 32768.0f;
190     }
191     
192     float joystickAxis(int axis)
193     {
194         if (joystick)
195         {
196             double a = cast(double)(SDL_JoystickGetAxis(joystick, axis));
197             return a / 32768.0f; //28000.0f;
198         }
199         else if (controller)
200         {
201             return cast(float)(SDL_GameControllerGetAxis(controller, cast(SDL_GameControllerAxis)axis)) / 32768.0f;
202         }
203         else return 0.0;
204     }
205 
206     void update()
207     {
208         numEvents = 0;
209         updateTimer();
210         
211         mouseRelX = 0;
212         mouseRelY = 0;
213 
214         for (uint i = 0; i < numUserEvents; i++)
215         {
216             Event e = userEventStack[i];
217             addEvent(e);
218         }
219 
220         numUserEvents = 0;
221 
222         SDL_Event event;
223 
224         while(SDL_PollEvent(&event))
225         {
226             Event e;
227             switch (event.type)
228             {
229                 case SDL_KEYDOWN:
230                     if (event.key.repeat && !enableKeyRepeat)
231                         break;
232                     if ((event.key.keysym.unicode & 0xFF80) == 0)
233                     {
234                         auto asciiChar = event.key.keysym.unicode & 0x7F;
235                         if (isPrintable(asciiChar))
236                         {
237                             e = Event(EventType.TextInput);
238                             e.unicode = asciiChar;
239                             addEvent(e);
240                         }
241                     }
242                     else
243                     {
244                         e = Event(EventType.TextInput);
245                         e.unicode = event.key.keysym.unicode;
246                         addEvent(e);
247                     }
248 
249                     keyPressed[event.key.keysym.scancode] = true;
250                     e = Event(EventType.KeyDown);
251                     e.key = event.key.keysym.scancode;
252                     addEvent(e);
253                     break;
254 
255                 case SDL_KEYUP:
256                     keyPressed[event.key.keysym.scancode] = false;
257                     e = Event(EventType.KeyUp);
258                     e.key = event.key.keysym.scancode;
259                     addEvent(e);
260                     break;
261 
262                 case SDL_MOUSEMOTION:
263                     mouseX = event.motion.x;
264                     mouseY = event.motion.y;
265                     mouseRelX = event.motion.xrel;
266                     mouseRelY = event.motion.yrel;
267                     break;
268 
269                 case SDL_MOUSEBUTTONDOWN:
270                     mouseButtonPressed[event.button.button] = true;
271                     e = Event(EventType.MouseButtonDown);
272                     e.button = event.button.button;
273                     addEvent(e);
274                     break;
275 
276                 case SDL_MOUSEBUTTONUP:
277                     mouseButtonPressed[event.button.button] = false;
278                     e = Event(EventType.MouseButtonUp);
279                     e.button = event.button.button;
280                     addEvent(e);
281                     break;
282                     
283                 case SDL_MOUSEWHEEL:
284                     e = Event(EventType.MouseWheel);
285                     e.mouseWheelX = event.wheel.x;
286                     e.mouseWheelY = event.wheel.y;
287                     addEvent(e);
288                     break;
289                     
290                 case SDL_JOYBUTTONDOWN:
291                     if (event.jbutton.state == SDL_PRESSED)
292                         e = Event(EventType.JoystickButtonDown);
293                     else if (event.jbutton.state == SDL_RELEASED)
294                         e = Event(EventType.JoystickButtonUp);
295                     e.joystickButton = event.jbutton.button;
296                     addEvent(e);
297                     break;
298                     
299                 case SDL_JOYBUTTONUP: 
300                     // TODO: add state modification
301                     if (event.jbutton.state == SDL_PRESSED)
302                         e = Event(EventType.JoystickButtonDown);
303                     else if (event.jbutton.state == SDL_RELEASED)
304                         e = Event(EventType.JoystickButtonUp);
305                     e.joystickButton = event.jbutton.button;
306                     addEvent(e);
307                     break;
308 
309                 case SDL_CONTROLLERBUTTONDOWN:
310                     // TODO: add state modification
311                     if (event.cbutton.state == SDL_PRESSED)
312                         e = Event(EventType.JoystickButtonDown);
313                     else if (event.cbutton.state == SDL_RELEASED)
314                         e = Event(EventType.JoystickButtonUp);
315                     e.joystickButton = event.cbutton.button;
316                     addEvent(e);
317                     break;
318 
319                 case SDL_CONTROLLERBUTTONUP: 
320                     // TODO: add state modification
321                     if (event.cbutton.state == SDL_PRESSED)
322                         e = Event(EventType.JoystickButtonDown);
323                     else if (event.cbutton.state == SDL_RELEASED)
324                         e = Event(EventType.JoystickButtonUp);
325                     e.joystickButton = event.cbutton.button;
326                     addEvent(e);
327                     break;
328 
329                 case SDL_CONTROLLERAXISMOTION:
330                     // TODO: add state modification
331                     e = Event(EventType.JoystickAxisMotion);
332                     e.joystickAxis = event.caxis.axis;
333                     e.joystickAxisValue = cast(float)event.caxis.value / 32768.0f;
334                     
335                     if (controller)
336                     {
337                         if (e.joystickAxis == 0)
338                             e.joystickAxisValue = SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_LEFTY);
339                         if (e.joystickAxis == 1)
340                             e.joystickAxisValue = SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_LEFTX);
341                             
342                         e.joystickAxisValue = e.joystickAxisValue / 32768.0f; 
343                     }
344                     
345                     addEvent(e);
346                     break;
347 
348                 case SDL_WINDOWEVENT:
349                     if (event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED)
350                     {
351                         windowWidth = event.window.data1;
352                         windowHeight = event.window.data2;
353                         e = Event(EventType.Resize);
354                         e.width = windowWidth;
355                         e.height = windowHeight;
356                         addEvent(e);
357                     }
358                     break;
359 /*
360                 case SDL_ACTIVEEVENT:
361                     if (event.active.state & SDL_APPACTIVE)
362                     {
363                         if (event.active.gain == 0)
364                         {
365                             writeln("Deactivated");
366                             windowFocused = false;
367                             e = Event(EventType.FocusLoss);
368                         }
369                         else
370                         {
371                             writeln("Activated");
372                             windowFocused = true;
373                             e = Event(EventType.FocusGain);
374                         }
375                     }
376                     else if (event.active.state & SDL_APPINPUTFOCUS)
377                     {
378                         if (event.active.gain == 0)
379                         {
380                             writeln("Lost focus");
381                             windowFocused = false;
382                             e = Event(EventType.FocusLoss);
383                         }
384                         else
385                         {
386                             writeln("Gained focus");
387                             windowFocused = true;
388                             e = Event(EventType.FocusGain);
389                         }
390                     }
391                     addEvent(e);
392                     break;
393 */
394                 case SDL_QUIT:
395                     running = false;
396                     e = Event(EventType.Quit);
397                     addEvent(e);
398                     break;
399 
400                 default:
401                     break;
402             }
403         }
404     }
405 
406     void updateTimer()
407     {
408         static int currentTime;
409         static int lastTime;
410 
411         static int FPSTickCounter;
412         static int FPSCounter = 0;
413 
414         currentTime = SDL_GetTicks();
415         auto elapsedTime = currentTime - lastTime;
416         lastTime = currentTime;
417         deltaTimeMs = elapsedTime;
418         deltaTime = cast(double)(elapsedTime) * 0.001;
419 
420         FPSTickCounter += elapsedTime;
421         FPSCounter++;
422         if (FPSTickCounter >= 1000) // 1 sec interval
423         {
424             fps = FPSCounter;
425             FPSCounter = 0;
426             FPSTickCounter = 0;
427             averageDelta = 1.0 / cast(double)(fps);
428         }
429     }
430 
431     void setMouse(int x, int y)
432     {
433         SDL_WarpMouseInWindow(window, x, y);
434         mouseX = x;
435         mouseY = y;
436     }
437 
438     void setMouseToCenter()
439     {
440         float x = (cast(float)windowWidth)/2;
441         float y = (cast(float)windowHeight)/2;
442         setMouse(cast(int)x, cast(int)y);
443     }
444 
445     void showCursor(bool mode)
446     {
447         SDL_ShowCursor(mode);
448     }
449     
450     float aspectRatio()
451     {
452         return cast(float)windowWidth / cast(float)windowHeight;
453     }
454 }
455 
456 abstract class EventListener: Owner
457 {
458     EventManager eventManager;
459     bool enabled = true;
460 
461     this(EventManager emngr, Owner owner)
462     {
463         super(owner);
464         eventManager = emngr;
465     }
466 
467     protected void generateUserEvent(int code)
468     {
469         eventManager.generateUserEvent(code);
470     }
471 
472     void processEvents()
473     {
474         if (!enabled)
475             return;
476 
477         for (uint i = 0; i < eventManager.numEvents; i++)
478         {
479             Event* e = &eventManager.eventStack[i];
480             processEvent(e);
481         }
482     }
483 
484     void processEvent(Event* e)
485     {
486         switch(e.type)
487         {
488             case EventType.KeyDown:
489                 onKeyDown(e.key);
490                 break;
491             case EventType.KeyUp:
492                 onKeyUp(e.key);
493                 break;
494             case EventType.TextInput:
495                 onTextInput(e.unicode);
496                 break;
497             case EventType.MouseButtonDown:
498                 onMouseButtonDown(e.button);
499                 break;
500             case EventType.MouseButtonUp:
501                 onMouseButtonUp(e.button);
502                 break;
503             case EventType.MouseWheel:
504                 onMouseWheel(e.mouseWheelX, e.mouseWheelY);
505                 break;
506             case EventType.JoystickButtonDown:
507                 onJoystickButtonDown(e.joystickButton);
508                 break;
509             case EventType.JoystickButtonUp:
510                 onJoystickButtonUp(e.joystickButton);
511                 break;
512             case EventType.JoystickAxisMotion:
513                 onJoystickAxisMotion(e.joystickAxis, e.joystickAxisValue);
514                 break;
515             case EventType.Resize:
516                 onResize(e.width, e.height);
517                 break;
518             case EventType.FocusLoss:
519                 onFocusLoss();
520                 break;
521             case EventType.FocusGain:
522                 onFocusGain();
523                 break;
524             case EventType.Quit:
525                 onQuit();
526                 break;
527             case EventType.AssetReload:
528                 onAssetReload(e.asset);
529                 break;
530             case EventType.UserEvent:
531                 onUserEvent(e.userCode);
532                 break;
533             default:
534                 break;
535         }
536     }
537 
538     void onKeyDown(int key) {}
539     void onKeyUp(int key) {}
540     void onTextInput(dchar code) {}
541     void onMouseButtonDown(int button) {}
542     void onMouseButtonUp(int button) {}
543     void onMouseWheel(int x, int y) {}
544     void onJoystickButtonDown(int button) {}
545     void onJoystickButtonUp(int button) {}
546     void onJoystickAxisMotion(int axis, float value) {}
547     void onResize(int width, int height) {}
548     void onFocusLoss() {}
549     void onFocusGain() {}
550     void onQuit() {}
551     void onAssetReload(Asset asset) {}
552     void onUserEvent(int code) {}
553 }