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 }