WPRINTF.C
/******************************************************************************\ 
*       This is a part of the Microsoft Source Code Samples.  
*       Copyright 1993 - 1998 Microsoft Corporation. 
*       All rights reserved.  
*       This source code is only intended as a supplement to  
*       Microsoft Development Tools and/or WinHelp documentation. 
*       See these sources for detailed information regarding the  
*       Microsoft samples programs. 
\******************************************************************************/ 
 
/*****************************************************************************\ 
* 
* Module: wprintf.c 
* 
*   Contains the routines for using printf windows 
* 
* Functions: 
* 
*   MyCreatePrintfWin() 
*   SetPrintFont() 
*   SetPrintfTabs() 
*   ClearPrintfWindow() 
*   CopyToClipboard() 
*   IsPrintfEmpty() 
*   PrintfWndProc() 
*   DebugPaint() 
*   InsertString() 
*   DebugHScroll() 
*   DebugVScroll() 
*   SetWindowClass() 
*   LinesInDebugWindow() 
*   CharsInDebugWindow() 
*   wprintfSetScrollRange() 
*   NewLine() 
* 
* Comments: 
* 
\*****************************************************************************/ 
 
#include "spy.h" 
 
 
 
/*****************************************************************************\ 
*                                                                              
*  g e n e r a l   c o n s t a n t s                                           
*                                                                              
\*****************************************************************************/ 
 
#define MAXBUFLEN 200         /* Maximum string length for wprintf */ 
 
#define FIRST(pTxt) ((pTxt)->iFirst) 
#define TOP(pTxt)   (((pTxt)->iFirst + (pTxt)->iTop) % (pTxt)->iMaxLines) 
#define LAST(pTxt)  (((pTxt)->iFirst + (pTxt)->iCount-1) % (pTxt)->iMaxLines) 
#define INC(pTxt,x) ((x) = ++(x) % (pTxt)->iMaxLines) 
#define DEC(pTxt,x) ((x) = --(x) % (pTxt)->iMaxLines) 
#define OFFSETX (pTxt->Tdx/2) 
#define OFFSETY 1 
#define VARSIZE 1 
 
#define BOUND(x,mn,mx) ((x) < (mn) ? (mn) : ((x) > (mx) ? (mx) : (x))) 
 
#define FTwixtI3(l,x,h) ((x)>=(l) && (x<=h)) 
 
#define EnterCrit(p) 
#define LeaveCrit(p) 
 
/*****************************************************************************\ 
*                                                                               
*   g l o b a l   v a r i a b l e s                                             
*                                                                               
\*****************************************************************************/ 
 
typedef struct { 
    INT     iLen; 
    CHAR    * *hText; 
}   LINE; 
 
struct TEXT_STRUCT { 
    CRITICAL_SECTION csSync;      // CritSect to sync the threads 
 
    INT     iFirst;               /* First line in queue */ 
    INT     iCount;               /* Number of lines in queue */ 
    INT     iTop;                 /* Line at top of window */ 
    INT     iLeft;                /* X offset of the window */ 
    INT     MaxLen;               /* Max String Length */ 
    INT     iMaxLines;            /* max number of LINEs */ 
    HFONT   hFont;                /* Font to draw with */ 
    DWORD   Tdx, Tdy;             /* Font Size */ 
    LINE    arLines[VARSIZE];     /* array of iMaxLines LINEs */ 
}; 
 
typedef struct TEXT_STRUCT *PTXT; /* pointer to a text struct */ 
typedef PTXT               *HTXT; /* Handle to a text struct */ 
 
PRIVATE INT iSem = 0; 
 
INT tabs[20]; 
INT nTabs = 0; 
 
/*****************************************************************************\ 
*                                                                               
*   f u n c t i o n   d e f i n i t i o n s                                     
*                                                                               
\*****************************************************************************/ 
 
LONG APIENTRY PrintfWndProc(HWND, UINT, WPARAM, LONG); 
 
PRIVATE VOID DebugPaint(HWND hwnd, LPPAINTSTRUCT pps); 
PRIVATE INT  InsertString (PTXT, CHAR *); 
PRIVATE VOID DebugHScroll(HWND, PTXT, INT); 
PRIVATE VOID DebugVScroll(HWND, PTXT, INT); 
PRIVATE BOOL SetWindowClass (HANDLE, LPSTR); 
PRIVATE INT  LinesInDebugWindow (HWND); 
PRIVATE INT  CharsInDebugWindow (HWND); 
PRIVATE VOID wprintfSetScrollRange (HWND, BOOL); 
PRIVATE VOID NewLine (PTXT pTxt); 
PRIVATE INT mwprintf( HWND hwnd, LPSTR format, ... ); 
 
 
 
/*****************************************************************************\ 
* MyCreatePrintfWin 
* 
* Creates a window to printf to. 
* 
* Arguments: 
*    HWND hwnd - handle to the parent window 
* 
* Returns: 
*    VOID 
\*****************************************************************************/ 
 
