Collection of DPF-based plugins for packaging
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1415 lines
35KB

  1. /*
  2. * DISTRHO Plugin Framework (DPF)
  3. * Copyright (C) 2012-2018 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. // we need this for now
  17. //#define PUGL_GRAB_FOCUS 1
  18. #include "../Base.hpp"
  19. #undef PUGL_HAVE_CAIRO
  20. #undef PUGL_HAVE_GL
  21. #define PUGL_HAVE_GL 1
  22. #include "pugl/pugl.h"
  23. #if defined(__GNUC__) && (__GNUC__ >= 7)
  24. # pragma GCC diagnostic push
  25. # pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
  26. #endif
  27. #if defined(DISTRHO_OS_WINDOWS)
  28. # include "pugl/pugl_win.cpp"
  29. # undef max
  30. # undef min
  31. #elif defined(DISTRHO_OS_MAC)
  32. # define PuglWindow DISTRHO_JOIN_MACRO(PuglWindow, DGL_NAMESPACE)
  33. # define PuglOpenGLView DISTRHO_JOIN_MACRO(PuglOpenGLView, DGL_NAMESPACE)
  34. # include "pugl/pugl_osx.m"
  35. #else
  36. # include <sys/types.h>
  37. # include <unistd.h>
  38. extern "C" {
  39. # include "pugl/pugl_x11.c"
  40. }
  41. #endif
  42. #if defined(__GNUC__) && (__GNUC__ >= 7)
  43. # pragma GCC diagnostic pop
  44. #endif
  45. #include "ApplicationPrivateData.hpp"
  46. #include "WidgetPrivateData.hpp"
  47. #include "../StandaloneWindow.hpp"
  48. #include "../../distrho/extra/String.hpp"
  49. #define FOR_EACH_WIDGET(it) \
  50. for (std::list<Widget*>::iterator it = fWidgets.begin(); it != fWidgets.end(); ++it)
  51. #define FOR_EACH_WIDGET_INV(rit) \
  52. for (std::list<Widget*>::reverse_iterator rit = fWidgets.rbegin(); rit != fWidgets.rend(); ++rit)
  53. #ifdef DEBUG
  54. # define DBG(msg) std::fprintf(stderr, "%s", msg);
  55. # define DBGp(...) std::fprintf(stderr, __VA_ARGS__);
  56. # define DBGF std::fflush(stderr);
  57. #else
  58. # define DBG(msg)
  59. # define DBGp(...)
  60. # define DBGF
  61. #endif
  62. START_NAMESPACE_DGL
  63. // -----------------------------------------------------------------------
  64. // Window Private
  65. struct Window::PrivateData {
  66. PrivateData(Application& app, Window* const self)
  67. : fApp(app),
  68. fSelf(self),
  69. fView(puglInit()),
  70. fFirstInit(true),
  71. fVisible(false),
  72. fResizable(true),
  73. fUsingEmbed(false),
  74. fWidth(1),
  75. fHeight(1),
  76. fTitle(nullptr),
  77. fWidgets(),
  78. fModal(),
  79. #if defined(DISTRHO_OS_WINDOWS)
  80. hwnd(nullptr),
  81. hwndParent(nullptr)
  82. #elif defined(DISTRHO_OS_MAC)
  83. fNeedsIdle(true),
  84. mView(nullptr),
  85. mWindow(nullptr),
  86. mParentWindow(nullptr)
  87. #else
  88. xDisplay(nullptr),
  89. xWindow(0)
  90. #endif
  91. {
  92. DBG("Creating window without parent..."); DBGF;
  93. init();
  94. }
  95. PrivateData(Application& app, Window* const self, Window& parent)
  96. : fApp(app),
  97. fSelf(self),
  98. fView(puglInit()),
  99. fFirstInit(true),
  100. fVisible(false),
  101. fResizable(true),
  102. fUsingEmbed(false),
  103. fWidth(1),
  104. fHeight(1),
  105. fTitle(nullptr),
  106. fWidgets(),
  107. fModal(parent.pData),
  108. #if defined(DISTRHO_OS_WINDOWS)
  109. hwnd(nullptr),
  110. hwndParent(nullptr)
  111. #elif defined(DISTRHO_OS_MAC)
  112. fNeedsIdle(false),
  113. mView(nullptr),
  114. mWindow(nullptr),
  115. mParentWindow(nullptr)
  116. #else
  117. xDisplay(nullptr),
  118. xWindow(0)
  119. #endif
  120. {
  121. DBG("Creating window with parent..."); DBGF;
  122. init();
  123. const PuglInternals* const parentImpl(parent.pData->fView->impl);
  124. // NOTE: almost a 1:1 copy of setTransientWinId()
  125. #if defined(DISTRHO_OS_WINDOWS)
  126. hwndParent = parentImpl->hwnd;
  127. SetWindowLongPtr(hwnd, GWLP_HWNDPARENT, (LONG_PTR)hwndParent);
  128. #elif defined(DISTRHO_OS_MAC)
  129. mParentWindow = parentImpl->window;
  130. #else
  131. XSetTransientForHint(xDisplay, xWindow, parentImpl->win);
  132. #endif
  133. }
  134. PrivateData(Application& app, Window* const self, const intptr_t parentId)
  135. : fApp(app),
  136. fSelf(self),
  137. fView(puglInit()),
  138. fFirstInit(true),
  139. fVisible(parentId != 0),
  140. fResizable(parentId == 0),
  141. fUsingEmbed(parentId != 0),
  142. fWidth(1),
  143. fHeight(1),
  144. fTitle(nullptr),
  145. fWidgets(),
  146. fModal(),
  147. #if defined(DISTRHO_OS_WINDOWS)
  148. hwnd(nullptr),
  149. hwndParent(nullptr)
  150. #elif defined(DISTRHO_OS_MAC)
  151. fNeedsIdle(parentId == 0),
  152. mView(nullptr),
  153. mWindow(nullptr),
  154. mParentWindow(nullptr)
  155. #else
  156. xDisplay(nullptr),
  157. xWindow(0)
  158. #endif
  159. {
  160. if (fUsingEmbed)
  161. {
  162. DBG("Creating embedded window..."); DBGF;
  163. puglInitWindowParent(fView, parentId);
  164. }
  165. else
  166. {
  167. DBG("Creating window without parent..."); DBGF;
  168. }
  169. init();
  170. if (fUsingEmbed)
  171. {
  172. DBG("NOTE: Embed window is always visible and non-resizable\n");
  173. puglShowWindow(fView);
  174. fApp.pData->oneShown();
  175. fFirstInit = false;
  176. }
  177. }
  178. void init()
  179. {
  180. if (fSelf == nullptr || fView == nullptr)
  181. {
  182. DBG("Failed!\n");
  183. return;
  184. }
  185. puglInitContextType(fView, PUGL_GL);
  186. puglInitUserResizable(fView, fResizable);
  187. puglInitWindowSize(fView, static_cast<int>(fWidth), static_cast<int>(fHeight));
  188. puglSetHandle(fView, this);
  189. puglSetDisplayFunc(fView, onDisplayCallback);
  190. puglSetKeyboardFunc(fView, onKeyboardCallback);
  191. puglSetMotionFunc(fView, onMotionCallback);
  192. puglSetMouseFunc(fView, onMouseCallback);
  193. puglSetScrollFunc(fView, onScrollCallback);
  194. puglSetSpecialFunc(fView, onSpecialCallback);
  195. puglSetReshapeFunc(fView, onReshapeCallback);
  196. puglSetCloseFunc(fView, onCloseCallback);
  197. #ifndef DGL_FILE_BROWSER_DISABLED
  198. puglSetFileSelectedFunc(fView, fileBrowserSelectedCallback);
  199. #endif
  200. puglCreateWindow(fView, nullptr);
  201. PuglInternals* impl = fView->impl;
  202. #if defined(DISTRHO_OS_WINDOWS)
  203. hwnd = impl->hwnd;
  204. DISTRHO_SAFE_ASSERT(hwnd != 0);
  205. #elif defined(DISTRHO_OS_MAC)
  206. mView = impl->glview;
  207. mWindow = impl->window;
  208. DISTRHO_SAFE_ASSERT(mView != nullptr);
  209. if (fUsingEmbed) {
  210. DISTRHO_SAFE_ASSERT(mWindow == nullptr);
  211. } else {
  212. DISTRHO_SAFE_ASSERT(mWindow != nullptr);
  213. }
  214. #else
  215. xDisplay = impl->display;
  216. xWindow = impl->win;
  217. DISTRHO_SAFE_ASSERT(xWindow != 0);
  218. if (! fUsingEmbed)
  219. {
  220. const pid_t pid = getpid();
  221. const Atom _nwp = XInternAtom(xDisplay, "_NET_WM_PID", False);
  222. XChangeProperty(xDisplay, xWindow, _nwp, XA_CARDINAL, 32, PropModeReplace, (const uchar*)&pid, 1);
  223. const Atom _wt = XInternAtom(xDisplay, "_NET_WM_WINDOW_TYPE", False);
  224. // Setting the window to both dialog and normal will produce a decorated floating dialog
  225. // Order is important: DIALOG needs to come before NORMAL
  226. const Atom _wts[2] = {
  227. XInternAtom(xDisplay, "_NET_WM_WINDOW_TYPE_DIALOG", False),
  228. XInternAtom(xDisplay, "_NET_WM_WINDOW_TYPE_NORMAL", False)
  229. };
  230. XChangeProperty(xDisplay, xWindow, _wt, XA_ATOM, 32, PropModeReplace, (const uchar*)&_wts, 2);
  231. }
  232. #endif
  233. puglEnterContext(fView);
  234. fApp.pData->windows.push_back(fSelf);
  235. DBG("Success!\n");
  236. }
  237. ~PrivateData()
  238. {
  239. DBG("Destroying window..."); DBGF;
  240. if (fModal.enabled)
  241. {
  242. exec_fini();
  243. close();
  244. }
  245. fWidgets.clear();
  246. if (fUsingEmbed)
  247. {
  248. puglHideWindow(fView);
  249. fApp.pData->oneHidden();
  250. }
  251. if (fSelf != nullptr)
  252. {
  253. fApp.pData->windows.remove(fSelf);
  254. fSelf = nullptr;
  255. }
  256. if (fView != nullptr)
  257. {
  258. puglDestroy(fView);
  259. fView = nullptr;
  260. }
  261. if (fTitle != nullptr)
  262. {
  263. std::free(fTitle);
  264. fTitle = nullptr;
  265. }
  266. #if defined(DISTRHO_OS_WINDOWS)
  267. hwnd = 0;
  268. #elif defined(DISTRHO_OS_MAC)
  269. mView = nullptr;
  270. mWindow = nullptr;
  271. #else
  272. xDisplay = nullptr;
  273. xWindow = 0;
  274. #endif
  275. DBG("Success!\n");
  276. }
  277. // -------------------------------------------------------------------
  278. void close()
  279. {
  280. DBG("Window close\n");
  281. if (fUsingEmbed)
  282. return;
  283. setVisible(false);
  284. if (! fFirstInit)
  285. {
  286. fApp.pData->oneHidden();
  287. fFirstInit = true;
  288. }
  289. }
  290. void exec(const bool lockWait)
  291. {
  292. DBG("Window exec\n");
  293. exec_init();
  294. if (lockWait)
  295. {
  296. for (; fVisible && fModal.enabled;)
  297. {
  298. idle();
  299. d_msleep(10);
  300. }
  301. exec_fini();
  302. }
  303. else
  304. {
  305. idle();
  306. }
  307. }
  308. // -------------------------------------------------------------------
  309. void exec_init()
  310. {
  311. DBG("Window modal loop starting..."); DBGF;
  312. DISTRHO_SAFE_ASSERT_RETURN(fModal.parent != nullptr, setVisible(true));
  313. fModal.enabled = true;
  314. fModal.parent->fModal.childFocus = this;
  315. fModal.parent->setVisible(true);
  316. setVisible(true);
  317. DBG("Ok\n");
  318. }
  319. void exec_fini()
  320. {
  321. DBG("Window modal loop stopping..."); DBGF;
  322. fModal.enabled = false;
  323. if (fModal.parent != nullptr)
  324. {
  325. fModal.parent->fModal.childFocus = nullptr;
  326. // the mouse position probably changed since the modal appeared,
  327. // so send a mouse motion event to the modal's parent window
  328. #if defined(DISTRHO_OS_WINDOWS)
  329. // TODO
  330. #elif defined(DISTRHO_OS_MAC)
  331. // TODO
  332. #else
  333. int i, wx, wy;
  334. uint u;
  335. ::Window w;
  336. if (XQueryPointer(fModal.parent->xDisplay, fModal.parent->xWindow, &w, &w, &i, &i, &wx, &wy, &u) == True)
  337. fModal.parent->onPuglMotion(wx, wy);
  338. #endif
  339. }
  340. DBG("Ok\n");
  341. }
  342. // -------------------------------------------------------------------
  343. void focus()
  344. {
  345. DBG("Window focus\n");
  346. #if defined(DISTRHO_OS_WINDOWS)
  347. SetForegroundWindow(hwnd);
  348. SetActiveWindow(hwnd);
  349. SetFocus(hwnd);
  350. #elif defined(DISTRHO_OS_MAC)
  351. if (mWindow != nullptr)
  352. [mWindow makeKeyWindow];
  353. #else
  354. XRaiseWindow(xDisplay, xWindow);
  355. XSetInputFocus(xDisplay, xWindow, RevertToPointerRoot, CurrentTime);
  356. XFlush(xDisplay);
  357. #endif
  358. }
  359. // -------------------------------------------------------------------
  360. void setVisible(const bool yesNo)
  361. {
  362. if (fVisible == yesNo)
  363. {
  364. DBG("Window setVisible matches current state, ignoring request\n");
  365. return;
  366. }
  367. if (fUsingEmbed)
  368. {
  369. DBG("Window setVisible cannot be called when embedded\n");
  370. return;
  371. }
  372. DBG("Window setVisible called\n");
  373. fVisible = yesNo;
  374. if (yesNo && fFirstInit)
  375. setSize(fWidth, fHeight, true);
  376. #if defined(DISTRHO_OS_WINDOWS)
  377. if (yesNo)
  378. {
  379. if (fFirstInit)
  380. {
  381. RECT rectChild, rectParent;
  382. if (hwndParent != nullptr &&
  383. GetWindowRect(hwnd, &rectChild) &&
  384. GetWindowRect(hwndParent, &rectParent))
  385. {
  386. SetWindowPos(hwnd, hwndParent,
  387. rectParent.left + (rectChild.right-rectChild.left)/2,
  388. rectParent.top + (rectChild.bottom-rectChild.top)/2,
  389. 0, 0, SWP_SHOWWINDOW|SWP_NOSIZE);
  390. }
  391. else
  392. {
  393. ShowWindow(hwnd, SW_SHOWNORMAL);
  394. }
  395. }
  396. else
  397. {
  398. ShowWindow(hwnd, SW_RESTORE);
  399. }
  400. }
  401. else
  402. {
  403. ShowWindow(hwnd, SW_HIDE);
  404. }
  405. UpdateWindow(hwnd);
  406. #elif defined(DISTRHO_OS_MAC)
  407. if (yesNo)
  408. {
  409. if (mWindow != nullptr)
  410. {
  411. if (mParentWindow != nullptr)
  412. [mParentWindow addChildWindow:mWindow
  413. ordered:NSWindowAbove];
  414. [mWindow setIsVisible:YES];
  415. }
  416. else
  417. {
  418. [mView setHidden:NO];
  419. }
  420. }
  421. else
  422. {
  423. if (mWindow != nullptr)
  424. {
  425. if (mParentWindow != nullptr)
  426. [mParentWindow removeChildWindow:mWindow];
  427. [mWindow setIsVisible:NO];
  428. }
  429. else
  430. {
  431. [mView setHidden:YES];
  432. }
  433. }
  434. #else
  435. if (yesNo)
  436. XMapRaised(xDisplay, xWindow);
  437. else
  438. XUnmapWindow(xDisplay, xWindow);
  439. XFlush(xDisplay);
  440. #endif
  441. if (yesNo)
  442. {
  443. if (fFirstInit)
  444. {
  445. fApp.pData->oneShown();
  446. fFirstInit = false;
  447. }
  448. }
  449. else if (fModal.enabled)
  450. exec_fini();
  451. }
  452. // -------------------------------------------------------------------
  453. void setResizable(const bool yesNo)
  454. {
  455. if (fResizable == yesNo)
  456. {
  457. DBG("Window setResizable matches current state, ignoring request\n");
  458. return;
  459. }
  460. if (fUsingEmbed)
  461. {
  462. DBG("Window setResizable cannot be called when embedded\n");
  463. return;
  464. }
  465. DBG("Window setResizable called\n");
  466. fResizable = yesNo;
  467. #if defined(DISTRHO_OS_WINDOWS)
  468. const int winFlags = fResizable ? GetWindowLong(hwnd, GWL_STYLE) | WS_SIZEBOX
  469. : GetWindowLong(hwnd, GWL_STYLE) & ~WS_SIZEBOX;
  470. SetWindowLong(hwnd, GWL_STYLE, winFlags);
  471. #elif defined(DISTRHO_OS_MAC)
  472. const uint flags(yesNo ? (NSViewWidthSizable|NSViewHeightSizable) : 0x0);
  473. [mView setAutoresizingMask:flags];
  474. #endif
  475. setSize(fWidth, fHeight, true);
  476. }
  477. // -------------------------------------------------------------------
  478. void setSize(uint width, uint height, const bool forced = false)
  479. {
  480. if (width <= 1 || height <= 1)
  481. {
  482. DBGp("Window setSize called with invalid value(s) %i %i, ignoring request\n", width, height);
  483. return;
  484. }
  485. if (fWidth == width && fHeight == height && ! forced)
  486. {
  487. DBGp("Window setSize matches current size, ignoring request (%i %i)\n", width, height);
  488. return;
  489. }
  490. fWidth = width;
  491. fHeight = height;
  492. DBGp("Window setSize called %s, size %i %i, resizable %s\n", forced ? "(forced)" : "(not forced)", width, height, fResizable?"true":"false");
  493. #if defined(DISTRHO_OS_WINDOWS)
  494. const int winFlags = WS_POPUPWINDOW | WS_CAPTION | (fResizable ? WS_SIZEBOX : 0x0);
  495. RECT wr = { 0, 0, static_cast<LONG>(width), static_cast<LONG>(height) };
  496. AdjustWindowRectEx(&wr, fUsingEmbed ? WS_CHILD : winFlags, FALSE, WS_EX_TOPMOST);
  497. SetWindowPos(hwnd, 0, 0, 0, wr.right-wr.left, wr.bottom-wr.top,
  498. SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOOWNERZORDER|SWP_NOZORDER);
  499. if (! forced)
  500. UpdateWindow(hwnd);
  501. #elif defined(DISTRHO_OS_MAC)
  502. [mView setFrame:NSMakeRect(0, 0, width, height)];
  503. if (mWindow != nullptr)
  504. {
  505. const NSSize size = NSMakeSize(width, height);
  506. [mWindow setContentSize:size];
  507. if (fResizable)
  508. {
  509. [mWindow setContentMinSize:NSMakeSize(1, 1)];
  510. [mWindow setContentMaxSize:NSMakeSize(99999, 99999)];
  511. [[mWindow standardWindowButton:NSWindowZoomButton] setHidden:NO];
  512. }
  513. else
  514. {
  515. [mWindow setContentMinSize:size];
  516. [mWindow setContentMaxSize:size];
  517. [[mWindow standardWindowButton:NSWindowZoomButton] setHidden:YES];
  518. }
  519. }
  520. #else
  521. XResizeWindow(xDisplay, xWindow, width, height);
  522. if (! fResizable)
  523. {
  524. XSizeHints sizeHints;
  525. memset(&sizeHints, 0, sizeof(sizeHints));
  526. sizeHints.flags = PSize|PMinSize|PMaxSize;
  527. sizeHints.width = static_cast<int>(width);
  528. sizeHints.height = static_cast<int>(height);
  529. sizeHints.min_width = static_cast<int>(width);
  530. sizeHints.min_height = static_cast<int>(height);
  531. sizeHints.max_width = static_cast<int>(width);
  532. sizeHints.max_height = static_cast<int>(height);
  533. XSetNormalHints(xDisplay, xWindow, &sizeHints);
  534. }
  535. if (! forced)
  536. XFlush(xDisplay);
  537. #endif
  538. puglPostRedisplay(fView);
  539. }
  540. // -------------------------------------------------------------------
  541. const char* getTitle() const noexcept
  542. {
  543. static const char* const kFallback = "";
  544. return fTitle != nullptr ? fTitle : kFallback;
  545. }
  546. void setTitle(const char* const title)
  547. {
  548. DBGp("Window setTitle \"%s\"\n", title);
  549. if (fTitle != nullptr)
  550. std::free(fTitle);
  551. fTitle = strdup(title);
  552. #if defined(DISTRHO_OS_WINDOWS)
  553. SetWindowTextA(hwnd, title);
  554. #elif defined(DISTRHO_OS_MAC)
  555. if (mWindow != nullptr)
  556. {
  557. NSString* titleString = [[NSString alloc]
  558. initWithBytes:title
  559. length:strlen(title)
  560. encoding:NSUTF8StringEncoding];
  561. [mWindow setTitle:titleString];
  562. }
  563. #else
  564. XStoreName(xDisplay, xWindow, title);
  565. #endif
  566. }
  567. void setTransientWinId(const uintptr_t winId)
  568. {
  569. DISTRHO_SAFE_ASSERT_RETURN(winId != 0,);
  570. #if defined(DISTRHO_OS_WINDOWS)
  571. hwndParent = (HWND)winId;
  572. SetWindowLongPtr(hwnd, GWLP_HWNDPARENT, (LONG_PTR)winId);
  573. #elif defined(DISTRHO_OS_MAC)
  574. NSWindow* const parentWindow = [NSApp windowWithWindowNumber:winId];
  575. DISTRHO_SAFE_ASSERT_RETURN(parentWindow != nullptr,);
  576. [parentWindow addChildWindow:mWindow
  577. ordered:NSWindowAbove];
  578. #else
  579. XSetTransientForHint(xDisplay, xWindow, static_cast< ::Window>(winId));
  580. #endif
  581. }
  582. // -------------------------------------------------------------------
  583. void addWidget(Widget* const widget)
  584. {
  585. fWidgets.push_back(widget);
  586. }
  587. void removeWidget(Widget* const widget)
  588. {
  589. fWidgets.remove(widget);
  590. }
  591. void idle()
  592. {
  593. puglProcessEvents(fView);
  594. #ifdef DISTRHO_OS_MAC
  595. if (fNeedsIdle)
  596. {
  597. NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
  598. NSEvent* event;
  599. for (;;)
  600. {
  601. event = [NSApp
  602. nextEventMatchingMask:NSAnyEventMask
  603. untilDate:[NSDate distantPast]
  604. inMode:NSDefaultRunLoopMode
  605. dequeue:YES];
  606. if (event == nil)
  607. break;
  608. [NSApp sendEvent: event];
  609. }
  610. [pool release];
  611. }
  612. #endif
  613. if (fModal.enabled && fModal.parent != nullptr)
  614. fModal.parent->idle();
  615. }
  616. // -------------------------------------------------------------------
  617. void onPuglDisplay()
  618. {
  619. fSelf->onDisplayBefore();
  620. FOR_EACH_WIDGET(it)
  621. {
  622. Widget* const widget(*it);
  623. widget->pData->display(fWidth, fHeight, false);
  624. }
  625. fSelf->onDisplayAfter();
  626. }
  627. int onPuglKeyboard(const bool press, const uint key)
  628. {
  629. DBGp("PUGL: onKeyboard : %i %i\n", press, key);
  630. if (fModal.childFocus != nullptr)
  631. {
  632. fModal.childFocus->focus();
  633. return 0;
  634. }
  635. Widget::KeyboardEvent ev;
  636. ev.press = press;
  637. ev.key = key;
  638. ev.mod = static_cast<Modifier>(puglGetModifiers(fView));
  639. ev.time = puglGetEventTimestamp(fView);
  640. FOR_EACH_WIDGET_INV(rit)
  641. {
  642. Widget* const widget(*rit);
  643. if (widget->isVisible() && widget->onKeyboard(ev))
  644. return 0;
  645. }
  646. return 1;
  647. }
  648. int onPuglSpecial(const bool press, const Key key)
  649. {
  650. DBGp("PUGL: onSpecial : %i %i\n", press, key);
  651. if (fModal.childFocus != nullptr)
  652. {
  653. fModal.childFocus->focus();
  654. return 0;
  655. }
  656. Widget::SpecialEvent ev;
  657. ev.press = press;
  658. ev.key = key;
  659. ev.mod = static_cast<Modifier>(puglGetModifiers(fView));
  660. ev.time = puglGetEventTimestamp(fView);
  661. FOR_EACH_WIDGET_INV(rit)
  662. {
  663. Widget* const widget(*rit);
  664. if (widget->isVisible() && widget->onSpecial(ev))
  665. return 0;
  666. }
  667. return 1;
  668. }
  669. void onPuglMouse(const int button, const bool press, const int x, const int y)
  670. {
  671. DBGp("PUGL: onMouse : %i %i %i %i\n", button, press, x, y);
  672. // FIXME - pugl sends 2 of these for each window on init, don't ask me why. we'll ignore it
  673. if (press && button == 0 && x == 0 && y == 0) return;
  674. if (fModal.childFocus != nullptr)
  675. return fModal.childFocus->focus();
  676. Widget::MouseEvent ev;
  677. ev.button = button;
  678. ev.press = press;
  679. ev.mod = static_cast<Modifier>(puglGetModifiers(fView));
  680. ev.time = puglGetEventTimestamp(fView);
  681. FOR_EACH_WIDGET_INV(rit)
  682. {
  683. Widget* const widget(*rit);
  684. ev.pos = Point<int>(x-widget->getAbsoluteX(), y-widget->getAbsoluteY());
  685. if (widget->isVisible() && widget->onMouse(ev))
  686. break;
  687. }
  688. }
  689. void onPuglMotion(const int x, const int y)
  690. {
  691. DBGp("PUGL: onMotion : %i %i\n", x, y);
  692. if (fModal.childFocus != nullptr)
  693. return;
  694. Widget::MotionEvent ev;
  695. ev.mod = static_cast<Modifier>(puglGetModifiers(fView));
  696. ev.time = puglGetEventTimestamp(fView);
  697. FOR_EACH_WIDGET_INV(rit)
  698. {
  699. Widget* const widget(*rit);
  700. ev.pos = Point<int>(x-widget->getAbsoluteX(), y-widget->getAbsoluteY());
  701. if (widget->isVisible() && widget->onMotion(ev))
  702. break;
  703. }
  704. }
  705. void onPuglScroll(const int x, const int y, const float dx, const float dy)
  706. {
  707. DBGp("PUGL: onScroll : %i %i %f %f\n", x, y, dx, dy);
  708. if (fModal.childFocus != nullptr)
  709. return;
  710. Widget::ScrollEvent ev;
  711. ev.delta = Point<float>(dx, dy);
  712. ev.mod = static_cast<Modifier>(puglGetModifiers(fView));
  713. ev.time = puglGetEventTimestamp(fView);
  714. FOR_EACH_WIDGET_INV(rit)
  715. {
  716. Widget* const widget(*rit);
  717. ev.pos = Point<int>(x-widget->getAbsoluteX(), y-widget->getAbsoluteY());
  718. if (widget->isVisible() && widget->onScroll(ev))
  719. break;
  720. }
  721. }
  722. void onPuglReshape(const int width, const int height)
  723. {
  724. DBGp("PUGL: onReshape : %i %i\n", width, height);
  725. if (width <= 1 && height <= 1)
  726. return;
  727. fWidth = static_cast<uint>(width);
  728. fHeight = static_cast<uint>(height);
  729. fSelf->onReshape(fWidth, fHeight);
  730. FOR_EACH_WIDGET(it)
  731. {
  732. Widget* const widget(*it);
  733. if (widget->pData->needsFullViewport)
  734. widget->setSize(fWidth, fHeight);
  735. }
  736. }
  737. void onPuglClose()
  738. {
  739. DBG("PUGL: onClose\n");
  740. if (fModal.enabled)
  741. exec_fini();
  742. fSelf->onClose();
  743. if (fModal.childFocus != nullptr)
  744. fModal.childFocus->fSelf->onClose();
  745. close();
  746. }
  747. // -------------------------------------------------------------------
  748. bool handlePluginKeyboard(const bool press, const uint key)
  749. {
  750. DBGp("PUGL: handlePluginKeyboard : %i %i\n", press, key);
  751. if (fModal.childFocus != nullptr)
  752. {
  753. fModal.childFocus->focus();
  754. return true;
  755. }
  756. Widget::KeyboardEvent ev;
  757. ev.press = press;
  758. ev.key = key;
  759. ev.mod = static_cast<Modifier>(fView->mods);
  760. ev.time = 0;
  761. if ((ev.mod & kModifierShift) != 0 && ev.key >= 'a' && ev.key <= 'z')
  762. ev.key -= 'a' - 'A'; // a-z -> A-Z
  763. FOR_EACH_WIDGET_INV(rit)
  764. {
  765. Widget* const widget(*rit);
  766. if (widget->isVisible() && widget->onKeyboard(ev))
  767. return true;
  768. }
  769. return false;
  770. }
  771. bool handlePluginSpecial(const bool press, const Key key)
  772. {
  773. DBGp("PUGL: handlePluginSpecial : %i %i\n", press, key);
  774. if (fModal.childFocus != nullptr)
  775. {
  776. fModal.childFocus->focus();
  777. return true;
  778. }
  779. int mods = 0x0;
  780. switch (key)
  781. {
  782. case kKeyShift:
  783. mods |= kModifierShift;
  784. break;
  785. case kKeyControl:
  786. mods |= kModifierControl;
  787. break;
  788. case kKeyAlt:
  789. mods |= kModifierAlt;
  790. break;
  791. default:
  792. break;
  793. }
  794. if (mods != 0x0)
  795. {
  796. if (press)
  797. fView->mods |= mods;
  798. else
  799. fView->mods &= ~(mods);
  800. }
  801. Widget::SpecialEvent ev;
  802. ev.press = press;
  803. ev.key = key;
  804. ev.mod = static_cast<Modifier>(fView->mods);
  805. ev.time = 0;
  806. FOR_EACH_WIDGET_INV(rit)
  807. {
  808. Widget* const widget(*rit);
  809. if (widget->isVisible() && widget->onSpecial(ev))
  810. return true;
  811. }
  812. return false;
  813. }
  814. // -------------------------------------------------------------------
  815. Application& fApp;
  816. Window* fSelf;
  817. PuglView* fView;
  818. bool fFirstInit;
  819. bool fVisible;
  820. bool fResizable;
  821. bool fUsingEmbed;
  822. uint fWidth;
  823. uint fHeight;
  824. char* fTitle;
  825. std::list<Widget*> fWidgets;
  826. struct Modal {
  827. bool enabled;
  828. PrivateData* parent;
  829. PrivateData* childFocus;
  830. Modal()
  831. : enabled(false),
  832. parent(nullptr),
  833. childFocus(nullptr) {}
  834. Modal(PrivateData* const p)
  835. : enabled(false),
  836. parent(p),
  837. childFocus(nullptr) {}
  838. ~Modal()
  839. {
  840. DISTRHO_SAFE_ASSERT(! enabled);
  841. DISTRHO_SAFE_ASSERT(childFocus == nullptr);
  842. }
  843. DISTRHO_DECLARE_NON_COPY_STRUCT(Modal)
  844. } fModal;
  845. #if defined(DISTRHO_OS_WINDOWS)
  846. HWND hwnd;
  847. HWND hwndParent;
  848. #elif defined(DISTRHO_OS_MAC)
  849. bool fNeedsIdle;
  850. PuglOpenGLView* mView;
  851. id mWindow;
  852. id mParentWindow;
  853. #else
  854. Display* xDisplay;
  855. ::Window xWindow;
  856. #endif
  857. // -------------------------------------------------------------------
  858. // Callbacks
  859. #define handlePtr ((PrivateData*)puglGetHandle(view))
  860. static void onDisplayCallback(PuglView* view)
  861. {
  862. handlePtr->onPuglDisplay();
  863. }
  864. static int onKeyboardCallback(PuglView* view, bool press, uint32_t key)
  865. {
  866. return handlePtr->onPuglKeyboard(press, key);
  867. }
  868. static int onSpecialCallback(PuglView* view, bool press, PuglKey key)
  869. {
  870. return handlePtr->onPuglSpecial(press, static_cast<Key>(key));
  871. }
  872. static void onMouseCallback(PuglView* view, int button, bool press, int x, int y)
  873. {
  874. handlePtr->onPuglMouse(button, press, x, y);
  875. }
  876. static void onMotionCallback(PuglView* view, int x, int y)
  877. {
  878. handlePtr->onPuglMotion(x, y);
  879. }
  880. static void onScrollCallback(PuglView* view, int x, int y, float dx, float dy)
  881. {
  882. handlePtr->onPuglScroll(x, y, dx, dy);
  883. }
  884. static void onReshapeCallback(PuglView* view, int width, int height)
  885. {
  886. handlePtr->onPuglReshape(width, height);
  887. }
  888. static void onCloseCallback(PuglView* view)
  889. {
  890. handlePtr->onPuglClose();
  891. }
  892. #ifndef DGL_FILE_BROWSER_DISABLED
  893. static void fileBrowserSelectedCallback(PuglView* view, const char* filename)
  894. {
  895. handlePtr->fSelf->fileBrowserSelected(filename);
  896. }
  897. #endif
  898. #undef handlePtr
  899. DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PrivateData)
  900. };
  901. // -----------------------------------------------------------------------
  902. // Window
  903. Window::Window(Application& app)
  904. : pData(new PrivateData(app, this)) {}
  905. Window::Window(Application& app, Window& parent)
  906. : pData(new PrivateData(app, this, parent)) {}
  907. Window::Window(Application& app, intptr_t parentId)
  908. : pData(new PrivateData(app, this, parentId)) {}
  909. Window::~Window()
  910. {
  911. delete pData;
  912. }
  913. void Window::show()
  914. {
  915. pData->setVisible(true);
  916. }
  917. void Window::hide()
  918. {
  919. pData->setVisible(false);
  920. }
  921. void Window::close()
  922. {
  923. pData->close();
  924. }
  925. void Window::exec(bool lockWait)
  926. {
  927. pData->exec(lockWait);
  928. }
  929. void Window::focus()
  930. {
  931. pData->focus();
  932. }
  933. void Window::repaint() noexcept
  934. {
  935. puglPostRedisplay(pData->fView);
  936. }
  937. // static int fib_filter_filename_filter(const char* const name)
  938. // {
  939. // return 1;
  940. // (void)name;
  941. // }
  942. #ifndef DGL_FILE_BROWSER_DISABLED
  943. bool Window::openFileBrowser(const FileBrowserOptions& options)
  944. {
  945. # ifdef SOFD_HAVE_X11
  946. using DISTRHO_NAMESPACE::String;
  947. // --------------------------------------------------------------------------
  948. // configure start dir
  949. // TODO: get abspath if needed
  950. // TODO: cross-platform
  951. String startDir(options.startDir);
  952. if (startDir.isEmpty())
  953. {
  954. if (char* const dir_name = get_current_dir_name())
  955. {
  956. startDir = dir_name;
  957. std::free(dir_name);
  958. }
  959. }
  960. DISTRHO_SAFE_ASSERT_RETURN(startDir.isNotEmpty(), false);
  961. if (! startDir.endsWith('/'))
  962. startDir += "/";
  963. DISTRHO_SAFE_ASSERT_RETURN(x_fib_configure(0, startDir) == 0, false);
  964. // --------------------------------------------------------------------------
  965. // configure title
  966. String title(options.title);
  967. if (title.isEmpty())
  968. {
  969. title = pData->getTitle();
  970. if (title.isEmpty())
  971. title = "FileBrowser";
  972. }
  973. DISTRHO_SAFE_ASSERT_RETURN(x_fib_configure(1, title) == 0, false);
  974. // --------------------------------------------------------------------------
  975. // configure filters
  976. x_fib_cfg_filter_callback(nullptr); //fib_filter_filename_filter);
  977. // --------------------------------------------------------------------------
  978. // configure buttons
  979. x_fib_cfg_buttons(3, options.buttons.listAllFiles-1);
  980. x_fib_cfg_buttons(1, options.buttons.showHidden-1);
  981. x_fib_cfg_buttons(2, options.buttons.showPlaces-1);
  982. // --------------------------------------------------------------------------
  983. // show
  984. return (x_fib_show(pData->xDisplay, pData->xWindow, /*options.width*/0, /*options.height*/0) == 0);
  985. # else
  986. // not implemented
  987. return false;
  988. // unused
  989. (void)options;
  990. # endif
  991. }
  992. #endif
  993. bool Window::isVisible() const noexcept
  994. {
  995. return pData->fVisible;
  996. }
  997. void Window::setVisible(bool yesNo)
  998. {
  999. pData->setVisible(yesNo);
  1000. }
  1001. bool Window::isResizable() const noexcept
  1002. {
  1003. return pData->fResizable;
  1004. }
  1005. void Window::setResizable(bool yesNo)
  1006. {
  1007. pData->setResizable(yesNo);
  1008. }
  1009. uint Window::getWidth() const noexcept
  1010. {
  1011. return pData->fWidth;
  1012. }
  1013. uint Window::getHeight() const noexcept
  1014. {
  1015. return pData->fHeight;
  1016. }
  1017. Size<uint> Window::getSize() const noexcept
  1018. {
  1019. return Size<uint>(pData->fWidth, pData->fHeight);
  1020. }
  1021. void Window::setSize(uint width, uint height)
  1022. {
  1023. pData->setSize(width, height);
  1024. }
  1025. void Window::setSize(Size<uint> size)
  1026. {
  1027. pData->setSize(size.getWidth(), size.getHeight());
  1028. }
  1029. const char* Window::getTitle() const noexcept
  1030. {
  1031. return pData->getTitle();
  1032. }
  1033. void Window::setTitle(const char* title)
  1034. {
  1035. pData->setTitle(title);
  1036. }
  1037. void Window::setTransientWinId(uintptr_t winId)
  1038. {
  1039. pData->setTransientWinId(winId);
  1040. }
  1041. Application& Window::getApp() const noexcept
  1042. {
  1043. return pData->fApp;
  1044. }
  1045. intptr_t Window::getWindowId() const noexcept
  1046. {
  1047. return puglGetNativeWindow(pData->fView);
  1048. }
  1049. void Window::_addWidget(Widget* const widget)
  1050. {
  1051. pData->addWidget(widget);
  1052. }
  1053. void Window::_removeWidget(Widget* const widget)
  1054. {
  1055. pData->removeWidget(widget);
  1056. }
  1057. void Window::_idle()
  1058. {
  1059. pData->idle();
  1060. }
  1061. // -----------------------------------------------------------------------
  1062. void Window::addIdleCallback(IdleCallback* const callback)
  1063. {
  1064. DISTRHO_SAFE_ASSERT_RETURN(callback != nullptr,)
  1065. pData->fApp.pData->idleCallbacks.push_back(callback);
  1066. }
  1067. void Window::removeIdleCallback(IdleCallback* const callback)
  1068. {
  1069. DISTRHO_SAFE_ASSERT_RETURN(callback != nullptr,)
  1070. pData->fApp.pData->idleCallbacks.remove(callback);
  1071. }
  1072. // -----------------------------------------------------------------------
  1073. void Window::onDisplayBefore()
  1074. {
  1075. glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  1076. glLoadIdentity();
  1077. }
  1078. void Window::onDisplayAfter()
  1079. {
  1080. }
  1081. void Window::onReshape(uint width, uint height)
  1082. {
  1083. glEnable(GL_BLEND);
  1084. glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  1085. glMatrixMode(GL_PROJECTION);
  1086. glLoadIdentity();
  1087. glOrtho(0.0, static_cast<GLdouble>(width), static_cast<GLdouble>(height), 0.0, 0.0, 1.0);
  1088. glViewport(0, 0, static_cast<GLsizei>(width), static_cast<GLsizei>(height));
  1089. glMatrixMode(GL_MODELVIEW);
  1090. glLoadIdentity();
  1091. }
  1092. void Window::onClose()
  1093. {
  1094. }
  1095. #ifndef DGL_FILE_BROWSER_DISABLED
  1096. void Window::fileBrowserSelected(const char*)
  1097. {
  1098. }
  1099. #endif
  1100. bool Window::handlePluginKeyboard(const bool press, const uint key)
  1101. {
  1102. return pData->handlePluginKeyboard(press, key);
  1103. }
  1104. bool Window::handlePluginSpecial(const bool press, const Key key)
  1105. {
  1106. return pData->handlePluginSpecial(press, key);
  1107. }
  1108. // -----------------------------------------------------------------------
  1109. StandaloneWindow::StandaloneWindow()
  1110. : Application(),
  1111. Window((Application&)*this),
  1112. fWidget(nullptr) {}
  1113. void StandaloneWindow::exec()
  1114. {
  1115. Window::show();
  1116. Application::exec();
  1117. }
  1118. void StandaloneWindow::onReshape(uint width, uint height)
  1119. {
  1120. if (fWidget != nullptr)
  1121. fWidget->setSize(width, height);
  1122. Window::onReshape(width, height);
  1123. }
  1124. void StandaloneWindow::_addWidget(Widget* widget)
  1125. {
  1126. if (fWidget == nullptr)
  1127. {
  1128. fWidget = widget;
  1129. fWidget->pData->needsFullViewport = true;
  1130. }
  1131. Window::_addWidget(widget);
  1132. }
  1133. void StandaloneWindow::_removeWidget(Widget* widget)
  1134. {
  1135. if (fWidget == widget)
  1136. {
  1137. fWidget->pData->needsFullViewport = false;
  1138. fWidget = nullptr;
  1139. }
  1140. Window::_removeWidget(widget);
  1141. }
  1142. // -----------------------------------------------------------------------
  1143. END_NAMESPACE_DGL
  1144. #undef DBG
  1145. #undef DBGF