SDL Mouse Click - c++

So, I'm currently working on an options menu for my game, I have a button that when pressed it changes it's text to the next resolution in an array, so basically the user presses this button to change their resolution to the next string in the array.
My problem is getting the click event.
Right now, when the user presses the button, it returns true while the mouse is down, instead of when the mouse is pressed. I want to only return true in the mouse event when the mouse is pressed.
I've looked around, and everything I've found seems to be similar to what I've done or, as I said, returning true while the mouse is down, instead of the initial click.
My events are handled in a EventManager singleton, and here are the functions that I see as necessary:
My update function, this is where the event is polled, it is worth noting I'm using a private SDL_Event named "e".
void EventManager::update(){
while(SDL_PollEvent(&e)){
SDL_GetMouseState(&mouseX, &mouseY);
switch(e.type){
case SDL_QUIT:
running = false;
}
}
}
My mousePress function, where I want a mouse press returned.
int EventManager::mousePress(){
if(e.type == SDL_MOUSEBUTTONDOWN){
return e.button.button;
}
return 0;
}

Instead of using SDL_GetMouseState(), which gets you the actual state of the mouse (thats probably where its name comes from ;) ), use the event you are polling. SDL should give you a SDL_MouseButtonEvent which contains the informations you need and should only be queued once.
See https://wiki.libsdl.org/SDL_MouseButtonEvent
Edit to clarify what i mean:
You would use something like this:
void EventManager::update(){
SDL_Event e;
while(SDL_PollEvent(&e)){
switch(e.type){
case SDL_QUIT:
running = false;
break;
case SDL_MOUSEBUTTONDOWN:
//do whatever you want to do after a mouse button was pressed,
// e.g.:
mousePress(e.button);
break;
}
}
}
Inside your mousePress-Function you can then test, which of the mouse buttons has been pressed:
void EventManager::mousePress(SDL_MouseButtonEvent& b){
if(b.button == SDL_BUTTON_LEFT){
//handle a left-click
}
}
This works, because SDL_PollEvent will only return exactly once for every Event. If theres no new Event, it will return an empty Event. So 1 click = 1 times SDL_PollEvent() with e being of type SDL_MOUSEBUTTONDOWN afterwards and 1 times SDL_PollEvent() with e being of type SDL_MOUSEBUTTONUP afterwards. If you call SDL_PollEvent() in between or afterwards, it will return 0 and leave e as an Empty Event, not calling the switch at all. If you respond to MOUSEBUTTONDOWN or MOUSEBUTTONUP or both is up to you...
I've also declared the SDL_Event a local variable to update(). Why? The Idea behind an Event is, that theres an Event-Object whenever some event has occured. Then you react to the event and forget about it. So theres no need to have a global variable. If you want to prevent constant construction/destruction, you can also declare it to be static. But thats just some hint, not related to your original question.

Related

Is there an event that fires from a C++ program when a control is about to lose focus?

I am trying to fix a validation bug in a MFC CEdit control. Currently, validation is performed in an OnChange event handler. But this does not work because it validates data before the user is finished entering it.
So, instead, I am trying to validate inside an OnKillFocus event handler. If validation fails, then I use GotoDlgCtrl() to return focus to the edit box that contained the invalid data. And when I call GotoDlgCtrl(), the kill focus event fires again, and I'm in an infinite loop.
So, I'd like to handle an event that fires just before the control loses focus, so that if I determine that the data is invalid, I can stop focus from leaving and instead get the user to enter correct data.
I know I've seen a Validating event someplace, but that was probably in the .Net world. But it offers the functionality I'm looking for.
Right-click the dialog resource and invoke Class Wizard:
Next, go to the Virtual Functions tab, locate PreTranslateMessage and add it:
Then, you can do something like this:
BOOL CTestDlgDlg::PreTranslateMessage(MSG* pMsg)
{
if (pMsg->message == WM_CHAR)
{
CWnd *pControl = GetDlgItem(IDC_EDIT1);
if (pControl->GetSafeHwnd() == pMsg->hwnd)
{
if (pMsg->wParam == _TINT('!'))
{
AfxMessageBox(_T("Not allowed ! character"));
return TRUE;
}
}
}
return CDialogEx::PreTranslateMessage(pMsg);
}
Normally the control is a member variable of type CEdit so you could compare against m_edit.GetSafeHwnd() instead.
Results:
Update
I realise you stated:
But this does not work because it validates data before the user is finished entering it.
You could use WM_KEYUP instead:
BOOL CTestDlgDlg::PreTranslateMessage(MSG* pMsg)
{
if (pMsg->message == WM_KEYUP)
{
CWnd *pControl = GetDlgItem(IDC_EDIT1);
if (pControl->GetSafeHwnd() == pMsg->hwnd)
{
CString str;
GetDlgItemText(IDC_EDIT1, str);
if (str.Find(_T("!")) >= 0)
{
AfxMessageBox(_T("Not allowed ! character"));
return TRUE;
}
}
}
return CDialogEx::PreTranslateMessage(pMsg);
}
That is give you a chance to validate after the display has been updated.
An alternative it to customize your DoDataExchange handler. In there you can validate as required. Then in your code you simple test the return value of UpdataData(TRUE) for FALSE.

