/* # Copyright (c) 2009, 2010 - OpenSLX Project, Computer Center University of # Freiburg # # This program is free software distributed under the GPL version 2. # See http://openslx.org/COPYING # # If you have any feedback please consult http://openslx.org/feedback and # send your suggestions, praise, or complaints to feedback@openslx.org # # General information about OpenSLX can be found at http://openslx.org/ # ----------------------------------------------------------------------------- # pvsClientGUI.cpp # - main loop for pvs client GUI. # - draws toolbar and system tray icon. # - uses DBUS to communicate with pvs client. # ----------------------------------------------------------------------------- */ #include #include "pvsgui.h" #include "version.h" PVSGUI::PVSGUI(QWidget *parent) : QWidget(parent) { setupUi(this); // stop running pvs qDebug("[%s] Stopping pvs daemon.", metaObject()->className()); QProcess::execute("pvs -c stop"); _menu = new QMenu(this); _hostMenu = new QMenu(tr("Connect"), this); _hosts = new QHash (); _vncViewer = new ClientVNCViewer(); _chatDialog = new ClientChatDialog(); _configDialog = new ClientConfigDialog(); _infoDialog = new ClientInfoDialog(); _aboutDialog = new AboutDialog(); _hostMenu->setEnabled(false); hostButton->setEnabled(false); _trayIcon = new QSystemTrayIcon(QIcon(":cam_off32.svg"), this); _trayIcon->setContextMenu(_menu); _trayIcon->setVisible(true); _chatDialog->setTrayIcon(_trayIcon); // connect to D-Bus and get interface QDBusConnection dbus = QDBusConnection::sessionBus(); _ifaceDBus = new OrgOpenslxPvsInterface("org.openslx.pvs", "/", dbus, this); if (dbus.isConnected()) qDebug("[%s] Connection to DBus successful.", metaObject()->className()); else qDebug("[%s] ERROR: Could not connect to DBus!", metaObject()->className()); // start pvs daemon qDebug("[%s] Starting pvs daemon.", metaObject()->className()); QDBusPendingReply reply0 = _ifaceDBus->start(); reply0.waitForFinished(); if (reply0.isValid() && reply0.value()) qDebug("[%s] Connection to PVS daemon successful.", metaObject()->className()); else qDebug("[%s] ERROR: Could not connect to PVS daemon!", metaObject()->className()); // ask if restricted mode is on QDBusPendingReply reply1 = _ifaceDBus->isRestricted(); reply1.waitForFinished(); _restricted = reply1.value(); setupMenu(); // get available hosts QDBusPendingReply reply2 = _ifaceDBus->getAvailableHosts(); reply2.waitForFinished(); QStringList hosts = reply2.value(); if (reply2.isValid() && !hosts.isEmpty()) foreach (QString host, hosts) addHost(host); // already connected? QDBusPendingReply reply3 = _ifaceDBus->isConnected(); reply3.waitForFinished(); QString host = reply3.value(); if (reply3.isValid() && host != "") connected(host); else disconnected(); // listen on port 29481 for incoming file transfers _serverSocket = new QTcpServer(); _serverSocket->listen(QHostAddress::Any, 29481); connect(_serverSocket, SIGNAL(newConnection()), this, SLOT(receiveFile())); // signals & slots - menu connect(_showAction, SIGNAL(toggled(bool)), this, SLOT(setVisible(bool))); connect(_disconnectAction, SIGNAL(triggered()), this, SLOT(pvsDisconnect())); connect(_startChatAction, SIGNAL(triggered()), _chatDialog, SLOT(open())); connect(_sendFileAction, SIGNAL(triggered()), this, SLOT(sendFile())); connect(_configAction, SIGNAL(triggered()), _configDialog, SLOT(open())); connect(_showInfoAction, SIGNAL(triggered()), _infoDialog, SLOT(open())); connect(_aboutAction, SIGNAL(triggered()), _aboutDialog, SLOT(open())); connect(_quitAction, SIGNAL(triggered()), qApp, SLOT(quit())); connect(_configDialog, SIGNAL(configChanged()), this, SLOT(updateConfig())); // signals & slots - toolbar connect(_menu, SIGNAL(aboutToHide()), this, SLOT(hide())); connect(_hostMenu, SIGNAL(aboutToHide()), this, SLOT(hide())); connect(_hostMenu, SIGNAL(triggered(QAction*)), this, SLOT(pvsConnect(QAction*))); // signals & slots - dbus connect(_ifaceDBus, SIGNAL(showMessage(QString, QString, bool)), this, SLOT(showMessage(QString, QString, bool))); connect(_ifaceDBus, SIGNAL(connected(QString)), this, SLOT(connected(QString))); connect(_ifaceDBus, SIGNAL(disconnected()), this, SLOT(disconnected())); connect(_ifaceDBus, SIGNAL(addHost(QString)), this, SLOT(addHost(QString))); connect(_ifaceDBus, SIGNAL(delHost(QString)), this, SLOT(delHost(QString))); connect(_ifaceDBus, SIGNAL(incomingMulticastTransferNew(qulonglong, QString, QString, qulonglong)), SLOT(incomingMulticastFile(qulonglong, QString, QString, qulonglong))); // show toolbar setWindowFlags(Qt::WindowStaysOnTopHint | Qt::X11BypassWindowManagerHint | Qt::FramelessWindowHint); setAttribute(Qt::WA_AlwaysShowToolTips); } PVSGUI::~PVSGUI() { _ifaceDBus->quit(); } //////////////////////////////////////////////////////////////////////////////// // Public void PVSGUI::updateConfig() { if (_settings.value("Display/location").isNull()) setPosition(POSITION_TOP_CENTER); else setPosition(_settings.value("Display/location").toInt()); } void PVSGUI::setVisible(bool visible) { QWidget::setVisible(visible); _showAction->setChecked(isVisible()); } void PVSGUI::setPosition(int position) { _position = position; switch (_position) { case POSITION_TOP_LEFT: move(0, 0); break; case POSITION_TOP_CENTER: move((QApplication::desktop()->width() - width()) / 2, 0); break; case POSITION_TOP_RIGHT: move(QApplication::desktop()->width() - width(), 0); break; case POSITION_BOTTOM_LEFT: move(0, QApplication::desktop()->height() - height()); break; case POSITION_BOTTOM_CENTER: move((QApplication::desktop()->width() - width()) / 2, QApplication::desktop()->height() - height()); break; case POSITION_BOTTOM_RIGHT: move(QApplication::desktop()->width() - width(), QApplication::desktop()->height() - height()); break; default: updateConfig(); } } void PVSGUI::hide() { if (!_menu->isVisible() && !_hostMenu->isVisible()) hide(true); } //////////////////////////////////////////////////////////////////////////////// // Protected void PVSGUI::leaveEvent(QEvent * e) { if (!_menu->isVisible() && !_hostMenu->isVisible()) hide(true); QWidget::leaveEvent(e); } void PVSGUI::enterEvent(QEvent * e) { hide(false); QWidget::enterEvent(e); } void PVSGUI::mousePressEvent(QMouseEvent *event) { QApplication::setOverrideCursor(QCursor(Qt::ClosedHandCursor)); _clickPoint = event->pos(); } void PVSGUI::mouseReleaseEvent(QMouseEvent *event) { QApplication::setOverrideCursor(QCursor(Qt::ArrowCursor)); } void PVSGUI::mouseMoveEvent(QMouseEvent *event) { if (event->globalX() - _clickPoint.x() >= 0 && event->globalX() - _clickPoint.x() + width() <= QApplication::desktop()->width()) { move(event->globalX() - _clickPoint.x(), y()); } } //////////////////////////////////////////////////////////////////////////////// // Private void PVSGUI::setupMenu() { // setup actions _showAction = new QAction(tr("Show &toolbar"), this); _showAction->setCheckable(true); _disconnectAction = new QAction(tr("&Disconnect"), this); _startChatAction = new QAction(tr("C&hat"), this); _sendFileAction = new QAction(tr("&Send File"), this); _configAction = new QAction(tr("&Config"), this); _showInfoAction = new QAction(tr("&Information"), this); _aboutAction = new QAction(tr("&About"), this); _quitAction = new QAction(tr("&Quit"), this); // setup menu _menu->addAction(_showAction); if (!_restricted) _menu->addMenu(_hostMenu); if (!_restricted) _menu->addAction(_disconnectAction); _menu->addAction(_showInfoAction); _menu->addSeparator(); _menu->addAction(_startChatAction); _menu->addAction(_sendFileAction); _menu->addSeparator(); if (!_restricted) _menu->addAction(_configAction); _menu->addAction(_aboutAction); _menu->addSeparator(); if (!_restricted) _menu->addAction(_quitAction); pvsButton->setMenu(_menu); hostButton->setMenu(_hostMenu); } void PVSGUI::hide(bool b) { if (b) { if (_position <= POSITION_TOP_RIGHT) move(x(), 2 - height()); else move(x(), QApplication::desktop()->height() - 2); } else { if (_position <= POSITION_TOP_RIGHT) move(x(), 0); else move(x(), QApplication::desktop()->height() - height()); } } void PVSGUI::pvsConnect(QAction *action) { QString host = action->text(); action->setChecked(false); // we set it manually // already connected? if (host == hostButton->text()) { action->setChecked(true); return; } // ask user for passwd bool ok = false; QString passwd = QInputDialog::getText(0, tr("PVS Connection"), tr( "Please enter password (If not needed leave blank):"), QLineEdit::Password, QString(), &ok); if (ok) { _ifaceDBus->pvsConnect(host, passwd); // send via dbus _passwd = passwd; // TODO: we have to ask the backend for passwd! qDebug("[%s] Host '%s' send via DBus.", metaObject()->className(), qPrintable(host)); } } void PVSGUI::pvsDisconnect() { QMessageBox::StandardButton result = QMessageBox::question(0, tr( "PVS Connection"), tr("Are you sure you want to disconnect?"), QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Ok); if (result == QMessageBox::Ok) _ifaceDBus->pvsDisconnect(); } void PVSGUI::showMessage(QString title, QString msg, bool useDialog) { if (!_settings.value("Display/showmsgs").toBool()) return; // show balloon message if supported, otherwise show message dialog if (!useDialog && _trayIcon && QSystemTrayIcon::supportsMessages()) _trayIcon->showMessage(title, msg, QSystemTrayIcon::Information, 10000); else QMessageBox::about(0, title, msg); } void PVSGUI::connected(QString host) { statusLabel->setText( "

Online

"); showMessage(tr("PVS connection"), tr("Connected to ") + host); if (_hosts->contains(host)) _hosts->value(host)->setChecked(true); hostButton->setText(host); _disconnectAction->setEnabled(true); _startChatAction->setEnabled(true); _sendFileAction->setEnabled(true); _showInfoAction->setEnabled(true); _infoDialog->setHost(host); _infoDialog->setPasswd(_passwd); if (_trayIcon) { _trayIcon->setIcon(QIcon(":cam_on32.svg")); _trayIcon->setToolTip(tr("Connected to ") + host); } // try to disable screensaver if (QProcess::execute("xscreensaver-command -exit") == 0) qDebug("xscreensaver disabled"); else qDebug("xscreensaver NOT disabled"); if (QProcess::execute("gnome-screensaver-command --exit") == 0) qDebug("gnome-screensaver disabled"); else qDebug("gnome-screensaver NOT disabled"); } void PVSGUI::disconnected() { statusLabel->setText( "

Offline

"); if (_hosts->contains(hostButton->text())) _hosts->value(hostButton->text())->setChecked(false); hostButton->setText("-"); _disconnectAction->setEnabled(false); _startChatAction->setEnabled(false); _sendFileAction->setEnabled(false); _showInfoAction->setEnabled(false); _passwd = ""; if (_trayIcon) { _trayIcon->setIcon(QIcon(":cam_off32.svg")); _trayIcon->setToolTip(tr("Disconnected")); } _vncViewer->close(); } void PVSGUI::addHost(QString host) { // avoid duplicates if (_hosts->contains(host)) return; QAction *action = _hostMenu->addAction(host); action->setCheckable(true); _hosts->insert(host, action); if (!_hosts->isEmpty()) { _hostMenu->setEnabled(true); hostButton->setEnabled(true); } if (hostButton->text() == "-") showMessage(tr("PVS Connection"), tr("New host available: ") + host); } void PVSGUI::delHost(QString host) { if (_hosts->contains(host)) { _hostMenu->removeAction(_hosts->value(host)); _hosts->remove(host); } if (_hosts->isEmpty()) { _hostMenu->setEnabled(false); hostButton->setEnabled(false); } } void PVSGUI::sendFile() { ClientFileSendDialog *d = new ClientFileSendDialog(); d->open(); } void PVSGUI::receiveFile() { QTcpSocket *socket = _serverSocket->nextPendingConnection(); ClientFileReceiveDialog *d = new ClientFileReceiveDialog(socket); d->open(); } void PVSGUI::incomingMulticastFile(qulonglong transferID, QString sender, QString basename, qulonglong size) { ClientFileReceiveDialog *d = new ClientFileReceiveDialog(sender, transferID, basename, size, _ifaceDBus, this); d->open(); } //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// // Main void printHelp() { QTextStream qout(stdout); qout << QObject::tr("Usage: pvsgui [OPTIONS]...") << endl; qout << QObject::tr("Start the Pool Video Switch GUI.") << endl; qout << QObject::tr("Options:") << endl << endl; qout << "-b or --toolbar" << "\t\t" << QObject::tr("Start with toolbar.") << endl; qout << "-p or --position" << "\t" << QObject::tr("Set toolbar position (0-5)") << endl; qout << "-h or --help" << "\t\t" << QObject::tr("Show this help text and quit.") << endl; qout << "-v or --version" << "\t\t" << QObject::tr("Show version and quit.") << endl; qout << endl; qout.flush(); exit(0); } void printVersion() { QTextStream qout(stdout); qout << QObject::tr("Version: ") << VERSION_STRING << endl; qout << endl; qout.flush(); exit(0); } int main(int argc, char *argv[]) { bool visible = false; int position = -1; // parse command line arguments int opt = 0; int longIndex = 0; static const char *optString = "hvp:b?"; static const struct option longOpts[] = { { "help", no_argument, NULL, 'h' }, { "version", no_argument, NULL, 'v' }, { "position", required_argument, NULL, 'p' }, { "toolbar", no_argument, NULL, 'b' } }; opt = getopt_long( argc, argv, optString, longOpts, &longIndex ); while( opt != -1 ) { switch( opt ) { case 'h': printHelp(); break; case 'v': printVersion(); break; case 'p': position = atoi(optarg); break; case 'b': visible = true; break; case '?': exit(1); } opt = getopt_long( argc, argv, optString, longOpts, &longIndex ); } QtSingleApplication app(argc, argv); app.setQuitOnLastWindowClosed(false); app.setOrganizationName("openslx"); app.setOrganizationDomain("openslx.org"); app.setApplicationName("pvsgui"); // only one instance should be allowed if (app.sendMessage("")) { qDebug("[PVSGUI] ERROR: Already running. Exiting"); return 0; } // use system locale as language to translate gui QTranslator translator; translator.load(":pvsgui"); app.installTranslator(&translator); PVSGUI pvsgui; pvsgui.setPosition(position); pvsgui.setVisible(visible); pvsgui.hide(); return app.exec(); }