1 /*
2 Copyright (c) 2014-2015 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 dgl.core.event;
30 
31 private
32 {
33     import std.stdio;
34     import std.ascii;
35     import derelict.sdl.sdl;
36     import dlib.core.memory;
37 }
38 
39 enum EventType
40 {
41     KeyDown,
42     KeyUp,
43     TextInput,
44     MouseMotion,
45     MouseButtonDown,
46     MouseButtonUp,
47     JoystickButtonDown,
48     JoystickButtonUp,
49     JoystickAxisMotion,
50     Resize,
51     FocusLoss,
52     FocusGain,
53     Quit,
54     UserEvent
55 }
56 
57 struct Event
58 {
59     EventType type;
60     int key;
61     dchar unicode;
62     int button;
63     int joystickButton;
64     int joystickAxis;
65     int joystickAxisValue;
66     int width;
67     int height;
68     int userCode;
69 }
70 
71 class EventManager
72 {
73     enum maxNumEvents = 50;
74     Event[maxNumEvents] eventStack;
75     Event[maxNumEvents] userEventStack;
76     uint numEvents;
77     uint numUserEvents;
78 
79     bool running = true;
80 
81     bool keyPressed[512] = false;
82     bool mouseButtonPressed[255] = false;
83     int mouseX = 0;
84     int mouseY = 0;
85 
86     double deltaTime = 0.0;
87     double averageDelta = 0.0;
88     uint deltaTimeMs = 0;
89     int fps = 0;
90 
91     uint videoWidth;
92     uint videoHeight;
93 
94     uint windowWidth;
95     uint windowHeight;
96     bool windowFocused = true;
97 
98     this(uint winWidth, uint winHeight)
99     {
100         windowWidth = winWidth;
101         windowHeight = winHeight;
102 
103         auto videoInfo = SDL_GetVideoInfo();
104         videoWidth = videoInfo.current_w;
105         videoHeight = videoInfo.current_h;
106     }
107 
108     void addEvent(Event e)
109     {
110         if (numEvents < maxNumEvents)
111         {
112             eventStack[numEvents] = e;
113             numEvents++;
114         }
115         else
116             writeln("Warning: event stack overflow");
117     }
118 
119     void addUserEvent(Event e)
120     {
121         if (numUserEvents < maxNumEvents)
122         {
123             userEventStack[numUserEvents] = e;
124             numUserEvents++;
125         }
126         else
127             writeln("Warning: user event stack overflow");
128     }
129 
130     void generateUserEvent(int code)
131     {
132         Event e = Event(EventType.UserEvent);
133         e.userCode = code;
134         addUserEvent(e);
135     }
136 
137     void update()
138     {
139         numEvents = 0;
140         updateTimer();
141 
142         if (SDL_WasInit(SDL_INIT_JOYSTICK))
143             SDL_JoystickUpdate();
144 
145         for (uint i = 0; i < numUserEvents; i++)
146         {
147             Event e = userEventStack[i];
148             addEvent(e);
149         }
150 
151         numUserEvents = 0;
152 
153         SDL_Event event;
154 
155         while(SDL_PollEvent(&event))
156         {
157             Event e;
158             switch (event.type)
159             {
160                 case SDL_KEYDOWN:
161                     if ((event.key.keysym.unicode & 0xFF80) == 0)
162                     {
163                         auto asciiChar = event.key.keysym.unicode & 0x7F;
164                         if (isPrintable(asciiChar))
165                         {
166                             e = Event(EventType.TextInput);
167                             e.unicode = asciiChar;
168                             addEvent(e);
169                         }
170                     }
171                     else
172                     {
173                         e = Event(EventType.TextInput);
174                         e.unicode = event.key.keysym.unicode;
175                         addEvent(e);
176                     }
177 
178                     keyPressed[event.key.keysym.sym] = true;
179                     e = Event(EventType.KeyDown);
180                     e.key = event.key.keysym.sym;
181                     addEvent(e);
182                     break;
183 
184                 case SDL_KEYUP:
185                     keyPressed[event.key.keysym.sym] = false;
186                     e = Event(EventType.KeyUp);
187                     e.key = event.key.keysym.sym;
188                     addEvent(e);
189                     break;
190 
191                 case SDL_MOUSEMOTION:
192                     mouseX = event.motion.x;
193                     mouseY = windowHeight - event.motion.y;
194                     break;
195 
196                 case SDL_MOUSEBUTTONDOWN:
197                     mouseButtonPressed[event.button.button] = true;
198                     e = Event(EventType.MouseButtonDown);
199                     e.button = event.button.button;
200                     addEvent(e);
201                     break;
202 
203                 case SDL_MOUSEBUTTONUP:
204                     mouseButtonPressed[event.button.button] = false;
205                     e = Event(EventType.MouseButtonUp);
206                     e.button = event.button.button;
207                     addEvent(e);
208                     break;
209 
210                 case SDL_JOYBUTTONDOWN:
211                     // TODO: add state modification
212                     e = Event(EventType.JoystickButtonDown);
213                     e.button = event.jbutton.button+1;
214                     addEvent(e);
215                     break;
216 
217                 case SDL_JOYBUTTONUP:
218                     // TODO: add state modification
219                     e = Event(EventType.JoystickButtonUp);
220                     e.button = event.jbutton.button+1;
221                     addEvent(e);
222                     break;
223 
224                 case SDL_JOYAXISMOTION:
225                     // TODO: add state modification
226                     e = Event(EventType.JoystickAxisMotion);
227                     e.joystickAxis = event.jaxis.axis;
228                     e.joystickAxis = event.jaxis.value;
229                     addEvent(e);
230                     break;
231 
232                 case SDL_VIDEORESIZE:
233                     writefln("Window resized to %s : %s", event.resize.w, event.resize.h);
234                     windowWidth = event.resize.w;
235                     windowHeight = event.resize.h;
236                     e = Event(EventType.Resize);
237                     e.width = windowWidth;
238                     e.height = windowHeight;
239                     addEvent(e);
240                     break;
241 
242                 case SDL_ACTIVEEVENT:
243                     if (event.active.state & SDL_APPACTIVE)
244                     {
245                         if (event.active.gain == 0)
246                         {
247                             writeln("Deactivated");
248                             windowFocused = false;
249                             e = Event(EventType.FocusLoss);
250                         }
251                         else
252                         {
253                             writeln("Activated");
254                             windowFocused = true;
255                             e = Event(EventType.FocusGain);
256                         }
257                     }
258                     else if (event.active.state & SDL_APPINPUTFOCUS)
259                     {
260                         if (event.active.gain == 0)
261                         {
262                             writeln("Lost focus");
263                             windowFocused = false;
264                             e = Event(EventType.FocusLoss);
265                         }
266                         else
267                         {
268                             writeln("Gained focus");
269                             windowFocused = true;
270                             e = Event(EventType.FocusGain);
271                         }
272                     }
273                     addEvent(e);
274                     break;
275 
276                 case SDL_QUIT:
277                     running = false;
278                     e = Event(EventType.Quit);
279                     addEvent(e);
280                     break;
281 
282                 default:
283                     break;
284             }
285         }
286     }
287 
288     void updateTimer()
289     {
290         static int currentTime;
291         static int lastTime;
292 
293         static int FPSTickCounter;
294         static int FPSCounter = 0;
295 
296         currentTime = SDL_GetTicks();
297         auto elapsedTime = currentTime - lastTime;
298         lastTime = currentTime;
299         deltaTimeMs = elapsedTime;
300         deltaTime = cast(double)(elapsedTime) * 0.001;
301 
302         FPSTickCounter += elapsedTime;
303         FPSCounter++;
304         if (FPSTickCounter >= 1000) // 1 sec interval
305         {
306             fps = FPSCounter;
307             FPSCounter = 0;
308             FPSTickCounter = 0;
309             averageDelta = 1.0 / cast(double)(fps);
310 	    }
311     }
312 
313     void setMouse(int x, int y)
314     {
315         SDL_WarpMouse(cast(ushort)x, cast(ushort)(windowHeight - y));
316         mouseX = x;
317         mouseY = y;
318     }
319 
320     void setMouseToCenter()
321     {
322         float x = (cast(float)windowWidth)/2;
323         float y = (cast(float)windowHeight)/2;
324         SDL_WarpMouse(cast(ushort)x, cast(ushort)(y));
325         mouseX = cast(int)x;
326         mouseY = cast(int)y;
327     }
328 
329     void showCursor(bool mode)
330     {
331         SDL_ShowCursor(mode);
332     }
333 }
334 
335 abstract class EventListener: Freeable
336 {
337     EventManager eventManager;
338     bool enabled = true;
339 
340     this(EventManager emngr)
341     {
342         eventManager = emngr;
343     }
344 
345     protected void generateUserEvent(int code)
346     {
347         eventManager.generateUserEvent(code);
348     }
349 
350     void processEvents()
351     {
352         if (!enabled)
353             return;
354 
355         for (uint i = 0; i < eventManager.numEvents; i++)
356         {
357             Event* e = &eventManager.eventStack[i];
358             processEvent(e);
359         }
360     }
361 
362     void processEvent(Event* e)
363     {
364         switch(e.type)
365         {
366             case EventType.KeyDown:
367                 onKeyDown(e.key);
368                 break;
369             case EventType.KeyUp:
370                 onKeyUp(e.key);
371                 break;
372             case EventType.TextInput:
373                 onTextInput(e.unicode);
374                 break;
375             case EventType.MouseButtonDown:
376                 onMouseButtonDown(e.button);
377                 break;
378             case EventType.MouseButtonUp:
379                 onMouseButtonUp(e.button);
380                 break;
381             case EventType.JoystickButtonDown:
382                 onJoystickButtonDown(e.joystickButton);
383                 break;
384             case EventType.JoystickButtonUp:
385                 onJoystickButtonDown(e.joystickButton);
386                 break;
387             case EventType.JoystickAxisMotion:
388                 onJoystickAxisMotion(e.joystickAxis, e.joystickAxisValue);
389                 break;
390             case EventType.Resize:
391                 onResize(e.width, e.height);
392                 break;
393             case EventType.FocusLoss:
394                 onFocusLoss();
395                 break;
396             case EventType.FocusGain:
397                 onFocusGain();
398                 break;
399             case EventType.Quit:
400                 onQuit();
401                 break;
402             case EventType.UserEvent:
403                 onUserEvent(e.userCode);
404                 break;
405             default:
406                 break;
407         }
408     }
409 
410     void onKeyDown(int key) {}
411     void onKeyUp(int key) {}
412     void onTextInput(dchar code) {}
413     void onMouseButtonDown(int button) {}
414     void onMouseButtonUp(int button) {}
415     void onJoystickButtonDown(int button) {}
416     void onJoystickButtonUp(int button) {}
417     void onJoystickAxisMotion(int axis, int value) {}
418     void onResize(int width, int height) {}
419     void onFocusLoss() {}
420     void onFocusGain() {}
421     void onQuit() {}
422     void onUserEvent(int code) {}
423 
424     void free()
425     {
426         Delete(this);
427     }
428 }
429 
430 import std.conv;
431 
432 class TextListener: EventListener
433 {
434     dchar[100] arr;
435     uint pos = 0;
436 
437     this(EventManager emngr)
438     {
439         super(emngr);
440     }
441 
442     override void onTextInput(dchar code)
443     {
444         if (pos < 100)
445         {
446             arr[pos] = code;
447             pos++;
448         }
449     }
450 
451     override void onKeyDown(int key)
452     {
453         if (key == SDLK_BACKSPACE)
454             back();
455     }
456 
457     void reset()
458     {
459         arr[] = 0;
460         pos = 0;
461     }
462 
463     void back()
464     {
465         if (pos > 0)
466             pos--;
467     }
468 
469     override string toString()
470     {
471         return to!string(arr[0..pos]);
472     }
473 
474     override void free()
475     {
476         Delete(this);
477     }
478 }