From Stephan Huber, "attached you'll find a first version of multi-touch-support for OS X (>=
10.6), which will forward all multi-touch events from a trackpad to the corresponding osgGA-event-structures. The support is switched off per default, but you can enable multi-touch support via a new flag for GraphicsWindowCocoa::WindowData or directly via the GraphicsWindowCocoa-class. After switching multi-touch-support on, all mouse-events from the trackpad get ignored, otherwise you'll have multiple events for the same pointer which is very confusing (as the trackpad reports absolute movement, and as a mouse relative movement). I think this is not a problem, as multi-touch-input is a completely different beast as a mouse, so you'll have to code your own event-handlers anyway. While coding this stuff, I asked myself if we should refactor GUIEventAdapter/EventQueue and assign a specific event-type for touch-input instead of using PUSH/DRAG/RELEASE. This will make it clearer how to use the code, but will break the mouse-emulation for the first touch-point and with that all existing manipulators. What do you think? I am happy to code the proposed changes. Additionally I created a small (and ugly) example osgmultitouch which makes use of the osgGA::MultiTouchTrackballManipulator, shows all touch-points on a HUD and demonstrates how to get the touchpoints from an osgGA::GUIEventAdapter. There's even a small example video here: http://vimeo.com/31611842"
This commit is contained in:
@@ -269,6 +269,9 @@ static NSRect convertToQuartzCoordinates(const NSRect& rect)
|
||||
unsigned int _cachedModifierFlags;
|
||||
BOOL _handleTabletEvents;
|
||||
|
||||
NSMutableDictionary* _touchPoints;
|
||||
unsigned int _lastTouchPointId;
|
||||
|
||||
}
|
||||
- (void)setGraphicsWindowCocoa: (osgViewer::GraphicsWindowCocoa*) win;
|
||||
|
||||
@@ -307,10 +310,23 @@ static NSRect convertToQuartzCoordinates(const NSRect& rect)
|
||||
- (void)tabletProximity:(NSEvent *)theEvent;
|
||||
- (void)handleTabletEvents:(NSEvent*)theEvent;
|
||||
|
||||
#if defined(MAC_OS_X_VERSION_10_6) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6)
|
||||
- (osgGA::GUIEventAdapter::TouchPhase) convertTouchPhase: (NSTouchPhase) phase;
|
||||
- (unsigned int)computeTouchId: (NSTouch*) touch;
|
||||
- (void)touchesBeganWithEvent:(NSEvent *)event;
|
||||
- (void)touchesMovedWithEvent:(NSEvent *)event;
|
||||
- (void)touchesEndedWithEvent:(NSEvent *)event;
|
||||
- (void)touchesCancelledWithEvent:(NSEvent *)event;
|
||||
#endif
|
||||
|
||||
- (BOOL)useMultiTouchOnly: (NSEvent*) event;
|
||||
|
||||
- (BOOL)acceptsFirstResponder;
|
||||
- (BOOL)becomeFirstResponder;
|
||||
- (BOOL)resignFirstResponder;
|
||||
|
||||
- (void)dealloc;
|
||||
|
||||
@end
|
||||
|
||||
@implementation GraphicsWindowCocoaGLView
|
||||
@@ -319,6 +335,13 @@ static NSRect convertToQuartzCoordinates(const NSRect& rect)
|
||||
-(void) setGraphicsWindowCocoa: (osgViewer::GraphicsWindowCocoa*) win
|
||||
{
|
||||
_win = win;
|
||||
_touchPoints = NULL;
|
||||
}
|
||||
|
||||
-(void) dealloc
|
||||
{
|
||||
if (_touchPoints) [_touchPoints release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (BOOL)acceptsFirstResponder
|
||||
@@ -336,6 +359,15 @@ static NSRect convertToQuartzCoordinates(const NSRect& rect)
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL) useMultiTouchOnly: (NSEvent*) event
|
||||
{
|
||||
#if defined(MAC_OS_X_VERSION_10_6) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6)
|
||||
return ([self acceptsTouchEvents] && ([event subtype] == NSTouchEventSubtype));
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
- (NSPoint) getLocalPoint: (NSEvent*)theEvent
|
||||
{
|
||||
@@ -405,6 +437,10 @@ static NSRect convertToQuartzCoordinates(const NSRect& rect)
|
||||
|
||||
- (void) mouseMoved:(NSEvent*)theEvent
|
||||
{
|
||||
// if multitouch is enabled, disable standard event handling
|
||||
if ([self useMultiTouchOnly: theEvent])
|
||||
return;
|
||||
|
||||
NSPoint converted_point = [self getLocalPoint: theEvent];
|
||||
DEBUG_OUT("Mouse moved" << converted_point.x << "/" << converted_point.y);
|
||||
_win->getEventQueue()->mouseMotion(converted_point.x, converted_point.y);
|
||||
@@ -414,6 +450,10 @@ static NSRect convertToQuartzCoordinates(const NSRect& rect)
|
||||
|
||||
- (void) mouseDown:(NSEvent*)theEvent
|
||||
{
|
||||
// if multitouch is enabled, disable standard event handling
|
||||
if ([self useMultiTouchOnly: theEvent])
|
||||
return;
|
||||
|
||||
DEBUG_OUT("Mouse down");
|
||||
// Because many Mac users have only a 1-button mouse, we should provide ways
|
||||
// to access the button 2 and 3 actions of osgViewer.
|
||||
@@ -443,6 +483,10 @@ static NSRect convertToQuartzCoordinates(const NSRect& rect)
|
||||
|
||||
- (void) mouseDragged:(NSEvent*)theEvent
|
||||
{
|
||||
// if multitouch is enabled, disable standard event handling
|
||||
if ([self useMultiTouchOnly: theEvent])
|
||||
return;
|
||||
|
||||
if (!_win) return;
|
||||
|
||||
NSPoint converted_point = [self getLocalPoint: theEvent];
|
||||
@@ -455,6 +499,10 @@ static NSRect convertToQuartzCoordinates(const NSRect& rect)
|
||||
|
||||
- (void) mouseUp:(NSEvent*)theEvent
|
||||
{
|
||||
// if multitouch is enabled, disable standard event handling
|
||||
if ([self useMultiTouchOnly: theEvent])
|
||||
return;
|
||||
|
||||
// Because many Mac users have only a 1-button mouse, we should provide ways
|
||||
// to access the button 2 and 3 actions of osgViewer.
|
||||
// I will use the Ctrl modifer to represent right-clicking
|
||||
@@ -737,6 +785,159 @@ static NSRect convertToQuartzCoordinates(const NSRect& rect)
|
||||
_win->getEventQueue()->penProximity(pt, [theEvent isEnteringProximity]);
|
||||
}
|
||||
|
||||
#if defined(MAC_OS_X_VERSION_10_6) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6)
|
||||
|
||||
- (osgGA::GUIEventAdapter::TouchPhase) convertTouchPhase: (NSTouchPhase) phase
|
||||
{
|
||||
switch(phase) {
|
||||
|
||||
case NSTouchPhaseBegan:
|
||||
return osgGA::GUIEventAdapter::TOUCH_BEGAN;
|
||||
break;
|
||||
case NSTouchPhaseMoved:
|
||||
return osgGA::GUIEventAdapter::TOUCH_MOVED;
|
||||
break;
|
||||
|
||||
case NSTouchPhaseStationary:
|
||||
return osgGA::GUIEventAdapter::TOUCH_STATIONERY;
|
||||
break;
|
||||
|
||||
case NSTouchPhaseEnded:
|
||||
case NSTouchPhaseCancelled:
|
||||
return osgGA::GUIEventAdapter::TOUCH_ENDED;
|
||||
break;
|
||||
}
|
||||
|
||||
return osgGA::GUIEventAdapter::TOUCH_ENDED;
|
||||
|
||||
}
|
||||
|
||||
|
||||
- (unsigned int)computeTouchId: (NSTouch*) touch
|
||||
{
|
||||
unsigned int result(0);
|
||||
|
||||
if(!_touchPoints) {
|
||||
_touchPoints = [[NSMutableDictionary alloc] init];
|
||||
_lastTouchPointId = 0;
|
||||
}
|
||||
|
||||
switch([touch phase])
|
||||
{
|
||||
|
||||
case NSTouchPhaseBegan:
|
||||
if ([_touchPoints objectForKey: [touch identity]] == nil)
|
||||
{
|
||||
[_touchPoints setObject: [NSNumber numberWithInt: _lastTouchPointId] forKey: [touch identity]];
|
||||
result = _lastTouchPointId++;
|
||||
break;
|
||||
}
|
||||
// missing "break" by intention!
|
||||
|
||||
case NSTouchPhaseMoved:
|
||||
case NSTouchPhaseStationary:
|
||||
{
|
||||
NSNumber* n = [_touchPoints objectForKey: [touch identity]];
|
||||
result = [n intValue];
|
||||
}
|
||||
break;
|
||||
|
||||
case NSTouchPhaseEnded:
|
||||
case NSTouchPhaseCancelled:
|
||||
{
|
||||
NSNumber* n = [_touchPoints objectForKey: [touch identity]];
|
||||
result = [n intValue];
|
||||
[_touchPoints removeObjectForKey: [touch identity]];
|
||||
if([_touchPoints count] == 0) {
|
||||
_lastTouchPointId = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
- (void)touchesBeganWithEvent:(NSEvent *)event
|
||||
{
|
||||
NSSet *allTouches = [event touchesMatchingPhase: NSTouchPhaseAny inView: self];
|
||||
|
||||
osg::ref_ptr<osgGA::GUIEventAdapter> osg_event(NULL);
|
||||
|
||||
NSRect bounds = [self bounds];
|
||||
for(unsigned int i=0; i<[allTouches count]; i++)
|
||||
{
|
||||
|
||||
NSTouch *touch = [[allTouches allObjects] objectAtIndex:i];
|
||||
NSPoint pos = [touch normalizedPosition];
|
||||
osg::Vec2 pixelPos(pos.x * bounds.size.width, (1-pos.y) * bounds.size.height);
|
||||
unsigned int touch_id = [self computeTouchId: touch];
|
||||
if (!osg_event) {
|
||||
osg_event = _win->getEventQueue()->touchBegan(touch_id, [self convertTouchPhase: [touch phase]], pixelPos.x(), pixelPos.y());
|
||||
} else {
|
||||
osg_event->addTouchPoint(touch_id, [self convertTouchPhase: [touch phase]], pixelPos.x(), pixelPos.y());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)touchesMovedWithEvent:(NSEvent *)event
|
||||
{
|
||||
NSSet *allTouches = [event touchesMatchingPhase: NSTouchPhaseAny inView: self];
|
||||
|
||||
osg::ref_ptr<osgGA::GUIEventAdapter> osg_event(NULL);
|
||||
NSRect bounds = [self bounds];
|
||||
|
||||
for(unsigned int i=0; i<[allTouches count]; i++)
|
||||
{
|
||||
NSTouch *touch = [[allTouches allObjects] objectAtIndex:i];
|
||||
NSPoint pos = [touch normalizedPosition];
|
||||
osg::Vec2 pixelPos(pos.x * bounds.size.width, (1 - pos.y) * bounds.size.height);
|
||||
unsigned int touch_id = [self computeTouchId: touch];
|
||||
if (!osg_event) {
|
||||
osg_event = _win->getEventQueue()->touchMoved(touch_id, [self convertTouchPhase: [touch phase]], pixelPos.x(), pixelPos.y());
|
||||
} else {
|
||||
osg_event->addTouchPoint(touch_id, [self convertTouchPhase: [touch phase]], pixelPos.x(), pixelPos.y());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- (void)touchesEndedWithEvent:(NSEvent *)event
|
||||
{
|
||||
NSSet *allTouches = [event touchesMatchingPhase: NSTouchPhaseAny inView: self];
|
||||
|
||||
osg::ref_ptr<osgGA::GUIEventAdapter> osg_event(NULL);
|
||||
NSRect bounds = [self bounds];
|
||||
|
||||
for(unsigned int i=0; i<[allTouches count]; i++)
|
||||
{
|
||||
NSTouch *touch = [[allTouches allObjects] objectAtIndex:i];
|
||||
NSPoint pos = [touch normalizedPosition];
|
||||
osg::Vec2 pixelPos(pos.x * bounds.size.width, (1 - pos.y) * bounds.size.height);
|
||||
unsigned int touch_id = [self computeTouchId: touch];
|
||||
if (!osg_event) {
|
||||
osg_event = _win->getEventQueue()->touchEnded(touch_id, [self convertTouchPhase: [touch phase]], pixelPos.x(), pixelPos.y(), 1);
|
||||
} else {
|
||||
osg_event->addTouchPoint(touch_id, [self convertTouchPhase: [touch phase]], pixelPos.x(), pixelPos.y(), 1);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
- (void)touchesCancelledWithEvent:(NSEvent *)event
|
||||
{
|
||||
[self touchesEndedWithEvent: event];
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@end
|
||||
|
||||
@@ -1002,23 +1203,30 @@ bool GraphicsWindowCocoa::realizeImplementation()
|
||||
// set graphics handle for shared usage
|
||||
setNSOpenGLContext(_context);
|
||||
|
||||
GraphicsWindowCocoaGLView* theView = [[ GraphicsWindowCocoaGLView alloc ] initWithFrame:[ _window frame ] ];
|
||||
[theView setAutoresizingMask: (NSViewWidthSizable | NSViewHeightSizable) ];
|
||||
[theView setGraphicsWindowCocoa: this];
|
||||
[theView setOpenGLContext:_context];
|
||||
_view = theView;
|
||||
OSG_DEBUG << "GraphicsWindowCocoa::realizeImplementation / view: " << theView << std::endl;
|
||||
|
||||
_view = [[ GraphicsWindowCocoaGLView alloc ] initWithFrame:[ _window frame ] ];
|
||||
[_view setAutoresizingMask: (NSViewWidthSizable | NSViewHeightSizable) ];
|
||||
[_view setGraphicsWindowCocoa: this];
|
||||
[_view setOpenGLContext:_context];
|
||||
|
||||
// enable multitouch
|
||||
if (_multiTouchEnabled || (windowData && windowData->isMultiTouchEnabled()))
|
||||
{
|
||||
setMultiTouchEnabled(true);
|
||||
}
|
||||
|
||||
OSG_DEBUG << "GraphicsWindowCocoa::realizeImplementation / view: " << _view << std::endl;
|
||||
|
||||
if (_ownsWindow) {
|
||||
[_window setContentView: theView];
|
||||
[_window setContentView: _view];
|
||||
setupNSWindow(_window);
|
||||
[theView release];
|
||||
|
||||
[_view release];
|
||||
|
||||
MenubarController::instance()->attachWindow( new CocoaWindowAdapter(this) );
|
||||
}
|
||||
else
|
||||
{
|
||||
windowData->setCreatedNSView(theView);
|
||||
windowData->setCreatedNSView(_view);
|
||||
}
|
||||
|
||||
[pool release];
|
||||
@@ -1423,6 +1631,24 @@ void GraphicsWindowCocoa::setSyncToVBlank(bool f)
|
||||
}
|
||||
|
||||
|
||||
bool GraphicsWindowCocoa::isMultiTouchEnabled()
|
||||
{
|
||||
return _multiTouchEnabled;
|
||||
}
|
||||
void GraphicsWindowCocoa::setMultiTouchEnabled(bool b)
|
||||
{
|
||||
_multiTouchEnabled = b;
|
||||
|
||||
#if defined(MAC_OS_X_VERSION_10_6) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6)
|
||||
if (_view) [_view setAcceptsTouchEvents: b];
|
||||
#else
|
||||
if (b) {
|
||||
OSG_WARN << "GraphicsWindowCocoa :: multi-touch only available for OS X >= 10.6, please check your compile settings" << std::endl;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------------------------------------
|
||||
// d'tor
|
||||
// ----------------------------------------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user