1
0
mirror of https://github.com/TomHarte/CLK.git synced 2025-02-16 18:30:32 +00:00

Adds the option for mouse capture.

This commit is contained in:
Thomas Harte 2019-06-11 16:30:53 -04:00
parent 3c075e9542
commit 15c38e2f15
2 changed files with 151 additions and 11 deletions

View File

@ -63,6 +63,29 @@ typedef NS_ENUM(NSInteger, CSOpenGLViewRedrawEvent) {
*/
- (void)paste:(nonnull id)sender;
@optional
/*!
Supplies a mouse moved event to the delegate. This functions only if
shouldCaptureMouse is set to YES, in which case the view will ensure it captures
the mouse and returns only relative motion
(Cf. CGAssociateMouseAndMouseCursorPosition). It will also elide mouseDragged:
(and rightMouseDragged:, etc) and mouseMoved: events.
*/
- (void)mouseMoved:(nonnull NSEvent *)event;
/*!
Supplies a mouse button down event. This elides mouseDown, rightMouseDown and otherMouseDown.
@c shouldCaptureMouse must be set to @c YES to receive these events.
*/
- (void)mouseDown:(nonnull NSEvent *)event;
/*!
Supplies a mouse button up event. This elides mouseUp, rightMouseUp and otherMouseUp.
@c shouldCaptureMouse must be set to @c YES to receive these events.
*/
- (void)mouseUp:(nonnull NSEvent *)event;
@end
/*!
@ -74,6 +97,8 @@ typedef NS_ENUM(NSInteger, CSOpenGLViewRedrawEvent) {
@property (atomic, weak, nullable) id <CSOpenGLViewDelegate> delegate;
@property (nonatomic, weak, nullable) id <CSOpenGLViewResponderDelegate> responderDelegate;
@property (nonatomic, assign) BOOL shouldCaptureMouse;
/*!
Ends the timer tracking time; should be called prior to giving up the last owning reference
to ensure that any retain cycles implied by the timer are resolved.
@ -89,4 +114,9 @@ typedef NS_ENUM(NSInteger, CSOpenGLViewRedrawEvent) {
*/
- (void)performWithGLContext:(nonnull dispatch_block_t)action;
/*!
Instructs that the mouse cursor, if currently captured, should be released.
*/
- (void)releaseMouse;
@end

View File

@ -19,6 +19,7 @@
NSTrackingArea *_mouseTrackingArea;
NSTimer *_mouseHideTimer;
BOOL _mouseIsCaptured;
}
- (void)prepareOpenGL {
@ -148,6 +149,13 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt
- (void)flagsChanged:(NSEvent *)theEvent {
[self.responderDelegate flagsChanged:theEvent];
// Release the mouse upon a control + command.
if(_mouseIsCaptured &&
theEvent.modifierFlags & NSEventModifierFlagControl &&
theEvent.modifierFlags & NSEventModifierFlagCommand) {
[self releaseMouse];
}
}
- (void)paste:(id)sender {
@ -170,6 +178,10 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt
#pragma mark - Mouse hiding
- (void)setShouldCaptureMouse:(BOOL)shouldCaptureMouse {
_shouldCaptureMouse = shouldCaptureMouse;
}
- (void)updateTrackingAreas {
[super updateTrackingAreas];
@ -185,9 +197,14 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt
[self addTrackingArea:_mouseTrackingArea];
}
- (void)mouseMoved:(NSEvent *)event {
[super mouseMoved:event];
[self scheduleMouseHide];
- (void)scheduleMouseHide {
if(!self.shouldCaptureMouse) {
[_mouseHideTimer invalidate];
_mouseHideTimer = [NSTimer scheduledTimerWithTimeInterval:3.0 repeats:NO block:^(NSTimer * _Nonnull timer) {
[NSCursor setHiddenUntilMouseMoves:YES];
}];
}
}
- (void)mouseEntered:(NSEvent *)event {
@ -195,18 +212,111 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeSt
[self scheduleMouseHide];
}
- (void)scheduleMouseHide {
[_mouseHideTimer invalidate];
_mouseHideTimer = [NSTimer scheduledTimerWithTimeInterval:3.0 repeats:NO block:^(NSTimer * _Nonnull timer) {
[NSCursor setHiddenUntilMouseMoves:YES];
}];
}
- (void)mouseExited:(NSEvent *)event {
[super mouseExited:event];
[_mouseHideTimer invalidate];
_mouseHideTimer = nil;
}
- (void)releaseMouse {
_mouseIsCaptured = NO;
CGAssociateMouseAndMouseCursorPosition(true);
[NSCursor unhide];
}
#pragma mark - Mouse motion
- (void)applyMouseMotion:(NSEvent *)event {
if(!self.shouldCaptureMouse) {
// Mouse capture is off, so don't play games with the cursor, just schedule it to
// hide in the near future.
[self scheduleMouseHide];
} else {
if(_mouseIsCaptured) {
// Mouse capture is on, so move the cursor back to the middle of the window, and
// forward the deltas to the listener.
//
// TODO: should I really need to invert the y coordinate myself? It suggests I
// might have an error in mapping here.
const NSPoint windowCentre = [self convertPoint:CGPointMake(self.bounds.size.width * 0.5, self.bounds.size.height * 0.5) toView:nil];
const NSPoint screenCentre = [self.window convertPointToScreen:windowCentre];
const CGRect screenFrame = self.window.screen.frame;
CGWarpMouseCursorPosition(NSMakePoint(
screenFrame.origin.x + screenCentre.x,
screenFrame.origin.y + screenFrame.size.height - screenCentre.y
));
[self.responderDelegate mouseMoved:event];
}
}
}
- (void)mouseDragged:(NSEvent *)event {
[self applyMouseMotion:event];
[super mouseDragged:event];
}
- (void)rightMouseDragged:(NSEvent *)event {
[self applyMouseMotion:event];
[super rightMouseDragged:event];
}
- (void)otherMouseDragged:(NSEvent *)event {
[self applyMouseMotion:event];
[super otherMouseDragged:event];
}
- (void)mouseMoved:(NSEvent *)event {
[self applyMouseMotion:event];
[super mouseMoved:event];
}
#pragma mark - Mouse buttons
- (void)applyButtonDown:(NSEvent *)event {
if(self.shouldCaptureMouse) {
_mouseIsCaptured = YES;
[NSCursor hide];
CGAssociateMouseAndMouseCursorPosition(false);
[self.responderDelegate mouseDown:event];
}
}
- (void)applyButtonUp:(NSEvent *)event {
if(self.shouldCaptureMouse) {
[self.responderDelegate mouseUp:event];
}
}
- (void)mouseDown:(NSEvent *)event {
[self applyButtonDown:event];
[super mouseDown:event];
}
- (void)rightMouseDown:(NSEvent *)event {
[self applyButtonDown:event];
[super rightMouseDown:event];
}
- (void)otherMouseDown:(NSEvent *)event {
[self applyButtonDown:event];
[super otherMouseDown:event];
}
- (void)mouseUp:(NSEvent *)event {
[self applyButtonUp:event];
[super mouseUp:event];
}
- (void)rightMouseUp:(NSEvent *)event {
[self applyButtonUp:event];
[super rightMouseUp:event];
}
- (void)otherMouseUp:(NSEvent *)event {
[self applyButtonUp:event];
[super otherMouseUp:event];
}
@end