aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--afdnotebook.cc8
-rw-r--r--afdnotebook.h4
-rw-r--r--mainwindow.cc9
-rw-r--r--notebookmodel.cc19
-rw-r--r--notebookmodel.h7
-rw-r--r--notebookview.cc12
-rw-r--r--notebookview.h4
-rw-r--r--paperreplay.cc61
-rw-r--r--paperreplay.h53
-rw-r--r--paperreplaymodel.cc19
-rw-r--r--paperreplaymodel.h5
-rw-r--r--smartpen.cc55
-rw-r--r--smartpen.h18
-rw-r--r--smartpensyncer.cc32
-rw-r--r--stfreader.cc13
-rw-r--r--stfstrokeitem.cc4
16 files changed, 204 insertions, 119 deletions
diff --git a/afdnotebook.cc b/afdnotebook.cc
index 913986e..58ae359 100644
--- a/afdnotebook.cc
+++ b/afdnotebook.cc
@@ -320,12 +320,12 @@ bool AfdNotebook::parseGfx(const QString &file)
if (!r.atEnd()) {
QXmlStreamAttributes attrs = r.attributes();
QString imageSrc = attrs.value("src").toString();
- qDebug() << "image src" << imageSrc;
+ qDebug() << " image src" << imageSrc;
int lastSlash = imageSrc.lastIndexOf('/');
int lastDot = imageSrc.lastIndexOf('.');
if (lastSlash >= 0 && lastDot > lastSlash) {
gfx.basename = imageSrc.mid(lastSlash + 1, lastDot - lastSlash - 1);
- qDebug() << "Got gfx" << gfx.basename;
+ qDebug() << " gfx" << gfx.basename;
}
}
@@ -368,8 +368,8 @@ bool AfdNotebook::findPenData()
stroke.file = penDir.filePath(strokeFile);
bool ok = true;
- if (ok) stroke.begin = Smartpen::fromPenTime(strokeFile.mid(2, 8).toLongLong(&ok, 16) * 1000ULL);
- if (ok) stroke.end = Smartpen::fromPenTime(strokeFile.mid(13, 8).toLongLong(&ok, 16) * 1000ULL);
+ if (ok) stroke.begin = strokeFile.mid(2, 8).toLongLong(&ok, 16) * 1000ULL;
+ if (ok) stroke.end = strokeFile.mid(13, 8).toLongLong(&ok, 16) * 1000ULL;
if (!ok) {
qWarning() << "Invalid stroke filename format" << strokeFile;
diff --git a/afdnotebook.h b/afdnotebook.h
index 49d06b0..645d002 100644
--- a/afdnotebook.h
+++ b/afdnotebook.h
@@ -70,8 +70,8 @@ private:
struct StrokeData {
QString file;
- QDateTime begin;
- QDateTime end;
+ quint64 begin;
+ quint64 end;
};
struct PenData {
diff --git a/mainwindow.cc b/mainwindow.cc
index 8a32f2c..dae4ae6 100644
--- a/mainwindow.cc
+++ b/mainwindow.cc
@@ -96,10 +96,12 @@ void MainWindow::openNotebook(const QString &pen, const QString &notebook)
_curPenName = pen;
_curNotebookName = notebook;
+ Smartpen::PenTime userTime = _notebooks->penUserTime(pen);
+
if (_curNotebookName == PAPER_REPLAY) {
QString replayDir = _notebooks->paperReplayDirectory(_curPenName);
- _replay->open(replayDir, 0);
+ _replay->open(replayDir, PAPER_REPLAY_GUID, userTime);
_replayModel->refresh();
ui->pane2Stack->setCurrentWidget(ui->paperReplayView);
@@ -108,6 +110,7 @@ void MainWindow::openNotebook(const QString &pen, const QString &notebook)
qDebug() << "Opening notebook" << _curPenName << _curNotebookName << nbDir;
+ ui->notebookView->setPenUserTime(userTime);
ui->notebookView->setPaperReplay(_notebooks->paperReplayDirectory(_curPenName));
ui->notebookView->setNotebook(nbDir);
ui->pane2Stack->setCurrentWidget(ui->notebookView);
@@ -215,9 +218,12 @@ void MainWindow::handlePaperReplayRequested(const QString &file, qint64 time)
QString filePath = finfo.canonicalFilePath();
if (_media->currentSource().fileName() != filePath) {
+ qDebug() << "requesting media " << filePath;
_media->setCurrentSource(QUrl::fromLocalFile(filePath));
}
+ qDebug() << "requesting media seek to" << time << "/" << _media->totalTime();
+
switch (_media->state()) {
case Phonon::PlayingState:
case Phonon::BufferingState:
@@ -250,6 +256,7 @@ void MainWindow::handleMediaStateChange(Phonon::State state)
ui->playButton->setVisible(false);
ui->pauseButton->setVisible(true);
if (_pendingSeek) {
+ qDebug() << "requesting (pending) media seek to" << _pendingSeek << "/" << _media->totalTime();
_media->seek(_pendingSeek);
_pendingSeek = 0;
}
diff --git a/notebookmodel.cc b/notebookmodel.cc
index 3898df7..5709b8b 100644
--- a/notebookmodel.cc
+++ b/notebookmodel.cc
@@ -72,6 +72,21 @@ QString NotebookModel::penDirectory(const QString &name) const
return _dataDir.filePath(name);
}
+NotebookModel::PenTime NotebookModel::penUserTime(const QString &name) const
+{
+ QDir penDir = penDirectory(name);
+ QFile userTimeFile(penDir.filePath(PEN_USER_TIME_FILE));
+ if (userTimeFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ QString data = QString::fromUtf8(userTimeFile.readLine(32));
+ userTimeFile.close();
+ return data.toULongLong();
+ }
+
+ qWarning() << "Could not read last user time for pen" << name << "; shown dates are likely to be off";
+
+ return 0;
+}
+
QString NotebookModel::notebookDirectory(const QString &penName, const QString &nbName) const
{
return _dataDir.filePath(penName + "/" + nbName);
@@ -423,7 +438,7 @@ bool NotebookModel::isPenArchive(const QString &pen) const
bool NotebookModel::isPenLocked(const QString &pen) const
{
QDir dir = penDir(pen);
- if (dir.exists(".sync.lck")) {
+ if (dir.exists(PEN_SYNC_LOCK_FILE)) {
return true; // TODO check if stale
} else {
return false;
@@ -433,7 +448,7 @@ bool NotebookModel::isPenLocked(const QString &pen) const
bool NotebookModel::isNotebookLocked(const QString &pen, const QString &notebook) const
{
QDir dir = notebookDir(pen, notebook);
- if (dir.exists(".sync.lck")) {
+ if (dir.exists(PEN_SYNC_LOCK_FILE)) {
return true;
} else {
return false;
diff --git a/notebookmodel.h b/notebookmodel.h
index 51148e5..3fda090 100644
--- a/notebookmodel.h
+++ b/notebookmodel.h
@@ -28,6 +28,10 @@
#define ARCHIVE_EXTENSION "archive"
#define AFD_NOTEBOOK_EXTENSION "afd"
+#define PEN_USER_TIME_FILE ".usertime"
+#define PEN_LAST_SYNC_FILE ".lastsync"
+#define PEN_SYNC_LOCK_FILE ".sync.lck"
+
class NotebookModel : public QAbstractItemModel
{
Q_OBJECT
@@ -35,6 +39,8 @@ class NotebookModel : public QAbstractItemModel
public:
explicit NotebookModel(QObject *parent = 0);
+ typedef quint64 PenTime;
+
enum Roles {
FileNameRole = Qt::UserRole
};
@@ -43,6 +49,7 @@ public:
static QString userDataDirectory();
QString penDirectory(const QString &name) const;
+ PenTime penUserTime(const QString &name) const;
QString notebookDirectory(const QString &penName, const QString &nbName) const;
QString notebookDirectory(const QModelIndex &index) const;
QString paperReplayDirectory(const QString &name) const;
diff --git a/notebookview.cc b/notebookview.cc
index c8fd1c0..505a337 100644
--- a/notebookview.cc
+++ b/notebookview.cc
@@ -70,6 +70,16 @@ QString NotebookView::paperReplay() const
return _replayPath;
}
+void NotebookView::setPenUserTime(quint64 userTime)
+{
+ _penUserTime = userTime;
+}
+
+quint64 NotebookView::penUserTime() const
+{
+ return _penUserTime;
+}
+
QList<int> NotebookView::pageNumbers() const
{
return _pages.keys();
@@ -231,7 +241,7 @@ bool NotebookView::createPages()
if (pens.isEmpty()) return false;
// Failure to open paperreplay data is not fatal
- bool haveReplay = _replay->open(_replayPath, _nb->guid());
+ bool haveReplay = _replay->open(_replayPath, _nb->guid(), _penUserTime);
QList<int> pagesWithStrokes = _nb->pagesWithStrokes(pens.first());
Q_ASSERT(_pages.isEmpty());
diff --git a/notebookview.h b/notebookview.h
index 4e557d1..c1e425d 100644
--- a/notebookview.h
+++ b/notebookview.h
@@ -43,6 +43,9 @@ public:
void setPaperReplay(const QString &path);
QString paperReplay() const;
+ void setPenUserTime(quint64 userTime);
+ quint64 penUserTime() const;
+
QList<int> pageNumbers() const;
int curPage() const;
@@ -88,6 +91,7 @@ private:
PaperReplay *_replay;
QString _nbPath;
QString _replayPath;
+ quint64 _penUserTime;
QMap<int, PageItem*> _pages;
QSizeF _maxPageSize;
int _numColumns;
diff --git a/paperreplay.cc b/paperreplay.cc
index bec39f1..c7a7f49 100644
--- a/paperreplay.cc
+++ b/paperreplay.cc
@@ -20,6 +20,7 @@
#include <QtCore/QDataStream>
#include <QtCore/QDirIterator>
#include "smartpen.h"
+#include "afdpageaddress.h"
#include "paperreplay.h"
namespace
@@ -57,7 +58,7 @@ PaperReplay::Session::Session() : d()
{
}
-PaperReplay::Session::Session(quint64 id) : d(new SessionData)
+PaperReplay::Session::Session(SessionId id) : d(new SessionData)
{
d->id = id;
}
@@ -71,7 +72,7 @@ bool PaperReplay::Session::isValid() const
return d;
}
-quint64 PaperReplay::Session::id() const
+PaperReplay::SessionId PaperReplay::Session::id() const
{
return d ? d->id : 0;
}
@@ -86,12 +87,12 @@ QString PaperReplay::Session::fileName() const
return d->file;
}
-qint64 PaperReplay::Session::startTime() const
+PaperReplay::PenTime PaperReplay::Session::startTime() const
{
return d->start;
}
-qint64 PaperReplay::Session::endTime() const
+PaperReplay::PenTime PaperReplay::Session::endTime() const
{
return d->end;
}
@@ -105,16 +106,16 @@ PaperReplay::SessionList::SessionList()
{
}
-PaperReplay::SessionList::SessionList(const QMap<qint64, Session> &byTime)
+PaperReplay::SessionList::SessionList(const QMap<PenTime, Session> &byTime)
: _m(byTime)
{
}
-QList<PaperReplay::Session> PaperReplay::SessionList::sessionsDuringTime(qint64 time) const
+QList<PaperReplay::Session> PaperReplay::SessionList::sessionsDuringTime(PenTime time) const
{
QList<Session> sessions;
if (_m.isEmpty()) return sessions;
- QMap<qint64, Session>::const_iterator it = _m.lowerBound(time);
+ QMap<PenTime, Session>::const_iterator it = _m.lowerBound(time);
if (it == _m.end()) --it;
@@ -129,7 +130,7 @@ QList<PaperReplay::Session> PaperReplay::SessionList::sessionsDuringTime(qint64
return sessions;
}
-bool PaperReplay::open(const QString &path, quint64 notebookGuid)
+bool PaperReplay::open(const QString &path, quint64 notebookGuid, PenTime userTime)
{
QString penSerial = findPenSerial(path);
if (penSerial.isEmpty()) {
@@ -145,6 +146,8 @@ bool PaperReplay::open(const QString &path, quint64 notebookGuid)
return false;
}
+ _userTime = userTime;
+
QDirIterator iter(_dir.path(), QStringList("PRS-*"), QDir::Dirs | QDir::NoDotAndDotDot);
while (iter.hasNext()) {
bool ok;
@@ -185,6 +188,7 @@ void PaperReplay::close()
_byPageTime.clear();
_sessions.clear();
_dir.setPath(QString());
+ _userTime = 0;
}
QList<PaperReplay::Session> PaperReplay::sessions() const
@@ -192,12 +196,17 @@ QList<PaperReplay::Session> PaperReplay::sessions() const
return _sessions.values();
}
-PaperReplay::SessionList PaperReplay::sessions(quint64 pageAddress) const
+PaperReplay::SessionList PaperReplay::sessions(PageAddress pageAddress) const
{
return SessionList(_byPageTime[pageAddress]);
}
-bool PaperReplay::parseSessionInfo(SessionData *session, const QString &path)
+PaperReplay::PenTime PaperReplay::userTime() const
+{
+ return _userTime;
+}
+
+bool PaperReplay::parseSessionInfo(SessionData *session, const QString &path) const
{
QFile f(path);
if (f.open(QIODevice::ReadOnly)) {
@@ -207,7 +216,7 @@ bool PaperReplay::parseSessionInfo(SessionData *session, const QString &path)
}
}
-bool PaperReplay::parseSessionInfo(SessionData *session, QIODevice *dev)
+bool PaperReplay::parseSessionInfo(SessionData *session, QIODevice *dev) const
{
unsigned char magic[2];
if (dev->read(reinterpret_cast<char*>(magic), 2) != 2 ||
@@ -231,12 +240,12 @@ bool PaperReplay::parseSessionInfo(SessionData *session, QIODevice *dev)
}
}
-bool PaperReplay::parseSessionInfoV3(SessionData *session, QIODevice *dev)
+bool PaperReplay::parseSessionInfoV3(SessionData *session, QIODevice *dev) const
{
QDataStream s(dev);
if (s.skipRawData(5) != 5) return false;
- qint64 startTime, endTime, creationTime;
+ PenTime startTime, endTime, creationTime;
QString name;
s >> startTime >> endTime >> creationTime;
@@ -248,12 +257,14 @@ bool PaperReplay::parseSessionInfoV3(SessionData *session, QIODevice *dev)
session->start = startTime;
session->end = endTime;
- qDebug() << "Session:" << name << Smartpen::fromPenTime(session->start) << Smartpen::fromPenTime(session->end);
+ qDebug() << " paperreplay" << QString("PRS-%1").arg(session->id, 0, 16) << name
+ << Smartpen::fromPenTime(_userTime, session->start)
+ << Smartpen::fromPenTime(_userTime, session->end);
quint16 num_clips;
s >> num_clips;
- Q_ASSERT(num_clips == 1); // TODO: We do not yet know how to handle this scenario
+ Q_ASSERT(num_clips == 1); // TODO: We do not yet know how to handle this scenario (more than one audio file per session)
for (uint i = 0; i < num_clips; ++i) {
QString file;
@@ -262,14 +273,16 @@ bool PaperReplay::parseSessionInfoV3(SessionData *session, QIODevice *dev)
}
s >> startTime >> endTime;
- qDebug() << " Clip:" << file << Smartpen::fromPenTime(startTime) << Smartpen::fromPenTime(endTime);
+ qDebug() << " clip" << file
+ << Smartpen::fromPenTime(_userTime, startTime) << Smartpen::fromPenTime(_userTime, endTime);
session->file = file;
}
quint16 num_strokes;
s >> num_strokes;
- // TODO:
+ // We are not doing anything with the stroke data stored here,
+ // rather we match the paperreplay when we load the strokes from notebook files
for (uint i = 0; i < num_strokes; ++i) {
quint64 a, b, c;
quint32 d;
@@ -280,13 +293,13 @@ bool PaperReplay::parseSessionInfoV3(SessionData *session, QIODevice *dev)
return false;
}
s >> f >> g;
- qDebug() << " Stroke:" << a << b << c << d << nbGuid << f << g;
+ qDebug() << " stroke" << a << b << c << d << nbGuid << f << g;
}
return true;
}
-bool PaperReplay::parseSessionPages(SessionData *session, const QString &path)
+bool PaperReplay::parseSessionPages(SessionData *session, const QString &path) const
{
QFile f(path);
if (f.open(QIODevice::ReadOnly)) {
@@ -296,7 +309,7 @@ bool PaperReplay::parseSessionPages(SessionData *session, const QString &path)
}
}
-bool PaperReplay::parseSessionPages(SessionData *session, QIODevice *dev)
+bool PaperReplay::parseSessionPages(SessionData *session, QIODevice *dev) const
{
unsigned char magic[2];
if (dev->read(reinterpret_cast<char*>(magic), 2) != 2 ||
@@ -320,7 +333,7 @@ bool PaperReplay::parseSessionPages(SessionData *session, QIODevice *dev)
}
}
-bool PaperReplay::parseSessionPagesV1(SessionData *session, QIODevice *dev)
+bool PaperReplay::parseSessionPagesV1(SessionData *session, QIODevice *dev) const
{
QDataStream s(dev);
if (s.skipRawData(1) != 1) return false;
@@ -330,12 +343,12 @@ bool PaperReplay::parseSessionPagesV1(SessionData *session, QIODevice *dev)
session->pages.reserve(session->pages.size() + num_pages);
for (uint i = 0; i < num_pages; ++i) {
- quint64 address;
- qint64 time;
+ PageAddress address;
+ PenTime time;
s >> address >> time;
session->pages.append(address);
- qDebug() << " Page:" << address << time << Smartpen::fromPenTime(time);
+ qDebug() << " page" << AfdPageAddress(address).toString() << Smartpen::fromPenTime(_userTime, time);
}
return true;
diff --git a/paperreplay.h b/paperreplay.h
index 9e61af7..62a75a6 100644
--- a/paperreplay.h
+++ b/paperreplay.h
@@ -24,18 +24,26 @@
#include <QtCore/QMap>
#include <QtCore/QVector>
-/** Name of the paper replay notebook */
+/** Name of the paper replay "fake" notebook.
+ * Contains all paper replay sessions not tied to another notebook. */
#define PAPER_REPLAY "Paper Replay"
+#define PAPER_REPLAY_GUID 0
class PaperReplay : public QObject
{
Q_OBJECT
+public:
+ typedef quint64 SessionId;
+ typedef quint64 PenTime;
+ typedef quint64 PageAddress;
+
+private:
struct SessionData : public QSharedData {
- quint64 id;
+ SessionId id;
QString name;
- qint64 start, end;
- QVector<quint64> pages;
+ PenTime start, end;
+ QVector<PageAddress> pages;
QString file;
};
@@ -49,12 +57,12 @@ public:
bool isValid() const;
- quint64 id() const;
+ SessionId id() const;
QString name() const;
- qint64 startTime() const;
- qint64 endTime() const;
+ PenTime startTime() const;
+ PenTime endTime() const;
QVector<quint64> pages() const;
@@ -63,7 +71,7 @@ public:
static bool startTimeLess(const Session &a, const Session &b);
private:
- Session(quint64 id);
+ Session(SessionId id);
QSharedDataPointer<SessionData> d;
friend class PaperReplay;
@@ -74,35 +82,38 @@ public:
public:
SessionList();
- QList<Session> sessionsDuringTime(qint64 time) const;
+ QList<Session> sessionsDuringTime(PenTime time) const;
private:
- explicit SessionList(const QMap<qint64, Session>& byTime);
+ explicit SessionList(const QMap<PenTime, Session>& byTime);
- QMap<qint64, Session> _m;
+ QMap<PenTime, Session> _m;
friend class PaperReplay;
};
- bool open(const QString &path, quint64 notebookGuid = 0);
+ bool open(const QString &path, quint64 notebookGuid, PenTime userTime);
void close();
QList<Session> sessions() const;
- SessionList sessions(quint64 pageAddress) const;
+ SessionList sessions(PageAddress pageAddress) const;
+
+ PenTime userTime() const;
private:
- static bool parseSessionInfo(SessionData *session, const QString &path);
- static bool parseSessionInfo(SessionData *session, QIODevice *dev);
- static bool parseSessionInfoV3(SessionData *session, QIODevice *dev);
+ bool parseSessionInfo(SessionData *session, const QString &path) const;
+ bool parseSessionInfo(SessionData *session, QIODevice *dev) const;
+ bool parseSessionInfoV3(SessionData *session, QIODevice *dev) const;
- static bool parseSessionPages(SessionData *session, const QString &path);
- static bool parseSessionPages(SessionData *session, QIODevice *dev);
- static bool parseSessionPagesV1(SessionData *session, QIODevice *dev);
+ bool parseSessionPages(SessionData *session, const QString &path) const;
+ bool parseSessionPages(SessionData *session, QIODevice *dev) const;
+ bool parseSessionPagesV1(SessionData *session, QIODevice *dev) const;
private:
QDir _dir;
- QHash<quint64, Session> _sessions;
- QMap<quint64, QMap<qint64, Session> > _byPageTime;
+ QHash<PageAddress, Session> _sessions;
+ QMap<PageAddress, QMap<PenTime, Session> > _byPageTime;
+ PenTime _userTime;
};
#endif // PAPERREPLAY_H
diff --git a/paperreplaymodel.cc b/paperreplaymodel.cc
index 480968f..4c40f76 100644
--- a/paperreplaymodel.cc
+++ b/paperreplaymodel.cc
@@ -102,30 +102,25 @@ void PaperReplayModel::refresh()
endResetModel();
}
-QString PaperReplayModel::getSessionName(const PaperReplay::Session &session)
+QString PaperReplayModel::getSessionName(const PaperReplay::Session &session) const
{
QString title = session.name();
if (title.isEmpty()) {
- QDateTime date = Smartpen::fromPenTime(session.startTime());
+ QDateTime date = Smartpen::fromPenTime(_replay->userTime(), session.startTime());
title = date.toString(Qt::DefaultLocaleLongDate);
}
return title;
}
-QString PaperReplayModel::getSessionDate(const PaperReplay::Session &session)
+QString PaperReplayModel::getSessionLength(const PaperReplay::Session &session) const
{
- QDateTime date = Smartpen::fromPenTime(session.startTime());
- return date.toString(Qt::DefaultLocaleShortDate);
-}
-
-QString PaperReplayModel::getSessionLength(const PaperReplay::Session &session)
-{
- int secs = Smartpen::fromPenTime(session.startTime()).secsTo(Smartpen::fromPenTime(session.endTime()));
- int mins = secs / 60;
+ qint64 msecs = session.endTime() - session.startTime();
+ uint secs = msecs / 1000;
+ uint mins = secs / 60;
secs %= 60;
- int hours = mins / 60;
+ uint hours = mins / 60;
mins %= 60;
const QChar fill('0');
diff --git a/paperreplaymodel.h b/paperreplaymodel.h
index dd59db3..e894d6c 100644
--- a/paperreplaymodel.h
+++ b/paperreplaymodel.h
@@ -41,9 +41,8 @@ public slots:
void refresh();
private:
- static QString getSessionName(const PaperReplay::Session &session);
- static QString getSessionDate(const PaperReplay::Session &session);
- static QString getSessionLength(const PaperReplay::Session &session);
+ QString getSessionName(const PaperReplay::Session &session) const;
+ QString getSessionLength(const PaperReplay::Session &session) const;
private:
PaperReplay *_replay;
diff --git a/smartpen.cc b/smartpen.cc
index ce26a31..686170d 100644
--- a/smartpen.cc
+++ b/smartpen.cc
@@ -16,7 +16,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <QtCore/QDateTime>
+#include <QtCore/QDataStream>
#include <QtCore/QDebug>
#include <QtCore/QDeadlineTimer>
#include <QtCore/QThread>
@@ -26,7 +26,6 @@
#include "xmlutils.h"
#include "smartpen.h"
-#define PEN_EPOCH (1289335960000LL)
#define PEN_MTU OBEX_MAXIMUM_MTU
#define PEN_TIMEOUT_SECONDS 5
@@ -86,7 +85,7 @@ QByteArray Smartpen::getObject(const QString &name)
return result;
}
-QString Smartpen::getParameter(Parameters parameter)
+QByteArray Smartpen::getParameter(Parameter parameter)
{
QString objectName = QString("ppdata?key=pp%1").arg(uint(parameter), 4, 16);
QByteArray data = getObject(objectName);
@@ -97,21 +96,36 @@ QString Smartpen::getParameter(Parameters parameter)
if (!r.atEnd()) {
QXmlStreamAttributes attrs = r.attributes();
- return attrs.value("value").toString();
+ QString value = attrs.value("value").toString();
+ if (!value.isEmpty()) {
+ if (value.startsWith("0x")) {
+ return QByteArray::fromHex(value.mid(2).toLatin1());
+ } else {
+ qWarning() << "Unknown parameter return format: " << value;
+ }
+ }
}
- return QString();
+ return QByteArray();
}
QString Smartpen::getPenName()
{
- QString name = getParameter(PenName);
- if (name.isEmpty()) {
- return name; // Empty string if unknown name
- }
+ return QString::fromUtf8(getParameter(PenName));
+}
- QByteArray hex = QByteArray::fromHex(name.mid(2).toLatin1());
- return QString::fromUtf8(hex);
+Smartpen::PenTime Smartpen::getPenTime(Parameter parameter)
+{
+ Smartpen::PenTime time;
+ QByteArray value = getParameter(parameter);
+ if (value.isEmpty() || value.size() != sizeof(Smartpen::PenTime)) {
+ qWarning() << "got invalid value for pen time: " << value.toHex();
+ return 0;
+ }
+ QDataStream ds(value);
+ ds.setByteOrder(QDataStream::LittleEndian);
+ ds >> time;
+ return time;
}
QVariantMap Smartpen::getPenInfo()
@@ -216,22 +230,11 @@ QByteArray Smartpen::getPaperReplay(PenTime from)
return getObject(QString("lspdata?name=com.livescribe.paperreplay.PaperReplay&start_time=%1&returnVersion=0.3&remoteCaller=WIN_LD_200").arg(from));
}
-qint64 Smartpen::toPenTime(const QDateTime &dt)
+QDateTime Smartpen::fromPenTime(PenTime userTime, PenTime penTime)
{
- if (dt.isValid()) {
- return dt.toMSecsSinceEpoch() - PEN_EPOCH;
- } else {
- return 0;
- }
-}
-
-QDateTime Smartpen::fromPenTime(qint64 t)
-{
- if (t) {
- return QDateTime::fromMSecsSinceEpoch(t + PEN_EPOCH).toLocalTime();
- } else {
- return QDateTime();
- }
+ QDateTime dt = QDateTime::fromMSecsSinceEpoch(penTime + userTime, Qt::UTC);
+ dt.setTimeSpec(Qt::LocalTime); // userTime is actually in LocalTime, so override tz conversion
+ return dt;
}
QString Smartpen::toPenSerial(quint64 id)
diff --git a/smartpen.h b/smartpen.h
index c75c494..2ff64e3 100644
--- a/smartpen.h
+++ b/smartpen.h
@@ -27,7 +27,7 @@
#define SMARTPEN_DPI_X (677.3333)
#define SMARTPEN_DPI_Y (677.3333)
-// TODO: These values are mostly random.
+// TODO: These values are obtained by observation and may be wrong
#define SMARTPEN_BLEED_X 333.3
#define SMARTPEN_BLEED_Y 333.3
@@ -40,19 +40,26 @@ public:
~Smartpen();
typedef QPair<unsigned int, unsigned int> Address;
- typedef qint64 PenTime;
typedef quint64 PenId;
+ typedef quint64 PenTime;
bool isConnected() const;
- enum Parameters : quint16 {
+ enum Parameter : quint16 {
+ /// The offset between the PenTime (see below) and the user's configured time. This value is static.
+ PenUserTime = 0x8003,
+ /// The current time as reported by the pen's rtc
+ PenRtcTime = 0x8004,
+ PenType = 0x8006,
PenName = 0x8011
};
QByteArray getObject(const QString& name);
- QString getParameter(Parameters parameter);
+ QByteArray getParameter(Parameter parameter);
QString getPenName();
+ PenTime getPenTime(Parameter parameter);
+
QVariantMap getPenInfo();
struct ChangeReport {
@@ -67,8 +74,7 @@ public:
QByteArray getLspData(const QString &name, PenTime from = 0);
QByteArray getPaperReplay(PenTime from = 0);
- static PenTime toPenTime(const QDateTime &dt);
- static QDateTime fromPenTime(PenTime t);
+ static QDateTime fromPenTime(PenTime userTime, PenTime penTime);
static QString toPenSerial(PenId id);
static PenId toPenId(const QString &serial);
diff --git a/smartpensyncer.cc b/smartpensyncer.cc
index 9149f33..794b7c5 100644
--- a/smartpensyncer.cc
+++ b/smartpensyncer.cc
@@ -26,7 +26,7 @@
#include "notebookmodel.h"
#include "smartpensyncer.h"
-#define BUFFER_SIZE 16 * 1024
+#define BUFFER_SIZE 128 * 1024
namespace {
class LockFile
@@ -108,7 +108,7 @@ Smartpen::PenTime TimestampFile::get()
if (_f.open(QIODevice::ReadOnly | QIODevice::Text)) {
QString data = QString::fromUtf8(_f.readLine(32));
_f.close();
- return data.toLongLong();
+ return data.toULongLong();
} else {
qWarning() << "Could not read timestamp file:" << _f.fileName();
}
@@ -219,10 +219,22 @@ bool SmartpenSyncer::syncPen()
return false;
}
- TimestampFile lastSyncFile(_penDataDir.filePath(".lastsync"));
+ // Get the current user time offset from the pen
+ // and store it so that we have it even when the pen is offline
+ TimestampFile userTimeFile(_penDataDir.filePath(PEN_USER_TIME_FILE));
+ Smartpen::PenTime userTime = _pen->getPenTime(Smartpen::PenUserTime);
+ userTimeFile.set(userTime);
+ qDebug() << "pen time base:" << userTime << Smartpen::fromPenTime(userTime, 0);
+
+ Smartpen::PenTime penTime = _pen->getPenTime(Smartpen::PenRtcTime);
+ qDebug() << "pen current time:" << penTime << Smartpen::fromPenTime(userTime, penTime);
+
+ // Read when is the last time we synchronized with this pen (in PenTime, not user time)
+ TimestampFile lastSyncFile(_penDataDir.filePath(PEN_LAST_SYNC_FILE));
Smartpen::PenTime lastSync = lastSyncFile.get();
if (lastSync != 0) lastSync += 1; // We want the changes _from_ the last sync
+ // Ask the pen for all the changes which happened since the last sync time
QList<Smartpen::ChangeReport> changes = _pen->getChangeList(lastSync);
Smartpen::PenTime changesEndTime = 0;
@@ -269,9 +281,9 @@ bool SmartpenSyncer::syncNotebook(Smartpen::PenTime lastSync, const Smartpen::Ch
}
}
- LockFile lock(notebookDir.filePath(".sync.lck"));
+ LockFile lock(notebookDir.filePath(PEN_SYNC_LOCK_FILE));
if (!lock.lock()) {
- qWarning() << "Notebook is already being synchronized; delete this file if it is not:" << notebookDir.absoluteFilePath(".sync.lck");
+ qWarning() << "Notebook is already being synchronized; delete this file if it is not:" << notebookDir.absoluteFilePath(PEN_SYNC_LOCK_FILE);
return false;
}
@@ -293,9 +305,9 @@ bool SmartpenSyncer::syncPaperReplay(Smartpen::PenTime lastSync, const Smartpen:
}
}
- LockFile lock(replayDir.filePath(".sync.lck"));
+ LockFile lock(replayDir.filePath(PEN_SYNC_LOCK_FILE));
if (!lock.lock()) {
- qWarning() << "Paper replay is already being synchronized; delete this file if it is not:" << replayDir.absoluteFilePath(".sync.lck");
+ qWarning() << "Paper replay is already being synchronized; delete this file if it is not:" << replayDir.absoluteFilePath(PEN_SYNC_LOCK_FILE);
return false;
}
@@ -319,7 +331,7 @@ bool SmartpenSyncer::extractZip(QByteArray &zipData, QDir &dir)
return false;
}
- QScopedArrayPointer<char> buffer(new char[BUFFER_SIZE]);
+ QByteArray buffer(BUFFER_SIZE, Qt::Uninitialized);
for (bool more=zip.goToFirstFile(); more; more=zip.goToNextFile()) {
QString zipName = zip.getCurrentFileName();
@@ -350,7 +362,7 @@ bool SmartpenSyncer::extractZip(QByteArray &zipData, QDir &dir)
}
while (!zipFile.atEnd()) {
- qint64 read = zipFile.read(buffer.data(), BUFFER_SIZE);
+ qint64 read = zipFile.read(buffer.data(), buffer.size());
if (read <= 0) {
qWarning() << "short read on:" << zipName;
zipFile.close();
@@ -368,8 +380,6 @@ bool SmartpenSyncer::extractZip(QByteArray &zipData, QDir &dir)
zipFile.close();
}
- buffer.reset();
-
if (zip.getZipError() == UNZ_OK) {
return true;
} else {
diff --git a/stfreader.cc b/stfreader.cc
index ff5e4f4..ad15b59 100644
--- a/stfreader.cc
+++ b/stfreader.cc
@@ -45,12 +45,12 @@ StfReader::StrokeHandler::~StrokeHandler()
bool StfReader::parseV1(BitReader& br)
{
- qint64 cur_time = 0;
+ quint64 cur_time = 0;
while (!br.atEnd()) {
syncV1(br);
quint8 header = br.readBits(8);
- qint64 time;
+ quint64 time;
QPoint p0, pa;
int f0;
@@ -81,6 +81,8 @@ bool StfReader::parseV1(BitReader& br)
p0.setY(br.readBits(16));
f0 = readForce(br);
+ quint64 stroke_time = cur_time;
+
if (handler) {
bool res = handler->startStroke(p0, f0, cur_time);
if (!res) return false;
@@ -110,7 +112,7 @@ bool StfReader::parseV1(BitReader& br)
if (time == 0) {
if (handler) {
- bool res = handler->endStroke(cur_time);
+ bool res = handler->endStroke(stroke_time);
if (!res) return false;
}
break;
@@ -149,12 +151,13 @@ bool StfReader::parseV1(BitReader& br)
pa *= 256 / static_cast<int>(time);
f0 += deltaf;
+ stroke_time += time;
+
if (handler) {
- bool res = handler->strokePoint(p0, f0, cur_time);
+ bool res = handler->strokePoint(p0, f0, stroke_time);
if (!res) return false;
}
}
-
}
return false;
diff --git a/stfstrokeitem.cc b/stfstrokeitem.cc
index 335e166..8fce6b7 100644
--- a/stfstrokeitem.cc
+++ b/stfstrokeitem.cc
@@ -51,9 +51,11 @@ void StfStrokeItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
if (event->button() == Qt::LeftButton && hasPaperReplay()) {
QGraphicsView *view = scene()->views().first();
if (NotebookView *nbview = qobject_cast<NotebookView*>(view)) {
- qint64 time = _startTime - _session.startTime();
+ PaperReplay::PenTime time = _startTime - _session.startTime();
if (time < 10) time = 0;
+ qDebug() << "requesting paper replay at time" << time << "/" << (_session.endTime() - _session.startTime());
+
nbview->requestPaperReplay(_session.fileName(), time);
event->accept();
return;