VOID 
MyCreatePrintfWin( 
    HWND hwnd 
    ) 
{ 
    RECT rc; 
 
    if (ghwndPrintf) 
        DestroyWindow(ghwndPrintf); 
 
    GetClientRect(hwnd, &rc); 
 
    ghwndPrintf = CreatePrintfWin(hwnd, ghInst, "", WS_CHILD | WS_VSCROLL | 
        WS_HSCROLL, -gcxBorder, -gcyBorder, rc.right + (2 *gcxBorder), 
        rc.bottom + (2 * gcyBorder), gnLines); 
} 
 
 
 
/*****************************************************************************\ 
* DebugPaint(hwnd, pps)                                                        
*                                                                              
* The paint function.                                                     
*                                                                         
* Arguments:                                                              
*    HWND hwnd - Window to paint to.                                     
*    LPPAINTSTRUCT - pps               
*                                                                               
*   Returns:                                                                    
*       nothing                                                                 
*                                                                               
\*****************************************************************************/ 
 
PRIVATE VOID 
DebugPaint( 
    HWND hwnd, 
    LPPAINTSTRUCT pps 
    ) 
{ 
    PTXT pTxt; 
    HTXT hTxt; 
    INT iQueue; 
    INT xco; 
    INT yco; 
    INT iLast; 
    HBRUSH hb; 
    COLORREF c; 
 
    hTxt = (HTXT)GetWindowLong(hwnd, 0); 
    pTxt = *hTxt; 
 
    SetTextColor(pps->hdc, GetSysColor(COLOR_WINDOWTEXT)); 
    c = GetSysColor(COLOR_WINDOW); 
    SetBkColor(pps->hdc, c); 
    hb = CreateSolidBrush(c); 
    if (pTxt->hFont) 
        SelectObject(pps->hdc, pTxt->hFont); 
 
    iLast  = LAST(pTxt); 
    iQueue = TOP(pTxt); 
 
    xco = OFFSETX - pTxt->iLeft * pTxt->Tdx; 
    yco = OFFSETY; 
 
    for (;;) 
    { 
        if (yco <= pps->rcPaint.bottom && 
                (yco + (LONG)(pTxt->Tdy)) >= pps->rcPaint.top) 
        { 
            if (pTxt->arLines[iQueue].hText == NULL 
                || (LPSTR)*(pTxt->arLines[iQueue].hText) == NULL) 
            { 
                RECT rcT; 
 
                rcT.top = yco; 
                rcT.bottom = yco+pTxt->Tdy; 
                rcT.left = pps->rcPaint.left; 
                rcT.right = pps->rcPaint.right; 
                FillRect(pps->hdc, &rcT, hb); 
            } 
            else 
            { 
                TabbedTextOut(pps->hdc, xco, yco, 
                    (LPSTR)*(pTxt->arLines[iQueue].hText), 
                    pTxt->arLines[iQueue].iLen, nTabs, tabs, xco); 
            } 
        } 
 
        if (iQueue == iLast) 
            break; 
 
        yco += pTxt->Tdy; 
        INC(pTxt, iQueue); 
    } 
 
    DeleteObject((HANDLE)hb); 
} 
 
 
 
/*****************************************************************************\ 
* SetWindowClass (hInstance)                                                 
*      
* Registers the window class of the printf window 
*                                                                          
* Arguments:                                                                  
*   HANDLE hInstance - instance handle of current instance                     
*   LPSTR lpch - pointer to class name 
*                                                                               
* Returns:                                                                    
*   TRUE if successful, FALSE if not                                        
\*****************************************************************************/ 
 
PRIVATE BOOL 
SetWindowClass( 
    HANDLE hInstance, 
    LPSTR lpch 
    ) 
{ 
    WNDCLASS wc; 
 
    wc.style          = CS_HREDRAW | CS_VREDRAW; 
    wc.lpfnWndProc    = PrintfWndProc; 
    wc.cbClsExtra     = 0; 
    wc.cbWndExtra     = sizeof(HANDLE); 
    wc.hInstance      = hInstance; 
    wc.hIcon          = NULL; 
    wc.hCursor        = LoadCursor(NULL, IDC_ARROW); 
    wc.hbrBackground  = (HBRUSH)(COLOR_WINDOW + 1); 
    wc.lpszMenuName   = NULL; 
    wc.lpszClassName  = lpch; 
 
    return RegisterClass(&wc); 
} 
 
 
/*****************************************************************************\ 
* CreatePrintfWin (hParent, lpchName, dwStyle, x, y, dx, dy, iMaxLines)        
*                                                                              
* Creates a window for the depositing of debuging messages.                 
*                                                                               
* Arguments:                                                                  
*   HWND hParent - Window handle of the parent window.                           
*   HANDLE hInstance - Module instance handle.                                      
*   lPSTR lpchName - String to appear in the caption bar of the debuging window    
*   DWORD dwStyle - Window style                                                  
*   INT x,y - Location of window                                            
*   INT dx,dy - Size of the window                                            
*   INT iMaxLines - The maximum number of text lines to display in the window     
*                                                                               
* Returns:                                                                    
*   A window handle of the debuging window, or NULL if a error occured.       
\*****************************************************************************/ 
 
