/****************************** Module Header ******************************\
* Module Name: accrare.c
*
* Copyright (c) 1985-95, Microsoft Corporation
*
* History:
* 12-18-95 a-jimhar 	Created based on rare.c
\***************************************************************************/


#include "Access.h"

#define REGSTR_PATH_APPEARANCE	__TEXT("Control Panel\\Appearance")
#define REGSTR_PATH_LOOKSCHEMES __TEXT("Control Panel\\Appearance\\Schemes")
#define REGSTR_VAL_CUSTOMCOLORS __TEXT("CustomColors")

#define APPEARANCESCHEME 	    REGSTR_PATH_LOOKSCHEMES
#define DEFSCHEMEKEY			REGSTR_PATH_APPEARANCE
#define DEFSCHEMENAME			__TEXT("Current")

#define HIGHCONTRASTSCHEME      __TEXT("High Contrast Scheme")
#define CURHCSCHEME		    	__TEXT("Volital HC Scheme")
#define PRE_HC_SCHEME			__TEXT("Pre-High Contrast Scheme")

#define REGSTR_PATH_SERIALKEYS  __TEXT("Control Panel\\Accessibility\\SerialKeys")
#define REGSTR_VAL_ACTIVEPORT   __TEXT("ActivePort")
#define REGSTR_VAL_BAUDRATE     __TEXT("BaudRate")
#define REGSTR_VAL_FLAGS        __TEXT("Flags")
#define REGSTR_VAL_PORTSTATE    __TEXT("PortState")

#define MAXSCHEMENAME 100

#ifndef COLOR_MAX
#define COLOR_MAX 25
#endif

// structure used to store a scheme in the registry
#ifdef UNICODE
#   define SCHEME_VERSION 2        // Ver 2 == Unicode
#else
#   define SCHEME_VERSION 1        // Ver 1 == Win95 ANSI
#endif

typedef struct {
	int version;
    NONCLIENTMETRICS ncm;
    LOGFONT lfIconTitle;
    COLORREF rgb[COLOR_MAX];
} SCHEMEDATA;


#define PMAP_STICKYKEYS 	   __TEXT("Control Panel\\Accessibility\\StickyKeys")
#define PMAP_KEYBOARDRESPONSE  __TEXT("Control Panel\\Accessibility\\Keyboard Response")
#define PMAP_MOUSEKEYS		   __TEXT("Control Panel\\Accessibility\\MouseKeys")
#define PMAP_TOGGLEKEYS 	   __TEXT("Control Panel\\Accessibility\\ToggleKeys")
#define PMAP_TIMEOUT		   __TEXT("Control Panel\\Accessibility\\TimeOut")
#define PMAP_SOUNDSENTRY	   __TEXT("Control Panel\\Accessibility\\SoundSentry")
#define PMAP_SHOWSOUNDS 	   __TEXT("Control Panel\\Accessibility\\ShowSounds")

#define ISACCESSFLAGSET(s, flag) ((s).dwFlags & flag)

#define SK_SPI_INITUSER -1

typedef int (*PSKEY_SPI)(
	UINT uAction, 
	UINT uParam, 
	LPSERIALKEYS lpvParam, 
	BOOL fuWinIni);


/***************************************************************************
 * GetRegValue
 *
 * Passed the key, and the identifier, return the string data from the
 * registry.
 ***************************************************************************/
 long GetRegValue(LPTSTR RegKey, LPTSTR RegEntry, LPTSTR RegVal, long Size)
 {
	HKEY  hReg; 							// Registry handle for schemes
	DWORD Type; 							// Type of value
	long retval;


	retval = RegCreateKey(HKEY_CURRENT_USER, RegKey, &hReg);
	if (retval != ERROR_SUCCESS)
		return retval;

	retval = RegQueryValueEx(hReg,
						RegEntry,
						NULL,
						(LPDWORD)&Type,
						(LPBYTE)RegVal,
						&Size);

	RegCloseKey(hReg);
	return retval;
 }

