This document describes the application programming interface for LibGGI, an extremely flexible and extensible, dynamic drawing library developed by the GGI project.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
There should not be any more changes to the LibGGI API until the next major version. Unfortunately, implementation and documentation do not always match; if you encounter any such differences or have any other questions or comments on this documentation, please mail us at <steve@ggi-project.org>, or the GGI mailing list. Thank you.
For other developers: if you want to change this manual and do not know DocBook/SGML, just mail me the ASCII version and I will gladly format and include it for you.
This manual is primarily written as a reference to the user-visible API of LibGGI. However, it is being expanded to cover basic graphics concepts as well. It will not be a full tutorial, as actual working code (as found in the demos/ directory of the LibGGI distribution) serves that function better. This manual is also not intended to be an LibGGI internals guide, although implementation hints are dropped throughout the manual :)
This documentation is written in DocBook. The SGML source can be used to generate Unix manpages, HTML and text formats automatically.
LibGGI, the dynamic GGI (General Graphics Interface) library is a flexible drawing library.
It provides an opaque interface to the display's acceleration functions. It was originally intended to allow user programs to interface with KGI, the GGI Kernel Graphics Interface, but other display types can be easily used by loading the appropriate "display target" (e.g. X, memory).
LibGGI consists of a main library (libggi.so) and a multitude of dynamic drivers. The library then loads the necessary "drivers" for the requested mode, taking hints from the graphics device if necessary.
LibGGI can also load extension libraries, e.g. to provide enhanced 2D and 3D functions.
It has been designed after having a look at several existing libraries, and so far we have found porting to be quite simple from and to most of them.
To use LibGGI, #include ggi/ggi.h in a C program. LibGGI functions are prefixed with ggi, and other #defines are prefixed with GGI_.
Most LibGGI functions return 0 to indicate success, and a negative error code to indicate errors. Return codes greater than 0 are usually additional hints or other non-negative integer data.
A list of error codes and descriptions can be found in the ggi/errors.h file, which is part of the LibGGI/LibGG package.
ggiInit initalizes the library. This function must be called before using other LibGGI functions; otherwise the results will be undefined.
ggiExit uninitializes the library (after being initalized by ggiInit) and automatically cleanup if necessary. This should be called after an application is finished with the library. If any GGI functions are called after the library has been uninitialized, the results will be undefined.
ggiInit allows multiple invocations. A reference count is maintained, and to completely uninitialize the library, ggiExit must be called as many times as ggiInit has been called beforehand.
ggiInit returns 0 for OK, otherwise an error code.
ggiExit returns:
after successfully cleaning up,
the number of 'open' ggiInit calls, if there has been more than one call to ggiInit. As ggiInit and ggiExit must be used in properly nested pairs, e.g. the first ggiExit after two ggiInits will return 1.
error, especially if more ggiExit calls have been done than ggiInit calls.
Example 3-1. Initialize and uninitialize LibGGI
if (ggiInit() != 0) { exit(1); /* can't start! */ } /* Do some LibGGI stuff */ ggiExit();
ggiPanic does a graceful shutdown, with printf(3) -style reporting, taking a format string and any additional variables. It will shut down the graphics modes active, close all visuals, print the given error message to stderr, and then exit the application.
ggiPanic should only be used by usermode programs when something is really screwed, and they do not know what to do. The same applies for libraries, but might be used in rare situations such as corruption of critical data structures.
A visual is simply a thing you can draw on. For example, a Virtual Console in fullscreen-mode, an X window, an invisible memory area, or a printer. It is identified by its handle of type ggi_visual_t, which is given to all drawing functions to indicate which visual to operate on.
Note: Some functions involving drawing, color, palette and frames do not work until you have set a mode on a visual.
Each visual is completely independent of other visuals. You can use these visuals to display on multiple monitors and/or in multiple windows or to work on "virtual" graphics devices like in-memory pixmaps or even PPM files on disk.
Most LibGGI functions are passed a visual returned by ggiOpen to know where they should operate on.
ggi_visual_t is opaque to the user. Do not try to access any part of the structure directly. It may change without notice.
ggiOpen opens a visual (along with associated inputs). The visual is specified as a display string, followed by NULL. If only NULL is specified, the default display target is opened. (This may be specified by the user.)
The other arguments are for internal purposes only, such as argptr, used to pass driver-specific, non-textual information to the driver.
ggiClose releases and destroys an open visual and its associated internal control structures. This will close X windows, return consoles to text-mode, etc.
ggiOpen returns the opened visual (ggi_visual_t), or NULL for error.
ggiClose returns 0 for OK, otherwise an error code.
Example 4-1. Open default visual
ggi_visual_t vis = ggiOpen(NULL); if(vis==NULL) { ggiPanic("Couldn't open default visual!\n"); }
Example 4-2. Open a memory visual
ggi_visual_t memvis = ggiOpen("display-memory", NULL); if(memvis==NULL) { return -1; }
Example 4-3. Closing visuals
ggiClose(memvis); ggiClose(vis);
After opening the visual, you must set a mode (specifying e.g. the dimensions, how many colors, etc.) before you can do anything useful with it, such as drawing.
This structure defines a visual's (graphics) mode.
The definition in ggi/ggi.h is:
typedef struct { sint16 x, y; } ggi_coord; typedef struct /* requested by user and changed by driver */ { uint32 frames; /* frames needed */ ggi_coord visible; /* vis. pixels, may change slightly */ ggi_coord virt; /* virtual pixels, may change */ ggi_coord size; /* size of visible in mm */ ggi_graphtype graphtype; /* which mode ? */ ggi_coord dpp; /* dots per pixel */ } ggi_mode;
You usually don't need to care about the definition, if you want to set a mode, but it is necessary if you want to find out the mode actually being set.
ggi_coord represents any (x,y) coordinate.
Use of multiple buffering is specified in the frames member of the ggi_mode struct, as the number of screen buffers needed.
Tip: Multiple buffering is the allocation of multiple framebuffers and drawing on/reading/displaying them independently. Applications use double-buffering to prepare a picture offline and switching display to it.
visible area represents the area of the visual that is visible to the user at any time.
ggi_graphtype specifies the graphic type:
Number of significant bits (e.g. bits representing the actual color or some other property of the pixel)
Applications can use GT_AUTO in any of these fields to get a 'recommended' setting.
The four aspects of a graphic type above are packed into a ggi_graphtype. See ggi/types.h for details about the possible schemes and subschemes.
There are also macros which represent some common ggi_graphtypes, mainly for compatibility:
equivalent to the GGI_AUTO (see next section) for graphic types. It indincates that any default value may be taken. Where possible this is currently a graphics mode, often the highest.
text modes with word- and longword-aligned characters and attributes.
(these modes use a palette.)
graphics modes with the respective bits per pixel. Note that for 32-bit modes, only the size (bits per pixel) is 32-bits, the depth (significant bits) is 24-bit.
If a given ggi_mode is set or checked, any size parameter may be GGI_AUTO (defined in ggi/types.h). It is a placeholder.
When GGI_AUTO is encountered in a parameter, it is replaced with corresponding values specified by GGI_DEFMODE variable.
If the corresponding value is not found in GGI_DEFMODE or that value is also GGI_AUTO, then the driver should select a reasonable value satisfying as many of the given constraints as possible. The following are some recommendations:
If a visible size is given but no virtual, the lowest reasonable (taking alignment and accelleration constraints into account) should be used.
If either visible x or y is given, the other one should give a x/y ratio close to that of the screen size, so normally about 4/3.
If a virtual size is given but no visible, the highest possible visible size is selected.
If no size is given, the driver uses some builtin default.
If the graphtype is unspecified, the highest possible graphtype that meets the geometry constraints is set/suggested.
dpp.x and dpp.y will be 1 for graphics. For text modes the largest possible dpp.x and dpp.y (the most fine-grained font) should be used.
frames will be 1 if not specified.
Note: The rules above are only recommendations (which are hoped to best capture user expectations) to the driver implementer and in no way guaranteed.
There are three types of mode calls:
The ggiCheck*Mode functions test whether a given mode is acceptable or suggest a better one,
The ggiSet*Mode functions try to set the specified mode and fail if that is impossible. ggiSetMode then returns a better suggestion, as ggiCheck*Mode do. (The suggested mode is not silently set.)
ggiGetMode returns the mode that is currently set.
If a given mode can not be set, the structure passed is changed to the suggested mode as follows:
Resolutions are always adjusted up. If you want the next lower, start out at 1,1 (or somewhere else reasonable) and jump up the ladder.
Only if the maximum resolution would be exceeded, resolutions are adjusted down to the maximum.
The above applies to visible and virtual size. If there is interference between them, the visible size is satified first if possible, then the virtual size.
Note that the adjustment of one value do not normally affect other values. For example, if (visible) 320x100 (virtual 320x200) is requested, the visible size may be adjusted to 320x200, but virtual size will be left alone. Of course, if the virtual size becomes less than visible size, then it will be adjusted as well. A good rule of thumb is that anything returned by ggiCheckMode is minimally valid.
Font sizes (i.e. the size of the pixel in textmodes) are handled the other way round: they are adjusted down except when there is nothing below.
Graphtype:
The basic rationale is to change graphtype only, if the card does not support it at all. If the maximum resolution is exceeded, then that is adjusted down and not the graphtype. This assumes, that if you request true-color, you really want that and not so badly the resolution you requested. If this is not the case, you can still retry with another graphtype or GT_AUTO.
If it is necessary to change to change the graphtype, the order should be ascending (if possible), i.e. 1->4->8->15->16->24/32 bit. So you always get a mode which can do more than you requested. Only when no better modes are available, the type is adjusted down.
It is possible to pass GGI_AUTO as any of the parameters. GGI_AUTO is a placeholder (e.g. the program does not care about a specific parameter). The display target will reset GGI_AUTO parameter to a default, such as the maximum, preferred, or GGI_DEFMODE resolution, while being compatible with the other settings. The presence of GGI_AUTO is not flagged as an error.
If ggiSet{Graph,Text}Mode is given GGI_AUTO as one or more of its arguments, it will silently set a mode. To get back this mode, you must use ggiGetMode.
Note: The resulting mode is guaranteed to be valid; if not, the application can assume that it cannot set any mode on the given visual and give up.
#include <ggi/ggi.h>
int ggiSetMode
(ggi_visual_t vis, ggi_mode *tm);
int ggiCheckMode
(ggi_visual_t vis, ggi_mode *tm);
int ggiGetMode
(ggi_visual_t vis, ggi_mode *tm);
ggiSetMode sets any mode (text or graphics).
ggiCheckMode checks whether or not the given mode (text or graphics) will work on the visual.
For both ggiSetMode and ggiCheckMode, normal mode negotiation is performed, and if the given mode is not possible, modifications will be made to the passed structure to make it work. If GGI_AUTO (or GT_AUTO for the graphtype) is specified for any of the members of *tm, these are filled in with the recommended parameters.
For ggiSetMode, if the mode cannot be set, a mode is suggested in *tm, but it will not be silently set. However, you'll probably want to use the convenience functions ggiSetTextMode and ggiSetGraphMode instead of ggiSetMode.
ggiGetMode gets the current mode set on the visual.
ggiSetMode returns 0 if the mode is set successfully, otherwise an error code.
For ggiCheckMode, a return of 0 means that a ggiSetMode call for the given mode mode would succeed. Otherwise, the mode given cannot be set. (But the modified mode passed back to the application should work.)
For both functions, if the only modifications made to the structure is filling GGI_AUTO parameters, it is still considered as success.
ggiGetMode gets the current mode. tm will be filled with the current mode on return.
#include <ggi/ggi.h>
int ggiSetTextMode
(ggi_visual_t visual,
int cols, int rows,
int vcols, int vrows,
int fontx, int fonty);
int ggiCheckTextMode
(ggi_visual_t visual,
int cols, int rows,
int vcols, int vrows,
int fontx, int fonty,
ggi_mode *suggested_mode, ...);
int ggiSetGraphMode
(ggi_visual_t visual,
int x, int y,
int xv, int yv,
ggi_graphtype type);
int ggiCheckGraphMode
(ggi_visual_t visual,
int x, int y,
int xv, int yv,
ggi_graphtype type,
ggi_mode *suggested_mode, ...);
int ggiSetSimpleMode
(ggi_visual_t visual,
int x, int y,
int frames,
ggi_graphtype type);
int ggiCheckSimpleMode
(ggi_visual_t visual,
int x, int y,
int frames,
ggi_graphtype type,
ggi_mode *suggested_mode);
The ggiCheck{Text,Graph,Simple}Mode and ggiSet{Text,Graph,Simple}Mode are convenient versions of ggiSetMode. They accept the mode parameters as integer arguments rather than as a ggi_mode struct that the application has to fill out. Otherwise, they are functionally equivalent to the 'master' ggiCheckMode and ggiSetMode functions, and the same mode-setting semantics apply.
ggiSetTextMode sets a textmode with given columns and rows, virtual columns and rows and a font of the given size.
ggiCheckTextMode checks a textmode with given columns and rows, virtual columns and rows and a font of the given size.
ggiSetGraphMode sets a graphics mode with a visible area of size x,y and a virtual area of size xvyv and the specified graphics type. (You can pan around the virtual area using ggiSetOrigin.)
ggiCheckGraphMode checks a graphics mode with the specified mode features.
ggiSetSimpleMode sets a graphics mode with a visible area of size xy and frames number of frames and the specified graphics type.
ggiCheckSimpleMode checks a graphics mode with the specified mode features.
If suggested_mode is not NULL, then it will be filled with the suggested mode, as documented under ggiCheckMode.
In text modes, the font size is the size of the pixel (ggi_mode.dpp).
For ggiCheckTextMode and ggiCheckGraphMode, a return of 0 means that a ggiSet*Mode call for this mode would succeed. Otherwise, the mode given cannot be set. In this case, suggested_mode is changed to the suggested mode.
For ggiSetTextMode and ggiSetGraphMode, a return of 0 indicates success, while any other value indicates an error.
Example 5-1. Try a 320x200x8 mode
err = ggiCheckGraphMode(vis, 320, 200, GGI_AUTO, GGI_AUTO, GT_8BIT, &sug_mode, NULL); if(err) { /* Check if returned mode is ok... /* } else { ggiSetMode(&sug_mode); }
#include <ggi/ggi.h>
int ggiParseMode
( const char *s, ggi_mode *m);
int ggiPrintMode
(ggi_mode *m);
int ggiSPrintMode
(char *s, ggi_mode *m);
int ggiFPrintMode
(FILE *s, ggi_mode *m);
ggiParseMode parses a string into a ggi_mode.
The ggi*PrintMode functions print all the members of ggi_mode in a human-readable form. ggiSPrintMode outputs to a preallocated string buffer, ggiFPrintMode outputs to a stdio FILE, and ggiPrintMode outputs to standard output. These functions correspond to sprintf(3), fprintf(3), printf(3), respectively.
The format of the string used by these functions is exactly the same as the one used in libggi(7) environment variables.
ggiParseMode returns:
on success, i.e. the string was correct. However, errors involving GT_*, position information, or mismatched brackets do not make it fail; these errors are simply ignored.
if there is text that can not be parsed. This text is printed to stderr. All parameters parsed so far are written into m.
So m contains all parameters that have been successfully parsed. For most applications there will be no need for testing ggiParseMode for failure.
#include <ggi/ggi.h>
int ggiSetDisplayFrame
(ggi_visual_t vis, int frameno);
int ggiSetWriteFrame
(ggi_visual_t vis, int frameno);
int ggiSetReadFrame
(ggi_visual_t vis, int frameno);
int ggiGetDisplayFrame
(ggi_visual_t vis);
int ggiGetWriteFrame
(ggi_visual_t vis);
int ggiGetReadFrame
(ggi_visual_t vis);
These functions are used for selecting or getting the current buffers, when using the multiple buffering function of LibGGI.
ggiSetDisplayFrame sets the frame that gets displayed.
ggiSetWriteFrame sets the frame for write operations such as ggiPuts and ggiHLine.
ggiSetReadFrame sets the frame for read operations, like ggiGetPixel and the ggiCrossBlit source.
ggiGetDisplayFrame reports the frame currently displayed.
ggiGetWriteFrame reports the frame currently written to.
ggiSetReadFrame reports the frame currently read from.
Frames are numbered from 0 to the number of frames requested - 1.
Visuals may have an indirect mapping off the pixel-value to a color via a programmable palette. This is e.g. true for the 8 bit IBM VGA modes. But even for "direct-mapped" modes, you will need to know which color maps to which pixel-value.
Try to cache the results of palette lookups in your application for efficiency purposes.
The ggi_color struct has 16 bit wide entries for red (.r), green (.g), and blue (.b) values. It also has an alpha value (.a>) which is unused in libggi, but allow LibGGI extensions to store an alpha value there.
Please scale your palette values as necessary.
The ggi_pixel, or the pixelvalue is a hardware-dependent representation of a color. A pixelvalue is usually obtained from a ggi_color by ggiMapColor or read from the visual by ggiGetPixel. You can safely assume that the relationship between a ggi_color and its associated ggi_pixel value does not change unless you change the visual or the mode or the palette.
You can also do calculations with ggi_pixel values. Their format is defined in Chapter 8.
#include <ggi/ggi.h>
ggi_pixel ggiMapColor
(ggi_visual_t vis, ggi_color *col);
int ggiUnmapPixel
(ggi_visual_t vis,
ggi_pixel pixel, ggi_color *col);
int ggiPackColors
(ggi_visual_t vis,
void *buf,
ggi_color *cols,
int len);
int ggiUnpackPixels
(ggi_visual_t vis,
void *buf,
ggi_color *cols,
int len);
ggiMapColor gets the pixelvalue for the given color.
ggiUnmapPixel gets the color associated with the given pixelvalue.
ggiPackColors converts the colors in cols to pixelvalues in buf. The output from this function is suitable for input to the ggiPut{HLine,VLine,Box} functions.
ggiUnpackPixels converts the pixelvalues in buf to individual elements of cols. This function maybe used to convert buffers output by the ggiGet{HLine,VLine,Box} functions from the pixelvalue representation to their actual colors.
The buffers output from ggiPackColors and the input to ggiUnpackPixels are in the same format as the get/put buffers. Their format is defined in ggi_pixelformat.
ggiMapColor returns a ggi_pixel.
ggiUnmapPixel, ggiPackColors, and ggiUnpackPixels returns 0 for OK, otherwise an error code.
#include <ggi/ggi.h>
int ggiGetPalette
(ggi_visual_t vis,
int s,
int len,
ggi_color *cmap);
int ggiSetPalette
(ggi_visual_t vis,
int s,
int len,
ggi_color *cmap);
LibGGI visuals in GT_PALETTE mode maps all pixelvalues to the corresponding ggi_color entry in the visual's palette (also known as a colormap or CLUT).
ggiSetPalette sets a range of palette values, of length len, starting at index number s. The index can be GGI_PALETTE_DONTCARE to indicate to indicate that the palette can be installed anywhere in the CLUT. This allows optimised use in windowing environments (to minimize color flashing between windows) and should be used if possible.
ggiGetPalette copies the specified colors (starting from s, for len colors) from the visual's palette to the array pointed by cmap.
ggiSetPalette returns the number of the first entry changed. Negative values indicate error (codes).
ggiGetPalette returns 0 for OK, otherwise an error code. When called with len=0 this function will not automatically succeed, but the return code will indicate whether there is a readable CLUT.
LibGGI guarantees that there will be a default palette when a palettized mode is set. What this default is, however, is dependent on the visual. For example, the X target deliberately avoids setting all colors to avoid color-flashing when moving between windows.
Applications that want to ensure that they have a full scale of all colors can call ggiSetColorfulPalette after mode set. This function uses a smarter color allocation scheme, causing good colors but still minimal flashing in windowed targets.
ggiSetColorfulPalette returns the number of the first entry changed. Negative values indicate error (codes).
#include <ggi/ggi.h>
int ggiSetGammaMap
(ggi_visual_t vis,
int s, int len,
ggi_color *gammamap);
int ggiGetGammaMap
(ggi_visual_t vis,
int s, int len,
ggi_color *gammamap);
int ggiSetGamma
(ggi_visual_t vis,
ggi_float r,
ggi_float g,
ggi_float b);
int ggiGetGamma
(ggi_visual_t vis,
ggi_float *r,
ggi_float *g,
ggi_float *b);
Some truecolor modes on some hardware can use the DAC's palette to lookup the values before sending to the monitor. Generally this is used for gamma correction by filling the lookup table with a curve, hence the name "gamma map", but it could be used for other things e.g. special effects in games.
ggiSetGammaMap and ggiGetGammaMap sets or gets the gamma map, for len colors starting at s.
ggiSetGamma and ggiGetGamma sets or gets the gamma correction for the visual.
Returns a pointer to the ggi_pixelformat structure.
Modifying the structure returned is not allowed. Do not attempt to free the pointer returned.
The ggi_pixelformat structure describes the format of a ggi_pixel. An application would use this if it wanted to directly output pixelvalues, rather than calling ggiMapColor or ggiPackColors to convert a ggi_color to a ggi_pixel value.
Other than the parameters of a ggi_graphtype that you can specifically request at mode setting, there is no other way to change the parameters of ggi_pixelformat. An application must not assume the presence of any particular pixelformat. If the application cannot handle a particular pixelformat, it should fall back on ggiMapColor, ggiPackColors or ggiCrossBlit.
/* Pixelformat for ggiGet/Put* buffers and pixellinearbuffers */ typedef struct { int depth; /* Number of significant bits */ int size; /* Physical size in bits */ /* * Simple and common things first : * * Usage of the mask/shift pairs: * If new_value is the _sizeof(ggi_pixel)*8bit_ value of the thing * you want to set, you do * * *pointer &= ~???_mask; // Mask out old bits * *pointer |= (new_value>>shift) & ???_mask; * * The reason to use 32 bit and "downshifting" is alignment * and extensibility. You can easily adjust to other datasizes * with a simple addition ... */ /* Simple colors: */ ggi_pixel red_mask; /* Bitmask of red bits */ int red_shift; /* Shift for red bits */ ggi_pixel green_mask; /* Bitmask of green bits */ int green_shift; /* Shift for green bits */ ggi_pixel blue_mask; /* Bitmask of blue bits */ int blue_shift; /* Shift for blue bits */ /* A few common attributes : */ ggi_pixel alpha_mask; /* Bitmask of alphachannel bits */ int alpha_shift; /* Shift for alpha bits */ ggi_pixel clut_mask; /* Bitmask of bits for the clut */ int clut_shift; /* Shift for bits for the clut*/ ggi_pixel fg_mask; /* Bitmask of foreground color */ int fg_shift; /* Shift for foreground color */ ggi_pixel bg_mask; /* Bitmask of background color */ int bg_shift; /* Shift for background color */ ggi_pixel texture_mask; /* Bitmask of the texture (for textmodes - the actual character) */ int texture_shift; /* Shift for texture */
The above is used to describe a pixel simply. More detailed information, if required, can be obtained from the following fields. See ggi/ggi.h for a listing of bitmeanings.
/* * Now if this doesn't suffice you might want to parse the following * to find out what each bit does: */ uint32 bitmeaning[sizeof(ggi_pixel)*8]; /* Shall we keep those ? */ uint32 flags; /* Pixelformat flags */ uint32 stdformat; /* Standard format identifier */ /* This one has only one use for the usermode application: * To quickly check, if two buffers are identical. If both * stdformats are the same and _NOT_ 0 (which means "WEIRD"), * you may use things like memcpy between them which will have * the desired effect ... */ } ggi_pixelformat; /* Pixelformat flags */ #define GGI_PF_REVERSE_ENDIAN 0x01 #define GGI_PF_HIGHBIT_RIGHT 0x02 #define GGI_PF_HAM 0x04 #define GGI_PF_EXTENDED 0x08
depth and size are same as the depth and access size information specified in the ggi_graphtype.
clut_mask is used in GT_PALETTE modes, indicating which bits correspond to an index to the CLUT (color look-up table).
fg_mask, bg_mask, and texture_mask are for text modes only, indicating the parts of the text-mode character.
LibGGI has a current context associated with each visual. This is done for performance reasons, as LibGGI can set up pointers to optimized functions when the GC changes (which can be monitored, as it may only be changed by the functions mentioned below), or switch the hardware state efficiently.
#include <ggi/ggi.h>
int ggiSetGCForeground
(ggi_visual_t vis, ggi_pixel color);
int ggiGetGCForeground
(ggi_visual_t vis, ggi_pixel *color);
int ggiSetGCBackground
(ggi_visual_t vis, ggi_pixel color);
int ggiGetGCBackground
(ggi_visual_t vis, ggi_pixel *color);
ggiSetGCForeground and ggiGetGCForeground set or reads the current colors for the foreground, used in all normal drawing functions.
ggiSetGCBackground and ggiGetGCBackground set or reads the current colors for the background, used in two-color operations like drawing text.
#include <ggi/ggi.h>
int ggiSetGCClipping
(ggi_visual_t vis,
int left, int top,
int right, int bottom);
int ggiGetGCClipping
(ggi_visual_t vis,
int *left, int *top,
int *right, int *bottom);
ggiSetGCClipping sets the the current clipping rectangle to (left,top)-(right,bottom). right and bottom are the bottom right corner of the rectangle + 1.
Initially the clipping rectangle is the whole virtual screen.
All LibGGI drawing primitives obey the clipping rectangle.
ggiGetGCClipping reads the coordinates of the current clipping rectangle.
LibGGI has three basic types of primitives when it comes to filling rectangular areas (including the degenerate cases of horizontal and vertical lines and single pixels).
We have found three operations commonly performed on such areas:
Draw: This means you set all contained pixels to the current foreground color (maybe modified by the update operations as set in the current graphics context).
Put: Fill the area with pixels of different value from a buffer.
Get: Read pixels from the screen into such a buffer.
Get/Put buffers are buffers used by the functions ggiGet*, ggiPut*, ggiPackColors and ggiUnpackPixels.
The format of the individual pixels in get/put buffers are defined by ggi_pixelformat (see the ggi_pixelformat section above).
Pixels are stored linearly, e.g. a rectangle with a width of three and a height of two will be stored with pixels (0,0) (1,0) (2,0) (0,1) (1,1), (2,1) in that order.
Get/put buffers use chunky pixels, unpacked, even if their representation in the framebuffer is packed (i.e. pixel size not multiple of 8 bits) or non-linear. Thus, the application does not need to know how to use planar or packed pixels for non-direct acccess.
Note: (You may specify use of packed buffers using the GT_SUB_PACKED_GETPUT ggi_graphtype flag, but as of this writing, no targets implement that yet.)
The get/put buffer passed to the LibGGI functions should be allocated for at least width * height * ((ggiGetPixelFormat()->size+7)/8) bytes.
(That is, the pixel size is obtained from ggi_pixelformat.size, rounded to a multiple of 8 bits (one byte), and is multiplied by the width and the height of the buffer.)
#include <ggi/ggi.h>
int ggiDrawPixel
(ggi_visual_t vis,
int x,
int y);
int ggiPutPixel
(ggi_visual_t vis,
int x,
int y,
ggi_pixel col);
int ggiGetPixel
(ggi_visual_t vis,
int x,
int y,
ggi_pixel *col);
#include <ggi/ggi.h>
int ggiDrawHLine
(ggi_visual_t vis, int x, int y, int w);
int ggiPutHLine
(ggi_visual_t vis, int x, int y, int w, void *buf);
int ggiGetHLine
(ggi_visual_t vis, int x, int y, int w, void *buf);
Draw, put, or get a horizontal line from (x,y), extending w pixels in the positive x direction (normally right). The height is one pixel.
#include <ggi/ggi.h>
int ggiDrawVLine
(ggi_visual_t vis, int x, int y, int h);
int ggiPutVLine
(ggi_visual_t vis, int x, int y, int h, void *buf);
int ggiGetVLine
(ggi_visual_t vis, int x, int y, int h, void *buf);
Draw, put, or get a vertical line from (x,y), extending h pixels in the positive y direction (normally down). The width of the line is one pixel.
#include <ggi/ggi.h>
int ggiDrawBox
(ggi_visual_t vis, int x, int y, int w, int h);
int ggiPutBox
(ggi_visual_t vis,
int x, int y, int w, int h,
void *buf);
int ggiGetBox
(ggi_visual_t vis,
int x, int y, int w, int h,
void *buf);
Draw, put, or get a rectangle at (x,y), extending w pixels in the positive x direction and h pixels in the positive y direction.
Fills the entire virtual screen with the current foreground color. It maybe more efficient than the corresponding call to ggiDrawBox().
Dependent on the visual and runtime environment found, applications may be granted direct access to hardware and/or library internal buffers. This may significantly enhance performance for certain pixel oriented applications or libraries.
The DirectBuffer is a mechanism in which a LibGGI program can use to determine all the characteristics of these buffers (typically the framebuffer), including the method of addressing, the stride, alignment requirements, and endianness.
However, use not conforming to this specification will have undefined effects and may cause data loss or corruption, program malfunction or abnormal program termination. So you don't really want to do this.
Only the framebuffer is defined currently.
A frame buffer may be organized as several distinct buffers. Each buffer may have a different layout. This means both the addressing scheme to be used as well as the addressing parameters may differ from buffer to buffer.
LibGGI currently has support for pixel-linear buffers, bit-planar buffers, and interleaved planar buffers.
A linear buffer is a region in the application's virtual memory address space. A pixel with the pixel coordinates (<x>,<y>) is assigned a pixel number according to the following formula: pixel_number = (origin_y + y) * stride + origin_x + x;
In any case both x and y must not be negative, x must be less than size_x and y must be less than size_y. For top-left-origin screen coordinates, stride and origin_y will both be positive. For bottom-left-origin screen coordinates, stride and origin_y will both be negative. This will result in the correct pixel number with the same formula in both cases. The pixel number will be used to address the pixel.
A certain number bpp of bits is stored per pixel, and this is indicated in the ggi_pixelformat.access field. For some visuals, the buffer might not be in host CPU native format and swapping operations need to be performed before writes or after reads.
Read and write access to the buffer is done using load and store instructions of the host CPU. The access width and alignment requirements are specified in the ggi_directbuffer structure.
Read operations should be performed using the read buffer and write operations should be performed using the write buffer. These might be the same, but need not. If they are, read/write may be done to either buffer. Please note, that either read or write may be NULL. These are write-only or read-only buffers, which might be caused by hardware limitations. Such buffers are not suited to do Read-Modify-Write operations, so take care.
More importantly, certain DirectBuffers need to be explicitly acquired (i.e. locked) before using (accessing their pointers). Such a situation may arise if the underlying visual supports mixed acceleration and framebuffer access, but they cannot occur at the same time. In that case, LibGGI needs to be informed when the application is using the framebuffer. An acquire is done by using ggiResourceAcquire(3) and it is released by calling ggiResourceRelease(3) You can determine whether the DirectBuffer needs to be acquired by using ggiResourceMustAcquire(3)
Be aware that the read, write and stride fields of the DirectBuffer may be changed by an acquire, and that they may be NULL or invalid when the DirectBuffer is not acquired.
Paged buffers are indicated with page_size != 0 in ggi_directbuffer.
Successive access to addresses addr0 and addr1 of either read or write buffers with addr0 / page_size != addr1 / page_size may be very expensive compared to successive accesses with addr0 / page_size == addr1 / page_size.
On i386 the penalty will be about 1500 cycles plus 4 cycles per to be remapped. Because of this, block transfer operations might become very inefficient for paged buffers. If there are two different buffers provided for read and write operations, you should do successive reads from one and do successive writes to the other. If not, it is recommended to copy pagewise into a temporary buffer and then to copy this temporary buffer back to screen.
typedef enum { blPixelLinearBuffer, blPixelPlanarBuffer, blExtended, blLastBufferLayout } ggi_bufferlayout; typedef struct { int stride; /* bytes per row */ ggi_pixelformat *pixelformat; /* format of the pixels */ } ggi_pixellinearbuffer; typedef struct { int next_line; /* bytes until next line */ int next_plane; /* bytes until next plane */ ggi_pixelformat *pixelformat; /* format of the pixels ??? */ /* shouldn't that rather describe the _planes_, then ??? becka */ } ggi_pixelplanarbuffer; /* Buffer types */ #define GGI_DB_NORMAL 0x0001 /* "frame" is valid when set */ #define GGI_DB_EXTENDED 0x0002 #define GGI_DB_MULTI_LEFT 0x0004 #define GGI_DB_MULTI_RIGHT 0x0008 /* Flags that may be or'ed with the buffer type */ #define GGI_DB_SIMPLE_PLB 0x00010000 /* GGI_DB_SIMPLE_PLB means that the buffer has the following properties: type=GGI_DB_NORMAL read=write layout=blPixelLinearBuffer */ typedef struct { uint32 type; /* buffer type */ int frame; /* framenumber (GGI_DB_NORMAL) */ /* access info */ void *read; /* buffer address for reads */ void *write; /* buffer address for writes */ unsigned int page_size; /* zero for true linear buffers */ uint32 noaccess; /* bitfield. bit x set means you may _not_ access this DB at the width of 2^x bytes. Usually 0, but _check_ it. */ uint32 align; /* bitfield. bit x set means you may only access this DB at the width of 2^x bytes, when the access is aligned to a multiple of 2^x. Note that bit 0 is a bit bogus here, but it should be always 0, as then ((noaccess|align)==0) is a quick check for "no restrictions". */ ggi_bufferlayout layout; /* The actual buffer info. Depends on layout. */ union { ggi_pixellinearbuffer plb; ggi_pixelplanarbuffer plan; void *extended; } buffer; } ggi_directbuffer;
is the frame number as used in multiple buffering. Note that each frame can export more than one DirectBuffer.
is an enumeration specifying whether the buffer is pixel-linear, planar, etc.
is a union of all buffer info. Check the layout member to see which member of use.
Please see Chapter 8 for information on ggi_pixelformat struct, which describes the format of the pixels for pixel-linear buffers.
#include <ggi/ggi.h>
int ggiDBGetNumBuffers
(ggi_visual_t vis);
const ggi_directbuffer *ggiDBGetBuffer
(ggi_visual_t vis, int bufnum);
ggiDBGetNumBuffers returns the number of DirectBuffers available to the application. ggiDBGetBuffer obtains the DirectBuffer at the specified position.
Use ggiDBGetBuffer to obtain the DirectBuffers at 0 to n-1, where n is the number returned by ggiDBGetNumBuffers.
Pixel-linear buffers have type==GGI_DB_SIMPLE_PLB | GGI_DB_NORMAL. You're on your own now.
DirectBuffers where ggiResourceMustAcquire(3) is true need to be 'acquired' (i.e. locked) before using. An acquire is done by using ggiResourceAcquire(3) and it is released by calling ggiResourceRelease(3) Beware that the read, write and stride fields of the DirectBuffer may be changed by an acquire, and that they may be NULL or invalid when the DirectBuffer is not acquired.
ggiDBGetNumBuffers returns the number of DirectBuffers available. 0 indicates that no DirectBuffers are available.
ggiDBGetBuffer returns a pointer to a DirectBuffer structure.
Example 11-1. How to obtain a DirectBuffer
ggi_visual_t vis; ggi_mode mode; int i; /* Framebuffer info */ unsigned char *fbptr[2]; int stride[2]; int numbufs; mode.frames = 2; /* Double-buffering */ mode.visible.x = 640; /* Screen res */ mode.visible.y = 480; mode.virt.x = GGI_AUTO; /* Any virtual resolution. Will usually be set mode.virt.y = GGI_AUTO; to be the same as visible but some targets may have restrictions on virtual size. */ mode.graphtype = GT_8BIT; /* Depend on 8-bit palette. */ mode.dpp.x = mode.dpp.y = GGI_AUTO; /* Always 1x1 but we don't care. */ if(ggiInit()) { /* Failed to initialize library. Bomb out. */ } vis = ggiOpen(NULL); if(!vis) { /* Opening default visual failed, quit. */ } if(ggiSetMode(vis, &mode)) { /* Set mode has failed, should check if suggested mode is o.k. for us, and try the call again. */ } numbufs = ggiDBGetNumBuffers(vis); for(i = 0; i < numbufs; i++) { ggi_directbuffer *db; int frameno; db = ggiDBGetBuffer(vis, i); if(!(db->type & GGI_DB_SIMPLE_PLB)) { /* We don't handle anything but simple pixel-linear buffers. Fall back to ggiPutBox() or something. */ continue; } frameno = db->frame; if(readptr[frameno] != NULL && (db->buffer.plb.pixelformat->flags & GGI_PF_REVERSE_ENDIAN)) { continue; } fbptr[frameno] = db->write; /* read == write for simple plbs */ /* Stride of framebuffer (in bytes). */ stride[frameno] = db->buffer.plb.stride; /* Check pixel format, be portable.... */
#include <ggi/ggi.h>
int ggiResourceAcquire
(ggi_resource_t res, uint32 actype);
int ggiResourceRelease
(ggi_resource_t res);
int ggiResourceMustAcquire
(ggi_resource_t res);
ggiResourceAcquire acquires (locks) a LibGGI resource, typically a DirectBuffer (see examples below). The actype indicates the desired access type for the operation. The following flags may be bitwise-or'ed together:
read access to the resource
write access to the resource
ggiResourceRelease releases (unlocks) an already-acquired resource.
ggiResourceMustAcquire determines whether or not the specified resource needs to be acquired before using.
ggiResourceAcquire and ggiResourceRelease return 0 on success, <0 on failure.
ggiResourceMustAcquire is simply a macro that returns true if the resource must be explicitly acquired and released, or false if not. However, it is still safe to call ggiResourceAcquire or ggiResourceRelease even in the latter case -- it would be a no-op.
Example 12-1. Using DirectBuffers
const ggi_directbuffer *dbuf; /* Acquire DirectBuffer before we use it. */ if (ggiResourceAcquire(dbuf->resource, GGI_ACTYPE_WRITE) != 0) { fail("Error acquiring DirectBuffer\n"); } /* Do framebuffer rendering here... */ /* Release DirectBuffer when done with it. */ ggiResourceRelease(dbuf->resource);
#include <ggi/ggi.h>
int ggiSetFlags
(ggi_visual_t vis, ggi_flags flags);
ggi_flags ggiGetFlags
(ggi_visual_t vis);
int ggiAddFlags
(ggi_visual_t vis, ggi_flags flags);
int ggiRemoveFlags
(ggi_visual_t vis, ggi_flags flags);
ggiSetFlags sets the specified flags (bitwise OR'd together) on a visual. This function is usually used to set async mode on a visual (see below).
ggiGetFlags obtains the flags currently in effect.
ggiAddFlags and ggiRemoveFlags are macros that set or unsets the specified flags.
ggiSetFlags, ggiAddFlags, and ggiRemoveFlags return 0 on success, <0 on failure.
ggiGetFlags returns the current flags.
Some visuals allow different modes with regard to when the screen is updated and the actual drawing takes place.
In synchronous mode when the drawing command returns, it is already or will be executed very shortly. So the visible effect is that everything is drawn immediately. (It is not guaranteed in the strict sense in that it is already drawn when the function call returns, but almost.) This is the default mode for all visuals.
The asynchronous mode does not guarantee that drawing commands are executed immediately, but is faster on many targets. If the visual does not support asynchronous mode, setting it has no effect.
To make sure that all pending graphics operations are actually done and the screen is updated, you need to call ggiFlush. (ggiFlush is not needed in synchronous mode.)
Warning |
The screen refresh the X target does every 1/20 s to fake a real SYNC mode can take about half the execution time of a program. So using synchronous mode can really slow things down. The synchronous mode is default because it is what most programmers expect. |
All operations are guaranteed to be performed in the order given in both modes. Reordering is not done.
So the recommendation for all graphics applications is to set the asynchronous mode. It will be far more efficient on some platforms and will never be worse.
Example 12-1. Setting up asynchronous mode
ggiAddFlags(vis, GGIFLAG_ASYNC); /* switches to asynchronous mode */ ggiFlush(vis); /* updates the screen */ ggiRemoveFlags(vis, GGIFLAG_ASYNC); /* switches to synchronous mode */
#include <ggi/ggi.h>
int ggiFlush
(ggi_visual_t vis);
int ggiFlushRegion
(ggi_visual_t vis,
int x, int y,
int w, int h);
ggiFlush waits for the visual to finish pending accelerator commands, and in some targets, it refreshes the framebuffer.
ggiFlushRegion performs the flush function only in the specified region if it would improve performance.
These functions are not needed if the visual is in synchronous mode.
This is a area-to-area-blit, all in the same visual. Copy the box described by x,y,w,h to the new location nx,ny. This automatically takes care of overlaps and optimizes for the given visual (e.g. uses HW-accel or intermediate buffers as appropriate).
ggiCopyBox will transfer an area between frames when the read frame is not the same as the write frame.
#include <ggi/ggi.h>
int ggiCrossBlit
(ggi_visual *src,
int sx, int sy,
int sw, int sh,
ggi_visual *dst,
int dx, int dy);
Blits a rectangular memory area from one visual to another. It handles colorspace-conversion. (Though it can be quite expensive, so take care.)
ggiCrossBlit will transfer an area from the source visual's read frame to the destination visual's write frame.
This function does not perform stretching.
#include <ggi/ggi.h>
int ggiSetOrigin
(ggi_visual_t vis, int x, int y);
int ggiGetOrigin
(ggi_visual_t vis, int *x, int *y);
ggiSetOrigin sets the top-left corner of the displayed area to (x, y).
When using a larger virtual area, you can pan the visible area over the virtual one to do scrolling. Some targets have extemely efficient means to do this (i.e. they do it in hardware).
Large virtual areas are also commonly used for buffering the display contents, but that is usually more easily accomplished by requesting a specific number of frames when setting a mode.
This call takes dot coordinates, not pixel coordinates as all other drawing primitives do. There is no difference in graphics modes because by definition dpp is 1x1, in text modes the application can do smooth scrolling.
ggiGetOrigin gets the current top-left corner of the displayed area into (x, y).
Due to rounding to the hardware's capabilities, the values retrieved by a subsequent ggiGetOrigin may not necessarily match those passed to ggiSetOrigin previously.
#include <ggi/ggi.h>
int ggiPutc
(ggi_visual_t vis,
int x, int y,
char c);
int ggiPuts
(ggi_visual_t vis,
int x, int y,
const char *str);
int ggiGetCharSize
(ggi_visual_t vis, int *width,int *height);
LibGGI provides a few functions to do basic character output. They are intended for debugging and simple GUI applications. They are simple on purpose: there is only one fixed-width font and its size cannot be changed. Only the standard ASCII character set (0x20 to 0x7f) is supported, with no internationalization features. All more complex character functions go beyond the scope of this base library.
ggiPutc puts a single character on a graphical visual.
ggiPuts puts multiple characters (of a C-style NULL-terminated string) at once. No special handling is applied to control characters like CR or LF. The associated glyph for control characters will be displayed. ggiPuts also only clips text at the clipping rectangle and does not wrap text.
ggiGetCharSize obtains the size of the character cell, in pixels. This function allows the application to position the text output accurately.
Note: The values returned by ggiGetCharSize is not the same as the values of dpp of the current ggi_mode, which is in dots. In graphics modes are 1x1 dpp by definition and use at least 8x8-pixel fonts. In text mode, the character cell is 1x1 pixels by definition and the dpp value is the actual size of the font.
#include <ggi/ggi.h>
gii_input_t ggiJoinInputs
(ggi_visual_t vis, gii_input_t inp);
gii_event_mask ggiEventPoll
(ggi_visual_t vis, gii_event_mask mask, struct timeval *t);
int ggiEventsQueued
(ggi_visual_t vis, gii_event_mask mask);
int ggiEventRead
(ggi_visual_t vis, gii_event *ev, gii_event_mask mask);
int ggiEventSend
(ggi_visual_t vis, gii_event *ev);
int ggiSetEventMask
(ggi_visual_t vis, gii_event_mask evm);
gii_event_mask ggiGetEventMask
(ggi_visual_t vis);
int giiAddEventMask
(ggi_visual_t vis, gii_event_mask mask);
int giiRemoveEventMask
(ggi_visual_t vis, gii_event_mask mask);
LibGGI provides input facilities through an auxiliary library, LibGII. Each LibGGI visual internally contains a gii_input_t input, and all LibGII functions are available to manipulate and process inputs. The LibGGI version of LibGII functions simply take a ggi_visual_t rather than gii_input_t. Events are LibGII types. All other semantics are the same; see the LibGII API Manual for details.
LibGGI visuals open the appropriate inputs already, including mouse and keyboard, or the inputs are 'intrinsic' to the visual, e.g. X mouse and keyboard events. Thus in the usual cases there is no need to open a LibGII gii_input_t directly (and that may in fact fail because an input device is already open). The LibGGI ggiEvent* functions should be used to do event handling in LibGGI.
ggiGetc gets a character from the keyboard, and blocks if there is no key immediately available.
ggiKbhit checks if a key has been hit on the keyboard. This does not consume the key. It is used for easy porting of old DOS applications.
Important: Do not poll like this: do while( ! ggiKbhit(vis) ); On a multitasking OS you would be wasting a lot of resources which could be available to other processes. If you want to wait for a key, use the ggiGetc call.