REMOTE.C
/**************************************************************************** 
                   Microsoft RPC Version 2.0 
           Copyright Microsoft Corp. 1992, 1993, 1994- 1996 
                        mandel Example 
 
    FILE:      remote.c 
 
    PURPOSE:   Client side of the RPC distributed application Mandel 
 
    COMMENTS:  Code to do the remote calculations for the Windows 
               Mandelbrot Set distributed drawing program. 
 
               Information coming into this module (via API calls) is 
               based on upper-left being (0,0) (the Windows standard). 
               We translate that to lower-left is (0,0) before we ship 
               it out onto the net, and we do reverse translations 
               accordingly. 
 
               The iteration data is passed back to the main window 
               procedure (by means of a WM_PAINTLINE message) which 
               draws the picture. 
 
               A word about the shared buffer: multiple buffers could 
               be used, but a single one is used. The buffer is requested 
               in this code, and then released after the data has been 
               drawn (in PaintLine() in mandel.c). So long as the painting 
               is done quickly, this is efficient. 
 
****************************************************************************/ 
 
#include <stdlib.h> 
#include <stdio.h> 
#include <string.h> 
#include <windows.h> 
 
#ifdef RPC 
#include "mdlrpc.h" 
#endif 
#include "mandel.h" 
 
 
/* 
 * External variables 
 */ 
extern int          fBound; 
extern svr_table    SvrTable;    // the server table 
extern int          iLines; 
extern double       dPrec; 
extern int          fContinueZoom; 
extern int          fZoomIn; 
extern int          iHistMaxI; 
extern int          iHistMaxJ; 
extern RECT         rcZoom; 
extern BOOL         fRectDefined; 
 
 
/* 
 * Picture information 
 */ 
int             cPictureID = 0;    // picture id, in case we reset in the middle 
static CPOINT   cptLL;             // upper-left 
static double   dPrecision;        // precision of draw 
static LONGRECT rclPicture;        // rectangle defining client window 
static DWORD    dwCurrentLine;     // next line to be drawn 
static DWORD    dwThreshold;       // threshold for iterations 
 
/* 
 * Function prototypes for local procs 
 */ 
DWORD CalcThreshold(double); 
 
 
/* 
 *  InitRemote -- 
 * 
 *  This function initializes everything for our remote connections. 
 *  It gets the local wksta name (making sure the wksta is started) 
 *  and it creates the mailslot with which to collect replies to our poll. 
 * 
 *  RETURNS 
 *      TRUE    - initialization succeeded 
 *      FALSE   - initialization failed, can't go on 
 */ 
 
BOOL InitRemote(HWND hWnd) 
{ 
 
#ifndef RPC 
    UNREFERENCED_PARAMETER(hWnd); 
#endif 
 
    // set up our local entry 
    strcpy(SvrTable.name, "Local machine"); 
    SvrTable.iStatus = SS_LOCAL; 
 
    // good, we succeeded 
    return(TRUE); 
} 
 
 
/* 
 *  CheckDrawStatus -- 
 * 
 *  This function does a check of all buffers being drawn. 
 * 
 *  If it finds an idle pipe, and there is work to be done, it assigns 
 *      a line, and writes out the request. 
 *  If it finds a read-pending pipe, it checks if the read has completed. 
 *      If it has, it is read and a message is sent so the read data can 
 *      be processed. 
 * 
 *  RETURNS 
 *      TRUE    - we did a piece of work 
 *      FALSE   - we could not find any work to do. 
 */ 
 
BOOL CheckDrawStatus(HWND hwnd) 
{ 
    CALCBUF     cb; 
    LPVOID      pbBuf; 
 
    while(TRUE) { 
 
        // Check the status 
        switch(SvrTable.iStatus) { 
 
        case SS_PAINTING: 
            break; 
 
        case SS_IDLE: 
            break; 
 
        case SS_LOCAL: 
            // Do a chunk of work locally 
 
#ifdef RPC 
            if (fBound == FALSE) 
                break; 
#endif 
 
            if ((long) dwCurrentLine > rclPicture.xRight) { 
                if (fContinueZoom == TRUE) { 
                    if ((fZoomIn == TRUE) && (dPrec < (double)MINPREC)) 
                        fZoomIn = FALSE;  // start zooming out 
                    if ((fZoomIn == FALSE) && (dPrec > (double)MAXPREC)) 
                        fZoomIn = TRUE; 
                    if (fZoomIn) { 
                        CountHistogram(); 
                        rcZoom.top    = iHistMaxJ * (WIDTH/4); 
                        rcZoom.bottom = rcZoom.top + (WIDTH/4) - 1; 
                        rcZoom.left   = iHistMaxI * (HEIGHT/4); 
                        rcZoom.right  = rcZoom.left + (HEIGHT/4) - 1; 
                        fRectDefined = TRUE; 
                        PostMessage(hwnd, WM_COMMAND, IDM_ZOOMIN, 0L); 
                    } 
                    else 
                        PostMessage(hwnd, WM_COMMAND, IDM_ZOOMOUT, 0L); 
                } 
                break; 
            } 
 
            if (TakeDrawBuffer() == FALSE) 
                break; 
 
            pbBuf = LockDrawBuffer(); 
 
            cb.rclDraw.xLeft = dwCurrentLine; 
            cb.rclDraw.xRight = dwCurrentLine + iLines - 1; 
            cb.rclDraw.yTop = rclPicture.yTop; 
            cb.rclDraw.yBottom = rclPicture.yBottom; 
 
            RpcTryExcept { 
                MandelCalc(&cptLL, 
                           &(cb.rclDraw), 
                           dPrecision, 
                           dwThreshold, 
                           (LINEBUF *) pbBuf); 
            } 
            RpcExcept(1) { 
                char szFail[MSGLEN]; 
 
                sprintf (szFail, "%s (0x%x)\n", EXCEPT_MSG, RpcExceptionCode()); 
                MessageBox(hwnd, 
                           szFail, 
                           "Remote Procedure Call", 
                           MB_ICONINFORMATION); 
                        KillTimer(hwnd, 1);  // stop timer for polls 
                    EnableMenuItem(GetMenu(hwnd), IDM_GO, MF_ENABLED);  // enable GO 
                    UnlockDrawBuffer(); 
 
                            ReturnDrawBuffer(); 
                        return(FALSE); 
            } 
            RpcEndExcept 
 
            UnlockDrawBuffer(); 
 
            SvrTable.cPicture = cPictureID; 
            SvrTable.dwLine = dwCurrentLine; 
            SvrTable.cLines = iLines; 
 
            PostMessage(hwnd, WM_PAINTLINE, 0, 0L); 
            dwCurrentLine += iLines; 
 
            return(TRUE); 
        } 
 
        return(FALSE); 
    } 
} 
 
 
/* 
 *  SetNewCalc -- 
 * 
 *  This sets up new information for a drawing and 
 *  updates the drawing ID so any calculations in progress will not 
 *  be mixed in. 
 */ 
 
