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.

1366 lines
33KB

  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. #elif defined(DISTRHO_OS_MAC)
  30. # include "pugl/pugl_osx.m"
  31. #else
  32. # include <sys/types.h>
  33. # include <unistd.h>
  34. extern "C" {
  35. # include "pugl/pugl_x11.c"
  36. }
  37. #endif
  38. #if defined(__GNUC__) && (__GNUC__ >= 7)
  39. # pragma GCC diagnostic pop
  40. #endif
  41. #include "ApplicationPrivateData.hpp"
  42. #include "WidgetPrivateData.hpp"
  43. #include "../StandaloneWindow.hpp"
  44. #include "../../distrho/extra/String.hpp"
  45. #define FOR_EACH_WIDGET(it) \
  46. for (std::list<Widget*>::iterator it = fWidgets.begin(); it != fWidgets.end(); ++it)
  47. #define FOR_EACH_WIDGET_INV(rit) \
  48. for (std::list<Widget*>::reverse_iterator rit = fWidgets.rbegin(); rit != fWidgets.rend(); ++rit)
  49. #ifdef DEBUG
  50. # define DBG(msg) std::fprintf(stderr, "%s", msg);
  51. # define DBGp(...) std::fprintf(stderr, __VA_ARGS__);
  52. # define DBGF std::fflush(stderr);
  53. #else
  54. # define DBG(msg)
  55. # define DBGp(...)
  56. # define DBGF
  57. #endif
  58. START_NAMESPACE_DGL
  59. // -----------------------------------------------------------------------
  60. // Window Private
  61. struct Window::PrivateData {
  62. PrivateData(Application& app, Window* const self)
  63. : fApp(app),
  64. fSelf(self),
  65. fView(puglInit()),
  66. fFirstInit(true),
  67. fVisible(false),
  68. fResizable(true),
  69. fUsingEmbed(false),
  70. fWidth(1),
  71. fHeight(1),
  72. fTitle(nullptr),
  73. fWidgets(),
  74. fModal(),
  75. #if defined(DISTRHO_OS_WINDOWS)
  76. hwnd(0)
  77. #elif defined(DISTRHO_OS_MAC)
  78. fNeedsIdle(true),
  79. mView(nullptr),
  80. mWindow(nullptr)
  81. #else
  82. xDisplay(nullptr),
  83. xWindow(0)
  84. #endif
  85. {
  86. DBG("Creating window without parent..."); DBGF;
  87. init();
  88. }
  89. PrivateData(Application& app, Window* const self, Window& parent)
  90. : fApp(app),
  91. fSelf(self),
  92. fView(puglInit()),
  93. fFirstInit(true),
  94. fVisible(false),
  95. fResizable(true),
  96. fUsingEmbed(false),
  97. fWidth(1),
  98. fHeight(1),
  99. fTitle(nullptr),
  100. fWidgets(),
  101. fModal(parent.pData),
  102. #if defined(DISTRHO_OS_WINDOWS)
  103. hwnd(0)
  104. #elif defined(DISTRHO_OS_MAC)
  105. fNeedsIdle(false),
  106. mView(nullptr),
  107. mWindow(nullptr)
  108. #else
  109. xDisplay(nullptr),
  110. xWindow(0)
  111. #endif
  112. {
  113. DBG("Creating window with parent..."); DBGF;
  114. init();
  115. const PuglInternals* const parentImpl(parent.pData->fView->impl);
  116. #if defined(DISTRHO_OS_WINDOWS)
  117. // TODO
  118. #elif defined(DISTRHO_OS_MAC)
  119. [parentImpl->window orderWindow:NSWindowBelow relativeTo:[[mView window] windowNumber]];
  120. #else
  121. XSetTransientForHint(xDisplay, xWindow, parentImpl->win);
  122. #endif
  123. return;
  124. // maybe unused
  125. (void)parentImpl;
  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_MAC)
  143. fNeedsIdle(parentId == 0),
  144. mView(nullptr),
  145. mWindow(nullptr)
  146. #else
  147. xDisplay(nullptr),
  148. xWindow(0)
  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. #else
  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. #else
  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. #else
  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. [mWindow makeKeyWindow];
  348. #else
  349. XRaiseWindow(xDisplay, xWindow);
  350. XSetInputFocus(xDisplay, xWindow, RevertToPointerRoot, CurrentTime);
  351. XFlush(xDisplay);
  352. #endif
  353. }
  354. // -------------------------------------------------------------------
  355. void setVisible(const bool yesNo)
  356. {
  357. if (fVisible == yesNo)
  358. {
  359. DBG("Window setVisible matches current state, ignoring request\n");
  360. return;
  361. }
  362. if (fUsingEmbed)
  363. {
  364. DBG("Window setVisible cannot be called when embedded\n");
  365. return;
  366. }
  367. DBG("Window setVisible called\n");
  368. fVisible = yesNo;
  369. if (yesNo && fFirstInit)
  370. setSize(fWidth, fHeight, true);
  371. #if defined(DISTRHO_OS_WINDOWS)
  372. if (yesNo)
  373. ShowWindow(hwnd, fFirstInit ? SW_SHOWNORMAL : SW_RESTORE);
  374. else
  375. ShowWindow(hwnd, SW_HIDE);
  376. UpdateWindow(hwnd);
  377. #elif defined(DISTRHO_OS_MAC)
  378. if (yesNo)
  379. {
  380. if (mWindow != nullptr)
  381. [mWindow setIsVisible:YES];
  382. else
  383. [mView setHidden:NO];
  384. }
  385. else
  386. {
  387. if (mWindow != nullptr)
  388. [mWindow setIsVisible:NO];
  389. else
  390. [mView setHidden:YES];
  391. }
  392. #else
  393. if (yesNo)
  394. XMapRaised(xDisplay, xWindow);
  395. else
  396. XUnmapWindow(xDisplay, xWindow);
  397. XFlush(xDisplay);
  398. #endif
  399. if (yesNo)
  400. {
  401. if (fFirstInit)
  402. {
  403. fApp.pData->oneShown();
  404. fFirstInit = false;
  405. }
  406. }
  407. else if (fModal.enabled)
  408. exec_fini();
  409. }
  410. // -------------------------------------------------------------------
  411. void setResizable(const bool yesNo)
  412. {
  413. if (fResizable == yesNo)
  414. {
  415. DBG("Window setResizable matches current state, ignoring request\n");
  416. return;
  417. }
  418. if (fUsingEmbed)
  419. {
  420. DBG("Window setResizable cannot be called when embedded\n");
  421. return;
  422. }
  423. DBG("Window setResizable called\n");
  424. fResizable = yesNo;
  425. #if defined(DISTRHO_OS_WINDOWS)
  426. const int winFlags = fResizable ? GetWindowLong(hwnd, GWL_STYLE) | WS_SIZEBOX
  427. : GetWindowLong(hwnd, GWL_STYLE) & ~WS_SIZEBOX;
  428. SetWindowLong(hwnd, GWL_STYLE, winFlags);
  429. #elif defined(DISTRHO_OS_MAC)
  430. const uint flags(yesNo ? (NSViewWidthSizable|NSViewHeightSizable) : 0x0);
  431. [mView setAutoresizingMask:flags];
  432. #endif
  433. setSize(fWidth, fHeight, true);
  434. }
  435. // -------------------------------------------------------------------
  436. void setSize(uint width, uint height, const bool forced = false)
  437. {
  438. if (width <= 1 || height <= 1)
  439. {
  440. DBGp("Window setSize called with invalid value(s) %i %i, ignoring request\n", width, height);
  441. return;
  442. }
  443. if (fWidth == width && fHeight == height && ! forced)
  444. {
  445. DBGp("Window setSize matches current size, ignoring request (%i %i)\n", width, height);
  446. return;
  447. }
  448. fWidth = width;
  449. fHeight = height;
  450. DBGp("Window setSize called %s, size %i %i, resizable %s\n", forced ? "(forced)" : "(not forced)", width, height, fResizable?"true":"false");
  451. #if defined(DISTRHO_OS_WINDOWS)
  452. const int winFlags = WS_POPUPWINDOW | WS_CAPTION | (fResizable ? WS_SIZEBOX : 0x0);
  453. RECT wr = { 0, 0, static_cast<long>(width), static_cast<long>(height) };
  454. AdjustWindowRectEx(&wr, fUsingEmbed ? WS_CHILD : winFlags, FALSE, WS_EX_TOPMOST);
  455. SetWindowPos(hwnd, 0, 0, 0, wr.right-wr.left, wr.bottom-wr.top,
  456. SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOOWNERZORDER|SWP_NOZORDER);
  457. if (! forced)
  458. UpdateWindow(hwnd);
  459. #elif defined(DISTRHO_OS_MAC)
  460. [mView setFrame:NSMakeRect(0, 0, width, height)];
  461. if (mWindow != nullptr)
  462. {
  463. const NSSize size = NSMakeSize(width, height);
  464. [mWindow setContentSize:size];
  465. if (fResizable)
  466. {
  467. [mWindow setContentMinSize:NSMakeSize(1, 1)];
  468. [mWindow setContentMaxSize:NSMakeSize(99999, 99999)];
  469. [[mWindow standardWindowButton:NSWindowZoomButton] setHidden:NO];
  470. }
  471. else
  472. {
  473. [mWindow setContentMinSize:size];
  474. [mWindow setContentMaxSize:size];
  475. [[mWindow standardWindowButton:NSWindowZoomButton] setHidden:YES];
  476. }
  477. }
  478. #else
  479. XResizeWindow(xDisplay, xWindow, width, height);
  480. if (! fResizable)
  481. {
  482. XSizeHints sizeHints;
  483. memset(&sizeHints, 0, sizeof(sizeHints));
  484. sizeHints.flags = PSize|PMinSize|PMaxSize;
  485. sizeHints.width = static_cast<int>(width);
  486. sizeHints.height = static_cast<int>(height);
  487. sizeHints.min_width = static_cast<int>(width);
  488. sizeHints.min_height = static_cast<int>(height);
  489. sizeHints.max_width = static_cast<int>(width);
  490. sizeHints.max_height = static_cast<int>(height);
  491. XSetNormalHints(xDisplay, xWindow, &sizeHints);
  492. }
  493. if (! forced)
  494. XFlush(xDisplay);
  495. #endif
  496. puglPostRedisplay(fView);
  497. }
  498. // -------------------------------------------------------------------
  499. const char* getTitle() const noexcept
  500. {
  501. static const char* const kFallback = "";
  502. return fTitle != nullptr ? fTitle : kFallback;
  503. }
  504. void setTitle(const char* const title)
  505. {
  506. DBGp("Window setTitle \"%s\"\n", title);
  507. if (fTitle != nullptr)
  508. std::free(fTitle);
  509. fTitle = strdup(title);
  510. #if defined(DISTRHO_OS_WINDOWS)
  511. SetWindowTextA(hwnd, title);
  512. #elif defined(DISTRHO_OS_MAC)
  513. if (mWindow != nullptr)
  514. {
  515. NSString* titleString = [[NSString alloc]
  516. initWithBytes:title
  517. length:strlen(title)
  518. encoding:NSUTF8StringEncoding];
  519. [mWindow setTitle:titleString];
  520. }
  521. #else
  522. XStoreName(xDisplay, xWindow, title);
  523. #endif
  524. }
  525. void setTransientWinId(const uintptr_t winId)
  526. {
  527. DISTRHO_SAFE_ASSERT_RETURN(winId != 0,);
  528. #if defined(DISTRHO_OS_WINDOWS)
  529. // TODO
  530. #elif defined(DISTRHO_OS_MAC)
  531. NSWindow* const window = [NSApp windowWithWindowNumber:winId];
  532. DISTRHO_SAFE_ASSERT_RETURN(window != nullptr,);
  533. [window addChildWindow:mWindow
  534. ordered:NSWindowAbove];
  535. [mWindow makeKeyWindow];
  536. #else
  537. XSetTransientForHint(xDisplay, xWindow, static_cast< ::Window>(winId));
  538. #endif
  539. }
  540. // -------------------------------------------------------------------
  541. void addWidget(Widget* const widget)
  542. {
  543. fWidgets.push_back(widget);
  544. }
  545. void removeWidget(Widget* const widget)
  546. {
  547. fWidgets.remove(widget);
  548. }
  549. void idle()
  550. {
  551. puglProcessEvents(fView);
  552. #ifdef DISTRHO_OS_MAC
  553. if (fNeedsIdle)
  554. {
  555. NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
  556. NSEvent* event;
  557. for (;;)
  558. {
  559. event = [NSApp
  560. nextEventMatchingMask:NSAnyEventMask
  561. untilDate:[NSDate distantPast]
  562. inMode:NSDefaultRunLoopMode
  563. dequeue:YES];
  564. if (event == nil)
  565. break;
  566. [NSApp sendEvent: event];
  567. }
  568. [pool release];
  569. }
  570. #endif
  571. if (fModal.enabled && fModal.parent != nullptr)
  572. fModal.parent->idle();
  573. }
  574. // -------------------------------------------------------------------
  575. void onPuglDisplay()
  576. {
  577. fSelf->onDisplayBefore();
  578. FOR_EACH_WIDGET(it)
  579. {
  580. Widget* const widget(*it);
  581. widget->pData->display(fWidth, fHeight);
  582. }
  583. fSelf->onDisplayAfter();
  584. }
  585. int onPuglKeyboard(const bool press, const uint key)
  586. {
  587. DBGp("PUGL: onKeyboard : %i %i\n", press, key);
  588. if (fModal.childFocus != nullptr)
  589. {
  590. fModal.childFocus->focus();
  591. return 0;
  592. }
  593. Widget::KeyboardEvent ev;
  594. ev.press = press;
  595. ev.key = key;
  596. ev.mod = static_cast<Modifier>(puglGetModifiers(fView));
  597. ev.time = puglGetEventTimestamp(fView);
  598. FOR_EACH_WIDGET_INV(rit)
  599. {
  600. Widget* const widget(*rit);
  601. if (widget->isVisible() && widget->onKeyboard(ev))
  602. return 0;
  603. }
  604. return 1;
  605. }
  606. int onPuglSpecial(const bool press, const Key key)
  607. {
  608. DBGp("PUGL: onSpecial : %i %i\n", press, key);
  609. if (fModal.childFocus != nullptr)
  610. {
  611. fModal.childFocus->focus();
  612. return 0;
  613. }
  614. Widget::SpecialEvent ev;
  615. ev.press = press;
  616. ev.key = key;
  617. ev.mod = static_cast<Modifier>(puglGetModifiers(fView));
  618. ev.time = puglGetEventTimestamp(fView);
  619. FOR_EACH_WIDGET_INV(rit)
  620. {
  621. Widget* const widget(*rit);
  622. if (widget->isVisible() && widget->onSpecial(ev))
  623. return 0;
  624. }
  625. return 1;
  626. }
  627. void onPuglMouse(const int button, const bool press, const int x, const int y)
  628. {
  629. DBGp("PUGL: onMouse : %i %i %i %i\n", button, press, x, y);
  630. // FIXME - pugl sends 2 of these for each window on init, don't ask me why. we'll ignore it
  631. if (press && button == 0 && x == 0 && y == 0) return;
  632. if (fModal.childFocus != nullptr)
  633. return fModal.childFocus->focus();
  634. Widget::MouseEvent ev;
  635. ev.button = button;
  636. ev.press = press;
  637. ev.mod = static_cast<Modifier>(puglGetModifiers(fView));
  638. ev.time = puglGetEventTimestamp(fView);
  639. FOR_EACH_WIDGET_INV(rit)
  640. {
  641. Widget* const widget(*rit);
  642. ev.pos = Point<int>(x-widget->getAbsoluteX(), y-widget->getAbsoluteY());
  643. if (widget->isVisible() && widget->onMouse(ev))
  644. break;
  645. }
  646. }
  647. void onPuglMotion(const int x, const int y)
  648. {
  649. DBGp("PUGL: onMotion : %i %i\n", x, y);
  650. if (fModal.childFocus != nullptr)
  651. return;
  652. Widget::MotionEvent ev;
  653. ev.mod = static_cast<Modifier>(puglGetModifiers(fView));
  654. ev.time = puglGetEventTimestamp(fView);
  655. FOR_EACH_WIDGET_INV(rit)
  656. {
  657. Widget* const widget(*rit);
  658. ev.pos = Point<int>(x-widget->getAbsoluteX(), y-widget->getAbsoluteY());
  659. if (widget->isVisible() && widget->onMotion(ev))
  660. break;
  661. }
  662. }
  663. void onPuglScroll(const int x, const int y, const float dx, const float dy)
  664. {
  665. DBGp("PUGL: onScroll : %i %i %f %f\n", x, y, dx, dy);
  666. if (fModal.childFocus != nullptr)
  667. return;
  668. Widget::ScrollEvent ev;
  669. ev.delta = Point<float>(dx, dy);
  670. ev.mod = static_cast<Modifier>(puglGetModifiers(fView));
  671. ev.time = puglGetEventTimestamp(fView);
  672. FOR_EACH_WIDGET_INV(rit)
  673. {
  674. Widget* const widget(*rit);
  675. ev.pos = Point<int>(x-widget->getAbsoluteX(), y-widget->getAbsoluteY());
  676. if (widget->isVisible() && widget->onScroll(ev))
  677. break;
  678. }
  679. }
  680. void onPuglReshape(const int width, const int height)
  681. {
  682. DBGp("PUGL: onReshape : %i %i\n", width, height);
  683. if (width <= 1 && height <= 1)
  684. return;
  685. fWidth = static_cast<uint>(width);
  686. fHeight = static_cast<uint>(height);
  687. fSelf->onReshape(fWidth, fHeight);
  688. FOR_EACH_WIDGET(it)
  689. {
  690. Widget* const widget(*it);
  691. if (widget->pData->needsFullViewport)
  692. widget->setSize(fWidth, fHeight);
  693. }
  694. }
  695. void onPuglClose()
  696. {
  697. DBG("PUGL: onClose\n");
  698. if (fModal.enabled)
  699. exec_fini();
  700. fSelf->onClose();
  701. if (fModal.childFocus != nullptr)
  702. fModal.childFocus->fSelf->onClose();
  703. close();
  704. }
  705. // -------------------------------------------------------------------
  706. bool handlePluginKeyboard(const bool press, const uint key)
  707. {
  708. DBGp("PUGL: handlePluginKeyboard : %i %i\n", press, key);
  709. if (fModal.childFocus != nullptr)
  710. {
  711. fModal.childFocus->focus();
  712. return true;
  713. }
  714. Widget::KeyboardEvent ev;
  715. ev.press = press;
  716. ev.key = key;
  717. ev.mod = static_cast<Modifier>(fView->mods);
  718. ev.time = 0;
  719. if ((ev.mod & kModifierShift) != 0 && ev.key >= 'a' && ev.key <= 'z')
  720. ev.key -= 'a' - 'A'; // a-z -> A-Z
  721. FOR_EACH_WIDGET_INV(rit)
  722. {
  723. Widget* const widget(*rit);
  724. if (widget->isVisible() && widget->onKeyboard(ev))
  725. return true;
  726. }
  727. return false;
  728. }
  729. bool handlePluginSpecial(const bool press, const Key key)
  730. {
  731. DBGp("PUGL: handlePluginSpecial : %i %i\n", press, key);
  732. if (fModal.childFocus != nullptr)
  733. {
  734. fModal.childFocus->focus();
  735. return true;
  736. }
  737. int mods = 0x0;
  738. switch (key)
  739. {
  740. case kKeyShift:
  741. mods |= kModifierShift;
  742. break;
  743. case kKeyControl:
  744. mods |= kModifierControl;
  745. break;
  746. case kKeyAlt:
  747. mods |= kModifierAlt;
  748. break;
  749. default:
  750. break;
  751. }
  752. if (mods != 0x0)
  753. {
  754. if (press)
  755. fView->mods |= mods;
  756. else
  757. fView->mods &= ~(mods);
  758. }
  759. Widget::SpecialEvent ev;
  760. ev.press = press;
  761. ev.key = key;
  762. ev.mod = static_cast<Modifier>(fView->mods);
  763. ev.time = 0;
  764. FOR_EACH_WIDGET_INV(rit)
  765. {
  766. Widget* const widget(*rit);
  767. if (widget->isVisible() && widget->onSpecial(ev))
  768. return true;
  769. }
  770. return false;
  771. }
  772. // -------------------------------------------------------------------
  773. Application& fApp;
  774. Window* fSelf;
  775. PuglView* fView;
  776. bool fFirstInit;
  777. bool fVisible;
  778. bool fResizable;
  779. bool fUsingEmbed;
  780. uint fWidth;
  781. uint fHeight;
  782. char* fTitle;
  783. std::list<Widget*> fWidgets;
  784. struct Modal {
  785. bool enabled;
  786. PrivateData* parent;
  787. PrivateData* childFocus;
  788. Modal()
  789. : enabled(false),
  790. parent(nullptr),
  791. childFocus(nullptr) {}
  792. Modal(PrivateData* const p)
  793. : enabled(false),
  794. parent(p),
  795. childFocus(nullptr) {}
  796. ~Modal()
  797. {
  798. DISTRHO_SAFE_ASSERT(! enabled);
  799. DISTRHO_SAFE_ASSERT(childFocus == nullptr);
  800. }
  801. DISTRHO_DECLARE_NON_COPY_STRUCT(Modal)
  802. } fModal;
  803. #if defined(DISTRHO_OS_WINDOWS)
  804. HWND hwnd;
  805. #elif defined(DISTRHO_OS_MAC)
  806. bool fNeedsIdle;
  807. PuglOpenGLView* mView;
  808. id mWindow;
  809. #else
  810. Display* xDisplay;
  811. ::Window xWindow;
  812. #endif
  813. // -------------------------------------------------------------------
  814. // Callbacks
  815. #define handlePtr ((PrivateData*)puglGetHandle(view))
  816. static void onDisplayCallback(PuglView* view)
  817. {
  818. handlePtr->onPuglDisplay();
  819. }
  820. static int onKeyboardCallback(PuglView* view, bool press, uint32_t key)
  821. {
  822. return handlePtr->onPuglKeyboard(press, key);
  823. }
  824. static int onSpecialCallback(PuglView* view, bool press, PuglKey key)
  825. {
  826. return handlePtr->onPuglSpecial(press, static_cast<Key>(key));
  827. }
  828. static void onMouseCallback(PuglView* view, int button, bool press, int x, int y)
  829. {
  830. handlePtr->onPuglMouse(button, press, x, y);
  831. }
  832. static void onMotionCallback(PuglView* view, int x, int y)
  833. {
  834. handlePtr->onPuglMotion(x, y);
  835. }
  836. static void onScrollCallback(PuglView* view, int x, int y, float dx, float dy)
  837. {
  838. handlePtr->onPuglScroll(x, y, dx, dy);
  839. }
  840. static void onReshapeCallback(PuglView* view, int width, int height)
  841. {
  842. handlePtr->onPuglReshape(width, height);
  843. }
  844. static void onCloseCallback(PuglView* view)
  845. {
  846. handlePtr->onPuglClose();
  847. }
  848. #ifndef DGL_FILE_BROWSER_DISABLED
  849. static void fileBrowserSelectedCallback(PuglView* view, const char* filename)
  850. {
  851. handlePtr->fSelf->fileBrowserSelected(filename);
  852. }
  853. #endif
  854. #undef handlePtr
  855. DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PrivateData)
  856. };
  857. // -----------------------------------------------------------------------
  858. // Window
  859. Window::Window(Application& app)
  860. : pData(new PrivateData(app, this)) {}
  861. Window::Window(Application& app, Window& parent)
  862. : pData(new PrivateData(app, this, parent)) {}
  863. Window::Window(Application& app, intptr_t parentId)
  864. : pData(new PrivateData(app, this, parentId)) {}
  865. Window::~Window()
  866. {
  867. delete pData;
  868. }
  869. void Window::show()
  870. {
  871. pData->setVisible(true);
  872. }
  873. void Window::hide()
  874. {
  875. pData->setVisible(false);
  876. }
  877. void Window::close()
  878. {
  879. pData->close();
  880. }
  881. void Window::exec(bool lockWait)
  882. {
  883. pData->exec(lockWait);
  884. }
  885. void Window::focus()
  886. {
  887. pData->focus();
  888. }
  889. void Window::repaint() noexcept
  890. {
  891. puglPostRedisplay(pData->fView);
  892. }
  893. // static int fib_filter_filename_filter(const char* const name)
  894. // {
  895. // return 1;
  896. // (void)name;
  897. // }
  898. #ifndef DGL_FILE_BROWSER_DISABLED
  899. bool Window::openFileBrowser(const FileBrowserOptions& options)
  900. {
  901. # ifdef SOFD_HAVE_X11
  902. using DISTRHO_NAMESPACE::String;
  903. // --------------------------------------------------------------------------
  904. // configure start dir
  905. // TODO: get abspath if needed
  906. // TODO: cross-platform
  907. String startDir(options.startDir);
  908. if (startDir.isEmpty())
  909. {
  910. if (char* const dir_name = get_current_dir_name())
  911. {
  912. startDir = dir_name;
  913. std::free(dir_name);
  914. }
  915. }
  916. DISTRHO_SAFE_ASSERT_RETURN(startDir.isNotEmpty(), false);
  917. if (! startDir.endsWith('/'))
  918. startDir += "/";
  919. DISTRHO_SAFE_ASSERT_RETURN(x_fib_configure(0, startDir) == 0, false);
  920. // --------------------------------------------------------------------------
  921. // configure title
  922. String title(options.title);
  923. if (title.isEmpty())
  924. {
  925. title = pData->getTitle();
  926. if (title.isEmpty())
  927. title = "FileBrowser";
  928. }
  929. DISTRHO_SAFE_ASSERT_RETURN(x_fib_configure(1, title) == 0, false);
  930. // --------------------------------------------------------------------------
  931. // configure filters
  932. x_fib_cfg_filter_callback(nullptr); //fib_filter_filename_filter);
  933. // --------------------------------------------------------------------------
  934. // configure buttons
  935. x_fib_cfg_buttons(3, options.buttons.listAllFiles-1);
  936. x_fib_cfg_buttons(1, options.buttons.showHidden-1);
  937. x_fib_cfg_buttons(2, options.buttons.showPlaces-1);
  938. // --------------------------------------------------------------------------
  939. // show
  940. return (x_fib_show(pData->xDisplay, pData->xWindow, /*options.width*/0, /*options.height*/0) == 0);
  941. # else
  942. // not implemented
  943. return false;
  944. # endif
  945. }
  946. #endif
  947. bool Window::isVisible() const noexcept
  948. {
  949. return pData->fVisible;
  950. }
  951. void Window::setVisible(bool yesNo)
  952. {
  953. pData->setVisible(yesNo);
  954. }
  955. bool Window::isResizable() const noexcept
  956. {
  957. return pData->fResizable;
  958. }
  959. void Window::setResizable(bool yesNo)
  960. {
  961. pData->setResizable(yesNo);
  962. }
  963. uint Window::getWidth() const noexcept
  964. {
  965. return pData->fWidth;
  966. }
  967. uint Window::getHeight() const noexcept
  968. {
  969. return pData->fHeight;
  970. }
  971. Size<uint> Window::getSize() const noexcept
  972. {
  973. return Size<uint>(pData->fWidth, pData->fHeight);
  974. }
  975. void Window::setSize(uint width, uint height)
  976. {
  977. pData->setSize(width, height);
  978. }
  979. void Window::setSize(Size<uint> size)
  980. {
  981. pData->setSize(size.getWidth(), size.getHeight());
  982. }
  983. const char* Window::getTitle() const noexcept
  984. {
  985. return pData->getTitle();
  986. }
  987. void Window::setTitle(const char* title)
  988. {
  989. pData->setTitle(title);
  990. }
  991. void Window::setTransientWinId(uintptr_t winId)
  992. {
  993. pData->setTransientWinId(winId);
  994. }
  995. Application& Window::getApp() const noexcept
  996. {
  997. return pData->fApp;
  998. }
  999. intptr_t Window::getWindowId() const noexcept
  1000. {
  1001. return puglGetNativeWindow(pData->fView);
  1002. }
  1003. void Window::_addWidget(Widget* const widget)
  1004. {
  1005. pData->addWidget(widget);
  1006. }
  1007. void Window::_removeWidget(Widget* const widget)
  1008. {
  1009. pData->removeWidget(widget);
  1010. }
  1011. void Window::_idle()
  1012. {
  1013. pData->idle();
  1014. }
  1015. // -----------------------------------------------------------------------
  1016. void Window::addIdleCallback(IdleCallback* const callback)
  1017. {
  1018. DISTRHO_SAFE_ASSERT_RETURN(callback != nullptr,)
  1019. pData->fApp.pData->idleCallbacks.push_back(callback);
  1020. }
  1021. void Window::removeIdleCallback(IdleCallback* const callback)
  1022. {
  1023. DISTRHO_SAFE_ASSERT_RETURN(callback != nullptr,)
  1024. pData->fApp.pData->idleCallbacks.remove(callback);
  1025. }
  1026. // -----------------------------------------------------------------------
  1027. void Window::onDisplayBefore()
  1028. {
  1029. glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  1030. glLoadIdentity();
  1031. }
  1032. void Window::onDisplayAfter()
  1033. {
  1034. }
  1035. void Window::onReshape(uint width, uint height)
  1036. {
  1037. glEnable(GL_BLEND);
  1038. glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  1039. glMatrixMode(GL_PROJECTION);
  1040. glLoadIdentity();
  1041. glOrtho(0.0, static_cast<GLdouble>(width), static_cast<GLdouble>(height), 0.0, 0.0, 1.0);
  1042. glViewport(0, 0, static_cast<GLsizei>(width), static_cast<GLsizei>(height));
  1043. glMatrixMode(GL_MODELVIEW);
  1044. glLoadIdentity();
  1045. }
  1046. void Window::onClose()
  1047. {
  1048. }
  1049. #ifndef DGL_FILE_BROWSER_DISABLED
  1050. void Window::fileBrowserSelected(const char*)
  1051. {
  1052. }
  1053. #endif
  1054. bool Window::handlePluginKeyboard(const bool press, const uint key)
  1055. {
  1056. return pData->handlePluginKeyboard(press, key);
  1057. }
  1058. bool Window::handlePluginSpecial(const bool press, const Key key)
  1059. {
  1060. return pData->handlePluginSpecial(press, key);
  1061. }
  1062. // -----------------------------------------------------------------------
  1063. StandaloneWindow::StandaloneWindow()
  1064. : Application(),
  1065. Window((Application&)*this),
  1066. fWidget(nullptr) {}
  1067. void StandaloneWindow::exec()
  1068. {
  1069. Window::show();
  1070. Application::exec();
  1071. }
  1072. void StandaloneWindow::onReshape(uint width, uint height)
  1073. {
  1074. if (fWidget != nullptr)
  1075. fWidget->setSize(width, height);
  1076. Window::onReshape(width, height);
  1077. }
  1078. void StandaloneWindow::_addWidget(Widget* widget)
  1079. {
  1080. if (fWidget == nullptr)
  1081. {
  1082. fWidget = widget;
  1083. fWidget->pData->needsFullViewport = true;
  1084. }
  1085. Window::_addWidget(widget);
  1086. }
  1087. void StandaloneWindow::_removeWidget(Widget* widget)
  1088. {
  1089. if (fWidget == widget)
  1090. {
  1091. fWidget->pData->needsFullViewport = false;
  1092. fWidget = nullptr;
  1093. }
  1094. Window::_removeWidget(widget);
  1095. }
  1096. // -----------------------------------------------------------------------
  1097. END_NAMESPACE_DGL
  1098. #undef DBG
  1099. #undef DBGF