1 /++ 2 A module for listening to incoming events from the window manager for their 3 subsequent processing. 4 5 Such a layer does not admit events directly to the data, but whether it can show 6 what is happening at the moment, which can serve as a cross-plotter tracking 7 of events. 8 9 Using the IEventHandler.nextEvent function, you can scroll through the queue of 10 events that can be processed and at each queue, the programmer needs to track 11 the events he needs by the functions of the same interface: 12 --- 13 while (event.nextEvent()) { 14 if (event.keyDown == Key.Space) foo(); 15 } 16 --- 17 As you can see, we loop through each event and read what happened. 18 19 Macros: 20 LREF = <a href="#$1">$1</a> 21 HREF = <a href="$1">$2</a> 22 23 Authors: $(HREF https://github.com/TodNaz,TodNaz) 24 Copyright: Copyright (c) 2020 - 2021, TodNaz. 25 License: $(HREF https://github.com/TodNaz/Tida/blob/master/LICENSE,MIT) 26 +/ 27 module tida.event; 28 29 enum DeprecatedMethodSize = 30 "This function is useless. Use the parameters `IWindow.width/IWindow.height`."; 31 32 /// Mouse keys. 33 enum MouseButton 34 { 35 unknown = 0, /// Unknown mouse button 36 left = 1, /// Left mouse button 37 right = 3, /// Right mouse button 38 middle = 2 /// Middle mouse button 39 } 40 41 /++ 42 Interface for cross-platform listening for events from the window manager. 43 +/ 44 interface IEventHandler 45 { 46 @safe: 47 /++ 48 Moves to the next event. If there are no more events, it returns false, 49 otherwise, it throws true and the programmer can safely check which event 50 s in the current queue. 51 +/ 52 bool nextEvent(); 53 54 /++ 55 Checking if any key is pressed in the current event. 56 +/ 57 bool isKeyDown(); 58 59 /++ 60 Checking if any key is released in the current event. 61 +/ 62 bool isKeyUp(); 63 64 /++ 65 Will return the key that was pressed. Returns zero if no key is pressed 66 in the current event. 67 +/ 68 @property int key(); 69 70 /++ 71 Returns the key at the moment the key was pressed, 72 otherwise it returns zero. 73 +/ 74 @property final int keyDown() 75 { 76 return isKeyDown ? key : 0; 77 } 78 79 /++ 80 Returns the key at the moment the key was released, 81 otherwise it returns zero. 82 +/ 83 @property final int keyUp() 84 { 85 return isKeyUp ? key : 0; 86 } 87 88 /++ 89 Check if the mouse button is pressed in the current event. 90 +/ 91 bool isMouseDown(); 92 93 /++ 94 Check if the mouse button is released in the current event. 95 +/ 96 bool isMouseUp(); 97 98 /++ 99 Returns the currently pressed or released mouse button. 100 +/ 101 @property MouseButton mouseButton(); 102 103 /++ 104 Returns the mouse button at the moment the key was pressed; 105 otherwise, it returns zero. 106 +/ 107 @property final MouseButton mouseDownButton() 108 { 109 return isMouseDown ? mouseButton : MouseButton.unknown; 110 } 111 112 /++ 113 Returns the mouse button at the moment the key was released; 114 otherwise, it returns zero. 115 +/ 116 @property final MouseButton mouseUpButton() 117 { 118 return isMouseUp ? mouseButton : MouseButton.unknown; 119 } 120 121 /++ 122 Returns the position of the mouse in the window. 123 +/ 124 @property int[2] mousePosition(); 125 126 /++ 127 Returns in which direction the user is turning the mouse wheel. 128 1 - down, -1 - up, 0 - does not twist. 129 130 This iteration is convenient for multiplying with some real movement coefficient. 131 +/ 132 @property int mouseWheel(); 133 134 /++ 135 Indicates whether the window has been resized in this event. 136 +/ 137 bool isResize(); 138 139 /++ 140 Returns the new size of the window 141 142 deprecated: 143 although this will already be available directly in the 144 window structure itself. 145 +/ 146 deprecated(DeprecatedMethodSize) 147 uint[2] newSizeWindow(); 148 149 /++ 150 Indicates whether the user is attempting to exit the program. 151 +/ 152 bool isQuit(); 153 154 /++ 155 Indicates whether the user has entered text information. 156 +/ 157 bool isInputText(); 158 159 /++ 160 User entered data. 161 +/ 162 @property string inputChar(); 163 164 @trusted: 165 final int opApply(scope int delegate(ref int) dg) 166 { 167 int count = 0; 168 169 while (this.nextEvent()) 170 { 171 dg(++count); 172 } 173 174 return 0; 175 } 176 } 177 178 version(Posix) 179 class EventHandler : IEventHandler 180 { 181 import x11.X, x11.Xlib, x11.Xutil; 182 import tida.window, tida.runtime; 183 184 private: 185 tida.window.Window window; 186 Atom destroyWindowEvent; 187 _XIC* ic; 188 189 public: 190 XEvent event; 191 192 @trusted: 193 this(tida.window.Window window) 194 { 195 this.window = window; 196 197 this.destroyWindowEvent = XInternAtom(runtime.display, "WM_DELETE_WINDOW", 0); 198 199 ic = XCreateIC( XOpenIM(runtime.display, null, null, null), 200 XNInputStyle, XIMPreeditNothing | XIMStatusNothing, 201 XNClientWindow, this.window.handle, null); 202 XSetICFocus(ic); 203 XSetLocaleModifiers("@im=none"); 204 } 205 206 override: 207 bool nextEvent() 208 { 209 auto pen = XPending(runtime.display); 210 211 if (pen != 0) 212 { 213 XNextEvent(runtime.display, &this.event); 214 } 215 216 return pen != 0; 217 } 218 219 bool isKeyDown() 220 { 221 return this.event.type == KeyPress; 222 } 223 224 bool isKeyUp() 225 { 226 return this.event.type == KeyRelease; 227 } 228 229 @property int key() 230 { 231 return this.event.xkey.keycode; 232 } 233 234 bool isMouseDown() 235 { 236 return this.event.type == ButtonPress; 237 } 238 239 bool isMouseUp() 240 { 241 return this.event.type == ButtonRelease; 242 } 243 244 @property MouseButton mouseButton() 245 { 246 return cast(MouseButton) this.event.xbutton.button; 247 } 248 249 @property int[2] mousePosition() @trusted 250 { 251 return [this.event.xmotion.x, this.event.xmotion.y]; 252 } 253 254 @property int mouseWheel() 255 { 256 return this.isMouseDown ? 257 (this.mouseButton == 4 ? -1 : (this.mouseButton == 5 ? 1 : 0)) : 0; 258 } 259 260 bool isResize() 261 { 262 return this.event.type == ConfigureNotify; 263 } 264 265 uint[2] newSizeWindow() 266 { 267 XWindowAttributes attr; 268 XGetWindowAttributes( runtime.display, 269 (cast(tida.window.Window) this.window).handle, &attr); 270 271 return [attr.width, attr.height]; 272 } 273 274 bool isQuit() 275 { 276 return this.event.xclient.data.l[0] == this.destroyWindowEvent; 277 } 278 279 bool isInputText() 280 { 281 return this.isKeyDown; 282 } 283 284 @property string inputChar() 285 { 286 int count; 287 string buf = new string(20); 288 KeySym ks; 289 Status status = 0; 290 291 count = Xutf8LookupString( this.ic, cast(XKeyPressedEvent*) &this.event.xkey, 292 cast(char*) buf.ptr, 20, &ks, &status); 293 294 return buf[0 .. count]; 295 } 296 } 297 298 version(Windows) 299 class EventHandler : IEventHandler 300 { 301 import tida.window, tida.runtime; 302 import core.sys.windows.windows; 303 304 private: 305 tida.window.Window window; 306 307 public: 308 MSG msg; 309 310 @safe: 311 this(tida.window.Window window) 312 { 313 this.window = window; 314 } 315 316 @trusted: 317 bool nextEvent() 318 { 319 TranslateMessage(&this.msg); 320 DispatchMessage(&this.msg); 321 322 return PeekMessage(&this.msg, this.window.handle, 0, 0, PM_REMOVE) != 0; 323 } 324 325 bool isKeyDown() 326 { 327 return this.msg.message == WM_KEYDOWN; 328 } 329 330 bool isKeyUp() 331 { 332 return this.msg.message == WM_KEYUP; 333 } 334 335 @property int key() 336 { 337 return cast(int) this.msg.wParam; 338 } 339 340 bool isMouseDown() 341 { 342 return this.msg.message == WM_LBUTTONDOWN || 343 this.msg.message == WM_RBUTTONDOWN || 344 this.msg.message == WM_MBUTTONDOWN; 345 } 346 347 bool isMouseUp() 348 { 349 return this.msg.message == WM_LBUTTONUP || 350 this.msg.message == WM_RBUTTONUP || 351 this.msg.message == WM_MBUTTONUP; 352 } 353 354 @property MouseButton mouseButton() 355 { 356 if (this.msg.message == WM_LBUTTONUP || this.msg.message == WM_LBUTTONDOWN) 357 return MouseButton.left; 358 359 if (this.msg.message == WM_RBUTTONUP || this.msg.message == WM_RBUTTONDOWN) 360 return MouseButton.right; 361 362 if (this.msg.message == WM_MBUTTONUP || this.msg.message == WM_MBUTTONDOWN) 363 return MouseButton.middle; 364 365 return MouseButton.unknown; 366 } 367 368 @property int[2] mousePosition() 369 { 370 POINT p; 371 GetCursorPos(&p); 372 ScreenToClient((cast(Window) this.window).handle, &p); 373 374 return [p.x, p.y]; 375 } 376 377 @property int mouseWheel() 378 { 379 if (this.msg.message != WM_MOUSEWHEEL) return 0; 380 381 return (cast(int) this.msg.wParam) > 0 ? -1 : 1; 382 } 383 384 bool isResize() 385 { 386 return this.msg.message == WM_SIZE; 387 } 388 389 uint[2] newSizeWindow() 390 { 391 RECT rect; 392 GetWindowRect((cast(Window) this.window).handle, &rect); 393 394 return [rect.right, rect.bottom]; 395 } 396 397 bool isQuit() 398 { 399 return window.isClose; 400 } 401 402 bool isInputText() 403 { 404 return this.msg.message == WM_CHAR; 405 } 406 407 string inputChar() 408 { 409 import std.utf : toUTF8; 410 411 wstring text = []; 412 text = [cast(wchar) msg.wParam]; 413 414 string utftext = text.toUTF8; 415 416 return [utftext[0]]; 417 } 418 } 419 420 version(Posix)/// 421 static enum Key 422 { 423 Escape = 9, 424 F1 = 67, 425 F2 = 68, 426 F3 = 69, 427 F4 = 70, 428 F5 = 71, 429 F6 = 72, 430 F7 = 73, 431 F8 = 74, 432 F9 = 75, 433 F10 = 76, 434 F11 = 95, 435 F12 = 96, 436 PrintScrn = 111, 437 ScrollLock = 78, 438 Pause = 110, 439 Backtick = 49, 440 K1 = 10, 441 K2 = 11, 442 K3 = 12, 443 K4 = 13, 444 K5 = 14, 445 K6 = 15, 446 K7 = 16, 447 K8 = 17, 448 K9 = 18, 449 K0 = 19, 450 Minus = 20, 451 Equal = 21, 452 Backspace = 22, 453 Insert = 106, 454 Home = 97, 455 PageUp = 99, 456 NumLock = 77, 457 KPSlash = 112, 458 KPStar = 63, 459 KPMinus = 82, 460 Tab = 23, 461 Q = 24, 462 W = 25, 463 E = 26, 464 R = 27, 465 T = 28, 466 Y = 29, 467 U = 30, 468 I = 31, 469 O = 32, 470 P = 33, 471 472 SqBrackLeft = 34, 473 SqBrackRight = 35, 474 SquareBracketLeft = 34, 475 SquareBracketRight = 35, 476 477 Return = 36, 478 Delete = 107, 479 End = 103, 480 PageDown = 105, 481 482 KP7 = 79, 483 KP8 = 80, 484 KP9 = 81, 485 486 CapsLock = 66, 487 A = 38, 488 S = 39, 489 D = 40, 490 F = 41, 491 G = 42, 492 H = 43, 493 J = 44, 494 K = 45, 495 L = 46, 496 Semicolons = 47, 497 Apostrophe = 48, 498 499 KP4 = 83, 500 KP5 = 84, 501 KP6 = 85, 502 503 ShiftLeft = 50, 504 International = 94, 505 506 Z = 52, 507 X = 53, 508 C = 54, 509 V = 55, 510 B = 56, 511 N = 57, 512 M = 58, 513 Comma = 59, 514 Point = 60, 515 Slash = 61, 516 517 ShiftRight = 62, 518 519 BackSlash = 51, 520 Up = 111, 521 522 KP1 = 87, 523 KP2 = 88, 524 KP3 = 89, 525 526 KPEnter = 108, 527 CtrlLeft = 37, 528 SuperLeft = 115, 529 AltLeft = 64, 530 Space = 65, 531 AltRight = 113, 532 LogoRight = 116, 533 Menu = 117, 534 CtrlRight = 109, 535 Left = 113, 536 Down = 116, 537 Right = 114, 538 KP0 = 90, 539 KPPoint = 91 540 } 541 542 version(Windows)/// 543 static enum Key 544 { 545 Escape = 0x1B, 546 F1 = 0x70, 547 F2 = 0x71, 548 F3 = 0x72, 549 F4 = 0x73, 550 F5 = 0x74, 551 F6 = 0x75, 552 F7 = 0x76, 553 F8 = 0x77, 554 F9 = 0x78, 555 F10 = 0x79, 556 F11 = 0x7A, 557 F12 = 0x7B, 558 PrintScrn = 0x2A, 559 ScrollLock = 0x91, 560 Pause = 0x13, 561 Backtick = 0xC0, 562 K1 = 0x31, 563 K2 = 0x32, 564 K3 = 0x33, 565 K4 = 0x34, 566 K5 = 0x35, 567 K6 = 0x36, 568 K7 = 0x37, 569 K8 = 0x38, 570 K9 = 0x39, 571 K0 = 0x30, 572 Minus = 0xBD, 573 Equal = 0xBB, 574 Backspace = 0x08, 575 Insert = 0x2D, 576 Home = 0x24, 577 PageUp = 0x21, 578 NumLock = 0x90, 579 KPSlash = 0x6F, 580 KPStar = 0xBB, 581 KPMinus = 0xBD, 582 Tab = 0x09, 583 Q = 0x51, 584 W = 0x57, 585 E = 0x45, 586 R = 0x52, 587 T = 0x54, 588 Y = 0x59, 589 U = 0x55, 590 I = 0x49, 591 O = 0x4F, 592 P = 0x50, 593 594 SqBrackLeft = 0xDB, 595 SqBrackRight = 0xDD, 596 SquareBracketLeft = 0x30, 597 SquareBracketRight = 0xBD, 598 599 Return = 0x0D, 600 Delete = 0x2E, 601 End = 0x23, 602 PageDown = 0x22, 603 604 KP7 = 0x67, 605 KP8 = 0x68, 606 KP9 = 0x69, 607 608 CapsLock = 0x14, 609 A = 0x41, 610 S = 0x53, 611 D = 0x44, 612 F = 0x46, 613 G = 0x47, 614 H = 0x48, 615 J = 0x4A, 616 K = 0x4B, 617 L = 0x4C, 618 Semicolons = 0xBA, 619 Apostrophe = 0xBF, 620 621 KP4 = 0x64, 622 KP5 = 0x65, 623 KP6 = 0x66, 624 625 ShiftLeft = 0xA0, 626 International = 0xA4, 627 628 Z = 0x5A, 629 X = 0x58, 630 C = 0x43, 631 V = 0x56, 632 B = 0x42, 633 N = 0x4E, 634 M = 0x4D, 635 Comma = 0xBC, 636 Point = 0xBE, 637 Slash = 0xBF, 638 639 ShiftRight = 0xA1, 640 641 BackSlash = 0xE2, 642 Up = 0x26, 643 644 KP1 = 0x61, 645 KP2 = 0x62, 646 KP3 = 0x63, 647 648 KPEnter = 0x6A, 649 CtrlLeft = 0xA2, 650 SuperLeft = 0xA4, 651 AltLeft = 0xA4, 652 Space = 0x20, 653 AltRight = 0xA5, 654 SuperRight = 0xA5, 655 Menu = 0, 656 CtrlRight = 0xA3, 657 Left = 0x25, 658 Down = 0x28, 659 Right = 0x27, 660 KP0 = 0x60, 661 KPPoint = 0x6F 662 }