c++ - 如何实现一致的方法来检测鼠标按钮是否被按住

标签 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 循环比人类释放按钮的 react 更快,因此,单击将被视为保持事件。我考虑过另一种方法,即更新全局 bool 变量(即 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();
    }   

此解决方案比第一种方法更好,但仍然不一致,因为线程不同步。在某些时候,当我只需单击一次时,就会检测到保持事件(即以毫秒为单位)。如何改进处理按住鼠标事件的方法?

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;
}

鼠标.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

鼠标.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;
}

最佳答案

为什么不获取 m_isRightHold = true; 事件的高分辨率时间,并在 m_isRightHold 继续的每个主循环迭代中比较从那时起耗时段为 true 以确定鼠标按钮已按住足够长的时间以考虑单击或按住的发生?

关于c++ - 如何实现一致的方法来检测鼠标按钮是否被按住,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44348886/

相关文章:

java - Java 中具有并行化的链式过滤器

javascript - Node.js 应用程序中的阻塞功能

c# - Control.MouseWheel 事件

ios - 通过cycript在越狱设备上模拟鼠标点击事件

javascript - 使用 mousemove 移动背景后的右边距

c++ - 如何操纵内容 ostream

c++ - 在 Linux 上测试按键是否被按下而不阻塞

c++ - 为什么 make_tie 不是一回事?

c++ - Eigen 中立方根的性能改进

C#锁单行(if语句)