GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/input/inputmanager.cpp Lines: 56 437 12.8 %
Date: 2020-06-04 Branches: 51 510 10.0 %

Line Branch Exec Source
1
/*
2
 *  The ManaPlus Client
3
 *  Copyright (C) 2012-2019  The ManaPlus Developers
4
 *
5
 *  This file is part of The ManaPlus Client.
6
 *
7
 *  This program is free software; you can redistribute it and/or modify
8
 *  it under the terms of the GNU General Public License as published by
9
 *  the Free Software Foundation; either version 2 of the License, or
10
 *  any later version.
11
 *
12
 *  This program is distributed in the hope that it will be useful,
13
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 *  GNU General Public License for more details.
16
 *
17
 *  You should have received a copy of the GNU General Public License
18
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
 */
20
21
#include "input/inputmanager.h"
22
23
#include "configuration.h"
24
#include "game.h"
25
#include "settings.h"
26
27
#include "being/localplayer.h"
28
#include "being/playerinfo.h"
29
30
#include "input/inputactionmap.h"
31
#include "input/inputactionsortfunctor.h"
32
#include "input/joystick.h"
33
#include "input/keyboardconfig.h"
34
#ifdef USE_SDL2
35
#include "input/touch/multitouchmanager.h"
36
#endif  // USE_SDL2
37
38
#include "input/touch/touchmanager.h"
39
40
#include "gui/gui.h"
41
#include "gui/sdlinput.h"
42
43
#include "gui/widgets/selldialog.h"
44
#include "gui/widgets/textfield.h"
45
46
#include "gui/widgets/tabs/setup_input.h"
47
48
#include "gui/windows/buydialog.h"
49
#include "gui/windows/chatwindow.h"
50
#include "gui/windows/inventorywindow.h"
51
#include "gui/windows/npcdialog.h"
52
#include "gui/windows/setupwindow.h"
53
#include "gui/windows/textdialog.h"
54
#include "gui/windows/tradewindow.h"
55
56
#include "utils/checkutils.h"
57
#include "utils/foreach.h"
58
#include "utils/gettext.h"
59
#include "utils/stdmove.h"
60
#include "utils/timer.h"
61
62
#include "gui/focushandler.h"
63
64
#include <algorithm>
65
66
#include "debug.h"
67
68
1
InputManager inputManager;
69
70
class QuitDialog;
71
72
extern QuitDialog *quitDialog;
73
74
namespace
75
{
76
    InputActionSortFunctor inputActionDataSorter;
77
}  // namespace
78
79
1
InputManager::InputManager() :
80
    mSetupInput(nullptr),
81
    mNewKeyIndex(InputAction::NO_VALUE),
82
    mMask(1),
83
    mNameMap(),
84
    mChatMap(),
85

1343
    mKey()
86
{
87
1
}
88
89
void InputManager::init() restrict2
90
{
91
#ifdef __SWITCH__
92
    SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, "1");
93
#endif
94
    for (size_t i = 0;
95
         i < CAST_SIZE(InputAction::TOTAL);
96
         i ++)
97
    {
98
        InputFunction &kf = mKey[i];
99
        mKeyStr[i].clear();
100
        for (size_t f = 0; f < inputFunctionSize; f ++)
101
        {
102
            InputItem &ki = kf.values[f];
103
            ki.type = InputType::UNKNOWN;
104
            ki.value = -1;
105
        }
106
    }
107
108
    mNewKeyIndex = InputAction::NO_VALUE;
109
110
    resetKeys();
111
    retrieve();
112
    update();
