[Libreoffice-commits] online.git: Branch 'feature/latency' - 3 commits - loleaflet/src wsd/ClientSession.cpp wsd/ClientSession.hpp wsd/DocumentBroker.cpp wsd/DocumentBroker.hpp wsd/protocol.txt wsd/TileCache.cpp wsd/TileCache.hpp

Tamás Zolnai tamas.zolnai at collabora.com
Thu Jun 7 11:14:05 UTC 2018


 loleaflet/src/layer/tile/CalcTileLayer.js    |   44 --------
 loleaflet/src/layer/tile/ImpressTileLayer.js |   41 -------
 loleaflet/src/layer/tile/TileLayer.js        |   16 +++
 loleaflet/src/layer/tile/WriterTileLayer.js  |   44 --------
 wsd/ClientSession.cpp                        |  136 +++++++++++++++++++++++++
 wsd/ClientSession.hpp                        |   32 ++++++
 wsd/DocumentBroker.cpp                       |  142 +++++++++++++++++++++------
 wsd/DocumentBroker.hpp                       |    1 
 wsd/TileCache.cpp                            |   18 ++-
 wsd/TileCache.hpp                            |    4 
 wsd/protocol.txt                             |    5 
 11 files changed, 316 insertions(+), 167 deletions(-)

New commits:
commit 5eb6ff875c7507c2f87d10d23d120804547260e3
Author: Tamás Zolnai <tamas.zolnai at collabora.com>
Date:   Thu Jun 7 13:13:36 2018 +0200

    Wait tileprocessed message from client to send new bunch of tiles
    
    We always  one bunch of tiles (e.g. all tiles invalidated) and we
    are waiting until client send tileprocessed message back for all
    tiles before sending the new tiles.
    By canceltiles message we drop every previously requested tiles and
    make wsd ready to send new tiles, which will be requested by the client
    in theory.
    
    Change-Id: I9901420ada549e962ffaf5e6bd58e52b86bd129d

