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.

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