//***************************************************************************
//
//	cover.cpp -- UCD to hide background windows
//
//	Copyright 1995, 1996, Dietrich Schultz
//  
//  Built with Microsoft Visual C++ 1.52 (16-bit) and 2.2 (32-bit)
//
//  Change History:
//
//	2/15/95 - dws
//
//		Initial version.
//
//	10/2/95 - dws
//
//		Due to popular demand, modified Cover to NOT automatically delete
//		the cover window when the presentation window closes. While 
//		useful during authoring (I thought) it didn't work well during
//		jumpfiles because the cover would be deleted during the jumpfile.
//		Define AUTO_UNCOVER to build it the old way.
//
//	2/25/96 - dws
//
//		Added Win32 support and compiled as a C++ file.
//
//***************************************************************************

#define STRICT
#include <windows.h>
#include "apwpost.h"
  
#ifdef _WIN32
#define DLLENTRY __declspec(dllexport) WINAPI
#else	// win16
#define	DLLENTRY	__export FAR PASCAL
#endif
  
// Define AUTO_UNCOVER to have the cover go away whenever the presentation 
// window or the course is closed.
//#define AUTO_UNCOVER

static HINSTANCE	ghinst;
static char			szclass[] = "CoverClass";
static BOOL			gBumpedRefCount = FALSE;

LRESULT DLLENTRY CoverWndProc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam );
HWND DoCover( void );
BOOL DllInit( HINSTANCE hInst );
LONG AWCreateObject( HWND hWnd, LPRECT rBounds, int nPort, LPSTR lpszProcName, 
					LPSTR lpszModuleFileName, LPVOID lpData, WORD nDataSize );

// The following routines are referenced by name from APW so nix the mangling.
extern "C" {
long DLLENTRY Cover( void );
void DLLENTRY Uncover( void );
long DLLENTRY ObjHandler( APWN_AB_PTR args, WORD msg );
}


#ifdef _WIN32
BOOL DLLENTRY DllMain( HINSTANCE hModule, DWORD fdwReason, LPVOID lpReserved )
{
	BOOL result = TRUE;

	switch (fdwReason)
	{
	case DLL_PROCESS_ATTACH:
		result = DllInit(hModule);
		break;

	case DLL_THREAD_ATTACH:
		break;

	case DLL_THREAD_DETACH:
		break;

	case DLL_PROCESS_DETACH:
		break;
	}

	return result;   // only relevant for DLL_PROCESS_ATTACH
}
#else                   
int DLLENTRY LibMain( HINSTANCE hInst, WORD wDataSeg, WORD cbHeapSize, LPSTR lpszCmdLine )
/*
	Called when the DLL is loaded in Win16. 
*/
{
	return DllInit(hInst);
}
#endif // _WIN32   


BOOL DllInit( HINSTANCE hInst )
/*
	Common routine for initializing the DLL. Called from DllMain in Win32 and 
	LibMain in Win16. Save our instance handle and register our window class.
*/
{
	WNDCLASS	wc;
	  
	ghinst = hInst;	
	
	wc.style 			= 0;
    wc.lpfnWndProc		= CoverWndProc;
    wc.cbClsExtra		= 0;
    wc.cbWndExtra		= 0;
    wc.hInstance		= hInst;
    wc.hIcon			= NULL;
    wc.hCursor			= LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground	= (HBRUSH)GetStockObject(BLACK_BRUSH);
    wc.lpszMenuName		= NULL;
    wc.lpszClassName	= szclass;
	
	return RegisterClass(&wc) ? TRUE : FALSE;
}

    
BOOL DLLENTRY find_cover( HWND hwnd, LPARAM lparam )
/*
	EnumTaskWindows callback. Used to find a cover window belonging
	to this task. When it finds one, the window handle is returned
	in lparam (which must a far pointer to an HWND).
*/
{
	char	class_name[16];
	
	GetClassName(hwnd, class_name, sizeof(class_name));
	if (lstrcmp(class_name, szclass) == 0)
	{
		*(HWND FAR *)lparam = hwnd;
		return FALSE; // found it, stop scanning
	}
	return TRUE;
}
     
     
BOOL DLLENTRY find_app_wnd( HWND hwnd, LPARAM lparam )
/*
	EnumTaskWindows callback. Used to locate the last (bottom-most) window 
	that belongs to this task. The window handle is returned in lparam 
	(which must be a far pointer to an HWND).
*/
{
	*(HWND FAR *)lparam = hwnd;
	return TRUE;	// enumerate them all
}
        
 
#ifdef _DEBUG    
BOOL DLLENTRY list_wnds( HWND hwnd, LPARAM lparam )
/*
	EnumTaskWindows callback. Lists windows to debug output. 
*/
{
	char	buf1[256], buf2[32];
	
	GetClassName(hwnd, buf2, sizeof(buf2));
	wsprintf(buf1, "0x%04lx (%s)\n", (DWORD)(WORD)hwnd, buf2); 
	OutputDebugString(buf1);
	return TRUE;	// enumerate them all
}
#endif // _DEBUG