PUBLIC HWND APIENTRY 
CreatePrintfWin ( 
HWND   hParent, 
HANDLE hInstance, 
LPSTR  lpchName, 
DWORD  dwStyle, 
INT   x, 
INT   y, 
INT   dx, 
INT   dy, 
INT    iMaxLines 
) 
{ 
    static BOOL bClass = FALSE;   /* Is the class registered */ 
 
    HWND   hwnd; 
    HTXT   hTxt;      /* handle to a debuging window struct */ 
    PTXT   pTxt; 
    static CHAR achClass[40]; 
 
    /* 
        *  Make a Class name that is unique across instances 
        */ 
    if (!bClass++) { 
        wsprintf(achClass, "WPRINTF_%4.4X", hInstance); 
        SetWindowClass(hInstance, achClass); 
    } 
 
    /* Allocate the window long before create the window, such that the 
           window proc can find the window long during the create. */ 
 
    hTxt = (HTXT)LocalAlloc(LHND, sizeof(struct TEXT_STRUCT) + (iMaxLines 
        - VARSIZE) * sizeof(LINE)); 
 
    if (!hTxt) { 
        return FALSE; 
    } 
 
    pTxt = *hTxt; 
 
    //InitializeCriticalSection(&pTxt->csSync); 
 
    pTxt->iFirst            = 0;    /* Set the queue up to have 1 NULL line */ 
    pTxt->iCount            = 1; 
    pTxt->iTop              = 0; 
    pTxt->iLeft             = 0; 
    pTxt->MaxLen            = 0; 
    pTxt->iMaxLines         = iMaxLines; 
    pTxt->arLines[0].hText  = NULL; 
    pTxt->arLines[0].iLen   = 0; 
 
    hwnd = CreateWindow((LPSTR)achClass, (LPSTR)lpchName, dwStyle, x, y, 
        dx, dy, (HWND)hParent,     /* parent window */ 
    (HMENU)NULL,       /* use class menu */ 
    (HANDLE)hInstance, /* handle to window instance */ 
    (LPSTR)hTxt        /* used by WM_CREATE to set the window long */ 
    ); 
 
    if (!hwnd) { 
        return FALSE; 
    } 
 
    wprintfSetScrollRange(hwnd, FALSE); 
 
    /* Make window visible */ 
    ShowWindow(hwnd, SHOW_OPENWINDOW); 
    return hwnd; 
} 
 
 
/*****************************************************************************\ 
* SetPrintfFont (hwnd,hFont)                                                 
*                
* Sets the font for the printf window. 
*                                                                
* Arguments:                                                                  
*   HWND hwnd - Window handle of the printf window.                           
*   HFONT hFont - Font handle                                                   
*                                                                               
* Returns:                                                                    
*                                                                               
\*****************************************************************************/ 
 
VOID 
SetPrintfFont( 
    HWND hwnd, 
    HFONT hfont 
    ) 
{ 
    PTXT pTxt; 
    HDC hDC; 
    TEXTMETRIC tm; 
    HFONT hfontOld; 
 
    pTxt = *(HTXT)GetWindowLong(hwnd, 0); 
    pTxt->hFont = hfont; 
 
    /* Find out the size of a Char in the font */ 
    hDC = GetDC(hwnd); 
    hfontOld = SelectObject(hDC, hfont); 
    DeleteObject(hfontOld); 
    GetTextMetrics(hDC, &tm); 
    pTxt->Tdy = tm.tmHeight; 
    pTxt->Tdx = tm.tmAveCharWidth; 
    ReleaseDC(hwnd, hDC); 
 
    CalculatePrintfTabs(hfont); 
 
    InvalidateRect(hwnd, NULL, TRUE); 
    UpdateWindow(hwnd); 
} 
 
 
 
/*****************************************************************************\ 
* SetPrintfTabs                                                              
* 
* Sets the Tabstops in the printf window. 
*         
* Arguments: 
*    INT n - number of tabs to set. 
*    LPINT pTabs - arrays of tabstops 
* 
* Returns:                                                                    
*    VOID                                                                               
\*****************************************************************************/ 
 
VOID 
SetPrintfTabs( 
    INT n, 
    LPINT pTabs 
    ) 
{ 
    INT i; 
 
    nTabs = n; 
    for (i = 0; i < nTabs; i++) 
        tabs[i] = *pTabs++; 
} 
 
 
 