113
}
114
115
void InputManager::update()
116
{
117
    keyboard.update();
118
    if (joystick != nullptr)
119
        joystick->update();
120
}
121
122
void InputManager::retrieve() restrict2
123
{
124
    for (int i = 0; i < CAST_S32(InputAction::TOTAL); i ++)
125
    {
126
        const std::string &restrict cmd = inputActionData[i].chatCommand;
127
        if (!cmd.empty())
128
        {
129
            StringVect tokens;
130
            splitToStringVector(tokens, cmd, '|');
131
            FOR_EACH (StringVectCIter, it, tokens)
132
                mChatMap[*it] = i;
133
        }
134
#ifdef USE_SDL2
135
        const std::string cf = std::string("sdl2")
136
            + inputActionData[i].configField;
137
#else  // USE_SDL2
138
139
        const std::string cf = inputActionData[i].configField;
140
#endif  // USE_SDL2
141
142
        InputFunction &restrict kf = mKey[i];
143
        if (!cf.empty())
144
        {
145
            mNameMap[cf] = static_cast<InputActionT>(i);
146
            const std::string keyStr = config.getValue(cf, "");
147
            const size_t keyStrSize = keyStr.size();
148
            if (keyStr.empty())
149
            {
150
                updateKeyString(kf, i);
151
                continue;
152
            }
153
154
            StringVect keys;
155
            splitToStringVector(keys, keyStr, ',');
156
            unsigned int i2 = 0;
157
            for (StringVectCIter it = keys.begin(), it_end = keys.end();
158
                 it != it_end && i2 < inputFunctionSize; ++ it)
159
            {
160
                std::string keyStr2 = *it;
161
                if (keyStrSize < 2)
162
                    continue;
163
                InputTypeT type = InputType::KEYBOARD;
164
                if ((keyStr2[0] < '0' || keyStr2[0] > '9')
165
                    && keyStr2[0] != '-')
166
                {
167
                    switch (keyStr2[0])
168
                    {
169
                        case 'm':
170
                            type = InputType::MOUSE;
171
                            break;
172
                        case 'j':
173
                            type = InputType::JOYSTICK;
174
                            break;
175
                        default:
176
                            break;
177
                    }
178
                    keyStr2 = keyStr2.substr(1);
179
                }
180
                const int key = atoi(keyStr2.c_str());
181
                if (key >= -255 && key < SDLK_LAST)
182
                {
183
                    kf.values[i2] = InputItem(type, key);
184
                    i2 ++;
185
                }
186
            }
187
            for (; i2 < inputFunctionSize; i2 ++)
188
                kf.values[i2] = InputItem();
189
        }
190
        updateKeyString(kf, i);
191
    }
192
}
193
194
void InputManager::store() const restrict2
195
{
196
    for (int i = 0; i < CAST_S32(InputAction::TOTAL); i ++)
197
    {
198
#ifdef USE_SDL2
199
        const std::string cf = std::string("sdl2")
200
            + inputActionData[i].configField;
201
#else  // USE_SDL2
202
203
        const std::string cf = inputActionData[i].configField;
204
#endif  // USE_SDL2
205
206
        if (!cf.empty())
207
        {
208
            std::string keyStr;
209
            const InputFunction &restrict kf = mKey[i];
210
211
            for (size_t i2 = 0; i2 < inputFunctionSize; i2 ++)
212
            {
213
                const InputItem &restrict key = kf.values[i2];
214
                if (key.type != InputType::UNKNOWN)
215
                {
216
                    std::string tmp("k");
217
                    switch (key.type)
218
                    {
219
                        case InputType::MOUSE:
220
                            tmp = "m";
221
                            break;
222
                        case InputType::JOYSTICK:
223
                            tmp = "j";
224
                            break;
225
                        case InputType::KEYBOARD:
226
                        case InputType::UNKNOWN:
227
                        default:
228
                            break;
229
                    }
230
                    if (key.value != -1)
231
                    {
232
                        if (keyStr.empty())
233
                        {
234
                            keyStr.append(tmp).append(toString(key.value));
235
                        }
236
                        else
237
                        {
238
                            keyStr.append(strprintf(",%s%d",
239
                                tmp.c_str(), key.value));
240
                        }
241
                    }
242
                }
243
            }
244
            if (keyStr.empty())
245
                keyStr = "-1";
246
247
            config.setValue(cf, keyStr);
248
        }
249
    }
250
}
251
252
void InputManager::resetKey(const InputActionT i) restrict2
253
{
254
    InputFunction &restrict key = mKey[CAST_SIZE(i)];
255
    for (size_t i2 = 1; i2 < inputFunctionSize; i2 ++)
256
    {
257
        InputItem &restrict ki2 = key.values[i2];
258
        ki2.type = InputType::UNKNOWN;
259
        ki2.value = -1;
260
    }
261
    const InputActionData &restrict kd =
262
        inputActionData[CAST_SIZE(i)];
263
    InputItem &restrict val0 = key.values[0];
264
    val0.type = kd.defaultType1;
265
    InputItem &restrict val1 = key.values[1];
266
    val1.type = kd.defaultType2;
267
#ifdef USE_SDL2
268
    if (kd.defaultType1 == InputType::KEYBOARD)
269
    {
270
        val0.value = SDL_GetScancodeFromKey(kd.defaultValue1);
271
        if (val0.value == SDL_SCANCODE_UNKNOWN)
272
            val0.value = -1;
273
    }
274
    else
275
        val0.value = kd.defaultValue1;
276
    if (kd.defaultType2 == InputType::KEYBOARD)
277
    {
278
        val1.value = SDL_GetScancodeFromKey(kd.defaultValue2);
279
        if (val1.value == SDL_SCANCODE_UNKNOWN)
280
            val1.value = -1;
281
    }
282
    else
283
        val1.value = kd.defaultValue2;
284
#else  // USE_SDL2
285
286
    val0.value = kd.defaultValue1;
287
    val1.value = kd.defaultValue2;
288
#endif  // USE_SDL2
289
290
    updateKeyString(key, CAST_SIZE(i));
291
}
292
293
void InputManager::resetKeys() restrict2
294
{
295
    for (int i = 0; i < CAST_S32(InputAction::TOTAL); i++)
296
        resetKey(static_cast<InputActionT>(i));
297
}
298
299
void InputManager::makeDefault(const InputActionT i) restrict2
300
{
301
    if (i > InputAction::NO_VALUE && i < InputAction::TOTAL)
302
    {
303
        resetKey(i);
304
        update();
305
    }
306
}
307
308
bool InputManager::hasConflicts(InputActionT &restrict key1,
309
                                InputActionT &restrict key2) const restrict2
