почему события ввода иногда не фиксируются в С++

avatar
Ammar Mujtaba Tariq
8 августа 2021 в 18:51
82
1
0
#include <Windows.h>
#include <vector>
using std::vector;

class event_handler{
private:
    DWORD n_evnts_read=0,n_evnts=0,evnt_type=0,mode=0;

public:
    int rec_indx = 0;
    vector<INPUT_RECORD> inrec;
    KEY_EVENT_RECORD kev;
    MOUSE_EVENT_RECORD mev;
    HANDLE hndlin = GetStdHandle(STD_INPUT_HANDLE);
    HANDLE hndlout = GetStdHandle(STD_OUTPUT_HANDLE);

    int cap_evnt_key(){

        n_evnts_read = n_evnts = evnt_type = 0;

        GetNumberOfConsoleInputEvents(hndlin, &n_evnts_read);
        if (n_evnts_read){
            inrec.resize(n_evnts_read);
            ReadConsoleInput(hndlin, &inrec[0], inrec.size(), &n_evnts);
            for(DWORD m = 0; m < n_evnts; m++)
                if (inrec[m].EventType == KEY_EVENT)
                {
                    kev = inrec[m].Event.KeyEvent;
                    fflush(stdin);
                    return 5;
                }
        }
        return -1;
    }

    int cap_evnt_mouse(){

        n_evnts_read = n_evnts = evnt_type = mode = 0;

        GetNumberOfConsoleInputEvents(hndlin, &n_evnts_read);
        if (n_evnts_read){
            inrec.resize(n_evnts_read);
            ReadConsoleInput(hndlin, &inrec[0], inrec.size(), &n_evnts);
            for(DWORD m = 0; m < n_evnts; m++)
                if (inrec[m].EventType == MOUSE_EVENT)
                {
                    mev = inrec[m].Event.MouseEvent;
                    fflush(stdin);
                    return 5;
                }
        }
        return -1;
    }

    DWORD key(){
        if (!kev.bKeyDown)
            return kev.wVirtualKeyCode;
        else
            return 0;
    }

    bool left_click(){
        if (mev.dwButtonState == FROM_LEFT_1ST_BUTTON_PRESSED)
            return 1;
        else
            return 0;
    }
} evnt_hndlr;   

void test()
{
    short n = 0;
    COORD pos;

    while (1)
    {
        if (evnt_hndlr.cap_evnt_key() == 5)
        {
            if (evnt_hndlr.key() == VK_RIGHT)
                //do something
            else if (evnt_hndlr.key() == VK_LEFT)
                //do something
        }
        else if (evnt_hndlr.cap_evnt_mouse() == 5)
        {
            if (evnt_hndlr.left_click() == 1)
                //do something
        }
    }
    exit(0);
}

Теперь, когда я запускаю test() внутри main() и пытаюсь перехватить событие мыши/клавиатуры, иногда он дает мне сигнал о том, что требуемое событие было перехвачено, но иногда это событие пропускается.

Пожалуйста, дайте мне надежный способ захвата событий ввода (особенно, когда события мыши и клавиатуры должны быть прочитаны одновременно), потому что эта процедура зависает (если я не использую неправильное слово). Кроме того, объясните мне, почему эта проблема происходит. Это действительно очень плохо для пользователей.

Источник
Remy Lebeau
8 августа 2021 в 21:32
0

Как вы думаете, что происходит, когда вызывается cap_evnt_key() и в очереди есть события мыши? Ты выбросишь их! То же самое с cap_evnt_mouse() с ключевыми событиями. Так что, конечно, вы иногда теряете события. Итак, вам нужно пересмотреть весь свой подход. Я бы предложил переместить GetNumberOfConsoleInputEvents()/ReadConsoleInput() в другой метод, который перемещает ожидающие события в кеш, а затем иметь другой метод, который извлекает события из этого кеша, возвращаясь к консоли для кэширования новых событий всякий раз, когда кеш пуст.

Ammar Mujtaba Tariq
9 августа 2021 в 04:48
0

1: Это не проблема синхронизации консоли или что-то в этом роде? 2: Разве вектор inrec не может действовать как кеш-буфер? Я перемещаю события всякий раз, когда я нажимаю какую-либо клавишу на клавиатуре или мыши, в inrec, а затем извлекаю требуемое после его захвата, а затем отбрасываю его, разве это не похоже на кеш? Или что-то еще нужно сделать и поддерживать?

Remy Lebeau
9 августа 2021 в 06:07
0

1: нет, потому что проблема в том, что ваша логика извлекает события и отбрасывает их. 2: может, если бы вы использовали его более эффективно. Например, всякий раз, когда вы вызываете cap_evnt_...() и доступны какие-либо новые события ввода, вы стираете текущее векторное содержимое и заполняете его только последними событиями, не проверяя, уже содержит более ранние интересующие вас события. Вам нужно добавить новые события к вектору, а не заменить его.

Ammar Mujtaba Tariq
9 августа 2021 в 07:06
0

int cap_evnt_key(){ n_evnts_read=n_evnts=evnt_type=0; GetNumberOfConsoleInputEvents(hndlin,&n_evnts_read); if(n_evnts_read){ inrec.resize(inrec.size()+n_evnts_read); ReadConsoleInput(hndlin,&inrec[inrec.size()-n_evnts_read],n_evnts_read,&n_evnts); for(int m=0;m<inrec.size();m++) if(inrec[m].EventType==KEY_EVENT) { kev=inrec[m].Event.KeyEvent; return 5; }}return -1;} взгляните на мою модифицированную версию cap_evnt_key() согласно вашему совету.

Ammar Mujtaba Tariq
9 августа 2021 в 07:11
0

