
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
top level
cwidget routines. These are the global routines
that initialize cwidget, shut it down, and control its main loop
(among other things).
#include <cwidget/toplevel.h>
cwidget comes with a
collection of stock
dialog
boxes
for things like displaying messages to the user. To access
the routines that build these dialogs, we write:
#include <cwidget/dialogs.h>
All the symbols provided by the cwidget library are in the
cwidget
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
cwidget:
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.
cw::toplevel::init();
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 L
in front of the string).
- The second argument tells cwidget what to do when the
ok
button is pressed. The expression
sigc::ptr_fun(cw::toplevel::exitmain);
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:
cw::toplevel::settoplevel(dialog);
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
exitmain()
is invoked.
cw::toplevel::mainloop();
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
exits.
cw::toplevel::shutdown();
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)
Of course, you'll need to have the
g++ and
libcwidget-dev
packages installed for this to work!