1 /* 2 Copyright (c) 2014-2018 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 dagon.core.event; 30 31 import std.stdio; 32 import std.ascii; 33 import std.conv; 34 import derelict.sdl2.sdl; 35 import dlib.core.memory; 36 import dagon.core.ownership; 37 import dagon.resource.asset; 38 39 enum EventType 40 { 41 KeyDown, 42 KeyUp, 43 TextInput, 44 MouseMotion, 45 MouseButtonDown, 46 MouseButtonUp, 47 MouseWheel, 48 JoystickButtonDown, 49 JoystickButtonUp, 50 JoystickAxisMotion, 51 Resize, 52 FocusLoss, 53 FocusGain, 54 Quit, 55 AssetReload, 56 UserEvent 57 } 58 59 struct Event 60 { 61 EventType type; 62 int key; 63 dchar unicode; 64 int button; 65 int joystickButton; 66 int joystickAxis; 67 float joystickAxisValue; 68 int width; 69 int height; 70 int userCode; 71 int mouseWheelX; 72 int mouseWheelY; 73 Asset asset; 74 } 75 76 class EventManager 77 { 78 SDL_Window* window; 79 80 enum maxNumEvents = 50; 81 Event[maxNumEvents] eventStack; 82 Event[maxNumEvents] userEventStack; 83 uint numEvents; 84 uint numUserEvents; 85 86 bool running = true; 87 88 bool[512] keyPressed = false; 89 bool[255] mouseButtonPressed = false; 90 int mouseX = 0; 91 int mouseY = 0; 92 int mouseRelX = 0; 93 int mouseRelY = 0; 94 bool enableKeyRepeat = false; 95 96 double deltaTime = 0.0; 97 double averageDelta = 0.0; 98 uint deltaTimeMs = 0; 99 int fps = 0; 100 101 //uint videoWidth; 102 //uint videoHeight; 103 104 uint windowWidth; 105 uint windowHeight; 106 bool windowFocused = true; 107 108 SDL_GameController* controller = null; 109 SDL_Joystick* joystick = null; 110 111 this(SDL_Window* win, uint winWidth, uint winHeight) 112 { 113 window = win; 114 115 windowWidth = winWidth; 116 windowHeight = winHeight; 117 118 //auto videoInfo = SDL_GetVideoInfo(); 119 //videoWidth = videoInfo.current_w; 120 //videoHeight = videoInfo.current_h; 121 122 SDL_InitSubSystem(SDL_INIT_JOYSTICK); 123 124 if (SDL_IsGameController(0)) 125 { 126 controller = SDL_GameControllerOpen(0); 127 128 SDL_GameControllerAddMappingsFromFile("gamecontrollerdb.txt"); 129 130 if (SDL_GameControllerMapping(controller) is null) 131 writeln("Warning: no mapping found for controller!"); 132 133 SDL_GameControllerEventState(SDL_ENABLE); 134 } 135 else 136 { 137 joystick = SDL_JoystickOpen(0); 138 } 139 } 140 141 void addEvent(Event e) 142 { 143 if (numEvents < maxNumEvents) 144 { 145 eventStack[numEvents] = e; 146 numEvents++; 147 } 148 else 149 writeln("Warning: event stack overflow"); 150 } 151 152 void generateAssetReloadEvent(Asset asset) 153 { 154 Event e = Event(EventType.AssetReload); 155 e.asset = asset; 156 addUserEvent(e); 157 } 158 159 void addUserEvent(Event e) 160 { 161 if (numUserEvents < maxNumEvents) 162 { 163 userEventStack[numUserEvents] = e; 164 numUserEvents++; 165 } 166 else 167 writeln("Warning: user event stack overflow"); 168 } 169 170 void generateUserEvent(int code) 171 { 172 Event e = Event(EventType.UserEvent); 173 e.userCode = code; 174 addUserEvent(e); 175 } 176 177 bool gameControllerAvailable() 178 { 179 return (controller !is null); 180 } 181 182 bool joystickAvailable() 183 { 184 return (joystick !is null || controller !is null); 185 } 186 187 float gameControllerAxis(int axis) 188 { 189 return cast(float)(SDL_GameControllerGetAxis(controller, cast(SDL_GameControllerAxis)axis)) / 32768.0f; 190 } 191 192 float joystickAxis(int axis) 193 { 194 if (joystick) 195 { 196 double a = cast(double)(SDL_JoystickGetAxis(joystick, axis)); 197 return a / 32768.0f; //28000.0f; 198 } 199 else if (controller) 200 { 201 return cast(float)(SDL_GameControllerGetAxis(controller, cast(SDL_GameControllerAxis)axis)) / 32768.0f; 202 } 203 else return 0.0; 204 } 205 206 void update() 207 { 208 numEvents = 0; 209 updateTimer(); 210 211 mouseRelX = 0; 212 mouseRelY = 0; 213 214 for (uint i = 0; i < numUserEvents; i++) 215 { 216 Event e = userEventStack[i]; 217 addEvent(e); 218 } 219 220 numUserEvents = 0; 221 222 SDL_Event event; 223 224 while(SDL_PollEvent(&event)) 225 { 226 Event e; 227 switch (event.type) 228 { 229 case SDL_KEYDOWN: 230 if (event.key.repeat && !enableKeyRepeat) 231 break; 232 if ((event.key.keysym.unicode & 0xFF80) == 0) 233 { 234 auto asciiChar = event.key.keysym.unicode & 0x7F; 235 if (isPrintable(asciiChar)) 236 { 237 e = Event(EventType.TextInput); 238 e.unicode = asciiChar; 239 addEvent(e); 240 } 241 } 242 else 243 { 244 e = Event(EventType.TextInput); 245 e.unicode = event.key.keysym.unicode; 246 addEvent(e); 247 } 248 249 keyPressed[event.key.keysym.scancode] = true; 250 e = Event(EventType.KeyDown); 251 e.key = event.key.keysym.scancode; 252 addEvent(e); 253 break; 254 255 case SDL_KEYUP: 256 keyPressed[event.key.keysym.scancode] = false; 257 e = Event(EventType.KeyUp); 258 e.key = event.key.keysym.scancode; 259 addEvent(e); 260 break; 261 262 case SDL_MOUSEMOTION: 263 mouseX = event.motion.x; 264 mouseY = event.motion.y; 265 mouseRelX = event.motion.xrel; 266 mouseRelY = event.motion.yrel; 267 break; 268 269 case SDL_MOUSEBUTTONDOWN: 270 mouseButtonPressed[event.button.button] = true; 271 e = Event(EventType.MouseButtonDown); 272 e.button = event.button.button; 273 addEvent(e); 274 break; 275 276 case SDL_MOUSEBUTTONUP: 277 mouseButtonPressed[event.button.button] = false; 278 e = Event(EventType.MouseButtonUp); 279 e.button = event.button.button; 280 addEvent(e); 281 break; 282 283 case SDL_MOUSEWHEEL: 284 e = Event(EventType.MouseWheel); 285 e.mouseWheelX = event.wheel.x; 286 e.mouseWheelY = event.wheel.y; 287 addEvent(e); 288 break; 289 290 case SDL_JOYBUTTONDOWN: 291 if (event.jbutton.state == SDL_PRESSED) 292 e = Event(EventType.JoystickButtonDown); 293 else if (event.jbutton.state == SDL_RELEASED) 294 e = Event(EventType.JoystickButtonUp); 295 e.joystickButton = event.jbutton.button; 296 addEvent(e); 297 break; 298 299 case SDL_JOYBUTTONUP: 300 // TODO: add state modification 301 if (event.jbutton.state == SDL_PRESSED) 302 e = Event(EventType.JoystickButtonDown); 303 else if (event.jbutton.state == SDL_RELEASED) 304 e = Event(EventType.JoystickButtonUp); 305 e.joystickButton = event.jbutton.button; 306 addEvent(e); 307 break; 308 309 case SDL_CONTROLLERBUTTONDOWN: 310 // TODO: add state modification 311 if (event.cbutton.state == SDL_PRESSED) 312 e = Event(EventType.JoystickButtonDown); 313 else if (event.cbutton.state == SDL_RELEASED) 314 e = Event(EventType.JoystickButtonUp); 315 e.joystickButton = event.cbutton.button; 316 addEvent(e); 317 break; 318 319 case SDL_CONTROLLERBUTTONUP: 320 // TODO: add state modification 321 if (event.cbutton.state == SDL_PRESSED) 322 e = Event(EventType.JoystickButtonDown); 323 else if (event.cbutton.state == SDL_RELEASED) 324 e = Event(EventType.JoystickButtonUp); 325 e.joystickButton = event.cbutton.button; 326 addEvent(e); 327 break; 328 329 case SDL_CONTROLLERAXISMOTION: 330 // TODO: add state modification 331 e = Event(EventType.JoystickAxisMotion); 332 e.joystickAxis = event.caxis.axis; 333 e.joystickAxisValue = cast(float)event.caxis.value / 32768.0f; 334 335 if (controller) 336 { 337 if (e.joystickAxis == 0) 338 e.joystickAxisValue = SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_LEFTY); 339 if (e.joystickAxis == 1) 340 e.joystickAxisValue = SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_LEFTX); 341 342 e.joystickAxisValue = e.joystickAxisValue / 32768.0f; 343 } 344 345 addEvent(e); 346 break; 347 348 case SDL_WINDOWEVENT: 349 if (event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) 350 { 351 windowWidth = event.window.data1; 352 windowHeight = event.window.data2; 353 e = Event(EventType.Resize); 354 e.width = windowWidth; 355 e.height = windowHeight; 356 addEvent(e); 357 } 358 break; 359 /* 360 case SDL_ACTIVEEVENT: 361 if (event.active.state & SDL_APPACTIVE) 362 { 363 if (event.active.gain == 0) 364 { 365 writeln("Deactivated"); 366 windowFocused = false; 367 e = Event(EventType.FocusLoss); 368 } 369 else 370 { 371 writeln("Activated"); 372 windowFocused = true; 373 e = Event(EventType.FocusGain); 374 } 375 } 376 else if (event.active.state & SDL_APPINPUTFOCUS) 377 { 378 if (event.active.gain == 0) 379 { 380 writeln("Lost focus"); 381 windowFocused = false; 382 e = Event(EventType.FocusLoss); 383 } 384 else 385 { 386 writeln("Gained focus"); 387 windowFocused = true; 388 e = Event(EventType.FocusGain); 389 } 390 } 391 addEvent(e); 392 break; 393 */ 394 case SDL_QUIT: 395 running = false; 396 e = Event(EventType.Quit); 397 addEvent(e); 398 break; 399 400 default: 401 break; 402 } 403 } 404 } 405 406 void updateTimer() 407 { 408 static int currentTime; 409 static int lastTime; 410 411 static int FPSTickCounter; 412 static int FPSCounter = 0; 413 414 currentTime = SDL_GetTicks(); 415 auto elapsedTime = currentTime - lastTime; 416 lastTime = currentTime; 417 deltaTimeMs = elapsedTime; 418 deltaTime = cast(double)(elapsedTime) * 0.001; 419 420 FPSTickCounter += elapsedTime; 421 FPSCounter++; 422 if (FPSTickCounter >= 1000) // 1 sec interval 423 { 424 fps = FPSCounter; 425 FPSCounter = 0; 426 FPSTickCounter = 0; 427 averageDelta = 1.0 / cast(double)(fps); 428 } 429 } 430 431 void setMouse(int x, int y) 432 { 433 SDL_WarpMouseInWindow(window, x, y); 434 mouseX = x; 435 mouseY = y; 436 } 437 438 void setMouseToCenter() 439 { 440 float x = (cast(float)windowWidth)/2; 441 float y = (cast(float)windowHeight)/2; 442 setMouse(cast(int)x, cast(int)y); 443 } 444 445 void showCursor(bool mode) 446 { 447 SDL_ShowCursor(mode); 448 } 449 450 float aspectRatio() 451 { 452 return cast(float)windowWidth / cast(float)windowHeight; 453 } 454 } 455 456 abstract class EventListener: Owner 457 { 458 EventManager eventManager; 459 bool enabled = true; 460 461 this(EventManager emngr, Owner owner) 462 { 463 super(owner); 464 eventManager = emngr; 465 } 466 467 protected void generateUserEvent(int code) 468 { 469 eventManager.generateUserEvent(code); 470 } 471 472 void processEvents() 473 { 474 if (!enabled) 475 return; 476 477 for (uint i = 0; i < eventManager.numEvents; i++) 478 { 479 Event* e = &eventManager.eventStack[i]; 480 processEvent(e); 481 } 482 } 483 484 void processEvent(Event* e) 485 { 486 switch(e.type) 487 { 488 case EventType.KeyDown: 489 onKeyDown(e.key); 490 break; 491 case EventType.KeyUp: 492 onKeyUp(e.key); 493 break; 494 case EventType.TextInput: 495 onTextInput(e.unicode); 496 break; 497 case EventType.MouseButtonDown: 498 onMouseButtonDown(e.button); 499 break; 500 case EventType.MouseButtonUp: 501 onMouseButtonUp(e.button); 502 break; 503 case EventType.MouseWheel: 504 onMouseWheel(e.mouseWheelX, e.mouseWheelY); 505 break; 506 case EventType.JoystickButtonDown: 507 onJoystickButtonDown(e.joystickButton); 508 break; 509 case EventType.JoystickButtonUp: 510 onJoystickButtonUp(e.joystickButton); 511 break; 512 case EventType.JoystickAxisMotion: 513 onJoystickAxisMotion(e.joystickAxis, e.joystickAxisValue); 514 break; 515 case EventType.Resize: 516 onResize(e.width, e.height); 517 break; 518 case EventType.FocusLoss: 519 onFocusLoss(); 520 break; 521 case EventType.FocusGain: 522 onFocusGain(); 523 break; 524 case EventType.Quit: 525 onQuit(); 526 break; 527 case EventType.AssetReload: 528 onAssetReload(e.asset); 529 break; 530 case EventType.UserEvent: 531 onUserEvent(e.userCode); 532 break; 533 default: 534 break; 535 } 536 } 537 538 void onKeyDown(int key) {} 539 void onKeyUp(int key) {} 540 void onTextInput(dchar code) {} 541 void onMouseButtonDown(int button) {} 542 void onMouseButtonUp(int button) {} 543 void onMouseWheel(int x, int y) {} 544 void onJoystickButtonDown(int button) {} 545 void onJoystickButtonUp(int button) {} 546 void onJoystickAxisMotion(int axis, float value) {} 547 void onResize(int width, int height) {} 548 void onFocusLoss() {} 549 void onFocusGain() {} 550 void onQuit() {} 551 void onAssetReload(Asset asset) {} 552 void onUserEvent(int code) {} 553 }