Qt: Improve widget device pixel ratio detection

Fixes forced integer scaling when running under Wayland.

Why on earth QScreen is giving integer DPRs is beyond me. But my
reasoning for pulling the DPR from here dates back to 2020, so it
was probably a Qt5 thing.
This commit is contained in:
Stenzek
2025-11-30 23:41:00 +10:00
parent df75127fc8
commit 11a0abaa83
6 changed files with 26 additions and 26 deletions

View File

@@ -169,6 +169,8 @@ void DisplayWidget::checkForSizeChange()
const auto& [size, dpr] = QtUtils::GetPixelSizeForWidget(this);
if (m_last_window_size != size || m_last_window_scale != dpr)
{
DEV_LOG("Display widget resized to {}x{} (Qt {}x{}) DPR={}", size.width(), size.height(), width(), height(), dpr);
m_last_window_size = size;
m_last_window_scale = dpr;
emit windowResizedEvent(size.width(), size.height(), static_cast<float>(dpr));
@@ -521,10 +523,9 @@ bool AuxiliaryDisplayWidget::event(QEvent* event)
case QEvent::MouseMove:
{
const qreal dpr = QtUtils::GetDevicePixelRatioForWidget(this);
const QPoint mouse_pos = static_cast<QMouseEvent*>(event)->pos();
const float scaled_x = static_cast<float>(static_cast<qreal>(mouse_pos.x()) * dpr);
const float scaled_y = static_cast<float>(static_cast<qreal>(mouse_pos.y()) * dpr);
const float scaled_x = static_cast<float>(static_cast<qreal>(mouse_pos.x()) * m_last_window_scale);
const float scaled_y = static_cast<float>(static_cast<qreal>(mouse_pos.y()) * m_last_window_scale);
g_emu_thread->queueAuxiliaryRenderWindowInputEvent(
m_userdata, Host::AuxiliaryRenderWindowEvent::MouseMoved,
@@ -583,6 +584,9 @@ bool AuxiliaryDisplayWidget::event(QEvent* event)
const auto& [size, dpr] = QtUtils::GetPixelSizeForWidget(this);
if (m_last_window_size != size || m_last_window_scale != dpr)
{
DEV_LOG("Display widget resized to {}x{} (Qt {}x{}) DPR={}", size.width(), size.height(), width(), height(),
dpr);
m_last_window_size = size;
m_last_window_scale = dpr;
g_emu_thread->queueAuxiliaryRenderWindowInputEvent(

View File

@@ -222,7 +222,7 @@ const char* GameListModel::getColumnName(Column col)
}
GameListModel::GameListModel(GameListWidget* parent)
: QAbstractTableModel(parent), m_device_pixel_ratio(QtUtils::GetDevicePixelRatioForWidget(parent)),
: QAbstractTableModel(parent), m_device_pixel_ratio(parent->devicePixelRatio()),
m_icon_pixmap_cache(MIN_COVER_CACHE_SIZE)
{
m_cover_scale = Host::GetBaseFloatSettingValue("UI", "GameListCoverArtScale", DEFAULT_COVER_SCALE);
@@ -232,7 +232,7 @@ GameListModel::GameListModel(GameListWidget* parent)
m_show_game_icons = Host::GetBaseBoolSettingValue("UI", "GameListShowGameIcons", true);
loadCommonImages();
updateCoverScale();
loadCoverScaleDependentPixmaps();
if (m_show_game_icons)
GameList::ReloadMemcardTimestampCache();
@@ -332,7 +332,7 @@ void GameListModel::setCoverScale(float scale)
updateCoverScale();
}
void GameListModel::updateCoverScale()
void GameListModel::loadCoverScaleDependentPixmaps()
{
QImage loading_image;
if (loading_image.load(QtHost::GetResourceQPath("images/placeholder.png", true)))
@@ -360,7 +360,11 @@ void GameListModel::updateCoverScale()
m_placeholder_image.setDevicePixelRatio(m_device_pixel_ratio);
m_placeholder_image.fill(QColor(0, 0, 0, 0));
}
}
void GameListModel::updateCoverScale()
{
loadCoverScaleDependentPixmaps();
emit coverScaleChanged(m_cover_scale);
emit dataChanged(index(0, Column_Cover), index(rowCount() - 1, Column_Cover),
{Qt::DecorationRole, Qt::FontRole, Qt::SizeHintRole});
@@ -415,6 +419,7 @@ void GameListModel::setDevicePixelRatio(qreal dpr)
m_loading_pixmap.setDevicePixelRatio(dpr);
m_flag_pixmap_cache.clear();
loadCommonImages();
loadCoverScaleDependentPixmaps();
refreshCovers();
refreshIcons();
}
@@ -2262,7 +2267,7 @@ bool GameListWidget::event(QEvent* e)
if (type == QEvent::Resize)
updateBackground(false);
else if (type == QEvent::DevicePixelRatioChange)
m_model->setDevicePixelRatio(QtUtils::GetDevicePixelRatioForWidget(this));
m_model->setDevicePixelRatio(devicePixelRatio());
return QWidget::event(e);
}

View File

@@ -144,6 +144,7 @@ private:
void loadCommonImages();
void loadSizeDependentPixmaps();
void updateCoverScale();
void loadCoverScaleDependentPixmaps();
void loadOrGenerateCover(const GameList::Entry* ge);
void invalidateCoverForPath(const std::string& path);
void coverLoaded(const std::string& path, const QImage& image, float scale);

View File

@@ -226,7 +226,7 @@ bool MemoryCardEditorWindow::event(QEvent* event)
{
if (event->type() == QEvent::DevicePixelRatioChange)
{
const qreal dpr = QtUtils::GetDevicePixelRatioForWidget(this);
const qreal dpr = devicePixelRatio();
MemoryCardEditorIconStyleDelegate::getForView(m_card_a.table)->setDevicePixelRatio(dpr);
MemoryCardEditorIconStyleDelegate::getForView(m_card_b.table)->setDevicePixelRatio(dpr);
}
@@ -261,7 +261,7 @@ void MemoryCardEditorWindow::connectCardUi(Card* card, QDialogButtonBox* buttonB
void MemoryCardEditorWindow::connectUi()
{
const qreal dpr = QtUtils::GetDevicePixelRatioForWidget(this);
const qreal dpr = devicePixelRatio();
m_ui.cardA->setItemDelegateForColumn(
0, new MemoryCardEditorIconStyleDelegate(m_card_a.files, dpr, m_current_frame_index, m_ui.cardA));
m_ui.cardB->setItemDelegateForColumn(

View File

@@ -501,15 +501,6 @@ QSize QtUtils::GetDeviceIndependentSize(const QSize& size, qreal device_pixel_ra
std::max(static_cast<int>(std::ceil(static_cast<qreal>(size.height()) / device_pixel_ratio)), 1));
}
qreal QtUtils::GetDevicePixelRatioForWidget(const QWidget* widget)
{
const QScreen* screen_for_ratio = widget->screen();
if (!screen_for_ratio)
screen_for_ratio = QGuiApplication::primaryScreen();
return screen_for_ratio ? screen_for_ratio->devicePixelRatio() : static_cast<qreal>(1);
}
std::pair<QSize, qreal> QtUtils::GetPixelSizeForWidget(const QWidget* widget)
{
// Why this nonsense? Qt's device independent sizes are integer, and fractional scaling is lossy.
@@ -517,7 +508,7 @@ std::pair<QSize, qreal> QtUtils::GetPixelSizeForWidget(const QWidget* widget)
#if defined(_WIN32)
if (RECT rc; GetClientRect(reinterpret_cast<HWND>(widget->winId()), &rc))
{
const qreal device_pixel_ratio = GetDevicePixelRatioForWidget(widget);
const qreal device_pixel_ratio = widget->devicePixelRatio();
return std::make_pair(QSize(static_cast<int>(rc.right - rc.left), static_cast<int>(rc.bottom - rc.top)),
device_pixel_ratio);
}
@@ -536,7 +527,7 @@ std::pair<QSize, qreal> QtUtils::GetPixelSizeForWidget(const QWidget* widget)
if (std::optional<std::pair<int, int>> size =
CocoaTools::GetViewSizeInPixels(reinterpret_cast<void*>(widget->winId())))
{
const qreal device_pixel_ratio = GetDevicePixelRatioForWidget(widget);
const qreal device_pixel_ratio = widget->devicePixelRatio();
return std::make_pair(QSize(size->first, size->second), device_pixel_ratio);
}
}
@@ -544,7 +535,7 @@ std::pair<QSize, qreal> QtUtils::GetPixelSizeForWidget(const QWidget* widget)
// On Linux, fuck you, enjoy round trip to the X server, and on Wayland you can't query it in the first place...
// I ain't dealing with this crap OS. Enjoy your mismatched sizes and shit experience.
const qreal device_pixel_ratio = GetDevicePixelRatioForWidget(widget);
const qreal device_pixel_ratio = widget->devicePixelRatio();
return std::make_pair(ApplyDevicePixelRatioToSize(widget->size(), device_pixel_ratio), device_pixel_ratio);
}
@@ -613,7 +604,9 @@ std::optional<WindowInfo> QtUtils::GetWindowInfoForWidget(QWidget* widget, Rende
}
wi.surface_refresh_rate = surface_refresh_rate.value();
INFO_LOG("Surface refresh rate: {} hz", wi.surface_refresh_rate);
INFO_LOG("Window size: {}x{} (Qt {}x{}), scale: {}, refresh rate {} hz", wi.surface_width, wi.surface_height,
widget->width(), widget->height(), wi.surface_scale, wi.surface_refresh_rate);
return wi;
}

View File

@@ -162,11 +162,8 @@ QSize ApplyDevicePixelRatioToSize(const QSize& size, qreal device_pixel_ratio);
/// Removes the device pixel ratio from the given size, giving the size in device-independent units.
QSize GetDeviceIndependentSize(const QSize& size, qreal device_pixel_ratio);
/// Returns the pixel ratio/scaling factor for a widget.
qreal GetDevicePixelRatioForWidget(const QWidget* widget);
/// Returns the pixel size (real geometry) for a widget.
/// Also returns the "real" DPR scale for the widget, ignoring any operating-system level downsampling.
/// Also returns the "real" DPR scale for the widget, ignoring any operating-system level downsampling.
std::pair<QSize, qreal> GetPixelSizeForWidget(const QWidget* widget);
/// Returns the common window info structure for a Qt widget.