关于Ogre自带输入系统OIS(Object-Oriented Input System)的源码分析
来源:互联网 发布:数据库课程设计实验 编辑:程序博客网 时间:2024/04/18 10:53
在看Ogre源码的时候注意到了这个开源的输入系统OIS,全称是Object-Oriented Input System。
好像我看到有人称为 Open Input System,不管叫什么总之还挺好用的。
OIS应该有其他平台的版本,但目前我只在Win32需要,所以其他平台的OIS版本不做讨论。
我使用的OIS版本是1.2.0, 现在应该已经有新的版本了,不过应该变化不会太大。
注意:
1. 我们此次的讨论和Ogre无任何关系,完全是直接对OIS的使用。
2. 关于OIS的手柄和力反馈部分不做讨论,有兴趣我之后会研究下。
OK! 让我们开始。。。
首先让我们看下OIS的整体目录结构
Extras部分不做讨论,貌似是支持了其他手柄,比如wii
前面说过OIS是区分平台的,所以可以看到目录结构中有个Win32的文件夹,下面放得就是Win32下使用的源代码。
而直接在Header Files下面的头文件和源文件均为平台通用的接口层,具体实现层就在类似Win32,Linux等文件夹下(我猜的,因为我没下载其他平台的版本)
下看下各个类之间的关系图
如上图, 可以看到所有Mouse,Keyboard,JoyStick都是继承自Object,具体平台的实现层其实是前面带Win32的,而接口正如前面说的是中间那3个类。
如上图,OIS的对输入的管理无疑是归InputManager管理了。
接下来看源代码,先从管理类InputManager开始
InputManager的类中有四个重要的函数,如下:
static InputManager* createInputSystem( ParamList ¶mList );
static void destroyInputSystem(InputManager* manager);
Object* createInputObject( Type iType, bool bufferMode, const std::string &vendor = "");
void destroyInputObject( Object* obj );
前两个函数是用来创建和销毁OIS整个系统的,OIS在Win32上使用的是DX的DirectInput,具体DirectInput的使用方法在此不做讨论。
后两个函数是用来创建和销毁对象的(鼠标,键盘,手柄)。
InputManager.cpp
InputManager* InputManager::createInputSystem( ParamList ¶mList ){InputManager* im = 0;#if defined OIS_SDL_PLATFORMim = new SDLInputManager();#elif defined OIS_WIN32_PLATFORMim = new Win32InputManager();#elif defined OIS_XBOX_PLATFORMim = new XBoxInputManager();#elif defined OIS_LINUX_PLATFORMim = new LinuxInputManager();#elif defined OIS_APPLE_PLATFORMim = new MacInputManager();#elseOIS_EXCEPT(E_General, "No platform library.. check build platform defines!");#endif try{im->_initialize(paramList);}catch(...){delete im;throw; //rethrow}return im;}
看上面的代码可以发现针对不通的平台会有不通的InputManager创建,Win32下自然就是Win32InputManager了。
他调用了Win32InputManager下的初始化函数。
Win32InputManager.cpp
void Win32InputManager::_initialize( ParamList ¶mList ){HINSTANCE hInst = 0;HRESULT hr;//TODO 64 bit proof this little conversion xxx wip//First of all, get the Windows Handle and InstanceParamList::iterator i = paramList.find("WINDOW");if( i == paramList.end() ) OIS_EXCEPT( E_InvalidParam, "Win32InputManager::Win32InputManager >> No HWND found!" );hWnd = (HWND)strtoul(i->second.c_str(), 0, 10);if( IsWindow(hWnd) == 0 )OIS_EXCEPT( E_General, "Win32InputManager::Win32InputManager >> The sent HWND is not valid!");hInst = GetModuleHandle(0);//Create the devicehr = DirectInput8Create( hInst, DIRECTINPUT_VERSION, IID_IDirectInput8, (VOID**)&mDirectInput, NULL ); if (FAILED(hr))OIS_EXCEPT( E_General, "Win32InputManager::Win32InputManager >> Not able to init DirectX8 Input!");//Ok, now we have DirectInput, parse whatever extra settings were sent to us_parseConfigSettings( paramList );_enumerateDevices();}看代码可以发现,是对DirectInput8的创建,然后设置了参数,然后去枚举设备了。
InputManager.cpp
Object* InputManager::createInputObject( Type iType, bool bufferMode, const std::string &vendor ){Object* obj = 0;FactoryList::iterator i = mFactories.begin(), e = mFactories.end();for( ; i != e; ++i){if( (*i)->freeDevices(iType) > 0 ){if( vendor == "" || (*i)->vendorExist(iType, vendor) ){obj = (*i)->createObject(this, iType, bufferMode, vendor);mFactoryObjects[obj] = (*i);break;}}}if(!obj)OIS_EXCEPT(E_InputDeviceNonExistant, "No devices match requested type.");try{//Intialize deviceobj->_initialize();}catch(...){//Somekind of error, cleanup and rethrowdestroyInputObject(obj);throw;}return obj;}从上面的代码可以看到其实没做什么,只是调用了createObject去创建Object,然后就初始化_initialize去了。
这两个函数很自然的想到是和平台相关的,具体的实现自然要到平台相关的InputManager中要,
Win32InputManager.cpp
Object* Win32InputManager::createObject(InputManager* creator, Type iType, bool bufferMode, const std::string & vendor){Object *obj = 0;switch(iType){case OISKeyboard: {if( keyboardUsed == false )obj = new Win32Keyboard(this, mDirectInput, bufferMode, kbSettings);break;}case OISMouse:{if( mouseUsed == false )obj = new Win32Mouse(this, mDirectInput, bufferMode, mouseSettings);break;}case OISJoyStick:{for(JoyStickInfoList::iterator i = unusedJoyStickList.begin(); i != unusedJoyStickList.end(); ++i){if(vendor == "" || i->vendor == vendor){obj = new Win32JoyStick(this, mDirectInput, bufferMode, joySettings, *i);unusedJoyStickList.erase(i);break;}}break;}default:break;}if( obj == 0 )OIS_EXCEPT(E_InputDeviceNonExistant, "No devices match requested type.");return obj;}到这就可以看到具体创建不通的设备的代码了。
如上就完成了对OIS以及各个设备的创建,关于销毁的部分就非常简单了,就不讨论了。
下面看下具体设备的代码。
先看下各个设备的父类也就是Object类,他里面的东西都是各个设备的通用属性,比如类型,名字,是否是缓冲模式等,有些函数是接口,需要各个设备自己实现。
接下来看看Mouse类和Win32Mouse类,根据上面InputManager和Win32InputManager的理解,这两个类肯定也是一个是对外的接口类,一个是具体实现类。
Mouse类很简单,三个函数
virtual void setEventCallback( MouseListener *mouseListener ) {mListener = mouseListener;}
MouseListener* getEventCallback() {return mListener;}
const MouseState& getMouseState() const { return mState; }
设置对鼠标的监听类,获得监听类指针以及获得鼠标的状态。
WIn32Mouse类同样简单,四个主要函数:
virtual void setBuffered(bool buffered); // 设置缓冲或者非缓冲模式
virtual void capture(); // 捕获鼠标,需要反复地调用
virtual void _initialize(); // 对鼠标的创建,看代码可以看到DirectInput的创建函数
bool _doMouseClick( int mouseButton, DIDEVICEOBJECTDATA& di ); // 在缓冲模式下有效的函数,用来调用已经注册的监听者
Win32Mouse.cpp
void Win32Mouse::capture(){//Clear old relative valuesmState.X.rel = mState.Y.rel = mState.Z.rel = 0;DIDEVICEOBJECTDATA diBuff[MOUSE_DX_BUFFERSIZE];DWORD entries = MOUSE_DX_BUFFERSIZE;HRESULT hr = mMouse->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), diBuff, &entries, 0 );if( hr != DI_OK ){hr = mMouse->Acquire();while( hr == DIERR_INPUTLOST ) hr = mMouse->Acquire();hr = mMouse->GetDeviceData( sizeof(DIDEVICEOBJECTDATA), diBuff, &entries, 0 );//Perhaps the user just tabbed away, and coop settings//are nonexclusive..so just ignoreif( FAILED(hr) )return;}bool axesMoved = false;//Accumulate all axis movements for one axesMove message..//Buttons are fired off as they are foundfor(unsigned int i = 0; i < entries; ++i ){switch( diBuff[i].dwOfs ){case DIMOFS_BUTTON0:if(!_doMouseClick(0, diBuff[i])) return;break;case DIMOFS_BUTTON1:if(!_doMouseClick(1, diBuff[i])) return;break;case DIMOFS_BUTTON2:if(!_doMouseClick(2, diBuff[i])) return;break;case DIMOFS_BUTTON3:if(!_doMouseClick(3, diBuff[i])) return;break;case DIMOFS_BUTTON4:if(!_doMouseClick(4, diBuff[i])) return;break;case DIMOFS_BUTTON5:if(!_doMouseClick(5, diBuff[i])) return;break;case DIMOFS_BUTTON6:if(!_doMouseClick(6, diBuff[i])) return;break;case DIMOFS_BUTTON7:if(!_doMouseClick(7, diBuff[i])) return;break;case DIMOFS_X:mState.X.rel += diBuff[i].dwData;axesMoved = true;break;case DIMOFS_Y:mState.Y.rel += diBuff[i].dwData;axesMoved = true;break;case DIMOFS_Z:mState.Z.rel += diBuff[i].dwData;axesMoved = true;break;default: break;} //end switch}//end forif( axesMoved ){if( coopSetting & DISCL_NONEXCLUSIVE ){//DirectInput provides us with meaningless values, so correct thatPOINT point;GetCursorPos(&point);ScreenToClient(mHwnd, &point);mState.X.abs = point.x;mState.Y.abs = point.y;}else{mState.X.abs += mState.X.rel;mState.Y.abs += mState.Y.rel;}mState.Z.abs += mState.Z.rel;//Clip values to windowif( 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;//Do the moveif( mListener && mBuffered )mListener->mouseMoved( MouseEvent( this, mState ) );}}
从上面的代码可以看到在获取完数据后会调用_doMouseClick函数来处理鼠标事件,
在往后可以看到在所有都处理完成后会判断如果是缓冲模式的话,调用mouseMoved将鼠标事件传递下去。
在_doMouseClick函数中也和mouseMoved类似会做缓冲和非缓冲的判断。
鼠标,键盘,手柄的处理有相似的地方,就不做讨论了。
关于OIS的使用,我想如果理解了OIS的具体实现,要使用起来肯定很容易,在此就暂时不做讨论了。
下面的链接是一个OIS的鼠标和键盘的示例代码链接。
http://download.csdn.net/detail/love0_0xin/4171517
转载请注明
- 关于Ogre自带输入系统OIS(Object-Oriented Input System)的源码分析
- 关于Ogre的OIS的一些资料
- OIS输入系统
- Object-Oriented System Development
- 在Ogre中使用OIS的两种模式(非缓冲输入和缓冲输入)
- OIS输入系统-1_OIS简介与使用----OIS几个重要的类的使用
- YII自带验证的源码分析
- input按键输入源码分析
- 关于系统自带的自动关机问题
- 关于系统自带的下拉刷新
- [开发优化] 系统自带短信程序源码部分分析
- Object-C 获得系统自带的字体
- redis object对象系统的源码分析
- Android事件输入系统(Input System)
- OIS输入
- 在WinCE中调用系统自带的输入面板
- Ogre + MFC + OIS
- javascript验证html5自带input type="date"用户输入日期过期的方法
- Rails 笔记(一)
- waitpid
- 开弓没有回头箭——我的程序员之路
- CODE::BLOCKS 使用手册 及 WIKI
- 一步步 学数据结构 之 三
- 关于Ogre自带输入系统OIS(Object-Oriented Input System)的源码分析
- java API chm html 1.5 1.6 中文版英文版 帮助文档
- OPERAND-SIZE和ADDRESS-SIZE属性
- Android学习第19课—XML文件解析
- 关于Wince进程外组件~ .
- win eclipse cdt安装
- memset()函数
- 第十六天博文
- QEMU翻译块(TB)分析