#include "grx11.h" #ifdef TEST #include "grfont.h" #endif #include #include #include #include #include #include #include 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; 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 ) { 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 %02x:%02x:%02x, 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++ ) { byte color = *_read; _write[0] = _write[1] = _write[2] = color; } 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; _write[0] = _write[1] = _write[2] = _write[3] = color; } 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->depth) { 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; 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; 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" } }; 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 /* TEST */