#pragma strings( readonly )

/*+--------------------------------------------------------------------------+*/
/*|                                                                          |*/
/*| PROGRAM NAME: PMLINES                                                    |*/
/*| -------------                                                            |*/
/*|  A Simple OS/2 Presentation Manager Graphics Demonstration Program       |*/
/*|                                                                          |*/
/*| COPYRIGHT:                                                               |*/
/*| ----------                                                               |*/
/*|  Copyright (C) International Business Machines Corp., 1991,1992.         |*/
/*|                                                                          |*/
/*| DISCLAIMER OF WARRANTIES:                                                |*/
/*| -------------------------                                                |*/
/*|  The following [enclosed] code is sample code created by IBM Corporation.|*/
/*|  This sample code is not part of any standard IBM product and is provided|*/
/*|  to you solely for the purpose of assisting you in the development of    |*/
/*|  your applications.  The code is provided "AS IS", without warranty of   |*/
/*|  any kind.  IBM shall not be liable for any damages arising out of your  |*/
/*|  use of the sample code, even if they have been advised of the           |*/
/*|  possibility of such damages.                                            |*/
/*|                                                                          |*/
/*| REVISION LEVEL: 1.0                                                      |*/
/*| ---------------                                                          |*/
/*|                                                                          |*/
/*| WHAT THIS PROGRAM DOES:                                                  |*/
/*| -----------------------                                                  |*/
/*|  This program displays a standard window and then draws lines in the     |*/
/*|  window.  Both the line and background colours change.                   |*/
/*|                                                                          |*/
/*| WHAT THIS PROGRAM DEMONSTRATES:                                          |*/
/*| -------------------------------                                          |*/
/*|  This program demonstrates how to create and display a standard window,  |*/
/*|  use simple menus and dialog boxes, use a second thread for drawing,     |*/
/*|  and displaying graphics using some basic GPI calls.                     |*/
/*|                                                                          |*/
/*| WHAT YOU NEED TO COMPILE THIS PROGRAM:                                   |*/
/*| --------------------------------------                                   |*/
/*|                                                                          |*/
/*|  REQUIRED FILES:                                                         |*/
/*|  ---------------                                                         |*/
/*|                                                                          |*/
/*|    PMLINES.C      - Source code                                          |*/
/*|    PMLINES.DEF    - Module definition file                               |*/
/*|    PMLINES.H      - Application header file                              |*/
/*|    PMLINES.ICO    - Icon file                                            |*/
/*|    SAMPLE1A.RC    - Resource file                                        |*/
/*|    PMLINES.DLG    - Dialog file                                          |*/
/*|                                                                          |*/
/*|    OS2.H          - Presentation Manager include file                    |*/
/*|    STDLIB.H       - Standard library function declarations               |*/
/*|    STRING.H       - String handling function declarations                |*/
/*|                                                                          |*/
/*|  REQUIRED LIBRARIES:                                                     |*/
/*|  -------------------                                                     |*/
/*|                                                                          |*/
/*|    OS2386.LIB     - Presentation Manager/OS2 library                     |*/
/*|    DDE4NBS.LIB    - Subsystem Development Library                        |*/
/*|                                                                          |*/
/*|  REQUIRED PROGRAMS:                                                      |*/
/*|  ------------------                                                      |*/
/*|                                                                          |*/
/*|    IBM C Set/2 Compiler                                                  |*/
/*|    IBM Linker                                                            |*/
/*|    Resource Compiler                                                     |*/
/*|                                                                          |*/
/*| EXPECTED INPUT:                                                          |*/
/*| ---------------                                                          |*/
/*|                                                                          |*/
/*| EXPECTED OUTPUT:                                                         |*/
/*| ----------------                                                         |*/
/*|                                                                          |*/
/*+--------------------------------------------------------------------------+*/

/*+--------------------------------------------------------------------------+*/
/*| System and library header files.                                         |*/
/*+--------------------------------------------------------------------------+*/

#define INCL_NOCOMMON
#define INCL_DOSPROCESS
#define INCL_DOSSEMAPHORES
#define INCL_DOSERRORS
#define INCL_WINWINDOWMGR
#define INCL_WINMESSAGEMGR
#define INCL_WINFRAMEMGR
#define INCL_WINDIALOGS
#define INCL_WININPUT
#define INCL_WINSWITCHLIST
#define INCL_WINPROGRAMLIST
#define INCL_GPICONTROL
#define INCL_GPIPRIMITIVES
#include <os2.h>
#include <stdlib.h>
#include <string.h>