310
{
311
    /**
312
     * No need to parse the square matrix: only check one triangle
313
     * that's enough to detect conflicts
314
     */
315
    for (int i = 0; i < CAST_S32(InputAction::TOTAL); i++)
316
    {
317
        const InputActionData &restrict kdi = inputActionData[i];
318
        if (*kdi.configField == 0)
319
            continue;
320
321
        const InputFunction &restrict ki = mKey[i];
322
        for (size_t i2 = 0; i2 < inputFunctionSize; i2 ++)
323
        {
324
            const InputItem &restrict vali2 = ki.values[i2];
325
            if (vali2.value == -1)
326
                continue;
327
328
            size_t j;
329
            for (j = i, j++; j < CAST_S32(InputAction::TOTAL); j++)
330
            {
331
                if ((kdi.grp & inputActionData[j].grp) == 0
332
                    || (*kdi.configField == 0))
333
                {
334
                    continue;
335
                }
336
337
                for (size_t j2 = 0; j2 < inputFunctionSize; j2 ++)
338
                {
339
                    const InputItem &restrict valj2 = mKey[j].values[j2];
340
                    // Allow for item shortcut and emote keys to overlap
341
                    // as well as emote and ignore keys, but no other keys
342
                    if (valj2.type != InputType::UNKNOWN
343
                        && vali2.value == valj2.value
344
                        && vali2.type == valj2.type)
345
                    {
346
                        key1 = static_cast<InputActionT>(i);
347
                        key2 = static_cast<InputActionT>(j);
348
                        return true;
349
                    }
350
                }
351
            }
352
        }
353
    }
354
    return false;
355
}
356
357
void InputManager::callbackNewKey() restrict2
358
{
359
#ifndef DYECMD
360
    mSetupInput->newKeyCallback(mNewKeyIndex);
361
#endif  // DYECMD
362
}
363
364
2
bool InputManager::isActionActive(const InputActionT index) const restrict2
365
{
366

2
    if (!isActionActive0(index))
367
        return false;
368
369
    const InputActionData &restrict key =
370
        inputActionData[CAST_SIZE(index)];
371
//    logger->log("isActionActive mask=%d, condition=%d, index=%d",
372
//        mMask, key.condition, index);
373
    if ((key.condition & mMask) != key.condition)
374
        return false;
375
    return true;
376
}
377
378
2
bool InputManager::isActionActive0(const InputActionT index)
379
{
380
2
    if (keyboard.isActionActive(index))
381
        return true;
382

2
    if ((joystick != nullptr) && joystick->isActionActive(index))
383
        return true;
384
2
    return touchManager.isActionActive(index);
385
}
386
387
InputFunction &InputManager::getKey(InputActionT index) restrict2
388
{
389
    if (CAST_S32(index) < 0 || index >= InputAction::TOTAL)
390
        index = InputAction::MOVE_UP;
391
    return mKey[CAST_SIZE(index)];
392
}
393
394
75
std::string InputManager::getKeyStringLong(const InputActionT index) const
395
                                           restrict2
396
{
397
150
    std::string keyStr;
398
75
    const InputFunction &restrict ki = mKey[CAST_SIZE(index)];
399
400
300
    for (size_t i = 0; i < inputFunctionSize; i ++)
401
    {
402
225
        const InputItem &restrict key = ki.values[i];
403
450
        std::string str;
404
225
        if (key.type == InputType::KEYBOARD)
405
        {
406
            if (key.value >= 0)
407
            {
408
                str = KeyboardConfig::getKeyName(key.value);
409
            }
410
            else if (key.value < -1)
411
            {
412
                // TRANSLATORS: long key name. must be short.
413
                str = strprintf(_("key_%d"), -key.value);
414
            }
415
        }
416
225
        else if (key.type == InputType::JOYSTICK)
417
        {
418
            // TRANSLATORS: long joystick button name. must be short.
419
            str = strprintf(_("JButton%d"), key.value + 1);
420
        }
421
225
        if (!str.empty())
422
        {
423
            if (keyStr.empty())
424
                keyStr = str;
425
            else
426
                keyStr.append(", ").append(str);
427
        }
428
    }
429
430
75
    if (keyStr.empty())
431
    {
432
        // TRANSLATORS: unknown long key type
433
150
        return _("unknown key");
434
    }
435
    return keyStr;
436
}
437
438
void InputManager::updateKeyString(const InputFunction &ki,
439
                                   const size_t actionIdx) restrict2
