summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/plugins/platforms/cocoa/qcocoahelpers.h129
-rw-r--r--src/plugins/platforms/cocoa/qcocoaintegration.mm2
-rw-r--r--src/plugins/platforms/cocoa/qcocoawindow.mm27
-rw-r--r--src/plugins/platforms/cocoa/qnswindow.h50
-rw-r--r--src/plugins/platforms/cocoa/qnswindow.mm237
5 files changed, 213 insertions, 232 deletions
diff --git a/src/plugins/platforms/cocoa/qcocoahelpers.h b/src/plugins/platforms/cocoa/qcocoahelpers.h
index 1f4f9cd276f..4478895538f 100644
--- a/src/plugins/platforms/cocoa/qcocoahelpers.h
+++ b/src/plugins/platforms/cocoa/qcocoahelpers.h
@@ -55,9 +55,6 @@
#include <QtGui/qpalette.h>
#include <QtGui/qscreen.h>
-#include <objc/runtime.h>
-#include <objc/message.h>
-
Q_FORWARD_DECLARE_OBJC_CLASS(QT_MANGLE_NAMESPACE(QNSView));
QT_BEGIN_NAMESPACE
@@ -191,131 +188,5 @@ QT_END_NAMESPACE
QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSPanelContentsWrapper);
-// -------------------------------------------------------------------------
-
-// Depending on the ABI of the platform, we may need to use objc_msgSendSuper_stret:
-// - http://www.sealiesoftware.com/blog/archive/2008/10/30/objc_explain_objc_msgSend_stret.html
-// - https://lists.apple.com/archives/cocoa-dev/2008/Feb/msg02338.html
-template <typename T>
-struct objc_msgsend_requires_stret
-{ static const bool value =
-#if defined(Q_PROCESSOR_X86)
- // Any return value larger than two registers on i386/x86_64
- sizeof(T) > sizeof(void*) * 2;
-#elif defined(Q_PROCESSOR_ARM_32)
- // Any return value larger than a single register on arm
- sizeof(T) > sizeof(void*);
-#elif defined(Q_PROCESSOR_ARM_64)
- // Stret not used on arm64
- false;
-#endif
-};
-
-template <>
-struct objc_msgsend_requires_stret<void>
-{ static const bool value = false; };
-
-template <typename ReturnType, typename... Args>
-ReturnType qt_msgSendSuper(id receiver, SEL selector, Args... args)
-{
- static_assert(!objc_msgsend_requires_stret<ReturnType>::value,
- "The given return type requires stret on this platform");
-
- typedef ReturnType (*SuperFn)(objc_super *, SEL, Args...);
- SuperFn superFn = reinterpret_cast<SuperFn>(objc_msgSendSuper);
- objc_super sup = { receiver, class_getSuperclass(object_getClass(receiver)) };
- return superFn(&sup, selector, args...);
-}
-
-template <typename ReturnType, typename... Args>
-ReturnType qt_msgSendSuper_stret(id receiver, SEL selector, Args... args)
-{
- static_assert(objc_msgsend_requires_stret<ReturnType>::value,
- "The given return type does not use stret on this platform");
-
- typedef void (*SuperStretFn)(ReturnType *, objc_super *, SEL, Args...);
- SuperStretFn superStretFn = reinterpret_cast<SuperStretFn>(objc_msgSendSuper_stret);
-
- objc_super sup = { receiver, class_getSuperclass(object_getClass(receiver)) };
- ReturnType ret;
- superStretFn(&ret, &sup, selector, args...);
- return ret;
-}
-
-template<typename... Args>
-class QSendSuperHelper {
-public:
- QSendSuperHelper(id receiver, SEL sel, Args... args)
- : m_receiver(receiver), m_selector(sel), m_args(std::make_tuple(args...)), m_sent(false)
- {
- }
-
- ~QSendSuperHelper()
- {
- if (!m_sent)
- msgSendSuper<void>(m_args);
- }
-
- template <typename ReturnType>
- operator ReturnType()
- {
-#if defined(QT_DEBUG)
- Method method = class_getInstanceMethod(object_getClass(m_receiver), m_selector);
- char returnTypeEncoding[256];
- method_getReturnType(method, returnTypeEncoding, sizeof(returnTypeEncoding));
- NSUInteger alignedReturnTypeSize = 0;
- NSGetSizeAndAlignment(returnTypeEncoding, nullptr, &alignedReturnTypeSize);
- Q_ASSERT(alignedReturnTypeSize == sizeof(ReturnType));
-#endif
- m_sent = true;
- return msgSendSuper<ReturnType>(m_args);
- }
-
-private:
- template <std::size_t... Ts>
- struct index {};
-
- template <std::size_t N, std::size_t... Ts>
- struct gen_seq : gen_seq<N - 1, N - 1, Ts...> {};
-
- template <std::size_t... Ts>
- struct gen_seq<0, Ts...> : index<Ts...> {};
-
- template <typename ReturnType, bool V>
- using if_requires_stret = typename std::enable_if<objc_msgsend_requires_stret<ReturnType>::value == V, ReturnType>::type;
-
- template <typename ReturnType, std::size_t... Is>
- if_requires_stret<ReturnType, false> msgSendSuper(std::tuple<Args...>& args, index<Is...>)
- {
- return qt_msgSendSuper<ReturnType>(m_receiver, m_selector, std::get<Is>(args)...);
- }
-
- template <typename ReturnType, std::size_t... Is>
- if_requires_stret<ReturnType, true> msgSendSuper(std::tuple<Args...>& args, index<Is...>)
- {
- return qt_msgSendSuper_stret<ReturnType>(m_receiver, m_selector, std::get<Is>(args)...);
- }
-
- template <typename ReturnType>
- ReturnType msgSendSuper(std::tuple<Args...>& args)
- {
- return msgSendSuper<ReturnType>(args, gen_seq<sizeof...(Args)>{});
- }
-
- id m_receiver;
- SEL m_selector;
- std::tuple<Args...> m_args;
- bool m_sent;
-};
-
-template<typename... Args>
-QSendSuperHelper<Args...> qt_objcDynamicSuperHelper(id receiver, SEL selector, Args... args)
-{
- return QSendSuperHelper<Args...>(receiver, selector, args...);
-}
-
-// Same as calling super, but the super_class field resolved at runtime instead of compile time
-#define qt_objcDynamicSuper(...) qt_objcDynamicSuperHelper(self, _cmd, ##__VA_ARGS__)
-
#endif //QCOCOAHELPERS_H
diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.mm b/src/plugins/platforms/cocoa/qcocoaintegration.mm
index 7b1e6893884..1df74c986a0 100644
--- a/src/plugins/platforms/cocoa/qcocoaintegration.mm
+++ b/src/plugins/platforms/cocoa/qcocoaintegration.mm
@@ -218,7 +218,7 @@ QWindow *QCocoaScreen::topLevelAt(const QPoint &point) const
continue;
id<QNSWindowProtocol> proto = static_cast<id<QNSWindowProtocol> >(nsWindow);
- QCocoaWindow *cocoaWindow = proto.platformWindow;
+ QCocoaWindow *cocoaWindow = proto.helper.platformWindow;
if (!cocoaWindow)
continue;
window = cocoaWindow->window();
diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm
index e906f0fd1c2..d3f26df6c55 100644
--- a/src/plugins/platforms/cocoa/qcocoawindow.mm
+++ b/src/plugins/platforms/cocoa/qcocoawindow.mm
@@ -210,6 +210,7 @@ QCocoaWindow::~QCocoaWindow()
QMacAutoReleasePool pool;
[m_nsWindow makeFirstResponder:nil];
[m_nsWindow setContentView:nil];
+ [m_nsWindow.helper detachFromPlatformWindow];
if ([m_view superview])
[m_view removeFromSuperview];
@@ -1278,34 +1279,10 @@ QCocoaNSWindow *QCocoaWindow::createNSWindow(bool shouldBePanel)
// Create NSWindow
Class windowClass = shouldBePanel ? [QNSPanel class] : [QNSWindow class];
QCocoaNSWindow *nsWindow = [[windowClass alloc] initWithContentRect:frame
- styleMask:windowStyleMask(flags)
- // Deferring window creation breaks OpenGL (the GL context is
- // set up before the window is shown and needs a proper window)
- backing:NSBackingStoreBuffered defer:NO
- screen:cocoaScreen->nativeScreen()];
-
+ screen:cocoaScreen->nativeScreen() styleMask:windowStyleMask(flags) qPlatformWindow:this];
Q_ASSERT_X(nsWindow.screen == cocoaScreen->nativeScreen(), "QCocoaWindow",
"Resulting NSScreen should match the requested NSScreen");
- nsWindow.delegate = [[QNSWindowDelegate alloc] initWithQCocoaWindow:this];
-
- // Prevent Cocoa from releasing the window on close. Qt
- // handles the close event asynchronously and we want to
- // make sure that NSWindow stays valid until the
- // QCocoaWindow is deleted by Qt.
- [nsWindow setReleasedWhenClosed:NO];
-
- if (alwaysShowToolWindow()) {
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
- [center addObserver:[QNSWindow class] selector:@selector(applicationActivationChanged:)
- name:NSApplicationWillResignActiveNotification object:nil];
- [center addObserver:[QNSWindow class] selector:@selector(applicationActivationChanged:)
- name:NSApplicationWillBecomeActiveNotification object:nil];
- });
- }
-
if (targetScreen != window()->screen())
QWindowSystemInterface::handleWindowScreenChanged(window(), targetScreen);
diff --git a/src/plugins/platforms/cocoa/qnswindow.h b/src/plugins/platforms/cocoa/qnswindow.h
index ac9cbb978fe..b13b6d42a9e 100644
--- a/src/plugins/platforms/cocoa/qnswindow.h
+++ b/src/plugins/platforms/cocoa/qnswindow.h
@@ -47,26 +47,62 @@
#include <AppKit/AppKit.h>
QT_FORWARD_DECLARE_CLASS(QCocoaWindow)
+Q_FORWARD_DECLARE_OBJC_CLASS(QT_MANGLE_NAMESPACE(QNSWindowHelper));
+
+// -------------------------------------------------------------------------
@interface NSWindow (FullScreenProperty)
@property(readonly) BOOL qt_fullScreen;
@end
+// -------------------------------------------------------------------------
+
@protocol QNSWindowProtocol
-@optional
-- (BOOL)canBecomeKeyWindow;
-- (void)sendEvent:(NSEvent*)theEvent;
+
+@property (nonatomic, readonly) QT_MANGLE_NAMESPACE(QNSWindowHelper) *helper;
+
+- (id)initWithContentRect:(NSRect)contentRect screen:(NSScreen*)screen
+ styleMask:(NSUInteger)windowStyle qPlatformWindow:(QCocoaWindow *)qpw;
+
+- (void)superSendEvent:(NSEvent *)theEvent;
- (void)closeAndRelease;
-- (void)dealloc;
-@property (nonatomic, readonly) QCocoaWindow *platformWindow;
+
@end
typedef NSWindow<QNSWindowProtocol> QCocoaNSWindow;
-@interface QT_MANGLE_NAMESPACE(QNSWindow) : NSWindow<QNSWindowProtocol> @end
+// -------------------------------------------------------------------------
+
+@interface QT_MANGLE_NAMESPACE(QNSWindowHelper) : NSObject
+{
+ QPointer<QCocoaWindow> _platformWindow;
+}
+
+@property (nonatomic, readonly) QCocoaNSWindow *window;
+@property (nonatomic, readonly) QCocoaWindow *platformWindow;
+
+- (id)initWithNSWindow:(QCocoaNSWindow *)window platformWindow:(QCocoaWindow *)platformWindow;
+- (void)handleWindowEvent:(NSEvent *)theEvent;
+- (void)detachFromPlatformWindow;
+
+@end
+
+QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSWindowHelper);
+
+// -------------------------------------------------------------------------
+
+@interface QT_MANGLE_NAMESPACE(QNSWindow) : NSWindow<QNSWindowProtocol>
+@end
+
QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSWindow);
-@interface QT_MANGLE_NAMESPACE(QNSPanel) : NSPanel<QNSWindowProtocol> @end
+// -------------------------------------------------------------------------
+
+@interface QT_MANGLE_NAMESPACE(QNSPanel) : NSPanel<QNSWindowProtocol>
+@end
+
QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSPanel);
+// -------------------------------------------------------------------------
+
#endif // QNSWINDOW_H
diff --git a/src/plugins/platforms/cocoa/qnswindow.mm b/src/plugins/platforms/cocoa/qnswindow.mm
index e5ddd3ca0fa..e44db3ff3b1 100644
--- a/src/plugins/platforms/cocoa/qnswindow.mm
+++ b/src/plugins/platforms/cocoa/qnswindow.mm
@@ -88,72 +88,32 @@ static bool isMouseEvent(NSEvent *ev)
}
@end
-#define super USE_qt_objcDynamicSuper_INSTEAD
-
-@implementation QNSWindow
-
-+ (void)load
-{
- const Class windowClass = [self class];
- const Class panelClass = [QNSPanel class];
-
- unsigned int methodDescriptionsCount;
- objc_method_description *methods = protocol_copyMethodDescriptionList(
- objc_getProtocol("QNSWindowProtocol"), NO, YES, &methodDescriptionsCount);
-
- for (unsigned int i = 0; i < methodDescriptionsCount; ++i) {
- objc_method_description method = methods[i];
- class_addMethod(panelClass, method.name,
- class_getMethodImplementation(windowClass, method.name),
- method.types);
- }
-
- free(methods);
-}
+@implementation QNSWindowHelper
- (QCocoaWindow *)platformWindow
{
- return qnsview_cast(self.contentView).platformWindow;
+ return _platformWindow.data();
}
-- (BOOL)canBecomeKeyWindow
+- (id)initWithNSWindow:(QCocoaNSWindow *)window platformWindow:(QCocoaWindow *)platformWindow
{
- QCocoaWindow *pw = self.platformWindow;
- if (!pw)
- return NO;
-
- if (pw->shouldRefuseKeyWindowAndFirstResponder())
- return NO;
+ if (self = [super init]) {
+ _window = window;
+ _platformWindow = platformWindow;
- if ([self isKindOfClass:[QNSPanel class]]) {
- // Only tool or dialog windows should become key:
- Qt::WindowType type = pw->window()->type();
- if (type == Qt::Tool || type == Qt::Dialog)
- return YES;
+ _window.delegate = [[QNSWindowDelegate alloc] initWithQCocoaWindow:_platformWindow];
- return NO;
- } else {
- // The default implementation returns NO for title-bar less windows,
- // override and return yes here to make sure popup windows such as
- // the combobox popup can become the key window.
- return YES;
+ // Prevent Cocoa from releasing the window on close. Qt
+ // handles the close event asynchronously and we want to
+ // make sure that NSWindow stays valid until the
+ // QCocoaWindow is deleted by Qt.
+ [_window setReleasedWhenClosed:NO];
}
-}
-
-- (BOOL)canBecomeMainWindow
-{
- BOOL canBecomeMain = YES; // By default, windows can become the main window
- // Windows with a transient parent (such as combobox popup windows)
- // cannot become the main window:
- QCocoaWindow *pw = self.platformWindow;
- if (!pw || pw->window()->transientParent())
- canBecomeMain = NO;
-
- return canBecomeMain;
+ return self;
}
-- (void)sendEvent:(NSEvent*)theEvent
+- (void)handleWindowEvent:(NSEvent *)theEvent
{
// We might get events for a NSWindow after the corresponding platform
// window has been deleted, as the NSWindow can outlive the QCocoaWindow
@@ -169,7 +129,7 @@ static bool isMouseEvent(NSEvent *ev)
return;
}
- qt_objcDynamicSuper(theEvent);
+ [self.window superSendEvent:theEvent];
if (!self.platformWindow)
return; // Platform window went away while processing event
@@ -177,31 +137,108 @@ static bool isMouseEvent(NSEvent *ev)
QCocoaWindow *pw = self.platformWindow;
if (pw->frameStrutEventsEnabled() && isMouseEvent(theEvent)) {
NSPoint loc = [theEvent locationInWindow];
- NSRect windowFrame = [self convertRectFromScreen:self.frame];
- NSRect contentFrame = self.contentView.frame;
+ NSRect windowFrame = [self.window convertRectFromScreen:[self.window frame]];
+ NSRect contentFrame = [[self.window contentView] frame];
if (NSMouseInRect(loc, windowFrame, NO) && !NSMouseInRect(loc, contentFrame, NO))
[qnsview_cast(pw->view()) handleFrameStrutMouseEvent:theEvent];
}
}
+- (void)detachFromPlatformWindow
+{
+ _platformWindow.clear();
+ [self.window.delegate release];
+ self.window.delegate = nil;
+}
+
+- (void)dealloc
+{
+ _window = nil;
+ _platformWindow.clear();
+ [super dealloc];
+}
+
+@end
+
+// Deferring window creation breaks OpenGL (the GL context is
+// set up before the window is shown and needs a proper window)
+static const bool kNoDefer = NO;
+
+@implementation QNSWindow
+
+@synthesize helper = _helper;
+
+- (id)initWithContentRect:(NSRect)contentRect
+ screen:(NSScreen*)screen
+ styleMask:(NSUInteger)windowStyle
+ qPlatformWindow:(QCocoaWindow *)qpw
+{
+ if (self = [super initWithContentRect:contentRect styleMask:windowStyle
+ backing:NSBackingStoreBuffered defer:kNoDefer screen:screen]) {
+ _helper = [[QNSWindowHelper alloc] initWithNSWindow:self platformWindow:qpw];
+ }
+ return self;
+}
+
+- (BOOL)canBecomeKeyWindow
+{
+ QCocoaWindow *pw = self.helper.platformWindow;
+ if (!pw)
+ return NO;
+
+ if (pw->shouldRefuseKeyWindowAndFirstResponder())
+ return NO;
+
+ // The default implementation returns NO for title-bar less windows,
+ // override and return yes here to make sure popup windows such as
+ // the combobox popup can become the key window.
+ return YES;
+}
+
+- (BOOL)canBecomeMainWindow
+{
+ BOOL canBecomeMain = YES; // By default, windows can become the main window
+
+ // Windows with a transient parent (such as combobox popup windows)
+ // cannot become the main window:
+ QCocoaWindow *pw = self.helper.platformWindow;
+ if (!pw || pw->window()->transientParent())
+ canBecomeMain = NO;
+
+ return canBecomeMain;
+}
+
+- (void)sendEvent:(NSEvent*)theEvent
+{
+ [self.helper handleWindowEvent:theEvent];
+}
+
+- (void)superSendEvent:(NSEvent *)theEvent
+{
+ [super sendEvent:theEvent];
+}
+
- (void)closeAndRelease
{
qCDebug(lcQpaCocoaWindow) << "closeAndRelease" << self;
- [self.delegate release];
- self.delegate = nil;
-
+ [self.helper detachFromPlatformWindow];
[self close];
[self release];
}
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wobjc-missing-super-calls"
- (void)dealloc
{
- qt_objcDynamicSuper();
+ [_helper release];
+ _helper = nil;
+ [super dealloc];
}
-#pragma clang diagnostic pop
+
+@end
+
+@implementation QNSPanel
+
+@synthesize helper = _helper;
+ (void)applicationActivationChanged:(NSNotification*)notification
{
@@ -247,7 +284,7 @@ static bool isMouseEvent(NSEvent *ev)
continue;
if ([window conformsToProtocol:@protocol(QNSWindowProtocol)]) {
- QCocoaWindow *cocoaWindow = static_cast<QCocoaNSWindow *>(window).platformWindow;
+ QCocoaWindow *cocoaWindow = static_cast<id<QNSWindowProtocol>>(window).helper.platformWindow;
window.level = notification.name == NSApplicationWillResignActiveNotification ?
NSNormalWindowLevel : cocoaWindow->windowLevel(cocoaWindow->window()->flags());
}
@@ -268,10 +305,70 @@ static bool isMouseEvent(NSEvent *ev)
}
}
-@end
+- (id)initWithContentRect:(NSRect)contentRect
+ screen:(NSScreen*)screen
+ styleMask:(NSUInteger)windowStyle
+ qPlatformWindow:(QCocoaWindow *)qpw
+{
+ if (self = [super initWithContentRect:contentRect styleMask:windowStyle
+ backing:NSBackingStoreBuffered defer:kNoDefer screen:screen]) {
+ _helper = [[QNSWindowHelper alloc] initWithNSWindow:self platformWindow:qpw];
+
+ if (qpw->alwaysShowToolWindow()) {
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
+ [center addObserver:[self class] selector:@selector(applicationActivationChanged:)
+ name:NSApplicationWillResignActiveNotification object:nil];
+ [center addObserver:[self class] selector:@selector(applicationActivationChanged:)
+ name:NSApplicationWillBecomeActiveNotification object:nil];
+ });
+ }
+ }
+ return self;
+}
-@implementation QNSPanel
-// Implementation shared with QNSWindow, see +[QNSWindow load] above
-@end
+- (BOOL)canBecomeKeyWindow
+{
+ QCocoaWindow *pw = self.helper.platformWindow;
+ if (!pw)
+ return NO;
+
+ if (pw->shouldRefuseKeyWindowAndFirstResponder())
+ return NO;
+
+ // Only tool or dialog windows should become key:
+ Qt::WindowType type = pw->window()->type();
+ if (type == Qt::Tool || type == Qt::Dialog)
+ return YES;
+
+ return NO;
+}
+
+- (void)sendEvent:(NSEvent*)theEvent
+{
+ [self.helper handleWindowEvent:theEvent];
+}
+
+- (void)superSendEvent:(NSEvent *)theEvent
+{
+ [super sendEvent:theEvent];
+}
+
+- (void)closeAndRelease
+{
+ qCDebug(lcQpaCocoaWindow) << "closeAndRelease" << self;
-#undef super
+ [self.helper detachFromPlatformWindow];
+ [self close];
+ [self release];
+}
+
+- (void)dealloc
+{
+ [_helper release];
+ _helper = nil;
+ [super dealloc];
+}
+
+@end