summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMorten Sorvig <msorvig@trolltech.com>2009-09-04 06:04:21 +0200
committerMorten Sorvig <msorvig@trolltech.com>2009-09-04 06:04:21 +0200
commit31abf9bb9055195ff27ded52ab5f700b168186da (patch)
tree3536713e8c9f44b336c78a59e2cd9d5a96b0e70d
parent3d93f4c7677610a23b347b5e9e8a6881e021dcd3 (diff)
Add support for static widgets
-rw-r--r--src/eventhandler.js35
-rw-r--r--src/eventqueue.cpp69
-rw-r--r--src/eventqueue.h12
-rw-r--r--src/sessionserver.cpp8
-rw-r--r--src/sessionserver.h11
-rw-r--r--src/webclient.cpp4
-rw-r--r--src/webclient.h9
-rw-r--r--src/webclientserver.cpp5
-rw-r--r--src/webclientserver.h5
-rw-r--r--src/widgeteventhandler.cpp30
-rw-r--r--src/widgeteventhandler.h2
11 files changed, 167 insertions, 23 deletions
diff --git a/src/eventhandler.js b/src/eventhandler.js
index bd3d5fe..ca57b7b 100644
--- a/src/eventhandler.js
+++ b/src/eventhandler.js
@@ -31,6 +31,24 @@ function createWidgetElement(pointer)
return element;
}
+function createStaticWidgetElement(pointer)
+{
+ var element = document.createElement("img");
+ element.id = this.elmentId(pointer);
+ element["pointer"] = pointer;
+ element.className = "divWidgetStatic";
+ element.imageLoaded = false;
+ hideElement(element);
+ document.body.appendChild(element);
+
+ dojo.connect(element, 'onmousedown', this, sendMousePressedPreventDefault);
+ dojo.connect(element, 'onmouseup', this, sendMouseReleased);
+ dojo.connect(element, 'ondblclick', this, sendMouseDoubleClick);
+ dojo.connect(element, 'onkeypress', this, sendKeyPress);
+ dojo.connect(element, 'onload', this, imageLoaded);
+ return element;
+}
+
function createInputElement(pointer, type)
{
var inputElement = document.createElement("input");
@@ -115,8 +133,6 @@ function createNoopElement()
function createElement(widgetType, id)
{
-
-
//console.log("x createElement src: " + this.baseUrl + " " + widgetType + " id" + id);
var element;
@@ -135,6 +151,8 @@ function createElement(widgetType, id)
dojo.connect(element, 'onmousedown', this, dragBegin);
} else if (widgetType == "skippedwidget") {
element = createNoopElement(id);
+ } else if (widgetType == "genericstatic") {
+ element = this.createStaticWidgetElement(id);
} else {
element = this.createWidgetElement(id);
}
@@ -275,7 +293,17 @@ function eventHandler(text)
// alert("key" + key + "type" + type);
if (type == "update") {
// alert("update" + widget);
- var source = this.jsonUrl + JSON.stringify({ "type" : "img", "id" : widget, "rand" : Math.random()});
+
+ var source;
+ console.log("update " + widgetType + element.qtclassName);
+
+ if (widgetType == "genericstatic") {
+ var imagehash = array[key].imagehash;
+ source = this.jsonUrl + JSON.stringify({ "type" : "img-static", "id" : widget, "imagehash" : imagehash });
+ console.log(source);
+ } else {
+ source = this.jsonUrl + JSON.stringify({ "type" : "img", "id" : widget, "rand" : Math.random()});
+ }
element.src = source;
} else if (type == "geometry") {
@@ -475,6 +503,7 @@ function setUpWebClientObject(webclientObject)
// ...surely there is a more ideomatic way :)
webclientObject.elmentId = elmentId
webclientObject.createWidgetElement = createWidgetElement
+ webclientObject.createStaticWidgetElement = createStaticWidgetElement
webclientObject.createInputElement = createInputElement
webclientObject.createLabelElement = createLabelElement
webclientObject.createTextareaElement = createTextareaElement
diff --git a/src/eventqueue.cpp b/src/eventqueue.cpp
index a1af3e4..6f010b7 100644
--- a/src/eventqueue.cpp
+++ b/src/eventqueue.cpp
@@ -91,10 +91,11 @@ void EventQueue::handleJsonRequest(QByteArray jsonText, HttpResponse *response)
json_object* type = json_object_object_get(jsonRequest, "type");
QByteArray typeText = json_object_get_string(type);
- DEBUG << typeText;
if (typeText == "img") {
handleImageRequest(jsonRequest, response);
+ } else if (typeText == "img-static") {
+ handleStaticImageRequest(jsonRequest, response);
}
json_object_put(jsonRequest); //free
}
@@ -107,6 +108,8 @@ void EventQueue::handleImageRequest(json_object* jsonRequest, HttpResponse *resp
const quintptr id = json_object_get_int(idObject);
DEBUG << "id" << this << id << images.value(id).size();
+ // if (staticCompressedImages.contains(id))
+ // qFatal("serving dynamic image for static widdget");
// Save server memory and CPU time by storing images in the png
// format after the first serve. The browser could request
@@ -132,12 +135,44 @@ void EventQueue::handleImageRequest(json_object* jsonRequest, HttpResponse *resp
response->setContentType("image/png");
}
+void EventQueue::handleStaticImageRequest(json_object* jsonRequest, HttpResponse *response)
+{
+ json_object* imageHashObject = json_object_object_get(jsonRequest, "imagehash");
+ DEBUG << "handleStaticImageRequest" << imageHashObject;
+ if (!imageHashObject)
+ return;
+
+ const quintptr imageHash = json_object_get_int(imageHashObject);
+ DEBUG << "imagehash" << this << imageHash;
+
+ quintptr imageid = staticImageHashToWidgetId.value(imageHash);
+ QByteArray pngImage = staticCompressedImages.value(imageid);
+ DEBUG << imageid << pngImage.count();
+ response->setBody(pngImage);
+ response->setContentType("image/png");
+}
+
void EventQueue::addUpdateEvent(quintptr id, const QImage &image, QRect updateRect)
{
DEBUG << "addUpdateEvent" << this << id << image.size();
images.insert(id, image);
addEvent(id, EventEntry::Update);
}
+void EventQueue::addStaticUpdateEvent(quintptr id, uint hash, const QByteArray &pngImage)
+{
+ DEBUG << "addStaticUpdateEvent" << this << hash << pngImage.size();
+ staticCompressedImages.insert(id, pngImage);
+ staticImageHashToWidgetId.insert(hash, id);
+ staticWidgetIdToImageHash.insert(id, hash);
+ addEvent(id, EventEntry::StaticUpdate);
+}
+
+void EventQueue::addStaticUpdateEvent(quintptr id)
+{
+ DEBUG << "addStaticUpdateEvent" << this << id;
+ // assert staticCompressedImages.conrains(id);
+ addEvent(id, EventEntry::StaticUpdate);
+}
void EventQueue::addGeometryEvent(quintptr id, QRect globalGeometry)
{
@@ -195,6 +230,8 @@ json_object *EventQueue::toJson(const EventEntry &event) const
{
case EventEntry::Update:
return jsonUpdateEvent(event);
+ case EventEntry::StaticUpdate:
+ return jsonStaticUpdateEvent(event);
case EventEntry::Show:
return jsonShowEvent(event);
case EventEntry::ShowLineEdit:
@@ -255,6 +292,18 @@ json_object *EventQueue::jsonUpdateEvent(const EventEntry &event) const
return obj;
}
+json_object *EventQueue::jsonStaticUpdateEvent(const EventEntry &event) const
+{
+ struct json_object *obj = json_object_new_object();
+ json_object_object_add(obj, "type", json_object_new_string("update"));
+ json_object_object_add(obj, "id", json_object_new_int(event.id));
+ json_object_object_add(obj, "imagehash",
+ json_object_new_string(QByteArray::number(staticWidgetIdToImageHash.value(event.id)).data()));
+ json_object_object_add(obj, "widgetType", toJsonWidgetType((QWidget *)event.id));
+ return obj;
+}
+
+
json_object *EventQueue::jsonGeometryEvent(const EventEntry &event) const
{
struct json_object *obj = json_object_new_object();
@@ -308,5 +357,21 @@ json_object *EventQueue::toJsonWidgetType(QWidget *widget) const
return json_object_new_string("midisubwindow");
if (qobject_cast<QPushButton *>(widget))
return json_object_new_string("pusbutton");
- return json_object_new_string("generic");
+
+ if (m_session->m_server->testHint(widget, WebClient::StaticWidget)) {
+ return json_object_new_string("genericstatic");
+ } else {
+ return json_object_new_string("generic");
+ }
}
+
+QByteArray EventQueue::pngCompress(const QImage &image)
+{
+ QByteArray pngImage;
+ QBuffer buffer(&pngImage);
+ buffer.open(QIODevice::WriteOnly);
+ QImageWriter writer(&buffer, "png");
+ writer.write(image);
+ return pngImage;
+}
+
diff --git a/src/eventqueue.h b/src/eventqueue.h
index 7085a32..7d93e1b 100644
--- a/src/eventqueue.h
+++ b/src/eventqueue.h
@@ -7,7 +7,7 @@
struct EventEntry
{
- enum Type { Noop, Update, Show, Hide, Geometry, ShowLineEdit, ParentChange, TextUpdate };
+ enum Type { Noop, Update, StaticUpdate, Show, Hide, Geometry, ShowLineEdit, ParentChange, TextUpdate };
inline EventEntry(int id, Type type)
:id(id), type(type) {}
@@ -33,7 +33,10 @@ public:
void handleRequest(HttpRequest *request, HttpResponse *response);
void handleJsonRequest(QByteArray jsonText, HttpResponse *response);
void handleImageRequest(json_object* jsonRequest, HttpResponse *response);
+ void handleStaticImageRequest(json_object* jsonRequest, HttpResponse *response);
void addUpdateEvent(quintptr id, const QImage &image, QRect updateRect);
+ void addStaticUpdateEvent(quintptr id, uint hash, const QByteArray &pngImage);
+ void addStaticUpdateEvent(quintptr id);
void addGeometryEvent(quintptr id, QRect globalGeometry);
void addParentChangeEvent(quintptr id);
void addEvent(quintptr id, EventEntry::Type type);
@@ -43,17 +46,22 @@ public:
json_object *jsonShowLineEditEvent(const EventEntry &event) const;
json_object *jsonHideEvent(const EventEntry &event) const;
json_object *jsonUpdateEvent(const EventEntry &event) const;
+ json_object *jsonStaticUpdateEvent(const EventEntry &event) const;
json_object *jsonGeometryEvent(const EventEntry &event) const;
json_object *jsonParentChangeEvent(const EventEntry &event) const;
json_object *jsonTextUpdateEvent(const EventEntry &event) const;
json_object *toJsonWidgetType(QWidget *widget) const;
bool isEmpty() { return events.isEmpty(); }
-//private:
+ static QByteArray pngCompress(const QImage &image);
+ //private:
Session *m_session;
QQueue<EventEntry> events;
QHash<quintptr, QImage> images;
QHash<quintptr, QByteArray> compressedImages;
+ QHash<quintptr, QByteArray> staticCompressedImages; // id -> png data
+ QHash<uint, quintptr> staticImageHashToWidgetId; // TODO: figure out a better way to map
+ QHash<quintptr, uint> staticWidgetIdToImageHash; // widget id <-> image hash <-> image data
QHash<quintptr, QRect> geometries;
};
diff --git a/src/sessionserver.cpp b/src/sessionserver.cpp
index 9d7f0a5..0aa5b9a 100644
--- a/src/sessionserver.cpp
+++ b/src/sessionserver.cpp
@@ -1,8 +1,10 @@
-#include "webclient.h"
+#include "sessionserver.h"
+#include "widgeteventhandler.h"
+#include "eventqueue.h"
+#include "webclientserver.h"
#include <json.h>
-#include <eventqueue.h>
-#include <webclientserver.h>
+
SessionServer::SessionServer(QWidget *widget, Session *session, Server *server)
{
diff --git a/src/sessionserver.h b/src/sessionserver.h
index 7a8a635..daaffcb 100644
--- a/src/sessionserver.h
+++ b/src/sessionserver.h
@@ -1,10 +1,13 @@
-#ifndef MULTIUSER_H
-#define MULTIUSER_H
+#ifndef SESSIONSERVER_H
+#define SESSIONSERVER_H
#include <QtGui>
-#include <webclientserver.h>
-#include <widgeteventhandler.h>
+class Session;
+class Server;
+class HttpRequest;
+class HttpResponse;
+class WidgetEventHandler;
class SessionServer : public QObject
{
Q_OBJECT
diff --git a/src/webclient.cpp b/src/webclient.cpp
index fd640f0..2777e4f 100644
--- a/src/webclient.cpp
+++ b/src/webclient.cpp
@@ -1,4 +1,6 @@
#include "webclient.h"
+#include "webclientserver.h"
+#include "sessionserver.h"
WebClient::WebClient()
{
@@ -24,7 +26,7 @@ void WebClient::setPort(quint16 port)
void WebClient::setWidgetHint(QWidget *widget, WidgetHint hint)
{
- widgetHints[widget].insert(hint);
+ server->widgetHints[widget].insert(hint);
}
void WebClient::setRootWidget(QWidget *widget)
diff --git a/src/webclient.h b/src/webclient.h
index f59d90f..ccbd6c5 100644
--- a/src/webclient.h
+++ b/src/webclient.h
@@ -1,7 +1,8 @@
#ifndef QT_WEBCLIENT_H
+#define QT_WEBCLIENT_H
-#include <sessionserver.h>
-#include <webclientserver.h>
+#include <QtGui>
+#include "webclientserver.h"
#ifdef QT_WEBCLIENT_DEBUG
#define DEBUG qDebug()
@@ -9,7 +10,8 @@
#define DEBUG if (0) qDebug()
#endif
-
+class Server;
+class Session;
class WebClient : public QObject
{
Q_OBJECT
@@ -32,7 +34,6 @@ private slots:
private:
Server *server;
QWidget *globalRootWidget;
- QHash<QWidget *, QSet<WidgetHint> > widgetHints;
};
diff --git a/src/webclientserver.cpp b/src/webclientserver.cpp
index 7a614c8..44d120e 100644
--- a/src/webclientserver.cpp
+++ b/src/webclientserver.cpp
@@ -387,6 +387,11 @@ bool Server::shouldSkipUpdate(const QByteArray &className)
return skipUpdatesClasses.contains(className);
}
+bool Server::testHint(QWidget *widget, int widgetHint)
+{
+ return (widgetHints.contains(widget) && widgetHints.value(widget).contains(widgetHint));
+}
+
FileServer::FileServer()
{
allowedFileNames = QSet<QString>()
diff --git a/src/webclientserver.h b/src/webclientserver.h
index eecac64..2a08b7b 100644
--- a/src/webclientserver.h
+++ b/src/webclientserver.h
@@ -1,6 +1,8 @@
#ifndef CONTENTSERVER_H
#define CONTENTSERVER_H
+#include <webclient.h>
+
#include <QtCore>
#include <QtNetwork>
@@ -122,6 +124,9 @@ public:
bool sendUpdatesForPlainQWidgets;
bool shouldSkipUpdate(const QByteArray &className);
QSet<QByteArray> skipUpdatesClasses;
+
+ bool testHint(QWidget *widget, int widgetHint);
+ QHash<QWidget *, QSet</*WebClient::WidgetHint*/ int> > widgetHints;
};
#endif
diff --git a/src/widgeteventhandler.cpp b/src/widgeteventhandler.cpp
index f150394..3568c43 100644
--- a/src/widgeteventhandler.cpp
+++ b/src/widgeteventhandler.cpp
@@ -349,7 +349,16 @@ void WidgetEventHandler::widgetPaint(QWidget *widget, const QRect &updateRect)
return ;
}
- qDebug() << "paint" << widget << widget->rect() << "update" << updateRect;
+ // Skip the painting step for static widgets that already has been painted,
+ // but send the update event to the client.
+ if (server->testHint(widget, WebClient::StaticWidget)) {
+ if (events.staticCompressedImages.contains(idForWidget(widget))) {
+ events.addStaticUpdateEvent(idForWidget(widget));
+ return;
+ }
+ }
+
+ DEBUG << "paint" << widget << widget->rect() << "update" << updateRect;
QImage image(widget->size(), QImage::Format_ARGB32_Premultiplied);
DEBUG << "widget->size" << widget->size();
@@ -362,10 +371,23 @@ void WidgetEventHandler::widgetPaint(QWidget *widget, const QRect &updateRect)
//DEBUG << "render done";
grabbing = false;
-
+ quintptr id = idForWidget(widget);
+
+ // The visual representation of a static widget does not change. Store the images
+ // for static widgets in png compressed form. Also store the hash of the
+ // image, and send this as the identity and eTag to the client. This way
+ // the client can cache the image, even across sessions when the widget
+ // pointer (id) changes value.
+ if (server->testHint(widget, WebClient::StaticWidget)){
+ if (events.staticCompressedImages.contains(id) == false) {
+ QByteArray compressedImage = EventQueue::pngCompress(image);
+ uint hash = qHash(compressedImage);
+ events.addStaticUpdateEvent(id, hash, compressedImage);
+ }
+ } else {
// DEBUG << "update" << widget << (int)widget;
- events.addUpdateEvent(idForWidget(widget), image, updateRect);
-
+ events.addUpdateEvent(idForWidget(widget), image, updateRect);
+ }
// DEBUG << "geometry" << widget << (int)widget << globalGeometry(widget);
events.addGeometryEvent(idForWidget(widget), globalGeometry(widget));
}
diff --git a/src/widgeteventhandler.h b/src/widgeteventhandler.h
index 3327541..afd3d4c 100644
--- a/src/widgeteventhandler.h
+++ b/src/widgeteventhandler.h
@@ -3,6 +3,7 @@
#include <QtGui>
#include <eventqueue.h>
+#include <webclientserver.h>
class Server;
class WidgetEventHandler : public QObject
@@ -50,6 +51,7 @@ public: //private:
QWidget *rootWidget;
QWidget *focusWidget; // hack hack
QSet<QObject*> disableUpdates;
+ QSet<QObject *> staticWidgets;
Server *server;
};