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 dlib.core.memory;
35 import dagon.core.libs;
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                     //auto unicode = event.key.keysym.keycode;
233                     
234                     if ((event.key.keysym.sym & 0xFF80) == 0)
235                     {
236                         auto asciiChar = event.key.keysym.sym & 0x7F;
237                         if (isPrintable(asciiChar))
238                         {
239                             e = Event(EventType.TextInput);
240                             e.unicode = asciiChar;
241                             addEvent(e);
242                         }
243                     }
244                     else
245                     {
246                         e = Event(EventType.TextInput);
247                         e.unicode = event.key.keysym.sym; //event.key.keysym.unicode;
248                         addEvent(e);
249                     }
250 
251                     keyPressed[event.key.keysym.scancode] = true;
252                     e = Event(EventType.KeyDown);
253                     e.key = event.key.keysym.scancode;
254                     addEvent(e);
255                     break;
256 
257                 case SDL_KEYUP:
258                     keyPressed[event.key.keysym.scancode] = false;
259                     e = Event(EventType.KeyUp);
260                     e.key = event.key.keysym.scancode;
261                     addEvent(e);
262                     break;
263 
264                 case SDL_MOUSEMOTION:
265                     mouseX = event.motion.x;
266                     mouseY = event.motion.y;
267                     mouseRelX = event.motion.xrel;
268                     mouseRelY = event.motion.yrel;
269                     break;
270 
271                 case SDL_MOUSEBUTTONDOWN:
272                     mouseButtonPressed[event.button.button] = true;
273                     e = Event(EventType.MouseButtonDown);
274                     e.button = event.button.button;
275                     addEvent(e);
276                     break;
277 
278                 case SDL_MOUSEBUTTONUP:
279                     mouseButtonPressed[event.button.button] = false;
280                     e = Event(EventType.MouseButtonUp);
281                     e.button = event.button.button;
282                     addEvent(e);
283                     break;
284                     
285                 case SDL_MOUSEWHEEL:
286                     e = Event(EventType.MouseWheel);
287                     e.mouseWheelX = event.wheel.x;
288                     e.mouseWheelY = event.wheel.y;
289                     addEvent(e);
290                     break;
291                     
292                 case SDL_JOYBUTTONDOWN:
293                     if (event.jbutton.state == SDL_PRESSED)
294                         e = Event(EventType.JoystickButtonDown);
295                     else if (event.jbutton.state == SDL_RELEASED)
296                         e = Event(EventType.JoystickButtonUp);
297                     e.joystickButton = event.jbutton.button;
298                     addEvent(e);
299                     break;
300                     
301                 case SDL_JOYBUTTONUP: 
302                     // TODO: add state modification
303                     if (event.jbutton.state == SDL_PRESSED)
304                         e = Event(EventType.JoystickButtonDown);
305                     else if (event.jbutton.state == SDL_RELEASED)
306                         e = Event(EventType.JoystickButtonUp);
307                     e.joystickButton = event.jbutton.button;
308                     addEvent(e);
309                     break;
310 
311                 case SDL_CONTROLLERBUTTONDOWN:
312                     // TODO: add state modification
313                     if (event.cbutton.state == SDL_PRESSED)
314                         e = Event(EventType.JoystickButtonDown);
315                     else if (event.cbutton.state == SDL_RELEASED)
316                         e = Event(EventType.JoystickButtonUp);
317                     e.joystickButton = event.cbutton.button;
318                     addEvent(e);
319                     break;
320 
321                 case SDL_CONTROLLERBUTTONUP: 
322                     // TODO: add state modification
323                     if (event.cbutton.state == SDL_PRESSED)
324                         e = Event(EventType.JoystickButtonDown);
325                     else if (event.cbutton.state == SDL_RELEASED)
326                         e = Event(EventType.JoystickButtonUp);
327                     e.joystickButton = event.cbutton.button;
328                     addEvent(e);
329                     break;
330 
331                 case SDL_CONTROLLERAXISMOTION:
332                     // TODO: add state modification
333                     e = Event(EventType.JoystickAxisMotion);
334                     e.joystickAxis = event.caxis.axis;
335                     e.joystickAxisValue = cast(float)event.caxis.value / 32768.0f;
336                     
337                     if (controller)
338                     {
339                         if (e.joystickAxis == 0)
340                             e.joystickAxisValue = SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_LEFTY);
341                         if (e.joystickAxis == 1)
342                             e.joystickAxisValue = SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_LEFTX);
343                             
344                         e.joystickAxisValue = e.joystickAxisValue / 32768.0f; 
345                     }
346                     
347                     addEvent(e);
348                     break;
349 
350                 case SDL_WINDOWEVENT:
351                     if (event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED)
352                     {
353                         windowWidth = event.window.data1;
354                         windowHeight = event.window.data2;
355                         e = Event(EventType.Resize);
356                         e.width = windowWidth;
357                         e.height = windowHeight;
358                         addEvent(e);
359                     }
360                     break;
361 /*
362                 case SDL_ACTIVEEVENT:
363                     if (event.active.state & SDL_APPACTIVE)
364                     {
365                         if (event.active.gain == 0)
366                         {
367                             writeln("Deactivated");
368                             windowFocused = false;
369                             e = Event(EventType.FocusLoss);
370                         }
371                         else
372                         {
373                             writeln("Activated");
374                             windowFocused = true;
375                             e = Event(EventType.FocusGain);
376                         }
377                     }
378                     else if (event.active.state & SDL_APPINPUTFOCUS)
379                     {
380                         if (event.active.gain == 0)
381                         {
382                             writeln("Lost focus");
383                             windowFocused = false;
384                             e = Event(EventType.FocusLoss);
385                         }
386                         else
387                         {
388                             writeln("Gained focus");
389                             windowFocused = true;
390                             e = Event(EventType.FocusGain);
391                         }
392                     }
393                     addEvent(e);
394                     break;
395 */
396                 case SDL_QUIT:
397                     running = false;
398                     e = Event(EventType.Quit);
399                     addEvent(e);
400                     break;
401 
402                 default:
403                     break;
404             }
405         }
406     }
407 
408     void updateTimer()
409     {
410         static int currentTime;
411         static int lastTime;
412 
413         static int FPSTickCounter;
414         static int FPSCounter = 0;
415 
416         currentTime = SDL_GetTicks();
417         auto elapsedTime = currentTime - lastTime;
418         lastTime = currentTime;
419         deltaTimeMs = elapsedTime;
420         deltaTime = cast(double)(elapsedTime) * 0.001;
421 
422         FPSTickCounter += elapsedTime;
423         FPSCounter++;
424         if (FPSTickCounter >= 1000) // 1 sec interval
425         {
426             fps = FPSCounter;
427             FPSCounter = 0;
428             FPSTickCounter = 0;
429             averageDelta = 1.0 / cast(double)(fps);
430         }
431     }
432 
433     void setMouse(int x, int y)
434     {
435         SDL_WarpMouseInWindow(window, x, y);
436         mouseX = x;
437         mouseY = y;
438     }
439 
440     void setMouseToCenter()
441     {
442         float x = (cast(float)windowWidth)/2;
443         float y = (cast(float)windowHeight)/2;
444         setMouse(cast(int)x, cast(int)y);
445     }
446 
447     void showCursor(bool mode)
448     {
449         SDL_ShowCursor(mode);
450     }
451     
452     float aspectRatio()
453     {
454         return cast(float)windowWidth / cast(float)windowHeight;
455     }
456 }
457 
458 abstract class EventListener: Owner
459 {
460     EventManager eventManager;
461     bool enabled = true;
462 
463     this(EventManager emngr, Owner owner)
464     {
465         super(owner);
466         eventManager = emngr;
467     }
468 
469     protected void generateUserEvent(int code)
470     {
471         eventManager.generateUserEvent(code);
472     }
473 
474     void processEvents()
475     {
476         if (!enabled)
477             return;
478 
479         for (uint i = 0; i < eventManager.numEvents; i++)
480         {
481             Event* e = &eventManager.eventStack[i];
482             processEvent(e);
483         }
484     }
485 
486     void processEvent(Event* e)
487     {
488         switch(e.type)
489         {
490             case EventType.KeyDown:
491                 onKeyDown(e.key);
492                 break;
493             case EventType.KeyUp:
494                 onKeyUp(e.key);
495                 break;
496             case EventType.TextInput:
497                 onTextInput(e.unicode);
498                 break;
499             case EventType.MouseButtonDown:
500                 onMouseButtonDown(e.button);
501                 break;
502             case EventType.MouseButtonUp:
503                 onMouseButtonUp(e.button);
504                 break;
505             case EventType.MouseWheel:
506                 onMouseWheel(e.mouseWheelX, e.mouseWheelY);
507                 break;
508             case EventType.JoystickButtonDown:
509                 onJoystickButtonDown(e.joystickButton);
510                 break;
511             case EventType.JoystickButtonUp:
512                 onJoystickButtonUp(e.joystickButton);
513                 break;
514             case EventType.JoystickAxisMotion:
515                 onJoystickAxisMotion(e.joystickAxis, e.joystickAxisValue);
516                 break;
517             case EventType.Resize:
518                 onResize(e.width, e.height);
519                 break;
520             case EventType.FocusLoss:
521                 onFocusLoss();
522                 break;
523             case EventType.FocusGain:
524                 onFocusGain();
525                 break;
526             case EventType.Quit:
527                 onQuit();
528                 break;
529             case EventType.AssetReload:
530                 onAssetReload(e.asset);
531                 break;
532             case EventType.UserEvent:
533                 onUserEvent(e.userCode);
534                 break;
535             default:
536                 break;
537         }
538     }
539 
540     void onKeyDown(int key) {}
541     void onKeyUp(int key) {}
542     void onTextInput(dchar code) {}
543     void onMouseButtonDown(int button) {}
544     void onMouseButtonUp(int button) {}
545     void onMouseWheel(int x, int y) {}
546     void onJoystickButtonDown(int button) {}
547     void onJoystickButtonUp(int button) {}
548     void onJoystickAxisMotion(int axis, float value) {}
549     void onResize(int width, int height) {}
550     void onFocusLoss() {}
551     void onFocusGain() {}
552     void onQuit() {}
553     void onAssetReload(Asset asset) {}
554     void onUserEvent(int code) {}
555 }