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.
342 lines
11 KiB
342 lines
11 KiB
/****************************************************************************
|
|
**
|
|
** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
|
|
** Contact: Qt Software Information (qt-info.com)
|
|
**
|
|
** This file is part of a Qt Solutions component.
|
|
**
|
|
** Commercial Usage
|
|
** Licensees holding valid Qt Solutions licenses may use this file in
|
|
** accordance with the Qt Solutions Commercial License Agreement provided
|
|
** with the Software or, alternatively, in accordance with the terms
|
|
** contained in a written agreement between you and Nokia.
|
|
**
|
|
** If you are unsure which license is appropriate for your use, please
|
|
** contact the sales department at qt-sales.com.
|
|
**
|
|
****************************************************************************/
|
|
|
|
#include "qtsingleapplication.h"
|
|
#include <QtGui/QWidget>
|
|
#include <QtCore/QByteArray>
|
|
#include <QtCore/QString>
|
|
#include <QtCore/QList>
|
|
#include <QtGui/QDesktopWidget>
|
|
#include <QtGui/QApplication>
|
|
#include <QtCore/QDebug>
|
|
#include <unistd.h>
|
|
#include <pwd.h>
|
|
#include <sys/types.h>
|
|
#include <X11/Xatom.h>
|
|
#include <X11/Xlib.h>
|
|
#include <QtGui/QX11Info>
|
|
|
|
|
|
class QtSingletonListener : public QWidget
|
|
{
|
|
public:
|
|
QtSingletonListener(Atom atom, QWidget* par = 0)
|
|
: QWidget(par)
|
|
{
|
|
QByteArray yes = "YES";
|
|
XChangeProperty(QX11Info::display(),
|
|
winId(),
|
|
atom, atom, 8,
|
|
PropModeReplace,
|
|
reinterpret_cast<unsigned char *>(yes.data()), 3);
|
|
}
|
|
};
|
|
|
|
class QtSingletonSysPrivate
|
|
{
|
|
public:
|
|
Atom selAtom;
|
|
Atom typAtom;
|
|
Atom listenAtom;
|
|
QWidget *listener;
|
|
QByteArray xpcs;
|
|
bool owner;
|
|
|
|
QString login() const;
|
|
QByteArray toXPCS(const QString &str) const; // X Portable Character Set
|
|
|
|
void sendMessageTo(Window wid, Atom sel, Atom typ,
|
|
const QString &message) const;
|
|
bool getProperty(char** data, unsigned long* nitems,
|
|
Window win, Atom sel, Atom typ) const;
|
|
void findWindows(QList<Window> &windows);
|
|
};
|
|
|
|
QByteArray QtSingletonSysPrivate::toXPCS(const QString &str) const
|
|
{
|
|
QString strtmp(str);
|
|
if (strtmp.isEmpty() || strtmp[0] != '_')
|
|
strtmp.prepend('_');
|
|
QByteArray tmp = strtmp.toLocal8Bit();
|
|
for (int i = 0; i < tmp.size(); ++i) {
|
|
if (!xpcs.contains(tmp[i]))
|
|
tmp[i] = '_';
|
|
}
|
|
return tmp;
|
|
}
|
|
|
|
bool QtSingletonSysPrivate::getProperty(char** data, unsigned long* nitems,
|
|
Window win, Atom sel, Atom typ) const
|
|
{
|
|
if (data == 0 || nitems == 0)
|
|
return false;
|
|
|
|
Atom actualType;
|
|
int actualFormat, ret;
|
|
long length = 1024;
|
|
unsigned long bafter;
|
|
for (;;) {
|
|
*data = 0;
|
|
*nitems = 0;
|
|
|
|
ret = XGetWindowProperty(QX11Info::display(),
|
|
win,
|
|
sel,
|
|
0L, length,
|
|
False,
|
|
typ,
|
|
&actualType,
|
|
&actualFormat,
|
|
nitems,
|
|
&bafter,
|
|
reinterpret_cast<unsigned char **>(data));
|
|
if (ret == Success && actualType == typ && *nitems > 0) {
|
|
if (bafter == 0)
|
|
return TRUE;
|
|
else {
|
|
if (actualFormat == 8)
|
|
length += (bafter / 4) + 1;
|
|
else if (actualFormat == 16)
|
|
length += (bafter / 2) + 1;
|
|
else if (actualFormat == 32)
|
|
length += bafter;
|
|
}
|
|
} else {
|
|
if (*nitems > 0 && *data != 0)
|
|
XFree(*data);
|
|
return FALSE;
|
|
}
|
|
if (*nitems > 0 && *data != 0)
|
|
XFree(*data);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
void QtSingletonSysPrivate::findWindows(QList<Window> &windows)
|
|
{
|
|
Window root, parent;
|
|
Window* children = 0;
|
|
unsigned int nchildren;
|
|
QDesktopWidget* desktop = qApp->desktop();
|
|
char* data;
|
|
unsigned long nitems;
|
|
|
|
for (int i = 0; i < desktop->numScreens(); ++i) {
|
|
if (XQueryTree(QX11Info::display(),
|
|
desktop->screen(i)->winId(),
|
|
&root, &parent,
|
|
&children, &nchildren) == 0)
|
|
continue;
|
|
if (nchildren > 0 && children != 0) {
|
|
for (unsigned int i = 0; i < nchildren; ++i) {
|
|
if (getProperty(&data, &nitems, children[i],
|
|
listenAtom, listenAtom)) {
|
|
if (data != 0)
|
|
XFree(data);
|
|
windows.append(children[i]);
|
|
}
|
|
}
|
|
XFree(children);
|
|
}
|
|
}
|
|
}
|
|
|
|
QString QtSingletonSysPrivate::login() const
|
|
{
|
|
struct passwd *pwd = getpwuid(getuid());
|
|
if (pwd) {
|
|
return QString(pwd->pw_name);
|
|
}
|
|
return QString();
|
|
}
|
|
|
|
void QtSingletonSysPrivate::sendMessageTo(Window wid, Atom sel, Atom typ,
|
|
const QString &message) const
|
|
{
|
|
if (typ != None) {
|
|
QByteArray umsg = message.toUtf8();
|
|
XChangeProperty(QX11Info::display(),
|
|
wid,
|
|
sel, typ, 8,
|
|
PropModeReplace,
|
|
reinterpret_cast<unsigned char *>(umsg.data()),
|
|
umsg.size());
|
|
}
|
|
}
|
|
|
|
void QtSingleApplication::sysInit()
|
|
{
|
|
sysd = new QtSingletonSysPrivate;
|
|
sysd->selAtom = None;
|
|
sysd->typAtom = None;
|
|
sysd->listenAtom = None;
|
|
sysd->listener = 0;
|
|
sysd->xpcs.resize(95);
|
|
sysd->xpcs = QByteArray("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~");
|
|
sysd->owner = false;
|
|
}
|
|
|
|
void QtSingleApplication::sysCleanup()
|
|
{
|
|
if (sysd) {
|
|
// make the old owner the current owner
|
|
if (sysd->owner) {
|
|
Window newowner = None;
|
|
|
|
// We don't want any of the windows to be destroyed
|
|
// while we're looping over them. So we grab the server.
|
|
XGrabServer(QX11Info::display());
|
|
|
|
QList<Window> windows;
|
|
sysd->findWindows(windows);
|
|
QList<Window>::ConstIterator it = windows.begin();
|
|
while (it != windows.end()) {
|
|
if (*it != sysd->listener->winId())
|
|
newowner = *it;
|
|
++it;
|
|
}
|
|
if (newowner != None)
|
|
sysd->sendMessageTo(newowner,
|
|
sysd->selAtom, sysd->typAtom,
|
|
"a");
|
|
|
|
XUngrabServer(QX11Info::display());
|
|
|
|
// Make sure that the server releases the grab as soon
|
|
// as possible.
|
|
XFlush(QX11Info::display());
|
|
}
|
|
delete sysd->listener;
|
|
delete sysd;
|
|
}
|
|
}
|
|
|
|
void QtSingleApplication::initialize(bool activate)
|
|
{
|
|
if (sysd->selAtom != None)
|
|
return;
|
|
|
|
QByteArray login = sysd->toXPCS(id()+sysd->login());
|
|
sysd->selAtom = XInternAtom(QX11Info::display(),
|
|
login,
|
|
False);
|
|
sysd->typAtom = XInternAtom(QX11Info::display(),
|
|
"_QTSINGLEAPPLICATION",
|
|
False);
|
|
sysd->listenAtom = XInternAtom(QX11Info::display(),
|
|
login+"_LISTENER",
|
|
False);
|
|
|
|
if (sysd->selAtom != None) {
|
|
sysd->listener = new QtSingletonListener(sysd->listenAtom);
|
|
Window lid = sysd->listener->winId();
|
|
XSetSelectionOwner(QX11Info::display(),
|
|
sysd->selAtom,
|
|
lid,
|
|
CurrentTime);
|
|
if (XGetSelectionOwner(QX11Info::display(), sysd->selAtom) == lid)
|
|
sysd->owner = true;
|
|
}
|
|
|
|
if (activate)
|
|
connect(this, SIGNAL(messageReceived(const QString&)),
|
|
this, SLOT(activateWindow()));
|
|
}
|
|
|
|
bool QtSingleApplication::isRunning() const
|
|
{
|
|
QByteArray login = sysd->toXPCS(id()+sysd->login());
|
|
Atom tmp = XInternAtom(QX11Info::display(),
|
|
login,
|
|
True);
|
|
|
|
if (tmp != None) {
|
|
WId wid = XGetSelectionOwner(QX11Info::display(),
|
|
tmp);
|
|
return (wid != None);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
bool QtSingleApplication::sendMessage(const QString &message, int)
|
|
{
|
|
QByteArray login = sysd->toXPCS(id()+sysd->login());
|
|
Atom sel = XInternAtom(QX11Info::display(),
|
|
login,
|
|
True);
|
|
if (sel == None)
|
|
return FALSE;
|
|
WId wid = XGetSelectionOwner(QX11Info::display(), sel);
|
|
if (wid != None) {
|
|
Atom typ = XInternAtom(QX11Info::display(),
|
|
"_QTSINGLEAPPLICATION",
|
|
True);
|
|
if (typ != None) {
|
|
sysd->sendMessageTo(wid, sel, typ, message);
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/*!
|
|
\internal
|
|
*/
|
|
|
|
bool QtSingleApplication::x11EventFilter(XEvent *msg)
|
|
{
|
|
if (sysd->listener != 0) {
|
|
if (msg->type == PropertyNotify) {
|
|
XPropertyEvent pev = msg->xproperty;
|
|
if (pev.window == sysd->listener->winId()) {
|
|
if (!sysd->owner) {
|
|
// We aren't the current owner, but some program changed our
|
|
// property. This will happen if more than one
|
|
// QtSingleApplication instance of the same type is running
|
|
// and one of them closes and we were the previous owner
|
|
// of the selection. Try to make ourselves the new owner
|
|
Window lid = sysd->listener->winId();
|
|
XSetSelectionOwner(QX11Info::display(),
|
|
sysd->selAtom,
|
|
lid,
|
|
CurrentTime);
|
|
if (XGetSelectionOwner(QX11Info::display(), sysd->selAtom) == lid)
|
|
sysd->owner = true;
|
|
return TRUE;
|
|
}
|
|
char* data = 0;
|
|
unsigned long nitems = 0;
|
|
if (sysd->getProperty(&data, &nitems, pev.window,
|
|
sysd->selAtom, sysd->typAtom)) {
|
|
QString str = QString::fromUtf8(reinterpret_cast<char *>(data), nitems);
|
|
if (data)
|
|
XFree(data);
|
|
emit messageReceived(str);
|
|
|
|
return TRUE;
|
|
}
|
|
if (data)
|
|
XFree(data);
|
|
}
|
|
} else if (msg->type == SelectionClear) {
|
|
if (msg->xselectionclear.selection == sysd->selAtom)
|
|
sysd->owner = false;
|
|
}
|
|
}
|
|
return QApplication::x11EventFilter(msg);
|
|
}
|
|
|