The Wayback Machine - https://web.archive.org/web/20070430133624/http://www.tfn.net:80/~amstpi/techoverview.html

An Overview of the GGI Project

[GGI Logo]

GGI, as shown above, stands for General Graphics Interface. As one might guess from the name, the primary purpose of this project is to create a standard way for a program to access graphics capabilities available in its runtime environment. In addition, it seeks to establish a standard architecture for graphics hardware device drivers and their interaction with the operating system, to offer a redesigned console system to support and coordinate multiple video cards and multiple input devices attached to a single machine (this is known as Multihead) and to offer a set of graphics libraries to perform useful graphical tasks, such as font handling, sprites, window management, and operations specific to three-dimensional graphics.

LibGGI

LibGGI is the core library of the GGI project. It is designed to dynamic and be display-independent. A program written with LibGGI will display to any target (a target can be a graphics device or something else, as I will explain shortly) that is available in the current runtime environment. Targets are compiled as separate dynamically loaded (also called shared) libraries and not brought into memory until needed. Each target exports its abilities replacing LibGGI stub functions. Targets, in cooperation with kernel graphics support (see below) might hit the graphics hardware directly, but can also go through more circuitous routes to display.

For example, there are two targets for LibGGI that display to X servers using the industry-standard Xlib (one optimized for performance where the client and server are on the same computer, the other optimized for the case that the client and server are on separate computers). Another target is makes use of the SVGALIB library on available on Linux and (I believe) FreeBSD. Another target will display over a network using the Virtual Network Console protocol. Yet another, somewhat whimsical, target displays via AAlib, the ASCII-Art library, in which normal hi-resolution graphics are dithered, approximated, and cajoled into a 80x25 ASCII text display.

This flexible target system also has another major advantage. It allows "targets on targets". Perhaps the easiest way to explain what this means is to describe a couple of the current LibGGI targets that fall under this category. The first is TrueEmu. This target emulates a full 24 bpp RGB mode on top of more limited modes (8 bit paletted, 15, 16 bit). This allows the application (which is hopefully not time-critical, as such emulation is hardly guaranteed to be speedy) to treat all displays as truecolor, with the target sitting as a layer in between doing color space conversion, dithering, etc. Actual output goes to another LibGGI target, such as Xlib. Another target is Multi. This target allows an application to manipulate a single visual, but actually draw to multiple visuals, producing identical output on each. For example, one might want to display identical X windows on different servers for a demonstration.

The important point to note is that a well written LibGGI application does not need to be recompiled to support each specific target. However, an application may request a specific target, in which case the end user must have it on his machine or the application will not run. Such impolite programs are discouraged. The most appropriate and most capable target is used when a program is run. For example, a program using LibGGI run in X will bring itself up in an X window. That same program (as in the same compiled binary on disk) will, at the console, make use of native GGI hardware support (user/kernel cooperation, described in the next section), SVGALIB, or AAlib (in approximately that order.) The most capable display target will be used, but in some cases a poorly written application might ask too much (or hardware supply too little.) It would be absurd, for example, to ask the AAlib target for a 1024x768 truecolor visual. Although AAlib might try to emulate it, the target is free to reject the request and the application may negotiate for a lower resolution or bit depth. Similarly, to support new targets the library itself need not be recompiled either. Targets are specified in a global configuration file (/etc/ggi/libggi.conf on Unix machines) and are loaded only at runtime by need.

LibGGI is currently fairly usable, although undergoing some redesign. Brave programmers who don't mind that they may have to adapt to a couple more API shifts before things settle nicely in stone will probably benefit by signing on to the LibGGI bandwagon. In my opinion, LibGGI has very nice, clean API that is very easy to learn, especially compared to the horrors of SVGALIB or X programming currently in the Linux world.

[LibGGI flow diagram]

Kernel Graphics Interface - KGI

