=head1 NAME Xgame - an X11 game/animation library, Version 1.0, 28 may 1999 The files in this package are Copyright (c) 1998, 1999 by B.W. van Schooten, all rights reserved. This software is distributed under the Perl Artistic License. The software comes with no warranty whatsoever. See file `artistic.license' for more details. =head1 DOCUMENTATION Xgame should include the following documentation, in man and pod format: =over 10 =item Xgame This file. Includes global description, installation instructions, changes, contact information, and programmer's manual. =item Xgamewin Basic graphics and I/O functions =item Xgamekey Keyboard symbol definitions =item Xgametmr Timer functions =item Xgamet Toolkit functions for game logic. Currently, this encompasses object movement and collision functions. =item Xgamezoom Separate zoom function =item r250 Functions of r250 random number generator =back =head1 DESCRIPTION Xgame is a small 'widget' or 'toolkit' that is supposed to make life easier for those who would like to try their hand at X programming and/or game/demo programming. It is meant to function under as many systems as possible, as a library which should be (almost) ready to plug in and use, and as an illustration of how to use Xlib or do computer animations. Note that it does NOT include Wimp stuff, like requesters, scrollbars and buttons. Xgame relies only on Xlib (the 'kernel' of the X window system) and the Xpm library (which is an unofficial but widespread standard library for handling XPM files). Xgame is (or should be) able to do the following things: =over 2 =item * open windows on a display of arbitrary depth, and automatically handle each window's redraw requests =item * draw pixels, lines and polygons, and print text =item * load and draw (optionally transparent) colour bitmaps using the XPM library =item * provide a separate buffer for drawing animated objects (like sprites), and a separate buffer for background graphics, with support for scrolling =item * have dirty rectangle handling built in, which can be used for updating the screen and clearing the sprite buffer =item * keep track of the state of the keyboard in keymap and lastkey variables, and keep track of the mouse position and buttons. =item * automatically re-scale a window's coordinate system on opening by supplying the desired scaled x and y size. =back Xgame has been tested on: Solaris (Sparc, SLC), FreeBSD (X86), NetBSD (X86, Acorn), Linux (X86), XFree86, Solaris X server, Exceed, X-Win32, Accelerated X, 1-bit, 4-bit, 8-bit, 16-bit and 24-bit display depth. It is unknown whether the display update causes glitches/flickering on bitplane video systems (since some of the existing documentation reports irritating glitches on some X servers). One would expect that video systems with packed-pixel memory organisation won't easily flicker, because any pixel can be changed with a single write, while bitplane systems will, because a pixel update takes at least as many writes as the screen depth, and maybe more if the X server decides to write data on a by-plane basis. I have never seen any flicker during screen updates, but unfortunately, I haven't been able to find any bitplane system to test this code on. If you report any irritating glitches, I would be interested to hear from you. =head1 HOW TO COMPILE, INSTALL, AND RUN This version consists of the following files: =over 4 =item xgame.c and .h Xgame main routines and data structures =item xgamet.c and .h toolkit for game logic: managing game objects and detecting collision =item xzoom.c and .h pixmap zoom function used by xgame.c =item keysyms.h contains all keysyms supported by Xgame =item keysyms.c contains table for remapping `alternative' keysyms to standard Xgame keysyms =item timer.c and .h a separate module with some handy timer functions =item stdargs.c and .h parses window settings from command line for example applications =item stdcolor.c some colour definitions used by x-plode.c =item vroot.h Vroot (Copyright (C) by Andreas Stolcke) is a standard program for handling virtual root windows. See source for full license. =item r250.c and .h The R250 pseudorandom generator, by S. Kirkpatrick, E. Stoll, W.L. Maier, and J.R. Van Zandt. This is a fast and improved replacement of the standard rand() function, and is used by several of the games. =back The package comes with some example demos: =over 4 =item cubes (cubes.c) 3D animated cubes =item wormdemo (worm.c, XPMs are in xpms-worm) 3D flying worm demo =item scroll (scroll.c, XPMs in xpms-scroll) An illustration of scrolling =item fastifs (fastifs.c) Animated IFS fractal =item bounce (bounce.c) Bouncing balls, an illustration of xgamet =back and some games: =over 4 =item x-plode (x-plode.c, XPMs in xpms-x-plode) Blow-em-up game (nearly finished) =item zooi (zooi.c) Zoop clone (only one level, no score) =item ureum (ureum.c) Superfast shoot 'em up (only one level, no score) =item dodge 'em (dodgem.c, XPMs in xpms-dodgem) An attempt at re-engineering of the original dodge 'em (only one level, no score) =item xcircuits (xcircuits.c, XPMs in xpms-xcircuits) Zenji clone (unfinished) =back For the time being, Xgame is not installed as a library, but is simply linked statically with the example programs. To compile the programs, type: xmkmf (convert the Imakefile to a Makefile) make (compile and link) The executables have names as given above. Some require the path to their XPM files to be known. Instead of using an environment variable, a run control file, or compile-time configuration, the path is supplied as the fourth command line parameter (the first three are window parameters). This way, you can install them easily by putting a small batch file in your bin directory which calls the executable and supplies the directory to the XPMs. The docs can be found in the subdirectory doc. These should include all man pages in pod and man format. Pod is a format used by Perl to create man pages easily. If you have Perl installed on your system, the man files can be generated from the pod files by running them through `pod2man'. =head1 ABOUT =head2 Origin Xgame originally started as an experiment to see how well suited the X window system is for action games. As a software developer, I found X attractive because it is proven technology and has a large user base which is now quickly becoming even larger, thanks to the increased efforts that have lately been put in free Unices. X originally has the reputation of being very slow and unsuited for flashy-looking games or demos. Now, several computer generations and rivalling Wimp systems later, it actually turns out to be quite efficient in comparison. Oddly, there haven't been many games for the X platform (though it appears this is changing!). Since I'm quite a fan of games, especially action games of the 'classic' type, I decided to try and write some myself, evolving a suitable library fulfilling my needs along the way. The first versions of the Xgame code, which were part of the game `xcircuits', date from around spring 1995. The library version, which emerged a little later, was used for neural network visualisation in my master's project (there are even some screen-grabs from Xgame windows in my master's thesis!). Back then, it only supported black&white windows, as that was all I had access to, and there was little support for buffering. However, it was only towards the end of 1997 that I started working on it seriously again. =head2 Glorified vectrex machines Contrary to some of the older machines I was used to (Amiga or XT with CGA), I recently found out just how much faster vector (line) graphics are on modern Intel-based boxes, as compared to (transparent) bitmap graphics. This is apparently because of the very much reduced throughput through the slow PC buses, and the relative ease of the required processing. There is no source bitmap or mask, and vector graphics are relatively sparse, so there's very little to be put through the bus. In typical PC screen modes, pixels can simply be drawn by writing a single byte or word, so no bit slicing routines are needed. Gradient calculation is fast, because the CPU can do it in its code cache. Also, one cannot optimise drawing of typical lines by writing multiple memory-contiguous pixels at a time, so it doesn't pay decreasing the screen depth, like it does with bitmaps, so it doesn't really matter whether your vectors are in 8 or 24 bits colour. So, at last, the Vectrex has returned, and this time it has 16 million colours instead of just two! I've added support for faster vector graphics in the new version (you can turn off the background pixmap and use the new XgameDrawVecObj function), though the buffering is still done by copying bitmaps (which is slow) in order to prevent flickering. The new cubes demo illustrates this; compare it in speed to the worm demo. =head1 PROGRAMMER'S MANUAL This section will explain the general ideas behind the structure of Xgame. For an explanation of the individual functions, see the corresponding man pages as listed at the beginning of this document. Various examples of most features of Xgame can be found in the example programs. The include structure of the different files in a typical Xgame application is shown below: a vertical line indicates that the program at the top includes the program at the bottom. An application should only need to use what is defined (rather than #included) in the included headerfiles. Note, though, that keysyms.h is an exception: it is automatically included by xgame.h, though the definitions in there may be used directly. +-------------------------------------------+ | APPLICATION + +----------+------+--------------+--------+-+ ....................|......|..............|........|...... . +===============+======+========+ +===+===+ +==+=====+ . | xgame.h | | |timer.h| |xgamet.h| . | xgame.c | | |timer.c| |xgamet.c| . +=+=======+=====+======+======+=+ +=====+=+ +========+ . | | | | | | . +-----+-+ +---+---+ | +====+====+ | | . |vroot.h| |xzoom.h| | |keysyms.h| | | . +---+---+ |xzoom.c| | |keysyms.c| | | . . | +---+---+ | +=+=======+ | | . ....|.........|.... | ..|.........|.........|.......XGAME. +---+---------+-----+---+-+ +-----+-----+ +-+----------+ | | || || | | | | | | =head2 Drawing fluid animations I The library encourages the programmer to make extensive use of temporary buffers and buffer copying to prevent screen flickering during animation and to make animating sprites over a static or scrolling background easy. The idea is to use the following basic procedures to display one frame of animation: (I) Drawing on the screen without flicker: 1. Draw whatever you want to display on a separate buffer. 2. Copy the buffer to the screen. If you would have drawn directly to the screen, you might be caught by the video beam in the act of drawing, and your unfinished drawing would be displayed on screen, which may look terribly flickery/glitchy, depending on how you have chosen to draw it. It is sometimes possible but usually not practical to choose a drawing method that causes only little flicker when done directly on the screen. Otherwise, a buffering method, like (I), is needed. (II) Drawing animated sprites on any background: 1. (If necessary) make any changes to the background. 2. Copy the background to your drawing area. 3. Draw the sprites on the drawing area. Note that this drawing method would cause flickering sprites when the drawing area would have been the screen, since all old sprites are cleared away completely before any new sprites are drawn. So, this method requires method (I) as well. I It's obviously a lot of waste copying two complete screenfuls just to display a few small sprites. It is much more efficient to copy only what has changed since the last copy. This requires the program to keep track of any changes it has made. Xgame does this automatically: any drawing function determines the smallest rectangular area that contains the object drawn, and puts this area in a list (a XgameDirtyRectList struct), using XgameAddRect or XgameAddBGRects. Copying now amounts to going through this list, copying only the areas that are listed in it. This method is called 'dirty rectangle' updating. Dirty rectangle notification can be turned off by setting the nonotify switch in the appropriate DirtyRectList structure. All operations that would have added entries to that list will no longer do that. It is then the responsibility of the programmer to make sure the proper areas will be copied. This can be done using the functions XgameClearArea, XgameClearSprite, XgameShowArea and XgameShowSprite. This feature may be useful for special cases where the Xgame dirty rectangle system is not desirable, for example, partial-screen scrolling (like in Defender), status displays which are not drawn on the background but which should be retained, and specific graphics which should be updated with a higher or lower frame rate than others. I Xgame has 2 bitmap buffers, called the background and the buffer. The background corresponds to the background in (II). The buffer corresponds to the buffer in (I) and the drawing area in (II). The dirty rectangle lists used for the copying as found in resp. (I) and (II) are resp. bufrectlist and bgrectlist in the XgameWindow struct. Either copy operation can be done by calling XgameFlushRectList, supplying the desired rectlist as the second argument. The background can also be turned off, in which case all functions that operate on the background are ignored, and the program acts as if the background were a single colour. The colour of the background is initially the first colour in the window's colour list, and can be changed afterwards using XgameChangeBGCol. The drawtobuffer switch in struct XgameWindow can be used to control the destination of the drawing routines. If it is 0, everything is drawn only on the background, otherwise everything is drawn only on the buffer. ------------+ / / / / Screen / / +------------ ^ XgameFlushRectList(windowhandle, bufrectlist) ----|-------+ / | / / / Buffer / / +------------ ^ XgameFlushRectList(windowhandle, bgrectlist) ----|-------+ / | / / / Background / / +------------ I In Xgame, it is possible to create a background that is larger than the screen, and determine which part of the background is shown on screen using the panning function XgameChangeBgOfs. Scrolling can be seen as one of the possible ways to fill in step 1 of algorithm (II). There are several ways in which you can use the panning mechanism for scrolling. The simplest (but also the most memory-expensive) is to make the background as big as the whole playing area within which to scroll. Now, you can choose which part of the background to show on screen by setting the offset of the topleft corner of the screen using XgameChangeBGOfs (see picture below). X offset | V +---------------------+ | background| Y offset -> | +------+ | | |screen| | | | | | | +------+ | | | | | +---------------------+ Unfortunately, this method costs a *lot* of memory, unless the background area is small. An alternative is to make the background about the same size as the screen, and draw bits of background on demand, as required by the amount of pixels you want to scroll in a certain direction. For example, if you want to scroll one pixel to the left, you draw one column of pixels on that part of the background that is just past the left edge of the screen, and then pan the screen one pixel to the left. In order to enable this method to continue scrolling in one direction indefinitely, the background wraps around at its edges (see picture below). . . . . . . | | | ...---+----------+----------+---... |background| | | | | | +----+---+ | | | screen | | ...---+-----+----+---+------+---... | | | | | | +----+---+ | | | | | | | ...---+----------+----------+---... | | | . . . . . . However, it is often hard or inefficient to draw columns or rows of any given number of pixels, as required by the method explained above, because it usually does not correspond to the form of the data you want to draw on the background. Usually, a background is defined as a grid of tiles, each tile being a pixmap of a fixed size (for example, 16x16 or 32x32). In our previous example of scrolling left one pixel, this would require drawing only one thin vertical slice of pixels out of each tile found in the column just past the leftmost part of the screen. This would require a lot of small drawing operations, and possibly a lot of administrative overhead. A way around this problem is to make the background slightly bigger than the screen, and use the now-available off-screen margins as a staging-area to prepare your data just before it is needed. An implementation of this method in the case of a tile-based map is found in the example program scroll.c. Note that XgameChangeBGOfs actually marks the whole buffer as dirty, which means that scrolling requires the whole background to be copied to the buffer (and naturally, the whole buffer to be copied to the screen). In future versions, I will try to minimize the former copy operation to only a few margins (this would cause a speed increase of nearly a factor 2), but I am still thinking of the most elegant way to implement this. In case you are drawing a lot of sprites on the buffer anyway, this optimisation will make less difference. =head2 Timing and frame skipping I In my own opinion, drawing 60 or 70 frames per second looks best by far, even though many modern games tend to run at lower frame rates, like 35 fps or less. Usually, these games actually run at 70 fps, but as soon as they find out their frames start running behind on schedule, they start skipping the actual drawing routines while the internal position updates still continue (frame skipping). Because the actual drawing is often by far the most CPU-expensive part of updating a frame, the program is able to get back on schedule by means of frame skipping. This way, the game speed remains constant, while the actual frame rate adapts itself automatically to the speed of the machine. Note that frame skipping is applicable if and only if everything is redrawn every frame, otherwise a skip may actually affect what will be displayed on future frames. So, operations done to the drawing area in method (II) can always be skipped, but things drawn incrementally, for example, the occasional updating of a relatively stationary background, cannot be skipped. I The package provides a separate timer routine (checktimeelapsed in timer.c) which is meant just for this purpose. You can set up a wait loop in which you call checktimeelapsed, supplying as parameter the time (in microseconds) that a frame is supposed to take. It returns a 0 as long as there is still time left to kill. In this case, stay in the wait loop. After enough time has elapsed, it returns a 1, and decrements its internal timer. Now, you can exit the loop, draw your frame, and return to the wait loop again. If you start running behind with drawing frames, checktimeelapsed behaves the same as usual, but will return 2 instead of 1, indicating that you should start frame skipping or saving time in other ways until you are back on schedule again. If you start lagging behind *very* much (or don't call the function for awhile) it will reset its internal timer so the frame skipping will not get out of hand. I Spending so much time in a wait loop is obviously a waste of CPU, and not very friendly to other processes either. In fact, the OS will start scheduling out your process pre-emptively when it takes up too much CPU, sometimes in a very coarse time grain (about once a second), causing jerky animation. It would be preferable if you could tell the OS when you'd like to be scheduled out. This is what checktimeelapsed's sleepallowed flag is for. If you set the flag to a value other than 0, it will call usleep() whenever it decides it is safe to do so. I The XgameWindow structure contains the skipdraw switch, which, when set to any other value than 0, causes all draw functions to skip completely. Note that the dirty rectangle copy function XgameFlushRect is NOT affected. If you want to skip a frame, you can simply set this switch at the beginning of your sprite draws, automatically skipping them without needing any conditional statements in your code. Note that you still need to skip the appropriate dirty rectangle copies, and see if you may or may not skip anything drawn on the background. =head2 Keeping in touch with events I There are a number of events you'll want to keep track of, in particular keyboard events, mouse events, and expose events. This is done by XgameHandleEvents. The function first waits until all the draw requests since the last call are actually drawn by the X server, and then processes any input events that occurred since. The variables keymap, lastkeyp, lastkeyr, and lastkeyevent in the XgameWindow struct can be used to read the keyboard state. The variables mousex, mousey, and mousebtn can be used to read the mouse state. I Expose events are redraw requests, which are generated as soon as some portion of your window was garbled in some way and needs to be refreshed. In Xgame, the areas that need to be redrawn are stored in the buffer's dirty rectangle list, and will be redrawn only if that list is flushed. This means that you have to call XgameHandleEvents and flush the list periodically to keep your window neat. I Some special things happen here: when your Xgame window obtains or loses the focus, the keymap is cleared to make sure it doesn't get inconsistent. When the mouse pointer leaves the window, the mouse variables are no longer being updated until it reenters, except when one of the buttons is being held down. =head2 Screen space usage Two features have been added, which allow applications to have more control over the usage of screen space. I The automatic scaling feature enables the application to rescale each window, so that it can be run comfortably on screen resolutions other than the one it was designed for, without requiring extra effort from the programmer. Scaling is done very easily: just supply the size you want the window to be as XgameOpenWindow's scaledxsize and scaledysize parameters, while keeping the xsize and ysize parameters as usual. Everything (pixmaps, fonts, mouse position, drawing coordinates) is scaled transparently. However, some things are not quite perfect: (1) Since it's not very well possible to select a font with a non-square aspect ratio, fonts always have square aspect, with sizes chosen according to the smallest of the x and y scale factors. Even though fonts are not scaled perfectly this way, they are guaranteed to stay within the area they normally would. (2) The scaled pixmaps are not anti-aliased, which means non-integer scaling factors may result in ugly pixmaps. The pixmaps look best at scaling factors such as 0.5, 0.75, 1.25, 1.5, or 2.0. Remember that, when scaling down, any text contained in pixmaps may become unreadable. If the text is important, it is better to use fonts instead. (3) Vector graphics do not necessarily have exactly the same shape in different scale sizes. In particular, lines thicker than 1 pixel have different effective thicknesses in different scale sizes. I Especially with video games and demos, it is sometimes desirable to be able to take over the whole screen, and not have any window borders or other windows that spoil the effect. In Xgame, there are two ways to do this. (1) Using the root window instead of a regular window. This can be done by opening a window with the flag XgameWinSetRoot set. The drawing area, as given by scaledxsize and scaledysize, may be smaller or bigger than the root window, but is always centered. Any borders around the drawing area are drawn using the standard root window tile. There are some drawbacks to using the root: the root is typically a shared resource. This means that: =over 4 =item (a) Other programs may be drawing on the root at the same time. This should however not cause any problems except for messy graphics. It's also possible to run multiple instances of an Xgame program on the root. =item (b) It is generally not possible to read the mouse buttons, because, for any window, the button events are process-exclusive, and the root's button events are usually taken by the window manager. =item (c) There are typically some things the window manager always draws on top of the root, like icons. =back (2) Opening a screen-sized window, and placing the window borders just outside the screen borders. This can be done by opening a window with the flag XgameWinSetFullScreen set. Like (1), the drawing area is centered, but the colour used for borders is the first colour in the colour list. This method does not have the disadvantages of using the root window, but it is an `aggressive' way of using screen resources, and may be considered offensive by some users. Remember that it is not possible to move or close a full-screen window in the regular manner, and it may be possible the window can't be closed at all except by exiting the program. So, it should be made very clear how the program can be exited as soon as it is started, or the full-screen mode should be an option which is off by default. =head2 The toolkit Xgamet New in this version is the toolkit Xgamet, defined in `xgamet.h'. It is fully separate from the other modules, and uses a minimum of operating system functions. Xgamet defines some functions for managing object lists, namely initialisation and freeing, and copying the contents of one list to another. The other functions consist of allocation of new objects in a list, and checking collision of one list with another, or with itself. I wrote this toolkit because I found that there are some specific routines most of my games require, namely routines for updating lists of sprites, and for checking collision within or between these lists. Code for updating lists is usually written easily enough, but I found that it takes more energy to write bug-free collision routines, even though I have written such routines many times before. So, I decided to write a generic one once and for all. I To use the toolkit, you need to create some object lists first, using the function XgametCreateObjList. The object data needed for checking collision is stored in an array within the list structure, storing only a necessary minimum of collision-related information for each object. It is also possible to have the toolkit aid in maintaining additional user data for each object. This data will have to be stored in some other array, consisting of elements of fixed size. If you want different types of object with varying user data sizes, you can do this by creating multiple object lists, one for each type of object. I Just after a list is created, it is empty. The slots in the lists (which are simply the members of the object data and user data arrays) may be allocated and initialised using XgametAllocObj. This function also returns the object's array index, which enables you to modify the object's data further by writing directly into the arrays. It is even guaranteed that this index stays the same as long as the object remains allocated. Objects may be freed by simply setting their exists field to zero. I Most object data fields do not seem to make much sense until you start using XgametCollideObjLists. This function enables you to check collision between objects in a list, or cross-check collision between objects in two different lists. Two objects collide when their bounding boxes overlap. The bounding box of an object is given by the object's position, added to the object's bounding box coordinates. The bounding box is given by an index into a separate array of bounding boxes, supplied separately to the collide function. This is because often you will only need a fixed and limited set of bounding boxes, for example, one for each (general) sprite shape, while you might have many objects which have the same shape. Instead of having to copy the shape's bounding box coordinates into the object data, you can now keep all coordinates in a fixed list of bounding boxes, and simply refer to a bounding box by setting its array index in the object. Collision is notified by having the XgametCollideObjLists function call a function of your choice: this is what the XgametNotifyHit function pointer array is for. The parameters supplied to your function are references to the two objects that collided. Your function handles the collision, and the collision check continues until all objects have been cross-checked. However, usually you do not want to have your function called for every object that collided, but only for some specific subset of objects, and perhaps you would want to have different functions for different kinds of objects. This is what the id and mask fields are for. Each bit in the id and mask corresponds to one function in the function pointer array. A function is called only when its bit in the I of an object in the I list matches with the same bit in the I of an object of the I list, i.e. ( (1 << array_index_of_notify_function) & object_in_list_1->id & object_in_list_2->mask ) != 0 It is also possible to have collisions checked in a specific order. This order can be specified by setting the priority field for each object. Objects in the first list which have high priority will be checked and notified first. Newly-allocated objects are not immediately taken into account by XgametCollideObjLists when they are created. In fact, they will be ignored until they are explicitly `synced' by XgametSyncObjList. This enables better control of collision, especially in case you create objects inside the collision notification routines. Usually, you don't want newly-created objects to become collidable immediately, but rather, have them stay idle until the next frame. This can be achieved by allocating them as normal, but only activating them by calling XgametSyncObjList once at the beginning of each frame. =head2 Practical programming using Xgamet Besides detecting collision, XgametCollideObjLists can also be used for the moving and displaying of objects. To do this, define a playfield which has as its bounding box the whole screen, and causes update/draw functions to be called when it collides with an object. It may also be useful to define walls and such as separate objects, which can be used to bounce or destroy game objects. This way, the main loop of your program can be reduced to a few calls to XgametSyncObjList and XgametCollideObjLists. These will do all the stuff for you: move and draw the sprites, collide them, and possibly, destroy or bounce off-screen objects. I found it easiest to structure an Xgamet program as follows: Declaration of one or more XgametObjLists and XgametBoxes Definition and declaration of user data arrays Declaration of default values for user data Some #defines defining meaning of the bits in collision ID and Mask notify functions function pointer array XgametNofityHit[], listing the notify functions MAIN () { Create object lists Level loop { Clear object lists Allocate playfield area and walls Allocate game objects Game loop { /* Basic I/O and timing stuff, like * checktimeelapsed, XgameHandleEvents, * XgameFlushRectList */ ... /* move, draw,, collide objects */ XgametSyncObjList(...) XgametCollideObjLists(...) } } Free object lists } By putting the notify functions strategically within the Xgamet data definitions, they have access to all definitions they need, yet you don't need to create function prototypes for them. If you like to put the notify functions somewhere else, you should create prototypes somewhere above the declaration of XgametNotifyHit[] array. In that case, the order of the other Xgamet declarations no longer matters much. =head1 BUGS Also new in version 1.0 is this list of design limitations, i.e. limitations that may require interface changes to remove. Though I consider some limitations as flaws, some are not really flaws, but have been decided upon deliberately. For other problems that are planned to be solved eventually, see the next section. =over 2 =item * Only two buffers, selected by the drawtobuffer flag, and no direct write to the screen. Since the usual drawing algorithms require one or two buffers, rather than zero or more than two, I consider this reasonable. However, I did discover that writing directly to screen may be preferable in some cases. For very fast (vector) drawings, and for very simple screen updates, having buffered write is a little too heavyweight. =item * Scaling is not fully transparent to the programmer. Only pixmaps and rectangular areas can be drawn scaled without getting side effects. In the case of text and vector graphics, the actual shape drawn may effectively vary for different scale sizes. Actually, I discovered that this is a problem only afterwards. I was trying to wrap up Zooi when I discovered that it was impossible to have the different waveforms merge seamlessly in all different scale sizes. This problem is not easy to solve: control over scaling is limited. In case you want to merge text or vector graphics seamlessly, it should at least be possible to obtain the sizes of the objects drawn. Currently, only the horizontal size of text can be obtained. =item * Colour system is palette-based. This means it is impractical to have a lot of colours. There is no limitation to the number of colours in XPMs though. I consider it reasonable not to have more than about 100 colours for use with the single-colour drawing functions. =item * Names are too long? I am using a systematic naming scheme, but the names turn out rather long. It is still acceptable though, because a typical program needs only few function calls. =item * Timer functions are a bit of a hack, and should be neatened. The resetthreshold and usleepmargin should be supplied as parameters to checktimeelapsed. The function should also allow larger time spans, like several seconds. The function names could be made to conform the rest of the naming scheme. =item * Keyboard input not appropriate for typing text. Since no keyboard buffer is kept and only the last pressed key can be read, frequent calls to XgameHandleEvents are needed to avoid losing keys. This hogs the system, even if the program is essentially idling. This may be solved by having a keyboard buffer with appropriate readout functions, and/or by enabling XgameHandleEvents to block for events. =item * XgametCollideObjLists currently sorts the second object list by position. This may be a dubious practice: 1. Is it really faster than not sorting? 2. Order of object updates may vary, which causes glitches when sprites overlap. I may change this in the next version. =item * Window cannot be reconfigured once opened. New fonts, colours, and pixmaps cannot be loaded without having to close and re-open a window. This may be solved by having separate OpenWindow and ConfigureWindow functions. =back =head1 FUTURE PLANS =over 2 =item * Ability to install the program as a shared library, and install man pages automatically =item * better window feature handling. Currently, the window is called 'Untitled' and can be resized even though the program doesn't adapt when you do. Also, it cannot be re-parented. =item * speed up the scrolling algorithm. This should be possible with only few or no changes in the interface. =item * restoring of the function XgameSpriteFromBitmapFile, in case I can get hold of the missing Xlib function (see source). =item * make it available as a package/library/widget in some distributions. =item * Mouse cursor shape control. At the very least, one should be able to turn the mouse cursor off. =item * Neaten some of the example programs. These are relatively dirty, and with few comments. =back More ambitious plans include: =over 2 =item * adding a sound module, which can play samples and music (like MOD or MIDI) =item * support for directly-manipulable images (XImages) =item * adding higher-level modules like a 3D-isometric engine and, a (simple) Wimp module, and some high-level text functions. =back =head1 CHANGES =head2 Changes V0.95 -> V1.0 Bugfixes: XgameDrawLine: clipping dirty rectangles noticeably speeds up offscreen lines in some cases. Neater and more general routines should be made to clip the dirty rectangles. (Version 0.95 Patchlevel 1): Mouse offset bug fixed: in full-screen modes, the offset was not adapted properly. Additions: Proper man pages. The header files have been changed to enable automatic generation of man pages. Comments were neatened, and placed after the definitions they refer to. The main docfile has been converted to POD, so it can easily be converted to man too. A new module containing object collision functions. It is defined in xgamet.h and xgamet.h. (Version 0.95 Patchlevel 2): New function XgameDrawSpriteArea: draws a given sub-area of a sprite. This enables putting multiple sprites in a single XPM file. Changes to example programs: X-plode has been modified to use Xgamet. Collision with skulls now works better. New games: zooi, ureum, dodgem (these are still in prototype stage). New demos: bounce. =head2 Changes V0.9 -> V0.95 Bugfixes: The dirty rectanges generated for vector objects were too large and even partially on-screen. Now, off-screen objects are skipped completely. Keymap should be signed char, not char. XComposeStatus passed to XLookupString should be NULL. The keymap is now cleared on a changefocus event, so it won't get badly inconsistent. Interface changes: Deleted XgameClearSprite and ShowSprite. These two functions did not seem useful enough to warrant the amount of duplicated code they were starting to generate. The keyboard reading routines have been much improved. It is now possible to use special keys, like cursor keys, modifiers (like the shift key) and function keys. Modifiers used to make the keymap inconsistent. Now, both the shifted and unshifted keysyms are always activated in the keymap. Instead of strings, the lastkey* variables now contain the XgameKeySym values. There are now separate lastkey* variables for the unshifted, shifted, and actual key symbols corresponding to the last key events. The new files keysyms.c and .h now contain Xgame's keysym definitions and X-to-Xgame remappings. XgameDrawText has been improved: it now returns the position of the first pixel right of the printed text. This allows printing a line of text in separate substrings, and placing a cursor at the end of a line. Also, it now calculates the exact metrics of the printed text instead of safe margins. Mouse support. It is now possible to read the mouse buttons and position. Off-screen positions are not notified, except when a button is being held (this appears to be a property of the X server). Full-screen support: root window mode (for convenience, vroot.h, Copyright by Andreas Stolcke, has been included in the distribution). Some window managers won't give focus to root. This is worked around by setting the focus on a mouse enter event. It is not possible to read the mouse buttons when using the root. Off-screen window border mode (this does not have the disadvantages of root mode). Automatic scaling support: there are two extra parameters to XgameOpenWindow, where you can supply the scaled x and y size. Everything (pixmaps, fonts, mouse position, drawing coordinates) is scaled transparently to fit the given x and y size. However, it's not (very well) possible to select a font with a non-square aspect ratio. When scaling changes the aspect ratio, the font is now chosen according to minimum of the x and y scale factor. The new file xzoom.c contains a stand-alone pixmap zooming function. Changes to other programs: Minor maintenance of all programs to fit the new interface. Programs now have extra parameters to select scaling and full-screen support. X-plode has one more enemy type, and 13 more levels. New animated-fractal demo fastifs.c. =head2 Changes V0.8 -> V0.9 Bugfixes: Better error handling and recovery, especially for sprites and fonts. Two of the shorthand text alignments were wrongly defined. Margins used for fonts were wrong: sometimes, the dirty rect would not quite enclose the text drawn. No safe margin of topleft of the rect around a line in some cases. Interface changes: XgameOpenWindow has some extra parameters. These allow for multiple fonts, custom pen colours, autorepeat on/off, colour choice for B&W screens, background on/off. Sprites loaded without hotspot now have their default center at topleft (this is what is used most often). The lists of names you supply should now be terminated by an empty string. struct XgameDirtyRectList has a new public member, nonotify, which disables all dirty rect notification for that list (except those generated by expose events). Turning off the background causes all functions that would operate on it to be ignored, and speeds up flushing the bgrectlist. The background is assumed to be a single colour (the first colour in the window's colour list), which can be changed by a new function XgameChangeBGCol. A couple of superfluous functions (XgameDrawTile and XgameDrawText) have been deleted. XgameDrawFText is renamed to XgameDrawText. XgameDrawText has changed parameters; there is an extra parameter fontnr. lastkey also registered keyreleases, while usually one would only need, and in fact expect, keypresses here. Now, the registering of the last key has been split into lastkeyevent (which has exactly the same behaviour as lastkey had previously). There are now two extra char members in the window struct: lastkeyp (last keypress) lastkeyr (last keyrelease). The macro symbols have been renamed to X standard names, starting with Xgame... There is a new function XgameDrawVecObj, which allows for quick&easy (2-D) vector drawing. Changes in other programs: There is a new cubes demo, illustrating the use of vector graphics. Based on a patch by Bill Adams (bill@evil.nwpacifica.net) X-plode now has a high score, level skip, and a game-over screen. Bugfix: the player was immune to rockets! No more... You now get 25 points for rockets and remaining bombs. =head2 Changes V0.7 -> V0.8 I've put most of my efforts in support for scrolling. It works now, but it's still a bit slow, though I already have some ideas on how to speed it up by a factor of 1.5-2. The parameter order of some functions has changed to improve the interface consistency. There is a new text draw function XgameDrawFText (draw flushed text), a replacement of XgameDrawCText (draw centered text) which is more flexible. X-Plode is nearer to completion, and a new game Xcircuits has been added (it's a port of a very old game I never finished. Xgame actually evolved from the graphics routines found in the original version). =head1 AUTHOR Xgame was created and is currently being maintained by B.W. van Schooten. The current Xgame homepage can be found on http://www.geocities.com/TimesSquare/Alley/3583/Xindex.html http://wwwhome.cs.utwente.nl/~schooten/software/Xindex.html If you have any questions, suggestions or bugfixes, you can contact me at vicman@dds.nl And, last but not least, thanks to the following people for sending patches and bug reports: =over 4 =item Bill Adams Patch adding highscores & skiplevel in x-plode =item Aaron Digulla Patch adding extended keys =item Dante Lorenzo Idea and Bugfix for Xgame0.95 patch 2 (which was the patch adding XgameDrawSpriteArea) =back