/*=========================================================================
 *
 *  Copyright (C) 1994-1995 Microsoft Corporation.  All Rights Reserved.
 *
 *  File:       winwatch.c
 *  Content:	16-bit window watch code
 *  History:
 *   Date	By	Reason
 *   ====	==	======
 *   20-jun-95	craige	split out of DCIMAN WINWATCH.C, tweaked
 *   22-jun-95	craige	re-worked for clipper stuff
 *   02-jul-95	craige	comment out clipper notify stuff
 *
 ***************************************************************************/
#define _INC_DCIDDI
#include "ddraw16.h"
#undef _INC_DCIDDI

#ifdef CLIPPER_NOTIFY
static LPWINWATCH	lpWWList;
static HHOOK 		hNextCallWndProc;
static BOOL		bUnHook;

long FAR PASCAL _loadds CallWndProcHook(int hc, WPARAM wParam, LPARAM lParam);

extern HRGN FAR PASCAL InquireVisRgn(HDC hdc);
extern DWORD FAR PASCAL GetRegionData(HRGN hrgn, DWORD count, LPRGNDATA prd);

/*
 * doNotify
 */
void doNotify(LPWINWATCH pww, DWORD code)
{
    if( pww->lpCallback )
    {
	extern void DDAPI DD32_ClippingNotify( LPVOID, DWORD );
	DD32_ClippingNotify( pww->self32, code );
    }

} /* doNotify */

/*
 * DD16_WWOpen
 */
void DDAPI DD16_WWOpen( LPWINWATCH ptr )
{
    if( lpWWList == NULL )
    {
	DPF( 2, "Setting Windows Hook" );
	hNextCallWndProc = (HHOOK) SetWindowsHook( WH_CALLWNDPROC,
					(HOOKPROC) CallWndProcHook );
    }
    lpWWList = ptr;

} /* DD16_WWOpen */

/*
 * DD16_WWClose
 */
void DDAPI DD16_WWClose( LPWINWATCH pww, LPWINWATCH newlist )
{
    if( pww->prd16 != NULL )
    {
	LocalFree( (HLOCAL) pww->prd16 );
	pww->prd16 = NULL;
    }
    if( newlist == NULL )
    {
	DPF( 3, "Flagging to Unhook" );
	bUnHook = TRUE;
    }
    lpWWList = newlist;

} /* DD16_WWClose */

/*
 * DD16_WWNotifyInit
 */
void DDAPI DD16_WWNotifyInit(
		LPWINWATCH pww,
		LPCLIPPERCALLBACK lpcallback,
		LPVOID param )
{
    doNotify( pww, WINWATCHNOTIFY_STOP );

    pww->lpCallback = lpcallback;
    pww->lpContext = param;

    doNotify( pww, WINWATCHNOTIFY_START );
    doNotify( pww, WINWATCHNOTIFY_CHANGED );
    pww->fNotify = FALSE;

} /* DD16_WWNotifyInit */

/*
 * getWindowRegionData
 */
static DWORD getWindowRegionData( HWND hwnd, DWORD size, RGNDATA NEAR * prd )
{
    HDC 	hdc;
    DWORD	dw;

    hdc = GetDCEx( hwnd, NULL, DCX_USESTYLE | DCX_CACHE );
    dw = GetRegionData( InquireVisRgn( hdc ), size, prd );
    ReleaseDC( hwnd, hdc );
    return dw;

} /* getWindowRegionData */

/*
 * DD16_WWGetClipList
 */