/***************************************************************************
 * SetRegValue
 *
 * Passed the key, and the identifier, set the string data from the
 * registry.
 ***************************************************************************/
 long SetRegValue(LPTSTR RegKey, LPTSTR RegEntry, LPVOID RegVal, long Size, DWORD Type)
 {
	HKEY  hReg; 							// Registry handle for schemes
	DWORD Reserved = 0;
	long retval;

	if (RegCreateKey(HKEY_CURRENT_USER,RegKey, &hReg) != ERROR_SUCCESS)
		return 0;

	// A common error is to omit the `+1', so we just smash the correct
	// value into place regardless.
	if (Type == REG_SZ)
		Size = (lstrlen(RegVal) + 1) * sizeof(TCHAR);

	retval = RegSetValueEx(hReg,
				  RegEntry,
				  0,
				  Type,
				  RegVal,
				  Size);


	RegCloseKey(hReg);
	return retval;
 }


/***************************************************************************
 * DelRegValue
 *
 * Passed the key and the subkey, delete the subkey.
 *
 ***************************************************************************/
long DelRegValue(LPTSTR RegKey, LPTSTR RegEntry)
 {
	HKEY  hReg; 							// Registry handle for schemes
	DWORD Reserved = 0;
	long retval;

	retval = RegCreateKey(HKEY_CURRENT_USER,RegKey, &hReg);
	if (retval != ERROR_SUCCESS)
		return retval;

	retval = RegDeleteValue(hReg,
						RegEntry);

	RegCloseKey(hReg);
	return retval;
 }


/***************************************************************************
 *
 * GetCurrentSchemeName
 *
 *	Input:	szBuf -> Buffer to receive name of scheme (MAXSCHEMENAME)
 * Output:	None
 *
 *	Returns the name of the current scheme.  If the current scheme does not
 *	have a name, create one (ID_PRE_HC_SCHEME).
 *
 *	If anything goes wrong, tough.	What can you do?
 *
 ***************************************************************************/

void NEAR PASCAL GetCurrentSchemeName(LPTSTR szBuf)
{
	if (GetRegValue(DEFSCHEMEKEY, DEFSCHEMENAME, szBuf, MAXSCHEMENAME*sizeof(TCHAR)) !=
			ERROR_SUCCESS) {
		SCHEMEDATA scm;
		int i;

		/* Load the current scheme into scm */
		scm.version = 1;
		scm.ncm.cbSize = sizeof(NONCLIENTMETRICS);
		SystemParametersInfo(SPI_GETNONCLIENTMETRICS,
								 sizeof(NONCLIENTMETRICS),
								 &scm.ncm,
								 0);

		SystemParametersInfo(SPI_GETICONTITLELOGFONT,
								 sizeof(LOGFONT),
								 &scm.lfIconTitle,
								 0);

		for (i = 0; i < COLOR_MAX; i++) {
			scm.rgb[i] = GetSysColor(i);
		}

		/* Now give it a name */
		SetRegValue(APPEARANCESCHEME, PRE_HC_SCHEME, &scm, sizeof(scm), REG_BINARY);
	}
}


/***************************************************************************
 *
 *
 * SetCurrentSchemeName
 *
 * Input: szName -> name of scheme to become current
 * Output: Boolean success/failure
 *
 ***************************************************************************/

typedef LONG (CALLBACK *APPLETPROC)(HWND, UINT, LPARAM, LPARAM);
typedef BOOL (CALLBACK *SETSCHEME)(LPCTSTR);
typedef BOOL (CALLBACK *SETSCHEMEA)(LPCSTR);

