mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
Added support for embedding native X11 widgets (such as gtk_plug or QX11EmbedWidget) on linux
This commit is contained in:
parent
9c62eaf4f8
commit
c69d24fa7a
7 changed files with 934 additions and 22 deletions
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
|
||||
|
|
|
|||
|
|
@ -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
modules/juce_gui_extra/embedding/juce_XEmbedComponent.h
Normal file
78
modules/juce_gui_extra/embedding/juce_XEmbedComponent.h
Normal 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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
modules/juce_gui_extra/native/juce_linux_XEmbedComponent.cpp
Normal file
700
modules/juce_gui_extra/native/juce_linux_XEmbedComponent.cpp
Normal 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…
Add table
Add a link
Reference in a new issue