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