![]() |
||||||||||||||
|
|
After several rounds of testing the usability and aesthetic appeal of various mockups, we hammered out this UI design and the accompanying developer style guide. Our relief in finishing the design was cut short with thoughts of actually putting the UI together. We had conscientiously avoided considering implementation when drafting this UI. What programmer in his right mind would WANT shaped windows, arrows spanning several widgets, and numerous fonts? Now it was time to roll up our sleeves, pick a GUI, and get to work.
CHOOSING GTK+/XLinux has never adopted any One True UI. There is a myriad of interfaces for both desktop and embedded products. While this fragmentation has been an obstacle in standardizing a Linux desktop, Linux's detachment from its UI has been a benefit to embedded devices. Old UIs are easily removed and newer ones are readily adopted. When designing a device requiring a custom interface, it is good to have a wide range of UI options. A quick survey of embeddable Linux widget sets gives a list including Gtk+, Qt/E, FLTK, OpenGUI, MiniGUI, PicoGUI, and PicoTK. Most of these toolkits require a separate windowing and/or display system like X Windows, Microwindows, or direct framebuffer access. Our GUI requirements include:
Additionally, we would prefer to use a mature GUI with a sizable application base. This gives a pool of applications that could be easily adapted to the device, as well as a large community of after-market developers. Using the above criteria, we narrowed our GUI choices to GTK+ or Qt/E. Qt is a GUI recently developed by TrollTech and used by the KDE desktop. Is has an OOP-intensive C++ framework. Qt/Embedded (Qt/E) is a reduced version of Qt that runs on a framebuffer. It sports a clean widget set, a signal/slot architecture, and graphics abilities including TrueType fonts and alpha blending. The Qt Palmtop Environment (QPE) is an application infrastructure that includes Qt/E running on a framebuffered client/server windowing architecture, along with a host of PIM applications. GTK+ is a GUI with a C-based object-oriented architecture. The GNOME desktop uses GTK+. GTK+ features clean abstractions between the GTK+ library which manages widgets and signals, the Gdk library which manages windowing and drawing, and the Glib library which provides low-level data structures and utilities. GTK+ was developed on the X Window System, but has been ported to many systems, including a framebuffer (GTK+/fb) and Microwindows, a small windowing system (see Introduction to Microwindows Programming Jan/Feb 2001). We briefly toyed with the idea of using GTK+/fb or GTK+/Microwindows. There are two problems with GTK+/fb: (1) applications cannot share the display, and as a consequence (2) if an application crashes, it brings down the entire GUI. Compiling applications together into a monolithic single framebuffered application only exacerbates (2), and makes it hard to allow add-on applications. While using Microwindows would dodge these problems, it is not yet mature. We refined our decision to Qt/E running within QPE versus GTK+ running on the X Window System. The decision to consider X may surprise many embedded developers. X is a 20+ year old network-transparent client-server windowing system. A single "display server" manages client display requests through an interprocess communication protocol. This makes it remarkably crash-proof. X does not include any widgets, but underlies many popular widget toolkits. Although it is well-established and well-supported, it is often assumed that X is bloated and incurs too much overhead to be useful in embedded devices. Jim Gettys, an original X author, rejects this belief, saying "most of what you hear about X being too big are from people who know little or nothing about the topic" (?). Recent efforts to streamline X have been successful, giving good performance on embedded hardware. We selected Keith Packard's TinyX, now a part of the standard XFree86 source tree, as our flavor of X. We ultimately chose GTK+ 1.2.8 running on X Windows, in turn running on a framebuffer. The decision to use GTK+ instead of Qt/E was primarily a conservative preference to stick with a proven solution, but it was also informed by key differences between GTK+ and Qt/E:
XThere has been a surge of interest and work in making X appropriate for embedded devices. Jim Gettys defended the future of PDAs running X, stating that "I believe very strongly that either GTK+/fb or Qt/E are dead ends" for PDAs because they cannot easily share applications with desktop systems and lack network transparency. He goes on to describe the ongoing work to put Xlib "on a diet." The standard TinyX distribution worked perfectly on our device. To further reduce the size of TinyX, we moved the color management system (CMS) into its own library. Not using the CMS saves 86KB. Other planned reductions include removing unused, large functions like arcs and widelines, and converting macros into functions.
TAILORING GTK+"Tailoring" is a good description for the next stage of our UI process. We started with the standard distribution of GTK+ 1.2.8 and snipped away excess, altered existing code, and added some new features. These modifications ranged from minor to extreme. Some even broke core GTK+ assumptions.
Removing WidgetsThe easiest change was trimming out unused widgets. The list of widgets is too long to reproduce here. It includes obviously unused widgets like GtkGamma and GtkHRuler, outdated widgets like GtkList (replaced by GtkCList), and widgets not required by our style guide like GtkFrame (why waste screen real estate on extra borders?).
Widget Sizing and DrawingThe next round of changes was altering how widgets sized and drew themselves. GTK+ provides a theme engine mechanism for controlling widget look-and-feel. It enables run-time selection of fonts, spacing, and drawing code. This mechanism did not meet our needs for two reasons. First, it wasn't flexible enough. Many spacing and drawing constants were hard-coded. Second, a theme would be an additional chunk of code and parameters that would sit alongside the GTK+ library and increase the GUI footprint.
It required a fair bit of sleuthing to unearth all the factors that go
into a widget's size. Consider the parameters that go into
While good OOP methodology dictates that widget changes should be implemented in a new subclassed widget, space constraints required us to tweak code in-place. GtkWindowGTK+ assumes that when one widget contains another widget (a "has-a" relationship) the two widgets are nested. In our UI, softkey bars belonging to application and dialog windows are not contained within those windows, yet adhere in other respects to the GTK+ containment relationship. A perfect solution was not obvious, but we were able to reconcile this conflict in a minimum of new code by adding special-case checks for softkey bar child widgets to a few key functions. The owning window listens for softkey key events and passes them to its softkey bar. Softkey arrows "poke" through the owning window's border and and display feedback when the softkey is pressed, so softkey callbacks are registered with the parent window to draw to the window's "button" areas when these events occur. We provide API at the level of the top-level windows to manage creating the softkey bar, populating it with softkeys, and attaching signals. A more innocuous hack was managing the special case of an application window containing just one scrollable region. Drawing a window border around a scrolled region border would waste valuable screen real estate. Thus, when a window only contains a scrolled widget, it does not display a border.
Font ManagementFinding a lightweight mechanism to display many fonts on the screen turned into a hairy problem. The difficulty is a combination of Gtk's heavy-weight widget "style" and X's archaic font management scheme.
As mentioned before, GTK+ has a theme engine that controls widget
look-and-feel. Just before a widget is displayed, it gets a style
object,
There are several problems with this practice.
We wrote a more elegant font management system that circumvents
gtk_widget_set_font_bold (widget, TRUE); gtk_widget_set_font_enlarge (widget, 1);
We implemented this font system by adding a An infuriatingly thorny issue involved letting programmers set widget font attributes relative to the base font at any time, even before the theme engine has determined the base font. We store requested font changes until a widget is shown, at which time its base font is known and we can apply the changes and use the actual font.
Window ManagerThe final step of implementing our UI was creating a window manager, a program that enforces display policy. We chose to modify Aewm, a tiny Open Source X Window manager written by Decklin Foster of Red Bean software. We hard-coded our windowing behavior preferences into Aewm. For example, the topmost window always gets focus and the root background is white. Our design calls for application windows with vertical titlebars and dialogs with horizontal titlebars. Our modified version of GTK+ communicates this window type information to the window manager via X atoms, and the window manager draws the titlebars. GTK+ also passes an atom requesting the titlebar font. In addition to its normal functions, our window manager serves as a communication daemon for managing intra- and inter-application windowing. Each user application window establishes a communication socket with the window manager and may name itself (e.g. "FooApp"). An application can request that it be lowered to the bottom of the window stack, or that a named application be raised. If a dialog opens on top of the topmost application window, the window manager tells the application window that it no longer has focus and should draw its widgets in an insensitive state. An application may request window manager eye-candy like a "zoom" special effect.
FOOTPRINTAfter the dust had settled from the frenzy of implementing our UI, we were left with a stable system that looked and behaved exactly like our draft UI. Compiled for the ARM architecture, our GUI's footprint is:
GTK+ still includes code that we will never use and could be removed. We estimate that another engineer-month of effort will reduce GTK+ to 850KB, bringing the GUI footprint to 2.8MB.
PERFORMANCEOur UI gives reasonable run-time performance on an ARM7 CPU (roughly equivalent to a 75MHz Pentium). Widget response time to user events is lightning fast. New screens are built and drawn at acceptable speeds. We have been plagued by slow launch times. Our most complicated application takes as long as 2.4 seconds to load and display. Of this, 1.5 seconds are spent building and displaying the UI. This lag stems from running GTK+ on X, which writes to a framebuffer, on under-powered hardware. No one factor is to blame. When we ran applications on our desktop machines displaying remotely to the device, initialization and drawing times were negligible. Conversely, there was good performance when running applications on the device displaying remotely to our desktop machines. Neither packet transmission, nor drawing to the framebuffer, nor GTK+'s computations were a bottleneck. The slowdown appears to be a consequence of these factors in tandem. We probably max out the under-powered CPU. It is also likely that there are also memory bandwidth constraints. At initialization time, GTK+ constructs many objects, GTK+ and X are communicating via shared memory, X is writing to the framebuffer, and the framebuffer is constantly written to the display. The memory bus runs at 18MHz and the framebuffer claims fully one quarter of this bandwidth, so the memory-intensive operations of initialization are understandably slow. Of course, the obvious but pricey solution would be to use better hardware. We estimate that on an iPaq (100MHz memory bus, 206MHz StrongARM) applications would load 2.5 times faster, giving a respectable 1 second worst-case launch time. But since our normal operation is perfectly reasonable, we will try other solutions first. X is costly at at initialization time. The client GTK+ application connects with the server, authenticates itself, and negotiates bit depth and other resources. The benefits of using X outweigh this overhead. X provides a robust client-server model that allows add-on applications. We can show different applications' windows at the same time, which cannot be done for most framebuffered solutions like GTK+/Fb (QPE is a notable exception). A 2.4 second lag is not the end of the world. On a 700MHz Windows NT machine, neither Word, nor Excel, nor Internet Explorer loads in less than 1.5 seconds. 'Kedit', a KDE application, loads in 1.37 seconds on a 500MHz PIII (http://www.suse.de/~bastian/Export/linking.txt). We realize this may not be a legitimate comparison, however. Users accept lag on powerful desktop machines but expect an instant response on portable devices. Why can PalmOS launch applications instantly? PalmOS achieves excellent speed on a small scale by using a thin OS running everything in a single address space. These are limiting factors for creating complex software. An application can crash the OS. There is no multitasking, hence no multiprocess solutions to problems. A modern operating system like Linux enables development of the complex software we want to create, but the trade-off is system overhead which is more visible on slower hardware. We have adopted two interim strategies for dealing with the launch time problem. The first strategy is to amuse the user with eye candy as the application launches. A slow response can be forgiven as long as something happens to show that the computer is working on your behalf. The other strategy is to predictively launch commonly-used applications in the background.
OPTIMIZATIONSWe were able to target a few areas of GTK+ for future optimization. We profiled the effect of removing floating point calculations from GTK+. Our ARM7 lacks an FPU so floating point calculations are a big performance hit. GTK+ uses floating point variables in a few, but commonly-used, widgets. We concluded that removing these calculations would give a 3-12% speed improvement, depending on the application. Pixmap-rich applications were unacceptably slow. Upon investigation, we discovered that the problem was largely caused by inefficient pixmap handling methods in GTK+ and X. The X pixmap (XPM) format is designed for compatibility, not for speed, and GTK's handling of it is not fully optimized. More importantly, the pixmaps are passed from the GTK client to the X server one pixel at a time. It would be far more efficient for the client to pass the pixmap by reference to the X server, allowing direct display of a precalculated binary image. We fixed obvious pixmap problems with GTK+ and wrote a temporary hack to grab post-rendered pixmaps from the X server and use the raw data as a replacement to XPMs. These raw images could be forcibly written to the X server. While this is an ugly solution that nobody in his right mind should use, it was 80% faster than using XPMs and demonstrates that an alternate pixmap solution could yield much better performance.
CONCLUSIONConsumer devices deserve a lovingly hand-crafted GUI. The Linux community has produced a number of Open Source GUIs that can be easily tailored to meet even the most bizarre design. The real difficulty is in selecting a GUI; after this, the implementation follows requirements in a fairly straightforward manner. GTK+ was the best GUI toolkit for our needs, and X Windows provided a stable client/server model that was worth the cost of footprint and launch speed. While we are proud that we got a 2.9MB custom GUI up and running on our hardware with five engineer-months of effort, most of the credit is due to an Open Source community that produces fine software that can be squished and prodded into embedded devices.
|
|
||||||||||||