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

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

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

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

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

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

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

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

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

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

5
}