1999-12-17 00:11:37 +01:00
|
|
|
#include "grx11.h"
|
|
|
|
|
2000-03-28 18:40:51 +02:00
|
|
|
#define TEST
|
1999-12-17 00:11:37 +01:00
|
|
|
|
|
|
|
#ifdef TEST
|
|
|
|
#include "grfont.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <X11/Xlib.h>
|
|
|
|
#include <X11/Xutil.h>
|
|
|
|
#include <X11/cursorfont.h>
|
|
|
|
#include <X11/keysym.h>
|
|
|
|
|
|
|
|
static void Panic( const char* message )
|
|
|
|
{
|
|
|
|
fprintf( stderr, "%s", message );
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
typedef struct Translator
|
|
|
|
{
|
|
|
|
KeySym xkey;
|
|
|
|
grKey grkey;
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
} Translator;
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
static
|
|
|
|
Translator key_translators[] =
|
|
|
|
{
|
|
|
|
{ XK_BackSpace, grKeyBackSpace },
|
|
|
|
{ XK_Tab, grKeyTab },
|
|
|
|
{ XK_Return, grKeyReturn },
|
|
|
|
{ XK_Escape, grKeyEsc },
|
|
|
|
{ XK_Home, grKeyHome },
|
|
|
|
{ XK_Left, grKeyLeft },
|
|
|
|
{ XK_Up, grKeyUp },
|
|
|
|
{ XK_Right, grKeyRight },
|
|
|
|
{ XK_Down, grKeyDown },
|
|
|
|
{ XK_Page_Up, grKeyPageUp },
|
|
|
|
{ XK_Page_Down, grKeyPageDown },
|
|
|
|
{ XK_End, grKeyEnd },
|
|
|
|
{ XK_Begin, grKeyHome },
|
|
|
|
{ XK_F1, grKeyF1 },
|
|
|
|
{ XK_F2, grKeyF2 },
|
|
|
|
{ XK_F3, grKeyF3 },
|
|
|
|
{ XK_F4, grKeyF4 },
|
|
|
|
{ XK_F5, grKeyF5 },
|
|
|
|
{ XK_F6, grKeyF6 },
|
|
|
|
{ XK_F7, grKeyF7 },
|
|
|
|
{ XK_F8, grKeyF8 },
|
|
|
|
{ XK_F9, grKeyF9 },
|
|
|
|
{ XK_F10, grKeyF10 },
|
|
|
|
{ XK_F11, grKeyF11 },
|
|
|
|
{ XK_F12, grKeyF12 }
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef TEST
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
#define grAlloc malloc
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
static Display* display;
|
|
|
|
static char* displayname = "";
|
|
|
|
|
|
|
|
static Cursor idle;
|
|
|
|
static Cursor busy;
|
|
|
|
|
|
|
|
#define MAX_PIXEL_MODES 32
|
|
|
|
|
|
|
|
typedef XPixmapFormatValues XDepth;
|
|
|
|
|
|
|
|
static int num_pixel_modes = 0;
|
2000-05-17 01:44:38 +02:00
|
|
|
static grPixelMode pixel_modes[ MAX_PIXEL_MODES ];
|
1999-12-17 00:11:37 +01:00
|
|
|
static XDepth pixel_depth[ MAX_PIXEL_MODES ];
|
|
|
|
|
|
|
|
typedef struct grXSurface_
|
|
|
|
{
|
|
|
|
grSurface root;
|
|
|
|
grBitmap image;
|
|
|
|
|
|
|
|
Window win;
|
|
|
|
Visual* visual;
|
|
|
|
Colormap colormap;
|
|
|
|
int depth;
|
2000-03-28 18:40:51 +02:00
|
|
|
XDepth* xdepth;
|
1999-12-17 00:11:37 +01:00
|
|
|
Bool gray;
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
GC gc;
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
XColor color[256]; /* gray levels palette for 8-bit modes */
|
|
|
|
XImage* ximage;
|
|
|
|
|
|
|
|
int win_org_x;
|
|
|
|
int win_org_y;
|
|
|
|
int win_width;
|
|
|
|
int win_height;
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
int image_width;
|
|
|
|
int image_height;
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
} grXSurface;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* close a given window */
|
|
|
|
static
|
|
|
|
void done_surface( grXSurface* surface )
|
|
|
|
{
|
|
|
|
XUnmapWindow( display, surface->win );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* close the device, i.e. the display connection */
|
|
|
|
static
|
|
|
|
void done_device( void )
|
|
|
|
{
|
|
|
|
XCloseDisplay( display );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static
|
|
|
|
void add_pixel_mode( grPixelMode pixel_mode,
|
|
|
|
XDepth* depth )
|
|
|
|
{
|
|
|
|
if ( num_pixel_modes >= MAX_PIXEL_MODES )
|
|
|
|
Panic( "X11.Too many pixel modes\n" );
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
pixel_modes[ num_pixel_modes ] = pixel_mode;
|
|
|
|
pixel_depth[ num_pixel_modes ] = *depth;
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
num_pixel_modes++;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static
|
|
|
|
int init_device( void )
|
|
|
|
{
|
|
|
|
XDepth dummy;
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
XrmInitialize();
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
display = XOpenDisplay( displayname );
|
|
|
|
if (!display)
|
|
|
|
{
|
|
|
|
return -1;
|
|
|
|
/* Panic( "Gr:error: cannot open X11 display\n" ); */
|
|
|
|
}
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
idle = XCreateFontCursor( display, XC_left_ptr );
|
|
|
|
busy = XCreateFontCursor( display, XC_watch );
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
num_pixel_modes = 0;
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
/* always enable the 8-bit gray levels pixel mode */
|
|
|
|
/* even if its display is emulated through a constrained palette */
|
|
|
|
/* or another color mode */
|
|
|
|
dummy.depth = 8;
|
|
|
|
dummy.bits_per_pixel = 8;
|
|
|
|
dummy.scanline_pad = 8;
|
|
|
|
add_pixel_mode( gr_pixel_mode_gray, &dummy );
|
|
|
|
|
|
|
|
{
|
|
|
|
int count;
|
|
|
|
XDepth* format;
|
|
|
|
XDepth* formats;
|
|
|
|
XVisualInfo template;
|
|
|
|
|
|
|
|
formats = XListPixmapFormats( display, &count );
|
|
|
|
format = formats;
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
#ifdef TEST
|
|
|
|
printf( "available pixmap formats\n" );
|
|
|
|
printf( "depth pixbits scanpad\n" );
|
|
|
|
#endif
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
while ( count-- > 0 )
|
|
|
|
{
|
2000-05-17 01:44:38 +02:00
|
|
|
#ifdef TEST
|
1999-12-17 00:11:37 +01:00
|
|
|
printf( " %3d %3d %3d\n",
|
|
|
|
format->depth,
|
|
|
|
format->bits_per_pixel,
|
|
|
|
format->scanline_pad );
|
|
|
|
#endif
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
if ( format->depth == 1 )
|
|
|
|
/* usually, this should be the first format */
|
|
|
|
add_pixel_mode( gr_pixel_mode_mono, format );
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
else if ( format->depth == 8 )
|
|
|
|
add_pixel_mode( gr_pixel_mode_pal8, format );
|
|
|
|
|
2000-05-17 01:44:38 +02:00
|
|
|
/* note, the 32-bit modes return a depth of 24, and 32 bits per pixel */
|
1999-12-17 00:11:37 +01:00
|
|
|
else if ( format->depth == 24 )
|
|
|
|
{
|
2000-05-17 01:44:38 +02:00
|
|
|
#ifdef TEST
|
2000-03-28 18:40:51 +02:00
|
|
|
{
|
|
|
|
int count2;
|
|
|
|
XVisualInfo* visuals;
|
|
|
|
XVisualInfo* visual;
|
|
|
|
const char* string = "unknown";
|
2000-05-17 01:44:38 +02:00
|
|
|
|
2000-03-28 18:40:51 +02:00
|
|
|
template.depth = format->depth;
|
|
|
|
visuals = XGetVisualInfo( display,
|
|
|
|
VisualDepthMask,
|
|
|
|
&template,
|
|
|
|
&count2 );
|
|
|
|
visual = visuals;
|
2000-05-17 01:44:38 +02:00
|
|
|
|
2000-03-28 18:40:51 +02:00
|
|
|
switch (visual->class)
|
|
|
|
{
|
|
|
|
case TrueColor: string = "TrueColor"; break;
|
|
|
|
case DirectColor: string = "DirectColor"; break;
|
|
|
|
case PseudoColor: string = "PseudoColor"; break;
|
|
|
|
case StaticGray : string = "StaticGray"; break;
|
|
|
|
case StaticColor: string = "StaticColor"; break;
|
|
|
|
case GrayScale: string = "GrayScale"; break;
|
|
|
|
}
|
|
|
|
|
|
|
|
printf( "> RGB %04lx:%04lx:%04lx, colors %3d, bits %2d %s\n",
|
|
|
|
visual->red_mask,
|
|
|
|
visual->green_mask,
|
|
|
|
visual->blue_mask,
|
|
|
|
visual->colormap_size,
|
|
|
|
visual->bits_per_rgb,
|
|
|
|
string );
|
|
|
|
visual++;
|
2000-05-17 01:44:38 +02:00
|
|
|
}
|
2000-03-28 18:40:51 +02:00
|
|
|
#endif
|
1999-12-17 00:11:37 +01:00
|
|
|
if ( format->bits_per_pixel == 24 )
|
|
|
|
add_pixel_mode( gr_pixel_mode_rgb24, format );
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
else if ( format->bits_per_pixel == 32 )
|
|
|
|
add_pixel_mode( gr_pixel_mode_rgb32, format );
|
|
|
|
}
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
else if ( format->depth == 16 )
|
|
|
|
{
|
|
|
|
int count2;
|
|
|
|
XVisualInfo* visuals;
|
|
|
|
XVisualInfo* visual;
|
|
|
|
|
|
|
|
template.depth = format->depth;
|
|
|
|
visuals = XGetVisualInfo( display,
|
|
|
|
VisualDepthMask,
|
|
|
|
&template,
|
|
|
|
&count2 );
|
|
|
|
visual = visuals;
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
while ( count2-- > 0 )
|
|
|
|
{
|
2000-05-17 01:44:38 +02:00
|
|
|
#ifdef TEST
|
1999-12-17 00:11:37 +01:00
|
|
|
const char* string = "unknown";
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
switch (visual->class)
|
|
|
|
{
|
|
|
|
case TrueColor: string = "TrueColor"; break;
|
|
|
|
case DirectColor: string = "DirectColor"; break;
|
|
|
|
case PseudoColor: string = "PseudoColor"; break;
|
|
|
|
case StaticGray : string = "StaticGray"; break;
|
|
|
|
case StaticColor: string = "StaticColor"; break;
|
|
|
|
case GrayScale: string = "GrayScale"; break;
|
|
|
|
}
|
|
|
|
|
2000-03-28 18:40:51 +02:00
|
|
|
printf( "> RGB %04lx:%04lx:%04lx, colors %3d, bits %2d %s\n",
|
1999-12-17 00:11:37 +01:00
|
|
|
visual->red_mask,
|
|
|
|
visual->green_mask,
|
|
|
|
visual->blue_mask,
|
|
|
|
visual->colormap_size,
|
|
|
|
visual->bits_per_rgb,
|
|
|
|
string );
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
#endif
|
|
|
|
if ( visual->red_mask == 0xf800 &&
|
|
|
|
visual->green_mask == 0x07e0 &&
|
|
|
|
visual->blue_mask == 0x001f )
|
|
|
|
add_pixel_mode( gr_pixel_mode_rgb565, format );
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
else if ( visual->red_mask == 0x7c00 &&
|
|
|
|
visual->green_mask == 0x03e0 &&
|
|
|
|
visual->blue_mask == 0x001f )
|
|
|
|
add_pixel_mode( gr_pixel_mode_rgb555, format );
|
2000-05-17 01:44:38 +02:00
|
|
|
|
|
|
|
/* other 16-bit modes are ignored */
|
1999-12-17 00:11:37 +01:00
|
|
|
visual++;
|
|
|
|
}
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
XFree( visuals );
|
|
|
|
}
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
format++;
|
|
|
|
}
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
XFree( formats );
|
|
|
|
}
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
gr_x11_device.num_pixel_modes = num_pixel_modes;
|
|
|
|
gr_x11_device.pixel_modes = pixel_modes;
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static
|
|
|
|
void convert_gray_to_pal8( grXSurface* surface,
|
|
|
|
int x,
|
|
|
|
int y,
|
|
|
|
int w,
|
|
|
|
int h )
|
|
|
|
{
|
|
|
|
grBitmap* target = &surface->image;
|
|
|
|
grBitmap* source = &surface->root.bitmap;
|
|
|
|
byte* write = (byte*)target->buffer + y*target->pitch + x;
|
|
|
|
byte* read = (byte*)source->buffer + y*source->pitch + x;
|
|
|
|
XColor* palette = surface->color;
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
while (h > 0)
|
|
|
|
{
|
|
|
|
byte* _write = write;
|
|
|
|
byte* _read = read;
|
|
|
|
byte* limit = _write + w;
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
for ( ; _write < limit; _write++, _read++ )
|
|
|
|
*_write = (byte) palette[ *_read ].pixel;
|
|
|
|
|
|
|
|
write += target->pitch;
|
|
|
|
read += source->pitch;
|
|
|
|
h--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static
|
|
|
|
void convert_gray_to_16( grXSurface* surface,
|
|
|
|
int x,
|
|
|
|
int y,
|
|
|
|
int w,
|
|
|
|
int h )
|
|
|
|
{
|
|
|
|
grBitmap* target = &surface->image;
|
|
|
|
grBitmap* source = &surface->root.bitmap;
|
|
|
|
byte* write = (byte*)target->buffer + y*target->pitch + 2*x;
|
|
|
|
byte* read = (byte*)source->buffer + y*source->pitch + x;
|
|
|
|
XColor* palette = surface->color;
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
while (h > 0)
|
|
|
|
{
|
|
|
|
byte* _write = write;
|
|
|
|
byte* _read = read;
|
|
|
|
byte* limit = _write + 2*w;
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
for ( ; _write < limit; _write += 2, _read++ )
|
|
|
|
*(short*)_write = (short)palette[ *_read ].pixel;
|
|
|
|
|
|
|
|
write += target->pitch;
|
|
|
|
read += source->pitch;
|
|
|
|
h--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static
|
|
|
|
void convert_gray_to_24( grXSurface* surface,
|
|
|
|
int x,
|
|
|
|
int y,
|
|
|
|
int w,
|
|
|
|
int h )
|
|
|
|
{
|
|
|
|
grBitmap* target = &surface->image;
|
|
|
|
grBitmap* source = &surface->root.bitmap;
|
|
|
|
byte* write = (byte*)target->buffer + y*target->pitch + 3*x;
|
|
|
|
byte* read = (byte*)source->buffer + y*source->pitch + x;
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
while (h > 0)
|
|
|
|
{
|
|
|
|
byte* _write = write;
|
|
|
|
byte* _read = read;
|
|
|
|
byte* limit = _write + 3*w;
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
for ( ; _write < limit; _write += 3, _read++ )
|
|
|
|
{
|
2000-03-28 18:40:51 +02:00
|
|
|
XColor* color = surface->color + *_read;
|
2000-05-17 01:44:38 +02:00
|
|
|
|
2000-03-28 18:40:51 +02:00
|
|
|
_write[0] = color->red;
|
|
|
|
_write[1] = color->green;
|
|
|
|
_write[2] = color->blue;
|
1999-12-17 00:11:37 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
write += target->pitch;
|
|
|
|
read += source->pitch;
|
|
|
|
h--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static
|
|
|
|
void convert_gray_to_32( grXSurface* surface,
|
|
|
|
int x,
|
|
|
|
int y,
|
|
|
|
int w,
|
|
|
|
int h )
|
|
|
|
{
|
|
|
|
grBitmap* target = &surface->image;
|
|
|
|
grBitmap* source = &surface->root.bitmap;
|
|
|
|
byte* write = (byte*)target->buffer + y*target->pitch + 4*x;
|
|
|
|
byte* read = (byte*)source->buffer + y*source->pitch + x;
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
while (h > 0)
|
|
|
|
{
|
|
|
|
byte* _write = write;
|
|
|
|
byte* _read = read;
|
|
|
|
byte* limit = _write + 4*w;
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
for ( ; _write < limit; _write += 4, _read++ )
|
|
|
|
{
|
|
|
|
byte color = *_read;
|
2000-03-28 18:40:51 +02:00
|
|
|
|
|
|
|
*(unsigned long*)_write = surface->color[color].pixel;
|
1999-12-17 00:11:37 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
write += target->pitch;
|
|
|
|
read += source->pitch;
|
|
|
|
h--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static
|
|
|
|
void convert_rectangle( grXSurface* surface,
|
|
|
|
int x,
|
|
|
|
int y,
|
|
|
|
int w,
|
|
|
|
int h )
|
|
|
|
{
|
|
|
|
int z;
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
/* first of all, clip to the surface's area */
|
|
|
|
if ( x >= surface->image.width ||
|
|
|
|
x+w <= 0 ||
|
|
|
|
y >= surface->image.rows ||
|
|
|
|
y+h <= 0 )
|
|
|
|
return;
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
if ( x < 0 )
|
|
|
|
{
|
|
|
|
w += x;
|
|
|
|
x = 0;
|
|
|
|
}
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
z = (x + w) - surface->image.width;
|
|
|
|
if (z > 0)
|
|
|
|
w -= z;
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
z = (y + h) - surface->image.rows;
|
|
|
|
if (z > 0)
|
|
|
|
h -= z;
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
/* convert the rectangle to the target depth for gray surfaces */
|
|
|
|
if (surface->gray)
|
|
|
|
{
|
2000-03-28 18:40:51 +02:00
|
|
|
switch (surface->xdepth->bits_per_pixel)
|
1999-12-17 00:11:37 +01:00
|
|
|
{
|
|
|
|
case 8 : convert_gray_to_pal8( surface, x, y, w, h ); break;
|
|
|
|
case 16: convert_gray_to_16 ( surface, x, y, w, h ); break;
|
|
|
|
case 24: convert_gray_to_24 ( surface, x, y, w, h ); break;
|
|
|
|
case 32: convert_gray_to_32 ( surface, x, y, w, h ); break;
|
|
|
|
}
|
|
|
|
}
|
2000-05-17 01:44:38 +02:00
|
|
|
}
|
1999-12-17 00:11:37 +01:00
|
|
|
|
|
|
|
|
|
|
|
static
|
|
|
|
void refresh_rectangle( grXSurface* surface,
|
|
|
|
int x,
|
|
|
|
int y,
|
|
|
|
int w,
|
|
|
|
int h )
|
|
|
|
{
|
|
|
|
if (surface->gray)
|
|
|
|
convert_rectangle( surface, x, y, w, h );
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
XPutImage( display,
|
|
|
|
surface->win,
|
|
|
|
surface->gc,
|
|
|
|
surface->ximage,
|
|
|
|
x, y, x, y, w, h );
|
|
|
|
}
|
|
|
|
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
static
|
|
|
|
void set_title( grXSurface* surface,
|
|
|
|
const char* title )
|
|
|
|
{
|
|
|
|
XStoreName( display, surface->win, title );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static
|
|
|
|
grKey KeySymTogrKey( KeySym key )
|
|
|
|
{
|
|
|
|
grKey k;
|
|
|
|
int count = sizeof(key_translators)/sizeof(key_translators[0]);
|
|
|
|
Translator* trans = key_translators;
|
|
|
|
Translator* limit = trans + count;
|
|
|
|
|
|
|
|
k = grKeyNone;
|
|
|
|
|
|
|
|
while ( trans < limit )
|
|
|
|
{
|
|
|
|
if ( trans->xkey == key )
|
|
|
|
{
|
|
|
|
k = trans->grkey;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
trans++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return k;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2000-05-17 01:44:38 +02:00
|
|
|
static
|
1999-12-17 00:11:37 +01:00
|
|
|
void listen_event( grXSurface* surface,
|
|
|
|
int event_mask,
|
|
|
|
grEvent* grevent )
|
|
|
|
{
|
|
|
|
static char key_buffer[10];
|
|
|
|
static int key_cursor = 0;
|
|
|
|
static int key_number = 0;
|
|
|
|
static XEvent x_event;
|
|
|
|
KeySym key;
|
|
|
|
|
|
|
|
int bool_exit;
|
|
|
|
grKey grkey;
|
|
|
|
|
|
|
|
XComposeStatus compose;
|
|
|
|
|
|
|
|
/* XXXX : For now, ignore the event mask, and only exit when */
|
|
|
|
/* a key is pressed.. */
|
|
|
|
(void)event_mask;
|
|
|
|
|
|
|
|
bool_exit = key_cursor < key_number;
|
|
|
|
|
|
|
|
XDefineCursor( display, surface->win, idle );
|
|
|
|
|
|
|
|
while ( !bool_exit )
|
|
|
|
{
|
|
|
|
XNextEvent( display, &x_event );
|
|
|
|
|
|
|
|
switch ( x_event.type )
|
|
|
|
{
|
2000-05-17 01:44:38 +02:00
|
|
|
case KeyPress:
|
1999-12-17 00:11:37 +01:00
|
|
|
key_number = XLookupString( &x_event.xkey,
|
|
|
|
key_buffer,
|
|
|
|
sizeof ( key_buffer ),
|
|
|
|
&key,
|
|
|
|
&compose );
|
|
|
|
key_cursor = 0;
|
|
|
|
|
|
|
|
if ( key_number == 0 ||
|
|
|
|
key > 512 )
|
|
|
|
{
|
|
|
|
/* this may be a special key like F1, F2, etc.. */
|
|
|
|
grkey = KeySymTogrKey(key);
|
|
|
|
if (grkey != grKeyNone)
|
2000-05-17 01:44:38 +02:00
|
|
|
goto Set_Key;
|
1999-12-17 00:11:37 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
bool_exit = 1;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MappingNotify:
|
|
|
|
XRefreshKeyboardMapping( &x_event.xmapping );
|
|
|
|
break;
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
case Expose:
|
|
|
|
refresh_rectangle( surface,
|
|
|
|
x_event.xexpose.x,
|
|
|
|
x_event.xexpose.y,
|
|
|
|
x_event.xexpose.width,
|
|
|
|
x_event.xexpose.height );
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* You should add more cases to handle mouse events, etc. */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
XDefineCursor( display, surface->win, busy );
|
|
|
|
XFlush ( display );
|
|
|
|
|
|
|
|
/* Now, translate the keypress to a grKey */
|
|
|
|
/* If this wasn't part of the simple translated keys, simply get the charcode */
|
|
|
|
/* from the character buffer */
|
|
|
|
grkey = grKEY(key_buffer[key_cursor++]);
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
Set_Key:
|
|
|
|
grevent->type = gr_key_down;
|
|
|
|
grevent->key = grkey;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
grXSurface* init_surface( grXSurface* surface,
|
|
|
|
grBitmap* bitmap )
|
|
|
|
{
|
|
|
|
int screen;
|
|
|
|
grBitmap* image;
|
|
|
|
char grays;
|
|
|
|
XDepth* format;
|
|
|
|
int image_depth;
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
screen = DefaultScreen( display );
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
surface->colormap = DefaultColormap( display, screen );
|
|
|
|
surface->depth = DefaultDepth( display, screen );
|
2000-05-17 01:44:38 +02:00
|
|
|
surface->visual = DefaultVisual( display, screen );
|
1999-12-17 00:11:37 +01:00
|
|
|
|
|
|
|
image = &surface->image;
|
|
|
|
|
|
|
|
/* force the surface image depth to 1 if necessary */
|
|
|
|
/* as this should be supported by all windows */
|
|
|
|
image_depth = surface->depth;
|
|
|
|
if (bitmap->mode == gr_pixel_mode_mono)
|
|
|
|
image_depth = 1;
|
|
|
|
|
|
|
|
grays = ( bitmap->mode == gr_pixel_mode_gray &&
|
|
|
|
bitmap->grays >= 2 );
|
|
|
|
|
|
|
|
surface->gray = grays;
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
/* copy dimensions */
|
|
|
|
image->width = bitmap->width;
|
|
|
|
image->rows = bitmap->rows;
|
|
|
|
image->mode = bitmap->mode;
|
|
|
|
image->pitch = 0;
|
|
|
|
image->grays = 0;
|
|
|
|
image->buffer = 0;
|
|
|
|
|
|
|
|
/* find the supported format corresponding to the request */
|
|
|
|
format = 0;
|
|
|
|
|
2000-05-17 01:44:38 +02:00
|
|
|
if (grays)
|
1999-12-17 00:11:37 +01:00
|
|
|
{
|
|
|
|
/* choose the default depth in case of grays rendering */
|
|
|
|
int i;
|
|
|
|
for ( i = 0; i < num_pixel_modes; i++ )
|
|
|
|
if ( image_depth == pixel_depth[i].depth )
|
|
|
|
{
|
|
|
|
format = pixel_depth + i;
|
2000-03-28 18:40:51 +02:00
|
|
|
surface->xdepth = format;
|
1999-12-17 00:11:37 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* otherwise, select the format depending on the pixel mode */
|
|
|
|
int i;
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
format = 0;
|
|
|
|
for ( i = 0; i < num_pixel_modes; i++ )
|
|
|
|
if ( pixel_modes[i] == bitmap->mode )
|
|
|
|
{
|
|
|
|
format = pixel_depth + i;
|
2000-03-28 18:40:51 +02:00
|
|
|
surface->xdepth = format;
|
1999-12-17 00:11:37 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
if (!format)
|
|
|
|
{
|
|
|
|
grError = gr_err_bad_argument;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* correct surface.depth. This is required because in the case */
|
|
|
|
/* of 32-bits pixels, the value of "format.depth" is 24 under X11 */
|
|
|
|
if ( format->depth == 24 &&
|
|
|
|
format->bits_per_pixel == 32 )
|
|
|
|
image_depth = 32;
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
/* allocate surface image */
|
|
|
|
{
|
|
|
|
int bits, over;
|
|
|
|
|
|
|
|
bits = image->width * format->bits_per_pixel;
|
|
|
|
over = bits % format->scanline_pad;
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
if (over)
|
|
|
|
bits += format->scanline_pad - over;
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
if (!grays)
|
|
|
|
{
|
|
|
|
image->width = bits;
|
|
|
|
bitmap->width = bits;
|
|
|
|
}
|
|
|
|
image->pitch = bits >> 3;
|
|
|
|
}
|
|
|
|
|
|
|
|
image->buffer = grAlloc( image->pitch * image->rows );
|
|
|
|
if (!image->buffer) return 0;
|
|
|
|
|
|
|
|
/* now, allocate a gray pal8 pixmap, only when we asked */
|
|
|
|
/* for an 8-bit pixmap */
|
|
|
|
if ( grays )
|
|
|
|
{
|
|
|
|
/* pad pitch to 32 bits */
|
|
|
|
bitmap->pitch = (bitmap->width + 3) & -4;
|
|
|
|
bitmap->buffer = grAlloc( bitmap->pitch * bitmap->rows );
|
|
|
|
if (!bitmap->buffer)
|
|
|
|
Panic( "grX11: could not allocate surface bitmap!\n" );
|
|
|
|
}
|
|
|
|
else /* otherwise */
|
|
|
|
{
|
|
|
|
*bitmap = *image;
|
|
|
|
}
|
|
|
|
|
|
|
|
surface->root.bitmap = *bitmap;
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
/* Now create the surface X11 image */
|
2000-05-17 01:44:38 +02:00
|
|
|
surface->ximage = XCreateImage( display,
|
1999-12-17 00:11:37 +01:00
|
|
|
surface->visual,
|
|
|
|
format->depth,
|
|
|
|
format->depth == 1 ? XYBitmap : ZPixmap,
|
|
|
|
0,
|
|
|
|
(char*)image->buffer,
|
|
|
|
image->width,
|
|
|
|
image->rows,
|
|
|
|
8,
|
|
|
|
0 );
|
|
|
|
if ( !surface->ximage )
|
|
|
|
Panic( "grX11: cannot create surface X11 image\n" );
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
|
|
|
|
/* allocate gray levels in the case of gray surface */
|
|
|
|
if ( grays )
|
|
|
|
{
|
|
|
|
XColor* color = surface->color;
|
|
|
|
int i;
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
for ( i = 0; i < bitmap->grays; i++, color++ )
|
|
|
|
{
|
|
|
|
color->red =
|
|
|
|
color->green =
|
|
|
|
color->blue = 65535 - ( i * 65535 ) / bitmap->grays;
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
if ( !XAllocColor( display, surface->colormap, color ) )
|
|
|
|
Panic( "ERROR: cannot allocate Color\n" );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if ( image_depth == 1 )
|
|
|
|
{
|
|
|
|
surface->ximage->byte_order = MSBFirst;
|
|
|
|
surface->ximage->bitmap_bit_order = MSBFirst;
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
XTextProperty xtp;
|
|
|
|
XSizeHints xsh;
|
|
|
|
XSetWindowAttributes xswa;
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
xswa.border_pixel = BlackPixel( display, screen );
|
|
|
|
xswa.background_pixel = WhitePixel( display, screen );
|
|
|
|
xswa.cursor = busy;
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
xswa.event_mask = KeyPressMask | ExposureMask;
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
surface->win = XCreateWindow( display,
|
|
|
|
RootWindow( display, screen ),
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
image->width,
|
|
|
|
image->rows,
|
|
|
|
10,
|
|
|
|
surface->depth,
|
2000-05-17 01:44:38 +02:00
|
|
|
InputOutput,
|
1999-12-17 00:11:37 +01:00
|
|
|
surface->visual,
|
|
|
|
CWBackPixel | CWBorderPixel |
|
|
|
|
CWEventMask | CWCursor,
|
|
|
|
&xswa );
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
XMapWindow( display, surface->win );
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
surface->gc = XCreateGC( display, RootWindow( display, screen ), 0L, NULL );
|
|
|
|
XSetForeground( display, surface->gc, xswa.border_pixel );
|
|
|
|
XSetBackground( display, surface->gc, xswa.background_pixel );
|
2000-05-17 01:44:38 +02:00
|
|
|
|
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
/* make window manager happy :-) */
|
|
|
|
xtp.value = (unsigned char*)"FreeType";
|
|
|
|
xtp.encoding = 31;
|
|
|
|
xtp.format = 8;
|
|
|
|
xtp.nitems = strlen( (char*)xtp.value );
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
xsh.x = 0;
|
|
|
|
xsh.y = 0;
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
xsh.width = image->width;
|
|
|
|
xsh.height = image->rows;
|
|
|
|
xsh.flags = (PPosition | PSize);
|
|
|
|
xsh.flags = 0;
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
XSetWMProperties( display, surface->win, &xtp, &xtp, NULL, 0, &xsh, NULL, NULL );
|
|
|
|
}
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
surface->root.done = (grDoneSurfaceFunc) done_surface;
|
|
|
|
surface->root.refresh_rect = (grRefreshRectFunc) refresh_rectangle;
|
|
|
|
surface->root.set_title = (grSetTitleFunc) set_title;
|
|
|
|
surface->root.listen_event = (grListenEventFunc) listen_event;
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
convert_rectangle( surface, 0, 0, bitmap->width, bitmap->rows );
|
|
|
|
return surface;
|
|
|
|
}
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
grDevice gr_x11_device =
|
|
|
|
{
|
|
|
|
sizeof( grXSurface ),
|
|
|
|
"x11",
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
init_device,
|
|
|
|
done_device,
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
(grDeviceInitSurfaceFunc) init_surface,
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
0,
|
|
|
|
0
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
#ifdef TEST
|
|
|
|
|
|
|
|
typedef struct grKeyName
|
|
|
|
{
|
|
|
|
grKey key;
|
|
|
|
const char* name;
|
|
|
|
|
|
|
|
} grKeyName;
|
|
|
|
|
|
|
|
|
|
|
|
static
|
|
|
|
const grKeyName key_names[] =
|
|
|
|
{
|
|
|
|
{ grKeyF1, "F1" },
|
|
|
|
{ grKeyF2, "F2" },
|
|
|
|
{ grKeyF3, "F3" },
|
|
|
|
{ grKeyF4, "F4" },
|
|
|
|
{ grKeyF5, "F5" },
|
|
|
|
{ grKeyF6, "F6" },
|
|
|
|
{ grKeyF7, "F7" },
|
|
|
|
{ grKeyF8, "F8" },
|
|
|
|
{ grKeyF9, "F9" },
|
|
|
|
{ grKeyF10, "F10" },
|
|
|
|
{ grKeyF11, "F11" },
|
|
|
|
{ grKeyF12, "F12" },
|
|
|
|
{ grKeyEsc, "Esc" },
|
|
|
|
{ grKeyHome, "Home" },
|
|
|
|
{ grKeyEnd, "End" },
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
{ grKeyPageUp, "Page_Up" },
|
|
|
|
{ grKeyPageDown, "Page_Down" },
|
|
|
|
{ grKeyLeft, "Left" },
|
|
|
|
{ grKeyRight, "Right" },
|
|
|
|
{ grKeyUp, "Up" },
|
|
|
|
{ grKeyDown, "Down" },
|
|
|
|
{ grKeyBackSpace, "BackSpace" },
|
|
|
|
{ grKeyReturn, "Return" }
|
|
|
|
};
|
|
|
|
|
2000-03-28 18:40:51 +02:00
|
|
|
#if 0
|
1999-12-17 00:11:37 +01:00
|
|
|
int main( void )
|
|
|
|
{
|
|
|
|
grSurface* surface;
|
|
|
|
int n;
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
grInit();
|
|
|
|
surface = grNewScreenSurface( 0, gr_pixel_mode_gray, 320, 400, 128 );
|
|
|
|
if (!surface)
|
|
|
|
Panic("Could not create window\n" );
|
|
|
|
else
|
|
|
|
{
|
|
|
|
grColor color;
|
|
|
|
grEvent event;
|
|
|
|
const char* string;
|
|
|
|
int x;
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
grSetSurfaceRefresh( surface, 1 );
|
|
|
|
grSetTitle(surface,"X11 driver demonstration" );
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
for ( x = -10; x < 10; x++ )
|
|
|
|
{
|
|
|
|
for ( n = 0; n < 128; n++ )
|
|
|
|
{
|
|
|
|
color.value = (n*3) & 127;
|
|
|
|
grWriteCellChar( surface,
|
|
|
|
x + ((n % 60) << 3),
|
|
|
|
80 + (x+10)*8*3 + ((n/60) << 3), n, color );
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
color.value = 64;
|
|
|
|
grWriteCellString( surface, 0, 0, "just an example", color );
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
do
|
|
|
|
{
|
|
|
|
listen_event((grXSurface*)surface, 0, &event);
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
/* return if ESC was pressed */
|
|
|
|
if ( event.key == grKeyEsc )
|
|
|
|
return 0;
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
/* otherwise, display key string */
|
|
|
|
color.value = (color.value + 8) & 127;
|
|
|
|
{
|
|
|
|
int count = sizeof(key_names)/sizeof(key_names[0]);
|
|
|
|
grKeyName* name = key_names;
|
|
|
|
grKeyName* limit = name + count;
|
|
|
|
const char* kname = 0;
|
|
|
|
char kname_temp[16];
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
while (name < limit)
|
|
|
|
{
|
|
|
|
if ( name->key == event.key )
|
|
|
|
{
|
|
|
|
kname = name->name;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
name++;
|
|
|
|
}
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
if (!kname)
|
|
|
|
{
|
|
|
|
sprintf( kname_temp, "char '%c'", (char)event.key );
|
|
|
|
kname = kname_temp;
|
|
|
|
}
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
grWriteCellString( surface, 30, 30, kname, color );
|
|
|
|
grRefreshSurface(surface);
|
|
|
|
paint_rectangle( surface, 0, 0, surface->bitmap.width, surface->bitmap.rows );
|
|
|
|
}
|
|
|
|
} while (1);
|
|
|
|
}
|
2000-05-17 01:44:38 +02:00
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
return 0;
|
2000-05-17 01:44:38 +02:00
|
|
|
|
|
|
|
|
1999-12-17 00:11:37 +01:00
|
|
|
}
|
2000-03-28 18:40:51 +02:00
|
|
|
#endif /* O */
|
1999-12-17 00:11:37 +01:00
|
|
|
#endif /* TEST */
|
|
|
|
|