1 /* 2 * DISTRHO Plugin Framework (DPF) 3 * Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com> 4 * Copyright (C) 2019 Jean Pierre Cimalando <jp-dev@inbox.ru> 5 * Copyright (C) 2019 Robin Gareus <robin@gareus.org> 6 * 7 * Permission to use, copy, modify, and/or distribute this software for any purpose with 8 * or without fee is hereby granted, provided that the above copyright notice and this 9 * permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD 12 * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN 13 * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 14 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER 15 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 16 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 // we need this for now 20 //#define PUGL_GRAB_FOCUS 1 21 22 #include "../Base.hpp" 23 24 #ifdef DGL_CAIRO 25 # define PUGL_CAIRO 26 # include "../Cairo.hpp" 27 #endif 28 #ifdef DGL_OPENGL 29 # define PUGL_OPENGL 30 # include "../OpenGL.hpp" 31 #endif 32 33 #include "pugl/pugl.h" 34 35 #if defined(__GNUC__) && (__GNUC__ >= 7) 36 # pragma GCC diagnostic push 37 # pragma GCC diagnostic ignored "-Wimplicit-fallthrough" 38 #endif 39 40 #if defined(DISTRHO_OS_HAIKU) 41 # define DGL_DEBUG_EVENTS 42 # include "pugl/pugl_haiku.cpp" 43 #elif defined(DISTRHO_OS_MAC) 44 # define PuglWindow DISTRHO_JOIN_MACRO(PuglWindow, DGL_NAMESPACE) 45 # define PuglOpenGLView DISTRHO_JOIN_MACRO(PuglOpenGLView, DGL_NAMESPACE) 46 # include "pugl/pugl_osx.m" 47 #elif defined(DISTRHO_OS_WINDOWS) 48 # include "pugl/pugl_win.cpp" 49 # undef max 50 # undef min 51 #else 52 # include <sys/types.h> 53 # include <unistd.h> 54 extern "C" { 55 # include "pugl/pugl_x11.c" 56 } 57 #endif 58 59 #if defined(__GNUC__) && (__GNUC__ >= 7) 60 # pragma GCC diagnostic pop 61 #endif 62 63 #include "ApplicationPrivateData.hpp" 64 #include "WidgetPrivateData.hpp" 65 #include "../StandaloneWindow.hpp" 66 #include "../../distrho/extra/String.hpp" 67 68 #define FOR_EACH_WIDGET(it) \ 69 for (std::list<Widget*>::iterator it = fWidgets.begin(); it != fWidgets.end(); ++it) 70 71 #define FOR_EACH_WIDGET_INV(rit) \ 72 for (std::list<Widget*>::reverse_iterator rit = fWidgets.rbegin(); rit != fWidgets.rend(); ++rit) 73 74 #if defined(DEBUG) && defined(DGL_DEBUG_EVENTS) 75 # define DBG(msg) std::fprintf(stderr, "%s", msg); 76 # define DBGp(...) std::fprintf(stderr, __VA_ARGS__); 77 # define DBGF std::fflush(stderr); 78 #else 79 # define DBG(msg) 80 # define DBGp(...) 81 # define DBGF 82 #endif 83 84 START_NAMESPACE_DGL 85 86 // ----------------------------------------------------------------------- 87 // Window Private 88 89 struct Window::PrivateData { 90 PrivateData(Application& app, Window* const self) 91 : fApp(app), 92 fSelf(self), 93 fView(puglInit()), 94 fFirstInit(true), 95 fVisible(false), 96 fResizable(true), 97 fUsingEmbed(false), 98 fWidth(1), 99 fHeight(1), 100 fScaling(1.0), 101 fAutoScaling(1.0), 102 fTitle(nullptr), 103 fWidgets(), 104 fModal(), 105 #if defined(DISTRHO_OS_HAIKU) 106 bApplication(nullptr), 107 bView(nullptr), 108 bWindow(nullptr) 109 #elif defined(DISTRHO_OS_MAC) 110 fNeedsIdle(true), 111 mView(nullptr), 112 mWindow(nullptr), 113 mParentWindow(nullptr) 114 # ifndef DGL_FILE_BROWSER_DISABLED 115 , fOpenFilePanel(nullptr), 116 fFilePanelDelegate(nullptr) 117 # endif 118 #elif defined(DISTRHO_OS_WINDOWS) 119 hwnd(nullptr), 120 hwndParent(nullptr) 121 #else 122 xDisplay(nullptr), 123 xWindow(0) 124 #endif 125 { 126 DBG("Creating window without parent..."); DBGF; 127 init(); 128 } 129 130 PrivateData(Application& app, Window* const self, Window& parent) 131 : fApp(app), 132 fSelf(self), 133 fView(puglInit()), 134 fFirstInit(true), 135 fVisible(false), 136 fResizable(true), 137 fUsingEmbed(false), 138 fWidth(1), 139 fHeight(1), 140 fScaling(1.0), 141 fAutoScaling(1.0), 142 fTitle(nullptr), 143 fWidgets(), 144 fModal(parent.pData), 145 #if defined(DISTRHO_OS_HAIKU) 146 bApplication(nullptr), 147 bView(nullptr), 148 bWindow(nullptr) 149 #elif defined(DISTRHO_OS_MAC) 150 fNeedsIdle(false), 151 mView(nullptr), 152 mWindow(nullptr), 153 mParentWindow(nullptr) 154 # ifndef DGL_FILE_BROWSER_DISABLED 155 , fOpenFilePanel(nullptr), 156 fFilePanelDelegate(nullptr) 157 # endif 158 #elif defined(DISTRHO_OS_WINDOWS) 159 hwnd(nullptr), 160 hwndParent(nullptr) 161 #else 162 xDisplay(nullptr), 163 xWindow(0) 164 #endif 165 { 166 DBG("Creating window with parent..."); DBGF; 167 init(); 168 169 const PuglInternals* const parentImpl(parent.pData->fView->impl); 170 171 // NOTE: almost a 1:1 copy of setTransientWinId() 172 #if defined(DISTRHO_OS_HAIKU) 173 // TODO 174 #elif defined(DISTRHO_OS_MAC) 175 mParentWindow = parentImpl->window; 176 #elif defined(DISTRHO_OS_WINDOWS) 177 hwndParent = parentImpl->hwnd; 178 SetWindowLongPtr(hwnd, GWLP_HWNDPARENT, (LONG_PTR)hwndParent); 179 #else 180 XSetTransientForHint(xDisplay, xWindow, parentImpl->win); 181 #endif 182 } 183 184 PrivateData(Application& app, Window* const self, const intptr_t parentId, const double scaling, const bool resizable) 185 : fApp(app), 186 fSelf(self), 187 fView(puglInit()), 188 fFirstInit(true), 189 fVisible(parentId != 0), 190 fResizable(resizable), 191 fUsingEmbed(parentId != 0), 192 fWidth(1), 193 fHeight(1), 194 fScaling(scaling), 195 fAutoScaling(1.0), 196 fTitle(nullptr), 197 fWidgets(), 198 fModal(), 199 #if defined(DISTRHO_OS_HAIKU) 200 bApplication(nullptr), 201 bView(nullptr), 202 bWindow(nullptr) 203 #elif defined(DISTRHO_OS_MAC) 204 fNeedsIdle(parentId == 0), 205 mView(nullptr), 206 mWindow(nullptr), 207 mParentWindow(nullptr) 208 # ifndef DGL_FILE_BROWSER_DISABLED 209 , fOpenFilePanel(nullptr), 210 fFilePanelDelegate(nullptr) 211 # endif 212 #elif defined(DISTRHO_OS_WINDOWS) 213 hwnd(nullptr), 214 hwndParent(nullptr) 215 #else 216 xDisplay(nullptr), 217 xWindow(0) 218 #endif 219 { 220 if (fUsingEmbed) 221 { 222 DBG("Creating embedded window..."); DBGF; 223 puglInitWindowParent(fView, parentId); 224 } 225 else 226 { 227 DBG("Creating window without parent..."); DBGF; 228 } 229 230 init(); 231 232 if (fUsingEmbed) 233 { 234 DBG("NOTE: Embed window is always visible and non-resizable\n"); 235 puglShowWindow(fView); 236 fApp.pData->oneShown(); 237 fFirstInit = false; 238 } 239 } 240 241 void init() 242 { 243 if (fSelf == nullptr || fView == nullptr) 244 { 245 DBG("Failed!\n"); 246 return; 247 } 248 249 puglInitUserResizable(fView, fResizable); 250 puglInitWindowSize(fView, static_cast<int>(fWidth), static_cast<int>(fHeight)); 251 252 puglSetHandle(fView, this); 253 puglSetDisplayFunc(fView, onDisplayCallback); 254 puglSetKeyboardFunc(fView, onKeyboardCallback); 255 puglSetMotionFunc(fView, onMotionCallback); 256 puglSetMouseFunc(fView, onMouseCallback); 257 puglSetScrollFunc(fView, onScrollCallback); 258 puglSetSpecialFunc(fView, onSpecialCallback); 259 puglSetReshapeFunc(fView, onReshapeCallback); 260 puglSetCloseFunc(fView, onCloseCallback); 261 #ifndef DGL_FILE_BROWSER_DISABLED 262 puglSetFileSelectedFunc(fView, fileBrowserSelectedCallback); 263 #endif 264 265 puglCreateWindow(fView, nullptr); 266 267 PuglInternals* impl = fView->impl; 268 269 #if defined(DISTRHO_OS_HAIKU) 270 bApplication = impl->app; 271 bView = impl->view; 272 bWindow = impl->window; 273 #elif defined(DISTRHO_OS_MAC) 274 mView = impl->view; 275 mWindow = impl->window; 276 DISTRHO_SAFE_ASSERT(mView != nullptr); 277 if (fUsingEmbed) { 278 DISTRHO_SAFE_ASSERT(mWindow == nullptr); 279 } else { 280 DISTRHO_SAFE_ASSERT(mWindow != nullptr); 281 } 282 #elif defined(DISTRHO_OS_WINDOWS) 283 hwnd = impl->hwnd; 284 DISTRHO_SAFE_ASSERT(hwnd != 0); 285 #else 286 xDisplay = impl->display; 287 xWindow = impl->win; 288 DISTRHO_SAFE_ASSERT(xWindow != 0); 289 290 if (! fUsingEmbed) 291 { 292 const pid_t pid = getpid(); 293 const Atom _nwp = XInternAtom(xDisplay, "_NET_WM_PID", False); 294 XChangeProperty(xDisplay, xWindow, _nwp, XA_CARDINAL, 32, PropModeReplace, (const uchar*)&pid, 1); 295 296 const Atom _wt = XInternAtom(xDisplay, "_NET_WM_WINDOW_TYPE", False); 297 298 // Setting the window to both dialog and normal will produce a decorated floating dialog 299 // Order is important: DIALOG needs to come before NORMAL 300 const Atom _wts[2] = { 301 XInternAtom(xDisplay, "_NET_WM_WINDOW_TYPE_DIALOG", False), 302 XInternAtom(xDisplay, "_NET_WM_WINDOW_TYPE_NORMAL", False) 303 }; 304 XChangeProperty(xDisplay, xWindow, _wt, XA_ATOM, 32, PropModeReplace, (const uchar*)&_wts, 2); 305 } 306 #endif 307 puglEnterContext(fView); 308 309 fApp.pData->windows.push_back(fSelf); 310 311 DBG("Success!\n"); 312 } 313 314 ~PrivateData() 315 { 316 DBG("Destroying window..."); DBGF; 317 318 if (fModal.enabled) 319 { 320 exec_fini(); 321 close(); 322 } 323 324 fWidgets.clear(); 325 326 if (fUsingEmbed) 327 { 328 puglHideWindow(fView); 329 fApp.pData->oneHidden(); 330 } 331 332 if (fSelf != nullptr) 333 { 334 fApp.pData->windows.remove(fSelf); 335 fSelf = nullptr; 336 } 337 338 if (fView != nullptr) 339 { 340 puglDestroy(fView); 341 fView = nullptr; 342 } 343 344 if (fTitle != nullptr) 345 { 346 std::free(fTitle); 347 fTitle = nullptr; 348 } 349 350 #if defined(DISTRHO_OS_HAIKU) 351 bApplication = nullptr; 352 bView = nullptr; 353 bWindow = nullptr; 354 #elif defined(DISTRHO_OS_MAC) 355 mView = nullptr; 356 mWindow = nullptr; 357 #elif defined(DISTRHO_OS_WINDOWS) 358 hwnd = 0; 359 #else 360 xDisplay = nullptr; 361 xWindow = 0; 362 #endif 363 364 #if defined(DISTRHO_OS_MAC) && !defined(DGL_FILE_BROWSER_DISABLED) 365 if (fOpenFilePanel) 366 { 367 [fOpenFilePanel release]; 368 fOpenFilePanel = nullptr; 369 } 370 if (fFilePanelDelegate) 371 { 372 [fFilePanelDelegate release]; 373 fFilePanelDelegate = nullptr; 374 } 375 #endif 376 377 DBG("Success!\n"); 378 } 379 380 // ------------------------------------------------------------------- 381 382 void close() 383 { 384 DBG("Window close\n"); 385 386 if (fUsingEmbed) 387 return; 388 389 setVisible(false); 390 391 if (! fFirstInit) 392 { 393 fApp.pData->oneHidden(); 394 fFirstInit = true; 395 } 396 } 397 398 void exec(const bool lockWait) 399 { 400 DBG("Window exec\n"); 401 exec_init(); 402 403 if (lockWait) 404 { 405 for (; fVisible && fModal.enabled;) 406 { 407 idle(); 408 d_msleep(10); 409 } 410 411 exec_fini(); 412 } 413 else 414 { 415 idle(); 416 } 417 } 418 419 // ------------------------------------------------------------------- 420 421 void exec_init() 422 { 423 DBG("Window modal loop starting..."); DBGF; 424 DISTRHO_SAFE_ASSERT_RETURN(fModal.parent != nullptr, setVisible(true)); 425 426 fModal.enabled = true; 427 fModal.parent->fModal.childFocus = this; 428 429 fModal.parent->setVisible(true); 430 setVisible(true); 431 432 DBG("Ok\n"); 433 } 434 435 void exec_fini() 436 { 437 DBG("Window modal loop stopping..."); DBGF; 438 fModal.enabled = false; 439 440 if (fModal.parent != nullptr) 441 { 442 fModal.parent->fModal.childFocus = nullptr; 443 444 // the mouse position probably changed since the modal appeared, 445 // so send a mouse motion event to the modal's parent window 446 #if defined(DISTRHO_OS_HAIKU) 447 // TODO 448 #elif defined(DISTRHO_OS_MAC) 449 // TODO 450 #elif defined(DISTRHO_OS_WINDOWS) 451 // TODO 452 #else 453 int i, wx, wy; 454 uint u; 455 ::Window w; 456 if (XQueryPointer(fModal.parent->xDisplay, fModal.parent->xWindow, &w, &w, &i, &i, &wx, &wy, &u) == True) 457 fModal.parent->onPuglMotion(wx, wy); 458 #endif 459 } 460 461 DBG("Ok\n"); 462 } 463 464 // ------------------------------------------------------------------- 465 466 void focus() 467 { 468 DBG("Window focus\n"); 469 #if defined(DISTRHO_OS_HAIKU) 470 if (bWindow != nullptr) 471 { 472 if (bWindow->LockLooper()) 473 { 474 bWindow->Activate(true); 475 bWindow->UnlockLooper(); 476 } 477 } 478 else 479 { 480 bView->MakeFocus(true); 481 } 482 #elif defined(DISTRHO_OS_MAC) 483 if (mWindow != nullptr) 484 [mWindow makeKeyWindow]; 485 #elif defined(DISTRHO_OS_WINDOWS) 486 SetForegroundWindow(hwnd); 487 SetActiveWindow(hwnd); 488 SetFocus(hwnd); 489 #else 490 XRaiseWindow(xDisplay, xWindow); 491 XSetInputFocus(xDisplay, xWindow, RevertToPointerRoot, CurrentTime); 492 XFlush(xDisplay); 493 #endif 494 } 495 496 // ------------------------------------------------------------------- 497 498 void setVisible(const bool yesNo) 499 { 500 if (fVisible == yesNo) 501 { 502 DBG("Window setVisible matches current state, ignoring request\n"); 503 return; 504 } 505 if (fUsingEmbed) 506 { 507 DBG("Window setVisible cannot be called when embedded\n"); 508 return; 509 } 510 511 DBG("Window setVisible called\n"); 512 513 fVisible = yesNo; 514 515 if (yesNo && fFirstInit) 516 setSize(fWidth, fHeight, true); 517 518 #if defined(DISTRHO_OS_HAIKU) 519 if (bWindow != nullptr) 520 { 521 if (bWindow->LockLooper()) 522 { 523 if (yesNo) 524 bWindow->Show(); 525 else 526 bWindow->Hide(); 527 528 // TODO use flush? 529 bWindow->Sync(); 530 bWindow->UnlockLooper(); 531 } 532 } 533 else 534 { 535 if (yesNo) 536 bView->Show(); 537 else 538 bView->Hide(); 539 } 540 #elif defined(DISTRHO_OS_MAC) 541 if (yesNo) 542 { 543 if (mWindow != nullptr) 544 { 545 if (mParentWindow != nullptr) 546 [mParentWindow addChildWindow:mWindow 547 ordered:NSWindowAbove]; 548 549 [mWindow setIsVisible:YES]; 550 } 551 else 552 { 553 [mView setHidden:NO]; 554 } 555 } 556 else 557 { 558 if (mWindow != nullptr) 559 { 560 if (mParentWindow != nullptr) 561 [mParentWindow removeChildWindow:mWindow]; 562 563 [mWindow setIsVisible:NO]; 564 } 565 else 566 { 567 [mView setHidden:YES]; 568 } 569 } 570 #elif defined(DISTRHO_OS_WINDOWS) 571 if (yesNo) 572 { 573 if (fFirstInit) 574 { 575 RECT rectChild, rectParent; 576 577 if (hwndParent != nullptr && 578 GetWindowRect(hwnd, &rectChild) && 579 GetWindowRect(hwndParent, &rectParent)) 580 { 581 SetWindowPos(hwnd, hwndParent, 582 rectParent.left + (rectChild.right-rectChild.left)/2, 583 rectParent.top + (rectChild.bottom-rectChild.top)/2, 584 0, 0, SWP_SHOWWINDOW|SWP_NOSIZE); 585 } 586 else 587 { 588 ShowWindow(hwnd, SW_SHOWNORMAL); 589 } 590 } 591 else 592 { 593 ShowWindow(hwnd, SW_RESTORE); 594 } 595 } 596 else 597 { 598 ShowWindow(hwnd, SW_HIDE); 599 } 600 601 UpdateWindow(hwnd); 602 #else 603 if (yesNo) 604 XMapRaised(xDisplay, xWindow); 605 else 606 XUnmapWindow(xDisplay, xWindow); 607 608 XFlush(xDisplay); 609 #endif 610 611 if (yesNo) 612 { 613 if (fFirstInit) 614 { 615 fApp.pData->oneShown(); 616 fFirstInit = false; 617 } 618 } 619 else if (fModal.enabled) 620 exec_fini(); 621 } 622 623 // ------------------------------------------------------------------- 624 625 void setResizable(const bool yesNo) 626 { 627 if (fResizable == yesNo) 628 { 629 DBG("Window setResizable matches current state, ignoring request\n"); 630 return; 631 } 632 if (fUsingEmbed) 633 { 634 DBG("Window setResizable cannot be called when embedded\n"); 635 return; 636 } 637 638 DBG("Window setResizable called\n"); 639 640 fResizable = yesNo; 641 fView->user_resizable = yesNo; 642 643 #if defined(DISTRHO_OS_HAIKU) 644 // TODO 645 // B_NO_BORDER 646 // B_TITLED_WINDOW_LOOK 647 // bWindow->SetFlags(); 648 #elif defined(DISTRHO_OS_MAC) 649 const uint flags = yesNo ? (NSViewWidthSizable|NSViewHeightSizable) : 0x0; 650 [mView setAutoresizingMask:flags]; 651 #elif defined(DISTRHO_OS_WINDOWS) 652 const int winFlags = fResizable ? GetWindowLong(hwnd, GWL_STYLE) | WS_SIZEBOX 653 : GetWindowLong(hwnd, GWL_STYLE) & ~WS_SIZEBOX; 654 SetWindowLong(hwnd, GWL_STYLE, winFlags); 655 #endif 656 657 setSize(fWidth, fHeight, true); 658 } 659 660 // ------------------------------------------------------------------- 661 662 void setGeometryConstraints(uint width, uint height, bool aspect) 663 { 664 // Did you forget to set DISTRHO_UI_USER_RESIZABLE ? 665 DISTRHO_SAFE_ASSERT_RETURN(fResizable,); 666 667 fView->min_width = width; 668 fView->min_height = height; 669 puglUpdateGeometryConstraints(fView, width, height, aspect); 670 } 671 672 // ------------------------------------------------------------------- 673 674 void setSize(uint width, uint height, const bool forced = false) 675 { 676 if (width <= 1 || height <= 1) 677 { 678 DBGp("Window setSize called with invalid value(s) %i %i, ignoring request\n", width, height); 679 return; 680 } 681 682 if (fWidth == width && fHeight == height && ! forced) 683 { 684 DBGp("Window setSize matches current size, ignoring request (%i %i)\n", width, height); 685 return; 686 } 687 688 fWidth = width; 689 fHeight = height; 690 691 DBGp("Window setSize called %s, size %i %i, resizable %s\n", forced ? "(forced)" : "(not forced)", width, height, fResizable?"true":"false"); 692 693 #if defined(DISTRHO_OS_HAIKU) 694 bView->ResizeTo(width, height); 695 696 if (bWindow != nullptr && bWindow->LockLooper()) 697 { 698 bWindow->MoveTo(50, 50); 699 bWindow->ResizeTo(width, height); 700 701 if (! forced) 702 bWindow->Flush(); 703 704 bWindow->UnlockLooper(); 705 } 706 // TODO resizable 707 #elif defined(DISTRHO_OS_MAC) 708 [mView setFrame:NSMakeRect(0, 0, width, height)]; 709 710 if (mWindow != nullptr) 711 { 712 const NSSize size = NSMakeSize(width, height); 713 [mWindow setContentSize:size]; 714 715 if (fResizable) 716 { 717 [mWindow setContentMinSize:NSMakeSize(1, 1)]; 718 [mWindow setContentMaxSize:NSMakeSize(99999, 99999)]; 719 [[mWindow standardWindowButton:NSWindowZoomButton] setHidden:NO]; 720 } 721 else 722 { 723 [mWindow setContentMinSize:size]; 724 [mWindow setContentMaxSize:size]; 725 [[mWindow standardWindowButton:NSWindowZoomButton] setHidden:YES]; 726 } 727 } 728 #elif defined(DISTRHO_OS_WINDOWS) 729 const int winFlags = WS_POPUPWINDOW | WS_CAPTION | (fResizable ? WS_SIZEBOX : 0x0); 730 RECT wr = { 0, 0, static_cast<LONG>(width), static_cast<LONG>(height) }; 731 AdjustWindowRectEx(&wr, fUsingEmbed ? WS_CHILD : winFlags, FALSE, WS_EX_TOPMOST); 732 733 SetWindowPos(hwnd, 0, 0, 0, wr.right-wr.left, wr.bottom-wr.top, 734 SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOOWNERZORDER|SWP_NOZORDER); 735 736 if (! forced) 737 UpdateWindow(hwnd); 738 #else 739 740 if (! fResizable) 741 { 742 XSizeHints sizeHints; 743 memset(&sizeHints, 0, sizeof(sizeHints)); 744 745 sizeHints.flags = PSize|PMinSize|PMaxSize; 746 sizeHints.width = static_cast<int>(width); 747 sizeHints.height = static_cast<int>(height); 748 sizeHints.min_width = static_cast<int>(width); 749 sizeHints.min_height = static_cast<int>(height); 750 sizeHints.max_width = static_cast<int>(width); 751 sizeHints.max_height = static_cast<int>(height); 752 753 XSetWMNormalHints(xDisplay, xWindow, &sizeHints); 754 } 755 756 XResizeWindow(xDisplay, xWindow, width, height); 757 758 if (! forced) 759 XFlush(xDisplay); 760 #endif 761 762 puglPostRedisplay(fView); 763 } 764 765 // ------------------------------------------------------------------- 766 767 const char* getTitle() const noexcept 768 { 769 static const char* const kFallback = ""; 770 771 return fTitle != nullptr ? fTitle : kFallback; 772 } 773 774 void setTitle(const char* const title) 775 { 776 DBGp("Window setTitle \"%s\"\n", title); 777 778 if (fTitle != nullptr) 779 std::free(fTitle); 780 781 fTitle = strdup(title); 782 783 #if defined(DISTRHO_OS_HAIKU) 784 if (bWindow != nullptr&& bWindow->LockLooper()) 785 { 786 bWindow->SetTitle(title); 787 bWindow->UnlockLooper(); 788 } 789 #elif defined(DISTRHO_OS_MAC) 790 if (mWindow != nullptr) 791 { 792 NSString* titleString = [[NSString alloc] 793 initWithBytes:title 794 length:strlen(title) 795 encoding:NSUTF8StringEncoding]; 796 797 [mWindow setTitle:titleString]; 798 } 799 #elif defined(DISTRHO_OS_WINDOWS) 800 SetWindowTextA(hwnd, title); 801 #else 802 XStoreName(xDisplay, xWindow, title); 803 Atom netWmName = XInternAtom(xDisplay, "_NET_WM_NAME", False); 804 Atom utf8String = XInternAtom(xDisplay, "UTF8_STRING", False); 805 XChangeProperty(xDisplay, xWindow, netWmName, utf8String, 8, PropModeReplace, (unsigned char *)title, strlen(title)); 806 #endif 807 } 808 809 void setTransientWinId(const uintptr_t winId) 810 { 811 DISTRHO_SAFE_ASSERT_RETURN(winId != 0,); 812 813 #if defined(DISTRHO_OS_HAIKU) 814 // TODO 815 #elif defined(DISTRHO_OS_MAC) 816 NSWindow* const parentWindow = [NSApp windowWithWindowNumber:winId]; 817 DISTRHO_SAFE_ASSERT_RETURN(parentWindow != nullptr,); 818 819 [parentWindow addChildWindow:mWindow 820 ordered:NSWindowAbove]; 821 #elif defined(DISTRHO_OS_WINDOWS) 822 hwndParent = (HWND)winId; 823 SetWindowLongPtr(hwnd, GWLP_HWNDPARENT, (LONG_PTR)winId); 824 #else 825 XSetTransientForHint(xDisplay, xWindow, static_cast< ::Window>(winId)); 826 #endif 827 } 828 829 // ------------------------------------------------------------------- 830 831 double getScaling() const noexcept 832 { 833 return fScaling; 834 } 835 836 void setAutoScaling(const double scaling) noexcept 837 { 838 DISTRHO_SAFE_ASSERT_RETURN(scaling > 0.0,); 839 840 fAutoScaling = scaling; 841 } 842 843 // ------------------------------------------------------------------- 844 845 bool getIgnoringKeyRepeat() const noexcept 846 { 847 return fView->ignoreKeyRepeat; 848 } 849 850 void setIgnoringKeyRepeat(bool ignore) noexcept 851 { 852 puglIgnoreKeyRepeat(fView, ignore); 853 } 854 855 // ------------------------------------------------------------------- 856 857 void addWidget(Widget* const widget) 858 { 859 fWidgets.push_back(widget); 860 } 861 862 void removeWidget(Widget* const widget) 863 { 864 fWidgets.remove(widget); 865 } 866 867 void idle() 868 { 869 puglProcessEvents(fView); 870 871 #ifdef DISTRHO_OS_HAIKU 872 if (bApplication != nullptr) 873 { 874 // bApplication->Lock(); 875 // bApplication->Loop(); 876 // bApplication->Unlock(); 877 } 878 #endif 879 880 #ifdef DISTRHO_OS_MAC 881 if (fNeedsIdle) 882 { 883 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; 884 NSEvent* event; 885 886 for (;;) 887 { 888 event = [NSApp 889 nextEventMatchingMask:NSAnyEventMask 890 untilDate:[NSDate distantPast] 891 inMode:NSDefaultRunLoopMode 892 dequeue:YES]; 893 894 if (event == nil) 895 break; 896 897 [NSApp sendEvent: event]; 898 } 899 900 [pool release]; 901 } 902 #endif 903 904 #if defined(DISTRHO_OS_WINDOWS) && !defined(DGL_FILE_BROWSER_DISABLED) 905 if (fSelectedFile.isNotEmpty()) 906 { 907 char* const buffer = fSelectedFile.getAndReleaseBuffer(); 908 fView->fileSelectedFunc(fView, buffer); 909 std::free(buffer); 910 } 911 #endif 912 913 if (fModal.enabled && fModal.parent != nullptr) 914 fModal.parent->idle(); 915 } 916 917 // ------------------------------------------------------------------- 918 919 void onPuglDisplay() 920 { 921 fSelf->onDisplayBefore(); 922 923 FOR_EACH_WIDGET(it) 924 { 925 Widget* const widget(*it); 926 widget->pData->display(fWidth, fHeight, fAutoScaling, false); 927 } 928 929 fSelf->onDisplayAfter(); 930 } 931 932 int onPuglKeyboard(const bool press, const uint key) 933 { 934 DBGp("PUGL: onKeyboard : %i %i\n", press, key); 935 936 if (fModal.childFocus != nullptr) 937 { 938 fModal.childFocus->focus(); 939 return 0; 940 } 941 942 Widget::KeyboardEvent ev; 943 ev.press = press; 944 ev.key = key; 945 ev.mod = static_cast<Modifier>(puglGetModifiers(fView)); 946 ev.time = puglGetEventTimestamp(fView); 947 948 FOR_EACH_WIDGET_INV(rit) 949 { 950 Widget* const widget(*rit); 951 952 if (widget->isVisible() && widget->onKeyboard(ev)) 953 return 0; 954 } 955 956 return 1; 957 } 958 959 int onPuglSpecial(const bool press, const Key key) 960 { 961 DBGp("PUGL: onSpecial : %i %i\n", press, key); 962 963 if (fModal.childFocus != nullptr) 964 { 965 fModal.childFocus->focus(); 966 return 0; 967 } 968 969 Widget::SpecialEvent ev; 970 ev.press = press; 971 ev.key = key; 972 ev.mod = static_cast<Modifier>(puglGetModifiers(fView)); 973 ev.time = puglGetEventTimestamp(fView); 974 975 FOR_EACH_WIDGET_INV(rit) 976 { 977 Widget* const widget(*rit); 978 979 if (widget->isVisible() && widget->onSpecial(ev)) 980 return 0; 981 } 982 983 return 1; 984 } 985 986 void onPuglMouse(const int button, const bool press, int x, int y) 987 { 988 DBGp("PUGL: onMouse : %i %i %i %i\n", button, press, x, y); 989 990 // FIXME - pugl sends 2 of these for each window on init, don't ask me why. we'll ignore it 991 if (press && button == 0 && x == 0 && y == 0) return; 992 993 if (fModal.childFocus != nullptr) 994 return fModal.childFocus->focus(); 995 996 x /= fAutoScaling; 997 y /= fAutoScaling; 998 999 Widget::MouseEvent ev; 1000 ev.button = button; 1001 ev.press = press; 1002 ev.mod = static_cast<Modifier>(puglGetModifiers(fView)); 1003 ev.time = puglGetEventTimestamp(fView); 1004 1005 FOR_EACH_WIDGET_INV(rit) 1006 { 1007 Widget* const widget(*rit); 1008 1009 ev.pos = Point<int>(x-widget->getAbsoluteX(), y-widget->getAbsoluteY()); 1010 1011 if (widget->isVisible() && widget->onMouse(ev)) 1012 break; 1013 } 1014 } 1015 1016 void onPuglMotion(int x, int y) 1017 { 1018 // DBGp("PUGL: onMotion : %i %i\n", x, y); 1019 1020 if (fModal.childFocus != nullptr) 1021 return; 1022 1023 x /= fAutoScaling; 1024 y /= fAutoScaling; 1025 1026 Widget::MotionEvent ev; 1027 ev.mod = static_cast<Modifier>(puglGetModifiers(fView)); 1028 ev.time = puglGetEventTimestamp(fView); 1029 1030 FOR_EACH_WIDGET_INV(rit) 1031 { 1032 Widget* const widget(*rit); 1033 1034 ev.pos = Point<int>(x-widget->getAbsoluteX(), y-widget->getAbsoluteY()); 1035 1036 if (widget->isVisible() && widget->onMotion(ev)) 1037 break; 1038 } 1039 } 1040 1041 void onPuglScroll(int x, int y, float dx, float dy) 1042 { 1043 DBGp("PUGL: onScroll : %i %i %f %f\n", x, y, dx, dy); 1044 1045 if (fModal.childFocus != nullptr) 1046 return; 1047 1048 x /= fAutoScaling; 1049 y /= fAutoScaling; 1050 dx /= fAutoScaling; 1051 dy /= fAutoScaling; 1052 1053 Widget::ScrollEvent ev; 1054 ev.delta = Point<float>(dx, dy); 1055 ev.mod = static_cast<Modifier>(puglGetModifiers(fView)); 1056 ev.time = puglGetEventTimestamp(fView); 1057 1058 FOR_EACH_WIDGET_INV(rit) 1059 { 1060 Widget* const widget(*rit); 1061 1062 ev.pos = Point<int>(x-widget->getAbsoluteX(), y-widget->getAbsoluteY()); 1063 1064 if (widget->isVisible() && widget->onScroll(ev)) 1065 break; 1066 } 1067 } 1068 1069 void onPuglReshape(const int width, const int height) 1070 { 1071 DBGp("PUGL: onReshape : %i %i\n", width, height); 1072 1073 if (width <= 1 && height <= 1) 1074 return; 1075 1076 fWidth = static_cast<uint>(width); 1077 fHeight = static_cast<uint>(height); 1078 1079 fSelf->onReshape(fWidth, fHeight); 1080 1081 FOR_EACH_WIDGET(it) 1082 { 1083 Widget* const widget(*it); 1084 1085 if (widget->pData->needsFullViewport) 1086 widget->setSize(fWidth, fHeight); 1087 } 1088 } 1089 1090 void onPuglClose() 1091 { 1092 DBG("PUGL: onClose\n"); 1093 1094 if (fModal.enabled) 1095 exec_fini(); 1096 1097 fSelf->onClose(); 1098 1099 if (fModal.childFocus != nullptr) 1100 fModal.childFocus->fSelf->onClose(); 1101 1102 close(); 1103 } 1104 1105 // ------------------------------------------------------------------- 1106 1107 bool handlePluginKeyboard(const bool press, const uint key) 1108 { 1109 DBGp("PUGL: handlePluginKeyboard : %i %i\n", press, key); 1110 1111 if (fModal.childFocus != nullptr) 1112 { 1113 fModal.childFocus->focus(); 1114 return true; 1115 } 1116 1117 Widget::KeyboardEvent ev; 1118 ev.press = press; 1119 ev.key = key; 1120 ev.mod = static_cast<Modifier>(fView->mods); 1121 ev.time = 0; 1122 1123 if ((ev.mod & kModifierShift) != 0 && ev.key >= 'a' && ev.key <= 'z') 1124 ev.key -= 'a' - 'A'; // a-z -> A-Z 1125 1126 FOR_EACH_WIDGET_INV(rit) 1127 { 1128 Widget* const widget(*rit); 1129 1130 if (widget->isVisible() && widget->onKeyboard(ev)) 1131 return true; 1132 } 1133 1134 return false; 1135 } 1136 1137 bool handlePluginSpecial(const bool press, const Key key) 1138 { 1139 DBGp("PUGL: handlePluginSpecial : %i %i\n", press, key); 1140 1141 if (fModal.childFocus != nullptr) 1142 { 1143 fModal.childFocus->focus(); 1144 return true; 1145 } 1146 1147 int mods = 0x0; 1148 1149 switch (key) 1150 { 1151 case kKeyShift: 1152 mods |= kModifierShift; 1153 break; 1154 case kKeyControl: 1155 mods |= kModifierControl; 1156 break; 1157 case kKeyAlt: 1158 mods |= kModifierAlt; 1159 break; 1160 default: 1161 break; 1162 } 1163 1164 if (mods != 0x0) 1165 { 1166 if (press) 1167 fView->mods |= mods; 1168 else 1169 fView->mods &= ~(mods); 1170 } 1171 1172 Widget::SpecialEvent ev; 1173 ev.press = press; 1174 ev.key = key; 1175 ev.mod = static_cast<Modifier>(fView->mods); 1176 ev.time = 0; 1177 1178 FOR_EACH_WIDGET_INV(rit) 1179 { 1180 Widget* const widget(*rit); 1181 1182 if (widget->isVisible() && widget->onSpecial(ev)) 1183 return true; 1184 } 1185 1186 return false; 1187 } 1188 1189 #if defined(DISTRHO_OS_MAC) && !defined(DGL_FILE_BROWSER_DISABLED) 1190 static void openPanelDidEnd(NSOpenPanel* panel, int returnCode, void *userData) 1191 { 1192 PrivateData* pData = (PrivateData*)userData; 1193 1194 if (returnCode == NSOKButton) 1195 { 1196 NSArray* urls = [panel URLs]; 1197 NSURL* fileUrl = nullptr; 1198 1199 for (NSUInteger i = 0, n = [urls count]; i < n && !fileUrl; ++i) 1200 { 1201 NSURL* url = (NSURL*)[urls objectAtIndex:i]; 1202 if ([url isFileURL]) 1203 fileUrl = url; 1204 } 1205 1206 if (fileUrl) 1207 { 1208 PuglView* view = pData->fView; 1209 if (view->fileSelectedFunc) 1210 { 1211 const char* fileName = [fileUrl.path UTF8String]; 1212 view->fileSelectedFunc(view, fileName); 1213 } 1214 } 1215 } 1216 1217 [pData->fOpenFilePanel release]; 1218 pData->fOpenFilePanel = nullptr; 1219 } 1220 #endif 1221 1222 // ------------------------------------------------------------------- 1223 1224 Application& fApp; 1225 Window* fSelf; 1226 GraphicsContext fContext; 1227 PuglView* fView; 1228 1229 bool fFirstInit; 1230 bool fVisible; 1231 bool fResizable; 1232 bool fUsingEmbed; 1233 uint fWidth; 1234 uint fHeight; 1235 double fScaling; 1236 double fAutoScaling; 1237 char* fTitle; 1238 std::list<Widget*> fWidgets; 1239 1240 struct Modal { 1241 bool enabled; 1242 PrivateData* parent; 1243 PrivateData* childFocus; 1244 1245 Modal() 1246 : enabled(false), 1247 parent(nullptr), 1248 childFocus(nullptr) {} 1249 1250 Modal(PrivateData* const p) 1251 : enabled(false), 1252 parent(p), 1253 childFocus(nullptr) {} 1254 1255 ~Modal() 1256 { 1257 DISTRHO_SAFE_ASSERT(! enabled); 1258 DISTRHO_SAFE_ASSERT(childFocus == nullptr); 1259 } 1260 1261 DISTRHO_DECLARE_NON_COPY_STRUCT(Modal) 1262 } fModal; 1263 1264 #if defined(DISTRHO_OS_HAIKU) 1265 BApplication* bApplication; 1266 BView* bView; 1267 BWindow* bWindow; 1268 #elif defined(DISTRHO_OS_MAC) 1269 bool fNeedsIdle; 1270 NSView<PuglGenericView>* mView; 1271 id mWindow; 1272 id mParentWindow; 1273 # ifndef DGL_FILE_BROWSER_DISABLED 1274 NSOpenPanel* fOpenFilePanel; 1275 id fFilePanelDelegate; 1276 # endif 1277 #elif defined(DISTRHO_OS_WINDOWS) 1278 HWND hwnd; 1279 HWND hwndParent; 1280 # ifndef DGL_FILE_BROWSER_DISABLED 1281 String fSelectedFile; 1282 # endif 1283 #else 1284 Display* xDisplay; 1285 ::Window xWindow; 1286 #endif 1287 1288 // ------------------------------------------------------------------- 1289 // Callbacks 1290 1291 #define handlePtr ((PrivateData*)puglGetHandle(view)) 1292 1293 static void onDisplayCallback(PuglView* view) 1294 { 1295 handlePtr->onPuglDisplay(); 1296 } 1297 1298 static int onKeyboardCallback(PuglView* view, bool press, uint32_t key) 1299 { 1300 return handlePtr->onPuglKeyboard(press, key); 1301 } 1302 1303 static int onSpecialCallback(PuglView* view, bool press, PuglKey key) 1304 { 1305 return handlePtr->onPuglSpecial(press, static_cast<Key>(key)); 1306 } 1307 1308 static void onMouseCallback(PuglView* view, int button, bool press, int x, int y) 1309 { 1310 handlePtr->onPuglMouse(button, press, x, y); 1311 } 1312 1313 static void onMotionCallback(PuglView* view, int x, int y) 1314 { 1315 handlePtr->onPuglMotion(x, y); 1316 } 1317 1318 static void onScrollCallback(PuglView* view, int x, int y, float dx, float dy) 1319 { 1320 handlePtr->onPuglScroll(x, y, dx, dy); 1321 } 1322 1323 static void onReshapeCallback(PuglView* view, int width, int height) 1324 { 1325 handlePtr->onPuglReshape(width, height); 1326 } 1327 1328 static void onCloseCallback(PuglView* view) 1329 { 1330 handlePtr->onPuglClose(); 1331 } 1332 1333 #ifndef DGL_FILE_BROWSER_DISABLED 1334 static void fileBrowserSelectedCallback(PuglView* view, const char* filename) 1335 { 1336 handlePtr->fSelf->fileBrowserSelected(filename); 1337 } 1338 #endif 1339 1340 #undef handlePtr 1341 1342 DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PrivateData) 1343 }; 1344 1345 // ----------------------------------------------------------------------- 1346 // Window 1347 1348 Window::Window(Application& app) 1349 : pData(new PrivateData(app, this)) {} 1350 1351 Window::Window(Application& app, Window& parent) 1352 : pData(new PrivateData(app, this, parent)) {} 1353 1354 Window::Window(Application& app, intptr_t parentId, double scaling, bool resizable) 1355 : pData(new PrivateData(app, this, parentId, scaling, resizable)) {} 1356 1357 Window::~Window() 1358 { 1359 delete pData; 1360 } 1361 1362 void Window::show() 1363 { 1364 pData->setVisible(true); 1365 } 1366 1367 void Window::hide() 1368 { 1369 pData->setVisible(false); 1370 } 1371 1372 void Window::close() 1373 { 1374 pData->close(); 1375 } 1376 1377 void Window::exec(bool lockWait) 1378 { 1379 pData->exec(lockWait); 1380 } 1381 1382 void Window::focus() 1383 { 1384 pData->focus(); 1385 } 1386 1387 void Window::repaint() noexcept 1388 { 1389 puglPostRedisplay(pData->fView); 1390 } 1391 1392 // static int fib_filter_filename_filter(const char* const name) 1393 // { 1394 // return 1; 1395 // (void)name; 1396 // } 1397 1398 #ifndef DGL_FILE_BROWSER_DISABLED 1399 1400 #ifdef DISTRHO_OS_MAC 1401 END_NAMESPACE_DGL 1402 @interface FilePanelDelegate : NSObject 1403 { 1404 void (*fCallback)(NSOpenPanel*, int, void*); 1405 void* fUserData; 1406 } 1407 -(id)initWithCallback:(void(*)(NSOpenPanel*, int, void*))callback userData:(void*)userData; 1408 -(void)openPanelDidEnd:(NSOpenPanel *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo; 1409 @end 1410 START_NAMESPACE_DGL 1411 #endif 1412 1413 bool Window::openFileBrowser(const FileBrowserOptions& options) 1414 { 1415 # ifdef SOFD_HAVE_X11 1416 using DISTRHO_NAMESPACE::String; 1417 1418 // -------------------------------------------------------------------------- 1419 // configure start dir 1420 1421 // TODO: get abspath if needed 1422 // TODO: cross-platform 1423 1424 String startDir(options.startDir); 1425 1426 # ifdef DISTRHO_OS_LINUX 1427 if (startDir.isEmpty()) 1428 { 1429 if (char* const dir_name = get_current_dir_name()) 1430 { 1431 startDir = dir_name; 1432 std::free(dir_name); 1433 } 1434 } 1435 # endif 1436 1437 DISTRHO_SAFE_ASSERT_RETURN(startDir.isNotEmpty(), false); 1438 1439 if (! startDir.endsWith('/')) 1440 startDir += "/"; 1441 1442 DISTRHO_SAFE_ASSERT_RETURN(x_fib_configure(0, startDir) == 0, false); 1443 1444 // -------------------------------------------------------------------------- 1445 // configure title 1446 1447 String title(options.title); 1448 1449 if (title.isEmpty()) 1450 { 1451 title = pData->getTitle(); 1452 1453 if (title.isEmpty()) 1454 title = "FileBrowser"; 1455 } 1456 1457 DISTRHO_SAFE_ASSERT_RETURN(x_fib_configure(1, title) == 0, false); 1458 1459 // -------------------------------------------------------------------------- 1460 // configure filters 1461 1462 x_fib_cfg_filter_callback(nullptr); //fib_filter_filename_filter); 1463 1464 // -------------------------------------------------------------------------- 1465 // configure buttons 1466 1467 x_fib_cfg_buttons(3, options.buttons.listAllFiles-1); 1468 x_fib_cfg_buttons(1, options.buttons.showHidden-1); 1469 x_fib_cfg_buttons(2, options.buttons.showPlaces-1); 1470 1471 // -------------------------------------------------------------------------- 1472 // show 1473 1474 return (x_fib_show(pData->xDisplay, pData->xWindow, /*options.width*/0, /*options.height*/0) == 0); 1475 # elif defined(DISTRHO_OS_WINDOWS) 1476 // the old and compatible dialog API 1477 OPENFILENAMEW ofn; 1478 memset(&ofn, 0, sizeof(ofn)); 1479 1480 ofn.lStructSize = sizeof(ofn); 1481 ofn.hwndOwner = pData->hwnd; 1482 1483 // set initial directory in UTF-16 coding 1484 std::vector<WCHAR> startDirW; 1485 if (options.startDir) 1486 { 1487 startDirW.resize(strlen(options.startDir) + 1); 1488 if (MultiByteToWideChar(CP_UTF8, 0, options.startDir, -1, startDirW.data(), startDirW.size())) 1489 ofn.lpstrInitialDir = startDirW.data(); 1490 } 1491 1492 // set title in UTF-16 coding 1493 std::vector<WCHAR> titleW; 1494 if (options.title) 1495 { 1496 titleW.resize(strlen(options.title) + 1); 1497 if (MultiByteToWideChar(CP_UTF8, 0, options.title, -1, titleW.data(), titleW.size())) 1498 ofn.lpstrTitle = titleW.data(); 1499 } 1500 1501 // prepare a buffer to receive the result 1502 std::vector<WCHAR> fileNameW(32768); // the Unicode maximum 1503 ofn.lpstrFile = fileNameW.data(); 1504 ofn.nMaxFile = (DWORD)fileNameW.size(); 1505 1506 // TODO synchronous only, can't do better with WinAPI native dialogs. 1507 // threading might work, if someone is motivated to risk it. 1508 if (GetOpenFileNameW(&ofn)) 1509 { 1510 // back to UTF-8 1511 std::vector<char> fileNameA(4 * 32768); 1512 if (WideCharToMultiByte(CP_UTF8, 0, fileNameW.data(), -1, fileNameA.data(), (int)fileNameA.size(), nullptr, nullptr)) 1513 { 1514 // handle it during the next idle cycle (fake async) 1515 pData->fSelectedFile = fileNameA.data(); 1516 } 1517 } 1518 1519 return true; 1520 # elif defined(DISTRHO_OS_MAC) 1521 if (pData->fOpenFilePanel) // permit one dialog at most 1522 { 1523 [pData->fOpenFilePanel makeKeyAndOrderFront:nil]; 1524 return false; 1525 } 1526 1527 NSOpenPanel* panel = [NSOpenPanel openPanel]; 1528 pData->fOpenFilePanel = [panel retain]; 1529 1530 [panel setCanChooseFiles:YES]; 1531 [panel setCanChooseDirectories:NO]; 1532 [panel setAllowsMultipleSelection:NO]; 1533 1534 if (options.startDir) 1535 [panel setDirectory:[NSString stringWithUTF8String:options.startDir]]; 1536 1537 if (options.title) 1538 { 1539 NSString* titleString = [[NSString alloc] 1540 initWithBytes:options.title 1541 length:strlen(options.title) 1542 encoding:NSUTF8StringEncoding]; 1543 [panel setTitle:titleString]; 1544 } 1545 1546 id delegate = pData->fFilePanelDelegate; 1547 if (!delegate) 1548 { 1549 delegate = [[FilePanelDelegate alloc] initWithCallback:&PrivateData::openPanelDidEnd 1550 userData:pData]; 1551 pData->fFilePanelDelegate = [delegate retain]; 1552 } 1553 1554 [panel beginSheetForDirectory:nullptr 1555 file:nullptr 1556 modalForWindow:nullptr 1557 modalDelegate:delegate 1558 didEndSelector:@selector(openPanelDidEnd:returnCode:contextInfo:) 1559 contextInfo:nullptr]; 1560 1561 return true; 1562 # else 1563 // not implemented 1564 return false; 1565 1566 // unused 1567 (void)options; 1568 # endif 1569 } 1570 1571 #ifdef DISTRHO_OS_MAC 1572 END_NAMESPACE_DGL 1573 @implementation FilePanelDelegate 1574 -(id)initWithCallback:(void(*)(NSOpenPanel*, int, void*))callback userData:(void *)userData 1575 { 1576 [super init]; 1577 self->fCallback = callback; 1578 self->fUserData = userData; 1579 return self; 1580 } 1581 1582 -(void)openPanelDidEnd:(NSOpenPanel *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo 1583 { 1584 self->fCallback(sheet, returnCode, self->fUserData); 1585 (void)contextInfo; 1586 } 1587 @end 1588 START_NAMESPACE_DGL 1589 #endif 1590 1591 #endif // !defined(DGL_FILE_BROWSER_DISABLED) 1592 1593 bool Window::isEmbed() const noexcept 1594 { 1595 return pData->fUsingEmbed; 1596 } 1597 1598 bool Window::isVisible() const noexcept 1599 { 1600 return pData->fVisible; 1601 } 1602 1603 void Window::setVisible(bool yesNo) 1604 { 1605 pData->setVisible(yesNo); 1606 } 1607 1608 bool Window::isResizable() const noexcept 1609 { 1610 return pData->fResizable; 1611 } 1612 1613 void Window::setResizable(bool yesNo) 1614 { 1615 pData->setResizable(yesNo); 1616 } 1617 1618 void Window::setGeometryConstraints(uint width, uint height, bool aspect) 1619 { 1620 pData->setGeometryConstraints(width, height, aspect); 1621 } 1622 1623 uint Window::getWidth() const noexcept 1624 { 1625 return pData->fWidth; 1626 } 1627 1628 uint Window::getHeight() const noexcept 1629 { 1630 return pData->fHeight; 1631 } 1632 1633 Size<uint> Window::getSize() const noexcept 1634 { 1635 return Size<uint>(pData->fWidth, pData->fHeight); 1636 } 1637 1638 void Window::setSize(uint width, uint height) 1639 { 1640 pData->setSize(width, height); 1641 } 1642 1643 void Window::setSize(Size<uint> size) 1644 { 1645 pData->setSize(size.getWidth(), size.getHeight()); 1646 } 1647 1648 const char* Window::getTitle() const noexcept 1649 { 1650 return pData->getTitle(); 1651 } 1652 1653 void Window::setTitle(const char* title) 1654 { 1655 pData->setTitle(title); 1656 } 1657 1658 void Window::setTransientWinId(uintptr_t winId) 1659 { 1660 pData->setTransientWinId(winId); 1661 } 1662 1663 double Window::getScaling() const noexcept 1664 { 1665 return pData->getScaling(); 1666 } 1667 1668 bool Window::getIgnoringKeyRepeat() const noexcept 1669 { 1670 return pData->getIgnoringKeyRepeat(); 1671 } 1672 1673 void Window::setIgnoringKeyRepeat(bool ignore) noexcept 1674 { 1675 pData->setIgnoringKeyRepeat(ignore); 1676 } 1677 1678 Application& Window::getApp() const noexcept 1679 { 1680 return pData->fApp; 1681 } 1682 1683 intptr_t Window::getWindowId() const noexcept 1684 { 1685 return puglGetNativeWindow(pData->fView); 1686 } 1687 1688 const GraphicsContext& Window::getGraphicsContext() const noexcept 1689 { 1690 GraphicsContext& context = pData->fContext; 1691 #ifdef DGL_CAIRO 1692 context.cairo = (cairo_t*)puglGetContext(pData->fView); 1693 #endif 1694 return context; 1695 } 1696 1697 void Window::_setAutoScaling(double scaling) noexcept 1698 { 1699 pData->setAutoScaling(scaling); 1700 } 1701 1702 void Window::_addWidget(Widget* const widget) 1703 { 1704 pData->addWidget(widget); 1705 } 1706 1707 void Window::_removeWidget(Widget* const widget) 1708 { 1709 pData->removeWidget(widget); 1710 } 1711 1712 void Window::_idle() 1713 { 1714 pData->idle(); 1715 } 1716 1717 // ----------------------------------------------------------------------- 1718 1719 void Window::addIdleCallback(IdleCallback* const callback) 1720 { 1721 DISTRHO_SAFE_ASSERT_RETURN(callback != nullptr,) 1722 1723 pData->fApp.pData->idleCallbacks.push_back(callback); 1724 } 1725 1726 void Window::removeIdleCallback(IdleCallback* const callback) 1727 { 1728 DISTRHO_SAFE_ASSERT_RETURN(callback != nullptr,) 1729 1730 pData->fApp.pData->idleCallbacks.remove(callback); 1731 } 1732 1733 // ----------------------------------------------------------------------- 1734 1735 void Window::onDisplayBefore() 1736 { 1737 #ifdef DGL_OPENGL 1738 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 1739 glLoadIdentity(); 1740 #endif 1741 } 1742 1743 void Window::onDisplayAfter() 1744 { 1745 } 1746 1747 void Window::onReshape(uint width, uint height) 1748 { 1749 #ifdef DGL_OPENGL 1750 glEnable(GL_BLEND); 1751 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 1752 glMatrixMode(GL_PROJECTION); 1753 glLoadIdentity(); 1754 glOrtho(0.0, static_cast<GLdouble>(width), static_cast<GLdouble>(height), 0.0, 0.0, 1.0); 1755 glViewport(0, 0, static_cast<GLsizei>(width), static_cast<GLsizei>(height)); 1756 glMatrixMode(GL_MODELVIEW); 1757 glLoadIdentity(); 1758 #endif 1759 } 1760 1761 void Window::onClose() 1762 { 1763 } 1764 1765 #ifndef DGL_FILE_BROWSER_DISABLED 1766 void Window::fileBrowserSelected(const char*) 1767 { 1768 } 1769 #endif 1770 1771 bool Window::handlePluginKeyboard(const bool press, const uint key) 1772 { 1773 return pData->handlePluginKeyboard(press, key); 1774 } 1775 1776 bool Window::handlePluginSpecial(const bool press, const Key key) 1777 { 1778 return pData->handlePluginSpecial(press, key); 1779 } 1780 1781 // ----------------------------------------------------------------------- 1782 1783 StandaloneWindow::StandaloneWindow() 1784 : Application(), 1785 Window((Application&)*this), 1786 fWidget(nullptr) {} 1787 1788 void StandaloneWindow::exec() 1789 { 1790 Window::show(); 1791 Application::exec(); 1792 } 1793 1794 void StandaloneWindow::onReshape(uint width, uint height) 1795 { 1796 if (fWidget != nullptr) 1797 fWidget->setSize(width, height); 1798 Window::onReshape(width, height); 1799 } 1800 1801 void StandaloneWindow::_addWidget(Widget* widget) 1802 { 1803 if (fWidget == nullptr) 1804 { 1805 fWidget = widget; 1806 fWidget->pData->needsFullViewport = true; 1807 } 1808 Window::_addWidget(widget); 1809 } 1810 1811 void StandaloneWindow::_removeWidget(Widget* widget) 1812 { 1813 if (fWidget == widget) 1814 { 1815 fWidget->pData->needsFullViewport = false; 1816 fWidget = nullptr; 1817 } 1818 Window::_removeWidget(widget); 1819 } 1820 1821 // ----------------------------------------------------------------------- 1822 1823 END_NAMESPACE_DGL 1824 1825 #undef DBG 1826 #undef DBGF 1827