|Table of Contents|
Gnome is the desktop environment of choice on my home Linux system because it is feature-packed and user friendly. Gnome is also flexible, and thanks to the Gtk-Perl module and associated desktop toolkit bindings, I can use my favorite programming language to further customize and extend my Gnome environment.
This article shows how a useful Gnome tool can be built in an afternoon. It is also an example of some common techniques to employ when doing this sort of GUI programming, including widget creation, signal handling, timers, and event loops. It also covers some Perl basics, in review. So, read on and you may be inspired to write some Gnome applications of your own.
Download the source code referred to in this article.
On a Gnome desktop, the panel contains a variety of buttons and other widgets which serve to launch applications, display menus, and other common functions. It's standard desktop fare, like the Microsoft Windows Start menu.
An applet is a particular kind of Gnome application, which resides within and operates on the panel itself. The Gnome distribution comes with several of these including a variety of clocks, the Game of Life, and system resource utilization monitors. The Gtk-Perl module enables a Perl programmer to create custom Gnome panel applets, and many other GUI-related applications.
The Gnome panel applet we'll build finds the local host's default TCP/IP gateway and displays the gateway's status on a button in the applet. When the button is in an "off" position the gateway is not polled (Figure 1).
Fig. 1: Gnome Gateway in the "off" position.
When the button is on, the gateway is polled at scheduled intervals and the button's label is updated with the result: (Figures 2a & 2b) response or non-response.
Fig. 2a: Gnome Gateway in the "on" position.
Fig. 2b: Gnome Gateway not available.
This diagnostic may be used to regularly and unobtrusively report the status of the local network relative to the machine's default gateway. Check the button's label to see how things are faring on the upstream network.
The applet uses the system commands
ping familiar to
Unix users, and system and network admins.
The top-level code in the script is lines 1 through 21. Lines 23
through 69 define subroutines. One subroutine is called by our
fetch_gateway), but two others are "callbacks" (
reset_state). A callback is a subroutine that will be called by
the Gnome code when something happens -- for example, a timer expires or button gets clicked. Now, let's see what we can learn about how the application
Line 3 indicates that we'll be using the Gnome module.
Gnome.pm is distributed with the Gtk-Perl package. The program discussed here has developed against Gtk-Perl version 0.7003.
Gnome.pm requires separate installation: download and unpack Gtk-Perl, install it, and then change directories into the GdkImlib and Gnome distribution and install them too.
If you want to develop panel applets (as we're doing here) you'll need to append the build option
--with-panel to the end of the usual
Makefile.PL portion of the install process:
perl Makefile.PL --with-panel
Gnome.pm hasn't made it to Version 1.0 yet I've found it to
be stable. The biggest problem is the lack of documentation.
Lines 5 and 6 initialize a new AppletWidget object in
$a. This object
is the container for all the doodads that will be part of our applet.
Line 8 creates a label for use when our button is in the "off" position.
Line 10 creates a timer. The prototype for the
Gtk->timeout_add( $interval, \&function, @function_data );
Here our interval is 20000 (this value is in milliseconds, so the
timer will go off every 20 seconds), and the function to be called
when the timer goes off is
check_gateway. We could use the third
parameter to pass some data into the
check_gateway function if it were
appropriate to do so. In this case it isn't necessary.
Line 11 creates a new ToggleButton object in
$button, and labels it "off".
Line 12 registers the other callback in this application. This one
will be called when a particular "signal" occurs within Gnome. These
aren't normal Unix signals like
SIGCHLD, but GUI events.
In this case, the event is "clicked". The ToggleButton widget also has
the signals "pressed", "released", "enter", and "leave", each of which is
emitted in response to either actions of the mouse pointer or a function
call, such as
$button->pressed(). In our application, Gnome will call
ToggleButton $button is clicked.
Line 14 sets the size of the button to be a square with sides of 50 pixels.
This is a good fit for the default Gnome panel. Gnome uses global theme
and style information to determine how the button
is to be drawn: line, color, shadow, etc. Line 15 calls the button's
show method, indicating that we're finished setting its attributes and
that it is ready for display. Line 16 adds the button to the applet.
Technically, we've packed the ToggleButton widget into the AppletWidget
container by invoking the AppletWidget's add method on the ToggleButton.
The ToggleButton is now a "child" of its container. At line 17 the
applet is allowed to be visible by calling its
show method as well. A
widget's children will not be displayed until the parent's
Line 19 calls the
fetch_gateway routine to gather in the local host's
default TCP/IP gateway. In that subroutine,
`netstat -rn` captures the
local routing table, returning all addresses in dotted quad
+notation. The test in line 30 gets triggered by the IP address
+0.0.0.0, which indicates the default gateway. When it matches, the
+gateway's name is looked up from the IP address, and stored in the
+variable $hostname. Finally, we translate this value to lowercase, which will look better when we finally display it on the
At line 21, we're ready to hand off to the
gtk_main event loop, which
is responsible for drawing the application on the screen and managing
user interaction. At this point, our only interface with the
application will be through signal handling and callback functions.
The Gnome Toolkit (GTK) is event driven: once we enter
application stays put until an event occurs (caught via a signal) and
the associated callback function is invoked. Therefore, we should have completed all of our application setup beforehand.
Now let's examine the two callback functions:
with catching a "clicked" signal on our button (line 12); and
check_gateway, associated with the timer (line 10).
At line 38, we query the state of the toggle button by invoking its
get_active method. This returns 0 if the button is off and 1 if it is
on. By default, the ToggleButton widget has one child -- its own label.
We label the button with the contents of
$off_label if it is in
the OFF position or the string "Wait" if it is in the ON position (figure
3) because we know that
check_gateway is going to be called soon
(within the next 20 seconds). Part of
check_gateway's job is to
update the button with the status of our TCP/IP gateway, the whole
point of this applet.
Fig. 3: Gnome Gateway waiting.
Line 53 stores either the value in
$hostname or the string
"gateway", depending upon whether or not
$hostname is longer than eight characters (lines 47-52). This is the number of characters that will
fit comfortably on one line within the button's label.
If the button is in the ON position (line 55), we go ahead and attempt
to ping the gateway with a single ICMP packet, redirecting
STDERR to the round-file. We don't care about the output of the
command, only it's return value, so we execute the command via
Ping will return 0 upon success (gateway alive) and
something else if it fails (probably because the gateway is dead), so
we check the result and update the button's label appropriately in
check_gateway to continue to be called, every 20 seconds, until
the application terminates. So, at line 68 the function returns a true
value. A true return value will allow further execution of a timer's
callback function while a false return value forces the opposite
Note that 20 seconds is enough time to allow the call to
timeout and return a value to the application. It is also an
appropriate level of resolution for this kind of discovery activity: If
information about the status of my gateway is at most 20 seconds old, then I'm happy. Your mileage may vary, of course.
It's easy to write Gnome applets in Perl. This simple example showed you the basic elements of Gnome programming, including the event model and callbacks. Go forth and hack your own applet!