home *** CD-ROM | disk | FTP | other *** search
/ gondwana.ecr.mu.oz.au/pub/ / Graphics.tar / Graphics / VOGLE.ZIP / VOGLE / DRIVERS / IBMPC / VOGLEPM.C < prev   
C/C++ Source or Header  |  2000-02-11  |  25KB  |  759 lines

  1. /****************************************************************************
  2.  
  3.     VoglePM.C - A VOGLE driver for OS/2 Presentation Manager
  4.     Version 1.0  (4/17/94)
  5.     (C) Copyright Claudio Fahey, 1994
  6.  
  7.     e-mail: claudio@uclink.berkeley.edu
  8.  
  9.     Permission granted to use, distribute, and modify, provided that this
  10.     notice remains intact.  If you distribute a modified version, you must
  11.     identify your modifications as such.
  12.  
  13. ****************************************************************************/
  14.  
  15. /****************************************************************************
  16.  
  17.     COMMENTS:
  18.  
  19.     This driver uses two threads.  The VOGLE application (the main() 
  20.     function), is the primary thread.  When it calls PM_init(), a second 
  21.     thread is started which deals with the PM window.  This allows the PM 
  22.     window to always be responsive, regardless of the status of the primary 
  23.     thread.  Unlike the MS-Windows driver, this driver won't lockup the system 
  24.     message queue if the primary thread doesn't call any of the PM_xxx 
  25.     functions for a long period of time.  Also, (and this is a very important 
  26.     point) the primary thread will continue to run and draw on the screen when 
  27.     the 2nd thread is showing the "About" window.  
  28.  
  29.     When the user closes the window using the System menu (the one in the 
  30.     upper left), it suspends and eventually kills the primary thread right 
  31.     where it is.  It doesn't wait until the primary thread calls a PM_xxx 
  32.     function like the MS-Windows driver does.  If you're running a program 
  33.     that shouldn't be killed at random points, then make sure you don't close 
  34.     it through the System menu (or the Window List).
  35.  
  36.     This driver can be closed and restarted by the VOGLE application as many 
  37.     times as needed, so printing to other drivers is supported.  However, the 
  38.     sample programs that print and then restart this driver (such as licosa 
  39.     and cube) don't work correctly in the new window.  This is because they 
  40.     perform their initialization (call backbuffer, set colors, etc.)  only 
  41.     once.  When the PM device is restarted, all these settings are lost.  I 
  42.     consider this a bug in the example programs so unless someone really needs 
  43.     this to work, I won't change this behavior.
  44.  
  45.     When the window is resized, the VOGLE device structure is updated with the
  46.     new window size.  However, VOGLE still clips itself to the original size.  
  47.     Unless someone wants me to fix this, I probably won't fix it anytime soon.  
  48.  
  49.     This driver uses a retained graphics segment to repaint itself on a 
  50.     WM_PAINT message.  To optimize double buffered animation, drawing in the 
  51.     back (Memory) buffer isn't retained.  Only drawing to the front (Screen) 
  52.     buffer is retained.  This isn't a problem for most animations, such as 
  53.     licosa and lcube, because the screen is constantly updated with 
  54.     PM_swapbuf.  An exception to this is the teapot example program because it 
  55.     only updates the screen after the user presses a key.  
  56.  
  57.     For details about how this driver works, just look at the code.  It is 
  58.     heavily commented so you should be able to easily follow it (at least I 
  59.     can :-) ).
  60.  
  61. ****************************************************************************/
  62.  
  63. #define INCL_GPI
  64. #define INCL_WIN
  65. #define INCL_DOS
  66. #include <os2.h>
  67. #include <stdlib.h>         // for min()
  68. #include <string.h>         // for strlen()
  69. #include "vogle.h"
  70.  
  71.  
  72. // Resource ID's
  73. #define RID_FRAME       1
  74. #define RID_ABOUTDLG    2
  75.  
  76. // Command ID's
  77. #define ID_ABOUT        1L
  78.  
  79. #define ID_SCREEN_SEG   1L
  80.  
  81. #define MAX_POLY        50      // Largest number of lines in a filled polygon
  82.  
  83. // Vogle interface functions
  84.  
  85. int PM_init( void );
  86. int PM_frontbuf( void );
  87. int PM_backbuf( void );
  88. int PM_swapbuf( void );
  89. int PM_sync( void );
  90. int PM_locator( int *x, int *y );
  91. int PM_string( char *szString );
  92. int PM_char( char c );
  93. int PM_setlw( int width );
  94. int PM_fill( int n, int *x, int *y );
  95. int PM_draw( int x, int y );
  96. int PM_color( int color );
  97. int PM_clear( void );
  98. int PM_font( char *font );
  99. int PM_getkey( void );
  100. int PM_checkkey( void );
  101. int PM_exit( void );
  102. int PM_verror( char *szString );
  103. int _PM_devcpy( void );
  104.  
  105. // PM user interface functions
  106. static MRESULT EXPENTRY ClientWinProc( HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2 );
  107. static void PMMain( void );
  108.  
  109. // Global variables
  110. static HAB hab;             // PM UI hab
  111. static HAB habPrim;         // vogle app (primary thread) hab
  112. static HMQ hmqPrim;         // vogle app (primary thread) message queue
  113. static HWND hwndFrame, hwndClient;
  114. static HDC hdcMemory;
  115. static HPS hpsMemory;       // Presentation space handle for the back buffer
  116. static HDC hdcScreen;
  117. static HPS hpsScreen;       // Presentation space handle for the screen
  118. static HPS hps;             // Current PS (hpsMemory or hpsScreen)
  119. static POINTL aptlBitBlt[3] = { 0, 0, 0, 0, 0, 0 };
  120.                             // Array used by GpiBitBlt
  121. static int NextKey;         // Key "buffer" - 0 when buffer is empty
  122. static BOOL flDontKillPrim; // TRUE when the 2nd thread shouldn't kill the
  123.                             // primary thread when closing the PM window
  124. static BOOL flSuspendPrim;  // TRUE when it's OK to suspend the primary thread
  125.                             // or when the primary thread should suspend itself
  126.                             // (see PM_verror)
  127.  
  128. // Semaphores
  129. static HEV hevChar;         // posted when client user types a key
  130. static HEV hevPMReady;      // posted when PM window is ready for vogle app
  131. static HEV hevPMDone;       // posted when PM window has been completely closed
  132. static HMTX hmtxPS;         // mutex semaphore for both PS's
  133.  
  134. // Macros for controlling access to the PS's from the two threads.
  135. // The 2nd thread must use the PS's when it receives WM_PAINT or WM_SIZE.
  136. #define REQUESTPS()     DosRequestMutexSem( hmtxPS, SEM_INDEFINITE_WAIT )
  137. #define RELEASEPS()     DosReleaseMutexSem( hmtxPS )
  138.  
  139. // *** Functions ***
  140.  
  141. // Drawing functions
  142.  
  143. // PM_frontbuf and PM_backbuf don't need to request the PS because they
  144. // only change hps which is only used by the primary thread
  145.  
  146. int PM_frontbuf( void )
  147.     {
  148.     hps = hpsScreen;
  149.     return 0;
  150.     }
  151.  
  152. int PM_backbuf( void )
  153.     {
  154.     hps = hpsMemory;
  155.     return 0;
  156.     }
  157.  
  158. // Copy Memory to Screen
  159. int PM_swapbuf( void )
  160.     {
  161.     REQUESTPS();
  162.  
  163.     // The screen will be "redrawn" so restart the screen segment
  164.     GpiDeleteSegment( hpsScreen, ID_SCREEN_SEG );
  165.     GpiOpenSegment( hpsScreen, ID_SCREEN_SEG );
  166.  
  167.     // Do the actual copying to the screen
  168.     GpiBitBlt( hpsScreen, hpsMemory, 3L, aptlBitBlt, ROP_SRCCOPY, BBO_IGNORE );
  169.  
  170.     RELEASEPS();
  171.     return 0;
  172.     }
  173.  
  174. int PM_clear( void )
  175.     {
  176.     // Set background color to foreground color
  177.     REQUESTPS();
  178.     GpiSetBackColor( hps, GpiQueryColor( hps ) );
  179.     GpiErase( hps );
  180.     if ( hps == hpsScreen )
  181.         {
  182.         // If the screen was cleared, we have to restart the graphics segment
  183.         GpiDeleteSegment( hps, ID_SCREEN_SEG );
  184.         GpiOpenSegment( hps, ID_SCREEN_SEG );
  185.         }
  186.     RELEASEPS();
  187.     return 0;
  188.     }
  189.  
  190. int PM_string( char *szString )
  191.     {
  192.     POINTL ptl;
  193.  
  194.     REQUESTPS();
  195.     ptl.x = vdevice.cpVx;
  196.     ptl.y = vdevice.cpVy;
  197.     GpiMove( hps, & ptl);
  198.     GpiCharString( hps, strlen(szString), szString );
  199.     RELEASEPS();
  200.     return 0;
  201.     }
  202.  
  203. int PM_char( char c )
  204.     {
  205.     char szString[] = " ";
  206.  
  207.     szString[0] = c;
  208.     return PM_string( szString );
  209.     }
  210.  
  211. int PM_setlw( int width )
  212.     {
  213.     REQUESTPS();
  214.     if ( width == 0 )   // thin
  215.         GpiSetLineWidth( hps, LINEWIDTH_NORMAL );
  216.     else
  217.         GpiSetLineWidth( hps, LINEWIDTH_THICK );
  218.     RELEASEPS();
  219.     return 0;
  220.     }
  221.  
  222. int PM_fill( int n, int *x, int *y )
  223.     {
  224.     POINTL aptl[MAX_POLY];
  225.     POINTL ptl;
  226.     POLYGON polygon;
  227.     int i;
  228.  
  229.     polygon.ulPoints = n - 1;
  230.     polygon.aPointl = aptl;
  231.  
  232.     ptl.x = x[0];
  233.     ptl.y = y[0];
  234.  
  235.     for ( i = 0 ; i < polygon.ulPoints && i < MAX_POLY ; i++ )
  236.         {
  237.         aptl[i].x = x[i + 1];
  238.         aptl[i].y = y[i + 1];
  239.         }
  240.  
  241.     REQUESTPS();
  242.     GpiMove( hps, &ptl );
  243.     GpiPolygons( hps, 1L, &polygon, 0L, 0L );
  244.     RELEASEPS();
  245.  
  246.     return 0;
  247.     }
  248.  
  249. int PM_draw( int x, int y )
  250.     {
  251.     POINTL ptl;
  252.  
  253.     REQUESTPS();
  254.     ptl.x = vdevice.cpVx;
  255.     ptl.y = vdevice.cpVy;
  256.     GpiMove( hps, & ptl);
  257.     ptl.x = x;
  258.     ptl.y = y;
  259.     GpiLine( hps, &ptl );
  260.     RELEASEPS();
  261.     return 0;
  262.     }
  263.  
  264. int PM_color( int color )
  265.     {
  266.     REQUESTPS();
  267.     GpiSetColor( hps, (LONG) color );
  268.     RELEASEPS();
  269.     return 0;
  270.     }
  271.  
  272. int PM_mapcolor( int i, int r, int g, int b )
  273.     {
  274.     LONG lColor[2];
  275.  
  276.     lColor[0] = i;
  277.     lColor[1] = r*65536 + g*256 + b;
  278.  
  279.     REQUESTPS();
  280.     // I assume the user would want to set the colors of both buffers
  281.     GpiCreateLogColorTable( hpsMemory, LCOL_PURECOLOR, LCOLF_INDRGB, 0L, 2, lColor );
  282.     GpiCreateLogColorTable( hpsScreen, LCOL_PURECOLOR, LCOLF_INDRGB, 0L, 2, lColor );
  283.     RELEASEPS();
  284.     return 0;
  285.     }
  286.  
  287. int PM_font( char *font )
  288.     {
  289.     // Not implemented, yet
  290.     return 1;
  291.     }
  292.  
  293. // Other vogle interface functions (non-drawing functions)
  294.  
  295. int PM_sync( void )
  296.     {
  297.     return -1;
  298.     }
  299.  
  300. int PM_locator( int *x, int *y )
  301.     {
  302.     POINTL ptl;
  303.     int iButtons = 0;
  304.  
  305.     // Get mouse position and convert to client coord. */
  306.     WinQueryPointerPos( HWND_DESKTOP, &ptl );
  307.     WinMapWindowPoints( HWND_DESKTOP, hwndClient, (PPOINTL) &ptl, 1 );
  308.     *x = ptl.x;
  309.     *y = ptl.y;
  310.  
  311.     if ( WinGetKeyState( HWND_DESKTOP, VK_BUTTON1 ) & 0x8000 )
  312.         iButtons |= 1;  // 001
  313.     if ( WinGetKeyState( HWND_DESKTOP, VK_BUTTON2 ) & 0x8000 )
  314.         iButtons |= 2;  // 010
  315.     if ( WinGetKeyState( HWND_DESKTOP, VK_BUTTON3 ) & 0x8000 )
  316.         iButtons |= 4;  // 100
  317.  
  318.     return iButtons;
  319.     }
  320.  
  321. int PM_getkey( void )
  322.     {
  323.     int Key;
  324.     ULONG cPost;
  325.  
  326.     // Wait for a semaphore.
  327.     //    If user presses a key, then WM_CHAR will set NextKey to ascii value
  328.     //    and then post the semaphore.
  329.  
  330.     DosWaitEventSem( hevChar, SEM_INDEFINITE_WAIT );
  331.     // Reset the sem because we are clearing the buffer
  332.     DosResetEventSem( hevChar, &cPost );
  333.     Key = NextKey;
  334.     NextKey = 0;                // Clear the "buffer"
  335.     return Key;
  336.     }
  337.  
  338. int PM_checkkey( void )
  339.     {
  340.     ULONG cPost;
  341.     int Key;
  342.  
  343.     // Reset the sem because we are clearing the buffer
  344.     DosResetEventSem( hevChar, &cPost );
  345.     Key = NextKey;
  346.     NextKey = 0;                // Clear the "buffer"
  347.     return Key;
  348.     }
  349.  
  350. int PM_verror( char *szString )
  351.     {
  352.     // Tell 2nd thread to not suspend this thread
  353.     flSuspendPrim = FALSE;
  354.  
  355.     if ( ! hmqPrim || ! habPrim )
  356.         {
  357.         // The message queue hasn't been created yet so we'll have to create it
  358.         // here before we give the user the error message.
  359.         habPrim = WinInitialize( 0L );
  360.         hmqPrim = WinCreateMsgQueue( habPrim, 0 );
  361.         WinMessageBox( HWND_DESKTOP, HWND_DESKTOP,  szString, "A VOGLE error has occurred",
  362.             0, MB_OK | MB_MOVEABLE | MB_ERROR );
  363.         WinDestroyMsgQueue( hmqPrim );
  364.         WinTerminate( habPrim );
  365.         hmqPrim = 0L;
  366.         habPrim = 0L;
  367.         }
  368.     else
  369.         {
  370.         // I can't make hwndClient the owner because if it's destroyed while in
  371.         // WinMessageBox, the program will crash (and possibly take OS/2 down
  372.         // with it).
  373.         WinMessageBox( HWND_DESKTOP, HWND_DESKTOP, szString, "A VOGLE error has occurred",
  374.             0, MB_OK | MB_MOVEABLE | MB_ERROR );
  375.         }
  376.  
  377.     if ( flSuspendPrim )
  378.         // If primary thread wanted to suspend this thread, then we'll
  379.         // suspend it now
  380.         DosSuspendThread( 1 );
  381.     else
  382.         // Tell 2nd thread that it's ok to suspend this thread
  383.         flSuspendPrim = TRUE;
  384.  
  385.     return 0;
  386.     }
  387.  
  388. int PM_exit( void )
  389.     {
  390.     // Tell 2nd thread to not kill primary thread when exiting
  391.     flDontKillPrim = TRUE;
  392.  
  393.     WinPostMsg( hwndClient, WM_QUIT, 0L, 0L );
  394.     WinDestroyMsgQueue( hmqPrim );
  395.     WinTerminate( habPrim );
  396.  
  397.     // Set pointers to null so PM_verror knows that a message queue doesn't exist
  398.     hmqPrim = 0L;
  399.     habPrim = 0L;
  400.  
  401.     DosWaitEventSem( hevPMDone, SEM_INDEFINITE_WAIT );
  402.     DosCloseEventSem( hevPMDone );
  403.  
  404.     return 1;
  405.     }
  406.  
  407. // This is the first function that is called by the vogle program.
  408. // This may be called several times from the same program (after a PM_exit).
  409. // Note: PM_verror may also be the first function.
  410. int PM_init( void )
  411.     {
  412.     TID tid;
  413.  
  414.     // Initialize PM usage for this primary thread
  415.     habPrim = WinInitialize( 0L );
  416.     hmqPrim = WinCreateMsgQueue( habPrim, 0 );
  417.  
  418.     // Initialize the ready and done semaphore and set them
  419.     DosCreateEventSem( 0L, &hevPMReady, 0L, FALSE );
  420.     DosCreateEventSem( 0L, &hevPMDone, 0L, FALSE );
  421.  
  422.     // Tell 2nd thread to kill primary thread if 2nd thread exits
  423.     flDontKillPrim = FALSE;
  424.  
  425.     // Tell 2nd thread that it's ok to suspend the primary thread
  426.     flSuspendPrim = TRUE;
  427.  
  428.     // Start the PM user interface thread. It doesn't use any C library
  429.     // functions so _beginthread doesn't need to be used.
  430.     DosCreateThread( &tid, (PFNTHREAD) PMMain, 0, 0, 8192 );
  431.  
  432.     // Wait for the PM UI process to startup completely
  433.     DosWaitEventSem( hevPMReady, SEM_INDEFINITE_WAIT );
  434.     DosCloseEventSem( hevPMReady );
  435.  
  436.     return 1;
  437.     }
  438.  
  439. // PM user interface functions
  440.  
  441. static void PMMain( void )
  442.     {
  443.     HMQ hmq;
  444.     QMSG qmsg;
  445.     ULONG flCreateFlags = FCF_TITLEBAR | FCF_SYSMENU | FCF_SIZEBORDER |
  446.                           FCF_MINMAX | FCF_TASKLIST | FCF_ICON;
  447.     static CHAR szClientClass[] = "Client Window";
  448.  
  449.     hab = WinInitialize( 0L );
  450.     hmq = WinCreateMsgQueue( hab, 0 );
  451.  
  452.     WinRegisterClass( hab, szClientClass, ClientWinProc, CS_SIZEREDRAW, 0 );
  453.  
  454.     // Create the client; this will *send* WM_CREATE
  455.     hwndFrame = WinCreateStdWindow( HWND_DESKTOP, WS_VISIBLE, &flCreateFlags,
  456.             szClientClass, "Vogle PM", 0L, 0L, RID_FRAME, &hwndClient );
  457.  
  458.     // *** Now I'm ready for the vogle app ***
  459.     // Post the ready semaphore to let PM_init continue
  460.     DosPostEventSem( hevPMReady );
  461.  
  462.     while ( WinGetMsg( hab, &qmsg, 0L, 0, 0 ) )
  463.         WinDispatchMsg( hab, &qmsg );
  464.  
  465.     // If the window was closed by this thread ...
  466.     // Suspend the primary thread so it doesn't use semaphores that will be closed,
  467.     // PS's that will be destroyed, or hwndClient which will be set to null.
  468.     // We can't kill it now because that will kill the whole process.
  469.     if ( ! flDontKillPrim )
  470.         {
  471.         if ( flSuspendPrim )
  472.             DosSuspendThread( 1 );
  473.         else
  474.             // If the primary thread can't be suspended now (ie: if it's handling
  475.             // the message queue), then tell it to suspend itself when it's done.
  476.             // Currently, this will only happen if the program is closed when
  477.             // PM_verror is running (showing the error message).
  478.             flSuspendPrim = TRUE;
  479.         }
  480.  
  481.     // At this point, the primary thread is no longer running (unless it is
  482.     // displaying an error message, in which case it'll suspend itself when
  483.     // it's done with the message).  It is either suspended or it is waiting
  484.     // for the PMDone semaphore.
  485.  
  486.     // Let PM_verror know that the client window is no longer usable
  487.     hwndClient = 0L;
  488.  
  489.     WinDestroyWindow( hwndFrame );
  490.     WinDestroyMsgQueue( hmq );
  491.     WinTerminate( hab );
  492.  
  493.     // If the window was closed by this thread then the whole process
  494.     // should be killed.
  495.     // If the window was closed by the vogle app, then I should post a
  496.     // semaphore, telling PM_exit to return to the vogle app.
  497.     if ( ! flDontKillPrim )
  498.         DosExit( 1, 99 );   // Kill process and return error level 99
  499.     else
  500.         DosPostEventSem( hevPMDone );
  501.     }
  502.  
  503. static MRESULT EXPENTRY ClientWinProc( HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2 )
  504.     {
  505.     static HBITMAP hbm = 0L;
  506.     HPS hpsPaint;
  507.     HBITMAP hbmOld;
  508.     SIZEL sizl = {0,0};
  509.     LONG lFormats[24];
  510.     BITMAPINFO2 bmInfo;
  511.     DEVOPENSTRUC dop = {0L, "DISPLAY", NULL, 0L, 0L, 0L, 0L, 0L, 0L};
  512.     BOOL flRc;
  513.     RECTL rcl;
  514.     SWP swpDefault;
  515.     int x, y, cx, cy, cxFrame, cyFrame, cxDefault, cyDefault;
  516.     APIRET rc;
  517.     LONG alDefaultColors[8] = { RGB_BLACK, RGB_RED, RGB_GREEN, RGB_YELLOW,
  518.                                 RGB_BLUE, RGB_PINK, RGB_CYAN, RGB_WHITE };
  519.     HWND hwndSysMenu;
  520.     MENUITEM MenuItem = { MIT_END, MIS_TEXT, 0L, ID_ABOUT, 0L, 1L };
  521.  
  522.     switch( msg )
  523.         {
  524.         case WM_CHAR:
  525.             if ( ! (SHORT1FROMMP(mp1) & KC_KEYUP) && (SHORT1FROMMP(mp1) & KC_CHAR) )
  526.                 {
  527.                 NextKey = SHORT1FROMMP(mp2);    // Put key in our "buffer"
  528.                 DosPostEventSem( hevChar );     // Tell PM_getkey to continue
  529.                 }
  530.             else if ( ! (SHORT1FROMMP(mp1) & KC_KEYUP) && (SHORT1FROMMP(mp1) & KC_VIRTUALKEY) )
  531.                 {
  532.                 if ( SHORT2FROMMP(mp2) == VK_ESC )
  533.                     {
  534.                     NextKey = 27;                   // Put key in our "buffer"
  535.                     DosPostEventSem( hevChar );     // Tell PM_getkey to continue
  536.                     }
  537.                 }
  538.             break;
  539.  
  540.         case WM_SIZE:
  541.             REQUESTPS();
  542.  
  543.             cx = SHORT1FROMMP(mp2);
  544.             cy = SHORT2FROMMP(mp2);
  545.  
  546.             // Create and use a device compatible bitmap for double buffering
  547.  
  548.             GpiQueryDeviceBitmapFormats( hpsScreen, 24L, lFormats );
  549.             bmInfo.cbFix = 16L;
  550.             bmInfo.cPlanes = (USHORT) lFormats[0];
  551.             bmInfo.cBitCount = (USHORT) lFormats[1];
  552.             bmInfo.cx = cx;
  553.             bmInfo.cy = cy;
  554.  
  555.             hbm = GpiCreateBitmap( hpsMemory, (PBITMAPINFOHEADER2) &bmInfo, 0L, NULL, NULL );
  556.             hbmOld = GpiSetBitmap( hpsMemory, hbm );
  557.  
  558.             if ( hbmOld )
  559.                 GpiDeleteBitmap( hbmOld );
  560.  
  561.             // set bitblt region
  562.             aptlBitBlt[1].x = cx;
  563.             aptlBitBlt[1].y = cy;
  564.  
  565.             // Set vogle device parameters
  566.             vdevice.sizeX = vdevice.sizeY = min(cx, cy);
  567.             vdevice.sizeSx = cx;
  568.             vdevice.sizeSy = cy;
  569.             vdevice.depth = lFormats[0] * lFormats[1];
  570.  
  571.             RELEASEPS();
  572.             break;
  573.  
  574.         case WM_PAINT:
  575.             REQUESTPS();
  576.             hpsPaint = WinBeginPaint( hwnd, hpsScreen, NULL );
  577.             GpiErase( hpsScreen );
  578.             GpiDrawSegment( hpsScreen, ID_SCREEN_SEG );
  579.             WinEndPaint( hpsPaint );
  580.             RELEASEPS();
  581.             break;
  582.  
  583.         case WM_COMMAND:
  584.             switch ( SHORT1FROMMP(mp1) )
  585.                 {
  586.                 case ID_ABOUT:
  587.                     WinDlgBox( HWND_DESKTOP, hwndFrame, WinDefDlgProc, NULLHANDLE, RID_ABOUTDLG, NULL );
  588.                     break;
  589.                 }
  590.             break;
  591.  
  592.         case WM_CREATE:
  593.  
  594.             // Initialize Memory DC and PS
  595.             hdcMemory = DevOpenDC( hab, OD_MEMORY, "*", 5L, (PDEVOPENDATA)&dop, NULLHANDLE );
  596.             hpsMemory = GpiCreatePS( hab, hdcMemory, &sizl, GPIT_MICRO | GPIA_ASSOC | PU_PELS );
  597.  
  598.             // Initialize Screen DC and PS
  599.             hdcScreen = WinOpenWindowDC( hwnd );
  600.             hpsScreen = GpiCreatePS( hab, hdcScreen, &sizl, GPIT_NORMAL | GPIA_ASSOC | PU_PELS );
  601.             GpiSetDrawingMode( hpsScreen, DM_DRAWANDRETAIN );
  602.             GpiOpenSegment( hpsScreen, ID_SCREEN_SEG );
  603.  
  604.             // Initialize the color tables for the PS's
  605.             GpiCreateLogColorTable( hpsMemory, LCOL_PURECOLOR, LCOLF_CONSECRGB,
  606.                                     0L, 8, alDefaultColors );
  607.             GpiCreateLogColorTable( hpsScreen, LCOL_PURECOLOR, LCOLF_CONSECRGB,
  608.                                     0L, 8, alDefaultColors );
  609.  
  610.             // Set the default PS to the screen
  611.             hps = hpsScreen;
  612.  
  613.             // Clear the keyboard "buffer"
  614.             NextKey = 0;
  615.  
  616.             // Initialize the semaphores
  617.             DosCreateEventSem( 0L, &hevChar, 0L, FALSE );   // initially set
  618.             DosCreateMutexSem( 0L, &hmtxPS, 0L, FALSE );    // initially unowned
  619.  
  620.             // Get frame handle so we can add the "About" menu item
  621.             // and set the frame's position and size
  622.             hwndFrame = WinQueryWindow( hwnd, QW_PARENT );
  623.  
  624.             // *** Add "About" menu item to the system menu ***
  625.  
  626.             // Get window handle of for system menu
  627.             hwndSysMenu = WinWindowFromID( hwndFrame, FID_SYSMENU );
  628.             WinSendMsg( hwndSysMenu, MM_QUERYITEM, MPFROM2SHORT(SC_SYSMENU, TRUE), (MPARAM) &MenuItem );
  629.             hwndSysMenu = MenuItem.hwndSubMenu;
  630.  
  631.             // Add separator
  632.             MenuItem.iPosition = MIT_END;
  633.             MenuItem.afStyle = MIS_SEPARATOR;
  634.             MenuItem.afAttribute = 0;
  635.             MenuItem.id = 0;
  636.             MenuItem.hwndSubMenu = 0;
  637.             MenuItem.hItem = 0;
  638.             WinSendMsg( hwndSysMenu, MM_INSERTITEM, (MPARAM) &MenuItem, NULL );
  639.  
  640.             // Add "About" item
  641.             MenuItem.afStyle = MIS_TEXT;
  642.             MenuItem.id = ID_ABOUT;
  643.             WinSendMsg( hwndSysMenu, MM_INSERTITEM, (MPARAM) &MenuItem,
  644.                 (MPARAM) "~About" );
  645.  
  646.             // *** Initialize the size and position of window ***
  647.  
  648.             // This is the prefered size from the user's program
  649.             // x,y refer to the position of the frame window
  650.             // cx,cy refer to the size of the client window
  651.             getprefposandsize( &x, &y, &cx, &cy );
  652.             
  653.             // Get the size, position, and status recommended by OS/2
  654.             WinQueryTaskSizePos( hab, 0, &swpDefault );
  655.             
  656.             // Calculate the client window size from the recommended frame size
  657.             rcl.xLeft = rcl.yBottom = 0;
  658.             rcl.xRight = swpDefault.cx;
  659.             rcl.yTop = swpDefault.cy;
  660.             WinCalcFrameRect( hwndFrame, &rcl, TRUE );
  661.             cxDefault = rcl.xRight - rcl.xLeft;
  662.             cyDefault = rcl.yTop - rcl.yBottom;
  663.             
  664.             // Vogle likes square windows for some reason so the default client
  665.             // window size will be a square whose side length is the minimum of
  666.             // the recommended lengths.
  667.             cxDefault = cyDefault = min(cxDefault,cyDefault);
  668.             
  669.             // Now set x,y for the frame window and cx,cy for the client window if
  670.             // they're not valid from getprefposandsize().
  671.             if ( x < 0 )
  672.                 x = swpDefault.x;
  673.             if ( y < 0 )
  674.                 y = swpDefault.y;
  675.             if ( cx < 0 )
  676.                 cx = cxDefault;
  677.             if ( cy < 0 )
  678.                 cy = cyDefault;
  679.             
  680.             // Now create a "dummy" rectangle - just the size is important.
  681.             // Then we'll calculate the required size of the frame that will contain
  682.             // the client window of size cx,cy.
  683.             rcl.xLeft = rcl.yBottom = 0;
  684.             rcl.xRight = cx;
  685.             rcl.yTop = cy;
  686.             WinCalcFrameRect( hwndFrame, &rcl, FALSE );
  687.             cxFrame = rcl.xRight - rcl.xLeft;
  688.             cyFrame = rcl.yTop - rcl.yBottom;
  689.             
  690.             // now resize (and maybe activate) the frame - this also resizes the client
  691.             WinSetWindowPos( hwndFrame, HWND_TOP, x, y, cxFrame, cyFrame, swpDefault.fl );
  692.  
  693.             break;
  694.  
  695.         case WM_DESTROY:
  696.             // Destroy everything created in WM_CREATE
  697.  
  698.             // NOTE: If this window is being closed from this thread, then the
  699.             // semaphores, PS's, and bitmaps may be busy and may not be deleted,
  700.             // closed, or destroyed.  (ie: hevChar would be
  701.             // busy if the vogle app were waiting for a character when the user
  702.             // closed the PM window.)  However, it doesn't matter because the
  703.             // whole process will terminate.
  704.  
  705.             // Destroy the semaphores
  706.             DosCloseEventSem( hevChar );
  707.             DosCloseEventSem( hmtxPS );
  708.  
  709.             // Destroy the DC's and PS's
  710.             GpiAssociate( hpsMemory, 0L );
  711.             GpiDestroyPS( hpsMemory );
  712.             GpiAssociate( hpsScreen, 0L );
  713.             GpiDestroyPS( hpsScreen );
  714.  
  715.             // Destroy the bitmap if it exists
  716.             if ( hbm )
  717.                 GpiDeleteBitmap( hbm );
  718.  
  719.             break;
  720.  
  721.         default:
  722.             return WinDefWindowProc( hwnd, msg, mp1, mp2 );
  723.         }
  724.     return NULL;
  725.     }
  726.  
  727. // Device entry for Vogle
  728. static DevEntry PMDev =
  729.     {
  730.     "os2pm",
  731.     "large",
  732.     "small",
  733.     PM_backbuf,
  734.     PM_char,
  735.     PM_checkkey,
  736.     PM_clear,
  737.     PM_color,
  738.     PM_draw,
  739.     PM_exit,
  740.     PM_fill,
  741.     PM_font,
  742.     PM_frontbuf,
  743.     PM_getkey,
  744.     PM_init,
  745.     PM_locator,
  746.     PM_mapcolor,
  747.     PM_setlw,
  748.     PM_string,
  749.     PM_swapbuf,
  750.     PM_sync
  751.     };
  752.  
  753. int _PM_devcpy()
  754.     {
  755.     vdevice.dev = PMDev;
  756.     return 0;
  757.     }
  758.  
  759.