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 "Common/Matrix.h"
|
||||||
#include "InputCommon/ControllerInterface/CoreDevice.h"
|
#include "InputCommon/ControllerInterface/CoreDevice.h"
|
||||||
|
|
||||||
|
#ifdef __OBJC__
|
||||||
|
@class DolWindowPositionObserver;
|
||||||
|
#else
|
||||||
|
class DolWindowPositionObserver;
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace ciface::Quartz
|
namespace ciface::Quartz
|
||||||
{
|
{
|
||||||
std::string KeycodeToName(const CGKeyCode keycode);
|
std::string KeycodeToName(const CGKeyCode keycode);
|
||||||
|
@ -59,13 +65,16 @@ public:
|
||||||
void UpdateInput() override;
|
void UpdateInput() override;
|
||||||
|
|
||||||
explicit KeyboardAndMouse(void* view);
|
explicit KeyboardAndMouse(void* view);
|
||||||
|
~KeyboardAndMouse() override;
|
||||||
|
|
||||||
std::string GetName() const override;
|
std::string GetName() const override;
|
||||||
std::string GetSource() const override;
|
std::string GetSource() const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void MainThreadInitialization(void* view);
|
||||||
|
|
||||||
Common::Vec2 m_cursor;
|
Common::Vec2 m_cursor;
|
||||||
|
|
||||||
uint32_t m_windowid;
|
DolWindowPositionObserver* m_window_pos_observer;
|
||||||
};
|
};
|
||||||
} // namespace ciface::Quartz
|
} // namespace ciface::Quartz
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include "InputCommon/ControllerInterface/Quartz/QuartzKeyboardAndMouse.h"
|
#include "InputCommon/ControllerInterface/Quartz/QuartzKeyboardAndMouse.h"
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
#include <Carbon/Carbon.h>
|
#include <Carbon/Carbon.h>
|
||||||
#include <Cocoa/Cocoa.h>
|
#include <Cocoa/Cocoa.h>
|
||||||
|
@ -12,6 +13,64 @@
|
||||||
|
|
||||||
#include "InputCommon/ControllerInterface/ControllerInterface.h"
|
#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
|
namespace ciface::Quartz
|
||||||
{
|
{
|
||||||
std::string KeycodeToName(const CGKeyCode keycode)
|
std::string KeycodeToName(const CGKeyCode keycode)
|
||||||
|
@ -149,20 +208,12 @@ KeyboardAndMouse::KeyboardAndMouse(void* view)
|
||||||
AddCombinedInput("Shift", {"Left Shift", "Right Shift"});
|
AddCombinedInput("Shift", {"Left Shift", "Right Shift"});
|
||||||
AddCombinedInput("Ctrl", {"Left Control", "Right Control"});
|
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
|
// PopulateDevices may be called on the Emuthread, so we need to ensure that
|
||||||
// these UI APIs are only ever called on the main thread.
|
// these UI APIs are only ever called on the main thread.
|
||||||
if ([NSThread isMainThread])
|
if ([NSThread isMainThread])
|
||||||
{
|
MainThreadInitialization(view);
|
||||||
m_windowid = [[cocoa_view window] windowNumber];
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
dispatch_sync(dispatch_get_main_queue(), [this, view] { MainThreadInitialization(view); });
|
||||||
dispatch_sync(dispatch_get_main_queue(), ^{
|
|
||||||
m_windowid = [[cocoa_view window] windowNumber];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// cursor, with a hax for-loop
|
// cursor, with a hax for-loop
|
||||||
for (unsigned int i = 0; i < 4; ++i)
|
for (unsigned int i = 0; i < 4; ++i)
|
||||||
|
@ -173,26 +224,19 @@ KeyboardAndMouse::KeyboardAndMouse(void* view)
|
||||||
AddInput(new Button(kCGMouseButtonCenter));
|
AddInput(new Button(kCGMouseButtonCenter));
|
||||||
}
|
}
|
||||||
|
|
||||||
void KeyboardAndMouse::UpdateInput()
|
// Very important that this is here
|
||||||
{
|
// C++ and ObjC++ have different views of the header, and only ObjC++'s will deallocate properly
|
||||||
CGRect bounds = CGRectZero;
|
KeyboardAndMouse::~KeyboardAndMouse() = default;
|
||||||
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));
|
|
||||||
|
|
||||||
if (CFDictionaryContainsKey(windowDescription, kCGWindowBounds))
|
void KeyboardAndMouse::MainThreadInitialization(void* view)
|
||||||
{
|
{
|
||||||
CFDictionaryRef boundsDictionary =
|
NSView* cocoa_view = (__bridge NSView*)view;
|
||||||
static_cast<CFDictionaryRef>(CFDictionaryGetValue(windowDescription, kCGWindowBounds));
|
m_window_pos_observer = [[DolWindowPositionObserver alloc] initWithView:cocoa_view];
|
||||||
|
|
||||||
if (boundsDictionary != nullptr)
|
|
||||||
CGRectMakeWithDictionaryRepresentation(boundsDictionary, &bounds);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CFRelease(windowDescriptions);
|
void KeyboardAndMouse::UpdateInput()
|
||||||
CFRelease(windowArray);
|
{
|
||||||
|
NSRect bounds = [m_window_pos_observer frame];
|
||||||
|
|
||||||
const double window_width = std::max(bounds.size.width, 1.0);
|
const double window_width = std::max(bounds.size.width, 1.0);
|
||||||
const double window_height = std::max(bounds.size.height, 1.0);
|
const double window_height = std::max(bounds.size.height, 1.0);
|
||||||
|
|
Loading…
Reference in New Issue