The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
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.

701 lines
23KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2017 - ROLI Ltd.
  5. Permission is granted to use this software under the terms of either:
  6. a) the GPL v2 (or any later version)
  7. b) the Affero GPL v3
  8. Details of these licenses can be found at: www.gnu.org/licenses
  9. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  11. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  12. ------------------------------------------------------------------------------
  13. To release a closed-source product which uses JUCE, commercial licenses are
  14. available: visit www.juce.com for more information.
  15. ==============================================================================
  16. */
  17. //==============================================================================
  18. bool juce_handleXEmbedEvent (ComponentPeer*, void*);
  19. Window juce_getCurrentFocusWindow (ComponentPeer*);
  20. //==============================================================================
  21. unsigned long juce_createKeyProxyWindow (ComponentPeer*);
  22. void juce_deleteKeyProxyWindow (ComponentPeer*);
  23. //==============================================================================
  24. class XEmbedComponent::Pimpl : private ComponentListener
  25. {
  26. public:
  27. //==============================================================================
  28. enum
  29. {
  30. maxXEmbedVersionToSupport = 0
  31. };
  32. enum Flags
  33. {
  34. XEMBED_MAPPED = (1<<0)
  35. };
  36. enum
  37. {
  38. XEMBED_EMBEDDED_NOTIFY = 0,
  39. XEMBED_WINDOW_ACTIVATE = 1,
  40. XEMBED_WINDOW_DEACTIVATE = 2,
  41. XEMBED_REQUEST_FOCUS = 3,
  42. XEMBED_FOCUS_IN = 4,
  43. XEMBED_FOCUS_OUT = 5,
  44. XEMBED_FOCUS_NEXT = 6,
  45. XEMBED_FOCUS_PREV = 7,
  46. XEMBED_MODALITY_ON = 10,
  47. XEMBED_MODALITY_OFF = 11,
  48. XEMBED_REGISTER_ACCELERATOR = 12,
  49. XEMBED_UNREGISTER_ACCELERATOR = 13,
  50. XEMBED_ACTIVATE_ACCELERATOR = 14
  51. };
  52. enum
  53. {
  54. XEMBED_FOCUS_CURRENT = 0,
  55. XEMBED_FOCUS_FIRST = 1,
  56. XEMBED_FOCUS_LAST = 2
  57. };
  58. //==============================================================================
  59. class SharedKeyWindow
  60. {
  61. public:
  62. //==============================================================================
  63. class Ref
  64. {
  65. public:
  66. Ref() : keyWindow (nullptr) {}
  67. Ref (Pimpl& p) { keyWindow = getKeyWindowForPeer (p.owner.getPeer()); }
  68. ~Ref() { free(); }
  69. //==============================================================================
  70. Ref (const Ref& o) : keyWindow (o.keyWindow) { if (keyWindow != nullptr) keyWindow->numRefs++; }
  71. Ref (Ref && o) : keyWindow (o.keyWindow) { o.keyWindow = nullptr; }
  72. Ref (std::nullptr_t) : keyWindow (nullptr) {}
  73. //==============================================================================
  74. Ref& operator= (std::nullptr_t) { free(); return *this; }
  75. Ref& operator= (const Ref& o)
  76. {
  77. free();
  78. keyWindow = o.keyWindow;
  79. if (keyWindow != nullptr)
  80. keyWindow->numRefs++;
  81. return *this;
  82. }
  83. Ref& operator= (Ref && o)
  84. {
  85. if (keyWindow != o.keyWindow)
  86. {
  87. free();
  88. keyWindow = o.keyWindow;
  89. }
  90. o.keyWindow = nullptr;
  91. return *this;
  92. }
  93. //==============================================================================
  94. SharedKeyWindow& operator*() noexcept { return *keyWindow; }
  95. SharedKeyWindow* operator->() noexcept { return keyWindow; }
  96. //==============================================================================
  97. bool operator== (std::nullptr_t) const noexcept { return (keyWindow == nullptr); }
  98. bool operator!= (std::nullptr_t) const noexcept { return (keyWindow != nullptr); }
  99. private:
  100. //==============================================================================
  101. void free()
  102. {
  103. if (keyWindow != nullptr)
  104. {
  105. if (--keyWindow->numRefs == 0)
  106. delete keyWindow;
  107. keyWindow = nullptr;
  108. }
  109. }
  110. SharedKeyWindow* keyWindow;
  111. };
  112. public:
  113. //==============================================================================
  114. Window getHandle() { return keyProxy; }
  115. static Window getCurrentFocusWindow (ComponentPeer* peerToLookFor)
  116. {
  117. if (keyWindows != nullptr && peerToLookFor != nullptr)
  118. {
  119. SharedKeyWindow* foundKeyWindow = (*keyWindows)[peerToLookFor];
  120. if (foundKeyWindow != nullptr)
  121. return foundKeyWindow->keyProxy;
  122. }
  123. return (Window)0;
  124. }
  125. private:
  126. //==============================================================================
  127. friend class Ref;
  128. SharedKeyWindow (ComponentPeer* peerToUse)
  129. : keyPeer (peerToUse),
  130. keyProxy (juce_createKeyProxyWindow (keyPeer)),
  131. numRefs (1)
  132. {}
  133. ~SharedKeyWindow()
  134. {
  135. juce_deleteKeyProxyWindow (keyPeer);
  136. if (keyWindows != nullptr)
  137. {
  138. keyWindows->remove (keyPeer);
  139. if (keyWindows->size() == 0)
  140. {
  141. delete keyWindows;
  142. keyWindows = nullptr;
  143. }
  144. }
  145. }
  146. ComponentPeer* keyPeer;
  147. Window keyProxy;
  148. int numRefs;
  149. static SharedKeyWindow* getKeyWindowForPeer (ComponentPeer* peerToLookFor)
  150. {
  151. jassert (peerToLookFor != nullptr);
  152. if (keyWindows == nullptr)
  153. keyWindows = new HashMap<ComponentPeer*,SharedKeyWindow*>;
  154. SharedKeyWindow* foundKeyWindow = (*keyWindows)[peerToLookFor];
  155. if (foundKeyWindow == nullptr)
  156. {
  157. foundKeyWindow = new SharedKeyWindow (peerToLookFor);
  158. keyWindows->set (peerToLookFor, foundKeyWindow);
  159. }
  160. return foundKeyWindow;
  161. }
  162. //==============================================================================
  163. friend class Ref;
  164. static HashMap<ComponentPeer*,SharedKeyWindow*>* keyWindows;
  165. };
  166. public:
  167. //==============================================================================
  168. Pimpl (XEmbedComponent& parent, Window x11Window, bool wantsKeyboardFocus)
  169. : owner (parent), atoms (x11display.get()), wantsFocus (wantsKeyboardFocus)
  170. {
  171. if (widgets == nullptr)
  172. widgets = new Array<Pimpl*>;
  173. widgets->add (this);
  174. createHostWindow();
  175. setClient (x11Window);
  176. owner.setWantsKeyboardFocus (wantsFocus);
  177. owner.addComponentListener (this);
  178. }
  179. ~Pimpl()
  180. {
  181. owner.removeComponentListener (this);
  182. setClient (0);
  183. if (host != 0)
  184. {
  185. Display* dpy = getDisplay();
  186. XDestroyWindow (dpy, host);
  187. XSync (dpy, false);
  188. const long mask = NoEventMask | KeyPressMask | KeyReleaseMask
  189. | EnterWindowMask | LeaveWindowMask | PointerMotionMask
  190. | KeymapStateMask | ExposureMask | StructureNotifyMask
  191. | FocusChangeMask;
  192. XEvent event;
  193. while (XCheckWindowEvent (dpy, host, mask, &event) == True)
  194. {}
  195. host = 0;
  196. }
  197. if (widgets != nullptr)
  198. {
  199. widgets->removeAllInstancesOf (this);
  200. if (widgets->size() == 0)
  201. {
  202. delete widgets;
  203. widgets = nullptr;
  204. }
  205. }
  206. }
  207. //==============================================================================
  208. void setClient (Window xembedClient)
  209. {
  210. removeClient();
  211. if (xembedClient != 0)
  212. {
  213. client = xembedClient;
  214. configureNotify();
  215. Display* dpy = getDisplay();
  216. XSelectInput (dpy, client, StructureNotifyMask | PropertyChangeMask | FocusChangeMask);
  217. getXEmbedMappedFlag();
  218. XReparentWindow (dpy, client, host, 0, 0);
  219. if (supportsXembed)
  220. sendXEmbedEvent (CurrentTime, XEMBED_EMBEDDED_NOTIFY, 0, (long) host, xembedVersion);
  221. updateMapping();
  222. }
  223. }
  224. void focusGained (FocusChangeType changeType)
  225. {
  226. if (client != 0 && supportsXembed && wantsFocus)
  227. {
  228. updateKeyFocus();
  229. sendXEmbedEvent (CurrentTime, XEMBED_FOCUS_IN,
  230. (changeType == focusChangedByTabKey ? XEMBED_FOCUS_FIRST : XEMBED_FOCUS_CURRENT));
  231. }
  232. }
  233. void focusLost (FocusChangeType)
  234. {
  235. if (client != 0 && supportsXembed && wantsFocus)
  236. {
  237. sendXEmbedEvent (CurrentTime, XEMBED_FOCUS_OUT);
  238. updateKeyFocus();
  239. }
  240. }
  241. void broughtToFront()
  242. {
  243. if (client != 0 && supportsXembed)
  244. sendXEmbedEvent (CurrentTime, XEMBED_WINDOW_ACTIVATE);
  245. }
  246. private:
  247. //==============================================================================
  248. XEmbedComponent& owner;
  249. Window client = 0, host = 0;
  250. ScopedXDisplay x11display;
  251. Atoms atoms;
  252. bool wantsFocus = false;
  253. bool supportsXembed = false;
  254. bool hasBeenMapped = false;
  255. int xembedVersion = maxXEmbedVersionToSupport;
  256. ComponentPeer* lastPeer = nullptr;
  257. SharedKeyWindow::Ref keyWindow;
  258. //==============================================================================
  259. void componentParentHierarchyChanged (Component&) override { peerChanged (owner.getPeer()); }
  260. void componentMovedOrResized (Component&, bool, bool) override
  261. {
  262. if (client != 0 && lastPeer != nullptr)
  263. {
  264. Display* dpy = getDisplay();
  265. Rectangle<int> newBounds = getX11BoundsFromJuce();
  266. XWindowAttributes attr;
  267. if (XGetWindowAttributes (dpy, host, &attr))
  268. {
  269. Rectangle<int> currentBounds (attr.x, attr.y, attr.width, attr.height);
  270. if (currentBounds != newBounds)
  271. {
  272. XMoveResizeWindow (dpy, host, newBounds.getX(), newBounds.getY(),
  273. static_cast<unsigned int> (newBounds.getWidth()),
  274. static_cast<unsigned int> (newBounds.getHeight()));
  275. if (currentBounds.getWidth() != newBounds.getWidth()
  276. || currentBounds.getHeight() != newBounds.getHeight())
  277. XResizeWindow (dpy, client,
  278. static_cast<unsigned int> (newBounds.getWidth()),
  279. static_cast<unsigned int> (newBounds.getHeight()));
  280. }
  281. }
  282. }
  283. }
  284. //==============================================================================
  285. void createHostWindow()
  286. {
  287. Display* dpy = getDisplay();
  288. int defaultScreen = XDefaultScreen (dpy);
  289. Window root = RootWindow (dpy, defaultScreen);
  290. XSetWindowAttributes swa;
  291. swa.border_pixel = 0;
  292. swa.background_pixmap = None;
  293. swa.override_redirect = True;
  294. swa.event_mask = StructureNotifyMask | FocusChangeMask;
  295. host = XCreateWindow (dpy, root, 0, 0, 1, 1, 0, CopyFromParent,
  296. InputOutput, CopyFromParent,
  297. CWEventMask | CWBorderPixel | CWBackPixmap | CWOverrideRedirect,
  298. &swa);
  299. }
  300. void removeClient()
  301. {
  302. if (client != 0)
  303. {
  304. Display* dpy = getDisplay();
  305. XSelectInput (dpy, client, 0);
  306. keyWindow = nullptr;
  307. int defaultScreen = XDefaultScreen (dpy);
  308. Window root = RootWindow (dpy, defaultScreen);
  309. if (hasBeenMapped)
  310. {
  311. XUnmapWindow (dpy, client);
  312. hasBeenMapped = false;
  313. }
  314. XReparentWindow (dpy, client, root, 0, 0);
  315. client = 0;
  316. }
  317. }
  318. void updateMapping()
  319. {
  320. if (client != 0)
  321. {
  322. const bool shouldBeMapped = getXEmbedMappedFlag();
  323. if (shouldBeMapped != hasBeenMapped)
  324. {
  325. hasBeenMapped = shouldBeMapped;
  326. if (shouldBeMapped)
  327. XMapWindow (getDisplay(), client);
  328. else
  329. XUnmapWindow (getDisplay(), client);
  330. }
  331. }
  332. }
  333. Window getParentX11Window()
  334. {
  335. if (ComponentPeer* peer = owner.getPeer())
  336. return reinterpret_cast<Window> (peer->getNativeHandle());
  337. return 0;
  338. }
  339. Display* getDisplay() { return reinterpret_cast<Display*> (x11display.get()); }
  340. //==============================================================================
  341. bool getXEmbedMappedFlag()
  342. {
  343. GetXProperty embedInfo (x11display.get(), client, atoms.XembedInfo, 0, 2, false, atoms.XembedInfo);
  344. if (embedInfo.success && embedInfo.actualFormat == 32
  345. && embedInfo.numItems >= 2 && embedInfo.data != nullptr)
  346. {
  347. long* buffer = (long*) embedInfo.data;
  348. supportsXembed = true;
  349. xembedVersion = jmin ((int) maxXEmbedVersionToSupport, (int) buffer[0]);
  350. return ((buffer[1] & XEMBED_MAPPED) != 0);
  351. }
  352. else
  353. {
  354. supportsXembed = false;
  355. xembedVersion = maxXEmbedVersionToSupport;
  356. }
  357. return true;
  358. }
  359. //==============================================================================
  360. void propertyChanged (const Atom& a)
  361. {
  362. if (a == atoms.XembedInfo)
  363. updateMapping();
  364. }
  365. void configureNotify()
  366. {
  367. XWindowAttributes attr;
  368. Display* dpy = getDisplay();
  369. if (XGetWindowAttributes (dpy, client, &attr))
  370. {
  371. XWindowAttributes hostAttr;
  372. if (XGetWindowAttributes (dpy, host, &hostAttr))
  373. if (attr.width != hostAttr.width || attr.height != hostAttr.height)
  374. XResizeWindow (dpy, host, (unsigned int) attr.width, (unsigned int) attr.height);
  375. // as the client window is not on any screen yet, we need to guess
  376. // on which screen it might appear to get a scaling factor :-(
  377. const Desktop::Displays& displays = Desktop::getInstance().getDisplays();
  378. ComponentPeer* peer = owner.getPeer();
  379. const double scale = (peer != nullptr ? displays.getDisplayContaining (peer->getBounds().getCentre())
  380. : displays.getMainDisplay()).scale;
  381. Point<int> topLeftInPeer
  382. = (peer != nullptr ? peer->getComponent().getLocalPoint (&owner, Point<int> (0, 0))
  383. : owner.getBounds().getTopLeft());
  384. Rectangle<int> newBounds (topLeftInPeer.getX(), topLeftInPeer.getY(),
  385. static_cast<int> (static_cast<double> (attr.width) / scale),
  386. static_cast<int> (static_cast<double> (attr.height) / scale));
  387. if (peer != nullptr)
  388. newBounds = owner.getLocalArea (&peer->getComponent(), newBounds);
  389. jassert (newBounds.getX() == 0 && newBounds.getY() == 0);
  390. if (newBounds != owner.getLocalBounds())
  391. owner.setSize (newBounds.getWidth(), newBounds.getHeight());
  392. }
  393. }
  394. void peerChanged (ComponentPeer* newPeer)
  395. {
  396. if (newPeer != lastPeer)
  397. {
  398. if (lastPeer != nullptr)
  399. keyWindow = nullptr;
  400. Display* dpy = getDisplay();
  401. Window rootWindow = RootWindow (dpy, DefaultScreen (dpy));
  402. Rectangle<int> newBounds = getX11BoundsFromJuce();
  403. if (newPeer == nullptr)
  404. XUnmapWindow (dpy, host);
  405. Window newParent = (newPeer != nullptr ? getParentX11Window() : rootWindow);
  406. XReparentWindow (dpy, host, newParent, newBounds.getX(), newBounds.getY());
  407. lastPeer = newPeer;
  408. if (newPeer != nullptr)
  409. {
  410. if (wantsFocus)
  411. {
  412. keyWindow = SharedKeyWindow::Ref (*this);
  413. updateKeyFocus();
  414. }
  415. componentMovedOrResized (owner, true, true);
  416. XMapWindow (dpy, host);
  417. broughtToFront();
  418. }
  419. }
  420. }
  421. void updateKeyFocus()
  422. {
  423. if (lastPeer != nullptr && lastPeer->isFocused())
  424. XSetInputFocus (getDisplay(), getCurrentFocusWindow (lastPeer), RevertToParent, CurrentTime);
  425. }
  426. //==============================================================================
  427. void handleXembedCmd (const ::Time& /*xTime*/, long opcode, long /*detail*/, long /*data1*/, long /*data2*/)
  428. {
  429. switch (opcode)
  430. {
  431. case XEMBED_REQUEST_FOCUS:
  432. if (wantsFocus)
  433. owner.grabKeyboardFocus();
  434. break;
  435. case XEMBED_FOCUS_NEXT:
  436. if (wantsFocus)
  437. owner.moveKeyboardFocusToSibling (true);
  438. break;
  439. case XEMBED_FOCUS_PREV:
  440. if (wantsFocus)
  441. owner.moveKeyboardFocusToSibling (false);
  442. break;
  443. }
  444. }
  445. bool handleX11Event (const XEvent& e)
  446. {
  447. if (e.xany.window == client)
  448. {
  449. switch (e.type)
  450. {
  451. case PropertyNotify:
  452. propertyChanged (e.xproperty.atom);
  453. return true;
  454. case ConfigureNotify:
  455. configureNotify();
  456. return true;
  457. }
  458. }
  459. else if (e.xany.window == host)
  460. {
  461. switch (e.type)
  462. {
  463. case GravityNotify:
  464. componentMovedOrResized (owner, true, true);
  465. return true;
  466. case ClientMessage:
  467. if (e.xclient.message_type == atoms.XembedMsgType && e.xclient.format == 32)
  468. {
  469. handleXembedCmd ((::Time) e.xclient.data.l[0], e.xclient.data.l[1],
  470. e.xclient.data.l[2], e.xclient.data.l[3],
  471. e.xclient.data.l[4]);
  472. return true;
  473. }
  474. break;
  475. }
  476. }
  477. return false;
  478. }
  479. void sendXEmbedEvent (const ::Time& xTime, long opcode,
  480. long opcodeMinor = 0, long data1 = 0, long data2 = 0)
  481. {
  482. XClientMessageEvent msg;
  483. Display* dpy = getDisplay();
  484. ::memset (&msg, 0, sizeof (XClientMessageEvent));
  485. msg.window = client;
  486. msg.type = ClientMessage;
  487. msg.message_type = atoms.XembedMsgType;
  488. msg.format = 32;
  489. msg.data.l[0] = (long) xTime;
  490. msg.data.l[1] = opcode;
  491. msg.data.l[2] = opcodeMinor;
  492. msg.data.l[3] = data1;
  493. msg.data.l[4] = data2;
  494. XSendEvent (dpy, client, False, NoEventMask, (XEvent*) &msg);
  495. XSync (dpy, False);
  496. }
  497. Rectangle<int> getX11BoundsFromJuce()
  498. {
  499. if (ComponentPeer* peer = owner.getPeer())
  500. {
  501. Rectangle<int> r
  502. = peer->getComponent().getLocalArea (&owner, owner.getLocalBounds());
  503. const double scale
  504. = Desktop::getInstance().getDisplays().getDisplayContaining (peer->localToGlobal (r.getCentre())).scale;
  505. return r * scale;
  506. }
  507. return owner.getLocalBounds();
  508. }
  509. //==============================================================================
  510. friend bool juce::juce_handleXEmbedEvent (ComponentPeer*, void*);
  511. friend unsigned long juce::juce_getCurrentFocusWindow (ComponentPeer*);
  512. static Array<Pimpl*>* widgets;
  513. static bool dispatchX11Event (ComponentPeer* p, const XEvent* eventArg)
  514. {
  515. if (widgets != nullptr)
  516. {
  517. if (eventArg != nullptr)
  518. {
  519. const XEvent& e = *eventArg;
  520. Window w = e.xany.window;
  521. if (w == 0) return false;
  522. for (auto && widget : *widgets)
  523. if (w == widget->host || w == widget->client)
  524. return widget->handleX11Event (e);
  525. }
  526. else
  527. {
  528. for (auto && widget : *widgets)
  529. {
  530. if (widget->owner.getPeer() == p)
  531. widget->peerChanged (nullptr);
  532. }
  533. }
  534. }
  535. return false;
  536. }
  537. static Window getCurrentFocusWindow (ComponentPeer* p)
  538. {
  539. if (widgets != nullptr && p != nullptr)
  540. {
  541. for (auto && widget : *widgets)
  542. if (widget->owner.getPeer() == p && widget->owner.hasKeyboardFocus (false))
  543. return widget->client;
  544. }
  545. return SharedKeyWindow::getCurrentFocusWindow (p);
  546. }
  547. };
  548. //==============================================================================
  549. Array<XEmbedComponent::Pimpl*>* XEmbedComponent::Pimpl::widgets = nullptr;
  550. HashMap<ComponentPeer*,XEmbedComponent::Pimpl::SharedKeyWindow*>* XEmbedComponent::Pimpl::SharedKeyWindow::keyWindows = nullptr;
  551. //==============================================================================
  552. XEmbedComponent::XEmbedComponent (unsigned long wID, bool wantsKeyboardFocus)
  553. : pimpl (new Pimpl (*this, wID, wantsKeyboardFocus))
  554. {
  555. setOpaque (true);
  556. }
  557. XEmbedComponent::~XEmbedComponent() {}
  558. void XEmbedComponent::paint (Graphics& g)
  559. {
  560. g.fillAll (Colours::lightgrey);
  561. }
  562. void XEmbedComponent::focusGained (FocusChangeType changeType) { pimpl->focusGained (changeType); }
  563. void XEmbedComponent::focusLost (FocusChangeType changeType) { pimpl->focusLost (changeType); }
  564. void XEmbedComponent::broughtToFront() { pimpl->broughtToFront(); }
  565. //==============================================================================
  566. bool juce_handleXEmbedEvent (ComponentPeer* p, void* e)
  567. {
  568. return ::XEmbedComponent::Pimpl::dispatchX11Event (p, reinterpret_cast<const XEvent*> (e));
  569. }
  570. unsigned long juce_getCurrentFocusWindow (ComponentPeer* peer)
  571. {
  572. return (unsigned long) ::XEmbedComponent::Pimpl::getCurrentFocusWindow (peer);
  573. }