diff --git a/README.md b/README.md index 6687b2b..af266f1 100644 --- a/README.md +++ b/README.md @@ -762,6 +762,20 @@ Add it to the kernel command line of your boot loader, reboot, and switch the power supply off once so the display recovers. Details and measurements are in issue #28. +### Display stops answering during Loop or Shuffle playback + +With Loop or Shuffle, the runtime hands the playlist to the display, and the +firmware switches media on its own. On a Panorama SE `391a:1021` with firmware +v2.0.6 and a carousel of still images, the display stopped answering anywhere +from half an hour to several hours into a session. It stays on USB but no +longer accepts data, and the runtime reports that the keepalive stopped after +3 retries. Restarting the runtime or rebinding the USB device does not help; +only switching the power supply off does. One still image ran for more than +10 hours on the same setup. + +Until TRYX fixes the firmware, use Single playback with one image or video. +Details are in issue #37. + ### The display session never starts after the first media upload Before 2.5.3 the runtime created its directories with permissions from the diff --git a/src/portalusb.cpp b/src/portalusb.cpp index 1033804..554c374 100644 --- a/src/portalusb.cpp +++ b/src/portalusb.cpp @@ -157,10 +157,12 @@ Access::Access(Registry ®istry, QObject *parent) : Access(registry, Options{} Access::Access(Registry ®istry, const Options &options, QObject *parent) : QObject(parent), registry_(registry), options_(options), bus_(QString()) { registerTypes(); - permissionDeadline_.setSingleShot(true); - connect(&permissionDeadline_, &QTimer::timeout, this, [this]() { - logPortalEvent(QStringLiteral("acquire_timeout")); - fail(tr("The USB permission request timed out. Restart the Flatpak background runtime to try again.")); + // A permission dialog that nobody answered after boot used to time out + // and leave the runtime without USB access until it was restarted (#37). + // The dialog now stays open until the user allows or denies access. + pendingNotice_.setSingleShot(true); + connect(&pendingNotice_, &QTimer::timeout, this, [this]() { + logPortalEvent(QStringLiteral("acquire_still_pending")); }); connectionCheck_.setInterval(250); connect(&connectionCheck_, &QTimer::timeout, this, [this]() { @@ -440,7 +442,7 @@ bool Access::maybeAcquire() { // fail() already published the terminal state. return true; } - permissionDeadline_.start(qMax(1, options_.permissionTimeoutMs)); + pendingNotice_.start(qMax(1, options_.pendingNoticeMs)); { QMutexLocker lock(®istry_.mutex_); // A new open request supersedes an earlier denial text. @@ -448,9 +450,7 @@ bool Access::maybeAcquire() { registry_.error_.clear(); } logPortalEvent(QStringLiteral("acquire_requested"), - {{QStringLiteral("product"), printerProductIdString(device.productId)}, - {QStringLiteral("timeout_ms"), - QString::number(qMax(1, options_.permissionTimeoutMs))}}); + {{QStringLiteral("product"), printerProductIdString(device.productId)}}); const QString request = requestPath_; call(owner_, kDesktopPath, kUsbInterface, QStringLiteral("AcquireDevices"), {QString(), QVariant::fromValue(Devices{{device.id, {{QStringLiteral("writable"), true}}}}), @@ -499,7 +499,7 @@ void Access::processAcquisitionResponse(uint response) { logPortalEvent(QStringLiteral("acquire_response"), {{QStringLiteral("code"), QString::number(response)}}); if (response != 0) { - permissionDeadline_.stop(); + pendingNotice_.stop(); requestPath_.clear(); pendingId_.clear(); pendingEndpoint_.clear(); @@ -579,7 +579,7 @@ void Access::finishAcquire() { : QStringLiteral("acquire_discarded")); if (!accepted) releaseDevices({pendingId_}); - permissionDeadline_.stop(); + pendingNotice_.stop(); requestPath_.clear(); pendingId_.clear(); pendingEndpoint_.clear(); @@ -641,7 +641,7 @@ void Access::stop() { running_ = false; ++generation_; sessionReady_ = false; - permissionDeadline_.stop(); + pendingNotice_.stop(); connectionCheck_.stop(); closeRemote(requestPath_, kRequestInterface); closeRemote(sessionPath_, kSessionInterface); diff --git a/src/portalusb.h b/src/portalusb.h index b1d9859..038d4e1 100644 --- a/src/portalusb.h +++ b/src/portalusb.h @@ -87,7 +87,9 @@ class Access final : public QObject { struct Options { QString service = QStringLiteral("org.freedesktop.portal.Desktop"); int callTimeoutMs = 2000; - int permissionTimeoutMs = 120000; + // The permission request stays open until the user answers it; after + // this long the runtime only logs once that it is still waiting. + int pendingNoticeMs = 120000; }; explicit Access(Registry ®istry, QObject *parent = nullptr); Access(Registry ®istry, const Options &options, QObject *parent = nullptr); @@ -128,7 +130,7 @@ private slots: QString connectionName_; QDBusConnection bus_; QDBusServiceWatcher *watcher_ = nullptr; - QTimer permissionDeadline_; + QTimer pendingNotice_; QTimer connectionCheck_; QString owner_; QString sessionPath_; diff --git a/tests/flatpak/portalusb_tests.cpp b/tests/flatpak/portalusb_tests.cpp index ec8b9f2..a2ec985 100644 --- a/tests/flatpak/portalusb_tests.cpp +++ b/tests/flatpak/portalusb_tests.cpp @@ -311,23 +311,28 @@ private slots: QVERIFY(!registry.snapshot().monitoring); } - void permissionTimeoutClosesRequestAndIgnoresLateSuccess() { + // An unanswered permission dialog stays open: a user who only allows + // access later, for example after login, still gets the device (#37). + void unansweredPermissionRequestStaysOpenUntilAllowed() { FakePortal portal; portal.replyAutomatically = false; QVERIFY(portal.install()); Registry registry; Access::Options options; - options.permissionTimeoutMs = 100; + options.pendingNoticeMs = 50; Access access(registry, options); QVERIFY(access.start()); QTRY_COMPARE(portal.acquisitions, 1); - QTRY_VERIFY(!registry.snapshot().error.isEmpty()); - QTRY_VERIFY(portal.request.closed); - QVERIFY(!registry.snapshot().acquisitionPending); + QTest::qWait(200); + QVERIFY(!portal.request.closed); + QVERIFY(registry.snapshot().acquisitionPending); + QVERIFY(registry.snapshot().error.isEmpty()); + QVERIFY(registry.snapshot().monitoring); emit portal.request.Response(0, {}); - QTest::qWait(25); - QCOMPARE(portal.finishes, 0); - QVERIFY(!registry.snapshot().monitoring); + QTRY_VERIFY(registry.snapshot().devices.size() == 1 && + registry.snapshot().devices.first().granted); + QVERIFY(!registry.snapshot().acquisitionPending); + QCOMPARE(portal.acquisitions, 1); } void portalOwnerLossRevokesWithoutReacquiring() { @@ -511,7 +516,7 @@ private slots: "product=\"391a:1021\" readable=\"true\" writable=\"true\" " "same_identity=\"false\" granted=\"false\"$"), QStringLiteral("^tryx_usb_portal event=\"acquire_requested\" " - "product=\"391a:1021\" timeout_ms=\"[0-9]+\"$"), + "product=\"391a:1021\"$"), QStringLiteral("^tryx_usb_portal event=\"acquire_acknowledged\"$"), QStringLiteral("^tryx_usb_portal event=\"acquire_response\" code=\"0\"$"), QStringLiteral("^tryx_usb_portal event=\"acquire_granted\"$"),