/*+--------------------------------------------------------------------------+*/
/*| Application header files.                                                |*/
/*+--------------------------------------------------------------------------+*/

#include "pmlines.h"

/*+--------------------------------------------------------------------------+*/
/*| Internal function prototypes.                                            |*/
/*+--------------------------------------------------------------------------+*/

static void InitTitle( HAB, HWND, char * );
static MRESULT EXPENTRY ClientWindowProc( HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2 );
static MRESULT EXPENTRY HelpDlgProc( HWND hwndDlg, ULONG msg, MPARAM mp1, MPARAM mp2 );
static FNTHREAD DrawingThread;
static void DisplayMessage( HAB, ULONG );

/*+--------------------------------------------------------------------------+*/
/*| Static global variables and local constants.                             |*/
/*+--------------------------------------------------------------------------+*/

#define WINDOW_CLASS   "My_Window"          /* window class name              */
#define COLOUR_TBL_LEN 16                   /* number of colour table entries */
#define MSG_BOX_ID     256                  /* error message box id           */
#define STRING_LENGTH  128                  /* size of buffer used for strings*/
#define STACK_SIZE     8192UL               /* stack size for second thread   */

static HWND   hwndClient = NULLHANDLE;      /* client window handle           */
static HMQ    hmqThread2 = NULLHANDLE;      /* message queue handle for thread*/
static POINTL ptl1;                         /* line start point               */
static POINTL ptl2;                         /* line end point                 */
static LONG   lFcolour = CLR_BLUE;          /* foreground (line) colour       */
static LONG   lBcolour = CLR_DARKGRAY;      /* background colour              */
static LONG   cxClient, cyClient;           /* client window X and Y size     */
static LONG   fcolourcounter = 1L;          /* wait time to change line colour*/
static TID    tidDrawing = 0UL;             /* drawing thread identifier      */
static char   szAppName[ MAXNAMEL + 1 ];    /* application name string        */

/*+--------------------------------------------------------------------------+*/
/*| Main control procedure.                                                  |*/
/*+--------------------------------------------------------------------------+*/

int main( void )
{
   HAB   hab = NULLHANDLE;                  /* PM anchor block handle         */
   HMQ   hmq = NULLHANDLE;                  /* message queue handle           */
   HWND  hwndFrame = NULLHANDLE;            /* frame window handle            */
   ULONG flCreate = 0UL;                    /* window creation control flags  */
   QMSG  qmsg;                              /* message from message queue     */
   int   rc = 1;

   do
   {
      /* Initialize PM and create a message queue of default size.            */

      if ( ( hab = WinInitialize( 0UL ) ) == NULLHANDLE )
         break;

      if ( ( hmq = WinCreateMsgQueue( hab, 0UL ) ) == NULLHANDLE )
         break;

      /* Register client window class.                                        */

      if ( !WinRegisterClass( hab,          /* PM anchor block handle         */
                              WINDOW_CLASS, /* window class name              */
                          ClientWindowProc, /* address of window procedure    */
                              CS_SIZEREDRAW,/* size changes cause redrawing   */
                              0UL ) )       /* window data                    */
      {
         DisplayMessage( hab, IDS_NOREGISTER );
         break;
      }

      /* Create the standard windows but do not add the application to the    */
      /* task manager list.                                                   */

      flCreate = FCF_STANDARD & ~FCF_TASKLIST;

      hwndFrame =
         WinCreateStdWindow( HWND_DESKTOP,  /* make desktop window the parent */
                             WS_VISIBLE,    /* frame window class style       */
                             &flCreate,     /* frame control flags            */
                             WINDOW_CLASS,  /* client window class name       */
                             "",            /* title bar text                 */
                             CS_SIZEREDRAW, /* client window class style      */
                             0UL,           /* resource file handle - in EXE  */
                             ID_WINDOW,     /* resources identifier           */
                             &hwndClient ); /* client window handle           */
      if ( hwndFrame == NULLHANDLE )
      {
         DisplayMessage( hab, IDS_NOSTDWINDOWS );
         break;
      }

      /* Initialize the window title and task switch list.                    */

      InitTitle( hab, hwndFrame, szAppName );

      /* Create the thread that will draw the lines.                          */
      /* NOTE: _beginthread MUST be used if the thread contains CRT calls     */

      if ( DosCreateThread( &tidDrawing, DrawingThread, 0UL, 0UL, STACK_SIZE ) )
      {
         DisplayMessage( hab, IDS_NOTHREAD );
         break;
      }

      /* While the WM_QUIT message is not received, dispatch the message.     */
      /* When the WM_QUIT message is received, WinGetMsg will return FALSE.   */

      while( WinGetMsg( hab, &qmsg, 0UL, 0UL, 0UL ) )
         WinDispatchMsg( hab,               /* PM anchor block handle         */
                         &qmsg );           /* pointer to message             */
      rc = 0;

   }  while ( FALSE );

   /* Destroy the standard windows if they were created.                      */

   if ( hwndFrame != NULLHANDLE )
      WinDestroyWindow( hwndFrame );        /* frame window handle            */

   /* Destroy the message queue and release the anchor block.                 */

   if ( hmq != NULLHANDLE )
      WinDestroyMsgQueue( hmq );

   if ( hab != NULLHANDLE )
      WinTerminate( hab );

   return rc;
}

