Merge branch '138-toggle-free-look-with-hotkey' into main/gingo-test
[ryzomcore.git] / ryzom / client / src / interface_v3 / input_handler_manager.cpp
blob28420dff4ccfd23a8915cc256da750b4f3e4bf82
1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
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>
7 //
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/>.
23 #include "stdpch.h"
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"
33 #include "../input.h"
34 #include "../client_cfg.h"
35 #include "../motion/user_controls.h"
36 #include "../init.h"
37 #include "../release.h"
39 #include "../r2/editor.h"
42 ///////////
43 // USING //
44 ///////////
45 using namespace std;
46 using namespace NLMISC;
48 ////////////
49 // GLOBAL //
50 ////////////
52 extern CActionsManager Actions;
53 extern CActionsManager EditActions;
55 // Hierarchical timer
56 H_AUTO_DECL ( RZ_Client_Pump_Events )
58 CInputHandlerManager* CInputHandlerManager::_Instance = NULL;
61 // ***************************************************************************
62 CInputHandlerManager::CInputHandlerManager()
64 _EventServer= NULL;
65 _MouseButtonsState = noButton;
66 _MouseX = _MouseY = _MouseLastX = _MouseLastY = 0;
67 _Focus = true;
68 _MouseWheel = 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()
85 if( _Instance )
87 delete _Instance;
88 _Instance = NULL;
92 // ***************************************************************************
93 void CInputHandlerManager::addToServer(NLMISC::CEventServer * server)
95 _EventServer = server;
97 // System
98 server->addListener(EventDestroyWindowId, this);
99 server->addListener(EventCloseWindowId, this);
100 server->addListener(EventSetFocusId, this);
101 server->addListener(EventDisplayChangeId, this);
103 // Mouse
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);
110 // Keyboard
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()
121 // System
122 _EventServer->removeListener(EventDestroyWindowId, this);
123 _EventServer->removeListener(EventCloseWindowId, this);
124 _EventServer->removeListener(EventSetFocusId, this);
125 _EventServer->removeListener(EventDisplayChangeId, this);
127 // Mouse
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);
134 // Keyboard
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;
156 if (!pEvent->Get)
158 // Deactivate all keys
159 _MouseButtonsState = noButton;
160 _Focus = false;
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();
172 else
174 _RecoverFocusLost = true; // force to update mouse pos on next click or move
175 CViewRenderer::getInstance()->getDriver()->showCursor(IsMouseCursorHardware());
176 _Focus = true;
179 if(!_SkipInterfaceManager)
182 if (R2::getEditor().isInitialized()
183 && (ClientCfg.R2EDEnabled || R2::getEditor().getCurrentTool())
186 R2::getEditor().handleEvent(NLGUI::CEventDescriptorSetFocus(pEvent->Get));
190 // re-post
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();
206 return;
209 // **** Event Focus
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) );
228 if (!handled)
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...
252 else
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);
263 // **** Event Mouse
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;
278 sint32 x, y;
279 rIP.getPointerDispPos(x, y);
280 eventDesc.setX(x);
281 eventDesc.setY(y);
283 bool handled = false;
285 // button down ?
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;
299 if (!handled)
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 );
319 // button up ?
320 else if (event==EventMouseUpId)
322 handled |= inputHandler.handleMouseButtonUpEvent( event );
324 // db click ?
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 );
330 // mouse move?
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
342 if (!handled)
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);
389 else
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);
403 else
405 nlwarning("R2 unknown mouse event '%s'", event.toString().c_str());
409 if (!handled)
411 // post to Action Manager
412 FilteredEventServer.postEvent(event.clone());
416 // **** Others
417 else
419 FilteredEventServer.postEvent( event.clone() );
424 // ***************************************************************************
425 bool CInputHandlerManager::updateMousePos(NLMISC::CEventMouse &event)
427 if (!IsMouseFreeLook())
428 return inputHandler.handleMouseMoveEvent( event );
430 return false;
434 // ***************************************************************************
435 void CInputHandlerManager::CComboKey::init(const NLGUI::CEventDescriptorKey &rDK)
437 Key= rDK.getKey();
438 CtrlFlags= 0;
439 if( rDK.getKeyCtrl() )
440 CtrlFlags|= CtrlKey;
441 if( rDK.getKeyShift() )
442 CtrlFlags|= ShiftKey;
443 if( rDK.getKeyAlt() )
444 CtrlFlags|= AltKey;
447 // ***************************************************************************
448 bool CInputHandlerManager::CComboKey::operator<(const CComboKey &c) const
450 if(Key!=c.Key)
451 return Key<c.Key;
452 else
453 return CtrlFlags<c.CtrlFlags;
457 // ***************************************************************************
458 bool CInputHandlerManager::isComboKeyChat(const NLGUI::CEventDescriptorKey &edk) const
460 CComboKey ckey;
461 ckey.init(edk);
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
470 CIXml read;
471 CIFile file;
472 xmlNodePtr root;
473 if (!file.open(fileName))
475 nlinfo(" could not open file %s",fileName.c_str());
479 read.init (file);
480 root = read.getRootNode();
483 catch (const Exception &e)
485 // Output error
486 root = NULL;
487 nlwarning ("CFormLoader: Error while loading the xml input file : %s", e.what());
489 if (!root)
491 nlinfo ("no root element in xml file %s",fileName.c_str());
492 return false;
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>");
498 return false;
501 //get all system nodes
502 xmlNodePtr cur = root->xmlChildrenNode;
503 while (cur)
505 // Read all combo_key_chat setup.
506 if ( !strcmp((char*)cur->name,"combo_key_chat") )
507 parseComboKeyChat(cur);
509 cur= cur->next;
512 return true;
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);
545 // reset
546 _SkipInterfaceManager= false;
550 // ***************************************************************************
551 void CInputHandlerManager::resetPos (sint x, sint y)
553 _MouseLastX = x;
554 _MouseX = x;
555 _MouseY = y;
556 _MouseLastY = y;
561 // ***************************************************************************
562 void CInputHandlerManager::parseComboKeyChat(xmlNodePtr cur)
564 xmlNodePtr keySon = cur->xmlChildrenNode;
565 while (keySon)
567 // check its a key
568 if ( strcmp((char*)keySon->name,"key") )
570 keySon= keySon->next;
571 continue;
574 vector<CComboKey> v;
575 parseKey(keySon, v);
577 for (uint i = 0; i < v.size(); ++i)
578 _ComboKeyChat.insert(v[i]);
580 // next
581 keySon= keySon->next;
586 // ***************************************************************************
587 void CInputHandlerManager::parseKey(xmlNodePtr cur, std::vector<CComboKey> &out)
589 // read it
590 CXMLAutoPtr prop;
591 string keyStr;
592 string flagStr;
594 // read id
595 prop= (char*) xmlGetProp( cur, (xmlChar*) "id" );
596 if(prop)
597 keyStr= (const char*)prop;
599 // read flags
600 prop= (char*) xmlGetProp( cur, (xmlChar*) "mod" );
601 if(prop)
602 flagStr= (const char*)prop;
605 // Build the Key
606 CComboKey comboKey;
607 // If an hexa code, translate
608 if( keyStr.find("0x")!=string::npos )
610 sint value;
611 sscanf(keyStr.c_str(), "%x", &value);
612 comboKey.Key= (TKey)value;
614 // else translate from enum
615 else
617 comboKey.Key= CEventKey::getKeyFromString(keyStr);
620 // If valid key
621 if(comboKey.Key==NLMISC::KeyCount)
623 nlwarning("Unknown Key Id in xml input file: %s", keyStr.c_str());
625 else
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 )
637 continue;
639 else
641 // if find 'c', then abort
642 if( flagStr.find('c')!= string::npos )
643 continue;
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 )
651 continue;
653 else
655 // if find 's', then abort
656 if( flagStr.find('s')!= string::npos )
657 continue;
660 // Test if Alt match
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 )
665 continue;
667 else
669 // if find 'a', then abort
670 if( flagStr.find('a')!= string::npos )
671 continue;
674 // If pass, Insert this combo
675 out.push_back(comboKey);