®RHA®PT2¯®LM10¯®RM75¯ENVIRONMENTS o CHARLES PETZOLD p. ®PN¯ of ®FP¯ Vol. 9, No. 12 Filename: ®VA$FI¯ ¯®PT2¯®LM20¯®RM50.5¯®LS2¯®TS22¯ ®NB¯®PT11¯®LM0¯®RM80¯®AL1¯®TS8,16,24,32,40¯ CODE BOX THREADS3.C COMPLETE LISTING /*--------------------------------------------------------- THREADS3.C -- Demonstrates drawing from a second thread (c) 1990, Ziff Communications Co. PC Magazine * Charles Petzold, 2/90 ---------------------------------------------------------*/ #define INCL_WIN #define INCL_GPI #include #include #include #define CXIMAGE 150 #define CYIMAGE 150 #define SPLINE 100 #define STACKSIZE (4096 * sizeof (int)) #define SEGNAME 1L #define PI 3.14159 typedef struct { HPS hps ; BOOL fTerminate ; ULONG semTriggerDraw ; ULONG semDoingDraw ; } THREADPARAMS ; MRESULT EXPENTRY ClientWndProc (HWND, USHORT, MPARAM, MPARAM) ; VOID FAR SecondThread (THREADPARAMS *) ; VOID ScaleImageToClient (HPS, SIZEL *) ; VOID DrawImage (HPS) ; VOID DrawLetters (HPS) ; HAB hab ; int main (void) { static CHAR szClientClass [] = "Threads3" ; static ULONG flFrameFlags = FCF_TITLEBAR | FCF_SYSMENU | FCF_SIZEBORDER | FCF_MINMAX | FCF_SHELLPOSITION | FCF_TASKLIST ; HAB hab ; HMQ hmq ; HWND hwndFrame, hwndClient ; QMSG qmsg ; hab = WinInitialize (0) ; hmq = WinCreateMsgQueue (hab, 0) ; WinRegisterClass (hab, szClientClass, ClientWndProc, CS_SIZEREDRAW, 0) ; hwndFrame = WinCreateStdWindow (HWND_DESKTOP, WS_VISIBLE, &flFrameFlags, szClientClass, NULL, 0L, NULL, 0, &hwndClient) ; while (WinGetMsg (hab, &qmsg, NULL, 0, 0)) WinDispatchMsg (hab, &qmsg) ; WinDestroyWindow (hwndFrame) ; WinDestroyMsgQueue (hmq) ; WinTerminate (hab) ; return 0 ; } MRESULT EXPENTRY ClientWndProc (HWND hwnd, USHORT msg, MPARAM mp1, MPARAM mp2) { static int aiThreadStack [STACKSIZE / sizeof (int)] ; static THREADPARAMS tp ; HDC hdc ; SIZEL sizlPage, sizlClient ; switch (msg) { case WM_CREATE: // create a presentation space for the window hdc = WinOpenWindowDC (hwnd) ; sizlPage.cx = 0 ; sizlPage.cy = 0 ; tp.hps = GpiCreatePS (hab, hdc, &sizlPage, PU_LOENGLISH | GPIF_DEFAULT | GPIT_NORMAL | GPIA_ASSOC) ; // start the thread after initialization tp.fTerminate = FALSE ; DosSemSet (&tp.semTriggerDraw) ; DosSemClear (&tp.semDoingDraw) ; _beginthread (SecondThread, aiThreadStack, STACKSIZE, &tp) ; // create the segment GpiSetDrawingMode (tp.hps, DM_RETAIN) ; GpiOpenSegment (tp.hps, SEGNAME) ; DrawImage (tp.hps) ; GpiCloseSegment (tp.hps) ; return 0 ; case WM_SIZE: // stop the thread from drawing GpiSetStopDraw (tp.hps, SDW_ON) ; DosSemWait (&tp.semDoingDraw, SEM_INDEFINITE_WAIT) ; // get new size of client window sizlClient.cx = SHORT1FROMMP (mp2) ; sizlClient.cy = SHORT2FROMMP (mp2) ; // set the new default view matrix GpiConvert (tp.hps, CVTC_DEVICE, CVTC_PAGE, 1L, (PPOINTL) &sizlClient) ; ScaleImageToClient (tp.hps, &sizlClient) ; return 0 ; case WM_PAINT: // stop the thread from drawing GpiSetStopDraw (tp.hps, SDW_ON) ; DosSemWait (&tp.semDoingDraw, SEM_INDEFINITE_WAIT) ; // erase the window WinBeginPaint (hwnd, tp.hps, NULL) ; GpiErase (tp.hps) ; WinEndPaint (tp.hps) ; // let the thread continue GpiSetStopDraw (tp.hps, SDW_OFF) ; DosSemSet (&tp.semDoingDraw) ; DosSemClear (&tp.semTriggerDraw) ; return 0 ; case WM_DESTROY: // stop the thread tp.fTerminate = TRUE ; GpiSetStopDraw (tp.hps, SDW_ON) ; DosSemWait (&tp.semDoingDraw, SEM_INDEFINITE_WAIT) ; // clean up GpiDeleteSegment (tp.hps, SEGNAME) ; GpiAssociate (tp.hps, NULL) ; GpiDestroyPS (tp.hps) ; return 0 ; } return WinDefWindowProc (hwnd, msg, mp1, mp2) ; } VOID _CDECL FAR SecondThread (THREADPARAMS * ptp) { HAB hab ; hab = WinInitialize (0) ; while (!ptp->fTerminate) { // wait for the semaphore to be cleared DosSemWait (&ptp->semTriggerDraw, SEM_INDEFINITE_WAIT) ; // draw the image GpiSavePS (ptp->hps) ; GpiDrawSegment (ptp->hps, SEGNAME) ; GpiRestorePS (ptp->hps, -1L) ; // set and clear the semaphores DosSemSet (&ptp->semTriggerDraw) ; DosSemClear (&ptp->semDoingDraw) ; } WinTerminate (hab) ; _endthread () ; } VOID ScaleImageToClient (HPS hps, SIZEL *psizlClient) { FIXED afxScale[2] ; MATRIXLF matlf ; POINTL ptl ; afxScale[0] = afxScale[1] = min (65536L * psizlClient->cx / CXIMAGE, 65536L * psizlClient->cy / CYIMAGE) ; ptl.x = 0 ; ptl.y = 0 ; GpiScale (hps, &matlf, TRANSFORM_REPLACE, afxScale, &ptl) ; ptl.x = (psizlClient->cx - afxScale[0] * CXIMAGE / 65536L) / 2 ; ptl.y = (psizlClient->cy - afxScale[1] * CYIMAGE / 65536L) / 2 ; GpiTranslate (hps, &matlf, TRANSFORM_ADD, &ptl) ; GpiSetDefaultViewMatrix (hps, 9L, &matlf, TRANSFORM_REPLACE) ; } VOID DrawImage (HPS hps) { MATRIXLF matlf ; POINTL ptl, aptl[4] ; SHORT iAngle ; // Draw outline of letters DrawLetters (hps) ; // Use letters outline as clipping path GpiBeginPath (hps, 1L) ; DrawLetters (hps) ; GpiEndPath (hps) ; GpiSetClipPath (hps, 1L, SCP_AND | SCP_ALTERNATE) ; for (iAngle = 0 ; iAngle < 360 ; iAngle++) { // Find matrix for rotation around origin ptl.x = 0 ; ptl.y = 0 ; GpiRotate (hps, &matlf, TRANSFORM_REPLACE, MAKEFIXED (iAngle, 0), &ptl) ; // Append matrix for translation to center of image ptl.x = CXIMAGE / 2 ; ptl.y = CYIMAGE / 2 ; GpiTranslate (hps, &matlf, TRANSFORM_ADD, &ptl) ; // Set model transform using composite matrix GpiSetModelTransformMatrix (hps, 9L, &matlf, TRANSFORM_REPLACE) ; // Draw spline curve aptl[0].x = 0 ; aptl[0].y = 0 ; aptl[1].x = SPLINE / 3 ; aptl[1].y = SPLINE / 2 ; aptl[2].x = 2 * SPLINE / 3 ; aptl[2].y = - SPLINE / 2 ; aptl[3].x = SPLINE ; aptl[3].y = 0 ; GpiMove (hps, aptl) ; GpiPolySpline (hps, 3L, aptl + 1) ; } } VOID DrawLetters (HPS hps) { POINTL ptl, aptl[3] ; // Outside of 'P' ptl.x = 4 ; ptl.y = 4 ; GpiMove (hps, &ptl) ; ptl.x = 4 ; ptl.y = 146 ; GpiLine (hps, &ptl) ; ptl.x = 45 ; ptl.y = 146 ; GpiLine (hps, &ptl) ; aptl[0].x = 72 ; aptl[0].y = 146 ; aptl[1].x = 72 ; aptl[1].y = 119 ; GpiPolyFillet (hps, 2L, aptl) ; ptl.x = 72 ; ptl.y = 85 ; GpiLine (hps, &ptl) ; aptl[0].x = 72 ; aptl[0].y = 58 ; aptl[1].x = 45 ; aptl[1].y = 58 ; GpiPolyFillet (hps, 2L, aptl) ; ptl.x = 35 ; ptl.y = 58 ; GpiLine (hps, &ptl) ; ptl.x = 35 ; ptl.y = 4 ; GpiLine (hps, &ptl) ; ptl.x = 4 ; ptl.y = 4 ; GpiLine (hps, &ptl) ; // Inside of 'P' ptl.x = 35 ; ptl.y = 77 ; GpiMove (hps, &ptl) ; ptl.x = 35 ; ptl.y = 125 ; GpiLine (hps, &ptl) ; aptl[0].x = 43 ; aptl[0].y = 125 ; aptl[1].x = 43 ; aptl[1].y = 117 ; GpiPolyFillet (hps, 2L, aptl) ; ptl.x = 43 ; ptl.y = 85 ; GpiLine (hps, &ptl) ; aptl[0].x = 43 ; aptl[0].y = 77 ; aptl[1].x = 35 ; aptl[1].y = 77 ; GpiPolyFillet (hps, 2L, aptl) ; // Outline of 'C' ptl.x = 116 ; ptl.y = 63 ; GpiMove (hps, &ptl) ; ptl.x = 145 ; ptl.y = 63 ; GpiLine (hps, &ptl) ; ptl.x = 145 ; ptl.y = 31 ; GpiLine (hps, &ptl) ; aptl[0].x = 145 ; aptl[0].y = 4 ; aptl[1].x = 118 ; aptl[1].y = 4 ; GpiPolyFillet (hps, 2L, aptl) ; ptl.x = 103 ; ptl.y = 4 ; GpiLine (hps, &ptl) ; aptl[0].x = 76 ; aptl[0].y = 4 ; aptl[1].x = 76 ; aptl[1].y = 31 ; GpiPolyFillet (hps, 2L, aptl) ; ptl.x = 76 ; ptl.y = 119 ; GpiLine (hps, &ptl) ; aptl[0].x = 76 ; aptl[0].y = 146 ; aptl[1].x = 103 ; aptl[1].y = 146 ; GpiPolyFillet (hps, 2L, aptl) ; ptl.x = 118 ; ptl.y = 146 ; GpiLine (hps, &ptl) ; aptl[0].x = 145 ; aptl[0].y = 146 ; aptl[1].x = 145 ; aptl[1].y = 119 ; GpiPolyFillet (hps, 2L, aptl) ; ptl.x = 145 ; ptl.y = 88 ; GpiLine (hps, &ptl) ; ptl.x = 116 ; ptl.y = 88 ; GpiLine (hps, &ptl) ; ptl.x = 116 ; ptl.y = 123 ; GpiLine (hps, &ptl) ; aptl[0].x = 116 ; aptl[0].y = 127 ; aptl[1].x = 108 ; aptl[1].y = 127 ; aptl[2].x = 108 ; aptl[2].y = 123 ; GpiPolyFillet (hps, 3L, aptl) ; ptl.x = 108 ; ptl.y = 30 ; GpiLine (hps, &ptl) ; aptl[0].x = 108 ; aptl[0].y = 26 ; aptl[1].x = 116 ; aptl[1].y = 26 ; aptl[2].x = 116 ; aptl[2].y = 30 ; GpiPolyFillet (hps, 3L, aptl) ; ptl.x = 116 ; ptl.y = 63 ; GpiLine (hps, &ptl) ; } caption: ®BB¯®PT7¯®AL0¯®LM0¯®RM69.6¯®LS2¯®MDBO¯Figure 3:®MDNM¯ The THREADS3.C source code listing contains several new functions that require OS/2 1.2.