/*+--------------------------------------------------------------------------+*/
/*| InitTitle - Initializes window title and task switch list.               |*/
/*+--------------------------------------------------------------------------+*/

static void InitTitle( HAB hab, HWND hwnd, char *szTitle )
{
   SWCNTRL tSwcntrl;                        /* task switch structure          */

   /* Load the application name from the resources in the EXE.  Set the title */
   /* bar text to this name.  Then add this application to the task manager   */
   /* list with the loaded application name.                                  */

   WinLoadString( hab, 0UL, IDS_APPNAME, MAXNAMEL, szTitle );
   WinSetWindowText( hwnd, szTitle );

   tSwcntrl.hwnd = hwnd;
   tSwcntrl.hwndIcon = NULLHANDLE;
   tSwcntrl.hprog = NULLHANDLE;
   tSwcntrl.idProcess = 0;
   tSwcntrl.idSession = 0;
   tSwcntrl.uchVisibility = SWL_VISIBLE;
   tSwcntrl.fbJump = SWL_JUMPABLE;
   strcpy( tSwcntrl.szSwtitle, szTitle );
   tSwcntrl.bProgType = PROG_PM;
   WinAddSwitchEntry( &tSwcntrl );

   return;
}

/*+--------------------------------------------------------------------------+*/
/*| ClientWindowProc - Client window procedure.                              |*/
/*+--------------------------------------------------------------------------+*/

