Browse Source

Added support for embedding native X11 widgets (such as gtk_plug or QX11EmbedWidget) on linux

tags/2021-05-28
hogliux 8 years ago
parent
commit
c69d24fa7a
7 changed files with 934 additions and 22 deletions
  1. +3
    -0
      modules/juce_gui_basics/native/juce_linux_X11.cpp
  2. +1
    -0
      modules/juce_gui_basics/native/juce_linux_X11.h
  3. +150
    -22
      modules/juce_gui_basics/native/juce_linux_X11_Windowing.cpp
  4. +78
    -0
      modules/juce_gui_extra/embedding/juce_XEmbedComponent.h
  5. +1
    -0
      modules/juce_gui_extra/juce_gui_extra.cpp
  6. +1
    -0
      modules/juce_gui_extra/juce_gui_extra.h
  7. +700
    -0
      modules/juce_gui_extra/native/juce_linux_XEmbedComponent.cpp

+ 3
- 0
modules/juce_gui_basics/native/juce_linux_X11.cpp View File

@@ -279,6 +279,9 @@ Atoms::Atoms(::Display* display)
XdndActionPrivate = getCreating (display, "XdndActionPrivate");
XdndActionDescription = getCreating (display, "XdndActionDescription");
XembedMsgType = getCreating (display, "_XEMBED");
XembedInfo = getCreating (display, "_XEMBED_INFO");
allowedMimeTypes[0] = getCreating (display, "UTF8_STRING");
allowedMimeTypes[1] = getCreating (display, "text/plain;charset=utf-8");
allowedMimeTypes[2] = getCreating (display, "text/plain");


+ 1
- 0
modules/juce_gui_basics/native/juce_linux_X11.h View File

@@ -103,6 +103,7 @@ struct Atoms
XdndAware, XdndEnter, XdndLeave, XdndPosition, XdndStatus,
XdndDrop, XdndFinished, XdndSelection, XdndTypeList, XdndActionList,
XdndActionDescription, XdndActionCopy, XdndActionPrivate,
XembedMsgType, XembedInfo,
allowedActions[5],
allowedMimeTypes[4];


+ 150
- 22
modules/juce_gui_basics/native/juce_linux_X11_Windowing.cpp View File

