[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-commits
Subject:    playground/base/plasma/applets/systray-refactor
From:       Fredrik Höglund <fredrik () kde ! org>
Date:       2008-10-06 23:45:57
Message-ID: 1223336757.831591.565.nullmailer () svn ! kde ! org
[Download RAW message or body]

SVN commit 868734 by fredrik:

Implement support for _NET_SYSTEM_TRAY_VISUAL and proper handling of
ARGB32 systray windows.


 M  +2 -1      CMakeLists.txt  
 M  +124 -3    protocols/fdo/fdoselectionmanager.cpp  
 M  +4 -0      protocols/fdo/fdoselectionmanager.h  
 M  +60 -4     protocols/fdo/x11embedcontainer.cpp  


--- trunk/playground/base/plasma/applets/systray-refactor/CMakeLists.txt #868733:868734
@@ -33,7 +33,8 @@
 
 kde4_add_plugin(plasma_applet_systemtray ${systemtray_SRCS})
 include_directories(${CMAKE_SOURCE_DIR})
-target_link_libraries(plasma_applet_systemtray ${KDE4_KDEUI_LIBS} plasma ${X11_LIBRARIES} \
Xrender) +target_link_libraries(plasma_applet_systemtray ${KDE4_KDEUI_LIBS} plasma \
${X11_LIBRARIES} ${X11_Xrender_LIB} +                      ${X11_Xfixes_LIB} ${X11_Xdamage_LIB} \
${X11_Xcomposite_LIB})  
 install(TARGETS plasma_applet_systemtray DESTINATION ${PLUGIN_INSTALL_DIR})
 install(FILES plasma-applet-systemtray.desktop DESTINATION ${SERVICES_INSTALL_DIR})
--- trunk/playground/base/plasma/applets/systray-refactor/protocols/fdo/fdoselectionmanager.cpp \
#868733:868734 @@ -25,8 +25,7 @@
 
 #include <KDebug>
 
-#include <X11/Xlib.h>
-
+#include <QtCore/QCoreApplication>
 #include <QtCore/QHash>
 #include <QtCore/QTimer>
 
@@ -35,7 +34,25 @@
 
 #include <KGlobal>
 
+// ### Change to config-X11.h before moving to kdebase!
+#include <config-plasma.h>
 
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+#include <X11/extensions/Xrender.h>
+
+#ifdef HAVE_XFIXES
+#  include <X11/extensions/Xfixes.h>
+#endif
+
+#ifdef HAVE_XDAMAGE
+#  include <X11/extensions/Xdamage.h>
+#endif
+
+#ifdef HAVE_XCOMPOSITE
+#  include <X11/extensions/Xcomposite.h>
+#endif
+
 #define SYSTEM_TRAY_REQUEST_DOCK    0
 #define SYSTEM_TRAY_BEGIN_MESSAGE   1
 #define SYSTEM_TRAY_CANCEL_MESSAGE  2
@@ -46,7 +63,43 @@
 namespace FDO
 {
 
+#if defined(HAVE_XFIXES) && defined(HAVE_XDAMAGE) && defined(HAVE_XCOMPOSITE)
+struct DamageWatch
+{
+    QWidget *container;
+    Damage damage;
+};
 
+static int damageEventBase;
+static QMap<WId, DamageWatch*> damageWatches;
+static QCoreApplication::EventFilter oldEventFilter;
+
+// Global event filter for intercepting damage events
+static bool x11EventFilter(void *message, long int *result)
+{
+    XEvent *event = reinterpret_cast<XEvent*>(message);
+    if (event->type == damageEventBase + XDamageNotify) {
+        XDamageNotifyEvent *e = reinterpret_cast<XDamageNotifyEvent*>(event);
+        if (DamageWatch *damageWatch = damageWatches.value(e->drawable)) {
+            // Create a new region and empty the damage region into it.
+            // The window is small enough that we don't really care about the region;
+            // we'll just throw it away and schedule a full repaint of the container.
+            XserverRegion region = XFixesCreateRegion(e->display, 0, 0);
+            XDamageSubtract(e->display, e->damage, None, region);
+            XFixesDestroyRegion(e->display, region);
+            damageWatch->container->update();
+        }
+    }
+
+    if (oldEventFilter && oldEventFilter != &x11EventFilter) {
+        return oldEventFilter(message, result);
+    } else {
+        return false;
+    }
+}
+#endif
+
+
 class SelectionManager::Singleton
 {
 public:
@@ -69,12 +122,25 @@
 {
 public:
     Private(SelectionManager *q)
-        : q(q)
+        : q(q), haveComposite(false)
     {
         display = QX11Info::display();
         selectionAtom = XInternAtom(display, "_NET_SYSTEM_TRAY_S" + \
                QByteArray::number(QX11Info::appScreen()), false);
         opcodeAtom = XInternAtom(display, "_NET_SYSTEM_TRAY_OPCODE", false);
         messageAtom = XInternAtom(display, "_NET_SYSTEM_TRAY_MESSAGE_DATA", false);
+        visualAtom = XInternAtom(display, "_NET_SYSTEM_TRAY_VISUAL", false);
+
+#if defined(HAVE_XFIXES) && defined(HAVE_XDAMAGE) && defined(HAVE_XCOMPOSITE)
+        int eventBase, errorBase;
+        bool haveXfixes = XFixesQueryExtension(display, &eventBase, &errorBase);
+        bool haveXdamage = XDamageQueryExtension(display, &damageEventBase, &errorBase);
+        bool haveXComposite = XCompositeQueryExtension(display, &eventBase, &errorBase);
+
+        if (haveXfixes && haveXdamage && haveXComposite) {
+            haveComposite = true;
+            oldEventFilter = QCoreApplication::instance()->setEventFilter(x11EventFilter);
+        }
+#endif
     }
 
     void createNotification(WId winId);
@@ -88,12 +154,14 @@
     Atom selectionAtom;
     Atom opcodeAtom;
     Atom messageAtom;
+    Atom visualAtom;
 
     QHash<WId, MessageRequest> messageRequests;
     QHash<WId, Task*> tasks;
     QHash<WId, Notification*> notifications;
 
     SelectionManager *q;
+    bool haveComposite;
 };
 
 
@@ -115,10 +183,43 @@
 
 SelectionManager::~SelectionManager()
 {
+    if (d->haveComposite) {
+        QCoreApplication::instance()->setEventFilter(oldEventFilter);
+    }
     delete d;
 }
 
 
+void SelectionManager::addDamageWatch(QWidget *container, WId client)
+{
+    DamageWatch *damage = new DamageWatch;
+    damage->container = container;
+    damage->damage = XDamageCreate(QX11Info::display(), client, XDamageReportNonEmpty);
+    damageWatches.insert(client, damage);
+}
+
+
+void SelectionManager::removeDamageWatch(QWidget *container)
+{
+    for (QMap<WId, DamageWatch*>::Iterator it = damageWatches.begin(); it != \
damageWatches.end(); ++it) +    {
+        DamageWatch *damage = *(it);
+        if (damage->container == container) {
+             XDamageDestroy(QX11Info::display(), damage->damage);
+             damageWatches.erase(it);
+             delete damage;
+             break;
+        } 
+    }
+}
+
+
+bool SelectionManager::haveComposite() const
+{
+    return d->haveComposite;
+}
+
+
 bool SelectionManager::x11Event(XEvent *event)
 {
     if (event->type == ClientMessage) {
@@ -156,6 +257,26 @@
         return;
     }
 
+    // Prefer the ARGB32 visual if available
+    int nvi;
+    VisualID visual = XVisualIDFromVisual((Visual*)QX11Info::appVisual());
+    XVisualInfo templ;
+    templ.screen  = DefaultScreen(d->display);
+    templ.depth   = 32;
+    templ.c_class = TrueColor;
+    XVisualInfo *xvi = XGetVisualInfo(d->display, VisualScreenMask | VisualDepthMask | \
VisualClassMask, +                                      &templ, &nvi);
+    for (int i = 0; i < nvi; i++) {
+        XRenderPictFormat *format = XRenderFindVisualFormat(d->display, xvi[i].visual);
+        if (format->type == PictTypeDirect && format->direct.alphaMask) {
+            visual = xvi[i].visualid;
+            break;
+        }
+    }
+
+    XChangeProperty(d->display, winId(), d->visualAtom, XA_VISUALID, 32,
+                    PropModeReplace, (const unsigned char*)&visual, 1);
+
     WId root = QX11Info::appRootWindow();
     XClientMessageEvent xev;
 
--- trunk/playground/base/plasma/applets/systray-refactor/protocols/fdo/fdoselectionmanager.h \
#868733:868734 @@ -44,6 +44,10 @@
     class Singleton;
     static SelectionManager* self();
 
+    void addDamageWatch(QWidget *container, WId client);
+    void removeDamageWatch(QWidget *container);
+    bool haveComposite() const;
+
 signals:
     void taskCreated(SystemTray::Task *task);
     void notificationCreated(SystemTray::Notification *notification);
--- trunk/playground/base/plasma/applets/systray-refactor/protocols/fdo/x11embedcontainer.cpp \
#868733:868734 @@ -20,13 +20,27 @@
  ***************************************************************************/
 
 #include "x11embedcontainer.h"
+#include "fdoselectionmanager.h"
 
+// KDE
+#include <KDebug>
+
+// Qt
+#include <QtCore/QTimer>
+#include <QtGui/QPainter>
+#include <QtGui/QX11Info>
+
+// Xlib
+// #### Change to config-X11.h before moving to kdebase!
+#include <config-plasma.h>
+
 #include <X11/Xlib.h>
 #include <X11/extensions/Xrender.h>
 
-#include <QtCore/QTimer>
+#ifdef HAVE_XCOMPOSITE
+#  include <X11/extensions/Xcomposite.h>
+#endif
 
-#include <QtGui/QX11Info>
 
 
 namespace SystemTray
@@ -40,9 +54,16 @@
 public:
     Private(X11EmbedContainer *q)
         : q(q),
+          picture(None),
           updatingBackground(false)
     {
     }
+    ~Private()
+    {
+        if (picture) {
+           XRenderFreePicture(QX11Info::display(), picture);
+        }
+    }
 
     void updateClientBackground();
     void sendExposeToClient();
@@ -50,6 +71,7 @@
     X11EmbedContainer *q;
 
     XWindowAttributes attr;
+    Picture picture;
     QImage bgImage;
     bool updatingBackground;
 };
@@ -66,6 +88,7 @@
 
 X11EmbedContainer::~X11EmbedContainer()
 {
+    SelectionManager::self()->removeDamageWatch(this);
     delete d;
 }
 
@@ -90,6 +113,25 @@
                                  CWBackPixel | CWBorderPixel | CWColormap, &sAttr);
     create(winId);
 
+#if defined(HAVE_XCOMPOSITE) && defined(HAVE_XFIXES) && defined(HAVE_XDAMAGE)
+    XRenderPictFormat *format = XRenderFindVisualFormat(display, d->attr.visual);
+    if (format->type == PictTypeDirect && format->direct.alphaMask &&
+        SelectionManager::self()->haveComposite())
+    {
+        // Redirect ARGB windows to offscreen storage so we can composite them ourselves
+        XRenderPictureAttributes attr;
+        attr.subwindow_mode = IncludeInferiors;
+
+        d->picture = XRenderCreatePicture(display, clientId, format, CPSubwindowMode, &attr);
+        XCompositeRedirectSubwindows(display, winId, CompositeRedirectManual);
+        SelectionManager::self()->addDamageWatch(this, clientId);
+
+        kDebug() << "Embedded client uses an ARGB visual -> compositing.";
+    } else {
+        kDebug() << "Embedded client is not using an ARGB visual.";
+    }
+#endif
+
     // repeat everything from QX11EmbedContainer's ctor that might be relevant
     setFocusPolicy(Qt::StrongFocus);
     setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
@@ -134,9 +176,23 @@
 {
     Q_UNUSED(event);
 
-    if (!d->updatingBackground) {
-        QTimer::singleShot(0, this, SLOT(updateBackgroundImage()));
+    if (!d->picture) {
+        if (!d->updatingBackground) {
+            QTimer::singleShot(0, this, SLOT(updateBackgroundImage()));
+        }
+        return;
     }
+
+    // Taking a detour via a QPixmap is unfortunately the only way we can get
+    // the window contents into Qt's backing store.
+    QPixmap pixmap(size());
+    pixmap.fill(Qt::transparent);
+
+    XRenderComposite(x11Info().display(), PictOpSrc, d->picture, None, \
pixmap.x11PictureHandle(), +                     0, 0, 0, 0, 0, 0, width(), height());
+
+    QPainter p(this);
+    p.drawPixmap(0, 0, pixmap);
 }
 
 


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic