如何实现检测鼠标按钮的一致方法

时间:2017-06-03 21:55:31

标签: c++ multithreading mouseevent glfw

我正在尝试实现Mouse类来处理鼠标操作的任何相关问题。除了检测到按住鼠标按钮外,一切正常。该库不为鼠标提供此功能,但有一个键盘事件选项(即通过REPEAT标志)。我必须手动实现它。第一个也是简单的方法是设置按下和释放按钮的标志

bool Mouse::isRightDown()
{
    if (m_button == GLFW_MOUSE_BUTTON_RIGHT && m_action == GLFW_PRESS){
        m_isRightHold = true;
        ...
}

bool Mouse::isRightUp()
{
    if (m_button == GLFW_MOUSE_BUTTON_RIGHT && m_action == GLFW_RELEASE ){
        m_isRightHold = false;
        ...
}

bool Mouse::isRightHold()
{
    if ( g_RightFlag ){
        ...
        return true;
    }
    return false;
}

现在在渲染循环中,我可以执行以下操作

while(!glfwWindowShouldClose(window)){
    glfwPollEvents();
    ...
    // Handle Right Button Mouse
    if ( Mouse::Instance()->isRightHold() ){
        std::cout << "Right Mouse Button is hold..." << std::endl;
    }
    ...
    glfwSwapBuffers(window);
}

但这种方法的问题在于,while循环比人类释放按钮的反应更快,因此,单击将被视为保持事件。我通过更新全局布尔变量(即g_RightFlag)考虑了另一种方法。变量将在独立线程中每900秒更新一次,如下所示

while (true){

        //std::this_thread::sleep_for(delay);


        std::chrono::duration<int, std::ratio<1, 1000>> delay(900);

        bool sleep = true;
        auto start = std::chrono::system_clock::now();
        while(sleep)
        {
            auto end = std::chrono::system_clock::now();
            auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
            if ( elapsed.count() > delay.count() ){
                sleep = false;
            }
        }   

        mtx.lock();
        g_RightFlag = m_isRightHold;
        g_LefFlag   = m_isLeftHold;
        mtx.unlock();
    }   

此解决方案优于第一种方法,但仍然不一致,因为线程未同步。在某些时刻,当我只做一次点击时,检测到保持事件(即以毫秒为单位)。如何改进处理Hold Mouse Event的方法?

main.cpp

#include <iostream>
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include "mouse.h"

int main(void)
{
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR,3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR,3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);


    GLFWwindow* window = glfwCreateWindow(800,600,"LearnOpenGL",nullptr,nullptr);
    if( window == nullptr ){
        std::cout << "Failed to create GLFW window" << std::endl;
        glfwTerminate();
        return -1;
    }
    glfwMakeContextCurrent(window);
    glewExperimental = GL_TRUE;
    if( glewInit() != GLEW_OK ){
        std::cout << "Failed to initialize GLEW" << std::endl;
        return -1;
    }

    int width, height;
    glfwGetFramebufferSize(window, &width, &height);
    glViewport(0,0, width, height);

    // callback events
    //Keyboard Event;
    Mouse::Instance();
    Mouse::Instance()->init(window);

    while(!glfwWindowShouldClose(window)){
        glfwPollEvents();

        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);

        // Handle Right Button Mouse
        if ( Mouse::Instance()->isRightDown() ){
            std::cout << "Right Mouse Button is pressed..." << std::endl;
        }

        if ( Mouse::Instance()->isRightUp() ){
            std::cout << "Right Mouse Button is released..." << std::endl;
        }

        if ( Mouse::Instance()->isRightHold() ){
            std::cout << "Right Mouse Button is hold..." << std::endl;
        }

        // Handle Left Button Mouse
        if ( Mouse::Instance()->isLeftDown() ){
            std::cout << "Left Mouse Button is pressed..." << std::endl;
        }

        if ( Mouse::Instance()->isLeftUp() ){
            std::cout << "Left Mouse Button is released..." << std::endl;
        }

        if ( Mouse::Instance()->isLeftHold() ){
            std::cout << "Left Mouse Button is hold..." << std::endl;
        }

        glfwSwapBuffers(window);
    }

    glfwTerminate();
    return 0;
}

mouse.h

#ifndef MOUSE_H
#define MOUSE_H

#include <thread>
#include <atomic>
#include <chrono>
#include <GLFW/glfw3.h>

class Mouse
{
public:

