feat(screen): preview mode changes with keep and revert

pull/18/head
reionwong 3 weeks ago
parent 3c3a8f808d
commit ee0dd83658

@ -28,6 +28,12 @@ Adapted for Cutefish: a versioned session D-Bus interface without Ash or Mojo.
<annotation name="org.qtproject.QtDBus.QtTypeName.In3" value="QList&lt;cutefish::display::ipc::DisplayRequest&gt;"/><annotation name="org.qtproject.QtDBus.QtTypeName.In5" value="QList&lt;cutefish::display::ipc::Placement&gt;"/><annotation name="org.qtproject.QtDBus.QtTypeName.In6" value="cutefish::display::ipc::MirrorGroups"/>
<arg name="operationToken" type="s" direction="out"/>
</method>
<method name="ApplyConfiguration">
<arg name="instanceId" type="s" direction="in"/><arg name="topologyGeneration" type="t" direction="in"/><arg name="appliedRevision" type="t" direction="in"/>
<arg name="displays" type="a(sb(iiiiiiubs)uu)" direction="in"/><arg name="primary" type="s" direction="in"/><arg name="placements" type="a(ssui)" direction="in"/><arg name="mirrorGroups" type="aas" direction="in"/>
<annotation name="org.qtproject.QtDBus.QtTypeName.In3" value="QList&lt;cutefish::display::ipc::DisplayRequest&gt;"/><annotation name="org.qtproject.QtDBus.QtTypeName.In5" value="QList&lt;cutefish::display::ipc::Placement&gt;"/><annotation name="org.qtproject.QtDBus.QtTypeName.In6" value="cutefish::display::ipc::MirrorGroups"/>
<arg name="operationToken" type="s" direction="out"/>
</method>
<method name="SetConfiguration">
<arg name="instanceId" type="s" direction="in"/><arg name="topologyGeneration" type="t" direction="in"/><arg name="appliedRevision" type="t" direction="in"/>
<arg name="displays" type="a(sb(iiiiiiubs)uu)" direction="in"/><arg name="primary" type="s" direction="in"/><arg name="placements" type="a(ssui)" direction="in"/><arg name="mirrorGroups" type="aas" direction="in"/>

