// **************************************************************************
// Access.c
//
// Accessability Property sheet page creator
//
// **************************************************************************

#include "Access.h"

#ifdef  UNICODE     // Windows uses UNICODE
#define _UNICODE    // but tchar.h uses _UNICODE
#endif

DWORD g_dwOrigFKFlags;
BOOL g_bFKOn;

#include <stdlib.h>
#include <stddef.h>
#include <tchar.h>

#define OLDDISABLED     32760

#ifndef FKF_VALID
#define FKF_VALID           0x0000007F
#endif

#ifndef SKF_VALID
#define SKF_VALID           0x000001FF
#endif

#ifndef MKF_VALID
#define MKF_VALID           0x000000FF
#endif

#ifndef ATF_VALID
#define ATF_VALID           0x00000003
#endif

#ifndef SSF_VALID
#define SSF_VALID           0x00000007
#endif

#ifndef TKF_VALID
#define TKF_VALID           0x0000003F
#endif

//////////////////////////////////////////////////////////////////////////

// collection of data that represents the saved accessability state
typedef struct ACCSTATE   // as
{
    // Keyboard property page
    STICKYKEYS     sk;
    FILTERKEYS     fk;
    TOGGLEKEYS     tk;
    BOOL           fExtraKeyboardHelp;

    // Sound Property page
    SOUNDSENTRY    ss;
    BOOL           fShowSounds;

    // Display Property page
    HIGHCONTRAST   hc;
    TCHAR          szDefaultScheme[256];  // hc.lpszDefaultScheme
    CARET_SETTINGS cs;

    // Mouse Property page
    MOUSEKEYS      mk;

    // General Property page
    BOOL               fShowWarnMsgOnFeatureActivate;
    BOOL               fPlaySndOnFeatureActivate;

    ACCESSTIMEOUT  ato;
    SERIALKEYS     serk;
    TCHAR          szActivePort[MAX_PATH];  // serk.szActivePort
    TCHAR          szPort[MAX_PATH];                // serk.szPort
} ACCSTATE, *PACCSTATE;


//////////////////////////////////////////////////////////////////////////
extern BOOL g_SPISetValue = FALSE;

static ACCSTATE s_asOrg;          // original settings from app start-up
static ACCSTATE s_asPrev;         // previous saved settings

extern BOOL  g_fWinNT = -1;       // TRUE if we're running on NT and must
                                  // disable some features

extern BOOL  g_fSaveSettings = TRUE;
extern BOOL  g_fShowWarnMsgOnFeatureActivate = TRUE;
extern BOOL  g_fPlaySndOnFeatureActivate = TRUE;
extern BOOL  g_fCopyToLogon = FALSE;
extern BOOL  g_fCopyToDefault = FALSE;
// Keyboard property page
// extern STICKYKEYS     g_sk = {0};
STICKYKEYS     g_sk;
FILTERKEYS     g_fk;
   // g_dwLastBounceKeySetting, g_nLastRepeatDelay, g_nLastRepeatRate
   //  and g_nLastWait are part of FilterKeys
   DWORD g_dwLastBounceKeySetting = 0;
   DWORD g_nLastRepeatDelay = 0;
   DWORD g_nLastRepeatRate = 0;
   DWORD g_nLastWait = 0;

TOGGLEKEYS     g_tk;
BOOL           g_fExtraKeyboardHelp = TRUE;

// Sound Property page
SOUNDSENTRY    g_ss;
BOOL           g_fShowSounds;

// Display Property page
HIGHCONTRAST   g_hc;
CARET_SETTINGS g_cs;

// Mouse Property page
MOUSEKEYS      g_mk;

// General Property page
ACCESSTIMEOUT  g_ato;
SERIALKEYS     g_serk;
TCHAR          g_szActivePort[MAX_PATH];
TCHAR          g_szPort[MAX_PATH];

#define CONTROL_PANEL_DESKTOP TEXT("Control Panel\\Desktop")
#define CURSOR_BLINK_RATE TEXT("CursorBlinkRate")
#define DEFAULT_BLINK_RATE 530

//////////////////////////////////////////////////////////////////////////

void CopyHighContrast(LPHIGHCONTRAST phcDest, LPHIGHCONTRAST phcSrc)
{
    LPTSTR lpszDefaultScheme = phcDest->lpszDefaultScheme;

    memcpy(phcDest, phcSrc, sizeof(*phcDest));
    phcDest->lpszDefaultScheme = lpszDefaultScheme;

    if (NULL != phcDest->lpszDefaultScheme)
    {
        lstrcpy(phcDest->lpszDefaultScheme, phcSrc->lpszDefaultScheme);
    }
}

//////////////////////////////////////////////////////////////////////////

BOOL IsHighContrastEqual(LPHIGHCONTRAST phcDest, LPHIGHCONTRAST phcSrc)
{
    BOOL fIsEqual = FALSE;
    LPTSTR lpszDefaultScheme = phcDest->lpszDefaultScheme;

    // Temporarily make the pointers match
    phcDest->lpszDefaultScheme = phcSrc->lpszDefaultScheme;

    // match the bits of the structures and the pointed to data
    fIsEqual = (0 == memcmp(phcDest, phcSrc, sizeof(*phcDest)) &&
                0 == lstrcmp(lpszDefaultScheme, phcSrc->lpszDefaultScheme));

    phcDest->lpszDefaultScheme = lpszDefaultScheme;

    return(fIsEqual);
}


