Bharath Nadampalli
4 min readJul 6, 2018

--

Mac application internals for Windows developer: Part 1.

Well it all started with a hello world program in win32 and that was my introduction to write GUI applications.

The initial inspiration was charles petzold and his legendary book programming windows. Hello world program of the First chapter is probably the most simplistic view of a GUI program.
Pseudo code would look like below.

int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPTSTR lpCmdLine,
_In_ int nCmdShow) {
RegisterClass(hInstance); // Creates a WNDCLASS structure basically the NSWindow kind of
// main class
if (!InitInstance (hInstance, nCmdShow)) {
return FALSE;
}
//Then there comes the (message loop) == NSRunloop
while(GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
exit(msg.wParam);
}

Dispatch Message find the window and its appropriate WindowProc and despatches the message to be handled there, something like this.

Mac os x also has as similar structure with a be different implementation. So I set of writing a simple Application with a button and button press to understand if I could mimic the win32 program and also may be understand the underpinnings of the windowing system in mac os x.

I have used two samples to navigate around this article.
a. UIapplicationbareBone app: this app does not use any .xib , plist just uses standard main function and message loop as shown above.
b. SampleBareBone app: this app replaces the standard NSApplication class with MyApplication which implements a run function having a message loop.

The first app code snippet is as shown below

As shown above the first line is a call to sharedApplication
Does the following things.
1. initializeAppContext as shown below installs eventHandlers

0x7fff3bca7201 <+141>: callq  0x7fff3c8b08c8            ; symbol stub for: InstallEventHandler0x7fff3bca7206 <+146>: callq  0x7fff3bca7c42            ; _NSInstallCocoaAppApplicationLevelEventHandlers0x7fff3bca720b <+151>: movl   0x5c623c9f(%rip), %eax    ; _cgsContextID

2. Adds CFRunLoopSource tries to register itself to a runloop which can be triggered externally to work on the event. Apple docs in fact gives a very neat description fo the same. There will be multiple calls to CFRunLoopSource from [NSApplication sharedApplication] each time adding source to for different piece of the app stack traces below show the same.

Another interesting fact about windowing systems GUI shell is tied to a windows server. In windows it is the Windows explorer , for Mac os x it is Finder (when we kill the finder process you would see the whole screen is refreshed while windows server is restart, same happens with explorer on windows.) and for IOS it is springboard. According to Jonathan Levin author of Mac Os X and IOS internals talks about multiple windows servers on mac os x to reduce the burden on the windowing server.

Anyway let us continue the journey now its time to discuss the Messageloop.
As we see in the above application Message loop is pretty straight forward.
Actually [NSApplication run] does exactly the same as shown the above code snippet.

This is where the second app comes into play, here I have replaced the NSApplication class with MyApplication class implementing the Run function. The code would be as shown below.

This function [self nextEventMatchingMask: untilDate:inMode: dequeue:] actually ends up calling the CFRunloopRun function which implements the actually message pump/loop.

So let us start looking how CFRunLoopRun is implemented fortunately which is open-sourced for the sake of brevity I have tried to show the implementation partially and explaining the same.

Let us understand what Runloop is first as per apples documentation

A CFRunLoop object monitors sources of input to a task and dispatches control when they become ready for processing. Examples of input sources might include user input devices, network connections, periodic or time-delayed events, and asynchronous callbacks.

It can monitor three kinds of objects
a. Sources
b. Timers
c. Observers
So according to the statement we can expect the same job done in the pseudo code for CFRunloop. Let us dive-in, the while loop starts of with
CFRunLoopDoObservers for timers and sources observe before, in both the functions, this would send notifications before timers / sources are processed.

Next __CFRunLoopServiceMachPort is called and the msg of type mach_msg_header_t is extracted along with the livePort of type mach_port_t is extracted.

Depeding on the type of the port a particular function takes over to service the message, most of the end up at mach_msg.
mach_msg the structure of which is shown here, looks like rpc kind of a structure, the option parameter serves are a direction indicator MACH_SEND_MSG and MACH_RCV_MSG

mach_msg_return_t   mach_msg
(mach_msg_header_t msg,
mach_msg_option_t option,
mach_msg_size_t send_size,
mach_msg_size_t receive_limit,
mach_port_t receive_name,
mach_msg_timeout_t timeout,
mach_port_t notify);

mach_msg_return_t mach_msg_overwrite
(mach_msg_header_t* send_msg,
mach_msg_option_t option,
mach_msg_size_t send_size,
mach_msg_size_t receive_limit,
mach_port_t receive_name,
mach_msg_timeout_t timeout,
mach_port_t notify,
mach_msg_header_t *receive_msg,
mach_msg_size_t receive_msg_size);

The interesting part was what happens after mach_msg is called which took me to another breakpoint and another stacktrace which I have truncated.

br s -n mach_msg

it ends up as we see in mach_msg_trap which is point of transition to kernel mode (that is how far i can get thro' as well).

No what does [NSApp sendEvent:event] do? This where as the documentation states the Responder Chain starts and this article ends!.
I will be back with more investigation on responder chain in my next post
Thanks for going through this.

References and tools:

  1. http://www.charlespetzold.com/blog/2014/12/The-Infamous-Windows-Hello-World-Program.html
  2. https://github.com/opensource-apple/CF/blob/master/CFRunLoop.c
  3. lldb debugger
  4. Hopper disassembler

--

--