|
|
|
/* 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.contains(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.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"
|