/****************************** Module Header ******************************\
* Module Name: init.c
*
* Copyright (c) 1985 - 1999, Microsoft Corporation
*
* This module contains all the init code for win32k.sys.
*
* History:
* 18-Sep-1990 DarrinM   Created.
\***************************************************************************/

#include "precomp.h"
#pragma hdrstop

#if DBG
LIST_ENTRY gDesktopList;
#endif

PVOID gpCountTable;

//
// External references
//
extern PVOID *apObjects;


extern PKWAIT_BLOCK gWaitBlockArray;
extern PVOID UserAtomTableHandle;
extern PKTIMER gptmrWD;

extern UNICODE_STRING* gpastrSetupExe;
extern WCHAR* glpSetupPrograms;

extern PHANDLEPAGE gpHandlePages;

extern PBWL pbwlCache;

//
// Forward references
//

#if DBG
VOID InitGlobalThreadLockArray(
    DWORD dwIndex);
#endif

VOID CheckLUIDDosDevicesEnabled(
    PBOOL result);

/*
 * Local Constants.
 */
#define GRAY_STRLEN         40

/*
 * Globals local to this file only.
 */
BOOL bPermanentFontsLoaded;

/*
 * Globals shared with W32
 */
CONST ULONG W32ProcessSize = sizeof(PROCESSINFO);
CONST ULONG W32ProcessTag = TAG_PROCESSINFO;
CONST ULONG W32ThreadSize = sizeof(THREADINFO);
CONST ULONG W32ThreadTag = TAG_THREADINFO;
PFAST_MUTEX gpW32FastMutex;

NTSTATUS
DriverEntry(
    IN PDRIVER_OBJECT DriverObject,
    IN PUNICODE_STRING RegistryPath);

#pragma alloc_text(INIT, DriverEntry)

NTSTATUS Win32UserInitialize(VOID);

#if defined(_X86_)
ULONG Win32UserProbeAddress;
#endif

/*
 * holds the result of "Are LUID DosDevices maps enabled?"
 * TRUE  - LUID DosDevices are enabled
 * FALSE - LUID DosDevices are not enabled
 */
ULONG gLUIDDeviceMapsEnabled;

VOID FreeSMS(PSMS psms);
VOID FreeImeHotKeys(VOID);

extern PPAGED_LOOKASIDE_LIST SMSLookaside;
extern PPAGED_LOOKASIDE_LIST QEntryLookaside;

/*
 * Max time is 10 minutes. The count is 10 min * 60 sec * 4 for 250 ms.
 */
#define MAX_TIME_OUT  (10 * 60 * 4)

/***************************************************************************\
* Win32kNtUserCleanup
*
* History:
* 5-Jan-1997 CLupu   Created.
\***************************************************************************/
BOOL Win32kNtUserCleanup(
    VOID)
{
    int i;

    TRACE_HYDAPI(("Win32kNtUserCleanup: Cleanup Resources\n"));

    if (gpresUser != NULL) {
        EnterCrit();
    }

    DbgDumpTrackedDesktops(TRUE);

    /*
     * Anything in this list must be cleaned up when threads go away.
     */
    UserAssert(gpbwlList == NULL);
    UserAssert(gpWinEventHooks == NULL);
    UserAssert(gpScancodeMap == NULL);

    /*
     * Free IME hotkeys.
     */
    FreeImeHotKeys();

    /*
     * Free the wallpaper name string.
     */
    if (gpszWall != NULL) {
        UserFreePool(gpszWall);
        gpszWall = NULL;
    }

    /*
     * Free the hung redraw stuff.
     */
    if (gpvwplHungRedraw != NULL) {
        UserFreePool(gpvwplHungRedraw);
        gpvwplHungRedraw = NULL;
    }

    /*
     * Free the arrary of setup app names.
     */
    if (gpastrSetupExe) {
        UserFreePool(gpastrSetupExe);
        gpastrSetupExe = NULL;
    }

    if (glpSetupPrograms) {
        UserFreePool(glpSetupPrograms);
        glpSetupPrograms = NULL;
    }

    /*
     * Free the cached window list.
     */
    if (pbwlCache != NULL) {
        UserFreePool(pbwlCache);
        pbwlCache = NULL;
    }


    /*
     * Free outstanding timers.
     */
    while (gptmrFirst != NULL) {
        FreeTimer(gptmrFirst);
    }

    if (gptmrWD) {
        KeCancelTimer(gptmrWD);
        UserFreePool(gptmrWD);
        gptmrWD = NULL;
    }

    if (gptmrMaster) {
        KeCancelTimer(gptmrMaster);
        UserFreePool(gptmrMaster);
        gptmrMaster = NULL;
    }

    /*
     * Cleanup monitors and windows layout  snapshots
     */
    CleanupMonitorsAndWindowsSnapShot();

    /*
     * Cleanup PnP input device synchronization event.
     */
    if (gpEventPnPWainting != NULL) {
        FreeKernelEvent(&gpEventPnPWainting);
    }

    /*
     * Cleanup mouse & kbd change events
     */
    for (i = 0; i <= DEVICE_TYPE_MAX; i++) {
        UserAssert(gptiRit == NULL);
        if (aDeviceTemplate[i].pkeHidChange) {
            FreeKernelEvent(&aDeviceTemplate[i].pkeHidChange);
        }
    }

    /*
     * Cleanup any system thread parameters.
     */
    CSTCleanupStack(FALSE);
    CSTCleanupStack(TRUE);


    EnterDeviceInfoListCrit();

#ifdef GENERIC_INPUT
    CleanupHidRequestList();
#endif

    while (gpDeviceInfoList) {
        /*
         * Assert that there is no outstanding read or PnP thread waiting
         * in RequestDeviceChanges() for this device.
         * Clear these flags anyway, to force the free.
         */
        UserAssert((gpDeviceInfoList->bFlags & GDIF_READING) == 0);
        UserAssert((gpDeviceInfoList->usActions & GDIAF_PNPWAITING) == 0);
        gpDeviceInfoList->bFlags &= ~GDIF_READING;
        gpDeviceInfoList->usActions &= ~GDIAF_PNPWAITING;
        FreeDeviceInfo(gpDeviceInfoList);
    }

#ifdef TRACK_PNP_NOTIFICATION
    CleanupPnpNotificationRecord();
#endif

    LeaveDeviceInfoListCrit();

    /*
     * Cleanup object references
     */
    if (gThinwireFileObject)
        ObDereferenceObject(gThinwireFileObject);

    if (gVideoFileObject)
        ObDereferenceObject(gVideoFileObject);

    if (gpRemoteBeepDevice)
        ObDereferenceObject(gpRemoteBeepDevice);

    /*
     * Cleanup our resources. There should be no threads in here
     * when we get called.
     */
    if (gpresMouseEventQueue) {
        ExDeleteResourceLite(gpresMouseEventQueue);
        ExFreePool(gpresMouseEventQueue);
        gpresMouseEventQueue = NULL;
    }

    if (gpresDeviceInfoList) {
        ExDeleteResourceLite(gpresDeviceInfoList);
        ExFreePool(gpresDeviceInfoList);
        gpresDeviceInfoList = NULL;
    }

    if (gpkeMouseData != NULL) {
        FreeKernelEvent(&gpkeMouseData);
    }

    if (apObjects) {
        UserFreePool(apObjects);
        apObjects = NULL;
    }

    if (gWaitBlockArray) {
        UserFreePool(gWaitBlockArray);
        gWaitBlockArray = NULL;
    }

    if (gpEventDiconnectDesktop != NULL) {
        FreeKernelEvent(&gpEventDiconnectDesktop);
    }

    if (gpevtDesktopDestroyed != NULL) {
        FreeKernelEvent(&gpevtDesktopDestroyed);
    }

    if (gpEventScanGhosts != NULL) {
        FreeKernelEvent(&gpEventScanGhosts);
    }

    if (gpevtVideoportCallout != NULL) {
        FreeKernelEvent(&gpevtVideoportCallout);
    }

    if (UserAtomTableHandle != NULL) {
        RtlDestroyAtomTable(UserAtomTableHandle);
        UserAtomTableHandle = NULL;
    }

    /*
     * cleanup the SMS lookaside buffer
     */
    {
        PSMS psmsNext;

        while (gpsmsList != NULL) {
            psmsNext = gpsmsList->psmsNext;
            UserAssert(gpsmsList->spwnd == NULL);
            FreeSMS(gpsmsList);
            gpsmsList = psmsNext;
        }

        if (SMSLookaside != NULL) {
            ExDeletePagedLookasideList(SMSLookaside);
            UserFreePool(SMSLookaside);
            SMSLookaside = NULL;
        }
    }

    /*
     * Let go of the attached queue for hard error handling.
     * Do this before we free the Qlookaside !
     */
    if (gHardErrorHandler.pqAttach != NULL) {

        UserAssert(gHardErrorHandler.pqAttach > 0);
        UserAssert(gHardErrorHandler.pqAttach->QF_flags & QF_INDESTROY);

        FreeQueue(gHardErrorHandler.pqAttach);
        gHardErrorHandler.pqAttach = NULL;
    }

    /*
     * Free the cached array of queues
     */
    FreeCachedQueues();

    /*
     * cleanup the QEntry lookaside buffer
     */
    if (QEntryLookaside != NULL) {
        ExDeletePagedLookasideList(QEntryLookaside);
        UserFreePool(QEntryLookaside);
        QEntryLookaside = NULL;
    }

    /*
     * Cleanup the keyboard layouts
     */
    CleanupKeyboardLayouts();

    {
        PWOWTHREADINFO pwti;

        /*
         * Cleanup gpwtiFirst list. This list is supposed to be empty
         * at this point. In one case during stress we hit the case where
         * it was not empty. The assert is to catch that case in
         * checked builds.
         */

        while (gpwtiFirst != NULL) {
            pwti = gpwtiFirst;
            gpwtiFirst = pwti->pwtiNext;
            UserFreePool(pwti);
        }
    }

    /*
     * Cleanup cached SMWP array
     */
    if (gSMWP.acvr != NULL) {
        UserFreePool(gSMWP.acvr);
    }

    /*
     * Free gpsdInitWinSta. This is != NULL only if the session didn't
     * make it to UserInitialize.
     */
    if (gpsdInitWinSta != NULL) {
        UserFreePool(gpsdInitWinSta);
        gpsdInitWinSta = NULL;
    }

    if (gpHandleFlagsMutex != NULL) {
        ExFreePool(gpHandleFlagsMutex);
        gpHandleFlagsMutex = NULL;
    }

    /*
     * Delete the power request structures.
     */
    DeletePowerRequestList();

    if (gpresUser != NULL) {
        LeaveCrit();
        ExDeleteResourceLite(gpresUser);
        ExFreePool(gpresUser);
        gpresUser = NULL;
    }
#if DBG
    /*
     * Cleanup the global thread lock structures
     */
    for (i = 0; i < gcThreadLocksArraysAllocated; i++) {
        UserFreePool(gpaThreadLocksArrays[i]);
        gpaThreadLocksArrays[i] = NULL;
    }
#endif // DBG

#ifdef GENERIC_INPUT
#if DBG
    /*
     * Checkup the HID related memory leak.
     */
    CheckupHidLeak();
#endif // DBG
#endif // GENERIC_INPUT

    return TRUE;
}

#if DBG

/***************************************************************************\
* TrackAddDesktop
*
* Track desktops for cleanup purposes
*
* History:
* 04-Dec-1997 clupu     Created.
\***************************************************************************/
VOID TrackAddDesktop(
    PVOID pDesktop)
{
    PLIST_ENTRY Entry;
    PVOID       Atom;

    TRACE_HYDAPI(("TrackAddDesktop %#p\n", pDesktop));

    Atom = (PVOID)UserAllocPool(sizeof(PVOID) + sizeof(LIST_ENTRY),
                                TAG_TRACKDESKTOP);
    if (Atom) {

        *(PVOID*)Atom = pDesktop;

        Entry = (PLIST_ENTRY)(((PCHAR)Atom) + sizeof(PVOID));

        InsertTailList(&gDesktopList, Entry);
    }
}

/***************************************************************************\
* TrackRemoveDesktop
*
* Track desktops for cleanup purposes
*
* History:
* 04-Dec-1997 clupu     Created.
\***************************************************************************/
VOID TrackRemoveDesktop(
    PVOID pDesktop)
{
    PLIST_ENTRY NextEntry;
    PVOID       Atom;

    TRACE_HYDAPI(("TrackRemoveDesktop %#p\n", pDesktop));

    NextEntry = gDesktopList.Flink;

    while (NextEntry != &gDesktopList) {

        Atom = (PVOID)(((PCHAR)NextEntry) - sizeof(PVOID));

        if (pDesktop == *(PVOID*)Atom) {

            RemoveEntryList(NextEntry);

            UserFreePool(Atom);

            break;
        }

        NextEntry = NextEntry->Flink;
    }
}

/***************************************************************************\
* DumpTrackedDesktops
*
* Dumps the tracked desktops
*
* History:
* 04-Dec-1997 clupu     Created.
\***************************************************************************/
VOID DumpTrackedDesktops(
    BOOL bBreak)
{
    PLIST_ENTRY NextEntry;
    PVOID       pdesk;
    PVOID       Atom;
    int         nAlive = 0;

    TRACE_HYDAPI(("DumpTrackedDesktops\n"));

    NextEntry = gDesktopList.Flink;

    while (NextEntry != &gDesktopList) {

        Atom = (PVOID)(((PCHAR)NextEntry) - sizeof(PVOID));

        pdesk = *(PVOID*)Atom;

        KdPrint(("pdesk %#p\n", pdesk));

        /*
         * Restart at the begining of the list since our
         * entry got deleted
         */
        NextEntry = NextEntry->Flink;

        nAlive++;
    }
    if (bBreak && nAlive > 0) {
        RIPMSG0(RIP_ERROR, "Desktop objects still around\n");
    }
}

#endif // DBG

VOID DestroyRegion(
    HRGN* prgn)
{
    if (*prgn != NULL) {
        GreSetRegionOwner(*prgn, OBJECT_OWNER_CURRENT);
        GreDeleteObject(*prgn);
        *prgn = NULL;
    }
}

VOID DestroyBrush(
    HBRUSH* pbr)
{
    if (*pbr != NULL) {
        //GreSetBrushOwner(*pbr, OBJECT_OWNER_CURRENT);
        GreDeleteObject(*pbr);
        *pbr = NULL;
    }
}

VOID DestroyBitmap(
    HBITMAP* pbm)
{
    if (*pbm != NULL) {
        GreSetBitmapOwner(*pbm, OBJECT_OWNER_CURRENT);
        GreDeleteObject(*pbm);
        *pbm = NULL;
    }
}

VOID DestroyDC(
    HDC* phdc)
{
    if (*phdc != NULL) {
        GreSetDCOwner(*phdc, OBJECT_OWNER_CURRENT);
        GreDeleteDC(*phdc);
        *phdc = NULL;
    }
}

VOID DestroyFont(
    HFONT* pfnt)
{
    if (*pfnt != NULL) {
        GreDeleteObject(*pfnt);
        *pfnt = NULL;
    }
}

/***************************************************************************\
* CleanupGDI
*
* Cleanup all the GDI global objects used in USERK
*
* History:
* 29-Jan-1998 clupu     Created.
\***************************************************************************/
VOID CleanupGDI(
    VOID)
{
    int i;

    /*
     * Free gpDispInfo stuff
     */
    DestroyDC(&gpDispInfo->hdcScreen);
    DestroyDC(&gpDispInfo->hdcBits);
    DestroyDC(&gpDispInfo->hdcGray);
    DestroyDC(&ghdcMem);
    DestroyDC(&ghdcMem2);
    DestroyDC(&gfade.hdc);

    /*
     * Free the cache DC stuff before the GRE cleanup.
     * Also notice that we call DelayedDestroyCacheDC which
     * we usualy call from DestroyProcessInfo. We do it
     * here because this is the last WIN32 thread.
     */
    DestroyCacheDCEntries(PtiCurrent());
    DestroyCacheDCEntries(NULL);
    DelayedDestroyCacheDC();

    UserAssert(gpDispInfo->pdceFirst == NULL);

    /*
     * Free bitmaps
     */
    DestroyBitmap(&gpDispInfo->hbmGray);
    DestroyBitmap(&ghbmBits);
    DestroyBitmap(&ghbmCaption);

    /*
     * Cleanup brushes
     */
    DestroyBrush(&ghbrHungApp);
    DestroyBrush(&gpsi->hbrGray);
    DestroyBrush(&ghbrWhite);
    DestroyBrush(&ghbrBlack);

    for (i = 0; i < COLOR_MAX; i++) {
        DestroyBrush(&(SYSHBRUSH(i)));
    }

    /*
     * Cleanup regions
     */
    DestroyRegion(&gpDispInfo->hrgnScreen);
    DestroyRegion(&ghrgnInvalidSum);
    DestroyRegion(&ghrgnVisNew);
    DestroyRegion(&ghrgnSWP1);
    DestroyRegion(&ghrgnValid);
    DestroyRegion(&ghrgnValidSum);
    DestroyRegion(&ghrgnInvalid);
    DestroyRegion(&ghrgnInv0);
    DestroyRegion(&ghrgnInv1);
    DestroyRegion(&ghrgnInv2);
    DestroyRegion(&ghrgnGDC);
    DestroyRegion(&ghrgnSCR);
    DestroyRegion(&ghrgnSPB1);
    DestroyRegion(&ghrgnSPB2);
    DestroyRegion(&ghrgnSW);
    DestroyRegion(&ghrgnScrl1);
    DestroyRegion(&ghrgnScrl2);
    DestroyRegion(&ghrgnScrlVis);
    DestroyRegion(&ghrgnScrlSrc);
    DestroyRegion(&ghrgnScrlDst);
    DestroyRegion(&ghrgnScrlValid);

    /*
     * Cleanup fonts
     */
    DestroyFont(&ghSmCaptionFont);
    DestroyFont(&ghMenuFont);
    DestroyFont(&ghMenuFontDef);
    DestroyFont(&ghStatusFont);
    DestroyFont(&ghIconFont);
    DestroyFont(&ghFontSys);

#ifdef LAME_BUTTON
    DestroyFont(&ghLameFont);
#endif  // LAME_BUTTON

    /*
     * wallpaper stuff.
     */
    if (ghpalWallpaper != NULL) {
        GreSetPaletteOwner(ghpalWallpaper, OBJECT_OWNER_CURRENT);
        GreDeleteObject(ghpalWallpaper);
        ghpalWallpaper = NULL;
    }
    DestroyBitmap(&ghbmWallpaper);

    /*
     * Unload the video driver
     */
    if (gpDispInfo->pmdev) {
        DrvDestroyMDEV(gpDispInfo->pmdev);
        GreFreePool(gpDispInfo->pmdev);
        gpDispInfo->pmdev = NULL;
        gpDispInfo->hDev = NULL;
    }

    /*
     * Free the monitor stuff
     */
    {
        PMONITOR pMonitor;
        PMONITOR pMonitorNext;

        pMonitor = gpDispInfo->pMonitorFirst;

        while (pMonitor != NULL) {
            pMonitorNext = pMonitor->pMonitorNext;
            DestroyMonitor(pMonitor);
            pMonitor = pMonitorNext;
        }

        UserAssert(gpDispInfo->pMonitorFirst == NULL);

        if (gpMonitorCached != NULL) {
            DestroyMonitor(gpMonitorCached);
        }
    }
}


/***************************************************************************\
*   DestroyHandleTableObjects
*
*   Destroy any object still in the handle table.
*
\***************************************************************************/

VOID DestroyHandleFirstPass(PHE phe)
{
    /*
     * First pass for the handle object destruction.
     * Destroy the object, when we can. Otherwise,
     * links to the other handle object should be cleared
     * so that there will not be a dependency issues
     * in the final, second pass.
     */

    if (phe->phead->cLockObj == 0) {
        HMDestroyObject(phe->phead);
    } else {
        /*
         * The object couldn't be destroyed.
         */
        if (phe->bType == TYPE_KBDLAYOUT) {
            PKL pkl = (PKL)phe->phead;
            UINT i;

            /*
             * Clear out the pkf references (they will be
             * destroyed cleanly in the second run anyway)
             */
            pkl->spkf = NULL;
            pkl->spkfPrimary = NULL;
            if (pkl->pspkfExtra) {
                for (i = 0; i < pkl->uNumTbl; ++i) {
                    pkl->pspkfExtra[i] = NULL;
                }
            }
            pkl->uNumTbl = 0;
        }
    }
}

VOID DestroyHandleSecondPass(PHE phe)
{
    /*
     * Destroy the object.
     */
    if (phe->phead->cLockObj > 0) {

        RIPMSG2(RIP_WARNING, "DestroyHandleSecondPass: phe %#p still locked (%d)!", phe, phe->phead->cLockObj);

        /*
         * We're going to die, why bothered by the lock count?
         * We're forcing the lockcount to 0, and call the destroy routine.
         */
        phe->phead->cLockObj = 0;
    }
    HMDestroyUnlockedObject(phe);
    UserAssert(phe->bType == TYPE_FREE);
}

VOID DestroyHandleTableObjects(VOID)
{
    PHE         pheT;
    DWORD       i;
    VOID (*HandleDestroyer)(PHE);
#if DBG
    DWORD       nLeak;
#endif

    /*
     * Make sure the handle table was created !
     */
    if (gSharedInfo.aheList == NULL) {
        return;
    }

    /*
     * Loop through the table destroying all remaining objects.
     */

#if DBG
    RIPMSG0(RIP_WARNING, "==== Start handle leak check\n");
    nLeak = 0;
    for (i = 0; i <= giheLast; ++i) {
        pheT = gSharedInfo.aheList + i;

        if (pheT->bType != TYPE_FREE) {
            ++nLeak;
            RIPMSG3(RIP_WARNING, "  LEAK -- Handle %p @%p type=%x\n", pheT->phead->h, pheT, pheT->bType);
        }
    }
    RIPMSG1(RIP_WARNING, "==== Handle leak check finished: 0n%d leaks detected.\n", nLeak);
#endif

    /*
     * First pass: destroy it, or cut the link to other handle based object
     */
    HandleDestroyer = DestroyHandleFirstPass;

repeat:
    for (i = 0; i <= giheLast; ++i) {
        pheT = gSharedInfo.aheList + i;

        if (pheT->bType == TYPE_FREE)
            continue;

        UserAssert(!(gahti[pheT->bType].bObjectCreateFlags & OCF_PROCESSOWNED) &&
                   !(gahti[pheT->bType].bObjectCreateFlags & OCF_THREADOWNED));

        UserAssert(!(pheT->bFlags & HANDLEF_DESTROY));

        HandleDestroyer(pheT);
    }

    if (HandleDestroyer == DestroyHandleFirstPass) {
        /*
         * Go for the second pass.
         */
        HandleDestroyer = DestroyHandleSecondPass;
        goto repeat;
    }
}

/***************************************************************************\
* Win32KDriverUnload
*
* Exit point for win32k.sys
*
\***************************************************************************/
#ifdef TRACE_MAP_VIEWS
extern PWin32Section gpSections;
#endif

VOID Win32KDriverUnload(
    IN PDRIVER_OBJECT DriverObject)
{
    TRACE_HYDAPI(("Win32KDriverUnload\n"));

    UNREFERENCED_PARAMETER(DriverObject);

    HYDRA_HINT(HH_DRIVERUNLOAD);

    /*
     * Cleanup all resources in GRE
     */
    MultiUserNtGreCleanup();

    HYDRA_HINT(HH_GRECLEANUP);

    /*
     * Cleanup CSRSS
     */
    if (gpepCSRSS) {
        ObDereferenceObject(gpepCSRSS);
        gpepCSRSS = NULL;
    }

    /*
     * BUG 305965. There might be cases when we end up with DCEs still
     * in the list. Go ahead and clean it up here.
     */
    if (gpDispInfo != NULL && gpDispInfo->pdceFirst != NULL) {
        PDCE pdce, pdceNext;

        RIPMSG0(RIP_ERROR, "Win32KDriverUnload: the DCE list is not empty");

        pdce = gpDispInfo->pdceFirst;

        while (pdce != NULL) {
            pdceNext = pdce->pdceNext;

            UserFreePool(pdce);

            pdce = pdceNext;
        }
        gpDispInfo->pdceFirst = NULL;
    }

    /*
     * Cleanup all resources in ntuser
     */
    Win32kNtUserCleanup();

    /*
     * Cleanup the handle table for any object that is neither process
     * owned nor thread owned
     */
    DestroyHandleTableObjects();


    HYDRA_HINT(HH_USERKCLEANUP);

#if DBG || FRE_LOCK_RECORD
    HMCleanUpHandleTable();
#endif

    /*
     * Free the handle page array
     */

    if (gpHandlePages != NULL) {
        UserFreePool(gpHandlePages);
        gpHandlePages = NULL;
    }

    if (CsrApiPort != NULL) {
        ObDereferenceObject(CsrApiPort);
        CsrApiPort = NULL;
    }

    /*
     * Destroy the shared memory.
     */
    if (ghSectionShared != NULL) {
        NTSTATUS Status;

        gpsi = NULL;

        if (gpvSharedBase != NULL) {
            Win32HeapDestroy(gpvSharedAlloc);
            Status = Win32UnmapViewInSessionSpace(gpvSharedBase);
            UserAssert(NT_SUCCESS(Status));
        }

        Win32DestroySection(ghSectionShared);
    }

    CleanupWin32HeapStubs();

#ifdef TRACE_MAP_VIEWS
    if (gpSections != NULL) {
        FRE_RIPMSG3(RIP_ERROR, "Section being leaked; do \"d%cs 0x%p l%x\" to find stacktrace of the offender and triage against that", (sizeof(ULONG_PTR) == 8 ? 'q' : 'd'), (ULONG_PTR)gpSections + FIELD_OFFSET(Win32Section, trace), ARRAY_SIZE(gpSections->trace));
    }
#endif // TRACE_MAP_VIEWS

    /*
     * Cleanup all the user pool allocations by hand
     */
    CleanupMediaChange();
    CleanupPoolAllocations();

    CleanUpPoolLimitations();
    CleanUpSections();

    /*
     * Cleanup W32 structures.
     */
    if (gpW32FastMutex != NULL) {
        ExFreePool(gpW32FastMutex);
        gpW32FastMutex = NULL;
    }

    /*
     * Remove and free the service vector.
     */
    if (!gbRemoteSession) {
        KeRemoveSystemServiceTable(W32_SERVICE_NUMBER);
        if (gpCountTable != NULL) {
            ExFreePool(gpCountTable);
            gpCountTable = NULL;
        }
    }
}

