Add support for listing notch and notchless native resolution options

This commit is contained in:
Cameron Gutman 2024-05-11 18:24:40 -05:00
parent ed68f920f1
commit 85a9f85c54
6 changed files with 88 additions and 33 deletions

View file

@ -74,6 +74,7 @@ SystemProperties::SystemProperties()
Q_ASSERT(!monitorRefreshRates.isEmpty()); Q_ASSERT(!monitorRefreshRates.isEmpty());
Q_ASSERT(!monitorNativeResolutions.isEmpty()); Q_ASSERT(!monitorNativeResolutions.isEmpty());
Q_ASSERT(!monitorSafeAreaResolutions.isEmpty());
} }
QRect SystemProperties::getNativeResolution(int displayIndex) QRect SystemProperties::getNativeResolution(int displayIndex)
@ -82,6 +83,12 @@ QRect SystemProperties::getNativeResolution(int displayIndex)
return monitorNativeResolutions.value(displayIndex); return monitorNativeResolutions.value(displayIndex);
} }
QRect SystemProperties::getSafeAreaResolution(int displayIndex)
{
// Returns default constructed QRect if out of bounds
return monitorSafeAreaResolutions.value(displayIndex);
}
int SystemProperties::getRefreshRate(int displayIndex) int SystemProperties::getRefreshRate(int displayIndex)
{ {
// Returns 0 if out of bounds // Returns 0 if out of bounds
@ -199,10 +206,12 @@ void SystemProperties::refreshDisplaysInternal()
SDL_DisplayMode bestMode; SDL_DisplayMode bestMode;
for (int displayIndex = 0; displayIndex < SDL_GetNumVideoDisplays(); displayIndex++) { for (int displayIndex = 0; displayIndex < SDL_GetNumVideoDisplays(); displayIndex++) {
SDL_DisplayMode desktopMode; SDL_DisplayMode desktopMode;
SDL_Rect safeArea;
if (StreamUtils::getNativeDesktopMode(displayIndex, &desktopMode)) { if (StreamUtils::getNativeDesktopMode(displayIndex, &desktopMode, &safeArea)) {
if (desktopMode.w <= 8192 && desktopMode.h <= 8192) { if (desktopMode.w <= 8192 && desktopMode.h <= 8192) {
monitorNativeResolutions.insert(displayIndex, QRect(0, 0, desktopMode.w, desktopMode.h)); monitorNativeResolutions.insert(displayIndex, QRect(0, 0, desktopMode.w, desktopMode.h));
monitorSafeAreaResolutions.insert(displayIndex, QRect(0, 0, safeArea.w, safeArea.h));
} }
else { else {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION,

View file

@ -30,6 +30,7 @@ public:
Q_INVOKABLE void refreshDisplays(); Q_INVOKABLE void refreshDisplays();
Q_INVOKABLE QRect getNativeResolution(int displayIndex); Q_INVOKABLE QRect getNativeResolution(int displayIndex);
Q_INVOKABLE QRect getSafeAreaResolution(int displayIndex);
Q_INVOKABLE int getRefreshRate(int displayIndex); Q_INVOKABLE int getRefreshRate(int displayIndex);
signals: signals:
@ -52,6 +53,7 @@ private:
QString unmappedGamepads; QString unmappedGamepads;
QSize maximumResolution; QSize maximumResolution;
QList<QRect> monitorNativeResolutions; QList<QRect> monitorNativeResolutions;
QList<QRect> monitorSafeAreaResolutions;
QList<int> monitorRefreshRates; QList<int> monitorRefreshRates;
QString versionString; QString versionString;
bool supportsHdr; bool supportsHdr;

View file

@ -133,33 +133,18 @@ Flickable {
AutoResizingComboBox { AutoResizingComboBox {
property int lastIndexValue property int lastIndexValue
// ignore setting the index at first, and actually set it when the component is loaded function addDetectedResolution(friendlyNamePrefix, rect) {
Component.onCompleted: {
// Refresh display data before using it to build the list
SystemProperties.refreshDisplays()
// Add native resolutions for all attached displays
var done = false
for (var displayIndex = 0; !done; displayIndex++) {
var screenRect = SystemProperties.getNativeResolution(displayIndex);
if (screenRect.width === 0) {
// Exceeded max count of displays
done = true
break
}
var indexToAdd = 0 var indexToAdd = 0
for (var j = 0; j < resolutionComboBox.count; j++) { for (var j = 0; j < resolutionComboBox.count; j++) {
var existing_width = parseInt(resolutionListModel.get(j).video_width); var existing_width = parseInt(resolutionListModel.get(j).video_width);
var existing_height = parseInt(resolutionListModel.get(j).video_height); var existing_height = parseInt(resolutionListModel.get(j).video_height);
if (screenRect.width === existing_width && screenRect.height === existing_height) { if (rect.width === existing_width && rect.height === existing_height) {
// Duplicate entry, skip // Duplicate entry, skip
indexToAdd = -1 indexToAdd = -1
break break
} }
else if (screenRect.width * screenRect.height > existing_width * existing_height) { else if (rect.width * rect.height > existing_width * existing_height) {
// Candidate entrypoint after this entry // Candidate entrypoint after this entry
indexToAdd = j + 1 indexToAdd = j + 1
} }
@ -169,14 +154,35 @@ Flickable {
if (indexToAdd >= 0) { if (indexToAdd >= 0) {
resolutionListModel.insert(indexToAdd, resolutionListModel.insert(indexToAdd,
{ {
"text": "Native ("+screenRect.width+"x"+screenRect.height+")", "text": friendlyNamePrefix+" ("+rect.width+"x"+rect.height+")",
"video_width": ""+screenRect.width, "video_width": ""+rect.width,
"video_height": ""+screenRect.height, "video_height": ""+rect.height,
"is_custom": false "is_custom": false
}) })
} }
} }
// ignore setting the index at first, and actually set it when the component is loaded
Component.onCompleted: {
// Refresh display data before using it to build the list
SystemProperties.refreshDisplays()
// Add native and safe area resolutions for all attached displays
var done = false
for (var displayIndex = 0; !done; displayIndex++) {
var screenRect = SystemProperties.getNativeResolution(displayIndex);
var safeAreaRect = SystemProperties.getSafeAreaResolution(displayIndex);
if (screenRect.width === 0) {
// Exceeded max count of displays
done = true
break
}
addDetectedResolution(qsTr("Native"), screenRect)
addDetectedResolution(qsTr("Native (Excluding Notch)"), safeAreaRect)
}
// Prune resolutions that are over the decoder's maximum // Prune resolutions that are over the decoder's maximum
var max_pixels = SystemProperties.maximumResolution.width * SystemProperties.maximumResolution.height; var max_pixels = SystemProperties.maximumResolution.width * SystemProperties.maximumResolution.height;
if (max_pixels > 0) { if (max_pixels > 0) {
@ -210,7 +216,7 @@ Flickable {
if (!index_set) { if (!index_set) {
// We did not find a match. This must be a custom resolution. // We did not find a match. This must be a custom resolution.
resolutionListModel.append({ resolutionListModel.append({
"text": "Custom ("+StreamingPreferences.width+"x"+StreamingPreferences.height+")", "text": qsTr("Custom")+" ("+StreamingPreferences.width+"x"+StreamingPreferences.height+")",
"video_width": ""+StreamingPreferences.width, "video_width": ""+StreamingPreferences.width,
"video_height": ""+StreamingPreferences.height, "video_height": ""+StreamingPreferences.height,
"is_custom": true "is_custom": true
@ -219,7 +225,7 @@ Flickable {
} }
else { else {
resolutionListModel.append({ resolutionListModel.append({
"text": "Custom", "text": qsTr("Custom"),
"video_width": "", "video_width": "",
"video_height": "", "video_height": "",
"is_custom": true "is_custom": true

View file

@ -1200,7 +1200,8 @@ void Session::updateOptimalWindowDisplayMode()
// If this doesn't fit the selected resolution, use the native // If this doesn't fit the selected resolution, use the native
// resolution of the panel (unscaled). // resolution of the panel (unscaled).
if (desktopMode.w < m_ActiveVideoWidth || desktopMode.h < m_ActiveVideoHeight) { if (desktopMode.w < m_ActiveVideoWidth || desktopMode.h < m_ActiveVideoHeight) {
if (!StreamUtils::getNativeDesktopMode(displayIndex, &desktopMode)) { SDL_Rect safeArea;
if (!StreamUtils::getNativeDesktopMode(displayIndex, &desktopMode, &safeArea)) {
return; return;
} }
} }

View file

@ -193,7 +193,7 @@ bool StreamUtils::hasFastAes()
#endif #endif
} }
bool StreamUtils::getNativeDesktopMode(int displayIndex, SDL_DisplayMode* mode) bool StreamUtils::getNativeDesktopMode(int displayIndex, SDL_DisplayMode* mode, SDL_Rect* safeArea)
{ {
#ifdef Q_OS_DARWIN #ifdef Q_OS_DARWIN
#define MAX_DISPLAYS 16 #define MAX_DISPLAYS 16
@ -223,6 +223,37 @@ bool StreamUtils::getNativeDesktopMode(int displayIndex, SDL_DisplayMode* mode)
break; break;
} }
} }
safeArea->x = 0;
safeArea->y = 0;
safeArea->w = mode->w;
safeArea->h = mode->h;
#if TARGET_CPU_ARM64
// Now that we found the native full-screen mode, let's look for one that matches along
// the width but not the height and we'll assume that's the safe area full-screen mode.
//
// There doesn't appear to be a CG API or flag that will tell us that a given mode
// is a "safe area" mode, so we have to use our own (brittle) heuristics. :(
//
// To avoid potential false positives, let's avoid checking for external displays, since
// we might have scenarios like a 1920x1200 display with an alternate 1920x1080 mode
// which would falsely trigger our notch detection here.
if (CGDisplayIsBuiltin(displayIds[displayIndex])) {
for (CFIndex i = 0; i < count; i++) {
auto cgMode = (CGDisplayModeRef)(CFArrayGetValueAtIndex(modeList, i));
auto cgModeWidth = static_cast<int>(CGDisplayModeGetWidth(cgMode));
auto cgModeHeight = static_cast<int>(CGDisplayModeGetHeight(cgMode));
// If the modes differ by more than 100, we'll assume it's not a notch mode
if (mode->w == cgModeWidth && mode->h != cgModeHeight && mode->h <= cgModeHeight + 100) {
safeArea->w = cgModeWidth;
safeArea->h = cgModeHeight;
}
}
}
#endif
CFRelease(modeList); CFRelease(modeList);
// Now find the SDL mode that matches the CG native mode // Now find the SDL mode that matches the CG native mode
@ -258,7 +289,13 @@ bool StreamUtils::getNativeDesktopMode(int displayIndex, SDL_DisplayMode* mode)
return false; return false;
} }
} }
safeArea->x = 0;
safeArea->y = 0;
safeArea->w = mode->w;
safeArea->h = mode->h;
#endif #endif
return true; return true;
} }

View file

@ -29,7 +29,7 @@ public:
void screenSpaceToNormalizedDeviceCoords(SDL_Rect* src, SDL_FRect* dst, int viewportWidth, int viewportHeight); void screenSpaceToNormalizedDeviceCoords(SDL_Rect* src, SDL_FRect* dst, int viewportWidth, int viewportHeight);
static static
bool getNativeDesktopMode(int displayIndex, SDL_DisplayMode* mode); bool getNativeDesktopMode(int displayIndex, SDL_DisplayMode* mode, SDL_Rect* safeArea);
static static
int getDisplayRefreshRate(SDL_Window* window); int getDisplayRefreshRate(SDL_Window* window);