shithub: freetype+ttf2subf

ref: 4f2d5b9a483996ddc103ead9eb223ba62b168a3b
dir: /demos/graph/gros2pm.c/

View raw version
#include "gros2pm.h"


#define INCL_DOS
#define INCL_WIN
#define INCL_GPI
#define INCL_SUB

#include <os2.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>


  static void Panic( const char* message )
  {
    fprintf( stderr, "%s", message );
    exit(1);
  }

  typedef struct Translator
  {
    ULONG   os2key;
    grKey   grkey;
    
  } Translator;
  

  static
  Translator  key_translators[] =
  {
    { VK_BACKSPACE, grKeyBackSpace },
    { VK_TAB,       grKeyTab       },
    { VK_ENTER,     grKeyReturn    },
    { VK_ESC,       grKeyEsc       },
    { VK_HOME,      grKeyHome      },
    { VK_LEFT,      grKeyLeft      },
    { VK_UP,        grKeyUp        },
    { VK_RIGHT,     grKeyRight     },
    { VK_DOWN,      grKeyDown      },
    { VK_PAGEUP,    grKeyPageUp    },
    { VK_PAGEDOWN,  grKeyPageDown  },
    { VK_END,       grKeyEnd       },
    { VK_F1,        grKeyF1        },
    { VK_F2,        grKeyF2        },
    { VK_F3,        grKeyF3        },
    { VK_F4,        grKeyF4        },
    { VK_F5,        grKeyF5        },
    { VK_F6,        grKeyF6        },
    { VK_F7,        grKeyF7        },
    { VK_F8,        grKeyF8        },
    { VK_F9,        grKeyF9        },
    { VK_F10,       grKeyF10       },
    { VK_F11,       grKeyF11       },
    { VK_F12,       grKeyF12       }
  };


#define MAX_PIXEL_MODES  32

  static int           num_pixel_modes = 0;
  static grPixelMode   pixel_modes[ MAX_PIXEL_MODES ];  
  static int           pixel_depth[ MAX_PIXEL_MODES ];

  static  HAB   gr_anchor;   /* device anchor block */

  typedef POINTL  PMBlitPoints[4];


  typedef struct grPMSurface_
  {
    grSurface  root;
    grBitmap   image;

    HAB        anchor;         /* handle to anchor block for surface's window */
    HWND       frame_window;   /* handle to window's frame                    */
    HWND       client_window;  /* handle to window's client                   */
    HWND       title_window;   /* handle to window's title bar                */

    HPS        image_ps;       /* memory presentation space used to hold */
                               /* the surface's content under PM         */
    HDC        image_dc;       /* memory device context for the image    */

    HEV        event_lock;     /* semaphore used in listen_surface   */
    HMTX       image_lock;     /* a mutex used to synchronise access */
                               /* to the memory presentation space   */
                               /* used to hold the surface           */

    TID        message_thread; /* thread used to process this surface's */
                               /* messages..                            */

    PBITMAPINFO2 bitmap_header;/* os/2 bitmap descriptor                   */
    HBITMAP      os2_bitmap;   /* Handle to OS/2 bitmap contained in image */
    BOOL         ready;        /* ??? */

    long         shades[256];  /* indices of gray levels in pixel_mode_gray */

    POINTL       surface_blit[4];  /* surface blitting table   */
    POINTL       magnify_blit[4];  /* magnifier blitting table */
    int          magnification;    /* level of magnification   */
    POINTL       magnify_center;
    SIZEL        magnify_size;

    grEvent      event;

    PMBlitPoints blit_points;

  } grPMSurface;



  static
  void  enable_os2_iostreams( void )
  {
    PTIB  thread_block;
    PPIB  process_block;

    /* XXX : This is a very nasty hack, it fools OS/2 and let the program */
    /*       call PM functions, even though stdin/stdout/stderr are still */
    /*       directed to the standard i/o streams..                       */
    /*       The program must be compiled with WINDOWCOMPAT               */
    /*                                                                    */
    /*   Credits go to Michal for finding this !!                         */
    /*                                                                    */
    DosGetInfoBlocks( &thread_block, &process_block );
    process_block->pib_ultype = 3;
  }



  static
  int  init_device( void )
  {
    enable_os2_iostreams();

    /* create an anchor block. This will allow this thread (i.e. the */
    /* main one) to call Gpi functions..                             */
    gr_anchor = WinInitialize(0);
    if (!gr_anchor)
    {
      /* could not initialise Presentation Manager */
      return -1;
    }

    return 0;
  }



  static
  void  done_device( void )
  {
    /* Indicates that we do not use the Presentation Manager, this */
    /* will also release all associated resources..                */
    WinTerminate( gr_anchor );
  }



  /* close a given window */
  static
  void  done_surface( grPMSurface*  surface )
  {
    if ( surface->frame_window )
      WinDestroyWindow( surface->frame_window );

    WinReleasePS( surface->image_ps );

    grDoneBitmap( &surface->image );
    grDoneBitmap( &surface->root.bitmap );
  }





  static
  void add_pixel_mode( grPixelMode  pixel_mode,
                       int          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++;
  }


