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
This commit is contained in:
parent
b96bc4267e
commit
798b241832
|
@ -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
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "InputCommon/ControllerInterface/Quartz/QuartzKeyboardAndMouse.h"
|
||||
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
|
||||
#include <Carbon/Carbon.h>
|
||||
#include <Cocoa/Cocoa.h>
|
||||
|
@ -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<std::mutex> guard(_mtx);
|
||||
return _frame;
|
||||
}
|
||||
|
||||
- (void)observeValueForKeyPath:(NSString*)keyPath
|
||||
ofObject:(id)object
|
||||
change:(NSDictionary<NSKeyValueChangeKey, id>*)change
|
||||
context:(void*)context
|
||||
{
|
||||
if (object == _window)
|
||||
{
|
||||
NSRect new_frame = [self calcFrame];
|
||||
std::lock_guard<std::mutex> 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));
|
||||
}
|
||||
|
||||
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<CFDictionaryRef>(CFArrayGetValueAtIndex(windowDescriptions, 0));
|
||||
// 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;
|
||||
|
||||
if (CFDictionaryContainsKey(windowDescription, kCGWindowBounds))
|
||||
void KeyboardAndMouse::MainThreadInitialization(void* view)
|
||||
{
|
||||
CFDictionaryRef boundsDictionary =
|
||||
static_cast<CFDictionaryRef>(CFDictionaryGetValue(windowDescription, kCGWindowBounds));
|
||||
|
||||
if (boundsDictionary != nullptr)
|
||||
CGRectMakeWithDictionaryRepresentation(boundsDictionary, &bounds);
|
||||
NSView* cocoa_view = (__bridge NSView*)view;
|
||||
m_window_pos_observer = [[DolWindowPositionObserver alloc] initWithView:cocoa_view];
|
||||
}
|
||||
|
||||
CFRelease(windowDescriptions);
|
||||
CFRelease(windowArray);
|
||||
void KeyboardAndMouse::UpdateInput()
|
||||
{
|
||||
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);
|
||||
|
|
Loading…
Reference in New Issue