440
{
441
    std::string keyStr;
442
    for (size_t i = 0; i < inputFunctionSize; i ++)
443
    {
444
        const InputItem &restrict key = ki.values[i];
445
        std::string str;
446
        if (key.type == InputType::KEYBOARD)
447
        {
448
            if (key.value >= 0)
449
            {
450
                str = KeyboardConfig::getKeyShortString(
451
                    KeyboardConfig::getKeyName(key.value));
452
            }
453
            else if (key.value < -1)
454
            {
455
                // TRANSLATORS: short key name. must be very short.
456
                str = strprintf(_("key_%d"), -key.value);
457
            }
458
        }
459
        else if (key.type == InputType::JOYSTICK)
460
        {
461
            // TRANSLATORS: short joystick button name. muse be very short
462
            str = strprintf(_("JB%d"), key.value + 1);
463
        }
464
        if (!str.empty())
465
        {
466
            if (keyStr.empty())
467
                keyStr = str;
468
            else
469
                keyStr.append(", ").append(str);
470
        }
471
    }
472
473
    if (keyStr.empty())
474
    {
475
        // TRANSLATORS: unknown short key type. must be short
476
        mKeyStr[actionIdx] = _("u key");
477
    }
478
    else
479
    {
480
        mKeyStr[actionIdx] = STD_MOVE(keyStr);
481
    }
482
}
483
484
std::string InputManager::getKeyValueString(const InputActionT index) const
485
                                            restrict2
486
{
487
    return mKeyStr[CAST_SIZE(index)];
488
}
489
490
std::string InputManager::getKeyValueByName(const std::string &restrict
491
                                            keyName) restrict2
492
{
493
    const StringInpActionMapCIter it = mNameMap.find(keyName);
494
495
    if (it == mNameMap.end())
496
        return std::string();
497
    return getKeyValueString((*it).second);
498
}
499
500
std::string InputManager::getKeyValueByNameLong(const std::string &restrict
501
                                                keyName) restrict2
502
{
503
    const StringInpActionMapCIter it = mNameMap.find(keyName);
504
505
    if (it == mNameMap.end())
506
        return std::string();
507
    return getKeyStringLong((*it).second);
508
}
509
510
void InputManager::addActionKey(const InputActionT action,
511
                                const InputTypeT type,
512
                                const int val) restrict2
513
{
514
    if (CAST_S32(action) < 0 || action >= InputAction::TOTAL)
515
        return;
516
517
    int idx = -1;
518
    InputFunction &restrict key = mKey[CAST_SIZE(action)];
519
    for (size_t i = 0; i < inputFunctionSize; i ++)
520
    {
521
        const InputItem &restrict val2 = key.values[i];
522
        if (val2.type == InputType::UNKNOWN ||
523
            (val2.type == type && val2.value == val))
524
        {
525
            idx = CAST_S32(i);
526
            break;
527
        }
528
    }
529
    if (idx == -1)
530
    {
531
        for (size_t i = 1; i < inputFunctionSize; i ++)
532
        {
533
            InputItem &restrict val1 = key.values[i - 1];
534
            InputItem &restrict val2 = key.values[i];
535
            val1.type = val2.type;
536
            val1.value = val2.value;
537
        }
538
        idx = inputFunctionSize - 1;
539
    }
540
541
    key.values[idx] = InputItem(type, val);
542
    updateKeyString(key, CAST_SIZE(action));
543
}
544
545
void InputManager::setNewKey(const SDL_Event &event,
546
                             const InputTypeT type) restrict2
547
{
548
    int val = -1;
549
    if (type == InputType::KEYBOARD)
550
        val = KeyboardConfig::getKeyValueFromEvent(event);
551
    else if (type == InputType::JOYSTICK && (joystick != nullptr))
552
        val = joystick->getButtonFromEvent(event);
553
554
    if (val != -1)
555
    {
556
        addActionKey(mNewKeyIndex, type, val);
557
        update();
558
    }
559
}
560
561
void InputManager::unassignKey() restrict2
562
{
563
    InputFunction &restrict key = mKey[CAST_SIZE(mNewKeyIndex)];
564
    for (size_t i = 0; i < inputFunctionSize; i ++)
565
    {
566
        InputItem &restrict val = key.values[i];
567
        val.type = InputType::UNKNOWN;
568
        val.value = -1;
569
    }
570
    updateKeyString(key, CAST_SIZE(mNewKeyIndex));
571
    update();
572
}
573
574
#ifndef DYECMD
575
bool InputManager::handleAssignKey(const SDL_Event &restrict event,
576
                                   const InputTypeT type) restrict2
