WindowPrivateData.cpp (35025B)
1 /* 2 * DISTRHO Plugin Framework (DPF) 3 * Copyright (C) 2012-2024 Filipe Coelho <falktx@falktx.com> 4 * 5 * Permission to use, copy, modify, and/or distribute this software for any purpose with 6 * or without fee is hereby granted, provided that the above copyright notice and this 7 * permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD 10 * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN 11 * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 12 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 13 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 14 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 */ 16 17 #include "WindowPrivateData.hpp" 18 #include "TopLevelWidgetPrivateData.hpp" 19 20 #include "pugl.hpp" 21 22 // #define DGL_DEBUG_EVENTS 23 24 #if defined(DEBUG) && defined(DGL_DEBUG_EVENTS) 25 # ifdef DISTRHO_PROPER_CPP11_SUPPORT 26 # include <cinttypes> 27 # else 28 # include <inttypes.h> 29 # endif 30 #endif 31 32 #ifdef DISTRHO_OS_WINDOWS 33 # include <windows.h> 34 #endif 35 36 START_NAMESPACE_DGL 37 38 #ifdef DISTRHO_OS_WINDOWS 39 # include "pugl-upstream/src/win.h" 40 #endif 41 42 #ifdef DGL_DEBUG_EVENTS 43 # define DGL_DBG(msg) std::fprintf(stderr, "%s", msg); 44 # define DGL_DBGp(...) std::fprintf(stderr, __VA_ARGS__); 45 # define DGL_DBGF std::fflush(stderr); 46 #else 47 # define DGL_DBG(msg) 48 # define DGL_DBGp(...) 49 # define DGL_DBGF 50 #endif 51 52 #define DEFAULT_WIDTH 640 53 #define DEFAULT_HEIGHT 480 54 55 #define FOR_EACH_TOP_LEVEL_WIDGET(it) \ 56 for (std::list<TopLevelWidget*>::iterator it = topLevelWidgets.begin(); it != topLevelWidgets.end(); ++it) 57 58 #define FOR_EACH_TOP_LEVEL_WIDGET_INV(rit) \ 59 for (std::list<TopLevelWidget*>::reverse_iterator rit = topLevelWidgets.rbegin(); rit != topLevelWidgets.rend(); ++rit) 60 61 // ----------------------------------------------------------------------- 62 63 static double getScaleFactor(const PuglView* const view) 64 { 65 // allow custom scale for testing 66 if (const char* const scale = getenv("DPF_SCALE_FACTOR")) 67 return std::max(1.0, std::atof(scale)); 68 69 if (view != nullptr) 70 return puglGetScaleFactor(view); 71 72 return 1.0; 73 } 74 75 static PuglView* puglNewViewWithTransientParent(PuglWorld* const world, PuglView* const transientParentView) 76 { 77 if (world == nullptr) 78 return nullptr; 79 80 if (PuglView* const view = puglNewView(world)) 81 { 82 puglSetTransientParent(view, puglGetNativeView(transientParentView)); 83 return view; 84 } 85 86 return nullptr; 87 } 88 89 static PuglView* puglNewViewWithParentWindow(PuglWorld* const world, const uintptr_t parentWindowHandle) 90 { 91 if (world == nullptr) 92 return nullptr; 93 94 if (PuglView* const view = puglNewView(world)) 95 { 96 puglSetParentWindow(view, parentWindowHandle); 97 98 if (parentWindowHandle != 0) 99 puglSetPosition(view, 0, 0); 100 101 return view; 102 } 103 104 return nullptr; 105 } 106 107 // ----------------------------------------------------------------------- 108 109 Window::PrivateData::PrivateData(Application& a, Window* const s) 110 : app(a), 111 appData(a.pData), 112 self(s), 113 view(appData->world != nullptr ? puglNewView(appData->world) : nullptr), 114 topLevelWidgets(), 115 isClosed(true), 116 isVisible(false), 117 isEmbed(false), 118 usesScheduledRepaints(false), 119 usesSizeRequest(false), 120 scaleFactor(DGL_NAMESPACE::getScaleFactor(view)), 121 autoScaling(false), 122 autoScaleFactor(1.0), 123 minWidth(0), 124 minHeight(0), 125 keepAspectRatio(false), 126 ignoreIdleCallbacks(false), 127 waitingForClipboardData(false), 128 waitingForClipboardEvents(false), 129 clipboardTypeId(0), 130 filenameToRenderInto(nullptr), 131 #ifndef DGL_FILE_BROWSER_DISABLED 132 fileBrowserHandle(nullptr), 133 #endif 134 modal() 135 { 136 initPre(DEFAULT_WIDTH, DEFAULT_HEIGHT, false); 137 } 138 139 Window::PrivateData::PrivateData(Application& a, Window* const s, PrivateData* const ppData) 140 : app(a), 141 appData(a.pData), 142 self(s), 143 view(puglNewViewWithTransientParent(appData->world, ppData->view)), 144 topLevelWidgets(), 145 isClosed(true), 146 isVisible(false), 147 isEmbed(false), 148 usesScheduledRepaints(false), 149 usesSizeRequest(false), 150 scaleFactor(ppData->scaleFactor), 151 autoScaling(false), 152 autoScaleFactor(1.0), 153 minWidth(0), 154 minHeight(0), 155 keepAspectRatio(false), 156 ignoreIdleCallbacks(false), 157 waitingForClipboardData(false), 158 waitingForClipboardEvents(false), 159 clipboardTypeId(0), 160 filenameToRenderInto(nullptr), 161 #ifndef DGL_FILE_BROWSER_DISABLED 162 fileBrowserHandle(nullptr), 163 #endif 164 modal(ppData) 165 { 166 initPre(DEFAULT_WIDTH, DEFAULT_HEIGHT, false); 167 } 168 169 Window::PrivateData::PrivateData(Application& a, Window* const s, 170 const uintptr_t parentWindowHandle, 171 const double scale, const bool resizable) 172 : app(a), 173 appData(a.pData), 174 self(s), 175 view(puglNewViewWithParentWindow(appData->world, parentWindowHandle)), 176 topLevelWidgets(), 177 isClosed(parentWindowHandle == 0), 178 isVisible(parentWindowHandle != 0), 179 isEmbed(parentWindowHandle != 0), 180 usesScheduledRepaints(false), 181 usesSizeRequest(false), 182 scaleFactor(scale != 0.0 ? scale : DGL_NAMESPACE::getScaleFactor(view)), 183 autoScaling(false), 184 autoScaleFactor(1.0), 185 minWidth(0), 186 minHeight(0), 187 keepAspectRatio(false), 188 ignoreIdleCallbacks(false), 189 waitingForClipboardData(false), 190 waitingForClipboardEvents(false), 191 clipboardTypeId(0), 192 filenameToRenderInto(nullptr), 193 #ifndef DGL_FILE_BROWSER_DISABLED 194 fileBrowserHandle(nullptr), 195 #endif 196 modal() 197 { 198 initPre(DEFAULT_WIDTH, DEFAULT_HEIGHT, resizable); 199 } 200 201 Window::PrivateData::PrivateData(Application& a, Window* const s, 202 const uintptr_t parentWindowHandle, 203 const uint width, const uint height, 204 const double scale, const bool resizable, 205 const bool _usesScheduledRepaints, 206 const bool _usesSizeRequest) 207 : app(a), 208 appData(a.pData), 209 self(s), 210 view(puglNewViewWithParentWindow(appData->world, parentWindowHandle)), 211 topLevelWidgets(), 212 isClosed(parentWindowHandle == 0), 213 isVisible(parentWindowHandle != 0 && view != nullptr), 214 isEmbed(parentWindowHandle != 0), 215 usesScheduledRepaints(_usesScheduledRepaints), 216 usesSizeRequest(_usesSizeRequest), 217 scaleFactor(scale != 0.0 ? scale : DGL_NAMESPACE::getScaleFactor(view)), 218 autoScaling(false), 219 autoScaleFactor(1.0), 220 minWidth(0), 221 minHeight(0), 222 keepAspectRatio(false), 223 ignoreIdleCallbacks(false), 224 waitingForClipboardData(false), 225 waitingForClipboardEvents(false), 226 clipboardTypeId(0), 227 filenameToRenderInto(nullptr), 228 #ifndef DGL_FILE_BROWSER_DISABLED 229 fileBrowserHandle(nullptr), 230 #endif 231 modal() 232 { 233 initPre(width != 0 ? width : DEFAULT_WIDTH, height != 0 ? height : DEFAULT_HEIGHT, resizable); 234 } 235 236 Window::PrivateData::~PrivateData() 237 { 238 appData->idleCallbacks.remove(this); 239 appData->windows.remove(self); 240 std::free(filenameToRenderInto); 241 242 if (view == nullptr) 243 return; 244 245 if (isEmbed) 246 { 247 #ifndef DGL_FILE_BROWSER_DISABLED 248 if (fileBrowserHandle != nullptr) 249 fileBrowserClose(fileBrowserHandle); 250 #endif 251 puglHide(view); 252 appData->oneWindowClosed(); 253 isClosed = true; 254 isVisible = false; 255 } 256 257 puglFreeView(view); 258 } 259 260 // ----------------------------------------------------------------------- 261 262 void Window::PrivateData::initPre(const uint width, const uint height, const bool resizable) 263 { 264 appData->windows.push_back(self); 265 appData->idleCallbacks.push_back(this); 266 memset(graphicsContext, 0, sizeof(graphicsContext)); 267 268 if (view == nullptr) 269 { 270 d_stderr2("Failed to create Pugl view, everything will fail!"); 271 return; 272 } 273 274 puglSetMatchingBackendForCurrentBuild(view); 275 puglSetHandle(view, this); 276 277 puglSetViewHint(view, PUGL_RESIZABLE, resizable ? PUGL_TRUE : PUGL_FALSE); 278 puglSetViewHint(view, PUGL_IGNORE_KEY_REPEAT, PUGL_FALSE); 279 #if defined(DGL_USE_RGBA) && DGL_USE_RGBA 280 puglSetViewHint(view, PUGL_DEPTH_BITS, 24); 281 #else 282 puglSetViewHint(view, PUGL_DEPTH_BITS, 16); 283 #endif 284 puglSetViewHint(view, PUGL_STENCIL_BITS, 8); 285 286 // PUGL_SAMPLES ?? 287 puglSetEventFunc(view, puglEventCallback); 288 289 // setting default size triggers system-level calls, do it last 290 puglSetSizeHint(view, PUGL_DEFAULT_SIZE, static_cast<PuglSpan>(width), static_cast<PuglSpan>(height)); 291 } 292 293 bool Window::PrivateData::initPost() 294 { 295 if (view == nullptr) 296 return false; 297 298 // create view now, as a few methods we allow devs to use require it 299 if (puglRealize(view) != PUGL_SUCCESS) 300 { 301 view = nullptr; 302 d_stderr2("Failed to realize Pugl view, everything will fail!"); 303 return false; 304 } 305 306 if (isEmbed) 307 { 308 appData->oneWindowShown(); 309 puglShow(view, PUGL_SHOW_PASSIVE); 310 } 311 312 return true; 313 } 314 315 // ----------------------------------------------------------------------- 316 317 void Window::PrivateData::close() 318 { 319 DGL_DBG("Window close\n"); 320 // DGL_DBGp("Window close DBG %i %i %p\n", isEmbed, isClosed, appData); 321 322 if (isEmbed || isClosed) 323 return; 324 325 isClosed = true; 326 hide(); 327 appData->oneWindowClosed(); 328 } 329 330 // ----------------------------------------------------------------------- 331 332 void Window::PrivateData::show() 333 { 334 if (isVisible) 335 { 336 DGL_DBG("Window show matches current visible state, ignoring request\n"); 337 return; 338 } 339 if (isEmbed) 340 { 341 DGL_DBG("Window show cannot be called when embedded\n"); 342 return; 343 } 344 345 DGL_DBG("Window show called\n"); 346 347 if (view == nullptr) 348 return; 349 350 if (isClosed) 351 { 352 isClosed = false; 353 appData->oneWindowShown(); 354 355 // FIXME 356 // PuglRect rect = puglGetFrame(view); 357 // puglSetWindowSize(view, static_cast<uint>(rect.width), static_cast<uint>(rect.height)); 358 359 #if defined(DISTRHO_OS_WINDOWS) 360 puglWin32ShowCentered(view); 361 #elif defined(DISTRHO_OS_MAC) 362 puglMacOSShowCentered(view); 363 #else 364 puglShow(view, PUGL_SHOW_RAISE); 365 #endif 366 } 367 else 368 { 369 #ifdef DISTRHO_OS_WINDOWS 370 puglWin32RestoreWindow(view); 371 #else 372 puglShow(view, PUGL_SHOW_RAISE); 373 #endif 374 } 375 376 isVisible = true; 377 } 378 379 void Window::PrivateData::hide() 380 { 381 if (isEmbed) 382 { 383 DGL_DBG("Window hide cannot be called when embedded\n"); 384 return; 385 } 386 if (! isVisible) 387 { 388 DGL_DBG("Window hide matches current visible state, ignoring request\n"); 389 return; 390 } 391 392 DGL_DBG("Window hide called\n"); 393 394 if (modal.enabled) 395 stopModal(); 396 397 #ifndef DGL_FILE_BROWSER_DISABLED 398 if (fileBrowserHandle != nullptr) 399 { 400 fileBrowserClose(fileBrowserHandle); 401 fileBrowserHandle = nullptr; 402 } 403 #endif 404 405 puglHide(view); 406 407 isVisible = false; 408 } 409 410 // ----------------------------------------------------------------------- 411 412 void Window::PrivateData::focus() 413 { 414 if (view == nullptr) 415 return; 416 417 if (! isEmbed) 418 puglRaiseWindow(view); 419 420 puglGrabFocus(view); 421 } 422 423 // ----------------------------------------------------------------------- 424 425 void Window::PrivateData::setResizable(const bool resizable) 426 { 427 DISTRHO_SAFE_ASSERT_RETURN(! isEmbed,); 428 429 DGL_DBG("Window setResizable called\n"); 430 431 puglSetResizable(view, resizable); 432 } 433 434 // ----------------------------------------------------------------------- 435 436 void Window::PrivateData::idleCallback() 437 { 438 #ifndef DGL_FILE_BROWSER_DISABLED 439 if (fileBrowserHandle != nullptr && fileBrowserIdle(fileBrowserHandle)) 440 { 441 self->onFileSelected(fileBrowserGetPath(fileBrowserHandle)); 442 fileBrowserClose(fileBrowserHandle); 443 fileBrowserHandle = nullptr; 444 } 445 #endif 446 } 447 448 // ----------------------------------------------------------------------- 449 // idle callback stuff 450 451 bool Window::PrivateData::addIdleCallback(IdleCallback* const callback, const uint timerFrequencyInMs) 452 { 453 if (ignoreIdleCallbacks || view == nullptr) 454 return false; 455 456 if (timerFrequencyInMs == 0) 457 { 458 appData->idleCallbacks.push_back(callback); 459 return true; 460 } 461 462 return puglStartTimer(view, (uintptr_t)callback, static_cast<double>(timerFrequencyInMs) / 1000.0) == PUGL_SUCCESS; 463 } 464 465 bool Window::PrivateData::removeIdleCallback(IdleCallback* const callback) 466 { 467 if (ignoreIdleCallbacks || view == nullptr) 468 return false; 469 470 if (std::find(appData->idleCallbacks.begin(), 471 appData->idleCallbacks.end(), callback) != appData->idleCallbacks.end()) 472 { 473 appData->idleCallbacks.remove(callback); 474 return true; 475 } 476 477 return puglStopTimer(view, (uintptr_t)callback) == PUGL_SUCCESS; 478 } 479 480 #ifndef DGL_FILE_BROWSER_DISABLED 481 // ----------------------------------------------------------------------- 482 // file handling 483 484 bool Window::PrivateData::openFileBrowser(const FileBrowserOptions& options) 485 { 486 if (fileBrowserHandle != nullptr) 487 fileBrowserClose(fileBrowserHandle); 488 489 FileBrowserOptions options2 = options; 490 491 if (options2.title == nullptr) 492 options2.title = puglGetViewString(view, PUGL_WINDOW_TITLE); 493 494 fileBrowserHandle = fileBrowserCreate(isEmbed, 495 puglGetNativeView(view), 496 autoScaling ? autoScaleFactor : scaleFactor, 497 options2); 498 499 return fileBrowserHandle != nullptr; 500 } 501 #endif // ! DGL_FILE_BROWSER_DISABLED 502 503 // ----------------------------------------------------------------------- 504 // modal handling 505 506 void Window::PrivateData::startModal() 507 { 508 DGL_DBG("Window modal loop starting..."); DGL_DBGF; 509 DISTRHO_SAFE_ASSERT_RETURN(modal.parent != nullptr, show()); 510 511 // activate modal mode for this window 512 modal.enabled = true; 513 514 // make parent give focus to us 515 modal.parent->modal.child = this; 516 517 // make sure both parent and ourselves are visible 518 modal.parent->show(); 519 show(); 520 521 #ifdef DISTRHO_OS_MAC 522 puglMacOSAddChildWindow(modal.parent->view, view); 523 #endif 524 525 DGL_DBG("Ok\n"); 526 } 527 528 void Window::PrivateData::stopModal() 529 { 530 DGL_DBG("Window modal loop stopping..."); DGL_DBGF; 531 532 // deactivate modal mode 533 modal.enabled = false; 534 535 // safety checks, make sure we have a parent and we are currently active as the child to give focus to 536 if (modal.parent == nullptr) 537 return; 538 if (modal.parent->modal.child != this) 539 return; 540 541 #ifdef DISTRHO_OS_MAC 542 puglMacOSRemoveChildWindow(modal.parent->view, view); 543 #endif 544 545 // stop parent from giving focus to us, so it behaves like normal 546 modal.parent->modal.child = nullptr; 547 548 // refocus main window after closing child 549 if (! modal.parent->isClosed) 550 { 551 const Widget::MotionEvent ev; 552 modal.parent->onPuglMotion(ev); 553 modal.parent->focus(); 554 } 555 556 DGL_DBG("Ok\n"); 557 } 558 559 void Window::PrivateData::runAsModal(const bool blockWait) 560 { 561 DGL_DBGp("Window::PrivateData::runAsModal %i\n", blockWait); 562 startModal(); 563 564 if (blockWait) 565 { 566 DISTRHO_SAFE_ASSERT_RETURN(appData->isStandalone,); 567 568 while (isVisible && modal.enabled) 569 appData->idle(10); 570 571 stopModal(); 572 } 573 else 574 { 575 appData->idle(0); 576 } 577 } 578 579 // ----------------------------------------------------------------------- 580 // pugl events 581 582 void Window::PrivateData::onPuglConfigure(const double width, const double height) 583 { 584 DISTRHO_SAFE_ASSERT_INT2_RETURN(width > 1 && height > 1, width, height,); 585 586 DGL_DBGp("PUGL: onReshape : %f %f\n", width, height); 587 588 if (autoScaling) 589 { 590 const double scaleHorizontal = width / static_cast<double>(minWidth); 591 const double scaleVertical = height / static_cast<double>(minHeight); 592 autoScaleFactor = scaleHorizontal < scaleVertical ? scaleHorizontal : scaleVertical; 593 } 594 else 595 { 596 autoScaleFactor = 1.0; 597 } 598 599 const uint uwidth = static_cast<uint>(width / autoScaleFactor + 0.5); 600 const uint uheight = static_cast<uint>(height / autoScaleFactor + 0.5); 601 602 self->onReshape(uwidth, uheight); 603 604 #ifndef DPF_TEST_WINDOW_CPP 605 FOR_EACH_TOP_LEVEL_WIDGET(it) 606 { 607 TopLevelWidget* const widget = *it; 608 609 /* Some special care here, we call Widget::setSize instead of the TopLevelWidget one. 610 * This is because we want TopLevelWidget::setSize to handle both window and widget size, 611 * but we dont want to change window size here, because we are the window.. 612 * So we just call the Widget specific method manually. 613 * 614 * Alternatively, we could expose a resize function on the pData, like done with the display function. 615 * But there is nothing extra we need to do in there, so this works fine. 616 */ 617 ((Widget*)widget)->setSize(uwidth, uheight); 618 } 619 #endif 620 621 // always repaint after a resize 622 puglPostRedisplay(view); 623 } 624 625 void Window::PrivateData::onPuglExpose() 626 { 627 // DGL_DBG("PUGL: onPuglExpose\n"); 628 629 puglOnDisplayPrepare(view); 630 631 #ifndef DPF_TEST_WINDOW_CPP 632 FOR_EACH_TOP_LEVEL_WIDGET(it) 633 { 634 TopLevelWidget* const widget(*it); 635 636 if (widget->isVisible()) 637 widget->pData->display(); 638 } 639 640 if (char* const filename = filenameToRenderInto) 641 { 642 const PuglRect rect = puglGetFrame(view); 643 filenameToRenderInto = nullptr; 644 renderToPicture(filename, getGraphicsContext(), static_cast<uint>(rect.width), static_cast<uint>(rect.height)); 645 std::free(filename); 646 } 647 #endif 648 } 649 650 void Window::PrivateData::onPuglClose() 651 { 652 DGL_DBG("PUGL: onClose\n"); 653 654 #ifndef DISTRHO_OS_MAC 655 // if we are running as standalone we can prevent closing in certain conditions 656 if (appData->isStandalone) 657 { 658 // a child window is active, gives focus to it 659 if (modal.child != nullptr) 660 return modal.child->focus(); 661 662 // ask window if we should close 663 if (! self->onClose()) 664 return; 665 } 666 #endif 667 668 if (modal.enabled) 669 stopModal(); 670 671 if (modal.child != nullptr) 672 { 673 modal.child->close(); 674 modal.child = nullptr; 675 } 676 677 close(); 678 } 679 680 void Window::PrivateData::onPuglFocus(const bool focus, const CrossingMode mode) 681 { 682 DGL_DBGp("onPuglFocus : %i %i | %i\n", focus, mode, isClosed); 683 684 if (isClosed) 685 return; 686 687 if (modal.child != nullptr) 688 return modal.child->focus(); 689 690 self->onFocus(focus, mode); 691 } 692 693 void Window::PrivateData::onPuglKey(const Widget::KeyboardEvent& ev) 694 { 695 DGL_DBGp("onPuglKey : %i %u %u\n", ev.press, ev.key, ev.keycode); 696 697 if (modal.child != nullptr) 698 return modal.child->focus(); 699 700 #ifndef DPF_TEST_WINDOW_CPP 701 FOR_EACH_TOP_LEVEL_WIDGET_INV(rit) 702 { 703 TopLevelWidget* const widget(*rit); 704 705 if (widget->isVisible() && widget->onKeyboard(ev)) 706 break; 707 } 708 #endif 709 } 710 711 void Window::PrivateData::onPuglText(const Widget::CharacterInputEvent& ev) 712 { 713 DGL_DBGp("onPuglText : %u %u %s\n", ev.keycode, ev.character, ev.string); 714 715 if (modal.child != nullptr) 716 return modal.child->focus(); 717 718 #ifndef DPF_TEST_WINDOW_CPP 719 FOR_EACH_TOP_LEVEL_WIDGET_INV(rit) 720 { 721 TopLevelWidget* const widget(*rit); 722 723 if (widget->isVisible() && widget->onCharacterInput(ev)) 724 break; 725 } 726 #endif 727 } 728 729 void Window::PrivateData::onPuglMouse(const Widget::MouseEvent& ev) 730 { 731 DGL_DBGp("onPuglMouse : %i %i %f %f\n", ev.button, ev.press, ev.pos.getX(), ev.pos.getY()); 732 733 if (modal.child != nullptr) 734 return modal.child->focus(); 735 736 #ifndef DPF_TEST_WINDOW_CPP 737 FOR_EACH_TOP_LEVEL_WIDGET_INV(rit) 738 { 739 TopLevelWidget* const widget(*rit); 740 741 if (widget->isVisible() && widget->onMouse(ev)) 742 break; 743 } 744 #endif 745 } 746 747 void Window::PrivateData::onPuglMotion(const Widget::MotionEvent& ev) 748 { 749 DGL_DBGp("onPuglMotion : %f %f\n", ev.pos.getX(), ev.pos.getY()); 750 751 if (modal.child != nullptr) 752 return modal.child->focus(); 753 754 #ifndef DPF_TEST_WINDOW_CPP 755 FOR_EACH_TOP_LEVEL_WIDGET_INV(rit) 756 { 757 TopLevelWidget* const widget(*rit); 758 759 if (widget->isVisible() && widget->onMotion(ev)) 760 break; 761 } 762 #endif 763 } 764 765 void Window::PrivateData::onPuglScroll(const Widget::ScrollEvent& ev) 766 { 767 DGL_DBGp("onPuglScroll : %f %f %f %f\n", ev.pos.getX(), ev.pos.getY(), ev.delta.getX(), ev.delta.getY()); 768 769 if (modal.child != nullptr) 770 return modal.child->focus(); 771 772 #ifndef DPF_TEST_WINDOW_CPP 773 FOR_EACH_TOP_LEVEL_WIDGET_INV(rit) 774 { 775 TopLevelWidget* const widget(*rit); 776 777 if (widget->isVisible() && widget->onScroll(ev)) 778 break; 779 } 780 #endif 781 } 782 783 const void* Window::PrivateData::getClipboard(size_t& dataSize) 784 { 785 clipboardTypeId = 0; 786 waitingForClipboardData = true, 787 waitingForClipboardEvents = true; 788 789 // begin clipboard dance here 790 if (puglPaste(view) != PUGL_SUCCESS) 791 { 792 dataSize = 0; 793 waitingForClipboardEvents = false; 794 return nullptr; 795 } 796 797 #ifdef DGL_USING_X11 798 // wait for type request, clipboardTypeId must be != 0 to be valid 799 int retry = static_cast<int>(2 / 0.03); 800 while (clipboardTypeId == 0 && waitingForClipboardData && --retry >= 0) 801 { 802 if (puglX11UpdateWithoutExposures(appData->world) != PUGL_SUCCESS) 803 break; 804 } 805 #endif 806 807 if (clipboardTypeId == 0) 808 { 809 dataSize = 0; 810 waitingForClipboardEvents = false; 811 return nullptr; 812 } 813 814 #ifdef DGL_USING_X11 815 // wait for actual data (assumes offer was accepted) 816 retry = static_cast<int>(2 / 0.03); 817 while (waitingForClipboardData && --retry >= 0) 818 { 819 if (puglX11UpdateWithoutExposures(appData->world) != PUGL_SUCCESS) 820 break; 821 } 822 #endif 823 824 if (clipboardTypeId == 0) 825 { 826 dataSize = 0; 827 waitingForClipboardEvents = false; 828 return nullptr; 829 } 830 831 waitingForClipboardEvents = false; 832 return puglGetClipboard(view, clipboardTypeId - 1, &dataSize); 833 } 834 835 uint32_t Window::PrivateData::onClipboardDataOffer() 836 { 837 DGL_DBG("onClipboardDataOffer\n"); 838 839 if ((clipboardTypeId = self->onClipboardDataOffer()) != 0) 840 return clipboardTypeId; 841 842 // stop waiting for data, it was rejected 843 waitingForClipboardData = false; 844 return 0; 845 } 846 847 void Window::PrivateData::onClipboardData(const uint32_t typeId) 848 { 849 if (clipboardTypeId != typeId) 850 clipboardTypeId = 0; 851 852 waitingForClipboardData = false; 853 } 854 855 #if defined(DEBUG) && defined(DGL_DEBUG_EVENTS) 856 static int printEvent(const PuglEvent* event, const char* prefix, const bool verbose); 857 #endif 858 859 PuglStatus Window::PrivateData::puglEventCallback(PuglView* const view, const PuglEvent* const event) 860 { 861 Window::PrivateData* const pData = (Window::PrivateData*)puglGetHandle(view); 862 #if defined(DEBUG) && defined(DGL_DEBUG_EVENTS) 863 if (event->type != PUGL_TIMER) { 864 printEvent(event, "pugl event: ", true); 865 } 866 #endif 867 868 if (pData->waitingForClipboardEvents) 869 { 870 switch (event->type) 871 { 872 case PUGL_UPDATE: 873 case PUGL_EXPOSE: 874 case PUGL_FOCUS_IN: 875 case PUGL_FOCUS_OUT: 876 case PUGL_KEY_PRESS: 877 case PUGL_KEY_RELEASE: 878 case PUGL_TEXT: 879 case PUGL_POINTER_IN: 880 case PUGL_POINTER_OUT: 881 case PUGL_BUTTON_PRESS: 882 case PUGL_BUTTON_RELEASE: 883 case PUGL_MOTION: 884 case PUGL_SCROLL: 885 case PUGL_TIMER: 886 case PUGL_LOOP_ENTER: 887 case PUGL_LOOP_LEAVE: 888 return PUGL_SUCCESS; 889 case PUGL_DATA_OFFER: 890 case PUGL_DATA: 891 break; 892 default: 893 d_stdout("Got event %d while waitingForClipboardEvents", event->type); 894 break; 895 } 896 } 897 898 switch (event->type) 899 { 900 ///< No event 901 case PUGL_NOTHING: 902 break; 903 904 ///< View realized, a #PuglRealizeEvent 905 case PUGL_REALIZE: 906 if (! pData->isEmbed && ! puglGetTransientParent(view)) 907 { 908 #if defined(DISTRHO_OS_WINDOWS) && defined(DGL_WINDOWS_ICON_ID) 909 WNDCLASSEX wClass = {}; 910 const HINSTANCE hInstance = GetModuleHandle(nullptr); 911 912 if (GetClassInfoEx(hInstance, view->world->strings[PUGL_CLASS_NAME], &wClass)) 913 wClass.hIcon = LoadIcon(nullptr, MAKEINTRESOURCE(DGL_WINDOWS_ICON_ID)); 914 915 SetClassLongPtr(view->impl->hwnd, GCLP_HICON, (LONG_PTR) LoadIcon(hInstance, MAKEINTRESOURCE(DGL_WINDOWS_ICON_ID))); 916 #endif 917 #ifdef DGL_USING_X11 918 puglX11SetWindowTypeAndPID(view, pData->appData->isStandalone); 919 #endif 920 } 921 break; 922 923 ///< View unrealizeed, a #PuglUnrealizeEvent 924 case PUGL_UNREALIZE: 925 break; 926 927 ///< View configured, a #PuglConfigureEvent 928 case PUGL_CONFIGURE: 929 // unused x, y (double) 930 pData->onPuglConfigure(event->configure.width, event->configure.height); 931 break; 932 933 ///< View ready to draw, a #PuglUpdateEvent 934 case PUGL_UPDATE: 935 break; 936 937 ///< View must be drawn, a #PuglExposeEvent 938 case PUGL_EXPOSE: 939 // unused x, y, width, height (double) 940 pData->onPuglExpose(); 941 break; 942 943 ///< View will be closed, a #PuglCloseEvent 944 case PUGL_CLOSE: 945 pData->onPuglClose(); 946 break; 947 948 ///< Keyboard focus entered view, a #PuglFocusEvent 949 case PUGL_FOCUS_IN: 950 ///< Keyboard focus left view, a #PuglFocusEvent 951 case PUGL_FOCUS_OUT: 952 pData->onPuglFocus(event->type == PUGL_FOCUS_IN, 953 static_cast<CrossingMode>(event->focus.mode)); 954 break; 955 956 ///< Key pressed, a #PuglKeyEvent 957 case PUGL_KEY_PRESS: 958 ///< Key released, a #PuglKeyEvent 959 case PUGL_KEY_RELEASE: 960 { 961 // unused x, y, xRoot, yRoot (double) 962 Widget::KeyboardEvent ev; 963 ev.mod = event->key.state; 964 ev.flags = event->key.flags; 965 ev.time = static_cast<uint>(event->key.time * 1000.0 + 0.5); 966 ev.press = event->type == PUGL_KEY_PRESS; 967 ev.key = event->key.key; 968 ev.keycode = event->key.keycode; 969 970 // keyboard events must always be lowercase 971 if (ev.key >= 'A' && ev.key <= 'Z') 972 { 973 ev.key += 'a' - 'A'; // A-Z -> a-z 974 ev.mod |= kModifierShift; 975 } 976 977 pData->onPuglKey(ev); 978 break; 979 } 980 981 ///< Character entered, a #PuglTextEvent 982 case PUGL_TEXT: 983 { 984 // unused x, y, xRoot, yRoot (double) 985 Widget::CharacterInputEvent ev; 986 ev.mod = event->text.state; 987 ev.flags = event->text.flags; 988 ev.time = static_cast<uint>(event->text.time * 1000.0 + 0.5); 989 ev.keycode = event->text.keycode; 990 ev.character = event->text.character; 991 std::strncpy(ev.string, event->text.string, sizeof(ev.string)); 992 pData->onPuglText(ev); 993 break; 994 } 995 996 ///< Pointer entered view, a #PuglCrossingEvent 997 case PUGL_POINTER_IN: 998 break; 999 ///< Pointer left view, a #PuglCrossingEvent 1000 case PUGL_POINTER_OUT: 1001 break; 1002 1003 ///< Mouse button pressed, a #PuglButtonEvent 1004 case PUGL_BUTTON_PRESS: 1005 ///< Mouse button released, a #PuglButtonEvent 1006 case PUGL_BUTTON_RELEASE: 1007 { 1008 Widget::MouseEvent ev; 1009 ev.mod = event->button.state; 1010 ev.flags = event->button.flags; 1011 ev.time = static_cast<uint>(event->button.time * 1000.0 + 0.5); 1012 ev.button = event->button.button + 1; 1013 ev.press = event->type == PUGL_BUTTON_PRESS; 1014 if (pData->autoScaling && 0) 1015 { 1016 const double scaleFactor = pData->autoScaleFactor; 1017 ev.pos = Point<double>(event->button.x / scaleFactor, event->button.y / scaleFactor); 1018 } 1019 else 1020 { 1021 ev.pos = Point<double>(event->button.x, event->button.y); 1022 } 1023 ev.absolutePos = ev.pos; 1024 pData->onPuglMouse(ev); 1025 break; 1026 } 1027 1028 ///< Pointer moved, a #PuglMotionEvent 1029 case PUGL_MOTION: 1030 { 1031 Widget::MotionEvent ev; 1032 ev.mod = event->motion.state; 1033 ev.flags = event->motion.flags; 1034 ev.time = static_cast<uint>(event->motion.time * 1000.0 + 0.5); 1035 if (pData->autoScaling && 0) 1036 { 1037 const double scaleFactor = pData->autoScaleFactor; 1038 ev.pos = Point<double>(event->motion.x / scaleFactor, event->motion.y / scaleFactor); 1039 } 1040 else 1041 { 1042 ev.pos = Point<double>(event->motion.x, event->motion.y); 1043 } 1044 ev.absolutePos = ev.pos; 1045 pData->onPuglMotion(ev); 1046 break; 1047 } 1048 1049 ///< Scrolled, a #PuglScrollEvent 1050 case PUGL_SCROLL: 1051 { 1052 Widget::ScrollEvent ev; 1053 ev.mod = event->scroll.state; 1054 ev.flags = event->scroll.flags; 1055 ev.time = static_cast<uint>(event->scroll.time * 1000.0 + 0.5); 1056 if (pData->autoScaling && 0) 1057 { 1058 const double scaleFactor = pData->autoScaleFactor; 1059 ev.pos = Point<double>(event->scroll.x / scaleFactor, event->scroll.y / scaleFactor); 1060 ev.delta = Point<double>(event->scroll.dx / scaleFactor, event->scroll.dy / scaleFactor); 1061 } 1062 else 1063 { 1064 ev.pos = Point<double>(event->scroll.x, event->scroll.y); 1065 ev.delta = Point<double>(event->scroll.dx, event->scroll.dy); 1066 } 1067 ev.direction = static_cast<ScrollDirection>(event->scroll.direction); 1068 ev.absolutePos = ev.pos; 1069 pData->onPuglScroll(ev); 1070 break; 1071 } 1072 1073 ///< Custom client message, a #PuglClientEvent 1074 case PUGL_CLIENT: 1075 break; 1076 1077 ///< Timer triggered, a #PuglTimerEvent 1078 case PUGL_TIMER: 1079 if (IdleCallback* const idleCallback = reinterpret_cast<IdleCallback*>(event->timer.id)) 1080 idleCallback->idleCallback(); 1081 break; 1082 1083 ///< Recursive loop left, a #PuglLoopLeaveEvent 1084 case PUGL_LOOP_ENTER: 1085 break; 1086 1087 ///< Recursive loop left, a #PuglEventLoopLeave 1088 case PUGL_LOOP_LEAVE: 1089 break; 1090 1091 ///< Data offered from clipboard, a #PuglDataOfferEvent 1092 case PUGL_DATA_OFFER: 1093 if (const uint32_t offerTypeId = pData->onClipboardDataOffer()) 1094 puglAcceptOffer(view, &event->offer, offerTypeId - 1); 1095 break; 1096 1097 ///< Data available from clipboard, a #PuglDataEvent 1098 case PUGL_DATA: 1099 pData->onClipboardData(event->data.typeIndex + 1); 1100 break; 1101 } 1102 1103 return PUGL_SUCCESS; 1104 } 1105 1106 // ----------------------------------------------------------------------- 1107 1108 #if defined(DEBUG) && defined(DGL_DEBUG_EVENTS) 1109 static int printModifiers(const uint32_t mods) 1110 { 1111 return fprintf(stderr, "Modifiers:%s%s%s%s\n", 1112 (mods & PUGL_MOD_SHIFT) ? " Shift" : "", 1113 (mods & PUGL_MOD_CTRL) ? " Ctrl" : "", 1114 (mods & PUGL_MOD_ALT) ? " Alt" : "", 1115 (mods & PUGL_MOD_SUPER) ? " Super" : ""); 1116 } 1117 1118 static int printEvent(const PuglEvent* event, const char* prefix, const bool verbose) 1119 { 1120 #define FFMT "%6.1f" 1121 #define PFMT FFMT " " FFMT 1122 #define PRINT(fmt, ...) fprintf(stderr, fmt, __VA_ARGS__) 1123 1124 switch (event->type) { 1125 case PUGL_NOTHING: 1126 return 0; 1127 case PUGL_KEY_PRESS: 1128 return PRINT("%sKey press code %3u key U+%04X\n", 1129 prefix, 1130 event->key.keycode, 1131 event->key.key); 1132 case PUGL_KEY_RELEASE: 1133 return PRINT("%sKey release code %3u key U+%04X\n", 1134 prefix, 1135 event->key.keycode, 1136 event->key.key); 1137 case PUGL_TEXT: 1138 return PRINT("%sText entry code %3u char U+%04X (%s)\n", 1139 prefix, 1140 event->text.keycode, 1141 event->text.character, 1142 event->text.string); 1143 case PUGL_BUTTON_PRESS: 1144 case PUGL_BUTTON_RELEASE: 1145 return (PRINT("%sMouse %u %s at " PFMT " ", 1146 prefix, 1147 event->button.button, 1148 (event->type == PUGL_BUTTON_PRESS) ? "down" : "up ", 1149 event->button.x, 1150 event->button.y) + 1151 printModifiers(event->scroll.state)); 1152 case PUGL_SCROLL: 1153 return (PRINT("%sScroll %5.1f %5.1f at " PFMT " ", 1154 prefix, 1155 event->scroll.dx, 1156 event->scroll.dy, 1157 event->scroll.x, 1158 event->scroll.y) + 1159 printModifiers(event->scroll.state)); 1160 case PUGL_POINTER_IN: 1161 return PRINT("%sMouse enter at " PFMT "\n", 1162 prefix, 1163 event->crossing.x, 1164 event->crossing.y); 1165 case PUGL_POINTER_OUT: 1166 return PRINT("%sMouse leave at " PFMT "\n", 1167 prefix, 1168 event->crossing.x, 1169 event->crossing.y); 1170 case PUGL_FOCUS_IN: 1171 return PRINT("%sFocus in %i\n", 1172 prefix, 1173 event->focus.mode); 1174 case PUGL_FOCUS_OUT: 1175 return PRINT("%sFocus out %i\n", 1176 prefix, 1177 event->focus.mode); 1178 case PUGL_CLIENT: 1179 return PRINT("%sClient %" PRIXPTR " %" PRIXPTR "\n", 1180 prefix, 1181 event->client.data1, 1182 event->client.data2); 1183 case PUGL_TIMER: 1184 return PRINT("%sTimer %" PRIuPTR "\n", prefix, event->timer.id); 1185 default: 1186 break; 1187 } 1188 1189 if (verbose) { 1190 switch (event->type) { 1191 case PUGL_CREATE: 1192 return fprintf(stderr, "%sCreate\n", prefix); 1193 case PUGL_DESTROY: 1194 return fprintf(stderr, "%sDestroy\n", prefix); 1195 case PUGL_MAP: 1196 return fprintf(stderr, "%sMap\n", prefix); 1197 case PUGL_UNMAP: 1198 return fprintf(stderr, "%sUnmap\n", prefix); 1199 case PUGL_UPDATE: 1200 return 0; // fprintf(stderr, "%sUpdate\n", prefix); 1201 case PUGL_CONFIGURE: 1202 return PRINT("%sConfigure " PFMT " " PFMT "\n", 1203 prefix, 1204 event->configure.x, 1205 event->configure.y, 1206 event->configure.width, 1207 event->configure.height); 1208 case PUGL_EXPOSE: 1209 return PRINT("%sExpose " PFMT " " PFMT "\n", 1210 prefix, 1211 event->expose.x, 1212 event->expose.y, 1213 event->expose.width, 1214 event->expose.height); 1215 case PUGL_CLOSE: 1216 return PRINT("%sClose\n", prefix); 1217 case PUGL_MOTION: 1218 return PRINT("%sMouse motion at " PFMT "\n", 1219 prefix, 1220 event->motion.x, 1221 event->motion.y); 1222 default: 1223 return PRINT("%sUnknown event type %d\n", prefix, (int)event->type); 1224 } 1225 } 1226 1227 #undef PRINT 1228 #undef PFMT 1229 #undef FFMT 1230 1231 return 0; 1232 } 1233 #endif 1234 1235 #undef DGL_DBG 1236 #undef DGL_DBGF 1237 1238 // ----------------------------------------------------------------------- 1239 1240 END_NAMESPACE_DGL