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 dlib.core.memory; 35 import dagon.core.libs; 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 //auto unicode = event.key.keysym.keycode; 233 234 if ((event.key.keysym.sym & 0xFF80) == 0) 235 { 236 auto asciiChar = event.key.keysym.sym & 0x7F; 237 if (isPrintable(asciiChar)) 238 { 239 e = Event(EventType.TextInput); 240 e.unicode = asciiChar; 241 addEvent(e); 242 } 243 } 244 else 245 { 246 e = Event(EventType.TextInput); 247 e.unicode = event.key.keysym.sym; //event.key.keysym.unicode; 248 addEvent(e); 249 } 250 251 keyPressed[event.key.keysym.scancode] = true; 252 e = Event(EventType.KeyDown); 253 e.key = event.key.keysym.scancode; 254 addEvent(e); 255 break; 256 257 case SDL_KEYUP: 258 keyPressed[event.key.keysym.scancode] = false; 259 e = Event(EventType.KeyUp); 260 e.key = event.key.keysym.scancode; 261 addEvent(e); 262 break; 263 264 case SDL_MOUSEMOTION: 265 mouseX = event.motion.x; 266 mouseY = event.motion.y; 267 mouseRelX = event.motion.xrel; 268 mouseRelY = event.motion.yrel; 269 break; 270 271 case SDL_MOUSEBUTTONDOWN: 272 mouseButtonPressed[event.button.button] = true; 273 e = Event(EventType.MouseButtonDown); 274 e.button = event.button.button; 275 addEvent(e); 276 break; 277 278 case SDL_MOUSEBUTTONUP: 279 mouseButtonPressed[event.button.button] = false; 280 e = Event(EventType.MouseButtonUp); 281 e.button = event.button.button; 282 addEvent(e); 283 break; 284 285 case SDL_MOUSEWHEEL: 286 e = Event(EventType.MouseWheel); 287 e.mouseWheelX = event.wheel.x; 288 e.mouseWheelY = event.wheel.y; 289 addEvent(e); 290 break; 291 292 case SDL_JOYBUTTONDOWN: 293 if (event.jbutton.state == SDL_PRESSED) 294 e = Event(EventType.JoystickButtonDown); 295 else if (event.jbutton.state == SDL_RELEASED) 296 e = Event(EventType.JoystickButtonUp); 297 e.joystickButton = event.jbutton.button; 298 addEvent(e); 299 break; 300 301 case SDL_JOYBUTTONUP: 302 // TODO: add state modification 303 if (event.jbutton.state == SDL_PRESSED) 304 e = Event(EventType.JoystickButtonDown); 305 else if (event.jbutton.state == SDL_RELEASED) 306 e = Event(EventType.JoystickButtonUp); 307 e.joystickButton = event.jbutton.button; 308 addEvent(e); 309 break; 310 311 case SDL_CONTROLLERBUTTONDOWN: 312 // TODO: add state modification 313 if (event.cbutton.state == SDL_PRESSED) 314 e = Event(EventType.JoystickButtonDown); 315 else if (event.cbutton.state == SDL_RELEASED) 316 e = Event(EventType.JoystickButtonUp); 317 e.joystickButton = event.cbutton.button; 318 addEvent(e); 319 break; 320 321 case SDL_CONTROLLERBUTTONUP: 322 // TODO: add state modification 323 if (event.cbutton.state == SDL_PRESSED) 324 e = Event(EventType.JoystickButtonDown); 325 else if (event.cbutton.state == SDL_RELEASED) 326 e = Event(EventType.JoystickButtonUp); 327 e.joystickButton = event.cbutton.button; 328 addEvent(e); 329 break; 330 331 case SDL_CONTROLLERAXISMOTION: 332 // TODO: add state modification 333 e = Event(EventType.JoystickAxisMotion); 334 e.joystickAxis = event.caxis.axis; 335 e.joystickAxisValue = cast(float)event.caxis.value / 32768.0f; 336 337 if (controller) 338 { 339 if (e.joystickAxis == 0) 340 e.joystickAxisValue = SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_LEFTY); 341 if (e.joystickAxis == 1) 342 e.joystickAxisValue = SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_LEFTX); 343 344 e.joystickAxisValue = e.joystickAxisValue / 32768.0f; 345 } 346 347 addEvent(e); 348 break; 349 350 case SDL_WINDOWEVENT: 351 if (event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) 352 { 353 windowWidth = event.window.data1; 354 windowHeight = event.window.data2; 355 e = Event(EventType.Resize); 356 e.width = windowWidth; 357 e.height = windowHeight; 358 addEvent(e); 359 } 360 break; 361 /* 362 case SDL_ACTIVEEVENT: 363 if (event.active.state & SDL_APPACTIVE) 364 { 365 if (event.active.gain == 0) 366 { 367 writeln("Deactivated"); 368 windowFocused = false; 369 e = Event(EventType.FocusLoss); 370 } 371 else 372 { 373 writeln("Activated"); 374 windowFocused = true; 375 e = Event(EventType.FocusGain); 376 } 377 } 378 else if (event.active.state & SDL_APPINPUTFOCUS) 379 { 380 if (event.active.gain == 0) 381 { 382 writeln("Lost focus"); 383 windowFocused = false; 384 e = Event(EventType.FocusLoss); 385 } 386 else 387 { 388 writeln("Gained focus"); 389 windowFocused = true; 390 e = Event(EventType.FocusGain); 391 } 392 } 393 addEvent(e); 394 break; 395 */ 396 case SDL_QUIT: 397 running = false; 398 e = Event(EventType.Quit); 399 addEvent(e); 400 break; 401 402 default: 403 break; 404 } 405 } 406 } 407 408 void updateTimer() 409 { 410 static int currentTime; 411 static int lastTime; 412 413 static int FPSTickCounter; 414 static int FPSCounter = 0; 415 416 currentTime = SDL_GetTicks(); 417 auto elapsedTime = currentTime - lastTime; 418 lastTime = currentTime; 419 deltaTimeMs = elapsedTime; 420 deltaTime = cast(double)(elapsedTime) * 0.001; 421 422 FPSTickCounter += elapsedTime; 423 FPSCounter++; 424 if (FPSTickCounter >= 1000) // 1 sec interval 425 { 426 fps = FPSCounter; 427 FPSCounter = 0; 428 FPSTickCounter = 0; 429 averageDelta = 1.0 / cast(double)(fps); 430 } 431 } 432 433 void setMouse(int x, int y) 434 { 435 SDL_WarpMouseInWindow(window, x, y); 436 mouseX = x; 437 mouseY = y; 438 } 439 440 void setMouseToCenter() 441 { 442 float x = (cast(float)windowWidth)/2; 443 float y = (cast(float)windowHeight)/2; 444 setMouse(cast(int)x, cast(int)y); 445 } 446 447 void showCursor(bool mode) 448 { 449 SDL_ShowCursor(mode); 450 } 451 452 float aspectRatio() 453 { 454 return cast(float)windowWidth / cast(float)windowHeight; 455 } 456 } 457 458 abstract class EventListener: Owner 459 { 460 EventManager eventManager; 461 bool enabled = true; 462 463 this(EventManager emngr, Owner owner) 464 { 465 super(owner); 466 eventManager = emngr; 467 } 468 469 protected void generateUserEvent(int code) 470 { 471 eventManager.generateUserEvent(code); 472 } 473 474 void processEvents() 475 { 476 if (!enabled) 477 return; 478 479 for (uint i = 0; i < eventManager.numEvents; i++) 480 { 481 Event* e = &eventManager.eventStack[i]; 482 processEvent(e); 483 } 484 } 485 486 void processEvent(Event* e) 487 { 488 switch(e.type) 489 { 490 case EventType.KeyDown: 491 onKeyDown(e.key); 492 break; 493 case EventType.KeyUp: 494 onKeyUp(e.key); 495 break; 496 case EventType.TextInput: 497 onTextInput(e.unicode); 498 break; 499 case EventType.MouseButtonDown: 500 onMouseButtonDown(e.button); 501 break; 502 case EventType.MouseButtonUp: 503 onMouseButtonUp(e.button); 504 break; 505 case EventType.MouseWheel: 506 onMouseWheel(e.mouseWheelX, e.mouseWheelY); 507 break; 508 case EventType.JoystickButtonDown: 509 onJoystickButtonDown(e.joystickButton); 510 break; 511 case EventType.JoystickButtonUp: 512 onJoystickButtonUp(e.joystickButton); 513 break; 514 case EventType.JoystickAxisMotion: 515 onJoystickAxisMotion(e.joystickAxis, e.joystickAxisValue); 516 break; 517 case EventType.Resize: 518 onResize(e.width, e.height); 519 break; 520 case EventType.FocusLoss: 521 onFocusLoss(); 522 break; 523 case EventType.FocusGain: 524 onFocusGain(); 525 break; 526 case EventType.Quit: 527 onQuit(); 528 break; 529 case EventType.AssetReload: 530 onAssetReload(e.asset); 531 break; 532 case EventType.UserEvent: 533 onUserEvent(e.userCode); 534 break; 535 default: 536 break; 537 } 538 } 539 540 void onKeyDown(int key) {} 541 void onKeyUp(int key) {} 542 void onTextInput(dchar code) {} 543 void onMouseButtonDown(int button) {} 544 void onMouseButtonUp(int button) {} 545 void onMouseWheel(int x, int y) {} 546 void onJoystickButtonDown(int button) {} 547 void onJoystickButtonUp(int button) {} 548 void onJoystickAxisMotion(int axis, float value) {} 549 void onResize(int width, int height) {} 550 void onFocusLoss() {} 551 void onFocusGain() {} 552 void onQuit() {} 553 void onAssetReload(Asset asset) {} 554 void onUserEvent(int code) {} 555 }