home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / os2plug.exe / SAMPLE / NPSHELL / npshell.cpp < prev    next >
Text File  |  1996-11-13  |  22KB  |  633 lines

  1. /***************************************************************************
  2.  *
  3.  * File name   :  npshell.cpp
  4.  *
  5.  *  Copyright (C) 1996 IBM Corporation
  6.  *
  7.  *      DISCLAIMER OF WARRANTIES.  The following [enclosed] code is
  8.  *      sample code created by IBM Corporation. This sample code is not
  9.  *      part of any standard or IBM product and is provided to you solely
  10.  *      for  the purpose of assisting you in the development of your
  11.  *      applications.  The code is provided "AS IS", without
  12.  *      warranty of any kind.  IBM shall not be liable for any damages
  13.  *      arising out of your use of the sample code, even if they have been
  14.  *      advised of the possibility of such damages.
  15.  *
  16.  * This file defines a "shell" plugin that plugin developers can use
  17.  * as the basis for a real plugin.  This shell just provides empty
  18.  * implementations of all functions that the plugin can implement
  19.  * that will be called by Netscape (the NPP_xxx methods defined in
  20.  * npapi.h).
  21.  *
  22.  ***************************************************************************/
  23.  
  24. #define INCL_WIN
  25. #define INCL_DOSPROCESS
  26. #define INCL_GPI
  27. #include <os2.h>
  28.  
  29. #include <string.h>
  30. #include <stdlib.h>
  31.  
  32. #ifndef _NPAPI_H_
  33. #include "npapi.h"
  34. #endif
  35.  
  36. //
  37. // Instance state information about the plugin.
  38. //
  39. // *Developers*: Use this struct to hold per-instance
  40. //               information that you'll need in the
  41. //               various functions in this file.
  42. //
  43.  
  44. typedef struct _PluginInstance PluginInstance;
  45. typedef struct _PluginInstance
  46. {
  47.     NPWindow*       fWindow;
  48.     HWND            hWnd;
  49.     uint16          fMode;
  50.     PFNWP           lpfnOldWndProc;
  51.     NPSavedData*    pSavedInstanceData;
  52.     PluginInstance* pNext;
  53. } PluginInstance;
  54.  
  55. MRESULT APIENTRY
  56. SubClassFunc(HWND hWnd,ULONG Message,MPARAM wParam, MPARAM lParam);
  57.  
  58. void Draw(PluginInstance *This, HPS hps, POINTL *endPoint, BOOL fPrinting);
  59.  
  60. HMODULE DLLInstance;
  61.  
  62. extern "C" {
  63.  #if defined ( __cplusplus )
  64.    void __ctordtorInit(void);
  65.    void __ctordtorTerm(void);
  66.  #endif
  67. }
  68.  
  69. /* _CRT_init is the C run-time environment initialization function.         */
  70. /* It will return 0 to indicate success and -1 to indicate failure.         */
  71. extern "C"
  72. int _CRT_init(void);
  73.  
  74. #ifdef   STATIC_LINK
  75. /* _CRT_term is the C run-time environment termination function.            */
  76. /* It only needs to be called when the C run-time functions are statically  */
  77. /* linked.                                                                  */
  78. extern "C"
  79. void _CRT_term(void);
  80.  
  81. #else
  82.  
  83. /* A clean up routine registered with DosExitList must be used if runtime   */
  84. /* calls are required at exit AND the runtime is dynamically linked.  This  */
  85. /* will guarantee that this clean up routine is run before the library DLL  */
  86. /* is terminated.                                                           */
  87.  
  88. static void _System cleanup(ULONG ulReason);
  89. #endif
  90.  
  91.  
  92. /* __ctordtorInit is the C++ run-time environment initialization function.  */
  93. /* It is required to initialize static objects and destructors are setup    */
  94. extern "C"
  95.    void __ctordtorInit(void);
  96.  
  97.  
  98. /* __ctordtorInit is the C++ run-time environment initialization function.  */
  99. /* It is required to insure static objects destructors are run              */
  100. /* You must call this before calling _CRT_term()                            */
  101. extern "C"
  102.    void __ctordtorInit(void);
  103.  
  104. extern "C"
  105. unsigned long _System _DLL_InitTerm(unsigned long hModule, unsigned long
  106.                                     ulFlag)
  107. {
  108.     DLLInstance = (HMODULE) hModule;
  109.     switch (ulFlag)
  110.     {
  111.         case 0:
  112.             if ( _CRT_init() == -1 )
  113.             {
  114.                 return(0UL);
  115.             }
  116. #if defined ( __cplusplus )
  117.             __ctordtorInit();
  118. #endif
  119.  
  120. #ifndef  STATIC_LINK
  121.  
  122.          /*******************************************************************/
  123.          /* A DosExitList routine must be used to clean up if runtime calls */
  124.          /* are required at exit and the runtime is dynamically linked.     */
  125.          /*******************************************************************/
  126.  
  127.             DosExitList(0x0000FF00|EXLST_ADD, cleanup);
  128. #endif
  129.             break;
  130.         case 1:
  131.  
  132. #if defined ( __cplusplus )
  133.             __ctordtorTerm();
  134. #endif
  135.  
  136. #ifdef  STATIC_LINK
  137.             _CRT_term();
  138. #endif
  139.             break;
  140.     }
  141.  
  142.     return 1;
  143. }
  144.  
  145. #ifndef  STATIC_LINK
  146. static void cleanup(ULONG ulReason)
  147. {
  148.    /* do any DLL cleanup here if needed AND if dynamically linked to the */
  149.    /* C Runtime libraries                                                */
  150.    DosExitList(EXLST_EXIT, cleanup);
  151.    return ;
  152. }
  153. #endif
  154.  
  155. // A plugin instance typically will subclass the plugin's client window, so
  156. // it can get Windows messages, (such as paint, palettechanged, keybd, etc).
  157. // To do work associated with a specific plugin instance the WndProc which
  158. // receives the Windows messages, (named "SubClassFunc" herein), needs access
  159. // to the "This" (PluginInstance*) ptr.
  160.  
  161. //* OS2 bugbug -- we need to reserve 4 bytes of window words to correct this
  162. //  gorp.....
  163.  
  164. // When Navigator registers the plugin client's window class, (the class for
  165. // the window passed in NPP_SetWindow()), Navigator does not reserve any
  166. // "extra" windows bytes.  If it had, the plugin could simply have stored its
  167. // "This" (PluginInstance*) ptr in the extra bytes.  But Nav did not, and the
  168. // plugin cannot, so a different technique must be used.  The technique used
  169. // is to keep a linked list of PluginInstance structures, and walk the list
  170. // to find which one is associated with the window handle.  Inefficient,
  171. // grungy, complicates the code, etc.  C'est la vie ...
  172.  
  173. PluginInstance* g_pHeadInstanceList = 0;
  174.  
  175. // Associate the hWnd with pInstance by setting the hWnd member of the
  176. // PluginInstance struct.  Also, add the PluginInstance struct to the list
  177. // if necessary
  178. static void AssociateInstance(HWND hWnd, PluginInstance* pInstance)
  179. {
  180.     pInstance->hWnd = hWnd;     // redundant, but usefull to get hwnd from
  181.                                 // pinstance later.
  182.     BOOL rc = WinSetWindowULong(hWnd, QWL_USER, (ULONG)pInstance);
  183. }
  184.  
  185. // Find the PluginInstance associated with this hWnd and return it
  186. static PluginInstance* GetInstance(HWND hWnd)
  187. {
  188.     return (PluginInstance*)WinQueryWindowULong(hWnd, QWL_USER);
  189. }
  190.  
  191. //----------------------------------------------------------------------------
  192. // NPP_Initialize:
  193. //----------------------------------------------------------------------------
  194. NPError NPP_Initialize(void)
  195. {
  196.     // do your one time initialization here, such as dynamically loading
  197.     // dependant DLLs
  198.     return NPERR_NO_ERROR;
  199. }
  200.  
  201.  
  202. //----------------------------------------------------------------------------
  203. // NPP_Shutdown:
  204. //----------------------------------------------------------------------------
  205. void NPP_Shutdown(void)
  206. {
  207.     // do your one time uninitialization here, such as unloading dynamically
  208.     // loaded DLLs
  209. }
  210.  
  211.  
  212. //----------------------------------------------------------------------------
  213. // NPP_New:
  214. //----------------------------------------------------------------------------
  215. NPError NP_LOADDS
  216. NPP_New(NPMIMEType pluginType,
  217.                 NPP instance,
  218.                 uint16 mode,
  219.                 int16 argc,
  220.                 char* argn[],
  221.                 char* argv[],
  222.                 NPSavedData* saved)
  223. {
  224.     if (instance == NULL)
  225.         return NPERR_INVALID_INSTANCE_ERROR;
  226.  
  227.     instance->pdata = NPN_MemAlloc(sizeof(PluginInstance));
  228.     PluginInstance* This = (PluginInstance*) instance->pdata;
  229.  
  230.     if (This == NULL)
  231.         return NPERR_OUT_OF_MEMORY_ERROR;
  232.     //
  233.     // *Developers*: Initialize fields of your plugin
  234.     // instance data here.  If the NPSavedData is non-
  235.     // NULL, you can use that data (returned by you from
  236.     // NPP_Destroy to set up the new plugin instance.
  237.     //
  238.  
  239.     This->fWindow = 0;
  240.     // mode is NP_EMBED, NP_FULL, or NP_BACKGROUND (see npapi.h)
  241.     This->fMode = mode;
  242.     This->hWnd = 0;
  243.     This->pSavedInstanceData = saved;
  244.     This->pNext = 0   ;
  245.  
  246.     return NPERR_NO_ERROR;
  247. }
  248.  
  249.  
  250. //-----------------------------------------------------------------------------
  251. // NPP_Destroy:
  252. //-----------------------------------------------------------------------------
  253. NPError NP_LOADDS
  254. NPP_Destroy(NPP instance, NPSavedData** save)
  255. {
  256.     if (instance == 0   )
  257.         return NPERR_INVALID_INSTANCE_ERROR;
  258.  
  259.     PluginInstance* This = (PluginInstance*) instance->pdata;
  260.  
  261.     //
  262.     // *Developers*: If desired, call NP_MemAlloc to create a
  263.     // NPSavedDate structure containing any state information
  264.     // that you want restored if this plugin instance is later
  265.     // recreated.
  266.     //
  267.  
  268.     if (This != 0   )
  269.     {
  270.         // Remove the subclass for the client window
  271.         if(This->hWnd)
  272.         {
  273.             WinSubclassWindow(This->hWnd, This->lpfnOldWndProc);
  274.         }
  275.  
  276.         // make some saved instance data if necessary
  277.         if(This->pSavedInstanceData == 0   ) {
  278.             // make a struct header for the data
  279.             This->pSavedInstanceData =
  280.                 (NPSavedData*)NPN_MemAlloc(sizeof (struct _NPSavedData));
  281.  
  282.             // fill in the struct
  283.             if(This->pSavedInstanceData != 0   ) {
  284.                 This->pSavedInstanceData->len = 0;
  285.                 This->pSavedInstanceData->buf = 0   ;
  286.  
  287.                 // replace the def below and references to it with your data
  288.                 #define SIDATA "aSavedInstanceDataBlock"
  289.  
  290.                 // the data
  291.                 This->pSavedInstanceData->buf = NPN_MemAlloc(sizeof SIDATA);
  292.  
  293.                 if(This->pSavedInstanceData->buf != 0   ) {
  294.                     strcpy((char*)This->pSavedInstanceData->buf, SIDATA);
  295.                     This->pSavedInstanceData->len = sizeof SIDATA;
  296.                 }
  297.             }
  298.         }
  299.  
  300.         // save some instance data
  301.         *save = This->pSavedInstanceData;
  302.  
  303.         NPN_MemFree(instance->pdata);
  304.         instance->pdata = 0   ;
  305.     }
  306.  
  307.     return NPERR_NO_ERROR;
  308. }
  309.  
  310.  
  311. //----------------------------------------------------------------------------
  312. // NPP_SetWindow:
  313. //----------------------------------------------------------------------------
  314. NPError NP_LOADDS
  315. NPP_SetWindow(NPP instance, NPWindow* window)
  316. {
  317.     if (instance == 0   )
  318.         return NPERR_INVALID_INSTANCE_ERROR;
  319.  
  320.     PluginInstance* This = (PluginInstance*) instance->pdata;
  321.  
  322.     //
  323.     // *Developers*: Before setting fWindow to point to the
  324.     // new window, you may wish to compare the new window
  325.     // info to the previous window (if any) to note window
  326.     // size changes, etc.
  327.     //
  328.     if((window->window != 0   ) && (This->hWnd == 0   ))
  329.     {
  330.         This->fWindow = window;
  331.         This->hWnd    = (HWND)This->fWindow->window;
  332.  
  333.         // subclass the window
  334.         This->lpfnOldWndProc = WinSubclassWindow(This->hWnd, SubClassFunc);
  335.         AssociateInstance(This->hWnd, This);
  336.     }
  337.     else {
  338.         // if window handle changed
  339.         if(This->hWnd != (HWND)window->window) {
  340.             // remember the new window
  341.             This->fWindow = window;
  342.  
  343.             // Remove the subclass for the old client window
  344.             WinSubclassWindow(This->hWnd, This->lpfnOldWndProc);
  345.  
  346.             // remember the new window handle
  347.             This->hWnd = (HWND)This->fWindow->window;
  348.  
  349.             if(This->hWnd != 0   ) {
  350.                 // subclass the new one
  351.                 This->lpfnOldWndProc = WinSubclassWindow(This->hWnd,
  352.                                                          SubClassFunc);
  353.                 AssociateInstance(This->hWnd, This);
  354.             }
  355.         }
  356.     }
  357.  
  358.     return NPERR_NO_ERROR;
  359. }
  360.  
  361.  
  362. //----------------------------------------------------------------------------
  363. // NPP_NewStream:
  364. //----------------------------------------------------------------------------
  365. NPError NP_LOADDS
  366. NPP_NewStream(NPP instance,
  367.               NPMIMEType type,
  368.               NPStream *stream,
  369.               NPBool seekable,
  370.               uint16 *stype)
  371. {
  372.     if (instance == 0   )
  373.         return NPERR_INVALID_INSTANCE_ERROR;
  374.     PluginInstance* This = (PluginInstance*) instance->pdata;
  375.  
  376.     // if your plugin must operate file based, you may wish to do this:
  377.     //    *stype = NP_ASFILE;
  378.     // remember, though, that use of NP_ASFILE is strongly discouraged;
  379.     // your plugin should attempt to work with data as it comes in on
  380.     // the stream if at all possible
  381.  
  382.     return NPERR_NO_ERROR;
  383. }
  384.  
  385.  
  386. //
  387. // *Developers*:
  388. // These next 2 functions are directly relevant in a plug-in which handles the
  389. // data in a streaming manner.  If you want zero bytes because no buffer space
  390. // is YET available, return 0.  As long as the stream has not been written
  391. // to the plugin, Navigator will continue trying to send bytes.  If the plugin
  392. // doesn't want them, just return some large number from NPP_WriteReady(), and
  393. // ignore them in NPP_Write().  For a NP_ASFILE stream, they are still called
  394. // but can safely be ignored using this strategy.
  395. //
  396.  
  397. int32 STREAMBUFSIZE = 0X0FFFFFFF;   // If we are reading from a file in
  398.                                     // NP_ASFILE mode, we can take any size
  399.                                     // stream in our write call (since we
  400.                                     // ignore it)
  401.  
  402. //----------------------------------------------------------------------------
  403. // NPP_WriteReady:
  404. //----------------------------------------------------------------------------
  405. int32 NP_LOADDS
  406. NPP_WriteReady(NPP instance, NPStream *stream)
  407. {
  408.     if (instance != 0   )
  409.         PluginInstance* This = (PluginInstance*) instance->pdata;
  410.  
  411.     return STREAMBUFSIZE;   // Number of bytes ready to accept in NPP_Write()
  412. }
  413.  
  414.  
  415. //----------------------------------------------------------------------------
  416. // NPP_Write:
  417. //----------------------------------------------------------------------------
  418. int32 NP_LOADDS
  419. NPP_Write(NPP instance, NPStream *stream,
  420.           int32 offset, int32 len, void *buffer)
  421. {
  422.     if (instance != 0   )
  423.         PluginInstance* This = (PluginInstance*) instance->pdata;
  424.  
  425.     return len;     // The number of bytes accepted.  Return a
  426.                     // negative number here if, e.g., there was an error
  427.                     // during plugin operation and you want to abort the
  428.                     // stream
  429. }
  430.  
  431.  
  432. //----------------------------------------------------------------------------
  433. // NPP_DestroyStream:
  434. //----------------------------------------------------------------------------
  435. NPError NP_LOADDS
  436. NPP_DestroyStream(NPP instance, NPStream *stream, NPError reason)
  437. {
  438.     if (instance == 0   )
  439.         return NPERR_INVALID_INSTANCE_ERROR;
  440.     PluginInstance* This = (PluginInstance*) instance->pdata;
  441.  
  442.     return NPERR_NO_ERROR;
  443. }
  444.  
  445.  
  446. //----------------------------------------------------------------------------
  447. // NPP_StreamAsFile:
  448. //----------------------------------------------------------------------------
  449. void NP_LOADDS
  450. NPP_StreamAsFile(NPP instance, NPStream *stream, const char* fname)
  451. {
  452.    if (instance == 0   )
  453.        return;
  454.  
  455.    PluginInstance* This = (PluginInstance*) instance->pdata;
  456.  
  457.    // invalidate window to ensure a redraw
  458.    WinInvalidateRect(This->hWnd, 0, TRUE);
  459. }
  460.  
  461.  
  462. //----------------------------------------------------------------------------
  463. // NPP_Print:
  464. //----------------------------------------------------------------------------
  465. void NP_LOADDS
  466. NPP_Print(NPP instance, NPPrint* printInfo)
  467. {
  468.     if(printInfo == 0   )   // trap invalid parm
  469.         return;
  470.  
  471.     if (instance != 0   )
  472.     {
  473.         PluginInstance* This = (PluginInstance*) instance->pdata;
  474.  
  475.         if (printInfo->mode == NP_FULL)
  476.         {
  477.             //
  478.             // *Developers*: If your plugin would like to take over
  479.             // printing completely when it is in full-screen mode,
  480.             // set printInfo->pluginPrinted to TRUE and print your
  481.             // plugin as you see fit.  If your plugin wants Netscape
  482.             // to handle printing in this case, set printInfo->pluginPrinted
  483.             // to FALSE (the default) and do nothing.  If you do want
  484.             // to handle printing yourself, printOne is true if the
  485.             // print button (as opposed to the print menu) was clicked.
  486.             // On the Macintosh, platformPrint is a THPrint; on Windows,
  487.             // platformPrint is a structure (defined in npapi.h) containing
  488.             // the printer name, port, etc.
  489.             //
  490.             void* platformPrint = printInfo->print.fullPrint.platformPrint;
  491.             NPBool printOne = printInfo->print.fullPrint.printOne;
  492.  
  493.             printInfo->print.fullPrint.pluginPrinted = FALSE; // Do the default
  494.  
  495.         }
  496.         else    // If not fullscreen, we must be embedded
  497.         {
  498.             //
  499.             // *Developers*: If your plugin is embedded, or is full-screen
  500.             // but you returned false in pluginPrinted above, NPP_Print
  501.             // will be called with mode == NP_EMBED.  The NPWindow
  502.             // in the printInfo gives the location and dimensions of
  503.             // the embedded plugin on the printed page.  On the Macintosh,
  504.             // platformPrint is the printer port; on Windows, platformPrint
  505.             // is the handle to the printing device context. On OS/2,
  506.             // platformPrint is the printing presentation space (HPS).
  507.             //
  508.             NPWindow* printWindow = &(printInfo->print.embedPrint.window);
  509.  
  510.             /* get Presentation Space */
  511.             void* platformPrint = printInfo->print.embedPrint.platformPrint;
  512.             HPS hps = (HPS)platformPrint;
  513.  
  514.             /* create GPI various data structures about the drawing area */
  515.             POINTL offWindow = { (int)printWindow->x, (int)printWindow->y };
  516.             POINTL endPoint = { (int)printWindow->width, (int)printWindow->height };
  517.             RECTL rect = { (int)printWindow->x,
  518.                            (int)printWindow->y,
  519.                            (int)printWindow->x + (int)printWindow->width,
  520.                            (int)printWindow->y + (int)printWindow->height };
  521.  
  522.             /* set model transform so origin is 0,0 */
  523.             MATRIXLF matModel;
  524.             GpiQueryModelTransformMatrix(hps, 9L, &matModel);
  525.             GpiTranslate(hps, &matModel, TRANSFORM_ADD, &offWindow);
  526.             GpiSetModelTransformMatrix(hps, 9L, &matModel, TRANSFORM_REPLACE);
  527.  
  528.             /* draw using common drawing routine */
  529.             Draw(This, hps, &endPoint, TRUE);
  530.         }
  531.     }
  532. }
  533.  
  534.  
  535. //----------------------------------------------------------------------------
  536. // NPP_HandleEvent:
  537. // Mac-only.
  538. //----------------------------------------------------------------------------
  539. int16 NP_LOADDS NPP_HandleEvent(NPP instance, void* event)
  540. {
  541.     NPBool eventHandled = FALSE;
  542.     if (instance == 0   )
  543.         return eventHandled;
  544.  
  545.     PluginInstance* This = (PluginInstance*) instance->pdata;
  546.  
  547.     //
  548.     // *Developers*: The "event" passed in is a Macintosh
  549.     // EventRecord*.  The event.what field can be any of the
  550.     // normal Mac event types, or one of the following additional
  551.     // types defined in npapi.h: getFocusEvent, loseFocusEvent,
  552.     // adjustCursorEvent.  The focus events inform your plugin
  553.     // that it will become, or is no longer, the recepient of
  554.     // key events.  If your plugin doesn't want to receive key
  555.     // events, return false when passed at getFocusEvent.  The
  556.     // adjustCursorEvent is passed repeatedly when the mouse is
  557.     // over your plugin; if your plugin doesn't want to set the
  558.     // cursor, return false.  Handle the standard Mac events as
  559.     // normal.  The return value for all standard events is currently
  560.     // ignored except for the key event: for key events, only return
  561.     // true if your plugin has handled that particular key event.
  562.     //
  563.  
  564.     return eventHandled;
  565. }
  566.  
  567. //
  568. // Here is a sample subclass function.
  569. //
  570. MRESULT APIENTRY
  571. SubClassFunc(  HWND hWnd,
  572.                ULONG Message,
  573.                MPARAM wParam,
  574.                MPARAM lParam)
  575. {
  576.     PluginInstance *This = GetInstance(hWnd);
  577.  
  578.     switch(Message) {
  579.     case WM_REALIZEPALETTE:
  580.         WinInvalidateRect(hWnd, 0, TRUE);
  581.         WinUpdateWindow(hWnd);
  582.         return 0;
  583.         break;
  584.  
  585.     case WM_PAINT:
  586.         {
  587.             /* invalidate the whole window */
  588.             WinInvalidateRect(hWnd, NULL, TRUE);
  589.  
  590.             /* get PS associated with window  and set to PU_TWIPS coordinates */
  591.             RECTL invalidRect;
  592.             HPS hps = WinBeginPaint(hWnd, NULL, &invalidRect);
  593.             SIZEL siz = { 0,0 };
  594.             GpiSetPS(hps, &siz, PU_TWIPS);
  595.  
  596.             /* get window size and convert to world coordinates */
  597.             RECTL rect;
  598.             WinQueryWindowRect(hWnd, &rect);
  599.             POINTL pts[2] = { { 0L, 0L },
  600.                               { rect.xRight, rect.yTop }
  601.                             };
  602.             GpiConvert(hps, CVTC_DEVICE, CVTC_WORLD, 2L, pts);
  603.  
  604.             /* draw using common drawing routine */
  605.             Draw(This, hps, &pts[1], FALSE);
  606.             WinEndPaint(hps);
  607.         }
  608.         return (MRESULT)0;
  609.         break;
  610.  
  611.     default:
  612.         break;
  613.     }
  614.  
  615.     return ((PFNWP)This->lpfnOldWndProc)(
  616.                           hWnd,
  617.                           Message,
  618.                           wParam,
  619.                           lParam);
  620. }
  621.  
  622. void Draw(PluginInstance *This, HPS hps, POINTL *endPoint, BOOL fPrinting)
  623. {
  624.     // Draw random colored box.
  625.     // Notice when you resize the browser window, the plugin actually gets
  626.     // drawn twice. You may want to draw into a bitmap and bitblt to
  627.     // improve performance if you plugin does a lot of drawing.
  628.     POINTL ptl = { 0, 0 };
  629.     GpiMove(hps, &ptl);
  630.     GpiSetColor(hps, rand()%15+1);
  631.     GpiBox(hps, DRO_OUTLINEFILL, endPoint, 0L, 0L);
  632. }
  633.