//////////////////////////////////////////////////////////////////////////

void CopySerialKeys(LPSERIALKEYS pskDest, LPSERIALKEYS pskSrc)
{
    LPTSTR lpszActivePort = pskDest->lpszActivePort;
    LPTSTR lpszPort = pskDest->lpszPort;

    memcpy(pskDest, pskSrc, sizeof(*pskDest));
    pskDest->lpszActivePort = lpszActivePort;

    if (NULL != pskDest->lpszActivePort)
    {
        lstrcpy(pskDest->lpszActivePort, pskSrc->lpszActivePort);
    }

    pskDest->lpszPort = lpszPort;
    if (NULL != pskDest->lpszPort)
    {
        lstrcpy(pskDest->lpszPort, pskSrc->lpszPort);
    }
}

//////////////////////////////////////////////////////////////////////////

BOOL IsSerialKeysEqual(LPSERIALKEYS pskDest, LPSERIALKEYS pskSrc)
{
    BOOL fIsEqual = FALSE;
    LPTSTR lpszActivePort = pskDest->lpszActivePort;
    LPTSTR lpszPort = pskDest->lpszPort;

    // Temporarily make the pointers match
    pskDest->lpszActivePort = pskSrc->lpszActivePort;
    pskDest->lpszPort = pskSrc->lpszPort;

    // match the bits of the structures and the pointed to data
    fIsEqual = (0 == memcmp(pskDest, pskSrc, sizeof(*pskDest)) &&
        (NULL == lpszActivePort ||
                0 == lstrcmp(lpszActivePort, pskSrc->lpszActivePort)) &&
        (NULL == lpszPort ||
                0 == lstrcmp(lpszPort, pskSrc->lpszPort)));

    pskDest->lpszActivePort = lpszActivePort;
    pskDest->lpszPort = lpszPort;

    return(fIsEqual);
}

//////////////////////////////////////////////////////////////////////////

BOOL IsAccStateEqual(PACCSTATE pasDest, PACCSTATE pasSrc)
{
    BOOL fIsEqual = FALSE;
    HIGHCONTRAST   hc = pasDest->hc;
    SERIALKEYS     serk = pasDest->serk;
    int nLen;

    // Clear out the unused sections of the string buffers
    nLen = lstrlen(pasDest->szDefaultScheme);
    memset(&pasDest->szDefaultScheme[nLen], 0,
        sizeof(pasDest->szDefaultScheme)-nLen*sizeof(*pasDest->szDefaultScheme));

    nLen = lstrlen(pasDest->szActivePort);
    memset(&pasDest->szActivePort[nLen], 0,
        sizeof(pasDest->szActivePort)-nLen*sizeof(*pasDest->szActivePort));

    nLen = lstrlen(pasDest->szPort);
    memset(&pasDest->szPort[nLen], 0,
            sizeof(pasDest->szPort)-nLen*sizeof(*pasDest->szPort));

    nLen = lstrlen(pasSrc->szDefaultScheme);
    memset(&pasSrc->szDefaultScheme[nLen], 0,
            sizeof(pasSrc->szDefaultScheme)-nLen*sizeof(*pasSrc->szDefaultScheme));

    nLen = lstrlen(pasSrc->szActivePort);
    memset(&pasSrc->szActivePort[nLen], 0,
            sizeof(pasSrc->szActivePort)-nLen*sizeof(*pasSrc->szActivePort));

    nLen = lstrlen(pasSrc->szActivePort);
    memset(&pasSrc->szPort[nLen], 0,
            sizeof(pasSrc->szPort)-nLen*sizeof(*pasSrc->szPort));

    // Temporarily make the elements with pointers match
    pasDest->hc = pasSrc->hc;
    pasDest->serk = pasSrc->serk;

    // match the bits of the structures and the elements with pointers
    fIsEqual = (0 == memcmp(pasDest, pasSrc, sizeof(*pasDest)) &&
            IsHighContrastEqual(&hc, &pasSrc->hc) &&
            IsSerialKeysEqual(&serk, &pasSrc->serk));

    pasDest->hc = hc;
    pasDest->serk = serk;

    return(fIsEqual);
}


//////////////////////////////////////////////////////////////////////////


int WINAPI RegQueryInt (int nDefault, HKEY hkey, LPTSTR lpSubKey, LPTSTR lpValueName) {

   DWORD dwType;
   DWORD dwVal = nDefault;
   DWORD cbData = sizeof(int);
   if (ERROR_SUCCESS == RegOpenKeyEx(hkey, lpSubKey, 0, KEY_QUERY_VALUE, &hkey)) {
      RegQueryValueEx(hkey, lpValueName, NULL, &dwType, (PBYTE) &dwVal, &cbData);
      RegCloseKey(hkey);
   }
   return(dwVal);
}


//////////////////////////////////////////////////////////////////////////


BOOL WINAPI RegSetInt (HKEY hkey, LPTSTR lpSubKey, LPTSTR lpValueName, int nVal) {
   BOOL fOk = FALSE;
   DWORD dwDisposition;
   LONG lRet;

   if (ERROR_SUCCESS == RegCreateKeyEx(hkey, lpSubKey, 0, NULL, REG_OPTION_NON_VOLATILE,
      KEY_SET_VALUE, NULL, &hkey, &dwDisposition)) {

      lRet = RegSetValueEx(hkey, lpValueName, 0, REG_DWORD, (CONST BYTE *) &nVal, sizeof(nVal));
      fOk = (ERROR_SUCCESS == lRet);
      RegCloseKey(hkey);
   }
   return fOk;
}


