/************************************************************************** ** ** This file is part of Qt Simulator ** ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: Nokia Corporation (info@qt.nokia.com) ** ** ** GNU Lesser General Public License Usage ** ** This file may be used under the terms of the GNU Lesser General Public ** License version 2.1 as published by the Free Software Foundation and ** appearing in the file LICENSE.LGPL included in the packaging of this file. ** Please review the following information to ensure the GNU Lesser General ** Public License version 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional ** rights. These rights are described in the Nokia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** Other Usage ** ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. ** ** If you have questions regarding the use of this file, please contact ** Nokia at info@qt.nokia.com. ** **************************************************************************/ #include "application.h" #include "applicationmanager.h" #include "applicationtablewidget.h" #include "deviceitem.h" #include "widgetmanager.h" #include "widget.h" #include #include #include #include #include #include #include #include #include #ifdef Q_OS_WIN # include # include #elif defined(Q_OS_UNIX) # include # include # include #endif namespace { int gAppIdCounter = 0; } ApplicationManager::ApplicationManager(const VersionStruct &simulatorVersion, QObject *parent) : QObject(parent) , mVersion(simulatorVersion) , mServer(0) , mPhononManager(0) , mWidgetManager(0) , mTableWidget(0) , mSimulatorStarted(false) { // simulator socket connection QLocalSocket checkSocket; checkSocket.connectToServer(SIMULATOR_DISPLAY_SERVERNAME + mVersion.toString()); if (checkSocket.waitForConnected(100)) { mSimulatorStarted = true; return; } mServer = new QLocalServer(); QLocalServer::removeServer(SIMULATOR_DISPLAY_SERVERNAME + mVersion.toString()); if (!mServer->listen(SIMULATOR_DISPLAY_SERVERNAME + mVersion.toString())) { qFatal("Could not open mServer"); } connect(mServer, SIGNAL(newConnection()), this, SLOT(handleConnection())); connect(this, SIGNAL(applicationRegistered(Application*)), this, SLOT(registerApplication(Application*))); // debug window mTableWidget = new ApplicationTableWidget(); mConfigWidget = new QWidget(); QHBoxLayout* layout = new QHBoxLayout; layout->addWidget(mTableWidget); mConfigWidget->setLayout(layout); connect(mTableWidget, SIGNAL(killApplication(int)), this, SLOT(killApplication(int))); // watch for simulator clients that have been terminated QTimer* watchDogTimer = new QTimer(this); watchDogTimer->setObjectName(QLatin1String("watchDogTimer")); connect(watchDogTimer, SIGNAL(timeout()), SLOT(watchDogTimer())); watchDogTimer->start(5000); } ApplicationManager::~ApplicationManager() { if (mServer) { mServer->close(); QLocalServer::removeServer(SIMULATOR_DISPLAY_SERVERNAME + mVersion.toString()); delete mServer; mServer = 0; } if (mTableWidget) delete mTableWidget; } void ApplicationManager::registerApplication(Application *newApp) { mApps.push_back(newApp); // add to debug application table mTableWidget->insertRow(0); QTableWidgetItem *item = new QTableWidgetItem(QString::number(newApp->id())); item->setData(0, newApp->id()); mTableWidget->setItem(0, 0, item); mTableWidget->setItem(0, 1, new QTableWidgetItem(newApp->name())); mTableWidget->setItem(0, 2, new QTableWidgetItem(newApp->clientVersion().toString())); mTableWidget->sortItems(0); if (mApps.size() == 1) emit firstAppRegistered(); //qDebug() << "ApplicationManager registered:" << newApp->name(); } void ApplicationManager::unregisterApplication(int id) { emit applicationUnRegistered(id); for (int i = 0; i < mApps.size(); ++i) { if (mApps.at(i)->id() == id) { Application* item = mApps.takeAt(i); //qDebug() << "ApplicationManager removes:" << item->name(); item->deleteLater(); // remove from debug application table for (int r = 0; r < mTableWidget->rowCount(); ++r) { if (mTableWidget->item(r, 0)->data(0).toInt() == id) { mTableWidget->removeRow(r); break; } } break; } } if (mApps.isEmpty()) { emit lastAppUnregistered(); } } bool ApplicationManager::hasApplications() const { return !mApps.empty(); } bool ApplicationManager::simulatorAlreadyStarted() const { return mSimulatorStarted; } bool ApplicationManager::initializeFontDirectory() { // make sure that we have a valid font directory QDir fontDir("fonts"); if (!fontDir.exists()) { QMessageBox errorMsg; errorMsg.setWindowTitle(tr("\"%1\" folder does not exist").arg("Fonts")); errorMsg.setText(tr("The \"%1\" folder could not be located in the installation directory.").arg("fonts")); errorMsg.setIcon(QMessageBox::Critical); errorMsg.exec(); return false; } mFontDirectory = fontDir.absolutePath(); return true; } void ApplicationManager::killApplication(int id) { for (int i = 0; i < mApps.size(); ++i) { if (mApps.at(i)->id() == id) { Application* item = mApps.at(i); item->kill(); break; } } } void ApplicationManager::killCurrentApplication() { Widget *activeWidget = mWidgetManager->activeWidget(); if (!activeWidget) return; mWidgetManager->activeWidget()->owner->kill(); } void ApplicationManager::killAllApplications() { foreach (Application* item, mApps) { item->kill(); } } Application* ApplicationManager::applicationForId(int id) { foreach(Application* item, mApps) { if (item->id() == id) return item; } return 0; } void ApplicationManager::handleConnection() { // get the incoming connection QLocalSocket* socket = mServer->nextPendingConnection(); if (!socket->isValid()) qWarning() << "Invalid socket!"; using namespace QtSimulatorPrivate; // read the command id qint32 requestCommand = 0; qint64 bytesToRead = sizeof(requestCommand); qint64 bytesRead = qt_blockingRead( socket, reinterpret_cast(&requestCommand), bytesToRead, 500); if (bytesRead < bytesToRead) { qWarning("Dropped incoming connection, couldn't read command id."); return; } if (requestCommand != Command::ApplicationConnect) { qWarning("Dropped incoming connection, it sent an invalid command."); return; } // read the command ApplicationConnectCommand cmd; bytesToRead = sizeof(cmd.request); bytesRead = qt_blockingRead( socket, reinterpret_cast(&cmd.request), bytesToRead, 500); if (bytesRead < bytesToRead) { qWarning("Dropped incoming connection, couldn't read command data."); return; } // send a reply cmd.reply.appId = gAppIdCounter + 1; cmd.reply.version = mVersion; qint64 bytesToWrite = sizeof(cmd.reply); qint64 bytesWritten = qt_blockingWrite( socket, reinterpret_cast(&cmd.reply), bytesToWrite, 500); if (bytesWritten < bytesToWrite) { qWarning("Dropped incoming connection, couldn't send connect reply."); return; } // success: register new application gAppIdCounter++; Application *app = new Application(QString::fromLatin1(cmd.request.applicationName), cmd.reply.appId, cmd.request.applicationPid, socket, this); if ( !app->socket()->isOpen()) { qWarning("Could not connect to application: Make sure %s has read/write permissions to the application socket.", qPrintable(qApp->applicationName())); delete app; return; } VersionStruct version; version = cmd.request.version; app->setClientVersion(version); app->setPhononManager(mPhononManager); app->setWidgetManager(mWidgetManager); emit applicationRegistered(app); } void ApplicationManager::setPhononManager(PhononManager *manager) { Q_ASSERT(!mPhononManager); mPhononManager = manager; } void ApplicationManager::setWidgetManager(WidgetManager *widMan) { // changing the widget manager not yet supported Q_ASSERT(!mWidgetManager); mWidgetManager = widMan; // basic display info data that's fixed for a widget manager QImage tmp(16, 16, widMan->imageFormat()); mDisplayInfo.format = tmp.format(); mDisplayInfo.numColors = tmp.numColors(); mDisplayInfo.depth = tmp.depth(); QDesktopWidget *desktop = QApplication::desktop(); mDisplayInfo.hostDpi = QSize(desktop->logicalDpiX(), desktop->logicalDpiY()); } QString ApplicationManager::fontDirectory() { return mFontDirectory; } const QtSimulatorPrivate::DisplayInfo &ApplicationManager::displayInfo() { return mDisplayInfo; } void ApplicationManager::watchDogTimer() { #ifdef Q_OS_WIN QHash pidMapping; foreach (Application* app, mApps) pidMapping.insert(app->processId(), app); HANDLE hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (hSnapShot == INVALID_HANDLE_VALUE) { QTimer* watchDogTimer = findChild(QLatin1String("watchDogTimer")); watchDogTimer->stop(); qWarning("Cannot create process snapshot. Watchdog timer stopped."); return; } BOOL bSuccess; PROCESSENTRY32 processEntry; processEntry.dwSize = sizeof(processEntry); bSuccess = Process32First(hSnapShot, &processEntry); while (bSuccess) { Application* app = pidMapping.value(processEntry.th32ProcessID, 0); if (app) pidMapping.remove(processEntry.th32ProcessID); bSuccess = Process32Next(hSnapShot, &processEntry); } CloseHandle(hSnapShot); QHash::iterator it; for (it = pidMapping.begin(); it != pidMapping.end(); ++it) watchDogBark(it.value()); #elif defined(Q_OS_UNIX) foreach (Application *app, mApps) { pid_t pgid = getpgid(app->processId()); if (pgid == -1 && errno == ESRCH) watchDogBark(app); } #else QTimer* watchDogTimer = findChild(QLatin1String("watchDogTimer")); watchDogTimer->stop(); qWarning("ApplicationManager::watchDogTimer not implemented"); #endif } void ApplicationManager::watchDogBark(Application* deadApp) { qWarning() << "Application died without unregistering. Unregistering now:" << deadApp->name(); unregisterApplication(deadApp->id()); } void ApplicationManager::updateDisplayInformation(const QSize &resolution, const DeviceData &device) { using namespace std; qreal pixelDiagonal = sqrt(pow((qreal)resolution.width(), 2) + pow((qreal)resolution.height(), 2)); QSizeF displaySizeInch(QSizeF(resolution) * device.diagonalInInch / pixelDiagonal); mDisplayInfo.size = resolution; mDisplayInfo.availableRect = device.availableGeometry; // calculate dpi if (device.forceDpi != -1) { mDisplayInfo.dpi = QSize(device.forceDpi, device.forceDpi); } else { mDisplayInfo.dpi.setWidth(resolution.width() / displaySizeInch.width()); mDisplayInfo.dpi.setHeight(resolution.height() / displaySizeInch.height()); } mDisplayInfo.defaultFontSize = device.defaultFontSize; mDisplayInfo.style = device.style; mDisplayInfo.styleTheme = device.styleTheme; mDisplayInfo.hasSoftKeys = !device.symbianSoftKeys.isEmpty(); const qreal MMperInch = 25.4; mDisplayInfo.sizeMM = QSize(displaySizeInch.width() * MMperInch, displaySizeInch.height() * MMperInch); foreach (Application *application, mApps) QtSimulatorPrivate::RemoteMetacall::call(application->socket(), QtSimulatorPrivate::NoSync, "updateDisplayInformation"); double newSoftKeyTextSize = 1.0 * mDisplayInfo.defaultFontSize / mDisplayInfo.hostDpi.width() * mDisplayInfo.dpi.width(); emit softkeyTextSizeChanged(newSoftKeyTextSize); } void ApplicationManager::updateMobilityVersion(int appId, VersionStruct version) { for (int i = 0; i < mTableWidget->rowCount(); i++) { QTableWidgetItem *item = mTableWidget->itemAt(i, 0); if (item->data(0) == appId) { mTableWidget->setItem(i, 3, new QTableWidgetItem(version.toString())); return; } } }