0

PDCurses からマウスの位置データを取得しようとしていますが、通常は機能します。問題は、イベント チェックが行われる前にマウス ボタンが 2 回押された場合、イベントの 1 つだけがキューから取り出されることです。これは、2 回目のプレスのイベントがまだキューにあり、次にマウスが押されたときに、最新の場所ではなく古い場所がポップされることを意味します。これが繰り返されると、キューがバックアップされ始め、報告されたマウスの押下がますます最近のものではなくなります。

私が使っgetchているのはマウス イベントだけなので (GetAsyncKeyStateキーボード イベントには Windows と独自のマネージャーを使用しています)、マウス イベントを読み取った後にイベント キューをクリアするだけで簡単に解決できると思いました。

残念ながら、キューをクリアできるメソッドが見つからないため、それほど簡単ではないようです。

私が考えることができる唯一の方法は、getchを使用して非ブロッキングに設定しnodelay、次に を繰り返し使用getchして、ポップされた最後のイベントを保持することでした。しかし、それは非効率的で不正確に思えます。

これは潜在的に XY 問題であるため、問題の関数は次のとおりです。

void EventHandler::getMousePos(int& x, int& y) {
    MEVENT event;
    if (nc_getmouse(&event) == OK) {
        x = event.x, y = event.y;
    }
}

EventHandler.h:

#ifndef EVENT_HANDLER_H
#define EVENT_HANDLER_H

#include <vector>
#include <atomic>
#include <thread>
#include <functional>
#include <windows.h>
#include <WinUser.h>
#include "curses.h"

#define LEFT_MOUSE VK_LBUTTON
#define RIGHT_MOUSE VK_RBUTTON
#define MIDDLE_MOUSE VK_MBUTTON

typedef std::function<void(char)> KeyHandler;
typedef std::function<void(char,long,long)> MouseHandler;

class EventHandler {

    std::thread listeningThread;

    std::atomic<bool> listening = false;

    std::vector<char> keysToCheck;
    std::vector<char> mButtonsToCheck;

    KeyHandler keyHandler = KeyHandler();
    MouseHandler mouseHandler = MouseHandler();

    void actOnPressedKeys();

public:
    EventHandler();

    ~EventHandler();

    void setKeyHandler(KeyHandler);
    void setMouseHandler(MouseHandler);

    void setKeysToListenOn(std::vector<char>);
    void setButtonsToListenOn(std::vector<char>);

    void listenForPresses(int loopMSDelay = 100);
    void stopListening();

    static void getMousePos(int& x, int& y);

};

#endif

EventHandler.cpp:

#include "EventHandler.h"

#include <thread>
#include <stdexcept>
#include <cctype>

EventHandler::EventHandler() {

}

EventHandler::~EventHandler() {
    stopListening();
    if (listeningThread.joinable()) {
        //May need to fix this. May cause the EventHandler to freeze
        // on destruction if listeningThread can't join;
        listeningThread.join();
    }
}

void EventHandler::actOnPressedKeys() {
    for (char key : keysToCheck) {
        if (GetAsyncKeyState(key)) {
            //pressedKeys.insert(key);
            keyHandler(key);
        }
    }

    for (char button : mButtonsToCheck) {
        if ( GetAsyncKeyState(button) ) {

            int x = 0, y = 0;
            getMousePos(x, y);
            mvprintw(y, x, "Button Press Detected");
            mouseHandler(button, x, y);
        }
    }
}

/*void EventHandler::actOnPressedKeys() {
    pressedKeys.forEach([](char key){
        keyHandler(key);
    });
}*/

void EventHandler::setKeyHandler(KeyHandler handler) {
    keyHandler = handler;
}

void EventHandler::setMouseHandler(MouseHandler handler) {
    mouseHandler = handler;
}

void EventHandler::setKeysToListenOn(std::vector<char> newListenKeys) {
    if (listening) {
        throw std::runtime_error::runtime_error(
            "Cannot change the listened-on keys while listening"
        );
        //This could be changed to killing the thread by setting
        // listening to false, changing the keys, then restarting
        // the listening thread. I can't see that being necessary though.
    }

    //Untested
    for (char& key : newListenKeys) {
        if (key >= 'a' && key <= 'z') {
            key += 32;
        }
    }

    keysToCheck = newListenKeys;

}

void EventHandler::setButtonsToListenOn(std::vector<char> newListenButtons) {
    if (listening) {
        throw std::runtime_error::runtime_error(
            "Cannot change the listened-on buttons while listening"
        );
    }

    mButtonsToCheck = newListenButtons;
}

void EventHandler::listenForPresses(int loopMSDelay) {
    listening = true;
    listeningThread = std::thread ([=]{
        do {
            actOnPressedKeys();
            std::this_thread::sleep_for(std::chrono::milliseconds(loopMSDelay));
        } while (listening);

    });
}

void EventHandler::stopListening() {
    listening = false;
}

void EventHandler::getMousePos(int& x, int& y) {
    MEVENT event;
    if (nc_getmouse(&event) == OK) {
        x = event.x, y = event.y;
    }
}
4

1 に答える 1

2

PDCursesflushinpは、要求された処理を実行することになっている を実装します。コメントと関数 (たとえば、win32 の場合) の状態

/* discard any pending keyboard or mouse input -- this is the core 
   routine for flushinp() */

void PDC_flushinp(void)
{
    PDC_LOG(("PDC_flushinp() - called\n"));

    FlushConsoleInputBuffer(pdc_con_in);
}

ちなみに、この関数はほとんどの curses 実装で提供されています。ncurses のマニュアルページへのリンクが参考になるかもしれません。

于 2015-04-29T22:45:30.943 に答える