blob: 8ca4f7f08ef6e7dcfdea258c4db3754668b5da21 [file] [log] [blame]
#include "grx11.h"
#define TEST
#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;
} Translator;
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
#define grAlloc malloc
#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;
static grPixelMode pixel_modes[ MAX_PIXEL_MODES ];
static XDepth pixel_depth[ MAX_PIXEL_MODES ];
typedef struct grXSurface_
{
grSurface root;
grBitmap image;
Window win;
Visual* visual;
Colormap colormap;
int depth;
XDepth* xdepth;
Bool gray;
GC gc;
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;
int image_width;
int image_height;
} 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" );
pixel_modes[ num_pixel_modes ] = pixel_mode;
pixel_depth[ num_pixel_modes ] = *depth;
num_pixel_modes++;
}
static
int init_device( void )
{
XDepth dummy;
XrmInitialize();
display = XOpenDisplay( displayname );
if (!display)
{
return -1;
/* Panic( "Gr:error: cannot open X11 display\n" ); */
}
idle = XCreateFontCursor( display, XC_left_ptr );
busy = XCreateFontCursor( display, XC_watch );
num_pixel_modes = 0;
/* 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;
#ifdef TEST
printf( "available pixmap formats\n" );
printf( "depth pixbits scanpad\n" );
#endif
while ( count-- > 0 )
{
#ifdef TEST
printf( " %3d %3d %3d\n",
format->depth,
format->bits_per_pixel,
format->scanline_pad );
#endif
if ( format->depth == 1 )
/* usually, this should be the first format */
add_pixel_mode( gr_pixel_mode_mono, format );
else if ( format->depth == 8 )
add_pixel_mode( gr_pixel_mode_pal8, format );
/* note, the 32-bit modes return a depth of 24, and 32 bits per pixel */
else if ( format->depth == 24 )
{
#ifdef TEST
{
int count2;
XVisualInfo* visuals;
XVisualInfo* visual;
const char* string = "unknown";
template.depth = format->depth;
visuals = XGetVisualInfo( display,
VisualDepthMask,
&template,
&count2 );
visual = visuals;
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++;
}
#endif
if ( format->bits_per_pixel == 24 )
add_pixel_mode( gr_pixel_mode_rgb24, format );
else if ( format->bits_per_pixel == 32 )
add_pixel_mode( gr_pixel_mode_rgb32, format );
}
else if ( format->depth == 16 )
{
int count2;
XVisualInfo* visuals;
XVisualInfo* visual;
template.depth = format->depth;
visuals = XGetVisualInfo( display,
VisualDepthMask,
&template,
&count2 );
visual = visuals;
while ( count2-- > 0 )
{
#ifdef TEST
const char* string = "unknown";
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 );
#endif
if ( visual->red_mask == 0xf800 &&
visual->green_mask == 0x07e0 &&
visual->blue_mask == 0x001f )
add_pixel_mode( gr_pixel_mode_rgb565, format );
else if ( visual->red_mask == 0x7c00 &&
visual->green_mask == 0x03e0 &&
visual->blue_mask == 0x001f )
add_pixel_mode( gr_pixel_mode_rgb555, format );
/* other 16-bit modes are ignored */
visual++;
}
XFree( visuals );
}
format++;
}
XFree( formats );
}
gr_x11_device.num_pixel_modes = num_pixel_modes;
gr_x11_device.pixel_modes = pixel_modes;
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;
while (h > 0)
{
byte* _write = write;
byte* _read = read;
byte* limit = _write + w;
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;
while (h > 0)
{
byte* _write = write;
byte* _read = read;
byte* limit = _write + 2*w;
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;
while (h > 0)
{
byte* _write = write;
byte* _read = read;
byte* limit = _write + 3*w;
for ( ; _write < limit; _write += 3, _read++ )
{
XColor* color = surface->color + *_read;
_write[0] = color->red;
_write[1] = color->green;
_write[2] = color->blue;
}
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;
while (h > 0)
{
byte* _write = write;
byte* _read = read;
byte* limit = _write + 4*w;
for ( ; _write < limit; _write += 4, _read++ )
{
byte color = *_read;
*(unsigned long*)_write = surface->color[color].pixel;
}
write += target->pitch;
read += source->pitch;
h--;
}
}
static
void convert_rectangle( grXSurface* surface,
int x,
int y,
int w,
int h )
{
int z;
/* 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;
if ( x < 0 )
{
w += x;
x = 0;
}
z = (x + w) - surface->image.width;
if (z > 0)
w -= z;
z = (y + h) - surface->image.rows;
if (z > 0)
h -= z;
/* convert the rectangle to the target depth for gray surfaces */
if (surface->gray)
{
switch (surface->xdepth->bits_per_pixel)
{
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;
}
}
}
static
void refresh_rectangle( grXSurface* surface,
int x,
int y,
int w,
int h )
{
if (surface->gray)
convert_rectangle( surface, x, y, w, h );
XPutImage( display,
surface->win,
surface->gc,
surface->ximage,
x, y, x, y, w, h );
}
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;
}
static
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 )
{
case KeyPress:
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)
goto Set_Key;
}
else
bool_exit = 1;
break;
case MappingNotify:
XRefreshKeyboardMapping( &x_event.xmapping );
break;
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++]);
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;
screen = DefaultScreen( display );
surface->colormap = DefaultColormap( display, screen );
surface->depth = DefaultDepth( display, screen );
surface->visual = DefaultVisual( display, screen );
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;
/* 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;
if (grays)
{
/* 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;
surface->xdepth = format;
break;
}
}
else
{
/* otherwise, select the format depending on the pixel mode */
int i;
format = 0;
for ( i = 0; i < num_pixel_modes; i++ )
if ( pixel_modes[i] == bitmap->mode )
{
format = pixel_depth + i;
surface->xdepth = format;
break;
}
}
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;
/* allocate surface image */
{
int bits, over;
bits = image->width * format->bits_per_pixel;
over = bits % format->scanline_pad;
if (over)
bits += format->scanline_pad - over;
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;
/* Now create the surface X11 image */
surface->ximage = XCreateImage( display,
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" );
/* allocate gray levels in the case of gray surface */
if ( grays )
{
XColor* color = surface->color;
int i;
for ( i = 0; i < bitmap->grays; i++, color++ )
{
color->red =
color->green =
color->blue = 65535 - ( i * 65535 ) / bitmap->grays;
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;
xswa.border_pixel = BlackPixel( display, screen );
xswa.background_pixel = WhitePixel( display, screen );
xswa.cursor = busy;
xswa.event_mask = KeyPressMask | ExposureMask;
surface->win = XCreateWindow( display,
RootWindow( display, screen ),
0,
0,
image->width,
image->rows,
10,
surface->depth,
InputOutput,
surface->visual,
CWBackPixel | CWBorderPixel |
CWEventMask | CWCursor,
&xswa );
XMapWindow( display, surface->win );
surface->gc = XCreateGC( display, RootWindow( display, screen ), 0L, NULL );
XSetForeground( display, surface->gc, xswa.border_pixel );
XSetBackground( display, surface->gc, xswa.background_pixel );
/* make window manager happy :-) */
xtp.value = (unsigned char*)"FreeType";
xtp.encoding = 31;
xtp.format = 8;
xtp.nitems = strlen( (char*)xtp.value );
xsh.x = 0;
xsh.y = 0;
xsh.width = image->width;
xsh.height = image->rows;
xsh.flags = (PPosition | PSize);
xsh.flags = 0;
XSetWMProperties( display, surface->win, &xtp, &xtp, NULL, 0, &xsh, NULL, NULL );
}
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;
convert_rectangle( surface, 0, 0, bitmap->width, bitmap->rows );
return surface;
}
grDevice gr_x11_device =
{
sizeof( grXSurface ),
"x11",
init_device,
done_device,
(grDeviceInitSurfaceFunc) init_surface,
0,
0
};
#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" },
{ grKeyPageUp, "Page_Up" },
{ grKeyPageDown, "Page_Down" },
{ grKeyLeft, "Left" },
{ grKeyRight, "Right" },
{ grKeyUp, "Up" },
{ grKeyDown, "Down" },
{ grKeyBackSpace, "BackSpace" },
{ grKeyReturn, "Return" }
};
#if 0
int main( void )
{
grSurface* surface;
int n;
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;
grSetSurfaceRefresh( surface, 1 );
grSetTitle(surface,"X11 driver demonstration" );
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 );
do
{
listen_event((grXSurface*)surface, 0, &event);
/* return if ESC was pressed */
if ( event.key == grKeyEsc )
return 0;
/* 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];
while (name < limit)
{
if ( name->key == event.key )
{
kname = name->name;
break;
}
name++;
}
if (!kname)
{
sprintf( kname_temp, "char '%c'", (char)event.key );
kname = kname_temp;
}
grWriteCellString( surface, 30, 30, kname, color );
grRefreshSurface(surface);
paint_rectangle( surface, 0, 0, surface->bitmap.width, surface->bitmap.rows );
}
} while (1);
}
return 0;
}
#endif /* O */
#endif /* TEST */