static MRESULT EXPENTRY ClientWindowProc( HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2 )
{
   HPS   hps = NULLHANDLE;                  /* presentation space handle      */

   /* Process the message.                                                    */

   switch( msg )
   {
      case WM_CREATE:
         /* The client window has been created but is not visible yet.        */
         /* Initialize the window here.                                       */
         break;

      case WM_CLOSE:
         /* Tell the main procedure to terminate the message loop.            */

         WinPostMsg( hwnd, WM_QUIT, NULL, NULL );
         break;

      case WM_PAINT:
         /* Post a message to the second thread to make it repaint the        */
         /* background.                                                       */

         hps = WinBeginPaint( hwnd, NULLHANDLE, NULL );

         WinPostQueueMsg( hmqThread2, WM_USER_REPAINT, 0UL, 0UL );

         WinEndPaint( hps );
         break;

      case WM_COMMAND:
         /* The user has chosen a menu item.  Process the selection           */
         /* accordingly.                                                      */

         switch ( SHORT1FROMMP( mp1 ) )
         {
            case IDM_EXITPROG:
               /* Post a close message.                                       */

               WinPostMsg( hwnd, WM_CLOSE, NULL, NULL );
               break;

            case IDM_RESUME:
               break;

            case IDM_HELPINSTRUCTIONS:
               /* Display the help dialog box.                                */

               WinMessageBox( HWND_DESKTOP,      /* desktop is the parent     */
                              hwnd,              /* owned by client window    */
                              INSTRUCTIONS,      /* pointer to message text   */
                              szAppName,         /* pointer to title text     */
                              1UL,               /* message box identifier    */
                              MB_OK | MB_INFORMATION | /* message box style   */
                              MB_APPLMODAL | MB_MOVEABLE );

               /* Force a repaint.                                            */

               WinInvalidateRegion( hwnd, NULLHANDLE, FALSE );
               break;

            case IDM_HELPABOUT:
               /* Display the help dialog box.                                */

               WinDlgBox( HWND_DESKTOP,     /* make the desktop the parent    */
                          hwnd,             /* owned by client window         */
                          HelpDlgProc,      /* address of dialog procedure    */
                          0UL,              /* module handle                  */
                          IDD_HELP,         /* dialog identifier in resource  */
                          NULL );           /* initialization data            */

               /* Force a repaint.                                            */

               WinInvalidateRegion( hwnd, NULLHANDLE, FALSE );
               break;
         }
         break;

      case WM_SIZE:
         /* Window is being resized so get new client window size and         */
         /* recompute the end points for the lines.                           */

         cxClient = SHORT1FROMMP( mp2 );    /* Get new client window size     */
         cyClient = SHORT2FROMMP( mp2 );
         ptl1.x = cxClient / 2;             /* Set new points for lines based */
         ptl1.y = cyClient / 2;             /* on the size of client window   */
         ptl2.x = ptl1.x / 2;
         ptl2.y = ptl1.y / 2;
         break;

      case WM_BUTTON1DOWN:
         /* The user has pushed mouse button 1 which means change the         */
         /* foreground colour.                                                */

         /* Make sure that this window has the focus.                         */

         WinSetFocus( HWND_DESKTOP, hwnd );

         /* Reset foreground colour counter and increment foreground colour.  */

         fcolourcounter = 1;
         lFcolour++;

         /* Make sure foreground and background colours are different.        */

         if ( lFcolour == lBcolour )
            lFcolour++;

         /* Make sure the foregound colour does not exceed CLR_PALEGRAY.      */

         if ( lFcolour >= CLR_PALEGRAY )
            lFcolour = CLR_BLUE;

         return ( MRESULT )TRUE;

      case WM_BUTTON1DBLCLK:
         /* The user has double clicked mouse button 1 which means change     */
         /* the background colour.                                            */

         /* Make sure that this window has the focus.                         */

         WinSetFocus( HWND_DESKTOP, hwnd );

         /* Increment the backgound colour but make sure the background       */
         /* does not exceed CLR_PALEGRAY.                                     */

         if ( lBcolour == CLR_PALEGRAY )
            lBcolour = CLR_BLUE;
         else
            ++lBcolour;

         /* Make sure foreground and background colours are different.        */

         if ( lFcolour == lBcolour )
            lFcolour++;

         /* Make sure the foregound colour does not exceed CLR_PALEGRAY.      */

         if ( lFcolour >= CLR_PALEGRAY )
            lFcolour = CLR_BLUE;

         /* Force the client window to repaint so that the background colour  */
         /* is updated.                                                       */

         WinInvalidateRegion( hwnd, NULLHANDLE, FALSE );

         return ( MRESULT )TRUE;

      case WM_BUTTON2DOWN:
         /* Make sure that this window has the focus.                         */

         WinSetFocus( HWND_DESKTOP, hwnd );

         /* Force the client window to repaint to clear the window.           */

         WinInvalidateRegion( hwnd, NULLHANDLE, FALSE );

         return ( MRESULT )TRUE;

      case WM_DESTROY:
         /* The client window is being destroyed so tell the second thread to */
         /* stop running.                                                     */

         WinPostQueueMsg( hmqThread2, WM_USER_END_THREAD, 0UL, 0UL );

         /* Wait for the drawing thread to terminate before returning.        */

         DosWaitThread( &tidDrawing, DCWW_WAIT );

         break;

      default:
         /* For all other messages, let the default window procedure          */
         /* process them.                                                     */

         return ( WinDefWindowProc( hwnd, msg, mp1, mp2 ) );
   }
   return NULL;
}

/*+--------------------------------------------------------------------------+*/
/*| HelpDlgProc - Help Dialog Procedure                                      |*/
/*+--------------------------------------------------------------------------+*/

static MRESULT EXPENTRY HelpDlgProc( HWND hwndDlg, ULONG msg, MPARAM mp1, MPARAM mp2 )
{
   switch( msg )
   {
      case WM_COMMAND:
         switch( SHORT1FROMMP( mp1 ) )      /* Extract the command value      */
         {
            case DID_OK:                    /* The Enter pushbutton or key    */
               WinDismissDlg( hwndDlg, TRUE );
               break;

            default:
               break;
         }
         break;

      default:
         return WinDefDlgProc( hwndDlg, msg, mp1, mp2 );
   }
   return NULL;
}

/*+--------------------------------------------------------------------------+*/
/*| DrawingThread - Procedure that draws the lines in the client window.     |*/
/*+--------------------------------------------------------------------------+*/