    static Mouse* Instance(){
        if(s_pInstance == NULL)
            s_pInstance = new Mouse;
        return s_pInstance;
    }
    void init(GLFWwindow* window);
    bool isRightDown();
    bool isRightUp();
    bool isRightHold();

    bool isLeftDown();
    bool isLeftUp();
    bool isLeftHold();

    std::atomic<int> m_button, m_action, m_mode;
private:
    static void mouse_button_callback(GLFWwindow* window, int button, int action, int mods);


    bool m_isRightHold, m_isLeftHold;

    GLFWwindow* m_pWindow;
    Mouse();
    static Mouse* s_pInstance;
    std::thread m_OnHoldThread;
    void initThread();
    void updateThread();
    void update(int b, int a, int m);

};

#endif

mouse.cpp

#include "mouse.h"
#include <iostream>
#include <mutex> // std::mutex

std::mutex mtx;

Mouse* Mouse::s_pInstance = NULL;
bool g_LefFlag(false);
bool g_RightFlag(false);

Mouse::Mouse()
    : m_button(-1), m_action(-1), m_mode(-1),
      m_isRightHold(false), m_isLeftHold(false)
{
    initThread();
}

void Mouse::init(GLFWwindow* window)
{
    m_pWindow = window;
    glfwSetMouseButtonCallback(window, mouse_button_callback);
}

void Mouse::mouse_button_callback(GLFWwindow* window, int button, int action, int mods)
{
    Mouse::Instance()->update(button, action, mods);
}

void Mouse::initThread()
{
    m_OnHoldThread = std::thread(&Mouse::updateThread,this);
}


void Mouse::updateThread()
{
    //std::chrono::milliseconds delay(1100);

    while (true){

        //std::this_thread::sleep_for(delay);


        std::chrono::duration<int, std::ratio<1, 1000>> delay(900);

        bool sleep = true;
        auto start = std::chrono::system_clock::now();
        while(sleep)
        {
            auto end = std::chrono::system_clock::now();
            auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
            if ( elapsed.count() > delay.count() ){
                sleep = false;
            }
        }   

        mtx.lock();
        g_RightFlag = m_isRightHold;
        g_LefFlag   = m_isLeftHold;
        mtx.unlock();
    }   
}


bool Mouse::isRightDown()
{
    if (m_button == GLFW_MOUSE_BUTTON_RIGHT && m_action == GLFW_PRESS){

        m_isRightHold = true;
        m_button   = -1;
        m_action   = -1;
        m_mode     = -1;
        return true;
    }

    return false;
}

bool Mouse::isRightUp()
{
    if (m_button == GLFW_MOUSE_BUTTON_RIGHT && m_action == GLFW_RELEASE ){
        m_isRightHold = false;

        mtx.lock();
        g_RightFlag = m_isRightHold;
        mtx.unlock();

        m_button   = -1;
        m_action   = -1;
        m_mode     = -1;
        return true;
    }

    return false;
}

bool Mouse::isRightHold()
{
    if ( g_RightFlag ){
        m_button   = -1;
        m_action   = -1;
        m_mode     = -1;

        return true;
    }

    return false;
}


bool Mouse::isLeftDown()
{
    if (m_button == GLFW_MOUSE_BUTTON_LEFT && m_action == GLFW_PRESS){

        m_isLeftHold = true;
        m_button   = -1;
        m_action   = -1;
        m_mode     = -1;
        return true;
    }

    return false;
}
bool Mouse::isLeftUp()
{
    if (m_button == GLFW_MOUSE_BUTTON_LEFT && m_action == GLFW_RELEASE ){
        m_isLeftHold = false;

        mtx.lock();
        g_LefFlag = m_isLeftHold;
        mtx.unlock();

        m_button   = -1;
        m_action   = -1;
        m_mode     = -1;
        return true;
    }

    return false;
}
bool Mouse::isLeftHold()
{
    if ( g_LefFlag ){
        m_button   = -1;
        m_action   = -1;
        m_mode     = -1;

        return true;
    }

    return false;
}

void Mouse::update(int b, int a, int m)
{
    m_button   = b;
    m_action   = a;
    m_mode     = m;
}

1 个答案:

答案 0 :(得分:1)

为什么你不能获得m_isRightHold = true;事件的高分辨率时间,并比较自那时起每个主循环迭代时间过去的时间段m_isRightHold继续为true customDate确定鼠标按钮已被保持足够长的时间以考虑点击或保持发生?