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