Browse Source

Fixed a bug in the linux XEmbedComponent and added support for host initiated embedding

tags/2021-05-28
hogliux 8 years ago
parent
commit
ec21d0464d
2 changed files with 98 additions and 20 deletions
  1. +34
    -5
      modules/juce_gui_extra/embedding/juce_XEmbedComponent.h
  2. +64
    -15
      modules/juce_gui_extra/native/juce_linux_XEmbedComponent.cpp

+ 34
- 5
modules/juce_gui_extra/embedding/juce_XEmbedComponent.h View File

@@ -38,11 +38,22 @@ unsigned long juce_getCurrentFocusWindow (ComponentPeer*);
Use this class to embed a foreign X11 widget from other toolkits such as
GTK+ or QT.
For GTK+, create a gtk_plug container and pass the plug's id
(gtk_plug_get_id) to the constructor of this class.
There are two ways to initiate the Xembed protocol. Either the client creates
a window and passes this to the host (client initiated) or the host
creates a window in which the client can reparent it's client widget
(host initiated). XEmbedComponent supports both protocol types.
For QT, use the QX11EmbedWidget class and pass the widget's
id (containerWinId()) to the constructor of this class.
This is how you embed a GTK+ widget: if you are using the client
initiated version of the protocol, then create a new gtk widget with
gtk_plug_new (0). Then query the window id of the plug via gtk_plug_get_id().
Pass this id to the constructor of this class.
If you are using the host initiated version of the protocol, then first create
the XEmbedComponent using the default constructor. Use getHostWindowID to get
the window id of the host, use this to construct your gtk plug via gtk_plug_new.
A similar approach can be used to embed QT widgets via QT's QX11EmbedWidget
class.
Other toolkits or raw X11 widgets should follow the X11 embed protocol:
https://specifications.freedesktop.org/xembed-spec/xembed-spec-latest.html
@@ -52,12 +63,30 @@ class XEmbedComponent : public Component
public:
//==============================================================================
/** Create a JUCE component wrapping the foreign widget with id wID */
/** Creates a JUCE component wrapping a foreign widget
Use this constructor if you are using the host initiated version
of the XEmbedProtocol. When using this version of the protocol
you must call getHostWindowID() and pass this id to the foreign toolkit.
*/
XEmbedComponent (bool wantsKeyboardFocus = true);
/** Create a JUCE component wrapping the foreign widget with id wID
Use this constructor if you are using the client initiated version
of the XEmbedProtocol.
*/
XEmbedComponent (unsigned long wID, bool wantsKeyboardFocus = true);
/** Destructor. */
~XEmbedComponent();
/** Use this method to retrieve the host's window id when using the
host initiated version of the XEmbedProtocol
*/
unsigned long getHostWindowID();
protected:
//==============================================================================
/** @internal */


+ 64
- 15
modules/juce_gui_extra/native/juce_linux_XEmbedComponent.cpp View File