@@ -26,6 +26,17 @@
#define JUCE_DEBUG_XERRORS 1
#endif
#if JUCE_MODULE_AVAILABLE_juce_gui_extra
#define JUCE_X11_SUPPORTS_XEMBED 1
#else
#define JUCE_X11_SUPPORTS_XEMBED 0
#endif
#if JUCE_X11_SUPPORTS_XEMBED
bool juce_handleXEmbedEvent (ComponentPeer*, void*);
unsigned long juce_getCurrentFocusWindow (ComponentPeer*);
#endif
extern WindowMessageReceiveCallback dispatchWindowMessage;
extern XContext windowHandleXContext;
@@ -1456,8 +1467,8 @@ class LinuxComponentPeer : public ComponentPeer
public:
LinuxComponentPeer (Component& comp, const int windowStyleFlags, Window parentToAddTo)
: ComponentPeer (comp, windowStyleFlags),
windowH (0), parentWindow (0),
fullScreen (false), mapped (false),
windowH (0), parentWindow (0), keyProxy (0),
fullScreen (false), mapped (false), focused (false),
visual (nullptr), depth (0),
isAlwaysOnTop (comp.isAlwaysOnTop()),
currentScaleFactor (1.0)
@@ -1484,6 +1495,10 @@ public:
// it's dangerous to delete a window on a thread other than the message thread..
jassert (MessageManager::getInstance()->currentThreadHasLockedMessageManager());
#if JUCE_X11_SUPPORTS_XEMBED
juce_handleXEmbedEvent (this, nullptr);
#endif
deleteIconPixmaps();
destroyWindow();
windowH = 0;
@@ -1718,6 +1733,33 @@ public:
return false;
}
bool isParentWindowOf (Window possibleChild) const
{
if (windowH != 0 && possibleChild != 0)
{
if (possibleChild == windowH)
return true;
Window* windowList = nullptr;
uint32 windowListSize = 0;
Window parent, root;
ScopedXLock xlock (display);
if (XQueryTree (display, possibleChild, &root, &parent, &windowList, &windowListSize) != 0)
{
if (windowList != nullptr)
XFree (windowList);
if (parent == root)
return false;
return isParentWindowOf (parent);
}
}
return false;
}
bool isFrontWindow() const
{
Window* windowList = nullptr;
@@ -1845,7 +1887,19 @@ public:
ScopedXLock xlock (display);
XGetInputFocus (display, &focusedWindow, &revert);
return focusedWindow == windowH;
return isParentWindowOf (focusedWindow);
}
Window getFocusWindow()
{
#if JUCE_X11_SUPPORTS_XEMBED
Window w = (Window) juce_getCurrentFocusWindow (this);
if (w != 0)
return w;
#endif
return windowH;
}
void grabFocus() override
@@ -1858,7 +1912,7 @@ public:
&& atts.map_state == IsViewable
&& ! isFocused())
{
XSetInputFocus (display, windowH, RevertToParent, (::Time) getUserTime());
XSetInputFocus (display, getFocusWindow(), RevertToParent, (::Time) getUserTime());
isActiveApplication = true;
}
}
@@ -2255,15 +2309,23 @@ public:
void handleFocusInEvent()
{
isActiveApplication = true;
if (isFocused())
if (isFocused() && ! focused)
{
focused = true;
handleFocusGain();
}
}
void handleFocusOutEvent()
{
isActiveApplication = false;
if (! isFocused())
if (! isFocused() && focused)
{
focused = false;
isActiveApplication = false;
handleFocusLoss();
}
}
void handleExposeEvent (XExposeEvent& exposeEvent)
@@ -2310,15 +2372,15 @@ public:
// if the native title bar is dragged, need to tell any active menus, etc.
if ((styleFlags & windowHasTitleBar) != 0
&& component.isCurrentlyBlockedByAnotherModalComponent())
&& component.isCurrentlyBlockedByAnotherModalComponent())
{
if (Component* const currentModalComp = Component::getCurrentlyModalComponent())
currentModalComp->inputAttemptWhenModal();
if (Component* const currentModalComp = Component::getCurrentlyModalComponent())
currentModalComp->inputAttemptWhenModal();
}
if (confEvent.window == windowH
&& confEvent.above != 0
&& isFrontWindow())
&& confEvent.above != 0
&& isFrontWindow())
{
handleBroughtToFront();
}
@@ -2386,7 +2448,11 @@ public:
&& XGetWindowAttributes (display, clientMsg.window, &atts))
{
if (atts.map_state == IsViewable)
XSetInputFocus (display, clientMsg.window, RevertToParent, (::Time) clientMsg.data.l[1]);
XSetInputFocus (display,
(clientMsg.window == windowH ? getFocusWindow ()
: clientMsg.window),
RevertToParent,
(::Time) clientMsg.data.l[1]);
}
}
}
@@ -2485,6 +2551,51 @@ public:
}
}
//==============================================================================
unsigned long createKeyProxy()
{
jassert (keyProxy == 0 && windowH != 0);
if (keyProxy == 0 && windowH != 0)
{
XSetWindowAttributes swa;
swa.event_mask = KeyPressMask | KeyReleaseMask | FocusChangeMask;
keyProxy = XCreateWindow (display, windowH,
-1, -1, 1, 1, 0, 0,
InputOnly, CopyFromParent,
CWEventMask,
&swa);
XMapWindow (display, keyProxy);
XSaveContext (display, (XID) keyProxy, windowHandleXContext, (XPointer) this);
}
return keyProxy;
}
void deleteKeyProxy()
{
jassert (keyProxy != 0);
if (keyProxy != 0)
{
XPointer handlePointer;
if (! XFindContext (display, (XID) keyProxy, windowHandleXContext, &handlePointer))
XDeleteContext (display, (XID) keyProxy, windowHandleXContext);
XDestroyWindow (display, keyProxy);
XSync (display, false);
XEvent event;
while (XCheckWindowEvent (display, keyProxy, getAllEventsMask(), &event) == True)
{}
keyProxy = 0;
}
}
//==============================================================================
bool dontRepaint;
@@ -2638,10 +2749,10 @@ private:
ScopedPointer<LinuxRepaintManager> repainter;
friend class LinuxRepaintManager;
Window windowH, parentWindow;
Window windowH, parentWindow, keyProxy;
Rectangle<int> bounds;
Image taskbarImage;
bool fullScreen, mapped;
bool fullScreen, mapped, focused;
Visual* visual;
int depth;
BorderSize<int> windowBorder;
@@ -2927,11 +3038,6 @@ private:
CWBorderPixel | CWColormap | CWBackPixmap | CWEventMask | CWOverrideRedirect,
&swa);
unsigned int buttonMask = EnterWindowMask | LeaveWindowMask | PointerMotionMask;
if ((styleFlags & windowIgnoresMouseClicks) == 0)
buttonMask |= ButtonPressMask | ButtonReleaseMask;
// Set the window context to identify the window handle object
if (XSaveContext (display, (XID) windowH, windowHandleXContext, (XPointer) this))
{
@@ -2984,6 +3090,10 @@ private:
ScopedXLock xlock (display);
XPointer handlePointer;
if (keyProxy != 0)
deleteKeyProxy();
if (! XFindContext (display, (XID) windowH, windowHandleXContext, &handlePointer))
XDeleteContext (display, (XID) windowH, windowHandleXContext);
@@ -3640,8 +3750,13 @@ namespace WindowingHelpers {
{
if (event.xany.window != None)
{
if (LinuxComponentPeer* const peer = LinuxComponentPeer::getPeerFor (event.xany.window))
peer->handleWindowMessage (event);
#if JUCE_X11_SUPPORTS_XEMBED
if (! juce_handleXEmbedEvent (nullptr, &event))
#endif
{
if (LinuxComponentPeer* const peer = LinuxComponentPeer::getPeerFor (event.xany.window))
peer->handleWindowMessage (event);
}
}
else if (event.xany.type == KeymapNotify)
{
@@ -3927,6 +4042,19 @@ void juce_LinuxRemoveRepaintListener (ComponentPeer* peer, Component* dummy)
linuxPeer->removeOpenGLRepaintListener (dummy);
}
unsigned long juce_createKeyProxyWindow (ComponentPeer* peer)
{
if (LinuxComponentPeer* linuxPeer = dynamic_cast<LinuxComponentPeer*> (peer))
return linuxPeer->createKeyProxy();
return 0;
}
void juce_deleteKeyProxyWindow (ComponentPeer* peer)
{
if (LinuxComponentPeer* linuxPeer = dynamic_cast<LinuxComponentPeer*> (peer))
linuxPeer->deleteKeyProxy();
}
//==============================================================================
#if JUCE_MODAL_LOOPS_PERMITTED
void JUCE_CALLTYPE NativeMessageBox::showMessageBox (AlertWindow::AlertIconType iconType,


+ 78
- 0
modules/juce_gui_extra/embedding/juce_XEmbedComponent.h View File

@@ -0,0 +1,78 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
#pragma once
/** @internal */
bool juce_handleXEmbedEvent (ComponentPeer*, void*);
/** @internal */
unsigned long juce_getCurrentFocusWindow (ComponentPeer*);
#if JUCE_LINUX || DOXYGEN
//==============================================================================
/**
A Linux-specific class that can embed a foreign X11 widget.
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.
For QT, use the QX11EmbedWidget class and pass the widget's
id (containerWinId()) to the constructor of this class.
Other toolkits or raw X11 widgets should follow the X11 embed protocol:
https://specifications.freedesktop.org/xembed-spec/xembed-spec-latest.html
*/
class XEmbedComponent : public Component
{
public:
//==============================================================================
/** Create a JUCE component wrapping the foreign widget with id wID */
XEmbedComponent (unsigned long wID, bool wantsKeyboardFocus = true);
/** Destructor. */
~XEmbedComponent();
protected:
//==============================================================================
/** @internal */
void paint (Graphics&) override;
void focusGained (FocusChangeType) override;
void focusLost (FocusChangeType) override;
void broughtToFront() override;
private:
friend bool juce::juce_handleXEmbedEvent (ComponentPeer*, void*);
friend unsigned long juce_getCurrentFocusWindow (ComponentPeer*);
class Pimpl;
friend struct ContainerDeletePolicy<Pimpl>;
ScopedPointer<Pimpl> pimpl;
};
#endif

+ 1
- 0
modules/juce_gui_extra/juce_gui_extra.cpp View File

@@ -123,6 +123,7 @@ namespace juce
//==============================================================================
#elif JUCE_LINUX
#include "native/juce_linux_XEmbedComponent.cpp"
#if JUCE_WEB_BROWSER
#include "native/juce_linux_X11_WebBrowserComponent.cpp"
#endif


+ 1
- 0
modules/juce_gui_extra/juce_gui_extra.h View File

@@ -86,6 +86,7 @@ namespace juce
#include "embedding/juce_ActiveXControlComponent.h"
#include "embedding/juce_NSViewComponent.h"
#include "embedding/juce_UIViewComponent.h"
#include "embedding/juce_XEmbedComponent.h"
#include "misc/juce_AppleRemote.h"
#include "misc/juce_BubbleMessageComponent.h"
#include "misc/juce_ColourSelector.h"


+ 700
- 0
modules/juce_gui_extra/native/juce_linux_XEmbedComponent.cpp View File

@@ -0,0 +1,700 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
Permission is granted to use this software under the terms of either:
a) the GPL v2 (or any later version)
b) the Affero GPL v3
Details of these licenses can be found at: www.gnu.org/licenses
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
------------------------------------------------------------------------------
To release a closed-source product which uses JUCE, commercial licenses are
available: visit www.juce.com for more information.
==============================================================================
*/
//==============================================================================
bool juce_handleXEmbedEvent (ComponentPeer*, void*);
Window juce_getCurrentFocusWindow (ComponentPeer*);
//==============================================================================
unsigned long juce_createKeyProxyWindow (ComponentPeer*);
void juce_deleteKeyProxyWindow (ComponentPeer*);
//==============================================================================
class XEmbedComponent::Pimpl : private ComponentListener
{
public:
//==============================================================================
enum
{
maxXEmbedVersionToSupport = 0
};
enum Flags
{
XEMBED_MAPPED = (1<<0)
};
enum
{
XEMBED_EMBEDDED_NOTIFY = 0,
XEMBED_WINDOW_ACTIVATE = 1,
XEMBED_WINDOW_DEACTIVATE = 2,
XEMBED_REQUEST_FOCUS = 3,
XEMBED_FOCUS_IN = 4,
XEMBED_FOCUS_OUT = 5,
XEMBED_FOCUS_NEXT = 6,
XEMBED_FOCUS_PREV = 7,
XEMBED_MODALITY_ON = 10,
XEMBED_MODALITY_OFF = 11,
XEMBED_REGISTER_ACCELERATOR = 12,
XEMBED_UNREGISTER_ACCELERATOR = 13,
XEMBED_ACTIVATE_ACCELERATOR = 14
};
enum
{
XEMBED_FOCUS_CURRENT = 0,
XEMBED_FOCUS_FIRST = 1,
XEMBED_FOCUS_LAST = 2
};
//==============================================================================
class SharedKeyWindow
{
public:
//==============================================================================
class Ref
{
public:
Ref() : keyWindow (nullptr) {}
Ref (Pimpl& p) { keyWindow = getKeyWindowForPeer (p.owner.getPeer()); }
~Ref() { free(); }
//==============================================================================
Ref (const Ref& o) : keyWindow (o.keyWindow) { if (keyWindow != nullptr) keyWindow->numRefs++; }
Ref (Ref && o) : keyWindow (o.keyWindow) { o.keyWindow = nullptr; }
Ref (std::nullptr_t) : keyWindow (nullptr) {}
//==============================================================================
Ref& operator= (std::nullptr_t) { free(); return *this; }
Ref& operator= (const Ref& o)
{
free();
keyWindow = o.keyWindow;
if (keyWindow != nullptr)
keyWindow->numRefs++;
return *this;
}
Ref& operator= (Ref && o)
{
if (keyWindow != o.keyWindow)
{
free();
keyWindow = o.keyWindow;
}
o.keyWindow = nullptr;
return *this;
}
//==============================================================================
SharedKeyWindow& operator*() noexcept { return *keyWindow; }
SharedKeyWindow* operator->() noexcept { return keyWindow; }
//==============================================================================
bool operator== (std::nullptr_t) const noexcept { return (keyWindow == nullptr); }
bool operator!= (std::nullptr_t) const noexcept { return (keyWindow != nullptr); }
private:
//==============================================================================
void free()
{
if (keyWindow != nullptr)
{
if (--keyWindow->numRefs == 0)
delete keyWindow;
keyWindow = nullptr;
}
}
SharedKeyWindow* keyWindow;
};
public:
//==============================================================================
Window getHandle() { return keyProxy; }
static Window getCurrentFocusWindow (ComponentPeer* peerToLookFor)
{
if (keyWindows != nullptr && peerToLookFor != nullptr)
{
SharedKeyWindow* foundKeyWindow = (*keyWindows)[peerToLookFor];
if (foundKeyWindow != nullptr)
return foundKeyWindow->keyProxy;
}
return (Window)0;
}
private:
//==============================================================================
friend class Ref;
SharedKeyWindow (ComponentPeer* peerToUse)
: keyPeer (peerToUse),
keyProxy (juce_createKeyProxyWindow (keyPeer)),
numRefs (1)
{}
~SharedKeyWindow()
{
juce_deleteKeyProxyWindow (keyPeer);
if (keyWindows != nullptr)
{
keyWindows->remove (keyPeer);
if (keyWindows->size() == 0)
{
delete keyWindows;
keyWindows = nullptr;
}
}
}
ComponentPeer* keyPeer;
Window keyProxy;
int numRefs;
static SharedKeyWindow* getKeyWindowForPeer (ComponentPeer* peerToLookFor)
{
jassert (peerToLookFor != nullptr);
if (keyWindows == nullptr)
keyWindows = new HashMap<ComponentPeer*,SharedKeyWindow*>;
SharedKeyWindow* foundKeyWindow = (*keyWindows)[peerToLookFor];
if (foundKeyWindow == nullptr)
{
foundKeyWindow = new SharedKeyWindow (peerToLookFor);
keyWindows->set (peerToLookFor, foundKeyWindow);
}
return foundKeyWindow;
}
//==============================================================================
friend class Ref;
static HashMap<ComponentPeer*,SharedKeyWindow*>* keyWindows;
};
public:
//==============================================================================
Pimpl (XEmbedComponent& parent, Window x11Window, bool wantsKeyboardFocus)
: owner (parent), atoms (x11display.get()), wantsFocus (wantsKeyboardFocus)
{
if (widgets == nullptr)
widgets = new Array<Pimpl*>;
widgets->add (this);
createHostWindow();
setClient (x11Window);
owner.setWantsKeyboardFocus (wantsFocus);
owner.addComponentListener (this);
}
~Pimpl()
{
owner.removeComponentListener (this);
setClient (0);
if (host != 0)
{
Display* dpy = getDisplay();
XDestroyWindow (dpy, host);
XSync (dpy, false);
const long mask = NoEventMask | KeyPressMask | KeyReleaseMask
| EnterWindowMask | LeaveWindowMask | PointerMotionMask
| KeymapStateMask | ExposureMask | StructureNotifyMask
| FocusChangeMask;
XEvent event;
while (XCheckWindowEvent (dpy, host, mask, &event) == True)
{}
host = 0;
}
if (widgets != nullptr)
{
widgets->removeAllInstancesOf (this);
if (widgets->size() == 0)
{
delete widgets;
widgets = nullptr;
}
}
}
//==============================================================================
void setClient (Window xembedClient)
{
removeClient();
if (xembedClient != 0)
{
client = xembedClient;
configureNotify();
Display* dpy = getDisplay();
XSelectInput (dpy, client, StructureNotifyMask | PropertyChangeMask | FocusChangeMask);
getXEmbedMappedFlag();
XReparentWindow (dpy, client, host, 0, 0);
if (supportsXembed)
sendXEmbedEvent (CurrentTime, XEMBED_EMBEDDED_NOTIFY, 0, (long) host, xembedVersion);
updateMapping();
}
}
void focusGained (FocusChangeType changeType)
{
if (client != 0 && supportsXembed && wantsFocus)
{
updateKeyFocus();
sendXEmbedEvent (CurrentTime, XEMBED_FOCUS_IN,
(changeType == focusChangedByTabKey ? XEMBED_FOCUS_FIRST : XEMBED_FOCUS_CURRENT));
}
}
void focusLost (FocusChangeType)
{
if (client != 0 && supportsXembed && wantsFocus)
{
sendXEmbedEvent (CurrentTime, XEMBED_FOCUS_OUT);
updateKeyFocus();
}
}
void broughtToFront()
{
if (client != 0 && supportsXembed)
sendXEmbedEvent (CurrentTime, XEMBED_WINDOW_ACTIVATE);
}
private:
//==============================================================================
XEmbedComponent& owner;
Window client = 0, host = 0;
ScopedXDisplay x11display;
Atoms atoms;
bool wantsFocus = false;
bool supportsXembed = false;
bool hasBeenMapped = false;
int xembedVersion = maxXEmbedVersionToSupport;
ComponentPeer* lastPeer = nullptr;
SharedKeyWindow::Ref keyWindow;
//==============================================================================
void componentParentHierarchyChanged (Component&) override { peerChanged (owner.getPeer()); }
void componentMovedOrResized (Component&, bool, bool) override
{
if (client != 0 && lastPeer != nullptr)
{
Display* dpy = getDisplay();
Rectangle<int> newBounds = getX11BoundsFromJuce();
XWindowAttributes attr;
if (XGetWindowAttributes (dpy, host, &attr))
{
Rectangle<int> currentBounds (attr.x, attr.y, attr.width, attr.height);
if (currentBounds != newBounds)
{
XMoveResizeWindow (dpy, host, newBounds.getX(), newBounds.getY(),
static_cast<unsigned int> (newBounds.getWidth()),
static_cast<unsigned int> (newBounds.getHeight()));
if (currentBounds.getWidth() != newBounds.getWidth()
|| currentBounds.getHeight() != newBounds.getHeight())
XResizeWindow (dpy, client,
static_cast<unsigned int> (newBounds.getWidth()),
static_cast<unsigned int> (newBounds.getHeight()));
}
}
}
}
//==============================================================================
void createHostWindow()
{
Display* dpy = getDisplay();
int defaultScreen = XDefaultScreen (dpy);
Window root = RootWindow (dpy, defaultScreen);
XSetWindowAttributes swa;
swa.border_pixel = 0;
swa.background_pixmap = None;
swa.override_redirect = True;
swa.event_mask = StructureNotifyMask | FocusChangeMask;
host = XCreateWindow (dpy, root, 0, 0, 1, 1, 0, CopyFromParent,
InputOutput, CopyFromParent,
CWEventMask | CWBorderPixel | CWBackPixmap | CWOverrideRedirect,
&swa);
}
void removeClient()
{
if (client != 0)
{
Display* dpy = getDisplay();
XSelectInput (dpy, client, 0);
keyWindow = nullptr;
int defaultScreen = XDefaultScreen (dpy);
Window root = RootWindow (dpy, defaultScreen);
if (hasBeenMapped)
{
XUnmapWindow (dpy, client);
hasBeenMapped = false;
}
XReparentWindow (dpy, client, root, 0, 0);
client = 0;
}
}
void updateMapping()
{
if (client != 0)
{
const bool shouldBeMapped = getXEmbedMappedFlag();
if (shouldBeMapped != hasBeenMapped)
{
hasBeenMapped = shouldBeMapped;
if (shouldBeMapped)
XMapWindow (getDisplay(), client);
else
XUnmapWindow (getDisplay(), client);
}
}
}
Window getParentX11Window()
{
if (ComponentPeer* peer = owner.getPeer())
return reinterpret_cast<Window> (peer->getNativeHandle());
return 0;
}
Display* getDisplay() { return reinterpret_cast<Display*> (x11display.get()); }
//==============================================================================
bool getXEmbedMappedFlag()
{
GetXProperty embedInfo (x11display.get(), client, atoms.XembedInfo, 0, 2, false, atoms.XembedInfo);
if (embedInfo.success && embedInfo.actualFormat == 32
&& embedInfo.numItems >= 2 && embedInfo.data != nullptr)
{
long* buffer = (long*) embedInfo.data;
supportsXembed = true;
xembedVersion = jmin ((int) maxXEmbedVersionToSupport, (int) buffer[0]);
return ((buffer[1] & XEMBED_MAPPED) != 0);
}
else
{
supportsXembed = false;
xembedVersion = maxXEmbedVersionToSupport;
}
return true;
}
//==============================================================================
void propertyChanged (const Atom& a)
{
if (a == atoms.XembedInfo)
updateMapping();
}
void configureNotify()
{
XWindowAttributes attr;
Display* dpy = getDisplay();
if (XGetWindowAttributes (dpy, client, &attr))
{
XWindowAttributes hostAttr;
if (XGetWindowAttributes (dpy, host, &hostAttr))
if (attr.width != hostAttr.width || attr.height != hostAttr.height)
XResizeWindow (dpy, host, (unsigned int) attr.width, (unsigned int) attr.height);
// as the client window is not on any screen yet, we need to guess
// on which screen it might appear to get a scaling factor :-(
const Desktop::Displays& displays = Desktop::getInstance().getDisplays();
ComponentPeer* peer = owner.getPeer();
const double scale = (peer != nullptr ? displays.getDisplayContaining (peer->getBounds().getCentre())
: displays.getMainDisplay()).scale;
Point<int> topLeftInPeer
= (peer != nullptr ? peer->getComponent().getLocalPoint (&owner, Point<int> (0, 0))
: owner.getBounds().getTopLeft());
Rectangle<int> newBounds (topLeftInPeer.getX(), topLeftInPeer.getY(),
static_cast<int> (static_cast<double> (attr.width) / scale),
static_cast<int> (static_cast<double> (attr.height) / scale));
if (peer != nullptr)
newBounds = owner.getLocalArea (&peer->getComponent(), newBounds);
jassert (newBounds.getX() == 0 && newBounds.getY() == 0);
if (newBounds != owner.getLocalBounds())
owner.setSize (newBounds.getWidth(), newBounds.getHeight());
}
}
void peerChanged (ComponentPeer* newPeer)
{
if (newPeer != lastPeer)
{
if (lastPeer != nullptr)
keyWindow = nullptr;
Display* dpy = getDisplay();
Window rootWindow = RootWindow (dpy, DefaultScreen (dpy));
Rectangle<int> newBounds = getX11BoundsFromJuce();
if (newPeer == nullptr)
XUnmapWindow (dpy, host);
Window newParent = (newPeer != nullptr ? getParentX11Window() : rootWindow);
XReparentWindow (dpy, host, newParent, newBounds.getX(), newBounds.getY());
lastPeer = newPeer;
if (newPeer != nullptr)
{
if (wantsFocus)
{
keyWindow = SharedKeyWindow::Ref (*this);
updateKeyFocus();
}
componentMovedOrResized (owner, true, true);
XMapWindow (dpy, host);
broughtToFront();
}
}
}
void updateKeyFocus()
{
if (lastPeer != nullptr && lastPeer->isFocused())
XSetInputFocus (getDisplay(), getCurrentFocusWindow (lastPeer), RevertToParent, CurrentTime);
}
//==============================================================================
void handleXembedCmd (const ::Time& /*xTime*/, long opcode, long /*detail*/, long /*data1*/, long /*data2*/)
{
switch (opcode)
{
case XEMBED_REQUEST_FOCUS:
if (wantsFocus)
owner.grabKeyboardFocus();
break;
case XEMBED_FOCUS_NEXT:
if (wantsFocus)
owner.moveKeyboardFocusToSibling (true);
break;
case XEMBED_FOCUS_PREV:
if (wantsFocus)
owner.moveKeyboardFocusToSibling (false);
break;
}
}
bool handleX11Event (const XEvent& e)
{
if (e.xany.window == client)
{
switch (e.type)
{
case PropertyNotify:
propertyChanged (e.xproperty.atom);
return true;
case ConfigureNotify:
configureNotify();
return true;
}
}
else if (e.xany.window == host)
{
switch (e.type)
{
case GravityNotify:
componentMovedOrResized (owner, true, true);
return true;
case ClientMessage:
if (e.xclient.message_type == atoms.XembedMsgType && e.xclient.format == 32)
{
handleXembedCmd ((::Time) e.xclient.data.l[0], e.xclient.data.l[1],
e.xclient.data.l[2], e.xclient.data.l[3],
e.xclient.data.l[4]);
return true;
}
break;
}
}
return false;
}
void sendXEmbedEvent (const ::Time& xTime, long opcode,
long opcodeMinor = 0, long data1 = 0, long data2 = 0)
{
XClientMessageEvent msg;
Display* dpy = getDisplay();
::memset (&msg, 0, sizeof (XClientMessageEvent));
msg.window = client;
msg.type = ClientMessage;
msg.message_type = atoms.XembedMsgType;
msg.format = 32;
msg.data.l[0] = (long) xTime;
msg.data.l[1] = opcode;
msg.data.l[2] = opcodeMinor;
msg.data.l[3] = data1;
msg.data.l[4] = data2;
XSendEvent (dpy, client, False, NoEventMask, (XEvent*) &msg);
XSync (dpy, False);
}
Rectangle<int> getX11BoundsFromJuce()
{
if (ComponentPeer* peer = owner.getPeer())
{
Rectangle<int> r
= peer->getComponent().getLocalArea (&owner, owner.getLocalBounds());
const double scale
= Desktop::getInstance().getDisplays().getDisplayContaining (peer->localToGlobal (r.getCentre())).scale;
return r * scale;
}
return owner.getLocalBounds();
}
//==============================================================================
friend bool juce::juce_handleXEmbedEvent (ComponentPeer*, void*);
friend unsigned long juce::juce_getCurrentFocusWindow (ComponentPeer*);
static Array<Pimpl*>* widgets;
static bool dispatchX11Event (ComponentPeer* p, const XEvent* eventArg)
{
if (widgets != nullptr)
{
if (eventArg != nullptr)
{
const XEvent& e = *eventArg;
Window w = e.xany.window;
if (w == 0) return false;
for (auto && widget : *widgets)
if (w == widget->host || w == widget->client)
return widget->handleX11Event (e);
}
else
{
for (auto && widget : *widgets)
{
if (widget->owner.getPeer() == p)
widget->peerChanged (nullptr);
}
}
}
return false;
}
static Window getCurrentFocusWindow (ComponentPeer* p)
{
if (widgets != nullptr && p != nullptr)
{
for (auto && widget : *widgets)
if (widget->owner.getPeer() == p && widget->owner.hasKeyboardFocus (false))
return widget->client;
}
return SharedKeyWindow::getCurrentFocusWindow (p);
}
};
//==============================================================================
Array<XEmbedComponent::Pimpl*>* XEmbedComponent::Pimpl::widgets = nullptr;
HashMap<ComponentPeer*,XEmbedComponent::Pimpl::SharedKeyWindow*>* XEmbedComponent::Pimpl::SharedKeyWindow::keyWindows = nullptr;
//==============================================================================
XEmbedComponent::XEmbedComponent (unsigned long wID, bool wantsKeyboardFocus)
: pimpl (new Pimpl (*this, wID, wantsKeyboardFocus))
{
setOpaque (true);
}
XEmbedComponent::~XEmbedComponent() {}
void XEmbedComponent::paint (Graphics& g)
{
g.fillAll (Colours::lightgrey);
}
void XEmbedComponent::focusGained (FocusChangeType changeType) { pimpl->focusGained (changeType); }
void XEmbedComponent::focusLost (FocusChangeType changeType) { pimpl->focusLost (changeType); }
void XEmbedComponent::broughtToFront() { pimpl->broughtToFront(); }
//==============================================================================
bool juce_handleXEmbedEvent (ComponentPeer* p, void* e)
{
return ::XEmbedComponent::Pimpl::dispatchX11Event (p, reinterpret_cast<const XEvent*> (e));
}
unsigned long juce_getCurrentFocusWindow (ComponentPeer* peer)
{
return (unsigned long) ::XEmbedComponent::Pimpl::getCurrentFocusWindow (peer);
}

Loading…
Cancel
Save