DWORD DDAPI DD16_WWGetClipList(
		LPWINWATCH pww,
		LPRECT prect,
		DWORD rdsize,
		LPRGNDATA prd )
{   
    DWORD 	dw;
    DWORD    	size;

    /*
     *	we have to see if the intersect rect has changed.
     */
    if( prect )
    {
	if( !EqualRect(prect, &pww->rect) )
	{
	    pww->fDirty = TRUE;
	}
    }
    else
    {
	if( !IsRectEmpty( &pww->rect ) )
	{
	    pww->fDirty = TRUE;
	}
    }

    /*
     * if we are not dirty just return the saved RGNDATA
     */
    if( !pww->fDirty && pww->prd16 )
    {
	size = sizeof( *prd ) + (WORD) pww->prd16->rdh.nRgnSize;
	if( prd == NULL )
	{
	    return size;
	}
	if( rdsize < size )
	{
	    size = rdsize;
	}
	_fmemcpy( prd, pww->prd16, (UINT) size );
	return size;
    }

    /*
     * get the RGNDATA, growing the memory if we have to
     */
    dw = getWindowRegionData( (HWND) pww->hWnd, pww->dwRDSize, pww->prd16 );

    if( dw > pww->dwRDSize )
    {
	DPF( 2, "GetClipList: growing RGNDATA memory from %ld to %ld",pww->dwRDSize, dw);

	if( pww->prd16 != NULL )
	{
	    LocalFree((HLOCAL)pww->prd16);
	}

	/*
	 * allocate new space plus some slop
	 */
	pww->dwRDSize = dw + 8*sizeof(RECTL);
	pww->prd16 = (RGNDATA NEAR *) LocalAlloc(LPTR, (UINT)pww->dwRDSize);

	if( pww->prd16 == NULL )
	{
	    goto error;
	}

	dw = getWindowRegionData( (HWND) pww->hWnd, pww->dwRDSize, pww->prd16 );

	if( dw > pww->dwRDSize )
	{
	    goto error;
	}
    }

    /*
     *	now intersect the region with the passed rect
     */
    if( prect )
    {
	pww->rect = *prect;

	DPF( 2, "GetClipList: intersect with [%d %d %d %d]", *prect);
	ClipRgnToRect( (HWND) pww->hWnd, prect, pww->prd16 );
    }
    else
    {
	SetRectEmpty( &pww->rect );
    }

    pww->fDirty = FALSE;

    size = sizeof( *prd ) + (WORD) pww->prd16->rdh.nRgnSize;
    if( prd == NULL )
    {
	return size;
    }
    if( rdsize < size )
    {
	size = rdsize;
    }
    _fmemcpy( prd, pww->prd16, (UINT) size );
    return size;

error:
    pww->dwRDSize = 0;
    return 0;

} /* DD16_WWGetClipList */

/****************************************************************************
 *                                                                          *
 * ROUTINES TO HANDLE NOTIFICATION MESSAGES FOLLOW                          *
 *                                                                          *
 ****************************************************************************/

/*
 * handleWindowDestroyed
 */
static void handleWindowDestroy( HWND hwnd )
{
    LPWINWATCH	pww;

    DPF( 2, "*** handleWindowDestroy: hwnd=%08lx", hwnd );

again:
    for( pww=lpWWList; pww; pww=pww->next16 )
    {
	if( (hwnd == NULL) || ((HWND)pww->hWnd == hwnd) )
	{
	    extern void DDAPI DD32_WWClose( DWORD );

	    doNotify( pww, WINWATCHNOTIFY_DESTROY );
	    DD32_WWClose( (DWORD) pww->self32 );
	    goto again;
	}
    }

} /* handleWindowDestroy */

/*
 * handleWindowPosChanged
 */
static void handleWindowPosChanged( HWND hwnd )
{
    LPWINWATCH	pww;
    LPWINWATCH	next;
    RECT 	rect;
    RECT	rectT;

    DPF( 20, "*** handleWindowPosChanged: hwnd=%08lx", hwnd );

    /*
     * get the screen rect that changed
     */
    GetWindowRect( hwnd, &rect );

    /*
     * send message to each notify routine
     */
    pww = lpWWList;
    while( pww != NULL )
    {
        next = pww->next16;

	GetWindowRect((HWND)pww->hWnd, &rectT);

	if( IntersectRect( &rectT, &rectT, &rect ) )
	{
	    pww->fNotify = TRUE;
	    pww->fDirty = TRUE;
	}

	if( pww->fNotify && pww->lpCallback )
	{
	    DPF( 20, "clip changed %04X [%d %d %d %d]", (HWND)pww->hWnd, rectT);
	    doNotify( pww, WINWATCHNOTIFY_CHANGED );
	    pww->fNotify = FALSE;
	}
	pww = next;
    }

} /* handleWindowPosChanged */

/*
 * sendChanging
 */
static void sendChanging( LPRECT prect )
{
    LPWINWATCH	pww;
    LPWINWATCH	next;
    RECT 	rectT;

    pww = lpWWList;
    while( pww != NULL )
    {
        next = pww->next16;

        GetWindowRect( (HWND)pww->hWnd, &rectT );

        if( IntersectRect(&rectT, &rectT, prect) )
        {
            pww->fDirty = TRUE;

            if( pww->lpCallback )
            {
                DPF( 20, "clip changing %04X [%d %d %d %d]", (HWND)pww->hWnd, rectT);
                doNotify( pww, WINWATCHNOTIFY_CHANGING );
                pww->fNotify = TRUE;
                pww->fDirty = TRUE;
            }
        }

	pww = next;
    }

} /* sendChanging */