void SetNewCalc(CPOINT cptUL, double dPrec, RECT rc) 
{ 
    // First, translate from upper left to lower left 
    cptLL.real = cptUL.real; 
    cptLL.imag = cptUL.imag - (dPrec * (rc.bottom - rc.top)); 
 
    // Now the precision 
    dPrecision = dPrec; 
 
    // The rectangle. Once again, translate. 
    rclPicture.xLeft = (long) rc.left; 
    rclPicture.xRight = (long) rc.right; 
    rclPicture.yBottom = (long) rc.top; 
    rclPicture.yTop = (long) rc.bottom; 
 
    // Current line, start of drawing 
    dwCurrentLine = rclPicture.xLeft; 
 
    dwThreshold = CalcThreshold(dPrecision); 
} 
 
 
void IncPictureID(void) 
{ 
    cPictureID++; 
} 
 
 
void ResetPictureID(void) 
{ 
    cPictureID = 0; 
} 
 
 
/* 
 *  CheckDrawing -- 
 * 
 *  Just a sanity check here -- a function to check to make sure that we're 
 *  on the right drawing 
 */ 
 
BOOL CheckDrawingID(int id) 
{ 
    return((id == cPictureID) ? TRUE : FALSE); 
} 
 
 
/* 
 *  TakeDrawBuffer ensures only one pipe read at a time. 
 *  LockDrawBuffer locks the handle and returns a pointer. 
 *  UnlockDrawBuffer unlocks the handle. 
 *  ReturnDrawBuffer lets another pipe read go. 
 *  FreeDrawBuffer ensures the allocated buffer is freed upon exit. 
 */ 
 
static BOOL fBufferTaken = FALSE; 
static HANDLE hSharedBuf = (HANDLE) NULL; 
 
 
BOOL TakeDrawBuffer(void) 
{ 
    if (fBufferTaken) { 
        return(FALSE); 
    } 
 
    if (hSharedBuf == (HANDLE) NULL) { 
        hSharedBuf = LocalAlloc(LMEM_MOVEABLE, MAX_BUFSIZE); 
        if (hSharedBuf == (HANDLE) NULL) 
            return(FALSE); 
    } 
 
    fBufferTaken = TRUE; 
    return(TRUE); 
} 
 
 
LPVOID LockDrawBuffer(void) 
{ 
    if (hSharedBuf == (HANDLE) NULL) 
        return(NULL); 
 
    return(LocalLock(hSharedBuf)); 
} 
 
 
void UnlockDrawBuffer(void) 
{ 
    LocalUnlock(hSharedBuf); 
} 
 
 
void ReturnDrawBuffer(void) 
{ 
    fBufferTaken = FALSE; 
} 
 
 
void FreeDrawBuffer(void) 
{ 
    if (hSharedBuf != (HANDLE) NULL) 
        LocalFree(hSharedBuf); 
} 
 
 
/* 
 *  CalcThreshold -- 
 * 
 *  We need an iteration threshold beyond which we give up. We want it to 
 *  increase the farther we zoom in. This code generates a threshold value 
 *  based on the precision of drawing. 
 * 
 *  RETURNS 
 *      threshold calculated based on precision 
 */ 
 
DWORD CalcThreshold(double precision) 
{ 
    DWORD   thres = 25; 
    double  multiplier = (double) 100.0; 
 
    /* for every 100, multiply by 2 */ 
    while ((precision *= multiplier) < (double)1.0) 
        thres *= 2; 
 
    return(thres); 
} 
 
 
/* 
 *  QueryThreshold -- 
 * 
 *  Callback for finding out what the current drawing's threshold is. 
 */ 
 
DWORD QueryThreshold(void) 
{ 
    return(dwThreshold); 
}