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); |
|
}
|
|
|