Skip to content

Commit

Permalink
Input:Quartz: Release cursor during modal popups
Browse files Browse the repository at this point in the history
  • Loading branch information
TellowKrinkle committed Apr 21, 2024
1 parent 21dbd6a commit bfc1676
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@
@interface DolWindowPositionObserver : NSObject

- (instancetype)initWithView:(NSView*)view;
- (void)addKeyWindowChangeCallback:(void(*)(void*, bool))callback ctx:(void*)ctx;
- (void)removeKeyWindowChangeCallback:(void*)ctx;
@property(readonly) NSRect frame;
@property(readonly, getter=isKeyWindow) bool keyWindow;

@end
#else
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ @implementation DolWindowPositionObserver
NSView* _view;
NSWindow* _window;
NSRect _frame;
std::atomic<bool> _is_key;
std::mutex _mtx;
std::vector<std::pair<void(*)(void*, bool), void*>> _key_callbacks;
}

- (NSRect)calcFrame
Expand All @@ -35,7 +37,12 @@ - (instancetype)initWithView:(NSView*)view
_view = view;
_window = [view window];
_frame = [self calcFrame];
_is_key.store([_window isKeyWindow], std::memory_order_relaxed);
[_window addObserver:self forKeyPath:@"frame" options:0 context:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyWindowDidChange:)
name:NSWindowDidBecomeKeyNotification
object:nil];
}
return self;
}
Expand All @@ -46,6 +53,38 @@ - (NSRect)frame
return _frame;
}

- (bool)isKeyWindow
{
return _is_key.load(std::memory_order_relaxed);
}

- (void)addKeyWindowChangeCallback:(void(*)(void*, bool))callback ctx:(void*)ctx;
{
std::lock_guard<std::mutex> guard(_mtx);
_key_callbacks.push_back(std::make_pair(callback, ctx));
}

- (void)removeKeyWindowChangeCallback:(void*)ctx
{
std::lock_guard<std::mutex> guard(_mtx);
_key_callbacks.erase(std::remove_if(_key_callbacks.begin(), _key_callbacks.end(),
[ctx](auto& entry){ return entry.second == ctx; }),
_key_callbacks.end());
}

- (void)keyWindowDidChange:(id)window
{
bool key = [_window isKeyWindow];
bool changed = key != _is_key.load(std::memory_order_relaxed);
_is_key.store(key, std::memory_order_relaxed);
if (changed)
{
std::lock_guard<std::mutex> guard(_mtx);
for (const auto& callback : _key_callbacks)
callback.first(callback.second, key);
}
}

- (void)observeValueForKeyPath:(NSString*)keyPath
ofObject:(id)object
change:(NSDictionary<NSKeyValueChangeKey, id>*)change
Expand All @@ -62,6 +101,7 @@ - (void)observeValueForKeyPath:(NSString*)keyPath
- (void)dealloc
{
[_window removeObserver:self forKeyPath:@"frame"];
[[NSNotificationCenter defaultCenter] removeObserver:self];
}

@end
Expand Down Expand Up @@ -209,7 +249,7 @@ - (void)dealloc
MainThreadInitialization(view);
else
dispatch_sync(dispatch_get_main_queue(), [this, view] { MainThreadInitialization(view); });
prime::InitQuartzInputMouse();
prime::InitQuartzInputMouse(m_window_pos_observer);