BOOL NEAR PASCAL SetCurrentSchemeName(LPCTSTR szName)
{
	BOOL fRc = FALSE;
	HINSTANCE hinst = LoadLibrary(__TEXT("DESK.CPL"));

	if (NULL != hinst) 
    {
		APPLETPROC ap = (APPLETPROC)GetProcAddress((HMODULE)hinst, "CPlApplet");
		if (ap) 
        {
			if (ap(0, CPL_INIT, 0, 0)) 
            {

#ifdef UNICODE
				SETSCHEME ss =
						(SETSCHEME)GetProcAddress(hinst, "DeskSetCurrentSchemeW");
				if (ss) 
                {
					fRc = ss(szName);
				}
				else
				{
					SETSCHEMEA ssa = 
                            (SETSCHEMEA)GetProcAddress(hinst, "DeskSetCurrentScheme");
					if (ssa) 
                    {
                        int cch = 0;
                        LPSTR pszName = NULL;
                            
                        WideCharToMultiByte( CP_ACP, 0, szName, -1, pszName, cch, NULL, NULL);
                        if (0 != cch)
                        {
                            pszName = LocalAlloc(LMEM_FIXED, cch);
                            if (NULL != pszName)
                            {
                                WideCharToMultiByte(CP_ACP, 0, szName, -1, pszName, cch, NULL, NULL);

			                    fRc = ssa(pszName);

                                LocalFree(pszName);
                            }
                        }
					}

				}

#else
				SETSCHEME ss =
						(SETSCHEME)GetProcAddress(hinst, "DeskSetCurrentScheme");
				if (ss) {
					fRc = ss(szName);
				}
#endif
				ap(0, CPL_EXIT, 0, 0);
			}
		}
		FreeLibrary(hinst);
	}
	return fRc;
}


/***************************************************************************
 *
 *
 * SetHighContrast
 *
 * Input: None
 * Output: None
 *
 *	Outline:
 *
 ***************************************************************************/

short FAR PASCAL SetHighContrast(LPTSTR lpszScheme, BOOL fEnabledOld)
{
	BOOL fOk = 0;
	TCHAR szBuf[MAXSCHEMENAME];

	szBuf[0] = '\0';

	if (!fEnabledOld)
	{
		GetCurrentSchemeName(szBuf);		/* Creates a name if necessary */
		SetRegValue(HC_KEY, PRE_HC_SCHEME, szBuf, 0, REG_SZ); /* Save it */
	}

	if (NULL != lpszScheme && '\0' != *lpszScheme)
	{
		lstrcpy(szBuf, lpszScheme);
	}
	else
	{
	   /*
		*  Get the name of the HC scheme.  By stupid design, we have to look
		*  in about fifty places...  We get the default one first, then try
		* to get better and better ones.  That way, when we're done, we have
		*  the best one that succeeded.
		*/
		lstrcpy(szBuf, WHITEBLACK_HC);
		GetRegValue(HC_KEY, HIGHCONTRASTSCHEME, szBuf, sizeof(szBuf));
		GetRegValue(DEFSCHEMEKEY, CURHCSCHEME, szBuf, sizeof(szBuf));
	}
	fOk = SetCurrentSchemeName(szBuf);

	return (short)fOk;
}


/***************************************************************************
 *
 *
 * ClearHighContrast
 *
 * Input: None
 * Output: None
 *
 *	Outline:
 *
 *		If high contrast is currently on:
 *
 *			Get the PRE_HC_SCHEME.
 *
 *			If able to get it:
 *
 *				Make it the current scheme.
 *
 *				If the name is IDS_PRE_HC_SCHEME, then delete the scheme
 *				data and set the current scheme name to null.  (Clean up.)
 *
 *			End if
 *
 *			Set the key that says that high contrast is now off.
 *
 *		End if
 *
 ***************************************************************************/

BOOL FAR PASCAL ClearHighContrast(BOOL fEnabledOld)
{
	BOOL fOk = FALSE;
	TCHAR szBuf[MAXSCHEMENAME];
	TCHAR szPreHc[MAXSCHEMENAME];
	
	szBuf[0] = '\0';
	if (fEnabledOld) {		/* Currently on */
		if (ERROR_SUCCESS ==
			GetRegValue(HC_KEY, PRE_HC_SCHEME, szBuf, sizeof(szBuf)) ) {

			fOk = SetCurrentSchemeName(szBuf);

			if (lstrcmpi(szBuf, PRE_HC_SCHEME) == 0) {
				DelRegValue(APPEARANCESCHEME, szPreHc);
				DelRegValue(DEFSCHEMEKEY, DEFSCHEMENAME);
			}
		}
	}

	return fOk;
}


