From 59b3628ba2dc262ecb6428528f0a441ff318f47c Mon Sep 17 00:00:00 2001 From: donghualin Date: Fri, 18 Mar 2022 17:03:08 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E5=85=B3=E9=97=AD=E6=98=BE=E7=A4=BA=E5=B1=8F=E5=94=A4=E9=86=92?= =?UTF-8?q?=E5=90=8E=E4=BB=BB=E5=8A=A1=E6=A0=8F=E6=98=BE=E7=A4=BA=E4=BD=8D?= =?UTF-8?q?=E7=BD=AE=E7=9A=84=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 问题原因:在自动关闭显示屏后,底层会先禁用掉显示器然后再开启显示器,前端收到禁用显示器的信号后,会把主屏幕设置为另外一个屏幕,这样任务栏就跑到副屏幕了 解决方案:增加一个类,专门用来处理这种情况,判断是否这种情况的方法是,删除显示器,更新主屏幕和新增显示器的操作在1.5秒之内发生;否则,认为是正常的新增删除调整主屏幕的操作,因为正常的这些操作下,屏幕会黑一下,而且这个黑的时间不止1.5秒 Log: 修复自动关闭显示器再唤醒后任务栏的位置的错误问题 Influence: 任务栏,设置屏幕为1分钟,自动关闭显示器为1分钟,任务栏设置为跟随鼠标,什么都不操作,等1分钟过后再唤醒,观察任务栏的位置 Bug: https://pms.uniontech.com/bug-view-114613.html Change-Id: I6d2190f2e8394261185ef42d544fba338cbe0e14 --- frame/util/multiscreenworker.cpp | 92 ++++++++++++++++++++++++++++++++ frame/util/multiscreenworker.h | 33 ++++++++++++ 2 files changed, 125 insertions(+) diff --git a/frame/util/multiscreenworker.cpp b/frame/util/multiscreenworker.cpp index 1275a0659..6c1cd907c 100644 --- a/frame/util/multiscreenworker.cpp +++ b/frame/util/multiscreenworker.cpp @@ -57,6 +57,7 @@ MultiScreenWorker::MultiScreenWorker(QWidget *parent, DWindowManagerHelper *help , m_monitorUpdateTimer(new QTimer(this)) , m_delayWakeTimer(new QTimer(this)) , m_ds(DIS_INS->primary()) + , m_screenMonitor(new ScreenChangeMonitor(&m_ds, this)) , m_state(AutoHide) { qInfo() << "init dock screen: " << m_ds.current(); @@ -1254,6 +1255,9 @@ QString MultiScreenWorker::getValidScreen(const Position &pos) //TODO 考虑在主屏幕名变化时自动更新,是不是就不需要手动处理了 m_ds.updatePrimary(DIS_INS->primary()); + if (m_screenMonitor->needUsedLastScreen()) + return m_screenMonitor->lastScreen(); + if (DIS_INS->canDock(DIS_INS->screen(m_ds.current()), pos)) return m_ds.current(); @@ -1770,3 +1774,91 @@ void MultiScreenWorker::tryToShowDock(int eventX, int eventY) } } } + +/** + * @brief 屏幕监视 + * @param ds + */ +#define TIMESPAN 1500 +ScreenChangeMonitor::ScreenChangeMonitor(DockScreen *ds, QObject *parent) + : QObject (parent) +{ + connect(qApp, &QApplication::primaryScreenChanged, this, [ this, ds ](QScreen *primaryScreen) { + if (!primaryScreen) + return; + + // 在screenAdded之后,又会发送一次主屏幕的变更的信息 + qInfo() << "primary Screen Changed,primary Screen" << primaryScreen->name(); + if (changedInSeconds()) + return; + + m_lastScreenName = ds->current(); + m_changePrimaryName = primaryScreen->name(); + m_changeTime = QDateTime::currentDateTime(); + qInfo() << "primary Changed Info:lastScreen:" << m_lastScreenName << "changeTime:" << m_changeTime; + }); + connect(qApp, &QApplication::screenAdded, this, [ this ](QScreen *newScreen) { + if (newScreen) { + m_newScreenName = newScreen->name(); + m_newTime = QDateTime::currentDateTime(); + qInfo() <<"screen added:" << m_newScreenName << ", added time:" << m_newTime; + } + }); + connect(qApp, &QApplication::screenRemoved, this, [ this ](QScreen *rmScreen) { + if (rmScreen) { + m_removeScreenName = rmScreen->name(); + m_removeTime = QDateTime::currentDateTime(); + qInfo() <<"screen removed:" << m_removeScreenName << ", removed time:" << m_removeTime; + } + }); +} + +ScreenChangeMonitor::~ScreenChangeMonitor() +{ +} + +bool ScreenChangeMonitor::changedInSeconds() +{ + // 如果上一次的主屏幕的名称为空,则认为未发送屏幕变化的信号 + if (m_changePrimaryName.isEmpty() || !m_changeTime.isValid()) + return false; + + qint64 nowMsec = QDateTime::currentDateTime().toMSecsSinceEpoch(); + qint64 preChangeMsec = m_changeTime.toMSecsSinceEpoch(); + return (nowMsec - preChangeMsec <= TIMESPAN); +} + +bool ScreenChangeMonitor::needUsedLastScreen() const +{ + if (m_lastScreenName.isEmpty()) + return false; + + // 判断是否在关闭显示器的时候由系统发出来的先禁用显示器再开启显示器 + if (!m_changeTime.isValid() || !m_removeTime.isValid() || !m_newTime.isValid()) + return false; + + // 如果禁用的掉的显示器和开启的显示器的名称不一样,则认为他们是正常的插拔操作 + if (m_removeScreenName != m_newScreenName) + return false; + + // 如果先插入显示器再拔掉显示器,则认为他们也是正常的插拔操作 + if (m_removeTime > m_newTime) + return false; + + // 只有在remove显示器在add显示器之前且时间小于1.5秒,才认为他们是由关闭显示器引起的 + qint64 changeMsec = m_changeTime.toMSecsSinceEpoch(); + + // 获取现在的时间,如果现在的时间大于这三个时间一定的值,则认为是手动操作 + qint64 nowMsec = QDateTime::currentDateTime().toMSecsSinceEpoch(); + if (nowMsec - changeMsec > TIMESPAN) + return false; + + qint64 addedMsec = m_newTime.toMSecsSinceEpoch(); + qint64 removeMsec = m_removeTime.toMSecsSinceEpoch(); + return ((addedMsec - removeMsec <= TIMESPAN) && qAbs(removeMsec - changeMsec) <= TIMESPAN); +} + +const QString ScreenChangeMonitor::lastScreen() +{ + return m_lastScreenName; +} diff --git a/frame/util/multiscreenworker.h b/frame/util/multiscreenworker.h index 84f7b0523..5472d138e 100644 --- a/frame/util/multiscreenworker.h +++ b/frame/util/multiscreenworker.h @@ -59,6 +59,7 @@ class QWidget; class QTimer; class MainWindow; class QGSettings; +class ScreenChangeMonitor; /** * @brief The DockScreen class @@ -261,6 +262,7 @@ private: QTimer *m_delayWakeTimer; // sp3需求,切换屏幕显示延时,默认2秒唤起任务栏 DockScreen m_ds; // 屏幕名称信息 + ScreenChangeMonitor *m_screenMonitor; // 用于监视屏幕是否为系统先拔再插 // 任务栏属性 double m_opacity; @@ -282,4 +284,35 @@ private: /*****************************************************************/ }; +/** + * 在控制中心设置了自动关闭显示器,等一段时间后,显示器会自动关闭。在多显示器的情况下,此时系统会自动禁用主屏 + * 马上又开启主屏(底层系统是这么设计的),唤醒后,在禁用主屏后,后端会改变主屏为另外一个显示器,再开启主屏, + * 后端又改变主屏为原来的主屏,这样就会导致的问题是:本来任务栏在主屏,由于瞬间触发了删除主屏幕的操作,引起了 + * 任务栏显示到副屏的问题,开启主屏后,由于任务栏已经在副屏了,就再也回不到主屏,因此,增加了这个类用于专门来 + * 处理这种异常情况 + */ +class ScreenChangeMonitor : public QObject +{ + Q_OBJECT + +public: + ScreenChangeMonitor(DockScreen *ds, QObject *parent); + ~ScreenChangeMonitor(); + bool needUsedLastScreen() const; + const QString lastScreen(); + +private: + bool changedInSeconds(); + +private: + QString m_lastScreenName; + + QString m_changePrimaryName; + QDateTime m_changeTime; + QString m_newScreenName; + QDateTime m_newTime; + QString m_removeScreenName; + QDateTime m_removeTime; +}; + #endif // MULTISCREENWORKER_H