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.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 break; 405 406 case SDL_DROPFILE: 407 e = Event(EventType.DropFile); 408 e.filename = to!string(event.drop.file); 409 addEvent(e); 410 break; 411 412 case SDL_QUIT: 413 exit(); 414 e = Event(EventType.Quit); 415 addEvent(e); 416 break; 417 418 default: 419 break; 420 } 421 } 422 } 423 424 protected int lastTime = 0; 425 void updateTimer() 426 { 427 int currentTime = SDL_GetTicks(); 428 auto elapsedTime = currentTime - lastTime; 429 lastTime = currentTime; 430 deltaTimeMs = elapsedTime; 431 deltaTime = cast(double)(elapsedTime) * 0.001; 432 } 433 434 void setMouse(int x, int y) 435 { 436 SDL_WarpMouseInWindow(window, x, y); 437 mouseX = x; 438 mouseY = y; 439 } 440 441 void setMouseToCenter() 442 { 443 float x = (cast(float)windowWidth) / 2; 444 float y = (cast(float)windowHeight) / 2; 445 setMouse(cast(int)x, cast(int)y); 446 } 447 448 void showCursor(bool mode) 449 { 450 SDL_ShowCursor(mode); 451 } 452 453 float aspectRatio() 454 { 455 return cast(float)windowWidth / cast(float)windowHeight; 456 } 457 458 void resetUpDown() 459 { 460 // reset all UP and DOWN events 461 foreach(key; toReset) 462 { 463 *key = false; 464 } 465 toReset.removeBack(cast(uint)toReset.length); 466 } 467 } 468 469 abstract class EventListener: Owner 470 { 471 EventManager eventManager; 472 InputManager inputManager; 473 bool enabled = true; 474 475 this(EventManager emngr, Owner owner) 476 { 477 super(owner); 478 eventManager = emngr; 479 if(emngr !is null) 480 inputManager = emngr.inputManager; 481 } 482 483 protected void generateUserEvent(int code) 484 { 485 eventManager.generateUserEvent(code); 486 } 487 488 void processEvents() 489 { 490 if (!enabled) 491 return; 492 493 for (uint i = 0; i < eventManager.numEvents; i++) 494 { 495 Event* e = &eventManager.eventStack[i]; 496 processEvent(e); 497 } 498 } 499 500 void processEvent(Event* e) 501 { 502 switch(e.type) 503 { 504 case EventType.KeyDown: 505 onKeyDown(e.key); 506 break; 507 case EventType.KeyUp: 508 onKeyUp(e.key); 509 break; 510 case EventType.TextInput: 511 onTextInput(e.unicode); 512 break; 513 case EventType.MouseButtonDown: 514 onMouseButtonDown(e.button); 515 break; 516 case EventType.MouseButtonUp: 517 onMouseButtonUp(e.button); 518 break; 519 case EventType.MouseWheel: 520 onMouseWheel(e.mouseWheelX, e.mouseWheelY); 521 break; 522 case EventType.JoystickButtonDown: 523 onJoystickButtonDown(e.joystickButton); 524 break; 525 case EventType.JoystickButtonUp: 526 onJoystickButtonUp(e.joystickButton); 527 break; 528 case EventType.JoystickAxisMotion: 529 onJoystickAxisMotion(e.joystickAxis, e.joystickAxisValue); 530 break; 531 case EventType.Resize: 532 onResize(e.width, e.height); 533 break; 534 case EventType.FocusLoss: 535 onFocusLoss(); 536 break; 537 case EventType.FocusGain: 538 onFocusGain(); 539 break; 540 case EventType.Quit: 541 onQuit(); 542 break; 543 case EventType.FileChange: 544 onFileChange(e.filename); 545 break; 546 case EventType.DropFile: 547 onDropFile(e.filename); 548 break; 549 case EventType.UserEvent: 550 onUserEvent(e.userCode); 551 break; 552 default: 553 break; 554 } 555 } 556 557 void onKeyDown(int key) {} 558 void onKeyUp(int key) {} 559 void onTextInput(dchar code) {} 560 void onMouseButtonDown(int button) {} 561 void onMouseButtonUp(int button) {} 562 void onMouseWheel(int x, int y) {} 563 void onJoystickButtonDown(int button) {} 564 void onJoystickButtonUp(int button) {} 565 void onJoystickAxisMotion(int axis, float value) {} 566 void onResize(int width, int height) {} 567 void onFocusLoss() {} 568 void onFocusGain() {} 569 void onQuit() {} 570 void onFileChange(string filename) {} 571 void onDropFile(string filename) {} 572 void onUserEvent(int code) {} 573 }