diff options
| author | Morten Sorvig <msorvig@trolltech.com> | 2009-09-04 06:04:21 +0200 |
|---|---|---|
| committer | Morten Sorvig <msorvig@trolltech.com> | 2009-09-04 06:04:21 +0200 |
| commit | 31abf9bb9055195ff27ded52ab5f700b168186da (patch) | |
| tree | 3536713e8c9f44b336c78a59e2cd9d5a96b0e70d | |
| parent | 3d93f4c7677610a23b347b5e9e8a6881e021dcd3 (diff) | |
Add support for static widgets
| -rw-r--r-- | src/eventhandler.js | 35 | ||||
| -rw-r--r-- | src/eventqueue.cpp | 69 | ||||
| -rw-r--r-- | src/eventqueue.h | 12 | ||||
| -rw-r--r-- | src/sessionserver.cpp | 8 | ||||
| -rw-r--r-- | src/sessionserver.h | 11 | ||||
| -rw-r--r-- | src/webclient.cpp | 4 | ||||
| -rw-r--r-- | src/webclient.h | 9 | ||||
| -rw-r--r-- | src/webclientserver.cpp | 5 | ||||
| -rw-r--r-- | src/webclientserver.h | 5 | ||||
| -rw-r--r-- | src/widgeteventhandler.cpp | 30 | ||||
| -rw-r--r-- | src/widgeteventhandler.h | 2 |
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; }; |