BOOL FAR PASCAL DoHighContrast(LPHIGHCONTRAST phc)
{
    BOOL fRc = 0;
	DWORD dwType;
	long lRet;
	HKEY hkey;
	DWORD dwDisposition;
	DWORD dwFlagsOld = 0;
	DWORD cbData;
	TCHAR szOldScheme[MAXSCHEMENAME];

	lRet = RegCreateKeyEx(
		HKEY_CURRENT_USER, // handle of an open key
		HC_KEY, 		   // address of subkey name
		0,				   // reserved
		NULL,			   // address of class string
		0,				   // special options flag
		KEY_READ | KEY_WRITE, // desired security access
		NULL,			   // address of key security structure
		&hkey,			   // address of buffer for opened handle
		&dwDisposition);   // address of disposition value buffer

	if (ERROR_SUCCESS == lRet) {
		cbData = sizeof(szOldScheme);
		RegQueryValueEx(
			hkey,
			HIGHCONTRAST_SCHEME,
			NULL,
			&dwType,
			(PBYTE)szOldScheme,
			&cbData);

		dwFlagsOld = RegQueryStrDW(
			0,
			HKEY_CURRENT_USER, 
			HC_KEY, 
			REGSTR_VAL_FLAGS);

		if (phc->dwFlags & HCF_HIGHCONTRASTON) {
			fRc = SetHighContrast(
				phc->lpszDefaultScheme,
				(dwFlagsOld & HCF_HIGHCONTRASTON));
		}
		else
		{
			fRc = ClearHighContrast(dwFlagsOld & HCF_HIGHCONTRASTON);
		}
		if (fRc) {
			RegSetStrDW(
				HKEY_CURRENT_USER, 
				HC_KEY, 
				REGSTR_VAL_FLAGS, 
				phc->dwFlags);
		}

		RegCloseKey(hkey);
	}
	return fRc;
}

/****************************************************************************/

