DISTRHO Plugin Framework
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.

1235 lines
31KB

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