Late last year, I spent some time disentangling aptitude's internal UI library and packaging it as a separate library, which I named cwidget. This article is the first in what will hopefully become a series describing the basic concepts and APIs of cwidget. It applies to the versions of cwidget currently available in Debian sid and lenny, 0.5.11-1 and 0.5.12-1. Autogenerated documentation of cwidget can be found here (but beware that as of this writing it is somewhat incomplete).
In this tutorial, I will walk through a simple program (hello.cc) that initializes cwidget, displays a simple message box, and then terminates the program when the user confirms the message. I assume a basic familiarity with C++.
The first thing to do is to include the portions of cwidget that our
program will use. Most importantly, we need the
cwidget routines. These are the global routines
that initialize cwidget, shut it down, and control its main loop
(among other things).
cwidget comes with a collection of stock
boxes for things like displaying messages to the user. To access
the routines that build these dialogs, we write:
All the symbols provided by the cwidget library are in the
namespace. Using the full library name means that there's a
reasonable chance that its names will not conflict with the names
provided by other libraries; however, it's a real pain to type
cwidget over and over. Since this is the only namespaced library we
are using in this program, it's handy to define
cw as an alias for
namespace cw = cwidget;
Now we're ready to write the main routine. The first interaction of client code with the cwidget library is to initialize it. This will initialize the ncurses library and put the terminal into a mode suitable for a full-screen curses program.
Next, we create a dialog box that will be displayed to the user. There are two things to note about this code:
cwidget is Unicode-aware and the
ok()routine expects the message string to be a wide-character string. In this case, that means that we need to pass in a wide-character string (indicated by typing
in front of the string).
The second argument tells cwidget what to do when the
okbutton is pressed. The expression
creates a slot using libsigc++. Briefly, a slot is a reference to a function or to a method of a class instance. When invoked, this particular slot will call
cwidget::toplevel::exitmain(), which causes the main cwidget loop to exit. The slot is wrapped in
cwidget::util::arg, a utility function that handles passing slots as optional arguments.
cw::widgets::widget_ref dialog = cw::dialogs::ok(L"Hello, world!", cw::util::arg(sigc::ptr_fun(cw::toplevel::exitmain)));
Now that we have a widget, the next step is to arrange for it to
appear on the terminal. In cwidget, control of the terminal is
assigned to a single
top-level widget: whatever it displays is
what gets displayed on the terminal, and all user input is passed
directly to it. In a real program this widget will typically manage a
collection of sub-widgets and assign each one a screen region, but for
now let's just display the dialog box we created:
With everything initialized, we're ready to start the main loop.
mainloop() will keep the display up-to-date and dispatch incoming
events (for instance, keystrokes and mouse presses) until
When the user presses Enter or clicks on the Ok button in the dialog,
exitmain() will be invoked by the binding that we set up earlier.
Once this happens, we need to shut down cwidget in order to restore
the terminal to its original state. If you skip this step, the
terminal will be garbled and will not work properly when your program
And that's it! To compile the program, change to the directory containing hello.cc and run:
$ g++ -o hello hello.cc $(pkg-config --cflags --libs cwidget)