"...drude/platforms/reference/tests/ReferenceDrudeTests.h" did not exist on "a402046652cab8ba297aa423e4cb57c904525144"
Commit 4b11dd5a authored by Phillip Castaneda's avatar Phillip Castaneda
Browse files

Adding OIS sources to SVN

parents
/*
The zlib/libpng License
Copyright (c) 2005-2007 Phillip Castaneda (pjcast -- www.wreckedgames.com)
This software is provided 'as-is', without any express or implied warranty. In no event will
the authors be held liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose, including commercial
applications, and to alter it and redistribute it freely, subject to the following
restrictions:
1. The origin of this software must not be misrepresented; you must not claim that
you wrote the original software. If you use this software in a product,
an acknowledgment in the product documentation would be appreciated but is
not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "SDL/SDLMouse.h"
#include "SDL/SDLInputManager.h"
#include "OISException.h"
#include "OISEvents.h"
using namespace OIS;
//-------------------------------------------------------------------//
SDLMouse::SDLMouse( bool buffered ) : mGrabbed(false), mRegainFocus(false)
{
mBuffered = buffered;
mType = OISMouse;
listener = 0;
}
//-------------------------------------------------------------------//
void SDLMouse::_initialize()
{
//Clear old state
mState.clear();
mRegainFocus = false;
_setGrab(true);
_setVisible(false);
static_cast<SDLInputManager*>(InputManager::getSingletonPtr())->_setGrabMode(true);
}
//-------------------------------------------------------------------//
SDLMouse::~SDLMouse()
{
_setGrab(true);
_setVisible(true);
static_cast<SDLInputManager*>(InputManager::getSingletonPtr())->_setGrabMode(false);
}
//-------------------------------------------------------------------//
void SDLMouse::capture()
{
//Used for going from SDL Button to OIS button
static const MouseButtonID ButtonMask[4] = {MB_Left, MB_Left, MB_Middle, MB_Right};
//Clear old relative values
mState.relX = mState.relY = mState.relZ = 0;
SDL_Event events[OIS_SDL_MOUSE_BUFF];
int count = SDL_PeepEvents(events, OIS_SDL_MOUSE_BUFF, SDL_GETEVENT, SDL_MOUSEEVENTMASK);
bool mouseXYMoved = false;
bool mouseZMoved = false;
for( int i = 0; i < count; ++i )
{
switch( events[i].type )
{
case SDL_MOUSEMOTION: mouseXYMoved = true; break;
case SDL_MOUSEBUTTONDOWN:
{
mRegainFocus = true;
int sdlButton = events[i].button.button;
if( sdlButton <= SDL_BUTTON_RIGHT )
{ //Left, Right, or Middle
mState.buttons |= (1 << ButtonMask[sdlButton]);
if( mBuffered && listener )
if( listener->mousePressed(MouseEvent(this,0,mState), ButtonMask[sdlButton]) == false )
return;
}
else
{ //mouse Wheel
mouseZMoved = true;
if( sdlButton == SDL_BUTTON_WHEELUP )
mState.relZ += 120;
else if( sdlButton == SDL_BUTTON_WHEELDOWN )
mState.relZ -= 120;
}
break;
}
case SDL_MOUSEBUTTONUP:
{
int sdlButton = events[i].button.button;
if( sdlButton <= SDL_BUTTON_RIGHT )
{ //Left, Right, or Middle
mState.buttons &= ~(1 << ButtonMask[sdlButton]);
if( mBuffered && listener )
if( listener->mouseReleased(MouseEvent(this,0,mState), ButtonMask[sdlButton]) == false )
return;
}
break;
}
}
}
//Handle X/Y axis move
if( mouseXYMoved )
{
SDL_GetMouseState( &mState.abX, &mState.abY );
SDL_GetRelativeMouseState( &mState.relX, &mState.relY );
if( mBuffered && listener )
listener->mouseMoved(MouseEvent(this, 0, mState));
}
//Handle Z Motion
if( mouseZMoved )
{
mState.abZ += mState.relZ;
if( mBuffered && listener )
listener->mouseMoved(MouseEvent(this, 0, mState));
}
//Handle Alt-Tabbing
SDLInputManager* man = static_cast<SDLInputManager*>(InputManager::getSingletonPtr());
if( man->_getGrabMode() == false )
{
if( mRegainFocus == false && mGrabbed == true )
{ //We had focus, but must release it now
_setGrab(false);
_setVisible(true);
}
else if( mRegainFocus == true && mGrabbed == false )
{ //We are gaining focus back (mouse clicked in window)
_setGrab(true);
_setVisible(false);
man->_setGrabMode(true); //Notify manager
}
}
}
//-------------------------------------------------------------------//
void SDLMouse::setBuffered(bool buffered)
{
mBuffered = buffered;
}
//-------------------------------------------------------------------//
void SDLMouse::_setGrab(bool grabbed)
{
if( grabbed )
SDL_WM_GrabInput(SDL_GRAB_ON);
else
SDL_WM_GrabInput(SDL_GRAB_OFF);
mGrabbed = grabbed;
}
//-------------------------------------------------------------------//
void SDLMouse::_setVisible(bool visible)
{
if( visible )
SDL_ShowCursor(SDL_ENABLE);
else
SDL_ShowCursor(SDL_DISABLE);
}
#include "OISConfig.h"
#ifdef OIS_LIRC_SUPPORT
/*
The zlib/libpng License
Copyright (c) 2005-2007 Phillip Castaneda (pjcast -- www.wreckedgames.com)
This software is provided 'as-is', without any express or implied warranty. In no event will
the authors be held liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose, including commercial
applications, and to alter it and redistribute it freely, subject to the following
restrictions:
1. The origin of this software must not be misrepresented; you must not claim that
you wrote the original software. If you use this software in a product,
an acknowledgment in the product documentation would be appreciated but is
not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "OISLIRC.h"
#include "OISLIRCFactoryCreator.h"
#include "OISException.h"
using namespace OIS;
//-----------------------------------------------------------------------------------//
LIRCControl::LIRCControl(InputManager* creator, int id, bool buffered, LIRCFactoryCreator* local_creator, RemoteInfo &info) :
JoyStick("Generic LIRC", buffered, id, creator),
mLIRCCreator(local_creator),
mRingBuffer(OIS_LIRC_EVENT_BUFFER),
mInfo(info)
{
//Fill in joystick information
mState.mButtons.resize(mInfo.buttons);
}
//-----------------------------------------------------------------------------------//
LIRCControl::~LIRCControl()
{
}
//-----------------------------------------------------------------------------------//
void LIRCControl::_initialize()
{
mState.clear();
}
//-----------------------------------------------------------------------------------//
void LIRCControl::setBuffered(bool buffered)
{
mBuffered = buffered;
}
//-----------------------------------------------------------------------------------//
void LIRCControl::capture()
{
//Anything to read?
int entries = mRingBuffer.GetReadAvailable();
if( entries <= 0 )
return;
LIRCEvent events[OIS_LIRC_EVENT_BUFFER];
if( entries > OIS_LIRC_EVENT_BUFFER )
entries = OIS_LIRC_EVENT_BUFFER;
mRingBuffer.Read(events, entries);
//Loop through each event
for( int i = 0; i < entries; ++i )
{
if( mBuffered && mListener )
{
//Quickly send off button events (there is no real stored state)
//As, even a held down button will kep generating button presses
mState.mButtons[events[i].button] = true;
if( !mListener->buttonPressed(JoyStickEvent(this, mState), events[i].button) )
return;
mState.mButtons[events[i].button] = false;
if( !mListener->buttonReleased(JoyStickEvent(this, mState), events[i].button) )
return;
}
}
}
//-----------------------------------------------------------------------------------//
void LIRCControl::queueButtonPressed(const std::string &id)
{
if( mRingBuffer.GetWriteAvailable() > 0 )
{
LIRCEvent evt;
evt.button = mInfo.buttonMap[id];
mRingBuffer.Write(&evt, 1);
}
}
//-----------------------------------------------------------------------------------//
Interface* LIRCControl::queryInterface(Interface::IType type)
{
return 0;
}
#endif
#include "OISConfig.h"
#ifdef OIS_LIRC_SUPPORT
/*
The zlib/libpng License
Copyright (c) 2005-2007 Phillip Castaneda (pjcast -- www.wreckedgames.com)
This software is provided 'as-is', without any express or implied warranty. In no event will
the authors be held liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose, including commercial
applications, and to alter it and redistribute it freely, subject to the following
restrictions:
1. The origin of this software must not be misrepresented; you must not claim that
you wrote the original software. If you use this software in a product,
an acknowledgment in the product documentation would be appreciated but is
not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#ifndef OIS_LIRC_H
#define OIS_LIRC_H
#include "OISJoyStick.h"
#include "OISLIRCRingBuffer.h"
namespace OIS
{
class LIRCFactoryCreator;
struct RemoteInfo
{
RemoteInfo() : buttons(0) {}
RemoteInfo( const RemoteInfo &other )
{
buttons = other.buttons;
buttonMap = other.buttonMap;
}
int buttons;
std::map<std::string, int> buttonMap;
};
//Number of ring buffer events. should be nice sized (the structure is not very big)
//Will be rounded up to power of two automatically
#define OIS_LIRC_EVENT_BUFFER 16
/** Specialty joystick - Linux Infrared Remote Support */
class _OISExport LIRCControl : public JoyStick
{
friend class LIRCFactoryCreator;
public:
LIRCControl(InputManager* creator, int id, bool buffered, LIRCFactoryCreator* local_creator, RemoteInfo &info);
~LIRCControl();
//Overrides of Object
/** copydoc Object::setBuffered */
void setBuffered(bool buffered);
/** copydoc Object::capture */
void capture();
/** copydoc Object::queryInterface */
Interface* queryInterface(Interface::IType type);
/** copydoc Object::_intialize */
void _initialize();
protected:
//! Internal method used to add a button press to the queue (called from thread)
void queueButtonPressed(const std::string &id);
//! The creator who created us
LIRCFactoryCreator *mLIRCCreator;
//! Ringbuffer is used to store events from thread and be read from capture
LIRCRingBuffer mRingBuffer;
//! Information about remote
RemoteInfo mInfo;
};
}
#endif //OIS_LIRC_H
#endif
#include "OISConfig.h"
#ifdef OIS_LIRC_SUPPORT
/*
The zlib/libpng License
Copyright (c) 2005-2007 Phillip Castaneda (pjcast -- www.wreckedgames.com)
This software is provided 'as-is', without any express or implied warranty. In no event will
the authors be held liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose, including commercial
applications, and to alter it and redistribute it freely, subject to the following
restrictions:
1. The origin of this software must not be misrepresented; you must not claim that
you wrote the original software. If you use this software in a product,
an acknowledgment in the product documentation would be appreciated but is
not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "OISLIRCFactoryCreator.h"
#include "OISException.h"
#include <assert.h>
#include <stdlib.h>
#ifdef OIS_WIN32_PLATFORM
# pragma warning (disable : 4996)
# pragma warning (disable : 4267)
# pragma warning (disable : 4554)
# pragma warning (disable : 4996)
# define _WIN32_WINNT 0x0500
#endif
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/thread.hpp>
#include <istream>
#include <sstream>
using namespace OIS;
//---------------------------------------------------------------------------------//
class LIRCFactoryCreator::BoostWrapper
{
public:
LIRCFactoryCreator::BoostWrapper() : mSocket(mIOService), mThreadHandler(0)
{
}
//TCP stuff
boost::asio::io_service mIOService;
boost::asio::ip::tcp::socket mSocket;
//Thread Stuff
//! Boost thread execution object (only alive when at least 1 lirc is alive)
boost::thread *mThreadHandler;
//! Gaurds access to the active lirc list
boost::mutex mLircListMutex;
};
//---------------------------------------------------------------------------------//
LIRCFactoryCreator::LIRCFactoryCreator() :
mConnected(false),
mThreadRunning(false),
mCount(0),
mWrapped(0)
{
mWrapped = new BoostWrapper();
mIP = (getenv("OIS_LIRC_IP") != 0) ? getenv("OIS_LIRC_IP") : "127.0.0.1";
mPort = (getenv("OIS_LIRC_PORT") != 0) ? getenv("OIS_LIRC_PORT") : "8765";
try
{
enableConnection(true);
discoverRemotes();
}
catch(...)
{
mCount = 0;
}
//Regardless of if there is remotes or not, we will close the conenction now.
enableConnection(false);
}
//---------------------------------------------------------------------------------//
LIRCFactoryCreator::~LIRCFactoryCreator()
{
enableConnectionThread(false);
enableConnection(false);
delete mWrapped;
}
//---------------------------------------------------------------------------------//
void LIRCFactoryCreator::discoverRemotes()
{
//http://www.lirc.org/html/technical.html#applications
mCount = 0;
mWrapped->mSocket.write_some(boost::asio::buffer("LIST\n"));
boost::asio::streambuf buffer;
//Read all remotes
bool start = false;
bool data = false;
for(;;)
{
boost::asio::read_until(mWrapped->mSocket, buffer, '\n');
std::istream str(&buffer);
std::string res;
str >> res;
if( res == "" ) //If nothing left, we are done
break;
else if( res == "ERROR" ) //If any errors, we leave immediately
return;
else if( res == "END" ) //We have reached the end block
start = false;
else if( res == "DATA" ) //After Data will be a list of remote names
{
start = true;
data = true;
continue;
}
//Have we gotten the DATA word yet?
if( start == false )
continue;
if( data ) //How many?
mCount = atoi(res.c_str());
else //What follows should now be a list of remote names
mUnusedRemotes.push_back(res);
data = false;
}
//Read information about each remote
boost::asio::streambuf buffer2;
for( int i = 0; i < mCount; ++i )
{
std::ostringstream istr;
istr << "LIST " << mUnusedRemotes[i] << "\n";
mWrapped->mSocket.write_some(boost::asio::buffer(istr.str()));
RemoteInfo information;
int buttonCount = 0;
start = data = false;
for(;;)
{
boost::asio::read_until(mWrapped->mSocket, buffer, '\n');
std::istream str(&buffer);
std::string res;
str >> res;
if( res == "" ) //If nothing left, we are done
break;
else if( res == "ERROR" ) //If error, bail out
return;
else if( res == "END" ) //We have reached the end block
start = false;
else if( res == "DATA" ) //After Data will be button count
{
start = true;
data = true;
continue;
}
//Have we gotten the DATA word yet?
if( start == false )
continue;
if( data ) //After button count, there will be a list of button names
information.buttons = atoi(res.c_str());
else
information.buttonMap[res] = buttonCount++;
data = false;
}
mJoyStickInformation[mUnusedRemotes[i]] = information;
}
}
//---------------------------------------------------------------------------------//
void LIRCFactoryCreator::enableConnection(bool enable, bool blocking)
{
if( enable == true && mConnected == false )
{
boost::asio::ip::tcp::resolver resolver(mWrapped->mIOService);
boost::asio::ip::tcp::resolver::query query(mIP, mPort);
boost::asio::ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
boost::asio::ip::tcp::resolver::iterator end;
//Connect (trying all found connections - ip4/ip6)
boost::asio::error result = boost::asio::error::host_not_found;
while (result && endpoint_iterator != end)
{
mWrapped->mSocket.close();
mWrapped->mSocket.connect(*endpoint_iterator++, boost::asio::assign_error(result));
}
if (result != boost::asio::error::success)
throw (result);
if( blocking == false )
{
mWrapped->mSocket.io_control(boost::asio::socket_base::non_blocking_io(true));
}
mConnected = true;
}
else if( enable == false )
{
mWrapped->mSocket.close();
mConnected = false;
}
}
//---------------------------------------------------------------------------------//
void LIRCFactoryCreator::enableConnectionThread(bool enable)
{
if( enable == true && mThreadRunning == false )
{
mThreadRunning = true;
mWrapped->mThreadHandler = new boost::thread(boost::bind(&LIRCFactoryCreator::threadUpdate, this));
}
else if( enable == false && mThreadRunning == true )
{
mThreadRunning = false;
mWrapped->mThreadHandler->join();
delete mWrapped->mThreadHandler;
mWrapped->mThreadHandler = 0;
}
}
//---------------------------------------------------------------------------------//
void LIRCFactoryCreator::threadUpdate()
{
boost::xtime timer;
boost::asio::streambuf buffer;
std::istream stream(&buffer);
std::string code, repeat, button, remote;
while( mThreadRunning )
{
try
{
while( mWrapped->mSocket.in_avail() > 0 )
{
boost::asio::read_until(mWrapped->mSocket, buffer, '\n');
stream >> code; //64 bit value, ignorable
stream >> repeat; //Repeat rate starting at zero (we ignore, for now)
stream >> button; //Button name
stream >> remote; //Remote name
{ //Lock object, find out which remote sent event
boost::mutex::scoped_lock arrayLock(mWrapped->mLircListMutex);
std::map<std::string, LIRCControl*>::iterator i = mUpdateRemotes.find(remote);
if( i != mUpdateRemotes.end() )
{
i->second->queueButtonPressed(button);
}
}
}
}
catch(...)
{ //Hmm, what should we do if we get a socket error here.. Ignore it I suppose,
} //and wait till the used remote objects get shutdown. We could try to
//reconnect, but how do we know if we will even get the same remotes.
boost::xtime_get(&timer, boost::TIME_UTC);
timer.nsec += 300000000; // 100 000 000 ~= .3 sec
boost::thread::sleep(timer);
}
}
//---------------------------------------------------------------------------------//
DeviceList LIRCFactoryCreator::freeDeviceList()
{
DeviceList list;
for( std::vector<std::string>::iterator i = mUnusedRemotes.begin(); i != mUnusedRemotes.end(); ++i )
list.insert(std::make_pair(OISJoyStick, *i));
return list;
}
//---------------------------------------------------------------------------------//
int LIRCFactoryCreator::totalDevices(Type iType)
{
if( iType == OISJoyStick )
return mCount;
else
return 0;
}
//---------------------------------------------------------------------------------//
int LIRCFactoryCreator::freeDevices(Type iType)
{
if( iType == OISJoyStick )
return (int)mUnusedRemotes.size();
else
return 0;
}
//---------------------------------------------------------------------------------//
bool LIRCFactoryCreator::vendorExist(Type iType, const std::string & vendor)
{
if( iType == OISJoyStick && std::find(mUnusedRemotes.begin(), mUnusedRemotes.end(), vendor) != mUnusedRemotes.end() )
return true;
else
return false;
}
//---------------------------------------------------------------------------------//
Object* LIRCFactoryCreator::createObject(InputManager* creator, Type iType, bool bufferMode, const std::string & vendor)
{
if( mUnusedRemotes.size() > 0 )
{
std::vector<std::string>::iterator remote = mUnusedRemotes.end();
if( vendor == "" )
remote = mUnusedRemotes.begin();
else
remote = std::find(mUnusedRemotes.begin(), mUnusedRemotes.end(), vendor);
if( remote != mUnusedRemotes.end() )
{
//Make sure connection is established
enableConnection(true, false);
//Make sure connection thread is alive
enableConnectionThread(true);
//Create device
LIRCControl *obj = new LIRCControl(creator, 0, bufferMode, this, mJoyStickInformation[*remote]);
//Add to used list, and then remove from unused list
{
boost::mutex::scoped_lock arrayLock(mWrapped->mLircListMutex);
mUpdateRemotes[*remote] = obj;
}
mUnusedRemotes.erase(remote);
return obj;
}
}
OIS_EXCEPT(E_InputDeviceNonExistant, "No Device found which matches description!");
}
//---------------------------------------------------------------------------------//
void LIRCFactoryCreator::destroyObject(Object* obj)
{
if( obj == 0 )
return;
int remotes_alive = 0;
{ //Scope lock
boost::mutex::scoped_lock arrayLock(mWrapped->mLircListMutex);
//Find object
std::map<std::string, LIRCControl*>::iterator i = mUpdateRemotes.begin(), e = mUpdateRemotes.end();
bool found = false;
for(; i != e; ++i)
{
if( i->second == obj )
{
found = true;
break;
}
}
if( found == false )
OIS_EXCEPT(E_General, "Device not found in LIRC remote collection!");
//Move from used to unused list
mUnusedRemotes.push_back(i->first);
mUpdateRemotes.erase(i);
delete obj;
remotes_alive = (int)mUpdateRemotes.size();
}
//Destroy thread if no longer in use (we do this after unlocking mutex!)
if( remotes_alive == 0 )
enableConnectionThread(false);
}
#endif
#include "OISConfig.h"
#ifdef OIS_LIRC_SUPPORT
/*
The zlib/libpng License
Copyright (c) 2005-2007 Phillip Castaneda (pjcast -- www.wreckedgames.com)
This software is provided 'as-is', without any express or implied warranty. In no event will
the authors be held liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose, including commercial
applications, and to alter it and redistribute it freely, subject to the following
restrictions:
1. The origin of this software must not be misrepresented; you must not claim that
you wrote the original software. If you use this software in a product,
an acknowledgment in the product documentation would be appreciated but is
not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#ifndef OIS_LIRCFactoryCreator_H
#define OIS_LIRCFactoryCreator_H
#include "OISPrereqs.h"
#include "OISFactoryCreator.h"
#include "OISLIRC.h"
namespace OIS
{
//Forward declare local classes
class LIRCControl;
/** LIRC Factory Creator Class */
class _OISExport LIRCFactoryCreator : public FactoryCreator
{
public:
LIRCFactoryCreator();
~LIRCFactoryCreator();
//FactoryCreator Overrides
/** @copydoc FactoryCreator::deviceList */
DeviceList freeDeviceList();
/** @copydoc FactoryCreator::totalDevices */
int totalDevices(Type iType);
/** @copydoc FactoryCreator::freeDevices */
int freeDevices(Type iType);
/** @copydoc FactoryCreator::vendorExist */
bool vendorExist(Type iType, const std::string & vendor);
/** @copydoc FactoryCreator::createObject */
Object* createObject(InputManager* creator, Type iType, bool bufferMode, const std::string & vendor = "");
/** @copydoc FactoryCreator::destroyObject */
void destroyObject(Object* obj);
protected:
//! Gets a list of all remotes available
void discoverRemotes();
//! Connects to LIRC server
void enableConnection(bool enable, bool blocking = true);
//! Creates/destroys threaded read
void enableConnectionThread(bool enable);
void threadUpdate();
std::string mIP;
std::string mPort;
bool mConnected;
volatile bool mThreadRunning;
std::map<std::string, LIRCControl*> mUpdateRemotes;
//! List of vendor named remotes that are not used yet
std::vector<std::string> mUnusedRemotes;
//! Information about enumerated remotes
std::map<std::string, RemoteInfo> mJoyStickInformation;
//! Number of total found remotes
int mCount;
//! Get the slow boost header includes from this header by using a proxy wrapper
class BoostWrapper;
//! Wrapped objects
BoostWrapper *mWrapped;
};
}
#endif //OIS_LIRCFactoryCreator_H
#endif
#include "OISConfig.h"
#ifdef OIS_LIRC_SUPPORT
/*
The zlib/libpng License
Copyright (c) 2005-2007 Phillip Castaneda (pjcast -- www.wreckedgames.com)
This software is provided 'as-is', without any express or implied warranty. In no event will
the authors be held liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose, including commercial
applications, and to alter it and redistribute it freely, subject to the following
restrictions:
1. The origin of this software must not be misrepresented; you must not claim that
you wrote the original software. If you use this software in a product,
an acknowledgment in the product documentation would be appreciated but is
not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
# ------------------------#
# Original License follows:
# ------------------------#
* PortAudio Portable Real-Time Audio Library
* Latest version at: http://www.audiomulch.com/portaudio/
* <platform> Implementation
* Copyright (c) 1999-2000 <author(s)>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* Any person wishing to distribute modifications to the Software is
* requested to send the modifications to the original developer so that
* they can be incorporated into the canonical version.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef OIS_LIRCRingBuffer_H
#define OIS_LIRCRingBuffer_H
#include "OISPrereqs.h"
namespace OIS
{
struct LIRCEvent
{
//! high bit (0x80000000) will be on if button pushed, else it was released
unsigned int button;
};
/// <summary>
/// Ring Buffer (fifo) used to store 16bit pcm data
/// </summary>
class LIRCRingBuffer
{
private:
//! Number of bytes in FIFO. Power of 2. Set by RingBuffer_Init
int bufferSize;
//! Used for wrapping indices with extra bit to distinguish full/empty.
int bigMask;
// Used for fitting indices to buffer.
int smallMask;
// Buffer holding the actual event buffers
LIRCEvent *buffer;
//! Index of next writable byte. Set by RingBuffer_AdvanceWriteIndex.
volatile int writeIndex;
//! Index of next readable byte. Set by RingBuffer_AdvanceReadIndex.
volatile int readIndex;
public:
LIRCRingBuffer( unsigned int numEntries )
{
numEntries = RoundUpToNextPowerOf2( numEntries );
//2 bytes per short
bufferSize = (int)numEntries;
buffer = new LIRCEvent[numEntries];
Flush();
bigMask = (int)(numEntries*2)-1;
smallMask = (int)(numEntries)-1;
}
~LIRCRingBuffer()
{
delete buffer;
}
unsigned int RoundUpToNextPowerOf2( unsigned int n )
{
int numBits = 0;
if( ((n-1) & n) == 0)
return n; //Already Power of two.
while( n > 0 )
{
n= n>>1;
numBits++;
}
return (unsigned int)(1<<numBits);
}
int GetReadAvailable( )
{
return ( (writeIndex - readIndex) & bigMask );
}
int GetWriteAvailable( )
{
return ( bufferSize - GetReadAvailable());
}
int Write( LIRCEvent *data, int numEntries )
{
int size1 = 0, size2 = 0, numWritten;
int data1Ptr = 0, data2Ptr = 0;
numWritten = GetWriteRegions( numEntries, data1Ptr, size1, data2Ptr, size2 );
if( size2 > 0 )
{
//copy to two parts
memcpy( &buffer[data1Ptr], data, sizeof(LIRCEvent) * size1 );
//Array.Copy( data, offsetPtr, buffer, data1Ptr, size1 );
memcpy( &buffer[data2Ptr], &data[size1], sizeof(LIRCEvent) * size2 );
//Array.Copy( data, offsetPtr + size1, buffer, data2Ptr, size2 );
}
else
{ //Copy all continous
memcpy( &buffer[data1Ptr], data, sizeof(LIRCEvent) * size1 );
//Array.Copy( data, offsetPtr, buffer, data1Ptr, size1 );
}
AdvanceWriteIndex( numWritten );
return numWritten;
}
/// <summary>
/// Reads requested number of entries into sent array.
/// Returns number written
/// </summary>
int Read( LIRCEvent *data, int numEntries )
{
int size1 = 0, size2 = 0, numRead, data1Ptr = 0, data2Ptr = 0;
numRead = GetReadRegions( numEntries, data1Ptr, size1, data2Ptr, size2 );
if( size2 > 0 )
{
memcpy( data, &buffer[data1Ptr], sizeof(LIRCEvent) * size1 );
//Array.Copy( buffer, data1Ptr, data, 0, size1 );
memcpy( &data[size1], &buffer[data2Ptr], sizeof(LIRCEvent) * size2 );
//Array.Copy( buffer, data2Ptr, data, size1, size2 );
}
else
memcpy( data, &buffer[data1Ptr], sizeof(LIRCEvent) * size1 );
//Array.Copy( buffer, data1Ptr, data, 0, size1 );
AdvanceReadIndex( numRead );
return numRead;
}
private:
int GetWriteRegions( int numEntries, int &dataPtr1, int &sizePtr1,
int &dataPtr2, int &sizePtr2 )
{
int index;
int available = GetWriteAvailable();
if( numEntries > available )
numEntries = available;
//Check to see if write is not contiguous.
index = writeIndex & smallMask;
if( (index + numEntries) > bufferSize )
{
//Write data in two blocks that wrap the buffer.
int firstHalf = bufferSize - index;
dataPtr1 = index;//&buffer[index];
sizePtr1 = firstHalf;
dataPtr2 = 0;//&buffer[0];
sizePtr2 = numEntries - firstHalf;
}
else
{
dataPtr1 = index;//&buffer[index];
sizePtr1 = numEntries;
dataPtr2 = 0;
sizePtr2 = 0;
}
return numEntries;
}
int GetReadRegions( int numEntries, int &dataPtr1, int &sizePtr1, int &dataPtr2, int &sizePtr2 )
{
int index;
int available = GetReadAvailable( );
if( numEntries > available )
numEntries = available;
// Check to see if read is not contiguous
index = readIndex & smallMask;
if( (index + numEntries) > bufferSize )
{
// Write data in two blocks that wrap the buffer
int firstHalf = bufferSize - index;
dataPtr1 = index;//&buffer[index];
sizePtr1 = firstHalf;
dataPtr2 = 0;//&buffer[0];
sizePtr2 = numEntries - firstHalf;
}
else
{
dataPtr1 = index;//&buffer[index];
sizePtr1 = numEntries;
dataPtr2 = 0;
sizePtr2 = 0;
}
return numEntries;
}
int AdvanceWriteIndex( int numEntries )
{
return writeIndex = (writeIndex + numEntries) & bigMask;
}
int AdvanceReadIndex( int numEntries )
{
return readIndex = (readIndex + numEntries) & bigMask;
}
void Flush( )
{
writeIndex = readIndex = 0;
}
};
}
#endif //#define OIS_LIRCRingBuffer_H
#endif
/*
The zlib/libpng License
Copyright (c) 2005-2007 Phillip Castaneda (pjcast -- www.wreckedgames.com)
This software is provided 'as-is', without any express or implied warranty. In no event will
the authors be held liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose, including commercial
applications, and to alter it and redistribute it freely, subject to the following
restrictions:
1. The origin of this software must not be misrepresented; you must not claim that
you wrote the original software. If you use this software in a product,
an acknowledgment in the product documentation would be appreciated but is
not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "iphone/iPhoneAccelerometer.h"
#include "iphone/iPhoneInputManager.h"
using namespace OIS;
//-------------------------------------------------------------------//
iPhoneAccelerometer::iPhoneAccelerometer( InputManager* creator, bool buffered )
: JoyStick(creator->inputSystemName(), buffered, 0, creator)
{
iPhoneInputManager *man = static_cast<iPhoneInputManager*>(mCreator);
man->_setAccelerometerUsed(true);
[man->_getDelegate() setAccelerometerObject:this];
[[UIAccelerometer sharedAccelerometer] setDelegate:man->_getDelegate()];
mUpdateInterval = 60.0f;
}
iPhoneAccelerometer::~iPhoneAccelerometer()
{
iPhoneInputManager *man = static_cast<iPhoneInputManager*>(mCreator);
man->_setAccelerometerUsed(false);
[man->_getDelegate() setAccelerometerObject:nil];
}
void iPhoneAccelerometer::_initialize()
{
// Clear old joy state
mState.mVectors.resize(1);
mState.clear();
mTempState.clear();
// Set the update interval
[[UIAccelerometer sharedAccelerometer] setUpdateInterval:(1.0 / mUpdateInterval)];
}
void iPhoneAccelerometer::setBuffered( bool buffered )
{
mBuffered = buffered;
}
void iPhoneAccelerometer::didAccelerate(UIAcceleration *acceleration)
{
mTempState.clear();
mTempState.x = acceleration.x;
mTempState.y = acceleration.y;
mTempState.z = acceleration.z;
}
void iPhoneAccelerometer::capture()
{
mState.clear();
mState.mVectors[0] = mTempState;
if(mListener && mBuffered)
mListener->axisMoved(JoyStickEvent(this, mState), 0);
}
/*
The zlib/libpng License
Copyright (c) 2006 Chris Snyder
This software is provided 'as-is', without any express or implied warranty. In no event will
the authors be held liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose, including commercial
applications, and to alter it and redistribute it freely, subject to the following
restrictions:
1. The origin of this software must not be misrepresented; you must not claim that
you wrote the original software. If you use this software in a product,
an acknowledgment in the product documentation would be appreciated but is
not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "iphone/iPhoneInputManager.h"
#include "iphone/iPhoneHelpers.h"
#include "iphone/iPhoneAccelerometer.h"
#include "iphone/iPhoneMultiTouch.h"
#include "OISException.h"
using namespace std;
using namespace OIS;
@implementation InputDelegate
@synthesize touchObject;
@synthesize accelerometerObject;
- (id)init {
if((self = [super init])) {
touchObject = nil;
accelerometerObject = nil;
}
return self;
}
- (void)dealloc {
delete touchObject; touchObject = NULL;
delete accelerometerObject; accelerometerObject = NULL;
[super dealloc];
}
- (BOOL)canBecomeFirstResponder
{
return YES;
}
#pragma mark Accelerator Event Handling
- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration {
accelerometerObject->didAccelerate(acceleration);
}
#pragma mark Touch Event Handling
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
for(UITouch *touch in touches) {
touchObject->_touchEnded(touch);
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
for(UITouch *touch in touches) {
touchObject->_touchMoved(touch);
}
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
for(UITouch *touch in touches) {
touchObject->_touchCancelled(touch);
}
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
for(UITouch *touch in touches) {
touchObject->_touchBegan(touch);
}
}
@end
//--------------------------------------------------------------------------------//
iPhoneInputManager::iPhoneInputManager() : InputManager("iPhone Input Manager")
{
mHideMouse = true;
bAccelerometerUsed = bMultiTouchUsed = false;
// Setup our internal factories
mFactories.push_back(this);
}
//--------------------------------------------------------------------------------//
iPhoneInputManager::~iPhoneInputManager()
{
[mDelegate release]; mDelegate = nil;
[mWindow release]; mWindow = nil;
}
//--------------------------------------------------------------------------------//
void iPhoneInputManager::_initialize( ParamList &paramList )
{
_parseConfigSettings( paramList );
mDelegate = [[InputDelegate alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Set flags that we want to accept multiple finger touches and be the only one to receive touch events
[mDelegate setMultipleTouchEnabled:YES];
[mDelegate setExclusiveTouch:YES];
[mDelegate becomeFirstResponder];
[mWindow addSubview:mDelegate];
}
//--------------------------------------------------------------------------------//
void iPhoneInputManager::_parseConfigSettings( ParamList &paramList )
{
// Some carbon apps are running in a window, however full screen apps
// do not have a window, so we need to account for that too.
ParamList::iterator i = paramList.find("WINDOW");
if(i != paramList.end())
{
mWindow = (UIWindow *)strtoul(i->second.c_str(), 0, 10);
if(mWindow == 0)
mWindow = nil;
}
else
{
// else get the main active window.. user might not have access to it through some
// graphics libraries, if that fails then try at the application level.
mWindow = [[UIApplication sharedApplication] keyWindow];
}
if(mWindow == nil)
OIS_EXCEPT( E_General, "iPhoneInputManager::_parseConfigSettings >> Unable to find a window or event target" );
}
//--------------------------------------------------------------------------------//
DeviceList iPhoneInputManager::freeDeviceList()
{
DeviceList ret;
if( bAccelerometerUsed == false )
ret.insert(std::make_pair(OISJoyStick, mInputSystemName));
if( bMultiTouchUsed == false )
ret.insert(std::make_pair(OISMultiTouch, mInputSystemName));
return ret;
}
//--------------------------------------------------------------------------------//
int iPhoneInputManager::totalDevices(Type iType)
{
switch(iType)
{
case OISJoyStick: return 1;
case OISMultiTouch: return 1;
default: return 0;
}
}
//--------------------------------------------------------------------------------//
int iPhoneInputManager::freeDevices(Type iType)
{
switch(iType)
{
case OISJoyStick: return bAccelerometerUsed ? 0 : 1;
case OISMultiTouch: return bMultiTouchUsed ? 0 : 1;
default: return 0;
}
}
//--------------------------------------------------------------------------------//
bool iPhoneInputManager::vendorExist(Type iType, const std::string & vendor)
{
if( ( iType == OISMultiTouch || iType == OISJoyStick ) && vendor == mInputSystemName )
return true;
return false;
}
//--------------------------------------------------------------------------------//
Object* iPhoneInputManager::createObject(InputManager* creator, Type iType, bool bufferMode,
const std::string & vendor)
{
Object *obj = 0;
switch(iType)
{
case OISJoyStick:
{
if( bAccelerometerUsed == false )
obj = new iPhoneAccelerometer(this, bufferMode);
break;
}
case OISMultiTouch:
{
if( bMultiTouchUsed == false )
obj = new iPhoneMultiTouch(this, bufferMode);
break;
}
default:
break;
}
if( obj == 0 )
OIS_EXCEPT(E_InputDeviceNonExistant, "No devices match requested type.");
return obj;
}
//--------------------------------------------------------------------------------//
void iPhoneInputManager::destroyObject(Object* obj)
{
delete obj;
}
/*
The zlib/libpng License
Copyright (c) 2005-2007 Phillip Castaneda (pjcast -- www.wreckedgames.com)
This software is provided 'as-is', without any express or implied warranty. In no event will
the authors be held liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose, including commercial
applications, and to alter it and redistribute it freely, subject to the following
restrictions:
1. The origin of this software must not be misrepresented; you must not claim that
you wrote the original software. If you use this software in a product,
an acknowledgment in the product documentation would be appreciated but is
not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "iphone/iPhoneMultiTouch.h"
#include "iphone/iPhoneInputManager.h"
using namespace OIS;
//-------------------------------------------------------------------//
iPhoneMultiTouch::iPhoneMultiTouch( InputManager* creator, bool buffered )
: MultiTouch(creator->inputSystemName(), buffered, 0, creator)
{
iPhoneInputManager *man = static_cast<iPhoneInputManager*>(mCreator);
man->_setMultiTouchUsed(true);
[man->_getDelegate() setTouchObject:this];
}
iPhoneMultiTouch::~iPhoneMultiTouch()
{
iPhoneInputManager *man = static_cast<iPhoneInputManager*>(mCreator);
man->_setMultiTouchUsed(false);
[man->_getDelegate() setTouchObject:nil];
}
void iPhoneMultiTouch::_initialize()
{
// mTempState.clear();
}
void iPhoneMultiTouch::setBuffered( bool buffered )
{
mBuffered = buffered;
}
void iPhoneMultiTouch::capture()
{
#if 0
for( std::multiset<MultiTouchState *>::iterator i = mStates.begin(), e = mStates.end(); i != e; ++i )
{
// Clear the state first
dynamic_cast<MultiTouchState *>(*i)->clear();
if(mTempState.X.rel || mTempState.Y.rel || mTempState.Z.rel)
{
MultiTouchState *iState = dynamic_cast<MultiTouchState *>(*i);
// NSLog(@"%i %i %i", mTempState.X.rel, mTempState.Y.rel, mTempState.Z.rel);
// Set new relative motion values
iState->X.rel = mTempState.X.rel;
iState->Y.rel = mTempState.Y.rel;
iState->Z.rel = mTempState.Z.rel;
// Update absolute position
iState->X.abs += mTempState.X.rel;
iState->Y.abs += mTempState.Y.rel;
if(iState->X.abs > iState->width)
iState->X.abs = iState->width;
else if(iState->X.abs < 0)
iState->X.abs = 0;
if(iState->Y.abs > iState->height)
iState->Y.abs = iState->height;
else if(iState->Y.abs < 0)
iState->Y.abs = 0;
iState->Z.abs += mTempState.Z.rel;
//Fire off event
if(mListener && mBuffered)
mListener->touchMoved(MultiTouchEvent(this, *iState));
}
}
mTempState.clear();
#endif
}
void iPhoneMultiTouch::_touchBegan(UITouch *touch)
{
CGPoint location = [touch locationInView:static_cast<iPhoneInputManager*>(mCreator)->_getDelegate()];
MultiTouchState newState;
newState.X.abs = location.x;
newState.Y.abs = location.y;
newState.touchType |= 1 << MT_Pressed;
if( mListener && mBuffered )
{
mListener->touchPressed(MultiTouchEvent(this, newState));
}
else
{
mStates.push_back(newState);
}
}
void iPhoneMultiTouch::_touchEnded(UITouch *touch)
{
CGPoint location = [touch locationInView:static_cast<iPhoneInputManager*>(mCreator)->_getDelegate()];
MultiTouchState newState;
newState.X.abs = location.x;
newState.Y.abs = location.y;
newState.touchType |= 1 << MT_Released;
if( mListener && mBuffered )
{
mListener->touchReleased(MultiTouchEvent(this, newState));
}
else
{
mStates.push_back(newState);
}
}
void iPhoneMultiTouch::_touchMoved(UITouch *touch)
{
CGPoint location = [touch locationInView:static_cast<iPhoneInputManager*>(mCreator)->_getDelegate()];
CGPoint previousLocation = [touch previousLocationInView:static_cast<iPhoneInputManager*>(mCreator)->_getDelegate()];
MultiTouchState newState;
newState.X.rel = (location.x - previousLocation.x);
newState.Y.rel = (location.y - previousLocation.y);
newState.X.abs = location.x;
newState.Y.abs = location.y;
newState.touchType |= 1 << MT_Moved;
if( mListener && mBuffered )
{
mListener->touchMoved(MultiTouchEvent(this, newState));
}
else
{
mStates.push_back(newState);
}
}
void iPhoneMultiTouch::_touchCancelled(UITouch *touch)
{
CGPoint location = [touch locationInView:static_cast<iPhoneInputManager*>(mCreator)->_getDelegate()];
MultiTouchState newState;
newState.X.abs = location.x;
newState.Y.abs = location.y;
newState.touchType |= 1 << MT_Cancelled;
if( mListener && mBuffered )
{
mListener->touchCancelled(MultiTouchEvent(this, newState));
}
else
{
mStates.push_back(newState);
}
}
/*
The zlib/libpng License
Copyright (c) 2005-2007 Phillip Castaneda (pjcast -- www.wreckedgames.com)
This software is provided 'as-is', without any express or implied warranty. In no event will
the authors be held liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose, including commercial
applications, and to alter it and redistribute it freely, subject to the following
restrictions:
1. The origin of this software must not be misrepresented; you must not claim that
you wrote the original software. If you use this software in a product,
an acknowledgment in the product documentation would be appreciated but is
not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "linux/EventHelpers.h"
#include "linux/LinuxPrereqs.h"
#include "linux/LinuxForceFeedback.h"
#include "OISException.h"
#include "OISJoyStick.h"
#include <linux/input.h>
#include <cstring>
//#define OIS_LINUX_JOY_DEBUG
#ifdef OIS_LINUX_JOY_DEBUG
# include <iostream>
#endif
using namespace std;
using namespace OIS;
class DeviceComponentInfo
{
public:
vector<int> buttons, relAxes, absAxes, hats;
};
bool inline isBitSet(unsigned char bits[], unsigned int bit)
{
return (bits[(bit)/(sizeof(unsigned char)*8)] >> ((bit)%(sizeof(unsigned char)*8))) & 1;
}
//-----------------------------------------------------------------------------//
DeviceComponentInfo getComponentInfo( int deviceID )
{
unsigned char ev_bits[1 + EV_MAX/8/sizeof(unsigned char)];
memset( ev_bits, 0, sizeof(ev_bits) );
//Read "all" (hence 0) components of the device
#ifdef OIS_LINUX_JOY_DEBUG
cout << "EventUtils::getComponentInfo(" << deviceID
<< ") : Reading device events features" << endl;
#endif
if (ioctl(deviceID, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits) == -1)
OIS_EXCEPT( E_General, "Could not read device events features");
DeviceComponentInfo components;
for (int i = 0; i < EV_MAX; i++)
{
if( isBitSet(ev_bits, i) )
{
// Absolute axis.
if(i == EV_ABS)
{
unsigned char abs_bits[1 + ABS_MAX/8/sizeof(unsigned char)];
memset( abs_bits, 0, sizeof(abs_bits) );
#ifdef OIS_LINUX_JOY_DEBUG
cout << "EventUtils::getComponentInfo(" << deviceID
<< ") : Reading device absolute axis features" << endl;
#endif
if (ioctl(deviceID, EVIOCGBIT(i, sizeof(abs_bits)), abs_bits) == -1)
OIS_EXCEPT( E_General, "Could not read device absolute axis features");
for (int j = 0; j < ABS_MAX; j++)
{
if( isBitSet(abs_bits, j) )
{
//input_absinfo abInfo;
//ioctl( fd, EVIOCGABS(j), abInfo );
if( j >= ABS_HAT0X && j <= ABS_HAT3Y )
{
components.hats.push_back(j);
}
else
{
components.absAxes.push_back(j);
//input_absinfo absinfo;
//ioctl(deviceID, EVIOCGABS(j), &absinfo);
//We cannot actually change these values :|
//absinfo.minimum = JoyStick::MIN_AXIS;
//absinfo.maximum = JoyStick::MAX_AXIS;
//ioctl(deviceID, EVIOCSABS(j), &absinfo);
}
}
}
}
else if(i == EV_REL)
{
unsigned char rel_bits[1 + REL_MAX/8/sizeof(unsigned char)];
memset( rel_bits, 0, sizeof(rel_bits) );
#ifdef OIS_LINUX_JOY_DEBUG
cout << "EventUtils::getComponentInfo(" << deviceID
<< ") : Reading device relative axis features" << endl;
#endif
if (ioctl(deviceID, EVIOCGBIT(i, sizeof(rel_bits)), rel_bits) == -1)
OIS_EXCEPT( E_General, "Could not read device relative axis features");
for (int j = 0; j < REL_MAX; j++)
{
if( isBitSet(rel_bits, j) )
{
components.relAxes.push_back(j);
}
}
}
else if(i == EV_KEY)
{
unsigned char key_bits[1 + KEY_MAX/8/sizeof(unsigned char)];
memset( key_bits, 0, sizeof(key_bits) );
#ifdef OIS_LINUX_JOY_DEBUG
cout << "EventUtils::getComponentInfo(" << deviceID
<< ") : Reading device buttons features" << endl;
#endif
if (ioctl(deviceID, EVIOCGBIT(i, sizeof(key_bits)), key_bits) == -1)
OIS_EXCEPT( E_General, "Could not read device buttons features");
for (int j = 0; j < KEY_MAX; j++)
{
if( isBitSet(key_bits, j) )
{
components.buttons.push_back(j);
}
}
}
}
}
return components;
}
//-----------------------------------------------------------------------------//
bool EventUtils::isJoyStick( int deviceID, JoyStickInfo &js )
{
if( deviceID == -1 )
OIS_EXCEPT( E_General, "Error with File Descriptor" );
DeviceComponentInfo info = getComponentInfo( deviceID );
int buttons = 0;
bool joyButtonFound = false;
js.button_map.clear();
#ifdef OIS_LINUX_JOY_DEBUG
cout << endl << "Displaying ButtonMapping Status:" << endl;
#endif
for(vector<int>::iterator i = info.buttons.begin(), e = info.buttons.end(); i != e; ++i )
{
//Check to ensure we find at least one joy only button
if( (*i >= BTN_JOYSTICK && *i < BTN_GAMEPAD)
|| (*i >= BTN_GAMEPAD && *i < BTN_DIGI)
|| (*i >= BTN_WHEEL && *i < KEY_OK) )
joyButtonFound = true;
js.button_map[*i] = buttons++;
#ifdef OIS_LINUX_JOY_DEBUG
cout << "Button Mapping ID (hex): " << hex << *i
<< " OIS Button Num: " << dec << buttons-1 << endl;
#endif
}
#ifdef OIS_LINUX_JOY_DEBUG
cout << endl;
#endif
//Joy Buttons found, so it must be a joystick or pad
if( joyButtonFound )
{
js.joyFileD = deviceID;
js.vendor = getName(deviceID);
js.buttons = buttons;
js.axes = info.relAxes.size() + info.absAxes.size();
js.hats = info.hats.size();
#ifdef OIS_LINUX_JOY_DEBUG
cout << endl << "Device name:" << js.vendor << endl;
cout << "Device unique Id:" << getUniqueId(deviceID) << endl;
cout << "Device physical location:" << getPhysicalLocation(deviceID) << endl;
#endif
//Map the Axes
#ifdef OIS_LINUX_JOY_DEBUG
cout << endl << "Displaying AxisMapping Status:" << endl;
#endif
int axes = 0;
for(vector<int>::iterator i = info.absAxes.begin(), e = info.absAxes.end(); i != e; ++i )
{
js.axis_map[*i] = axes;
#ifdef OIS_LINUX_JOY_DEBUG
cout << "EventUtils::isJoyStick(" << deviceID
<< ") : Reading device absolute axis #" << *i << " features" << endl;
#endif
input_absinfo absinfo;
if (ioctl(deviceID, EVIOCGABS(*i), &absinfo) == -1)
OIS_EXCEPT( E_General, "Could not read device absolute axis features");
js.axis_range[axes] = Range(absinfo.minimum, absinfo.maximum);
#ifdef OIS_LINUX_JOY_DEBUG
cout << "Axis Mapping ID (hex): " << hex << *i
<< " OIS Axis Num: " << dec << axes << endl;
#endif
++axes;
}
}
return joyButtonFound;
}
//-----------------------------------------------------------------------------//
string EventUtils::getName( int deviceID )
{
#ifdef OIS_LINUX_JOY_DEBUG
cout << "EventUtils::getName(" << deviceID
<< ") : Reading device name" << endl;
#endif
char name[OIS_DEVICE_NAME];
if (ioctl(deviceID, EVIOCGNAME(OIS_DEVICE_NAME), name) == -1)
OIS_EXCEPT( E_General, "Could not read device name");
return string(name);
}
//-----------------------------------------------------------------------------//
string EventUtils::getUniqueId( int deviceID )
{
#ifdef OIS_LINUX_JOY_DEBUG
cout << "EventUtils::getUniqueId(" << deviceID
<< ") : Reading device unique Id" << endl;
#endif
#define OIS_DEVICE_UNIQUE_ID 128
char uId[OIS_DEVICE_UNIQUE_ID];
if (ioctl(deviceID, EVIOCGUNIQ(OIS_DEVICE_UNIQUE_ID), uId) == -1)
OIS_EXCEPT( E_General, "Could not read device unique Id");
return string(uId);
}
//-----------------------------------------------------------------------------//
string EventUtils::getPhysicalLocation( int deviceID )
{
#ifdef OIS_LINUX_JOY_DEBUG
cout << "EventUtils::getPhysicalLocation(" << deviceID
<< ") : Reading device physical location" << endl;
#endif
#define OIS_DEVICE_PHYSICAL_LOCATION 128
char physLoc[OIS_DEVICE_PHYSICAL_LOCATION];
if (ioctl(deviceID, EVIOCGPHYS(OIS_DEVICE_PHYSICAL_LOCATION), physLoc) == -1)
OIS_EXCEPT( E_General, "Could not read device physical location");
return string(physLoc);
}
//-----------------------------------------------------------------------------//
void EventUtils::enumerateForceFeedback( int deviceID, LinuxForceFeedback** ff )
{
//Linux Event to OIS Event Mappings
map<int, Effect::EType> typeMap;
typeMap[FF_CONSTANT] = Effect::Constant;
typeMap[FF_RAMP] = Effect::Ramp;
typeMap[FF_SPRING] = Effect::Spring;
typeMap[FF_FRICTION] = Effect::Friction;
typeMap[FF_SQUARE] = Effect::Square;
typeMap[FF_TRIANGLE] = Effect::Triangle;
typeMap[FF_SINE] = Effect::Sine;
typeMap[FF_SAW_UP] = Effect::SawToothUp;
typeMap[FF_SAW_DOWN] = Effect::SawToothDown;
typeMap[FF_DAMPER] = Effect::Damper;
typeMap[FF_INERTIA] = Effect::Inertia;
typeMap[FF_CUSTOM] = Effect::Custom;
map<int, Effect::EForce> forceMap;
forceMap[FF_CONSTANT] = Effect::ConstantForce;
forceMap[FF_RAMP] = Effect::RampForce;
forceMap[FF_SPRING] = Effect::ConditionalForce;
forceMap[FF_FRICTION] = Effect::ConditionalForce;
forceMap[FF_SQUARE] = Effect::PeriodicForce;
forceMap[FF_TRIANGLE] = Effect::PeriodicForce;
forceMap[FF_SINE] = Effect::PeriodicForce;
forceMap[FF_SAW_UP] = Effect::PeriodicForce;
forceMap[FF_SAW_DOWN] = Effect::PeriodicForce;
forceMap[FF_DAMPER] = Effect::ConditionalForce;
forceMap[FF_INERTIA] = Effect::ConditionalForce;
forceMap[FF_CUSTOM] = Effect::CustomForce;
//Remove any previously existing memory and create fresh
removeForceFeedback( ff );
*ff = new LinuxForceFeedback(deviceID);
//Read overall force feedback features
unsigned char ff_bits[1 + FF_MAX/8/sizeof(unsigned char)];
memset(ff_bits, 0, sizeof(ff_bits));
#ifdef OIS_LINUX_JOY_DEBUG
cout << "EventUtils::enumerateForceFeedback(" << deviceID
<< ") : Reading device force feedback features" << endl;
#endif
if (ioctl(deviceID, EVIOCGBIT(EV_FF, sizeof(ff_bits)), ff_bits) == -1)
OIS_EXCEPT( E_General, "Could not read device force feedback features");
#ifdef OIS_LINUX_JOY_DEBUG
cout << "FF bits: " << hex;
for (int i = 0; i < sizeof(ff_bits); i++)
cout << (int)ff_bits[i];
cout << endl << dec;
#endif
//FF Axes
//if( isBitSet(ff_bits, ABS_X) ) //X Axis
//if( isBitSet(ff_bits, ABS_Y) ) //Y Axis
//if( isBitSet(ff_bits, ABS_WHEEL) ) //Wheel
//FF Effects
for( int effect = FF_EFFECT_MIN; effect <= FF_WAVEFORM_MAX; effect++ )
{
// The RUMBLE force type is ignored, as periodic force one is more powerfull.
// The PERIODIC force type is processed later, for each associated periodic effect type.
if (effect == FF_RUMBLE || effect == FF_PERIODIC)
continue;
if(isBitSet(ff_bits, effect))
{
#ifdef OIS_LINUX_JOY_DEBUG
cout << " Effect Type: " << Effect::getEffectTypeName(typeMap[effect]) << endl;
#endif
(*ff)->_addEffectTypes( forceMap[effect], typeMap[effect] );
}
}
//FF device properties
if (isBitSet(ff_bits, FF_GAIN))
(*ff)->_setGainSupport(true);
if (isBitSet(ff_bits, FF_AUTOCENTER))
(*ff)->_setAutoCenterSupport(true);
//Check to see if any effects were added, else destroy the pointer
const ForceFeedback::SupportedEffectList &list = (*ff)->getSupportedEffects();
if( list.size() == 0 )
removeForceFeedback( ff );
}
//-----------------------------------------------------------------------------//
void EventUtils::removeForceFeedback( LinuxForceFeedback** ff )
{
delete *ff;
*ff = 0;
}
/*
The zlib/libpng License
Copyright (c) 2005-2007 Phillip Castaneda (pjcast -- www.wreckedgames.com)
This software is provided 'as-is', without any express or implied warranty. In no event will
the authors be held liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose, including commercial
applications, and to alter it and redistribute it freely, subject to the following
restrictions:
1. The origin of this software must not be misrepresented; you must not claim that
you wrote the original software. If you use this software in a product,
an acknowledgment in the product documentation would be appreciated but is
not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "linux/LinuxForceFeedback.h"
#include "OISException.h"
#include <cstdlib>
#include <errno.h>
#include <memory.h>
using namespace OIS;
// 0 = No trace; 1 = Important traces; 2 = Debug traces
#define OIS_LINUX_JOYFF_DEBUG 1
#ifdef OIS_LINUX_JOYFF_DEBUG
# include <iostream>
using namespace std;
#endif
//--------------------------------------------------------------//
LinuxForceFeedback::LinuxForceFeedback(int deviceID) :
ForceFeedback(), mJoyStick(deviceID)
{
}
//--------------------------------------------------------------//
LinuxForceFeedback::~LinuxForceFeedback()
{
// Unload all effects.
for(EffectList::iterator i = mEffectList.begin(); i != mEffectList.end(); ++i )
{
struct ff_effect *linEffect = i->second;
if( linEffect )
_unload(linEffect->id);
}
mEffectList.clear();
}
//--------------------------------------------------------------//
unsigned short LinuxForceFeedback::getFFMemoryLoad()
{
int nEffects = -1;
if (ioctl(mJoyStick, EVIOCGEFFECTS, &nEffects) == -1)
OIS_EXCEPT(E_General, "Unknown error reading max number of uploaded effects.");
#if (OIS_LINUX_JOYFF_DEBUG > 1)
cout << "LinuxForceFeedback("<< mJoyStick
<< ") : Read device max number of uploaded effects : " << nEffects << endl;
#endif
return (unsigned short int)(nEffects > 0 ? 100.0*mEffectList.size()/nEffects : 100);
}
//--------------------------------------------------------------//
void LinuxForceFeedback::setMasterGain(float value)
{
if (!mSetGainSupport)
{
#if (OIS_LINUX_JOYFF_DEBUG > 0)
cout << "LinuxForceFeedback("<< mJoyStick << ") : Setting master gain "
<< "is not supported by the device" << endl;
#endif
return;
}
struct input_event event;
memset(&event, 0, sizeof(event));
event.type = EV_FF;
event.code = FF_GAIN;
if (value < 0.0)
value = 0.0;
else if (value > 1.0)
value = 1.0;
event.value = (__s32)(value * 0xFFFFUL);
#if (OIS_LINUX_JOYFF_DEBUG > 0)
cout << "LinuxForceFeedback("<< mJoyStick << ") : Setting master gain to "
<< value << " => " << event.value << endl;
#endif
if (write(mJoyStick, &event, sizeof(event)) != sizeof(event)) {
OIS_EXCEPT(E_General, "Unknown error changing master gain.");
}
}
//--------------------------------------------------------------//
void LinuxForceFeedback::setAutoCenterMode(bool enabled)
{
if (!mSetAutoCenterSupport)
{
#if (OIS_LINUX_JOYFF_DEBUG > 0)
cout << "LinuxForceFeedback("<< mJoyStick << ") : Setting auto-center mode "
<< "is not supported by the device" << endl;
#endif
return;
}
struct input_event event;
memset(&event, 0, sizeof(event));
event.type = EV_FF;
event.code = FF_AUTOCENTER;
event.value = (__s32)(enabled*0xFFFFFFFFUL);
#if (OIS_LINUX_JOYFF_DEBUG > 0)
cout << "LinuxForceFeedback("<< mJoyStick << ") : Toggling auto-center to "
<< enabled << " => 0x" << hex << event.value << dec << endl;
#endif
if (write(mJoyStick, &event, sizeof(event)) != sizeof(event)) {
OIS_EXCEPT(E_General, "Unknown error toggling auto-center.");
}
}
//--------------------------------------------------------------//
void LinuxForceFeedback::upload( const Effect* effect )
{
switch( effect->force )
{
case OIS::Effect::ConstantForce:
_updateConstantEffect(effect);
break;
case OIS::Effect::ConditionalForce:
_updateConditionalEffect(effect);
break;
case OIS::Effect::PeriodicForce:
_updatePeriodicEffect(effect);
break;
case OIS::Effect::RampForce:
_updateRampEffect(effect);
break;
case OIS::Effect::CustomForce:
//_updateCustomEffect(effect);
//break;
default:
OIS_EXCEPT(E_NotImplemented, "Requested force not implemented yet, sorry!");
break;
}
}
//--------------------------------------------------------------//
void LinuxForceFeedback::modify( const Effect* effect )
{
upload(effect);
}
//--------------------------------------------------------------//
void LinuxForceFeedback::remove( const Effect* effect )
{
//Get the effect - if it exists
EffectList::iterator i = mEffectList.find(effect->_handle);
if( i != mEffectList.end() )
{
struct ff_effect *linEffect = i->second;
if( linEffect )
{
_stop(effect->_handle);
_unload(effect->_handle);
free(linEffect);
mEffectList.erase(i);
}
else
mEffectList.erase(i);
}
}
//--------------------------------------------------------------//
// To Signed16/Unsigned15 safe conversions
#define MaxUnsigned15Value 0x7FFF
#define toUnsigned15(value) \
(__u16)((value) < 0 ? 0 : ((value) > MaxUnsigned15Value ? MaxUnsigned15Value : (value)))
#define MaxSigned16Value 0x7FFF
#define MinSigned16Value -0x7FFF
#define toSigned16(value) \
(__s16)((value) < MinSigned16Value ? MinSigned16Value : ((value) > MaxSigned16Value ? MaxSigned16Value : (value)))
// OIS to Linux duration
#define LinuxInfiniteDuration 0xFFFF
#define OISDurationUnitMS 1000 // OIS duration unit (microseconds), expressed in milliseconds (theLinux duration unit)
// linux/input.h : All duration values are expressed in ms. Values above 32767 ms (0x7fff)
// should not be used and have unspecified results.
#define LinuxDuration(oisDuration) ((oisDuration) == Effect::OIS_INFINITE ? LinuxInfiniteDuration \
: toUnsigned15((oisDuration)/OISDurationUnitMS))
// OIS to Linux levels
#define OISMaxLevel 10000
#define LinuxMaxLevel 0x7FFF
// linux/input.h : Valid range for the attack and fade levels is 0x0000 - 0x7fff
#define LinuxPositiveLevel(oisLevel) toUnsigned15(LinuxMaxLevel*(long)(oisLevel)/OISMaxLevel)
#define LinuxSignedLevel(oisLevel) toSigned16(LinuxMaxLevel*(long)(oisLevel)/OISMaxLevel)
//--------------------------------------------------------------//
void LinuxForceFeedback::_setCommonProperties(struct ff_effect *event,
struct ff_envelope *ffenvelope,
const Effect* effect, const Envelope *envelope )
{
memset(event, 0, sizeof(struct ff_effect));
if (envelope && ffenvelope && envelope->isUsed()) {
ffenvelope->attack_length = LinuxDuration(envelope->attackLength);
ffenvelope->attack_level = LinuxPositiveLevel(envelope->attackLevel);
ffenvelope->fade_length = LinuxDuration(envelope->fadeLength);
ffenvelope->fade_level = LinuxPositiveLevel(envelope->fadeLevel);
}
#if (OIS_LINUX_JOYFF_DEBUG > 1)
cout << endl;
if (envelope && ffenvelope)
{
cout << " Enveloppe :" << endl
<< " AttackLen : " << envelope->attackLength
<< " => " << ffenvelope->attack_length << endl
<< " AttackLvl : " << envelope->attackLevel
<< " => " << ffenvelope->attack_level << endl
<< " FadeLen : " << envelope->fadeLength
<< " => " << ffenvelope->fade_length << endl
<< " FadeLvl : " << envelope->fadeLevel
<< " => " << ffenvelope->fade_level << endl;
}
#endif
event->direction = (__u16)(1 + (effect->direction*45.0+135.0)*0xFFFFUL/360.0);
#if (OIS_LINUX_JOYFF_DEBUG > 1)
cout << " Direction : " << Effect::getDirectionName(effect->direction)
<< " => 0x" << hex << event->direction << dec << endl;
#endif
// TODO trigger_button 0 vs. -1
event->trigger.button = effect->trigger_button; // < 0 ? 0 : effect->trigger_button;
event->trigger.interval = LinuxDuration(effect->trigger_interval);
#if (OIS_LINUX_JOYFF_DEBUG > 1)
cout << " Trigger :" << endl
<< " Button : " << effect->trigger_button
<< " => " << event->trigger.button << endl
<< " Interval : " << effect->trigger_interval
<< " => " << event->trigger.interval << endl;
#endif
event->replay.length = LinuxDuration(effect->replay_length);
event->replay.delay = LinuxDuration(effect->replay_delay);
#if (OIS_LINUX_JOYFF_DEBUG > 1)
cout << " Replay :" << endl
<< " Length : " << effect->replay_length
<< " => " << event->replay.length << endl
<< " Delay : " << effect->replay_delay
<< " => " << event->replay.delay << endl;
#endif
}
//--------------------------------------------------------------//
void LinuxForceFeedback::_updateConstantEffect( const Effect* eff )
{
struct ff_effect event;
ConstantEffect *effect = static_cast<ConstantEffect*>(eff->getForceEffect());
_setCommonProperties(&event, &event.u.constant.envelope, eff, &effect->envelope);
event.type = FF_CONSTANT;
event.id = -1;
event.u.constant.level = LinuxSignedLevel(effect->level);
#if (OIS_LINUX_JOYFF_DEBUG > 1)
cout << " Level : " << effect->level
<< " => " << event.u.constant.level << endl;
#endif
_upload(&event, eff);
}
//--------------------------------------------------------------//
void LinuxForceFeedback::_updateRampEffect( const Effect* eff )
{
struct ff_effect event;
RampEffect *effect = static_cast<RampEffect*>(eff->getForceEffect());
_setCommonProperties(&event, &event.u.constant.envelope, eff, &effect->envelope);
event.type = FF_RAMP;
event.id = -1;
event.u.ramp.start_level = LinuxSignedLevel(effect->startLevel);
event.u.ramp.end_level = LinuxSignedLevel(effect->endLevel);
#if (OIS_LINUX_JOYFF_DEBUG > 1)
cout << " StartLevel : " << effect->startLevel
<< " => " << event.u.ramp.start_level << endl
<< " EndLevel : " << effect->endLevel
<< " => " << event.u.ramp.end_level << endl;
#endif
_upload(&event, eff);
}
//--------------------------------------------------------------//
void LinuxForceFeedback::_updatePeriodicEffect( const Effect* eff )
{
struct ff_effect event;
PeriodicEffect *effect = static_cast<PeriodicEffect*>(eff->getForceEffect());
_setCommonProperties(&event, &event.u.periodic.envelope, eff, &effect->envelope);
event.type = FF_PERIODIC;
event.id = -1;
switch( eff->type )
{
case OIS::Effect::Square:
event.u.periodic.waveform = FF_SQUARE;
break;
case OIS::Effect::Triangle:
event.u.periodic.waveform = FF_TRIANGLE;
break;
case OIS::Effect::Sine:
event.u.periodic.waveform = FF_SINE;
break;
case OIS::Effect::SawToothUp:
event.u.periodic.waveform = FF_SAW_UP;
break;
case OIS::Effect::SawToothDown:
event.u.periodic.waveform = FF_SAW_DOWN;
break;
// Note: No support for Custom periodic force effect for the moment
//case OIS::Effect::Custom:
//event.u.periodic.waveform = FF_CUSTOM;
//break;
default:
OIS_EXCEPT(E_General, "No such available effect for Periodic force!");
break;
}
event.u.periodic.period = LinuxDuration(effect->period);
event.u.periodic.magnitude = LinuxPositiveLevel(effect->magnitude);
event.u.periodic.offset = LinuxPositiveLevel(effect->offset);
event.u.periodic.phase = (__u16)(effect->phase*event.u.periodic.period/36000.0); // ?????
// Note: No support for Custom periodic force effect for the moment
event.u.periodic.custom_len = 0;
event.u.periodic.custom_data = 0;
#if (OIS_LINUX_JOYFF_DEBUG > 1)
cout << " Magnitude : " << effect->magnitude
<< " => " << event.u.periodic.magnitude << endl
<< " Period : " << effect->period
<< " => " << event.u.periodic.period << endl
<< " Offset : " << effect->offset
<< " => " << event.u.periodic.offset << endl
<< " Phase : " << effect->phase
<< " => " << event.u.periodic.phase << endl;
#endif
_upload(&event, eff);
}
//--------------------------------------------------------------//
void LinuxForceFeedback::_updateConditionalEffect( const Effect* eff )
{
struct ff_effect event;
ConditionalEffect *effect = static_cast<ConditionalEffect*>(eff->getForceEffect());
_setCommonProperties(&event, NULL, eff, NULL);
switch( eff->type )
{
case OIS::Effect::Friction:
event.type = FF_FRICTION;
break;
case OIS::Effect::Damper:
event.type = FF_DAMPER;
break;
case OIS::Effect::Inertia:
event.type = FF_INERTIA;
break;
case OIS::Effect::Spring:
event.type = FF_SPRING;
break;
default:
OIS_EXCEPT(E_General, "No such available effect for Conditional force!");
break;
}
event.id = -1;
event.u.condition[0].right_saturation = LinuxSignedLevel(effect->rightSaturation);
event.u.condition[0].left_saturation = LinuxSignedLevel(effect->leftSaturation);
event.u.condition[0].right_coeff = LinuxSignedLevel(effect->rightCoeff);
event.u.condition[0].left_coeff = LinuxSignedLevel(effect->leftCoeff);
event.u.condition[0].deadband = LinuxPositiveLevel(effect->deadband);// Unit ??
event.u.condition[0].center = LinuxSignedLevel(effect->center); // Unit ?? TODO ?
// TODO support for second condition
event.u.condition[1] = event.u.condition[0];
#if (OIS_LINUX_JOYFF_DEBUG > 1)
cout << " Condition[0] : " << endl
<< " RightSaturation : " << effect->rightSaturation
<< " => " << event.u.condition[0].right_saturation << endl
<< " LeftSaturation : " << effect->leftSaturation
<< " => " << event.u.condition[0]. left_saturation << endl
<< " RightCoefficient : " << effect->rightCoeff
<< " => " << event.u.condition[0].right_coeff << endl
<< " LeftCoefficient : " << effect->leftCoeff
<< " => " << event.u.condition[0].left_coeff << endl
<< " DeadBand : " << effect->deadband
<< " => " << event.u.condition[0].deadband << endl
<< " Center : " << effect->center
<< " => " << event.u.condition[0].center << endl;
cout << " Condition[1] : Not implemented" << endl;
#endif
_upload(&event, eff);
}
//--------------------------------------------------------------//
void LinuxForceFeedback::_upload( struct ff_effect* ffeffect, const Effect* effect)
{
struct ff_effect *linEffect = 0;
//Get the effect - if it exists
EffectList::iterator i = mEffectList.find(effect->_handle);
//It has been created already
if( i != mEffectList.end() )
linEffect = i->second;
if( linEffect == 0 )
{
#if (OIS_LINUX_JOYFF_DEBUG > 1)
cout << endl << "LinuxForceFeedback("<< mJoyStick << ") : Adding new effect : "
<< Effect::getEffectTypeName(effect->type) << endl;
#endif
//This effect has not yet been created, so create it in the device
if (ioctl(mJoyStick, EVIOCSFF, ffeffect) == -1) {
// TODO device full check
// OIS_EXCEPT(E_DeviceFull, "Remove an effect before adding more!");
OIS_EXCEPT(E_General, "Unknown error creating effect (may be the device is full)->..");
}
// Save returned effect handle
effect->_handle = ffeffect->id;
// Save a copy of the uploaded effect for later simple modifications
linEffect = (struct ff_effect *)calloc(1, sizeof(struct ff_effect));
memcpy(linEffect, ffeffect, sizeof(struct ff_effect));
mEffectList[effect->_handle] = linEffect;
// Start playing the effect.
_start(effect->_handle);
}
else
{
#if (OIS_LINUX_JOYFF_DEBUG > 1)
cout << endl << "LinuxForceFeedback("<< mJoyStick << ") : Replacing effect : "
<< Effect::getEffectTypeName(effect->type) << endl;
#endif
// Keep same id/handle, as this is just an update in the device.
ffeffect->id = effect->_handle;
// Update effect in the device.
if (ioctl(mJoyStick, EVIOCSFF, ffeffect) == -1) {
OIS_EXCEPT(E_General, "Unknown error updating an effect->..");
}
// Update local linEffect for next time.
memcpy(linEffect, ffeffect, sizeof(struct ff_effect));
}
#if (OIS_LINUX_JOYFF_DEBUG > 1)
cout << "LinuxForceFeedback("<< mJoyStick
<< ") : Effect handle : " << effect->_handle << endl;
#endif
}
//--------------------------------------------------------------//
void LinuxForceFeedback::_stop( int handle) {
struct input_event stop;
stop.type = EV_FF;
stop.code = handle;
stop.value = 0;
#if (OIS_LINUX_JOYFF_DEBUG > 1)
cout << endl << "LinuxForceFeedback("<< mJoyStick
<< ") : Stopping effect with handle " << handle << endl;
#endif
if (write(mJoyStick, &stop, sizeof(stop)) != sizeof(stop)) {
OIS_EXCEPT(E_General, "Unknown error stopping effect->..");
}
}
//--------------------------------------------------------------//
void LinuxForceFeedback::_start( int handle) {
struct input_event play;
play.type = EV_FF;
play.code = handle;
play.value = 1; // Play once.
#if (OIS_LINUX_JOYFF_DEBUG > 1)
cout << endl << "LinuxForceFeedback("<< mJoyStick
<< ") : Starting effect with handle " << handle << endl;
#endif
if (write(mJoyStick, &play, sizeof(play)) != sizeof(play)) {
OIS_EXCEPT(E_General, "Unknown error playing effect->..");
}
}
//--------------------------------------------------------------//
void LinuxForceFeedback::_unload( int handle)
{
#if (OIS_LINUX_JOYFF_DEBUG > 1)
cout << endl << "LinuxForceFeedback("<< mJoyStick
<< ") : Removing effect with handle " << handle << endl;
#endif
if (ioctl(mJoyStick, EVIOCRMFF, handle) == -1) {
OIS_EXCEPT(E_General, "Unknown error removing effect->..");
}
}
/*
The zlib/libpng License
Copyright (c) 2005-2007 Phillip Castaneda (pjcast -- www.wreckedgames.com)
This software is provided 'as-is', without any express or implied warranty. In no event will
the authors be held liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose, including commercial
applications, and to alter it and redistribute it freely, subject to the following
restrictions:
1. The origin of this software must not be misrepresented; you must not claim that
you wrote the original software. If you use this software in a product,
an acknowledgment in the product documentation would be appreciated but is
not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "linux/LinuxInputManager.h"
#include "linux/LinuxKeyboard.h"
#include "linux/LinuxJoyStickEvents.h"
#include "linux/LinuxMouse.h"
#include "OISException.h"
#include <cstdlib>
using namespace OIS;
//--------------------------------------------------------------------------------//
LinuxInputManager::LinuxInputManager() : InputManager("X11InputManager")
{
window = 0;
//Default settings
grabMouse = true;
grabKeyboard = true;
hideMouse = true;
mGrabs = true;
useXRepeat = false;
keyboardUsed = mouseUsed = false;
//Setup our internal factories
mFactories.push_back(this);
}
//--------------------------------------------------------------------------------//
LinuxInputManager::~LinuxInputManager()
{
//Close all joysticks
LinuxJoyStick::_clearJoys(unusedJoyStickList);
}
//--------------------------------------------------------------------------------//
void LinuxInputManager::_initialize( ParamList &paramList )
{
_parseConfigSettings( paramList );
//Enumerate all devices attached
_enumerateDevices();
}
//--------------------------------------------------------------------------------//
void LinuxInputManager::_parseConfigSettings( ParamList &paramList )
{
ParamList::iterator i = paramList.find("WINDOW");
if( i == paramList.end() )
OIS_EXCEPT( E_InvalidParam, "LinuxInputManager >> No WINDOW!" );
//TODO 64 bit proof this little conversion xxx wip
window = strtoul(i->second.c_str(), 0, 10);
//--------- Keyboard Settings ------------//
i = paramList.find("XAutoRepeatOn");
if( i != paramList.end() )
if( i->second == "true" )
useXRepeat = true;
i = paramList.find("x11_keyboard_grab");
if( i != paramList.end() )
if( i->second == "false" )
grabKeyboard = false;
//--------- Mouse Settings ------------//
i = paramList.find("x11_mouse_grab");
if( i != paramList.end() )
if( i->second == "false" )
grabMouse = false;
i = paramList.find("x11_mouse_hide");
if( i != paramList.end() )
if( i->second == "false" )
hideMouse = false;
}
//--------------------------------------------------------------------------------//
void LinuxInputManager::_enumerateDevices()
{
//Enumerate all attached devices
unusedJoyStickList = LinuxJoyStick::_scanJoys();
joySticks = unusedJoyStickList.size();
}
//----------------------------------------------------------------------------//
DeviceList LinuxInputManager::freeDeviceList()
{
DeviceList ret;
if( keyboardUsed == false )
ret.insert(std::make_pair(OISKeyboard, mInputSystemName));
if( mouseUsed == false )
ret.insert(std::make_pair(OISMouse, mInputSystemName));
for(JoyStickInfoList::iterator i = unusedJoyStickList.begin(); i != unusedJoyStickList.end(); ++i)
ret.insert(std::make_pair(OISJoyStick, i->vendor));
return ret;
}
//----------------------------------------------------------------------------//
int LinuxInputManager::totalDevices(Type iType)
{
switch(iType)
{
case OISKeyboard: return 1;
case OISMouse: return 1;
case OISJoyStick: return joySticks;
default: return 0;
}
}
//----------------------------------------------------------------------------//
int LinuxInputManager::freeDevices(Type iType)
{
switch(iType)
{
case OISKeyboard: return keyboardUsed ? 0 : 1;
case OISMouse: return mouseUsed ? 0 : 1;
case OISJoyStick: return (int)unusedJoyStickList.size();
default: return 0;
}
}
//----------------------------------------------------------------------------//
bool LinuxInputManager::vendorExist(Type iType, const std::string & vendor)
{
if( (iType == OISKeyboard || iType == OISMouse) && vendor == mInputSystemName )
{
return true;
}
else if( iType == OISJoyStick )
{
for(JoyStickInfoList::iterator i = unusedJoyStickList.begin(); i != unusedJoyStickList.end(); ++i)
if(i->vendor == vendor)
return true;
}
return false;
}
//----------------------------------------------------------------------------//
Object* LinuxInputManager::createObject(InputManager *creator, Type iType, bool bufferMode, const std::string & vendor)
{
Object *obj = 0;
switch(iType)
{
case OISKeyboard:
{
if( keyboardUsed == false )
obj = new LinuxKeyboard(this, bufferMode, grabKeyboard, useXRepeat);
break;
}
case OISMouse:
{
if( mouseUsed == false )
obj = new LinuxMouse(this, bufferMode, grabMouse, hideMouse);
break;
}
case OISJoyStick:
{
for(JoyStickInfoList::iterator i = unusedJoyStickList.begin(); i != unusedJoyStickList.end(); ++i)
{
if(vendor == "" || i->vendor == vendor)
{
obj = new LinuxJoyStick(this, bufferMode, *i);
unusedJoyStickList.erase(i);
break;
}
}
break;
}
default:
break;
}
if( obj == 0 )
OIS_EXCEPT(E_InputDeviceNonExistant, "No devices match requested type.");
return obj;
}
//----------------------------------------------------------------------------//
void LinuxInputManager::destroyObject( Object* obj )
{
if( obj )
{
if( obj->type() == OISJoyStick )
{
unusedJoyStickList.push_back( ((LinuxJoyStick*)obj)->_getJoyInfo() );
}
delete obj;
}
}
/*
The zlib/libpng License
Copyright (c) 2005-2007 Phillip Castaneda (pjcast -- www.wreckedgames.com)
This software is provided 'as-is', without any express or implied warranty. In no event will
the authors be held liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose, including commercial
applications, and to alter it and redistribute it freely, subject to the following
restrictions:
1. The origin of this software must not be misrepresented; you must not claim that
you wrote the original software. If you use this software in a product,
an acknowledgment in the product documentation would be appreciated but is
not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "OISConfig.h"
#include "linux/LinuxJoyStickEvents.h"
#include "linux/LinuxInputManager.h"
#include "linux/LinuxForceFeedback.h"
#include "linux/EventHelpers.h"
#include "OISEvents.h"
#include "OISException.h"
#include <fcntl.h> //Needed to Open a file descriptor
#include <cassert>
#include <linux/input.h>
#include <sstream>
# include <iostream>
using namespace std;
using namespace OIS;
//#define OIS_LINUX_JOY_DEBUG
//-------------------------------------------------------------------//
LinuxJoyStick::LinuxJoyStick(InputManager* creator, bool buffered, const JoyStickInfo& js)
: JoyStick(js.vendor, buffered, js.devId, creator)
{
mJoyStick = js.joyFileD;
mState.mAxes.clear();
mState.mAxes.resize(js.axes);
mState.mButtons.clear();
mState.mButtons.resize(js.buttons);
mPOVs = js.hats;
mButtonMap = js.button_map;
mAxisMap = js.axis_map;
mRanges = js.axis_range;
ff_effect = 0;
}
//-------------------------------------------------------------------//
LinuxJoyStick::~LinuxJoyStick()
{
EventUtils::removeForceFeedback( &ff_effect );
}
//-------------------------------------------------------------------//
void LinuxJoyStick::_initialize()
{
//Clear old joy state
mState.mAxes.resize(mAxisMap.size());
mState.clear();
//This will create and new us a force feedback structure if it exists
EventUtils::enumerateForceFeedback( mJoyStick, &ff_effect );
if( mJoyStick == -1 )
OIS_EXCEPT(E_InputDeviceNonExistant, "LinuxJoyStick::_initialize() >> JoyStick Not Found!");
}
//-------------------------------------------------------------------//
void LinuxJoyStick::capture()
{
static const short POV_MASK[8] = {0,0,1,1,2,2,3,3};
//Used to determine if an axis has been changed and needs an event
bool axisMoved[32] = {false, false, false, false, false, false, false, false, false, false, false, false, false,
false, false, false, false, false, false, false, false, false, false, false, false, false,
false, false, false, false, false, false};
//We are in non blocking mode - we just read once, and try to fill up buffer
input_event js[JOY_BUFFERSIZE];
int ret = read(mJoyStick, &js, sizeof(struct input_event) * JOY_BUFFERSIZE);
if( ret <= 0 )
return;
//Determine how many whole events re read up
ret /= sizeof(struct input_event);
for(int i = 0; i < ret; ++i)
{
switch(js[i].type)
{
case EV_KEY: //Button
{
int button = mButtonMap[js[i].code];
#ifdef OIS_LINUX_JOY_DEBUG
cout << "\nButton Code: " << js[i].code << ", OIS Value: " << button << endl;
#endif
//Check to see whether push or released event...
if(js[i].value)
{
mState.mButtons[button] = true;
if( mBuffered && mListener )
if(!mListener->buttonPressed(JoyStickEvent(this,mState), button)) return;
}
else
{
mState.mButtons[button] = false;
if( mBuffered && mListener )
if(!mListener->buttonReleased(JoyStickEvent(this,mState), button)) return;
}
break;
}
case EV_ABS: //Absolute Axis
{
//A Stick (BrakeDefine is the highest possible Axis)
if( js[i].code <= ABS_BRAKE )
{
int axis = mAxisMap[js[i].code];
assert( axis < 32 && "Too many axes (Max supported is 32). Report this to OIS forums!" );
axisMoved[axis] = true;
//check for rescaling:
if( mRanges[axis].min == JoyStick::MIN_AXIS && mRanges[axis].max != JoyStick::MAX_AXIS )
{ //Scale is perfect
mState.mAxes[axis].abs = js[i].value;
}
else
{ //Rescale
float proportion = (float)(js[i].value-mRanges[axis].max)/(float)(mRanges[axis].min-mRanges[axis].max);
mState.mAxes[axis].abs = (int)(32767.0f - (65535.0f * proportion));
}
}
else if( js[i].code <= ABS_HAT3Y ) //A POV - Max four POVs allowed
{
//Normalise the POV to between 0-7
//Even is X Axis, Odd is Y Axis
unsigned char LinuxPovNumber = js[i].code - 16;
short OIS_POVIndex = POV_MASK[LinuxPovNumber];
//Handle X Axis first (Even) (left right)
if((LinuxPovNumber & 0x0001) == 0)
{
//Why do this? Because, we use a bit field, and when this axis is east,
//it can't possibly be west too. So clear out the two X axes, then refil
//it in with the new direction bit.
//Clear the East/West Bit Flags first
mState.mPOV[OIS_POVIndex].direction &= 0x11110011;
if( js[i].value == -1 ) //Left
mState.mPOV[OIS_POVIndex].direction |= Pov::West;
else if( js[i].value == 1 ) //Right
mState.mPOV[OIS_POVIndex].direction |= Pov::East;
}
//Handle Y Axis (Odd) (up down)
else
{
//Clear the North/South Bit Flags first
mState.mPOV[OIS_POVIndex].direction &= 0x11111100;
if( js[i].value == -1 ) //Up
mState.mPOV[OIS_POVIndex].direction |= Pov::North;
else if( js[i].value == 1 ) //Down
mState.mPOV[OIS_POVIndex].direction |= Pov::South;
}
if( mBuffered && mListener )
if( mListener->povMoved( JoyStickEvent(this,mState), OIS_POVIndex) == false )
return;
}
break;
}
case EV_REL: //Relative Axes (Do any joystick actually have a relative axis?)
#ifdef OIS_LINUX_JOY_DEBUG
cout << "\nWarning: Relatives axes not supported yet" << endl;
#endif
break;
default: break;
}
}
//All axes and POVs are combined into one movement per pair per captured frame
if( mBuffered && mListener )
{
for( int i = 0; i < 32; ++i )
if( axisMoved[i] )
if( mListener->axisMoved( JoyStickEvent(this,mState), i) == false )
return;
}
}
//-------------------------------------------------------------------//
void LinuxJoyStick::setBuffered(bool buffered)
{
if( buffered != mBuffered )
{
mBuffered = buffered;
_initialize();
}
}
//-------------------------------------------------------------------//
JoyStickInfo LinuxJoyStick::_getJoyInfo()
{
JoyStickInfo js;
js.devId = mDevID;
js.joyFileD = mJoyStick;
js.vendor = mVendor;
js.axes = (int)mState.mAxes.size();
js.buttons = (int)mState.mButtons.size();
js.hats = mPOVs;
js.button_map = mButtonMap;
js.axis_map = mAxisMap;
js.axis_range = mRanges;
return js;
}
//-------------------------------------------------------------------//
JoyStickInfoList LinuxJoyStick::_scanJoys()
{
JoyStickInfoList joys;
//Search through all of the event devices.. and identify which ones are joysticks
//xxx move this to InputManager, as it can also scan all other events
for(int i = 0; i < 64; ++i )
{
stringstream s;
s << "/dev/input/event" << i;
int fd = open( s.str().c_str(), O_RDWR |O_NONBLOCK );
if(fd == -1)
continue;
#ifdef OIS_LINUX_JOY_DEBUG
cout << "Opening " << s.str() << "..." << endl;
#endif
try
{
JoyStickInfo js;
if( EventUtils::isJoyStick(fd, js) )
{
joys.push_back(js);
#ifdef OIS_LINUX_JOY_DEBUG
cout << "=> Joystick added to list." << endl;
#endif
}
else
{
#ifdef OIS_LINUX_JOY_DEBUG
cout << "=> Not a joystick." << endl;
#endif
close(fd);
}
}
catch(...)
{
#ifdef OIS_LINUX_JOY_DEBUG
cout << "Exception caught!!" << endl;
#endif
close(fd);
}
}
return joys;
}
//-------------------------------------------------------------------//
void LinuxJoyStick::_clearJoys(JoyStickInfoList &joys)
{
for(JoyStickInfoList::iterator i = joys.begin(); i != joys.end(); ++i)
close(i->joyFileD);
joys.clear();
}
//-------------------------------------------------------------------//
Interface* LinuxJoyStick::queryInterface(Interface::IType type)
{
if( ff_effect && type == Interface::ForceFeedback )
return ff_effect;
return 0;
}
/*
The zlib/libpng License
Copyright (c) 2005-2007 Phillip Castaneda (pjcast -- www.wreckedgames.com)
This software is provided 'as-is', without any express or implied warranty. In no event will
the authors be held liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose, including commercial
applications, and to alter it and redistribute it freely, subject to the following
restrictions:
1. The origin of this software must not be misrepresented; you must not claim that
you wrote the original software. If you use this software in a product,
an acknowledgment in the product documentation would be appreciated but is
not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "linux/LinuxInputManager.h"
#include "linux/LinuxKeyboard.h"
#include "OISException.h"
#include "OISEvents.h"
#include <X11/keysym.h>
#include <X11/Xutil.h>
#include <cstring>
using namespace OIS;
#include <iostream>
//-------------------------------------------------------------------//
LinuxKeyboard::LinuxKeyboard(InputManager* creator, bool buffered, bool grab, bool useXRepeat)
: Keyboard(creator->inputSystemName(), buffered, 0, creator)
{
setlocale(LC_CTYPE, ""); //Set the locale to (hopefully) the users LANG UTF-8 Env var
display = 0;
window = 0;
grabKeyboard = grab;
keyFocusLost = false;
xAutoRepeat = useXRepeat;
oldXAutoRepeat = false;
//X Key Map to KeyCode
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_1, KC_1));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_2, KC_2));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_3, KC_3));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_4, KC_4));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_5, KC_5));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_6, KC_6));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_7, KC_7));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_8, KC_8));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_9, KC_9));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_0, KC_0));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_BackSpace, KC_BACK));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_minus, KC_MINUS));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_equal, KC_EQUALS));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_space, KC_SPACE));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_comma, KC_COMMA));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_period, KC_PERIOD));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_backslash, KC_BACKSLASH));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_slash, KC_SLASH));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_bracketleft, KC_LBRACKET));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_bracketright, KC_RBRACKET));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_Escape,KC_ESCAPE));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_Caps_Lock, KC_CAPITAL));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_Tab, KC_TAB));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_Return, KC_RETURN));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_Control_L, KC_LCONTROL));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_Control_R, KC_RCONTROL));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_colon, KC_COLON));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_semicolon, KC_SEMICOLON));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_apostrophe, KC_APOSTROPHE));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_grave, KC_GRAVE));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_b, KC_B));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_a, KC_A));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_c, KC_C));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_d, KC_D));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_e, KC_E));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_f, KC_F));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_g, KC_G));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_h, KC_H));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_i, KC_I));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_j, KC_J));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_k, KC_K));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_l, KC_L));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_m, KC_M));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_n, KC_N));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_o, KC_O));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_p, KC_P));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_q, KC_Q));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_r, KC_R));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_s, KC_S));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_t, KC_T));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_u, KC_U));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_v, KC_V));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_w, KC_W));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_x, KC_X));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_y, KC_Y));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_z, KC_Z));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_F1, KC_F1));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_F2, KC_F2));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_F3, KC_F3));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_F4, KC_F4));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_F5, KC_F5));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_F6, KC_F6));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_F7, KC_F7));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_F8, KC_F8));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_F9, KC_F9));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_F10, KC_F10));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_F11, KC_F11));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_F12, KC_F12));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_F13, KC_F13));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_F14, KC_F14));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_F15, KC_F15));
//Keypad
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_KP_0, KC_NUMPAD0));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_KP_1, KC_NUMPAD1));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_KP_2, KC_NUMPAD2));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_KP_3, KC_NUMPAD3));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_KP_4, KC_NUMPAD4));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_KP_5, KC_NUMPAD5));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_KP_6, KC_NUMPAD6));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_KP_7, KC_NUMPAD7));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_KP_8, KC_NUMPAD8));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_KP_9, KC_NUMPAD9));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_KP_Add, KC_ADD));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_KP_Subtract, KC_SUBTRACT));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_KP_Decimal, KC_DECIMAL));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_KP_Equal, KC_NUMPADEQUALS));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_KP_Divide, KC_DIVIDE));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_KP_Multiply, KC_MULTIPLY));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_KP_Enter, KC_NUMPADENTER));
//Keypad with numlock off
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_KP_Home, KC_NUMPAD7));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_KP_Up, KC_NUMPAD8));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_KP_Page_Up, KC_NUMPAD9));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_KP_Left, KC_NUMPAD4));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_KP_Begin, KC_NUMPAD5));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_KP_Right, KC_NUMPAD6));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_KP_End, KC_NUMPAD1));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_KP_Down, KC_NUMPAD2));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_KP_Page_Down, KC_NUMPAD3));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_KP_Insert, KC_NUMPAD0));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_KP_Delete, KC_DECIMAL));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_Up, KC_UP));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_Down, KC_DOWN));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_Left, KC_LEFT));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_Right, KC_RIGHT));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_Page_Up, KC_PGUP));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_Page_Down, KC_PGDOWN));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_Home, KC_HOME));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_End, KC_END));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_Num_Lock, KC_NUMLOCK));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_Print, KC_SYSRQ));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_Scroll_Lock, KC_SCROLL));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_Pause, KC_PAUSE));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_Shift_R, KC_RSHIFT));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_Shift_L, KC_LSHIFT));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_Alt_R, KC_RMENU));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_Alt_L, KC_LMENU));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_Insert, KC_INSERT));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_Delete, KC_DELETE));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_Super_L, KC_LWIN));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_Super_R, KC_RWIN));
keyConversion.insert(XtoOIS_KeyMap::value_type(XK_Menu, KC_APPS));
static_cast<LinuxInputManager*>(mCreator)->_setKeyboardUsed(true);
}
//-------------------------------------------------------------------//
void LinuxKeyboard::_initialize()
{
//Clear our keyboard state buffer
memset( &KeyBuffer, 0, 256 );
mModifiers = 0;
if( display ) XCloseDisplay(display);
display = 0;
window = static_cast<LinuxInputManager*>(mCreator)->_getWindow();
//Create our local X mListener connection
if( !(display = XOpenDisplay(0)) )
OIS_EXCEPT(E_General, "LinuxKeyboard::_initialize >> Error opening X!");
//Set it to recieve Input events
if( XSelectInput(display, window, KeyPressMask | KeyReleaseMask) == BadWindow )
OIS_EXCEPT(E_General, "LinuxKeyboard::_initialize: X error!");
if( grabKeyboard )
XGrabKeyboard(display,window,True,GrabModeAsync,GrabModeAsync,CurrentTime);
keyFocusLost = false;
if( xAutoRepeat == false )
{
//We do not want to blindly turn on autorepeat later when quiting if
//it was not on to begin with.. So, let us check and see first
XKeyboardState old;
XGetKeyboardControl( display, &old );
oldXAutoRepeat = false;
if( old.global_auto_repeat == AutoRepeatModeOn )
oldXAutoRepeat = true;
XAutoRepeatOff( display );
}
}
//-------------------------------------------------------------------//
LinuxKeyboard::~LinuxKeyboard()
{
if( display )
{
if( oldXAutoRepeat )
XAutoRepeatOn(display);
if( grabKeyboard )
XUngrabKeyboard(display, CurrentTime);
XCloseDisplay(display);
}
static_cast<LinuxInputManager*>(mCreator)->_setKeyboardUsed(true);
}
//-------------------------------------------------------------------//
unsigned int UTF8ToUTF32(unsigned char* buf)
{
unsigned char &FirstChar = buf[0];
if(FirstChar < 128)
return FirstChar;
unsigned int val = 0;
unsigned int len = 0;
if((FirstChar & 0xE0) == 0xC0) //2 Chars
{
len = 2;
val = FirstChar & 0x1F;
}
else if((FirstChar & 0xF0) == 0xE0) //3 Chars
{
len = 3;
val = FirstChar & 0x0F;
}
else if((FirstChar & 0xF8) == 0xF0) //4 Chars
{
len = 4;
val = FirstChar & 0x07;
}
else if((FirstChar & 0xFC) == 0xF8) //5 Chars
{
len = 5;
val = FirstChar & 0x03;
}
else // if((FirstChar & 0xFE) == 0xFC) //6 Chars
{
len = 6;
val = FirstChar & 0x01;
}
for(int i = 1; i < len; i++)
val = (val << 6) | (buf[i] & 0x3F);
return val;
}
//-------------------------------------------------------------------//
bool LinuxKeyboard::isKeyDown( KeyCode key ) const
{
return (KeyBuffer[key]);
}
//-------------------------------------------------------------------//
void LinuxKeyboard::capture()
{
KeySym key;
XEvent event;
LinuxInputManager* linMan = static_cast<LinuxInputManager*>(mCreator);
while( XPending(display) > 0 )
{
XNextEvent(display, &event);
if( KeyPress == event.type )
{
unsigned int character = 0;
if( mTextMode != Off )
{
unsigned char buffer[6] = {0,0,0,0,0,0};
XLookupString(&event.xkey, (char*)buffer, 6, &key, 0);
if( mTextMode == Unicode )
character = UTF8ToUTF32(buffer);
else if( mTextMode == Ascii)
character = buffer[0];
}
//Mask out the modifier states X11 sets and read again
event.xkey.state &= ~ShiftMask;
event.xkey.state &= ~LockMask;
XLookupString(&event.xkey, 0, 0,&key, 0);
_injectKeyDown(key, character);
//Just printing out some debugging info.. to verify all chars are mapped
//std::cout << "KEY PRESSED X=" << event.xkey.keycode;
//std::cout << "\n KeySym=" << key << std::endl;
//Check for Alt-Tab
if( event.xkey.state & Mod1Mask && key == XK_Tab )
linMan->_setGrabState(false);
}
else if( KeyRelease == event.type )
{
//Mask out the modifier states X sets.. or we will get improper values
event.xkey.state &= ~ShiftMask;
event.xkey.state &= ~LockMask;
//Else, it is a valid key release
XLookupString(&event.xkey,NULL,0,&key,NULL);
_injectKeyUp(key);
}
}
//If grabbing mode is on.. Handle focus lost/gained via Alt-Tab and mouse clicks
if( grabKeyboard )
{
if( linMan->_getGrabState() == false )
{
// are no longer grabbing
if( keyFocusLost == false )
{
//UnGrab KeyBoard
XUngrabKeyboard(display, CurrentTime);
keyFocusLost = true;
}
}
else
{
//We are grabbing - and regained focus
if( keyFocusLost == true )
{
//ReGrab KeyBoard
XGrabKeyboard(display, window, True, GrabModeAsync, GrabModeAsync, CurrentTime);
keyFocusLost = false;
}
}
}
}
//-------------------------------------------------------------------//
void LinuxKeyboard::setBuffered(bool buffered)
{
mBuffered = buffered;
}
//-------------------------------------------------------------------//
bool LinuxKeyboard::_injectKeyDown( KeySym key, int text )
{
KeyCode kc = keyConversion[key];
KeyBuffer[kc] = 1;
//Turn on modifier flags
if( kc == KC_LCONTROL || kc == KC_RCONTROL)
mModifiers |= Ctrl;
else if( kc == KC_LSHIFT || kc == KC_RSHIFT )
mModifiers |= Shift;
else if( kc == KC_LMENU || kc == KC_RMENU )
mModifiers |= Alt;
if( mBuffered && mListener )
return mListener->keyPressed(KeyEvent(this,kc,text));
return true;
}
//-------------------------------------------------------------------//
bool LinuxKeyboard::_injectKeyUp( KeySym key )
{
KeyCode kc = keyConversion[key];
KeyBuffer[kc] = 0;
//Turn off modifier flags
if( kc == KC_LCONTROL || kc == KC_RCONTROL)
mModifiers &= ~Ctrl;
else if( kc == KC_LSHIFT || kc == KC_RSHIFT )
mModifiers &= ~Shift;
else if( kc == KC_LMENU || kc == KC_RMENU )
mModifiers &= ~Alt;
if( mBuffered && mListener )
return mListener->keyReleased(KeyEvent(this, kc, 0));
return true;
}
//-------------------------------------------------------------------//
const std::string& LinuxKeyboard::getAsString( KeyCode kc )
{
mGetString = "Unknown";
char *temp = 0;
XtoOIS_KeyMap::iterator i = keyConversion.begin(),
e = keyConversion.end();
for( ; i != e; ++i )
{
if( i->second == kc )
{
temp = XKeysymToString(i->first);
if( temp )
mGetString = temp;
break;
}
}
return mGetString;
}
//-------------------------------------------------------------------//
void LinuxKeyboard::copyKeyStates( char keys[256] ) const
{
memcpy( keys, KeyBuffer, 256 );
}
/*
The zlib/libpng License
Copyright (c) 2005-2007 Phillip Castaneda (pjcast -- www.wreckedgames.com)
This software is provided 'as-is', without any express or implied warranty. In no event will
the authors be held liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose, including commercial
applications, and to alter it and redistribute it freely, subject to the following
restrictions:
1. The origin of this software must not be misrepresented; you must not claim that
you wrote the original software. If you use this software in a product,
an acknowledgment in the product documentation would be appreciated but is
not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered friosom any source distribution.
*/
#include "linux/LinuxMouse.h"
#include "linux/LinuxInputManager.h"
#include "OISException.h"
#include "OISEvents.h"
using namespace OIS;
//-------------------------------------------------------------------//
LinuxMouse::LinuxMouse(InputManager* creator, bool buffered, bool grab, bool hide)
: Mouse(creator->inputSystemName(), buffered, 0, creator)
{
display = 0;
window = 0;
cursor = 0;
grabMouse = grab;
hideMouse = hide;
static_cast<LinuxInputManager*>(mCreator)->_setMouseUsed(true);
}
//-------------------------------------------------------------------//
void LinuxMouse::_initialize()
{
//Clear old state
mState.clear();
mMoved = false;
mWarped = false;
//6 is just some random value... hardly ever would anyone have a window smaller than 6
oldXMouseX = oldXMouseY = 6;
oldXMouseZ = 0;
if( display ) XCloseDisplay(display);
display = 0;
window = static_cast<LinuxInputManager*>(mCreator)->_getWindow();
//Create our local X mListener connection
if( !(display = XOpenDisplay(0)) )
OIS_EXCEPT(E_General, "LinuxMouse::_initialize >> Error opening X!");
//Set it to recieve Mouse Input events
if( XSelectInput(display, window, ButtonPressMask | ButtonReleaseMask | PointerMotionMask) == BadWindow )
OIS_EXCEPT(E_General, "LinuxMouse::_initialize >> X error!");
//Warp mouse inside window
XWarpPointer(display,None,window,0,0,0,0, 6,6);
//Create a blank cursor:
Pixmap bm_no;
XColor black, dummy;
Colormap colormap;
static char no_data[] = { 0,0,0,0,0,0,0,0 };
colormap = DefaultColormap( display, DefaultScreen(display) );
XAllocNamedColor( display, colormap, "black", &black, &dummy );
bm_no = XCreateBitmapFromData( display, window, no_data, 8, 8 );
cursor = XCreatePixmapCursor( display, bm_no, bm_no, &black, &black, 0, 0 );
grab( grabMouse );
hide( hideMouse );
mouseFocusLost = false;
}
//-------------------------------------------------------------------//
LinuxMouse::~LinuxMouse()
{
if( display )
{
grab(false);
hide(false);
XFreeCursor(display, cursor);
XCloseDisplay(display);
}
static_cast<LinuxInputManager*>(mCreator)->_setMouseUsed(false);
}
//-------------------------------------------------------------------//
void LinuxMouse::setBuffered(bool buffered)
{
mBuffered = buffered;
}
//-------------------------------------------------------------------//
void LinuxMouse::capture()
{
//Clear out last frames values
mState.X.rel = 0;
mState.Y.rel = 0;
mState.Z.rel = 0;
_processXEvents();
mWarped = false;
if( mMoved == true )
{
if( mBuffered && mListener )
mListener->mouseMoved( MouseEvent( this, mState ) );
mMoved = false;
}
//Check for losing/gaining mouse grab focus (alt-tab, etc)
if( grabMouse )
{
if( static_cast<LinuxInputManager*>(mCreator)->_getGrabState() )
{
if( mouseFocusLost ) //We just regained mouse grab focus
{
grab( true );
hide( hideMouse );
mouseFocusLost = false;
}
}
else
{
if( mouseFocusLost == false ) //We just lost mouse grab focus
{
grab( false );
hide( false );
mouseFocusLost = true;
}
}
}
}
//-------------------------------------------------------------------//
void LinuxMouse::_processXEvents()
{
//X11 Button Events: 1=left 2=middle 3=right; Our Bit Postion: 1=Left 2=Right 3=Middle
char mask[4] = {0,1,4,2};
XEvent event;
//Poll x11 for events mouse events
while( XPending(display) > 0 )
{
XNextEvent(display, &event);
if( event.type == MotionNotify )
{ //Mouse moved
//Ignore out of bounds mouse if we just warped
if( mWarped )
{
if(event.xmotion.x < 5 || event.xmotion.x > mState.width - 5 ||
event.xmotion.y < 5 || event.xmotion.y > mState.height - 5)
continue;
}
//Compute this frames Relative X & Y motion
mState.X.rel = event.xmotion.x - oldXMouseX;
mState.Y.rel = event.xmotion.y - oldXMouseY;
//Store old values for next time to compute relative motion
oldXMouseX = event.xmotion.x;
oldXMouseY = event.xmotion.y;
mState.X.abs += mState.X.rel;
mState.Y.abs += mState.Y.rel;
//Check to see if we are grabbing the mouse to the window (requires clipping and warping)
if( grabMouse )
{
if( mState.X.abs < 0 )
mState.X.abs = 0;
else if( mState.X.abs > mState.width )
mState.X.abs = mState.width;
if( mState.Y.abs < 0 )
mState.Y.abs = 0;
else if( mState.Y.abs > mState.height )
mState.Y.abs = mState.height;
if( mouseFocusLost == false )
{
//Keep mouse in window (fudge factor)
if(event.xmotion.x < 5 || event.xmotion.x > mState.width - 5 ||
event.xmotion.y < 5 || event.xmotion.y > mState.height - 5 )
{
oldXMouseX = mState.width >> 1; //center x
oldXMouseY = mState.height >> 1; //center y
XWarpPointer(display, None, window, 0, 0, 0, 0, oldXMouseX, oldXMouseY);
mWarped = true;
}
}
}
mMoved = true;
}
else if( event.type == ButtonPress )
{ //Button down
static_cast<LinuxInputManager*>(mCreator)->_setGrabState(true);
if( event.xbutton.button < 4 )
{
mState.buttons |= mask[event.xbutton.button];
if( mBuffered && mListener )
if( mListener->mousePressed( MouseEvent( this, mState ),
(MouseButtonID)(mask[event.xbutton.button] >> 1)) == false )
return;
}
}
else if( event.type == ButtonRelease )
{ //Button up
if( event.xbutton.button < 4 )
{
mState.buttons &= ~mask[event.xbutton.button];
if( mBuffered && mListener )
if( mListener->mouseReleased( MouseEvent( this, mState ),
(MouseButtonID)(mask[event.xbutton.button] >> 1)) == false )
return;
}
//The Z axis gets pushed/released pair message (this is up)
else if( event.xbutton.button == 4 )
{
mState.Z.rel += 120;
mState.Z.abs += 120;
mMoved = true;
}
//The Z axis gets pushed/released pair message (this is down)
else if( event.xbutton.button == 5 )
{
mState.Z.rel -= 120;
mState.Z.abs -= 120;
mMoved = true;
}
}
}
}
//-------------------------------------------------------------------//
void LinuxMouse::grab(bool grab)
{
if( grab )
XGrabPointer(display, window, True, 0, GrabModeAsync, GrabModeAsync, window, None, CurrentTime);
else
XUngrabPointer(display, CurrentTime);
}
//-------------------------------------------------------------------//
void LinuxMouse::hide(bool hide)
{
if( hide )
XDefineCursor(display, window, cursor);
else
XUndefineCursor(display, window);
}
/*
The zlib/libpng License
Copyright (c) 2006 Phillip Castaneda
This software is provided 'as-is', without any express or implied warranty. In no event will
the authors be held liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose, including commercial
applications, and to alter it and redistribute it freely, subject to the following
restrictions:
1. The origin of this software must not be misrepresented; you must not claim that
you wrote the original software. If you use this software in a product,
an acknowledgment in the product documentation would be appreciated but is
not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "mac/MacHIDManager.h"
#include "mac/MacJoyStick.h"
#include "OISException.h"
#include "OISObject.h"
#include <iostream>
using namespace std;
using namespace OIS;
//------------------------------------------------------------------------------------------------------//
//------------------------------------------------------------------------------------------------------//
template<typename T>
T getDictionaryItemAsRef(CFDictionaryRef dict, const char* keyName)
{
return CFDictionaryGetValue(dict, OIS_CFString(keyName));
}
template<>
CFArrayRef getDictionaryItemAsRef(CFDictionaryRef dict, const char* keyName)
{
CFTypeRef temp = CFDictionaryGetValue(dict, OIS_CFString(keyName));
if(temp && CFGetTypeID(temp) == CFArrayGetTypeID())
return (CFArrayRef)temp;
else
return 0;
}
template<>
CFStringRef getDictionaryItemAsRef(CFDictionaryRef dict, const char* keyName)
{
CFTypeRef temp = CFDictionaryGetValue(dict, OIS_CFString(keyName));
if(temp && CFGetTypeID(temp) == CFStringGetTypeID())
return (CFStringRef)temp;
else
return 0;
}
template<>
CFNumberRef getDictionaryItemAsRef(CFDictionaryRef dict, const char* keyName)
{
CFTypeRef temp = CFDictionaryGetValue(dict, OIS_CFString(keyName));
if(temp && CFGetTypeID(temp) == CFNumberGetTypeID())
return (CFNumberRef)temp;
else
return 0;
}
//------------------------------------------------------------------------------------------------------//
//------------------------------------------------------------------------------------------------------//
template<typename T>
T getArrayItemAsRef(CFArrayRef array, CFIndex idx)
{
return CFArrayGetValueAtIndex(array, idx);
}
template<>
CFDictionaryRef getArrayItemAsRef(CFArrayRef array, CFIndex idx)
{
CFTypeRef temp = CFArrayGetValueAtIndex(array, idx);
if(temp && CFGetTypeID(temp) == CFDictionaryGetTypeID())
return (CFDictionaryRef)temp;
else
return 0;
}
//------------------------------------------------------------------------------------------------------//
int getInt32(CFNumberRef ref)
{
int r = 0;
if (r)
CFNumberGetValue(ref, kCFNumberIntType, &r);
return r;
}
//--------------------------------------------------------------------------------//
MacHIDManager::MacHIDManager()
{
}
//--------------------------------------------------------------------------------//
MacHIDManager::~MacHIDManager()
{
}
//------------------------------------------------------------------------------------------------------//
void MacHIDManager::initialize()
{
//Make the search more specific by adding usage flags
int usage = kHIDUsage_GD_Joystick;
int page = kHIDPage_GenericDesktop;
io_iterator_t iterator = lookUpDevices(usage, page);
if(iterator)
iterateAndOpenDevices(iterator);
//Doesn't support multiple usage flags, iterate twice
usage = kHIDUsage_GD_GamePad;
iterator = lookUpDevices(usage, page);
if(iterator)
iterateAndOpenDevices(iterator);
}
//------------------------------------------------------------------------------------------------------//
io_iterator_t MacHIDManager::lookUpDevices(int usage, int page)
{
CFMutableDictionaryRef deviceLookupMap = IOServiceMatching(kIOHIDDeviceKey);
if(!deviceLookupMap)
OIS_EXCEPT(E_General, "Could not setup HID device search parameters");
CFNumberRef usageRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usage);
CFNumberRef pageRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &page);
CFDictionarySetValue(deviceLookupMap, CFSTR(kIOHIDPrimaryUsageKey), usageRef);
CFDictionarySetValue(deviceLookupMap, CFSTR(kIOHIDPrimaryUsagePageKey), pageRef);
//IOServiceGetMatchingServices consumes the map so we do not have to release it ourself
io_iterator_t iterator = 0;
IOReturn result = IOServiceGetMatchingServices(kIOMasterPortDefault, deviceLookupMap, &iterator);
CFRelease(usageRef);
CFRelease(pageRef);
if(result == kIOReturnSuccess)
{
return iterator;
}
//TODO: Throw exception instead?
else
{
return 0;
}
}
//------------------------------------------------------------------------------------------------------//
void MacHIDManager::iterateAndOpenDevices(io_iterator_t iterator)
{
io_object_t hidDevice = 0;
while ((hidDevice = IOIteratorNext(iterator)) !=0)
{
//Get the current registry items property map
CFMutableDictionaryRef propertyMap = 0;
if (IORegistryEntryCreateCFProperties(hidDevice, &propertyMap, kCFAllocatorDefault, kNilOptions) == KERN_SUCCESS && propertyMap)
{
//Go through device to find all needed info
HidInfo* hid = enumerateDeviceProperties(propertyMap);
if(hid)
{
//todo - we need to hold an open interface so we do not have to enumerate again later
//should be able to watch for device removals also
// Testing opening / closing interface
IOCFPlugInInterface **pluginInterface = NULL;
SInt32 score = 0;
if (IOCreatePlugInInterfaceForService(hidDevice, kIOHIDDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &pluginInterface, &score) == kIOReturnSuccess)
{
IOHIDDeviceInterface **interface;
HRESULT pluginResult = (*pluginInterface)->QueryInterface(pluginInterface, CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID), (void **)&(interface));
if(pluginResult != S_OK)
OIS_EXCEPT(E_General, "Not able to create plugin interface");
IODestroyPlugInInterface(pluginInterface);
hid->interface = interface;
//Check for duplicates - some devices have multiple usage
if(std::find(mDeviceList.begin(), mDeviceList.end(), hid) == mDeviceList.end())
mDeviceList.push_back(hid);
}
}
}
}
IOObjectRelease(iterator);
}
//------------------------------------------------------------------------------------------------------//
HidInfo* MacHIDManager::enumerateDeviceProperties(CFMutableDictionaryRef propertyMap)
{
HidInfo* info = new HidInfo();
info->type = OISJoyStick;
CFStringRef str = getDictionaryItemAsRef<CFStringRef>(propertyMap, kIOHIDManufacturerKey);
if (str)
info->vendor = CFStringGetCStringPtr(str, CFStringGetSystemEncoding());
str = getDictionaryItemAsRef<CFStringRef>(propertyMap, kIOHIDProductKey);
if (str)
info->productKey = CFStringGetCStringPtr(str, CFStringGetSystemEncoding());
info->combinedKey = info->vendor + " " + info->productKey;
//Go through all items in this device (i.e. buttons, hats, sticks, axes, etc)
CFArrayRef array = getDictionaryItemAsRef<CFArrayRef>(propertyMap, kIOHIDElementKey);
if (array)
for (int i = 0; i < CFArrayGetCount(array); i++)
parseDeviceProperties(getArrayItemAsRef<CFDictionaryRef>(array, i));
return info;
}
//------------------------------------------------------------------------------------------------------//
void MacHIDManager::parseDeviceProperties(CFDictionaryRef properties)
{
if(!properties)
return;
CFArrayRef array = getDictionaryItemAsRef<CFArrayRef>(properties, kIOHIDElementKey);
if (array)
{
for (int i = 0; i < CFArrayGetCount(array); i++)
{
CFDictionaryRef element = getArrayItemAsRef<CFDictionaryRef>(array, i);
if (element)
{
if(getInt32(getDictionaryItemAsRef<CFNumberRef>(element, kIOHIDElementTypeKey)) == kIOHIDElementTypeCollection)
{ //Check if we need to recurse further intoi another collection
if(getInt32(getDictionaryItemAsRef<CFNumberRef>(element, kIOHIDElementUsagePageKey)) == kHIDPage_GenericDesktop)
parseDeviceProperties(element);
}
else
{
switch(getInt32(getDictionaryItemAsRef<CFNumberRef>(element, kIOHIDElementUsagePageKey)))
{
case kHIDPage_GenericDesktop:
switch(getInt32(getDictionaryItemAsRef<CFNumberRef>(element, kIOHIDElementUsageKey)))
{
case kHIDUsage_GD_Pointer:
cout << "\tkHIDUsage_GD_Pointer\n";
parseDevicePropertiesGroup(element);
break;
case kHIDUsage_GD_X:
case kHIDUsage_GD_Y:
case kHIDUsage_GD_Z:
case kHIDUsage_GD_Rx:
case kHIDUsage_GD_Ry:
case kHIDUsage_GD_Rz:
cout << "\tAxis\n";
break;
case kHIDUsage_GD_Slider:
case kHIDUsage_GD_Dial:
case kHIDUsage_GD_Wheel:
cout << "\tUnsupported kHIDUsage_GD_Wheel\n";
break;
case kHIDUsage_GD_Hatswitch:
cout << "\tUnsupported - kHIDUsage_GD_Hatswitch\n";
break;
}
break;
case kHIDPage_Button:
cout << "\tkHIDPage_Button\n";
break;
}
}
}
}
}
}
//------------------------------------------------------------------------------------------------------//
void MacHIDManager::parseDevicePropertiesGroup(CFDictionaryRef properties)
{
if(!properties)
return;
CFArrayRef array = getDictionaryItemAsRef<CFArrayRef>(properties, kIOHIDElementKey);
if(array)
{
for (int i = 0; i < CFArrayGetCount(array); i++)
{
CFDictionaryRef element = getArrayItemAsRef<CFDictionaryRef>(array, i);
if (element)
{
switch(getInt32(getDictionaryItemAsRef<CFNumberRef>(element, kIOHIDElementUsagePageKey)))
{
case kHIDPage_GenericDesktop:
switch(getInt32(getDictionaryItemAsRef<CFNumberRef>(element, kIOHIDElementUsageKey)))
{
case kHIDUsage_GD_X:
case kHIDUsage_GD_Y:
case kHIDUsage_GD_Z:
case kHIDUsage_GD_Rx:
case kHIDUsage_GD_Ry:
case kHIDUsage_GD_Rz:
cout << "\t\tAxis\n";
break;
case kHIDUsage_GD_Slider:
case kHIDUsage_GD_Dial:
case kHIDUsage_GD_Wheel:
cout << "\tUnsupported - kHIDUsage_GD_Wheel\n";
break;
case kHIDUsage_GD_Hatswitch:
cout << "\tUnsupported - kHIDUsage_GD_Hatswitch\n";
break;
}
break;
case kHIDPage_Button:
break;
}
}
}
}
}
//--------------------------------------------------------------------------------//
DeviceList MacHIDManager::freeDeviceList()
{
DeviceList ret;
HidInfoList::iterator it = mDeviceList.begin(), end = mDeviceList.end();
for(; it != end; ++it)
{
if((*it)->inUse == false)
ret.insert(std::make_pair((*it)->type, (*it)->combinedKey));
}
return ret;
}
//--------------------------------------------------------------------------------//
int MacHIDManager::totalDevices(Type iType)
{
int ret = 0;
HidInfoList::iterator it = mDeviceList.begin(), end = mDeviceList.end();
for(; it != end; ++it)
{
if((*it)->type == iType)
ret++;
}
return ret;
}
//--------------------------------------------------------------------------------//
int MacHIDManager::freeDevices(Type iType)
{
int ret = 0;
HidInfoList::iterator it = mDeviceList.begin(), end = mDeviceList.end();
for(; it != end; ++it)
{
if((*it)->inUse == false && (*it)->type == iType)
ret++;
}
return ret;
}
//--------------------------------------------------------------------------------//
bool MacHIDManager::vendorExist(Type iType, const std::string & vendor)
{
HidInfoList::iterator it = mDeviceList.begin(), end = mDeviceList.end();
for(; it != end; ++it)
{
if((*it)->type == iType && (*it)->combinedKey == vendor)
return true;
}
return false;
}
//--------------------------------------------------------------------------------//
Object* MacHIDManager::createObject(InputManager* creator, Type iType, bool bufferMode,
const std::string & vendor)
{
Object *obj = 0;
HidInfoList::iterator it = mDeviceList.begin(), end = mDeviceList.end();
for(; it != end; ++it)
{
if((*it)->inUse == false && (*it)->type == iType && (vendor == "" || (*it)->combinedKey == vendor))
{
switch(iType)
{
case OISJoyStick:
obj = new MacJoyStick(vendor, bufferMode, *it, creator);
(*it)->inUse = true;
return obj;
case OISTablet:
//Create MacTablet
break;
default:
break;
}
}
}
return obj;
}
//--------------------------------------------------------------------------------//
void MacHIDManager::destroyObject(Object* obj)
{
delete obj;
}
/*
The zlib/libpng License
Copyright (c) 2006 Chris Snyder
This software is provided 'as-is', without any express or implied warranty. In no event will
the authors be held liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose, including commercial
applications, and to alter it and redistribute it freely, subject to the following
restrictions:
1. The origin of this software must not be misrepresented; you must not claim that
you wrote the original software. If you use this software in a product,
an acknowledgment in the product documentation would be appreciated but is
not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "mac/MacHelpers.h"
#include "mac/MacKeyboard.h"
#include "mac/MacMouse.h"
#include "OISException.h"
#include <Carbon/Carbon.h>
using namespace OIS;
//-------------------------------------------------------------------//
OSStatus KeyDownWrapper( EventHandlerCallRef nextHandler,
EventRef theEvent,
void* callClass )
{
// TODO find a better way. This cast isn't very safe
if (callClass != NULL) {
((MacKeyboard*)callClass)->_keyDownCallback( theEvent );
// propagate the event down the chain
return CallNextEventHandler( nextHandler, theEvent );
}
else {
OIS_EXCEPT(E_General, "KeyDownWrapper >> Being called by something other than our event handler!");
return noErr;
}
}
//-------------------------------------------------------------------//
OSStatus KeyUpWrapper( EventHandlerCallRef nextHandler,
EventRef theEvent,
void* callClass )
{
if (callClass != NULL) {
((MacKeyboard*)callClass)->_keyUpCallback( theEvent );
// propagate the event down the chain
return CallNextEventHandler( nextHandler, theEvent );
}
else {
OIS_EXCEPT(E_General, "KeyUpWrapper >> Being called by something other than our event handler!");
return noErr;
}
}
//-------------------------------------------------------------------//
OSStatus KeyModWrapper( EventHandlerCallRef nextHandler,
EventRef theEvent,
void* callClass )
{
if (callClass != NULL) {
((MacKeyboard*)callClass)->_modChangeCallback( theEvent );
// propagate the event down the chain
return CallNextEventHandler( nextHandler, theEvent );
}
else {
OIS_EXCEPT(E_General, "KeyModWrapper >> Being called by something other than our event handler!");
return noErr;
}
}
/*
//-------------------------------------------------------------------//
OSStatus MouseMoveWrapper( EventHandlerCallRef nextHandler,
EventRef theEvent,
void* callClass )
{
if (callClass != NULL) {
((MacMouse*)callClass)->_mouseMoveCallback( theEvent );
// propagate the event down the chain
return CallNextEventHandler( nextHandler, theEvent );
}
else {
OIS_EXCEPT(E_General, "MouseMoveWrapper >> Being called by something other than our event handler!");
return noErr;
}
}
//-------------------------------------------------------------------//
OSStatus MouseScrollWrapper( EventHandlerCallRef nextHandler,
EventRef theEvent,
void* callClass )
{
if (callClass != NULL) {
((MacMouse*)callClass)->_mouseScrollCallback( theEvent );
// propagate the event down the chain
return CallNextEventHandler( nextHandler, theEvent );
}
else {
OIS_EXCEPT(E_General, "MouseScrollWrapper >> Being called by something other than our event handler!");
return noErr;
}
}
//-------------------------------------------------------------------//
OSStatus MouseButtonWrapper( EventHandlerCallRef nextHandler,
EventRef theEvent,
void* callClass )
{
if (callClass != NULL) {
((MacMouse*)callClass)->_mouseButtonCallback( theEvent );
// propagate the event down the chain
return CallNextEventHandler( nextHandler, theEvent );
}
else {
OIS_EXCEPT(E_General, "MouseButtonWrapper >> Being called by something other than our event handler!");
return noErr;
}
}
*/
//-------------------------------------------------------------------//
OSStatus MouseWrapper( EventHandlerCallRef nextHandler, EventRef theEvent, void* callClass )
{
if (callClass != NULL)
{
((MacMouse*)callClass)->_mouseCallback( theEvent );
// propagate the event down the chain
return CallNextEventHandler( nextHandler, theEvent );
}
else
OIS_EXCEPT(E_General, "MouseWrapper >> Being called by something other than our event handler!");
}
/*
The zlib/libpng License
Copyright (c) 2006 Chris Snyder
This software is provided 'as-is', without any express or implied warranty. In no event will
the authors be held liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose, including commercial
applications, and to alter it and redistribute it freely, subject to the following
restrictions:
1. The origin of this software must not be misrepresented; you must not claim that
you wrote the original software. If you use this software in a product,
an acknowledgment in the product documentation would be appreciated but is
not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "mac/MacInputManager.h"
#include "mac/MacKeyboard.h"
#include "mac/MacMouse.h"
#include "mac/MacHIDManager.h"
#include "OISException.h"
#include <Carbon/Carbon.h>
#include <iostream>
using namespace std;
using namespace OIS;
//--------------------------------------------------------------------------------//
MacInputManager::MacInputManager() : InputManager("Mac OSX Input Manager")
{
mHideMouse = true;
mUseRepeat = false;
mEventTargetRef = NULL;
mWindow = NULL;
keyboardUsed = mouseUsed = false;
//Setup our internal factories
mFactories.push_back(this);
mHIDManager = new MacHIDManager();
mFactories.push_back(mHIDManager);
}
//--------------------------------------------------------------------------------//
MacInputManager::~MacInputManager()
{
delete mHIDManager;
}
//--------------------------------------------------------------------------------//
void MacInputManager::_initialize( ParamList &paramList )
{
_parseConfigSettings( paramList );
//Enumerate all devices attached
_enumerateDevices();
mHIDManager->initialize();
}
//--------------------------------------------------------------------------------//
void MacInputManager::_parseConfigSettings( ParamList &paramList )
{
// Some carbon apps are running in a window, however full screen apps
// do not have a window, so we need to account for that too.
ParamList::iterator i = paramList.find("WINDOW");
if(i != paramList.end())
{
mWindow = (WindowRef)strtoul(i->second.c_str(), 0, 10);
if(mWindow == 0)
{
mWindow = NULL;
mEventTargetRef = GetApplicationEventTarget();
}
else
{
//mEventTargetRef = GetWindowEventTarget(mWindow);
mEventTargetRef = GetApplicationEventTarget();
}
}
else
{
// else get the main active window.. user might not have access to it through some
// graphics libraries, if that fails then try at the application level.
mWindow = ActiveNonFloatingWindow();
if(mWindow == NULL)
{
mEventTargetRef = GetApplicationEventTarget();
}
else
{
//mEventTargetRef = GetWindowEventTarget(mWindow);
mEventTargetRef = GetApplicationEventTarget();
}
}
if(mEventTargetRef == NULL)
OIS_EXCEPT( E_General, "MacInputManager::_parseConfigSettings >> Unable to find a window or event target" );
// Keyboard
if(paramList.find("MacAutoRepeatOn") != paramList.end())
{
if(paramList.find("MacAutoRepeatOn")->second == "true")
{
mUseRepeat = true;
}
}
}
//--------------------------------------------------------------------------------//
void MacInputManager::_enumerateDevices()
{
}
//--------------------------------------------------------------------------------//
DeviceList MacInputManager::freeDeviceList()
{
DeviceList ret;
if( keyboardUsed == false )
ret.insert(std::make_pair(OISKeyboard, mInputSystemName));
if( mouseUsed == false )
ret.insert(std::make_pair(OISMouse, mInputSystemName));
return ret;
}
//--------------------------------------------------------------------------------//
int MacInputManager::totalDevices(Type iType)
{
switch(iType)
{
case OISKeyboard: return 1;
case OISMouse: return 1;
default: return 0;
}
}
//--------------------------------------------------------------------------------//
int MacInputManager::freeDevices(Type iType)
{
switch(iType)
{
case OISKeyboard: return keyboardUsed ? 0 : 1;
case OISMouse: return mouseUsed ? 0 : 1;
default: return 0;
}
}
//--------------------------------------------------------------------------------//
bool MacInputManager::vendorExist(Type iType, const std::string & vendor)
{
if( (iType == OISKeyboard || iType == OISMouse) && vendor == mInputSystemName )
return true;
return false;
}
//--------------------------------------------------------------------------------//
Object* MacInputManager::createObject(InputManager* creator, Type iType, bool bufferMode,
const std::string & vendor)
{
Object *obj = 0;
switch(iType)
{
case OISKeyboard:
{
if( keyboardUsed == false )
obj = new MacKeyboard(this, bufferMode, mUseRepeat);
break;
}
case OISMouse:
{
if( mouseUsed == false )
obj = new MacMouse(this, bufferMode);
break;
}
default:
{
obj = mHIDManager->createObject(creator, iType, bufferMode, vendor);
break;
}
}
if( obj == 0 )
OIS_EXCEPT(E_InputDeviceNonExistant, "No devices match requested type.");
return obj;
}
//--------------------------------------------------------------------------------//
void MacInputManager::destroyObject(Object* obj)
{
delete obj;
}
/*
The zlib/libpng License
Copyright (c) 2005-2007 Phillip Castaneda (pjcast -- www.wreckedgames.com)
This software is provided 'as-is', without any express or implied warranty. In no event will
the authors be held liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose, including commercial
applications, and to alter it and redistribute it freely, subject to the following
restrictions:
1. The origin of this software must not be misrepresented; you must not claim that
you wrote the original software. If you use this software in a product,
an acknowledgment in the product documentation would be appreciated but is
not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "mac/MacJoyStick.h"
#include "mac/MacHIDManager.h"
#include "mac/MacInputManager.h"
#include "OISEvents.h"
#include "OISException.h"
#include <cassert>
using namespace OIS;
//--------------------------------------------------------------------------------------------------//
MacJoyStick::MacJoyStick(const std::string &vendor, bool buffered, HidInfo* info, InputManager* creator) :
JoyStick(vendor, buffered, 0 /*device id*/, creator), mInfo(info)
{
}
//--------------------------------------------------------------------------------------------------//
MacJoyStick::~MacJoyStick()
{
//TODO: check if the queue has been started first?
//(*mQueue)->stop(mQueue);
(*mQueue)->dispose(mQueue);
(*mQueue)->Release(mQueue);
//TODO: check if the interface has been opened first?
(*mInfo->interface)->close(mInfo->interface);
(*mInfo->interface)->Release(mInfo->interface);
}
//--------------------------------------------------------------------------------------------------//
void MacJoyStick::_initialize()
{
assert(mInfo && "Given HidInfo invalid");
assert(mInfo->interface && "Joystick interface invalid");
//TODO: Is this necessary?
//Clear old state
mState.mAxes.clear();
if ((*mInfo->interface)->open(mInfo->interface, 0) != KERN_SUCCESS)
OIS_EXCEPT(E_General, "MacJoyStick::_initialize() >> Could not initialize joy device!");
mState.clear();
_enumerateCookies();
mState.mButtons.resize(mInfo->numButtons);
mState.mAxes.resize(mInfo->numAxes);
mQueue = _createQueue();
}
//--------------------------------------------------------------------------------------------------//
void MacJoyStick::capture()
{
assert(mQueue && "Queue must be initialized before calling MacJoyStick::capture()");
AbsoluteTime zeroTime = {0,0};
IOHIDEventStruct event;
IOReturn result = (*mQueue)->getNextEvent(mQueue, &event, zeroTime, 0);
while(result == kIOReturnSuccess)
{
switch(event.type)
{
case kIOHIDElementTypeInput_Button:
{
std::vector<IOHIDElementCookie>::iterator buttonIt = std::find(mCookies.buttonCookies.begin(), mCookies.buttonCookies.end(), event.elementCookie);
int button = std::distance(mCookies.buttonCookies.begin(), buttonIt);
mState.mButtons[button] = (event.value == 1);
if(mBuffered && mListener)
{
if(event.value == 0)
mListener->buttonPressed(JoyStickEvent(this, mState), button);
else if(event.value == 1)
mListener->buttonReleased(JoyStickEvent(this, mState), button);
}
break;
}
case kIOHIDElementTypeInput_Misc:
//TODO: It's an axis! - kind of - for gamepads - or should this be a pov?
case kIOHIDElementTypeInput_Axis:
mState.mAxes[(int)event.elementCookie].abs = event.value;
if(mBuffered && mListener) mListener->axisMoved(JoyStickEvent(this, mState), (int)event.elementCookie);
break;
}
result = (*mQueue)->getNextEvent(mQueue, &event, zeroTime, 0);
}
}
//--------------------------------------------------------------------------------------------------//
void MacJoyStick::setBuffered(bool buffered)
{
mBuffered = buffered;
}
//--------------------------------------------------------------------------------------------------//
Interface* MacJoyStick::queryInterface(Interface::IType type)
{
//Thought about using covariant return type here.. however,
//some devices may allow LED light changing, or other interface stuff
//f( ff_device && type == Interface::ForceFeedback )
//return ff_device;
//else
return 0;
}
//--------------------------------------------------------------------------------------------------//
void MacJoyStick::_enumerateCookies()
{
assert(mInfo && "Given HidInfo invalid");
assert(mInfo->interface && "Joystick interface invalid");
CFTypeRef object;
long number;
IOHIDElementCookie cookie;
long usage;
long usagePage;
CFDictionaryRef element;
// Copy all elements, since we're grabbing most of the elements
// for this device anyway, and thus, it's faster to iterate them
// ourselves. When grabbing only one or two elements, a matching
// dictionary should be passed in here instead of NULL.
CFArrayRef elements;
IOReturn success = reinterpret_cast<IOHIDDeviceInterface122*>(*mInfo->interface)->copyMatchingElements(mInfo->interface, NULL, &elements);
if (success == kIOReturnSuccess)
{
const CFIndex numOfElements = CFArrayGetCount(elements);
for (CFIndex i = 0; i < numOfElements; ++i)
{
element = static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(elements, i));
//Get cookie
object = (CFDictionaryGetValue(element,
CFSTR(kIOHIDElementCookieKey)));
if (object == 0 || CFGetTypeID(object) != CFNumberGetTypeID())
continue;
if(!CFNumberGetValue((CFNumberRef) object, kCFNumberLongType,
&number))
continue;
cookie = (IOHIDElementCookie) number;
//Get usage
object = CFDictionaryGetValue(element,
CFSTR(kIOHIDElementUsageKey));
if (object == 0 || CFGetTypeID(object) != CFNumberGetTypeID())
continue;
if (!CFNumberGetValue((CFNumberRef) object, kCFNumberLongType,
&number))
continue;
usage = number;
//Get usage page
object = CFDictionaryGetValue(element,
CFSTR(kIOHIDElementUsagePageKey));
if (object == 0 || CFGetTypeID(object) != CFNumberGetTypeID())
continue;
if (!CFNumberGetValue((CFNumberRef) object, kCFNumberLongType,
&number))
continue;
usagePage = number;
switch(usagePage)
{
case kHIDPage_GenericDesktop:
switch(usage)
{
case kHIDUsage_GD_Pointer:
break;
case kHIDUsage_GD_X:
case kHIDUsage_GD_Y:
case kHIDUsage_GD_Z:
case kHIDUsage_GD_Rx:
case kHIDUsage_GD_Ry:
case kHIDUsage_GD_Rz:
mCookies.axisCookies.push_back(cookie);
break;
case kHIDUsage_GD_Slider:
case kHIDUsage_GD_Dial:
case kHIDUsage_GD_Wheel:
break;
case kHIDUsage_GD_Hatswitch:
break;
}
break;
case kHIDPage_Button:
mCookies.buttonCookies.push_back(cookie);
break;
}
}
mInfo->numButtons = mCookies.buttonCookies.size();
mInfo->numAxes = mCookies.axisCookies.size();
}
else
{
OIS_EXCEPT(E_General, "JoyStick elements could not be copied: copyMatchingElements failed with error: " + success);
}
}
//--------------------------------------------------------------------------------------------------//
IOHIDQueueInterface** MacJoyStick::_createQueue(unsigned int depth)
{
assert(mInfo && "Given HidInfo invalid");
assert(mInfo->interface && "Joystick interface invalid");
IOHIDQueueInterface** queue = (*mInfo->interface)->allocQueue(mInfo->interface);
if (queue)
{
//create the queue
IOReturn result = (*queue)->create(queue, 0, depth);
if(result == kIOReturnSuccess)
{
//add elements to the queue
std::vector<IOHIDElementCookie>::iterator it = mCookies.axisCookies.begin();
for(; it != mCookies.axisCookies.end(); ++it)
{
result = (*queue)->addElement(queue, (*it), 0);
}
it = mCookies.buttonCookies.begin();
for(; it != mCookies.buttonCookies.end(); ++it)
{
result = (*queue)->addElement(queue, (*it), 0);
}
//start data delivery to queue
result = (*queue)->start(queue);
if(result == kIOReturnSuccess)
{
return queue;
}
else
{
OIS_EXCEPT(E_General, "Queue could not be started.");
}
}
else
{
OIS_EXCEPT(E_General, "Queue could not be created.");
}
}
else
{
OIS_EXCEPT(E_General, "Queue allocation failed.");
}
}
/*
The zlib/libpng License
Copyright (c) 2006 Chris Snyder
This software is provided 'as-is', without any express or implied warranty. In no event will
the authors be held liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose, including commercial
applications, and to alter it and redistribute it freely, subject to the following
restrictions:
1. The origin of this software must not be misrepresented; you must not claim that
you wrote the original software. If you use this software in a product,
an acknowledgment in the product documentation would be appreciated but is
not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "mac/MacKeyboard.h"
#include "mac/MacInputManager.h"
#include "mac/MacHelpers.h"
#include "OISException.h"
#include "OISEvents.h"
#include <Carbon/Carbon.h>
#include <list>
#include <string>
const EventTypeSpec DownSpec[] = {{kEventClassKeyboard, kEventRawKeyDown}, //non - repeats
{kEventClassKeyboard, kEventRawKeyRepeat}}; //repeats
const EventTypeSpec UpSpec = {kEventClassKeyboard, kEventRawKeyUp},
ModSpec = {kEventClassKeyboard, kEventRawKeyModifiersChanged};
const EventTypeSpec AllSpecs[] = {{kEventClassKeyboard, kEventRawKeyDown},
{kEventClassKeyboard, kEventRawKeyRepeat},
{kEventClassKeyboard, kEventRawKeyUp},
{kEventClassKeyboard, kEventRawKeyModifiersChanged}};
using namespace OIS;
//-------------------------------------------------------------------//
MacKeyboard::MacKeyboard( InputManager* creator, bool buffered, bool repeat )
: Keyboard(creator->inputSystemName(), buffered, 0, creator)
{
keyDownEventRef = NULL;
keyUpEventRef = NULL;
keyModEventRef = NULL;
useRepeat = repeat;
// Get a so-called "Univeral procedure pointer" for our callback
keyDownUPP = NewEventHandlerUPP( KeyDownWrapper );
keyUpUPP = NewEventHandlerUPP( KeyUpWrapper );
keyModUPP = NewEventHandlerUPP( KeyModWrapper );
// populate the conversion map
populateKeyConversion();
static_cast<MacInputManager*>(mCreator)->_setKeyboardUsed(true);
}
//-------------------------------------------------------------------//
MacKeyboard::~MacKeyboard()
{
// Remove our handlers so that this instance doesn't get called
// after it is deleted
if (keyDownEventRef != NULL)
RemoveEventHandler(keyDownEventRef);
if (keyUpEventRef != NULL)
RemoveEventHandler(keyUpEventRef);
if (keyModEventRef != NULL)
RemoveEventHandler(keyModEventRef);
// dispose of our UPPs
DisposeEventHandlerUPP(keyDownUPP);
DisposeEventHandlerUPP(keyUpUPP);
DisposeEventHandlerUPP(keyModUPP);
//Free the input managers keyboard
static_cast<MacInputManager*>(mCreator)->_setKeyboardUsed(false);
}
//-------------------------------------------------------------------//
void MacKeyboard::_initialize()
{
EventTargetRef event = ((MacInputManager*)mCreator)->_getEventTarget();
memset( &KeyBuffer, 0, 256 );
mModifiers = 0;
prevModMask = 0;
// just in case this gets called after the first time.. better safe
if (keyDownEventRef != NULL)
RemoveEventHandler(keyDownEventRef);
if (keyUpEventRef != NULL)
RemoveEventHandler(keyUpEventRef);
if (keyModEventRef != NULL)
RemoveEventHandler(keyModEventRef);
keyDownEventRef = NULL;
keyUpEventRef = NULL;
keyModEventRef = NULL;
OSStatus status;
// send both elements of downspec array... second index is for repeat events
if ( useRepeat )
status = InstallEventHandler( event, keyDownUPP, 2, DownSpec, this, &keyDownEventRef );
else
status = InstallEventHandler( event, keyDownUPP, 1, DownSpec, this, &keyDownEventRef );
if (status != noErr)
OIS_EXCEPT( E_General, "MacKeyboard::_initialize >> Error loading KeyDown event handler" );
if (InstallEventHandler( event, keyUpUPP, 1, &UpSpec, this, &keyUpEventRef ) != noErr)
OIS_EXCEPT( E_General, "MacKeyboard::_initialize >> Error loading KeyUp event handler" );
if (InstallEventHandler( event, keyModUPP, 1, &ModSpec, this, &keyModEventRef ) != noErr )
OIS_EXCEPT( E_General, "MacKeyboard::_initialize >> Error loading Keymods event handler" );
}
//-------------------------------------------------------------------//
bool MacKeyboard::isKeyDown( KeyCode key ) const
{
return (bool)KeyBuffer[key];
}
//-------------------------------------------------------------------//
void MacKeyboard::capture()
{
// if not buffered just return, we update the unbuffered automatically
if ( !mBuffered || !mListener )
return;
//If the mListener returns false, that means that we are probably deleted...
//send no more events and just leave as the this pointer is invalid now...
bool ret = true;
// run through our event stack
eventStack::iterator cur_it;
for (cur_it = pendingEvents.begin(); cur_it != pendingEvents.end(); cur_it++)
{
if ( (*cur_it).Type == MAC_KEYDOWN || (*cur_it).Type == MAC_KEYREPEAT)
mListener->keyPressed( (*cur_it).Event );
else if ( (*cur_it).Type == MAC_KEYUP )
mListener->keyReleased( (*cur_it).Event );
}
pendingEvents.clear();
}
//-------------------------------------------------------------------//
std::string& MacKeyboard::getAsString( KeyCode key )
{
getString = "";
return getString;
}
//-------------------------------------------------------------------//
void MacKeyboard::setBuffered( bool buffered )
{
mBuffered = buffered;
}
#include <iostream>
//-------------------------------------------------------------------//
void MacKeyboard::_keyDownCallback( EventRef theEvent )
{
UInt32 virtualKey;
OSStatus status;
unsigned int time = (unsigned int)GetEventTime(theEvent);
status = GetEventParameter(theEvent,
'kcod', // get it in virtual keycode
typeUInt32, NULL, // desired return type
sizeof(UInt32), NULL, // bufsize
&virtualKey );
KeyCode kc = keyConversion[virtualKey];
// record what kind of text we should pass the KeyEvent
UniChar text[10];
char macChar;
// TODO clean this up
if (mTextMode == Unicode)
{
//get string size
UInt32 stringsize;
//status = GetEventParameter( theEvent, 'kuni', typeUnicodeText, NULL, 0, &stringsize, NULL);
//status = GetEventParameter( theEvent, 'kuni', typeUnicodeText, NULL, sizeof(UniChar)*10, NULL, &text );
status = GetEventParameter( theEvent, 'kuni', typeUnicodeText, NULL, sizeof(UniChar) * 10, &stringsize, &text );
std::cout << "String length: " << stringsize << std::endl;
//wstring unitext;
//for (int i=0;i<10;i++) unitext += (wchar_t)text[i];
//wcout << "Unicode out: " << unitext << endl;
if(stringsize > 0)
{
// for each unicode char, send an event
stringsize--; // no termination char
for ( int i = 0; i < stringsize; i++ )
{
injectEvent( kc, time, MAC_KEYDOWN, (unsigned int)text[i] );
}
}
}
else if (mTextMode == Ascii)
{
status = GetEventParameter( theEvent, 'kchr', typeChar, NULL, sizeof(char), NULL, &macChar );
injectEvent( kc, time, MAC_KEYDOWN, (unsigned int)macChar );
}
else
{
injectEvent( kc, time, MAC_KEYDOWN );
}
}
//-------------------------------------------------------------------//
void MacKeyboard::_keyUpCallback( EventRef theEvent )
{
UInt32 virtualKey;
OSStatus status;
status = GetEventParameter( theEvent, kEventParamKeyCode, typeUInt32,
NULL, sizeof(UInt32), NULL, &virtualKey );
KeyCode kc = keyConversion[virtualKey];
injectEvent( kc, (int)GetEventTime(theEvent), MAC_KEYUP );
}
//-------------------------------------------------------------------//
void MacKeyboard::_modChangeCallback( EventRef theEvent )
{
UInt32 mods;
OSStatus status;
status = GetEventParameter( theEvent, kEventParamKeyModifiers,
typeUInt32, NULL, sizeof(UInt32), NULL, &mods );
// find the changed bit
UInt32 change = prevModMask ^ mods;
MacEventType newstate = ((change & prevModMask) > 0) ? MAC_KEYUP : MAC_KEYDOWN;
unsigned int time = (int)GetEventTime( theEvent );
//cout << "preMask: " << hex << prevModMask << endl;
//cout << "ModMask: " << hex << mods << endl;
//cout << "Change: " << hex << (change & prevModMask) << endl << endl;
// TODO test modifiers on a full keyboard to check if different mask for left/right
switch (change)
{
case (shiftKey): // shift
mModifiers &= (newstate == MAC_KEYDOWN) ? Shift : ~Shift;
injectEvent( KC_LSHIFT, time, newstate );
//injectEvent( KC_RSHIFT, time, newstate );
break;
case (optionKey): // option (alt)
mModifiers &= (newstate == MAC_KEYDOWN) ? Alt : -Alt;
//injectEvent( KC_RMENU, time, newstate );
injectEvent( KC_LMENU, time, newstate );
break;
case (controlKey): // Ctrl
mModifiers += (newstate == MAC_KEYDOWN) ? Ctrl : -Ctrl;
//injectEvent( KC_RCONTROL, time, newstate );
injectEvent( KC_LCONTROL, time, newstate );
break;
case (cmdKey): // apple
//injectEvent( KC_RWIN, time, newstate );
injectEvent( KC_LWIN, time, newstate );
break;
case (kEventKeyModifierFnMask): // fn key
injectEvent( KC_APPS, time, newstate );
break;
case (kEventKeyModifierNumLockMask): // numlock
injectEvent( KC_NUMLOCK, time, newstate );
break;
case (alphaLock): // caps lock
injectEvent( KC_CAPITAL, time, newstate );
break;
}
prevModMask = mods;
}
//-------------------------------------------------------------------//
void MacKeyboard::injectEvent( KeyCode kc, unsigned int time, MacEventType type, unsigned int txt )
{
// set to 1 if this is either a keydown or repeat
KeyBuffer[kc] = ( type == MAC_KEYUP ) ? 0 : 1;
if ( mBuffered && mListener )
pendingEvents.push_back( MacKeyStackEvent( KeyEvent(this, kc, txt), type) );
}
//-------------------------------------------------------------------//
void MacKeyboard::copyKeyStates( char keys[256] ) const
{
memcpy( keys, KeyBuffer, 256 );
}
//-------------------------------------------------------------------//
void MacKeyboard::populateKeyConversion()
{
// TODO finish the key mapping
// Virtual Key Map to KeyCode
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x12, KC_1));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x13, KC_2));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x14, KC_3));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x15, KC_4));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x17, KC_5));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x16, KC_6));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x1A, KC_7));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x1C, KC_8));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x19, KC_9));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x1D, KC_0));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x33, KC_BACK)); // might be wrong
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x1B, KC_MINUS));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x18, KC_EQUALS));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x31, KC_SPACE));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x2B, KC_COMMA));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x2F, KC_PERIOD));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x2A, KC_BACKSLASH));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x2C, KC_SLASH));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x21, KC_LBRACKET));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x1E, KC_RBRACKET));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x35, KC_ESCAPE));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x39, KC_CAPITAL));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x30, KC_TAB));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x24, KC_RETURN)); // double check return/enter
//keyConversion.insert(VirtualtoOIS_KeyMap::value_type(XK_colon, KC_COLON)); // no colon?
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x29, KC_SEMICOLON));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x27, KC_APOSTROPHE));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x32, KC_GRAVE));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x0B, KC_B));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x00, KC_A));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x08, KC_C));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x02, KC_D));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x0E, KC_E));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x03, KC_F));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x05, KC_G));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x04, KC_H));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x22, KC_I));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x26, KC_J));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x28, KC_K));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x25, KC_L));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x2E, KC_M));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x2D, KC_N));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x1F, KC_O));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x23, KC_P));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x0C, KC_Q));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x0F, KC_R));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x01, KC_S));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x11, KC_T));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x20, KC_U));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x09, KC_V));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x0D, KC_W));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x07, KC_X));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x10, KC_Y));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x06, KC_Z));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x7A, KC_F1));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x78, KC_F2));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x63, KC_F3));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x76, KC_F4));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x60, KC_F5));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x61, KC_F6));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x62, KC_F7));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x64, KC_F8));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x65, KC_F9));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x6D, KC_F10));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x67, KC_F11));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x6F, KC_F12));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x69, KC_F13));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x6B, KC_F14));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x71, KC_F15));
//Keypad
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x52, KC_NUMPAD0));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x53, KC_NUMPAD1));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x54, KC_NUMPAD2));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x55, KC_NUMPAD3));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x56, KC_NUMPAD4));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x57, KC_NUMPAD5));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x58, KC_NUMPAD6));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x59, KC_NUMPAD7));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x5B, KC_NUMPAD8));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x5C, KC_NUMPAD9));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x45, KC_ADD));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x4E, KC_SUBTRACT));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x41, KC_DECIMAL));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x51, KC_NUMPADEQUALS));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x4B, KC_DIVIDE));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x43, KC_MULTIPLY));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x4C, KC_NUMPADENTER));
//Keypad with numlock off
//keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x73, KC_NUMPAD7)); // not sure of these
//keyConversion.insert(VirtualtoOIS_KeyMap::value_type(XK_KP_Up, KC_NUMPAD8)); // check on a non-laptop
//keyConversion.insert(VirtualtoOIS_KeyMap::value_type(XK_KP_Page_Up, KC_NUMPAD9));
//keyConversion.insert(VirtualtoOIS_KeyMap::value_type(XK_KP_Left, KC_NUMPAD4));
//keyConversion.insert(VirtualtoOIS_KeyMap::value_type(XK_KP_Begin, KC_NUMPAD5));
//keyConversion.insert(VirtualtoOIS_KeyMap::value_type(XK_KP_Right, KC_NUMPAD6));
//keyConversion.insert(VirtualtoOIS_KeyMap::value_type(XK_KP_End, KC_NUMPAD1));
//keyConversion.insert(VirtualtoOIS_KeyMap::value_type(XK_KP_Down, KC_NUMPAD2));
//keyConversion.insert(VirtualtoOIS_KeyMap::value_type(XK_KP_Page_Down, KC_NUMPAD3));
//keyConversion.insert(VirtualtoOIS_KeyMap::value_type(XK_KP_Insert, KC_NUMPAD0));
//keyConversion.insert(VirtualtoOIS_KeyMap::value_type(XK_KP_Delete, KC_DECIMAL));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x7E, KC_UP));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x7D, KC_DOWN));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x7B, KC_LEFT));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x7C, KC_RIGHT));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x74, KC_PGUP));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x79, KC_PGDOWN));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x73, KC_HOME));
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x77, KC_END));
//keyConversion.insert(VirtualtoOIS_KeyMap::value_type(XK_Print, KC_SYSRQ)); // ??
//keyConversion.insert(VirtualtoOIS_KeyMap::value_type(XK_Scroll_Lock, KC_SCROLL)); // ??
//keyConversion.insert(VirtualtoOIS_KeyMap::value_type(XK_Pause, KC_PAUSE)); // ??
//keyConversion.insert(VirtualtoOIS_KeyMap::value_type(XK_Insert, KC_INSERT)); // ??
keyConversion.insert(VirtualtoOIS_KeyMap::value_type(0x75, KC_DELETE)); // del under help key?
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment