From f9dad9d608326d721a7890de88bfccecd4b66ca2 Mon Sep 17 00:00:00 2001 From: ed Date: Thu, 27 Jun 2019 14:21:44 +0100 Subject: [PATCH] Linux: Improved XEmbed support This commit makes some minor improvements to the XEmbedComponent class and adds support for embedding JUCE windows in other hosts using the XEmbed protocol. It also includes some minor fixes for X11 peers when they have a parent window. --- .../juce_gui_basics/native/juce_linux_X11.h | 35 ++++++ .../native/juce_linux_X11_Windowing.cpp | 105 ++++++++++-------- .../native/juce_linux_XEmbedComponent.cpp | 45 ++------ 3 files changed, 103 insertions(+), 82 deletions(-) diff --git a/modules/juce_gui_basics/native/juce_linux_X11.h b/modules/juce_gui_basics/native/juce_linux_X11.h index f5c5cdd98e..a6a5b4f371 100644 --- a/modules/juce_gui_basics/native/juce_linux_X11.h +++ b/modules/juce_gui_basics/native/juce_linux_X11.h @@ -139,4 +139,39 @@ struct GetXProperty int actualFormat; }; +//============================================================================== +enum +{ + maxXEmbedVersionToSupport = 0 +}; + +enum +{ + 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 +}; + } // namespace juce diff --git a/modules/juce_gui_basics/native/juce_linux_X11_Windowing.cpp b/modules/juce_gui_basics/native/juce_linux_X11_Windowing.cpp index 500d5d6442..ba562dd8bd 100644 --- a/modules/juce_gui_basics/native/juce_linux_X11_Windowing.cpp +++ b/modules/juce_gui_basics/native/juce_linux_X11_Windowing.cpp @@ -1163,6 +1163,20 @@ public: } } + void updateScaleFactorFromNewBounds (const Rectangle& newBounds, bool isPhysical) + { + Point translation = (parentWindow != 0 ? getScreenPosition (isPhysical) : Point()); + + auto newScaleFactor = Desktop::getInstance().getDisplays().findDisplayForRect (newBounds.translated (translation.x, translation.y), isPhysical).scale + / Desktop::getInstance().getGlobalScaleFactor(); + + if (! approximatelyEqual (newScaleFactor, currentScaleFactor)) + { + currentScaleFactor = newScaleFactor; + scaleFactorListeners.call ([&] (ScaleFactorListener& l) { l.nativeScaleFactorChanged (currentScaleFactor); }); + } + } + void setBounds (const Rectangle& newBounds, bool isNowFullScreen) override { if (fullScreen && ! isNowFullScreen) @@ -1200,16 +1214,10 @@ public: bounds = newBounds.withSize (jmax (1, newBounds.getWidth()), jmax (1, newBounds.getHeight())); - auto& displays = Desktop::getInstance().getDisplays(); + updateScaleFactorFromNewBounds (bounds, false); - auto newScaleFactor = displays.findDisplayForRect (bounds, true).scale / Desktop::getInstance().getGlobalScaleFactor(); - if (! approximatelyEqual (newScaleFactor, currentScaleFactor)) - { - currentScaleFactor = newScaleFactor; - scaleFactorListeners.call ([&] (ScaleFactorListener& l) { l.nativeScaleFactorChanged (currentScaleFactor); }); - } - - auto physicalBounds = displays.logicalToPhysical (bounds); + auto physicalBounds = (parentWindow == 0 ? Desktop::getInstance().getDisplays().logicalToPhysical (bounds) + : bounds * currentScaleFactor); WeakReference deletionChecker (&component); ScopedXLock xlock (display); @@ -1245,21 +1253,21 @@ public: } } - Rectangle getBounds() const override { return bounds; } - - Point localToGlobal (Point relativePosition) override + Point getScreenPosition (bool physical) const { - return relativePosition + bounds.getPosition().toFloat(); + if (physical) + return Desktop::getInstance().getDisplays().logicalToPhysical (bounds.getTopLeft()); + + return bounds.getTopLeft(); } + Rectangle getBounds() const override { return bounds; } + using ComponentPeer::localToGlobal; - - Point globalToLocal (Point screenPosition) override - { - return screenPosition - bounds.getPosition().toFloat(); - } + Point localToGlobal (Point relativePosition) override { return relativePosition + getScreenPosition (false).toFloat(); } using ComponentPeer::globalToLocal; + Point globalToLocal (Point screenPosition) override { return screenPosition - getScreenPosition (false).toFloat(); } void setAlpha (float /* newAlpha */) override { @@ -1624,7 +1632,7 @@ public: case ClientMessage: handleClientMessageEvent (event.xclient, event); break; case SelectionNotify: handleDragAndDropSelection (event); break; case ConfigureNotify: handleConfigureNotifyEvent (event.xconfigure); break; - case ReparentNotify: handleReparentNotifyEvent(); break; + case ReparentNotify: case GravityNotify: handleGravityNotify(); break; case SelectionClear: handleExternalSelectionClear(); break; case SelectionRequest: handleExternalSelectionRequest (event); break; @@ -2012,24 +2020,6 @@ public: handleBroughtToFront(); } - void handleReparentNotifyEvent() - { - parentWindow = 0; - Window wRoot = 0; - Window* wChild = nullptr; - unsigned int numChildren; - - { - ScopedXLock xlock (display); - XQueryTree (display, windowH, &wRoot, &parentWindow, &wChild, &numChildren); - } - - if (parentWindow == windowH || parentWindow == wRoot) - parentWindow = 0; - - handleGravityNotify(); - } - void handleGravityNotify() { updateWindowBounds(); @@ -2112,6 +2102,10 @@ public: { externalResetDragAndDrop(); } + else if (clientMsg.message_type == atoms->XembedMsgType && clientMsg.format == 32) + { + handleXEmbedMessage (clientMsg); + } } bool externalDragTextInit (const String& text, std::function cb) @@ -2140,6 +2134,27 @@ public: return externalDragInit (false, uriList.joinIntoString ("\r\n"), cb); } + void handleXEmbedMessage (XClientMessageEvent& clientMsg) + { + switch (clientMsg.data.l[1]) + { + case XEMBED_EMBEDDED_NOTIFY: + parentWindow = (::Window) clientMsg.data.l[3]; + updateWindowBounds(); + component.setBounds (bounds); + break; + case XEMBED_FOCUS_IN: + handleFocusInEvent(); + break; + case XEMBED_FOCUS_OUT: + handleFocusOutEvent(); + break; + + default: + break; + } + } + //============================================================================== void showMouseCursor (Cursor cursor) noexcept { @@ -2714,6 +2729,9 @@ private: xchangeProperty (windowH, atoms->XdndActionDescription, XA_STRING, 8, "", 0); xchangeProperty (windowH, atoms->XdndAware, XA_ATOM, 32, &atoms->DndVersion, 1); + unsigned long info[2] = { 0, 1 }; + xchangeProperty (windowH, atoms->XembedInfo, atoms->XembedInfo, 32, (unsigned char*) info, 2); + initialisePointerMap(); updateModifierMappings(); } @@ -2823,21 +2841,16 @@ private: ScopedXLock xlock (display); - if (XGetGeometry (display, (::Drawable) windowH, &root, &wx, &wy, &ww, &wh, &bw, &bitDepth)) + if (XGetGeometry (display, (::Drawable) windowH, &root, &wx, &wy, &ww, &wh, &bw, &bitDepth) && parentWindow == 0) if (! XTranslateCoordinates (display, windowH, root, 0, 0, &wx, &wy, &child)) wx = wy = 0; Rectangle physicalBounds (wx, wy, (int) ww, (int) wh); - auto& displays = Desktop::getInstance().getDisplays(); - auto newScaleFactor = displays.findDisplayForRect (physicalBounds, true).scale / Desktop::getInstance().getGlobalScaleFactor(); - if (! approximatelyEqual (newScaleFactor, currentScaleFactor)) - { - currentScaleFactor = newScaleFactor; - scaleFactorListeners.call ([&] (ScaleFactorListener& l) { l.nativeScaleFactorChanged (currentScaleFactor); }); - } + updateScaleFactorFromNewBounds (physicalBounds, true); - bounds = displays.physicalToLogical (physicalBounds); + bounds = (parentWindow == 0 ? Desktop::getInstance().getDisplays().physicalToLogical (physicalBounds) + : physicalBounds / currentScaleFactor); } } diff --git a/modules/juce_gui_extra/native/juce_linux_XEmbedComponent.cpp b/modules/juce_gui_extra/native/juce_linux_XEmbedComponent.cpp index 3a1f34d84f..d1c0ae7151 100644 --- a/modules/juce_gui_extra/native/juce_linux_XEmbedComponent.cpp +++ b/modules/juce_gui_extra/native/juce_linux_XEmbedComponent.cpp @@ -38,40 +38,6 @@ 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 - }; - //============================================================================== struct SharedKeyWindow : public ReferenceCountedObject { @@ -201,7 +167,14 @@ public: static_cast (newBounds.getHeight())); } - XSelectInput (dpy, client, StructureNotifyMask | PropertyChangeMask | FocusChangeMask); + auto eventMask = StructureNotifyMask | PropertyChangeMask | FocusChangeMask; + + XWindowAttributes clientAttr; + XGetWindowAttributes (dpy, client, &clientAttr); + + if ((eventMask & clientAttr.your_event_mask) != eventMask) + XSelectInput (dpy, client, clientAttr.your_event_mask | eventMask); + getXEmbedMappedFlag(); if (shouldReparent) @@ -365,7 +338,7 @@ private: Window getParentX11Window() { - if (auto peer = owner.getPeer()) + if (auto* peer = owner.getPeer()) return reinterpret_cast (peer->getNativeHandle()); return {};