diff options
Diffstat (limited to 'kdesktop/pixmapserver.cc')
-rw-r--r-- | kdesktop/pixmapserver.cc | 254 |
1 files changed, 254 insertions, 0 deletions
diff --git a/kdesktop/pixmapserver.cc b/kdesktop/pixmapserver.cc new file mode 100644 index 000000000..ba66ef966 --- /dev/null +++ b/kdesktop/pixmapserver.cc @@ -0,0 +1,254 @@ +/* vi: ts=8 sts=4 sw=4 + * + * This file is part of the KDE project, module kdesktop. + * Copyright (C) 1999 Geert Jansen <g.t.jansen@stud.tue.nl> + * + * You can Freely distribute this program under the GNU General Public + * License. See the file "COPYING" for the exact licensing terms. + * + * + * Shared pixmap server for KDE. + * + * 5 Dec 99: Geert Jansen: + * + * Initial implementation using the X11 selection mechanism. + */ + +#include <assert.h> + + +#include <kapplication.h> +#include <kdebug.h> + +#include <X11/X.h> +#include <X11/Xlib.h> + +#include "pixmapserver.h" + +#ifndef None +#define None 0L +#endif + +#ifdef __GNUC__ +#define ID __PRETTY_FUNCTION__ << ": " +#else +#define ID "KPixmapServer: " +#endif + + +KPixmapServer::KPixmapServer() + : QWidget(0L, "shpixmap comm window") +{ + kapp->installX11EventFilter(this); + pixmap = XInternAtom(qt_xdisplay(), "PIXMAP", false); +} + + +KPixmapServer::~KPixmapServer() +{ + SelectionIterator it; + for (it=m_Selections.begin(); it!=m_Selections.end(); it++) + XSetSelectionOwner(qt_xdisplay(), it.key(), None, CurrentTime); + + DataIterator it2; + for (it2=m_Data.begin(); it2!=m_Data.end(); it2++) + delete it2.data().pixmap; +} + + +void KPixmapServer::add(QString name, QPixmap *pm, bool overwrite) +{ + if (m_Names.contains(name)) + { + if (overwrite) + remove(name); + else return; + } + + QString str = QString("KDESHPIXMAP:%1").arg(name); + Atom sel = XInternAtom(qt_xdisplay(), str.latin1(), false); + KPixmapInode pi; + pi.handle = pm->handle(); + pi.selection = sel; + m_Names[name] = pi; + + KSelectionInode si; + si.name = name; + si.handle = pm->handle(); + m_Selections[sel] = si; + + DataIterator it = m_Data.find(pm->handle()); + if (it == m_Data.end()) + { + KPixmapData data; + data.pixmap = pm; + data.usecount = 0; + data.refcount = 1; + m_Data[pm->handle()] = data; + } else + it.data().refcount++; + + XSetSelectionOwner(qt_xdisplay(), sel, winId(), CurrentTime); +} + + +void KPixmapServer::remove(QString name) +{ + // Remove the name + NameIterator it = m_Names.find(name); + if (it == m_Names.end()) + return; + KPixmapInode pi = it.data(); + m_Names.remove(it); + + // Remove and disown the selection + SelectionIterator it2 = m_Selections.find(pi.selection); + assert(it2 != m_Selections.end()); + m_Selections.remove(it2); + XSetSelectionOwner(qt_xdisplay(), pi.selection, None, CurrentTime); + + // Decrease refcount on data + DataIterator it3 = m_Data.find(pi.handle); + assert(it3 != m_Data.end()); + it3.data().refcount--; + if (!it3.data().refcount && !it3.data().usecount) + { + delete it3.data().pixmap; + m_Data.remove(it3); + } +} + + +QStringList KPixmapServer::list() +{ + QStringList lst; + NameIterator it; + for (it=m_Names.begin(); it!=m_Names.end(); it++) + lst += it.key(); + return lst; +} + + +void KPixmapServer::setOwner(QString name) +{ + NameIterator it = m_Names.find(name); + if (it == m_Names.end()) + return; + + XSetSelectionOwner(qt_xdisplay(), it.data().selection, winId(), CurrentTime); +} + + +bool KPixmapServer::x11Event(XEvent *event) +{ + // Handle SelectionRequest events by which a X client can request a + // shared pixmap. + + if (event->type == SelectionRequest) + { + XSelectionRequestEvent *ev = &event->xselectionrequest; + + // Build negative reply + XEvent reply; + reply.type = SelectionNotify; + reply.xselection.display = qt_xdisplay(); + reply.xselection.requestor = ev->requestor; + reply.xselection.selection = ev->selection; + reply.xselection.target = pixmap; + reply.xselection.property = None; + reply.xselection.time = ev->time; + + // Check if we know about this selection + Atom sel = ev->selection; + SelectionIterator it = m_Selections.find(sel); + if (it == m_Selections.end()) + return false; + KSelectionInode si = it.data(); + + // Only convert to pixmap + if (ev->target != pixmap) + { + kdDebug(1204) << ID << "illegal target\n"; + XSendEvent(qt_xdisplay(), ev->requestor, false, 0, &reply); + return true; + } + + // Check if there is no transaction in progress to the same property + if (m_Active.contains(ev->property)) + { + kdDebug(1204) << ID << "selection is busy.\n"; + XSendEvent(qt_xdisplay(), ev->requestor, false, 0, &reply); + return true; + } + + // Check if the selection was not deleted + DataIterator it2 = m_Data.find(si.handle); + if (it2 == m_Data.end()) + { + kdDebug(1204) << ID << "selection has been deleted.\n"; + XSendEvent(qt_xdisplay(), ev->requestor, false, 0, &reply); + return true; + } + + kdDebug(1204) << ID << "request for " << si.name << "\n"; + + // All OK: pass the pixmap handle. + XChangeProperty(qt_xdisplay(), ev->requestor, ev->property, pixmap, + 32, PropModeReplace, (unsigned char *) &si.handle, 1); + it2.data().usecount++; + m_Active[ev->property] = si.handle; + + // Request PropertyNotify events for the target window + // XXX: The target window better not be handled by us! + XSelectInput(qt_xdisplay(), ev->requestor, PropertyChangeMask); + + // Acknowledge to the client and return + reply.xselection.property = ev->property; + XSendEvent(qt_xdisplay(), ev->requestor, false, 0, &reply); + return true; + } + + // ICCCM says that the target property is to be deleted by the + // requestor. We are notified of this by a PropertyNotify. Only then, we + // can actually delete the pixmap if it was removed. + + if (event->type == PropertyNotify) + { + XPropertyEvent *ev = &event->xproperty; + + AtomIterator it = m_Active.find(ev->atom); + if (it == m_Active.end()) + return false; + HANDLE handle = it.data(); + m_Active.remove(it); + + DataIterator it2 = m_Data.find(handle); + assert(it2 != m_Data.end()); + it2.data().usecount--; + if (!it2.data().usecount && !it2.data().refcount) + { + delete it2.data().pixmap; + m_Data.remove(it2); + } + return true; + } + + // Handle SelectionClear events. + + if (event->type == SelectionClear) + { + XSelectionClearEvent *ev = &event->xselectionclear; + + SelectionIterator it = m_Selections.find(ev->selection); + if (it == m_Selections.end()) + return false; + + emit selectionCleared(it.data().name); + return true; + } + + // Process further + return false; +} + +#include "pixmapserver.moc" |