For a long time I have been wondering how modal dialog is implemented.
Let me take Qt as an example. (Nearly all GUI toolkit has this mechanism)
In the main event loop, a slot is called, and in this slot a modal dialog is opened. Before the dialog is closed, the slot doesn't return control to the main event loop. So I thought that the main event loop is blocked and become unresponsive. Apparently this is not true, since when you open a modal dialog, the background main window is still working, like repainting its UI or keep displaying a curve or some graph. It just becomes not to accept any user input.
I did an experiment. I didn't open a modal dialog in the slot, but start a new thread there, and wait for the thread to finish in that slot. This definitely blocked the main event loop.
How modal dialog is implemented after all? How does it keep main event loop unblocked but at the same time blocked the calling slot?
There is only ever a need for a single event loop, and it does not block when a modal dialog appears. Though, I suppose, different toolkits may handle this differently. You would need to consult the documentation to know for sure. Conceptually, however, it all works in the same way.
Every event has a source where the event occured. When a modal dialog appears, the event loop either ignores or redirects all events that originate outside of the dialog. There's really no magic to it. Generally speaking, it's like an if statement in the event loop code that says "if (modal_is_shown() and !event_is_in_modal_window()) {ignore_and_wait_for_next_event()}". Of course, the logic is a bit more complicated, but that's the gist of it.
If you're looking for examples here's another one:
In Tk, there is only ever one event loop. Modal behavior (doesn't have to be dialog, can also be tooltips, textbox etc) is simply implemented by making the main window ignore mouse and keyboard events. All other events like redraws etc. can still be serviced because the event loop is still running.
Tk implements this via the [grab] function. Calling grab on a UI object makes it the only object able to respond to keyboard and mouse events. Essentially blocking all other objects. This doesn't mess with the event loop. It merely temporarily disables event handlers until the grab is released.
It should be noted that Unix-like operating systems running X also has grab built in to the windowing system. So it's not necessarily implemented merely by UI toolkit libraries but is sometimes also a built in feature of the OS. Again, this is implemented by simple blocking/disabling of events instead of instantiating separate event loops. I believe this also used to be the case for the older MacOS before OSX. Not sure about OSX or Windows though. Even though modality is often implemented by the OS itself, toolkits like Qt and Tk often implement their own mechanisms to standardize behaviors across different platforms.
So the conclusion is, it is not necessary to block the main event loop to implement modality. You just need to block the events and/or event handlers.
The answer by https://stackoverflow.com/users/893/greg-hewgill is correct.
However, reading the follow-up discussion between him and https://stackoverflow.com/users/188326/solotim , I feel that there is room for further clarification, by means of prose and some pseudo-code.
I'll handle the prose part with a fact-list:
The main message loop does not run until the modal activity is finished
However, events are still delivered while the modal activity is running
This is because there is a nested event loop within the modal activity.
So far I just repeated Greg's answer, please bear with me as it is for continuity's sake. Below is where I hope to contribute additional, useful info.
The nested event loop is part of the GUI toolkit, and as such, it knows the callback functions related to every window in existence
When the nested event loop raises an event (such as a repaint event directed to the main window), it invokes the callback function associated with that event. Note that "callback" here may refer to a method of a class representing a window, in object-oriented systems.
the callback function does what is needed (e.g., repaint), and returns right back to the nested message loop (the one within the modal activity)
Last, but not least, here's pseudo-code to hopefully illustrate further, using a fictitious "GuiToolkit":
void GuiToolkit::RunModal( ModalWindow *m )
{
// main event loop
while( !GuiToolkit::IsFinished() && m->IsOpen() )
{
GuiToolkit::ProcessEvent(); // this will call
// MainWindow::OnRepaint
// as needed, via the virtual
// method of the base class
// NonModalWindow::OnRepaint
}
}
class AboutDialog: public ModalWindow
{
}
class MainWindow: public NonModalWindow
{
virtual void OnRepaint()
{
...
}
virtual void OnAboutBox()
{
AboutDialog about;
GuiToolkit::RunModal(&about); // blocks here!!
}
}
main()
{
MainWindow window;
GuiToolkit::Register( &window ) // GuiToolkit knows how to
// invoke methods of window
// main event loop
while( !GuiToolkit::IsFinished() )
{
GuiToolkit::ProcessEvent(); // this will call
// MainWindow::OnAboutBox
// at some point
}
}
In general, a modal dialog box of this type is implemented by running its own message loop instead of your application's message loop. Messages directed to your main window (such as timer or paint messages) will still get delivered, even during the modal operation.
In some situations, you may have to be careful that you don't recursively do the same thing repeatedly. For example, if you trigger a modal dialog box on a timer message combined with some persistent flag, you'll want to make sure you don't keep bringing up the same dialog box repeatedly whenever the timer message fires.
Related
I'm working on a custom cross platform UI library that needs a synchronous "ShowPopup" method that shows a popup, runs an event loop until it's finished and automatically cancels when clicking outside the popup or pressing escape. Keyboard, mouse and scroll wheel events need to be dispatched to the popup but other events (paint, draw, timers etc...) need to be dispatched to their regular targets while the loop runs.
Edit: for clarification, by popup, I mean this kind of menu style popup window, not an alert/dialog etc...
On Windows I've implemented this fairly simply by calling GetMessage/DispatchMessage and filtering and dispatching messages as appropriate. Works fine.
I've much less experience with Cocoa/OS X however and finding the whole event loop/dispatch paradigm a bit confusing. I've seen the following article which explains how to implement a mouse tracking loop which is very similar to what I need:
http://stpeterandpaul.ca/tiger/documentation/Cocoa/Conceptual/EventOverview/HandlingMouseEvents/chapter_5_section_4.html
but... there's some things about this that concern me.
The linked article states: "the application’s main thread is unable to process any other requests during an event-tracking loop and timers might not fire". Might not? Why not, when not, how to make sure they do?
The docs for nextEventMatchingMask:untilDate:inMode:dequeue: states "events that do not match one of the specified event types are left in the queue.". That seems a little odd. Does this mean that if an event loop only asks for mouse events then any pressed keys will be processed once the loop finishes? That'd be weird.
Is it possible to peek at a message in the event queue without removing it. eg: the Windows version of my library uses this to close the popup when it's clicked outside, but leaves the click event in the queue so that clicking outside the popup on a another button doesn't require a second click.
I've read and re-read about run loop modes but still don't really get it. A good explanation of what these are for would be great.
Are there any other good examples of implementing an event loop for a popup. Even better would be pseudo-code for what the built in NSApplication run loop does.
Another way of putting all this... what's the Cocoa equivalent of Windows' PeekMessage(..., PM_REMOVE), PeekMessage(..., PM_NOREMOVE) and DispatchMessage().
Any help greatly appreciated.
What exactly is a "popup" as you're using the term? That term means different things in different GUI APIs. Is it just a modal dialog window?
Update for edits to question:
It seems you just want to implement a custom menu. Apple provides a sample project, CustomMenus, which illustrates that technique. It's a companion to one of the WWDC 2010 session videos, Session 145, "Key Event Handling in Cocoa Applications".
Depending on exactly what you need to achieve, you might want to use an NSAlert. Alternatively, you can use a custom window and just run it modally using the -runModalForWindow: method of NSApplication.
To meet your requirement of ending the modal session when the user clicks outside of the window, you could use a local event monitor. There's even an example of just such functionality in the (modern, current) Cocoa Event Handling Guide: Monitoring Events.
All of that said, here are (hopefully no longer relevant) answers to your specific questions:
The linked article states: "the application’s main thread is unable to process any other requests during an event-tracking loop and
timers might not fire". Might not? Why not, when not, how to make
sure they do?
Because timers are scheduled in a particular run loop mode or set of modes. See the answer to question 4, below. You would typically use the event-tracking mode when running an event-tracking loop, so timers which are not scheduled in that mode will not run.
You could use the default mode for your event-tracking loop, but it really isn't a good idea. It might cause unexpected re-entrancy.
Assuming your pop-up is similar to a modal window, you should probably use NSModalPanelRunLoopMode.
The docs for nextEventMatchingMask:untilDate:inMode:dequeue:
states "events that do not match one of the specified event types are
left in the queue.". That seems a little odd. Does this mean that if
an event loop only asks for mouse events then any pressed keys will be
processed once the loop finishes? That'd be weird.
Yes, that's what it means. It's up to you to prevent that weird outcome. If you were to read a version of the Cocoa Event Handling Guide from this decade, you'd find there's a section on how to deal with this. ;-P
Is it possible to peek at a message in the event queue without removing it. eg: the Windows version of my library uses this to close
the popup when it's clicked outside, but leaves the click event in the
queue so that clicking outside the popup on a another button doesn't
require a second click.
Yes. Did you notice the "dequeue:" parameter of nextEventMatchingMask:untilDate:inMode:dequeue:? If you pass NO for that, then the event is left in the queue.
I've read and re-read about run loop modes but still don't really get it. A good explanation of what these are for would be great.
It's hard to know what to tell you without knowing what you're confused about and how the Apple guide failed you.
Are you familiar with handling multiple asynchronous communication channels using a loop around select(), poll(), epoll(), or kevent()? It's kind of like that, but a bit more automated. Not only do you build a data structure which lists the input sources you want to monitor and what specific events on those input sources you're interested in, but each input source also has a callback associated with it. Running the run loop is like calling one of the above functions to wait for input but also, when input arrives, calling the callback associated with the source to handle that input. You can run a single turn of that loop, run it until a specific time, or even run it indefinitely.
With run loops, the input sources can be organized into sets. The sets are called "modes" and identified by name (i.e. a string). When you run a run loop, you specify which set of input sources it should monitor by specifying which mode it should run in. The other input sources are still known to the run loop, but just ignored temporarily.
The -nextEventMatchingMask:untilDate:inMode:dequeue: method is, more or less, running the thread's run loop internally. In addition to whatever input sources were already present in the run loop, it temporarily adds an input source to monitor events from the windowing system, including mouse and key events.
Are there any other good examples of implementing an event loop for a popup. Even better would be pseudo-code for what the built in
NSApplication run loop does.
There's old Apple sample code, which is actually their implementation of GLUT. It provides a subclass of NSApplication and overrides the -run method. When you strip away some stuff that's only relevant for application start-up or GLUT, it's pretty simple. It's just a loop around -nextEventMatchingMask:... and -sendEvent:.
We have a kiosk mode application for Windows Mobile 5 that was going strong for a year at least in production. It uses window subclassing through the SetWindowLong windows API function to override the behavior of the taskbar to prevent users from ever leaving our application or other explicitly allowed applications.
My callback overrides the handling of a few window messages and calls the default handler for other messages. This is done by storing the previous function pointer returned by SetWindowLong, and using it inside of the new function by calling CallWindowProc on it.
Then we had to update the application to be compatible with Windows Mobile 6.5.3, and started having a bunch of problems. I followed this article to disable the bottom menu buttons which are new on 6.5. On a single application, it works fine and the menu respects my callback. As soon as the user opens up another application through ours, that window seems to be recreated, and I had to devise a mechanism to detect foreground window changes and then "resubclass" the window again.
In my code, I don't have control of when exactly this window is recreated, so my first attempt was to use the GetWindowLong function to obtain the current callback address and test it against my own function address. I learned the hard way that I can't just compare the values like that, since it does not always return the function pointer.
Because I'm not able to test if my method is the current handler that way, I end up sometimes setting my method as the handler, and the previous handler is also my own method (in this case, the window was not recreated and thus already had my method set). This results in an infinite loop, since my callback ends up calling itself indefinitely.
How can I know if my custom function is the one being used by a certain window, so that I can avoid this infinite recursion?
Don't use SetWindowLong(GWL_WNDPROC) to subclass a window. Use SetWindowSubClass() instead (like Raymond Chen said):
Subclassing Controls
Safer subclassing
Amongst providing safer subclassing semantics (such as allowing multiple subclasses at a time), it also allows you to associate user-defined data with each subclass. So you could use GetWindowSubclass() to check if you have already subclassed the window or not.
Or, you could simply keep track of whether or not you have already subclassed the window. A simple boolean variable will suffice. Once your subclass is in place, you MUST remove the subclass before the window is fully destroyed. So, for instance, when the subclass receives the WM_NCDESTROY message, you can remove the subclass and clear your boolean at the same time, and then the next time you see the window again your boolean will tell you that you need to subclass the window.
Send the custom message to window. Handle this message in your custom function to return some value that will indicate that it was your custom handler.. and so on.
if(SendMessage(hwnd, mymsg, 0, 0) != myvalue)
;// It's not your handler
I'm building an application with a main window and a sub-window that I'd like to present as a modal session. So I'm using this code within the window controller:
self.session = [[NSApplication sharedApplication] beginModalSessionForWindow: self.window];
[[NSApplication sharedApplication] runModalSession: self.session];
However, regardless of where I put this code - in windowDidLoad, in a post-windowDidLoad call from the main window, or even in the window controller's init function - what I get is a modeless sub-window. The sub-window appears over the main window, but the main window continues to respond to input events.
What am I doing wrong?
nvm - found my answer by reading some examples. (Once again, hours of research before posting on Stack failed to yield answers, and I stumble across insight right after posting.)
For posterity, here's the answer: beginModalSessionForWindow doesn't work like runModalForWindow.
With runModal, you can execute that code anywhere, and Cocoa will immediately halt all processing except for the modal window. (Unfortunately, that includes timers tied to non-UI events for background processing.) The code that executes runModal is blocked, and resumes only after the modal window is closed. The system enforces the modality of the window.
beginModalSessionForWindow works very differently. Wherever you execute it, the code starts up the modal window and then keeps executing. In the general use case (as demonstrated in Apple's examples), if you call beginModal before a loop and then condition the loop on polling the status of the session, the loop can perform whatever other processing you want; and during the loop, it's also blocking normal UI events - just like any IBAction would when executing a long-running loop.
The point is that beginModalSession actually doesn't enforce any modality of the window. Rather, your code enforces the modality of the window by executing a long-running loop. If you don't use a loop, but instead just let the "modal" session run and resume the ordinary event loop... then your other windows get all event processing, including UI events. The "modal" window becomes modeless.
I will argue that "beginModalSessionForWindow" should really be called beginModelessSessionForWindow, because that's what it does: it creates a modeless window and returns.
I have a problem with my mouse. Every now and then it will double click when I only single click. I know this is a problem with the mouse, and I've contacted the manufacturer, still waiting for a reply. But in the meantime I was wondering if there was a way that I could find out when the left mouse button had been clicked twice within a very short period (probably 1-10 milliseconds) of time, and disable the second click.
I mostly know how to use hooks, so that's not the problem, my main question is how to stop an event from happening, if that's possible.
The information on how to prevent the mouse message from being processed is in the documentation of the "LowLevelMouseProc callback function" in MSDN:
http://msdn.microsoft.com/en-us/library/windows/desktop/ms644986(v=vs.85).aspx
Specifically, it says: "If the hook procedure processed the message, it may return a nonzero value to prevent the system from passing the message to the rest of the hook chain or the target window procedure." So, if you know about windows hooks, you know how to do it.
EDIT: Actually, now that I think more about it, you don't want to discard any event. You simply want to transform the doubleclick event into just another left-button-down event. I believe you can do it from within the hook handler, and it will work. Have you tried it?
In C#'s WinForms, you write an event handler involving the mouse receiving a MouseEventArgs object. Inside it, you can access certain info such as the number of times it was clicked, for example.
protected void RowClicked(object sender, MouseEventArgs evt)
{
// Trigger it when the mouse was clicked only once
if( evt.Button.Clicks == 1 ) {
// ... more things ...
}
return;
}
Other GUI libraries have other possibilities. That said, your problem has nothing to do with GUI libraries. You have to change the sensitivity of your mouse, in the configuration options of your operating system. For example, in the Windows' control panel, you can change how much time has to pass between a click and another one to be considered a doble-click. In lUbuntu, you can do the very same, in System menu >> Preferences >> Keyboard and Mouse.
I'm fairly new to GUI programming and I'm trying to write a plotting lib in D to use with some otherwise console-based scientific apps. I'm using DFL as my GUI library.
Assume my plot form has a method called showPlot() that's supposed to display the plot on the screen. I would like to be able to have any thread in my app throw up a plot window and either block until the plot is closed or continue working, without the caller of showPlot() having to know what any other thread is doing with regard to plotting, or what plots were created in the past and may still be on screen. (The internals of showPlot() may, of course, have this knowledge.)
I'm still trying to wrap my head around how GUI libs typically work under the hood. It seems like you're supposed to only have one GUI thread, and one main form. I'd appreciate answers at the language/library-agnostic design pattern level in addition to language/library-specific ones.
Edit: To emphasize, this app has no GUI besides the plots that it throws up at interesting points in its execution. It's basically a console app plus a few plots. Therefore, there's no well-defined "main" form.
What you're going to be able to do will likely rely on how DFL works. Typically, in a GUI app, there's an event thread which handles all the events in your app - be it repaint events, button click events, mouse click events or whatever. That thread calls the event handler which is registered to handle the event (that frequently being the widget which was clicked on or whatnot). The problem that you run into is that if you try and do too much in those event handlers, you lock up the event thread, so the other events (including repaint events) don't get processed in a timely manner. Some GUI toolkits even specifically limit what you're allowed to do in an event handler. Some also limit certain types of operations to that specific thread (like doing anything - especially object creation - with actual GUI-code like the various widget or window classes that the toolkit is bound to have).
Typically, the way to handle this is to have the event thread either fire off separate threads in the event handlers and let those other threads actually handle the events, or you set some amount of state in the event handler, a separate, already-running thread is alerted to this change in state (possibly using the observer pattern), and it handles things appropriately based on that state. In either case, what the event handlers themselves do is generally fairly limited.
How GUIs work is generally very event-based. The program handles events from the user and the system and doesn't tend to a do a lot without being signaled to do it. Frequently, GUI apps don't do anything until they're told to by an event (though there are plenty of cases where a background thread is doing some sort of work separate from the events). What you're trying to do doesn't sound particularly event based, so that complicates things a bit.
My guess would be that you would need to have your app create a new window every time that you want to throw a new plot up. That window would likely be a child window of the main window which would be hidden since you obviously don't need it, and presumably DFL requires you to have a main window of some kind. There's a decent chance that each thread which wanted to create a window would have to tell the main GUI window to do that, but it depends on DFL really. It's also possible that DFL allows any thread to create new GUI elements (such as a new window).
Regardless, it would almost certainly be the main event thread which actually handles the window. The thread which wants to create a window and populate it would likely have to create the window (either directly or indirectly), and then update the state of the window by updating a set of shared variables so that the event thread could appropriately repaint the window. The most that original thread would likely to do actually repaint the window is send a repaint event to window after it's updated the state of the shared variables that hold the data necessary to paint the window. It wouldn't handle the painting itself.
As for the thread blocking, it would likely have to do busy-waiting until the window closed event was received by the event thread, and some shared variable was updated, or you could make it go to sleep until the event thread woke it up after receiving the window closed event. In the case where you didn't want it to block, it just wouldn't care about waiting to be told about the window-closed event and would keep chugging along.
In any case, hopefully that's enough information to put you in the right direction. Usually events drive everything, with everything hanging off the GUI, so to speak. But in your case, your app is using the GUI more like stdout. So, you might do better to first work on some small, fairly stupid GUI apps that are actual, normal, event-based GUI apps (like a Tic-Tac-Toe game or something) in order to get a better handle on how the GUI stuff works before trying to get it to work in a somewhat less standard manner like you are.
GUI frameworks typically have their own own event-loop, and call the application code as callbacks in reaction to external events(button clicks, timers, redraw, ...). The main difference between a "typical" console application and a GUI application is that you give up the control about when a function is called to the GUI framework.
Threads are typically used when the application code contains some long-running process, which cannot be interrupted into smaller chunks(large computations, copying files, take control over the world, ...). Then one thread is used to keep the GUI responsive, while the work is done in a separate thread. The main problem is to keep both threads correct in sync, since most GUI toolkits are not capable to handle calls from more than one thread. When you have only small work parts, which don't block the GUI for long time (<0.1s), then it is better to stay without worker threads.
I also strongly suggest to separate the GUI code and the application logic apart from each other. Having GUI code and application code mixed together is a maintenance nightmare.