/***************************************************************************\
* DriverEntry
*
* Entry point needed to initialize win32k.sys.
*
\***************************************************************************/
NTSTATUS DriverEntry(
    IN PDRIVER_OBJECT DriverObject,
    IN PUNICODE_STRING RegistryPath)
{
    NTSTATUS Status;
    OBJECT_ATTRIBUTES obja;
    UNICODE_STRING strName;
    HANDLE hEventFirstSession;

    UNREFERENCED_PARAMETER(RegistryPath);

    HYDRA_HINT(HH_DRIVERENTRY);

    gpvWin32kImageBase = DriverObject->DriverStart;

#if DBG
    /*
     * Initialize the desktop tracking list.
     */
    InitializeListHead(&gDesktopList);
#endif // DBG

#ifdef GENERIC_INPUT
    /*
     * Initialize the global HID request list.
     */
    InitializeHidRequestList();
#endif

    /*
     * Find out if this is a remote session.
     */
    RtlInitUnicodeString(&strName, L"\\UniqueSessionIdEvent");

    InitializeObjectAttributes(&obja,
                               &strName,
                               OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
                               NULL,
                               NULL);

    Status = ZwCreateEvent(&hEventFirstSession,
                           EVENT_ALL_ACCESS,
                           &obja,
                           SynchronizationEvent,
                           FALSE);
    if (!NT_SUCCESS(Status)) {
        if (Status == STATUS_OBJECT_NAME_COLLISION) {
            gbRemoteSession = TRUE;
        } else {
            goto Failure;
        }
    } else {
        gbRemoteSession = FALSE;
    }

    /*
     * Set the unload address
     */
    DriverObject->DriverUnload = Win32KDriverUnload;

    /*
     * Initialize data used for the timers. We want to do this really early,
     * before any Win32 Timer will be created. We need to be very careful to
     * not do anything that will need Win32 initialized yet.
     */

    gcmsLastTimer = NtGetTickCount();


    /*
     * Initialize the Win32 structures. We need to do this before we create
     * any threads.
     */
    gpW32FastMutex = ExAllocatePoolWithTag(NonPagedPool,
                                           sizeof(FAST_MUTEX),
                                           TAG_SYSTEM);
    if (gpW32FastMutex == NULL) {
        Status = STATUS_NO_MEMORY;
        goto Failure;
    }
    ExInitializeFastMutex(gpW32FastMutex);

    if (!gbRemoteSession) {

#if DBG
        /*
         * Allocate and zero the system service count table. Do not use
         * UserAllocPool for this allocation.
         */
        gpCountTable = ExAllocatePoolWithTag(NonPagedPool,
                                             W32pServiceLimit * sizeof(ULONG),
                                             'llac');
        if (gpCountTable == NULL) {
            Status = STATUS_NO_MEMORY;
            goto Failure;
        }

        RtlZeroMemory(gpCountTable, W32pServiceLimit * sizeof(ULONG));
#endif

        /*
         * We only establish the system entry table once for the
         * whole system, even though WIN32K.SYS is instanced on a winstation
         * basis. This is because the VM changes assure that all loads of
         * WIN32K.SYS are at the exact same address, even if a fixup had
         * to occur.
         */
        UserVerify(KeAddSystemServiceTable(W32pServiceTable,
                                           gpCountTable,
                                           W32pServiceLimit,
                                           W32pArgumentTable,
                                           W32_SERVICE_NUMBER));
    }

    /*
     * Initialize the critical section before establishing the callouts so
     * we can assume that it's always valid.
     */
    if (!InitCreateUserCrit()) {
        Status = STATUS_NO_MEMORY;
        goto Failure;
    }

    if (!gbRemoteSession) {
        WIN32_CALLOUTS_FPNS Win32Callouts;

        Win32Callouts.ProcessCallout = W32pProcessCallout;
        Win32Callouts.ThreadCallout = W32pThreadCallout;
        Win32Callouts.GlobalAtomTableCallout = UserGlobalAtomTableCallout;
        Win32Callouts.PowerEventCallout = UserPowerEventCallout;
        Win32Callouts.PowerStateCallout = UserPowerStateCallout;
        Win32Callouts.JobCallout = UserJobCallout;
        Win32Callouts.BatchFlushRoutine = (PVOID)NtGdiFlushUserBatch;

        Win32Callouts.DesktopOpenProcedure = (PKWIN32_OBJECT_CALLOUT)DesktopOpenProcedure;
        Win32Callouts.DesktopOkToCloseProcedure = (PKWIN32_OBJECT_CALLOUT)OkayToCloseDesktop;
        Win32Callouts.DesktopCloseProcedure = (PKWIN32_OBJECT_CALLOUT)UnmapDesktop;
        Win32Callouts.DesktopDeleteProcedure = (PKWIN32_OBJECT_CALLOUT)FreeDesktop;

        Win32Callouts.WindowStationOkToCloseProcedure = (PKWIN32_OBJECT_CALLOUT)OkayToCloseWindowStation;
        Win32Callouts.WindowStationCloseProcedure = (PKWIN32_OBJECT_CALLOUT)DestroyWindowStation;
        Win32Callouts.WindowStationDeleteProcedure = (PKWIN32_OBJECT_CALLOUT)FreeWindowStation;
        Win32Callouts.WindowStationParseProcedure = (PKWIN32_OBJECT_CALLOUT)ParseWindowStation;
        Win32Callouts.WindowStationOpenProcedure = (PKWIN32_OBJECT_CALLOUT)WindowStationOpenProcedure;

        PsEstablishWin32Callouts(&Win32Callouts);
    }

    Status = InitSectionTrace();
    if (!NT_SUCCESS(Status)) {
        goto Failure;
    }

    if (!InitWin32HeapStubs()) {
        Status = STATUS_NO_MEMORY;
        goto Failure;
    }

    /*
     * Initialize pool limitation array.
     */
    Status = InitPoolLimitations();
    if (!NT_SUCCESS(Status)) {
        goto Failure;
    }

    /*
     * Create the event that is signaled when a desktop does away
     */
    gpevtDesktopDestroyed = CreateKernelEvent(SynchronizationEvent, FALSE);
    if (gpevtDesktopDestroyed == NULL) {
        RIPMSG0(RIP_WARNING, "Couldn't create gpevtDesktopDestroyed");
        Status = STATUS_NO_MEMORY;
        goto Failure;
    }

    /*
     * Create the event that is signaled when no disconnect/reconnect is pending
     */
    gpevtVideoportCallout = CreateKernelEvent(NotificationEvent, FALSE);
    if (gpevtVideoportCallout == NULL) {
        RIPMSG0(RIP_WARNING, "Couldn't create gpevtVideoportCallout");
        Status = STATUS_NO_MEMORY;
        goto Failure;
    }

#if defined(_X86_)
    /*
     * Keep our own copy of this to avoid double indirections on probing
     */
    Win32UserProbeAddress = *MmUserProbeAddress;
#endif

    if ((hModuleWin = MmPageEntireDriver(DriverEntry)) == NULL) {
        RIPMSG0(RIP_WARNING, "MmPageEntireDriver failed");
        Status = STATUS_NO_MEMORY;
        goto Failure;
    }

#if DBG
    /*
     * Initialize the gpaThreadLocksArray mechanism
     */
    gFreeTLList = gpaThreadLocksArrays[gcThreadLocksArraysAllocated] =
    UserAllocPoolZInit(sizeof(TL)*MAX_THREAD_LOCKS, TAG_GLOBALTHREADLOCK);
    if (gFreeTLList == NULL) {
        Status = STATUS_NO_MEMORY;
        goto Failure;
    }
    InitGlobalThreadLockArray(0);
    gcThreadLocksArraysAllocated = 1;
#endif


    if (!InitializeGre()) {
        RIPMSG0(RIP_WARNING, "InitializeGre failed");
        Status = STATUS_NO_MEMORY;
        goto Failure;
    }

    Status = Win32UserInitialize();

    if (!NT_SUCCESS(Status)) {
        RIPMSG1(RIP_WARNING, "Win32UserInitialize failed with Status %x",
                Status);
        goto Failure;
    }

    /*
     * Remember Session Creation Time. This is used to decide if power
     * messages need to be sent.
     */
    gSessionCreationTime = KeQueryInterruptTime();

    //
    // Initialize rundown protection for WindowStation objects.
    //
    ExInitializeRundownProtection(&gWinstaRunRef);

    /*
     * Check if LUID DosDevices are enabled
     */
    CheckLUIDDosDevicesEnabled(&gLUIDDeviceMapsEnabled);

    return STATUS_SUCCESS;

Failure:

    RIPMSG1(RIP_WARNING,
            "Initialization of WIN32K.SYS failed with Status = 0x%x",
            Status);

    Win32KDriverUnload(NULL);
    return Status;
}

/***************************************************************************\
* xxxAddFontResourceW
*
*
* History:
\***************************************************************************/

int xxxAddFontResourceW(
    LPWSTR lpFile,
    FLONG  flags,
    DESIGNVECTOR *pdv)
{
    UNICODE_STRING strFile;

    RtlInitUnicodeString(&strFile, lpFile);

    return xxxClientAddFontResourceW(&strFile, flags, pdv);
}

/***************************************************************************\
* LW_DriversInit
*
*
* History:
\***************************************************************************/

VOID LW_DriversInit(VOID)
{
    /*
     * Initialize the keyboard typematic rate.
     */
    SetKeyboardRate((UINT)gnKeyboardSpeed);

    /*
     * Adjust VK modification table if not default (type 4) kbd.
     */
    if (gKeyboardInfo.KeyboardIdentifier.Type == 3)
        gapulCvt_VK = gapulCvt_VK_84;

    /*
     * Adjust VK modification table for IBM 5576 002/003 keyboard.
     */
    if (JAPANESE_KEYBOARD(gKeyboardInfo.KeyboardIdentifier) &&
        (gKeyboardInfo.KeyboardIdentifier.Subtype == 3))
        gapulCvt_VK = gapulCvt_VK_IBM02;

    /*
     * Initialize NLS keyboard globals.
     */
    NlsKbdInitializePerSystem();
}

/***************************************************************************\
* LoadCPUserPreferences
*
* 06/07/96  GerardoB  Created
\***************************************************************************/
BOOL LoadCPUserPreferences(
    PUNICODE_STRING pProfileUserName,
    DWORD dwPolicyOnly)
{
    DWORD pdwValue[SPI_BOOLMASKDWORDSIZE], dw;
    PPROFILEVALUEINFO ppvi = gpviCPUserPreferences;

    UserAssert(1 + SPI_DWORDRANGECOUNT == ARRAY_SIZE(gpviCPUserPreferences));

    /*
     * The first value in gpviCPUserPreferences corresponds to the bit mask
     */
    dw =  FastGetProfileValue(pProfileUserName,
                              ppvi->uSection,
                              ppvi->pwszKeyName,
                              NULL,
                              (LPBYTE)pdwValue,
                              sizeof(*pdwValue),
                              dwPolicyOnly);

    /*
     * Copy only the amount of data read and no more than what we expect.
     */
    if (dw != 0) {
        if (dw > sizeof(gpdwCPUserPreferencesMask)) {
            dw = sizeof(gpdwCPUserPreferencesMask);
        }

        RtlCopyMemory(gpdwCPUserPreferencesMask, pdwValue, dw);
    }

    ppvi++;

    /*
     * DWORD values
     */
    for (dw = 1; dw < 1 + SPI_DWORDRANGECOUNT; dw++, ppvi++) {
        if (FastGetProfileValue(pProfileUserName,
                                ppvi->uSection,
                                ppvi->pwszKeyName,
                                NULL,
                                (LPBYTE)pdwValue,
                                sizeof(DWORD),
                                dwPolicyOnly)) {

            ppvi->dwValue = *pdwValue;
        }
    }

    /*
     * Propagate gpsi flags
     */
    PropagetUPBOOLTogpsi(COMBOBOXANIMATION);
    PropagetUPBOOLTogpsi(LISTBOXSMOOTHSCROLLING);
    PropagetUPBOOLTogpsi(KEYBOARDCUES);
    SET_OR_CLEAR_SRVIF(SRVIF_KEYBOARDPREF, TEST_BOOL_ACCF(ACCF_KEYBOARDPREF));

    gpsi->uCaretWidth = UP(CARETWIDTH);
    SYSMET(CXFOCUSBORDER) = UP(FOCUSBORDERWIDTH);
    SYSMET(CYFOCUSBORDER) = UP(FOCUSBORDERHEIGHT);

    PropagetUPBOOLTogpsi(UIEFFECTS);

    EnforceColorDependentSettings();

    return TRUE;
}

