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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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 _______________________________________________ Libreoffice-commits mailing list [email protected] https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits
