winapi_historyhow it came about |
|
Here's how the process of binding a subset of winapi for the purpose of writing a humble Win32 GUI could lead you to write this library in the end.
So you are on Windows and you want to write a GUI. With just the ffi, the headers from the Windows SDK and the good old MSDN you could bind enough of winapi to get by. So you start binding and transcribing to Lua the necessary functions, macros and constants for windows and controls and messages and all that's needed to get the app off the ground.
Soon you realize some facts about winapi that draw more and more attention to the binding than to the actual application that you want to write.
Most functions can result in an error but there's different ways in which they report that (some by a zero result, others with null, others with -1 and so on). When that happens you have to call GetLastResult
and FormatMessageA
every time if you want to get a clue of what happened. And so you start wrapping the calls.
Windows and most controls must be initialized with good defaults even if you don't care for them, or your controls will refuse to get created or you'll get strange display bugs.
You can set multiple properties of existing controls at once via "setinfo" commands, but you have to specify which properties you want to set values for using a bitmask field - another artifact of a statically-typed language and old times when RAM was scarce.
The various "state" fields are bit fields and so they too have a bitmask that you have to set according to which bits you want to set.
Also some structs have a "cbSize" field that you have to set to the sizeof the struct every time you create a new one. Other times there's a "version" field for similar purpose. If you forget this, nothing will be set and you won't even get an error.
You want to feed the GUI unicode strings. To use string constants from your source file (which is probably in utf8) you have to bind MultiByteToWideChar
and call it on all your string constants and other utf8 strings coming from different places.
Winapi, like most C APIs counts from 0 so you always have to adjust for that in your loops which leads to off-by-one bugs.
To set callbacks on individual controls to respond to events you have to subclass the controls. Then you have to dispatch them by hand, typecasting for the various different meanings of wParam
and lParam
. It soon becomes clear that for an app with more than a handful of windows and controls, some sort dispatching system must be devised to allow event handlers to be set for individual windows, controls and/or event types. Message decoders must also be written for each event type.
To change the state or behavior of controls after creation you have to awkwardly send messages to them via SendMessage
, jamming your values and structs into wParam
and lParam
with lots of typecasting and bit shuffling. There are macros for these that you can transcribe to Lua, but it's a lot of grunt work.
In winapi, the objects you create you have to destroy yourself. But when assigned as children or properties to other objects, the parent takes the responsibility of destroying them when it is itself destroyed. You want to prevent memory leaks so you assign the objects a finalizer tied to the garbage collector, but you are careful to unassign it when the object gets owned by another object, and assign it back again when it gets disowned.
Now you have windows and controls on the screen and you can respond to events. But writing GUIs procedural style is tedious to say the least. So you now have to devise an object system to encapsulate the controls creation, setting and getting of properties and assigning event handlers. It needs to have inheritance because you want to reuse the many properties and methods of similar controls like buttons and checkboxes. Virtual properties that are read and written to by way of getters and setters would also be nice, considering the sheer number of those.
Finally, you may remember how Delphi had a simple yet very effective layouting system based on anchors. Windows doesn't come with that, so you write that too.
This is more/less where the winapi binding is right now, and this is the process that got it here. There are still a lot of missing pieces of this huge API, but there's an infrastructure of utilities and conventions and code organization in place (which is outlined in the dev doc) that makes further work on the library relatively painless and opens it up for collaboration.