-
-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Qt: Add display monitor selector #14196
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 3 commits
c0dca9c
86422df
5323acf
84d506e
b008615
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2837,6 +2837,18 @@ void MainWindow::createDisplayWidget(bool fullscreen, bool render_to_main) | |
| } | ||
|
|
||
| m_display_surface = new DisplaySurface(); | ||
|
|
||
| const int monitor_index = Host::GetBaseIntSettingValue("UI", "DisplayMonitor", 0); | ||
| const QList<QScreen*> screens = QGuiApplication::screens(); | ||
| QScreen* target_screen; | ||
| if (monitor_index == 1) | ||
| target_screen = QGuiApplication::primaryScreen(); | ||
| //Separator from UI takes up index 2 | ||
| else if (monitor_index >= 3 && (monitor_index - 3) < screens.size()) | ||
| target_screen = screens[monitor_index - 3]; | ||
| else | ||
| target_screen = screen(); | ||
|
|
||
| if (fullscreen || !render_to_main) | ||
| { | ||
| #ifdef DISPLAY_SURFACE_WINDOW | ||
|
|
@@ -2860,19 +2872,20 @@ void MainWindow::createDisplayWidget(bool fullscreen, bool render_to_main) | |
|
|
||
| #ifdef DISPLAY_SURFACE_WINDOW | ||
| if (isVisible() && g_emu_thread->shouldRenderToMain()) | ||
| m_display_surface->setGeometry(screen()->geometry()); | ||
| m_display_surface->setGeometry(target_screen->geometry()); | ||
| else | ||
| restoreDisplayWindowGeometryFromConfig(); | ||
| restoreDisplayWindowGeometryFromConfig(target_screen); | ||
|
|
||
| if (fullscreen) | ||
| m_display_surface->showFullScreen(); | ||
| else | ||
| m_display_surface->showNormal(); | ||
| #else | ||
| m_display_container->setScreen(target_screen); | ||
| if (isVisible() && g_emu_thread->shouldRenderToMain()) | ||
| m_display_container->move(screen()->availableGeometry().topLeft()); | ||
| m_display_container->move(target_screen->availableGeometry().topLeft()); | ||
| else | ||
| restoreDisplayWindowGeometryFromConfig(); | ||
| restoreDisplayWindowGeometryFromConfig(target_screen); | ||
|
|
||
| if (fullscreen) | ||
| m_display_container->showFullScreen(); | ||
|
|
@@ -2886,18 +2899,34 @@ void MainWindow::createDisplayWidget(bool fullscreen, bool render_to_main) | |
| if (m_is_temporarily_windowed && g_emu_thread->shouldRenderToMain()) | ||
| m_display_surface->setGeometry(geometry()); | ||
| else | ||
| restoreDisplayWindowGeometryFromConfig(); | ||
| restoreDisplayWindowGeometryFromConfig(target_screen); | ||
| m_display_surface->showNormal(); | ||
| #else | ||
| m_display_container->setScreen(target_screen); | ||
| if (m_is_temporarily_windowed && g_emu_thread->shouldRenderToMain()) | ||
| m_display_container->setGeometry(geometry()); | ||
| else | ||
| restoreDisplayWindowGeometryFromConfig(); | ||
| restoreDisplayWindowGeometryFromConfig(target_screen); | ||
| m_display_container->showNormal(); | ||
| #endif | ||
| } | ||
| else | ||
| { | ||
| if (screen() != target_screen) | ||
| { | ||
| m_pre_game_main_window_geometry = saveGeometry(); | ||
|
|
||
| if (!m_target_screen_main_window_geometry.isEmpty()) | ||
| restoreGeometry(m_target_screen_main_window_geometry); | ||
|
|
||
| if (m_target_screen_main_window_geometry.isEmpty() || | ||
| !target_screen->availableGeometry().contains(geometry().center())) | ||
| { | ||
| const QRect screenGeo = target_screen->availableGeometry(); | ||
| move(screenGeo.x() + (screenGeo.width() - width()) / 2, | ||
| screenGeo.y() + (screenGeo.height() - height()) / 2); | ||
| } | ||
| } | ||
| pxAssertRel(m_ui.mainContainer->count() == 1, "Has no display widget"); | ||
| m_ui.mainContainer->addWidget(m_display_container); | ||
| m_ui.mainContainer->setCurrentIndex(1); | ||
|
|
@@ -3005,6 +3034,24 @@ void MainWindow::destroyDisplayWidget(bool show_game_list) | |
| { | ||
| pxAssertRel(m_ui.mainContainer->indexOf(m_display_container) == 1, "Display widget in stack"); | ||
| m_ui.mainContainer->removeWidget(m_display_container); | ||
| if (!m_pre_game_main_window_geometry.isEmpty()) | ||
| { | ||
| const int monitor_index = Host::GetBaseIntSettingValue("UI", "DisplayMonitor", 0); | ||
| const QList<QScreen*> screens = QGuiApplication::screens(); | ||
| QScreen* target_screen; | ||
| if (monitor_index == 1) | ||
| target_screen = QGuiApplication::primaryScreen(); | ||
| else if (monitor_index >= 3 && (monitor_index - 3) < screens.size()) | ||
| target_screen = screens[monitor_index - 3]; | ||
| else | ||
| target_screen = nullptr; | ||
| if (target_screen && screen() == target_screen) | ||
| m_target_screen_main_window_geometry = saveGeometry(); | ||
| else | ||
| m_target_screen_main_window_geometry.clear(); | ||
| restoreGeometry(m_pre_game_main_window_geometry); | ||
| m_pre_game_main_window_geometry.clear(); | ||
| } | ||
| if (show_game_list) | ||
| { | ||
| m_ui.mainContainer->setCurrentIndex(0); | ||
|
|
@@ -3116,10 +3163,11 @@ void MainWindow::saveDisplayWindowGeometryToConfig() | |
| } | ||
| } | ||
|
|
||
| void MainWindow::restoreDisplayWindowGeometryFromConfig() | ||
| void MainWindow::restoreDisplayWindowGeometryFromConfig(QScreen* target_screen) | ||
| { | ||
| const std::string geometry_b64 = Host::GetBaseStringSettingValue("UI", "DisplayWindowGeometry"); | ||
| const QByteArray geometry = QByteArray::fromBase64(QByteArray::fromStdString(geometry_b64)); | ||
|
|
||
| if (!geometry.isEmpty()) | ||
| { | ||
| m_display_surface->restoreGeometry(geometry); | ||
|
|
@@ -3138,6 +3186,28 @@ void MainWindow::restoreDisplayWindowGeometryFromConfig() | |
| m_display_surface->resize(640, 480); | ||
| #else | ||
| m_display_container->resize(640, 480); | ||
| #endif | ||
| } | ||
|
|
||
| // if a target screen is specified and the window is not on it, center on it | ||
| if (target_screen) | ||
| { | ||
| #ifdef DISPLAY_SURFACE_WINDOW | ||
| if (!target_screen->availableGeometry().contains(m_display_surface->geometry())) | ||
| { | ||
| const QRect screenGeo = target_screen->availableGeometry(); | ||
| const QPoint center(screenGeo.x() + (screenGeo.width() - m_display_surface->width()) / 2, | ||
| screenGeo.y() + (screenGeo.height() - m_display_surface->height()) / 2); | ||
| m_display_surface->setGeometry(QRect(center, m_display_surface->size())); | ||
| } | ||
| #else | ||
| if (!target_screen->availableGeometry().contains(m_display_container->geometry())) | ||
| { | ||
| const QRect screenGeo = target_screen->availableGeometry(); | ||
| const QPoint center(screenGeo.x() + (screenGeo.width() - m_display_container->width()) / 2, | ||
| screenGeo.y() + (screenGeo.height() - m_display_container->height()) / 2); | ||
| m_display_container->setGeometry(QRect(center, m_display_container->size())); | ||
| } | ||
| #endif | ||
| } | ||
| } | ||
|
|
@@ -3165,6 +3235,11 @@ void MainWindow::doSettings(const char* category /* = nullptr */) | |
| if (!dlg->isVisible()) | ||
| { | ||
| dlg->show(); | ||
| if (dlg->screen() != screen()) | ||
| { | ||
| const QRect screenGeo = screen()->availableGeometry(); | ||
| dlg->move(screenGeo.center() - QPoint(dlg->frameGeometry().width() / 2, dlg->frameGeometry().height() / 2)); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. maybe slightly easier to use: Also it's probably(?) more efficient to do this before calling
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll try it and see it if it works.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
dlg->frameGeometry() returns a copy of Qrect of the settings window so calling moveCenter() on it won't move the settings window directly.
This part is correct, but it's more about the correct frame geometry than visibility. I looked into this a bit more. Before show() is called, frameGeometry() pretty much equals geometry() because the windows manager hasn't decorated the final window so the final size hasn't been determined yet. So if show() is called after frameGeometry(), frameGeometry() will be missing the title bar and borders and our calculations will be wrong. That's why I had to move some showNormals() to get clamping to work correctly.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The line I wrote was just meant as a hint to convey my idea, it's not the full code. Of course you still need to call |
||
| } | ||
| } | ||
| else | ||
| { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -12,6 +12,7 @@ | |
| #include "QtHost.h" | ||
|
|
||
| #include <QtCore/QLocale> | ||
| #include <QtGui/QScreen> | ||
|
|
||
| const char* InterfaceSettingsWidget::THEME_NAMES[] = { | ||
| QT_TRANSLATE_NOOP("InterfaceSettingsWidget", "Native"), | ||
|
|
@@ -123,6 +124,17 @@ InterfaceSettingsWidget::InterfaceSettingsWidget(SettingsWindow* settings_dialog | |
| { | ||
| m_ui.mouseLock->setEnabled(false); | ||
| } | ||
| m_ui.displayMonitor->addItem(tr("Current Monitor")); | ||
| m_ui.displayMonitor->addItem(tr("Primary Monitor")); | ||
| m_ui.displayMonitor->insertSeparator(m_ui.displayMonitor->count()); | ||
| const QList<QScreen*> screens = QGuiApplication::screens(); | ||
| for (int i = 0; i < screens.size(); i++) | ||
| m_ui.displayMonitor->addItem(tr("Monitor %1: %2").arg(i + 1).arg(screens[i]->name())); | ||
| m_ui.displayMonitor->setCurrentIndex(Host::GetBaseIntSettingValue("UI", "DisplayMonitor", 0)); | ||
| connect(m_ui.displayMonitor, &QComboBox::currentIndexChanged, this, [](int index) { | ||
| Host::SetBaseIntSettingValue("UI", "DisplayMonitor", index); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Using the combobox index as persistent screen identifier seems unreliable to me. The documentation of
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Currently if an invalid index is used, the current monitor will be used so we don't have to worry about crashes. There are QT signals for display add and remove so we can have the interface settings update in real time whenever a monitor is connected or disconnected. I haven't pushed that yet, but it seems to be working fine so far. But it is true, there is no guarantee the ordering will stable. We can use the names of the displays... but that won't work in case someone has multiple monitors of the same name. The monitors have to be sorted in a consistent, deterministic order. My idea right now is geometry position, but I haven't tested yet.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I wasn't worried about crashing (the code is already robust against that) but about the wrong monitor being used, e.g., when the index is still valid but refers to a different monitor because the
There is
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. QScreen::serialNumber doesn't work consistently so I went with sorting displays by geometry positions, so the order is always consistent. If a user rearranges their monitors in their OS, a game may launch on an unexpected display, but the interface settings will still reflect the correct index, and they can correct it from there. |
||
| Host::CommitBaseSettingChanges(); | ||
| }); | ||
|
|
||
| SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.startFullscreen, "UI", "StartFullscreen", false); | ||
| SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.doubleClickTogglesFullscreen, "UI", "DoubleClickTogglesFullscreen", true); | ||
|
|
@@ -203,6 +215,10 @@ InterfaceSettingsWidget::InterfaceSettingsWidget(SettingsWindow* settings_dialog | |
| tr("Checked"), tr("Displays a modal dialog when a save state load/save operation fails.")); | ||
| dialog()->registerWidgetHelp(m_ui.preferEnglishGameList, tr("Prefer English Game Titles"), tr("Unchecked"), | ||
| tr("For games with both a title in the game's native language and one in English, prefer the English title. Affects how game titles are displayed on the game list, window title and Discord Presence")); | ||
| dialog()->registerWidgetHelp( | ||
| m_ui.displayMonitor, tr("Display Monitor"), tr("Current Monitor"), | ||
| tr("Selects which monitor the PCSX2 game window will be displayed on when launching." | ||
| "<br><b>On Linux Wayland, this setting only takes effect when Start Fullscreen is enabled.</b>")); | ||
| dialog()->registerWidgetHelp(m_ui.startFullscreen, tr("Start Fullscreen"), tr("Unchecked"), | ||
| tr("Automatically switches to fullscreen mode when a game is started.")); | ||
| dialog()->registerWidgetHelp(m_ui.hideMouseCursor, tr("Hide Cursor In Fullscreen"), tr("Unchecked"), | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it possible that the target screen geometry is smaller than the current window geometry? e.g. moving to a smaller screen. In that case, should we clamp width and height to the available screen geometry?
I suggest writing a helper function for this, since the same logic is used two more times in
restoreDisplayWindowGeometryFromConfig().There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch. We have to clamp slightly a little bit more than the available screen geometry though. availableGeometry() doesn't take to account the title bar, at least on windows, so it's possible it gets cut off. I need to use frameGeometry() to subtract the difference.