/***************************************************************************\
* LW_LoadProfileInitData
*
* Only stuff that gets initialized at boot should go here. Per user settings
* should be initialized in xxxUpdatePerUserSystemParameters.
\***************************************************************************/
VOID LW_LoadProfileInitData(
    PUNICODE_STRING pProfileUserName)
{
    FastGetProfileIntFromID(pProfileUserName,
                            PMAP_WINDOWSM,
                            STR_DDESENDTIMEOUT,
                            0,
                            &guDdeSendTimeout,
                            0);
}

/***************************************************************************\
* LW_LoadResources
*
*
* History:
\***************************************************************************/
VOID LW_LoadResources(
    PUNICODE_STRING pProfileUserName)
{
    WCHAR rgch[4];

    /*
     * See if the Mouse buttons need swapping.
     */
    FastGetProfileStringFromIDW(pProfileUserName,
                                PMAP_MOUSE,
                                STR_SWAPBUTTONS,
                                szN,
                                rgch,
                                ARRAY_SIZE(rgch),
                                0);
    SYSMET(SWAPBUTTON) = (*rgch == '1' || *rgch == *szY || *rgch == *szy);

    /*
     * See if we should beep.
     */
    FastGetProfileStringFromIDW(pProfileUserName,
                                PMAP_BEEP,
                                STR_BEEP,
                                szY,
                                rgch,
                                ARRAY_SIZE(rgch),
                                0);

    SET_OR_CLEAR_PUDF(PUDF_BEEP, (rgch[0] == *szY) || (rgch[0] == *szy));

    /*
     * See if we should have extended sounds.
     */
    FastGetProfileStringFromIDW(pProfileUserName,
                                PMAP_BEEP,
                                STR_EXTENDEDSOUNDS,
                                szN,
                                rgch,
                                ARRAY_SIZE(rgch),
                                0);

    SET_OR_CLEAR_PUDF(PUDF_EXTENDEDSOUNDS, (rgch[0] == *szY || rgch[0] == *szy));
}

/***************************************************************************\
* xxxLoadSomeStrings
*
* This function loads a bunch of strings from the user32 resource string
* table
* This is done to keep all the localizable strings in user side to be MUI
* Manageable.
*
* History:
* 4-Mar-2000 MHamid    Created.
\***************************************************************************/
VOID xxxLoadSomeStrings(
    VOID)
{
    int i, str, id;

    /*
     * MessageBox strings.
     */
    for (i = 0, str = STR_OK, id = IDOK; i<MAX_MB_STRINGS; i++, str++, id++) {
        gpsi->MBStrings[i].uStr = str;
        gpsi->MBStrings[i].uID = id;
        xxxClientLoadStringW(str,
                             gpsi->MBStrings[i].szName,
                             ARRAY_SIZE(gpsi->MBStrings[i].szName));
    }

    /*
     * Load ToolTip strings.
     */
    xxxClientLoadStringW(STR_TT_MIN,     gszMIN,     ARRAY_SIZE(gszMIN));
    xxxClientLoadStringW(STR_TT_MAX,     gszMAX,     ARRAY_SIZE(gszMAX));
    xxxClientLoadStringW(STR_TT_RESUP,   gszRESUP,   ARRAY_SIZE(gszRESUP));
    xxxClientLoadStringW(STR_TT_RESDOWN, gszRESDOWN, ARRAY_SIZE(gszRESDOWN));
    xxxClientLoadStringW(STR_TT_SCLOSE,  gszSCLOSE,  ARRAY_SIZE(gszSCLOSE));
    xxxClientLoadStringW(STR_TT_HELP,    gszHELP,    ARRAY_SIZE(gszHELP));
}

/***************************************************************************\
* xxxInitWindowStation
*
* History:
* 6-Sep-1996 CLupu   Created.
* 21-Jan-98  SamerA  Renamed to xxxInitWindowStation since it may leave the
*                    critical section.
\***************************************************************************/
BOOL xxxInitWindowStation(
    VOID)
{
    TL tlName;
    PUNICODE_STRING pProfileUserName = CreateProfileUserName(&tlName);
    BOOL fRet;

    /*
     * Load all profile data first
     */
    LW_LoadProfileInitData(pProfileUserName);

    /*
     * Initialize User in a specific order.
     */
    LW_DriversInit();

    xxxLoadSomeStrings();

    /*
     * This is the initialization from Chicago
     */
    if (!(fRet = xxxSetWindowNCMetrics(pProfileUserName, NULL, TRUE, -1))) {
        RIPMSG0(RIP_WARNING, "xxxInitWindowStation failed in xxxSetWindowNCMetrics");
        goto Exit;
    }

    SetMinMetrics(pProfileUserName, NULL);

    if (!(fRet = SetIconMetrics(pProfileUserName, NULL))) {
        RIPMSG0(RIP_WARNING, "xxxInitWindowStation failed in SetIconMetrics");
        goto Exit;
    }

    if (!(fRet = FinalUserInit())) {
        RIPMSG0(RIP_WARNING, "xxxInitWindowStation failed in FinalUserInit");
        goto Exit;
    }

    /*
     * Initialize the key cache index.
     */
    gpsi->dwKeyCache = 1;

Exit:
    FreeProfileUserName(pProfileUserName, &tlName);

    return fRet;
}

/***************************************************************************\
* CreateTerminalInput
*
*
* History:
* 6-Sep-1996 CLupu   Created.
\***************************************************************************/
BOOL CreateTerminalInput(
    PTERMINAL pTerm)
{
    UserAssert(pTerm != NULL);

    /*
     * call to the client side to clean up the [Fonts] section
     * of the registry. This will only take significant chunk of time
     * if the [Fonts] key changed during since the last boot and if
     * there are lots of fonts loaded
     */
    ClientFontSweep();

    /*
     * Load the standard fonts before we create any DCs. At this time we can
     * only add the fonts that do not reside on the net. They may be needed
     * by winlogon. Our winlogon needs only ms sans serif, but private
     * winlogon's may need some other fonts as well. The fonts on the net
     * will be added later, right after all the net connections have been
     * restored.
     */
    xxxLW_LoadFonts(FALSE);

    /*
     * Initialize the input system.
     */
    if (!xxxInitInput(pTerm)) {
        return FALSE;
    }

    return TRUE;
}


/***************************************************************************\
* CI_GetClrVal
*
* Returns the RGB value of a color string from WIN.INI.
*
* History:
\***************************************************************************/
DWORD CI_GetClrVal(
    LPWSTR p,
    DWORD clrDefval)
{
    LPBYTE pl;
    BYTE val;
    int i;
    DWORD clrval;

    if (*p == UNICODE_NULL) {
        return clrDefval;
    }

    /*
     * Initialize the pointer to the LONG return value. Set to MSB.
     */
    pl = (LPBYTE)&clrval;

    /*
     * Get three goups of numbers seprated by non-numeric characters.
     */
    for (i = 0; i < 3; i++) {

        /*
         * Skip over any non-numeric characters within the string.
         */
        while ((*p != UNICODE_NULL) && !(*p >= TEXT('0') && *p <= TEXT('9'))) {
            p++;
        }

        /*
         * Are we (prematurely) at the end of the string?
         */
        if (*p == UNICODE_NULL) {
            RIPMSG0(RIP_WARNING, "CI_GetClrVal: Color string is corrupted");
            return clrDefval;
        }

        /*
         * Get the next series of digits.
         */
        val = 0;
        while (*p >= TEXT('0') && *p <= TEXT('9'))
            val = (BYTE)((int)val*10 + (int)*p++ - '0');

        /*
         * HACK! Store the group in the LONG return value.
         */
        *pl++ = val;
    }
    /*
     * Force the MSB to zero for GDI.
     */
    *pl = 0;

    return clrval;
}

/***************************************************************************\
* xxxODI_ColorInit
*
*
* History:
\***************************************************************************/

VOID xxxODI_ColorInit(PUNICODE_STRING pProfileUserName)
{
    int      i;
    COLORREF colorVals[STR_COLOREND - STR_COLORSTART + 1];
    INT      colorIndex[STR_COLOREND - STR_COLORSTART + 1];
    WCHAR    rgchValue[25];

#if COLOR_MAX - (STR_COLOREND - STR_COLORSTART + 1)
#error "COLOR_MAX value conflicts with STR_COLOREND - STR_COLORSTART"
#endif

    /*
     * Now set up default color values.
     * These are not in display drivers anymore since we just want default.
     * The real values are stored in the profile.
     */
    RtlCopyMemory(gpsi->argbSystem, gargbInitial, sizeof(COLORREF) * COLOR_MAX);
    RtlCopyMemory(gpsi->argbSystemUnmatched, gpsi->argbSystem, sizeof(COLORREF) * COLOR_MAX);

    for (i = 0; i < COLOR_MAX; i++) {
        LUID    luidCaller;

        /*
         * Try to find a WIN.INI entry for this object.
         */
        *rgchValue = 0;
        /*
         * Special case the background color. Try using Winlogon's value
         * if present. If the value doesn't exist then use USER's value.
         */
        if ((COLOR_BACKGROUND == i) &&
            NT_SUCCESS(GetProcessLuid(NULL, &luidCaller)) &&
            RtlEqualLuid(&luidCaller, &luidSystem)) {

            FastGetProfileStringFromIDW(pProfileUserName,
                                        PMAP_WINLOGON,
                                        STR_COLORSTART + COLOR_BACKGROUND,
                                        szNull,
                                        rgchValue,
                                        ARRAY_SIZE(rgchValue),
                                        0);
        }
        if (*rgchValue == 0) {
            FastGetProfileStringFromIDW(pProfileUserName,
                                        PMAP_COLORS,
                                        STR_COLORSTART + i,
                                        szNull,
                                        rgchValue,
                                        ARRAY_SIZE(rgchValue),
                                        0);
        }

        /*
         * Convert the string into an RGB value and store. Use the
         * default RGB value if the profile value is missing.
         */
        colorVals[i]  = CI_GetClrVal(rgchValue, gpsi->argbSystem[i]);
        colorIndex[i] = i;
    }

    xxxSetSysColors(pProfileUserName,
                    i,
                    colorIndex,
                    colorVals,
                    SSCF_FORCESOLIDCOLOR | SSCF_SETMAGICCOLORS);
}


/***********************************************************************\
* _LoadIconsAndCursors
*
* Used in parallel with the client side - LoadIconsAndCursors. This
* assumes that only the default configurable cursors and icons have
* been loaded and searches the global icon cache for them to fixup
* the default resource ids to standard ids. Also initializes the
* rgsys arrays allowing SYSCUR and SYSICO macros to work.
*
* 14-Oct-1995 SanfordS  created.
\***********************************************************************/
VOID _LoadCursorsAndIcons(
    VOID)
{
    PCURSOR pcur;
    int     i;

    pcur = gpcurFirst;

    /*
     * Only CSR can call this (and only once).
     */
    if (!ISCSRSS()) {
        return;
    }

    HYDRA_HINT(HH_LOADCURSORS);

    while (pcur) {
        UserAssert(!IS_PTR(pcur->strName.Buffer));

        switch (pcur->rt) {
        case RT_ICON:
            UserAssert((LONG_PTR)pcur->strName.Buffer >= OIC_FIRST_DEFAULT);

            UserAssert((LONG_PTR)pcur->strName.Buffer <
                    OIC_FIRST_DEFAULT + COIC_CONFIGURABLE);

            i = PTR_TO_ID(pcur->strName.Buffer) - OIC_FIRST_DEFAULT;
            pcur->strName.Buffer = (LPWSTR)gasysico[i].Id;

            if (pcur->CURSORF_flags & CURSORF_LRSHARED) {
                UserAssert(gasysico[i].spcur == NULL);
                Lock(&gasysico[i].spcur, pcur);
            } else {
                UserAssert(gpsi->hIconSmWindows == NULL);
                UserAssert((int)pcur->cx == SYSMET(CXSMICON));
                /*
                 * The special small winlogo icon is not shared.
                 */
                gpsi->hIconSmWindows = PtoH(pcur);
            }
            break;

        case RT_CURSOR:
            UserAssert((LONG_PTR)pcur->strName.Buffer >= OCR_FIRST_DEFAULT);

            UserAssert((LONG_PTR)pcur->strName.Buffer <
                    OCR_FIRST_DEFAULT + COCR_CONFIGURABLE);

            i = PTR_TO_ID(pcur->strName.Buffer) - OCR_FIRST_DEFAULT;
            pcur->strName.Buffer = (LPWSTR)gasyscur[i].Id;
            Lock(&gasyscur[i].spcur, pcur);
            break;

        default:
            // Should be nothing in the cache but these!
            RIPMSG1(RIP_ERROR, "Bogus object in cursor list: 0x%p", pcur);
        }

        pcur = pcur->pcurNext;
    }

    /*
     * copy special icon handles to global spots for later use.
     */
    gpsi->hIcoWindows = PtoH(SYSICO(WINLOGO));
}

