Handling top 'x' close button click in SDL2 window - events

There are plenty of questions and answers on pressing the close ('x') button on the last existing window in an SDL2 program. In that case, the SDL_QUIT event gets triggered (see https://wiki.libsdl.org/SDL_EventType) and you can handle the event via that. However, if you have two or more windows open, and you press the close button on one of them, which event gets triggered? I cannot find anything on this. I have tried using SDL_WINDOWEVENT_CLOSE to catch the event, but that does not seem to work either.

A short working C++ snippet here below: clicking on the "X" button closes both open windows and quit the application.
switch (m_event.type) {
case SDL_KEYDOWN:
switch (m_event.key.keysym.sym) {
case SDLK_m: // enter menu state
case SDLK_ESCAPE:
machine->popState();
break;
default:
break;
}
break;
case SDL_WINDOWEVENT:
switch (m_event.window.event) {
case SDL_WINDOWEVENT_CLOSE: // exit game
machine->quit();
break;
default:
break;
}
break;
default:
// logFileStderr("TutorialState unknown event...\n");
break;
}

Related

Calling ShowWindow(SW_SHOW) on ATL modeless dialog not bringing window to front

I have a modeless dialog which I am showing and hiding programmatically from a worker thread. The problem is that the CWindow::ShowWindow(SW_SHOW) function is sometimes bringing the dialog to the front, and sometimes not.
Task: Show/Hide a simple logging & control window for an ATL COM object (dll) that runs in a separate thread. The COM object isn't a control: it is being used to wrap processing code for use in Excel VBA.
I've created a CMyDialog class derived from the CAxDialogImpl template. Then I'm using the CWorkerThread template for the worker thread. This is the Execute block (ie the function that runs in the worker thread).
HRESULT CLogProcessor::Execute(DWORD_PTR dwParam, HANDLE hObject)
{
m_bRunning = true; //Winging it as this isn't thread-safe, but I don't think that's the issue.
if (m_pDlg == 0)
{
m_pDlg = new CMyDialog(); //My dialog class
m_pDlg->SetStopEvent(m_hStopEvent); //Save the handle of the stop event in the Dialog class
m_pDlg->Create(NULL); //Create with no parent
m_pDlg->ShowWindow(SW_SHOW); //Show the window
}
//Loop here until told to stop by the Stop Event being signalled in the calling thread
while (WaitForSingleObject(m_hStopEvent, 0) == WAIT_TIMEOUT) //Polling Stop Event
{
//Have to keep the message loop going!
MSG uMsg;
while (PeekMessage(&uMsg, m_pDlg->m_hWnd, 0, 0, PM_REMOVE))
{
TranslateMessage(&uMsg);
DispatchMessage(&uMsg);
}
//The thread uses a protected "stack" of string messages to write to my logging window
string strMsg;
while (m_MsgStack.PopFront(strMsg))
{
std::wstring ws = std::wstring(strMsg.begin(), strMsg.end());
CWindow eb(m_pDlg->GetDlgItem(IDC_EDIT1));
eb.SendMessage(EM_REPLACESEL, 0, (LPARAM)ws.c_str());
}
}
//Stop event has been signalled so clear up the dialog
m_pDlg->DestroyWindow();
delete m_pDlg;
m_pDlg = 0;
m_bRunning = false;
return S_OK;
}
My Log object has two functions, StartLog() and StopLog() which are called by the COM object which is doing the main work of data processing.
void CLogProcessor::StartLog()
{
if (!m_bRunning)
{
::SetEvent(m_hStartEvent);
}
}
void CLogProcessor::StopLog()
{
if (m_bRunning)
{
::SetEvent(m_hStopEvent);
}
}
This works fine. I run VBA code in an Excel spreadsheet with buttons that call (via the COM object) StartLog() and StopLog(). The first time the Log Dialog appears all is good: it is activated and comes to the front. Firing StopLog() closes the dialog and it disappears. But the second time I call StartLog() the dialog window appears but it is not at the front, but behind other windows. I have tried a CWindow::BringWindowToTop() and/or SetFocus() call straight after the ShowWindow() but it makes no difference.
Another interesting feature is that my dialog has a system menu and so an 'X' to close it in the top-right hand corner. In the Dialog's message map, I am handling the WM_CLOSE message:
BEGIN_MSG_MAP(CMyDialog)
... Other entries
MESSAGE_HANDLER(WM_CLOSE, OnClose)
...
CHAIN_MSG_MAP(CAxDialogImpl<CMyDialog>)
END_MSG_MAP()
With OnClose() setting the Stop Event handle (previously passed to the Dialog object). Ie doing pretty much the same as the StopLog() function.
LRESULT CMyDialog::OnClose(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
::SetEvent(m_hStopEvent);
return 0;
}
If I close the log dialog with the mouse click, it goes away as before. BUT the next time I call StartLog() the dialog does what it is supposed to do and comes to the front, which is the action I want. So I replaced the StopLog() code with a SendMessage(WM_CLOSE) to my dialog: it closed the window, but again it did not reappear on top. Another difference is that using the Close X on the dialog means that the Dialog has the focus when it is closed. Closing it from the Excel button means that the Excel window has the focus.
I realize this is rather a long question, and thanks very much if you have soldiered down to the end! I'd been keen for any solution / insight.
Update: I didn't solve the problem as stated, just re-worked the code to use a modal Dialog box in the worker thread. When I am done with the dialog, I PostMessage(WM_CLOSE) from the main thread: the dialog ends and the worker thread finishes (having set an event to say it is done, so that the thread is not destroyed before the Dialog is cleaned up).
The dialog polls the stack of strings to display in response to a WM_USER+xxx message, posted from the main thread using PostMessage();
I still have the same issue that the dialog doesnt appear on top the second time I run the worker thread, but this time adding a ShowWindow(SW_SHOW) in the OnInitDialog() handler seems to bring the dialog to the top when it appears.
It also means I don't need my own message loop.

Can't change edit text background when using Common Controls 6.0

I'm working on a Win32 C++ GUI Desktop application.
All the edit text are created statically in the resource file and their background is changed in the DialogBox routine using the WM_CTLCOLOREDIT and WM_CTLCOLORSTATIC messages.
case WM_CTLCOLOREDIT:
if (lParam == (LPARAM)Edit1Hwnd)
return SetBkColor((HDC)wParam, GuiColors[RED]);
else if (lParam == (LPARAM)Edit2Hwnd)
return SetBkColor((HDC)wParam, GuiColors[GREEN]);
break;
case WM_CTLCOLORSTATIC:
if (lParam == (LPARAM)Edit3Hwnd)
return SetBkColor((HDC)wParam, GuiColors[BLUE]);
else if (lParam == (LPARAM)Edit4Hwnd)
return SetBkColor((HDC)wParam, GuiColors[YELLOW]);
break;
When the window loads I can see that all the edit have the correct background color.
The problem rises when I use Common Controls 6.0 (which I need in order to load a BMP as a list-view background).
In order to enable Common Controls 6.0 I do:
#pragma comment(linker,"\"/manifestdependency:type='win32' \name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
If I run the program with the Common Controls enabled, all the edit text appear with a white background. But I can change the edit color selecting it with the mouse cursor.
Searching on the site I found this question.
It confirms the behaviour I experience, but it doesn't give a solution for changing the edit background color when it doesn't have the focus.
Any help is appreciated.
EDIT:
Thanks RbMm for your answer, I changed the code to
case WM_CTLCOLOREDIT:
if (lParam == (LPARAM)Edit1Hwnd)
return (LRESULT)redBrush;
break;
Where redBrush is declared and created like this
static HBRUSH redBrush = NULL;
redBrush = CreateSolidBrush(GuiColors[RED]);
The edit background color is properly red painted at the start, but when the edit gets the focus it turns white again (it turns back to red when the focus is lost).
EDIT 2:
Thanks zett42, now it works!
case WM_CTLCOLOREDIT:
if (lParam == (LPARAM)Edit1Hwnd){
SetBkColor((HDC)wParam, GuiColors[RED]);
return (LRESULT)redBrush;
}
break;

How to make After Effects UI button to respond immediately while the script is processing

When I run this code in ExtendScript Toolkit (Target app - After Effects CC 2015) the cancel button is not responding until the progress bar has finished.
I would like to make it work so that the cancel button would respond immediately.
I know that pressing the "Esc" key works immediately, so it should be possible to make the cancel button work as well.
testWindow = new Window ("palette", "Processing:", undefined);
pb = testWindow.add("progressBar",undefined, 0);
cancelButton = testWindow.add("button", undefined, "Cancel");
testWindow.show();
cancelButton.onClick = function(){
alert("Cancel");
}
i = 1;
while (i <= 1000000) {
pb.value = Math.round(100*i/1000000);
testWindow.update();
i++
}
I think it has always been so: while a script is running, the user interface is on hold and non responding. Adding a Cancel button somewhere cannot work.
The only way i know for a user to cancel the execution of script is the ESC key.

Using a button and a timer, have an annoying bug

I'm using a button to activate a timer. In that button's code, it turns on the timer. I'll attach the code, though I doubt it'll make sense out of context.
- (IBAction)btnStartWasClicked:(NSButton *)sender {
if ([btnStartTitle.title isEqualToString:#"Start"])
{
NSLog(#"Debug: Start button was clicked."); //Debug
//Changes the Start button to say Stop
[btnStartTitle setTitle:#"Stop"];
started = TRUE;
[self tick:nil];
NSLog(#"Debug: Timer has started."); //Debug
[btnReset setEnabled:FALSE];
[btnLap setEnabled:TRUE];
if (lapTimerIsPaused == TRUE)
{
lapTimerIsRunning = TRUE;
[self tickAfterLap:nil];
}
}
else
{
NSLog(#"Debug: Stop button was clicked."); //Debug
//Changes the Stop button to say Start
[btnStartTitle setTitle:#"Start"];
started = FALSE;
NSLog(#"Timer has been stopped."); //Debug
[btnReset setEnabled:TRUE];
lapTimerIsRunning = FALSE;
lapTimerIsPaused = TRUE;
[btnLap setEnabled:NO];
}
I currently have a nasty bug I'm not quite sure how to get rid of. If the user double-clicks the start button really quickly, it will either:
A) Do nothing, just change the start button to say stop.
B) Make the timer count twice as fast (if the double-click is REALLY quick.) THIS ONLY HAPPENS WHEN THE START BUTTON IS SET TO SAY STOP, NOT START.
My bug is B. I'm not quite sure how to squash it.
Edit: The lap timer is a different timer than the regular timer, and I don't think the bug I'm having affects it, so ignore the lap timer code.
Any ideas?

wp7 xna touch. I want the user to keep tapping, not hold

Trying to make an xna game, where the user needs to tap to stop a bar going down. The simulation is like what we see in a WWE PS3/xbox game, the give up bar.
Anyway, the way i have done it, serves the purpose. However, if the user holds the touch, touch values keep incrementing.
What I want is, if the user taps it, only 1 point will be scored. And when he taps again, he will get 1 point more. At the moment what is happening is, if the user holds it, it keeps incrementing, kind of like in a keyboard (if u hold a button, keypresses keeps going on).
foreach (TouchLocation location in TouchPanel.GetState())
{
if (location.State == TouchLocationState.Moved)
{
touchPoints++;
break;
}
}
Change from TouchLocationState.Moved to TouchLocationState.Pressed. That should only occur with each new touch.
foreach (TouchLocation location in TouchPanel.GetState())
{
TouchLocation prevLocation;
bool prevLocationAvailable = location.TryGetPreviousLocation(out prevLocation);
if (location.State == TouchLocationState.Pressed && prevLocation.State != TouchLocationState.Pressed)
{
touchPoints++;
break;
}
}
And for the future, if you're doing Keyboard input and want each press of the keyboard to increment the value but to be ignored if the player is holding down the space, you just have to store the previous state of the keyboard. Then you compare if the current keyboard state the key is being pressed and the previous keyboard state the key wasn't pressed.
(didn't actually code in compiler so there could be some code issues with the below)
KeyboardState currentKeyboardState = Keyboard.GetState();
if (currentKeyboardState.IsKeyDown(Keys.Space) && !previousKeyboardState.IsKeyDown(Keys.Space) {
//Do whatever it is you want to do with the press of the space key
}
previousKeyboardState = currentKeyboardState;
This is a snippet of my code of how i got each touch to be a individual value.
The system.diagnostics line is the test that will show you in your output box.
TouchCollection touch = TouchPanel.GetState();
foreach (TouchLocation touchLocation in touch)
{
if (touchLocation.State == TouchLocationState.Pressed)
{
base.InputPointX = touchLocation.Position.X;
base.InputPointY = touchLocation.Position.Y;
System.Diagnostics.Debug.WriteLine("X: " + base.InputPointX + \
", Y: " + base.InputPointY);
base.Update();
return true;
}
}

Resources