You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdebase/kdesktop/pixmapserver.cc

255 lines
6.1 KiB

/* 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()
: TQWidget(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(TQString name, TQPixmap *pm, bool overwrite)
{
if (m_Names.tqcontains(name))
{
if (overwrite)
remove(name);
else return;
}
TQString str = TQString("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(TQString 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);
}
}
TQStringList KPixmapServer::list()
{
TQStringList lst;
NameIterator it;
for (it=m_Names.begin(); it!=m_Names.end(); it++)
lst += it.key();
return lst;
}
void KPixmapServer::setOwner(TQString 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.tqcontains(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"