diff options
author | Tomas Junnonen <tomas.junnonen@nokia.com> | 2010-04-12 13:50:25 +0300 |
---|---|---|
committer | Tomas Junnonen <tomas.junnonen@nokia.com> | 2010-04-12 13:52:31 +0300 |
commit | da73676c8a5af66b55523a9cdfbfbea2baa88a2a (patch) | |
tree | 0a3b8933a1817c152116da5fa8a7b5cdd8102e60 /mthemedaemon | |
parent | 8832674482d3b9a7fcf77b0cfdcb8e6fe4960b4d (diff) |
Changes: Renamed dui to meegotouch
By: Holger, Daniel, Janne
RevBy: Tomas, Holger
Diffstat (limited to 'mthemedaemon')
-rw-r--r-- | mthemedaemon/.gitignore | 2 | ||||
-rw-r--r-- | mthemedaemon/keypresswaiter.h | 41 | ||||
-rw-r--r-- | mthemedaemon/main.cpp | 57 | ||||
-rw-r--r-- | mthemedaemon/mthemedaemon.pro | 62 | ||||
-rw-r--r-- | mthemedaemon/mthemedaemonserver.cpp | 409 | ||||
-rw-r--r-- | mthemedaemon/mthemedaemonserver.h | 93 | ||||
-rw-r--r-- | mthemedaemon/test/client.cpp | 421 | ||||
-rw-r--r-- | mthemedaemon/test/client.h | 109 | ||||
-rw-r--r-- | mthemedaemon/test/clientmanager.cpp | 352 | ||||
-rw-r--r-- | mthemedaemon/test/clientmanager.h | 68 | ||||
-rw-r--r-- | mthemedaemon/test/main.cpp | 65 | ||||
-rw-r--r-- | mthemedaemon/test/mthemedaemontest.pro | 50 | ||||
-rw-r--r-- | mthemedaemon/test/testdaemon/testdaemon.pro | 45 | ||||
-rw-r--r-- | mthemedaemon/test/themes/base/index.theme | 7 | ||||
-rw-r--r-- | mthemedaemon/test/themes/base/m/libduicore/style/libdui.css | 1 | ||||
-rw-r--r-- | mthemedaemon/test/themes/theme_one/index.theme | 8 | ||||
-rw-r--r-- | mthemedaemon/test/themes/theme_one/m/constants.ini | 34 | ||||
-rw-r--r-- | mthemedaemon/test/themes/theme_one/m/locale/fi/constants.ini | 34 |
18 files changed, 1858 insertions, 0 deletions
diff --git a/mthemedaemon/.gitignore b/mthemedaemon/.gitignore new file mode 100644 index 00000000..a6ec32e5 --- /dev/null +++ b/mthemedaemon/.gitignore @@ -0,0 +1,2 @@ +duithemedaemon + diff --git a/mthemedaemon/keypresswaiter.h b/mthemedaemon/keypresswaiter.h new file mode 100644 index 00000000..0ddcd959 --- /dev/null +++ b/mthemedaemon/keypresswaiter.h @@ -0,0 +1,41 @@ +/*************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (directui@nokia.com) +** +** This file is part of libmeegotouch. +** +** If you have questions regarding the use of this file, please contact +** Nokia at directui@nokia.com. +** +** This library is free software; you can redistribute it and/or +** modify it 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. +** +****************************************************************************/ + +#ifndef KEYPRESSWAITER_H +#define KEYPRESSWAITER_H + +#include <QApplication> +#include <QThread> +#include <QTextStream> +#include <QFile> +#include <QtDebug> + +class KeyPressWaiter : public QThread +{ + Q_OBJECT +public: + + void run() { + qDebug() << "Hit enter to exit.."; + QTextStream qin(stdin, QFile::ReadOnly); + qin.readLine(); + } +}; + +#endif diff --git a/mthemedaemon/main.cpp b/mthemedaemon/main.cpp new file mode 100644 index 00000000..b58cd2c5 --- /dev/null +++ b/mthemedaemon/main.cpp @@ -0,0 +1,57 @@ +/*************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (directui@nokia.com) +** +** This file is part of libmeegotouch. +** +** If you have questions regarding the use of this file, please contact +** Nokia at directui@nokia.com. +** +** This library is free software; you can redistribute it and/or +** modify it 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. +** +****************************************************************************/ + +#include "mthemedaemonserver.h" +#include <QDebug> +#include <QApplication> +#include <signal.h> + +#ifdef CLOSE_ON_ENTER +#include "keypresswaiter.h" +#endif + +void sigclose(int) +{ + // kill the daemon so that it can save it's current state (caches, refcounts, etc) + qApp->quit(); +} + +int main(int argc, char **argv) +{ + signal(SIGTERM, sigclose); + signal(SIGINT, sigclose); + + QApplication::setGraphicsSystem(QLatin1String("native")); + + QApplication app(argc, argv); + + MThemeDaemonServer server; + +#ifdef CLOSE_ON_ENTER + KeyPressWaiter keyWaiter; + QObject::connect(&keyWaiter, SIGNAL(finished()), qApp, SLOT(quit())); + keyWaiter.start(); +#endif + + if (app.arguments().indexOf("-quitimmediately") >= 0) { + QTimer::singleShot(0, &app, SLOT(quit())); + } + + return app.exec(); +} diff --git a/mthemedaemon/mthemedaemon.pro b/mthemedaemon/mthemedaemon.pro new file mode 100644 index 00000000..e1871173 --- /dev/null +++ b/mthemedaemon/mthemedaemon.pro @@ -0,0 +1,62 @@ +include(../mkspecs/common.pri) + +INCLUDEPATH += \ + . \ + ../src/include \ + ../src/corelib \ + ../src/corelib/core \ + +DEPENDPATH += $$INCLUDEPATH +QMAKE_LIBDIR += ../lib +TEMPLATE = app +DEPENDPATH += . + +QT += svg network + +DEFINES += MTHEME_PRINT_DEBUG +#DEFINES += CLOSE_ON_ENTER + +# enable QString optimizations +DEFINES += QT_USE_FAST_CONCATENATION QT_USE_FAST_OPERATOR_PLUS + +# Check for mixing of const and non-const iterators, +# which can cause problems when built with some compilers: +DEFINES += QT_STRICT_ITERATORS + +!win32:CONFIG += link_pkgconfig +PKGCONFIG += gconf-2.0 + +# Input +SOURCES += main.cpp \ + mthemedaemonserver.cpp \ + ../src/corelib/theme/mthemedaemon.cpp \ + ../src/corelib/theme/mcommonpixmaps.cpp \ + ../src/corelib/theme/mimagedirectory.cpp \ + ../src/corelib/theme/mthemedaemonclient.cpp \ + ../src/corelib/theme/mthemedaemonprotocol.cpp \ + ../src/corelib/theme/mthemeresourcemanager.cpp \ + ../src/corelib/core/mgconfitem.cpp \ + ../src/corelib/core/mcpumonitor.cpp \ + +HEADERS += \ + mthemedaemonserver.h \ + ../src/corelib/theme/mthemedaemon.h \ + ../src/corelib/theme/mcommonpixmaps.h \ + ../src/corelib/theme/mimagedirectory.h \ + ../src/corelib/theme/mthemedaemonclient.h \ + ../src/corelib/theme/mthemedaemonprotocol.h \ + ../src/corelib/theme/mthemeresourcemanager.h \ + ../src/corelib/core/mgconfitem.h \ + ../src/corelib/core/mcpumonitor.h \ + keypresswaiter.h \ + +QMAKE_EXTRA_TARGETS += check +check.depends = $$TARGET +check.commands = $$system(true) + +QMAKE_EXTRA_TARGETS += check-xml +check-xml.depends = $$TARGET +check-xml.commands = $$system(true) + +target.path = $$M_INSTALL_BIN +INSTALLS += target \ diff --git a/mthemedaemon/mthemedaemonserver.cpp b/mthemedaemon/mthemedaemonserver.cpp new file mode 100644 index 00000000..9a632171 --- /dev/null +++ b/mthemedaemon/mthemedaemonserver.cpp @@ -0,0 +1,409 @@ +/*************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (directui@nokia.com) +** +** This file is part of libmeegotouch. +** +** If you have questions regarding the use of this file, please contact +** Nokia at directui@nokia.com. +** +** This library is free software; you can redistribute it and/or +** modify it 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. +** +****************************************************************************/ + +#include "mthemedaemonserver.h" +#include <theme/mthemedaemonclient.h> +#include <MDebug> +#include <MNamespace> + +#include <QLocalSocket> +#include <QDir> + + +using namespace M::MThemeDaemonProtocol; + +MThemeDaemonServer::MThemeDaemonServer() : + currentTheme("/M/theme/name"), + currentLocale("/M/i18n/Language") +{ + const QString defaultTheme("devel"); + + // 1) make sure that gconf has some value for the current theme + if (currentTheme.value().isNull()) + currentTheme.set(defaultTheme); + + // 2) activate the current theme + QHash<MThemeDaemonClient *, QList<PixmapIdentifier> > pixmapsToReload; + if (!daemon.activateTheme(currentTheme.value().toString(), currentLocale.value().toString(), QList<MThemeDaemonClient *>(), pixmapsToReload)) { + // could not activate current theme, change to devel + if (!daemon.activateTheme(defaultTheme, currentLocale.value().toString(), QList<MThemeDaemonClient *>(), pixmapsToReload)) { + qFatal("MThemeDaemonServer - Could not find themes, aborting!"); + } + currentTheme.set(defaultTheme); + } + Q_ASSERT(pixmapsToReload.isEmpty()); + + // 3) make sure we're notified if locale or theme changes + connect(¤tTheme, SIGNAL(valueChanged()), SLOT(themeChanged())); + connect(¤tLocale, SIGNAL(valueChanged()), SLOT(localeChanged())); + + // 4) make sure we have a themedaemon directory in /var/cache/m/ + QDir cacheDir(MThemeDaemon::systemThemeCacheDirectory()); + if(!cacheDir.exists()) { + if(!cacheDir.mkpath(MThemeDaemon::systemThemeCacheDirectory())) { + qFatal("MThemeDaemonServer - Could not create cache directory for themedaemon. No write permissions to %s", + qPrintable(cacheDir.absolutePath())); + } + } + + // 5) make sure we have a cache directory for the current theme + if(!cacheDir.exists(currentTheme.value().toString())) { + if(!cacheDir.mkdir(currentTheme.value().toString())) { + qFatal("MThemeDaemonServer - Could not create cache directory for current theme. No write permissions to %s", + qPrintable(cacheDir.absolutePath())); + } + } + + + // start socket server for client registeration + // first remove the old one, if there's such + QLocalServer::removeServer(M::MThemeDaemonProtocol::ServerAddress); + connect(&server, SIGNAL(newConnection()), SLOT(clientConnected())); + if (!server.listen(M::MThemeDaemonProtocol::ServerAddress)) { + mWarning("MThemeDaemonServer") << "Failed to start socket server."; + } + + processQueueTimer.setInterval(0); + processQueueTimer.setSingleShot(false); + connect(&processQueueTimer, SIGNAL(timeout()), SLOT(processOneQueueItem())); +} + +MThemeDaemonServer::~MThemeDaemonServer() +{ + processQueueTimer.stop(); + + // close socket server + server.close(); + + // remove all registered clients + while (registeredClients.count() > 0) { + MThemeDaemonClient *client = registeredClients.begin().value(); + daemon.removeClient(client); + delete client; + registeredClients.erase(registeredClients.begin()); + } + + loadPixmapsQueue.clear(); + releasePixmapsQueue.clear(); +} + +// gets called when a new client(socket) connects to the daemon +void MThemeDaemonServer::clientConnected() +{ + while (server.hasPendingConnections()) { + QLocalSocket *socket = server.nextPendingConnection(); + QObject::connect(socket, SIGNAL(disconnected()), SLOT(clientDisconnected())); + QObject::connect(socket, SIGNAL(readyRead()), SLOT(clientDataAvailable())); + } +} + +// gets called when any client has disconnected +void MThemeDaemonServer::clientDisconnected() +{ + QLocalSocket *socket = qobject_cast<QLocalSocket *>(sender()); + if (socket) { + MThemeDaemonClient *client = registeredClients.value(socket, NULL); + if (client) { + daemon.removeClient(client); + registeredClients.remove(socket); + + // remove all queued pixmap requests + QMutableListIterator<QueueItem> pi = loadPixmapsQueue; + while (pi.hasNext()) { + if (pi.next().client == client) + pi.remove(); + } + // remove all queued pixmap releases + pi = releasePixmapsQueue; + while (pi.hasNext()) { + if (pi.next().client == client) + pi.remove(); + } + + delete client; + } + socket->deleteLater(); + } +} + + +// gets called when any client has sent data over socket +void MThemeDaemonServer::clientDataAvailable() +{ + QLocalSocket *socket = qobject_cast<QLocalSocket *>(sender()); + if (!socket) { + return; + } + + // has the client registered? + MThemeDaemonClient *client = registeredClients.value(socket, NULL); + if (!client) { + + // create a temporary data stream to read from the socket + QDataStream stream(socket); + + // loop as long as the socket has some data left + while (socket->bytesAvailable()) { + + // read one packet from the socket + Packet packet; + stream >> packet; + // and check if it's a registration request + if (packet.type() != Packet::RequestRegistrationPacket) { + // reply error + stream << Packet(Packet::ErrorPacket, packet.sequenceNumber(), + new String("You must send registration packet before requesting anything else!")); + } else { + // we got the registration packet so register the client, and continue normally + client = new MThemeDaemonClient(socket, static_cast<const String *>(packet.data())->string, daemon.themeInheritanceChain()); + registeredClients.insert(socket, client); + daemon.addClient(client); + client->stream() << Packet(Packet::ThemeChangedPacket, packet.sequenceNumber(), + new ThemeChangeInfo(daemon.themeInheritanceChain(), daemon.themeLibraryNames())); + break; + } + } + } + + // client has registered, so we'll read all packets that are currently available from the socket + while (socket->bytesAvailable()) { + + // read packet from socket + Packet packet; + client->stream() >> packet; + + // process it according to packet type + switch (packet.type()) { + + case Packet::RequestRegistrationPacket: { + // client tried to register a second time + client->stream() << Packet(Packet::ErrorPacket, packet.sequenceNumber(), + new String("You have already registered!")); + mWarning("MThemeDaemonServer") << "Client with name" << client->name() + << "tried to register a second time!"; + } break; + + case Packet::RequestPixmapPacket: { + // client requested a pixmap + const PixmapIdentifier *id = static_cast<const PixmapIdentifier *>(packet.data()); + pixmapRequested(client, *id, packet.sequenceNumber()); + } break; + + case Packet::ReleasePixmapPacket: { + // client requested a pixmap release + const PixmapIdentifier *id = static_cast<const PixmapIdentifier *>(packet.data()); + pixmapReleaseRequested(client, *id, packet.sequenceNumber()); + } break; + + case Packet::RequestClearPixmapDirectoriesPacket: { + client->removeAddedImageDirectories(); + } break; + + case Packet::RequestNewPixmapDirectoryPacket: { + const StringBool *sb = static_cast<const StringBool *>(packet.data()); + client->addCustomImageDirectory(sb->string, sb->b ? M::Recursive : M::NonRecursive); + } break; + + + case Packet::QueryThemeDaemonStatusPacket: { + themeDaemonStatus(client, packet.sequenceNumber()); + } break; + + + default: { + mWarning("MThemeDaemonServer") << "unknown packet received:" << packet.type(); + } break; + } + } +} + +void MThemeDaemonServer::themeChanged() +{ + if (daemon.currentTheme() == currentTheme.value().toString()) + return; + + QHash<MThemeDaemonClient *, QList<PixmapIdentifier> > pixmapsToReload; + if (daemon.activateTheme(currentTheme.value().toString(), currentLocale.value().toString(), registeredClients.values(), pixmapsToReload)) { + // theme change succeeded, let's inform all clients + add the pixmaps to load-list + Packet themeChangedPacket(Packet::ThemeChangedPacket, 0, new ThemeChangeInfo(daemon.themeInheritanceChain(), daemon.themeLibraryNames())); + + QHash<MThemeDaemonClient *, QList<PixmapIdentifier> >::iterator i = pixmapsToReload.begin(); + QHash<MThemeDaemonClient *, QList<PixmapIdentifier> >::iterator end = pixmapsToReload.end(); + for (; i != end; ++i) { + MThemeDaemonClient *client = i.key(); + const QList<PixmapIdentifier> &ids = i.value(); + + client->stream() << themeChangedPacket; + + const QList<PixmapIdentifier>::const_iterator idsEnd = ids.end(); + for (QList<PixmapIdentifier>::const_iterator iId = ids.begin(); iId != idsEnd; ++iId) { + + const QueueItem item (client, *iId); + if (!releasePixmapsQueue.removeOne(item)) { + loadPixmapsQueue.enqueue(item); + } + } + } + + // make sure we have a cache directory for the current theme + QDir cacheDir(MThemeDaemon::systemThemeCacheDirectory()); + if(!cacheDir.exists(currentTheme.value().toString())) { + if(!cacheDir.mkdir(currentTheme.value().toString())) { + qFatal("MThemeDaemonServer - Could not create cache directory for current theme. No write permissions to %s", + qPrintable(cacheDir.absolutePath())); + } + } + + if (!loadPixmapsQueue.isEmpty() && !processQueueTimer.isActive()) + processQueueTimer.start(); + + } else { + // theme change failed, so change the theme back also in gconf. + mWarning("MThemeDaemonServer") << "Could not change theme to" << currentTheme.value().toString(); + currentTheme.set(daemon.currentTheme()); + } +} + +void MThemeDaemonServer::localeChanged() +{ + QHash<MThemeDaemonClient *, QList<PixmapIdentifier> > pixmapsToReload; + daemon.changeLocale(currentLocale.value().toString(), registeredClients.values(), pixmapsToReload); + + QHash<MThemeDaemonClient *, QList<PixmapIdentifier> >::iterator i = pixmapsToReload.begin(); + QHash<MThemeDaemonClient *, QList<PixmapIdentifier> >::iterator end = pixmapsToReload.end(); + for (; i != end; ++i) { + MThemeDaemonClient *client = i.key(); + const QList<PixmapIdentifier> &ids = i.value(); + + const QList<PixmapIdentifier>::const_iterator idsEnd = ids.end(); + for (QList<PixmapIdentifier>::const_iterator iId = ids.begin(); iId != idsEnd; ++iId) { + + const QueueItem item (client, *iId); + if (!releasePixmapsQueue.removeOne(item)) { + loadPixmapsQueue.enqueue(item); + } + } + } + if (!loadPixmapsQueue.isEmpty() && !processQueueTimer.isActive()) + processQueueTimer.start(); +} + +void MThemeDaemonServer::processOneQueueItem() +{ + if (!loadPixmapsQueue.isEmpty()) { + const QueueItem item = loadPixmapsQueue.dequeue(); + Qt::HANDLE handle = 0; + if (daemon.pixmap(item.client, item.pixmapId, handle)) { + item.client->stream() << Packet(Packet::PixmapUpdatedPacket, item.sequenceNumber, + new PixmapHandle(item.pixmapId, handle)); + } else { + const QString message = + QString::fromLatin1("requested pixmap '%1' %2x%3 already acquired by client").arg( + item.pixmapId.imageId, + QString::number(item.pixmapId.size.width()), + QString::number(item.pixmapId.size.height())); + item.client->stream() << Packet(Packet::ErrorPacket, item.sequenceNumber, + new String(message)); + } + } else if (!releasePixmapsQueue.isEmpty()) { + const QueueItem item = releasePixmapsQueue.dequeue(); + if (!daemon.releasePixmap(item.client, item.pixmapId)) { + const QString message = + QString::fromLatin1("pixmap to release '%1' %2x%3 not acquired by client").arg( + item.pixmapId.imageId, + QString::number(item.pixmapId.size.width()), + QString::number(item.pixmapId.size.height())); + item.client->stream() << Packet(Packet::ErrorPacket, item.sequenceNumber, + new String(message)); + } + } + + if (loadPixmapsQueue.isEmpty() && releasePixmapsQueue.isEmpty()) { + processQueueTimer.stop(); + } +} + +void MThemeDaemonServer::pixmapRequested(MThemeDaemonClient *client, + const PixmapIdentifier &id, quint64 sequenceNumber) +{ + // if the client has requested a release for this pixmap, we'll remove the + // release request, and reply with the existing pixmap handle + const QueueItem item (client, id, sequenceNumber); + if (releasePixmapsQueue.removeOne(item)) { + // removeOne succeeds if there was a release request still on queue + // find the resource for the requested pixmap + ImageResource *resource = client->pixmaps.value(id); + // in case the resource is NULL, the pixmap is not loaded (it's not found from the current theme) + if (!resource) { + client->stream() << Packet(Packet::PixmapUpdatedPacket, sequenceNumber, + new PixmapHandle(id, 0)); + } else { +#ifndef Q_WS_MAC + client->stream() << Packet(Packet::PixmapUpdatedPacket, sequenceNumber, + new PixmapHandle(id, resource->pixmapHandle(id.size))); +#endif + } + } else { + // the requested pixmap was not in release queue, so we'll queue the load + loadPixmapsQueue.enqueue(item); + if (!processQueueTimer.isActive()) + processQueueTimer.start(); + } +} + +void MThemeDaemonServer::pixmapReleaseRequested(MThemeDaemonClient *client, + const PixmapIdentifier &id, + quint64 sequenceNumber) +{ + // if the pixmap request is in queue, we can just remove it from there + const QueueItem item (client, id, sequenceNumber); + if (!loadPixmapsQueue.removeOne(item)) { + // in case the removeOne fails, we need to queue the release request. + releasePixmapsQueue.enqueue(item); + if (!processQueueTimer.isActive()) + processQueueTimer.start(); + } +} + +void MThemeDaemonServer::themeDaemonStatus(MThemeDaemonClient *client, + quint64 sequenceNumber) const +{ + QList<ClientInfo> clientList; + + foreach(const MThemeDaemonClient * c, registeredClients.values()) { + ClientInfo info; + info.name = c->name(); + info.pixmaps = c->pixmaps.keys(); + + foreach(const QueueItem &item, loadPixmapsQueue) { + if (item.client == c) + info.requestedPixmaps.append(item.pixmapId); + } + foreach(const QueueItem &item, releasePixmapsQueue) { + if (item.client == c) + info.releasedPixmaps.append(item.pixmapId); + } + + clientList.append(info); + } + + client->stream() << Packet(Packet::ThemeDaemonStatusPacket, sequenceNumber, + new ClientList(clientList)); +} + diff --git a/mthemedaemon/mthemedaemonserver.h b/mthemedaemon/mthemedaemonserver.h new file mode 100644 index 00000000..f919929f --- /dev/null +++ b/mthemedaemon/mthemedaemonserver.h @@ -0,0 +1,93 @@ +/*************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (directui@nokia.com) +** +** This file is part of libmeegotouch. +** +** If you have questions regarding the use of this file, please contact +** Nokia at directui@nokia.com. +** +** This library is free software; you can redistribute it and/or +** modify it 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. +** +****************************************************************************/ + +#ifndef MTHEMEDAEMONSERVER_H +#define MTHEMEDAEMONSERVER_H + +#include <QObject> +#include <QQueue> +#include <QLocalServer> +#include <MGConfItem> +#include <QTimer> + + +#include <mthemedaemon.h> + +class QLocalSocket; +class MThemeDaemonClient; + +//! \internal +class MThemeDaemonServer : public QObject +{ + Q_OBJECT +public: + MThemeDaemonServer(); + virtual ~MThemeDaemonServer(); + +private slots: + void clientConnected(); + void clientDisconnected(); + void clientDataAvailable(); + + void themeChanged(); + void localeChanged(); + + void processOneQueueItem(); + +private: + void pixmapRequested(MThemeDaemonClient *client, + const M::MThemeDaemonProtocol::PixmapIdentifier &id, + quint64 sequenceNumber); + void pixmapReleaseRequested(MThemeDaemonClient *client, + const M::MThemeDaemonProtocol::PixmapIdentifier &id, + quint64 sequenceNumber); + void themeDaemonStatus(MThemeDaemonClient *client, quint64 sequenceNumber) const; + +private: + struct QueueItem + { + quint64 sequenceNumber; + MThemeDaemonClient * client; + M::MThemeDaemonProtocol::PixmapIdentifier pixmapId; + + QueueItem(MThemeDaemonClient *c, M::MThemeDaemonProtocol::PixmapIdentifier p, + quint64 s = 0) : sequenceNumber(s), client(c), pixmapId(p) {} + + // Ignore the sequence number when testing for equality. A custom + // predicate would be more appropriate, if Qt would support that. + bool operator==(const QueueItem &other) const + { return (client == other.client && pixmapId == other.pixmapId); } + bool operator!=(const QueueItem &other) const + { return (client != other.client || pixmapId != other.pixmapId); } + }; + + QLocalServer server; + QHash<QLocalSocket *, MThemeDaemonClient *> registeredClients; + MThemeDaemon daemon; + + MGConfItem currentTheme; + MGConfItem currentLocale; + + QQueue<QueueItem> loadPixmapsQueue; + QQueue<QueueItem> releasePixmapsQueue; + QTimer processQueueTimer; +}; +//! \internal_end +#endif + diff --git a/mthemedaemon/test/client.cpp b/mthemedaemon/test/client.cpp new file mode 100644 index 00000000..a27e73e3 --- /dev/null +++ b/mthemedaemon/test/client.cpp @@ -0,0 +1,421 @@ +/*************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (directui@nokia.com) +** +** This file is part of libmeegotouch. +** +** If you have questions regarding the use of this file, please contact +** Nokia at directui@nokia.com. +** +** This library is free software; you can redistribute it and/or +** modify it 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. +** +****************************************************************************/ + +#include <stdlib.h> +#include <QTimer> +#include <QDebug> +#include <QPainter> +#include <QImage> +#include <MGConfItem> +#include "client.h" +#include "clientmanager.h" +#include "mthemedaemon.h" +#include "mthemedaemonprotocol.h" + +using namespace M::MThemeDaemonProtocol; + +ClientThread::ClientThread(ClientManager* manager) : manager(manager) +{ +} + +void ClientThread::run() +{ + Client client(identifier); + connect(&client, + SIGNAL(pixmapReady(const QString&, Client*, quint32, const QString&, const QSize&)), + manager, + SLOT(pixmapReady(const QString&, Client*, quint32, const QString&, const QSize&))); + + exec(); +} + +void ClientThread::setId(const QString &id) +{ + identifier = id; +} + +const QString &ClientThread::getId() const +{ + return identifier; +} + +Client::Client(const QString &identifier) : identifier(identifier), registered(false), packetsSent(0) +{ + operationCount = rand() % MAX_OPERATION_COUNT; + + int count = 50; + // list all themes + QDir themeDirectory(THEME_ROOT_DIRECTORY); + QStringList list = themeDirectory.entryList(QDir::Dirs|QDir::NoDotAndDotDot); + for(int i=0; i<list.size(); i++) { + QString themeName = list.at(i); + + // create path for images + QString imagesPath = themeName + QDir::separator() + getImageDirectory(); + if(themeDirectory.mkpath(imagesPath)) + { + // create imagery + //int count = rand() % 200; + // create imagery + for (int i = 0; i < count; i++) { + QImage image(64, 64, QImage::Format_ARGB32); + image.fill(rand()); + + QString filename = themeDirectory.absolutePath() + QDir::separator() + imagesPath + QDir::separator() + QString::number(i) + ".png"; + image.save(filename, "PNG"); + } + } + } + + stream.setDevice(&socket); + + connect(&socket, SIGNAL(connected()), SLOT(connected())); + connect(&socket, SIGNAL(disconnected()), SLOT(disconnected())); + connect(&socket, SIGNAL(readyRead()), this, SLOT(readyRead())); + + // connect to server + socket.connectToServer(M::MThemeDaemonProtocol::ServerAddress); + + if (!socket.waitForConnected(10000)) { + qDebug() << "ERROR:" << identifier << "- failed to connect server:" << socket.error(); + } else { + // perform some task once we get into event loop + QTimer::singleShot(0, this, SLOT(sendPacket())); + } +} + +Client::~Client() +{ + if (socket.state() == QLocalSocket::ConnectedState) { + socket.disconnectFromServer(); + } +} + +const QString &Client::getId() const +{ + return identifier; +} + +void Client::connected() +{ +// QLocalSocket* socket = qobject_cast<QLocalSocket*>(sender()); +} + +void Client::disconnected() +{ +// QLocalSocket* socket = qobject_cast<QLocalSocket*>(sender()); +} + +M::MThemeDaemonProtocol::Packet Client::processOnePacket() +{ + Packet packet; + stream >> packet; + + switch (packet.type()) { + case Packet::PixmapUpdatedPacket: { + const PixmapHandle *handle = static_cast<const PixmapHandle *>(packet.data()); + + if(handle->pixmapHandle) { + emit pixmapReady(currentTheme, this, handle->pixmapHandle, handle->identifier.imageId, handle->identifier.size); + waitVerify.acquire(); + } else { + qDebug() << "ERROR: daemon returned null handle for" << handle->identifier.imageId; + } + } break; + + case Packet::ThemeChangedPacket: { + const M::MThemeDaemonProtocol::ThemeChangeInfo *data = static_cast<const M::MThemeDaemonProtocol::ThemeChangeInfo *>(packet.data()); + currentTheme = data->themeInheritance.at(0); + }break; + + case Packet::ThemeDaemonStatusPacket: + break; + + default: + break; + } + + return packet; +} + +void Client::pixmapVerified(const QString& imageId, const QSize& size) +{ + PixmapIdentifier identifier(imageId, size); + + if (requestedPixmaps.contains(identifier)) { + // pixmap found from requested list -> new one + requestedPixmaps.remove(identifier); + readyPixmaps.insert(identifier); + } else if(readyPixmaps.contains(identifier)) { + // this pixmap was already ready, so it is just updated (probably due to theme change) + } else { + qDebug() << "ERROR:" << imageId << "- pixmap reply to unknown request"; + } + + waitVerify.release(); +} + + +void Client::readyRead() +{ + while (socket.bytesAvailable() > 0) { + processOnePacket(); + } +} + +void Client::sendPacket() +{ + if (operationCount > 0) { + + // randomize a task to us + Task task = (Task)(rand() % NumberOfTasks); + + switch (task) { + case RegisterToServer: + registerToServer(); + break; + + case RequestPixmap: { + // this directory contains all icons for this theme + QDir themeIconDirectory = currentTheme + QDir::separator() + QString("meegotouch") + QDir::separator() + QString("icons"); + QStringList iconList = themeIconDirectory.entryList(QDir::Files); + + // this directory contains all images for this client + QDir imageDirectory = currentTheme + QDir::separator() + getImageDirectory(); + QStringList imageList = imageDirectory.entryList(QDir::Files); + + // combine both lists as one, we'll request something from this list + QStringList list; + list.append(iconList); + list.append(imageList); + + if (list.count() > 0) { + // select image + int index = rand() % list.count(); + + // get pixmap identifier + QFileInfo info(list[index]); + PixmapIdentifier pixmapIdentifier(info.baseName(), QSize(64, 64)); + + // make sure this has not been requested already + if (!requestedPixmaps.contains(pixmapIdentifier) && !readyPixmaps.contains(pixmapIdentifier)) { + requestPixmap(pixmapIdentifier); + } else { + // it was already requested, should we try to request some other pixmap instead? + // nah, go away and do something else next time.. + } + } + } break; + + case ReleasePixmap: { + // make sure that we have something to release + if (readyPixmaps.count() > 0) { + // release the first pixmap we have, this could have better logic.. + PixmapIdentifier toRemove = *readyPixmaps.begin(); + releasePixmap(toRemove); + } + } break; + + case CheckConsistency: + checkConsistency(); + break; + + default: + break; + } + // do something else next time + operationCount--; + QTimer::singleShot(TASK_EXECUTION_INTERVAL, this, SLOT(sendPacket())); + } else if (readyPixmaps.count() > 0 || requestedPixmaps.count() > 0) { + + while (readyPixmaps.count() > 0) { + PixmapIdentifier toRemove = *readyPixmaps.begin(); + releasePixmap(toRemove); + } + + while (requestedPixmaps.count() > 0) { + PixmapIdentifier toRemove = *requestedPixmaps.begin(); + releasePixmap(toRemove); + } + + QTimer::singleShot(1000, this, SLOT(sendPacket())); + } else { + checkConsistency(); + QThread::currentThread()->quit(); + } +} + +void Client::registerToServer() +{ +#ifdef PRINT_INFO_MESSAGES + qDebug() << "INFO:" << identifier << "- registering to server"; +#endif + Packet registration(Packet::RequestRegistrationPacket, packetsSent++); + registration.setData(new String(identifier)); + stream << registration; + + registered = true; +} + +void Client::requestPixmap(M::MThemeDaemonProtocol::PixmapIdentifier &pixmapIdentifier) +{ +#ifdef PRINT_INFO_MESSAGES + qDebug() << "INFO:" << identifier << "- requesting pixmap" << pixmapIdentifier.imageId << pixmapIdentifier.size; +#endif + Packet packet(Packet::RequestPixmapPacket, packetsSent++); + packet.setData(new PixmapIdentifier(pixmapIdentifier)); + stream << packet; + + if (registered) { + requestedPixmaps.insert(pixmapIdentifier); + } +} + +void Client::releasePixmap(M::MThemeDaemonProtocol::PixmapIdentifier &pixmapIdentifier) +{ +#ifdef PRINT_INFO_MESSAGES + qDebug() << "INFO:" << identifier << "- releasing pixmap" << pixmapIdentifier.imageId << pixmapIdentifier.size; +#endif + Packet packet(Packet::ReleasePixmapPacket, packetsSent++); + packet.setData(new PixmapIdentifier(pixmapIdentifier)); + stream << packet; + readyPixmaps.remove(pixmapIdentifier); + requestedPixmaps.remove(pixmapIdentifier); +} + +void Client::checkConsistency() +{ + if (!registered) + return; + + bool consistency = false; + + // disconnect signal + disconnect(&socket, SIGNAL(readyRead()), this, SLOT(readyRead())); + + // send status query packet + Packet packet(Packet::QueryThemeDaemonStatusPacket, packetsSent++); + stream << packet; + if (!socket.flush()) { + qDebug() << "ERROR:" << identifier << "- failed to write to socket" << socket.error(); + } + + bool done = false; + while (!done) { + // wait for response + if (socket.waitForReadyRead(10000)) { + while (socket.bytesAvailable() > 0) { + // handle packet that was received + Packet packet = processOnePacket(); + + // check whether it's correct type + if (packet.type() == Packet::ThemeDaemonStatusPacket) { + + // extract reply and perform consistency check + const ClientList *list = static_cast<const ClientList *>(packet.data()); + consistency = isDataConsistent(list); + done = true; + break; + } + } + } else { + // something bad happened, we did not get reply + qDebug() << "ERROR:" << identifier << "- failed to receive data from socket" << socket.error(); + break; + } + } + + // reconnect signal back + connect(&socket, SIGNAL(readyRead()), this, SLOT(readyRead())); + + // process rest of the packets + readyRead(); + + if (!consistency) { + qDebug() << "ERROR:" << identifier << "- Consistency check FAILED!"; + } +} + +bool Client::isDataConsistent(const M::MThemeDaemonProtocol::ClientList *list) +{ + foreach(ClientInfo info, list->clients) { + // find correct info + if (info.name == identifier) { + + // check that we're really registered + if (!registered) { + // we're not registered but our name is still in the client list + return false; + } + + // theme change may cause pixmaps to move from loaded list to request list in daemon side, + // so we'll compare both lists as one + + // create list of daemon-side pixmaps (requests & loaded) + QList<PixmapIdentifier> daemon; + daemon.append(info.requestedPixmaps); + daemon.append(info.pixmaps); + + // create list of client-side pixmaps (requests & loaded) + QList<PixmapIdentifier> client; + client.append(requestedPixmaps.toList()); + client.append(readyPixmaps.toList()); + + // check that the daemon has correct amount of pixmaps in load queue + if (daemon.count() != client.count()) { + qDebug() << "ERROR:" << identifier << "- incorrect pixmap count, Themedaemon says:" << daemon.count() << "and client says:" << client.count(); + break; + } + + // check that we can find all daemon-side pixmaps from the client-side list + foreach(const PixmapIdentifier & pixmapIdentifier, daemon) { + if (!client.contains(pixmapIdentifier)) { + // pixmap not found from client, but themedaemon reported it -> inconsistent state + qDebug() << "ERROR:" << identifier << "- pixmap not found from client-side list:" << pixmapIdentifier.imageId << '(' << pixmapIdentifier.size << ')'; + break; + } else { + // found, we can remove this one from client list + client.removeOne(pixmapIdentifier); + } + } + + // check that we can find all client-side pixmaps from the daemon-side list + foreach(const PixmapIdentifier & pixmapIdentifier, client) { + if (!daemon.contains(pixmapIdentifier)) { + // pixmap not found from daemon-side list, but exists in client-side list -> inconsistent state + qDebug() << "ERROR:" << identifier << "- pixmap not found from daemon-side list:" << pixmapIdentifier.imageId << '(' << pixmapIdentifier.size << ')'; + break; + } + } + + return true; + } + } + + // if we're registered, we shouldn't end up being here and vice versa + return registered ? false : true; +} + +QString Client::getImageDirectory() const +{ + return QString("meegotouch") + QDir::separator() + + QString(identifier) + QDir::separator() + + QString("images"); +} + diff --git a/mthemedaemon/test/client.h b/mthemedaemon/test/client.h new file mode 100644 index 00000000..01749c43 --- /dev/null +++ b/mthemedaemon/test/client.h @@ -0,0 +1,109 @@ +/*************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (directui@nokia.com) +** +** This file is part of libmeegotouch. +** +** If you have questions regarding the use of this file, please contact +** Nokia at directui@nokia.com. +** +** This library is free software; you can redistribute it and/or +** modify it 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. +** +****************************************************************************/ + +#ifndef CLIENT_H +#define CLIENT_H + +#include <QThread> +#include <QDataStream> +#include <QSize> +#include <QDir> +#include <QLocalSocket> +#include <QSemaphore> + +#include "mthemedaemonclient.h" + +class Client; +class ClientManager; + +class ClientThread : public QThread +{ +public: + ClientThread(ClientManager* manager); + void setId(const QString &id); + const QString &getId() const; + +protected: + virtual void run(); +private: + ClientManager* manager; + QString identifier; +}; + +class Client : public QObject +{ + Q_OBJECT + + static const int MAX_OPERATION_COUNT = 100; + static const int TASK_EXECUTION_INTERVAL = 50; + + enum Task { + RegisterToServer, + RequestPixmap, + ReleasePixmap, + CheckConsistency, + + NumberOfTasks + }; + +public: + Client(const QString &identifier); + ~Client(); + + const QString& getId() const; + + QString getImageDirectory() const; + void pixmapVerified(const QString& imageId, const QSize& size); + +signals: + void pixmapReady(const QString& theme, Client* client, quint32 handle, const QString&, const QSize&); + + +protected: + + void registerToServer(); + void requestPixmap(M::MThemeDaemonProtocol::PixmapIdentifier &); + void releasePixmap(M::MThemeDaemonProtocol::PixmapIdentifier &); + void checkConsistency(); + M::MThemeDaemonProtocol::Packet processOnePacket(); + +private slots: + void connected(); + void disconnected(); + void sendPacket(); + void readyRead(); + +private: + + bool isDataConsistent(const M::MThemeDaemonProtocol::ClientList *reply); + void quit(); + + QLocalSocket socket; + QDataStream stream; + QSet<M::MThemeDaemonProtocol::PixmapIdentifier> requestedPixmaps; + QSet<M::MThemeDaemonProtocol::PixmapIdentifier> readyPixmaps; + QString identifier; + bool registered; + int operationCount; + quint64 packetsSent; + QString currentTheme; + QSemaphore waitVerify; +}; + +#endif diff --git a/mthemedaemon/test/clientmanager.cpp b/mthemedaemon/test/clientmanager.cpp new file mode 100644 index 00000000..a277970b --- /dev/null +++ b/mthemedaemon/test/clientmanager.cpp @@ -0,0 +1,352 @@ +/*************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (directui@nokia.com) +** +** This file is part of libmeegotouch. +** +** If you have questions regarding the use of this file, please contact +** Nokia at directui@nokia.com. +** +** This library is free software; you can redistribute it and/or +** modify it 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. +** +****************************************************************************/ + +#include <QDebug> +#include <QTimer> +#include <QApplication> +#include <MGConfItem> +#include <QDir> +#include <QFile> +#include <QPainter> +#include <QSvgGenerator> +#include <QSvgRenderer> +#include "clientmanager.h" +#include "client.h" + +#define CLIENT_ID(client) "Client (0x" + QString::number((quint32) client, 16) + ')' + +void removeDirectoryRecursive(const QString &path) +{ + QDir root(path); + if (root.exists()) { + QFileInfoList entries = root.entryInfoList(QDir::NoDotAndDotDot | QDir::Dirs | QDir::Files); + for (int i = 0; i < entries.count(); i++) { + if (entries[i].isDir()) { + removeDirectoryRecursive(entries[i].filePath()); + root.rmdir(entries[i].absoluteFilePath()); + } else { + root.remove(entries[i].absoluteFilePath()); + } + } + } +} + +void ClientManager::cleanup() +{ + QDir themeDirectory(THEME_ROOT_DIRECTORY); + QStringList list = themeDirectory.entryList(QDir::Dirs|QDir::NoDotAndDotDot); + for(int i=0; i<list.size(); i++) { + QString path = list.at(i); + + QDir theme(themeDirectory.absolutePath() + QDir::separator() + path + QDir::separator() + QString("meegotouch")); + // find all client directories + QStringList directories = theme.entryList(QDir::Dirs|QDir::NoDotAndDotDot); + for(int j=0; j<directories.size(); j++) { + if(directories.at(j).startsWith("client") || directories.at(j) == QString("icons")) + { + removeDirectoryRecursive(theme.absolutePath() + QDir::separator() + directories.at(j)); + theme.rmdir(directories.at(j)); + } + } + // loop all locales + for(int localeIndex=1; localeIndex<locales.size(); localeIndex++) + { + // remove all locale-specific icons + QString localeSpecificIconsDirectory = theme.absolutePath() + QDir::separator() + + "locale" + QDir::separator() + + locales[localeIndex] + QDir::separator() + + QString("icons"); + removeDirectoryRecursive(localeSpecificIconsDirectory); + theme.rmdir(localeSpecificIconsDirectory); + } + } +} + +void createSVGFile(const QString& path, int index) +{ + QString filename = path + QDir::separator() + QString("icon-") + QString::number(index) + ".svg"; + + QFile file(filename); + file.open(QIODevice::WriteOnly); + + QSvgGenerator generator; + generator.setFileName(filename); + generator.setSize(QSize(200, 200)); + generator.setViewBox(QRect(0, 0, 200, 200)); + generator.setOutputDevice(&file); + + QPainter painter; + painter.begin(&generator); + + QColor color(rand() % 255, rand() % 255, rand() % 255); + painter.fillRect(generator.viewBox(), color); + painter.end(); + + file.close(); +} + +ClientManager::ClientManager(QProcess &process) : shutdown(false), themedaemon(process) +{ + locales.append("en"); + locales.append("fi"); + + cleanup(); + + int count = 50; + // create svg images for icons for all themes & locales + QDir themeDirectory(THEME_ROOT_DIRECTORY); + QStringList list = themeDirectory.entryList(QDir::Dirs|QDir::NoDotAndDotDot); + for(int i=0; i<list.size(); i++) { + QString themeName = list.at(i); + + // create path for images + QString iconsPath = themeName + QDir::separator() + QString("meegotouch") + QDir::separator() + QString("icons"); + if(themeDirectory.mkpath(iconsPath)) + { + // create svg icon + //int count = (rand() + 10) % 200; + + // create imagery to theme's root icons dir + for (int imageIndex = 0; imageIndex < count; imageIndex++) { + createSVGFile(themeDirectory.absolutePath() + QDir::separator() + iconsPath, imageIndex); + } + + // create locales + QString localeRoot = themeName + QDir::separator() + QString("meegotouch") + QDir::separator() + QString("locale"); + for(int localeIndex=1; localeIndex<locales.size(); localeIndex++) { + QString localeIconPath = localeRoot + QDir::separator() + locales[localeIndex] + QDir::separator() + QString("icons"); + if(themeDirectory.mkpath(localeIconPath)) { + // create svg imagery + for (int imageIndex = 0; imageIndex < count; imageIndex++) { + createSVGFile(themeDirectory.absolutePath() + QDir::separator() + localeIconPath, imageIndex); + } + } + } + } + } + + // start the test after 1 sec (allow themedaemon to get online) + QTimer::singleShot(1000, this, SLOT(start())); +} + +ClientManager::~ClientManager() +{ + // perform cleanup + cleanup(); +} + +void ClientManager::spawnClient() +{ + static unsigned int clientId = 0; + ClientThread *client = new ClientThread(this); + client->setId("client" + QString::number(clientId++)); + + // generate imagery for testing purposes + connect(client, SIGNAL(started()), this, SLOT(clientStarted())); + connect(client, SIGNAL(finished()), this, SLOT(clientFinished())); + clients.insert(client); + + client->start(); +#ifdef PRINT_INFO_MESSAGES + qDebug() << "INFO: ClientManager - Client started, number of active clients:" << clients.count(); +#endif +} + +void ClientManager::start() +{ +#ifdef PRINT_INFO_MESSAGES + qDebug() << "INFO: ClientManager - Starting up..."; +#endif + // change theme & begin theme switching + changeThemeAndLocale(); + + // start performing consistency checks & spawn new clients + QTimer::singleShot(0, this, SLOT(checkConsistency())); +} + +void ClientManager::stop() +{ + shutdown = true; + qDebug() << "INFO: ClientManager - Shutting down..."; + + if (clients.count() == 0) { + qApp->quit(); + } +} + +void ClientManager::clientStarted() +{ + Client *client = static_cast<Client *>(sender()); + Q_UNUSED(client); +} + + +void ClientManager::clientFinished() +{ + ClientThread *client = static_cast<ClientThread *>(sender()); + + removeDirectoryRecursive(QString(IMAGESDIR) + QDir::separator() + client->getId()); + clients.remove(client); + client->exit(); + client->wait(); + delete client; +#ifdef PRINT_INFO_MESSAGES + qDebug() << "INFO: ClientManager - Client finished, number of active clients:" << clients.count(); +#endif + if (shutdown) { + if (clients.count() == 0) { + qApp->quit(); + } + } +} + +void ClientManager::checkConsistency() +{ + // check that themedaemon has not crashed + if (themedaemon.state() != QProcess::Running) { + qDebug() << "ERROR: ClientManager (consistency check)- Themedaemon is not running"; + qDebug() << themedaemon.readAllStandardError(); + qDebug() << themedaemon.readAllStandardOutput(); + + shutdown = true; + foreach(ClientThread * thread, clients) { + thread->exit(); + thread->wait(); + delete thread; + } + qApp->quit(); + } + + if (shutdown == false) { + // spawn new clients if there is room + while (clients.count() < ClientManager::MAX_CLIENT_COUNT) { + spawnClient(); + } + // get ready to perform another consistency check + QTimer::singleShot(5000, this, SLOT(checkConsistency())); + } +} + +void ClientManager::changeThemeAndLocale() +{ +#ifdef HAVE_GCONF + // get list of themes + QDir themeDirectory(THEME_ROOT_DIRECTORY); + QStringList list = themeDirectory.entryList(QDir::Dirs|QDir::NoDotAndDotDot); + + if(list.size() == 0) + return; + + int themeIndex = rand() % list.size(); + +#ifdef PRINT_INFO_MESSAGES + qDebug() << "INFO: Changing theme to:" << list[themeIndex]; +#endif + MGConfItem themeName("/M/theme/name"); + //themeName.set(list[themeIndex]); + +// int localeIndex = rand() % locales.size(); +// MGConfItem localeName("/M/i18n/Language"); +// localeName.set(locales[localeIndex]); + + // change theme and locale again after a short period of time + QTimer::singleShot(2000, this, SLOT(changeThemeAndLocale())); +#endif +} + +void ClientManager::pixmapReady(const QString& theme, Client* client, quint32 handle, const QString& imageId, const QSize& size) +{ + if(!verifyPixmap(theme, client, handle, imageId, size)) { + qDebug() << "ERROR:" << client->getId() << "- incorrect color found when verifying returned pixmap (" << imageId << ')'; + } else { +#ifdef PRINT_INFO_MESSAGES + qDebug() << "INFO:" << client->getId() << "- pixmap comparison OK (" << imageId << ')'; +#endif + } + client->pixmapVerified(imageId, size); +} + +bool ClientManager::verifyPixmap(const QString& theme, Client* client, quint32 handle, const QString& imageId, const QSize& size) +{ + // this is what we got from daemon + QPixmap daemon = QPixmap::fromX11Pixmap(handle, QPixmap::ImplicitlyShared); + + // this is what we have + QPixmap clientPixmap(size); + + QDir themeDirectory(THEME_ROOT_DIRECTORY); + + // either icon-<xx> or just <xx> + if(imageId.startsWith("icon")) + { +#ifdef HAVE_GCONF +// MGConfItem localeName("/M/i18n/Language"); +// QString locale = localeName.value().toString(); + QDir iconDirectory; +// if(locale.isEmpty()) + { + iconDirectory = QDir(theme + QDir::separator() + + QString("meegotouch") + QDir::separator() + QString("icons")); + } +// else +// { +// iconDirectory = QDir(theme + QDir::separator() + +// QString("meegotouch") + QDir::separator() + +// QString("locale") + QDir::separator() + +// locale + QDir::separator() + +// QString("icons")); +// } + + // create svg renderer + QString filename = iconDirectory.absolutePath() + QDir::separator() + imageId + ".svg"; + QSvgRenderer renderer(filename); + if(!renderer.isValid()) + { + qDebug() << "ERROR: Failed to construct SVG renderer for:" << filename; + return false; + } + // render pixmap + QPainter painter(&clientPixmap); + renderer.render(&painter); +#else + return true; +#endif + } + else + { + QDir imageDirectory = theme + QDir::separator() + client->getImageDirectory(); + QString filename = imageDirectory.absolutePath() + QDir::separator() + imageId + ".png"; + if(!clientPixmap.load(filename, "PNG")) + qDebug() << "ERROR: Failed to construct PNG image:" << filename; + } + + // make sure that the pixel in the center of the pixmap is equal (these are always one-color images) + QImage d = daemon.toImage(); + QImage c = clientPixmap.toImage(); + + QRgb color = d.pixel(size.width() / 2, size.height() / 2); + QRgb color2 = c.pixel(size.width() / 2, size.height() / 2); + + if(color != color2) { + qDebug() << "ERROR: Colors don't match:" << theme << QColor(color) << QColor(color2); + return false; + } + + return true; +} diff --git a/mthemedaemon/test/clientmanager.h b/mthemedaemon/test/clientmanager.h new file mode 100644 index 00000000..7b565f59 --- /dev/null +++ b/mthemedaemon/test/clientmanager.h @@ -0,0 +1,68 @@ +/*************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (directui@nokia.com) +** +** This file is part of libmeegotouch. +** +** If you have questions regarding the use of this file, please contact +** Nokia at directui@nokia.com. +** +** This library is free software; you can redistribute it and/or +** modify it 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. +** +****************************************************************************/ + +#ifndef CLIENTMANAGER_H +#define CLIENTMANAGER_H + +#include <QObject> +#include <QProcess> +#include <QSet> +#include <QSize> + +class ClientThread; +class Client; + +static const QString THEME_ROOT_DIRECTORY = QString("themes"); + +class ClientManager : public QObject +{ + Q_OBJECT + + static const int MAX_CLIENT_COUNT = 10; + +public: + ClientManager(QProcess &process); + ~ClientManager(); + +protected: + void spawnClient(); + +private slots: + void start(); + void stop(); + + void checkConsistency(); + + void clientStarted(); + void clientFinished(); + void changeThemeAndLocale(); + + void pixmapReady(const QString& theme, Client* client, quint32 handle, const QString& imageId, const QSize& size); + +private: + void cleanup(); + bool verifyPixmap(const QString& theme, Client* client, quint32 handle, const QString& imageId, const QSize& size); + + QStringList locales; + QSet<ClientThread *> clients; + bool shutdown; + QProcess &themedaemon; +}; + +#endif diff --git a/mthemedaemon/test/main.cpp b/mthemedaemon/test/main.cpp new file mode 100644 index 00000000..0c1c9532 --- /dev/null +++ b/mthemedaemon/test/main.cpp @@ -0,0 +1,65 @@ +/*************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (directui@nokia.com) +** +** This file is part of libmeegotouch. +** +** If you have questions regarding the use of this file, please contact +** Nokia at directui@nokia.com. +** +** This library is free software; you can redistribute it and/or +** modify it 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. +** +****************************************************************************/ + +#include <QDebug> +#include <QApplication> +#include <QDir> +#include <MGConfItem> +#include <theme/mthemedaemon.h> +#include "keypresswaiter.h" +#include "clientmanager.h" + +int main(int argc, char **argv) +{ + QApplication app(argc, argv); + +#ifdef HAVE_GCONF + const QString themeName = "theme_one"; + QDir themeDirectory(THEME_ROOT_DIRECTORY + '/' + themeName); + if (themeDirectory.exists()) { + MGConfItem theme("/M/theme/name"); + theme.set(themeName); + } +#endif + + QProcess td; + td.start("./testdaemon/testdaemon", QStringList()); + // start theme daemon + if (td.waitForStarted()) { + // This is the class that handles the test + ClientManager manager(td); + + // close on enter + KeyPressWaiter keyWaiter; + QObject::connect(&keyWaiter, SIGNAL(finished()), &manager, SLOT(stop())); + keyWaiter.start(); + + // event loop + int result = app.exec(); + + // stop theme daemon + if (td.state() == QProcess::Running) { + td.close(); + } + return result; + } + + qDebug() << "Theme daemon failed to start"; + return 0; +} diff --git a/mthemedaemon/test/mthemedaemontest.pro b/mthemedaemon/test/mthemedaemontest.pro new file mode 100644 index 00000000..69c9b5a0 --- /dev/null +++ b/mthemedaemon/test/mthemedaemontest.pro @@ -0,0 +1,50 @@ +include(../../mkspecs/common.pri) + +INCLUDEPATH += . ../ ../../src/include ../../src ../../src/corelib ../../src/corelib/core ../../src/corelib/theme +DEPENDPATH += $$INCLUDEPATH +QMAKE_LIBDIR += ../lib +TEMPLATE = app +DEPENDPATH += . + +QT += svg network + +#DEFINES += PRINT_INFO_MESSAGES + +# Check for mixing of const and non-const iterators, +# which can cause problems when built with some compilers: +DEFINES += QT_STRICT_ITERATORS + +# override theme directory +DEFINES += IMAGESDIR=\\\"./images\\\" + +!win32:CONFIG += link_pkgconfig +PKGCONFIG += gconf-2.0 + +# Input +SOURCES += main.cpp \ + clientmanager.cpp \ + client.cpp \ + ../../src/corelib/theme/mthemedaemon.cpp \ + ../../src/corelib/theme/mcommonpixmaps.cpp \ + ../../src/corelib/theme/mimagedirectory.cpp \ + ../../src/corelib/theme/mthemedaemonclient.cpp \ + ../../src/corelib/theme/mthemedaemonprotocol.cpp \ + ../../src/corelib/theme/mthemeresourcemanager.cpp \ + ../../src/corelib/core/mgconfitem.cpp \ + ../../src/corelib/core/mcpumonitor.cpp \ + + +HEADERS += clientmanager.h \ + client.h \ + ../keypresswaiter.h \ + ../../src/corelib/theme/imthemedaemon.h \ + ../../src/corelib/theme/mthemedaemon.h \ + ../../src/corelib/theme/mcommonpixmaps.h \ + ../../src/corelib/theme/mimagedirectory.h \ + ../../src/corelib/theme/mthemedaemonclient.h \ + ../../src/corelib/theme/mthemedaemonprotocol.h \ + ../../src/corelib/theme/mthemeresourcemanager.h \ + ../../src/corelib/core/mgconfitem.h \ + ../../src/corelib/core/mcpumonitor.h \ + +SUBDIRS += testdaemodrgtrgn diff --git a/mthemedaemon/test/testdaemon/testdaemon.pro b/mthemedaemon/test/testdaemon/testdaemon.pro new file mode 100644 index 00000000..a3a98663 --- /dev/null +++ b/mthemedaemon/test/testdaemon/testdaemon.pro @@ -0,0 +1,45 @@ +INCLUDEPATH += . ../../../src/include ../../../src ../../../src/corelib/core ../../../src/corelib/ + +TEMPLATE = app +DEPENDPATH += . + +QT += svg network + +#DEFINES += MTHEME_PRINT_DEBUG +#DEFINES += CLOSE_ON_ENTER + +DEFINES += THEMEDIR=\\\"\"themes\"\\\" + +# enable QString optimizations +DEFINES += QT_USE_FAST_CONCATENATION QT_USE_FAST_OPERATOR_PLUS + +# Check for mixing of const and non-const iterators, +# which can cause problems when built with some compilers: +DEFINES += QT_STRICT_ITERATORS + +CONFIG += link_pkgconfig +PKGCONFIG += gconf-2.0 + +# Input +SOURCES += ../../main.cpp \ + ../../mthemedaemonserver.cpp \ + ../../../src/corelib/theme/mthemedaemon.cpp \ + ../../../src/corelib/theme/mcommonpixmaps.cpp \ + ../../../src/corelib/theme/mimagedirectory.cpp \ + ../../../src/corelib/theme/mthemedaemonclient.cpp \ + ../../../src/corelib/theme/mthemedaemonprotocol.cpp \ + ../../../src/corelib/theme/mthemeresourcemanager.cpp \ + ../../../src/corelib/core/mgconfitem.cpp \ + ../../../src/corelib/core/mcpumonitor.cpp \ + +HEADERS += \ + ../../mthemedaemonserver.h \ + ../../../src/corelib/theme/mthemedaemon.h \ + ../../../src/corelib/theme/mcommonpixmaps.h \ + ../../../src/corelib/theme/mimagedirectory.h \ + ../../../src/corelib/theme/mthemedaemonclient.h \ + ../../../src/corelib/theme/mthemedaemonprotocol.h \ + ../../../src/corelib/theme/mthemeresourcemanager.h \ + ../../../src/corelib/core/mgconfitem.h \ + ../../../src/corelib/core/mcpumonitor.h \ + diff --git a/mthemedaemon/test/themes/base/index.theme b/mthemedaemon/test/themes/base/index.theme new file mode 100644 index 00000000..2c5cca43 --- /dev/null +++ b/mthemedaemon/test/themes/base/index.theme @@ -0,0 +1,7 @@ +[Desktop Entry] +Type=X-DUI-Metatheme +Name=Base +Encoding=UTF-8 + +[X-DUI-Metatheme] +X-Visible=false diff --git a/mthemedaemon/test/themes/base/m/libduicore/style/libdui.css b/mthemedaemon/test/themes/base/m/libduicore/style/libdui.css new file mode 100644 index 00000000..4bd8276f --- /dev/null +++ b/mthemedaemon/test/themes/base/m/libduicore/style/libdui.css @@ -0,0 +1 @@ +// empty file diff --git a/mthemedaemon/test/themes/theme_one/index.theme b/mthemedaemon/test/themes/theme_one/index.theme new file mode 100644 index 00000000..1949ed97 --- /dev/null +++ b/mthemedaemon/test/themes/theme_one/index.theme @@ -0,0 +1,8 @@ +[Desktop Entry] +Type=X-DUI-Metatheme +Name=One +Encoding=UTF-8 + +[X-DUI-Metatheme] +X-Visible=true +X-Inherits=base diff --git a/mthemedaemon/test/themes/theme_one/m/constants.ini b/mthemedaemon/test/themes/theme_one/m/constants.ini new file mode 100644 index 00000000..3599b2b3 --- /dev/null +++ b/mthemedaemon/test/themes/theme_one/m/constants.ini @@ -0,0 +1,34 @@ +[Palette] +COLOR_FOREGROUND = #000000 ;text color +COLOR_SECONDARY_FOREGROUND = #cc6633 ;secondary text +COLOR_BACKGROUND = #ffffff ;background + +;reversed elements +COLOR_INVERTED_FOREGROUND = #ffffff ;text color +COLOR_INVERTED_SECONDARY_FOREGROUND = #CC6633 ;secondary text +COLOR_INVERTED_BACKGROUND = #FFFFFF ;background + +;selection +COLOR_SELECT = #CC6633 ;selected item background + +;notification colors +COLOR_WARNING = #CC0000 ;"red" attention color +COLOR_ATTENTION = #CC9900 ;"yellow" attention color +COLOR_NOTIFICATION = #C3F500 ;"green" attention color + +COLOR_LINK = #3465a4; +COLOR_LINK_ACTIVE = #f5bf00; + +COLOR_ACCENT1 = #aad400 ;crayon #1 for applications +COLOR_ACCENT2 = #69bfde ;crayon #2 for applications +COLOR_ACCENT3 = #ff9955 ;crayon #3 for applications +COLOR_ACCENT4 = #de87cd ;crayon #4 for applications +COLOR_ACCENT5 = #d8b427 ;crayon #5 for applications + +[Fonts] +FONT_XLARGE = "Nokia Sans" 3.2mm ;32px +FONT_LARGE = "Nokia Sans" 2.8mm ;28px +FONT_DEFAULT = "Nokia Sans" 2.4mm ;24px +FONT_SMALL = "Nokia Sans" 2.0mm ;20px +FONT_XSMALL = "Nokia Sans" 1.6mm ;16px + diff --git a/mthemedaemon/test/themes/theme_one/m/locale/fi/constants.ini b/mthemedaemon/test/themes/theme_one/m/locale/fi/constants.ini new file mode 100644 index 00000000..73dd058d --- /dev/null +++ b/mthemedaemon/test/themes/theme_one/m/locale/fi/constants.ini @@ -0,0 +1,34 @@ +[Palette] +COLOR_FOREGROUND = #ffffff ;text color +COLOR_SECONDARY_FOREGROUND = #cc6633 ;secondary text +COLOR_BACKGROUND = #000000 ;background + +;reversed elements +COLOR_INVERTED_FOREGROUND = #000000 ;text color +COLOR_INVERTED_SECONDARY_FOREGROUND = #CC6633 ;secondary text +COLOR_INVERTED_BACKGROUND = #FFFFFF ;background + +;selection +COLOR_SELECT = #CC6633 ;selected item background + +;notification colors +COLOR_WARNING = #CC0000 ;"red" attention color +COLOR_ATTENTION = #CC9900 ;"yellow" attention color +COLOR_NOTIFICATION = #C3F500 ;"green" attention color + +COLOR_LINK = #3465a4; +COLOR_LINK_ACTIVE = #f5bf00; + +COLOR_ACCENT1 = #aad400 ;crayon #1 for applications +COLOR_ACCENT2 = #69bfde ;crayon #2 for applications +COLOR_ACCENT3 = #ff9955 ;crayon #3 for applications +COLOR_ACCENT4 = #de87cd ;crayon #4 for applications +COLOR_ACCENT5 = #d8b427 ;crayon #5 for applications + +[Fonts] +FONT_XLARGE = "Arial" 3.2mm ;32px +FONT_LARGE = "Arial" 2.8mm ;28px +FONT_DEFAULT = "Arial" 2.4mm ;24px +FONT_SMALL = "Arial" 2.0mm ;20px +FONT_XSMALL = "Arial" 1.6mm ;16px + |