/*****************************************************************************\ 
* ClearPrintfWindow 
*                                                                               
* Clears all text from the window                                              
*                                                                              
* Arguments:                                                                  
*    HWND hwnd - window handle for the Degubing window                   
* 
* Returns: 
*    VOID                                                                               
\*****************************************************************************/ 
 
VOID 
ClearPrintfWindow( 
    HWND hwnd 
    ) 
{ 
    INT   i, iQueue; 
    PTXT  pTxt; 
    HTXT  hTxt; 
 
    if (hwnd != NULL && IsWindow(hwnd)) { 
        hTxt  = (HTXT)GetWindowLong(hwnd, 0); 
        pTxt = *hTxt; 
 
        EnterCrit(pTxt); 
 
        iQueue = TOP(pTxt); 
        for (i = 0; i < (pTxt)->iCount; i++, INC(pTxt, iQueue)) 
            if ((pTxt)->arLines[iQueue].hText != NULL) { 
                LocalFree ((HANDLE)pTxt->arLines[iQueue].hText); 
                pTxt->arLines[iQueue].hText = NULL; 
            } 
 
        pTxt->iFirst            = 0;  /* Set the queue up to have 1 NULL line */ 
        pTxt->iCount            = 1; 
        pTxt->iTop              = 0; 
        pTxt->iLeft             = 0; 
        pTxt->MaxLen            = 0; 
        pTxt->arLines[0].hText  = NULL; 
        pTxt->arLines[0].iLen   = 0; 
 
        wprintfSetScrollRange(hwnd, FALSE); 
        InvalidateRect(hwnd, NULL, TRUE); 
 
        LeaveCrit(pTxt); 
    } 
} 
 
 
/*****************************************************************************\ 
* PrintfWndProc( hwnd, uiMessage, wParam, lParam )                           
* 
* The window proc for the debugging window.  This processes all           
* of the window's messages.                                               
*                                                                               
* Arguments:                                                                  
*   HWND hwnd - window handle for the parent window                     
*   UINT uiMessage -message number                                          
*   WPARAM wParam - message-dependent                                       
*   LPARAM lParam - message-dependent                                       
*                                                                               
* Returns:                                                                    
*   0 if processed, nonzero if ignored                                      
\*****************************************************************************/ 
 
