关于Ogre自带输入系统OIS(Object-Oriented Input System)的源码分析
2012-03-25 18:08
441 查看
在看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创建,Win32下自然就是Win32InputManager了。
他调用了Win32InputManager下的初始化函数。
这两个函数很自然的想到是和平台相关的,具体的实现自然要到平台相关的InputManager中要,
如上就完成了对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 ); // 在缓冲模式下有效的函数,用来调用已经注册的监听者
从上面的代码可以看到在获取完数据后会调用_doMouseClick函数来处理鼠标事件,
在往后可以看到在所有都处理完成后会判断如果是缓冲模式的话,调用mouseMoved将鼠标事件传递下去。
在_doMouseClick函数中也和mouseMoved类似会做缓冲和非缓冲的判断。
鼠标,键盘,手柄的处理有相似的地方,就不做讨论了。
关于OIS的使用,我想如果理解了OIS的具体实现,要使用起来肯定很容易,在此就暂时不做讨论了。
下面的链接是一个OIS的鼠标和键盘的示例代码链接。
http://download.csdn.net/detail/love0_0xin/4171517
转载请注明
好像我看到有人称为 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_PLATFORM im = new SDLInputManager(); #elif defined OIS_WIN32_PLATFORM im = new Win32InputManager(); #elif defined OIS_XBOX_PLATFORM im = new XBoxInputManager(); #elif defined OIS_LINUX_PLATFORM im = new LinuxInputManager(); #elif defined OIS_APPLE_PLATFORM im = new MacInputManager(); #else OIS_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 Instance ParamList::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 device hr = 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 device obj->_initialize(); } catch(...) { //Somekind of error, cleanup and rethrow destroyInputObject(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 values mState.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 ignore if( FAILED(hr) ) return; } bool axesMoved = false; //Accumulate all axis movements for one axesMove message.. //Buttons are fired off as they are found for(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 for if( axesMoved ) { if( coopSetting & DISCL_NONEXCLUSIVE ) { //DirectInput provides us with meaningless values, so correct that POINT 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 window 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; //Do the move if( mListener && mBuffered ) mListener->mouseMoved( MouseEvent( this, mState ) ); } }
从上面的代码可以看到在获取完数据后会调用_doMouseClick函数来处理鼠标事件,
在往后可以看到在所有都处理完成后会判断如果是缓冲模式的话,调用mouseMoved将鼠标事件传递下去。
在_doMouseClick函数中也和mouseMoved类似会做缓冲和非缓冲的判断。
鼠标,键盘,手柄的处理有相似的地方,就不做讨论了。
关于OIS的使用,我想如果理解了OIS的具体实现,要使用起来肯定很容易,在此就暂时不做讨论了。
下面的链接是一个OIS的鼠标和键盘的示例代码链接。
http://download.csdn.net/detail/love0_0xin/4171517
转载请注明
相关文章推荐
- MapReduce 的格式输入----SequenceFileInputFormat ---源码分析
- 源码角度分析Android的事件输入系统(input system)及ANR原理分析
- Android输入系统源码分析
- input输入子系统源码部分函数分析
- linux 输入子系统(2)----简单实例分析系统结构(input_dev层)
- Android核心分析---inputAndroid输入系统之输入路径详解
- MR-2.输入格式(InputFormat)FileInputFormat源码分析
- java源码分析:从System类中获取系统关键属性信息
- Android系统SystemServer进程启动过程源码分析
- MapReduce的输入格式---KeyValueTextInputFormat---源码分析
- 2.输入系统驱动(2)-input系统框架层分析
- CI框架源码完全分析之核心文件(输入类)Input.php
- CI框架源码完全分析之核心文件(输入类)Input.php
- 系统调用入口函数源码分析system_call——X86_64
- input子系统的输入核心层源码分析
- CI框架源码完全分析之核心文件(输入类)Input.php
- ads7846驱动及android系统input输入子系统分析
- input子系统基础之按键4——输入核心层源码分析
- [开发优化] 系统自带短信程序源码部分分析
- 关于微信手机端IOS系统中input输入框无法输入的问题