| | |
| | | #include "cocoawindowcontext_p.h" |
| | | |
| | | #include <objc/runtime.h> |
| | | #include <AppKit/AppKit.h> |
| | | |
| | | #include <QtGui/QGuiApplication> |
| | | |
| | | namespace QWK { |
| | | |
| | | #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) |
| | | static const struct QWK_Hook { |
| | | QWK_Hook() { |
| | | qputenv("QT_MAC_WANTS_LAYER", "1"); |
| | | } |
| | | } g_hook{}; |
| | | static const struct QWK_Hook { |
| | | QWK_Hook() { |
| | | qputenv("QT_MAC_WANTS_LAYER", "1"); |
| | | } |
| | | } g_hook{}; |
| | | #endif |
| | | |
| | | class NSWindowProxy |
| | | { |
| | | Q_DISABLE_COPY_MOVE(NSWindowProxy) |
| | | |
| | | public: |
| | | NSWindowProxy(NSWindow *macWindow) |
| | | { |
| | | if (instances.contains(macWindow)) { |
| | | return; |
| | | class NSWindowProxy : public QObject { |
| | | Q_OBJECT |
| | | public: |
| | | NSWindowProxy(NSWindow *macWindow) { |
| | | if (instances.contains(macWindow)) { |
| | | return; |
| | | } |
| | | nswindow = macWindow; |
| | | instances.insert(macWindow, this); |
| | | if (!windowClass) { |
| | | windowClass = [nswindow class]; |
| | | replaceImplementations(); |
| | | } |
| | | } |
| | | nswindow = macWindow; |
| | | instances.insert(macWindow, this); |
| | | if (!windowClass) { |
| | | windowClass = [nswindow class]; |
| | | replaceImplementations(); |
| | | |
| | | ~NSWindowProxy() override { |
| | | instances.remove(nswindow); |
| | | if (instances.count() <= 0) { |
| | | restoreImplementations(); |
| | | windowClass = nil; |
| | | } |
| | | nswindow = nil; |
| | | } |
| | | } |
| | | |
| | | ~NSWindowProxy() override |
| | | { |
| | | instances.remove(nswindow); |
| | | if (instances.count() <= 0) { |
| | | restoreImplementations(); |
| | | windowClass = nil; |
| | | } |
| | | nswindow = nil; |
| | | } |
| | | public Q_SLOTS: |
| | | void replaceImplementations() { |
| | | Method method = class_getInstanceMethod(windowClass, @selector(setStyleMask:)); |
| | | Q_ASSERT(method); |
| | | oldSetStyleMask = reinterpret_cast<setStyleMaskPtr>( |
| | | method_setImplementation(method, reinterpret_cast<IMP>(setStyleMask))); |
| | | Q_ASSERT(oldSetStyleMask); |
| | | |
| | | public Q_SLOTS: |
| | | void replaceImplementations() |
| | | { |
| | | Method method = class_getInstanceMethod(windowClass, @selector(setStyleMask:)); |
| | | Q_ASSERT(method); |
| | | oldSetStyleMask = reinterpret_cast<setStyleMaskPtr>(method_setImplementation(method, reinterpret_cast<IMP>(setStyleMask))); |
| | | Q_ASSERT(oldSetStyleMask); |
| | | |
| | | method = class_getInstanceMethod(windowClass, @selector(setTitlebarAppearsTransparent:)); |
| | | Q_ASSERT(method); |
| | | oldSetTitlebarAppearsTransparent = reinterpret_cast<setTitlebarAppearsTransparentPtr>(method_setImplementation(method, reinterpret_cast<IMP>(setTitlebarAppearsTransparent))); |
| | | Q_ASSERT(oldSetTitlebarAppearsTransparent); |
| | | method = |
| | | class_getInstanceMethod(windowClass, @selector(setTitlebarAppearsTransparent:)); |
| | | Q_ASSERT(method); |
| | | oldSetTitlebarAppearsTransparent = |
| | | reinterpret_cast<setTitlebarAppearsTransparentPtr>(method_setImplementation( |
| | | method, reinterpret_cast<IMP>(setTitlebarAppearsTransparent))); |
| | | Q_ASSERT(oldSetTitlebarAppearsTransparent); |
| | | |
| | | #if 0 |
| | | method = class_getInstanceMethod(windowClass, @selector(canBecomeKeyWindow)); |
| | |
| | | Q_ASSERT(oldCanBecomeMainWindow); |
| | | #endif |
| | | |
| | | method = class_getInstanceMethod(windowClass, @selector(sendEvent:)); |
| | | Q_ASSERT(method); |
| | | oldSendEvent = reinterpret_cast<sendEventPtr>(method_setImplementation(method, reinterpret_cast<IMP>(sendEvent))); |
| | | Q_ASSERT(oldSendEvent); |
| | | } |
| | | method = class_getInstanceMethod(windowClass, @selector(sendEvent:)); |
| | | Q_ASSERT(method); |
| | | oldSendEvent = reinterpret_cast<sendEventPtr>( |
| | | method_setImplementation(method, reinterpret_cast<IMP>(sendEvent))); |
| | | Q_ASSERT(oldSendEvent); |
| | | } |
| | | |
| | | void restoreImplementations() |
| | | { |
| | | Method method = class_getInstanceMethod(windowClass, @selector(setStyleMask:)); |
| | | Q_ASSERT(method); |
| | | method_setImplementation(method, reinterpret_cast<IMP>(oldSetStyleMask)); |
| | | oldSetStyleMask = nil; |
| | | void restoreImplementations() { |
| | | Method method = class_getInstanceMethod(windowClass, @selector(setStyleMask:)); |
| | | Q_ASSERT(method); |
| | | method_setImplementation(method, reinterpret_cast<IMP>(oldSetStyleMask)); |
| | | oldSetStyleMask = nil; |
| | | |
| | | method = class_getInstanceMethod(windowClass, @selector(setTitlebarAppearsTransparent:)); |
| | | Q_ASSERT(method); |
| | | method_setImplementation(method, reinterpret_cast<IMP>(oldSetTitlebarAppearsTransparent)); |
| | | oldSetTitlebarAppearsTransparent = nil; |
| | | method = |
| | | class_getInstanceMethod(windowClass, @selector(setTitlebarAppearsTransparent:)); |
| | | Q_ASSERT(method); |
| | | method_setImplementation(method, |
| | | reinterpret_cast<IMP>(oldSetTitlebarAppearsTransparent)); |
| | | oldSetTitlebarAppearsTransparent = nil; |
| | | |
| | | #if 0 |
| | | method = class_getInstanceMethod(windowClass, @selector(canBecomeKeyWindow)); |
| | |
| | | oldCanBecomeMainWindow = nil; |
| | | #endif |
| | | |
| | | method = class_getInstanceMethod(windowClass, @selector(sendEvent:)); |
| | | Q_ASSERT(method); |
| | | method_setImplementation(method, reinterpret_cast<IMP>(oldSendEvent)); |
| | | oldSendEvent = nil; |
| | | } |
| | | method = class_getInstanceMethod(windowClass, @selector(sendEvent:)); |
| | | Q_ASSERT(method); |
| | | method_setImplementation(method, reinterpret_cast<IMP>(oldSendEvent)); |
| | | oldSendEvent = nil; |
| | | } |
| | | |
| | | void setSystemTitleBarVisible(const bool visible) |
| | | { |
| | | NSView *nsview = [nswindow contentView]; |
| | | if (!nsview) { |
| | | return; |
| | | } |
| | | nsview.wantsLayer = YES; |
| | | nswindow.styleMask |= NSWindowStyleMaskResizable; |
| | | if (visible) { |
| | | nswindow.styleMask &= ~NSWindowStyleMaskFullSizeContentView; |
| | | } else { |
| | | nswindow.styleMask |= NSWindowStyleMaskFullSizeContentView; |
| | | } |
| | | nswindow.titlebarAppearsTransparent = (visible ? NO : YES); |
| | | nswindow.titleVisibility = (visible ? NSWindowTitleVisible : NSWindowTitleHidden); |
| | | nswindow.hasShadow = YES; |
| | | nswindow.showsToolbarButton = NO; |
| | | nswindow.movableByWindowBackground = NO; |
| | | nswindow.movable = NO; |
| | | // For some unknown reason, we don't need the following hack in Qt versions below or equal to 6.2.4. |
| | | void setSystemTitleBarVisible(const bool visible) { |
| | | NSView *nsview = [nswindow contentView]; |
| | | if (!nsview) { |
| | | return; |
| | | } |
| | | nsview.wantsLayer = YES; |
| | | nswindow.styleMask |= NSWindowStyleMaskResizable; |
| | | if (visible) { |
| | | nswindow.styleMask &= ~NSWindowStyleMaskFullSizeContentView; |
| | | } else { |
| | | nswindow.styleMask |= NSWindowStyleMaskFullSizeContentView; |
| | | } |
| | | nswindow.titlebarAppearsTransparent = (visible ? NO : YES); |
| | | nswindow.titleVisibility = (visible ? NSWindowTitleVisible : NSWindowTitleHidden); |
| | | nswindow.hasShadow = YES; |
| | | nswindow.showsToolbarButton = NO; |
| | | nswindow.movableByWindowBackground = NO; |
| | | nswindow.movable = NO; |
| | | // For some unknown reason, we don't need the following hack in Qt versions below or |
| | | // equal to 6.2.4. |
| | | #if (QT_VERSION > QT_VERSION_CHECK(6, 2, 4)) |
| | | [nswindow standardWindowButton:NSWindowCloseButton].hidden = (visible ? NO : YES); |
| | | [nswindow standardWindowButton:NSWindowMiniaturizeButton].hidden = (visible ? NO : YES); |
| | | [nswindow standardWindowButton:NSWindowZoomButton].hidden = (visible ? NO : YES); |
| | | [nswindow standardWindowButton:NSWindowCloseButton].hidden = (visible ? NO : YES); |
| | | [nswindow standardWindowButton:NSWindowMiniaturizeButton].hidden = (visible ? NO : YES); |
| | | [nswindow standardWindowButton:NSWindowZoomButton].hidden = (visible ? NO : YES); |
| | | #endif |
| | | } |
| | | } |
| | | |
| | | private: |
| | | static BOOL canBecomeKeyWindow(id obj, SEL sel) |
| | | { |
| | | if (instances.contains(reinterpret_cast<NSWindow *>(obj))) { |
| | | private: |
| | | static BOOL canBecomeKeyWindow(id obj, SEL sel) { |
| | | if (instances.contains(reinterpret_cast<NSWindow *>(obj))) { |
| | | return YES; |
| | | } |
| | | |
| | | if (oldCanBecomeKeyWindow) { |
| | | return oldCanBecomeKeyWindow(obj, sel); |
| | | } |
| | | |
| | | return YES; |
| | | } |
| | | |
| | | if (oldCanBecomeKeyWindow) { |
| | | return oldCanBecomeKeyWindow(obj, sel); |
| | | } |
| | | static BOOL canBecomeMainWindow(id obj, SEL sel) { |
| | | if (instances.contains(reinterpret_cast<NSWindow *>(obj))) { |
| | | return YES; |
| | | } |
| | | |
| | | return YES; |
| | | } |
| | | if (oldCanBecomeMainWindow) { |
| | | return oldCanBecomeMainWindow(obj, sel); |
| | | } |
| | | |
| | | static BOOL canBecomeMainWindow(id obj, SEL sel) |
| | | { |
| | | if (instances.contains(reinterpret_cast<NSWindow *>(obj))) { |
| | | return YES; |
| | | } |
| | | |
| | | if (oldCanBecomeMainWindow) { |
| | | return oldCanBecomeMainWindow(obj, sel); |
| | | static void setStyleMask(id obj, SEL sel, NSWindowStyleMask styleMask) { |
| | | if (instances.contains(reinterpret_cast<NSWindow *>(obj))) { |
| | | styleMask |= NSWindowStyleMaskFullSizeContentView; |
| | | } |
| | | |
| | | if (oldSetStyleMask) { |
| | | oldSetStyleMask(obj, sel, styleMask); |
| | | } |
| | | } |
| | | |
| | | return YES; |
| | | } |
| | | static void setTitlebarAppearsTransparent(id obj, SEL sel, BOOL transparent) { |
| | | if (instances.contains(reinterpret_cast<NSWindow *>(obj))) { |
| | | transparent = YES; |
| | | } |
| | | |
| | | static void setStyleMask(id obj, SEL sel, NSWindowStyleMask styleMask) |
| | | { |
| | | if (instances.contains(reinterpret_cast<NSWindow *>(obj))) { |
| | | styleMask |= NSWindowStyleMaskFullSizeContentView; |
| | | if (oldSetTitlebarAppearsTransparent) { |
| | | oldSetTitlebarAppearsTransparent(obj, sel, transparent); |
| | | } |
| | | } |
| | | |
| | | if (oldSetStyleMask) { |
| | | oldSetStyleMask(obj, sel, styleMask); |
| | | } |
| | | } |
| | | |
| | | static void setTitlebarAppearsTransparent(id obj, SEL sel, BOOL transparent) |
| | | { |
| | | if (instances.contains(reinterpret_cast<NSWindow *>(obj))) { |
| | | transparent = YES; |
| | | } |
| | | |
| | | if (oldSetTitlebarAppearsTransparent) { |
| | | oldSetTitlebarAppearsTransparent(obj, sel, transparent); |
| | | } |
| | | } |
| | | |
| | | static void sendEvent(id obj, SEL sel, NSEvent *event) |
| | | { |
| | | if (oldSendEvent) { |
| | | oldSendEvent(obj, sel, event); |
| | | } |
| | | static void sendEvent(id obj, SEL sel, NSEvent *event) { |
| | | if (oldSendEvent) { |
| | | oldSendEvent(obj, sel, event); |
| | | } |
| | | |
| | | #if 0 |
| | | const auto nswindow = reinterpret_cast<NSWindow *>(obj); |
| | |
| | | proxy->lastMouseDownEvent = nil; |
| | | } |
| | | #endif |
| | | } |
| | | } |
| | | |
| | | private: |
| | | NSWindow *nswindow = nil; |
| | | //NSEvent *lastMouseDownEvent = nil; |
| | | private: |
| | | NSWindow *nswindow = nil; |
| | | // NSEvent *lastMouseDownEvent = nil; |
| | | |
| | | static inline QHash<NSWindow *, NSWindowProxy *> instances = {}; |
| | | static inline QHash<NSWindow *, NSWindowProxy *> instances = {}; |
| | | |
| | | static inline Class windowClass = nil; |
| | | static inline Class windowClass = nil; |
| | | |
| | | using setStyleMaskPtr = void(*)(id, SEL, NSWindowStyleMask); |
| | | static inline setStyleMaskPtr oldSetStyleMask = nil; |
| | | using setStyleMaskPtr = void (*)(id, SEL, NSWindowStyleMask); |
| | | static inline setStyleMaskPtr oldSetStyleMask = nil; |
| | | |
| | | using setTitlebarAppearsTransparentPtr = void(*)(id, SEL, BOOL); |
| | | static inline setTitlebarAppearsTransparentPtr oldSetTitlebarAppearsTransparent = nil; |
| | | using setTitlebarAppearsTransparentPtr = void (*)(id, SEL, BOOL); |
| | | static inline setTitlebarAppearsTransparentPtr oldSetTitlebarAppearsTransparent = nil; |
| | | |
| | | using canBecomeKeyWindowPtr = BOOL(*)(id, SEL); |
| | | static inline canBecomeKeyWindowPtr oldCanBecomeKeyWindow = nil; |
| | | using canBecomeKeyWindowPtr = BOOL (*)(id, SEL); |
| | | static inline canBecomeKeyWindowPtr oldCanBecomeKeyWindow = nil; |
| | | |
| | | using canBecomeMainWindowPtr = BOOL(*)(id, SEL); |
| | | static inline canBecomeMainWindowPtr oldCanBecomeMainWindow = nil; |
| | | using canBecomeMainWindowPtr = BOOL (*)(id, SEL); |
| | | static inline canBecomeMainWindowPtr oldCanBecomeMainWindow = nil; |
| | | |
| | | using sendEventPtr = void(*)(id, SEL, NSEvent *); |
| | | static inline sendEventPtr oldSendEvent = nil; |
| | | }; |
| | | using sendEventPtr = void (*)(id, SEL, NSEvent *); |
| | | static inline sendEventPtr oldSendEvent = nil; |
| | | }; |
| | | |
| | | using ProxyList = QHash<WId, NSWindowProxy *>; |
| | | Q_GLOBAL_STATIC(ProxyList, g_proxyList); |
| | | using ProxyList = QHash<WId, NSWindowProxy *>; |
| | | Q_GLOBAL_STATIC(ProxyList, g_proxyList); |
| | | |
| | | static inline NSWindow *mac_getNSWindow(const WId windowId) |
| | | { |
| | | static inline NSWindow *mac_getNSWindow(const WId windowId) { |
| | | const auto nsview = reinterpret_cast<NSView *>(windowId); |
| | | if (!nsview) { |
| | | return nil; |
| | |
| | | return [nsview window]; |
| | | } |
| | | |
| | | static inline void cleanupProxy() |
| | | { |
| | | if (g_proxyList()->isEmpty()) { |
| | | return; |
| | | } |
| | | const auto &data = *g_proxyList(); |
| | | qDeleteAll(data); |
| | | g_proxyList()->clear(); |
| | | } |
| | | |
| | | static inline NSWindowProxy *ensureWindowProxy(const WId windowId) |
| | | { |
| | | auto it = g_proxyList()->find(windowId); |
| | | if (it == g_proxyList()->end()) { |
| | | NSWindow *nswindow = mac_getNSWindow(windowId); |
| | | if (!nswindow) { |
| | | return nil; |
| | | static inline void cleanupProxy() { |
| | | if (g_proxyList()->isEmpty()) { |
| | | return; |
| | | } |
| | | const auto proxy = new NSWindowProxy(nswindow); |
| | | it = g_proxyList()->insert(windowId, proxy); |
| | | const auto &data = *g_proxyList(); |
| | | qDeleteAll(data); |
| | | g_proxyList()->clear(); |
| | | } |
| | | static bool cleanerInstalled = false; |
| | | if (!cleanerInstalled) { |
| | | cleanerInstalled = true; |
| | | qAddPostRoutine(cleanupProxy); |
| | | |
| | | static inline NSWindowProxy *ensureWindowProxy(const WId windowId) { |
| | | auto it = g_proxyList()->find(windowId); |
| | | if (it == g_proxyList()->end()) { |
| | | NSWindow *nswindow = mac_getNSWindow(windowId); |
| | | if (!nswindow) { |
| | | return nil; |
| | | } |
| | | const auto proxy = new NSWindowProxy(nswindow); |
| | | it = g_proxyList()->insert(windowId, proxy); |
| | | } |
| | | static bool cleanerInstalled = false; |
| | | if (!cleanerInstalled) { |
| | | cleanerInstalled = true; |
| | | qAddPostRoutine(cleanupProxy); |
| | | } |
| | | return it.value(); |
| | | } |
| | | return it.value(); |
| | | } |
| | | |
| | | CocoaWindowContext::CocoaWindowContext() { |
| | | } |
| | |
| | | |
| | | bool CocoaWindowContext::setupHost() { |
| | | #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) |
| | | m_windowHandle->setProperty("_q_mac_wantsLayer", 1); |
| | | m_windowHandle->setProperty("_q_mac_wantsLayer", 1); |
| | | #endif |
| | | WId winId = m_windowHandle->winId(); |
| | | ensureWindowProxy(winId)->setSystemTitleBarVisible(false); |
| | |
| | | } |
| | | |
| | | } |
| | | |
| | | #include "cocoawindowcontext.moc" |