PUBLIC LONG APIENTRY 
PrintfWndProc( 
HWND   hwnd, 
UINT   uiMessage, 
WPARAM wParam, 
LONG   lParam 
) 
{ 
    PAINTSTRUCT rPS; 
    HTXT        hTxt; 
    PTXT        pTxt; 
 
    hTxt  = (HTXT)GetWindowLong(hwnd, 0); 
 
    if ( hTxt ) { 
        pTxt = *hTxt; 
    } 
 
    switch (uiMessage) { 
    case WM_CREATE: 
        { 
            /* set the WindowLong before any other message tries to 
                         * reference it during the create of a window 
                         */ 
            LPCREATESTRUCT csWindowLong = (LPCREATESTRUCT) lParam; 
 
            hTxt = (HTXT)csWindowLong->lpCreateParams; 
 
            SetWindowLong(hwnd, 0, (LONG)hTxt); 
            SetPrintfFont(hwnd, ghfontPrintf); 
            wprintfSetScrollRange(hwnd, FALSE); 
        } 
        break; 
 
    case WM_DESTROY: 
        { 
            INT i, iQueue; 
 
            EnterCrit(pTxt); 
 
            iQueue = TOP(pTxt); 
            for (i = 0; i < (pTxt)->iCount; i++, INC(pTxt, iQueue)) 
                if ((pTxt)->arLines[iQueue].hText != NULL) { 
                    LocalFree ((HANDLE)(pTxt)->arLines[iQueue].hText); 
                    pTxt->arLines[iQueue].hText = NULL; 
                } 
 
            LeaveCrit(pTxt); 
            // DeleteCriticalSection(&pTxt->csSync); 
 
            LocalFree((HANDLE)hTxt); 
            break; 
        } 
 
    case WM_SIZE: 
        EnterCrit(pTxt); 
        if (!iSem) { 
            wprintfSetScrollRange(hwnd, TRUE); 
        } 
        DebugVScroll(hwnd, pTxt, 0); 
        LeaveCrit(pTxt); 
        break; 
 
    case WM_VSCROLL: 
        EnterCrit(pTxt); 
 
        switch (LOWORD(wParam)) { 
        case SB_LINEDOWN: 
            DebugVScroll(hwnd, pTxt, 1); 
            break; 
        case SB_LINEUP: 
            DebugVScroll(hwnd, pTxt, -1); 
            break; 
        case SB_PAGEUP: 
            DebugVScroll(hwnd, pTxt, -LinesInDebugWindow(hwnd)); 
            break; 
        case SB_PAGEDOWN: 
            DebugVScroll(hwnd, pTxt, LinesInDebugWindow(hwnd)); 
            break; 
        case SB_THUMBTRACK: 
        case SB_THUMBPOSITION: 
            DebugVScroll(hwnd, pTxt, HIWORD(wParam) - pTxt->iTop); 
            break; 
        case SB_ENDSCROLL: 
            break; 
        } 
 
        LeaveCrit(pTxt); 
        break; 
 
    case WM_HSCROLL: 
        EnterCrit(pTxt); 
 
        switch (LOWORD(wParam)) { 
        case SB_LINEDOWN: 
            DebugHScroll (hwnd, pTxt, 1); 
            break; 
        case SB_LINEUP: 
            DebugHScroll (hwnd, pTxt, -1); 
            break; 
        case SB_PAGEUP: 
            DebugHScroll (hwnd, pTxt, -CharsInDebugWindow(hwnd)); 
            break; 
        case SB_PAGEDOWN: 
            DebugHScroll (hwnd, pTxt, CharsInDebugWindow(hwnd)); 
            break; 
        case SB_THUMBTRACK: 
        case SB_THUMBPOSITION: 
            DebugHScroll(hwnd, pTxt, HIWORD(wParam) - pTxt->iLeft); 
            break; 
        case SB_ENDSCROLL: 
            break; 
        } 
 
        LeaveCrit(pTxt); 
        break; 
 
    case WM_PAINT: 
        EnterCrit(pTxt); 
 
        BeginPaint(hwnd, (LPPAINTSTRUCT) & rPS); 
        DebugPaint(hwnd, &rPS); 
        EndPaint(hwnd, (LPPAINTSTRUCT) & rPS); 
 
        LeaveCrit(pTxt); 
        break; 
 
    case WM_KEYDOWN: 
        EnterCrit(pTxt); 
 
        switch (wParam) { 
        case VK_UP: 
            DebugVScroll(hwnd, pTxt, -1); 
            break; 
        case VK_DOWN: 
            DebugVScroll(hwnd, pTxt, 1); 
            break; 
        case VK_PRIOR: 
            DebugVScroll(hwnd, pTxt, -LinesInDebugWindow(hwnd)); 
            break; 
        case VK_NEXT: 
            DebugVScroll(hwnd, pTxt, LinesInDebugWindow(hwnd)); 
            break; 
        case VK_LEFT: 
            DebugHScroll(hwnd, pTxt, -1); 
            break; 
        case VK_RIGHT: 
            DebugHScroll(hwnd, pTxt, 1); 
            break; 
        } 
 
        LeaveCrit(pTxt); 
        break; 
 
    case WM_KEYUP: 
        break; 
 
    case WM_VWPRINTF: 
        return mwprintf( hwnd, (LPSTR)"%s", (LPSTR)wParam ); 
 
    default: 
        return DefWindowProc(hwnd, uiMessage, wParam, lParam); 
    } 
    return 0L; 
} 
 
 
/*****************************************************************************\ 
* DebugScroll 
*    
* Scrolls the debug window vertically 
* 
* Arguments: 
*    HWND hwnd - handle to the debug window 
*    PTXT pTxt - pointer to the text to scroll 
*    INT n - number of lines to scroll 
* 
* Returns: 
*    VOID 
\*****************************************************************************/ 
 
PRIVATE VOID 
DebugVScroll( 
HWND hwnd, 
PTXT pTxt, 
INT  n 
) 
{ 
    RECT rect; 
    INT  iMinPos, iMaxPos; 
 
    GetScrollRange(hwnd, SB_VERT, (LPINT) &iMinPos, (LPINT) &iMaxPos); 
    GetClientRect(hwnd, (LPRECT) &rect); 
    rect.left += OFFSETX; 
    rect.top  += OFFSETY; 
 
    n = BOUND(pTxt->iTop + n, iMinPos, iMaxPos) - pTxt->iTop; 
    if (n == 0) 
        return; 
 
    pTxt->iTop += n; 
    ScrollWindow(hwnd, 0, -n * pTxt->Tdy, (LPRECT) &rect, (LPRECT) &rect); 
    SetScrollPos(hwnd, SB_VERT, pTxt->iTop, TRUE); 
} 
 
/*****************************************************************************\ 
* DebugHScroll 
* 
* Performs the horizontal scroll calculations 
* 
* Arguments: 
*    HWND hwnd - handle to the debug window 
*    PTXT pTxt - pointer to the text to scroll 
*    INT n - number of characters to scroll 
* 
* Returns: 
*    VOID 
\*****************************************************************************/ 
 
