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:
Robert Osfield
2012-02-03 15:15:37 +00:00
parent 85bce8b8ad
commit 0e3de701d9

View File

@@ -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
// ----------------------------------------------------------------------------------------------------------