577
{
578
    if ((setupWindow != nullptr) && setupWindow->isWindowVisible() &&
579
        getNewKeyIndex() > InputAction::NO_VALUE)
580
    {
581
        setNewKey(event, type);
582
        callbackNewKey();
583
        setNewKeyIndex(InputAction::NO_VALUE);
584
        return true;
585
    }
586
    return false;
587
}
588
#else  // DYECMD
589
590
bool InputManager::handleAssignKey(const SDL_Event &restrict event A_UNUSED,
591
                                   const InputTypeT type A_UNUSED) restrict2
592
{
593
    return false;
594
}
595
#endif  // DYECMD
596
597
bool InputManager::handleEvent(const SDL_Event &restrict event) restrict2
598
{
599
    BLOCK_START("InputManager::handleEvent")
600
    switch (event.type)
601
    {
602
        case SDL_KEYDOWN:
603
        {
604
#ifdef USE_SDL2
605
            if (keyboard.ignoreKey(event))
606
            {
607
                BLOCK_END("InputManager::handleEvent")
608
                return true;
609
            }
610
#endif  // USE_SDL2
611
612
            keyboard.refreshActiveKeys();
613
            updateConditionMask(true);
614
            if (handleAssignKey(event, InputType::KEYBOARD))
615
            {
616
                BLOCK_END("InputManager::handleEvent")
617
                return true;
618
            }
619
620
            keyboard.handleActivateKey(event);
621
            // send straight to gui for certain windows
622
#ifndef DYECMD
623
            if ((quitDialog != nullptr) || TextDialog::isActive())
624
            {
625
                if (guiInput != nullptr)
626
                    guiInput->pushInput(event);
627
                if (gui != nullptr)
628
                    gui->handleInput();
629
                BLOCK_END("InputManager::handleEvent")
630
                return true;
631
            }
632
#endif  // DYECMD
633
634
            break;
635
        }
636
        case SDL_KEYUP:
637
        {
638
#ifdef USE_SDL2
639
            if (keyboard.ignoreKey(event))
640
            {
641
                BLOCK_END("InputManager::handleEvent")
642
                return true;
643
            }
644
#endif  // USE_SDL2
645
646
            keyboard.refreshActiveKeys();
647
            updateConditionMask(false);
648
            keyboard.handleDeActicateKey(event);
649
            break;
650
        }
651
        case SDL_JOYBUTTONDOWN:
652
        {
653
            updateConditionMask(true);
654
//            joystick.handleActicateButton(event);
655
            if (handleAssignKey(event, InputType::JOYSTICK))
656
            {
657
                BLOCK_END("InputManager::handleEvent")
658
                return true;
659
            }
660
            break;
661
        }
662
        case SDL_JOYBUTTONUP:
663
        {
664
            updateConditionMask(false);
665
//            joystick.handleDeActicateButton(event);
666
            break;
667
        }
668
#ifdef USE_SDL2
669
        case SDL_FINGERDOWN:
670
            multiTouchManager.handleFingerDown(event);
671
            break;
672
        case SDL_FINGERUP:
673
            multiTouchManager.handleFingerUp(event);
674
            break;
675
#else  // USE_SDL2
676
#ifdef ANDROID
677
        case SDL_ACCELEROMETER:
678
        {
679
            break;
680
        }
681
#endif  // ANDROID
682
#endif  // USE_SDL2
683
684
        default:
685
            break;
686
    }
687
688
    if (guiInput != nullptr)
689
        guiInput->pushInput(event);
690
    if (gui != nullptr)
691
    {
692
        const bool res = gui->handleInput();
693
        if (res && event.type == SDL_KEYDOWN)
694
        {
695
            BLOCK_END("InputManager::handleEvent")
696
            return true;
697
        }
698
    }
699
700
    switch (event.type)
701
    {
702
        case SDL_KEYDOWN:
703
            if (isActionActive(InputAction::IGNORE_INPUT_1) ||
704
                isActionActive(InputAction::IGNORE_INPUT_2))
705
            {
706
                BLOCK_END("InputManager::handleEvent")
707
                return true;
708
            }
709
            if (triggerAction(keyboard.getActionVector(event)))
710
            {
711
                BLOCK_END("InputManager::handleEvent")
712
                return true;
713
            }
714
            break;
715
716
// disabled temporary
717
//        case SDL_KEYUP:
718
//            if (triggerAction(keyboard.getActionVector(event)))
719
//            {
720
//                BLOCK_END("InputManager::handleEvent")
721
//                return true;
722
//            }
723
//            break;
724
725
        case SDL_JOYBUTTONDOWN:
726
            if ((joystick != nullptr) && joystick->validate())
727
            {
728
                if (triggerAction(joystick->getActionVector(event)))
729
                {
730
                    BLOCK_END("InputManager::handleEvent")
731
                    return true;
732
                }
733
            }
734
            break;
735
#ifdef ANDROID
736
#ifndef USE_SDL2
737
        case SDL_ACCELEROMETER:
738
        {
739
            break;
740
        }
741
#endif  // USE_SDL2
742
#endif  // ANDROID
743
744
        default:
745
            break;
746
    }
747
748
    BLOCK_END("InputManager::handleEvent")
749
    return false;
750
}
751
752
void InputManager::handleRepeat()
753
{
754
    const int time = tick_time;
755
    keyboard.handleRepeat(time);
756
    if (joystick != nullptr)
757
        joystick->handleRepeat(time);
758
}
759
760
void InputManager::updateConditionMask(const bool pressed A_UNUSED) restrict2
761
{
762
    mMask = 1;
763
    if (keyboard.isEnabled())
764
        mMask |= InputCondition::ENABLED;
765
#ifndef DYECMD
766
    if (((chatWindow == nullptr) || !chatWindow->isInputFocused()) &&
767
        !NpcDialog::isAnyInputFocused() &&
768
        !InventoryWindow::isAnyInputFocused() &&
769
        ((tradeWindow == nullptr) || !tradeWindow->isInpupFocused()))
770
    {
771
        if (gui != nullptr)
772
        {
773
            FocusHandler *restrict const focus = gui->getFocusHandler();
774
            if (focus != nullptr)
775
            {
776
                if (dynamic_cast<TextField*>(focus->getFocused()) == nullptr)
777
                    mMask |= InputCondition::NOINPUT;
778
            }
779
            else
780
            {
781
                mMask |= InputCondition::NOINPUT;
782
            }
783
        }
784
        else
785
        {
786
            mMask |= InputCondition::NOINPUT;
787
        }
788
    }
789
790
    if (!BuyDialog::isActive() && !SellDialog::isActive())
791
        mMask |= InputCondition::NOBUYSELL;
792
793
    if (!PlayerInfo::isVending())
794
        mMask |= InputCondition::NOVENDING;
795
    if (!PlayerInfo::isInRoom())
796
        mMask |= InputCondition::NOROOM;
797
798
    const NpcDialog *restrict const dialog = NpcDialog::getActive();
799
    if ((dialog == nullptr) || !dialog->isTextInputFocused())
800
        mMask |= InputCondition::NONPCINPUT;
801
    if ((dialog == nullptr) || (dialog->isCloseState() != 0))
802
    {
803
        mMask |= InputCondition::NONPCDIALOG;
804
        if (!InventoryWindow::isStorageActive())
805
            mMask |= InputCondition::NOTALKING;
806
    }
807
    if ((setupWindow == nullptr) || !setupWindow->isWindowVisible())
808
        mMask |= InputCondition::NOSETUP;
809
810
    if ((Game::instance() != nullptr) && Game::instance()->getValidSpeed())
811
        mMask |= InputCondition::VALIDSPEED;
812
813
    if (Game::instance() != nullptr)
814
        mMask |= InputCondition::INGAME;
815
816
    if (localPlayer != nullptr)
817
    {
818
        if (localPlayer->getFollow().empty())
819
            mMask |= InputCondition::NOFOLLOW;
820
821
        if (!localPlayer->isTrickDead())
822
            mMask |= InputCondition::NOBLOCK;
823
824
        if (localPlayer->isAlive())
825
            mMask |= InputCondition::ALIVE;
826
    }
827
    else
828
    {
829
        mMask |= InputCondition::NOFOLLOW;
830
        mMask |= InputCondition::NOBLOCK;
831
    }
832
#endif  // DYECMD
833
834
    if (!settings.awayMode)
835
        mMask |= InputCondition::NOAWAY;
836
837
    if (gui != nullptr && gui->getFocusHandler()->getModalFocused() == nullptr)
838
        mMask |= InputCondition::NOMODAL;
839
840
    if (!settings.disableGameModifiers)
841
        mMask |= InputCondition::EMODS;
842
843
    if (!isActionActive0(InputAction::STOP_ATTACK)
844
        && !isActionActive0(InputAction::UNTARGET))
845
    {
846
        mMask |= InputCondition::NOTARGET;
847
    }
848
    // enable it temporary
849
    mMask |= InputCondition::KEY_DOWN;
850
//    if (pressed == true)
851
//        mMask |= InputCondition::KEY_DOWN;
852
//    else
853
//        mMask |= InputCondition::KEY_UP;
854
}
855
856
bool InputManager::checkKey(const InputActionData *restrict const key) const
857
                            restrict2