PRIVATE VOID 
DebugHScroll( 
HWND hwnd, 
PTXT pTxt, 
INT  n 
) 
{ 
    RECT rect; 
    INT  iMinPos, iMaxPos; 
 
    GetScrollRange (hwnd, SB_HORZ, (LPINT) &iMinPos, (LPINT) &iMaxPos); 
    GetClientRect (hwnd, (LPRECT) & rect); 
    rect.left += OFFSETX; 
    rect.top  += OFFSETY; 
 
    n = BOUND(pTxt->iLeft + n, iMinPos, iMaxPos) - pTxt->iLeft; 
    if (n == 0) 
        return; 
 
    pTxt->iLeft += n; 
    ScrollWindow(hwnd, -n * pTxt->Tdx, 0, (LPRECT) & rect, (LPRECT) & rect); 
    SetScrollPos(hwnd, SB_HORZ, pTxt->iLeft, TRUE); 
} 
 
/*****************************************************************************\ 
* LinesInDebugWindow 
* 
* Calculates the number of lines in the debug window 
* 
* Arguments: 
*    HWND hwnd - handle to the debug window 
* 
* Returns: 
*    INT - number of lines in the debug window 
\*****************************************************************************/ 
 
PRIVATE INT 
LinesInDebugWindow ( 
HWND hwnd 
) 
{ 
    RECT CRect; 
    PTXT pTxt; 
 
    pTxt = *(HTXT)GetWindowLong(hwnd, 0); 
    GetClientRect(hwnd, &CRect); 
    if ( pTxt->Tdy == 0 ) { 
        return 0; 
    } 
    return pTxt ? (CRect.bottom - CRect.top - OFFSETY) / pTxt->Tdy : 0; 
} 
 
 
/*****************************************************************************\ 
* CharsInDebugWindow 
* 
* Calculates the number of characters horizontally in the debug window 
* 
* Arguments: 
*    HWND hwnd - handle to the debug window 
* 
* Returns: 
*    INT - number of horizontal characters in the debug window 
\*****************************************************************************/ 
 
PRIVATE INT 
CharsInDebugWindow ( 
HWND hwnd 
) 
{ 
    RECT CRect; 
    PTXT pTxt; 
 
    pTxt = *(HTXT)GetWindowLong (hwnd, 0); 
    GetClientRect(hwnd, (LPRECT) & CRect); 
    if ( pTxt->Tdx == 0 ) { 
        return 0; 
    } 
    return pTxt ? (CRect.right - CRect.left - OFFSETX) / pTxt->Tdx : 0; 
} 
 
PRIVATE INT 
mwprintf( 
    HWND hwnd, 
    LPSTR format, 
    ... 
    ) 
{ 
    va_list marker; 
    INT i; 
 
    va_start(marker, format); 
 
    i = vwprintf(hwnd, format, marker); 
 
    va_end(marker); 
 
    return i; 
} 
 
 
/*****************************************************************************\ 
* vwprintf 
* 
* variable printf - works like the C runtime printf 
* 
* Arguments: 
*    HWND hwnd - handle to the debug window 
*    LPSTR format - pointer to the format string 
*    va_list marker - pointer to marker 
* 
* Returns: 
*    INT - number of arguments printed 
\*****************************************************************************/ 
PUBLIC INT FAR cdecl 
vwprintf( 
HWND  hwnd, 
LPSTR format, 
va_list marker 
) 
{ 
    static HWND hwndLast = NULL; 
    static CHAR  pchBuf[MAXBUFLEN]; 
    RECT  rect, rcClient; 
    INT   iRet; 
    INT   cLinesDisplayed;       // lines of output to show 
    INT   cLinesFitInWindow;    // lines that can fit in the current window 
    INT   cLinesNew;  // how much left to scroll 
    PTXT  pTxt; 
    HTXT  hTxt; 
    BOOL  fNoScrollB; 
 
    if (hwnd == NULL) 
        hwnd = hwndLast; 
 
    if (hwnd == NULL || !IsWindow(hwnd)) 
        return 0;  /* exit if bad window handle */ 
 
    hwndLast = hwnd; 
 
    // 
    // First format the line and wait until we can play with the Txt structure 
    // 
    iRet = wvsprintf((LPSTR)pchBuf, format, marker); 
    hTxt = (HTXT)GetWindowLong(hwnd, 0); 
    pTxt = (PTXT)LocalLock((HANDLE)hTxt); 
    if(pTxt == NULL) 
        return 0;  // exit if bad memory block 
 
    EnterCrit(pTxt); 
 
    // 
    // Number of lines that we can display stuff in 
    // 
    cLinesFitInWindow   = LinesInDebugWindow(hwnd); 
 
    if (cLinesFitInWindow > pTxt->iMaxLines) { 
        fNoScrollB = TRUE; 
        cLinesFitInWindow = pTxt->iMaxLines; 
    } else { 
        fNoScrollB = FALSE; 
    } 
 
    // 
    // Number of lines actually displayed in the current window 
    // 
    cLinesDisplayed   = min(pTxt->iCount, cLinesFitInWindow); 
 
    // 
    // Return value is number of new lines to display 
    // 
    cLinesNew = InsertString(pTxt, pchBuf); 
 
    // 
    // Now make sure the new text is painted only if visable 
    // 
    GetClientRect(hwnd, (LPRECT) & rect); 
    rcClient = rect; 
 
    // 
    // Calculate how much of the window to invalidate 
    // 
    rect.top += (cLinesDisplayed - 1) * pTxt->Tdy; 
 
    InvalidateRect(hwnd, (LPRECT)&rect, TRUE); 
 
    // 
    // If we have more lines than we can display, scroll the window 
    // such that the last line printed is now at the bottom 
    // 
    if (cLinesDisplayed + cLinesNew > cLinesFitInWindow) { 
        cLinesNew = cLinesDisplayed + cLinesNew - cLinesFitInWindow; 
 
        if (fNoScrollB) { 
            rcClient.bottom = cLinesDisplayed * pTxt->Tdy; 
            ScrollWindow(hwnd, 0, -cLinesNew * pTxt->Tdy, (LPRECT) &rcClient, (LPRECT) &rcClient); 
        } else { 
            wprintfSetScrollRange(hwnd, FALSE); 
            DebugVScroll(hwnd, pTxt, cLinesNew); 
        } 
        LeaveCrit(pTxt); 
    } else { 
        LeaveCrit(pTxt); 
    } 
 
    LocalUnlock((HANDLE)hTxt); 
 
    return(iRet);       /* return the count of arguments printed */ 
} 
 
