summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJavier S. Pedro <maemo@javispedro.com>2012-08-17 02:29:28 +0200
committerJavier S. Pedro <maemo@javispedro.com>2012-08-17 02:29:28 +0200
commit9c44782c5eab5635ca6adf4717409bf2ffb6c694 (patch)
tree99f774bccb6e964864168b346155bbd5a52bf3b2
parentf9ac9d207025fb8d40d1be753cde78beb77aa202 (diff)
downloadsowatch-9c44782c5eab5635ca6adf4717409bf2ffb6c694.tar.gz
sowatch-9c44782c5eab5635ca6adf4717409bf2ffb6c694.zip
new nekowatchlet and minor graphics/performance changes
-rw-r--r--libsowatch/graphicswatchlet.cpp28
-rw-r--r--libsowatch/graphicswatchlet.h6
-rw-r--r--libsowatch/watchpaintengine.cpp19
-rw-r--r--libsowatch/watchserver.cpp5
-rw-r--r--metawatch/metawatch.h2
-rw-r--r--nekowatchlet/Neko.qml176
-rw-r--r--nekowatchlet/icon.pngbin0 -> 177 bytes
-rw-r--r--nekowatchlet/metawatch-digital.qml49
-rw-r--r--nekowatchlet/neko.pngbin0 -> 2485 bytes
-rw-r--r--nekowatchlet/nekowatchlet.cpp17
-rw-r--r--nekowatchlet/nekowatchlet.h20
-rw-r--r--nekowatchlet/nekowatchlet.pro26
-rw-r--r--nekowatchlet/nekowatchletplugin.cpp38
-rw-r--r--nekowatchlet/nekowatchletplugin.h25
-rw-r--r--nekowatchlet/original-resources/awake.pngbin0 -> 410 bytes
-rw-r--r--nekowatchlet/original-resources/down1.pngbin0 -> 392 bytes
-rw-r--r--nekowatchlet/original-resources/down2.pngbin0 -> 390 bytes
-rw-r--r--nekowatchlet/original-resources/dtogi1.pngbin0 -> 388 bytes
-rw-r--r--nekowatchlet/original-resources/dtogi2.pngbin0 -> 390 bytes
-rw-r--r--nekowatchlet/original-resources/dwleft1.pngbin0 -> 392 bytes
-rw-r--r--nekowatchlet/original-resources/dwleft2.pngbin0 -> 398 bytes
-rw-r--r--nekowatchlet/original-resources/dwright1.pngbin0 -> 398 bytes
-rw-r--r--nekowatchlet/original-resources/dwright2.pngbin0 -> 401 bytes
-rw-r--r--nekowatchlet/original-resources/jare2.pngbin0 -> 400 bytes
-rw-r--r--nekowatchlet/original-resources/kaki1.pngbin0 -> 400 bytes
-rw-r--r--nekowatchlet/original-resources/kaki2.pngbin0 -> 393 bytes
-rw-r--r--nekowatchlet/original-resources/left1.pngbin0 -> 399 bytes
-rw-r--r--nekowatchlet/original-resources/left2.pngbin0 -> 390 bytes
-rw-r--r--nekowatchlet/original-resources/ltogi1.pngbin0 -> 398 bytes
-rw-r--r--nekowatchlet/original-resources/ltogi2.pngbin0 -> 397 bytes
-rwxr-xr-xnekowatchlet/original-resources/makeatlas.sh32
-rw-r--r--nekowatchlet/original-resources/mati2.pngbin0 -> 383 bytes
-rw-r--r--nekowatchlet/original-resources/mati3.pngbin0 -> 388 bytes
-rw-r--r--nekowatchlet/original-resources/right1.pngbin0 -> 400 bytes
-rw-r--r--nekowatchlet/original-resources/right2.pngbin0 -> 400 bytes
-rw-r--r--nekowatchlet/original-resources/rtogi1.pngbin0 -> 395 bytes
-rw-r--r--nekowatchlet/original-resources/rtogi2.pngbin0 -> 397 bytes
-rw-r--r--nekowatchlet/original-resources/sleep1.pngbin0 -> 393 bytes
-rw-r--r--nekowatchlet/original-resources/sleep2.pngbin0 -> 380 bytes
-rw-r--r--nekowatchlet/original-resources/up1.pngbin0 -> 373 bytes
-rw-r--r--nekowatchlet/original-resources/up2.pngbin0 -> 385 bytes
-rw-r--r--nekowatchlet/original-resources/upleft1.pngbin0 -> 389 bytes
-rw-r--r--nekowatchlet/original-resources/upleft2.pngbin0 -> 404 bytes
-rw-r--r--nekowatchlet/original-resources/upright1.pngbin0 -> 396 bytes
-rw-r--r--nekowatchlet/original-resources/upright2.pngbin0 -> 407 bytes
-rw-r--r--nekowatchlet/original-resources/utogi1.pngbin0 -> 397 bytes
-rw-r--r--nekowatchlet/original-resources/utogi2.pngbin0 -> 396 bytes
-rw-r--r--nekowatchlet/res/makeatlas.sh32
-rw-r--r--sowatch.pro4
-rw-r--r--sowatchd/watchhandler.cpp12
50 files changed, 479 insertions, 12 deletions
diff --git a/libsowatch/graphicswatchlet.cpp b/libsowatch/graphicswatchlet.cpp
index 2d58ff6..08441b0 100644
--- a/libsowatch/graphicswatchlet.cpp
+++ b/libsowatch/graphicswatchlet.cpp
@@ -7,8 +7,10 @@
using namespace sowatch;
-GraphicsWatchlet::GraphicsWatchlet(WatchServer* server, const QString& id) :
- Watchlet(server, id), _scene(0), _frameTimer(), _damaged()
+GraphicsWatchlet::GraphicsWatchlet(WatchServer* server, const QString& id)
+ : Watchlet(server, id),
+ _scene(0), _frameTimer(),
+ _fullUpdateMode(false), _damaged()
{
_frameTimer.setSingleShot(true);
connect(&_frameTimer, SIGNAL(timeout()), SLOT(frameTimeout()));
@@ -35,6 +37,16 @@ void GraphicsWatchlet::setScene(QGraphicsScene *scene)
}
}
+bool GraphicsWatchlet::fullUpdateMode() const
+{
+ return _fullUpdateMode;
+}
+
+void GraphicsWatchlet::setFullUpdateMode(bool fullUpdateMode)
+{
+ _fullUpdateMode = fullUpdateMode;
+}
+
QRectF GraphicsWatchlet::sceneRect() const
{
if (_scene) {
@@ -56,12 +68,16 @@ QRect GraphicsWatchlet::viewportRect() const
void GraphicsWatchlet::sceneChanged(const QList<QRectF> &rects)
{
+ // Only consider scene updates if the watchlet is active
if (_active) {
- // Only consider scene updates if the watchlet is active
QRect viewport = viewportRect();
- foreach(const QRectF& frect, rects) {
- QRect rect = frect.toAlignedRect() & viewport;
- _damaged += rect;
+ if (_fullUpdateMode) {
+ _damaged += viewport;
+ } else {
+ foreach(const QRectF& frect, rects) {
+ QRect rect = frect.toAlignedRect() & viewport;
+ _damaged += rect;
+ }
}
// Start frame timer if we got new data
diff --git a/libsowatch/graphicswatchlet.h b/libsowatch/graphicswatchlet.h
index 34f69cb..24413da 100644
--- a/libsowatch/graphicswatchlet.h
+++ b/libsowatch/graphicswatchlet.h
@@ -13,6 +13,8 @@ namespace sowatch
class SOWATCH_EXPORT GraphicsWatchlet : public Watchlet
{
Q_OBJECT
+ Q_PROPERTY(bool fullUpdateMode READ fullUpdateMode WRITE setFullUpdateMode)
+
public:
explicit GraphicsWatchlet(WatchServer* server, const QString& id);
~GraphicsWatchlet();
@@ -20,6 +22,9 @@ public:
QGraphicsScene* scene();
void setScene(QGraphicsScene* scene);
+ bool fullUpdateMode() const;
+ void setFullUpdateMode(bool fullUpdateMode);
+
QRectF sceneRect() const;
QRect viewportRect() const;
@@ -38,6 +43,7 @@ private slots:
void frameTimeout();
private:
+ bool _fullUpdateMode;
QRegion _damaged;
};
diff --git a/libsowatch/watchpaintengine.cpp b/libsowatch/watchpaintengine.cpp
index 78e6dfd..ea0dad8 100644
--- a/libsowatch/watchpaintengine.cpp
+++ b/libsowatch/watchpaintengine.cpp
@@ -5,8 +5,13 @@
using namespace sowatch;
+#define ENABLE_TRACE 0
+
+#if ENABLE_TRACE
+#define TRACE(x) x
+#else
#define TRACE(x)
-//#define TRACE(x) x
+#endif
WatchPaintEngine::WatchPaintEngine()
: QPaintEngine(QPaintEngine::AllFeatures),
@@ -31,19 +36,26 @@ bool WatchPaintEngine::begin(QPaintDevice *pdev)
_clipRegion = _area;
_transform = QTransform();
+ TRACE(qDebug() << " -- BEGIN FRAME -----");
+
return _painter.begin(pdev);
}
bool WatchPaintEngine::end()
{
+ TRACE(qDebug() << " -- END FRAME -------");
+ TRACE(qDebug() << _damaged << "------");
+
return _painter.end();
}
void WatchPaintEngine::damageMappedRect(const QRect &r)
{
if (_clipEnabled) {
+ TRACE(qDebug() << "Damaging" << _clipRegion.intersected(r));
_damaged += _clipRegion.intersected(r);
} else {
+ TRACE(qDebug() << "Damaging" << r);
_damaged += r;
}
}
@@ -152,12 +164,14 @@ void WatchPaintEngine::drawPath(const QPainterPath &path)
void WatchPaintEngine::drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr)
{
+ TRACE(qDebug() << __func__ << r << pm << sr);
damageRect(r);
_painter.drawPixmap(r, pm, sr);
}
void WatchPaintEngine::drawPoints(const QPointF *points, int pointCount)
{
+ TRACE(qDebug() << __func__ << points);
int i;
for (i = 0; i < pointCount; i++) {
const QPointF& p = points[i];
@@ -169,6 +183,7 @@ void WatchPaintEngine::drawPoints(const QPointF *points, int pointCount)
void WatchPaintEngine::drawPoints(const QPoint *points, int pointCount)
{
+ TRACE(qDebug() << __func__ << points);
int i;
for (i = 0; i < pointCount; i++) {
const QPoint& p = points[i];
@@ -180,6 +195,7 @@ void WatchPaintEngine::drawPoints(const QPoint *points, int pointCount)
void WatchPaintEngine::drawPolygon(const QPointF *points, int pointCount, PolygonDrawMode mode)
{
+ TRACE(qDebug() << __func__ << points);
QPolygonF p(pointCount);
int i;
for (i = 0; i < pointCount; i++) {
@@ -192,6 +208,7 @@ void WatchPaintEngine::drawPolygon(const QPointF *points, int pointCount, Polygo
void WatchPaintEngine::drawPolygon(const QPoint *points, int pointCount, PolygonDrawMode mode)
{
+ TRACE(qDebug() << __func__ << points);
QPolygon p(pointCount);
int i;
for (i = 0; i < pointCount; i++) {
diff --git a/libsowatch/watchserver.cpp b/libsowatch/watchserver.cpp
index 511c2c5..3d9db24 100644
--- a/libsowatch/watchserver.cpp
+++ b/libsowatch/watchserver.cpp
@@ -58,13 +58,16 @@ void WatchServer::setNextWatchletButton(const QString& value)
void WatchServer::addWatchlet(Watchlet *watchlet)
{
+ Q_ASSERT(watchlet);
insertWatchlet(_watchlets.size(), watchlet);
}
void WatchServer::insertWatchlet(int position, Watchlet *watchlet)
{
- const QString id = watchlet->id();
+ Q_ASSERT(watchlet);
Q_ASSERT(watchlet->_server == this);
+
+ const QString id = watchlet->id();
Q_ASSERT(!_watchletIds.contains(id));
_watchlets.insert(position, watchlet);
diff --git a/metawatch/metawatch.h b/metawatch/metawatch.h
index e6cedc6..109fb23 100644
--- a/metawatch/metawatch.h
+++ b/metawatch/metawatch.h
@@ -26,7 +26,7 @@ public:
explicit MetaWatch(ConfigKey *settings, QObject *parent = 0);
~MetaWatch();
- static const int DelayBetweenMessages = 10;
+ static const int DelayBetweenMessages = 5;
static const int VibrateLength = 500;
static const int DelayBetweenRings = 2500;
diff --git a/nekowatchlet/Neko.qml b/nekowatchlet/Neko.qml
new file mode 100644
index 0000000..b8b08ad
--- /dev/null
+++ b/nekowatchlet/Neko.qml
@@ -0,0 +1,176 @@
+import QtQuick 1.0
+
+Item {
+ id: neko
+ height: 32
+ width: 32
+ clip: true
+
+ property int targetX;
+ property int targetY;
+ property alias imageSource: sprite.source
+ property bool running: true
+
+ property int maxSpeed: 6
+
+ property int _anim
+ property int _animFrames
+ property int _animCurFrame
+
+ property int _distanceX: targetX - x - (width / 2)
+ property int _distanceY: targetY - y - (5 * height / 6)
+ property int _distance: Math.sqrt(_distanceX * _distanceX + _distanceY * _distanceY)
+
+ property int _calcSpeedX: _distance < maxSpeed ?
+ _distanceX :
+ ((maxSpeed * _distanceX) / _distance)
+ property int _calcSpeedY: _distance < maxSpeed ?
+ _distanceY :
+ ((maxSpeed * _distanceY) / _distance)
+
+ property real _calcAngle: Math.atan2(_calcSpeedY, _calcSpeedX)
+
+ property int _speedX: 0
+ property int _speedY: 0
+
+ signal arrived
+
+ function chooseWalkAnimation(speedX, speedY, angle) {
+ var PiPer8 = Math.PI / 8;
+
+ if (angle > 7 * PiPer8) {
+ return 6; // left
+ } else if (angle > 5 * PiPer8) {
+ return 2; // dwleft
+ } else if (angle > 3 * PiPer8) {
+ return 0; // down
+ } else if (angle > 1 * PiPer8) {
+ return 3; // dwright
+ } else if (angle > -1 * PiPer8) {
+ return 9; // right
+ } else if (angle > -3 * PiPer8) {
+ return 14; // upright
+ } else if (angle > -5 * PiPer8) {
+ return 12; // up
+ } else {
+ return 13; // upleft
+ }
+ }
+
+ Image {
+ id: sprite
+ source: "neko.png"
+
+ x: -(neko.width * neko._animCurFrame)
+ y: -(neko.height * neko._anim)
+ }
+
+ Timer {
+ id: timer
+ interval: 250
+ running: neko.running
+ repeat: true
+ onTriggered: {
+ neko._animCurFrame = (_animCurFrame + 1) % _animFrames
+ neko.x = neko.x + _speedX;
+ neko.y = neko.y + _speedY;
+
+ if (state == "WALKING" && _distance == 0) {
+ neko.arrived();
+ }
+ }
+ }
+
+ Timer {
+ id: idleToScratchTimer
+ onTriggered: neko.state = "SCRATCHING"
+ }
+
+ Timer {
+ id: idleToYawnTimer
+ onTriggered: neko.state = "YAWNING"
+ }
+
+ Timer {
+ id: scrachTimer
+ interval: timer.interval * 2
+ running: neko.running && state === "SCRATCHING"
+ onTriggered: neko.state = "IDLING"
+ }
+
+ Timer {
+ id: yawnTimer
+ interval: timer.interval * 3
+ running: neko.running && state === "YAWNING"
+ onTriggered: neko.state = "IDLING"
+ }
+
+ onTargetXChanged: state = "WALKING"
+ onTargetYChanged: state = "WALKING"
+
+ onArrived: state = "IDLING"
+
+ states: [
+ State {
+ name: "SLEEPING"
+ PropertyChanges {
+ target: neko
+ _anim: 11 // sleep
+ _animFrames: 2
+ _speedX: 0
+ _speedY: 0
+ }
+ },
+ State {
+ name: "IDLING"
+ PropertyChanges {
+ target: neko
+ _anim: 4 // jare
+ _animFrames: 1
+ _animCurFrame: 0
+ _speedX: 0
+ _speedY: 0
+ }
+ PropertyChanges {
+ target: idleToScratchTimer
+ interval: Math.random() * 10000
+ running: neko.running && state == "IDLING"
+ }
+ PropertyChanges {
+ target: idleToYawnTimer
+ interval: Math.random() * 20000
+ running: neko.running && state == "IDLING"
+ }
+ },
+ State {
+ name: "SCRATCHING"
+ PropertyChanges {
+ target: neko
+ _anim: 5 // kaki
+ _animFrames: 2
+ _speedX: 0
+ _speedY: 0
+ }
+ },
+ State {
+ name: "YAWNING"
+ PropertyChanges {
+ target: neko
+ _anim: 8 // mati
+ _animFrames: 2
+ _speedX: 0
+ _speedY: 0
+ }
+ },
+ State {
+ name: "WALKING"
+ PropertyChanges {
+ target: neko
+ _anim: chooseWalkAnimation(_calcSpeedX, _calcSpeedY, _calcAngle)
+ _animFrames: 2
+ _speedX: _calcSpeedX
+ _speedY: _calcSpeedY
+ }
+ }
+ ]
+}
diff --git a/nekowatchlet/icon.png b/nekowatchlet/icon.png
new file mode 100644
index 0000000..4c5fb83
--- /dev/null
+++ b/nekowatchlet/icon.png
Binary files differ
diff --git a/nekowatchlet/metawatch-digital.qml b/nekowatchlet/metawatch-digital.qml
new file mode 100644
index 0000000..b1e16c8
--- /dev/null
+++ b/nekowatchlet/metawatch-digital.qml
@@ -0,0 +1,49 @@
+import QtQuick 1.0
+import com.javispedro.sowatch.metawatch 1.0
+
+MWPage {
+ Neko {
+ id: neko
+ running: watch.active
+
+ targetX: goal.x
+ targetY: goal.y
+ }
+
+ Rectangle {
+ id: goal
+ width: 2
+ height: 2
+ color: "black"
+
+ Behavior on x { SmoothedAnimation { velocity: 80; }}
+ Behavior on y { SmoothedAnimation { velocity: 80; }}
+ }
+
+ function goToRandomPosition() {
+ goal.x = 16 + Math.floor(Math.random() * (width - 32));
+ goal.y = 16 + Math.floor(Math.random() * (height - 32));
+ }
+
+ function goToSleep() {
+ neko.state = "SLEEPING";
+ }
+
+ Connections {
+ target: watch
+ onActiveChanged: {
+ if (watch.active) {
+ goToSleep();
+ }
+ }
+
+ onButtonPressed : {
+ switch (button) {
+ case 1:
+ case 2:
+ goToRandomPosition();
+ break;
+ }
+ }
+ }
+}
diff --git a/nekowatchlet/neko.png b/nekowatchlet/neko.png
new file mode 100644
index 0000000..45d588c
--- /dev/null
+++ b/nekowatchlet/neko.png
Binary files differ
diff --git a/nekowatchlet/nekowatchlet.cpp b/nekowatchlet/nekowatchlet.cpp
new file mode 100644
index 0000000..627d127
--- /dev/null
+++ b/nekowatchlet/nekowatchlet.cpp
@@ -0,0 +1,17 @@
+#include "nekowatchlet.h"
+
+using namespace sowatch;
+
+const QLatin1String NekoWatchlet::myId("com.javispedro.sowatch.neko");
+
+NekoWatchlet::NekoWatchlet(WatchServer* server) :
+ DeclarativeWatchlet(server, myId)
+{
+ // Workaround Qt's stupid clip region calculation when
+ // - There's a QML item with clip = true
+ // - And we are using "compat" updateRects() signal mode
+ setFullUpdateMode(true);
+
+ setSource(QUrl(SOWATCH_QML_DIR "/nekowatchlet/" + server->watch()->model() + ".qml"));
+}
+
diff --git a/nekowatchlet/nekowatchlet.h b/nekowatchlet/nekowatchlet.h
new file mode 100644
index 0000000..067606a
--- /dev/null
+++ b/nekowatchlet/nekowatchlet.h
@@ -0,0 +1,20 @@
+#ifndef NEKOWATCHLET_H
+#define NEKOWATCHLET_H
+
+#include <sowatch.h>
+
+namespace sowatch
+{
+
+class NekoWatchlet : public DeclarativeWatchlet
+{
+ Q_OBJECT
+public:
+ explicit NekoWatchlet(WatchServer* server);
+
+ static const QLatin1String myId;
+};
+
+}
+
+#endif // NEKOWATCHLET_H
diff --git a/nekowatchlet/nekowatchlet.pro b/nekowatchlet/nekowatchlet.pro
new file mode 100644
index 0000000..b45979a
--- /dev/null
+++ b/nekowatchlet/nekowatchlet.pro
@@ -0,0 +1,26 @@
+TARGET = nekowatchlet
+TEMPLATE = lib
+CONFIG += plugin
+
+SOURCES += nekowatchletplugin.cpp nekowatchlet.cpp
+
+HEADERS += nekowatchletplugin.h nekowatchlet.h
+
+qml_files.files = metawatch-digital.qml Neko.qml icon.png neko.png
+
+LIBS += -L$$OUT_PWD/../libsowatch/ -lsowatch
+INCLUDEPATH += $$PWD/../libsowatch
+DEPENDPATH += $$PWD/../libsowatch
+QML_IMPORT_PATH += $$PWD/../metawatch/qml
+
+unix:!symbian {
+ !isEmpty(MEEGO_VERSION_MAJOR)|maemo5 {
+ QMAKE_RPATHDIR += /opt/sowatch/lib
+ target.path = /opt/sowatch/lib/watchlets
+ qml_files.path = /opt/sowatch/qml/$$TARGET
+ } else {
+ target.path = /usr/lib/sowatch/watchlets
+ qml_files.path = /usr/share/sowatch/qml/$$TARGET
+ }
+ INSTALLS += target qml_files
+}
diff --git a/nekowatchlet/nekowatchletplugin.cpp b/nekowatchlet/nekowatchletplugin.cpp
new file mode 100644
index 0000000..c891530
--- /dev/null
+++ b/nekowatchlet/nekowatchletplugin.cpp
@@ -0,0 +1,38 @@
+#include "nekowatchlet.h"
+#include "nekowatchletplugin.h"
+
+using namespace sowatch;
+
+NekoWatchletPlugin::NekoWatchletPlugin(QObject *parent) :
+ QObject(parent)
+{
+}
+
+NekoWatchletPlugin::~NekoWatchletPlugin()
+{
+}
+
+QStringList NekoWatchletPlugin::watchlets()
+{
+ QStringList l;
+ l << NekoWatchlet::myId;
+ return l;
+}
+
+WatchletPluginInterface::WatchletInfo NekoWatchletPlugin::describeWatchlet(const QString &id)
+{
+ WatchletInfo info;
+ if (id != NekoWatchlet::myId) return info;
+ info.name = "Neko";
+ info.icon = QUrl::fromLocalFile(SOWATCH_QML_DIR "/nekowatchlet/icon.png");
+ return info;
+}
+
+Watchlet* NekoWatchletPlugin::getWatchlet(const QString& driver, ConfigKey *settings, WatchServer *server)
+{
+ Q_UNUSED(driver);
+ Q_UNUSED(settings);
+ return new NekoWatchlet(server);
+}
+
+Q_EXPORT_PLUGIN2(notificationswatchlet, NekoWatchletPlugin)
diff --git a/nekowatchlet/nekowatchletplugin.h b/nekowatchlet/nekowatchletplugin.h
new file mode 100644
index 0000000..f7f1bdf
--- /dev/null
+++ b/nekowatchlet/nekowatchletplugin.h
@@ -0,0 +1,25 @@
+#ifndef NEKOWATCHLETPLUGIN_H
+#define NEKOWATCHLETPLUGIN_H
+
+#include <sowatch.h>
+
+namespace sowatch
+{
+
+class NekoWatchletPlugin : public QObject, public WatchletPluginInterface
+{
+ Q_OBJECT
+ Q_INTERFACES(sowatch::WatchletPluginInterface)
+
+public:
+ explicit NekoWatchletPlugin(QObject *parent = 0);
+ ~NekoWatchletPlugin();
+
+ QStringList watchlets();
+ WatchletInfo describeWatchlet(const QString &id);
+ Watchlet* getWatchlet(const QString& id, ConfigKey *settings, WatchServer* server);
+};
+
+}
+
+#endif // NEKOWATCHLETPLUGIN_H
diff --git a/nekowatchlet/original-resources/awake.png b/nekowatchlet/original-resources/awake.png
new file mode 100644
index 0000000..6eaf9c3
--- /dev/null
+++ b/nekowatchlet/original-resources/awake.png
Binary files differ
diff --git a/nekowatchlet/original-resources/down1.png b/nekowatchlet/original-resources/down1.png
new file mode 100644
index 0000000..e3f3656
--- /dev/null
+++ b/nekowatchlet/original-resources/down1.png
Binary files differ
diff --git a/nekowatchlet/original-resources/down2.png b/nekowatchlet/original-resources/down2.png
new file mode 100644
index 0000000..ad2e387
--- /dev/null
+++ b/nekowatchlet/original-resources/down2.png
Binary files differ
diff --git a/nekowatchlet/original-resources/dtogi1.png b/nekowatchlet/original-resources/dtogi1.png
new file mode 100644
index 0000000..29bbb36
--- /dev/null
+++ b/nekowatchlet/original-resources/dtogi1.png
Binary files differ
diff --git a/nekowatchlet/original-resources/dtogi2.png b/nekowatchlet/original-resources/dtogi2.png
new file mode 100644
index 0000000..03b8a17
--- /dev/null
+++ b/nekowatchlet/original-resources/dtogi2.png
Binary files differ
diff --git a/nekowatchlet/original-resources/dwleft1.png b/nekowatchlet/original-resources/dwleft1.png
new file mode 100644
index 0000000..855c779
--- /dev/null
+++ b/nekowatchlet/original-resources/dwleft1.png
Binary files differ
diff --git a/nekowatchlet/original-resources/dwleft2.png b/nekowatchlet/original-resources/dwleft2.png
new file mode 100644
index 0000000..753260f
--- /dev/null
+++ b/nekowatchlet/original-resources/dwleft2.png
Binary files differ
diff --git a/nekowatchlet/original-resources/dwright1.png b/nekowatchlet/original-resources/dwright1.png
new file mode 100644
index 0000000..fa6dc8a
--- /dev/null
+++ b/nekowatchlet/original-resources/dwright1.png
Binary files differ
diff --git a/nekowatchlet/original-resources/dwright2.png b/nekowatchlet/original-resources/dwright2.png
new file mode 100644
index 0000000..61ed2a3
--- /dev/null
+++ b/nekowatchlet/original-resources/dwright2.png
Binary files differ
diff --git a/nekowatchlet/original-resources/jare2.png b/nekowatchlet/original-resources/jare2.png
new file mode 100644
index 0000000..a0a0662
--- /dev/null
+++ b/nekowatchlet/original-resources/jare2.png
Binary files differ
diff --git a/nekowatchlet/original-resources/kaki1.png b/nekowatchlet/original-resources/kaki1.png
new file mode 100644
index 0000000..c499f43
--- /dev/null
+++ b/nekowatchlet/original-resources/kaki1.png
Binary files differ
diff --git a/nekowatchlet/original-resources/kaki2.png b/nekowatchlet/original-resources/kaki2.png
new file mode 100644
index 0000000..b4e0c4e
--- /dev/null
+++ b/nekowatchlet/original-resources/kaki2.png
Binary files differ
diff --git a/nekowatchlet/original-resources/left1.png b/nekowatchlet/original-resources/left1.png
new file mode 100644
index 0000000..eb990ab
--- /dev/null
+++ b/nekowatchlet/original-resources/left1.png
Binary files differ
diff --git a/nekowatchlet/original-resources/left2.png b/nekowatchlet/original-resources/left2.png
new file mode 100644
index 0000000..532aaa9
--- /dev/null
+++ b/nekowatchlet/original-resources/left2.png
Binary files differ
diff --git a/nekowatchlet/original-resources/ltogi1.png b/nekowatchlet/original-resources/ltogi1.png
new file mode 100644
index 0000000..30702c9
--- /dev/null
+++ b/nekowatchlet/original-resources/ltogi1.png
Binary files differ
diff --git a/nekowatchlet/original-resources/ltogi2.png b/nekowatchlet/original-resources/ltogi2.png
new file mode 100644
index 0000000..658bbe7
--- /dev/null
+++ b/nekowatchlet/original-resources/ltogi2.png
Binary files differ
diff --git a/nekowatchlet/original-resources/makeatlas.sh b/nekowatchlet/original-resources/makeatlas.sh
new file mode 100755
index 0000000..e04a50f
--- /dev/null
+++ b/nekowatchlet/original-resources/makeatlas.sh
@@ -0,0 +1,32 @@
+#!/bin/bash
+
+set -e
+
+allfiles=$(echo *.png | sort)
+atlasfiles=""
+stems=""
+
+for i in $allfiles
+do
+ echo $i
+ if [[ $i =~ ^([a-z]+)[1-9].png ]]
+ then
+ stem=${BASH_REMATCH[1]}
+ output=atlas/$stem.png
+ if [[ $atlasfiles != *$output* ]]
+ then
+ montage $stem?.png -geometry 32x32 $output
+ atlasfiles="$atlasfiles $output"
+ stems="$stems $stem"
+ fi
+ fi
+done
+
+montage $atlasfiles -geometry 64x32+0+0 -gravity NorthWest -tile 1x atlas/atlas.png
+
+rm -f $atlasfiles
+
+echo $stems | tr ' ' '\n' > atlas/atlas.txt
+
+exit 0
+
diff --git a/nekowatchlet/original-resources/mati2.png b/nekowatchlet/original-resources/mati2.png
new file mode 100644
index 0000000..e2e079e
--- /dev/null
+++ b/nekowatchlet/original-resources/mati2.png
Binary files differ
diff --git a/nekowatchlet/original-resources/mati3.png b/nekowatchlet/original-resources/mati3.png
new file mode 100644
index 0000000..10146b3
--- /dev/null
+++ b/nekowatchlet/original-resources/mati3.png
Binary files differ
diff --git a/nekowatchlet/original-resources/right1.png b/nekowatchlet/original-resources/right1.png
new file mode 100644
index 0000000..6e12852
--- /dev/null
+++ b/nekowatchlet/original-resources/right1.png
Binary files differ
diff --git a/nekowatchlet/original-resources/right2.png b/nekowatchlet/original-resources/right2.png
new file mode 100644
index 0000000..da40e74
--- /dev/null
+++ b/nekowatchlet/original-resources/right2.png
Binary files differ
diff --git a/nekowatchlet/original-resources/rtogi1.png b/nekowatchlet/original-resources/rtogi1.png
new file mode 100644
index 0000000..0bdf57d
--- /dev/null
+++ b/nekowatchlet/original-resources/rtogi1.png
Binary files differ
diff --git a/nekowatchlet/original-resources/rtogi2.png b/nekowatchlet/original-resources/rtogi2.png
new file mode 100644
index 0000000..728ecde
--- /dev/null
+++ b/nekowatchlet/original-resources/rtogi2.png
Binary files differ
diff --git a/nekowatchlet/original-resources/sleep1.png b/nekowatchlet/original-resources/sleep1.png
new file mode 100644
index 0000000..f108b22
--- /dev/null
+++ b/nekowatchlet/original-resources/sleep1.png
Binary files differ
diff --git a/nekowatchlet/original-resources/sleep2.png b/nekowatchlet/original-resources/sleep2.png
new file mode 100644
index 0000000..08d9a2f
--- /dev/null
+++ b/nekowatchlet/original-resources/sleep2.png
Binary files differ
diff --git a/nekowatchlet/original-resources/up1.png b/nekowatchlet/original-resources/up1.png
new file mode 100644
index 0000000..ca4bb36
--- /dev/null
+++ b/nekowatchlet/original-resources/up1.png
Binary files differ
diff --git a/nekowatchlet/original-resources/up2.png b/nekowatchlet/original-resources/up2.png
new file mode 100644
index 0000000..0a5adf9
--- /dev/null
+++ b/nekowatchlet/original-resources/up2.png
Binary files differ
diff --git a/nekowatchlet/original-resources/upleft1.png b/nekowatchlet/original-resources/upleft1.png
new file mode 100644
index 0000000..4ae9d60
--- /dev/null
+++ b/nekowatchlet/original-resources/upleft1.png
Binary files differ
diff --git a/nekowatchlet/original-resources/upleft2.png b/nekowatchlet/original-resources/upleft2.png
new file mode 100644
index 0000000..673c7e2
--- /dev/null
+++ b/nekowatchlet/original-resources/upleft2.png
Binary files differ
diff --git a/nekowatchlet/original-resources/upright1.png b/nekowatchlet/original-resources/upright1.png
new file mode 100644
index 0000000..911201c
--- /dev/null
+++ b/nekowatchlet/original-resources/upright1.png
Binary files differ
diff --git a/nekowatchlet/original-resources/upright2.png b/nekowatchlet/original-resources/upright2.png
new file mode 100644
index 0000000..d95e06e
--- /dev/null
+++ b/nekowatchlet/original-resources/upright2.png
Binary files differ
diff --git a/nekowatchlet/original-resources/utogi1.png b/nekowatchlet/original-resources/utogi1.png
new file mode 100644
index 0000000..791d85b
--- /dev/null
+++ b/nekowatchlet/original-resources/utogi1.png
Binary files differ
diff --git a/nekowatchlet/original-resources/utogi2.png b/nekowatchlet/original-resources/utogi2.png
new file mode 100644
index 0000000..1a5dcd1
--- /dev/null
+++ b/nekowatchlet/original-resources/utogi2.png
Binary files differ
diff --git a/nekowatchlet/res/makeatlas.sh b/nekowatchlet/res/makeatlas.sh
new file mode 100644
index 0000000..60449ae
--- /dev/null
+++ b/nekowatchlet/res/makeatlas.sh
@@ -0,0 +1,32 @@
+#!/bin/bash
+
+set -e
+
+allfiles=$(echo *.png | sort)
+atlasfiles=""
+stems=""
+
+for i in $allfiles
+do
+ echo $i
+ if [[ $i =~ ^([a-z]+)[1-9].png ]]
+ then
+ stem=${BASH_REMATCH[1]}
+ output=$stem.png.tmp
+ if [[ $atlasfiles != *$output* ]]
+ then
+ montage $stem?.png -geometry 32x32 $output
+ atlasfiles="$atlasfiles $output"
+ stems="$stems $stem"
+ fi
+ fi
+done
+
+montage $atlasfiles -geometry 64x32+0+0 -gravity NorthWest -tile 1x atlas.png
+
+rm -f $atlasfiles
+
+echo $stems | tr ' ' '\n' > atlas.txt
+
+exit 0
+
diff --git a/sowatch.pro b/sowatch.pro
index f0aae71..dc181de 100644
--- a/sowatch.pro
+++ b/sowatch.pro
@@ -16,6 +16,10 @@ sysinfowatchlet.depends = libsowatch
qmsgwatchlet.depends = libsowatch
qmapwatchlet.depends = libsowatch
+# Less useful watchlets
+SUBDIRS += nekowatchlet
+nekowatchlet.depends = libsowatch
+
unix {
SUBDIRS += sowatchd
SUBDIRS += sowatchui
diff --git a/sowatchd/watchhandler.cpp b/sowatchd/watchhandler.cpp
index 4cd86d3..cc75d45 100644
--- a/sowatchd/watchhandler.cpp
+++ b/sowatchd/watchhandler.cpp
@@ -117,9 +117,15 @@ void WatchHandler::updateWatchlets()
// We need to add this watchlet
const QString id = newWatchlets[i];
Watchlet *watchlet = createWatchlet(id);
- _watchlet_order << id;
- _watchlets[id] = watchlet;
- _server->addWatchlet(watchlet);
+ if (watchlet) {
+ _watchlet_order << id;
+ _watchlets[id] = watchlet;
+ _server->addWatchlet(watchlet);
+ } else {
+ qWarning() << "Failed to load watchlet" << id;
+ newWatchlets.removeAt(i);
+ i--; // Retry
+ }
} else if (newWatchlets[i] != _watchlet_order[i]) {
// Let's find out if this watchlet has been moved, or removed.
const QString id = _watchlet_order[i];