• Skip to content
  • Skip to link menu
KDE 4.2 API Reference
  • KDE API Reference
  • API Reference
  • Sitemap
  • Contact Us
 

Applets

fdoselectionmanager.cpp

Go to the documentation of this file.
00001 /***************************************************************************
00002  *   fdoselectionmanager.cpp                                               *
00003  *                                                                         *
00004  *   Copyright (C) 2008 Jason Stubbs <jasonbstubbs@gmail.com>              *
00005  *                                                                         *
00006  *   This program is free software; you can redistribute it and/or modify  *
00007  *   it under the terms of the GNU General Public License as published by  *
00008  *   the Free Software Foundation; either version 2 of the License, or     *
00009  *   (at your option) any later version.                                   *
00010  *                                                                         *
00011  *   This program is distributed in the hope that it will be useful,       *
00012  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00013  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
00014  *   GNU General Public License for more details.                          *
00015  *                                                                         *
00016  *   You should have received a copy of the GNU General Public License     *
00017  *   along with this program; if not, write to the                         *
00018  *   Free Software Foundation, Inc.,                                       *
00019  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA .        *
00020  ***************************************************************************/
00021 
00022 #include "fdonotification.h"
00023 #include "fdoselectionmanager.h"
00024 #include "fdotask.h"
00025 #include "x11embedpainter.h"
00026 
00027 #include <KDebug>
00028 
00029 #include <QtCore/QCoreApplication>
00030 #include <QtCore/QHash>
00031 #include <QtCore/QTimer>
00032 
00033 #include <QtGui/QTextDocument>
00034 #include <QtGui/QX11Info>
00035 
00036 #include <KGlobal>
00037 
00038 #include <config-X11.h>
00039 
00040 #include <X11/Xlib.h>
00041 #include <X11/Xatom.h>
00042 #include <X11/extensions/Xrender.h>
00043 
00044 #ifdef HAVE_XFIXES
00045 #  include <X11/extensions/Xfixes.h>
00046 #endif
00047 
00048 #ifdef HAVE_XDAMAGE
00049 #  include <X11/extensions/Xdamage.h>
00050 #endif
00051 
00052 #ifdef HAVE_XCOMPOSITE
00053 #  include <X11/extensions/Xcomposite.h>
00054 #endif
00055 
00056 #define SYSTEM_TRAY_REQUEST_DOCK    0
00057 #define SYSTEM_TRAY_BEGIN_MESSAGE   1
00058 #define SYSTEM_TRAY_CANCEL_MESSAGE  2
00059 
00060 
00061 namespace SystemTray
00062 {
00063 
00064 static FdoSelectionManager *s_manager = 0;
00065 static X11EmbedPainter *s_painter = 0;
00066 
00067 #if defined(HAVE_XFIXES) && defined(HAVE_XDAMAGE) && defined(HAVE_XCOMPOSITE)
00068 struct DamageWatch
00069 {
00070     QWidget *container;
00071     Damage damage;
00072 };
00073 
00074 static int damageEventBase = 0;
00075 static QMap<WId, DamageWatch*> damageWatches;
00076 static QCoreApplication::EventFilter oldEventFilter;
00077 
00078 // Global event filter for intercepting damage events
00079 static bool x11EventFilter(void *message, long int *result)
00080 {
00081     XEvent *event = reinterpret_cast<XEvent*>(message);
00082     if (event->type == damageEventBase + XDamageNotify) {
00083         XDamageNotifyEvent *e = reinterpret_cast<XDamageNotifyEvent*>(event);
00084         if (DamageWatch *damageWatch = damageWatches.value(e->drawable)) {
00085             // Create a new region and empty the damage region into it.
00086             // The window is small enough that we don't really care about the region;
00087             // we'll just throw it away and schedule a full repaint of the container.
00088             XserverRegion region = XFixesCreateRegion(e->display, 0, 0);
00089             XDamageSubtract(e->display, e->damage, None, region);
00090             XFixesDestroyRegion(e->display, region);
00091             damageWatch->container->update();
00092         }
00093     }
00094 
00095     if (oldEventFilter && oldEventFilter != x11EventFilter) {
00096         return oldEventFilter(message, result);
00097     } else {
00098         return false;
00099     }
00100 }
00101 #endif
00102 
00103 
00104 struct MessageRequest
00105 {
00106     long messageId;
00107     long timeout;
00108     long bytesRemaining;
00109     QByteArray message;
00110 };
00111 
00112 
00113 class FdoSelectionManagerPrivate
00114 {
00115 public:
00116     FdoSelectionManagerPrivate(FdoSelectionManager *q)
00117         : q(q), haveComposite(false)
00118     {
00119         display = QX11Info::display();
00120         selectionAtom = XInternAtom(display, "_NET_SYSTEM_TRAY_S" + QByteArray::number(QX11Info::appScreen()), false);
00121         opcodeAtom = XInternAtom(display, "_NET_SYSTEM_TRAY_OPCODE", false);
00122         messageAtom = XInternAtom(display, "_NET_SYSTEM_TRAY_MESSAGE_DATA", false);
00123         visualAtom = XInternAtom(display, "_NET_SYSTEM_TRAY_VISUAL", false);
00124 
00125 #if defined(HAVE_XFIXES) && defined(HAVE_XDAMAGE) && defined(HAVE_XCOMPOSITE)
00126         int eventBase, errorBase;
00127         bool haveXfixes = XFixesQueryExtension(display, &eventBase, &errorBase);
00128         bool haveXdamage = XDamageQueryExtension(display, &damageEventBase, &errorBase);
00129         bool haveXComposite = XCompositeQueryExtension(display, &eventBase, &errorBase);
00130 
00131         if (haveXfixes && haveXdamage && haveXComposite) {
00132             haveComposite = true;
00133             oldEventFilter = QCoreApplication::instance()->setEventFilter(x11EventFilter);
00134         }
00135 #endif
00136     }
00137 
00138     void createNotification(WId winId);
00139 
00140     void handleRequestDock(const XClientMessageEvent &event);
00141     void handleBeginMessage(const XClientMessageEvent &event);
00142     void handleMessageData(const XClientMessageEvent &event);
00143     void handleCancelMessage(const XClientMessageEvent &event);
00144 
00145     Display *display;
00146     Atom selectionAtom;
00147     Atom opcodeAtom;
00148     Atom messageAtom;
00149     Atom visualAtom;
00150 
00151     QHash<WId, MessageRequest> messageRequests;
00152     QHash<WId, FdoTask*> tasks;
00153     QHash<WId, FdoNotification*> notifications;
00154 
00155     FdoSelectionManager *q;
00156     bool haveComposite;
00157 };
00158 
00159 FdoSelectionManager::FdoSelectionManager()
00160     : d(new FdoSelectionManagerPrivate(this))
00161 {
00162     // Init the selection later just to ensure that no signals are sent
00163     // until after construction is done and the creating object has a
00164     // chance to connect.
00165     QTimer::singleShot(0, this, SLOT(initSelection()));
00166 }
00167 
00168 
00169 FdoSelectionManager::~FdoSelectionManager()
00170 {
00171 #if defined(HAVE_XFIXES) && defined(HAVE_XDAMAGE) && defined(HAVE_XCOMPOSITE)
00172     if (d->haveComposite && QCoreApplication::instance()) {
00173         QCoreApplication::instance()->setEventFilter(oldEventFilter);
00174     }
00175 #endif
00176 
00177     if (s_manager == this) {
00178         s_manager = 0;
00179         delete s_painter;
00180         s_painter = 0;
00181     }
00182 
00183     delete d;
00184 }
00185 
00186 FdoSelectionManager *FdoSelectionManager::manager()
00187 {
00188     return s_manager;
00189 }
00190 
00191 X11EmbedPainter *FdoSelectionManager::painter()
00192 {
00193     return s_painter;
00194 }
00195 
00196 void FdoSelectionManager::addDamageWatch(QWidget *container, WId client)
00197 {
00198 #if defined(HAVE_XFIXES) && defined(HAVE_XDAMAGE) && defined(HAVE_XCOMPOSITE)
00199     DamageWatch *damage = new DamageWatch;
00200     damage->container = container;
00201     damage->damage = XDamageCreate(QX11Info::display(), client, XDamageReportNonEmpty);
00202     damageWatches.insert(client, damage);
00203 #endif
00204 }
00205 
00206 void FdoSelectionManager::removeDamageWatch(QWidget *container)
00207 {
00208 #if defined(HAVE_XFIXES) && defined(HAVE_XDAMAGE) && defined(HAVE_XCOMPOSITE)
00209     for (QMap<WId, DamageWatch*>::Iterator it = damageWatches.begin(); it != damageWatches.end(); ++it)
00210     {
00211         DamageWatch *damage = *(it);
00212         if (damage->container == container) {
00213              XDamageDestroy(QX11Info::display(), damage->damage);
00214              damageWatches.erase(it);
00215              delete damage;
00216              break;
00217         } 
00218     }
00219 #endif
00220 }
00221 
00222 
00223 bool FdoSelectionManager::haveComposite() const
00224 {
00225     return d->haveComposite;
00226 }
00227 
00228 
00229 bool FdoSelectionManager::x11Event(XEvent *event)
00230 {
00231     if (event->type == ClientMessage) {
00232         if (event->xclient.message_type == d->opcodeAtom) {
00233             switch (event->xclient.data.l[1]) {
00234             case SYSTEM_TRAY_REQUEST_DOCK:
00235                 d->handleRequestDock(event->xclient);
00236                 return true;
00237             case SYSTEM_TRAY_BEGIN_MESSAGE:
00238                 d->handleBeginMessage(event->xclient);
00239                 return true;
00240             case SYSTEM_TRAY_CANCEL_MESSAGE:
00241                 d->handleCancelMessage(event->xclient);
00242                 return true;
00243             }
00244         } else if (event->xclient.message_type == d->messageAtom) {
00245             d->handleMessageData(event->xclient);
00246             return true;
00247         }
00248     }
00249 
00250     return QWidget::x11Event(event);
00251 }
00252 
00253 
00254 void FdoSelectionManager::initSelection()
00255 {
00256     XSetSelectionOwner(d->display, d->selectionAtom, winId(), CurrentTime);
00257 
00258     WId selectionOwner = XGetSelectionOwner(d->display, d->selectionAtom);
00259     if (selectionOwner != winId()) {
00260         // FIXME: Hmmm... Reading the docs on XSetSelectionOwner,
00261         // this should not be possible.
00262         kDebug() << "Tried to set selection owner to" << winId() << "but it is set to" << selectionOwner;
00263         return;
00264     }
00265 
00266     // Prefer the ARGB32 visual if available
00267     int nvi;
00268     VisualID visual = XVisualIDFromVisual((Visual*)QX11Info::appVisual());
00269     XVisualInfo templ;
00270     templ.visualid = visual;
00271     XVisualInfo *xvi = XGetVisualInfo(d->display, VisualIDMask, &templ, &nvi);
00272     if (xvi) {
00273         templ.screen  = xvi[0].screen;
00274         templ.depth   = xvi[0].depth;
00275         templ.c_class = xvi[0].c_class;
00276         XFree(xvi);
00277         xvi = XGetVisualInfo(d->display, VisualScreenMask | VisualDepthMask | VisualClassMask,
00278                              &templ, &nvi);
00279         for (int i = 0; i < nvi; i++) {
00280             XRenderPictFormat *format = XRenderFindVisualFormat(d->display, xvi[i].visual);
00281             if (format->type == PictTypeDirect && format->direct.alphaMask) {
00282                 visual = xvi[i].visualid;
00283                 break;
00284             }
00285         }
00286         XFree(xvi);
00287     }
00288     XChangeProperty(d->display, winId(), d->visualAtom, XA_VISUALID, 32,
00289                     PropModeReplace, (const unsigned char*)&visual, 1);
00290 
00291     if (!s_painter) {
00292         s_painter = new X11EmbedPainter;
00293     }
00294     s_manager = this;
00295 
00296     WId root = QX11Info::appRootWindow();
00297     XClientMessageEvent xev;
00298 
00299     xev.type = ClientMessage;
00300     xev.window = root;
00301     xev.message_type = XInternAtom(d->display, "MANAGER", false);
00302     xev.format = 32;
00303     xev.data.l[0] = CurrentTime;
00304     xev.data.l[1] = d->selectionAtom;
00305     xev.data.l[2] = winId();
00306     xev.data.l[3] = 0;
00307     xev.data.l[4] = 0;
00308 
00309     XSendEvent(d->display, root, false, StructureNotifyMask, (XEvent*)&xev);
00310 }
00311 
00312 
00313 void FdoSelectionManagerPrivate::handleRequestDock(const XClientMessageEvent &event)
00314 {
00315     const WId winId = (WId)event.data.l[2];
00316 
00317     if (tasks.contains(winId)) {
00318         kDebug() << "got a dock request from an already existing task";
00319         return;
00320     }
00321 
00322     FdoTask *task = new FdoTask(winId);
00323 
00324     tasks[winId] = task;
00325     q->connect(task, SIGNAL(taskDeleted(WId)), q, SLOT(cleanupTask(WId)));
00326 
00327     emit q->taskCreated(task);
00328 }
00329 
00330 
00331 void FdoSelectionManager::cleanupTask(WId winId)
00332 {
00333     d->tasks.remove(winId);
00334 }
00335 
00336 
00337 void FdoSelectionManagerPrivate::handleBeginMessage(const XClientMessageEvent &event)
00338 {
00339     const WId winId = event.window;
00340 
00341     MessageRequest request;
00342     request.messageId = event.data.l[4];
00343     request.timeout = event.data.l[2];
00344     request.bytesRemaining = event.data.l[3];
00345 
00346     if (request.bytesRemaining) {
00347         messageRequests[winId] = request;
00348     }
00349 }
00350 
00351 
00352 void FdoSelectionManagerPrivate::handleMessageData(const XClientMessageEvent &event)
00353 {
00354     const WId winId = event.window;
00355     const char *messageData = event.data.b;
00356 
00357     if (!messageRequests.contains(winId)) {
00358         kDebug() << "Unexpected message data from" << winId;
00359         return;
00360     }
00361 
00362     MessageRequest &request = messageRequests[winId];
00363     const int messageSize = qMin(request.bytesRemaining, 20l);
00364     request.bytesRemaining -= messageSize;
00365     request.message += QByteArray(messageData, messageSize);
00366 
00367     if (request.bytesRemaining == 0) {
00368         createNotification(winId);
00369         messageRequests.remove(winId);
00370     }
00371 }
00372 
00373 
00374 void FdoSelectionManagerPrivate::createNotification(WId winId)
00375 {
00376     if (!tasks.contains(winId)) {
00377         kDebug() << "message request from unknown task" << winId;
00378         return;
00379     }
00380 
00381     MessageRequest &request = messageRequests[winId];
00382     Task *task = tasks[winId];
00383 
00384     QString message = QString::fromUtf8(request.message);
00385     message = QTextDocument(message).toHtml();
00386 
00387     FdoNotification *notification = new FdoNotification(winId, task);
00388     notification->setApplicationName(task->name());
00389     notification->setApplicationIcon(task->icon());
00390     notification->setMessage(message);
00391     notification->setTimeout(request.timeout);
00392 
00393     q->connect(notification, SIGNAL(notificationDeleted(WId)), q, SLOT(cleanupNotification(WId)));
00394     emit q->notificationCreated(notification);
00395 }
00396 
00397 
00398 void FdoSelectionManagerPrivate::handleCancelMessage(const XClientMessageEvent &event)
00399 {
00400     const WId winId = event.window;
00401     const long messageId = event.data.l[2];
00402 
00403     if (messageRequests.contains(winId) && messageRequests[winId].messageId == messageId) {
00404         messageRequests.remove(winId);
00405     } else if (notifications.contains(winId)) {
00406         notifications.take(winId)->deleteLater();
00407     }
00408 }
00409 
00410 
00411 void FdoSelectionManager::cleanupNotification(WId winId)
00412 {
00413     d->notifications.remove(winId);
00414 }
00415 
00416 }

Applets

Skip menu "Applets"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members

API Reference

Skip menu "API Reference"
  • KWin
  •   KWin Libraries
  • Libraries
  •   libkworkspace
  •   libsolidcontrol
  •   libtaskmanager
  • Plasma
  •   Animators
  •   Applets
  •   Engines
  • Solid Modules
Generated for API Reference by doxygen 1.5.7
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal