#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 #include #include #include #include #include #include #include #include #include #ifdef Q_OS_WIN #include #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 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(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(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(screen); if(normalScreen->child()->hasMainWidget() && normalScreen->child()->hasSecondaryWidget()) normalScreen->hideMagicCorner(false); } } } bool LtsMultiMonitor::overlapBaseLayouts(QWidget *widget) { if (!isOverlapBaseLayouts()) return false; Q_ASSERT(qobject_cast(widget) == nullptr); if (qobject_cast(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!
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(widget); auto *secondaryWidget = qobject_cast(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 &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(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(screen) != nullptr) { auto *screenNormal = static_cast(screen); mms = new MultiMonitorSelectorNormal(screenNormal, isSecondary, irreplacableWidgets); } else if (qobject_cast(screen) != nullptr && !ltsScreenNormalOnly) { auto *screenWide = static_cast(screen); mms = new MultiMonitorSelectorWide(screenWide, nullptr, irreplacableWidgets); } else continue; QPointer 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(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(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; } }