Я не удаляю содержимое inrec и продолжаю добавлять следующие события чтения, пока не будет выполнена желаемая работа. Как только это будет сделано, я resize() обнулю его, это хороший способ сохранить кеш? Если нет, пожалуйста, объясните мне. Потому что проблема усугубилась (очевидно, по моей плохой логике :) )

Remy Lebeau
9 августа 2021 в 07:16
0

ваш новый код игнорирует существующие события в векторе, если нет доступных для чтения новых событий. Кроме того, вы не удаляете события из вектора по мере их обработки. И поскольку вы храните оба вида событий в одном векторе, изменение размера вектора до 0 приведет к потере ВСЕХ событий. Скажем, вы закончите обработку ключевых событий, вы потеряете события мыши. И наоборот. Я все еще думаю, что весь ваш подход неверен и должен быть переработан с нуля.

Ammar Mujtaba Tariq
9 августа 2021 в 07:28
0

Давайте продолжим обсуждение в чате.

Ответы (1)

avatar
Remy Lebeau
9 августа 2021 в 08:01
1

Вы не обрабатываете каждое прочитанное событие. Ваш код может отбрасывать множество событий. Каждый раз, когда вы вызываете cap_evnt_...(), если есть какие-либо новые события, доступные для чтения, вы отбрасываете все события, которые вы уже прочитали, в vector, которые еще не были обработаны.

.

Попробуйте вместо этого что-то вроде этого:

#include <Windows.h>
#include <vector>

class event_handler{
private:
    std::vector<INPUT_RECORD> evnts;
    INPUT_RECORD inrec;
    HANDLE hndlin = GetStdHandle(STD_INPUT_HANDLE);
    HANDLE hndlout = GetStdHandle(STD_OUTPUT_HANDLE);

public:
    int cap_evnt_read() {
        if (evnts.empty()) {
            DWORD n_evnts = 0;
            GetNumberOfConsoleInputEvents(hndlin, &n_evnts);
            if (n_evnts == 0) return -1;
            evnts.resize(n_evnts);
            ReadConsoleInput(hndlin, &evnts[0], n_evnts, &n_evnts);
        }
        inrec = evnts.front();
        evnts.erase(events.begin());
        return inrec.EventType;
    }

    DWORD key() const {
        if (!inrec.Event.KeyEvent.bKeyDown)
            return inrec.Event.KeyEvent.wVirtualKeyCode;
        else
            return 0;
    }

    bool left_click() const {
        return (inrec.Event.MouseEvent.dwButtonState == FROM_LEFT_1ST_BUTTON_PRESSED);
    }
}
evnt_hndlr;   

void test()
{
    while (true)
    {
        switch (evnt_hndlr.cap_evnt_read())
        {
            case KEY_EVENT:
            {
                if (evnt_hndlr.key() == VK_RIGHT)
                    // do something
                else if (evnt_hndlr.key() == VK_LEFT)
                    // do something
                break;
            }

            case MOUSE_EVENT:
            {
                if (evnt_hndlr.left_click())
                    // do something
                break;
            }
       }
    }
    exit(0);
}
Ammar Mujtaba Tariq
9 августа 2021 в 11:06
0

О, это действительно работает! Большое спасибо, я действительно многому научился.

Ammar Mujtaba Tariq
9 августа 2021 в 11:11
0

Есть ли способ захватить горячие клавиши? я имею в виду, что ((evnt_hndlr.key()==VK_RIGHT)&&(evnt_hndlr.inrec.Event.KeyEvent.dwControlKeyState==LEFT_ALT_PRESSED)) (Alt+Right) не оценивается как истина.

Remy Lebeau
9 августа 2021 в 15:12
0

@AmmarMujtabaTariq есть ли причина, по которой вы проверяете if (!bKeyDown) (т.е. клавишу вверх) вместо if (bKeyDown) (т.е. клавишу вниз)? Вы обрабатываете события отпускания клавиш, а не события нажатия клавиш.

Ammar Mujtaba Tariq
9 августа 2021 в 16:47
0

На самом деле, этот метод должен был быть реализован для щелчка мыши, потому что, когда пользователь по ошибке нажимает кнопку, у него должно быть время, чтобы перетащить мышь за пределы кнопки и отпустить ее, чтобы действие можно было игнорировать (это происходит только при отпускании кнопки). событие обрабатывается). Возможно, вы знаете эту стратегию. И мой предыдущий вопрос, вы еще не ответили на него :)

Ammar Mujtaba Tariq
10 августа 2021 в 18:47
0

Возникла еще одна проблема, я отключил режим быстрого редактирования и включил режим оконного ввода и режим ввода мышью во время запуска программы, но даже тогда мне приходится переделывать их в каждой итерации, где находится readconsoleinput(). В противном случае он не принимает ввод с мыши, ввод с клавиатуры в порядке.

Remy Lebeau
10 августа 2021 в 19:03
0

@AmmarMujtabaTariq "Я должен повторно выполнять [окно|режим ввода мыши] в каждой итерации, где находится readconsoleinput(). В противном случае он не принимает ввод с помощью мыши" - сброс режимов ввода не должен необходимо, согласно документации Microsoft.

Ammar Mujtaba Tariq
11 августа 2021 в 07:05
0

но в противном случае он не принимает входные данные, это ошибка компилятора или что-то в этом роде?

Remy Lebeau
11 августа 2021 в 15:24
0

@AmmarMujtabaTariq Компилятор не влияет на поведение API. Происходит что-то еще.

Ammar Mujtaba Tariq
11 августа 2021 в 17:41
0

ОК, еще одна проблема, которая вызывает проблему, заключается в том, что даже после того, как я написал cursorinfo.bVisible=FALSE, мой курсор большую часть времени не скрывается (потому что иногда он работает, но не всегда).