BOOL AccessSKeySystemParametersInfo(
	UINT uAction, 
	UINT uParam, 
	LPSERIALKEYS psk, 
	BOOL fu)
{
	BOOL fRet = FALSE;
    static PSKEY_SPI s_pSKEY_SystemParametersInfo =NULL;
    static BOOL s_fSKeySPIAttemptedLoad = FALSE;
#ifdef UNICODE
	static BOOL s_fMustConvert = FALSE;
	CHAR szActivePort[MAX_PATH]; 
	CHAR szPort[MAX_PATH]; 
	PWSTR pszActivePort; 
	PWSTR pszPort; 
#endif


	if (NULL == s_pSKEY_SystemParametersInfo && !s_fSKeySPIAttemptedLoad)
	{
		BOOL fRc = FALSE;
		HINSTANCE hinst = LoadLibrary(__TEXT("SKDLL.DLL"));

		if (NULL != hinst) 
		{

#ifdef UNICODE

			s_pSKEY_SystemParametersInfo = (PSKEY_SPI)GetProcAddress(
				(HMODULE)hinst, "SKEY_SystemParametersInfoW");
			if (NULL == s_pSKEY_SystemParametersInfo) 
			{
				s_pSKEY_SystemParametersInfo = (PSKEY_SPI)GetProcAddress(
					(HMODULE)hinst, "SKEY_SystemParametersInfo");
				s_fMustConvert = TRUE;
			}

#else
			s_pSKEY_SystemParametersInfo = (PSKEY_SPI)GetProcAddress(
				(HMODULE)hinst, "SKEY_SystemParametersInfo");

#endif // UNICODE

			// We don't bother calling FreeLibrary(hinst), the library will be freed
			// when the app terminates
		}
        s_fSKeySPIAttemptedLoad = TRUE;
	}

    if (NULL != s_pSKEY_SystemParametersInfo)
    {

#ifdef UNICODE

		if (s_fMustConvert) 
		{
            memset(szActivePort, 0, sizeof(szActivePort));
            memset(szPort, 0, sizeof(szPort));

	        pszActivePort = psk->lpszActivePort; 
            pszPort = psk->lpszPort; 

			if (NULL != psk->lpszActivePort)
			{
				psk->lpszActivePort = (PTSTR)szActivePort;
			}

			if (NULL != psk->lpszPort)
			{
				psk->lpszPort = (PTSTR)szPort; 
			}

            if (SPI_SETSERIALKEYS == uAction)
			{
				if (NULL != psk->lpszActivePort)
				{
					WideCharToMultiByte(
						CP_ACP, 0, pszActivePort, -1, 
						(PCHAR)psk->lpszActivePort, MAX_PATH, NULL, NULL);
				}
				if (NULL != psk->lpszPort)
				{
					WideCharToMultiByte(
						CP_ACP, 0, pszPort, -1, 
						(PCHAR)psk->lpszPort, MAX_PATH, NULL, NULL);
				}
			}
		}
#endif // UNICODE

		fRet = (BOOL)(*s_pSKEY_SystemParametersInfo)(
    		uAction, 
			uParam, 
			psk, 
			fu);
#ifdef UNICODE

		if (s_fMustConvert && SPI_GETSERIALKEYS == uAction) 
		{

			if (NULL != psk->lpszActivePort)
			{
				MultiByteToWideChar(
					CP_ACP, 0, (PCHAR)psk->lpszActivePort, -1,
					pszActivePort, MAX_PATH);
			}
			if (NULL != psk->lpszPort)
			{
				MultiByteToWideChar(
					CP_ACP, 0, (PCHAR)psk->lpszPort, -1,
					pszPort, MAX_PATH);
			}
		}
		if (NULL != psk->lpszActivePort)
		{
			psk->lpszActivePort = pszActivePort;
		}

		if (NULL != psk->lpszPort)
		{
			psk->lpszPort = pszPort; 
		}

#endif // UNICODE

    }
    return(fRet);
}


/***************************************************************************\
* FixupAndRetrySystemParametersInfo
*
* Used by access but not implemented by NT's SPI:
*
* SPI_GETKEYBOARDPREF
* SPI_SETKEYBOARDPREF
*
* SPI_GETHIGHCONTRAST
* SPI_SETHIGHCONTRAST
*
* SPI_GETSERIALKEYS
* SPI_SETSERIALKEYS
*
*
* History:
* 12-18-95 a-jimhar 	Created, derived from xxxSystemParametersInfo
* 01-22-95 a-jimhar 	Removed old code that worked around NT bugs
*
* On NT this function fixes the parameters and calls SystemParametersInfo
*
\***************************************************************************/