KGI is a standard graphics driver API. This API is abstract and intended to be portable, so a KGI driver for Linux could be ported to FreeBSD or Solaris x86 with a single recompile and possibly linking with wrapper libraries. There is also work on a wrapper (for Linux) that will allow KGI drivers to be statically linked into binaries (most useful for testing) and used on unmodified kernels. A KGI driver is compiled as a loadable module, which can be inserted into the kernel at any point, but usually at boot time. The reason for putting a graphics driver into the kernel is to address instability and security risks caused by purely userspace graphics When a graphics application crashes, the kernel is able to reset the video mode as part of other cleanup such as deallocating memory and closing file descriptors.

It may be argued, that putting an entire graphics system in the kernel would not only lead to bloat, but would be slow due to many user <---> kernel transitions. This is true, and this is what KGI seeks to avoid as much as possible. The primary purpose of KGI drivers is, in fact, modesettting (handling and keeping track of requests, during console switches, and resetting video mode after an application has quit/crashed). However, it must also protect insecure registers. It is quite possible to crash the system entirely by playing tricks with graphics cards registers (locking the PCI bus, for example), and for this reason the kernel must protect these sensitive parts of the video hardware. Such registers cannot be accessed from userspace applications for security reasons, and their functionality wrapped in a secure layer. Some badly designed graphics cards have dangerous registers intermingled with perfectly safe registers. In this case there is no choice: the granularity of the most processors does not extend to single video card registers like this, they must all be screened out. Well designed hardware, however, groups dangerous registers away from benign ones, and so userspace acceleration is possible.

For speed, there is a userspace library that linked into GGI that is specific to each KGI driver. This library handles secure graphics commands in userspace, but calls the kernel driver when it must access secure resources. This is referred to as library/kernel cooperation. Acceleration calls in userspace are fast and efficient, and so used whenever possible without sacrificing security. Finally true speed freak users may request that, at the possible expense of security and stability, security features be turned off and all the hardware hit from userspace, hopefully offering a performance boost.

KGI is currently being debugged and seeking drivers to be written for it. It is also investigating integration (or at least compatibility) with it's current competitor for (Linux) kernel graphics support, Geert Uytterhoeven's fbdev.

[KGI flow diagram]

Event Stacks - EvStacks

This is probably the most complex element of GGI. The author will admit now that, having not looked at the EvStack code, he does not have a full understanding of it. He is instead working from and passing descriptions and from the actual EvStacks to you, gentle reader.

EvStacks is a new console system for Linux (it may be ported to FreeBSD and other Open Source Unixes at some time in the future). It is intended to bring a more flexible and dynamic system for managing virtual consoles, including support for multiple video cards and input devices (known as multihead). This is accomplished by arranging the console subsystem into a tree. The different branches and leaves of this tree make up heads (generally a video card or similar output device with input devices bound to it) and virtual terminals. When events are generated they are broadcast through the tree starting at the root of their immediate parent. For example keyboard events bound only to one head will only be broadcast to that head. Events can pass through accept/deny/modify filters. These filters are called Stacks, they are the gateways that control message propagation and where EvStacks get their odd name (think of it as a "stack" of filters, right?)

What does this complex event-passing scheme give us? Well, quite a bit of flexibility for one. Messages being propagated across the tree can be translated into different forms and delivered to a wide variety of targets. For example, EvStacks makes it easy to support multiple terminal emulations on different heads (different monitors on different video cards), on different virtual terminals (different screens on the same video card), or upon hitting some special hotkey that switches emulation on the fly! If, for example, you wanted to use a German keyboard layout on your odd-numbered VT's and a American keyboard layout on the even-numbered ones, this is as easy as binding the German keymap to VT 1, 3, & 5 and the American keymap to VT 2, 4, & 6. Input events generated on the even VT's are translated using the German keyboard Event Stack, and events from the odd VT's similarly with the American keyboard Stack. EvStacks is controlled via the /proc interface, where methods and handlers and for various types of events are registered.

