osx: support for QScreen changes at runtime, and physical dimensions

QScreen's physical size and logical DPI come from the operating system.
Physical DPI is calculated as pixel size / physical size.  Whenever the
user changes the display settings, applicationDidChangeScreenParameters
is called; QScreens are created and destroyed when displays are added
and removed, and each QScreen which continues to exist gets updated
properties from the OS.

Change-Id: I7f2e9e32a3ad53d73ea987f39a0c62fa8dd22b05
Reviewed-by: Shawn Rutledge <shawn.rutledge@digia.com>
Reviewed-by: Morten Johan Sørvig <morten.sorvig@digia.com>
bb10
Shawn Rutledge 2012-10-01 19:26:36 +02:00 committed by The Qt Project
parent 274c258521
commit d0573aa914
3 changed files with 99 additions and 19 deletions

View File

@ -77,6 +77,7 @@
#import "qcocoaapplicationdelegate.h"
#import "qnswindowdelegate.h"
#import "qcocoamenuloader.h"
#include "qcocoaintegration.h"
#include <qevent.h>
#include <qurl.h>
#include <qdebug.h>
@ -346,7 +347,7 @@ static void cleanupCocoaApplicationDelegate()
- (void)applicationDidChangeScreenParameters:(NSNotification *)notification
{
Q_UNUSED(notification);
//QDesktopWidgetImplementation::instance()->onResize();
((QCocoaIntegration*)QGuiApplicationPrivate::platformIntegration())->updateScreens();
}
- (void)setReflectionDelegate:(NSObject <NSApplicationDelegate> *)oldDelegate

View File

@ -61,23 +61,38 @@ public:
QCocoaScreen(int screenIndex);
~QCocoaScreen();
// ----------------------------------------------------
// Virtual methods overridden from QPlatformScreen
QPixmap grabWindow(WId window, int x, int y, int width, int height) const;
QRect geometry() const { return m_geometry; }
QRect availableGeometry() const { return m_availableGeometry; }
int depth() const { return m_depth; }
QImage::Format format() const { return m_format; }
QSizeF physicalSize() const { return m_physicalSize; }
QDpi logicalDpi() const { return m_logicalDpi; }
qreal refreshRate() const { return m_refreshRate; }
QString name() const { return m_name; }
QPlatformCursor *cursor() const { return m_cursor; }
QList<QPlatformScreen *> virtualSiblings() const { return m_siblings; }
// ----------------------------------------------------
// Additional methods
void setVirtualSiblings(QList<QPlatformScreen *> siblings) { m_siblings = siblings; }
NSScreen *osScreen() const { return m_screen; }
void updateGeometry();
public:
NSScreen *m_screen;
QRect m_geometry;
QRect m_availableGeometry;
QDpi m_logicalDpi;
qreal m_refreshRate;
int m_depth;
QString m_name;
QImage::Format m_format;
QSizeF m_physicalSize;
QCocoaCursor *m_cursor;
QList<QPlatformScreen *> m_siblings;
};
class QCocoaIntegration : public QPlatformIntegration
@ -105,6 +120,8 @@ public:
QPlatformServices *services() const;
QVariant styleHint(StyleHint hint) const;
void updateScreens();
private:
QScopedPointer<QPlatformFontDatabase> mFontDb;

View File

