static
  grPMSurface*  init_surface( grPMSurface*  surface,
                              grBitmap*     bitmap )
  {
    PBITMAPINFO2  bit;
    SIZEL         sizl = { 0, 0 };
    LONG          palette[256];
    LOG(( "Os2PM: init_surface( %08lx, %08lx )\n",
          (long)surface, (long)bitmap ));

    LOG(( "       -- input bitmap =\n" ));
    LOG(( "       --   mode   = %d\n", bitmap->mode ));
    LOG(( "       --   grays  = %d\n", bitmap->grays ));
    LOG(( "       --   width  = %d\n", bitmap->width ));
    LOG(( "       --   height = %d\n", bitmap->rows ));

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

    LOG(( "       -- output bitmap =\n" ));
    LOG(( "       --   mode   = %d\n", bitmap->mode ));
    LOG(( "       --   grays  = %d\n", bitmap->grays ));
    LOG(( "       --   width  = %d\n", bitmap->width ));
    LOG(( "       --   height = %d\n", bitmap->rows ));

    bitmap->pitch = -bitmap->pitch;
    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  = 255;
    bit->argbColor[0].bGreen = 0;
    bit->argbColor[0].bRed   = 0;

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

    bit->cBitCount = (bitmap->mode == gr_pixel_mode_gray ? 8 : 1 );

    if (bitmap->mode == gr_pixel_mode_gray)
    {
      RGB2*  color = bit->argbColor;
      int    x, count;

      count = bitmap->grays;
      for ( x = 0; x < count; x++, color++ )
      {
        color->bBlue  =
        color->bGreen =
        color->bRed   = (((count-x)*255)/count);
      }
    }
    else
    {
      RGB2*  color = bit->argbColor;

      color[0].bBlue  =
      color[0].bGreen =
      color[0].bRed   = 0;

      color[1].bBlue  =
      color[1].bGreen =
      color[1].bRed   = 255;
    }

    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 );
    surface->bitmap_header = 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 );

    /* wait for the window creation */
    LOCK(surface->image_lock);
    UNLOCK(surface->image_lock);

    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;
  }