858
{
859
    // logger->log("checkKey mask=%d, condition=%d", mMask, key->condition);
860
    if ((key == nullptr) || (key->condition & mMask) != key->condition)
861
        return false;
862
863
    return key->modKeyIndex == InputAction::NO_VALUE
864
        || isActionActive0(key->modKeyIndex);
865
}
866
867
bool InputManager::invokeKey(const InputActionData *restrict const key,
868
                             const InputActionT keyNum) restrict2
869
{
870
    // no validation to keyNum because it validated in caller
871
872
    if (checkKey(key))
873
    {
874
        InputEvent evt(keyNum, mMask);
875
        ActionFuncPtr func = *(inputActionData[
876
            CAST_SIZE(keyNum)].action);
877
        if ((func != nullptr) && func(evt))
878
            return true;
879
    }
880
    return false;
881
}
882
883
void InputManager::executeAction(const InputActionT keyNum) restrict2
884
{
885
    if (keyNum < InputAction::MOVE_UP || keyNum >= InputAction::TOTAL)
886
        return;
887
888
    ActionFuncPtr func = *(inputActionData[CAST_SIZE(
889
        keyNum)].action);
890
    if (func != nullptr)
891
    {
892
        InputEvent evt(keyNum, mMask);
893
        func(evt);
894
    }
895
}
896
897
bool InputManager::executeChatCommand(const std::string &restrict cmd,
898
                                      const std::string &restrict args,
899
                                      ChatTab *restrict const tab) restrict2
