[Libreoffice-commits] online.git: 3 commits - loolwsd/LOOLSession.cpp loolwsd/LOOLSession.hpp loolwsd/protocol.txt loolwsd/TileCache.cpp loolwsd/TileCache.hpp
Tor Lillqvist
tml at collabora.com
Thu May 28 08:49:25 PDT 2015
loolwsd/LOOLSession.cpp | 64 ++++++++++++++++++++++++++++++-------
loolwsd/LOOLSession.hpp | 6 ++-
loolwsd/TileCache.cpp | 53 ++++++++++++++++++++++++++++++
loolwsd/TileCache.hpp | 6 +++
loolwsd/protocol.txt | 83 +++++++++++++++++++++++++++---------------------
5 files changed, 164 insertions(+), 48 deletions(-)
New commits:
commit bf985624f1cdf5f6299c2b64e1805edb79f719a1
Author: Tor Lillqvist <tml at collabora.com>
Date: Thu May 28 18:39:05 2015 +0300
Handle LOK_CALLBACK_INVALIDATE_TILES in the server too
Remove any intersecting cached tiles. It is the parent process that handles
the tile cache, so it must look for invalidatetiles: messages, too, before
passing them on to the client. To know for which part we should remove tiles,
add an "ephemeral" curpart: message that the child process sends to the parent
process before the invalidatetils: message.
diff --git a/loolwsd/LOOLSession.cpp b/loolwsd/LOOLSession.cpp
index 9bbca5b..a98a577 100644
--- a/loolwsd/LOOLSession.cpp
+++ b/loolwsd/LOOLSession.cpp
@@ -106,7 +106,8 @@ std::mutex MasterProcessSession::_rngMutex;
MasterProcessSession::MasterProcessSession(std::shared_ptr<WebSocket> ws, Kind kind) :
LOOLSession(ws, kind),
- _childId(0)
+ _childId(0),
+ _curPart(0)
{
std::cout << Util::logPrefix() << "MasterProcessSession ctor this=" << this << " ws=" << _ws.get() << std::endl;
}
@@ -134,8 +135,19 @@ bool MasterProcessSession::handleInput(char *buffer, int length)
// Note that this handles both forwarding requests from the client to the child process, and
// forwarding replies from the child process to the client. Or does it?
- // Snoop at tile: and status: messages and (re-)cache them
+ // Snoop at some messages and manipulate tile cache information as needed
auto peer = _peer.lock();
+
+ if (_kind == Kind::ToPrisoner)
+ {
+ if (tokens[0] == "curpart:" &&
+ tokens.count() == 2 &&
+ getTokenInteger(tokens[1], "part", _curPart))
+ {
+ return true;
+ }
+ }
+
if (_kind == Kind::ToPrisoner && peer && peer->_tileCache)
{
if (tokens[0] == "tile:")
@@ -159,6 +171,11 @@ bool MasterProcessSession::handleInput(char *buffer, int length)
assert(firstLine.size() == static_cast<std::string::size_type>(length));
peer->_tileCache->saveStatus(firstLine);
}
+ else if (tokens[0] == "invalidatetiles:")
+ {
+ assert(firstLine.size() == static_cast<std::string::size_type>(length));
+ peer->_tileCache->invalidateTiles(_curPart, firstLine);
+ }
}
forwardToPeer(buffer, length);
@@ -212,15 +229,16 @@ bool MasterProcessSession::handleInput(char *buffer, int length)
}
return loadDocument(buffer, length, tokens);
}
- else if (tokens[0] != "status" &&
- tokens[0] != "tile" &&
+ else if (tokens[0] != "invalidatetiles" &&
tokens[0] != "key" &&
tokens[0] != "mouse" &&
- tokens[0] != "uno" &&
- tokens[0] != "selecttext" &&
- tokens[0] != "selectgraphic" &&
tokens[0] != "resetselection" &&
- tokens[0] != "saveas")
+ tokens[0] != "saveas" &&
+ tokens[0] != "selectgraphic" &&
+ tokens[0] != "selecttext" &&
+ tokens[0] != "status" &&
+ tokens[0] != "tile" &&
+ tokens[0] != "uno")
{
sendTextFrame("error: cmd=" + tokens[0] + " kind=unknown");
return false;
@@ -230,6 +248,10 @@ bool MasterProcessSession::handleInput(char *buffer, int length)
sendTextFrame("error: cmd=" + tokens[0] + " kind=nodocloaded");
return false;
}
+ else if (tokens[0] == "invalidatetiles")
+ {
+ return invalidateTiles(buffer, length, tokens);
+ }
else if (tokens[0] == "status")
{
return getStatus(buffer, length);
@@ -406,6 +428,25 @@ void MasterProcessSession::preSpawn()
_childProcesses[child.id()] = childId;
}
+bool MasterProcessSession::invalidateTiles(const char *buffer, int length, StringTokenizer& tokens)
+{
+ int part, tilePosX, tilePosY, tileWidth, tileHeight;
+
+ if (tokens.count() != 6 ||
+ !getTokenInteger(tokens[1], "part", part) ||
+ !getTokenInteger(tokens[2], "tileposx", tilePosX) ||
+ !getTokenInteger(tokens[3], "tileposy", tilePosY) ||
+ !getTokenInteger(tokens[4], "tilewidth", tileWidth) ||
+ !getTokenInteger(tokens[5], "tileheight", tileHeight))
+ {
+ sendTextFrame("error: cmd=invalidatetiles kind=syntax");
+ return false;
+ }
+
+ _tileCache->invalidateTiles(_curPart, tilePosX, tilePosY, tileWidth, tileHeight);
+ return true;
+}
+
bool MasterProcessSession::loadDocument(const char *buffer, int length, StringTokenizer& tokens)
{
if (tokens.count() != 2)
@@ -592,8 +633,8 @@ void MasterProcessSession::forwardToPeer(const char *buffer, int length)
ChildProcessSession::ChildProcessSession(std::shared_ptr<WebSocket> ws, LibreOfficeKit *loKit) :
LOOLSession(ws, Kind::ToMaster),
- _loKit(loKit),
- _loKitDocument(NULL)
+ _loKitDocument(NULL),
+ _loKit(loKit)
{
std::cout << Util::logPrefix() << "ChildProcessSession ctor this=" << this << " ws=" << _ws.get() << std::endl;
}
@@ -690,11 +731,12 @@ extern "C"
{
static void myCallback(int nType, const char* pPayload, void* pData)
{
- LOOLSession *srv = reinterpret_cast<LOOLSession *>(pData);
+ ChildProcessSession *srv = reinterpret_cast<ChildProcessSession *>(pData);
switch ((LibreOfficeKitCallbackType) nType)
{
case LOK_CALLBACK_INVALIDATE_TILES:
+ srv->sendTextFrame("curpart: part=" + std::to_string(srv->_loKitDocument->pClass->getPart(srv->_loKitDocument)));
srv->sendTextFrame("invalidatetiles: " + std::string(pPayload));
break;
case LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR:
diff --git a/loolwsd/LOOLSession.hpp b/loolwsd/LOOLSession.hpp
index 2d780d7..c73456c 100644
--- a/loolwsd/LOOLSession.hpp
+++ b/loolwsd/LOOLSession.hpp
@@ -109,6 +109,8 @@ public:
virtual bool getStatus(const char *buffer, int length);
protected:
+ bool invalidateTiles(const char *buffer, int length, Poco::StringTokenizer& tokens);
+
virtual bool loadDocument(const char *buffer, int length, Poco::StringTokenizer& tokens) override;
virtual void sendTile(const char *buffer, int length, Poco::StringTokenizer& tokens);
@@ -141,6 +143,7 @@ private:
Poco::UInt64 _childId;
static Poco::Random _rng;
static std::mutex _rngMutex;
+ int _curPart;
};
class ChildProcessSession final : public LOOLSession
@@ -153,6 +156,8 @@ public:
virtual bool getStatus(const char *buffer, int length);
+ LibreOfficeKitDocument *_loKitDocument;
+
protected:
virtual bool loadDocument(const char *buffer, int length, Poco::StringTokenizer& tokens) override;
@@ -169,7 +174,6 @@ public:
std::string _jail;
std::string _loSubPath;
LibreOfficeKit *_loKit;
- LibreOfficeKitDocument *_loKitDocument;
};
#endif
diff --git a/loolwsd/TileCache.cpp b/loolwsd/TileCache.cpp
index 3b673f0..c72888e 100644
--- a/loolwsd/TileCache.cpp
+++ b/loolwsd/TileCache.cpp
@@ -10,11 +10,13 @@
#include "config.h"
#include <cassert>
+#include <cstdio>
#include <fstream>
#include <iostream>
#include <memory>
#include <string>
+#include <Poco/DirectoryIterator.h>
#include <Poco/File.h>
#include <Poco/Path.h>
#include <Poco/SHA1Engine.h>
@@ -118,6 +120,52 @@ void TileCache::saveStatus(const std::string& status)
statusStream.close();
}
+void TileCache::invalidateTiles(int part, int x, int y, int width, int height)
+{
+ std::string dirName = cacheDirName();
+
+ std::vector<std::string> toBeRemoved;
+
+ for (auto tileIterator = Poco::DirectoryIterator(dirName); tileIterator != Poco::DirectoryIterator(); ++tileIterator)
+ {
+ std::string baseName = tileIterator.path().getBaseName();
+
+ int tilePart, tilePixelWidth, tilePixelHeight, tilePosX, tilePosY, tileWidth, tileHeight;
+
+ if (parseCacheFileName(baseName, tilePart, tilePixelWidth, tilePixelHeight, tilePosX, tilePosY, tileWidth, tileHeight))
+ {
+ std::cout << "Tile " << baseName << " is " << tileWidth << "x" << tileHeight << "@+" << tilePosX << "+" << tilePosY << std::endl;
+ if (tilePart == part &&
+ tilePosX < x + width && tilePosX + tileWidth >= x &&
+ tilePosY < y + height && tilePosY + tileHeight >= y)
+ {
+ std::cout << "Match!" << std::endl;
+ toBeRemoved.push_back(tileIterator.path().toString());
+ }
+ }
+ }
+
+ for (auto i: toBeRemoved)
+ std::remove(i.c_str());
+}
+
+void TileCache::invalidateTiles(int part, const std::string& tiles)
+{
+ Poco::StringTokenizer tokens(tiles, " ", Poco::StringTokenizer::TOK_IGNORE_EMPTY | Poco::StringTokenizer::TOK_TRIM);
+
+ assert(tokens[0] == "invalidateTiles:");
+
+ if (tokens.count() != 5)
+ return;
+
+ int width(std::stoi(tokens[1]));
+ int height(std::stoi(tokens[2]));
+ int x(std::stoi(tokens[3]));
+ int y(std::stoi(tokens[4]));
+
+ invalidateTiles(part, x, y, width, height);
+}
+
std::string TileCache::cacheDirName()
{
Poco::SHA1Engine digestEngine;
@@ -136,6 +184,11 @@ std::string TileCache::cacheFileName(int part, int width, int height, int tilePo
std::to_string(tileWidth) + "x" + std::to_string(tileHeight) + ".png");
}
+bool TileCache::parseCacheFileName(std::string& fileName, int& part, int& width, int& height, int& tilePosX, int& tilePosY, int& tileWidth, int& tileHeight)
+{
+ return (std::sscanf(fileName.c_str(), "%d_%dx%d.%d,%d.%dx%d", &part, &width, &height, &tilePosX, &tilePosY, &tileWidth, &tileHeight) == 7);
+}
+
Poco::Timestamp TileCache::getLastModified()
{
std::fstream modTimeFile(cacheDirName() + "/modtime.txt", std::ios::in);
diff --git a/loolwsd/TileCache.hpp b/loolwsd/TileCache.hpp
index 28d8e3a..21704a2 100644
--- a/loolwsd/TileCache.hpp
+++ b/loolwsd/TileCache.hpp
@@ -28,9 +28,15 @@ public:
// The parameter is a status: message
void saveStatus(const std::string& status);
+ // The tiles parameter is an invalidatetiles: message as sent by the child process
+ void invalidateTiles(int part, const std::string& tiles);
+
+ void invalidateTiles(int part, int x, int y, int width, int height);
+
private:
std::string cacheDirName();
std::string cacheFileName(int part, int width, int height, int tilePosX, int tilePosY, int tileWidth, int tileHeight);
+ bool parseCacheFileName(std::string& fileName, int& part, int& width, int& height, int& tilePosX, int& tilePosY, int& tileWidth, int& tileHeight);
Poco::Timestamp getLastModified();
const std::string& _docURL;
diff --git a/loolwsd/protocol.txt b/loolwsd/protocol.txt
index 9dce5cf..adb5950 100644
--- a/loolwsd/protocol.txt
+++ b/loolwsd/protocol.txt
@@ -108,6 +108,12 @@ child <id>
parent has passed the id (a 64-bit random number) to the child
when starting it, so this is how the child identificates itself.
+curpart: part=<partNumber>
+
+ Sent to the parent process before certain messages that the parent
+ needs to act on in addition to passing them on to the client, like
+ invalidatetiles:
+
nextmessage: size=<upperlimit>
each tile: message sent from the child to the parent is preceded
commit 1d75eacd6a2d124b46f601f4353a54980fb3a3e9
Author: Tor Lillqvist <tml at collabora.com>
Date: Thu May 28 17:57:29 2015 +0300
Add an invalidatetiles message from client to server
Mainly as a debugging aid to make it easier to check the tile cache invalidation
code, but might be useful for real, too.
diff --git a/loolwsd/protocol.txt b/loolwsd/protocol.txt
index 30f428e..9dce5cf 100644
--- a/loolwsd/protocol.txt
+++ b/loolwsd/protocol.txt
@@ -11,6 +11,11 @@ tiles proactively (guessing what the client might need). Etc.
client -> server
================
+invalidatetiles part=<partNumber> tileposx=<xpos> tileposy=<ypos> tilewidth=<tileWidth> tileheight=<tileHeight>
+
+ All parameters are numbers. Makes the server remove any cached
+ tiles intersecting with the given area (in twips).
+
key type=<type> char=<charcode> key=<keycode>
<type> is 'input' or 'up', <charcode> and <keycode> are numbers.
commit 5d9f5e715a73f3c2d85ebeeb0a5404606bfcd10f
Author: Tor Lillqvist <tml at collabora.com>
Date: Thu May 28 17:55:49 2015 +0300
Sort the messages in the sections
diff --git a/loolwsd/protocol.txt b/loolwsd/protocol.txt
index 10aa581..30f428e 100644
--- a/loolwsd/protocol.txt
+++ b/loolwsd/protocol.txt
@@ -11,29 +11,26 @@ tiles proactively (guessing what the client might need). Etc.
client -> server
================
+key type=<type> char=<charcode> key=<keycode>
+
+ <type> is 'input' or 'up', <charcode> and <keycode> are numbers.
+
load <pathname>
Deprecated.
load url=<url>
-status
-
-tile part=<partNumber> width=<width> height=<height> tileposx=<xpos> tileposy=<ypos> tilewidth=<tileWidth> tileheight=<tileHeight>
-
- All parameters are numbers.
-
-key type=<type> char=<charcode> key=<keycode>
-
- <type> is 'input' or 'up', <charcode> and <keycode> are numbers.
-
mouse type=<type> x=<x> y=<y> count=<count>
<type> is 'buttondown', 'buttonup' or 'move', others are numbers.
-uno <command>
+resetselection
- <command> is a line of text.
+saveas url=<url> format=<format> options=<options>
+
+ <url> is a URL, encoded. <format> is also URL-encoded, i.e. spaces as %20
+ options are the whole rest of the line, not URL-encoded, and can be empty
selecttext type=<type> x=<x> y=<y>
@@ -43,12 +40,16 @@ selectgraphic type=<type> x=<x> y=<y>
<type> is 'start' or 'end' <x> and <y> are numbers.
-resetselection
+status
-saveas url=<url> format=<format> options=<options>
+tile part=<partNumber> width=<width> height=<height> tileposx=<xpos> tileposy=<ypos> tilewidth=<tileWidth> tileheight=<tileHeight>
+
+ All parameters are numbers.
+
+uno <command>
+
+ <command> is a line of text.
- <url> is a URL, encoded. <format> is also URL-encoded, i.e. spaces as %20
- options are the whole rest of the line, not URL-encoded, and can be empty
server -> client
================
@@ -60,15 +61,6 @@ error: cmd=<command> kind=<kind>
message that caused the error. <kind> is some single-word
classification
-status: type=<typeName> parts=<numberOfParts> current=<currentPartNumber> width=<width> height=<height>
-
- <typeName> is 'text, 'spreadsheet', 'presentation', 'drawing' or 'other. Others are numbers.
-
-tile: part=<partNumber> width=<width> height=<height> tileposx=<xpos> tileposy=<ypos> tilewidth=<tileWidth> tileheight=<tileHeight>
-<binaryPngImage>
-
- The parameters from the corresponding 'tile' command.
-
nextmessage: size=<byteSize>
<byteSize> is the size, in bytes, of the next message, in case it
@@ -78,36 +70,44 @@ nextmessage: size=<byteSize>
must be handled by clients that cannot (like those using Poco
1.6.0).
+status: type=<typeName> parts=<numberOfParts> current=<currentPartNumber> width=<width> height=<height>
+
+ <typeName> is 'text, 'spreadsheet', 'presentation', 'drawing' or 'other. Others are numbers.
+
+tile: part=<partNumber> width=<width> height=<height> tileposx=<xpos> tileposy=<ypos> tilewidth=<tileWidth> tileheight=<tileHeight>
+<binaryPngImage>
+
+ The parameters from the corresponding 'tile' command.
+
Each LOK_CALLBACK_FOO_BAR callback causes a corresponding message to
the client, consisting of the FOO_BAR part in lowercase, without
underscore, followed by a colon, space and the callback payload. For
instance:
-invalidatetiles: <payload>
-
invalidatecursor:
-
+invalidatetiles: <payload>
The communication between the parent process (the one keeping open the
Websocket connections to the clients) and a child process (handling
one document through LibreOfficeKit) uses the same protocol, with
the following additions and changes:
+
child -> parent
===============
child <id>
-Must be the first message sent from the child to the parent. The
-parent has passed the id (a 64-bit random number) to the child when
-starting it, so this is how the child identificates itself.
+ Must be the first message sent from the child to the parent. The
+ parent has passed the id (a 64-bit random number) to the child
+ when starting it, so this is how the child identificates itself.
nextmessage: size=<upperlimit>
-each tile: message sent from the child to the parent is preceded by a
-nextmessage: message that gives an upper limit on the size of the
-tile: message that will follow. (We assume it is only tile: messages
-that can be "large".) Once we depend on Poco 1.6.1, where one doesn't
-need to use a pre-allocated buffer when receiving WebSocket messages,
-this will go away.
+ each tile: message sent from the child to the parent is preceded
+ by a nextmessage: message that gives an upper limit on the size of
+ the tile: message that will follow. (We assume it is only tile:
+ messages that can be "large".) Once we depend on Poco 1.6.1, where
+ one doesn't need to use a pre-allocated buffer when receiving
+ WebSocket messages, this will go away.
More information about the Libreoffice-commits
mailing list