Files
qt6-migration-pipeline/Lts/Common/LtsMultiMonitor/lts_multi_monitor.cpp
T

697 lines
14 KiB
C++
Executable File

#include "lts_multi_monitor.h"
#include "lts_multi_monitor_selector_normal.h"
#include "lts_multi_monitor_selector_wide.h"
#include "lts_screen_normal.h"
#include "lts_screen_wide.h"
#include "lts_dummy_widget.h"
#include "lts_screen_container.h"
#include <LtsWidgets/lts_message_dialog.h>
#include <LtsWidgets/lts_base_layout_widget.h>
#include <LtsSession/lts_session_data.h>
#include <QGuiApplication>
#include <QScreen>
#include <QPointer>
#include <QWidget>
#include <QRect>
#include <QMessageBox>
#include <QDebug>
#ifdef Q_OS_WIN
#include <QtPlatformHeaders/qwindowswindowfunctions.h>
#endif
namespace LtsMultiMonitor
{
static LtsMultiMonitor *s_ltsMultiMonitor = nullptr;
LtsMultiMonitor *LtsMultiMonitor::getInstance()
{
if (!s_ltsMultiMonitor)
{
s_ltsMultiMonitor = new LtsMultiMonitor;
qAddPostRoutine(&cleanupLtsMultiMonitor);
}
return s_ltsMultiMonitor;
}
void LtsMultiMonitor::cleanupLtsMultiMonitor()
{
delete s_ltsMultiMonitor;
s_ltsMultiMonitor = nullptr;
}
LtsMultiMonitor::LtsMultiMonitor()
: QObject(nullptr)
, m_mainScreen(nullptr)
, m_active(false)
, m_initialized(false)
, m_skipScreenId(-1)
, m_subSkipScreenId(-1)
, m_overlapBaseLayouts(false)
, m_debugMargins(false)
{}
LtsMultiMonitor::~LtsMultiMonitor()
{
for (auto i = m_screens.begin(); i != m_screens.end(); ++i)
{
LtsScreenBase *screen = *i;
disconnect(screen, &QObject::destroyed, this, &LtsMultiMonitor::screenDied);
screen->deleteLater();
}
}
void LtsMultiMonitor::screenDied()
{
Q_ASSERT(false);
}
void LtsMultiMonitor::init()
{
if (LtsSessionData::LtsSessionData::getInstance()->isTablet())
{
m_overlapBaseLayouts = true;
m_initialized = true;
return;
}
if (m_initialized || !m_active)
return;
QList<QScreen *> screenList = QGuiApplication::screens();
const QScreen &mainScreen = *QGuiApplication::primaryScreen();
for (auto i = screenList.constBegin(); i != screenList.constEnd(); ++i)
{
const QScreen &screen = **i;
bool found = false;
for (LtsScreenBase *ltsScreen : qAsConst(m_screens))
{
if (ltsScreen->serialNumber() == screen.name())
{
found = true;
break;
}
}
if (found)
continue;
int screenId = m_nextScreenId++;
QRect screenGeo(screen.geometry());
if (screenId == m_skipScreenId)
{
if (m_subSkipScreenId == 0)
{
QRect newScreenGeo(screenGeo);
newScreenGeo.moveLeft(screenGeo.x() + (screenGeo.width() / 2));
newScreenGeo.setWidth(screenGeo.width() / 2);
screenGeo = newScreenGeo;
}
else if (m_subSkipScreenId == 1)
{
QRect newScreenGeo(screenGeo);
newScreenGeo.setWidth(screenGeo.width() / 2);
screenGeo = newScreenGeo;
}
else
{
continue;
}
}
bool isUltraWideView = (screenGeo.width() > 3000);
LtsScreenBase *newScreen;
if (isUltraWideView)
{
newScreen = new LtsScreenWide(screenGeo, screenId, m_debugMargins, screen.name());
}
else
{
auto *newScreenNormal = new LtsScreenNormal(std::move(screenGeo), screenId, m_debugMargins, screen.name());
newScreen = newScreenNormal;
connect(newScreenNormal, &LtsScreenNormal::signalMakeAllSwole,
this, &LtsMultiMonitor::signalMakeAllSwole);
connect(this, &LtsMultiMonitor::signalMakeAllSwole,
newScreenNormal, &LtsScreenNormal::signalMakeSwole);
}
connect(newScreen, &QObject::destroyed, this, &LtsMultiMonitor::screenDied);
if ((&mainScreen) == (&screen))
m_mainScreen = newScreen;
m_screens.append(newScreen);
}
if (m_screens.isEmpty())
{
QMessageBox::critical(nullptr, QString(), tr("No screens found - cannot start application."));
abort();
}
if (m_mainScreen == nullptr)
m_mainScreen = m_screens.first();
m_initialized = true;
emit signalScreenCountChanged(screenCount());
}
int LtsMultiMonitor::screenCount() const
{
return m_screens.count();
}
bool LtsMultiMonitor::cancelAndProcessIfInactive(QWidget *widget) const
{
if (m_active)
return false;
widget->activateWindow();
widget->show();
place(widget);
return true;
}
void LtsMultiMonitor::place(QWidget *widget)
{
auto *blw = qobject_cast<LtsWidgets::LtsBaseLayoutWidget *>(widget);
if (blw != nullptr)
emit blw->signalSuccessfullyPlaced();
}
bool LtsMultiMonitor::putScreen(QWidget *widget, int screenId, int screenPart)
{
Q_ASSERT(widget != nullptr);
if (widget == nullptr)
return false;
if (cancelAndProcessIfInactive(widget))
return true;
init();
if (overlapBaseLayouts(widget))
return true;
bool isSecondary = isWidgetSecondary(widget);
qWarning() << "putScreen: isSecondary" << isSecondary;
for (LtsScreenBase *screen : qAsConst(m_screens))
{
if (screen->screenId() != screenId)
continue;
screen->switchToTemp(isSecondary ? LtsScreenBase::WidgetType::SecondaryWidget : LtsScreenBase::WidgetType::MainWidget);
try
{
screen->putWidget(widget, screenPart);
}
catch (...)
{
screen->revertSwitch();
return selectScreen(widget);
}
screen->check();
place(widget);
screen->finalizeSwitch();
return true;
}
return selectScreen(widget);
}
bool LtsMultiMonitor::putScreenAutomatically(QWidget *widget)
{
if (widget == nullptr)
return false;
if (cancelAndProcessIfInactive(widget))
return true;
init();
if (overlapBaseLayouts(widget))
return true;
for (LtsScreenBase *screen : qAsConst(m_screens))
{
if (screen->screenId() == m_skipScreenId || !screen->isEmpty())
continue;
screen->switchToTemp(LtsScreenBase::WidgetType::SecondaryWidget);
try
{
screen->putWidget(widget, 1);
}
catch (...)
{
try
{
screen->putWidget(widget, 2);
}
catch (...)
{
screen->revertSwitch();
continue;
}
}
screen->check();
place(widget);
screen->finalizeSwitch();
return true;
}
return false;
}
bool LtsMultiMonitor::isOverlapBaseLayouts() const
{
return (m_overlapBaseLayouts );
}
void LtsMultiMonitor::freezeMagicCorner(int screenId)
{
for (auto *screen : qAsConst(m_screens))
{
if (screen->screenId() == screenId)
{
LtsScreenNormal* normalScreen = qobject_cast<LtsScreenNormal*>(screen);
if(normalScreen->child()->hasMainWidget() && normalScreen->child()->hasSecondaryWidget())
{
normalScreen->hideMagicCorner(true);
if(normalScreen->child()->activeWidgetType() == LtsScreenBase::WidgetType::MainWidget)
normalScreen->child()->toggle();
}
}
}
}
void LtsMultiMonitor::unfreezeMagicCorner(int screenId)
{
for (auto *screen : qAsConst(m_screens))
{
if (screen->screenId() == screenId)
{
LtsScreenNormal* normalScreen = qobject_cast<LtsScreenNormal*>(screen);
if(normalScreen->child()->hasMainWidget() && normalScreen->child()->hasSecondaryWidget())
normalScreen->hideMagicCorner(false);
}
}
}
bool LtsMultiMonitor::overlapBaseLayouts(QWidget *widget)
{
if (!isOverlapBaseLayouts())
return false;
Q_ASSERT(qobject_cast<FullscreenVideoDummyWidget *>(widget) == nullptr);
if (qobject_cast<FullscreenVideoDummyWidget *>(widget) != nullptr)
{
return false;
}
#ifdef Q_OS_WIN
widget->winId();
QWindowsWindowFunctions::setHasBorderInFullScreen(widget->windowHandle(), true);
#endif
widget->activateWindow();
widget->showFullScreen();
place(widget);
return true;
}
bool LtsMultiMonitor::selectAndSwap(QWidget *widget)
{
if (!m_active)
return false;
Q_ASSERT(!isOverlapBaseLayouts());
if (isOverlapBaseLayouts())
return false;
LtsScreenContainer *oldContainer = nullptr;
for (LtsScreenBase *_screen : qAsConst(m_screens))
{
try
{
oldContainer = _screen->containerOfWidget(widget);
}
catch (...){}
}
if (oldContainer == nullptr)
{
LtsWidgets::LtsMessageDialog::showMessage(widget, tr("Could not determine screen id for this swap!<br>Swapping not possible, please try again later."));
return false;
}
return selectScreen(widget, oldContainer);
}
LtsScreenBase *LtsMultiMonitor::widgetsScreen(QWidget *widget)
{
for (LtsScreenBase *screen : qAsConst(m_screens))
if (screen->hasWidget(widget))
return screen;
return nullptr;
}
bool LtsMultiMonitor::isWidgetSecondary(QWidget *widget)
{
#ifndef GUEZ_MODE
return false;
#endif
auto *blw = qobject_cast<LtsWidgets::LtsBaseLayoutWidget *>(widget);
auto *secondaryWidget = qobject_cast<SecondaryWidget *>(widget);
bool isSecondaryWidget = ((blw != nullptr && blw->isSecondaryWidget()) || secondaryWidget != nullptr);
return isSecondaryWidget;
}
class StupidDeleter
{
public:
explicit StupidDeleter(LtsScreenBase *screen)
: m_screen(screen)
{}
~StupidDeleter()
{
if (m_screen != nullptr)
{
m_screen->revertSwitch();
}
}
void reset()
{
m_screen = nullptr;
}
private:
LtsScreenBase *m_screen;
};
bool LtsMultiMonitor::selectScreen(QWidget *widget, LtsScreenContainer *swap, bool putOnMainIfFree, bool ltsScreenNormalOnly, const QVector<QWidget *> &irreplacableWidgets)
{
Q_ASSERT(widget != nullptr);
if (widget == nullptr)
return false;
if (cancelAndProcessIfInactive(widget))
return true;
init();
if (overlapBaseLayouts(widget))
return true;
bool isSecondary = isWidgetSecondary(widget);
if (!isSecondary)
{
bool allEmpty = putOnMainIfFree;
for (LtsScreenBase *screen : qAsConst(m_screens))
{
screen->switchToTemp(LtsScreenBase::WidgetType::MainWidget);
bool isEmpty = screen->isEmpty();
screen->revertSwitch();
if (isEmpty)
continue;
allEmpty = false;
break;
}
if (allEmpty)
{
m_mainScreen->switchToTemp(LtsScreenBase::WidgetType::MainWidget);
try
{
m_mainScreen->putWidget(widget);
}
catch (...)
{
qWarning() << "failed - reverting";
m_mainScreen->revertSwitch();
Q_ASSERT_X(false, __FUNCTION__, "screen promised to be empty but 'putWidgetSomewhere' method failed regardless");
auto *blw = qobject_cast<LtsWidgets::LtsBaseLayoutWidget *>(widget);
if (blw != nullptr)
emit blw->signalClose();
else
widget->deleteLater();
return false;
}
m_mainScreen->check();
place(widget);
m_mainScreen->finalizeSwitch();
return true;
}
}
auto *selectionProcessWatcher = new QObject;
bool minOnce = false;
for (auto i = m_screens.begin(); i != m_screens.end(); ++i)
{
LtsScreenBase *screen = *i;
screen->switchToTemp(isSecondary ? LtsScreenBase::WidgetType::SecondaryWidget : LtsScreenBase::WidgetType::MainWidget);
StupidDeleter _(screen);
if (screen->isIrreplaceable())
{
continue;
}
bool skip = false;
for (QWidget *widget : qAsConst(irreplacableWidgets))
{
if (screen->hasWidget(widget))
{
skip = true;
continue;
}
}
if (skip)
continue;
MultiMonitorSelectorBase *mms;
if (qobject_cast<LtsScreenNormal *>(screen) != nullptr)
{
auto *screenNormal = static_cast<LtsScreenNormal *>(screen);
mms = new MultiMonitorSelectorNormal(screenNormal, isSecondary, irreplacableWidgets);
}
else if (qobject_cast<LtsScreenWide *>(screen) != nullptr && !ltsScreenNormalOnly)
{
auto *screenWide = static_cast<LtsScreenWide *>(screen);
mms = new MultiMonitorSelectorWide(screenWide, nullptr, irreplacableWidgets);
}
else
continue;
QPointer<QWidget> widgetPtr(widget);
connect(widget, &QWidget::destroyed,
mms, &MultiMonitorSelectorBase::close);
connect(mms, &MultiMonitorSelectorBase::rejected,
this, [widgetPtr, swap]()
{
if (!widgetPtr || swap != nullptr)
return;
auto *blw = qobject_cast<LtsWidgets::LtsBaseLayoutWidget *>(widgetPtr.data());
if (blw != nullptr)
emit blw->signalClose();
else
widgetPtr->deleteLater();
});
connect(mms, &MultiMonitorSelectorBase::signalSelected,
this, [this, mms, widgetPtr]()
{
for (LtsScreenBase *screen : qAsConst(m_screens))
screen->check();
if (widgetPtr)
place(widgetPtr.data());
mms->deleteLater();
emit signalSelected();
});
connect(mms, &MultiMonitorSelectorBase::destroyed,
selectionProcessWatcher, &QObject::deleteLater);
connect(selectionProcessWatcher, &QObject::destroyed,
mms, &MultiMonitorSelectorBase::close);
mms->setSwap(swap);
mms->setWidget(widget);
mms->show();
_.reset();
minOnce = true;
}
if (!minOnce)
{
selectionProcessWatcher->deleteLater();
auto *blw = qobject_cast<LtsWidgets::LtsBaseLayoutWidget *>(widget);
if (blw != nullptr)
emit blw->signalClose();
else
widget->deleteLater();
LtsWidgets::LtsMessageDialog::showMessage(m_mainScreen->screenDialog(), tr("There are no free screens available to select from."));
return false;
}
return true;
}
}