@ -139,7 +139,7 @@ void DisplayClient::refresh()
QString DisplayClient::apply(const QList<ipc::DisplayRequest> &displays, const QString &primary,
const QList<ipc::Placement> &placements,
const ipc::MirrorGroups &mirrorGroups)
const ipc::MirrorGroups &mirrorGroups, bool preview)
{
if (!available() || operationPending())
return {};
@ -149,9 +149,13 @@ QString DisplayClient::apply(const QList<ipc::DisplayRequest> &displays, const Q
m_operationTimeout.start();
const auto epoch = m_serviceEpoch;
const auto operation = ++m_operationSerial;
auto pending = m_interface->SetConfiguration(m_state.instanceId, m_state.topologyGeneration,
m_state.appliedRevision, displays, primary,
placements, mirrorGroups);
auto pending = preview
? m_interface->ApplyConfiguration(m_state.instanceId, m_state.topologyGeneration,
m_state.appliedRevision, displays, primary,
placements, mirrorGroups)
: m_interface->SetConfiguration(m_state.instanceId, m_state.topologyGeneration,
m_state.appliedRevision, displays, primary,
placements, mirrorGroups);
auto *watcher = new QDBusPendingCallWatcher(pending, this);
connect(watcher, &QDBusPendingCallWatcher::finished, this, [this, watcher, epoch, operation] {
QDBusPendingReply<QString> reply = *watcher;

@ -51,10 +51,13 @@ public:
const State &state() const { return m_state; }
void refresh();
// A preview is applied but persisted only by confirm(); the compositor
// reverts it on its own deadline otherwise.
QString apply(const QList<cutefish::display::ipc::DisplayRequest> &displays,
const QString &primary,
const QList<cutefish::display::ipc::Placement> &placements,
const cutefish::display::ipc::MirrorGroups &mirrorGroups);
const cutefish::display::ipc::MirrorGroups &mirrorGroups,
bool preview = false);
void confirm(const QString &token);
void revert(const QString &token);

@ -439,6 +439,20 @@ void DisplayOutputModel::discard()
setState(m_applied);
}
bool DisplayOutputModel::needsConfirmation() const
{
for (const auto &e : m_entries) {
const auto *mode = selectedMode(e);
if (!e.enabled || !mode || mode->current)
continue;
// A display being switched on has no current mode to leave behind.
for (const auto &applied : e.physical.modes)
if (applied.current)
return true;
}
return false;
}
QList<ipc::DisplayRequest> DisplayOutputModel::requests() const
{
QList<ipc::DisplayRequest> r;

@ -57,6 +57,10 @@ public:
bool dirty() const { return m_dirty; }
// A display switched to another mode may show nothing at all, so that
// draft is previewed and kept only on confirmation.
bool needsConfirmation() const;
QList<cutefish::display::ipc::DisplayRequest> requests() const;
QList<cutefish::display::ipc::Placement> placements() const;
cutefish::display::ipc::MirrorGroups mirrorGroups() const;

@ -76,7 +76,8 @@ void DisplayScreen::apply()
m_error.clear();
emit stateChanged();
if (m_client.apply(m_outputModel->requests(), m_outputModel->primary(),
m_outputModel->placements(), m_outputModel->mirrorGroups())
m_outputModel->placements(), m_outputModel->mirrorGroups(),
m_outputModel->needsConfirmation())
.isEmpty()) {
m_busy = false;
m_error = tr("The display service is unavailable.");

@ -72,6 +72,50 @@ private slots:
QCOMPARE(model.data(selected, DisplayOutputModel::IdRole).toString(), QString("1"));
}
void onlyModeChangesNeedConfirmation()
{
DisplayClient::State state = extendedDisplays(2);
state.physical[0].connectionType = 1;
for (auto &physical : state.physical) {
cutefish::display::ipc::Mode mode;
mode.width = 1280;
mode.height = 720;
mode.refreshMilliHz = 60000;
physical.modes.append(mode);
}
DisplayOutputModel model;
model.setState(state);
QVERIFY(!model.needsConfirmation());
QVERIFY(model.setData(model.index(1), 1.25, DisplayOutputModel::ScaleRole));
QVERIFY(model.dirty());
QVERIFY(!model.needsConfirmation());
// The internal panel is no exception.
QVERIFY(model.setData(model.index(0), 1, DisplayOutputModel::ResolutionIndexRole));
QVERIFY(model.needsConfirmation());
QVERIFY(model.setData(model.index(0), 0, DisplayOutputModel::ResolutionIndexRole));
QVERIFY(!model.needsConfirmation());
QVERIFY(model.setData(model.index(1), 1, DisplayOutputModel::ResolutionIndexRole));
QVERIFY(model.needsConfirmation());
QVERIFY(model.setData(model.index(1), false, DisplayOutputModel::EnabledRole));
QVERIFY(!model.needsConfirmation());
model.discard();
QVERIFY(!model.needsConfirmation());
// Switching a display on picks a mode without one to fall back to.
for (auto &mode : state.physical[1].modes)
mode.current = false;
state.logical.removeLast();
model.setState(state);
QVERIFY(model.setData(model.index(1), true, DisplayOutputModel::EnabledRole));
QVERIFY(model.dirty());
QVERIFY(!model.needsConfirmation());
}
void mirroringThePrimaryHandsItToTheSource()
{
DisplayOutputModel model;

@ -116,7 +116,7 @@ private slots:
}
}
void clickAppliesAndPersistsWithoutConfirmation()
void clickAppliesAndAModeChangeIsConfirmed()
{
QTemporaryDir directory;
QVERIFY(directory.isValid());
@ -161,13 +161,34 @@ private slots:
QVERIFY2(screen.error().isEmpty(), qPrintable(screen.error()));
QTRY_COMPARE(observer.state().logical.first().width, 1280);
QTRY_COMPARE(observer.state().logical.first().height, 720);
// A mode change is previewed and kept only on confirmation.
QVERIFY(model->setData(model->index(0), 2, DisplayOutputModel::ResolutionIndexRole));
QTRY_VERIFY_WITH_TIMEOUT(!screen.busy(), 5000);
QVERIFY2(screen.error().isEmpty(), qPrintable(screen.error()));
QTRY_COMPARE(observer.state().logical.first().width, 854);
QTRY_COMPARE(observer.state().logical.first().height, 480);
QTRY_VERIFY(screen.pendingConfirmation());
QVERIFY(screen.confirmationSeconds() > 0);
QVERIFY(!screen.dirty());
screen.keep();
QTRY_VERIFY_WITH_TIMEOUT(!screen.pendingConfirmation(), 5000);
QCOMPARE(observer.state().logical.first().width, 854);
QCOMPARE(model->data(model->index(0), DisplayOutputModel::SizeRole).toSize(),
QSize(1280, 720));
// Reverting restores the kept mode without a round trip through the draft.
QVERIFY(model->setData(model->index(0), 1, DisplayOutputModel::ResolutionIndexRole));
QTRY_VERIFY_WITH_TIMEOUT(!screen.busy(), 5000);
QVERIFY2(screen.error().isEmpty(), qPrintable(screen.error()));
QTRY_VERIFY(screen.pendingConfirmation());
QVERIFY(model->data(model->index(0), DisplayOutputModel::SizeRole).toSize()
!= QSize(1280, 720));
screen.revert();
QTRY_VERIFY_WITH_TIMEOUT(!screen.pendingConfirmation(), 5000);
QTRY_COMPARE(model->data(model->index(0), DisplayOutputModel::SizeRole).toSize(),
QSize(1280, 720));
QTRY_COMPARE(observer.state().logical.first().width, 854);
QVERIFY(!screen.dirty());
int currentModes = 0;
for (const auto &mode : observer.state().physical.first().modes) {
if (!mode.current)

Loading…
Cancel
Save