/**************************************************************************\
*                                                                          *
*       ROUTINE:    ClkColorsDlgProc ( )                                   *
*                                                                          *
*       COMMENT:    "Clock Color Preferences" Dialog                       *
*                                                                          *
*       RETURNS:    MRESULT, 0 or return value from WinDefDlgProc          *
*                                                                          *
\**************************************************************************/
MRESULT EXPENTRY ClkColorsDlgProc(HWND hwnd, ULONG usMsg, MPARAM mp1, MPARAM mp2)
{
   static USHORT usCheckedPartID = 0, usCheckedColorID = 0;
   static LONG clrBackgroundNew;
   static LONG clrFaceNew;
   static LONG clrHourHandNew;
   static LONG clrMinuteHandNew;
   static LONG *pclrNew;

   switch (usMsg)
   {
      USHORT usButtonID;
      HWND hwndButton;
      HPS hpsT;
      RECTL rclButton, rclButtonInterior;

   case WM_INITDLG:
      hpsT = WinGetPS(hwnd);

      GpiCreateLogColorTable(hpsT, LCOL_RESET, LCOLF_RGB, 0L, 0L, NULL);

      /* Load the new values from the current ones.    */
      clrBackgroundNew = GpiQueryColorIndex(hpsT, 0L, cp.clrBackground);

      clrFaceNew       = GpiQueryColorIndex(hpsT, 0L, cp.clrFace      );
      clrHourHandNew   = GpiQueryColorIndex(hpsT, 0L, cp.clrHourHand  );
      clrMinuteHandNew = GpiQueryColorIndex(hpsT, 0L, cp.clrMinuteHand);

      WinReleasePS(hpsT);

      /* click the "Background" radio button */
      WinPostMsg(WinWindowFromID(hwnd,
                                 CLKCLR_BACKGROUND),
                                 BM_CLICK,
                                 MPFROMSHORT(TRUE),
                                 MPVOID);

      /* Let the default dialog procedure handle anything else.   */
      break;

   case WM_COMMAND:
      switch (LOUSHORT(mp1))
      {
         case DID_OK:
            hpsT = WinGetPS(hwnd);

            GpiCreateLogColorTable(hpsT, LCOLF_RGB, 0L, 0L, 0L, NULL);

              /*
               * Uupdate the one the user has selected.
               */
            switch(usCheckedPartID)
            {
            case CLKCLR_BACKGROUND:
               cp.clrBackground = GpiQueryRGBColor(hpsT, 0L, clrBackgroundNew);
               break;

            case CLKCLR_FACE:
               cp.clrFace = GpiQueryRGBColor(hpsT, 0L, clrFaceNew);
               break;

            case CLKCLR_HOURHAND:
               cp.clrHourHand = GpiQueryRGBColor(hpsT, 0L, clrHourHandNew);
               break;

            case CLKCLR_MINUTEHAND:
               cp.clrMinuteHand = GpiQueryRGBColor(hpsT, 0L, clrMinuteHandNew);
               break;
            }
            WinReleasePS(hpsT);

              /* repaint with the new colors */
            WinInvalidateRect(hwndFrame, NULL, TRUE);

         case DID_CANCEL:
            WinDismissDlg(hwnd, TRUE);
      }
      return NULL;

   case WM_CONTROL:
      usButtonID = SHORT1FROMMP(mp1);

      /* selecting a new object */
      if (usButtonID & CLKCLR_OBJECTS)
      {
         MRESULT rc;

         switch (usButtonID)
         {
            case CLKCLR_BACKGROUND:
               pclrNew = &clrBackgroundNew;
               break;
            case CLKCLR_FACE:
               pclrNew = &clrFaceNew;
               break;
            case CLKCLR_HOURHAND:
               pclrNew = &clrHourHandNew;
               break;
            case CLKCLR_MINUTEHAND:
               pclrNew = &clrMinuteHandNew;
         }
         usCheckedPartID = usButtonID;

          /* click the button for the new object's current color */
         rc = WinSendMsg(WinWindowFromID(hwnd,
                                         (CLKCLR_BUTTONSHIFT + *pclrNew)),
                                         BM_CLICK,
                                         MPFROMSHORT(TRUE), MPVOID);
         break;
      }

      switch (SHORT2FROMMP(mp1))
      {
      case BN_CLICKED:
         *pclrNew = (LONG)usButtonID - CLKCLR_BUTTONSHIFT;

           /* Turn off the check state of the previously checked
            * button and turn on the check state of the button
            * just clicked.
            */
         WinCheckButton(hwnd, usCheckedColorID, FALSE);
         WinCheckButton(hwnd, usButtonID, TRUE);
         usCheckedColorID = usButtonID;
         break;

      case BN_PAINT:

              /* Fill only the interior of the button, so we don't
               * conflict with the focus indicator */
         hwndButton = ((PUSERBUTTON) mp2) -> hwnd;
         WinQueryWindowRect(hwndButton, &rclButton);
         rclButton.xLeft++;
         rclButton.yBottom++;
         rclButton.xRight--;
         rclButton.yTop--;
         WinFillRect(((PUSERBUTTON) mp2)->hps,
                       &rclButton,
                       (LONG)usButtonID - CLKCLR_BUTTONSHIFT);

         /* Hollow out those buttons which aren't checked */
         if (!WinQueryButtonCheckstate(hwnd, usButtonID))
         {
            rclButtonInterior.xLeft   = rclButton.xLeft   + 4;
            rclButtonInterior.yBottom = rclButton.yBottom + 4;
            rclButtonInterior.xRight  = rclButton.xRight  - 4;
            rclButtonInterior.yTop    = rclButton.yTop    - 4;
            WinFillRect(((PUSERBUTTON)mp2)->hps,
                          &rclButtonInterior, SYSCLR_WINDOW);
         }
         break;
      }
      /* fall through to the default control processing */
   }
   return WinDefDlgProc(hwnd, usMsg, mp1, mp2);
}