LCOV - code coverage report
Current view: top level - source/gui/ObjectTypes - CHotkeyPicker.cpp (source / functions) Hit Total Coverage
Test: 0 A.D. test coverage report Lines: 3 90 3.3 %
Date: 2023-01-19 00:18:29 Functions: 2 16 12.5 %

          Line data    Source code
       1             : /* Copyright (C) 2021 Wildfire Games.
       2             :  * This file is part of 0 A.D.
       3             :  *
       4             :  * 0 A.D. is free software: you can redistribute it and/or modify
       5             :  * it under the terms of the GNU General Public License as published by
       6             :  * the Free Software Foundation, either version 2 of the License, or
       7             :  * (at your option) any later version.
       8             :  *
       9             :  * 0 A.D. is distributed in the hope that it will be useful,
      10             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12             :  * GNU General Public License for more details.
      13             :  *
      14             :  * You should have received a copy of the GNU General Public License
      15             :  * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.
      16             :  */
      17             : 
      18             : #include "precompiled.h"
      19             : 
      20             : #include "CHotkeyPicker.h"
      21             : 
      22             : #include "gui/ObjectBases/IGUIObject.h"
      23             : #include "lib/timer.h"
      24             : #include "ps/CLogger.h"
      25             : #include "ps/Hotkey.h"
      26             : #include "ps/KeyName.h"
      27             : #include "scriptinterface/ScriptConversions.h"
      28             : 
      29             : 
      30           1 : const CStr CHotkeyPicker::EventNameCombination = "Combination";
      31           1 : const CStr CHotkeyPicker::EventNameKeyChange = "KeyChange";
      32             : 
      33             : // Don't send the scancode, JS doesn't care.
      34           0 : template<> void Script::ToJSVal(const ScriptRequest& rq, JS::MutableHandleValue ret, const CHotkeyPicker::Key& val)
      35             : {
      36           0 :     Script::ToJSVal(rq, ret, val.scancodeName);
      37           0 : }
      38             : 
      39             : // Unused, but JSVAL_VECTOR requires it.
      40           0 : template<> bool Script::FromJSVal(const ScriptRequest&, const JS::HandleValue, CHotkeyPicker::Key&)
      41             : {
      42           0 :     LOGWARNING("FromJSVal<CHotkeyPicker>: Not implemented");
      43           0 :     return false;
      44             : }
      45             : 
      46           0 : JSVAL_VECTOR(CHotkeyPicker::Key);
      47             : 
      48           0 : CHotkeyPicker::CHotkeyPicker(CGUI& pGUI) : IGUIObject(pGUI), m_TimeToCombination(this, "time_to_combination", 1.f)
      49             : {
      50             :     // 8 keys at the same time is probably more than we'll ever need.
      51           0 :     m_KeysPressed.reserve(8);
      52           0 : }
      53             : 
      54           0 : CHotkeyPicker::~CHotkeyPicker()
      55             : {
      56           0 : }
      57             : 
      58           0 : void CHotkeyPicker::FireEvent(const CStr& event)
      59             : {
      60           0 :     ScriptRequest rq(*m_pGUI.GetScriptInterface());
      61             : 
      62           0 :     JS::RootedValueArray<1> args(rq.cx);
      63           0 :     JS::RootedValue keys(rq.cx);
      64           0 :     Script::ToJSVal(rq, &keys, m_KeysPressed);
      65           0 :     args[0].set(keys);
      66           0 :     ScriptEvent(event, args);
      67           0 : }
      68             : 
      69           0 : void CHotkeyPicker::Tick()
      70             : {
      71           0 :     if (m_KeysPressed.size() == 0)
      72           0 :         return;
      73             : 
      74           0 :     double time = timer_Time();
      75           0 :     if (time - m_LastKeyChange < m_TimeToCombination)
      76           0 :         return;
      77             : 
      78           0 :     FireEvent(EventNameCombination);
      79             : 
      80           0 :     return;
      81             : }
      82             : 
      83           0 : void CHotkeyPicker::HandleMessage(SGUIMessage& Message)
      84             : {
      85           0 :     IGUIObject::HandleMessage(Message);
      86           0 :     switch (Message.type)
      87             :     {
      88           0 :     case GUIM_GOT_FOCUS:
      89             :     case GUIM_LOST_FOCUS:
      90             :     {
      91           0 :         m_KeysPressed.clear();
      92           0 :         m_LastKeyChange = timer_Time();
      93           0 :         break;
      94             :     }
      95           0 :     default:
      96           0 :         break;
      97             :     }
      98           0 : }
      99             : 
     100           0 : InReaction CHotkeyPicker::PreemptEvent(const SDL_Event_* ev)
     101             : {
     102           0 :     switch (ev->ev.type)
     103             :     {
     104             :     // Handle the same mouse events that hotkeys handle
     105           0 :     case SDL_MOUSEBUTTONDOWN:
     106             :     case SDL_MOUSEBUTTONUP:
     107             :     case SDL_MOUSEWHEEL:
     108             :     {
     109             :         SDL_Scancode scancode;
     110             : 
     111           0 :         if (ev->ev.type != SDL_MOUSEWHEEL)
     112             :         {
     113             :             // Wait a little bit -> this gets triggered when clicking on a button,
     114             :             // but after the button click is processed, thus immediately triggering...
     115           0 :             if (timer_Time()-m_LastKeyChange < 0.2)
     116           0 :                 return IN_HANDLED;
     117             :             // This is from hotkeyHandler - not sure what it does in all honesty.
     118           0 :             if(ev->ev.button.button >= SDL_BUTTON_X1)
     119           0 :                 scancode = static_cast<SDL_Scancode>(MOUSE_BASE + (int)ev->ev.button.button + 2);
     120             :             else
     121           0 :                 scancode = static_cast<SDL_Scancode>(MOUSE_BASE + (int)ev->ev.button.button);
     122             :         }
     123             :         else
     124             :         {
     125           0 :             if (ev->ev.wheel.y > 0)
     126           0 :                 scancode = static_cast<SDL_Scancode>(MOUSE_WHEELUP);
     127           0 :             else if (ev->ev.wheel.y < 0)
     128           0 :                 scancode = static_cast<SDL_Scancode>(MOUSE_WHEELDOWN);
     129           0 :             else if (ev->ev.wheel.x > 0)
     130           0 :                 scancode = static_cast<SDL_Scancode>(MOUSE_X2);
     131           0 :             else if (ev->ev.wheel.x < 0)
     132           0 :                 scancode = static_cast<SDL_Scancode>(MOUSE_X1);
     133             :             else
     134           0 :                 return IN_HANDLED;
     135             :         }
     136             :         // Don't handle keys and mouse together except for modifiers.
     137           0 :         m_KeysPressed.erase(std::remove_if(m_KeysPressed.begin(), m_KeysPressed.end(), [](const Key& k) {
     138           0 :             return static_cast<int>(k.code) < UNIFIED_SHIFT || static_cast<int>(k.code) >= UNIFIED_LAST; } ), m_KeysPressed.end());
     139           0 :         m_KeysPressed.emplace_back(Key{scancode, FindScancodeName(scancode)});
     140             :         // For mouse events, assume we immediately want to return.
     141           0 :         FireEvent(EventNameCombination);
     142             : 
     143           0 :         return IN_HANDLED;
     144             :     }
     145           0 :     case SDL_KEYDOWN:
     146             :     case SDL_KEYUP:
     147             :     {
     148           0 :         SDL_Scancode scancode = ev->ev.key.keysym.scancode;
     149             : 
     150             :         // Don't handle caps-lock, it doesn't really work in-game and it's a weird hotkey.
     151           0 :         if (scancode == SDL_SCANCODE_CAPSLOCK)
     152           0 :             return IN_PASS;
     153             : 
     154           0 :         if (scancode == SDL_SCANCODE_LSHIFT || scancode == SDL_SCANCODE_RSHIFT)
     155           0 :             scancode = static_cast<SDL_Scancode>(UNIFIED_SHIFT);
     156           0 :         else if (scancode == SDL_SCANCODE_LCTRL || scancode == SDL_SCANCODE_RCTRL)
     157           0 :             scancode = static_cast<SDL_Scancode>(UNIFIED_CTRL);
     158           0 :         else if (scancode == SDL_SCANCODE_LALT || scancode == SDL_SCANCODE_RALT)
     159           0 :             scancode = static_cast<SDL_Scancode>(UNIFIED_ALT);
     160           0 :         else if (scancode == SDL_SCANCODE_LGUI || scancode == SDL_SCANCODE_RGUI)
     161           0 :             scancode = static_cast<SDL_Scancode>(UNIFIED_SUPER);
     162             : 
     163           0 :         if (ev->ev.type == SDL_KEYDOWN)
     164             :         {
     165             :             std::vector<Key>::const_iterator it = \
     166           0 :                 std::find_if(m_KeysPressed.begin(), m_KeysPressed.end(), [&scancode](Key& k) { return k.code == scancode; });
     167             :             // Can happen if multiple keys are mapped the same.
     168           0 :             if (it != m_KeysPressed.end())
     169           0 :                 return IN_HANDLED;
     170           0 :             m_KeysPressed.emplace_back(Key{scancode, FindScancodeName(scancode)});
     171             :         }
     172             :         else
     173             :         {
     174             :             std::vector<Key>::const_iterator it = \
     175           0 :                 std::find_if(m_KeysPressed.begin(), m_KeysPressed.end(), [&scancode](Key& k) { return k.code == scancode; });
     176             :             // Might happen if a key was down before this object is created.
     177           0 :             if (it == m_KeysPressed.end())
     178           0 :                 return IN_HANDLED;
     179           0 :             m_KeysPressed.erase(it);
     180             :         }
     181             : 
     182           0 :         FireEvent(EventNameKeyChange);
     183             : 
     184             :         // Register after-JS in case this takes a while (probably not but it doesn't hurt).
     185           0 :         m_LastKeyChange = timer_Time();
     186           0 :         return IN_HANDLED;
     187             :     }
     188           0 :     default:
     189             :     {
     190           0 :         return IN_PASS;
     191             :     }
     192             :     }
     193           3 : }

Generated by: LCOV version 1.13