//////////////////////////////////////////////////////////////////////////


void WINAPI RegQueryStr(
   LPTSTR lpDefault,
   HKEY hkey,
   LPTSTR lpSubKey,
   LPTSTR lpValueName,
   LPTSTR lpszValue,
   DWORD cbData) // note this is bytes, not characters.
{
   DWORD dwType;

   lstrcpy(lpszValue, lpDefault);
   if (ERROR_SUCCESS == RegOpenKeyEx(hkey, lpSubKey, 0, KEY_QUERY_VALUE, &hkey)) {
      RegQueryValueEx(hkey, lpValueName, NULL, &dwType, (PBYTE) lpszValue, &cbData);
      RegCloseKey(hkey);
   }
}

/***************************************************************************\
**AccessWriteProfileString
*
* History:
* 12-19-95 a-jimhar 	Created (was called AccessWriteProfileString)
* 02-08-95 a-jimhar     revised and moved from accrare.c to access.c
\***************************************************************************/
BOOL RegSetStr(
    HKEY hkey,
    LPCTSTR lpSection,
    LPCTSTR lpKeyName,
    LPCTSTR lpString)
{
    BOOL fRet = FALSE;
    LONG lErr;
    DWORD dwDisposition;

    lErr = RegCreateKeyEx(
            hkey,
            lpSection,
            0,
            NULL,
            REG_OPTION_NON_VOLATILE,
            KEY_SET_VALUE,
            NULL,
            &hkey,
            &dwDisposition);

    if (ERROR_SUCCESS == lErr)
    {
        if (NULL != lpString)
        {
            lErr = RegSetValueEx(
                    hkey,
                    lpKeyName,
                    0,
                    REG_SZ,
                    (CONST BYTE *)lpString,
                    (lstrlen(lpString) + 1) * sizeof(*lpString));
        }
        else
        {
            lErr = RegSetValueEx(
                    hkey,
                    lpKeyName,
                    0,
                    REG_SZ,
                    (CONST BYTE *)__TEXT(""),
                    1 * sizeof(*lpString));
        }

        if (ERROR_SUCCESS == lErr)
        {
            fRet = TRUE;
        }
        RegCloseKey(hkey);
    }
    return(fRet);
}


DWORD WINAPI RegQueryStrDW(
    DWORD dwDefault,
    HKEY hkey,
    LPTSTR lpSubKey,
    LPTSTR lpValueName)
{
    DWORD dwRet = dwDefault;
    TCHAR szTemp[40];
    TCHAR szDefault[40];

    const LPTSTR pwszd = __TEXT("%d");

    wsprintf(szDefault, pwszd, dwDefault);

    RegQueryStr(
        szDefault,
        hkey,
        lpSubKey,
        lpValueName,
        szTemp,
        sizeof(szTemp));

    dwRet = _ttol(szTemp);

    return dwRet;
}


BOOL RegSetStrDW(
    HKEY hkey,
    LPTSTR lpSection,
    LPCTSTR lpKeyName,
    DWORD dwValue)
{
    BOOL fRet;
    TCHAR szTemp[40];
    const LPTSTR pwszd = __TEXT("%d");

    wsprintf(szTemp, pwszd, dwValue);
    fRet = RegSetStr(hkey, lpSection, lpKeyName, szTemp);

    return fRet;
}


//////////////////////////////////////////////////////////////////////////


/*------------------------------------------------------------------
 * Function void KillAccStat()
 *
 * Purpose     Check if accstat is already running.  If it is we need
 *             to check to see if it should be.  It should only be running
 *             if each feature that is on also has the 'show status on
 *             screen flag checked.  If not we want to kill accstat.
 *
 * Params:     None
 *
 * Return:     TRUE if we had to kill accstat
 *             FALSE if accstat not running/valid session
 *------------------------------------------------------------------*/

void KillAccStat (void) {
   BOOL fCanTurnOff = FALSE;     // Can we turn off accstat due to invalid feature?
   BOOL fValidFeature = FALSE;   // Are there any valid features?

   // Accstat may be running.  Determine if it should be running
   // We need to check the FilterKeys, MouseKeys and StickyKeys
   if (g_sk.dwFlags & SKF_STICKYKEYSON)
      if (!(g_sk.dwFlags & SKF_INDICATOR))
         fCanTurnOff = TRUE;   // A mismatched flag - we MAY be able to turn off.
      else
         fValidFeature = TRUE; // A valid feature - we CAN't turn off accstat.

   if (g_fk.dwFlags & FKF_FILTERKEYSON)
      if (!(g_fk.dwFlags & FKF_INDICATOR))
         fCanTurnOff = TRUE;   // A mismatched flag - we MAY be able to turn off.
      else
         fValidFeature = TRUE; // A valid feature - we CAN't turn off accstat.

   if (g_mk.dwFlags & MKF_MOUSEKEYSON)
      if (!(g_mk.dwFlags & MKF_INDICATOR))
         fCanTurnOff = TRUE;   // A mismatched flag - we MAY be able to turn off.
      else
         fValidFeature = TRUE; // A valid feature - we CAN't turn off accstat.

   // Now we have two flags: fCanTurnOff is TRUE if there is a mismatched flag set
   // ie, feature on, indicator off.  ValidFeature is TRUE if any feature has
   // ON and INDICATOR set which implies accstat must remain active.
   if (!fValidFeature && fCanTurnOff) {
      TCHAR szBuf[256];
      HWND hwndAccStat;
      LoadString(g_hinst, IDS_ACCSTAT_WINDOW_TITLE, szBuf, ARRAY_SIZE(szBuf));
      if (IsWindow(hwndAccStat = FindWindow(NULL, szBuf))) {
         // Note sending 1 as the lParam tells accstat to shutup and
         // go away NOW.
         SendMessage(hwndAccStat, WM_SYSCOMMAND, SC_CLOSE, 1);
      }
   }
}


