Events, handlers, and focus: how OS X apps work

Mac apps are quite different from the Unix-style tools which you can run in Terminal’s command line, or the traditional idea of a ‘computer program’. Unless you do something to them, most just sit there, waiting endlessly for you to tell them what to do.

I have previously explained how an app initialises, and mentioned that once up and running, it sits in its Main Event Loop, waiting for keyboard and mouse events to handle. Here I will explain a bit more about how this works, and why it works the way that it does.

From the outset, with the first Mac classic apps and System 1.0 in 1984, the great majority of apps are event-driven. When you start typing into one of the app’s windows, select one of its menu commands, or the app receives an incoming network packet intended for it, for example, the app’s code has to know how to handle that event.

Typing on the keyboard is most straightforward: OS X sends the app a message containing the text (in Unicode) which you have typed. The app will see it inserted at the current place, determined by the current location of the cursor insertion point, and add that text to the document.

How the app handles mouse (or trackpad, etc.) events depends on where the pointer is. If over a block of text in the frontmost window, the cursor insertion point will be placed there, ready for you to enter text. If on a dialog button, the event will be a button click, and the app then has to work out how to respond, perhaps cancelling and dismissing that dialog without making further changes.

These events are only passed to the frontmost window of all the windows on the display – that is, the window which has the focus. This makes sense for keyboard events, as you would not want text being entered into a window further back, which does not have the focus, just because the pointer happens to be over it. Before you could enter text from the keyboard into that window, it would have to be brought to the front (into focus), and the cursor insertion point placed in an area in which text can be entered.

This also makes sense for mouse events. If there was a dialog in one of the windows further back in the stack of windows, and in order to bring that window to the front, you happened to click on what turned out to be its Dismiss button, you would rightly be upset if the dialog vanished into thin air as a result.

So although every active app is running its Main Event Loop, OS X carefully filters the events according to whether they occur in the frontmost window, which has the focus.

If you click on a window which is further back, instead of feeding that event to the app’s Main Event Loop as a click at that location in that window, the correct response is to bring that window into focus, in the front.

The common and notable exceptions to this are window controls, such as the ‘traffic lights’ at the top left, which close, hide, or maximise that window, and scroll gestures. These allow you to scroll a window’s contents and to close (etc.) a window without bringing it into focus, to make the basic control of windows easier.

There are some windowing systems – Windows most notably – which work differently, and give you the option of being able to ‘click through’ straight into the event loop of a window which is not in focus. That is dangerously inconsistent, and often results in user errors which are not easy to undo. Please do not ever expect a Mac to behave that way, even as a power-user option: don’t even think it.

For the sake of requiring a second click – the first to bring the window into focus, the second to be a new event for the window to handle – OS X is thoroughly consistent, and much less likely to do the unexpected and unintended.