diff --git a/Source/Core/InputCommon/ControllerInterface/Quartz/QuartzKeyboardAndMouse.h b/Source/Core/InputCommon/ControllerInterface/Quartz/QuartzKeyboardAndMouse.h index dfcf216e45..e4bdb6e5fb 100644 --- a/Source/Core/InputCommon/ControllerInterface/Quartz/QuartzKeyboardAndMouse.h +++ b/Source/Core/InputCommon/ControllerInterface/Quartz/QuartzKeyboardAndMouse.h @@ -8,6 +8,12 @@ #include "Common/Matrix.h" #include "InputCommon/ControllerInterface/CoreDevice.h" +#ifdef __OBJC__ +@class DolWindowPositionObserver; +#else +class DolWindowPositionObserver; +#endif + namespace ciface::Quartz { std::string KeycodeToName(const CGKeyCode keycode); @@ -59,13 +65,16 @@ public: void UpdateInput() override; explicit KeyboardAndMouse(void* view); + ~KeyboardAndMouse() override; std::string GetName() const override; std::string GetSource() const override; private: + void MainThreadInitialization(void* view); + Common::Vec2 m_cursor; - uint32_t m_windowid; + DolWindowPositionObserver* m_window_pos_observer; }; } // namespace ciface::Quartz diff --git a/Source/Core/InputCommon/ControllerInterface/Quartz/QuartzKeyboardAndMouse.mm b/Source/Core/InputCommon/ControllerInterface/Quartz/QuartzKeyboardAndMouse.mm index 9cc8d07926..9b82ef591c 100644 --- a/Source/Core/InputCommon/ControllerInterface/Quartz/QuartzKeyboardAndMouse.mm +++ b/Source/Core/InputCommon/ControllerInterface/Quartz/QuartzKeyboardAndMouse.mm @@ -4,6 +4,7 @@ #include "InputCommon/ControllerInterface/Quartz/QuartzKeyboardAndMouse.h" #include +#include #include #include @@ -12,6 +13,64 @@ #include "InputCommon/ControllerInterface/ControllerInterface.h" +/// Helper class to get window position data from threads other than the main thread +@interface DolWindowPositionObserver : NSObject + +- (instancetype)initWithView:(NSView*)view; +@property(readonly) NSRect frame; + +@end + +@implementation DolWindowPositionObserver +{ + NSWindow* _window; + NSRect _frame; + std::mutex _mtx; +} + +- (NSRect)calcFrame +{ + return [_window frame]; +} + +- (instancetype)initWithView:(NSView*)view +{ + self = [super init]; + if (self) + { + _window = [view window]; + _frame = [self calcFrame]; + [_window addObserver:self forKeyPath:@"frame" options:0 context:nil]; + } + return self; +} + +- (NSRect)frame +{ + std::lock_guard guard(_mtx); + return _frame; +} + +- (void)observeValueForKeyPath:(NSString*)keyPath + ofObject:(id)object + change:(NSDictionary*)change + context:(void*)context +{ + if (object == _window) + { + NSRect new_frame = [self calcFrame]; + std::lock_guard guard(_mtx); + _frame = new_frame; + } +} + +- (void)dealloc +{ + [_window removeObserver:self forKeyPath:@"frame"]; +} + +@end + namespace ciface::Quartz { std::string KeycodeToName(const CGKeyCode keycode) @@ -149,20 +208,12 @@ KeyboardAndMouse::KeyboardAndMouse(void* view) AddCombinedInput("Shift", {"Left Shift", "Right Shift"}); AddCombinedInput("Ctrl", {"Left Control", "Right Control"}); - NSView* cocoa_view = (__bridge NSView*)view; - // PopulateDevices may be called on the Emuthread, so we need to ensure that // these UI APIs are only ever called on the main thread. if ([NSThread isMainThread]) - { - m_windowid = [[cocoa_view window] windowNumber]; - } + MainThreadInitialization(view); else - { - dispatch_sync(dispatch_get_main_queue(), ^{ - m_windowid = [[cocoa_view window] windowNumber]; - }); - } + dispatch_sync(dispatch_get_main_queue(), [this, view] { MainThreadInitialization(view); }); // cursor, with a hax for-loop for (unsigned int i = 0; i < 4; ++i) @@ -173,26 +224,19 @@ KeyboardAndMouse::KeyboardAndMouse(void* view) AddInput(new Button(kCGMouseButtonCenter)); } +// Very important that this is here +// C++ and ObjC++ have different views of the header, and only ObjC++'s will deallocate properly +KeyboardAndMouse::~KeyboardAndMouse() = default; + +void KeyboardAndMouse::MainThreadInitialization(void* view) +{ + NSView* cocoa_view = (__bridge NSView*)view; + m_window_pos_observer = [[DolWindowPositionObserver alloc] initWithView:cocoa_view]; +} + void KeyboardAndMouse::UpdateInput() { - CGRect bounds = CGRectZero; - CGWindowID windowid[1] = {m_windowid}; - CFArrayRef windowArray = CFArrayCreate(nullptr, (const void**)windowid, 1, nullptr); - CFArrayRef windowDescriptions = CGWindowListCreateDescriptionFromArray(windowArray); - CFDictionaryRef windowDescription = - static_cast(CFArrayGetValueAtIndex(windowDescriptions, 0)); - - if (CFDictionaryContainsKey(windowDescription, kCGWindowBounds)) - { - CFDictionaryRef boundsDictionary = - static_cast(CFDictionaryGetValue(windowDescription, kCGWindowBounds)); - - if (boundsDictionary != nullptr) - CGRectMakeWithDictionaryRepresentation(boundsDictionary, &bounds); - } - - CFRelease(windowDescriptions); - CFRelease(windowArray); + NSRect bounds = [m_window_pos_observer frame]; const double window_width = std::max(bounds.size.width, 1.0); const double window_height = std::max(bounds.size.height, 1.0);