Audio plugin host https://kx.studio/carla
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.

1268 lines
31KB

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