From dae21f2638d1ccdb03a6ff44f7fa866be437c49d Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Tue, 4 Sep 2018 00:21:37 -0400 Subject: [PATCH] Detect native panel resolution on Retina displays. Fixes #59 --- app/gui/SettingsView.qml | 66 +++++++++++-------- app/settings/streamingpreferences.cpp | 35 ++++++++-- app/settings/streamingpreferences.h | 6 +- app/streaming/session.cpp | 7 +- app/streaming/streamutils.cpp | 61 +++++++++++++++++ app/streaming/streamutils.h | 3 + .../video/ffmpeg-renderers/pacer/pacer.cpp | 6 +- 7 files changed, 145 insertions(+), 39 deletions(-) diff --git a/app/gui/SettingsView.qml b/app/gui/SettingsView.qml index 0ce7f0de..9cd2884d 100644 --- a/app/gui/SettingsView.qml +++ b/app/gui/SettingsView.qml @@ -56,37 +56,51 @@ ScrollView { // ignore setting the index at first, and actually set it when the component is loaded Component.onCompleted: { // Add native resolutions for all attached displays - for (var displayIndex = 0;; displayIndex++) { - var screenRect = prefs.getDisplayResolution(displayIndex) - if (screenRect.width === 0) { - // Exceeded max count of displays - break - } + var done = false + for (var displayIndex = 0; !done; displayIndex++) { + for (var displayResIndex = 0; displayResIndex < 2; displayResIndex++) { + var screenRect; - var indexToAdd = 0 - for (var j = 0; j < resolutionComboBox.count; j++) { - var existing_width = parseInt(resolutionListModel.get(j).video_width); - var existing_height = parseInt(resolutionListModel.get(j).video_height); + // Some platforms have different desktop resolutions + // and native resolutions (like macOS with Retina displays) + if (displayResIndex == 0) { + screenRect = prefs.getDesktopResolution(displayIndex) + } + else { + screenRect = prefs.getNativeResolution(displayIndex) + } - if (screenRect.width === existing_width && screenRect.height === existing_height) { - // Duplicate entry, skip - indexToAdd = -1 + if (screenRect.width === 0) { + // Exceeded max count of displays + done = true break } - else if (screenRect.width * screenRect.height > existing_width * existing_height) { - // Candidate entrypoint after this entry - indexToAdd = j + 1 - } - } - // Insert this display's resolution if it's not a duplicate - if (indexToAdd >= 0) { - resolutionListModel.insert(indexToAdd, - { - "text": "Native ("+screenRect.width+"x"+screenRect.height+")", - "video_width": ""+screenRect.width, - "video_height": ""+screenRect.height - }) + var indexToAdd = 0 + for (var j = 0; j < resolutionComboBox.count; j++) { + var existing_width = parseInt(resolutionListModel.get(j).video_width); + var existing_height = parseInt(resolutionListModel.get(j).video_height); + + if (screenRect.width === existing_width && screenRect.height === existing_height) { + // Duplicate entry, skip + indexToAdd = -1 + break + } + else if (screenRect.width * screenRect.height > existing_width * existing_height) { + // Candidate entrypoint after this entry + indexToAdd = j + 1 + } + } + + // Insert this display's resolution if it's not a duplicate + if (indexToAdd >= 0) { + resolutionListModel.insert(indexToAdd, + { + "text": "Native ("+screenRect.width+"x"+screenRect.height+")", + "video_width": ""+screenRect.width, + "video_height": ""+screenRect.height + }) + } } } diff --git a/app/settings/streamingpreferences.cpp b/app/settings/streamingpreferences.cpp index d5cad9ab..c153aff7 100644 --- a/app/settings/streamingpreferences.cpp +++ b/app/settings/streamingpreferences.cpp @@ -1,5 +1,6 @@ #include "streamingpreferences.h" #include "streaming/session.hpp" +#include "streaming/streamutils.h" #include @@ -94,7 +95,7 @@ int StreamingPreferences::getMaximumStreamingFrameRate() SDL_DisplayMode mode, bestMode, desktopMode; for (int displayIndex = 0; displayIndex < SDL_GetNumVideoDisplays(); displayIndex++) { // Get the native desktop resolution - if (SDL_GetDesktopDisplayMode(displayIndex, &desktopMode) == 0) { + if (StreamUtils::getRealDesktopMode(displayIndex, &desktopMode)) { // Start at desktop mode and work our way up bestMode = desktopMode; @@ -119,7 +120,7 @@ int StreamingPreferences::getMaximumStreamingFrameRate() return maxFrameRate; } -QRect StreamingPreferences::getDisplayResolution(int displayIndex) +QRect StreamingPreferences::getDesktopResolution(int displayIndex) { if (SDL_InitSubSystem(SDL_INIT_VIDEO) != 0) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, @@ -134,7 +135,7 @@ QRect StreamingPreferences::getDisplayResolution(int displayIndex) } SDL_DisplayMode mode; - int err = SDL_GetCurrentDisplayMode(displayIndex, &mode); + int err = SDL_GetDesktopDisplayMode(displayIndex, &mode); SDL_QuitSubSystem(SDL_INIT_VIDEO); if (err == 0) { @@ -142,12 +143,38 @@ QRect StreamingPreferences::getDisplayResolution(int displayIndex) } else { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, - "SDL_GetCurrentDisplayMode() failed: %s", + "SDL_GetDesktopDisplayMode() failed: %s", SDL_GetError()); return QRect(); } } +QRect StreamingPreferences::getNativeResolution(int displayIndex) +{ + if (SDL_InitSubSystem(SDL_INIT_VIDEO) != 0) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, + "SDL_InitSubSystem(SDL_INIT_VIDEO) failed: %s", + SDL_GetError()); + return QRect(); + } + + if (displayIndex >= SDL_GetNumVideoDisplays()) { + SDL_QuitSubSystem(SDL_INIT_VIDEO); + return QRect(); + } + + SDL_DisplayMode mode; + bool success = StreamUtils::getRealDesktopMode(displayIndex, &mode); + SDL_QuitSubSystem(SDL_INIT_VIDEO); + + if (success) { + return QRect(0, 0, mode.w, mode.h); + } + else { + return QRect(); + } +} + int StreamingPreferences::getDefaultBitrate(int width, int height, int fps) { // This table prefers 16:10 resolutions because they are diff --git a/app/settings/streamingpreferences.h b/app/settings/streamingpreferences.h index 7125a4f2..c770c574 100644 --- a/app/settings/streamingpreferences.h +++ b/app/settings/streamingpreferences.h @@ -21,7 +21,9 @@ public: Q_INVOKABLE static int getMaximumStreamingFrameRate(); - Q_INVOKABLE QRect getDisplayResolution(int displayIndex); + Q_INVOKABLE QRect getDesktopResolution(int displayIndex); + + Q_INVOKABLE QRect getNativeResolution(int displayIndex); void reload(); @@ -56,7 +58,7 @@ public: WM_FULLSCREEN_DESKTOP, WM_WINDOWED }; - Q_ENUM(WindowMode); + Q_ENUM(WindowMode) Q_PROPERTY(int width MEMBER width NOTIFY displayModeChanged) Q_PROPERTY(int height MEMBER height NOTIFY displayModeChanged) diff --git a/app/streaming/session.cpp b/app/streaming/session.cpp index 05604235..38f63c30 100644 --- a/app/streaming/session.cpp +++ b/app/streaming/session.cpp @@ -1,5 +1,6 @@ #include "session.hpp" #include "settings/streamingpreferences.h" +#include "streaming/streamutils.h" #include #include @@ -544,7 +545,7 @@ void Session::getWindowDimensions(bool fullScreen, else if (fullScreen) { for (int i = 0; i < SDL_GetNumVideoDisplays(); i++) { SDL_DisplayMode mode; - if (SDL_GetDesktopDisplayMode(i, &mode) == 0 && + if (StreamUtils::getRealDesktopMode(i, &mode) && m_ActiveVideoWidth == mode.w && m_ActiveVideoHeight == mode.h) { SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, @@ -603,7 +604,7 @@ void Session::updateOptimalWindowDisplayMode() int displayIndex = SDL_GetWindowDisplayIndex(m_Window); // Get the native desktop resolution - if (SDL_GetDesktopDisplayMode(displayIndex, &desktopMode) == 0) { + if (StreamUtils::getRealDesktopMode(displayIndex, &desktopMode)) { // Start with the native desktop resolution and try to find // the highest refresh rate that our stream FPS evenly divides. bestMode = desktopMode; @@ -757,7 +758,7 @@ void Session::exec() y, width, height, - 0); + SDL_WINDOW_ALLOW_HIGHDPI); if (!m_Window) { SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL_CreateWindow() failed: %s", diff --git a/app/streaming/streamutils.cpp b/app/streaming/streamutils.cpp index c0d46c18..1b776d6c 100644 --- a/app/streaming/streamutils.cpp +++ b/app/streaming/streamutils.cpp @@ -1,5 +1,11 @@ #include "streamutils.h" +#include + +#ifdef Q_OS_DARWIN +#include +#endif + void StreamUtils::scaleSourceToDestinationSurface(SDL_Rect* src, SDL_Rect* dst) { int dstH = dst->w * src->h / src->w; @@ -18,3 +24,58 @@ void StreamUtils::scaleSourceToDestinationSurface(SDL_Rect* src, SDL_Rect* dst) SDL_assert(dst->h * src->w / src->h <= dst->w); } } + +bool StreamUtils::getRealDesktopMode(int displayIndex, SDL_DisplayMode* mode) +{ +#ifdef Q_OS_DARWIN +#define MAX_DISPLAYS 16 + CGDirectDisplayID displayIds[MAX_DISPLAYS]; + uint32_t displayCount = 0; + CGGetActiveDisplayList(MAX_DISPLAYS, displayIds, &displayCount); + if (displayIndex >= displayCount) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, + "Too many displays: %d vs %d", + displayIndex, displayCount); + return false; + } + + SDL_zerop(mode); + + // Retina displays have non-native resolutions both below and above (!) their + // native resolution, so it's impossible for us to figure out what's actually + // native on macOS using the SDL API alone. We'll talk to CoreGraphics to + // find the correct resolution and match it in our SDL list. + CFArrayRef modeList = CGDisplayCopyAllDisplayModes(displayIds[displayIndex], nullptr); + CFIndex count = CFArrayGetCount(modeList); + for (CFIndex i = 0; i < count; i++) { + auto cgMode = (CGDisplayModeRef)(CFArrayGetValueAtIndex(modeList, i)); + if ((CGDisplayModeGetIOFlags(cgMode) & kDisplayModeNativeFlag) != 0) { + mode->w = static_cast(CGDisplayModeGetWidth(cgMode)); + mode->h = static_cast(CGDisplayModeGetHeight(cgMode)); + break; + } + } + CFRelease(modeList); + + // Now find the SDL mode that matches the CG native mode + for (int i = 0; i < SDL_GetNumDisplayModes(displayIndex); i++) { + SDL_DisplayMode thisMode; + if (SDL_GetDisplayMode(displayIndex, i, &thisMode) == 0) { + if (thisMode.w == mode->w && thisMode.h == mode->h && + thisMode.refresh_rate >= mode->refresh_rate) { + *mode = thisMode; + break; + } + } + } +#else + if (SDL_GetDesktopDisplayMode(displayIndex, mode) != 0) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, + "SDL_GetDesktopDisplayMode() failed: %s", + SDL_GetError()); + return false; + } +#endif + + return true; +} diff --git a/app/streaming/streamutils.h b/app/streaming/streamutils.h index ecf54add..b037bb2c 100644 --- a/app/streaming/streamutils.h +++ b/app/streaming/streamutils.h @@ -7,4 +7,7 @@ class StreamUtils public: static void scaleSourceToDestinationSurface(SDL_Rect* src, SDL_Rect* dst); + + static + bool getRealDesktopMode(int displayIndex, SDL_DisplayMode* mode); }; diff --git a/app/streaming/video/ffmpeg-renderers/pacer/pacer.cpp b/app/streaming/video/ffmpeg-renderers/pacer/pacer.cpp index c42f3077..4ce58323 100644 --- a/app/streaming/video/ffmpeg-renderers/pacer/pacer.cpp +++ b/app/streaming/video/ffmpeg-renderers/pacer/pacer.cpp @@ -1,4 +1,5 @@ #include "pacer.h" +#include "streaming/streamutils.h" #include "nullthreadedvsyncsource.h" @@ -143,10 +144,7 @@ bool Pacer::initialize(SDL_Window* window, int maxVideoFps, bool enableVsync) } else { // Use the current display mode for windowed and borderless - if (SDL_GetCurrentDisplayMode(displayIndex, &mode) != 0) { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, - "SDL_GetCurrentDisplayMode() failed: %s", - SDL_GetError()); + if (!StreamUtils::getRealDesktopMode(displayIndex, &mode)) { return false; } }