// cursor, with a hax for-loop
for (unsigned int i = 0; i < 4; ++i)
Expand Down
9 changes: 6 additions & 3 deletions Source/Core/InputCommon/QuartzInputMouse.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,25 @@ typedef void* id;
namespace prime
{

bool InitQuartzInputMouse();
bool InitQuartzInputMouse(DolWindowPositionObserver* window);

class QuartzInputMouse: public GenericMouse
{
public:
explicit QuartzInputMouse();
explicit QuartzInputMouse(DolWindowPositionObserver* window);
~QuartzInputMouse();
void InputCallback(NSEvent* event);
void UpdateInput() override;
void LockCursorToGameWindow() override;
void UnlockCursor();

private:
NSEvent*(^m_event_callback)(NSEvent*);
id m_monitor = nullptr;
std::atomic<void*> m_monitor{nullptr};
DolWindowPositionObserver* m_window;
std::atomic<int32_t> thread_dx;
std::atomic<int32_t> thread_dy;
std::mutex m_mtx;
};

}
50 changes: 37 additions & 13 deletions Source/Core/InputCommon/QuartzInputMouse.mm
Original file line number Diff line number Diff line change
Expand Up @@ -15,24 +15,34 @@
namespace prime
{

bool InitQuartzInputMouse()
bool InitQuartzInputMouse(DolWindowPositionObserver* window)
{
g_mouse_input.reset(new QuartzInputMouse());
g_mouse_input.reset(new QuartzInputMouse(window));
return true;
}

QuartzInputMouse::QuartzInputMouse()
QuartzInputMouse::QuartzInputMouse(DolWindowPositionObserver* window)
{
m_window = window;
m_event_callback = ^NSEvent*(NSEvent* event) {
InputCallback(event);
return event;
};
void (*key_callback)(void*, bool) = [](void* ctx, bool key){
if (key)
return;
QuartzInputMouse* me = static_cast<QuartzInputMouse*>(ctx);
std::lock_guard<std::mutex> lock(me->m_mtx);
me->UnlockCursor();
};
[m_window addKeyWindowChangeCallback:key_callback ctx:this];
}

QuartzInputMouse::~QuartzInputMouse()
{
if (m_monitor)
[NSEvent removeMonitor:m_monitor];
[m_window removeKeyWindowChangeCallback:this];
if (void* monitor = m_monitor.load(std::memory_order_relaxed))
[NSEvent removeMonitor:(__bridge id)monitor];
}

void QuartzInputMouse::InputCallback(NSEvent* event)
Expand All @@ -50,8 +60,13 @@ bool InitQuartzInputMouse()

void QuartzInputMouse::LockCursorToGameWindow()
{
bool wants_locked = Host_RendererHasFocus() && cursor_locked;
bool is_locked = m_monitor;
bool wants_locked = Host_RendererHasFocus() && cursor_locked && [m_window isKeyWindow];
bool is_locked = m_monitor.load(std::memory_order_relaxed);
if (wants_locked == is_locked)
return;
std::lock_guard<std::mutex> lock(m_mtx);
wants_locked = Host_RendererHasFocus() && cursor_locked && [m_window isKeyWindow];
is_locked = m_monitor.load(std::memory_order_relaxed);
if (wants_locked == is_locked)
return;
if (wants_locked)
Expand All @@ -62,17 +77,26 @@ bool InitQuartzInputMouse()
// Clear any accumulated movement
thread_dx.store(0, std::memory_order_relaxed);
thread_dy.store(0, std::memory_order_relaxed);
m_monitor = [NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskMouseMoved
handler:m_event_callback];
id monitor = [NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskMouseMoved
handler:m_event_callback];
m_monitor.store((__bridge void*)monitor, std::memory_order_relaxed);
}
else
{
CGAssociateMouseAndMouseCursorPosition(true);
[NSCursor unhide];
[NSEvent removeMonitor:m_monitor];
m_monitor = nullptr;
UnlockCursor();
cursor_locked = false;
}
}

void QuartzInputMouse::UnlockCursor()
{
void* monitor = m_monitor.load(std::memory_order_relaxed);
if (!monitor)
return;
CGAssociateMouseAndMouseCursorPosition(true);
[NSCursor unhide];
[NSEvent removeMonitor:(__bridge id)monitor];
m_monitor.store(nullptr, std::memory_order_relaxed);
}

}

0 comments on commit bfc1676

Please sign in to comment.