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