JUCE-7.0.12-0-g4f43011b96 JUCE-7.0.12-0-g4f43011b96
JUCE — C++ application framework with suport for VST, VST3, LV2 audio plug-ins

« « « Anklang Documentation
Loading...
Searching...
No Matches
juce_XEmbedComponent_linux.cpp
Go to the documentation of this file.
1 /*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - Raw Material Software Limited
6
7 JUCE is an open source library subject to commercial or open-source
8 licensing.
9
10 By using JUCE, you agree to the terms of both the JUCE 7 End-User License
11 Agreement and JUCE Privacy Policy.
12
13 End User License Agreement: www.juce.com/juce-7-licence
14 Privacy Policy: www.juce.com/juce-privacy-policy
15
16 Or: You may also use this code under the terms of the GPL v3 (see
17 www.gnu.org/licenses).
18
19 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
20 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
21 DISCLAIMED.
22
23 ==============================================================================
24*/
25
26namespace juce
27{
28
29::Window juce_createKeyProxyWindow (ComponentPeer*);
30void juce_deleteKeyProxyWindow (::Window);
31
32//==============================================================================
33enum
34{
35 maxXEmbedVersionToSupport = 0
36};
37
38enum
39{
40 XEMBED_MAPPED = (1<<0)
41};
42
43enum
44{
45 XEMBED_EMBEDDED_NOTIFY = 0,
46 XEMBED_WINDOW_ACTIVATE = 1,
47 XEMBED_WINDOW_DEACTIVATE = 2,
48 XEMBED_REQUEST_FOCUS = 3,
49 XEMBED_FOCUS_IN = 4,
50 XEMBED_FOCUS_OUT = 5,
51 XEMBED_FOCUS_NEXT = 6,
52 XEMBED_FOCUS_PREV = 7,
53 XEMBED_MODALITY_ON = 10,
54 XEMBED_MODALITY_OFF = 11,
55 XEMBED_REGISTER_ACCELERATOR = 12,
56 XEMBED_UNREGISTER_ACCELERATOR = 13,
57 XEMBED_ACTIVATE_ACCELERATOR = 14
58};
59
60enum
61{
62 XEMBED_FOCUS_CURRENT = 0,
63 XEMBED_FOCUS_FIRST = 1,
64 XEMBED_FOCUS_LAST = 2
65};
66
67//==============================================================================
69{
70public:
71 //==============================================================================
73 {
75 : keyPeer (peerToUse),
76 keyProxy (juce_createKeyProxyWindow (keyPeer)),
77 association (peerToUse, keyProxy)
78 {}
79
81 {
82 association = {};
83 juce_deleteKeyProxyWindow (keyProxy);
84
85 auto& keyWindows = getKeyWindows();
86 keyWindows.remove (keyPeer);
87 }
88
90
91 //==============================================================================
92 Window getHandle() { return keyProxy; }
93
94 static Window getCurrentFocusWindow (ComponentPeer* peerToLookFor)
95 {
96 auto& keyWindows = getKeyWindows();
97
98 if (peerToLookFor != nullptr)
100 return foundKeyWindow->keyProxy;
101
102 return {};
103 }
104
105 static SharedKeyWindow::Ptr getKeyWindowForPeer (ComponentPeer* peerToLookFor)
106 {
107 jassert (peerToLookFor != nullptr);
108
109 auto& keyWindows = getKeyWindows();
111
112 if (foundKeyWindow == nullptr)
113 {
116 }
117
118 return foundKeyWindow;
119 }
120
121 private:
122 //==============================================================================
123 ComponentPeer* keyPeer;
124 Window keyProxy;
125 ScopedWindowAssociation association;
126
127 static HashMap<ComponentPeer*, SharedKeyWindow*>& getKeyWindows()
128 {
129 // store a weak reference to the shared key windows
131 return keyWindows;
132 }
133 };
134
135public:
136 //==============================================================================
137 Pimpl (XEmbedComponent& parent, Window x11Window,
139 : owner (parent),
140 infoAtom (XWindowSystem::getInstance()->getAtoms().XembedInfo),
141 messageTypeAtom (XWindowSystem::getInstance()->getAtoms().XembedMsgType),
142 clientInitiated (isClientInitiated),
143 wantsFocus (wantsKeyboardFocus),
144 allowResize (shouldAllowResize)
145 {
146 getWidgets().add (this);
147
148 createHostWindow();
149
150 if (clientInitiated)
151 setClient (x11Window, true);
152
153 owner.setWantsKeyboardFocus (wantsFocus);
154 owner.addComponentListener (this);
155 }
156
157 ~Pimpl() override
158 {
159 owner.removeComponentListener (this);
160 setClient (0, true);
161
162 if (host != 0)
163 {
164 auto dpy = getDisplay();
165
166 X11Symbols::getInstance()->xDestroyWindow (dpy, host);
167 X11Symbols::getInstance()->xSync (dpy, false);
168
173
174 XEvent event;
175 while (X11Symbols::getInstance()->xCheckWindowEvent (dpy, host, mask, &event) == True)
176 {}
177
178 host = 0;
179 }
180
181 getWidgets().removeAllInstancesOf (this);
182 }
183
184 //==============================================================================
185 void setClient (Window xembedClient, bool shouldReparent)
186 {
187 removeClient();
188
189 if (xembedClient != 0)
190 {
191 auto dpy = getDisplay();
192
193 client = xembedClient;
194
195 // if the client has initiated the component then keep the clients size
196 // otherwise the client should use the host's window' size
197 if (clientInitiated)
198 {
199 configureNotify();
200 }
201 else
202 {
203 auto newBounds = getX11BoundsFromJuce();
204 X11Symbols::getInstance()->xResizeWindow (dpy, client, static_cast<unsigned int> (newBounds.getWidth()),
205 static_cast<unsigned int> (newBounds.getHeight()));
206 }
207
209
211 X11Symbols::getInstance()->xGetWindowAttributes (dpy, client, &clientAttr);
212
213 if ((eventMask & clientAttr.your_event_mask) != eventMask)
214 X11Symbols::getInstance()->xSelectInput (dpy, client, clientAttr.your_event_mask | eventMask);
215
216 getXEmbedMappedFlag();
217
218 if (shouldReparent)
219 X11Symbols::getInstance()->xReparentWindow (dpy, client, host, 0, 0);
220
221 if (supportsXembed)
222 sendXEmbedEvent (CurrentTime, XEMBED_EMBEDDED_NOTIFY, 0, (long) host, xembedVersion);
223
224 updateMapping();
225 }
226 }
227
228 void focusGained (FocusChangeType changeType)
229 {
230 if (client != 0 && supportsXembed && wantsFocus)
231 {
232 updateKeyFocus();
233 sendXEmbedEvent (CurrentTime, XEMBED_FOCUS_IN,
234 (changeType == focusChangedByTabKey ? XEMBED_FOCUS_FIRST : XEMBED_FOCUS_CURRENT));
235 }
236 }
237
238 void focusLost (FocusChangeType)
239 {
240 if (client != 0 && supportsXembed && wantsFocus)
241 {
242 sendXEmbedEvent (CurrentTime, XEMBED_FOCUS_OUT);
243 updateKeyFocus();
244 }
245 }
246
247 void broughtToFront()
248 {
249 if (client != 0 && supportsXembed)
250 sendXEmbedEvent (CurrentTime, XEMBED_WINDOW_ACTIVATE);
251 }
252
253 unsigned long getHostWindowID()
254 {
255 // You are using the client initiated version of the protocol. You cannot
256 // retrieve the window id of the host. Please read the documentation for
257 // the XEmebedComponent class.
258 jassert (! clientInitiated);
259
260 return host;
261 }
262
263 void updateEmbeddedBounds()
264 {
265 componentMovedOrResized (owner, true, true);
266 }
267
268private:
269 //==============================================================================
270 XEmbedComponent& owner;
271 Window client = 0, host = 0;
272 Atom infoAtom, messageTypeAtom;
273
274 bool clientInitiated;
275 bool wantsFocus = false;
276 bool allowResize = false;
277 bool supportsXembed = false;
278 bool hasBeenMapped = false;
279 int xembedVersion = maxXEmbedVersionToSupport;
280
281 ComponentPeer* lastPeer = nullptr;
282 SharedKeyWindow::Ptr keyWindow;
283
284 //==============================================================================
285 void componentParentHierarchyChanged (Component&) override { peerChanged (owner.getPeer()); }
286 void componentMovedOrResized (Component&, bool, bool) override
287 {
288 if (host != 0 && lastPeer != nullptr)
289 {
290 auto dpy = getDisplay();
291 auto newBounds = getX11BoundsFromJuce();
293
294 if (X11Symbols::getInstance()->xGetWindowAttributes (dpy, host, &attr))
295 {
296 Rectangle<int> currentBounds (attr.x, attr.y, attr.width, attr.height);
297 if (currentBounds != newBounds)
298 {
299 X11Symbols::getInstance()->xMoveResizeWindow (dpy, host, newBounds.getX(), newBounds.getY(),
300 static_cast<unsigned int> (newBounds.getWidth()),
301 static_cast<unsigned int> (newBounds.getHeight()));
302 }
303 }
304
305 if (client != 0 && X11Symbols::getInstance()->xGetWindowAttributes (dpy, client, &attr))
306 {
307 Rectangle<int> currentBounds (attr.x, attr.y, attr.width, attr.height);
308
309 if ((currentBounds.getWidth() != newBounds.getWidth()
310 || currentBounds.getHeight() != newBounds.getHeight()))
311 {
312 X11Symbols::getInstance()->xMoveResizeWindow (dpy, client, 0, 0,
313 static_cast<unsigned int> (newBounds.getWidth()),
314 static_cast<unsigned int> (newBounds.getHeight()));
315 }
316 }
317 }
318 }
319
320 //==============================================================================
321 void createHostWindow()
322 {
323 auto dpy = getDisplay();
324 int defaultScreen = X11Symbols::getInstance()->xDefaultScreen (dpy);
325 Window root = X11Symbols::getInstance()->xRootWindow (dpy, defaultScreen);
326
328 swa.border_pixel = 0;
329 swa.background_pixmap = None;
330 swa.override_redirect = True;
332
333 host = X11Symbols::getInstance()->xCreateWindow (dpy, root, 0, 0, 1, 1, 0, CopyFromParent,
336 &swa);
337 }
338
339 void removeClient()
340 {
341 if (client != 0)
342 {
343 auto dpy = getDisplay();
344 X11Symbols::getInstance()->xSelectInput (dpy, client, 0);
345
346 keyWindow = nullptr;
347
348 int defaultScreen = X11Symbols::getInstance()->xDefaultScreen (dpy);
349 Window root = X11Symbols::getInstance()->xRootWindow (dpy, defaultScreen);
350
351 if (hasBeenMapped)
352 {
353 X11Symbols::getInstance()->xUnmapWindow (dpy, client);
354 hasBeenMapped = false;
355 }
356
357 X11Symbols::getInstance()->xReparentWindow (dpy, client, root, 0, 0);
358 client = 0;
359
360 X11Symbols::getInstance()->xSync (dpy, False);
361 }
362 }
363
364 void updateMapping()
365 {
366 if (client != 0)
367 {
368 const bool shouldBeMapped = getXEmbedMappedFlag();
369
370 if (shouldBeMapped != hasBeenMapped)
371 {
372 hasBeenMapped = shouldBeMapped;
373
374 if (shouldBeMapped)
375 X11Symbols::getInstance()->xMapWindow (getDisplay(), client);
376 else
377 X11Symbols::getInstance()->xUnmapWindow (getDisplay(), client);
378 }
379 }
380 }
381
382 Window getParentX11Window()
383 {
384 if (auto* peer = owner.getPeer())
385 return reinterpret_cast<Window> (peer->getNativeHandle());
386
387 return {};
388 }
389
390 Display* getDisplay() { return XWindowSystem::getInstance()->getDisplay(); }
391
392 //==============================================================================
393 bool getXEmbedMappedFlag()
394 {
395 XWindowSystemUtilities::GetXProperty embedInfo (getDisplay(), client, infoAtom, 0, 2, false, infoAtom);
396
397 if (embedInfo.success && embedInfo.actualFormat == 32
398 && embedInfo.numItems >= 2 && embedInfo.data != nullptr)
399 {
400 long version;
401 memcpy (&version, embedInfo.data, sizeof (long));
402
403 supportsXembed = true;
404 xembedVersion = jmin ((int) maxXEmbedVersionToSupport, (int) version);
405
406 long flags;
407 memcpy (&flags, embedInfo.data + sizeof (long), sizeof (long));
408
409 return ((flags & XEMBED_MAPPED) != 0);
410 }
411 else
412 {
413 supportsXembed = false;
414 xembedVersion = maxXEmbedVersionToSupport;
415 }
416
417 return true;
418 }
419
420 //==============================================================================
421 void propertyChanged (const Atom& a)
422 {
423 if (a == infoAtom)
424 updateMapping();
425 }
426
427 void configureNotify()
428 {
430 auto dpy = getDisplay();
431
432 if (X11Symbols::getInstance()->xGetWindowAttributes (dpy, client, &attr))
433 {
435
436 if (X11Symbols::getInstance()->xGetWindowAttributes (dpy, host, &hostAttr))
437 if (attr.width != hostAttr.width || attr.height != hostAttr.height)
438 X11Symbols::getInstance()->xResizeWindow (dpy, host, (unsigned int) attr.width, (unsigned int) attr.height);
439
440 // as the client window is not on any screen yet, we need to guess
441 // on which screen it might appear to get a scaling factor :-(
442 auto& displays = Desktop::getInstance().getDisplays();
443 auto* peer = owner.getPeer();
444 const double scale = (peer != nullptr ? peer->getPlatformScaleFactor()
445 : displays.getPrimaryDisplay()->scale);
446
447 Point<int> topLeftInPeer
448 = (peer != nullptr ? peer->getComponent().getLocalPoint (&owner, Point<int> (0, 0))
449 : owner.getBounds().getTopLeft());
450
451 Rectangle<int> newBounds (topLeftInPeer.getX(), topLeftInPeer.getY(),
452 static_cast<int> (static_cast<double> (attr.width) / scale),
453 static_cast<int> (static_cast<double> (attr.height) / scale));
454
455
456 if (peer != nullptr)
457 newBounds = owner.getLocalArea (&peer->getComponent(), newBounds);
458
459 jassert (newBounds.getX() == 0 && newBounds.getY() == 0);
460
461 if (newBounds != owner.getLocalBounds())
462 owner.setSize (newBounds.getWidth(), newBounds.getHeight());
463 }
464 }
465
466 void peerChanged (ComponentPeer* newPeer)
467 {
468 if (newPeer != lastPeer)
469 {
470 if (lastPeer != nullptr)
471 keyWindow = nullptr;
472
473 auto dpy = getDisplay();
474 Window rootWindow = X11Symbols::getInstance()->xRootWindow (dpy, DefaultScreen (dpy));
475 Rectangle<int> newBounds = getX11BoundsFromJuce();
476
477 if (newPeer == nullptr)
478 X11Symbols::getInstance()->xUnmapWindow (dpy, host);
479
480 Window newParent = (newPeer != nullptr ? getParentX11Window() : rootWindow);
481 X11Symbols::getInstance()->xReparentWindow (dpy, host, newParent, newBounds.getX(), newBounds.getY());
482
483 lastPeer = newPeer;
484
485 if (newPeer != nullptr)
486 {
487 if (wantsFocus)
488 {
489 keyWindow = SharedKeyWindow::getKeyWindowForPeer (newPeer);
490 updateKeyFocus();
491 }
492
493 componentMovedOrResized (owner, true, true);
494 X11Symbols::getInstance()->xMapWindow (dpy, host);
495
496 broughtToFront();
497 }
498 }
499 }
500
501 void updateKeyFocus()
502 {
503 if (lastPeer != nullptr && lastPeer->isFocused())
504 X11Symbols::getInstance()->xSetInputFocus (getDisplay(), getCurrentFocusWindow (lastPeer), RevertToParent, CurrentTime);
505 }
506
507 //==============================================================================
508 void handleXembedCmd (const ::Time& /*xTime*/, long opcode, long /*detail*/, long /*data1*/, long /*data2*/)
509 {
510 switch (opcode)
511 {
512 case XEMBED_REQUEST_FOCUS:
513 if (wantsFocus)
514 owner.grabKeyboardFocus();
515 break;
516
517 case XEMBED_FOCUS_NEXT:
518 if (wantsFocus)
519 owner.moveKeyboardFocusToSibling (true);
520 break;
521
522 case XEMBED_FOCUS_PREV:
523 if (wantsFocus)
524 owner.moveKeyboardFocusToSibling (false);
525 break;
526
527 default:
528 break;
529 }
530 }
531
532 bool handleX11Event (const XEvent& e)
533 {
534 if (e.xany.window == client && client != 0)
535 {
536 switch (e.type)
537 {
538 case PropertyNotify:
539 propertyChanged (e.xproperty.atom);
540 return true;
541
542 case ConfigureNotify:
543 if (allowResize)
544 configureNotify();
545 else
546 MessageManager::callAsync ([this] {componentMovedOrResized (owner, true, true);});
547
548 return true;
549
550 default:
551 break;
552 }
553 }
554 else if (e.xany.window == host && host != 0)
555 {
556 switch (e.type)
557 {
558 case ReparentNotify:
559 if (e.xreparent.parent == host && e.xreparent.window != client)
560 {
561 setClient (e.xreparent.window, false);
562 return true;
563 }
564 break;
565
566 case CreateNotify:
567 if (e.xcreatewindow.parent != e.xcreatewindow.window && e.xcreatewindow.parent == host && e.xcreatewindow.window != client)
568 {
569 setClient (e.xcreatewindow.window, false);
570 return true;
571 }
572 break;
573
574 case GravityNotify:
575 componentMovedOrResized (owner, true, true);
576 return true;
577
578 case ClientMessage:
579 if (e.xclient.message_type == messageTypeAtom && e.xclient.format == 32)
580 {
581 handleXembedCmd ((::Time) e.xclient.data.l[0], e.xclient.data.l[1],
582 e.xclient.data.l[2], e.xclient.data.l[3],
583 e.xclient.data.l[4]);
584
585 return true;
586 }
587 break;
588
589 default:
590 break;
591 }
592 }
593
594 return false;
595 }
596
597 void sendXEmbedEvent (const ::Time& xTime, long opcode,
598 long opcodeMinor = 0, long data1 = 0, long data2 = 0)
599 {
601 auto dpy = getDisplay();
602
603 ::memset (&msg, 0, sizeof (XClientMessageEvent));
604 msg.window = client;
605 msg.type = ClientMessage;
606 msg.message_type = messageTypeAtom;
607 msg.format = 32;
608 msg.data.l[0] = (long) xTime;
609 msg.data.l[1] = opcode;
610 msg.data.l[2] = opcodeMinor;
611 msg.data.l[3] = data1;
612 msg.data.l[4] = data2;
613
614 X11Symbols::getInstance()->xSendEvent (dpy, client, False, NoEventMask, (XEvent*) &msg);
615 X11Symbols::getInstance()->xSync (dpy, False);
616 }
617
618 Rectangle<int> getX11BoundsFromJuce()
619 {
620 if (auto* peer = owner.getPeer())
621 {
622 auto r = peer->getComponent().getLocalArea (&owner, owner.getLocalBounds());
623 return r * peer->getPlatformScaleFactor() * peer->getComponent().getDesktopScaleFactor();
624 }
625
626 return owner.getLocalBounds();
627 }
628
629 //==============================================================================
630 friend bool juce::juce_handleXEmbedEvent (ComponentPeer*, void*);
631 friend unsigned long juce::juce_getCurrentFocusWindow (ComponentPeer*);
632
633 static Array<Pimpl*>& getWidgets()
634 {
635 static Array<Pimpl*> i;
636 return i;
637 }
638
639 static bool dispatchX11Event (ComponentPeer* p, const XEvent* eventArg)
640 {
641 if (eventArg != nullptr)
642 {
643 auto& e = *eventArg;
644
645 if (auto w = e.xany.window)
646 for (auto* widget : getWidgets())
647 if (w == widget->host || w == widget->client)
648 return widget->handleX11Event (e);
649 }
650 else
651 {
652 for (auto* widget : getWidgets())
653 if (widget->owner.getPeer() == p)
654 widget->peerChanged (nullptr);
655 }
656
657 return false;
658 }
659
660 static Window getCurrentFocusWindow (ComponentPeer* p)
661 {
662 if (p != nullptr)
663 {
664 for (auto* widget : getWidgets())
665 if (widget->owner.getPeer() == p && widget->owner.hasKeyboardFocus (false))
666 return widget->client;
667 }
668
669 return SharedKeyWindow::getCurrentFocusWindow (p);
670 }
671};
672
673//==============================================================================
679
685
687
689{
690 g.fillAll (Colours::lightgrey);
691}
692
695void XEmbedComponent::broughtToFront() { pimpl->broughtToFront(); }
696unsigned long XEmbedComponent::getHostWindowID() { return pimpl->getHostWindowID(); }
697void XEmbedComponent::removeClient() { pimpl->setClient (0, true); }
698void XEmbedComponent::updateEmbeddedBounds() { pimpl->updateEmbeddedBounds(); }
699
700//==============================================================================
701bool juce_handleXEmbedEvent (ComponentPeer* p, void* e)
702{
703 return XEmbedComponent::Pimpl::dispatchX11Event (p, reinterpret_cast<const XEvent*> (e));
704}
705
706unsigned long juce_getCurrentFocusWindow (ComponentPeer* peer)
707{
708 return (unsigned long) XEmbedComponent::Pimpl::getCurrentFocusWindow (peer);
709}
710
711} // namespace juce
Gets informed about changes to a component's hierarchy or position.
The Component class uses a ComponentPeer internally to create and manage a real operating-system wind...
void removeComponentListener(ComponentListener *listenerToRemove)
Removes a component listener.
void addComponentListener(ComponentListener *newListener)
Adds a listener to be told about changes to the component hierarchy or position.
FocusChangeType
Enumeration used by the focusGained() and focusLost() methods.
@ focusChangedByTabKey
Means that the user pressed the tab key to move the focus.
bool hasKeyboardFocus(bool trueIfChildIsFocused) const
Returns true if this component currently has the keyboard focus.
void setOpaque(bool shouldBeOpaque)
Indicates whether any parts of the component might be transparent.
Rectangle< int > getBounds() const noexcept
Returns this component's bounding box.
Component() noexcept
Creates a component.
void setWantsKeyboardFocus(bool wantsFocus) noexcept
Sets a flag to indicate whether this component wants keyboard focus or not.
ComponentPeer * getPeer() const
Returns the heavyweight window that contains this component.
const Displays & getDisplays() const noexcept
Returns the Displays object representing the connected displays.
static Desktop &JUCE_CALLTYPE getInstance()
There's only one desktop object, and this method will return it.
A graphics context, used for drawing a component or image.
void fillAll() const
Fills the context's entire clip region with the current colour or brush.
Holds a set of mappings between some key/value pairs.
static bool callAsync(std::function< void()> functionToCall)
Asynchronously invokes a function or C++11 lambda on the message thread.
A base class which provides methods for reference-counting.
A Linux-specific class that can embed a foreign X11 widget.
unsigned long getHostWindowID()
Use this method to retrieve the host's window id when using the host initiated version of the XEmbedP...
~XEmbedComponent() override
Destructor.
void focusLost(FocusChangeType) override
Called to indicate that this component has just lost the keyboard focus.
void removeClient()
Removes the client window from the host.
void paint(Graphics &) override
Components can override this method to draw their content.
XEmbedComponent(bool wantsKeyboardFocus=true, bool allowForeignWidgetToResizeComponent=false)
Creates a JUCE component wrapping a foreign widget.
void broughtToFront() override
Called when this component has been moved to the front of its siblings.
void updateEmbeddedBounds()
Forces the embedded window to match the current size of this component.
void focusGained(FocusChangeType) override
Called to indicate that this component has just acquired the keyboard focus.
#define jassert(expression)
Platform-independent assertion macro.
memcpy
JUCE Namespace.
constexpr Type jmin(Type a, Type b)
Returns the smaller of two values.
Type unalignedPointerCast(void *ptr) noexcept
Casts a pointer to another type via void*, which suppresses the cast-align warning which sometimes ar...
Definition juce_Memory.h:88
typedef long