1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2013 Laszlo KIS-ADAM (dfighter) <dfighter1985@gmail.com>
6 // Copyright (C) 2014-2020 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
8 // This program is free software: you can redistribute it and/or modify
9 // it under the terms of the GNU Affero General Public License as
10 // published by the Free Software Foundation, either version 3 of the
11 // License, or (at your option) any later version.
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 // GNU Affero General Public License for more details.
18 // You should have received a copy of the GNU Affero General Public License
19 // along with this program. If not, see <http://www.gnu.org/licenses/>.
25 #include "nel/misc/i_xml.h"
26 #include "nel/misc/file.h"
28 #include "nel/misc/xml_auto_ptr.h"
30 #include "input_handler_manager.h"
31 #include "interface_manager.h"
32 #include "../actions.h"
34 #include "../client_cfg.h"
35 #include "../motion/user_controls.h"
37 #include "../release.h"
39 #include "../r2/editor.h"
46 using namespace NLMISC
;
52 extern CActionsManager Actions
;
53 extern CActionsManager EditActions
;
56 H_AUTO_DECL ( RZ_Client_Pump_Events
)
58 CInputHandlerManager
* CInputHandlerManager::_Instance
= NULL
;
61 // ***************************************************************************
62 CInputHandlerManager::CInputHandlerManager()
65 _MouseButtonsState
= noButton
;
66 _MouseX
= _MouseY
= _MouseLastX
= _MouseLastY
= 0;
69 _SkipInterfaceManager
=false;
70 _RecoverFocusLost
= false;
72 inputHandler
.setListener( CInterfaceManager::getInstance() );
73 CGroupEditBox::setComboKeyHandler( this );
76 // ***************************************************************************
77 CInputHandlerManager::~CInputHandlerManager()
79 CGroupEditBox::setComboKeyHandler( NULL
);
82 // ********************************************************************************************
83 void CInputHandlerManager::releaseInstance()
92 // ***************************************************************************
93 void CInputHandlerManager::addToServer(NLMISC::CEventServer
* server
)
95 _EventServer
= server
;
98 server
->addListener(EventDestroyWindowId
, this);
99 server
->addListener(EventCloseWindowId
, this);
100 server
->addListener(EventSetFocusId
, this);
101 server
->addListener(EventDisplayChangeId
, this);
104 server
->addListener(EventMouseMoveId
, this);
105 server
->addListener(EventMouseDownId
, this);
106 server
->addListener(EventMouseUpId
, this);
107 server
->addListener(EventMouseWheelId
, this);
108 server
->addListener(EventMouseDblClkId
, this);
111 server
->addListener(EventStringId
, this);
112 server
->addListener(EventCharId
, this);
113 server
->addListener(EventKeyDownId
, this);
114 server
->addListener(EventKeyUpId
, this);
118 // ***************************************************************************
119 void CInputHandlerManager::release()
122 _EventServer
->removeListener(EventDestroyWindowId
, this);
123 _EventServer
->removeListener(EventCloseWindowId
, this);
124 _EventServer
->removeListener(EventSetFocusId
, this);
125 _EventServer
->removeListener(EventDisplayChangeId
, this);
128 _EventServer
->removeListener(EventMouseMoveId
, this);
129 _EventServer
->removeListener(EventMouseDownId
, this);
130 _EventServer
->removeListener(EventMouseUpId
, this);
131 _EventServer
->removeListener(EventMouseWheelId
, this);
132 _EventServer
->removeListener(EventMouseDblClkId
, this);
135 _EventServer
->removeListener(EventStringId
, this);
136 _EventServer
->removeListener(EventCharId
, this);
137 _EventServer
->removeListener(EventKeyDownId
, this);
138 _EventServer
->removeListener(EventKeyUpId
, this);
141 // ***************************************************************************
142 void CInputHandlerManager::operator ()(const NLMISC::CEvent
&event
)
144 HandleSystemCursorCapture(event
);
146 if (event
== EventDisplayChangeId
)
150 // Process message to InterfaceManager
151 CInterfaceManager
*pIM
= CInterfaceManager::getInstance();
153 if (event
==EventSetFocusId
)
155 CEventSetFocus
*pEvent
=(CEventSetFocus
*)&event
;
158 // Deactivate all keys
159 _MouseButtonsState
= noButton
;
162 if (!_SkipInterfaceManager
)
164 // if there was some control capturing the mouse, warn them that they lost the focus
165 inputHandler
.handleSetFocusEvent( event
);
166 UserControls
.stopFreeLook();
168 // be nice with other app : let the mouse reappear (useful in direct 3D mode with no hardware cursor)
169 CViewRenderer::getInstance()->getDriver()->showCursor(true);
170 // Driver->setSystemArrow();
174 _RecoverFocusLost
= true; // force to update mouse pos on next click or move
175 CViewRenderer::getInstance()->getDriver()->showCursor(IsMouseCursorHardware());
179 if(!_SkipInterfaceManager
)
182 if (R2::getEditor().isInitialized()
183 && (ClientCfg
.R2EDEnabled
|| R2::getEditor().getCurrentTool())
186 R2::getEditor().handleEvent(NLGUI::CEventDescriptorSetFocus(pEvent
->Get
));
191 FilteredEventServer
.postEvent( event
.clone() );
194 // If want to skip the interface Manager
195 if(_SkipInterfaceManager
)
197 // redirect to FilteredEventServer
198 FilteredEventServer
.postEvent( event
.clone() );
199 /* Invalidate Actions managers by simulate keyUp on all. Else can have this scenario:
200 - keyDown ("forward" action valide) before teleporting
201 - keyUp while teleporting (=> _SkipInterfaceManager==true)
202 - action still valide when teleported
204 EditActions
.releaseAllKeyNoRunning();
205 Actions
.releaseAllKeyNoRunning();
211 // **** Event Keyboard
212 if( event
== EventKeyDownId
||
213 event
== EventKeyUpId
||
214 event
== EventCharId
||
215 event
== EventStringId
)
217 // if not handled, post to Action Manager
218 if( !inputHandler
.handleKeyboardEvent( event
) )
220 // See if handled by editor
221 bool handled
= false;
222 if (R2::getEditor().isInitialized()
223 && (ClientCfg
.R2EDEnabled
|| R2::getEditor().getCurrentTool())
226 handled
= R2::getEditor().handleEvent(NLGUI::CEventDescriptorKey((const CEventKey
&) event
) );
230 // Event from the Keyboard (DOWN KEYS)
231 if(event
== EventKeyDownId
)
233 CEventKeyDown
* downEvent
=(CEventKeyDown
*)&event
;
234 if (!CWidgetManager::getInstance()->getCaptureKeyboard () || !EditActions
.keyPushed (*downEvent
))
235 Actions
.keyPushed (*downEvent
);
237 // Event from the Keyboard (UP KEYS)
238 else if(event
== EventKeyUpId
)
240 CEventKeyUp
* upEvent
=(CEventKeyUp
*)&event
;
241 EditActions
.keyReleased (*upEvent
);
242 Actions
.keyReleased (*upEvent
);
246 /* if handled, still post released key for the following bug:
247 - I press 'Z' to go 'forward' before entering chat
248 - I enter chat, keeping Z pressed
249 - I release 'Z' in chat (thus handled by pIM->handleEvent())
250 - the player continue running...
254 // don't send keydown to action managers
255 if(event
==EventKeyUpId
)
257 CEventKeyUp
* upEvent
=(CEventKeyUp
*)&event
;
258 EditActions
.keyReleased (*upEvent
);
259 Actions
.keyReleased (*upEvent
);
264 else if(CWidgetManager::getInstance()->getPointer() && _Focus
/* && CWidgetManager::getInstance()->isMouseHandlingEnabled() */ &&
265 ( event
== EventMouseMoveId
||
266 event
== EventMouseDownId
||
267 event
== EventMouseUpId
||
268 event
== EventMouseWheelId
||
269 event
== EventMouseDblClkId
)
274 CViewPointer
&rIP
= *static_cast<CViewPointer
*>(CWidgetManager::getInstance()->getPointer());
276 NLGUI::CEventDescriptorMouse eventDesc
;
279 rIP
.getPointerDispPos(x
, y
);
283 bool handled
= false;
286 static volatile bool doTest
= false;
288 if (!doTest
|| (doTest
&& CWidgetManager::getInstance()->isMouseHandlingEnabled()))
290 if (event
==EventMouseDownId
)
292 if (_RecoverFocusLost
)
294 handled
|= updateMousePos((CEventMouse
&)event
); // must update mouse pos here,
295 // because when app window focus is gained by a mouse click, this is
296 // the only place where we can retrieve mouse pos before a mouse move
297 _RecoverFocusLost
= false;
301 if (R2::getEditor().isInitialized()
302 && (R2::isEditionCurrent() || R2::getEditor().getCurrentTool()))
304 const NLMISC::CEventMouseDown
*mouseDownEvent
= static_cast<const NLMISC::CEventMouseDown
*>(&event
);
305 if (mouseDownEvent
->Button
& NLMISC::leftButton
)
307 eventDesc
.setEventTypeExtended(CEventDescriptorMouse::mouseleftdown
);
308 handled
|= R2::getEditor().handleEvent(eventDesc
);
310 if (mouseDownEvent
->Button
& NLMISC::rightButton
)
312 eventDesc
.setEventTypeExtended(CEventDescriptorMouse::mouserightdown
);
313 handled
|= R2::getEditor().handleEvent(eventDesc
);
317 handled
|= inputHandler
.handleMouseButtonDownEvent( event
);
320 else if (event
==EventMouseUpId
)
322 handled
|= inputHandler
.handleMouseButtonUpEvent( event
);
325 else if (event
== EventMouseDblClkId
)
327 // TODO: yoyo make it work if needed (for now, seems preferable to manage in each ActionHandler)
328 handled
|= inputHandler
.handleMouseDblClickEvent( event
);
331 else if(event
== EventMouseMoveId
)
333 handled
|= updateMousePos((CEventMouse
&)event
);
335 else if (event
== EventMouseWheelId
)
337 handled
|= inputHandler
.handleMouseWheelEvent( event
);
341 // if Event not handled, post to Action Manager
344 if (R2::getEditor().isInitialized()
345 && (R2::isEditionCurrent() || R2::getEditor().getCurrentTool()))
347 if (event
== EventMouseDownId
)
349 const NLMISC::CEventMouseDown
*mouseDownEvent
= static_cast<const NLMISC::CEventMouseDown
*>(&event
);
350 if (mouseDownEvent
->Button
& NLMISC::leftButton
)
352 eventDesc
.setEventTypeExtended(CEventDescriptorMouse::mouseleftdown
);
353 handled
|= R2::getEditor().handleEvent(eventDesc
);
355 if (mouseDownEvent
->Button
& NLMISC::rightButton
)
357 eventDesc
.setEventTypeExtended(CEventDescriptorMouse::mouserightdown
);
358 handled
|= R2::getEditor().handleEvent(eventDesc
);
361 else if (event
== EventMouseUpId
)
363 const NLMISC::CEventMouseUp
*mouseUpEvent
= static_cast<const NLMISC::CEventMouseUp
*>(&event
);
364 if (mouseUpEvent
->Button
& NLMISC::leftButton
)
366 eventDesc
.setEventTypeExtended(CEventDescriptorMouse::mouseleftup
);
367 handled
|= R2::getEditor().handleEvent(eventDesc
);
369 if (mouseUpEvent
->Button
& NLMISC::rightButton
)
371 eventDesc
.setEventTypeExtended(CEventDescriptorMouse::mouserightup
);
372 handled
|= R2::getEditor().handleEvent(eventDesc
);
375 else if (event
== EventMouseDblClkId
)
377 const NLMISC::CEventMouseDblClk
*mouseDblClkEvent
= static_cast<const NLMISC::CEventMouseDblClk
*>(&event
);
378 if (mouseDblClkEvent
->Button
& NLMISC::leftButton
)
380 eventDesc
.setEventTypeExtended(CEventDescriptorMouse::mouseleftdblclk
);
381 handled
|= R2::getEditor().handleEvent(eventDesc
);
383 if (mouseDblClkEvent
->Button
& NLMISC::rightButton
)
385 eventDesc
.setEventTypeExtended(CEventDescriptorMouse::mouserightdblclk
);
386 handled
|= R2::getEditor().handleEvent(eventDesc
);
391 if (event
== EventMouseWheelId
)
393 const NLMISC::CEventMouseWheel
*wheelEvent
= static_cast<const NLMISC::CEventMouseWheel
*>(&event
);
394 eventDesc
.setEventTypeExtended(CEventDescriptorMouse::mousewheel
);
395 eventDesc
.setWheel(wheelEvent
->Direction
? 1 : -1);
396 handled
= R2::getEditor().handleEvent(eventDesc
);
398 else if (event
== EventMouseMoveId
)
400 eventDesc
.setEventTypeExtended(CEventDescriptorMouse::mousemove
);
401 handled
= R2::getEditor().handleEvent(eventDesc
);
405 nlwarning("R2 unknown mouse event '%s'", event
.toString().c_str());
411 // post to Action Manager
412 FilteredEventServer
.postEvent(event
.clone());
419 FilteredEventServer
.postEvent( event
.clone() );
424 // ***************************************************************************
425 bool CInputHandlerManager::updateMousePos(NLMISC::CEventMouse
&event
)
427 if (!IsMouseFreeLook())
428 return inputHandler
.handleMouseMoveEvent( event
);
434 // ***************************************************************************
435 void CInputHandlerManager::CComboKey::init(const NLGUI::CEventDescriptorKey
&rDK
)
439 if( rDK
.getKeyCtrl() )
441 if( rDK
.getKeyShift() )
442 CtrlFlags
|= ShiftKey
;
443 if( rDK
.getKeyAlt() )
447 // ***************************************************************************
448 bool CInputHandlerManager::CComboKey::operator<(const CComboKey
&c
) const
453 return CtrlFlags
<c
.CtrlFlags
;
457 // ***************************************************************************
458 bool CInputHandlerManager::isComboKeyChat(const NLGUI::CEventDescriptorKey
&edk
) const
462 return _ComboKeyChat
.find(ckey
)!=_ComboKeyChat
.end();
466 // ***************************************************************************
467 bool CInputHandlerManager::readInputConfigFile(const std::string
& fileName
)
469 // parse the XML input config file to build the events to actions map
473 if (!file
.open(fileName
))
475 nlinfo(" could not open file %s",fileName
.c_str());
480 root
= read
.getRootNode();
483 catch (const Exception
&e
)
487 nlwarning ("CFormLoader: Error while loading the xml input file : %s", e
.what());
491 nlinfo ("no root element in xml file %s",fileName
.c_str());
494 //the root node name should be input_config
495 if (strcmp( (char*)root
->name
,"input_config") )
497 nlinfo("in a xml input config, the root node must be <input_config>");
501 //get all system nodes
502 xmlNodePtr cur
= root
->xmlChildrenNode
;
505 // Read all combo_key_chat setup.
506 if ( !strcmp((char*)cur
->name
,"combo_key_chat") )
507 parseComboKeyChat(cur
);
516 // ***************************************************************************
517 void CInputHandlerManager::pumpEvents()
519 H_AUTO_USE ( RZ_Client_Pump_Events
)
521 nlassert(_EventServer
);
523 // pump the src EventServer
524 _EventServer
->pump(true);
526 // pump the filtered ones
527 FilteredEventServer
.pump();
531 // ***************************************************************************
532 void CInputHandlerManager::pumpEventsNoIM()
534 nlassert(_EventServer
);
536 // Set Mode to skip InterfaceManager Handling
537 _SkipInterfaceManager
= true;
539 // pump the src EventServer
540 _EventServer
->pump(true);
542 // pump the filtered ones
543 FilteredEventServer
.pump(true);
546 _SkipInterfaceManager
= false;
550 // ***************************************************************************
551 void CInputHandlerManager::resetPos (sint x
, sint y
)
561 // ***************************************************************************
562 void CInputHandlerManager::parseComboKeyChat(xmlNodePtr cur
)
564 xmlNodePtr keySon
= cur
->xmlChildrenNode
;
568 if ( strcmp((char*)keySon
->name
,"key") )
570 keySon
= keySon
->next
;
577 for (uint i
= 0; i
< v
.size(); ++i
)
578 _ComboKeyChat
.insert(v
[i
]);
581 keySon
= keySon
->next
;
586 // ***************************************************************************
587 void CInputHandlerManager::parseKey(xmlNodePtr cur
, std::vector
<CComboKey
> &out
)
595 prop
= (char*) xmlGetProp( cur
, (xmlChar
*) "id" );
597 keyStr
= (const char*)prop
;
600 prop
= (char*) xmlGetProp( cur
, (xmlChar
*) "mod" );
602 flagStr
= (const char*)prop
;
607 // If an hexa code, translate
608 if( keyStr
.find("0x")!=string::npos
)
611 sscanf(keyStr
.c_str(), "%x", &value
);
612 comboKey
.Key
= (TKey
)value
;
614 // else translate from enum
617 comboKey
.Key
= CEventKey::getKeyFromString(keyStr
);
621 if(comboKey
.Key
==NLMISC::KeyCount
)
623 nlwarning("Unknown Key Id in xml input file: %s", keyStr
.c_str());
627 // Test All flags if match flagStr
628 for(uint i
=0;i
<8;i
++)
630 comboKey
.CtrlFlags
= i
;
632 // Test if Control match
633 if(comboKey
.CtrlFlags
& CComboKey::CtrlKey
)
635 // If don't find c or C, abort
636 if( flagStr
.find('c')== string::npos
&& flagStr
.find('C')== string::npos
)
641 // if find 'c', then abort
642 if( flagStr
.find('c')!= string::npos
)
646 // Test if Shift match
647 if(comboKey
.CtrlFlags
& CComboKey::ShiftKey
)
649 // If don't find s or S, abort
650 if( flagStr
.find('s')== string::npos
&& flagStr
.find('S')== string::npos
)
655 // if find 's', then abort
656 if( flagStr
.find('s')!= string::npos
)
661 if(comboKey
.CtrlFlags
& CComboKey::AltKey
)
663 // If don't find a or A, abort
664 if( flagStr
.find('a')== string::npos
&& flagStr
.find('A')== string::npos
)
669 // if find 'a', then abort
670 if( flagStr
.find('a')!= string::npos
)
674 // If pass, Insert this combo
675 out
.push_back(comboKey
);