900
{
901
    const StringIntMapCIter it = mChatMap.find(cmd);
902
    if (it != mChatMap.end())
903
    {
904
        ActionFuncPtr func = *(inputActionData[(*it).second].action);
905
        if (func != nullptr)
906
        {
907
            InputEvent evt(args, tab, mMask);
908
            func(evt);
909
            return true;
910
        }
911
    }
912
    else
913
    {
914
        reportAlways("Unknown chat command: /%s %s",
915
            cmd.c_str(),
916
            args.c_str())
917
    }
918
    return false;
919
}
920
921
bool InputManager::executeRemoteChatCommand(const std::string &restrict cmd,
922
                                            const std::string &restrict args,
923
                                            ChatTab *restrict const tab)
924
                                            restrict2
925
{
926
    const StringIntMapCIter it = mChatMap.find(cmd);
927
    if (it != mChatMap.end())
928
    {
929
        const InputActionData &restrict data = inputActionData[(*it).second];
930
        if (data.isProtected == Protected_true)
931
            return false;
932
        ActionFuncPtr func = *(data.action);
933
        if (func != nullptr)
934
        {
935
            InputEvent evt(args, tab, mMask);
936
            func(evt);
937
            return true;
938
        }
939
    }
940
    return false;
941
}
942
943
bool InputManager::executeChatCommand(const InputActionT keyNum,
944
                                      const std::string &restrict args,
945
                                      ChatTab *restrict const tab) restrict2
946
{
947
    if (CAST_S32(keyNum) < 0 || keyNum >= InputAction::TOTAL)
948
        return false;
949
    ActionFuncPtr func = *(inputActionData[CAST_SIZE(
950
        keyNum)].action);
951
    if (func != nullptr)
952
    {
953
        InputEvent evt(args, tab, mMask);
954
        func(evt);
955
        return true;
956
    }
957
    return false;
958
}
959
960
1
void InputManager::updateKeyActionMap(KeyToActionMap &restrict actionMap,
961
                                      KeyToIdMap &restrict idMap,
962
                                      KeyTimeMap &restrict keyTimeMap,
963
                                      const InputTypeT type) const restrict2