@ -58,6 +58,7 @@
#include <QtCore/qcoreapplication.h>
#include <QtPlatformSupport/private/qcoretextfontdatabase_p.h>
#include <IOKit/graphics/IOGraphicsLib.h>
static void initResources()
{
@ -67,10 +68,21 @@ static void initResources()
QT_BEGIN_NAMESPACE
QCocoaScreen::QCocoaScreen(int screenIndex)
:QPlatformScreen()
QCocoaScreen::QCocoaScreen(int screenIndex) :
QPlatformScreen(), m_refreshRate(60.0)
{
m_screen = [[NSScreen screens] objectAtIndex:screenIndex];
updateGeometry();
m_cursor = new QCocoaCursor;
}
QCocoaScreen::~QCocoaScreen()
{
delete m_cursor;
}
void QCocoaScreen::updateGeometry()
{
NSRect frameRect = [m_screen frame];
m_geometry = QRect(frameRect.origin.x, frameRect.origin.y, frameRect.size.width, frameRect.size.height);
NSRect visibleRect = [m_screen visibleFrame];
@ -79,19 +91,28 @@ QCocoaScreen::QCocoaScreen(int screenIndex)
visibleRect.size.width, visibleRect.size.height);
m_format = QImage::Format_RGB32;
m_depth = NSBitsPerPixelFromDepth([m_screen depth]);
const int dpi = 72;
const qreal inch = 25.4;
m_physicalSize = QSizeF(m_geometry.size()) * inch / dpi;
NSDictionary *devDesc = [m_screen deviceDescription];
CGDirectDisplayID dpy = [[devDesc objectForKey:@"NSScreenNumber"] unsignedIntValue];
CGSize size = CGDisplayScreenSize(dpy);
m_physicalSize = QSizeF(size.width, size.height);
NSSize resolution = [[devDesc valueForKey:NSDeviceResolution] sizeValue];
m_logicalDpi.first = resolution.width;
m_logicalDpi.second = resolution.height;
m_refreshRate = CGDisplayModeGetRefreshRate(CGDisplayCopyDisplayMode(dpy));
m_cursor = new QCocoaCursor;
};
// Get m_name (brand/model of the monitor)
NSDictionary *deviceInfo = (NSDictionary *)IODisplayCreateInfoDictionary(CGDisplayIOServicePort(dpy), kIODisplayOnlyPreferredName);
NSDictionary *localizedNames = [deviceInfo objectForKey:[NSString stringWithUTF8String:kDisplayProductName]];
if ([localizedNames count] > 0)
m_name = QString::fromUtf8([[localizedNames objectForKey:[[localizedNames allKeys] objectAtIndex:0]] UTF8String]);
[deviceInfo release];
QCocoaScreen::~QCocoaScreen()
{
delete m_cursor;
QWindowSystemInterface::handleScreenGeometryChange(screen(), geometry());
QWindowSystemInterface::handleScreenLogicalDotsPerInchChange(screen(), resolution.width, resolution.height);
QWindowSystemInterface::handleScreenRefreshRateChange(screen(), m_refreshRate);
QWindowSystemInterface::handleScreenAvailableGeometryChange(screen(), availableGeometry());
}
extern CGContextRef qt_mac_cg_context(const QPaintDevice *pdev);
@ -204,12 +225,7 @@ QCocoaIntegration::QCocoaIntegration()
[newDelegate setMenuLoader:qtMenuLoader];
}
NSArray *screens = [NSScreen screens];
for (uint i = 0; i < [screens count]; i++) {
QCocoaScreen *screen = new QCocoaScreen(i);
mScreens.append(screen);
screenAdded(screen);
}
updateScreens();
QMacPasteboardMime::initializeMimeTypes();
}
@ -237,6 +253,52 @@ QCocoaIntegration::~QCocoaIntegration()
}
}
/*!
\brief Synchronizes the screen list, adds new screens, removes deleted ones
*/
void QCocoaIntegration::updateScreens()
{
NSArray *screens = [NSScreen screens];
QSet<QCocoaScreen*> remainingScreens = QSet<QCocoaScreen*>::fromList(mScreens);
QList<QPlatformScreen *> siblings;
for (uint i = 0; i < [screens count]; i++) {
NSScreen* scr = [[NSScreen screens] objectAtIndex:i];
CGDirectDisplayID dpy = [[[scr deviceDescription] objectForKey:@"NSScreenNumber"] unsignedIntValue];
// If this screen is a mirror and is not the primary one of the mirror set, ignore it.
if (CGDisplayIsInMirrorSet(dpy)) {
CGDirectDisplayID primary = CGDisplayMirrorsDisplay(dpy);
if (primary != kCGNullDirectDisplay && primary != dpy)
continue;
}
QCocoaScreen* screen = NULL;
foreach (QCocoaScreen* existingScr, mScreens)
// NSScreen documentation says do not cache the array returned from [NSScreen screens].
// However in practice, we can identify a screen by its pointer: if resolution changes,
// the NSScreen object will be the same instance, just with different values.
if (existingScr->osScreen() == scr) {
screen = existingScr;
break;
}
if (screen) {
remainingScreens.remove(screen);
screen->updateGeometry();
} else {
screen = new QCocoaScreen(i);
mScreens.append(screen);
screenAdded(screen);
}
siblings << screen;
}
// Now the leftovers in remainingScreens are no longer current, so we can delete them.
foreach (QCocoaScreen* screen, remainingScreens) {
mScreens.removeOne(screen);
delete screen;
}
// All screens in mScreens are siblings, because we ignored the mirrors.
foreach (QCocoaScreen* screen, mScreens)
screen->setVirtualSiblings(siblings);
}
bool QCocoaIntegration::hasCapability(QPlatformIntegration::Capability cap) const
{
switch (cap) {