/***********************************************************************\
* UnloadCursorsAndIcons
*
* Used for cleanup of win32k.
*
* Dec-10-1997 clupu  created.
\***********************************************************************/
VOID UnloadCursorsAndIcons(
    VOID)
{
    PCURSOR pcur;
    int     ind;

    TRACE_HYDAPI(("UnloadCursorsAndIcons\n"));

    /*
     * Unlock the icons.
     */
    for (ind = 0; ind < COIC_CONFIGURABLE; ind++) {
        pcur = gasysico[ind].spcur;

        if (pcur == NULL) {
            continue;
        }

        pcur->head.ppi = PpiCurrent();
        Unlock(&gasysico[ind].spcur);
    }

    /*
     * Unlock the cursors.
     */
    for (ind = 0; ind < COCR_CONFIGURABLE; ind++) {
        pcur = gasyscur[ind].spcur;

        if (pcur == NULL) {
            continue;
        }

        pcur->head.ppi = PpiCurrent();
        Unlock(&gasyscur[ind].spcur);
    }
}

/***********************************************************************\
* xxxUpdateSystemCursorsFromRegistry
*
* Reloads all customizable cursors from the registry.
*
* 09-Oct-1995 SanfordS  created.
\***********************************************************************/
VOID xxxUpdateSystemCursorsFromRegistry(
    PUNICODE_STRING pProfileUserName)
{
    int            i;
    UNICODE_STRING strName;
    TCHAR          szFilename[MAX_PATH];
    PCURSOR        pCursor;
    UINT           LR_flags;

    for (i = 0; i < COCR_CONFIGURABLE; i++) {
        FastGetProfileStringFromIDW(pProfileUserName,
                                    PMAP_CURSORS,
                                    gasyscur[i].StrId,
                                    TEXT(""),
                                    szFilename,
                                    ARRAY_SIZE(szFilename),
                                    0);

        if (*szFilename) {
            RtlInitUnicodeString(&strName, szFilename);
            LR_flags = LR_LOADFROMFILE | LR_ENVSUBST | LR_DEFAULTSIZE;
        } else {
            RtlInitUnicodeStringOrId(&strName,
                                     MAKEINTRESOURCE(i + OCR_FIRST_DEFAULT));
            LR_flags = LR_ENVSUBST | LR_DEFAULTSIZE;
        }

        pCursor = xxxClientLoadImage(&strName,
                                     0,
                                     IMAGE_CURSOR,
                                     0,
                                     0,
                                     LR_flags,
                                     FALSE);

        if (pCursor) {
            zzzSetSystemImage(pCursor, gasyscur[i].spcur);
        } else {
            RIPMSG1(RIP_WARNING, "Unable to update cursor. id=%x.", i + OCR_FIRST_DEFAULT);

        }
    }
}

/***********************************************************************\
* xxxUpdateSystemIconsFromRegistry
*
* Reloads all customizable icons from the registry.
*
* 09-Oct-1995 SanfordS  created.
\***********************************************************************/
VOID xxxUpdateSystemIconsFromRegistry(
    PUNICODE_STRING pProfileUserName)
{
    int            i;
    UNICODE_STRING strName;
    TCHAR          szFilename[MAX_PATH];
    PCURSOR        pCursor;
    UINT           LR_flags;

    for (i = 0; i < COIC_CONFIGURABLE; i++) {
        FastGetProfileStringFromIDW(pProfileUserName,
                                    PMAP_ICONS,
                                    gasysico[i].StrId,
                                    TEXT(""),
                                    szFilename,
                                    ARRAY_SIZE(szFilename),
                                    0);

        if (*szFilename) {
            RtlInitUnicodeString(&strName, szFilename);
            LR_flags = LR_LOADFROMFILE | LR_ENVSUBST;
        } else {
            RtlInitUnicodeStringOrId(&strName,
                                     MAKEINTRESOURCE(i + OIC_FIRST_DEFAULT));
            LR_flags = LR_ENVSUBST;
        }

        pCursor = xxxClientLoadImage(&strName,
                                     0,
                                     IMAGE_ICON,
                                     0,
                                     0,
                                     LR_flags,
                                     FALSE);

        RIPMSG3(RIP_VERBOSE,
                (!IS_PTR(strName.Buffer)) ?
                        "%#.8lx = Loaded id %ld" :
                        "%#.8lx = Loaded file %ws for id %ld",
                PtoH(pCursor),
                strName.Buffer,
                i + OIC_FIRST_DEFAULT);

        if (pCursor) {
            zzzSetSystemImage(pCursor, gasysico[i].spcur);
        } else {
            RIPMSG1(RIP_WARNING, "Unable to update icon. id=%ld", i + OIC_FIRST_DEFAULT);
        }

        /*
         * update the small winlogo icon which is referenced by gpsi.
         * Seems like we should load the small version for all configurable
         * icons anyway. What is needed is for CopyImage to support
         * copying of images loaded from files with LR_COPYFROMRESOURCE
         * allowing a reaload of the bits. (SAS)
         */
        if (i == OIC_WINLOGO_DEFAULT - OIC_FIRST_DEFAULT) {
            PCURSOR pCurSys = HtoP(gpsi->hIconSmWindows);

            if (pCurSys != NULL) {
                pCursor = xxxClientLoadImage(&strName,
                                             0,
                                             IMAGE_ICON,
                                             SYSMET(CXSMICON),
                                             SYSMET(CYSMICON),
                                             LR_flags,
                                             FALSE);

                if (pCursor) {
                    zzzSetSystemImage(pCursor, pCurSys);
                } else {
                    RIPMSG0(RIP_WARNING, "Unable to update small winlogo icon.");
                }
            }
        }
    }
}

/***************************************************************************\
* LW_BrushInit
*
*
* History:
\***************************************************************************/
BOOL LW_BrushInit(
    VOID)
{
    HBITMAP hbmGray;
    CONST static WORD patGray[8] = {0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa};

    /*
     * Create a gray brush to be used with GrayString.
     */
    hbmGray = GreCreateBitmap(8, 8, 1, 1, (LPBYTE)patGray);
    if (hbmGray == NULL) {
        return FALSE;
    }

    gpsi->hbrGray  = GreCreatePatternBrush(hbmGray);
    ghbrWhite = GreGetStockObject(WHITE_BRUSH);
    ghbrBlack = GreGetStockObject(BLACK_BRUSH);

    UserAssert(ghbrWhite != NULL && ghbrBlack != NULL);

    if (gpsi->hbrGray == NULL) {
        return FALSE;
    }

    GreDeleteObject(hbmGray);
    GreSetBrushOwnerPublic(gpsi->hbrGray);
    ghbrHungApp = GreCreateSolidBrush(0);

    if (ghbrHungApp == NULL) {
        return FALSE;
    }

    GreSetBrushOwnerPublic(ghbrHungApp);

    return TRUE;
}

/***************************************************************************\
* LW_RegisterWindows
*
*
* History:
\***************************************************************************/
BOOL LW_RegisterWindows(
    VOID)
{
    int i;
    WNDCLASSVEREX wndcls;
    PTHREADINFO ptiCurrent = PtiCurrent();
    BOOL fSuccess = TRUE;
    BOOL fSystem = (ptiCurrent->TIF_flags & TIF_SYSTEMTHREAD) != 0;

    CONST static struct {
        BOOLEAN     fSystem;
        BOOLEAN     fGlobalClass;
        WORD        fnid;
        UINT        style;
        WNDPROC     lpfnWndProc;
        int         cbWndExtra;
        BOOL        fNormalCursor : 1;
        HBRUSH      hbrBackground;
        LPCTSTR     lpszClassName;
    } rc[] = {
        { TRUE, TRUE, FNID_DESKTOP,
            CS_DBLCLKS,
            (WNDPROC)xxxDesktopWndProc,
            sizeof(DESKWND) - sizeof(WND),
            TRUE,
            (HBRUSH)(COLOR_BACKGROUND + 1),
            DESKTOPCLASS},
        { TRUE, FALSE, FNID_SWITCH,
            CS_VREDRAW | CS_HREDRAW | CS_SAVEBITS,
            (WNDPROC)xxxSwitchWndProc,
            sizeof(SWITCHWND) - sizeof(WND),
            TRUE,
            NULL,
            SWITCHWNDCLASS},
        { TRUE, FALSE, FNID_MENU,
            CS_DBLCLKS | CS_SAVEBITS | CS_DROPSHADOW,
            (WNDPROC)xxxMenuWindowProc,
            sizeof(PPOPUPMENU),
            FALSE,
            (HBRUSH)(COLOR_MENU + 1),
            MENUCLASS},
        { FALSE, FALSE, FNID_SCROLLBAR,
            CS_VREDRAW | CS_HREDRAW | CS_DBLCLKS | CS_PARENTDC,
            (WNDPROC)xxxSBWndProc,
            sizeof(SBWND) - sizeof(WND),
            TRUE,
            NULL,
            L"ScrollBar"},
        { TRUE, FALSE, FNID_TOOLTIP,
            CS_DBLCLKS | CS_SAVEBITS,
            (WNDPROC)xxxTooltipWndProc,
            sizeof(TOOLTIPWND) - sizeof(WND),
            TRUE,
            NULL,
            TOOLTIPCLASS},
        { TRUE, TRUE, FNID_ICONTITLE,
            0,
            (WNDPROC)xxxDefWindowProc,
            0,
            TRUE,
            NULL,
            ICONTITLECLASS},
        { FALSE, FALSE, 0,
            0,
            (WNDPROC)xxxEventWndProc,
            sizeof(PSVR_INSTANCE_INFO),
            FALSE,
            NULL,
            L"DDEMLEvent"},
#ifdef HUNGAPP_GHOSTING
        { TRUE, TRUE, FNID_GHOST,
            0,
            (WNDPROC)xxxGhostWndProc,
            0,
            TRUE,
            NULL,
            L"Ghost"},
#endif // HUNGAPP_GHOSTING
        { TRUE, TRUE, 0,
            0,
            (WNDPROC)xxxDefWindowProc,
            0,
            TRUE,
            NULL,
            L"SysShadow"},
        { TRUE, TRUE, FNID_MESSAGEWND,
            0,
            (WNDPROC)xxxDefWindowProc,
            4,
            TRUE,
            NULL,
            szMESSAGE}
    };


    /*
     * All other classes are registered via the table.
     */
    wndcls.cbClsExtra   = 0;
    wndcls.hInstance    = hModuleWin;
    wndcls.hIcon        = NULL;
    wndcls.hIconSm      = NULL;
    wndcls.lpszMenuName = NULL;

    for (i = 0; i < ARRAY_SIZE(rc); i++) {
        if (fSystem && !rc[i].fSystem) {
            continue;
        }
        wndcls.style        = rc[i].style;
        wndcls.lpfnWndProc  = rc[i].lpfnWndProc;
        wndcls.cbWndExtra   = rc[i].cbWndExtra;
        wndcls.hCursor      = rc[i].fNormalCursor ? PtoH(SYSCUR(ARROW)) : NULL;
        wndcls.hbrBackground= rc[i].hbrBackground;
        wndcls.lpszClassName= rc[i].lpszClassName;
        wndcls.lpszClassNameVer= rc[i].lpszClassName;

        if (InternalRegisterClassEx(&wndcls,
                                    rc[i].fnid,
                                    CSF_SERVERSIDEPROC | CSF_WIN40COMPAT) == NULL) {
            RIPMSG0(RIP_WARNING, "LW_RegisterWindows: InternalRegisterClassEx failed");
            fSuccess = FALSE;
            break;
        }

        if (fSystem && rc[i].fGlobalClass) {
            if (InternalRegisterClassEx(&wndcls,
                                    rc[i].fnid,
                                    CSF_SERVERSIDEPROC | CSF_SYSTEMCLASS | CSF_WIN40COMPAT) == NULL) {

                RIPMSG0(RIP_WARNING, "LW_RegisterWindows: InternalRegisterClassEx failed");
                fSuccess = FALSE;
                break;
            }
        }
    }

    if (fSuccess) {
        ptiCurrent->ppi->W32PF_Flags |= W32PF_CLASSESREGISTERED;
#ifndef LAZY_CLASS_INIT
        if (!fSystem && ptiCurrent->pClientInfo) {
            try {
                ptiCurrent->pClientInfo->CI_flags |= CI_REGISTERCLASSES;
            } except (W32ExceptionHandler(TRUE, RIP_WARNING)) {
                   fSuccess = FALSE;
            }
        }
#endif
    }

    return fSuccess;
}