/*
 * handleWindowPosChanging
 */
static void handleWindowPosChanging( HWND hwnd, LPWINDOWPOS pwinpos )
{
    RECT	rect;
    RECT	rect_win;
    int		cx;
    int		cy;

    /*
     *  get the current screen rect.
     */
    DPF( 20, "*** handleWindowPosChanging: hwnd=%08lx", hwnd );
    GetWindowRect( hwnd, &rect_win);
    rect = rect_win;

    if( pwinpos == NULL )
    {
        sendChanging( &rect );
        return;
    }

    /*
     * compute the new rect
     */
    if( pwinpos->flags & SWP_NOSIZE )
    {
        cx = rect.right - rect.left;
        cy = rect.bottom - rect.top;
    }
    else
    {
        cx = pwinpos->cx;
        cy = pwinpos->cy;
    }

    if( !(pwinpos->flags & SWP_NOMOVE) )
    {
        rect.left = pwinpos->x;
        rect.top  = pwinpos->y;

        if( GetWindowLong(hwnd, GWL_STYLE) & WS_CHILD )
        {
            ClientToScreen(GetParent(hwnd), (LPPOINT)&rect);
        }
    }

    rect.right = rect.left + cx;
    rect.bottom = rect.top + cy;

    /*
     * only send changing if we are really changing... if the new
     * rect is the same as the old rect, then check if something else
     * interesting is happening...
     */
    if( EqualRect( &rect, &rect_win ) )
    {
        if( !(pwinpos->flags & SWP_NOZORDER) )
	{
            sendChanging(&rect);
	}

        if( pwinpos->flags & (SWP_SHOWWINDOW|SWP_HIDEWINDOW) )
	{
            sendChanging(&rect);
	}
    }
    else
    {
        sendChanging( &rect_win );
        sendChanging( &rect );
    }

} /* handleWindowPosChanging */

/*
 * checkScreenLock
 */
static void checkScreenLock( void )
{
    static BOOL bScreenLocked;
    BOOL 	is_locked;
    HDC		hdc;
    RECT 	rect;

    hdc = GetDC(NULL);
    is_locked = (GetClipBox(hdc, &rect) == NULLREGION);
    ReleaseDC(NULL, hdc);

    if( is_locked != bScreenLocked )
    {
	/*
	 * pretend the desktop window has moved, this will cause
	 * all WINWATCH handles to be set to dirty, and also be notified.
	 */
	handleWindowPosChanging( GetDesktopWindow(), NULL );
        handleWindowPosChanged( GetDesktopWindow() );
	bScreenLocked = is_locked;
    }

} /* checkScreenLock */

/*
 * CallWndProcHook
 */
long FAR PASCAL _loadds CallWndProcHook( int hc, WPARAM wParam, LPARAM lParam )
{
    /* reversed MSG minus time and pt */
    typedef struct
    {
	LONG	lParam;
	WORD	wParam;
	WORD	message;
	HWND	hwnd;
    } MSGR, FAR *LPMSGR;

    LPMSGR	lpmsg;
    LPWINDOWPOS	lpwinpos;
    long	rc;

    if( hc == HC_ACTION )
    {
	lpmsg = (MSGR FAR *)lParam;

	switch( lpmsg->message )
	{
	case WM_DESTROY:
	    handleWindowDestroy( lpmsg->hwnd );
	    break;

	case WM_WINDOWPOSCHANGING:
	    lpwinpos = (LPWINDOWPOS) lpmsg->lParam;
	    handleWindowPosChanging( lpwinpos->hwnd, lpwinpos );
	    break;

	case WM_WINDOWPOSCHANGED:
	    lpwinpos = (LPWINDOWPOS) lpmsg->lParam;
	    handleWindowPosChanged( lpwinpos->hwnd );
	    break;

	case WM_EXITSIZEMOVE:
	    checkScreenLock();
	    break;
	case WM_ENTERSIZEMOVE:
	    checkScreenLock();
	    break;
	}
    }

    rc = DefHookProc(hc, wParam, lParam, &(HOOKPROC)hNextCallWndProc);
    if( bUnHook )
    {
	DPF( 2, "Unhooking WindowsHook" );
	UnhookWindowsHook( WH_CALLWNDPROC, (HOOKPROC)CallWndProcHook );
	bUnHook = FALSE;
    }
    return rc;

} /* CallWndProcHook */
#endif