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 bool isResize = window.isResize; 387 window.isResize = false; 388 389 return isResize; 390 } 391 392 uint[2] newSizeWindow() 393 { 394 RECT rect; 395 GetWindowRect((cast(Window) this.window).handle, &rect); 396 397 return [rect.right, rect.bottom]; 398 } 399 400 bool isQuit() 401 { 402 return window.isClose; 403 } 404 405 bool isInputText() 406 { 407 return this.msg.message == WM_CHAR; 408 } 409 410 string inputChar() 411 { 412 import std.utf : toUTF8; 413 414 wstring text = []; 415 text = [cast(wchar) msg.wParam]; 416 417 string utftext = text.toUTF8; 418 419 return [utftext[0]]; 420 } 421 } 422 423 version(Posix)/// 424 static enum Key 425 { 426 Escape = 9, 427 F1 = 67, 428 F2 = 68, 429 F3 = 69, 430 F4 = 70, 431 F5 = 71, 432 F6 = 72, 433 F7 = 73, 434 F8 = 74, 435 F9 = 75, 436 F10 = 76, 437 F11 = 95, 438 F12 = 96, 439 PrintScrn = 111, 440 ScrollLock = 78, 441 Pause = 110, 442 Backtick = 49, 443 K1 = 10, 444 K2 = 11, 445 K3 = 12, 446 K4 = 13, 447 K5 = 14, 448 K6 = 15, 449 K7 = 16, 450 K8 = 17, 451 K9 = 18, 452 K0 = 19, 453 Minus = 20, 454 Equal = 21, 455 Backspace = 22, 456 Insert = 106, 457 Home = 97, 458 PageUp = 99, 459 NumLock = 77, 460 KPSlash = 112, 461 KPStar = 63, 462 KPMinus = 82, 463 Tab = 23, 464 Q = 24, 465 W = 25, 466 E = 26, 467 R = 27, 468 T = 28, 469 Y = 29, 470 U = 30, 471 I = 31, 472 O = 32, 473 P = 33, 474 475 SqBrackLeft = 34, 476 SqBrackRight = 35, 477 SquareBracketLeft = 34, 478 SquareBracketRight = 35, 479 480 Return = 36, 481 Delete = 107, 482 End = 103, 483 PageDown = 105, 484 485 KP7 = 79, 486 KP8 = 80, 487 KP9 = 81, 488 489 CapsLock = 66, 490 A = 38, 491 S = 39, 492 D = 40, 493 F = 41, 494 G = 42, 495 H = 43, 496 J = 44, 497 K = 45, 498 L = 46, 499 Semicolons = 47, 500 Apostrophe = 48, 501 502 KP4 = 83, 503 KP5 = 84, 504 KP6 = 85, 505 506 ShiftLeft = 50, 507 International = 94, 508 509 Z = 52, 510 X = 53, 511 C = 54, 512 V = 55, 513 B = 56, 514 N = 57, 515 M = 58, 516 Comma = 59, 517 Point = 60, 518 Slash = 61, 519 520 ShiftRight = 62, 521 522 BackSlash = 51, 523 Up = 111, 524 525 KP1 = 87, 526 KP2 = 88, 527 KP3 = 89, 528 529 KPEnter = 108, 530 CtrlLeft = 37, 531 SuperLeft = 115, 532 AltLeft = 64, 533 Space = 65, 534 AltRight = 113, 535 LogoRight = 116, 536 Menu = 117, 537 CtrlRight = 109, 538 Left = 113, 539 Down = 116, 540 Right = 114, 541 KP0 = 90, 542 KPPoint = 91 543 } 544 545 version(Windows)/// 546 static enum Key 547 { 548 Escape = 0x1B, 549 F1 = 0x70, 550 F2 = 0x71, 551 F3 = 0x72, 552 F4 = 0x73, 553 F5 = 0x74, 554 F6 = 0x75, 555 F7 = 0x76, 556 F8 = 0x77, 557 F9 = 0x78, 558 F10 = 0x79, 559 F11 = 0x7A, 560 F12 = 0x7B, 561 PrintScrn = 0x2A, 562 ScrollLock = 0x91, 563 Pause = 0x13, 564 Backtick = 0xC0, 565 K1 = 0x31, 566 K2 = 0x32, 567 K3 = 0x33, 568 K4 = 0x34, 569 K5 = 0x35, 570 K6 = 0x36, 571 K7 = 0x37, 572 K8 = 0x38, 573 K9 = 0x39, 574 K0 = 0x30, 575 Minus = 0xBD, 576 Equal = 0xBB, 577 Backspace = 0x08, 578 Insert = 0x2D, 579 Home = 0x24, 580 PageUp = 0x21, 581 NumLock = 0x90, 582 KPSlash = 0x6F, 583 KPStar = 0xBB, 584 KPMinus = 0xBD, 585 Tab = 0x09, 586 Q = 0x51, 587 W = 0x57, 588 E = 0x45, 589 R = 0x52, 590 T = 0x54, 591 Y = 0x59, 592 U = 0x55, 593 I = 0x49, 594 O = 0x4F, 595 P = 0x50, 596 597 SqBrackLeft = 0xDB, 598 SqBrackRight = 0xDD, 599 SquareBracketLeft = 0x30, 600 SquareBracketRight = 0xBD, 601 602 Return = 0x0D, 603 Delete = 0x2E, 604 End = 0x23, 605 PageDown = 0x22, 606 607 KP7 = 0x67, 608 KP8 = 0x68, 609 KP9 = 0x69, 610 611 CapsLock = 0x14, 612 A = 0x41, 613 S = 0x53, 614 D = 0x44, 615 F = 0x46, 616 G = 0x47, 617 H = 0x48, 618 J = 0x4A, 619 K = 0x4B, 620 L = 0x4C, 621 Semicolons = 0xBA, 622 Apostrophe = 0xBF, 623 624 KP4 = 0x64, 625 KP5 = 0x65, 626 KP6 = 0x66, 627 628 ShiftLeft = 0xA0, 629 International = 0xA4, 630 631 Z = 0x5A, 632 X = 0x58, 633 C = 0x43, 634 V = 0x56, 635 B = 0x42, 636 N = 0x4E, 637 M = 0x4D, 638 Comma = 0xBC, 639 Point = 0xBE, 640 Slash = 0xBF, 641 642 ShiftRight = 0xA1, 643 644 BackSlash = 0xE2, 645 Up = 0x26, 646 647 KP1 = 0x61, 648 KP2 = 0x62, 649 KP3 = 0x63, 650 651 KPEnter = 0x6A, 652 CtrlLeft = 0xA2, 653 SuperLeft = 0xA4, 654 AltLeft = 0xA4, 655 Space = 0x20, 656 AltRight = 0xA5, 657 SuperRight = 0xA5, 658 Menu = 0, 659 CtrlRight = 0xA3, 660 Left = 0x25, 661 Down = 0x28, 662 Right = 0x27, 663 KP0 = 0x60, 664 KPPoint = 0x6F 665 }