static BOOL FixupAndRetrySystemParametersInfo(
	UINT wFlag,
	DWORD wParam,
	PVOID lParam,
	UINT flags)	// we ignoring this flag 
				// could add support for SPIF_UPDATEINIFILE and SPIF_SENDCHANGE
{
	BOOL fRet = FALSE;
	BOOL fCallSpi = FALSE;
	BOOL fChanged = FALSE;

	if (NULL != (PVOID)lParam)
	{
		switch (wFlag) {


		// Fake support
		case SPI_GETKEYBOARDPREF:
			{
				*(PBOOL) lParam = FALSE;
				fRet = TRUE;
				fCallSpi = FALSE;
			}
			break;

#ifdef ENABLEHIGHCONTRAST //// bugbug a-jimhar remove this ifdef when ready to enable highcontrast ////

		case SPI_GETHIGHCONTRAST:
			{
				LPHIGHCONTRAST phc = (LPHIGHCONTRAST)lParam;
                static TCHAR szCurScheme[MAXSCHEMENAME]; 	 // current scheme name


				if (NULL != phc &&
				   (sizeof(*phc) == phc->cbSize || 0 == phc->cbSize))
				{
					DWORD dwType;
					long lRet;
					HKEY hkey;
					DWORD dwDisposition;

					lRet = RegCreateKeyEx(
						HKEY_CURRENT_USER, // handle of an open key
						HC_KEY, 		   // address of subkey name
						0,				   // reserved
						NULL,			   // address of class string
						0,				   // special options flag
						KEY_READ,		   // desired security access
						NULL,			   // address of key security structure
						&hkey,			   // address of buffer for opened handle
						&dwDisposition);   // address of disposition value buffer

					if (ERROR_SUCCESS == lRet) {
					    DWORD cbData;

						cbData = sizeof(szCurScheme);
						RegQueryValueEx(
							hkey,
							HIGHCONTRAST_SCHEME,
							NULL,
							&dwType,
							(PBYTE)szCurScheme,
							&cbData);

						phc->lpszDefaultScheme = szCurScheme;

						cbData = sizeof(phc->dwFlags);

						phc->dwFlags = RegQueryStrDW(
							0,
							HKEY_CURRENT_USER, 
							HC_KEY, 
							REGSTR_VAL_FLAGS);

                        phc->dwFlags |= HCF_AVAILABLE;
						RegCloseKey(hkey);
					}
				}
				fCallSpi = FALSE;
			}
			break;

		case SPI_SETHIGHCONTRAST:
			{
				LPHIGHCONTRAST phc = (LPHIGHCONTRAST)lParam;

				if (NULL != phc &&
				   (sizeof(*phc) == phc->cbSize || 0 == phc->cbSize))
				{
					DoHighContrast(phc);
    				fChanged = TRUE;
				}

				fCallSpi = FALSE;
			}
			break;

#endif ////////////////////////////////////////////////////////////////////////

		case SPI_GETSERIALKEYS:
            {
		        LPSERIALKEYS psk = (LPSERIALKEYS)lParam;

			    if (NULL != psk &&
			       (sizeof(*psk) == psk->cbSize || 0 == psk->cbSize))
			    {
					fRet = AccessSKeySystemParametersInfo(
						wFlag, 
						0, 
						psk, 
						TRUE);
                }
                fCallSpi = FALSE;
            }
			break;

		case SPI_SETSERIALKEYS:
            {
		        LPSERIALKEYS psk = (LPSERIALKEYS)lParam;

			    if (NULL != psk &&
			       (sizeof(*psk) == psk->cbSize || 0 == psk->cbSize))
			    {
					fRet = AccessSKeySystemParametersInfo(
						wFlag, 
						0, 
						psk, 
						TRUE);
	    			fChanged = TRUE;
                }
		        fCallSpi = FALSE;
            }
			break;

		default:
			// This function is only for fix-up and second chance calls.
			// We didn't fix anything, don't call SPI.
			fCallSpi = FALSE;
			fRet = FALSE;
			break;
		}
	}

	if (fCallSpi)
	{
		fRet = SystemParametersInfo(wFlag, wParam, lParam, flags);
	}
	else if (fChanged && (flags & SPIF_SENDCHANGE))
	{
        DWORD dwResult;

        SendMessageTimeout(
			HWND_BROADCAST, 
			WM_WININICHANGE, 
			wFlag, 
			(LONG)NULL,
            SMTO_NORMAL, 
			100, 
			&dwResult);
	}
	return(fRet);
}

/***************************************************************************\
* AccessSystemParametersInfo
*
* History:
* 12-18-95 a-jimhar 	Created.
\***************************************************************************/

BOOL AccessSystemParametersInfo(
	UINT wFlag,
	DWORD wParam,
	PVOID lParam,
	UINT flags)
{
	BOOL fRet;

	// first give the system SPI a chance

	fRet = SystemParametersInfo(wFlag, wParam, lParam, flags);

	if (!fRet && g_fWinNT)
	{
		// the system SPI failed, fixup the params and try again

		fRet = FixupAndRetrySystemParametersInfo(wFlag, wParam, lParam, flags);
	}

	return(fRet);
}