/**************************************************************************** ** ** 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 #include #include #include #include #include #include #include #include #include #include #include #include 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(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 &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(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 &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(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 windows; sysd->findWindows(windows); QList::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(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); }