/* * Copyright (C) 2004 Girish Ramakrishnan All Rights Reserved. * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this software; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. */ // $Id: kdocker.cpp,v 1.24 2005/02/04 10:25:46 cs19713 Exp $ #include #include #include #include #include #include #include #include #include "trace.h" #include "traylabelmgr.h" #include "kdocker.h" #include #include #include #include #include // #define TMPFILE_PREFIX QString("/tmp/kdocker.") #define TMPFILE_PREFIX QDir::homeDirPath() + "/.kdocker." KDocker::KDocker(int& argc, char** argv) :QApplication(argc, argv), mTrayLabelMgr(0) { INIT_TRACE(); /* * Load localisation strings. Most examples I have seen load QTranslator * in main(). As a result the translator object lingers around till the end * of the program. I tried the same thing here and all i got was translations * for usage(). You dont want to know about the sleepless night i spent * trying to figure this out (yup, the source helped) */ QTranslator *translator = new QTranslator(0); QString f = QString("kdocker_") + QTextCodec::locale(); if (!translator->load(f, QString(TRANSLATIONS_PATH)) && !translator->load(f, applicationDirPath() + "/i18n") && !translator->load(f, QDir::currentDirPath() + "/i18n")) { qDebug("Sorry, your locale is not supported. If you are interested " "in providing translations for your locale, contact " "gramakri@uiuc.edu\n"); } installTranslator(translator); // Attempt doing anything only if the CLI arguments were good opterr = 0; // suppress the warning int option; while ((option = getopt(argc, argv, TrayLabelMgr::options().latin1())) != EOF) { if (option == '?') { if (optopt == 'v') printVersion(); else printUsage(optopt); ::exit(0); } } /* * Detect and transfer control to previous instance (if one exists) * _KDOCKER_RUNNING is a X Selection. We start out by trying to locate the * selection owner. If someone else owns it, transfer control to that * instance of KDocker */ Display *display = QPaintDevice::x11AppDisplay(); Atom kdocker = XInternAtom(display, "_KDOCKER_RUNNING", False); Window prev_instance = XGetSelectionOwner(display, kdocker); if (prev_instance == None) { mSelectionOwner = XCreateSimpleWindow(display, qt_xrootwin(), 1, 1, 1, 1, 1, 1, 1); XSetSelectionOwner(display, kdocker, mSelectionOwner, CurrentTime); TRACE("Selection owner set to 0x%x", (unsigned) mSelectionOwner); mTrayLabelMgr = TrayLabelMgr::instance(); } else notifyPreviousInstance(prev_instance); // does not return } void KDocker::printVersion(void) { qDebug("Qt: %s", qVersion()); qDebug("KDocker: %s", KDOCKER_APP_VERSION); } // Prints the CLI arguments. Does not return void KDocker::printUsage(char optopt) { if (optopt != 'h') qDebug(tr("kdocker: invalid option -- %1").arg(optopt)); qDebug(tr("Usage: KDocker [options] command\n")); qDebug(tr("Docks any application into the system tray\n")); qDebug(tr("command \tCommand to execute\n")); qDebug(tr("Options")); qDebug(tr("-a \tShow author information")); qDebug(tr("-b \tDont warn about non-normal windows (blind mode)")); qDebug(tr("-d \tDisable session management")); qDebug(tr("-e \tEnable session management")); qDebug(tr("-f \tDock window that has the focus(active window)")); qDebug(tr("-h \tDisplay this help")); qDebug(tr("-i icon\tCustom dock Icon")); qDebug(tr("-l \tLaunch on startup")); qDebug(tr("-m \tKeep application window mapped (dont hide on dock)")); qDebug(tr("-o \tDock when obscured")); qDebug(tr("-p secs\tSet ballooning timeout (popup time)")); qDebug(tr("-q \tDisable ballooning title changes (quiet)")); qDebug(tr("-t \tRemove this application from the task bar")); qDebug(tr("-v \tDisplay version")); qDebug(tr("-w wid \tWindow id of the application to dock\n")); qDebug(tr("NOTE: Use -d for all startup scripts.\n")); qDebug(tr("Bugs and wishes to gramakri@uiuc.edu")); qDebug(tr("Project information at http://kdocker.sourceforge.net")); } void KDocker::notifyPreviousInstance(Window prevInstance) { Display *display = QPaintDevice::x11AppDisplay(); TRACE("Notifying previous instance [%x]", (unsigned) prevInstance); // Dump all arguments in temporary file QFile f(TMPFILE_PREFIX + QString().setNum(getpid())); if (!f.open(IO_WriteOnly)) return; QTextStream s(&f); /* * Its normal to use KDocker in startup scripts. We could be getting restored * from a session at the same time. So, if we were getting restored and * another instance already exists, send across the session id. Remember, qt * strips out all the arguments that it understands. So need to do it by hand. */ if (isSessionRestored()) s << argv()[0] << " " << "-session" << " " << sessionId(); else for (int i = 0; i < argc(); i++) s << argv()[i] << " "; f.close(); /* * Now tell our previous instance that we came to pass. Actually, it can * figure it out itself using PropertyNotify events but this is a lot nicer */ XClientMessageEvent dock_event; memset(&dock_event, 0, sizeof(XClientMessageEvent)); dock_event.display = display; dock_event.window = prevInstance; dock_event.send_event = True; dock_event.type = ClientMessage; dock_event.message_type = 0x220679; // it all started this day dock_event.format = 8; dock_event.data.l[0] = 0xBABE; // love letter ;) dock_event.data.l[1] = getpid(); XSendEvent(display, prevInstance, False, 0, (XEvent *) &dock_event); XSync(display, False); ::exit(0); } /* * The X11 Event filter called by Qt. Look out for ClientMessage events from * our new instance */ bool KDocker::x11EventFilter(XEvent * event) { if (event->type == ClientMessage) { // look for requests from a new instance of kdocker XClientMessageEvent *client = (XClientMessageEvent *) event; if (!(client->message_type == 0x220679 && client->data.l[0] == 0xBABE)) return FALSE; TRACE("ClientMessage from PID=%ld. SelOwn=0x%x", client->data.l[1], (unsigned) mSelectionOwner); char tmp[50]; struct stat buf; sprintf(tmp, TMPFILE_PREFIX "%ld", client->data.l[1]); if (stat(tmp, &buf) || (getuid()!=buf.st_uid)) { /* * We make sure that the owner of this process and the owner of the file * are the same. This will prevent someone from executing arbitrary * programs by sending client message. Of course, you can send a message * only if you are authenticated to the X session and have permission to * create files in TMPFILE_PREFIX. So this code is there just for the * heck of it. */ TRACE("User %i is trying something fishy...", buf.st_uid); unlink(tmp); return TRUE; } QFile f(tmp); if (!f.open(IO_ReadOnly)) return TRUE; QTextStream s(&f); QStringList argv; while (!s.atEnd()) { QString x; s >> x; argv += x; } f.close(); unlink(tmp); // delete the tmp file mTrayLabelMgr->processCommand(argv); return TRUE; } else return mTrayLabelMgr->x11EventFilter(event); } /* * XSMP Support */ void KDocker::saveState(QSessionManager &sm) { QString sf = mTrayLabelMgr->saveSession(); QStringList discard_command; discard_command << "rm" << sf; sm.setDiscardCommand(discard_command); sm.setRestartHint(QSessionManager::RestartIfRunning); QStringList restart_command; restart_command << this->argv()[0] << "-session" << sm.sessionId(); sm.setRestartCommand(restart_command); TRACE("SessionFile=%s AppName=%s", sf.latin1(), this->argv()[0]); DUMP_TRACE(QDir::homeDirPath() + "/kdocker.trace"); // sm.setRestartCommand(applicationFilePath()); }