static void DrawingThread( ULONG ulThreadArg )
{
   /* Declare local variables.                                                */

   HAB   habThread2 = NULLHANDLE;           /* anchor block handle for thread */
   HPS   hps = NULLHANDLE;                  /* presentation Space handle      */
   LONG  delta1x = START_DELTA_X;           /* start point delta values       */
   LONG  delta1y = START_DELTA_Y;
   LONG  delta2x = END_DELTA_X;             /* end point delta values         */
   LONG  delta2y = END_DELTA_Y;
   RECTL rclClient;                         /* rectangle for client window    */
   QMSG  qmsgThread2;                       /* message queue structure        */
   BOOL  have_message;                      /* indicator that a message is    */
                                            /* on the queue to be peeked at   */

   /* Change the priority class of this thread to idle class.                 */

   DosSetPriority( PRTYS_THREAD, PRTYC_IDLETIME, 0L, 0UL );

   /* Initialize thread to PM.                                                */

   habThread2 = WinInitialize( 0UL );
   hmqThread2 = WinCreateMsgQueue( habThread2, 0UL );

   /* Get a presentation space.                                               */

   hps = WinGetPS( hwndClient );

   /* Run drawing thread.                                                     */

   qmsgThread2.msg = WM_USER_REPAINT;
   have_message = TRUE;

   while ( !(have_message == TRUE && qmsgThread2.msg == WM_USER_END_THREAD) )
   {
      if ( have_message == TRUE && qmsgThread2.msg == WM_USER_REPAINT )
      {
         WinQueryWindowRect( hwndClient, &rclClient );
         WinFillRect( hps, &rclClient, lBcolour );
      }
      else
      {
         /* Increment the foreground colour if necessary.                     */

         if ( ++fcolourcounter > ( ( cxClient + cyClient ) >> 1 ) )
         {
            fcolourcounter = 1;                /* Reset fcolourcounter and    */
            lFcolour++;                        /* increment foreground colour.*/

            /* Make sure foreground and background colours are different.     */

            if ( lFcolour == lBcolour )
               lFcolour++;

            /* Make sure the foregound colour does not exceed CLR_PALEGRAY.   */

            if ( lFcolour >= CLR_PALEGRAY )
               lFcolour = CLR_BLUE;
         }

         /* Add deltas to the start and end points for a line.                */

         /* If the start point would be invalid then negate the delta.        */

         if ( ( ptl1.x + delta1x ) > cxClient )
            delta1x = -delta1x;

         if ( ( ptl1.x + delta1x ) < 1 )
            delta1x = -delta1x;

         if ( ( ptl1.y + delta1y ) > cyClient )
            delta1y = -delta1y;

         if ( ( ptl1.y + delta1y ) < 1 )
            delta1y = -delta1y;

         /* Add delta to start point.                                         */

         ptl1.x += delta1x;
         ptl1.y += delta1y;

         /* If the end point would be invalid then negate the delta.          */

         if ( ( ptl2.x + delta2x ) > cxClient )
            delta2x = -delta2x;
         if ( ( ptl2.x + delta2x ) < 1 )
            delta2x = -delta2x;
         if ( ( ptl2.y + delta2y ) > cyClient )
            delta2y = -delta2y;
         if ( ( ptl2.y + delta2y ) < 1 )
            delta2y = -delta2y;

         /* Add delta to end point.                                           */

         ptl2.x += delta2x;
         ptl2.y += delta2y;

         /* Now draw the line.                                                */

         GpiSetColor( hps, lFcolour );
         GpiMove( hps, &ptl1 );                /* Move to start point         */
         GpiLine( hps, &ptl2 );                /* Draw new line               */
      }

      /* Peek in the message queue to see if the main thread has posted a msg.*/

      have_message = WinPeekMsg( habThread2,
                                 &qmsgThread2,
                                 NULLHANDLE,
                                 0UL,
                                 0UL,
                                 PM_REMOVE );
   }

   /* Clean up and terminate drawing thread.                                  */

   WinReleasePS( hps );
   WinDestroyMsgQueue( hmqThread2 );
   WinTerminate( habThread2 );
   DosExit( EXIT_THREAD, 0UL );
}

/*+--------------------------------------------------------------------------+*/
/*| DisplayMessage - display an error message in a message box.              |*/
/*+--------------------------------------------------------------------------+*/

static void DisplayMessage( HAB hab, ULONG ulStringNum )
{
   char szTemp[ STRING_LENGTH ];

   WinLoadString( hab, 0UL, ulStringNum, STRING_LENGTH, szTemp );

   WinAlarm( HWND_DESKTOP,                  /* desktop window handle          */
             WA_ERROR );                    /* type of alarm                  */

   WinMessageBox( HWND_DESKTOP,             /* parent window handle           */
                  HWND_DESKTOP,             /* owner window handle            */
                  szTemp,                   /* pointer to message text        */
                  szAppName,                /* pointer to title text          */
                  MSG_BOX_ID,               /* message box identifier         */
                  MB_OK | MB_ERROR |        /* message box style              */
                  MB_SYSTEMMODAL );

   return;
}