/*****************************************************************************\ 
* wprintfSetScrollRange 
* 
* Sets the scroll range of the debug window 
* 
* Arguments: 
*    HWND hwnd - handle to the debug window 
*    BOOL fRedraw - whether or not to redraw the window 
* 
* Returns: 
*    VOID 
\*****************************************************************************/ 
 
PRIVATE VOID 
wprintfSetScrollRange ( 
HWND hwnd, 
BOOL bRedraw 
) 
{ 
    PTXT pTxt; 
    INT  iRange; 
    INT  iLeftCritSect = 0; 
 
    iSem++; 
    pTxt = *(HTXT)GetWindowLong(hwnd, 0); 
 
    /* Update the scroll bars */ 
    iRange = pTxt->iCount - 1 - LinesInDebugWindow(hwnd); 
 
    if (iRange < 0) { 
        iRange = 0; 
        DebugVScroll(hwnd, pTxt, -pTxt->iTop); 
    } 
 
    while (GetCurrentThreadId() == (DWORD)pTxt->csSync.OwningThread){ 
LeaveCrit(pTxt); 
iLeftCritSect++; 
    } 
 
    SetScrollRange(hwnd, SB_VERT, 0, iRange, FALSE); 
    SetScrollPos(hwnd, SB_VERT, pTxt->iTop, bRedraw); 
 
    if(iLeftCritSect) { 
EnterCrit(pTxt); 
    } 
 
    iRange = pTxt->MaxLen - CharsInDebugWindow(hwnd) + 1; 
if (iRange < 0) { 
        iRange = 0; 
        DebugHScroll(hwnd, pTxt, -pTxt->iLeft); 
    } 
 
    if(iLeftCritSect) 
LeaveCrit(pTxt); 
 
    SetScrollRange(hwnd, SB_HORZ, 0, iRange, FALSE); 
    SetScrollPos(hwnd, SB_HORZ, pTxt->iLeft, bRedraw); 
 
    while (iLeftCritSect--) { 
EnterCrit(pTxt); 
    } 
 
    iSem--; 
} 
 
/*****************************************************************************\ 
* NewLine 
* 
* Calculates when a new line is needed in the debug window 
* 
* Arguments: 
*    PTXT pTxt - pointer to the text 
* 
* Returns: 
*    VOID 
\*****************************************************************************/ 
 
PRIVATE VOID 
NewLine ( 
PTXT pTxt 
) 
{ 
    INT iLast = LAST(pTxt); 
 
    if (pTxt->iCount == pTxt->iMaxLines) { 
        LocalFree ((HANDLE)pTxt->arLines[pTxt->iFirst].hText); 
        pTxt->arLines[pTxt->iFirst].hText = NULL; 
        INC (pTxt, pTxt->iFirst); 
        if (pTxt->iTop > 0) { 
            pTxt->iTop--; 
        } 
    } else { 
        pTxt->iCount++; 
    } 
    iLast = LAST(pTxt); 
    pTxt->arLines[iLast].hText = NULL; 
    pTxt->arLines[iLast].iLen  = 0; 
} 
 
/*****************************************************************************\ 
* InsertString 
* 
* Inserts a string into the debug window 
* 
* Arguments: 
*    PTXT pTxt - pointer to the text 
*    CHAR *str - pointer to insertion string 
* 
* Returns: 
*    INT - Line number at which string was inserted 
\*****************************************************************************/ 
 
