aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--60-livescribe.rules2
-rw-r--r--README.md6
-rw-r--r--main.cc7
-rw-r--r--mainwindow.cc36
-rw-r--r--mainwindow.h4
-rw-r--r--mainwindow.ui75
-rw-r--r--notebookmodel.cc15
-rw-r--r--notebookview.h4
-rw-r--r--pageitem.cc2
-rw-r--r--pageitem.h2
-rw-r--r--paperreplaymodel.cc2
-rw-r--r--scribiu.pro16
-rw-r--r--smartpen.cc269
-rw-r--r--smartpen.h14
-rw-r--r--smartpenmanager.cc261
-rw-r--r--smartpenmanager.h31
-rw-r--r--smartpensyncer.cc48
-rw-r--r--smartpensyncer.h20
-rw-r--r--stfgraphicsitem.h2
-rw-r--r--stfstrokeitem.cc8
-rw-r--r--stfstrokeitem.h2
22 files changed, 590 insertions, 237 deletions
diff --git a/.gitignore b/.gitignore
index 75c107b..bebf921 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,2 @@
*.pro.user
+/build
diff --git a/60-livescribe.rules b/60-livescribe.rules
index 340ac5a..a10d9c3 100644
--- a/60-livescribe.rules
+++ b/60-livescribe.rules
@@ -1 +1 @@
-SUBSYSTEM=="usb", ENV{ID_VENDOR}=="Livescribe", TAG+="udev-acl", TAG+="livescribe-pen"
+SUBSYSTEM=="usb", ENV{ID_VENDOR}=="Livescribe", TAG+="uaccess", TAG+="livescribe-pen"
diff --git a/README.md b/README.md
index aecd148..bee62c2 100644
--- a/README.md
+++ b/README.md
@@ -10,8 +10,8 @@ It also allows you to export individual pages as PNG files or voice memos as AAC
# Requirements
-Scribiu requires Qt 4.8, including the core, gui, svg and phonon modules.
-It also requires libudev, openobex, libusb (<1.0) and QuaZip.
+Scribiu requires Qt 5, including the core, gui, widgets, and svg modules.
+It also requires phonon, libudev, openobex (>=1.7), libusb (>=1.0) and QuaZip.
Most of these should be packaged by your distribution.
This program should work with the Livescribe Pulse as well as the Livescribe Echo.
@@ -32,7 +32,7 @@ It should automatically start synchronizing after connecting a Smartpen.
# Design
-By default Scribiu stores its information inside `$XDG_DATA_HOME/data/scribiu`.
+By default Scribiu stores its information inside `$XDG_DATA_HOME/scribiu`.
Inside there you will find a directory for every synchronized pen, and inside the pen directory, you will find a subdirectory for each one of your LiveScribe notebooks.
These directories contain the raw notebook, stroke, and voice data as received from the pen -- no processing is done.
diff --git a/main.cc b/main.cc
index 9ddf7c4..406a729 100644
--- a/main.cc
+++ b/main.cc
@@ -17,15 +17,18 @@
*/
#include "mainwindow.h"
-#include <QApplication>
+#include <QtWidgets/QApplication>
int main(int argc, char *argv[])
{
+ QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
+ QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
+
QApplication app(argc, argv);
app.setOrganizationName("scribiu");
app.setOrganizationDomain("com.javispedro.scribiu");
app.setApplicationName("scribiu");
- app.setApplicationVersion("1.0");
+ app.setApplicationVersion("1.2");
MainWindow w;
w.show();
diff --git a/mainwindow.cc b/mainwindow.cc
index 5d513b1..5c27145 100644
--- a/mainwindow.cc
+++ b/mainwindow.cc
@@ -19,8 +19,8 @@
#include <QtCore/QDebug>
#include <QtCore/QTimer>
#include <QtCore/QSettings>
-#include <QtGui/QFileDialog>
-#include <QtGui/QMessageBox>
+#include <QtWidgets/QFileDialog>
+#include <QtWidgets/QMessageBox>
#include <QtSvg/QSvgGenerator>
#include "mainwindow.h"
#include "ui_mainwindow.h"
@@ -37,30 +37,15 @@ MainWindow::MainWindow(QWidget *parent) :
_statusLabel(new QLabel)
{
ui->setupUi(this);
-#if QT_VERSION < QT_VERSION_CHECK(5, 1, 0)
- // Some tricks for DPI support
- const qreal scale = logicalDpiX() / 96.0;
- if (scale > 1.1) {
- ui->notebookTree->header()->setDefaultSectionSize(ui->notebookTree->header()->defaultSectionSize() * scale);
- ui->notebookTree->setIconSize(QSize(16, 16) * scale);
- ui->prevButton->setMaximumSize(ui->prevButton->maximumSize() * scale);
- ui->nextButton->setMaximumSize(ui->nextButton->maximumSize() * scale);
- ui->exportButton->setMaximumSize(ui->exportButton->maximumSize() * scale);
- ui->playButton->setMaximumSize(ui->playButton->maximumSize() * scale);
- ui->pauseButton->setMaximumSize(ui->pauseButton->maximumSize() * scale);
- ui->playButton->setMaximumSize(ui->prevButton->maximumSize() * scale);
- ui->pageEdit->setMaximumWidth(ui->pageEdit->maximumWidth() * scale);
- }
-#endif
ui->notebookTree->setModel(_notebooks);
- ui->notebookTree->header()->setResizeMode(0, QHeaderView::Stretch);
- ui->notebookTree->header()->setResizeMode(1, QHeaderView::Fixed);
- ui->notebookTree->header()->setResizeMode(2, QHeaderView::Fixed);
+ ui->notebookTree->header()->setSectionResizeMode(0, QHeaderView::Stretch);
+ ui->notebookTree->header()->setSectionResizeMode(1, QHeaderView::Fixed);
+ ui->notebookTree->header()->setSectionResizeMode(2, QHeaderView::Fixed);
ui->notebookTree->expandAll();
ui->notebookView->setVisible(false);
ui->paperReplayView->setModel(_replayModel);
- ui->paperReplayView->horizontalHeader()->setResizeMode(0, QHeaderView::Stretch);
- ui->paperReplayView->horizontalHeader()->setResizeMode(1, QHeaderView::Fixed);
+ ui->paperReplayView->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch);
+ ui->paperReplayView->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Fixed);
ui->paperReplayView->setVisible(false);
Phonon::createPath(_media, _mediaOutput);
_media->setTickInterval(500);
@@ -185,10 +170,11 @@ void MainWindow::handleNotebookSelected(const QModelIndex &index)
return;
}
- QModelIndex child = parent.child(index.row(), 0);
+ // Get column 0, which corresponds to notebook name
+ QModelIndex nb = _notebooks->index(index.row(), 0, parent);
openNotebook(_notebooks->data(parent, Qt::DisplayRole).toString(),
- _notebooks->data(child, Qt::DisplayRole).toString());
+ _notebooks->data(nb, Qt::DisplayRole).toString());
}
void MainWindow::handleNotebookRowsInserted(const QModelIndex &index, int start, int end)
@@ -220,7 +206,7 @@ void MainWindow::handlePaperReplayRequested(const QString &file, qint64 time)
QString filePath = finfo.canonicalFilePath();
if (_media->currentSource().fileName() != filePath) {
- _media->setCurrentSource(filePath);
+ _media->setCurrentSource(QUrl::fromLocalFile(filePath));
}
switch (_media->state()) {
diff --git a/mainwindow.h b/mainwindow.h
index 6689795..f3f6128 100644
--- a/mainwindow.h
+++ b/mainwindow.h
@@ -19,8 +19,8 @@
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
-#include <QtGui/QMainWindow>
-#include <QtGui/QLabel>
+#include <QtWidgets/QMainWindow>
+#include <QtWidgets/QLabel>
#include <phonon/MediaObject>
#include <phonon/AudioOutput>
#include "notebookmodel.h"
diff --git a/mainwindow.ui b/mainwindow.ui
index 1cfff9a..c04ff30 100644
--- a/mainwindow.ui
+++ b/mainwindow.ui
@@ -15,8 +15,7 @@
</property>
<property name="windowIcon">
<iconset theme="scribiu">
- <normaloff/>
- </iconset>
+ <normaloff>.</normaloff>.</iconset>
</property>
<widget class="QWidget" name="centralWidget">
<layout class="QVBoxLayout" name="verticalLayout">
@@ -57,7 +56,7 @@
<bool>false</bool>
</attribute>
<attribute name="headerDefaultSectionSize">
- <number>27</number>
+ <number>28</number>
</attribute>
<attribute name="headerStretchLastSection">
<bool>false</bool>
@@ -72,19 +71,12 @@
<layout class="QHBoxLayout" name="viewpaneTools">
<item>
<widget class="QToolButton" name="prevButton">
- <property name="maximumSize">
- <size>
- <width>30</width>
- <height>30</height>
- </size>
- </property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset theme="go-previous">
- <normaloff/>
- </iconset>
+ <normaloff>.</normaloff>.</iconset>
</property>
</widget>
</item>
@@ -109,16 +101,9 @@
</item>
<item>
<widget class="QToolButton" name="nextButton">
- <property name="maximumSize">
- <size>
- <width>30</width>
- <height>30</height>
- </size>
- </property>
<property name="icon">
<iconset theme="go-next">
- <normaloff/>
- </iconset>
+ <normaloff>.</normaloff>.</iconset>
</property>
</widget>
</item>
@@ -140,19 +125,12 @@
</item>
<item>
<widget class="QToolButton" name="exportButton">
- <property name="maximumSize">
- <size>
- <width>30</width>
- <height>30</height>
- </size>
- </property>
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset theme="document-save-as">
- <normaloff/>
- </iconset>
+ <normaloff>.</normaloff>.</iconset>
</property>
</widget>
</item>
@@ -277,42 +255,28 @@
</property>
<item>
<widget class="QToolButton" name="playButton">
- <property name="maximumSize">
- <size>
- <width>30</width>
- <height>30</height>
- </size>
- </property>
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset theme="media-playback-start">
- <normaloff/>
- </iconset>
+ <normaloff>.</normaloff>.</iconset>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="pauseButton">
- <property name="maximumSize">
- <size>
- <width>30</width>
- <height>30</height>
- </size>
- </property>
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset theme="media-playback-pause">
- <normaloff/>
- </iconset>
+ <normaloff>.</normaloff>.</iconset>
</property>
</widget>
</item>
<item>
- <widget class="Phonon::SeekSlider" name="replaySlider" native="true"/>
+ <widget class="Phonon::SeekSlider" name="replaySlider"/>
</item>
<item>
<widget class="QLabel" name="mediaPosLabel"/>
@@ -338,7 +302,7 @@
<x>0</x>
<y>0</y>
<width>718</width>
- <height>23</height>
+ <height>29</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">
@@ -362,8 +326,7 @@
<action name="actionQuit">
<property name="icon">
<iconset theme="application-exit">
- <normaloff/>
- </iconset>
+ <normaloff>.</normaloff>.</iconset>
</property>
<property name="text">
<string>&amp;Quit</string>
@@ -378,8 +341,7 @@
<action name="actionExport">
<property name="icon">
<iconset theme="document-save-as">
- <normaloff/>
- </iconset>
+ <normaloff>.</normaloff>.</iconset>
</property>
<property name="text">
<string>&amp;Export...</string>
@@ -388,8 +350,7 @@
<action name="actionAbout">
<property name="icon">
<iconset theme="help-about">
- <normaloff/>
- </iconset>
+ <normaloff>.</normaloff>.</iconset>
</property>
<property name="text">
<string>About...</string>
@@ -399,6 +360,12 @@
<layoutdefault spacing="6" margin="11"/>
<customwidgets>
<customwidget>
+ <class>Phonon::SeekSlider</class>
+ <extends>QWidget</extends>
+ <header location="global">phonon/seekslider.h</header>
+ <container>1</container>
+ </customwidget>
+ <customwidget>
<class>NotebookView</class>
<extends>QGraphicsView</extends>
<header>notebookview.h</header>
@@ -411,12 +378,6 @@
<slot>nextPage()</slot>
</slots>
</customwidget>
- <customwidget>
- <class>Phonon::SeekSlider</class>
- <extends>QWidget</extends>
- <header location="global">Phonon/SeekSlider</header>
- <container>1</container>
- </customwidget>
</customwidgets>
<resources/>
<connections>
diff --git a/notebookmodel.cc b/notebookmodel.cc
index e52d3f4..b9c897e 100644
--- a/notebookmodel.cc
+++ b/notebookmodel.cc
@@ -18,10 +18,10 @@
#include <QtCore/QDebug>
#include <QtCore/QSettings>
-#include <QtGui/QApplication>
#include <QtGui/QIcon>
#include <QtGui/QDesktopServices>
-#include <QtGui/QStyle>
+#include <QtWidgets/QApplication>
+#include <QtWidgets/QStyle>
#include "paperreplay.h"
#include "afdnotebook.h"
#include "notebookmodel.h"
@@ -46,7 +46,7 @@ NotebookModel::NotebookModel(QObject *parent) :
QString NotebookModel::defaultDataDirectory()
{
- QString path = QDesktopServices::storageLocation(QDesktopServices::DataLocation);
+ QString path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
if (path.isEmpty()) {
path = QDir::home().absoluteFilePath(".scribiu");
}
@@ -150,6 +150,7 @@ QVariant NotebookModel::data(const QModelIndex &index, int role) const
}
break;
}
+ break;
case Qt::TextAlignmentRole:
switch (index.column()) {
case 0:
@@ -158,6 +159,7 @@ QVariant NotebookModel::data(const QModelIndex &index, int role) const
case 2:
return Qt::AlignCenter;
}
+ break;
}
}
return QVariant();
@@ -239,9 +241,10 @@ void NotebookModel::refresh()
{
QStringList pens = _dataDir.entryList(QStringList("*.pen"), QDir::Dirs, QDir::Name);
for (int i = 0; i < pens.size(); i++) {
- pens[i].chop(4);
+ pens[i].chop(4); // Remove .pen extension
}
+ // Insert/remove new/deleted pens
int i = 0, j = 0;
while (i < _pens.size() && j < pens.size()) {
int comp = QString::compare(_pens[i], pens[j], Qt::CaseInsensitive);
@@ -336,7 +339,7 @@ void NotebookModel::refreshPen(const QString &name)
int NotebookModel::indexOfPen(const QString &name)
{
- QStringList::const_iterator it = qBinaryFind(_pens, name);
+ auto it = std::lower_bound(_pens.begin(), _pens.end(), name);
if (it == _pens.end()) {
return -1;
} else {
@@ -361,7 +364,7 @@ QIcon NotebookModel::getNotebookIcon(const QString &pen, const QString &notebook
candidates << "userdata/icon/Notebook.png"
<< "userdata/icon/active_64x64.png"
<< "userdata/icon/active_32x32.png"
- << "userdata/icon/active_16x16.png";
+ << "userdata/icon/active_16x16.png";
}
QIcon icon;
diff --git a/notebookview.h b/notebookview.h
index 605db5c..c9f5686 100644
--- a/notebookview.h
+++ b/notebookview.h
@@ -19,8 +19,8 @@
#ifndef NOTEBOOKVIEW_H
#define NOTEBOOKVIEW_H
-#include <QtGui/QGraphicsView>
-#include <QtGui/QGraphicsItem>
+#include <QtWidgets/QGraphicsView>
+#include <QtWidgets/QGraphicsItem>
#include "afdnotebook.h"
#include "paperreplay.h"
#include "pageitem.h"
diff --git a/pageitem.cc b/pageitem.cc
index 09c84ae..b5be4cf 100644
--- a/pageitem.cc
+++ b/pageitem.cc
@@ -35,7 +35,7 @@ PageItem::PageItem(AfdNotebook *nb, PaperReplay *replay, int pageNum, QGraphicsI
bg->setShapeMode(QGraphicsPixmapItem::BoundingRectShape);
bg->setTransformationMode(Qt::SmoothTransformation);
QRectF bgRect = bg->boundingRect();
- bg->scale(_pageTrim.width() / bgRect.width(), _pageTrim.height() / bgRect.height());
+ bg->setScale(std::min(_pageTrim.width() / bgRect.width(), _pageTrim.height() / bgRect.height()));
bg->setPos(_pageTrim.topLeft());
}
diff --git a/pageitem.h b/pageitem.h
index 446b7b5..831ca61 100644
--- a/pageitem.h
+++ b/pageitem.h
@@ -19,7 +19,7 @@
#ifndef PAGEITEM_H
#define PAGEITEM_H
-#include <QtGui/QGraphicsItem>
+#include <QtWidgets/QGraphicsItem>
#include "afdnotebook.h"
#include "paperreplay.h"
diff --git a/paperreplaymodel.cc b/paperreplaymodel.cc
index ae17367..480968f 100644
--- a/paperreplaymodel.cc
+++ b/paperreplaymodel.cc
@@ -98,7 +98,7 @@ void PaperReplayModel::refresh()
{
beginResetModel();
_sessions = _replay->sessions();
- qSort(_sessions.begin(), _sessions.end(), PaperReplay::Session::startTimeLess);
+ std::sort(_sessions.begin(), _sessions.end(), PaperReplay::Session::startTimeLess);
endResetModel();
}
diff --git a/scribiu.pro b/scribiu.pro
index a9b12d0..0623205 100644
--- a/scribiu.pro
+++ b/scribiu.pro
@@ -1,16 +1,16 @@
-QT += core gui svg phonon
-
-greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
-
TARGET = scribiu
TEMPLATE = app
+QT += core gui widgets svg
+QT += phonon4qt5
+
+CONFIG += c++11
+
CONFIG += link_pkgconfig
-PKGCONFIG += libudev libusb openobex
-LIBS += -lquazip
+PKGCONFIG += libudev libusb-1.0 openobex quazip
-SOURCES += main.cc\
- mainwindow.cc \
+SOURCES += main.cc \
+ mainwindow.cc \
smartpenmanager.cc \
notebookmodel.cc \
smartpensyncer.cc \
diff --git a/smartpen.cc b/smartpen.cc
index b03f0e5..1282e33 100644
--- a/smartpen.cc
+++ b/smartpen.cc
@@ -18,29 +18,25 @@
#include <QtCore/QDateTime>
#include <QtCore/QDebug>
+#include <QtCore/QDeadlineTimer>
+#include <QtCore/QThread>
#include <QtCore/QStringList>
#include <QtCore/QtEndian>
-#include <usb.h>
+#include <libusb-1.0/libusb.h>
#include "xmlutils.h"
#include "smartpen.h"
-#define PEN_EPOCH (1289335960000LL) // This is probably not correct
-#define PEN_MTU 900
-#define PEN_TIMEOUT_SECONDS 30
+#define PEN_EPOCH (1289335960000LL)
+#define PEN_MTU OBEX_MAXIMUM_MTU
+#define PEN_TIMEOUT_SECONDS 5
#define INVALID_CID 0xFFFFFFFFU
static const char pen_serial_chars[] = "ABCDEFGHJKMNPQRSTUWXYZ23456789";
static const unsigned int pen_serial_num_chars = sizeof(pen_serial_chars) - 1;
-/* Terrible hack comes now: */
-struct obex_usb_intf_transport_t {
- struct obex_usb_intf_transport_t *prev, *next; /* Next and previous interfaces in the list */
- struct usb_device *device; /* USB device that has the interface */
-};
-
Smartpen::Smartpen(QObject *parent) :
- QObject(parent), _obex(0), _connId(INVALID_CID)
+ QObject(parent), _obex(0), _connId(INVALID_CID), _reqComplete(false), _continueReceived(0)
{
}
@@ -58,6 +54,10 @@ bool Smartpen::isConnected() const
QByteArray Smartpen::getObject(const QString &name)
{
+ qDebug() << "Getting object" << name;
+
+ prepareRequest();
+
obex_object_t *obj = OBEX_ObjectNew(_obex, OBEX_CMD_GET);
Q_ASSERT(obj);
@@ -72,23 +72,13 @@ QByteArray Smartpen::getObject(const QString &name)
return QByteArray();
}
- qDebug() << "Getting object" << name;
-
if (OBEX_Request(_obex, obj) < 0) {
qWarning() << "Get object request failed";
+ OBEX_ObjectDelete(_obex, obj);
return QByteArray();
}
- QDateTime start = QDateTime::currentDateTimeUtc();
- QDateTime now;
- do {
- OBEX_HandleInput(_obex, PEN_TIMEOUT_SECONDS);
- now = QDateTime::currentDateTimeUtc();
- } while (_inBuf.isEmpty() && start.secsTo(now) < PEN_TIMEOUT_SECONDS);
-
- if (_inBuf.isEmpty()) {
- qWarning() << "Did not receive any data in" << start.secsTo(now) << "seconds";
- }
+ waitForRequestComplete(PEN_TIMEOUT_SECONDS);
QByteArray result;
qSwap(_inBuf, result);
@@ -117,7 +107,7 @@ QString Smartpen::getPenName()
{
QString name = getParameter(PenName);
if (name.isEmpty()) {
- return name;
+ return name; // Empty string if unknown name
}
QByteArray hex = QByteArray::fromHex(name.mid(2).toLatin1());
@@ -163,7 +153,7 @@ QList<Smartpen::ChangeReport> Smartpen::getChangeList(PenTime from)
QByteArray data = getObject(QString("changelist?start_time=%1").arg(from));
QXmlStreamReader r(data);
- qDebug() << QString::fromAscii(data);
+ qDebug() << "changelist:" << QString::fromLatin1(data);
advanceToFirstChildElement(r, "xml");
advanceToFirstChildElement(r, "changelist");
@@ -271,6 +261,64 @@ quint64 Smartpen::toPenId(const QString &serial)
return id;
}
+bool Smartpen::reset(const Address &addr)
+{
+ libusb_context *ctx = 0;
+ libusb_device **devlist = 0;
+ libusb_device *dev = 0;
+ libusb_device_handle *handle = 0;
+ ssize_t ndevs;
+ int err = 0;
+
+ err = libusb_init(&ctx);
+ if (err != 0) {
+ qWarning() << "libusb_init failed:" << err;
+ goto err0;
+ }
+
+ ndevs = libusb_get_device_list(ctx, &devlist);
+ if (ndevs < 0) {
+ qWarning() << "libusb_get_device_list failed:" << err;
+ goto err1;
+ }
+
+ for (ssize_t i = 0; i < ndevs; ++i) {
+ if (libusb_get_bus_number(devlist[i]) == addr.first &&
+ libusb_get_device_address(devlist[i]) == addr.second) {
+ dev = devlist[i];
+ }
+ }
+
+ if (!dev) {
+ qWarning() << "could not find device in libusb";
+ err = -ENODEV;
+ goto err2;
+ }
+
+ err = libusb_open(dev, &handle);
+ if (err != 0) {
+ qWarning() << "libusb_open failed:" << err;
+ goto err2;
+ }
+
+ err = libusb_reset_device(handle);
+ if (err != 0) {
+ qWarning() << "libusb_reset_device failed: " << err;
+ goto err3;
+ }
+
+ qDebug() << "USB device resetted";
+
+err3:
+ libusb_close(handle);
+err2:
+ libusb_free_device_list(devlist, 1);
+err1:
+ libusb_exit(ctx);
+err0:
+ return err == 0;
+}
+
bool Smartpen::connectToPen(const Address &addr)
{
if (_obex) {
@@ -278,18 +326,21 @@ bool Smartpen::connectToPen(const Address &addr)
return false;
}
- _obex = OBEX_Init(OBEX_TRANS_USB, obexEventCb, 0);
+ _connId = INVALID_CID;
+
+ _obex = OBEX_Init(OBEX_TRANS_USB, obexEventCb, OBEX_FL_CLOEXEC);
Q_ASSERT(_obex);
OBEX_SetUserData(_obex, this);
OBEX_SetTransportMTU(_obex, PEN_MTU, PEN_MTU);
- obex_interface_t *interfaces, *ls_interface = 0;
- int count = OBEX_FindInterfaces(_obex, &interfaces);
+ obex_interface_t *ls_interface = 0;
+ int count = OBEX_EnumerateInterfaces(_obex);
for (int i = 0; i < count; i++) {
- if (interfaces[i].usb.intf->device->bus->location == addr.first &&
- interfaces[i].usb.intf->device->devnum == addr.second) {
- ls_interface = &interfaces[i];
+ obex_interface_t *intf = OBEX_GetInterfaceByIndex(_obex, i);
+ if (intf->usb.bus_number == addr.first &&
+ intf->usb.device_address == addr.second) {
+ ls_interface = intf;
}
}
@@ -298,22 +349,16 @@ bool Smartpen::connectToPen(const Address &addr)
return false;
}
- usb_dev_handle *handle = usb_open(ls_interface->usb.intf->device);
- if (handle) {
- qDebug() << "resetting usb device";
- usb_reset(handle);
- usb_close(handle);
- } else {
- qWarning() << "could not open usb device for resetting";
- }
-
- qDebug() << "connecting to" << ls_interface->usb.product;
+ qDebug() << "Connecting to" << ls_interface->usb.product;
- if (OBEX_InterfaceConnect(_obex, ls_interface) < 0) {
- qWarning() << "Could not connect to Livescribe interface";
+ int err = OBEX_InterfaceConnect(_obex, ls_interface) ;
+ if (err < 0) {
+ qWarning() << "Could not connect to Livescribe interface" << strerror(-err);
return false;
}
+ prepareRequest();
+
static const char * livescribe_service = "LivescribeService";
obex_object_t *object = OBEX_ObjectNew(_obex, OBEX_CMD_CONNECT);
obex_headerdata_t hd;
@@ -337,7 +382,7 @@ bool Smartpen::connectToPen(const Address &addr)
qDebug() << "Connection in progress";
- OBEX_HandleInput(_obex, PEN_TIMEOUT_SECONDS);
+ waitForRequestComplete(PEN_TIMEOUT_SECONDS);
return _connId != INVALID_CID;
}
@@ -346,19 +391,27 @@ void Smartpen::disconnectFromPen()
{
if (_connId != INVALID_CID) {
if (_obex) {
+ OBEX_CancelRequest(_obex, 0);
+
+ prepareRequest();
+
obex_object_t *object = OBEX_ObjectNew(_obex, OBEX_CMD_DISCONNECT);
Q_ASSERT(object);
addConnHeader(object);
- OBEX_Request(_obex, object);
- OBEX_HandleInput(_obex, PEN_TIMEOUT_SECONDS);
+ if (OBEX_Request(_obex, object) == 0) {
+ qDebug() << "Send disconnect";
+ waitForRequestComplete(PEN_TIMEOUT_SECONDS);
+ }
}
_connId = INVALID_CID;
}
if (_obex) {
+ OBEX_TransportDisconnect(_obex);
OBEX_Cleanup(_obex);
_obex = 0;
}
_inBuf.clear();
+ qDebug() << "Disconnected";
}
void Smartpen::obexEventCb(obex_t *handle, obex_object_t *obj,
@@ -372,24 +425,38 @@ void Smartpen::obexEventCb(obex_t *handle, obex_object_t *obj,
void Smartpen::handleObexEvent(obex_object_t *object,
int event, int obex_cmd, int obex_rsp)
{
+ // Special flag used for temporarily ignoring the synthetic events generated during OBEX_CancelRequest
+ static bool aborting_continue = false;
+ if (aborting_continue) return;
switch (event) {
case OBEX_EV_PROGRESS:
- if (obex_cmd == OBEX_CMD_GET) {
- // It seems that the pen wants us to add this header on every continue response
- addConnHeader(object);
- }
break;
case OBEX_EV_REQDONE:
qDebug() << "event reqdone cmd=" << obex_cmd << " rsp=" << OBEX_ResponseToString(obex_rsp);
handleObexRequestDone(object, obex_cmd, obex_rsp);
break;
case OBEX_EV_LINKERR:
- qWarning() << "link error cmd=" << obex_cmd;
- emit error();
+ qWarning() << "link error cmd=" << obex_cmd << " rsp=" << OBEX_ResponseToString(obex_rsp);
+ emit linkError("Link error");
+ break;
+ case OBEX_EV_PARSEERR:
+ qWarning() << "parse error cmd=" << obex_cmd << " rsp=" << OBEX_ResponseToString(obex_rsp);
+ emit linkError("Protocol error");
+ break;
+ case OBEX_EV_CONTINUE:
+ // The standard "Continue" messages sent by OpenObex "forget" to include the ConnectionID, confusing the pen
+ // We're going to therefore implement the "continue" state machine on our own
+ handleObexContinue(object, obex_cmd);
+
+ // Cancel OpenObex's Continue message, avoiding the spurious events that generates
+ Q_ASSERT(!aborting_continue);
+ aborting_continue = true;
+ OBEX_CancelRequest(_obex, 0);
+ aborting_continue = false;
break;
default:
- qDebug() << "event" << event << obex_cmd << obex_rsp;
+ qDebug() << "event unknown=" << event << " cmd=" << obex_cmd << " rsp=" << OBEX_ResponseToString(obex_rsp);
break;
}
}
@@ -400,6 +467,8 @@ void Smartpen::handleObexRequestDone(obex_object_t *object, int obex_cmd, int ob
obex_headerdata_t hdata;
quint32 hlen;
+ _reqComplete = true;
+
switch (obex_cmd & ~OBEX_FINAL) {
case OBEX_CMD_CONNECT:
switch (obex_rsp) {
@@ -414,7 +483,7 @@ void Smartpen::handleObexRequestDone(obex_object_t *object, int obex_cmd, int ob
break;
default:
qWarning() << "Failed connection request:" << OBEX_ResponseToString(obex_rsp);
- emit error();
+ emit linkError("OBEX connection error");
break;
}
break;
@@ -437,7 +506,7 @@ void Smartpen::handleObexRequestDone(obex_object_t *object, int obex_cmd, int ob
qDebug() << "GET request succesful";
while (OBEX_ObjectGetNextHeader(_obex, object, &header_id, &hdata, &hlen)) {
if (header_id == OBEX_HDR_BODY || header_id == OBEX_HDR_BODY_END) {
- _inBuf = QByteArray(reinterpret_cast<const char*>(hdata.bs), hlen);
+ _inBuf.append(reinterpret_cast<const char*>(hdata.bs), hlen);
}
}
break;
@@ -450,6 +519,85 @@ void Smartpen::handleObexRequestDone(obex_object_t *object, int obex_cmd, int ob
}
}
+void Smartpen::handleObexContinue(obex_object_t *object, int obex_cmd)
+{
+ const uint8_t *data;
+ int len;
+
+ _continueReceived++;
+
+ switch (obex_cmd & ~OBEX_FINAL) {
+ case OBEX_CMD_GET:
+ len = OBEX_ObjectReadStream(_obex, object, &data);
+ if (len > 0) {
+ _inBuf.append(reinterpret_cast<const char*>(data), len);
+ }
+ break;
+ }
+}
+
+void Smartpen::prepareRequest()
+{
+ _reqComplete = false;
+ _continueReceived = 0;
+ _inBuf.clear();
+}
+
+bool Smartpen::waitForRequestComplete(int timeout)
+{
+ QDeadlineTimer timer(timeout * 1000UL);
+ timer.setTimerType(Qt::CoarseTimer);
+
+ int cmd = OBEX_ObjectGetCommand(_obex, NULL);
+
+ do {
+ if (OBEX_HandleInput(_obex, timer.remainingTime() / 1000) < 0) {
+ qWarning() << "OBEX_HandleInput failed";
+ break;
+ }
+ if (_continueReceived) {
+ sendContinue(cmd);
+ _continueReceived--;
+
+ // Reset timeout
+ timer.setRemainingTime(timeout * 1000UL);
+ }
+ } while (!_reqComplete && !timer.hasExpired());
+
+ if (!_reqComplete) {
+ qWarning() << "Did not complete request in" << timeout << "seconds";
+ emit linkError("Timeout");
+ return false;
+ }
+
+ return true;
+}
+
+void Smartpen::addConnHeader(obex_object_t *obj) const
+{
+ obex_headerdata_t hd;
+ hd.bq4 = _connId;
+ if (OBEX_ObjectAddHeader(_obex, obj, OBEX_HDR_CONNECTION, hd, sizeof(hd.bq4), OBEX_FL_FIT_ONE_PACKET) < 0) {
+ qCritical() << "Could not add connection header";
+ }
+}
+
+bool Smartpen::sendContinue(int obex_cmd)
+{
+ obex_object_t *obj = OBEX_ObjectNew(_obex, obex_cmd);
+ Q_ASSERT(obj);
+
+ addConnHeader(obj);
+
+ if (OBEX_Request(_obex, obj) < 0) {
+ qWarning() << "Send continue failed";
+ OBEX_ObjectDelete(_obex, obj);
+ return false;
+ }
+
+ return true;
+}
+
QString Smartpen::toPenSerialSegment(quint32 id, int len)
{
@@ -469,7 +617,7 @@ quint32 Smartpen::fromPenSerialSegment(const QString &s)
quint32 id = 0;
for (int i = 0; i < len; i++) {
- uint val = qFind(&pen_serial_chars[0], &pen_serial_chars[pen_serial_num_chars], s[i]) - &pen_serial_chars[0];
+ uint val = std::find(&pen_serial_chars[0], &pen_serial_chars[pen_serial_num_chars], s[i]) - &pen_serial_chars[0];
if (val >= pen_serial_num_chars) return 0;
id = val + id * pen_serial_num_chars;
}
@@ -488,12 +636,3 @@ QByteArray Smartpen::encodeUtf16(const QString &s)
p[size] = 0;
return data;
}
-
-void Smartpen::addConnHeader(obex_object_t *obj) const
-{
- obex_headerdata_t hd;
- hd.bq4 = _connId;
- if (OBEX_ObjectAddHeader(_obex, obj, OBEX_HDR_CONNECTION, hd, sizeof(hd.bq4), 0) < 0) {
- qCritical() << "Could not add connection header";
- }
-}
diff --git a/smartpen.h b/smartpen.h
index 18ac955..30c1f74 100644
--- a/smartpen.h
+++ b/smartpen.h
@@ -73,12 +73,14 @@ public:
static QString toPenSerial(PenId id);
static PenId toPenId(const QString &serial);
+ static bool reset(const Address &addr);
+
public slots:
bool connectToPen(const Address &addr);
void disconnectFromPen();
signals:
- void error();
+ void linkError(const QString &msg);
private:
static void obexEventCb(obex_t *handle, obex_object_t *obj,
@@ -86,17 +88,25 @@ private:
void handleObexEvent(obex_object_t *object,
int event, int obex_cmd, int obex_rsp);
void handleObexRequestDone(obex_object_t *object, int obex_cmd, int obex_rsp);
+ void handleObexContinue(obex_object_t *object, int obex_cmd);
+
+ void prepareRequest();
+ bool waitForRequestComplete(int timeout);
+ void addConnHeader(obex_object_t *object) const;
+ bool sendContinue(int obex_cmd);
static QString toPenSerialSegment(quint32 id, int len);
static quint32 fromPenSerialSegment(const QString &s);
static QByteArray encodeUtf16(const QString &s);
- void addConnHeader(obex_object_t *object) const;
+
private:
obex_t * _obex;
quint32 _connId;
QByteArray _inBuf;
+ bool _reqComplete;
+ uint _continueReceived;
};
#endif // SMARTPEN_H
diff --git a/smartpenmanager.cc b/smartpenmanager.cc
index 0ac56e2..1eeb685 100644
--- a/smartpenmanager.cc
+++ b/smartpenmanager.cc
@@ -21,17 +21,30 @@
#include "smartpenmanager.h"
+// Sync with a connected pen every 120 seconds
+#define SYNC_INTERVAL 120
+// If the previous sync failed, try in 5 seconds
+#define FAILED_SYNC_INTERVAL 5
+#define MAX_SYNC_RETRIES 3
+
SmartpenManager::SmartpenManager(QObject *parent)
: QObject(parent), _udev(udev_new()), _monitor(udev_monitor_new_from_netlink(_udev, "udev")),
- _notifier(new QSocketNotifier(udev_monitor_get_fd(_monitor), QSocketNotifier::Read))
+ _notifier(new QSocketNotifier(udev_monitor_get_fd(_monitor), QSocketNotifier::Read)),
+ _nextTry(new QTimer(this))
{
- udev_monitor_filter_add_match_tag(_monitor, "livescribe-pen");
-
connect(_notifier, SIGNAL(activated(int)), SLOT(handleMonitorActivity()));
+ connect(_nextTry, SIGNAL(timeout()), SLOT(handleTimerNextTry()));
+ _nextTry->setSingleShot(true);
+ _nextTry->setTimerType(Qt::VeryCoarseTimer);
+
+ // Start udev monitoring
+ udev_monitor_filter_add_match_subsystem_devtype(_monitor, "usb", "usb_device");
+ udev_monitor_filter_add_match_tag(_monitor, "livescribe-pen");
udev_monitor_enable_receiving(_monitor);
udev_enumerate *scan = udev_enumerate_new(_udev);
+ udev_enumerate_add_match_subsystem(scan, "usb");
udev_enumerate_add_match_tag(scan, "livescribe-pen");
if (udev_enumerate_scan_devices(scan) == 0) {
@@ -39,7 +52,7 @@ SmartpenManager::SmartpenManager(QObject *parent)
udev_list_entry_foreach(i, l) {
const char *path = udev_list_entry_get_name(i);
udev_device *dev = udev_device_new_from_syspath(_udev, path);
- processDevice(dev);
+ processDeviceAdded(dev);
udev_device_unref(dev);
}
} else {
@@ -56,18 +69,26 @@ SmartpenManager::~SmartpenManager()
udev_unref(_udev);
}
+QStringList SmartpenManager::pensConnected() const
+{
+ QStringList pens;
+ pens.reserve(_pens.size());
+ foreach (const PenInfo &pen, _pens) {
+ if (pen.connected) {
+ pens.append(pen.name);
+ }
+ }
+ return pens;
+}
+
QStringList SmartpenManager::pensBeingSynchronized() const
{
QStringList pens;
- pens.reserve(_syncers.size());
- for (QMap<Smartpen::Address, SmartpenSyncer*>::const_iterator it = _syncers.begin();
- it != _syncers.end(); ++it) {
- QString name = it.value()->penName();
- if (name.isEmpty()) {
- Smartpen::Address addr = it.value()->penAddress();
- name = QString("%1-%2").arg(addr.first).arg(addr.second);
+ pens.reserve(_pens.size());
+ foreach (const PenInfo &pen, _pens) {
+ if (pen.syncer && pen.syncer->isRunning()) {
+ pens.append(pen.name);
}
- pens.append(name);
}
return pens;
}
@@ -78,8 +99,11 @@ void SmartpenManager::handleMonitorActivity()
udev_device *dev = udev_monitor_receive_device(_monitor);
if (dev) {
const char *action = udev_device_get_action(dev);
- if (action && strcmp(action, "add") == 0) {
- processDevice(dev);
+ Q_ASSERT(action);
+ if (strcmp(action, "add") == 0) {
+ processDeviceAdded(dev);
+ } else if (strcmp(action, "remove") == 0) {
+ processDeviceRemoved(dev);
}
udev_device_unref(dev);
}
@@ -92,31 +116,212 @@ void SmartpenManager::handleSyncerFinished()
qDebug() << "Finished synchronization with pen with address:" << addr;
- _syncers.remove(addr);
- emit pensBeingSynchronizedChanged();
+ Q_ASSERT(_pens.contains(addr));
+
+ PenInfo &pen = _pens[addr];
+
+ bool failed = syncer->hasErrors();
+ bool disconnected = false;
- if (syncer->hasErrors()) {
+ if (failed) {
qWarning() << "Synchronization with address" << addr << "failed";
- emit syncFailed(syncer->penName());
+ emit syncFailed(pen.name);
+ if (pen.connected) {
+ if (pen.numRetries < MAX_SYNC_RETRIES) {
+ // Try resetting USB device first
+ if (Smartpen::reset(addr)) {
+ pen.numRetries++;
+ pen.nextTry = QDateTime::currentDateTimeUtc().addSecs(FAILED_SYNC_INTERVAL);
+ } else {
+ qWarning() << "Failed to reset; assuming disconnected";
+ pen.connected = false;
+ disconnected = true;
+ }
+ } else {
+ qWarning() << "Too many failures; assuming disconnected";
+ pen.connected = false;
+ disconnected = true;
+ }
+ }
} else {
- emit syncComplete(syncer->penName());
+ emit syncComplete(pen.name);
+ if (pen.connected) {
+ pen.numRetries = 0;
+ pen.nextTry = QDateTime::currentDateTimeUtc().addSecs(SYNC_INTERVAL);
+ }
+ }
+
+ pen.syncer->deleteLater();
+ pen.syncer = 0;
+
+ emit pensBeingSynchronizedChanged();
+ if (disconnected)
+ emit pensConnectedChanged();
+
+ scheduleNextTry();
+}
+
+void SmartpenManager::handleGotPenName(const QString &name)
+{
+ SmartpenSyncer *syncer = static_cast<SmartpenSyncer*>(sender());
+ Smartpen::Address addr = syncer->penAddress();
+
+ Q_ASSERT(_pens.contains(addr));
+
+ PenInfo &pen = _pens[addr];
+ pen.name = name;
+
+ if (pen.connected) {
+ emit pensConnectedChanged();
}
+ if (pen.syncer) {
+ emit pensBeingSynchronizedChanged();
+ }
+}
+
+void SmartpenManager::handleTimerNextTry()
+{
+ QDateTime now = QDateTime::currentDateTimeUtc().addSecs(1);
- syncer->deleteLater();
+ foreach (const PenInfo &pen, _pens) {
+ // Not connected or already syncing: ignore
+ if (!pen.connected || pen.syncer) continue;
+
+ if (now >= pen.nextTry) {
+ trySync(pen.addr);
+ }
+ }
+
+ scheduleNextTry();
}
-void SmartpenManager::processDevice(udev_device *dev)
+void SmartpenManager::processDeviceAdded(udev_device *dev)
{
uint busnum = atol(udev_device_get_sysattr_value(dev, "busnum"));
uint devnum = atol(udev_device_get_sysattr_value(dev, "devnum"));
+ Smartpen::Address addr(busnum, devnum);
+ QString name = parseUdevEscapedString(udev_device_get_property_value(dev, "ID_MODEL_ENC"));
+
+ qDebug() << "Found smartpen with address:" << addr << name;
+
+ PenInfo &pen = _pens[addr];
+ pen.addr = addr;
+ if (pen.name.isEmpty()) pen.name = name;
+ pen.connected = true;
+
+ // Schedule an attempt to sync in 1 sec
+ if (!pen.syncer) {
+ pen.numRetries = 0;
+ pen.nextTry = QDateTime::currentDateTimeUtc().addSecs(1);
+
+ scheduleNextTry();
+ }
+
+ emit pensConnectedChanged();
+}
+
+void SmartpenManager::processDeviceRemoved(udev_device *dev)
+{
+ uint busnum = atol(udev_device_get_sysattr_value(dev, "busnum"));
+ uint devnum = atol(udev_device_get_sysattr_value(dev, "devnum"));
Smartpen::Address addr(busnum, devnum);
- if (!_syncers.contains(addr)) {
- SmartpenSyncer *syncer = new SmartpenSyncer(addr, this);
- _syncers.insert(addr, syncer);
- connect(syncer, SIGNAL(finished()), SLOT(handleSyncerFinished()));
- connect(syncer, SIGNAL(penNameChanged()), SIGNAL(pensBeingSynchronizedChanged()));
- syncer->start();
- emit pensBeingSynchronizedChanged();
+
+ qDebug() << "Device removed with address:" << addr;
+
+ if (_pens.contains(addr)) {
+ PenInfo &pen = _pens[addr];
+ if (pen.syncer) {
+ pen.syncer->abort();
+ }
+ pen.connected = false;
+ emit pensConnectedChanged();
+ }
+}
+
+void SmartpenManager::trySync(const Smartpen::Address &addr)
+{
+ Q_ASSERT(_pens.contains(addr));
+ PenInfo &pen = _pens[addr];
+
+ qDebug() << "Starting sync for" << pen.name;
+
+ Q_ASSERT(pen.addr == addr);
+ Q_ASSERT(pen.connected);
+ Q_ASSERT(!pen.syncer);
+
+ pen.syncer = new SmartpenSyncer(addr, this);
+ connect(pen.syncer, SIGNAL(finished()), SLOT(handleSyncerFinished()));
+ connect(pen.syncer, SIGNAL(gotPenName(QString)), SLOT(handleGotPenName(QString)));
+ pen.syncer->start();
+
+ emit pensBeingSynchronizedChanged();
+}
+
+void SmartpenManager::scheduleNextTry()
+{
+ QDateTime nearest;
+ foreach (const PenInfo &pen, _pens) {
+ // Not connected or already syncing: ignore
+ if (!pen.connected || pen.syncer) continue;
+
+ if (nearest.isNull() || pen.nextTry < nearest) {
+ nearest = pen.nextTry;
+ }
+ }
+ if (nearest.isValid()) {
+ qint64 msecs = QDateTime::currentDateTimeUtc().msecsTo(nearest);
+ qDebug() << "Sleeping for" << msecs << "ms";
+ _nextTry->start(msecs);
+ } else {
+ qDebug() << "Nothing else to do";
+ _nextTry->stop();
+ }
+}
+
+QString SmartpenManager::parseUdevEscapedString(const char *s)
+{
+ // This just tries to parse \xddd C-style escape sequences
+ // Likely broken, see if there's a generic way to do this
+ const int l = strlen(s);
+
+ QString r;
+ r.reserve(l);
+
+ int prev = 0;
+ const char *bs;
+ while (prev < l && (bs = strchr(s + prev, '\\')) != NULL) {
+ int pos = bs - s;
+ r.append(QString::fromLatin1(s + prev, pos - prev));
+ if (pos + 1 >= l || s[pos + 1] == '\\') {
+ r.append('\\');
+ pos += 2;
+ } else {
+ char c = s[pos + 1];
+ if (c == 'x') {
+ int start = pos + 2;
+ int i = start;
+ for ( ; i < l; ++i) {
+ c = s[i];
+ if (s[i] >= '0' && s[i] <= '9') continue;
+ if (s[i] >= 'A' && s[i] <= 'F') continue;
+ if (s[i] >= 'a' && s[i] <= 'f') continue;
+ break;
+ }
+ bool ok = false;
+ uint charNum = QString::fromLatin1(s + start, i - start).toUInt(&ok, 16);
+ if (ok) {
+ r.append(QChar::fromLatin1(charNum));
+ } else {
+ r.append(QString::fromLatin1(s + pos, i - pos));
+ }
+ pos = i;
+ }
+ }
+ prev = pos;
+ }
+ if (prev < l) {
+ r.append(QString::fromLatin1(s + prev));
}
+ return r;
}
diff --git a/smartpenmanager.h b/smartpenmanager.h
index 42bdc06..a823709 100644
--- a/smartpenmanager.h
+++ b/smartpenmanager.h
@@ -19,10 +19,11 @@
#ifndef SMARTPENMANAGER_H
#define SMARTPENMANAGER_H
-#include <QtCore/QObject>
-#include <QtCore/QSocketNotifier>
#include <QtCore/QMap>
+#include <QtCore/QObject>
#include <QtCore/QPair>
+#include <QtCore/QSocketNotifier>
+#include <QtCore/QTimer>
#include "smartpensyncer.h"
struct udev;
@@ -37,28 +38,46 @@ public:
explicit SmartpenManager(QObject *parent = 0);
~SmartpenManager();
+ QStringList pensConnected() const;
QStringList pensBeingSynchronized() const;
signals:
void syncComplete(const QString &penName);
void syncFailed(const QString &penName);
+ void pensConnectedChanged();
void pensBeingSynchronizedChanged();
-public slots:
-
private slots:
void handleMonitorActivity();
void handleSyncerFinished();
+ void handleGotPenName(const QString &name);
+ void handleTimerNextTry();
private:
- void processDevice(udev_device *dev);
+ void processDeviceAdded(udev_device *dev);
+ void processDeviceRemoved(udev_device *dev);
+
+ void trySync(const Smartpen::Address &addr);
+ void scheduleNextTry();
+
+ static QString parseUdevEscapedString(const char *s);
private:
udev *_udev;
udev_monitor *_monitor;
QSocketNotifier *_notifier;
- QMap<Smartpen::Address, SmartpenSyncer*> _syncers;
+ struct PenInfo {
+ Smartpen::Address addr;
+ bool connected = false;
+ QString name;
+ SmartpenSyncer *syncer = 0;
+ QDateTime nextTry;
+ uint numRetries = 0;
+ };
+
+ QMap<Smartpen::Address, PenInfo> _pens;
+ QTimer *_nextTry;
};
#endif // SMARTPENMANAGER_H
diff --git a/smartpensyncer.cc b/smartpensyncer.cc
index f076ff9..8047f4e 100644
--- a/smartpensyncer.cc
+++ b/smartpensyncer.cc
@@ -20,7 +20,7 @@
#include <QtCore/QScopedArrayPointer>
#include <QtCore/QThread>
#include <QtCore/QDebug>
-#include <quazip/quazipfile.h>
+#include <quazip5/quazipfile.h>
#include <cstdio>
#include "paperreplay.h"
#include "notebookmodel.h"
@@ -79,7 +79,7 @@ bool LockFile::lock()
QFileInfo info(_path);
if (info.exists()) {
- if (info.created().secsTo(QDateTime::currentDateTime()) > 10 * 60) {
+ if (info.lastModified().secsTo(QDateTime::currentDateTime()) > 10 * 60) {
if (QFile::remove(info.filePath())) {
qDebug() << "Removing stale lock file:" << info.absoluteFilePath();
}
@@ -106,7 +106,7 @@ Smartpen::PenTime TimestampFile::get()
{
if (_f.exists()) {
if (_f.open(QIODevice::ReadOnly | QIODevice::Text)) {
- QString data = QString::fromAscii(_f.readLine(24));
+ QString data = QString::fromUtf8(_f.readLine(32));
_f.close();
return data.toLongLong();
} else {
@@ -120,18 +120,21 @@ Smartpen::PenTime TimestampFile::get()
void TimestampFile::set(Smartpen::PenTime time)
{
if (_f.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
- _f.write(QString::number(time).toAscii());
+ _f.write(QString::number(time).toUtf8());
_f.close();
} else {
qWarning() << "Could not set timestamp file:" << _f.fileName();
return;
}
}
-}
+} /* anonymous namespace */
SmartpenSyncer::SmartpenSyncer(const Smartpen::Address &addr, QObject *parent) :
- QThread(parent), _addr(addr), _pen(new Smartpen(this)), _errored(false), _aborted(false)
+ QThread(parent),
+ _addr(addr), _errored(false), _aborted(false),
+ _pen(new Smartpen(this))
{
+ connect(_pen, SIGNAL(linkError(QString)), SLOT(handleLinkError(QString)));
}
SmartpenSyncer::~SmartpenSyncer()
@@ -147,11 +150,6 @@ Smartpen::Address SmartpenSyncer::penAddress() const
return _addr;
}
-QString SmartpenSyncer::penName() const
-{
- return _penName;
-}
-
bool SmartpenSyncer::hasErrors() const
{
return _errored;
@@ -162,6 +160,12 @@ void SmartpenSyncer::abort()
_aborted = true;
}
+void SmartpenSyncer::reset()
+{
+ _aborted = false;
+ _errored = false;
+}
+
void SmartpenSyncer::run()
{
if (!_pen->connectToPen(_addr)) {
@@ -170,19 +174,23 @@ void SmartpenSyncer::run()
return;
}
+ if (_aborted) {
+ _pen->disconnectFromPen();
+ return;
+ }
+
_penName = _pen->getPenName();
qDebug() << "got pen name:" << _penName;
- emit penNameChanged();
+ emit gotPenName(_penName);
QVariantMap penInfo = _pen->getPenInfo();
if (penInfo.isEmpty()) {
qWarning() << "Could not get pen info";
_errored = true;
+ _pen->disconnectFromPen();
return;
}
- _penSerial = penInfo["penserial"].toString();
-
_penDataDir.setPath(NotebookModel::userDataDirectory() + "/" + _penName + ".pen");
if (!_penDataDir.exists()) {
if (!_penDataDir.mkpath(".")) {
@@ -194,6 +202,12 @@ void SmartpenSyncer::run()
_errored = true;
}
+ if (_errored) {
+ qDebug() << "Sync finished with errors";
+ } else {
+ qDebug() << "Sync finished";
+ }
+
_pen->disconnectFromPen();
}
@@ -363,3 +377,9 @@ bool SmartpenSyncer::extractZip(QByteArray &zipData, QDir &dir)
return false;
}
}
+
+void SmartpenSyncer::handleLinkError(const QString &msg)
+{
+ qWarning() << "link error:" << msg;
+ _errored = true;
+}
diff --git a/smartpensyncer.h b/smartpensyncer.h
index ea31917..9dae5c0 100644
--- a/smartpensyncer.h
+++ b/smartpensyncer.h
@@ -19,6 +19,7 @@
#ifndef SMARTPENSYNCER_H
#define SMARTPENSYNCER_H
+#include <atomic>
#include <QtCore/QThread>
#include <QtCore/QDir>
#include "smartpen.h"
@@ -26,20 +27,20 @@
class SmartpenSyncer : public QThread
{
Q_OBJECT
+
public:
explicit SmartpenSyncer(const Smartpen::Address &addr, QObject *parent = 0);
~SmartpenSyncer();
Smartpen::Address penAddress() const;
- QString penName() const;
-
bool hasErrors() const;
signals:
- void penNameChanged();
+ void gotPenName(const QString &name);
public slots:
void abort();
+ void reset();
private:
void run();
@@ -48,13 +49,16 @@ private:
bool syncPaperReplay(Smartpen::PenTime lastSync, const Smartpen::ChangeReport &change);
bool extractZip(QByteArray &zipData, QDir &dir);
+private slots:
+ void handleLinkError(const QString &msg);
+
private:
- Smartpen::Address _addr;
- Smartpen *_pen;
- bool _errored;
- bool _aborted;
+ const Smartpen::Address _addr;
+ std::atomic<bool> _errored;
+ std::atomic<bool> _aborted;
- QString _penSerial;
+ // To be used only from this thread
+ Smartpen *_pen;
QString _penName;
QDir _penDataDir;
};
diff --git a/stfgraphicsitem.h b/stfgraphicsitem.h
index 6f7038a..84c4d71 100644
--- a/stfgraphicsitem.h
+++ b/stfgraphicsitem.h
@@ -19,7 +19,7 @@
#ifndef STFGRAPHICSITEM_H
#define STFGRAPHICSITEM_H
-#include <QtGui/QGraphicsItem>
+#include <QtWidgets/QGraphicsItem>
#include "paperreplay.h"
class StfGraphicsItem : public QGraphicsItem
diff --git a/stfstrokeitem.cc b/stfstrokeitem.cc
index e2a7714..dbc7ca0 100644
--- a/stfstrokeitem.cc
+++ b/stfstrokeitem.cc
@@ -19,8 +19,8 @@
#include <QtCore/QDebug>
#include <QtGui/QPen>
#include <QtGui/QCursor>
-#include <QtGui/QGraphicsScene>
-#include <QtGui/QGraphicsSceneMouseEvent>
+#include <QtWidgets/QGraphicsScene>
+#include <QtWidgets/QGraphicsSceneMouseEvent>
#include "notebookview.h"
#include "stfstrokeitem.h"
@@ -28,10 +28,12 @@ StfStrokeItem::StfStrokeItem(const QPainterPath &stroke, const PaperReplay::Sess
: QGraphicsPathItem(stroke, parent), _session(session),
_startTime(startTime), _endTime(endTime)
{
+ QPen pen(Qt::black, 8.0f);
if (_session.isValid()) {
- setPen(QPen(Qt::darkGreen));
+ pen.setColor(Qt::darkGreen);
setCursor(Qt::PointingHandCursor);
}
+ setPen(pen);
}
int StfStrokeItem::type() const
diff --git a/stfstrokeitem.h b/stfstrokeitem.h
index 5825c10..5e7a253 100644
--- a/stfstrokeitem.h
+++ b/stfstrokeitem.h
@@ -19,7 +19,7 @@
#ifndef STFSTROKEITEM_H
#define STFSTROKEITEM_H
-#include <QtGui/QGraphicsPathItem>
+#include <QtWidgets/QGraphicsPathItem>
#include "paperreplay.h"
class StfStrokeItem : public QGraphicsPathItem