SFML button triggers twice

I'm using SFML to write game and I got small problem with my button. Here is function I use to check if button is clicked:
bool InputManager::isSpriteClicked(sf::Sprite object, sf::Mouse::Button button, sf::RenderWindow &window) {
if (sf::Mouse::isButtonPressed(button)) {
sf::IntRect rect(object.getPosition().x, object.getPosition().y,
object.getGlobalBounds().width, object.getGlobalBounds().height);
if (rect.contains(sf::Mouse::getPosition(window))) {
return true;
}
}
return false;
}
It works almost fine, yet sometimes once I press this button, action is trigerred twice, like I double click it, even tho I didnt even release it yet. I tried to involve sf::Event::MouseButtonReleased but It actually wasnt helping too. What I want to achieve is of course just 1 action per 1 button press/release/whatever.
Here is example of my GameLoop if its needed
void GameState::handleUserInput() {
sf::Event evnt;
while (this->m_data->window.pollEvent(evnt)) {
if (this->m_data->input.isSpriteClicked(this->m_rollButton, sf::Mouse::Left, this->m_data->window)) {
m_gameEngine.startTurn(m_gameStatusBox);
}
}
void GameState::update(sf::Time dt) {
m_gameEngine.getActivePlayer().move(dt);
}
void GameState::draw() {
this->m_data->window.display();
}
Short answer:
if (event.type == sf::Event::MouseButtonPressed)
Long story By the way, it's second link in google....
You're mixing two different approaches to manage user input
Input by events: used to manage those things that happen once and you want to be notified
Real time input: basically, poll repeatedly for input status (in this case, the mouse)
The general rule:
if (sf::Mouse::isButtonPressed(sf::Mouse::Left))
// WHILE the left mouse button is being pressed, do something
if (event.type == sf::Event::MouseButtonPressed)
// WHEN the left mouse button has been pressed, do something
If you want to learn more, i recommend you to read this chapter (or better, the whole book) about SFML game dev.

How to get the released button inside MouseReleaseEvent in Qt

In MouseReleaseEvent(QMouseEvent *e), is there a way to know which button was released without using a new variable ? I mean something like in the MousePressEvent(QMouseEvent *e) with e.buttons().
I tried e.buttons() in the releaseEvent it's not working (which is logical).
e is already a variable. Just use:
void mouseReleaseEvent(QMouseEvent *e)
{
if (e->button() == Qt::LeftButton) // Left button...
{
// Do something related to the left button
}
else if (e->button() == Qt::RightButton) // Right button...
{
// Do something related to the right button
}
else if (e->button() == Qt::MidButton) // Middle button...
{
// Do something related to the middle button
}
}
A switch statement also works. I prefer the series of if -- else if because they make it easier to handle evente modifiers, i.e., e->modifiers() in order to check for alt or control clicks. The series of if's is short enough not to create any burden on the program.
EDIT: Note that you should use the button() function, not its plural buttons() version. See the explanation in #Merlin069 answer.
The problem in the posted code is this: -
if(e->buttons() & Qt::LeftButton)
As the Qt documentation states for the release event: -
... For mouse release events this excludes the button that caused the event.
The buttons() function will return the current state of the buttons, so since this is a release event, the code will return false, as it's no longer pressed.
However, the documentation for the button() function states:-
Returns the button that caused the event.
So you can use the button() function here.

Delete Key is not triggering KeyUp & KeyDown Event

I am currently dealing with a multi-form application and am having issue registering a del key press, the application that requires the del key is a form with a frame on it with objects painted on it that can be selected, upon pressing the del key the selected objects are to be deleted via a deleteObjects method. The code I am currently using is as follows
void __fastcall TF_Image::KeyUpKbd( WORD &Key )
{
if(Key == VK_DELETE || Key == VK_DKEY)
{
deleteSelectedObjects();
}
}
(Note: There are other paramenters in the function call but they aren't used)
TF_Image inherits from TFrame
I have tried mapping other keys other than the del key ie the D key and have found that the method is called with no problem. I have discovered that when pressing (physically) the del key the methods associated with KeyUp & KeyDown are not called.
Edit: So i've attempted to add the DeleteSelectedOb() method to my WndProc method without much luck either.
void __fastcall TF_ImgBrowserOA::WndProc(TMessage &Message)
{
if (Message.Msg == WM_KEYDOWN)
{
if (Message.WParam == VK_DELETE)
{
F_Image->DeleteSelectedOb();
}
}
//code that manages window resize
TForm::WndProc(Message);
}
The WndProc method doent appear to respond to keystrokes
So after cleaning up some code in some other modules and removing unneccessary menu's I decided to go back and look at this section again after I found a similar piece of code implementing a similar function, I couldn't see much difference between them and so I recompiled and attempted to run my Delete function from the KeyDown event and for some reason it just worked, I suspect it came down to an issue of another element holding focus in the Application. As a precaution I also called a SetFocus() to the frame in which I required this code to operate in. Its still a mystery to me why this didn't work intially though.
Here is a snippet for my TRichEdit control (Script_Edit).
TWndMethod *PrevWndProc = Script_Edit->WindowProc;
Script_Edit->WindowProc = MyWndProc;
void __fastcall My_Form::MyWndProc(TMessage &Message) {
switch (Message.Msg) {
case WM_KEYDOWN: {
// Check for DELETE and BACKSPACE keys
if( Message.WParam == VK_BACK ||
Message.WParam == VK_DELETE
) {
// Do whatever you need
}
break;
default:
// call default handler if not processed
PrevWndProc(Message);
}
}
You can't get much closer to the message core than this with VCL...

Mac event tap just delays discarded events

I'm trying to write some code that discards all keyboard and mouse events when enabled on Mac OSX 10.6. My code runs as the root user. The approach I'm taking is to create an event tap that discards all events passed to it (while enabled). The event tap callback function looks like this:
CGEventRef MyTapCallback(CGEventTapProxy proxy,
CGEventType type,
CGEventRef event,
void *refcon)
{
return CKeyLocker::isEnabled() ? NULL : event;
}
And the code I'm using to enable and disable the event tap looks like this:
void CKeyLocker::enable(bool bEnable)
{
if (bEnable == m_bEnabled)
return;
if (bEnable)
{
// which events are we interested in?
CGEventMask evMask = kCGEventMaskForAllEvents;
CFMachPortRef mp = CGEventTapCreate(kCGHIDEventTap,
kCGHeadInsertEventTap,
kCGEventTapOptionDefault,
evMask,
MyTapCallback,
NULL);
if (mp)
{
qDebug() << "Tap created and active. mp =" << mp;
m_enabledTap = mp;
m_bEnabled = true;
}
}
else
{
CGEventTapEnable(m_enabledTap, false);
CFRelease(m_enabledTap);
m_enabledTap =0;
m_bEnabled = false;
qDebug() << "Tap destroyed and inactive";
}
}
This approach works very well while the event tap is active - I can hammer on the keyboard and mouse as much as I want and no events make it through the system. However, when the tap is disabled all the keys I pushed while the tap was active appear in the current window. It's like the event tap is just delaying the events, rather than destroying them, which is odd, since the Mac documentation clearly states:
If the event tap is an active filter, your callback function should return one of the following:
The (possibly modified) event that is passed in. This event is passed back to the event system.
A newly-constructed event. After the new event has been passed back to the event system, the new event will be released along with the original event.
NULL if the event passed in is to be deleted.
I'm returning NULL, but the event doesn't seem to be deleted. Any ideas?
The linked comment does not have an answer from what I see, so I'll dump some info from what I've seen when poking around with this stuff.
First, I have much better luck with CGEventTapCreateForPSN. It's as if the system gives you some leeway for restricting your tap. However, from this example it looks like this is not sufficient.
Next up - and this /may/ be all you need... In your call back, you probably want (and may need) to check for the following events:
switch (type)
{
case kCGEventTapDisabledByTimeout:
case kCGEventTapDisabledByUserInput:
{
CFMachPortRef *pTap = (CFMachPortRef*)refcon;
CGEventTapEnable( *pTap, true );
return NULL;
}
default:
break;
}
Regardless of what the various documentation does or doesn't say, it's been my observation that the OS feels like it's 'probing' for bad callbacks; basically disabling event tap callbacks that are universally eating events. If you re-register in these cases the OS seems to be ok with it, as if saying: OK, you seem to know what you're doing, but I'll probably poke you again in a bit to make sure.
It's really strange, we use event taps for the same purpose (input blocking in a given scenario) and works perfectly 10.4 - 10.8.2. excpet one thing, it should not block or receive events from a password dialog (which is not a big surprise)
What I can see now is different compared to you sample is:
we use kCGTailAppendEventTap instead of kCGHeadInsertEventTap (this should not matter)
we do some event logging in the installed callback
we have some user event data in some self injected events, that filtered out, but apart from this we simply return NULL to drop an unwanted event (like you do), I can confirm, not all events are ignorable!
we turn on/off the event tap this way:
bool SetInputFilter(bool bOn)
{
bool result = false;
CFRunLoopRef runLoopRef = CFRunLoopGetMain();
if (bOn) {
// Create an event tap.
CGEventMask eventMask = kCGEventMaskForAllEvents;
if ((m_eventTapInput = CGEventTapCreate(kCGHIDEventTap,
kCGTailAppendEventTap,
kCGEventTapOptionDefault,
eventMask, CGInputEventCallback, this)) == NULL) {
Log(L"Failed to create event tap");
return result;
}
// Create a run loop source.
m_runLoopEventTapSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, m_eventTapInput, 0);
CFRelease(m_eventTapInput); // CFMachPortCreateRunLoopSource retains m_eventTapInput
if (m_runLoopEventTapSource == NULL) {
Log(L"Failed to create run loop source for event tap");
return result;
}
// Add to the current run loop.
CFRunLoopAddSource(runLoopRef, m_runLoopEventTapSource, kCFRunLoopCommonModes);//kCFRunLoopDefaultMode);
CFRelease(m_runLoopEventTapSource); // CFRunLoopAddSource retains m_runLoopEventTapSource
result = true;
}
else {
// Disable the event tap.
if (m_eventTapInput)
CGEventTapEnable(m_eventTapInput, false);
// Remove our run loop source from the current run loop.
if (runLoopRef && m_runLoopEventTapSource) {
CFRunLoopRemoveSource(runLoopRef, m_runLoopEventTapSource, kCFRunLoopCommonModes);//kCFRunLoopDefaultMode);
m_runLoopEventTapSource = NULL; // removing m_runLoopEventTapSource releases last reference of m_runLoopEventTapSource too
m_eventTapInput = NULL; // removing m_runLoopEventTapSource releases last reference of m_eventTapInput too
}
}
return result;
}
I can verify that returning NULL does effectively delete some events, but i have also seen times when it does not, exactly how it decides what deletions to permit is unclear but it looks like mass deletions seem to be prevented e.g.: when you delete more than 100 events or so in a row.