/**********************************************************\
* VOID vCheckMMInstance
*
* History:
*  Feb-06-98    Xudong Wu [TessieW]
* Wrote it.
\**********************************************************/
VOID vCheckMMInstance(
    LPWSTR pchSrch,
    DESIGNVECTOR  *pdv)
{
    LPWSTR  pKeyName;
    WCHAR   szName[MAX_PATH], *pszName = szName;
    WCHAR   szCannonicalName[MAX_PATH];
    ULONG   NumAxes;

    pdv->dvNumAxes = 0;
    pKeyName = pchSrch;
    while (*pKeyName && (*pKeyName++ != TEXT('('))) {
        /* do nothing */;
    }

    if (*pKeyName){
        if (!_wcsicmp(pKeyName, L"OpenType)")) {
            pKeyName = pchSrch;
            while(*pKeyName != TEXT('(')) {
                *pszName++ = *pKeyName++;
            }
            *pszName = 0;

            GreGetCannonicalName(szName, szCannonicalName, &NumAxes, pdv);
        }
    }
}

BOOL bEnumerateRegistryFonts(
    BOOL bPermanent)
{
    LPWSTR pchKeys, pchSrch, lpchT;
    int cchReal, cFont;
    WCHAR szFontFile[MAX_PATH];
    FLONG flAFRW;
    TL tlPool;
    DESIGNVECTOR  dv;
    WCHAR szPreloadFontFile[MAX_PATH];
    static int LastFontLoaded = -1;

    /*
     * if we are not just checking whether this is a registry font
     */
    flAFRW = (bPermanent ? AFRW_ADD_LOCAL_FONT : AFRW_ADD_REMOTE_FONT);

    cchReal = (int)FastGetProfileKeysW(NULL,
            PMAP_FONTS,
            TEXT("vgasys.fnt"),
            &pchKeys
            );

#if DBG
    if (cchReal == 0) {
        RIPMSG0(RIP_WARNING, "bEnumerateRegistryFonts: cchReal is 0");
    }
#endif

    if (!pchKeys) {
        return FALSE;
    }

    ThreadLockPool(PtiCurrent(), pchKeys, &tlPool);

    /*
     * If we got here first, we load the fonts until this preload font.
     * Preload fonts will be used by Winlogon UI, then we need to make sure
     * the font is available when Winlogon UI comes up.
     */
    if (LastFontLoaded == -1) {
        FastGetProfileStringW(NULL, PMAP_WINLOGON,
                              TEXT("PreloadFontFile"),
                              TEXT("Micross.ttf"),
                              szPreloadFontFile,
                              MAX_PATH,
                              0);
        RIPMSG1(RIP_VERBOSE, "Winlogon preload font = %ws\n",szPreloadFontFile);
    }

    /*
     * Now we have all the key names in pchKeys.
     */
    if (cchReal != 0) {

        cFont   = 0;
        pchSrch = pchKeys;

        do {
            // check to see whether this is MM(OpenType) instance
            vCheckMMInstance(pchSrch, &dv);

            if (FastGetProfileStringW(NULL,
                                      PMAP_FONTS,
                                      pchSrch,
                                      TEXT("vgasys.fon"),
                                      szFontFile,
                                      (MAX_PATH - 5),
                                      0)) {

                /*
                 * If no extension, append ".FON"
                 */
                for (lpchT = szFontFile; *lpchT != TEXT('.'); lpchT++) {

                    if (*lpchT == 0) {
                        wcscat(szFontFile, TEXT(".FON"));
                        break;
                    }
                }

                if ((cFont > LastFontLoaded) && bPermanent) {

                    /*
                     * skip if we've already loaded this local font.
                     */
                    xxxAddFontResourceW(szFontFile, flAFRW, dv.dvNumAxes ? &dv : NULL);
                }

                if (!bPermanent) {
                    xxxAddFontResourceW(szFontFile, flAFRW, dv.dvNumAxes ? &dv : NULL);
                }

                if ((LastFontLoaded == -1) &&
                    /*
                     * Compare with the font file name from Registry.
                     */
                    (!_wcsnicmp(szFontFile, szPreloadFontFile, wcslen(szPreloadFontFile))) &&
                    (bPermanent)) {

                    /*
                     * On the first time through only load up until
                     * ms sans serif for winlogon to use. Later we
                     * will spawn off a thread which loads the remaining
                     * fonts in the background.
                     */
                    LastFontLoaded = cFont;

                    ThreadUnlockAndFreePool(PtiCurrent(), &tlPool);
                    return TRUE;
                }
            }

            /*
             * Skip to the next key.
             */
            while (*pchSrch++) {
                /* do nothing */;
            }

            cFont += 1;
        } while (pchSrch < ((LPWSTR)pchKeys + cchReal));
    }

    /*
     * signal that all the permanent fonts have been loaded
     */
    bPermanentFontsLoaded = TRUE;

    ThreadUnlockAndFreePool(PtiCurrent(), &tlPool);

    if (!bPermanent) {
        SET_PUDF(PUDF_FONTSARELOADED);
    }

    return TRUE;
}

extern VOID CloseFNTCache(VOID);

/***************************************************************************\
* xxxLW_LoadFonts
*
*
* History:
\***************************************************************************/
VOID xxxLW_LoadFonts(
    BOOL bRemote)
{
    BOOL bTimeOut = FALSE;

    if (bRemote) {
        LARGE_INTEGER li;
        ULONG         ulWaitCount = 0;

        /*
         * Before we can proceed we must make sure that all the permanent
         * fonts  have been loaded.
         */

        while (!bPermanentFontsLoaded) {
            if (!gbRemoteSession || ulWaitCount < MAX_TIME_OUT) {
                LeaveCrit();
                li.QuadPart = (LONGLONG)-10000 * CMSSLEEP;
                KeDelayExecutionThread(KernelMode, FALSE, &li);
                EnterCrit();
            } else {
                bTimeOut = TRUE;
                break;
            }

            ulWaitCount++;
        }

        if (!bTimeOut) {
            if (!bEnumerateRegistryFonts(FALSE)) {
                return; // Nothing we can do.
            }

            // Add remote type 1 fonts.
            ClientLoadRemoteT1Fonts();
        }
    } else {
        xxxAddFontResourceW(L"marlett.ttf", AFRW_ADD_LOCAL_FONT,NULL);
        if (!bEnumerateRegistryFonts(TRUE)) {
            return; // Nothing we can do.
        }

        //
        // Add local type 1 fonts.
        // Only want to be called once, the second time after ms sans serif
        // was installed
        //
        if (bPermanentFontsLoaded) {
            ClientLoadLocalT1Fonts();

            // All the fonts loaded, we can close the FNTCache.
            CloseFNTCache();
        }

    }
}

/***************************************************************************\
* FinalUserInit
*
* History:
\***************************************************************************/
BOOL FinalUserInit(
    VOID)
{
    HBITMAP hbm;
    PPCLS   ppcls;

    gpDispInfo->hdcGray = GreCreateCompatibleDC(gpDispInfo->hdcScreen);

    if (gpDispInfo->hdcGray == NULL) {
        return FALSE;
    }

    GreSelectFont(gpDispInfo->hdcGray, ghFontSys);
    GreSetDCOwner(gpDispInfo->hdcGray, OBJECT_OWNER_PUBLIC);

    gpDispInfo->cxGray = gpsi->cxSysFontChar * GRAY_STRLEN;
    gpDispInfo->cyGray = gpsi->cySysFontChar + 2;
    gpDispInfo->hbmGray = GreCreateBitmap(gpDispInfo->cxGray, gpDispInfo->cyGray, 1, 1, 0L);

    if (gpDispInfo->hbmGray == NULL) {
        return FALSE;
    }

    GreSetBitmapOwner(gpDispInfo->hbmGray, OBJECT_OWNER_PUBLIC);

    hbm = GreSelectBitmap(gpDispInfo->hdcGray, gpDispInfo->hbmGray);
    GreSetTextColor(gpDispInfo->hdcGray, 0x00000000L);
    GreSelectBrush(gpDispInfo->hdcGray, gpsi->hbrGray);
    GreSetBkMode(gpDispInfo->hdcGray, OPAQUE);
    GreSetBkColor(gpDispInfo->hdcGray, 0x00FFFFFFL);

    /*
     * Setup menu animation dc for global menu state
     */
    if (MNSetupAnimationDC(&gMenuState)) {
        GreSetDCOwner(gMenuState.hdcAni, OBJECT_OWNER_PUBLIC);
    } else {
        RIPMSG0(RIP_WARNING, "FinalUserInit: MNSetupAnimationDC failed");
    }

    /*
     * Creation of the queue registers some bogus classes. Get rid
     * of them and register the real ones.
     */
    ppcls = &PpiCurrent()->pclsPublicList;
    while ((*ppcls != NULL) && !((*ppcls)->style & CS_GLOBALCLASS)) {
        DestroyClass(ppcls);
    }

    return TRUE;
}

/***************************************************************************\
* InitializeClientPfnArrays
*
* This routine gets called by the client to tell the kernel where
* its important functions can be located.
*
* 18-Apr-1995 JimA  Created.
\***************************************************************************/
NTSTATUS InitializeClientPfnArrays(
    CONST PFNCLIENT *ppfnClientA,
    CONST PFNCLIENT *ppfnClientW,
    CONST PFNCLIENTWORKER *ppfnClientWorker,
    HANDLE hModUser)
{
    static BOOL fHaveClientPfns = FALSE;

    /*
     * Remember client side addresses in this global structure. These are
     * always constant, so this is ok. Note that if either of the pointers
     * are invalid, the exception will be handled in the thunk and
     * fHaveClientPfns will not be set.
     */
    if (!fHaveClientPfns && ppfnClientA != NULL) {
        if (!ISCSRSS()) {
            RIPMSG0(RIP_WARNING, "InitializeClientPfnArrays failed !csrss");
            return STATUS_ACCESS_DENIED;
        }
        gpsi->apfnClientA = *ppfnClientA;
        gpsi->apfnClientW = *ppfnClientW;
        gpsi->apfnClientWorker = *ppfnClientWorker;

        gpfnwp[ICLS_BUTTON]  = gpsi->apfnClientW.pfnButtonWndProc;
        gpfnwp[ICLS_EDIT]  = gpsi->apfnClientW.pfnDefWindowProc;
        gpfnwp[ICLS_STATIC]  = gpsi->apfnClientW.pfnStaticWndProc;
        gpfnwp[ICLS_LISTBOX]  = gpsi->apfnClientW.pfnListBoxWndProc;
        gpfnwp[ICLS_SCROLLBAR]  = (PROC)xxxSBWndProc;
        gpfnwp[ICLS_COMBOBOX]  = gpsi->apfnClientW.pfnComboBoxWndProc;
        gpfnwp[ICLS_DESKTOP]  = (PROC)xxxDesktopWndProc;
        gpfnwp[ICLS_DIALOG]  = gpsi->apfnClientW.pfnDialogWndProc;
        gpfnwp[ICLS_MENU]  = (PROC)xxxMenuWindowProc;
        gpfnwp[ICLS_SWITCH]  = (PROC)xxxSwitchWndProc;
        gpfnwp[ICLS_ICONTITLE] = gpsi->apfnClientW.pfnTitleWndProc;
        gpfnwp[ICLS_MDICLIENT] = gpsi->apfnClientW.pfnMDIClientWndProc;
        gpfnwp[ICLS_COMBOLISTBOX] = gpsi->apfnClientW.pfnComboListBoxProc;
        gpfnwp[ICLS_TOOLTIP] = (PROC)xxxTooltipWndProc;

        /*
         * Change this assert when new classes are added.
         */
        UserAssert(ICLS_MAX == ICLS_GHOST + 1);

        hModClient = hModUser;
        fHaveClientPfns = TRUE;
    }

    /*
     * Assert that user32.dll on the client side has loaded at the correct
     * address.
     */
    UserAssert(ppfnClientA == NULL ||
               gpsi->apfnClientA.pfnButtonWndProc == ppfnClientA->pfnButtonWndProc);

    return STATUS_SUCCESS;
}