//////////////////////////////////////////////////////////////////////////


void WINAPI GetAccessibilitySettings (void) {
   BOOL fUpdate;

   if (g_fWinNT == -1) {
      OSVERSIONINFO osvi;
      osvi.dwOSVersionInfoSize = sizeof(osvi);
      GetVersionEx(&osvi);
      g_fWinNT = (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT);
   }

   g_fShowWarnMsgOnFeatureActivate = (BOOL) RegQueryInt(TRUE, HKEY_CURRENT_USER,
      GENERAL_KEY, WARNING_SOUNDS);

   s_asPrev.fShowWarnMsgOnFeatureActivate = g_fShowWarnMsgOnFeatureActivate;

   // Query the Sound On Activation entry
   g_fPlaySndOnFeatureActivate = (BOOL) RegQueryInt(TRUE, HKEY_CURRENT_USER,
      GENERAL_KEY, SOUND_ON_ACTIVATION);

   s_asPrev.fPlaySndOnFeatureActivate = g_fPlaySndOnFeatureActivate;

   g_fSaveSettings = TRUE;

   // Keyboard property page
   g_sk.cbSize = sizeof(g_sk);
   AccessSystemParametersInfo(SPI_GETSTICKYKEYS, sizeof(g_sk), &g_sk, 0);
   s_asPrev.sk = g_sk;

   g_fk.cbSize = sizeof(g_fk);
   AccessSystemParametersInfo(SPI_GETFILTERKEYS, sizeof(g_fk), &g_fk, 0);
   g_fk.dwFlags |= FKF_AVAILABLE;

   // FILTERKEYS used to use OLDDISABLED as it's "unused" flag.  This doesn't
   // work very well on NT (SPI_SETFILTERKEYS calls fail).  We now use 0
   // for disabled values.  Take this opertunity to change any OLDDISABLED
   // values to 0 and save if needed.

   fUpdate = FALSE;

   if (OLDDISABLED == g_fk.iBounceMSec)
   {
      g_fk.iBounceMSec = 0;
      fUpdate = TRUE;
   }
   if (OLDDISABLED == g_fk.iDelayMSec)
   {
      g_fk.iDelayMSec = 0;
      fUpdate = TRUE;
   }
   if (OLDDISABLED == g_fk.iRepeatMSec)
   {
      g_fk.iRepeatMSec = 0;
      fUpdate = TRUE;
   }
   if (OLDDISABLED == g_fk.iWaitMSec)
   {
       g_fk.iWaitMSec = 0;
       fUpdate = TRUE;
   }

   if (fUpdate)
   {
        AccessSystemParametersInfo(
                SPI_SETFILTERKEYS, sizeof(g_fk), &g_fk, SPIF_UPDATEINIFILE | SPIF_SENDWININICHANGE);
   }

   s_asPrev.fk = g_fk;
   // fix Filter keys bug
   g_dwOrigFKFlags = g_fk.dwFlags;
   g_bFKOn = g_fk.dwFlags & FKF_FILTERKEYSON;

   // g_dwLastBounceKeySetting, g_nLastRepeatDelay, g_nLastRepeatRate
   // and g_nLastWait are part of FilterKeys

   if (0 != g_fk.iBounceMSec) {
      // Bounce keys enabeled
      g_fk.iDelayMSec = 0;
      g_fk.iRepeatMSec = 0;
      g_fk.iWaitMSec = 0;

      g_dwLastBounceKeySetting = g_fk.iBounceMSec;
      g_nLastRepeatDelay = RegQueryInt(0, HKEY_CURRENT_USER, FILTER_KEY, LAST_REPEAT_DELAY);
      g_nLastRepeatRate = RegQueryInt(0, HKEY_CURRENT_USER, FILTER_KEY, LAST_REPEAT_RATE);
      g_nLastWait = RegQueryInt(0, HKEY_CURRENT_USER, FILTER_KEY, LAST_WAIT);
   }
   else
   {
      if (0 == g_fk.iDelayMSec)
      {
          g_fk.iRepeatMSec = 0;
      }
      g_dwLastBounceKeySetting = RegQueryInt(0, HKEY_CURRENT_USER, FILTER_KEY, LAST_BOUNCE_SETTING);
      g_nLastRepeatDelay = RegQueryInt(0, HKEY_CURRENT_USER, FILTER_KEY, LAST_REPEAT_DELAY);
      g_nLastRepeatRate = RegQueryInt(0, HKEY_CURRENT_USER, FILTER_KEY, LAST_REPEAT_RATE);
      if (0 != g_fk.iWaitMSec)
      {
         g_nLastWait = g_fk.iWaitMSec;
      }
      else
      {
         g_nLastWait = RegQueryInt(0, HKEY_CURRENT_USER, FILTER_KEY, LAST_WAIT);
      }
   }

   g_tk.cbSize = sizeof(g_tk);
   AccessSystemParametersInfo(SPI_GETTOGGLEKEYS, sizeof(g_tk), &g_tk, 0);
   s_asPrev.tk = g_tk;

   AccessSystemParametersInfo(SPI_GETKEYBOARDPREF, 0, &g_fExtraKeyboardHelp, 0);
   s_asPrev.fExtraKeyboardHelp = g_fExtraKeyboardHelp;

   // Sound Property page
   g_ss.cbSize = sizeof(g_ss);
   AccessSystemParametersInfo(SPI_GETSOUNDSENTRY, sizeof(g_ss), &g_ss, 0);
   s_asPrev.ss = g_ss;

   SystemParametersInfo(SPI_GETSHOWSOUNDS, 0, &g_fShowSounds, 0);

   // BUG, BUG GetSystemMetrics() is not updating value on reboot :a-anilk
   // g_fShowSounds = GetSystemMetrics(SM_SHOWSOUNDS);
   s_asPrev.fShowSounds = g_fShowSounds;

   // Display Property page
   g_hc.cbSize = sizeof(g_hc);
   AccessSystemParametersInfo(SPI_GETHIGHCONTRAST, sizeof(g_hc), &g_hc, 0);

   // Currently NT will not store these flags.  We fake them so we
   // can tell if they actually changed.

   s_asPrev.hc.lpszDefaultScheme = s_asPrev.szDefaultScheme;
   CopyHighContrast(&s_asPrev.hc, &g_hc);

   SystemParametersInfo(SPI_GETCARETWIDTH, 0, (PVOID)&g_cs.dwCaretWidth, 0);
   g_cs.dwCaretBlinkRate = RegQueryStrDW(
								 DEFAULT_BLINK_RATE
							   , HKEY_CURRENT_USER
							   , CONTROL_PANEL_DESKTOP
							   , CURSOR_BLINK_RATE);
   if (g_cs.dwCaretBlinkRate == BLINK_OFF)
       g_cs.dwCaretBlinkRate = CURSORMAX;
   s_asPrev.cs.dwCaretBlinkRate = g_cs.dwCaretBlinkRate;
   s_asPrev.cs.dwCaretWidth = g_cs.dwCaretWidth;

   // Mouse Property page
   g_mk.cbSize = sizeof(g_mk);
   AccessSystemParametersInfo(SPI_GETMOUSEKEYS, sizeof(g_mk), &g_mk, 0);
   s_asPrev.mk = g_mk;

   // General Property page
   g_ato.cbSize = sizeof(g_ato);
   AccessSystemParametersInfo(SPI_GETACCESSTIMEOUT, sizeof(g_ato), &g_ato, 0);
   s_asPrev.ato = g_ato;

   g_serk.cbSize = sizeof(g_serk);
   g_serk.lpszActivePort = g_szActivePort;
   g_serk.lpszPort = g_szPort;
   AccessSystemParametersInfo(SPI_GETSERIALKEYS, sizeof(g_serk), &g_serk, 0);

   s_asPrev.serk.lpszActivePort = s_asPrev.szActivePort;
   s_asPrev.serk.lpszPort = s_asPrev.szPort;
   CopySerialKeys(&s_asPrev.serk, &g_serk);

   if (NULL == s_asOrg.hc.lpszDefaultScheme)
   {
      // s_asOrg has not yet been initialized
      s_asOrg = s_asPrev;
      s_asOrg.hc.lpszDefaultScheme = s_asOrg.szDefaultScheme;
      s_asOrg.serk.lpszActivePort = s_asOrg.szActivePort;
      s_asOrg.serk.lpszPort = s_asOrg.szPort;
   }
}


