From b96bc4267ea4f87a332a349187e0038fdcf6253c Mon Sep 17 00:00:00 2001 From: TellowKrinkle Date: Fri, 12 Aug 2022 02:09:53 -0500 Subject: [PATCH 1/3] InputCommon: Enable ARC for obj-c++ --- Source/Core/InputCommon/CMakeLists.txt | 3 +++ .../ControllerInterface/Quartz/QuartzKeyboardAndMouse.mm | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Source/Core/InputCommon/CMakeLists.txt b/Source/Core/InputCommon/CMakeLists.txt index 06ffb37a2d..80c2415744 100644 --- a/Source/Core/InputCommon/CMakeLists.txt +++ b/Source/Core/InputCommon/CMakeLists.txt @@ -124,6 +124,9 @@ elseif(APPLE) ${FORCEFEEDBACK_LIBRARY} ${IOK_LIBRARY} ) + target_compile_options(inputcommon PRIVATE + -fobjc-arc + ) elseif(X11_FOUND) target_sources(inputcommon PRIVATE ControllerInterface/Xlib/XInput2.cpp diff --git a/Source/Core/InputCommon/ControllerInterface/Quartz/QuartzKeyboardAndMouse.mm b/Source/Core/InputCommon/ControllerInterface/Quartz/QuartzKeyboardAndMouse.mm index b8ae0219be..9cc8d07926 100644 --- a/Source/Core/InputCommon/ControllerInterface/Quartz/QuartzKeyboardAndMouse.mm +++ b/Source/Core/InputCommon/ControllerInterface/Quartz/QuartzKeyboardAndMouse.mm @@ -149,7 +149,7 @@ KeyboardAndMouse::KeyboardAndMouse(void* view) AddCombinedInput("Shift", {"Left Shift", "Right Shift"}); AddCombinedInput("Ctrl", {"Left Control", "Right Control"}); - NSView* cocoa_view = reinterpret_cast(view); + 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. From 798b241832270bcb7fbc35b07bc297a288e64372 Mon Sep 17 00:00:00 2001 From: TellowKrinkle Date: Fri, 12 Aug 2022 03:02:47 -0500 Subject: [PATCH 2/3] InputCommon:QuarzKB&M: Use KVO to watch window position CGWindowListCreateDescriptionFromArray would block for up to ~1ms, which isn't a great thing to do on the main emulation thread --- .../Quartz/QuartzKeyboardAndMouse.h | 11 +- .../Quartz/QuartzKeyboardAndMouse.mm | 100 +++++++++++++----- 2 files changed, 82 insertions(+), 29 deletions(-) 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); From 588c4bd6355f20dd74008054b1bb876810f4ba13 Mon Sep 17 00:00:00 2001 From: TellowKrinkle Date: Fri, 12 Aug 2022 03:06:33 -0500 Subject: [PATCH 3/3] InputCommon:QuarzKB&M: Use view position instead of window position --- .../ControllerInterface/Quartz/QuartzKeyboardAndMouse.mm | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Source/Core/InputCommon/ControllerInterface/Quartz/QuartzKeyboardAndMouse.mm b/Source/Core/InputCommon/ControllerInterface/Quartz/QuartzKeyboardAndMouse.mm index 9b82ef591c..a610956d93 100644 --- a/Source/Core/InputCommon/ControllerInterface/Quartz/QuartzKeyboardAndMouse.mm +++ b/Source/Core/InputCommon/ControllerInterface/Quartz/QuartzKeyboardAndMouse.mm @@ -23,6 +23,7 @@ @implementation DolWindowPositionObserver { + NSView* _view; NSWindow* _window; NSRect _frame; std::mutex _mtx; @@ -30,7 +31,7 @@ - (NSRect)calcFrame { - return [_window frame]; + return [_window convertRectToScreen:[_view frame]]; } - (instancetype)initWithView:(NSView*)view @@ -38,6 +39,7 @@ self = [super init]; if (self) { + _view = view; _window = [view window]; _frame = [self calcFrame]; [_window addObserver:self forKeyPath:@"frame" options:0 context:nil];