Aside from the obvious benefits to internationalization and the ability to manage many video and input connections (making possible, for example, to give two users a complete console with separate monitors, keyboards, and mice - essentially sharing the computer), the flexibility offered by EvStacks has suggested some more exotic uses to which it could be put. For example, replacing the standard console with a brail printer for blind users. Standard programs could have their output preprocessed and set to the printer. If such a printer were combined with a special keyboard (perhaps needed a special keymap) for a completely touch-based computing environment for the blind, it would benefit from tighter kernel integration.

Granted, this is already mostly possible with pipes, but the idea of natively supporting such devices at or near the kernel level is intriguing. However, routines using these hooks do not need to be in the kernel. They can exist userspace and be easily swapped in and out. A speech synthesis program that wished to take over a virtual terminal as the output device would of course run mostly in userspace but might benefit by hooking into the kernel. The argument in this case is not so much about what is possible as what is useful. Hacking around with pipes could certainly most problems, but tends to create specialized solutions to specific problems. What if the first device mentioned used a special brail keyboard, and you wanted to combine this keyboard with the speech synth program mentioned next? What if the brail keyboard and printer were combined in a single program that opened up a pipe and did all the I/O with that? You have to hack that program. What if keyboard and printer are EvStack devices? Just replace the printer with the speech synth and you're set). It is appealing to be able to hook in new and strange I/O devices and have them automatically work with existing systems via translation tables taking in weird events (say using a 3D joystick in place of a mouse) and translating them into standard events. Since such a translation function would exist in userspace for the sole purpose turning on even into another.. The alternative is custom programs for each very specific translation (an example would be my program, joy2key) which is not, ultimately, a satisfactory solution. EvStacks would, for example, allow translating all joystick events into keyboard arrow keys in the appropriate directions at the kernel level so that applications never know the different.

EvStacks will be basis of the GGI 0.10.0 release. It is presently in testing by a brave few. It will be a Linux kernel patch, and will include an option to not use it in kernel configuration (restoring most of the old console code) and in addition runs in "compatability mode" when no KGI driver is loaded, i.e. legacy SVGALIB programs and applications like XFree86 which access the hardware directly will be able to run. GGI will also provide an SVGALIB wrapper (more below) and a GGI-based X server.

[EvStacks flow diagram]

GGI subprojects

Note: all the Lib* libraries are extension libraries of LibGGI. They are built on and depend on LibGGI, but LibGGI doesn't depend on them. Thus, LibGGI remains nice and slim while we pile on the features in here

LibGGI2D
Library of advanced or "nonprimitive" drawing functions, including polygons, arcs, sprites, stretching bitmaps, etc, and anti-aliased versions of most of those.
LibGGI3D
Provides functionality specific to manipulating computer graphics in three dimensions. Based on OpenGL but intended to mainly provide a common hardware layer for writing higher level 3D libraries, such as Mesa-GGI
LibGGU
General Graphics Utilities. Common command line parsing, loading/saving images and various graphics formats, and other miscellaneous functions useful when dealing with computer graphics. Allows plugins to support new graphics formats.
LibGFont
Generic Font library. Provides a simple, consistent API for glyph rendering on top of other font engines (mainly FreeType and T1Lib) and also bitmap fonts, via a Plugin system to support different font types.
LibGWT
General Windowing Toolkit. Functions to manage the complex clipping and event delivery needed to support a simple windowing system
LibGII
General Input Interface. Manages user input. Allows event abstraction, i.e. application registers a unique event type (such as "player jumps") and the library takes care of input bindings. The application need only worry about "player jumps" events, not stuff like "keypad_up pressed" and "gray_pgdown released" events.
SVGALIB Wrapper
To support legacy SVGALIB applications, the SVGALIB Wrapper is a library mimicking the SVGALIB API that wraps around LibGGI. It brings most of the advantages of GGI while retaining compatibility with old programs.

Peter Amstutz
Last modified: Tue May 5 23:42:57 EDT 1998