long DLLENTRY Cover( void )
/*  
	User code entry point to create a cover window behind the application.
	This routine creates an invisible object in the APW display list. The 
	object handler actually creates the cover window.
*/
{
#ifdef AUTO_UNCOVER
 	char moduleName[256];
 	RECT rect;
 	long objID;
			 	
	GetModuleFileName(ghinst, moduleName, sizeof(moduleName));
	SetRect(&rect, 0, 0, 0, 0);
	objID = AWCreateObject(AWGetWindow(), &rect, SCREEN_PORT, 
				"ObjHandler", moduleName, NULL, 0);
	return objID;
#else
	// Bump our library ref count so we're not unloaded when the course file
	// is closed during jumpfiles. If we're unloaded the cover window is
	// also deleted.
	if (!gBumpedRefCount)
	{
 		char moduleName[256];
 		
 		GetModuleFileName(ghinst, moduleName, sizeof(moduleName));
		#ifdef _WIN32
		if (LoadLibrary(moduleName) != NULL)
		#else // win16
 		if (LoadLibrary(moduleName) > HINSTANCE_ERROR)
		#endif
 			gBumpedRefCount = TRUE;
 	}

	DoCover();
	return 0;
#endif
}


HWND DoCover( void )
/*  
	Create the cover window and insert it behind all other top-level task 
	windows. Does nothing if the cover window exists already. Returns the 
	cover window handle or NULL if it failed.
*/
{
	#ifdef _WIN32
	DWORD	htask		= GetCurrentThreadId();
	#else
	HTASK	htask 		= GetCurrentTask();
	#endif
	HWND	cover_wnd	= NULL;
	
	// See if the cover window exists already.
	EnumTaskWindows(htask, find_cover, (LPARAM)(LPVOID)&cover_wnd);
	if (cover_wnd == NULL)
	{   
		// Find the rear-most application window. The cover window will be 
		// placed immediately behind it.
		HWND app_wnd = NULL;
		EnumTaskWindows(htask, find_app_wnd, (LPARAM)(LPVOID)&app_wnd);
		if (app_wnd)
		{
			cover_wnd = CreateWindow(szclass, "Cover", 
							WS_POPUP|WS_MAXIMIZE|WS_DISABLED,
					 		0, 0, 0, 0, HWND_DESKTOP, NULL, ghinst, NULL);
			if (cover_wnd)
			{   
				// Position it behind the app window and show it. (Use 
				// SetWindowPos to show the window because using 
				// ShowWindow(SW_SHOWNA) brings it to the front.)
				SetWindowPos(cover_wnd, app_wnd, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE);
				SetWindowPos(cover_wnd, NULL, 0, 0, 0, 0, SWP_SHOWWINDOW|SWP_NOZORDER|SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE);
			}
		}
	}	
	return cover_wnd;
}


void DLLENTRY Uncover( void )
/*
	Remove the cover window. Does nothing if there is no cover window.
*/
{
	#ifdef _WIN32
	DWORD	htask		= GetCurrentThreadId();
	#else
	HTASK	htask 		= GetCurrentTask();
	#endif
	HWND	cover_wnd	= NULL;

#ifndef AUTO_UNCOVER	
	// Decrement our ref count when we uncover.
	if (gBumpedRefCount)
	{
 		FreeLibrary(ghinst);
 		gBumpedRefCount = FALSE;
 	}
#endif

	EnumTaskWindows(htask, find_cover, (LPARAM)(LPVOID)&cover_wnd);
	if (cover_wnd)
		DestroyWindow(cover_wnd);
}


LRESULT DLLENTRY CoverWndProc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam )
/*
	Window procedure for the cover window. It's only function is to suppress 
	the beep you'd normally get when clicking on a disabled window. We still 
	allow WM_MOUSEMOVE to get through to the DefWindowProc so that the cursor 
	is properly set.
*/
{
	if (msg == WM_SETCURSOR && LOWORD(lparam) == HTERROR && HIWORD(lparam) != WM_MOUSEMOVE)
		return 0;	// suppresses beep
	return DefWindowProc(hwnd, msg, wparam, lparam);
}


#ifdef AUTO_UNCOVER                                    
long DLLENTRY ObjHandler( APWN_AB_PTR args, WORD msg )
/*
	APW object handler. 
*/
{
	switch (msg)
	{
	case APWN_INIT:
	case APWN_WINDOWOPEN:
		
		// Create the cover window on initialization and when
		// the presentation window is opened. The latter case is 
		// common during authoring and happens every time the presentation 
		// window is (re)opened.
        
        DoCover();
		break;

	case APWN_WINDOWCLOSE:
 	case APWN_DESTROY:

		// Presentation window is closing or we are being destroyed. Get rid 
		// of the cover window.
        
        Uncover();
		break;
 	}

	return 0;
}
#endif // AUTO_UNCOVER


#ifdef AUTO_UNCOVER                                    
LONG AWCreateObject( HWND hWnd, LPRECT rBounds, int nPort, 
								LPSTR lpszProcName, LPSTR lpszModuleFileName, 
								LPVOID lpData, WORD nDataSize )
/*
	Utility routine to post an object on the Authorware display list.
*/
{
	APWC_POSTPB	post;
	
	post.size			= sizeof(APWC_POSTPB);
	post.dll_ptr		= lpszModuleFileName;
	post.function_ptr	= lpszProcName;
	post.rect			= *rBounds;
	post.port			= nPort;
	post.data_ptr		= lpData;
	post.data_size		= nDataSize;

	return SendMessage(hWnd, APWC_POST, 0, (LONG)(APWC_POSTPB_PTR)&post);
}
#endif // AUTO_UNCOVER