/***************************************************************************\
* GetKbdLangSwitch
*
* read the kbd language hotkey setting - if any - from the registry and set
* LangToggle[] appropriately.
*
* values are:
*              1 : VK_MENU     (this is the default)
*              2 : VK_CONTROL
*              3 : none
* History:
\***************************************************************************/
BOOL GetKbdLangSwitch(
    PUNICODE_STRING pProfileUserName)
{
    DWORD dwToggle;
    LCID  lcid;

    FastGetProfileIntW(pProfileUserName,
                                  PMAP_UKBDLAYOUTTOGGLE,
                                  TEXT("Hotkey"),
                                  1,
                                  &dwToggle,
                                  0);

    gbGraveKeyToggle = FALSE;

    switch (dwToggle) {
    case 4:
        /*
         * Grave accent keyboard switch for thai locales
         */
        ZwQueryDefaultLocale(FALSE, &lcid);
        gbGraveKeyToggle = (PRIMARYLANGID(lcid) == LANG_THAI) ? TRUE : FALSE;
        /*
         * fall through (intentional) and disable the ctrl/alt toggle mechanism
         */
    case 3:
        gLangToggle[0].bVkey = 0;
        gLangToggle[0].bScan = 0;
        break;

    case 2:
        gLangToggle[0].bVkey = VK_CONTROL;
        break;

    default:
        gLangToggle[0].bVkey = VK_MENU;
        break;
    }

    return TRUE;
}

/***************************************************************************\
* HideMouseTrails
*
* Hide the mouse trails one by one.
*
* History:
* 04-10-00 MHamid     Created.
\***************************************************************************/
VOID HideMouseTrails(
    PWND pwnd,
    UINT message,
    UINT_PTR nID,
    LPARAM lParam)
{
    if (gMouseTrailsToHide > 0) {
        if (InterlockedDecrement(&gMouseTrailsToHide) < gMouseTrails) {
            GreMovePointer(gpDispInfo->hDev, gpsi->ptCursor.x, gpsi->ptCursor.y,
                           MP_PROCEDURAL);
        }
    }

    UNREFERENCED_PARAMETER(pwnd);
    UNREFERENCED_PARAMETER(message);
    UNREFERENCED_PARAMETER(nID);
    UNREFERENCED_PARAMETER(lParam);
}

/***************************************************************************\
*
*  SetMouseTrails
*
*      n = 0,1 turn off mouse trails.
*      n > 1   turn on mouse trails (Trials = n-1).
*
\***************************************************************************/
VOID SetMouseTrails(
    UINT n)
{
    CheckCritIn();

    SetPointer(FALSE);
    gMouseTrails = n ? n-1 : n;
    SetPointer(TRUE);

    if (!IsRemoteConnection() && (!!gtmridMouseTrails ^ !!gMouseTrails)) {
        if (gMouseTrails) {
            /*
             * Create the gtmridMouseTrails timer in the desktop thread,
             * becuase if we creat it here it will get killed when the current
             * thread (App thread calling SPI_SETMOUSETRAILS) get destroied.
             */
            _PostMessage(gTermIO.ptiDesktop->pDeskInfo->spwnd, WM_CREATETRAILTIMER, 0, 0);
        } else {
            FindTimer(NULL, gtmridMouseTrails, TMRF_RIT, TRUE);
            gtmridMouseTrails = 0;
        }
    }
}

#ifdef IMM_PER_LOGON
extern BOOL IsIMMEnabledSystem(VOID);
extern BOOL IsCTFIMEEnabledSystem(VOID);

BOOL UpdatePerUserImmEnabling(
    VOID)
{
    /*
     * Update the IME enabling flag
     */
    SET_OR_CLEAR_SRVIF(SRVIF_IME, IsIMMEnabledSystem());
    RIPMSGF1(RIP_VERBOSE, "New Imm flag = %d", !!IS_IME_ENABLED());

    /*
     * Update the CTFIME enabling flag
     */
    SET_OR_CLEAR_SRVIF(SRVIF_CTFIME_ENABLED, IsCTFIMEEnabledSystem());
    RIPMSG1(RIP_VERBOSE, "_UpdatePerUserImmEnabling: new CTFIME flag = %d", !!IS_CICERO_ENABLED());

    return TRUE;
}
#endif

