diff options
-rw-r--r-- | afdnotebook.cc | 8 | ||||
-rw-r--r-- | afdnotebook.h | 4 | ||||
-rw-r--r-- | mainwindow.cc | 9 | ||||
-rw-r--r-- | notebookmodel.cc | 19 | ||||
-rw-r--r-- | notebookmodel.h | 7 | ||||
-rw-r--r-- | notebookview.cc | 12 | ||||
-rw-r--r-- | notebookview.h | 4 | ||||
-rw-r--r-- | paperreplay.cc | 61 | ||||
-rw-r--r-- | paperreplay.h | 53 | ||||
-rw-r--r-- | paperreplaymodel.cc | 19 | ||||
-rw-r--r-- | paperreplaymodel.h | 5 | ||||
-rw-r--r-- | smartpen.cc | 55 | ||||
-rw-r--r-- | smartpen.h | 18 | ||||
-rw-r--r-- | smartpensyncer.cc | 32 | ||||
-rw-r--r-- | stfreader.cc | 13 | ||||
-rw-r--r-- | stfstrokeitem.cc | 4 |
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 ¬ebook) _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 ¬ebook) 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 ¬ebook) 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) @@ -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; |