minilibs vs. megalibs
Case study: gtk2
Purpose
The application needs a GUI: widgets, buttons, menus, popups.
Actual implementation: gtk
The application starts, initializes gtk, builds the GUI and runs gtk_main().
When the top window is closed or gtk_main_quit() is called, gtk_main() exits
the application.
How is it broken by design
The relation between the application and the GUI is broken.
Gtk is not a lib that provides GUI facilities for an existing application
(additive development).
Instead, gtk takes over the main loop, forcing the application to be implemented
in a certain way. In other words, a gtk application is not designed around
the problem it tries to solve, but around the GUI, following the One Way gtk
requires, or trying to work it around (subtractive development).
How it affects the application, how things get complicated
The application must be written in an event-driven way and should not have
its own main loop. Event driven mechanisms provided in gtk (... and glib)
must be used. In practice this means if an application wants to do anything
differently compared to the way gtk is designed, it will need to work
around gtk or just live with limitations imposed by gtk:
- real life applications most likely won't be able to avoid using glib OOP and glib signals to some extent; this makes debugging harder
- normally gtk_init() just exits if it faces difficulty, because "why would the application go on without gtk?"; there is another call, gtk_init_check() that does not kill the process on error (see also: additive vs. subtractive design )
- gtk changes locale and other global states; these features are on by default and there are extra calls to switch them off; because of the default-on policy, the application programmer must be aware of all such features of gtk to be able to turn off the ones that interfere with the application
There is no proper way to fully uninitialize gtk and keep the application
running. For example if an application starts without a GUI, then wants
to set up a GUI for a short time, later on close the GUI and go on processing
data, it is simply not possible with gtk. The top window created with
gtk_init() is not closed or destroyed by gtk, it disappears only when the
process exits. This is because of the broken relation between the application
and gtk: the gtk API assumes (thus enforces) the ideas that:
- the application exists only as long as the GUI exists
- the application will not attempt to use anything else than gtk for a GUI
- the application will have only one GUI session so gtk, as a library, will be initialized only once in a process
The last issue causes problems in
pcb-rnd, where the application has support for multiple GUIs. It is
impossible to properly switch from the gtk GUI to another GUI and then switch
back, because the gtk window stays open as gtk can not be shut down.
Generally, it's hard to have code that does anything else than init/uninit
before and after the GUI main loop, or has multiple successive GUI main loops.
How the minilib approach would fix this
Provide API for:
- initialize and uninitialize the GUI
- building the GUI (creating windows, adding widgets, buttons, menus, etc.)
- processing input events collected by the application
Let the application do whatever it wants to do; the GUI lib is not a programming
environment that:
- tries to run the main loop, dispatch events automatically;
- implement OOP and force it on the application;
- implement whatever large and expensive default infrastructure that the application needs to work around;
- determine the main task of the application.
The GUI lib is a library that should be focusing on one thing: running the
GUI. It should be a servant that implements an infrastructure of non-intrusive,
slave functions the application can combine whatever ways seems reasonable
to build a GUI.