@@ -203,8 +203,9 @@ public:
public:
//==============================================================================
Pimpl (XEmbedComponent& parent, Window x11Window, bool wantsKeyboardFocus)
: owner (parent), atoms (x11display.get()), wantsFocus (wantsKeyboardFocus)
Pimpl (XEmbedComponent& parent, Window x11Window, bool wantsKeyboardFocus, bool isClientInitiated)
: owner (parent), atoms (x11display.get()), clientInitiated (isClientInitiated),
wantsFocus (wantsKeyboardFocus)
{
if (widgets == nullptr)
widgets = new Array<Pimpl*>;
@@ -212,7 +213,9 @@ public:
widgets->add (this);
createHostWindow();
setClient (x11Window);
if (clientInitiated)
setClient (x11Window, true);
owner.setWantsKeyboardFocus (wantsFocus);
owner.addComponentListener (this);
@@ -221,7 +224,7 @@ public:
~Pimpl()
{
owner.removeComponentListener (this);
setClient (0);
setClient (0, true);
if (host != 0)
{
@@ -253,21 +256,34 @@ public:
}
}
//==============================================================================
void setClient (Window xembedClient)
void setClient (Window xembedClient, bool shouldReparent)
{
removeClient();
if (xembedClient != 0)
{
Display* dpy = getDisplay();
client = xembedClient;
configureNotify();
// if the client has initiated the component then keep the clients size
// otherwise the client should use the host's window' size
if (clientInitiated)
{
configureNotify();
}
else
{
Rectangle<int> newBounds = getX11BoundsFromJuce();
XResizeWindow (dpy, client, static_cast<unsigned int> (newBounds.getWidth()),
static_cast<unsigned int> (newBounds.getHeight()));
}
Display* dpy = getDisplay();
XSelectInput (dpy, client, StructureNotifyMask | PropertyChangeMask | FocusChangeMask);
getXEmbedMappedFlag();
XReparentWindow (dpy, client, host, 0, 0);
if (shouldReparent)
XReparentWindow (dpy, client, host, 0, 0);
if (supportsXembed)
sendXEmbedEvent (CurrentTime, XEMBED_EMBEDDED_NOTIFY, 0, (long) host, xembedVersion);
@@ -301,6 +317,16 @@ public:
sendXEmbedEvent (CurrentTime, XEMBED_WINDOW_ACTIVATE);
}
unsigned long getHostWindowID()
{
// You are using the client initiated version of the protocol. You cannot
// retrieve the window id of the host. Please read the documentation for
// the XEmebedComponent class.
jassert (! clientInitiated);
return host;
}
private:
//==============================================================================
XEmbedComponent& owner;
@@ -309,6 +335,7 @@ private:
ScopedXDisplay x11display;
Atoms atoms;
bool clientInitiated;
bool wantsFocus = false;
bool supportsXembed = false;
bool hasBeenMapped = false;
@@ -321,7 +348,7 @@ private:
void componentParentHierarchyChanged (Component&) override { peerChanged (owner.getPeer()); }
void componentMovedOrResized (Component&, bool, bool) override
{
if (client != 0 && lastPeer != nullptr)
if (host != 0 && lastPeer != nullptr)
{
Display* dpy = getDisplay();
Rectangle<int> newBounds = getX11BoundsFromJuce();
@@ -336,8 +363,8 @@ private:
static_cast<unsigned int> (newBounds.getWidth()),
static_cast<unsigned int> (newBounds.getHeight()));
if (currentBounds.getWidth() != newBounds.getWidth()
|| currentBounds.getHeight() != newBounds.getHeight())
if (client != 0 && (currentBounds.getWidth() != newBounds.getWidth()
|| currentBounds.getHeight() != newBounds.getHeight()))
XResizeWindow (dpy, client,
static_cast<unsigned int> (newBounds.getWidth()),
static_cast<unsigned int> (newBounds.getHeight()));
@@ -357,7 +384,7 @@ private:
swa.border_pixel = 0;
swa.background_pixmap = None;
swa.override_redirect = True;
swa.event_mask = StructureNotifyMask | FocusChangeMask;
swa.event_mask = SubstructureNotifyMask | StructureNotifyMask | FocusChangeMask;
host = XCreateWindow (dpy, root, 0, 0, 1, 1, 0, CopyFromParent,
InputOutput, CopyFromParent,
@@ -547,7 +574,7 @@ private:
bool handleX11Event (const XEvent& e)
{
if (e.xany.window == client)
if (e.xany.window == client && client != 0)
{
switch (e.type)
{
@@ -559,10 +586,24 @@ private:
return true;
}
}
else if (e.xany.window == host)
else if (e.xany.window == host && host != 0)
{
switch (e.type)
{
case ReparentNotify:
if (e.xreparent.parent == host && e.xreparent.window != client)
{
setClient (e.xreparent.window, false);
return true;
}
break;
case CreateNotify:
if (e.xcreatewindow.parent != e.xcreatewindow.window && e.xcreatewindow.parent == host && e.xcreatewindow.window != client)
{
setClient (e.xcreatewindow.window, false);
return true;
}
break;
case GravityNotify:
componentMovedOrResized (owner, true, true);
return true;
@@ -576,6 +617,7 @@ private:
return true;
}
break;
}
}
@@ -671,8 +713,14 @@ Array<XEmbedComponent::Pimpl*>* XEmbedComponent::Pimpl::widgets = nullptr;
HashMap<ComponentPeer*,XEmbedComponent::Pimpl::SharedKeyWindow*>* XEmbedComponent::Pimpl::SharedKeyWindow::keyWindows = nullptr;
//==============================================================================
XEmbedComponent::XEmbedComponent (bool wantsKeyboardFocus)
: pimpl (new Pimpl (*this, 0, wantsKeyboardFocus, false))
{
setOpaque (true);
}
XEmbedComponent::XEmbedComponent (unsigned long wID, bool wantsKeyboardFocus)
: pimpl (new Pimpl (*this, wID, wantsKeyboardFocus))
: pimpl (new Pimpl (*this, wID, wantsKeyboardFocus, true))
{
setOpaque (true);
}
@@ -687,6 +735,7 @@ void XEmbedComponent::paint (Graphics& g)
void XEmbedComponent::focusGained (FocusChangeType changeType) { pimpl->focusGained (changeType); }
void XEmbedComponent::focusLost (FocusChangeType changeType) { pimpl->focusLost (changeType); }
void XEmbedComponent::broughtToFront() { pimpl->broughtToFront(); }
unsigned long XEmbedComponent::getHostWindowID() { return pimpl->getHostWindowID(); }
//==============================================================================
bool juce_handleXEmbedEvent (ComponentPeer* p, void* e)


Loading…
Cancel
Save