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

How to implement consistent approach for detecting mouse button being hold

本文关键字:鼠标 检测 按钮 方法 何实现 实现      更新时间:2023-10-16

我正在尝试实现鼠标类来处理与鼠标操作相关的任何问题。除了检测到鼠标按钮被按住外,一切正常。该库不为鼠标提供此功能,但有一个键盘事件选项(即通过 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-loop比人类释放按钮的反应更快,因此,单击将被视为保持事件。我考虑了另一种方法,即更新全局布尔变量(即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 事件(即以毫秒为单位)。如何改进处理保持鼠标事件的方法?

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

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