shithub: freetype+ttf2subf

ref: 9ab7c3c37e1514004f7e6ec05761f868e68420a6
dir: /demos/graph/grx11.c/

View raw version
#include "grx11.h"


#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;
    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 */