PRIVATE INT 
InsertString ( 
PTXT  pTxt, 
CHAR  *str 
) 
{ 
    CHAR   pchBuf[MAXBUFLEN];        /* intermediate buffer */ 
    INT    iBuf; 
    INT    iLast = LAST(pTxt); 
    INT    cLine = 0; 
 
    for (iBuf = 0; iBuf < pTxt->arLines[iLast].iLen; iBuf++) 
        pchBuf[iBuf] = (*pTxt->arLines[iLast].hText)[iBuf]; 
 
    while (*str != '\0') { 
        while ((*str != '\n') && (*str != '\0')) 
            pchBuf[iBuf++] = *str++; 
 
        if (pTxt->arLines[iLast].hText != NULL) 
            LocalFree((HANDLE)pTxt->arLines[iLast].hText); 
 
        /* Test for the case of a zero length line, Only brian would do this */ 
 
        if (iBuf == 0) 
            pTxt->arLines[iLast].hText == NULL; 
        else { 
            if ((pTxt->arLines[iLast].hText = (CHAR **)LocalAlloc(LHND, iBuf)) 
                == NULL) { 
                return 0; 
            } 
        } 
 
        pTxt->arLines[iLast].iLen = iBuf; 
        while (--iBuf >= 0 ) 
            (*pTxt->arLines[iLast].hText)[iBuf] = pchBuf[iBuf]; 
 
        if (*str == '\n') {   /* Now do the next string after the \n */ 
            str++; 
            cLine++; 
            iBuf = 0; 
            NewLine(pTxt); 
            INC(pTxt, iLast); 
        } 
    } 
 
    return cLine; 
} 
 
/*****************************************************************************\ 
* CopyToClipboard 
* 
* Copies all lines to the clipboard in text format. 
* 
* Arguments: 
*   none 
* 
* Returns: 
*   TRUE if successful, FALSE if not. 
* 
\*****************************************************************************/ 
 
BOOL 
CopyToClipboard( 
    VOID 
    ) 
{ 
    PTXT pTxt; 
    INT iQueue; 
    INT cch; 
    INT i; 
    BOOL fSuccess = FALSE; 
    LPSTR pBuf = NULL; 
    LPSTR pb; 
 
    pTxt = *(HTXT)GetWindowLong(ghwndPrintf, 0); 
 
    EnterCrit(pTxt); 
 
    iQueue = FIRST(pTxt); 
    cch = 0; 
    for (i = 0; i < pTxt->iCount; i++, INC(pTxt, iQueue)) 
    { 
        if (pTxt->arLines[iQueue].hText != NULL) 
        { 
            // 
            // Count the characters in the line, plus room for the 
            // carriage return and newline. 
            // 
            cch += pTxt->arLines[iQueue].iLen; 
            cch += 2; 
        } 
    } 
 
    // 
    // Add one for the terminating null. 
    // 
    cch++; 
 
    if (!(pBuf = (LPSTR)GlobalAlloc(GMEM_DDESHARE, cch * sizeof(TCHAR)))) 
    { 
        LeaveCrit(pTxt); 
        return FALSE; 
    } 
 
    pb = pBuf; 
    iQueue = FIRST(pTxt); 
    for (i = 0; i < pTxt->iCount; i++, INC(pTxt, iQueue)) 
    { 
        if (pTxt->arLines[iQueue].hText != NULL) 
        { 
            lstrcpy(pb, *pTxt->arLines[iQueue].hText); 
            pb += pTxt->arLines[iQueue].iLen; 
            *pb++ = '\r'; 
            *pb++ = '\n'; 
        } 
    } 
 
    LeaveCrit(pTxt); 
 
    if (OpenClipboard(ghwndSpyApp)) 
    { 
        EmptyClipboard(); 
        fSuccess = SetClipboardData(CF_TEXT, pBuf) ? TRUE : FALSE; 
        CloseClipboard(); 
    } 
 
    return fSuccess; 
} 
 
/*****************************************************************************\ 
* IsPrintfEmpty 
* 
* Used to determine if the printf window is empty or not. 
* 
* Arguments: 
*   none 
* 
* Returns: 
*   TRUE if the printf window is empty, FALSE if there is at least 
*   one line in the window. 
* 
\*****************************************************************************/ 
 
BOOL 
IsPrintfEmpty( 
    VOID 
    ) 
{ 
    PTXT pTxt; 
 
    pTxt = *(HTXT)GetWindowLong(ghwndPrintf, 0); 
 
    // 
    // It is empty if the line count is zero (doesn't currently happen) 
    // or if there is only one line and it is NULL. 
    // 
    return (pTxt->iCount == 0 || 
        (pTxt->iCount == 1 && pTxt->arLines[FIRST(pTxt)].hText == NULL)) 
        ? TRUE : FALSE; 
}