//////////////////////////////////////////////////////////////////////////

//a-anilk: Change, Admin options, Keyboard flags: 05/06/99
void WINAPI SetAccessibilitySettings (void) {
   HKEY hkey;
   DWORD dwDisposition;
   UINT fWinIni = SPIF_UPDATEINIFILE | SPIF_SENDWININICHANGE;
   BOOL fAnyNotifyChange = FALSE;

   g_SPISetValue = TRUE;

   SetCursor(LoadCursor(NULL, IDC_WAIT));

   if (g_fShowWarnMsgOnFeatureActivate) {
      g_hc.dwFlags |= HCF_CONFIRMHOTKEY;
      g_fk.dwFlags |= FKF_CONFIRMHOTKEY;
      g_sk.dwFlags |= SKF_CONFIRMHOTKEY;
      g_mk.dwFlags |= MKF_CONFIRMHOTKEY;
      g_tk.dwFlags |= TKF_CONFIRMHOTKEY;
   } else {
      g_hc.dwFlags &= ~HCF_CONFIRMHOTKEY;
      g_fk.dwFlags &= ~FKF_CONFIRMHOTKEY;
      g_sk.dwFlags &= ~SKF_CONFIRMHOTKEY;
      g_mk.dwFlags &= ~MKF_CONFIRMHOTKEY;
      g_tk.dwFlags &= ~TKF_CONFIRMHOTKEY;
   }

   if (g_fPlaySndOnFeatureActivate) {
      g_hc.dwFlags  |= HCF_HOTKEYSOUND;
      g_fk.dwFlags  |= FKF_HOTKEYSOUND;
      g_sk.dwFlags  |= SKF_HOTKEYSOUND;
      g_mk.dwFlags  |= MKF_HOTKEYSOUND;
      g_tk.dwFlags  |= TKF_HOTKEYSOUND;
      g_ato.dwFlags |= ATF_ONOFFFEEDBACK;
   } else {
      g_hc.dwFlags  &= ~HCF_HOTKEYSOUND;
      g_fk.dwFlags  &= ~FKF_HOTKEYSOUND;
      g_sk.dwFlags  &= ~SKF_HOTKEYSOUND;
      g_mk.dwFlags  &= ~MKF_HOTKEYSOUND;
      g_tk.dwFlags  &= ~TKF_HOTKEYSOUND;
      g_ato.dwFlags &= ~ATF_ONOFFFEEDBACK;
   }


   // Keyboard property page

   if (0 != memcmp(&g_sk, &s_asPrev.sk, sizeof(g_sk)))
   {
      if (g_fWinNT)
      {
         g_sk.dwFlags &= SKF_VALID;
      }
      AccessSystemParametersInfo(SPI_SETSTICKYKEYS, sizeof(g_sk), &g_sk, fWinIni);
      s_asPrev.sk = g_sk;
      fAnyNotifyChange = TRUE;
   }

	if (g_bFKOn)
		g_fk.dwFlags |= FKF_FILTERKEYSON;
	else
		g_fk.dwFlags &= ~FKF_FILTERKEYSON;

	g_dwOrigFKFlags = g_fk.dwFlags;

   if (0 != memcmp(&g_fk, &s_asPrev.fk, sizeof(g_fk)))
   {
      if (g_fWinNT)
      {
         g_fk.dwFlags &= FKF_VALID;
      }

      // g_dwLastBounceKeySetting, g_nLastRepeatDelay, g_nLastRepeatRate
      // and g_nLastWait are part of FilterKeys

      if (0 != g_fk.iBounceMSec) {
         // Bounce keys enabeled
         g_fk.iDelayMSec = 0;
         g_fk.iRepeatMSec = 0;
         g_fk.iWaitMSec = 0;

         g_dwLastBounceKeySetting = g_fk.iBounceMSec;
      }
      else
      {
         g_nLastWait = g_fk.iWaitMSec;
         if (0 != g_fk.iDelayMSec)
         {
            // Slow key enabled
            g_nLastRepeatDelay = g_fk.iDelayMSec;
            g_nLastRepeatRate = g_fk.iRepeatMSec;
         }
         else
         {
            // neither Bounce or Slow
            g_fk.iRepeatMSec = 0;
         }
      }

      AccessSystemParametersInfo(SPI_SETFILTERKEYS, sizeof(g_fk), &g_fk, fWinIni);
      s_asPrev.fk = g_fk;

      fAnyNotifyChange = TRUE;
   }

   // always save these
   RegSetInt(HKEY_CURRENT_USER, FILTER_KEY, LAST_BOUNCE_SETTING, g_dwLastBounceKeySetting);
   RegSetInt(HKEY_CURRENT_USER, FILTER_KEY, LAST_REPEAT_DELAY, g_nLastRepeatDelay);
   RegSetInt(HKEY_CURRENT_USER, FILTER_KEY, LAST_REPEAT_RATE, g_nLastRepeatRate);
   RegSetInt(HKEY_CURRENT_USER, FILTER_KEY, LAST_WAIT, g_nLastWait);

   if (0 != memcmp(&g_tk, &s_asPrev.tk, sizeof(g_tk)))
   {
      if (g_fWinNT)
      {
         g_tk.dwFlags &= TKF_VALID;
      }
      AccessSystemParametersInfo(SPI_SETTOGGLEKEYS, sizeof(g_tk), &g_tk, fWinIni);
      s_asPrev.tk = g_tk;
      fAnyNotifyChange = TRUE;
   }

   if (g_fExtraKeyboardHelp != s_asPrev.fExtraKeyboardHelp)
   {
	   // Set this too. Some controls check this flag...0x100B
      AccessSystemParametersInfo(SPI_SETKEYBOARDCUES, 0, IntToPtr(g_fExtraKeyboardHelp), fWinIni);

      AccessSystemParametersInfo(SPI_SETKEYBOARDPREF, g_fExtraKeyboardHelp, 0, fWinIni);
      s_asPrev.fExtraKeyboardHelp = g_fExtraKeyboardHelp;
      fAnyNotifyChange = TRUE;
   }

   // Display Property page

   // BUGBUG a-jimhar 03-22-96 verify changes to display property page save
   // code when display page is added back in on NT

   if (!IsHighContrastEqual(&g_hc, &s_asPrev.hc))
   {
      AccessSystemParametersInfo(SPI_SETHIGHCONTRAST, sizeof(g_hc), &g_hc, fWinIni);
      if (ERROR_SUCCESS == RegCreateKeyEx(
         HKEY_CURRENT_USER,
         HC_KEY,
         0,
         __TEXT(""),
         REG_OPTION_NON_VOLATILE,
         KEY_SET_VALUE,
         NULL,
         &hkey,
         &dwDisposition))
      {
         RegSetValueEx(hkey, HIGHCONTRAST_SCHEME, 0, REG_SZ, (PBYTE) g_hc.lpszDefaultScheme,
            (lstrlen(g_hc.lpszDefaultScheme) + 1) * sizeof(*g_hc.lpszDefaultScheme));
         RegSetValueEx(hkey, VOLATILE_SCHEME, 0, REG_SZ, (PBYTE) g_hc.lpszDefaultScheme,
            (lstrlen(g_hc.lpszDefaultScheme) + 1) * sizeof(*g_hc.lpszDefaultScheme));
         RegCloseKey(hkey);
         hkey = NULL;
      }
      CopyHighContrast(&s_asPrev.hc, &g_hc);
      fAnyNotifyChange = TRUE;
   }

   if (g_cs.dwCaretBlinkRate != s_asPrev.cs.dwCaretBlinkRate)
   {
       DWORD dwCaretBlinkRate = (g_cs.dwCaretBlinkRate < CURSORMAX)?g_cs.dwCaretBlinkRate:BLINK_OFF;

	   // Set the blink rate for this session
       SetCaretBlinkTime(dwCaretBlinkRate);

	   // and persist it to the registry
	   RegSetStrDW(HKEY_CURRENT_USER, CONTROL_PANEL_DESKTOP, CURSOR_BLINK_RATE, dwCaretBlinkRate);
   }

   if (g_cs.dwCaretWidth != s_asPrev.cs.dwCaretWidth)
       AccessSystemParametersInfo(SPI_SETCARETWIDTH, 0, IntToPtr(g_cs.dwCaretWidth), fWinIni);

   s_asPrev.cs = g_cs;

   // Mouse Property page
   if (0 != memcmp(&g_mk, &s_asPrev.mk, sizeof(g_mk)))
   {
      if (g_fWinNT)
      {
         g_mk.dwFlags &= MKF_VALID;
      }
      AccessSystemParametersInfo(SPI_SETMOUSEKEYS, sizeof(g_mk), &g_mk, fWinIni);
      s_asPrev.mk = g_mk;
      fAnyNotifyChange = TRUE;
   }

   // General Property page
   if (g_fPlaySndOnFeatureActivate) {
      g_ato.dwFlags |= ATF_ONOFFFEEDBACK;
   } else {
      g_ato.dwFlags &= ~ATF_ONOFFFEEDBACK;
   }

   if (0 != memcmp(&g_ato, &s_asPrev.ato, sizeof(g_ato)))
   {
      if (g_fWinNT)
      {
         g_ato.dwFlags &= ATF_VALID;
      }
      AccessSystemParametersInfo(SPI_SETACCESSTIMEOUT, sizeof(g_ato), &g_ato, fWinIni);
      s_asPrev.ato = g_ato;
      fAnyNotifyChange = TRUE;
   }

   if (!IsSerialKeysEqual(&g_serk, &s_asPrev.serk))
   {
      AccessSystemParametersInfo(SPI_SETSERIALKEYS, sizeof(g_serk), &g_serk, fWinIni);
      CopySerialKeys(&s_asPrev.serk, &g_serk);
      fAnyNotifyChange = TRUE;
   }

   if (g_fSaveSettings) {
      if (RegCreateKeyEx(HKEY_CURRENT_USER, GENERAL_KEY, 0, __TEXT(""), REG_OPTION_NON_VOLATILE,
         KEY_SET_VALUE, NULL, &hkey, &dwDisposition) == ERROR_SUCCESS) {

         // Save the Warning Sounds entry
          if (g_fShowWarnMsgOnFeatureActivate != s_asPrev.fShowWarnMsgOnFeatureActivate)
          {
               RegSetValueEx(hkey, WARNING_SOUNDS, 0, REG_DWORD, (PBYTE) &g_fShowWarnMsgOnFeatureActivate,
                  sizeof(g_fShowWarnMsgOnFeatureActivate));
               s_asPrev.fShowWarnMsgOnFeatureActivate = g_fShowWarnMsgOnFeatureActivate;
          }

         // Save the Sound On Activation entry
          if (g_fPlaySndOnFeatureActivate != s_asPrev.fPlaySndOnFeatureActivate)
          {
              RegSetValueEx(hkey, SOUND_ON_ACTIVATION, 0, REG_DWORD, (PBYTE) &g_fPlaySndOnFeatureActivate,
                sizeof(g_fPlaySndOnFeatureActivate));
              s_asPrev.fPlaySndOnFeatureActivate = g_fPlaySndOnFeatureActivate;
          }
         RegCloseKey(hkey);
         hkey = NULL;
      }
   }

   // Sound Property page
   if (0 != memcmp(&g_ss, &s_asPrev.ss, sizeof(g_ss)))
   {
      if (g_fWinNT)
      {
         g_ss.dwFlags &= SSF_VALID;
      }
      AccessSystemParametersInfo(SPI_SETSOUNDSENTRY, sizeof(g_ss), &g_ss, fWinIni);
      s_asPrev.ss = g_ss;
      fAnyNotifyChange = TRUE;
   }


   // We do the sound property page last because the SPI_SETSHOWSOUNDS call is used
   // to send out notifications.  We make this call if either g_fShowSounds changed
   // or we need to send out notifications
   // Changed Nov.18 '98 to send out WM_SETTINGSCHANGE Seperately.

   if (g_fShowSounds != s_asPrev.fShowSounds /*||
      (fAnyNotifyChange && g_fSaveSettings)*/)
   {
      // if (g_fSaveSettings) fWinIni |= SPIF_SENDWININICHANGE;

      AccessSystemParametersInfo(SPI_SETSHOWSOUNDS, g_fShowSounds, NULL, fWinIni);
      s_asPrev.fShowSounds = g_fShowSounds;
   }

   g_SPISetValue = FALSE;

   // Do Admin options
   SaveDefaultSettings(g_fCopyToLogon, g_fCopyToDefault);

   SetCursor(LoadCursor(NULL, IDC_ARROW));
}