#define LOCK(x)    DosRequestMutexSem( x, SEM_INDEFINITE_WAIT );
#define UNLOCK(x)  DosReleaseMutexSem( x )


  static
  const int  pixel_mode_bit_count[] =
  {
    0,
    1,
    4,
    8,   /* pal8 */
    8,   /* gray */
    15,
    16,
    24,
    32
  };


 /************************************************************************
  *
  * Technical note : how the OS/2 Presntation Manager driver works
  *
  * PM is, in my opinion, a bloated and over-engineered graphics
  * sub-system, even though it has lots of nice features. Here are
  * a few tidbits about it :
  *
  *
  * - under PM, a "bitmap" is a device-specific object whose bits are
  *   not directly accessible to the client application. This means
  *   that we must use a scheme like the following to display our
  *   surfaces :
  *
  *     - hold, for each surface, its own bitmap buffer where the
  *       rest of MiGS writes directly.
  *
  *     - create a PM bitmap object with the same dimensions (and
  *       possibly format).
  *
  *     - copy the content of each updated rectangle into the
  *       PM bitmap with the function 'GpiSetBitmapBits'.
  *
  *     - finally, "blit" the PM bitmap to the screen calling
  *       'GpiBlitBlt'
  *
  * - but there is more : you cannot directly blit a PM bitmap to the
  *   screen with PM. The 'GpiBlitBlt' only works with presentation
  *   spaces. This means that we also need to create, for each surface :
  *
  *     - a memory presentation space, used to hold the PM bitmap
  *     - a "memory device context" for the presentation space
  *
  *   The blit is then performed from the memory presentation space
  *   to the screen's presentation space..
  *
  *
  * - because each surface creates its own event-handling thread,
  *   we must protect the surface's presentation space from concurrent
  *   accesses (i.e. calls to 'GpiSetBitmapBits' when drawing to the
  *   surface, and calls to 'GpiBlitBlt' when drawing it on the screen
  *   are performed in two different threads).
  *
  *   we use a simple mutex to do this.
  *
  *
  * - we also use a semaphore to perform a rendez-vous between the
  *   main and event-handling threads (needed in "listen_event").
  *
  ************************************************************************/

  static
  void  RunPMWindow( grPMSurface*  surface );












  static
  void  convert_gray_to_pal8( grPMSurface* 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;
    long*      palette = surface->shades;
    
    while (h > 0)
    {
      byte*  _write = write;
      byte*  _read  = read;
      byte*  limit  = _write + w;
      
      for ( ; _write < limit; _write++, _read++ )
        *_write = (byte) palette[ *_read ];

      write += target->pitch;
      read  += source->pitch;
      h--;
    }
  }


  static
  void  convert_gray_to_16( grPMSurface* 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;
    long*           palette = surface->shades;
    
    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 ];

      write += target->pitch;
      read  += source->pitch;
      h--;
    }
  }


  static
  void  convert_gray_to_24( grPMSurface* 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( grPMSurface* 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( grPMSurface* 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->root.bitmap.mode == gr_pixel_mode_gray)
    {
      switch (surface->image.mode)
      {
        case gr_pixel_mode_pal8  :
          convert_gray_to_pal8( surface, x, y, w, h );
          break;

        case gr_pixel_mode_rgb555:
        case gr_pixel_mode_rgb565:
          convert_gray_to_16  ( surface, x, y, w, h );
          break;

        case gr_pixel_mode_rgb24:
          convert_gray_to_24  ( surface, x, y, w, h );
          break;

        case gr_pixel_mode_rgb32:
          convert_gray_to_32  ( surface, x, y, w, h );
          break;

        default:
          ;
      }
    }
  } 


  static
  void  refresh_rectangle( grPMSurface* surface,
                           int          x,
                           int          y,
                           int          w,
                           int          h )
  {
    convert_rectangle( surface, x, y, w, h );

    WinInvalidateRect( surface->client_window, NULL, FALSE );
    WinUpdateWindow( surface->frame_window );
  }

  
  static
  void  set_title( grPMSurface* surface,
                   const char*  title )
  {
    WinSetWindowText( surface->title_window, (PSZ)title );
  }



  static  
  void  listen_event( grPMSurface* surface,
                      int          event_mask,
                      grEvent*     grevent )
  {
    ULONG  ulRequestCount;

    (void) event_mask;   /* ignored for now */

    /* the listen_event function blocks until there is an event to process */
    DosWaitEventSem( surface->event_lock, SEM_INDEFINITE_WAIT );
    DosQueryEventSem( surface->event_lock, &ulRequestCount );
    *grevent = surface->event;
    DosResetEventSem( surface->event_lock, &ulRequestCount );

    return;
  }


  static
  int  init_surface( grPMSurface*  surface,
                     grBitmap*     bitmap )
  {
    PBITMAPINFO2  bit;
    SIZEL         sizl = { 0, 0 };
    LONG          palette[256];

    /* create the bitmap - under OS/2, we support all modes as PM */
    /* handles all conversions automatically..                    */
    if ( grNewBitmap( surface->root.bitmap.mode,
                      surface->root.bitmap.grays,
                      surface->root.bitmap.width,
                      surface->root.bitmap.rows,
                      bitmap ) )
      return grError;

    surface->root.bitmap = *bitmap;

    /* create the image and event lock */
    DosCreateEventSem( NULL, &surface->event_lock, 0, TRUE  );
    DosCreateMutexSem( NULL, &surface->image_lock, 0, FALSE );

    /* create the image's presentation space */
    surface->image_dc = DevOpenDC( gr_anchor,
                                   OD_MEMORY, (PSZ)"*", 0L, 0L, 0L );

    surface->image_ps = GpiCreatePS( gr_anchor,
                                     surface->image_dc,
                                     &sizl,
                                     PU_PELS    | GPIT_MICRO |
                                     GPIA_ASSOC | GPIF_DEFAULT );

    GpiSetBackMix( surface->image_ps, BM_OVERPAINT );

    /* create the image's PM bitmap */
    bit = (PBITMAPINFO2)grAlloc( sizeof(BITMAPINFO2) + 256*sizeof(RGB2) );
    surface->bitmap_header = bit;

    bit->cbFix   = sizeof( BITMAPINFOHEADER2 );
    bit->cx      = surface->root.bitmap.width;
    bit->cy      = surface->root.bitmap.rows;
    bit->cPlanes = 1;

    bit->argbColor[0].bBlue  = 0;
    bit->argbColor[0].bGreen = 0;
    bit->argbColor[0].bRed   = 0;

    bit->argbColor[1].bBlue  = 255;
    bit->argbColor[1].bGreen = 255;
    bit->argbColor[1].bRed   = 255;

    bit->cBitCount = pixel_mode_bit_count[ surface->root.bitmap.mode ];

    surface->os2_bitmap = GpiCreateBitmap( surface->image_ps,
                                           (PBITMAPINFOHEADER2)bit,
                                           0L, NULL, NULL );

    GpiSetBitmap( surface->image_ps, surface->os2_bitmap );

    bit->cbFix = sizeof( BITMAPINFOHEADER2 );
    GpiQueryBitmapInfoHeader( surface->os2_bitmap,
                              (PBITMAPINFOHEADER2)bit );

    /* for gr_pixel_mode_gray, create a gray-levels logical palette */
    if ( bitmap->mode == gr_pixel_mode_gray )
    {
      int     x, count;

      count = bitmap->grays;
      for ( x = 0; x < count; x++ )
        palette[x] = (((count-x)*255)/count) * 0x010101;

      /* create logical color table */
      GpiCreateLogColorTable( surface->image_ps,
                              (ULONG) LCOL_PURECOLOR,
                              (LONG)  LCOLF_CONSECRGB,
                              (LONG)  0L,
                              (LONG)  count,
                              (PLONG) palette );

      /* now, copy the color indexes to surface->shades */
      for ( x = 0; x < count; x++ )
        surface->shades[x] = GpiQueryColorIndex( surface->image_ps,
                                                 0, palette[x] );
    }

    /* set up the blit points array */
    surface->blit_points[1].x = surface->root.bitmap.width;
    surface->blit_points[1].y = surface->root.bitmap.rows;
    surface->blit_points[3]   = surface->blit_points[1];

    /* Finally, create the event handling thread for the surface's window */
    DosCreateThread( &surface->message_thread,
                     (PFNTHREAD) RunPMWindow,
                     (ULONG)     surface,
                     0UL,
                     32920 );

    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 0;
  }



  MRESULT EXPENTRY  Message_Process( HWND    handle,
                                     ULONG   mess,
                                     MPARAM  parm1,
                                     MPARAM  parm2 );


  static
  void  RunPMWindow( grPMSurface*  surface )
  {
    unsigned char   class_name[] = "DisplayClass";
             ULONG  class_flags;

    static   HMQ    queue;
             QMSG   message;

    /* create an anchor to allow this thread to use PM */
    surface->anchor = WinInitialize(0);
    if (!surface->anchor)
    {
      printf( "Error doing WinInitialize()\n" );
      return;
    }

    /* create a message queue */
    queue = WinCreateMsgQueue( surface->anchor, 0 );
    if (!queue)
    {
      printf( "Error doing >inCreateMsgQueue()\n" );
      return;
    }

    /* register the window class */
    if ( !WinRegisterClass( surface->anchor,
                            (PSZ)   class_name,
                            (PFNWP) Message_Process,
                            CS_SIZEREDRAW,
                            0 ) )
    {
      printf( "Error doing WinRegisterClass()\n" );
      return;
    }

    /* create the PM window */
    class_flags = FCF_TITLEBAR | FCF_MINBUTTON | FCF_DLGBORDER | 
                  FCF_TASKLIST | FCF_SYSMENU; 

    surface->frame_window = WinCreateStdWindow(
                                HWND_DESKTOP,
                                WS_VISIBLE,
                                &class_flags,
                                (PSZ) class_name,
                                (PSZ) "FreeType PM Graphics",
                                WS_VISIBLE,
                                0, 0,
                                &surface->client_window );
    if (!surface->frame_window)
    {
      printf( "Error doing WinCreateStdWindow()\n" );
      return;
    }

    /* find the title window handle */
    surface->title_window = WinWindowFromID( surface->frame_window,
                                             FID_TITLEBAR );

    /* set Window size and position */
    WinSetWindowPos( surface->frame_window,
                     0L,
                     (SHORT) 60,
                     (SHORT) WinQuerySysValue( HWND_DESKTOP, SV_CYSCREEN ) -
                             surface->root.bitmap.rows + 100,

                     (SHORT) WinQuerySysValue( HWND_DESKTOP, SV_CYDLGFRAME )*2 +
                             surface->root.bitmap.width,

                     (SHORT) WinQuerySysValue( HWND_DESKTOP, SV_CYTITLEBAR ) +
                             WinQuerySysValue( HWND_DESKTOP, SV_CYDLGFRAME )*2 +
                             surface->root.bitmap.rows,

                     SWP_SIZE | SWP_MOVE );

    /* save the handle to the current surface within the window words */
    WinSetWindowPtr( surface->frame_window,QWL_USER, surface );

    /* run the message queue till the end */
    while ( WinGetMsg( surface->anchor, &message, (HWND)NULL, 0, 0 ) )
      WinDispatchMsg( surface->anchor, &message );

    /* clean-up */
    WinDestroyWindow( surface->frame_window );
    surface->frame_window = 0;

    WinDestroyMsgQueue( queue );
    WinTerminate( surface->anchor );

    /* await death... */
    while ( 1 )
      DosSleep( 100 );
  }




  /* Message processing for our PM Window class */
  MRESULT EXPENTRY  Message_Process( HWND    handle,
                                     ULONG   mess,
                                     MPARAM  parm1,
                                     MPARAM  parm2 )
  {
     static HDC     screen_dc;
     static HPS     screen_ps;
     static BOOL    minimized;

     SIZEL   sizl;
     SWP     swp;

     grPMSurface*  surface;

    /* get the handle to the window's surface */
    surface = (grPMSurface*)WinQueryWindowPtr( handle, QWL_USER );

    switch( mess )
    {
    case WM_DESTROY:
      /* warn the main thread to quit if it didn't know */
      surface->event.type = gr_event_key;
      surface->event.key  = grKeyEsc;
      DosPostEventSem( surface->event_lock );
      break;

    case WM_CREATE:
      /* set original magnification */
      minimized = FALSE;

      /* create Device Context and Presentation Space for screen. */
      screen_dc = WinOpenWindowDC( handle );
      screen_ps = GpiCreatePS( surface->anchor,
                               screen_dc,
                               &sizl,
                               PU_PELS | GPIT_MICRO |
                               GPIA_ASSOC | GPIF_DEFAULT );

      /* take the input focus */
      WinFocusChange( HWND_DESKTOP, handle, 0L );
      break;

    case WM_MINMAXFRAME:
      /* to update minimized if changed */
      swp = *((PSWP) parm1);
      if ( swp.fl & SWP_MINIMIZE ) 
        minimized = TRUE;
      if ( swp.fl & SWP_RESTORE )
        minimized = FALSE;
      return WinDefWindowProc( handle, mess, parm1, parm2 );
      break;

    case WM_ERASEBACKGROUND:
    case WM_PAINT:  
      /* copy the memory image of the screen out to the real screen */
      DosRequestMutexSem( surface->image_lock, SEM_INDEFINITE_WAIT );
      WinBeginPaint( handle, screen_ps, NULL );
      
      /* main image and magnified picture */
      GpiBitBlt( screen_ps,
                 surface->image_ps,
                 4L,
                 surface->blit_points,
                 ROP_SRCCOPY, BBO_AND );

      WinEndPaint( screen_ps );
      DosReleaseMutexSem( surface->image_lock );   
      break;

    case WM_CHAR:
      if ( CHARMSG( &mess )->fs & KC_KEYUP )
        break;

      /* look for a specific vkey */
      {
        int          count = sizeof( key_translators )/sizeof( key_translators[0] );
        Translator*  trans = key_translators;
        Translator*  limit = trans + count;

        for ( ; trans < limit; trans++ )
          if ( CHARMSG(&mess)->vkey == trans->os2key )
          {
            surface->event.key = trans->grkey;
            goto Do_Key_Event;
          }
      }

      /* otherwise, simply record the character code */
      if ( (CHARMSG( &mess )->fs & KC_CHAR) == 0 )
        break;

      surface->event.key = CHARMSG(&mess)->chr;

    Do_Key_Event:
      surface->event.type = gr_event_key;
      DosPostEventSem( surface->event_lock );
      break;

    default:
      return WinDefWindowProc( handle, mess, parm1, parm2 );
    }

    return (MRESULT) FALSE;
  }







#if 0
  static
  grKey  KeySymTogrKey(   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( grPMSurface* surface,
                      int          event_mask,
                      grEvent*     grevent )
  {
    grKey           grkey;

    /* XXXX : For now, ignore the event mask, and only exit when */
    /*        a key is pressed..                                 */
    (void)event_mask;


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

#endif



  grDevice  gr_os2pm_device =
  {
    sizeof( grPMSurface ),
    "os2pm",
    
    init_device,
    done_device,
    
    (grDeviceInitSurfaceFunc) init_surface,
    
    0,
    0
    
  };