/***************************************************************************\
* xxxUpdatePerUserSystemParameters
*
* Called by winlogon to set Window system parameters to the current user's
* profile.
*
* 18-Sep-1992 IanJa     Created.
* 18-Nov-1993 SanfordS  Moved more winlogon init code to here for speed.
* 31-Mar-2001 Msadek    Changed parm from BOOL to flags for TS Slow Link
*                       Perf DCR.
* 02-Feb-2002 MMcCr     Added SPI_SETBLOCKSENDINPUTRESETS feature
\***************************************************************************/
BOOL xxxUpdatePerUserSystemParameters(
    DWORD  dwFlags)
{
    /*
     * NB - Any local variables that are used in appiPolicy must be initialized.
     * Otherwise, during a policy change, it is possible for them to be used w/o
     * being initialized and all heck breaks loose. Windows Bug #314150.
     */
    int             i;
    HANDLE          hKey;
    DWORD           dwFontSmoothing = GreGetFontEnumeration();
    DWORD           dwFontSmoothingContrast;
    DWORD           dwFontSmoothingOrientation;
    BOOL            fDragFullWindows = TEST_PUDF(PUDF_DRAGFULLWINDOWS);
    TL              tlName;
    PUNICODE_STRING pProfileUserName = NULL;
    DWORD           dwPolicyFlags = 0;
    DWORD           dwData;
    BOOL            bPolicyChange;
    BOOL            bUserLoggedOn;
    BOOL            bRemoteSettings;

    SPINFO spiPolicy[] = {
        { PMAP_DESKTOP,  SPI_SETBLOCKSENDINPUTRESETS, STR_BLOCKSENDINPUTRESETS, 0 },
        { PMAP_DESKTOP,  SPI_SETSCREENSAVETIMEOUT,    STR_SCREENSAVETIMEOUT,    0 },
        { PMAP_DESKTOP,  SPI_SETSCREENSAVEACTIVE,     STR_SCREENSAVEACTIVE,     0 },
        { PMAP_DESKTOP,  SPI_SETDRAGHEIGHT,           STR_DRAGHEIGHT,           4 },
        { PMAP_DESKTOP,  SPI_SETDRAGWIDTH,            STR_DRAGWIDTH,            4 },
        { PMAP_DESKTOP,  SPI_SETWHEELSCROLLLINES,     STR_WHEELSCROLLLINES,     3 },
    };

    SPINFO spiNotPolicy[] = {
        { PMAP_KEYBOARD, SPI_SETKEYBOARDDELAY,     STR_KEYDELAY,          0 },
        { PMAP_KEYBOARD, SPI_SETKEYBOARDSPEED,     STR_KEYSPEED,         15 },
        { PMAP_MOUSE,    SPI_SETDOUBLECLICKTIME,   STR_DBLCLKSPEED,     500 },
        { PMAP_MOUSE,    SPI_SETDOUBLECLKWIDTH,    STR_DOUBLECLICKWIDTH,  4 },
        { PMAP_MOUSE,    SPI_SETDOUBLECLKHEIGHT,   STR_DOUBLECLICKHEIGHT, 4 },
        { PMAP_MOUSE,    SPI_SETSNAPTODEFBUTTON,   STR_SNAPTO,            0 },
        { PMAP_WINDOWSU, SPI_SETMENUDROPALIGNMENT, STR_MENUDROPALIGNMENT, 0 },
        { PMAP_INPUTMETHOD, SPI_SETSHOWIMEUI,      STR_SHOWIMESTATUS,     1 },
    };

    PROFINTINFO apiiPolicy[] = {
        { PMAP_DESKTOP,  (LPWSTR)STR_MENUSHOWDELAY,       400, &gdtMNDropDown },
        { PMAP_DESKTOP,  (LPWSTR)STR_DRAGFULLWINDOWS,       2, &fDragFullWindows },
        { PMAP_DESKTOP,  (LPWSTR)STR_FASTALTTABROWS,        3, &gnFastAltTabRows },
        { PMAP_DESKTOP,  (LPWSTR)STR_FASTALTTABCOLUMNS,     7, &gnFastAltTabColumns },
        { PMAP_DESKTOP,  (LPWSTR)STR_MAXLEFTOVERLAPCHARS,   3, &(gpsi->wMaxLeftOverlapChars) },
        { PMAP_DESKTOP,  (LPWSTR)STR_MAXRIGHTOVERLAPCHARS,  3, &(gpsi->wMaxRightOverlapChars) },
        { PMAP_DESKTOP,  (LPWSTR)STR_FONTSMOOTHING,         0, &dwFontSmoothing },
        { 0,             NULL,                              0, NULL }
    };

    PROFINTINFO apiiNoPolicy[] = {
        { PMAP_MOUSE,       (LPWSTR)STR_MOUSETHRESH1, 6,  &gMouseThresh1 },
        { PMAP_MOUSE,       (LPWSTR)STR_MOUSETHRESH2, 10, &gMouseThresh2 },
        { PMAP_MOUSE,       (LPWSTR)STR_MOUSESPEED,   1,  &gMouseSpeed },
        { PMAP_INPUTMETHOD, (LPWSTR)STR_HEXNUMPAD,    0,  &gfEnableHexNumpad },
        { 0,                NULL,                     0,  NULL }
    };

    UserAssert(IsWinEventNotifyDeferredOK());

    bPolicyChange = dwFlags & UPUSP_POLICYCHANGE;
    bUserLoggedOn = dwFlags & UPUSP_USERLOGGEDON;
    bRemoteSettings = dwFlags & UPUSP_REMOTESETTINGS;

    /*
     * Make sure the caller is the logon process.
     */
    if (PsGetCurrentProcessId() != gpidLogon) {
        if (!bPolicyChange) {
            RIPMSG0(RIP_WARNING, "Access denied in xxxUpdatePerUserSystemParameters");
        }

        return FALSE;
    }

    pProfileUserName = CreateProfileUserName(&tlName);

    /*
     * If the desktop policy hasn't changed and we came here because we
     * thought it had, we're done.
     */
    if (bPolicyChange && !bRemoteSettings) {
        if (!CheckDesktopPolicyChange(pProfileUserName)) {
            FreeProfileUserName(pProfileUserName, &tlName);
            return FALSE;
        }
        dwPolicyFlags = POLICY_ONLY;
        UserAssert(!bUserLoggedOn);
    }

    /*
     * If new user is logging in, we need to recheck for
     * user policy changes.
     */
    if (bUserLoggedOn) {
        gdwPolicyFlags |= POLICY_USER;
    }

    /*
     * We don't want remote settings to be read all the time so spcify it here
     * if the caller wants to.Update it here since we do not save it in
     * gdwPolicyFlags [msadek].
     */
    if (bRemoteSettings) {
        dwPolicyFlags |= POLICY_REMOTE;
    }

    /*
     * Get the timeout for low level hooks from the registry.
     */
    dwData = 300;
    FastGetProfileValue(pProfileUserName,
                        PMAP_DESKTOP,
                        (LPWSTR)STR_LLHOOKSTIMEOUT,
                        (LPBYTE)&dwData,
                        (LPBYTE)&gnllHooksTimeout,
                        sizeof(int),
                        dwPolicyFlags);

    /*
     * Control Panel User Preferences.
     */
    LoadCPUserPreferences(pProfileUserName, dwPolicyFlags);

#ifdef LAME_BUTTON

    /*
     * Lame button text.
     */
    FastGetProfileValue(pProfileUserName,
                        PMAP_DESKTOP,
                        (LPWSTR)STR_LAMEBUTTONENABLED,
                        NULL,
                        (LPBYTE)&gdwLameFlags,
                        sizeof(DWORD),
                        dwPolicyFlags);
#endif  // LAME_BUTTON


    if (!bPolicyChange) {
        /*
         * Set syscolors from registry.
         */
        xxxODI_ColorInit(pProfileUserName);

        LW_LoadResources(pProfileUserName);

        /*
         * This is the initialization from Chicago.
         */
        xxxSetWindowNCMetrics(pProfileUserName, NULL, TRUE, -1); // Colors must be set first
        SetMinMetrics(pProfileUserName, NULL);
        SetIconMetrics(pProfileUserName, NULL);

        /*
         * Read the keyboard layout switching hot key.
         */
        GetKbdLangSwitch(pProfileUserName);

        /*
         * Set the default thread locale for the system based on the value
         * in the current user's registry profile.
         */
        ZwSetDefaultLocale( TRUE, 0 );

        /*
         * Set the default UI language based on the value in the current
         * user's registry profile.
         */
        ZwSetDefaultUILanguage(0);

        /*
         * And then Get it.
         */
        ZwQueryDefaultUILanguage(&(gpsi->UILangID));

        /*
         * Now load strings using the currnet UILangID.
         */
        xxxLoadSomeStrings();

        /*
         * Destroy the desktop system menus, so that they're recreated with
         * the correct UI language if the current user's UI language is
         * different from the previous one. This is done by finding the
         * interactive window station and destroying all its desktops's
         * system menus.
         */
        if (grpWinStaList != NULL) {
            PDESKTOP        pdesk;
            PMENU           pmenu;

            UserAssert(!(grpWinStaList->dwWSF_Flags & WSF_NOIO));
            for (pdesk = grpWinStaList->rpdeskList; pdesk != NULL; pdesk = pdesk->rpdeskNext) {
                if (pdesk->spmenuSys != NULL) {
                    pmenu = pdesk->spmenuSys;
                    if (UnlockDesktopSysMenu(&pdesk->spmenuSys)) {
                        _DestroyMenu(pmenu);
                    }
                }
                if (pdesk->spmenuDialogSys != NULL) {
                    pmenu = pdesk->spmenuDialogSys;
                    if (UnlockDesktopSysMenu(&pdesk->spmenuDialogSys)) {
                        _DestroyMenu(pmenu);
                    }
                }
            }
        }

        xxxUpdateSystemCursorsFromRegistry(pProfileUserName);

        /*
         * now go set a bunch of random values from the win.ini file.
         */
        for (i = 0; i < ARRAY_SIZE(spiNotPolicy); i++) {
            if (FastGetProfileIntFromID(pProfileUserName,
                                    spiNotPolicy[i].idSection,
                                    spiNotPolicy[i].idRes,
                                    spiNotPolicy[i].def,
                                    &dwData,
                                    0)) {
                xxxSystemParametersInfo(spiNotPolicy[i].id, dwData, 0L, 0);
            }
        }

        FastGetProfileIntsW(pProfileUserName, apiiNoPolicy, 0);
    }

    /*
     * Reset desktop pattern now. Note no parameters. It just goes off
     * and reads the registry and sets the desktop pattern.
     */
    xxxSystemParametersInfo(SPI_SETDESKPATTERN, (UINT)-1, 0L, 0); // 265 version

    /*
     * Initialize IME show status
     */
    if (bUserLoggedOn) {
        gfIMEShowStatus = IMESHOWSTATUS_NOTINITIALIZED;
    }

    /*
     * Now go set a bunch of random values from the registry.
     */
    for (i = 0; i < ARRAY_SIZE(spiPolicy); i++) {
        if (FastGetProfileIntFromID(pProfileUserName,
                                spiPolicy[i].idSection,
                                spiPolicy[i].idRes,
                                spiPolicy[i].def,
                                &dwData,
                                dwPolicyFlags)) {
            xxxSystemParametersInfo(spiPolicy[i].id, dwData, 0L, 0);
        }
    }

    /*
     * Read profile integers and do any fixups.
     */
    FastGetProfileIntsW(pProfileUserName, apiiPolicy, dwPolicyFlags);

    if (gnFastAltTabColumns < 2) {
        gnFastAltTabColumns = 7;
    }

    if (gnFastAltTabRows < 1) {
        gnFastAltTabRows = 3;
    }

    /*
     * If this is the first time the user logs on, set the DragFullWindows
     * to the default. If we have an accelerated device, enable full drag.
     */
    if (fDragFullWindows == 2) {
        WCHAR szTemp[40], szDragFullWindows[40];

        SET_OR_CLEAR_PUDF(
                PUDF_DRAGFULLWINDOWS,
                GreGetDeviceCaps(gpDispInfo->hdcScreen, BLTALIGNMENT) == 0);

        if (bUserLoggedOn) {
            swprintf(szTemp, L"%d", TEST_BOOL_PUDF(PUDF_DRAGFULLWINDOWS));

            ServerLoadString(hModuleWin,
                             STR_DRAGFULLWINDOWS,
                             szDragFullWindows,
                             ARRAY_SIZE(szDragFullWindows));

            FastWriteProfileStringW(pProfileUserName,
                                    PMAP_DESKTOP,
                                    szDragFullWindows,
                                    szTemp);
        }
    } else {
        SET_OR_CLEAR_PUDF(PUDF_DRAGFULLWINDOWS, fDragFullWindows);
    }

    /*
     * !!!LATER!!! (adams) See if the following profile retrievals can't
     * be done in the "spi" array above (e.g. SPI_SETSNAPTO).
     */

    /*
     * For remote connections or remote assistance, we may not want a
     * blinking caret for perf reasons. So, we try to read the value from
     * the registry, if there is one set. If nothing is set, we will
     * default to whatever is on desktop.
     */
    dwData = gpsi->dtCaretBlink;

    if (FastGetProfileIntFromID(pProfileUserName,
                                PMAP_DESKTOP,
                                STR_BLINK,
                                500,
                                &dwData,
                                bRemoteSettings? dwPolicyFlags : bPolicyChange)) {
        _SetCaretBlinkTime(dwData);
    }

    if (!bPolicyChange) {
        /*
         * Set mouse settings
         */
        FastGetProfileIntFromID(pProfileUserName, PMAP_MOUSE, STR_MOUSESENSITIVITY, MOUSE_SENSITIVITY_DEFAULT, &gMouseSensitivity, 0);

        if ((gMouseSensitivity < MOUSE_SENSITIVITY_MIN) || (gMouseSensitivity > MOUSE_SENSITIVITY_MAX)) {
            gMouseSensitivity = MOUSE_SENSITIVITY_DEFAULT;
        }
        gMouseSensitivityFactor = CalculateMouseSensitivity(gMouseSensitivity);

#ifdef SUBPIXEL_MOUSE
        ReadDefaultAccelerationCurves(pProfileUserName);
        ResetMouseAccelerationCurves();
#endif

        /*
         * Set mouse trails.
         */
        FastGetProfileIntFromID(pProfileUserName, PMAP_MOUSE, STR_MOUSETRAILS, 0, &dwData, 0);
        SetMouseTrails(dwData);

        /*
         * Font Information
         */
        FastGetProfileIntW(pProfileUserName, PMAP_TRUETYPE, TEXT("TTOnly"), FALSE, &dwData, 0);
        GreSetFontEnumeration(dwData);

        /*
         * Window animation
         */
        FastGetProfileIntFromID(pProfileUserName, PMAP_METRICS, STR_MINANIMATE, TRUE, &dwData, 0);
        SET_OR_CLEAR_PUDF(PUDF_ANIMATE, dwData);

        /*
         * Mouse tracking variables
         */
        FastGetProfileIntFromID(pProfileUserName, PMAP_MOUSE, STR_MOUSEHOVERWIDTH, SYSMET(CXDOUBLECLK), &gcxMouseHover, 0);
        FastGetProfileIntFromID(pProfileUserName, PMAP_MOUSE, STR_MOUSEHOVERHEIGHT, SYSMET(CYDOUBLECLK), &gcyMouseHover, 0);
        FastGetProfileIntFromID(pProfileUserName, PMAP_MOUSE, STR_MOUSEHOVERTIME, gdtMNDropDown, &gdtMouseHover, 0);
    }

    /*
     * Initial Keyboard state:  ScrollLock, NumLock and CapsLock state;
     * global (per-user) kbd layout attributes (such as ShiftLock/CapsLock)
     */
    if (!bPolicyChange) {
        UpdatePerUserKeyboardIndicators(pProfileUserName);
        UpdatePerUserKeyboardMappings(pProfileUserName);
        FastGetProfileDwordW(pProfileUserName, PMAP_UKBDLAYOUT, L"Attributes", 0, &gdwKeyboardAttributes, 0);
        gdwKeyboardAttributes = KLL_GLOBAL_ATTR_FROM_KLF(gdwKeyboardAttributes);

        xxxUpdatePerUserAccessPackSettings(pProfileUserName);
    }

    /*
     * If we successfully opened this, we assume we have a network.
     */
    if (hKey = OpenCacheKeyEx(NULL, PMAP_NETWORK, KEY_READ, NULL)) {
        RIPMSG0(RIP_WARNING | RIP_NONAME, "");
        SYSMET(NETWORK) = RNC_NETWORKS;

        ZwClose(hKey);
    }

    SYSMET(NETWORK) |= RNC_LOGON;

    /*
     * Font smoothing
     */

    /* clear the flags from what could have been set for the previous user */
    GreSetFontEnumeration(FE_SET_AA);
    GreSetFontEnumeration(FE_SET_CT);

    if (dwFontSmoothing & FE_AA_ON)
        GreSetFontEnumeration( dwFontSmoothing | FE_SET_AA );

    if (UPDWORDValue(SPI_GETFONTSMOOTHINGTYPE) & FE_FONTSMOOTHINGCLEARTYPE)
        GreSetFontEnumeration( dwFontSmoothing | FE_SET_CT | FE_CT_ON);

    dwFontSmoothingContrast = UPDWORDValue(SPI_GETFONTSMOOTHINGCONTRAST);

    if (dwFontSmoothingContrast == 0)
        dwFontSmoothingContrast = DEFAULT_CT_CONTRAST;

    GreSetFontContrast(dwFontSmoothingContrast);

    dwFontSmoothingOrientation = UPDWORDValue(SPI_GETFONTSMOOTHINGORIENTATION);

    GreSetLCDOrientation(dwFontSmoothingOrientation);

    /*
     * Desktop Build Number Painting
     */
    if(0 && USER_SHARED_DATA->SystemExpirationDate.QuadPart || gfUnsignedDrivers) {
        gdwCanPaintDesktop = 1;
    } else {
         FastGetProfileDwordW(pProfileUserName, PMAP_DESKTOP, L"PaintDesktopVersion", 0, &gdwCanPaintDesktop, dwPolicyFlags);
    }

    if (!bPolicyChange) {
        FastGetProfileStringW(pProfileUserName,
                              PMAP_WINLOGON,
                              TEXT("DefaultUserName"),
                              TEXT("Unknown"),
                              gszUserName,
                              ARRAY_SIZE(gszUserName),
                              0);
        FastGetProfileStringW(pProfileUserName,
                              PMAP_WINLOGON,
                              TEXT("DefaultDomainName"),
                              TEXT("Unknown"),
                              gszDomainName,
                              ARRAY_SIZE(gszDomainName),
                              0);
        FastGetProfileStringW(pProfileUserName,
                              PMAP_COMPUTERNAME,
                              TEXT("ComputerName"),
                              TEXT("Unknown"),
                              gszComputerName,
                              ARRAY_SIZE(gszComputerName),
                              0);
    }
    FreeProfileUserName(pProfileUserName, &tlName);

    /*
     * Do this if we are here for real policy change
     * i.e. not a remote settings policy change.
     */
    if (dwFlags == UPUSP_POLICYCHANGE) {
        xxxUserResetDisplayDevice();
    }

    return TRUE;
}

/*
 * Called by InitOemXlateTables via SFI_INITANSIOEM
 */
VOID InitAnsiOem(PCHAR pOemToAnsi, PCHAR pAnsiToOem)
{
    UserAssert(gpsi != NULL);
    UserAssert(pOemToAnsi != NULL);
    UserAssert(pAnsiToOem != NULL);

    try {
        ProbeForRead(pOemToAnsi, NCHARS, sizeof(BYTE));
        ProbeForRead(pAnsiToOem, NCHARS, sizeof(BYTE));

        RtlCopyMemory(gpsi->acOemToAnsi, pOemToAnsi, NCHARS);
        RtlCopyMemory(gpsi->acAnsiToOem, pAnsiToOem, NCHARS);
    } except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
    }
}

/***************************************************************************\
* RegisterLPK
*
* Called by InitializeLpkHooks on the client side after an LPK is
* loaded for the current process.
*
* 05-Nov-1996 GregoryW  Created.
\***************************************************************************/
VOID RegisterLPK(
    DWORD dwLpkEntryPoints)
{
    PpiCurrent()->dwLpkEntryPoints = dwLpkEntryPoints;
}

/***************************************************************************\
* Enforce color-depth dependent settings on systems with less
* then 256 colors.
*
* 2/13/1998   vadimg          created
\***************************************************************************/
VOID EnforceColorDependentSettings(VOID)
{
    if (gpDispInfo->fAnyPalette) {
        gbDisableAlpha = TRUE;
    } else if (GreGetDeviceCaps(gpDispInfo->hdcScreen, NUMCOLORS) == -1) {
        gbDisableAlpha = FALSE;
    } else {
        gbDisableAlpha = TRUE;
    }
}

#if DBG
VOID InitGlobalThreadLockArray(
    DWORD dwIndex)
{
    PTL pTLArray = gpaThreadLocksArrays[dwIndex];
    int i;

    for (i = 0; i < MAX_THREAD_LOCKS - 1; i++) {
        pTLArray[i].next = &pTLArray[i + 1];
    }

    pTLArray[MAX_THREAD_LOCKS - 1].next = NULL;
}
#endif


/***************************************************************************\
* CheckLUIDDosDevicesEnabled
*
* Checks if LUID DosDevices are Enabled.
*
* 8/20/2000   ELi          created
\***************************************************************************/
VOID CheckLUIDDosDevicesEnabled(
    PBOOL pResult)
{
    ULONG LUIDDeviceMapsEnabled;
    NTSTATUS Status;

    UserAssert(pResult != NULL);

    Status = NtQueryInformationProcess(NtCurrentProcess(),
                                       ProcessLUIDDeviceMapsEnabled,
                                       &LUIDDeviceMapsEnabled,
                                       sizeof(LUIDDeviceMapsEnabled),
                                       NULL);
    if (NT_SUCCESS(Status)) {
        *pResult = (LUIDDeviceMapsEnabled != 0);
    } else {
        *pResult = FALSE;
    }
}