//////////////////////////////////////////////////////////////////////////


INT_PTR WINAPI KeyboardDlg (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
INT_PTR WINAPI SoundDlg (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
INT_PTR WINAPI GeneralDlg (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
INT_PTR WINAPI DisplayDlg (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
INT_PTR WINAPI MouseDlg (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

#define MAX_PAGES 10


// ************************************************************************
// OpenAccessPropertySheet
// Opens property sheet
// ************************************************************************

BOOL OpenAccessPropertySheet (HWND hwnd, int nID) {
   HPROPSHEETPAGE rPages[MAX_PAGES];
   PROPSHEETPAGE psp;
   PROPSHEETHEADER psh;
   INT_PTR nPsRet;

   KillAccStat();
   GetAccessibilitySettings();

   // Simple errorchecking - only allow control to move to tabs 0-4.
   // Any tab request greater than 4 is invalid - so default to tab 0
   if ((nID < 0) || (nID > 4)) nID = 0;

   // Initialize the property sheets
   psh.dwSize = sizeof(psh);
   // SteveDon 5-26-98
   // no longer use PSH_PROPTITLE because we want it to read "Accessibility Options"
   // rather than "Accessibility Properties" or "Properties for Accessibility"
   psh.dwFlags = 0;     // psh.dwFlags = PSH_PROPTITLE; // | PSH_PROPSHEETPAGE | PSP_USEICONID;
   psh.hwndParent = hwnd;
   psh.hInstance = g_hinst;
   psh.pszCaption = MAKEINTRESOURCE(IDS_PROPERTY_TITLE); //ACCESSIBILITY);
   psh.pszIcon = MAKEINTRESOURCE(IDI_ACCESS);
   psh.nPages = 0;
   psh.nStartPage = 0;
   psh.phpage = rPages;

   // Add First Sheet, keyboard
   psp.dwSize = sizeof(psp);
   psp.dwFlags = PSP_DEFAULT;
   psp.hInstance = g_hinst;
   psp.pszTemplate = MAKEINTRESOURCE(IDD_KEYBOARD);
   psp.pfnDlgProc = KeyboardDlg;
   psp.lParam = 0;

   psh.phpage[psh.nPages] = CreatePropertySheetPage(&psp);
   if (psh.phpage[psh.nPages]) psh.nPages++;

   // Add second sheet, Sound
   psp.dwSize = sizeof(psp);
   psp.dwFlags = PSP_DEFAULT;
   psp.hInstance = g_hinst;
   psp.pszTemplate = MAKEINTRESOURCE(IDD_SOUND);
   psp.pfnDlgProc = SoundDlg;
   psp.lParam = 0;

   psh.phpage[psh.nPages] = CreatePropertySheetPage(&psp);
   if (psh.phpage[psh.nPages]) psh.nPages++;

   // Add third sheet, Display
   psp.dwSize = sizeof(psp);
   psp.dwFlags = PSP_DEFAULT;
   psp.hInstance = g_hinst;
   psp.pszTemplate = MAKEINTRESOURCE(IDD_DISPLAY);
   psp.pfnDlgProc = DisplayDlg;
   psp.lParam = 0;

   psh.phpage[psh.nPages] = CreatePropertySheetPage(&psp);
   if (psh.phpage[psh.nPages]) psh.nPages++;

   // Add fourth sheet, Mouse
   psp.dwSize = sizeof(psp);
   psp.dwFlags = PSP_DEFAULT;
   psp.hInstance = g_hinst;
   psp.pszTemplate = MAKEINTRESOURCE(IDD_MOUSE);
   psp.pfnDlgProc = MouseDlg;
   psp.lParam = 0;

   psh.phpage[psh.nPages] = CreatePropertySheetPage(&psp);
   if (psh.phpage[psh.nPages]) psh.nPages++;

   // Add fifth sheet, General
   psp.dwSize = sizeof(psp);
   psp.dwFlags = PSP_DEFAULT;
   psp.hInstance = g_hinst;
   psp.pszTemplate = MAKEINTRESOURCE(IDD_GENERAL);
   psp.pfnDlgProc = GeneralDlg;
   psp.lParam = 0;

   psh.phpage[psh.nPages] = CreatePropertySheetPage(&psp);
   if (psh.phpage[psh.nPages]) psh.nPages++;

   // Simple errorchecking - only allow control to move to tabs 0 to psh.nPages
   // Any tab request greater than psh.nPages is invalid
   if (0 <= nID && nID < (int)psh.nPages)
   {
      psh.nStartPage = nID;
   }

   nPsRet = PropertySheet(&psh);

   if ( nPsRet <= 0 )
       return FALSE;
   else
       return TRUE;
}

///////////////////////////////// End of File /////////////////////////////////