I am writing a program that scans if the Left Mouse Button is held down, then sends a left mouse button up and continues after that. The problem is, since I am sending a left mouse button up, the program will not continue because the left mouse button is not being held down anymore.
Here is some pseudo:
if(GetKeyState(VK_LBUTTON) < 0){
Sleep(10);
mouse_event(MOUSEEVENTF_LEFTUP, p.x, p.y, 0, 0);
//Rest of code
}
How can I detect the left mouse button being down after this? Would I have to use a driver?
Just use a flag:
bool lButtonWasDown=flase;
if(GetKeyState(VK_LBUTTON) < 0){
Sleep(10);
lButtonWasDown = true;
mouse_event(MOUSEEVENTF_LEFTUP, p.x, p.y, 0, 0);
}
if (lButtonWasDown)
{
// rest of the code for lButtonWasDown = true
}
From reading your description of the program only here is my implementation.
Using the Windows API:
while (true) {
//check if left mouse button is down
if (GetKeyState(VK_LBUTTON) & 0x8000) {
//send left mouse button up
//You might want to place a delay in here
//to simulate the natural speed of a mouse click
//Sleep(140);
INPUT Input = { 0 };
::ZeroMemory(&Input, sizeof(INPUT));
Input.type = INPUT_MOUSE;
Input.mi.dwFlags = MOUSEEVENTF_LEFTUP;
::SendInput(1, &Input, sizeof(INPUT));
}
}
You can place this code into a function that you call in a thread if you want to do other things while you forcibly stop people from clicking and dragging.
void noHoldingAllowed() {
//insert code here used above...
}
int main(void) {
std::thread t1(noHoldingAllowed);
//other stuff...
return 0;
{
I haven't tested this code, but it should work
while(programIsRunning)
{
if(GetKeyState(VK_LBUTTON) < 0){
Sleep(10);
mouse_event(MOUSEEVENTF_LEFTUP, p.x, p.y, 0, 0);
// rest of the code
}
It should work since if you have the Lmouse button held while the loop reruns if will trigger the if statement which will then trigger a mouseup event.
note:
You might be able to do while(GetKeyState(VK_LBUTTON) < 0){//code here}
Related
I have CMFCRibbonBar control. I need to create my custom tooltip. My tooltip derives from CMFCToolTipCtrl and works quite well. But...
When I hover a ribbon button, tooltip shows up. That's great. But when I move the mouse out of the button, tooltip is closed. That is not what I want. I just need to be able to move the mouse on the tooltip and click the link that is on the tooltip. Imagine this is some kind of interactive tooltip. What can I do to achieve that?
OK, I've done something that is useful, but the outcome is not satisfying 100%.
So, first of all, create your own tooltip, inheriting from CMfcToolTipCtrl.
The idea is that:
- user may want to interact with your tooltip, or not. So we have to create some smart way from closing and showing the tooltip.
- We can assume, that when user hovers the tooltip with mouse, then he wants to interact.
Unfortunately whenever user moves the mouse from the ribbon button, the tooltip dissapears. But sometimes we can catch MouseMove inside it. But it's rather rare. So, we have to get the moment, when tooltip is closed by a system.
There is such a message that we can add to message map:
ON_NOTIFY_REFLECT(TTN_POP, &CAsInteractiveToolTip::OnPop)
Now, our OnPop will look like that (I am using pImpl idiom):
void CAsInteractiveToolTip::OnPop(NMHDR* pNMHDR, LRESULT* pResult)
{
if (m_pImpl->m_forceClose)
{
CMFCToolTipCtrl::OnPop(pNMHDR, pResult);
m_pImpl->m_forceOpened = false;
m_pImpl->m_forceClose = false;
m_pImpl->StopForceOpenTimer();
}
else
{
m_pImpl->StartForceOpenTimer();
}
*pResult = 0;
}
Now, what's happening here is:
- when tooltip is being closed, check if it's force closed by our code. If not, it means that it's closed by system. In such case, we have to give the user a chance to hover the mouse over our tooltip. So, we have to show the tooltip again (force it to show). This is done in timer method. StartForceOpenTimer is simple method that starts the timer:
void StartForceOpenTimer()
{
if (!m_forceOpenTimerActive)
{
m_self.SetTimer(IDT_FORCE_OPEN_TIMER, 100, (TIMERPROC)NULL);
m_forceOpenTimerActive = true;
}
}
Now, the magic starts in timer method:
void CAsInteractiveToolTip::OnForceTimer()
{
static DWORD waitForUserStartTime = 0;
static bool waitingForUserReaction = false;
if (!waitingForUserReaction)
{
//open and give the user chance to mouse move over it within 0.5 seconds
SetWindowPos(&wndTopMost, -1, -1, -1, -1, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_SHOWWINDOW);
waitForUserStartTime = GetTickCount();
waitingForUserReaction = true;
return;
}
if (GetTickCount() - waitForUserStartTime > 500)
{
m_pImpl->StopForceOpenTimer();
m_pImpl->m_forceClose = true;
waitingForUserReaction = false;
m_pImpl->PopToolTip();
return;
}
if (m_pImpl->m_doForceOpen)
{
m_pImpl->StopForceOpenTimer();
waitingForUserReaction = false;
m_pImpl->m_forceOpened = true;
}
}
Overall idea is:
- force to show the tooltip
- wait about 0.5 second for a user to hover the mouse
- if user hovers the mouse over tooltip window, we can assume that he wants to interact. So we can leave the window opened.
- if user doens't interact with the window within 0.5 second, just close the tooltip again.
Now, PopToolTip method just starts another timer with interval of 100 ms.
And here is the other part of the magic:
void CAsInteractiveToolTip::OnPopTimer()
{
m_pImpl->StopForceOpenTimer();
KillTimer(IDT_POP_TIMER);
//Pop();
m_pImpl->m_forceClose = true;
m_pImpl->m_hdr.idFrom = 2;
m_pImpl->m_hdr.hwndFrom = GetSafeHwnd();
m_pImpl->m_hdr.code = (int)TTN_POP; //4294966774
GetParent()->SendMessage(WM_NOTIFY, 1, (LPARAM)&m_pImpl->m_hdr);
//GetParent()->SendMessage(WM_NOTIFY, 2, (LPARAM)&m_pImpl->m_hdr);
ShowWindow(SW_HIDE);
}
Now, this method should just pop (hide) the tooltip. But for some reason in my case calling Pop() method does nothing. So I would have to send WM_NOTIFY message with appropriate parameters (they are taken from my debug observations).
Now, OnPop will start again, but this time m_forceClose is set to true, so the tooltip will not show again (the first timer will not run).
Now the third part of the magic - Mouse Move. Just add it to your message map:
ON_WM_MOUSEMOVE()
And the method:
void CAsInteractiveToolTip::OnMouseMove(UINT nFlags, CPoint point)
{
m_pImpl->m_doForceOpen = true; //let the first timer know, that user wants to interact
CMFCToolTipCtrl::OnMouseMove(nFlags, point);
}
And you can just hide the tooltip when user clicks on it. Just:
void CAsInteractiveToolTip::OnLButtonDown(UINT nFlags, CPoint point)
{
m_pImpl->m_forceClose = true;
m_pImpl->PopToolTip();
}
This is not the ideal solution, but it somehow works. If anyone has any suggestions, I will be happy to hear them :)
I'm adapting some skeleton code to learn how OpenGL works and have in SphericalCameraManipulator.cpp which allows me to pan and tilt the camera while I hold down right mouse:
void SphericalCameraManipulator::handleMouseMotion(int x, int y)
{
//Motion
float xMotion = (float)x - previousMousePosition[0];
float yMotion = (float)y - previousMousePosition[1];
if(reset)
{
xMotion = yMotion = 0.0;
reset = false;
}
this->previousMousePosition[0] = (float)x;
this->previousMousePosition[1] = (float)y;
//Right Button Action
if(this->currentButton == GLUT_RIGHT_BUTTON && this->currentState == GLUT_DOWN)
{
this->pan -= xMotion*this->panTiltMouseStep ;
this->tilt += yMotion*this->panTiltMouseStep ;
}
//Enforce Ranges
this->enforceRanges();
}
I deleted the left mouse action as I don't want it to control the camera, and it now actions a command in the main code body.
//Left Button Action
if(button == GLUT_LEFT_BUTTON && state == GLUT_DOWN)
{
... //game action
}
My problem is that when I click the left mouse, the press of the right mouse is cancelled, and I must release and repress the right mouse to continue controlling the camera.
Is there a way to prevent this from happening? It interrupts the flow of the game. I'm using GLUT
Rabbid76's comment saying there is no mouse hold event set me on the right path.
I wrote a small function that simply recorded the last state of mouse button 2 (the 'camera look around' button):
void SphericalCameraManipulator::handleMouse(int button, int state)
{
this->currentButton = button;
this->currentState = state;
//to enable mouse look around
if (this->currentButton == 2) //right mouse
this->lastLookState = this->currentState;
if(this->currentState == GLUT_UP)
{
reset = true;
}
}
This mean even when the current state was regarding the left mouse button (game action) it was still possiblew to look around since a 'mouse two button up' event had not occured:
this->currentButton = 2;
this->currentState = 1;
So I'm working on a tower defense game. And I need to select a tile where the user clicks the mouse (right now i'm just drawing a border around the tile). Now the code I have works sometimes and I'm not sure how to get it to work better. Right now with the code I have when I click on an area, the tile gets selected and sometimes it deselects right away and other times it doesn't. Almost like I'm holding the mouse down and it just keeps "clicking" the mouse. What I need is for it to select the tile without deselecting it no matter how long the mouse button is being pressed. My mouse input class is set up like this:
MouseInput.h
#pragma once
#include <SDL.h>
class MouseInput
{
public:
MouseInput();
~MouseInput();
void Update();
bool GetMouseButton(int index);
SDL_Event GetEvent();
bool GetPressed();
bool GetReleased();
private:
void GetButtonStates();
private:
bool mouseArray[3];
int x, y;
bool justReleased;
bool justPressed;
SDL_Event e;
};
MouseInput.cpp
#include "Input.h"
MouseInput::MouseInput()
:
x(0),
y(0),
justReleased(false),
justPressed(false)
{
SDL_PumpEvents();
for (int i = 0; i < 3; i++)
{
mouseArray[i] = false;
}
}
MouseInput::~MouseInput()
{
}
void MouseInput::Update()
{
SDL_PollEvent(&e);
justReleased = false;
justPressed = false;
if (e.type == SDL_MOUSEBUTTONDOWN && e.button.button == SDL_BUTTON_LEFT)
justPressed = true;
else if (e.type == SDL_MOUSEBUTTONUP && e.button.button == SDL_BUTTON_LEFT)
justReleased = true;
}
bool MouseInput::GetPressed()
{
return justPressed;
}
bool MouseInput::GetReleased()
{
return justReleased;
}
bool MouseInput::GetMouseButton(int index)
{
if (index <= 3 && index >= 1)
{
return mouseArray[index - 1];
}
return false;
}
SDL_Event MouseInput::GetEvent()
{
return e;
}
void MouseInput::GetButtonStates()
{
SDL_PollEvent(&e);
if (e.type == SDL_MOUSEBUTTONDOWN)
{
// 1 = Left; 2 = Center; 3 = Right
mouseArray[e.button.button - 1] = true;
}
if (e.type == SDL_MOUSEBUTTONUP)
{
mouseArray[e.button.button - 1] = false;
}
}
In my Game class I have setup the mouseinput as well as a variable to store the coordinates of the tile that was clicked. I also Have a variable pressed to check to see if the mouse was pressed. In my updating function I check if the mouse was not pressed. If the mouse was not pressed, then I check if the mouse is pressed that way the game can get the coordinates of the mouse position. I know that sounds crazy so here is some of my code:
std::pair<int, int> coords; // Will store the coordinates of the tile position that the mouse rests in
bool pressed; // If the mouse was pressed (i should call it toggle because it's true if the mouse was pressed once, then false if the mouse was pressed again, then true...ect.)
Updating function
Mouse Class
bool clicked; // In my custom Mouse.h file and set to false in constructor
MouseInput input; // In my custom Mouse.h file
Mouse class Update()
input.Update(); // Calls the MouseInput updating
if (input.GetPressed() && !clicked)
{
clicked = !clicked;
printf("OK");
}
//else if (clicked && input.GetReleased())
else if (clicked && input.GetPressed()) // Somewhat works. It works almost like before. If I click the mouse it shows up then sometimes when I click again it works, and other times I have to click a couple times
{
clicked = !clicked;
}
In my game class I just check if mouse clicked is true, then draw what i need
if (mouse.GetClicked())
{
// Draw what I need here.
// Currently is only drawn when I hold down the mouse
// And not drawn when I release the mouse.
}
Then when I'm drawing the square around the tile, I only draw it if pressed = true. Otherwise it is not drawn. This code works sometimes, and other times it doesn't. In order to get the effect I want, I have to real quickly click the mouse. If I don't, sometimes it appears as if I'm holding the mouse button down and the square just flashes.
The issue is that the "mouse-down" event is exactly that - an event. It should happen once, be handled, and then not occur until the button is physically pressed again. With your code, once pressed it'll set the internal state to "pressed" and then not change it until it's released. Which is okay, except your code polling this internal state treats it as if it's an event, not as state, so every time the update function runs while the mouse button is pressed, it'll see a "new button press".
This (simplified version of your code that uses one button and an idea of showing an image only when a button's pressed) might help you visualise it:
bool buttonPressed = false;
bool imageShown = false;
while (true)
{
SDL_Event e;
SDL_PollEvent(&e);
if (e.type == SDL_MOUSEBUTTONDOWN)
buttonPressed = true;
else if (e.type == SDL_MOUSEBUTTONUP)
buttonPressed = false;
if (!imageShown && buttonPressed)
imageShown = true;
else if (buttonPressed)
imageShown = false;
}
Do you see how each time the loop runs, even if no events have been fired this iteration, the state remains? This would cause the imageShown variable to keep toggling on and off each iteration, until a "button up" event is fired to reset the state.
You should probably rework a bit of your input controller - you need to expose a way to differentiate from "button being pressed right now" and "button that's been pressed for a while".
An idea would be to provide eg. ButtonsPressed and ButtonsJustPressed members (although I'm sure you can think of more appropriate names). The ButtonsJustPressed members should be reset to false each time you call GetButtonStates, so they're only true if on that specific iteration of the loop, they've been pressed, while the ButtonsPressed members are exactly what you're doing right now.
A tweaked version of the above to take this into account:
bool buttonJustPressed = false;
bool buttonJustReleased = false;
bool imageShown = false;
while (true)
{
// As far as we know, this iteration no events have been fired
buttonJustPressed = false;
buttonJustReleased = false
SDL_Event e;
SDL_PollEvent(&e);
if (e.type == SDL_MOUSEBUTTONDOWN)
buttonJustPressed = true;
else if (e.type == SDL_MOUSEBUTTONUP)
buttonJustReleased = true;
if (!imageShown && buttonJustPressed)
imageShown = true;
else if (imageShown && buttonJustReleased)
imageShown = false;
}
See how this version will only change the visibility of the image if a button has just been pressed/released - if the button's being held down, the events aren't fired, so the state remains false, and the image's state remains the same.
So basically am learning OpenGL and the GLFW libraries from the tutorial on page: http://www.opengl-tutorial.org/beginners-tutorials/tutorial-6-keyboard-and-mouse/
My problems is with this less lesson showing the control of camera movement with mouse.
Basicaly it makes the application to get "FPS" like camera, with disabled cursor being moved on center of screen with each frame. But the camera gets crazy when we lose focus on the window and then it regains. For example if we click on the window to regain focus away from the middle of view, the camera will be moved by big amount. I tried to fix this issue with adding window focus callback:
void window_focus_callback(GLFWwindow* window, int focused){
if (focused)
{
//center mouse on screen
int width, height;
glfwGetWindowSize(window, &width, &height);
glfwSetCursorPos(window, 1024 / 2, 768 / 2);
windowFocused = true;
}
else
{
windowFocused = false;
}
And in the main application loop:
if(windowFocused) computeMatricesFromInputs();
But for some reason this solution doesnt work.
Is there any way to fix this issue using glfw?
Question is a bit old, but I recently suffered a similar issue. So just sharing, more solutions exist.
I use GLFW_CURSOR_DISABLED. In this mode the mouse position is not (yet) updated when you receive the 'on' focus event, so call to GetCursorPos delivers the previous value. The new cursor position arrives in the MouseMove callback AFTER the 'on' focus event.
I solved it by keeping track of the regain of focus and use this int he OnMouseMove callback to either dispatch a MouseInit (to snap the cursor) or a regular MouseMove.
This way I can ALT+TAB out of my window and return with the cursor somewhere else without nasty jumps/rotations of the camera.
void InputManagerGLFW::Callback_OnMouseMove(
GLFWwindow* window,
double xpos, //
double ypos) //
{
if (!mFocusRegained)
{
mMouseBuffer.Move(xpos, ypos);
}
else
{
mFocusRegained = false;
mMouseBuffer.Init(xpos, ypos);
}
}
void InputManagerGLFW::Callback_OnFocus(
GLFWwindow* window,
int focused)
{
if (focused)
{
// The window gained input focus
// Note: the mouse position is not yet updated
// the new position is provided by the mousemove callback AFTER this callback
Log::Info("focus");
// use flag to indicate the OnMouseMove that we just regained focus,
// so the first mouse move must be handled differently
mFocusRegained = true;
// this will NOT work!!!
// double x,y;
// glfwGetCursorPos(window,&x,&y);
// mMouseBuffer.Init(x,y);
}
else
{
// The window lost input focus
Log::Info("focus lost");
}
}
I'm currently working on project which will have two RichEdit controls one close to the other: let's say RichEdit1 is on left and RichEdit2 is on the right.
The user scenario I want to enable in the project is:
User mouse LButton down at somewhere in RichEdit1, e.g. before the 3rd char, in total 7 chars.
User drag the mouse to RichEdit2, e.g. after the 6th char, in total 11 chars.
User mouse LButton up.
I want to see both RichEdit1 3rd char to end and RichEdit2 begin to 6th char are selected.
Currently I notice that once mouse LButton down on RichEdit1, after I move the mouse to RichEdit2, the RichEdit2 could not receive mouse event before I release mouse.
Any suggestion will be appreciated. Thank you!
When the mouse button is pressed down on RichEdit1, it captures the mouse, thus subsequent mouse messages are sent to RichEdit1 until the mouse button is released. That is why RichEdit2 does not receive any mouse events while dragging over RichEdit2.
You will have to process the mouse move messages in RichEdit1 and check if their coordinates are outside of RichEdit1's client area. If so, convert them into coordinates relative to RichEdit2's client's area and then send EM_SETSEL/EM_EXSETSEL messages to RichEdit2 as needed. For example:
int RichEdit2StartIndex = -1;
...
// in RichEdit1's message handler...
case WM_MOUSEMOVE:
{
if ((wParam & MK_LBUTTON) == 0)
break;
int xPos = GET_X_LPARAM(lParam);
int yPos = GET_Y_LPARAM(lParam);
RECT r;
GetClientRect(hwndRichEdit1, &r);
if (xPos < (r.right - r.left))
{
if (RichEdit2StartIndex != -1)
{
SendMessage(hwndRichEdit2, EM_SETSEL, -1, 0);
RichEdit2StartIndex = -1;
}
}
else
{
POINT pt;
pt.x = xPos;
pt.y = yPos;
MapWindowPoints(hwndRichEdit1, hwndRichEdit2, &pt, 1);
POINTL pl;
Pl.x := pt.x;
Pl.y := pt.y;
int idx = SendMessage(hwndRichEdit2, EM_CHARFROMPOS, 0, (LPARAM)&Pl);
if (idx != -1)
{
if (RichEdit2StartIndex == -1)
RichEdit2StartIndex = idx;
SendMessage(hwndRichEdit2, EM_SETSEL, RichEdit2StartIndex, idx);
}
}
break;
}
Vice versa when dragging a selection from RichEdit2 to RichEdit1.
And make sure that both RichEdit controls have the ES_NOHIDESEL style applied so you can see the selection in both controls at the same time.