diff --git a/loleaflet/src/layer/tile/TileLayer.js b/loleaflet/src/layer/tile/TileLayer.js
index c1bd9ca75..0ad81644c 100644
--- a/loleaflet/src/layer/tile/TileLayer.js
+++ b/loleaflet/src/layer/tile/TileLayer.js
@@ -1428,6 +1428,9 @@ L.TileLayer = L.GridLayer.extend({
 			tile.el.src = img;
 		}
 		L.Log.log(textMsg, L.INCOMING, key);
+
+		// Send acknowledgment, that the tile message arrived
+		this._map._socket.sendMessage('tileprocessed tile= ' + key);
 	},
 
 	_tileOnLoad: function (done, tile) {
diff --git a/wsd/ClientSession.cpp b/wsd/ClientSession.cpp
index 4b51a2770..b5bd45c2b 100644
--- a/wsd/ClientSession.cpp
+++ b/wsd/ClientSession.cpp
@@ -47,7 +47,8 @@ ClientSession::ClientSession(const std::string& id,
     _tileWidthPixel(0),
     _tileHeightPixel(0),
     _tileWidthTwips(0),
-    _tileHeightTwips(0)
+    _tileHeightTwips(0),
+    _tilesOnFly(0)
 {
     const size_t curConnections = ++LOOLWSD::NumConnections;
     LOG_INF("ClientSession ctor [" << getName() << "], current number of connections: " << curConnections);
@@ -130,6 +131,7 @@ bool ClientSession::_handleInput(const char *buffer, int length)
         return loadDocument(buffer, length, tokens, docBroker);
     }
     else if (tokens[0] != "canceltiles" &&
+             tokens[0] != "tileprocessed" &&
              tokens[0] != "clientzoom" &&
              tokens[0] != "clientvisiblearea" &&
              tokens[0] != "outlinestate" &&
@@ -321,6 +323,13 @@ bool ClientSession::_handleInput(const char *buffer, int length)
             return forwardToChild(std::string(buffer, length), docBroker);
         }
     }
+    else if (tokens[0] == "tileprocessed")
+    {
+        if(_tilesOnFly > 0) // canceltiles message can zero this value
+            --_tilesOnFly;
+        docBroker->sendRequestedTiles(shared_from_this());
+        return true;
+    }
     else
     {
         if (tokens[0] == "key")
diff --git a/wsd/ClientSession.hpp b/wsd/ClientSession.hpp
index 24f56b00e..824ee80b2 100644
--- a/wsd/ClientSession.hpp
+++ b/wsd/ClientSession.hpp
@@ -17,9 +17,11 @@
 #include "DocumentBroker.hpp"
 #include <Poco/URI.h>
 #include <Rectangle.hpp>
+#include <boost/optional.hpp>
 
 class DocumentBroker;
 
+
 /// Represents a session to a LOOL client, in the WSD process.
 class ClientSession final : public Session, public std::enable_shared_from_this<ClientSession>
 {
@@ -105,6 +107,12 @@ public:
     /// Set WOPI fileinfo object
     void setWopiFileInfo(std::unique_ptr<WopiStorage::WOPIFileInfo>& wopiFileInfo) { _wopiFileInfo = std::move(wopiFileInfo); }
 
+    boost::optional<TileCombined>& getRequestedTiles() { return _requestedTiles; }
+
+    int getTilesOnFly() const { return _tilesOnFly; }
+    void setTilesOnFly(int tilesOnFly) { _tilesOnFly = tilesOnFly; }
+
+
 private:
 
     /// SocketHandler: disconnection event.
@@ -185,8 +193,13 @@ private:
 
     // Type of the docuemnt, extracter from status message
     std::string _docType;
+
+    int _tilesOnFly;
+
+    boost::optional<TileCombined> _requestedTiles;
 };
 
+
 #endif
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/wsd/DocumentBroker.cpp b/wsd/DocumentBroker.cpp
index 1006dc2dc..3058a4ce6 100644
--- a/wsd/DocumentBroker.cpp
+++ b/wsd/DocumentBroker.cpp
@@ -1288,62 +1288,144 @@ void DocumentBroker::handleTileCombinedRequest(TileCombined& tileCombined,
     std::unique_lock<std::mutex> lock(_mutex);
 
     LOG_TRC("TileCombined request for " << tileCombined.serialize());
+    std::cerr << "handleTileCombinedRequest: " << std::endl;
 
-    // Satisfy as many tiles from the cache.
-    std::vector<TileDesc> tiles;
+    // Check which newly requested tiles needs rendering.
+    std::vector<TileDesc> tilesNeedsRendering;
     for (auto& tile : tileCombined.getTiles())
     {
         std::unique_ptr<std::fstream> cachedTile = _tileCache->lookupTile(tile);
-        if (cachedTile)
-        {
-            //TODO: Combine the response to reduce latency.
-#if ENABLE_DEBUG
-            const std::string response = tile.serialize("tile:") + " renderid=cached\n";
-#else
-            const std::string response = tile.serialize("tile:") + "\n";
-#endif
-
-            std::vector<char> output;
-            output.reserve(static_cast<size_t>(4) * tile.getWidth() * tile.getHeight());
-            output.resize(response.size());
-            std::memcpy(output.data(), response.data(), response.size());
-
-            assert(cachedTile->is_open());
-            cachedTile->seekg(0, std::ios_base::end);
-            const size_t pos = output.size();
-            std::streamsize size = cachedTile->tellg();
-            output.resize(pos + size);
-            cachedTile->seekg(0, std::ios_base::beg);
-            cachedTile->read(output.data() + pos, size);
+        if(cachedTile)
             cachedTile->close();
-
-            session->sendBinaryFrame(output.data(), output.size());
-        }
         else
         {
             // Not cached, needs rendering.
             tile.setVersion(++_tileVersion);
-            tileCache().subscribeToTileRendering(tile, session);
-            tiles.push_back(tile);
+            tilesNeedsRendering.push_back(tile);
             _debugRenderedTileCount++;
         }
     }
 
-    if (!tiles.empty())
+    // Send rendering request
+    if (!tilesNeedsRendering.empty())
     {
-        TileCombined newTileCombined = TileCombined::create(tiles);
+        std::cerr << "tilesNeedsRendering.size(): " << tilesNeedsRendering.size() << std::endl;
+        TileCombined newTileCombined = TileCombined::create(tilesNeedsRendering);
 
         // Forward to child to render.
         const std::string req = newTileCombined.serialize("tilecombine");
         LOG_DBG("Sending residual tilecombine: " << req);
         _childProcess->sendTextFrame(req);
     }
+
+    // Accumulate tiles
+    boost::optional<TileCombined>& requestedTiles = session->getRequestedTiles();
+    if(requestedTiles == boost::none)
+    {
+        requestedTiles = TileCombined::create(tileCombined.getTiles());
+    }
+    // Drop duplicated tiles, but use newer version number
+    else
+    {
+        for (const auto& newTile : tileCombined.getTiles())
+        {
+            const TileDesc& firstOldTile = requestedTiles.get().getTiles()[0];
+            if(newTile.getPart() != firstOldTile.getPart() ||
+               newTile.getWidth() != firstOldTile.getWidth() ||
+               newTile.getHeight() != firstOldTile.getHeight() ||
+               newTile.getTileWidth() != firstOldTile.getTileWidth() ||
+               newTile.getTileHeight() != firstOldTile.getTileHeight() )
+            {
+                LOG_WRN("Different visible area information in tile requests");
+            }
+
+            bool tileFound = false;
+            for (auto& oldTile : requestedTiles.get().getTiles())
+            {
+                if(oldTile.getTilePosX() == newTile.getTilePosX() &&
+                   oldTile.getTilePosY() == newTile.getTilePosY() )
+                {
+                    oldTile.setVersion(newTile.getVersion());
+                    oldTile.setOldWireId(newTile.getOldWireId());
+                    oldTile.setWireId(newTile.getWireId());
+                    tileFound = true;
+                    break;
+                }
+            }
+            if(!tileFound)
+                requestedTiles.get().getTiles().push_back(newTile);
+        }
+    }
+
+    lock.unlock();
+    lock.release();
+    sendRequestedTiles(session);
+}
+
+void DocumentBroker::sendRequestedTiles(const std::shared_ptr<ClientSession>& session)
+{
+    assert(session->getTilesOnFly() >= 0);
+    std::unique_lock<std::mutex> lock(_mutex);
+
+    // All tiles were processed on client side what we sent last time, so we can send a new banch of tiles
+    // which was invalidated / requested in the meantime
+    boost::optional<TileCombined>& requestedTiles = session->getRequestedTiles();
+    if(session->getTilesOnFly() == 0 && requestedTiles != boost::none && !requestedTiles.get().getTiles().empty())
+    {
+        session->setTilesOnFly(requestedTiles.get().getTiles().size());
+
+        // Satisfy as many tiles from the cache.
+    std::cerr << "requestedTiles.size(): " << requestedTiles.get().getTiles().size() << std::endl;
+        for (auto& tile : requestedTiles.get().getTiles())
+        {
+        std::cerr << "alma: "<< std::endl;
+            std::unique_ptr<std::fstream> cachedTile = _tileCache->lookupTile(tile);
+            if (cachedTile)
+            {
+        std::cerr << "cachedTile: "<< std::endl;
+                //TODO: Combine the response to reduce latency.
+#if ENABLE_DEBUG
+                const std::string response = tile.serialize("tile:") + " renderid=cached\n";
+#else
+                const std::string response = tile.serialize("tile:") + "\n";
+#endif
+
+                std::vector<char> output;
+                output.reserve(static_cast<size_t>(4) * tile.getWidth() * tile.getHeight());
+                output.resize(response.size());
+                std::memcpy(output.data(), response.data(), response.size());
+
+                assert(cachedTile->is_open());
+                cachedTile->seekg(0, std::ios_base::end);
+                const auto pos = output.size();
+                std::streamsize size = cachedTile->tellg();
+                output.resize(pos + size);
+                cachedTile->seekg(0, std::ios_base::beg);
+                cachedTile->read(output.data() + pos, size);
+                cachedTile->close();
+
+                session->sendBinaryFrame(output.data(), output.size());
+            }
+            else
+            {
+        std::cerr << "not cachedTile: "<< std::endl;
+                // Not cached, needs rendering. Rendering request was already sent
+                tileCache().subscribeToTileRendering(tile, session);
+        std::cerr << "not cachedTile2: "<< std::endl;
+            }
+        }
+        requestedTiles = boost::none;
+    }
 }
 
 void DocumentBroker::cancelTileRequests(const std::shared_ptr<ClientSession>& session)
 {
     std::unique_lock<std::mutex> lock(_mutex);
 
+    // Clear tile requests
+    session->setTilesOnFly(0);
+    session->getRequestedTiles() = boost::none;
+
     const std::string canceltiles = tileCache().cancelTiles(session);
     if (!canceltiles.empty())
     {
diff --git a/wsd/DocumentBroker.hpp b/wsd/DocumentBroker.hpp
index fc3cb095d..f683c9e18 100644
--- a/wsd/DocumentBroker.hpp
+++ b/wsd/DocumentBroker.hpp
@@ -301,6 +301,7 @@ public:
     void handleDialogRequest(const std::string& dialogCmd);
     void handleTileCombinedRequest(TileCombined& tileCombined,
                                    const std::shared_ptr<ClientSession>& session);
+    void sendRequestedTiles(const std::shared_ptr<ClientSession>& session);
     void cancelTileRequests(const std::shared_ptr<ClientSession>& session);
     void handleTileResponse(const std::vector<char>& payload);
     void handleDialogPaintResponse(const std::vector<char>& payload, bool child);
diff --git a/wsd/protocol.txt b/wsd/protocol.txt
index 1697a8de4..c7a126670 100644
--- a/wsd/protocol.txt
+++ b/wsd/protocol.txt
@@ -31,6 +31,11 @@ canceltiles
     parameter. There is no guarantee of exactly which tile: messages
     might still be sent back to the client.
 
+tileprocessed tile=<tileid>
+
+    Previously sent tile (server -> client) arrived and processed by the client.
+    Tileid has the next stucture : <tile x coord>:<tile y coord>:<zoom level>:<selected part>
+
 downloadas name=<fileName> id=<id> format=<document format> options=<SkipImages, etc>
 
     Exports the current document to the desired format and returns a download URL
commit c34f228e7e352aad1fe483ee54b96840eea7edf9
Author: Tamás Zolnai <tamas.zolnai at collabora.com>
Date:   Thu May 31 20:20:09 2018 +0200

    Request new tiles in wsd by invalidateTiles message
    
    And don't wait for the client to send back a tilecombine
    request.
    
    Change-Id: I9ea5de0f6b12dfaaf61992d34735d5b78ea382ed

diff --git a/loleaflet/src/layer/tile/CalcTileLayer.js b/loleaflet/src/layer/tile/CalcTileLayer.js
index a53a6e34a..3e0bf2dfa 100644
--- a/loleaflet/src/layer/tile/CalcTileLayer.js
+++ b/loleaflet/src/layer/tile/CalcTileLayer.js
@@ -242,9 +242,6 @@ L.CalcTileLayer = L.TileLayer.extend({
 			command.height = parseInt(strTwips[3]);
 			command.part = this._selectedPart;
 		}
-		if (this._docType === 'text') {
-			command.part = 0;
-		}
 		var topLeftTwips = new L.Point(command.x, command.y);
 		var offset = new L.Point(command.width, command.height);
 		var bottomRightTwips = topLeftTwips.add(offset);
@@ -256,11 +253,6 @@ L.CalcTileLayer = L.TileLayer.extend({
 		var visibleBottomRight = this._latLngToTwips(this._map.getBounds().getSouthEast());
 		var visibleArea = new L.Bounds(visibleTopLeft, visibleBottomRight);
 
-		var tilePositionsX = '';
-		var tilePositionsY = '';
-		var oldWireIds = '';
-		var needsNewTiles = false;
-
 		for (var key in this._tiles) {
 			var coords = this._tiles[key].coords;
 			var tileTopLeft = this._coordsToTwips(coords);
@@ -274,24 +266,6 @@ L.CalcTileLayer = L.TileLayer.extend({
 					this._tiles[key]._invalidCount = 1;
 				}
 				if (visibleArea.intersects(bounds)) {
-					if (tilePositionsX !== '') {
-						tilePositionsX += ',';
-					}
-					tilePositionsX += tileTopLeft.x;
-					if (tilePositionsY !== '') {
-						tilePositionsY += ',';
-					}
-					tilePositionsY += tileTopLeft.y;
-					if (oldWireIds !== '') {
-						oldWireIds += ',';
-					}
-					if (this._tiles[key].oldWireId === undefined) {
-						oldWireIds += '0';
-					}
-					else {
-						oldWireIds += this._tiles[key].oldWireId;
-					}
-					needsNewTiles = true;
 					if (this._debug) {
 						this._debugAddInvalidationData(this._tiles[key]);
 					}
@@ -304,24 +278,6 @@ L.CalcTileLayer = L.TileLayer.extend({
 			}
 		}
 
-		if (needsNewTiles && command.part === this._selectedPart)
-		{
-			var message = 'tilecombine ' +
-				'part=' + command.part + ' ' +
-				'width=' + this._tileWidthPx + ' ' +
-				'height=' + this._tileHeightPx + ' ' +
-				'tileposx=' + tilePositionsX + ' ' +
-				'tileposy=' + tilePositionsY + ' ' +
-				'tilewidth=' + this._tileWidthTwips + ' ' +
-				'tileheight=' + this._tileHeightTwips + ' ' +
-				'oldwid=' + oldWireIds;
-
-			this._map._socket.sendMessage(message, '');
-			if (this._debug) {
-				this._debugAddInvalidationMessage(message);
-			}
-		}
-
 		for (key in this._tileCache) {
 			// compute the rectangle that each tile covers in the document based
 			// on the zoom level
diff --git a/loleaflet/src/layer/tile/ImpressTileLayer.js b/loleaflet/src/layer/tile/ImpressTileLayer.js
index abad6a799..83c78ccfb 100644
--- a/loleaflet/src/layer/tile/ImpressTileLayer.js
+++ b/loleaflet/src/layer/tile/ImpressTileLayer.js
@@ -366,11 +366,6 @@ L.ImpressTileLayer = L.TileLayer.extend({
 		var visibleBottomRight = this._latLngToTwips(this._map.getBounds().getSouthEast());
 		var visibleArea = new L.Bounds(visibleTopLeft, visibleBottomRight);
 
-		var tilePositionsX = '';
-		var tilePositionsY = '';
-		var oldWireIds = '';
-		var needsNewTiles = false;
-
 		for (var key in this._tiles) {
 			var coords = this._tiles[key].coords;
 			var tileTopLeft = this._coordsToTwips(coords);
@@ -384,24 +379,6 @@ L.ImpressTileLayer = L.TileLayer.extend({
 					this._tiles[key]._invalidCount = 1;
 				}
 				if (visibleArea.intersects(bounds)) {
-					if (tilePositionsX !== '') {
-						tilePositionsX += ',';
-					}
-					tilePositionsX += tileTopLeft.x;
-					if (tilePositionsY !== '') {
-						tilePositionsY += ',';
-					}
-					tilePositionsY += tileTopLeft.y;
-					if (oldWireIds !== '') {
-						oldWireIds += ',';
-					}
-					if (this._tiles[key].oldWireId === undefined) {
-						oldWireIds += '0';
-					}
-					else {
-						oldWireIds += this._tiles[key].oldWireId;
-					}
-					needsNewTiles = true;
 					if (this._debug) {
 						this._debugAddInvalidationData(this._tiles[key]);
 					}
@@ -414,24 +391,6 @@ L.ImpressTileLayer = L.TileLayer.extend({
 			}
 		}
 
-		if (needsNewTiles && command.part === this._selectedPart)
-		{
-			var message = 'tilecombine ' +
-				'part=' + command.part + ' ' +
-				'width=' + this._tileWidthPx + ' ' +
-				'height=' + this._tileHeightPx + ' ' +
-				'tileposx=' + tilePositionsX + ' ' +
-				'tileposy=' + tilePositionsY + ' ' +
-				'tilewidth=' + this._tileWidthTwips + ' ' +
-				'tileheight=' + this._tileHeightTwips + ' ' +
-				'oldwid=' + oldWireIds;
-
-			this._map._socket.sendMessage(message, '');
-			if (this._debug) {
-				this._debugAddInvalidationMessage(message);
-			}
-		}
-
 		for (key in this._tileCache) {
 			// compute the rectangle that each tile covers in the document based
 			// on the zoom level
diff --git a/loleaflet/src/layer/tile/WriterTileLayer.js b/loleaflet/src/layer/tile/WriterTileLayer.js
index 6c1a886b8..8c9f46e81 100644
--- a/loleaflet/src/layer/tile/WriterTileLayer.js
+++ b/loleaflet/src/layer/tile/WriterTileLayer.js
@@ -109,10 +109,6 @@ L.WriterTileLayer = L.TileLayer.extend({
 		var visibleTopLeft = this._latLngToTwips(this._map.getBounds().getNorthWest());
 		var visibleBottomRight = this._latLngToTwips(this._map.getBounds().getSouthEast());
 		var visibleArea = new L.Bounds(visibleTopLeft, visibleBottomRight);
-		var tilePositionsX = '';
-		var tilePositionsY = '';
-		var oldWireIds = '';
-		var needsNewTiles = false;
 		for (var key in this._tiles) {
 			var coords = this._tiles[key].coords;
 			var tileTopLeft = this._coordsToTwips(coords);
@@ -126,24 +122,6 @@ L.WriterTileLayer = L.TileLayer.extend({
 					this._tiles[key]._invalidCount = 1;
 				}
 				if (visibleArea.intersects(bounds)) {
-					if (tilePositionsX !== '') {
-						tilePositionsX += ',';
-					}
-					tilePositionsX += tileTopLeft.x;
-					if (tilePositionsY !== '') {
-						tilePositionsY += ',';
-					}
-					tilePositionsY += tileTopLeft.y;
-					if (oldWireIds !== '') {
-						oldWireIds += ',';
-					}
-					if (this._tiles[key].oldWireId === undefined) {
-						oldWireIds += '0';
-					}
-					else {
-						oldWireIds += this._tiles[key].oldWireId;
-					}
-					needsNewTiles = true;
 					if (this._debug) {
 						this._debugAddInvalidationData(this._tiles[key]);
 					}
@@ -156,28 +134,6 @@ L.WriterTileLayer = L.TileLayer.extend({
 			}
 		}
 
-		if (needsNewTiles)
-		{
-			// CalcTileLayer.js and ImpressTileLayer.js avoid this when
-			// command.part !== this._selectedPart; but in Writer, the part is
-			// always 0 anyway
-			var message = 'tilecombine ' +
-				'part=' + command.part + ' ' +
-				'width=' + this._tileWidthPx + ' ' +
-				'height=' + this._tileHeightPx + ' ' +
-				'tileposx=' + tilePositionsX + ' ' +
-				'tileposy=' + tilePositionsY + ' ' +
-				'tilewidth=' + this._tileWidthTwips + ' ' +
-				'tileheight=' + this._tileHeightTwips + ' ' +
-				'oldwid=' + oldWireIds;
-
-			this._map._socket.sendMessage(message, '');
-
-			if (this._debug) {
-				this._debugAddInvalidationMessage(message);
-			}
-		}
-
 		for (key in this._tileCache) {
 			// compute the rectangle that each tile covers in the document based
 			// on the zoom level
diff --git a/wsd/ClientSession.cpp b/wsd/ClientSession.cpp
index 136523c62..4b51a2770 100644
--- a/wsd/ClientSession.cpp
+++ b/wsd/ClientSession.cpp
@@ -822,6 +822,13 @@ bool ClientSession::handleKitToClientMessage(const char* buffer, const int lengt
             setViewLoaded();
             docBroker->setLoaded();
 
+            const std::string stringMsg(buffer, length);
+            const size_t index = stringMsg.find("type=") + 5;
+            if (index != std::string::npos)
+            {
+                _docType = stringMsg.substr(index, stringMsg.find_first_of(' ', index) - index);
+            }
+
             // Forward the status response to the client.
             return forwardToClient(payload);
         }
@@ -847,7 +854,7 @@ bool ClientSession::handleKitToClientMessage(const char* buffer, const int lengt
         else if (tokens[0] == "invalidatetiles:")
         {
             assert(firstLine.size() == static_cast<std::string::size_type>(length));
-            docBroker->invalidateTiles(firstLine);
+            handleTileInvalidation(firstLine, docBroker);
         }
         else if (tokens[0] == "invalidatecursor:")
         {
@@ -1025,4 +1032,49 @@ void ClientSession::dumpState(std::ostream& os)
 
 }
 
+void ClientSession::handleTileInvalidation(const std::string& message,
+    const std::shared_ptr<DocumentBroker>& docBroker)
+{
+    docBroker->invalidateTiles(message);
+    std::pair<int, Util::Rectangle> result = TileCache::parseInvalidateMsg(message);
+    int part = result.first;
+    Util::Rectangle& invalidateRect = result.second;
+
+    if(_docType.find("text") != std::string::npos) // For Writer we don't have separate parts
+        part = 0;
+
+    if(part == -1) // If no part is specifed we use the part used by the client
+        part = _clientSelectedPart;
+
+    std::vector<TileDesc> invalidTiles;
+    if(part == _clientSelectedPart)
+    {
+        Util::Rectangle intersection;
+        intersection._x1 = std::max(invalidateRect._x1, _clientVisibleArea._x1);
+        intersection._y1 = std::max(invalidateRect._y1, _clientVisibleArea._y1);
+        intersection._x2 = std::min(invalidateRect._x2, _clientVisibleArea._x2);
+        intersection._y2 = std::min(invalidateRect._y2, _clientVisibleArea._y2);
+        if(intersection.isValid()) // Client visible area and invalidated rectangle has intersection
+        {
+            for(int i = std::ceil(intersection._x1 / _tileWidthTwips);
+                i <= std::ceil(intersection._x2 / _tileWidthTwips); ++i)
+            {
+                for(int j = std::ceil(intersection._y1 / _tileHeightTwips);
+                    j <= std::ceil(intersection._y2 / _tileHeightTwips); ++j)
+                {
+                    invalidTiles.emplace_back(TileDesc(part, _tileWidthPixel, _tileHeightPixel, i * _tileWidthTwips, j * _tileHeightTwips, _tileWidthTwips, _tileHeightTwips, -1, 0, -1, false));
+                    invalidTiles.back().setOldWireId(0);
+                    invalidTiles.back().setWireId(0);
+                }
+            }
+        }
+    }
+
+    if(!invalidTiles.empty())
+    {
+        TileCombined tileCombined = TileCombined::create(invalidTiles);
+        docBroker->handleTileCombinedRequest(tileCombined, shared_from_this());
+    }
+}
+
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/wsd/ClientSession.hpp b/wsd/ClientSession.hpp
index 6bc480763..24f56b00e 100644
--- a/wsd/ClientSession.hpp
+++ b/wsd/ClientSession.hpp
@@ -142,6 +142,9 @@ private:
 
     void dumpState(std::ostream& os) override;
 
+    void handleTileInvalidation(const std::string& message,
+                                const std::shared_ptr<DocumentBroker>& docBroker);
+
 private:
     std::weak_ptr<DocumentBroker> _docBroker;
 
@@ -179,6 +182,9 @@ private:
     int _tileHeightPixel;
     int _tileWidthTwips;
     int _tileHeightTwips;
+
+    // Type of the docuemnt, extracter from status message
+    std::string _docType;
 };
 
 #endif
diff --git a/wsd/TileCache.cpp b/wsd/TileCache.cpp
index 51f1933de..ef697933a 100644
--- a/wsd/TileCache.cpp
+++ b/wsd/TileCache.cpp
@@ -339,22 +339,27 @@ void TileCache::invalidateTiles(int part, int x, int y, int width, int height)
 
 void TileCache::invalidateTiles(const std::string& tiles)
 {
+    std::pair<int, Util::Rectangle> result = TileCache::parseInvalidateMsg(tiles);
+    Util::Rectangle& invalidateRect = result.second;
+    invalidateTiles(result.first, invalidateRect.getLeft(), invalidateRect.getTop(), invalidateRect.getWidth(), invalidateRect.getHeight());
+}
+
+std::pair<int, Util::Rectangle> TileCache::parseInvalidateMsg(const std::string& tiles)
+{
     StringTokenizer tokens(tiles, " ", StringTokenizer::TOK_IGNORE_EMPTY | StringTokenizer::TOK_TRIM);
 
     assert(tokens[0] == "invalidatetiles:");
 
     if (tokens.count() == 2 && tokens[1] == "EMPTY")
     {
-        invalidateTiles(-1, 0, 0, INT_MAX, INT_MAX);
-        return;
+        return std::pair<int, Util::Rectangle>(-1, Util::Rectangle(0, 0, INT_MAX, INT_MAX));
     }
     else if (tokens.count() == 3 && tokens[1] == "EMPTY,")
     {
         int part = 0;
         if (stringToInteger(tokens[2], part))
         {
-            invalidateTiles(part, 0, 0, INT_MAX, INT_MAX);
-            return;
+            return std::pair<int, Util::Rectangle>(part, Util::Rectangle(0, 0, INT_MAX, INT_MAX));
         }
     }
     else
@@ -367,12 +372,13 @@ void TileCache::invalidateTiles(const std::string& tiles)
             getTokenInteger(tokens[4], "width", width) &&
             getTokenInteger(tokens[5], "height", height))
         {
-            invalidateTiles(part, x, y, width, height);
-            return;
+
+            return std::pair<int, Util::Rectangle>(part, Util::Rectangle(x, y, width, height));
         }
     }
 
     LOG_ERR("Unexpected invalidatetiles request [" << tiles << "].");
+    return std::pair<int, Util::Rectangle>(-1, Util::Rectangle(0, 0, 0, 0));
 }
 
 void TileCache::removeFile(const std::string& fileName)
diff --git a/wsd/TileCache.hpp b/wsd/TileCache.hpp
index 5f8e71cfe..d8c48eaa4 100644
--- a/wsd/TileCache.hpp
+++ b/wsd/TileCache.hpp
@@ -17,6 +17,7 @@
 #include <string>
 
 #include <Poco/Timestamp.h>
+#include <Rectangle.hpp>
 
 #include "TileDesc.hpp"
 
@@ -72,6 +73,9 @@ public:
     // The tiles parameter is an invalidatetiles: message as sent by the child process
     void invalidateTiles(const std::string& tiles);
 
+    /// Parse invalidateTiles message to a part number and a rectangle of the invalidated area
+    static std::pair<int, Util::Rectangle> parseInvalidateMsg(const std::string& tiles);
+
     /// Store the timestamp to modtime.txt.
     void saveLastModified(const Poco::Timestamp& timestamp);
 
commit a837a26eb8a154ef03f1f52d6b1e82a8f198a73c
Author: Tamás Zolnai <tamas.zolnai at collabora.com>
Date:   Wed May 30 19:35:13 2018 +0200

    Store client's visible are information in wsd
    
    Change-Id: Iec3c146181b7db2e76247d5775076e6ac90eed2c

diff --git a/loleaflet/src/layer/tile/TileLayer.js b/loleaflet/src/layer/tile/TileLayer.js
index 4b04d1ebf..c1bd9ca75 100644
--- a/loleaflet/src/layer/tile/TileLayer.js
+++ b/loleaflet/src/layer/tile/TileLayer.js
@@ -1473,10 +1473,23 @@ L.TileLayer = L.GridLayer.extend({
 			this._map._socket.sendMessage('clientzoom ' + this._clientZoom);
 			this._clientZoom = null;
 		}
+
+		if (this._clientVisibleArea) {
+			// Visible area is dirty, update it on the server.
+			var visibleArea = this._map._container.getBoundingClientRect();
+			var pos = this._pixelsToTwips(new L.Point(visibleArea.left, visibleArea.top));
+			var size = this._pixelsToTwips(new L.Point(visibleArea.width, visibleArea.height));
+			var payload = 'clientvisiblearea x=' + Math.round(pos.x) + ' y=' + Math.round(pos.y) +
+				' width=' + Math.round(size.x) + ' height=' + Math.round(size.y);
+			this._map._socket.sendMessage(payload);
+			this._clientVisibleArea = false;
+		}
+
 		this._map._socket.sendMessage('mouse type=' + type +
 				' x=' + x + ' y=' + y + ' count=' + count +
 				' buttons=' + buttons + ' modifier=' + modifier);
 
+
 		if (type === 'buttondown') {
 			this._clearSearchResults();
 		}
diff --git a/wsd/ClientSession.cpp b/wsd/ClientSession.cpp
index 00833bd05..136523c62 100644
--- a/wsd/ClientSession.cpp
+++ b/wsd/ClientSession.cpp
@@ -41,7 +41,13 @@ ClientSession::ClientSession(const std::string& id,
     _isDocumentOwner(false),
     _isAttached(false),
     _isViewLoaded(false),
-    _keyEvents(1)
+    _keyEvents(1),
+    _clientVisibleArea(0, 0, 0, 0),
+    _clientSelectedPart(0),
+    _tileWidthPixel(0),
+    _tileHeightPixel(0),
+    _tileWidthTwips(0),
+    _tileHeightTwips(0)
 {
     const size_t curConnections = ++LOOLWSD::NumConnections;
     LOG_INF("ClientSession ctor [" << getName() << "], current number of connections: " << curConnections);
@@ -258,6 +264,63 @@ bool ClientSession::_handleInput(const char *buffer, int length)
             docBroker->broadcastMessage("commandresult: { \"command\": \"savetostorage\", \"success\": true }");
         }
     }
+    else if (tokens[0] == "clientvisiblearea")
+    {
+        int x;
+        int y;
+        int width;
+        int height;
+        if (tokens.size() != 5 ||
+            !getTokenInteger(tokens[1], "x", x) ||
+            !getTokenInteger(tokens[2], "y", y) ||
+            !getTokenInteger(tokens[3], "width", width) ||
+            !getTokenInteger(tokens[4], "height", height))
+        {
+            sendTextFrame("error: cmd=clientvisiblearea kind=syntax");
+            return false;
+        }
+        else
+        {
+            _clientVisibleArea = Util::Rectangle(x, y, width, height);
+            return forwardToChild(std::string(buffer, length), docBroker);
+        }
+    }
+    else if (tokens[0] == "setclientpart")
+    {
+        int temp;
+        if (tokens.size() != 2 ||
+            !getTokenInteger(tokens[1], "part", temp))
+        {
+            sendTextFrame("error: cmd=setclientpart kind=syntax");
+            return false;
+        }
+        else
+        {
+            _clientSelectedPart = temp;
+            return forwardToChild(std::string(buffer, length), docBroker);
+        }
+    }
+    else if (tokens[0] == "clientzoom")
+    {
+        int tilePixelWidth, tilePixelHeight, tileTwipWidth, tileTwipHeight;
+        if (tokens.size() != 5 ||
+            !getTokenInteger(tokens[1], "tilepixelwidth", tilePixelWidth) ||
+            !getTokenInteger(tokens[2], "tilepixelheight", tilePixelHeight) ||
+            !getTokenInteger(tokens[3], "tiletwipwidth", tileTwipWidth) ||
+            !getTokenInteger(tokens[4], "tiletwipheight", tileTwipHeight))
+        {
+            sendTextFrame("error: cmd=clientzoom kind=syntax");
+            return false;
+        }
+        else
+        {
+            _tileWidthPixel = tilePixelWidth;
+            _tileHeightPixel = tilePixelHeight;
+            _tileWidthTwips = tileTwipWidth;
+            _tileHeightTwips = tileTwipHeight;
+            return forwardToChild(std::string(buffer, length), docBroker);
+        }
+    }
     else
     {
         if (tokens[0] == "key")
@@ -635,6 +698,14 @@ bool ClientSession::handleKitToClientMessage(const char* buffer, const int lengt
         int curPart;
         return getTokenInteger(tokens[1], "part", curPart);
     }
+    else if (tokens[0] == "setpart:" && tokens.size() == 2)
+    {
+        int setPart;
+        if(getTokenInteger(tokens[1], "part", setPart))
+            _clientSelectedPart = setPart;
+        else
+            return false;
+    }
     else if (tokens.size() == 3 && tokens[0] == "saveas:")
     {
         bool isConvertTo = static_cast<bool>(_saveAsSocket);
diff --git a/wsd/ClientSession.hpp b/wsd/ClientSession.hpp
index 2366ca78b..6bc480763 100644
--- a/wsd/ClientSession.hpp
+++ b/wsd/ClientSession.hpp
@@ -16,6 +16,7 @@
 #include "SenderQueue.hpp"
 #include "DocumentBroker.hpp"
 #include <Poco/URI.h>
+#include <Rectangle.hpp>
 
 class DocumentBroker;
 
@@ -166,6 +167,18 @@ private:
     uint64_t _keyEvents;
 
     SenderQueue<std::shared_ptr<Message>> _senderQueue;
+
+    /// Visible area of the client
+    Util::Rectangle _clientVisibleArea;
+
+    /// Selected part of the document viewed by the client (no parts in Writer)
+    int _clientSelectedPart;
+
+    /// Zoom properties of the client
+    int _tileWidthPixel;
+    int _tileHeightPixel;
+    int _tileWidthTwips;
+    int _tileHeightTwips;
 };
 
 #endif


More information about the Libreoffice-commits mailing list