964
{
965
1
    actionMap.clear();
966
1
    keyTimeMap.clear();
967
968
671
    for (size_t i = 0; i < CAST_SIZE(InputAction::TOTAL); i ++)
969
    {
970
670
        const InputFunction &restrict key = mKey[i];
971
670
        const InputActionData &restrict kd = inputActionData[i];
972
670
        if (kd.action != nullptr)
973
        {
974
4312
            for (size_t i2 = 0; i2 < inputFunctionSize; i2 ++)
975
            {
976
1848
                const InputItem &restrict ki = key.values[i2];
977

1848
                if (ki.type == type && ki.value != -1)
978
                {
979
                    actionMap[ki.value].push_back(
980
                        static_cast<InputActionT>(i));
981
                }
982
            }
983
        }
984

670
        if (kd.configField != nullptr && (kd.grp & Input::GRP_GUICHAN) != 0)
985
        {
986
287
            for (size_t i2 = 0; i2 < inputFunctionSize; i2 ++)
987
            {
988
123
                const InputItem &restrict ki = key.values[i2];
989

123
                if (ki.type == type && ki.value != -1)
990
                    idMap[ki.value] = static_cast<InputActionT>(i);
991
            }
992
        }
993

670
        if (kd.configField != nullptr && (kd.grp & Input::GRP_REPEAT) != 0)
994
        {
995
21
            for (size_t i2 = 0; i2 < inputFunctionSize; i2 ++)
996
            {
997
9
                const InputItem &restrict ki = key.values[i2];
998

9
                if (ki.type == type && ki.value != -1)
999
                    keyTimeMap[ki.value] = 0;
1000
            }
1001
        }
1002
    }
1003
1004
1
    inputActionDataSorter.keys = &inputActionData[0];
1005
2
    FOR_EACH (KeyToActionMapIter, it, actionMap)
1006
    {
1007
        KeysVector *const keys = &it->second;
1008
        if (keys->size() > 1)
1009
            std::sort(keys->begin(), keys->end(), inputActionDataSorter);
1010
    }
1011
1
}
1012
1013
bool InputManager::triggerAction(const KeysVector *restrict const ptrs)
1014
                                 restrict2
1015
{
1016
    if (ptrs == nullptr)
1017
        return false;
1018
1019
//    logger->log("ptrs: %d", (int)ptrs.size());
1020
1021
    FOR_EACHP (KeysVectorCIter, it, ptrs)
1022
    {
1023
        const InputActionT keyNum = *it;
1024
        if (CAST_S32(keyNum) < 0 || keyNum >= InputAction::TOTAL)
1025
            continue;
1026
1027
        if (invokeKey(&inputActionData[CAST_SIZE(keyNum)], keyNum))
1028
            return true;
1029
    }
1030
    return false;
1031
}
1032
1033
InputActionT InputManager::getKeyIndex(const int value,
1034
                                       const int grp,
1035
                                       const InputTypeT type) const restrict2
1036
{
1037
    for (size_t i = 0; i < CAST_SIZE(InputAction::TOTAL); i++)
1038
    {
1039
        const InputFunction &restrict key = mKey[i];
1040
        const InputActionData &restrict kd = inputActionData[i];
1041
        for (size_t i2 = 0; i2 < inputFunctionSize; i2 ++)
1042
        {
1043
            const InputItem &restrict vali2 = key.values[i2];
1044
            if (value == vali2.value &&
1045
                (grp & kd.grp) != 0 && vali2.type == type)
1046
            {
1047
                return static_cast<InputActionT>(i);
1048
            }
1049
        }
1050
    }
1051
    return InputAction::NO_VALUE;
1052
}
1053
1054
InputActionT InputManager::getActionByKey(const SDL_Event &restrict event)
1055
                                          const restrict2
1056
{
1057
    // for now support only keyboard events
1058
    if (event.type == SDL_KEYDOWN || event.type == SDL_KEYUP)
1059
    {
1060
        const InputActionT idx = keyboard.getActionId(event);
1061
        if (CAST_S32(idx) >= 0 &&
1062
            checkKey(&inputActionData[CAST_SIZE(idx)]))
1063
        {
1064
            return idx;
1065
        }
1066
    }
1067
    return InputAction::NO_VALUE;
1068
}
1069
1070
InputActionT InputManager::getActionByConfigField(const std::string &field)
1071
{
1072
    for (int i = 0; i < CAST_S32(InputAction::TOTAL); i ++)
1073
    {
1074
        const std::string cf = inputActionData[i].configField;
1075
        if (field == cf)
1076
            return static_cast<InputActionT>(i);
1077
    }
1078
    return InputAction::NO_VALUE;
1079
}
1080
1081
2
void InputManager::addChatCommands(std::list<std::string> &restrict arr)
1082
{
1083
2
    const int sz = CAST_S32(InputAction::TOTAL);
1084
1342
    for (int i = 0; i < sz; i++)
1085
    {
1086
1340
        const InputActionData &restrict ad = inputActionData[i];
1087
4020
        std::string cmd = ad.chatCommand;
1088
1340
        if (!cmd.empty())
1089
        {
1090
1284
            StringVect tokens;
1091
642
            splitToStringVector(tokens, cmd, '|');
1092
4244
            FOR_EACH (StringVectCIter, it, tokens)
1093
            {
1094
6204
                cmd = std::string("/").append(*it);
1095
1034
                if (ad.useArgs == UseArgs_true)
1096
534
                    cmd.append(" ");
1097
1034
                arr.push_back(cmd);
1098
            }
1099
        }
1100
    }
1101

5
}