2015-04-28 70 views
0

我想从PDCurses获取鼠标位置数据,它通常工作。问题是,如果在事件检查发生之前按下鼠标按钮两次,则只有一个事件会从队列中弹出。这意味着第二次按下的事件仍然在队列中,并且下次按下鼠标时,旧位置将弹出而不是最近的位置。如果这种情况持续发生,则队列开始备份,并且报告的鼠标按下变得越来越近。因为我使用getch的唯一的事情是鼠标事件(我使用Window的GetAsyncKeyState和我自己的键盘事件管理器),我认为一个简单的修复就是在阅读完后清除事件队列一个鼠标事件。有没有办法清除Curse的事件队列?

不幸的是,它似乎并不那么容易,因为我找不到任何允许清除队列的方法。

我能想到的唯一方法是使用nodelaygetch设置为非阻塞,然后重复使用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; 
    } 
} 
+0

当然你想要最近的鼠标位置,而不是最近的?因此,您应该找到一种方法来读取并放弃队列中最后一次鼠标事件以外的所有事件。 – EJP

回答

2

PDCurses实现flushinp ,这是应该做的要求。注释和功能(适用于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); 
} 

此功能通过沿途设置在最诅咒实现的状态。这里是ncurses中的manual page的链接,这可能会有所帮助。

+0

谢谢。我看了很多普通的诅咒手册。我很惊讶,我从来没有见过它。我最终写了一个while循环来弹出它们并返回最后一个事件。我很好奇性能差异会是什么。 – Carcigenicate

+0

这是值得检查。我没有用过这个与PDCurses,但知道它在ncurses中的使用。 –

相关问题