I've done a bunch of searching, and the last thing I could find was from 2011, where it seemed that it wasn't possible to have nonexclusive mouse input on the mac. I've had a couple customer complaints about it (my game doesn't require mouse input - it's played by riding a bicycle).
Has this changed? If not, can anyone suggest a way I can fake it? I'm thinking if the mouse gets within 10 pixels of the edge of my window, I'll call some magical function to make my window not the topmost (or at least, not active). Anyone know if there's an ogre or OSX function that'll do that for me?
Nonexclusive mouse input with OIS on OSX
-
- Halfling
- Posts: 44
- Joined: Fri Mar 29, 2013 5:40 pm
- x 4
- areay
- Bugbear
- Posts: 819
- Joined: Wed May 05, 2010 4:59 am
- Location: Auckland, NZ
- x 69
Re: Nonexclusive mouse input with OIS on OSX
This is all I could find http://ogre3d.org/forums/viewtopic.php?f=2&t=64155 TLDR; looks like OIS might not support this for Mac.
I have verified that the current release on SF still doesn't support this but I did find another user's homebrew patch to add it in http://www.wreckedgames.com/forum/index ... pic=1176.0
I have verified that the current release on SF still doesn't support this but I did find another user's homebrew patch to add it in http://www.wreckedgames.com/forum/index ... pic=1176.0
-
- Halfling
- Posts: 44
- Joined: Fri Mar 29, 2013 5:40 pm
- x 4
Re: Nonexclusive mouse input with OIS on OSX
Rats. Identical to my own findings.
-
- Halfling
- Posts: 44
- Joined: Fri Mar 29, 2013 5:40 pm
- x 4
Re: Nonexclusive mouse input with OIS on OSX
If you're compiling OIS from source (it's actually pretty easy), it isn't hard to make the necessary changes to OIS::MacMouse.
I didn't want _entirely_ nonexclusive mouse input, just a way for the user to quickly get their cursor back. So I changed MacMouse to "Ungrab" the mouse if the user hit the edge of my window going fairly quickly.
I didn't look into full nonexclusion since it wasn't my goal, but I can't imagine it'd be hard to at least simulate. Just get rid of the CGAssociateMouseAndMouseCursorPosition(FALSE), CGDisplayHideCursor, and a couple others. It's only a couple hundred lines of code.
Here's my MacMouse.cpp. The key changes are in capture() [where I check for a fast-moving exit] and the new function "Ungrab()", where I give the cursor back to the OS.
I didn't want _entirely_ nonexclusive mouse input, just a way for the user to quickly get their cursor back. So I changed MacMouse to "Ungrab" the mouse if the user hit the edge of my window going fairly quickly.
I didn't look into full nonexclusion since it wasn't my goal, but I can't imagine it'd be hard to at least simulate. Just get rid of the CGAssociateMouseAndMouseCursorPosition(FALSE), CGDisplayHideCursor, and a couple others. It's only a couple hundred lines of code.
Here's my MacMouse.cpp. The key changes are in capture() [where I check for a fast-moving exit] and the new function "Ungrab()", where I give the cursor back to the OS.
Code: Select all
#include "mac/MacMouse.h"
#include "mac/MacInputManager.h"
#include "mac/MacHelpers.h"
#include "OISException.h"
#include "OISEvents.h"
#include <Carbon/Carbon.h>
#include <list>
#include <iostream>
using namespace OIS;
//Events we subscribe to and remove from queue
const EventTypeSpec mouseEvents[] = {
{ kEventClassMouse, kEventMouseDown },
{ kEventClassMouse, kEventMouseUp },
{ kEventClassMouse, kEventMouseMoved },
{ kEventClassMouse, kEventMouseDragged },
{ kEventClassMouse, kEventMouseWheelMoved }
};
const EventTypeSpec WinFocusAcquired [] = {{kEventClassApplication, kEventAppDeactivated}};
//-------------------------------------------------------------------//
MacMouse::MacMouse( InputManager* creator, bool buffered )
: Mouse(creator->inputSystemName(), buffered, 0, creator), mNeedsToRegainFocus( false )
{
mouseEventRef = NULL;
mWindowFocusHandler = NULL;
// Get a "Univeral procedure pointer" for our callback
mouseUPP = NewEventHandlerUPP(MouseWrapper);
mWindowFocusListener = NewEventHandlerUPP(WindowFocusChanged);
static_cast<MacInputManager*>(mCreator)->_setMouseUsed(true);
}
MacMouse::~MacMouse()
{
if(mouseEventRef != NULL)
RemoveEventHandler(mouseEventRef);
if(mWindowFocusHandler != NULL)
RemoveEventHandler(mWindowFocusHandler);
DisposeEventHandlerUPP(mouseUPP);
DisposeEventHandlerUPP(mWindowFocusListener);
// Restore Mouse
CGAssociateMouseAndMouseCursorPosition(TRUE);
CGDisplayShowCursor(kCGDirectMainDisplay);
static_cast<MacInputManager*>(mCreator)->_setMouseUsed(false);
}
void MacMouse::_initialize()
{
mState.clear();
mTempState.clear();
mMouseWarped = false;
// Hide OS Mouse
CGDisplayHideCursor(kCGDirectMainDisplay);
MacInputManager* im = static_cast<MacInputManager*>(mCreator);
WindowRef win = im->_getWindow();
if(win)
{
Rect clipRect = {0.0f, 0.0f, 0.0f, 0.0f};
GetWindowBounds(win, kWindowContentRgn, &clipRect);
CGPoint warpPoint;
warpPoint.x = ((clipRect.right - clipRect.left) / 2) + clipRect.left;
warpPoint.y = ((clipRect.bottom - clipRect.top) / 2) + clipRect.top;
CGDisplayMoveCursorToPoint(kCGDirectMainDisplay, warpPoint); //Place at display origin
mMouseWarped = true;
}
//Now that mouse is warped, start listening for events
EventTargetRef event = ((MacInputManager*)mCreator)->_getEventTarget();
if(mouseEventRef != NULL)
RemoveEventHandler(mouseEventRef);
if(mWindowFocusHandler != NULL)
RemoveEventHandler(mWindowFocusHandler);
mouseEventRef = mWindowFocusHandler = NULL;
if(InstallEventHandler(event, mouseUPP, GetEventTypeCount(mouseEvents), mouseEvents, this, &mouseEventRef) != noErr)
OIS_EXCEPT( E_General, "MacMouse::_initialize >> Error loading Mouse event handler" );
if(InstallEventHandler(event, mWindowFocusListener, GetEventTypeCount(WinFocusAcquired), WinFocusAcquired, this, &mWindowFocusHandler) != noErr)
OIS_EXCEPT( E_General, "MacMouse::_initialize >> Error loading Mouse event handler" );
//Lock OS Mouse movement
mNeedsToRegainFocus = false;
CGAssociateMouseAndMouseCursorPosition(FALSE);
}
OSStatus MacMouse::WindowFocusChanged(EventHandlerCallRef nextHandler, EventRef event, void* macMouse)
{
//std::cout << "Window Focus Changed\n";
MacMouse* _this = static_cast<MacMouse*>(macMouse);
if (_this)
{
_this->Ungrab();
// propagate the event down the chain
return CallNextEventHandler(nextHandler, event);
}
else
OIS_EXCEPT(E_General, "MouseWrapper >> Being called by something other than our event handler!");
}
void MacMouse::setBuffered( bool buffered )
{
mBuffered = buffered;
}
void MacMouse::Ungrab()
{
mNeedsToRegainFocus = true;
CGAssociateMouseAndMouseCursorPosition(TRUE);
CGDisplayShowCursor(kCGDirectMainDisplay);
{ // setting OS cursor to where the game cursor is so the user has at least a mildly-happy outcome
MacInputManager* im = static_cast<MacInputManager*>(mCreator);
WindowRef win = im->_getWindow();
Rect clipRect = {0.0f, 0.0f, 0.0f, 0.0f};
GetWindowBounds(win, kWindowContentRgn, &clipRect);
CGPoint warpPoint;
warpPoint.x = clipRect.left + mState.X.abs;
warpPoint.y = clipRect.top + mState.Y.abs;
CGDisplayMoveCursorToPoint(kCGDirectMainDisplay, warpPoint); //Place at display origin
}
}
void MacMouse::capture()
{
mState.X.rel = 0;
mState.Y.rel = 0;
mState.Z.rel = 0;
if(mTempState.X.rel || mTempState.Y.rel || mTempState.Z.rel)
{
//printf("%i %i %i\n\n", mTempState.X.rel, mTempState.Y.rel, mTempState.Z.rel);
//Set new relative motion values
mState.X.rel = mTempState.X.rel;
mState.Y.rel = mTempState.Y.rel;
mState.Z.rel = mTempState.Z.rel;
//Update absolute position
mState.X.abs += mTempState.X.rel;
mState.Y.abs += mTempState.Y.rel;
int diffLevel = 0;
if(mState.X.abs > mState.width)
{
diffLevel = mState.X.abs - mState.width;
mState.X.abs = mState.width;
}
else if(mState.X.abs < 0)
{
diffLevel = -mState.X.abs;
mState.X.abs = 0;
}
if(mState.Y.abs > mState.height)
{
diffLevel = mState.Y.abs - mState.height;
mState.Y.abs = mState.height;
}
else if(mState.Y.abs < 0)
{
diffLevel = -mState.Y.abs;
mState.Y.abs = 0;
}
if(diffLevel > 20) // if they hit the edge hard enough, then ungrab the mouse - the user wants to get out of this window...
{
Ungrab();
}
mState.Z.abs += mTempState.Z.rel;
//Fire off event
if(mListener && mBuffered)
mListener->mouseMoved(MouseEvent(this, mState));
}
mTempState.clear();
}
void MacMouse::_mouseCallback( EventRef theEvent )
{
OSStatus result = eventNotHandledErr;
UInt32 kind = GetEventKind (theEvent);
switch(kind)
{
case kEventMouseDragged:
case kEventMouseMoved:
{
//HIPoint location = {0.0f, 0.0f};
HIPoint delta = {0.0f, 0.0f};
//Rect clipRect = {0.0f, 0.0f, 0.0f, 0.0f};
if(mNeedsToRegainFocus)
break;
// Capture the parameters
// TODO: Look into HIViewNewTrackingArea
//GetEventParameter(theEvent, kEventParamMouseLocation, typeHIPoint, NULL, sizeof(HIPoint), NULL, &location);
GetEventParameter(theEvent, kEventParamMouseDelta, typeHIPoint, NULL, sizeof(HIPoint), NULL, &delta);
// relative positioning
if(!mMouseWarped)
{
mTempState.X.rel += delta.x;
mTempState.Y.rel += delta.y;
}
mMouseWarped = false;
break;
}
case kEventMouseDown:
{
EventMouseButton button = 0;
int mouseButton = 3;
UInt32 modifiers = 0;
if(mNeedsToRegainFocus)
break;
// Capture parameters
GetEventParameter(theEvent, kEventParamMouseButton, typeMouseButton, NULL, sizeof(EventMouseButton), NULL, &button);
GetEventParameter(theEvent, kEventParamKeyModifiers, typeUInt32, NULL, sizeof(UInt32), NULL, &modifiers);
if((button == kEventMouseButtonTertiary) || ((button == kEventMouseButtonPrimary) && (modifiers & optionKey)))
{
mouseButton = 2;
mState.buttons |= 1 << mouseButton;
}
else if((button == kEventMouseButtonSecondary) || ((button == kEventMouseButtonPrimary) && (modifiers & controlKey)))
{
mouseButton = 1;
mState.buttons |= 1 << mouseButton;
}
else if(button == kEventMouseButtonPrimary)
{
mouseButton = 0;
mState.buttons |= 1 << mouseButton;
}
if( mListener && mBuffered )
mListener->mousePressed( MouseEvent( this, mState ), (MouseButtonID)mouseButton );
break;
}
case kEventMouseUp:
{
EventMouseButton button = 0;
int mouseButton = 3;
UInt32 modifiers = 0;
if(mNeedsToRegainFocus)
{
mNeedsToRegainFocus = false;
CGAssociateMouseAndMouseCursorPosition(false);
MacInputManager* im = static_cast<MacInputManager*>(mCreator);
WindowRef win = im->_getWindow();
if(win)
{
Rect clipRect = {0.0f, 0.0f, 0.0f, 0.0f};
GetWindowBounds(win, kWindowContentRgn, &clipRect);
CGPoint warpPoint;
warpPoint.x = ((clipRect.right - clipRect.left) / 2) + clipRect.left;
warpPoint.y = ((clipRect.bottom - clipRect.top) / 2) + clipRect.top;
CGDisplayMoveCursorToPoint(kCGDirectMainDisplay, warpPoint); //Place at display origin
CGDisplayHideCursor(kCGDirectMainDisplay);
mMouseWarped = true;
}
//Once we regain focus, we do not really know what state all the buttons are in - for now, set to not pressed. todo, check current status
//compare against old status, and send off any needed events
mState.buttons = 0;
break;
}
// Capture parameters
GetEventParameter(theEvent, kEventParamMouseButton, typeMouseButton, NULL, sizeof(EventMouseButton), NULL, &button);
GetEventParameter(theEvent, kEventParamKeyModifiers, typeUInt32, NULL, sizeof(UInt32), NULL, &modifiers);
if ((button == kEventMouseButtonTertiary) || ((button == kEventMouseButtonPrimary) && (modifiers & optionKey)))
{
mouseButton = 2;
mState.buttons &= ~(1 << mouseButton);
}
else if ((button == kEventMouseButtonSecondary) || ((button == kEventMouseButtonPrimary) && (modifiers & controlKey)))
{
mouseButton = 1;
mState.buttons &= ~(1 << mouseButton);
}
else if (button == kEventMouseButtonPrimary)
{
mouseButton = 0;
mState.buttons &= ~(1 << mouseButton);
}
if( mListener && mBuffered )
mListener->mouseReleased( MouseEvent( this, mState ), (MouseButtonID)mouseButton );
break;
}
case kEventMouseWheelMoved:
{
SInt32 wheelDelta = 0;
EventMouseWheelAxis wheelAxis = 0;
// Capture parameters
GetEventParameter(theEvent, kEventParamMouseWheelAxis, typeMouseWheelAxis, NULL, sizeof(EventMouseWheelAxis), NULL, &wheelAxis);
GetEventParameter(theEvent, kEventParamMouseWheelDelta, typeSInt32, NULL, sizeof(SInt32), NULL, &wheelDelta);
// If the Y axis of the wheel changed, then update the Z
// Does OIS care about the X wheel axis?
if(wheelAxis == kEventMouseWheelAxisY)
mTempState.Z.rel += (wheelDelta * 60);
break;
}
default:
break;
}
}